mirror of
https://github.com/EQEmu/Server.git
synced 2026-06-11 03:31:08 +00:00
[Feature] New SPAs pass 2 (#1459)
* Implemented SPA Duration Pct Implemented new spell effects SE_Duration_HP_Pct 524 SE_Duration_Mana_Pct 525 SE_Duration_Endurance_Pct 526 Consumes 'base1' % of your maximum health/mana/endurance every 6 seconds. 'max' is maximum amount that can be consumed per tic. Additional Functionality Can be used as a heal/gain % by setting the base1 value to a positive. * Implemented SPA Instant Mana/End pct Fixes for SPA 524-526 Implemented SE_Instant_Mana_Pct 522 SE_Instant_Endurance_Pct 523 Extracts 'base1' percent of your maximum mana/endurance, or 'max', whichever is lower. * Implemented: SPA 521 EndAbsorbPctDmg Implemented SE_Endurance_Absorb_Pct_Damage 521 Absorb Damage using Endurance: base1 % (base2 End per 1 HP) Note: Both base1 and base2 need to be divided by 100 for actually value * Implemented SE_HealthTransfer 509 Implemented SE_Health_Transfer 509 'life burn' Consume base2 % of Hit Points to Damage for base % of Hit Points Can be used for heal Act of Valor * Implemented SPA 515,516,518,496 Implemented SE_AC_Avoidance_Max_Percent 515 SE_AC_Mitigation_Max_Percent 516 SE_Attack_Accuracy_Max_Percent 518 Above are stackable defense and offensive mods SE_Critical_Melee_Damage_Mod_Max 496 - This is a non stackable melee critical modifier * Implemented SPA 503 , 505 SE_Melee_Damage_Position_Mod 503 define SE_Damage_Taken_Position_Mod 505 SPA 503 increase/decreases melee damage by percent base1 based on your position base2 0=back 1=front SPA 504 increase/decreases melee damage taken by percent base1 based on your position base2 0=back 1=front * Implemented 467,468 Implemented SE_DS_Mitigation_Amount 467 SE_DS_Mitigation_Percentage 468 Reduce incoming DS by amt or percentage. base1 is value, if a reduction is desired it should be set to negative for both. * Fixes Formula fixes * Update spdat.h Added spa descriptions. * Implemented SPA 469, 470 Implemented SE_Chance_Best_in_Spell_Grp 469 Chance to cast highest scribed spell within a spell group. All base2 spells share roll chance, only 1 cast. SE_Trigger_Best_in_Spell_Grp 470 Chance to cast highest scribed spell within a spell group. Each spell has own chance. Additional Changes: Rewrote TrySpellTrigger function used for SPA 340 since it incorporates SPA 469. Improved code so that chance of spell being triggered should be more accurate statistically. * Implemented SPA 474, 494 Implemented SE_Pet_Crit_Melee_Damage_Pct_Owner 474 - Gives pets a critical melee damage modifier from the owner SE_Pet_Add_Atk 494 - Gives pet a ATK bonus from the owner Fixed SE_PetMeleeMitigation 397 - The bonus was not being calculated * Implemented SPA 465,477,478 Implemented SE_PC_Pet_AE_Rampage 465 Chance for pet to AE rampage with a damage modifier SE_Hatelist_To_Top_Index 477 Chance to be put on top of RAMPAGE list SE_Hatelist_To_Tail_Index 478 Chance to be put on bottom of RAMPAGE list * Implemented Implemented SE_Fearstun 502 Stun with a max level limit. Normal stun restrictions don't apply. Base1 duration, base2 PC duration, max is level limit SE_TwinCastBlocker 39 Previously unused spell effect that is now used on live. Simply, if this effect is present in a spell then the spell can not be twin cast. * Implemented SPA 483 Implemented Fc_Spell_Damage_Pct_IncomingPC 483 - Focus effect that modifies iby percent incoming spell damage on the target. Base1= min Base2= max. Final percent is random between max and min each time focus is applied from a spell cast. Note: Written to stack with similar functioning focus SPA 269 SE_FcSpellVulnerability. * Implemented SPA 484 Implemented SE_Fc_Spell_Damage_Amt_IncomingPC 484 // focus effect that modifies incoming spell damage by flat amount. Consider it a debuff that adds damage to incoming spells. Positive value to add additional damage. * Implemented SPA 481, 485,486,512 Implemented SE_Fc_Cast_Spell_On_Land 481 Focus effect that is checked when a spell is cast on a target, if target has this focus effect and all limiting criteria are met, then the target will cast a spell as specified by the focus. Can be given a roll chance for success. Base1=Chance, Base2=Spellid Note: This spell has a huge amount of potential applications. See 'Alliance' type spells on live. (ie live spell 50247) Implemented associated focus limits seen in live spells. SE_Ff_CasterClass 485 - Caster of spell on target with a focus effect that is checked by incoming spells must be specified class or classes. SE_Ff_Same_Caster 486 -Caster of spell on target with a focus effect that is checked by incoming spells 0=Must be different caster 1=Must be same caster The following is an associated effect seen with SPA 481 SE_Proc_Timer_Modifier 512 This provides a way to rate limit the amount of spell triggers generated by SPA 481. For example after 1 successful spell trigger no additional spells can be triggered for 1.5 seconds. Ie. Base=1 and Base2 1500. Written in a flexible format to allow scaling of multiple different buffs with this effect at same time. * Stacking fixes for new effects Stacking fixes for new effects. * merge with upstream master merge and update up spdat.h * Update spdat.h * Fix for bolt spell targeting self if target zone/died while casting. Fix for bolt spell targeting self if target zone/died while casting. Despite the name being "ST_TargetOptional", this target type is reserved for projectile spells which all require a target, thus should be treated like any other targeted spell.
This commit is contained in:
+339
-26
@@ -197,7 +197,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove
|
||||
if (!IsPowerDistModSpell(spell_id))
|
||||
SetSpellPowerDistanceMod(0);
|
||||
|
||||
bool SE_SpellTrigger_HasCast = false;
|
||||
bool spell_trigger_cast_complete = false; //Used with SE_Spell_Trigger and SE_Chance_Best_in_Spell_Grp, true when spell has been triggered.
|
||||
|
||||
// if buff slot, use instrument mod there, otherwise calc it
|
||||
uint32 instrument_mod = buffslot > -1 ? buffs[buffslot].instrument_mod : caster ? caster->GetInstrumentMod(spell_id) : 10;
|
||||
@@ -2822,9 +2822,9 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove
|
||||
|
||||
case SE_SpellTrigger: {
|
||||
|
||||
if (!SE_SpellTrigger_HasCast) {
|
||||
if (!spell_trigger_cast_complete) {
|
||||
if (caster && caster->TrySpellTrigger(this, spell_id, i))
|
||||
SE_SpellTrigger_HasCast = true;
|
||||
spell_trigger_cast_complete = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -2873,8 +2873,83 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove
|
||||
HealDamage(amt, caster);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
case SE_Chance_Best_in_Spell_Grp: {
|
||||
if (!spell_trigger_cast_complete) {
|
||||
if (caster && caster->TrySpellTrigger(this, spell_id, i))
|
||||
spell_trigger_cast_complete = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case SE_Trigger_Best_in_Spell_Grp: {
|
||||
|
||||
if (caster && !caster->IsClient())
|
||||
break;
|
||||
|
||||
if (zone->random.Roll(spells[spell_id].base[i])) {
|
||||
uint32 best_spell_id = caster->CastToClient()->GetHighestScribedSpellinSpellGroup(spells[spell_id].base2[i]);
|
||||
|
||||
if (caster && IsValidSpell(best_spell_id))
|
||||
caster->SpellFinished(best_spell_id, this, EQ::spells::CastingSlot::Item, 0, -1, spells[best_spell_id].ResistDiff);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case SE_Trigger_Spell_Non_Item: {
|
||||
//Only trigger if not from item
|
||||
if (caster && caster->IsClient() && GetCastedSpellInvSlot() > 0)
|
||||
break;
|
||||
|
||||
if (zone->random.Roll(spells[spell_id].base[i]) && IsValidSpell(spells[spell_id].base2[i]))
|
||||
caster->SpellFinished(spells[spell_id].base2[i], this, EQ::spells::CastingSlot::Item, 0, -1, spells[spells[spell_id].base2[i]].ResistDiff);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case SE_Hatelist_To_Tail_Index: {
|
||||
if (caster && zone->random.Roll(spells[spell_id].base[i]))
|
||||
caster->SetBottomRampageList();
|
||||
break;
|
||||
}
|
||||
|
||||
case SE_Hatelist_To_Top_Index: {
|
||||
if (caster && zone->random.Roll(spells[spell_id].base[i]))
|
||||
caster->SetTopRampageList();
|
||||
break;
|
||||
}
|
||||
|
||||
case SE_Fearstun: {
|
||||
//Normal 'stun' restrictions do not apply. base1=duration, base2=PC duration, max =lv restrict
|
||||
if (!caster)
|
||||
break;
|
||||
|
||||
if (IsNPC() && GetSpecialAbility(UNSTUNABLE)) {
|
||||
caster->MessageString(Chat::SpellFailure, IMMUNE_STUN);
|
||||
break;
|
||||
}
|
||||
|
||||
if (IsNPC() && GetSpecialAbility(UNFEARABLE)) {
|
||||
caster->MessageString(Chat::SpellFailure, IMMUNE_FEAR);
|
||||
break;
|
||||
}
|
||||
|
||||
if (spells[spell_id].max[i] == 0 || GetLevel() <= spells[spell_id].max[i]) {
|
||||
if (IsClient() && spells[spell_id].base2[i])
|
||||
Stun(spells[spell_id].base2[i]);
|
||||
else
|
||||
Stun(spells[spell_id].base[i]);
|
||||
}
|
||||
else
|
||||
caster->MessageString(Chat::SpellFailure, FEAR_TOO_HIGH);
|
||||
break;
|
||||
}
|
||||
|
||||
case SE_Proc_Timer_Modifier:{
|
||||
buffs[buffslot].focusproclimit_procamt = spells[spell_id].base[i]; //Set max amount of procs before lockout timer
|
||||
break;
|
||||
}
|
||||
|
||||
case SE_PersistentEffect:
|
||||
MakeAura(spell_id);
|
||||
break;
|
||||
@@ -2990,6 +3065,8 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove
|
||||
case SE_HealRate:
|
||||
case SE_SkillDamageTaken:
|
||||
case SE_FcSpellVulnerability:
|
||||
case SE_Fc_Spell_Damage_Pct_IncomingPC:
|
||||
case SE_Fc_Spell_Damage_Amt_IncomingPC:
|
||||
case SE_FcTwincast:
|
||||
case SE_DelayDeath:
|
||||
case SE_CastOnFadeEffect:
|
||||
@@ -3131,6 +3208,12 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove
|
||||
case SE_DS_Mitigation_Amount:
|
||||
case SE_DS_Mitigation_Percentage:
|
||||
case SE_Double_Backstab_Front:
|
||||
case SE_Pet_Crit_Melee_Damage_Pct_Owner:
|
||||
case SE_Pet_Add_Atk:
|
||||
case SE_TwinCastBlocker:
|
||||
case SE_Fc_Cast_Spell_On_Land:
|
||||
case SE_Ff_CasterClass:
|
||||
case SE_Ff_Same_Caster:
|
||||
{
|
||||
break;
|
||||
}
|
||||
@@ -4406,13 +4489,14 @@ int16 Client::CalcAAFocus(focusType type, const AA::Rank &rank, uint16 spell_id)
|
||||
case SE_Blank:
|
||||
break;
|
||||
|
||||
// Handle Focus Limits
|
||||
// Handle Focus Limits
|
||||
|
||||
case SE_LimitResist:
|
||||
if (base1 < 0) {
|
||||
if (spell.resisttype == -base1) // Exclude
|
||||
LimitFailure = true;
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
LimitInclude[0] = true;
|
||||
if (spell.resisttype == base1) // Include
|
||||
LimitInclude[1] = true;
|
||||
@@ -4433,12 +4517,13 @@ int16 Client::CalcAAFocus(focusType type, const AA::Rank &rank, uint16 spell_id)
|
||||
// every level over cap reduces the effect by base2 percent unless from a clicky when
|
||||
// ItemCastsUseFocus is true
|
||||
if (lvldiff > 0 && (spell_level <= RuleI(Character, MaxLevel) ||
|
||||
RuleB(Character, ItemCastsUseFocus) == false)) {
|
||||
RuleB(Character, ItemCastsUseFocus) == false)) {
|
||||
if (base2 > 0) {
|
||||
lvlModifier -= base2 * lvldiff;
|
||||
if (lvlModifier < 1)
|
||||
LimitFailure = true;
|
||||
} else
|
||||
}
|
||||
else
|
||||
LimitFailure = true;
|
||||
}
|
||||
break;
|
||||
@@ -4462,7 +4547,8 @@ int16 Client::CalcAAFocus(focusType type, const AA::Rank &rank, uint16 spell_id)
|
||||
if (base1 < 0) { // Exclude
|
||||
if (spell_id == -base1)
|
||||
LimitFailure = true;
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
LimitInclude[2] = true;
|
||||
if (spell_id == base1) // Include
|
||||
LimitInclude[3] = true;
|
||||
@@ -4479,13 +4565,15 @@ int16 Client::CalcAAFocus(focusType type, const AA::Rank &rank, uint16 spell_id)
|
||||
if (base1 < 0) {
|
||||
if (IsEffectInSpell(spell_id, -base1)) // Exclude
|
||||
LimitFailure = true;
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
LimitInclude[4] = true;
|
||||
// they use 33 here for all classes ... unsure if the type check is really needed
|
||||
if (base1 == SE_SummonPet && type == focusReagentCost) {
|
||||
if (IsSummonPetSpell(spell_id) || IsSummonSkeletonSpell(spell_id))
|
||||
LimitInclude[5] = true;
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
if (IsEffectInSpell(spell_id, base1)) // Include
|
||||
LimitInclude[5] = true;
|
||||
}
|
||||
@@ -4519,7 +4607,8 @@ int16 Client::CalcAAFocus(focusType type, const AA::Rank &rank, uint16 spell_id)
|
||||
if (base1 < 0) {
|
||||
if (-base1 == spell.targettype) // Exclude
|
||||
LimitFailure = true;
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
LimitInclude[6] = true;
|
||||
if (base1 == spell.targettype) // Include
|
||||
LimitInclude[7] = true;
|
||||
@@ -4538,7 +4627,8 @@ int16 Client::CalcAAFocus(focusType type, const AA::Rank &rank, uint16 spell_id)
|
||||
if (base1 < 0) {
|
||||
if (-base1 == spell.spellgroup) // Exclude
|
||||
LimitFailure = true;
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
LimitInclude[8] = true;
|
||||
if (base1 == spell.spellgroup) // Include
|
||||
LimitInclude[9] = true;
|
||||
@@ -4549,7 +4639,8 @@ int16 Client::CalcAAFocus(focusType type, const AA::Rank &rank, uint16 spell_id)
|
||||
if (base1 < 0) {
|
||||
if (-base1 == spell.skill)
|
||||
LimitFailure = true;
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
LimitInclude[10] = true;
|
||||
if (base1 == spell.skill)
|
||||
LimitInclude[11] = true;
|
||||
@@ -4560,7 +4651,8 @@ int16 Client::CalcAAFocus(focusType type, const AA::Rank &rank, uint16 spell_id)
|
||||
if (base1 < 0) { // Exclude
|
||||
if (CheckSpellCategory(spell_id, base1, SE_LimitSpellClass))
|
||||
return (0);
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
LimitInclude[12] = true;
|
||||
if (CheckSpellCategory(spell_id, base1, SE_LimitSpellClass)) // Include
|
||||
LimitInclude[13] = true;
|
||||
@@ -4571,7 +4663,8 @@ int16 Client::CalcAAFocus(focusType type, const AA::Rank &rank, uint16 spell_id)
|
||||
if (base1 < 0) { // Exclude
|
||||
if (CheckSpellCategory(spell_id, base1, SE_LimitSpellSubclass))
|
||||
return (0);
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
LimitInclude[14] = true;
|
||||
if (CheckSpellCategory(spell_id, base1, SE_LimitSpellSubclass)) // Include
|
||||
LimitInclude[15] = true;
|
||||
@@ -4600,7 +4693,12 @@ int16 Client::CalcAAFocus(focusType type, const AA::Rank &rank, uint16 spell_id)
|
||||
LimitFailure = true;
|
||||
break;
|
||||
|
||||
// Handle Focus Effects
|
||||
/* These are not applicable to AA's because there is never a 'caster' of the 'buff' with the focus effect.
|
||||
case SE_Ff_Same_Caster:
|
||||
case SE_Ff_CasterClass:
|
||||
*/
|
||||
|
||||
// Handle Focus Effects
|
||||
case SE_ImprovedDamage:
|
||||
if (type == focusImprovedDamage && base1 > value)
|
||||
value = base1;
|
||||
@@ -4667,11 +4765,13 @@ int16 Client::CalcAAFocus(focusType type, const AA::Rank &rank, uint16 spell_id)
|
||||
if (value > 0) {
|
||||
if (base1 > value)
|
||||
value = base1;
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
if (base1 < value)
|
||||
value = base1;
|
||||
}
|
||||
} else
|
||||
}
|
||||
else
|
||||
value = base1;
|
||||
}
|
||||
break;
|
||||
@@ -4685,7 +4785,8 @@ int16 Client::CalcAAFocus(focusType type, const AA::Rank &rank, uint16 spell_id)
|
||||
if (type == focusTriggerOnCast) {
|
||||
if (zone->random.Roll(base1)) {
|
||||
value = base2;
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
value = 0;
|
||||
LimitFailure = true;
|
||||
}
|
||||
@@ -4697,6 +4798,11 @@ int16 Client::CalcAAFocus(focusType type, const AA::Rank &rank, uint16 spell_id)
|
||||
value = base1;
|
||||
break;
|
||||
|
||||
case SE_Fc_Spell_Damage_Pct_IncomingPC:
|
||||
if (type == focusFcSpellDamagePctIncomingPC)
|
||||
value = base1;
|
||||
break;
|
||||
|
||||
case SE_BlockNextSpellFocus:
|
||||
if (type == focusBlockNextSpell) {
|
||||
if (zone->random.Roll(base1))
|
||||
@@ -4709,7 +4815,7 @@ int16 Client::CalcAAFocus(focusType type, const AA::Rank &rank, uint16 spell_id)
|
||||
value = base1;
|
||||
break;
|
||||
|
||||
// Note if using these as AA, make sure this is first focus used.
|
||||
// Note if using these as AA, make sure this is first focus used.
|
||||
case SE_SympatheticProc:
|
||||
if (type == focusSympatheticProc)
|
||||
value = base2;
|
||||
@@ -4735,6 +4841,11 @@ int16 Client::CalcAAFocus(focusType type, const AA::Rank &rank, uint16 spell_id)
|
||||
value = base1;
|
||||
break;
|
||||
|
||||
case SE_Fc_Spell_Damage_Amt_IncomingPC:
|
||||
if (type == focusFcSpellDamageAmtIncomingPC)
|
||||
value = base1;
|
||||
break;
|
||||
|
||||
case SE_FcHealAmtIncoming:
|
||||
if (type == focusFcHealAmtIncoming)
|
||||
value = base1;
|
||||
@@ -4789,6 +4900,14 @@ int16 Client::CalcAAFocus(focusType type, const AA::Rank &rank, uint16 spell_id)
|
||||
if (type == focusFcStunTimeMod)
|
||||
value = base1;
|
||||
break;
|
||||
|
||||
case SE_Fc_Cast_Spell_On_Land:
|
||||
if (type == focusFcCastSpellOnLand) {
|
||||
if (zone->random.Roll(base1)) {
|
||||
value = base2;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4805,8 +4924,13 @@ int16 Client::CalcAAFocus(focusType type, const AA::Rank &rank, uint16 spell_id)
|
||||
|
||||
//given an item/spell's focus ID and the spell being cast, determine the focus ammount, if any
|
||||
//assumes that spell_id is not a bard spell and that both ids are valid spell ids
|
||||
int16 Mob::CalcFocusEffect(focusType type, uint16 focus_id, uint16 spell_id, bool best_focus)
|
||||
int16 Mob::CalcFocusEffect(focusType type, uint16 focus_id, uint16 spell_id, bool best_focus, uint16 casterid)
|
||||
{
|
||||
/*
|
||||
'this' is always the caster of the spell_id, most foci check for effects on the caster, however some check for effects on the target.
|
||||
'casterid' is the casterid of the caster of spell_id, used when spell_id is cast on a target with a focus effect that is checked by incoming spell.
|
||||
*/
|
||||
|
||||
if (!IsValidSpell(focus_id) || !IsValidSpell(spell_id))
|
||||
return 0;
|
||||
|
||||
@@ -5040,6 +5164,23 @@ int16 Mob::CalcFocusEffect(focusType type, uint16 focus_id, uint16 spell_id, boo
|
||||
}
|
||||
break;
|
||||
|
||||
case SE_Ff_Same_Caster://hmm do i need to pass casterid from buff slot here
|
||||
if (focus_spell.base[i] == 0) {
|
||||
if (casterid == GetID())
|
||||
return 0;//Mob casting is same as target, fail if you are casting on yourself.
|
||||
}
|
||||
else if (focus_spell.base[i] == 1) {
|
||||
if (casterid != GetID())
|
||||
return 0;//Mob casting is not same as target, fail if you are not casting on yourself.
|
||||
}
|
||||
break;
|
||||
|
||||
case SE_Ff_CasterClass:
|
||||
// Do not use this limit more then once per spell. If multiple class, treat value like items would.
|
||||
if (!PassLimitClass(focus_spell.base[i], GetClass()))
|
||||
return 0;
|
||||
break;
|
||||
|
||||
// handle effects
|
||||
case SE_ImprovedDamage:
|
||||
if (type == focusImprovedDamage) {
|
||||
@@ -5211,13 +5352,30 @@ int16 Mob::CalcFocusEffect(focusType type, uint16 focus_id, uint16 spell_id, boo
|
||||
if (type == focusSpellVulnerability) {
|
||||
if (best_focus) {
|
||||
if (focus_spell.base2[i] != 0)
|
||||
value = focus_spell.base2[i];
|
||||
value = focus_spell.base2[i]; //max damage
|
||||
else
|
||||
value = focus_spell.base[i];
|
||||
value = focus_spell.base[i]; //min damage
|
||||
} else if (focus_spell.base2[i] == 0 || focus_spell.base[i] == focus_spell.base2[i]) {
|
||||
value = focus_spell.base[i];
|
||||
value = focus_spell.base[i]; //If no max damage set, then default to min damage
|
||||
} else {
|
||||
value = zone->random.Int(focus_spell.base[i], focus_spell.base2[i]);
|
||||
value = zone->random.Int(focus_spell.base[i], focus_spell.base2[i]); //else random for value
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case SE_Fc_Spell_Damage_Pct_IncomingPC:
|
||||
if (type == focusFcSpellDamagePctIncomingPC) {
|
||||
if (best_focus) {
|
||||
if (focus_spell.base2[i] != 0)
|
||||
value = focus_spell.base2[i]; //max damage
|
||||
else
|
||||
value = focus_spell.base[i]; //min damage
|
||||
}
|
||||
else if (focus_spell.base2[i] == 0 || focus_spell.base[i] == focus_spell.base2[i]) {
|
||||
value = focus_spell.base[i]; //If no max damage set, then default to min damage
|
||||
}
|
||||
else {
|
||||
value = zone->random.Int(focus_spell.base[i], focus_spell.base2[i]); //else random for value
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -5247,6 +5405,11 @@ int16 Mob::CalcFocusEffect(focusType type, uint16 focus_id, uint16 spell_id, boo
|
||||
value = focus_spell.base[i];
|
||||
break;
|
||||
|
||||
case SE_Fc_Spell_Damage_Amt_IncomingPC:
|
||||
if (type == focusFcSpellDamageAmtIncomingPC)
|
||||
value = focus_spell.base[i];
|
||||
break;
|
||||
|
||||
case SE_FcHealAmtIncoming:
|
||||
if (type == focusFcHealAmtIncoming)
|
||||
value = focus_spell.base[i];
|
||||
@@ -5307,6 +5470,14 @@ int16 Mob::CalcFocusEffect(focusType type, uint16 focus_id, uint16 spell_id, boo
|
||||
value = focus_spell.base[i];
|
||||
break;
|
||||
|
||||
case SE_Fc_Cast_Spell_On_Land:
|
||||
if (type == focusFcCastSpellOnLand) {
|
||||
if (zone->random.Roll(focus_spell.base[i])) {
|
||||
value = focus_spell.base2[i];
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
#if EQDEBUG >= 6
|
||||
// this spits up a lot of garbage when calculating spell focuses
|
||||
// since they have all kinds of extra effects on them.
|
||||
@@ -6232,6 +6403,7 @@ bool Mob::DoHPToManaCovert(uint16 mana_cost)
|
||||
int32 Mob::GetFcDamageAmtIncoming(Mob *caster, uint32 spell_id, bool use_skill, uint16 skill )
|
||||
{
|
||||
//Used to check focus derived from SE_FcDamageAmtIncoming which adds direct damage to Spells or Skill based attacks.
|
||||
//Used to check focus derived from SE_Fc_Spell_Damage_Amt_IncomingPC which adds direct damage to Spells.
|
||||
int32 dmg = 0;
|
||||
bool limit_exists = false;
|
||||
bool skill_found = false;
|
||||
@@ -6280,6 +6452,20 @@ int32 Mob::GetFcDamageAmtIncoming(Mob *caster, uint32 spell_id, bool use_skill,
|
||||
}
|
||||
}
|
||||
}
|
||||
if (spellbonuses.FocusEffects[focusFcSpellDamageAmtIncomingPC]) {
|
||||
int buff_count = GetMaxTotalSlots();
|
||||
for (int i = 0; i < buff_count; i++) {
|
||||
|
||||
if ((IsValidSpell(buffs[i].spellid) && (IsEffectInSpell(buffs[i].spellid, SE_FcDamageAmtIncoming)))) {
|
||||
|
||||
int32 focus = caster->CalcFocusEffect(focusFcSpellDamageAmtIncomingPC, buffs[i].spellid, spell_id);
|
||||
if (focus) {
|
||||
dmg += focus;
|
||||
CheckNumHitsRemaining(NumHit::MatchingSpells, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return dmg;
|
||||
}
|
||||
|
||||
@@ -6967,6 +7153,133 @@ void Mob::TryTriggerThreshHold(int32 damage, int effect_id, Mob* attacker){
|
||||
}
|
||||
}
|
||||
|
||||
void Mob::CastSpellOnLand(Mob* caster, uint32 spell_id)
|
||||
{
|
||||
/*
|
||||
This function checks for incoming spells on a mob, if they meet the criteria for focus SE_Fc_Cast_Spell_on_Land then
|
||||
a new spell will be cast by THIS mob as specified by the focus effect. Note: Chance to cast the spell is determined in
|
||||
the CalcFocusEffect function if not 100pct.
|
||||
ApplyFocusProcLimiter() function checks for SE_Proc_Timer_Modifier which allows for limiting how often a spell from effect can be triggered
|
||||
for example, if set to base=1 and base2= 1500, then for everyone 1 successful trigger, you will be unable to trigger again for 1.5 seconds.
|
||||
|
||||
Live only has this focus in buffs/debuffs that can be placed on a target. TODO: Will consider adding support for it as AA and Item.
|
||||
*/
|
||||
if (!caster)
|
||||
return;
|
||||
|
||||
uint32 trigger_spell_id = 0;
|
||||
|
||||
//Step 1: Check this focus effect exists on the mob.
|
||||
if (spellbonuses.FocusEffects[focusFcCastSpellOnLand]) {
|
||||
|
||||
int buff_count = GetMaxTotalSlots();
|
||||
for (int i = 0; i < buff_count; i++) {
|
||||
|
||||
if ((IsValidSpell(buffs[i].spellid) && (buffs[i].spellid != spell_id) && IsEffectInSpell(buffs[i].spellid, SE_Fc_Cast_Spell_On_Land))) {
|
||||
|
||||
//Step 2: Check if we pass all focus limiters and focus chance roll
|
||||
trigger_spell_id = caster->CalcFocusEffect(focusFcCastSpellOnLand, buffs[i].spellid, spell_id, false, buffs[i].casterid);
|
||||
|
||||
if (IsValidSpell(trigger_spell_id) && (trigger_spell_id != spell_id)) {
|
||||
|
||||
//Step 3: Check if SE_Proc_Time_Modifier is present and if so apply it.
|
||||
if (ApplyFocusProcLimiter(buffs[i].spellid, i)) {
|
||||
//Step 4: Cast spells
|
||||
if (IsBeneficialSpell(trigger_spell_id)) {
|
||||
SpellFinished(trigger_spell_id, this, EQ::spells::CastingSlot::Item, 0, -1, spells[trigger_spell_id].ResistDiff);
|
||||
}
|
||||
else {
|
||||
Mob* current_target = GetTarget();
|
||||
//For now don't let players cast detrimental effects on themselves if they are targeting themselves. Need to confirm behavior.
|
||||
if (current_target && current_target->GetID() != GetID())
|
||||
SpellFinished(trigger_spell_id, current_target, EQ::spells::CastingSlot::Item, 0, -1, spells[trigger_spell_id].ResistDiff);
|
||||
}
|
||||
}
|
||||
|
||||
if (i >= 0)
|
||||
CheckNumHitsRemaining(NumHit::MatchingSpells, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool Mob::ApplyFocusProcLimiter(uint32 spell_id, int buffslot)
|
||||
{
|
||||
if (buffslot < 0)
|
||||
return false;
|
||||
|
||||
//Do not allow spell cast if timer is active.
|
||||
if (buffs[buffslot].focusproclimit_time > 0)
|
||||
return false;
|
||||
|
||||
/*
|
||||
SE_Proc_Timer_Modifier
|
||||
base1= amount of total procs allowed until lock out timer is triggered, should be set to at least 1 in any spell for the effect to function.
|
||||
base2= lock out timer, which prevents any more procs set in ms 1500 = 1.5 seconds
|
||||
This system allows easy scaling for multiple different buffs with same effects each having seperate active individual timer checks. Ie.
|
||||
*/
|
||||
|
||||
if (IsValidSpell(spell_id)) {
|
||||
|
||||
for (int i = 0; i < EFFECT_COUNT; i++) {
|
||||
|
||||
//Step 1: Find which slot the spell effect is in.
|
||||
if (spells[spell_id].effectid[i] == SE_Proc_Timer_Modifier) {
|
||||
|
||||
//Step 2: Check if you still have procs left to trigger, and if so reduce available procs
|
||||
if (buffs[buffslot].focusproclimit_procamt > 0) {
|
||||
--buffs[buffslot].focusproclimit_procamt; //Reduce total amount of triggers possible.
|
||||
}
|
||||
|
||||
//Step 3: If you used all the procs in the time frame then set proc amount back to max
|
||||
if (buffs[buffslot].focusproclimit_procamt == 0 && spells[spell_id].base[i] > 0) {
|
||||
buffs[buffslot].focusproclimit_procamt = spells[spell_id].base[i];//reset to max
|
||||
|
||||
//Step 4: Check if timer exists on this spell, and then set it, and activiate global timer if not active
|
||||
if (buffs[buffslot].focusproclimit_time ==0 && spells[spell_id].base2[i] > 0) {
|
||||
buffs[buffslot].focusproclimit_time = spells[spell_id].base2[i];//set time
|
||||
|
||||
//Step 5: If timer is not already running, then start it.
|
||||
if (!focus_proc_limit_timer.Enabled()) {
|
||||
focus_proc_limit_timer.Start(250);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void Mob::FocusProcLimitProcess()
|
||||
{
|
||||
/*
|
||||
Fast 250 ms uinversal timer for checking Focus effects that have a proc rate limiter set in actual time.
|
||||
*/
|
||||
bool stop_timer = true;
|
||||
int buff_count = GetMaxTotalSlots();
|
||||
for (int buffs_i = 0; buffs_i < buff_count; ++buffs_i)
|
||||
{
|
||||
if (IsValidSpell(buffs[buffs_i].spellid))
|
||||
{
|
||||
if (buffs[buffs_i].focusproclimit_time > 0) {
|
||||
buffs[buffs_i].focusproclimit_time -= 250;
|
||||
stop_timer = false;
|
||||
}
|
||||
|
||||
if (buffs[buffs_i].focusproclimit_time < 0)
|
||||
buffs[buffs_i].focusproclimit_time = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (stop_timer) {
|
||||
focus_proc_limit_timer.Disable();
|
||||
}
|
||||
}
|
||||
|
||||
bool Mob::CheckSpellCategory(uint16 spell_id, int category_id, int effect_id){
|
||||
|
||||
if (!IsValidSpell(spell_id) || !category_id)
|
||||
|
||||
Reference in New Issue
Block a user