From 84f86a467e7c289936571c738f9422868ecaaee6 Mon Sep 17 00:00:00 2001 From: Mat Date: Thu, 26 Mar 2020 19:54:40 +0200 Subject: [PATCH] Improvements to blaze and ghast (#4547) --- src/MobSpawner.cpp | 10 +++++- src/Mobs/Blaze.cpp | 65 ++++++++++++++++++++++++--------- src/Mobs/Blaze.h | 9 +++++ src/Mobs/Ghast.cpp | 86 +++++++++++++++++++++++++++++++++++++------- src/Mobs/Ghast.h | 15 +++++++- src/Mobs/Monster.cpp | 2 +- 6 files changed, 155 insertions(+), 32 deletions(-) diff --git a/src/MobSpawner.cpp b/src/MobSpawner.cpp index 5cb8f0d33..0869e7282 100644 --- a/src/MobSpawner.cpp +++ b/src/MobSpawner.cpp @@ -123,7 +123,6 @@ bool cMobSpawner::CanSpawnHere(cChunk * a_Chunk, Vector3i a_RelPos, eMonsterType } case mtBlaze: - case mtGhast: { return ( (targetBlock == E_BLOCK_AIR) && @@ -194,6 +193,15 @@ bool cMobSpawner::CanSpawnHere(cChunk * a_Chunk, Vector3i a_RelPos, eMonsterType break; } + case mtGhast: + { + return ( + (targetBlock == E_BLOCK_AIR) && + (blockAbove == E_BLOCK_AIR) && + (random.RandBool()) + ); + } + case mtGuardian: { return ( diff --git a/src/Mobs/Blaze.cpp b/src/Mobs/Blaze.cpp index 80a887b03..1c48a88d2 100644 --- a/src/Mobs/Blaze.cpp +++ b/src/Mobs/Blaze.cpp @@ -9,7 +9,9 @@ cBlaze::cBlaze(void) : - super("Blaze", mtBlaze, "entity.blaze.hurt", "entity.blaze.death", "entity.blaze.ambient", 0.6, 1.8) + super("Blaze", mtBlaze, "entity.blaze.hurt", "entity.blaze.death", "entity.blaze.ambient", 0.6, 1.8), + m_IsCharging(false), + m_ChargeTimer(0) { SetGravity(-8.0f); SetAirDrag(0.05f); @@ -34,23 +36,54 @@ void cBlaze::GetDrops(cItems & a_Drops, cEntity * a_Killer) bool cBlaze::Attack(std::chrono::milliseconds a_Dt) { - if ((GetTarget() != nullptr) && (m_AttackCoolDownTicksLeft == 0)) + if ((GetTarget() != nullptr) && (m_AttackCoolDownTicksLeft == 0) && (!m_IsCharging)) { - // Setting this higher gives us more wiggle room for attackrate - Vector3d Speed = GetLookVector() * 20; - Speed.y = Speed.y + 1; - - auto FireCharge = cpp14::make_unique(this, GetPosition().addedY(1), Speed); - auto FireChargePtr = FireCharge.get(); - if (!FireChargePtr->Initialize(std::move(FireCharge), *m_World)) - { - return false; - } - - ResetAttackCooldown(); - // ToDo: Shoot 3 fireballs instead of 1. - + m_IsCharging = true; return true; } return false; } + + + + + +void cBlaze::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) +{ + super::Tick(a_Dt, a_Chunk); + if (!IsTicking()) + { + // The base class tick destroyed us + return; + } + + if (m_IsCharging) + { + m_ChargeTimer++; + if ( + (m_ChargeTimer == 5) || + (m_ChargeTimer == 10) || + (m_ChargeTimer == 15) + ) + { + Vector3d Speed = GetLookVector() * 20; + Speed.y = Speed.y + 1; + + auto FireCharge = cpp14::make_unique(this, GetPosition().addedY(1), Speed); + auto FireChargePtr = FireCharge.get(); + FireChargePtr->Initialize(std::move(FireCharge), *m_World); + + m_World->BroadcastSoundEffect("entity.ghast.shoot", GetPosition(), 4.0f, 1.0f); + } + } + + if ((m_IsCharging) && (m_ChargeTimer > 15)) + { + m_ChargeTimer = 0; + m_IsCharging = false; + ResetAttackCooldown(); + } +} + + + diff --git a/src/Mobs/Blaze.h b/src/Mobs/Blaze.h index ca755b626..3e6726efa 100644 --- a/src/Mobs/Blaze.h +++ b/src/Mobs/Blaze.h @@ -17,6 +17,15 @@ public: CLASS_PROTODEF(cBlaze) + virtual void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override; virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = nullptr) override; virtual bool Attack(std::chrono::milliseconds a_Dt) override; + +private: + /** Specifies whether or not the blaze has started shooting fireballs. */ + bool m_IsCharging; + + /** Number of ticks since the blaze started charging. + Used to create 3 successive projectiles. */ + int m_ChargeTimer; } ; diff --git a/src/Mobs/Ghast.cpp b/src/Mobs/Ghast.cpp index 2f1f2cf8b..40a7bdf0f 100644 --- a/src/Mobs/Ghast.cpp +++ b/src/Mobs/Ghast.cpp @@ -9,8 +9,13 @@ cGhast::cGhast(void) : - super("Ghast", mtGhast, "entity.ghast.hurt", "entity.ghast.death", "entity.ghast.ambient", 4, 4) + super("Ghast", mtGhast, "entity.ghast.hurt", "entity.ghast.death", "entity.ghast.ambient", 4, 4), + m_IsCharging(false), + m_FlightCooldown(5), + m_TicksUntilShot(10) { + SetGravity(0); + SetAirDrag(0); } @@ -34,20 +39,14 @@ void cGhast::GetDrops(cItems & a_Drops, cEntity * a_Killer) bool cGhast::Attack(std::chrono::milliseconds a_Dt) { - if ((GetTarget() != nullptr) && (m_AttackCoolDownTicksLeft == 0)) + if ((GetTarget() != nullptr) && (m_AttackCoolDownTicksLeft == 0) && (!m_IsCharging)) { - // Setting this higher gives us more wiggle room for attackrate - Vector3d Speed = GetLookVector() * 20; - Speed.y = Speed.y + 1; + auto & Random = GetRandomProvider(); + auto SoundPitchMultiplier = 1.0f + (Random.RandReal(1.0f) - Random.RandReal(1.0f)) * 0.2f; - auto GhastBall = cpp14::make_unique(this, GetPosition().addedY(1), Speed); - auto GhastBallPtr = GhastBall.get(); - if (!GhastBallPtr->Initialize(std::move(GhastBall), *m_World)) - { - return false; - } - - ResetAttackCooldown(); + m_World->BroadcastSoundEffect("entity.ghast.warn", GetPosition(), 4.0f, SoundPitchMultiplier * 0.9f); + m_IsCharging = true; + m_World->BroadcastEntityMetadata(*this); return true; } return false; @@ -55,3 +54,64 @@ bool cGhast::Attack(std::chrono::milliseconds a_Dt) + + +bool cGhast::DoTakeDamage(TakeDamageInfo & a_TDI) +{ + // No fall damage + if (a_TDI.DamageType == dtFalling) + { + return false; + } + + return super::DoTakeDamage(a_TDI); +} + + + + + +void cGhast::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) +{ + super::Tick(a_Dt, a_Chunk); + if (!IsTicking()) + { + // The base class tick destroyed us + return; + } + + if ((m_IsCharging) && (m_TicksUntilShot-- == 0)) + { + m_TicksUntilShot = 10; + m_IsCharging = false; + m_World->BroadcastEntityMetadata(*this); + + Vector3d Speed = GetLookVector() * 20; + Speed.y = Speed.y + 1; + + auto GhastBall = cpp14::make_unique(this, GetPosition(), Speed); + auto GhastBallPtr = GhastBall.get(); + GhastBallPtr->Initialize(std::move(GhastBall), *m_World); + + m_World->BroadcastSoundEffect("entity.ghast.shoot", GetPosition(), 4.0f, 1.0f); + + ResetAttackCooldown(); + } + + // TODO: Better flying + if (m_FlightCooldown-- == 0) + { + m_FlightCooldown = 5; + auto & Random = GetRandomProvider(); + auto SpeedVector = Vector3d(Random.RandReal(-0.3, 0.3), Random.RandReal(-0.4, 0.4), Random.RandReal(-0.3, 0.3)); + + if (GetPosY() > 120) + { + SpeedVector = Vector3d(Random.RandReal(-0.4, 0.4), Random.RandReal(-0.45, 0.4), Random.RandReal(-0.4, 0.4)); + } + + AddSpeed(SpeedVector); + } +} + + diff --git a/src/Mobs/Ghast.h b/src/Mobs/Ghast.h index a41a72ddc..7312b25c9 100644 --- a/src/Mobs/Ghast.h +++ b/src/Mobs/Ghast.h @@ -17,10 +17,23 @@ public: CLASS_PROTODEF(cGhast) + virtual void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override; virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = nullptr) override; virtual bool Attack(std::chrono::milliseconds a_Dt) override; + virtual bool DoTakeDamage(TakeDamageInfo & a_TDI) override; - bool IsCharging(void) const {return false; } + bool IsCharging(void) const { return m_IsCharging; } + +private: + /** Specifies whether or not the ghast has started shooting a fireball. */ + bool m_IsCharging; + + /** Number of ticks until the ghast tries to fly to another position. */ + int m_FlightCooldown; + + /** Number of ticks until a projectile is created. + Only used while m_IsCharging is true. */ + int m_TicksUntilShot; } ; diff --git a/src/Mobs/Monster.cpp b/src/Mobs/Monster.cpp index 9a826ed21..7c57e4f2d 100644 --- a/src/Mobs/Monster.cpp +++ b/src/Mobs/Monster.cpp @@ -313,7 +313,7 @@ void cMonster::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) HandleDaylightBurning(*Chunk, WouldBurnAt(GetPosition(), *Chunk)); bool a_IsFollowingPath = false; - if (m_PathfinderActivated) + if (m_PathfinderActivated && (GetMobType() != mtGhast)) // Pathfinder is currently disabled for ghasts, which have their own flying mechanism { if (ReachedFinalDestination() || (m_LeashToPos != nullptr)) {