diff --git a/common/spdat.h b/common/spdat.h index bb71c1e88..ab128a37c 100644 --- a/common/spdat.h +++ b/common/spdat.h @@ -401,7 +401,7 @@ typedef enum { //#define SE_TrapCircumvention 245 // *not implemented[AA] - decreases the chance that you will set off a trap when opening a chest #define SE_SetBreathLevel 246 // *not implemented as bonus #define SE_RaiseSkillCap 247 // *not implemented[AA] - adds skill over the skill cap. -//#define SE_SecondaryForte 248 // not implemented as bonus(gives you a 2nd specialize skill that can go past 50 to 100) +#define SE_SecondaryForte 248 // not implemented as bonus(gives you a 2nd specialize skill that can go past 50 to 100) #define SE_SecondaryDmgInc 249 // implemented[AA] Allows off hand weapon to recieve a damage bonus (Sinister Strikes) #define SE_SpellProcChance 250 // implemented - Increase chance to proc from melee proc spells (ie Spirit of Panther) #define SE_ConsumeProjectile 251 // implemented[AA] - chance to not consume an arrow (ConsumeProjectile = 100) @@ -416,12 +416,12 @@ typedef enum { #define SE_AddSingingMod 260 // implemented[AA] - Instrument/Singing Mastery, base1 is the mod, base2 is the ItemType #define SE_SongModCap 261 // implemented[AA] - Song Mod cap increase (no longer used on live) #define SE_RaiseStatCap 262 // implemented -//#define SE_TradeSkillMastery 263 // not implemented - lets you raise more than one tradeskill above master. +#define SE_TradeSkillMastery 263 // not implemented - lets you raise more than one tradeskill above master. #define SE_HastenedAASkill 264 // implemented #define SE_MasteryofPast 265 // implemented[AA] - Spells less than effect values level can not be fizzled #define SE_ExtraAttackChance 266 // implemented - increase chance to score an extra attack with a 2-Handed Weapon. #define SE_PetDiscipline2 267 // *not implemented - /pet focus, /pet no cast -//#define SE_ReduceTradeskillFail 268 // *not implemented? - reduces chance to fail with given tradeskill by a percent chance +#define SE_ReduceTradeskillFail 268 // *not implemented? - reduces chance to fail with given tradeskill by a percent chance #define SE_MaxBindWound 269 // implemented[AA] - Increase max HP you can bind wound. #define SE_BardSongRange 270 // implemented[AA] - increase range of beneficial bard songs (Sionachie's Crescendo) #define SE_BaseMovementSpeed 271 // implemented[AA] - mods basemove speed, doesn't stack with other move mods @@ -441,7 +441,7 @@ typedef enum { //#define SE_NimbleEvasion 285 // *not implemented - base1 = 100 for max #define SE_FcDamageAmt 286 // implemented - adds direct spell damage #define SE_SpellDurationIncByTic 287 // implemented -#define SE_SpecialAttackKBProc 288 // implemented[AA] - Chance to to do a knockback from special attacks [AA Dragon Punch]. +#define SE_SkillAttackProc 288 // implemented[AA] - Chance to proc spell on skill attack usage (ex. Dragon Punch) #define SE_CastOnFadeEffect 289 // implemented - Triggers only if fades after natural duration. #define SE_IncreaseRunSpeedCap 290 // implemented[AA] - increases run speed over the hard cap #define SE_Purify 291 // implemented - Removes determental effects @@ -515,8 +515,8 @@ typedef enum { //#define SE_PassiveSenseTrap 359 // *not implemented - Invulnerability (Brell's Blessing) #define SE_ProcOnKillShot 360 // implemented - a buff that has a base1 % to cast spell base2 when you kill a "challenging foe" base3 min level #define SE_SpellOnDeath 361 // implemented - casts spell on death of buffed -//#define SE_PotionBeltSlots 362 // *not implemented[AA] 'Quick Draw' expands the potion belt by one additional available item slot per rank. -//#define SE_BandolierSlots 363 // *not implemented[AA] 'Battle Ready' expands the bandolier by one additional save slot per rank. +#define SE_PotionBeltSlots 362 // *not implemented[AA] 'Quick Draw' expands the potion belt by one additional available item slot per rank. +#define SE_BandolierSlots 363 // *not implemented[AA] 'Battle Ready' expands the bandolier by one additional save slot per rank. #define SE_TripleAttackChance 364 // implemented #define SE_ProcOnSpellKillShot 365 // implemented - chance to trigger a spell on kill when the kill is caused by a specific spell with this effect in it (10470 Venin) #define SE_ShieldEquipDmgMod 366 // implemented[AA] Damage modifier to melee if shield equiped. (base1 = dmg mod , base2 = ?) ie Shield Specialist AA @@ -525,7 +525,7 @@ typedef enum { #define SE_CorruptionCounter 369 // implemented #define SE_ResistCorruption 370 // implemented #define SE_AttackSpeed4 371 // implemented - stackable slow effect 'Inhibit Melee' -//#define SE_ForageSkill 372 // *not implemented[AA] Will increase the skill cap for those that have the Forage skill and grant the skill and raise the cap to those that do not. +#define SE_ForageSkill 372 // *not implemented[AA] Will increase the skill cap for those that have the Forage skill and grant the skill and raise the cap to those that do not. #define SE_CastOnFadeEffectAlways 373 // implemented - Triggers if fades after natural duration OR from rune/numhits fades. #define SE_ApplyEffect 374 // implemented #define SE_DotCritDmgIncrease 375 // implemented - Increase damage of DoT critical amount diff --git a/zone/attack.cpp b/zone/attack.cpp index 74cb33272..cecf37ff1 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -1372,6 +1372,13 @@ bool Client::Attack(Mob* other, int Hand, bool bRiposte, bool IsStrikethrough, b /////////////////////////////////////////////////////////// ////// Send Attack Damage /////////////////////////////////////////////////////////// + if (damage > 0 && aabonuses.SkillAttackProc[0] && aabonuses.SkillAttackProc[1] == skillinuse && + IsValidSpell(aabonuses.SkillAttackProc[2])) { + float chance = aabonuses.SkillAttackProc[0] / 1000.0f; + if (zone->random.Roll(chance)) + SpellFinished(aabonuses.SkillAttackProc[2], other, 10, 0, -1, + spells[aabonuses.SkillAttackProc[2]].ResistDiff); + } other->Damage(this, damage, SPELL_UNKNOWN, skillinuse); if (IsDead()) return false; diff --git a/zone/bonuses.cpp b/zone/bonuses.cpp index 437befe11..23a896979 100644 --- a/zone/bonuses.cpp +++ b/zone/bonuses.cpp @@ -632,812 +632,814 @@ void Client::CalcEdibleBonuses(StatBonuses* newbon) { } } -void Client::CalcAABonuses(StatBonuses* newbon) { - memset(newbon, 0, sizeof(StatBonuses)); //start fresh +void Client::CalcAABonuses(StatBonuses *newbon) +{ + memset(newbon, 0, sizeof(StatBonuses)); // start fresh - int i; - uint32 slots = 0; - uint32 aa_AA = 0; - uint32 aa_value = 0; - //aa old - //if(this->aa) { - // for (i = 0; i < MAX_PP_AA_ARRAY; i++) { //iterate through all of the client's AAs - // if (this->aa[i]) { // make sure aa exists or we'll crash zone - // aa_AA = this->aa[i]->AA; //same as aaid from the aa_effects table - // aa_value = this->aa[i]->value; //how many points in it - // if (aa_AA > 0 || aa_value > 0) { //do we have the AA? if 1 of the 2 is set, we can assume we do - // //slots = database.GetTotalAALevels(aa_AA); //find out how many effects from aa_effects table - // slots = zone->GetTotalAALevels(aa_AA); //find out how many effects from aa_effects, which is loaded into memory - // if (slots > 0) //and does it have any effects? may be able to put this above, not sure if it runs on each iteration - // ApplyAABonuses(aa_AA, slots, newbon); //add the bonuses - // } - // } - // } - //} + for (const auto &aa : aa_ranks) { + auto ability = zone->GetAlternateAdvancementAbility(aa.first); + // zone doesn't know about this! bad data some where + if (!ability) + continue; + + auto rank = ability->GetRankByPointsSpent(aa.second.first); + // bad data or no effects + if (!rank || rank->effects.empty()) + continue; + + ApplyAABonuses(*rank, newbon); + } } - //A lot of the normal spell functions (IsBlankSpellEffect, etc) are set for just spells (in common/spdat.h). //For now, we'll just put them directly into the code and comment with the corresponding normal function //Maybe we'll fix it later? :-D -void Client::ApplyAABonuses(uint32 aaid, uint32 slots, StatBonuses* newbon) +void Client::ApplyAABonuses(const AA::Rank &rank, StatBonuses *newbon) { - if(slots == 0) //sanity check. why bother if no slots to fill? + if (rank.effects.empty()) // sanity check. why bother if no slots to fill? return; - //from AA_Ability struct uint32 effect = 0; int32 base1 = 0; - int32 base2 = 0; //only really used for SE_RaiseStatCap & SE_ReduceSkillTimer in aa_effects table + int32 base2 = 0; // only really used for SE_RaiseStatCap & SE_ReduceSkillTimer in aa_effects table uint32 slot = 0; - //AA old - //std::map >::const_iterator find_iter = aa_effects.find(aaid); - //if(find_iter == aa_effects.end()) - //{ - // return; - //} - // - //for (std::map::const_iterator iter = aa_effects[aaid].begin(); iter != aa_effects[aaid].end(); ++iter) { - // effect = iter->second.skill_id; - // base1 = iter->second.base1; - // base2 = iter->second.base2; - // slot = iter->second.slot; - // - // //we default to 0 (SE_CurrentHP) for the effect, so if there aren't any base1/2 values, we'll just skip it - // if (effect == 0 && base1 == 0 && base2 == 0) - // continue; - // - // //IsBlankSpellEffect() - // if (effect == SE_Blank || (effect == SE_CHA && base1 == 0) || effect == SE_StackingCommand_Block || effect == SE_StackingCommand_Overwrite) - // continue; - // - // Log.Out(Logs::Detail, Logs::AA, "Applying Effect %d from AA %u in slot %d (base1: %d, base2: %d) on %s", effect, aaid, slot, base1, base2, this->GetCleanName()); - // - // uint8 focus = IsFocusEffect(0, 0, true,effect); - // if (focus) - // { - // newbon->FocusEffects[focus] = static_cast(effect); - // continue; - // } - // - // switch (effect) - // { - // //Note: AA effects that use accuracy are skill limited, while spell effect is not. - // case SE_Accuracy: - // if ((base2 == ALL_SKILLS) && (newbon->Accuracy[HIGHEST_SKILL+1] < base1)) - // newbon->Accuracy[HIGHEST_SKILL+1] = base1; - // else if (newbon->Accuracy[base2] < base1) - // newbon->Accuracy[base2] += base1; - // break; - // case SE_CurrentHP: //regens - // newbon->HPRegen += base1; - // break; - // case SE_CurrentEndurance: - // newbon->EnduranceRegen += base1; - // break; - // case SE_MovementSpeed: - // newbon->movementspeed += base1; //should we let these stack? - // /*if (base1 > newbon->movementspeed) //or should we use a total value? - // newbon->movementspeed = base1;*/ - // break; - // case SE_STR: - // newbon->STR += base1; - // break; - // case SE_DEX: - // newbon->DEX += base1; - // break; - // case SE_AGI: - // newbon->AGI += base1; - // break; - // case SE_STA: - // newbon->STA += base1; - // break; - // case SE_INT: - // newbon->INT += base1; - // break; - // case SE_WIS: - // newbon->WIS += base1; - // break; - // case SE_CHA: - // newbon->CHA += base1; - // break; - // case SE_WaterBreathing: - // //handled by client - // break; - // case SE_CurrentMana: - // newbon->ManaRegen += base1; - // break; - // case SE_ItemManaRegenCapIncrease: - // newbon->ItemManaRegenCap += base1; - // break; - // case SE_ResistFire: - // newbon->FR += base1; - // break; - // case SE_ResistCold: - // newbon->CR += base1; - // break; - // case SE_ResistPoison: - // newbon->PR += base1; - // break; - // case SE_ResistDisease: - // newbon->DR += base1; - // break; - // case SE_ResistMagic: - // newbon->MR += base1; - // break; - // case SE_ResistCorruption: - // newbon->Corrup += base1; - // break; - // case SE_IncreaseSpellHaste: - // break; - // case SE_IncreaseRange: - // break; - // case SE_MaxHPChange: - // newbon->MaxHP += base1; - // break; - // case SE_Packrat: - // newbon->Packrat += base1; - // break; - // case SE_TwoHandBash: - // break; - // case SE_SetBreathLevel: - // break; - // case SE_RaiseStatCap: - // switch(base2) - // { - // //are these #define'd somewhere? - // case 0: //str - // newbon->STRCapMod += base1; - // break; - // case 1: //sta - // newbon->STACapMod += base1; - // break; - // case 2: //agi - // newbon->AGICapMod += base1; - // break; - // case 3: //dex - // newbon->DEXCapMod += base1; - // break; - // case 4: //wis - // newbon->WISCapMod += base1; - // break; - // case 5: //int - // newbon->INTCapMod += base1; - // break; - // case 6: //cha - // newbon->CHACapMod += base1; - // break; - // case 7: //mr - // newbon->MRCapMod += base1; - // break; - // case 8: //cr - // newbon->CRCapMod += base1; - // break; - // case 9: //fr - // newbon->FRCapMod += base1; - // break; - // case 10: //pr - // newbon->PRCapMod += base1; - // break; - // case 11: //dr - // newbon->DRCapMod += base1; - // break; - // case 12: //corruption - // newbon->CorrupCapMod += base1; - // break; - // } - // break; - // case SE_PetDiscipline2: - // break; - // case SE_SpellSlotIncrease: - // break; - // case SE_MysticalAttune: - // newbon->BuffSlotIncrease += base1; - // break; - // case SE_TotalHP: - // newbon->HP += base1; - // break; - // case SE_StunResist: - // newbon->StunResist += base1; - // break; - // case SE_SpellCritChance: - // newbon->CriticalSpellChance += base1; - // break; - // case SE_SpellCritDmgIncrease: - // newbon->SpellCritDmgIncrease += base1; - // break; - // case SE_DotCritDmgIncrease: - // newbon->DotCritDmgIncrease += base1; - // break; - // case SE_ResistSpellChance: - // newbon->ResistSpellChance += base1; - // break; - // case SE_CriticalHealChance: - // newbon->CriticalHealChance += base1; - // break; - // case SE_CriticalHealOverTime: - // newbon->CriticalHealOverTime += base1; - // break; - // case SE_CriticalDoTChance: - // newbon->CriticalDoTChance += base1; - // break; - // case SE_ReduceSkillTimer: - // newbon->SkillReuseTime[base2] += base1; - // break; - // case SE_Fearless: - // newbon->Fearless = true; - // break; - // case SE_PersistantCasting: - // newbon->PersistantCasting += base1; - // break; - // case SE_DelayDeath: - // newbon->DelayDeath += base1; - // break; - // case SE_FrontalStunResist: - // newbon->FrontalStunResist += base1; - // break; - // case SE_ImprovedBindWound: - // newbon->BindWound += base1; - // break; - // case SE_MaxBindWound: - // newbon->MaxBindWound += base1; - // break; - // case SE_ExtraAttackChance: - // newbon->ExtraAttackChance += base1; - // break; - // case SE_SeeInvis: - // newbon->SeeInvis = base1; - // break; - // case SE_BaseMovementSpeed: - // newbon->BaseMovementSpeed += base1; - // break; - // case SE_IncreaseRunSpeedCap: - // newbon->IncreaseRunSpeedCap += base1; - // break; - // case SE_ConsumeProjectile: - // newbon->ConsumeProjectile += base1; - // break; - // case SE_ForageAdditionalItems: - // newbon->ForageAdditionalItems += base1; - // break; - // case SE_Salvage: - // newbon->SalvageChance += base1; - // break; - // case SE_ArcheryDamageModifier: - // newbon->ArcheryDamageModifier += base1; - // break; - // case SE_DoubleRangedAttack: - // newbon->DoubleRangedAttack += base1; - // break; - // case SE_DamageShield: - // newbon->DamageShield += base1; - // break; - // case SE_CharmBreakChance: - // newbon->CharmBreakChance += base1; - // break; - // case SE_OffhandRiposteFail: - // newbon->OffhandRiposteFail += base1; - // break; - // case SE_ItemAttackCapIncrease: - // newbon->ItemATKCap += base1; - // break; - // case SE_GivePetGroupTarget: - // newbon->GivePetGroupTarget = true; - // break; - // case SE_ItemHPRegenCapIncrease: - // newbon->ItemHPRegenCap = +base1; - // break; - // case SE_Ambidexterity: - // newbon->Ambidexterity += base1; - // break; - // case SE_PetMaxHP: - // newbon->PetMaxHP += base1; - // break; - // case SE_AvoidMeleeChance: - // newbon->AvoidMeleeChanceEffect += base1; - // break; - // case SE_CombatStability: - // newbon->CombatStability += base1; - // break; - // case SE_AddSingingMod: - // switch (base2) - // { - // case ItemTypeWindInstrument: - // newbon->windMod += base1; - // break; - // case ItemTypeStringedInstrument: - // newbon->stringedMod += base1; - // break; - // case ItemTypeBrassInstrument: - // newbon->brassMod += base1; - // break; - // case ItemTypePercussionInstrument: - // newbon->percussionMod += base1; - // break; - // case ItemTypeSinging: - // newbon->singingMod += base1; - // break; - // } - // break; - // case SE_SongModCap: - // newbon->songModCap += base1; - // break; - // case SE_PetCriticalHit: - // newbon->PetCriticalHit += base1; - // break; - // case SE_PetAvoidance: - // newbon->PetAvoidance += base1; - // break; - // case SE_ShieldBlock: - // newbon->ShieldBlock += base1; - // break; - // case SE_ShieldEquipHateMod: - // newbon->ShieldEquipHateMod += base1; - // break; - // case SE_ShieldEquipDmgMod: - // newbon->ShieldEquipDmgMod[0] += base1; - // newbon->ShieldEquipDmgMod[1] += base2; - // break; - // case SE_SecondaryDmgInc: - // newbon->SecondaryDmgInc = true; - // break; - // case SE_ChangeAggro: - // newbon->hatemod += base1; - // break; - // case SE_EndurancePool: - // newbon->Endurance += base1; - // break; - // case SE_ChannelChanceItems: - // newbon->ChannelChanceItems += base1; - // break; - // case SE_ChannelChanceSpells: - // newbon->ChannelChanceSpells += base1; - // break; - // case SE_DoubleSpecialAttack: - // newbon->DoubleSpecialAttack += base1; - // break; - // case SE_TripleBackstab: - // newbon->TripleBackstab += base1; - // break; - // case SE_FrontalBackstabMinDmg: - // newbon->FrontalBackstabMinDmg = true; - // break; - // case SE_FrontalBackstabChance: - // newbon->FrontalBackstabChance += base1; - // break; - // case SE_BlockBehind: - // newbon->BlockBehind += base1; - // break; - // - // case SE_StrikeThrough: - // case SE_StrikeThrough2: - // newbon->StrikeThrough += base1; - // break; - // case SE_DoubleAttackChance: - // newbon->DoubleAttackChance += base1; - // break; - // case SE_GiveDoubleAttack: - // newbon->GiveDoubleAttack += base1; - // break; - // case SE_ProcChance: - // newbon->ProcChanceSPA += base1; - // break; - // case SE_RiposteChance: - // newbon->RiposteChance += base1; - // break; - // case SE_Flurry: - // newbon->FlurryChance += base1; - // break; - // case SE_PetFlurry: - // newbon->PetFlurry += base1; - // break; - // case SE_BardSongRange: - // newbon->SongRange += base1; - // break; - // case SE_RootBreakChance: - // newbon->RootBreakChance += base1; - // break; - // case SE_UnfailingDivinity: - // newbon->UnfailingDivinity += base1; - // break; - // case SE_CrippBlowChance: - // newbon->CrippBlowChance += base1; - // break; - // - // case SE_HitChance: - // { - // if(base2 == ALL_SKILLS) - // newbon->HitChanceEffect[HIGHEST_SKILL+1] += base1; - // else - // newbon->HitChanceEffect[base2] += base1; - // } - // - // case SE_ProcOnKillShot: - // for(int i = 0; i < MAX_SPELL_TRIGGER*3; i+=3) - // { - // if(!newbon->SpellOnKill[i] || ((newbon->SpellOnKill[i] == base2) && (newbon->SpellOnKill[i+1] < base1))) - // { - // //base1 = chance, base2 = SpellID to be triggered, base3 = min npc level - // newbon->SpellOnKill[i] = base2; - // newbon->SpellOnKill[i+1] = base1; - // - // if (GetLevel() > 15) - // newbon->SpellOnKill[i+2] = GetLevel() - 15; //AA specifiy "non-trivial" - // else - // newbon->SpellOnKill[i+2] = 0; - // - // break; - // } - // } - // break; - // - // case SE_SpellOnDeath: - // for(int i = 0; i < MAX_SPELL_TRIGGER*2; i+=2) - // { - // if(!newbon->SpellOnDeath[i]) - // { - // // base1 = SpellID to be triggered, base2 = chance to fire - // newbon->SpellOnDeath[i] = base1; - // newbon->SpellOnDeath[i+1] = base2; - // break; - // } - // } - // break; - // - // case SE_TriggerOnCast: - // - // for(int i = 0; i < MAX_SPELL_TRIGGER; i++) - // { - // if (newbon->SpellTriggers[i] == aaid) - // break; - // - // if(!newbon->SpellTriggers[i]) - // { - // //Save the 'aaid' of each triggerable effect to an array - // newbon->SpellTriggers[i] = aaid; - // break; - // } - // } - // break; - // - // case SE_CriticalHitChance: - // { - // if(base2 == ALL_SKILLS) - // newbon->CriticalHitChance[HIGHEST_SKILL+1] += base1; - // else - // newbon->CriticalHitChance[base2] += base1; - // } - // break; - // - // case SE_CriticalDamageMob: - // { - // // base1 = effect value, base2 = skill restrictions(-1 for all) - // if(base2 == ALL_SKILLS) - // newbon->CritDmgMob[HIGHEST_SKILL+1] += base1; - // else - // newbon->CritDmgMob[base2] += base1; - // break; - // } - // - // case SE_CriticalSpellChance: - // { - // newbon->CriticalSpellChance += base1; - // - // if (base2 > newbon->SpellCritDmgIncNoStack) - // newbon->SpellCritDmgIncNoStack = base2; - // - // break; - // } - // - // case SE_ResistFearChance: - // { - // if(base1 == 100) // If we reach 100% in a single spell/item then we should be immune to negative fear resist effects until our immunity is over - // newbon->Fearless = true; - // - // newbon->ResistFearChance += base1; // these should stack - // break; - // } - // - // case SE_SkillDamageAmount: - // { - // if(base2 == ALL_SKILLS) - // newbon->SkillDamageAmount[HIGHEST_SKILL+1] += base1; - // else - // newbon->SkillDamageAmount[base2] += base1; - // break; - // } - // - // case SE_SpecialAttackKBProc: - // { - // //You can only have one of these per client. [AA Dragon Punch] - // newbon->SpecialAttackKBProc[0] = base1; //Chance base 100 = 25% proc rate - // newbon->SpecialAttackKBProc[1] = base2; //Skill to KB Proc Off - // break; - // } - // - // case SE_DamageModifier: - // { - // if(base2 == ALL_SKILLS) - // newbon->DamageModifier[HIGHEST_SKILL+1] += base1; - // else - // newbon->DamageModifier[base2] += base1; - // break; - // } - // - // case SE_DamageModifier2: - // { - // if(base2 == ALL_SKILLS) - // newbon->DamageModifier2[HIGHEST_SKILL+1] += base1; - // else - // newbon->DamageModifier2[base2] += base1; - // break; - // } - // - // case SE_SlayUndead: - // { - // if(newbon->SlayUndead[1] < base1) - // newbon->SlayUndead[0] = base1; // Rate - // newbon->SlayUndead[1] = base2; // Damage Modifier - // break; - // } - // - // case SE_DoubleRiposte: - // { - // newbon->DoubleRiposte += base1; - // } - // - // case SE_GiveDoubleRiposte: - // { - // //0=Regular Riposte 1=Skill Attack Riposte 2=Skill - // if(base2 == 0){ - // if(newbon->GiveDoubleRiposte[0] < base1) - // newbon->GiveDoubleRiposte[0] = base1; - // } - // //Only for special attacks. - // else if(base2 > 0 && (newbon->GiveDoubleRiposte[1] < base1)){ - // newbon->GiveDoubleRiposte[1] = base1; - // newbon->GiveDoubleRiposte[2] = base2; - // } - // - // break; - // } - // - // //Kayen: Not sure best way to implement this yet. - // //Physically raises skill cap ie if 55/55 it will raise to 55/60 - // case SE_RaiseSkillCap: - // { - // if(newbon->RaiseSkillCap[0] < base1){ - // newbon->RaiseSkillCap[0] = base1; //value - // newbon->RaiseSkillCap[1] = base2; //skill - // } - // break; - // } - // - // case SE_MasteryofPast: - // { - // if(newbon->MasteryofPast < base1) - // newbon->MasteryofPast = base1; - // break; - // } - // - // case SE_CastingLevel2: - // case SE_CastingLevel: - // { - // newbon->effective_casting_level += base1; - // break; - // } - // - // case SE_DivineSave: - // { - // if(newbon->DivineSaveChance[0] < base1) - // { - // newbon->DivineSaveChance[0] = base1; - // newbon->DivineSaveChance[1] = base2; - // } - // break; - // } - // - // - // case SE_SpellEffectResistChance: - // { - // for(int e = 0; e < MAX_RESISTABLE_EFFECTS*2; e+=2) - // { - // if(newbon->SEResist[e+1] && (newbon->SEResist[e] == base2) && (newbon->SEResist[e+1] < base1)){ - // newbon->SEResist[e] = base2; //Spell Effect ID - // newbon->SEResist[e+1] = base1; //Resist Chance - // break; - // } - // else if (!newbon->SEResist[e+1]){ - // newbon->SEResist[e] = base2; //Spell Effect ID - // newbon->SEResist[e+1] = base1; //Resist Chance - // break; - // } - // } - // break; - // } - // - // case SE_MitigateDamageShield: - // { - // if (base1 < 0) - // base1 = base1*(-1); - // - // newbon->DSMitigationOffHand += base1; - // break; - // } - // - // case SE_FinishingBlow: - // { - // //base1 = chance, base2 = damage - // if (newbon->FinishingBlow[1] < base2){ - // newbon->FinishingBlow[0] = base1; - // newbon->FinishingBlow[1] = base2; - // } - // break; - // } - // - // case SE_FinishingBlowLvl: - // { - // //base1 = level, base2 = ??? (Set to 200 in AA data, possible proc rate mod?) - // if (newbon->FinishingBlowLvl[0] < base1){ - // newbon->FinishingBlowLvl[0] = base1; - // newbon->FinishingBlowLvl[1] = base2; - // } - // break; - // } - // - // case SE_StunBashChance: - // newbon->StunBashChance += base1; - // break; - // - // case SE_IncreaseChanceMemwipe: - // newbon->IncreaseChanceMemwipe += base1; - // break; - // - // case SE_CriticalMend: - // newbon->CriticalMend += base1; - // break; - // - // case SE_HealRate: - // newbon->HealRate += base1; - // break; - // - // case SE_MeleeLifetap: - // { - // - // if((base1 < 0) && (newbon->MeleeLifetap > base1)) - // newbon->MeleeLifetap = base1; - // - // else if(newbon->MeleeLifetap < base1) - // newbon->MeleeLifetap = base1; - // break; - // } - // - // case SE_Vampirism: - // newbon->Vampirism += base1; - // break; - // - // case SE_FrenziedDevastation: - // newbon->FrenziedDevastation += base2; - // break; - // - // case SE_SpellProcChance: - // newbon->SpellProcChance += base1; - // break; - // - // case SE_Berserk: - // newbon->BerserkSPA = true; - // break; - // - // case SE_Metabolism: - // newbon->Metabolism += base1; - // break; - // - // case SE_ImprovedReclaimEnergy: - // { - // if((base1 < 0) && (newbon->ImprovedReclaimEnergy > base1)) - // newbon->ImprovedReclaimEnergy = base1; - // - // else if(newbon->ImprovedReclaimEnergy < base1) - // newbon->ImprovedReclaimEnergy = base1; - // break; - // } - // - // case SE_HeadShot: - // { - // if(newbon->HeadShot[1] < base2){ - // newbon->HeadShot[0] = base1; - // newbon->HeadShot[1] = base2; - // } - // break; - // } - // - // case SE_HeadShotLevel: - // { - // if(newbon->HSLevel < base1) - // newbon->HSLevel = base1; - // break; - // } - // - // case SE_Assassinate: - // { - // if(newbon->Assassinate[1] < base2){ - // newbon->Assassinate[0] = base1; - // newbon->Assassinate[1] = base2; - // } - // break; - // } - // - // case SE_AssassinateLevel: - // { - // if(newbon->AssassinateLevel < base1) - // newbon->AssassinateLevel = base1; - // break; - // } - // - // case SE_PetMeleeMitigation: - // newbon->PetMeleeMitigation += base1; - // break; - // - // case SE_MeleeVulnerability: - // newbon->MeleeVulnerability += base1; - // break; - // - // case SE_FactionModPct: - // { - // if((base1 < 0) && (newbon->FactionModPct > base1)) - // newbon->FactionModPct = base1; - // - // else if(newbon->FactionModPct < base1) - // newbon->FactionModPct = base1; - // break; - // } - // - // case SE_IllusionPersistence: - // newbon->IllusionPersistence = true; - // break; - // - // case SE_LimitToSkill:{ - // if (base1 <= HIGHEST_SKILL) - // newbon->LimitToSkill[base1] = true; - // break; - // } - // - // case SE_SkillProc:{ - // for(int e = 0; e < MAX_SKILL_PROCS; e++) - // { - // if(newbon->SkillProc[e] && newbon->SkillProc[e] == aaid) - // break; //Do not use the same aa id more than once. - // - // else if(!newbon->SkillProc[e]){ - // newbon->SkillProc[e] = aaid; - // break; - // } - // } - // break; - // } - // - // case SE_SkillProcSuccess:{ - // - // for(int e = 0; e < MAX_SKILL_PROCS; e++) - // { - // if(newbon->SkillProcSuccess[e] && newbon->SkillProcSuccess[e] == aaid) - // break; //Do not use the same spell id more than once. - // - // else if(!newbon->SkillProcSuccess[e]){ - // newbon->SkillProcSuccess[e] = aaid; - // break; - // } - // } - // break; - // } - // - // case SE_MeleeMitigation: - // newbon->MeleeMitigationEffect -= base1; - // break; - // - // } - //} + for (const auto &e : rank.effects) { + effect = e.effect_id; + base1 = e.base1; + base2 = e.base2; + slot = e.slot; + + // we default to 0 (SE_CurrentHP) for the effect, so if there aren't any base1/2 values, we'll just skip it + if (effect == 0 && base1 == 0 && base2 == 0) + continue; + + // IsBlankSpellEffect() + if (effect == SE_Blank || (effect == SE_CHA && base1 == 0) || effect == SE_StackingCommand_Block || + effect == SE_StackingCommand_Overwrite) + continue; + + Log.Out(Logs::Detail, Logs::AA, "Applying Effect %d from AA %u in slot %d (base1: %d, base2: %d) on %s", + effect, rank.id, slot, base1, base2, GetCleanName()); + + uint8 focus = IsFocusEffect(0, 0, true, effect); + if (focus) { + newbon->FocusEffects[focus] = static_cast(effect); + continue; + } + + switch (effect) { + // Note: AA effects that use accuracy are skill limited, while spell effect is not. + case SE_Accuracy: + if ((base2 == ALL_SKILLS) && (newbon->Accuracy[HIGHEST_SKILL + 1] < base1)) + newbon->Accuracy[HIGHEST_SKILL + 1] = base1; + else if (newbon->Accuracy[base2] < base1) + newbon->Accuracy[base2] += base1; + break; + case SE_CurrentHP: // regens + newbon->HPRegen += base1; + break; + case SE_CurrentEndurance: + newbon->EnduranceRegen += base1; + break; + case SE_MovementSpeed: + newbon->movementspeed += base1; // should we let these stack? + /*if (base1 > newbon->movementspeed) //or should we use a total value? + newbon->movementspeed = base1;*/ + break; + case SE_STR: + newbon->STR += base1; + break; + case SE_DEX: + newbon->DEX += base1; + break; + case SE_AGI: + newbon->AGI += base1; + break; + case SE_STA: + newbon->STA += base1; + break; + case SE_INT: + newbon->INT += base1; + break; + case SE_WIS: + newbon->WIS += base1; + break; + case SE_CHA: + newbon->CHA += base1; + break; + case SE_WaterBreathing: + // handled by client + break; + case SE_CurrentMana: + newbon->ManaRegen += base1; + break; + case SE_ManaPool: + newbon->Mana += base1; + break; + case SE_ItemManaRegenCapIncrease: + newbon->ItemManaRegenCap += base1; + break; + case SE_ResistFire: + newbon->FR += base1; + break; + case SE_ResistCold: + newbon->CR += base1; + break; + case SE_ResistPoison: + newbon->PR += base1; + break; + case SE_ResistDisease: + newbon->DR += base1; + break; + case SE_ResistMagic: + newbon->MR += base1; + break; + case SE_ResistCorruption: + newbon->Corrup += base1; + break; + case SE_IncreaseSpellHaste: + break; + case SE_IncreaseRange: + break; + case SE_MaxHPChange: + newbon->MaxHP += base1; + break; + case SE_Packrat: + newbon->Packrat += base1; + break; + case SE_TwoHandBash: + break; + case SE_SetBreathLevel: + break; + case SE_RaiseStatCap: + switch (base2) { + // are these #define'd somewhere? + case 0: // str + newbon->STRCapMod += base1; + break; + case 1: // sta + newbon->STACapMod += base1; + break; + case 2: // agi + newbon->AGICapMod += base1; + break; + case 3: // dex + newbon->DEXCapMod += base1; + break; + case 4: // wis + newbon->WISCapMod += base1; + break; + case 5: // int + newbon->INTCapMod += base1; + break; + case 6: // cha + newbon->CHACapMod += base1; + break; + case 7: // mr + newbon->MRCapMod += base1; + break; + case 8: // cr + newbon->CRCapMod += base1; + break; + case 9: // fr + newbon->FRCapMod += base1; + break; + case 10: // pr + newbon->PRCapMod += base1; + break; + case 11: // dr + newbon->DRCapMod += base1; + break; + case 12: // corruption + newbon->CorrupCapMod += base1; + break; + } + break; + case SE_PetDiscipline2: + break; + case SE_SpellSlotIncrease: + break; + case SE_MysticalAttune: + newbon->BuffSlotIncrease += base1; + break; + case SE_TotalHP: + newbon->HP += base1; + break; + case SE_StunResist: + newbon->StunResist += base1; + break; + case SE_SpellCritChance: + newbon->CriticalSpellChance += base1; + break; + case SE_SpellCritDmgIncrease: + newbon->SpellCritDmgIncrease += base1; + break; + case SE_DotCritDmgIncrease: + newbon->DotCritDmgIncrease += base1; + break; + case SE_ResistSpellChance: + newbon->ResistSpellChance += base1; + break; + case SE_CriticalHealChance: + newbon->CriticalHealChance += base1; + break; + case SE_CriticalHealOverTime: + newbon->CriticalHealOverTime += base1; + break; + case SE_CriticalDoTChance: + newbon->CriticalDoTChance += base1; + break; + case SE_ReduceSkillTimer: + newbon->SkillReuseTime[base2] += base1; + break; + case SE_Fearless: + newbon->Fearless = true; + break; + case SE_PersistantCasting: + newbon->PersistantCasting += base1; + break; + case SE_DelayDeath: + newbon->DelayDeath += base1; + break; + case SE_FrontalStunResist: + newbon->FrontalStunResist += base1; + break; + case SE_ImprovedBindWound: + newbon->BindWound += base1; + break; + case SE_MaxBindWound: + newbon->MaxBindWound += base1; + break; + case SE_ExtraAttackChance: + newbon->ExtraAttackChance += base1; + break; + case SE_SeeInvis: + newbon->SeeInvis = base1; + break; + case SE_BaseMovementSpeed: + newbon->BaseMovementSpeed += base1; + break; + case SE_IncreaseRunSpeedCap: + newbon->IncreaseRunSpeedCap += base1; + break; + case SE_ConsumeProjectile: + newbon->ConsumeProjectile += base1; + break; + case SE_ForageAdditionalItems: + newbon->ForageAdditionalItems += base1; + break; + case SE_Salvage: + newbon->SalvageChance += base1; + break; + case SE_ArcheryDamageModifier: + newbon->ArcheryDamageModifier += base1; + break; + case SE_DoubleRangedAttack: + newbon->DoubleRangedAttack += base1; + break; + case SE_DamageShield: + newbon->DamageShield += base1; + break; + case SE_CharmBreakChance: + newbon->CharmBreakChance += base1; + break; + case SE_OffhandRiposteFail: + newbon->OffhandRiposteFail += base1; + break; + case SE_ItemAttackCapIncrease: + newbon->ItemATKCap += base1; + break; + case SE_GivePetGroupTarget: + newbon->GivePetGroupTarget = true; + break; + case SE_ItemHPRegenCapIncrease: + newbon->ItemHPRegenCap = +base1; + break; + case SE_Ambidexterity: + newbon->Ambidexterity += base1; + break; + case SE_PetMaxHP: + newbon->PetMaxHP += base1; + break; + case SE_AvoidMeleeChance: + newbon->AvoidMeleeChanceEffect += base1; + break; + case SE_CombatStability: + newbon->CombatStability += base1; + break; + case SE_AddSingingMod: + switch (base2) { + case ItemTypeWindInstrument: + newbon->windMod += base1; + break; + case ItemTypeStringedInstrument: + newbon->stringedMod += base1; + break; + case ItemTypeBrassInstrument: + newbon->brassMod += base1; + break; + case ItemTypePercussionInstrument: + newbon->percussionMod += base1; + break; + case ItemTypeSinging: + newbon->singingMod += base1; + break; + } + break; + case SE_SongModCap: + newbon->songModCap += base1; + break; + case SE_PetCriticalHit: + newbon->PetCriticalHit += base1; + break; + case SE_PetAvoidance: + newbon->PetAvoidance += base1; + break; + case SE_ShieldBlock: + newbon->ShieldBlock += base1; + break; + case SE_ShieldEquipHateMod: + newbon->ShieldEquipHateMod += base1; + break; + case SE_ShieldEquipDmgMod: + newbon->ShieldEquipDmgMod[0] += base1; + newbon->ShieldEquipDmgMod[1] += base2; + break; + case SE_SecondaryDmgInc: + newbon->SecondaryDmgInc = true; + break; + case SE_ChangeAggro: + newbon->hatemod += base1; + break; + case SE_EndurancePool: + newbon->Endurance += base1; + break; + case SE_ChannelChanceItems: + newbon->ChannelChanceItems += base1; + break; + case SE_ChannelChanceSpells: + newbon->ChannelChanceSpells += base1; + break; + case SE_DoubleSpecialAttack: + newbon->DoubleSpecialAttack += base1; + break; + case SE_TripleBackstab: + newbon->TripleBackstab += base1; + break; + case SE_FrontalBackstabMinDmg: + newbon->FrontalBackstabMinDmg = true; + break; + case SE_FrontalBackstabChance: + newbon->FrontalBackstabChance += base1; + break; + case SE_BlockBehind: + newbon->BlockBehind += base1; + break; + + case SE_StrikeThrough: + case SE_StrikeThrough2: + newbon->StrikeThrough += base1; + break; + case SE_DoubleAttackChance: + newbon->DoubleAttackChance += base1; + break; + case SE_GiveDoubleAttack: + newbon->GiveDoubleAttack += base1; + break; + case SE_ProcChance: + newbon->ProcChanceSPA += base1; + break; + case SE_RiposteChance: + newbon->RiposteChance += base1; + break; + case SE_Flurry: + newbon->FlurryChance += base1; + break; + case SE_PetFlurry: + newbon->PetFlurry += base1; + break; + case SE_BardSongRange: + newbon->SongRange += base1; + break; + case SE_RootBreakChance: + newbon->RootBreakChance += base1; + break; + case SE_UnfailingDivinity: + newbon->UnfailingDivinity += base1; + break; + case SE_CrippBlowChance: + newbon->CrippBlowChance += base1; + break; + + case SE_HitChance: { + if (base2 == ALL_SKILLS) + newbon->HitChanceEffect[HIGHEST_SKILL + 1] += base1; + else + newbon->HitChanceEffect[base2] += base1; + } + + case SE_ProcOnKillShot: + for (int i = 0; i < MAX_SPELL_TRIGGER * 3; i += 3) { + if (!newbon->SpellOnKill[i] || + ((newbon->SpellOnKill[i] == base2) && (newbon->SpellOnKill[i + 1] < base1))) { + // base1 = chance, base2 = SpellID to be triggered, base3 = min npc level + newbon->SpellOnKill[i] = base2; + newbon->SpellOnKill[i + 1] = base1; + + if (GetLevel() > 15) + newbon->SpellOnKill[i + 2] = + GetLevel() - 15; // AA specifiy "non-trivial" + else + newbon->SpellOnKill[i + 2] = 0; + + break; + } + } + break; + + case SE_SpellOnDeath: + for (int i = 0; i < MAX_SPELL_TRIGGER * 2; i += 2) { + if (!newbon->SpellOnDeath[i]) { + // base1 = SpellID to be triggered, base2 = chance to fire + newbon->SpellOnDeath[i] = base1; + newbon->SpellOnDeath[i + 1] = base2; + break; + } + } + break; + + case SE_TriggerOnCast: + + for (int i = 0; i < MAX_SPELL_TRIGGER; i++) { + if (newbon->SpellTriggers[i] == rank.id) + break; + + if (!newbon->SpellTriggers[i]) { + // Save the 'rank.id' of each triggerable effect to an array + newbon->SpellTriggers[i] = rank.id; + break; + } + } + break; + + case SE_CriticalHitChance: { + if (base2 == ALL_SKILLS) + newbon->CriticalHitChance[HIGHEST_SKILL + 1] += base1; + else + newbon->CriticalHitChance[base2] += base1; + } break; + + case SE_CriticalDamageMob: { + // base1 = effect value, base2 = skill restrictions(-1 for all) + if (base2 == ALL_SKILLS) + newbon->CritDmgMob[HIGHEST_SKILL + 1] += base1; + else + newbon->CritDmgMob[base2] += base1; + break; + } + + case SE_CriticalSpellChance: { + newbon->CriticalSpellChance += base1; + + if (base2 > newbon->SpellCritDmgIncNoStack) + newbon->SpellCritDmgIncNoStack = base2; + + break; + } + + case SE_ResistFearChance: { + if (base1 == 100) // If we reach 100% in a single spell/item then we should be immune to + // negative fear resist effects until our immunity is over + newbon->Fearless = true; + + newbon->ResistFearChance += base1; // these should stack + break; + } + + case SE_SkillDamageAmount: { + if (base2 == ALL_SKILLS) + newbon->SkillDamageAmount[HIGHEST_SKILL + 1] += base1; + else + newbon->SkillDamageAmount[base2] += base1; + break; + } + + case SE_SkillAttackProc: { + // You can only have one of these per client. [AA Dragon Punch] + newbon->SkillAttackProc[0] = base1; // Chance base 1000 = 100% proc rate + newbon->SkillAttackProc[1] = base2; // Skill to Proc Off + newbon->SkillAttackProc[2] = rank.spell; // spell to proc + break; + } + + case SE_DamageModifier: { + if (base2 == ALL_SKILLS) + newbon->DamageModifier[HIGHEST_SKILL + 1] += base1; + else + newbon->DamageModifier[base2] += base1; + break; + } + + case SE_DamageModifier2: { + if (base2 == ALL_SKILLS) + newbon->DamageModifier2[HIGHEST_SKILL + 1] += base1; + else + newbon->DamageModifier2[base2] += base1; + break; + } + + case SE_SlayUndead: { + if (newbon->SlayUndead[1] < base1) + newbon->SlayUndead[0] = base1; // Rate + newbon->SlayUndead[1] = base2; // Damage Modifier + break; + } + + case SE_DoubleRiposte: { + newbon->DoubleRiposte += base1; + } + + case SE_GiveDoubleRiposte: { + // 0=Regular Riposte 1=Skill Attack Riposte 2=Skill + if (base2 == 0) { + if (newbon->GiveDoubleRiposte[0] < base1) + newbon->GiveDoubleRiposte[0] = base1; + } + // Only for special attacks. + else if (base2 > 0 && (newbon->GiveDoubleRiposte[1] < base1)) { + newbon->GiveDoubleRiposte[1] = base1; + newbon->GiveDoubleRiposte[2] = base2; + } + + break; + } + + // Kayen: Not sure best way to implement this yet. + // Physically raises skill cap ie if 55/55 it will raise to 55/60 + case SE_RaiseSkillCap: { + if (newbon->RaiseSkillCap[0] < base1) { + newbon->RaiseSkillCap[0] = base1; // value + newbon->RaiseSkillCap[1] = base2; // skill + } + break; + } + + case SE_MasteryofPast: { + if (newbon->MasteryofPast < base1) + newbon->MasteryofPast = base1; + break; + } + + case SE_CastingLevel2: + case SE_CastingLevel: { + newbon->effective_casting_level += base1; + break; + } + + case SE_DivineSave: { + if (newbon->DivineSaveChance[0] < base1) { + newbon->DivineSaveChance[0] = base1; + newbon->DivineSaveChance[1] = base2; + } + break; + } + + case SE_SpellEffectResistChance: { + for (int e = 0; e < MAX_RESISTABLE_EFFECTS * 2; e += 2) { + if (newbon->SEResist[e + 1] && (newbon->SEResist[e] == base2) && + (newbon->SEResist[e + 1] < base1)) { + newbon->SEResist[e] = base2; // Spell Effect ID + newbon->SEResist[e + 1] = base1; // Resist Chance + break; + } else if (!newbon->SEResist[e + 1]) { + newbon->SEResist[e] = base2; // Spell Effect ID + newbon->SEResist[e + 1] = base1; // Resist Chance + break; + } + } + break; + } + + case SE_MitigateDamageShield: { + if (base1 < 0) + base1 = base1 * (-1); + + newbon->DSMitigationOffHand += base1; + break; + } + + case SE_FinishingBlow: { + // base1 = chance, base2 = damage + if (newbon->FinishingBlow[1] < base2) { + newbon->FinishingBlow[0] = base1; + newbon->FinishingBlow[1] = base2; + } + break; + } + + case SE_FinishingBlowLvl: { + // base1 = level, base2 = ??? (Set to 200 in AA data, possible proc rate mod?) + if (newbon->FinishingBlowLvl[0] < base1) { + newbon->FinishingBlowLvl[0] = base1; + newbon->FinishingBlowLvl[1] = base2; + } + break; + } + + case SE_StunBashChance: + newbon->StunBashChance += base1; + break; + + case SE_IncreaseChanceMemwipe: + newbon->IncreaseChanceMemwipe += base1; + break; + + case SE_CriticalMend: + newbon->CriticalMend += base1; + break; + + case SE_HealRate: + newbon->HealRate += base1; + break; + + case SE_MeleeLifetap: { + + if ((base1 < 0) && (newbon->MeleeLifetap > base1)) + newbon->MeleeLifetap = base1; + + else if (newbon->MeleeLifetap < base1) + newbon->MeleeLifetap = base1; + break; + } + + case SE_Vampirism: + newbon->Vampirism += base1; + break; + + case SE_FrenziedDevastation: + newbon->FrenziedDevastation += base2; + break; + + case SE_SpellProcChance: + newbon->SpellProcChance += base1; + break; + + case SE_Berserk: + newbon->BerserkSPA = true; + break; + + case SE_Metabolism: + newbon->Metabolism += base1; + break; + + case SE_ImprovedReclaimEnergy: { + if ((base1 < 0) && (newbon->ImprovedReclaimEnergy > base1)) + newbon->ImprovedReclaimEnergy = base1; + + else if (newbon->ImprovedReclaimEnergy < base1) + newbon->ImprovedReclaimEnergy = base1; + break; + } + + case SE_HeadShot: { + if (newbon->HeadShot[1] < base2) { + newbon->HeadShot[0] = base1; + newbon->HeadShot[1] = base2; + } + break; + } + + case SE_HeadShotLevel: { + if (newbon->HSLevel < base1) + newbon->HSLevel = base1; + break; + } + + case SE_Assassinate: { + if (newbon->Assassinate[1] < base2) { + newbon->Assassinate[0] = base1; + newbon->Assassinate[1] = base2; + } + break; + } + + case SE_AssassinateLevel: { + if (newbon->AssassinateLevel < base1) + newbon->AssassinateLevel = base1; + break; + } + + case SE_PetMeleeMitigation: + newbon->PetMeleeMitigation += base1; + break; + + case SE_MeleeVulnerability: + newbon->MeleeVulnerability += base1; + break; + + case SE_FactionModPct: { + if ((base1 < 0) && (newbon->FactionModPct > base1)) + newbon->FactionModPct = base1; + + else if (newbon->FactionModPct < base1) + newbon->FactionModPct = base1; + break; + } + + case SE_IllusionPersistence: + newbon->IllusionPersistence = true; + break; + + case SE_LimitToSkill: { + if (base1 <= HIGHEST_SKILL) + newbon->LimitToSkill[base1] = true; + break; + } + + case SE_SkillProc: { + for (int e = 0; e < MAX_SKILL_PROCS; e++) { + if (newbon->SkillProc[e] && newbon->SkillProc[e] == rank.id) + break; // Do not use the same aa id more than once. + + else if (!newbon->SkillProc[e]) { + newbon->SkillProc[e] = rank.id; + break; + } + } + break; + } + + case SE_SkillProcSuccess: { + + for (int e = 0; e < MAX_SKILL_PROCS; e++) { + if (newbon->SkillProcSuccess[e] && newbon->SkillProcSuccess[e] == rank.id) + break; // Do not use the same spell id more than once. + + else if (!newbon->SkillProcSuccess[e]) { + newbon->SkillProcSuccess[e] = rank.id; + break; + } + } + break; + } + + case SE_MeleeMitigation: + newbon->MeleeMitigationEffect -= base1; + break; + + case SE_ATK: + newbon->ATK += base1; + break; + // to do + case SE_ReduceTradeskillFail: + break; + case SE_PotionBeltSlots: + break; + case SE_BandolierSlots: + break; + case SE_ForageSkill: + break; + case SE_TradeSkillMastery: + break; + case SE_SecondaryForte: + break; + + // not handled here + case SE_HastenedAASkill: + // not handled here but don't want to clutter debug log -- these may need to be verified to ignore + case SE_LimitMaxLevel: + case SE_LimitResist: + case SE_LimitTarget: + case SE_LimitEffect: + case SE_LimitSpellType: + case SE_LimitMinDur: + case SE_LimitInstant: + case SE_LimitMinLevel: + case SE_LimitCastTimeMin: + case SE_LimitCastTimeMax: + case SE_LimitSpell: + case SE_LimitCombatSkills: + case SE_LimitManaMin: + case SE_LimitSpellGroup: + case SE_LimitSpellClass: + case SE_LimitSpellSubclass: + case SE_LimitHPPercent: + case SE_LimitManaPercent: + case SE_LimitEndPercent: + case SE_LimitClass: + case SE_LimitRace: + case SE_LimitCastingSkill: + case SE_LimitUseMin: + case SE_LimitUseType: + break; + + default: + Log.Out(Logs::Detail, Logs::AA, "SPA %d not accounted for in AA %s (%d)", effect, rank.base_ability->name.c_str(), rank.id); + break; + } + + } } void Mob::CalcSpellBonuses(StatBonuses* newbon) diff --git a/zone/bot.cpp b/zone/bot.cpp index 1d8c16f57..5200f3e95 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -7712,14 +7712,14 @@ void Bot::DoSpecialAttackDamage(Mob *who, SkillUseTypes skill, int32 max_damage, CheckNumHitsRemaining(NumHit::OutgoingHitSuccess); //[AA Dragon Punch] value[0] = 100 for 25%, chance value[1] = skill - if(aabonuses.SpecialAttackKBProc[0] && aabonuses.SpecialAttackKBProc[1] == skill){ +/* if(aabonuses.SpecialAttackKBProc[0] && aabonuses.SpecialAttackKBProc[1] == skill){ int kb_chance = 25; kb_chance += kb_chance*(100-aabonuses.SpecialAttackKBProc[0])/100; if (zone->random.Int(0, 99) < kb_chance) SpellFinished(904, who, 10, 0, -1, spells[904].ResistDiff); //who->Stun(100); Kayen: This effect does not stun on live, it only moves the NPC. - } + }*/ if (HasSkillProcs()) TrySkillProc(who, skill, ReuseTime*1000); @@ -9078,7 +9078,7 @@ void Bot::DoBuffTic(const Buffs_Struct &buff, int slot, Mob* caster) { Mob::DoBuffTic(buff, slot, caster); } -bool Bot::CastSpell(uint16 spell_id, uint16 target_id, uint16 slot, int32 cast_time, int32 mana_cost, +bool Bot::CastSpell(uint16 spell_id, uint16 target_id, uint16 slot, int32 cast_time, int32 mana_cost, uint32* oSpellWillFinish, uint32 item_slot, int16 *resist_adjust, uint32 aa_id) { bool Result = false; diff --git a/zone/client.h b/zone/client.h index faa4eeb3a..014747576 100644 --- a/zone/client.h +++ b/zone/client.h @@ -789,7 +789,7 @@ public: bool CheckAAEffect(aaEffectType type); void HandleAAAction(aaID activate); int16 CalcAAFocusEffect(focusType type, uint16 focus_spell, uint16 spell_id); - int16 CalcAAFocus(focusType type, uint32 aa_ID, uint16 spell_id); + int16 CalcAAFocus(focusType type, const AA::Rank &rank, uint16 spell_id); void RefundAA(); int32 GetAAEffectDataBySlot(uint32 aa_ID, uint32 slot_id, bool GetEffect, bool GetBase1, bool GetBase2); int32 GetAAEffectid(uint32 aa_ID, uint32 slot_id) { return GetAAEffectDataBySlot(aa_ID, slot_id, true, false,false); } @@ -1265,7 +1265,7 @@ protected: int CalcRecommendedLevelBonus(uint8 level, uint8 reclevel, int basestat); void CalcEdibleBonuses(StatBonuses* newbon); void CalcAABonuses(StatBonuses* newbon); - void ApplyAABonuses(uint32 aaid, uint32 slots, StatBonuses* newbon); + void ApplyAABonuses(const AA::Rank &rank, StatBonuses* newbon); void ProcessItemCaps(); void MakeBuffFadePacket(uint16 spell_id, int slot_id, bool send_message = true); bool client_data_loaded; diff --git a/zone/client_mods.cpp b/zone/client_mods.cpp index f12c20064..56a22de9a 100644 --- a/zone/client_mods.cpp +++ b/zone/client_mods.cpp @@ -1121,7 +1121,7 @@ int32 Client::CalcMaxMana() switch (GetCasterClass()) { case 'I': case 'W': { - max_mana = (CalcBaseMana() + itembonuses.Mana + spellbonuses.Mana + GroupLeadershipAAManaEnhancement()); + max_mana = (CalcBaseMana() + itembonuses.Mana + spellbonuses.Mana + aabonuses.Mana + GroupLeadershipAAManaEnhancement()); break; } case 'N': { diff --git a/zone/common.h b/zone/common.h index d9e3637a8..e658a352d 100644 --- a/zone/common.h +++ b/zone/common.h @@ -412,7 +412,7 @@ struct StatBonuses { int8 BaseMovementSpeed; // Adjust base run speed, does not stack with other movement bonuses. uint8 IncreaseRunSpeedCap; // Increase max run speed above cap. int32 DoubleSpecialAttack; // Chance to to perform a double special attack (ie flying kick 2x) - int32 SpecialAttackKBProc[2]; // Chance to to do a knockback from special attacks. (0 = chance 1 = Skill) + int32 SkillAttackProc[3]; // [0] chance to proc [2] spell on [1] skill usage uint8 FrontalStunResist; // Chance to resist a frontal stun int32 BindWound; // Increase amount of HP by percent. int32 MaxBindWound; // Increase max amount of HP you can bind wound. diff --git a/zone/mob.cpp b/zone/mob.cpp index 965007fcf..35b0d62d2 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -3510,9 +3510,11 @@ void Mob::TriggerOnCast(uint32 focus_spell, uint32 spell_id, bool aa_trigger) uint32 trigger_spell_id = 0; - if (aa_trigger && IsClient()){ - //focus_spell = aaid - trigger_spell_id = CastToClient()->CalcAAFocus(focusTriggerOnCast, focus_spell, spell_id); + if (aa_trigger && IsClient()) { + // focus_spell = aaid + auto rank = zone->GetAlternateAdvancementRank(focus_spell); + if (rank) + trigger_spell_id = CastToClient()->CalcAAFocus(focusTriggerOnCast, *rank, spell_id); if(IsValidSpell(trigger_spell_id) && GetTarget()) SpellFinished(trigger_spell_id, GetTarget(), 10, 0, -1, spells[trigger_spell_id].ResistDiff); diff --git a/zone/special_attacks.cpp b/zone/special_attacks.cpp index a3557d021..0021350a4 100644 --- a/zone/special_attacks.cpp +++ b/zone/special_attacks.cpp @@ -149,22 +149,19 @@ void Mob::DoSpecialAttackDamage(Mob *who, SkillUseTypes skill, int32 max_damage, } who->AddToHateList(this, hate, 0, false); + if (max_damage > 0 && aabonuses.SkillAttackProc[0] && aabonuses.SkillAttackProc[1] == skill && + IsValidSpell(aabonuses.SkillAttackProc[2])) { + float chance = aabonuses.SkillAttackProc[0] / 1000.0f; + if (zone->random.Roll(chance)) + SpellFinished(aabonuses.SkillAttackProc[2], who, 10, 0, -1, + spells[aabonuses.SkillAttackProc[2]].ResistDiff); + } who->Damage(this, max_damage, SPELL_UNKNOWN, skill, false); //Make sure 'this' has not killed the target and 'this' is not dead (Damage shield ect). if(!GetTarget())return; if (HasDied()) return; - //[AA Dragon Punch] value[0] = 100 for 25%, chance value[1] = skill - if(aabonuses.SpecialAttackKBProc[0] && aabonuses.SpecialAttackKBProc[1] == skill){ - int kb_chance = 25; - kb_chance += kb_chance*(100-aabonuses.SpecialAttackKBProc[0])/100; - - if (zone->random.Roll(kb_chance)) - SpellFinished(904, who, 10, 0, -1, spells[904].ResistDiff); - //who->Stun(100); Kayen: This effect does not stun on live, it only moves the NPC. - } - if (HasSkillProcs()) TrySkillProc(who, skill, ReuseTime*1000); @@ -2442,19 +2439,18 @@ void Mob::DoMeleeSkillAttackDmg(Mob* other, uint16 weapon_damage, SkillUseTypes } other->AddToHateList(this, hate, 0, false); + if (damage > 0 && aabonuses.SkillAttackProc[0] && aabonuses.SkillAttackProc[1] == skillinuse && + IsValidSpell(aabonuses.SkillAttackProc[2])) { + float chance = aabonuses.SkillAttackProc[0] / 1000.0f; + if (zone->random.Roll(chance)) + SpellFinished(aabonuses.SkillAttackProc[2], other, 10, 0, -1, + spells[aabonuses.SkillAttackProc[2]].ResistDiff); + } other->Damage(this, damage, SPELL_UNKNOWN, skillinuse); if (HasDied()) return; - if(aabonuses.SpecialAttackKBProc[0] && aabonuses.SpecialAttackKBProc[1] == skillinuse){ - int kb_chance = 25; - kb_chance += kb_chance*(100-aabonuses.SpecialAttackKBProc[0])/100; - - if (zone->random.Roll(kb_chance)) - SpellFinished(904, other, 10, 0, -1, spells[904].ResistDiff); - } - if (CanSkillProc && HasSkillProcs()) TrySkillProc(other, skillinuse, ReuseTime); diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index 8ac60c125..0bb7b78b9 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -4186,7 +4186,7 @@ int32 Client::GetAAEffectDataBySlot(uint32 aa_ID, uint32 slot_id, bool GetEffect } -int16 Client::CalcAAFocus(focusType type, uint32 aa_ID, uint16 spell_id) +int16 Client::CalcAAFocus(focusType type, const AA::Rank &rank, uint16 spell_id) { const SPDat_Spell_Struct &spell = spells[spell_id]; @@ -4200,8 +4200,9 @@ int16 Client::CalcAAFocus(focusType type, uint32 aa_ID, uint16 spell_id) uint32 slot = 0; bool LimitFailure = false; - bool LimitInclude[MaxLimitInclude] = { false }; - /* Certain limits require only one of several Include conditions to be true. Ie. Add damage to fire OR ice spells. + bool LimitInclude[MaxLimitInclude] = {false}; + /* Certain limits require only one of several Include conditions to be true. Ie. Add damage to fire OR ice + spells. 0/1 SE_LimitResist 2/3 SE_LimitSpell 4/5 SE_LimitEffect @@ -4214,452 +4215,431 @@ int16 Client::CalcAAFocus(focusType type, uint32 aa_ID, uint16 spell_id) */ int FocusCount = 0; - //old AA - //std::map >::const_iterator find_iter = aa_effects.find(aa_ID); - //if(find_iter == aa_effects.end()) - //{ - // return 0; - //} - // - //for (std::map::const_iterator iter = aa_effects[aa_ID].begin(); iter != aa_effects[aa_ID].end(); ++iter) - //{ - // effect = iter->second.skill_id; - // base1 = iter->second.base1; - // base2 = iter->second.base2; - // slot = iter->second.slot; - // - // /* - // AA Foci's can contain multiple focus effects within the same AA. - // To handle this we will not automatically return zero if a limit is found. - // Instead if limit is found and multiple focus effects, we will reset the limit check - // when the next valid focus effect is found. - // */ - // - // if (IsFocusEffect(0, 0, true,effect) || (effect == SE_TriggerOnCast)){ - // FocusCount++; - // //If limit found on prior check next, else end loop. - // if (FocusCount > 1){ - // - // for(int e = 0; e < MaxLimitInclude; e+=2) { - // if (LimitInclude[e] && !LimitInclude[e+1]) - // LimitFailure = true; - // } - // - // if (LimitFailure){ - // value = 0; - // LimitFailure = false; - // - // for(int e = 0; e < MaxLimitInclude; e++) { - // LimitInclude[e] = false; //Reset array - // } - // } - // - // else{ - // break; - // } - // } - // } - // - // - // switch (effect) - // { - // case SE_Blank: - // break; - // - // //Handle Focus Limits - // - // case SE_LimitResist: - // if(base1 < 0){ - // if(spell.resisttype == -base1) //Exclude - // LimitFailure = true; - // } - // else { - // LimitInclude[0] = true; - // if (spell.resisttype == base1) //Include - // LimitInclude[1] = true; - // } - // break; - // - // case SE_LimitInstant: - // if(base1 == 1 && spell.buffduration) //Fail if not instant - // LimitFailure = true; - // if(base1 == 0 && (spell.buffduration == 0)) //Fail if instant - // LimitFailure = true; - // - // break; - // - // case SE_LimitMaxLevel: - // spell_level = spell.classes[(GetClass()%16) - 1]; - // lvldiff = spell_level - base1; - // //every level over cap reduces the effect by base2 percent unless from a clicky when ItemCastsUseFocus is true - // if(lvldiff > 0 && (spell_level <= RuleI(Character, MaxLevel) || RuleB(Character, ItemCastsUseFocus) == false)) { - // if(base2 > 0){ - // lvlModifier -= base2*lvldiff; - // if(lvlModifier < 1) - // LimitFailure = true; - // } - // else - // LimitFailure = true; - // } - // break; - // - // case SE_LimitMinLevel: - // if((spell.classes[(GetClass()%16) - 1]) < base1) - // LimitFailure = true; - // break; - // - // case SE_LimitCastTimeMin: - // if (static_cast(spell.cast_time) < base1) - // LimitFailure = true; - // break; - // - // case SE_LimitCastTimeMax: - // if (static_cast(spell.cast_time) > base1) - // LimitFailure = true; - // break; - // - // case SE_LimitSpell: - // if(base1 < 0) { //Exclude - // if (spell_id == -base1) - // LimitFailure = true; - // } - // else { - // LimitInclude[2] = true; - // if (spell_id == base1) //Include - // LimitInclude[3] = true; - // } - // break; - // - // case SE_LimitMinDur: - // if (base1 > CalcBuffDuration_formula(GetLevel(), spell.buffdurationformula, spell.buffduration)) - // LimitFailure = true; - // - // break; - // - // case SE_LimitEffect: - // if(base1 < 0){ - // if(IsEffectInSpell(spell_id,-base1)) //Exclude - // LimitFailure = true; - // } - // else{ - // LimitInclude[4] = true; - // if(IsEffectInSpell(spell_id,base1)) //Include - // LimitInclude[5] = true; - // } - // break; - // - // case SE_LimitSpellType: - // switch(base1) - // { - // case 0: - // if (!IsDetrimentalSpell(spell_id)) - // LimitFailure = true; - // break; - // case 1: - // if (!IsBeneficialSpell(spell_id)) - // LimitFailure = true; - // break; - // } - // break; - // - // case SE_LimitManaMin: - // if(spell.mana < base1) - // LimitFailure = true; - // break; - // - // case SE_LimitTarget: - // if (base1 < 0) { - // if (-base1 == spell.targettype) //Exclude - // LimitFailure = true; - // } - // else { - // LimitInclude[6] = true; - // if (base1 == spell.targettype) //Include - // LimitInclude[7] = true; - // } - // break; - // - // case SE_LimitCombatSkills: - // if (base1 == 0 && (IsCombatSkill(spell_id) || IsCombatProc(spell_id))) //Exclude Discs / Procs - // LimitFailure = true; - // else if (base1 == 1 && (!IsCombatSkill(spell_id) || !IsCombatProc(spell_id))) //Exclude Spells - // LimitFailure = true; - // - // break; - // - // case SE_LimitSpellGroup: - // if(base1 < 0) { - // if (-base1 == spell.spellgroup) //Exclude - // LimitFailure = true; - // } - // else { - // LimitInclude[8] = true; - // if (base1 == spell.spellgroup) //Include - // LimitInclude[9] = true; - // } - // break; - // - // case SE_LimitCastingSkill: - // if(base1 < 0) { - // if(-base1 == spell.skill) - // LimitFailure = true; - // } - // else { - // LimitInclude[10] = true; - // if(base1 == spell.skill) - // LimitInclude[11] = true; - // } - // break; - // - // case SE_LimitSpellClass: - // if(base1 < 0) { //Exclude - // if (CheckSpellCategory(spell_id, base1, SE_LimitSpellClass)) - // return(0); - // } - // else { - // LimitInclude[12] = true; - // if (CheckSpellCategory(spell_id, base1, SE_LimitSpellClass)) //Include - // LimitInclude[13] = true; - // } - // break; - // - // case SE_LimitSpellSubclass: - // if(base1 < 0) { //Exclude - // if (CheckSpellCategory(spell_id, base1, SE_LimitSpellSubclass)) - // return(0); - // } - // else { - // LimitInclude[14] = true; - // if (CheckSpellCategory(spell_id, base1, SE_LimitSpellSubclass)) //Include - // LimitInclude[15] = true; - // } - // break; - // - // case SE_LimitClass: - // //Do not use this limit more then once per spell. If multiple class, treat value like items would. - // if (!PassLimitClass(base1, GetClass())) - // LimitFailure = true; - // break; - // - // case SE_LimitRace: - // if (base1 != GetRace()) - // LimitFailure = true; - // break; - // - // case SE_LimitUseMin: - // if (base1 > spell.numhits) - // LimitFailure = true; - // break; - // - // case SE_LimitUseType: - // if (base1 != spell.numhitstype) - // LimitFailure = true; - // break; - // - // //Handle Focus Effects - // case SE_ImprovedDamage: - // if (type == focusImprovedDamage && base1 > value) - // value = base1; - // break; - // - // case SE_ImprovedHeal: - // if (type == focusImprovedHeal && base1 > value) - // value = base1; - // break; - // - // case SE_ReduceManaCost: - // if (type == focusManaCost) - // value = base1; - // break; - // - // case SE_IncreaseSpellHaste: - // if (type == focusSpellHaste && base1 > value) - // value = base1; - // break; - // - // case SE_IncreaseSpellDuration: - // if (type == focusSpellDuration && base1 > value) - // value = base1; - // break; - // - // case SE_SpellDurationIncByTic: - // if (type == focusSpellDurByTic && base1 > value) - // value = base1; - // break; - // - // case SE_SwarmPetDuration: - // if (type == focusSwarmPetDuration && base1 > value) - // value = base1; - // break; - // - // case SE_IncreaseRange: - // if (type == focusRange && base1 > value) - // value = base1; - // break; - // - // case SE_ReduceReagentCost: - // if (type == focusReagentCost && base1 > value) - // value = base1; - // break; - // - // case SE_PetPowerIncrease: - // if (type == focusPetPower && base1 > value) - // value = base1; - // break; - // - // case SE_SpellResistReduction: - // if (type == focusResistRate && base1 > value) - // value = base1; - // break; - // - // case SE_SpellHateMod: - // if (type == focusSpellHateMod ) { - // if(value != 0) { - // if(value > 0){ - // if(base1 > value) - // value = base1; - // } - // else{ - // if(base1 < value) - // value = base1; - // } - // } - // else - // value = base1; - // } - // break; - // - // case SE_ReduceReuseTimer: - // if(type == focusReduceRecastTime) - // value = base1 / 1000; - // break; - // - // case SE_TriggerOnCast: - // if(type == focusTriggerOnCast){ - // if(zone->random.Roll(base1)) { - // value = base2; - // } else { - // value = 0; - // LimitFailure = true; - // } - // break; - // } - // - // case SE_FcSpellVulnerability: - // if(type == focusSpellVulnerability) - // value = base1; - // break; - // - // case SE_BlockNextSpellFocus: - // if(type == focusBlockNextSpell){ - // if(zone->random.Roll(base1)) - // value = 1; - // } - // break; - // - // case SE_FcTwincast: - // if(type == focusTwincast) - // value = base1; - // break; - // - // //Note if using these as AA, make sure this is first focus used. - // case SE_SympatheticProc: - // if(type == focusSympatheticProc) - // value = base2; - // break; - // - // case SE_FcDamageAmt: - // if(type == focusFcDamageAmt) - // value = base1; - // break; - // - // case SE_FcDamageAmtCrit: - // if(type == focusFcDamageAmtCrit) - // value = base1; - // break; - // - // case SE_FcDamageAmtIncoming: - // if(type == focusFcDamageAmtIncoming) - // value = base1; - // break; - // - // case SE_FcHealAmtIncoming: - // if(type == focusFcHealAmtIncoming) - // value = base1; - // break; - // - // case SE_FcHealPctCritIncoming: - // if (type == focusFcHealPctCritIncoming) - // value = base1; - // break; - // - // case SE_FcHealAmtCrit: - // if(type == focusFcHealAmtCrit) - // value = base1; - // break; - // - // case SE_FcHealAmt: - // if(type == focusFcHealAmt) - // value = base1; - // break; - // - // case SE_FcHealPctIncoming: - // if(type == focusFcHealPctIncoming) - // value = base1; - // break; - // - // case SE_FcBaseEffects: - // if (type == focusFcBaseEffects) - // value = base1; - // break; - // - // case SE_FcDamagePctCrit: - // if(type == focusFcDamagePctCrit) - // value = base1; - // break; - // - // case SE_FcIncreaseNumHits: - // if(type == focusIncreaseNumHits) - // value = base1; - // break; - // - // case SE_FcLimitUse: - // if(type == focusFcLimitUse) - // value = base1; - // break; - // - // case SE_FcMute: - // if(type == focusFcMute) - // value = base1; - // break; - // - // case SE_FcStunTimeMod: - // if(type == focusFcStunTimeMod) - // value = base1; - // break; - // - // } - //} + for (const auto &e : rank.effects) { + effect = e.effect_id; + base1 = e.base1; + base2 = e.base2; + slot = e.slot; - for(int e = 0; e < MaxLimitInclude; e+=2) { - if (LimitInclude[e] && !LimitInclude[e+1]) + /* + AA Foci's can contain multiple focus effects within the same AA. + To handle this we will not automatically return zero if a limit is found. + Instead if limit is found and multiple focus effects, we will reset the limit check + when the next valid focus effect is found. + */ + + if (IsFocusEffect(0, 0, true, effect) || (effect == SE_TriggerOnCast)) { + FocusCount++; + // If limit found on prior check next, else end loop. + if (FocusCount > 1) { + + for (int e = 0; e < MaxLimitInclude; e += 2) { + if (LimitInclude[e] && !LimitInclude[e + 1]) + LimitFailure = true; + } + + if (LimitFailure) { + value = 0; + LimitFailure = false; + + for (int e = 0; e < MaxLimitInclude; e++) { + LimitInclude[e] = false; // Reset array + } + } + + else { + break; + } + } + } + + switch (effect) { + case SE_Blank: + break; + + // Handle Focus Limits + + case SE_LimitResist: + if (base1 < 0) { + if (spell.resisttype == -base1) // Exclude + LimitFailure = true; + } else { + LimitInclude[0] = true; + if (spell.resisttype == base1) // Include + LimitInclude[1] = true; + } + break; + + case SE_LimitInstant: + if (base1 == 1 && spell.buffduration) // Fail if not instant + LimitFailure = true; + if (base1 == 0 && (spell.buffduration == 0)) // Fail if instant + LimitFailure = true; + + break; + + case SE_LimitMaxLevel: + spell_level = spell.classes[(GetClass() % 16) - 1]; + lvldiff = spell_level - base1; + // every level over cap reduces the effect by base2 percent unless from a clicky when + // ItemCastsUseFocus is true + if (lvldiff > 0 && (spell_level <= RuleI(Character, MaxLevel) || + RuleB(Character, ItemCastsUseFocus) == false)) { + if (base2 > 0) { + lvlModifier -= base2 * lvldiff; + if (lvlModifier < 1) + LimitFailure = true; + } else + LimitFailure = true; + } + break; + + case SE_LimitMinLevel: + if ((spell.classes[(GetClass() % 16) - 1]) < base1) + LimitFailure = true; + break; + + case SE_LimitCastTimeMin: + if (static_cast(spell.cast_time) < base1) + LimitFailure = true; + break; + + case SE_LimitCastTimeMax: + if (static_cast(spell.cast_time) > base1) + LimitFailure = true; + break; + + case SE_LimitSpell: + if (base1 < 0) { // Exclude + if (spell_id == -base1) + LimitFailure = true; + } else { + LimitInclude[2] = true; + if (spell_id == base1) // Include + LimitInclude[3] = true; + } + break; + + case SE_LimitMinDur: + if (base1 > CalcBuffDuration_formula(GetLevel(), spell.buffdurationformula, spell.buffduration)) + LimitFailure = true; + + break; + + case SE_LimitEffect: + if (base1 < 0) { + if (IsEffectInSpell(spell_id, -base1)) // Exclude + LimitFailure = true; + } else { + LimitInclude[4] = true; + if (IsEffectInSpell(spell_id, base1)) // Include + LimitInclude[5] = true; + } + break; + + case SE_LimitSpellType: + switch (base1) { + case 0: + if (!IsDetrimentalSpell(spell_id)) + LimitFailure = true; + break; + case 1: + if (!IsBeneficialSpell(spell_id)) + LimitFailure = true; + break; + } + break; + + case SE_LimitManaMin: + if (spell.mana < base1) + LimitFailure = true; + break; + + case SE_LimitTarget: + if (base1 < 0) { + if (-base1 == spell.targettype) // Exclude + LimitFailure = true; + } else { + LimitInclude[6] = true; + if (base1 == spell.targettype) // Include + LimitInclude[7] = true; + } + break; + + case SE_LimitCombatSkills: + if (base1 == 0 && (IsCombatSkill(spell_id) || IsCombatProc(spell_id))) // Exclude Discs / Procs + LimitFailure = true; + else if (base1 == 1 && (!IsCombatSkill(spell_id) || !IsCombatProc(spell_id))) // Exclude Spells + LimitFailure = true; + + break; + + case SE_LimitSpellGroup: + if (base1 < 0) { + if (-base1 == spell.spellgroup) // Exclude + LimitFailure = true; + } else { + LimitInclude[8] = true; + if (base1 == spell.spellgroup) // Include + LimitInclude[9] = true; + } + break; + + case SE_LimitCastingSkill: + if (base1 < 0) { + if (-base1 == spell.skill) + LimitFailure = true; + } else { + LimitInclude[10] = true; + if (base1 == spell.skill) + LimitInclude[11] = true; + } + break; + + case SE_LimitSpellClass: + if (base1 < 0) { // Exclude + if (CheckSpellCategory(spell_id, base1, SE_LimitSpellClass)) + return (0); + } else { + LimitInclude[12] = true; + if (CheckSpellCategory(spell_id, base1, SE_LimitSpellClass)) // Include + LimitInclude[13] = true; + } + break; + + case SE_LimitSpellSubclass: + if (base1 < 0) { // Exclude + if (CheckSpellCategory(spell_id, base1, SE_LimitSpellSubclass)) + return (0); + } else { + LimitInclude[14] = true; + if (CheckSpellCategory(spell_id, base1, SE_LimitSpellSubclass)) // Include + LimitInclude[15] = true; + } + break; + + case SE_LimitClass: + // Do not use this limit more then once per spell. If multiple class, treat value like items + // would. + if (!PassLimitClass(base1, GetClass())) + LimitFailure = true; + break; + + case SE_LimitRace: + if (base1 != GetRace()) + LimitFailure = true; + break; + + case SE_LimitUseMin: + if (base1 > spell.numhits) + LimitFailure = true; + break; + + case SE_LimitUseType: + if (base1 != spell.numhitstype) + LimitFailure = true; + break; + + // Handle Focus Effects + case SE_ImprovedDamage: + if (type == focusImprovedDamage && base1 > value) + value = base1; + break; + + case SE_ImprovedHeal: + if (type == focusImprovedHeal && base1 > value) + value = base1; + break; + + case SE_ReduceManaCost: + if (type == focusManaCost) + value = base1; + break; + + case SE_IncreaseSpellHaste: + if (type == focusSpellHaste && base1 > value) + value = base1; + break; + + case SE_IncreaseSpellDuration: + if (type == focusSpellDuration && base1 > value) + value = base1; + break; + + case SE_SpellDurationIncByTic: + if (type == focusSpellDurByTic && base1 > value) + value = base1; + break; + + case SE_SwarmPetDuration: + if (type == focusSwarmPetDuration && base1 > value) + value = base1; + break; + + case SE_IncreaseRange: + if (type == focusRange && base1 > value) + value = base1; + break; + + case SE_ReduceReagentCost: + if (type == focusReagentCost && base1 > value) + value = base1; + break; + + case SE_PetPowerIncrease: + if (type == focusPetPower && base1 > value) + value = base1; + break; + + case SE_SpellResistReduction: + if (type == focusResistRate && base1 > value) + value = base1; + break; + + case SE_SpellHateMod: + if (type == focusSpellHateMod) { + if (value != 0) { + if (value > 0) { + if (base1 > value) + value = base1; + } else { + if (base1 < value) + value = base1; + } + } else + value = base1; + } + break; + + case SE_ReduceReuseTimer: + if (type == focusReduceRecastTime) + value = base1 / 1000; + break; + + case SE_TriggerOnCast: + if (type == focusTriggerOnCast) { + if (zone->random.Roll(base1)) { + value = base2; + } else { + value = 0; + LimitFailure = true; + } + break; + } + + case SE_FcSpellVulnerability: + if (type == focusSpellVulnerability) + value = base1; + break; + + case SE_BlockNextSpellFocus: + if (type == focusBlockNextSpell) { + if (zone->random.Roll(base1)) + value = 1; + } + break; + + case SE_FcTwincast: + if (type == focusTwincast) + value = base1; + break; + + // Note if using these as AA, make sure this is first focus used. + case SE_SympatheticProc: + if (type == focusSympatheticProc) + value = base2; + break; + + case SE_FcDamageAmt: + if (type == focusFcDamageAmt) + value = base1; + break; + + case SE_FcDamageAmtCrit: + if (type == focusFcDamageAmtCrit) + value = base1; + break; + + case SE_FcDamageAmtIncoming: + if (type == focusFcDamageAmtIncoming) + value = base1; + break; + + case SE_FcHealAmtIncoming: + if (type == focusFcHealAmtIncoming) + value = base1; + break; + + case SE_FcHealPctCritIncoming: + if (type == focusFcHealPctCritIncoming) + value = base1; + break; + + case SE_FcHealAmtCrit: + if (type == focusFcHealAmtCrit) + value = base1; + break; + + case SE_FcHealAmt: + if (type == focusFcHealAmt) + value = base1; + break; + + case SE_FcHealPctIncoming: + if (type == focusFcHealPctIncoming) + value = base1; + break; + + case SE_FcBaseEffects: + if (type == focusFcBaseEffects) + value = base1; + break; + + case SE_FcDamagePctCrit: + if (type == focusFcDamagePctCrit) + value = base1; + break; + + case SE_FcIncreaseNumHits: + if (type == focusIncreaseNumHits) + value = base1; + break; + + case SE_FcLimitUse: + if (type == focusFcLimitUse) + value = base1; + break; + + case SE_FcMute: + if (type == focusFcMute) + value = base1; + break; + + case SE_FcStunTimeMod: + if (type == focusFcStunTimeMod) + value = base1; + break; + } + } + + for (int e = 0; e < MaxLimitInclude; e += 2) { + if (LimitInclude[e] && !LimitInclude[e + 1]) return 0; } if (LimitFailure) return 0; - return(value*lvlModifier/100); + return (value * lvlModifier / 100); } //given an item/spell's focus ID and the spell being cast, determine the focus ammount, if any //assumes that spell_id is not a bard spell and that both ids are valid spell ids -int16 Mob::CalcFocusEffect(focusType type, uint16 focus_id, uint16 spell_id, bool best_focus) { - - if(!IsValidSpell(focus_id) || !IsValidSpell(spell_id)) +int16 Mob::CalcFocusEffect(focusType type, uint16 focus_id, uint16 spell_id, bool best_focus) +{ + if (!IsValidSpell(focus_id) || !IsValidSpell(spell_id)) return 0; - const SPDat_Spell_Struct &focus_spell = spells[focus_id]; const SPDat_Spell_Struct &spell = spells[spell_id]; @@ -4669,8 +4649,9 @@ int16 Mob::CalcFocusEffect(focusType type, uint16 focus_id, uint16 spell_id, boo int lvldiff = 0; uint32 Caston_spell_id = 0; - bool LimitInclude[MaxLimitInclude] = { false }; - /* Certain limits require only one of several Include conditions to be true. Ie. Add damage to fire OR ice spells. + bool LimitInclude[MaxLimitInclude] = {false}; + /* Certain limits require only one of several Include conditions to be true. Ie. Add damage to fire OR ice + spells. 0/1 SE_LimitResist 2/3 SE_LimitSpell 4/5 SE_LimitEffect @@ -4690,21 +4671,20 @@ int16 Mob::CalcFocusEffect(focusType type, uint16 focus_id, uint16 spell_id, boo break; case SE_LimitResist: - if(focus_spell.base[i] < 0){ - if (spell.resisttype == -focus_spell.base[i]) //Exclude + if (focus_spell.base[i] < 0) { + if (spell.resisttype == -focus_spell.base[i]) // Exclude return 0; - } - else { + } else { LimitInclude[0] = true; - if (spell.resisttype == focus_spell.base[i]) //Include + if (spell.resisttype == focus_spell.base[i]) // Include LimitInclude[1] = true; } break; case SE_LimitInstant: - if(focus_spell.base[i] == 1 && spell.buffduration) //Fail if not instant + if (focus_spell.base[i] == 1 && spell.buffduration) // Fail if not instant return 0; - if(focus_spell.base[i] == 0 && (spell.buffduration == 0)) //Fail if instant + if (focus_spell.base[i] == 0 && (spell.buffduration == 0)) // Fail if instant return 0; break; @@ -4712,16 +4692,17 @@ int16 Mob::CalcFocusEffect(focusType type, uint16 focus_id, uint16 spell_id, boo case SE_LimitMaxLevel: if (IsNPC()) break; - spell_level = spell.classes[(GetClass()%16) - 1]; + spell_level = spell.classes[(GetClass() % 16) - 1]; lvldiff = spell_level - focus_spell.base[i]; - //every level over cap reduces the effect by focus_spell.base2[i] percent unless from a clicky when ItemCastsUseFocus is true - if(lvldiff > 0 && (spell_level <= RuleI(Character, MaxLevel) || RuleB(Character, ItemCastsUseFocus) == false)){ - if(focus_spell.base2[i] > 0){ - lvlModifier -= focus_spell.base2[i]*lvldiff; - if(lvlModifier < 1) + // every level over cap reduces the effect by focus_spell.base2[i] percent unless from a clicky + // when ItemCastsUseFocus is true + if (lvldiff > 0 && (spell_level <= RuleI(Character, MaxLevel) || + RuleB(Character, ItemCastsUseFocus) == false)) { + if (focus_spell.base2[i] > 0) { + lvlModifier -= focus_spell.base2[i] * lvldiff; + if (lvlModifier < 1) return 0; - } - else + } else return 0; } break; @@ -4729,116 +4710,115 @@ int16 Mob::CalcFocusEffect(focusType type, uint16 focus_id, uint16 spell_id, boo case SE_LimitMinLevel: if (IsNPC()) break; - if (spell.classes[(GetClass()%16) - 1] < focus_spell.base[i]) - return(0); + if (spell.classes[(GetClass() % 16) - 1] < focus_spell.base[i]) + return (0); break; case SE_LimitCastTimeMin: if (spells[spell_id].cast_time < (uint16)focus_spell.base[i]) - return(0); + return (0); break; case SE_LimitCastTimeMax: if (spells[spell_id].cast_time > (uint16)focus_spell.base[i]) - return(0); + return (0); break; case SE_LimitSpell: - if(focus_spell.base[i] < 0) { //Exclude + if (focus_spell.base[i] < 0) { // Exclude if (spell_id == -focus_spell.base[i]) - return(0); - } - else { + return (0); + } else { LimitInclude[2] = true; - if (spell_id == focus_spell.base[i]) //Include + if (spell_id == focus_spell.base[i]) // Include LimitInclude[3] = true; } break; case SE_LimitMinDur: - if (focus_spell.base[i] > CalcBuffDuration_formula(GetLevel(), spell.buffdurationformula, spell.buffduration)) - return(0); + if (focus_spell.base[i] > + CalcBuffDuration_formula(GetLevel(), spell.buffdurationformula, spell.buffduration)) + return (0); break; case SE_LimitEffect: - if(focus_spell.base[i] < 0){ - if(IsEffectInSpell(spell_id,-focus_spell.base[i])) //Exclude + if (focus_spell.base[i] < 0) { + if (IsEffectInSpell(spell_id, -focus_spell.base[i])) // Exclude return 0; - } - else{ + } else { LimitInclude[4] = true; - if(IsEffectInSpell(spell_id,focus_spell.base[i])) //Include + if (IsEffectInSpell(spell_id, focus_spell.base[i])) // Include LimitInclude[5] = true; } break; - case SE_LimitSpellType: - switch( focus_spell.base[i]){ - case 0: - if (!IsDetrimentalSpell(spell_id)) - return 0; - break; - case 1: - if (!IsBeneficialSpell(spell_id)) - return 0; - break; - default: - Log.Out(Logs::General, Logs::Normal, "CalcFocusEffect: unknown limit spelltype %d", focus_spell.base[i]); + switch (focus_spell.base[i]) { + case 0: + if (!IsDetrimentalSpell(spell_id)) + return 0; + break; + case 1: + if (!IsBeneficialSpell(spell_id)) + return 0; + break; + default: + Log.Out(Logs::General, Logs::Normal, "CalcFocusEffect: unknown limit spelltype %d", + focus_spell.base[i]); } break; case SE_LimitManaMin: - if(spell.mana < focus_spell.base[i]) + if (spell.mana < focus_spell.base[i]) return 0; break; case SE_LimitTarget: if (focus_spell.base[i] < 0) { - if (-focus_spell.base[i] == spell.targettype) //Exclude + if (-focus_spell.base[i] == spell.targettype) // Exclude return 0; - } - else { + } else { LimitInclude[6] = true; - if (focus_spell.base[i] == spell.targettype) //Include + if (focus_spell.base[i] == spell.targettype) // Include LimitInclude[7] = true; } break; case SE_LimitCombatSkills: - if (focus_spell.base[i] == 0 && (IsCombatSkill(spell_id) || IsCombatProc(spell_id))) //Exclude Discs / Procs + if (focus_spell.base[i] == 0 && + (IsCombatSkill(spell_id) || IsCombatProc(spell_id))) // Exclude Discs / Procs return 0; - else if (focus_spell.base[i] == 1 && (!IsCombatSkill(spell_id) || !IsCombatProc(spell_id))) //Exclude Spells + else if (focus_spell.base[i] == 1 && + (!IsCombatSkill(spell_id) || !IsCombatProc(spell_id))) // Exclude Spells return 0; break; case SE_LimitSpellGroup: - if(focus_spell.base[i] < 0) { - if (-focus_spell.base[i] == spell.spellgroup) //Exclude + if (focus_spell.base[i] < 0) { + if (-focus_spell.base[i] == spell.spellgroup) // Exclude return 0; - } - else { + } else { LimitInclude[8] = true; - if (focus_spell.base[i] == spell.spellgroup) //Include + if (focus_spell.base[i] == spell.spellgroup) // Include LimitInclude[9] = true; } break; case SE_LimitCastingSkill: - if(focus_spell.base[i] < 0) { - if(-focus_spell.base[i] == spell.skill) + if (focus_spell.base[i] < 0) { + if (-focus_spell.base[i] == spell.skill) return 0; - } - else { + } else { LimitInclude[10] = true; - if(focus_spell.base[i] == spell.skill) + if (focus_spell.base[i] == spell.skill) LimitInclude[11] = true; } break; case SE_LimitClass: - //Do not use this limit more then once per spell. If multiple class, treat value like items would. + // Do not use this limit more then once per spell. If multiple class, treat value like items + // would. if (!PassLimitClass(focus_spell.base[i], GetClass())) return 0; break; @@ -4864,40 +4844,38 @@ int16 Mob::CalcFocusEffect(focusType type, uint16 focus_id, uint16 spell_id, boo break; case SE_LimitSpellClass: - if(focus_spell.base[i] < 0) { //Exclude + if (focus_spell.base[i] < 0) { // Exclude if (CheckSpellCategory(spell_id, focus_spell.base[i], SE_LimitSpellClass)) - return(0); - } - else { + return (0); + } else { LimitInclude[12] = true; - if (CheckSpellCategory(spell_id, focus_spell.base[i], SE_LimitSpellClass)) //Include + if (CheckSpellCategory(spell_id, focus_spell.base[i], SE_LimitSpellClass)) // Include LimitInclude[13] = true; } break; case SE_LimitSpellSubclass: - if(focus_spell.base[i] < 0) { //Exclude + if (focus_spell.base[i] < 0) { // Exclude if (CheckSpellCategory(spell_id, focus_spell.base[i], SE_LimitSpellSubclass)) - return(0); - } - else { + return (0); + } else { LimitInclude[14] = true; - if (CheckSpellCategory(spell_id, focus_spell.base[i], SE_LimitSpellSubclass)) //Include + if (CheckSpellCategory(spell_id, focus_spell.base[i], SE_LimitSpellSubclass)) // Include LimitInclude[15] = true; } break; - - //handle effects + // handle effects case SE_ImprovedDamage: if (type == focusImprovedDamage) { // This is used to determine which focus should be used for the random calculation - if(best_focus) { + if (best_focus) { // If the spell contains a value in the base2 field then that is the max value if (focus_spell.base2[i] != 0) { value = focus_spell.base2[i]; } - // If the spell does not contain a base2 value, then its a straight non random value + // If the spell does not contain a base2 value, then its a straight non random + // value else { value = focus_spell.base[i]; } @@ -4905,8 +4883,7 @@ int16 Mob::CalcFocusEffect(focusType type, uint16 focus_id, uint16 spell_id, boo // Actual focus calculation starts here else if (focus_spell.base2[i] == 0 || focus_spell.base[i] == focus_spell.base2[i]) { value = focus_spell.base[i]; - } - else { + } else { value = zone->random.Int(focus_spell.base[i], focus_spell.base2[i]); } } @@ -4914,18 +4891,15 @@ int16 Mob::CalcFocusEffect(focusType type, uint16 focus_id, uint16 spell_id, boo case SE_ImprovedHeal: if (type == focusImprovedHeal) { - if(best_focus) { + if (best_focus) { if (focus_spell.base2[i] != 0) { value = focus_spell.base2[i]; - } - else { + } else { value = focus_spell.base[i]; } - } - else if (focus_spell.base2[i] == 0 || focus_spell.base[i] == focus_spell.base2[i]) { + } else if (focus_spell.base2[i] == 0 || focus_spell.base[i] == focus_spell.base2[i]) { value = focus_spell.base[i]; - } - else { + } else { value = zone->random.Int(focus_spell.base[i], focus_spell.base2[i]); } } @@ -4933,18 +4907,15 @@ int16 Mob::CalcFocusEffect(focusType type, uint16 focus_id, uint16 spell_id, boo case SE_ReduceManaCost: if (type == focusManaCost) { - if(best_focus) { + if (best_focus) { if (focus_spell.base2[i] != 0) { value = focus_spell.base2[i]; - } - else { + } else { value = focus_spell.base[i]; } - } - else if (focus_spell.base2[i] == 0 || focus_spell.base[i] == focus_spell.base2[i]) { + } else if (focus_spell.base2[i] == 0 || focus_spell.base[i] == focus_spell.base2[i]) { value = focus_spell.base[i]; - } - else { + } else { value = zone->random.Int(focus_spell.base[i], focus_spell.base2[i]); } } @@ -4991,30 +4962,28 @@ int16 Mob::CalcFocusEffect(focusType type, uint16 focus_id, uint16 spell_id, boo break; case SE_SpellHateMod: - if (type == focusSpellHateMod){ - if(value != 0){ - if(value > 0){ - if(focus_spell.base[i] > value) + if (type == focusSpellHateMod) { + if (value != 0) { + if (value > 0) { + if (focus_spell.base[i] > value) + value = focus_spell.base[i]; + } else { + if (focus_spell.base[i] < value) value = focus_spell.base[i]; } - else{ - if(focus_spell.base[i] < value) - value = focus_spell.base[i]; - } - } - else + } else value = focus_spell.base[i]; } break; case SE_ReduceReuseTimer: - if(type == focusReduceRecastTime) + if (type == focusReduceRecastTime) value = focus_spell.base[i] / 1000; break; case SE_TriggerOnCast: - if(type == focusTriggerOnCast){ - if(zone->random.Roll(focus_spell.base[i])) + if (type == focusTriggerOnCast) { + if (zone->random.Roll(focus_spell.base[i])) value = focus_spell.base2[i]; else value = 0; @@ -5022,50 +4991,50 @@ int16 Mob::CalcFocusEffect(focusType type, uint16 focus_id, uint16 spell_id, boo break; case SE_BlockNextSpellFocus: - if(type == focusBlockNextSpell){ - if(zone->random.Roll(focus_spell.base[i])) + if (type == focusBlockNextSpell) { + if (zone->random.Roll(focus_spell.base[i])) value = 1; } break; case SE_SympatheticProc: - if(type == focusSympatheticProc) { + if (type == focusSympatheticProc) { value = focus_id; } break; case SE_FcSpellVulnerability: - if(type == focusSpellVulnerability) + if (type == focusSpellVulnerability) value = focus_spell.base[i]; break; case SE_FcTwincast: - if(type == focusTwincast) + if (type == focusTwincast) value = focus_spell.base[i]; break; case SE_FcDamageAmt: - if(type == focusFcDamageAmt) + if (type == focusFcDamageAmt) value = focus_spell.base[i]; break; case SE_FcDamageAmtCrit: - if(type == focusFcDamageAmtCrit) + if (type == focusFcDamageAmtCrit) value = focus_spell.base[i]; break; case SE_FcDamageAmtIncoming: - if(type == focusFcDamageAmtIncoming) + if (type == focusFcDamageAmtIncoming) value = focus_spell.base[i]; break; case SE_FcHealAmtIncoming: - if(type == focusFcHealAmtIncoming) + if (type == focusFcHealAmtIncoming) value = focus_spell.base[i]; break; case SE_FcDamagePctCrit: - if(type == focusFcDamagePctCrit) + if (type == focusFcDamagePctCrit) value = focus_spell.base[i]; break; @@ -5075,17 +5044,17 @@ int16 Mob::CalcFocusEffect(focusType type, uint16 focus_id, uint16 spell_id, boo break; case SE_FcHealAmtCrit: - if(type == focusFcHealAmtCrit) + if (type == focusFcHealAmtCrit) value = focus_spell.base[i]; break; - case SE_FcHealAmt: - if(type == focusFcHealAmt) + case SE_FcHealAmt: + if (type == focusFcHealAmt) value = focus_spell.base[i]; break; case SE_FcHealPctIncoming: - if(type == focusFcHealPctIncoming) + if (type == focusFcHealPctIncoming) value = focus_spell.base[i]; break; @@ -5095,51 +5064,51 @@ int16 Mob::CalcFocusEffect(focusType type, uint16 focus_id, uint16 spell_id, boo break; case SE_FcIncreaseNumHits: - if(type == focusIncreaseNumHits) + if (type == focusIncreaseNumHits) value = focus_spell.base[i]; break; case SE_FcLimitUse: - if(type == focusFcLimitUse) + if (type == focusFcLimitUse) value = focus_spell.base[i]; break; case SE_FcMute: - if(type == focusFcMute) + if (type == focusFcMute) value = focus_spell.base[i]; break; case SE_FcStunTimeMod: - if(type == focusFcStunTimeMod) + if (type == focusFcStunTimeMod) value = focus_spell.base[i]; break; case SE_FcTimerRefresh: - if(type == focusFcTimerRefresh) + if (type == focusFcTimerRefresh) value = focus_spell.base[i]; break; #if EQDEBUG >= 6 - //this spits up a lot of garbage when calculating spell focuses - //since they have all kinds of extra effects on them. + // this spits up a lot of garbage when calculating spell focuses + // since they have all kinds of extra effects on them. default: - Log.Out(Logs::General, Logs::Normal, "CalcFocusEffect: unknown effectid %d", focus_spell.effectid[i]); + Log.Out(Logs::General, Logs::Normal, "CalcFocusEffect: unknown effectid %d", + focus_spell.effectid[i]); #endif } - } - for(int e = 0; e < MaxLimitInclude; e+=2) { - if (LimitInclude[e] && !LimitInclude[e+1]) + for (int e = 0; e < MaxLimitInclude; e += 2) { + if (LimitInclude[e] && !LimitInclude[e + 1]) return 0; } - if (Caston_spell_id){ - if(IsValidSpell(Caston_spell_id) && (Caston_spell_id != spell_id)) + if (Caston_spell_id) { + if (IsValidSpell(Caston_spell_id) && (Caston_spell_id != spell_id)) SpellFinished(Caston_spell_id, this, 10, 0, -1, spells[Caston_spell_id].ResistDiff); } - return(value*lvlModifier/100); + return (value * lvlModifier / 100); } uint16 Client::GetSympatheticFocusEffect(focusType type, uint16 spell_id) { @@ -5501,26 +5470,24 @@ int16 Client::GetFocusEffect(focusType type, uint16 spell_id) { if (aabonuses.FocusEffects[type]){ int16 Total3 = 0; - uint32 slots = 0; - uint32 aa_AA = 0; - uint32 aa_value = 0; - //aa old - //for (int i = 0; i < MAX_PP_AA_ARRAY; i++) - //{ - // aa_AA = this->aa[i]->AA; - // aa_value = this->aa[i]->value; - // if (aa_AA < 1 || aa_value < 1) - // continue; - // - // Total3 = CalcAAFocus(type, aa_AA, spell_id); - // if (Total3 > 0 && realTotal3 >= 0 && Total3 > realTotal3) { - // realTotal3 = Total3; - // } - // else if (Total3 < 0 && Total3 < realTotal3) { - // realTotal3 = Total3; - // } - //} + for (const auto &aa : aa_ranks) { + auto ability = zone->GetAlternateAdvancementAbility(aa.first); + if (!ability) + continue; + + auto rank = ability->GetRankByPointsSpent(aa.second.first); + if (!rank || rank->effects.empty()) + continue; + + Total3 = CalcAAFocus(type, *rank, spell_id); + if (Total3 > 0 && realTotal3 >= 0 && Total3 > realTotal3) { + realTotal3 = Total3; + } + else if (Total3 < 0 && Total3 < realTotal3) { + realTotal3 = Total3; + } + } } if(type == focusReagentCost && IsSummonPetSpell(spell_id) && GetAA(aaElementalPact))