diff options
Diffstat (limited to 'src/Mobs')
53 files changed, 563 insertions, 217 deletions
diff --git a/src/Mobs/AggressiveMonster.cpp b/src/Mobs/AggressiveMonster.cpp index 447bf3549..5f5b1853d 100644 --- a/src/Mobs/AggressiveMonster.cpp +++ b/src/Mobs/AggressiveMonster.cpp @@ -42,7 +42,7 @@ void cAggressiveMonster::InStateChasing(float a_Dt) MoveToPosition(m_Target->GetPosition()); } } -} +} @@ -95,12 +95,14 @@ void cAggressiveMonster::Attack(float a_Dt) { m_AttackInterval += a_Dt * m_AttackRate; - if ((m_Target != NULL) && (m_AttackInterval > 3.0)) + if ((m_Target == NULL) || (m_AttackInterval < 3.0)) { - // Setting this higher gives us more wiggle room for attackrate - m_AttackInterval = 0.0; - m_Target->TakeDamage(dtMobAttack, this, m_AttackDamage, 0); + return; } + + // Setting this higher gives us more wiggle room for attackrate + m_AttackInterval = 0.0; + m_Target->TakeDamage(dtMobAttack, this, m_AttackDamage, 0); } @@ -108,14 +110,13 @@ void cAggressiveMonster::Attack(float a_Dt) bool cAggressiveMonster::IsMovingToTargetPosition() { - static const float epsilon = 0.000000000001f; // Difference between destination x and target x is negligible (to 10^-12 precision) - if (fabsf((float)m_FinalDestination.x - (float)m_Target->GetPosX()) < epsilon) + if (fabsf((float)m_FinalDestination.x - (float)m_Target->GetPosX()) < std::numeric_limits<float>::epsilon()) { return false; } // Difference between destination z and target z is negligible (to 10^-12 precision) - else if (fabsf((float)m_FinalDestination.z - (float)m_Target->GetPosZ()) > epsilon) + else if (fabsf((float)m_FinalDestination.z - (float)m_Target->GetPosZ()) > std::numeric_limits<float>::epsilon()) { return false; } diff --git a/src/Mobs/Bat.cpp b/src/Mobs/Bat.cpp index 1417ddd9e..c072d4f48 100644 --- a/src/Mobs/Bat.cpp +++ b/src/Mobs/Bat.cpp @@ -7,8 +7,7 @@ cBat::cBat(void) : - // TODO: The size is only a guesstimate, measure in vanilla and fix the size values here - super("Bat", mtBat, "mob.bat.hurt", "mob.bat.death", 0.7, 0.7) + super("Bat", mtBat, "mob.bat.hurt", "mob.bat.death", 0.5, 0.9) { } diff --git a/src/Mobs/Bat.h b/src/Mobs/Bat.h index e878d0ee8..6b06aeb4f 100644 --- a/src/Mobs/Bat.h +++ b/src/Mobs/Bat.h @@ -15,7 +15,7 @@ class cBat : public: cBat(void); - CLASS_PROTODEF(cBat); + CLASS_PROTODEF(cBat) bool IsHanging(void) const {return false; } } ; diff --git a/src/Mobs/Blaze.cpp b/src/Mobs/Blaze.cpp index 326b42f07..b4104d530 100644 --- a/src/Mobs/Blaze.cpp +++ b/src/Mobs/Blaze.cpp @@ -9,8 +9,7 @@ cBlaze::cBlaze(void) : - // TODO: The size is only a guesstimate, measure in vanilla and fix the size values here - super("Blaze", mtBlaze, "mob.blaze.hit", "mob.blaze.death", 0.7, 1.8) + super("Blaze", mtBlaze, "mob.blaze.hit", "mob.blaze.death", 0.6, 1.8) { } @@ -45,9 +44,10 @@ void cBlaze::Attack(float a_Dt) { return; } - if (!FireCharge->Initialize(m_World)) + if (!FireCharge->Initialize(*m_World)) { delete FireCharge; + FireCharge = NULL; return; } m_World->BroadcastSpawnEntity(*FireCharge); diff --git a/src/Mobs/Blaze.h b/src/Mobs/Blaze.h index 5970451c7..f283b1070 100644 --- a/src/Mobs/Blaze.h +++ b/src/Mobs/Blaze.h @@ -15,7 +15,7 @@ class cBlaze : public: cBlaze(void); - CLASS_PROTODEF(cBlaze); + CLASS_PROTODEF(cBlaze) virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override; virtual void Attack(float a_Dt) override; diff --git a/src/Mobs/CMakeLists.txt b/src/Mobs/CMakeLists.txt index 53c265803..2c092c15f 100644 --- a/src/Mobs/CMakeLists.txt +++ b/src/Mobs/CMakeLists.txt @@ -4,9 +4,75 @@ project (MCServer) include_directories ("${PROJECT_SOURCE_DIR}/../") -file(GLOB SOURCE - "*.cpp" - "*.h" -) +SET (SRCS + AggressiveMonster.cpp + Bat.cpp + Blaze.cpp + CaveSpider.cpp + Chicken.cpp + Cow.cpp + Creeper.cpp + EnderDragon.cpp + Enderman.cpp + Ghast.cpp + Giant.cpp + Horse.cpp + IronGolem.cpp + MagmaCube.cpp + Monster.cpp + Mooshroom.cpp + PassiveAggressiveMonster.cpp + PassiveMonster.cpp + Pig.cpp + Sheep.cpp + Skeleton.cpp + Slime.cpp + SnowGolem.cpp + Spider.cpp + Squid.cpp + Villager.cpp + Witch.cpp + Wither.cpp + Wolf.cpp + Zombie.cpp + ZombiePigman.cpp) + +SET (HDRS + AggressiveMonster.h + Bat.h + Blaze.h + CaveSpider.h + Chicken.h + Cow.h + Creeper.h + EnderDragon.h + Enderman.h + Ghast.h + Giant.h + Horse.h + IncludeAllMonsters.h + IronGolem.h + MagmaCube.h + Monster.h + Mooshroom.h + Ocelot.h + PassiveAggressiveMonster.h + PassiveMonster.h + Pig.h + Sheep.h + Silverfish.h + Skeleton.h + Slime.h + SnowGolem.h + Spider.h + Squid.h + Villager.h + Witch.h + Wither.h + Wolf.h + Zombie.h + ZombiePigman.h) -add_library(Mobs ${SOURCE}) +if(NOT MSVC) + add_library(Mobs ${SRCS} ${HDRS}) +endif() diff --git a/src/Mobs/CaveSpider.cpp b/src/Mobs/CaveSpider.cpp index 56ecd2d28..118a6e93b 100644 --- a/src/Mobs/CaveSpider.cpp +++ b/src/Mobs/CaveSpider.cpp @@ -20,7 +20,6 @@ void cCaveSpider::Tick(float a_Dt, cChunk & a_Chunk) { super::Tick(a_Dt, a_Chunk); - // TODO: Check vanilla if cavespiders really get passive during the day / in daylight m_EMPersonality = (GetWorld()->GetTimeOfDay() < (12000 + 1000)) ? PASSIVE : AGGRESSIVE; } @@ -28,6 +27,21 @@ void cCaveSpider::Tick(float a_Dt, cChunk & a_Chunk) +void cCaveSpider::Attack(float a_Dt) +{ + super::Attack(a_Dt); + + if (m_Target->IsPawn()) + { + // TODO: Easy = no poison, Medium = 7 seconds, Hard = 15 seconds + ((cPawn *) m_Target)->AddEntityEffect(cEntityEffect::effPoison, 7 * 20, 0); + } +} + + + + + void cCaveSpider::GetDrops(cItems & a_Drops, cEntity * a_Killer) { int LootingLevel = 0; diff --git a/src/Mobs/CaveSpider.h b/src/Mobs/CaveSpider.h index be9f174f9..f9ed10e1b 100644 --- a/src/Mobs/CaveSpider.h +++ b/src/Mobs/CaveSpider.h @@ -14,9 +14,10 @@ class cCaveSpider : public: cCaveSpider(void); - CLASS_PROTODEF(cCaveSpider); + CLASS_PROTODEF(cCaveSpider) virtual void Tick(float a_Dt, cChunk & a_Chunk) override; + virtual void Attack(float a_Dt) override; virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override; } ; diff --git a/src/Mobs/Chicken.h b/src/Mobs/Chicken.h index a4c1d6b9e..b1a50b61c 100644 --- a/src/Mobs/Chicken.h +++ b/src/Mobs/Chicken.h @@ -14,7 +14,7 @@ class cChicken : public: cChicken(void); - CLASS_PROTODEF(cChicken); + CLASS_PROTODEF(cChicken) virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override; virtual void Tick(float a_Dt, cChunk & a_Chunk) override; diff --git a/src/Mobs/Cow.h b/src/Mobs/Cow.h index 973171ab5..8814b7e09 100644 --- a/src/Mobs/Cow.h +++ b/src/Mobs/Cow.h @@ -15,7 +15,7 @@ class cCow : public: cCow(); - CLASS_PROTODEF(cCow); + CLASS_PROTODEF(cCow) virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override; virtual void OnRightClicked(cPlayer & a_Player) override; diff --git a/src/Mobs/Creeper.cpp b/src/Mobs/Creeper.cpp index 3471b4cf1..02718edf8 100644 --- a/src/Mobs/Creeper.cpp +++ b/src/Mobs/Creeper.cpp @@ -37,13 +37,13 @@ void cCreeper::Tick(float a_Dt, cChunk & a_Chunk) { if (m_bIsBlowing) { - m_ExplodingTimer += 1; + m_ExplodingTimer += 1; } if (m_ExplodingTimer == 30) { m_World->DoExplosionAt((m_bIsCharged ? 5 : 3), GetPosX(), GetPosY(), GetPosZ(), false, esMonster, this); - Destroy(); + Destroy(); // Just in case we aren't killed by the explosion } } } @@ -54,6 +54,12 @@ void cCreeper::Tick(float a_Dt, cChunk & a_Chunk) void cCreeper::GetDrops(cItems & a_Drops, cEntity * a_Killer) { + if (m_ExplodingTimer == 30) + { + // Exploded creepers drop naught but charred flesh, which Minecraft doesn't have + return; + } + int LootingLevel = 0; if (a_Killer != NULL) { @@ -61,11 +67,29 @@ void cCreeper::GetDrops(cItems & a_Drops, cEntity * a_Killer) } AddRandomDropItem(a_Drops, 0, 2 + LootingLevel, E_ITEM_GUNPOWDER); - if ((a_Killer != NULL) && (a_Killer->IsProjectile())) + if ((a_Killer != NULL) && a_Killer->IsProjectile() && (((cProjectileEntity *)a_Killer)->GetCreatorUniqueID() >= 0)) { - if (((cMonster *)((cProjectileEntity *)a_Killer)->GetCreator())->GetMobType() == mtSkeleton) + class cProjectileCreatorCallback : public cEntityCallback { - // 12 music discs. TickRand starts from 0, so range = 11. Disk IDs start at 2256, so add that. There. + public: + cProjectileCreatorCallback(void) + { + } + + virtual bool Item(cEntity * a_Entity) override + { + if (a_Entity->IsMob() && ((cMonster *)a_Entity)->GetMobType() == mtSkeleton) + { + return true; + } + return false; + } + }; + + cProjectileCreatorCallback PCC; + if (GetWorld()->DoWithEntityByID(((cProjectileEntity *)a_Killer)->GetCreatorUniqueID(), PCC)) + { + // 12 music discs. TickRand starts from 0 to 11. Disk IDs start at 2256, so add that. There. AddRandomDropItem(a_Drops, 1, 1, (short)m_World->GetTickRandomNumber(11) + 2256); } } @@ -75,9 +99,12 @@ void cCreeper::GetDrops(cItems & a_Drops, cEntity * a_Killer) -void cCreeper::DoTakeDamage(TakeDamageInfo & a_TDI) +bool cCreeper::DoTakeDamage(TakeDamageInfo & a_TDI) { - super::DoTakeDamage(a_TDI); + if (!super::DoTakeDamage(a_TDI)) + { + return false; + } if (a_TDI.DamageType == dtLightning) { @@ -85,6 +112,7 @@ void cCreeper::DoTakeDamage(TakeDamageInfo & a_TDI) } m_World->BroadcastEntityMetadata(*this); + return true; } @@ -97,7 +125,7 @@ void cCreeper::Attack(float a_Dt) if (!m_bIsBlowing) { - m_World->BroadcastSoundEffect("game.tnt.primed", (int)GetPosX() * 8, (int)GetPosY() * 8, (int)GetPosZ() * 8, 1.f, (float)(0.75 + ((float)((GetUniqueID() * 23) % 32)) / 64)); + m_World->BroadcastSoundEffect("game.tnt.primed", GetPosX(), GetPosY(), GetPosZ(), 1.f, (float)(0.75 + ((float)((GetUniqueID() * 23) % 32)) / 64)); m_bIsBlowing = true; m_World->BroadcastEntityMetadata(*this); } @@ -115,7 +143,7 @@ void cCreeper::OnRightClicked(cPlayer & a_Player) { a_Player.UseEquippedItem(); } - m_World->BroadcastSoundEffect("game.tnt.primed", (int)GetPosX() * 8, (int)GetPosY() * 8, (int)GetPosZ() * 8, 1.f, (float)(0.75 + ((float)((GetUniqueID() * 23) % 32)) / 64)); + m_World->BroadcastSoundEffect("game.tnt.primed", GetPosX(), GetPosY(), GetPosZ(), 1.f, (float)(0.75 + ((float)((GetUniqueID() * 23) % 32)) / 64)); m_bIsBlowing = true; m_World->BroadcastEntityMetadata(*this); m_BurnedWithFlintAndSteel = true; diff --git a/src/Mobs/Creeper.h b/src/Mobs/Creeper.h index 9abca369b..747daca09 100644 --- a/src/Mobs/Creeper.h +++ b/src/Mobs/Creeper.h @@ -15,10 +15,10 @@ class cCreeper : public: cCreeper(void); - CLASS_PROTODEF(cCreeper); + CLASS_PROTODEF(cCreeper) virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override; - virtual void DoTakeDamage(TakeDamageInfo & a_TDI) override; + virtual bool DoTakeDamage(TakeDamageInfo & a_TDI) override; virtual void Attack(float a_Dt) override; virtual void Tick(float a_Dt, cChunk & a_Chunk) override; virtual void OnRightClicked(cPlayer & a_Player) override; diff --git a/src/Mobs/EnderDragon.h b/src/Mobs/EnderDragon.h index 77177edfe..1d4cd657c 100644 --- a/src/Mobs/EnderDragon.h +++ b/src/Mobs/EnderDragon.h @@ -15,7 +15,7 @@ class cEnderDragon : public: cEnderDragon(void); - CLASS_PROTODEF(cEnderDragon); + CLASS_PROTODEF(cEnderDragon) virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override; } ; diff --git a/src/Mobs/Enderman.h b/src/Mobs/Enderman.h index 32e40e70b..aa2eff682 100644 --- a/src/Mobs/Enderman.h +++ b/src/Mobs/Enderman.h @@ -15,7 +15,7 @@ class cEnderman : public: cEnderman(void); - CLASS_PROTODEF(cEnderman); + CLASS_PROTODEF(cEnderman) virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override; diff --git a/src/Mobs/Ghast.cpp b/src/Mobs/Ghast.cpp index d8a7663f8..6aac14779 100644 --- a/src/Mobs/Ghast.cpp +++ b/src/Mobs/Ghast.cpp @@ -46,9 +46,10 @@ void cGhast::Attack(float a_Dt) { return; } - if (!GhastBall->Initialize(m_World)) + if (!GhastBall->Initialize(*m_World)) { delete GhastBall; + GhastBall = NULL; return; } m_World->BroadcastSpawnEntity(*GhastBall); diff --git a/src/Mobs/Ghast.h b/src/Mobs/Ghast.h index 43e8bedb6..1d4e6b94a 100644 --- a/src/Mobs/Ghast.h +++ b/src/Mobs/Ghast.h @@ -15,7 +15,7 @@ class cGhast : public: cGhast(void); - CLASS_PROTODEF(cGhast); + CLASS_PROTODEF(cGhast) virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override; virtual void Attack(float a_Dt) override; diff --git a/src/Mobs/Giant.h b/src/Mobs/Giant.h index 356dd4352..7c04c9b4f 100644 --- a/src/Mobs/Giant.h +++ b/src/Mobs/Giant.h @@ -15,7 +15,7 @@ class cGiant : public: cGiant(void); - CLASS_PROTODEF(cGiant); + CLASS_PROTODEF(cGiant) virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override; } ; diff --git a/src/Mobs/Horse.cpp b/src/Mobs/Horse.cpp index 9d130301f..67a09d4ab 100644 --- a/src/Mobs/Horse.cpp +++ b/src/Mobs/Horse.cpp @@ -63,7 +63,7 @@ void cHorse::Tick(float a_Dt, cChunk & a_Chunk) m_Attachee->Detach(); m_bIsRearing = true; } - } + } else { m_bIsTame = true; diff --git a/src/Mobs/Horse.h b/src/Mobs/Horse.h index be0c23f9b..47189b3b0 100644 --- a/src/Mobs/Horse.h +++ b/src/Mobs/Horse.h @@ -15,7 +15,7 @@ class cHorse : public: cHorse(int Type, int Color, int Style, int TameTimes); - CLASS_PROTODEF(cHorse); + CLASS_PROTODEF(cHorse) virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override; virtual void Tick(float a_Dt, cChunk & a_Chunk) override; diff --git a/src/Mobs/IronGolem.h b/src/Mobs/IronGolem.h index 41c60438c..c5341ed76 100644 --- a/src/Mobs/IronGolem.h +++ b/src/Mobs/IronGolem.h @@ -15,11 +15,11 @@ class cIronGolem : public: cIronGolem(void); - CLASS_PROTODEF(cIronGolem); + CLASS_PROTODEF(cIronGolem) virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override; - // Iron golems do not drown + // Iron golems do not drown nor float virtual void HandleAir(void) override {} virtual void SetSwimState(cChunk & a_Chunk) override {} } ; diff --git a/src/Mobs/MagmaCube.h b/src/Mobs/MagmaCube.h index 43065cae5..bfe63fa2e 100644 --- a/src/Mobs/MagmaCube.h +++ b/src/Mobs/MagmaCube.h @@ -15,7 +15,7 @@ public: /// Creates a MagmaCube of the specified size; size is 1 .. 3, with 1 being the smallest cMagmaCube(int a_Size); - CLASS_PROTODEF(cMagmaCube); + CLASS_PROTODEF(cMagmaCube) virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override; int GetSize(void) const { return m_Size; } diff --git a/src/Mobs/Monster.cpp b/src/Mobs/Monster.cpp index 14d951393..622a67816 100644 --- a/src/Mobs/Monster.cpp +++ b/src/Mobs/Monster.cpp @@ -46,8 +46,8 @@ static const struct {cMonster::mtSheep, "sheep"}, {cMonster::mtSilverfish, "silverfish"}, {cMonster::mtSkeleton, "skeleton"}, - {cMonster::mtSnowGolem, "snowgolem"}, {cMonster::mtSlime, "slime"}, + {cMonster::mtSnowGolem, "snowgolem"}, {cMonster::mtSpider, "spider"}, {cMonster::mtSquid, "squid"}, {cMonster::mtVillager, "villager"}, @@ -62,7 +62,7 @@ static const struct -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// // cMonster: cMonster::cMonster(const AString & a_ConfigName, eType a_MobType, const AString & a_SoundHurt, const AString & a_SoundDeath, double a_Width, double a_Height) @@ -115,12 +115,10 @@ void cMonster::TickPathFinding() const int PosY = POSY_TOINT; const int PosZ = POSZ_TOINT; - m_FinalDestination.y = (double)FindFirstNonAirBlockPosition(m_FinalDestination.x, m_FinalDestination.z); - std::vector<Vector3d> m_PotentialCoordinates; m_TraversedCoordinates.push_back(Vector3i(PosX, PosY, PosZ)); - static const struct // Define which directions to try to move to + static const struct // Define which directions to try to move to { int x, z; } gCrossCoords[] = @@ -128,7 +126,7 @@ void cMonster::TickPathFinding() { 1, 0}, {-1, 0}, { 0, 1}, - { 0,-1}, + { 0, -1}, } ; if ((PosY - 1 < 0) || (PosY + 2 > cChunkDef::Height) /* PosY + 1 will never be true if PosY + 2 is not */) @@ -201,19 +199,6 @@ void cMonster::TickPathFinding() -void cMonster::MoveToPosition(const Vector3f & a_Position) -{ - FinishPathFinding(); - - m_FinalDestination = a_Position; - m_bMovingToDestination = true; - TickPathFinding(); -} - - - - - void cMonster::MoveToPosition(const Vector3d & a_Position) { FinishPathFinding(); @@ -227,15 +212,7 @@ void cMonster::MoveToPosition(const Vector3d & a_Position) bool cMonster::IsCoordinateInTraversedList(Vector3i a_Coords) { - for (std::vector<Vector3i>::const_iterator itr = m_TraversedCoordinates.begin(); itr != m_TraversedCoordinates.end(); ++itr) - { - if (itr->Equals(a_Coords)) - { - return true; - } - } - - return false; + return (std::find(m_TraversedCoordinates.begin(), m_TraversedCoordinates.end(), a_Coords) != m_TraversedCoordinates.end()); } @@ -296,39 +273,47 @@ void cMonster::Tick(float a_Dt, cChunk & a_Chunk) { if (m_bOnGround) { - m_Destination.y = FindFirstNonAirBlockPosition(m_Destination.x, m_Destination.z); - if (DoesPosYRequireJump((int)floor(m_Destination.y))) { m_bOnGround = false; - AddPosY(1.5); // Jump!! + AddSpeedY(5.2); // Jump!! } } Vector3f Distance = m_Destination - GetPosition(); - if(!ReachedDestination() && !ReachedFinalDestination()) // If we haven't reached any sort of destination, move + if (!ReachedDestination() && !ReachedFinalDestination()) // If we haven't reached any sort of destination, move { Distance.y = 0; Distance.Normalize(); - Distance *= 5; - SetSpeedX(Distance.x); - SetSpeedZ(Distance.z); + + if (m_bOnGround) + { + Distance *= 2.5; + } + else + { + // Don't let the mob move too much if he's falling. + Distance *= 0.25; + } + + AddSpeedX(Distance.x); + AddSpeedZ(Distance.z); if (m_EMState == ESCAPING) - { //Runs Faster when escaping :D otherwise they just walk away + { // Runs Faster when escaping :D otherwise they just walk away SetSpeedX (GetSpeedX() * 2.f); SetSpeedZ (GetSpeedZ() * 2.f); } } else { - if (ReachedFinalDestination()) // If we have reached the ultimate, final destination, stop pathfinding and attack if appropriate + 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 + TickPathFinding(); // We have reached the next point in our path, calculate another point } } } @@ -343,18 +328,20 @@ void cMonster::Tick(float a_Dt, cChunk & a_Chunk) // 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 InStateChasing(a_Dt); break; - } + } case ESCAPING: { InStateEscaping(a_Dt); break; } + + case ATTACKING: break; } // switch (m_EMState) BroadcastMovementUpdate(); @@ -423,14 +410,11 @@ void cMonster::HandleFalling() + int cMonster::FindFirstNonAirBlockPosition(double a_PosX, double a_PosZ) { int PosY = POSY_TOINT; - - if (PosY < 0) - PosY = 0; - else if (PosY > cChunkDef::Height) - PosY = cChunkDef::Height; + PosY = Clamp(PosY, 0, cChunkDef::Height); if (!cBlockInfo::IsSolid(m_World->GetBlock((int)floor(a_PosX), PosY, (int)floor(a_PosZ)))) { @@ -457,29 +441,35 @@ int cMonster::FindFirstNonAirBlockPosition(double a_PosX, double a_PosZ) -void cMonster::DoTakeDamage(TakeDamageInfo & a_TDI) +bool cMonster::DoTakeDamage(TakeDamageInfo & a_TDI) { - super::DoTakeDamage(a_TDI); + if (!super::DoTakeDamage(a_TDI)) + { + return false; + } - if((m_SoundHurt != "") && (m_Health > 0)) - m_World->BroadcastSoundEffect(m_SoundHurt, (int)(GetPosX() * 8), (int)(GetPosY() * 8), (int)(GetPosZ() * 8), 1.0f, 0.8f); + if (!m_SoundHurt.empty() && (m_Health > 0)) + { + m_World->BroadcastSoundEffect(m_SoundHurt, GetPosX(), GetPosY(), GetPosZ(), 1.0f, 0.8f); + } if (a_TDI.Attacker != NULL) { m_Target = a_TDI.Attacker; } + return true; } -void cMonster::KilledBy(cEntity * a_Killer) +void cMonster::KilledBy(TakeDamageInfo & a_TDI) { - super::KilledBy(a_Killer); + super::KilledBy(a_TDI); if (m_SoundHurt != "") { - m_World->BroadcastSoundEffect(m_SoundDeath, (int)(GetPosX() * 8), (int)(GetPosY() * 8), (int)(GetPosZ() * 8), 1.0f, 0.8f); + m_World->BroadcastSoundEffect(m_SoundDeath, GetPosX(), GetPosY(), GetPosZ(), 1.0f, 0.8f); } int Reward; switch (m_MobType) @@ -540,7 +530,7 @@ void cMonster::KilledBy(cEntity * a_Killer) break; } } - if ((a_Killer != NULL) && (!IsBaby())) + if ((a_TDI.Attacker != NULL) && (!IsBaby())) { m_World->SpawnExperienceOrb(GetPosX(), GetPosY(), GetPosZ(), Reward); } @@ -551,8 +541,8 @@ void cMonster::KilledBy(cEntity * a_Killer) -//Checks to see if EventSeePlayer should be fired -//monster sez: Do I see the player +// Checks to see if EventSeePlayer should be fired +// monster sez: Do I see the player void cMonster::CheckEventSeePlayer(void) { // TODO: Rewrite this to use cWorld's DoWithPlayers() @@ -569,7 +559,7 @@ void cMonster::CheckEventSeePlayer(void) void cMonster::CheckEventLostPlayer(void) -{ +{ if (m_Target != NULL) { if ((m_Target->GetPosition() - GetPosition()).Length() > m_SightDistance) @@ -612,7 +602,7 @@ void cMonster::InStateIdle(float a_Dt) { if (m_bMovingToDestination) { - return; // Still getting there + return; // Still getting there } m_IdleInterval += a_Dt; @@ -621,7 +611,7 @@ void cMonster::InStateIdle(float a_Dt) { // At this interval the results are predictable int rem = m_World->GetTickRandomNumber(6) + 1; - m_IdleInterval -= 1; // So nothing gets dropped when the server hangs for a few seconds + m_IdleInterval -= 1; // So nothing gets dropped when the server hangs for a few seconds Vector3d Dist; Dist.x = (double)m_World->GetTickRandomNumber(10) - 5; @@ -688,6 +678,15 @@ void cMonster::GetMonsterConfig(const AString & a_Name) +bool cMonster::IsUndead(void) +{ + return false; +} + + + + + AString cMonster::MobTypeToString(cMonster::eType a_MobType) { // Mob types aren't sorted, so we need to search linearly: @@ -823,15 +822,19 @@ cMonster * cMonster::NewMonsterFromType(cMonster::eType a_MobType) switch (a_MobType) { case mtMagmaCube: + { + toReturn = new cMagmaCube(Random.NextInt(2) + 1); + break; + } case mtSlime: { - toReturn = new cSlime(Random.NextInt(2) + 1); + toReturn = new cSlime(1 << Random.NextInt(3)); // Size 1, 2 or 4 break; } case mtSkeleton: { // TODO: Actual detection of spawning in Nether - toReturn = new cSkeleton(Random.NextInt(1) == 0 ? false : true); + toReturn = new cSkeleton((Random.NextInt(1) == 0) ? false : true); break; } case mtVillager: @@ -878,7 +881,7 @@ cMonster * cMonster::NewMonsterFromType(cMonster::eType a_MobType) case mtMooshroom: toReturn = new cMooshroom(); break; case mtOcelot: toReturn = new cOcelot(); break; case mtPig: toReturn = new cPig(); break; - case mtSheep: toReturn = new cSheep (Random.NextInt(15)); break; // Colour parameter + case mtSheep: toReturn = new cSheep(); break; case mtSilverfish: toReturn = new cSilverfish(); break; case mtSnowGolem: toReturn = new cSnowGolem(); break; case mtSpider: toReturn = new cSpider(); break; @@ -886,7 +889,7 @@ cMonster * cMonster::NewMonsterFromType(cMonster::eType a_MobType) case mtWitch: toReturn = new cWitch(); break; case mtWither: toReturn = new cWither(); break; case mtWolf: toReturn = new cWolf(); break; - case mtZombie: toReturn = new cZombie(false); break; // TODO: Infected zombie parameter + case mtZombie: toReturn = new cZombie(false); break; // TODO: Infected zombie parameter case mtZombiePigman: toReturn = new cZombiePigman(); break; default: { @@ -1012,7 +1015,7 @@ void cMonster::HandleDaylightBurning(cChunk & a_Chunk) (a_Chunk.GetBlock(RelX, RelY, RelZ) != E_BLOCK_SOULSAND) && // Not on soulsand (GetWorld()->GetTimeOfDay() < (12000 + 1000)) && // It is nighttime !IsOnFire() && // Not already burning - (GetWorld()->GetWeather() != eWeather_Rain) // Not raining + GetWorld()->IsWeatherWetAt(POSX_TOINT, POSZ_TOINT) // Not raining ) { // Burn for 100 ticks, then decide again diff --git a/src/Mobs/Monster.h b/src/Mobs/Monster.h index 6b9c4fab8..cdbd26c09 100644 --- a/src/Mobs/Monster.h +++ b/src/Mobs/Monster.h @@ -61,39 +61,38 @@ public: enum eFamily { - mfHostile = 0, // Spider, Zombies ... - mfPassive = 1, // Cows, Pigs - mfAmbient = 2, // Bats - mfWater = 3, // Squid + mfHostile = 0, // Spider, Zombies ... + mfPassive = 1, // Cows, Pigs + mfAmbient = 2, // Bats + mfWater = 3, // Squid mfNoSpawn, - mfUnhandled, // Nothing. Be sure this is the last and the others are in order + mfUnhandled, // Nothing. Be sure this is the last and the others are in order } ; // tolua_end enum MState{ATTACKING, IDLE, CHASING, ESCAPING} m_EMState; - enum MPersonality{PASSIVE,AGGRESSIVE,COWARDLY} m_EMPersonality; + 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 + 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, eType a_MobType, const AString & a_SoundHurt, const AString & a_SoundDeath, double a_Width, double a_Height); - CLASS_PROTODEF(cMonster); + CLASS_PROTODEF(cMonster) virtual void SpawnOn(cClientHandle & a_ClientHandle) override; virtual void Tick(float a_Dt, cChunk & a_Chunk) override; - virtual void DoTakeDamage(TakeDamageInfo & a_TDI) override; + virtual bool DoTakeDamage(TakeDamageInfo & a_TDI) override; - virtual void KilledBy(cEntity * a_Killer) override; + virtual void KilledBy(TakeDamageInfo & a_TDI) override; - virtual void MoveToPosition(const Vector3f & a_Position); - virtual void MoveToPosition(const Vector3d & a_Position); // tolua_export + virtual void MoveToPosition(const Vector3d & a_Position); // tolua_export virtual bool ReachedDestination(void); // tolua_begin @@ -107,6 +106,9 @@ public: /// 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); + virtual void EventLosePlayer(void); virtual void CheckEventLostPlayer(void); @@ -178,6 +180,7 @@ protected: /** Stores if mobile is currently moving towards the ultimate, final destination */ bool m_bMovingToDestination; + /** Finds the first 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 */ @@ -229,7 +232,7 @@ protected: AString m_SoundHurt; AString m_SoundDeath; - float m_AttackRate; + float m_AttackRate; int m_AttackDamage; int m_AttackRange; float m_AttackInterval; @@ -246,13 +249,13 @@ protected: bool m_BurnsInDaylight; /** 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); + 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); + 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, short a_LootingLevel); + void AddRandomRareDropItem(cItems & a_Drops, cItems & a_Items, short a_LootingLevel); /** Adds armor that is equipped with the chance saved in m_DropChance[...] (this will be greter than 1 if piccked up or 0.085 + (0.01 per LootingLevel) if born with) to the drop*/ void AddRandomArmorDropItem(cItems & a_Drops, short a_LootingLevel); @@ -261,7 +264,7 @@ protected: void AddRandomWeaponDropItem(cItems & a_Drops, short a_LootingLevel); -} ; // tolua_export +} ; // tolua_export diff --git a/src/Mobs/Mooshroom.h b/src/Mobs/Mooshroom.h index 16f6c8248..fb002c2bf 100644 --- a/src/Mobs/Mooshroom.h +++ b/src/Mobs/Mooshroom.h @@ -15,7 +15,7 @@ class cMooshroom : public: cMooshroom(void); - CLASS_PROTODEF(cMooshroom); + CLASS_PROTODEF(cMooshroom) virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override; virtual void OnRightClicked(cPlayer & a_Player) override; diff --git a/src/Mobs/Ocelot.h b/src/Mobs/Ocelot.h index adb7a1f75..f2727d354 100644 --- a/src/Mobs/Ocelot.h +++ b/src/Mobs/Ocelot.h @@ -18,7 +18,7 @@ public: { } - CLASS_PROTODEF(cOcelot); + CLASS_PROTODEF(cOcelot) } ; diff --git a/src/Mobs/PassiveAggressiveMonster.cpp b/src/Mobs/PassiveAggressiveMonster.cpp index 4b45f9a2a..24501b1ba 100644 --- a/src/Mobs/PassiveAggressiveMonster.cpp +++ b/src/Mobs/PassiveAggressiveMonster.cpp @@ -19,9 +19,12 @@ cPassiveAggressiveMonster::cPassiveAggressiveMonster(const AString & a_ConfigNam -void cPassiveAggressiveMonster::DoTakeDamage(TakeDamageInfo & a_TDI) +bool cPassiveAggressiveMonster::DoTakeDamage(TakeDamageInfo & a_TDI) { - super::DoTakeDamage(a_TDI); + if (!super::DoTakeDamage(a_TDI)) + { + return false; + } if ((m_Target != NULL) && (m_Target->IsPlayer())) { @@ -30,6 +33,7 @@ void cPassiveAggressiveMonster::DoTakeDamage(TakeDamageInfo & a_TDI) m_EMState = CHASING; } } + return true; } diff --git a/src/Mobs/PassiveAggressiveMonster.h b/src/Mobs/PassiveAggressiveMonster.h index 2c5ef30b1..a0da50e8e 100644 --- a/src/Mobs/PassiveAggressiveMonster.h +++ b/src/Mobs/PassiveAggressiveMonster.h @@ -15,7 +15,7 @@ class cPassiveAggressiveMonster : public: cPassiveAggressiveMonster(const AString & a_ConfigName, eType a_MobType, const AString & a_SoundHurt, const AString & a_SoundDeath, double a_Width, double a_Height); - virtual void DoTakeDamage(TakeDamageInfo & a_TDI) override; + virtual bool DoTakeDamage(TakeDamageInfo & a_TDI) override; } ; diff --git a/src/Mobs/PassiveMonster.cpp b/src/Mobs/PassiveMonster.cpp index 904cd63cc..2861d7314 100644 --- a/src/Mobs/PassiveMonster.cpp +++ b/src/Mobs/PassiveMonster.cpp @@ -18,13 +18,17 @@ cPassiveMonster::cPassiveMonster(const AString & a_ConfigName, eType a_MobType, -void cPassiveMonster::DoTakeDamage(TakeDamageInfo & a_TDI) +bool cPassiveMonster::DoTakeDamage(TakeDamageInfo & a_TDI) { - super::DoTakeDamage(a_TDI); + if (!super::DoTakeDamage(a_TDI)) + { + return false; + } if ((a_TDI.Attacker != this) && (a_TDI.Attacker != NULL)) { m_EMState = ESCAPING; } + return true; } diff --git a/src/Mobs/PassiveMonster.h b/src/Mobs/PassiveMonster.h index 0b3c155da..70574585a 100644 --- a/src/Mobs/PassiveMonster.h +++ b/src/Mobs/PassiveMonster.h @@ -18,7 +18,7 @@ public: virtual void Tick(float a_Dt, cChunk & a_Chunk) override; /// When hit by someone, run away - virtual void DoTakeDamage(TakeDamageInfo & a_TDI) override; + virtual bool DoTakeDamage(TakeDamageInfo & a_TDI) override; /** Returns the item that the animal of this class follows when a player holds it in hand Return an empty item not to follow (default). */ virtual const cItem GetFollowedItem(void) const { return cItem(); } diff --git a/src/Mobs/Pig.cpp b/src/Mobs/Pig.cpp index e862f5aaa..1f77cf613 100644 --- a/src/Mobs/Pig.cpp +++ b/src/Mobs/Pig.cpp @@ -78,3 +78,23 @@ void cPig::OnRightClicked(cPlayer & a_Player) + + +void cPig::Tick(float a_Dt, cChunk & a_Chunk) +{ + super::Tick(a_Dt, a_Chunk); + + // If the attachee player is holding a carrot-on-stick, let them drive this pig: + if (m_bIsSaddled && (m_Attachee != NULL)) + { + if (m_Attachee->IsPlayer() && (m_Attachee->GetEquippedWeapon().m_ItemType == E_ITEM_CARROT_ON_STICK)) + { + MoveToPosition((m_Attachee->GetPosition()) + (m_Attachee->GetLookVector()*10)); + m_bMovingToDestination = true; + } + } +} + + + + diff --git a/src/Mobs/Pig.h b/src/Mobs/Pig.h index d434324c1..534a0ca6f 100644 --- a/src/Mobs/Pig.h +++ b/src/Mobs/Pig.h @@ -15,10 +15,11 @@ class cPig : public: cPig(void); - CLASS_PROTODEF(cPig); + CLASS_PROTODEF(cPig) virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override; virtual void OnRightClicked(cPlayer & a_Player) override; + virtual void Tick(float a_Dt, cChunk & a_Chunk) override; virtual const cItem GetFollowedItem(void) const override { return cItem(E_ITEM_CARROT); } diff --git a/src/Mobs/Sheep.cpp b/src/Mobs/Sheep.cpp index 5a6b760af..9fb47201d 100644 --- a/src/Mobs/Sheep.cpp +++ b/src/Mobs/Sheep.cpp @@ -5,6 +5,7 @@ #include "../BlockID.h" #include "../Entities/Player.h" #include "../World.h" +#include "FastRandom.h" @@ -16,6 +17,16 @@ cSheep::cSheep(int a_Color) : m_WoolColor(a_Color), m_TimeToStopEating(-1) { + // Generate random wool color. + if (m_WoolColor == -1) + { + m_WoolColor = GenerateNaturalRandomColor(); + } + + if ((m_WoolColor < 0) || (m_WoolColor > 15)) + { + m_WoolColor = 0; + } } @@ -37,20 +48,17 @@ void cSheep::GetDrops(cItems & a_Drops, cEntity * a_Killer) void cSheep::OnRightClicked(cPlayer & a_Player) { const cItem & EquippedItem = a_Player.GetEquippedItem(); - if ((EquippedItem.m_ItemType == E_ITEM_SHEARS) && (!m_IsSheared)) + if ((EquippedItem.m_ItemType == E_ITEM_SHEARS) && !IsSheared() && !IsBaby()) { m_IsSheared = true; m_World->BroadcastEntityMetadata(*this); - - if (!a_Player.IsGameModeCreative()) - { - a_Player.UseEquippedItem(); - } + a_Player.UseEquippedItem(); cItems Drops; int NumDrops = m_World->GetTickRandomNumber(2) + 1; Drops.push_back(cItem(E_BLOCK_WOOL, NumDrops, m_WoolColor)); m_World->SpawnItemPickups(Drops, GetPosX(), GetPosY(), GetPosZ(), 10); + m_World->BroadcastSoundEffect("mob.sheep.shear", GetPosX(), GetPosY(), GetPosZ(), 1.0f, 1.0f); } else if ((EquippedItem.m_ItemType == E_ITEM_DYE) && (m_WoolColor != 15 - EquippedItem.m_ItemDamage)) { @@ -81,12 +89,12 @@ void cSheep::Tick(float a_Dt, cChunk & a_Chunk) if (m_TimeToStopEating > 0) { - m_bMovingToDestination = false; // The sheep should not move when he's eating + m_bMovingToDestination = false; // The sheep should not move when he's eating m_TimeToStopEating--; if (m_TimeToStopEating == 0) { - if (m_World->GetBlock(PosX, PosY, PosZ) == E_BLOCK_GRASS) // Make sure grass hasn't been destroyed in the meantime + if (m_World->GetBlock(PosX, PosY, PosZ) == E_BLOCK_GRASS) // Make sure grass hasn't been destroyed in the meantime { // The sheep ate the grass so we change it to dirt m_World->SetBlock(PosX, PosY, PosZ, E_BLOCK_DIRT, 0); @@ -109,3 +117,38 @@ void cSheep::Tick(float a_Dt, cChunk & a_Chunk) } } + + + + +NIBBLETYPE cSheep::GenerateNaturalRandomColor(void) +{ + cFastRandom Random; + int Chance = Random.NextInt(101); + + if (Chance <= 81) + { + return E_META_WOOL_WHITE; + } + else if (Chance <= 86) + { + return E_META_WOOL_BLACK; + } + else if (Chance <= 91) + { + return E_META_WOOL_GRAY; + } + else if (Chance <= 96) + { + return E_META_WOOL_LIGHTGRAY; + } + else if (Chance <= 99) + { + return E_META_WOOL_BROWN; + } + else + { + return E_META_WOOL_PINK; + } +} + diff --git a/src/Mobs/Sheep.h b/src/Mobs/Sheep.h index 402e8e61c..28e1c7254 100644 --- a/src/Mobs/Sheep.h +++ b/src/Mobs/Sheep.h @@ -13,21 +13,32 @@ class cSheep : typedef cPassiveMonster super; public: - cSheep(int a_Color); - - CLASS_PROTODEF(cSheep); - + + /** The number is the color of the sheep. + Use E_META_WOOL_* constants for the wool color. + If you type -1, the server will generate a random color + with the GenerateNaturalRandomColor() function. */ + cSheep(int a_Color = -1); + + CLASS_PROTODEF(cSheep) + virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override; virtual void OnRightClicked(cPlayer & a_Player) override; virtual void Tick(float a_Dt, cChunk & a_Chunk) override; virtual const cItem GetFollowedItem(void) const override { return cItem(E_ITEM_WHEAT); } + /** Generates a random color for the sheep like the vanilla server. + The percent's where used are from the wiki: http://minecraft.gamepedia.com/Sheep#Breeding */ + static NIBBLETYPE GenerateNaturalRandomColor(void); + bool IsSheared(void) const { return m_IsSheared; } + void SetSheared(bool a_IsSheared) { m_IsSheared = a_IsSheared; } + int GetFurColor(void) const { return m_WoolColor; } + void SetFurColor(int a_WoolColor) { m_WoolColor = a_WoolColor; } private: - bool m_IsSheared; int m_WoolColor; int m_TimeToStopEating; diff --git a/src/Mobs/Silverfish.h b/src/Mobs/Silverfish.h index a6e11c49d..2df333dbc 100644 --- a/src/Mobs/Silverfish.h +++ b/src/Mobs/Silverfish.h @@ -18,7 +18,7 @@ public: { } - CLASS_PROTODEF(cSilverfish); + CLASS_PROTODEF(cSilverfish) } ; diff --git a/src/Mobs/Skeleton.cpp b/src/Mobs/Skeleton.cpp index 1e62d7987..cd707f4bb 100644 --- a/src/Mobs/Skeleton.cpp +++ b/src/Mobs/Skeleton.cpp @@ -4,6 +4,7 @@ #include "Skeleton.h" #include "../World.h" #include "../Entities/ArrowEntity.h" +#include "ClientHandle.h" @@ -47,13 +48,12 @@ void cSkeleton::GetDrops(cItems & a_Drops, cEntity * a_Killer) -void cSkeleton::MoveToPosition(const Vector3f & a_Position) +void cSkeleton::MoveToPosition(const Vector3d & a_Position) { - // If the destination is in the sun and if it is not night AND the skeleton isn't on fire then block the movement. + // If the destination is sufficiently skylight challenged AND the skeleton isn't on fire then block the movement if ( !IsOnFire() && - (m_World->GetTimeOfDay() < 13187) && - (m_World->GetBlockSkyLight((int) a_Position.x, (int) a_Position.y, (int) a_Position.z) == 15) + (m_World->GetBlockSkyLight((int)floor(a_Position.x), (int)floor(a_Position.y), (int)floor(a_Position.z)) - m_World->GetSkyDarkness() > 8) ) { m_bMovingToDestination = false; @@ -81,12 +81,27 @@ void cSkeleton::Attack(float a_Dt) { return; } - if (!Arrow->Initialize(m_World)) + if (!Arrow->Initialize(*m_World)) { delete Arrow; + Arrow = NULL; return; } m_World->BroadcastSpawnEntity(*Arrow); m_AttackInterval = 0.0; } } + + + + + +void cSkeleton::SpawnOn(cClientHandle & a_ClientHandle) +{ + super::SpawnOn(a_ClientHandle); + a_ClientHandle.SendEntityEquipment(*this, 0, cItem(E_ITEM_BOW)); +} + + + + diff --git a/src/Mobs/Skeleton.h b/src/Mobs/Skeleton.h index 8f31b42e1..577588b32 100644 --- a/src/Mobs/Skeleton.h +++ b/src/Mobs/Skeleton.h @@ -15,12 +15,16 @@ class cSkeleton : public: cSkeleton(bool IsWither); - CLASS_PROTODEF(cSkeleton); + CLASS_PROTODEF(cSkeleton) virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override; - virtual void MoveToPosition(const Vector3f & a_Position) override; + virtual void MoveToPosition(const Vector3d & a_Position) override; virtual void Attack(float a_Dt) override; - bool IsWither(void) const { return m_bIsWither; }; + virtual void SpawnOn(cClientHandle & a_ClientHandle) override; + + virtual bool IsUndead(void) override { return true; } + + bool IsWither(void) const { return m_bIsWither; } private: diff --git a/src/Mobs/Slime.cpp b/src/Mobs/Slime.cpp index 52a52bb39..b709ec664 100644 --- a/src/Mobs/Slime.cpp +++ b/src/Mobs/Slime.cpp @@ -2,16 +2,25 @@ #include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules #include "Slime.h" +#include "FastRandom.h" +#include "World.h" -/// Creates a slime of the specified size; size is 1 .. 3, with 1 being the smallest cSlime::cSlime(int a_Size) : - super("Slime", mtSlime, "mob.slime.attack", "mob.slime.attack", 0.6 * a_Size, 0.6 * a_Size), + super("Slime", + mtSlime, + Printf("mob.slime.%s", GetSizeName(a_Size).c_str()), + Printf("mob.slime.%s", GetSizeName(a_Size).c_str()), + 0.6 * a_Size, + 0.6 * a_Size + ), m_Size(a_Size) { + SetMaxHealth(a_Size * a_Size); + SetAttackDamage(a_Size); } @@ -25,7 +34,9 @@ void cSlime::GetDrops(cItems & a_Drops, cEntity * a_Killer) { LootingLevel = a_Killer->GetEquippedWeapon().m_Enchantments.GetLevel(cEnchantments::enchLooting); } - if (GetSize() == 1) + + // Only slimes with the size 1 can drop slimeballs. + if (m_Size == 1) { AddRandomDropItem(a_Drops, 0, 2 + LootingLevel, E_ITEM_SLIMEBALL); } @@ -34,3 +45,62 @@ void cSlime::GetDrops(cItems & a_Drops, cEntity * a_Killer) + +void cSlime::Attack(float a_Dt) +{ + if (m_Size > 1) + { + // Only slimes larger than size 1 attack a player. + super::Attack(a_Dt); + } +} + + + + + +void cSlime::KilledBy(TakeDamageInfo & a_TDI) +{ + if (GetHealth() > 0) + { + return; + } + + if (m_Size != 1) + { + cFastRandom Random; + int SpawnAmount = 2 + Random.NextInt(3); + + for (int i = 0; i < SpawnAmount; ++i) + { + double AddX = (i % 2 - 0.5) * m_Size / 4.0; + double AddZ = (i / 2 - 0.5) * m_Size / 4.0; + + cSlime * NewSlime = new cSlime(m_Size / 2); + NewSlime->SetPosition(GetPosX() + AddX, GetPosY() + 0.5, GetPosZ() + AddZ); + NewSlime->SetYaw(Random.NextFloat(1.0f) * 360.0f); + m_World->SpawnMobFinalize(NewSlime); + } + } + super::KilledBy(a_TDI); +} + + + + + +const AString cSlime::GetSizeName(int a_Size) const +{ + if (a_Size > 1) + { + return "big"; + } + else + { + return "small"; + } +} + + + + diff --git a/src/Mobs/Slime.h b/src/Mobs/Slime.h index 782c3113f..f0b800f94 100644 --- a/src/Mobs/Slime.h +++ b/src/Mobs/Slime.h @@ -13,17 +13,26 @@ class cSlime : typedef cAggressiveMonster super; public: - /// Creates a slime of the specified size; size is 1 .. 3, with 1 being the smallest + /** Creates a slime of the specified size; size can be 1, 2 or 4, with 1 is the smallest and 4 is the tallest. */ cSlime(int a_Size); - CLASS_PROTODEF(cSlime); + CLASS_PROTODEF(cSlime) + // cAggressiveMonster overrides: virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override; + virtual void Attack(float a_Dt) override; + virtual void KilledBy(TakeDamageInfo & a_TDI) override; + int GetSize(void) const { return m_Size; } + /** Returns the text describing the slime's size, as used by the client's resource subsystem for sounds. + Returns either "big" or "small". */ + const AString GetSizeName(int a_Size) const; + protected: - /// Size of the slime, 1 .. 3, with 1 being the smallest + /** Size of the slime, with 1 being the smallest. + Vanilla uses sizes 1, 2 and 4 only. */ int m_Size; } ; diff --git a/src/Mobs/SnowGolem.cpp b/src/Mobs/SnowGolem.cpp index c1979a495..76334d970 100644 --- a/src/Mobs/SnowGolem.cpp +++ b/src/Mobs/SnowGolem.cpp @@ -30,7 +30,7 @@ void cSnowGolem::GetDrops(cItems & a_Drops, cEntity * a_Killer) void cSnowGolem::Tick(float a_Dt, cChunk & a_Chunk) { super::Tick(a_Dt, a_Chunk); - if (IsBiomeNoDownfall(m_World->GetBiomeAt((int) floor(GetPosX()), (int) floor(GetPosZ())) )) + if (IsBiomeNoDownfall(m_World->GetBiomeAt((int) floor(GetPosX()), (int) floor(GetPosZ())))) { TakeDamage(*this); } diff --git a/src/Mobs/SnowGolem.h b/src/Mobs/SnowGolem.h index ff5e90da8..aba89e52d 100644 --- a/src/Mobs/SnowGolem.h +++ b/src/Mobs/SnowGolem.h @@ -15,7 +15,7 @@ class cSnowGolem : public: cSnowGolem(void); - CLASS_PROTODEF(cSnowGolem); + CLASS_PROTODEF(cSnowGolem) virtual void Tick(float a_Dt, cChunk & a_Chunk) override; virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override; diff --git a/src/Mobs/Spider.h b/src/Mobs/Spider.h index 51e65d028..813d2e266 100644 --- a/src/Mobs/Spider.h +++ b/src/Mobs/Spider.h @@ -15,7 +15,7 @@ class cSpider : public: cSpider(void); - CLASS_PROTODEF(cSpider); + CLASS_PROTODEF(cSpider) virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override; } ; diff --git a/src/Mobs/Squid.h b/src/Mobs/Squid.h index a9dba8b70..b57340427 100644 --- a/src/Mobs/Squid.h +++ b/src/Mobs/Squid.h @@ -17,7 +17,7 @@ public: virtual void Tick(float a_Dt, cChunk & a_Chunk) override; - CLASS_PROTODEF(cSquid); + CLASS_PROTODEF(cSquid) virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override; diff --git a/src/Mobs/Villager.cpp b/src/Mobs/Villager.cpp index d049acc1e..1cdac7c74 100644 --- a/src/Mobs/Villager.cpp +++ b/src/Mobs/Villager.cpp @@ -23,9 +23,13 @@ cVillager::cVillager(eVillagerType VillagerType) : -void cVillager::DoTakeDamage(TakeDamageInfo & a_TDI) +bool cVillager::DoTakeDamage(TakeDamageInfo & a_TDI) { - super::DoTakeDamage(a_TDI); + if (!super::DoTakeDamage(a_TDI)) + { + return false; + } + if ((a_TDI.Attacker != NULL) && a_TDI.Attacker->IsPlayer()) { if (m_World->GetTickRandomNumber(5) == 3) @@ -33,6 +37,7 @@ void cVillager::DoTakeDamage(TakeDamageInfo & a_TDI) m_World->BroadcastEntityStatus(*this, esVillagerAngry); } } + return true; } @@ -50,7 +55,7 @@ void cVillager::Tick(float a_Dt, cChunk & a_Chunk) { switch (m_Type) { - case vtFarmer: + case vtFarmer: { HandleFarmerPlaceCrops(); } @@ -133,8 +138,8 @@ void cVillager::HandleFarmerPrepareFarmCrops() m_CropsPos = Vector3i((int) GetPosX() + X - 5, (int) GetPosY() + Y - 3, (int) GetPosZ() + Z - 5); MoveToPosition(Vector3f((float) (m_CropsPos.x + 0.5), (float) m_CropsPos.y, (float) (m_CropsPos.z + 0.5))); return; - } // for Y loop. - } // Repeat the procces 5 times. + } // for Y loop. + } // Repeat the procces 5 times. } diff --git a/src/Mobs/Villager.h b/src/Mobs/Villager.h index 5bba4d4ba..aa81f0790 100644 --- a/src/Mobs/Villager.h +++ b/src/Mobs/Villager.h @@ -27,19 +27,18 @@ public: cVillager(eVillagerType VillagerType); - CLASS_PROTODEF(cVillager); + CLASS_PROTODEF(cVillager) // cEntity overrides - virtual void DoTakeDamage(TakeDamageInfo & a_TDI) override; + virtual bool DoTakeDamage(TakeDamageInfo & a_TDI) override; virtual void Tick (float a_Dt, cChunk & a_Chunk) override; // cVillager functions /** return true if the given blocktype are: crops, potatoes or carrots.*/ bool IsBlockFarmable(BLOCKTYPE a_BlockType); - ////////////////////////////////////////////////////////////////// // Farmer functions - /** It searches in a 11x7x11 area for crops. If it found some it will navigate to them.*/ + /** Searches in a 11x7x11 area for crops. If it found some it will navigate to them.*/ void HandleFarmerPrepareFarmCrops(); /** Looks if the farmer has reached it's destination, and if it's still crops and the destination is closer then 2 blocks it will harvest them.*/ @@ -49,7 +48,7 @@ public: void HandleFarmerPlaceCrops(); // Get and set functions. - int GetVilType(void) const { return m_Type; } + int GetVilType(void) const { return m_Type; } Vector3i GetCropsPos(void) const { return m_CropsPos; } bool DoesHaveActionActivated(void) const { return m_VillagerAction; } diff --git a/src/Mobs/Witch.h b/src/Mobs/Witch.h index 51c63322a..bd059f61d 100644 --- a/src/Mobs/Witch.h +++ b/src/Mobs/Witch.h @@ -16,7 +16,7 @@ class cWitch : public: cWitch(); - CLASS_PROTODEF(cWitch); + CLASS_PROTODEF(cWitch) virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override; diff --git a/src/Mobs/Wither.cpp b/src/Mobs/Wither.cpp index 8f5d28b68..578b47995 100644 --- a/src/Mobs/Wither.cpp +++ b/src/Mobs/Wither.cpp @@ -2,7 +2,9 @@ #include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules #include "Wither.h" + #include "../World.h" +#include "../Entities/Player.h" @@ -10,7 +12,7 @@ cWither::cWither(void) : super("Wither", mtWither, "mob.wither.hurt", "mob.wither.death", 0.9, 4.0), - m_InvulnerableTicks(220) + m_WitherInvulnerableTicks(220) { SetMaxHealth(300); } @@ -28,7 +30,7 @@ bool cWither::IsArmored(void) const -bool cWither::Initialize(cWorld * a_World) +bool cWither::Initialize(cWorld & a_World) { // Set health before BroadcastSpawnEntity() SetHealth(GetMaxHealth() / 3); @@ -40,24 +42,24 @@ bool cWither::Initialize(cWorld * a_World) -void cWither::DoTakeDamage(TakeDamageInfo & a_TDI) +bool cWither::DoTakeDamage(TakeDamageInfo & a_TDI) { if (a_TDI.DamageType == dtDrowning) { - return; + return false; } - if (m_InvulnerableTicks > 0) + if (m_WitherInvulnerableTicks > 0) { - return; + return false; } if (IsArmored() && (a_TDI.DamageType == dtRangedAttack)) { - return; + return false; } - super::DoTakeDamage(a_TDI); + return super::DoTakeDamage(a_TDI); } @@ -68,16 +70,16 @@ void cWither::Tick(float a_Dt, cChunk & a_Chunk) { super::Tick(a_Dt, a_Chunk); - if (m_InvulnerableTicks > 0) + if (m_WitherInvulnerableTicks > 0) { - unsigned int NewTicks = m_InvulnerableTicks - 1; + unsigned int NewTicks = m_WitherInvulnerableTicks - 1; if (NewTicks == 0) { m_World->DoExplosionAt(7.0, GetPosX(), GetPosY(), GetPosZ(), false, esWitherBirth, this); } - m_InvulnerableTicks = NewTicks; + m_WitherInvulnerableTicks = NewTicks; if ((NewTicks % 10) == 0) { @@ -100,3 +102,35 @@ void cWither::GetDrops(cItems & a_Drops, cEntity * a_Killer) + +void cWither::KilledBy(TakeDamageInfo & a_TDI) +{ + super::KilledBy(a_TDI); + + class cPlayerCallback : public cPlayerListCallback + { + Vector3f m_Pos; + + virtual bool Item(cPlayer * a_Player) + { + // TODO 2014-05-21 xdot: Vanilla minecraft uses an AABB check instead of a radius one + double Dist = (a_Player->GetPosition() - m_Pos).Length(); + if (Dist < 50.0) + { + // If player is close, award achievement + a_Player->AwardAchievement(achKillWither); + } + return false; + } + + public: + cPlayerCallback(const Vector3f & a_Pos) : m_Pos(a_Pos) {} + + } PlayerCallback(GetPosition()); + + m_World->ForEachPlayer(PlayerCallback); +} + + + + diff --git a/src/Mobs/Wither.h b/src/Mobs/Wither.h index bc78bfaad..2403823ed 100644 --- a/src/Mobs/Wither.h +++ b/src/Mobs/Wither.h @@ -15,25 +15,28 @@ class cWither : public: cWither(void); - CLASS_PROTODEF(cWither); + CLASS_PROTODEF(cWither) - unsigned int GetNumInvulnerableTicks(void) const { return m_InvulnerableTicks; } + unsigned int GetWitherInvulnerableTicks(void) const { return m_WitherInvulnerableTicks; } - void SetNumInvulnerableTicks(unsigned int a_Ticks) { m_InvulnerableTicks = a_Ticks; } + void SetWitherInvulnerableTicks(unsigned int a_Ticks) { m_WitherInvulnerableTicks = a_Ticks; } /** Returns whether the wither is invulnerable to arrows. */ bool IsArmored(void) const; // cEntity overrides - virtual bool Initialize(cWorld * a_World) override; + virtual bool Initialize(cWorld & a_World) override; virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override; - virtual void DoTakeDamage(TakeDamageInfo & a_TDI) override; + virtual bool DoTakeDamage(TakeDamageInfo & a_TDI) override; virtual void Tick(float a_Dt, cChunk & a_Chunk) override; + virtual void KilledBy(TakeDamageInfo & a_TDI) override; + + virtual bool IsUndead(void) override { return true; } private: /** The number of ticks of invulnerability left after being initially created. Zero once invulnerability has expired. */ - unsigned int m_InvulnerableTicks; + unsigned int m_WitherInvulnerableTicks; } ; diff --git a/src/Mobs/Wolf.cpp b/src/Mobs/Wolf.cpp index f02b8a4fc..5bb97d30e 100644 --- a/src/Mobs/Wolf.cpp +++ b/src/Mobs/Wolf.cpp @@ -25,14 +25,19 @@ cWolf::cWolf(void) : -void cWolf::DoTakeDamage(TakeDamageInfo & a_TDI) +bool cWolf::DoTakeDamage(TakeDamageInfo & a_TDI) { - super::DoTakeDamage(a_TDI); + if (super::DoTakeDamage(a_TDI)) + { + return false; + } + if (!m_IsTame) { m_IsAngry = true; } - m_World->BroadcastEntityMetadata(*this); // Broadcast health and possibly angry face + m_World->BroadcastEntityMetadata(*this); // Broadcast health and possibly angry face + return true; } @@ -109,7 +114,7 @@ void cWolf::OnRightClicked(cPlayer & a_Player) } case E_ITEM_DYE: { - if (a_Player.GetName() == m_OwnerName) // Is the player the owner of the dog? + if (a_Player.GetName() == m_OwnerName) // Is the player the owner of the dog? { SetCollarColor(15 - a_Player.GetEquippedItem().m_ItemDamage); if (!a_Player.IsGameModeCreative()) @@ -121,7 +126,7 @@ void cWolf::OnRightClicked(cPlayer & a_Player) } default: { - if (a_Player.GetName() == m_OwnerName) // Is the player the owner of the dog? + if (a_Player.GetName() == m_OwnerName) // Is the player the owner of the dog? { SetIsSitting(!IsSitting()); } @@ -167,7 +172,7 @@ void cWolf::Tick(float a_Dt, cChunk & a_Chunk) m_World->BroadcastEntityMetadata(*this); } - m_FinalDestination = a_Closest_Player->GetPosition(); // So that we will look at a player holding food + m_FinalDestination = a_Closest_Player->GetPosition(); // So that we will look at a player holding food // Don't move to the player if the wolf is sitting. if (!IsSitting()) diff --git a/src/Mobs/Wolf.h b/src/Mobs/Wolf.h index 5925373e1..2e83db701 100644 --- a/src/Mobs/Wolf.h +++ b/src/Mobs/Wolf.h @@ -16,9 +16,9 @@ class cWolf : public: cWolf(void); - CLASS_PROTODEF(cWolf); + CLASS_PROTODEF(cWolf) - virtual void DoTakeDamage(TakeDamageInfo & a_TDI) override; + virtual bool DoTakeDamage(TakeDamageInfo & a_TDI) override; virtual void OnRightClicked(cPlayer & a_Player) override; virtual void Tick(float a_Dt, cChunk & a_Chunk) override; virtual void TickFollowPlayer(); diff --git a/src/Mobs/Zombie.cpp b/src/Mobs/Zombie.cpp index f19e096ee..30225c32d 100644 --- a/src/Mobs/Zombie.cpp +++ b/src/Mobs/Zombie.cpp @@ -42,13 +42,12 @@ void cZombie::GetDrops(cItems & a_Drops, cEntity * a_Killer) -void cZombie::MoveToPosition(const Vector3f & a_Position) +void cZombie::MoveToPosition(const Vector3d & a_Position) { - // If the destination is in the sun and if it is not night AND the zombie isn't on fire then block the movement. + // If the destination is sufficiently skylight challenged AND the skeleton isn't on fire then block the movement if ( !IsOnFire() && - (m_World->GetTimeOfDay() < 13187) && - (m_World->GetBlockSkyLight((int)a_Position.x, (int)a_Position.y, (int)a_Position.z) == 15) + (m_World->GetBlockSkyLight((int)floor(a_Position.x), (int)floor(a_Position.y), (int)floor(a_Position.z)) - m_World->GetSkyDarkness() > 8) ) { m_bMovingToDestination = false; diff --git a/src/Mobs/Zombie.h b/src/Mobs/Zombie.h index 1ba368f9c..118b6e6e7 100644 --- a/src/Mobs/Zombie.h +++ b/src/Mobs/Zombie.h @@ -14,13 +14,15 @@ class cZombie : public: cZombie(bool a_IsVillagerZombie); - CLASS_PROTODEF(cZombie); + CLASS_PROTODEF(cZombie) virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override; - virtual void MoveToPosition(const Vector3f & a_Position) override; + virtual void MoveToPosition(const Vector3d & a_Position) override; - bool IsVillagerZombie(void) const {return m_IsVillagerZombie; } - bool IsConverting (void) const {return m_IsConverting; } + virtual bool IsUndead(void) override { return true; } + + bool IsVillagerZombie(void) const { return m_IsVillagerZombie; } + bool IsConverting (void) const { return m_IsConverting; } private: diff --git a/src/Mobs/ZombiePigman.cpp b/src/Mobs/ZombiePigman.cpp index c9d94face..05350f877 100644 --- a/src/Mobs/ZombiePigman.cpp +++ b/src/Mobs/ZombiePigman.cpp @@ -37,11 +37,11 @@ void cZombiePigman::GetDrops(cItems & a_Drops, cEntity * a_Killer) -void cZombiePigman::KilledBy(cEntity * a_Killer) +void cZombiePigman::KilledBy(TakeDamageInfo & a_TDI) { - super::KilledBy(a_Killer); + super::KilledBy(a_TDI); - if ((a_Killer != NULL) && (a_Killer->IsPlayer())) + if ((a_TDI.Attacker != NULL) && (a_TDI.Attacker->IsPlayer())) { // TODO: Anger all nearby zombie pigmen // TODO: In vanilla, if one player angers ZPs, do they attack any nearby player, or only that one attacker? diff --git a/src/Mobs/ZombiePigman.h b/src/Mobs/ZombiePigman.h index ab3cebf6d..bae0115eb 100644 --- a/src/Mobs/ZombiePigman.h +++ b/src/Mobs/ZombiePigman.h @@ -14,10 +14,12 @@ class cZombiePigman : public: cZombiePigman(void); - CLASS_PROTODEF(cZombiePigman); + CLASS_PROTODEF(cZombiePigman) virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override; - virtual void KilledBy(cEntity * a_Killer) override; + virtual void KilledBy(TakeDamageInfo & a_TDI) override; + + virtual bool IsUndead(void) override { return true; } } ; |