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);
}
//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<float>(weapon_speed) *
RuleR(Combat, AvgProcsPerMinute) / 60000.0f); // compensate for weapon_speed being in ms
ProcBonus += static_cast<float>(mydex) * RuleR(Combat, ProcPerMinDexContrib);
ProcChance += ProcChance * ProcBonus / 100.0f;
} else {
ProcChance = RuleR(Combat, BaseProcChance) +
static_cast<float>(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<float>(aabonuses.ProcChanceSPA +
spellbonuses.ProcChanceSPA + itembonuses.ProcChanceSPA);
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;
// 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
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<float>(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<float>(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)

View File

@ -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<float>(weapon_speed) *
RuleR(Combat, AvgProcsPerMinute) / 60000.0f); // compensate for weapon_speed being in ms
ProcBonus += static_cast<float>(mydex) * RuleR(Combat, ProcPerMinDexContrib);
ProcChance += ProcChance * ProcBonus / 100.0f;
} else {
ProcChance = RuleR(Combat, BaseProcChance) +
static_cast<float>(mydex) / RuleR(Combat, ProcDexDivideBy);
ProcChance += ProcChance*ProcBonus / 100.0f;
}

View File

@ -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);

View File

@ -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);