mirror of
https://github.com/EQEmu/Server.git
synced 2025-12-13 06:21:28 +00:00
WIP on auras
Lots to do still Normal buffing auras currently work for the most part
This commit is contained in:
parent
be0374d197
commit
94038ebb75
@ -408,6 +408,7 @@ N(OP_ReloadUI),
|
|||||||
N(OP_RemoveAllDoors),
|
N(OP_RemoveAllDoors),
|
||||||
N(OP_RemoveBlockedBuffs),
|
N(OP_RemoveBlockedBuffs),
|
||||||
N(OP_RemoveNimbusEffect),
|
N(OP_RemoveNimbusEffect),
|
||||||
|
N(OP_RemoveTrap),
|
||||||
N(OP_Report),
|
N(OP_Report),
|
||||||
N(OP_ReqClientSpawn),
|
N(OP_ReqClientSpawn),
|
||||||
N(OP_ReqNewZone),
|
N(OP_ReqNewZone),
|
||||||
@ -523,6 +524,7 @@ N(OP_TributeToggle),
|
|||||||
N(OP_TributeUpdate),
|
N(OP_TributeUpdate),
|
||||||
N(OP_Untargetable),
|
N(OP_Untargetable),
|
||||||
N(OP_UpdateAA),
|
N(OP_UpdateAA),
|
||||||
|
N(OP_UpdateAura),
|
||||||
N(OP_UpdateLeadershipAA),
|
N(OP_UpdateLeadershipAA),
|
||||||
N(OP_VetClaimReply),
|
N(OP_VetClaimReply),
|
||||||
N(OP_VetClaimRequest),
|
N(OP_VetClaimRequest),
|
||||||
|
|||||||
@ -5332,6 +5332,24 @@ struct fling_struct {
|
|||||||
/* 28 */
|
/* 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
|
// Restore structure packing to default
|
||||||
#pragma pack()
|
#pragma pack()
|
||||||
|
|
||||||
|
|||||||
@ -1674,6 +1674,7 @@ void SharedDatabase::LoadSpells(void *data, int max_spells) {
|
|||||||
for (y = 0; y < 16; y++)
|
for (y = 0; y < 16; y++)
|
||||||
sp[tempid].deities[y]=atoi(row[126+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].uninterruptable=atoi(row[146]) != 0;
|
||||||
sp[tempid].ResistDiff=atoi(row[147]);
|
sp[tempid].ResistDiff=atoi(row[147]);
|
||||||
sp[tempid].dot_stacking_exempt = atoi(row[148]) != 0;
|
sp[tempid].dot_stacking_exempt = atoi(row[148]) != 0;
|
||||||
|
|||||||
@ -562,9 +562,9 @@ typedef enum {
|
|||||||
#define SE_LimitManaMin 348 // implemented
|
#define SE_LimitManaMin 348 // implemented
|
||||||
#define SE_ShieldEquipDmgMod 349 // implemented[AA] Increase melee base damage (indirectly increasing hate) when wearing a shield.
|
#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_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_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_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_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_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_LearnTrap 355 // *not implemented - looks to be some type of invulnerability? Test LT (8758)
|
||||||
//#define SE_ChangeTriggerType 356 // not used
|
//#define SE_ChangeTriggerType 356 // not used
|
||||||
@ -757,7 +757,7 @@ struct SPDat_Spell_Struct
|
|||||||
// -- DIETY_BERTOXXULOUS ... DIETY_VEESHAN
|
// -- DIETY_BERTOXXULOUS ... DIETY_VEESHAN
|
||||||
/* 142 */ //int8 npc_no_cast; // 142: between 0 & 100 -- NPC_NO_CAST
|
/* 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
|
/* 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
|
/* 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
|
/* 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
|
/* 147 */ int16 ResistDiff; // -- RESIST_MOD
|
||||||
|
|||||||
@ -675,3 +675,7 @@ OP_RAWOutOfSession=0x0000
|
|||||||
# we need to document the differences between these packets to make identifying them easier
|
# we need to document the differences between these packets to make identifying them easier
|
||||||
OP_Some3ByteHPUpdate=0x0000 # initial HP update for mobs
|
OP_Some3ByteHPUpdate=0x0000 # initial HP update for mobs
|
||||||
OP_InitialHPUpdate=0x0000
|
OP_InitialHPUpdate=0x0000
|
||||||
|
|
||||||
|
#aura related
|
||||||
|
OP_UpdateAura=0x1456
|
||||||
|
OP_RemoveTrap=0x71da
|
||||||
|
|||||||
13
utils/sql/git/required/2017_07_xx_aura.sql
Normal file
13
utils/sql/git/required/2017_07_xx_aura.sql
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
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,
|
||||||
|
PRIMARY KEY(`type`)
|
||||||
|
)
|
||||||
@ -5,6 +5,7 @@ SET(zone_sources
|
|||||||
aa_ability.cpp
|
aa_ability.cpp
|
||||||
aggro.cpp
|
aggro.cpp
|
||||||
aggromanager.cpp
|
aggromanager.cpp
|
||||||
|
aura.cpp
|
||||||
attack.cpp
|
attack.cpp
|
||||||
beacon.cpp
|
beacon.cpp
|
||||||
bonuses.cpp
|
bonuses.cpp
|
||||||
@ -136,6 +137,7 @@ SET(zone_headers
|
|||||||
aa.h
|
aa.h
|
||||||
aa_ability.h
|
aa_ability.h
|
||||||
aggromanager.h
|
aggromanager.h
|
||||||
|
aura.h
|
||||||
basic_functions.h
|
basic_functions.h
|
||||||
beacon.h
|
beacon.h
|
||||||
bot.h
|
bot.h
|
||||||
|
|||||||
435
zone/aura.cpp
Normal file
435
zone/aura.cpp
Normal file
@ -0,0 +1,435 @@
|
|||||||
|
#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)
|
||||||
|
{
|
||||||
|
GiveNPCTypeData(type_data); // we will delete this later on
|
||||||
|
m_owner = owner->GetID();
|
||||||
|
|
||||||
|
if (record.aura_type < static_cast<int>(AuraType::Max))
|
||||||
|
type = static_cast<AuraType>(record.aura_type);
|
||||||
|
else
|
||||||
|
type = AuraType::OnAllGroupMembers;
|
||||||
|
|
||||||
|
if (record.spawn_type < static_cast<int>(AuraSpawns::Max))
|
||||||
|
spawn_type = static_cast<AuraSpawns>(record.spawn_type);
|
||||||
|
else
|
||||||
|
spawn_type = AuraSpawns::GroupMembers;
|
||||||
|
|
||||||
|
if (record.movement < static_cast<int>(AuraMovement::Max))
|
||||||
|
movement_type = static_cast<AuraMovement>(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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Aura::ProcessOnAllFriendlies(Mob *owner)
|
||||||
|
{
|
||||||
|
Shout("Stub 1");
|
||||||
|
}
|
||||||
|
|
||||||
|
void Aura::ProcessOnAllGroupMembers(Mob *owner)
|
||||||
|
{
|
||||||
|
if (!process_timer.Check())
|
||||||
|
return;
|
||||||
|
auto &mob_list = entity_list.GetMobList(); // read only reference so we can do it all inline
|
||||||
|
std::set<int> delayed_remove;
|
||||||
|
if (owner->IsRaidGrouped() && owner->IsClient()) { // currently raids are just client, but safety check
|
||||||
|
auto raid = owner->GetRaid();
|
||||||
|
if (raid == nullptr) { // well shit
|
||||||
|
Depop();
|
||||||
|
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());
|
||||||
|
SpellFinished(spell_id, mob);
|
||||||
|
} else if (mob->IsPet() && mob->IsPetOwnerClient() && mob->GetOwner() && verify_raid_client_pet(mob)) {
|
||||||
|
casted_on.insert(mob->GetID());
|
||||||
|
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());
|
||||||
|
SpellFinished(spell_id, mob);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (owner->IsGrouped()) {
|
||||||
|
auto group = owner->GetGroup();
|
||||||
|
if (group == nullptr) { // uh oh
|
||||||
|
Depop();
|
||||||
|
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());
|
||||||
|
SpellFinished(spell_id, mob);
|
||||||
|
} else if (mob->IsNPC() && mob->CastToNPC()->GetSwarmInfo() && verify_group_swarm(mob->CastToNPC())) {
|
||||||
|
casted_on.insert(mob->GetID());
|
||||||
|
SpellFinished(spell_id, mob);
|
||||||
|
} else if (group->IsGroupMember(mob) && DistanceSquared(GetPosition(), mob->GetPosition()) <= distance) {
|
||||||
|
casted_on.insert(mob->GetID());
|
||||||
|
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());
|
||||||
|
SpellFinished(spell_id, mob);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool is_buff = IsBuffSpell(spell_id);
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cast_timer.Enabled() || !cast_timer.Check())
|
||||||
|
return;
|
||||||
|
|
||||||
|
// TODO: 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 && (!is_buff || !mob->IsAffectedByBuff(spell_id)))
|
||||||
|
|
||||||
|
}*/
|
||||||
|
}
|
||||||
|
|
||||||
|
void Aura::ProcessOnGroupMembersPets(Mob *owner)
|
||||||
|
{
|
||||||
|
Shout("Stub 3");
|
||||||
|
}
|
||||||
|
|
||||||
|
void Aura::ProcessTotem(Mob *owner)
|
||||||
|
{
|
||||||
|
Shout("Stub 4");
|
||||||
|
}
|
||||||
|
|
||||||
|
void Aura::ProcessEnterTrap(Mob *owner)
|
||||||
|
{
|
||||||
|
Shout("Stub 5");
|
||||||
|
}
|
||||||
|
|
||||||
|
void Aura::ProcessExitTrap(Mob *owner)
|
||||||
|
{
|
||||||
|
Shout("Stub 6");
|
||||||
|
}
|
||||||
|
|
||||||
|
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 (movement_type == AuraMovement::Follow && GetPosition() != owner->GetPosition() && movement_timer.Check()) {
|
||||||
|
m_Position = owner->GetPosition();
|
||||||
|
SendPosUpdate();
|
||||||
|
}
|
||||||
|
// TODO: waypoints?
|
||||||
|
|
||||||
|
if (process_func)
|
||||||
|
process_func(*this, owner);
|
||||||
|
|
||||||
|
// TODO: quest calls
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Aura::Depop(bool unused)
|
||||||
|
{
|
||||||
|
// TODO: clean up buffs
|
||||||
|
p_depop = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
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<int>(AuraType::Max)) {
|
||||||
|
return; // TODO: log
|
||||||
|
}
|
||||||
|
|
||||||
|
bool trap = false;
|
||||||
|
|
||||||
|
switch (static_cast<AuraType>(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);
|
||||||
|
entity_list.AddNPC(npc, true, true);
|
||||||
|
|
||||||
|
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 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]);
|
||||||
|
|
||||||
|
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();
|
||||||
|
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();
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
76
zone/aura.h
Normal file
76
zone/aura.h
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
#ifndef AURA_H
|
||||||
|
#define AURA_H
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
#include <set>
|
||||||
|
|
||||||
|
#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
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Aura(NPCType *type_data, Mob *owner, AuraRecord &record);
|
||||||
|
~Aura() { };
|
||||||
|
|
||||||
|
bool IsAura() const { return true; }
|
||||||
|
bool Process();
|
||||||
|
void Depop(bool unused = false);
|
||||||
|
|
||||||
|
void ProcessOnAllFriendlies(Mob *owner);
|
||||||
|
void ProcessOnAllGroupMembers(Mob *owner);
|
||||||
|
void ProcessOnGroupMembersPets(Mob *owner);
|
||||||
|
void ProcessTotem(Mob *owner);
|
||||||
|
void ProcessEnterTrap(Mob *owner);
|
||||||
|
void ProcessExitTrap(Mob *owner);
|
||||||
|
|
||||||
|
private:
|
||||||
|
int m_owner;
|
||||||
|
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<void(Aura &, Mob *)> process_func;
|
||||||
|
std::set<int> casted_on; // we keep track of the other entities we've casted on
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* !AURA_H */
|
||||||
|
|
||||||
@ -1460,6 +1460,14 @@ void Mob::ApplyAABonuses(const AA::Rank &rank, StatBonuses *newbon)
|
|||||||
newbon->FeignedMinionChance = base1;
|
newbon->FeignedMinionChance = base1;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case SE_AdditionalAura:
|
||||||
|
newbon->aura_slots += base1;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SE_IncreaseTrapCount:
|
||||||
|
newbon->trap_slots += base1;
|
||||||
|
break;
|
||||||
|
|
||||||
// to do
|
// to do
|
||||||
case SE_PetDiscipline:
|
case SE_PetDiscipline:
|
||||||
break;
|
break;
|
||||||
@ -3201,6 +3209,16 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne
|
|||||||
if (new_bonus->FeignedCastOnChance < effect_value)
|
if (new_bonus->FeignedCastOnChance < effect_value)
|
||||||
new_bonus->FeignedCastOnChance = effect_value;
|
new_bonus->FeignedCastOnChance = effect_value;
|
||||||
break;
|
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
|
//Special custom cases for loading effects on to NPC from 'npc_spels_effects' table
|
||||||
if (IsAISpellEffect) {
|
if (IsAISpellEffect) {
|
||||||
|
|||||||
@ -383,6 +383,7 @@ void MapOpcodes()
|
|||||||
ConnectedOpcodes[OP_TributeUpdate] = &Client::Handle_OP_TributeUpdate;
|
ConnectedOpcodes[OP_TributeUpdate] = &Client::Handle_OP_TributeUpdate;
|
||||||
ConnectedOpcodes[OP_VetClaimRequest] = &Client::Handle_OP_VetClaimRequest;
|
ConnectedOpcodes[OP_VetClaimRequest] = &Client::Handle_OP_VetClaimRequest;
|
||||||
ConnectedOpcodes[OP_VoiceMacroIn] = &Client::Handle_OP_VoiceMacroIn;
|
ConnectedOpcodes[OP_VoiceMacroIn] = &Client::Handle_OP_VoiceMacroIn;
|
||||||
|
ConnectedOpcodes[OP_UpdateAura] = &Client::Handle_OP_UpdateAura;;
|
||||||
ConnectedOpcodes[OP_WearChange] = &Client::Handle_OP_WearChange;
|
ConnectedOpcodes[OP_WearChange] = &Client::Handle_OP_WearChange;
|
||||||
ConnectedOpcodes[OP_WhoAllRequest] = &Client::Handle_OP_WhoAllRequest;
|
ConnectedOpcodes[OP_WhoAllRequest] = &Client::Handle_OP_WhoAllRequest;
|
||||||
ConnectedOpcodes[OP_WorldUnknown001] = &Client::Handle_OP_Ignore;
|
ConnectedOpcodes[OP_WorldUnknown001] = &Client::Handle_OP_Ignore;
|
||||||
@ -14360,6 +14361,29 @@ void Client::Handle_OP_VoiceMacroIn(const EQApplicationPacket *app)
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Client::Handle_OP_UpdateAura(const EQApplicationPacket *app)
|
||||||
|
{
|
||||||
|
if (app->size < 4) {
|
||||||
|
Log(Logs::General, Logs::None, "Size mismatch in OP_UpdateAura");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto action = *(uint32_t *)app->pBuffer; // action tells us the size
|
||||||
|
switch (action) {
|
||||||
|
case 2:
|
||||||
|
RemoveAllAuras();
|
||||||
|
break;
|
||||||
|
case 1: {
|
||||||
|
auto ads = (AuraDestory_Struct *)app->pBuffer;
|
||||||
|
RemoveAura(ads->entity_id);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 0: // client doesn't send this
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
void Client::Handle_OP_WearChange(const EQApplicationPacket *app)
|
void Client::Handle_OP_WearChange(const EQApplicationPacket *app)
|
||||||
{
|
{
|
||||||
if (app->size != sizeof(WearChange_Struct)) {
|
if (app->size != sizeof(WearChange_Struct)) {
|
||||||
|
|||||||
@ -288,6 +288,7 @@
|
|||||||
void Handle_OP_TributeNPC(const EQApplicationPacket *app);
|
void Handle_OP_TributeNPC(const EQApplicationPacket *app);
|
||||||
void Handle_OP_TributeToggle(const EQApplicationPacket *app);
|
void Handle_OP_TributeToggle(const EQApplicationPacket *app);
|
||||||
void Handle_OP_TributeUpdate(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_VetClaimRequest(const EQApplicationPacket *app);
|
||||||
void Handle_OP_VoiceMacroIn(const EQApplicationPacket *app);
|
void Handle_OP_VoiceMacroIn(const EQApplicationPacket *app);
|
||||||
void Handle_OP_WearChange(const EQApplicationPacket *app);
|
void Handle_OP_WearChange(const EQApplicationPacket *app);
|
||||||
|
|||||||
@ -105,6 +105,8 @@
|
|||||||
#define PET_BUTTON_FOCUS 8
|
#define PET_BUTTON_FOCUS 8
|
||||||
#define PET_BUTTON_SPELLHOLD 9
|
#define PET_BUTTON_SPELLHOLD 9
|
||||||
|
|
||||||
|
#define AURA_HARDCAP 2
|
||||||
|
|
||||||
typedef enum { //focus types
|
typedef enum { //focus types
|
||||||
focusSpellHaste = 1,
|
focusSpellHaste = 1,
|
||||||
focusSpellDuration,
|
focusSpellDuration,
|
||||||
@ -536,6 +538,8 @@ struct StatBonuses {
|
|||||||
int16 FeignedCastOnChance; // Percent Value
|
int16 FeignedCastOnChance; // Percent Value
|
||||||
bool PetCommands[PET_MAXCOMMANDS]; // SPA 267
|
bool PetCommands[PET_MAXCOMMANDS]; // SPA 267
|
||||||
int FeignedMinionChance; // SPA 281 base1 = chance, just like normal FD
|
int FeignedMinionChance; // SPA 281 base1 = chance, just like normal FD
|
||||||
|
int aura_slots;
|
||||||
|
int trap_slots;
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef struct
|
typedef struct
|
||||||
|
|||||||
@ -80,6 +80,7 @@ public:
|
|||||||
virtual bool IsBeacon() const { return false; }
|
virtual bool IsBeacon() const { return false; }
|
||||||
virtual bool IsEncounter() const { return false; }
|
virtual bool IsEncounter() const { return false; }
|
||||||
virtual bool IsBot() const { return false; }
|
virtual bool IsBot() const { return false; }
|
||||||
|
virtual bool IsAura() const { return false; }
|
||||||
|
|
||||||
virtual bool Process() { return false; }
|
virtual bool Process() { return false; }
|
||||||
virtual bool Save() { return true; }
|
virtual bool Save() { return true; }
|
||||||
|
|||||||
34
zone/mob.h
34
zone/mob.h
@ -45,6 +45,8 @@ class EQApplicationPacket;
|
|||||||
class Group;
|
class Group;
|
||||||
class NPC;
|
class NPC;
|
||||||
class Raid;
|
class Raid;
|
||||||
|
class Aura;
|
||||||
|
struct AuraRecord;
|
||||||
struct NewSpawn_Struct;
|
struct NewSpawn_Struct;
|
||||||
struct PlayerPositionUpdateServer_Struct;
|
struct PlayerPositionUpdateServer_Struct;
|
||||||
|
|
||||||
@ -85,6 +87,22 @@ public:
|
|||||||
int params[MAX_SPECIAL_ATTACK_PARAMS];
|
int params[MAX_SPECIAL_ATTACK_PARAMS];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct AuraInfo {
|
||||||
|
char name[64];
|
||||||
|
int spawn_id;
|
||||||
|
int icon;
|
||||||
|
AuraInfo() : spawn_id(0), icon(0)
|
||||||
|
{
|
||||||
|
memset(name, 0, 64);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct AuraMgr {
|
||||||
|
int count; // active auras
|
||||||
|
AuraInfo auras[AURA_HARDCAP];
|
||||||
|
AuraMgr() : count(0) { }
|
||||||
|
};
|
||||||
|
|
||||||
Mob(const char* in_name,
|
Mob(const char* in_name,
|
||||||
const char* in_lastname,
|
const char* in_lastname,
|
||||||
int32 in_cur_hp,
|
int32 in_cur_hp,
|
||||||
@ -308,6 +326,7 @@ public:
|
|||||||
void BuffProcess();
|
void BuffProcess();
|
||||||
virtual void DoBuffTic(const Buffs_Struct &buff, int slot, Mob* caster = nullptr);
|
virtual void DoBuffTic(const Buffs_Struct &buff, int slot, Mob* caster = nullptr);
|
||||||
void BuffFadeBySpellID(uint16 spell_id);
|
void BuffFadeBySpellID(uint16 spell_id);
|
||||||
|
void BuffFadeBySpellIDAndCaster(uint16 spell_id, uint16 caster_id);
|
||||||
void BuffFadeByEffect(int effectid, int skipslot = -1);
|
void BuffFadeByEffect(int effectid, int skipslot = -1);
|
||||||
void BuffFadeAll();
|
void BuffFadeAll();
|
||||||
void BuffFadeNonPersistDeath();
|
void BuffFadeNonPersistDeath();
|
||||||
@ -315,6 +334,7 @@ public:
|
|||||||
void BuffFadeBySlot(int slot, bool iRecalcBonuses = true);
|
void BuffFadeBySlot(int slot, bool iRecalcBonuses = true);
|
||||||
void BuffFadeDetrimentalByCaster(Mob *caster);
|
void BuffFadeDetrimentalByCaster(Mob *caster);
|
||||||
void BuffFadeBySitModifier();
|
void BuffFadeBySitModifier();
|
||||||
|
bool IsAffectedByBuff(uint16 spell_id);
|
||||||
void BuffModifyDurationBySpellID(uint16 spell_id, int32 newDuration);
|
void BuffModifyDurationBySpellID(uint16 spell_id, int32 newDuration);
|
||||||
int AddBuff(Mob *caster, const uint16 spell_id, int duration = 0, int32 level_override = -1);
|
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);
|
int CanBuffStack(uint16 spellid, uint8 caster_level, bool iFailIfOverwrite = false);
|
||||||
@ -604,6 +624,17 @@ public:
|
|||||||
bool PlotPositionAroundTarget(Mob* target, float &x_dest, float &y_dest, float &z_dest,
|
bool PlotPositionAroundTarget(Mob* target, float &x_dest, float &y_dest, float &z_dest,
|
||||||
bool lookForAftArc = true);
|
bool lookForAftArc = true);
|
||||||
|
|
||||||
|
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) {}
|
||||||
|
void RemoveAllAuras() {}
|
||||||
|
|
||||||
//Procs
|
//Procs
|
||||||
void TriggerDefensiveProcs(Mob *on, uint16 hand = EQEmu::inventory::slotPrimary, bool FromSkillProc = false, int damage = 0);
|
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);
|
bool AddRangedProc(uint16 spell_id, uint16 iChance = 3, uint16 base_spell_id = SPELL_UNKNOWN);
|
||||||
@ -1465,6 +1496,9 @@ protected:
|
|||||||
|
|
||||||
bool IsHorse;
|
bool IsHorse;
|
||||||
|
|
||||||
|
AuraMgr aura_mgr;
|
||||||
|
AuraMgr trap_mgr;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void _StopSong(); //this is not what you think it is
|
void _StopSong(); //this is not what you think it is
|
||||||
Mob* target;
|
Mob* target;
|
||||||
|
|||||||
@ -90,6 +90,7 @@ class Client;
|
|||||||
class Group;
|
class Group;
|
||||||
class Raid;
|
class Raid;
|
||||||
class Spawn2;
|
class Spawn2;
|
||||||
|
class Aura;
|
||||||
|
|
||||||
namespace EQEmu
|
namespace EQEmu
|
||||||
{
|
{
|
||||||
@ -425,6 +426,7 @@ protected:
|
|||||||
NPCType* NPCTypedata_ours; //special case for npcs with uniquely created data.
|
NPCType* NPCTypedata_ours; //special case for npcs with uniquely created data.
|
||||||
|
|
||||||
friend class EntityList;
|
friend class EntityList;
|
||||||
|
friend class Aura;
|
||||||
std::list<struct NPCFaction*> faction_list;
|
std::list<struct NPCFaction*> faction_list;
|
||||||
uint32 copper;
|
uint32 copper;
|
||||||
uint32 silver;
|
uint32 silver;
|
||||||
|
|||||||
@ -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.
|
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)
|
Client *Raid::GetClientByIndex(uint16 index)
|
||||||
{
|
{
|
||||||
if(index > MAX_RAID_MEMBERS)
|
if(index > MAX_RAID_MEMBERS)
|
||||||
|
|||||||
@ -142,6 +142,7 @@ public:
|
|||||||
//keeps me from having to keep iterating through the list
|
//keeps me from having to keep iterating through the list
|
||||||
//when I want lots of data from the same entry
|
//when I want lots of data from the same entry
|
||||||
uint32 GetPlayerIndex(const char *name);
|
uint32 GetPlayerIndex(const char *name);
|
||||||
|
uint32 GetPlayerIndex(Client *c);
|
||||||
//for perl interface
|
//for perl interface
|
||||||
Client *GetClientByIndex(uint16 index);
|
Client *GetClientByIndex(uint16 index);
|
||||||
const char *GetClientNameByIndex(uint8 index);
|
const char *GetClientNameByIndex(uint8 index);
|
||||||
|
|||||||
@ -2791,6 +2791,10 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case SE_PersistentEffect:
|
||||||
|
MakeAura(spell_id);
|
||||||
|
break;
|
||||||
|
|
||||||
// Handled Elsewhere
|
// Handled Elsewhere
|
||||||
case SE_ImmuneFleeing:
|
case SE_ImmuneFleeing:
|
||||||
case SE_NegateSpellEffect:
|
case SE_NegateSpellEffect:
|
||||||
|
|||||||
@ -4189,6 +4189,21 @@ void Mob::BuffFadeBySpellID(uint16 spell_id)
|
|||||||
CalcBonuses();
|
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
|
// removes buffs containing effectid, skipping skipslot
|
||||||
void Mob::BuffFadeByEffect(int effectid, int skipslot)
|
void Mob::BuffFadeByEffect(int effectid, int skipslot)
|
||||||
{
|
{
|
||||||
@ -4207,6 +4222,16 @@ void Mob::BuffFadeByEffect(int effectid, int skipslot)
|
|||||||
CalcBonuses();
|
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
|
// checks if 'this' can be affected by spell_id from caster
|
||||||
// returns true if the spell should fail, false otherwise
|
// returns true if the spell should fail, false otherwise
|
||||||
bool Mob::IsImmuneToSpell(uint16 spell_id, Mob *caster)
|
bool Mob::IsImmuneToSpell(uint16 spell_id, Mob *caster)
|
||||||
|
|||||||
@ -361,6 +361,7 @@
|
|||||||
#define GAIN_GROUP_LEADERSHIP_EXP 8788 //
|
#define GAIN_GROUP_LEADERSHIP_EXP 8788 //
|
||||||
#define GAIN_RAID_LEADERSHIP_EXP 8789 //
|
#define GAIN_RAID_LEADERSHIP_EXP 8789 //
|
||||||
#define BUFF_MINUTES_REMAINING 8799 //%1 (%2 minutes remaining)
|
#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 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_MOSTLY_SUCCESSFUL 9029 //Your spell was mostly successful.
|
||||||
#define SLOW_PARTIALLY_SUCCESSFUL 9030 // Your spell was partially successful.
|
#define SLOW_PARTIALLY_SUCCESSFUL 9030 // Your spell was partially successful.
|
||||||
@ -375,6 +376,7 @@
|
|||||||
#define SHAKE_OFF_STUN 9077 //You shake off the stun effect!
|
#define SHAKE_OFF_STUN 9077 //You shake off the stun effect!
|
||||||
#define STRIKETHROUGH_STRING 9078 //You strike through your opponent's defenses!
|
#define STRIKETHROUGH_STRING 9078 //You strike through your opponent's defenses!
|
||||||
#define SPELL_REFLECT 9082 //%1's spell has been reflected by %2.
|
#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 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 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.
|
#define SNEAK_RESTRICT 9240 //You can not use this ability because you have not been hidden for long enough.
|
||||||
|
|||||||
@ -123,6 +123,18 @@ struct PetRecord {
|
|||||||
uint32 equipmentset; // default equipment for the pet
|
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
|
||||||
|
};
|
||||||
|
|
||||||
// Actual pet info for a client.
|
// Actual pet info for a client.
|
||||||
struct PetInfo {
|
struct PetInfo {
|
||||||
uint16 SpellID;
|
uint16 SpellID;
|
||||||
@ -404,6 +416,7 @@ public:
|
|||||||
void AddLootDropToNPC(NPC* npc, uint32 lootdrop_id, ItemList* itemlist, uint8 droplimit, uint8 mindrop);
|
void AddLootDropToNPC(NPC* npc, uint32 lootdrop_id, ItemList* itemlist, uint8 droplimit, uint8 mindrop);
|
||||||
uint32 GetMaxNPCSpellsID();
|
uint32 GetMaxNPCSpellsID();
|
||||||
uint32 GetMaxNPCSpellsEffectsID();
|
uint32 GetMaxNPCSpellsEffectsID();
|
||||||
|
bool GetAuraEntry(uint16 spell_id, AuraRecord &record);
|
||||||
|
|
||||||
DBnpcspells_Struct* GetNPCSpells(uint32 iDBSpellsID);
|
DBnpcspells_Struct* GetNPCSpells(uint32 iDBSpellsID);
|
||||||
DBnpcspellseffects_Struct* GetNPCSpellsEffects(uint32 iDBSpellsEffectsID);
|
DBnpcspellseffects_Struct* GetNPCSpellsEffects(uint32 iDBSpellsEffectsID);
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user