From 01bd8bd9faf02863ea022cb3a7a86a9f9209727c Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Thu, 27 Feb 2014 03:19:25 -0500 Subject: [PATCH] Reworked Weapon Procing Broke up the logic to be a bit easier to follow Procs should happen in the order that they do on live Proc rates for spell buff procs should be more consistent with live --- zone/attack.cpp | 284 ++++++++++++++++++++++++------------------------ zone/bot.cpp | 35 +++--- zone/bot.h | 2 +- zone/mob.h | 3 +- 4 files changed, 161 insertions(+), 163 deletions(-) diff --git a/zone/attack.cpp b/zone/attack.cpp index 78ab250cd..b658ba655 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -3885,19 +3885,13 @@ void Mob::HealDamage(uint32 amount, Mob* caster) { safe_delete_array(TempString); } - - //proc chance includes proc bonus -float Mob::GetProcChances(float &BaseProcChance, float &ProcBonus, float &ProcChance, uint16 weapon_speed, uint16 hand) { +float Mob::GetProcChances(float ProcBonus, uint16 weapon_speed, uint16 hand) +{ int mydex = GetDEX(); - ProcBonus = 0; - ProcChance = 0; - BaseProcChance = 0; + float ProcChance = 0.0f; - ProcBonus = float(aabonuses.ProcChanceSPA + spellbonuses.ProcChanceSPA + itembonuses.ProcChanceSPA); //Spell Effects - ProcBonus += float(itembonuses.ProcChance)/10.0f; //Combat Effects - - switch(hand){ + switch (hand) { case 13: weapon_speed = attack_timer.GetDuration(); break; @@ -3909,24 +3903,20 @@ float Mob::GetProcChances(float &BaseProcChance, float &ProcBonus, float &ProcCh break; } - //calculate the weapon speed in ms, so we can use the rule to compare against. - - if(weapon_speed < RuleI(Combat, MinHastedDelay)) // fast as a client can swing, so should be the floor of the proc chance + // fast as a client can swing, so should be the floor of the proc chance + if (weapon_speed < RuleI(Combat, MinHastedDelay)) weapon_speed = RuleI(Combat, MinHastedDelay); - if(RuleB(Combat, AdjustProcPerMinute) == true) - { - ProcChance = ((float)weapon_speed * RuleR(Combat, AvgProcsPerMinute) / 60000.0f); // compensate for weapon_speed being in ms - BaseProcChance = ProcChance; - ProcBonus += float(mydex) * RuleR(Combat, ProcPerMinDexContrib); - ProcChance += ProcChance*ProcBonus/100.0f; - } - else - { - ProcChance = RuleR(Combat, BaseProcChance) + float(mydex) / RuleR(Combat, ProcDexDivideBy); - BaseProcChance = ProcChance; - ProcChance += ProcChance*ProcBonus/100.0f; + if (RuleB(Combat, AdjustProcPerMinute)) { + ProcChance = (static_cast(weapon_speed) * + RuleR(Combat, AvgProcsPerMinute) / 60000.0f); // compensate for weapon_speed being in ms + ProcBonus += static_cast(mydex) * RuleR(Combat, ProcPerMinDexContrib); + ProcChance += ProcChance * ProcBonus / 100.0f; + } else { + ProcChance = RuleR(Combat, BaseProcChance) + + static_cast(mydex) / RuleR(Combat, ProcDexDivideBy); + ProcChance += ProcChance * ProcBonus / 100.0f; } mlog(COMBAT__PROCS, "Proc chance %.2f (%.2f from bonuses)", ProcChance, ProcBonus); @@ -4029,162 +4019,178 @@ void Mob::TryWeaponProc(const ItemInst* weapon_g, Mob *on, uint16 hand) { } if(!weapon_g) { - TryWeaponProc(nullptr, (const Item_Struct*)nullptr, on, hand); + TrySpellProc(nullptr, (const Item_Struct*)nullptr, on); return; } if(!weapon_g->IsType(ItemClassCommon)) { - TryWeaponProc(nullptr, (const Item_Struct*) nullptr, on, hand); + TrySpellProc(nullptr, (const Item_Struct*)nullptr, on); return; } - //do main procs + // Innate + aug procs from weapons + // TODO: powersource procs TryWeaponProc(weapon_g, weapon_g->GetItem(), on, hand); + // Procs from Buffs and AA both melee and range + TrySpellProc(weapon_g, weapon_g->GetItem(), on, hand); + return; +} - //we have to calculate these again, oh well +void Mob::TryWeaponProc(const ItemInst *inst, const Item_Struct *weapon, Mob *on, uint16 hand) +{ + if (!weapon) + return; + uint16 skillinuse = 28; int ourlevel = GetLevel(); - float ProcChance, ProcBonus, BaseProcChance; - GetProcChances(BaseProcChance, ProcBonus, ProcChance, weapon_g->GetItem()->Delay, hand); - if(hand != 13) - { + float ProcBonus = static_cast(aabonuses.ProcChanceSPA + + spellbonuses.ProcChanceSPA + itembonuses.ProcChanceSPA); + ProcBonus += static_cast(itembonuses.ProcChance) / 10.0f; // Combat Effects + float ProcChance = GetProcChances(ProcBonus, weapon->Delay, hand); + + if (hand != 13) //Is Archery intened to proc at 50% rate? ProcChance /= 2; + + // Try innate proc on weapon + // We can proc once here, either weapon or one aug + bool proced = false; // silly bool to prevent augs from going if weapon does + skillinuse = GetSkillByItemType(weapon->ItemType); + if (weapon->Proc.Type == ET_CombatProc) { + float WPC = ProcChance * (100.0f + // Proc chance for this weapon + static_cast(weapon->ProcRate)) / 100.0f; + if (MakeRandomFloat(0, 1) <= WPC) { // 255 dex = 0.084 chance of proc. No idea what this number should be really. + if (weapon->Proc.Level > ourlevel) { + mlog(COMBAT__PROCS, + "Tried to proc (%s), but our level (%d) is lower than required (%d)", + weapon->Name, ourlevel, weapon->Proc.Level); + if (IsPet()) { + Mob *own = GetOwner(); + if (own) + own->Message_StringID(13, PROC_PETTOOLOW); + } else { + Message_StringID(13, PROC_TOOLOW); + } + } else { + mlog(COMBAT__PROCS, + "Attacking weapon (%s) successfully procing spell %d (%.2f percent chance)", + weapon->Name, weapon->Proc.Effect, WPC * 100); + ExecWeaponProc(inst, weapon->Proc.Effect, on); + proced = true; + } + } } - //do augment procs - int r; - for(r = 0; r < MAX_AUGMENT_SLOTS; r++) { - const ItemInst* aug_i = weapon_g->GetAugment(r); - if(!aug_i) - continue; - const Item_Struct* aug = aug_i->GetItem(); - if(!aug) - continue; + if (!proced) { + for (int r = 0; r < MAX_AUGMENT_SLOTS; r++) { + const ItemInst *aug_i = inst->GetAugment(r); + if (!aug_i) // no aug, try next slot! + continue; + const Item_Struct *aug = aug_i->GetItem(); + if (!aug) + continue; - if (aug->Proc.Type == ET_CombatProc) { - ProcChance = ProcChance*(100+aug->ProcRate)/100; - if (MakeRandomFloat(0, 1) < ProcChance) { - if(aug->Proc.Level > ourlevel) { - Mob * own = GetOwner(); - if(own != nullptr) { - own->Message_StringID(13,PROC_PETTOOLOW); + if (aug->Proc.Type == ET_CombatProc) { + float APC = ProcChance * (100.0f + // Proc chance for this aug + static_cast(aug->ProcRate)) / 100.0f; + if (MakeRandomFloat(0, 1) <= APC) { + if (IsPet()) { + Mob *own = GetOwner(); + if (own) + own->Message_StringID(13, PROC_PETTOOLOW); } else { - Message_StringID(13,PROC_TOOLOW); + Message_StringID(13, PROC_TOOLOW); } } else { ExecWeaponProc(aug_i, aug->Proc.Effect, on); + break; } } } } + // TODO: Powersource procs + if (HasSkillProcs()) + TrySkillProc(on, skillinuse, ProcChance); + + return; } -void Mob::TryWeaponProc(const ItemInst *inst, const Item_Struct* weapon, Mob *on, uint16 hand) { - uint16 skillinuse = 28; - int ourlevel = GetLevel(); - float ProcChance, ProcBonus, BaseProcChance; - if(weapon!=nullptr) - GetProcChances(BaseProcChance, ProcBonus, ProcChance, weapon->Delay, hand); +void Mob::TrySpellProc(const ItemInst *inst, const Item_Struct *weapon, Mob *on, uint16 hand) +{ + float ProcBonus = static_cast(spellbonuses.SpellProcChance + + itembonuses.SpellProcChance + aabonuses.SpellProcChance); + float ProcChance = 0.0f; + if (weapon) + ProcChance = GetProcChances(ProcBonus, weapon->Delay, hand); else - GetProcChances(BaseProcChance, ProcBonus, ProcChance); + ProcChance = GetProcChances(ProcBonus); - if(hand != 13) //Is Archery intended to proc at 50% rate? + if (hand != 13) //Is Archery intened to proc at 50% rate? ProcChance /= 2; - //give weapon a chance to proc first. - if(weapon != nullptr) { - skillinuse = GetSkillByItemType(weapon->ItemType); - if (weapon->Proc.Type == ET_CombatProc) { - float WPC = ProcChance*(100.0f+(float)weapon->ProcRate)/100.0f; - if (MakeRandomFloat(0, 1) <= WPC) { // 255 dex = 0.084 chance of proc. No idea what this number should be really. - if(weapon->Proc.Level > ourlevel) { - mlog(COMBAT__PROCS, "Tried to proc (%s), but our level (%d) is lower than required (%d)", weapon->Name, ourlevel, weapon->Proc.Level); - Mob * own = GetOwner(); - if(own != nullptr) { - own->Message_StringID(13,PROC_PETTOOLOW); - } else { - Message_StringID(13,PROC_TOOLOW); - } + bool rangedattk = false; + if (weapon && hand == 11) { + if (weapon->ItemType == ItemTypeArrow || + weapon->ItemType == ItemTypeLargeThrowing || + weapon->ItemType == ItemTypeSmallThrowing || + weapon->ItemType == ItemTypeBow) + rangedattk = true; + } + + for (uint32 i = 0; i < MAX_PROCS; i++) { + if (IsPet() && hand == 13) //Pets can only proc spell procs from their primay hand (ie; beastlord pets) + continue; // If pets ever can proc from off hand, this will need to change + + // Not ranged + if (!rangedattk) { + // Perma procs (AAs) + if (PermaProcs[i].spellID != SPELL_UNKNOWN) { + if (MakeRandomInt(0, 99) < PermaProcs[i].chance) { // TODO: Do these get spell bonus? + mlog(COMBAT__PROCS, + "Permanent proc %d procing spell %d (%d percent chance)", + i, PermaProcs[i].spellID, PermaProcs[i].chance); + ExecWeaponProc(nullptr, PermaProcs[i].spellID, on); } else { - mlog(COMBAT__PROCS, "Attacking weapon (%s) successfully procing spell %d (%.2f percent chance)", weapon->Name, weapon->Proc.Effect, ProcChance*100); - ExecWeaponProc(inst, weapon->Proc.Effect, on); + mlog(COMBAT__PROCS, + "Permanent proc %d failed to proc %d (%d percent chance)", + i, PermaProcs[i].spellID, PermaProcs[i].chance); } - } else { - mlog(COMBAT__PROCS, "Attacking weapon (%s) did no proc (%.2f percent chance).", weapon->Name, ProcChance*100); } - } - } - if(ProcBonus == -1) { - LogFile->write(EQEMuLog::Error, "ProcBonus was -1 value!"); - return; - } - - bool bRangedAttack = false; - if (weapon != nullptr) { - if (weapon->ItemType == ItemTypeBow || weapon->ItemType == ItemTypeLargeThrowing || weapon->ItemType == ItemTypeSmallThrowing) { - bRangedAttack = true; - } - } - - bool isRanged = false; - if(weapon) - { - if(weapon->ItemType == ItemTypeArrow || - weapon->ItemType == ItemTypeLargeThrowing || - weapon->ItemType == ItemTypeSmallThrowing || - weapon->ItemType == ItemTypeBow) - { - isRanged = true; - } - } - - int16 SpellProcChance = spellbonuses.SpellProcChance + itembonuses.SpellProcChance + aabonuses.SpellProcChance; - uint32 i; - for(i = 0; i < MAX_PROCS; i++) { - if (PermaProcs[i].spellID != SPELL_UNKNOWN) { - if(MakeRandomInt(0, 100) < PermaProcs[i].chance) { //TODO: Unclear if these are treated like Spells or WeaponProcs - mlog(COMBAT__PROCS, "Permanent proc %d procing spell %d (%d percent chance)", i, PermaProcs[i].spellID, PermaProcs[i].chance); - ExecWeaponProc(nullptr, PermaProcs[i].spellID, on); - } else { - mlog(COMBAT__PROCS, "Permanent proc %d failed to proc %d (%d percent chance)", i, PermaProcs[i].spellID, PermaProcs[i].chance); - } - } - - if(!isRanged) - { - if(IsPet() && hand != 13) //Pets can only proc spell procs from their primay hand (ie; beastlord pets) - { - //Maybe implement this later if pets are ever given dual procs? - } - else - { - int chance = BaseProcChance * (SpellProcs[i].chance); - chance += chance*SpellProcChance/100; - if(MakeRandomInt(0, 100) < chance) { - mlog(COMBAT__PROCS, "Spell proc %d procing spell %d (%d percent chance)", i, SpellProcs[i].spellID, chance); + // Spell procs (buffs) + if (SpellProcs[i].spellID != SPELL_UNKNOWN) { + float chance = ProcChance * (SpellProcs[i].chance / 100.0f); + if (MakeRandomFloat(0, 1) <= chance) { + mlog(COMBAT__PROCS, + "Spell proc %d procing spell %d (%.2f percent chance)", + i, SpellProcs[i].spellID, chance); ExecWeaponProc(nullptr, SpellProcs[i].spellID, on); CheckNumHitsRemaining(11, 0, SpellProcs[i].base_spellID); } else { - mlog(COMBAT__PROCS, "Spell proc %d failed to proc %d (%d percent chance)", i, SpellProcs[i].spellID, chance); + mlog(COMBAT__PROCS, + "Spell proc %d failed to proc %d (%.2f percent chance)", + i, SpellProcs[i].spellID, chance); } } - } - if (bRangedAttack) { - int chance = BaseProcChance * RangedProcs[i].chance; - chance += chance*SpellProcChance/100; - if(MakeRandomInt(0, 100) < chance) { - mlog(COMBAT__PROCS, "Ranged proc %d procing spell %d", i, RangedProcs[i].spellID, RangedProcs[i].chance); - ExecWeaponProc(nullptr, RangedProcs[i].spellID, on); - CheckNumHitsRemaining(11, 0, RangedProcs[i].base_spellID); - } else { - mlog(COMBAT__PROCS, "Ranged proc %d failed to proc %d", i, RangedProcs[i].spellID, RangedProcs[i].chance); + } else if (rangedattk) { // ranged only + // ranged spell procs (buffs) + if (RangedProcs[i].spellID != SPELL_UNKNOWN) { + float chance = ProcChance * (RangedProcs[i].chance / 100.0f); + if (MakeRandomFloat(0, 1) <= chance) { + mlog(COMBAT__PROCS, + "Ranged proc %d procing spell %d (%.2f percent chance)", + i, RangedProcs[i].spellID, chance); + ExecWeaponProc(nullptr, RangedProcs[i].spellID, on); + CheckNumHitsRemaining(11, 0, RangedProcs[i].base_spellID); + } else { + mlog(COMBAT__PROCS, + "Ranged proc %d failed to proc %d (%.2f percent chance)", + i, RangedProcs[i].spellID, chance); + } } } } - if (HasSkillProcs()) - TrySkillProc(on, skillinuse, ProcChance); + return; } void Mob::TryPetCriticalHit(Mob *defender, uint16 skill, int32 &damage) diff --git a/zone/bot.cpp b/zone/bot.cpp index 3cac34024..2b9580452 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -7705,16 +7705,11 @@ int16 Bot::CalcBotFocusEffect(BotfocusType bottype, uint16 focus_id, uint16 spel } //proc chance includes proc bonus -float Bot::GetProcChances(float &BaseProcChance, float &ProcBonus, float &ProcChance, uint16 weapon_speed, uint16 hand) { +float Bot::GetProcChances(float ProcBonus, uint16 weapon_speed, uint16 hand) { int mydex = GetDEX(); - ProcBonus = 0; - ProcChance = 0; - BaseProcChance = 0; + float ProcChance = 0.0f; - ProcBonus = float(aabonuses.ProcChanceSPA + spellbonuses.ProcChanceSPA + itembonuses.ProcChanceSPA); //Spell Effects - ProcBonus += float(itembonuses.ProcChance)/10.0f; //Combat Effects - - switch(hand){ + switch (hand) { case SLOT_PRIMARY: weapon_speed = attack_timer.GetDuration(); break; @@ -7726,23 +7721,19 @@ float Bot::GetProcChances(float &BaseProcChance, float &ProcBonus, float &ProcCh break; } - //calculate the weapon speed in ms, so we can use the rule to compare against. - - if(weapon_speed < RuleI(Combat, MinHastedDelay)) // fast as a client can swing, so should be the floor of the proc chance + // fast as a client can swing, so should be the floor of the proc chance + if (weapon_speed < RuleI(Combat, MinHastedDelay)) weapon_speed = RuleI(Combat, MinHastedDelay); - if(RuleB(Combat, AdjustProcPerMinute) == true) - { - ProcChance = ((float)weapon_speed * RuleR(Combat, AvgProcsPerMinute) / 60000.0f); // compensate for weapon_speed being in ms - ProcChance = BaseProcChance; - ProcBonus += float(mydex) * RuleR(Combat, ProcPerMinDexContrib); - ProcChance += ProcChance*ProcBonus / 100.0f; - } - else - { - ProcChance = RuleR(Combat, BaseProcChance) + float(mydex) / RuleR(Combat, ProcDexDivideBy); - ProcChance = BaseProcChance; + if (RuleB(Combat, AdjustProcPerMinute)) { + ProcChance = (static_cast(weapon_speed) * + RuleR(Combat, AvgProcsPerMinute) / 60000.0f); // compensate for weapon_speed being in ms + ProcBonus += static_cast(mydex) * RuleR(Combat, ProcPerMinDexContrib); + ProcChance += ProcChance * ProcBonus / 100.0f; + } else { + ProcChance = RuleR(Combat, BaseProcChance) + + static_cast(mydex) / RuleR(Combat, ProcDexDivideBy); ProcChance += ProcChance*ProcBonus / 100.0f; } diff --git a/zone/bot.h b/zone/bot.h index 8cf439c57..b0ff52845 100644 --- a/zone/bot.h +++ b/zone/bot.h @@ -167,7 +167,7 @@ public: uint16 BotGetSpells(int spellslot) { return AIspells[spellslot].spellid; } uint16 BotGetSpellType(int spellslot) { return AIspells[spellslot].type; } uint16 BotGetSpellPriority(int spellslot) { return AIspells[spellslot].priority; } - virtual float GetProcChances(float &BaseProcChance, float &ProcBonus, float &ProcChance, uint16 weapon_speed, uint16 hand); + virtual float GetProcChances(float ProcBonus, uint16 weapon_speed, uint16 hand); virtual bool AvoidDamage(Mob* other, int32 &damage, bool CanRiposte); virtual int GetMonkHandToHandDamage(void); virtual bool TryFinishingBlow(Mob *defender, SkillUseTypes skillinuse); diff --git a/zone/mob.h b/zone/mob.h index 47e87412f..9b5b353eb 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -969,9 +969,10 @@ protected: bool PassLimitClass(uint32 Classes_, uint16 Class_); void TryDefensiveProc(const ItemInst* weapon, Mob *on, uint16 hand = 13, int damage=0); void TryWeaponProc(const ItemInst* inst, const Item_Struct* weapon, Mob *on, uint16 hand = 13); + void TrySpellProc(const ItemInst* inst, const Item_Struct* weapon, Mob *on, uint16 hand = 13); void TryWeaponProc(const ItemInst* weapon, Mob *on, uint16 hand = 13); void ExecWeaponProc(const ItemInst* weapon, uint16 spell_id, Mob *on); - virtual float GetProcChances(float &BaseProcChance, float &ProcBonus, float &ProcChance, uint16 weapon_speed = 30, uint16 hand = 13); + virtual float GetProcChances(float ProcBonus, uint16 weapon_speed = 30, uint16 hand = 13); virtual float GetDefensiveProcChances(float &ProcBonus, float &ProcChance, uint16 weapon_speed = 30, uint16 hand = 13); int GetWeaponDamage(Mob *against, const Item_Struct *weapon_item); int GetWeaponDamage(Mob *against, const ItemInst *weapon_item, uint32 *hate = nullptr);