diff options
Diffstat (limited to 'src')
34 files changed, 1398 insertions, 89 deletions
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..dabe8debb 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, cEntity * a_Originator, 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..e10cca708 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, cEntity * a_Originator, 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_Originator, 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..94371c830 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, cEntity * a_Originator, 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..c80344c30 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, cEntity * a_Originator, 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_Originator, 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..4d5a350d4 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, cEntity * a_Originator, 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..9881785cb --- /dev/null +++ b/src/Entities/EntityEffect.cpp @@ -0,0 +1,291 @@ +#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_Creator(NULL), + m_DistanceModifier(1) +{ + +} + + + + + +cEntityEffect::cEntityEffect(int a_Duration, short a_Intensity, cPawn *a_Creator, double a_DistanceModifier): + m_Ticks(0), + m_Duration(a_Duration), + m_Intensity(a_Intensity), + m_Creator(a_Creator), + m_DistanceModifier(a_DistanceModifier) +{ + +} + + + + + +cEntityEffect::~cEntityEffect() +{ + +} + + + + + +cEntityEffect * cEntityEffect::CreateEntityEffect(cEntityEffect::eType a_EffectType, int a_Duration, short a_Intensity, cPawn * a_Creator, double a_DistanceModifier) +{ + switch (a_EffectType) + { + case cEntityEffect::effNoEffect: return new cEntityEffect (a_Duration, a_Intensity, a_Creator, a_DistanceModifier); + + case cEntityEffect::effAbsorption: return new cEntityEffectAbsorption (a_Duration, a_Intensity, a_Creator, a_DistanceModifier); + case cEntityEffect::effBlindness: return new cEntityEffectBlindness (a_Duration, a_Intensity, a_Creator, a_DistanceModifier); + case cEntityEffect::effFireResistance: return new cEntityEffectFireResistance(a_Duration, a_Intensity, a_Creator, a_DistanceModifier); + case cEntityEffect::effHaste: return new cEntityEffectHaste (a_Duration, a_Intensity, a_Creator, a_DistanceModifier); + case cEntityEffect::effHealthBoost: return new cEntityEffectHealthBoost (a_Duration, a_Intensity, a_Creator, a_DistanceModifier); + case cEntityEffect::effHunger: return new cEntityEffectHunger (a_Duration, a_Intensity, a_Creator, a_DistanceModifier); + case cEntityEffect::effInstantDamage: return new cEntityEffectInstantDamage (a_Duration, a_Intensity, a_Creator, a_DistanceModifier); + case cEntityEffect::effInstantHealth: return new cEntityEffectInstantHealth (a_Duration, a_Intensity, a_Creator, a_DistanceModifier); + case cEntityEffect::effInvisibility: return new cEntityEffectInvisibility (a_Duration, a_Intensity, a_Creator, a_DistanceModifier); + case cEntityEffect::effJumpBoost: return new cEntityEffectJumpBoost (a_Duration, a_Intensity, a_Creator, a_DistanceModifier); + case cEntityEffect::effMiningFatigue: return new cEntityEffectMiningFatigue (a_Duration, a_Intensity, a_Creator, a_DistanceModifier); + case cEntityEffect::effNausea: return new cEntityEffectNausea (a_Duration, a_Intensity, a_Creator, a_DistanceModifier); + case cEntityEffect::effNightVision: return new cEntityEffectNightVision (a_Duration, a_Intensity, a_Creator, a_DistanceModifier); + case cEntityEffect::effPoison: return new cEntityEffectPoison (a_Duration, a_Intensity, a_Creator, a_DistanceModifier); + case cEntityEffect::effRegeneration: return new cEntityEffectRegeneration (a_Duration, a_Intensity, a_Creator, a_DistanceModifier); + case cEntityEffect::effResistance: return new cEntityEffectResistance (a_Duration, a_Intensity, a_Creator, a_DistanceModifier); + case cEntityEffect::effSaturation: return new cEntityEffectSaturation (a_Duration, a_Intensity, a_Creator, a_DistanceModifier); + case cEntityEffect::effSlowness: return new cEntityEffectSlowness (a_Duration, a_Intensity, a_Creator, a_DistanceModifier); + case cEntityEffect::effSpeed: return new cEntityEffectSpeed (a_Duration, a_Intensity, a_Creator, a_DistanceModifier); + case cEntityEffect::effStrength: return new cEntityEffectStrength (a_Duration, a_Intensity, a_Creator, a_DistanceModifier); + case cEntityEffect::effWaterBreathing: return new cEntityEffectWaterBreathing(a_Duration, a_Intensity, a_Creator, a_DistanceModifier); + case cEntityEffect::effWeakness: return new cEntityEffectWeakness (a_Duration, a_Intensity, a_Creator, a_DistanceModifier); + case cEntityEffect::effWither: return new cEntityEffectWither (a_Duration, a_Intensity, a_Creator, 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, m_Creator, amount, 0); + 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, m_Creator, amount, 0); +} + + + + + +/************************************************************************ + **** 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, m_Creator, 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, m_Creator, 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..ae7958e11 --- /dev/null +++ b/src/Entities/EntityEffect.h @@ -0,0 +1,438 @@ +#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_Creator The pawn that produced this entity effect + @param a_DistanceModifier The distance modifier for affecting potency, defaults to 1 */ + cEntityEffect(int a_Duration, short a_Intensity, cPawn * a_Creator, 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_Creator The pawn that produced this entity effect + @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, cPawn * a_Creator, 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 pawn that produced this entity effect */ + cPawn *GetCreator() { return m_Creator; } + /** 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 SetCreator(cPawn * a_Creator) { m_Creator = a_Creator; } + 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 pawn that produced this entity effect (threw the potion, etc) */ + cPawn *m_Creator; + + /** 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, cPawn * a_Creator, double a_DistanceModifier = 1): + super(a_Duration, a_Intensity, a_Creator, a_DistanceModifier) + { + } +}; + +/************************************************************************ + **** Slowness + ************************************************************************/ +class cEntityEffectSlowness: + public cEntityEffect +{ + typedef cEntityEffect super; +public: + cEntityEffectSlowness(int a_Duration, short a_Intensity, cPawn * a_Creator, double a_DistanceModifier = 1): + super(a_Duration, a_Intensity, a_Creator, a_DistanceModifier) + { + } +}; + +/************************************************************************ + **** Haste + ************************************************************************/ +class cEntityEffectHaste: + public cEntityEffect +{ + typedef cEntityEffect super; +public: + cEntityEffectHaste(int a_Duration, short a_Intensity, cPawn * a_Creator, double a_DistanceModifier = 1): + super(a_Duration, a_Intensity, a_Creator, a_DistanceModifier) + { + } +}; + +/************************************************************************ + **** Mining Fatigue + ************************************************************************/ +class cEntityEffectMiningFatigue: + public cEntityEffect +{ + typedef cEntityEffect super; +public: + cEntityEffectMiningFatigue(int a_Duration, short a_Intensity, cPawn * a_Creator, double a_DistanceModifier = 1): + super(a_Duration, a_Intensity, a_Creator, a_DistanceModifier) + { + } +}; + +/************************************************************************ + **** Strength + ************************************************************************/ +class cEntityEffectStrength: + public cEntityEffect +{ + typedef cEntityEffect super; +public: + cEntityEffectStrength(int a_Duration, short a_Intensity, cPawn * a_Creator, double a_DistanceModifier = 1): + super(a_Duration, a_Intensity, a_Creator, a_DistanceModifier) + { + } +}; + +/************************************************************************ + **** Instant Health + ************************************************************************/ +class cEntityEffectInstantHealth: + public cEntityEffect +{ + typedef cEntityEffect super; +public: + cEntityEffectInstantHealth(int a_Duration, short a_Intensity, cPawn * a_Creator, double a_DistanceModifier = 1): + super(a_Duration, a_Intensity, a_Creator, 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, cPawn * a_Creator, double a_DistanceModifier = 1): + super(a_Duration, a_Intensity, a_Creator, 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, cPawn * a_Creator, double a_DistanceModifier = 1): + super(a_Duration, a_Intensity, a_Creator, a_DistanceModifier) + { + } +}; + +/************************************************************************ + **** Nausea + ************************************************************************/ +class cEntityEffectNausea: + public cEntityEffect +{ + typedef cEntityEffect super; +public: + cEntityEffectNausea(int a_Duration, short a_Intensity, cPawn * a_Creator, double a_DistanceModifier = 1): + super(a_Duration, a_Intensity, a_Creator, a_DistanceModifier) + { + } +}; + +/************************************************************************ + **** Regeneration + ************************************************************************/ +class cEntityEffectRegeneration: + public cEntityEffect +{ + typedef cEntityEffect super; +public: + cEntityEffectRegeneration(int a_Duration, short a_Intensity, cPawn * a_Creator, double a_DistanceModifier = 1): + super(a_Duration, a_Intensity, a_Creator, 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, cPawn * a_Creator, double a_DistanceModifier = 1): + super(a_Duration, a_Intensity, a_Creator, a_DistanceModifier) + { + } +}; + +/************************************************************************ + **** Fire Resistance + ************************************************************************/ +class cEntityEffectFireResistance: + public cEntityEffect +{ + typedef cEntityEffect super; +public: + cEntityEffectFireResistance(int a_Duration, short a_Intensity, cPawn * a_Creator, double a_DistanceModifier = 1): + super(a_Duration, a_Intensity, a_Creator, a_DistanceModifier) + { + } +}; + +/************************************************************************ + **** Water Breathing + ************************************************************************/ +class cEntityEffectWaterBreathing: + public cEntityEffect +{ + typedef cEntityEffect super; +public: + cEntityEffectWaterBreathing(int a_Duration, short a_Intensity, cPawn * a_Creator, double a_DistanceModifier = 1): + super(a_Duration, a_Intensity, a_Creator, a_DistanceModifier) + { + } +}; + +/************************************************************************ + **** Invisibility + ************************************************************************/ +class cEntityEffectInvisibility: + public cEntityEffect +{ + typedef cEntityEffect super; +public: + cEntityEffectInvisibility(int a_Duration, short a_Intensity, cPawn * a_Creator, double a_DistanceModifier = 1): + super(a_Duration, a_Intensity, a_Creator, a_DistanceModifier) + { + } +}; + +/************************************************************************ + **** Blindness + ************************************************************************/ +class cEntityEffectBlindness: + public cEntityEffect +{ + typedef cEntityEffect super; +public: + cEntityEffectBlindness(int a_Duration, short a_Intensity, cPawn * a_Creator, double a_DistanceModifier = 1): + super(a_Duration, a_Intensity, a_Creator, a_DistanceModifier) + { + } +}; + +/************************************************************************ + **** Night Vision + ************************************************************************/ +class cEntityEffectNightVision: + public cEntityEffect +{ + typedef cEntityEffect super; +public: + cEntityEffectNightVision(int a_Duration, short a_Intensity, cPawn * a_Creator, double a_DistanceModifier = 1): + super(a_Duration, a_Intensity, a_Creator, a_DistanceModifier) + { + } +}; + +/************************************************************************ + **** Hunger + ************************************************************************/ +class cEntityEffectHunger: + public cEntityEffect +{ + typedef cEntityEffect super; +public: + cEntityEffectHunger(int a_Duration, short a_Intensity, cPawn * a_Creator, double a_DistanceModifier = 1): + super(a_Duration, a_Intensity, a_Creator, 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, cPawn * a_Creator, double a_DistanceModifier = 1): + super(a_Duration, a_Intensity, a_Creator, 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, cPawn * a_Creator, double a_DistanceModifier = 1): + super(a_Duration, a_Intensity, a_Creator, 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, cPawn * a_Creator, double a_DistanceModifier = 1): + super(a_Duration, a_Intensity, a_Creator, 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, cPawn * a_Creator, double a_DistanceModifier = 1): + super(a_Duration, a_Intensity, a_Creator, a_DistanceModifier) + { + } +}; + +/************************************************************************ + **** Absorption + ************************************************************************/ +class cEntityEffectAbsorption: + public cEntityEffect +{ + typedef cEntityEffect super; +public: + cEntityEffectAbsorption(int a_Duration, short a_Intensity, cPawn * a_Creator, double a_DistanceModifier = 1): + super(a_Duration, a_Intensity, a_Creator, a_DistanceModifier) + { + } +}; + +/************************************************************************ + **** Saturation + ************************************************************************/ +class cEntityEffectSaturation: + public cEntityEffect +{ + typedef cEntityEffect super; +public: + cEntityEffectSaturation(int a_Duration, short a_Intensity, cPawn * a_Creator, double a_DistanceModifier = 1): + super(a_Duration, a_Intensity, a_Creator, 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..62f71e20f 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,94 @@ 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(); +} + + + + + +void cPawn::AddEntityEffect(cEntityEffect::eType a_EffectType, int a_Duration, short a_Intensity, cPawn * a_Creator, double a_DistanceModifier) +{ + // Check if the plugins allow the addition: + if (cPluginManager::Get()->CallHookEntityAddEffect(*this, a_EffectType, a_Duration, a_Intensity, a_Creator, 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_Creator, 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..307e5db3d 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,35 @@ 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_Creator The pawn that produced the effect (e.g. threw the potion) + @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, cPawn * a_Creator, 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 dbb8cd26c..cc07c25a8 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) @@ -567,18 +566,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; } @@ -594,17 +584,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); } @@ -1940,17 +1920,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 f247ac2f9..2053305ea 100644 --- a/src/Entities/Player.h +++ b/src/Entities/Player.h @@ -264,13 +264,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); } @@ -279,7 +278,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); @@ -290,7 +288,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 */ @@ -445,9 +443,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..3d2ef279f --- /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(), m_EntityEffect.GetCreator(), 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..43b9f280d --- /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), a_Player), 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); + 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..34135714d 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, this); + } +} + + + + + 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 */ |