mirror of
https://github.com/EQEmu/Server.git
synced 2025-12-11 16:51:29 +00:00
Implement aggro meter for RoF2 (RoF wasn't tested)
I didn't test RoF, so it's disabled for now (change AggroMeterAvaliable if you want to test) Group member meters probably buggy ... but do later The "lock target" feature isn't working currently either
This commit is contained in:
parent
9f4604ec3e
commit
08c2f73e37
@ -25,6 +25,9 @@ N(OP_AdventureRequest),
|
||||
N(OP_AdventureStatsReply),
|
||||
N(OP_AdventureStatsRequest),
|
||||
N(OP_AdventureUpdate),
|
||||
N(OP_AggroMeterLockTarget),
|
||||
N(OP_AggroMeterTargetInfo),
|
||||
N(OP_AggroMeterUpdate),
|
||||
N(OP_AltCurrency),
|
||||
N(OP_AltCurrencyMerchantReply),
|
||||
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, 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, 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_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.
|
||||
|
||||
@ -359,6 +359,9 @@ OP_OpenContainer=0x654f
|
||||
OP_Marquee=0x288a
|
||||
OP_Fling=0x6b8e
|
||||
OP_CancelSneakHide=0x265f
|
||||
OP_AggroMeterLockTarget=0x70b7
|
||||
OP_AggroMeterTargetInfo=0x18fe
|
||||
OP_AggroMeterUpdate=0x75aa
|
||||
|
||||
OP_DzQuit=0x5fc8
|
||||
OP_DzListTimers=0x67b9
|
||||
|
||||
@ -360,6 +360,9 @@ OP_ItemRecastDelay=0x15a9
|
||||
OP_ResetAA=0x1669
|
||||
OP_Fling=0x6f80
|
||||
OP_CancelSneakHide=0x0927
|
||||
OP_AggroMeterLockTarget=0x1643
|
||||
OP_AggroMeterTargetInfo=0x16bc
|
||||
OP_AggroMeterUpdate=0x1781
|
||||
|
||||
# Expeditions
|
||||
OP_DzAddPlayer=0x4701
|
||||
|
||||
@ -4,6 +4,7 @@ SET(zone_sources
|
||||
aa.cpp
|
||||
aa_ability.cpp
|
||||
aggro.cpp
|
||||
aggromanager.cpp
|
||||
attack.cpp
|
||||
beacon.cpp
|
||||
bonuses.cpp
|
||||
@ -132,6 +133,7 @@ SET(zone_sources
|
||||
SET(zone_headers
|
||||
aa.h
|
||||
aa_ability.h
|
||||
aggromanager.h
|
||||
basic_functions.h
|
||||
beacon.h
|
||||
bot.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 */
|
||||
165
zone/client.cpp
165
zone/client.cpp
@ -154,6 +154,7 @@ Client::Client(EQStreamInterface* ieqs)
|
||||
afk_toggle_timer(250),
|
||||
helm_toggle_timer(250),
|
||||
light_update_timer(600),
|
||||
aggro_meter_timer(1000),
|
||||
m_Proximity(FLT_MAX, FLT_MAX, FLT_MAX), //arbitrary large number
|
||||
m_ZoneSummonLocation(-2.0f,-2.0f,-2.0f),
|
||||
m_AutoAttackPosition(0.0f, 0.0f, 0.0f, 0.0f),
|
||||
@ -8771,3 +8772,167 @@ void Client::CheckRegionTypeChanges()
|
||||
else if (GetPVP())
|
||||
SetPVP(false, false);
|
||||
}
|
||||
|
||||
void Client::ProcessAggroMeter()
|
||||
{
|
||||
if (!AggroMeterAvailable())
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -49,6 +49,7 @@ namespace EQEmu
|
||||
#include "../common/guilds.h"
|
||||
//#include "../common/item_data.h"
|
||||
#include "xtargetautohaters.h"
|
||||
#include "aggromanager.h"
|
||||
|
||||
#include "common.h"
|
||||
#include "merc.h"
|
||||
@ -1133,6 +1134,11 @@ public:
|
||||
bool GroupFollow(Client* inviter);
|
||||
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();
|
||||
bool CheckCanSpawnMerc(uint32 template_id);
|
||||
bool CheckCanHireMerc(Mob* merchant, uint32 template_id);
|
||||
@ -1470,6 +1476,7 @@ private:
|
||||
Timer afk_toggle_timer;
|
||||
Timer helm_toggle_timer;
|
||||
Timer light_update_timer;
|
||||
Timer aggro_meter_timer;
|
||||
|
||||
glm::vec3 m_Proximity;
|
||||
|
||||
@ -1560,6 +1567,8 @@ private:
|
||||
XTargetAutoHaters m_autohatermgr;
|
||||
XTargetAutoHaters *m_activeautohatermgr;
|
||||
|
||||
AggroMeter m_aggrometer;
|
||||
|
||||
Timer ItemTickTimer;
|
||||
Timer ItemQuestTimer;
|
||||
std::map<std::string,std::string> accountflags;
|
||||
|
||||
@ -120,6 +120,7 @@ void MapOpcodes()
|
||||
ConnectedOpcodes[OP_AdventureMerchantSell] = &Client::Handle_OP_AdventureMerchantSell;
|
||||
ConnectedOpcodes[OP_AdventureRequest] = &Client::Handle_OP_AdventureRequest;
|
||||
ConnectedOpcodes[OP_AdventureStatsRequest] = &Client::Handle_OP_AdventureStatsRequest;
|
||||
ConnectedOpcodes[OP_AggroMeterLockTarget] = &Client::Handle_OP_AggroMeterLockTarget;
|
||||
ConnectedOpcodes[OP_AltCurrencyMerchantRequest] = &Client::Handle_OP_AltCurrencyMerchantRequest;
|
||||
ConnectedOpcodes[OP_AltCurrencyPurchase] = &Client::Handle_OP_AltCurrencyPurchase;
|
||||
ConnectedOpcodes[OP_AltCurrencyReclaim] = &Client::Handle_OP_AltCurrencyReclaim;
|
||||
@ -2400,6 +2401,17 @@ void Client::Handle_OP_AdventureStatsRequest(const EQApplicationPacket *app)
|
||||
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)
|
||||
{
|
||||
VERIFY_PACKET_LENGTH(OP_AltCurrencyMerchantRequest, app, uint32);
|
||||
|
||||
@ -32,6 +32,7 @@
|
||||
void Handle_OP_AdventureMerchantSell(const EQApplicationPacket *app);
|
||||
void Handle_OP_AdventureRequest(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_AltCurrencyPurchase(const EQApplicationPacket *app);
|
||||
void Handle_OP_AltCurrencyReclaim(const EQApplicationPacket *app);
|
||||
|
||||
@ -684,7 +684,8 @@ bool Client::Process() {
|
||||
if (client_state == CLIENT_CONNECTED) {
|
||||
if (m_dirtyautohaters)
|
||||
ProcessXTargetAutoHaters();
|
||||
// aggro meter stuff should live here
|
||||
if (aggro_meter_timer.Check())
|
||||
ProcessAggroMeter();
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
||||
@ -23,6 +23,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
#include "raids.h"
|
||||
|
||||
#include "../common/rulesys.h"
|
||||
#include "../common/data_verification.h"
|
||||
|
||||
#include "hate_list.h"
|
||||
#include "quest_parser_collection.h"
|
||||
@ -277,7 +278,24 @@ int HateList::GetSummonedPetCountOnHateList(Mob *hater) {
|
||||
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
|
||||
if (!zone->IsLoaded())
|
||||
@ -310,6 +328,11 @@ Mob *HateList::GetEntWithMostHateOnList(Mob *center)
|
||||
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());
|
||||
if (center->IsNPC() && center->CastToNPC()->IsUnderwaterOnly() && zone->HasWaterMap()) {
|
||||
if (!zone->watermap->InLiquid(hateEntryPosition)) {
|
||||
@ -436,6 +459,11 @@ Mob *HateList::GetEntWithMostHateOnList(Mob *center)
|
||||
while (iterator != list.end())
|
||||
{
|
||||
struct_HateList *cur = (*iterator);
|
||||
if (cur->entity_on_hatelist == skip) {
|
||||
++iterator;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (center->IsNPC() && center->CastToNPC()->IsUnderwaterOnly() && zone->HasWaterMap()) {
|
||||
if(!zone->watermap->InLiquid(glm::vec3(cur->entity_on_hatelist->GetPosition()))) {
|
||||
skipped_count++;
|
||||
|
||||
@ -41,9 +41,9 @@ public:
|
||||
|
||||
Mob *GetClosestEntOnHateList(Mob *hater);
|
||||
Mob *GetDamageTopOnHateList(Mob *hater);
|
||||
Mob *GetEntWithMostHateOnList(Mob *center);
|
||||
Mob *GetEntWithMostHateOnList(Mob *center, Mob *skip = nullptr);
|
||||
Mob *GetRandomEntOnHateList();
|
||||
Mob* GetEntWithMostHateOnList();
|
||||
Mob *GetEntWithMostHateOnList();
|
||||
|
||||
bool IsEntOnHateList(Mob *mob);
|
||||
bool IsHateListEmpty();
|
||||
@ -51,6 +51,7 @@ public:
|
||||
|
||||
int AreaRampage(Mob *caster, Mob *target, int count, ExtraAttackOptions *opts);
|
||||
int GetSummonedPetCountOnHateList(Mob *hater);
|
||||
int GetHateRatio(Mob *top, Mob *other);
|
||||
|
||||
int32 GetEntHateAmount(Mob *ent, bool in_damage = false);
|
||||
|
||||
@ -73,4 +74,5 @@ private:
|
||||
Mob *hate_owner;
|
||||
};
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
@ -539,7 +539,9 @@ public:
|
||||
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 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* GetSecondaryHate(Mob *skip) { return hate_list.GetEntWithMostHateOnList(this, skip); }
|
||||
Mob* GetHateDamageTop(Mob* other) { return hate_list.GetDamageTopOnHateList(other);}
|
||||
Mob* GetHateRandom() { return hate_list.GetRandomEntOnHateList();}
|
||||
Mob* GetHateMost() { return hate_list.GetEntWithMostHateOnList();}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user