mirror of
https://github.com/EQEmu/Server.git
synced 2026-06-09 22:20:24 +00:00
Merge branch 'master' into Bot_Raid_work
This commit is contained in:
+360
-243
@@ -44,18 +44,16 @@ float Mob::GetActSpellRange(uint16 spell_id, float range, bool IsBard)
|
||||
|
||||
int32 Mob::GetActSpellDamage(uint16 spell_id, int32 value, Mob* target) {
|
||||
|
||||
if (spells[spell_id].targettype == ST_Self)
|
||||
if (spells[spell_id].target_type == ST_Self)
|
||||
return value;
|
||||
|
||||
if (IsNPC())
|
||||
value += value*CastToNPC()->GetSpellFocusDMG()/100;
|
||||
|
||||
Critical = false; //Mitch removed bool
|
||||
int32 value_BaseEffect = 0;
|
||||
bool Critical = false;
|
||||
int32 base_value = value;
|
||||
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)
|
||||
value -= (GetLevel() - 40) * 20;
|
||||
@@ -99,15 +97,16 @@ int32 Mob::GetActSpellDamage(uint16 spell_id, int32 value, Mob* target) {
|
||||
|
||||
if (Critical){
|
||||
|
||||
value = value_BaseEffect*ratio/100;
|
||||
value = base_value*ratio/100;
|
||||
|
||||
value += value_BaseEffect*GetFocusEffect(focusImprovedDamage, spell_id)/100;
|
||||
value += value_BaseEffect*GetFocusEffect(focusImprovedDamage2, spell_id)/100;
|
||||
value += base_value*GetFocusEffect(focusImprovedDamage, spell_id)/100;
|
||||
value += base_value*GetFocusEffect(focusImprovedDamage2, spell_id)/100;
|
||||
|
||||
value += int(value_BaseEffect*GetFocusEffect(focusFcDamagePctCrit, spell_id)/100)*ratio/100;
|
||||
value += int(base_value*GetFocusEffect(focusFcDamagePctCrit, spell_id)/100)*ratio/100;
|
||||
value += int(base_value*GetFocusEffect(focusFcAmplifyMod, spell_id) / 100)*ratio / 100;
|
||||
|
||||
if (target) {
|
||||
value += int(value_BaseEffect*target->GetVulnerability(this, spell_id, 0)/100)*ratio/100;
|
||||
value += int(base_value*target->GetVulnerability(this, spell_id, 0)/100)*ratio/100;
|
||||
value -= target->GetFcDamageAmtIncoming(this, spell_id);
|
||||
}
|
||||
|
||||
@@ -115,12 +114,13 @@ int32 Mob::GetActSpellDamage(uint16 spell_id, int32 value, Mob* target) {
|
||||
|
||||
value -= GetFocusEffect(focusFcDamageAmt, spell_id);
|
||||
value -= GetFocusEffect(focusFcDamageAmt2, spell_id);
|
||||
value -= GetFocusEffect(focusFcAmplifyAmt, spell_id);
|
||||
|
||||
if (RuleB(Spells, IgnoreSpellDmgLvlRestriction) && !spells[spell_id].no_heal_damage_item_mod && itembonuses.SpellDmg)
|
||||
value -= GetExtraSpellAmt(spell_id, itembonuses.SpellDmg, value)*ratio / 100;
|
||||
value -= GetExtraSpellAmt(spell_id, itembonuses.SpellDmg, base_value)*ratio / 100;
|
||||
|
||||
else if(!spells[spell_id].no_heal_damage_item_mod && itembonuses.SpellDmg && spells[spell_id].classes[(GetClass() % 17) - 1] >= GetLevel() - 5)
|
||||
value -= GetExtraSpellAmt(spell_id, itembonuses.SpellDmg, value)*ratio/100;
|
||||
value -= GetExtraSpellAmt(spell_id, itembonuses.SpellDmg, base_value)*ratio/100;
|
||||
|
||||
else if (IsNPC() && CastToNPC()->GetSpellScale())
|
||||
value = int(static_cast<float>(value) * CastToNPC()->GetSpellScale() / 100.0f);
|
||||
@@ -136,15 +136,16 @@ int32 Mob::GetActSpellDamage(uint16 spell_id, int32 value, Mob* target) {
|
||||
}
|
||||
}
|
||||
//Non Crtical Hit Calculation pathway
|
||||
value = value_BaseEffect;
|
||||
value = base_value;
|
||||
|
||||
value += value_BaseEffect*GetFocusEffect(focusImprovedDamage, spell_id)/100;
|
||||
value += value_BaseEffect*GetFocusEffect(focusImprovedDamage2, spell_id)/100;
|
||||
value += base_value*GetFocusEffect(focusImprovedDamage, spell_id)/100;
|
||||
value += base_value*GetFocusEffect(focusImprovedDamage2, spell_id)/100;
|
||||
|
||||
value += value_BaseEffect*GetFocusEffect(focusFcDamagePctCrit, spell_id)/100;
|
||||
value += base_value*GetFocusEffect(focusFcDamagePctCrit, spell_id)/100;
|
||||
value += base_value*GetFocusEffect(focusFcAmplifyMod, spell_id)/100;
|
||||
|
||||
if (target) {
|
||||
value += value_BaseEffect*target->GetVulnerability(this, spell_id, 0)/100;
|
||||
value += base_value*target->GetVulnerability(this, spell_id, 0)/100;
|
||||
value -= target->GetFcDamageAmtIncoming(this, spell_id);
|
||||
}
|
||||
|
||||
@@ -152,12 +153,13 @@ int32 Mob::GetActSpellDamage(uint16 spell_id, int32 value, Mob* target) {
|
||||
|
||||
value -= GetFocusEffect(focusFcDamageAmt, spell_id);
|
||||
value -= GetFocusEffect(focusFcDamageAmt2, spell_id);
|
||||
value -= GetFocusEffect(focusFcAmplifyAmt, spell_id);
|
||||
|
||||
if (RuleB(Spells, IgnoreSpellDmgLvlRestriction) && !spells[spell_id].no_heal_damage_item_mod && itembonuses.SpellDmg)
|
||||
value -= GetExtraSpellAmt(spell_id, itembonuses.SpellDmg, value);
|
||||
value -= GetExtraSpellAmt(spell_id, itembonuses.SpellDmg, base_value);
|
||||
|
||||
else if (!spells[spell_id].no_heal_damage_item_mod && itembonuses.SpellDmg && spells[spell_id].classes[(GetClass() % 17) - 1] >= GetLevel() - 5)
|
||||
value -= GetExtraSpellAmt(spell_id, itembonuses.SpellDmg, value);
|
||||
value -= GetExtraSpellAmt(spell_id, itembonuses.SpellDmg, base_value);
|
||||
|
||||
if (IsNPC() && CastToNPC()->GetSpellScale())
|
||||
value = int(static_cast<float>(value) * CastToNPC()->GetSpellScale() / 100.0f);
|
||||
@@ -165,15 +167,43 @@ int32 Mob::GetActSpellDamage(uint16 spell_id, int32 value, Mob* target) {
|
||||
return value;
|
||||
}
|
||||
|
||||
int32 Mob::GetActReflectedSpellDamage(int32 spell_id, int32 value, int effectiveness) {
|
||||
/*
|
||||
Reflected spells use the spells base damage before any modifiers or formulas applied.
|
||||
That value can then be modifier by the reflect spells 'max' value, defined here as effectiveness
|
||||
Default effectiveness is set at 100.
|
||||
Extra Spell Damage stat from the with the reflect effect will be applied to reflected damage
|
||||
with no level limitation, this was confirmed with extensive parsing ~Kayen
|
||||
*/
|
||||
if (IsNPC()) {
|
||||
value += value * CastToNPC()->GetSpellFocusDMG() / 100;
|
||||
|
||||
if (CastToNPC()->GetSpellScale()) {
|
||||
value = int(static_cast<float>(value) * CastToNPC()->GetSpellScale() / 100.0f);
|
||||
}
|
||||
}
|
||||
|
||||
int32 base_spell_dmg = value;
|
||||
|
||||
value = value * effectiveness / 100;
|
||||
|
||||
if (!spells[spell_id].no_heal_damage_item_mod && itembonuses.SpellDmg) {
|
||||
value -= GetExtraSpellAmt(spell_id, itembonuses.SpellDmg, base_spell_dmg);
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
int32 Mob::GetActDoTDamage(uint16 spell_id, int32 value, Mob* target) {
|
||||
|
||||
if (target == nullptr)
|
||||
return value;
|
||||
|
||||
if (IsNPC()) {
|
||||
value += value * CastToNPC()->GetSpellFocusDMG() / 100;
|
||||
}
|
||||
|
||||
if (IsNPC())
|
||||
value += value*CastToNPC()->GetSpellFocusDMG()/100;
|
||||
|
||||
int32 value_BaseEffect = 0;
|
||||
int32 base_value = value;
|
||||
int32 extra_dmg = 0;
|
||||
int16 chance = 0;
|
||||
chance += itembonuses.CriticalDoTChance + spellbonuses.CriticalDoTChance + aabonuses.CriticalDoTChance;
|
||||
@@ -184,23 +214,32 @@ int32 Mob::GetActDoTDamage(uint16 spell_id, int32 value, Mob* target) {
|
||||
if (spells[spell_id].override_crit_chance > 0 && chance > spells[spell_id].override_crit_chance)
|
||||
chance = spells[spell_id].override_crit_chance;
|
||||
|
||||
value_BaseEffect = value + (value*GetFocusEffect(focusFcBaseEffects, spell_id)/100);
|
||||
|
||||
if (chance > 0 && (zone->random.Roll(chance))) {
|
||||
if (!spells[spell_id].good_effect && chance > 0 && (zone->random.Roll(chance))) {
|
||||
int32 ratio = 200;
|
||||
ratio += itembonuses.DotCritDmgIncrease + spellbonuses.DotCritDmgIncrease + aabonuses.DotCritDmgIncrease;
|
||||
value = value_BaseEffect*ratio/100;
|
||||
value += int(value_BaseEffect*GetFocusEffect(focusImprovedDamage, spell_id)/100)*ratio/100;
|
||||
value += int(value_BaseEffect*GetFocusEffect(focusImprovedDamage2, spell_id)/100)*ratio/100;
|
||||
value += int(value_BaseEffect*GetFocusEffect(focusFcDamagePctCrit, spell_id)/100)*ratio/100;
|
||||
value += int(value_BaseEffect*target->GetVulnerability(this, spell_id, 0)/100)*ratio/100;
|
||||
value = base_value*ratio/100;
|
||||
value += int(base_value*GetFocusEffect(focusImprovedDamage, spell_id)/100)*ratio/100;
|
||||
value += int(base_value*GetFocusEffect(focusImprovedDamage2, spell_id)/100)*ratio/100;
|
||||
value += int(base_value*GetFocusEffect(focusFcDamagePctCrit, spell_id)/100)*ratio/100;
|
||||
value += int(base_value*GetFocusEffect(focusFcAmplifyMod, spell_id) / 100)*ratio/100;
|
||||
value += int(base_value*target->GetVulnerability(this, spell_id, 0)/100)*ratio/100;
|
||||
extra_dmg = target->GetFcDamageAmtIncoming(this, spell_id) +
|
||||
int(GetFocusEffect(focusFcDamageAmtCrit, spell_id)*ratio/100) +
|
||||
GetFocusEffect(focusFcDamageAmt, spell_id) +
|
||||
GetFocusEffect(focusFcDamageAmt2, spell_id);
|
||||
GetFocusEffect(focusFcDamageAmt2, spell_id) +
|
||||
GetFocusEffect(focusFcAmplifyAmt, spell_id);
|
||||
|
||||
if (RuleB(Spells, DOTsScaleWithSpellDmg)) {
|
||||
if (RuleB(Spells, IgnoreSpellDmgLvlRestriction) && !spells[spell_id].no_heal_damage_item_mod && itembonuses.SpellDmg) {
|
||||
extra_dmg += GetExtraSpellAmt(spell_id, itembonuses.SpellDmg, base_value)*ratio/100;
|
||||
}
|
||||
else if(!spells[spell_id].no_heal_damage_item_mod && itembonuses.SpellDmg && spells[spell_id].classes[(GetClass() % 17) - 1] >= GetLevel() - 5) {
|
||||
extra_dmg += GetExtraSpellAmt(spell_id, itembonuses.SpellDmg, base_value)*ratio/100;
|
||||
}
|
||||
}
|
||||
|
||||
if (extra_dmg) {
|
||||
int duration = CalcBuffDuration(this, this, spell_id);
|
||||
int duration = CalcBuffDuration(this, target, spell_id);
|
||||
if (duration > 0)
|
||||
extra_dmg /= duration;
|
||||
}
|
||||
@@ -209,18 +248,29 @@ int32 Mob::GetActDoTDamage(uint16 spell_id, int32 value, Mob* target) {
|
||||
}
|
||||
else {
|
||||
|
||||
value = value_BaseEffect;
|
||||
value += value_BaseEffect*GetFocusEffect(focusImprovedDamage, spell_id)/100;
|
||||
value += value_BaseEffect*GetFocusEffect(focusImprovedDamage2, spell_id)/100;
|
||||
value += value_BaseEffect*GetFocusEffect(focusFcDamagePctCrit, spell_id)/100;
|
||||
value += value_BaseEffect*target->GetVulnerability(this, spell_id, 0)/100;
|
||||
value = base_value;
|
||||
value += base_value*GetFocusEffect(focusImprovedDamage, spell_id)/100;
|
||||
value += base_value*GetFocusEffect(focusImprovedDamage2, spell_id)/100;
|
||||
value += base_value*GetFocusEffect(focusFcDamagePctCrit, spell_id)/100;
|
||||
value += base_value*GetFocusEffect(focusFcAmplifyMod, spell_id)/100;
|
||||
value += base_value*target->GetVulnerability(this, spell_id, 0)/100;
|
||||
extra_dmg = target->GetFcDamageAmtIncoming(this, spell_id) +
|
||||
GetFocusEffect(focusFcDamageAmtCrit, spell_id) +
|
||||
GetFocusEffect(focusFcDamageAmt, spell_id) +
|
||||
GetFocusEffect(focusFcDamageAmt2, spell_id);
|
||||
GetFocusEffect(focusFcDamageAmt2, spell_id) +
|
||||
GetFocusEffect(focusFcAmplifyAmt, spell_id);
|
||||
|
||||
if (RuleB(Spells, DOTsScaleWithSpellDmg)) {
|
||||
if (RuleB(Spells, IgnoreSpellDmgLvlRestriction) && !spells[spell_id].no_heal_damage_item_mod && itembonuses.SpellDmg) {
|
||||
extra_dmg += GetExtraSpellAmt(spell_id, itembonuses.SpellDmg, base_value);
|
||||
}
|
||||
else if(!spells[spell_id].no_heal_damage_item_mod && itembonuses.SpellDmg && spells[spell_id].classes[(GetClass() % 17) - 1] >= GetLevel() - 5) {
|
||||
extra_dmg += GetExtraSpellAmt(spell_id, itembonuses.SpellDmg, base_value);
|
||||
}
|
||||
}
|
||||
|
||||
if (extra_dmg) {
|
||||
int duration = CalcBuffDuration(this, this, spell_id);
|
||||
int duration = CalcBuffDuration(this, target, spell_id);
|
||||
if (duration > 0)
|
||||
extra_dmg /= duration;
|
||||
}
|
||||
@@ -253,69 +303,104 @@ int32 Mob::GetExtraSpellAmt(uint16 spell_id, int32 extra_spell_amt, int32 base_s
|
||||
else
|
||||
extra_spell_amt = extra_spell_amt * total_cast_time / 7000;
|
||||
|
||||
if(extra_spell_amt*2 < base_spell_dmg)
|
||||
return 0;
|
||||
//Confirmed with parsing 10/9/21 ~Kayen
|
||||
if (extra_spell_amt * 2 > abs(base_spell_dmg)) {
|
||||
extra_spell_amt = abs(base_spell_dmg) / 2;
|
||||
}
|
||||
|
||||
return extra_spell_amt;
|
||||
return extra_spell_amt;
|
||||
}
|
||||
|
||||
int32 Mob::GetActSpellHealing(uint16 spell_id, int32 value, Mob* target) {
|
||||
|
||||
if (target == nullptr)
|
||||
target = this;
|
||||
|
||||
if (IsNPC())
|
||||
value += value*CastToNPC()->GetSpellFocusHeal()/100;
|
||||
if (IsNPC()) {
|
||||
value += value * CastToNPC()->GetSpellFocusHeal() / 100;
|
||||
}
|
||||
|
||||
int32 value_BaseEffect = 0;
|
||||
int16 chance = 0;
|
||||
int8 modifier = 1;
|
||||
Critical = false; //mitch
|
||||
Critical = false;
|
||||
int32 base_value = value;
|
||||
int16 critical_chance = 0;
|
||||
int8 critical_modifier = 1;
|
||||
|
||||
value_BaseEffect = value + (value*GetFocusEffect(focusFcBaseEffects, spell_id)/100);
|
||||
if (spells[spell_id].buff_duration < 1) {
|
||||
critical_chance += itembonuses.CriticalHealChance + spellbonuses.CriticalHealChance + aabonuses.CriticalHealChance;
|
||||
|
||||
value = value_BaseEffect;
|
||||
if (spellbonuses.CriticalHealDecay) {
|
||||
critical_chance += GetDecayEffectValue(spell_id, SE_CriticalHealDecay);
|
||||
}
|
||||
}
|
||||
else {
|
||||
critical_chance = itembonuses.CriticalHealOverTime + spellbonuses.CriticalHealOverTime + aabonuses.CriticalHealOverTime;
|
||||
|
||||
value += int(value_BaseEffect*GetFocusEffect(focusImprovedHeal, spell_id)/100);
|
||||
if (spellbonuses.CriticalRegenDecay) {
|
||||
critical_chance += GetDecayEffectValue(spell_id, SE_CriticalRegenDecay);
|
||||
}
|
||||
}
|
||||
|
||||
// Instant Heals
|
||||
if(spells[spell_id].buffduration < 1) {
|
||||
if (critical_chance) {
|
||||
|
||||
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 (spells[spell_id].override_crit_chance > 0 && chance > spells[spell_id].override_crit_chance)
|
||||
chance = spells[spell_id].override_crit_chance;
|
||||
|
||||
if(chance && (zone->random.Roll(chance))) {
|
||||
Critical = true;
|
||||
modifier = 2; //At present time no critical heal amount modifier SPA exists.
|
||||
if (spells[spell_id].override_crit_chance > 0 && critical_chance > spells[spell_id].override_crit_chance) {
|
||||
critical_chance = spells[spell_id].override_crit_chance;
|
||||
}
|
||||
|
||||
value *= modifier;
|
||||
value += GetFocusEffect(focusFcHealAmtCrit, spell_id) * modifier;
|
||||
value += GetFocusEffect(focusFcHealAmt, spell_id);
|
||||
value += target->GetFocusIncoming(focusFcHealAmtIncoming, SE_FcHealAmtIncoming, this, spell_id);
|
||||
if (zone->random.Roll(critical_chance)) {
|
||||
critical_modifier = 2; //At present time no critical heal amount modifier SPA exists.
|
||||
}
|
||||
}
|
||||
|
||||
if(!spells[spell_id].no_heal_damage_item_mod && itembonuses.HealAmt && spells[spell_id].classes[(GetClass()%17) - 1] >= GetLevel() - 5)
|
||||
value += GetExtraSpellAmt(spell_id, itembonuses.HealAmt, value) * modifier;
|
||||
if (GetClass() == CLERIC) {
|
||||
value += int(base_value*RuleI(Spells, ClericInnateHealFocus) / 100); //confirmed on live parsing clerics get an innate 5 pct heal focus
|
||||
}
|
||||
value += int(base_value*GetFocusEffect(focusImprovedHeal, spell_id) / 100);
|
||||
value += int(base_value*GetFocusEffect(focusFcAmplifyMod, spell_id) / 100);
|
||||
|
||||
value += value*target->GetHealRate(spell_id, this)/100;
|
||||
// Instant Heals
|
||||
if (spells[spell_id].buff_duration < 1) {
|
||||
|
||||
if (IsNPC() && CastToNPC()->GetHealScale())
|
||||
if (target) {
|
||||
value += int(base_value * target->GetFocusEffect(focusFcHealPctIncoming, spell_id, this)/100); //SPA 393 Add before critical
|
||||
value += int(base_value * target->GetFocusEffect(focusFcHealPctCritIncoming, spell_id, this)/100); //SPA 395 Add before critical (?)
|
||||
}
|
||||
|
||||
value += GetFocusEffect(focusFcHealAmtCrit, spell_id); //SPA 396 Add before critical
|
||||
|
||||
//Using IgnoreSpellDmgLvlRestriction to also allow healing to scale
|
||||
if (RuleB(Spells, IgnoreSpellDmgLvlRestriction) && !spells[spell_id].no_heal_damage_item_mod && itembonuses.HealAmt) {
|
||||
value += GetExtraSpellAmt(spell_id, itembonuses.HealAmt, base_value);//Item Heal Amt Add before critical
|
||||
}
|
||||
else if (!spells[spell_id].no_heal_damage_item_mod && itembonuses.HealAmt && spells[spell_id].classes[(GetClass() % 17) - 1] >= GetLevel() - 5) {
|
||||
value += GetExtraSpellAmt(spell_id, itembonuses.HealAmt, base_value);//Item Heal Amt Add before critical
|
||||
}
|
||||
|
||||
if (target) {
|
||||
value += value * target->GetHealRate() / 100; //SPA 120 modifies value after Focus Applied but before critical
|
||||
}
|
||||
|
||||
/*
|
||||
Apply critical hit modifier
|
||||
*/
|
||||
|
||||
value *= critical_modifier;
|
||||
value += GetFocusEffect(focusFcHealAmt, spell_id); //SPA 392 Add after critical
|
||||
value += GetFocusEffect(focusFcAmplifyAmt, spell_id); //SPA 508 ? Add after critical
|
||||
|
||||
if (target) {
|
||||
value += target->GetFocusEffect(focusFcHealAmtIncoming, spell_id, this); //SPA 394 Add after critical
|
||||
}
|
||||
|
||||
if (IsNPC() && CastToNPC()->GetHealScale()) {
|
||||
value = int(static_cast<float>(value) * CastToNPC()->GetHealScale() / 100.0f);
|
||||
}
|
||||
|
||||
if (Critical) {
|
||||
if (critical_modifier > 1) {
|
||||
entity_list.MessageCloseString(
|
||||
this, true, 100, Chat::SpellCrit,
|
||||
OTHER_CRIT_HEAL, GetName(), itoa(value));
|
||||
|
||||
if (IsClient())
|
||||
if (IsClient()) {
|
||||
MessageString(Chat::SpellCrit, YOU_CRIT_HEAL, itoa(value));
|
||||
}
|
||||
}
|
||||
|
||||
return value;
|
||||
@@ -323,20 +408,32 @@ int32 Mob::GetActSpellHealing(uint16 spell_id, int32 value, Mob* target) {
|
||||
|
||||
//Heal over time spells. [Heal Rate and Additional Healing effects do not increase this value]
|
||||
else {
|
||||
//Using IgnoreSpellDmgLvlRestriction to also allow healing to scale
|
||||
int32 extra_heal = 0;
|
||||
if (RuleB(Spells, HOTsScaleWithHealAmt)) {
|
||||
if (RuleB(Spells, IgnoreSpellDmgLvlRestriction) && !spells[spell_id].no_heal_damage_item_mod && itembonuses.HealAmt) {
|
||||
extra_heal += GetExtraSpellAmt(spell_id, itembonuses.HealAmt, base_value);
|
||||
}
|
||||
else if(!spells[spell_id].no_heal_damage_item_mod && itembonuses.HealAmt && spells[spell_id].classes[(GetClass() % 17) - 1] >= GetLevel() - 5) {
|
||||
extra_heal += GetExtraSpellAmt(spell_id, itembonuses.HealAmt, base_value);
|
||||
}
|
||||
}
|
||||
|
||||
if (extra_heal) {
|
||||
int duration = CalcBuffDuration(this, target, spell_id);
|
||||
if (duration > 0) {
|
||||
extra_heal /= duration;
|
||||
value += extra_heal;
|
||||
}
|
||||
}
|
||||
|
||||
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))
|
||||
value *= 2;
|
||||
if (critical_chance && zone->random.Roll(critical_chance))
|
||||
value *= critical_modifier;
|
||||
}
|
||||
|
||||
if (IsNPC() && CastToNPC()->GetHealScale())
|
||||
if (IsNPC() && CastToNPC()->GetHealScale()) {
|
||||
value = int(static_cast<float>(value) * CastToNPC()->GetHealScale() / 100.0f);
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
@@ -409,29 +506,6 @@ int32 Mob::GetActSpellDuration(uint16 spell_id, int32 duration)
|
||||
return ifocused + 1;
|
||||
}
|
||||
|
||||
int32 Client::GetActSpellCasttime(uint16 spell_id, int32 casttime)
|
||||
{
|
||||
int32 cast_reducer = 0;
|
||||
cast_reducer += GetFocusEffect(focusSpellHaste, spell_id);
|
||||
|
||||
//this function loops through the effects of spell_id many times
|
||||
//could easily be consolidated.
|
||||
|
||||
if (GetLevel() >= 51 && casttime >= 3000 && !BeneficialSpell(spell_id)
|
||||
&& (GetClass() == SHADOWKNIGHT || GetClass() == RANGER
|
||||
|| GetClass() == PALADIN || GetClass() == BEASTLORD ))
|
||||
cast_reducer += (GetLevel()-50)*3;
|
||||
|
||||
//LIVE AA SpellCastingDeftness, QuickBuff, QuickSummoning, QuickEvacuation, QuickDamage
|
||||
|
||||
if (cast_reducer > RuleI(Spells, MaxCastTimeReduction))
|
||||
cast_reducer = RuleI(Spells, MaxCastTimeReduction);
|
||||
|
||||
casttime = (casttime*(100 - cast_reducer)/100);
|
||||
|
||||
return casttime;
|
||||
}
|
||||
|
||||
bool Client::TrainDiscipline(uint32 itemid) {
|
||||
|
||||
//get the item info
|
||||
@@ -534,6 +608,90 @@ bool Client::TrainDiscipline(uint32 itemid) {
|
||||
return(false);
|
||||
}
|
||||
|
||||
bool Client::MemorizeSpellFromItem(uint32 item_id) {
|
||||
const EQ::ItemData *item = database.GetItem(item_id);
|
||||
if(item == nullptr) {
|
||||
Message(Chat::Red, "Unable to find the scroll!");
|
||||
LogError("Unable to find scroll id [{}]\n", (unsigned long)item_id);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!item->IsClassCommon() || item->ItemType != EQ::item::ItemTypeSpell) {
|
||||
Message(Chat::Red, "Invalid item type, you cannot learn from this item.");
|
||||
SummonItem(item_id);
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!(
|
||||
item->Name[0] == 'S' &&
|
||||
item->Name[1] == 'p' &&
|
||||
item->Name[2] == 'e' &&
|
||||
item->Name[3] == 'l' &&
|
||||
item->Name[4] == 'l' &&
|
||||
item->Name[5] == ':' &&
|
||||
item->Name[6] == ' '
|
||||
)) {
|
||||
Message(Chat::Red, "This item is not a scroll.");
|
||||
SummonItem(item_id);
|
||||
return false;
|
||||
}
|
||||
int player_class = GetClass();
|
||||
uint32 cbit = 1 << (player_class - 1);
|
||||
if(!(item->Classes & cbit)) {
|
||||
Message(Chat::Red, "Your class cannot learn from this scroll.");
|
||||
SummonItem(item_id);
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32 spell_id = item->Scroll.Effect;
|
||||
if(!IsValidSpell(spell_id)) {
|
||||
Message(Chat::Red, "This scroll contains invalid knowledge.");
|
||||
return false;
|
||||
}
|
||||
|
||||
const SPDat_Spell_Struct &spell = spells[spell_id];
|
||||
uint8 level_to_use = spell.classes[player_class - 1];
|
||||
if(level_to_use == 255) {
|
||||
Message(Chat::Red, "Your class cannot learn from this scroll.");
|
||||
SummonItem(item_id);
|
||||
return false;
|
||||
}
|
||||
|
||||
if(level_to_use > GetLevel()) {
|
||||
Message(Chat::Red, "You must be at least level %d to learn this spell.", level_to_use);
|
||||
SummonItem(item_id);
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int index = 0; index < EQ::spells::SPELLBOOK_SIZE; index++) {
|
||||
if (!HasSpellScribed(spell_id)) {
|
||||
auto next_slot = GetNextAvailableSpellBookSlot();
|
||||
if (next_slot != -1) {
|
||||
ScribeSpell(spell_id, next_slot);
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
Message(
|
||||
Chat::Red,
|
||||
"Unable to scribe spell %s (%i) to spellbook: no more spell book slots available.",
|
||||
((spell_id >= 0 && spell_id < SPDAT_RECORDS) ? spells[spell_id].name : "Out-of-range"),
|
||||
spell_id
|
||||
);
|
||||
SummonItem(item_id);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
Message(Chat::Red, "You already know this spell.");
|
||||
SummonItem(item_id);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Message(Chat::Red, "You have learned too many spells and can learn no more.");
|
||||
return false;
|
||||
}
|
||||
|
||||
void Client::TrainDiscBySpellID(int32 spell_id)
|
||||
{
|
||||
int i;
|
||||
@@ -570,8 +728,15 @@ void Client::SendDisciplineUpdate() {
|
||||
|
||||
bool Client::UseDiscipline(uint32 spell_id, uint32 target) {
|
||||
// Dont let client waste a reuse timer if they can't use the disc
|
||||
if (IsStunned() || IsFeared() || IsMezzed() || IsAmnesiad() || IsPet())
|
||||
if ((IsStunned() && !IgnoreCastingRestriction(spell_id))||
|
||||
IsFeared() ||
|
||||
(IsMezzed() && !IgnoreCastingRestriction(spell_id)) ||
|
||||
IsAmnesiad() ||
|
||||
IsPet())
|
||||
{
|
||||
if (IsAmnesiad()) {
|
||||
MessageString(Chat::Red, MELEE_SILENCE);
|
||||
}
|
||||
return(false);
|
||||
}
|
||||
|
||||
@@ -590,6 +755,10 @@ bool Client::UseDiscipline(uint32 spell_id, uint32 target) {
|
||||
return(false);
|
||||
}
|
||||
|
||||
if (DivineAura() && !IgnoreCastingRestriction(spell_id)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
//can we use the spell?
|
||||
const SPDat_Spell_Struct &spell = spells[spell_id];
|
||||
uint8 level_to_use = spell.classes[GetClass() - 1];
|
||||
@@ -605,7 +774,7 @@ bool Client::UseDiscipline(uint32 spell_id, uint32 target) {
|
||||
return(false);
|
||||
}
|
||||
|
||||
if(GetEndurance() < spell.EndurCost) {
|
||||
if(GetEndurance() < spell.endurance_cost) {
|
||||
Message(11, "You are too fatigued to use this skill right now.");
|
||||
return(false);
|
||||
}
|
||||
@@ -617,22 +786,26 @@ bool Client::UseDiscipline(uint32 spell_id, uint32 target) {
|
||||
}
|
||||
|
||||
// the client does this check before calling CastSpell, should prevent discs being eaten
|
||||
if (spell.buffdurationformula != 0 && spell.targettype == ST_Self && HasDiscBuff())
|
||||
if (spell.buff_duration_formula != 0 && spell.target_type == ST_Self && HasDiscBuff())
|
||||
return false;
|
||||
|
||||
//Check the disc timer
|
||||
pTimerType DiscTimer = pTimerDisciplineReuseStart + spell.EndurTimerIndex;
|
||||
pTimerType DiscTimer = pTimerDisciplineReuseStart + spell.timer_id;
|
||||
if(!p_timers.Expired(&database, DiscTimer, false)) { // lets not set the reuse timer in case CastSpell fails (or we would have to turn off the timer, but CastSpell will set it as well)
|
||||
/*char val1[20]={0};*/ //unused
|
||||
/*char val2[20]={0};*/ //unused
|
||||
uint32 remain = p_timers.GetRemainingTime(DiscTimer);
|
||||
//MessageString(Chat::White, DISCIPLINE_CANUSEIN, ConvertArray((remain)/60,val1), ConvertArray(remain%60,val2));
|
||||
Message(0, "You can use this discipline in %d minutes %d seconds.", ((remain)/60), (remain%60));
|
||||
return(false);
|
||||
uint32 remaining_time = p_timers.GetRemainingTime(DiscTimer);
|
||||
Message(
|
||||
Chat::White,
|
||||
fmt::format(
|
||||
"You can use this discipline in {}.",
|
||||
ConvertSecondsToTime(remaining_time)
|
||||
).c_str()
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
if(spell.recast_time > 0)
|
||||
{
|
||||
bool instant_recast = true;
|
||||
|
||||
if (spell.recast_time > 0) {
|
||||
uint32 reduced_recast = spell.recast_time / 1000;
|
||||
auto focus = GetFocusEffect(focusReduceRecastTime, spell_id);
|
||||
// do stupid stuff because custom servers.
|
||||
@@ -643,22 +816,36 @@ bool Client::UseDiscipline(uint32 spell_id, uint32 target) {
|
||||
reduced_recast = 0;
|
||||
if (GetPTimers().Enabled((uint32)DiscTimer))
|
||||
GetPTimers().Clear(&database, (uint32)DiscTimer);
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
reduced_recast -= focus;
|
||||
}
|
||||
|
||||
if (reduced_recast > 0)
|
||||
CastSpell(spell_id, target, EQ::spells::CastingSlot::Discipline, -1, -1, 0, -1, (uint32)DiscTimer, reduced_recast);
|
||||
else{
|
||||
CastSpell(spell_id, target, EQ::spells::CastingSlot::Discipline);
|
||||
return true;
|
||||
}
|
||||
if (reduced_recast > 0) {
|
||||
instant_recast = false;
|
||||
|
||||
SendDisciplineTimer(spells[spell_id].EndurTimerIndex, reduced_recast);
|
||||
if (GetClass() == BARD && IsCasting() && spells[spell_id].cast_time == 0) {
|
||||
if (DoCastingChecksOnCaster(spell_id)) {
|
||||
SpellFinished(spell_id, entity_list.GetMob(target), EQ::spells::CastingSlot::Discipline, 0, -1, spells[spell_id].resist_difficulty, false, -1, (uint32)DiscTimer, reduced_recast, false);
|
||||
}
|
||||
}
|
||||
else {
|
||||
CastSpell(spell_id, target, EQ::spells::CastingSlot::Discipline, -1, -1, 0, -1, (uint32)DiscTimer, reduced_recast);
|
||||
}
|
||||
|
||||
SendDisciplineTimer(spells[spell_id].timer_id, reduced_recast);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
CastSpell(spell_id, target, EQ::spells::CastingSlot::Discipline);
|
||||
|
||||
if (instant_recast) {
|
||||
if (GetClass() == BARD && IsCasting() && spells[spell_id].cast_time == 0) {
|
||||
if (DoCastingChecksOnCaster(spell_id)) {
|
||||
SpellFinished(spell_id, entity_list.GetMob(target), EQ::spells::CastingSlot::Discipline, 0, -1, spells[spell_id].resist_difficulty, false, -1, 0xFFFFFFFF, 0, false);
|
||||
}
|
||||
}
|
||||
else {
|
||||
CastSpell(spell_id, target, EQ::spells::CastingSlot::Discipline);
|
||||
}
|
||||
}
|
||||
return(true);
|
||||
}
|
||||
@@ -666,7 +853,7 @@ bool Client::UseDiscipline(uint32 spell_id, uint32 target) {
|
||||
uint32 Client::GetDisciplineTimer(uint32 timer_id) {
|
||||
pTimerType disc_timer_id = pTimerDisciplineReuseStart + timer_id;
|
||||
uint32 disc_timer = 0;
|
||||
if (GetPTimers().Enabled((uint32)disc_timer_id)) {
|
||||
if (GetPTimers().Enabled(disc_timer_id)) {
|
||||
disc_timer = GetPTimers().GetRemainingTime(disc_timer_id);
|
||||
}
|
||||
return disc_timer;
|
||||
@@ -674,12 +861,22 @@ uint32 Client::GetDisciplineTimer(uint32 timer_id) {
|
||||
|
||||
void Client::ResetDisciplineTimer(uint32 timer_id) {
|
||||
pTimerType disc_timer_id = pTimerDisciplineReuseStart + timer_id;
|
||||
if (GetPTimers().Enabled((uint32)disc_timer_id)) {
|
||||
GetPTimers().Clear(&database, (uint32)disc_timer_id);
|
||||
if (GetPTimers().Enabled(disc_timer_id)) {
|
||||
GetPTimers().Clear(&database, disc_timer_id);
|
||||
}
|
||||
SendDisciplineTimer(timer_id, 0);
|
||||
}
|
||||
|
||||
void Client::ResetAllDisciplineTimers() {
|
||||
for (pTimerType disc_timer_id = pTimerDisciplineReuseStart; disc_timer_id <= pTimerDisciplineReuseEnd; disc_timer_id++) {
|
||||
uint32 current_timer_id = (disc_timer_id - pTimerDisciplineReuseStart);
|
||||
if (GetPTimers().Enabled(disc_timer_id)) {
|
||||
GetPTimers().Clear(&database, disc_timer_id);
|
||||
}
|
||||
SendDisciplineTimer(current_timer_id, 0);
|
||||
}
|
||||
}
|
||||
|
||||
bool Client::HasDisciplineLearned(uint16 spell_id) {
|
||||
bool has_learned = false;
|
||||
for (auto index = 0; index < MAX_PP_DISCIPLINES; ++index) {
|
||||
@@ -763,7 +960,7 @@ void EntityList::AESpell(
|
||||
)
|
||||
{
|
||||
const auto &cast_target_position =
|
||||
spells[spell_id].targettype == ST_Ring ?
|
||||
spells[spell_id].target_type == ST_Ring ?
|
||||
caster_mob->GetTargetRingLocation() :
|
||||
static_cast<glm::vec3>(center_mob->GetPosition());
|
||||
|
||||
@@ -786,14 +983,14 @@ void EntityList::AESpell(
|
||||
/**
|
||||
* Max AOE targets
|
||||
*/
|
||||
int max_targets_allowed = 0; // unlimited
|
||||
int max_targets_allowed = RuleI(Range, AOEMaxTargets); // unlimited
|
||||
if (max_targets) { // rains pass this in since they need to preserve the count through waves
|
||||
max_targets_allowed = *max_targets;
|
||||
}
|
||||
else if (spells[spell_id].aemaxtargets) {
|
||||
max_targets_allowed = spells[spell_id].aemaxtargets;
|
||||
else if (spells[spell_id].aoe_max_targets) {
|
||||
max_targets_allowed = spells[spell_id].aoe_max_targets;
|
||||
}
|
||||
else if (IsTargetableAESpell(spell_id) && is_detrimental_spell && !is_npc) {
|
||||
else if (IsTargetableAESpell(spell_id) && is_detrimental_spell && !is_npc && !IsEffectInSpell(spell_id, SE_Lull) && !IsEffectInSpell(spell_id, SE_Mez)) {
|
||||
max_targets_allowed = 4;
|
||||
}
|
||||
|
||||
@@ -823,15 +1020,15 @@ void EntityList::AESpell(
|
||||
continue;
|
||||
}
|
||||
|
||||
if (spells[spell_id].targettype == ST_TargetAENoPlayersPets && current_mob->IsPetOwnerClient()) {
|
||||
if (spells[spell_id].target_type == ST_TargetAENoPlayersPets && current_mob->IsPetOwnerClient()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (spells[spell_id].targettype == ST_AreaClientOnly && !current_mob->IsClient()) {
|
||||
if (spells[spell_id].target_type == ST_AreaClientOnly && !current_mob->IsClient()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (spells[spell_id].targettype == ST_AreaNPCOnly && !current_mob->IsNPC()) {
|
||||
if (spells[spell_id].target_type == ST_AreaNPCOnly && !current_mob->IsNPC()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -865,21 +1062,21 @@ void EntityList::AESpell(
|
||||
}
|
||||
|
||||
if (is_npc && current_mob->IsNPC() &&
|
||||
spells[spell_id].targettype != ST_AreaNPCOnly) { //check npc->npc casting
|
||||
spells[spell_id].target_type != ST_AreaNPCOnly) { //check npc->npc casting
|
||||
FACTION_VALUE faction_value = current_mob->GetReverseFactionCon(caster_mob);
|
||||
if (is_detrimental_spell) {
|
||||
//affect mobs that are on our hate list, or
|
||||
//which have bad faction with us
|
||||
if (
|
||||
!(caster_mob->CheckAggro(current_mob) ||
|
||||
faction_value == FACTION_THREATENLY ||
|
||||
faction_value == FACTION_THREATENINGLY ||
|
||||
faction_value == FACTION_SCOWLS)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else {
|
||||
//only affect mobs we would assist.
|
||||
if (!(faction_value <= FACTION_AMIABLE)) {
|
||||
if (!(faction_value <= FACTION_AMIABLY)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
@@ -919,6 +1116,9 @@ void EntityList::AESpell(
|
||||
}
|
||||
}
|
||||
|
||||
current_mob->CalcSpellPowerDistanceMod(spell_id, distance_to_target);
|
||||
caster_mob->SpellOnTarget(spell_id, current_mob, 0, true, resist_adjust);
|
||||
|
||||
/**
|
||||
* Increment hit count if max targets
|
||||
*/
|
||||
@@ -928,9 +1128,6 @@ void EntityList::AESpell(
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
current_mob->CalcSpellPowerDistanceMod(spell_id, distance_to_target);
|
||||
caster_mob->SpellOnTarget(spell_id, current_mob, false, true, resist_adjust);
|
||||
}
|
||||
|
||||
LogAoeCast("Done iterating [{}]", caster_mob->GetCleanName());
|
||||
@@ -1001,90 +1198,6 @@ void EntityList::MassGroupBuff(
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Causes caster to hit every mob within dist range of center with a bard pulse of spell_id
|
||||
* NPC spells will only affect other NPCs with compatible faction
|
||||
*
|
||||
* @param caster
|
||||
* @param center
|
||||
* @param spell_id
|
||||
* @param affect_caster
|
||||
*/
|
||||
void EntityList::AEBardPulse(
|
||||
Mob *caster,
|
||||
Mob *center,
|
||||
uint16 spell_id,
|
||||
bool affect_caster)
|
||||
{
|
||||
Mob *current_mob = nullptr;
|
||||
float distance = caster->GetAOERange(spell_id);
|
||||
float distance_squared = distance * distance;
|
||||
bool is_detrimental_spell = IsDetrimentalSpell(spell_id);
|
||||
bool is_npc = caster->IsNPC();
|
||||
|
||||
for (auto &it : entity_list.GetCloseMobList(caster, distance)) {
|
||||
current_mob = it.second;
|
||||
|
||||
/**
|
||||
* Skip self
|
||||
*/
|
||||
if (current_mob == center) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (current_mob == caster && !affect_caster) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (DistanceSquared(center->GetPosition(), current_mob->GetPosition()) > distance_squared) { //make sure they are in range
|
||||
continue;
|
||||
}
|
||||
|
||||
/**
|
||||
* check npc->npc casting
|
||||
*/
|
||||
if (is_npc && current_mob->IsNPC()) {
|
||||
FACTION_VALUE faction = current_mob->GetReverseFactionCon(caster);
|
||||
if (is_detrimental_spell) {
|
||||
//affect mobs that are on our hate list, or
|
||||
//which have bad faction with us
|
||||
if (!(caster->CheckAggro(current_mob) || faction == FACTION_THREATENLY || faction == FACTION_SCOWLS)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else {
|
||||
//only affect mobs we would assist.
|
||||
if (!(faction <= FACTION_AMIABLE)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* LOS
|
||||
*/
|
||||
if (is_detrimental_spell) {
|
||||
if (!center->CheckLosFN(current_mob)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else { // check to stop casting beneficial ae buffs (to wit: bard songs) on enemies...
|
||||
// See notes in AESpell() above for more info.
|
||||
if (caster->IsAttackAllowed(current_mob, true)) {
|
||||
continue;
|
||||
}
|
||||
if (caster->CheckAggro(current_mob)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
current_mob->BardPulse(spell_id, caster);
|
||||
}
|
||||
if (caster->IsClient()) {
|
||||
caster->CastToClient()->CheckSongSkillIncrease(spell_id);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Rampage - Normal and Duration rampages
|
||||
* NPCs handle it differently in Mob::Rampage
|
||||
@@ -1100,7 +1213,8 @@ void EntityList::AEAttack(
|
||||
float distance,
|
||||
int Hand,
|
||||
int count,
|
||||
bool is_from_spell)
|
||||
bool is_from_spell,
|
||||
int attack_rounds)
|
||||
{
|
||||
Mob *current_mob = nullptr;
|
||||
float distance_squared = distance * distance;
|
||||
@@ -1112,15 +1226,18 @@ void EntityList::AEAttack(
|
||||
if (current_mob->IsNPC()
|
||||
&& current_mob != attacker //this is not needed unless NPCs can use this
|
||||
&& (attacker->IsAttackAllowed(current_mob))
|
||||
&& current_mob->GetRace() != 216 && current_mob->GetRace() != 472 /* dont attack horses */
|
||||
&& !current_mob->IsHorse() /* dont attack mounts */
|
||||
&& (DistanceSquared(current_mob->GetPosition(), attacker->GetPosition()) <= distance_squared)
|
||||
) {
|
||||
|
||||
if (!attacker->IsClient() || attacker->GetClass() == MONK || attacker->GetClass() == RANGER) {
|
||||
attacker->Attack(current_mob, Hand, false, false, is_from_spell);
|
||||
}
|
||||
else {
|
||||
attacker->CastToClient()->DoAttackRounds(current_mob, Hand, is_from_spell);
|
||||
for (int i = 0; i < attack_rounds; i++) {
|
||||
|
||||
if (!attacker->IsClient() || attacker->GetClass() == MONK || attacker->GetClass() == RANGER) {
|
||||
attacker->Attack(current_mob, Hand, false, false, is_from_spell);
|
||||
}
|
||||
else {
|
||||
attacker->CastToClient()->DoAttackRounds(current_mob, Hand, is_from_spell);
|
||||
}
|
||||
}
|
||||
|
||||
hit_count++;
|
||||
|
||||
Reference in New Issue
Block a user