From 886b321e66dfdbe6848e118482ef07e098f1e107 Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Mon, 20 Dec 2021 09:47:32 -0500 Subject: [PATCH] [Spells] Rework of SPA 288 SE_SkillAttackProc (#1893) * start * updated 288 --- common/spdat.h | 3 +- zone/attack.cpp | 70 +++++++++++++++++++++++++++++++++------- zone/bonuses.cpp | 32 +++++++++++++++--- zone/common.h | 9 +++--- zone/mob.cpp | 2 ++ zone/mob.h | 3 +- zone/special_attacks.cpp | 28 +++++++--------- 7 files changed, 108 insertions(+), 39 deletions(-) diff --git a/common/spdat.h b/common/spdat.h index cf7f7b21e..9358bde69 100644 --- a/common/spdat.h +++ b/common/spdat.h @@ -189,6 +189,7 @@ #define MAX_FOCUS_PROC_LIMIT_TIMERS 20 //Number of focus recast timers that can be going at same time (This is arbitrary) #define MAX_PROC_LIMIT_TIMERS 8 //Number of proc delay timers that can be going at same time, different proc types get their own timer array. (This is arbitrary) #define MAX_APPEARANCE_EFFECTS 20 //Up to 20 Appearance Effects can be saved to a mobs appearance effect array, these will be sent to other clients when they enter a zone (This is arbitrary) +#define MAX_CAST_ON_SKILL_USE 36; //Actual amount is MAX/3 const int Z_AGGRO=10; @@ -1004,7 +1005,7 @@ typedef enum { #define SE_NimbleEvasion 285 // *not implemented - base1 = 100 for max #define SE_FcDamageAmt 286 // implemented, @Fc, On Caster, spell damage mod flat amt, base: amt #define SE_SpellDurationIncByTic 287 // implemented, @Fc, SPA: 287, SE_SpellDurationIncByTic, On Caster, spell buff duration mod, base: tics -#define SE_SkillAttackProc 288 // implemented[AA] - Chance to proc spell on skill attack usage (ex. Dragon Punch) +#define SE_SkillAttackProc 288 // implemented, @Procs, chance to cast a spell when using a skill, base: chance, limit: skill, max: spellid, note: if used in AA the spell id is set in aa_ranks spell field, chance is calculated as 100% = value 1000. #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, @Dispel, remove up specified amount of detiremental spells, base: amt removed, limit: none, max: none, Note: excluding charm, fear, resurrection, and revival sickness diff --git a/zone/attack.cpp b/zone/attack.cpp index 30b61f4a2..5568e6e15 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -1603,29 +1603,26 @@ bool Client::Attack(Mob* other, int Hand, bool bRiposte, bool IsStrikethrough, b /////////////////////////////////////////////////////////// ////// Send Attack Damage /////////////////////////////////////////////////////////// - if (my_hit.damage_done > 0 && aabonuses.SkillAttackProc[SBIndex::SKILLATK_PROC_CHANCE] && aabonuses.SkillAttackProc[SBIndex::SKILLATK_PROC_SKILL] == my_hit.skill && - IsValidSpell(aabonuses.SkillAttackProc[SBIndex::SKILLATK_PROC_SPELL_ID])) { - float chance = aabonuses.SkillAttackProc[SBIndex::SKILLATK_PROC_CHANCE] / 1000.0f; - if (zone->random.Roll(chance)) - SpellFinished(aabonuses.SkillAttackProc[SBIndex::SKILLATK_PROC_SPELL_ID], other, EQ::spells::CastingSlot::Item, 0, -1, - spells[aabonuses.SkillAttackProc[SBIndex::SKILLATK_PROC_SPELL_ID]].resist_difficulty); - } other->Damage(this, my_hit.damage_done, SPELL_UNKNOWN, my_hit.skill, true, -1, false, m_specialattacks); - if (IsDead()) return false; + if (IsDead()) { + return false; + } MeleeLifeTap(my_hit.damage_done); CommonBreakInvisibleFromCombat(); - if (GetTarget()) + if (GetTarget()) { TriggerDefensiveProcs(other, Hand, true, my_hit.damage_done); + } - if (my_hit.damage_done > 0) + if (my_hit.damage_done > 0) { return true; - - else + } + else { return false; + } } //used by complete heal and #heal @@ -4489,6 +4486,8 @@ void Mob::TrySpellProc(const EQ::ItemInstance *inst, const EQ::ItemData *weapon, } } + TryCastOnSkillUse(on, skillinuse); + if (HasSkillProcs() && hand != EQ::invslot::slotRange) { //We check ranged skill procs within the attack functions. TrySkillProc(on, skillinuse, 0, false, hand); } @@ -5272,6 +5271,53 @@ float Mob::GetSkillProcChances(uint16 ReuseTime, uint16 hand) { return ProcChance; } +void Mob::TryCastOnSkillUse(Mob *on, EQ::skills::SkillType skill) { + + if (!spellbonuses.HasSkillAttackProc[skill] && !itembonuses.HasSkillAttackProc[skill] && !aabonuses.HasSkillAttackProc[skill]) { + return; + } + + if (!on) { + SetTarget(nullptr); + LogError("A null Mob object was passed to Mob::TryCastOnSkillUse for evaluation!"); + return; + } + + if (on->HasDied()) { + return; + } + + if (spellbonuses.HasSkillAttackProc[skill]) { + for (int i = 0; i < MAX_CAST_ON_SKILL_USE i += 3) { + if (spellbonuses.SkillAttackProc[i + SBIndex::SKILLATK_PROC_SPELL_ID] && skill == spellbonuses.SkillAttackProc[i + SBIndex::SKILLATK_PROC_SKILL]) { + if (IsValidSpell(spellbonuses.SkillAttackProc[i + SBIndex::SKILLATK_PROC_SPELL_ID]) && zone->random.Int(1, 1000) <= spellbonuses.SkillAttackProc[i + SBIndex::SKILLATK_PROC_CHANCE]) { + SpellFinished(spellbonuses.SkillAttackProc[i + SBIndex::SKILLATK_PROC_SPELL_ID], on, EQ::spells::CastingSlot::Item, 0, -1, spells[spellbonuses.SkillAttackProc[i + SBIndex::SKILLATK_PROC_SPELL_ID]].resist_difficulty); + } + } + } + } + + if (itembonuses.HasSkillAttackProc[skill]) { + for (int i = 0; i < MAX_CAST_ON_SKILL_USE i += 3) { + if (itembonuses.SkillAttackProc[i + SBIndex::SKILLATK_PROC_SPELL_ID] && skill == itembonuses.SkillAttackProc[i + SBIndex::SKILLATK_PROC_SKILL]) { + if (IsValidSpell(itembonuses.SkillAttackProc[i + SBIndex::SKILLATK_PROC_SPELL_ID]) && zone->random.Int(1, 1000) <= spellbonuses.SkillAttackProc[i + SBIndex::SKILLATK_PROC_CHANCE]) { + SpellFinished(itembonuses.SkillAttackProc[i + SBIndex::SKILLATK_PROC_SPELL_ID], on, EQ::spells::CastingSlot::Item, 0, -1, spells[itembonuses.SkillAttackProc[i + SBIndex::SKILLATK_PROC_SPELL_ID]].resist_difficulty); + } + } + } + } + + if (aabonuses.HasSkillAttackProc[skill]) { + for (int i = 0; i < MAX_CAST_ON_SKILL_USE i += 3) { + if (IsValidSpell(aabonuses.SkillAttackProc[i + SBIndex::SKILLATK_PROC_SPELL_ID]) && aabonuses.SkillAttackProc[i + SBIndex::SKILLATK_PROC_SPELL_ID] && skill == aabonuses.SkillAttackProc[i + SBIndex::SKILLATK_PROC_SKILL]) { + if (zone->random.Int(1, 1000) <= aabonuses.SkillAttackProc[i + SBIndex::SKILLATK_PROC_CHANCE]) { + SpellFinished(aabonuses.SkillAttackProc[i + SBIndex::SKILLATK_PROC_SPELL_ID], on, EQ::spells::CastingSlot::Item, 0, -1, spells[aabonuses.SkillAttackProc[i + SBIndex::SKILLATK_PROC_SPELL_ID]].resist_difficulty); + } + } + } + } +} + bool Mob::TryRootFadeByDamage(int buffslot, Mob* attacker) { /*Dev Quote 2010: http://forums.station.sony.com/eq/posts/list.m?topic_id=161443 diff --git a/zone/bonuses.cpp b/zone/bonuses.cpp index 5684abfc4..04ab98239 100644 --- a/zone/bonuses.cpp +++ b/zone/bonuses.cpp @@ -1207,11 +1207,18 @@ void Mob::ApplyAABonuses(const AA::Rank &rank, StatBonuses *newbon) } case SE_SkillAttackProc: { - // You can only have one of these per client. [AA Dragon Punch] - newbon->SkillAttackProc[SBIndex::SKILLATK_PROC_CHANCE] = base_value; // Chance base 1000 = 100% proc rate - newbon->SkillAttackProc[SBIndex::SKILLATK_PROC_SKILL] = limit_value; // Skill to Proc Off - newbon->SkillAttackProc[SBIndex::SKILLATK_PROC_SPELL_ID] = rank.spell; // spell to proc - break; + for (int i = 0; i < MAX_CAST_ON_SKILL_USE i += 3) { + if (!newbon->SkillAttackProc[i + SBIndex::SKILLATK_PROC_SPELL_ID]) { // spell id + newbon->SkillAttackProc[i + SBIndex::SKILLATK_PROC_SPELL_ID] = rank.spell; // spell to proc + newbon->SkillAttackProc[i + SBIndex::SKILLATK_PROC_CHANCE] = base_value; // Chance base 1000 = 100% proc rate + newbon->SkillAttackProc[i + SBIndex::SKILLATK_PROC_SKILL] = limit_value; // Skill to Proc Offr + + if (limit_value < EQ::skills::HIGHEST_SKILL) { + newbon->HasSkillAttackProc[limit_value] = true; //check first before looking for any effects. + } + break; + } + } } case SE_DamageModifier: { @@ -3578,6 +3585,21 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne break; } + case SE_SkillAttackProc: { + for (int i = 0; i < MAX_CAST_ON_SKILL_USE i += 3) { + if (!new_bonus->SkillAttackProc[i + SBIndex::SKILLATK_PROC_SPELL_ID]) { // spell id + new_bonus->SkillAttackProc[i + SBIndex::SKILLATK_PROC_SPELL_ID] = max_value; // spell to proc + new_bonus->SkillAttackProc[i + SBIndex::SKILLATK_PROC_CHANCE] = effect_value; // Chance base 1000 = 100% proc rate + new_bonus->SkillAttackProc[i + SBIndex::SKILLATK_PROC_SKILL] = limit_value; // Skill to Proc Offr + + if (limit_value < EQ::skills::HIGHEST_SKILL) { + new_bonus->HasSkillAttackProc[limit_value] = true; //check first before looking for any effects. + } + break; + } + } + } + case SE_PC_Pet_Rampage: { new_bonus->PC_Pet_Rampage[SBIndex::PET_RAMPAGE_CHANCE] += effect_value; //Chance to rampage if (new_bonus->PC_Pet_Rampage[SBIndex::PET_RAMPAGE_DMG_MOD] < limit_value) diff --git a/zone/common.h b/zone/common.h index a853b564e..a7a5f07a0 100644 --- a/zone/common.h +++ b/zone/common.h @@ -572,6 +572,7 @@ struct StatBonuses { uint8 IncreaseRunSpeedCap; // Increase max run speed above cap. int32 DoubleSpecialAttack; // Chance to to perform a double special attack (ie flying kick 2x) int32 SkillAttackProc[3]; // [0] chance to proc [2] spell on [1] skill usage + bool HasSkillAttackProc[EQ::skills::HIGHEST_SKILL + 1]; //check if any skill proc is present before assessing for all skill procs 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. @@ -671,9 +672,9 @@ namespace SBIndex { constexpr uint16 POSITION_FRONT = 1; // SPA 503-506 constexpr uint16 PET_RAMPAGE_CHANCE = 0; // SPA 464,465 constexpr uint16 PET_RAMPAGE_DMG_MOD = 1; // SPA 465,465 - constexpr uint16 SKILLATK_PROC_CHANCE = 0; // SPA 427 - constexpr uint16 SKILLATK_PROC_SKILL = 1; // SPA 427 - constexpr uint16 SKILLATK_PROC_SPELL_ID = 2; // SPA 427 + constexpr uint16 SKILLATK_PROC_SPELL_ID = 0; // SPA 288 + constexpr uint16 SKILLATK_PROC_CHANCE = 1; // SPA 288 + constexpr uint16 SKILLATK_PROC_SKILL = 2; // SPA 288 constexpr uint16 SLAYUNDEAD_RATE_MOD = 0; // SPA 219 constexpr uint16 SLAYUNDEAD_DMG_MOD = 1; // SPA 219 constexpr uint16 DOUBLE_RIPOSTE_CHANCE = 0; // SPA 223 @@ -692,7 +693,7 @@ namespace SBIndex { constexpr uint16 COMBAT_PROC_ORIGIN_ID = 0; // SPA constexpr uint16 COMBAT_PROC_SPELL_ID = 1; // SPA constexpr uint16 COMBAT_PROC_RATE_MOD = 2; // SPA - constexpr uint16 COMBAT_PROC_REUSE_TIMER = 3; // SPA + constexpr uint16 COMBAT_PROC_REUSE_TIMER = 3; // SPA }; diff --git a/zone/mob.cpp b/zone/mob.cpp index f4e4582be..87b315612 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -4242,6 +4242,8 @@ void Mob::TriggerDefensiveProcs(Mob *on, uint16 hand, bool FromSkillProc, int da break; } + TryCastOnSkillUse(on, skillinuse); + if (on->HasSkillProcs()) { on->TrySkillProc(this, skillinuse, 0, false, hand, true); } diff --git a/zone/mob.h b/zone/mob.h index 0ee5a76c2..34c991177 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -1447,9 +1447,10 @@ protected: bool spawned; void CalcSpellBonuses(StatBonuses* newbon); virtual void CalcBonuses(); - void TrySkillProc(Mob *on, EQ::skills::SkillType skill, uint16 ReuseTime, bool Success = false, uint16 hand = 0, bool IsDefensive = false); // hand = SlotCharm? + void TrySkillProc(Mob *on, EQ::skills::SkillType skill, uint16 ReuseTime, bool Success = false, uint16 hand = 0, bool IsDefensive = false); // hand if 0 means its a skill ability for proc rate checks, otherwise hand is passed. bool PassLimitToSkill(EQ::skills::SkillType skill, int32 spell_id, int proc_type, int aa_id=0); bool PassLimitClass(uint32 Classes_, uint16 Class_); + void TryCastOnSkillUse(Mob *on, EQ::skills::SkillType skill); void TryDefensiveProc(Mob *on, uint16 hand = EQ::invslot::slotPrimary); void TryWeaponProc(const EQ::ItemInstance* inst, const EQ::ItemData* weapon, Mob *on, uint16 hand = EQ::invslot::slotPrimary); void TrySpellProc(const EQ::ItemInstance* inst, const EQ::ItemData* weapon, Mob *on, uint16 hand = EQ::invslot::slotPrimary); diff --git a/zone/special_attacks.cpp b/zone/special_attacks.cpp index a8c6f932d..1e86a6792 100644 --- a/zone/special_attacks.cpp +++ b/zone/special_attacks.cpp @@ -196,14 +196,6 @@ void Mob::DoSpecialAttackDamage(Mob *who, EQ::skills::SkillType skill, int32 bas DoAttack(who, my_hit); who->AddToHateList(this, hate, 0); - if (my_hit.damage_done > 0 && aabonuses.SkillAttackProc[SBIndex::SKILLATK_PROC_CHANCE] && aabonuses.SkillAttackProc[SBIndex::SKILLATK_PROC_SKILL] == skill && - IsValidSpell(aabonuses.SkillAttackProc[SBIndex::SKILLATK_PROC_SPELL_ID])) { - float chance = aabonuses.SkillAttackProc[SBIndex::SKILLATK_PROC_CHANCE] / 1000.0f; - if (zone->random.Roll(chance)) - SpellFinished(aabonuses.SkillAttackProc[SBIndex::SKILLATK_PROC_SPELL_ID], who, EQ::spells::CastingSlot::Item, 0, -1, - spells[aabonuses.SkillAttackProc[SBIndex::SKILLATK_PROC_SPELL_ID]].resist_difficulty); - } - who->Damage(this, my_hit.damage_done, SPELL_UNKNOWN, skill, false); // Make sure 'this' has not killed the target and 'this' is not dead (Damage shield ect). @@ -212,6 +204,8 @@ void Mob::DoSpecialAttackDamage(Mob *who, EQ::skills::SkillType skill, int32 bas if (HasDied()) return; + TryCastOnSkillUse(who, skill); + if (HasSkillProcs()) { TrySkillProc(who, skill, ReuseTime * 1000); } @@ -916,6 +910,8 @@ void Mob::DoArcheryAttackDmg(Mob *other, const EQ::ItemInstance *RangeWeapon, co TryWeaponProc(Ammo, Ammo->GetItem(), other, EQ::invslot::slotRange); } + TryCastOnSkillUse(other, EQ::skills::SkillArchery); + // Skill Proc Attempt if (HasSkillProcs() && other && !other->HasDied()) { if (ReuseTime) { @@ -1279,6 +1275,8 @@ void NPC::DoRangedAttackDmg(Mob* other, bool Launch, int16 damage_mod, int16 cha TrySpellProc(nullptr, (const EQ::ItemData*)nullptr, other, EQ::invslot::slotRange); } + TryCastOnSkillUse(other, skillInUse); + if (HasSkillProcs() && other && !other->HasDied()) { TrySkillProc(other, skillInUse, 0, false, EQ::invslot::slotRange); } @@ -2046,6 +2044,8 @@ void Mob::Taunt(NPC *who, bool always_succeed, int chance_bonus, bool FromSpell, MessageString(Chat::SpellFailure, FAILED_TAUNT); } + TryCastOnSkillUse(who, EQ::skills::SkillTaunt); + if (HasSkillProcs()) { TrySkillProc(who, EQ::skills::SkillTaunt, TauntReuseTime * 1000); } @@ -2101,6 +2101,8 @@ void Mob::InstillDoubt(Mob *who) { }*/ } + TryCastOnSkillUse(who, EQ::skills::SkillIntimidation); + if (HasSkillProcs()) { TrySkillProc(who, EQ::skills::SkillIntimidation, InstillDoubtReuseTime * 1000); } @@ -2253,19 +2255,13 @@ void Mob::DoMeleeSkillAttackDmg(Mob *other, uint16 weapon_damage, EQ::skills::Sk } other->AddToHateList(this, hate, 0); - if (damage > 0 && aabonuses.SkillAttackProc[SBIndex::SKILLATK_PROC_CHANCE] && aabonuses.SkillAttackProc[SBIndex::SKILLATK_PROC_SKILL] == skillinuse && - IsValidSpell(aabonuses.SkillAttackProc[SBIndex::SKILLATK_PROC_SPELL_ID])) { - float chance = aabonuses.SkillAttackProc[SBIndex::SKILLATK_PROC_CHANCE] / 1000.0f; - if (zone->random.Roll(chance)) - SpellFinished(aabonuses.SkillAttackProc[SBIndex::SKILLATK_PROC_SPELL_ID], other, EQ::spells::CastingSlot::Item, 0, -1, - spells[aabonuses.SkillAttackProc[SBIndex::SKILLATK_PROC_SPELL_ID]].resist_difficulty); - } - other->Damage(this, damage, SPELL_UNKNOWN, skillinuse); if (HasDied()) return; + TryCastOnSkillUse(other, skillinuse); + if (CanSkillProc && HasSkillProcs()) { TrySkillProc(other, skillinuse, ReuseTime); }