Merge branch 'master' into raycast

This commit is contained in:
KimLS 2014-04-29 13:01:26 -07:00
commit 0c9d640683
14 changed files with 557 additions and 99 deletions

View File

@ -1,5 +1,19 @@
EQEMu Changelog (Started on Sept 24, 2003 15:50)
-------------------------------------------------------
== 04/27/2014 ==
Kayen: 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.
== 04/25/2014 ==
cavedude: Corrected a crash in spawn_conditions caused by NPCs on a one way path.
cavedude: Added strict column to spawn_events which will prevent an event from enabling if it's mid-cycle.

View File

@ -0,0 +1,104 @@
-- Note: The data entered into the new table are only examples and can be deleted/modified as needed.
ALTER TABLE `npc_types` ADD `npc_spells_effects_id` int( 11 ) UNSIGNED NOT NULL DEFAULT '0' AFTER `npc_spells_id`;
SET FOREIGN_KEY_CHECKS=0;
-- ----------------------------
-- Table structure for `npc_spells_effects`
-- ----------------------------
DROP TABLE IF EXISTS `npc_spells_effects`;
CREATE TABLE `npc_spells_effects` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`name` tinytext,
`parent_list` int(11) unsigned NOT NULL DEFAULT '0',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1080 DEFAULT CHARSET=latin1;
-- ----------------------------
-- Records of npc_spells_effects
-- ----------------------------
INSERT INTO `npc_spells_effects` VALUES ('1', 'Critical Melee [All Skills]', '0');
INSERT INTO `npc_spells_effects` VALUES ('2', 'Damage Shield', '0');
INSERT INTO `npc_spells_effects` VALUES ('3', 'Melee Haste', '0');
INSERT INTO `npc_spells_effects` VALUES ('4', 'Resist Spell Chance', '0');
INSERT INTO `npc_spells_effects` VALUES ('5', 'Resist Direct Dmg Spell Chance', '0');
INSERT INTO `npc_spells_effects` VALUES ('6', 'Reflect Spell Chance', '0');
INSERT INTO `npc_spells_effects` VALUES ('7', 'Spell Damage Shield', '0');
INSERT INTO `npc_spells_effects` VALUES ('8', 'Melee Mitigation [All]', '0');
INSERT INTO `npc_spells_effects` VALUES ('9', 'Avoid Melee', '0');
INSERT INTO `npc_spells_effects` VALUES ('10', 'Riposte Chance', '0');
INSERT INTO `npc_spells_effects` VALUES ('11', 'Dodge Chance', '0');
INSERT INTO `npc_spells_effects` VALUES ('12', 'Parry Chance', '0');
INSERT INTO `npc_spells_effects` VALUES ('13', 'Decrease Dmg Taken [2HS]', '0');
INSERT INTO `npc_spells_effects` VALUES ('14', 'Increase Dmg Taken [1HS]', '0');
INSERT INTO `npc_spells_effects` VALUES ('15', 'Block Chance', '0');
INSERT INTO `npc_spells_effects` VALUES ('16', 'Melee Lifetap', '0');
INSERT INTO `npc_spells_effects` VALUES ('17', 'Hit Chance', '0');
INSERT INTO `npc_spells_effects` VALUES ('18', 'Increase Dmg [1HS]', '0');
INSERT INTO `npc_spells_effects` VALUES ('19', 'Increase Archery Dmg', '0');
INSERT INTO `npc_spells_effects` VALUES ('20', 'Flurry Chance', '0');
INSERT INTO `npc_spells_effects` VALUES ('21', 'Add Damage [2HS]', '0');
INSERT INTO `npc_spells_effects` VALUES ('22', 'Divine Aura', '0');
INSERT INTO `npc_spells_effects` VALUES ('23', 'Cast CH on Kill', '0');
INSERT INTO `npc_spells_effects` VALUES ('24', 'Critical Heal', '0');
INSERT INTO `npc_spells_effects` VALUES ('25', 'Critical Direct Dmg', '0');
INSERT INTO `npc_spells_effects` VALUES ('26', 'Heal Rate', '0');
INSERT INTO `npc_spells_effects` VALUES ('27', 'Negate Damage Shield', '0');
INSERT INTO `npc_spells_effects` VALUES ('28', 'Increase Spell Vulnerability [All]', '0');
INSERT INTO `npc_spells_effects` VALUES ('29', 'Decrease Spell Vulnerability [FR]', '0');
INSERT INTO `npc_spells_effects` VALUES ('30', 'Movement Speed', '0');
SET FOREIGN_KEY_CHECKS=0;
-- ----------------------------
-- Table structure for `npc_spells_effects_entries`
-- ----------------------------
DROP TABLE IF EXISTS `npc_spells_effects_entries`;
CREATE TABLE `npc_spells_effects_entries` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`npc_spells_effects_id` int(11) NOT NULL DEFAULT '0',
`spell_effect_id` smallint(5) NOT NULL DEFAULT '0',
`minlevel` tinyint(3) unsigned NOT NULL DEFAULT '0',
`maxlevel` tinyint(3) unsigned NOT NULL DEFAULT '255',
`se_base` int(11) NOT NULL DEFAULT '0',
`se_limit` int(11) NOT NULL DEFAULT '0',
`se_max` int(11) NOT NULL DEFAULT '0',
PRIMARY KEY (`id`),
UNIQUE KEY `spellsid_spellid` (`npc_spells_effects_id`,`spell_effect_id`)
) ENGINE=InnoDB AUTO_INCREMENT=18374 DEFAULT CHARSET=latin1;
-- ----------------------------
-- Records of npc_spells_effects_entries
-- ----------------------------
INSERT INTO `npc_spells_effects_entries` VALUES ('1', '1', '169', '0', '255', '10000', '-1', '0');
INSERT INTO `npc_spells_effects_entries` VALUES ('2', '2', '59', '0', '255', '-60', '0', '0');
INSERT INTO `npc_spells_effects_entries` VALUES ('3', '3', '11', '0', '255', '150', '0', '0');
INSERT INTO `npc_spells_effects_entries` VALUES ('4', '4', '180', '0', '255', '50', '0', '0');
INSERT INTO `npc_spells_effects_entries` VALUES ('5', '5', '378', '0', '255', '85', '0', '0');
INSERT INTO `npc_spells_effects_entries` VALUES ('6', '6', '158', '0', '255', '50', '0', '0');
INSERT INTO `npc_spells_effects_entries` VALUES ('7', '7', '157', '0', '255', '-300', '0', '0');
INSERT INTO `npc_spells_effects_entries` VALUES ('8', '8', '168', '0', '255', '-50', '0', '0');
INSERT INTO `npc_spells_effects_entries` VALUES ('9', '9', '172', '0', '255', '10000', '0', '0');
INSERT INTO `npc_spells_effects_entries` VALUES ('10', '10', '173', '0', '255', '10000', '0', '0');
INSERT INTO `npc_spells_effects_entries` VALUES ('11', '11', '174', '0', '255', '10000', '0', '0');
INSERT INTO `npc_spells_effects_entries` VALUES ('12', '12', '175', '0', '255', '10000', '0', '0');
INSERT INTO `npc_spells_effects_entries` VALUES ('13', '13', '197', '0', '255', '-80', '3', '0');
INSERT INTO `npc_spells_effects_entries` VALUES ('14', '14', '197', '0', '255', '80', '1', '0');
INSERT INTO `npc_spells_effects_entries` VALUES ('15', '15', '188', '0', '255', '10000', '0', '0');
INSERT INTO `npc_spells_effects_entries` VALUES ('16', '16', '178', '0', '255', '90', '0', '0');
INSERT INTO `npc_spells_effects_entries` VALUES ('17', '17', '184', '0', '255', '10000', '-1', '0');
INSERT INTO `npc_spells_effects_entries` VALUES ('18', '18', '185', '0', '255', '100', '1', '0');
INSERT INTO `npc_spells_effects_entries` VALUES ('19', '19', '301', '0', '255', '100', '0', '0');
INSERT INTO `npc_spells_effects_entries` VALUES ('20', '20', '279', '0', '255', '50', '0', '0');
INSERT INTO `npc_spells_effects_entries` VALUES ('21', '21', '220', '0', '255', '2000', '1', '0');
INSERT INTO `npc_spells_effects_entries` VALUES ('22', '22', '40', '0', '255', '1', '0', '0');
INSERT INTO `npc_spells_effects_entries` VALUES ('23', '23', '360', '0', '255', '100', '13', '0');
INSERT INTO `npc_spells_effects_entries` VALUES ('24', '24', '274', '0', '255', '90', '0', '0');
INSERT INTO `npc_spells_effects_entries` VALUES ('25', '25', '294', '0', '255', '100', '200', '0');
INSERT INTO `npc_spells_effects_entries` VALUES ('26', '26', '120', '0', '255', '50', '0', '0');
INSERT INTO `npc_spells_effects_entries` VALUES ('27', '27', '382', '0', '255', '0', '55', '0');
INSERT INTO `npc_spells_effects_entries` VALUES ('28', '28', '296', '0', '255', '1000', '-1', '0');
INSERT INTO `npc_spells_effects_entries` VALUES ('29', '29', '296', '0', '255', '-50', '2', '0');
INSERT INTO `npc_spells_effects_entries` VALUES ('30', '30', '3', '0', '255', '60', '0', '0');

View File

@ -530,6 +530,7 @@ void NPC::AI_Start(uint32 iMoveDelay) {
if (NPCTypedata) {
AI_AddNPCSpells(NPCTypedata->npc_spells_id);
ProcessSpecialAbilities(NPCTypedata->special_abilities);
AI_AddNPCSpellsEffects(NPCTypedata->npc_spells_effects_id);
}
SendTo(GetX(), GetY(), GetZ());
@ -2298,6 +2299,7 @@ create table npc_spells_entries (
*/
bool IsSpellInList(DBnpcspells_Struct* spell_list, int16 iSpellID);
bool IsSpellEffectInList(DBnpcspellseffects_Struct* spelleffect_list, uint16 iSpellEffectID, int32 base, int32 limit, int32 max);
bool Compare_AI_Spells(AISpells_Struct i, AISpells_Struct j);
bool NPC::AI_AddNPCSpells(uint32 iDBSpellsID) {
@ -2372,6 +2374,105 @@ bool NPC::AI_AddNPCSpells(uint32 iDBSpellsID) {
return true;
}
bool NPC::AI_AddNPCSpellsEffects(uint32 iDBSpellsEffectsID) {
npc_spells_effects_id = iDBSpellsEffectsID;
AIspellsEffects.clear();
if (iDBSpellsEffectsID == 0)
return false;
DBnpcspellseffects_Struct* spell_effects_list = database.GetNPCSpellsEffects(iDBSpellsEffectsID);
if (!spell_effects_list) {
return false;
}
DBnpcspellseffects_Struct* parentlist = database.GetNPCSpellsEffects(spell_effects_list->parent_list);
uint32 i;
#if MobAI_DEBUG_Spells >= 10
std::cout << "Loading NPCSpellsEffects onto " << this->GetName() << ": dbspellseffectsid=" << iDBSpellsEffectsID;
if (spell_effects_list) {
std::cout << " (found, " << spell_effects_list->numentries << "), parentlist=" << spell_effects)list->parent_list;
if (spell_effects_list->parent_list) {
if (parentlist) {
std::cout << " (found, " << parentlist->numentries << ")";
}
else
std::cout << " (not found)";
}
}
else
std::cout << " (not found)";
std::cout << std::endl;
#endif
if (parentlist) {
for (i=0; i<parentlist->numentries; i++) {
if (GetLevel() >= parentlist->entries[i].minlevel && GetLevel() <= parentlist->entries[i].maxlevel && parentlist->entries[i].spelleffectid > 0) {
if (!IsSpellEffectInList(spell_effects_list, parentlist->entries[i].spelleffectid, parentlist->entries[i].base,
parentlist->entries[i].limit, parentlist->entries[i].max))
{
AddSpellEffectToNPCList(parentlist->entries[i].spelleffectid,
parentlist->entries[i].base, parentlist->entries[i].limit,
parentlist->entries[i].max);
}
}
}
}
for (i=0; i<spell_effects_list->numentries; i++) {
if (GetLevel() >= spell_effects_list->entries[i].minlevel && GetLevel() <= spell_effects_list->entries[i].maxlevel && spell_effects_list->entries[i].spelleffectid > 0) {
AddSpellEffectToNPCList(spell_effects_list->entries[i].spelleffectid,
spell_effects_list->entries[i].base, spell_effects_list->entries[i].limit,
spell_effects_list->entries[i].max);
}
}
return true;
}
void NPC::ApplyAISpellEffects(StatBonuses* newbon)
{
if (!AI_HasSpellsEffects())
return;
for(int i=0; i < AIspellsEffects.size(); i++)
{
ApplySpellsBonuses(0, 0, newbon, 0, false, 0,-1,
true, AIspellsEffects[i].spelleffectid, AIspellsEffects[i].base, AIspellsEffects[i].limit,AIspellsEffects[i].max);
}
return;
}
// adds a spell to the list, taking into account priority and resorting list as needed.
void NPC::AddSpellEffectToNPCList(uint16 iSpellEffectID, int32 base, int32 limit, int32 max)
{
if(!iSpellEffectID)
return;
HasAISpellEffects = true;
AISpellsEffects_Struct t;
t.spelleffectid = iSpellEffectID;
t.base = base;
t.limit = limit;
t.max = max;
AIspellsEffects.push_back(t);
}
bool IsSpellEffectInList(DBnpcspellseffects_Struct* spelleffect_list, uint16 iSpellEffectID, int32 base, int32 limit, int32 max) {
for (uint32 i=0; i < spelleffect_list->numentries; i++) {
if (spelleffect_list->entries[i].spelleffectid == iSpellEffectID && spelleffect_list->entries[i].base == base
&& spelleffect_list->entries[i].limit == limit && spelleffect_list->entries[i].max == max)
return true;
}
return false;
}
bool IsSpellInList(DBnpcspells_Struct* spell_list, int16 iSpellID) {
for (uint32 i=0; i < spell_list->numentries; i++) {
if (spell_list->entries[i].spellid == iSpellID)
@ -2436,6 +2537,7 @@ void NPC::AISpellsList(Client *c)
DBnpcspells_Struct* ZoneDatabase::GetNPCSpells(uint32 iDBSpellsID) {
if (iDBSpellsID == 0)
return 0;
if (!npc_spells_cache) {
npc_spells_maxid = GetMaxNPCSpellsID();
npc_spells_cache = new DBnpcspells_Struct*[npc_spells_maxid+1];
@ -2445,11 +2547,13 @@ DBnpcspells_Struct* ZoneDatabase::GetNPCSpells(uint32 iDBSpellsID) {
npc_spells_loadtried[i] = false;
}
}
if (iDBSpellsID > npc_spells_maxid)
return 0;
if (npc_spells_cache[iDBSpellsID]) { // it's in the cache, easy =)
return npc_spells_cache[iDBSpellsID];
}
else if (!npc_spells_loadtried[iDBSpellsID]) { // no reason to ask the DB again if we have failed once already
npc_spells_loadtried[iDBSpellsID] = true;
char errbuf[MYSQL_ERRMSG_SIZE];
@ -2460,7 +2564,7 @@ DBnpcspells_Struct* ZoneDatabase::GetNPCSpells(uint32 iDBSpellsID) {
if (RunQuery(query, MakeAnyLenString(&query, "SELECT id, parent_list, attack_proc, proc_chance from npc_spells where id=%d", iDBSpellsID), errbuf, &result)) {
safe_delete_array(query);
if (mysql_num_rows(result) == 1) {
row = mysql_fetch_row(result);
row = mysql_fetch_row(result);
uint32 tmpparent_list = atoi(row[1]);
int16 tmpattack_proc = atoi(row[2]);
uint8 tmpproc_chance = atoi(row[3]);
@ -2548,3 +2652,104 @@ uint32 ZoneDatabase::GetMaxNPCSpellsID() {
return 0;
}
DBnpcspellseffects_Struct* ZoneDatabase::GetNPCSpellsEffects(uint32 iDBSpellsEffectsID) {
if (iDBSpellsEffectsID == 0)
return 0;
if (!npc_spellseffects_cache) {
npc_spellseffects_maxid = GetMaxNPCSpellsEffectsID();
npc_spellseffects_cache = new DBnpcspellseffects_Struct*[npc_spellseffects_maxid+1];
npc_spellseffects_loadtried = new bool[npc_spellseffects_maxid+1];
for (uint32 i=0; i<=npc_spellseffects_maxid; i++) {
npc_spellseffects_cache[i] = 0;
npc_spellseffects_loadtried[i] = false;
}
}
if (iDBSpellsEffectsID > npc_spellseffects_maxid)
return 0;
if (npc_spellseffects_cache[iDBSpellsEffectsID]) { // it's in the cache, easy =)
return npc_spellseffects_cache[iDBSpellsEffectsID];
}
else if (!npc_spellseffects_loadtried[iDBSpellsEffectsID]) { // no reason to ask the DB again if we have failed once already
npc_spellseffects_loadtried[iDBSpellsEffectsID] = true;
char errbuf[MYSQL_ERRMSG_SIZE];
char *query = 0;
MYSQL_RES *result;
MYSQL_ROW row;
if (RunQuery(query, MakeAnyLenString(&query, "SELECT id, parent_list from npc_spells_effects where id=%d", iDBSpellsEffectsID), errbuf, &result)) {
safe_delete_array(query);
if (mysql_num_rows(result) == 1) {
row = mysql_fetch_row(result);
uint32 tmpparent_list = atoi(row[1]);
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_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;
npc_spellseffects_cache[iDBSpellsEffectsID]->numentries = mysql_num_rows(result);
int j = 0;
while ((row = mysql_fetch_row(result))) {
int spell_effect_id = atoi(row[0]);
npc_spellseffects_cache[iDBSpellsEffectsID]->entries[j].spelleffectid = spell_effect_id;
npc_spellseffects_cache[iDBSpellsEffectsID]->entries[j].minlevel = atoi(row[1]);
npc_spellseffects_cache[iDBSpellsEffectsID]->entries[j].maxlevel = atoi(row[2]);
npc_spellseffects_cache[iDBSpellsEffectsID]->entries[j].base = atoi(row[3]);
npc_spellseffects_cache[iDBSpellsEffectsID]->entries[j].limit = atoi(row[4]);
npc_spellseffects_cache[iDBSpellsEffectsID]->entries[j].max = atoi(row[5]);
j++;
}
mysql_free_result(result);
return npc_spellseffects_cache[iDBSpellsEffectsID];
}
else {
std::cerr << "Error in AddNPCSpells query1 '" << query << "' " << errbuf << std::endl;
safe_delete_array(query);
return 0;
}
}
else {
mysql_free_result(result);
}
}
else {
std::cerr << "Error in AddNPCSpells query1 '" << query << "' " << errbuf << std::endl;
safe_delete_array(query);
return 0;
}
return 0;
}
return 0;
}
uint32 ZoneDatabase::GetMaxNPCSpellsEffectsID() {
char errbuf[MYSQL_ERRMSG_SIZE];
char *query = 0;
MYSQL_RES *result;
MYSQL_ROW row;
if (RunQuery(query, MakeAnyLenString(&query, "SELECT max(id) from npc_spells_effects"), errbuf, &result)) {
safe_delete_array(query);
if (mysql_num_rows(result) == 1) {
row = mysql_fetch_row(result);
uint32 ret = 0;
if (row[0])
ret = atoi(row[0]);
mysql_free_result(result);
return ret;
}
mysql_free_result(result);
}
else {
std::cerr << "Error in GetMaxNPCSpellsEffectsID query '" << query << "' " << errbuf << std::endl;
safe_delete_array(query);
return 0;
}
return 0;
}

View File

@ -2120,7 +2120,8 @@ bool NPC::Death(Mob* killerMob, int32 damage, uint16 spell, SkillUseTypes attack
if(p_depop == true)
return false;
HasAISpellEffects = false;
BuffFadeAll();
uint8 killed_level = GetLevel();

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;
@ -1258,6 +1264,10 @@ void Mob::CalcSpellBonuses(StatBonuses* newbon)
}
}
//Applies any perma NPC spell bonuses from npc_spells_effects table.
if (IsNPC())
CastToNPC()->ApplyAISpellEffects(newbon);
//Removes the spell bonuses that are effected by a 'negate' debuff.
if (spellbonuses.NegateEffects){
for(i = 0; i < buff_count; i++) {
@ -1270,12 +1280,13 @@ void Mob::CalcSpellBonuses(StatBonuses* newbon)
if (GetClass() == BARD) newbon->ManaRegen = 0; // Bards do not get mana regen from spells.
}
void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* newbon, uint16 casterId, bool item_bonus, uint32 ticsremaining, int buffslot)
void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* newbon, uint16 casterId, bool item_bonus, uint32 ticsremaining, int buffslot,
bool IsAISpellEffect, uint16 effect_id, int32 se_base, int32 se_limit, int32 se_max)
{
int i, effect_value;
int i, effect_value, base2, max, effectid;
Mob *caster = nullptr;
if(!IsValidSpell(spell_id))
if(!IsAISpellEffect && !IsValidSpell(spell_id))
return;
if(casterId > 0)
@ -1283,19 +1294,35 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne
for (i = 0; i < EFFECT_COUNT; i++)
{
if(IsBlankSpellEffect(spell_id, i))
continue;
//Buffs/Item effects
if (!IsAISpellEffect) {
uint8 focus = IsFocusEffect(spell_id, i);
if (focus)
{
newbon->FocusEffects[focus] = spells[spell_id].effectid[i];
continue;
if(IsBlankSpellEffect(spell_id, i))
continue;
uint8 focus = IsFocusEffect(spell_id, i);
if (focus)
{
newbon->FocusEffects[focus] = spells[spell_id].effectid[i];
continue;
}
effectid = spells[spell_id].effectid[i];
effect_value = CalcSpellEffectValue(spell_id, i, casterlevel, caster, ticsremaining);
base2 = spells[spell_id].base2[i];
max = spells[spell_id].max[i];
}
//Use AISpellEffects
else {
effectid = effect_id;
effect_value = se_base;
base2 = se_limit;
max = se_max;
i = EFFECT_COUNT; //End the loop
}
effect_value = CalcSpellEffectValue(spell_id, i, casterlevel, caster, ticsremaining);
switch (spells[spell_id].effectid[i])
switch (effectid)
{
case SE_CurrentHP: //regens
if(effect_value > 0) {
@ -1631,27 +1658,27 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne
case SE_CriticalHitChance:
{
if (RuleB(Spells, AdditiveBonusValues) && item_bonus) {
if(spells[spell_id].base2[i] == -1)
if(base2 == -1)
newbon->CriticalHitChance[HIGHEST_SKILL+1] += effect_value;
else
newbon->CriticalHitChance[spells[spell_id].base2[i]] += effect_value;
newbon->CriticalHitChance[base2] += effect_value;
}
else if(effect_value < 0) {
if(spells[spell_id].base2[i] == -1 && newbon->CriticalHitChance[HIGHEST_SKILL+1] > effect_value)
if(base2 == -1 && newbon->CriticalHitChance[HIGHEST_SKILL+1] > effect_value)
newbon->CriticalHitChance[HIGHEST_SKILL+1] = effect_value;
else if(spells[spell_id].base2[i] != -1 && newbon->CriticalHitChance[spells[spell_id].base2[i]] > effect_value)
newbon->CriticalHitChance[spells[spell_id].base2[i]] = effect_value;
else if(base2 != -1 && newbon->CriticalHitChance[base2] > effect_value)
newbon->CriticalHitChance[base2] = effect_value;
}
else if(spells[spell_id].base2[i] == -1 && newbon->CriticalHitChance[HIGHEST_SKILL+1] < effect_value)
else if(base2 == -1 && newbon->CriticalHitChance[HIGHEST_SKILL+1] < effect_value)
newbon->CriticalHitChance[HIGHEST_SKILL+1] = effect_value;
else if(spells[spell_id].base2[i] != -1 && newbon->CriticalHitChance[spells[spell_id].base2[i]] < effect_value)
newbon->CriticalHitChance[spells[spell_id].base2[i]] = effect_value;
else if(base2 != -1 && newbon->CriticalHitChance[base2] < effect_value)
newbon->CriticalHitChance[base2] = effect_value;
break;
}
@ -1770,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;
}
@ -1812,7 +1839,7 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne
case SE_HundredHands:
{
if (RuleB(Spells, AdditiveBonusValues) && item_bonus)
newbon->HundredHands += spells[spell_id].base[i];
newbon->HundredHands += effect_value;
if (effect_value > 0 && effect_value > newbon->HundredHands)
newbon->HundredHands = effect_value; //Increase Weapon Delay
@ -1825,7 +1852,7 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne
{
if(newbon->MeleeSkillCheck < effect_value) {
newbon->MeleeSkillCheck = effect_value;
newbon->MeleeSkillCheckSkill = spells[spell_id].base2[i]==-1?255:spells[spell_id].base2[i];
newbon->MeleeSkillCheckSkill = base2==-1?255:base2;
}
break;
}
@ -1834,13 +1861,13 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne
{
if (RuleB(Spells, AdditiveBonusValues) && item_bonus){
if(spells[spell_id].base2[i] == -1)
if(base2 == -1)
newbon->HitChanceEffect[HIGHEST_SKILL+1] += effect_value;
else
newbon->HitChanceEffect[spells[spell_id].base2[i]] += effect_value;
newbon->HitChanceEffect[base2] += effect_value;
}
else if(spells[spell_id].base2[i] == -1){
else if(base2 == -1){
if ((effect_value < 0) && (newbon->HitChanceEffect[HIGHEST_SKILL+1] > effect_value))
newbon->HitChanceEffect[HIGHEST_SKILL+1] = effect_value;
@ -1852,12 +1879,12 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne
else {
if ((effect_value < 0) && (newbon->HitChanceEffect[spells[spell_id].base2[i]] > effect_value))
newbon->HitChanceEffect[spells[spell_id].base2[i]] = effect_value;
if ((effect_value < 0) && (newbon->HitChanceEffect[base2] > effect_value))
newbon->HitChanceEffect[base2] = effect_value;
else if (!newbon->HitChanceEffect[spells[spell_id].base2[i]] ||
((newbon->HitChanceEffect[spells[spell_id].base2[i]] > 0) && (newbon->HitChanceEffect[spells[spell_id].base2[i]] < effect_value)))
newbon->HitChanceEffect[spells[spell_id].base2[i]] = effect_value;
else if (!newbon->HitChanceEffect[base2] ||
((newbon->HitChanceEffect[base2] > 0) && (newbon->HitChanceEffect[base2] < effect_value)))
newbon->HitChanceEffect[base2] = effect_value;
}
break;
@ -1866,19 +1893,19 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne
case SE_DamageModifier:
{
if(spells[spell_id].base2[i] == -1)
if(base2 == -1)
newbon->DamageModifier[HIGHEST_SKILL+1] += effect_value;
else
newbon->DamageModifier[spells[spell_id].base2[i]] += effect_value;
newbon->DamageModifier[base2] += effect_value;
break;
}
case SE_MinDamageModifier:
{
if(spells[spell_id].base2[i] == -1)
if(base2 == -1)
newbon->MinDamageModifier[HIGHEST_SKILL+1] += effect_value;
else
newbon->MinDamageModifier[spells[spell_id].base2[i]] += effect_value;
newbon->MinDamageModifier[base2] += effect_value;
break;
}
@ -1921,8 +1948,8 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne
newbon->DeathSave[0] = effect_value; //1='Partial' 2='Full'
newbon->DeathSave[1] = buffslot;
//These are used in later expansion spell effects.
newbon->DeathSave[2] = spells[spell_id].base2[i];//Min level for HealAmt
newbon->DeathSave[3] = spells[spell_id].max[i];//HealAmt
newbon->DeathSave[2] = base2;//Min level for HealAmt
newbon->DeathSave[3] = max;//HealAmt
}
break;
}
@ -1937,7 +1964,7 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne
else if(newbon->DivineSaveChance[0] < effect_value)
{
newbon->DivineSaveChance[0] = effect_value;
newbon->DivineSaveChance[1] = spells[spell_id].base2[i];
newbon->DivineSaveChance[1] = base2;
//SetDeathSaveChance(true);
}
break;
@ -1972,10 +1999,10 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne
case SE_SkillDamageTaken:
{
if(spells[spell_id].base2[i] == -1)
if(base2 == -1)
newbon->SkillDmgTaken[HIGHEST_SKILL+1] += effect_value;
else
newbon->SkillDmgTaken[spells[spell_id].base2[i]] += effect_value;
newbon->SkillDmgTaken[base2] += effect_value;
break;
}
@ -2000,8 +2027,8 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne
{
newbon->CriticalSpellChance += effect_value;
if (spells[spell_id].base2[i] > newbon->SpellCritDmgIncNoStack)
newbon->SpellCritDmgIncNoStack = spells[spell_id].base2[i];
if (base2 > newbon->SpellCritDmgIncNoStack)
newbon->SpellCritDmgIncNoStack = base2;
break;
}
@ -2053,9 +2080,9 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne
if(!newbon->SpellOnKill[e])
{
// Base2 = Spell to fire | Base1 = % chance | Base3 = min level
newbon->SpellOnKill[e] = spells[spell_id].base2[i];
newbon->SpellOnKill[e] = base2;
newbon->SpellOnKill[e+1] = effect_value;
newbon->SpellOnKill[e+2] = spells[spell_id].max[i];
newbon->SpellOnKill[e+2] = max;
break;
}
}
@ -2069,7 +2096,7 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne
if(!newbon->SpellOnDeath[e])
{
// Base2 = Spell to fire | Base1 = % chance
newbon->SpellOnDeath[e] = spells[spell_id].base2[i];
newbon->SpellOnDeath[e] = base2;
newbon->SpellOnDeath[e+1] = effect_value;
break;
}
@ -2079,26 +2106,26 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne
case SE_CriticalDamageMob:
{
if(spells[spell_id].base2[i] == -1)
if(base2 == -1)
newbon->CritDmgMob[HIGHEST_SKILL+1] += effect_value;
else
newbon->CritDmgMob[spells[spell_id].base2[i]] += effect_value;
newbon->CritDmgMob[base2] += effect_value;
break;
}
case SE_ReduceSkillTimer:
{
if(newbon->SkillReuseTime[spells[spell_id].base2[i]] < effect_value)
newbon->SkillReuseTime[spells[spell_id].base2[i]] = effect_value;
if(newbon->SkillReuseTime[base2] < effect_value)
newbon->SkillReuseTime[base2] = effect_value;
break;
}
case SE_SkillDamageAmount:
{
if(spells[spell_id].base2[i] == -1)
if(base2 == -1)
newbon->SkillDamageAmount[HIGHEST_SKILL+1] += effect_value;
else
newbon->SkillDamageAmount[spells[spell_id].base2[i]] += effect_value;
newbon->SkillDamageAmount[base2] += effect_value;
break;
}
@ -2188,10 +2215,10 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne
case SE_SkillDamageAmount2:
{
if(spells[spell_id].base2[i] == -1)
if(base2 == -1)
newbon->SkillDamageAmount2[HIGHEST_SKILL+1] += effect_value;
else
newbon->SkillDamageAmount2[spells[spell_id].base2[i]] += effect_value;
newbon->SkillDamageAmount2[base2] += effect_value;
break;
}
@ -2219,7 +2246,7 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne
if (newbon->MeleeThresholdGuard[0] < effect_value){
newbon->MeleeThresholdGuard[0] = effect_value;
newbon->MeleeThresholdGuard[1] = buffslot;
newbon->MeleeThresholdGuard[2] = spells[spell_id].base2[i];
newbon->MeleeThresholdGuard[2] = base2;
}
break;
}
@ -2229,7 +2256,7 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne
if (newbon->SpellThresholdGuard[0] < effect_value){
newbon->SpellThresholdGuard[0] = effect_value;
newbon->SpellThresholdGuard[1] = buffslot;
newbon->SpellThresholdGuard[2] = spells[spell_id].base2[i];
newbon->SpellThresholdGuard[2] = base2;
}
break;
}
@ -2263,20 +2290,20 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne
case SE_TriggerMeleeThreshold:
{
if (newbon->TriggerMeleeThreshold[2] < spells[spell_id].base2[i]){
if (newbon->TriggerMeleeThreshold[2] < base2){
newbon->TriggerMeleeThreshold[0] = effect_value;
newbon->TriggerMeleeThreshold[1] = buffslot;
newbon->TriggerMeleeThreshold[2] = spells[spell_id].base2[i];
newbon->TriggerMeleeThreshold[2] = base2;
}
break;
}
case SE_TriggerSpellThreshold:
{
if (newbon->TriggerSpellThreshold[2] < spells[spell_id].base2[i]){
if (newbon->TriggerSpellThreshold[2] < base2){
newbon->TriggerSpellThreshold[0] = effect_value;
newbon->TriggerSpellThreshold[1] = buffslot;
newbon->TriggerSpellThreshold[2] = spells[spell_id].base2[i];
newbon->TriggerSpellThreshold[2] = base2;
}
break;
}
@ -2291,7 +2318,7 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne
case SE_ShieldEquipDmgMod:
newbon->ShieldEquipDmgMod[0] += effect_value;
newbon->ShieldEquipDmgMod[1] += spells[spell_id].base2[i];
newbon->ShieldEquipDmgMod[1] += base2;
break;
case SE_BlockBehind:
@ -2380,7 +2407,7 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne
break;
case SE_AddSingingMod:
switch (spells[spell_id].base2[i])
switch (base2)
{
case ItemTypeWindInstrument:
newbon->windMod += effect_value;
@ -2473,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] = spells[spell_id].base2[i]) && (newbon->SEResist[e+1] < effect_value)) ){
newbon->SEResist[e] = spells[spell_id].base2[i];
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;
}
}
@ -2493,7 +2524,7 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne
case SE_GiveDoubleRiposte:
{
//Only allow for regular double riposte chance.
if(newbon->GiveDoubleRiposte[spells[spell_id].base2[i]] == 0){
if(newbon->GiveDoubleRiposte[base2] == 0){
if(newbon->GiveDoubleRiposte[0] < effect_value)
newbon->GiveDoubleRiposte[0] = effect_value;
}
@ -2504,7 +2535,7 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne
{
if(newbon->SlayUndead[1] < effect_value)
newbon->SlayUndead[0] = effect_value; // Rate
newbon->SlayUndead[1] = spells[spell_id].base2[i]; // Damage Modifier
newbon->SlayUndead[1] = base2; // Damage Modifier
break;
}
@ -2520,7 +2551,7 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne
case SE_ImprovedTaunt:
if (newbon->ImprovedTaunt[0] < effect_value) {
newbon->ImprovedTaunt[0] = effect_value;
newbon->ImprovedTaunt[1] = spells[spell_id].base2[i];
newbon->ImprovedTaunt[1] = base2;
newbon->ImprovedTaunt[2] = buffslot;
}
break;
@ -2531,7 +2562,7 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne
break;
case SE_FrenziedDevastation:
newbon->FrenziedDevastation += spells[spell_id].base2[i];
newbon->FrenziedDevastation += base2;
break;
case SE_Root:
@ -2577,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;
}
}
}
}

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;
}

View File

@ -4140,10 +4140,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);
}
}
}
@ -4156,19 +4156,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);
}
}
@ -4183,21 +4183,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);
}
}
}

View File

@ -182,7 +182,8 @@ public:
bool IsBeneficialAllowed(Mob *target);
virtual int GetCasterLevel(uint16 spell_id);
void ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* newbon, uint16 casterID = 0,
bool item_bonus = false, uint32 ticsremaining = 0, int buffslot = -1);
bool item_bonus = false, uint32 ticsremaining = 0, int buffslot = -1,
bool IsAISpellEffect = false, uint16 effect_id = 0, int32 se_base = 0, int32 se_limit = 0, int32 se_max = 0);
void NegateSpellsBonuses(uint16 spell_id);
virtual float GetActSpellRange(uint16 spell_id, float range, bool IsBard = false) { return range;}
virtual int32 GetActSpellDamage(uint16 spell_id, int32 value, Mob* target = nullptr) { return value; }

View File

@ -231,6 +231,7 @@ NPC::NPC(const NPCType* d, Spawn2* in_respawn, float x, float y, float z, float
npc_spells_id = 0;
HasAISpell = false;
HasAISpellEffects = false;
if(GetClass() == MERCERNARY_MASTER && RuleB(Mercs, AllowMercs))
{

View File

@ -66,6 +66,13 @@ struct AISpells_Struct {
int16 resist_adjust;
};
struct AISpellsEffects_Struct {
uint16 spelleffectid;
int32 base;
int32 limit;
int32 max;
};
class AA_SwarmPetInfo;
class NPC : public Mob
@ -96,8 +103,11 @@ public:
virtual void AI_Stop();
void AI_DoMovement();
bool AI_AddNPCSpells(uint32 iDBSpellsID);
bool AI_AddNPCSpellsEffects(uint32 iDBSpellsEffectsID);
virtual bool AI_EngagedCastCheck();
bool AI_HasSpells() { return HasAISpell; }
bool AI_HasSpellsEffects() { return HasAISpellEffects; }
void ApplyAISpellEffects(StatBonuses* newbon);
virtual bool AI_PursueCastCheck();
virtual bool AI_IdleCastCheck();
@ -289,6 +299,7 @@ public:
inline void GiveNPCTypeData(NPCType *ours) { NPCTypedata_ours = ours; }
inline const uint32 GetNPCSpellsID() const { return npc_spells_id; }
inline const uint32 GetNPCSpellsEffectsID() const { return npc_spells_effects_id; }
ItemList itemlist; //kathgar - why is this public? Doing other things or I would check the code
@ -339,6 +350,7 @@ public:
uint32 GetAdventureTemplate() const { return adventure_template_id; }
void AddSpellToNPCList(int16 iPriority, int16 iSpellID, uint16 iType, int16 iManaCost, int32 iRecastDelay, int16 iResistAdjust);
void AddSpellEffectToNPCList(uint16 iSpellEffectID, int32 base, int32 limit, int32 max);
void RemoveSpellFromNPCList(int16 spell_id);
Timer *GetRefaceTimer() const { return reface_timer; }
const uint32 GetAltCurrencyType() const { return NPCTypedata->alt_currency_type; }
@ -400,8 +412,11 @@ protected:
bool HasAISpell;
virtual bool AICastSpell(Mob* tar, uint8 iChance, uint16 iSpellTypes);
virtual bool AIDoSpellCast(uint8 i, Mob* tar, int32 mana_cost, uint32* oDontDoAgainBefore = 0);
uint32 npc_spells_effects_id;
std::vector<AISpellsEffects_Struct> AIspellsEffects;
bool HasAISpellEffects;
uint32 max_dmg;
uint32 min_dmg;
int32 accuracy_rating;

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;

View File

@ -33,8 +33,11 @@ ZoneDatabase::ZoneDatabase(const char* host, const char* user, const char* passw
void ZoneDatabase::ZDBInitVars() {
memset(door_isopen_array, 0, sizeof(door_isopen_array));
npc_spells_maxid = 0;
npc_spellseffects_maxid = 0;
npc_spells_cache = 0;
npc_spellseffects_cache = 0;
npc_spells_loadtried = 0;
npc_spellseffects_loadtried = 0;
max_faction = 0;
faction_array = nullptr;
}
@ -49,6 +52,14 @@ ZoneDatabase::~ZoneDatabase() {
}
safe_delete_array(npc_spells_loadtried);
if (npc_spellseffects_cache) {
for (x=0; x<=npc_spellseffects_maxid; x++) {
safe_delete_array(npc_spellseffects_cache[x]);
}
safe_delete_array(npc_spellseffects_cache);
}
safe_delete_array(npc_spellseffects_loadtried);
if (faction_array != nullptr) {
for (x=0; x <= max_faction; x++) {
if (faction_array[x] != 0)
@ -1053,6 +1064,7 @@ const NPCType* ZoneDatabase::GetNPCType (uint32 id) {
"npc_types.attack_count,"
"npc_types.special_abilities,"
"npc_types.npc_spells_id,"
"npc_types.npc_spells_effects_id,"
"npc_types.d_meele_texture1,"
"npc_types.d_meele_texture2,"
"npc_types.prim_melee_type,"
@ -1151,6 +1163,7 @@ const NPCType* ZoneDatabase::GetNPCType (uint32 id) {
tmpNPCType->attack_count = atoi(row[r++]);
strn0cpy(tmpNPCType->special_abilities, row[r++], 512);
tmpNPCType->npc_spells_id = atoi(row[r++]);
tmpNPCType->npc_spells_effects_id = atoi(row[r++]);
tmpNPCType->d_meele_texture1 = atoi(row[r++]);
tmpNPCType->d_meele_texture2 = atoi(row[r++]);
tmpNPCType->prim_melee_type = atoi(row[r++]);

View File

@ -30,6 +30,17 @@ struct DBnpcspells_entries_Struct {
};
#pragma pack()
#pragma pack(1)
struct DBnpcspellseffects_entries_Struct {
int16 spelleffectid;
uint8 minlevel;
uint8 maxlevel;
int32 base;
int32 limit;
int32 max;
};
#pragma pack()
struct DBnpcspells_Struct {
uint32 parent_list;
int16 attack_proc;
@ -38,6 +49,12 @@ struct DBnpcspells_Struct {
DBnpcspells_entries_Struct entries[0];
};
struct DBnpcspellseffects_Struct {
uint32 parent_list;
uint32 numentries;
DBnpcspellseffects_entries_Struct entries[0];
};
struct DBTradeskillRecipe_Struct {
SkillUseTypes tradeskill;
int16 skill_needed;
@ -345,7 +362,9 @@ public:
void AddLootTableToNPC(NPC* npc,uint32 loottable_id, ItemList* itemlist, uint32* copper, uint32* silver, uint32* gold, uint32* plat);
void AddLootDropToNPC(NPC* npc,uint32 lootdrop_id, ItemList* itemlist, uint8 droplimit, uint8 mindrop);
uint32 GetMaxNPCSpellsID();
uint32 GetMaxNPCSpellsEffectsID();
DBnpcspells_Struct* GetNPCSpells(uint32 iDBSpellsID);
DBnpcspellseffects_Struct* GetNPCSpellsEffects(uint32 iDBSpellsEffectsID);
/*
* Mercs
@ -475,8 +494,11 @@ protected:
uint32 max_faction;
Faction** faction_array;
uint32 npc_spells_maxid;
uint32 npc_spellseffects_maxid;
DBnpcspells_Struct** npc_spells_cache;
bool* npc_spells_loadtried;
DBnpcspellseffects_Struct** npc_spellseffects_cache;
bool* npc_spellseffects_loadtried;
uint8 door_isopen_array[255];
};

View File

@ -53,6 +53,7 @@ struct NPCType
uint8 helmtexture;
uint32 loottable_id;
uint32 npc_spells_id;
uint32 npc_spells_effects_id;
int32 npc_faction_id;
uint32 merchanttype;
uint32 alt_currency_type;