[Spells] Throwing procs fixed and other proc updates (#1871)

* first updating sbindex defines

* updates

* updates

* proctypes added for organization

* debug

* updates

* range procs cleaned up

* skill proc clean up

* fix

* remove debugs

* [Spells] Throwing procs fixed and other proc updates

* [Spells] Throwing procs fixed and other proc updates

bot fix

* [Spells] Throwing procs fixed and other proc updates

proctype updates
This commit is contained in:
KayenEQ 2021-12-14 12:34:51 -05:00 committed by GitHub
parent 73acc3310c
commit 119b2d023f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 393 additions and 253 deletions

View File

@ -1609,19 +1609,19 @@ uint32 GetProcLimitTimer(int32 spell_id, int proc_type) {
bool use_next_timer = false; bool use_next_timer = false;
for (int i = 0; i < EFFECT_COUNT; ++i) { for (int i = 0; i < EFFECT_COUNT; ++i) {
if (proc_type == SE_WeaponProc) { if (proc_type == ProcType::MELEE_PROC) {
if (spells[spell_id].effect_id[i] == SE_WeaponProc || spells[spell_id].effect_id[i] == SE_AddMeleeProc) { if (spells[spell_id].effect_id[i] == SE_WeaponProc || spells[spell_id].effect_id[i] == SE_AddMeleeProc) {
use_next_timer = true; use_next_timer = true;
} }
} }
if (proc_type == SE_RangedProc) { if (proc_type == ProcType::RANGED_PROC) {
if (spells[spell_id].effect_id[i] == SE_RangedProc) { if (spells[spell_id].effect_id[i] == SE_RangedProc) {
use_next_timer = true; use_next_timer = true;
} }
} }
if (proc_type == SE_DefensiveProc) { if (proc_type == ProcType::DEFENSIVE_PROC) {
if (spells[spell_id].effect_id[i] == SE_DefensiveProc) { if (spells[spell_id].effect_id[i] == SE_DefensiveProc) {
use_next_timer = true; use_next_timer = true;
} }

View File

@ -533,6 +533,15 @@ enum ReflectSpellType
RELFECT_ALL_SINGLE_TARGET_SPELLS = 3, RELFECT_ALL_SINGLE_TARGET_SPELLS = 3,
REFLECT_ALL_SPELLS = 4, REFLECT_ALL_SPELLS = 4,
}; };
//For better organizing in proc effects, not used in spells.
enum ProcType
{
MELEE_PROC = 1,
RANGED_PROC = 2,
DEFENSIVE_PROC = 3,
SKILL_PROC = 4,
SKILL_PROC_SUCCESS = 5,
};
enum SpellTypes : uint32 enum SpellTypes : uint32
{ {
@ -1134,8 +1143,8 @@ typedef enum {
#define SE_GravityEffect 424 // implemented - Pulls/pushes you toward/away the mob at a set pace #define SE_GravityEffect 424 // implemented - Pulls/pushes you toward/away the mob at a set pace
//#define SE_Display 425 // *not implemented - Illusion: Flying Dragon(21626) //#define SE_Display 425 // *not implemented - Illusion: Flying Dragon(21626)
#define SE_IncreaseExtTargetWindow 426 // *not implmented[AA] - increases the capacity of your extended target window #define SE_IncreaseExtTargetWindow 426 // *not implmented[AA] - increases the capacity of your extended target window
#define SE_SkillProc 427 // implemented - chance to proc when using a skill(ie taunt) #define SE_SkillProcAttempt 427 // implemented - chance to proc when using a skill(ie taunt)
#define SE_LimitToSkill 428 // implemented - limits what skills will effect a skill proc #define SE_LimitToSkill 428 // implemented, @Procs, limits what combat skills will effect a skill proc, base: skill value, limit: none, max: none
#define SE_SkillProcSuccess 429 // implemented - chance to proc when tje skill in use successfully fires. #define SE_SkillProcSuccess 429 // implemented - chance to proc when tje skill in use successfully fires.
//#define SE_PostEffect 430 // *not implemented - Fear of the Dark(27641) - Alters vision //#define SE_PostEffect 430 // *not implemented - Fear of the Dark(27641) - Alters vision
//#define SE_PostEffectData 431 // *not implemented - Fear of the Dark(27641) - Alters vision //#define SE_PostEffectData 431 // *not implemented - Fear of the Dark(27641) - Alters vision

View File

@ -1603,12 +1603,12 @@ bool Client::Attack(Mob* other, int Hand, bool bRiposte, bool IsStrikethrough, b
/////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////
////// Send Attack Damage ////// Send Attack Damage
/////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////
if (my_hit.damage_done > 0 && aabonuses.SkillAttackProc[SBIndex::SKILLPROC_CHANCE] && aabonuses.SkillAttackProc[SBIndex::SKILLPROC_SKILL] == my_hit.skill && 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::SKILLPROC_SPELL_ID])) { IsValidSpell(aabonuses.SkillAttackProc[SBIndex::SKILLATK_PROC_SPELL_ID])) {
float chance = aabonuses.SkillAttackProc[SBIndex::SKILLPROC_CHANCE] / 1000.0f; float chance = aabonuses.SkillAttackProc[SBIndex::SKILLATK_PROC_CHANCE] / 1000.0f;
if (zone->random.Roll(chance)) if (zone->random.Roll(chance))
SpellFinished(aabonuses.SkillAttackProc[SBIndex::SKILLPROC_SPELL_ID], other, EQ::spells::CastingSlot::Item, 0, -1, SpellFinished(aabonuses.SkillAttackProc[SBIndex::SKILLATK_PROC_SPELL_ID], other, EQ::spells::CastingSlot::Item, 0, -1,
spells[aabonuses.SkillAttackProc[SBIndex::SKILLPROC_SPELL_ID]].resist_difficulty); 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); other->Damage(this, my_hit.damage_done, SPELL_UNKNOWN, my_hit.skill, true, -1, false, m_specialattacks);
@ -1616,9 +1616,6 @@ bool Client::Attack(Mob* other, int Hand, bool bRiposte, bool IsStrikethrough, b
MeleeLifeTap(my_hit.damage_done); MeleeLifeTap(my_hit.damage_done);
if (my_hit.damage_done > 0 && HasSkillProcSuccess() && other && other->GetHP() > 0)
TrySkillProc(other, my_hit.skill, 0, true, Hand);
CommonBreakInvisibleFromCombat(); CommonBreakInvisibleFromCombat();
if (GetTarget()) if (GetTarget())
@ -3471,7 +3468,6 @@ bool Mob::HasDefensiveProcs() const
bool Mob::HasSkillProcs() const bool Mob::HasSkillProcs() const
{ {
for (int i = 0; i < MAX_SKILL_PROCS; i++) { for (int i = 0; i < MAX_SKILL_PROCS; i++) {
if (spellbonuses.SkillProc[i] || itembonuses.SkillProc[i] || aabonuses.SkillProc[i]) if (spellbonuses.SkillProc[i] || itembonuses.SkillProc[i] || aabonuses.SkillProc[i])
return true; return true;
@ -4164,12 +4160,12 @@ void Mob::TryDefensiveProc(Mob *on, uint16 hand) {
//Spell Procs and Quest added procs //Spell Procs and Quest added procs
for (int i = 0; i < MAX_PROCS; i++) { for (int i = 0; i < MAX_PROCS; i++) {
if (IsValidSpell(DefensiveProcs[i].spellID)) { if (IsValidSpell(DefensiveProcs[i].spellID)) {
if (!IsProcLimitTimerActive(DefensiveProcs[i].base_spellID, DefensiveProcs[i].proc_reuse_time, SE_DefensiveProc)) { if (!IsProcLimitTimerActive(DefensiveProcs[i].base_spellID, DefensiveProcs[i].proc_reuse_time, ProcType::DEFENSIVE_PROC)) {
float chance = ProcChance * (static_cast<float>(DefensiveProcs[i].chance) / 100.0f); float chance = ProcChance * (static_cast<float>(DefensiveProcs[i].chance) / 100.0f);
if (zone->random.Roll(chance)) { if (zone->random.Roll(chance)) {
ExecWeaponProc(nullptr, DefensiveProcs[i].spellID, on); ExecWeaponProc(nullptr, DefensiveProcs[i].spellID, on);
CheckNumHitsRemaining(NumHit::DefensiveSpellProcs, 0, DefensiveProcs[i].base_spellID); CheckNumHitsRemaining(NumHit::DefensiveSpellProcs, 0, DefensiveProcs[i].base_spellID);
SetProcLimitTimer(DefensiveProcs[i].base_spellID, DefensiveProcs[i].proc_reuse_time, SE_DefensiveProc); SetProcLimitTimer(DefensiveProcs[i].base_spellID, DefensiveProcs[i].proc_reuse_time, ProcType::DEFENSIVE_PROC);
} }
} }
} }
@ -4178,17 +4174,17 @@ void Mob::TryDefensiveProc(Mob *on, uint16 hand) {
//AA Procs //AA Procs
if (IsClient()){ if (IsClient()){
for (int i = 0; i < MAX_AA_PROCS; i += 4) { for (int i = 0; i < MAX_AA_PROCS; i += 4) {
int32 aa_rank_id = aabonuses.DefensiveProc[i]; int32 aa_rank_id = aabonuses.DefensiveProc[i + +SBIndex::COMBAT_PROC_ORIGIN_ID];
int32 aa_spell_id = aabonuses.DefensiveProc[i + 1]; int32 aa_spell_id = aabonuses.DefensiveProc[i + SBIndex::COMBAT_PROC_SPELL_ID];
int32 aa_proc_chance = 100 + aabonuses.DefensiveProc[i + 2]; int32 aa_proc_chance = 100 + aabonuses.DefensiveProc[i + SBIndex::COMBAT_PROC_RATE_MOD];
uint32 aa_proc_reuse_timer = aabonuses.DefensiveProc[i + 3]; uint32 aa_proc_reuse_timer = aabonuses.DefensiveProc[i + SBIndex::COMBAT_PROC_REUSE_TIMER];
if (aa_rank_id) { if (aa_rank_id) {
if (!IsProcLimitTimerActive(-aa_rank_id, aa_proc_reuse_timer, SE_DefensiveProc)) { if (!IsProcLimitTimerActive(-aa_rank_id, aa_proc_reuse_timer, ProcType::DEFENSIVE_PROC)) {
float chance = ProcChance * (static_cast<float>(aa_proc_chance) / 100.0f); float chance = ProcChance * (static_cast<float>(aa_proc_chance) / 100.0f);
if (zone->random.Roll(chance) && IsValidSpell(aa_spell_id)) { if (zone->random.Roll(chance) && IsValidSpell(aa_spell_id)) {
ExecWeaponProc(nullptr, aa_spell_id, on); ExecWeaponProc(nullptr, aa_spell_id, on);
SetProcLimitTimer(-aa_rank_id, aa_proc_reuse_timer, SE_DefensiveProc); SetProcLimitTimer(-aa_rank_id, aa_proc_reuse_timer, ProcType::DEFENSIVE_PROC);
} }
} }
} }
@ -4197,7 +4193,8 @@ void Mob::TryDefensiveProc(Mob *on, uint16 hand) {
} }
} }
void Mob::TryWeaponProc(const EQ::ItemInstance* weapon_g, Mob *on, uint16 hand) { void Mob::TryCombatProcs(const EQ::ItemInstance* weapon_g, Mob *on, uint16 hand, const EQ::ItemData* weapon_data) {
if (!on) { if (!on) {
SetTarget(nullptr); SetTarget(nullptr);
LogError("A null Mob object was passed to Mob::TryWeaponProc for evaluation!"); LogError("A null Mob object was passed to Mob::TryWeaponProc for evaluation!");
@ -4214,6 +4211,13 @@ void Mob::TryWeaponProc(const EQ::ItemInstance* weapon_g, Mob *on, uint16 hand)
return; return;
} }
//used for special case when checking last ammo item on projectile hit.
if (!weapon_g && weapon_data) {
TryWeaponProc(nullptr, weapon_data, on, hand);
TrySpellProc(nullptr, weapon_data, on, hand);
return;
}
if (!weapon_g) { if (!weapon_g) {
TrySpellProc(nullptr, (const EQ::ItemData*)nullptr, on); TrySpellProc(nullptr, (const EQ::ItemData*)nullptr, on);
return; return;
@ -4253,7 +4257,7 @@ void Mob::TryWeaponProc(const EQ::ItemInstance *inst, const EQ::ItemData *weapon
// Try innate proc on weapon // Try innate proc on weapon
// We can proc once here, either weapon or one aug // We can proc once here, either weapon or one aug
bool proced = false; // silly bool to prevent augs from going if weapon does bool proced = false; // silly bool to prevent augs from going if weapon does
skillinuse = GetSkillByItemType(weapon->ItemType);
if (weapon->Proc.Type == EQ::item::ItemEffectCombatProc && IsValidSpell(weapon->Proc.Effect)) { if (weapon->Proc.Type == EQ::item::ItemEffectCombatProc && IsValidSpell(weapon->Proc.Effect)) {
float WPC = ProcChance * (100.0f + // Proc chance for this weapon float WPC = ProcChance * (100.0f + // Proc chance for this weapon
static_cast<float>(weapon->ProcRate)) / 100.0f; static_cast<float>(weapon->ProcRate)) / 100.0f;
@ -4330,8 +4334,16 @@ void Mob::TrySpellProc(const EQ::ItemInstance *inst, const EQ::ItemData *weapon,
float ProcChance = 0.0f; float ProcChance = 0.0f;
ProcChance = GetProcChances(ProcBonus, hand); ProcChance = GetProcChances(ProcBonus, hand);
if (hand == EQ::invslot::slotSecondary) bool passed_skill_limit_check = true;
EQ::skills::SkillType skillinuse = EQ::skills::SkillHandtoHand;
if (weapon){
skillinuse = GetSkillByItemType(weapon->ItemType);
}
if (hand == EQ::invslot::slotSecondary) {
ProcChance /= 2; ProcChance /= 2;
}
bool rangedattk = false; bool rangedattk = false;
if (weapon && hand == EQ::invslot::slotRange) { if (weapon && hand == EQ::invslot::slotRange) {
@ -4343,8 +4355,9 @@ void Mob::TrySpellProc(const EQ::ItemInstance *inst, const EQ::ItemData *weapon,
} }
} }
if (!weapon && hand == EQ::invslot::slotRange && GetSpecialAbility(SPECATK_RANGED_ATK)) if (!weapon && hand == EQ::invslot::slotRange && GetSpecialAbility(SPECATK_RANGED_ATK)) {
rangedattk = true; rangedattk = true;
}
int16 poison_slot=-1; int16 poison_slot=-1;
@ -4353,8 +4366,9 @@ void Mob::TrySpellProc(const EQ::ItemInstance *inst, const EQ::ItemData *weapon,
continue; // If pets ever can proc from off hand, this will need to change continue; // If pets ever can proc from off hand, this will need to change
if (SpellProcs[i].base_spellID == POISON_PROC && if (SpellProcs[i].base_spellID == POISON_PROC &&
(!weapon || weapon->ItemType != EQ::item::ItemType1HPiercing)) (!weapon || weapon->ItemType != EQ::item::ItemType1HPiercing)) {
continue; // Old school poison will only proc with 1HP equipped. continue; // Old school poison will only proc with 1HP equipped.
}
// Not ranged // Not ranged
if (!rangedattk) { if (!rangedattk) {
@ -4376,13 +4390,15 @@ void Mob::TrySpellProc(const EQ::ItemInstance *inst, const EQ::ItemData *weapon,
continue; // Process the poison proc last per @mackal continue; // Process the poison proc last per @mackal
} }
if (!IsProcLimitTimerActive(SpellProcs[i].base_spellID, SpellProcs[i].proc_reuse_time, SE_WeaponProc)) { passed_skill_limit_check = PassLimitToSkill(skillinuse, SpellProcs[i].base_spellID, ProcType::MELEE_PROC);
if (passed_skill_limit_check && !IsProcLimitTimerActive(SpellProcs[i].base_spellID, SpellProcs[i].proc_reuse_time, ProcType::MELEE_PROC)) {
float chance = ProcChance * (static_cast<float>(SpellProcs[i].chance) / 100.0f); float chance = ProcChance * (static_cast<float>(SpellProcs[i].chance) / 100.0f);
if (zone->random.Roll(chance)) { if (zone->random.Roll(chance)) {
LogCombat("Spell proc [{}] procing spell [{}] ([{}] percent chance)", i, SpellProcs[i].spellID, chance); LogCombat("Spell proc [{}] procing spell [{}] ([{}] percent chance)", i, SpellProcs[i].spellID, chance);
SendBeginCast(SpellProcs[i].spellID, 0); SendBeginCast(SpellProcs[i].spellID, 0);
ExecWeaponProc(nullptr, SpellProcs[i].spellID, on, SpellProcs[i].level_override); ExecWeaponProc(nullptr, SpellProcs[i].spellID, on, SpellProcs[i].level_override);
SetProcLimitTimer(SpellProcs[i].base_spellID, SpellProcs[i].proc_reuse_time, SE_WeaponProc); SetProcLimitTimer(SpellProcs[i].base_spellID, SpellProcs[i].proc_reuse_time, ProcType::MELEE_PROC);
CheckNumHitsRemaining(NumHit::OffensiveSpellProcs, 0, SpellProcs[i].base_spellID); CheckNumHitsRemaining(NumHit::OffensiveSpellProcs, 0, SpellProcs[i].base_spellID);
} }
else { else {
@ -4395,13 +4411,15 @@ void Mob::TrySpellProc(const EQ::ItemInstance *inst, const EQ::ItemData *weapon,
// ranged spell procs (buffs) // ranged spell procs (buffs)
if (RangedProcs[i].spellID != SPELL_UNKNOWN) { if (RangedProcs[i].spellID != SPELL_UNKNOWN) {
if (!IsProcLimitTimerActive(RangedProcs[i].base_spellID, RangedProcs[i].proc_reuse_time, SE_RangedProc)) { passed_skill_limit_check = PassLimitToSkill(skillinuse, RangedProcs[i].base_spellID, ProcType::RANGED_PROC);
if (passed_skill_limit_check && !IsProcLimitTimerActive(RangedProcs[i].base_spellID, RangedProcs[i].proc_reuse_time, ProcType::RANGED_PROC)) {
float chance = ProcChance * (static_cast<float>(RangedProcs[i].chance) / 100.0f); float chance = ProcChance * (static_cast<float>(RangedProcs[i].chance) / 100.0f);
if (zone->random.Roll(chance)) { if (zone->random.Roll(chance)) {
LogCombat("Ranged proc [{}] procing spell [{}] ([{}] percent chance)", i, RangedProcs[i].spellID, chance); LogCombat("Ranged proc [{}] procing spell [{}] ([{}] percent chance)", i, RangedProcs[i].spellID, chance);
ExecWeaponProc(nullptr, RangedProcs[i].spellID, on); ExecWeaponProc(nullptr, RangedProcs[i].spellID, on);
CheckNumHitsRemaining(NumHit::OffensiveSpellProcs, 0, RangedProcs[i].base_spellID); CheckNumHitsRemaining(NumHit::OffensiveSpellProcs, 0, RangedProcs[i].base_spellID);
SetProcLimitTimer(RangedProcs[i].base_spellID, RangedProcs[i].proc_reuse_time, SE_RangedProc); SetProcLimitTimer(RangedProcs[i].base_spellID, RangedProcs[i].proc_reuse_time, ProcType::RANGED_PROC);
} }
else { else {
LogCombat("Ranged proc [{}] failed to proc [{}] ([{}] percent chance)", i, RangedProcs[i].spellID, chance); LogCombat("Ranged proc [{}] failed to proc [{}] ([{}] percent chance)", i, RangedProcs[i].spellID, chance);
@ -4411,7 +4429,7 @@ void Mob::TrySpellProc(const EQ::ItemInstance *inst, const EQ::ItemData *weapon,
} }
} }
//AA Procs //AA Melee and Ranged Procs
if (IsClient()) { if (IsClient()) {
for (int i = 0; i < MAX_AA_PROCS; i += 4) { for (int i = 0; i < MAX_AA_PROCS; i += 4) {
@ -4423,22 +4441,25 @@ void Mob::TrySpellProc(const EQ::ItemInstance *inst, const EQ::ItemData *weapon,
if (!rangedattk) { if (!rangedattk) {
aa_rank_id = aabonuses.SpellProc[i]; aa_rank_id = aabonuses.SpellProc[i + SBIndex::COMBAT_PROC_ORIGIN_ID];
aa_spell_id = aabonuses.SpellProc[i + 1]; aa_spell_id = aabonuses.SpellProc[i + SBIndex::COMBAT_PROC_SPELL_ID];
aa_proc_chance += aabonuses.SpellProc[i + 2]; aa_proc_chance += aabonuses.SpellProc[i + SBIndex::COMBAT_PROC_RATE_MOD];
aa_proc_reuse_timer = aabonuses.SpellProc[i + 3]; aa_proc_reuse_timer = aabonuses.SpellProc[i + SBIndex::COMBAT_PROC_RATE_MOD];
proc_type = SE_WeaponProc; proc_type = ProcType::MELEE_PROC;
} }
else { else {
aa_rank_id = aabonuses.RangedProc[i]; aa_rank_id = aabonuses.RangedProc[i + SBIndex::COMBAT_PROC_ORIGIN_ID];
aa_spell_id = aabonuses.RangedProc[i + 1]; aa_spell_id = aabonuses.RangedProc[i + SBIndex::COMBAT_PROC_SPELL_ID];
aa_proc_chance += aabonuses.RangedProc[i + 2]; aa_proc_chance += aabonuses.RangedProc[i + SBIndex::COMBAT_PROC_RATE_MOD];
aa_proc_reuse_timer = aabonuses.RangedProc[i + 3]; aa_proc_reuse_timer = aabonuses.RangedProc[i + SBIndex::COMBAT_PROC_RATE_MOD];
proc_type = SE_RangedProc; proc_type = ProcType::RANGED_PROC;
} }
if (aa_rank_id) { if (aa_rank_id) {
if (!IsProcLimitTimerActive(-aa_rank_id, aa_proc_reuse_timer, proc_type)) {
passed_skill_limit_check = PassLimitToSkill(skillinuse, 0, proc_type, aa_rank_id);
if (passed_skill_limit_check && !IsProcLimitTimerActive(-aa_rank_id, aa_proc_reuse_timer, proc_type)) {
float chance = ProcChance * (static_cast<float>(aa_proc_chance) / 100.0f); float chance = ProcChance * (static_cast<float>(aa_proc_chance) / 100.0f);
if (zone->random.Roll(chance) && IsValidSpell(aa_spell_id)) { if (zone->random.Roll(chance) && IsValidSpell(aa_spell_id)) {
LogCombat("AA proc [{}] procing spell [{}] ([{}] percent chance)", aa_rank_id, aa_spell_id, chance); LogCombat("AA proc [{}] procing spell [{}] ([{}] percent chance)", aa_rank_id, aa_spell_id, chance);
@ -4469,13 +4490,12 @@ void Mob::TrySpellProc(const EQ::ItemInstance *inst, const EQ::ItemData *weapon,
} }
if (HasSkillProcs() && hand != EQ::invslot::slotRange) { //We check ranged skill procs within the attack functions. if (HasSkillProcs() && hand != EQ::invslot::slotRange) { //We check ranged skill procs within the attack functions.
uint16 skillinuse = 28;
if (weapon)
skillinuse = GetSkillByItemType(weapon->ItemType);
TrySkillProc(on, skillinuse, 0, false, hand); TrySkillProc(on, skillinuse, 0, false, hand);
} }
if (HasSkillProcSuccess() && hand != EQ::invslot::slotRange) { //We check ranged skill procs within the attack functions.
TrySkillProc(on, skillinuse, 0, true, hand);
}
return; return;
} }
@ -5040,7 +5060,7 @@ void Mob::ApplyDamageTable(DamageHitInfo &hit)
Log(Logs::Detail, Logs::Attack, "Damage table applied %d (max %d)", percent, damage_table.max_extra); Log(Logs::Detail, Logs::Attack, "Damage table applied %d (max %d)", percent, damage_table.max_extra);
} }
void Mob::TrySkillProc(Mob *on, uint16 skill, uint16 ReuseTime, bool Success, uint16 hand, bool IsDefensive) void Mob::TrySkillProc(Mob *on, EQ::skills::SkillType skill, uint16 ReuseTime, bool Success, uint16 hand, bool IsDefensive)
{ {
if (!on) { if (!on) {
@ -5049,12 +5069,19 @@ void Mob::TrySkillProc(Mob *on, uint16 skill, uint16 ReuseTime, bool Success, ui
return; return;
} }
if (!spellbonuses.LimitToSkill[skill] && !itembonuses.LimitToSkill[skill] && !aabonuses.LimitToSkill[skill]) if (on->HasDied()) {
return; return;
}
/*Allow one proc from each (Spell/Item/AA) if (!spellbonuses.LimitToSkill[skill] && !itembonuses.LimitToSkill[skill] && !aabonuses.LimitToSkill[skill]) {
return;
}
/*
Allow one proc from each (Spell/Item/AA)
Kayen: Due to limited avialability of effects on live it is too difficult Kayen: Due to limited avialability of effects on live it is too difficult
to confirm how they stack at this time, will adjust formula when more data is avialablle to test.*/ to confirm how they stack at this time, will adjust formula when more data is avialablle to test.
*/
bool CanProc = true; bool CanProc = true;
uint16 base_spell_id = 0; uint16 base_spell_id = 0;
@ -5069,22 +5096,24 @@ void Mob::TrySkillProc(Mob *on, uint16 skill, uint16 ReuseTime, bool Success, ui
if (spellbonuses.LimitToSkill[skill]) { if (spellbonuses.LimitToSkill[skill]) {
for (int e = 0; e < MAX_SKILL_PROCS; e++) { for (int i = 0; i < MAX_SKILL_PROCS; i++) {
if (CanProc && if (CanProc &&
((!Success && spellbonuses.SkillProc[e] && IsValidSpell(spellbonuses.SkillProc[e])) ((!Success && spellbonuses.SkillProc[i] && IsValidSpell(spellbonuses.SkillProc[i]))
|| (Success && spellbonuses.SkillProcSuccess[e] && IsValidSpell(spellbonuses.SkillProcSuccess[e])))) { || (Success && spellbonuses.SkillProcSuccess[i] && IsValidSpell(spellbonuses.SkillProcSuccess[i])))) {
if (Success) if (Success) {
base_spell_id = spellbonuses.SkillProcSuccess[e]; base_spell_id = spellbonuses.SkillProcSuccess[i];
else }
base_spell_id = spellbonuses.SkillProc[e]; else {
base_spell_id = spellbonuses.SkillProc[i];
}
proc_spell_id = 0; proc_spell_id = 0;
ProcMod = 0; ProcMod = 0;
for (int i = 0; i < EFFECT_COUNT; i++) { for (int i = 0; i < EFFECT_COUNT; i++) {
if (spells[base_spell_id].effect_id[i] == SE_SkillProc || spells[base_spell_id].effect_id[i] == SE_SkillProcSuccess) { if (spells[base_spell_id].effect_id[i] == SE_SkillProcAttempt || spells[base_spell_id].effect_id[i] == SE_SkillProcSuccess) {
proc_spell_id = spells[base_spell_id].base_value[i]; proc_spell_id = spells[base_spell_id].base_value[i];
ProcMod = static_cast<float>(spells[base_spell_id].limit_value[i]); ProcMod = static_cast<float>(spells[base_spell_id].limit_value[i]);
} }
@ -5095,8 +5124,7 @@ void Mob::TrySkillProc(Mob *on, uint16 skill, uint16 ReuseTime, bool Success, ui
float final_chance = chance * (ProcMod / 100.0f); float final_chance = chance * (ProcMod / 100.0f);
if (zone->random.Roll(final_chance)) { if (zone->random.Roll(final_chance)) {
ExecWeaponProc(nullptr, proc_spell_id, on); ExecWeaponProc(nullptr, proc_spell_id, on);
CheckNumHitsRemaining(NumHit::OffensiveSpellProcs, 0, CheckNumHitsRemaining(NumHit::OffensiveSpellProcs, 0, base_spell_id);
base_spell_id);
CanProc = false; CanProc = false;
break; break;
} }
@ -5114,21 +5142,23 @@ void Mob::TrySkillProc(Mob *on, uint16 skill, uint16 ReuseTime, bool Success, ui
if (itembonuses.LimitToSkill[skill]) { if (itembonuses.LimitToSkill[skill]) {
CanProc = true; CanProc = true;
for (int e = 0; e < MAX_SKILL_PROCS; e++) { for (int i = 0; i < MAX_SKILL_PROCS; i++) {
if (CanProc && if (CanProc &&
((!Success && itembonuses.SkillProc[e] && IsValidSpell(itembonuses.SkillProc[e])) ((!Success && itembonuses.SkillProc[i] && IsValidSpell(itembonuses.SkillProc[i]))
|| (Success && itembonuses.SkillProcSuccess[e] && IsValidSpell(itembonuses.SkillProcSuccess[e])))) { || (Success && itembonuses.SkillProcSuccess[i] && IsValidSpell(itembonuses.SkillProcSuccess[i])))) {
if (Success) if (Success) {
base_spell_id = itembonuses.SkillProcSuccess[e]; base_spell_id = itembonuses.SkillProcSuccess[i];
else }
base_spell_id = itembonuses.SkillProc[e]; else {
base_spell_id = itembonuses.SkillProc[i];
}
proc_spell_id = 0; proc_spell_id = 0;
ProcMod = 0; ProcMod = 0;
for (int i = 0; i < EFFECT_COUNT; i++) { for (int i = 0; i < EFFECT_COUNT; i++) {
if (spells[base_spell_id].effect_id[i] == SE_SkillProc || spells[base_spell_id].effect_id[i] == SE_SkillProcSuccess) { if (spells[base_spell_id].effect_id[i] == SE_SkillProcAttempt || spells[base_spell_id].effect_id[i] == SE_SkillProcSuccess) {
proc_spell_id = spells[base_spell_id].base_value[i]; proc_spell_id = spells[base_spell_id].base_value[i];
ProcMod = static_cast<float>(spells[base_spell_id].limit_value[i]); ProcMod = static_cast<float>(spells[base_spell_id].limit_value[i]);
} }
@ -5161,16 +5191,16 @@ void Mob::TrySkillProc(Mob *on, uint16 skill, uint16 ReuseTime, bool Success, ui
int32 limit_value = 0; int32 limit_value = 0;
uint32 slot = 0; uint32 slot = 0;
for (int e = 0; e < MAX_SKILL_PROCS; e++) { for (int i = 0; i < MAX_SKILL_PROCS; i++) {
if (CanProc && if (CanProc &&
((!Success && aabonuses.SkillProc[e]) ((!Success && aabonuses.SkillProc[i])
|| (Success && aabonuses.SkillProcSuccess[e]))) { || (Success && aabonuses.SkillProcSuccess[i]))) {
int aaid = 0; int aaid = 0;
if (Success) if (Success)
base_spell_id = aabonuses.SkillProcSuccess[e]; base_spell_id = aabonuses.SkillProcSuccess[i];
else else
base_spell_id = aabonuses.SkillProc[e]; base_spell_id = aabonuses.SkillProc[i];
proc_spell_id = 0; proc_spell_id = 0;
ProcMod = 0; ProcMod = 0;
@ -5190,7 +5220,7 @@ void Mob::TrySkillProc(Mob *on, uint16 skill, uint16 ReuseTime, bool Success, ui
limit_value = effect.limit_value; limit_value = effect.limit_value;
slot = effect.slot; slot = effect.slot;
if (effect_id == SE_SkillProc || effect_id == SE_SkillProcSuccess) { if (effect_id == SE_SkillProcAttempt || effect_id == SE_SkillProcSuccess) {
proc_spell_id = base_value; proc_spell_id = base_value;
ProcMod = static_cast<float>(limit_value); ProcMod = static_cast<float>(limit_value);
} }
@ -5225,12 +5255,14 @@ float Mob::GetSkillProcChances(uint16 ReuseTime, uint16 hand) {
if (!ReuseTime && hand) { if (!ReuseTime && hand) {
weapon_speed = GetWeaponSpeedbyHand(hand); weapon_speed = GetWeaponSpeedbyHand(hand);
ProcChance = static_cast<float>(weapon_speed) * (RuleR(Combat, AvgProcsPerMinute) / 60000.0f); ProcChance = static_cast<float>(weapon_speed) * (RuleR(Combat, AvgProcsPerMinute) / 60000.0f);
if (hand == EQ::invslot::slotSecondary) if (hand == EQ::invslot::slotSecondary) {
ProcChance /= 2; ProcChance /= 2;
} }
}
else else {
ProcChance = static_cast<float>(ReuseTime) * (RuleR(Combat, AvgProcsPerMinute) / 60000.0f); ProcChance = static_cast<float>(ReuseTime) * (RuleR(Combat, AvgProcsPerMinute) / 60000.0f);
}
return ProcChance; return ProcChance;
} }

View File

@ -1074,11 +1074,11 @@ void Mob::ApplyAABonuses(const AA::Rank &rank, StatBonuses *newbon)
case SE_WeaponProc: case SE_WeaponProc:
case SE_AddMeleeProc: case SE_AddMeleeProc:
for (int i = 0; i < MAX_AA_PROCS; i += 4) { for (int i = 0; i < MAX_AA_PROCS; i += 4) {
if (!newbon->SpellProc[i]) { if (!newbon->SpellProc[i + SBIndex::COMBAT_PROC_ORIGIN_ID]) {
newbon->SpellProc[i] = rank.id; //aa rank id newbon->SpellProc[i + SBIndex::COMBAT_PROC_ORIGIN_ID] = rank.id; //aa rank id
newbon->SpellProc[i + 1] = base_value; //proc spell id newbon->SpellProc[i + SBIndex::COMBAT_PROC_SPELL_ID] = base_value; //proc spell id
newbon->SpellProc[i + 2] = limit_value; //proc rate modifer newbon->SpellProc[i + SBIndex::COMBAT_PROC_RATE_MOD] = limit_value; //proc rate modifer
newbon->SpellProc[i + 3] = 0; //Lock out Timer newbon->SpellProc[i + SBIndex::COMBAT_PROC_REUSE_TIMER] = 0; //Lock out Timer
break; break;
} }
} }
@ -1086,11 +1086,11 @@ void Mob::ApplyAABonuses(const AA::Rank &rank, StatBonuses *newbon)
case SE_RangedProc: case SE_RangedProc:
for (int i = 0; i < MAX_AA_PROCS; i += 4) { for (int i = 0; i < MAX_AA_PROCS; i += 4) {
if (!newbon->RangedProc[i]) { if (!newbon->RangedProc[i + SBIndex::COMBAT_PROC_ORIGIN_ID]) {
newbon->RangedProc[i] = rank.id; //aa rank id newbon->RangedProc[i + SBIndex::COMBAT_PROC_ORIGIN_ID] = rank.id; //aa rank id
newbon->RangedProc[i + 1] = base_value; //proc spell id newbon->RangedProc[i + SBIndex::COMBAT_PROC_SPELL_ID] = base_value; //proc spell id
newbon->RangedProc[i + 2] = limit_value; //proc rate modifer newbon->RangedProc[i + SBIndex::COMBAT_PROC_RATE_MOD] = limit_value; //proc rate modifer
newbon->RangedProc[i + 3] = 0; //Lock out Timer newbon->RangedProc[i + SBIndex::COMBAT_PROC_REUSE_TIMER] = 0; //Lock out Timer
break; break;
} }
} }
@ -1098,11 +1098,11 @@ void Mob::ApplyAABonuses(const AA::Rank &rank, StatBonuses *newbon)
case SE_DefensiveProc: case SE_DefensiveProc:
for (int i = 0; i < MAX_AA_PROCS; i += 4) { for (int i = 0; i < MAX_AA_PROCS; i += 4) {
if (!newbon->DefensiveProc[i]) { if (!newbon->DefensiveProc[i + SBIndex::COMBAT_PROC_ORIGIN_ID]) {
newbon->DefensiveProc[i] = rank.id; //aa rank id newbon->DefensiveProc[i + SBIndex::COMBAT_PROC_ORIGIN_ID] = rank.id; //aa rank id
newbon->DefensiveProc[i + 1] = base_value; //proc spell id newbon->DefensiveProc[i + SBIndex::COMBAT_PROC_SPELL_ID] = base_value; //proc spell id
newbon->DefensiveProc[i + 2] = limit_value; //proc rate modifer newbon->DefensiveProc[i + SBIndex::COMBAT_PROC_RATE_MOD] = limit_value; //proc rate modifer
newbon->DefensiveProc[i + 3] = 0; //Lock out Timer newbon->DefensiveProc[i + SBIndex::COMBAT_PROC_REUSE_TIMER] = 0; //Lock out Timer
break; break;
} }
} }
@ -1118,23 +1118,23 @@ void Mob::ApplyAABonuses(const AA::Rank &rank, StatBonuses *newbon)
newbon->Proc_Timer_Modifier = true; newbon->Proc_Timer_Modifier = true;
for (int i = 0; i < MAX_AA_PROCS; i += 4) { for (int i = 0; i < MAX_AA_PROCS; i += 4) {
if (newbon->SpellProc[i] == rank.id) { if (newbon->SpellProc[i + SBIndex::COMBAT_PROC_ORIGIN_ID] == rank.id) {
if (!newbon->SpellProc[i + 3]) { if (!newbon->SpellProc[i + SBIndex::COMBAT_PROC_REUSE_TIMER]) {
newbon->SpellProc[i + 3] = limit_value;//Lock out Timer newbon->SpellProc[i + SBIndex::COMBAT_PROC_REUSE_TIMER] = limit_value;//Lock out Timer
break; break;
} }
} }
if (newbon->RangedProc[i] == rank.id) { if (newbon->RangedProc[i + SBIndex::COMBAT_PROC_ORIGIN_ID] == rank.id) {
if (!newbon->RangedProc[i + 3]) { if (!newbon->RangedProc[i + SBIndex::COMBAT_PROC_REUSE_TIMER]) {
newbon->RangedProc[i + 3] = limit_value;//Lock out Timer newbon->RangedProc[i + SBIndex::COMBAT_PROC_REUSE_TIMER] = limit_value;//Lock out Timer
break; break;
} }
} }
if (newbon->DefensiveProc[i] == rank.id) { if (newbon->DefensiveProc[i + SBIndex::COMBAT_PROC_ORIGIN_ID] == rank.id) {
if (!newbon->DefensiveProc[i + 3]) { if (!newbon->DefensiveProc[i + SBIndex::COMBAT_PROC_REUSE_TIMER]) {
newbon->DefensiveProc[i + 3] = limit_value;//Lock out Timer newbon->DefensiveProc[i + SBIndex::COMBAT_PROC_REUSE_TIMER] = limit_value;//Lock out Timer
break; break;
} }
} }
@ -1208,9 +1208,9 @@ void Mob::ApplyAABonuses(const AA::Rank &rank, StatBonuses *newbon)
case SE_SkillAttackProc: { case SE_SkillAttackProc: {
// You can only have one of these per client. [AA Dragon Punch] // You can only have one of these per client. [AA Dragon Punch]
newbon->SkillAttackProc[SBIndex::SKILLPROC_CHANCE] = base_value; // Chance base 1000 = 100% proc rate newbon->SkillAttackProc[SBIndex::SKILLATK_PROC_CHANCE] = base_value; // Chance base 1000 = 100% proc rate
newbon->SkillAttackProc[SBIndex::SKILLPROC_SKILL] = limit_value; // Skill to Proc Off newbon->SkillAttackProc[SBIndex::SKILLATK_PROC_SKILL] = limit_value; // Skill to Proc Off
newbon->SkillAttackProc[SBIndex::SKILLPROC_SPELL_ID] = rank.spell; // spell to proc newbon->SkillAttackProc[SBIndex::SKILLATK_PROC_SPELL_ID] = rank.spell; // spell to proc
break; break;
} }
@ -1458,15 +1458,19 @@ void Mob::ApplyAABonuses(const AA::Rank &rank, StatBonuses *newbon)
break; break;
case SE_LimitToSkill: { case SE_LimitToSkill: {
// Bad data or unsupported new skill // Bad data or unsupported new skill
if (limit_value > EQ::skills::HIGHEST_SKILL) if (base_value > EQ::skills::HIGHEST_SKILL) {
break; break;
if (base_value <= EQ::skills::HIGHEST_SKILL) }
if (base_value <= EQ::skills::HIGHEST_SKILL) {
newbon->LimitToSkill[base_value] = true; newbon->LimitToSkill[base_value] = true;
newbon->LimitToSkill[EQ::skills::HIGHEST_SKILL + 3] = true; //Used as a general exists check
}
break; break;
} }
case SE_SkillProc: { case SE_SkillProcAttempt: {
for (int e = 0; e < MAX_SKILL_PROCS; e++) { for (int e = 0; e < MAX_SKILL_PROCS; e++) {
if (newbon->SkillProc[e] && newbon->SkillProc[e] == rank.id) if (newbon->SkillProc[e] && newbon->SkillProc[e] == rank.id)
break; // Do not use the same aa id more than once. break; // Do not use the same aa id more than once.
@ -3535,15 +3539,17 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne
case SE_LimitToSkill:{ case SE_LimitToSkill:{
// Bad data or unsupported new skill // Bad data or unsupported new skill
if (limit_value > EQ::skills::HIGHEST_SKILL) if (effect_value > EQ::skills::HIGHEST_SKILL) {
break; break;
}
if (effect_value <= EQ::skills::HIGHEST_SKILL){ if (effect_value <= EQ::skills::HIGHEST_SKILL){
new_bonus->LimitToSkill[effect_value] = true; new_bonus->LimitToSkill[effect_value] = true;
new_bonus->LimitToSkill[EQ::skills::HIGHEST_SKILL + 3] = true; //Used as a general exists check
} }
break; break;
} }
case SE_SkillProc:{ case SE_SkillProcAttempt:{
for(int e = 0; e < MAX_SKILL_PROCS; e++) for(int e = 0; e < MAX_SKILL_PROCS; e++)
{ {
@ -5528,7 +5534,7 @@ void Mob::NegateSpellEffectBonuses(uint16 spell_id)
} }
} }
case SE_SkillProc: { case SE_SkillProcAttempt: {
for (int e = 0; e < MAX_SKILL_PROCS; e++) for (int e = 0; e < MAX_SKILL_PROCS; e++)
{ {
if (negate_spellbonus) { spellbonuses.SkillProc[e] = effect_value; } if (negate_spellbonus) { spellbonuses.SkillProc[e] = effect_value; }

View File

@ -3248,7 +3248,7 @@ void Bot::AI_Process()
TriggerDefensiveProcs(tar, EQ::invslot::slotPrimary, false); TriggerDefensiveProcs(tar, EQ::invslot::slotPrimary, false);
TEST_COMBATANTS(); TEST_COMBATANTS();
TryWeaponProc(p_item, tar, EQ::invslot::slotPrimary); TryCombatProcs(p_item, tar, EQ::invslot::slotPrimary);
// bool tripleSuccess = false; // bool tripleSuccess = false;
@ -3337,7 +3337,7 @@ void Bot::AI_Process()
Attack(tar, EQ::invslot::slotSecondary); // Single attack with offhand Attack(tar, EQ::invslot::slotSecondary); // Single attack with offhand
TEST_COMBATANTS(); TEST_COMBATANTS();
TryWeaponProc(s_item, tar, EQ::invslot::slotSecondary); TryCombatProcs(s_item, tar, EQ::invslot::slotSecondary);
TEST_COMBATANTS(); TEST_COMBATANTS();
if (CanThisClassDoubleAttack() && CheckBotDoubleAttack()) { if (CanThisClassDoubleAttack() && CheckBotDoubleAttack()) {

View File

@ -721,17 +721,17 @@ void Client::CompleteConnect()
case SE_AddMeleeProc: case SE_AddMeleeProc:
case SE_WeaponProc: case SE_WeaponProc:
{ {
AddProcToWeapon(GetProcID(buffs[j1].spellid, x1), false, 100 + spells[buffs[j1].spellid].limit_value[x1], buffs[j1].spellid, buffs[j1].casterlevel, GetProcLimitTimer(buffs[j1].spellid, SE_WeaponProc)); AddProcToWeapon(GetProcID(buffs[j1].spellid, x1), false, 100 + spells[buffs[j1].spellid].limit_value[x1], buffs[j1].spellid, buffs[j1].casterlevel, GetProcLimitTimer(buffs[j1].spellid, ProcType::MELEE_PROC));
break; break;
} }
case SE_DefensiveProc: case SE_DefensiveProc:
{ {
AddDefensiveProc(GetProcID(buffs[j1].spellid, x1), 100 + spells[buffs[j1].spellid].limit_value[x1], buffs[j1].spellid, GetProcLimitTimer(buffs[j1].spellid, SE_DefensiveProc)); AddDefensiveProc(GetProcID(buffs[j1].spellid, x1), 100 + spells[buffs[j1].spellid].limit_value[x1], buffs[j1].spellid, GetProcLimitTimer(buffs[j1].spellid, ProcType::DEFENSIVE_PROC));
break; break;
} }
case SE_RangedProc: case SE_RangedProc:
{ {
AddRangedProc(GetProcID(buffs[j1].spellid, x1), 100 + spells[buffs[j1].spellid].limit_value[x1], buffs[j1].spellid, GetProcLimitTimer(buffs[j1].spellid, SE_RangedProc)); AddRangedProc(GetProcID(buffs[j1].spellid, x1), 100 + spells[buffs[j1].spellid].limit_value[x1], buffs[j1].spellid, GetProcLimitTimer(buffs[j1].spellid, ProcType::RANGED_PROC));
break; break;
} }
} }

View File

@ -414,7 +414,7 @@ bool Client::Process() {
else if (auto_attack_target->GetHP() > -10) // -10 so we can watch people bleed in PvP else if (auto_attack_target->GetHP() > -10) // -10 so we can watch people bleed in PvP
{ {
EQ::ItemInstance *wpn = GetInv().GetItem(EQ::invslot::slotPrimary); EQ::ItemInstance *wpn = GetInv().GetItem(EQ::invslot::slotPrimary);
TryWeaponProc(wpn, auto_attack_target, EQ::invslot::slotPrimary); TryCombatProcs(wpn, auto_attack_target, EQ::invslot::slotPrimary);
TriggerDefensiveProcs(auto_attack_target, EQ::invslot::slotPrimary, false); TriggerDefensiveProcs(auto_attack_target, EQ::invslot::slotPrimary, false);
DoAttackRounds(auto_attack_target, EQ::invslot::slotPrimary); DoAttackRounds(auto_attack_target, EQ::invslot::slotPrimary);
@ -460,7 +460,7 @@ bool Client::Process() {
CheckIncreaseSkill(EQ::skills::SkillDualWield, auto_attack_target, -10); CheckIncreaseSkill(EQ::skills::SkillDualWield, auto_attack_target, -10);
if (CheckDualWield()) { if (CheckDualWield()) {
EQ::ItemInstance *wpn = GetInv().GetItem(EQ::invslot::slotSecondary); EQ::ItemInstance *wpn = GetInv().GetItem(EQ::invslot::slotSecondary);
TryWeaponProc(wpn, auto_attack_target, EQ::invslot::slotSecondary); TryCombatProcs(wpn, auto_attack_target, EQ::invslot::slotSecondary);
DoAttackRounds(auto_attack_target, EQ::invslot::slotSecondary); DoAttackRounds(auto_attack_target, EQ::invslot::slotSecondary);
} }

View File

@ -533,7 +533,7 @@ struct StatBonuses {
int32 Metabolism; // Food/drink consumption rates. int32 Metabolism; // Food/drink consumption rates.
bool Sanctuary; // Sanctuary effect, lowers place on hate list until cast on others. bool Sanctuary; // Sanctuary effect, lowers place on hate list until cast on others.
int32 FactionModPct; // Modifies amount of faction gained. int32 FactionModPct; // Modifies amount of faction gained.
bool LimitToSkill[EQ::skills::HIGHEST_SKILL + 2]; // Determines if we need to search for a skill proc. bool LimitToSkill[EQ::skills::HIGHEST_SKILL + 3]; // Determines if we need to search for a skill proc.
uint32 SkillProc[MAX_SKILL_PROCS]; // Max number of spells containing skill_procs. 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 SkillProcSuccess[MAX_SKILL_PROCS]; // Max number of spells containing skill_procs_success.
int32 SpellProc[MAX_AA_PROCS]; // Max number of spells containing melee spell procs. int32 SpellProc[MAX_AA_PROCS]; // Max number of spells containing melee spell procs.
@ -671,9 +671,9 @@ namespace SBIndex {
constexpr uint16 POSITION_FRONT = 1; // SPA 503-506 constexpr uint16 POSITION_FRONT = 1; // SPA 503-506
constexpr uint16 PET_RAMPAGE_CHANCE = 0; // SPA 464,465 constexpr uint16 PET_RAMPAGE_CHANCE = 0; // SPA 464,465
constexpr uint16 PET_RAMPAGE_DMG_MOD = 1; // SPA 465,465 constexpr uint16 PET_RAMPAGE_DMG_MOD = 1; // SPA 465,465
constexpr uint16 SKILLPROC_CHANCE = 0; // SPA 427 constexpr uint16 SKILLATK_PROC_CHANCE = 0; // SPA 427
constexpr uint16 SKILLPROC_SKILL = 1; // SPA 427 constexpr uint16 SKILLATK_PROC_SKILL = 1; // SPA 427
constexpr uint16 SKILLPROC_SPELL_ID = 2; // SPA 427 constexpr uint16 SKILLATK_PROC_SPELL_ID = 2; // SPA 427
constexpr uint16 SLAYUNDEAD_RATE_MOD = 0; // SPA 219 constexpr uint16 SLAYUNDEAD_RATE_MOD = 0; // SPA 219
constexpr uint16 SLAYUNDEAD_DMG_MOD = 1; // SPA 219 constexpr uint16 SLAYUNDEAD_DMG_MOD = 1; // SPA 219
constexpr uint16 DOUBLE_RIPOSTE_CHANCE = 0; // SPA 223 constexpr uint16 DOUBLE_RIPOSTE_CHANCE = 0; // SPA 223
@ -688,14 +688,18 @@ namespace SBIndex {
constexpr uint16 REFLECT_CHANCE = 0; // SPA 158 constexpr uint16 REFLECT_CHANCE = 0; // SPA 158
constexpr uint16 REFLECT_RESISTANCE_MOD = 1; // SPA 158 constexpr uint16 REFLECT_RESISTANCE_MOD = 1; // SPA 158
constexpr uint16 REFLECT_DMG_EFFECTIVENESS = 2; // SPA 158 constexpr uint16 REFLECT_DMG_EFFECTIVENESS = 2; // SPA 158
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
}; };
typedef struct typedef struct
{ {
uint16 spellID; int32 spellID;
uint16 chance; uint16 chance;
uint16 base_spellID; int32 base_spellID;
int level_override; int level_override;
uint32 proc_reuse_time; uint32 proc_reuse_time;
} tProc; } tProc;

View File

@ -4213,15 +4213,17 @@ int Mob::GetSnaredAmount()
void Mob::TriggerDefensiveProcs(Mob *on, uint16 hand, bool FromSkillProc, int damage) void Mob::TriggerDefensiveProcs(Mob *on, uint16 hand, bool FromSkillProc, int damage)
{ {
if (!on) if (!on) {
return; return;
}
if (!FromSkillProc) if (!FromSkillProc) {
on->TryDefensiveProc(this, hand); on->TryDefensiveProc(this, hand);
}
//Defensive Skill Procs //Defensive Skill Procs
if (damage < 0 && damage >= -4) { if (damage < 0 && damage >= -4) {
uint16 skillinuse = 0; EQ::skills::SkillType skillinuse = EQ::skills::SkillBlock;
switch (damage) { switch (damage) {
case (-1): case (-1):
skillinuse = EQ::skills::SkillBlock; skillinuse = EQ::skills::SkillBlock;
@ -4240,13 +4242,15 @@ void Mob::TriggerDefensiveProcs(Mob *on, uint16 hand, bool FromSkillProc, int da
break; break;
} }
if (on->HasSkillProcs()) if (on->HasSkillProcs()) {
on->TrySkillProc(this, skillinuse, 0, false, hand, true); on->TrySkillProc(this, skillinuse, 0, false, hand, true);
}
if (on->HasSkillProcSuccess()) if (on && on->HasSkillProcSuccess()) {
on->TrySkillProc(this, skillinuse, 0, true, hand, true); on->TrySkillProc(this, skillinuse, 0, true, hand, true);
} }
} }
}
void Mob::SetDelta(const glm::vec4& delta) { void Mob::SetDelta(const glm::vec4& delta) {
m_Delta = delta; m_Delta = delta;
@ -5617,7 +5621,7 @@ void Mob::SlowMitigation(Mob* caster)
} }
} }
uint16 Mob::GetSkillByItemType(int ItemType) EQ::skills::SkillType Mob::GetSkillByItemType(int ItemType)
{ {
switch (ItemType) { switch (ItemType) {
case EQ::item::ItemType1HSlash: case EQ::item::ItemType1HSlash:
@ -5673,22 +5677,6 @@ uint8 Mob::GetItemTypeBySkill(EQ::skills::SkillType skill)
} }
} }
bool Mob::PassLimitToSkill(uint16 spell_id, uint16 skill) {
if (!IsValidSpell(spell_id))
return false;
for (int i = 0; i < EFFECT_COUNT; i++) {
if (spells[spell_id].effect_id[i] == SE_LimitToSkill){
if (spells[spell_id].base_value[i] == skill){
return true;
}
}
}
return false;
}
uint16 Mob::GetWeaponSpeedbyHand(uint16 hand) { uint16 Mob::GetWeaponSpeedbyHand(uint16 hand) {
uint16 weapon_speed = 0; uint16 weapon_speed = 0;

View File

@ -701,7 +701,7 @@ public:
static uint32 RandomTimer(int min, int max); static uint32 RandomTimer(int min, int max);
static uint8 GetDefaultGender(uint16 in_race, uint8 in_gender = 0xFF); static uint8 GetDefaultGender(uint16 in_race, uint8 in_gender = 0xFF);
static bool IsPlayerRace(uint16 in_race); static bool IsPlayerRace(uint16 in_race);
uint16 GetSkillByItemType(int ItemType); EQ::skills::SkillType GetSkillByItemType(int ItemType);
uint8 GetItemTypeBySkill(EQ::skills::SkillType skill); uint8 GetItemTypeBySkill(EQ::skills::SkillType skill);
virtual void MakePet(uint16 spell_id, const char* pettype, const char *petname = nullptr); virtual void MakePet(uint16 spell_id, const char* pettype, const char *petname = nullptr);
virtual void MakePoweredPet(uint16 spell_id, const char* pettype, int16 petpower, const char *petname = nullptr, float in_size = 0.0f); virtual void MakePoweredPet(uint16 spell_id, const char* pettype, int16 petpower, const char *petname = nullptr, float in_size = 0.0f);
@ -864,6 +864,7 @@ public:
bool IsTargetedFocusEffect(int focus_type); bool IsTargetedFocusEffect(int focus_type);
bool HasPersistDeathIllusion(int32 spell_id); bool HasPersistDeathIllusion(int32 spell_id);
bool TryDoubleMeleeRoundEffect(); bool TryDoubleMeleeRoundEffect();
bool GetUseDoubleMeleeRoundDmgBonus() const { return use_double_melee_round_dmg_bonus; } bool GetUseDoubleMeleeRoundDmgBonus() const { return use_double_melee_round_dmg_bonus; }
inline void SetUseDoubleMeleeRoundDmgBonus(bool val) { use_double_melee_round_dmg_bonus = val; } inline void SetUseDoubleMeleeRoundDmgBonus(bool val) { use_double_melee_round_dmg_bonus = val; }
@ -1446,13 +1447,13 @@ protected:
bool spawned; bool spawned;
void CalcSpellBonuses(StatBonuses* newbon); void CalcSpellBonuses(StatBonuses* newbon);
virtual void CalcBonuses(); virtual void CalcBonuses();
void TrySkillProc(Mob *on, uint16 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 = SlotCharm?
bool PassLimitToSkill(uint16 spell_id, uint16 skill); bool PassLimitToSkill(EQ::skills::SkillType skill, int32 spell_id, int proc_type, int aa_id=0);
bool PassLimitClass(uint32 Classes_, uint16 Class_); bool PassLimitClass(uint32 Classes_, uint16 Class_);
void TryDefensiveProc(Mob *on, uint16 hand = EQ::invslot::slotPrimary); 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 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); void TrySpellProc(const EQ::ItemInstance* inst, const EQ::ItemData* weapon, Mob *on, uint16 hand = EQ::invslot::slotPrimary);
void TryWeaponProc(const EQ::ItemInstance* weapon, Mob *on, uint16 hand = EQ::invslot::slotPrimary); void TryCombatProcs(const EQ::ItemInstance* weapon, Mob *on, uint16 hand = EQ::invslot::slotPrimary, const EQ::ItemData* weapon_data = nullptr);
void ExecWeaponProc(const EQ::ItemInstance* weapon, uint16 spell_id, Mob *on, int level_override = -1); void ExecWeaponProc(const EQ::ItemInstance* weapon, uint16 spell_id, Mob *on, int level_override = -1);
virtual float GetProcChances(float ProcBonus, uint16 hand = EQ::invslot::slotPrimary); virtual float GetProcChances(float ProcBonus, uint16 hand = EQ::invslot::slotPrimary);
virtual float GetDefensiveProcChances(float &ProcBonus, float &ProcChance, uint16 hand = EQ::invslot::slotPrimary, Mob *on = nullptr); virtual float GetDefensiveProcChances(float &ProcBonus, float &ProcChance, uint16 hand = EQ::invslot::slotPrimary, Mob *on = nullptr);

View File

@ -580,13 +580,13 @@ void NPC::SetPetState(SpellBuff_Struct *pet_buffs, uint32 *items) {
case SE_WeaponProc: case SE_WeaponProc:
// We need to reapply buff based procs // We need to reapply buff based procs
// We need to do this here so suspended pets also regain their procs. // We need to do this here so suspended pets also regain their procs.
AddProcToWeapon(GetProcID(buffs[j1].spellid,x1), false, 100+spells[buffs[j1].spellid].limit_value[x1], buffs[j1].spellid, buffs[j1].casterlevel, GetProcLimitTimer(buffs[j1].spellid, SE_WeaponProc)); AddProcToWeapon(GetProcID(buffs[j1].spellid,x1), false, 100+spells[buffs[j1].spellid].limit_value[x1], buffs[j1].spellid, buffs[j1].casterlevel, GetProcLimitTimer(buffs[j1].spellid, ProcType::MELEE_PROC));
break; break;
case SE_DefensiveProc: case SE_DefensiveProc:
AddDefensiveProc(GetProcID(buffs[j1].spellid, x1), 100 + spells[buffs[j1].spellid].limit_value[x1], buffs[j1].spellid, GetProcLimitTimer(buffs[j1].spellid, SE_DefensiveProc)); AddDefensiveProc(GetProcID(buffs[j1].spellid, x1), 100 + spells[buffs[j1].spellid].limit_value[x1], buffs[j1].spellid, GetProcLimitTimer(buffs[j1].spellid, ProcType::DEFENSIVE_PROC));
break; break;
case SE_RangedProc: case SE_RangedProc:
AddRangedProc(GetProcID(buffs[j1].spellid, x1), 100 + spells[buffs[j1].spellid].limit_value[x1], buffs[j1].spellid, GetProcLimitTimer(buffs[j1].spellid, SE_RangedProc)); AddRangedProc(GetProcID(buffs[j1].spellid, x1), 100 + spells[buffs[j1].spellid].limit_value[x1], buffs[j1].spellid, GetProcLimitTimer(buffs[j1].spellid, ProcType::RANGED_PROC));
break; break;
case SE_Charm: case SE_Charm:
case SE_Rune: case SE_Rune:

View File

@ -196,12 +196,12 @@ void Mob::DoSpecialAttackDamage(Mob *who, EQ::skills::SkillType skill, int32 bas
DoAttack(who, my_hit); DoAttack(who, my_hit);
who->AddToHateList(this, hate, 0); who->AddToHateList(this, hate, 0);
if (my_hit.damage_done > 0 && aabonuses.SkillAttackProc[SBIndex::SKILLPROC_CHANCE] && aabonuses.SkillAttackProc[SBIndex::SKILLPROC_SKILL] == skill && if (my_hit.damage_done > 0 && aabonuses.SkillAttackProc[SBIndex::SKILLATK_PROC_CHANCE] && aabonuses.SkillAttackProc[SBIndex::SKILLATK_PROC_SKILL] == skill &&
IsValidSpell(aabonuses.SkillAttackProc[SBIndex::SKILLPROC_SPELL_ID])) { IsValidSpell(aabonuses.SkillAttackProc[SBIndex::SKILLATK_PROC_SPELL_ID])) {
float chance = aabonuses.SkillAttackProc[SBIndex::SKILLPROC_CHANCE] / 1000.0f; float chance = aabonuses.SkillAttackProc[SBIndex::SKILLATK_PROC_CHANCE] / 1000.0f;
if (zone->random.Roll(chance)) if (zone->random.Roll(chance))
SpellFinished(aabonuses.SkillAttackProc[SBIndex::SKILLPROC_SPELL_ID], who, EQ::spells::CastingSlot::Item, 0, -1, SpellFinished(aabonuses.SkillAttackProc[SBIndex::SKILLATK_PROC_SPELL_ID], who, EQ::spells::CastingSlot::Item, 0, -1,
spells[aabonuses.SkillAttackProc[SBIndex::SKILLPROC_SPELL_ID]].resist_difficulty); spells[aabonuses.SkillAttackProc[SBIndex::SKILLATK_PROC_SPELL_ID]].resist_difficulty);
} }
who->Damage(this, my_hit.damage_done, SPELL_UNKNOWN, skill, false); who->Damage(this, my_hit.damage_done, SPELL_UNKNOWN, skill, false);
@ -212,12 +212,13 @@ void Mob::DoSpecialAttackDamage(Mob *who, EQ::skills::SkillType skill, int32 bas
if (HasDied()) if (HasDied())
return; return;
if (HasSkillProcs()) if (HasSkillProcs()) {
TrySkillProc(who, skill, ReuseTime * 1000); TrySkillProc(who, skill, ReuseTime * 1000);
}
if (my_hit.damage_done > 0 && HasSkillProcSuccess()) if (my_hit.damage_done > 0 && HasSkillProcSuccess()) {
TrySkillProc(who, skill, ReuseTime * 1000, true); TrySkillProc(who, skill, ReuseTime * 1000, true);
} }
}
// We should probably refactor this to take the struct not the packet // We should probably refactor this to take the struct not the packet
void Client::OPCombatAbility(const CombatAbility_Struct *ca_atk) void Client::OPCombatAbility(const CombatAbility_Struct *ca_atk)
@ -798,7 +799,7 @@ void Mob::DoArcheryAttackDmg(Mob *other, const EQ::ItemInstance *RangeWeapon, co
const EQ::ItemInstance *_RangeWeapon = nullptr; const EQ::ItemInstance *_RangeWeapon = nullptr;
const EQ::ItemInstance *_Ammo = nullptr; const EQ::ItemInstance *_Ammo = nullptr;
const EQ::ItemData *ammo_lost = nullptr; const EQ::ItemData *last_ammo_used = nullptr;
/* /*
If LaunchProjectile is false this function will do archery damage on target, If LaunchProjectile is false this function will do archery damage on target,
@ -830,7 +831,7 @@ void Mob::DoArcheryAttackDmg(Mob *other, const EQ::ItemInstance *RangeWeapon, co
if (_Ammo && _Ammo->GetItem() && _Ammo->GetItem()->ID == ammo_id) if (_Ammo && _Ammo->GetItem() && _Ammo->GetItem()->ID == ammo_id)
Ammo = _Ammo; Ammo = _Ammo;
else else
ammo_lost = database.GetItem(ammo_id); last_ammo_used = database.GetItem(ammo_id);
} }
} }
} }
@ -900,37 +901,42 @@ void Mob::DoArcheryAttackDmg(Mob *other, const EQ::ItemInstance *RangeWeapon, co
other->Damage(this, TotalDmg, SPELL_UNKNOWN, EQ::skills::SkillArchery); other->Damage(this, TotalDmg, SPELL_UNKNOWN, EQ::skills::SkillArchery);
// Skill Proc Success
if (TotalDmg > 0 && HasSkillProcSuccess() && other && !other->HasDied()) {
if (ReuseTime)
TrySkillProc(other, EQ::skills::SkillArchery, ReuseTime);
else
TrySkillProc(other, EQ::skills::SkillArchery, 0, true, EQ::invslot::slotRange);
}
// end of old fuck
if (LaunchProjectile)
return; // Shouldn't reach this point durring initial launch phase, but just in case.
// Weapon Proc // Weapon Proc
if (RangeWeapon && other && !other->HasDied()) if (RangeWeapon && other && !other->HasDied()) {
TryWeaponProc(RangeWeapon, other, EQ::invslot::slotRange); TryCombatProcs(RangeWeapon, other, EQ::invslot::slotRange);
}
// Ammo Proc // Ammo Proc, do not try spell procs if from ammo.
if (ammo_lost) if (last_ammo_used) {
TryWeaponProc(nullptr, ammo_lost, other, EQ::invslot::slotRange); TryWeaponProc(nullptr, last_ammo_used, other, EQ::invslot::slotRange);
else if (Ammo && other && !other->HasDied()) }
TryWeaponProc(Ammo, other, EQ::invslot::slotRange); else if (Ammo && other && !other->HasDied()) {
TryWeaponProc(Ammo, Ammo->GetItem(), other, EQ::invslot::slotRange);
}
// Skill Proc // Skill Proc Attempt
if (HasSkillProcs() && other && !other->HasDied()) { if (HasSkillProcs() && other && !other->HasDied()) {
if (ReuseTime) if (ReuseTime) {
TrySkillProc(other, EQ::skills::SkillArchery, ReuseTime); TrySkillProc(other, EQ::skills::SkillArchery, ReuseTime);
else }
else {
TrySkillProc(other, EQ::skills::SkillArchery, 0, false, EQ::invslot::slotRange); TrySkillProc(other, EQ::skills::SkillArchery, 0, false, EQ::invslot::slotRange);
} }
} }
// Skill Proc Success ... can proc off hits OR misses
if (HasSkillProcSuccess() && other && !other->HasDied()) {
if (ReuseTime) {
TrySkillProc(other, EQ::skills::SkillArchery, ReuseTime, true);
}
else {
TrySkillProc(other, EQ::skills::SkillArchery, 0, true, EQ::invslot::slotRange);
}
}
}
bool Mob::TryProjectileAttack(Mob *other, const EQ::ItemData *item, EQ::skills::SkillType skillInUse, bool Mob::TryProjectileAttack(Mob *other, const EQ::ItemData *item, EQ::skills::SkillType skillInUse,
uint16 weapon_dmg, const EQ::ItemInstance *RangeWeapon, uint16 weapon_dmg, const EQ::ItemInstance *RangeWeapon,
const EQ::ItemInstance *Ammo, int AmmoSlot, float speed) const EQ::ItemInstance *Ammo, int AmmoSlot, float speed)
@ -991,7 +997,7 @@ bool Mob::TryProjectileAttack(Mob *other, const EQ::ItemData *item, EQ::skills::
if (Ammo && Ammo->GetItem()) if (Ammo && Ammo->GetItem())
ProjectileAtk[slot].ammo_id = Ammo->GetItem()->ID; ProjectileAtk[slot].ammo_id = Ammo->GetItem()->ID;
ProjectileAtk[slot].ammo_slot = 0; ProjectileAtk[slot].ammo_slot = AmmoSlot;
ProjectileAtk[slot].skill = skillInUse; ProjectileAtk[slot].skill = skillInUse;
ProjectileAtk[slot].speed_mod = speed; ProjectileAtk[slot].speed_mod = speed;
@ -1268,17 +1274,20 @@ void NPC::DoRangedAttackDmg(Mob* other, bool Launch, int16 damage_mod, int16 cha
other->Damage(this, TotalDmg, SPELL_UNKNOWN, skillInUse); other->Damage(this, TotalDmg, SPELL_UNKNOWN, skillInUse);
if (TotalDmg > 0 && HasSkillProcSuccess() && !other->HasDied())
TrySkillProc(other, skillInUse, 0, true, EQ::invslot::slotRange);
//try proc on hits and misses //try proc on hits and misses
if(other && !other->HasDied()) if (other && !other->HasDied()) {
TrySpellProc(nullptr, (const EQ::ItemData*)nullptr, other, EQ::invslot::slotRange); TrySpellProc(nullptr, (const EQ::ItemData*)nullptr, other, EQ::invslot::slotRange);
}
if (HasSkillProcs() && other && !other->HasDied()) if (HasSkillProcs() && other && !other->HasDied()) {
TrySkillProc(other, skillInUse, 0, false, EQ::invslot::slotRange); TrySkillProc(other, skillInUse, 0, false, EQ::invslot::slotRange);
} }
if (HasSkillProcSuccess() && other && !other->HasDied()) {
TrySkillProc(other, skillInUse, 0, true, EQ::invslot::slotRange);
}
}
void Client::ThrowingAttack(Mob* other, bool CanDoubleAttack) { //old was 51 void Client::ThrowingAttack(Mob* other, bool CanDoubleAttack) { //old was 51
//conditions to use an attack checked before we are called //conditions to use an attack checked before we are called
if (!other) if (!other)
@ -1355,7 +1364,7 @@ void Client::ThrowingAttack(Mob* other, bool CanDoubleAttack) { //old was 51
return; return;
} }
DoThrowingAttackDmg(other, RangeWeapon, item); DoThrowingAttackDmg(other, RangeWeapon, item, 0, 0, 0, 0, 0,ammo_slot);
// Consume Ammo, unless Ammo Consumption is disabled // Consume Ammo, unless Ammo Consumption is disabled
if (RuleB(Combat, ThrowingConsumesAmmo)) { if (RuleB(Combat, ThrowingConsumesAmmo)) {
@ -1378,8 +1387,8 @@ void Mob::DoThrowingAttackDmg(Mob *other, const EQ::ItemInstance *RangeWeapon, c
return; return;
} }
const EQ::ItemInstance *_RangeWeapon = nullptr; const EQ::ItemInstance *m_RangeWeapon = nullptr;//throwing weapon
const EQ::ItemData *ammo_lost = nullptr; const EQ::ItemData *last_ammo_used = nullptr;
/* /*
If LaunchProjectile is false this function will do archery damage on target, If LaunchProjectile is false this function will do archery damage on target,
@ -1394,12 +1403,14 @@ void Mob::DoThrowingAttackDmg(Mob *other, const EQ::ItemInstance *RangeWeapon, c
} else { } else {
if (!RangeWeapon && range_id) { if (!RangeWeapon && range_id) {
if (IsClient()) { if (IsClient()) {
_RangeWeapon = CastToClient()->m_inv[AmmoSlot]; m_RangeWeapon = CastToClient()->m_inv[AmmoSlot];
if (_RangeWeapon && _RangeWeapon->GetItem() &&
_RangeWeapon->GetItem()->ID != range_id) if (m_RangeWeapon && m_RangeWeapon->GetItem() && m_RangeWeapon->GetItem()->ID == range_id) {
RangeWeapon = _RangeWeapon; RangeWeapon = m_RangeWeapon;
else }
ammo_lost = database.GetItem(range_id); else {
last_ammo_used = database.GetItem(range_id);
}
} }
} }
} }
@ -1412,10 +1423,12 @@ void Mob::DoThrowingAttackDmg(Mob *other, const EQ::ItemInstance *RangeWeapon, c
int WDmg = 0; int WDmg = 0;
if (!weapon_damage) { if (!weapon_damage) {
if (IsClient() && RangeWeapon) if (IsClient() && RangeWeapon) {
WDmg = GetWeaponDamage(other, RangeWeapon); WDmg = GetWeaponDamage(other, RangeWeapon);
else if (AmmoItem) }
else if (AmmoItem) {
WDmg = GetWeaponDamage(other, AmmoItem); WDmg = GetWeaponDamage(other, AmmoItem);
}
if (LaunchProjectile) { if (LaunchProjectile) {
TryProjectileAttack(other, AmmoItem, EQ::skills::SkillThrowing, WDmg, RangeWeapon, TryProjectileAttack(other, AmmoItem, EQ::skills::SkillThrowing, WDmg, RangeWeapon,
@ -1426,8 +1439,9 @@ void Mob::DoThrowingAttackDmg(Mob *other, const EQ::ItemInstance *RangeWeapon, c
WDmg = weapon_damage; WDmg = weapon_damage;
} }
if (focus) // From FcBaseEffects if (focus) { // no longer used, keep for quests
WDmg += WDmg * focus / 100; WDmg += WDmg * focus / 100;
}
int TotalDmg = 0; int TotalDmg = 0;
@ -1455,29 +1469,28 @@ void Mob::DoThrowingAttackDmg(Mob *other, const EQ::ItemInstance *RangeWeapon, c
other->Damage(this, TotalDmg, SPELL_UNKNOWN, EQ::skills::SkillThrowing); other->Damage(this, TotalDmg, SPELL_UNKNOWN, EQ::skills::SkillThrowing);
if (TotalDmg > 0 && HasSkillProcSuccess() && other && !other->HasDied()) { if (other && !other->HasDied()) {
if (ReuseTime) TryCombatProcs(RangeWeapon, other, EQ::invslot::slotRange, last_ammo_used);
TrySkillProc(other, EQ::skills::SkillThrowing, ReuseTime);
else
TrySkillProc(other, EQ::skills::SkillThrowing, 0, true, EQ::invslot::slotRange);
} }
// end old shit
if (LaunchProjectile)
return;
// Throwing item Proc
if (ammo_lost)
TryWeaponProc(nullptr, ammo_lost, other, EQ::invslot::slotRange);
else if (RangeWeapon && other && !other->HasDied())
TryWeaponProc(RangeWeapon, other, EQ::invslot::slotRange);
if (HasSkillProcs() && other && !other->HasDied()) { if (HasSkillProcs() && other && !other->HasDied()) {
if (ReuseTime) if (ReuseTime) {
TrySkillProc(other, EQ::skills::SkillThrowing, ReuseTime); TrySkillProc(other, EQ::skills::SkillThrowing, ReuseTime);
else }
else {
TrySkillProc(other, EQ::skills::SkillThrowing, 0, false, EQ::invslot::slotRange); TrySkillProc(other, EQ::skills::SkillThrowing, 0, false, EQ::invslot::slotRange);
} }
}
if (HasSkillProcSuccess() && other && !other->HasDied()) {
if (ReuseTime) {
TrySkillProc(other, EQ::skills::SkillThrowing, ReuseTime, true);
}
else {
TrySkillProc(other, EQ::skills::SkillThrowing, 0, true, EQ::invslot::slotRange);
}
}
if (IsClient()) { if (IsClient()) {
CastToClient()->CheckIncreaseSkill(EQ::skills::SkillThrowing, GetTarget()); CastToClient()->CheckIncreaseSkill(EQ::skills::SkillThrowing, GetTarget());
} }
@ -1965,7 +1978,7 @@ void Mob::Taunt(NPC *who, bool always_succeed, int chance_bonus, bool FromSpell,
Mob *hate_top = who->GetHateMost(); Mob *hate_top = who->GetHateMost();
int level_difference = GetLevel() - who->GetLevel(); int level_difference = GetLevel() - who->GetLevel();
bool Success = false; bool success = false;
// Support for how taunt worked pre 2000 on LIVE - Can not taunt NPC over your level. // Support for how taunt worked pre 2000 on LIVE - Can not taunt NPC over your level.
if ((RuleB(Combat, TauntOverLevel) == false) && (level_difference < 0) || if ((RuleB(Combat, TauntOverLevel) == false) && (level_difference < 0) ||
@ -1978,7 +1991,7 @@ void Mob::Taunt(NPC *who, bool always_succeed, int chance_bonus, bool FromSpell,
if ((hate_top && hate_top->GetHPRatio() >= 20) || hate_top == nullptr || chance_bonus) { if ((hate_top && hate_top->GetHPRatio() >= 20) || hate_top == nullptr || chance_bonus) {
// SE_Taunt this is flat chance // SE_Taunt this is flat chance
if (chance_bonus) { if (chance_bonus) {
Success = zone->random.Roll(chance_bonus); success = zone->random.Roll(chance_bonus);
} else { } else {
float tauntchance = 50.0f; float tauntchance = 50.0f;
@ -2012,14 +2025,14 @@ void Mob::Taunt(NPC *who, bool always_succeed, int chance_bonus, bool FromSpell,
tauntchance /= 100.0f; tauntchance /= 100.0f;
Success = tauntchance > zone->random.Real(0, 1); success = tauntchance > zone->random.Real(0, 1);
} }
if (Success) { if (success) {
if (hate_top && hate_top != this) { if (hate_top && hate_top != this) {
int newhate = (who->GetNPCHate(hate_top) - who->GetNPCHate(this)) + 1 + bonus_hate; int newhate = (who->GetNPCHate(hate_top) - who->GetNPCHate(this)) + 1 + bonus_hate;
who->CastToNPC()->AddToHateList(this, newhate); who->CastToNPC()->AddToHateList(this, newhate);
Success = true; success = true;
} else { } else {
who->CastToNPC()->AddToHateList(this, 12); who->CastToNPC()->AddToHateList(this, 12);
} }
@ -2033,12 +2046,14 @@ void Mob::Taunt(NPC *who, bool always_succeed, int chance_bonus, bool FromSpell,
MessageString(Chat::SpellFailure, FAILED_TAUNT); MessageString(Chat::SpellFailure, FAILED_TAUNT);
} }
if (HasSkillProcs()) if (HasSkillProcs()) {
TrySkillProc(who, EQ::skills::SkillTaunt, TauntReuseTime * 1000); TrySkillProc(who, EQ::skills::SkillTaunt, TauntReuseTime * 1000);
}
if (Success && HasSkillProcSuccess()) if (success && HasSkillProcSuccess()) {
TrySkillProc(who, EQ::skills::SkillTaunt, TauntReuseTime * 1000, true); TrySkillProc(who, EQ::skills::SkillTaunt, TauntReuseTime * 1000, true);
} }
}
void Mob::InstillDoubt(Mob *who) { void Mob::InstillDoubt(Mob *who) {
//make sure we can use this skill //make sure we can use this skill
@ -2061,6 +2076,7 @@ void Mob::InstillDoubt(Mob *who) {
//I think this formula needs work //I think this formula needs work
int value = 0; int value = 0;
bool success = false;
//user's bonus //user's bonus
value += GetSkill(EQ::skills::SkillIntimidation) + GetCHA() / 4; value += GetSkill(EQ::skills::SkillIntimidation) + GetCHA() / 4;
@ -2073,6 +2089,7 @@ void Mob::InstillDoubt(Mob *who) {
//cast fear on them... should prolly be a different spell //cast fear on them... should prolly be a different spell
//and should be un-resistable. //and should be un-resistable.
SpellOnTarget(229, who, 0, true, -2000); SpellOnTarget(229, who, 0, true, -2000);
success = true;
//is there a success message? //is there a success message?
} else { } else {
MessageString(Chat::LightBlue,NOT_SCARING); MessageString(Chat::LightBlue,NOT_SCARING);
@ -2083,6 +2100,14 @@ void Mob::InstillDoubt(Mob *who) {
entity_list.AEAttack(target, 50); entity_list.AEAttack(target, 50);
}*/ }*/
} }
if (HasSkillProcs()) {
TrySkillProc(who, EQ::skills::SkillIntimidation, InstillDoubtReuseTime * 1000);
}
if (success && HasSkillProcSuccess()) {
TrySkillProc(who, EQ::skills::SkillIntimidation, InstillDoubtReuseTime * 1000, true);
}
} }
int Mob::TryHeadShot(Mob *defender, EQ::skills::SkillType skillInUse) int Mob::TryHeadShot(Mob *defender, EQ::skills::SkillType skillInUse)
@ -2228,12 +2253,12 @@ void Mob::DoMeleeSkillAttackDmg(Mob *other, uint16 weapon_damage, EQ::skills::Sk
} }
other->AddToHateList(this, hate, 0); other->AddToHateList(this, hate, 0);
if (damage > 0 && aabonuses.SkillAttackProc[SBIndex::SKILLPROC_CHANCE] && aabonuses.SkillAttackProc[SBIndex::SKILLPROC_SKILL] == skillinuse && if (damage > 0 && aabonuses.SkillAttackProc[SBIndex::SKILLATK_PROC_CHANCE] && aabonuses.SkillAttackProc[SBIndex::SKILLATK_PROC_SKILL] == skillinuse &&
IsValidSpell(aabonuses.SkillAttackProc[SBIndex::SKILLPROC_SPELL_ID])) { IsValidSpell(aabonuses.SkillAttackProc[SBIndex::SKILLATK_PROC_SPELL_ID])) {
float chance = aabonuses.SkillAttackProc[SBIndex::SKILLPROC_CHANCE] / 1000.0f; float chance = aabonuses.SkillAttackProc[SBIndex::SKILLATK_PROC_CHANCE] / 1000.0f;
if (zone->random.Roll(chance)) if (zone->random.Roll(chance))
SpellFinished(aabonuses.SkillAttackProc[SBIndex::SKILLPROC_SPELL_ID], other, EQ::spells::CastingSlot::Item, 0, -1, SpellFinished(aabonuses.SkillAttackProc[SBIndex::SKILLATK_PROC_SPELL_ID], other, EQ::spells::CastingSlot::Item, 0, -1,
spells[aabonuses.SkillAttackProc[SBIndex::SKILLPROC_SPELL_ID]].resist_difficulty); spells[aabonuses.SkillAttackProc[SBIndex::SKILLATK_PROC_SPELL_ID]].resist_difficulty);
} }
other->Damage(this, damage, SPELL_UNKNOWN, skillinuse); other->Damage(this, damage, SPELL_UNKNOWN, skillinuse);
@ -2241,12 +2266,14 @@ void Mob::DoMeleeSkillAttackDmg(Mob *other, uint16 weapon_damage, EQ::skills::Sk
if (HasDied()) if (HasDied())
return; return;
if (CanSkillProc && HasSkillProcs()) if (CanSkillProc && HasSkillProcs()) {
TrySkillProc(other, skillinuse, ReuseTime); TrySkillProc(other, skillinuse, ReuseTime);
}
if (CanSkillProc && (damage > 0) && HasSkillProcSuccess()) if (CanSkillProc && (damage > 0) && HasSkillProcSuccess()) {
TrySkillProc(other, skillinuse, ReuseTime, true); TrySkillProc(other, skillinuse, ReuseTime, true);
} }
}
bool Mob::CanDoSpecialAttack(Mob *other) { bool Mob::CanDoSpecialAttack(Mob *other) {
//Make sure everything is valid before doing any attacks. //Make sure everything is valid before doing any attacks.

View File

@ -1821,7 +1821,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove
#ifdef SPELL_EFFECT_SPAM #ifdef SPELL_EFFECT_SPAM
snprintf(effect_desc, _EDLEN, "Weapon Proc: %s (id %d)", spells[effect_value].name, procid); snprintf(effect_desc, _EDLEN, "Weapon Proc: %s (id %d)", spells[effect_value].name, procid);
#endif #endif
AddProcToWeapon(procid, false, 100 + spells[spell_id].limit_value[i], spell_id, caster_level, GetProcLimitTimer(spell_id, SE_WeaponProc)); AddProcToWeapon(procid, false, 100 + spells[spell_id].limit_value[i], spell_id, caster_level, GetProcLimitTimer(spell_id, ProcType::MELEE_PROC));
break; break;
} }
@ -1831,7 +1831,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove
#ifdef SPELL_EFFECT_SPAM #ifdef SPELL_EFFECT_SPAM
snprintf(effect_desc, _EDLEN, "Ranged Proc: %+i", effect_value); snprintf(effect_desc, _EDLEN, "Ranged Proc: %+i", effect_value);
#endif #endif
AddRangedProc(procid, 100 + spells[spell_id].limit_value[i], spell_id, GetProcLimitTimer(spell_id, SE_RangedProc)); AddRangedProc(procid, 100 + spells[spell_id].limit_value[i], spell_id, GetProcLimitTimer(spell_id, ProcType::RANGED_PROC));
break; break;
} }
@ -1841,7 +1841,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove
#ifdef SPELL_EFFECT_SPAM #ifdef SPELL_EFFECT_SPAM
snprintf(effect_desc, _EDLEN, "Defensive Proc: %s (id %d)", spells[effect_value].name, procid); snprintf(effect_desc, _EDLEN, "Defensive Proc: %s (id %d)", spells[effect_value].name, procid);
#endif #endif
AddDefensiveProc(procid, 100 + spells[spell_id].limit_value[i], spell_id, GetProcLimitTimer(spell_id, SE_DefensiveProc)); AddDefensiveProc(procid, 100 + spells[spell_id].limit_value[i], spell_id, GetProcLimitTimer(spell_id, ProcType::DEFENSIVE_PROC));
break; break;
} }
@ -3156,7 +3156,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove
case SE_LimitSpellClass: case SE_LimitSpellClass:
case SE_Sanctuary: case SE_Sanctuary:
case SE_PetMeleeMitigation: case SE_PetMeleeMitigation:
case SE_SkillProc: case SE_SkillProcAttempt:
case SE_SkillProcSuccess: case SE_SkillProcSuccess:
case SE_SpellResistReduction: case SE_SpellResistReduction:
case SE_Duration_HP_Pct: case SE_Duration_HP_Pct:
@ -8518,6 +8518,87 @@ bool Mob::PassCharmTargetRestriction(Mob *target) {
return true; return true;
} }
bool Mob::PassLimitToSkill(EQ::skills::SkillType skill, int32 spell_id, int proc_type, int aa_id)
{
/*
Check if SE_AddMeleProc or SE_RangedProc have a skill limiter. Passes automatically if no skill limiters present.
*/
int32 proc_type_spaid = 0;
if (proc_type == ProcType::MELEE_PROC) {
proc_type_spaid = SE_AddMeleeProc;
}
if (proc_type == ProcType::RANGED_PROC) {
proc_type_spaid = SE_RangedProc;
}
bool match_proc_type = false;
bool has_limit_check = false;
if (!aa_id && spellbonuses.LimitToSkill[EQ::skills::HIGHEST_SKILL + 3]) {
if (spell_id == SPELL_UNKNOWN) {
return false;
}
for (int i = 0; i < EFFECT_COUNT; i++) {
if (spells[spell_id].effect_id[i] == proc_type_spaid) {
match_proc_type = true;
}
if (match_proc_type && spells[spell_id].effect_id[i] == SE_LimitToSkill && spells[spell_id].base_value[i] <= EQ::skills::HIGHEST_SKILL) {
has_limit_check = true;
if (spells[spell_id].base_value[i] == skill) {
return true;
}
}
}
}
else if (aabonuses.LimitToSkill[EQ::skills::HIGHEST_SKILL + 3]) {
int rank_id = 1;
AA::Rank *rank = zone->GetAlternateAdvancementRank(aa_id);
if (!rank) {
return true;
}
AA::Ability *ability_in = rank->base_ability;
if (!ability_in) {
return true;
}
for (auto &aa : aa_ranks) {
auto ability_rank = zone->GetAlternateAdvancementAbilityAndRank(aa.first, aa.second.first);
auto ability = ability_rank.first;
auto rank = ability_rank.second;
if (!ability) {
continue;
}
for (auto &effect : rank->effects) {
if (effect.effect_id == proc_type_spaid) {
match_proc_type = true;
}
if (match_proc_type && effect.effect_id == SE_LimitToSkill && effect.base_value <= EQ::skills::HIGHEST_SKILL) {
has_limit_check = true;
if (effect.base_value == skill) {
return true;
}
}
}
}
}
if (has_limit_check) {
return false; //Limit was found, but not matched, fail.
}
else {
return true; //No limit is present, automatically pass.
}
}
bool Mob::CanFocusUseRandomEffectivenessByType(focusType type) bool Mob::CanFocusUseRandomEffectivenessByType(focusType type)
{ {
switch (type) { switch (type) {
@ -8760,7 +8841,7 @@ bool Mob::IsProcLimitTimerActive(int32 base_spell_id, uint32 proc_reuse_time, in
for (int i = 0; i < MAX_PROC_LIMIT_TIMERS; i++) { for (int i = 0; i < MAX_PROC_LIMIT_TIMERS; i++) {
if (proc_type == SE_WeaponProc) { if (proc_type == ProcType::MELEE_PROC) {
if (spell_proclimit_spellid[i] == base_spell_id) { if (spell_proclimit_spellid[i] == base_spell_id) {
if (spell_proclimit_timer[i].Enabled()) { if (spell_proclimit_timer[i].Enabled()) {
if (spell_proclimit_timer[i].GetRemainingTime() > 0) { if (spell_proclimit_timer[i].GetRemainingTime() > 0) {
@ -8773,7 +8854,7 @@ bool Mob::IsProcLimitTimerActive(int32 base_spell_id, uint32 proc_reuse_time, in
} }
} }
} }
else if (proc_type == SE_RangedProc) { else if (proc_type == ProcType::RANGED_PROC) {
if (ranged_proclimit_spellid[i] == base_spell_id) { if (ranged_proclimit_spellid[i] == base_spell_id) {
if (ranged_proclimit_timer[i].Enabled()) { if (ranged_proclimit_timer[i].Enabled()) {
if (ranged_proclimit_timer[i].GetRemainingTime() > 0) { if (ranged_proclimit_timer[i].GetRemainingTime() > 0) {
@ -8786,7 +8867,7 @@ bool Mob::IsProcLimitTimerActive(int32 base_spell_id, uint32 proc_reuse_time, in
} }
} }
} }
else if (proc_type == SE_DefensiveProc) { else if (proc_type == ProcType::DEFENSIVE_PROC) {
if (def_proclimit_spellid[i] == base_spell_id) { if (def_proclimit_spellid[i] == base_spell_id) {
if (def_proclimit_timer[i].Enabled()) { if (def_proclimit_timer[i].Enabled()) {
if (def_proclimit_timer[i].GetRemainingTime() > 0) { if (def_proclimit_timer[i].GetRemainingTime() > 0) {
@ -8813,7 +8894,7 @@ void Mob::SetProcLimitTimer(int32 base_spell_id, uint32 proc_reuse_time, int pro
for (int i = 0; i < MAX_PROC_LIMIT_TIMERS; i++) { for (int i = 0; i < MAX_PROC_LIMIT_TIMERS; i++) {
if (proc_type == SE_WeaponProc) { if (proc_type == ProcType::MELEE_PROC) {
if (!spell_proclimit_spellid[i] && !is_set) { if (!spell_proclimit_spellid[i] && !is_set) {
spell_proclimit_spellid[i] = base_spell_id; spell_proclimit_spellid[i] = base_spell_id;
spell_proclimit_timer[i].SetTimer(proc_reuse_time); spell_proclimit_timer[i].SetTimer(proc_reuse_time);
@ -8825,7 +8906,7 @@ void Mob::SetProcLimitTimer(int32 base_spell_id, uint32 proc_reuse_time, int pro
} }
} }
if (proc_type == SE_RangedProc) { if (proc_type == ProcType::RANGED_PROC) {
if (!ranged_proclimit_spellid[i] && !is_set) { if (!ranged_proclimit_spellid[i] && !is_set) {
ranged_proclimit_spellid[i] = base_spell_id; ranged_proclimit_spellid[i] = base_spell_id;
ranged_proclimit_timer[i].SetTimer(proc_reuse_time); ranged_proclimit_timer[i].SetTimer(proc_reuse_time);
@ -8837,7 +8918,7 @@ void Mob::SetProcLimitTimer(int32 base_spell_id, uint32 proc_reuse_time, int pro
} }
} }
if (proc_type == SE_DefensiveProc) { if (proc_type == ProcType::DEFENSIVE_PROC) {
if (!def_proclimit_spellid[i] && !is_set) { if (!def_proclimit_spellid[i] && !is_set) {
def_proclimit_spellid[i] = base_spell_id; def_proclimit_spellid[i] = base_spell_id;
def_proclimit_timer[i].SetTimer(proc_reuse_time); def_proclimit_timer[i].SetTimer(proc_reuse_time);

View File

@ -1014,8 +1014,6 @@ void Mob::TuneMeleeMitigation(Mob *attacker, DamageHitInfo &hit, int ac_override
// +0.5 for rounding, min to 1 dmg // +0.5 for rounding, min to 1 dmg
hit.damage_done = std::max(static_cast<int>(roll * static_cast<double>(hit.base_damage) + 0.5), 1); hit.damage_done = std::max(static_cast<int>(roll * static_cast<double>(hit.base_damage) + 0.5), 1);
//Shout("mitigation %d vs offense %d. base %d rolled %f damage %d", mitigation, hit.offense, hit.base_damage, roll, hit.damage_done);
} }
int Mob::TuneACSum(bool skip_caps, int ac_override, int add_ac) int Mob::TuneACSum(bool skip_caps, int ac_override, int add_ac)
@ -1093,10 +1091,6 @@ int Mob::TuneACSum(bool skip_caps, int ac_override, int add_ac)
auto over_cap = ac - softcap; auto over_cap = ac - softcap;
ac = softcap + (over_cap * returns); ac = softcap + (over_cap * returns);
} }
//Shout("ACSum ac %i softcap %i returns %.2f", ac, softcap, static_cast<float>(returns));
}
else {
//Shout("ACSum ac %i", ac);
} }
return ac; return ac;
@ -1384,7 +1378,6 @@ bool Mob::TuneCheckHitChance(Mob* other, DamageHitInfo &hit, int avoidance_overr
Mob *attacker = other; Mob *attacker = other;
Mob *defender = this; Mob *defender = this;
//Shout("CheckHitChance(%s) attacked by %s", defender->GetName(), attacker->GetName());
if (defender->IsClient() && defender->CastToClient()->IsSitting()) if (defender->IsClient() && defender->CastToClient()->IsSitting())
return true; return true;
@ -1402,7 +1395,6 @@ bool Mob::TuneCheckHitChance(Mob* other, DamageHitInfo &hit, int avoidance_overr
// Then your chance to simply avoid the attack is checked (defender's avoidance roll beat the attacker's accuracy roll.) // Then your chance to simply avoid the attack is checked (defender's avoidance roll beat the attacker's accuracy roll.)
int tohit_roll = zone->random.Roll0(accuracy); int tohit_roll = zone->random.Roll0(accuracy);
int avoid_roll = zone->random.Roll0(avoidance); int avoid_roll = zone->random.Roll0(avoidance);
//Shout("CheckHitChance accuracy(%d => %d) avoidance(%d => %d)", accuracy, tohit_roll, avoidance, avoid_roll);
// tie breaker? Don't want to be biased any one way // tie breaker? Don't want to be biased any one way
if (tohit_roll == avoid_roll) if (tohit_roll == avoid_roll)