mirror of
https://github.com/EQEmu/Server.git
synced 2025-12-12 05:21:29 +00:00
[Feature] New SPAs pass 2 (#1459)
* Implemented SPA Duration Pct Implemented new spell effects SE_Duration_HP_Pct 524 SE_Duration_Mana_Pct 525 SE_Duration_Endurance_Pct 526 Consumes 'base1' % of your maximum health/mana/endurance every 6 seconds. 'max' is maximum amount that can be consumed per tic. Additional Functionality Can be used as a heal/gain % by setting the base1 value to a positive. * Implemented SPA Instant Mana/End pct Fixes for SPA 524-526 Implemented SE_Instant_Mana_Pct 522 SE_Instant_Endurance_Pct 523 Extracts 'base1' percent of your maximum mana/endurance, or 'max', whichever is lower. * Implemented: SPA 521 EndAbsorbPctDmg Implemented SE_Endurance_Absorb_Pct_Damage 521 Absorb Damage using Endurance: base1 % (base2 End per 1 HP) Note: Both base1 and base2 need to be divided by 100 for actually value * Implemented SE_HealthTransfer 509 Implemented SE_Health_Transfer 509 'life burn' Consume base2 % of Hit Points to Damage for base % of Hit Points Can be used for heal Act of Valor * Implemented SPA 515,516,518,496 Implemented SE_AC_Avoidance_Max_Percent 515 SE_AC_Mitigation_Max_Percent 516 SE_Attack_Accuracy_Max_Percent 518 Above are stackable defense and offensive mods SE_Critical_Melee_Damage_Mod_Max 496 - This is a non stackable melee critical modifier * Implemented SPA 503 , 505 SE_Melee_Damage_Position_Mod 503 define SE_Damage_Taken_Position_Mod 505 SPA 503 increase/decreases melee damage by percent base1 based on your position base2 0=back 1=front SPA 504 increase/decreases melee damage taken by percent base1 based on your position base2 0=back 1=front * Implemented 467,468 Implemented SE_DS_Mitigation_Amount 467 SE_DS_Mitigation_Percentage 468 Reduce incoming DS by amt or percentage. base1 is value, if a reduction is desired it should be set to negative for both. * Fixes Formula fixes * Update spdat.h Added spa descriptions. * Implemented SPA 469, 470 Implemented SE_Chance_Best_in_Spell_Grp 469 Chance to cast highest scribed spell within a spell group. All base2 spells share roll chance, only 1 cast. SE_Trigger_Best_in_Spell_Grp 470 Chance to cast highest scribed spell within a spell group. Each spell has own chance. Additional Changes: Rewrote TrySpellTrigger function used for SPA 340 since it incorporates SPA 469. Improved code so that chance of spell being triggered should be more accurate statistically. * Implemented SPA 474, 494 Implemented SE_Pet_Crit_Melee_Damage_Pct_Owner 474 - Gives pets a critical melee damage modifier from the owner SE_Pet_Add_Atk 494 - Gives pet a ATK bonus from the owner Fixed SE_PetMeleeMitigation 397 - The bonus was not being calculated * Implemented SPA 465,477,478 Implemented SE_PC_Pet_AE_Rampage 465 Chance for pet to AE rampage with a damage modifier SE_Hatelist_To_Top_Index 477 Chance to be put on top of RAMPAGE list SE_Hatelist_To_Tail_Index 478 Chance to be put on bottom of RAMPAGE list * Implemented Implemented SE_Fearstun 502 Stun with a max level limit. Normal stun restrictions don't apply. Base1 duration, base2 PC duration, max is level limit SE_TwinCastBlocker 39 Previously unused spell effect that is now used on live. Simply, if this effect is present in a spell then the spell can not be twin cast. * Implemented SPA 483 Implemented Fc_Spell_Damage_Pct_IncomingPC 483 - Focus effect that modifies iby percent incoming spell damage on the target. Base1= min Base2= max. Final percent is random between max and min each time focus is applied from a spell cast. Note: Written to stack with similar functioning focus SPA 269 SE_FcSpellVulnerability. * Implemented SPA 484 Implemented SE_Fc_Spell_Damage_Amt_IncomingPC 484 // focus effect that modifies incoming spell damage by flat amount. Consider it a debuff that adds damage to incoming spells. Positive value to add additional damage. * Implemented SPA 481, 485,486,512 Implemented SE_Fc_Cast_Spell_On_Land 481 Focus effect that is checked when a spell is cast on a target, if target has this focus effect and all limiting criteria are met, then the target will cast a spell as specified by the focus. Can be given a roll chance for success. Base1=Chance, Base2=Spellid Note: This spell has a huge amount of potential applications. See 'Alliance' type spells on live. (ie live spell 50247) Implemented associated focus limits seen in live spells. SE_Ff_CasterClass 485 - Caster of spell on target with a focus effect that is checked by incoming spells must be specified class or classes. SE_Ff_Same_Caster 486 -Caster of spell on target with a focus effect that is checked by incoming spells 0=Must be different caster 1=Must be same caster The following is an associated effect seen with SPA 481 SE_Proc_Timer_Modifier 512 This provides a way to rate limit the amount of spell triggers generated by SPA 481. For example after 1 successful spell trigger no additional spells can be triggered for 1.5 seconds. Ie. Base=1 and Base2 1500. Written in a flexible format to allow scaling of multiple different buffs with this effect at same time. * Stacking fixes for new effects Stacking fixes for new effects. * merge with upstream master merge and update up spdat.h * Update spdat.h * Fix for bolt spell targeting self if target zone/died while casting. Fix for bolt spell targeting self if target zone/died while casting. Despite the name being "ST_TargetOptional", this target type is reserved for projectile spells which all require a target, thus should be treated like any other targeted spell.
This commit is contained in:
parent
8a2a1b152e
commit
2b74d71ff5
@ -1218,6 +1218,12 @@ bool IsEffectIgnoredInStacking(int spa)
|
||||
case SE_LimitUseType:
|
||||
case SE_GravityEffect:
|
||||
case 425:
|
||||
//Spell effects implemented after ROF2, following same pattern, lets assume these should go here.
|
||||
case SE_Fc_Spell_Damage_Pct_IncomingPC:
|
||||
case SE_Fc_Spell_Damage_Amt_IncomingPC:
|
||||
case SE_Ff_CasterClass:
|
||||
case SE_Ff_Same_Caster:
|
||||
case SE_Proc_Timer_Modifier:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
|
||||
@ -165,6 +165,7 @@
|
||||
#define MAX_SYMPATHETIC_PROCS 10 // Number of sympathetic procs a client can have (This is arbitrary)
|
||||
|
||||
|
||||
|
||||
const int Z_AGGRO=10;
|
||||
|
||||
const uint32 MobAISpellRange=100; // max range of buffs
|
||||
@ -278,7 +279,7 @@ enum RESISTTYPE
|
||||
|
||||
//Target Type IDs
|
||||
typedef enum {
|
||||
/* 01 */ ST_TargetOptional = 0x01,
|
||||
/* 01 */ ST_TargetOptional = 0x01, //only used for targeted projectile spells
|
||||
/* 02 */ ST_AEClientV1 = 0x02,
|
||||
/* 03 */ ST_GroupTeleport = 0x03,
|
||||
/* 04 */ ST_AECaster = 0x04,
|
||||
@ -381,7 +382,7 @@ typedef enum {
|
||||
#define SE_PoisonCounter 36 // implemented
|
||||
//#define SE_DetectHostile 37 // not used
|
||||
//#define SE_DetectMagic 38 // not used
|
||||
//#define SE_DetectPoison 39 // not used
|
||||
#define SE_TwinCastBlocker 39 // implemented - If present in spell, then the spell can not be twincast.
|
||||
#define SE_DivineAura 40 // implemented
|
||||
#define SE_Destroy 41 // implemented - Disintegrate, Banishment of Shadows
|
||||
#define SE_ShadowStep 42 // implemented
|
||||
@ -464,7 +465,7 @@ typedef enum {
|
||||
#define SE_AttackSpeed3 119 // implemented
|
||||
#define SE_HealRate 120 // implemented - reduces healing by a %
|
||||
#define SE_ReverseDS 121 // implemented
|
||||
//#define SE_ReduceSkill 122 // not used
|
||||
//#define SE_ReduceSkill 122 // not implemented TODO: Now used on live, decreases skills by percent
|
||||
#define SE_Screech 123 // implemented Spell Blocker(If have buff with value +1 will block any effect with -1)
|
||||
#define SE_ImprovedDamage 124 // implemented
|
||||
#define SE_ImprovedHeal 125 // implemented
|
||||
@ -488,7 +489,7 @@ typedef enum {
|
||||
#define SE_LimitCastTimeMin 143 // implemented
|
||||
#define SE_LimitCastTimeMax 144 // implemented (*not used in any known live spell)
|
||||
#define SE_Teleport2 145 // implemented - Banishment of the Pantheon
|
||||
//#define SE_ElectricityResist 146 // *not implemented (Lightning Rod: 23233)
|
||||
//#define SE_ElectricityResist 146 // *not implemented TODO: Now used on live, xyz for teleport spells? also in temp pets?
|
||||
#define SE_PercentalHeal 147 // implemented
|
||||
#define SE_StackingCommand_Block 148 // implemented?
|
||||
#define SE_StackingCommand_Overwrite 149 // implemented?
|
||||
@ -638,7 +639,7 @@ typedef enum {
|
||||
#define SE_FrontalStunResist 293 // implemented[AA] - Reduce chance to be stunned from front. -- live descriptions sounds like this isn't limited to frontal anymore
|
||||
#define SE_CriticalSpellChance 294 // implemented - increase chance to critical hit and critical damage modifier.
|
||||
//#define SE_ReduceTimerSpecial 295 // not used
|
||||
#define SE_FcSpellVulnerability 296 // implemented - increase in incoming spell damage
|
||||
#define SE_FcSpellVulnerability 296 // implemented - increase in incoming spell damage [base1= min dmg base2= max dmg]
|
||||
#define SE_FcDamageAmtIncoming 297 // implemented - debuff that adds points damage to spells cast on target (focus effect).
|
||||
#define SE_ChangeHeight 298 // implemented
|
||||
#define SE_WakeTheDead 299 // implemented
|
||||
@ -674,7 +675,7 @@ typedef enum {
|
||||
#define SE_ManaAbsorbPercentDamage 329 // implemented
|
||||
#define SE_CriticalDamageMob 330 // implemented
|
||||
#define SE_Salvage 331 // implemented - chance to recover items that would be destroyed in failed tradeskill combine
|
||||
#define SE_SummonToCorpse 332 // *not implemented AA - Call of the Wild (Druid/Shaman Res spell with no exp)
|
||||
#define SE_SummonToCorpse 332 // *not implemented AA - Call of the Wild (Druid/Shaman Res spell with no exp) TOOD: implement this.
|
||||
#define SE_CastOnRuneFadeEffect 333 // implemented
|
||||
#define SE_BardAEDot 334 // implemented
|
||||
#define SE_BlockNextSpellFocus 335 // implemented - base1 chance to block next spell ie Puratus (8494)
|
||||
@ -682,7 +683,7 @@ typedef enum {
|
||||
#define SE_PercentXPIncrease 337 // implemented
|
||||
#define SE_SummonAndResAllCorpses 338 // implemented
|
||||
#define SE_TriggerOnCast 339 // implemented
|
||||
#define SE_SpellTrigger 340 // implemented - chance to trigger spell
|
||||
#define SE_SpellTrigger 340 // implemented - chance to trigger spell [Share rolls with 469] All base2 spells share roll chance, only 1 cast.
|
||||
#define SE_ItemAttackCapIncrease 341 // implemented[AA] - increases the maximum amount of attack you can gain from items.
|
||||
#define SE_ImmuneFleeing 342 // implemented - stop mob from fleeing
|
||||
#define SE_InterruptCasting 343 // implemented - % chance to interrupt spells being cast every tic. Cacophony (8272)
|
||||
@ -739,7 +740,7 @@ typedef enum {
|
||||
#define SE_FcHealAmtIncoming 394 // implemented - Adds/Removes amount of healing on target by X value with foucs restrictions.
|
||||
#define SE_FcHealPctCritIncoming 395 // implemented[AA] - Increases chance of having a heal crit when cast on you. [focus limited]
|
||||
#define SE_FcHealAmtCrit 396 // implemented - Adds a direct healing amount to spells
|
||||
#define SE_PetMeleeMitigation 397 // implemented[AA] - additional mitigation to your pets. Adds AC.
|
||||
#define SE_PetMeleeMitigation 397 // implemented[AA] - additional mitigation to your pets. Adds AC
|
||||
#define SE_SwarmPetDuration 398 // implemented - Affects the duration of swarm pets
|
||||
#define SE_FcTwincast 399 // implemented - cast 2 spells for every 1
|
||||
#define SE_HealGroupFromMana 400 // implemented - Drains mana and heals for each point of mana drained
|
||||
@ -807,28 +808,28 @@ typedef enum {
|
||||
#define SE_FcDamageAmt2 462 // implemented - Increase spell damage by flat amount (SE_Fc_Damage_Amt2)
|
||||
//#define SE_Shield_Target 463 //
|
||||
#define SE_PC_Pet_Rampage 464 // implemented - Base1 % chance to do rampage for base2 % of damage each melee round
|
||||
//#define SE_PC_Pet_AE_Rampage 465 // Would assume as above but need to confirm.
|
||||
#define SE_PC_Pet_AE_Rampage 465 // implemented - Base1 % chance to do AE rampage for base2 % of damage each melee round
|
||||
#define SE_PC_Pet_Flurry_Chance 466 // implemented - Base1 % chance to do flurry from double attack hit.
|
||||
#define SE_DS_Mitigation_Amount 467 // implemented - Modify incoming damage shield damage by a flat amount
|
||||
#define SE_DS_Mitigation_Percentage 468 // implemented - Modify incoming damage shield damage by percentage
|
||||
//#define SE_Chance_Best_in_Spell_Grp 469 //
|
||||
//#define SE_Trigger_Best_in_Spell Grp 470 //
|
||||
#define SE_Chance_Best_in_Spell_Grp 469 // implemented - Chance to cast highest scribed spell within a spell group. All base2 spells share roll chance, only 1 cast.
|
||||
#define SE_Trigger_Best_in_Spell_Grp 470 // implemented - Chance to cast highest scribed spell within a spell group. Each spell has own chance.
|
||||
//#define SE_Double_Melee_Round 471 //
|
||||
//#define SE_Buy_AA_Rank 472 //
|
||||
#define SE_Double_Backstab_Front 473 // implemented - Chance to double backstab from front
|
||||
//#define SE_Pet_Crit_Melee_Damage_Pct_Owner 474 //
|
||||
//#define SE_Trigger_Spell_Non_Item 475 //
|
||||
#define SE_Pet_Crit_Melee_Damage_Pct_Owner 474 // implemenetd - Critical damage mod applied to pets from owner
|
||||
#define SE_Trigger_Spell_Non_Item 475 // implemented - Trigger spell on cast only if not from item click.
|
||||
//#define SE_Weapon_Stance 476 //
|
||||
//#define SE_Hatelist_To_Top_Index 477 //
|
||||
//#define SE_Hatelist_To_Tail_Index 478 //
|
||||
#define SE_Hatelist_To_Top_Index 477 // Implemented - Chance to be set to top of rampage list
|
||||
#define SE_Hatelist_To_Tail_Index 478 // Implemented - Chance to be set to bottom of rampage list
|
||||
//#define SE_Ff_Value_Min 479 //
|
||||
//#define SE_Ff_Value_Max 480 //
|
||||
//#define SE_Fc_Cast_Spell_On_Land 481 //
|
||||
#define SE_Fc_Cast_Spell_On_Land 481 // Implemented - [FOCUS] Spells cast on target with this Focus Effect will have chance to cause a Spell to be cast if limits met.
|
||||
//#define SE_Skill_Base_Damage_Mod 482 //
|
||||
//#define SE_Fc_Spell_Damage_Pct_IncomingPC 483 //
|
||||
//#define SE_Fc_Spell_Damage_Amt_IncomingPC 484 //
|
||||
//#define SE_Ff_CasterClass 485 //
|
||||
//#define SE_Ff_Same_Caster 486 //
|
||||
#define SE_Fc_Spell_Damage_Pct_IncomingPC 483 // Implemented - [FOCUS] modifies incoming spell damage by percent
|
||||
#define SE_Fc_Spell_Damage_Amt_IncomingPC 484 // Implemented - [FOCUS] modifies incoming spell damage by flat amount. Typically adds damage to incoming spells.
|
||||
#define SE_Ff_CasterClass 485 // Implemented - [FOCUS LIMIT] Caster of spell on target with a focus effect that is checked by incoming spells must be specified class.
|
||||
#define SE_Ff_Same_Caster 486 // Implemented - [FOCUS LIMIT] Caster of spell on target with a focus effect that is checked by incoming spells 0=Must be different caster 1=Must be same caster
|
||||
//#define SE_Extend_Tradeskill_Cap 487 //
|
||||
//#define SE_Defender_Melee_Force_Pct_PC 488 //
|
||||
//#define SE_Worn_Endurance_Regen_Cap 489 //
|
||||
@ -836,7 +837,7 @@ typedef enum {
|
||||
//#define SE_Ff_ReuseTimeMax 491 //
|
||||
//#define SE_Ff_Endurance_Min 492 //
|
||||
//#define SE_Ff_Endurance_Max 493 //
|
||||
//#define SE_Pet_Add_Atk 494 //
|
||||
#define SE_Pet_Add_Atk 494 // implemented - Bonus on pet owner which gives their pet increased attack stat
|
||||
//#define SE_Ff_DurationMax 495 //
|
||||
#define SE_Critical_Melee_Damage_Mod_Max 496 // implemented - increase or decrease by percent critical damage (not stackable)
|
||||
//#define SE_Ff_FocusCastProcNoBypass 497 //
|
||||
@ -844,7 +845,7 @@ typedef enum {
|
||||
//#define SE_AddExtraAttackPct_1h_Secondary 499 //
|
||||
//#define SE_Fc_CastTimeMod2 500 //
|
||||
//#define SE_Fc_CastTimeAmt 501 //
|
||||
//#define SE_Fearstun 502 //
|
||||
#define SE_Fearstun 502 // implemented - Stun with a max level limit. Normal stun restrictions don't apply.
|
||||
#define SE_Melee_Damage_Position_Mod 503 // implemented - modify melee damage by pct if done from Front or Behind
|
||||
//#define SE_Melee_Damage_Position_Amt 504 //
|
||||
#define SE_Damage_Taken_Position_Mod 505 // implemented - mitigate melee damage by pct if dmg taken from Front or Behind
|
||||
@ -854,7 +855,7 @@ typedef enum {
|
||||
#define SE_Health_Transfer 509 // implemented - exchange health for damage or healing on a target. ie Lifeburn/Act of Valor
|
||||
//#define SE_Fc_ResistIncoming 510 //
|
||||
//#define SE_Ff_FocusTimerMin 511 //
|
||||
//#define SE_Proc_Timer_Modifier 512 //
|
||||
#define SE_Proc_Timer_Modifier 512 // implemented - spell trigger limiter used currently with SPA 481, ie. limit to 1 proc every 1.5 seconds (base=1 base2=1500).
|
||||
//#define SE_Mana_Max_Percent 513 //
|
||||
//#define SE_Endurance_Max_Percent 514 //
|
||||
#define SE_AC_Avoidance_Max_Percent 515 // implemented - stackable avoidance modifier
|
||||
|
||||
@ -1018,6 +1018,7 @@ int32 Mob::CheckAggroAmount(uint16 spell_id, Mob *target, bool isproc)
|
||||
case SE_Mez:
|
||||
case SE_Charm:
|
||||
case SE_Fear:
|
||||
case SE_Fearstun:
|
||||
AggroAmount += default_aggro;
|
||||
break;
|
||||
case SE_Root:
|
||||
|
||||
@ -274,14 +274,8 @@ int Mob::GetTotalDefense()
|
||||
// 172 Evasion aka SE_AvoidMeleeChance
|
||||
evasion_bonus += itembonuses.AvoidMeleeChanceEffect + aabonuses.AvoidMeleeChanceEffect; // item bonus here isn't mod2 avoidance
|
||||
|
||||
Mob *owner = nullptr;
|
||||
if (IsPet())
|
||||
owner = GetOwner();
|
||||
else if (IsNPC() && CastToNPC()->GetSwarmOwner())
|
||||
owner = entity_list.GetMobID(CastToNPC()->GetSwarmOwner());
|
||||
|
||||
if (owner) // 215 Pet Avoidance % aka SE_PetAvoidance
|
||||
evasion_bonus += owner->aabonuses.PetAvoidance + owner->spellbonuses.PetAvoidance + owner->itembonuses.PetAvoidance;
|
||||
// 215 Pet Avoidance % aka SE_PetAvoidance
|
||||
evasion_bonus += GetPetAvoidanceBonusFromOwner();
|
||||
|
||||
// Evasion is a percentage bonus according to AA descriptions
|
||||
if (evasion_bonus)
|
||||
@ -823,13 +817,7 @@ int Mob::ACSum(bool skip_caps)
|
||||
// According to the guild hall Combat Dummies, a level 50 classic EQ mob it should be ~115
|
||||
// For a 60 PoP mob ~120, 70 OoW ~120
|
||||
ac += GetAC();
|
||||
Mob *owner = nullptr;
|
||||
if (IsPet())
|
||||
owner = GetOwner();
|
||||
else if (CastToNPC()->GetSwarmOwner())
|
||||
owner = entity_list.GetMobID(CastToNPC()->GetSwarmOwner());
|
||||
if (owner)
|
||||
ac += owner->aabonuses.PetAvoidance + owner->spellbonuses.PetAvoidance + owner->itembonuses.PetAvoidance;
|
||||
ac += GetPetACBonusFromOwner();
|
||||
auto spell_aa_ac = aabonuses.AC + spellbonuses.AC;
|
||||
ac += GetSkill(EQ::skills::SkillDefense) / 5;
|
||||
if (EQ::ValueWithin(static_cast<int>(GetClass()), NECROMANCER, ENCHANTER))
|
||||
@ -925,7 +913,7 @@ int Mob::offense(EQ::skills::SkillType skill)
|
||||
if (stat_bonus >= 75)
|
||||
offense += (2 * stat_bonus - 150) / 3;
|
||||
|
||||
offense += GetATK();
|
||||
offense += GetATK() + GetPetATKBonusFromOwner();
|
||||
return offense;
|
||||
}
|
||||
|
||||
@ -4343,7 +4331,7 @@ void Mob::TryPetCriticalHit(Mob *defender, DamageHitInfo &hit)
|
||||
|
||||
if (critChance > 0) {
|
||||
if (zone->random.Roll(critChance)) {
|
||||
critMod += GetCritDmgMod(hit.skill);
|
||||
critMod += GetCritDmgMod(hit.skill, owner);
|
||||
hit.damage_done += 5;
|
||||
hit.damage_done = (hit.damage_done * critMod) / 100;
|
||||
|
||||
@ -5266,15 +5254,22 @@ void Mob::CommonOutgoingHitSuccess(Mob* defender, DamageHitInfo &hit, ExtraAttac
|
||||
if (mod > 0)
|
||||
spec_mod = mod;
|
||||
if ((IsPet() || IsTempPet()) && IsPetOwnerClient()) {
|
||||
int spell = spellbonuses.PC_Pet_Rampage[1] + itembonuses.PC_Pet_Rampage[1] + aabonuses.PC_Pet_Rampage[1];
|
||||
if (spell > spec_mod)
|
||||
spec_mod = spell;
|
||||
//SE_PC_Pet_Rampage SPA 464 on pet, damage modifier
|
||||
int spell_mod = spellbonuses.PC_Pet_Rampage[1] + itembonuses.PC_Pet_Rampage[1] + aabonuses.PC_Pet_Rampage[1];
|
||||
if (spell_mod > spec_mod)
|
||||
spec_mod = spell_mod;
|
||||
}
|
||||
}
|
||||
else if (IsSpecialAttack(eSpecialAttacks::AERampage)) {
|
||||
int mod = GetSpecialAbilityParam(SPECATK_AREA_RAMPAGE, 2);
|
||||
if (mod > 0)
|
||||
spec_mod = mod;
|
||||
if ((IsPet() || IsTempPet()) && IsPetOwnerClient()) {
|
||||
//SE_PC_Pet_AE_Rampage SPA 465 on pet, damage modifier
|
||||
int spell_mod = spellbonuses.PC_Pet_AE_Rampage[1] + itembonuses.PC_Pet_AE_Rampage[1] + aabonuses.PC_Pet_AE_Rampage[1];
|
||||
if (spell_mod > spec_mod)
|
||||
spec_mod = spell_mod;
|
||||
}
|
||||
}
|
||||
if (spec_mod > 0)
|
||||
hit.damage_done = (hit.damage_done * spec_mod) / 100;
|
||||
@ -5625,6 +5620,48 @@ void Mob::DoOffHandAttackRounds(Mob *target, ExtraAttackOptions *opts)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int Mob::GetPetAvoidanceBonusFromOwner()
|
||||
{
|
||||
Mob *owner = nullptr;
|
||||
if (IsPet())
|
||||
owner = GetOwner();
|
||||
else if (IsNPC() && CastToNPC()->GetSwarmOwner())
|
||||
owner = entity_list.GetMobID(CastToNPC()->GetSwarmOwner());
|
||||
|
||||
if (owner)
|
||||
return owner->aabonuses.PetAvoidance + owner->spellbonuses.PetAvoidance + owner->itembonuses.PetAvoidance;
|
||||
|
||||
return 0;
|
||||
}
|
||||
int Mob::GetPetACBonusFromOwner()
|
||||
{
|
||||
Mob *owner = nullptr;
|
||||
if (IsPet())
|
||||
owner = GetOwner();
|
||||
else if (IsNPC() && CastToNPC()->GetSwarmOwner())
|
||||
owner = entity_list.GetMobID(CastToNPC()->GetSwarmOwner());
|
||||
|
||||
if (owner)
|
||||
return owner->aabonuses.PetMeleeMitigation + owner->spellbonuses.PetMeleeMitigation + owner->itembonuses.PetMeleeMitigation;
|
||||
|
||||
return 0;
|
||||
}
|
||||
int Mob::GetPetATKBonusFromOwner()
|
||||
{
|
||||
Mob *owner = nullptr;
|
||||
if (IsPet())
|
||||
owner = GetOwner();
|
||||
else if (IsNPC() && CastToNPC()->GetSwarmOwner())
|
||||
owner = entity_list.GetMobID(CastToNPC()->GetSwarmOwner());
|
||||
|
||||
if (owner)
|
||||
return owner->aabonuses.Pet_Add_Atk + owner->spellbonuses.Pet_Add_Atk + owner->itembonuses.Pet_Add_Atk;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
bool Mob::GetWasSpawnedInWater() const {
|
||||
return spawned_in_water;
|
||||
}
|
||||
|
||||
@ -1438,6 +1438,13 @@ void Mob::ApplyAABonuses(const AA::Rank &rank, StatBonuses *newbon)
|
||||
break;
|
||||
}
|
||||
|
||||
case SE_PC_Pet_AE_Rampage: {
|
||||
newbon->PC_Pet_AE_Rampage[0] += base1; //Chance to rampage
|
||||
if (newbon->PC_Pet_AE_Rampage[1] < base2)
|
||||
newbon->PC_Pet_AE_Rampage[1] = base2; //Damage modifer - take highest
|
||||
break;
|
||||
}
|
||||
|
||||
case SE_PC_Pet_Flurry_Chance:
|
||||
newbon->PC_Pet_Flurry += base1; //Chance to Flurry
|
||||
break;
|
||||
@ -1546,6 +1553,13 @@ void Mob::ApplyAABonuses(const AA::Rank &rank, StatBonuses *newbon)
|
||||
newbon->DS_Mitigation_Percentage += base1;
|
||||
break;
|
||||
|
||||
case SE_Pet_Crit_Melee_Damage_Pct_Owner:
|
||||
newbon->Pet_Crit_Melee_Damage_Pct_Owner += base1;
|
||||
break;
|
||||
|
||||
case SE_Pet_Add_Atk:
|
||||
newbon->Pet_Add_Atk += base1;
|
||||
break;
|
||||
|
||||
// to do
|
||||
case SE_PetDiscipline:
|
||||
@ -3282,6 +3296,13 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne
|
||||
break;
|
||||
}
|
||||
|
||||
case SE_PC_Pet_AE_Rampage: {
|
||||
new_bonus->PC_Pet_AE_Rampage[0] += effect_value; //Chance to rampage
|
||||
if (new_bonus->PC_Pet_AE_Rampage[1] < base2)
|
||||
new_bonus->PC_Pet_AE_Rampage[1] = base2; //Damage modifer - take highest
|
||||
break;
|
||||
}
|
||||
|
||||
case SE_PC_Pet_Flurry_Chance:
|
||||
new_bonus->PC_Pet_Flurry += effect_value; //Chance to Flurry
|
||||
break;
|
||||
@ -3386,7 +3407,13 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne
|
||||
new_bonus->DS_Mitigation_Percentage += effect_value;
|
||||
break;
|
||||
|
||||
case SE_Pet_Crit_Melee_Damage_Pct_Owner:
|
||||
new_bonus->Pet_Crit_Melee_Damage_Pct_Owner += effect_value;
|
||||
break;
|
||||
|
||||
case SE_Pet_Add_Atk:
|
||||
new_bonus->Pet_Add_Atk += effect_value;
|
||||
break;
|
||||
|
||||
//Special custom cases for loading effects on to NPC from 'npc_spels_effects' table
|
||||
if (IsAISpellEffect) {
|
||||
@ -3708,6 +3735,8 @@ uint8 Mob::IsFocusEffect(uint16 spell_id,int effect_index, bool AA,uint32 aa_eff
|
||||
return 0; //This is calculated as an actual bonus
|
||||
case SE_FcSpellVulnerability:
|
||||
return focusSpellVulnerability;
|
||||
case SE_Fc_Spell_Damage_Pct_IncomingPC:
|
||||
return focusFcSpellDamagePctIncomingPC;
|
||||
case SE_BlockNextSpellFocus:
|
||||
//return focusBlockNextSpell;
|
||||
return 0; //This is calculated as an actual bonus
|
||||
@ -3725,6 +3754,8 @@ uint8 Mob::IsFocusEffect(uint16 spell_id,int effect_index, bool AA,uint32 aa_eff
|
||||
return focusFcDamagePctCrit;
|
||||
case SE_FcDamageAmtIncoming:
|
||||
return focusFcDamageAmtIncoming;
|
||||
case SE_Fc_Spell_Damage_Amt_IncomingPC:
|
||||
return focusFcSpellDamageAmtIncomingPC;
|
||||
case SE_FcHealAmtIncoming:
|
||||
return focusFcHealAmtIncoming;
|
||||
case SE_FcHealPctIncoming:
|
||||
@ -3739,6 +3770,8 @@ uint8 Mob::IsFocusEffect(uint16 spell_id,int effect_index, bool AA,uint32 aa_eff
|
||||
return focusFcMute;
|
||||
case SE_FcTimerRefresh:
|
||||
return focusFcTimerRefresh;
|
||||
case SE_Fc_Cast_Spell_On_Land:
|
||||
return focusFcCastSpellOnLand;
|
||||
case SE_FcStunTimeMod:
|
||||
return focusFcStunTimeMod;
|
||||
case SE_FcHealPctCritIncoming:
|
||||
@ -3747,6 +3780,7 @@ uint8 Mob::IsFocusEffect(uint16 spell_id,int effect_index, bool AA,uint32 aa_eff
|
||||
return focusFcHealAmt;
|
||||
case SE_FcHealAmtCrit:
|
||||
return focusFcHealAmtCrit;
|
||||
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@ -4978,6 +5012,37 @@ void Mob::NegateSpellsBonuses(uint16 spell_id)
|
||||
aabonuses.DS_Mitigation_Percentage = effect_value;
|
||||
break;
|
||||
|
||||
case SE_Pet_Crit_Melee_Damage_Pct_Owner:
|
||||
spellbonuses.Pet_Crit_Melee_Damage_Pct_Owner = effect_value;
|
||||
itembonuses.Pet_Crit_Melee_Damage_Pct_Owner = effect_value;
|
||||
aabonuses.Pet_Crit_Melee_Damage_Pct_Owner = effect_value;
|
||||
break;
|
||||
|
||||
case SE_Pet_Add_Atk:
|
||||
spellbonuses.Pet_Add_Atk = effect_value;
|
||||
itembonuses.Pet_Add_Atk = effect_value;
|
||||
aabonuses.Pet_Add_Atk = effect_value;
|
||||
break;
|
||||
|
||||
case SE_PC_Pet_Rampage:
|
||||
spellbonuses.PC_Pet_Rampage[0] = effect_value;
|
||||
itembonuses.PC_Pet_Rampage[0] = effect_value;
|
||||
aabonuses.PC_Pet_Rampage[0] = effect_value;
|
||||
spellbonuses.PC_Pet_Rampage[1] = effect_value;
|
||||
itembonuses.PC_Pet_Rampage[1] = effect_value;
|
||||
aabonuses.PC_Pet_Rampage[1] = effect_value;
|
||||
break;
|
||||
|
||||
case SE_PC_Pet_AE_Rampage:
|
||||
spellbonuses.PC_Pet_AE_Rampage[0] = effect_value;
|
||||
itembonuses.PC_Pet_AE_Rampage[0] = effect_value;
|
||||
aabonuses.PC_Pet_AE_Rampage[0] = effect_value;
|
||||
spellbonuses.PC_Pet_AE_Rampage[1] = effect_value;
|
||||
itembonuses.PC_Pet_AE_Rampage[1] = effect_value;
|
||||
aabonuses.PC_Pet_AE_Rampage[1] = effect_value;
|
||||
break;
|
||||
|
||||
|
||||
case SE_SkillProcSuccess:{
|
||||
for(int e = 0; e < MAX_SKILL_PROCS; e++)
|
||||
{
|
||||
|
||||
@ -1021,6 +1021,7 @@ public:
|
||||
int GetNextAvailableSpellBookSlot(int starting_slot = 0);
|
||||
inline uint32 GetSpellByBookSlot(int book_slot) { return m_pp.spell_book[book_slot]; }
|
||||
inline bool HasSpellScribed(int spellid) { return FindSpellBookSlotBySpellID(spellid) != -1; }
|
||||
uint32 GetHighestScribedSpellinSpellGroup(uint32 spell_group);
|
||||
uint16 GetMaxSkillAfterSpecializationRules(EQ::skills::SkillType skillid, uint16 maxSkill);
|
||||
void SendPopupToClient(const char *Title, const char *Text, uint32 PopupID = 0, uint32 Buttons = 0, uint32 Duration = 0);
|
||||
void SendFullPopup(const char *Title, const char *Text, uint32 PopupID = 0, uint32 NegativeID = 0, uint32 Buttons = 0, uint32 Duration = 0, const char *ButtonName0 = 0, const char *ButtonName1 = 0, uint32 SoundControls = 0);
|
||||
|
||||
@ -538,6 +538,9 @@ bool Client::Process() {
|
||||
}
|
||||
}
|
||||
|
||||
if (focus_proc_limit_timer.Check() && !dead)
|
||||
FocusProcLimitProcess();
|
||||
|
||||
if (client_state == CLIENT_KICKED) {
|
||||
Save();
|
||||
OnDisconnect(true);
|
||||
|
||||
@ -124,6 +124,7 @@ typedef enum { //focus types
|
||||
focusSpellHateMod,
|
||||
focusTriggerOnCast,
|
||||
focusSpellVulnerability,
|
||||
focusFcSpellDamagePctIncomingPC,
|
||||
focusTwincast,
|
||||
focusSympatheticProc,
|
||||
focusFcDamageAmt,
|
||||
@ -135,6 +136,8 @@ typedef enum { //focus types
|
||||
focusBlockNextSpell,
|
||||
focusFcHealPctIncoming,
|
||||
focusFcDamageAmtIncoming,
|
||||
focusFcSpellDamageAmtIncomingPC,
|
||||
focusFcCastSpellOnLand,
|
||||
focusFcHealAmtIncoming,
|
||||
focusFcBaseEffects,
|
||||
focusIncreaseNumHits,
|
||||
@ -319,6 +322,8 @@ struct Buffs_Struct {
|
||||
int32 ExtraDIChance;
|
||||
int16 RootBreakChance; //Not saved to dbase
|
||||
uint32 instrument_mod;
|
||||
int16 focusproclimit_time; //timer to limit number of procs from focus effects
|
||||
int16 focusproclimit_procamt; //amount of procs that can be cast before timer limiter is set
|
||||
bool persistant_buff;
|
||||
bool client; //True if the caster is a client
|
||||
bool UpdateClient;
|
||||
@ -524,6 +529,7 @@ struct StatBonuses {
|
||||
uint32 SkillProc[MAX_SKILL_PROCS]; // Max number of spells containing skill_procs.
|
||||
uint32 SkillProcSuccess[MAX_SKILL_PROCS]; // Max number of spells containing skill_procs_success.
|
||||
uint32 PC_Pet_Rampage[2]; // 0= % chance to rampage, 1=damage modifier
|
||||
uint32 PC_Pet_AE_Rampage[2]; // 0= % chance to AE rampage, 1=damage modifier
|
||||
uint32 PC_Pet_Flurry; // Percent chance flurry from double attack
|
||||
int32 Attack_Accuracy_Max_Percent; // Increase ATK accuracy by percent.
|
||||
int32 AC_Mitigation_Max_Percent; // Increase AC mitigation by percent
|
||||
@ -532,7 +538,10 @@ struct StatBonuses {
|
||||
int32 Melee_Damage_Position_Mod[2]; // base = percent melee damage increase base2 0=back 1=front. [0]Back[1]Front
|
||||
int32 Double_Backstab_Front; // base = percent chance to double back stab front
|
||||
int32 DS_Mitigation_Amount; // base = flat amt DS mitigation. Negative value to reduce
|
||||
int32 DS_Mitigation_Percentage; // base = percent amt of DS mitigation. Negative value to reduce
|
||||
int32 DS_Mitigation_Percentage; // base = percent amt of DS mitigation. Negative value to reduce
|
||||
int32 Pet_Crit_Melee_Damage_Pct_Owner; // base = percent mod for pet critcal damage from owner
|
||||
int32 Pet_Add_Atk; // base = Pet ATK bonus from owner
|
||||
|
||||
|
||||
// AAs
|
||||
int8 Packrat; //weight reduction for items, 1 point = 10%
|
||||
|
||||
251
zone/mob.cpp
251
zone/mob.cpp
@ -102,6 +102,7 @@ Mob::Mob(
|
||||
ranged_timer(2000),
|
||||
tic_timer(6000),
|
||||
mana_timer(2000),
|
||||
focus_proc_limit_timer(250),
|
||||
spellend_timer(0),
|
||||
rewind_timer(30000),
|
||||
bindwound_timer(10000),
|
||||
@ -3537,57 +3538,82 @@ void Mob::TriggerOnCast(uint32 focus_spell, uint32 spell_id, bool aa_trigger)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
bool Mob::TrySpellTrigger(Mob *target, uint32 spell_id, int effect)
|
||||
{
|
||||
if(!target || !IsValidSpell(spell_id))
|
||||
if (!target || !IsValidSpell(spell_id))
|
||||
return false;
|
||||
|
||||
int spell_trig = 0;
|
||||
// Count all the percentage chances to trigger for all effects
|
||||
for(int i = 0; i < EFFECT_COUNT; i++)
|
||||
{
|
||||
if (spells[spell_id].effectid[i] == SE_SpellTrigger)
|
||||
spell_trig += spells[spell_id].base[i];
|
||||
}
|
||||
// If all the % add to 100, then only one of the effects can fire but one has to fire.
|
||||
if (spell_trig == 100)
|
||||
{
|
||||
int trig_chance = 100;
|
||||
for(int i = 0; i < EFFECT_COUNT; i++)
|
||||
{
|
||||
if (spells[spell_id].effectid[i] == SE_SpellTrigger)
|
||||
{
|
||||
if(zone->random.Int(0, trig_chance) <= spells[spell_id].base[i])
|
||||
{
|
||||
// If we trigger an effect then its over.
|
||||
if (IsValidSpell(spells[spell_id].base2[i])){
|
||||
SpellFinished(spells[spell_id].base2[i], target, EQ::spells::CastingSlot::Item, 0, -1, spells[spells[spell_id].base2[i]].ResistDiff);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Increase the chance to fire for the next effect, if all effects fail, the final effect will fire.
|
||||
trig_chance -= spells[spell_id].base[i];
|
||||
}
|
||||
}
|
||||
/*The effects SE_SpellTrigger (SPA 340) and SE_Chance_Best_in_Spell_Grp (SPA 469) work as follows, you typically will have 2-3 different spells each with their own
|
||||
chance to be triggered with all chances equaling up to 100 pct, with only 1 spell out of the group being ultimately cast.
|
||||
(ie Effect1 trigger spellA with 30% chance, Effect2 triggers spellB with 20% chance, Effect3 triggers spellC with 50% chance).
|
||||
The following function ensures a stastically accurate chance for each spell to be cast based on their chance values. These effects are also used in spells where there
|
||||
is only 1 effect using the trigger effect. In those situations we simply roll a chance for that spell to be cast once.
|
||||
Note: Both SPA 340 and 469 can be in same spell and both cummulative add up to 100 pct chances. SPA469 only difference being the spell cast will
|
||||
be "best in spell group", instead of a defined spell_id.*/
|
||||
|
||||
}
|
||||
}
|
||||
// if the chances don't add to 100, then each effect gets a chance to fire, chance for no trigger as well.
|
||||
else
|
||||
int chance_array[EFFECT_COUNT] = {};
|
||||
int total_chance = 0;
|
||||
int effect_slot = effect;
|
||||
bool CastSpell = false;
|
||||
|
||||
for (int i = 0; i < EFFECT_COUNT; i++)
|
||||
{
|
||||
if(zone->random.Int(0, 100) <= spells[spell_id].base[effect])
|
||||
{
|
||||
if (IsValidSpell(spells[spell_id].base2[effect])){
|
||||
SpellFinished(spells[spell_id].base2[effect], target, EQ::spells::CastingSlot::Item, 0, -1, spells[spells[spell_id].base2[effect]].ResistDiff);
|
||||
return true; //Only trigger once of these per spell effect.
|
||||
if (spells[spell_id].effectid[i] == SE_SpellTrigger || spells[spell_id].effectid[i] == SE_Chance_Best_in_Spell_Grp)
|
||||
total_chance += spells[spell_id].base[i];
|
||||
}
|
||||
|
||||
if (total_chance == 100)
|
||||
{
|
||||
int current_chance = 0;
|
||||
int cummulative_chance = 0;
|
||||
|
||||
for (int i = 0; i < EFFECT_COUNT; i++){
|
||||
//Find spells with SPA 340 and add the cummulative percent chances to the roll array
|
||||
if ((spells[spell_id].effectid[i] == SE_SpellTrigger) || (spells[spell_id].effectid[i] == SE_Chance_Best_in_Spell_Grp)){
|
||||
|
||||
cummulative_chance = current_chance + spells[spell_id].base[i];
|
||||
chance_array[i] = cummulative_chance;
|
||||
current_chance = cummulative_chance;
|
||||
}
|
||||
}
|
||||
int random_roll = zone->random.Int(1, 100);
|
||||
//Determine which spell out of the group of the spells (each with own percent chance out of 100) will be cast based on a single roll.
|
||||
for (int i = 0; i < EFFECT_COUNT; i++){
|
||||
if (chance_array[i] != 0 && random_roll <= chance_array[i]) {
|
||||
effect_slot = i;
|
||||
CastSpell = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//If the chances don't add to 100, then each effect gets a chance to fire, chance for no trigger as well.
|
||||
else if (zone->random.Roll(spells[spell_id].base[effect])) {
|
||||
CastSpell = true; //In this case effect_slot is what was passed into function.
|
||||
}
|
||||
|
||||
if (CastSpell) {
|
||||
if (spells[spell_id].effectid[effect_slot] == SE_SpellTrigger && IsValidSpell(spells[spell_id].base2[effect_slot])) {
|
||||
SpellFinished(spells[spell_id].base2[effect_slot], target, EQ::spells::CastingSlot::Item, 0, -1, spells[spells[spell_id].base2[effect_slot]].ResistDiff);
|
||||
return true;
|
||||
}
|
||||
else if (IsClient() & spells[spell_id].effectid[effect_slot] == SE_Chance_Best_in_Spell_Grp) {
|
||||
uint32 best_spell_id = CastToClient()->GetHighestScribedSpellinSpellGroup(spells[spell_id].base2[effect_slot]);
|
||||
if (IsValidSpell(best_spell_id)) {
|
||||
SpellFinished(best_spell_id, target, EQ::spells::CastingSlot::Item, 0, -1, spells[best_spell_id].ResistDiff);
|
||||
}
|
||||
return true;//Do nothing if you don't have the any spell in spell group scribed.
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void Mob::TryTriggerOnValueAmount(bool IsHP, bool IsMana, bool IsEndur, bool IsPet)
|
||||
{
|
||||
/*
|
||||
@ -3676,6 +3702,9 @@ void Mob::TryTwincast(Mob *caster, Mob *target, uint32 spell_id)
|
||||
if(!IsValidSpell(spell_id))
|
||||
return;
|
||||
|
||||
if (IsEffectInSpell(spell_id, SE_TwinCastBlocker))
|
||||
return;
|
||||
|
||||
if(IsClient())
|
||||
{
|
||||
int32 focus = CastToClient()->GetFocusEffect(focusTwincast, spell_id);
|
||||
@ -3739,34 +3768,46 @@ void Mob::TryOnSpellFinished(Mob *caster, Mob *target, uint16 spell_id)
|
||||
|
||||
int32 Mob::GetVulnerability(Mob* caster, uint32 spell_id, uint32 ticsremaining)
|
||||
{
|
||||
/*
|
||||
Modifies incoming spell damage by percent, to increase or decrease damage, can be limited to specific resists.
|
||||
Can be applied through quest function, spell focus or npc_spells_effects table. This function is run on the target of the spell.
|
||||
*/
|
||||
|
||||
if (!IsValidSpell(spell_id))
|
||||
return 0;
|
||||
|
||||
if (!caster)
|
||||
return 0;
|
||||
|
||||
int32 value = 0;
|
||||
int32 total_mod = 0;
|
||||
int32 innate_mod = 0;
|
||||
int32 fc_spell_vulnerability_mod = 0;
|
||||
int32 fc_spell_damage_pct_incomingPC_mod = 0;
|
||||
|
||||
//Apply innate vulnerabilities
|
||||
//Apply innate vulnerabilities from quest functions and tables
|
||||
if (Vulnerability_Mod[GetSpellResistType(spell_id)] != 0)
|
||||
value = Vulnerability_Mod[GetSpellResistType(spell_id)];
|
||||
|
||||
innate_mod = Vulnerability_Mod[GetSpellResistType(spell_id)];
|
||||
|
||||
else if (Vulnerability_Mod[HIGHEST_RESIST+1] != 0)
|
||||
value = Vulnerability_Mod[HIGHEST_RESIST+1];
|
||||
innate_mod = Vulnerability_Mod[HIGHEST_RESIST+1];
|
||||
|
||||
//Apply spell derived vulnerabilities
|
||||
if (spellbonuses.FocusEffects[focusSpellVulnerability]){
|
||||
//[Apply spell derived vulnerabilities] Step 1: Check this focus effect exists on the mob.
|
||||
if (spellbonuses.FocusEffects[focusSpellVulnerability]){
|
||||
|
||||
int32 tmp_focus = 0;
|
||||
int tmp_buffslot = -1;
|
||||
|
||||
/*
|
||||
Find all buffs that may contain SPA 296, then find which slot has the highest possible effect. Since the focus can use
|
||||
a min and max amount value to determine final focus amt. To find the best focus, use only max value if possible. Once the
|
||||
best is found. Run it again to get the final value randoming between min and max.
|
||||
*/
|
||||
int buff_count = GetMaxTotalSlots();
|
||||
for(int i = 0; i < buff_count; i++) {
|
||||
|
||||
if((IsValidSpell(buffs[i].spellid) && IsEffectInSpell(buffs[i].spellid, SE_FcSpellVulnerability))){
|
||||
|
||||
int32 focus = caster->CalcFocusEffect(focusSpellVulnerability, buffs[i].spellid, spell_id, true);
|
||||
int32 focus = caster->CalcFocusEffect(focusSpellVulnerability, buffs[i].spellid, spell_id, true, buffs[tmp_buffslot].casterid);
|
||||
|
||||
if (!focus)
|
||||
continue;
|
||||
@ -3780,21 +3821,61 @@ int32 Mob::GetVulnerability(Mob* caster, uint32 spell_id, uint32 ticsremaining)
|
||||
tmp_focus = focus;
|
||||
tmp_buffslot = i;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
tmp_focus = caster->CalcFocusEffect(focusSpellVulnerability, buffs[tmp_buffslot].spellid, spell_id);
|
||||
|
||||
if (tmp_focus < -99)
|
||||
tmp_focus = -99;
|
||||
|
||||
value += tmp_focus;
|
||||
fc_spell_vulnerability_mod = caster->CalcFocusEffect(focusSpellVulnerability, buffs[tmp_buffslot].spellid, spell_id, false, buffs[tmp_buffslot].casterid);
|
||||
|
||||
if (tmp_buffslot >= 0)
|
||||
CheckNumHitsRemaining(NumHit::MatchingSpells, tmp_buffslot);
|
||||
}
|
||||
return value;
|
||||
|
||||
if (spellbonuses.FocusEffects[focusFcSpellDamagePctIncomingPC]) {
|
||||
|
||||
int32 tmp_focus = 0;
|
||||
int tmp_buffslot = -1;
|
||||
|
||||
/*
|
||||
Find all buffs that may contain SPA 483, then find which slot has the highest possible effect. Since the focus can use
|
||||
a min and max amount value to determine final focus amt. To find the best focus, use only max value if possible. Once the
|
||||
best is found. Run it again to get the final value randoming between min and max.
|
||||
*/
|
||||
int buff_count = GetMaxTotalSlots();
|
||||
for (int i = 0; i < buff_count; i++) {
|
||||
|
||||
if ((IsValidSpell(buffs[i].spellid) && IsEffectInSpell(buffs[i].spellid, SE_Fc_Spell_Damage_Pct_IncomingPC))) {
|
||||
|
||||
int32 focus = caster->CalcFocusEffect(focusFcSpellDamagePctIncomingPC, buffs[i].spellid, spell_id, true, buffs[tmp_buffslot].casterid);
|
||||
|
||||
if (!focus)
|
||||
continue;
|
||||
|
||||
if (tmp_focus && focus > tmp_focus) {
|
||||
tmp_focus = focus;
|
||||
tmp_buffslot = i;
|
||||
}
|
||||
|
||||
else if (!tmp_focus) {
|
||||
tmp_focus = focus;
|
||||
tmp_buffslot = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fc_spell_damage_pct_incomingPC_mod = caster->CalcFocusEffect(focusFcSpellDamagePctIncomingPC, buffs[tmp_buffslot].spellid, spell_id, false, buffs[tmp_buffslot].casterid);
|
||||
|
||||
if (tmp_buffslot >= 0)
|
||||
CheckNumHitsRemaining(NumHit::MatchingSpells, tmp_buffslot);
|
||||
}
|
||||
|
||||
total_mod = fc_spell_vulnerability_mod + fc_spell_damage_pct_incomingPC_mod;
|
||||
|
||||
//Don't let focus derived mods reduce past 99% mitigation. Quest related can, and for custom functionality if negative will give a healing affect instead of damage.
|
||||
if (total_mod < -99)
|
||||
total_mod = -99;
|
||||
|
||||
total_mod += innate_mod;
|
||||
return total_mod;
|
||||
}
|
||||
|
||||
int32 Mob::GetSkillDmgTaken(const EQ::skills::SkillType skill_used, ExtraAttackOptions *opts)
|
||||
@ -3856,6 +3937,61 @@ int16 Mob::GetHealRate(uint16 spell_id, Mob* caster) {
|
||||
return heal_rate;
|
||||
}
|
||||
|
||||
void Mob::SetBottomRampageList()
|
||||
{
|
||||
auto &mob_list = entity_list.GetCloseMobList(this);
|
||||
|
||||
for (auto &e : mob_list) {
|
||||
auto mob = e.second;
|
||||
if (!mob) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!mob->GetSpecialAbility(SPECATK_RAMPAGE)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (mob->IsNPC() && mob->CheckAggro(this)) {
|
||||
for (int i = 0; i < mob->RampageArray.size(); i++) {
|
||||
// Find this mob in the rampage list
|
||||
if (this->GetID() == mob->RampageArray[i]) {
|
||||
//Move to bottom of Rampage List
|
||||
auto it = mob->RampageArray.begin() + i;
|
||||
std::rotate(it, it + 1, mob->RampageArray.end());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Mob::SetTopRampageList()
|
||||
{
|
||||
auto &mob_list = entity_list.GetCloseMobList(this);
|
||||
|
||||
for (auto &e : mob_list) {
|
||||
auto mob = e.second;
|
||||
if (!mob) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!mob->GetSpecialAbility(SPECATK_RAMPAGE)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (mob->IsNPC() && mob->CheckAggro(this)) {
|
||||
for (int i = 0; i < mob->RampageArray.size(); i++) {
|
||||
// Find this mob in the rampage list
|
||||
if (this->GetID() == mob->RampageArray[i]) {
|
||||
//Move to Top of Rampage List
|
||||
auto it = mob->RampageArray.begin() + i;
|
||||
std::rotate(it, it + 1, mob->RampageArray.end());
|
||||
std::rotate(mob->RampageArray.rbegin(), mob->RampageArray.rbegin() + 1, mob->RampageArray.rend());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool Mob::TryFadeEffect(int slot)
|
||||
{
|
||||
if (!buffs[slot].spellid)
|
||||
@ -4647,7 +4783,7 @@ bool Mob::TrySpellOnDeath()
|
||||
//in death because the heal will not register before the script kills you.
|
||||
}
|
||||
|
||||
int16 Mob::GetCritDmgMod(uint16 skill)
|
||||
int16 Mob::GetCritDmgMod(uint16 skill, Mob* owner)
|
||||
{
|
||||
int critDmg_mod = 0;
|
||||
|
||||
@ -4658,6 +4794,9 @@ int16 Mob::GetCritDmgMod(uint16 skill)
|
||||
critDmg_mod += itembonuses.CritDmgModNoStack[EQ::skills::HIGHEST_SKILL + 1] + spellbonuses.CritDmgModNoStack[EQ::skills::HIGHEST_SKILL + 1] + aabonuses.CritDmgModNoStack[EQ::skills::HIGHEST_SKILL + 1] +
|
||||
itembonuses.CritDmgModNoStack[skill] + spellbonuses.CritDmgModNoStack[skill] + aabonuses.CritDmgModNoStack[skill];
|
||||
|
||||
if (owner) //Checked in TryPetCriticalHit
|
||||
critDmg_mod += owner->aabonuses.Pet_Crit_Melee_Damage_Pct_Owner + owner->itembonuses.Pet_Crit_Melee_Damage_Pct_Owner + owner->spellbonuses.Pet_Crit_Melee_Damage_Pct_Owner;
|
||||
|
||||
return critDmg_mod;
|
||||
}
|
||||
|
||||
|
||||
14
zone/mob.h
14
zone/mob.h
@ -770,7 +770,7 @@ public:
|
||||
void QuestJournalledSay(Client *QuestInitiator, const char *str, Journal::Options &opts);
|
||||
int32 GetItemStat(uint32 itemid, const char *identifier);
|
||||
|
||||
int16 CalcFocusEffect(focusType type, uint16 focus_id, uint16 spell_id, bool best_focus=false);
|
||||
int16 CalcFocusEffect(focusType type, uint16 focus_id, uint16 spell_id, bool best_focus=false, uint16 casterid=0);
|
||||
uint8 IsFocusEffect(uint16 spellid, int effect_index, bool AA=false,uint32 aa_effect=0);
|
||||
void SendIllusionPacket(uint16 in_race, uint8 in_gender = 0xFF, uint8 in_texture = 0xFF, uint8 in_helmtexture = 0xFF,
|
||||
uint8 in_haircolor = 0xFF, uint8 in_beardcolor = 0xFF, uint8 in_eyecolor1 = 0xFF, uint8 in_eyecolor2 = 0xFF,
|
||||
@ -814,7 +814,7 @@ public:
|
||||
void CastOnCure(uint32 spell_id);
|
||||
void CastOnNumHitFade(uint32 spell_id);
|
||||
void SlowMitigation(Mob* caster);
|
||||
int16 GetCritDmgMod(uint16 skill);
|
||||
int16 GetCritDmgMod(uint16 skill, Mob* owner = nullptr);
|
||||
int16 GetMeleeDamageMod_SE(uint16 skill);
|
||||
int16 GetMeleeMinDamageMod_SE(uint16 skill);
|
||||
int16 GetCrippBlowChance();
|
||||
@ -837,6 +837,10 @@ public:
|
||||
inline int16 GetSpellPowerDistanceMod() const { return SpellPowerDistanceMod; };
|
||||
inline void SetSpellPowerDistanceMod(int16 value) { SpellPowerDistanceMod = value; };
|
||||
int32 GetSpellStat(uint32 spell_id, const char *identifier, uint8 slot = 0);
|
||||
|
||||
void CastSpellOnLand(Mob* caster, uint32 spell_id);
|
||||
void FocusProcLimitProcess();
|
||||
bool ApplyFocusProcLimiter(uint32 spell_id, int buffslot = -1);
|
||||
|
||||
void ModSkillDmgTaken(EQ::skills::SkillType skill_num, int value);
|
||||
int16 GetModSkillDmgTaken(const EQ::skills::SkillType skill_num);
|
||||
@ -909,6 +913,9 @@ public:
|
||||
inline bool IsTempPet() const { return _IsTempPet; }
|
||||
inline void SetTempPet(bool value) { _IsTempPet = value; }
|
||||
inline bool IsHorse() { return is_horse; }
|
||||
int GetPetAvoidanceBonusFromOwner();
|
||||
int GetPetACBonusFromOwner();
|
||||
int GetPetATKBonusFromOwner();
|
||||
|
||||
inline const bodyType GetBodyType() const { return bodytype; }
|
||||
inline const bodyType GetOrigBodyType() const { return orig_bodytype; }
|
||||
@ -966,6 +973,8 @@ public:
|
||||
bool Rampage(ExtraAttackOptions *opts);
|
||||
bool AddRampage(Mob*);
|
||||
void ClearRampage();
|
||||
void SetBottomRampageList();
|
||||
void SetTopRampageList();
|
||||
void AreaRampage(ExtraAttackOptions *opts);
|
||||
inline bool IsSpecialAttack(eSpecialAttacks in) { return m_specialattacks == in; }
|
||||
|
||||
@ -1414,6 +1423,7 @@ protected:
|
||||
int16 slow_mitigation; // Allows for a slow mitigation (100 = 100%, 50% = 50%)
|
||||
Timer tic_timer;
|
||||
Timer mana_timer;
|
||||
Timer focus_proc_limit_timer;
|
||||
|
||||
//spell casting vars
|
||||
Timer spellend_timer;
|
||||
|
||||
@ -1125,6 +1125,9 @@ void Mob::AI_Process() {
|
||||
|
||||
ProjectileAttack();
|
||||
|
||||
if (focus_proc_limit_timer.Check())
|
||||
FocusProcLimitProcess();
|
||||
|
||||
auto npcSpawnPoint = CastToNPC()->GetSpawnPoint();
|
||||
if (GetSpecialAbility(TETHER)) {
|
||||
float tether_range = static_cast<float>(GetSpecialAbilityParam(TETHER, 0));
|
||||
@ -1218,16 +1221,15 @@ void Mob::AI_Process() {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//SE_PC_Pet_Rampage SPA 464 on pet, chance modifier
|
||||
if ((IsPet() || IsTempPet()) && IsPetOwnerClient()) {
|
||||
if (spellbonuses.PC_Pet_Rampage[0] || itembonuses.PC_Pet_Rampage[0] ||
|
||||
aabonuses.PC_Pet_Rampage[0]) {
|
||||
int chance = spellbonuses.PC_Pet_Rampage[0] + itembonuses.PC_Pet_Rampage[0] +
|
||||
aabonuses.PC_Pet_Rampage[0];
|
||||
if (zone->random.Roll(chance)) {
|
||||
Rampage(nullptr);
|
||||
}
|
||||
int chance = spellbonuses.PC_Pet_Rampage[0] + itembonuses.PC_Pet_Rampage[0] + aabonuses.PC_Pet_Rampage[0];
|
||||
if (chance && zone->random.Roll(chance)) {
|
||||
Rampage(nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (GetSpecialAbility(SPECATK_RAMPAGE) && !specialed) {
|
||||
int rampage_chance = GetSpecialAbilityParam(SPECATK_RAMPAGE, 0);
|
||||
@ -1263,6 +1265,14 @@ void Mob::AI_Process() {
|
||||
}
|
||||
}
|
||||
|
||||
//SE_PC_Pet_Rampage SPA 465 on pet, chance modifier
|
||||
if ((IsPet() || IsTempPet()) && IsPetOwnerClient()) {
|
||||
int chance = spellbonuses.PC_Pet_AE_Rampage[0] + itembonuses.PC_Pet_AE_Rampage[0] + aabonuses.PC_Pet_AE_Rampage[0];
|
||||
if (chance && zone->random.Roll(chance)) {
|
||||
Rampage(nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
if (GetSpecialAbility(SPECATK_AREA_RAMPAGE) && !specialed) {
|
||||
int rampage_chance = GetSpecialAbilityParam(SPECATK_AREA_RAMPAGE, 0);
|
||||
rampage_chance = rampage_chance > 0 ? rampage_chance : 20;
|
||||
|
||||
@ -197,7 +197,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove
|
||||
if (!IsPowerDistModSpell(spell_id))
|
||||
SetSpellPowerDistanceMod(0);
|
||||
|
||||
bool SE_SpellTrigger_HasCast = false;
|
||||
bool spell_trigger_cast_complete = false; //Used with SE_Spell_Trigger and SE_Chance_Best_in_Spell_Grp, true when spell has been triggered.
|
||||
|
||||
// if buff slot, use instrument mod there, otherwise calc it
|
||||
uint32 instrument_mod = buffslot > -1 ? buffs[buffslot].instrument_mod : caster ? caster->GetInstrumentMod(spell_id) : 10;
|
||||
@ -2822,9 +2822,9 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove
|
||||
|
||||
case SE_SpellTrigger: {
|
||||
|
||||
if (!SE_SpellTrigger_HasCast) {
|
||||
if (!spell_trigger_cast_complete) {
|
||||
if (caster && caster->TrySpellTrigger(this, spell_id, i))
|
||||
SE_SpellTrigger_HasCast = true;
|
||||
spell_trigger_cast_complete = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -2873,8 +2873,83 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove
|
||||
HealDamage(amt, caster);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
case SE_Chance_Best_in_Spell_Grp: {
|
||||
if (!spell_trigger_cast_complete) {
|
||||
if (caster && caster->TrySpellTrigger(this, spell_id, i))
|
||||
spell_trigger_cast_complete = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case SE_Trigger_Best_in_Spell_Grp: {
|
||||
|
||||
if (caster && !caster->IsClient())
|
||||
break;
|
||||
|
||||
if (zone->random.Roll(spells[spell_id].base[i])) {
|
||||
uint32 best_spell_id = caster->CastToClient()->GetHighestScribedSpellinSpellGroup(spells[spell_id].base2[i]);
|
||||
|
||||
if (caster && IsValidSpell(best_spell_id))
|
||||
caster->SpellFinished(best_spell_id, this, EQ::spells::CastingSlot::Item, 0, -1, spells[best_spell_id].ResistDiff);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case SE_Trigger_Spell_Non_Item: {
|
||||
//Only trigger if not from item
|
||||
if (caster && caster->IsClient() && GetCastedSpellInvSlot() > 0)
|
||||
break;
|
||||
|
||||
if (zone->random.Roll(spells[spell_id].base[i]) && IsValidSpell(spells[spell_id].base2[i]))
|
||||
caster->SpellFinished(spells[spell_id].base2[i], this, EQ::spells::CastingSlot::Item, 0, -1, spells[spells[spell_id].base2[i]].ResistDiff);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case SE_Hatelist_To_Tail_Index: {
|
||||
if (caster && zone->random.Roll(spells[spell_id].base[i]))
|
||||
caster->SetBottomRampageList();
|
||||
break;
|
||||
}
|
||||
|
||||
case SE_Hatelist_To_Top_Index: {
|
||||
if (caster && zone->random.Roll(spells[spell_id].base[i]))
|
||||
caster->SetTopRampageList();
|
||||
break;
|
||||
}
|
||||
|
||||
case SE_Fearstun: {
|
||||
//Normal 'stun' restrictions do not apply. base1=duration, base2=PC duration, max =lv restrict
|
||||
if (!caster)
|
||||
break;
|
||||
|
||||
if (IsNPC() && GetSpecialAbility(UNSTUNABLE)) {
|
||||
caster->MessageString(Chat::SpellFailure, IMMUNE_STUN);
|
||||
break;
|
||||
}
|
||||
|
||||
if (IsNPC() && GetSpecialAbility(UNFEARABLE)) {
|
||||
caster->MessageString(Chat::SpellFailure, IMMUNE_FEAR);
|
||||
break;
|
||||
}
|
||||
|
||||
if (spells[spell_id].max[i] == 0 || GetLevel() <= spells[spell_id].max[i]) {
|
||||
if (IsClient() && spells[spell_id].base2[i])
|
||||
Stun(spells[spell_id].base2[i]);
|
||||
else
|
||||
Stun(spells[spell_id].base[i]);
|
||||
}
|
||||
else
|
||||
caster->MessageString(Chat::SpellFailure, FEAR_TOO_HIGH);
|
||||
break;
|
||||
}
|
||||
|
||||
case SE_Proc_Timer_Modifier:{
|
||||
buffs[buffslot].focusproclimit_procamt = spells[spell_id].base[i]; //Set max amount of procs before lockout timer
|
||||
break;
|
||||
}
|
||||
|
||||
case SE_PersistentEffect:
|
||||
MakeAura(spell_id);
|
||||
break;
|
||||
@ -2990,6 +3065,8 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove
|
||||
case SE_HealRate:
|
||||
case SE_SkillDamageTaken:
|
||||
case SE_FcSpellVulnerability:
|
||||
case SE_Fc_Spell_Damage_Pct_IncomingPC:
|
||||
case SE_Fc_Spell_Damage_Amt_IncomingPC:
|
||||
case SE_FcTwincast:
|
||||
case SE_DelayDeath:
|
||||
case SE_CastOnFadeEffect:
|
||||
@ -3131,6 +3208,12 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove
|
||||
case SE_DS_Mitigation_Amount:
|
||||
case SE_DS_Mitigation_Percentage:
|
||||
case SE_Double_Backstab_Front:
|
||||
case SE_Pet_Crit_Melee_Damage_Pct_Owner:
|
||||
case SE_Pet_Add_Atk:
|
||||
case SE_TwinCastBlocker:
|
||||
case SE_Fc_Cast_Spell_On_Land:
|
||||
case SE_Ff_CasterClass:
|
||||
case SE_Ff_Same_Caster:
|
||||
{
|
||||
break;
|
||||
}
|
||||
@ -4406,13 +4489,14 @@ int16 Client::CalcAAFocus(focusType type, const AA::Rank &rank, uint16 spell_id)
|
||||
case SE_Blank:
|
||||
break;
|
||||
|
||||
// Handle Focus Limits
|
||||
// Handle Focus Limits
|
||||
|
||||
case SE_LimitResist:
|
||||
if (base1 < 0) {
|
||||
if (spell.resisttype == -base1) // Exclude
|
||||
LimitFailure = true;
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
LimitInclude[0] = true;
|
||||
if (spell.resisttype == base1) // Include
|
||||
LimitInclude[1] = true;
|
||||
@ -4433,12 +4517,13 @@ int16 Client::CalcAAFocus(focusType type, const AA::Rank &rank, uint16 spell_id)
|
||||
// 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)) {
|
||||
RuleB(Character, ItemCastsUseFocus) == false)) {
|
||||
if (base2 > 0) {
|
||||
lvlModifier -= base2 * lvldiff;
|
||||
if (lvlModifier < 1)
|
||||
LimitFailure = true;
|
||||
} else
|
||||
}
|
||||
else
|
||||
LimitFailure = true;
|
||||
}
|
||||
break;
|
||||
@ -4462,7 +4547,8 @@ int16 Client::CalcAAFocus(focusType type, const AA::Rank &rank, uint16 spell_id)
|
||||
if (base1 < 0) { // Exclude
|
||||
if (spell_id == -base1)
|
||||
LimitFailure = true;
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
LimitInclude[2] = true;
|
||||
if (spell_id == base1) // Include
|
||||
LimitInclude[3] = true;
|
||||
@ -4479,13 +4565,15 @@ int16 Client::CalcAAFocus(focusType type, const AA::Rank &rank, uint16 spell_id)
|
||||
if (base1 < 0) {
|
||||
if (IsEffectInSpell(spell_id, -base1)) // Exclude
|
||||
LimitFailure = true;
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
LimitInclude[4] = true;
|
||||
// they use 33 here for all classes ... unsure if the type check is really needed
|
||||
if (base1 == SE_SummonPet && type == focusReagentCost) {
|
||||
if (IsSummonPetSpell(spell_id) || IsSummonSkeletonSpell(spell_id))
|
||||
LimitInclude[5] = true;
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
if (IsEffectInSpell(spell_id, base1)) // Include
|
||||
LimitInclude[5] = true;
|
||||
}
|
||||
@ -4519,7 +4607,8 @@ int16 Client::CalcAAFocus(focusType type, const AA::Rank &rank, uint16 spell_id)
|
||||
if (base1 < 0) {
|
||||
if (-base1 == spell.targettype) // Exclude
|
||||
LimitFailure = true;
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
LimitInclude[6] = true;
|
||||
if (base1 == spell.targettype) // Include
|
||||
LimitInclude[7] = true;
|
||||
@ -4538,7 +4627,8 @@ int16 Client::CalcAAFocus(focusType type, const AA::Rank &rank, uint16 spell_id)
|
||||
if (base1 < 0) {
|
||||
if (-base1 == spell.spellgroup) // Exclude
|
||||
LimitFailure = true;
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
LimitInclude[8] = true;
|
||||
if (base1 == spell.spellgroup) // Include
|
||||
LimitInclude[9] = true;
|
||||
@ -4549,7 +4639,8 @@ int16 Client::CalcAAFocus(focusType type, const AA::Rank &rank, uint16 spell_id)
|
||||
if (base1 < 0) {
|
||||
if (-base1 == spell.skill)
|
||||
LimitFailure = true;
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
LimitInclude[10] = true;
|
||||
if (base1 == spell.skill)
|
||||
LimitInclude[11] = true;
|
||||
@ -4560,7 +4651,8 @@ int16 Client::CalcAAFocus(focusType type, const AA::Rank &rank, uint16 spell_id)
|
||||
if (base1 < 0) { // Exclude
|
||||
if (CheckSpellCategory(spell_id, base1, SE_LimitSpellClass))
|
||||
return (0);
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
LimitInclude[12] = true;
|
||||
if (CheckSpellCategory(spell_id, base1, SE_LimitSpellClass)) // Include
|
||||
LimitInclude[13] = true;
|
||||
@ -4571,7 +4663,8 @@ int16 Client::CalcAAFocus(focusType type, const AA::Rank &rank, uint16 spell_id)
|
||||
if (base1 < 0) { // Exclude
|
||||
if (CheckSpellCategory(spell_id, base1, SE_LimitSpellSubclass))
|
||||
return (0);
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
LimitInclude[14] = true;
|
||||
if (CheckSpellCategory(spell_id, base1, SE_LimitSpellSubclass)) // Include
|
||||
LimitInclude[15] = true;
|
||||
@ -4600,7 +4693,12 @@ int16 Client::CalcAAFocus(focusType type, const AA::Rank &rank, uint16 spell_id)
|
||||
LimitFailure = true;
|
||||
break;
|
||||
|
||||
// Handle Focus Effects
|
||||
/* These are not applicable to AA's because there is never a 'caster' of the 'buff' with the focus effect.
|
||||
case SE_Ff_Same_Caster:
|
||||
case SE_Ff_CasterClass:
|
||||
*/
|
||||
|
||||
// Handle Focus Effects
|
||||
case SE_ImprovedDamage:
|
||||
if (type == focusImprovedDamage && base1 > value)
|
||||
value = base1;
|
||||
@ -4667,11 +4765,13 @@ int16 Client::CalcAAFocus(focusType type, const AA::Rank &rank, uint16 spell_id)
|
||||
if (value > 0) {
|
||||
if (base1 > value)
|
||||
value = base1;
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
if (base1 < value)
|
||||
value = base1;
|
||||
}
|
||||
} else
|
||||
}
|
||||
else
|
||||
value = base1;
|
||||
}
|
||||
break;
|
||||
@ -4685,7 +4785,8 @@ int16 Client::CalcAAFocus(focusType type, const AA::Rank &rank, uint16 spell_id)
|
||||
if (type == focusTriggerOnCast) {
|
||||
if (zone->random.Roll(base1)) {
|
||||
value = base2;
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
value = 0;
|
||||
LimitFailure = true;
|
||||
}
|
||||
@ -4697,6 +4798,11 @@ int16 Client::CalcAAFocus(focusType type, const AA::Rank &rank, uint16 spell_id)
|
||||
value = base1;
|
||||
break;
|
||||
|
||||
case SE_Fc_Spell_Damage_Pct_IncomingPC:
|
||||
if (type == focusFcSpellDamagePctIncomingPC)
|
||||
value = base1;
|
||||
break;
|
||||
|
||||
case SE_BlockNextSpellFocus:
|
||||
if (type == focusBlockNextSpell) {
|
||||
if (zone->random.Roll(base1))
|
||||
@ -4709,7 +4815,7 @@ int16 Client::CalcAAFocus(focusType type, const AA::Rank &rank, uint16 spell_id)
|
||||
value = base1;
|
||||
break;
|
||||
|
||||
// Note if using these as AA, make sure this is first focus used.
|
||||
// Note if using these as AA, make sure this is first focus used.
|
||||
case SE_SympatheticProc:
|
||||
if (type == focusSympatheticProc)
|
||||
value = base2;
|
||||
@ -4735,6 +4841,11 @@ int16 Client::CalcAAFocus(focusType type, const AA::Rank &rank, uint16 spell_id)
|
||||
value = base1;
|
||||
break;
|
||||
|
||||
case SE_Fc_Spell_Damage_Amt_IncomingPC:
|
||||
if (type == focusFcSpellDamageAmtIncomingPC)
|
||||
value = base1;
|
||||
break;
|
||||
|
||||
case SE_FcHealAmtIncoming:
|
||||
if (type == focusFcHealAmtIncoming)
|
||||
value = base1;
|
||||
@ -4789,6 +4900,14 @@ int16 Client::CalcAAFocus(focusType type, const AA::Rank &rank, uint16 spell_id)
|
||||
if (type == focusFcStunTimeMod)
|
||||
value = base1;
|
||||
break;
|
||||
|
||||
case SE_Fc_Cast_Spell_On_Land:
|
||||
if (type == focusFcCastSpellOnLand) {
|
||||
if (zone->random.Roll(base1)) {
|
||||
value = base2;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -4805,8 +4924,13 @@ int16 Client::CalcAAFocus(focusType type, const AA::Rank &rank, uint16 spell_id)
|
||||
|
||||
//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)
|
||||
int16 Mob::CalcFocusEffect(focusType type, uint16 focus_id, uint16 spell_id, bool best_focus, uint16 casterid)
|
||||
{
|
||||
/*
|
||||
'this' is always the caster of the spell_id, most foci check for effects on the caster, however some check for effects on the target.
|
||||
'casterid' is the casterid of the caster of spell_id, used when spell_id is cast on a target with a focus effect that is checked by incoming spell.
|
||||
*/
|
||||
|
||||
if (!IsValidSpell(focus_id) || !IsValidSpell(spell_id))
|
||||
return 0;
|
||||
|
||||
@ -5040,6 +5164,23 @@ int16 Mob::CalcFocusEffect(focusType type, uint16 focus_id, uint16 spell_id, boo
|
||||
}
|
||||
break;
|
||||
|
||||
case SE_Ff_Same_Caster://hmm do i need to pass casterid from buff slot here
|
||||
if (focus_spell.base[i] == 0) {
|
||||
if (casterid == GetID())
|
||||
return 0;//Mob casting is same as target, fail if you are casting on yourself.
|
||||
}
|
||||
else if (focus_spell.base[i] == 1) {
|
||||
if (casterid != GetID())
|
||||
return 0;//Mob casting is not same as target, fail if you are not casting on yourself.
|
||||
}
|
||||
break;
|
||||
|
||||
case SE_Ff_CasterClass:
|
||||
// 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;
|
||||
|
||||
// handle effects
|
||||
case SE_ImprovedDamage:
|
||||
if (type == focusImprovedDamage) {
|
||||
@ -5211,13 +5352,30 @@ int16 Mob::CalcFocusEffect(focusType type, uint16 focus_id, uint16 spell_id, boo
|
||||
if (type == focusSpellVulnerability) {
|
||||
if (best_focus) {
|
||||
if (focus_spell.base2[i] != 0)
|
||||
value = focus_spell.base2[i];
|
||||
value = focus_spell.base2[i]; //max damage
|
||||
else
|
||||
value = focus_spell.base[i];
|
||||
value = focus_spell.base[i]; //min damage
|
||||
} else if (focus_spell.base2[i] == 0 || focus_spell.base[i] == focus_spell.base2[i]) {
|
||||
value = focus_spell.base[i];
|
||||
value = focus_spell.base[i]; //If no max damage set, then default to min damage
|
||||
} else {
|
||||
value = zone->random.Int(focus_spell.base[i], focus_spell.base2[i]);
|
||||
value = zone->random.Int(focus_spell.base[i], focus_spell.base2[i]); //else random for value
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case SE_Fc_Spell_Damage_Pct_IncomingPC:
|
||||
if (type == focusFcSpellDamagePctIncomingPC) {
|
||||
if (best_focus) {
|
||||
if (focus_spell.base2[i] != 0)
|
||||
value = focus_spell.base2[i]; //max damage
|
||||
else
|
||||
value = focus_spell.base[i]; //min damage
|
||||
}
|
||||
else if (focus_spell.base2[i] == 0 || focus_spell.base[i] == focus_spell.base2[i]) {
|
||||
value = focus_spell.base[i]; //If no max damage set, then default to min damage
|
||||
}
|
||||
else {
|
||||
value = zone->random.Int(focus_spell.base[i], focus_spell.base2[i]); //else random for value
|
||||
}
|
||||
}
|
||||
break;
|
||||
@ -5247,6 +5405,11 @@ int16 Mob::CalcFocusEffect(focusType type, uint16 focus_id, uint16 spell_id, boo
|
||||
value = focus_spell.base[i];
|
||||
break;
|
||||
|
||||
case SE_Fc_Spell_Damage_Amt_IncomingPC:
|
||||
if (type == focusFcSpellDamageAmtIncomingPC)
|
||||
value = focus_spell.base[i];
|
||||
break;
|
||||
|
||||
case SE_FcHealAmtIncoming:
|
||||
if (type == focusFcHealAmtIncoming)
|
||||
value = focus_spell.base[i];
|
||||
@ -5307,6 +5470,14 @@ int16 Mob::CalcFocusEffect(focusType type, uint16 focus_id, uint16 spell_id, boo
|
||||
value = focus_spell.base[i];
|
||||
break;
|
||||
|
||||
case SE_Fc_Cast_Spell_On_Land:
|
||||
if (type == focusFcCastSpellOnLand) {
|
||||
if (zone->random.Roll(focus_spell.base[i])) {
|
||||
value = focus_spell.base2[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.
|
||||
@ -6232,6 +6403,7 @@ bool Mob::DoHPToManaCovert(uint16 mana_cost)
|
||||
int32 Mob::GetFcDamageAmtIncoming(Mob *caster, uint32 spell_id, bool use_skill, uint16 skill )
|
||||
{
|
||||
//Used to check focus derived from SE_FcDamageAmtIncoming which adds direct damage to Spells or Skill based attacks.
|
||||
//Used to check focus derived from SE_Fc_Spell_Damage_Amt_IncomingPC which adds direct damage to Spells.
|
||||
int32 dmg = 0;
|
||||
bool limit_exists = false;
|
||||
bool skill_found = false;
|
||||
@ -6280,6 +6452,20 @@ int32 Mob::GetFcDamageAmtIncoming(Mob *caster, uint32 spell_id, bool use_skill,
|
||||
}
|
||||
}
|
||||
}
|
||||
if (spellbonuses.FocusEffects[focusFcSpellDamageAmtIncomingPC]) {
|
||||
int buff_count = GetMaxTotalSlots();
|
||||
for (int i = 0; i < buff_count; i++) {
|
||||
|
||||
if ((IsValidSpell(buffs[i].spellid) && (IsEffectInSpell(buffs[i].spellid, SE_FcDamageAmtIncoming)))) {
|
||||
|
||||
int32 focus = caster->CalcFocusEffect(focusFcSpellDamageAmtIncomingPC, buffs[i].spellid, spell_id);
|
||||
if (focus) {
|
||||
dmg += focus;
|
||||
CheckNumHitsRemaining(NumHit::MatchingSpells, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return dmg;
|
||||
}
|
||||
|
||||
@ -6967,6 +7153,133 @@ void Mob::TryTriggerThreshHold(int32 damage, int effect_id, Mob* attacker){
|
||||
}
|
||||
}
|
||||
|
||||
void Mob::CastSpellOnLand(Mob* caster, uint32 spell_id)
|
||||
{
|
||||
/*
|
||||
This function checks for incoming spells on a mob, if they meet the criteria for focus SE_Fc_Cast_Spell_on_Land then
|
||||
a new spell will be cast by THIS mob as specified by the focus effect. Note: Chance to cast the spell is determined in
|
||||
the CalcFocusEffect function if not 100pct.
|
||||
ApplyFocusProcLimiter() function checks for SE_Proc_Timer_Modifier which allows for limiting how often a spell from effect can be triggered
|
||||
for example, if set to base=1 and base2= 1500, then for everyone 1 successful trigger, you will be unable to trigger again for 1.5 seconds.
|
||||
|
||||
Live only has this focus in buffs/debuffs that can be placed on a target. TODO: Will consider adding support for it as AA and Item.
|
||||
*/
|
||||
if (!caster)
|
||||
return;
|
||||
|
||||
uint32 trigger_spell_id = 0;
|
||||
|
||||
//Step 1: Check this focus effect exists on the mob.
|
||||
if (spellbonuses.FocusEffects[focusFcCastSpellOnLand]) {
|
||||
|
||||
int buff_count = GetMaxTotalSlots();
|
||||
for (int i = 0; i < buff_count; i++) {
|
||||
|
||||
if ((IsValidSpell(buffs[i].spellid) && (buffs[i].spellid != spell_id) && IsEffectInSpell(buffs[i].spellid, SE_Fc_Cast_Spell_On_Land))) {
|
||||
|
||||
//Step 2: Check if we pass all focus limiters and focus chance roll
|
||||
trigger_spell_id = caster->CalcFocusEffect(focusFcCastSpellOnLand, buffs[i].spellid, spell_id, false, buffs[i].casterid);
|
||||
|
||||
if (IsValidSpell(trigger_spell_id) && (trigger_spell_id != spell_id)) {
|
||||
|
||||
//Step 3: Check if SE_Proc_Time_Modifier is present and if so apply it.
|
||||
if (ApplyFocusProcLimiter(buffs[i].spellid, i)) {
|
||||
//Step 4: Cast spells
|
||||
if (IsBeneficialSpell(trigger_spell_id)) {
|
||||
SpellFinished(trigger_spell_id, this, EQ::spells::CastingSlot::Item, 0, -1, spells[trigger_spell_id].ResistDiff);
|
||||
}
|
||||
else {
|
||||
Mob* current_target = GetTarget();
|
||||
//For now don't let players cast detrimental effects on themselves if they are targeting themselves. Need to confirm behavior.
|
||||
if (current_target && current_target->GetID() != GetID())
|
||||
SpellFinished(trigger_spell_id, current_target, EQ::spells::CastingSlot::Item, 0, -1, spells[trigger_spell_id].ResistDiff);
|
||||
}
|
||||
}
|
||||
|
||||
if (i >= 0)
|
||||
CheckNumHitsRemaining(NumHit::MatchingSpells, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool Mob::ApplyFocusProcLimiter(uint32 spell_id, int buffslot)
|
||||
{
|
||||
if (buffslot < 0)
|
||||
return false;
|
||||
|
||||
//Do not allow spell cast if timer is active.
|
||||
if (buffs[buffslot].focusproclimit_time > 0)
|
||||
return false;
|
||||
|
||||
/*
|
||||
SE_Proc_Timer_Modifier
|
||||
base1= amount of total procs allowed until lock out timer is triggered, should be set to at least 1 in any spell for the effect to function.
|
||||
base2= lock out timer, which prevents any more procs set in ms 1500 = 1.5 seconds
|
||||
This system allows easy scaling for multiple different buffs with same effects each having seperate active individual timer checks. Ie.
|
||||
*/
|
||||
|
||||
if (IsValidSpell(spell_id)) {
|
||||
|
||||
for (int i = 0; i < EFFECT_COUNT; i++) {
|
||||
|
||||
//Step 1: Find which slot the spell effect is in.
|
||||
if (spells[spell_id].effectid[i] == SE_Proc_Timer_Modifier) {
|
||||
|
||||
//Step 2: Check if you still have procs left to trigger, and if so reduce available procs
|
||||
if (buffs[buffslot].focusproclimit_procamt > 0) {
|
||||
--buffs[buffslot].focusproclimit_procamt; //Reduce total amount of triggers possible.
|
||||
}
|
||||
|
||||
//Step 3: If you used all the procs in the time frame then set proc amount back to max
|
||||
if (buffs[buffslot].focusproclimit_procamt == 0 && spells[spell_id].base[i] > 0) {
|
||||
buffs[buffslot].focusproclimit_procamt = spells[spell_id].base[i];//reset to max
|
||||
|
||||
//Step 4: Check if timer exists on this spell, and then set it, and activiate global timer if not active
|
||||
if (buffs[buffslot].focusproclimit_time ==0 && spells[spell_id].base2[i] > 0) {
|
||||
buffs[buffslot].focusproclimit_time = spells[spell_id].base2[i];//set time
|
||||
|
||||
//Step 5: If timer is not already running, then start it.
|
||||
if (!focus_proc_limit_timer.Enabled()) {
|
||||
focus_proc_limit_timer.Start(250);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void Mob::FocusProcLimitProcess()
|
||||
{
|
||||
/*
|
||||
Fast 250 ms uinversal timer for checking Focus effects that have a proc rate limiter set in actual time.
|
||||
*/
|
||||
bool stop_timer = true;
|
||||
int buff_count = GetMaxTotalSlots();
|
||||
for (int buffs_i = 0; buffs_i < buff_count; ++buffs_i)
|
||||
{
|
||||
if (IsValidSpell(buffs[buffs_i].spellid))
|
||||
{
|
||||
if (buffs[buffs_i].focusproclimit_time > 0) {
|
||||
buffs[buffs_i].focusproclimit_time -= 250;
|
||||
stop_timer = false;
|
||||
}
|
||||
|
||||
if (buffs[buffs_i].focusproclimit_time < 0)
|
||||
buffs[buffs_i].focusproclimit_time = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (stop_timer) {
|
||||
focus_proc_limit_timer.Disable();
|
||||
}
|
||||
}
|
||||
|
||||
bool Mob::CheckSpellCategory(uint16 spell_id, int category_id, int effect_id){
|
||||
|
||||
if (!IsValidSpell(spell_id) || !category_id)
|
||||
|
||||
@ -406,8 +406,7 @@ bool Mob::DoCastSpell(uint16 spell_id, uint16 target_id, CastingSlot slot,
|
||||
spell.targettype == ST_Self ||
|
||||
spell.targettype == ST_AECaster ||
|
||||
spell.targettype == ST_Ring ||
|
||||
spell.targettype == ST_Beam ||
|
||||
spell.targettype == ST_TargetOptional) && target_id == 0)
|
||||
spell.targettype == ST_Beam) && target_id == 0)
|
||||
{
|
||||
LogSpells("Spell [{}] auto-targeted the caster. Group? [{}], target type [{}]", spell_id, IsGroupSpell(spell_id), spell.targettype);
|
||||
target_id = GetID();
|
||||
@ -1585,8 +1584,12 @@ bool Mob::DetermineSpellTargets(uint16 spell_id, Mob *&spell_target, Mob *&ae_ce
|
||||
|
||||
case ST_TargetOptional:
|
||||
{
|
||||
if(!spell_target)
|
||||
spell_target = this;
|
||||
if (!spell_target)
|
||||
{
|
||||
LogSpells("Spell [{}] canceled: invalid target (normal)", spell_id);
|
||||
MessageString(Chat::Red, SPELL_NEED_TAR);
|
||||
return false; // can't cast these unless we have a target
|
||||
}
|
||||
CastAction = SingleTarget;
|
||||
break;
|
||||
}
|
||||
@ -3350,6 +3353,8 @@ int Mob::AddBuff(Mob *caster, uint16 spell_id, int duration, int32 level_overrid
|
||||
buffs[emptyslot].dot_rune = 0;
|
||||
buffs[emptyslot].ExtraDIChance = 0;
|
||||
buffs[emptyslot].RootBreakChance = 0;
|
||||
buffs[emptyslot].focusproclimit_time = 0;
|
||||
buffs[emptyslot].focusproclimit_procamt = 0;
|
||||
buffs[emptyslot].instrument_mod = caster ? caster->GetInstrumentMod(spell_id) : 10;
|
||||
|
||||
if (level_override > 0) {
|
||||
@ -3972,6 +3977,10 @@ bool Mob::SpellOnTarget(uint16 spell_id, Mob *spelltar, bool reflect, bool use_r
|
||||
return false;
|
||||
}
|
||||
|
||||
//Check SE_Fc_Cast_Spell_On_Land SPA 481 on target, if hit by this spell and Conditions are Met then target will cast the specified spell.
|
||||
if (spelltar)
|
||||
spelltar->CastSpellOnLand(this, spell_id);
|
||||
|
||||
if (IsValidSpell(spells[spell_id].RecourseLink) && spells[spell_id].RecourseLink != spell_id)
|
||||
SpellFinished(spells[spell_id].RecourseLink, this, CastingSlot::Item, 0, -1, spells[spells[spell_id].RecourseLink].ResistDiff);
|
||||
|
||||
@ -5251,6 +5260,27 @@ int Client::FindSpellBookSlotBySpellID(uint16 spellid) {
|
||||
return -1; //default
|
||||
}
|
||||
|
||||
uint32 Client::GetHighestScribedSpellinSpellGroup(uint32 spell_group)
|
||||
{
|
||||
//Typical live spells follow 1/5/10 rank value for actual ranks 1/2/3, but this can technically be set as anything.
|
||||
|
||||
int highest_rank = 0; //highest ranked found in spellgroup
|
||||
uint32 highest_spell_id = 0; //spell_id of the highest ranked spell you have scribed in that spell rank.
|
||||
|
||||
for (int i = 0; i < EQ::spells::SPELLBOOK_SIZE; i++) {
|
||||
|
||||
if (IsValidSpell(m_pp.spell_book[i])) {
|
||||
if (spells[m_pp.spell_book[i]].spellgroup == spell_group) {
|
||||
if (highest_rank < spells[m_pp.spell_book[i]].rank) {
|
||||
highest_rank = spells[m_pp.spell_book[i]].rank;
|
||||
highest_spell_id = m_pp.spell_book[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return highest_spell_id;
|
||||
}
|
||||
|
||||
bool Client::SpellGlobalCheck(uint16 spell_id, uint32 char_id) {
|
||||
std::string spell_global_name;
|
||||
int spell_global_value;
|
||||
|
||||
@ -3666,6 +3666,8 @@ void ZoneDatabase::LoadBuffs(Client *client)
|
||||
buffs[slot_id].caston_z = caston_z;
|
||||
buffs[slot_id].ExtraDIChance = ExtraDIChance;
|
||||
buffs[slot_id].RootBreakChance = 0;
|
||||
buffs[slot_id].focusproclimit_time = 0;
|
||||
buffs[slot_id].focusproclimit_procamt = 0;
|
||||
buffs[slot_id].UpdateClient = false;
|
||||
buffs[slot_id].instrument_mod = instrument_mod;
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user