diff options
-rw-r--r-- | src/Mobs/Behaviors/BehaviorAggressive.h | 26 | ||||
-rw-r--r-- | src/Mobs/Monster.cpp | 2079 | ||||
-rw-r--r-- | src/Mobs/Monster.h | 453 |
3 files changed, 1317 insertions, 1241 deletions
diff --git a/src/Mobs/Behaviors/BehaviorAggressive.h b/src/Mobs/Behaviors/BehaviorAggressive.h index 43747d0e2..0235aa99b 100644 --- a/src/Mobs/Behaviors/BehaviorAggressive.h +++ b/src/Mobs/Behaviors/BehaviorAggressive.h @@ -9,23 +9,23 @@ class cBehaviorAggressive { public: - cBehaviorAggressive(cMonster * a_Parent, int a_MinimumLight); + cBehaviorAggressive(cMonster * a_Parent, int a_MinimumLight); - // cBehaviorAggressive(cMonster * a_Parent, bool a_HatesPlayer); - // TODO agression toward specific players, and specific mobtypes, etc - // Agression under specific conditions (nighttime, etc) + // cBehaviorAggressive(cMonster * a_Parent, bool a_HatesPlayer); + // TODO agression toward specific players, and specific mobtypes, etc + // Agression under specific conditions (nighttime, etc) - // Functions our host Monster should invoke: - bool ActiveTick(); - void Destroyed(); + // Functions our host Monster should invoke: + bool ActiveTick(); + void Destroyed(); private: - cPawn * FindNewTarget(); + cPawn * FindNewTarget(); - // Our parent - cMonster * m_Parent; - cBehaviorChaser * m_ParentChaser; + // Our parent + cMonster * m_Parent; + cBehaviorChaser * m_ParentChaser; - // The mob we want to attack - cPawn * m_Target; + // The mob we want to attack + cPawn * m_Target; }; diff --git a/src/Mobs/Monster.cpp b/src/Mobs/Monster.cpp index d1c2413c3..f2af7b716 100644 --- a/src/Mobs/Monster.cpp +++ b/src/Mobs/Monster.cpp @@ -34,43 +34,43 @@ m_VanillaName is the name that vanilla use for this mob. */ static const struct { - eMonsterType m_Type; - const char * m_lcName; - const char * m_VanillaName; - const char * m_VanillaNameNBT; + eMonsterType m_Type; + const char * m_lcName; + const char * m_VanillaName; + const char * m_VanillaNameNBT; } g_MobTypeNames[] = { - {mtBat, "bat", "Bat", "bat"}, - {mtBlaze, "blaze", "Blaze", "blaze"}, - {mtCaveSpider, "cavespider", "CaveSpider", "cave_spider"}, - {mtChicken, "chicken", "Chicken", "chicken"}, - {mtCow, "cow", "Cow", "cow"}, - {mtCreeper, "creeper", "Creeper", "creeper"}, - {mtEnderman, "enderman", "Enderman", "enderman"}, - {mtEnderDragon, "enderdragon", "EnderDragon", "ender_dragon"}, - {mtGhast, "ghast", "Ghast", "ghast"}, - {mtGiant, "giant", "Giant", "giant"}, - {mtGuardian, "guardian", "Guardian", "guardian"}, - {mtHorse, "horse", "EntityHorse", "horse"}, - {mtIronGolem, "irongolem", "VillagerGolem", "iron_golem"}, - {mtMagmaCube, "magmacube", "LavaSlime", "magma_cube"}, - {mtMooshroom, "mooshroom", "MushroomCow", "mooshroom"}, - {mtOcelot, "ocelot", "Ozelot", "ocelot"}, - {mtPig, "pig", "Pig", "pig"}, - {mtRabbit, "rabbit", "Rabbit", "rabbit"}, - {mtSheep, "sheep", "Sheep", "sheep"}, - {mtSilverfish, "silverfish", "Silverfish", "silverfish"}, - {mtSkeleton, "skeleton", "Skeleton", "skeleton"}, - {mtSlime, "slime", "Slime", "slime"}, - {mtSnowGolem, "snowgolem", "SnowMan", "snow_golem"}, - {mtSpider, "spider", "Spider", "spider"}, - {mtSquid, "squid", "Squid", "squid"}, - {mtVillager, "villager", "Villager", "villager"}, - {mtWitch, "witch", "Witch", "witch"}, - {mtWither, "wither", "WitherBoss", "wither"}, - {mtWolf, "wolf", "Wolf", "wolf"}, - {mtZombie, "zombie", "Zombie", "zombie"}, - {mtZombiePigman, "zombiepigman", "PigZombie", "zombie_pigman"}, + {mtBat, "bat", "Bat", "bat"}, + {mtBlaze, "blaze", "Blaze", "blaze"}, + {mtCaveSpider, "cavespider", "CaveSpider", "cave_spider"}, + {mtChicken, "chicken", "Chicken", "chicken"}, + {mtCow, "cow", "Cow", "cow"}, + {mtCreeper, "creeper", "Creeper", "creeper"}, + {mtEnderman, "enderman", "Enderman", "enderman"}, + {mtEnderDragon, "enderdragon", "EnderDragon", "ender_dragon"}, + {mtGhast, "ghast", "Ghast", "ghast"}, + {mtGiant, "giant", "Giant", "giant"}, + {mtGuardian, "guardian", "Guardian", "guardian"}, + {mtHorse, "horse", "EntityHorse", "horse"}, + {mtIronGolem, "irongolem", "VillagerGolem", "iron_golem"}, + {mtMagmaCube, "magmacube", "LavaSlime", "magma_cube"}, + {mtMooshroom, "mooshroom", "MushroomCow", "mooshroom"}, + {mtOcelot, "ocelot", "Ozelot", "ocelot"}, + {mtPig, "pig", "Pig", "pig"}, + {mtRabbit, "rabbit", "Rabbit", "rabbit"}, + {mtSheep, "sheep", "Sheep", "sheep"}, + {mtSilverfish, "silverfish", "Silverfish", "silverfish"}, + {mtSkeleton, "skeleton", "Skeleton", "skeleton"}, + {mtSlime, "slime", "Slime", "slime"}, + {mtSnowGolem, "snowgolem", "SnowMan", "snow_golem"}, + {mtSpider, "spider", "Spider", "spider"}, + {mtSquid, "squid", "Squid", "squid"}, + {mtVillager, "villager", "Villager", "villager"}, + {mtWitch, "witch", "Witch", "witch"}, + {mtWither, "wither", "WitherBoss", "wither"}, + {mtWolf, "wolf", "Wolf", "wolf"}, + {mtZombie, "zombie", "Zombie", "zombie"}, + {mtZombiePigman, "zombiepigman", "PigZombie", "zombie_pigman"}, } ; @@ -81,46 +81,46 @@ static const struct // cMonster: cMonster::cMonster(const AString & a_ConfigName, eMonsterType a_MobType, const AString & a_SoundHurt, const AString & a_SoundDeath, double a_Width, double a_Height) - : super(etMonster, a_Width, a_Height) - , m_EMState(IDLE) - , m_EMPersonality(AGGRESSIVE) - , m_PathFinder(a_Width, a_Height) - , m_PathfinderActivated(false) - , m_JumpCoolDown(0) - , m_IdleInterval(0) - , m_DestroyTimer(0) - , m_MobType(a_MobType) - , m_CustomName("") - , m_CustomNameAlwaysVisible(false) - , m_SoundHurt(a_SoundHurt) - , m_SoundDeath(a_SoundDeath) - , m_AttackRate(3) - , m_AttackDamage(1) - , m_AttackRange(1) - , m_AttackCoolDownTicksLeft(0) - , m_SightDistance(25) - , m_DropChanceWeapon(0.085f) - , m_DropChanceHelmet(0.085f) - , m_DropChanceChestplate(0.085f) - , m_DropChanceLeggings(0.085f) - , m_DropChanceBoots(0.085f) - , m_CanPickUpLoot(true) - , m_TicksSinceLastDamaged(100) - , m_BurnsInDaylight(false) - , m_RelativeWalkSpeed(1) - , m_Age(1) - , m_AgingTimer(20 * 60 * 20) // about 20 minutes - , m_WasLastTargetAPlayer(false) - , m_LeashedTo(nullptr) - , m_LeashToPos(nullptr) - , m_IsLeashActionJustDone(false) - , m_CanBeLeashed(GetMobFamily() == eFamily::mfPassive) - , m_Target(nullptr) + : super(etMonster, a_Width, a_Height) + , m_EMState(IDLE) + , m_EMPersonality(AGGRESSIVE) + , m_PathFinder(a_Width, a_Height) + , m_PathfinderActivated(false) + , m_JumpCoolDown(0) + , m_IdleInterval(0) + , m_DestroyTimer(0) + , m_MobType(a_MobType) + , m_CustomName("") + , m_CustomNameAlwaysVisible(false) + , m_SoundHurt(a_SoundHurt) + , m_SoundDeath(a_SoundDeath) + , m_AttackRate(3) + , m_AttackDamage(1) + , m_AttackRange(1) + , m_AttackCoolDownTicksLeft(0) + , m_SightDistance(25) + , m_DropChanceWeapon(0.085f) + , m_DropChanceHelmet(0.085f) + , m_DropChanceChestplate(0.085f) + , m_DropChanceLeggings(0.085f) + , m_DropChanceBoots(0.085f) + , m_CanPickUpLoot(true) + , m_TicksSinceLastDamaged(100) + , m_BurnsInDaylight(false) + , m_RelativeWalkSpeed(1) + , m_Age(1) + , m_AgingTimer(20 * 60 * 20) // about 20 minutes + , m_WasLastTargetAPlayer(false) + , m_LeashedTo(nullptr) + , m_LeashToPos(nullptr) + , m_IsLeashActionJustDone(false) + , m_CanBeLeashed(GetMobFamily() == eFamily::mfPassive) + , m_Target(nullptr) { - if (!a_ConfigName.empty()) - { - GetMonsterConfig(a_ConfigName); - } + if (!a_ConfigName.empty()) + { + GetMonsterConfig(a_ConfigName); + } } @@ -129,7 +129,7 @@ cMonster::cMonster(const AString & a_ConfigName, eMonsterType a_MobType, const A cMonster::~cMonster() { - ASSERT(GetTarget() == nullptr); + ASSERT(GetTarget() == nullptr); } @@ -138,19 +138,19 @@ cMonster::~cMonster() void cMonster::Destroy(bool a_ShouldBroadcast) { - if (IsLeashed()) - { - cEntity * LeashedTo = GetLeashedTo(); - Unleash(false, a_ShouldBroadcast); - - // Remove leash knot if there are no more mobs leashed to - if (!LeashedTo->HasAnyMobLeashed() && LeashedTo->IsLeashKnot()) - { - LeashedTo->Destroy(); - } - } - - super::Destroy(a_ShouldBroadcast); + if (IsLeashed()) + { + cEntity * LeashedTo = GetLeashedTo(); + Unleash(false, a_ShouldBroadcast); + + // Remove leash knot if there are no more mobs leashed to + if (!LeashedTo->HasAnyMobLeashed() && LeashedTo->IsLeashKnot()) + { + LeashedTo->Destroy(); + } + } + + super::Destroy(a_ShouldBroadcast); } @@ -159,8 +159,8 @@ void cMonster::Destroy(bool a_ShouldBroadcast) void cMonster::Destroyed() { - SetTarget(nullptr); // Tell them we're no longer targeting them. - super::Destroyed(); + SetTarget(nullptr); // Tell them we're no longer targeting them. + super::Destroyed(); } @@ -169,12 +169,12 @@ void cMonster::Destroyed() void cMonster::SpawnOn(cClientHandle & a_Client) { - a_Client.SendSpawnMob(*this); + a_Client.SendSpawnMob(*this); - if (IsLeashed()) - { - a_Client.SendLeashEntity(*this, *this->GetLeashedTo()); - } + if (IsLeashed()) + { + a_Client.SendLeashEntity(*this, *this->GetLeashedTo()); + } } @@ -183,71 +183,71 @@ void cMonster::SpawnOn(cClientHandle & a_Client) void cMonster::MoveToWayPoint(cChunk & a_Chunk) { - if ((m_NextWayPointPosition - GetPosition()).SqrLength() < WAYPOINT_RADIUS * WAYPOINT_RADIUS) - { - return; - } - - if (m_JumpCoolDown <= 0) - { - if (DoesPosYRequireJump(FloorC(m_NextWayPointPosition.y))) - { - if ( - (IsOnGround() && (GetSpeed().SqrLength() <= 0.5)) || // If walking on the ground, we need to slow down first, otherwise we miss the jump - IsSwimming() - ) - { - m_bOnGround = false; - m_JumpCoolDown = 20; - AddPosY(1.6); // Jump!! - SetSpeedY(1); - 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 - } - } - } - else - { - --m_JumpCoolDown; - } - - Vector3d Distance = m_NextWayPointPosition - GetPosition(); - if ((std::abs(Distance.x) > 0.05) || (std::abs(Distance.z) > 0.05)) - { - 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); - } - - // Speed up leashed mobs getting far from player - if (IsLeashed() && GetLeashedTo()->IsPlayer()) - { - Distance = GetLeashedTo()->GetPosition() - GetPosition(); - Distance.Normalize(); - AddSpeedX(Distance.x); - AddSpeedZ(Distance.z); - } + if ((m_NextWayPointPosition - GetPosition()).SqrLength() < WAYPOINT_RADIUS * WAYPOINT_RADIUS) + { + return; + } + + if (m_JumpCoolDown <= 0) + { + if (DoesPosYRequireJump(FloorC(m_NextWayPointPosition.y))) + { + if ( + (IsOnGround() && (GetSpeed().SqrLength() <= 0.5)) || // If walking on the ground, we need to slow down first, otherwise we miss the jump + IsSwimming() + ) + { + m_bOnGround = false; + m_JumpCoolDown = 20; + AddPosY(1.6); // Jump!! + SetSpeedY(1); + 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 + } + } + } + else + { + --m_JumpCoolDown; + } + + Vector3d Distance = m_NextWayPointPosition - GetPosition(); + if ((std::abs(Distance.x) > 0.05) || (std::abs(Distance.z) > 0.05)) + { + 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); + } + + // Speed up leashed mobs getting far from player + if (IsLeashed() && GetLeashedTo()->IsPlayer()) + { + Distance = GetLeashedTo()->GetPosition() - GetPosition(); + Distance.Normalize(); + AddSpeedX(Distance.x); + AddSpeedZ(Distance.z); + } } @@ -263,8 +263,8 @@ void cMonster::MoveToWayPoint(cChunk & a_Chunk) void cMonster::MoveToPosition(const Vector3d & a_Position) { - m_FinalDestination = a_Position; - m_PathfinderActivated = true; + m_FinalDestination = a_Position; + m_PathfinderActivated = true; } @@ -273,7 +273,7 @@ void cMonster::MoveToPosition(const Vector3d & a_Position) void cMonster::StopMovingToPosition() { - m_PathfinderActivated = false; + m_PathfinderActivated = false; } @@ -282,140 +282,140 @@ void cMonster::StopMovingToPosition() void cMonster::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) { - super::Tick(a_Dt, a_Chunk); - if (!IsTicking()) - { - // The base class tick destroyed us - return; - } - GET_AND_VERIFY_CURRENT_CHUNK(Chunk, POSX_TOINT, POSZ_TOINT); - - ASSERT((GetTarget() == nullptr) || (GetTarget()->IsPawn() && (GetTarget()->GetWorld() == GetWorld()))); - if (m_AttackCoolDownTicksLeft > 0) - { - m_AttackCoolDownTicksLeft -= 1; - } - - if (m_Health <= 0) - { - // 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)) - { - Destroy(true); - } - return; - } - - if (m_TicksSinceLastDamaged < 100) - { - ++m_TicksSinceLastDamaged; - } - if ((GetTarget() != nullptr)) - { - ASSERT(GetTarget()->IsTicking()); - - if (GetTarget()->IsPlayer()) - { - if (!static_cast<cPlayer *>(GetTarget())->CanMobsTarget()) - { - SetTarget(nullptr); - m_EMState = IDLE; - } - } - } - - // Process the undead burning in daylight. - HandleDaylightBurning(*Chunk, WouldBurnAt(GetPosition(), *Chunk)); - - bool a_IsFollowingPath = false; - if (m_PathfinderActivated) - { - if (ReachedFinalDestination() || (m_LeashToPos != nullptr)) - { - StopMovingToPosition(); // Simply sets m_PathfinderActivated to false. - } - else - { - // Note that m_NextWayPointPosition is actually returned by GetNextWayPoint) - switch (m_PathFinder.GetNextWayPoint(*Chunk, GetPosition(), &m_FinalDestination, &m_NextWayPointPosition, m_EMState == IDLE ? true : false)) - { - case ePathFinderStatus::PATH_FOUND: - { - /* 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(); - } - else - { - a_IsFollowingPath = true; // Used for proper body / head orientation only. - MoveToWayPoint(*Chunk); - } - break; - } - case ePathFinderStatus::PATH_NOT_FOUND: - { - StopMovingToPosition(); - break; - } - default: - { - - } - } - } - } - - SetPitchAndYawFromDestination(a_IsFollowingPath); - - switch (m_EMState) - { - case IDLE: - { - // If enemy passive we ignore checks for player visibility. - InStateIdle(a_Dt, a_Chunk); - break; - } - case CHASING: - { - // If we do not see a player anymore skip chasing action. - InStateChasing(a_Dt, a_Chunk); - break; - } - case ESCAPING: - { - InStateEscaping(a_Dt, a_Chunk); - break; - } - case ATTACKING: break; - } // switch (m_EMState) - - // Leash calculations - if ((m_TicksAlive % LEASH_ACTIONS_TICK_STEP) == 0) - { - CalcLeashActions(); - } - - BroadcastMovementUpdate(); - - if (m_AgingTimer > 0) - { - m_AgingTimer--; - if ((m_AgingTimer <= 0) && IsBaby()) - { - SetAge(1); - m_World->BroadcastEntityMetadata(*this); - } - } + super::Tick(a_Dt, a_Chunk); + if (!IsTicking()) + { + // The base class tick destroyed us + return; + } + GET_AND_VERIFY_CURRENT_CHUNK(Chunk, POSX_TOINT, POSZ_TOINT); + + ASSERT((GetTarget() == nullptr) || (GetTarget()->IsPawn() && (GetTarget()->GetWorld() == GetWorld()))); + if (m_AttackCoolDownTicksLeft > 0) + { + m_AttackCoolDownTicksLeft -= 1; + } + + if (m_Health <= 0) + { + // 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)) + { + Destroy(true); + } + return; + } + + if (m_TicksSinceLastDamaged < 100) + { + ++m_TicksSinceLastDamaged; + } + if ((GetTarget() != nullptr)) + { + ASSERT(GetTarget()->IsTicking()); + + if (GetTarget()->IsPlayer()) + { + if (!static_cast<cPlayer *>(GetTarget())->CanMobsTarget()) + { + SetTarget(nullptr); + m_EMState = IDLE; + } + } + } + + // Process the undead burning in daylight. + HandleDaylightBurning(*Chunk, WouldBurnAt(GetPosition(), *Chunk)); + + bool a_IsFollowingPath = false; + if (m_PathfinderActivated) + { + if (ReachedFinalDestination() || (m_LeashToPos != nullptr)) + { + StopMovingToPosition(); // Simply sets m_PathfinderActivated to false. + } + else + { + // Note that m_NextWayPointPosition is actually returned by GetNextWayPoint) + switch (m_PathFinder.GetNextWayPoint(*Chunk, GetPosition(), &m_FinalDestination, &m_NextWayPointPosition, m_EMState == IDLE ? true : false)) + { + case ePathFinderStatus::PATH_FOUND: + { + /* 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(); + } + else + { + a_IsFollowingPath = true; // Used for proper body / head orientation only. + MoveToWayPoint(*Chunk); + } + break; + } + case ePathFinderStatus::PATH_NOT_FOUND: + { + StopMovingToPosition(); + break; + } + default: + { + + } + } + } + } + + SetPitchAndYawFromDestination(a_IsFollowingPath); + + switch (m_EMState) + { + case IDLE: + { + // If enemy passive we ignore checks for player visibility. + InStateIdle(a_Dt, a_Chunk); + break; + } + case CHASING: + { + // If we do not see a player anymore skip chasing action. + InStateChasing(a_Dt, a_Chunk); + break; + } + case ESCAPING: + { + InStateEscaping(a_Dt, a_Chunk); + break; + } + case ATTACKING: break; + } // switch (m_EMState) + + // Leash calculations + if ((m_TicksAlive % LEASH_ACTIONS_TICK_STEP) == 0) + { + CalcLeashActions(); + } + + BroadcastMovementUpdate(); + + if (m_AgingTimer > 0) + { + m_AgingTimer--; + if ((m_AgingTimer <= 0) && IsBaby()) + { + SetAge(1); + m_World->BroadcastEntityMetadata(*this); + } + } } @@ -424,31 +424,31 @@ void cMonster::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) void cMonster::CalcLeashActions() { - // This mob just spotted in the world and [m_LeashToPos not null] shows that should be leashed to a leash knot at m_LeashToPos. - // This keeps trying until knot is found. Leash knot may be in a different chunk that needn't or can't be loaded yet. - if (!IsLeashed() && (m_LeashToPos != nullptr)) - { - auto LeashKnot = cLeashKnot::FindKnotAtPos(*m_World, { FloorC(m_LeashToPos->x), FloorC(m_LeashToPos->y), FloorC(m_LeashToPos->z) }); - if (LeashKnot != nullptr) - { - LeashTo(LeashKnot); - SetLeashToPos(nullptr); - } - } - else if (IsLeashed()) // Mob is already leashed to an entity: follow it. - { - // TODO: leashed mobs in vanilla can move around up to 5 blocks distance from leash origin - MoveToPosition(m_LeashedTo->GetPosition()); - - // If distance to target > 10 break leash - Vector3f a_Distance(m_LeashedTo->GetPosition() - GetPosition()); - double Distance(a_Distance.Length()); - if (Distance > 10.0) - { - LOGD("Leash broken (distance)"); - Unleash(false); - } - } + // This mob just spotted in the world and [m_LeashToPos not null] shows that should be leashed to a leash knot at m_LeashToPos. + // This keeps trying until knot is found. Leash knot may be in a different chunk that needn't or can't be loaded yet. + if (!IsLeashed() && (m_LeashToPos != nullptr)) + { + auto LeashKnot = cLeashKnot::FindKnotAtPos(*m_World, { FloorC(m_LeashToPos->x), FloorC(m_LeashToPos->y), FloorC(m_LeashToPos->z) }); + if (LeashKnot != nullptr) + { + LeashTo(LeashKnot); + SetLeashToPos(nullptr); + } + } + else if (IsLeashed()) // Mob is already leashed to an entity: follow it. + { + // TODO: leashed mobs in vanilla can move around up to 5 blocks distance from leash origin + MoveToPosition(m_LeashedTo->GetPosition()); + + // If distance to target > 10 break leash + Vector3f a_Distance(m_LeashedTo->GetPosition() - GetPosition()); + double Distance(a_Distance.Length()); + if (Distance > 10.0) + { + LOGD("Leash broken (distance)"); + Unleash(false); + } + } } @@ -457,52 +457,52 @@ void cMonster::CalcLeashActions() void cMonster::SetPitchAndYawFromDestination(bool a_IsFollowingPath) { - Vector3d BodyDistance; - if (!a_IsFollowingPath && (GetTarget() != nullptr)) - { - BodyDistance = GetTarget()->GetPosition() - GetPosition(); - } - else - { - BodyDistance = m_NextWayPointPosition - GetPosition(); - } - double BodyRotation, BodyPitch; - BodyDistance.Normalize(); - VectorToEuler(BodyDistance.x, BodyDistance.y, BodyDistance.z, BodyRotation, BodyPitch); - SetYaw(BodyRotation); - - Vector3d HeadDistance; - if (GetTarget() != nullptr) - { - if (GetTarget()->IsPlayer()) // Look at a player - { - HeadDistance = GetTarget()->GetPosition() - GetPosition(); - } - else // Look at some other entity - { - HeadDistance = GetTarget()->GetPosition() - GetPosition(); - // HeadDistance.y = GetTarget()->GetPosY() + GetHeight(); - } - } - else // Look straight - { - HeadDistance = BodyDistance; - HeadDistance.y = 0; - } - - double HeadRotation, HeadPitch; - HeadDistance.Normalize(); - VectorToEuler(HeadDistance.x, HeadDistance.y, HeadDistance.z, HeadRotation, HeadPitch); - if ((std::abs(BodyRotation - HeadRotation) < 70) && (std::abs(HeadPitch) < 60)) - { - SetHeadYaw(HeadRotation); - SetPitch(-HeadPitch); - } - else - { - SetHeadYaw(BodyRotation); - SetPitch(0); - } + Vector3d BodyDistance; + if (!a_IsFollowingPath && (GetTarget() != nullptr)) + { + BodyDistance = GetTarget()->GetPosition() - GetPosition(); + } + else + { + BodyDistance = m_NextWayPointPosition - GetPosition(); + } + double BodyRotation, BodyPitch; + BodyDistance.Normalize(); + VectorToEuler(BodyDistance.x, BodyDistance.y, BodyDistance.z, BodyRotation, BodyPitch); + SetYaw(BodyRotation); + + Vector3d HeadDistance; + if (GetTarget() != nullptr) + { + if (GetTarget()->IsPlayer()) // Look at a player + { + HeadDistance = GetTarget()->GetPosition() - GetPosition(); + } + else // Look at some other entity + { + HeadDistance = GetTarget()->GetPosition() - GetPosition(); + // HeadDistance.y = GetTarget()->GetPosY() + GetHeight(); + } + } + else // Look straight + { + HeadDistance = BodyDistance; + HeadDistance.y = 0; + } + + double HeadRotation, HeadPitch; + HeadDistance.Normalize(); + VectorToEuler(HeadDistance.x, HeadDistance.y, HeadDistance.z, HeadRotation, HeadPitch); + if ((std::abs(BodyRotation - HeadRotation) < 70) && (std::abs(HeadPitch) < 60)) + { + SetHeadYaw(HeadRotation); + SetPitch(-HeadPitch); + } + else + { + SetHeadYaw(BodyRotation); + SetPitch(0); + } } @@ -511,8 +511,8 @@ void cMonster::SetPitchAndYawFromDestination(bool a_IsFollowingPath) void cMonster::HandleFalling() { - m_bTouchGround = IsOnGround(); - super::HandleFalling(); + m_bTouchGround = IsOnGround(); + super::HandleFalling(); } @@ -521,27 +521,27 @@ void cMonster::HandleFalling() int cMonster::FindFirstNonAirBlockPosition(double a_PosX, double a_PosZ) { - int PosY = POSY_TOINT; - PosY = Clamp(PosY, 0, cChunkDef::Height); - - if (!cBlockInfo::IsSolid(m_World->GetBlock(FloorC(a_PosX), PosY, FloorC(a_PosZ)))) - { - while (!cBlockInfo::IsSolid(m_World->GetBlock(FloorC(a_PosX), PosY, FloorC(a_PosZ))) && (PosY > 0)) - { - PosY--; - } - - return PosY + 1; - } - else - { - while ((PosY < cChunkDef::Height) && cBlockInfo::IsSolid(m_World->GetBlock(static_cast<int>(floor(a_PosX)), PosY, static_cast<int>(floor(a_PosZ))))) - { - PosY++; - } - - return PosY; - } + int PosY = POSY_TOINT; + PosY = Clamp(PosY, 0, cChunkDef::Height); + + if (!cBlockInfo::IsSolid(m_World->GetBlock(FloorC(a_PosX), PosY, FloorC(a_PosZ)))) + { + while (!cBlockInfo::IsSolid(m_World->GetBlock(FloorC(a_PosX), PosY, FloorC(a_PosZ))) && (PosY > 0)) + { + PosY--; + } + + return PosY + 1; + } + else + { + while ((PosY < cChunkDef::Height) && cBlockInfo::IsSolid(m_World->GetBlock(static_cast<int>(floor(a_PosX)), PosY, static_cast<int>(floor(a_PosZ))))) + { + PosY++; + } + + return PosY; + } } @@ -550,28 +550,28 @@ int cMonster::FindFirstNonAirBlockPosition(double a_PosX, double a_PosZ) bool cMonster::DoTakeDamage(TakeDamageInfo & a_TDI) { - if (!super::DoTakeDamage(a_TDI)) - { - return false; - } - - if (!m_SoundHurt.empty() && (m_Health > 0)) - { - m_World->BroadcastSoundEffect(m_SoundHurt, GetPosX(), GetPosY(), GetPosZ(), 1.0f, 0.8f); - } - - if ((a_TDI.Attacker != nullptr) && a_TDI.Attacker->IsPawn()) - { - if ( - (!a_TDI.Attacker->IsPlayer()) || - (static_cast<cPlayer *>(a_TDI.Attacker)->CanMobsTarget()) - ) - { - SetTarget(static_cast<cPawn*>(a_TDI.Attacker)); - } - m_TicksSinceLastDamaged = 0; - } - return true; + if (!super::DoTakeDamage(a_TDI)) + { + return false; + } + + if (!m_SoundHurt.empty() && (m_Health > 0)) + { + m_World->BroadcastSoundEffect(m_SoundHurt, GetPosX(), GetPosY(), GetPosZ(), 1.0f, 0.8f); + } + + if ((a_TDI.Attacker != nullptr) && a_TDI.Attacker->IsPawn()) + { + if ( + (!a_TDI.Attacker->IsPlayer()) || + (static_cast<cPlayer *>(a_TDI.Attacker)->CanMobsTarget()) + ) + { + SetTarget(static_cast<cPawn*>(a_TDI.Attacker)); + } + m_TicksSinceLastDamaged = 0; + } + return true; } @@ -580,77 +580,77 @@ bool cMonster::DoTakeDamage(TakeDamageInfo & a_TDI) void cMonster::KilledBy(TakeDamageInfo & a_TDI) { - super::KilledBy(a_TDI); - if (m_SoundHurt != "") - { - m_World->BroadcastSoundEffect(m_SoundDeath, GetPosX(), GetPosY(), GetPosZ(), 1.0f, 0.8f); - } - int Reward; - switch (m_MobType) - { - // Animals - case mtChicken: - case mtCow: - case mtHorse: - case mtPig: - case mtRabbit: - case mtSheep: - case mtSquid: - case mtMooshroom: - case mtOcelot: - case mtWolf: - { - Reward = GetRandomProvider().RandInt(1, 3); - break; - } - - // Monsters - case mtCaveSpider: - case mtCreeper: - case mtEnderman: - case mtGhast: - case mtGuardian: - case mtSilverfish: - case mtSkeleton: - case mtSpider: - case mtWitch: - case mtZombie: - case mtZombiePigman: - case mtSlime: - case mtMagmaCube: - { - Reward = GetRandomProvider().RandInt(6, 8); - break; - } - case mtBlaze: - { - Reward = 10; - break; - } - - // Bosses - case mtEnderDragon: - { - Reward = 12000; - break; - } - case mtWither: - { - Reward = 50; - break; - } - - default: - { - Reward = 0; - break; - } - } - if ((a_TDI.Attacker != nullptr) && (!IsBaby())) - { - m_World->SpawnExperienceOrb(GetPosX(), GetPosY(), GetPosZ(), Reward); - } - m_DestroyTimer = std::chrono::milliseconds(0); + super::KilledBy(a_TDI); + if (m_SoundHurt != "") + { + m_World->BroadcastSoundEffect(m_SoundDeath, GetPosX(), GetPosY(), GetPosZ(), 1.0f, 0.8f); + } + int Reward; + switch (m_MobType) + { + // Animals + case mtChicken: + case mtCow: + case mtHorse: + case mtPig: + case mtRabbit: + case mtSheep: + case mtSquid: + case mtMooshroom: + case mtOcelot: + case mtWolf: + { + Reward = GetRandomProvider().RandInt(1, 3); + break; + } + + // Monsters + case mtCaveSpider: + case mtCreeper: + case mtEnderman: + case mtGhast: + case mtGuardian: + case mtSilverfish: + case mtSkeleton: + case mtSpider: + case mtWitch: + case mtZombie: + case mtZombiePigman: + case mtSlime: + case mtMagmaCube: + { + Reward = GetRandomProvider().RandInt(6, 8); + break; + } + case mtBlaze: + { + Reward = 10; + break; + } + + // Bosses + case mtEnderDragon: + { + Reward = 12000; + break; + } + case mtWither: + { + Reward = 50; + break; + } + + default: + { + Reward = 0; + break; + } + } + if ((a_TDI.Attacker != nullptr) && (!IsBaby())) + { + m_World->SpawnExperienceOrb(GetPosX(), GetPosY(), GetPosZ(), Reward); + } + m_DestroyTimer = std::chrono::milliseconds(0); } @@ -659,37 +659,37 @@ void cMonster::KilledBy(TakeDamageInfo & a_TDI) void cMonster::OnRightClicked(cPlayer & a_Player) { - super::OnRightClicked(a_Player); - - const cItem & EquippedItem = a_Player.GetEquippedItem(); - if ((EquippedItem.m_ItemType == E_ITEM_NAME_TAG) && !EquippedItem.m_CustomName.empty()) - { - SetCustomName(EquippedItem.m_CustomName); - if (!a_Player.IsGameModeCreative()) - { - a_Player.GetInventory().RemoveOneEquippedItem(); - } - } - - // Using leashes - m_IsLeashActionJustDone = false; - if (IsLeashed() && (GetLeashedTo() == &a_Player)) // a player can only unleash a mob leashed to him - { - Unleash(!a_Player.IsGameModeCreative()); - } - else if (IsLeashed()) - { - // Mob is already leashed but client anticipates the server action and draws a leash link, so we need to send current leash to cancel it - m_World->BroadcastLeashEntity(*this, *this->GetLeashedTo()); - } - else if (CanBeLeashed() && (EquippedItem.m_ItemType == E_ITEM_LEASH)) - { - if (!a_Player.IsGameModeCreative()) - { - a_Player.GetInventory().RemoveOneEquippedItem(); - } - LeashTo(&a_Player); - } + super::OnRightClicked(a_Player); + + const cItem & EquippedItem = a_Player.GetEquippedItem(); + if ((EquippedItem.m_ItemType == E_ITEM_NAME_TAG) && !EquippedItem.m_CustomName.empty()) + { + SetCustomName(EquippedItem.m_CustomName); + if (!a_Player.IsGameModeCreative()) + { + a_Player.GetInventory().RemoveOneEquippedItem(); + } + } + + // Using leashes + m_IsLeashActionJustDone = false; + if (IsLeashed() && (GetLeashedTo() == &a_Player)) // a player can only unleash a mob leashed to him + { + Unleash(!a_Player.IsGameModeCreative()); + } + else if (IsLeashed()) + { + // Mob is already leashed but client anticipates the server action and draws a leash link, so we need to send current leash to cancel it + m_World->BroadcastLeashEntity(*this, *this->GetLeashedTo()); + } + else if (CanBeLeashed() && (EquippedItem.m_ItemType == E_ITEM_LEASH)) + { + if (!a_Player.IsGameModeCreative()) + { + a_Player.GetInventory().RemoveOneEquippedItem(); + } + LeashTo(&a_Player); + } } @@ -700,13 +700,13 @@ void cMonster::OnRightClicked(cPlayer & a_Player) // monster sez: Do I see the player void cMonster::CheckEventSeePlayer(cChunk & a_Chunk) { - // TODO: Rewrite this to use cWorld's DoWithPlayers() - cPlayer * Closest = m_World->FindClosestPlayer(GetPosition(), static_cast<float>(m_SightDistance), false); + // TODO: Rewrite this to use cWorld's DoWithPlayers() + cPlayer * Closest = m_World->FindClosestPlayer(GetPosition(), static_cast<float>(m_SightDistance), false); - if (Closest != nullptr) - { - EventSeePlayer(Closest, a_Chunk); - } + if (Closest != nullptr) + { + EventSeePlayer(Closest, a_Chunk); + } } @@ -715,17 +715,17 @@ void cMonster::CheckEventSeePlayer(cChunk & a_Chunk) void cMonster::CheckEventLostPlayer(void) { - if (GetTarget() != nullptr) - { - if ((GetTarget()->GetPosition() - GetPosition()).Length() > m_SightDistance) - { - EventLosePlayer(); - } - } - else - { - EventLosePlayer(); - } + if (GetTarget() != nullptr) + { + if ((GetTarget()->GetPosition() - GetPosition()).Length() > m_SightDistance) + { + EventLosePlayer(); + } + } + else + { + EventLosePlayer(); + } } @@ -736,8 +736,8 @@ void cMonster::CheckEventLostPlayer(void) // default to change state to chasing void cMonster::EventSeePlayer(cPlayer * a_SeenPlayer, cChunk & a_Chunk) { - UNUSED(a_Chunk); - SetTarget(a_SeenPlayer); + UNUSED(a_Chunk); + SetTarget(a_SeenPlayer); } @@ -746,8 +746,8 @@ void cMonster::EventSeePlayer(cPlayer * a_SeenPlayer, cChunk & a_Chunk) void cMonster::EventLosePlayer(void) { - SetTarget(nullptr); - m_EMState = IDLE; + SetTarget(nullptr); + m_EMState = IDLE; } @@ -756,51 +756,51 @@ void cMonster::EventLosePlayer(void) void cMonster::InStateIdle(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) { - if (m_PathfinderActivated) - { - return; // Still getting there - } - - m_IdleInterval += a_Dt; - - if (m_IdleInterval > std::chrono::seconds(1)) - { - auto & Random = GetRandomProvider(); - - // At this interval the results are predictable - int rem = Random.RandInt(1, 7); - m_IdleInterval -= std::chrono::seconds(1); // So nothing gets dropped when the server hangs for a few seconds - - Vector3d Dist; - Dist.x = static_cast<double>(Random.RandInt(-5, 5)); - Dist.z = static_cast<double>(Random.RandInt(-5, 5)); - - if ((Dist.SqrLength() > 2) && (rem >= 3)) - { - - Vector3d Destination(GetPosX() + Dist.x, GetPosition().y, GetPosZ() + Dist.z); - - cChunk * Chunk = a_Chunk.GetNeighborChunk(static_cast<int>(Destination.x), static_cast<int>(Destination.z)); - if ((Chunk == nullptr) || !Chunk->IsValid()) - { - return; - } - - BLOCKTYPE BlockType; - NIBBLETYPE BlockMeta; - int RelX = static_cast<int>(Destination.x) - Chunk->GetPosX() * cChunkDef::Width; - int RelZ = static_cast<int>(Destination.z) - Chunk->GetPosZ() * cChunkDef::Width; - int YBelowUs = static_cast<int>(Destination.y) - 1; - if (YBelowUs >= 0) - { - Chunk->GetBlockTypeMeta(RelX, YBelowUs, RelZ, BlockType, BlockMeta); - if (BlockType != E_BLOCK_STATIONARY_WATER) // Idle mobs shouldn't enter water on purpose - { - MoveToPosition(Destination); - } - } - } - } + if (m_PathfinderActivated) + { + return; // Still getting there + } + + m_IdleInterval += a_Dt; + + if (m_IdleInterval > std::chrono::seconds(1)) + { + auto & Random = GetRandomProvider(); + + // At this interval the results are predictable + int rem = Random.RandInt(1, 7); + m_IdleInterval -= std::chrono::seconds(1); // So nothing gets dropped when the server hangs for a few seconds + + Vector3d Dist; + Dist.x = static_cast<double>(Random.RandInt(-5, 5)); + Dist.z = static_cast<double>(Random.RandInt(-5, 5)); + + if ((Dist.SqrLength() > 2) && (rem >= 3)) + { + + Vector3d Destination(GetPosX() + Dist.x, GetPosition().y, GetPosZ() + Dist.z); + + cChunk * Chunk = a_Chunk.GetNeighborChunk(static_cast<int>(Destination.x), static_cast<int>(Destination.z)); + if ((Chunk == nullptr) || !Chunk->IsValid()) + { + return; + } + + BLOCKTYPE BlockType; + NIBBLETYPE BlockMeta; + int RelX = static_cast<int>(Destination.x) - Chunk->GetPosX() * cChunkDef::Width; + int RelZ = static_cast<int>(Destination.z) - Chunk->GetPosZ() * cChunkDef::Width; + int YBelowUs = static_cast<int>(Destination.y) - 1; + if (YBelowUs >= 0) + { + Chunk->GetBlockTypeMeta(RelX, YBelowUs, RelZ, BlockType, BlockMeta); + if (BlockType != E_BLOCK_STATIONARY_WATER) // Idle mobs shouldn't enter water on purpose + { + MoveToPosition(Destination); + } + } + } + } } @@ -811,7 +811,7 @@ void cMonster::InStateIdle(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) // This state should always be defined in each child class void cMonster::InStateChasing(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) { - UNUSED(a_Dt); + UNUSED(a_Dt); } @@ -821,19 +821,19 @@ void cMonster::InStateChasing(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) // What to do if in Escaping State void cMonster::InStateEscaping(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) { - UNUSED(a_Dt); - - if (GetTarget() != nullptr) - { - Vector3d newloc = GetPosition(); - newloc.x = (GetTarget()->GetPosition().x < newloc.x)? (newloc.x + m_SightDistance): (newloc.x - m_SightDistance); - newloc.z = (GetTarget()->GetPosition().z < newloc.z)? (newloc.z + m_SightDistance): (newloc.z - m_SightDistance); - MoveToPosition(newloc); - } - else - { - m_EMState = IDLE; // This shouldnt be required but just to be safe - } + UNUSED(a_Dt); + + if (GetTarget() != nullptr) + { + Vector3d newloc = GetPosition(); + newloc.x = (GetTarget()->GetPosition().x < newloc.x)? (newloc.x + m_SightDistance): (newloc.x - m_SightDistance); + newloc.z = (GetTarget()->GetPosition().z < newloc.z)? (newloc.z + m_SightDistance): (newloc.z - m_SightDistance); + MoveToPosition(newloc); + } + else + { + m_EMState = IDLE; // This shouldnt be required but just to be safe + } } @@ -842,7 +842,7 @@ void cMonster::InStateEscaping(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) void cMonster::ResetAttackCooldown() { - m_AttackCoolDownTicksLeft = static_cast<int>(3 * 20 * m_AttackRate); // A second has 20 ticks, an attack rate of 1 means 1 hit every 3 seconds + m_AttackCoolDownTicksLeft = static_cast<int>(3 * 20 * m_AttackRate); // A second has 20 ticks, an attack rate of 1 means 1 hit every 3 seconds } @@ -851,18 +851,18 @@ void cMonster::ResetAttackCooldown() void cMonster::SetCustomName(const AString & a_CustomName) { - m_CustomName = a_CustomName; - - // The maximal length is 64 - if (a_CustomName.length() > 64) - { - m_CustomName = a_CustomName.substr(0, 64); - } - - if (m_World != nullptr) - { - m_World->BroadcastEntityMetadata(*this); - } + m_CustomName = a_CustomName; + + // The maximal length is 64 + if (a_CustomName.length() > 64) + { + m_CustomName = a_CustomName.substr(0, 64); + } + + if (m_World != nullptr) + { + m_World->BroadcastEntityMetadata(*this); + } } @@ -871,11 +871,11 @@ void cMonster::SetCustomName(const AString & a_CustomName) void cMonster::SetCustomNameAlwaysVisible(bool a_CustomNameAlwaysVisible) { - m_CustomNameAlwaysVisible = a_CustomNameAlwaysVisible; - if (m_World != nullptr) - { - m_World->BroadcastEntityMetadata(*this); - } + m_CustomNameAlwaysVisible = a_CustomNameAlwaysVisible; + if (m_World != nullptr) + { + m_World->BroadcastEntityMetadata(*this); + } } @@ -884,7 +884,7 @@ void cMonster::SetCustomNameAlwaysVisible(bool a_CustomNameAlwaysVisible) void cMonster::GetMonsterConfig(const AString & a_Name) { - cRoot::Get()->GetMonsterConfig()->AssignAttributes(this, a_Name); + cRoot::Get()->GetMonsterConfig()->AssignAttributes(this, a_Name); } @@ -893,7 +893,7 @@ void cMonster::GetMonsterConfig(const AString & a_Name) bool cMonster::IsUndead(void) { - return false; + return false; } @@ -902,17 +902,17 @@ bool cMonster::IsUndead(void) AString cMonster::MobTypeToString(eMonsterType a_MobType) { - // Mob types aren't sorted, so we need to search linearly: - for (size_t i = 0; i < ARRAYCOUNT(g_MobTypeNames); i++) - { - if (g_MobTypeNames[i].m_Type == a_MobType) - { - return g_MobTypeNames[i].m_lcName; - } - } - - // Not found: - return ""; + // Mob types aren't sorted, so we need to search linearly: + for (size_t i = 0; i < ARRAYCOUNT(g_MobTypeNames); i++) + { + if (g_MobTypeNames[i].m_Type == a_MobType) + { + return g_MobTypeNames[i].m_lcName; + } + } + + // Not found: + return ""; } @@ -921,17 +921,17 @@ AString cMonster::MobTypeToString(eMonsterType a_MobType) AString cMonster::MobTypeToVanillaName(eMonsterType a_MobType) { - // Mob types aren't sorted, so we need to search linearly: - for (size_t i = 0; i < ARRAYCOUNT(g_MobTypeNames); i++) - { - if (g_MobTypeNames[i].m_Type == a_MobType) - { - return g_MobTypeNames[i].m_VanillaName; - } - } - - // Not found: - return ""; + // Mob types aren't sorted, so we need to search linearly: + for (size_t i = 0; i < ARRAYCOUNT(g_MobTypeNames); i++) + { + if (g_MobTypeNames[i].m_Type == a_MobType) + { + return g_MobTypeNames[i].m_VanillaName; + } + } + + // Not found: + return ""; } @@ -940,17 +940,17 @@ AString cMonster::MobTypeToVanillaName(eMonsterType a_MobType) AString cMonster::MobTypeToVanillaNBT(eMonsterType a_MobType) { - // Mob types aren't sorted, so we need to search linearly: - for (size_t i = 0; i < ARRAYCOUNT(g_MobTypeNames); i++) - { - if (g_MobTypeNames[i].m_Type == a_MobType) - { - return g_MobTypeNames[i].m_VanillaNameNBT; - } - } - - // Not found: - return ""; + // Mob types aren't sorted, so we need to search linearly: + for (size_t i = 0; i < ARRAYCOUNT(g_MobTypeNames); i++) + { + if (g_MobTypeNames[i].m_Type == a_MobType) + { + return g_MobTypeNames[i].m_VanillaNameNBT; + } + } + + // Not found: + return ""; } @@ -959,37 +959,37 @@ AString cMonster::MobTypeToVanillaNBT(eMonsterType a_MobType) eMonsterType cMonster::StringToMobType(const AString & a_Name) { - AString lcName = StrToLower(a_Name); - - // Search Cuberite name: - for (size_t i = 0; i < ARRAYCOUNT(g_MobTypeNames); i++) - { - if (strcmp(g_MobTypeNames[i].m_lcName, lcName.c_str()) == 0) - { - return g_MobTypeNames[i].m_Type; - } - } - - // Not found. Search Vanilla name: - for (size_t i = 0; i < ARRAYCOUNT(g_MobTypeNames); i++) - { - if (strcmp(StrToLower(g_MobTypeNames[i].m_VanillaName).c_str(), lcName.c_str()) == 0) - { - return g_MobTypeNames[i].m_Type; - } - } - - // Search in NBT name - for (size_t i = 0; i < ARRAYCOUNT(g_MobTypeNames); i++) - { - if (strcmp(StrToLower(g_MobTypeNames[i].m_VanillaNameNBT).c_str(), lcName.c_str()) == 0) - { - return g_MobTypeNames[i].m_Type; - } - } - - // Not found: - return mtInvalidType; + AString lcName = StrToLower(a_Name); + + // Search Cuberite name: + for (size_t i = 0; i < ARRAYCOUNT(g_MobTypeNames); i++) + { + if (strcmp(g_MobTypeNames[i].m_lcName, lcName.c_str()) == 0) + { + return g_MobTypeNames[i].m_Type; + } + } + + // Not found. Search Vanilla name: + for (size_t i = 0; i < ARRAYCOUNT(g_MobTypeNames); i++) + { + if (strcmp(StrToLower(g_MobTypeNames[i].m_VanillaName).c_str(), lcName.c_str()) == 0) + { + return g_MobTypeNames[i].m_Type; + } + } + + // Search in NBT name + for (size_t i = 0; i < ARRAYCOUNT(g_MobTypeNames); i++) + { + if (strcmp(StrToLower(g_MobTypeNames[i].m_VanillaNameNBT).c_str(), lcName.c_str()) == 0) + { + return g_MobTypeNames[i].m_Type; + } + } + + // Not found: + return mtInvalidType; } @@ -998,46 +998,46 @@ eMonsterType cMonster::StringToMobType(const AString & a_Name) cMonster::eFamily cMonster::FamilyFromType(eMonsterType a_Type) { - // Passive-agressive mobs are counted in mob spawning code as passive - - switch (a_Type) - { - case mtBat: return mfAmbient; - case mtBlaze: return mfHostile; - case mtCaveSpider: return mfHostile; - case mtChicken: return mfPassive; - case mtCow: return mfPassive; - case mtCreeper: return mfHostile; - case mtEnderDragon: return mfNoSpawn; - case mtEnderman: return mfHostile; - case mtGhast: return mfHostile; - case mtGiant: return mfNoSpawn; - case mtGuardian: return mfWater; // Just because they have special spawning conditions. If Watertemples have been added, this needs to be edited! - case mtHorse: return mfPassive; - case mtIronGolem: return mfPassive; - case mtMagmaCube: return mfHostile; - case mtMooshroom: return mfHostile; - case mtOcelot: return mfPassive; - case mtPig: return mfPassive; - case mtRabbit: return mfPassive; - case mtSheep: return mfPassive; - case mtSilverfish: return mfHostile; - case mtSkeleton: return mfHostile; - case mtSlime: return mfHostile; - case mtSnowGolem: return mfNoSpawn; - case mtSpider: return mfHostile; - case mtSquid: return mfWater; - case mtVillager: return mfPassive; - case mtWitch: return mfHostile; - case mtWither: return mfNoSpawn; - case mtWolf: return mfHostile; - case mtZombie: return mfHostile; - case mtZombiePigman: return mfHostile; - - case mtInvalidType: break; - } - ASSERT(!"Unhandled mob type"); - return mfUnhandled; + // Passive-agressive mobs are counted in mob spawning code as passive + + switch (a_Type) + { + case mtBat: return mfAmbient; + case mtBlaze: return mfHostile; + case mtCaveSpider: return mfHostile; + case mtChicken: return mfPassive; + case mtCow: return mfPassive; + case mtCreeper: return mfHostile; + case mtEnderDragon: return mfNoSpawn; + case mtEnderman: return mfHostile; + case mtGhast: return mfHostile; + case mtGiant: return mfNoSpawn; + case mtGuardian: return mfWater; // Just because they have special spawning conditions. If Watertemples have been added, this needs to be edited! + case mtHorse: return mfPassive; + case mtIronGolem: return mfPassive; + case mtMagmaCube: return mfHostile; + case mtMooshroom: return mfHostile; + case mtOcelot: return mfPassive; + case mtPig: return mfPassive; + case mtRabbit: return mfPassive; + case mtSheep: return mfPassive; + case mtSilverfish: return mfHostile; + case mtSkeleton: return mfHostile; + case mtSlime: return mfHostile; + case mtSnowGolem: return mfNoSpawn; + case mtSpider: return mfHostile; + case mtSquid: return mfWater; + case mtVillager: return mfPassive; + case mtWitch: return mfHostile; + case mtWither: return mfNoSpawn; + case mtWolf: return mfHostile; + case mtZombie: return mfHostile; + case mtZombiePigman: return mfHostile; + + case mtInvalidType: break; + } + ASSERT(!"Unhandled mob type"); + return mfUnhandled; } @@ -1046,17 +1046,17 @@ cMonster::eFamily cMonster::FamilyFromType(eMonsterType a_Type) int cMonster::GetSpawnDelay(cMonster::eFamily a_MobFamily) { - switch (a_MobFamily) - { - case mfHostile: return 40; - case mfPassive: return 40; - case mfAmbient: return 40; - case mfWater: return 400; - case mfNoSpawn: return -1; - case mfUnhandled: break; - } - ASSERT(!"Unhandled mob family"); - return -1; + switch (a_MobFamily) + { + case mfHostile: return 40; + case mfPassive: return 40; + case mfAmbient: return 40; + case mfWater: return 400; + case mfNoSpawn: return -1; + case mfUnhandled: break; + } + ASSERT(!"Unhandled mob family"); + return -1; } @@ -1067,27 +1067,27 @@ int cMonster::GetSpawnDelay(cMonster::eFamily a_MobFamily) /** Sets the target. */ void cMonster::SetTarget (cPawn * a_NewTarget) { - ASSERT((a_NewTarget == nullptr) || (IsTicking())); - if (m_Target == a_NewTarget) - { - return; - } - cPawn * OldTarget = m_Target; - m_Target = a_NewTarget; - - if (OldTarget != nullptr) - { - // Notify the old target that we are no longer targeting it. - OldTarget->NoLongerTargetingMe(this); - } - - if (a_NewTarget != nullptr) - { - ASSERT(a_NewTarget->IsTicking()); - // Notify the new target that we are now targeting it. - m_Target->TargetingMe(this); - m_WasLastTargetAPlayer = m_Target->IsPlayer(); - } + ASSERT((a_NewTarget == nullptr) || (IsTicking())); + if (m_Target == a_NewTarget) + { + return; + } + cPawn * OldTarget = m_Target; + m_Target = a_NewTarget; + + if (OldTarget != nullptr) + { + // Notify the old target that we are no longer targeting it. + OldTarget->NoLongerTargetingMe(this); + } + + if (a_NewTarget != nullptr) + { + ASSERT(a_NewTarget->IsTicking()); + // Notify the new target that we are now targeting it. + m_Target->TargetingMe(this); + m_WasLastTargetAPlayer = m_Target->IsPlayer(); + } } @@ -1097,7 +1097,7 @@ void cMonster::SetTarget (cPawn * a_NewTarget) void cMonster::UnsafeUnsetTarget() { - m_Target = nullptr; + m_Target = nullptr; } @@ -1106,7 +1106,72 @@ void cMonster::UnsafeUnsetTarget() cPawn * cMonster::GetTarget() { - return m_Target; + return m_Target; +} + + + + + +cBehaviorAggressive * cMonster::GetBehaviorAggressive() +{ + return nullptr; +} + + + + + +cBehaviorBreeder * cMonster::GetBehaviorBreeder() +{ + return nullptr; +} + + + + + +const cBehaviorBreeder * cMonster::GetBehaviorBreeder() const +{ + return nullptr; +} + + + + + +cBehaviorChaser * cMonster::GetBehaviorChaser() +{ + return nullptr; +} + + + + + +cBehaviorStriker * cMonster::GetBehaviorStriker() +{ + return nullptr; +} + + + + + +cBehaviorWanderer * cMonster::GetBehaviorWanderer() +{ + return nullptr; +} + + + + + +void cMonster::InheritFromParents(cMonster * a_Parent1, cMonster * a_Parent2) +{ + UNUSED(a_Parent1); + UNUSED(a_Parent2); + return; } @@ -1115,84 +1180,84 @@ cPawn * cMonster::GetTarget() std::unique_ptr<cMonster> cMonster::NewMonsterFromType(eMonsterType a_MobType) { - auto & Random = GetRandomProvider(); - - // Create the mob entity - switch (a_MobType) - { - case mtMagmaCube: - { - return cpp14::make_unique<cMagmaCube>(1 << Random.RandInt(2)); // Size 1, 2 or 4 - } - case mtSlime: - { - return cpp14::make_unique<cSlime>(1 << Random.RandInt(2)); // Size 1, 2 or 4 - } - case mtSkeleton: - { - // TODO: Actual detection of spawning in Nether - return cpp14::make_unique<cSkeleton>(false); - } - case mtVillager: - { - int VillagerType = Random.RandInt(6); - if (VillagerType == 6) - { - // Give farmers a better chance of spawning - VillagerType = 0; - } - - return cpp14::make_unique<cVillager>(static_cast<cVillager::eVillagerType>(VillagerType)); - } - case mtHorse: - { - // Horses take a type (species), a colour, and a style (dots, stripes, etc.) - int HorseType = Random.RandInt(7); - int HorseColor = Random.RandInt(6); - int HorseStyle = Random.RandInt(4); - int HorseTameTimes = Random.RandInt(1, 6); - - if ((HorseType == 5) || (HorseType == 6) || (HorseType == 7)) - { - // Increase chances of normal horse (zero) - HorseType = 0; - } - - return cpp14::make_unique<cHorse>(HorseType, HorseColor, HorseStyle, HorseTameTimes); - } - - case mtBat: return cpp14::make_unique<cBat>(); - case mtBlaze: return cpp14::make_unique<cBlaze>(); - case mtCaveSpider: return cpp14::make_unique<cCaveSpider>(); - case mtChicken: return cpp14::make_unique<cChicken>(); - case mtCow: return cpp14::make_unique<cCow>(); - case mtCreeper: return cpp14::make_unique < cCreeper>(); - case mtEnderDragon: return cpp14::make_unique<cEnderDragon>(); - case mtEnderman: return cpp14::make_unique<cEnderman>(); - case mtGhast: return cpp14::make_unique<cGhast>(); - case mtGiant: return cpp14::make_unique<cGiant>(); - case mtGuardian: return cpp14::make_unique<cGuardian>(); - case mtIronGolem: return cpp14::make_unique<cIronGolem>(); - case mtMooshroom: return cpp14::make_unique<cMooshroom>(); - case mtOcelot: return cpp14::make_unique<cOcelot>(); - case mtPig: return cpp14::make_unique<cPig>(); - case mtRabbit: return cpp14::make_unique<cRabbit>(); - case mtSheep: return cpp14::make_unique<cSheep>(); - case mtSilverfish: return cpp14::make_unique<cSilverfish>(); - case mtSnowGolem: return cpp14::make_unique<cSnowGolem>(); - case mtSpider: return cpp14::make_unique<cSpider>(); - case mtSquid: return cpp14::make_unique<cSquid>(); - case mtWitch: return cpp14::make_unique<cWitch>(); - case mtWither: return cpp14::make_unique<cWither>(); - case mtWolf: return cpp14::make_unique<cWolf>(); - case mtZombie: return cpp14::make_unique<cZombie>(false); // TODO: Infected zombie parameter - case mtZombiePigman: return cpp14::make_unique<cZombiePigman>(); - default: - { - ASSERT(!"Unhandled mob type whilst trying to spawn mob!"); - return nullptr; - } - } + auto & Random = GetRandomProvider(); + + // Create the mob entity + switch (a_MobType) + { + case mtMagmaCube: + { + return cpp14::make_unique<cMagmaCube>(1 << Random.RandInt(2)); // Size 1, 2 or 4 + } + case mtSlime: + { + return cpp14::make_unique<cSlime>(1 << Random.RandInt(2)); // Size 1, 2 or 4 + } + case mtSkeleton: + { + // TODO: Actual detection of spawning in Nether + return cpp14::make_unique<cSkeleton>(false); + } + case mtVillager: + { + int VillagerType = Random.RandInt(6); + if (VillagerType == 6) + { + // Give farmers a better chance of spawning + VillagerType = 0; + } + + return cpp14::make_unique<cVillager>(static_cast<cVillager::eVillagerType>(VillagerType)); + } + case mtHorse: + { + // Horses take a type (species), a colour, and a style (dots, stripes, etc.) + int HorseType = Random.RandInt(7); + int HorseColor = Random.RandInt(6); + int HorseStyle = Random.RandInt(4); + int HorseTameTimes = Random.RandInt(1, 6); + + if ((HorseType == 5) || (HorseType == 6) || (HorseType == 7)) + { + // Increase chances of normal horse (zero) + HorseType = 0; + } + + return cpp14::make_unique<cHorse>(HorseType, HorseColor, HorseStyle, HorseTameTimes); + } + + case mtBat: return cpp14::make_unique<cBat>(); + case mtBlaze: return cpp14::make_unique<cBlaze>(); + case mtCaveSpider: return cpp14::make_unique<cCaveSpider>(); + case mtChicken: return cpp14::make_unique<cChicken>(); + case mtCow: return cpp14::make_unique<cCow>(); + case mtCreeper: return cpp14::make_unique < cCreeper>(); + case mtEnderDragon: return cpp14::make_unique<cEnderDragon>(); + case mtEnderman: return cpp14::make_unique<cEnderman>(); + case mtGhast: return cpp14::make_unique<cGhast>(); + case mtGiant: return cpp14::make_unique<cGiant>(); + case mtGuardian: return cpp14::make_unique<cGuardian>(); + case mtIronGolem: return cpp14::make_unique<cIronGolem>(); + case mtMooshroom: return cpp14::make_unique<cMooshroom>(); + case mtOcelot: return cpp14::make_unique<cOcelot>(); + case mtPig: return cpp14::make_unique<cPig>(); + case mtRabbit: return cpp14::make_unique<cRabbit>(); + case mtSheep: return cpp14::make_unique<cSheep>(); + case mtSilverfish: return cpp14::make_unique<cSilverfish>(); + case mtSnowGolem: return cpp14::make_unique<cSnowGolem>(); + case mtSpider: return cpp14::make_unique<cSpider>(); + case mtSquid: return cpp14::make_unique<cSquid>(); + case mtWitch: return cpp14::make_unique<cWitch>(); + case mtWither: return cpp14::make_unique<cWither>(); + case mtWolf: return cpp14::make_unique<cWolf>(); + case mtZombie: return cpp14::make_unique<cZombie>(false); // TODO: Infected zombie parameter + case mtZombiePigman: return cpp14::make_unique<cZombiePigman>(); + default: + { + ASSERT(!"Unhandled mob type whilst trying to spawn mob!"); + return nullptr; + } + } } @@ -1201,17 +1266,17 @@ std::unique_ptr<cMonster> cMonster::NewMonsterFromType(eMonsterType a_MobType) void cMonster::AddRandomDropItem(cItems & a_Drops, unsigned int a_Min, unsigned int a_Max, short a_Item, short a_ItemHealth) { - auto Count = GetRandomProvider().RandInt<unsigned int>(a_Min, a_Max); - auto MaxStackSize = static_cast<unsigned char>(ItemHandler(a_Item)->GetMaxStackSize()); - while (Count > MaxStackSize) - { - a_Drops.emplace_back(a_Item, MaxStackSize, a_ItemHealth); - Count -= MaxStackSize; - } - if (Count > 0) - { - a_Drops.emplace_back(a_Item, Count, a_ItemHealth); - } + auto Count = GetRandomProvider().RandInt<unsigned int>(a_Min, a_Max); + auto MaxStackSize = static_cast<unsigned char>(ItemHandler(a_Item)->GetMaxStackSize()); + while (Count > MaxStackSize) + { + a_Drops.emplace_back(a_Item, MaxStackSize, a_ItemHealth); + Count -= MaxStackSize; + } + if (Count > 0) + { + a_Drops.emplace_back(a_Item, Count, a_ItemHealth); + } } @@ -1220,10 +1285,10 @@ void cMonster::AddRandomDropItem(cItems & a_Drops, unsigned int a_Min, unsigned void cMonster::AddRandomUncommonDropItem(cItems & a_Drops, float a_Chance, short a_Item, short a_ItemHealth) { - if (GetRandomProvider().RandBool(a_Chance / 100.0)) - { - a_Drops.push_back(cItem(a_Item, 1, a_ItemHealth)); - } + if (GetRandomProvider().RandBool(a_Chance / 100.0)) + { + a_Drops.push_back(cItem(a_Item, 1, a_ItemHealth)); + } } @@ -1232,12 +1297,12 @@ void cMonster::AddRandomUncommonDropItem(cItems & a_Drops, float a_Chance, short void cMonster::AddRandomRareDropItem(cItems & a_Drops, cItems & a_Items, unsigned int a_LootingLevel) { - auto & r1 = GetRandomProvider(); - if (r1.RandBool((5 + a_LootingLevel) / 200.0)) - { - size_t Rare = r1.RandInt<size_t>(a_Items.Size() - 1); - a_Drops.push_back(a_Items.at(Rare)); - } + auto & r1 = GetRandomProvider(); + if (r1.RandBool((5 + a_LootingLevel) / 200.0)) + { + size_t Rare = r1.RandInt<size_t>(a_Items.Size() - 1); + a_Drops.push_back(a_Items.at(Rare)); + } } @@ -1246,41 +1311,41 @@ void cMonster::AddRandomRareDropItem(cItems & a_Drops, cItems & a_Items, unsigne void cMonster::AddRandomArmorDropItem(cItems & a_Drops, unsigned int a_LootingLevel) { - auto & r1 = GetRandomProvider(); - - double LootingBonus = a_LootingLevel / 100.0; - - if (r1.RandBool(m_DropChanceHelmet + LootingBonus)) - { - if (!GetEquippedHelmet().IsEmpty()) - { - a_Drops.push_back(GetEquippedHelmet()); - } - } - - if (r1.RandBool(m_DropChanceChestplate + LootingBonus)) - { - if (!GetEquippedChestplate().IsEmpty()) - { - a_Drops.push_back(GetEquippedChestplate()); - } - } - - if (r1.RandBool(m_DropChanceLeggings + LootingBonus)) - { - if (!GetEquippedLeggings().IsEmpty()) - { - a_Drops.push_back(GetEquippedLeggings()); - } - } - - if (r1.RandBool(m_DropChanceBoots + LootingBonus)) - { - if (!GetEquippedBoots().IsEmpty()) - { - a_Drops.push_back(GetEquippedBoots()); - } - } + auto & r1 = GetRandomProvider(); + + double LootingBonus = a_LootingLevel / 100.0; + + if (r1.RandBool(m_DropChanceHelmet + LootingBonus)) + { + if (!GetEquippedHelmet().IsEmpty()) + { + a_Drops.push_back(GetEquippedHelmet()); + } + } + + if (r1.RandBool(m_DropChanceChestplate + LootingBonus)) + { + if (!GetEquippedChestplate().IsEmpty()) + { + a_Drops.push_back(GetEquippedChestplate()); + } + } + + if (r1.RandBool(m_DropChanceLeggings + LootingBonus)) + { + if (!GetEquippedLeggings().IsEmpty()) + { + a_Drops.push_back(GetEquippedLeggings()); + } + } + + if (r1.RandBool(m_DropChanceBoots + LootingBonus)) + { + if (!GetEquippedBoots().IsEmpty()) + { + a_Drops.push_back(GetEquippedBoots()); + } + } } @@ -1289,13 +1354,13 @@ void cMonster::AddRandomArmorDropItem(cItems & a_Drops, unsigned int a_LootingLe void cMonster::AddRandomWeaponDropItem(cItems & a_Drops, unsigned int a_LootingLevel) { - if (GetRandomProvider().RandBool(m_DropChanceWeapon + (a_LootingLevel / 100.0))) - { - if (!GetEquippedWeapon().IsEmpty()) - { - a_Drops.push_back(GetEquippedWeapon()); - } - } + if (GetRandomProvider().RandBool(m_DropChanceWeapon + (a_LootingLevel / 100.0))) + { + if (!GetEquippedWeapon().IsEmpty()) + { + a_Drops.push_back(GetEquippedWeapon()); + } + } } @@ -1304,28 +1369,28 @@ void cMonster::AddRandomWeaponDropItem(cItems & a_Drops, unsigned int a_LootingL 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; - } - if (!a_Chunk.IsLightValid()) - { - m_World->QueueLightChunk(GetChunkX(), GetChunkZ()); - return; - } - - if (!IsOnFire() && WouldBurn) - { - // Burn for 100 ticks, then decide again - StartBurning(100); - } + if (!m_BurnsInDaylight) + { + return; + } + + int RelY = POSY_TOINT; + if ((RelY < 0) || (RelY >= cChunkDef::Height)) + { + // Outside the world + return; + } + if (!a_Chunk.IsLightValid()) + { + m_World->QueueLightChunk(GetChunkX(), GetChunkZ()); + return; + } + + if (!IsOnFire() && WouldBurn) + { + // Burn for 100 ticks, then decide again + StartBurning(100); + } } @@ -1333,65 +1398,65 @@ void cMonster::HandleDaylightBurning(cChunk & a_Chunk, bool WouldBurn) bool cMonster::WouldBurnAt(Vector3d a_Location, cChunk & a_Chunk) { - // If the Y coord is out of range, return the most logical result without considering anything else: - int RelY = FloorC(a_Location.y); - if (RelY < 0) - { - // Never burn under the world - return false; - } - if (RelY >= cChunkDef::Height) - { - // Always burn above the world - return true; - } - if (RelY <= 0) - { - // The mob is about to die, no point in burning - return false; - } - - PREPARE_REL_AND_CHUNK(a_Location, a_Chunk); - if (!RelSuccess) - { - return false; - } - - if ( - (Chunk->GetBlock(Rel.x, Rel.y, Rel.z) != E_BLOCK_SOULSAND) && // Not on soulsand - (GetWorld()->GetTimeOfDay() < 12000 + 1000) && // Daytime - GetWorld()->IsWeatherSunnyAt(POSX_TOINT, POSZ_TOINT) // Not raining - ) - { - int MobHeight = FloorC(a_Location.y + GetHeight()) - 1; // The block Y coord of the mob's head - if (MobHeight >= cChunkDef::Height) - { - return true; - } - // Start with the highest block and scan down to the mob's head. - // If a non transparent is found, return false (do not burn). Otherwise return true. - // Note that this loop is not a performance concern as transparent blocks are rare and the loop almost always bailes out - // instantly.(An exception is e.g. standing under a long column of glass). - int CurrentBlock = Chunk->GetHeight(Rel.x, Rel.z); - while (CurrentBlock >= MobHeight) - { - BLOCKTYPE Block = Chunk->GetBlock(Rel.x, CurrentBlock, Rel.z); - if ( - // Do not burn if a block above us meets one of the following conditions: - (!cBlockInfo::IsTransparent(Block)) || - (Block == E_BLOCK_LEAVES) || - (Block == E_BLOCK_NEW_LEAVES) || - (IsBlockWater(Block)) - ) - { - return false; - } - --CurrentBlock; - } - return true; - - } - return false; + // If the Y coord is out of range, return the most logical result without considering anything else: + int RelY = FloorC(a_Location.y); + if (RelY < 0) + { + // Never burn under the world + return false; + } + if (RelY >= cChunkDef::Height) + { + // Always burn above the world + return true; + } + if (RelY <= 0) + { + // The mob is about to die, no point in burning + return false; + } + + PREPARE_REL_AND_CHUNK(a_Location, a_Chunk); + if (!RelSuccess) + { + return false; + } + + if ( + (Chunk->GetBlock(Rel.x, Rel.y, Rel.z) != E_BLOCK_SOULSAND) && // Not on soulsand + (GetWorld()->GetTimeOfDay() < 12000 + 1000) && // Daytime + GetWorld()->IsWeatherSunnyAt(POSX_TOINT, POSZ_TOINT) // Not raining + ) + { + int MobHeight = FloorC(a_Location.y + GetHeight()) - 1; // The block Y coord of the mob's head + if (MobHeight >= cChunkDef::Height) + { + return true; + } + // Start with the highest block and scan down to the mob's head. + // If a non transparent is found, return false (do not burn). Otherwise return true. + // Note that this loop is not a performance concern as transparent blocks are rare and the loop almost always bailes out + // instantly.(An exception is e.g. standing under a long column of glass). + int CurrentBlock = Chunk->GetHeight(Rel.x, Rel.z); + while (CurrentBlock >= MobHeight) + { + BLOCKTYPE Block = Chunk->GetBlock(Rel.x, CurrentBlock, Rel.z); + if ( + // Do not burn if a block above us meets one of the following conditions: + (!cBlockInfo::IsTransparent(Block)) || + (Block == E_BLOCK_LEAVES) || + (Block == E_BLOCK_NEW_LEAVES) || + (IsBlockWater(Block)) + ) + { + return false; + } + --CurrentBlock; + } + return true; + + } + return false; } @@ -1400,7 +1465,7 @@ bool cMonster::WouldBurnAt(Vector3d a_Location, cChunk & a_Chunk) cMonster::eFamily cMonster::GetMobFamily(void) const { - return FamilyFromType(m_MobType); + return FamilyFromType(m_MobType); } @@ -1409,22 +1474,22 @@ cMonster::eFamily cMonster::GetMobFamily(void) const void cMonster::LeashTo(cEntity * a_Entity, bool a_ShouldBroadcast) { - // Do nothing if already leashed - if (m_LeashedTo != nullptr) - { - return; - } + // Do nothing if already leashed + if (m_LeashedTo != nullptr) + { + return; + } - m_LeashedTo = a_Entity; + m_LeashedTo = a_Entity; - a_Entity->AddLeashedMob(this); + a_Entity->AddLeashedMob(this); - if (a_ShouldBroadcast) - { - m_World->BroadcastLeashEntity(*this, *a_Entity); - } + if (a_ShouldBroadcast) + { + m_World->BroadcastLeashEntity(*this, *a_Entity); + } - m_IsLeashActionJustDone = true; + m_IsLeashActionJustDone = true; } @@ -1433,29 +1498,29 @@ void cMonster::LeashTo(cEntity * a_Entity, bool a_ShouldBroadcast) void cMonster::Unleash(bool a_ShouldDropLeashPickup, bool a_ShouldBroadcast) { - // Do nothing if not leashed - if (m_LeashedTo == nullptr) - { - return; - } + // Do nothing if not leashed + if (m_LeashedTo == nullptr) + { + return; + } - m_LeashedTo->RemoveLeashedMob(this); + m_LeashedTo->RemoveLeashedMob(this); - m_LeashedTo = nullptr; + m_LeashedTo = nullptr; - if (a_ShouldDropLeashPickup) - { - cItems Pickups; - Pickups.Add(cItem(E_ITEM_LEASH, 1, 0)); - GetWorld()->SpawnItemPickups(Pickups, GetPosX() + 0.5, GetPosY() + 0.5, GetPosZ() + 0.5); - } + if (a_ShouldDropLeashPickup) + { + cItems Pickups; + Pickups.Add(cItem(E_ITEM_LEASH, 1, 0)); + GetWorld()->SpawnItemPickups(Pickups, GetPosX() + 0.5, GetPosY() + 0.5, GetPosZ() + 0.5); + } - if (a_ShouldBroadcast) - { - m_World->BroadcastUnleashEntity(*this); - } + if (a_ShouldBroadcast) + { + m_World->BroadcastUnleashEntity(*this); + } - m_IsLeashActionJustDone = true; + m_IsLeashActionJustDone = true; } @@ -1464,5 +1529,5 @@ void cMonster::Unleash(bool a_ShouldDropLeashPickup, bool a_ShouldBroadcast) void cMonster::Unleash(bool a_ShouldDropLeashPickup) { - Unleash(a_ShouldDropLeashPickup, true); + Unleash(a_ShouldDropLeashPickup, true); } diff --git a/src/Mobs/Monster.h b/src/Mobs/Monster.h index ab5b2cf2f..3bb34974b 100644 --- a/src/Mobs/Monster.h +++ b/src/Mobs/Monster.h @@ -13,320 +13,331 @@ class cClientHandle; // tolua_begin class cMonster : - public cPawn + public cPawn { - typedef cPawn super; + typedef cPawn super; public: - enum eFamily - { - mfHostile = 0, // Spider, Zombies ... - mfPassive = 1, // Cows, Pigs - mfAmbient = 2, // Bats - mfWater = 3, // Squid, Guardian + enum eFamily + { + mfHostile = 0, // Spider, Zombies ... + mfPassive = 1, // Cows, Pigs + mfAmbient = 2, // Bats + mfWater = 3, // Squid, Guardian - mfNoSpawn, - mfUnhandled, // Nothing. Be sure this is the last and the others are in order - } ; + mfNoSpawn, + mfUnhandled, // Nothing. Be sure this is the last and the others are in order + } ; - // tolua_end + // tolua_end - enum MState{ATTACKING, IDLE, CHASING, ESCAPING} m_EMState; - enum MPersonality{PASSIVE, AGGRESSIVE, COWARDLY} m_EMPersonality; + enum MState{ATTACKING, IDLE, CHASING, ESCAPING} m_EMState; + enum MPersonality{PASSIVE, AGGRESSIVE, COWARDLY} m_EMPersonality; - /** Creates the mob object. - If a_ConfigName is not empty, the configuration is loaded using GetMonsterConfig() - a_MobType is the type of the mob (also used in the protocol ( http://wiki.vg/Entities#Mobs 2012_12_22)) - a_SoundHurt and a_SoundDeath are assigned into m_SoundHurt and m_SoundDeath, respectively - */ - cMonster(const AString & a_ConfigName, eMonsterType a_MobType, const AString & a_SoundHurt, const AString & a_SoundDeath, double a_Width, double a_Height); + /** Creates the mob object. + If a_ConfigName is not empty, the configuration is loaded using GetMonsterConfig() + a_MobType is the type of the mob (also used in the protocol ( http://wiki.vg/Entities#Mobs 2012_12_22)) + a_SoundHurt and a_SoundDeath are assigned into m_SoundHurt and m_SoundDeath, respectively + */ + cMonster(const AString & a_ConfigName, eMonsterType a_MobType, const AString & a_SoundHurt, const AString & a_SoundDeath, double a_Width, double a_Height); - virtual ~cMonster() override; + virtual ~cMonster() override; - virtual void Destroy(bool a_ShouldBroadcast = true) override; + virtual void Destroy(bool a_ShouldBroadcast = true) override; - virtual void Destroyed() override; + virtual void Destroyed() override; - CLASS_PROTODEF(cMonster) + CLASS_PROTODEF(cMonster) - virtual void SpawnOn(cClientHandle & a_ClientHandle) override; + virtual void SpawnOn(cClientHandle & a_ClientHandle) override; - virtual void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override; + virtual void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override; - virtual bool DoTakeDamage(TakeDamageInfo & a_TDI) override; + virtual bool DoTakeDamage(TakeDamageInfo & a_TDI) override; - virtual void KilledBy(TakeDamageInfo & a_TDI) override; + virtual void KilledBy(TakeDamageInfo & a_TDI) override; - virtual void OnRightClicked(cPlayer & a_Player) override; + virtual void OnRightClicked(cPlayer & a_Player) override; - virtual void HandleFalling(void) override; + virtual void HandleFalling(void) override; - /** Engage pathfinder and tell it to calculate a path to a given position, and move the mob 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 + /** Engage pathfinder and tell it to calculate a path to a given position, and move the mob 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 - // tolua_begin - eMonsterType GetMobType(void) const { return m_MobType; } - eFamily GetMobFamily(void) const; - // tolua_end + // tolua_begin + eMonsterType GetMobType(void) const { return m_MobType; } + eFamily GetMobFamily(void) const; + // tolua_end - virtual void CheckEventSeePlayer(cChunk & a_Chunk); - virtual void EventSeePlayer(cPlayer * a_Player, cChunk & a_Chunk); + virtual void CheckEventSeePlayer(cChunk & a_Chunk); + virtual void EventSeePlayer(cPlayer * a_Player, cChunk & a_Chunk); - // tolua_begin + // tolua_begin - /** Returns whether the mob can be leashed. */ - bool CanBeLeashed() const { return m_CanBeLeashed; } + /** Returns whether the mob can be leashed. */ + bool CanBeLeashed() const { return m_CanBeLeashed; } - /** Sets whether the mob can be leashed, for extensibility in plugins */ - void SetCanBeLeashed(bool a_CanBeLeashed) { m_CanBeLeashed = a_CanBeLeashed; } + /** Sets whether the mob can be leashed, for extensibility in plugins */ + void SetCanBeLeashed(bool a_CanBeLeashed) { m_CanBeLeashed = a_CanBeLeashed; } - /** Returns whether the monster is leashed to an entity. */ - bool IsLeashed() const { return (m_LeashedTo != nullptr); } + /** Returns whether the monster is leashed to an entity. */ + bool IsLeashed() const { return (m_LeashedTo != nullptr); } - /** Leash the monster to an entity. */ - void LeashTo(cEntity * a_Entity, bool a_ShouldBroadcast = true); + /** Leash the monster to an entity. */ + void LeashTo(cEntity * a_Entity, bool a_ShouldBroadcast = true); - /** Unleash the monster. Overload for the Unleash(bool, bool) function for plugins */ - void Unleash(bool a_ShouldDropLeashPickup); + /** Unleash the monster. Overload for the Unleash(bool, bool) function for plugins */ + void Unleash(bool a_ShouldDropLeashPickup); - /** Returns the entity to where this mob is leashed, returns nullptr if it's not leashed */ - cEntity * GetLeashedTo() const { return m_LeashedTo; } + /** Returns the entity to where this mob is leashed, returns nullptr if it's not leashed */ + cEntity * GetLeashedTo() const { return m_LeashedTo; } - // tolua_end + // tolua_end - /** Unleash the monster. */ - void Unleash(bool a_ShouldDropLeashPickup, bool a_ShouldBroadcast); + /** Unleash the monster. */ + void Unleash(bool a_ShouldDropLeashPickup, bool a_ShouldBroadcast); - /** Sets entity position to where is leashed this mob */ - void SetLeashToPos(Vector3d * pos) { m_LeashToPos = std::unique_ptr<Vector3d>(pos); } + /** Sets entity position to where is leashed this mob */ + void SetLeashToPos(Vector3d * pos) { m_LeashToPos = std::unique_ptr<Vector3d>(pos); } - /** Gets entity position to where mob should be leashed */ - Vector3d * GetLeashToPos() const { return m_LeashToPos.get(); } + /** Gets entity position to where mob should be leashed */ + Vector3d * GetLeashToPos() const { return m_LeashToPos.get(); } - /** Reads the monster configuration for the specified monster name and assigns it to this object. */ - void GetMonsterConfig(const AString & a_Name); + /** Reads the monster configuration for the specified monster name and assigns it to this object. */ + void GetMonsterConfig(const AString & a_Name); - /** Returns whether this mob is undead (skeleton, zombie, etc.) */ - virtual bool IsUndead(void); + /** Returns whether this mob is undead (skeleton, zombie, etc.) */ + virtual bool IsUndead(void); - virtual void EventLosePlayer(void); - virtual void CheckEventLostPlayer(void); + virtual void EventLosePlayer(void); + virtual void CheckEventLostPlayer(void); - virtual void InStateIdle (std::chrono::milliseconds a_Dt, cChunk & a_Chunk); - virtual void InStateChasing (std::chrono::milliseconds a_Dt, cChunk & a_Chunk); - virtual void InStateEscaping(std::chrono::milliseconds a_Dt, cChunk & a_Chunk); + virtual void InStateIdle (std::chrono::milliseconds a_Dt, cChunk & a_Chunk); + virtual void InStateChasing (std::chrono::milliseconds a_Dt, cChunk & a_Chunk); + virtual void InStateEscaping(std::chrono::milliseconds a_Dt, cChunk & a_Chunk); - int GetAttackRate() { return static_cast<int>(m_AttackRate); } - void SetAttackRate(float a_AttackRate) { m_AttackRate = a_AttackRate; } - void SetAttackRange(int a_AttackRange) { m_AttackRange = a_AttackRange; } - void SetAttackDamage(int a_AttackDamage) { m_AttackDamage = a_AttackDamage; } - void SetSightDistance(int a_SightDistance) { m_SightDistance = a_SightDistance; } + int GetAttackRate() { return static_cast<int>(m_AttackRate); } + void SetAttackRate(float a_AttackRate) { m_AttackRate = a_AttackRate; } + void SetAttackRange(int a_AttackRange) { m_AttackRange = a_AttackRange; } + void SetAttackDamage(int a_AttackDamage) { m_AttackDamage = a_AttackDamage; } + void SetSightDistance(int a_SightDistance) { m_SightDistance = a_SightDistance; } - float GetDropChanceWeapon() { return m_DropChanceWeapon; } - float GetDropChanceHelmet() { return m_DropChanceHelmet; } - float GetDropChanceChestplate() { return m_DropChanceChestplate; } - float GetDropChanceLeggings() { return m_DropChanceLeggings; } - float GetDropChanceBoots() { return m_DropChanceBoots; } - bool CanPickUpLoot() { return m_CanPickUpLoot; } - void SetDropChanceWeapon(float a_DropChanceWeapon) { m_DropChanceWeapon = a_DropChanceWeapon; } - void SetDropChanceHelmet(float a_DropChanceHelmet) { m_DropChanceHelmet = a_DropChanceHelmet; } - void SetDropChanceChestplate(float a_DropChanceChestplate) { m_DropChanceChestplate = a_DropChanceChestplate; } - void SetDropChanceLeggings(float a_DropChanceLeggings) { m_DropChanceLeggings = a_DropChanceLeggings; } - void SetDropChanceBoots(float a_DropChanceBoots) { m_DropChanceBoots = a_DropChanceBoots; } - void SetCanPickUpLoot(bool a_CanPickUpLoot) { m_CanPickUpLoot = a_CanPickUpLoot; } - void ResetAttackCooldown(); + float GetDropChanceWeapon() { return m_DropChanceWeapon; } + float GetDropChanceHelmet() { return m_DropChanceHelmet; } + float GetDropChanceChestplate() { return m_DropChanceChestplate; } + float GetDropChanceLeggings() { return m_DropChanceLeggings; } + float GetDropChanceBoots() { return m_DropChanceBoots; } + bool CanPickUpLoot() { return m_CanPickUpLoot; } + void SetDropChanceWeapon(float a_DropChanceWeapon) { m_DropChanceWeapon = a_DropChanceWeapon; } + void SetDropChanceHelmet(float a_DropChanceHelmet) { m_DropChanceHelmet = a_DropChanceHelmet; } + void SetDropChanceChestplate(float a_DropChanceChestplate) { m_DropChanceChestplate = a_DropChanceChestplate; } + void SetDropChanceLeggings(float a_DropChanceLeggings) { m_DropChanceLeggings = a_DropChanceLeggings; } + void SetDropChanceBoots(float a_DropChanceBoots) { m_DropChanceBoots = a_DropChanceBoots; } + void SetCanPickUpLoot(bool a_CanPickUpLoot) { m_CanPickUpLoot = a_CanPickUpLoot; } + void ResetAttackCooldown(); - /** Sets whether the mob burns in daylight. Only evaluated at next burn-decision tick */ - void SetBurnsInDaylight(bool a_BurnsInDaylight) { m_BurnsInDaylight = a_BurnsInDaylight; } + /** Sets whether the mob burns in daylight. Only evaluated at next burn-decision tick */ + void SetBurnsInDaylight(bool a_BurnsInDaylight) { m_BurnsInDaylight = a_BurnsInDaylight; } - double GetRelativeWalkSpeed(void) const { return m_RelativeWalkSpeed; } // tolua_export - void SetRelativeWalkSpeed(double a_WalkSpeed) { m_RelativeWalkSpeed = a_WalkSpeed; } // tolua_export + double GetRelativeWalkSpeed(void) const { return m_RelativeWalkSpeed; } // tolua_export + void SetRelativeWalkSpeed(double a_WalkSpeed) { m_RelativeWalkSpeed = a_WalkSpeed; } // tolua_export - // Overridables to handle ageable mobs - virtual bool IsTame (void) const { return false; } - virtual bool IsSitting (void) const { return false; } + // Overridables to handle ageable mobs + virtual bool IsTame (void) const { return false; } + virtual bool IsSitting (void) const { return false; } - // tolua_begin - bool IsBaby (void) const { return m_Age < 0; } - int GetAge (void) const { return m_Age; } - void SetAge(int a_Age) { m_Age = a_Age; } - // tolua_end + // tolua_begin + bool IsBaby (void) const { return m_Age < 0; } + int GetAge (void) const { return m_Age; } + void SetAge(int a_Age) { m_Age = a_Age; } + // tolua_end - // tolua_begin + // tolua_begin - /** Returns true if the monster has a custom name. */ - bool HasCustomName(void) const { return !m_CustomName.empty(); } + /** Returns true if the monster has a custom name. */ + bool HasCustomName(void) const { return !m_CustomName.empty(); } - /** Gets the custom name of the monster. If no custom name is set, the function returns an empty string. */ - const AString & GetCustomName(void) const { return m_CustomName; } + /** Gets the custom name of the monster. If no custom name is set, the function returns an empty string. */ + const AString & GetCustomName(void) const { return m_CustomName; } - /** Sets the custom name of the monster. You see the name over the monster. - If you want to disable the custom name, simply set an empty string. */ - void SetCustomName(const AString & a_CustomName); + /** Sets the custom name of the monster. You see the name over the monster. + If you want to disable the custom name, simply set an empty string. */ + void SetCustomName(const AString & a_CustomName); - /** Is the custom name of this monster always visible? If not, you only see the name when you sight the mob. */ - bool IsCustomNameAlwaysVisible(void) const { return m_CustomNameAlwaysVisible; } + /** Is the custom name of this monster always visible? If not, you only see the name when you sight the mob. */ + bool IsCustomNameAlwaysVisible(void) const { return m_CustomNameAlwaysVisible; } - /** Sets the custom name visiblity of this monster. - If it's false, you only see the name when you sight the mob. If it's true, you always see the custom name. */ - void SetCustomNameAlwaysVisible(bool a_CustomNameAlwaysVisible); + /** Sets the custom name visiblity of this monster. + If it's false, you only see the name when you sight the mob. If it's true, you always see the custom name. */ + void SetCustomNameAlwaysVisible(bool a_CustomNameAlwaysVisible); - /** Translates MobType enum to a string, empty string if unknown */ - static AString MobTypeToString(eMonsterType a_MobType); + /** Translates MobType enum to a string, empty string if unknown */ + static AString MobTypeToString(eMonsterType a_MobType); - /** Translates MobType enum to the vanilla name of the mob, empty string if unknown. */ - static AString MobTypeToVanillaName(eMonsterType a_MobType); + /** Translates MobType enum to the vanilla name of the mob, empty string if unknown. */ + static AString MobTypeToVanillaName(eMonsterType a_MobType); - /** Translates MobType string to the enum, mtInvalidType if not recognized */ - static eMonsterType StringToMobType(const AString & a_MobTypeName); + /** Translates MobType string to the enum, mtInvalidType if not recognized */ + static eMonsterType StringToMobType(const AString & a_MobTypeName); - /** Returns the mob family based on the type */ - static eFamily FamilyFromType(eMonsterType a_MobType); + /** Returns the mob family based on the type */ + static eFamily FamilyFromType(eMonsterType a_MobType); - /** Returns the spawn delay (number of game ticks between spawn attempts) for the given mob family */ - static int GetSpawnDelay(cMonster::eFamily a_MobFamily); + /** Returns the spawn delay (number of game ticks between spawn attempts) for the given mob family */ + static int GetSpawnDelay(cMonster::eFamily a_MobFamily); - // tolua_end + // tolua_end - /** Translates the MobType enum to the vanilla nbt name */ - static AString MobTypeToVanillaNBT(eMonsterType a_MobType); + /** Translates the MobType enum to the vanilla nbt name */ + static AString MobTypeToVanillaNBT(eMonsterType a_MobType); - /** Sets the target that this mob will chase. Pass a nullptr to unset. */ - void SetTarget (cPawn * a_NewTarget); + /** Sets the target that this mob will chase. Pass a nullptr to unset. */ + void SetTarget (cPawn * a_NewTarget); - /** Unset the target without notifying the target entity. Do not use this, use SetTarget(nullptr) instead. - This is only used by cPawn internally. */ - void UnsafeUnsetTarget(); + /** Unset the target without notifying the target entity. Do not use this, use SetTarget(nullptr) instead. + This is only used by cPawn internally. */ + void UnsafeUnsetTarget(); - /** Returns the current target. */ - cPawn * GetTarget(); + /** Returns the current target. */ + cPawn * GetTarget(); - /** Creates a new object of the specified mob. - a_MobType is the type of the mob to be created - Asserts and returns null if mob type is not specified - */ - static std::unique_ptr<cMonster> NewMonsterFromType(eMonsterType a_MobType); + /** Creates a new object of the specified mob. + a_MobType is the type of the mob to be created + Asserts and returns null if mob type is not specified + */ + static std::unique_ptr<cMonster> NewMonsterFromType(eMonsterType a_MobType); - /** Returns if this mob last target was a player to avoid destruction on player quit */ - bool WasLastTargetAPlayer() const { return m_WasLastTargetAPlayer; } + /** Returns if this mob last target was a player to avoid destruction on player quit */ + bool WasLastTargetAPlayer() const { return m_WasLastTargetAPlayer; } + + // Behavior getters + virtual cBehaviorAggressive * GetBehaviorAggressive(); + virtual cBehaviorBreeder * GetBehaviorBreeder(); + virtual const cBehaviorBreeder * GetBehaviorBreeder() const; + virtual cBehaviorChaser * GetBehaviorChaser(); + virtual cBehaviorStriker * GetBehaviorStriker(); + virtual cBehaviorWanderer * GetBehaviorWanderer(); + + // Polymorphic behavior functions + virtual void InheritFromParents(cMonster * a_Parent1, cMonster * a_Parent2); protected: - /** The pathfinder instance handles pathfinding for this monster. */ - cPathFinder m_PathFinder; + /** The pathfinder instance handles pathfinding for this monster. */ + cPathFinder m_PathFinder; - /** Stores if pathfinder is being used - set when final destination is set, and unset when stopped moving to final destination */ - bool m_PathfinderActivated; + /** Stores if pathfinder is being used - set when final destination is set, and unset when stopped moving to final destination */ + bool m_PathfinderActivated; - /** Coordinates of the next position that should be reached */ - Vector3d m_NextWayPointPosition; + /** Coordinates of the next position that should be reached */ + Vector3d m_NextWayPointPosition; - /** Coordinates for the ultimate, final destination. */ - Vector3d m_FinalDestination; + /** Coordinates for the ultimate, final destination. */ + Vector3d m_FinalDestination; - /** 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. - If no suitable position is found, returns cChunkDef::Height. */ - int FindFirstNonAirBlockPosition(double a_PosX, double a_PosZ); + /** 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. + If no suitable position is found, returns cChunkDef::Height. */ + int FindFirstNonAirBlockPosition(double a_PosX, double a_PosZ); - /** Returns if the ultimate, final destination has been reached. */ - bool ReachedFinalDestination(void) { return ((m_FinalDestination - GetPosition()).SqrLength() < WAYPOINT_RADIUS * WAYPOINT_RADIUS); } + /** Returns if the ultimate, final destination has been reached. */ + bool ReachedFinalDestination(void) { return ((m_FinalDestination - GetPosition()).SqrLength() < WAYPOINT_RADIUS * WAYPOINT_RADIUS); } - /** Returns whether or not the target is close enough for attack. */ - bool TargetIsInRange(void) - { - ASSERT(GetTarget() != nullptr); - return ((GetTarget()->GetPosition() - GetPosition()).SqrLength() < (m_AttackRange * m_AttackRange)); - } + /** Returns whether or not the target is close enough for attack. */ + bool TargetIsInRange(void) + { + ASSERT(GetTarget() != nullptr); + return ((GetTarget()->GetPosition() - GetPosition()).SqrLength() < (m_AttackRange * m_AttackRange)); + } - /** Returns whether the monster needs to jump to reach a given height. */ - inline bool DoesPosYRequireJump(double a_PosY) - { - return (a_PosY > GetPosY() + 0.8); // Assume that differences up to 0.8 blocks can be walked instead of jumped - } + /** Returns whether the monster needs to jump to reach a given height. */ + inline bool DoesPosYRequireJump(double a_PosY) + { + return (a_PosY > GetPosY() + 0.8); // Assume that differences up to 0.8 blocks can be walked instead of jumped + } - /** Move in a straight line to the next waypoint in the path, will jump if needed. */ - void MoveToWayPoint(cChunk & a_Chunk); + /** Move in a straight line to the next waypoint in the path, will jump if needed. */ + void MoveToWayPoint(cChunk & a_Chunk); - /** Stops pathfinding. Calls ResetPathFinding and sets m_IsFollowingPath to false */ - void StopMovingToPosition(); + /** Stops pathfinding. Calls ResetPathFinding and sets m_IsFollowingPath to false */ + void StopMovingToPosition(); - /** Sets the body yaw and head yaw */ - void SetPitchAndYawFromDestination(bool a_IsFollowingPath); + /** Sets the body yaw and head yaw */ + void SetPitchAndYawFromDestination(bool a_IsFollowingPath); - int m_JumpCoolDown; + int m_JumpCoolDown; - std::chrono::milliseconds m_IdleInterval; - std::chrono::milliseconds m_DestroyTimer; + std::chrono::milliseconds m_IdleInterval; + std::chrono::milliseconds m_DestroyTimer; - eMonsterType m_MobType; - AString m_CustomName; - bool m_CustomNameAlwaysVisible; + eMonsterType m_MobType; + AString m_CustomName; + bool m_CustomNameAlwaysVisible; - AString m_SoundHurt; - AString m_SoundDeath; + AString m_SoundHurt; + AString m_SoundDeath; - float m_AttackRate; - int m_AttackDamage; - int m_AttackRange; - int m_AttackCoolDownTicksLeft; - int m_SightDistance; + float m_AttackRate; + int m_AttackDamage; + int m_AttackRange; + int m_AttackCoolDownTicksLeft; + int m_SightDistance; - float m_DropChanceWeapon; - float m_DropChanceHelmet; - float m_DropChanceChestplate; - float m_DropChanceLeggings; - float m_DropChanceBoots; - bool m_CanPickUpLoot; - int m_TicksSinceLastDamaged; // How many ticks ago we were last damaged by a player? + float m_DropChanceWeapon; + float m_DropChanceHelmet; + float m_DropChanceChestplate; + float m_DropChanceLeggings; + float m_DropChanceBoots; + bool m_CanPickUpLoot; + int m_TicksSinceLastDamaged; // How many ticks ago we were last damaged by a player? - void HandleDaylightBurning(cChunk & a_Chunk, bool WouldBurn); - bool WouldBurnAt(Vector3d a_Location, cChunk & a_Chunk); - bool m_BurnsInDaylight; - double m_RelativeWalkSpeed; + void HandleDaylightBurning(cChunk & a_Chunk, bool WouldBurn); + bool WouldBurnAt(Vector3d a_Location, cChunk & a_Chunk); + bool m_BurnsInDaylight; + double m_RelativeWalkSpeed; - int m_Age; - int m_AgingTimer; + int m_Age; + int m_AgingTimer; - bool m_WasLastTargetAPlayer; + bool m_WasLastTargetAPlayer; - /** Entity leashed to */ - cEntity * m_LeashedTo; + /** Entity leashed to */ + cEntity * m_LeashedTo; - /** Entity pos where this mob was leashed to. Used when deserializing the chunk in order to make the mob find the leash knot. */ - std::unique_ptr<Vector3d> m_LeashToPos; + /** Entity pos where this mob was leashed to. Used when deserializing the chunk in order to make the mob find the leash knot. */ + std::unique_ptr<Vector3d> m_LeashToPos; - /** Mob has ben leashed or unleashed in current player action. Avoids double actions on horses. */ - bool m_IsLeashActionJustDone; + /** Mob has ben leashed or unleashed in current player action. Avoids double actions on horses. */ + bool m_IsLeashActionJustDone; - /** Determines whether a monster can be leashed */ - bool m_CanBeLeashed; + /** Determines whether a monster can be leashed */ + bool m_CanBeLeashed; - /** Adds a random number of a_Item between a_Min and a_Max to itemdrops a_Drops */ - void AddRandomDropItem(cItems & a_Drops, unsigned int a_Min, unsigned int a_Max, short a_Item, short a_ItemHealth = 0); + /** Adds a random number of a_Item between a_Min and a_Max to itemdrops a_Drops */ + void AddRandomDropItem(cItems & a_Drops, unsigned int a_Min, unsigned int a_Max, short a_Item, short a_ItemHealth = 0); - /** Adds a item a_Item with the chance of a_Chance (in percent) to itemdrops a_Drops */ - void AddRandomUncommonDropItem(cItems & a_Drops, float a_Chance, short a_Item, short a_ItemHealth = 0); + /** Adds a item a_Item with the chance of a_Chance (in percent) to itemdrops a_Drops */ + void AddRandomUncommonDropItem(cItems & a_Drops, float a_Chance, short a_Item, short a_ItemHealth = 0); - /** Adds one rare item out of the list of rare items a_Items modified by the looting level a_LootingLevel(I-III or custom) to the itemdrop a_Drops */ - void AddRandomRareDropItem(cItems & a_Drops, cItems & a_Items, unsigned int a_LootingLevel); + /** Adds one rare item out of the list of rare items a_Items modified by the looting level a_LootingLevel(I-III or custom) to the itemdrop a_Drops */ + void AddRandomRareDropItem(cItems & a_Drops, cItems & a_Items, unsigned int a_LootingLevel); - /** Adds armor that is equipped with the chance saved in m_DropChance[...] (this will be greter than 1 if picked up or 0.085 + (0.01 per LootingLevel) if born with) to the drop */ - void AddRandomArmorDropItem(cItems & a_Drops, unsigned int a_LootingLevel); + /** Adds armor that is equipped with the chance saved in m_DropChance[...] (this will be greter than 1 if picked up or 0.085 + (0.01 per LootingLevel) if born with) to the drop */ + void AddRandomArmorDropItem(cItems & a_Drops, unsigned int a_LootingLevel); - /** Adds weapon that is equipped with the chance saved in m_DropChance[...] (this will be greter than 1 if picked up or 0.085 + (0.01 per LootingLevel) if born with) to the drop */ - void AddRandomWeaponDropItem(cItems & a_Drops, unsigned int a_LootingLevel); + /** Adds weapon that is equipped with the chance saved in m_DropChance[...] (this will be greter than 1 if picked up or 0.085 + (0.01 per LootingLevel) if born with) to the drop */ + void AddRandomWeaponDropItem(cItems & a_Drops, unsigned int a_LootingLevel); private: - /** A pointer to the entity this mobile is aiming to reach. - The validity of this pointer SHALL be guaranteed by the pointee; - it MUST be reset when the pointee changes worlds or is destroyed. */ - cPawn * m_Target; + /** A pointer to the entity this mobile is aiming to reach. + The validity of this pointer SHALL be guaranteed by the pointee; + it MUST be reset when the pointee changes worlds or is destroyed. */ + cPawn * m_Target; - /** Leash calculations inside Tick function */ - void CalcLeashActions(); + /** Leash calculations inside Tick function */ + void CalcLeashActions(); } ; // tolua_export |