mirror of
https://github.com/EQEmu/Server.git
synced 2026-06-10 23:20:25 +00:00
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.
This commit is contained in:
+5
-4
@@ -165,6 +165,7 @@
|
||||
#define MAX_SYMPATHETIC_PROCS 10 // Number of sympathetic procs a client can have (This is arbitrary)
|
||||
|
||||
|
||||
|
||||
const int Z_AGGRO=10;
|
||||
|
||||
const uint32 MobAISpellRange=100; // max range of buffs
|
||||
@@ -823,12 +824,12 @@ typedef enum {
|
||||
#define SE_Hatelist_To_Tail_Index 478 // Implemented - Chance to be set to bottom of rampage list
|
||||
//#define SE_Ff_Value_Min 479 //
|
||||
//#define SE_Ff_Value_Max 480 //
|
||||
//#define SE_Fc_Cast_Spell_On_Land 481 //
|
||||
#define SE_Fc_Cast_Spell_On_Land 481 // [FOCUS EFFECT] Spells cast on target with this Focus Effect will have chance to cause a Spell to be cast if limits met.
|
||||
//#define SE_Skill_Base_Damage_Mod 482 //
|
||||
#define SE_Fc_Spell_Damage_Pct_IncomingPC 483 // Implemented - modifies incoming spell damage by percent
|
||||
#define SE_Fc_Spell_Damage_Amt_IncomingPC 484 // Implemented - modifies incoming spell damage by flat amount. Typically adds damage to incoming spells.
|
||||
//#define SE_Ff_CasterClass 485 //
|
||||
//#define SE_Ff_Same_Caster 486 //
|
||||
#define SE_Ff_CasterClass 485 // [FOCUS LIMIT] Caster of spell on target with a focus effect that is checked by incoming spells must be specified class.
|
||||
#define SE_Ff_Same_Caster 486 // [FOCUS LIMIT] 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
|
||||
//#define SE_Extend_Tradeskill_Cap 487 //
|
||||
//#define SE_Defender_Melee_Force_Pct_PC 488 //
|
||||
//#define SE_Worn_Endurance_Regen_Cap 489 //
|
||||
@@ -854,7 +855,7 @@ typedef enum {
|
||||
#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_Proc_Timer_Modifier 512 //
|
||||
//#define SE_Mana_Max_Percent 513 //
|
||||
//#define SE_Endurance_Max_Percent 514 //
|
||||
#define SE_AC_Avoidance_Max_Percent 515 // implemented - stackable avoidance modifier
|
||||
|
||||
@@ -1018,6 +1018,7 @@ int32 Mob::CheckAggroAmount(uint16 spell_id, Mob *target, bool isproc)
|
||||
case SE_Mez:
|
||||
case SE_Charm:
|
||||
case SE_Fear:
|
||||
case SE_Fearstun:
|
||||
AggroAmount += default_aggro;
|
||||
break;
|
||||
case SE_Root:
|
||||
|
||||
@@ -3771,6 +3771,8 @@ uint8 Mob::IsFocusEffect(uint16 spell_id,int effect_index, bool AA,uint32 aa_eff
|
||||
return focusFcMute;
|
||||
case SE_FcTimerRefresh:
|
||||
return focusFcTimerRefresh;
|
||||
case SE_Fc_Cast_Spell_On_Land:
|
||||
return focusFcCastSpellOnLand;
|
||||
case SE_FcStunTimeMod:
|
||||
return focusFcStunTimeMod;
|
||||
case SE_FcHealPctCritIncoming:
|
||||
|
||||
@@ -538,6 +538,9 @@ bool Client::Process() {
|
||||
}
|
||||
}
|
||||
|
||||
if (focus_proc_limit_timer.Check() && !dead)
|
||||
FocusProcLimitProcess();
|
||||
|
||||
if (client_state == CLIENT_KICKED) {
|
||||
Save();
|
||||
OnDisconnect(true);
|
||||
|
||||
@@ -137,6 +137,7 @@ typedef enum { //focus types
|
||||
focusFcHealPctIncoming,
|
||||
focusFcDamageAmtIncoming,
|
||||
focusFcSpellDamageAmtIncomingPC,
|
||||
focusFcCastSpellOnLand,
|
||||
focusFcHealAmtIncoming,
|
||||
focusFcBaseEffects,
|
||||
focusIncreaseNumHits,
|
||||
@@ -321,6 +322,8 @@ struct Buffs_Struct {
|
||||
int32 ExtraDIChance;
|
||||
int16 RootBreakChance; //Not saved to dbase
|
||||
uint32 instrument_mod;
|
||||
int16 focusproclimit_time; //timer to limit number of procs from focus effects
|
||||
int16 focusproclimit_procamt; //amount of procs that can be cast before timer limiter is set
|
||||
bool persistant_buff;
|
||||
bool client; //True if the caster is a client
|
||||
bool UpdateClient;
|
||||
|
||||
+5
-4
@@ -102,6 +102,7 @@ Mob::Mob(
|
||||
ranged_timer(2000),
|
||||
tic_timer(6000),
|
||||
mana_timer(2000),
|
||||
focus_proc_limit_timer(250),
|
||||
spellend_timer(0),
|
||||
rewind_timer(30000),
|
||||
bindwound_timer(10000),
|
||||
@@ -3806,7 +3807,7 @@ int32 Mob::GetVulnerability(Mob* caster, uint32 spell_id, uint32 ticsremaining)
|
||||
|
||||
if((IsValidSpell(buffs[i].spellid) && IsEffectInSpell(buffs[i].spellid, SE_FcSpellVulnerability))){
|
||||
|
||||
int32 focus = caster->CalcFocusEffect(focusSpellVulnerability, buffs[i].spellid, spell_id, true);
|
||||
int32 focus = caster->CalcFocusEffect(focusSpellVulnerability, buffs[i].spellid, spell_id, true, buffs[tmp_buffslot].casterid);
|
||||
|
||||
if (!focus)
|
||||
continue;
|
||||
@@ -3823,7 +3824,7 @@ int32 Mob::GetVulnerability(Mob* caster, uint32 spell_id, uint32 ticsremaining)
|
||||
}
|
||||
}
|
||||
|
||||
fc_spell_vulnerability_mod = caster->CalcFocusEffect(focusSpellVulnerability, buffs[tmp_buffslot].spellid, spell_id);
|
||||
fc_spell_vulnerability_mod = caster->CalcFocusEffect(focusSpellVulnerability, buffs[tmp_buffslot].spellid, spell_id, false, buffs[tmp_buffslot].casterid);
|
||||
|
||||
if (tmp_buffslot >= 0)
|
||||
CheckNumHitsRemaining(NumHit::MatchingSpells, tmp_buffslot);
|
||||
@@ -3844,7 +3845,7 @@ int32 Mob::GetVulnerability(Mob* caster, uint32 spell_id, uint32 ticsremaining)
|
||||
|
||||
if ((IsValidSpell(buffs[i].spellid) && IsEffectInSpell(buffs[i].spellid, SE_Fc_Spell_Damage_Pct_IncomingPC))) {
|
||||
|
||||
int32 focus = caster->CalcFocusEffect(focusFcSpellDamagePctIncomingPC, buffs[i].spellid, spell_id, true);
|
||||
int32 focus = caster->CalcFocusEffect(focusFcSpellDamagePctIncomingPC, buffs[i].spellid, spell_id, true, buffs[tmp_buffslot].casterid);
|
||||
|
||||
if (!focus)
|
||||
continue;
|
||||
@@ -3861,7 +3862,7 @@ int32 Mob::GetVulnerability(Mob* caster, uint32 spell_id, uint32 ticsremaining)
|
||||
}
|
||||
}
|
||||
|
||||
fc_spell_damage_pct_incomingPC_mod = caster->CalcFocusEffect(focusFcSpellDamagePctIncomingPC, buffs[tmp_buffslot].spellid, spell_id);
|
||||
fc_spell_damage_pct_incomingPC_mod = caster->CalcFocusEffect(focusFcSpellDamagePctIncomingPC, buffs[tmp_buffslot].spellid, spell_id, false, buffs[tmp_buffslot].casterid);
|
||||
|
||||
if (tmp_buffslot >= 0)
|
||||
CheckNumHitsRemaining(NumHit::MatchingSpells, tmp_buffslot);
|
||||
|
||||
+6
-1
@@ -770,7 +770,7 @@ public:
|
||||
void QuestJournalledSay(Client *QuestInitiator, const char *str, Journal::Options &opts);
|
||||
int32 GetItemStat(uint32 itemid, const char *identifier);
|
||||
|
||||
int16 CalcFocusEffect(focusType type, uint16 focus_id, uint16 spell_id, bool best_focus=false);
|
||||
int16 CalcFocusEffect(focusType type, uint16 focus_id, uint16 spell_id, bool best_focus=false, uint16 casterid=0);
|
||||
uint8 IsFocusEffect(uint16 spellid, int effect_index, bool AA=false,uint32 aa_effect=0);
|
||||
void SendIllusionPacket(uint16 in_race, uint8 in_gender = 0xFF, uint8 in_texture = 0xFF, uint8 in_helmtexture = 0xFF,
|
||||
uint8 in_haircolor = 0xFF, uint8 in_beardcolor = 0xFF, uint8 in_eyecolor1 = 0xFF, uint8 in_eyecolor2 = 0xFF,
|
||||
@@ -837,6 +837,10 @@ public:
|
||||
inline int16 GetSpellPowerDistanceMod() const { return SpellPowerDistanceMod; };
|
||||
inline void SetSpellPowerDistanceMod(int16 value) { SpellPowerDistanceMod = value; };
|
||||
int32 GetSpellStat(uint32 spell_id, const char *identifier, uint8 slot = 0);
|
||||
|
||||
void CastSpellOnLand(Mob* caster, uint32 spell_id);
|
||||
void FocusProcLimitProcess();
|
||||
bool ApplyFocusProcLimiter(uint32 spell_id, int buffslot = -1);
|
||||
|
||||
void ModSkillDmgTaken(EQ::skills::SkillType skill_num, int value);
|
||||
int16 GetModSkillDmgTaken(const EQ::skills::SkillType skill_num);
|
||||
@@ -1419,6 +1423,7 @@ protected:
|
||||
int16 slow_mitigation; // Allows for a slow mitigation (100 = 100%, 50% = 50%)
|
||||
Timer tic_timer;
|
||||
Timer mana_timer;
|
||||
Timer focus_proc_limit_timer;
|
||||
|
||||
//spell casting vars
|
||||
Timer spellend_timer;
|
||||
|
||||
@@ -1125,6 +1125,9 @@ void Mob::AI_Process() {
|
||||
|
||||
ProjectileAttack();
|
||||
|
||||
if (focus_proc_limit_timer.Check())
|
||||
FocusProcLimitProcess();
|
||||
|
||||
auto npcSpawnPoint = CastToNPC()->GetSpawnPoint();
|
||||
if (GetSpecialAbility(TETHER)) {
|
||||
float tether_range = static_cast<float>(GetSpecialAbilityParam(TETHER, 0));
|
||||
|
||||
+208
-23
@@ -2934,17 +2934,22 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove
|
||||
break;
|
||||
}
|
||||
|
||||
if (GetLevel() <= spells[spell_id].max[i]) {
|
||||
if (IsClient())
|
||||
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(effect_value);
|
||||
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;
|
||||
@@ -3206,6 +3211,9 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove
|
||||
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;
|
||||
}
|
||||
@@ -4481,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;
|
||||
@@ -4508,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;
|
||||
@@ -4537,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;
|
||||
@@ -4554,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;
|
||||
}
|
||||
@@ -4594,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;
|
||||
@@ -4613,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;
|
||||
@@ -4624,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;
|
||||
@@ -4635,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;
|
||||
@@ -4646,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;
|
||||
@@ -4675,7 +4693,7 @@ int16 Client::CalcAAFocus(focusType type, const AA::Rank &rank, uint16 spell_id)
|
||||
LimitFailure = true;
|
||||
break;
|
||||
|
||||
// Handle Focus Effects
|
||||
// Handle Focus Effects
|
||||
case SE_ImprovedDamage:
|
||||
if (type == focusImprovedDamage && base1 > value)
|
||||
value = base1;
|
||||
@@ -4742,11 +4760,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;
|
||||
@@ -4760,7 +4780,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;
|
||||
}
|
||||
@@ -4771,7 +4792,7 @@ int16 Client::CalcAAFocus(focusType type, const AA::Rank &rank, uint16 spell_id)
|
||||
if (type == focusSpellVulnerability)
|
||||
value = base1;
|
||||
break;
|
||||
|
||||
|
||||
case SE_Fc_Spell_Damage_Pct_IncomingPC:
|
||||
if (type == focusFcSpellDamagePctIncomingPC)
|
||||
value = base1;
|
||||
@@ -4789,7 +4810,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;
|
||||
@@ -4874,6 +4895,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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4890,8 +4919,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;
|
||||
|
||||
@@ -5125,6 +5159,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) {
|
||||
@@ -5414,6 +5465,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.
|
||||
@@ -7089,6 +7148,132 @@ 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.
|
||||
*/
|
||||
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
|
||||
base2= lock out timer, which prevents any more procs
|
||||
This system allows easy scaling for multiple different buffs with same effects each having seperate active individual timer checks.
|
||||
*/
|
||||
|
||||
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)
|
||||
|
||||
@@ -3350,6 +3350,8 @@ int Mob::AddBuff(Mob *caster, uint16 spell_id, int duration, int32 level_overrid
|
||||
buffs[emptyslot].dot_rune = 0;
|
||||
buffs[emptyslot].ExtraDIChance = 0;
|
||||
buffs[emptyslot].RootBreakChance = 0;
|
||||
buffs[emptyslot].focusproclimit_time = 0;
|
||||
buffs[emptyslot].focusproclimit_procamt = 0;
|
||||
buffs[emptyslot].instrument_mod = caster ? caster->GetInstrumentMod(spell_id) : 10;
|
||||
|
||||
if (level_override > 0) {
|
||||
@@ -3972,6 +3974,10 @@ bool Mob::SpellOnTarget(uint16 spell_id, Mob *spelltar, bool reflect, bool use_r
|
||||
return false;
|
||||
}
|
||||
|
||||
//Check SE_Fc_Cast_Spell_On_Land SPA 481 on target, if hit by this spell and Conditions are Met then target will cast the specified spell.
|
||||
if (spelltar)
|
||||
spelltar->CastSpellOnLand(this, spell_id);
|
||||
|
||||
if (IsValidSpell(spells[spell_id].RecourseLink) && spells[spell_id].RecourseLink != spell_id)
|
||||
SpellFinished(spells[spell_id].RecourseLink, this, CastingSlot::Item, 0, -1, spells[spells[spell_id].RecourseLink].ResistDiff);
|
||||
|
||||
|
||||
@@ -3666,6 +3666,8 @@ void ZoneDatabase::LoadBuffs(Client *client)
|
||||
buffs[slot_id].caston_z = caston_z;
|
||||
buffs[slot_id].ExtraDIChance = ExtraDIChance;
|
||||
buffs[slot_id].RootBreakChance = 0;
|
||||
buffs[slot_id].focusproclimit_time = 0;
|
||||
buffs[slot_id].focusproclimit_procamt = 0;
|
||||
buffs[slot_id].UpdateClient = false;
|
||||
buffs[slot_id].instrument_mod = instrument_mod;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user