diff --git a/changelog.txt b/changelog.txt index db22c784e..eb1785b41 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,5 +1,10 @@ EQEMu Changelog (Started on Sept 24, 2003 15:50) ------------------------------------------------------- +== 02/18/2014 == +Kayen: Implemented SE_TriggerOnReqCaster - triggers a spell which a certain criteria are met (below X amount of hp,mana,end, number of pets on hatelist) +Kayen: Implemented SE_ImprovedTaunt - Locks Aggro On Caster and Decrease other Players Aggro by X% on NPC targets below level Y +Kayen: Fixed an error where SE_ChangeAggro was adding its bonus x 2 for spell generated aggro. (this applies also to spell casting subtlety AA reduction) + == 02/14/2014 == Kayen: Fixes for buffs not fading under certain conditions in revised numhits system, and other fixes. Kayen: Implemented support for spell_new field CastRestrictions (limits what type of targets spells can effect). diff --git a/common/spdat.cpp b/common/spdat.cpp index 2cbf8a97d..807a1ba2d 100644 --- a/common/spdat.cpp +++ b/common/spdat.cpp @@ -949,7 +949,7 @@ bool IsDebuffSpell(uint16 spell_id) if (IsBeneficialSpell(spell_id) || IsEffectHitpointsSpell(spell_id) || IsStunSpell(spell_id) || IsMezSpell(spell_id) || IsCharmSpell(spell_id) || IsSlowSpell(spell_id) || IsEffectInSpell(spell_id, SE_Root) || IsEffectInSpell(spell_id, SE_CancelMagic) || - IsEffectInSpell(spell_id, SE_MovementSpeed) || IsFearSpell(spell_id) || IsEffectInSpell(spell_id, SE_Calm)) + IsEffectInSpell(spell_id, SE_MovementSpeed) || IsFearSpell(spell_id) || IsEffectInSpell(spell_id, SE_InstantHate)) return false; else return true; diff --git a/common/spdat.h b/common/spdat.h index 64b3aedc1..237125b90 100644 --- a/common/spdat.h +++ b/common/spdat.h @@ -148,7 +148,7 @@ typedef enum { // full listing: https://forums.station.sony.com/eq/index.php?threads/enumerated-spa-list.206288/ // mirror: http://pastebin.com/MYeQqGwe #define SE_CurrentHP 0 // implemented - Heals and nukes, repeates every tic if in a buff -#define SE_ArmorClass 1 // implemented +#define SE_ArmorClass 1 // implemented #define SE_ATK 2 // implemented #define SE_MovementSpeed 3 // implemented - SoW, SoC, etc #define SE_STR 4 // implemented @@ -239,7 +239,7 @@ typedef enum { #define SE_ModelSize 89 // implemented - Shrink, Growth #define SE_Cloak 90 // *not implemented - Used in only 2 spells #define SE_SummonCorpse 91 // implemented -#define SE_Calm 92 // implemented - Hate modifier stuff(poorly named) +#define SE_InstantHate 92 // implemented - add hate #define SE_StopRain 93 // implemented - Wake of Karana #define SE_NegateIfCombat 94 // *not implemented? - Works client side but there is comment todo in spell effects...Component of Spirit of Scale #define SE_Sacrifice 95 // implemented @@ -572,7 +572,7 @@ typedef enum { #define SE_LimitUseMin 422 // implemented - limit a focus to require a min amount of numhits value (used with above) #define SE_LimitUseType 423 // implemented - limit a focus to require a certain numhits type #define SE_GravityEffect 424 // implemented - Pulls/pushes you toward/away the mob at a set pace -#define SE_Display 425 // *not implemented - Illusion: Flying Dragon(21626) +//#define SE_Display 425 // *not implemented - Illusion: Flying Dragon(21626) #define SE_IncreaseExtTargetWindow 426 // *not implmented[AA] - increases the capacity of your extended target window #define SE_SkillProc 427 // implemented - chance to proc when using a skill(ie taunt) #define SE_LimitToSkill 428 // implemented - limits what skills will effect a skill proc @@ -589,9 +589,9 @@ typedef enum { #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_CancleIfMoved 441 // *not implemented - Buff is removed from target when target moves X amount of distance away from where initially hit. -#define SE_TriggerOnValueAmount 442 // implemented - triggers a spell which a certain criteria are met (below X amount of hp,mana,end, number of pets on hatelist) -#define SE_TriggerIfMovement 443 // *not implemented - Trigger a spell if you move (37846 | Chopping Block) -#define SE_ImprovedTaunt 444 // *not implemented - Locks Aggro On Caster and Decrease other Players Aggro by X% up to level Z +#define SE_TriggerOnReqTarget 442 // implemented - triggers a spell which a certain criteria are met (below X amount of hp,mana,end, number of pets on hatelist) +#define SE_TriggerOnReqCaster 443 // implemented - triggers a spell which a certain criteria are met (below X amount of hp,mana,end, number of pets on hatelist) +#define SE_ImprovedTaunt 444 // implemented - Locks Aggro On Caster and Decrease other Players Aggro by X% on NPC targets below level Y #define SE_AddMercSlot 445 // *not implemented[AA] - [Hero's Barracks] Allows you to conscript additional mercs. //#define SE_AStacker 446 // *not implementet - bufff stacking blocker ? (26219 | Qirik's Watch) //#define SE_BStacker 447 // *not implemented diff --git a/zone/MobAI.cpp b/zone/MobAI.cpp index e65e357ad..38addf695 100644 --- a/zone/MobAI.cpp +++ b/zone/MobAI.cpp @@ -1054,7 +1054,8 @@ void Mob::AI_Process() { SetTarget(hate_list.GetTop(this)); } } else { - SetTarget(hate_list.GetTop(this)); + if (!ImprovedTaunt()) + SetTarget(hate_list.GetTop(this)); } } diff --git a/zone/aggro.cpp b/zone/aggro.cpp index 8fa426a67..326e320c0 100644 --- a/zone/aggro.cpp +++ b/zone/aggro.cpp @@ -1257,7 +1257,7 @@ int32 Mob::CheckAggroAmount(uint16 spell_id, bool isproc) break; } case SE_ReduceHate: - case SE_Calm: { + case SE_InstantHate: { nonModifiedAggro = CalcSpellEffectValue_formula(spells[spell_id].formula[o], spells[spell_id].base[o], spells[spell_id].max[o], slevel, spell_id); break; } @@ -1282,9 +1282,6 @@ int32 Mob::CheckAggroAmount(uint16 spell_id, bool isproc) if (IsClient()) HateMod += CastToClient()->GetFocusEffect(focusSpellHateMod, spell_id); - //Live AA - Spell casting subtlety - HateMod += aabonuses.hatemod + spellbonuses.hatemod + itembonuses.hatemod; - AggroAmount = (AggroAmount * HateMod) / 100; //made up number probably scales a bit differently on live but it seems like it will be close enough @@ -1405,7 +1402,7 @@ bool Mob::PassCharismaCheck(Mob* caster, Mob* spellTarget, uint16 spell_id) { return true; //1: The mob has a default 25% chance of being allowed a resistance check against the charm. - if (MakeRandomInt(0, 100) > RuleI(Spells, CharmBreakCheckChance)) + if (MakeRandomInt(0, 99) > RuleI(Spells, CharmBreakCheckChance)) return true; //2: The mob makes a resistance check against the charm @@ -1419,7 +1416,7 @@ bool Mob::PassCharismaCheck(Mob* caster, Mob* spellTarget, uint16 spell_id) { //3: At maxed ability, Total Domination has a 50% chance of preventing the charm break that otherwise would have occurred. uint16 TotalDominationBonus = caster->aabonuses.CharmBreakChance + caster->spellbonuses.CharmBreakChance + caster->itembonuses.CharmBreakChance; - if (MakeRandomInt(0, 100) < TotalDominationBonus) + if (MakeRandomInt(0, 99) < TotalDominationBonus) return true; } diff --git a/zone/attack.cpp b/zone/attack.cpp index 18571a08f..32a8318da 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -1303,6 +1303,7 @@ bool Client::Attack(Mob* other, int Hand, bool bRiposte, bool IsStrikethrough, b // Hate Generation is on a per swing basis, regardless of a hit, miss, or block, its always the same. // If we are this far, this means we are atleast making a swing. + if (!bRiposte) // Ripostes never generate any aggro. other->AddToHateList(this, hate); @@ -1868,6 +1869,7 @@ bool NPC::Attack(Mob* other, int Hand, bool bRiposte, bool IsStrikethrough, bool mlog(COMBAT__HITS, "Generating hate %d towards %s", hate, GetName()); // now add done damage to the hate list other->AddToHateList(this, hate); + } else { if(opts) { damage *= opts->damage_percent; @@ -2402,7 +2404,9 @@ bool NPC::Death(Mob* killerMob, int32 damage, uint16 spell, SkillUseTypes attack } void Mob::AddToHateList(Mob* other, int32 hate, int32 damage, bool iYellForHelp, bool bFrenzy, bool iBuffTic) { + assert(other != nullptr); + if (other == this) return; @@ -2483,6 +2487,10 @@ void Mob::AddToHateList(Mob* other, int32 hate, int32 damage, bool iYellForHelp, if(damage > GetHP()) damage = GetHP(); + if (spellbonuses.ImprovedTaunt[1] && (GetLevel() < spellbonuses.ImprovedTaunt[0]) + && other && (buffs[spellbonuses.ImprovedTaunt[0]].casterid != other->GetID())) + hate = (hate*spellbonuses.ImprovedTaunt[1])/100; + hate_list.Add(other, hate, damage, bFrenzy, !iBuffTic); if(other->IsClient()) diff --git a/zone/bonuses.cpp b/zone/bonuses.cpp index fbd4020eb..dc8776c67 100644 --- a/zone/bonuses.cpp +++ b/zone/bonuses.cpp @@ -2496,7 +2496,8 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne break; } - case SE_TriggerOnValueAmount: + case SE_TriggerOnReqTarget: + case SE_TriggerOnReqCaster: newbon->TriggerOnValueAmount = true; break; @@ -2504,6 +2505,14 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne newbon->DivineAura = true; break; + case SE_ImprovedTaunt: + if (newbon->ImprovedTaunt[0] < effect_value) { + newbon->ImprovedTaunt[0] = effect_value; + newbon->ImprovedTaunt[1] = spells[spell_id].base2[i]; + newbon->ImprovedTaunt[2] = buffslot; + } + break; + } } } diff --git a/zone/bot.cpp b/zone/bot.cpp index ff732d50c..3805dd025 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -12973,7 +12973,7 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { pacer->Say("Trying to pacify %s \n", target->GetCleanName()); if(pacer->Bot_Command_CalmTarget(target)) { - if(target->FindType(SE_Lull) || target->FindType(SE_Harmony) || target->FindType(SE_Calm)) + if(target->FindType(SE_Lull) || target->FindType(SE_Harmony) || target->FindType(SE_InstantHate)) //if(pacer->IsPacified(target)) c->Message(0, "I have successfully pacified %s.", target->GetCleanName()); return; @@ -12989,7 +12989,7 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { pacer->Say("Trying to pacify %s \n", target->GetCleanName()); if(pacer->Bot_Command_CalmTarget(target)) { - if(target->FindType(SE_Lull) || target->FindType(SE_Harmony) || target->FindType(SE_Calm)) + if(target->FindType(SE_Lull) || target->FindType(SE_Harmony) || target->FindType(SE_InstantHate)) //if(pacer->IsPacified(target)) c->Message(0, "I have successfully pacified %s.", target->GetCleanName()); return; diff --git a/zone/common.h b/zone/common.h index 758dd1cc4..622dafb6f 100644 --- a/zone/common.h +++ b/zone/common.h @@ -328,6 +328,7 @@ struct StatBonuses { bool CriticalHealDecay; // increase critical heal chance, decays based on spell level cast bool CriticalDotDecay; // increase critical dot chance, decays based on spell level cast bool DivineAura; // invulnerability + int16 ImprovedTaunt[3]; // 0 = Max Level 1 = Aggro modifier 2 = buffid //bool AbsorbMagicAtt; // Magic Rune *Need to be implemented for NegateEffect //bool MeleeRune; // Melee Rune *Need to be implemented for NegateEffect diff --git a/zone/merc.cpp b/zone/merc.cpp index 6da94f3e3..ef5818b6d 100644 --- a/zone/merc.cpp +++ b/zone/merc.cpp @@ -3670,7 +3670,7 @@ MercSpell Merc::GetBestMercSpellForHate(Merc* caster) { result.time_cancast = 0; if(caster) { - std::list mercSpellList = GetMercSpellsForSpellEffect(caster, SE_Calm); + std::list mercSpellList = GetMercSpellsForSpellEffect(caster, SE_InstantHate); for(std::list::iterator mercSpellListItr = mercSpellList.begin(); mercSpellListItr != mercSpellList.end(); ++mercSpellListItr) { // Assuming all the spells have been loaded into this list by level and in descending order diff --git a/zone/mob.cpp b/zone/mob.cpp index 078fed266..cee9003cc 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -3228,11 +3228,20 @@ void Mob::TryApplyEffect(Mob *target, uint32 spell_id) void Mob::TryTriggerOnValueAmount(bool IsHP, bool IsMana, bool IsEndur, bool IsPet) { /* + At present time there is no obvious difference between ReqTarget and ReqCaster + ReqTarget is typically used in spells cast on a target where the trigger occurs on that target. + ReqCaster is typically self only spells where the triggers on self. + Regardless both trigger on the owner of the buff. + */ + + /* + Base2 Range: 1004 = Below < 80% HP Base2 Range: 500-520 = Below (base2 - 500)*5 HP Base2 Range: 521 = Below (?) Mana UKNOWN - Will assume its 20% unless proven otherwise Base2 Range: 522 = Below (40%) Endurance Base2 Range: 523 = Below (40%) Mana Base2 Range: 220-? = Number of pets on hatelist to trigger (base2 - 220) (Set at 30 pets max for now) + 38311 = < 10% mana; */ if (!spellbonuses.TriggerOnValueAmount) @@ -3250,21 +3259,25 @@ void Mob::TryTriggerOnValueAmount(bool IsHP, bool IsMana, bool IsEndur, bool IsP for(int i = 0; i < EFFECT_COUNT; i++){ - if (spells[spell_id].effectid[i] == SE_TriggerOnValueAmount){ + if ((spells[spell_id].effectid[i] == SE_TriggerOnReqTarget) || (spells[spell_id].effectid[i] == SE_TriggerOnReqCaster)) { int base2 = spells[spell_id].base2[i]; bool use_spell = false; if (IsHP){ - if ((base2 >= 500 && base2 <= 520) && GetHPRatio() < (base2 - 500)*5){ + if ((base2 >= 500 && base2 <= 520) && GetHPRatio() < (base2 - 500)*5) + use_spell = true; + + else if (base2 = 1004 && GetHPRatio() < 80) use_spell = true; - } } else if (IsMana){ - if ( (base2 = 521 && GetManaRatio() < 20) || (base2 = 523 && GetManaRatio() < 40)) { + if ( (base2 = 521 && GetManaRatio() < 20) || (base2 = 523 && GetManaRatio() < 40)) + use_spell = true; + + else if (base2 = 38311 && GetManaRatio() < 10) use_spell = true; - } } else if (IsEndur){ @@ -3283,10 +3296,6 @@ void Mob::TryTriggerOnValueAmount(bool IsHP, bool IsMana, bool IsEndur, bool IsP if (use_spell){ SpellFinished(spells[spell_id].base[i], this, 10, 0, -1, spells[spell_id].ResistDiff); - /*Note, spell data shows numhits values of 0 or 1, however many descriptions of these spells indicate they should - be fading when consumed even with numhits of 0 (It makes sense they should fade...). - Unless proven otherwise, they should fade when triggered. */ - if(!TryFadeEffect(e)) BuffFadeBySlot(e); } diff --git a/zone/mob.h b/zone/mob.h index 7f64fbd90..7ab28f127 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -591,6 +591,7 @@ public: int32 GetExtraSpellAmt(uint16 spell_id, int32 extra_spell_amt, int32 base_spell_dmg); void MeleeLifeTap(int32 damage); bool PassCastRestriction(bool UseCastRestriction = true, int16 value = 0, bool IsDamage = true); + bool ImprovedTaunt(); void ModSkillDmgTaken(SkillUseTypes skill_num, int value); int16 GetModSkillDmgTaken(const SkillUseTypes skill_num); diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index d9438ef90..5e4ac7bfd 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -2667,7 +2667,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial) case SE_ChangeAggro: case SE_Hate2: case SE_Identify: - case SE_Calm: + case SE_InstantHate: case SE_ReduceHate: case SE_SpellDamageShield: case SE_ReverseDS: @@ -2806,7 +2806,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial) case SE_DoubleRangedAttack: case SE_ShieldEquipHateMod: case SE_ShieldEquipDmgMod: - case SE_TriggerOnValueAmount: + case SE_TriggerOnReqTarget: case SE_LimitRace: case SE_FcLimitUse: case SE_FcMute: @@ -5577,6 +5577,28 @@ bool Mob::TryDispel(uint8 caster_level, uint8 buff_level, int level_modifier){ return false; } + +bool Mob::ImprovedTaunt(){ + + if (spellbonuses.ImprovedTaunt[2]){ + + if (GetLevel() > spellbonuses.ImprovedTaunt[0]) + return false; + + target = entity_list.GetMob(buffs[spellbonuses.ImprovedTaunt[2]].casterid); + + if (target){ + SetTarget(target); + return true; + } + else + BuffFadeByEffect(spellbonuses.ImprovedTaunt[2]); //If caster killed removed effect. + } + + return false; +} + + bool Mob::PassCastRestriction(bool UseCastRestriction, int16 value, bool IsDamage) { /*If return TRUE spell met all restrictions and can continue (this = target). @@ -5633,7 +5655,7 @@ bool Mob::PassCastRestriction(bool UseCastRestriction, int16 value, bool IsDama Range 839 : Unknown *not implemented Range 842 - 844 : Humaniod lv MAX ((842 - 800) * 2) Range 845 - 847 : UNKNOWN - Range 10000+ : Limit to Race [base2 - 10000 = Race] (*Not on live: Too useful a function to not implement) + Range 10000 - 11000 : Limit to Race [base2 - 10000 = Race] (*Not on live: Too useful a function to not implement) THIS IS A WORK IN PROGRESS */ @@ -5650,7 +5672,7 @@ bool Mob::PassCastRestriction(bool UseCastRestriction, int16 value, bool IsDama break; case 101: - if (GetBodyType() == BT_Dragon) + if (GetBodyType() == BT_Dragon || GetBodyType() == BT_VeliousDragon || GetBodyType() == BT_Dragon3) return true; break; @@ -5786,6 +5808,11 @@ bool Mob::PassCastRestriction(bool UseCastRestriction, int16 value, bool IsDama return true; break; + case 701: + if (!IsPet()) + return true; + break; + case 818: if (GetBodyType() == BT_Undead) return true;