mirror of
https://github.com/EQEmu/Server.git
synced 2026-03-23 06:32:28 +00:00
Merge branch 'master' of https://github.com/EQEmu/Server
This commit is contained in:
commit
b41bb8e179
@ -25,6 +25,9 @@ N(OP_AdventureRequest),
|
|||||||
N(OP_AdventureStatsReply),
|
N(OP_AdventureStatsReply),
|
||||||
N(OP_AdventureStatsRequest),
|
N(OP_AdventureStatsRequest),
|
||||||
N(OP_AdventureUpdate),
|
N(OP_AdventureUpdate),
|
||||||
|
N(OP_AggroMeterLockTarget),
|
||||||
|
N(OP_AggroMeterTargetInfo),
|
||||||
|
N(OP_AggroMeterUpdate),
|
||||||
N(OP_AltCurrency),
|
N(OP_AltCurrency),
|
||||||
N(OP_AltCurrencyMerchantReply),
|
N(OP_AltCurrencyMerchantReply),
|
||||||
N(OP_AltCurrencyMerchantRequest),
|
N(OP_AltCurrencyMerchantRequest),
|
||||||
|
|||||||
@ -114,6 +114,7 @@ RULE_BOOL(Character, CheckCursorEmptyWhenLooting, true) // If true, a player can
|
|||||||
RULE_BOOL(Character, MaintainIntoxicationAcrossZones, true) // If true, alcohol effects are maintained across zoning and logging out/in.
|
RULE_BOOL(Character, MaintainIntoxicationAcrossZones, true) // If true, alcohol effects are maintained across zoning and logging out/in.
|
||||||
RULE_BOOL(Character, EnableDiscoveredItems, true) // If enabled, it enables EVENT_DISCOVER_ITEM and also saves character names and timestamps for the first time an item is discovered.
|
RULE_BOOL(Character, EnableDiscoveredItems, true) // If enabled, it enables EVENT_DISCOVER_ITEM and also saves character names and timestamps for the first time an item is discovered.
|
||||||
RULE_BOOL(Character, EnableXTargetting, true) // Enable Extended Targetting Window, for users with UF and later clients.
|
RULE_BOOL(Character, EnableXTargetting, true) // Enable Extended Targetting Window, for users with UF and later clients.
|
||||||
|
RULE_BOOL(Character, EnableAggroMeter, true) // Enable Aggro Meter, for users with RoF and later clients.
|
||||||
RULE_BOOL(Character, KeepLevelOverMax, false) // Don't delevel a character that has somehow gone over the level cap
|
RULE_BOOL(Character, KeepLevelOverMax, false) // Don't delevel a character that has somehow gone over the level cap
|
||||||
RULE_INT(Character, FoodLossPerUpdate, 35) // How much food/water you lose per stamina update
|
RULE_INT(Character, FoodLossPerUpdate, 35) // How much food/water you lose per stamina update
|
||||||
RULE_INT(Character, BaseInstrumentSoftCap, 36) // Softcap for instrument mods, 36 commonly referred to as "3.6" as well.
|
RULE_INT(Character, BaseInstrumentSoftCap, 36) // Softcap for instrument mods, 36 commonly referred to as "3.6" as well.
|
||||||
|
|||||||
@ -359,6 +359,9 @@ OP_OpenContainer=0x654f
|
|||||||
OP_Marquee=0x288a
|
OP_Marquee=0x288a
|
||||||
OP_Fling=0x6b8e
|
OP_Fling=0x6b8e
|
||||||
OP_CancelSneakHide=0x265f
|
OP_CancelSneakHide=0x265f
|
||||||
|
OP_AggroMeterLockTarget=0x70b7
|
||||||
|
OP_AggroMeterTargetInfo=0x18fe
|
||||||
|
OP_AggroMeterUpdate=0x75aa
|
||||||
|
|
||||||
OP_DzQuit=0x5fc8
|
OP_DzQuit=0x5fc8
|
||||||
OP_DzListTimers=0x67b9
|
OP_DzListTimers=0x67b9
|
||||||
|
|||||||
@ -360,6 +360,9 @@ OP_ItemRecastDelay=0x15a9
|
|||||||
OP_ResetAA=0x1669
|
OP_ResetAA=0x1669
|
||||||
OP_Fling=0x6f80
|
OP_Fling=0x6f80
|
||||||
OP_CancelSneakHide=0x0927
|
OP_CancelSneakHide=0x0927
|
||||||
|
OP_AggroMeterLockTarget=0x1643
|
||||||
|
OP_AggroMeterTargetInfo=0x16bc
|
||||||
|
OP_AggroMeterUpdate=0x1781
|
||||||
|
|
||||||
# Expeditions
|
# Expeditions
|
||||||
OP_DzAddPlayer=0x4701
|
OP_DzAddPlayer=0x4701
|
||||||
|
|||||||
@ -4,6 +4,7 @@ SET(zone_sources
|
|||||||
aa.cpp
|
aa.cpp
|
||||||
aa_ability.cpp
|
aa_ability.cpp
|
||||||
aggro.cpp
|
aggro.cpp
|
||||||
|
aggromanager.cpp
|
||||||
attack.cpp
|
attack.cpp
|
||||||
beacon.cpp
|
beacon.cpp
|
||||||
bonuses.cpp
|
bonuses.cpp
|
||||||
@ -122,6 +123,7 @@ SET(zone_sources
|
|||||||
water_map_v2.cpp
|
water_map_v2.cpp
|
||||||
waypoints.cpp
|
waypoints.cpp
|
||||||
worldserver.cpp
|
worldserver.cpp
|
||||||
|
xtargetautohaters.cpp
|
||||||
zone.cpp
|
zone.cpp
|
||||||
zone_config.cpp
|
zone_config.cpp
|
||||||
zonedb.cpp
|
zonedb.cpp
|
||||||
@ -131,6 +133,7 @@ SET(zone_sources
|
|||||||
SET(zone_headers
|
SET(zone_headers
|
||||||
aa.h
|
aa.h
|
||||||
aa_ability.h
|
aa_ability.h
|
||||||
|
aggromanager.h
|
||||||
basic_functions.h
|
basic_functions.h
|
||||||
beacon.h
|
beacon.h
|
||||||
bot.h
|
bot.h
|
||||||
@ -215,6 +218,7 @@ SET(zone_headers
|
|||||||
water_map_v1.h
|
water_map_v1.h
|
||||||
water_map_v2.h
|
water_map_v2.h
|
||||||
worldserver.h
|
worldserver.h
|
||||||
|
xtargetautohaters.h
|
||||||
zone.h
|
zone.h
|
||||||
zone_config.h
|
zone_config.h
|
||||||
zonedb.h
|
zonedb.h
|
||||||
|
|||||||
11
zone/aggromanager.cpp
Normal file
11
zone/aggromanager.cpp
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
#include "aggromanager.h"
|
||||||
|
|
||||||
|
AggroMeter::AggroMeter() : lock_id(0), target_id(0), secondary_id(0), lock_changed(false)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < AT_Max; ++i) {
|
||||||
|
data[i].type = i;
|
||||||
|
data[i].pct = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
80
zone/aggromanager.h
Normal file
80
zone/aggromanager.h
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
#ifndef AGGROMANAGER_H
|
||||||
|
#define AGGROMANAGER_H
|
||||||
|
|
||||||
|
#include "../common/types.h"
|
||||||
|
#include <assert.h>
|
||||||
|
#include <cstddef>
|
||||||
|
|
||||||
|
class AggroMeter
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
enum AggroTypes {
|
||||||
|
AT_Player,
|
||||||
|
AT_Secondary,
|
||||||
|
AT_Group1,
|
||||||
|
AT_Group2,
|
||||||
|
AT_Group3,
|
||||||
|
AT_Group4,
|
||||||
|
AT_Group5,
|
||||||
|
AT_XTarget1,
|
||||||
|
AT_XTarget2,
|
||||||
|
AT_XTarget3,
|
||||||
|
AT_XTarget4,
|
||||||
|
AT_XTarget5,
|
||||||
|
AT_XTarget6,
|
||||||
|
AT_XTarget7,
|
||||||
|
AT_XTarget8,
|
||||||
|
AT_XTarget9,
|
||||||
|
AT_XTarget10,
|
||||||
|
AT_XTarget11,
|
||||||
|
AT_XTarget12,
|
||||||
|
AT_XTarget13,
|
||||||
|
AT_XTarget14,
|
||||||
|
AT_XTarget15,
|
||||||
|
AT_XTarget16,
|
||||||
|
AT_XTarget17,
|
||||||
|
AT_XTarget18,
|
||||||
|
AT_XTarget19,
|
||||||
|
AT_XTarget20,
|
||||||
|
AT_Max
|
||||||
|
};
|
||||||
|
|
||||||
|
private:
|
||||||
|
struct AggroData {
|
||||||
|
int16 type;
|
||||||
|
int16 pct;
|
||||||
|
};
|
||||||
|
|
||||||
|
AggroData data[AT_Max];
|
||||||
|
int lock_id; // we set this
|
||||||
|
int target_id; // current target or if PC targeted, their Target
|
||||||
|
// so secondary depends on if we have aggro or not
|
||||||
|
// When we are the current target, this will be the 2nd person on list
|
||||||
|
// When we are not tanking, this will be the current tank
|
||||||
|
int secondary_id;
|
||||||
|
|
||||||
|
// so we need some easy way to detect the client changing but still delaying the packet
|
||||||
|
bool lock_changed;
|
||||||
|
public:
|
||||||
|
AggroMeter();
|
||||||
|
~AggroMeter() {}
|
||||||
|
|
||||||
|
inline void set_lock_id(int in) { lock_id = in; lock_changed = true; }
|
||||||
|
inline bool update_lock() { bool ret = lock_changed; lock_changed = false; return ret; }
|
||||||
|
inline void set_target_id(int in) { target_id = in; }
|
||||||
|
inline void set_secondary_id(int in) { secondary_id = in; }
|
||||||
|
// returns true when changed
|
||||||
|
inline bool set_pct(AggroTypes t, int pct) { assert(t >= AT_Player && t < AT_Max); if (data[t].pct == pct) return false; data[t].pct = pct; return true; }
|
||||||
|
|
||||||
|
inline int get_lock_id() const { return lock_id; }
|
||||||
|
inline int get_target_id() const { return target_id; }
|
||||||
|
inline int get_secondary_id() const { return secondary_id; }
|
||||||
|
inline int get_pct(AggroTypes t) const { assert(t >= AT_Player && t < AT_Max); return data[t].pct; }
|
||||||
|
// the ID of the spawn for player entry depends on lock_id
|
||||||
|
inline int get_player_aggro_id() const { return lock_id ? lock_id : target_id; }
|
||||||
|
// fuck it, lets just use a buffer the size of the largest to work with
|
||||||
|
const inline size_t max_packet_size() const { return sizeof(uint8) + sizeof(uint32) + sizeof(uint8) + (sizeof(uint8) + sizeof(uint16)) * AT_Max; }
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* !AGGROMANAGER_H */
|
||||||
@ -2432,9 +2432,9 @@ void Mob::AddToHateList(Mob* other, uint32 hate /*= 0*/, int32 damage /*= 0*/, b
|
|||||||
Mob* mypet = this->GetPet();
|
Mob* mypet = this->GetPet();
|
||||||
Mob* myowner = this->GetOwner();
|
Mob* myowner = this->GetOwner();
|
||||||
Mob* targetmob = this->GetTarget();
|
Mob* targetmob = this->GetTarget();
|
||||||
|
bool on_hatelist = CheckAggro(other);
|
||||||
|
|
||||||
if(other){
|
if(other){
|
||||||
bool on_hatelist = CheckAggro(other);
|
|
||||||
AddRampage(other);
|
AddRampage(other);
|
||||||
if (on_hatelist) { // odd reason, if you're not on the hate list, subtlety etc don't apply!
|
if (on_hatelist) { // odd reason, if you're not on the hate list, subtlety etc don't apply!
|
||||||
// Spell Casting Subtlety etc
|
// Spell Casting Subtlety etc
|
||||||
@ -2510,7 +2510,7 @@ void Mob::AddToHateList(Mob* other, uint32 hate /*= 0*/, int32 damage /*= 0*/, b
|
|||||||
|
|
||||||
hate_list.AddEntToHateList(other, hate, damage, bFrenzy, !iBuffTic);
|
hate_list.AddEntToHateList(other, hate, damage, bFrenzy, !iBuffTic);
|
||||||
|
|
||||||
if(other->IsClient())
|
if(other->IsClient() && !on_hatelist)
|
||||||
other->CastToClient()->AddAutoXTarget(this);
|
other->CastToClient()->AddAutoXTarget(this);
|
||||||
|
|
||||||
#ifdef BOTS
|
#ifdef BOTS
|
||||||
@ -2549,7 +2549,7 @@ void Mob::AddToHateList(Mob* other, uint32 hate /*= 0*/, int32 damage /*= 0*/, b
|
|||||||
if(!owner->GetSpecialAbility(IMMUNE_AGGRO))
|
if(!owner->GetSpecialAbility(IMMUNE_AGGRO))
|
||||||
{
|
{
|
||||||
hate_list.AddEntToHateList(owner, 0, 0, false, !iBuffTic);
|
hate_list.AddEntToHateList(owner, 0, 0, false, !iBuffTic);
|
||||||
if(owner->IsClient())
|
if(owner->IsClient() && !CheckAggro(owner))
|
||||||
owner->CastToClient()->AddAutoXTarget(this);
|
owner->CastToClient()->AddAutoXTarget(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
361
zone/client.cpp
361
zone/client.cpp
@ -154,6 +154,7 @@ Client::Client(EQStreamInterface* ieqs)
|
|||||||
afk_toggle_timer(250),
|
afk_toggle_timer(250),
|
||||||
helm_toggle_timer(250),
|
helm_toggle_timer(250),
|
||||||
light_update_timer(600),
|
light_update_timer(600),
|
||||||
|
aggro_meter_timer(1000),
|
||||||
m_Proximity(FLT_MAX, FLT_MAX, FLT_MAX), //arbitrary large number
|
m_Proximity(FLT_MAX, FLT_MAX, FLT_MAX), //arbitrary large number
|
||||||
m_ZoneSummonLocation(-2.0f,-2.0f,-2.0f),
|
m_ZoneSummonLocation(-2.0f,-2.0f,-2.0f),
|
||||||
m_AutoAttackPosition(0.0f, 0.0f, 0.0f, 0.0f),
|
m_AutoAttackPosition(0.0f, 0.0f, 0.0f, 0.0f),
|
||||||
@ -310,6 +311,8 @@ Client::Client(EQStreamInterface* ieqs)
|
|||||||
}
|
}
|
||||||
MaxXTargets = 5;
|
MaxXTargets = 5;
|
||||||
XTargetAutoAddHaters = true;
|
XTargetAutoAddHaters = true;
|
||||||
|
m_autohatermgr.SetOwner(this, nullptr, nullptr);
|
||||||
|
m_activeautohatermgr = &m_autohatermgr;
|
||||||
LoadAccountFlags();
|
LoadAccountFlags();
|
||||||
|
|
||||||
initial_respawn_selection = 0;
|
initial_respawn_selection = 0;
|
||||||
@ -4159,6 +4162,18 @@ bool Client::GroupFollow(Client* inviter) {
|
|||||||
}
|
}
|
||||||
if (raid->RaidCount() < MAX_RAID_MEMBERS)
|
if (raid->RaidCount() < MAX_RAID_MEMBERS)
|
||||||
{
|
{
|
||||||
|
// okay, so we now have a single client (this) joining a group in a raid
|
||||||
|
// And they're not already in the raid (which is above and doesn't need xtarget shit)
|
||||||
|
if (!GetXTargetAutoMgr()->empty()) {
|
||||||
|
raid->GetXTargetAutoMgr()->merge(*GetXTargetAutoMgr());
|
||||||
|
GetXTargetAutoMgr()->clear();
|
||||||
|
RemoveAutoXTargets();
|
||||||
|
}
|
||||||
|
|
||||||
|
SetXTargetAutoMgr(GetXTargetAutoMgr());
|
||||||
|
if (!GetXTargetAutoMgr()->empty())
|
||||||
|
SetDirtyAutoHaters();
|
||||||
|
|
||||||
if (raid->GroupCount(groupToUse) < 6)
|
if (raid->GroupCount(groupToUse) < 6)
|
||||||
{
|
{
|
||||||
raid->SendRaidCreate(this);
|
raid->SendRaidCreate(this);
|
||||||
@ -4234,7 +4249,9 @@ bool Client::GroupFollow(Client* inviter) {
|
|||||||
inviter->SendGroupLeaderChangePacket(inviter->GetName());
|
inviter->SendGroupLeaderChangePacket(inviter->GetName());
|
||||||
inviter->SendGroupJoinAcknowledge();
|
inviter->SendGroupJoinAcknowledge();
|
||||||
}
|
}
|
||||||
|
group->GetXTargetAutoMgr()->merge(*inviter->GetXTargetAutoMgr());
|
||||||
|
inviter->GetXTargetAutoMgr()->clear();
|
||||||
|
inviter->SetXTargetAutoMgr(group->GetXTargetAutoMgr());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!group)
|
if (!group)
|
||||||
@ -7171,12 +7188,12 @@ void Client::UpdateClientXTarget(Client *c)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IT IS NOT SAFE TO CALL THIS IF IT'S NOT INITIAL AGGRO
|
||||||
void Client::AddAutoXTarget(Mob *m, bool send)
|
void Client::AddAutoXTarget(Mob *m, bool send)
|
||||||
{
|
{
|
||||||
if(!XTargettingAvailable() || !XTargetAutoAddHaters)
|
m_activeautohatermgr->increment_count(m);
|
||||||
return;
|
|
||||||
|
|
||||||
if(IsXTarget(m))
|
if (!XTargettingAvailable() || !XTargetAutoAddHaters || IsXTarget(m))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
for(int i = 0; i < GetMaxXTargets(); ++i)
|
for(int i = 0; i < GetMaxXTargets(); ++i)
|
||||||
@ -7195,60 +7212,15 @@ void Client::AddAutoXTarget(Mob *m, bool send)
|
|||||||
|
|
||||||
void Client::RemoveXTarget(Mob *m, bool OnlyAutoSlots)
|
void Client::RemoveXTarget(Mob *m, bool OnlyAutoSlots)
|
||||||
{
|
{
|
||||||
if (!XTargettingAvailable())
|
m_activeautohatermgr->decrement_count(m);
|
||||||
return;
|
// now we may need to clean up our CurrentTargetNPC entries
|
||||||
|
|
||||||
bool HadFreeAutoSlotsBefore = false;
|
|
||||||
|
|
||||||
int FreedAutoSlots = 0;
|
|
||||||
|
|
||||||
if (m->GetID() == 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
for (int i = 0; i < GetMaxXTargets(); ++i) {
|
for (int i = 0; i < GetMaxXTargets(); ++i) {
|
||||||
if (OnlyAutoSlots && XTargets[i].Type != Auto)
|
if (XTargets[i].Type == CurrentTargetNPC && XTargets[i].ID == m->GetID()) {
|
||||||
continue;
|
XTargets[i].Type = Auto;
|
||||||
|
|
||||||
if (XTargets[i].ID == m->GetID()) {
|
|
||||||
if (XTargets[i].Type == CurrentTargetNPC)
|
|
||||||
XTargets[i].Type = Auto;
|
|
||||||
|
|
||||||
if (XTargets[i].Type == Auto)
|
|
||||||
++FreedAutoSlots;
|
|
||||||
|
|
||||||
XTargets[i].ID = 0;
|
XTargets[i].ID = 0;
|
||||||
XTargets[i].dirty = true;
|
XTargets[i].dirty = true;
|
||||||
} else {
|
|
||||||
if (XTargets[i].Type == Auto && XTargets[i].ID == 0)
|
|
||||||
HadFreeAutoSlotsBefore = true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// move shit up! If the removed NPC was in a CurrentTargetNPC slot it becomes Auto
|
|
||||||
// and we need to potentially fill it
|
|
||||||
std::queue<int> empty_slots;
|
|
||||||
for (int i = 0; i < GetMaxXTargets(); ++i) {
|
|
||||||
if (XTargets[i].Type != Auto)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (XTargets[i].ID == 0) {
|
|
||||||
empty_slots.push(i);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (XTargets[i].ID != 0 && !empty_slots.empty()) {
|
|
||||||
int temp = empty_slots.front();
|
|
||||||
std::swap(XTargets[i], XTargets[temp]);
|
|
||||||
XTargets[i].dirty = XTargets[temp].dirty = true;
|
|
||||||
empty_slots.pop();
|
|
||||||
empty_slots.push(i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// If there are more mobs aggro on us than we had auto-hate slots, add one of those haters into the slot(s) we
|
|
||||||
// just freed up.
|
|
||||||
if (!HadFreeAutoSlotsBefore && FreedAutoSlots)
|
|
||||||
entity_list.RefreshAutoXTargets(this);
|
|
||||||
SendXTargetUpdates();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Client::UpdateXTargetType(XTargetType Type, Mob *m, const char *Name)
|
void Client::UpdateXTargetType(XTargetType Type, Mob *m, const char *Name)
|
||||||
@ -7405,6 +7377,123 @@ void Client::ShowXTargets(Client *c)
|
|||||||
|
|
||||||
for(int i = 0; i < GetMaxXTargets(); ++i)
|
for(int i = 0; i < GetMaxXTargets(); ++i)
|
||||||
c->Message(0, "Xtarget Slot: %i, Type: %2i, ID: %4i, Name: %s", i, XTargets[i].Type, XTargets[i].ID, XTargets[i].Name);
|
c->Message(0, "Xtarget Slot: %i, Type: %2i, ID: %4i, Name: %s", i, XTargets[i].Type, XTargets[i].ID, XTargets[i].Name);
|
||||||
|
auto &list = GetXTargetAutoMgr()->get_list();
|
||||||
|
// yeah, I kept having to do something for debugging to tell if managers were the same object or not :P
|
||||||
|
// so lets use the address as an "ID"
|
||||||
|
c->Message(0, "XTargetAutoMgr ID %p size %d", GetXTargetAutoMgr(), list.size());
|
||||||
|
int count = 0;
|
||||||
|
for (auto &e : list) {
|
||||||
|
c->Message(0, "spawn id %d count %d", e.spawn_id, e.count);
|
||||||
|
count++;
|
||||||
|
if (count == 20) { // lets not spam too many ...
|
||||||
|
c->Message(0, " ... ");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Client::ProcessXTargetAutoHaters()
|
||||||
|
{
|
||||||
|
if (!XTargettingAvailable())
|
||||||
|
return;
|
||||||
|
|
||||||
|
// move shit up! If the removed NPC was in a CurrentTargetNPC slot it becomes Auto
|
||||||
|
// and we need to potentially fill it
|
||||||
|
std::queue<int> empty_slots;
|
||||||
|
for (int i = 0; i < GetMaxXTargets(); ++i) {
|
||||||
|
if (XTargets[i].Type != Auto)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (XTargets[i].ID != 0 && !GetXTargetAutoMgr()->contains_mob(XTargets[i].ID)) {
|
||||||
|
XTargets[i].ID = 0;
|
||||||
|
XTargets[i].dirty = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (XTargets[i].ID == 0) {
|
||||||
|
empty_slots.push(i);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (XTargets[i].ID != 0 && !empty_slots.empty()) {
|
||||||
|
int temp = empty_slots.front();
|
||||||
|
std::swap(XTargets[i], XTargets[temp]);
|
||||||
|
XTargets[i].dirty = XTargets[temp].dirty = true;
|
||||||
|
empty_slots.pop();
|
||||||
|
empty_slots.push(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// okay, now we need to check if we have any empty slots and if we have aggro
|
||||||
|
// We make the assumption that if we shuffled the NPCs up that they're still on the aggro
|
||||||
|
// list in the same order. We could probably do this better and try to calc if
|
||||||
|
// there are new NPCs for our empty slots on the manager, but ahhh fuck it.
|
||||||
|
if (!empty_slots.empty() && !GetXTargetAutoMgr()->empty() && XTargetAutoAddHaters) {
|
||||||
|
auto &haters = GetXTargetAutoMgr()->get_list();
|
||||||
|
for (auto &e : haters) {
|
||||||
|
auto *mob = entity_list.GetMob(e.spawn_id);
|
||||||
|
if (!IsXTarget(mob)) {
|
||||||
|
auto slot = empty_slots.front();
|
||||||
|
empty_slots.pop();
|
||||||
|
XTargets[slot].dirty = true;
|
||||||
|
XTargets[slot].ID = mob->GetID();
|
||||||
|
strn0cpy(XTargets[slot].Name, mob->GetCleanName(), 64);
|
||||||
|
}
|
||||||
|
if (empty_slots.empty())
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
m_dirtyautohaters = false;
|
||||||
|
SendXTargetUpdates();
|
||||||
|
}
|
||||||
|
|
||||||
|
// This function is called when a client is added to a group
|
||||||
|
// Group leader joining isn't handled by this function
|
||||||
|
void Client::JoinGroupXTargets(Group *g)
|
||||||
|
{
|
||||||
|
if (!g)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!GetXTargetAutoMgr()->empty()) {
|
||||||
|
g->GetXTargetAutoMgr()->merge(*GetXTargetAutoMgr());
|
||||||
|
GetXTargetAutoMgr()->clear();
|
||||||
|
RemoveAutoXTargets();
|
||||||
|
}
|
||||||
|
|
||||||
|
SetXTargetAutoMgr(g->GetXTargetAutoMgr());
|
||||||
|
|
||||||
|
if (!GetXTargetAutoMgr()->empty())
|
||||||
|
SetDirtyAutoHaters();
|
||||||
|
}
|
||||||
|
|
||||||
|
// This function is called when a client leaves a group
|
||||||
|
void Client::LeaveGroupXTargets(Group *g)
|
||||||
|
{
|
||||||
|
if (!g)
|
||||||
|
return;
|
||||||
|
|
||||||
|
SetXTargetAutoMgr(nullptr); // this will set it back to our manager
|
||||||
|
RemoveAutoXTargets();
|
||||||
|
entity_list.RefreshAutoXTargets(this); // this will probably break the temporal ordering, but whatever
|
||||||
|
// We now have a rebuilt, valid auto hater manager, so we need to demerge from the groups
|
||||||
|
if (!GetXTargetAutoMgr()->empty()) {
|
||||||
|
GetXTargetAutoMgr()->demerge(*g->GetXTargetAutoMgr()); // this will remove entries where we only had aggro
|
||||||
|
SetDirtyAutoHaters();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// This function is called when a client leaves a group
|
||||||
|
void Client::LeaveRaidXTargets(Raid *r)
|
||||||
|
{
|
||||||
|
if (!r)
|
||||||
|
return;
|
||||||
|
|
||||||
|
SetXTargetAutoMgr(nullptr); // this will set it back to our manager
|
||||||
|
RemoveAutoXTargets();
|
||||||
|
entity_list.RefreshAutoXTargets(this); // this will probably break the temporal ordering, but whatever
|
||||||
|
// We now have a rebuilt, valid auto hater manager, so we need to demerge from the groups
|
||||||
|
if (!GetXTargetAutoMgr()->empty()) {
|
||||||
|
GetXTargetAutoMgr()->demerge(*r->GetXTargetAutoMgr()); // this will remove entries where we only had aggro
|
||||||
|
SetDirtyAutoHaters();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Client::SetMaxXTargets(uint8 NewMax)
|
void Client::SetMaxXTargets(uint8 NewMax)
|
||||||
@ -8683,3 +8772,169 @@ void Client::CheckRegionTypeChanges()
|
|||||||
else if (GetPVP())
|
else if (GetPVP())
|
||||||
SetPVP(false, false);
|
SetPVP(false, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Client::ProcessAggroMeter()
|
||||||
|
{
|
||||||
|
if (!AggroMeterAvailable()) {
|
||||||
|
aggro_meter_timer.Disable();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// we need to decide if we need to send OP_AggroMeterTargetInfo now
|
||||||
|
// This packet sends the current lock target ID and the current target ID
|
||||||
|
// target ID will be either our target or our target of target when we're targeting a PC
|
||||||
|
bool send_targetinfo = false;
|
||||||
|
auto cur_tar = GetTarget();
|
||||||
|
|
||||||
|
// probably should have PVP rules ...
|
||||||
|
if (cur_tar && cur_tar != this) {
|
||||||
|
if (cur_tar->IsNPC() && !cur_tar->IsPetOwnerClient() && cur_tar->GetID() != m_aggrometer.get_target_id()) {
|
||||||
|
m_aggrometer.set_target_id(cur_tar->GetID());
|
||||||
|
send_targetinfo = true;
|
||||||
|
} else if ((cur_tar->IsPetOwnerClient() || cur_tar->IsClient()) && cur_tar->GetTarget() && cur_tar->GetTarget()->GetID() != m_aggrometer.get_target_id()) {
|
||||||
|
m_aggrometer.set_target_id(cur_tar->GetTarget()->GetID());
|
||||||
|
send_targetinfo = true;
|
||||||
|
}
|
||||||
|
} else if (m_aggrometer.get_target_id()) {
|
||||||
|
m_aggrometer.set_target_id(0);
|
||||||
|
send_targetinfo = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_aggrometer.update_lock())
|
||||||
|
send_targetinfo = true;
|
||||||
|
|
||||||
|
if (send_targetinfo) {
|
||||||
|
auto app = new EQApplicationPacket(OP_AggroMeterTargetInfo, sizeof(uint32) * 2);
|
||||||
|
app->WriteUInt32(m_aggrometer.get_lock_id());
|
||||||
|
app->WriteUInt32(m_aggrometer.get_target_id());
|
||||||
|
FastQueuePacket(&app);
|
||||||
|
}
|
||||||
|
|
||||||
|
// we could just calculate how big the packet would need to be ... but it's easier this way :P should be 87 bytes
|
||||||
|
auto app = new EQApplicationPacket(OP_AggroMeterUpdate, m_aggrometer.max_packet_size());
|
||||||
|
|
||||||
|
cur_tar = entity_list.GetMob(m_aggrometer.get_target_id());
|
||||||
|
|
||||||
|
// first we must check the secondary
|
||||||
|
// TODO: lock target should affect secondary as well
|
||||||
|
bool send = false;
|
||||||
|
Mob *secondary = nullptr;
|
||||||
|
bool has_aggro = false;
|
||||||
|
if (cur_tar) {
|
||||||
|
if (cur_tar->GetTarget() == this) {// we got aggro
|
||||||
|
secondary = cur_tar->GetSecondaryHate(this);
|
||||||
|
has_aggro = true;
|
||||||
|
} else {
|
||||||
|
secondary = cur_tar->GetTarget();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (secondary && secondary->GetID() != m_aggrometer.get_secondary_id()) {
|
||||||
|
m_aggrometer.set_secondary_id(secondary->GetID());
|
||||||
|
app->WriteUInt8(1);
|
||||||
|
app->WriteUInt32(m_aggrometer.get_secondary_id());
|
||||||
|
send = true;
|
||||||
|
} else if (!secondary && m_aggrometer.get_secondary_id()) {
|
||||||
|
m_aggrometer.set_secondary_id(0);
|
||||||
|
app->WriteUInt8(1);
|
||||||
|
app->WriteUInt32(0);
|
||||||
|
send = true;
|
||||||
|
} else { // might not need to send in this case
|
||||||
|
app->WriteUInt8(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto count_offset = app->GetWritePosition();
|
||||||
|
app->WriteUInt8(0);
|
||||||
|
|
||||||
|
int count = 0;
|
||||||
|
auto add_entry = [&app, &count, this](AggroMeter::AggroTypes i) {
|
||||||
|
count++;
|
||||||
|
app->WriteUInt8(i);
|
||||||
|
app->WriteUInt16(m_aggrometer.get_pct(i));
|
||||||
|
};
|
||||||
|
// TODO: Player entry should either be lock or yourself, ignoring lock for now
|
||||||
|
// player, secondary, and group depend on your target/lock
|
||||||
|
if (cur_tar) {
|
||||||
|
if (m_aggrometer.set_pct(AggroMeter::AT_Player, cur_tar->GetHateRatio(cur_tar->GetTarget(), this)))
|
||||||
|
add_entry(AggroMeter::AT_Player);
|
||||||
|
|
||||||
|
if (m_aggrometer.set_pct(AggroMeter::AT_Secondary, has_aggro ? cur_tar->GetHateRatio(this, secondary) : secondary ? 100 : 0))
|
||||||
|
add_entry(AggroMeter::AT_Secondary);
|
||||||
|
|
||||||
|
// fuuuuuuuuuuuuuuuuuuuuuuuucckkkkkkkkkkkkkkk raids
|
||||||
|
if (IsRaidGrouped()) {
|
||||||
|
auto raid = GetRaid();
|
||||||
|
if (raid) {
|
||||||
|
auto gid = raid->GetGroup(this);
|
||||||
|
if (gid < 12) {
|
||||||
|
int at_id = AggroMeter::AT_Group1;
|
||||||
|
for (int i = 0; i < MAX_RAID_MEMBERS; ++i) {
|
||||||
|
if (raid->members[i].member && raid->members[i].member != this && raid->members[i].GroupNumber == gid) {
|
||||||
|
if (m_aggrometer.set_pct(static_cast<AggroMeter::AggroTypes>(at_id), cur_tar->GetHateRatio(cur_tar->GetTarget(), raid->members[i].member)))
|
||||||
|
add_entry(static_cast<AggroMeter::AggroTypes>(at_id));
|
||||||
|
at_id++;
|
||||||
|
if (at_id > AggroMeter::AT_Group5)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (IsGrouped()) {
|
||||||
|
auto group = GetGroup();
|
||||||
|
if (group) {
|
||||||
|
int at_id = AggroMeter::AT_Group1;
|
||||||
|
for (int i = 0; i < MAX_GROUP_MEMBERS; ++i) {
|
||||||
|
if (group->members[i] && group->members[i] != this) {
|
||||||
|
if (m_aggrometer.set_pct(static_cast<AggroMeter::AggroTypes>(at_id), cur_tar->GetHateRatio(cur_tar->GetTarget(), group->members[i])))
|
||||||
|
add_entry(static_cast<AggroMeter::AggroTypes>(at_id));
|
||||||
|
at_id++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else { // we might need to clear out some data now
|
||||||
|
if (m_aggrometer.set_pct(AggroMeter::AT_Player, 0))
|
||||||
|
add_entry(AggroMeter::AT_Player);
|
||||||
|
if (m_aggrometer.set_pct(AggroMeter::AT_Secondary, 0))
|
||||||
|
add_entry(AggroMeter::AT_Secondary);
|
||||||
|
if (m_aggrometer.set_pct(AggroMeter::AT_Group1, 0))
|
||||||
|
add_entry(AggroMeter::AT_Group1);
|
||||||
|
if (m_aggrometer.set_pct(AggroMeter::AT_Group2, 0))
|
||||||
|
add_entry(AggroMeter::AT_Group2);
|
||||||
|
if (m_aggrometer.set_pct(AggroMeter::AT_Group3, 0))
|
||||||
|
add_entry(AggroMeter::AT_Group3);
|
||||||
|
if (m_aggrometer.set_pct(AggroMeter::AT_Group4, 0))
|
||||||
|
add_entry(AggroMeter::AT_Group5);
|
||||||
|
if (m_aggrometer.set_pct(AggroMeter::AT_Group5, 0))
|
||||||
|
add_entry(AggroMeter::AT_Group5);
|
||||||
|
}
|
||||||
|
|
||||||
|
// now to go over our xtargets
|
||||||
|
// if the entry is an NPC it's our hate relative to the NPCs current tank
|
||||||
|
// if it's a PC, it's their hate relative to our current target
|
||||||
|
for (int i = 0; i < GetMaxXTargets(); ++i) {
|
||||||
|
if (XTargets[i].ID) {
|
||||||
|
auto mob = entity_list.GetMob(XTargets[i].ID);
|
||||||
|
if (mob) {
|
||||||
|
int ratio = 0;
|
||||||
|
if (mob->IsNPC())
|
||||||
|
ratio = mob->GetHateRatio(mob->GetTarget(), this);
|
||||||
|
else if (cur_tar)
|
||||||
|
ratio = cur_tar->GetHateRatio(cur_tar->GetTarget(), mob);
|
||||||
|
if (m_aggrometer.set_pct(static_cast<AggroMeter::AggroTypes>(AggroMeter::AT_XTarget1 + i), ratio))
|
||||||
|
add_entry(static_cast<AggroMeter::AggroTypes>(AggroMeter::AT_XTarget1 + i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (send || count) {
|
||||||
|
app->size = app->GetWritePosition(); // this should be safe, although not recommended
|
||||||
|
// but this way we can have a smaller buffer created for the packet dispatched to the client w/o resizing this one
|
||||||
|
app->SetWritePosition(count_offset);
|
||||||
|
app->WriteUInt8(count);
|
||||||
|
FastQueuePacket(&app);
|
||||||
|
} else {
|
||||||
|
safe_delete(app);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@ -48,6 +48,8 @@ namespace EQEmu
|
|||||||
#include "../common/inventory_profile.h"
|
#include "../common/inventory_profile.h"
|
||||||
#include "../common/guilds.h"
|
#include "../common/guilds.h"
|
||||||
//#include "../common/item_data.h"
|
//#include "../common/item_data.h"
|
||||||
|
#include "xtargetautohaters.h"
|
||||||
|
#include "aggromanager.h"
|
||||||
|
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
#include "merc.h"
|
#include "merc.h"
|
||||||
@ -1122,9 +1124,21 @@ public:
|
|||||||
void RemoveGroupXTargets();
|
void RemoveGroupXTargets();
|
||||||
void RemoveAutoXTargets();
|
void RemoveAutoXTargets();
|
||||||
void ShowXTargets(Client *c);
|
void ShowXTargets(Client *c);
|
||||||
|
inline XTargetAutoHaters *GetXTargetAutoMgr() { return m_activeautohatermgr; } // will be either raid or group or self
|
||||||
|
inline void SetXTargetAutoMgr(XTargetAutoHaters *in) { if (in) m_activeautohatermgr = in; else m_activeautohatermgr = &m_autohatermgr; }
|
||||||
|
inline void SetDirtyAutoHaters() { m_dirtyautohaters = true; }
|
||||||
|
void ProcessXTargetAutoHaters(); // fixes up our auto haters
|
||||||
|
void JoinGroupXTargets(Group *g);
|
||||||
|
void LeaveGroupXTargets(Group *g);
|
||||||
|
void LeaveRaidXTargets(Raid *r);
|
||||||
bool GroupFollow(Client* inviter);
|
bool GroupFollow(Client* inviter);
|
||||||
inline bool GetRunMode() const { return runmode; }
|
inline bool GetRunMode() const { return runmode; }
|
||||||
|
|
||||||
|
inline bool AggroMeterAvailable() const { return ((m_ClientVersionBit & EQEmu::versions::bit_RoF2AndLater)) && RuleB(Character, EnableAggroMeter); } // RoF untested
|
||||||
|
inline void SetAggroMeterLock(int in) { m_aggrometer.set_lock_id(in); }
|
||||||
|
|
||||||
|
void ProcessAggroMeter(); // builds packet and sends
|
||||||
|
|
||||||
void InitializeMercInfo();
|
void InitializeMercInfo();
|
||||||
bool CheckCanSpawnMerc(uint32 template_id);
|
bool CheckCanSpawnMerc(uint32 template_id);
|
||||||
bool CheckCanHireMerc(Mob* merchant, uint32 template_id);
|
bool CheckCanHireMerc(Mob* merchant, uint32 template_id);
|
||||||
@ -1462,6 +1476,7 @@ private:
|
|||||||
Timer afk_toggle_timer;
|
Timer afk_toggle_timer;
|
||||||
Timer helm_toggle_timer;
|
Timer helm_toggle_timer;
|
||||||
Timer light_update_timer;
|
Timer light_update_timer;
|
||||||
|
Timer aggro_meter_timer;
|
||||||
|
|
||||||
glm::vec3 m_Proximity;
|
glm::vec3 m_Proximity;
|
||||||
|
|
||||||
@ -1546,8 +1561,13 @@ private:
|
|||||||
|
|
||||||
uint8 MaxXTargets;
|
uint8 MaxXTargets;
|
||||||
bool XTargetAutoAddHaters;
|
bool XTargetAutoAddHaters;
|
||||||
|
bool m_dirtyautohaters;
|
||||||
|
|
||||||
struct XTarget_Struct XTargets[XTARGET_HARDCAP];
|
struct XTarget_Struct XTargets[XTARGET_HARDCAP];
|
||||||
|
XTargetAutoHaters m_autohatermgr;
|
||||||
|
XTargetAutoHaters *m_activeautohatermgr;
|
||||||
|
|
||||||
|
AggroMeter m_aggrometer;
|
||||||
|
|
||||||
Timer ItemTickTimer;
|
Timer ItemTickTimer;
|
||||||
Timer ItemQuestTimer;
|
Timer ItemQuestTimer;
|
||||||
|
|||||||
@ -120,6 +120,7 @@ void MapOpcodes()
|
|||||||
ConnectedOpcodes[OP_AdventureMerchantSell] = &Client::Handle_OP_AdventureMerchantSell;
|
ConnectedOpcodes[OP_AdventureMerchantSell] = &Client::Handle_OP_AdventureMerchantSell;
|
||||||
ConnectedOpcodes[OP_AdventureRequest] = &Client::Handle_OP_AdventureRequest;
|
ConnectedOpcodes[OP_AdventureRequest] = &Client::Handle_OP_AdventureRequest;
|
||||||
ConnectedOpcodes[OP_AdventureStatsRequest] = &Client::Handle_OP_AdventureStatsRequest;
|
ConnectedOpcodes[OP_AdventureStatsRequest] = &Client::Handle_OP_AdventureStatsRequest;
|
||||||
|
ConnectedOpcodes[OP_AggroMeterLockTarget] = &Client::Handle_OP_AggroMeterLockTarget;
|
||||||
ConnectedOpcodes[OP_AltCurrencyMerchantRequest] = &Client::Handle_OP_AltCurrencyMerchantRequest;
|
ConnectedOpcodes[OP_AltCurrencyMerchantRequest] = &Client::Handle_OP_AltCurrencyMerchantRequest;
|
||||||
ConnectedOpcodes[OP_AltCurrencyPurchase] = &Client::Handle_OP_AltCurrencyPurchase;
|
ConnectedOpcodes[OP_AltCurrencyPurchase] = &Client::Handle_OP_AltCurrencyPurchase;
|
||||||
ConnectedOpcodes[OP_AltCurrencyReclaim] = &Client::Handle_OP_AltCurrencyReclaim;
|
ConnectedOpcodes[OP_AltCurrencyReclaim] = &Client::Handle_OP_AltCurrencyReclaim;
|
||||||
@ -575,6 +576,11 @@ void Client::CompleteConnect()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
raid->SendGroupLeadershipAA(this, grpID); // this may get sent an extra time ...
|
raid->SendGroupLeadershipAA(this, grpID); // this may get sent an extra time ...
|
||||||
|
|
||||||
|
SetXTargetAutoMgr(raid->GetXTargetAutoMgr());
|
||||||
|
if (!GetXTargetAutoMgr()->empty())
|
||||||
|
SetDirtyAutoHaters();
|
||||||
|
|
||||||
if (raid->IsLocked())
|
if (raid->IsLocked())
|
||||||
raid->SendRaidLockTo(this);
|
raid->SendRaidLockTo(this);
|
||||||
}
|
}
|
||||||
@ -1548,8 +1554,8 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app)
|
|||||||
// we purchased a new one while out-of-zone.
|
// we purchased a new one while out-of-zone.
|
||||||
if (group->IsLeader(this))
|
if (group->IsLeader(this))
|
||||||
group->SendLeadershipAAUpdate();
|
group->SendLeadershipAAUpdate();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
JoinGroupXTargets(group);
|
||||||
group->UpdatePlayer(this);
|
group->UpdatePlayer(this);
|
||||||
LFG = false;
|
LFG = false;
|
||||||
}
|
}
|
||||||
@ -2395,6 +2401,17 @@ void Client::Handle_OP_AdventureStatsRequest(const EQApplicationPacket *app)
|
|||||||
FastQueuePacket(&outapp);
|
FastQueuePacket(&outapp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Client::Handle_OP_AggroMeterLockTarget(const EQApplicationPacket *app)
|
||||||
|
{
|
||||||
|
if (app->size < sizeof(uint32)) {
|
||||||
|
Log.Out(Logs::General, Logs::Error, "Handle_OP_AggroMeterLockTarget had a packet that was too small.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
SetAggroMeterLock(app->ReadUInt32(0));
|
||||||
|
ProcessAggroMeter();
|
||||||
|
}
|
||||||
|
|
||||||
void Client::Handle_OP_AltCurrencyMerchantRequest(const EQApplicationPacket *app)
|
void Client::Handle_OP_AltCurrencyMerchantRequest(const EQApplicationPacket *app)
|
||||||
{
|
{
|
||||||
VERIFY_PACKET_LENGTH(OP_AltCurrencyMerchantRequest, app, uint32);
|
VERIFY_PACKET_LENGTH(OP_AltCurrencyMerchantRequest, app, uint32);
|
||||||
@ -10806,7 +10823,8 @@ void Client::Handle_OP_RaidCommand(const EQApplicationPacket *app)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
g->DisbandGroup();
|
g->JoinRaidXTarget(r);
|
||||||
|
g->DisbandGroup(true);
|
||||||
r->GroupUpdate(freeGroup);
|
r->GroupUpdate(freeGroup);
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
@ -10871,7 +10889,8 @@ void Client::Handle_OP_RaidCommand(const EQApplicationPacket *app)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ig->DisbandGroup();
|
ig->JoinRaidXTarget(r, true);
|
||||||
|
ig->DisbandGroup(true);
|
||||||
r->GroupUpdate(groupFree);
|
r->GroupUpdate(groupFree);
|
||||||
groupFree = r->GetFreeGroup();
|
groupFree = r->GetFreeGroup();
|
||||||
}
|
}
|
||||||
@ -10924,10 +10943,11 @@ void Client::Handle_OP_RaidCommand(const EQApplicationPacket *app)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
g->DisbandGroup();
|
g->JoinRaidXTarget(r);
|
||||||
|
g->DisbandGroup(true);
|
||||||
r->GroupUpdate(groupFree);
|
r->GroupUpdate(groupFree);
|
||||||
}
|
}
|
||||||
else
|
else // target does not have a group
|
||||||
{
|
{
|
||||||
if (ig){
|
if (ig){
|
||||||
r = new Raid(i);
|
r = new Raid(i);
|
||||||
@ -10981,14 +11001,15 @@ void Client::Handle_OP_RaidCommand(const EQApplicationPacket *app)
|
|||||||
r->SendRaidCreate(this);
|
r->SendRaidCreate(this);
|
||||||
r->SendMakeLeaderPacketTo(r->leadername, this);
|
r->SendMakeLeaderPacketTo(r->leadername, this);
|
||||||
r->SendBulkRaid(this);
|
r->SendBulkRaid(this);
|
||||||
|
ig->JoinRaidXTarget(r, true);
|
||||||
r->AddMember(this);
|
r->AddMember(this);
|
||||||
ig->DisbandGroup();
|
ig->DisbandGroup(true);
|
||||||
r->GroupUpdate(0);
|
r->GroupUpdate(0);
|
||||||
if (r->IsLocked()) {
|
if (r->IsLocked()) {
|
||||||
r->SendRaidLockTo(this);
|
r->SendRaidLockTo(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else{
|
else{ // neither has a group
|
||||||
r = new Raid(i);
|
r = new Raid(i);
|
||||||
entity_list.AddRaid(r);
|
entity_list.AddRaid(r);
|
||||||
r->SetRaidDetails();
|
r->SetRaidDetails();
|
||||||
@ -12419,14 +12440,33 @@ void Client::Handle_OP_ShopPlayerSell(const EQApplicationPacket *app)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
int cost_quantity = mp->quantity;
|
uint32 cost_quantity = mp->quantity;
|
||||||
if (inst->IsCharged())
|
if (inst->IsCharged())
|
||||||
int cost_quantity = 1;
|
uint32 cost_quantity = 1;
|
||||||
|
|
||||||
|
uint32 i;
|
||||||
|
|
||||||
|
if (RuleB(Merchant, UsePriceMod)) {
|
||||||
|
for (i = 0; i < cost_quantity; i++) {
|
||||||
|
price = (uint32)((item->Price * i)*(RuleR(Merchant, BuyCostMod))*Client::CalcPriceMod(vendor, true) + 0.5); // need to round up, because client does it automatically when displaying price
|
||||||
|
if (price > 4000000000) {
|
||||||
|
cost_quantity = i;
|
||||||
|
mp->quantity = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
for (i = 0; i < cost_quantity; i++) {
|
||||||
|
price = (uint32)((item->Price * i)*(RuleR(Merchant, BuyCostMod)) + 0.5); // need to round up, because client does it automatically when displaying price
|
||||||
|
if (price > 4000000000) {
|
||||||
|
cost_quantity = i;
|
||||||
|
mp->quantity = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (RuleB(Merchant, UsePriceMod))
|
|
||||||
price = (int)((item->Price*cost_quantity)*(RuleR(Merchant, BuyCostMod))*Client::CalcPriceMod(vendor, true) + 0.5); // need to round up, because client does it automatically when displaying price
|
|
||||||
else
|
|
||||||
price = (int)((item->Price*cost_quantity)*(RuleR(Merchant, BuyCostMod)) + 0.5);
|
|
||||||
AddMoneyToPP(price, false);
|
AddMoneyToPP(price, false);
|
||||||
|
|
||||||
if (inst->IsStackable() || inst->IsCharged())
|
if (inst->IsStackable() || inst->IsCharged())
|
||||||
@ -14087,6 +14127,7 @@ void Client::Handle_OP_XTargetAutoAddHaters(const EQApplicationPacket *app)
|
|||||||
}
|
}
|
||||||
|
|
||||||
XTargetAutoAddHaters = app->ReadUInt8(0);
|
XTargetAutoAddHaters = app->ReadUInt8(0);
|
||||||
|
SetDirtyAutoHaters();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Client::Handle_OP_XTargetOpen(const EQApplicationPacket *app)
|
void Client::Handle_OP_XTargetOpen(const EQApplicationPacket *app)
|
||||||
|
|||||||
@ -32,6 +32,7 @@
|
|||||||
void Handle_OP_AdventureMerchantSell(const EQApplicationPacket *app);
|
void Handle_OP_AdventureMerchantSell(const EQApplicationPacket *app);
|
||||||
void Handle_OP_AdventureRequest(const EQApplicationPacket *app);
|
void Handle_OP_AdventureRequest(const EQApplicationPacket *app);
|
||||||
void Handle_OP_AdventureStatsRequest(const EQApplicationPacket *app);
|
void Handle_OP_AdventureStatsRequest(const EQApplicationPacket *app);
|
||||||
|
void Handle_OP_AggroMeterLockTarget(const EQApplicationPacket *app);
|
||||||
void Handle_OP_AltCurrencyMerchantRequest(const EQApplicationPacket *app);
|
void Handle_OP_AltCurrencyMerchantRequest(const EQApplicationPacket *app);
|
||||||
void Handle_OP_AltCurrencyPurchase(const EQApplicationPacket *app);
|
void Handle_OP_AltCurrencyPurchase(const EQApplicationPacket *app);
|
||||||
void Handle_OP_AltCurrencyReclaim(const EQApplicationPacket *app);
|
void Handle_OP_AltCurrencyReclaim(const EQApplicationPacket *app);
|
||||||
|
|||||||
@ -681,6 +681,13 @@ bool Client::Process() {
|
|||||||
Message(0, "Your enemies have forgotten you!");
|
Message(0, "Your enemies have forgotten you!");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (client_state == CLIENT_CONNECTED) {
|
||||||
|
if (m_dirtyautohaters)
|
||||||
|
ProcessXTargetAutoHaters();
|
||||||
|
if (aggro_meter_timer.Check())
|
||||||
|
ProcessAggroMeter();
|
||||||
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1404,15 +1404,15 @@ void EntityList::RemoveFromTargets(Mob *mob, bool RemoveFromXTargets)
|
|||||||
if (!m)
|
if (!m)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
m->RemoveFromHateList(mob);
|
|
||||||
|
|
||||||
if (RemoveFromXTargets) {
|
if (RemoveFromXTargets) {
|
||||||
if (m->IsClient())
|
if (m->IsClient() && mob->CheckAggro(m))
|
||||||
m->CastToClient()->RemoveXTarget(mob, false);
|
m->CastToClient()->RemoveXTarget(mob, false);
|
||||||
// FadingMemories calls this function passing the client.
|
// FadingMemories calls this function passing the client.
|
||||||
else if (mob->IsClient())
|
else if (mob->IsClient() && m->CheckAggro(mob))
|
||||||
mob->CastToClient()->RemoveXTarget(m, false);
|
mob->CastToClient()->RemoveXTarget(m, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
m->RemoveFromHateList(mob);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2557,6 +2557,8 @@ void EntityList::RemoveFromHateLists(Mob *mob, bool settoone)
|
|||||||
it->second->RemoveFromHateList(mob);
|
it->second->RemoveFromHateList(mob);
|
||||||
else
|
else
|
||||||
it->second->SetHateAmountOnEnt(mob, 1);
|
it->second->SetHateAmountOnEnt(mob, 1);
|
||||||
|
if (mob->IsClient())
|
||||||
|
mob->CastToClient()->RemoveXTarget(it->second, false); // gotta do book keeping
|
||||||
}
|
}
|
||||||
++it;
|
++it;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -64,6 +64,8 @@ Group::Group(uint32 gid)
|
|||||||
MarkedNPCs[i] = 0;
|
MarkedNPCs[i] = 0;
|
||||||
|
|
||||||
NPCMarkerID = 0;
|
NPCMarkerID = 0;
|
||||||
|
|
||||||
|
m_autohatermgr.SetOwner(nullptr, this, nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
//creating a new group
|
//creating a new group
|
||||||
@ -94,6 +96,7 @@ Group::Group(Mob* leader)
|
|||||||
MarkedNPCs[i] = 0;
|
MarkedNPCs[i] = 0;
|
||||||
|
|
||||||
NPCMarkerID = 0;
|
NPCMarkerID = 0;
|
||||||
|
m_autohatermgr.SetOwner(nullptr, this, nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
Group::~Group()
|
Group::~Group()
|
||||||
@ -339,6 +342,9 @@ bool Group::AddMember(Mob* newmember, const char *NewMemberName, uint32 Characte
|
|||||||
database.SetGroupID(NewMemberName, GetID(), CharacterID, ismerc);
|
database.SetGroupID(NewMemberName, GetID(), CharacterID, ismerc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (newmember && newmember->IsClient())
|
||||||
|
newmember->CastToClient()->JoinGroupXTargets(this);
|
||||||
|
|
||||||
safe_delete(outapp);
|
safe_delete(outapp);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@ -701,8 +707,10 @@ bool Group::DelMember(Mob* oldmember, bool ignoresender)
|
|||||||
if (oldmember->GetName() == mentoree_name)
|
if (oldmember->GetName() == mentoree_name)
|
||||||
ClearGroupMentor();
|
ClearGroupMentor();
|
||||||
|
|
||||||
if(oldmember->IsClient())
|
if(oldmember->IsClient()) {
|
||||||
SendMarkedNPCsToMember(oldmember->CastToClient(), true);
|
SendMarkedNPCsToMember(oldmember->CastToClient(), true);
|
||||||
|
oldmember->CastToClient()->LeaveGroupXTargets(this);
|
||||||
|
}
|
||||||
|
|
||||||
if(GroupCount() < 3)
|
if(GroupCount() < 3)
|
||||||
{
|
{
|
||||||
@ -855,7 +863,7 @@ uint32 Group::GetTotalGroupDamage(Mob* other) {
|
|||||||
return total;
|
return total;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Group::DisbandGroup() {
|
void Group::DisbandGroup(bool joinraid) {
|
||||||
auto outapp = new EQApplicationPacket(OP_GroupUpdate, sizeof(GroupUpdate_Struct));
|
auto outapp = new EQApplicationPacket(OP_GroupUpdate, sizeof(GroupUpdate_Struct));
|
||||||
|
|
||||||
GroupUpdate_Struct* gu = (GroupUpdate_Struct*) outapp->pBuffer;
|
GroupUpdate_Struct* gu = (GroupUpdate_Struct*) outapp->pBuffer;
|
||||||
@ -882,6 +890,8 @@ void Group::DisbandGroup() {
|
|||||||
database.SetGroupID(members[i]->GetCleanName(), 0, members[i]->CastToClient()->CharacterID(), false);
|
database.SetGroupID(members[i]->GetCleanName(), 0, members[i]->CastToClient()->CharacterID(), false);
|
||||||
members[i]->CastToClient()->QueuePacket(outapp);
|
members[i]->CastToClient()->QueuePacket(outapp);
|
||||||
SendMarkedNPCsToMember(members[i]->CastToClient(), true);
|
SendMarkedNPCsToMember(members[i]->CastToClient(), true);
|
||||||
|
if (!joinraid)
|
||||||
|
members[i]->CastToClient()->LeaveGroupXTargets(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (members[i]->IsMerc())
|
if (members[i]->IsMerc())
|
||||||
@ -2275,6 +2285,30 @@ void Group::UpdateXTargetMarkedNPC(uint32 Number, Mob *m)
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Group::SetDirtyAutoHaters()
|
||||||
|
{
|
||||||
|
for (int i = 0; i < MAX_GROUP_MEMBERS; ++i)
|
||||||
|
if (members[i] && members[i]->IsClient())
|
||||||
|
members[i]->CastToClient()->SetDirtyAutoHaters();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Group::JoinRaidXTarget(Raid *raid, bool first)
|
||||||
|
{
|
||||||
|
if (!GetXTargetAutoMgr()->empty())
|
||||||
|
raid->GetXTargetAutoMgr()->merge(*GetXTargetAutoMgr());
|
||||||
|
|
||||||
|
for (int i = 0; i < MAX_GROUP_MEMBERS; ++i) {
|
||||||
|
if (members[i] && members[i]->IsClient()) {
|
||||||
|
auto *client = members[i]->CastToClient();
|
||||||
|
if (!first)
|
||||||
|
client->RemoveAutoXTargets();
|
||||||
|
client->SetXTargetAutoMgr(raid->GetXTargetAutoMgr());
|
||||||
|
if (!client->GetXTargetAutoMgr()->empty())
|
||||||
|
client->SetDirtyAutoHaters();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Group::SetMainTank(const char *NewMainTankName)
|
void Group::SetMainTank(const char *NewMainTankName)
|
||||||
{
|
{
|
||||||
MainTankName = NewMainTankName;
|
MainTankName = NewMainTankName;
|
||||||
|
|||||||
@ -22,6 +22,7 @@
|
|||||||
#include "../common/types.h"
|
#include "../common/types.h"
|
||||||
|
|
||||||
#include "mob.h"
|
#include "mob.h"
|
||||||
|
#include "xtargetautohaters.h"
|
||||||
|
|
||||||
class Client;
|
class Client;
|
||||||
class EQApplicationPacket;
|
class EQApplicationPacket;
|
||||||
@ -58,7 +59,7 @@ public:
|
|||||||
void SendWorldGroup(uint32 zone_id,Mob* zoningmember);
|
void SendWorldGroup(uint32 zone_id,Mob* zoningmember);
|
||||||
bool DelMemberOOZ(const char *Name);
|
bool DelMemberOOZ(const char *Name);
|
||||||
bool DelMember(Mob* oldmember,bool ignoresender = false);
|
bool DelMember(Mob* oldmember,bool ignoresender = false);
|
||||||
void DisbandGroup();
|
void DisbandGroup(bool joinraid = false);
|
||||||
void GetMemberList(std::list<Mob*>& member_list, bool clear_list = true);
|
void GetMemberList(std::list<Mob*>& member_list, bool clear_list = true);
|
||||||
void GetClientList(std::list<Client*>& client_list, bool clear_list = true);
|
void GetClientList(std::list<Client*>& client_list, bool clear_list = true);
|
||||||
#ifdef BOTS
|
#ifdef BOTS
|
||||||
@ -140,6 +141,9 @@ public:
|
|||||||
void ChangeLeader(Mob* newleader);
|
void ChangeLeader(Mob* newleader);
|
||||||
const char *GetClientNameByIndex(uint8 index);
|
const char *GetClientNameByIndex(uint8 index);
|
||||||
void UpdateXTargetMarkedNPC(uint32 Number, Mob *m);
|
void UpdateXTargetMarkedNPC(uint32 Number, Mob *m);
|
||||||
|
void SetDirtyAutoHaters();
|
||||||
|
inline XTargetAutoHaters *GetXTargetAutoMgr() { return &m_autohatermgr; }
|
||||||
|
void JoinRaidXTarget(Raid *raid, bool first = false);
|
||||||
|
|
||||||
void SetGroupMentor(int percent, char *name);
|
void SetGroupMentor(int percent, char *name);
|
||||||
void ClearGroupMentor();
|
void ClearGroupMentor();
|
||||||
@ -168,6 +172,8 @@ private:
|
|||||||
std::string mentoree_name;
|
std::string mentoree_name;
|
||||||
Client *mentoree;
|
Client *mentoree;
|
||||||
int mentor_percent;
|
int mentor_percent;
|
||||||
|
|
||||||
|
XTargetAutoHaters m_autohatermgr;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@ -23,6 +23,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|||||||
#include "raids.h"
|
#include "raids.h"
|
||||||
|
|
||||||
#include "../common/rulesys.h"
|
#include "../common/rulesys.h"
|
||||||
|
#include "../common/data_verification.h"
|
||||||
|
|
||||||
#include "hate_list.h"
|
#include "hate_list.h"
|
||||||
#include "quest_parser_collection.h"
|
#include "quest_parser_collection.h"
|
||||||
@ -277,7 +278,24 @@ int HateList::GetSummonedPetCountOnHateList(Mob *hater) {
|
|||||||
return pet_count;
|
return pet_count;
|
||||||
}
|
}
|
||||||
|
|
||||||
Mob *HateList::GetEntWithMostHateOnList(Mob *center)
|
int HateList::GetHateRatio(Mob *top, Mob *other)
|
||||||
|
{
|
||||||
|
auto other_entry = Find(other);
|
||||||
|
|
||||||
|
if (!other_entry || other_entry->stored_hate_amount < 1)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
auto top_entry = Find(top);
|
||||||
|
|
||||||
|
if (!top_entry || top_entry->stored_hate_amount < 1)
|
||||||
|
return 999; // shouldn't happen if you call it right :P
|
||||||
|
|
||||||
|
return EQEmu::Clamp(static_cast<int>((other_entry->stored_hate_amount * 100) / top_entry->stored_hate_amount), 1, 999);
|
||||||
|
}
|
||||||
|
|
||||||
|
// skip is used to ignore a certain mob on the list
|
||||||
|
// Currently used for getting 2nd on list for aggro meter
|
||||||
|
Mob *HateList::GetEntWithMostHateOnList(Mob *center, Mob *skip)
|
||||||
{
|
{
|
||||||
// hack fix for zone shutdown crashes on some servers
|
// hack fix for zone shutdown crashes on some servers
|
||||||
if (!zone->IsLoaded())
|
if (!zone->IsLoaded())
|
||||||
@ -310,6 +328,11 @@ Mob *HateList::GetEntWithMostHateOnList(Mob *center)
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (cur->entity_on_hatelist == skip) {
|
||||||
|
++iterator;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
auto hateEntryPosition = glm::vec3(cur->entity_on_hatelist->GetX(), cur->entity_on_hatelist->GetY(), cur->entity_on_hatelist->GetZ());
|
auto hateEntryPosition = glm::vec3(cur->entity_on_hatelist->GetX(), cur->entity_on_hatelist->GetY(), cur->entity_on_hatelist->GetZ());
|
||||||
if (center->IsNPC() && center->CastToNPC()->IsUnderwaterOnly() && zone->HasWaterMap()) {
|
if (center->IsNPC() && center->CastToNPC()->IsUnderwaterOnly() && zone->HasWaterMap()) {
|
||||||
if (!zone->watermap->InLiquid(hateEntryPosition)) {
|
if (!zone->watermap->InLiquid(hateEntryPosition)) {
|
||||||
@ -436,6 +459,11 @@ Mob *HateList::GetEntWithMostHateOnList(Mob *center)
|
|||||||
while (iterator != list.end())
|
while (iterator != list.end())
|
||||||
{
|
{
|
||||||
struct_HateList *cur = (*iterator);
|
struct_HateList *cur = (*iterator);
|
||||||
|
if (cur->entity_on_hatelist == skip) {
|
||||||
|
++iterator;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (center->IsNPC() && center->CastToNPC()->IsUnderwaterOnly() && zone->HasWaterMap()) {
|
if (center->IsNPC() && center->CastToNPC()->IsUnderwaterOnly() && zone->HasWaterMap()) {
|
||||||
if(!zone->watermap->InLiquid(glm::vec3(cur->entity_on_hatelist->GetPosition()))) {
|
if(!zone->watermap->InLiquid(glm::vec3(cur->entity_on_hatelist->GetPosition()))) {
|
||||||
skipped_count++;
|
skipped_count++;
|
||||||
|
|||||||
@ -41,9 +41,9 @@ public:
|
|||||||
|
|
||||||
Mob *GetClosestEntOnHateList(Mob *hater);
|
Mob *GetClosestEntOnHateList(Mob *hater);
|
||||||
Mob *GetDamageTopOnHateList(Mob *hater);
|
Mob *GetDamageTopOnHateList(Mob *hater);
|
||||||
Mob *GetEntWithMostHateOnList(Mob *center);
|
Mob *GetEntWithMostHateOnList(Mob *center, Mob *skip = nullptr);
|
||||||
Mob *GetRandomEntOnHateList();
|
Mob *GetRandomEntOnHateList();
|
||||||
Mob* GetEntWithMostHateOnList();
|
Mob *GetEntWithMostHateOnList();
|
||||||
|
|
||||||
bool IsEntOnHateList(Mob *mob);
|
bool IsEntOnHateList(Mob *mob);
|
||||||
bool IsHateListEmpty();
|
bool IsHateListEmpty();
|
||||||
@ -51,6 +51,7 @@ public:
|
|||||||
|
|
||||||
int AreaRampage(Mob *caster, Mob *target, int count, ExtraAttackOptions *opts);
|
int AreaRampage(Mob *caster, Mob *target, int count, ExtraAttackOptions *opts);
|
||||||
int GetSummonedPetCountOnHateList(Mob *hater);
|
int GetSummonedPetCountOnHateList(Mob *hater);
|
||||||
|
int GetHateRatio(Mob *top, Mob *other);
|
||||||
|
|
||||||
int32 GetEntHateAmount(Mob *ent, bool in_damage = false);
|
int32 GetEntHateAmount(Mob *ent, bool in_damage = false);
|
||||||
|
|
||||||
@ -74,3 +75,4 @@ private:
|
|||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|||||||
@ -539,7 +539,9 @@ public:
|
|||||||
void DoubleAggro(Mob *other) { uint32 in_hate = GetHateAmount(other); SetHateAmountOnEnt(other, (in_hate ? in_hate * 2 : 1)); }
|
void DoubleAggro(Mob *other) { uint32 in_hate = GetHateAmount(other); SetHateAmountOnEnt(other, (in_hate ? in_hate * 2 : 1)); }
|
||||||
uint32 GetHateAmount(Mob* tmob, bool is_dam = false) { return hate_list.GetEntHateAmount(tmob,is_dam);}
|
uint32 GetHateAmount(Mob* tmob, bool is_dam = false) { return hate_list.GetEntHateAmount(tmob,is_dam);}
|
||||||
uint32 GetDamageAmount(Mob* tmob) { return hate_list.GetEntHateAmount(tmob, true);}
|
uint32 GetDamageAmount(Mob* tmob) { return hate_list.GetEntHateAmount(tmob, true);}
|
||||||
|
int GetHateRatio(Mob *first, Mob *with) { return hate_list.GetHateRatio(first, with); }
|
||||||
Mob* GetHateTop() { return hate_list.GetEntWithMostHateOnList(this);}
|
Mob* GetHateTop() { return hate_list.GetEntWithMostHateOnList(this);}
|
||||||
|
Mob* GetSecondaryHate(Mob *skip) { return hate_list.GetEntWithMostHateOnList(this, skip); }
|
||||||
Mob* GetHateDamageTop(Mob* other) { return hate_list.GetDamageTopOnHateList(other);}
|
Mob* GetHateDamageTop(Mob* other) { return hate_list.GetDamageTopOnHateList(other);}
|
||||||
Mob* GetHateRandom() { return hate_list.GetRandomEntOnHateList();}
|
Mob* GetHateRandom() { return hate_list.GetRandomEntOnHateList();}
|
||||||
Mob* GetHateMost() { return hate_list.GetEntWithMostHateOnList();}
|
Mob* GetHateMost() { return hate_list.GetEntWithMostHateOnList();}
|
||||||
|
|||||||
@ -43,6 +43,8 @@ Raid::Raid(uint32 raidID)
|
|||||||
memset(leadername, 0, 64);
|
memset(leadername, 0, 64);
|
||||||
locked = false;
|
locked = false;
|
||||||
LootType = 4;
|
LootType = 4;
|
||||||
|
|
||||||
|
m_autohatermgr.SetOwner(nullptr, nullptr, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
Raid::Raid(Client* nLeader)
|
Raid::Raid(Client* nLeader)
|
||||||
@ -60,6 +62,8 @@ Raid::Raid(Client* nLeader)
|
|||||||
strn0cpy(leadername, nLeader->GetName(), 64);
|
strn0cpy(leadername, nLeader->GetName(), 64);
|
||||||
locked = false;
|
locked = false;
|
||||||
LootType = 4;
|
LootType = 4;
|
||||||
|
|
||||||
|
m_autohatermgr.SetOwner(nullptr, nullptr, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
Raid::~Raid()
|
Raid::~Raid()
|
||||||
@ -121,6 +125,26 @@ void Raid::AddMember(Client *c, uint32 group, bool rleader, bool groupleader, bo
|
|||||||
c->SetRaidGrouped(true);
|
c->SetRaidGrouped(true);
|
||||||
SendRaidMOTD(c);
|
SendRaidMOTD(c);
|
||||||
|
|
||||||
|
// xtarget shit ..........
|
||||||
|
if (group == RAID_GROUPLESS) {
|
||||||
|
if (rleader) {
|
||||||
|
GetXTargetAutoMgr()->merge(*c->GetXTargetAutoMgr());
|
||||||
|
c->GetXTargetAutoMgr()->clear();
|
||||||
|
c->SetXTargetAutoMgr(GetXTargetAutoMgr());
|
||||||
|
} else {
|
||||||
|
if (!c->GetXTargetAutoMgr()->empty()) {
|
||||||
|
GetXTargetAutoMgr()->merge(*c->GetXTargetAutoMgr());
|
||||||
|
c->GetXTargetAutoMgr()->clear();
|
||||||
|
c->RemoveAutoXTargets();
|
||||||
|
}
|
||||||
|
|
||||||
|
c->SetXTargetAutoMgr(GetXTargetAutoMgr());
|
||||||
|
|
||||||
|
if (!c->GetXTargetAutoMgr()->empty())
|
||||||
|
c->SetDirtyAutoHaters();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
auto pack = new ServerPacket(ServerOP_RaidAdd, sizeof(ServerRaidGeneralAction_Struct));
|
auto pack = new ServerPacket(ServerOP_RaidAdd, sizeof(ServerRaidGeneralAction_Struct));
|
||||||
ServerRaidGeneralAction_Struct *rga = (ServerRaidGeneralAction_Struct*)pack->pBuffer;
|
ServerRaidGeneralAction_Struct *rga = (ServerRaidGeneralAction_Struct*)pack->pBuffer;
|
||||||
rga->rid = GetID();
|
rga->rid = GetID();
|
||||||
@ -143,8 +167,10 @@ void Raid::RemoveMember(const char *characterName)
|
|||||||
LearnMembers();
|
LearnMembers();
|
||||||
VerifyRaid();
|
VerifyRaid();
|
||||||
|
|
||||||
if(client)
|
if(client) {
|
||||||
client->SetRaidGrouped(false);
|
client->SetRaidGrouped(false);
|
||||||
|
client->LeaveRaidXTargets(this);
|
||||||
|
}
|
||||||
|
|
||||||
auto pack = new ServerPacket(ServerOP_RaidRemove, sizeof(ServerRaidGeneralAction_Struct));
|
auto pack = new ServerPacket(ServerOP_RaidRemove, sizeof(ServerRaidGeneralAction_Struct));
|
||||||
ServerRaidGeneralAction_Struct *rga = (ServerRaidGeneralAction_Struct*)pack->pBuffer;
|
ServerRaidGeneralAction_Struct *rga = (ServerRaidGeneralAction_Struct*)pack->pBuffer;
|
||||||
@ -1672,3 +1698,11 @@ void Raid::CheckGroupMentor(uint32 group_id, Client *c)
|
|||||||
group_mentor[group_id].mentoree = c;
|
group_mentor[group_id].mentoree = c;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Raid::SetDirtyAutoHaters()
|
||||||
|
{
|
||||||
|
for (int i = 0; i < MAX_RAID_MEMBERS; ++i)
|
||||||
|
if (members[i].member)
|
||||||
|
members[i].member->SetDirtyAutoHaters();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@ -20,6 +20,7 @@
|
|||||||
|
|
||||||
#include "../common/types.h"
|
#include "../common/types.h"
|
||||||
#include "groups.h"
|
#include "groups.h"
|
||||||
|
#include "xtargetautohaters.h"
|
||||||
|
|
||||||
class Client;
|
class Client;
|
||||||
class EQApplicationPacket;
|
class EQApplicationPacket;
|
||||||
@ -230,6 +231,9 @@ public:
|
|||||||
inline int GetMentorPercent(uint32 group_id) { return group_mentor[group_id].mentor_percent; }
|
inline int GetMentorPercent(uint32 group_id) { return group_mentor[group_id].mentor_percent; }
|
||||||
inline Client *GetMentoree(uint32 group_id) { return group_mentor[group_id].mentoree; }
|
inline Client *GetMentoree(uint32 group_id) { return group_mentor[group_id].mentoree; }
|
||||||
|
|
||||||
|
void SetDirtyAutoHaters();
|
||||||
|
inline XTargetAutoHaters *GetXTargetAutoMgr() { return &m_autohatermgr; }
|
||||||
|
|
||||||
RaidMember members[MAX_RAID_MEMBERS];
|
RaidMember members[MAX_RAID_MEMBERS];
|
||||||
char leadername[64];
|
char leadername[64];
|
||||||
protected:
|
protected:
|
||||||
@ -244,6 +248,8 @@ protected:
|
|||||||
GroupLeadershipAA_Struct group_aa[MAX_RAID_GROUPS];
|
GroupLeadershipAA_Struct group_aa[MAX_RAID_GROUPS];
|
||||||
|
|
||||||
GroupMentor group_mentor[MAX_RAID_GROUPS];
|
GroupMentor group_mentor[MAX_RAID_GROUPS];
|
||||||
|
|
||||||
|
XTargetAutoHaters m_autohatermgr;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -897,6 +897,9 @@ void WorldServer::Process() {
|
|||||||
Inviter->CastToClient()->SendGroupLeaderChangePacket(Inviter->GetName());
|
Inviter->CastToClient()->SendGroupLeaderChangePacket(Inviter->GetName());
|
||||||
Inviter->CastToClient()->SendGroupJoinAcknowledge();
|
Inviter->CastToClient()->SendGroupJoinAcknowledge();
|
||||||
}
|
}
|
||||||
|
group->GetXTargetAutoMgr()->merge(*Inviter->CastToClient()->GetXTargetAutoMgr());
|
||||||
|
Inviter->CastToClient()->GetXTargetAutoMgr()->clear();
|
||||||
|
Inviter->CastToClient()->SetXTargetAutoMgr(group->GetXTargetAutoMgr());
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!group)
|
if(!group)
|
||||||
@ -1011,6 +1014,7 @@ void WorldServer::Process() {
|
|||||||
group->SetGroupMentor(mentor_percent, mentoree_name);
|
group->SetGroupMentor(mentor_percent, mentoree_name);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
client->JoinGroupXTargets(group);
|
||||||
}
|
}
|
||||||
else if (client->GetMerc())
|
else if (client->GetMerc())
|
||||||
{
|
{
|
||||||
@ -1109,6 +1113,7 @@ void WorldServer::Process() {
|
|||||||
r->SendRaidRemoveAll(rga->playername);
|
r->SendRaidRemoveAll(rga->playername);
|
||||||
Client *rem = entity_list.GetClientByName(rga->playername);
|
Client *rem = entity_list.GetClientByName(rga->playername);
|
||||||
if(rem){
|
if(rem){
|
||||||
|
rem->LeaveRaidXTargets(r);
|
||||||
r->SendRaidDisband(rem);
|
r->SendRaidDisband(rem);
|
||||||
}
|
}
|
||||||
r->LearnMembers();
|
r->LearnMembers();
|
||||||
|
|||||||
112
zone/xtargetautohaters.cpp
Normal file
112
zone/xtargetautohaters.cpp
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
#include "xtargetautohaters.h"
|
||||||
|
#include "mob.h"
|
||||||
|
#include "client.h"
|
||||||
|
#include "raids.h"
|
||||||
|
#include "groups.h"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
void XTargetAutoHaters::increment_count(Mob *in)
|
||||||
|
{
|
||||||
|
assert(in != nullptr);
|
||||||
|
auto it = std::find_if(m_haters.begin(), m_haters.end(),
|
||||||
|
[&in](const HatersCount &c) { return c.spawn_id == in->GetID(); });
|
||||||
|
|
||||||
|
// we are on the list, we just need to increment the count
|
||||||
|
if (it != m_haters.end()) {
|
||||||
|
it->count++;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// We are not on the list
|
||||||
|
HatersCount c;
|
||||||
|
c.spawn_id = in->GetID();
|
||||||
|
c.count = 1;
|
||||||
|
|
||||||
|
m_haters.push_back(c);
|
||||||
|
// trigger event on owner
|
||||||
|
if (m_client)
|
||||||
|
m_client->SetDirtyAutoHaters();
|
||||||
|
else if (m_group)
|
||||||
|
m_group->SetDirtyAutoHaters();
|
||||||
|
else if (m_raid)
|
||||||
|
m_raid->SetDirtyAutoHaters();
|
||||||
|
}
|
||||||
|
|
||||||
|
void XTargetAutoHaters::decrement_count(Mob *in)
|
||||||
|
{
|
||||||
|
assert(in != nullptr);
|
||||||
|
auto it = std::find_if(m_haters.begin(), m_haters.end(),
|
||||||
|
[&in](const HatersCount &c) { return c.spawn_id == in->GetID(); });
|
||||||
|
|
||||||
|
// we are not on the list ... shouldn't happen
|
||||||
|
if (it == m_haters.end())
|
||||||
|
return;
|
||||||
|
it->count--;
|
||||||
|
if (it->count == 0) {
|
||||||
|
m_haters.erase(it);
|
||||||
|
if (m_client)
|
||||||
|
m_client->SetDirtyAutoHaters();
|
||||||
|
else if (m_group)
|
||||||
|
m_group->SetDirtyAutoHaters();
|
||||||
|
else if (m_raid)
|
||||||
|
m_raid->SetDirtyAutoHaters();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void XTargetAutoHaters::merge(XTargetAutoHaters &other)
|
||||||
|
{
|
||||||
|
bool trigger = false;
|
||||||
|
for (auto &e : other.m_haters) {
|
||||||
|
auto it = std::find_if(m_haters.begin(), m_haters.end(),
|
||||||
|
[&e](const HatersCount &c) { return e.spawn_id == c.spawn_id; });
|
||||||
|
if (it != m_haters.end()) {
|
||||||
|
it->count += e.count;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
m_haters.push_back(e);
|
||||||
|
trigger = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (trigger) {
|
||||||
|
if (m_client)
|
||||||
|
m_client->SetDirtyAutoHaters();
|
||||||
|
else if (m_group)
|
||||||
|
m_group->SetDirtyAutoHaters();
|
||||||
|
else if (m_raid)
|
||||||
|
m_raid->SetDirtyAutoHaters();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// demerge this from other. other belongs to group/raid you just left
|
||||||
|
void XTargetAutoHaters::demerge(XTargetAutoHaters &other)
|
||||||
|
{
|
||||||
|
bool trigger = false;
|
||||||
|
for (auto &e : m_haters) {
|
||||||
|
auto it = std::find_if(other.m_haters.begin(), other.m_haters.end(),
|
||||||
|
[&e](const HatersCount &c) { return e.spawn_id == c.spawn_id; });
|
||||||
|
if (it != other.m_haters.end()) {
|
||||||
|
it->count -= e.count;
|
||||||
|
if (it->count == 0) {
|
||||||
|
trigger = true;
|
||||||
|
other.m_haters.erase(it);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (trigger) {
|
||||||
|
if (other.m_client)
|
||||||
|
other.m_client->SetDirtyAutoHaters();
|
||||||
|
else if (other.m_group)
|
||||||
|
other.m_group->SetDirtyAutoHaters();
|
||||||
|
else if (other.m_raid)
|
||||||
|
other.m_raid->SetDirtyAutoHaters();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool XTargetAutoHaters::contains_mob(int spawn_id)
|
||||||
|
{
|
||||||
|
auto it = std::find_if(m_haters.begin(), m_haters.end(),
|
||||||
|
[spawn_id](const HatersCount &c) { return c.spawn_id == spawn_id; });
|
||||||
|
return it != m_haters.end();
|
||||||
|
}
|
||||||
|
|
||||||
48
zone/xtargetautohaters.h
Normal file
48
zone/xtargetautohaters.h
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
#ifndef XTARGETAUTOHATERS_H
|
||||||
|
#define XTARGETAUTOHATERS_H
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
class Mob;
|
||||||
|
class Client;
|
||||||
|
class Group;
|
||||||
|
class Raid;
|
||||||
|
|
||||||
|
class XTargetAutoHaters {
|
||||||
|
struct HatersCount {
|
||||||
|
int spawn_id;
|
||||||
|
int count;
|
||||||
|
};
|
||||||
|
public:
|
||||||
|
XTargetAutoHaters() : m_client(nullptr), m_group(nullptr), m_raid(nullptr) {}
|
||||||
|
XTargetAutoHaters(Client *co, Group *go, Raid *ro) : m_client(co), m_group(go), m_raid(ro) {}
|
||||||
|
~XTargetAutoHaters() {}
|
||||||
|
|
||||||
|
void merge(XTargetAutoHaters &other);
|
||||||
|
void demerge(XTargetAutoHaters &other);
|
||||||
|
void increment_count(Mob *in);
|
||||||
|
void decrement_count(Mob *in);
|
||||||
|
|
||||||
|
bool contains_mob(int spawn_id);
|
||||||
|
|
||||||
|
inline const std::vector<HatersCount> &get_list() { return m_haters; }
|
||||||
|
inline void SetOwner(Client *c, Group *g, Raid *r) {m_client = c; m_group = g; m_raid = r; }
|
||||||
|
inline void clear() { m_haters.clear(); }
|
||||||
|
inline bool empty() { return m_haters.empty(); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
/* This will contain all of the mobs that are possible to fill in an autohater
|
||||||
|
* slot. This keeps track of ALL MOBS for a client or group or raid
|
||||||
|
* This list needs to be merged when you join group/raid/etc
|
||||||
|
*/
|
||||||
|
std::vector<HatersCount> m_haters;
|
||||||
|
|
||||||
|
// So this is the object that owns us ... only 1 shouldn't be null
|
||||||
|
Client *m_client;
|
||||||
|
Group *m_group;
|
||||||
|
Raid *m_raid;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* !XTARGETAUTOHATERS_H */
|
||||||
|
|
||||||
Loading…
x
Reference in New Issue
Block a user