Implemented support for allowing most focus effects to now be usable

by NPCs (ie Heal/Damage focus, cast time, spell range ect) from
both spell buffs and items.
Rule for enabling spell focus is TRUE by default
Rule for enabling item focus is FALSE by default.

Consilidated a number of redundant client / mob functions
to use the same pathway for calculating effect values.
This commit is contained in:
KayenEQ 2014-12-08 01:22:01 -05:00
parent f81f62670e
commit 70f570dbd9
15 changed files with 250 additions and 361 deletions

View File

@ -325,6 +325,8 @@ RULE_INT ( Spells, AI_IdleBeneficialChance, 100) // Chance while idle to do a be
RULE_BOOL ( Spells, SHDProcIDOffByOne, true) // pre June 2009 SHD spell procs were off by 1, they stopped doing this in June 2009 (so UF+ spell files need this false)
RULE_BOOL ( Spells, Jun182014HundredHandsRevamp, false) // this should be true for if you import a spell file newer than June 18, 2014
RULE_BOOL ( Spells, SwarmPetTargetLock, false) // Use old method of swarm pets target locking till target dies then despawning.
RULE_BOOL ( Spells, NPC_UseFocusFromSpells, true) // Allow npcs to use most spell derived focus effects.
RULE_BOOL ( Spells, NPC_UseFocusFromItems, false) // Allow npcs to use most item derived focus effects.
RULE_CATEGORY_END()
RULE_CATEGORY( Combat )

View File

@ -519,8 +519,7 @@ void Mob::TemporaryPets(uint16 spell_id, Mob *targ, const char *name_override, u
}
}
if(IsClient())
pet.duration += (CastToClient()->GetFocusEffect(focusSwarmPetDuration, spell_id) / 1000);
pet.duration += GetFocusEffect(focusSwarmPetDuration, spell_id) / 1000;
pet.npc_id = record.npc_type;

View File

@ -1128,8 +1128,7 @@ int32 Mob::CheckAggroAmount(uint16 spell_id, bool isproc)
int HateMod = RuleI(Aggro, SpellAggroMod);
if (IsClient())
HateMod += CastToClient()->GetFocusEffect(focusSpellHateMod, spell_id);
HateMod += GetFocusEffect(focusSpellHateMod, spell_id);
AggroAmount = (AggroAmount * HateMod) / 100;
@ -1178,8 +1177,7 @@ int32 Mob::CheckHealAggroAmount(uint16 spell_id, uint32 heal_possible)
if (AggroAmount > 0) {
int HateMod = RuleI(Aggro, SpellAggroMod);
if (IsClient())
HateMod += CastToClient()->GetFocusEffect(focusSpellHateMod, spell_id);
HateMod += GetFocusEffect(focusSpellHateMod, spell_id);
//Live AA - Spell casting subtlety
HateMod += aabonuses.hatemod + spellbonuses.hatemod + itembonuses.hatemod;

View File

@ -1386,15 +1386,6 @@ void Client::Damage(Mob* other, int32 damage, uint16 spell_id, SkillUseTypes att
if(spell_id==0)
spell_id = SPELL_UNKNOWN;
if(spell_id!=0 && spell_id != SPELL_UNKNOWN && other && damage > 0)
{
if(other->IsNPC() && !other->IsPet())
{
float npcspellscale = other->CastToNPC()->GetSpellScale();
damage = ((float)damage * npcspellscale) / (float)100;
}
}
// cut all PVP spell damage to 2/3 -solar
// Blasting ourselfs is considered PvP
//Don't do PvP mitigation if the caster is damaging himself
@ -3806,13 +3797,6 @@ void Mob::HealDamage(uint32 amount, Mob *caster, uint16 spell_id)
int32 curhp = GetHP();
uint32 acthealed = 0;
if (caster && amount > 0) {
if (caster->IsNPC() && !caster->IsPet()) {
float npchealscale = caster->CastToNPC()->GetHealScale();
amount = (static_cast<float>(amount) * npchealscale) / 100.0f;
}
}
if (amount > (maxhp - curhp))
acthealed = (maxhp - curhp);
else

View File

@ -3050,6 +3050,13 @@ void NPC::CalcItemBonuses(StatBonuses *newbon)
if (cur->Worn.Effect>0 && (cur->Worn.Type == ET_WornEffect)) { // latent effects
ApplySpellsBonuses(cur->Worn.Effect, cur->Worn.Level, newbon);
}
if (RuleB(Spells, NPC_UseFocusFromItems)){
if (cur->Focus.Effect>0 && (cur->Focus.Type == ET_Focus)){ // focus effects
ApplySpellsBonuses(cur->Focus.Effect, cur->Focus.Level, newbon, 0, true);
}
}
if (cur->Haste > newbon->haste)
newbon->haste = cur->Haste;
}

View File

@ -485,14 +485,8 @@ public:
inline virtual int32 GetDelayDeath() const { return aabonuses.DelayDeath + spellbonuses.DelayDeath + itembonuses.DelayDeath + 11; }
float GetActSpellRange(uint16 spell_id, float range, bool IsBard = false);
int32 GetActSpellDamage(uint16 spell_id, int32 value, Mob* target = nullptr);
int32 GetActSpellHealing(uint16 spell_id, int32 value, Mob* target = nullptr);
int32 GetActSpellCost(uint16 spell_id, int32);
int32 GetActSpellDuration(uint16 spell_id, int32);
int32 GetActSpellCasttime(uint16 spell_id, int32);
int32 GetDotFocus(uint16 spell_id, int32 value);
int32 GetActDoTDamage(uint16 spell_id, int32 value, Mob* target = nullptr);
virtual bool CheckFizzle(uint16 spell_id);
virtual bool CheckSpellLevelRestriction(uint16 spell_id);
virtual int GetCurrentBuffSlots() const;

View File

@ -30,7 +30,7 @@
#include "string_ids.h"
#include "npc_ai.h"
float Client::GetActSpellRange(uint16 spell_id, float range, bool IsBard)
float Mob::GetActSpellRange(uint16 spell_id, float range, bool IsBard)
{
float extrange = 100;
@ -39,94 +39,58 @@ float Client::GetActSpellRange(uint16 spell_id, float range, bool IsBard)
return (range * extrange) / 100;
}
int32 NPC::GetActSpellDamage(uint16 spell_id, int32 value, Mob* target) {
//Quest scale all NPC spell damage via $npc->SetSpellFocusDMG(value)
//DoT Damage - Mob::DoBuffTic [spell_effects.cpp] / Direct Damage Mob::SpellEffect [spell_effects.cpp]
int32 dmg = value;
if (target) {
value += dmg*target->GetVulnerability(this, spell_id, 0)/100;
if (spells[spell_id].buffduration == 0)
value -= target->GetFcDamageAmtIncoming(this, spell_id);
else
value -= target->GetFcDamageAmtIncoming(this, spell_id)/spells[spell_id].buffduration;
}
value += dmg*GetSpellFocusDMG()/100;
if (AI_HasSpellsEffects()){
int16 chance = 0;
int ratio = 0;
if (spells[spell_id].buffduration == 0) {
chance += spellbonuses.CriticalSpellChance + spellbonuses.FrenziedDevastation;
if (chance && zone->random.Roll(chance)) {
ratio += spellbonuses.SpellCritDmgIncrease + spellbonuses.SpellCritDmgIncNoStack;
value += (value*ratio)/100;
entity_list.MessageClose_StringID(this, true, 100, MT_SpellCrits, OTHER_CRIT_BLAST, GetCleanName(), itoa(-value));
}
}
else {
chance += spellbonuses.CriticalDoTChance;
if (chance && zone->random.Roll(chance)) {
ratio += spellbonuses.DotCritDmgIncrease;
value += (value*ratio)/100;
}
}
}
return value;
}
int32 Client::GetActSpellDamage(uint16 spell_id, int32 value, Mob* target) {
int32 Mob::GetActSpellDamage(uint16 spell_id, int32 value, Mob* target) {
if (spells[spell_id].targettype == ST_Self)
return value;
if (IsNPC())
value += value*CastToNPC()->GetSpellFocusDMG()/100;
bool Critical = false;
int32 value_BaseEffect = 0;
int chance = 0;
value_BaseEffect = value + (value*GetFocusEffect(focusFcBaseEffects, spell_id)/100);
// Need to scale HT damage differently after level 40! It no longer scales by the constant value in the spell file. It scales differently, instead of 10 more damage per level, it does 30 more damage per level. So we multiply the level minus 40 times 20 if they are over level 40.
if ( (spell_id == SPELL_HARM_TOUCH || spell_id == SPELL_HARM_TOUCH2 || spell_id == SPELL_IMP_HARM_TOUCH ) && GetLevel() > 40)
if ((spell_id == SPELL_HARM_TOUCH || spell_id == SPELL_HARM_TOUCH2 || spell_id == SPELL_IMP_HARM_TOUCH ) && GetLevel() > 40)
value -= (GetLevel() - 40) * 20;
//This adds the extra damage from the AA Unholy Touch, 450 per level to the AA Improved Harm TOuch.
if (spell_id == SPELL_IMP_HARM_TOUCH) //Improved Harm Touch
if (spell_id == SPELL_IMP_HARM_TOUCH && IsClient()) //Improved Harm Touch
value -= GetAA(aaUnholyTouch) * 450; //Unholy Touch
int chance = RuleI(Spells, BaseCritChance); //Wizard base critical chance is 2% (Does not scale with level)
chance = RuleI(Spells, BaseCritChance); //Wizard base critical chance is 2% (Does not scale with level)
chance += itembonuses.CriticalSpellChance + spellbonuses.CriticalSpellChance + aabonuses.CriticalSpellChance;
chance += itembonuses.FrenziedDevastation + spellbonuses.FrenziedDevastation + aabonuses.FrenziedDevastation;
if (chance > 0 || (GetClass() == WIZARD && GetLevel() >= RuleI(Spells, WizCritLevel))) {
//Crtical Hit Calculation pathway
if (chance > 0 || (IsClient() && GetClass() == WIZARD && GetLevel() >= RuleI(Spells, WizCritLevel))) {
int32 ratio = RuleI(Spells, BaseCritRatio); //Critical modifier is applied from spell effects only. Keep at 100 for live like criticals.
//Improved Harm Touch is a guaranteed crit if you have at least one level of SCF.
if (spell_id == SPELL_IMP_HARM_TOUCH && (GetAA(aaSpellCastingFury) > 0) && (GetAA(aaUnholyTouch) > 0))
if (spell_id == SPELL_IMP_HARM_TOUCH && IsClient() && (GetAA(aaSpellCastingFury) > 0) && (GetAA(aaUnholyTouch) > 0))
chance = 100;
if (zone->random.Roll(chance)) {
if (zone->random.Roll(chance)) {
Critical = true;
ratio += itembonuses.SpellCritDmgIncrease + spellbonuses.SpellCritDmgIncrease + aabonuses.SpellCritDmgIncrease;
ratio += itembonuses.SpellCritDmgIncNoStack + spellbonuses.SpellCritDmgIncNoStack + aabonuses.SpellCritDmgIncNoStack;
}
else if (GetClass() == WIZARD && (GetLevel() >= RuleI(Spells, WizCritLevel)) && (zone->random.Roll(RuleI(Spells, WizCritChance)))) {
ratio += zone->random.Int(20,70); //Wizard innate critical chance is calculated seperately from spell effect and is not a set ratio. (20-70 is parse confirmed)
Critical = true;
else if ((IsClient() && GetClass() == WIZARD) || (IsMerc() && GetClass() == CASTERDPS)) {
if ((GetLevel() >= RuleI(Spells, WizCritLevel)) && zone->random.Roll(RuleI(Spells, WizCritChance))){
//Wizard innate critical chance is calculated seperately from spell effect and is not a set ratio. (20-70 is parse confirmed)
ratio += zone->random.Int(20,70);
Critical = true;
}
}
ratio += RuleI(Spells, WizCritRatio); //Default is zero
if (IsClient() && GetClass() == WIZARD)
ratio += RuleI(Spells, WizCritRatio); //Default is zero
if (Critical){
value = value_BaseEffect*ratio/100;
@ -147,14 +111,19 @@ int32 Client::GetActSpellDamage(uint16 spell_id, int32 value, Mob* target) {
if(itembonuses.SpellDmg && spells[spell_id].classes[(GetClass()%16) - 1] >= GetLevel() - 5)
value -= GetExtraSpellAmt(spell_id, itembonuses.SpellDmg, value)*ratio/100;
else if (IsNPC() && CastToNPC()->GetSpellScale())
value = int(static_cast<float>(value) * CastToNPC()->GetSpellScale() / 100.0f);
entity_list.MessageClose_StringID(this, true, 100, MT_SpellCrits,
OTHER_CRIT_BLAST, GetName(), itoa(-value));
Message_StringID(MT_SpellCrits, YOU_CRIT_BLAST, itoa(-value));
if (IsClient())
Message_StringID(MT_SpellCrits, YOU_CRIT_BLAST, itoa(-value));
return value;
}
}
//Non Crtical Hit Calculation pathway
value = value_BaseEffect;
value += value_BaseEffect*GetFocusEffect(focusImprovedDamage, spell_id)/100;
@ -173,14 +142,20 @@ int32 Client::GetActSpellDamage(uint16 spell_id, int32 value, Mob* target) {
if(itembonuses.SpellDmg && spells[spell_id].classes[(GetClass()%16) - 1] >= GetLevel() - 5)
value -= GetExtraSpellAmt(spell_id, itembonuses.SpellDmg, value);
if (IsNPC() && CastToNPC()->GetSpellScale())
value = int(static_cast<float>(value) * CastToNPC()->GetSpellScale() / 100.0f);
return value;
}
int32 Client::GetActDoTDamage(uint16 spell_id, int32 value, Mob* target) {
int32 Mob::GetActDoTDamage(uint16 spell_id, int32 value, Mob* target) {
if (target == nullptr)
return value;
if (IsNPC())
value += value*CastToNPC()->GetSpellFocusDMG()/100;
int32 value_BaseEffect = 0;
int32 extra_dmg = 0;
int16 chance = 0;
@ -209,25 +184,28 @@ int32 Client::GetActDoTDamage(uint16 spell_id, int32 value, Mob* target) {
}
value -= extra_dmg;
}
else {
return value;
value = value_BaseEffect;
value += value_BaseEffect*GetFocusEffect(focusImprovedDamage, spell_id)/100;
value += value_BaseEffect*GetFocusEffect(focusFcDamagePctCrit, spell_id)/100;
value += value_BaseEffect*target->GetVulnerability(this, spell_id, 0)/100;
extra_dmg = target->GetFcDamageAmtIncoming(this, spell_id) +
GetFocusEffect(focusFcDamageAmtCrit, spell_id) +
GetFocusEffect(focusFcDamageAmt, spell_id);
if (extra_dmg) {
int duration = CalcBuffDuration(this, this, spell_id);
if (duration > 0)
extra_dmg /= duration;
}
value -= extra_dmg;
}
value = value_BaseEffect;
value += value_BaseEffect*GetFocusEffect(focusImprovedDamage, spell_id)/100;
value += value_BaseEffect*GetFocusEffect(focusFcDamagePctCrit, spell_id)/100;
value += value_BaseEffect*target->GetVulnerability(this, spell_id, 0)/100;
extra_dmg = target->GetFcDamageAmtIncoming(this, spell_id) +
GetFocusEffect(focusFcDamageAmtCrit, spell_id) +
GetFocusEffect(focusFcDamageAmt, spell_id);
if (extra_dmg) {
int duration = CalcBuffDuration(this, this, spell_id);
if (duration > 0)
extra_dmg /= duration;
}
value -= extra_dmg;
if (IsNPC() && CastToNPC()->GetSpellScale())
value = int(static_cast<float>(value) * CastToNPC()->GetSpellScale() / 100.0f);
return value;
}
@ -254,39 +232,14 @@ int32 Mob::GetExtraSpellAmt(uint16 spell_id, int32 extra_spell_amt, int32 base_s
return extra_spell_amt;
}
int32 NPC::GetActSpellHealing(uint16 spell_id, int32 value, Mob* target) {
//Scale all NPC spell healing via SetSpellFocusHeal(value)
value += value*GetSpellFocusHeal()/100;
if (target) {
value += target->GetFocusIncoming(focusFcHealAmtIncoming, SE_FcHealAmtIncoming, this, spell_id);
value += value*target->GetHealRate(spell_id, this)/100;
}
//Allow for critical heal chance if NPC is loading spell effect bonuses.
if (AI_HasSpellsEffects()){
if(spells[spell_id].buffduration < 1) {
if(spellbonuses.CriticalHealChance && (zone->random.Roll(spellbonuses.CriticalHealChance))) {
value = value*2;
entity_list.MessageClose_StringID(this, true, 100, MT_SpellCrits, OTHER_CRIT_HEAL, GetCleanName(), itoa(value));
}
}
else if(spellbonuses.CriticalHealOverTime && (zone->random.Roll(spellbonuses.CriticalHealOverTime))) {
value = value*2;
}
}
return value;
}
int32 Client::GetActSpellHealing(uint16 spell_id, int32 value, Mob* target) {
int32 Mob::GetActSpellHealing(uint16 spell_id, int32 value, Mob* target) {
if (target == nullptr)
target = this;
if (IsNPC())
value += value*CastToNPC()->GetSpellFocusHeal()/100;
int32 value_BaseEffect = 0;
int16 chance = 0;
int8 modifier = 1;
@ -323,10 +276,15 @@ int32 Client::GetActSpellHealing(uint16 spell_id, int32 value, Mob* target) {
value += value*target->GetHealRate(spell_id, this)/100;
if (IsNPC() && CastToNPC()->GetHealScale())
value = int(static_cast<float>(value) * CastToNPC()->GetHealScale() / 100.0f);
if (Critical) {
entity_list.MessageClose_StringID(this, true, 100, MT_SpellCrits,
OTHER_CRIT_HEAL, GetName(), itoa(value));
Message_StringID(MT_SpellCrits, YOU_CRIT_HEAL, itoa(value));
if (IsClient())
Message_StringID(MT_SpellCrits, YOU_CRIT_HEAL, itoa(value));
}
return value;
@ -343,9 +301,12 @@ int32 Client::GetActSpellHealing(uint16 spell_id, int32 value, Mob* target) {
chance += GetDecayEffectValue(spell_id, SE_CriticalRegenDecay);
if(chance && zone->random.Roll(chance))
return (value * 2);
value *= 2;
}
if (IsNPC() && CastToNPC()->GetHealScale())
value = int(static_cast<float>(value) * CastToNPC()->GetHealScale() / 100.0f);
return value;
}
@ -359,9 +320,9 @@ int32 Client::GetActSpellCost(uint16 spell_id, int32 cost)
cost *= 2;
// Formula = Unknown exact, based off a random percent chance up to mana cost(after focuses) of the cast spell
if(this->itembonuses.Clairvoyance && spells[spell_id].classes[(GetClass()%16) - 1] >= GetLevel() - 5)
if(itembonuses.Clairvoyance && spells[spell_id].classes[(GetClass()%16) - 1] >= GetLevel() - 5)
{
int16 mana_back = this->itembonuses.Clairvoyance * zone->random.Int(1, 100) / 100;
int16 mana_back = itembonuses.Clairvoyance * zone->random.Int(1, 100) / 100;
// Doesnt generate mana, so best case is a free spell
if(mana_back > cost)
mana_back = cost;
@ -451,7 +412,7 @@ int32 Client::GetActSpellCost(uint16 spell_id, int32 cost)
return cost;
}
int32 Client::GetActSpellDuration(uint16 spell_id, int32 duration)
int32 Mob::GetActSpellDuration(uint16 spell_id, int32 duration)
{
if (spells[spell_id].not_extendable)
return duration;
@ -463,7 +424,7 @@ int32 Client::GetActSpellDuration(uint16 spell_id, int32 duration)
// Only need this for clients, since the change was for bard songs, I assume we should keep non bard songs getting +1
// However if its bard or not and is mez, charm or fear, we need to add 1 so that client is in sync
if (!(IsShortDurationBuff(spell_id) && IsBardSong(spell_id)) ||
if (IsClient() && !(IsShortDurationBuff(spell_id) && IsBardSong(spell_id)) ||
IsFearSpell(spell_id) ||
IsCharmSpell(spell_id) ||
IsMezSpell(spell_id) ||
@ -664,7 +625,7 @@ bool Client::UseDiscipline(uint32 spell_id, uint32 target) {
if(spell.recast_time > 0)
{
uint32 reduced_recast = spell.recast_time / 1000;
reduced_recast -= CastToClient()->GetFocusEffect(focusReduceRecastTime, spell_id);
reduced_recast -= GetFocusEffect(focusReduceRecastTime, spell_id);
if(reduced_recast <= 0){
reduced_recast = 0;
if (GetPTimers().Enabled((uint32)DiscTimer))

View File

@ -2656,153 +2656,6 @@ int16 Merc::GetFocusEffect(focusType type, uint16 spell_id) {
return realTotal + realTotal2 + realTotal3;
}
int32 Merc::GetActSpellDamage(uint16 spell_id, int32 value, Mob* target) {
if (spells[spell_id].targettype == ST_Self)
return value;
bool Critical = false;
int32 value_BaseEffect = 0;
value_BaseEffect = value + (value*GetFocusEffect(focusFcBaseEffects, spell_id)/100);
int chance = RuleI(Spells, BaseCritChance);
chance += itembonuses.CriticalSpellChance + spellbonuses.CriticalSpellChance + aabonuses.CriticalSpellChance;
if (chance > 0){
int32 ratio = RuleI(Spells, BaseCritRatio); //Critical modifier is applied from spell effects only. Keep at 100 for live like criticals.
if (zone->random.Roll(chance)) {
Critical = true;
ratio += itembonuses.SpellCritDmgIncrease + spellbonuses.SpellCritDmgIncrease + aabonuses.SpellCritDmgIncrease;
ratio += itembonuses.SpellCritDmgIncNoStack + spellbonuses.SpellCritDmgIncNoStack + aabonuses.SpellCritDmgIncNoStack;
}
else if (GetClass() == CASTERDPS && (GetLevel() >= RuleI(Spells, WizCritLevel)) && (zone->random.Roll(RuleI(Spells, WizCritChance)))) {
ratio = zone->random.Int(1,100); //Wizard innate critical chance is calculated seperately from spell effect and is not a set ratio.
Critical = true;
}
ratio += RuleI(Spells, WizCritRatio); //Default is zero
if (Critical){
value = value_BaseEffect*ratio/100;
value += value_BaseEffect*GetFocusEffect(focusImprovedDamage, spell_id)/100;
value += int(value_BaseEffect*GetFocusEffect(focusFcDamagePctCrit, spell_id)/100)*ratio/100;
if (target) {
value += int(value_BaseEffect*target->GetVulnerability(this, spell_id, 0)/100)*ratio/100;
value -= target->GetFcDamageAmtIncoming(this, spell_id);
}
value -= GetFocusEffect(focusFcDamageAmtCrit, spell_id)*ratio/100;
value -= GetFocusEffect(focusFcDamageAmt, spell_id);
if(itembonuses.SpellDmg && spells[spell_id].classes[(GetClass()%16) - 1] >= GetLevel() - 5)
value -= GetExtraSpellAmt(spell_id, itembonuses.SpellDmg, value)*ratio/100;
value = (value * GetSpellScale() / 100);
entity_list.MessageClose_StringID(this, false, 100, MT_SpellCrits,
OTHER_CRIT_BLAST, GetName(), itoa(-value));
return value;
}
}
value = value_BaseEffect;
value += value_BaseEffect*GetFocusEffect(focusImprovedDamage, spell_id)/100;
value += value_BaseEffect*GetFocusEffect(focusFcDamagePctCrit, spell_id)/100;
if (target) {
value += value_BaseEffect*target->GetVulnerability(this, spell_id, 0)/100;
value -= target->GetFcDamageAmtIncoming(this, spell_id);
}
value -= GetFocusEffect(focusFcDamageAmtCrit, spell_id);
value -= GetFocusEffect(focusFcDamageAmt, spell_id);
if(itembonuses.SpellDmg && spells[spell_id].classes[(GetClass()%16) - 1] >= GetLevel() - 5)
value -= GetExtraSpellAmt(spell_id, itembonuses.SpellDmg, value);
value = (value * GetSpellScale() / 100);
return value;
}
int32 Merc::GetActSpellHealing(uint16 spell_id, int32 value, Mob* target) {
if (target == nullptr)
target = this;
int32 value_BaseEffect = 0;
int16 chance = 0;
int8 modifier = 1;
bool Critical = false;
value_BaseEffect = value + (value*GetFocusEffect(focusFcBaseEffects, spell_id)/100);
value = value_BaseEffect;
value += int(value_BaseEffect*GetFocusEffect(focusImprovedHeal, spell_id)/100);
// Instant Heals
if(spells[spell_id].buffduration < 1) {
chance += itembonuses.CriticalHealChance + spellbonuses.CriticalHealChance + aabonuses.CriticalHealChance;
chance += target->GetFocusIncoming(focusFcHealPctCritIncoming, SE_FcHealPctCritIncoming, this, spell_id);
if (spellbonuses.CriticalHealDecay)
chance += GetDecayEffectValue(spell_id, SE_CriticalHealDecay);
if(chance && zone->random.Roll(chance)) {
Critical = true;
modifier = 2; //At present time no critical heal amount modifier SPA exists.
}
value *= modifier;
value += GetFocusEffect(focusFcHealAmtCrit, spell_id) * modifier;
value += GetFocusEffect(focusFcHealAmt, spell_id);
value += target->GetFocusIncoming(focusFcHealAmtIncoming, SE_FcHealAmtIncoming, this, spell_id);
if(itembonuses.HealAmt && spells[spell_id].classes[(GetClass()%16) - 1] >= GetLevel() - 5)
value += GetExtraSpellAmt(spell_id, itembonuses.HealAmt, value) * modifier;
value += value*target->GetHealRate(spell_id, this)/100;
if (Critical)
entity_list.MessageClose(this, false, 100, MT_SpellCrits, "%s performs an exceptional heal! (%d)", GetName(), value);
return value;
}
//Heal over time spells. [Heal Rate and Additional Healing effects do not increase this value]
else {
chance = itembonuses.CriticalHealOverTime + spellbonuses.CriticalHealOverTime + aabonuses.CriticalHealOverTime;
chance += target->GetFocusIncoming(focusFcHealPctCritIncoming, SE_FcHealPctCritIncoming, this, spell_id);
if (spellbonuses.CriticalRegenDecay)
chance += GetDecayEffectValue(spell_id, SE_CriticalRegenDecay);
if(chance && zone->random.Roll(chance))
return (value * 2);
}
return value;
}
int32 Merc::GetActSpellCost(uint16 spell_id, int32 cost)
{
// Formula = Unknown exact, based off a random percent chance up to mana cost(after focuses) of the cast spell

View File

@ -83,8 +83,6 @@ public:
Corpse* GetGroupMemberCorpse();
// Merc Spell Casting Methods
virtual int32 GetActSpellDamage(uint16 spell_id, int32 value, Mob* target = nullptr);
virtual int32 GetActSpellHealing(uint16 spell_id, int32 value, Mob* target = nullptr);
virtual int32 GetActSpellCasttime(uint16 spell_id, int32 casttime);
virtual int32 GetActSpellCost(uint16 spell_id, int32 cost);
int8 GetChanceToCastBySpellType(int16 spellType);

View File

@ -2926,6 +2926,10 @@ uint32 Mob::GetLevelHP(uint8 tlevel)
}
int32 Mob::GetActSpellCasttime(uint16 spell_id, int32 casttime) {
int32 cast_reducer = 0;
cast_reducer += GetFocusEffect(focusSpellHaste, spell_id);
if (level >= 60 && casttime > 1000)
{
casttime = casttime / 2;
@ -2938,6 +2942,8 @@ int32 Mob::GetActSpellCasttime(uint16 spell_id, int32 casttime) {
else
casttime -= cast_deduction;
}
casttime = (casttime*(100 - cast_reducer)/100);
return(casttime);
}
@ -2973,8 +2979,7 @@ void Mob::ExecWeaponProc(const ItemInst *inst, uint16 spell_id, Mob *on) {
bool twinproc = false;
int32 twinproc_chance = 0;
if(IsClient())
twinproc_chance = CastToClient()->GetFocusEffect(focusTwincast, spell_id);
twinproc_chance = GetFocusEffect(focusTwincast, spell_id);
if(twinproc_chance && zone->random.Roll(twinproc_chance))
twinproc = true;

View File

@ -198,11 +198,12 @@ public:
bool item_bonus = false, uint32 ticsremaining = 0, int buffslot = -1,
bool IsAISpellEffect = false, uint16 effect_id = 0, int32 se_base = 0, int32 se_limit = 0, int32 se_max = 0);
void NegateSpellsBonuses(uint16 spell_id);
virtual float GetActSpellRange(uint16 spell_id, float range, bool IsBard = false) { return range;}
virtual int32 GetActSpellDamage(uint16 spell_id, int32 value, Mob* target = nullptr) { return value; }
virtual int32 GetActSpellHealing(uint16 spell_id, int32 value, Mob* target = nullptr) { return value; }
virtual float GetActSpellRange(uint16 spell_id, float range, bool IsBard = false);
virtual int32 GetActSpellDamage(uint16 spell_id, int32 value, Mob* target = nullptr);
virtual int32 GetActDoTDamage(uint16 spell_id, int32 value, Mob* target);
virtual int32 GetActSpellHealing(uint16 spell_id, int32 value, Mob* target = nullptr);
virtual int32 GetActSpellCost(uint16 spell_id, int32 cost){ return cost;}
virtual int32 GetActSpellDuration(uint16 spell_id, int32 duration){ return duration;}
virtual int32 GetActSpellDuration(uint16 spell_id, int32 duration);
virtual int32 GetActSpellCasttime(uint16 spell_id, int32 casttime);
float ResistSpell(uint8 resist_type, uint16 spell_id, Mob *caster, bool use_resist_override = false,
int resist_override = 0, bool CharismaCheck = false, bool CharmTick = false, bool IsRoot = false);
@ -1044,6 +1045,7 @@ protected:
int GetKickDamage();
int GetBashDamage();
virtual void ApplySpecialAttackMod(SkillUseTypes skill, int32 &dmg, int32 &mindmg);
virtual int16 GetFocusEffect(focusType type, uint16 spell_id) { return 0; }
void CalculateNewFearpoint();
float FindGroundZ(float new_x, float new_y, float z_offset=0.0);
Map::Vertex UpdatePath(float ToX, float ToY, float ToZ, float Speed, bool &WaypointChange, bool &NodeReached);

View File

@ -135,9 +135,6 @@ public:
void CalcNPCRegen();
void CalcNPCDamage();
int32 GetActSpellDamage(uint16 spell_id, int32 value, Mob* target = nullptr);
int32 GetActSpellHealing(uint16 spell_id, int32 value, Mob* target = nullptr);
virtual void SetTarget(Mob* mob);
virtual uint16 GetSkill(SkillUseTypes skill_num) const { if (skill_num <= HIGHEST_SKILL) { return skills[skill_num]; } return 0; }
@ -406,6 +403,8 @@ public:
bool IsRaidTarget() const { return raid_target; };
int16 GetFocusEffect(focusType type, uint16 spell_id);
protected:
const NPCType* NPCTypedata;
@ -446,6 +445,7 @@ protected:
virtual bool AIDoSpellCast(uint8 i, Mob* tar, int32 mana_cost, uint32* oDontDoAgainBefore = 0);
AISpellsVar_Struct AISpellVar;
uint32 npc_spells_effects_id;
std::vector<AISpellsEffects_Struct> AIspellsEffects;
bool HasAISpellEffects;

View File

@ -230,7 +230,7 @@ void Mob::MakePoweredPet(uint16 spell_id, const char* pettype, int16 petpower,
int16 act_power = 0; // The actual pet power we'll use.
if (petpower == -1) {
if (this->IsClient()) {
act_power = CastToClient()->GetFocusEffect(focusPetPower, spell_id);
act_power = CastToClient()->GetFocusEffect(focusPetPower, spell_id);//Client only
act_power = CastToClient()->mod_pet_power(act_power, spell_id);
}
}

View File

@ -179,11 +179,8 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial)
int numhit = spells[spell_id].numhits;
if (caster && caster->IsClient()){
numhit += numhit*caster->CastToClient()->GetFocusEffect(focusFcLimitUse, spell_id)/100;
numhit += caster->CastToClient()->GetFocusEffect(focusIncreaseNumHits, spell_id);
}
numhit += numhit*caster->GetFocusEffect(focusFcLimitUse, spell_id)/100;
numhit += caster->GetFocusEffect(focusIncreaseNumHits, spell_id);
buffs[buffslot].numhits = numhit;
}
@ -714,7 +711,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial)
mlog(COMBAT__HITS, "Stunned. We had %d percent resist chance.", stun_resist);
if (caster->IsClient())
effect_value += effect_value*caster->CastToClient()->GetFocusEffect(focusFcStunTimeMod, spell_id)/100;
effect_value += effect_value*caster->GetFocusEffect(focusFcStunTimeMod, spell_id)/100;
Stun(effect_value);
} else {
@ -2265,8 +2262,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial)
int16 focus = 0;
int ReuseTime = spells[spell_id].recast_time + spells[spell_id].recovery_time;
if(caster->IsClient())
focus = caster->CastToClient()->GetFocusEffect(focusFcBaseEffects, spell_id);
focus = caster->GetFocusEffect(focusFcBaseEffects, spell_id);
switch(spells[spell_id].skill)
{
@ -3469,30 +3465,18 @@ void Mob::DoBuffTic(uint16 spell_id, int slot, uint32 ticsremaining, uint8 caste
{
effect_value = CalcSpellEffectValue(spell_id, i, caster_level, caster, ticsremaining);
//Handle client cast DOTs here.
if (caster && caster->IsClient() && IsDetrimentalSpell(spell_id) && effect_value < 0) {
effect_value = caster->CastToClient()->GetActDoTDamage(spell_id, effect_value, this);
if (!caster->CastToClient()->GetFeigned())
AddToHateList(caster, -effect_value);
}
if(effect_value < 0)
{
if(caster)
{
if(!caster->IsClient()){
if (!IsClient()) //Allow NPC's to generate hate if casted on other NPC's.
AddToHateList(caster, -effect_value);
}
if(caster->IsNPC())
effect_value = caster->CastToNPC()->GetActSpellDamage(spell_id, effect_value, this);
caster->ResourceTap(-effect_value, spell_id);
if (caster && effect_value < 0 && IsDetrimentalSpell(spell_id)){
if (caster->IsClient()){
if (!caster->CastToClient()->GetFeigned())
AddToHateList(caster, -effect_value);
}
else if (!IsClient()) //Allow NPC's to generate hate if casted on other NPC's.
AddToHateList(caster, -effect_value);
effect_value = caster->GetActDoTDamage(spell_id, effect_value, this);
caster->ResourceTap(-effect_value, spell_id);
effect_value = -effect_value;
Damage(caster, effect_value, spell_id, spell.skill, false, i, true);
} else if(effect_value > 0) {
@ -5318,11 +5302,8 @@ int16 Client::GetFocusEffect(focusType type, uint16 spell_id) {
//Improved Healing, Damage & Mana Reduction are handled differently in that some are random percentages
//In these cases we need to find the most powerful effect, so that each piece of gear wont get its own chance
if((type == focusManaCost || type == focusImprovedHeal || type == focusImprovedDamage)
&& RuleB(Spells, LiveLikeFocusEffects))
{
if(RuleB(Spells, LiveLikeFocusEffects) && (type == focusManaCost || type == focusImprovedHeal || type == focusImprovedDamage))
rand_effectiveness = true;
}
//Check if item focus effect exists for the client.
if (itembonuses.FocusEffects[type]){
@ -5540,6 +5521,122 @@ int16 Client::GetFocusEffect(focusType type, uint16 spell_id) {
return realTotal + realTotal2 + realTotal3;
}
int16 NPC::GetFocusEffect(focusType type, uint16 spell_id) {
int16 realTotal = 0;
int16 realTotal2 = 0;
bool rand_effectiveness = false;
//Improved Healing, Damage & Mana Reduction are handled differently in that some are random percentages
//In these cases we need to find the most powerful effect, so that each piece of gear wont get its own chance
if(RuleB(Spells, LiveLikeFocusEffects) && (type == focusManaCost || type == focusImprovedHeal || type == focusImprovedDamage))
rand_effectiveness = true;
if (RuleB(Spells, NPC_UseFocusFromItems) && itembonuses.FocusEffects[type]){
const Item_Struct* TempItem = 0;
const Item_Struct* UsedItem = 0;
uint16 UsedFocusID = 0;
int16 Total = 0;
int16 focus_max = 0;
int16 focus_max_real = 0;
//item focus
for(int i = 0; i < EmuConstants::EQUIPMENT_SIZE; i++){
const Item_Struct *cur = database.GetItem(equipment[i]);
if(!cur)
continue;
TempItem = cur;
if (TempItem && TempItem->Focus.Effect > 0 && TempItem->Focus.Effect != SPELL_UNKNOWN) {
if(rand_effectiveness) {
focus_max = CalcFocusEffect(type, TempItem->Focus.Effect, spell_id, true);
if (focus_max > 0 && focus_max_real >= 0 && focus_max > focus_max_real) {
focus_max_real = focus_max;
UsedItem = TempItem;
UsedFocusID = TempItem->Focus.Effect;
} else if (focus_max < 0 && focus_max < focus_max_real) {
focus_max_real = focus_max;
UsedItem = TempItem;
UsedFocusID = TempItem->Focus.Effect;
}
}
else {
Total = CalcFocusEffect(type, TempItem->Focus.Effect, spell_id);
if (Total > 0 && realTotal >= 0 && Total > realTotal) {
realTotal = Total;
UsedItem = TempItem;
UsedFocusID = TempItem->Focus.Effect;
} else if (Total < 0 && Total < realTotal) {
realTotal = Total;
UsedItem = TempItem;
UsedFocusID = TempItem->Focus.Effect;
}
}
}
}
if(UsedItem && rand_effectiveness && focus_max_real != 0)
realTotal = CalcFocusEffect(type, UsedFocusID, spell_id);
}
if (RuleB(Spells, NPC_UseFocusFromSpells) && spellbonuses.FocusEffects[type]){
//Spell Focus
int16 Total2 = 0;
int16 focus_max2 = 0;
int16 focus_max_real2 = 0;
int buff_tracker = -1;
int buff_slot = 0;
uint16 focusspellid = 0;
uint16 focusspell_tracker = 0;
int buff_max = GetMaxTotalSlots();
for (buff_slot = 0; buff_slot < buff_max; buff_slot++) {
focusspellid = buffs[buff_slot].spellid;
if (focusspellid == 0 || focusspellid >= SPDAT_RECORDS)
continue;
if(rand_effectiveness) {
focus_max2 = CalcFocusEffect(type, focusspellid, spell_id, true);
if (focus_max2 > 0 && focus_max_real2 >= 0 && focus_max2 > focus_max_real2) {
focus_max_real2 = focus_max2;
buff_tracker = buff_slot;
focusspell_tracker = focusspellid;
} else if (focus_max2 < 0 && focus_max2 < focus_max_real2) {
focus_max_real2 = focus_max2;
buff_tracker = buff_slot;
focusspell_tracker = focusspellid;
}
}
else {
Total2 = CalcFocusEffect(type, focusspellid, spell_id);
if (Total2 > 0 && realTotal2 >= 0 && Total2 > realTotal2) {
realTotal2 = Total2;
buff_tracker = buff_slot;
focusspell_tracker = focusspellid;
} else if (Total2 < 0 && Total2 < realTotal2) {
realTotal2 = Total2;
buff_tracker = buff_slot;
focusspell_tracker = focusspellid;
}
}
}
if(focusspell_tracker && rand_effectiveness && focus_max_real2 != 0)
realTotal2 = CalcFocusEffect(type, focusspell_tracker, spell_id);
// For effects like gift of mana that only fire once, save the spellid into an array that consists of all available buff slots.
if(buff_tracker >= 0 && buffs[buff_tracker].numhits > 0) {
m_spellHitsLeft[buff_tracker] = focusspell_tracker;
}
}
return realTotal + realTotal2;
}
void Mob::CheckNumHitsRemaining(uint8 type, int32 buff_slot, uint16 spell_id)
{
/*
@ -5946,15 +6043,13 @@ int32 Mob::ApplySpellEffectiveness(Mob* caster, int16 spell_id, int32 value, boo
if (!caster)
return value;
if (caster->IsClient()){
int16 focus = caster->CastToClient()->GetFocusEffect(focusFcBaseEffects, spell_id);
int16 focus = GetFocusEffect(focusFcBaseEffects, spell_id);
if (IsBard)
value += focus;
if (IsBard)
value += focus;
else
value += value*focus/100;
else
value += value*focus/100;
}
return value;
}

View File

@ -176,7 +176,7 @@ bool Mob::CastSpell(uint16 spell_id, uint16 target_id, uint16 slot,
BuffFadeByEffect(SE_Sanctuary);
if(IsClient()){
int chance = CastToClient()->GetFocusEffect(focusFcMute, spell_id);
int chance = CastToClient()->GetFocusEffect(focusFcMute, spell_id);//Client only
if (zone->random.Roll(chance)) {
Message_StringID(13, SILENCED_STRING);
@ -1043,7 +1043,7 @@ void Mob::CastedSpellFinished(uint16 spell_id, uint32 target_id, uint16 slot,
// Check for consumables and Reagent focus items
// first check for component reduction
if(IsClient()) {
int reg_focus = CastToClient()->GetFocusEffect(focusReagentCost,spell_id);
int reg_focus = CastToClient()->GetFocusEffect(focusReagentCost,spell_id);//Client only
if(zone->random.Roll(reg_focus)) {
mlog(SPELLS__CASTING, "Spell %d: Reagent focus item prevented reagent consumption (%d chance)", spell_id, reg_focus);
} else {
@ -2237,7 +2237,7 @@ bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, uint16 slot, uint16
{
recast -= GetAA(aaTouchoftheWicked) * 420;
}
int reduction = CastToClient()->GetFocusEffect(focusReduceRecastTime, spell_id);
int reduction = CastToClient()->GetFocusEffect(focusReduceRecastTime, spell_id);//Client only
if(reduction)
recast -= reduction;
@ -4189,14 +4189,8 @@ float Mob::ResistSpell(uint8 resist_type, uint16 spell_id, Mob *caster, bool use
//Get resist modifier and adjust it based on focus 2 resist about eq to 1% resist chance
int resist_modifier = (use_resist_override) ? resist_override : spells[spell_id].ResistDiff;
if(caster->IsClient())
{
if(IsValidSpell(spell_id))
{
int focus_resist = caster->CastToClient()->GetFocusEffect(focusResistRate, spell_id);
resist_modifier -= 2 * focus_resist;
}
}
int focus_resist = caster->GetFocusEffect(focusResistRate, spell_id);
resist_modifier -= 2 * focus_resist;
//Check for fear resist
bool IsFear = false;
@ -4580,17 +4574,14 @@ float Mob::GetAOERange(uint16 spell_id) {
if(range == 0)
range = 10; //something....
if (IsClient()) {
if(IsBardSong(spell_id) && IsBeneficialSpell(spell_id)) {
//Live AA - Extended Notes, SionachiesCrescendo
float song_bonus = static_cast<float>(aabonuses.SongRange + spellbonuses.SongRange + itembonuses.SongRange);
range += range*song_bonus /100.0f;
}
range = CastToClient()->GetActSpellRange(spell_id, range);
if(IsBardSong(spell_id) && IsBeneficialSpell(spell_id)) {
//Live AA - Extended Notes, SionachiesCrescendo
float song_bonus = static_cast<float>(aabonuses.SongRange + spellbonuses.SongRange + itembonuses.SongRange);
range += range*song_bonus /100.0f;
}
range = GetActSpellRange(spell_id, range);
return(range);
}