summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--MCServer/Plugins/APIDump/APIDesc.lua3
-rw-r--r--MCServer/Plugins/APIDump/Hooks/OnEntityAddEffect.lua34
-rw-r--r--src/Bindings/AllToLua.pkg2
-rw-r--r--src/Bindings/Plugin.h1
-rw-r--r--src/Bindings/PluginLua.cpp21
-rw-r--r--src/Bindings/PluginLua.h1
-rw-r--r--src/Bindings/PluginManager.cpp21
-rw-r--r--src/Bindings/PluginManager.h2
-rw-r--r--src/BlockID.cpp3
-rw-r--r--src/BlockID.h2
-rw-r--r--src/CMakeLists.txt2
-rw-r--r--src/ClientHandle.cpp7
-rw-r--r--src/Entities/Effects.h30
-rw-r--r--src/Entities/Entity.cpp4
-rw-r--r--src/Entities/Entity.h3
-rw-r--r--src/Entities/EntityEffect.cpp289
-rw-r--r--src/Entities/EntityEffect.h430
-rw-r--r--src/Entities/Pawn.cpp100
-rw-r--r--src/Entities/Pawn.h28
-rw-r--r--src/Entities/Player.cpp35
-rw-r--r--src/Entities/Player.h9
-rw-r--r--src/Entities/ProjectileEntity.cpp2
-rw-r--r--src/Entities/SplashPotionEntity.cpp89
-rw-r--r--src/Entities/SplashPotionEntity.h59
-rw-r--r--src/Entities/WitherSkullEntity.cpp40
-rw-r--r--src/Entities/WitherSkullEntity.h34
-rw-r--r--src/Globals.h1
-rw-r--r--src/Items/ItemHandler.cpp25
-rw-r--r--src/Items/ItemHandler.h5
-rw-r--r--src/Items/ItemMilk.h28
-rw-r--r--src/Items/ItemPotion.h154
-rw-r--r--src/Mobs/AggressiveMonster.cpp10
-rw-r--r--src/Mobs/CaveSpider.cpp15
-rw-r--r--src/Mobs/CaveSpider.h1
-rw-r--r--src/Mobs/Monster.cpp20
-rw-r--r--src/Mobs/Monster.h4
36 files changed, 1425 insertions, 89 deletions
diff --git a/MCServer/Plugins/APIDump/APIDesc.lua b/MCServer/Plugins/APIDump/APIDesc.lua
index ea9f38db4..0e626c580 100644
--- a/MCServer/Plugins/APIDump/APIDesc.lua
+++ b/MCServer/Plugins/APIDump/APIDesc.lua
@@ -1691,6 +1691,9 @@ a_Player:OpenWindow(Window);
TakeDamage = { Return = "" },
KilledBy = { Return = "" },
GetHealth = { Return = "number" },
+ AddEntityEffect = { Params = "EffectType, {{cEntityEffect}}", Return = "", Notes = "Applies an entity effect" },
+ RemoveEntityEffect = { Params = "EffectType", Return = "", Notes = "Removes a currently applied entity effect" },
+ ClearEntityEffects = { Return = "", Notes = "Removes all currently applied entity effects" },
},
Inherits = "cEntity",
}, -- cPawn
diff --git a/MCServer/Plugins/APIDump/Hooks/OnEntityAddEffect.lua b/MCServer/Plugins/APIDump/Hooks/OnEntityAddEffect.lua
new file mode 100644
index 000000000..423a2200b
--- /dev/null
+++ b/MCServer/Plugins/APIDump/Hooks/OnEntityAddEffect.lua
@@ -0,0 +1,34 @@
+return
+{
+ HOOK_ENTITY_ADD_EFFECT =
+ {
+ CalledWhen = "An entity effect is about to get added to an entity.",
+ DefaultFnName = "OnEntityAddEffect", -- also used as pagename
+ Desc = [[
+ This hook is called whenever an entity effect is about to be added to an entity. The plugin may
+ disallow the addition by returning true.</p>
+ <p>Note that this hook only fires for adding the effect, but not for the actual effect application. See
+ also the {{OnEntityRemoveEffect|HOOK_ENTITY_REMOVE_EFFECT}} for notification about effects expiring /
+ removing, and {{OnEntityApplyEffect|HOOK_ENTITY_APPLY_EFFECT}} for the actual effect application to the
+ entity.
+ ]],
+ Params =
+ {
+ { Name = "Entity", Type = "{{cEntity}}", Notes = "The entity to which the effect is about to be added" },
+ { Name = "EffectType", Type = "number", Notes = "The type of the effect to be added. One of the effXXX constants." },
+ { Name = "EffectDuration", Type = "number", Notes = "The duration of the effect to be added, in ticks." },
+ { Name = "EffectIntensity", Type = "number", Notes = "The intensity (level) of the effect to be added. " },
+ { Name = "Originator", Type = "{{cEntity}}", Notes = "The entity who originated the effect (threw the potion, the cavespider that used poison bite, etc.) May be nil if there's no originator associated with the effect. " },
+ { Name = "DistanceModifier", Type = "number", Notes = "The modifier for the effect intensity, based on distance. Used mainly for splash potions." },
+ },
+ Returns = [[
+ If the plugin returns true, the effect will not be added and none of the remaining hook handlers will
+ be called. If the plugin returns false, MCServer calls all the remaining hook handlers and finally
+ the effect is added to the entity.
+ ]],
+ }, -- HOOK_EXECUTE_COMMAND
+}
+
+
+
+
diff --git a/src/Bindings/AllToLua.pkg b/src/Bindings/AllToLua.pkg
index 4fe86e1c5..1e5dfd2fe 100644
--- a/src/Bindings/AllToLua.pkg
+++ b/src/Bindings/AllToLua.pkg
@@ -40,7 +40,7 @@ $cfile "../Entities/Painting.h"
$cfile "../Entities/Pickup.h"
$cfile "../Entities/ProjectileEntity.h"
$cfile "../Entities/TNTEntity.h"
-$cfile "../Entities/Effects.h"
+$cfile "../Entities/EntityEffect.h"
$cfile "../Server.h"
$cfile "../World.h"
$cfile "../Inventory.h"
diff --git a/src/Bindings/Plugin.h b/src/Bindings/Plugin.h
index 8ba20c026..5196a03dc 100644
--- a/src/Bindings/Plugin.h
+++ b/src/Bindings/Plugin.h
@@ -57,6 +57,7 @@ public:
virtual bool OnCollectingPickup (cPlayer * a_Player, cPickup * a_Pickup) = 0;
virtual bool OnCraftingNoRecipe (const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe) = 0;
virtual bool OnDisconnect (cClientHandle & a_Client, const AString & a_Reason) = 0;
+ virtual bool OnEntityAddEffect (cEntity & a_Entity, int a_EffectType, int a_EffectDurationTicks, int a_EffectIntensity, double a_DistanceModifier) = 0;
virtual bool OnExecuteCommand (cPlayer * a_Player, const AStringVector & a_Split) = 0;
virtual bool OnExploded (cWorld & a_World, double a_ExplosionSize, bool a_CanCauseFire, double a_X, double a_Y, double a_Z, eExplosionSource a_Source, void * a_SourceData) = 0;
virtual bool OnExploding (cWorld & a_World, double & a_ExplosionSize, bool & a_CanCauseFire, double a_X, double a_Y, double a_Z, eExplosionSource a_Source, void * a_SourceData) = 0;
diff --git a/src/Bindings/PluginLua.cpp b/src/Bindings/PluginLua.cpp
index 104380ea4..a08803189 100644
--- a/src/Bindings/PluginLua.cpp
+++ b/src/Bindings/PluginLua.cpp
@@ -420,6 +420,26 @@ bool cPluginLua::OnDisconnect(cClientHandle & a_Client, const AString & a_Reason
+bool cPluginLua::OnEntityAddEffect(cEntity & a_Entity, int a_EffectType, int a_EffectDurationTicks, int a_EffectIntensity, double a_DistanceModifier)
+{
+ cCSLock Lock(m_CriticalSection);
+ bool res = false;
+ cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_ENTITY_ADD_EFFECT];
+ for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr)
+ {
+ m_LuaState.Call((int)(**itr), &a_Entity, a_EffectType, a_EffectDurationTicks, a_EffectIntensity, a_DistanceModifier, cLuaState::Return, res);
+ if (res)
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+
+
+
bool cPluginLua::OnExecuteCommand(cPlayer * a_Player, const AStringVector & a_Split)
{
cCSLock Lock(m_CriticalSection);
@@ -1524,6 +1544,7 @@ const char * cPluginLua::GetHookFnName(int a_HookType)
case cPluginManager::HOOK_CRAFTING_NO_RECIPE: return "OnCraftingNoRecipe";
case cPluginManager::HOOK_DISCONNECT: return "OnDisconnect";
case cPluginManager::HOOK_PLAYER_ANIMATION: return "OnPlayerAnimation";
+ case cPluginManager::HOOK_ENTITY_ADD_EFFECT: return "OnEntityAddEffect";
case cPluginManager::HOOK_EXECUTE_COMMAND: return "OnExecuteCommand";
case cPluginManager::HOOK_HANDSHAKE: return "OnHandshake";
case cPluginManager::HOOK_KILLING: return "OnKilling";
diff --git a/src/Bindings/PluginLua.h b/src/Bindings/PluginLua.h
index 9c9de95c6..0ea76ba24 100644
--- a/src/Bindings/PluginLua.h
+++ b/src/Bindings/PluginLua.h
@@ -80,6 +80,7 @@ public:
virtual bool OnCollectingPickup (cPlayer * a_Player, cPickup * a_Pickup) override;
virtual bool OnCraftingNoRecipe (const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe) override;
virtual bool OnDisconnect (cClientHandle & a_Client, const AString & a_Reason) override;
+ virtual bool OnEntityAddEffect (cEntity & a_Entity, int a_EffectType, int a_EffectDurationTicks, int a_EffectIntensity, double a_DistanceModifier) override;
virtual bool OnExecuteCommand (cPlayer * a_Player, const AStringVector & a_Split) override;
virtual bool OnExploded (cWorld & a_World, double a_ExplosionSize, bool a_CanCauseFire, double a_X, double a_Y, double a_Z, eExplosionSource a_Source, void * a_SourceData) override;
virtual bool OnExploding (cWorld & a_World, double & a_ExplosionSize, bool & a_CanCauseFire, double a_X, double a_Y, double a_Z, eExplosionSource a_Source, void * a_SourceData) override;
diff --git a/src/Bindings/PluginManager.cpp b/src/Bindings/PluginManager.cpp
index 7e6502515..2264faf8b 100644
--- a/src/Bindings/PluginManager.cpp
+++ b/src/Bindings/PluginManager.cpp
@@ -474,6 +474,27 @@ bool cPluginManager::CallHookDisconnect(cClientHandle & a_Client, const AString
+bool cPluginManager::CallHookEntityAddEffect(cEntity & a_Entity, int a_EffectType, int a_EffectDurationTicks, int a_EffectIntensity, double a_DistanceModifier)
+{
+ HookMap::iterator Plugins = m_Hooks.find(HOOK_ENTITY_ADD_EFFECT);
+ if (Plugins == m_Hooks.end())
+ {
+ return false;
+ }
+ for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr)
+ {
+ if ((*itr)->OnEntityAddEffect(a_Entity, a_EffectType, a_EffectDurationTicks, a_EffectIntensity, a_DistanceModifier))
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+
+
+
bool cPluginManager::CallHookExecuteCommand(cPlayer * a_Player, const AStringVector & a_Split)
{
FIND_HOOK(HOOK_EXECUTE_COMMAND);
diff --git a/src/Bindings/PluginManager.h b/src/Bindings/PluginManager.h
index d435024bb..2f0ec0e27 100644
--- a/src/Bindings/PluginManager.h
+++ b/src/Bindings/PluginManager.h
@@ -82,6 +82,7 @@ public: // tolua_export
HOOK_CRAFTING_NO_RECIPE,
HOOK_DISCONNECT,
HOOK_PLAYER_ANIMATION,
+ HOOK_ENTITY_ADD_EFFECT,
HOOK_EXECUTE_COMMAND,
HOOK_EXPLODED,
HOOK_EXPLODING,
@@ -183,6 +184,7 @@ public: // tolua_export
bool CallHookCollectingPickup (cPlayer * a_Player, cPickup & a_Pickup);
bool CallHookCraftingNoRecipe (const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe);
bool CallHookDisconnect (cClientHandle & a_Client, const AString & a_Reason);
+ bool CallHookEntityAddEffect (cEntity & a_Entity, int a_EffectType, int a_EffectDurationTicks, int a_EffectIntensity, double a_DistanceModifier);
bool CallHookExecuteCommand (cPlayer * a_Player, const AStringVector & a_Split); // If a_Player == NULL, it is a console cmd
bool CallHookExploded (cWorld & a_World, double a_ExplosionSize, bool a_CanCauseFire, double a_X, double a_Y, double a_Z, eExplosionSource a_Source, void * a_SourceData);
bool CallHookExploding (cWorld & a_World, double & a_ExplosionSize, bool & a_CanCauseFire, double a_X, double a_Y, double a_Z, eExplosionSource a_Source, void * a_SourceData);
diff --git a/src/BlockID.cpp b/src/BlockID.cpp
index bfe826f40..8edc51664 100644
--- a/src/BlockID.cpp
+++ b/src/BlockID.cpp
@@ -363,6 +363,7 @@ AString DamageTypeToString(eDamageType a_DamageType)
case dtLightning: return "dtLightning";
case dtOnFire: return "dtOnFire";
case dtPoisoning: return "dtPoisoning";
+ case dtWithering: return "dtWithering";
case dtPotionOfHarming: return "dtPotionOfHarming";
case dtRangedAttack: return "dtRangedAttack";
case dtStarving: return "dtStarving";
@@ -408,6 +409,7 @@ eDamageType StringToDamageType(const AString & a_DamageTypeString)
{ dtCactusContact, "dtCactusContact"},
{ dtLavaContact, "dtLavaContact"},
{ dtPoisoning, "dtPoisoning"},
+ { dtWithering, "dtWithering"},
{ dtOnFire, "dtOnFire"},
{ dtFireContact, "dtFireContact"},
{ dtInVoid, "dtInVoid"},
@@ -433,6 +435,7 @@ eDamageType StringToDamageType(const AString & a_DamageTypeString)
{ dtCactusContact, "dtCacti"},
{ dtLavaContact, "dtLava"},
{ dtPoisoning, "dtPoison"},
+ { dtWithering, "dtWither"},
{ dtOnFire, "dtBurning"},
{ dtFireContact, "dtInFire"},
{ dtAdmin, "dtPlugin"},
diff --git a/src/BlockID.h b/src/BlockID.h
index 272fd319d..e3567b6fc 100644
--- a/src/BlockID.h
+++ b/src/BlockID.h
@@ -811,6 +811,7 @@ enum eDamageType
dtCactusContact, // Contact with a cactus block
dtLavaContact, // Contact with a lava block
dtPoisoning, // Having the poison effect
+ dtWithering, // Having the wither effect
dtOnFire, // Being on fire
dtFireContact, // Standing inside a fire block
dtInVoid, // Falling into the Void (Y < 0)
@@ -837,6 +838,7 @@ enum eDamageType
dtCacti = dtCactusContact,
dtLava = dtLavaContact,
dtPoison = dtPoisoning,
+ dtWither = dtWithering,
dtBurning = dtOnFire,
dtInFire = dtFireContact,
dtPlugin = dtAdmin,
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 5ff1c4e3c..fdc33cd82 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -43,7 +43,7 @@ set(BINDING_DEPENDECIES
Cuboid.h
Defines.h
Enchantments.h
- Entities/Effects.h
+ Entities/EntityEffect.h
Entities/Entity.h
Entities/Floater.h
Entities/Pawn.h
diff --git a/src/ClientHandle.cpp b/src/ClientHandle.cpp
index efa734b44..6ee9d9e1d 100644
--- a/src/ClientHandle.cpp
+++ b/src/ClientHandle.cpp
@@ -880,7 +880,7 @@ void cClientHandle::HandleLeftClick(int a_BlockX, int a_BlockY, int a_BlockZ, eB
case DIG_STATUS_SHOOT_EAT:
{
cItemHandler * ItemHandler = cItemHandler::GetItemHandler(m_Player->GetEquippedItem());
- if (ItemHandler->IsFood())
+ if (ItemHandler->IsFood() || ItemHandler->IsDrinkable(m_Player->GetEquippedItem().m_ItemDamage))
{
m_Player->AbortEating();
return;
@@ -1205,15 +1205,16 @@ void cClientHandle::HandleRightClick(int a_BlockX, int a_BlockY, int a_BlockZ, e
return;
}
+ short EquippedDamage = Equipped.m_ItemDamage;
cItemHandler * ItemHandler = cItemHandler::GetItemHandler(Equipped.m_ItemType);
if (ItemHandler->IsPlaceable() && (a_BlockFace != BLOCK_FACE_NONE))
{
HandlePlaceBlock(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ, *ItemHandler);
}
- else if (ItemHandler->IsFood() && !m_Player->IsGameModeCreative())
+ else if ((ItemHandler->IsFood() || ItemHandler->IsDrinkable(EquippedDamage)) && !m_Player->IsGameModeCreative())
{
- if (m_Player->IsSatiated())
+ if (m_Player->IsSatiated() && !ItemHandler->IsDrinkable(EquippedDamage))
{
// The player is satiated, they cannot eat
return;
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/Entity.cpp b/src/Entities/Entity.cpp
index 26823924f..042c4b4c3 100644
--- a/src/Entities/Entity.cpp
+++ b/src/Entities/Entity.cpp
@@ -310,7 +310,8 @@ 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.DamageType == dtAttack || a_TDI.DamageType == dtArrowAttack))
{
a_TDI.FinalDamage += 2;
m_World->BroadcastEntityAnimation(*this, 4); // Critical hit
@@ -431,6 +432,7 @@ bool cEntity::ArmorCoversAgainst(eDamageType a_DamageType)
case dtStarving:
case dtInVoid:
case dtPoisoning:
+ case dtWithering:
case dtPotionOfHarming:
case dtFalling:
case dtLightning:
diff --git a/src/Entities/Entity.h b/src/Entities/Entity.h
index f4080f8aa..867d87bb7 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); }
@@ -315,7 +316,7 @@ public:
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..e68ded8b0
--- /dev/null
+++ b/src/Entities/EntityEffect.cpp
@@ -0,0 +1,289 @@
+#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()
+{
+
+}
+
+
+
+
+
+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!");
+}
+
+
+
+
+
+void cEntityEffect::OnTick(cPawn & a_Target)
+{
+ // Reduce the effect's duration
+ ++m_Ticks;
+}
+
+
+
+
+
+void cEntityEffect::OnActivate(cPawn & a_Target)
+{
+}
+
+
+
+
+
+void cEntityEffect::OnDeactivate(cPawn & a_Target)
+{
+}
+
+
+
+
+
+/************************************************************************
+ **** Instant Health
+ ************************************************************************/
+void cEntityEffectInstantHealth::OnActivate(cPawn & a_Target)
+{
+ // Base amount = 6, doubles for every increase in intensity
+ int amount = (int)(6 * std::pow(2.0, m_Intensity) * m_DistanceModifier);
+
+ if (a_Target.IsMob())
+ {
+ if (((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);
+}
+
+
+
+
+
+/************************************************************************
+ **** Instant Damage
+ ************************************************************************/
+void cEntityEffectInstantDamage::OnActivate(cPawn & a_Target)
+{
+ // Base amount = 6, doubles for every increase in intensity
+ int amount = (int)(6 * std::pow(2.0, m_Intensity) * m_DistanceModifier);
+
+ if (a_Target.IsMob())
+ {
+ if (((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
+}
+
+
+
+
+
+/************************************************************************
+ **** Regeneration
+ ************************************************************************/
+void cEntityEffectRegeneration::OnTick(cPawn & a_Target)
+{
+ super::OnTick(a_Target);
+
+ if (a_Target.IsMob())
+ {
+ if (((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);
+}
+
+
+
+
+
+/************************************************************************
+ **** Hunger
+ ************************************************************************/
+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
+ }
+}
+
+
+
+
+
+/************************************************************************
+ **** Weakness
+ ************************************************************************/
+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
+}
+
+
+
+
+
+/************************************************************************
+ **** Poison
+ ************************************************************************/
+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);
+ }
+ }
+}
+
+
+
+
+
+/************************************************************************
+ **** Wither
+ ************************************************************************/
+void cEntityEffectWither::OnTick(cPawn & a_Target)
+{
+ super::OnTick(a_Target);
+
+ // Poison 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);
+ }
+ //TODO: "<Player> withered away>
+}
+
+
+
+
+
+/************************************************************************
+ **** Saturation
+ ************************************************************************/
+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..c593fba81
--- /dev/null
+++ b/src/Entities/EntityEffect.h
@@ -0,0 +1,430 @@
+#pragma once
+
+class cPawn;
+
+// tolua_begin
+class cEntityEffect
+{
+public:
+
+ /** All types of entity effects (numbers correspond to IDs) */
+ 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,
+ } ;
+
+ /** 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);
+
+ 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() { return m_Ticks; }
+ /** Returns the duration of the effect */
+ int GetDuration() { return m_Duration; }
+ /** Returns how strong the effect will be applied */
+ short GetIntensity() { return m_Intensity; }
+ /** Returns the distance modifier for affecting potency */
+ double GetDistanceModifier() { 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; }
+
+ virtual void OnTick(cPawn & a_Target);
+ virtual void OnActivate(cPawn & a_Target);
+ 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;
+};
+
+/************************************************************************
+ **** Speed
+ ************************************************************************/
+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)
+ {
+ }
+};
+
+/************************************************************************
+ **** Slowness
+ ************************************************************************/
+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)
+ {
+ }
+};
+
+/************************************************************************
+ **** Haste
+ ************************************************************************/
+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)
+ {
+ }
+};
+
+/************************************************************************
+ **** Mining Fatigue
+ ************************************************************************/
+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)
+ {
+ }
+};
+
+/************************************************************************
+ **** Strength
+ ************************************************************************/
+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)
+ {
+ }
+};
+
+/************************************************************************
+ **** Instant Health
+ ************************************************************************/
+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;
+};
+
+/************************************************************************
+ **** Instant Damage
+ ************************************************************************/
+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;
+};
+
+/************************************************************************
+ **** Jump Boost
+ ************************************************************************/
+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)
+ {
+ }
+};
+
+/************************************************************************
+ **** Nausea
+ ************************************************************************/
+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)
+ {
+ }
+};
+
+/************************************************************************
+ **** Regeneration
+ ************************************************************************/
+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;
+};
+
+/************************************************************************
+ **** Resistance
+ ************************************************************************/
+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)
+ {
+ }
+};
+
+/************************************************************************
+ **** Fire Resistance
+ ************************************************************************/
+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)
+ {
+ }
+};
+
+/************************************************************************
+ **** Water Breathing
+ ************************************************************************/
+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)
+ {
+ }
+};
+
+/************************************************************************
+ **** Invisibility
+ ************************************************************************/
+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)
+ {
+ }
+};
+
+/************************************************************************
+ **** Blindness
+ ************************************************************************/
+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)
+ {
+ }
+};
+
+/************************************************************************
+ **** Night Vision
+ ************************************************************************/
+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)
+ {
+ }
+};
+
+/************************************************************************
+ **** Hunger
+ ************************************************************************/
+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)
+ {
+ }
+
+ virtual void OnTick(cPawn & a_Target) override;
+};
+
+/************************************************************************
+ **** Weakness
+ ************************************************************************/
+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)
+ {
+ }
+
+ virtual void OnTick(cPawn & a_Target) override;
+};
+
+/************************************************************************
+ **** Poison
+ ************************************************************************/
+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)
+ {
+ }
+
+ virtual void OnTick(cPawn & a_Target) override;
+};
+
+/************************************************************************
+ **** Wither
+ ************************************************************************/
+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)
+ {
+ }
+
+ virtual void OnTick(cPawn & a_Target) override;
+};
+
+/************************************************************************
+ **** Health Boost
+ ************************************************************************/
+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)
+ {
+ }
+};
+
+/************************************************************************
+ **** Absorption
+ ************************************************************************/
+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)
+ {
+ }
+};
+
+/************************************************************************
+ **** Saturation
+ ************************************************************************/
+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;
+};
+
+
+
+// tolua_end
diff --git a/src/Entities/Pawn.cpp b/src/Entities/Pawn.cpp
index fffefd538..840736f6a 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(cEntity * a_Killer)
+{
+ ClearEntityEffects();
+ super::KilledBy(a_Killer);
+}
+
+
+
+
+
+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..252ec5edb 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(cEntity * a_Killer) 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 944ed643e..baa3fe7f6 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),
@@ -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);
}
@@ -1994,17 +1974,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)
{
diff --git a/src/Entities/Player.h b/src/Entities/Player.h
index 8f9b46e0f..5aebe861a 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 */
@@ -454,9 +452,6 @@ protected:
/** 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..c2d97589f 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"
@@ -260,6 +261,7 @@ 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())
diff --git a/src/Entities/SplashPotionEntity.cpp b/src/Entities/SplashPotionEntity.cpp
new file mode 100644
index 000000000..e84f1c430
--- /dev/null
+++ b/src/Entities/SplashPotionEntity.cpp
@@ -0,0 +1,89 @@
+#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
+
+#include "SplashPotionEntity.h"
+#include "Pawn.h"
+
+
+
+
+
+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_PotionName) :
+ super(pkSplashPotion, a_Creator, a_X, a_Y, a_Z, 0.25, 0.25),
+ m_EntityEffectType(a_EntityEffectType),
+ m_EntityEffect(a_EntityEffect),
+ m_PotionName(a_PotionName)
+{
+ 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, a_HitPos.x, a_HitPos.y, a_HitPos.z, m_PotionName);
+}
+
+
+
+
+
+cSplashPotionEntity::cSplashPotionCallback::cSplashPotionCallback(const Vector3d & a_HitPos, cEntityEffect::eType &a_EntityEffectType, cEntityEffect &a_EntityEffect):
+ m_HitPos(a_HitPos),
+ m_EntityEffectType(a_EntityEffectType),
+ m_EntityEffect(a_EntityEffect)
+{
+
+}
+
+
+
+
+
+bool cSplashPotionEntity::cSplashPotionCallback::Item(cEntity * a_Entity)
+{
+ double SplashDistance = (a_Entity->GetPosition() - m_HitPos).Length();
+ if (SplashDistance < 20 && a_Entity->IsPawn())
+ {
+ // 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;
+ }
+
+ m_EntityEffect.SetDistanceModifier(Reduction);
+ ((cPawn *) a_Entity)->AddEntityEffect(m_EntityEffectType, m_EntityEffect.GetDuration(), m_EntityEffect.GetIntensity(), Reduction);
+ }
+ return false;
+}
+
+
+
+
diff --git a/src/Entities/SplashPotionEntity.h b/src/Entities/SplashPotionEntity.h
new file mode 100644
index 000000000..548ba3a3e
--- /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_PotionName);
+
+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;
+
+ /** 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);
+
+ cEntityEffect::eType m_EntityEffectType;
+ cEntityEffect m_EntityEffect;
+ int m_PotionName;
+
+ class cSplashPotionCallback :
+ public cEntityCallback
+ {
+ public:
+ cSplashPotionCallback(const Vector3d & a_HitPos, cEntityEffect::eType &a_EntityEffectType, cEntityEffect &a_EntityEffect);
+
+ virtual bool Item(cEntity *a_Entity) override;
+
+ private:
+ const Vector3d &m_HitPos;
+ cEntityEffect::eType &m_EntityEffectType;
+ cEntityEffect &m_EntityEffect;
+ };
+
+} ; // tolua_export
diff --git a/src/Entities/WitherSkullEntity.cpp b/src/Entities/WitherSkullEntity.cpp
new file mode 100644
index 000000000..03e36a3f4
--- /dev/null
+++ b/src/Entities/WitherSkullEntity.cpp
@@ -0,0 +1,40 @@
+#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..85ba55d4d
--- /dev/null
+++ b/src/Entities/WitherSkullEntity.h
@@ -0,0 +1,34 @@
+//
+// WitherSkullEntity.h
+//
+
+#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
diff --git a/src/Globals.h b/src/Globals.h
index 0c11429bd..998676fb1 100644
--- a/src/Globals.h
+++ b/src/Globals.h
@@ -383,6 +383,5 @@ T Clamp(T a_Value, T a_Min, T a_Max)
#include "BiomeDef.h"
#include "BlockID.h"
#include "BlockInfo.h"
-#include "Entities/Effects.h"
diff --git a/src/Items/ItemHandler.cpp b/src/Items/ItemHandler.cpp
index 423039cf4..7fae2d395 100644
--- a/src/Items/ItemHandler.cpp
+++ b/src/Items/ItemHandler.cpp
@@ -19,6 +19,7 @@
#include "ItemCloth.h"
#include "ItemComparator.h"
#include "ItemDoor.h"
+#include "ItemMilk.h"
#include "ItemDye.h"
#include "ItemEmptyMap.h"
#include "ItemFishingRod.h"
@@ -34,6 +35,7 @@
#include "ItemNetherWart.h"
#include "ItemPainting.h"
#include "ItemPickaxe.h"
+#include "ItemPotion.h"
#include "ItemThrowable.h"
#include "ItemRedstoneDust.h"
#include "ItemRedstoneRepeater.h"
@@ -120,9 +122,11 @@ cItemHandler *cItemHandler::CreateItemHandler(int a_ItemType)
case E_ITEM_FLOWER_POT: return new cItemFlowerPotHandler(a_ItemType);
case E_BLOCK_LILY_PAD: return new cItemLilypadHandler(a_ItemType);
case E_ITEM_MAP: return new cItemMapHandler();
+ case E_ITEM_MILK: return new cItemMilkHandler();
case E_ITEM_ITEM_FRAME: return new cItemItemFrameHandler(a_ItemType);
case E_ITEM_NETHER_WART: return new cItemNetherWartHandler(a_ItemType);
case E_ITEM_PAINTING: return new cItemPaintingHandler(a_ItemType);
+ case E_ITEM_POTIONS: return new cItemPotionHandler();
case E_ITEM_REDSTONE_DUST: return new cItemRedstoneDustHandler(a_ItemType);
case E_ITEM_REDSTONE_REPEATER: return new cItemRedstoneRepeaterHandler(a_ItemType);
case E_ITEM_SHEARS: return new cItemShearsHandler(a_ItemType);
@@ -478,7 +482,6 @@ bool cItemHandler::IsFood(void)
case E_ITEM_BREAD:
case E_ITEM_RAW_PORKCHOP:
case E_ITEM_COOKED_PORKCHOP:
- case E_ITEM_MILK:
case E_ITEM_RAW_FISH:
case E_ITEM_COOKED_FISH:
case E_ITEM_COOKIE:
@@ -504,6 +507,24 @@ bool cItemHandler::IsFood(void)
+bool cItemHandler::IsDrinkable(short a_ItemDamage)
+{
+ UNUSED(a_ItemDamage);
+
+ switch (m_ItemType)
+ {
+ case E_ITEM_MILK:
+ {
+ return true;
+ }
+ } // switch (m_ItemType)
+ return false;
+}
+
+
+
+
+
bool cItemHandler::IsPlaceable(void)
{
// We can place any block that has a corresponding E_BLOCK_TYPE:
@@ -580,7 +601,7 @@ bool cItemHandler::EatItem(cPlayer * a_Player, cItem * a_Item)
cFastRandom r1;
if ((r1.NextInt(100, a_Player->GetUniqueID()) - Info.PoisonChance) <= 0)
{
- a_Player->FoodPoison(300);
+ a_Player->FoodPoison(600); // Give the player food poisoning for 30 seconds.
}
}
diff --git a/src/Items/ItemHandler.h b/src/Items/ItemHandler.h
index e13198cd7..cffca11ab 100644
--- a/src/Items/ItemHandler.h
+++ b/src/Items/ItemHandler.h
@@ -82,6 +82,9 @@ public:
/** Indicates if this item is food */
virtual bool IsFood(void);
+ /** Indicates if this item is drinkable */
+ virtual bool IsDrinkable(short a_ItemDamage);
+
/** Blocks simply get placed */
virtual bool IsPlaceable(void);
@@ -99,7 +102,7 @@ public:
BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta
);
- /** Returns whether this tool/item can harvest a specific block (e.g. wooden pickaxe can harvest stone, but wood can�t) DEFAULT: False */
+ /** Returns whether this tool/item can harvest a specific block (e.g. wooden pickaxe can harvest stone, but wood can't) DEFAULT: False */
virtual bool CanHarvestBlock(BLOCKTYPE a_BlockType);
static cItemHandler * GetItemHandler(int a_ItemType);
diff --git a/src/Items/ItemMilk.h b/src/Items/ItemMilk.h
new file mode 100644
index 000000000..db7bc13be
--- /dev/null
+++ b/src/Items/ItemMilk.h
@@ -0,0 +1,28 @@
+
+#pragma once
+
+class cItemMilkHandler:
+ public cItemHandler
+{
+ typedef cItemHandler super;
+public:
+ cItemMilkHandler():
+ super(E_ITEM_MILK)
+ {
+ }
+
+ virtual bool IsDrinkable(short a_ItemDamage) override
+ {
+ UNUSED(a_ItemDamage);
+ return true;
+ }
+
+ virtual bool EatItem(cPlayer * a_Player, cItem * a_Item) override
+ {
+ UNUSED(a_Item);
+ a_Player->ClearEntityEffects();
+ a_Player->GetInventory().RemoveOneEquippedItem();
+ a_Player->GetInventory().AddItem(E_ITEM_BUCKET);
+ return true;
+ }
+};
diff --git a/src/Items/ItemPotion.h b/src/Items/ItemPotion.h
new file mode 100644
index 000000000..5badeda94
--- /dev/null
+++ b/src/Items/ItemPotion.h
@@ -0,0 +1,154 @@
+
+#pragma once
+
+#include "../Entities/EntityEffect.h"
+#include "../Entities/SplashPotionEntity.h"
+
+class cItemPotionHandler:
+ public cItemHandler
+{
+ typedef cItemHandler super;
+
+ int GetPotionName(short a_ItemDamage)
+ {
+ return a_ItemDamage & 63;
+ }
+
+ cEntityEffect::eType GetEntityEffectType(short a_ItemDamage)
+ {
+ // Potion effect bits are different from entity effect values
+ // For reference: http://minecraft.gamepedia.com/Data_values#.22Potion_effect.22_bits
+ switch (a_ItemDamage & 15)
+ {
+ case 1: return cEntityEffect::effRegeneration;
+ case 2: return cEntityEffect::effSpeed;
+ case 3: return cEntityEffect::effFireResistance;
+ case 4: return cEntityEffect::effPoison;
+ case 5: return cEntityEffect::effInstantHealth;
+ case 6: return cEntityEffect::effNightVision;
+ case 8: return cEntityEffect::effWeakness;
+ case 9: return cEntityEffect::effStrength;
+ case 10: return cEntityEffect::effSlowness;
+ case 12: return cEntityEffect::effInstantDamage;
+ case 13: return cEntityEffect::effWaterBreathing;
+ case 14: return cEntityEffect::effInvisibility;
+
+ // No effect potions
+ case 0:
+ case 7:
+ case 11:
+ case 15:
+ {
+ break;
+ }
+ }
+
+ return cEntityEffect::effNoEffect;
+ }
+
+ short GetEntityEffectIntensity(short a_ItemDamage)
+ {
+ // Level II potion if fifth bit is set
+ if (a_ItemDamage & 32) return 1;
+ else return 0;
+ }
+
+ int GetEntityEffectDuration(short a_ItemDamage)
+ {
+ // Base duration in ticks
+ int base = 0;
+ double TierCoeff = 1, ExtCoeff = 1, SplashCoeff = 1;
+
+ switch (GetEntityEffectType(a_ItemDamage))
+ {
+ case cEntityEffect::effRegeneration:
+ case cEntityEffect::effPoison:
+ {
+ base = 900;
+ break;
+ }
+
+ case cEntityEffect::effSpeed:
+ case cEntityEffect::effFireResistance:
+ case cEntityEffect::effNightVision:
+ case cEntityEffect::effStrength:
+ case cEntityEffect::effWaterBreathing:
+ case cEntityEffect::effInvisibility:
+ {
+ base = 3600;
+ break;
+ }
+
+ case cEntityEffect::effWeakness:
+ case cEntityEffect::effSlowness:
+ {
+ base = 1800;
+ break;
+ }
+ }
+
+ // If potion is level 2, half the duration. If not, stays the same
+ TierCoeff = (GetEntityEffectIntensity(a_ItemDamage) > 0) ? 0.5 : 1;
+
+ // If potion is extended, multiply duration by 8/3. If not, stays the same
+ // Extended potion if sixth bit is set
+ ExtCoeff = (a_ItemDamage & 64) ? (8.0/3.0) : 1;
+
+ // If potion is splash potion, multiply duration by 3/4. If not, stays the same
+ SplashCoeff = IsDrinkable(a_ItemDamage) ? 1 : 0.75;
+
+ // For reference: http://minecraft.gamepedia.com/Data_values#.22Tier.22_bit
+ // http://minecraft.gamepedia.com/Data_values#.22Extended_duration.22_bit
+ // http://minecraft.gamepedia.com/Data_values#.22Splash_potion.22_bit
+
+ return (int)(base * TierCoeff * ExtCoeff * SplashCoeff);
+ }
+
+public:
+ cItemPotionHandler():
+ super(E_ITEM_POTIONS)
+ {
+ }
+
+ virtual bool IsDrinkable(short a_ItemDamage) override
+ {
+ // Drinkable potion if 13th bit is set
+ // For reference: http://minecraft.gamepedia.com/Potions#Data_value_table
+ return ((a_ItemDamage & 8192) != 0);
+ }
+
+ virtual bool OnItemUse(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_Dir) override
+ {
+ Vector3d Pos = a_Player->GetThrowStartPos();
+ Vector3d Speed = a_Player->GetLookVector() * 7;
+
+ short potion_damage = a_Item.m_ItemDamage;
+ cSplashPotionEntity *Projectile = new cSplashPotionEntity(a_Player, Pos.x, Pos.y, Pos.z, &Speed, GetEntityEffectType(potion_damage), cEntityEffect(GetEntityEffectDuration(potion_damage), GetEntityEffectIntensity(potion_damage)), GetPotionName(potion_damage));
+ if (Projectile == NULL)
+ {
+ return false;
+ }
+ if (!Projectile->Initialize(*a_World))
+ {
+ delete Projectile;
+ return false;
+ }
+
+ if (!a_Player->IsGameModeCreative())
+ {
+ a_Player->GetInventory().RemoveOneEquippedItem();
+ }
+
+ // Called when potion is a splash potion
+ return true;
+ }
+
+ virtual bool EatItem(cPlayer * a_Player, cItem * a_Item) override
+ {
+ // Called when potion is a drinkable potion
+ a_Player->AddEntityEffect(GetEntityEffectType(a_Item->m_ItemDamage), GetEntityEffectDuration(a_Item->m_ItemDamage), GetEntityEffectIntensity(a_Item->m_ItemDamage));
+ a_Player->GetInventory().RemoveOneEquippedItem();
+ a_Player->GetInventory().AddItem(E_ITEM_GLASS_BOTTLE);
+ return true;
+ }
+};
diff --git a/src/Mobs/AggressiveMonster.cpp b/src/Mobs/AggressiveMonster.cpp
index 85b122034..de881f4eb 100644
--- a/src/Mobs/AggressiveMonster.cpp
+++ b/src/Mobs/AggressiveMonster.cpp
@@ -95,12 +95,14 @@ void cAggressiveMonster::Attack(float a_Dt)
{
m_AttackInterval += a_Dt * m_AttackRate;
- if ((m_Target != NULL) && (m_AttackInterval > 3.0))
+ if ((m_Target == NULL) || (m_AttackInterval < 3.0))
{
- // Setting this higher gives us more wiggle room for attackrate
- m_AttackInterval = 0.0;
- m_Target->TakeDamage(dtMobAttack, this, m_AttackDamage, 0);
+ return;
}
+
+ // Setting this higher gives us more wiggle room for attackrate
+ m_AttackInterval = 0.0;
+ m_Target->TakeDamage(dtMobAttack, this, m_AttackDamage, 0);
}
diff --git a/src/Mobs/CaveSpider.cpp b/src/Mobs/CaveSpider.cpp
index 1157b81f9..118a6e93b 100644
--- a/src/Mobs/CaveSpider.cpp
+++ b/src/Mobs/CaveSpider.cpp
@@ -27,6 +27,21 @@ void cCaveSpider::Tick(float a_Dt, cChunk & a_Chunk)
+void cCaveSpider::Attack(float a_Dt)
+{
+ super::Attack(a_Dt);
+
+ if (m_Target->IsPawn())
+ {
+ // TODO: Easy = no poison, Medium = 7 seconds, Hard = 15 seconds
+ ((cPawn *) m_Target)->AddEntityEffect(cEntityEffect::effPoison, 7 * 20, 0);
+ }
+}
+
+
+
+
+
void cCaveSpider::GetDrops(cItems & a_Drops, cEntity * a_Killer)
{
int LootingLevel = 0;
diff --git a/src/Mobs/CaveSpider.h b/src/Mobs/CaveSpider.h
index be9f174f9..3f8b2cece 100644
--- a/src/Mobs/CaveSpider.h
+++ b/src/Mobs/CaveSpider.h
@@ -17,6 +17,7 @@ public:
CLASS_PROTODEF(cCaveSpider);
virtual void Tick(float a_Dt, cChunk & a_Chunk) override;
+ virtual void Attack(float a_Dt) override;
virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override;
} ;
diff --git a/src/Mobs/Monster.cpp b/src/Mobs/Monster.cpp
index aaef7580d..f4827d5f5 100644
--- a/src/Mobs/Monster.cpp
+++ b/src/Mobs/Monster.cpp
@@ -435,6 +435,7 @@ void cMonster::HandleFalling()
+
int cMonster::FindFirstNonAirBlockPosition(double a_PosX, double a_PosZ)
{
int PosY = POSY_TOINT;
@@ -706,6 +707,25 @@ void cMonster::GetMonsterConfig(const AString & a_Name)
+bool cMonster::IsUndead(void)
+{
+ switch (GetMobType())
+ {
+ case mtZombie:
+ case mtZombiePigman:
+ case mtSkeleton:
+ case mtWither:
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+
+
+
AString cMonster::MobTypeToString(cMonster::eType a_MobType)
{
// Mob types aren't sorted, so we need to search linearly:
diff --git a/src/Mobs/Monster.h b/src/Mobs/Monster.h
index 7d7e90eb2..638d5be39 100644
--- a/src/Mobs/Monster.h
+++ b/src/Mobs/Monster.h
@@ -107,6 +107,9 @@ public:
/// Reads the monster configuration for the specified monster name and assigns it to this object.
void GetMonsterConfig(const AString & a_Name);
+ /** Returns whether this mob is undead (skeleton, zombie, etc.) */
+ bool IsUndead(void);
+
virtual void EventLosePlayer(void);
virtual void CheckEventLostPlayer(void);
@@ -178,6 +181,7 @@ protected:
/** Stores if mobile is currently moving towards the ultimate, final destination */
bool m_bMovingToDestination;
+
/** Finds the first non-air block position (not the highest, as cWorld::GetHeight does)
If current Y is nonsolid, goes down to try to find a solid block, then returns that + 1
If current Y is solid, goes up to find first nonsolid block, and returns that */