Implemented new table 'npc_spells_effects' and 'npc_spells_effects_entires'.

Implemented new field in 'npc_spell_effects_id' in npc_types.

These are used to directly apply spell effect bonuses to NPC's without requirings spells/buffs.
Example: Allow an npc to spawn with an innate 50 pt damage shield and a 5% chance to critical hit.

Please see the wiki page: http://wiki.eqemulator.org/p?npc_spell_effects_entries for details.

*NPC's can now do critical heals / damage spells if bonus is applied from table.

Required SQL: utils/sql/git/required/2014_04_27_AISpellEffects.sql
Note: 30 examples of spell effects have been included by default in this sql. Edited/removed as needed.
This commit is contained in:
KayenEQ
2014-04-27 03:57:14 -04:00
parent 62e48e7701
commit 380cf8691a
7 changed files with 208 additions and 35 deletions
+2 -7
View File
@@ -1901,7 +1901,7 @@ bool NPC::AI_EngagedCastCheck() {
AIautocastspell_timer->Disable(); //prevent the timer from going off AGAIN while we are casting.
mlog(AI__SPELLS, "Engaged autocast check triggered. Trying to cast healing spells then maybe offensive spells.");
Shout("KAYEN");
// try casting a heal or gate
if (!AICastSpell(this, 100, SpellType_Heal | SpellType_Escape | SpellType_InCombatBuff)) {
// try casting a heal on nearby
@@ -2437,12 +2437,9 @@ void NPC::ApplyAISpellEffects(StatBonuses* newbon)
{
if (!AI_HasSpellsEffects())
return;
for(int i=0; i < AIspellsEffects.size(); i++)
{
Shout("ApplyAISpellEffects %i %i %i %i", AIspellsEffects[i].spelleffectid, AIspellsEffects[i].base, AIspellsEffects[i].limit,AIspellsEffects[i].max);
ApplySpellsBonuses(0, 0, newbon, 0, false, 0,-1,
true, AIspellsEffects[i].spelleffectid, AIspellsEffects[i].base, AIspellsEffects[i].limit,AIspellsEffects[i].max);
}
@@ -2456,7 +2453,6 @@ void NPC::AddSpellEffectToNPCList(uint16 iSpellEffectID, int32 base, int32 limit
if(!iSpellEffectID)
return;
HasAISpellEffects = true;
AISpellsEffects_Struct t;
@@ -2465,7 +2461,6 @@ void NPC::AddSpellEffectToNPCList(uint16 iSpellEffectID, int32 base, int32 limit
t.base = base;
t.limit = limit;
t.max = max;
Shout("AddSpellEffectToNPCList %i %i %i %i", iSpellEffectID,base, limit, max );
AIspellsEffects.push_back(t);
}
@@ -2692,7 +2687,7 @@ DBnpcspellseffects_Struct* ZoneDatabase::GetNPCSpellsEffects(uint32 iDBSpellsEff
mysql_free_result(result);
if (RunQuery(query, MakeAnyLenString(&query, "SELECT spell_effect_id, minlevel, maxlevel,se_base, se_limit, se_max from npc_spells_effects_entries where npc_spells_effects_id=%d ORDER BY minlevel", iDBSpellsEffectsID), errbuf, &result)) {
safe_delete_array(query);
uint32 tmpSize = sizeof(DBnpcspellseffects_Struct) + (sizeof(DBnpcspellseffects_Struct) * mysql_num_rows(result));
uint32 tmpSize = sizeof(DBnpcspellseffects_Struct) + (sizeof(DBnpcspellseffects_entries_Struct) * mysql_num_rows(result));
npc_spellseffects_cache[iDBSpellsEffectsID] = (DBnpcspellseffects_Struct*) new uchar[tmpSize];
memset(npc_spellseffects_cache[iDBSpellsEffectsID], 0, tmpSize);
npc_spellseffects_cache[iDBSpellsEffectsID]->parent_list = tmpparent_list;
+30 -12
View File
@@ -1158,14 +1158,20 @@ void Client::ApplyAABonuses(uint32 aaid, uint32 slots, StatBonuses* newbon)
break;
}
case SE_SpellEffectResistChance:
{
for(int e = 0; e < MAX_RESISTABLE_EFFECTS*2; e+=2)
{
if(!newbon->SEResist[e] || ((newbon->SEResist[e] = base2) && (newbon->SEResist[e+1] < base1)) ){
newbon->SEResist[e] = base2;
newbon->SEResist[e+1] = base1;
break;
if(newbon->SEResist[e+1] && (newbon->SEResist[e] == base2) && (newbon->SEResist[e+1] < base1)){
newbon->SEResist[e] = base2; //Spell Effect ID
newbon->SEResist[e+1] = base1; //Resist Chance
break;
}
else if (!newbon->SEResist[e+1]){
newbon->SEResist[e] = base2; //Spell Effect ID
newbon->SEResist[e+1] = base1; //Resist Chance
break;
}
}
break;
@@ -1791,10 +1797,10 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne
newbon->MeleeLifetap += spells[spell_id].base[i];
else if((effect_value < 0) && (newbon->MeleeLifetap > effect_value))
newbon->MeleeLifetap = spells[spell_id].base[i];
newbon->MeleeLifetap = effect_value;
else if(newbon->MeleeLifetap < spells[spell_id].base[i])
newbon->MeleeLifetap = spells[spell_id].base[i];
else if(newbon->MeleeLifetap < effect_value)
newbon->MeleeLifetap = effect_value;
break;
}
@@ -2494,10 +2500,14 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne
{
for(int e = 0; e < MAX_RESISTABLE_EFFECTS*2; e+=2)
{
if(!newbon->SEResist[e] &&
((newbon->SEResist[e] = base2) && (newbon->SEResist[e+1] < effect_value)) ){
newbon->SEResist[e] = base2;
newbon->SEResist[e+1] = effect_value;
if(newbon->SEResist[e+1] && (newbon->SEResist[e] == base2) && (newbon->SEResist[e+1] < effect_value)){
newbon->SEResist[e] = base2; //Spell Effect ID
newbon->SEResist[e+1] = effect_value; //Resist Chance
break;
}
else if (!newbon->SEResist[e+1]){
newbon->SEResist[e] = base2; //Spell Effect ID
newbon->SEResist[e+1] = effect_value; //Resist Chance
break;
}
}
@@ -2598,7 +2608,15 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne
case SE_Screech:
newbon->Screech = effect_value;
break;
//Special custom cases for loading effects on to NPC from 'npc_spels_effects' table
if (IsAISpellEffect) {
//Non-Focused Effect to modify incomming spell damage by resist type.
case SE_FcSpellVulnerability:
ModVulnerability(base2, effect_value);
break;
}
}
}
}
+43 -1
View File
@@ -55,9 +55,36 @@ int32 NPC::GetActSpellDamage(uint16 spell_id, int32 value, Mob* target) {
else
value -= target->GetFcDamageAmtIncoming(this, spell_id)/spells[spell_id].buffduration;
}
value += dmg*SpellFocusDMG/100;
if (AI_HasSpellsEffects()){
int16 chance = 0;
int ratio = 0;
if (spells[spell_id].buffduration == 0) {
chance += spellbonuses.CriticalSpellChance + spellbonuses.FrenziedDevastation;
if (chance && MakeRandomInt(1,100) <= chance){
ratio += spellbonuses.SpellCritDmgIncrease + spellbonuses.SpellCritDmgIncNoStack;
value += (value*ratio)/100;
entity_list.MessageClose_StringID(this, true, 100, MT_SpellCrits, OTHER_CRIT_BLAST, GetCleanName(), itoa(-value));
}
}
else {
chance += spellbonuses.CriticalDoTChance;
if (chance && MakeRandomInt(1,100) <= chance){
ratio += spellbonuses.DotCritDmgIncrease;
value += (value*ratio)/100;
}
}
}
return value;
}
@@ -254,6 +281,21 @@ int32 NPC::GetActSpellHealing(uint16 spell_id, int32 value, Mob* target) {
value += target->GetFocusIncoming(focusFcHealAmtIncoming, SE_FcHealAmtIncoming, this, spell_id);
value += value*target->GetHealRate(spell_id, this)/100;
}
//Allow for critical heal chance if NPC is loading spell effect bonuses.
if (AI_HasSpellsEffects()){
if(spells[spell_id].buffduration < 1) {
if(spellbonuses.CriticalHealChance && (MakeRandomInt(0,99) < spellbonuses.CriticalHealChance)) {
value = value*2;
entity_list.MessageClose_StringID(this, true, 100, MT_SpellCrits, OTHER_CRIT_HEAL, GetCleanName(), itoa(value));
}
}
else if(spellbonuses.CriticalHealOverTime && (MakeRandomInt(0,99) < spellbonuses.CriticalHealOverTime)) {
value = value*2;
}
}
return value;
}
+14 -14
View File
@@ -4150,10 +4150,10 @@ void Mob::TrySpellOnKill(uint8 level, uint16 spell_id)
for (int i = 0; i < EFFECT_COUNT; i++) {
if (spells[spell_id].effectid[i] == SE_SpellOnKill2)
{
if (spells[spell_id].max[i] <= level)
if (IsValidSpell(spells[spell_id].base2[i]) && spells[spell_id].max[i] <= level)
{
if(MakeRandomInt(0,99) < spells[spell_id].base[i])
SpellFinished(spells[spell_id].base2[i], this);
SpellFinished(spells[spell_id].base2[i], this, 10, 0, -1, spells[spells[spell_id].base2[i]].ResistDiff);
}
}
}
@@ -4166,19 +4166,19 @@ void Mob::TrySpellOnKill(uint8 level, uint16 spell_id)
// Allow to check AA, items and buffs in all cases. Base2 = Spell to fire | Base1 = % chance | Base3 = min level
for(int i = 0; i < MAX_SPELL_TRIGGER*3; i+=3) {
if(aabonuses.SpellOnKill[i] && (level >= aabonuses.SpellOnKill[i + 2])) {
if(aabonuses.SpellOnKill[i] && IsValidSpell(aabonuses.SpellOnKill[i]) && (level >= aabonuses.SpellOnKill[i + 2])) {
if(MakeRandomInt(0, 99) < static_cast<int>(aabonuses.SpellOnKill[i + 1]))
SpellFinished(aabonuses.SpellOnKill[i], this);
SpellFinished(aabonuses.SpellOnKill[i], this, 10, 0, -1, spells[aabonuses.SpellOnKill[i]].ResistDiff);
}
if(itembonuses.SpellOnKill[i] && (level >= itembonuses.SpellOnKill[i + 2])){
if(itembonuses.SpellOnKill[i] && IsValidSpell(itembonuses.SpellOnKill[i]) && (level >= itembonuses.SpellOnKill[i + 2])){
if(MakeRandomInt(0, 99) < static_cast<int>(itembonuses.SpellOnKill[i + 1]))
SpellFinished(itembonuses.SpellOnKill[i], this);
SpellFinished(itembonuses.SpellOnKill[i], this, 10, 0, -1, spells[aabonuses.SpellOnKill[i]].ResistDiff);
}
if(spellbonuses.SpellOnKill[i] && (level >= spellbonuses.SpellOnKill[i + 2])) {
if(spellbonuses.SpellOnKill[i] && IsValidSpell(spellbonuses.SpellOnKill[i]) && (level >= spellbonuses.SpellOnKill[i + 2])) {
if(MakeRandomInt(0, 99) < static_cast<int>(spellbonuses.SpellOnKill[i + 1]))
SpellFinished(spellbonuses.SpellOnKill[i], this);
SpellFinished(spellbonuses.SpellOnKill[i], this, 10, 0, -1, spells[aabonuses.SpellOnKill[i]].ResistDiff);
}
}
@@ -4193,21 +4193,21 @@ bool Mob::TrySpellOnDeath()
return false;
for(int i = 0; i < MAX_SPELL_TRIGGER*2; i+=2) {
if(IsClient() && aabonuses.SpellOnDeath[i]) {
if(IsClient() && aabonuses.SpellOnDeath[i] && IsValidSpell(aabonuses.SpellOnDeath[i])) {
if(MakeRandomInt(0, 99) < static_cast<int>(aabonuses.SpellOnDeath[i + 1])) {
SpellFinished(aabonuses.SpellOnDeath[i], this);
SpellFinished(aabonuses.SpellOnDeath[i], this, 10, 0, -1, spells[aabonuses.SpellOnDeath[i]].ResistDiff);
}
}
if(itembonuses.SpellOnDeath[i]) {
if(itembonuses.SpellOnDeath[i] && IsValidSpell(itembonuses.SpellOnDeath[i])) {
if(MakeRandomInt(0, 99) < static_cast<int>(itembonuses.SpellOnDeath[i + 1])) {
SpellFinished(itembonuses.SpellOnDeath[i], this);
SpellFinished(itembonuses.SpellOnDeath[i], this, 10, 0, -1, spells[itembonuses.SpellOnDeath[i]].ResistDiff);
}
}
if(spellbonuses.SpellOnDeath[i]) {
if(spellbonuses.SpellOnDeath[i] && IsValidSpell(spellbonuses.SpellOnDeath[i])) {
if(MakeRandomInt(0, 99) < static_cast<int>(spellbonuses.SpellOnDeath[i + 1])) {
SpellFinished(spellbonuses.SpellOnDeath[i], this);
SpellFinished(spellbonuses.SpellOnDeath[i], this, 10, 0, -1, spells[spellbonuses.SpellOnDeath[i]].ResistDiff);
}
}
}
+1 -1
View File
@@ -5675,7 +5675,7 @@ uint16 Mob::GetSpellEffectResistChance(uint16 spell_id)
if(!IsValidSpell(spell_id))
return 0;
if (!aabonuses.SEResist[0] && !spellbonuses.SEResist[0] && !itembonuses.SEResist[0])
if (!aabonuses.SEResist[1] && !spellbonuses.SEResist[1] && !itembonuses.SEResist[1])
return 0;
uint16 resist_chance = 0;