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/patches/rof2.cpp b/common/patches/rof2.cpp index 30efa95b9..2e8d8458b 100644 --- a/common/patches/rof2.cpp +++ b/common/patches/rof2.cpp @@ -4120,6 +4120,7 @@ namespace RoF2 VARSTRUCT_ENCODE_STRING(Buffer, emu->name); VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->spawnId); VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->level); + // actually melee range variable, this probably screws the shit out of melee ranges :D if (emu->DestructibleObject) { VARSTRUCT_ENCODE_TYPE(float, Buffer, 10); // was int and 0x41200000 @@ -4128,7 +4129,7 @@ namespace RoF2 { VARSTRUCT_ENCODE_TYPE(float, Buffer, SpawnSize - 0.7); // Eye Height? } - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->NPC); + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->NPC); // 0 PC, 1 NPC etc structs::Spawn_Struct_Bitfields *Bitfields = (structs::Spawn_Struct_Bitfields*)Buffer; @@ -4159,6 +4160,7 @@ namespace RoF2 Buffer += sizeof(structs::Spawn_Struct_Bitfields); + // actually part of bitfields uint8 OtherData = 0; if (emu->class_ == 62) //LDoN Chest @@ -4174,6 +4176,7 @@ namespace RoF2 OtherData = OtherData | 0xe1; // Live has 0xe1 for OtherData VARSTRUCT_ENCODE_TYPE(uint8, Buffer, OtherData); + // float EmitterScalingRadius if (emu->DestructibleObject) { @@ -4183,6 +4186,7 @@ namespace RoF2 { VARSTRUCT_ENCODE_TYPE(float, Buffer, -1); // unknown3 } + // int DefaultEmitterID VARSTRUCT_ENCODE_TYPE(float, Buffer, 0); // unknown4 if (emu->DestructibleObject || emu->class_ == 62) @@ -4192,8 +4196,9 @@ namespace RoF2 VARSTRUCT_ENCODE_STRING(Buffer, emu->DestructibleString); VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->DestructibleAppearance); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->DestructibleUnk1); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->DestructibleUnk1); // ObjectAnimationID + // these 10 are SoundIDs VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->DestructibleID1); VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->DestructibleID2); VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->DestructibleID3); @@ -4205,8 +4210,8 @@ namespace RoF2 VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->DestructibleUnk5); VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->DestructibleUnk6); VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->DestructibleUnk7); - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->DestructibleUnk8); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->DestructibleUnk9); + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->DestructibleUnk8); // bInteractiveObjectCollidable + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->DestructibleUnk9); // IteractiveObjectType } @@ -4214,6 +4219,7 @@ namespace RoF2 { // Setting this next field to zero will cause a crash. Looking at ShowEQ, if it is zero, the bodytype field is not // present. Will sort that out later. + // This is the CharacterPropertyHash, it can have multiple fields VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 1); // This is a properties count field VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->bodytype); } @@ -4233,10 +4239,10 @@ namespace RoF2 VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->drakkin_tattoo); VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->drakkin_details); - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->equip_chest2); - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); // unknown9 - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); // unknown10 - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->helm); // unknown11 + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->equip_chest2); // InNonPCRaceIllusion + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); // material + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); // variation + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->helm); // headtype VARSTRUCT_ENCODE_TYPE(float, Buffer, emu->size); VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->face); @@ -4244,6 +4250,7 @@ namespace RoF2 VARSTRUCT_ENCODE_TYPE(float, Buffer, emu->runspeed); VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->race); + // From MQ2: todo: create enum for this byte. Holding: Nothing=0 A RightHand Weapon=1 A Shield=2 Dual Wielding Two Weapons=3 A Spear=4 A LeftHand Weapon=5 A Two Handed Weapon=6 A bow=7 VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); // ShowEQ calls this 'Holding' VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->deity); if (emu->NPC) @@ -4276,19 +4283,19 @@ namespace RoF2 VARSTRUCT_ENCODE_STRING(Buffer, emu->lastName); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); // aatitle ?? + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); // aatitle VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->NPC ? 0 : 1); // unknown - Must be 1 for guild name to be shown abover players head. - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); // unknown + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); // TempPet VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->petOwnerId); - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); // unknown13 + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); // FindBits MQ2 name VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->PlayerState); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); // unknown15 - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); // unknown16 - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); // unknown17 - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0xffffffff); // unknown18 - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0xffffffff); // unknown19 + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); // NpcTintIndex + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); // PrimaryTintIndex + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); // SecondaryTintIndex + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0xffffffff); // These do something with OP_WeaponEquip1 + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0xffffffff); // ^ if ((emu->NPC == 0) || (emu->race <= 12) || (emu->race == 128) || (emu->race == 130) || (emu->race == 330) || (emu->race == 522)) { @@ -4356,12 +4363,16 @@ namespace RoF2 VARSTRUCT_ENCODE_STRING(Buffer, emu->suffix); } + // skipping two ints + // unknown, maybe some sort of spawn ID + // SplineID -- no idea Buffer += 8; VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->IsMercenary); - VARSTRUCT_ENCODE_STRING(Buffer, "0000000000000000"); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0xffffffff); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0xffffffff); + VARSTRUCT_ENCODE_STRING(Buffer, "0000000000000000"); // RealEstateItemGuid + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0xffffffff); // RealEstateID + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0xffffffff); // RealEstateItemID // 29 zero bytes follow + // PhysicsEffects follow here ... unsure what they are but it's a count followed by a struct like {spellid, casterid, effectid, baseeffect} Buffer += 29; if (Buffer != (BufferStart + PacketSize)) { diff --git a/common/patches/rof2_structs.h b/common/patches/rof2_structs.h index 379941d51..1b3c41794 100644 --- a/common/patches/rof2_structs.h +++ b/common/patches/rof2_structs.h @@ -328,38 +328,43 @@ showeq -> eqemu sed -e 's/_t//g' -e 's/seto_0xFF/set_to_0xFF/g' */ +// I think this is actually 5 bytes +// IDA's pseudocode reads this as 5 bytes pulled into 2 DWORDs struct Spawn_Struct_Bitfields { + // byte 1 /*00*/ unsigned gender:2; // Gender (0=male, 1=female, 2=monster) /*02*/ unsigned ispet:1; // Guessed based on observing live spawns /*03*/ unsigned afk:1; // 0=no, 1=afk /*04*/ unsigned anon:2; // 0=normal, 1=anon, 2=roleplay /*06*/ unsigned gm:1; -/*06*/ unsigned sneak:1; +/*07*/ unsigned sneak:1; + // byte 2 /*08*/ unsigned lfg:1; -/*09*/ unsigned unknown09:1; -/*10*/ unsigned invis:1; // May have invis & sneak the wrong way around ... not sure how to tell which is which -/*11*/ unsigned invis1:1; // GM Invis? Can only be seen with #gm on - same for the below -/*12*/ unsigned invis2:1; // This one also make the NPC/PC invis -/*13*/ unsigned invis3:1; // This one also make the NPC/PC invis -/*14*/ unsigned invis4:1; // This one also make the NPC/PC invis -/*15*/ unsigned invis6:1; // This one also make the NPC/PC invis -/*16*/ unsigned invis7:1; // This one also make the NPC/PC invis -/*17*/ unsigned invis8:1; // This one also make the NPC/PC invis -/*18*/ unsigned invis9:1; // This one also make the NPC/PC invis -/*19*/ unsigned invis10:1; // This one also make the NPC/PC invis -/*20*/ unsigned invis11:1; // This one also make the NPC/PC invis -/*21*/ unsigned invis12:1; // This one also make the NPC/PC invis +/*09*/ unsigned betabuffed:1; +/*10*/ unsigned invis:12; // there are 3000 different (non-GM) invis levels /*22*/ unsigned linkdead:1; // 1 Toggles LD on or off after name. Correct for RoF2 /*23*/ unsigned showhelm:1; + // byte 4 /*24*/ unsigned unknown24:1; // Prefixes name with ! /*25*/ unsigned trader:1; -/*26*/ unsigned unknown26:1; +/*26*/ unsigned animationonpop:1; /*27*/ unsigned targetable:1; /*28*/ unsigned targetable_with_hotkey:1; /*29*/ unsigned showname:1; -/*30*/ unsigned unknown30:1; -/*30*/ unsigned untargetable:1; // Untargetable with mouse +/*30*/ unsigned idleanimationsoff:1; // what we called statue? +/*31*/ unsigned untargetable:1; // bClickThrough +/* do these later +32 unsigned buyer:1; +33 unsigned offline:1; +34 unsigned interactiveobject:1; +35 unsigned flung:1; // hmm this vfunc appears to do stuff with leve and flung variables +36 unsigned title:1; +37 unsigned suffix:1; +38 unsigned padding1:1; +39 unsigned padding2:1; +40 unsinged padding3:1; +*/ /* // Unknown in RoF2 unsigned betabuffed:1; @@ -498,7 +503,7 @@ struct Spawn_Struct /*0000*/ //char title[0]; // only read if(hasTitleOrSuffix & 4) /*0000*/ //char suffix[0]; // only read if(hasTitleOrSuffix & 8) - char unknown20[8]; + char unknown20[8]; // 2 ints, first unknown, 2nd SplineID uint8 IsMercenary; // If NPC == 1 and this == 1, then the NPC name is Orange. /*0000*/ char unknown21[55]; }; diff --git a/common/patches/rof_structs.h b/common/patches/rof_structs.h index 4c73c28d4..24439d7f0 100644 --- a/common/patches/rof_structs.h +++ b/common/patches/rof_structs.h @@ -339,18 +339,7 @@ struct Spawn_Struct_Bitfields /*06*/ unsigned sneak:1; /*08*/ unsigned lfg:1; /*09*/ unsigned unknown09:1; -/*10*/ unsigned invis:1; // May have invis & sneak the wrong way around ... not sure how to tell which is which -/*11*/ unsigned invis1:1; // GM Invis? Can only be seen with #gm on - same for the below -/*12*/ unsigned invis2:1; // This one also make the NPC/PC invis -/*13*/ unsigned invis3:1; // This one also make the NPC/PC invis -/*14*/ unsigned invis4:1; // This one also make the NPC/PC invis -/*15*/ unsigned invis6:1; // This one also make the NPC/PC invis -/*16*/ unsigned invis7:1; // This one also make the NPC/PC invis -/*17*/ unsigned invis8:1; // This one also make the NPC/PC invis -/*18*/ unsigned invis9:1; // This one also make the NPC/PC invis -/*19*/ unsigned invis10:1; // This one also make the NPC/PC invis -/*20*/ unsigned invis11:1; // This one also make the NPC/PC invis -/*21*/ unsigned invis12:1; // This one also make the NPC/PC invis +/*10*/ unsigned invis:12; // there are 3000 different (non-GM) invis levels /*22*/ unsigned linkdead:1; // 1 Toggles LD on or off after name. Correct for RoF /*23*/ unsigned showhelm:1; /*24*/ unsigned unknown24:1; // Prefixes name with ! diff --git a/common/patches/sod_structs.h b/common/patches/sod_structs.h index ed71d837b..af449f758 100644 --- a/common/patches/sod_structs.h +++ b/common/patches/sod_structs.h @@ -250,8 +250,7 @@ struct Spawn_Struct_Bitfields unsigned sneak:1; unsigned lfg:1; unsigned padding5:1; - unsigned invis:1; // 0 = visible, 1 = invis/sneaking - unsigned padding7:11; + unsigned invis:12; // there are 3000 different (non-GM) invis levels unsigned gm:1; unsigned anon:2; // 0=normal, 1=anon, 2=roleplay unsigned gender:2; // Gender (0=male, 1=female, 2=monster) diff --git a/common/patches/uf_structs.h b/common/patches/uf_structs.h index 5a9562117..b187f9f82 100644 --- a/common/patches/uf_structs.h +++ b/common/patches/uf_structs.h @@ -250,8 +250,7 @@ struct Spawn_Struct_Bitfields unsigned sneak:1; unsigned lfg:1; unsigned padding5:1; - unsigned invis:1; // 0 = visible, 1 = invis/sneaking - unsigned padding7:11; + unsigned invis:12; // there are 3000 different (non-GM) invis levels unsigned gm:1; unsigned anon:2; // 0=normal, 1=anon, 2=roleplay unsigned gender:2; // Gender (0=male, 1=female, 2=monster) 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..5ab461eaa --- /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", 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", 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", 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", 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", 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", 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", 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", 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", 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", 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", 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", 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", 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", 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", 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", 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", 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", 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", 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", 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", 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", 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", 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", 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", 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", 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..8c54b75ee --- /dev/null +++ b/zone/aura.cpp @@ -0,0 +1,936 @@ +#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 + // need to update client UI too + auto app = new EQApplicationPacket(OP_UpdateAura, sizeof(AuraDestory_Struct)); + auto ads = (AuraDestory_Struct *)app->pBuffer; + ads->action = 1; // delete + ads->entity_id = spawn_id; + CastToClient()->QueuePacket(app); + safe_delete(app); + } + 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/bot.cpp b/zone/bot.cpp index f5a9285a3..c78c41d62 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -2929,6 +2929,7 @@ void Bot::FillSpawnStruct(NewSpawn_Struct* ns, Mob* ForWho) { ns->spawn.light = m_Light.Type[EQEmu::lightsource::LightActive]; ns->spawn.helm = helmtexture; //(GetShowHelm() ? helmtexture : 0); //0xFF; ns->spawn.equip_chest2 = texture; //0xFF; + ns->spawn.show_name = true; const EQEmu::ItemData* item = nullptr; const EQEmu::ItemInstance* inst = nullptr; uint32 spawnedbotid = 0; 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..95c9013b2 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,28 @@ 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); + bool good = false; + for (int i = 0; i < trap_mgr.count; ++i) { + if (trap_mgr.auras[i].spawn_id == id) { + good = true; + break; + } + } + if (good) + RemoveAura(id); + else + Message_StringID(MT_SpellFailure, NOT_YOUR_TRAP); // pretty sure this was red +} + void Client::Handle_OP_Report(const EQApplicationPacket *app) { if (!CanUseReport) @@ -14310,6 +14336,24 @@ 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); + QueuePacket(app); // if we don't resend this, the client gets confused + 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..7b6382a20 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, @@ -205,6 +207,16 @@ typedef enum { //fear states enum { FlyMode0 = 0, FlyMode1 = 1, Flymode2 = 2, FlyMode3 = 3 }; +// This is actually FlyMode, from MQ2 +enum GravityBehavior { + Ground, + Flying, + Levitating, + Water, + Floating, // boat + LevitateWhileRunning +}; + struct TradeEntity; class Trade; enum TradeState { @@ -536,6 +548,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/merc.cpp b/zone/merc.cpp index 80565c6b4..a3ff051f2 100644 --- a/zone/merc.cpp +++ b/zone/merc.cpp @@ -1211,6 +1211,7 @@ void Merc::FillSpawnStruct(NewSpawn_Struct* ns, Mob* ForWho) { ns->spawn.flymode = 0; ns->spawn.NPC = 1; // 0=player,1=npc,2=pc corpse,3=npc corpse ns->spawn.IsMercenary = 1; + ns->spawn.show_name = true; UpdateActiveLight(); ns->spawn.light = m_Light.Type[EQEmu::lightsource::LightActive]; diff --git a/zone/mob.h b/zone/mob.h index cd7ecf7a8..bdc1e7c3a 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..c8175c8b6 100644 --- a/zone/string_ids.h +++ b/zone/string_ids.h @@ -286,6 +286,7 @@ #define TRADESKILL_LEARN_RECIPE 3457 //You have learned the recipe %1! #define EXPEDITION_MIN_REMAIN 3551 //You only have %1 minutes remaining before this expedition comes to an end. #define LOOT_NOT_ALLOWED 3562 //You are not allowed to loot the item: %1. +#define NOT_YOUR_TRAP 3671 //You cannot remove this, you are only allowed to remove traps you have set. #define NO_CAST_ON_PET 4045 //You cannot cast this spell on your pet. #define REWIND_WAIT 4059 //You must wait a bit longer before using the rewind command again. #define CORPSEDRAG_LIMIT 4061 //You are already dragging as much as you can! @@ -361,6 +362,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 +377,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/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);