1
0

Merge pull request #1079 from mc-server/potions

Added potions and entity effect accessors
This commit is contained in:
Mattes D 2014-07-17 11:23:09 +02:00
commit ec77cf1b06
40 changed files with 1596 additions and 119 deletions

View File

@ -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

View File

@ -0,0 +1,33 @@
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 = "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
}

View File

@ -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"

View File

@ -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;

View File

@ -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";

View File

@ -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;

View File

@ -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);

View File

@ -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);

View File

@ -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"},

View File

@ -319,7 +319,8 @@ enum ENUM_ITEM_ID
E_ITEM_GHAST_TEAR = 370,
E_ITEM_GOLD_NUGGET = 371,
E_ITEM_NETHER_WART = 372,
E_ITEM_POTIONS = 373,
E_ITEM_POTION = 373,
E_ITEM_POTIONS = 373, // OBSOLETE, use E_ITEM_POTION instead
E_ITEM_GLASS_BOTTLE = 374,
E_ITEM_SPIDER_EYE = 375,
E_ITEM_FERMENTED_SPIDER_EYE = 376,
@ -811,6 +812,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 +839,7 @@ enum eDamageType
dtCacti = dtCactusContact,
dtLava = dtLavaContact,
dtPoison = dtPoisoning,
dtWither = dtWithering,
dtBurning = dtOnFire,
dtInFire = dtFireContact,
dtPlugin = dtAdmin,

View File

@ -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

View File

@ -877,7 +877,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;
@ -1202,15 +1202,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;

View File

@ -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

View File

@ -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));
@ -431,6 +435,7 @@ bool cEntity::ArmorCoversAgainst(eDamageType a_DamageType)
case dtStarving:
case dtInVoid:
case dtPoisoning:
case dtWithering:
case dtPotionOfHarming:
case dtFalling:
case dtLightning:

View File

@ -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; }

View File

@ -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
}
}

476
src/Entities/EntityEffect.h Normal file
View File

@ -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;
};

View File

@ -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);
}
}

View File

@ -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

View File

@ -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);
}
@ -2015,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)
{

View File

@ -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;

View File

@ -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:
{
ASSERT(a_Item != NULL);
@ -311,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?

View File

@ -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);
}

View File

@ -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

View File

@ -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);
}

View File

@ -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

View File

@ -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"

View File

@ -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);
@ -470,33 +474,17 @@ bool cItemHandler::IsTool()
bool cItemHandler::IsFood(void)
{
switch (m_ItemType)
{
case E_ITEM_RED_APPLE:
case E_ITEM_GOLDEN_APPLE:
case E_ITEM_MUSHROOM_SOUP:
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:
case E_ITEM_MELON_SLICE:
case E_ITEM_RAW_BEEF:
case E_ITEM_STEAK:
case E_ITEM_RAW_CHICKEN:
case E_ITEM_COOKED_CHICKEN:
case E_ITEM_ROTTEN_FLESH:
case E_ITEM_SPIDER_EYE:
case E_ITEM_CARROT:
case E_ITEM_POTATO:
case E_ITEM_BAKED_POTATO:
case E_ITEM_POISONOUS_POTATO:
{
return true;
}
} // switch (m_ItemType)
return false;
}
bool cItemHandler::IsDrinkable(short a_ItemDamage)
{
UNUSED(a_ItemDamage);
return false;
}
@ -580,7 +568,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.
}
}

View File

@ -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<EFBFBD>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);

28
src/Items/ItemMilk.h Normal file
View File

@ -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;
}
};

199
src/Items/ItemPotion.h Normal file
View File

@ -0,0 +1,199 @@
#pragma once
#include "../Entities/EntityEffect.h"
#include "../Entities/SplashPotionEntity.h"
class cItemPotionHandler:
public cItemHandler
{
typedef cItemHandler super;
public:
cItemPotionHandler():
super(E_ITEM_POTION)
{
}
/** Returns the potion particle type (used by the client for visuals), based on the potion's damage value */
static int GetPotionParticleType(short a_ItemDamage)
{
// Lowest six bits
return (a_ItemDamage & 0x3f);
}
/** Translates the potion's damage value into the entity effect that the potion gives */
static cEntityEffect::eType GetEntityEffectType(short a_ItemDamage)
{
// Lowest four bits
// Potion effect bits are different from entity effect values
// For reference: http://minecraft.gamepedia.com/Data_values#.22Potion_effect.22_bits
switch (a_ItemDamage & 0x0f)
{
case 0x01: return cEntityEffect::effRegeneration;
case 0x02: return cEntityEffect::effSpeed;
case 0x03: return cEntityEffect::effFireResistance;
case 0x04: return cEntityEffect::effPoison;
case 0x05: return cEntityEffect::effInstantHealth;
case 0x06: return cEntityEffect::effNightVision;
case 0x08: return cEntityEffect::effWeakness;
case 0x09: return cEntityEffect::effStrength;
case 0x0a: return cEntityEffect::effSlowness;
case 0x0c: return cEntityEffect::effInstantDamage;
case 0x0d: return cEntityEffect::effWaterBreathing;
case 0x0e: return cEntityEffect::effInvisibility;
// No effect potions
case 0x00:
case 0x07:
case 0x0b: // Will be potion of leaping in 1.8
case 0x0f:
{
break;
}
}
return cEntityEffect::effNoEffect;
}
/** Retrieves the intensity level from the potion's damage value.
Returns 0 for level I potions, 1 for level II potions. */
static short GetEntityEffectIntensity(short a_ItemDamage)
{
// Level II potion if the fifth lowest bit is set
return ((a_ItemDamage & 0x20) != 0) ? 1 : 0;
}
/** Returns the effect duration, in ticks, based on the potion's damage value */
static 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 II, 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 lowest bit is set
ExtCoeff = (a_ItemDamage & 0x40) ? (8.0 / 3.0) : 1;
// If potion is splash potion, multiply duration by 3/4. If not, stays the same
SplashCoeff = IsPotionDrinkable(a_ItemDamage) ? 1 : 0.75;
// Ref.:
// 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);
}
/** Returns true if the potion with the given damage is drinkable */
static bool IsPotionDrinkable(short a_ItemDamage)
{
// Drinkable potion if 13th lowest bit is set
// Ref.: http://minecraft.gamepedia.com/Potions#Data_value_table
return ((a_ItemDamage & 0x2000) != 0);
}
// cItemHandler overrides:
virtual bool IsDrinkable(short a_ItemDamage) override
{
// Drinkable potion if 13th lowest bit is set
// Ref.: http://minecraft.gamepedia.com/Potions#Data_value_table
return IsPotionDrinkable(a_ItemDamage);
}
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
{
short PotionDamage = a_Item.m_ItemDamage;
// Do not throw non-splash potions:
if (IsPotionDrinkable(PotionDamage))
{
return false;
}
Vector3d Pos = a_Player->GetThrowStartPos();
Vector3d Speed = a_Player->GetLookVector() * 7;
cSplashPotionEntity * Projectile = new cSplashPotionEntity(
a_Player, Pos.x, Pos.y, Pos.z, Speed,
GetEntityEffectType(PotionDamage), cEntityEffect(GetEntityEffectDuration(PotionDamage),
GetEntityEffectIntensity(PotionDamage)), GetPotionParticleType(PotionDamage)
);
if (Projectile == NULL)
{
return false;
}
if (!Projectile->Initialize(*a_World))
{
delete Projectile;
return false;
}
if (!a_Player->IsGameModeCreative())
{
a_Player->GetInventory().RemoveOneEquippedItem();
}
return true;
}
virtual bool EatItem(cPlayer * a_Player, cItem * a_Item) override
{
short PotionDamage = a_Item->m_ItemDamage;
// Do not drink undrinkable potions:
if (!IsDrinkable(a_Item->m_ItemDamage))
{
return false;
}
a_Player->AddEntityEffect(GetEntityEffectType(PotionDamage), GetEntityEffectDuration(PotionDamage), GetEntityEffectIntensity(PotionDamage));
a_Player->GetInventory().RemoveOneEquippedItem();
a_Player->GetInventory().AddItem(E_ITEM_GLASS_BOTTLE);
return true;
}
};

View File

@ -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);
}

View File

@ -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;

View File

@ -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;
} ;

View File

@ -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:

View File

@ -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 */

View File

@ -29,6 +29,7 @@
#include "../Entities/Minecart.h"
#include "../Entities/Pickup.h"
#include "../Entities/ArrowEntity.h"
#include "../Entities/SplashPotionEntity.h"
#include "../Entities/TNTEntity.h"
#include "../Entities/ExpOrb.h"
#include "../Entities/HangingEntity.h"
@ -604,6 +605,16 @@ void cNBTChunkSerializer::AddProjectileEntity(cProjectileEntity * a_Projectile)
m_Writer.AddDouble("damage", Arrow->GetDamageCoeff());
break;
}
case cProjectileEntity::pkSplashPotion:
{
cSplashPotionEntity * Potion = (cSplashPotionEntity *)a_Projectile;
m_Writer.AddInt("EffectType", (Int16)Potion->GetEntityEffectType());
m_Writer.AddInt("EffectDuration", (Int16)Potion->GetEntityEffect().GetDuration());
m_Writer.AddShort("EffectIntensity", Potion->GetEntityEffect().GetIntensity());
m_Writer.AddDouble("EffectDistanceModifier", Potion->GetEntityEffect().GetDistanceModifier());
m_Writer.AddInt("PotionName", Potion->GetPotionParticleType());
}
case cProjectileEntity::pkGhastFireball:
{
m_Writer.AddInt("ExplosionPower", 1);

View File

@ -46,6 +46,7 @@ class cTNTEntity;
class cExpOrb;
class cHangingEntity;
class cItemFrame;
class cEntityEffect;

View File

@ -36,6 +36,7 @@
#include "../Entities/Minecart.h"
#include "../Entities/Pickup.h"
#include "../Entities/ArrowEntity.h"
#include "../Entities/SplashPotionEntity.h"
#include "../Entities/ThrownEggEntity.h"
#include "../Entities/ThrownEnderPearlEntity.h"
#include "../Entities/ThrownSnowballEntity.h"
@ -1156,6 +1157,10 @@ void cWSSAnvil::LoadEntityFromNBT(cEntityList & a_Entities, const cParsedNBT & a
{
LoadArrowFromNBT(a_Entities, a_NBT, a_EntityTagIdx);
}
else if (strncmp(a_IDTag, "SplashPotion", a_IDTagLength) == 0)
{
LoadSplashPotionFromNBT(a_Entities, a_NBT, a_EntityTagIdx);
}
else if (strncmp(a_IDTag, "Snowball", a_IDTagLength) == 0)
{
LoadSnowballFromNBT(a_Entities, a_NBT, a_EntityTagIdx);
@ -1662,6 +1667,29 @@ void cWSSAnvil::LoadArrowFromNBT(cEntityList & a_Entities, const cParsedNBT & a_
void cWSSAnvil::LoadSplashPotionFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx)
{
std::auto_ptr<cSplashPotionEntity> SplashPotion(new cSplashPotionEntity(NULL, 0, 0, 0, Vector3d(0, 0, 0), cEntityEffect::effNoEffect, cEntityEffect(), 0));
if (!LoadProjectileBaseFromNBT(*SplashPotion.get(), a_NBT, a_TagIdx))
{
return;
}
int EffectDuration = a_NBT.FindChildByName(a_TagIdx, "EffectDuration");
int EffectIntensity = a_NBT.FindChildByName(a_TagIdx, "EffectIntensity");
int EffectDistanceModifier = a_NBT.FindChildByName(a_TagIdx, "EffectDistanceModifier");
SplashPotion->SetEntityEffectType((cEntityEffect::eType) a_NBT.FindChildByName(a_TagIdx, "EffectType"));
SplashPotion->SetEntityEffect(cEntityEffect(EffectDuration, EffectIntensity, EffectDistanceModifier));
SplashPotion->SetPotionParticleType(a_NBT.FindChildByName(a_TagIdx, "PotionName"));
// Store the new splash potion in the entities list:
a_Entities.push_back(SplashPotion.release());
}
void cWSSAnvil::LoadSnowballFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx)
{

View File

@ -163,6 +163,7 @@ protected:
void LoadMinecartHFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx);
void LoadArrowFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx);
void LoadSplashPotionFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx);
void LoadSnowballFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx);
void LoadEggFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx);
void LoadFireballFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx);