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
This commit is contained in:
Michael Cook (mackal) 2014-02-27 03:19:25 -05:00
parent 489a6ffd16
commit 01bd8bd9fa
4 changed files with 161 additions and 163 deletions

View File

@ -3885,19 +3885,13 @@ void Mob::HealDamage(uint32 amount, Mob* caster) {
safe_delete_array(TempString); safe_delete_array(TempString);
} }
//proc chance includes proc bonus //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(); int mydex = GetDEX();
ProcBonus = 0; float ProcChance = 0.0f;
ProcChance = 0;
BaseProcChance = 0;
ProcBonus = float(aabonuses.ProcChanceSPA + spellbonuses.ProcChanceSPA + itembonuses.ProcChanceSPA); //Spell Effects switch (hand) {
ProcBonus += float(itembonuses.ProcChance)/10.0f; //Combat Effects
switch(hand){
case 13: case 13:
weapon_speed = attack_timer.GetDuration(); weapon_speed = attack_timer.GetDuration();
break; break;
@ -3909,24 +3903,20 @@ float Mob::GetProcChances(float &BaseProcChance, float &ProcBonus, float &ProcCh
break; break;
} }
//calculate the weapon speed in ms, so we can use the rule to compare against. //calculate the weapon speed in ms, so we can use the rule to compare against.
// fast as a client can swing, so should be the floor of the proc chance
if(weapon_speed < RuleI(Combat, MinHastedDelay)) // 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); weapon_speed = RuleI(Combat, MinHastedDelay);
if(RuleB(Combat, AdjustProcPerMinute) == true) if (RuleB(Combat, AdjustProcPerMinute)) {
{ ProcChance = (static_cast<float>(weapon_speed) *
ProcChance = ((float)weapon_speed * RuleR(Combat, AvgProcsPerMinute) / 60000.0f); // compensate for weapon_speed being in ms RuleR(Combat, AvgProcsPerMinute) / 60000.0f); // compensate for weapon_speed being in ms
BaseProcChance = ProcChance; ProcBonus += static_cast<float>(mydex) * RuleR(Combat, ProcPerMinDexContrib);
ProcBonus += float(mydex) * RuleR(Combat, ProcPerMinDexContrib); ProcChance += ProcChance * ProcBonus / 100.0f;
ProcChance += ProcChance*ProcBonus/100.0f; } else {
} ProcChance = RuleR(Combat, BaseProcChance) +
else static_cast<float>(mydex) / RuleR(Combat, ProcDexDivideBy);
{ ProcChance += ProcChance * ProcBonus / 100.0f;
ProcChance = RuleR(Combat, BaseProcChance) + float(mydex) / RuleR(Combat, ProcDexDivideBy);
BaseProcChance = ProcChance;
ProcChance += ProcChance*ProcBonus/100.0f;
} }
mlog(COMBAT__PROCS, "Proc chance %.2f (%.2f from bonuses)", ProcChance, ProcBonus); 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) { if(!weapon_g) {
TryWeaponProc(nullptr, (const Item_Struct*)nullptr, on, hand); TrySpellProc(nullptr, (const Item_Struct*)nullptr, on);
return; return;
} }
if(!weapon_g->IsType(ItemClassCommon)) { if(!weapon_g->IsType(ItemClassCommon)) {
TryWeaponProc(nullptr, (const Item_Struct*) nullptr, on, hand); TrySpellProc(nullptr, (const Item_Struct*)nullptr, on);
return; return;
} }
//do main procs // Innate + aug procs from weapons
// TODO: powersource procs
TryWeaponProc(weapon_g, weapon_g->GetItem(), on, hand); 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(); int ourlevel = GetLevel();
float ProcChance, ProcBonus, BaseProcChance; float ProcBonus = static_cast<float>(aabonuses.ProcChanceSPA +
GetProcChances(BaseProcChance, ProcBonus, ProcChance, weapon_g->GetItem()->Delay, hand); spellbonuses.ProcChanceSPA + itembonuses.ProcChanceSPA);
if(hand != 13) ProcBonus += static_cast<float>(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; 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<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);
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 if (!proced) {
int r; for (int r = 0; r < MAX_AUGMENT_SLOTS; r++) {
for(r = 0; r < MAX_AUGMENT_SLOTS; r++) { const ItemInst *aug_i = inst->GetAugment(r);
const ItemInst* aug_i = weapon_g->GetAugment(r); if (!aug_i) // no aug, try next slot!
if(!aug_i)
continue; continue;
const Item_Struct* aug = aug_i->GetItem(); const Item_Struct *aug = aug_i->GetItem();
if(!aug) if (!aug)
continue; continue;
if (aug->Proc.Type == ET_CombatProc) { if (aug->Proc.Type == ET_CombatProc) {
ProcChance = ProcChance*(100+aug->ProcRate)/100; float APC = ProcChance * (100.0f + // Proc chance for this aug
if (MakeRandomFloat(0, 1) < ProcChance) { static_cast<float>(aug->ProcRate)) / 100.0f;
if(aug->Proc.Level > ourlevel) { if (MakeRandomFloat(0, 1) <= APC) {
Mob * own = GetOwner(); if (IsPet()) {
if(own != nullptr) { Mob *own = GetOwner();
own->Message_StringID(13,PROC_PETTOOLOW); if (own)
own->Message_StringID(13, PROC_PETTOOLOW);
} else { } else {
Message_StringID(13,PROC_TOOLOW); Message_StringID(13, PROC_TOOLOW);
} }
} else { } else {
ExecWeaponProc(aug_i, aug->Proc.Effect, on); 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) { void Mob::TrySpellProc(const ItemInst *inst, const Item_Struct *weapon, Mob *on, uint16 hand)
uint16 skillinuse = 28; {
int ourlevel = GetLevel(); float ProcBonus = static_cast<float>(spellbonuses.SpellProcChance +
float ProcChance, ProcBonus, BaseProcChance; itembonuses.SpellProcChance + aabonuses.SpellProcChance);
if(weapon!=nullptr) float ProcChance = 0.0f;
GetProcChances(BaseProcChance, ProcBonus, ProcChance, weapon->Delay, hand); if (weapon)
ProcChance = GetProcChances(ProcBonus, weapon->Delay, hand);
else 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; ProcChance /= 2;
//give weapon a chance to proc first. bool rangedattk = false;
if(weapon != nullptr) { if (weapon && hand == 11) {
skillinuse = GetSkillByItemType(weapon->ItemType); if (weapon->ItemType == ItemTypeArrow ||
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);
}
} 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);
}
} 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 == ItemTypeLargeThrowing ||
weapon->ItemType == ItemTypeSmallThrowing || weapon->ItemType == ItemTypeSmallThrowing ||
weapon->ItemType == ItemTypeBow) weapon->ItemType == ItemTypeBow)
{ rangedattk = true;
isRanged = true;
}
} }
int16 SpellProcChance = spellbonuses.SpellProcChance + itembonuses.SpellProcChance + aabonuses.SpellProcChance; for (uint32 i = 0; i < MAX_PROCS; i++) {
uint32 i; if (IsPet() && hand == 13) //Pets can only proc spell procs from their primay hand (ie; beastlord pets)
for(i = 0; i < MAX_PROCS; i++) { 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 (PermaProcs[i].spellID != SPELL_UNKNOWN) {
if(MakeRandomInt(0, 100) < PermaProcs[i].chance) { //TODO: Unclear if these are treated like Spells or WeaponProcs 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); 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); ExecWeaponProc(nullptr, PermaProcs[i].spellID, on);
} else { } else {
mlog(COMBAT__PROCS, "Permanent proc %d failed to proc %d (%d percent chance)", i, PermaProcs[i].spellID, PermaProcs[i].chance); mlog(COMBAT__PROCS,
"Permanent proc %d failed to proc %d (%d percent chance)",
i, PermaProcs[i].spellID, PermaProcs[i].chance);
} }
} }
if(!isRanged) // Spell procs (buffs)
{ if (SpellProcs[i].spellID != SPELL_UNKNOWN) {
if(IsPet() && hand != 13) //Pets can only proc spell procs from their primay hand (ie; beastlord pets) float chance = ProcChance * (SpellProcs[i].chance / 100.0f);
{ if (MakeRandomFloat(0, 1) <= chance) {
//Maybe implement this later if pets are ever given dual procs? mlog(COMBAT__PROCS,
} "Spell proc %d procing spell %d (%.2f percent chance)",
else i, SpellProcs[i].spellID, chance);
{
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);
ExecWeaponProc(nullptr, SpellProcs[i].spellID, on); ExecWeaponProc(nullptr, SpellProcs[i].spellID, on);
CheckNumHitsRemaining(11, 0, SpellProcs[i].base_spellID); CheckNumHitsRemaining(11, 0, SpellProcs[i].base_spellID);
} else { } 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);
} }
} }
} } else if (rangedattk) { // ranged only
if (bRangedAttack) { // ranged spell procs (buffs)
int chance = BaseProcChance * RangedProcs[i].chance; if (RangedProcs[i].spellID != SPELL_UNKNOWN) {
chance += chance*SpellProcChance/100; float chance = ProcChance * (RangedProcs[i].chance / 100.0f);
if(MakeRandomInt(0, 100) < chance) { if (MakeRandomFloat(0, 1) <= chance) {
mlog(COMBAT__PROCS, "Ranged proc %d procing spell %d", i, RangedProcs[i].spellID, RangedProcs[i].chance); mlog(COMBAT__PROCS,
"Ranged proc %d procing spell %d (%.2f percent chance)",
i, RangedProcs[i].spellID, chance);
ExecWeaponProc(nullptr, RangedProcs[i].spellID, on); ExecWeaponProc(nullptr, RangedProcs[i].spellID, on);
CheckNumHitsRemaining(11, 0, RangedProcs[i].base_spellID); CheckNumHitsRemaining(11, 0, RangedProcs[i].base_spellID);
} else { } else {
mlog(COMBAT__PROCS, "Ranged proc %d failed to proc %d", i, RangedProcs[i].spellID, RangedProcs[i].chance); mlog(COMBAT__PROCS,
"Ranged proc %d failed to proc %d (%.2f percent chance)",
i, RangedProcs[i].spellID, chance);
}
} }
} }
} }
if (HasSkillProcs()) return;
TrySkillProc(on, skillinuse, ProcChance);
} }
void Mob::TryPetCriticalHit(Mob *defender, uint16 skill, int32 &damage) void Mob::TryPetCriticalHit(Mob *defender, uint16 skill, int32 &damage)

View File

@ -7705,16 +7705,11 @@ int16 Bot::CalcBotFocusEffect(BotfocusType bottype, uint16 focus_id, uint16 spel
} }
//proc chance includes proc bonus //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(); int mydex = GetDEX();
ProcBonus = 0; float ProcChance = 0.0f;
ProcChance = 0;
BaseProcChance = 0;
ProcBonus = float(aabonuses.ProcChanceSPA + spellbonuses.ProcChanceSPA + itembonuses.ProcChanceSPA); //Spell Effects switch (hand) {
ProcBonus += float(itembonuses.ProcChance)/10.0f; //Combat Effects
switch(hand){
case SLOT_PRIMARY: case SLOT_PRIMARY:
weapon_speed = attack_timer.GetDuration(); weapon_speed = attack_timer.GetDuration();
break; break;
@ -7726,23 +7721,19 @@ float Bot::GetProcChances(float &BaseProcChance, float &ProcBonus, float &ProcCh
break; break;
} }
//calculate the weapon speed in ms, so we can use the rule to compare against. //calculate the weapon speed in ms, so we can use the rule to compare against.
// fast as a client can swing, so should be the floor of the proc chance
if(weapon_speed < RuleI(Combat, MinHastedDelay)) // 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); weapon_speed = RuleI(Combat, MinHastedDelay);
if(RuleB(Combat, AdjustProcPerMinute) == true) if (RuleB(Combat, AdjustProcPerMinute)) {
{ ProcChance = (static_cast<float>(weapon_speed) *
ProcChance = ((float)weapon_speed * RuleR(Combat, AvgProcsPerMinute) / 60000.0f); // compensate for weapon_speed being in ms RuleR(Combat, AvgProcsPerMinute) / 60000.0f); // compensate for weapon_speed being in ms
ProcChance = BaseProcChance; ProcBonus += static_cast<float>(mydex) * RuleR(Combat, ProcPerMinDexContrib);
ProcBonus += float(mydex) * RuleR(Combat, ProcPerMinDexContrib); ProcChance += ProcChance * ProcBonus / 100.0f;
ProcChance += ProcChance*ProcBonus / 100.0f; } else {
} ProcChance = RuleR(Combat, BaseProcChance) +
else static_cast<float>(mydex) / RuleR(Combat, ProcDexDivideBy);
{
ProcChance = RuleR(Combat, BaseProcChance) + float(mydex) / RuleR(Combat, ProcDexDivideBy);
ProcChance = BaseProcChance;
ProcChance += ProcChance*ProcBonus / 100.0f; ProcChance += ProcChance*ProcBonus / 100.0f;
} }

View File

@ -167,7 +167,7 @@ public:
uint16 BotGetSpells(int spellslot) { return AIspells[spellslot].spellid; } uint16 BotGetSpells(int spellslot) { return AIspells[spellslot].spellid; }
uint16 BotGetSpellType(int spellslot) { return AIspells[spellslot].type; } uint16 BotGetSpellType(int spellslot) { return AIspells[spellslot].type; }
uint16 BotGetSpellPriority(int spellslot) { return AIspells[spellslot].priority; } 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 bool AvoidDamage(Mob* other, int32 &damage, bool CanRiposte);
virtual int GetMonkHandToHandDamage(void); virtual int GetMonkHandToHandDamage(void);
virtual bool TryFinishingBlow(Mob *defender, SkillUseTypes skillinuse); virtual bool TryFinishingBlow(Mob *defender, SkillUseTypes skillinuse);

View File

@ -969,9 +969,10 @@ protected:
bool PassLimitClass(uint32 Classes_, uint16 Class_); bool PassLimitClass(uint32 Classes_, uint16 Class_);
void TryDefensiveProc(const ItemInst* weapon, Mob *on, uint16 hand = 13, int damage=0); 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 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 TryWeaponProc(const ItemInst* weapon, Mob *on, uint16 hand = 13);
void ExecWeaponProc(const ItemInst* weapon, uint16 spell_id, Mob *on); 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); 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 Item_Struct *weapon_item);
int GetWeaponDamage(Mob *against, const ItemInst *weapon_item, uint32 *hate = nullptr); int GetWeaponDamage(Mob *against, const ItemInst *weapon_item, uint32 *hate = nullptr);