summaryrefslogtreecommitdiffstats
path: root/src/Entities
diff options
context:
space:
mode:
Diffstat (limited to 'src/Entities')
-rw-r--r--src/Entities/ArrowEntity.cpp4
-rw-r--r--src/Entities/Effects.h30
-rw-r--r--src/Entities/EnderCrystal.cpp4
-rw-r--r--src/Entities/EnderCrystal.h2
-rw-r--r--src/Entities/Entity.cpp19
-rw-r--r--src/Entities/Entity.h5
-rw-r--r--src/Entities/EntityEffect.cpp287
-rw-r--r--src/Entities/EntityEffect.h476
-rw-r--r--src/Entities/ItemFrame.cpp6
-rw-r--r--src/Entities/ItemFrame.h2
-rw-r--r--src/Entities/Painting.h4
-rw-r--r--src/Entities/Pawn.cpp100
-rw-r--r--src/Entities/Pawn.h28
-rw-r--r--src/Entities/Player.cpp83
-rw-r--r--src/Entities/Player.h13
-rw-r--r--src/Entities/ProjectileEntity.cpp11
-rw-r--r--src/Entities/ProjectileEntity.h2
-rw-r--r--src/Entities/SplashPotionEntity.cpp120
-rw-r--r--src/Entities/SplashPotionEntity.h59
-rw-r--r--src/Entities/WitherSkullEntity.cpp49
-rw-r--r--src/Entities/WitherSkullEntity.h35
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