diff --git a/changelog.txt b/changelog.txt index 6a6382e5f..bb6ffbb9c 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,5 +1,13 @@ EQEMu Changelog (Started on Sept 24, 2003 15:50) ------------------------------------------------------- + +== 12/13/2013 == +Kayen: Implemented additional functionality for SE_CurrentHP utilizing base2 values. (ie limit to body type) +Kayen: Implemented SE_MitigateMeleeDamageSP (Partial Melee Rune that only is lowered if melee hits are over X amount of damage) +Kayen: Implemented SE_SpellOnAmtDmgTaken (Effect is triggered when X amount of damage is taken) +Kayen: Fix for various spell triggers/procs to now properly use their resist modifier. +Kayen: Fix to mob->ModSkillDmgTaken(skill_num, value), setting value to -1 will now properly effect all skills. + == 12/04/2013 == demonstar55: Fixed SpellType_Charm case in AICastSpell diff --git a/common/spdat.h b/common/spdat.h index 98917e347..a19d4e594 100644 --- a/common/spdat.h +++ b/common/spdat.h @@ -582,7 +582,7 @@ typedef enum { #define SE_CriticalHealOverTime2 435 // implemented - increase critical heal over time chance //#define SE_Unknown432 436 // not used #define SE_Anchor 437 // *not implemented - Teleport Guild Hall Anchor(33099) -//#define SE_Unknown438 438 // not used +#define SE_Anchor2 438 // *not implemented - Translocate Primary Anchor (27750) #define SE_IncreaseAssassinationLvl 439 // *not implemented[AA] - increases the maximum level of humanoid that can be affected by assassination #define SE_FinishingBlowLvl 440 // implemented[AA] - Sets the level Finishing blow can be triggered on an NPC #define SE_MovementSpeed2 441 // *not implemented - new snare effect @@ -590,6 +590,15 @@ typedef enum { //#define SE_Unknown443 443 // *not implemented - related to Finishing Blow AA #define SE_AggroLock 444 // *not implemented - target will ignore all but caster for duration #define SE_AdditionalMercenary 445 // *not implemented[AA] - [Hero's Barracks] Allows you to conscript additional mercs. +//#define SE_Unknown446 446 // *not implementet - bufff stacking blocker ? Blezon's Persistence +//#define SE_Unknown447 447 // *not implemented +//#define SE_Unknown448 448 // *not implemented +//#define SE_Unknown449 449 // *not implemented +//#define SE_Unknown450 450 // *not implemented +#define SE_MitigateMeleeDamageSP 451 // implemented Partial Melee Rune that only is lowered if melee hits are over X amount of damage +//#define SE_Unknown452 452 // *not implemented +#define SE_SpellOnAmtDmgTaken 453 // implemented Trigger effect on X amount of damage taken + // LAST diff --git a/zone/attack.cpp b/zone/attack.cpp index 7cf35e2a2..205babcfd 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -3114,12 +3114,37 @@ int Mob::GetMonkHandToHandDelay(void) } } +int32 Mob::ReduceAllDamage(int32 damage) +{ + if(damage <= 0) + return damage; + + int32 slot = -1; + + if (spellbonuses.SpellOnAmtDmgTaken[2]){ + slot = spellbonuses.SpellOnAmtDmgTaken[1]; + + if (slot >= 0) { + if(damage > buffs[slot].melee_rune) { + if(!TryFadeEffect(slot)) + BuffFadeBySlot(slot); + } + else{ + buffs[slot].melee_rune = (buffs[slot].melee_rune - damage); + CheckHitsRemaining(slot); + } + } + } + return(damage); +} + int32 Mob::ReduceDamage(int32 damage) { if(damage <= 0) return damage; int32 slot = -1; + bool DisableMeleeRune = false; if (spellbonuses.NegateAttacks[0]){ slot = spellbonuses.NegateAttacks[1]; @@ -3129,7 +3154,36 @@ int32 Mob::ReduceDamage(int32 damage) } } - if (spellbonuses.MitigateMeleeRune[0]){ + //Only mitigate if damage is above the minimium specified. + if (spellbonuses.MitigateMeleeRuneSP[0]){ + slot = spellbonuses.MitigateMeleeRuneSP[1]; + + if (slot >= 0 && (damage > spellbonuses.MitigateMeleeRuneSP[2])) + { + DisableMeleeRune = true; + int damage_to_reduce = damage * spellbonuses.MitigateMeleeRuneSP[0] / 100; + if(damage_to_reduce > buffs[slot].melee_rune) + { + mlog(SPELLS__EFFECT_VALUES, "Mob::ReduceDamage SE_MitigateMeleeDamageSP %d damage negated, %d" + " damage remaining, fading buff.", damage_to_reduce, buffs[slot].melee_rune); + damage -= damage_to_reduce; + if(!TryFadeEffect(slot)) + BuffFadeBySlot(slot); + //UpdateRuneFlags(); + } + else + { + mlog(SPELLS__EFFECT_VALUES, "Mob::ReduceDamage SE_MitigateMeleeDamageSP %d damage negated, %d" + " damage remaining.", damage_to_reduce, buffs[slot].melee_rune); + buffs[slot].melee_rune = (buffs[slot].melee_rune - damage_to_reduce); + damage -= damage_to_reduce; + CheckHitsRemaining(slot); + } + } + } + + + if (spellbonuses.MitigateMeleeRune[0] && !DisableMeleeRune){ slot = spellbonuses.MitigateMeleeRune[1]; if(slot >= 0) { @@ -3424,7 +3478,8 @@ void Mob::CommonDamage(Mob* attacker, int32 &damage, const uint16 spell_id, cons attacker->SendAppearancePacket(AT_Sneak, 0); } //final damage has been determined. - + + ReduceAllDamage(damage); SetHP(GetHP() - damage); if(HasDied()) { diff --git a/zone/bonuses.cpp b/zone/bonuses.cpp index d7e3dcfe0..0f5517253 100644 --- a/zone/bonuses.cpp +++ b/zone/bonuses.cpp @@ -2150,6 +2150,17 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne break; } + + case SE_MitigateMeleeDamageSP: + { + if (newbon->MitigateMeleeRuneSP[0] < effect_value){ + newbon->MitigateMeleeRuneSP[0] = effect_value; + newbon->MitigateMeleeRuneSP[1] = buffslot; + newbon->MitigateMeleeRuneSP[2] = spells[spell_id].base2[i]; + } + break; + } + case SE_MitigateSpellDamage: { if (newbon->MitigateSpellRune[0] < effect_value){ @@ -2158,7 +2169,7 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne } break; } - + case SE_ManaAbsorbPercentDamage: { if (newbon->ManaAbsorbPercentDamage[0] < effect_value){ @@ -2168,6 +2179,16 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne break; } + case SE_SpellOnAmtDmgTaken: + { + if (newbon->SpellOnAmtDmgTaken[2] < spells[spell_id].base2[i]){ + newbon->SpellOnAmtDmgTaken[0] = effect_value; + newbon->SpellOnAmtDmgTaken[1] = buffslot; + newbon->SpellOnAmtDmgTaken[2] = spells[spell_id].base2[i]; + } + break; + } + case SE_ShieldBlock: newbon->ShieldBlock += effect_value; break; @@ -3386,6 +3407,12 @@ void Mob::NegateSpellsBonuses(uint16 spell_id) spellbonuses.MitigateMeleeRune[1] = -1; break; + case SE_MitigateMeleeDamageSP: + spellbonuses.MitigateMeleeRuneSP[0] = effect_value; + spellbonuses.MitigateMeleeRuneSP[1] = -1; + spellbonuses.MitigateMeleeRuneSP[1] = effect_value; + break; + case SE_MitigateSpellDamage: spellbonuses.MitigateSpellRune[0] = effect_value; spellbonuses.MitigateSpellRune[1] = -1; diff --git a/zone/common.h b/zone/common.h index bf0625b5f..513732e35 100644 --- a/zone/common.h +++ b/zone/common.h @@ -307,7 +307,9 @@ struct StatBonuses { int16 SkillDamageAmount2[HIGHEST_SKILL+2]; // Adds skill specific damage uint16 NegateAttacks[2]; // 0 = bool HasEffect 1 = Buff Slot uint16 MitigateMeleeRune[2]; // 0 = Mitigation value 1 = Buff Slot + uint16 MitigateMeleeRuneSP[3]; // 0 = Mitigation value 1 = Buff Slot 2 = Min damage to trigger. uint16 MitigateSpellRune[2]; // 0 = Mitigation value 1 = Buff Slot + uint32 SpellOnAmtDmgTaken[3]; // 0 = Spell Effect ID 1 = Buff slot 2 = Damage Amount to Trigger uint16 ManaAbsorbPercentDamage[2]; // 0 = Mitigation value 1 = Buff Slot int16 ShieldBlock; // Chance to Shield Block int16 BlockBehind; // Chance to Block Behind (with our without shield) diff --git a/zone/hate_list.cpp b/zone/hate_list.cpp index a6fb6be51..49058fc5e 100644 --- a/zone/hate_list.cpp +++ b/zone/hate_list.cpp @@ -244,6 +244,25 @@ void HateList::DoFactionHits(int32 nfl_id) { } } +int HateList::SummonedPetCount(Mob *hater) { + + //Function to get number of 'Summoned' pets on a targets hate list to allow calculations for certian spell effects. + //Unclear from description that pets are required to be 'summoned body type'. Will not require at this time. + int petcount = 0; + auto iterator = list.begin(); + while(iterator != list.end()) { + + if((*iterator)->ent != nullptr && (*iterator)->ent->IsNPC() && ((*iterator)->ent->CastToNPC()->IsPet() || ((*iterator)->ent->CastToNPC()->GetSwarmOwner() > 0))) + { + ++petcount; + } + + ++iterator; + } + + return petcount; +} + Mob *HateList::GetTop(Mob *center) { Mob* top = nullptr; diff --git a/zone/hate_list.h b/zone/hate_list.h index 7b708895d..a3e6c6a88 100644 --- a/zone/hate_list.h +++ b/zone/hate_list.h @@ -58,6 +58,8 @@ public: void CheckFrenzyHate(); //Gets the target with the most hate regardless of things like frenzy etc. Mob* GetMostHate(); + // Count 'Summoned' pets on hatelist + int SummonedPetCount(Mob *hater); int AreaRampage(Mob *caster, Mob *target, int count, ExtraAttackOptions *opts); diff --git a/zone/mob.cpp b/zone/mob.cpp index 820db7beb..a55325b51 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -3130,14 +3130,14 @@ void Mob::TriggerOnCast(uint32 focus_spell, uint32 spell_id, bool aa_trigger) trigger_spell_id = CastToClient()->CalcAAFocus(focusTriggerOnCast, focus_spell, spell_id); if(IsValidSpell(trigger_spell_id) && GetTarget()) - SpellFinished(trigger_spell_id, GetTarget()); + SpellFinished(trigger_spell_id, GetTarget(), 10, 0, -1, spells[trigger_spell_id].ResistDiff); } else{ trigger_spell_id = CalcFocusEffect(focusTriggerOnCast, focus_spell, spell_id); if(IsValidSpell(trigger_spell_id) && GetTarget()){ - SpellFinished(trigger_spell_id, GetTarget()); + SpellFinished(trigger_spell_id, GetTarget(),10, 0, -1, spells[trigger_spell_id].ResistDiff); CheckHitsRemaining(0, false,false, 0, focus_spell); } } @@ -3229,7 +3229,7 @@ void Mob::TryTwincast(Mob *caster, Mob *target, uint32 spell_id) if(MakeRandomInt(0, 100) <= focus) { Message(MT_Spells,"You twincast %s!",spells[spell_id].name); - SpellFinished(spell_id, target); + SpellFinished(spell_id, target, 10, 0, -1, spells[spell_id].ResistDiff); } } } @@ -3247,7 +3247,7 @@ void Mob::TryTwincast(Mob *caster, Mob *target, uint32 spell_id) { if(MakeRandomInt(0, 100) <= focus) { - SpellFinished(spell_id, target); + SpellFinished(spell_id, target, 10, 0, -1, spells[spell_id].ResistDiff); } } } @@ -3365,7 +3365,8 @@ bool Mob::TryFadeEffect(int slot) { for(int i = 0; i < EFFECT_COUNT; i++) { - if (spells[buffs[slot].spellid].effectid[i] == SE_CastOnWearoff || spells[buffs[slot].spellid].effectid[i] == SE_EffectOnFade) + if (spells[buffs[slot].spellid].effectid[i] == SE_CastOnWearoff || spells[buffs[slot].spellid].effectid[i] == SE_EffectOnFade + || spells[buffs[slot].spellid].effectid[i] == SE_SpellOnAmtDmgTaken) { uint16 spell_id = spells[buffs[slot].spellid].base[i]; BuffFadeBySlot(slot); @@ -3410,7 +3411,7 @@ void Mob::TrySympatheticProc(Mob *target, uint32 spell_id) SpellFinished(focus_trigger, target); else - SpellFinished(focus_trigger, this); + SpellFinished(focus_trigger, this, 10, 0, -1, spells[focus_trigger].ResistDiff); } // For detrimental spells, if the triggered spell is beneficial, then it will land on the caster // if the triggered spell is also detrimental, then it will land on the target @@ -3420,7 +3421,7 @@ void Mob::TrySympatheticProc(Mob *target, uint32 spell_id) SpellFinished(focus_trigger, this); else - SpellFinished(focus_trigger, target); + SpellFinished(focus_trigger, target, 10, 0, -1, spells[focus_trigger].ResistDiff); } CheckHitsRemaining(0, false,false, 0, focus_spell); } @@ -4419,7 +4420,7 @@ void Mob::ModSkillDmgTaken(SkillUseTypes skill_num, int value) SkillDmgTaken_Mod[skill_num] = value; - else if (skill_num == 255) + else if (skill_num == 255 || skill_num == -1) SkillDmgTaken_Mod[HIGHEST_SKILL+1] = value; } @@ -4428,7 +4429,7 @@ int16 Mob::GetModSkillDmgTaken(const SkillUseTypes skill_num) if (skill_num <= HIGHEST_SKILL) return SkillDmgTaken_Mod[skill_num]; - else if (skill_num == 255) + else if (skill_num == 255 || skill_num == -1) return SkillDmgTaken_Mod[HIGHEST_SKILL+1]; return 0; diff --git a/zone/mob.h b/zone/mob.h index 962bef3c8..76aa89b79 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -644,6 +644,7 @@ public: int32 ReduceDamage(int32 damage); int32 AffectMagicalDamage(int32 damage, uint16 spell_id, const bool iBuffTic, Mob* attacker); + int32 ReduceAllDamage(int32 damage); virtual void DoSpecialAttackDamage(Mob *who, SkillUseTypes skill, int32 max_damage, int32 min_damage = 1, int32 hate_override = -1, int ReuseTime = 10, bool HitChance=false); virtual void DoThrowingAttackDmg(Mob* other, const ItemInst* RangeWeapon=nullptr, const Item_Struct* item=nullptr, uint16 weapon_damage=0, int16 chance_mod=0,int16 focus=0); diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index 18e08030b..52646f262 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -214,6 +214,84 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial) int32 dmg = effect_value; if(dmg < 0) { + + /*Special Cases where Base2 is defined + Range 105 : Plant + Range 120 : Undead + Range 123 : Humanoid + Range 190 : No Raid boss flag *not implemented + Range 191 : This spell will deal less damage to 'exceptionally strong targets' - Raid boss flag *not implemented + Range 201 : Damage if HP > 75% + Range 221 - 299 : Causing damage dependent on how many pets/swarmpets are attacking your target. + Range 300 - 303 : UNKOWN *not implemented + Range 399 - 499 : Heal if HP within a specified range (400 = 0-25% 401 = 25 - 35% 402 = 35-45% ect) + Range 500 - 599 : Heal if HP less than a specified value + Range 600 - 699 : Limit to Body Type [base2 - 600 = Body] + Range 818 - 819 : If Undead/If Not Undead + Range 835 - : Unknown *not implemented + Range 836 - 837 : Progression Server / Live Server *not implemented + Range 839 - : Unknown *not implemented + Range 10000+ : Limit to Race [base2 - 10000 = Race] (*Not on live: Too useful a function to not implement) + + */ + + if (spells[spell_id].base2[i] > 0){ + + //It is unlikely these effects would give a fail message (Need to confirm) + if (spells[spell_id].base2[i] == 105){ + if (GetBodyType() != BT_Plant) + break; + } + + else if (spells[spell_id].base2[i] == 120){ + if (GetBodyType() != BT_Undead) + break; + } + + else if (spells[spell_id].base2[i] == 123){ + if (GetBodyType() != BT_Humanoid) + break; + } + + //Limit to Body Type. + else if (spells[spell_id].base2[i] >= 600 && spells[spell_id].base2[i] <= 699){ + if (GetBodyType() != (spells[spell_id].base2[i] - 600)){ + //caster->Message_StringID(13,CANNOT_AFFECT_NPC); + break; + } + } + + else if (spells[spell_id].base2[i] == 201){ + if (GetHPRatio() < 75) + break; + } + + //Limit to Race. *Not implemented on live + else if (spells[spell_id].base2[i] >= 10000 && spells[spell_id].base2[i] <= 11000){ + if (GetRace() != (spells[spell_id].base2[i] - 10000)){ + break; + } + } + + //Limit to amount of pets + else if (spells[spell_id].base2[i] >= 221 && spells[spell_id].base2[i] <= 299){ + bool allow_spell = false; + int count = hate_list.SummonedPetCount(this); + + for (int base2_value = 221; base2_value <= 233; ++base2_value){ + if (spells[spell_id].base2[i] == base2_value){ + if (count >= (base2_value - 220)){ + allow_spell = true; + break; + } + } + } + + if (!allow_spell) + break; + } + } + // take partial damage into account dmg = (int32) (dmg * partial / 100); @@ -229,6 +307,60 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial) } else if(dmg > 0) { //healing spell... + + if (spells[spell_id].base2[i] > 0) + { + bool allow_spell = false; + + //Heal only if HP within specified range. [Doesn't follow a set forumla for all values...] + if (spells[spell_id].base2[i] >= 400 && spells[spell_id].base2[i] <= 408){ + for (int base2_value = 400; base2_value <= 408; ++base2_value){ + if (spells[spell_id].base2[i] == base2_value){ + + if (spells[spell_id].base2[i] == 400){ + if (GetHPRatio() <= 25){ + allow_spell = true; + break; + } + } + + else if (spells[spell_id].base2[i] == base2_value){ + if (GetHPRatio() > 25+((base2_value - 401)*10) && GetHPRatio() <= 35+((base2_value - 401)*10)){ + allow_spell = true; + break; + } + } + } + } + } + + + else if (spells[spell_id].base2[i] >= 500 && spells[spell_id].base2[i] <= 520){ + for (int base2_value = 500; base2_value <= 520; ++base2_value){ + if (spells[spell_id].base2[i] == base2_value){ + + if (spells[spell_id].base2[i] == base2_value){ + if (GetHPRatio() < (base2_value - 500)*5) { + allow_spell = true; + break; + } + } + } + } + } + + else if (spells[spell_id].base2[i] == 399){ + if (GetHPRatio() > 15 && GetHPRatio() <= 25){ + allow_spell = true; + break; + } + } + + if(!allow_spell) + break; + } + + if(caster) dmg = caster->GetActSpellHealing(spell_id, dmg); @@ -1230,6 +1362,12 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial) break; } + case SE_MitigateMeleeDamageSP: + { + buffs[buffslot].melee_rune = spells[spell_id].max[i]; + break; + } + case SE_MitigateSpellDamage: { buffs[buffslot].magic_rune = GetPartialMagicRuneAmount(spell_id); @@ -1237,6 +1375,13 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial) break; } + //Using the melee_rune variable, however it will be calculated for both spell and melee. + case SE_SpellOnAmtDmgTaken: + { + buffs[buffslot].melee_rune = spells[spell_id].base2[i]; + break; + } + case SE_Levitate: { #ifdef SPELL_EFFECT_SPAM @@ -5075,7 +5220,7 @@ bool Mob::CheckHitsRemaining(uint32 buff_slot, bool when_spell_done, bool negate } // For lowering numhits when we already know the effects buff_slot - // Effects: SE_SpellVulnerability,SE_MitigateMeleeDamage,SE_NegateAttacks,SE_MitigateSpellDamage,SE_ManaAbsorbPercentDamage + // Effects: SE_SpellVulnerability,SE_MitigateMeleeDamage,SE_MitigateMeleeDamage2,SE_NegateAttacks,SE_MitigateSpellDamage,SE_ManaAbsorbPercentDamage if(spells[buffs[buff_slot].spellid].numhits > 0 || negate) { if(buffs[buff_slot].numhits > 1) { buffs[buff_slot].numhits--;