diff --git a/common/spdat.h b/common/spdat.h index 837f10f78..24aac5890 100644 --- a/common/spdat.h +++ b/common/spdat.h @@ -62,6 +62,8 @@ #define SPELL_SHAPECHANGE60 1924 #define SPELL_COMMAND_OF_DRUZZIL 3355 #define SPELL_SHAPECHANGE70 6503 +#define SPELL_MANA_BURN 2751 +#define SPELL_LIFE_BURN 2755 // these have known hardcoded behavior but we don't do anything yet, move them above this comment when fixed #define SPELL_THE_DAINS_JUSTICE 1476 #define SPELL_MODULATION 1502 @@ -152,7 +154,7 @@ #define SPELL_RESURRECTION_SICKNESS 756 #define SPELL_RESURRECTION_SICKNESS2 5249 #define SPELL_REVIVAL_SICKNESS 13087 -#define SPELL_MANA_BURN 2751 + #define EFFECT_COUNT 12 @@ -807,13 +809,13 @@ typedef enum { #define SE_PC_Pet_Rampage 464 // implemented - Base1 % chance to do rampage for base2 % of damage each melee round //#define SE_PC_Pet_AE_Rampage 465 // Would assume as above but need to confirm. #define SE_PC_Pet_Flurry_Chance 466 // implemented - Base1 % chance to do flurry from double attack hit. -//#define SE_DS_Mitigation_Amount 467 // -//#define SE_DS_Mitigation_Percentage 468 // +#define SE_DS_Mitigation_Amount 467 // implemented - Modify incoming damage shield damage by a flat amount +#define SE_DS_Mitigation_Percentage 468 // implemented - Modify incoming damage shield damage by percentage //#define SE_Chance_Best_in_Spell_Grp 469 // //#define SE_Trigger_Best_in_Spell Grp 470 // //#define SE_Double_Melee_Round 471 // //#define SE_Buy_AA_Rank 472 // -//#define SE_Double_Backstab_Front 473 // +#define SE_Double_Backstab_Front 473 // implemented - Chance to double backstab from front //#define SE_Pet_Crit_Melee_Damage_Pct_Owner 474 // //#define SE_Trigger_Spell_Non_Item 475 // //#define SE_Weapon_Stance 476 // @@ -836,37 +838,37 @@ typedef enum { //#define SE_Ff_Endurance_Max 493 // //#define SE_Pet_Add_Atk 494 // //#define SE_Ff_DurationMax 495 // -//#define SE_Critical_Melee_Damage_Mod_Max 496 // +#define SE_Critical_Melee_Damage_Mod_Max 496 // implemented - increase or decrease by percent critical damage (not stackable) //#define SE_Ff_FocusCastProcNoBypass 497 // //#define SE_AddExtraAttackPct_1h_Primary 498 // //#define SE_AddExtraAttackPct_1h_Secondary 499 // //#define SE_Fc_CastTimeMod2 500 // //#define SE_Fc_CastTimeAmt 501 // //#define SE_Fearstun 502 // -//#define SE_Melee_Damage_Position_Mod 503 // +#define SE_Melee_Damage_Position_Mod 503 // implemented - modify melee damage by pct if done from Front or Behind //#define SE_Melee_Damage_Position_Amt 504 // -//#define SE_Damage_Taken_Position_Mod 505 // +#define SE_Damage_Taken_Position_Mod 505 // implemented - mitigate melee damage by pct if dmg taken from Front or Behind //#define SE_Damage_Taken_Position_Amt 506 // //#define SE_Fc_Amplify_Mod 507 // //#define SE_Fc_Amplify_Amt 508 // -//#define SE_Health_Transfer 509 // +#define SE_Health_Transfer 509 // implemented - exchange health for damage or healing on a target. ie Lifeburn/Act of Valor //#define SE_Fc_ResistIncoming 510 // //#define SE_Ff_FocusTimerMin 511 // //#define SE_Proc_Timer_Modifier 512 // //#define SE_Mana_Max_Percent 513 // //#define SE_Endurance_Max_Percent 514 // -//#define SE_AC_Avoidance_Max_Percent 515 // -//#define SE_AC_Mitigation_Max_Percent 516 // -//#define SE_Attack_Offense_Max_Percent 517 // -//#define SE_Attack_Accuracy_Max_Percent 518 // +#define SE_AC_Avoidance_Max_Percent 515 // implemented - stackable avoidance modifier +#define SE_AC_Mitigation_Max_Percent 516 // implemented - stackable defense modifier +//#define SE_Attack_Offense_Max_Percent 517 // +#define SE_Attack_Accuracy_Max_Percent 518 // implemented - stackable accurary modifer //#define SE_Luck_Amount 519 // //#define SE_Luck_Percent 520 // -//#define SE_Endurance_Absorb_Pct_Damage 521 // -//#define SE_Instant_Mana_Pct 522 // -//#define SE_Instant_Endurance_Pct 523 // -//#define SE_Duration_HP_Pct 524 // -//#define SE_Duration_Mana_Pct 525 // -//#define SE_Duration_Endurance_Pct 526 // +#define SE_Endurance_Absorb_Pct_Damage 521 // implemented - Reduces % of Damage using Endurance, drains endurance at a ratio (ie. 0.05 Endurance per Hit Point) +#define SE_Instant_Mana_Pct 522 // implemented - Increase/Decrease mana by percent of max mana +#define SE_Instant_Endurance_Pct 523 // implemented - Increase/Decrease mana by percent of max endurance +#define SE_Duration_HP_Pct 524 // implemented - Decrease Current Hit Points by % of Total Hit Points per Tick, up to a MAX per tick +#define SE_Duration_Mana_Pct 525 // implemented - Decrease Current Mana by % of Total Mana per Tick, up to a MAX per tick +#define SE_Duration_Endurance_Pct 526 // implemented - Decrease Current Endurance by % of Total Hit Points per Tick, up to a MAX per tick // LAST diff --git a/zone/attack.cpp b/zone/attack.cpp index d7450c341..303d46fe5 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -188,6 +188,11 @@ int Mob::GetTotalToHit(EQ::skills::SkillType skill, int chance_mod) if (skill != EQ::skills::SkillArchery && skill != EQ::skills::SkillThrowing) accuracy += itembonuses.HitChance; + //518 Increase ATK accuracy by percentage, stackable + auto atkhit_bonus = itembonuses.Attack_Accuracy_Max_Percent + aabonuses.Attack_Accuracy_Max_Percent + spellbonuses.Attack_Accuracy_Max_Percent; + if (atkhit_bonus) + accuracy += round(static_cast(accuracy) * static_cast(atkhit_bonus) * 0.0001); + // 216 Melee Accuracy Amt aka SE_Accuracy -- flat bonus accuracy += itembonuses.Accuracy[EQ::skills::HIGHEST_SKILL + 1] + aabonuses.Accuracy[EQ::skills::HIGHEST_SKILL + 1] + @@ -233,6 +238,11 @@ int Mob::compute_defense() if (IsClient()) defense += CastToClient()->GetHeroicAGI() / 10; + //516 SE_AC_Mitigation_Max_Percent + auto ac_bonus = itembonuses.AC_Mitigation_Max_Percent + aabonuses.AC_Mitigation_Max_Percent + spellbonuses.AC_Mitigation_Max_Percent; + if (ac_bonus) + defense += round(static_cast(defense) * static_cast(ac_bonus) * 0.0001); + defense += itembonuses.AvoidMeleeChance; // item mod2 if (IsNPC()) defense += CastToNPC()->GetAvoidanceRating(); @@ -255,7 +265,12 @@ int Mob::GetTotalDefense() auto evasion_bonus = spellbonuses.AvoidMeleeChanceEffect; // we check this first since it has a special case if (evasion_bonus >= 10000) return -1; - // + + // 515 SE_AC_Avoidance_Max_Percent + auto ac_aviodance_bonus = itembonuses.AC_Avoidance_Max_Percent + aabonuses.AC_Avoidance_Max_Percent + spellbonuses.AC_Avoidance_Max_Percent; + if (ac_aviodance_bonus) + avoidance += round(static_cast(avoidance) * static_cast(ac_aviodance_bonus) * 0.0001); + // 172 Evasion aka SE_AvoidMeleeChance evasion_bonus += itembonuses.AvoidMeleeChanceEffect + aabonuses.AvoidMeleeChanceEffect; // item bonus here isn't mod2 avoidance @@ -2887,6 +2902,10 @@ void Mob::DamageShield(Mob* attacker, bool spell_ds) { DS += aabonuses.DamageShield; //Live AA - coat of thistles. (negative value) DS -= itembonuses.DamageShield; //+Damage Shield should only work when you already have a DS spell + DS -= attacker->aabonuses.DS_Mitigation_Amount + attacker->itembonuses.DS_Mitigation_Amount + attacker->spellbonuses.DS_Mitigation_Amount; //Negative value to reduce + //Do not allow flat amount reductions to reduce past 0. + if (DS >= 0) + return; //Spell data for damage shield mitigation shows a negative value for spells for clients and positive //value for spells that effect pets. Unclear as to why. For now will convert all positive to be consistent. @@ -2896,7 +2915,12 @@ void Mob::DamageShield(Mob* attacker, bool spell_ds) { attacker->aabonuses.DSMitigationOffHand; DS -= DS*mitigation / 100; } - DS -= DS * attacker->itembonuses.DSMitigation / 100; + + int ds_mitigation = attacker->itembonuses.DSMitigation; + // Subtract mitigations because DS_Mitigation_Percentage is a negative value when reducing total, thus final value will be positive + ds_mitigation -= attacker->aabonuses.DS_Mitigation_Percentage + attacker->itembonuses.DS_Mitigation_Percentage + attacker->spellbonuses.DS_Mitigation_Percentage; //Negative value to reduce + + DS -= DS * ds_mitigation / 100; } attacker->Damage(this, -DS, spellid, EQ::skills::SkillAbjuration/*hackish*/, false); //we can assume there is a spell now @@ -3323,8 +3347,8 @@ int32 Mob::ReduceAllDamage(int32 damage) if (damage <= 0) return damage; - if (spellbonuses.ManaAbsorbPercentDamage[0]) { - int32 mana_reduced = damage * spellbonuses.ManaAbsorbPercentDamage[0] / 100; + if (spellbonuses.ManaAbsorbPercentDamage) { + int32 mana_reduced = damage * spellbonuses.ManaAbsorbPercentDamage / 100; if (GetMana() >= mana_reduced) { damage -= mana_reduced; SetMana(GetMana() - mana_reduced); @@ -3332,6 +3356,19 @@ int32 Mob::ReduceAllDamage(int32 damage) } } + if (spellbonuses.EnduranceAbsorbPercentDamage[0]) { + int32 damage_reduced = damage * spellbonuses.EnduranceAbsorbPercentDamage[0] / 10000; //If hit for 1000, at 10% then lower damage by 100; + int32 endurance_drain = damage_reduced * spellbonuses.EnduranceAbsorbPercentDamage[1] / 10000; //Reduce endurance by 0.05% per HP loss + if (endurance_drain < 1) + endurance_drain = 1; + + if (IsClient() && CastToClient()->GetEndurance() >= endurance_drain) { + damage -= damage_reduced; + CastToClient()->SetEndurance(CastToClient()->GetEndurance() - endurance_drain); + TryTriggerOnValueAmount(false, false, true); + } + } + CheckNumHitsRemaining(NumHit::IncomingDamage); return(damage); @@ -4628,6 +4665,7 @@ void Mob::ApplyMeleeDamageMods(uint16 skill, int &damage, Mob *defender, ExtraAt int dmgbonusmod = 0; dmgbonusmod += GetMeleeDamageMod_SE(skill); + dmgbonusmod += GetMeleeDmgPositionMod(defender); if (opts) dmgbonusmod += opts->melee_damage_bonus_flat; @@ -5241,7 +5279,9 @@ void Mob::CommonOutgoingHitSuccess(Mob* defender, DamageHitInfo &hit, ExtraAttac if (spec_mod > 0) hit.damage_done = (hit.damage_done * spec_mod) / 100; - hit.damage_done += (hit.damage_done * defender->GetSkillDmgTaken(hit.skill, opts) / 100) + (defender->GetFcDamageAmtIncoming(this, 0, true, hit.skill)); + int pct_damage_reduction = defender->GetSkillDmgTaken(hit.skill, opts) + defender->GetPositionalDmgTaken(this); + + hit.damage_done += (hit.damage_done * pct_damage_reduction / 100) + (defender->GetFcDamageAmtIncoming(this, 0, true, hit.skill)); CheckNumHitsRemaining(NumHit::OutgoingHitSuccess); } diff --git a/zone/bonuses.cpp b/zone/bonuses.cpp index e6c99f154..bc8075275 100644 --- a/zone/bonuses.cpp +++ b/zone/bonuses.cpp @@ -978,6 +978,9 @@ void Mob::ApplyAABonuses(const AA::Rank &rank, StatBonuses *newbon) case SE_FrontalBackstabChance: newbon->FrontalBackstabChance += base1; break; + case SE_Double_Backstab_Front: + newbon->Double_Backstab_Front += base1; + break; case SE_BlockBehind: newbon->BlockBehind += base1; break; @@ -1102,6 +1105,19 @@ void Mob::ApplyAABonuses(const AA::Rank &rank, StatBonuses *newbon) break; } + case SE_Critical_Melee_Damage_Mod_Max: + { + // Bad data or unsupported new skill + if (base2 > EQ::skills::HIGHEST_SKILL) + break; + int skill = base2 == ALL_SKILLS ? EQ::skills::HIGHEST_SKILL + 1 : base2; + if (base1 < 0 && newbon->CritDmgModNoStack[skill] > base1) + newbon->CritDmgModNoStack[skill] = base1; + else if (base1 > 0 && newbon->CritDmgModNoStack[skill] < base1) + newbon->CritDmgModNoStack[skill] = base1; + break; + } + case SE_CriticalSpellChance: { newbon->CriticalSpellChance += base1; @@ -1487,6 +1503,50 @@ void Mob::ApplyAABonuses(const AA::Rank &rank, StatBonuses *newbon) } break; + case SE_Attack_Accuracy_Max_Percent: + newbon->Attack_Accuracy_Max_Percent += base1; + break; + + case SE_AC_Mitigation_Max_Percent: + newbon->AC_Mitigation_Max_Percent += base1; + break; + + case SE_AC_Avoidance_Max_Percent: + newbon->AC_Avoidance_Max_Percent += base1; + break; + + case SE_Damage_Taken_Position_Mod: + { + //Mitigate if damage taken from behind base2 = 0, from front base2 = 1 + if (base2 < 0 || base2 > 2) + break; + else if (base1 < 0 && newbon->Damage_Taken_Position_Mod[base2] > base1) + newbon->Damage_Taken_Position_Mod[base2] = base1; + else if (base1 > 0 && newbon->Damage_Taken_Position_Mod[base2] < base1) + newbon->Damage_Taken_Position_Mod[base2] = base1; + break; + } + + case SE_Melee_Damage_Position_Mod: + { + if (base2 < 0 || base2 > 2) + break; + else if (base1 < 0 && newbon->Melee_Damage_Position_Mod[base2] > base1) + newbon->Melee_Damage_Position_Mod[base2] = base1; + else if (base1 > 0 && newbon->Melee_Damage_Position_Mod[base2] < base1) + newbon->Melee_Damage_Position_Mod[base2] = base1; + break; + } + + case SE_DS_Mitigation_Amount: + newbon->DS_Mitigation_Amount += base1; + break; + + case SE_DS_Mitigation_Percentage: + newbon->DS_Mitigation_Percentage += base1; + break; + + // to do case SE_PetDiscipline: break; @@ -2485,6 +2545,20 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne break; } + case SE_Critical_Melee_Damage_Mod_Max: + { + // Bad data or unsupported new skill + if (base2 > EQ::skills::HIGHEST_SKILL) + break; + int skill = base2 == ALL_SKILLS ? EQ::skills::HIGHEST_SKILL + 1 : base2; + if (effect_value < 0 && new_bonus->CritDmgModNoStack[skill] > effect_value) + new_bonus->CritDmgModNoStack[skill] = effect_value; + else if (effect_value > 0 && new_bonus->CritDmgModNoStack[skill] < effect_value) { + new_bonus->CritDmgModNoStack[skill] = effect_value; + } + break; + } + case SE_ReduceSkillTimer: { if(new_bonus->SkillReuseTime[base2] < effect_value) @@ -2690,9 +2764,17 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne case SE_ManaAbsorbPercentDamage: { - if (new_bonus->ManaAbsorbPercentDamage[0] < effect_value){ - new_bonus->ManaAbsorbPercentDamage[0] = effect_value; - new_bonus->ManaAbsorbPercentDamage[1] = buffslot; + if (new_bonus->ManaAbsorbPercentDamage < effect_value){ + new_bonus->ManaAbsorbPercentDamage = effect_value; + } + break; + } + + case SE_Endurance_Absorb_Pct_Damage: + { + if (new_bonus->EnduranceAbsorbPercentDamage[0] < effect_value) { + new_bonus->EnduranceAbsorbPercentDamage[0] = effect_value; + new_bonus->EnduranceAbsorbPercentDamage[1] = base2; } break; } @@ -2762,6 +2844,10 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne new_bonus->FrontalBackstabChance += effect_value; break; + case SE_Double_Backstab_Front: + new_bonus->Double_Backstab_Front += effect_value; + break; + case SE_ConsumeProjectile: new_bonus->ConsumeProjectile += effect_value; break; @@ -3250,6 +3336,57 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne if (new_bonus->trap_slots < effect_value) new_bonus->trap_slots = effect_value; break; + + case SE_Attack_Accuracy_Max_Percent: + new_bonus->Attack_Accuracy_Max_Percent += effect_value; + break; + + + case SE_AC_Mitigation_Max_Percent: + new_bonus->AC_Mitigation_Max_Percent += effect_value; + break; + + case SE_AC_Avoidance_Max_Percent: + new_bonus->AC_Avoidance_Max_Percent += effect_value; + break; + + case SE_Damage_Taken_Position_Mod: + { + //Mitigate if damage taken from behind base2 = 0, from front base2 = 1 + if (base2 < 0 || base2 > 2) + break; + if (AdditiveWornBonus) + new_bonus->Damage_Taken_Position_Mod[base2] += effect_value; + else if (effect_value < 0 && new_bonus->Damage_Taken_Position_Mod[base2] > effect_value) + new_bonus->Damage_Taken_Position_Mod[base2] = effect_value; + else if (effect_value > 0 && new_bonus->Damage_Taken_Position_Mod[base2] < effect_value) + new_bonus->Damage_Taken_Position_Mod[base2] = effect_value; + break; + } + + case SE_Melee_Damage_Position_Mod: + { + //Increase damage by percent from behind base2 = 0, from front base2 = 1 + if (base2 < 0 || base2 > 2) + break; + if (AdditiveWornBonus) + new_bonus->Melee_Damage_Position_Mod[base2] += effect_value; + else if (effect_value < 0 && new_bonus->Melee_Damage_Position_Mod[base2] > effect_value) + new_bonus->Melee_Damage_Position_Mod[base2] = effect_value; + else if (effect_value > 0 && new_bonus->Melee_Damage_Position_Mod[base2] < effect_value) + new_bonus->Melee_Damage_Position_Mod[base2] = effect_value; + break; + } + + case SE_DS_Mitigation_Amount: + new_bonus->DS_Mitigation_Amount += effect_value; + break; + + case SE_DS_Mitigation_Percentage: + new_bonus->DS_Mitigation_Percentage += effect_value; + break; + + //Special custom cases for loading effects on to NPC from 'npc_spels_effects' table if (IsAISpellEffect) { @@ -4258,6 +4395,17 @@ void Mob::NegateSpellsBonuses(uint16 spell_id) break; } + case SE_Critical_Melee_Damage_Mod_Max: + { + for (int e = 0; e < EQ::skills::HIGHEST_SKILL + 1; e++) + { + spellbonuses.CritDmgModNoStack[e] = effect_value; + aabonuses.CritDmgModNoStack[e] = effect_value; + itembonuses.CritDmgModNoStack[e] = effect_value; + } + break; + } + case SE_SkillDamageAmount: { for (int e = 0; e < EQ::skills::HIGHEST_SKILL + 1; e++) @@ -4353,8 +4501,12 @@ void Mob::NegateSpellsBonuses(uint16 spell_id) break; case SE_ManaAbsorbPercentDamage: - spellbonuses.ManaAbsorbPercentDamage[0] = effect_value; - spellbonuses.ManaAbsorbPercentDamage[1] = -1; + spellbonuses.ManaAbsorbPercentDamage = effect_value; + break; + + case SE_Endurance_Absorb_Pct_Damage: + spellbonuses.EnduranceAbsorbPercentDamage[0] = effect_value; + spellbonuses.EnduranceAbsorbPercentDamage[1] = effect_value; break; case SE_ShieldBlock: @@ -4428,6 +4580,12 @@ void Mob::NegateSpellsBonuses(uint16 spell_id) itembonuses.FrontalBackstabChance = effect_value; break; + case SE_Double_Backstab_Front: + spellbonuses.Double_Backstab_Front = effect_value; + aabonuses.Double_Backstab_Front = effect_value; + itembonuses.Double_Backstab_Front = effect_value; + break; + case SE_ConsumeProjectile: spellbonuses.ConsumeProjectile = effect_value; aabonuses.ConsumeProjectile = effect_value; @@ -4770,6 +4928,56 @@ void Mob::NegateSpellsBonuses(uint16 spell_id) aabonuses.IllusionPersistence = false; break; + case SE_Attack_Accuracy_Max_Percent: + spellbonuses.Attack_Accuracy_Max_Percent = effect_value; + itembonuses.Attack_Accuracy_Max_Percent = effect_value; + aabonuses.Attack_Accuracy_Max_Percent = effect_value; + break; + + + case SE_AC_Mitigation_Max_Percent: + spellbonuses.AC_Mitigation_Max_Percent = effect_value; + itembonuses.AC_Mitigation_Max_Percent = effect_value; + aabonuses.AC_Mitigation_Max_Percent = effect_value; + break; + + case SE_AC_Avoidance_Max_Percent: + spellbonuses.AC_Avoidance_Max_Percent = effect_value; + itembonuses.AC_Avoidance_Max_Percent = effect_value; + aabonuses.AC_Avoidance_Max_Percent = effect_value; + break; + + case SE_Melee_Damage_Position_Mod: + spellbonuses.Melee_Damage_Position_Mod[0] = effect_value; + aabonuses.Melee_Damage_Position_Mod[0] = effect_value; + itembonuses.Melee_Damage_Position_Mod[0] = effect_value; + spellbonuses.Melee_Damage_Position_Mod[1] = effect_value; + aabonuses.Melee_Damage_Position_Mod[1] = effect_value; + itembonuses.Melee_Damage_Position_Mod[1] = effect_value; + break; + + case SE_Damage_Taken_Position_Mod: + spellbonuses.Damage_Taken_Position_Mod[0] = effect_value; + aabonuses.Damage_Taken_Position_Mod[0] = effect_value; + itembonuses.Damage_Taken_Position_Mod[0] = effect_value; + spellbonuses.Damage_Taken_Position_Mod[1] = effect_value; + aabonuses.Damage_Taken_Position_Mod[1] = effect_value; + itembonuses.Damage_Taken_Position_Mod[1] = effect_value; + break; + + + case SE_DS_Mitigation_Amount: + spellbonuses.DS_Mitigation_Amount = effect_value; + itembonuses.DS_Mitigation_Amount = effect_value; + aabonuses.DS_Mitigation_Amount = effect_value; + break; + + case SE_DS_Mitigation_Percentage: + spellbonuses.DS_Mitigation_Percentage = effect_value; + itembonuses.DS_Mitigation_Percentage = effect_value; + aabonuses.DS_Mitigation_Percentage = effect_value; + break; + case SE_SkillProcSuccess:{ for(int e = 0; e < MAX_SKILL_PROCS; e++) { diff --git a/zone/common.h b/zone/common.h index fd513bdde..01c074b11 100644 --- a/zone/common.h +++ b/zone/common.h @@ -461,6 +461,7 @@ struct StatBonuses { uint32 SpellOnKill[MAX_SPELL_TRIGGER*3]; // Chance to proc after killing a mob uint32 SpellOnDeath[MAX_SPELL_TRIGGER*2]; // Chance to have effect cast when you die int32 CritDmgMod[EQ::skills::HIGHEST_SKILL + 2]; // All Skills + -1 + int32 CritDmgModNoStack[EQ::skills::HIGHEST_SKILL + 2];// Critical melee damage modifier by percent, does not stack. int32 SkillReuseTime[EQ::skills::HIGHEST_SKILL + 1]; // Reduces skill timers int32 SkillDamageAmount[EQ::skills::HIGHEST_SKILL + 2]; // All Skills + -1 int32 TwoHandBluntBlock; // chance to block when wielding two hand blunt weapon @@ -494,7 +495,8 @@ struct StatBonuses { uint32 MitigateDotRune[4]; // 0 = Mitigation value 1 = Buff Slot 2 = Max mitigation per tick 3 = Rune Amt bool TriggerMeleeThreshold; // Has Melee Threshhold bool TriggerSpellThreshold; // Has Spell Threshhold - uint32 ManaAbsorbPercentDamage[2]; // 0 = Mitigation value 1 = Buff Slot + uint32 ManaAbsorbPercentDamage; // 0 = Mitigation value + int32 EnduranceAbsorbPercentDamage[2]; // 0 = Mitigation value 1 = Percent Endurance drain per HP lost int32 ShieldBlock; // Chance to Shield Block int32 BlockBehind; // Chance to Block Behind (with our without shield) bool CriticalRegenDecay; // increase critical regen chance, decays based on spell level cast @@ -523,6 +525,14 @@ struct StatBonuses { uint32 SkillProcSuccess[MAX_SKILL_PROCS]; // Max number of spells containing skill_procs_success. uint32 PC_Pet_Rampage[2]; // 0= % chance to rampage, 1=damage modifier uint32 PC_Pet_Flurry; // Percent chance flurry from double attack + int32 Attack_Accuracy_Max_Percent; // Increase ATK accuracy by percent. + int32 AC_Mitigation_Max_Percent; // Increase AC mitigation by percent + int32 AC_Avoidance_Max_Percent; // Increase AC avoidance by percent + int32 Damage_Taken_Position_Mod[2]; // base = percent melee damage reduction base2 0=back 1=front. [0]Back[1]Front + int32 Melee_Damage_Position_Mod[2]; // base = percent melee damage increase base2 0=back 1=front. [0]Back[1]Front + int32 Double_Backstab_Front; // base = percent chance to double back stab front + int32 DS_Mitigation_Amount; // base = flat amt DS mitigation. Negative value to reduce + int32 DS_Mitigation_Percentage; // base = percent amt of DS mitigation. Negative value to reduce // AAs int8 Packrat; //weight reduction for items, 1 point = 10% diff --git a/zone/lua_stat_bonuses.cpp b/zone/lua_stat_bonuses.cpp index d311cf123..b6561199b 100644 --- a/zone/lua_stat_bonuses.cpp +++ b/zone/lua_stat_bonuses.cpp @@ -1162,7 +1162,7 @@ uint32 Lua_StatBonuses::GetMitigateDotRune(int idx) const { uint32 Lua_StatBonuses::GetManaAbsorbPercentDamage(int idx) const { Lua_Safe_Call_Int(); - return self->ManaAbsorbPercentDamage[idx]; + return self->ManaAbsorbPercentDamage; } int32 Lua_StatBonuses::GetImprovedTaunt(int idx) const { diff --git a/zone/mob.cpp b/zone/mob.cpp index dd1ad6475..46d9447f9 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -3711,6 +3711,32 @@ void Mob::TryTwincast(Mob *caster, Mob *target, uint32 spell_id) } } +//Used for effects that should occur after the completion of the spell +void Mob::TryOnSpellFinished(Mob *caster, Mob *target, uint16 spell_id) +{ + if (!IsValidSpell(spell_id)) + return; + + /*Apply damage from Lifeburn type effects on caster at end of spell cast. + This allows for the AE spells to function without repeatedly killing caster + Damage or heal portion can be found as regular single use spell effect + */ + if (IsEffectInSpell(spell_id, SE_Health_Transfer)){ + for (int i = 0; i < EFFECT_COUNT; i++) { + + if (spells[spell_id].effectid[i] == SE_Health_Transfer) { + int new_hp = GetMaxHP(); + new_hp -= GetMaxHP() * spells[spell_id].base[i] / 1000; + + if (new_hp > 0) + SetHP(new_hp); + else + Kill(); + } + } + } +} + int32 Mob::GetVulnerability(Mob* caster, uint32 spell_id, uint32 ticsremaining) { if (!IsValidSpell(spell_id)) @@ -3771,7 +3797,7 @@ int32 Mob::GetVulnerability(Mob* caster, uint32 spell_id, uint32 ticsremaining) return value; } -int16 Mob::GetSkillDmgTaken(const EQ::skills::SkillType skill_used, ExtraAttackOptions *opts) +int32 Mob::GetSkillDmgTaken(const EQ::skills::SkillType skill_used, ExtraAttackOptions *opts) { int skilldmg_mod = 0; @@ -3790,6 +3816,33 @@ int16 Mob::GetSkillDmgTaken(const EQ::skills::SkillType skill_used, ExtraAttackO return skilldmg_mod; } +int32 Mob::GetPositionalDmgTaken(Mob *attacker) +{ + if (!attacker) + return 0; + + int front_arc = 0; + int back_arc = 0; + int total_mod = 0; + + back_arc += itembonuses.Damage_Taken_Position_Mod[0] + aabonuses.Damage_Taken_Position_Mod[0] + spellbonuses.Damage_Taken_Position_Mod[0]; + front_arc += itembonuses.Damage_Taken_Position_Mod[1] + aabonuses.Damage_Taken_Position_Mod[1] + spellbonuses.Damage_Taken_Position_Mod[1]; + + if (back_arc || front_arc) { //Do they have this bonus? + if (attacker->BehindMob(this, attacker->GetX(), attacker->GetY()))//Check if attacker is striking from behind + total_mod = back_arc; //If so, apply the back arc modifier only + else + total_mod = front_arc;//If not, apply the front arc modifer only + } + + total_mod = round(static_cast(total_mod) * 0.1); + + if (total_mod < -100) + total_mod = -100; + + return total_mod; +} + int16 Mob::GetHealRate(uint16 spell_id, Mob* caster) { int16 heal_rate = 0; @@ -4598,10 +4651,13 @@ int16 Mob::GetCritDmgMod(uint16 skill) { int critDmg_mod = 0; - // All skill dmg mod + Skill specific + // All skill dmg mod + Skill specific [SPA 330 and 496] critDmg_mod += itembonuses.CritDmgMod[EQ::skills::HIGHEST_SKILL + 1] + spellbonuses.CritDmgMod[EQ::skills::HIGHEST_SKILL + 1] + aabonuses.CritDmgMod[EQ::skills::HIGHEST_SKILL + 1] + itembonuses.CritDmgMod[skill] + spellbonuses.CritDmgMod[skill] + aabonuses.CritDmgMod[skill]; + critDmg_mod += itembonuses.CritDmgModNoStack[EQ::skills::HIGHEST_SKILL + 1] + spellbonuses.CritDmgModNoStack[EQ::skills::HIGHEST_SKILL + 1] + aabonuses.CritDmgModNoStack[EQ::skills::HIGHEST_SKILL + 1] + + itembonuses.CritDmgModNoStack[skill] + spellbonuses.CritDmgModNoStack[skill] + aabonuses.CritDmgModNoStack[skill]; + return critDmg_mod; } @@ -4692,6 +4748,35 @@ int16 Mob::GetCrippBlowChance() return crip_chance; } + +int16 Mob::GetMeleeDmgPositionMod(Mob* defender) +{ + if (!defender) + return 0; + + int front_arc = 0; + int back_arc = 0; + int total_mod = 0; + + back_arc += itembonuses.Melee_Damage_Position_Mod[0] + aabonuses.Melee_Damage_Position_Mod[0] + spellbonuses.Melee_Damage_Position_Mod[0]; + front_arc += itembonuses.Melee_Damage_Position_Mod[1] + aabonuses.Melee_Damage_Position_Mod[1] + spellbonuses.Melee_Damage_Position_Mod[1]; + + if (back_arc || front_arc) { //Do they have this bonus? + if (BehindMob(defender, GetX(), GetY()))//Check if attacker is striking from behind + total_mod = back_arc; //If so, apply the back arc modifier only + else + total_mod = front_arc;//If not, apply the front arc modifer only + } + + total_mod = round(static_cast(total_mod) * 0.1); + + if (total_mod < -100) + total_mod = -100; + + return total_mod; + +} + int16 Mob::GetSkillReuseTime(uint16 skill) { int skill_reduction = this->itembonuses.SkillReuseTime[skill] + this->spellbonuses.SkillReuseTime[skill] + this->aabonuses.SkillReuseTime[skill]; diff --git a/zone/mob.h b/zone/mob.h index ea5723fa6..c0ce7bf5a 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -353,6 +353,7 @@ public: void CalcDestFromHeading(float heading, float distance, float MaxZDiff, float StartX, float StartY, float &dX, float &dY, float &dZ); void BeamDirectional(uint16 spell_id, int16 resist_adjust); void ConeDirectional(uint16 spell_id, int16 resist_adjust); + void TryOnSpellFinished(Mob *caster, Mob *target, uint16 spell_id); //Buff void BuffProcess(); @@ -802,7 +803,8 @@ public: int32 GetVulnerability(Mob* caster, uint32 spell_id, uint32 ticsremaining); int32 GetFcDamageAmtIncoming(Mob *caster, uint32 spell_id, bool use_skill = false, uint16 skill=0); int32 GetFocusIncoming(focusType type, int effect, Mob *caster, uint32 spell_id); - int16 GetSkillDmgTaken(const EQ::skills::SkillType skill_used, ExtraAttackOptions *opts = nullptr); + int32 GetSkillDmgTaken(const EQ::skills::SkillType skill_used, ExtraAttackOptions *opts = nullptr); + int32 GetPositionalDmgTaken(Mob *attacker); void DoKnockback(Mob *caster, uint32 pushback, uint32 pushup); int16 CalcResistChanceBonus(); int16 CalcFearResistChance(); @@ -816,6 +818,7 @@ public: int16 GetMeleeDamageMod_SE(uint16 skill); int16 GetMeleeMinDamageMod_SE(uint16 skill); int16 GetCrippBlowChance(); + int16 GetMeleeDmgPositionMod(Mob* defender); int16 GetSkillReuseTime(uint16 skill); int GetCriticalChanceBonus(uint16 skill); int16 GetSkillDmgAmt(uint16 skill); diff --git a/zone/special_attacks.cpp b/zone/special_attacks.cpp index 717bc4fbf..be3408c81 100644 --- a/zone/special_attacks.cpp +++ b/zone/special_attacks.cpp @@ -585,6 +585,10 @@ void Mob::TryBackstab(Mob *other, int ReuseTime) { if(IsClient()) CastToClient()->CheckIncreaseSkill(EQ::skills::SkillBackstab, other, 10); m_specialattacks = eSpecialAttacks::None; + + int double_bs_front = aabonuses.Double_Backstab_Front + itembonuses.Double_Backstab_Front + spellbonuses.Double_Backstab_Front; + if (double_bs_front && other->GetHP() > 0 && zone->random.Roll(double_bs_front)) + RogueBackstab(other, false, ReuseTime); } else { //We do a single regular attack if we attack from the front without chaotic stab Attack(other, EQ::invslot::slotPrimary); diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index 4fdb926b4..801700bd5 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -275,11 +275,11 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove #endif int32 dmg = effect_value; - if (spell_id == 2751 && caster) //Manaburn + if (spell_id == SPELL_MANA_BURN && caster) //Manaburn { dmg = caster->GetMana()*-3; caster->SetMana(0); - } else if (spell_id == 2755 && caster) //Lifeburn + } else if (spell_id == SPELL_LIFE_BURN && caster) //Lifeburn { dmg = caster->GetHP(); // just your current HP caster->SetHP(dmg / 4); // 2003 patch notes say ~ 1/4 HP. Should this be 1/4 your current HP or do 3/4 max HP dmg? Can it kill you? @@ -303,6 +303,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove break; } + case SE_PercentalHeal: { #ifdef SPELL_EFFECT_SPAM @@ -2828,6 +2829,52 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove break; } + + case SE_Instant_Mana_Pct: { + effect_value = spells[spell_id].base[i]; + int32 amt = abs(GetMaxMana() * effect_value / 10000); + if (spells[spell_id].max[i] && amt > spells[spell_id].max[i]) + amt = spells[spell_id].max[i]; + + if (effect_value < 0) { + SetMana(GetMana() - amt); + } + else { + SetMana(GetMana() + amt); + } + break; + } + + case SE_Instant_Endurance_Pct: { + effect_value = spells[spell_id].base[i]; + if (IsClient()) { + int32 amt = abs(CastToClient()->GetMaxEndurance() * effect_value / 10000); + if (spells[spell_id].max[i] && amt > spells[spell_id].max[i]) + amt = spells[spell_id].max[i]; + + if (effect_value < 0) { + CastToClient()->SetEndurance(CastToClient()->GetEndurance() - amt); + } + else { + CastToClient()->SetEndurance(CastToClient()->GetEndurance() + amt); + } + } + break; + } + /*Calc for base1 is found in TryOnSpellFinished() due to needing to account for AOE functionality + since effect can potentially kill caster*/ + case SE_Health_Transfer: { + effect_value = spells[spell_id].base2[i]; + int32 amt = abs(caster->GetMaxHP() * effect_value / 1000); + + if (effect_value < 0) + Damage(caster, amt, spell_id, spell.skill, false, buffslot, false); + else + HealDamage(amt, caster); + break; + } + + case SE_PersistentEffect: MakeAura(spell_id); break; @@ -3071,6 +3118,19 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove case SE_SkillProc: case SE_SkillProcSuccess: case SE_SpellResistReduction: + case SE_Duration_HP_Pct: + case SE_Duration_Mana_Pct: + case SE_Duration_Endurance_Pct: + case SE_Endurance_Absorb_Pct_Damage: + case SE_AC_Mitigation_Max_Percent: + case SE_AC_Avoidance_Max_Percent: + case SE_Attack_Accuracy_Max_Percent: + case SE_Critical_Melee_Damage_Mod_Max: + case SE_Melee_Damage_Position_Mod: + case SE_Damage_Taken_Position_Mod: + case SE_DS_Mitigation_Amount: + case SE_DS_Mitigation_Percentage: + case SE_Double_Backstab_Front: { break; } @@ -3777,6 +3837,55 @@ void Mob::DoBuffTic(const Buffs_Struct &buff, int slot, Mob *caster) break; } + case SE_Duration_HP_Pct: { + effect_value = spells[buff.spellid].base[i]; + int32 amt = abs(GetMaxHP() * effect_value / 100); + if (spells[buff.spellid].max[i] && amt > spells[buff.spellid].max[i]) + amt = spells[buff.spellid].max[i]; + + if (effect_value < 0) { + Damage(this, amt, 0, EQ::skills::SkillEvocation, false); + } + else { + HealDamage(amt); + } + break; + } + + case SE_Duration_Mana_Pct: { + effect_value = spells[buff.spellid].base[i]; + int32 amt = abs(GetMaxMana() * effect_value / 100); + if (spells[buff.spellid].max[i] && amt > spells[buff.spellid].max[i]) + amt = spells[buff.spellid].max[i]; + + if (effect_value < 0) { + + SetMana(GetMana() - amt); + } + else { + SetMana(GetMana() + amt); + } + break; + } + + case SE_Duration_Endurance_Pct: { + effect_value = spells[buff.spellid].base[i]; + + if (IsClient()) { + int32 amt = abs(CastToClient()->GetMaxEndurance() * effect_value / 100); + if (spells[buff.spellid].max[i] && amt > spells[buff.spellid].max[i]) + amt = spells[buff.spellid].max[i]; + + if (effect_value < 0) { + CastToClient()->SetEndurance(CastToClient()->GetEndurance() - amt); + } + else { + CastToClient()->SetEndurance(CastToClient()->GetEndurance() + amt); + } + } + break; + } + default: { // do we need to do anyting here? } diff --git a/zone/spells.cpp b/zone/spells.cpp index c7029b73d..5e0db7c8a 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -1393,6 +1393,8 @@ void Mob::CastedSpellFinished(uint16 spell_id, uint32 target_id, CastingSlot slo TrySympatheticProc(target, spell_id); } + TryOnSpellFinished(this, target, spell_id); + TryTwincast(this, target, spell_id); TryTriggerOnCast(spell_id, 0);