Caryatis |
11-06-2010 06:22 PM |
So after posting I realized a better solution. What we lack is a way to calculate focus effects from AAs, which would benefit this situation but also alleviate the need to hardcode so many AAs.
Specifically related to this effect, this was the only way to accomplish passive AA triggers as there are no spells in the DB that trigger Gift of Mana or Healing Light type AAs(for activated AA triggers like Pyromancy, a buff is placed on your char which then triggers Pyromantic Ignition, but for Gift of Mana, there is no such "pre-buff").
Now you can build AAs just like spells(think of the aa_effects slot as an effect slot in a buff). Currently the list is pretty bare(only has the limits, improved dmg and triggeroncast) but I didnt want to bog down this update anymore than necessary as its quite large as it is. Later on, I will migrate the current hardcoded AAs so that the information is in the aa_effects DB.
REQUIRED SQL(changes base1 and base2 in aa_effects to be signed, as some focuses make use of negative values):
Code:
ALTER TABLE `aa_effects` CHANGE COLUMN `base1` `base1` MEDIUMINT(8) NOT NULL DEFAULT '0' AFTER `effectid`, CHANGE COLUMN `base2` `base2` MEDIUMINT(8) NOT NULL DEFAULT '0' AFTER `base1`;
DIFF:
Code:
Index: EQEmuServer/zone/bonuses.cpp
===================================================================
--- EQEmuServer/zone/bonuses.cpp (revision 1714)
+++ EQEmuServer/zone/bonuses.cpp (working copy)
@@ -584,16 +584,6 @@
break;
case SE_IncreaseRange:
break;
- case SE_LimitEffect:
- break;
- case SE_LimitSpellType:
- break;
- case SE_LimitMinDur:
- break;
- case SE_LimitInstant:
- break;
- case SE_LimitCastTime:
- break;
case SE_MaxHPChange:
newbon->MaxHP += base1;
break;
@@ -659,9 +649,20 @@
case SE_TotalHP:
newbon->HP += base1;
break;
+ case SE_TriggerOnCast:
+ for(int i = 0; i < MAX_SPELL_TRIGGER; i+=2)
+ {
+ if(!newbon->SpellTriggers[i])
+ {
+ // base1 = SpellID to be triggered, base2 = chance to fire
+ newbon->SpellTriggers[i] = base1;
+ newbon->SpellTriggers[i+1] = base2;
+ break;
+ }
+ }
+ break;
}
}
-
}
void Mob::CalcSpellBonuses(StatBonuses* newbon)
@@ -1206,6 +1207,18 @@
newbon->Accuracy = effect_value;
break;
}
+ case SE_TriggerOnCast:
+ {
+ for(int i = 0; i < MAX_SPELL_TRIGGER; i++)
+ {
+ if(!newbon->SpellTriggers[i])
+ {
+ newbon->SpellTriggers[i] = spell_id;
+ break;
+ }
+ }
+ break;
+ }
}
Index: EQEmuServer/zone/client.h
===================================================================
--- EQEmuServer/zone/client.h (revision 1714)
+++ EQEmuServer/zone/client.h (working copy)
@@ -666,7 +666,9 @@
int32 GetAA(int32 aa_id) const;
bool SetAA(int32 aa_id, int32 new_value);
inline uint32 GetAAPointsSpent() { return m_pp.aapoints_spent; }
-
+ sint16 CalcAAFocusEffect(focusType type, int16 focus_spell, int16 spell_id);
+ sint16 CalcAAFocus(focusType type, uint32 aa_ID, int16 spell_id);
+
sint16 acmod();
// Item methods
Index: EQEmuServer/zone/mob.cpp
===================================================================
--- EQEmuServer/zone/mob.cpp (revision 1714)
+++ EQEmuServer/zone/mob.cpp (working copy)
@@ -3085,28 +3085,90 @@
}
-void Mob::TryTriggerOnCast(Mob *target, uint32 spell_id)
+void Mob::TryTriggerOnCast(uint32 spell_id, bool aa_trigger)
{
- if(target == NULL || !IsValidSpell(spell_id))
+ if(!IsValidSpell(spell_id))
+ return;
+
+ if(aa_trigger)
{
- return;
+ for(int i = 0; i < MAX_SPELL_TRIGGER; i+=2)
+ {
+ if(this->aabonuses.SpellTriggers[i])
+ TriggerOnCast(this->aabonuses.SpellTriggers[i], spell_id, this->aabonuses.SpellTriggers[i+1]);
+ }
}
+ else
+ {
+ if(this->itembonuses.SpellTriggers[0])
+ {
+ for(int i = 0; i < MAX_SPELL_TRIGGER; i++)
+ {
+ if(this->itembonuses.SpellTriggers[i])
+ TriggerOnCast(this->itembonuses.SpellTriggers[i], spell_id, 0);
+ }
+ }
+ if(this->spellbonuses.SpellTriggers[0])
+ {
+ for(int i = 0; i < MAX_SPELL_TRIGGER; i++)
+ {
+ if(this->spellbonuses.SpellTriggers[i])
+ TriggerOnCast(this->spellbonuses.SpellTriggers[i], spell_id, 0);
+ }
+ }
+ }
+}
- uint32 buff_count = GetMaxTotalSlots();
- for(int i = 0; i < buff_count; i++)
+void Mob::TriggerOnCast(uint32 focus_spell, uint32 spell_id, uint8 aa_chance)
+{
+ if(!IsValidSpell(focus_spell) || !IsValidSpell(spell_id))
+ return;
+
+ sint32 focus = 0;
+ if(!aa_chance)
{
- if(IsEffectInSpell(buffs[i].spellid, SE_TriggerOnCast))
+ focus = CalcFocusEffect(focusTriggerOnCast, focus_spell, spell_id);
+ if(focus)
{
- sint32 focus = CalcFocusEffect(focusTriggerOnCast, buffs[i].spellid, spell_id);
- if(focus == 1)
+ for(int i = 0; i < EFFECT_COUNT; i++)
{
- if(MakeRandomInt(0, 1000) < spells[buffs[i].spellid].base[0])
+ if (spells[focus_spell].effectid[i] == SE_TriggerOnCast)
{
- SpellOnTarget(spells[buffs[i].spellid].base2[0], target);
+ // 100 = 100% chance to proc... seriously KLS :)
+ if(MakeRandomInt(0, 99) < spells[focus_spell].base[i])
+ {
+ if(spells[spells[focus_spell].base2[i]].targettype == ST_Self || spells[spells[focus_spell].base2[i]].targettype == ST_Group)
+ {
+ SpellOnTarget(spells[focus_spell].base2[i], this);
+ }
+ else if (this->GetTarget())
+ {
+ SpellOnTarget(spells[focus_spell].base2[i], this->GetTarget());
+ }
+ }
}
}
}
}
+ // Innate AA Triggers
+ else
+ {
+ focus = this->CastToClient()->CalcAAFocusEffect(focusTriggerOnCast, focus_spell, spell_id);
+ if(focus)
+ {
+ if(MakeRandomInt(0, 99) < aa_chance)
+ {
+ if(spells[focus_spell].targettype == ST_Self || spells[focus_spell].targettype == ST_Group)
+ {
+ SpellOnTarget(focus_spell, this);
+ }
+ else if (this->GetTarget())
+ {
+ SpellOnTarget(focus_spell, this->GetTarget());
+ }
+ }
+ }
+ }
}
void Mob::TrySpellTrigger(Mob *target, uint32 spell_id)
Index: EQEmuServer/zone/mob.h
===================================================================
--- EQEmuServer/zone/mob.h (revision 1714)
+++ EQEmuServer/zone/mob.h (working copy)
@@ -275,6 +275,7 @@
sint8 HundredHands; //extra haste, stacks with all other haste i
sint8 MeleeLifetap;
+ uint32 SpellTriggers[MAX_SPELL_TRIGGER];
int XPRateMod;
sint8 Packrat; //weight reduction for items, 1 point = 10%
@@ -790,7 +791,8 @@
bool PassCharismaCheck(Mob* caster, Mob* spellTarget, int16 spell_id);
bool TryDeathSave();
void DoBuffWearOffEffect(uint32 index);
- void TryTriggerOnCast(Mob *target, uint32 spell_id);
+ void TryTriggerOnCast(uint32 spell_id, bool aa_trigger);
+ void TriggerOnCast(uint32 focus_spell, uint32 spell_id, uint8 aa_chance);
void TrySpellTrigger(Mob *target, uint32 spell_id);
void TryApplyEffect(Mob *target, uint32 spell_id);
void TryTwincast(Mob *caster, Mob *target, uint32 spell_id);
Index: EQEmuServer/zone/spdat.h
===================================================================
--- EQEmuServer/zone/spdat.h (revision 1714)
+++ EQEmuServer/zone/spdat.h (working copy)
@@ -43,6 +43,7 @@
#define DB_LoadSPDat //load from DB vs spells_us.txt. for now, we're piggybacking NEW_LoadSPDat, so it will take precedence
#define EFFECT_COUNT 12
+#define MAX_SPELL_TRIGGER 12 // One for each slot(only 6 for AA since AA use 2)
const int Z_AGGRO=10;
Index: EQEmuServer/zone/spell_effects.cpp
===================================================================
--- EQEmuServer/zone/spell_effects.cpp (revision 1714)
+++ EQEmuServer/zone/spell_effects.cpp (working copy)
@@ -3765,6 +3765,183 @@
CalcBonuses();
}
+sint16 Client::CalcAAFocusEffect(focusType type, int16 focus_spell, int16 spell_id)
+{
+ uint32 slots = 0;
+ uint32 aa_AA = 0;
+ uint32 aa_value = 0;
+
+ sint32 value = 0;
+ // Iterate through all of the client's AAs
+ for (int i = 0; i < MAX_PP_AA_ARRAY; i++)
+ {
+ aa_AA = this->aa[i]->AA;
+ aa_value = this->aa[i]->value;
+ if (aa_AA > 0 || aa_value > 0)
+ {
+ slots = zone->GetTotalAALevels(aa_AA);
+ if (slots > 0)
+ for(int j = 1;j <= slots; j++)
+ {
+ switch (aa_effects[aa_AA][j].skill_id)
+ {
+ case SE_TriggerOnCast:
+ // If focus_spell matches the spell listed in the DB, load these restrictions
+ if(type == focusTriggerOnCast && focus_spell == aa_effects[aa_AA][j].base1)
+ value = CalcAAFocus(type, aa_AA, spell_id);
+ break;
+ }
+ }
+ }
+ }
+ return value;
+}
+
+
+sint16 Client::CalcAAFocus(focusType type, uint32 aa_ID, int16 spell_id)
+{
+ const SPDat_Spell_Struct &spell = spells[spell_id];
+
+ sint16 value = 0;
+ int lvlModifier = 100;
+ int lvldiff = 0;
+
+ int32 effect = 0;
+ int32 base1 = 0;
+ int32 base2 = 0;
+ int32 slot = 0;
+
+ std::map<uint32, std::map<uint32, AA_Ability> >::const_iterator find_iter = aa_effects.find(aa_ID);
+ if(find_iter == aa_effects.end())
+ {
+ return 0;
+ }
+
+ for (map<uint32, AA_Ability>::const_iterator iter = aa_effects[aa_ID].begin(); iter != aa_effects[aa_ID].end(); ++iter)
+ {
+ effect = iter->second.skill_id;
+ base1 = iter->second.base1;
+ base2 = iter->second.base2;
+ slot = iter->second.slot;
+
+ switch (effect)
+ {
+ case SE_Blank:
+ break;
+
+ // Limits
+ case SE_LimitResist:
+ if(base1)
+ {
+ if(spell.resisttype != base1)
+ return 0;
+ }
+ break;
+ case SE_LimitInstant:
+ if(spell.buffduration)
+ return 0;
+ break;
+ case SE_LimitMaxLevel:
+ lvldiff = (spell.classes[(GetClass()%16) - 1]) - base1;
+ //every level over cap reduces the effect by base2 percent
+ if(lvldiff > 0)
+ {
+ if(base2 > 0)
+ {
+ lvlModifier -= base2*lvldiff;
+ if(lvlModifier < 1)
+ return 0;
+ }
+ else {
+ return 0;
+ }
+ }
+ break;
+ case SE_LimitMinLevel:
+ if((spell.classes[(GetClass()%16) - 1]) < base1)
+ return 0;
+ break;
+ case SE_LimitCastTime:
+ if (spell.cast_time < base1)
+ return 0;
+ break;
+ case SE_LimitSpell:
+ // Exclude spell(any but this)
+ if(base1 < 0) {
+ if (spell_id == (base1*-1))
+ return 0;
+ }
+ else {
+ // Include Spell(only this)
+ if (spell_id != base1)
+ return(0);
+ }
+ break;
+ case SE_LimitMinDur:
+ if (base1 > CalcBuffDuration_formula(GetLevel(), spell.buffdurationformula, spell.buffduration))
+ return(0);
+ break;
+ case SE_LimitEffect:
+ // Exclude effect(any but this)
+ if(base1 < 0) {
+ if(IsEffectInSpell(spell_id,(base1*-1)))
+ return 0;
+ }
+ else {
+ // Include effect(only this)
+ if(!IsEffectInSpell(spell_id,base1))
+ return 0;
+ }
+ break;
+ case SE_LimitSpellType:
+ switch(base1)
+ {
+ case 0:
+ if (!IsDetrimentalSpell(spell_id))
+ return 0;
+ break;
+ case 1:
+ if (!IsBeneficialSpell(spell_id))
+ return 0;
+ break;
+ }
+ break;
+
+ // Passive Focus Effects
+ case SE_ImprovedDamage:
+ switch (base2)
+ {
+ case 0:
+ if (type == focusImprovedDamage && base1 > value)
+ value = base1;
+ break;
+ case 1:
+ if (type == focusImprovedCritical && base1 > value)
+ value = base1;
+ break;
+ case 2:
+ if (type == focusImprovedUndeadDamage && base1 > value)
+ value = base1;
+ break;
+ case 3:
+ if (type == 10 && base1 > value)
+ value = base1;
+ break;
+ }
+ break;
+
+
+ // Unique Focus Effects
+ case SE_TriggerOnCast:
+ if(type == focusTriggerOnCast)
+ value = 1;
+ break;
+
+ }
+ }
+ return(value*lvlModifier/100);
+}
+
//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
sint16 Mob::CalcFocusEffect(focusType type, int16 focus_id, int16 spell_id) {
@@ -3999,9 +4176,8 @@
case SE_TriggerOnCast:
{
if(type == focusTriggerOnCast)
- {
value = 1;
- }
+
break;
}
case SE_SpellVulnerability:
@@ -4140,7 +4316,32 @@
realTotal2 = Total2;
}
}
+
+ // AA Focus
+ sint16 Total3 = 0;
+ sint16 realTotal3 = 0;
+
+ uint32 slots = 0;
+ uint32 aa_AA = 0;
+ uint32 aa_value = 0;
+
+ for (int i = 0; i < MAX_PP_AA_ARRAY; i++)
+ {
+ aa_AA = this->aa[i]->AA;
+ aa_value = this->aa[i]->value;
+ if (aa_AA < 1 || aa_value < 1)
+ continue;
+
+ Total3 = CalcAAFocus(type, aa_AA, spell_id);
+ if (Total3 > 0 && realTotal3 >= 0 && Total3 > realTotal3) {
+ realTotal3 = Total3;
+ }
+ else if (Total3 < 0 && Total3 < realTotal3) {
+ realTotal3 = Total3;
+ }
+ }
+
if(type == focusReagentCost && IsSummonPetSpell(spell_id) && GetAA(aaElementalPact))
return 100;
@@ -4150,7 +4351,7 @@
//by reagent conservation for obvious reasons.
}
- return realTotal + realTotal2;
+ return realTotal + realTotal2 + realTotal3;
}
//for some stupid reason SK procs return theirs one base off...
Index: EQEmuServer/zone/spells.cpp
===================================================================
--- EQEmuServer/zone/spells.cpp (revision 1714)
+++ EQEmuServer/zone/spells.cpp (working copy)
@@ -1072,6 +1072,14 @@
TrySympatheticProc(target, spell_id);
TryTwincast(this, target, spell_id);
+
+ if(this->itembonuses.SpellTriggers[0] || this->spellbonuses.SpellTriggers[0])
+ TryTriggerOnCast(spell_id, 0);
+
+ if(this->IsClient()) {
+ if(this->aabonuses.SpellTriggers[0])
+ TryTriggerOnCast(spell_id, 1);
+ }
// we're done casting, now try to apply the spell
if( !SpellFinished(spell_id, spell_target, slot, mana_used, inventory_slot) )
@@ -3128,7 +3136,6 @@
}
}
- TryTriggerOnCast(spelltar, spell_id);
TrySpellTrigger(spelltar, spell_id);
TryApplyEffect(spelltar, spell_id);
For Testing...
This SQL will add the AA Gift of Mana so that you can easily test/see how it works to set an AA up under the new system(it scales to 100% proc rate, for testing purposes).
Code:
INSERT INTO `altadv_vars` (`skill_id`, `name`, `cost`, `max_level`, `hotkey_sid`, `hotkey_sid2`, `title_sid`, `desc_sid`, `type`, `spellid`, `prereq_skill`, `prereq_minpoints`, `spell_type`, `spell_refresh`, `classes`, `berserker`, `class_type`, `cost_inc`, `aa_expansion`, `special_category`, `sof_type`, `sof_cost_inc`, `sof_max_level`, `sof_next_skill`, `clientver`, `account_time_required`) VALUES (1435, 'Gift of Mana', 3, 3, 4294967295, 4294967295, 1435, 1435, 8, 0, 0, 0, 0, 0, 31812, 0, 66, 3, 9, 4294967295, 3, 3, 1, 1435, 1, 0);
INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (1435, 1, 339, 8105, 10);
INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (1435, 2, 134, 70, 0);
INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (1435, 3, 142, 65, 0);
INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (1435, 4, 137, 0, 0);
INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (1436, 1, 339, 8105, 50);
INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (1436, 2, 134, 70, 0);
INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (1436, 3, 142, 65, 0);
INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (1436, 4, 137, 0, 0);
INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (1437, 1, 339, 8105, 100);
INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (1437, 2, 134, 70, 0);
INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (1437, 3, 142, 65, 0);
INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (1437, 4, 137, 0, 0);
|