diff --git a/common/emu_oplist.h b/common/emu_oplist.h index e695c94a9..29a20cb14 100644 --- a/common/emu_oplist.h +++ b/common/emu_oplist.h @@ -408,6 +408,7 @@ N(OP_ReloadUI), N(OP_RemoveAllDoors), N(OP_RemoveBlockedBuffs), N(OP_RemoveNimbusEffect), +N(OP_RemoveTrap), N(OP_Report), N(OP_ReqClientSpawn), N(OP_ReqNewZone), @@ -523,6 +524,7 @@ N(OP_TributeToggle), N(OP_TributeUpdate), N(OP_Untargetable), N(OP_UpdateAA), +N(OP_UpdateAura), N(OP_UpdateLeadershipAA), N(OP_VetClaimReply), N(OP_VetClaimRequest), diff --git a/common/eq_packet_structs.h b/common/eq_packet_structs.h index 6e853b4bd..79b8f7250 100644 --- a/common/eq_packet_structs.h +++ b/common/eq_packet_structs.h @@ -5333,6 +5333,24 @@ struct fling_struct { /* 28 */ }; +// used when action == 0 +struct AuraCreate_Struct { +/* 00 */ uint32 action; // 0 = add, 1 = delete, 2 = reset +/* 04 */ uint32 type; // unsure -- normal auras show 1 clicky (ex. Circle of Power) show 0 +/* 08 */ char aura_name[64]; +/* 72 */ uint32 entity_id; +/* 76 */ uint32 icon; +/* 80 */ +}; + +// used when action == 1 +struct AuraDestory_Struct { +/* 00 */ uint32 action; // 0 = add, 1 = delete, 2 = reset +/* 04 */ uint32 entity_id; +/* 08 */ +}; +// I think we can assume it's just action for 2, client doesn't seem to do anything with the rest of the data in that case + // Restore structure packing to default #pragma pack() diff --git a/common/net/daybreak_connection.cpp b/common/net/daybreak_connection.cpp index 283f8f93d..095fab525 100644 --- a/common/net/daybreak_connection.cpp +++ b/common/net/daybreak_connection.cpp @@ -382,13 +382,8 @@ void EQ::Net::DaybreakConnection::ProcessPacket(Packet &p) return; } - if (p.GetInt8(0) != 0) { - LogF(Logs::Detail, Logs::Netcode, "Error parsing packet, did not start with a 0 frame, not a valid protocol packet."); - return; - } - auto opcode = p.GetInt8(1); - if (opcode == OP_KeepAlive || opcode == OP_OutboundPing) { + if (p.GetInt8(0) == 0 && (opcode == OP_KeepAlive || opcode == OP_OutboundPing)) { return; } @@ -407,10 +402,16 @@ void EQ::Net::DaybreakConnection::ProcessPacket(Packet &p) for (int i = 1; i >= 0; --i) { switch (m_encode_passes[i]) { case EncodeCompression: - Decompress(temp, DaybreakHeader::size(), temp.Length() - DaybreakHeader::size()); + if(temp.GetInt8(0) == 0) + Decompress(temp, DaybreakHeader::size(), temp.Length() - DaybreakHeader::size()); + else + Decompress(temp, 1, temp.Length() - 1); break; case EncodeXOR: - Decode(temp, DaybreakHeader::size(), temp.Length() - DaybreakHeader::size()); + if (temp.GetInt8(0) == 0) + Decode(temp, DaybreakHeader::size(), temp.Length() - DaybreakHeader::size()); + else + Decode(temp, 1, temp.Length() - 1); break; default: break; @@ -425,7 +426,10 @@ void EQ::Net::DaybreakConnection::ProcessPacket(Packet &p) for (int i = 1; i >= 0; --i) { switch (m_encode_passes[i]) { case EncodeXOR: - Decode(temp, DaybreakHeader::size(), temp.Length() - DaybreakHeader::size()); + if (temp.GetInt8(0) == 0) + Decode(temp, DaybreakHeader::size(), temp.Length() - DaybreakHeader::size()); + else + Decode(temp, 1, temp.Length() - 1); break; default: break; @@ -1187,24 +1191,21 @@ void EQ::Net::DaybreakConnection::InternalSend(Packet &p) if (PacketCanBeEncoded(p)) { DynamicPacket out; - - if (p.GetUInt8(0) != 0) { - out.PutUInt8(0, 0); - out.PutUInt8(1, OP_Combined); - out.PutUInt8(2, p.Length()); - out.PutPacket(3, p); - } - else { - out.PutPacket(0, p); - } + out.PutPacket(0, p); for (int i = 0; i < 2; ++i) { switch (m_encode_passes[i]) { case EncodeCompression: - Compress(out, DaybreakHeader::size(), out.Length() - DaybreakHeader::size()); + if(out.GetInt8(0) == 0) + Compress(out, DaybreakHeader::size(), out.Length() - DaybreakHeader::size()); + else + Compress(out, 1, out.Length() - 1); break; case EncodeXOR: - Encode(out, DaybreakHeader::size(), out.Length() - DaybreakHeader::size()); + if (out.GetInt8(0) == 0) + Encode(out, DaybreakHeader::size(), out.Length() - DaybreakHeader::size()); + else + Encode(out, 1, out.Length() - 1); break; default: break; diff --git a/common/net/eqstream.cpp b/common/net/eqstream.cpp index 26fdf1d8e..ffa441939 100644 --- a/common/net/eqstream.cpp +++ b/common/net/eqstream.cpp @@ -81,7 +81,12 @@ void EQ::Net::EQStream::QueuePacket(const EQApplicationPacket *p, bool ack_req) break; } - m_connection->QueuePacket(out); + if (ack_req) { + m_connection->QueuePacket(out); + } + else { + m_connection->QueuePacket(out, 0, false); + } } } diff --git a/common/shareddb.cpp b/common/shareddb.cpp index 2efb06913..331826ef2 100644 --- a/common/shareddb.cpp +++ b/common/shareddb.cpp @@ -1674,6 +1674,7 @@ void SharedDatabase::LoadSpells(void *data, int max_spells) { for (y = 0; y < 16; y++) sp[tempid].deities[y]=atoi(row[126+y]); + sp[tempid].new_icon=atoi(row[144]); sp[tempid].uninterruptable=atoi(row[146]) != 0; sp[tempid].ResistDiff=atoi(row[147]); sp[tempid].dot_stacking_exempt = atoi(row[148]) != 0; diff --git a/common/spdat.h b/common/spdat.h index bd1dacdf8..605e9a6cf 100644 --- a/common/spdat.h +++ b/common/spdat.h @@ -562,9 +562,9 @@ typedef enum { #define SE_LimitManaMin 348 // implemented #define SE_ShieldEquipDmgMod 349 // implemented[AA] Increase melee base damage (indirectly increasing hate) when wearing a shield. #define SE_ManaBurn 350 // implemented - Drains mana for damage/heal at a defined ratio up to a defined maximum amount of mana. -//#define SE_PersistentEffect 351 // *not implemented. creates a trap/totem that casts a spell (spell id + base1?) when anything comes near it. can probably make a beacon for this -//#define SE_IncreaseTrapCount 352 // *not implemented - looks to be some type of invulnerability? Test ITC (8755) -//#define SE_AdditionalAura 353 // *not implemented - allows use of more than 1 aura, aa effect +#define SE_PersistentEffect 351 // *not implemented. creates a trap/totem that casts a spell (spell id + base1?) when anything comes near it. can probably make a beacon for this +#define SE_IncreaseTrapCount 352 // *not implemented - looks to be some type of invulnerability? Test ITC (8755) +#define SE_AdditionalAura 353 // *not implemented - allows use of more than 1 aura, aa effect //#define SE_DeactivateAllTraps 354 // *not implemented - looks to be some type of invulnerability? Test DAT (8757) //#define SE_LearnTrap 355 // *not implemented - looks to be some type of invulnerability? Test LT (8758) //#define SE_ChangeTriggerType 356 // not used @@ -757,7 +757,7 @@ struct SPDat_Spell_Struct // -- DIETY_BERTOXXULOUS ... DIETY_VEESHAN /* 142 */ //int8 npc_no_cast; // 142: between 0 & 100 -- NPC_NO_CAST /* 143 */ //int ai_pt_bonus; // 143: always set to 0, client doesn't save this -- AI_PT_BONUS -/* 144 */ //int16 new_icon // Spell icon used by the client in uifiles/default/spells??.tga, both for spell gems & buff window. Looks to depreciate icon & memicon -- NEW_ICON +/* 144 */ int16 new_icon; // Spell icon used by the client in uifiles/default/spells??.tga, both for spell gems & buff window. Looks to depreciate icon & memicon -- NEW_ICON /* 145 */ //int16 spellanim; // Doesn't look like it's the same as #doanim, so not sure what this is, particles I think -- SPELL_EFFECT_INDEX /* 146 */ bool uninterruptable; // Looks like anything != 0 is uninterruptable. Values are mostly -1, 0, & 1 (Fetid Breath = 90?) -- NO_INTERRUPT /* 147 */ int16 ResistDiff; // -- RESIST_MOD diff --git a/common/version.h b/common/version.h index a1cfbce16..52af87872 100644 --- a/common/version.h +++ b/common/version.h @@ -30,7 +30,7 @@ Manifest: https://github.com/EQEmu/Server/blob/master/utils/sql/db_update_manifest.txt */ -#define CURRENT_BINARY_DATABASE_VERSION 9113 +#define CURRENT_BINARY_DATABASE_VERSION 9114 #ifdef BOTS #define CURRENT_BINARY_BOTS_DATABASE_VERSION 9017 #else diff --git a/utils/patches/patch_RoF.conf b/utils/patches/patch_RoF.conf index a746804d9..339bfd800 100644 --- a/utils/patches/patch_RoF.conf +++ b/utils/patches/patch_RoF.conf @@ -672,3 +672,7 @@ OP_Some3ByteHPUpdate=0x0000 # initial HP update for mobs OP_InitialHPUpdate=0x0000 OP_ItemRecastDelay=0x57ed + +#aura related +OP_UpdateAura=0x1fa9 +OP_RemoveTrap=0x6a4d diff --git a/utils/patches/patch_RoF2.conf b/utils/patches/patch_RoF2.conf index ee9b0e646..e63e5e9e9 100644 --- a/utils/patches/patch_RoF2.conf +++ b/utils/patches/patch_RoF2.conf @@ -675,3 +675,7 @@ OP_RAWOutOfSession=0x0000 # we need to document the differences between these packets to make identifying them easier OP_Some3ByteHPUpdate=0x0000 # initial HP update for mobs OP_InitialHPUpdate=0x0000 + +#aura related +OP_UpdateAura=0x1456 +OP_RemoveTrap=0x71da diff --git a/utils/patches/patch_SoD.conf b/utils/patches/patch_SoD.conf index 08e072d33..ed7a19715 100644 --- a/utils/patches/patch_SoD.conf +++ b/utils/patches/patch_SoD.conf @@ -669,3 +669,7 @@ OP_Some3ByteHPUpdate=0x0000 # initial HP update for mobs OP_InitialHPUpdate=0x0000 # OP_ItemRecastDelay=0x15c4 + +#aura related +OP_UpdateAura=0x169a +OP_RemoveTrap=0x4bb6 diff --git a/utils/patches/patch_SoF.conf b/utils/patches/patch_SoF.conf index d758daea9..7ed41ff4a 100644 --- a/utils/patches/patch_SoF.conf +++ b/utils/patches/patch_SoF.conf @@ -658,3 +658,7 @@ OP_ItemRecastDelay=0x0ada #OP_NpcMoveUpdate=0x0d11 #SEQ 10/07/08 --NEW FROM SEQ #OP_Zone_MissingName01=0x0000 # #new titles avaliable: # + +#aura related +OP_UpdateAura=0x62a9 +OP_RemoveTrap=0x7bd9 diff --git a/utils/patches/patch_UF.conf b/utils/patches/patch_UF.conf index 2002a85f9..af698d52a 100644 --- a/utils/patches/patch_UF.conf +++ b/utils/patches/patch_UF.conf @@ -685,3 +685,7 @@ OP_ItemRecastDelay=0x82d7 # unhandled OP_ShieldGroup=0x23a1 + +#aura related +OP_UpdateAura=0x2480 +OP_RemoveTrap=0x0115 diff --git a/utils/sql/db_update_manifest.txt b/utils/sql/db_update_manifest.txt index 8070c328e..faccf9e48 100644 --- a/utils/sql/db_update_manifest.txt +++ b/utils/sql/db_update_manifest.txt @@ -367,6 +367,7 @@ 9111|2017_06_24_saylink_index.sql|SHOW INDEX FROM `saylink` WHERE `key_name` = 'phrase_index'|empty| 9112|2017_06_24_rule_values_expand.sql|SHOW COLUMNS FROM rule_values WHERE Field = 'rule_value' and Type = 'varchar(30)'|empty| 9113|2017_07_19_show_name.sql|SHOW COLUMNS FROM `npc_types` LIKE 'show_name'|empty| +9114|2017_07_22_aura.sql|SHOW TABLES LIKE 'auras'|empty| # Upgrade conditions: # This won't be needed after this system is implemented, but it is used database that are not diff --git a/utils/sql/git/required/2017_07_22_aura.sql b/utils/sql/git/required/2017_07_22_aura.sql new file mode 100644 index 000000000..409df9c72 --- /dev/null +++ b/utils/sql/git/required/2017_07_22_aura.sql @@ -0,0 +1,127 @@ +CREATE TABLE `auras` ( + `type` INT(10) NOT NULL, + `npc_type` INT(10) NOT NULL, + `name` VARCHAR(64) NOT NULL, + `spell_id` INT(10) NOT NULL, + `distance` INT(10) NOT NULL DEFAULT 60, + `aura_type` INT(10) NOT NULL DEFAULT 1, + `spawn_type` INT(10) NOT NULL DEFAULT 0, + `movement` INT(10) NOT NULL DEFAULT 0, + `duration` INT(10) NOT NULL DEFAULT 5400, + `icon` INT(10) NOT NULL DEFAULT -1, + `cast_time` INT(10) NOT NULL DEFAULT 0, + PRIMARY KEY(`type`) +); + +CREATE TABLE `character_auras` ( + `id` INT(10) NOT NULL, + `slot` TINYINT(10) NOT NULL, + `spell_id` INT(10) NOT NULL, + PRIMARY KEY (`id`, `slot`) +); + +SELECT IFNULL((MAX(id) + 1), 2000000) INTO @suggestedid FROM npc_types where id LIKE '2000___'; +INSERT INTO npc_types SET id=@suggestedid, name="IOAuraOfTheMuse55", lastname="", level="55", race="127", class="62", bodytype="11", hp="4027.6216", mana="0.0000", gender="0", texture="0", helmtexture="0", herosforgemodel="0", size="2", hp_regen_rate="0", mana_regen_rate="0", loottable_id="0", npc_spells_id="0", mindmg="52.7514", maxdmg="166.8270", attack_count="-1", special_abilities="12,1^13,1^14,1^15,1^16,1^17,1^18,1^19,1^20,1^21,1^22,1^23,1^24,1^25,1^28,1^31,1^35,1^", aggroradius="70", assistradius="0", face="0", luclin_hairstyle="0", luclin_haircolor="0", luclin_eyecolor="0", luclin_eyecolor2="0", luclin_beardcolor="0", luclin_beard="0", drakkin_heritage="0", drakkin_tattoo="0", drakkin_details="0", armortint_red="0", armortint_green="0", armortint_blue="0", d_melee_texture1="0", d_melee_texture2="0", prim_melee_type="28", sec_melee_type="28", runspeed="1.25", MR="21.1027", CR="21.1027", DR="21.1027", FR="21.1027", PR="21.1027", Corrup="21.1027", PhR="48.3333", see_invis="0", see_invis_undead="0", qglobal="0", AC="257.3784", npc_aggro="0", spawn_limit="0", attack_delay="29.4486", findable="0", STR="205.0000", STA="205.0000", DEX="205.0000", AGI="205.0000", _INT="205.0000", WIS="205.0000", CHA="205.0000", see_hide="0", see_improved_hide="0", trackable="0", ATK="0", Accuracy="0", Avoidance="0", slow_mitigation="0", version="0", maxlevel="0", scalerate="100", private_corpse="0", unique_spawn_by_name="0", underwater="0", isquest="0", emoteid="0", spellscale="100", healscale="100", no_target_hotkey="0", raid_target="0", light="0", ignore_despawn="0", show_name="0"; +INSERT INTO auras SET type=8926, npc_type=@suggestedid, name="Aura_of_Insight", spell_id=8939, distance=60, aura_type=1, spawn_type=0, movement=0, duration=5400, icon=99, cast_time=-1; + +SELECT IFNULL((MAX(id) + 1), 2000000) INTO @suggestedid FROM npc_types where id LIKE '2000___'; +INSERT INTO npc_types SET id=@suggestedid, name="IOAuraOfTheMuse", lastname="", level="70", race="127", class="62", bodytype="11", hp="4027.6216", mana="0.0000", gender="0", texture="0", helmtexture="0", herosforgemodel="0", size="2", hp_regen_rate="0", mana_regen_rate="0", loottable_id="0", npc_spells_id="0", mindmg="52.7514", maxdmg="166.8270", attack_count="-1", special_abilities="12,1^13,1^14,1^15,1^16,1^17,1^18,1^19,1^20,1^21,1^22,1^23,1^24,1^25,1^28,1^31,1^35,1^", aggroradius="70", assistradius="0", face="0", luclin_hairstyle="0", luclin_haircolor="0", luclin_eyecolor="0", luclin_eyecolor2="0", luclin_beardcolor="0", luclin_beard="0", drakkin_heritage="0", drakkin_tattoo="0", drakkin_details="0", armortint_red="0", armortint_green="0", armortint_blue="0", d_melee_texture1="0", d_melee_texture2="0", prim_melee_type="28", sec_melee_type="28", runspeed="1.25", MR="21.1027", CR="21.1027", DR="21.1027", FR="21.1027", PR="21.1027", Corrup="21.1027", PhR="48.3333", see_invis="0", see_invis_undead="0", qglobal="0", AC="257.3784", npc_aggro="0", spawn_limit="0", attack_delay="29.4486", findable="0", STR="205.0000", STA="205.0000", DEX="205.0000", AGI="205.0000", _INT="205.0000", WIS="205.0000", CHA="205.0000", see_hide="0", see_improved_hide="0", trackable="0", ATK="0", Accuracy="0", Avoidance="0", slow_mitigation="0", version="0", maxlevel="0", scalerate="100", private_corpse="0", unique_spawn_by_name="0", underwater="0", isquest="0", emoteid="0", spellscale="100", healscale="100", no_target_hotkey="0", raid_target="0", light="0", ignore_despawn="0", show_name="0"; +INSERT INTO auras SET type=8488, npc_type=@suggestedid, name="Aura_of_the_Muse", spell_id=8489, distance=60, aura_type=1, spawn_type=0, movement=0, duration=5400, icon=-1, cast_time=-1; + +SELECT IFNULL((MAX(id) + 1), 2000000) INTO @suggestedid FROM npc_types where id LIKE '2000___'; +INSERT INTO npc_types SET id=@suggestedid, name="IOChampionsAura55", lastname="", level="55", race="127", class="62", bodytype="11", hp="4027.6216", mana="0.0000", gender="0", texture="0", helmtexture="0", herosforgemodel="0", size="2", hp_regen_rate="0", mana_regen_rate="0", loottable_id="0", npc_spells_id="0", mindmg="52.7514", maxdmg="166.8270", attack_count="-1", special_abilities="12,1^13,1^14,1^15,1^16,1^17,1^18,1^19,1^20,1^21,1^22,1^23,1^24,1^25,1^28,1^31,1^35,1^", aggroradius="70", assistradius="0", face="0", luclin_hairstyle="0", luclin_haircolor="0", luclin_eyecolor="0", luclin_eyecolor2="0", luclin_beardcolor="0", luclin_beard="0", drakkin_heritage="0", drakkin_tattoo="0", drakkin_details="0", armortint_red="0", armortint_green="0", armortint_blue="0", d_melee_texture1="0", d_melee_texture2="0", prim_melee_type="28", sec_melee_type="28", runspeed="1.25", MR="21.1027", CR="21.1027", DR="21.1027", FR="21.1027", PR="21.1027", Corrup="21.1027", PhR="48.3333", see_invis="0", see_invis_undead="0", qglobal="0", AC="257.3784", npc_aggro="0", spawn_limit="0", attack_delay="29.4486", findable="0", STR="205.0000", STA="205.0000", DEX="205.0000", AGI="205.0000", _INT="205.0000", WIS="205.0000", CHA="205.0000", see_hide="0", see_improved_hide="0", trackable="0", ATK="0", Accuracy="0", Avoidance="0", slow_mitigation="0", version="0", maxlevel="0", scalerate="100", private_corpse="0", unique_spawn_by_name="0", underwater="0", isquest="0", emoteid="0", spellscale="100", healscale="100", no_target_hotkey="0", raid_target="0", light="0", ignore_despawn="0", show_name="0"; +INSERT INTO auras SET type=8921, npc_type=@suggestedid, name="Myrmidon's_Aura", spell_id=8935, distance=60, aura_type=1, spawn_type=0, movement=0, duration=5400, icon=-1, cast_time=-1; + +SELECT IFNULL((MAX(id) + 1), 2000000) INTO @suggestedid FROM npc_types where id LIKE '2000___'; +INSERT INTO npc_types SET id=@suggestedid, name="IOChampionsAura", lastname="", level="70", race="127", class="62", bodytype="11", hp="4027.6216", mana="0.0000", gender="0", texture="0", helmtexture="0", herosforgemodel="0", size="2", hp_regen_rate="0", mana_regen_rate="0", loottable_id="0", npc_spells_id="0", mindmg="52.7514", maxdmg="166.8270", attack_count="-1", special_abilities="12,1^13,1^14,1^15,1^16,1^17,1^18,1^19,1^20,1^21,1^22,1^23,1^24,1^25,1^28,1^31,1^35,1^", aggroradius="70", assistradius="0", face="0", luclin_hairstyle="0", luclin_haircolor="0", luclin_eyecolor="0", luclin_eyecolor2="0", luclin_beardcolor="0", luclin_beard="0", drakkin_heritage="0", drakkin_tattoo="0", drakkin_details="0", armortint_red="0", armortint_green="0", armortint_blue="0", d_melee_texture1="0", d_melee_texture2="0", prim_melee_type="28", sec_melee_type="28", runspeed="1.25", MR="21.1027", CR="21.1027", DR="21.1027", FR="21.1027", PR="21.1027", Corrup="21.1027", PhR="48.3333", see_invis="0", see_invis_undead="0", qglobal="0", AC="257.3784", npc_aggro="0", spawn_limit="0", attack_delay="29.4486", findable="0", STR="205.0000", STA="205.0000", DEX="205.0000", AGI="205.0000", _INT="205.0000", WIS="205.0000", CHA="205.0000", see_hide="0", see_improved_hide="0", trackable="0", ATK="0", Accuracy="0", Avoidance="0", slow_mitigation="0", version="0", maxlevel="0", scalerate="100", private_corpse="0", unique_spawn_by_name="0", underwater="0", isquest="0", emoteid="0", spellscale="100", healscale="100", no_target_hotkey="0", raid_target="0", light="0", ignore_despawn="0", show_name="0"; +INSERT INTO auras SET type=8468, npc_type=@suggestedid, name="Champion's_Aura", spell_id=8469, distance=60, aura_type=1, spawn_type=0, movement=0, duration=5400, icon=-1, cast_time=-1; + +SELECT IFNULL((MAX(id) + 1), 2000000) INTO @suggestedid FROM npc_types where id LIKE '2000___'; +INSERT INTO npc_types SET id=@suggestedid, name="IOBlessedAura55", lastname="", level="55", race="127", class="62", bodytype="11", hp="4027.6216", mana="0.0000", gender="0", texture="0", helmtexture="0", herosforgemodel="0", size="2", hp_regen_rate="0", mana_regen_rate="0", loottable_id="0", npc_spells_id="0", mindmg="52.7514", maxdmg="166.8270", attack_count="-1", special_abilities="12,1^13,1^14,1^15,1^16,1^17,1^18,1^19,1^20,1^21,1^22,1^23,1^24,1^25,1^28,1^31,1^35,1^", aggroradius="70", assistradius="0", face="0", luclin_hairstyle="0", luclin_haircolor="0", luclin_eyecolor="0", luclin_eyecolor2="0", luclin_beardcolor="0", luclin_beard="0", drakkin_heritage="0", drakkin_tattoo="0", drakkin_details="0", armortint_red="0", armortint_green="0", armortint_blue="0", d_melee_texture1="0", d_melee_texture2="0", prim_melee_type="28", sec_melee_type="28", runspeed="1.25", MR="21.1027", CR="21.1027", DR="21.1027", FR="21.1027", PR="21.1027", Corrup="21.1027", PhR="48.3333", see_invis="0", see_invis_undead="0", qglobal="0", AC="257.3784", npc_aggro="0", spawn_limit="0", attack_delay="29.4486", findable="0", STR="205.0000", STA="205.0000", DEX="205.0000", AGI="205.0000", _INT="205.0000", WIS="205.0000", CHA="205.0000", see_hide="0", see_improved_hide="0", trackable="0", ATK="0", Accuracy="0", Avoidance="0", slow_mitigation="0", version="0", maxlevel="0", scalerate="100", private_corpse="0", unique_spawn_by_name="0", underwater="0", isquest="0", emoteid="0", spellscale="100", healscale="100", no_target_hotkey="0", raid_target="0", light="0", ignore_despawn="0", show_name="0"; +INSERT INTO auras SET type=8925, npc_type=@suggestedid, name="Holy_Aura", spell_id=8938, distance=60, aura_type=1, spawn_type=0, movement=0, duration=5400, icon=-1, cast_time=-1; + +SELECT IFNULL((MAX(id) + 1), 2000000) INTO @suggestedid FROM npc_types where id LIKE '2000___'; +INSERT INTO npc_types SET id=@suggestedid, name="IOBlessedAura", lastname="", level="70", race="127", class="62", bodytype="11", hp="4027.6216", mana="0.0000", gender="0", texture="0", helmtexture="0", herosforgemodel="0", size="2", hp_regen_rate="0", mana_regen_rate="0", loottable_id="0", npc_spells_id="0", mindmg="52.7514", maxdmg="166.8270", attack_count="-1", special_abilities="12,1^13,1^14,1^15,1^16,1^17,1^18,1^19,1^20,1^21,1^22,1^23,1^24,1^25,1^28,1^31,1^35,1^", aggroradius="70", assistradius="0", face="0", luclin_hairstyle="0", luclin_haircolor="0", luclin_eyecolor="0", luclin_eyecolor2="0", luclin_beardcolor="0", luclin_beard="0", drakkin_heritage="0", drakkin_tattoo="0", drakkin_details="0", armortint_red="0", armortint_green="0", armortint_blue="0", d_melee_texture1="0", d_melee_texture2="0", prim_melee_type="28", sec_melee_type="28", runspeed="1.25", MR="21.1027", CR="21.1027", DR="21.1027", FR="21.1027", PR="21.1027", Corrup="21.1027", PhR="48.3333", see_invis="0", see_invis_undead="0", qglobal="0", AC="257.3784", npc_aggro="0", spawn_limit="0", attack_delay="29.4486", findable="0", STR="205.0000", STA="205.0000", DEX="205.0000", AGI="205.0000", _INT="205.0000", WIS="205.0000", CHA="205.0000", see_hide="0", see_improved_hide="0", trackable="0", ATK="0", Accuracy="0", Avoidance="0", slow_mitigation="0", version="0", maxlevel="0", scalerate="100", private_corpse="0", unique_spawn_by_name="0", underwater="0", isquest="0", emoteid="0", spellscale="100", healscale="100", no_target_hotkey="0", raid_target="0", light="0", ignore_despawn="0", show_name="0"; +INSERT INTO auras SET type=8481, npc_type=@suggestedid, name="Blessed_Aura", spell_id=8482, distance=60, aura_type=1, spawn_type=0, movement=0, duration=5400, icon=-1, cast_time=-1; + +SELECT IFNULL((MAX(id) + 1), 2000000) INTO @suggestedid FROM npc_types where id LIKE '2000___'; +INSERT INTO npc_types SET id=@suggestedid, name="IOMastersAura55", lastname="", level="55", race="127", class="62", bodytype="11", hp="4027.6216", mana="0.0000", gender="0", texture="0", helmtexture="0", herosforgemodel="0", size="2", hp_regen_rate="0", mana_regen_rate="0", loottable_id="0", npc_spells_id="0", mindmg="52.7514", maxdmg="166.8270", attack_count="-1", special_abilities="12,1^13,1^14,1^15,1^16,1^17,1^18,1^19,1^20,1^21,1^22,1^23,1^24,1^25,1^28,1^31,1^35,1^", aggroradius="70", assistradius="0", face="0", luclin_hairstyle="0", luclin_haircolor="0", luclin_eyecolor="0", luclin_eyecolor2="0", luclin_beardcolor="0", luclin_beard="0", drakkin_heritage="0", drakkin_tattoo="0", drakkin_details="0", armortint_red="0", armortint_green="0", armortint_blue="0", d_melee_texture1="0", d_melee_texture2="0", prim_melee_type="28", sec_melee_type="28", runspeed="1.25", MR="21.1027", CR="21.1027", DR="21.1027", FR="21.1027", PR="21.1027", Corrup="21.1027", PhR="48.3333", see_invis="0", see_invis_undead="0", qglobal="0", AC="257.3784", npc_aggro="0", spawn_limit="0", attack_delay="29.4486", findable="0", STR="205.0000", STA="205.0000", DEX="205.0000", AGI="205.0000", _INT="205.0000", WIS="205.0000", CHA="205.0000", see_hide="0", see_improved_hide="0", trackable="0", ATK="0", Accuracy="0", Avoidance="0", slow_mitigation="0", version="0", maxlevel="0", scalerate="100", private_corpse="0", unique_spawn_by_name="0", underwater="0", isquest="0", emoteid="0", spellscale="100", healscale="100", no_target_hotkey="0", raid_target="0", light="0", ignore_despawn="0", show_name="0"; +INSERT INTO auras SET type=8923, npc_type=@suggestedid, name="Disciples_Aura", spell_id=8937, distance=60, aura_type=1, spawn_type=0, movement=0, duration=5400, icon=-1, cast_time=-1; + +SELECT IFNULL((MAX(id) + 1), 2000000) INTO @suggestedid FROM npc_types where id LIKE '2000___'; +INSERT INTO npc_types SET id=@suggestedid, name="IOMastersAura", lastname="", level="70", race="127", class="62", bodytype="11", hp="4027.6216", mana="0.0000", gender="0", texture="0", helmtexture="0", herosforgemodel="0", size="2", hp_regen_rate="0", mana_regen_rate="0", loottable_id="0", npc_spells_id="0", mindmg="52.7514", maxdmg="166.8270", attack_count="-1", special_abilities="12,1^13,1^14,1^15,1^16,1^17,1^18,1^19,1^20,1^21,1^22,1^23,1^24,1^25,1^28,1^31,1^35,1^", aggroradius="70", assistradius="0", face="0", luclin_hairstyle="0", luclin_haircolor="0", luclin_eyecolor="0", luclin_eyecolor2="0", luclin_beardcolor="0", luclin_beard="0", drakkin_heritage="0", drakkin_tattoo="0", drakkin_details="0", armortint_red="0", armortint_green="0", armortint_blue="0", d_melee_texture1="0", d_melee_texture2="0", prim_melee_type="28", sec_melee_type="28", runspeed="1.25", MR="21.1027", CR="21.1027", DR="21.1027", FR="21.1027", PR="21.1027", Corrup="21.1027", PhR="48.3333", see_invis="0", see_invis_undead="0", qglobal="0", AC="257.3784", npc_aggro="0", spawn_limit="0", attack_delay="29.4486", findable="0", STR="205.0000", STA="205.0000", DEX="205.0000", AGI="205.0000", _INT="205.0000", WIS="205.0000", CHA="205.0000", see_hide="0", see_improved_hide="0", trackable="0", ATK="0", Accuracy="0", Avoidance="0", slow_mitigation="0", version="0", maxlevel="0", scalerate="100", private_corpse="0", unique_spawn_by_name="0", underwater="0", isquest="0", emoteid="0", spellscale="100", healscale="100", no_target_hotkey="0", raid_target="0", light="0", ignore_despawn="0", show_name="0"; +INSERT INTO auras SET type=8474, npc_type=@suggestedid, name="Master's_Aura", spell_id=8475, distance=60, aura_type=1, spawn_type=0, movement=0, duration=5400, icon=-1, cast_time=-1; + +SELECT IFNULL((MAX(id) + 1), 2000000) INTO @suggestedid FROM npc_types where id LIKE '2000___'; +INSERT INTO npc_types SET id=@suggestedid, name="IOQuicksandTrap55", lastname="", level="55", race="127", class="62", bodytype="11", hp="4027.6216", mana="0.0000", gender="0", texture="0", helmtexture="0", herosforgemodel="0", size="2", hp_regen_rate="0", mana_regen_rate="0", loottable_id="0", npc_spells_id="0", mindmg="52.7514", maxdmg="166.8270", attack_count="-1", special_abilities="12,1^13,1^14,1^15,1^16,1^17,1^18,1^19,1^20,1^21,1^22,1^23,1^24,1^25,1^28,1^31,1^35,1^", aggroradius="70", assistradius="0", face="0", luclin_hairstyle="0", luclin_haircolor="0", luclin_eyecolor="0", luclin_eyecolor2="0", luclin_beardcolor="0", luclin_beard="0", drakkin_heritage="0", drakkin_tattoo="0", drakkin_details="0", armortint_red="0", armortint_green="0", armortint_blue="0", d_melee_texture1="0", d_melee_texture2="0", prim_melee_type="28", sec_melee_type="28", runspeed="1.25", MR="21.1027", CR="21.1027", DR="21.1027", FR="21.1027", PR="21.1027", Corrup="21.1027", PhR="48.3333", see_invis="0", see_invis_undead="0", qglobal="0", AC="257.3784", npc_aggro="0", spawn_limit="0", attack_delay="29.4486", findable="0", STR="205.0000", STA="205.0000", DEX="205.0000", AGI="205.0000", _INT="205.0000", WIS="205.0000", CHA="205.0000", see_hide="0", see_improved_hide="0", trackable="0", ATK="0", Accuracy="0", Avoidance="0", slow_mitigation="0", version="0", maxlevel="0", scalerate="100", private_corpse="0", unique_spawn_by_name="0", underwater="0", isquest="0", emoteid="0", spellscale="100", healscale="100", no_target_hotkey="0", raid_target="0", light="0", ignore_despawn="0", show_name="0"; +INSERT INTO auras SET type=8933, npc_type=@suggestedid, name="Earthen_Strength", spell_id=8948, distance=60, aura_type=2, spawn_type=0, movement=0, duration=5400, icon=-1, cast_time=-1; + +SELECT IFNULL((MAX(id) + 1), 2000000) INTO @suggestedid FROM npc_types where id LIKE '2000___'; +INSERT INTO npc_types SET id=@suggestedid, name="IOQuicksandTrap", lastname="", level="70", race="127", class="62", bodytype="11", hp="4027.6216", mana="0.0000", gender="0", texture="0", helmtexture="0", herosforgemodel="0", size="2", hp_regen_rate="0", mana_regen_rate="0", loottable_id="0", npc_spells_id="0", mindmg="52.7514", maxdmg="166.8270", attack_count="-1", special_abilities="12,1^13,1^14,1^15,1^16,1^17,1^18,1^19,1^20,1^21,1^22,1^23,1^24,1^25,1^28,1^31,1^35,1^", aggroradius="70", assistradius="0", face="0", luclin_hairstyle="0", luclin_haircolor="0", luclin_eyecolor="0", luclin_eyecolor2="0", luclin_beardcolor="0", luclin_beard="0", drakkin_heritage="0", drakkin_tattoo="0", drakkin_details="0", armortint_red="0", armortint_green="0", armortint_blue="0", d_melee_texture1="0", d_melee_texture2="0", prim_melee_type="28", sec_melee_type="28", runspeed="1.25", MR="21.1027", CR="21.1027", DR="21.1027", FR="21.1027", PR="21.1027", Corrup="21.1027", PhR="48.3333", see_invis="0", see_invis_undead="0", qglobal="0", AC="257.3784", npc_aggro="0", spawn_limit="0", attack_delay="29.4486", findable="0", STR="205.0000", STA="205.0000", DEX="205.0000", AGI="205.0000", _INT="205.0000", WIS="205.0000", CHA="205.0000", see_hide="0", see_improved_hide="0", trackable="0", ATK="0", Accuracy="0", Avoidance="0", slow_mitigation="0", version="0", maxlevel="0", scalerate="100", private_corpse="0", unique_spawn_by_name="0", underwater="0", isquest="0", emoteid="0", spellscale="100", healscale="100", no_target_hotkey="0", raid_target="0", light="0", ignore_despawn="0", show_name="0"; +INSERT INTO auras SET type=8518, npc_type=@suggestedid, name="Rathe's_Strength", spell_id=8519, distance=60, aura_type=2, spawn_type=0, movement=0, duration=5400, icon=-1, cast_time=-1; + +SELECT IFNULL((MAX(id) + 1), 2000000) INTO @suggestedid FROM npc_types where id LIKE '2000___'; +INSERT INTO npc_types SET id=@suggestedid, name="IOIllusionistsAura55", lastname="", level="55", race="127", class="62", bodytype="11", hp="4027.6216", mana="0.0000", gender="0", texture="0", helmtexture="0", herosforgemodel="0", size="2", hp_regen_rate="0", mana_regen_rate="0", loottable_id="0", npc_spells_id="0", mindmg="52.7514", maxdmg="166.8270", attack_count="-1", special_abilities="12,1^13,1^14,1^15,1^16,1^17,1^18,1^19,1^20,1^21,1^22,1^23,1^24,1^25,1^28,1^31,1^35,1^", aggroradius="70", assistradius="0", face="0", luclin_hairstyle="0", luclin_haircolor="0", luclin_eyecolor="0", luclin_eyecolor2="0", luclin_beardcolor="0", luclin_beard="0", drakkin_heritage="0", drakkin_tattoo="0", drakkin_details="0", armortint_red="0", armortint_green="0", armortint_blue="0", d_melee_texture1="0", d_melee_texture2="0", prim_melee_type="28", sec_melee_type="28", runspeed="1.25", MR="21.1027", CR="21.1027", DR="21.1027", FR="21.1027", PR="21.1027", Corrup="21.1027", PhR="48.3333", see_invis="0", see_invis_undead="0", qglobal="0", AC="257.3784", npc_aggro="0", spawn_limit="0", attack_delay="29.4486", findable="0", STR="205.0000", STA="205.0000", DEX="205.0000", AGI="205.0000", _INT="205.0000", WIS="205.0000", CHA="205.0000", see_hide="0", see_improved_hide="0", trackable="0", ATK="0", Accuracy="0", Avoidance="0", slow_mitigation="0", version="0", maxlevel="0", scalerate="100", private_corpse="0", unique_spawn_by_name="0", underwater="0", isquest="0", emoteid="0", spellscale="100", healscale="100", no_target_hotkey="0", raid_target="0", light="0", ignore_despawn="0", show_name="0"; +INSERT INTO auras SET type=8931, npc_type=@suggestedid, name="Beguiler's_Aura", spell_id=8946, distance=60, aura_type=1, spawn_type=0, movement=0, duration=5400, icon=-1, cast_time=-1; + +SELECT IFNULL((MAX(id) + 1), 2000000) INTO @suggestedid FROM npc_types where id LIKE '2000___'; +INSERT INTO npc_types SET id=@suggestedid, name="IOIllusionistsAura", lastname="", level="70", race="127", class="62", bodytype="11", hp="4027.6216", mana="0.0000", gender="0", texture="0", helmtexture="0", herosforgemodel="0", size="2", hp_regen_rate="0", mana_regen_rate="0", loottable_id="0", npc_spells_id="0", mindmg="52.7514", maxdmg="166.8270", attack_count="-1", special_abilities="12,1^13,1^14,1^15,1^16,1^17,1^18,1^19,1^20,1^21,1^22,1^23,1^24,1^25,1^28,1^31,1^35,1^", aggroradius="70", assistradius="0", face="0", luclin_hairstyle="0", luclin_haircolor="0", luclin_eyecolor="0", luclin_eyecolor2="0", luclin_beardcolor="0", luclin_beard="0", drakkin_heritage="0", drakkin_tattoo="0", drakkin_details="0", armortint_red="0", armortint_green="0", armortint_blue="0", d_melee_texture1="0", d_melee_texture2="0", prim_melee_type="28", sec_melee_type="28", runspeed="1.25", MR="21.1027", CR="21.1027", DR="21.1027", FR="21.1027", PR="21.1027", Corrup="21.1027", PhR="48.3333", see_invis="0", see_invis_undead="0", qglobal="0", AC="257.3784", npc_aggro="0", spawn_limit="0", attack_delay="29.4486", findable="0", STR="205.0000", STA="205.0000", DEX="205.0000", AGI="205.0000", _INT="205.0000", WIS="205.0000", CHA="205.0000", see_hide="0", see_improved_hide="0", trackable="0", ATK="0", Accuracy="0", Avoidance="0", slow_mitigation="0", version="0", maxlevel="0", scalerate="100", private_corpse="0", unique_spawn_by_name="0", underwater="0", isquest="0", emoteid="0", spellscale="100", healscale="100", no_target_hotkey="0", raid_target="0", light="0", ignore_despawn="0", show_name="0"; +INSERT INTO auras SET type=8509, npc_type=@suggestedid, name="Illusionist's_Aura", spell_id=8510, distance=60, aura_type=1, spawn_type=0, movement=0, duration=5400, icon=-1, cast_time=-1; + +SELECT IFNULL((MAX(id) + 1), 2000000) INTO @suggestedid FROM npc_types where id LIKE '2000___'; +INSERT INTO npc_types SET id=@suggestedid, name="IOLivingVineTrap55", lastname="", level="55", race="127", class="62", bodytype="11", hp="4027.6216", mana="0.0000", gender="0", texture="0", helmtexture="0", herosforgemodel="0", size="2", hp_regen_rate="0", mana_regen_rate="0", loottable_id="0", npc_spells_id="0", mindmg="52.7514", maxdmg="166.8270", attack_count="-1", special_abilities="12,1^13,1^14,1^15,1^16,1^17,1^18,1^19,1^20,1^21,1^22,1^23,1^24,1^25,1^28,1^31,1^35,1^", aggroradius="70", assistradius="0", face="0", luclin_hairstyle="0", luclin_haircolor="0", luclin_eyecolor="0", luclin_eyecolor2="0", luclin_beardcolor="0", luclin_beard="0", drakkin_heritage="0", drakkin_tattoo="0", drakkin_details="0", armortint_red="0", armortint_green="0", armortint_blue="0", d_melee_texture1="0", d_melee_texture2="0", prim_melee_type="28", sec_melee_type="28", runspeed="1.25", MR="21.1027", CR="21.1027", DR="21.1027", FR="21.1027", PR="21.1027", Corrup="21.1027", PhR="48.3333", see_invis="0", see_invis_undead="0", qglobal="0", AC="257.3784", npc_aggro="0", spawn_limit="0", attack_delay="29.4486", findable="0", STR="205.0000", STA="205.0000", DEX="205.0000", AGI="205.0000", _INT="205.0000", WIS="205.0000", CHA="205.0000", see_hide="0", see_improved_hide="0", trackable="0", ATK="0", Accuracy="0", Avoidance="0", slow_mitigation="0", version="0", maxlevel="0", scalerate="100", private_corpse="0", unique_spawn_by_name="0", underwater="0", isquest="0", emoteid="0", spellscale="100", healscale="100", no_target_hotkey="0", raid_target="0", light="0", ignore_despawn="0", show_name="0"; +INSERT INTO auras SET type=8929, npc_type=@suggestedid, name="Aura_of_the_Grove", spell_id=8943, distance=60, aura_type=1, spawn_type=0, movement=0, duration=5400, icon=1, cast_time=12; + +SELECT IFNULL((MAX(id) + 1), 2000000) INTO @suggestedid FROM npc_types where id LIKE '2000___'; +INSERT INTO npc_types SET id=@suggestedid, name="IOLivingVineTrap", lastname="", level="70", race="127", class="62", bodytype="11", hp="4027.6216", mana="0.0000", gender="0", texture="0", helmtexture="0", herosforgemodel="0", size="2", hp_regen_rate="0", mana_regen_rate="0", loottable_id="0", npc_spells_id="0", mindmg="52.7514", maxdmg="166.8270", attack_count="-1", special_abilities="12,1^13,1^14,1^15,1^16,1^17,1^18,1^19,1^20,1^21,1^22,1^23,1^24,1^25,1^28,1^31,1^35,1^", aggroradius="70", assistradius="0", face="0", luclin_hairstyle="0", luclin_haircolor="0", luclin_eyecolor="0", luclin_eyecolor2="0", luclin_beardcolor="0", luclin_beard="0", drakkin_heritage="0", drakkin_tattoo="0", drakkin_details="0", armortint_red="0", armortint_green="0", armortint_blue="0", d_melee_texture1="0", d_melee_texture2="0", prim_melee_type="28", sec_melee_type="28", runspeed="1.25", MR="21.1027", CR="21.1027", DR="21.1027", FR="21.1027", PR="21.1027", Corrup="21.1027", PhR="48.3333", see_invis="0", see_invis_undead="0", qglobal="0", AC="257.3784", npc_aggro="0", spawn_limit="0", attack_delay="29.4486", findable="0", STR="205.0000", STA="205.0000", DEX="205.0000", AGI="205.0000", _INT="205.0000", WIS="205.0000", CHA="205.0000", see_hide="0", see_improved_hide="0", trackable="0", ATK="0", Accuracy="0", Avoidance="0", slow_mitigation="0", version="0", maxlevel="0", scalerate="100", private_corpse="0", unique_spawn_by_name="0", underwater="0", isquest="0", emoteid="0", spellscale="100", healscale="100", no_target_hotkey="0", raid_target="0", light="0", ignore_despawn="0", show_name="0"; +INSERT INTO auras SET type=8499, npc_type=@suggestedid, name="Aura_of_Life", spell_id=8500, distance=60, aura_type=1, spawn_type=0, movement=0, duration=5400, icon=1, cast_time=12; + +SELECT IFNULL((MAX(id) + 1), 2000000) INTO @suggestedid FROM npc_types where id LIKE '2000___'; +INSERT INTO npc_types SET id=@suggestedid, name="IOAuraOfThePious55", lastname="", level="55", race="127", class="62", bodytype="11", hp="4027.6216", mana="0.0000", gender="0", texture="0", helmtexture="0", herosforgemodel="0", size="2", hp_regen_rate="0", mana_regen_rate="0", loottable_id="0", npc_spells_id="0", mindmg="52.7514", maxdmg="166.8270", attack_count="-1", special_abilities="12,1^13,1^14,1^15,1^16,1^17,1^18,1^19,1^20,1^21,1^22,1^23,1^24,1^25,1^28,1^31,1^35,1^", aggroradius="70", assistradius="0", face="0", luclin_hairstyle="0", luclin_haircolor="0", luclin_eyecolor="0", luclin_eyecolor2="0", luclin_beardcolor="0", luclin_beard="0", drakkin_heritage="0", drakkin_tattoo="0", drakkin_details="0", armortint_red="0", armortint_green="0", armortint_blue="0", d_melee_texture1="0", d_melee_texture2="0", prim_melee_type="28", sec_melee_type="28", runspeed="1.25", MR="21.1027", CR="21.1027", DR="21.1027", FR="21.1027", PR="21.1027", Corrup="21.1027", PhR="48.3333", see_invis="0", see_invis_undead="0", qglobal="0", AC="257.3784", npc_aggro="0", spawn_limit="0", attack_delay="29.4486", findable="0", STR="205.0000", STA="205.0000", DEX="205.0000", AGI="205.0000", _INT="205.0000", WIS="205.0000", CHA="205.0000", see_hide="0", see_improved_hide="0", trackable="0", ATK="0", Accuracy="0", Avoidance="0", slow_mitigation="0", version="0", maxlevel="0", scalerate="100", private_corpse="0", unique_spawn_by_name="0", underwater="0", isquest="0", emoteid="0", spellscale="100", healscale="100", no_target_hotkey="0", raid_target="0", light="0", ignore_despawn="0", show_name="0"; +INSERT INTO auras SET type=8928, npc_type=@suggestedid, name="Aura_of_the_Zealot", spell_id=8940, distance=60, aura_type=1, spawn_type=0, movement=0, duration=5400, icon=-1, cast_time=-1; + +SELECT IFNULL((MAX(id) + 1), 2000000) INTO @suggestedid FROM npc_types where id LIKE '2000___'; +INSERT INTO npc_types SET id=@suggestedid, name="IOAuraOfThePious", lastname="", level="70", race="127", class="62", bodytype="11", hp="4027.6216", mana="0.0000", gender="0", texture="0", helmtexture="0", herosforgemodel="0", size="2", hp_regen_rate="0", mana_regen_rate="0", loottable_id="0", npc_spells_id="0", mindmg="52.7514", maxdmg="166.8270", attack_count="-1", special_abilities="12,1^13,1^14,1^15,1^16,1^17,1^18,1^19,1^20,1^21,1^22,1^23,1^24,1^25,1^28,1^31,1^35,1^", aggroradius="70", assistradius="0", face="0", luclin_hairstyle="0", luclin_haircolor="0", luclin_eyecolor="0", luclin_eyecolor2="0", luclin_beardcolor="0", luclin_beard="0", drakkin_heritage="0", drakkin_tattoo="0", drakkin_details="0", armortint_red="0", armortint_green="0", armortint_blue="0", d_melee_texture1="0", d_melee_texture2="0", prim_melee_type="28", sec_melee_type="28", runspeed="1.25", MR="21.1027", CR="21.1027", DR="21.1027", FR="21.1027", PR="21.1027", Corrup="21.1027", PhR="48.3333", see_invis="0", see_invis_undead="0", qglobal="0", AC="257.3784", npc_aggro="0", spawn_limit="0", attack_delay="29.4486", findable="0", STR="205.0000", STA="205.0000", DEX="205.0000", AGI="205.0000", _INT="205.0000", WIS="205.0000", CHA="205.0000", see_hide="0", see_improved_hide="0", trackable="0", ATK="0", Accuracy="0", Avoidance="0", slow_mitigation="0", version="0", maxlevel="0", scalerate="100", private_corpse="0", unique_spawn_by_name="0", underwater="0", isquest="0", emoteid="0", spellscale="100", healscale="100", no_target_hotkey="0", raid_target="0", light="0", ignore_despawn="0", show_name="0"; +INSERT INTO auras SET type=8495, npc_type=@suggestedid, name="Aura_of_the_Pious", spell_id=8496, distance=60, aura_type=1, spawn_type=0, movement=0, duration=5400, icon=-1, cast_time=-1; + +SELECT IFNULL((MAX(id) + 1), 2000000) INTO @suggestedid FROM npc_types where id LIKE '2000___'; +INSERT INTO npc_types SET id=@suggestedid, name="IOBloodlustAura55", lastname="", level="55", race="127", class="62", bodytype="11", hp="4027.6216", mana="0.0000", gender="0", texture="0", helmtexture="0", herosforgemodel="0", size="2", hp_regen_rate="0", mana_regen_rate="0", loottable_id="0", npc_spells_id="0", mindmg="52.7514", maxdmg="166.8270", attack_count="-1", special_abilities="12,1^13,1^14,1^15,1^16,1^17,1^18,1^19,1^20,1^21,1^22,1^23,1^24,1^25,1^28,1^31,1^35,1^", aggroradius="70", assistradius="0", face="0", luclin_hairstyle="0", luclin_haircolor="0", luclin_eyecolor="0", luclin_eyecolor2="0", luclin_beardcolor="0", luclin_beard="0", drakkin_heritage="0", drakkin_tattoo="0", drakkin_details="0", armortint_red="0", armortint_green="0", armortint_blue="0", d_melee_texture1="0", d_melee_texture2="0", prim_melee_type="28", sec_melee_type="28", runspeed="1.25", MR="21.1027", CR="21.1027", DR="21.1027", FR="21.1027", PR="21.1027", Corrup="21.1027", PhR="48.3333", see_invis="0", see_invis_undead="0", qglobal="0", AC="257.3784", npc_aggro="0", spawn_limit="0", attack_delay="29.4486", findable="0", STR="205.0000", STA="205.0000", DEX="205.0000", AGI="205.0000", _INT="205.0000", WIS="205.0000", CHA="205.0000", see_hide="0", see_improved_hide="0", trackable="0", ATK="0", Accuracy="0", Avoidance="0", slow_mitigation="0", version="0", maxlevel="0", scalerate="100", private_corpse="0", unique_spawn_by_name="0", underwater="0", isquest="0", emoteid="0", spellscale="100", healscale="100", no_target_hotkey="0", raid_target="0", light="0", ignore_despawn="0", show_name="0"; +INSERT INTO auras SET type=8924, npc_type=@suggestedid, name="Aura_of_Rage", spell_id=8959, distance=60, aura_type=1, spawn_type=0, movement=0, duration=5400, icon=-1, cast_time=-1; + +SELECT IFNULL((MAX(id) + 1), 2000000) INTO @suggestedid FROM npc_types where id LIKE '2000___'; +INSERT INTO npc_types SET id=@suggestedid, name="IOBloodlustAura", lastname="", level="70", race="127", class="62", bodytype="11", hp="4027.6216", mana="0.0000", gender="0", texture="0", helmtexture="0", herosforgemodel="0", size="2", hp_regen_rate="0", mana_regen_rate="0", loottable_id="0", npc_spells_id="0", mindmg="52.7514", maxdmg="166.8270", attack_count="-1", special_abilities="12,1^13,1^14,1^15,1^16,1^17,1^18,1^19,1^20,1^21,1^22,1^23,1^24,1^25,1^28,1^31,1^35,1^", aggroradius="70", assistradius="0", face="0", luclin_hairstyle="0", luclin_haircolor="0", luclin_eyecolor="0", luclin_eyecolor2="0", luclin_beardcolor="0", luclin_beard="0", drakkin_heritage="0", drakkin_tattoo="0", drakkin_details="0", armortint_red="0", armortint_green="0", armortint_blue="0", d_melee_texture1="0", d_melee_texture2="0", prim_melee_type="28", sec_melee_type="28", runspeed="1.25", MR="21.1027", CR="21.1027", DR="21.1027", FR="21.1027", PR="21.1027", Corrup="21.1027", PhR="48.3333", see_invis="0", see_invis_undead="0", qglobal="0", AC="257.3784", npc_aggro="0", spawn_limit="0", attack_delay="29.4486", findable="0", STR="205.0000", STA="205.0000", DEX="205.0000", AGI="205.0000", _INT="205.0000", WIS="205.0000", CHA="205.0000", see_hide="0", see_improved_hide="0", trackable="0", ATK="0", Accuracy="0", Avoidance="0", slow_mitigation="0", version="0", maxlevel="0", scalerate="100", private_corpse="0", unique_spawn_by_name="0", underwater="0", isquest="0", emoteid="0", spellscale="100", healscale="100", no_target_hotkey="0", raid_target="0", light="0", ignore_despawn="0", show_name="0"; +INSERT INTO auras SET type=8477, npc_type=@suggestedid, name="Bloodlust_Aura", spell_id=8478, distance=60, aura_type=1, spawn_type=0, movement=0, duration=5400, icon=-1, cast_time=-1; + +SELECT IFNULL((MAX(id) + 1), 2000000) INTO @suggestedid FROM npc_types where id LIKE '2000___'; +INSERT INTO npc_types SET id=@suggestedid, name="IOIdolOfMalaTrap55", lastname="", level="55", race="514", class="62", bodytype="5", hp="4027.6216", mana="0.0000", gender="2", texture="0", helmtexture="0", herosforgemodel="0", size="2.5", hp_regen_rate="0", mana_regen_rate="0", loottable_id="0", npc_spells_id="0", mindmg="52.7514", maxdmg="166.8270", attack_count="-1", special_abilities="12,1^13,1^14,1^15,1^16,1^17,1^18,1^19,1^20,1^21,1^22,1^23,1^24,1^25,1^28,1^31,1^35,1^", aggroradius="70", assistradius="0", face="0", luclin_hairstyle="0", luclin_haircolor="0", luclin_eyecolor="0", luclin_eyecolor2="0", luclin_beardcolor="0", luclin_beard="0", drakkin_heritage="0", drakkin_tattoo="0", drakkin_details="0", armortint_red="0", armortint_green="0", armortint_blue="0", d_melee_texture1="0", d_melee_texture2="0", prim_melee_type="28", sec_melee_type="28", runspeed="1.25", MR="21.1027", CR="21.1027", DR="21.1027", FR="21.1027", PR="21.1027", Corrup="21.1027", PhR="48.3333", see_invis="0", see_invis_undead="0", qglobal="0", AC="257.3784", npc_aggro="0", spawn_limit="0", attack_delay="29.4486", findable="0", STR="205.0000", STA="205.0000", DEX="205.0000", AGI="205.0000", _INT="205.0000", WIS="205.0000", CHA="205.0000", see_hide="0", see_improved_hide="0", trackable="0", ATK="0", Accuracy="0", Avoidance="0", slow_mitigation="0", version="0", maxlevel="0", scalerate="100", private_corpse="0", unique_spawn_by_name="0", underwater="0", isquest="0", emoteid="0", spellscale="100", healscale="100", no_target_hotkey="0", raid_target="0", light="0", ignore_despawn="0", show_name="0"; +INSERT INTO auras SET type=8930, npc_type=@suggestedid, name="Soul_Idol", spell_id=8945, distance=60, aura_type=3, spawn_type=1, movement=1, duration=120, icon=-1, cast_time=12; + +SELECT IFNULL((MAX(id) + 1), 2000000) INTO @suggestedid FROM npc_types where id LIKE '2000___'; +INSERT INTO npc_types SET id=@suggestedid, name="IOIdolOfMalaTrap", lastname="", level="70", race="514", class="62", bodytype="5", hp="4027.6216", mana="0.0000", gender="2", texture="0", helmtexture="0", herosforgemodel="0", size="2.5", hp_regen_rate="0", mana_regen_rate="0", loottable_id="0", npc_spells_id="0", mindmg="52.7514", maxdmg="166.8270", attack_count="-1", special_abilities="12,1^13,1^14,1^15,1^16,1^17,1^18,1^19,1^20,1^21,1^22,1^23,1^24,1^25,1^28,1^31,1^35,1^", aggroradius="70", assistradius="0", face="0", luclin_hairstyle="0", luclin_haircolor="0", luclin_eyecolor="0", luclin_eyecolor2="0", luclin_beardcolor="0", luclin_beard="0", drakkin_heritage="0", drakkin_tattoo="0", drakkin_details="0", armortint_red="0", armortint_green="0", armortint_blue="0", d_melee_texture1="0", d_melee_texture2="0", prim_melee_type="28", sec_melee_type="28", runspeed="1.25", MR="21.1027", CR="21.1027", DR="21.1027", FR="21.1027", PR="21.1027", Corrup="21.1027", PhR="48.3333", see_invis="0", see_invis_undead="0", qglobal="0", AC="257.3784", npc_aggro="0", spawn_limit="0", attack_delay="29.4486", findable="0", STR="205.0000", STA="205.0000", DEX="205.0000", AGI="205.0000", _INT="205.0000", WIS="205.0000", CHA="205.0000", see_hide="0", see_improved_hide="0", trackable="0", ATK="0", Accuracy="0", Avoidance="0", slow_mitigation="0", version="0", maxlevel="0", scalerate="100", private_corpse="0", unique_spawn_by_name="0", underwater="0", isquest="0", emoteid="0", spellscale="100", healscale="100", no_target_hotkey="0", raid_target="0", light="0", ignore_despawn="0", show_name="0"; +INSERT INTO auras SET type=8504, npc_type=@suggestedid, name="Spirit_Idol", spell_id=8505, distance=60, aura_type=3, spawn_type=1, movement=1, duration=120, icon=-1, cast_time=12; + +SELECT IFNULL((MAX(id) + 1), 2000000) INTO @suggestedid FROM npc_types where id LIKE '2000___'; +INSERT INTO npc_types SET id=@suggestedid, name="IODeathRuneTrap55", lastname="", level="55", race="510", class="62", bodytype="5", hp="4027.6216", mana="0.0000", gender="2", texture="0", helmtexture="0", herosforgemodel="0", size="3", hp_regen_rate="0", mana_regen_rate="0", loottable_id="0", npc_spells_id="0", mindmg="52.7514", maxdmg="166.8270", attack_count="-1", special_abilities="12,1^13,1^14,1^15,1^16,1^17,1^18,1^19,1^20,1^21,1^22,1^23,1^24,1^25,1^28,1^31,1^35,1^", aggroradius="70", assistradius="0", face="0", luclin_hairstyle="0", luclin_haircolor="0", luclin_eyecolor="0", luclin_eyecolor2="0", luclin_beardcolor="0", luclin_beard="0", drakkin_heritage="0", drakkin_tattoo="0", drakkin_details="0", armortint_red="0", armortint_green="0", armortint_blue="0", d_melee_texture1="0", d_melee_texture2="0", prim_melee_type="28", sec_melee_type="28", runspeed="1.25", MR="21.1027", CR="21.1027", DR="21.1027", FR="21.1027", PR="21.1027", Corrup="21.1027", PhR="48.3333", see_invis="0", see_invis_undead="0", qglobal="0", AC="257.3784", npc_aggro="0", spawn_limit="0", attack_delay="29.4486", findable="0", STR="205.0000", STA="205.0000", DEX="205.0000", AGI="205.0000", _INT="205.0000", WIS="205.0000", CHA="205.0000", see_hide="0", see_improved_hide="0", trackable="0", ATK="0", Accuracy="0", Avoidance="0", slow_mitigation="0", version="0", maxlevel="0", scalerate="100", private_corpse="0", unique_spawn_by_name="0", underwater="0", isquest="0", emoteid="0", spellscale="100", healscale="100", no_target_hotkey="0", raid_target="0", light="0", ignore_despawn="0", show_name="0"; +INSERT INTO auras SET type=8934, npc_type=@suggestedid, name="a_dark_rune", spell_id=8949, distance=25, aura_type=4, spawn_type=1, movement=1, duration=120, icon=-1, cast_time=-1; + +SELECT IFNULL((MAX(id) + 1), 2000000) INTO @suggestedid FROM npc_types where id LIKE '2000___'; +INSERT INTO npc_types SET id=@suggestedid, name="IODeathRuneTrap", lastname="", level="70", race="510", class="62", bodytype="5", hp="4027.6216", mana="0.0000", gender="2", texture="0", helmtexture="0", herosforgemodel="0", size="3", hp_regen_rate="0", mana_regen_rate="0", loottable_id="0", npc_spells_id="0", mindmg="52.7514", maxdmg="166.8270", attack_count="-1", special_abilities="12,1^13,1^14,1^15,1^16,1^17,1^18,1^19,1^20,1^21,1^22,1^23,1^24,1^25,1^28,1^31,1^35,1^", aggroradius="70", assistradius="0", face="0", luclin_hairstyle="0", luclin_haircolor="0", luclin_eyecolor="0", luclin_eyecolor2="0", luclin_beardcolor="0", luclin_beard="0", drakkin_heritage="0", drakkin_tattoo="0", drakkin_details="0", armortint_red="0", armortint_green="0", armortint_blue="0", d_melee_texture1="0", d_melee_texture2="0", prim_melee_type="28", sec_melee_type="28", runspeed="1.25", MR="21.1027", CR="21.1027", DR="21.1027", FR="21.1027", PR="21.1027", Corrup="21.1027", PhR="48.3333", see_invis="0", see_invis_undead="0", qglobal="0", AC="257.3784", npc_aggro="0", spawn_limit="0", attack_delay="29.4486", findable="0", STR="205.0000", STA="205.0000", DEX="205.0000", AGI="205.0000", _INT="205.0000", WIS="205.0000", CHA="205.0000", see_hide="0", see_improved_hide="0", trackable="0", ATK="0", Accuracy="0", Avoidance="0", slow_mitigation="0", version="0", maxlevel="0", scalerate="100", private_corpse="0", unique_spawn_by_name="0", underwater="0", isquest="0", emoteid="0", spellscale="100", healscale="100", no_target_hotkey="0", raid_target="0", light="0", ignore_despawn="0", show_name="0"; +INSERT INTO auras SET type=8523, npc_type=@suggestedid, name="a_death_rune", spell_id=8524, distance=25, aura_type=4, spawn_type=1, movement=1, duration=120, icon=-1, cast_time=-1; + +SELECT IFNULL((MAX(id) + 1), 2000000) INTO @suggestedid FROM npc_types where id LIKE '2000___'; +INSERT INTO npc_types SET id=@suggestedid, name="IOFireRuneTrap55", lastname="", level="55", race="510", class="62", bodytype="5", hp="4027.6216", mana="0.0000", gender="2", texture="0", helmtexture="0", herosforgemodel="0", size="3", hp_regen_rate="0", mana_regen_rate="0", loottable_id="0", npc_spells_id="0", mindmg="52.7514", maxdmg="166.8270", attack_count="-1", special_abilities="12,1^13,1^14,1^15,1^16,1^17,1^18,1^19,1^20,1^21,1^22,1^23,1^24,1^25,1^28,1^31,1^35,1^", aggroradius="70", assistradius="0", face="0", luclin_hairstyle="0", luclin_haircolor="0", luclin_eyecolor="0", luclin_eyecolor2="0", luclin_beardcolor="0", luclin_beard="0", drakkin_heritage="0", drakkin_tattoo="0", drakkin_details="0", armortint_red="0", armortint_green="0", armortint_blue="0", d_melee_texture1="0", d_melee_texture2="0", prim_melee_type="28", sec_melee_type="28", runspeed="1.25", MR="21.1027", CR="21.1027", DR="21.1027", FR="21.1027", PR="21.1027", Corrup="21.1027", PhR="48.3333", see_invis="0", see_invis_undead="0", qglobal="0", AC="257.3784", npc_aggro="0", spawn_limit="0", attack_delay="29.4486", findable="0", STR="205.0000", STA="205.0000", DEX="205.0000", AGI="205.0000", _INT="205.0000", WIS="205.0000", CHA="205.0000", see_hide="0", see_improved_hide="0", trackable="0", ATK="0", Accuracy="0", Avoidance="0", slow_mitigation="0", version="0", maxlevel="0", scalerate="100", private_corpse="0", unique_spawn_by_name="0", underwater="0", isquest="0", emoteid="0", spellscale="100", healscale="100", no_target_hotkey="0", raid_target="0", light="0", ignore_despawn="0", show_name="0"; +INSERT INTO auras SET type=8932, npc_type=@suggestedid, name="a_fiery_rune", spell_id=8947, distance=25, aura_type=4, spawn_type=1, movement=1, duration=120, icon=-1, cast_time=-1; + +SELECT IFNULL((MAX(id) + 1), 2000000) INTO @suggestedid FROM npc_types where id LIKE '2000___'; +INSERT INTO npc_types SET id=@suggestedid, name="IOFireRuneTrap", lastname="", level="70", race="510", class="62", bodytype="5", hp="4027.6216", mana="0.0000", gender="2", texture="0", helmtexture="0", herosforgemodel="0", size="3", hp_regen_rate="0", mana_regen_rate="0", loottable_id="0", npc_spells_id="0", mindmg="52.7514", maxdmg="166.8270", attack_count="-1", special_abilities="12,1^13,1^14,1^15,1^16,1^17,1^18,1^19,1^20,1^21,1^22,1^23,1^24,1^25,1^28,1^31,1^35,1^", aggroradius="70", assistradius="0", face="0", luclin_hairstyle="0", luclin_haircolor="0", luclin_eyecolor="0", luclin_eyecolor2="0", luclin_beardcolor="0", luclin_beard="0", drakkin_heritage="0", drakkin_tattoo="0", drakkin_details="0", armortint_red="0", armortint_green="0", armortint_blue="0", d_melee_texture1="0", d_melee_texture2="0", prim_melee_type="28", sec_melee_type="28", runspeed="1.25", MR="21.1027", CR="21.1027", DR="21.1027", FR="21.1027", PR="21.1027", Corrup="21.1027", PhR="48.3333", see_invis="0", see_invis_undead="0", qglobal="0", AC="257.3784", npc_aggro="0", spawn_limit="0", attack_delay="29.4486", findable="0", STR="205.0000", STA="205.0000", DEX="205.0000", AGI="205.0000", _INT="205.0000", WIS="205.0000", CHA="205.0000", see_hide="0", see_improved_hide="0", trackable="0", ATK="0", Accuracy="0", Avoidance="0", slow_mitigation="0", version="0", maxlevel="0", scalerate="100", private_corpse="0", unique_spawn_by_name="0", underwater="0", isquest="0", emoteid="0", spellscale="100", healscale="100", no_target_hotkey="0", raid_target="0", light="0", ignore_despawn="0", show_name="0"; +INSERT INTO auras SET type=8513, npc_type=@suggestedid, name="a_fire_rune", spell_id=8514, distance=25, aura_type=4, spawn_type=1, movement=1, duration=120, icon=-1, cast_time=-1; + +SELECT IFNULL((MAX(id) + 1), 2000000) INTO @suggestedid FROM npc_types where id LIKE '2000___'; +INSERT INTO npc_types SET id=@suggestedid, name="IOPoisonSpikesTrap55", lastname="", level="55", race="513", class="62", bodytype="5", hp="4027.6216", mana="0.0000", gender="2", texture="0", helmtexture="0", herosforgemodel="0", size="3", hp_regen_rate="0", mana_regen_rate="0", loottable_id="0", npc_spells_id="0", mindmg="52.7514", maxdmg="166.8270", attack_count="-1", special_abilities="12,1^13,1^14,1^15,1^16,1^17,1^18,1^19,1^20,1^21,1^22,1^23,1^24,1^25,1^28,1^31,1^35,1^", aggroradius="70", assistradius="0", face="0", luclin_hairstyle="0", luclin_haircolor="0", luclin_eyecolor="0", luclin_eyecolor2="0", luclin_beardcolor="0", luclin_beard="0", drakkin_heritage="0", drakkin_tattoo="0", drakkin_details="0", armortint_red="0", armortint_green="0", armortint_blue="0", d_melee_texture1="0", d_melee_texture2="0", prim_melee_type="28", sec_melee_type="28", runspeed="1.25", MR="21.1027", CR="21.1027", DR="21.1027", FR="21.1027", PR="21.1027", Corrup="21.1027", PhR="48.3333", see_invis="0", see_invis_undead="0", qglobal="0", AC="257.3784", npc_aggro="0", spawn_limit="0", attack_delay="29.4486", findable="0", STR="205.0000", STA="205.0000", DEX="205.0000", AGI="205.0000", _INT="205.0000", WIS="205.0000", CHA="205.0000", see_hide="0", see_improved_hide="0", trackable="0", ATK="0", Accuracy="0", Avoidance="0", slow_mitigation="0", version="0", maxlevel="0", scalerate="100", private_corpse="0", unique_spawn_by_name="0", underwater="0", isquest="0", emoteid="0", spellscale="100", healscale="100", no_target_hotkey="0", raid_target="0", light="0", ignore_despawn="0", show_name="0"; +INSERT INTO auras SET type=8922, npc_type=@suggestedid, name="poison_spurs", spell_id=8936, distance=25, aura_type=4, spawn_type=1, movement=1, duration=120, icon=-1, cast_time=-1; + +SELECT IFNULL((MAX(id) + 1), 2000000) INTO @suggestedid FROM npc_types where id LIKE '2000___'; +INSERT INTO npc_types SET id=@suggestedid, name="IOPoisonSpikesTrap", lastname="", level="70", race="513", class="62", bodytype="5", hp="4027.6216", mana="0.0000", gender="2", texture="0", helmtexture="0", herosforgemodel="0", size="3", hp_regen_rate="0", mana_regen_rate="0", loottable_id="0", npc_spells_id="0", mindmg="52.7514", maxdmg="166.8270", attack_count="-1", special_abilities="12,1^13,1^14,1^15,1^16,1^17,1^18,1^19,1^20,1^21,1^22,1^23,1^24,1^25,1^28,1^31,1^35,1^", aggroradius="70", assistradius="0", face="0", luclin_hairstyle="0", luclin_haircolor="0", luclin_eyecolor="0", luclin_eyecolor2="0", luclin_beardcolor="0", luclin_beard="0", drakkin_heritage="0", drakkin_tattoo="0", drakkin_details="0", armortint_red="0", armortint_green="0", armortint_blue="0", d_melee_texture1="0", d_melee_texture2="0", prim_melee_type="28", sec_melee_type="28", runspeed="1.25", MR="21.1027", CR="21.1027", DR="21.1027", FR="21.1027", PR="21.1027", Corrup="21.1027", PhR="48.3333", see_invis="0", see_invis_undead="0", qglobal="0", AC="257.3784", npc_aggro="0", spawn_limit="0", attack_delay="29.4486", findable="0", STR="205.0000", STA="205.0000", DEX="205.0000", AGI="205.0000", _INT="205.0000", WIS="205.0000", CHA="205.0000", see_hide="0", see_improved_hide="0", trackable="0", ATK="0", Accuracy="0", Avoidance="0", slow_mitigation="0", version="0", maxlevel="0", scalerate="100", private_corpse="0", unique_spawn_by_name="0", underwater="0", isquest="0", emoteid="0", spellscale="100", healscale="100", no_target_hotkey="0", raid_target="0", light="0", ignore_despawn="0", show_name="0"; +INSERT INTO auras SET type=8471, npc_type=@suggestedid, name="Poison Spikes", spell_id=8472, distance=25, aura_type=4, spawn_type=1, movement=1, duration=120, icon=-1, cast_time=-1; +UPDATE npc_types SET special_abilities = TRIM(TRAILING '^' FROM special_abilities); + diff --git a/zone/CMakeLists.txt b/zone/CMakeLists.txt index 0c2c9bd63..0171c8eb9 100644 --- a/zone/CMakeLists.txt +++ b/zone/CMakeLists.txt @@ -5,6 +5,7 @@ SET(zone_sources aa_ability.cpp aggro.cpp aggromanager.cpp + aura.cpp attack.cpp beacon.cpp bonuses.cpp @@ -136,6 +137,7 @@ SET(zone_headers aa.h aa_ability.h aggromanager.h + aura.h basic_functions.h beacon.h bot.h diff --git a/zone/aura.cpp b/zone/aura.cpp new file mode 100644 index 000000000..52b8a32d8 --- /dev/null +++ b/zone/aura.cpp @@ -0,0 +1,928 @@ +#include "../common/string_util.h" + +#include "aura.h" +#include "client.h" +#include "string_ids.h" +#include "raids.h" + +Aura::Aura(NPCType *type_data, Mob *owner, AuraRecord &record) + : NPC(type_data, 0, owner->GetPosition(), FlyMode3), spell_id(record.spell_id), distance(record.distance), + remove_timer(record.duration), movement_timer(100), process_timer(100), aura_id(-1) +{ + GiveNPCTypeData(type_data); // we will delete this later on + m_owner = owner->GetID(); + + if (record.cast_time) { + cast_timer.SetTimer(record.cast_time); + cast_timer.Disable(); // we don't want to be enabled yet + } + + if (record.aura_type < static_cast(AuraType::Max)) + type = static_cast(record.aura_type); + else + type = AuraType::OnAllGroupMembers; + + if (record.spawn_type < static_cast(AuraSpawns::Max)) + spawn_type = static_cast(record.spawn_type); + else + spawn_type = AuraSpawns::GroupMembers; + + if (record.movement < static_cast(AuraMovement::Max)) + movement_type = static_cast(record.movement); + else + movement_type = AuraMovement::Follow; + + switch (type) { + case AuraType::OnAllFriendlies: + process_func = &Aura::ProcessOnAllFriendlies; + break; + case AuraType::OnAllGroupMembers: + process_func = &Aura::ProcessOnAllGroupMembers; + break; + case AuraType::OnGroupMembersPets: + process_func = &Aura::ProcessOnGroupMembersPets; + break; + case AuraType::Totem: + process_func = &Aura::ProcessTotem; + break; + case AuraType::EnterTrap: + process_func = &Aura::ProcessEnterTrap; + break; + case AuraType::ExitTrap: + process_func = &Aura::ProcessExitTrap; + break; + default: + process_func = nullptr; + } +} + +Mob *Aura::GetOwner() +{ + return entity_list.GetMob(m_owner); +} + +// not 100% sure how this one should work and PVP affects ... +void Aura::ProcessOnAllFriendlies(Mob *owner) +{ + auto &mob_list = entity_list.GetMobList(); // read only reference so we can do it all inline + std::set delayed_remove; + bool is_buff = IsBuffSpell(spell_id); // non-buff spells don't cast on enter + + for (auto &e : mob_list) { + auto mob = e.second; + if (mob->IsClient() || mob->IsPetOwnerClient() || mob->IsMerc()) { + auto it = casted_on.find(mob->GetID()); + + if (it != casted_on.end()) { // we are already on the list, let's check for removal + if (DistanceSquared(GetPosition(), mob->GetPosition()) > distance) + delayed_remove.insert(mob->GetID()); + } else { // not on list, lets check if we're in range + if (DistanceSquared(GetPosition(), mob->GetPosition()) <= distance) { + casted_on.insert(mob->GetID()); + if (is_buff) + SpellFinished(spell_id, mob); + } + } + } + } + + for (auto &e : delayed_remove) { + auto mob = entity_list.GetMob(e); + if (mob != nullptr && is_buff) // some auras cast instant spells so no need to remove + mob->BuffFadeBySpellIDAndCaster(spell_id, GetID()); + casted_on.erase(e); + } + + // so if we have a cast timer and our set isn't empty and timer is disabled we need to enable it + if (cast_timer.GetDuration() > 0 && !cast_timer.Enabled() && !casted_on.empty()) + cast_timer.Start(); + + if (!cast_timer.Enabled() || !cast_timer.Check()) + return; + + for (auto &e : casted_on) { + auto mob = entity_list.GetMob(e); + if (mob != nullptr) + SpellFinished(spell_id, mob); + } +} + +void Aura::ProcessOnAllGroupMembers(Mob *owner) +{ + auto &mob_list = entity_list.GetMobList(); // read only reference so we can do it all inline + std::set delayed_remove; + bool is_buff = IsBuffSpell(spell_id); // non-buff spells don't cast on enter + + if (owner->IsRaidGrouped() && owner->IsClient()) { // currently raids are just client, but safety check + auto raid = owner->GetRaid(); + if (raid == nullptr) { // well shit + owner->RemoveAura(GetID(), false, true); + return; + } + auto group_id = raid->GetGroup(owner->CastToClient()); + + // some lambdas so the for loop is less horrible ... + auto verify_raid_client = [&raid, &group_id, this](Client *c) { + auto idx = raid->GetPlayerIndex(c); + if (c->GetID() == m_owner) { + return DistanceSquared(GetPosition(), c->GetPosition()) <= distance; + } else if (idx == 0xFFFFFFFF || raid->members[idx].GroupNumber != group_id || raid->members[idx].GroupNumber == 0xFFFFFFFF) { + return false; + } else if (DistanceSquared(GetPosition(), c->GetPosition()) > distance) { + return false; + } + return true; + }; + + auto verify_raid_client_pet = [&raid, &group_id, this](Mob *m) { + auto idx = raid->GetPlayerIndex(m->GetOwner()->CastToClient()); + if (m->GetOwner()->GetID() == m_owner) { + return DistanceSquared(GetPosition(), m->GetPosition()) <= distance; + } else if (idx == 0xFFFFFFFF || raid->members[idx].GroupNumber != group_id || raid->members[idx].GroupNumber == 0xFFFFFFFF) { + return false; + } else if (DistanceSquared(GetPosition(), m->GetPosition()) > distance) { + return false; + } + return true; + }; + + auto verify_raid_client_swarm = [&raid, &group_id, this](NPC *n) { + auto owner = entity_list.GetMob(n->GetSwarmOwner()); + if (owner == nullptr) + return false; + auto idx = raid->GetPlayerIndex(owner->CastToClient()); + if (owner->GetID() == m_owner) { + return DistanceSquared(GetPosition(), n->GetPosition()) <= distance; + } else if (idx == 0xFFFFFFFF || raid->members[idx].GroupNumber != group_id || raid->members[idx].GroupNumber == 0xFFFFFFFF) { + return false; + } else if (DistanceSquared(GetPosition(), n->GetPosition()) > distance) { + return false; + } + return true; + }; + + for (auto &e : mob_list) { + auto mob = e.second; + // step 1: check if we're already managing this NPC's buff + auto it = casted_on.find(mob->GetID()); + if (it != casted_on.end()) { + // verify still good! + if (mob->IsClient()) { + if (!verify_raid_client(mob->CastToClient())) + delayed_remove.insert(mob->GetID()); + } else if (mob->IsPet() && mob->IsPetOwnerClient() && mob->GetOwner()) { + if (!verify_raid_client_pet(mob)) + delayed_remove.insert(mob->GetID()); + } else if (mob->IsNPC() && mob->IsPetOwnerClient()) { + auto npc = mob->CastToNPC(); + if (!verify_raid_client_swarm(npc)) + delayed_remove.insert(mob->GetID()); + } + } else { // we're not on it! + if (mob->IsClient() && verify_raid_client(mob->CastToClient())) { + casted_on.insert(mob->GetID()); + if (is_buff) + SpellFinished(spell_id, mob); + } else if (mob->IsPet() && mob->IsPetOwnerClient() && mob->GetOwner() && verify_raid_client_pet(mob)) { + casted_on.insert(mob->GetID()); + if (is_buff) + SpellFinished(spell_id, mob); + } else if (mob->IsNPC() && mob->IsPetOwnerClient()) { + auto npc = mob->CastToNPC(); + if (verify_raid_client_swarm(npc)) { + casted_on.insert(mob->GetID()); + if (is_buff) + SpellFinished(spell_id, mob); + } + } + } + } + } else if (owner->IsGrouped()) { + auto group = owner->GetGroup(); + if (group == nullptr) { // uh oh + owner->RemoveAura(GetID(), false, true); + return; + } + + // lambdas to make for loop less ugly + auto verify_group_pet = [&group, this](Mob *m) { + auto owner = m->GetOwner(); + if (owner != nullptr && group->IsGroupMember(owner) && DistanceSquared(GetPosition(), m->GetPosition()) <= distance) + return true; + return false; + }; + + auto verify_group_swarm = [&group, this](NPC *n) { + auto owner = entity_list.GetMob(n->GetSwarmOwner()); + if (owner != nullptr && group->IsGroupMember(owner) && DistanceSquared(GetPosition(), n->GetPosition()) <= distance) + return true; + return false; + }; + + for (auto &e : mob_list) { + auto mob = e.second; + auto it = casted_on.find(mob->GetID()); + + if (it != casted_on.end()) { // make sure we're still valid + if (mob->IsPet()) { + if (!verify_group_pet(mob)) + delayed_remove.insert(mob->GetID()); + } else if (mob->IsNPC() && mob->CastToNPC()->GetSwarmInfo()) { + if (!verify_group_swarm(mob->CastToNPC())) + delayed_remove.insert(mob->GetID()); + } else if (!group->IsGroupMember(mob) || DistanceSquared(GetPosition(), mob->GetPosition()) > distance) { + delayed_remove.insert(mob->GetID()); + } + } else { // not on, check if we should be! + if (mob->IsPet() && verify_group_pet(mob)) { + casted_on.insert(mob->GetID()); + if (is_buff) + SpellFinished(spell_id, mob); + } else if (mob->IsNPC() && mob->CastToNPC()->GetSwarmInfo() && verify_group_swarm(mob->CastToNPC())) { + casted_on.insert(mob->GetID()); + if (is_buff) + SpellFinished(spell_id, mob); + } else if (group->IsGroupMember(mob) && DistanceSquared(GetPosition(), mob->GetPosition()) <= distance) { + casted_on.insert(mob->GetID()); + if (is_buff) + SpellFinished(spell_id, mob); + } + } + } + } else { + auto verify_solo = [&owner, this](Mob *m) { + if (m->IsPet() && m->GetOwnerID() == owner->GetID()) + return true; + else if (m->IsNPC() && m->CastToNPC()->GetSwarmOwner() == owner->GetID()) + return true; + else if (m->GetID() == owner->GetID()) + return true; + else + return false; + }; + for (auto &e : mob_list) { + auto mob = e.second; + auto it = casted_on.find(mob->GetID()); + bool good = verify_solo(mob); + + if (it != casted_on.end()) { // make sure still valid + if (!good || DistanceSquared(GetPosition(), mob->GetPosition()) > distance) { + delayed_remove.insert(mob->GetID()); + } + } else if (good && DistanceSquared(GetPosition(), mob->GetPosition()) <= distance) { + casted_on.insert(mob->GetID()); + if (is_buff) + SpellFinished(spell_id, mob); + } + } + } + + for (auto &e : delayed_remove) { + auto mob = entity_list.GetMob(e); + if (mob != nullptr && is_buff) // some auras cast instant spells so no need to remove + mob->BuffFadeBySpellIDAndCaster(spell_id, GetID()); + casted_on.erase(e); + } + + // so if we have a cast timer and our set isn't empty and timer is disabled we need to enable it + if (cast_timer.GetDuration() > 0 && !cast_timer.Enabled() && !casted_on.empty()) + cast_timer.Start(); + + if (!cast_timer.Enabled() || !cast_timer.Check()) + return; + + // some auras have to recast (DRU for example, non-buff too) + for (auto &e : casted_on) { + auto mob = entity_list.GetMob(e); + if (mob != nullptr) + SpellFinished(spell_id, mob); + } +} + +void Aura::ProcessOnGroupMembersPets(Mob *owner) +{ + auto &mob_list = entity_list.GetMobList(); // read only reference so we can do it all inline + std::set delayed_remove; + bool is_buff = IsBuffSpell(spell_id); // non-buff spells don't cast on enter + // This type can either live on the pet (level 55/70 MAG aura) or on the pet owner (level 85 MAG aura) + auto group_member = owner->GetOwnerOrSelf(); + + if (group_member->IsRaidGrouped() && group_member->IsClient()) { // currently raids are just client, but safety check + auto raid = group_member->GetRaid(); + if (raid == nullptr) { // well shit + owner->RemoveAura(GetID(), false, true); + return; + } + auto group_id = raid->GetGroup(group_member->CastToClient()); + + // some lambdas so the for loop is less horrible ... + auto verify_raid_client_pet = [&raid, &group_id, &group_member, this](Mob *m) { + auto idx = raid->GetPlayerIndex(m->GetOwner()->CastToClient()); + if (m->GetOwner()->GetID() == group_member->GetID()) { + return DistanceSquared(GetPosition(), m->GetPosition()) <= distance; + } else if (idx == 0xFFFFFFFF || raid->members[idx].GroupNumber != group_id || raid->members[idx].GroupNumber == 0xFFFFFFFF) { + return false; + } else if (DistanceSquared(GetPosition(), m->GetPosition()) > distance) { + return false; + } + return true; + }; + + auto verify_raid_client_swarm = [&raid, &group_id, &group_member, this](NPC *n) { + auto owner = entity_list.GetMob(n->GetSwarmOwner()); + if (owner == nullptr) + return false; + auto idx = raid->GetPlayerIndex(owner->CastToClient()); + if (owner->GetID() == group_member->GetID()) { + return DistanceSquared(GetPosition(), n->GetPosition()) <= distance; + } else if (idx == 0xFFFFFFFF || raid->members[idx].GroupNumber != group_id || raid->members[idx].GroupNumber == 0xFFFFFFFF) { + return false; + } else if (DistanceSquared(GetPosition(), n->GetPosition()) > distance) { + return false; + } + return true; + }; + + for (auto &e : mob_list) { + auto mob = e.second; + // step 1: check if we're already managing this NPC's buff + auto it = casted_on.find(mob->GetID()); + if (it != casted_on.end()) { + // verify still good! + if (mob->IsPet() && mob->IsPetOwnerClient() && mob->GetOwner()) { + if (!verify_raid_client_pet(mob)) + delayed_remove.insert(mob->GetID()); + } else if (mob->IsNPC() && mob->IsPetOwnerClient()) { + auto npc = mob->CastToNPC(); + if (!verify_raid_client_swarm(npc)) + delayed_remove.insert(mob->GetID()); + } + } else { // we're not on it! + if (mob->IsClient()) { + continue; // never hit client + } else if (mob->IsPet() && mob->IsPetOwnerClient() && mob->GetOwner() && verify_raid_client_pet(mob)) { + casted_on.insert(mob->GetID()); + if (is_buff) + SpellFinished(spell_id, mob); + } else if (mob->IsNPC() && mob->IsPetOwnerClient()) { + auto npc = mob->CastToNPC(); + if (verify_raid_client_swarm(npc)) { + casted_on.insert(mob->GetID()); + if (is_buff) + SpellFinished(spell_id, mob); + } + } + } + } + } else if (group_member->IsGrouped()) { + auto group = group_member->GetGroup(); + if (group == nullptr) { // uh oh + owner->RemoveAura(GetID(), false, true); + return; + } + + // lambdas to make for loop less ugly + auto verify_group_pet = [&group, this](Mob *m) { + auto owner = m->GetOwner(); + if (owner != nullptr && group->IsGroupMember(owner) && DistanceSquared(GetPosition(), m->GetPosition()) <= distance) + return true; + return false; + }; + + auto verify_group_swarm = [&group, this](NPC *n) { + auto owner = entity_list.GetMob(n->GetSwarmOwner()); + if (owner != nullptr && group->IsGroupMember(owner) && DistanceSquared(GetPosition(), n->GetPosition()) <= distance) + return true; + return false; + }; + + for (auto &e : mob_list) { + auto mob = e.second; + auto it = casted_on.find(mob->GetID()); + + if (it != casted_on.end()) { // make sure we're still valid + if (mob->IsPet()) { + if (!verify_group_pet(mob)) + delayed_remove.insert(mob->GetID()); + } else if (mob->IsNPC() && mob->CastToNPC()->GetSwarmInfo()) { + if (!verify_group_swarm(mob->CastToNPC())) + delayed_remove.insert(mob->GetID()); + } + } else { // not on, check if we should be! + if (mob->IsClient()) { + continue; + } else if (mob->IsPet() && verify_group_pet(mob)) { + casted_on.insert(mob->GetID()); + if (is_buff) + SpellFinished(spell_id, mob); + } else if (mob->IsNPC() && mob->CastToNPC()->GetSwarmInfo() && verify_group_swarm(mob->CastToNPC())) { + casted_on.insert(mob->GetID()); + if (is_buff) + SpellFinished(spell_id, mob); + } + } + } + } else { + auto verify_solo = [&group_member, this](Mob *m) { + if (m->IsPet() && m->GetOwnerID() == group_member->GetID()) + return true; + else if (m->IsNPC() && m->CastToNPC()->GetSwarmOwner() == group_member->GetID()) + return true; + else + return false; + }; + for (auto &e : mob_list) { + auto mob = e.second; + auto it = casted_on.find(mob->GetID()); + bool good = verify_solo(mob); + + if (it != casted_on.end()) { // make sure still valid + if (!good || DistanceSquared(GetPosition(), mob->GetPosition()) > distance) { + delayed_remove.insert(mob->GetID()); + } + } else if (good && DistanceSquared(GetPosition(), mob->GetPosition()) <= distance) { + casted_on.insert(mob->GetID()); + if (is_buff) + SpellFinished(spell_id, mob); + } + } + } + + for (auto &e : delayed_remove) { + auto mob = entity_list.GetMob(e); + if (mob != nullptr && is_buff) // some auras cast instant spells so no need to remove + mob->BuffFadeBySpellIDAndCaster(spell_id, GetID()); + casted_on.erase(e); + } + + // so if we have a cast timer and our set isn't empty and timer is disabled we need to enable it + if (cast_timer.GetDuration() > 0 && !cast_timer.Enabled() && !casted_on.empty()) + cast_timer.Start(); + + if (!cast_timer.Enabled() || !cast_timer.Check()) + return; + + // some auras have to recast (DRU for example, non-buff too) + for (auto &e : casted_on) { + auto mob = entity_list.GetMob(e); + if (mob != nullptr) + SpellFinished(spell_id, mob); + } +} + +void Aura::ProcessTotem(Mob *owner) +{ + auto &mob_list = entity_list.GetMobList(); // read only reference so we can do it all inline + std::set delayed_remove; + bool is_buff = IsBuffSpell(spell_id); // non-buff spells don't cast on enter + + for (auto &e : mob_list) { + auto mob = e.second; + if (mob == this) + continue; + if (mob == owner) + continue; + if (owner->IsAttackAllowed(mob)) { // might need more checks ... + bool in_range = DistanceSquared(GetPosition(), mob->GetPosition()) <= distance; + auto it = casted_on.find(mob->GetID()); + if (it != casted_on.end()) { + if (!in_range) + delayed_remove.insert(mob->GetID()); + } else if (in_range) { + casted_on.insert(mob->GetID()); + SpellFinished(spell_id, mob); + } + } + } + + for (auto &e : delayed_remove) { + auto mob = entity_list.GetMob(e); + if (mob != nullptr && is_buff) // some auras cast instant spells so no need to remove + mob->BuffFadeBySpellIDAndCaster(spell_id, GetID()); + casted_on.erase(e); + } + + // so if we have a cast timer and our set isn't empty and timer is disabled we need to enable it + if (cast_timer.GetDuration() > 0 && !cast_timer.Enabled() && !casted_on.empty()) + cast_timer.Start(); + + if (!cast_timer.Enabled() || !cast_timer.Check()) + return; + + for (auto &e : casted_on) { + auto mob = entity_list.GetMob(e); + if (mob != nullptr) + SpellFinished(spell_id, mob); + } +} + +void Aura::ProcessEnterTrap(Mob *owner) +{ + auto &mob_list = entity_list.GetMobList(); // read only reference so we can do it all inline + + for (auto &e : mob_list) { + auto mob = e.second; + if (mob == this) + continue; + // might need more checks ... + if (owner->IsAttackAllowed(mob) && DistanceSquared(GetPosition(), mob->GetPosition()) <= distance) { + SpellFinished(spell_id, mob); + owner->RemoveAura(GetID(), false); // if we're a buff (ex. NEC) we don't want to strip :P + break; + } + } +} + +void Aura::ProcessExitTrap(Mob *owner) +{ + auto &mob_list = entity_list.GetMobList(); // read only reference so we can do it all inline + + for (auto &e : mob_list) { + auto mob = e.second; + if (mob == this) + continue; + // might need more checks ... + if (owner->IsAttackAllowed(mob)) { + bool in_range = DistanceSquared(GetPosition(), mob->GetPosition()) <= distance; + auto it = casted_on.find(mob->GetID()); + if (it != casted_on.end()) { + if (!in_range) { + SpellFinished(spell_id, mob); + owner->RemoveAura(GetID(), false); // if we're a buff we don't want to strip :P + break; + } + } else if (in_range) { + casted_on.insert(mob->GetID()); + } + } + } +} + +// this is less than ideal, but other solutions are a bit all over the place +// and hard to reason about +void Aura::ProcessSpawns() +{ + const auto &clients = entity_list.GetClientList(); + for (auto &e : clients) { + auto c = e.second; + bool spawned = spawned_for.find(c->GetID()) != spawned_for.end(); + if (ShouldISpawnFor(c)) { + if (!spawned) { + EQApplicationPacket app; + CreateSpawnPacket(&app, this); + c->QueuePacket(&app); + SendArmorAppearance(c); + spawned_for.insert(c->GetID()); + } + } else if (spawned) { + EQApplicationPacket app; + CreateDespawnPacket(&app, false); + c->QueuePacket(&app); + spawned_for.erase(c->GetID()); + } + } + return; +} + +bool Aura::Process() +{ + // Aura::Depop clears buffs + if (p_depop) + return false; + + auto owner = entity_list.GetMob(m_owner); + if (owner == nullptr) { + Depop(); + return true; + } + + if (remove_timer.Check()) { + owner->RemoveAura(GetID(), false, true); + return true; + } + + if (movement_type == AuraMovement::Follow && GetPosition() != owner->GetPosition() && movement_timer.Check()) { + m_Position = owner->GetPosition(); + auto app = new EQApplicationPacket(OP_ClientUpdate, sizeof(PlayerPositionUpdateServer_Struct)); + auto spu = (PlayerPositionUpdateServer_Struct*)app->pBuffer; + MakeSpawnUpdate(spu); + auto it = spawned_for.begin(); + while (it != spawned_for.end()) { + auto client = entity_list.GetClientByID(*it); + if (client) { + client->QueuePacket(app); + ++it; + } else { + it = spawned_for.erase(it); + } + } + } + // TODO: waypoints? + + if (!process_timer.Check()) + return true; + + if (spawn_type != AuraSpawns::Noone) + ProcessSpawns(); // bit of a hack + + if (process_func) + process_func(*this, owner); + + // TODO: quest calls + return true; +} + +bool Aura::ShouldISpawnFor(Client *c) +{ + if (spawn_type == AuraSpawns::Noone) + return false; + + if (spawn_type == AuraSpawns::Everyone) + return true; + + // hey, it's our owner! + if (c->GetID() == m_owner) + return true; + + // so this one is a bit trickier + auto owner = GetOwner(); + if (owner == nullptr) + return false; // hmm + + owner = owner->GetOwnerOrSelf(); // pet auras we need the pet's owner + if (owner == nullptr) // shouldn't really be needed + return false; + + // gotta check again for pet aura case -.- + if (owner == c) + return true; + + if (owner->IsRaidGrouped() && owner->IsClient()) { + auto raid = owner->GetRaid(); + if (raid == nullptr) + return false; // hmm + auto group_id = raid->GetGroup(owner->CastToClient()); + if (group_id == 0xFFFFFFFF) // owner handled above, and they're in a raid and groupless + return false; + + auto idx = raid->GetPlayerIndex(c); + if (idx == 0xFFFFFFFF) // they're not in our raid! + return false; + + if (raid->members[idx].GroupNumber != group_id) // in our raid, but not our group + return false; + + return true; // we got here so we know that 1 they're in our raid and 2 they're in our group! + } else if (owner->IsGrouped()) { + auto group = owner->GetGroup(); + if (group == nullptr) + return false; // hmm + + // easy, in our group + return group->IsGroupMember(c); + } + + // our owner is not raided or grouped, and they're handled above so we don't spawn! + return false; +} + +void Aura::Depop(bool skip_strip) +{ + // NEC trap casts a dot, so we need some way to not strip :P + if (!skip_strip && IsBuffSpell(spell_id)) { + for (auto &e : casted_on) { + auto mob = entity_list.GetMob(e); + if (mob != nullptr) + mob->BuffFadeBySpellIDAndCaster(spell_id, GetID()); + } + } + casted_on.clear(); + p_depop = true; +} + +// This creates an aura from a casted spell +void Mob::MakeAura(uint16 spell_id) +{ + // TODO: verify room in AuraMgr + if (!IsValidSpell(spell_id)) + return; + + AuraRecord record; + if (!database.GetAuraEntry(spell_id, record)) { + Message(13, "Unable to find data for aura %s", spells[spell_id].name); + Log(Logs::General, Logs::Error, "Unable to find data for aura %d, check auras table.", spell_id); + return; + } + + if (!IsValidSpell(record.spell_id)) { + Message(13, "Casted spell (%d) is not valid for aura %s", record.spell_id, spells[spell_id].name); + Log(Logs::General, Logs::Error, "Casted spell (%d) is not valid for aura %d, check auras table.", + record.spell_id, spell_id); + return; + } + + if (record.aura_type > static_cast(AuraType::Max)) { + return; // TODO: log + } + + bool trap = false; + + switch (static_cast(record.aura_type)) { + case AuraType::ExitTrap: + case AuraType::EnterTrap: + case AuraType::Totem: + trap = true; + break; + default: + trap = false; + break; + } + + if (!CanSpawnAura(trap)) + return; + + const auto base = database.LoadNPCTypesData(record.npc_type); + if (base == nullptr) { + Message(13, "Unable to load NPC data for aura %s", spells[spell_id].teleport_zone); + Log(Logs::General, Logs::Error, + "Unable to load NPC data for aura %s (NPC ID %d), check auras and npc_types tables.", + spells[spell_id].teleport_zone, record.npc_type); + return; + } + + auto npc_type = new NPCType; + memcpy(npc_type, base, sizeof(NPCType)); + + strn0cpy(npc_type->name, record.name, 64); + + auto npc = new Aura(npc_type, this, record); + npc->SetAuraID(spell_id); + entity_list.AddNPC(npc, false); + + if (trap) + AddTrap(npc, record); + else + AddAura(npc, record); +} + +bool ZoneDatabase::GetAuraEntry(uint16 spell_id, AuraRecord &record) +{ + auto query = StringFormat("SELECT npc_type, name, spell_id, distance, aura_type, spawn_type, movement, " + "duration, icon, cast_time FROM auras WHERE type='%d'", + spell_id); + + auto results = QueryDatabase(query); + if (!results.Success()) + return false; + + if (results.RowCount() != 1) + return false; + + auto row = results.begin(); + + record.npc_type = atoi(row[0]); + strn0cpy(record.name, row[1], 64); + record.spell_id = atoi(row[2]); + record.distance = atoi(row[3]); + record.distance *= record.distance; // so we can avoid sqrt + record.aura_type = atoi(row[4]); + record.spawn_type = atoi(row[5]); + record.movement = atoi(row[6]); + record.duration = atoi(row[7]) * 1000; // DB is in seconds + record.icon = atoi(row[8]); + record.cast_time = atoi(row[9]) * 1000; // DB is in seconds + + return true; +} + +void Mob::AddAura(Aura *aura, AuraRecord &record) +{ + // this is called only when it's safe + assert(aura != nullptr); + strn0cpy(aura_mgr.auras[aura_mgr.count].name, aura->GetCleanName(), 64); + aura_mgr.auras[aura_mgr.count].spawn_id = aura->GetID(); + aura_mgr.auras[aura_mgr.count].aura = aura; + if (record.icon == -1) + aura_mgr.auras[aura_mgr.count].icon = spells[record.spell_id].new_icon; + else + aura_mgr.auras[aura_mgr.count].icon = record.icon; + if (IsClient()) { + auto outapp = new EQApplicationPacket(OP_UpdateAura, sizeof(AuraCreate_Struct)); + auto aura_create = (AuraCreate_Struct *)outapp->pBuffer; + aura_create->action = 0; + aura_create->type = 1; // this can be 0 sometimes too + strn0cpy(aura_create->aura_name, aura_mgr.auras[aura_mgr.count].name, 64); + aura_create->entity_id = aura_mgr.auras[aura_mgr.count].spawn_id; + aura_create->icon = aura_mgr.auras[aura_mgr.count].icon; + CastToClient()->FastQueuePacket(&outapp); + } + // we can increment this now + aura_mgr.count++; +} + +void Mob::AddTrap(Aura *aura, AuraRecord &record) +{ + // this is called only when it's safe + assert(aura != nullptr); + strn0cpy(trap_mgr.auras[trap_mgr.count].name, aura->GetCleanName(), 64); + trap_mgr.auras[trap_mgr.count].spawn_id = aura->GetID(); + trap_mgr.auras[trap_mgr.count].aura = aura; + if (record.icon == -1) + trap_mgr.auras[trap_mgr.count].icon = spells[record.spell_id].new_icon; + else + trap_mgr.auras[trap_mgr.count].icon = record.icon; + // doesn't send to client + trap_mgr.count++; +} + +bool Mob::CanSpawnAura(bool trap) +{ + if (trap && !HasFreeTrapSlots()) { + Message_StringID(MT_SpellFailure, NO_MORE_TRAPS); + return false; + } else if (!trap && !HasFreeAuraSlots()) { + Message_StringID(MT_SpellFailure, NO_MORE_AURAS); + return false; + } + + return true; +} + +void Mob::RemoveAllAuras() +{ + if (IsClient()) { + database.SaveAuras(CastToClient()); + EQApplicationPacket outapp(OP_UpdateAura, 4); + outapp.WriteUInt32(2); + CastToClient()->QueuePacket(&outapp); + } + + // this is sent on camp/zone, so it just despawns? + if (aura_mgr.count) { + for (auto &e : aura_mgr.auras) { + if (e.aura) + e.aura->Depop(); + } + } + + aura_mgr.count = 0; + + if (trap_mgr.count) { + for (auto &e : trap_mgr.auras) { + if (e.aura) + e.aura->Depop(); + } + } + + trap_mgr.count = 0; + + return; +} + +void Mob::RemoveAura(int spawn_id, bool skip_strip, bool expired) +{ + for (int i = 0; i < aura_mgr.count; ++i) { + auto &aura = aura_mgr.auras[i]; + if (aura.spawn_id == spawn_id) { + if (aura.aura) + aura.aura->Depop(skip_strip); + if (expired && IsClient()) + CastToClient()->SendColoredText( + CC_Yellow, StringFormat("%s has expired.", aura.name)); // TODO: verify color + while (aura_mgr.count - 1 > i) { + i++; + aura.spawn_id = aura_mgr.auras[i].spawn_id; + aura.icon = aura_mgr.auras[i].icon; + aura.aura = aura_mgr.auras[i].aura; + aura_mgr.auras[i].aura = nullptr; + strn0cpy(aura.name, aura_mgr.auras[i].name, 64); + } + aura_mgr.count--; + return; + } + } + + for (int i = 0; i < trap_mgr.count; ++i) { + auto &aura = trap_mgr.auras[i]; + if (aura.spawn_id == spawn_id) { + if (aura.aura) + aura.aura->Depop(skip_strip); + if (expired && IsClient()) + CastToClient()->SendColoredText( + CC_Yellow, StringFormat("%s has expired.", aura.name)); // TODO: verify color + while (trap_mgr.count - 1 > i) { + i++; + aura.spawn_id = trap_mgr.auras[i].spawn_id; + aura.icon = trap_mgr.auras[i].icon; + aura.aura = trap_mgr.auras[i].aura; + trap_mgr.auras[i].aura = nullptr; + strn0cpy(aura.name, trap_mgr.auras[i].name, 64); + } + trap_mgr.count--; + return; + } + } + + return; +} + diff --git a/zone/aura.h b/zone/aura.h new file mode 100644 index 000000000..ff4f2d51c --- /dev/null +++ b/zone/aura.h @@ -0,0 +1,91 @@ +#ifndef AURA_H +#define AURA_H + +#include +#include + +#include "mob.h" +#include "npc.h" +#include "../common/types.h" +#include "../common/timer.h" + +class Group; +class Raid; +class Mob; +struct NPCType; + +enum class AuraType { + OnAllFriendlies, // AE PC/Pet basically (ex. Circle of Power) + OnAllGroupMembers, // Normal buffing aura (ex. Champion's Aura) + OnGroupMembersPets, // Hits just pets (ex. Rathe's Strength) + Totem, // Starts pulsing on a timer when an enemy enters (ex. Idol of Malos) + EnterTrap, // Casts once when an enemy enters (ex. Fire Rune) + ExitTrap, // Casts when they start to flee (ex. Poison Spikes Trap) + FullyScripted, // We just call script function not a predefined + Max +}; + +enum class AuraSpawns { + GroupMembers, // most auras use this + Everyone, // this is like traps and clickies who cast on everyone + Noone, // custom! + Max +}; + +enum class AuraMovement { + Follow, // follows caster + Stationary, + Pathing, // some sorted pathing TODO: implement + Max +}; + +class Aura : public NPC +{ + // NOTE: We may have to override more virtual functions if they're causing issues +public: + Aura(NPCType *type_data, Mob *owner, AuraRecord &record); + ~Aura() { }; + + bool IsAura() const { return true; } + bool Process(); + void Depop(bool skip_strip = false); + Mob *GetOwner(); + + void ProcessOnAllFriendlies(Mob *owner); + void ProcessOnAllGroupMembers(Mob *owner); + void ProcessOnGroupMembersPets(Mob *owner); + void ProcessTotem(Mob *owner); + void ProcessEnterTrap(Mob *owner); + void ProcessExitTrap(Mob *owner); + void ProcessSpawns(); + + // we only save auras that follow you, and player casted + inline bool AuraZones() { return movement_type == AuraMovement::Follow && aura_id > -1; } + inline int GetSpellID() { return spell_id; } + inline int GetAuraID() { return aura_id; } + inline void SetAuraID(int in) { aura_id = in; } + + bool ShouldISpawnFor(Client *c); + // so when we join a group, we need to spawn not already spawned auras + // This is only possible when spawn type is GroupMembers + inline bool JoinGroupSpawnCheck() { return spawn_type == AuraSpawns::GroupMembers; } +private: + int m_owner; + int aura_id; // spell ID of the aura spell -1 if aura isn't from a casted spell + int spell_id; // spell we cast + int distance; // distance we remove + Timer remove_timer; // when we depop + Timer process_timer; // rate limit process calls + Timer cast_timer; // some auras pulse + Timer movement_timer; // rate limit movement updates + AuraType type; + AuraSpawns spawn_type; + AuraMovement movement_type; + + std::function process_func; + std::set casted_on; // we keep track of the other entities we've casted on + std::set spawned_for; +}; + +#endif /* !AURA_H */ + diff --git a/zone/bonuses.cpp b/zone/bonuses.cpp index 22a323a81..4406508fe 100644 --- a/zone/bonuses.cpp +++ b/zone/bonuses.cpp @@ -1460,6 +1460,14 @@ void Mob::ApplyAABonuses(const AA::Rank &rank, StatBonuses *newbon) newbon->FeignedMinionChance = base1; break; + case SE_AdditionalAura: + newbon->aura_slots += base1; + break; + + case SE_IncreaseTrapCount: + newbon->trap_slots += base1; + break; + // to do case SE_PetDiscipline: break; @@ -3201,6 +3209,16 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne if (new_bonus->FeignedCastOnChance < effect_value) new_bonus->FeignedCastOnChance = effect_value; break; + + case SE_AdditionalAura: + if (new_bonus->aura_slots < effect_value) + new_bonus->aura_slots = effect_value; + break; + + case SE_IncreaseTrapCount: + if (new_bonus->trap_slots < effect_value) + new_bonus->trap_slots = effect_value; + break; //Special custom cases for loading effects on to NPC from 'npc_spels_effects' table if (IsAISpellEffect) { diff --git a/zone/client.h b/zone/client.h index 640d9dc98..952c98333 100644 --- a/zone/client.h +++ b/zone/client.h @@ -299,6 +299,7 @@ public: const char* GetBuyerWelcomeMessage() { return BuyerWelcomeMessage.c_str(); } void FillSpawnStruct(NewSpawn_Struct* ns, Mob* ForWho); + bool ShouldISpawnFor(Client *c) { return !GMHideMe(c) && !IsHoveringForRespawn(); } virtual bool Process(); void LogMerchant(Client* player, Mob* merchant, uint32 quantity, uint32 price, const EQEmu::ItemData* item, bool buying); void SendPacketQueue(bool Block = true); diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index e8ae68e69..8a2c266f1 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -323,6 +323,7 @@ void MapOpcodes() ConnectedOpcodes[OP_RecipesSearch] = &Client::Handle_OP_RecipesSearch; ConnectedOpcodes[OP_ReloadUI] = &Client::Handle_OP_ReloadUI; ConnectedOpcodes[OP_RemoveBlockedBuffs] = &Client::Handle_OP_RemoveBlockedBuffs; + ConnectedOpcodes[OP_RemoveTrap] = &Client::Handle_OP_RemoveTrap; ConnectedOpcodes[OP_Report] = &Client::Handle_OP_Report; ConnectedOpcodes[OP_RequestDuel] = &Client::Handle_OP_RequestDuel; ConnectedOpcodes[OP_RequestTitles] = &Client::Handle_OP_RequestTitles; @@ -383,6 +384,7 @@ void MapOpcodes() ConnectedOpcodes[OP_TributeUpdate] = &Client::Handle_OP_TributeUpdate; ConnectedOpcodes[OP_VetClaimRequest] = &Client::Handle_OP_VetClaimRequest; ConnectedOpcodes[OP_VoiceMacroIn] = &Client::Handle_OP_VoiceMacroIn; + ConnectedOpcodes[OP_UpdateAura] = &Client::Handle_OP_UpdateAura;; ConnectedOpcodes[OP_WearChange] = &Client::Handle_OP_WearChange; ConnectedOpcodes[OP_WhoAllRequest] = &Client::Handle_OP_WhoAllRequest; ConnectedOpcodes[OP_WorldUnknown001] = &Client::Handle_OP_Ignore; @@ -882,6 +884,8 @@ void Client::CompleteConnect() SetPetCommandState(PET_BUTTON_SPELLHOLD, 0); } + database.LoadAuras(this); // this ends up spawning them so probably safer to load this later (here) + entity_list.RefreshClientXTargets(this); worldserver.RequestTellQueue(GetName()); @@ -11792,6 +11796,18 @@ void Client::Handle_OP_RemoveBlockedBuffs(const EQApplicationPacket *app) } } +void Client::Handle_OP_RemoveTrap(const EQApplicationPacket *app) +{ + if (app->size != 4) {// just an int + Log(Logs::General, Logs::None, "Size mismatch in OP_RemoveTrap expected 4 got %i", app->size); + DumpPacket(app); + return; + } + + auto id = app->ReadUInt32(0); + RemoveAura(id); +} + void Client::Handle_OP_Report(const EQApplicationPacket *app) { if (!CanUseReport) @@ -14310,6 +14326,23 @@ void Client::Handle_OP_VoiceMacroIn(const EQApplicationPacket *app) } +void Client::Handle_OP_UpdateAura(const EQApplicationPacket *app) +{ + if (app->size != sizeof(AuraDestory_Struct)) { + Log(Logs::General, Logs::None, "Size mismatch in OP_UpdateAura expected %i got %i", + sizeof(AuraDestory_Struct), app->size); + return; + } + + // client only sends this for removing + auto aura = (AuraDestory_Struct *)app->pBuffer; + if (aura->action != 1) + return; // could log I guess, but should only ever get this action + + RemoveAura(aura->entity_id); + return; +} + void Client::Handle_OP_WearChange(const EQApplicationPacket *app) { if (app->size != sizeof(WearChange_Struct)) { diff --git a/zone/client_packet.h b/zone/client_packet.h index 76a26e04e..c63a57825 100644 --- a/zone/client_packet.h +++ b/zone/client_packet.h @@ -236,6 +236,7 @@ void Handle_OP_RecipesSearch(const EQApplicationPacket *app); void Handle_OP_ReloadUI(const EQApplicationPacket *app); void Handle_OP_RemoveBlockedBuffs(const EQApplicationPacket *app); + void Handle_OP_RemoveTrap(const EQApplicationPacket *app); void Handle_OP_Report(const EQApplicationPacket *app); void Handle_OP_RequestDuel(const EQApplicationPacket *app); void Handle_OP_RequestTitles(const EQApplicationPacket *app); @@ -288,6 +289,7 @@ void Handle_OP_TributeNPC(const EQApplicationPacket *app); void Handle_OP_TributeToggle(const EQApplicationPacket *app); void Handle_OP_TributeUpdate(const EQApplicationPacket *app); + void Handle_OP_UpdateAura(const EQApplicationPacket *app); void Handle_OP_VetClaimRequest(const EQApplicationPacket *app); void Handle_OP_VoiceMacroIn(const EQApplicationPacket *app); void Handle_OP_WearChange(const EQApplicationPacket *app); diff --git a/zone/client_process.cpp b/zone/client_process.cpp index 672848acc..3e08c1fba 100644 --- a/zone/client_process.cpp +++ b/zone/client_process.cpp @@ -723,6 +723,8 @@ void Client::OnDisconnect(bool hard_disconnect) { } } + RemoveAllAuras(); + Mob *Other = trade->With(); if(Other) { diff --git a/zone/common.h b/zone/common.h index 7294c87dc..246600c8f 100644 --- a/zone/common.h +++ b/zone/common.h @@ -105,6 +105,8 @@ #define PET_BUTTON_FOCUS 8 #define PET_BUTTON_SPELLHOLD 9 +#define AURA_HARDCAP 2 + typedef enum { //focus types focusSpellHaste = 1, focusSpellDuration, @@ -536,6 +538,8 @@ struct StatBonuses { int16 FeignedCastOnChance; // Percent Value bool PetCommands[PET_MAXCOMMANDS]; // SPA 267 int FeignedMinionChance; // SPA 281 base1 = chance, just like normal FD + int aura_slots; + int trap_slots; }; typedef struct diff --git a/zone/entity.cpp b/zone/entity.cpp index e9d8139a7..344e79a04 100644 --- a/zone/entity.cpp +++ b/zone/entity.cpp @@ -1246,12 +1246,9 @@ void EntityList::SendZoneSpawns(Client *client) auto it = mob_list.begin(); while (it != mob_list.end()) { Mob *ent = it->second; - if (!(ent->InZone()) || (ent->IsClient())) { - if (ent->CastToClient()->GMHideMe(client) || - ent->CastToClient()->IsHoveringForRespawn()) { - ++it; - continue; - } + if (!ent->InZone() || !ent->ShouldISpawnFor(client)) { + ++it; + continue; } app = new EQApplicationPacket; @@ -1279,17 +1276,16 @@ void EntityList::SendZoneSpawnsBulk(Client *client) for (auto it = mob_list.begin(); it != mob_list.end(); ++it) { spawn = it->second; if (spawn && spawn->GetID() > 0 && spawn->Spawned()) { - if (spawn->IsClient() && (spawn->CastToClient()->GMHideMe(client) || - spawn->CastToClient()->IsHoveringForRespawn())) + if (!spawn->ShouldISpawnFor(client)) continue; #if 1 const glm::vec4& spos = spawn->GetPosition(); - + delaypkt = false; if (DistanceSquared(cpos, spos) > dmax || (spawn->IsClient() && (spawn->GetRace() == MINOR_ILL_OBJ || spawn->GetRace() == TREE))) delaypkt = true; - + if (delaypkt) { app = new EQApplicationPacket; spawn->CreateSpawnPacket(app); @@ -2659,7 +2655,7 @@ void EntityList::SendPositionUpdates(Client *client, uint32 cLastUpdate, float u mob && !mob->IsCorpse() && (it->second != client) && (mob->IsClient() || iSendEvenIfNotChanged || (mob->LastChange() >= cLastUpdate)) - && (!it->second->IsClient() || !it->second->CastToClient()->GMHideMe(client)) + && (it->second->ShouldISpawnFor(client)) ) { if ( update_range == 0 diff --git a/zone/entity.h b/zone/entity.h index 036e0c419..29075d9fe 100644 --- a/zone/entity.h +++ b/zone/entity.h @@ -80,6 +80,7 @@ public: virtual bool IsBeacon() const { return false; } virtual bool IsEncounter() const { return false; } virtual bool IsBot() const { return false; } + virtual bool IsAura() const { return false; } virtual bool Process() { return false; } virtual bool Save() { return true; } diff --git a/zone/fearpath.cpp b/zone/fearpath.cpp index 4d39717b5..c2ce95fbe 100644 --- a/zone/fearpath.cpp +++ b/zone/fearpath.cpp @@ -154,7 +154,8 @@ void Mob::CalculateNewFearpoint() int loop = 0; float ranx, rany, ranz; - currently_fleeing = false; + + currently_fleeing = true; while (loop < 100) //Max 100 tries { int ran = 250 - (loop*2); @@ -167,11 +168,13 @@ void Mob::CalculateNewFearpoint() float fdist = ranz - GetZ(); if (fdist >= -12 && fdist <= 12 && CheckCoordLosNoZLeaps(GetX(),GetY(),GetZ(),ranx,rany,ranz)) { - currently_fleeing = true; break; } } - if (currently_fleeing) - m_FearWalkTarget = glm::vec3(ranx, rany, ranz); + + if (loop <= 100) + { + m_FearWalkTarget = glm::vec3(ranx, rany, ranz); + } } diff --git a/zone/mob.h b/zone/mob.h index 8286bf647..c32887ff5 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -45,6 +45,8 @@ class EQApplicationPacket; class Group; class NPC; class Raid; +class Aura; +struct AuraRecord; struct NewSpawn_Struct; struct PlayerPositionUpdateServer_Struct; @@ -85,6 +87,23 @@ public: int params[MAX_SPECIAL_ATTACK_PARAMS]; }; + struct AuraInfo { + char name[64]; + int spawn_id; + int icon; + Aura *aura; + AuraInfo() : spawn_id(0), icon(0), aura(nullptr) + { + memset(name, 0, 64); + } + }; + + struct AuraMgr { + int count; // active auras + AuraInfo auras[AURA_HARDCAP]; + AuraMgr() : count(0) { } + }; + Mob(const char* in_name, const char* in_lastname, int32 in_cur_hp, @@ -308,6 +327,7 @@ public: void BuffProcess(); virtual void DoBuffTic(const Buffs_Struct &buff, int slot, Mob* caster = nullptr); void BuffFadeBySpellID(uint16 spell_id); + void BuffFadeBySpellIDAndCaster(uint16 spell_id, uint16 caster_id); void BuffFadeByEffect(int effectid, int skipslot = -1); void BuffFadeAll(); void BuffFadeNonPersistDeath(); @@ -315,6 +335,7 @@ public: void BuffFadeBySlot(int slot, bool iRecalcBonuses = true); void BuffFadeDetrimentalByCaster(Mob *caster); void BuffFadeBySitModifier(); + bool IsAffectedByBuff(uint16 spell_id); void BuffModifyDurationBySpellID(uint16 spell_id, int32 newDuration); int AddBuff(Mob *caster, const uint16 spell_id, int duration = 0, int32 level_override = -1); int CanBuffStack(uint16 spellid, uint8 caster_level, bool iFailIfOverwrite = false); @@ -528,6 +549,7 @@ public: void SendPosition(); void SetSpawned() { spawned = true; }; bool Spawned() { return spawned; }; + virtual bool ShouldISpawnFor(Client *c) { return true; } void SetFlyMode(uint8 flymode); inline void Teleport(glm::vec3 NewPosition) { m_Position.x = NewPosition.x; m_Position.y = NewPosition.y; m_Position.z = NewPosition.z; }; @@ -604,6 +626,19 @@ public: bool PlotPositionAroundTarget(Mob* target, float &x_dest, float &y_dest, float &z_dest, bool lookForAftArc = true); + // aura functions + void MakeAura(uint16 spell_id); + inline int GetAuraSlots() { return 1 + aabonuses.aura_slots + itembonuses.aura_slots + spellbonuses.aura_slots; } + inline int GetTrapSlots() { return 1 + aabonuses.trap_slots + itembonuses.trap_slots + spellbonuses.trap_slots; } + inline bool HasFreeAuraSlots() { return aura_mgr.count < GetAuraSlots(); } + inline bool HasFreeTrapSlots() { return trap_mgr.count < GetTrapSlots(); } + void AddAura(Aura *aura, AuraRecord &record); + void AddTrap(Aura *aura, AuraRecord &record); + bool CanSpawnAura(bool trap); + void RemoveAura(int spawn_id, bool skip_strip = false, bool expired = false); + void RemoveAllAuras(); + inline AuraMgr &GetAuraMgr() { return aura_mgr; } // mainly used for zone db loading/saving + //Procs void TriggerDefensiveProcs(Mob *on, uint16 hand = EQEmu::inventory::slotPrimary, bool FromSkillProc = false, int damage = 0); bool AddRangedProc(uint16 spell_id, uint16 iChance = 3, uint16 base_spell_id = SPELL_UNKNOWN); @@ -1465,6 +1500,9 @@ protected: bool IsHorse; + AuraMgr aura_mgr; + AuraMgr trap_mgr; + private: void _StopSong(); //this is not what you think it is Mob* target; diff --git a/zone/npc.h b/zone/npc.h index 30e54da55..c973eb79d 100644 --- a/zone/npc.h +++ b/zone/npc.h @@ -90,6 +90,7 @@ class Client; class Group; class Raid; class Spawn2; +class Aura; namespace EQEmu { @@ -425,6 +426,7 @@ protected: NPCType* NPCTypedata_ours; //special case for npcs with uniquely created data. friend class EntityList; + friend class Aura; std::list faction_list; uint32 copper; uint32 silver; diff --git a/zone/raids.cpp b/zone/raids.cpp index baf2d71b0..26845910c 100644 --- a/zone/raids.cpp +++ b/zone/raids.cpp @@ -491,6 +491,14 @@ uint32 Raid::GetPlayerIndex(const char *name){ return 0; //should never get to here if we do everything else right, set it to 0 so we never crash things that rely on it. } +uint32 Raid::GetPlayerIndex(Client *c) +{ + for (int i = 0; i < MAX_RAID_MEMBERS; ++i) + if (c == members[i].member) + return i; + return 0xFFFFFFFF; // return sentinel value, make sure you check it unlike the above function +} + Client *Raid::GetClientByIndex(uint16 index) { if(index > MAX_RAID_MEMBERS) diff --git a/zone/raids.h b/zone/raids.h index 232a0057f..df43d7755 100644 --- a/zone/raids.h +++ b/zone/raids.h @@ -142,6 +142,7 @@ public: //keeps me from having to keep iterating through the list //when I want lots of data from the same entry uint32 GetPlayerIndex(const char *name); + uint32 GetPlayerIndex(Client *c); //for perl interface Client *GetClientByIndex(uint16 index); const char *GetClientNameByIndex(uint8 index); diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index 2f0d65907..fc9cd51a3 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -2791,6 +2791,10 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove break; } + case SE_PersistentEffect: + MakeAura(spell_id); + break; + // Handled Elsewhere case SE_ImmuneFleeing: case SE_NegateSpellEffect: diff --git a/zone/spells.cpp b/zone/spells.cpp index 58121a760..8e7d6721a 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -3447,7 +3447,7 @@ bool Mob::SpellOnTarget(uint16 spell_id, Mob *spelltar, bool reflect, bool use_r if(spelltar->IsClient() && spelltar->CastToClient()->IsHoveringForRespawn()) return false; - if(IsDetrimentalSpell(spell_id) && !IsAttackAllowed(spelltar) && !IsResurrectionEffects(spell_id)) { + if(IsDetrimentalSpell(spell_id) && !IsAttackAllowed(spelltar, true) && !IsResurrectionEffects(spell_id)) { if(!IsClient() || !CastToClient()->GetGM()) { Message_StringID(MT_SpellFailure, SPELL_NO_HOLD); return false; @@ -4189,6 +4189,21 @@ void Mob::BuffFadeBySpellID(uint16 spell_id) CalcBonuses(); } +void Mob::BuffFadeBySpellIDAndCaster(uint16 spell_id, uint16 caster_id) +{ + bool recalc_bonus = false; + auto buff_count = GetMaxTotalSlots(); + for (int i = 0; i < buff_count; ++i) { + if (buffs[i].spellid == spell_id && buffs[i].casterid == caster_id) { + BuffFadeBySlot(i, false); + recalc_bonus = true; + } + } + + if (recalc_bonus) + CalcBonuses(); +} + // removes buffs containing effectid, skipping skipslot void Mob::BuffFadeByEffect(int effectid, int skipslot) { @@ -4207,6 +4222,16 @@ void Mob::BuffFadeByEffect(int effectid, int skipslot) CalcBonuses(); } +bool Mob::IsAffectedByBuff(uint16 spell_id) +{ + int buff_count = GetMaxTotalSlots(); + for (int i = 0; i < buff_count; ++i) + if (buffs[i].spellid == spell_id) + return true; + + return false; +} + // checks if 'this' can be affected by spell_id from caster // returns true if the spell should fail, false otherwise bool Mob::IsImmuneToSpell(uint16 spell_id, Mob *caster) diff --git a/zone/string_ids.h b/zone/string_ids.h index 65810d576..663d2067a 100644 --- a/zone/string_ids.h +++ b/zone/string_ids.h @@ -361,6 +361,7 @@ #define GAIN_GROUP_LEADERSHIP_EXP 8788 // #define GAIN_RAID_LEADERSHIP_EXP 8789 // #define BUFF_MINUTES_REMAINING 8799 //%1 (%2 minutes remaining) +#define NO_MORE_TRAPS 9002 //You have already placed your maximum number of traps. #define FEAR_TOO_HIGH 9035 //Your target is too high of a level for your fear spell. #define SLOW_MOSTLY_SUCCESSFUL 9029 //Your spell was mostly successful. #define SLOW_PARTIALLY_SUCCESSFUL 9030 // Your spell was partially successful. @@ -375,6 +376,7 @@ #define SHAKE_OFF_STUN 9077 //You shake off the stun effect! #define STRIKETHROUGH_STRING 9078 //You strike through your opponent's defenses! #define SPELL_REFLECT 9082 //%1's spell has been reflected by %2. +#define NO_MORE_AURAS 9160 //You do not have sufficient focus to maintain that ability. #define NEW_SPELLS_AVAIL 9149 //You have new spells available to you. Check the merchants near your guild master. #define FD_CAST_ON_NO_BREAK 9174 //The strength of your will allows you to resume feigning death. #define SNEAK_RESTRICT 9240 //You can not use this ability because you have not been hidden for long enough. diff --git a/zone/waypoints.cpp b/zone/waypoints.cpp index c56f0daaf..8eb838a72 100644 --- a/zone/waypoints.cpp +++ b/zone/waypoints.cpp @@ -495,7 +495,8 @@ bool Mob::MakeNewPositionAndSendUpdate(float x, float y, float z, int speed, boo m_Position.y = new_y; m_Position.z = new_z; - if(fix_z_timer.Check() && !this->IsEngaged()) + if(fix_z_timer.Check() && + (!this->IsEngaged() || flee_mode || currently_fleeing)) this->FixZ(); tar_ndx++; @@ -1129,4 +1130,4 @@ void NPC::SaveGuardSpotCharm() void NPC::RestoreGuardSpotCharm() { m_GuardPoint = m_GuardPointSaved; -} \ No newline at end of file +} diff --git a/zone/zonedb.cpp b/zone/zonedb.cpp index c90b30ce7..bd28bcba3 100644 --- a/zone/zonedb.cpp +++ b/zone/zonedb.cpp @@ -11,6 +11,7 @@ #include "merc.h" #include "zone.h" #include "zonedb.h" +#include "aura.h" #include #include @@ -3152,6 +3153,37 @@ void ZoneDatabase::LoadBuffs(Client *client) } } +void ZoneDatabase::SaveAuras(Client *c) +{ + auto query = StringFormat("DELETE FROM `character_auras` WHERE `id` = %u", c->CharacterID()); + auto results = database.QueryDatabase(query); + if (!results.Success()) + return; + + const auto &auras = c->GetAuraMgr(); + for (int i = 0; i < auras.count; ++i) { + auto aura = auras.auras[i].aura; + if (aura && aura->AuraZones()) { + query = StringFormat("INSERT INTO `character_auras` (id, slot, spell_id) VALUES(%u, %d, %d)", + c->CharacterID(), i, aura->GetAuraID()); + auto results = database.QueryDatabase(query); + if (!results.Success()) + return; + } + } +} + +void ZoneDatabase::LoadAuras(Client *c) +{ + auto query = StringFormat("SELECT `spell_id` FROM `character_auras` WHERE `id` = %u ORDER BY `slot`", c->CharacterID()); + auto results = database.QueryDatabase(query); + if (!results.Success()) + return; + + for (auto row = results.begin(); row != results.end(); ++row) + c->MakeAura(atoi(row[0])); +} + void ZoneDatabase::SavePetInfo(Client *client) { PetInfo *petinfo = nullptr; diff --git a/zone/zonedb.h b/zone/zonedb.h index b592be480..a8ab3659f 100644 --- a/zone/zonedb.h +++ b/zone/zonedb.h @@ -123,6 +123,19 @@ struct PetRecord { uint32 equipmentset; // default equipment for the pet }; +struct AuraRecord { + uint32 npc_type; + char name[64]; // name shown in UI if shown and spawn name + int spell_id; + int distance; + int aura_type; + int spawn_type; + int movement; + int duration; // seconds some live for 90 mins (normal) others for 2 mins (traps) + int icon; // -1 will use the buffs NEW_ICON + int cast_time; // seconds some auras recast on a timer, most seem to be every 12 seconds +}; + // Actual pet info for a client. struct PetInfo { uint16 SpellID; @@ -260,6 +273,8 @@ public: void SaveBuffs(Client *c); void LoadBuffs(Client *c); + void SaveAuras(Client *c); + void LoadAuras(Client *c); void LoadPetInfo(Client *c); void SavePetInfo(Client *c); void RemoveTempFactions(Client *c); @@ -404,6 +419,7 @@ public: void AddLootDropToNPC(NPC* npc, uint32 lootdrop_id, ItemList* itemlist, uint8 droplimit, uint8 mindrop); uint32 GetMaxNPCSpellsID(); uint32 GetMaxNPCSpellsEffectsID(); + bool GetAuraEntry(uint16 spell_id, AuraRecord &record); DBnpcspells_Struct* GetNPCSpells(uint32 iDBSpellsID); DBnpcspellseffects_Struct* GetNPCSpellsEffects(uint32 iDBSpellsEffectsID);