diff options
Diffstat (limited to 'src/Entities')
-rw-r--r-- | src/Entities/ArrowEntity.cpp | 4 | ||||
-rw-r--r-- | src/Entities/Effects.h | 30 | ||||
-rw-r--r-- | src/Entities/EnderCrystal.cpp | 4 | ||||
-rw-r--r-- | src/Entities/EnderCrystal.h | 2 | ||||
-rw-r--r-- | src/Entities/Entity.cpp | 19 | ||||
-rw-r--r-- | src/Entities/Entity.h | 5 | ||||
-rw-r--r-- | src/Entities/EntityEffect.cpp | 287 | ||||
-rw-r--r-- | src/Entities/EntityEffect.h | 476 | ||||
-rw-r--r-- | src/Entities/ItemFrame.cpp | 6 | ||||
-rw-r--r-- | src/Entities/ItemFrame.h | 2 | ||||
-rw-r--r-- | src/Entities/Painting.h | 4 | ||||
-rw-r--r-- | src/Entities/Pawn.cpp | 100 | ||||
-rw-r--r-- | src/Entities/Pawn.h | 28 | ||||
-rw-r--r-- | src/Entities/Player.cpp | 83 | ||||
-rw-r--r-- | src/Entities/Player.h | 13 | ||||
-rw-r--r-- | src/Entities/ProjectileEntity.cpp | 11 | ||||
-rw-r--r-- | src/Entities/ProjectileEntity.h | 2 | ||||
-rw-r--r-- | src/Entities/SplashPotionEntity.cpp | 120 | ||||
-rw-r--r-- | src/Entities/SplashPotionEntity.h | 59 | ||||
-rw-r--r-- | src/Entities/WitherSkullEntity.cpp | 49 | ||||
-rw-r--r-- | src/Entities/WitherSkullEntity.h | 35 |
21 files changed, 1229 insertions, 110 deletions
diff --git a/src/Entities/ArrowEntity.cpp b/src/Entities/ArrowEntity.cpp index d5e41bd46..a3a1667e4 100644 --- a/src/Entities/ArrowEntity.cpp +++ b/src/Entities/ArrowEntity.cpp @@ -79,8 +79,8 @@ void cArrowEntity::OnHitSolidBlock(const Vector3d & a_HitPos, eBlockFace a_HitFa } Vector3d Hit = a_HitPos; - Vector3d SinkMovement = (GetSpeed() / 800); - Hit += (SinkMovement * 0.01) / SinkMovement.Length(); // Make arrow sink into block a centimetre so it lodges (but not to far so it goes black clientside) + Vector3d SinkMovement = (GetSpeed() / 1000); + Hit += SinkMovement * (0.0005 / SinkMovement.Length()); // Make arrow sink into block a centimetre so it lodges (but not to far so it goes black clientside) super::OnHitSolidBlock(Hit, a_HitFace); Vector3i BlockHit = Hit.Floor(); diff --git a/src/Entities/Effects.h b/src/Entities/Effects.h deleted file mode 100644 index baf3302fb..000000000 --- a/src/Entities/Effects.h +++ /dev/null @@ -1,30 +0,0 @@ -#pragma once - -// tolua_begin -enum ENUM_ENTITY_EFFECT -{ - E_EFFECT_SPEED = 1, - E_EFFECT_SLOWNESS = 2, - E_EFFECT_HASTE = 3, - E_EFFECT_MINING_FATIGUE = 4, - E_EFFECT_STENGTH = 5, - E_EFFECT_INSTANT_HEALTH = 6, - E_EFFECT_INSTANT_DAMAGE = 7, - E_EFFECT_JUMP_BOOST = 8, - E_EFFECT_NAUSEA = 9, - E_EFFECT_REGENERATION = 10, - E_EFFECT_RESISTANCE = 11, - E_EFFECT_FIRE_RESISTANCE = 12, - E_EFFECT_WATER_BREATHING = 13, - E_EFFECT_INVISIBILITY = 14, - E_EFFECT_BLINDNESS = 15, - E_EFFECT_NIGHT_VISION = 16, - E_EFFECT_HUNGER = 17, - E_EFFECT_WEAKNESS = 18, - E_EFFECT_POISON = 19, - E_EFFECT_WITHER = 20, - E_EFFECT_HEALTH_BOOST = 21, - E_EFFECT_ABSORPTION = 22, - E_EFFECT_SATURATION = 23, -} ; -// tolua_end diff --git a/src/Entities/EnderCrystal.cpp b/src/Entities/EnderCrystal.cpp index a640b236c..bf86a6c42 100644 --- a/src/Entities/EnderCrystal.cpp +++ b/src/Entities/EnderCrystal.cpp @@ -42,9 +42,9 @@ void cEnderCrystal::Tick(float a_Dt, cChunk & a_Chunk) -void cEnderCrystal::KilledBy(cEntity * a_Killer) +void cEnderCrystal::KilledBy(TakeDamageInfo & a_TDI) { - super::KilledBy(a_Killer); + super::KilledBy(a_TDI); m_World->DoExplosionAt(6.0, GetPosX(), GetPosY(), GetPosZ(), true, esEnderCrystal, this); diff --git a/src/Entities/EnderCrystal.h b/src/Entities/EnderCrystal.h index 5b86df987..30211de13 100644 --- a/src/Entities/EnderCrystal.h +++ b/src/Entities/EnderCrystal.h @@ -24,7 +24,7 @@ private: // cEntity overrides: virtual void SpawnOn(cClientHandle & a_ClientHandle) override; virtual void Tick(float a_Dt, cChunk & a_Chunk) override; - virtual void KilledBy(cEntity * a_Killer) override; + virtual void KilledBy(TakeDamageInfo & a_TDI) override; }; // tolua_export diff --git a/src/Entities/Entity.cpp b/src/Entities/Entity.cpp index 26823924f..c6dc1fca3 100644 --- a/src/Entities/Entity.cpp +++ b/src/Entities/Entity.cpp @@ -310,10 +310,14 @@ bool cEntity::DoTakeDamage(TakeDamageInfo & a_TDI) cPlayer * Player = (cPlayer *)a_TDI.Attacker; // IsOnGround() only is false if the player is moving downwards - if (!Player->IsOnGround()) // TODO: Better damage increase, and check for enchantments (and use magic critical instead of plain) + // TODO: Better damage increase, and check for enchantments (and use magic critical instead of plain) + if (!Player->IsOnGround()) { - a_TDI.FinalDamage += 2; - m_World->BroadcastEntityAnimation(*this, 4); // Critical hit + if ((a_TDI.DamageType == dtAttack) || (a_TDI.DamageType == dtArrowAttack)) + { + a_TDI.FinalDamage += 2; + m_World->BroadcastEntityAnimation(*this, 4); // Critical hit + } } Player->GetStatManager().AddValue(statDamageDealt, (StatValue)floor(a_TDI.FinalDamage * 10 + 0.5)); @@ -368,7 +372,7 @@ bool cEntity::DoTakeDamage(TakeDamageInfo & a_TDI) if (m_Health <= 0) { - KilledBy(a_TDI.Attacker); + KilledBy(a_TDI); if (a_TDI.Attacker != NULL) { @@ -431,6 +435,7 @@ bool cEntity::ArmorCoversAgainst(eDamageType a_DamageType) case dtStarving: case dtInVoid: case dtPoisoning: + case dtWithering: case dtPotionOfHarming: case dtFalling: case dtLightning: @@ -524,11 +529,11 @@ double cEntity::GetKnockbackAmountAgainst(const cEntity & a_Receiver) -void cEntity::KilledBy(cEntity * a_Killer) +void cEntity::KilledBy(TakeDamageInfo & a_TDI) { m_Health = 0; - cRoot::Get()->GetPluginManager()->CallHookKilling(*this, a_Killer); + cRoot::Get()->GetPluginManager()->CallHookKilling(*this, a_TDI.Attacker, a_TDI); if (m_Health > 0) { @@ -538,7 +543,7 @@ void cEntity::KilledBy(cEntity * a_Killer) // Drop loot: cItems Drops; - GetDrops(Drops, a_Killer); + GetDrops(Drops, a_TDI.Attacker); m_World->SpawnItemPickups(Drops, GetPosX(), GetPosY(), GetPosZ()); m_World->BroadcastEntityStatus(*this, esGenericDead); diff --git a/src/Entities/Entity.h b/src/Entities/Entity.h index f4080f8aa..ea44c2722 100644 --- a/src/Entities/Entity.h +++ b/src/Entities/Entity.h @@ -158,6 +158,7 @@ public: bool IsPlayer (void) const { return (m_EntityType == etPlayer); } bool IsPickup (void) const { return (m_EntityType == etPickup); } bool IsMob (void) const { return (m_EntityType == etMonster); } + bool IsPawn (void) const { return (IsMob() || IsPlayer()); } bool IsFallingBlock(void) const { return (m_EntityType == etFallingBlock); } bool IsMinecart (void) const { return (m_EntityType == etMinecart); } bool IsBoat (void) const { return (m_EntityType == etBoat); } @@ -309,13 +310,13 @@ public: virtual cItem GetEquippedBoots(void) const { return cItem(); } /// Called when the health drops below zero. a_Killer may be NULL (environmental damage) - virtual void KilledBy(cEntity * a_Killer); + virtual void KilledBy(TakeDamageInfo & a_TDI); /// Called when the entity kills another entity virtual void Killed(cEntity * a_Victim) {} /// Heals the specified amount of HPs - void Heal(int a_HitPoints); + virtual void Heal(int a_HitPoints); /// Returns the health of this entity int GetHealth(void) const { return m_Health; } diff --git a/src/Entities/EntityEffect.cpp b/src/Entities/EntityEffect.cpp new file mode 100644 index 000000000..4da7cac85 --- /dev/null +++ b/src/Entities/EntityEffect.cpp @@ -0,0 +1,287 @@ +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "EntityEffect.h" +#include "../Mobs/Monster.h" +#include "Player.h" + + + + +cEntityEffect::cEntityEffect(): + m_Ticks(0), + m_Duration(0), + m_Intensity(0), + m_DistanceModifier(1) +{ + +} + + + + + +cEntityEffect::cEntityEffect(int a_Duration, short a_Intensity, double a_DistanceModifier): + m_Ticks(0), + m_Duration(a_Duration), + m_Intensity(a_Intensity), + m_DistanceModifier(a_DistanceModifier) +{ + +} + + + + + +cEntityEffect::cEntityEffect(const cEntityEffect & a_OtherEffect): + m_Ticks(a_OtherEffect.m_Ticks), + m_Duration(a_OtherEffect.m_Duration), + m_Intensity(a_OtherEffect.m_Intensity), + m_DistanceModifier(a_OtherEffect.m_DistanceModifier) +{ + +} + + + + + +cEntityEffect & cEntityEffect::operator=(cEntityEffect a_OtherEffect) +{ + std::swap(m_Ticks, a_OtherEffect.m_Ticks); + std::swap(m_Duration, a_OtherEffect.m_Duration); + std::swap(m_Intensity, a_OtherEffect.m_Intensity); + std::swap(m_DistanceModifier, a_OtherEffect.m_DistanceModifier); + return *this; +} + + + + + +cEntityEffect * cEntityEffect::CreateEntityEffect(cEntityEffect::eType a_EffectType, int a_Duration, short a_Intensity, double a_DistanceModifier) +{ + switch (a_EffectType) + { + case cEntityEffect::effNoEffect: return new cEntityEffect (a_Duration, a_Intensity, a_DistanceModifier); + + case cEntityEffect::effAbsorption: return new cEntityEffectAbsorption (a_Duration, a_Intensity, a_DistanceModifier); + case cEntityEffect::effBlindness: return new cEntityEffectBlindness (a_Duration, a_Intensity, a_DistanceModifier); + case cEntityEffect::effFireResistance: return new cEntityEffectFireResistance(a_Duration, a_Intensity, a_DistanceModifier); + case cEntityEffect::effHaste: return new cEntityEffectHaste (a_Duration, a_Intensity, a_DistanceModifier); + case cEntityEffect::effHealthBoost: return new cEntityEffectHealthBoost (a_Duration, a_Intensity, a_DistanceModifier); + case cEntityEffect::effHunger: return new cEntityEffectHunger (a_Duration, a_Intensity, a_DistanceModifier); + case cEntityEffect::effInstantDamage: return new cEntityEffectInstantDamage (a_Duration, a_Intensity, a_DistanceModifier); + case cEntityEffect::effInstantHealth: return new cEntityEffectInstantHealth (a_Duration, a_Intensity, a_DistanceModifier); + case cEntityEffect::effInvisibility: return new cEntityEffectInvisibility (a_Duration, a_Intensity, a_DistanceModifier); + case cEntityEffect::effJumpBoost: return new cEntityEffectJumpBoost (a_Duration, a_Intensity, a_DistanceModifier); + case cEntityEffect::effMiningFatigue: return new cEntityEffectMiningFatigue (a_Duration, a_Intensity, a_DistanceModifier); + case cEntityEffect::effNausea: return new cEntityEffectNausea (a_Duration, a_Intensity, a_DistanceModifier); + case cEntityEffect::effNightVision: return new cEntityEffectNightVision (a_Duration, a_Intensity, a_DistanceModifier); + case cEntityEffect::effPoison: return new cEntityEffectPoison (a_Duration, a_Intensity, a_DistanceModifier); + case cEntityEffect::effRegeneration: return new cEntityEffectRegeneration (a_Duration, a_Intensity, a_DistanceModifier); + case cEntityEffect::effResistance: return new cEntityEffectResistance (a_Duration, a_Intensity, a_DistanceModifier); + case cEntityEffect::effSaturation: return new cEntityEffectSaturation (a_Duration, a_Intensity, a_DistanceModifier); + case cEntityEffect::effSlowness: return new cEntityEffectSlowness (a_Duration, a_Intensity, a_DistanceModifier); + case cEntityEffect::effSpeed: return new cEntityEffectSpeed (a_Duration, a_Intensity, a_DistanceModifier); + case cEntityEffect::effStrength: return new cEntityEffectStrength (a_Duration, a_Intensity, a_DistanceModifier); + case cEntityEffect::effWaterBreathing: return new cEntityEffectWaterBreathing(a_Duration, a_Intensity, a_DistanceModifier); + case cEntityEffect::effWeakness: return new cEntityEffectWeakness (a_Duration, a_Intensity, a_DistanceModifier); + case cEntityEffect::effWither: return new cEntityEffectWither (a_Duration, a_Intensity, a_DistanceModifier); + } + + ASSERT(!"Unhandled entity effect type!"); + return NULL; +} + + + + + +void cEntityEffect::OnTick(cPawn & a_Target) +{ + // Reduce the effect's duration + ++m_Ticks; +} + + + + + +///////////////////////////////////////////////////////////////////////// +// cEntityEffectInstantHealth: + +void cEntityEffectInstantHealth::OnActivate(cPawn & a_Target) +{ + // Base amount = 6, doubles for every increase in intensity + int amount = (int)(6 * (1 << m_Intensity) * m_DistanceModifier); + + if (a_Target.IsMob() && ((cMonster &) a_Target).IsUndead()) + { + a_Target.TakeDamage(dtPotionOfHarming, NULL, amount, 0); // TODO: Store attacker in a pointer-safe way, pass to TakeDamage + return; + } + a_Target.Heal(amount); +} + + + + + +///////////////////////////////////////////////////////////////////////// +// cEntityEffectInstantDamage: + +void cEntityEffectInstantDamage::OnActivate(cPawn & a_Target) +{ + // Base amount = 6, doubles for every increase in intensity + int amount = (int)(6 * (1 << m_Intensity) * m_DistanceModifier); + + if (a_Target.IsMob() && ((cMonster &) a_Target).IsUndead()) + { + a_Target.Heal(amount); + return; + } + a_Target.TakeDamage(dtPotionOfHarming, NULL, amount, 0); // TODO: Store attacker in a pointer-safe way, pass to TakeDamage +} + + + + + +///////////////////////////////////////////////////////////////////////// +// cEntityEffectRegeneration: + +void cEntityEffectRegeneration::OnTick(cPawn & a_Target) +{ + super::OnTick(a_Target); + + if (a_Target.IsMob() && ((cMonster &) a_Target).IsUndead()) + { + return; + } + + // Regen frequency = 50 ticks, divided by potion level (Regen II = 25 ticks) + int frequency = (int) std::floor(50.0 / (double)(m_Intensity + 1)); + + if ((m_Ticks % frequency) != 0) + { + return; + } + + a_Target.Heal(1); +} + + + + + +///////////////////////////////////////////////////////////////////////// +// cEntityEffectHunger: + +void cEntityEffectHunger::OnTick(cPawn & a_Target) +{ + super::OnTick(a_Target); + + if (a_Target.IsPlayer()) + { + cPlayer & Target = (cPlayer &) a_Target; + Target.SetFoodExhaustionLevel(Target.GetFoodExhaustionLevel() + 0.025); // 0.5 per second = 0.025 per tick + } +} + + + + + +///////////////////////////////////////////////////////////////////////// +// cEntityEffectWeakness: + +void cEntityEffectWeakness::OnTick(cPawn & a_Target) +{ + super::OnTick(a_Target); + + // Damage reduction = 0.5 damage, multiplied by potion level (Weakness II = 1 damage) + // double dmg_reduc = 0.5 * (a_Effect.GetIntensity() + 1); + + // TODO: Implement me! + // TODO: Weakened villager zombies can be turned back to villagers with the god apple +} + + + + + +///////////////////////////////////////////////////////////////////////// +// cEntityEffectPoison: + +void cEntityEffectPoison::OnTick(cPawn & a_Target) +{ + super::OnTick(a_Target); + + if (a_Target.IsMob()) + { + cMonster & Target = (cMonster &) a_Target; + + // Doesn't effect undead mobs, spiders + if ( + Target.IsUndead() || + (Target.GetMobType() == cMonster::mtSpider) || + (Target.GetMobType() == cMonster::mtCaveSpider) + ) + { + return; + } + } + + // Poison frequency = 25 ticks, divided by potion level (Poison II = 12 ticks) + int frequency = (int) std::floor(25.0 / (double)(m_Intensity + 1)); + + if ((m_Ticks % frequency) == 0) + { + // Cannot take poison damage when health is at 1 + if (a_Target.GetHealth() > 1) + { + a_Target.TakeDamage(dtPoisoning, NULL, 1, 0); + } + } +} + + + + + +///////////////////////////////////////////////////////////////////////// +// cEntityEffectWither: + +void cEntityEffectWither::OnTick(cPawn & a_Target) +{ + super::OnTick(a_Target); + + // Damage frequency = 40 ticks, divided by effect level (Wither II = 20 ticks) + int frequency = (int) std::floor(25.0 / (double)(m_Intensity + 1)); + + if ((m_Ticks % frequency) == 0) + { + a_Target.TakeDamage(dtWither, NULL, 1, 0); + } +} + + + + + +///////////////////////////////////////////////////////////////////////// +// cEntityEffectSaturation: + +void cEntityEffectSaturation::OnTick(cPawn & a_Target) +{ + if (a_Target.IsPlayer()) + { + cPlayer & Target = (cPlayer &) a_Target; + Target.SetFoodSaturationLevel(Target.GetFoodSaturationLevel() + (1 + m_Intensity)); // Increase saturation 1 per tick, adds 1 for every increase in level + } +} + + + + diff --git a/src/Entities/EntityEffect.h b/src/Entities/EntityEffect.h new file mode 100644 index 000000000..ebd611ff0 --- /dev/null +++ b/src/Entities/EntityEffect.h @@ -0,0 +1,476 @@ +#pragma once + +class cPawn; + +// tolua_begin +class cEntityEffect +{ +public: + + /** All types of entity effects (numbers correspond to protocol / storage types) */ + enum eType + { + effNoEffect = 0, + effSpeed = 1, + effSlowness = 2, + effHaste = 3, + effMiningFatigue = 4, + effStrength = 5, + effInstantHealth = 6, + effInstantDamage = 7, + effJumpBoost = 8, + effNausea = 9, + effRegeneration = 10, + effResistance = 11, + effFireResistance = 12, + effWaterBreathing = 13, + effInvisibility = 14, + effBlindness = 15, + effNightVision = 16, + effHunger = 17, + effWeakness = 18, + effPoison = 19, + effWither = 20, + effHealthBoost = 21, + effAbsorption = 22, + effSaturation = 23, + } ; + + // tolua_end + + /** Creates an empty entity effect */ + cEntityEffect(void); + + /** Creates an entity effect of the specified type + @param a_Duration How long this effect will last, in ticks + @param a_Intensity How strong the effect will be applied + @param a_DistanceModifier The distance modifier for affecting potency, defaults to 1 */ + cEntityEffect(int a_Duration, short a_Intensity, double a_DistanceModifier = 1); + + /** Creates an entity effect by copying another + @param a_OtherEffect The other effect to copy */ + cEntityEffect(const cEntityEffect & a_OtherEffect); + + /** Creates an entity effect by copying another + @param a_OtherEffect The other effect to copy */ + cEntityEffect & operator=(cEntityEffect a_OtherEffect); + + virtual ~cEntityEffect(void) {}; + + /** Creates a pointer to the proper entity effect from the effect type + @warning This function creates raw pointers that must be manually managed. + @param a_EffectType The effect type to create the effect from + @param a_Duration How long this effect will last, in ticks + @param a_Intensity How strong the effect will be applied + @param a_DistanceModifier The distance modifier for affecting potency, defaults to 1 */ + static cEntityEffect * CreateEntityEffect(cEntityEffect::eType a_EffectType, int a_Duration, short a_Intensity, double a_DistanceModifier); + + /** Returns how many ticks this effect has been active for */ + int GetTicks(void) const { return m_Ticks; } + + /** Returns the duration of the effect */ + int GetDuration(void) const { return m_Duration; } + + /** Returns how strong the effect will be applied */ + short GetIntensity(void) const { return m_Intensity; } + + /** Returns the distance modifier for affecting potency */ + double GetDistanceModifier(void) const { return m_DistanceModifier; } + + void SetTicks(int a_Ticks) { m_Ticks = a_Ticks; } + void SetDuration(int a_Duration) { m_Duration = a_Duration; } + void SetIntensity(short a_Intensity) { m_Intensity = a_Intensity; } + void SetDistanceModifier(double a_DistanceModifier) { m_DistanceModifier = a_DistanceModifier; } + + /** Called on each tick. + By default increases the m_Ticks, descendants may override to provide additional processing. */ + virtual void OnTick(cPawn & a_Target); + + /** Called when the effect is first added to an entity */ + virtual void OnActivate(cPawn & a_Target) { } + + /** Called when the effect is removed from an entity */ + virtual void OnDeactivate(cPawn & a_Target) { } + +protected: + /** How many ticks this effect has been active for */ + int m_Ticks; + + /** How long this effect will last, in ticks */ + int m_Duration; + + /** How strong the effect will be applied */ + short m_Intensity; + + /** The distance modifier for affecting potency */ + double m_DistanceModifier; +}; // tolua_export + + + + + +class cEntityEffectSpeed: + public cEntityEffect +{ + typedef cEntityEffect super; +public: + cEntityEffectSpeed(int a_Duration, short a_Intensity, double a_DistanceModifier = 1): + super(a_Duration, a_Intensity, a_DistanceModifier) + { + } +}; + + + + + +class cEntityEffectSlowness: + public cEntityEffect +{ + typedef cEntityEffect super; +public: + cEntityEffectSlowness(int a_Duration, short a_Intensity, double a_DistanceModifier = 1): + super(a_Duration, a_Intensity, a_DistanceModifier) + { + } +}; + + + + + +class cEntityEffectHaste: + public cEntityEffect +{ + typedef cEntityEffect super; +public: + cEntityEffectHaste(int a_Duration, short a_Intensity, double a_DistanceModifier = 1): + super(a_Duration, a_Intensity, a_DistanceModifier) + { + } +}; + + + + + +class cEntityEffectMiningFatigue: + public cEntityEffect +{ + typedef cEntityEffect super; +public: + cEntityEffectMiningFatigue(int a_Duration, short a_Intensity, double a_DistanceModifier = 1): + super(a_Duration, a_Intensity, a_DistanceModifier) + { + } +}; + + + + + +class cEntityEffectStrength: + public cEntityEffect +{ + typedef cEntityEffect super; +public: + cEntityEffectStrength(int a_Duration, short a_Intensity, double a_DistanceModifier = 1): + super(a_Duration, a_Intensity, a_DistanceModifier) + { + } +}; + + + + + +class cEntityEffectInstantHealth: + public cEntityEffect +{ + typedef cEntityEffect super; +public: + cEntityEffectInstantHealth(int a_Duration, short a_Intensity, double a_DistanceModifier = 1): + super(a_Duration, a_Intensity, a_DistanceModifier) + { + } + + virtual void OnActivate(cPawn & a_Target) override; +}; + + + + + +class cEntityEffectInstantDamage: + public cEntityEffect +{ + typedef cEntityEffect super; +public: + cEntityEffectInstantDamage(int a_Duration, short a_Intensity, double a_DistanceModifier = 1): + super(a_Duration, a_Intensity, a_DistanceModifier) + { + } + + virtual void OnActivate(cPawn & a_Target) override; +}; + + + + + +class cEntityEffectJumpBoost: + public cEntityEffect +{ + typedef cEntityEffect super; +public: + cEntityEffectJumpBoost(int a_Duration, short a_Intensity, double a_DistanceModifier = 1): + super(a_Duration, a_Intensity, a_DistanceModifier) + { + } +}; + + + + + +class cEntityEffectNausea: + public cEntityEffect +{ + typedef cEntityEffect super; +public: + cEntityEffectNausea(int a_Duration, short a_Intensity, double a_DistanceModifier = 1): + super(a_Duration, a_Intensity, a_DistanceModifier) + { + } +}; + + + + + +class cEntityEffectRegeneration: + public cEntityEffect +{ + typedef cEntityEffect super; +public: + cEntityEffectRegeneration(int a_Duration, short a_Intensity, double a_DistanceModifier = 1): + super(a_Duration, a_Intensity, a_DistanceModifier) + { + } + + virtual void OnTick(cPawn & a_Target) override; +}; + + + + + +class cEntityEffectResistance: + public cEntityEffect +{ + typedef cEntityEffect super; +public: + cEntityEffectResistance(int a_Duration, short a_Intensity, double a_DistanceModifier = 1): + super(a_Duration, a_Intensity, a_DistanceModifier) + { + } +}; + + + + + +class cEntityEffectFireResistance: + public cEntityEffect +{ + typedef cEntityEffect super; +public: + cEntityEffectFireResistance(int a_Duration, short a_Intensity, double a_DistanceModifier = 1): + super(a_Duration, a_Intensity, a_DistanceModifier) + { + } +}; + + + + + +class cEntityEffectWaterBreathing: + public cEntityEffect +{ + typedef cEntityEffect super; +public: + cEntityEffectWaterBreathing(int a_Duration, short a_Intensity, double a_DistanceModifier = 1): + super(a_Duration, a_Intensity, a_DistanceModifier) + { + } +}; + + + + + +class cEntityEffectInvisibility: + public cEntityEffect +{ + typedef cEntityEffect super; +public: + cEntityEffectInvisibility(int a_Duration, short a_Intensity, double a_DistanceModifier = 1): + super(a_Duration, a_Intensity, a_DistanceModifier) + { + } +}; + + + + + +class cEntityEffectBlindness: + public cEntityEffect +{ + typedef cEntityEffect super; +public: + cEntityEffectBlindness(int a_Duration, short a_Intensity, double a_DistanceModifier = 1): + super(a_Duration, a_Intensity, a_DistanceModifier) + { + } +}; + + + + + +class cEntityEffectNightVision: + public cEntityEffect +{ + typedef cEntityEffect super; +public: + cEntityEffectNightVision(int a_Duration, short a_Intensity, double a_DistanceModifier = 1): + super(a_Duration, a_Intensity, a_DistanceModifier) + { + } +}; + + + + + +class cEntityEffectHunger: + public cEntityEffect +{ + typedef cEntityEffect super; +public: + cEntityEffectHunger(int a_Duration, short a_Intensity, double a_DistanceModifier = 1): + super(a_Duration, a_Intensity, a_DistanceModifier) + { + } + + // cEntityEffect overrides: + virtual void OnTick(cPawn & a_Target) override; +}; + + + + + +class cEntityEffectWeakness: + public cEntityEffect +{ + typedef cEntityEffect super; +public: + cEntityEffectWeakness(int a_Duration, short a_Intensity, double a_DistanceModifier = 1): + super(a_Duration, a_Intensity, a_DistanceModifier) + { + } + + // cEntityEffect overrides: + virtual void OnTick(cPawn & a_Target) override; +}; + + + + + +class cEntityEffectPoison: + public cEntityEffect +{ + typedef cEntityEffect super; +public: + cEntityEffectPoison(int a_Duration, short a_Intensity, double a_DistanceModifier = 1): + super(a_Duration, a_Intensity, a_DistanceModifier) + { + } + + // cEntityEffect overrides: + virtual void OnTick(cPawn & a_Target) override; +}; + + + + + +class cEntityEffectWither: + public cEntityEffect +{ + typedef cEntityEffect super; +public: + cEntityEffectWither(int a_Duration, short a_Intensity, double a_DistanceModifier = 1): + super(a_Duration, a_Intensity, a_DistanceModifier) + { + } + + // cEntityEffect overrides: + virtual void OnTick(cPawn & a_Target) override; +}; + + + + + +class cEntityEffectHealthBoost: + public cEntityEffect +{ + typedef cEntityEffect super; +public: + cEntityEffectHealthBoost(int a_Duration, short a_Intensity, double a_DistanceModifier = 1): + super(a_Duration, a_Intensity, a_DistanceModifier) + { + } +}; + + + + + +class cEntityEffectAbsorption: + public cEntityEffect +{ + typedef cEntityEffect super; +public: + cEntityEffectAbsorption(int a_Duration, short a_Intensity, double a_DistanceModifier = 1): + super(a_Duration, a_Intensity, a_DistanceModifier) + { + } +}; + + + + + +class cEntityEffectSaturation: + public cEntityEffect +{ + typedef cEntityEffect super; +public: + cEntityEffectSaturation(int a_Duration, short a_Intensity, double a_DistanceModifier = 1): + super(a_Duration, a_Intensity, a_DistanceModifier) + { + } + + virtual void OnTick(cPawn & a_Target) override; +}; + + + + diff --git a/src/Entities/ItemFrame.cpp b/src/Entities/ItemFrame.cpp index 7bc7bda8d..4cd707faa 100644 --- a/src/Entities/ItemFrame.cpp +++ b/src/Entities/ItemFrame.cpp @@ -51,17 +51,17 @@ void cItemFrame::OnRightClicked(cPlayer & a_Player) -void cItemFrame::KilledBy(cEntity * a_Killer) +void cItemFrame::KilledBy(TakeDamageInfo & a_TDI) { if (m_Item.IsEmpty()) { SetHealth(0); - super::KilledBy(a_Killer); + super::KilledBy(a_TDI); Destroy(); return; } - if ((a_Killer != NULL) && a_Killer->IsPlayer() && !((cPlayer *)a_Killer)->IsGameModeCreative()) + if ((a_TDI.Attacker != NULL) && a_TDI.Attacker->IsPlayer() && !((cPlayer *)a_TDI.Attacker)->IsGameModeCreative()) { cItems Item; Item.push_back(m_Item); diff --git a/src/Entities/ItemFrame.h b/src/Entities/ItemFrame.h index 6577e7d94..9261e52cc 100644 --- a/src/Entities/ItemFrame.h +++ b/src/Entities/ItemFrame.h @@ -35,7 +35,7 @@ public: private: virtual void OnRightClicked(cPlayer & a_Player) override; - virtual void KilledBy(cEntity * a_Killer) override; + virtual void KilledBy(TakeDamageInfo & a_TDI) override; virtual void GetDrops(cItems & a_Items, cEntity * a_Killer) override; cItem m_Item; diff --git a/src/Entities/Painting.h b/src/Entities/Painting.h index c1024bd1b..30af6d412 100644 --- a/src/Entities/Painting.h +++ b/src/Entities/Painting.h @@ -26,9 +26,9 @@ private: virtual void SpawnOn(cClientHandle & a_Client) override; virtual void Tick(float a_Dt, cChunk & a_Chunk) override; virtual void GetDrops(cItems & a_Items, cEntity * a_Killer) override; - virtual void KilledBy(cEntity * a_Killer) override + virtual void KilledBy(TakeDamageInfo & a_TDI) override { - super::KilledBy(a_Killer); + super::KilledBy(a_TDI); Destroy(); } diff --git a/src/Entities/Pawn.cpp b/src/Entities/Pawn.cpp index fffefd538..fe6c24a7a 100644 --- a/src/Entities/Pawn.cpp +++ b/src/Entities/Pawn.cpp @@ -2,14 +2,16 @@ #include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules #include "Pawn.h" +#include "../World.h" +#include "../Bindings/PluginManager.h" -cPawn::cPawn(eEntityType a_EntityType, double a_Width, double a_Height) - : cEntity(a_EntityType, 0, 0, 0, a_Width, a_Height) - , m_bBurnable(true) +cPawn::cPawn(eEntityType a_EntityType, double a_Width, double a_Height): + super(a_EntityType, 0, 0, 0, a_Width, a_Height), + m_EntityEffects(tEffectMap()) { } @@ -17,3 +19,95 @@ cPawn::cPawn(eEntityType a_EntityType, double a_Width, double a_Height) +void cPawn::Tick(float a_Dt, cChunk & a_Chunk) +{ + // Iterate through this entity's applied effects + for (tEffectMap::iterator iter = m_EntityEffects.begin(); iter != m_EntityEffects.end();) + { + // Copies values to prevent pesky wrong accesses and erasures + cEntityEffect::eType EffectType = iter->first; + cEntityEffect * Effect = iter->second; + + Effect->OnTick(*this); + + // Iterates (must be called before any possible erasure) + ++iter; + + // Remove effect if duration has elapsed + if (Effect->GetDuration() - Effect->GetTicks() <= 0) + { + RemoveEntityEffect(EffectType); + } + + // TODO: Check for discrepancies between client and server effect values + } + + super::Tick(a_Dt, a_Chunk); +} + + + + + +void cPawn::KilledBy(TakeDamageInfo & a_TDI) +{ + ClearEntityEffects(); + super::KilledBy(a_TDI); +} + + + + + +void cPawn::AddEntityEffect(cEntityEffect::eType a_EffectType, int a_Duration, short a_Intensity, double a_DistanceModifier) +{ + // Check if the plugins allow the addition: + if (cPluginManager::Get()->CallHookEntityAddEffect(*this, a_EffectType, a_Duration, a_Intensity, a_DistanceModifier)) + { + // A plugin disallows the addition, bail out. + return; + } + + // No need to add empty effects: + if (a_EffectType == cEntityEffect::effNoEffect) + { + return; + } + a_Duration = (int)(a_Duration * a_DistanceModifier); + + m_EntityEffects[a_EffectType] = cEntityEffect::CreateEntityEffect(a_EffectType, a_Duration, a_Intensity, a_DistanceModifier); + m_World->BroadcastEntityEffect(*this, a_EffectType, a_Intensity, a_Duration); + m_EntityEffects[a_EffectType]->OnActivate(*this); +} + + + + + +void cPawn::RemoveEntityEffect(cEntityEffect::eType a_EffectType) +{ + m_World->BroadcastRemoveEntityEffect(*this, a_EffectType); + m_EntityEffects[a_EffectType]->OnDeactivate(*this); + delete m_EntityEffects[a_EffectType]; + m_EntityEffects.erase(a_EffectType); +} + + + + + +void cPawn::ClearEntityEffects() +{ + // Iterate through this entity's applied effects + for (tEffectMap::iterator iter = m_EntityEffects.begin(); iter != m_EntityEffects.end();) + { + // Copy values to prevent pesky wrong erasures + cEntityEffect::eType EffectType = iter->first; + + // Iterates (must be called before any possible erasure) + ++iter; + + // Remove effect + RemoveEntityEffect(EffectType); + } +} diff --git a/src/Entities/Pawn.h b/src/Entities/Pawn.h index e76337d86..63c7cfbb6 100644 --- a/src/Entities/Pawn.h +++ b/src/Entities/Pawn.h @@ -2,6 +2,7 @@ #pragma once #include "Entity.h" +#include "EntityEffect.h" @@ -18,9 +19,34 @@ public: CLASS_PROTODEF(cPawn); cPawn(eEntityType a_EntityType, double a_Width, double a_Height); + + virtual void Tick(float a_Dt, cChunk & a_Chunk) override; + virtual void KilledBy(TakeDamageInfo & a_TDI) override; + + // tolua_begin + + /** Applies an entity effect + Checks with plugins if they allow the addition. + @param a_EffectType The entity effect to apply + @param a_EffectDurationTicks The duration of the effect + @param a_EffectIntensity The level of the effect (0 = Potion I, 1 = Potion II, etc) + @param a_DistanceModifier The scalar multiplied to the potion duration, only applies to splash potions) + */ + void AddEntityEffect(cEntityEffect::eType a_EffectType, int a_EffectDurationTicks, short a_EffectIntensity, double a_DistanceModifier = 1); + + /** Removes a currently applied entity effect + @param a_EffectType The entity effect to remove + */ + void RemoveEntityEffect(cEntityEffect::eType a_EffectType); + + /** Removes all currently applied entity effects (used when drinking milk) */ + void ClearEntityEffects(void); + + // tolua_end protected: - bool m_bBurnable; + typedef std::map<cEntityEffect::eType, cEntityEffect *> tEffectMap; + tEffectMap m_EntityEffects; } ; // tolua_export diff --git a/src/Entities/Player.cpp b/src/Entities/Player.cpp index b1b7fc74e..da50843b6 100644 --- a/src/Entities/Player.cpp +++ b/src/Entities/Player.cpp @@ -41,7 +41,6 @@ cPlayer::cPlayer(cClientHandle* a_Client, const AString & a_PlayerName) : m_FoodSaturationLevel(5.0), m_FoodTickTimer(0), m_FoodExhaustionLevel(0.0), - m_FoodPoisonedTicksRemaining(0), m_LastJumpHeight(0), m_LastGroundHeight(0), m_bTouchGround(false), @@ -130,7 +129,7 @@ cPlayer::~cPlayer(void) if (!cRoot::Get()->GetPluginManager()->CallHookPlayerDestroyed(*this)) { cRoot::Get()->BroadcastChatLeave(Printf("%s has left the game", GetName().c_str())); - LOGINFO("Player %s has left the game.", GetName().c_str()); + LOGINFO("Player %s has left the game", GetName().c_str()); } LOGD("Deleting cPlayer \"%s\" at %p, ID %d", GetName().c_str(), this, GetUniqueID()); @@ -563,18 +562,9 @@ void cPlayer::SetFoodExhaustionLevel(double a_FoodExhaustionLevel) -void cPlayer::SetFoodPoisonedTicksRemaining(int a_FoodPoisonedTicksRemaining) -{ - m_FoodPoisonedTicksRemaining = a_FoodPoisonedTicksRemaining; -} - - - - - bool cPlayer::Feed(int a_Food, double a_Saturation) { - if (m_FoodLevel >= MAX_FOOD_LEVEL) + if (IsSatiated()) { return false; } @@ -590,17 +580,7 @@ bool cPlayer::Feed(int a_Food, double a_Saturation) void cPlayer::FoodPoison(int a_NumTicks) { - bool HasBeenFoodPoisoned = (m_FoodPoisonedTicksRemaining > 0); - m_FoodPoisonedTicksRemaining = std::max(m_FoodPoisonedTicksRemaining, a_NumTicks); - if (!HasBeenFoodPoisoned) - { - m_World->BroadcastRemoveEntityEffect(*this, E_EFFECT_HUNGER); - SendHealth(); - } - else - { - m_World->BroadcastEntityEffect(*this, E_EFFECT_HUNGER, 0, 400); // Give the player the "Hunger" effect for 20 seconds. - } + AddEntityEffect(cEntityEffect::effHunger, a_NumTicks, 0, NULL); } @@ -885,9 +865,9 @@ bool cPlayer::DoTakeDamage(TakeDamageInfo & a_TDI) -void cPlayer::KilledBy(cEntity * a_Killer) +void cPlayer::KilledBy(TakeDamageInfo & a_TDI) { - super::KilledBy(a_Killer); + super::KilledBy(a_TDI); if (m_Health > 0) { @@ -911,19 +891,40 @@ void cPlayer::KilledBy(cEntity * a_Killer) m_World->SpawnItemPickups(Pickups, GetPosX(), GetPosY(), GetPosZ(), 10); SaveToDisk(); // Save it, yeah the world is a tough place ! - if (a_Killer == NULL) + if (a_TDI.Attacker == NULL) { - GetWorld()->BroadcastChatDeath(Printf("%s was killed by environmental damage", GetName().c_str())); + AString DamageText; + switch (a_TDI.DamageType) + { + case dtRangedAttack: DamageText = "was shot"; break; + case dtLightning: DamageText = "was plasmified by lightining"; break; + case dtFalling: DamageText = (GetWorld()->GetTickRandomNumber(10) % 2 == 0) ? "fell to death" : "hit the ground too hard"; break; + case dtDrowning: DamageText = "drowned"; break; + case dtSuffocating: DamageText = (GetWorld()->GetTickRandomNumber(10) % 2 == 0) ? "git merge'd into a block" : "fused with a block"; break; + case dtStarving: DamageText = "forgot the importance of food"; break; + case dtCactusContact: DamageText = "was impaled on a cactus"; break; + case dtLavaContact: DamageText = "was melted by lava"; break; + case dtPoisoning: DamageText = "died from septicaemia"; break; + case dtOnFire: DamageText = "forgot to stop, drop, and roll"; break; + case dtFireContact: DamageText = "burnt themselves to death"; break; + case dtInVoid: DamageText = "somehow fell out of the world"; break; + case dtPotionOfHarming: DamageText = "was magicked to death"; break; + case dtEnderPearl: DamageText = "misused an ender pearl"; break; + case dtAdmin: DamageText = "was administrator'd"; break; + case dtExplosion: DamageText = "blew up"; break; + default: DamageText = "died, somehow; we've no idea how though"; break; + } + GetWorld()->BroadcastChatDeath(Printf("%s %s", GetName().c_str(), DamageText.c_str())); } - else if (a_Killer->IsPlayer()) + else if (a_TDI.Attacker->IsPlayer()) { - cPlayer * Killer = (cPlayer *)a_Killer; + cPlayer * Killer = (cPlayer *)a_TDI.Attacker; GetWorld()->BroadcastChatDeath(Printf("%s was killed by %s", GetName().c_str(), Killer->GetName().c_str())); } else { - AString KillerClass = a_Killer->GetClass(); + AString KillerClass = a_TDI.Attacker->GetClass(); KillerClass.erase(KillerClass.begin()); // Erase the 'c' of the class (e.g. "cWitch" -> "Witch") GetWorld()->BroadcastChatDeath(Printf("%s was killed by a %s", GetName().c_str(), KillerClass.c_str())); @@ -1804,7 +1805,7 @@ bool cPlayer::LoadFromFile(const AString & a_FileName) cStatSerializer StatSerializer(cRoot::Get()->GetDefaultWorld()->GetName(), GetName(), &m_Stats); StatSerializer.Load(); - LOGD("Player \"%s\" was read from file \"%s\", spawning at {%.2f, %.2f, %.2f} in world \"%s\"", + LOGD("Player %s was read from file \"%s\", spawning at {%.2f, %.2f, %.2f} in world \"%s\"", GetName().c_str(), a_FileName.c_str(), GetPosX(), GetPosY(), GetPosZ(), m_LoadedWorldName.c_str() ); @@ -1994,17 +1995,6 @@ void cPlayer::HandleFood(void) } } - // Apply food poisoning food exhaustion: - if (m_FoodPoisonedTicksRemaining > 0) - { - m_FoodPoisonedTicksRemaining--; - m_FoodExhaustionLevel += 0.025; // 0.5 per second = 0.025 per tick - } - else - { - m_World->BroadcastRemoveEntityEffect(*this, E_EFFECT_HUNGER); // Remove the "Hunger" effect. - } - // Apply food exhaustion that has accumulated: if (m_FoodExhaustionLevel >= 4.0) { @@ -2141,6 +2131,8 @@ void cPlayer::ApplyFoodExhaustionFromMovement() { return; } + + // If we have just teleported, apply no exhaustion if (m_bIsTeleporting) { m_bIsTeleporting = false; @@ -2152,6 +2144,13 @@ void cPlayer::ApplyFoodExhaustionFromMovement() { return; } + + // Process exhaustion every two ticks as that is how frequently m_LastPos is updated + // Otherwise, we apply exhaustion for a 'movement' every tick, one of which is an already processed value + if (GetWorld()->GetWorldAge() % 2 != 0) + { + return; + } // Calculate the distance travelled, update the last pos: Vector3d Movement(GetPosition() - m_LastPos); diff --git a/src/Entities/Player.h b/src/Entities/Player.h index 8f9b46e0f..793f11e57 100644 --- a/src/Entities/Player.h +++ b/src/Entities/Player.h @@ -265,13 +265,12 @@ public: void TossPickup(const cItem & a_Item); /** Heals the player by the specified amount of HPs (positive only); sends health update */ - void Heal(int a_Health); + virtual void Heal(int a_Health) override; int GetFoodLevel (void) const { return m_FoodLevel; } double GetFoodSaturationLevel (void) const { return m_FoodSaturationLevel; } int GetFoodTickTimer (void) const { return m_FoodTickTimer; } double GetFoodExhaustionLevel (void) const { return m_FoodExhaustionLevel; } - int GetFoodPoisonedTicksRemaining(void) const { return m_FoodPoisonedTicksRemaining; } /** Returns true if the player is satiated, i. e. their foodlevel is at the max and they cannot eat anymore */ bool IsSatiated(void) const { return (m_FoodLevel >= MAX_FOOD_LEVEL); } @@ -280,7 +279,6 @@ public: void SetFoodSaturationLevel (double a_FoodSaturationLevel); void SetFoodTickTimer (int a_FoodTickTimer); void SetFoodExhaustionLevel (double a_FoodExhaustionLevel); - void SetFoodPoisonedTicksRemaining(int a_FoodPoisonedTicksRemaining); /** Adds to FoodLevel and FoodSaturationLevel, returns true if any food has been consumed, false if player "full" */ bool Feed(int a_Food, double a_Saturation); @@ -291,7 +289,7 @@ public: m_FoodExhaustionLevel += a_Exhaustion; } - /** Starts the food poisoning for the specified amount of ticks; if already foodpoisoned, sets FoodPoisonedTicksRemaining to the larger of the two */ + /** Starts the food poisoning for the specified amount of ticks */ void FoodPoison(int a_NumTicks); /** Returns true if the player is currently in the process of eating the currently equipped item */ @@ -324,7 +322,7 @@ public: /** Aborts the current eating operation */ void AbortEating(void); - virtual void KilledBy(cEntity * a_Killer) override; + virtual void KilledBy(TakeDamageInfo & a_TDI) override; virtual void Killed(cEntity * a_Victim) override; @@ -449,14 +447,11 @@ protected: double m_FoodSaturationLevel; /** Count-up to the healing or damaging action, based on m_FoodLevel */ - int m_FoodTickTimer; + int m_FoodTickTimer; /** A "buffer" which adds up hunger before it is substracted from m_FoodSaturationLevel or m_FoodLevel. Each action adds a little */ double m_FoodExhaustionLevel; - /** Number of ticks remaining for the foodpoisoning effect; zero if not foodpoisoned */ - int m_FoodPoisonedTicksRemaining; - float m_LastJumpHeight; float m_LastGroundHeight; bool m_bTouchGround; diff --git a/src/Entities/ProjectileEntity.cpp b/src/Entities/ProjectileEntity.cpp index 0bb34019e..019ebeaa5 100644 --- a/src/Entities/ProjectileEntity.cpp +++ b/src/Entities/ProjectileEntity.cpp @@ -21,6 +21,7 @@ #include "FireChargeEntity.h" #include "FireworkEntity.h" #include "GhastFireballEntity.h" +#include "WitherSkullEntity.h" #include "Player.h" @@ -243,7 +244,7 @@ cProjectileEntity::cProjectileEntity(eKind a_Kind, cEntity * a_Creator, const Ve -cProjectileEntity * cProjectileEntity::Create(eKind a_Kind, cEntity * a_Creator, double a_X, double a_Y, double a_Z, const cItem & a_Item, const Vector3d * a_Speed) +cProjectileEntity * cProjectileEntity::Create(eKind a_Kind, cEntity * a_Creator, double a_X, double a_Y, double a_Z, const cItem * a_Item, const Vector3d * a_Speed) { Vector3d Speed; if (a_Speed != NULL) @@ -260,14 +261,16 @@ cProjectileEntity * cProjectileEntity::Create(eKind a_Kind, cEntity * a_Creator, case pkGhastFireball: return new cGhastFireballEntity (a_Creator, a_X, a_Y, a_Z, Speed); case pkFireCharge: return new cFireChargeEntity (a_Creator, a_X, a_Y, a_Z, Speed); case pkExpBottle: return new cExpBottleEntity (a_Creator, a_X, a_Y, a_Z, Speed); + case pkWitherSkull: return new cWitherSkullEntity (a_Creator, a_X, a_Y, a_Z, Speed); case pkFirework: { - if (a_Item.m_FireworkItem.m_Colours.empty()) + ASSERT(a_Item != NULL); + if (a_Item->m_FireworkItem.m_Colours.empty()) { return NULL; } - return new cFireworkEntity(a_Creator, a_X, a_Y, a_Z, a_Item); + return new cFireworkEntity(a_Creator, a_X, a_Y, a_Z, *a_Item); } } @@ -310,7 +313,7 @@ AString cProjectileEntity::GetMCAClassName(void) const case pkFireCharge: return "SmallFireball"; case pkEnderPearl: return "ThrownEnderpearl"; case pkExpBottle: return "ThrownExpBottle"; - case pkSplashPotion: return "ThrownPotion"; + case pkSplashPotion: return "SplashPotion"; case pkWitherSkull: return "WitherSkull"; case pkFirework: return "Firework"; case pkFishingFloat: return ""; // Unknown, perhaps MC doesn't save this? diff --git a/src/Entities/ProjectileEntity.h b/src/Entities/ProjectileEntity.h index 7b38169e2..14cee1272 100644 --- a/src/Entities/ProjectileEntity.h +++ b/src/Entities/ProjectileEntity.h @@ -46,7 +46,7 @@ public: cProjectileEntity(eKind a_Kind, cEntity * a_Creator, double a_X, double a_Y, double a_Z, double a_Width, double a_Height); cProjectileEntity(eKind a_Kind, cEntity * a_Creator, const Vector3d & a_Pos, const Vector3d & a_Speed, double a_Width, double a_Height); - static cProjectileEntity * Create(eKind a_Kind, cEntity * a_Creator, double a_X, double a_Y, double a_Z, const cItem & a_Item, const Vector3d * a_Speed = NULL); + static cProjectileEntity * Create(eKind a_Kind, cEntity * a_Creator, double a_X, double a_Y, double a_Z, const cItem * a_Item, const Vector3d * a_Speed = NULL); /// Called by the physics blocktracer when the entity hits a solid block, the hit position and the face hit (BLOCK_FACE_) is given virtual void OnHitSolidBlock(const Vector3d & a_HitPos, eBlockFace a_HitFace); diff --git a/src/Entities/SplashPotionEntity.cpp b/src/Entities/SplashPotionEntity.cpp new file mode 100644 index 000000000..ed5afaf11 --- /dev/null +++ b/src/Entities/SplashPotionEntity.cpp @@ -0,0 +1,120 @@ +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "SplashPotionEntity.h" +#include "Pawn.h" + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cSplashPotionEntityCallback: + +/** Used to distribute the splashed potion effect among nearby entities */ +class cSplashPotionCallback : + public cEntityCallback +{ +public: + /** Creates the callback. + @param a_HitPos The position where the splash potion has splashed + @param a_EntityEffectType The effect type of the potion + @param a_EntityEffect The effect description */ + cSplashPotionCallback(const Vector3d & a_HitPos, cEntityEffect::eType a_EntityEffectType, const cEntityEffect & a_EntityEffect) : + m_HitPos(a_HitPos), + m_EntityEffectType(a_EntityEffectType), + m_EntityEffect(a_EntityEffect) + { + } + + /** Called by cWorld::ForEachEntity(), adds the stored entity effect to the entity, if it is close enough. */ + virtual bool Item(cEntity * a_Entity) override + { + double SplashDistance = (a_Entity->GetPosition() - m_HitPos).Length(); + if (SplashDistance >= 20) + { + // Too far away + return false; + } + if (!a_Entity->IsPawn()) + { + // Not an entity that can take effects + return false; + } + + // y = -0.25x + 1, where x is the distance from the player. Approximation for potion splash. + // TODO: better equation + double Reduction = -0.25 * SplashDistance + 1.0; + if (Reduction < 0) + { + Reduction = 0; + } + + ((cPawn *) a_Entity)->AddEntityEffect(m_EntityEffectType, m_EntityEffect.GetDuration(), m_EntityEffect.GetIntensity(), Reduction); + return false; + } + +private: + const Vector3d & m_HitPos; + cEntityEffect::eType m_EntityEffectType; + const cEntityEffect & m_EntityEffect; +}; + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cSplashPotionEntity: + +cSplashPotionEntity::cSplashPotionEntity( + cEntity * a_Creator, + double a_X, double a_Y, double a_Z, + const Vector3d & a_Speed, + cEntityEffect::eType a_EntityEffectType, + cEntityEffect a_EntityEffect, + int a_PotionParticleType +) : + super(pkSplashPotion, a_Creator, a_X, a_Y, a_Z, 0.25, 0.25), + m_EntityEffectType(a_EntityEffectType), + m_EntityEffect(a_EntityEffect), + m_PotionParticleType(a_PotionParticleType) +{ + SetSpeed(a_Speed); +} + + + + + +void cSplashPotionEntity::OnHitSolidBlock(const Vector3d & a_HitPos, eBlockFace a_HitFace) +{ + Splash(a_HitPos); + Destroy(); +} + + + + + +void cSplashPotionEntity::OnHitEntity(cEntity & a_EntityHit, const Vector3d & a_HitPos) +{ + a_EntityHit.TakeDamage(dtRangedAttack, this, 0, 1); + Splash(a_HitPos); + Destroy(true); +} + + + + + +void cSplashPotionEntity::Splash(const Vector3d & a_HitPos) +{ + cSplashPotionCallback Callback(a_HitPos, m_EntityEffectType, m_EntityEffect); + m_World->ForEachEntity(Callback); + + m_World->BroadcastSoundParticleEffect(2002, (int)a_HitPos.x, (int)a_HitPos.y, (int)a_HitPos.z, m_PotionParticleType); +} + + + + diff --git a/src/Entities/SplashPotionEntity.h b/src/Entities/SplashPotionEntity.h new file mode 100644 index 000000000..076e477da --- /dev/null +++ b/src/Entities/SplashPotionEntity.h @@ -0,0 +1,59 @@ +// +// SplashPotionEntity.h +// + +#pragma once + +#include "ProjectileEntity.h" +#include "EntityEffect.h" +#include "../World.h" +#include "Entity.h" + + + + +// tolua_begin + +class cSplashPotionEntity : + public cProjectileEntity +{ + typedef cProjectileEntity super; + +public: + + // tolua_end + + CLASS_PROTODEF(cSplashPotionEntity); + + cSplashPotionEntity( + cEntity * a_Creator, + double a_X, double a_Y, double a_Z, + const Vector3d & a_Speed, + cEntityEffect::eType a_EntityEffectType, + cEntityEffect a_EntityEffect, + int a_PotionParticleType + ); + + cEntityEffect::eType GetEntityEffectType (void) const { return m_EntityEffectType; } + cEntityEffect GetEntityEffect (void) const { return m_EntityEffect; } + int GetPotionParticleType(void) const { return m_PotionParticleType; } + + void SetEntityEffectType(cEntityEffect::eType a_EntityEffectType) { m_EntityEffectType = a_EntityEffectType; } + void SetEntityEffect(cEntityEffect a_EntityEffect) { m_EntityEffect = a_EntityEffect; } + void SetPotionParticleType(int a_PotionParticleType) { m_PotionParticleType = a_PotionParticleType; } + +protected: + + cEntityEffect::eType m_EntityEffectType; + cEntityEffect m_EntityEffect; + int m_PotionParticleType; + + + // cProjectileEntity overrides: + virtual void OnHitSolidBlock(const Vector3d & a_HitPos, eBlockFace a_HitFace) override; + virtual void OnHitEntity (cEntity & a_EntityHit, const Vector3d & a_HitPos) override; + + /** Splashes the potion, fires its particle effects and sounds + @param a_HitPos The position where the potion will splash */ + void Splash(const Vector3d & a_HitPos); +} ; // tolua_export diff --git a/src/Entities/WitherSkullEntity.cpp b/src/Entities/WitherSkullEntity.cpp new file mode 100644 index 000000000..a7e774bba --- /dev/null +++ b/src/Entities/WitherSkullEntity.cpp @@ -0,0 +1,49 @@ + +// WitherSkullEntity.cpp + +// Implements the cWitherSkullEntity class representing the entity used by both blue and black wither skulls + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "WitherSkullEntity.h" +#include "../World.h" + + + + + +cWitherSkullEntity::cWitherSkullEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z, const Vector3d & a_Speed) : + super(pkWitherSkull, a_Creator, a_X, a_Y, a_Z, 0.25, 0.25) +{ + SetSpeed(a_Speed); +} + + + + + +void cWitherSkullEntity::OnHitSolidBlock(const Vector3d & a_HitPos, eBlockFace a_HitFace) +{ + // TODO: Explode + // TODO: Apply wither effect to entities nearby + Destroy(); +} + + + + + +void cWitherSkullEntity::OnHitEntity(cEntity & a_EntityHit, const Vector3d & a_HitPos) +{ + // TODO: If entity is Ender Crystal, destroy it + a_EntityHit.TakeDamage(dtRangedAttack, this, 0, 1); + + // TODO: Explode + // TODO: Apply wither effect to entity and others nearby + + Destroy(true); +} + + + + diff --git a/src/Entities/WitherSkullEntity.h b/src/Entities/WitherSkullEntity.h new file mode 100644 index 000000000..8b3639802 --- /dev/null +++ b/src/Entities/WitherSkullEntity.h @@ -0,0 +1,35 @@ + +// WitherSkullEntity.h + +// Declares the cWitherSkullEntity class representing the entity used by both blue and black wither skulls + +#pragma once + +#include "ProjectileEntity.h" + + + + + +// tolua_begin + +class cWitherSkullEntity : +public cProjectileEntity +{ + typedef cProjectileEntity super; + +public: + + // tolua_end + + CLASS_PROTODEF(cWitherSkullEntity); + + cWitherSkullEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z, const Vector3d & a_Speed); + +protected: + + // cProjectileEntity overrides: + virtual void OnHitSolidBlock(const Vector3d & a_HitPos, eBlockFace a_HitFace) override; + virtual void OnHitEntity (cEntity & a_EntityHit, const Vector3d & a_HitPos) override; + +} ; // tolua_export |