mirror of
https://github.com/EQEmu/Server.git
synced 2026-04-15 21:12:25 +00:00
Scanning optimization work from over a year ago from EZ - cleaned up a bit
This commit is contained in:
parent
07fd803d41
commit
8cb51eb253
@ -107,6 +107,9 @@ namespace Logs {
|
|||||||
Emergency,
|
Emergency,
|
||||||
Alert,
|
Alert,
|
||||||
Notice,
|
Notice,
|
||||||
|
AIScanClose,
|
||||||
|
AIYellForHelp,
|
||||||
|
AICastBeneficialClose,
|
||||||
MaxCategoryID /* Don't Remove this */
|
MaxCategoryID /* Don't Remove this */
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -172,7 +175,10 @@ namespace Logs {
|
|||||||
"Critical",
|
"Critical",
|
||||||
"Emergency",
|
"Emergency",
|
||||||
"Alert",
|
"Alert",
|
||||||
"Notice"
|
"Notice",
|
||||||
|
"AI Scan Close",
|
||||||
|
"AI Yell For Help",
|
||||||
|
"AI Cast Beneficial Close",
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -491,6 +491,36 @@
|
|||||||
OutF(LogSys, Logs::Detail, Logs::Status, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\
|
OutF(LogSys, Logs::Detail, Logs::Status, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
|
#define LogAIScanClose(message, ...) do {\
|
||||||
|
if (LogSys.log_settings[Logs::AIScanClose].is_category_enabled == 1)\
|
||||||
|
OutF(LogSys, Logs::General, Logs::AIScanClose, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define LogAIScanCloseDetail(message, ...) do {\
|
||||||
|
if (LogSys.log_settings[Logs::AIScanClose].is_category_enabled == 1)\
|
||||||
|
OutF(LogSys, Logs::Detail, Logs::AIScanClose, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define LogAIYellForHelp(message, ...) do {\
|
||||||
|
if (LogSys.log_settings[Logs::AIYellForHelp].is_category_enabled == 1)\
|
||||||
|
OutF(LogSys, Logs::General, Logs::AIYellForHelp, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define LogAIYellForHelpDetail(message, ...) do {\
|
||||||
|
if (LogSys.log_settings[Logs::AIYellForHelp].is_category_enabled == 1)\
|
||||||
|
OutF(LogSys, Logs::Detail, Logs::AIYellForHelp, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define LogAICastBeneficialClose(message, ...) do {\
|
||||||
|
if (LogSys.log_settings[Logs::AICastBeneficialClose].is_category_enabled == 1)\
|
||||||
|
OutF(LogSys, Logs::General, Logs::AICastBeneficialClose, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define LogAICastBeneficialCloseDetail(message, ...) do {\
|
||||||
|
if (LogSys.log_settings[Logs::AICastBeneficialClose].is_category_enabled == 1)\
|
||||||
|
OutF(LogSys, Logs::Detail, Logs::AICastBeneficialClose, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\
|
||||||
|
} while (0)
|
||||||
|
|
||||||
#define Log(debug_level, log_category, message, ...) do {\
|
#define Log(debug_level, log_category, message, ...) do {\
|
||||||
if (LogSys.log_settings[log_category].is_category_enabled == 1)\
|
if (LogSys.log_settings[log_category].is_category_enabled == 1)\
|
||||||
LogSys.Out(debug_level, log_category, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\
|
LogSys.Out(debug_level, log_category, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\
|
||||||
|
|||||||
@ -568,6 +568,7 @@ RULE_INT(Range, ClientPositionUpdates, 300, "")
|
|||||||
RULE_INT(Range, ClientForceSpawnUpdateRange, 1000, "")
|
RULE_INT(Range, ClientForceSpawnUpdateRange, 1000, "")
|
||||||
RULE_INT(Range, CriticalDamage, 80, "")
|
RULE_INT(Range, CriticalDamage, 80, "")
|
||||||
RULE_INT(Range, ClientNPCScan, 300, "")
|
RULE_INT(Range, ClientNPCScan, 300, "")
|
||||||
|
RULE_INT(Range, MobCloseScanDistance, 300, "")
|
||||||
RULE_CATEGORY_END()
|
RULE_CATEGORY_END()
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -189,10 +189,10 @@ namespace WorldserverCommandHandler {
|
|||||||
|
|
||||||
Json::Value schema;
|
Json::Value schema;
|
||||||
|
|
||||||
schema["server_tables"] = server_tables_json;
|
|
||||||
schema["player_tables"] = player_tables_json;
|
|
||||||
schema["content_tables"] = content_tables_json;
|
schema["content_tables"] = content_tables_json;
|
||||||
schema["login_tables"] = login_tables_json;
|
schema["login_tables"] = login_tables_json;
|
||||||
|
schema["player_tables"] = player_tables_json;
|
||||||
|
schema["server_tables"] = server_tables_json;
|
||||||
schema["state_tables"] = state_tables_json;
|
schema["state_tables"] = state_tables_json;
|
||||||
schema["version_tables"] = version_tables_json;
|
schema["version_tables"] = version_tables_json;
|
||||||
|
|
||||||
|
|||||||
110
zone/aggro.cpp
110
zone/aggro.cpp
@ -36,19 +36,6 @@
|
|||||||
extern Zone* zone;
|
extern Zone* zone;
|
||||||
//#define LOSDEBUG 6
|
//#define LOSDEBUG 6
|
||||||
|
|
||||||
//look around a client for things which might aggro the client.
|
|
||||||
void EntityList::CheckClientAggro(Client *around)
|
|
||||||
{
|
|
||||||
for (auto it = mob_list.begin(); it != mob_list.end(); ++it) {
|
|
||||||
Mob *mob = it->second;
|
|
||||||
if (mob->IsClient()) //also ensures that mob != around
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (mob->CheckWillAggro(around) && !mob->CheckAggro(around))
|
|
||||||
mob->AddToHateList(around, 25);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void EntityList::DescribeAggro(Client *towho, NPC *from_who, float d, bool verbose) {
|
void EntityList::DescribeAggro(Client *towho, NPC *from_who, float d, bool verbose) {
|
||||||
float d2 = d*d;
|
float d2 = d*d;
|
||||||
|
|
||||||
@ -402,22 +389,6 @@ bool Mob::CheckWillAggro(Mob *mob) {
|
|||||||
return(false);
|
return(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
Mob* EntityList::AICheckNPCtoNPCAggro(Mob* sender, float iAggroRange, float iAssistRange) {
|
|
||||||
if (!sender || !sender->IsNPC())
|
|
||||||
return(nullptr);
|
|
||||||
|
|
||||||
auto it = npc_list.begin();
|
|
||||||
while (it != npc_list.end()) {
|
|
||||||
Mob *mob = it->second;
|
|
||||||
|
|
||||||
if (sender->CheckWillAggro(mob))
|
|
||||||
return mob;
|
|
||||||
++it;
|
|
||||||
}
|
|
||||||
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
int EntityList::GetHatedCount(Mob *attacker, Mob *exclude, bool inc_gray_con)
|
int EntityList::GetHatedCount(Mob *attacker, Mob *exclude, bool inc_gray_con)
|
||||||
{
|
{
|
||||||
// Return a list of how many non-feared, non-mezzed, non-green mobs, within aggro range, hate *attacker
|
// Return a list of how many non-feared, non-mezzed, non-green mobs, within aggro range, hate *attacker
|
||||||
@ -462,82 +433,11 @@ int EntityList::GetHatedCount(Mob *attacker, Mob *exclude, bool inc_gray_con)
|
|||||||
return Count;
|
return Count;
|
||||||
}
|
}
|
||||||
|
|
||||||
void EntityList::AIYellForHelp(Mob* sender, Mob* attacker) {
|
/**
|
||||||
if(!sender || !attacker)
|
* @param target
|
||||||
return;
|
* @param isSpellAttack
|
||||||
if (sender->GetPrimaryFaction() == 0 )
|
* @return
|
||||||
return; // well, if we dont have a faction set, we're gonna be indiff to everybody
|
*/
|
||||||
|
|
||||||
if (sender->HasAssistAggro())
|
|
||||||
return;
|
|
||||||
|
|
||||||
for (auto it = npc_list.begin(); it != npc_list.end(); ++it) {
|
|
||||||
NPC *mob = it->second;
|
|
||||||
if (!mob)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (mob->CheckAggro(attacker))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (sender->NPCAssistCap() >= RuleI(Combat, NPCAssistCap))
|
|
||||||
break;
|
|
||||||
|
|
||||||
float r = mob->GetAssistRange();
|
|
||||||
r = r * r;
|
|
||||||
|
|
||||||
if (
|
|
||||||
mob != sender
|
|
||||||
&& mob != attacker
|
|
||||||
// && !mob->IsCorpse()
|
|
||||||
// && mob->IsAIControlled()
|
|
||||||
&& mob->GetPrimaryFaction() != 0
|
|
||||||
&& DistanceSquared(mob->GetPosition(), sender->GetPosition()) <= r
|
|
||||||
&& !mob->IsEngaged()
|
|
||||||
&& ((!mob->IsPet()) || (mob->IsPet() && mob->GetOwner() && !mob->GetOwner()->IsClient()))
|
|
||||||
// If we're a pet we don't react to any calls for help if our owner is a client
|
|
||||||
)
|
|
||||||
{
|
|
||||||
//if they are in range, make sure we are not green...
|
|
||||||
//then jump in if they are our friend
|
|
||||||
if(mob->GetLevel() >= 50 || attacker->GetLevelCon(mob->GetLevel()) != CON_GRAY)
|
|
||||||
{
|
|
||||||
bool useprimfaction = false;
|
|
||||||
if(mob->GetPrimaryFaction() == sender->CastToNPC()->GetPrimaryFaction())
|
|
||||||
{
|
|
||||||
const NPCFactionList *cf = database.GetNPCFactionEntry(mob->GetNPCFactionID());
|
|
||||||
if(cf){
|
|
||||||
if(cf->assistprimaryfaction != 0)
|
|
||||||
useprimfaction = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(useprimfaction || sender->GetReverseFactionCon(mob) <= FACTION_AMIABLE )
|
|
||||||
{
|
|
||||||
//attacking someone on same faction, or a friend
|
|
||||||
//Father Nitwit: make sure we can see them.
|
|
||||||
if(mob->CheckLosFN(sender)) {
|
|
||||||
#if (EQDEBUG>=5)
|
|
||||||
LogDebug("AIYellForHelp(\"[{}]\",\"[{}]\") [{}] attacking [{}] Dist [{}] Z [{}]",
|
|
||||||
sender->GetName(), attacker->GetName(), mob->GetName(),
|
|
||||||
attacker->GetName(), DistanceSquared(mob->GetPosition(),
|
|
||||||
sender->GetPosition()), std::abs(sender->GetZ()+mob->GetZ()));
|
|
||||||
#endif
|
|
||||||
mob->AddToHateList(attacker, 25, 0, false);
|
|
||||||
sender->AddAssistCap();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
returns false if attack should not be allowed
|
|
||||||
I try to list every type of conflict that's possible here, so it's easy
|
|
||||||
to see how the decision is made. Yea, it could be condensed and made
|
|
||||||
faster, but I'm doing it this way to make it readable and easy to modify
|
|
||||||
*/
|
|
||||||
|
|
||||||
bool Mob::IsAttackAllowed(Mob *target, bool isSpellAttack)
|
bool Mob::IsAttackAllowed(Mob *target, bool isSpellAttack)
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|||||||
@ -226,7 +226,6 @@ public:
|
|||||||
Client(EQStreamInterface * ieqs);
|
Client(EQStreamInterface * ieqs);
|
||||||
~Client();
|
~Client();
|
||||||
|
|
||||||
std::unordered_map<Mob *, float> close_mobs;
|
|
||||||
bool is_client_moving;
|
bool is_client_moving;
|
||||||
|
|
||||||
void SetDisplayMobInfoWindow(bool display_mob_info_window);
|
void SetDisplayMobInfoWindow(bool display_mob_info_window);
|
||||||
|
|||||||
@ -251,17 +251,21 @@ bool Client::Process() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Build a close range list of NPC's */
|
/**
|
||||||
|
* Scan close range mobs
|
||||||
|
*
|
||||||
|
* Used in aggro checks
|
||||||
|
*/
|
||||||
if (npc_close_scan_timer.Check()) {
|
if (npc_close_scan_timer.Check()) {
|
||||||
close_mobs.clear();
|
close_mobs.clear();
|
||||||
//Force spawn updates when traveled far
|
|
||||||
bool force_spawn_updates = false;
|
|
||||||
float client_update_range = (RuleI(Range, ClientForceSpawnUpdateRange) * RuleI(Range, ClientForceSpawnUpdateRange));
|
|
||||||
float scan_range = (RuleI(Range, ClientNPCScan) * RuleI(Range, ClientNPCScan));
|
float scan_range = (RuleI(Range, ClientNPCScan) * RuleI(Range, ClientNPCScan));
|
||||||
auto &mob_list = entity_list.GetMobList();
|
auto &mob_list = entity_list.GetMobList();
|
||||||
for (auto itr = mob_list.begin(); itr != mob_list.end(); ++itr) {
|
|
||||||
Mob* mob = itr->second;
|
for (auto itr : mob_list) {
|
||||||
|
Mob *mob = itr.second;
|
||||||
float distance = DistanceSquared(m_Position, mob->GetPosition());
|
float distance = DistanceSquared(m_Position, mob->GetPosition());
|
||||||
|
|
||||||
if (mob->IsNPC()) {
|
if (mob->IsNPC()) {
|
||||||
if (distance <= scan_range) {
|
if (distance <= scan_range) {
|
||||||
close_mobs.insert(std::pair<Mob *, float>(mob, distance));
|
close_mobs.insert(std::pair<Mob *, float>(mob, distance));
|
||||||
|
|||||||
@ -2494,7 +2494,7 @@ bool EntityList::RemoveMob(uint16 delete_id)
|
|||||||
auto it = mob_list.find(delete_id);
|
auto it = mob_list.find(delete_id);
|
||||||
if (it != mob_list.end()) {
|
if (it != mob_list.end()) {
|
||||||
|
|
||||||
RemoveMobFromClientCloseLists(it->second);
|
RemoveMobFromCloseLists(it->second);
|
||||||
|
|
||||||
if (npc_list.count(delete_id))
|
if (npc_list.count(delete_id))
|
||||||
entity_list.RemoveNPC(delete_id);
|
entity_list.RemoveNPC(delete_id);
|
||||||
@ -2512,17 +2512,19 @@ bool EntityList::RemoveMob(uint16 delete_id)
|
|||||||
// This is for if the ID is deleted for some reason
|
// This is for if the ID is deleted for some reason
|
||||||
bool EntityList::RemoveMob(Mob *delete_mob)
|
bool EntityList::RemoveMob(Mob *delete_mob)
|
||||||
{
|
{
|
||||||
if (delete_mob == 0)
|
if (delete_mob == 0) {
|
||||||
return true;
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
auto it = mob_list.begin();
|
auto it = mob_list.begin();
|
||||||
while (it != mob_list.end()) {
|
while (it != mob_list.end()) {
|
||||||
if (it->second == delete_mob) {
|
if (it->second == delete_mob) {
|
||||||
RemoveMobFromClientCloseLists(it->second);
|
RemoveMobFromCloseLists(it->second);
|
||||||
|
|
||||||
safe_delete(it->second);
|
safe_delete(it->second);
|
||||||
if (!corpse_list.count(it->first))
|
if (!corpse_list.count(it->first)) {
|
||||||
free_ids.push(it->first);
|
free_ids.push(it->first);
|
||||||
|
}
|
||||||
mob_list.erase(it);
|
mob_list.erase(it);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -2539,28 +2541,62 @@ bool EntityList::RemoveNPC(uint16 delete_id)
|
|||||||
// make sure its proximity is removed
|
// make sure its proximity is removed
|
||||||
RemoveProximity(delete_id);
|
RemoveProximity(delete_id);
|
||||||
// remove from client close lists
|
// remove from client close lists
|
||||||
RemoveMobFromClientCloseLists(npc->CastToMob());
|
RemoveMobFromCloseLists(npc->CastToMob());
|
||||||
// remove from the list
|
// remove from the list
|
||||||
npc_list.erase(it);
|
npc_list.erase(it);
|
||||||
|
|
||||||
// remove from limit list if needed
|
// remove from limit list if needed
|
||||||
if (npc_limit_list.count(delete_id))
|
if (npc_limit_list.count(delete_id)) {
|
||||||
npc_limit_list.erase(delete_id);
|
npc_limit_list.erase(delete_id);
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool EntityList::RemoveMobFromClientCloseLists(Mob *mob)
|
/**
|
||||||
|
* @param mob
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
bool EntityList::RemoveMobFromCloseLists(Mob *mob)
|
||||||
{
|
{
|
||||||
auto it = client_list.begin();
|
auto it = mob_list.begin();
|
||||||
while (it != client_list.end()) {
|
while (it != mob_list.end()) {
|
||||||
it->second->close_mobs.erase(mob);
|
it->second->close_mobs.erase(mob);
|
||||||
++it;
|
++it;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param close_mobs
|
||||||
|
* @param scanning_mob
|
||||||
|
*/
|
||||||
|
void EntityList::ScanCloseMobs(std::unordered_map<Mob *, float> &close_mobs, Mob *scanning_mob)
|
||||||
|
{
|
||||||
|
float scan_range = RuleI(Range, MobCloseScanDistance) * RuleI(Range, MobCloseScanDistance);
|
||||||
|
int list_count = 0;
|
||||||
|
|
||||||
|
close_mobs.clear();
|
||||||
|
|
||||||
|
auto it = mob_list.begin();
|
||||||
|
while (it != mob_list.end()) {
|
||||||
|
float distance = DistanceSquared(scanning_mob->GetPosition(), it->second->GetPosition());
|
||||||
|
if (distance <= scan_range) {
|
||||||
|
close_mobs.insert(std::pair<Mob *, float>(it->second, distance));
|
||||||
|
list_count++;
|
||||||
|
}
|
||||||
|
else if (it->second->GetAggroRange() >= scan_range) {
|
||||||
|
close_mobs.insert(std::pair<Mob *, float>(it->second, distance));
|
||||||
|
list_count++;
|
||||||
|
}
|
||||||
|
++it;
|
||||||
|
}
|
||||||
|
|
||||||
|
LogAIScanClose("Close List Size [{}] for mob [{}]", list_count, scanning_mob->GetCleanName());
|
||||||
|
}
|
||||||
|
|
||||||
bool EntityList::RemoveMerc(uint16 delete_id)
|
bool EntityList::RemoveMerc(uint16 delete_id)
|
||||||
{
|
{
|
||||||
auto it = merc_list.find(delete_id);
|
auto it = merc_list.find(delete_id);
|
||||||
|
|||||||
@ -284,7 +284,7 @@ public:
|
|||||||
bool RemoveTrap(uint16 delete_id);
|
bool RemoveTrap(uint16 delete_id);
|
||||||
bool RemoveObject(uint16 delete_id);
|
bool RemoveObject(uint16 delete_id);
|
||||||
bool RemoveProximity(uint16 delete_npc_id);
|
bool RemoveProximity(uint16 delete_npc_id);
|
||||||
bool RemoveMobFromClientCloseLists(Mob *mob);
|
bool RemoveMobFromCloseLists(Mob *mob);
|
||||||
void RemoveAllMobs();
|
void RemoveAllMobs();
|
||||||
void RemoveAllClients();
|
void RemoveAllClients();
|
||||||
void RemoveAllNPCs();
|
void RemoveAllNPCs();
|
||||||
@ -443,11 +443,7 @@ public:
|
|||||||
bool LimitCheckBoth(uint32 npc_type, uint32 spawngroup_id, int group_count, int type_count);
|
bool LimitCheckBoth(uint32 npc_type, uint32 spawngroup_id, int group_count, int type_count);
|
||||||
bool LimitCheckName(const char* npc_name);
|
bool LimitCheckName(const char* npc_name);
|
||||||
|
|
||||||
void CheckClientAggro(Client *around);
|
|
||||||
Mob* AICheckNPCtoNPCAggro(Mob* sender, float iAggroRange, float iAssistRange);
|
|
||||||
int GetHatedCount(Mob *attacker, Mob *exclude, bool inc_gray_con);
|
int GetHatedCount(Mob *attacker, Mob *exclude, bool inc_gray_con);
|
||||||
void AIYellForHelp(Mob* sender, Mob* attacker);
|
|
||||||
bool AICheckCloseBeneficialSpells(NPC* caster, uint8 iChance, float iRange, uint32 iSpellTypes);
|
|
||||||
bool Merc_AICheckCloseBeneficialSpells(Merc* caster, uint8 iChance, float iRange, uint32 iSpellTypes);
|
bool Merc_AICheckCloseBeneficialSpells(Merc* caster, uint8 iChance, float iRange, uint32 iSpellTypes);
|
||||||
Mob* GetTargetForMez(Mob* caster);
|
Mob* GetTargetForMez(Mob* caster);
|
||||||
uint32 CheckNPCsClose(Mob *center);
|
uint32 CheckNPCsClose(Mob *center);
|
||||||
@ -501,6 +497,7 @@ public:
|
|||||||
void RefreshAutoXTargets(Client *c);
|
void RefreshAutoXTargets(Client *c);
|
||||||
void RefreshClientXTargets(Client *c);
|
void RefreshClientXTargets(Client *c);
|
||||||
void SendAlternateAdvancementStats();
|
void SendAlternateAdvancementStats();
|
||||||
|
void ScanCloseMobs(std::unordered_map<Mob *, float> &close_mobs, Mob *scanning_mob);
|
||||||
|
|
||||||
void GetTrapInfo(Client* client);
|
void GetTrapInfo(Client* client);
|
||||||
bool IsTrapGroupSpawned(uint32 trap_id, uint8 group);
|
bool IsTrapGroupSpawned(uint32 trap_id, uint8 group);
|
||||||
|
|||||||
14
zone/mob.cpp
14
zone/mob.cpp
@ -116,7 +116,9 @@ Mob::Mob(
|
|||||||
m_specialattacks(eSpecialAttacks::None),
|
m_specialattacks(eSpecialAttacks::None),
|
||||||
attack_anim_timer(1000),
|
attack_anim_timer(1000),
|
||||||
position_update_melee_push_timer(500),
|
position_update_melee_push_timer(500),
|
||||||
hate_list_cleanup_timer(6000)
|
hate_list_cleanup_timer(6000),
|
||||||
|
mob_scan_close(6000),
|
||||||
|
mob_check_moving_timer(1000)
|
||||||
{
|
{
|
||||||
mMovementManager = &MobMovementManager::Get();
|
mMovementManager = &MobMovementManager::Get();
|
||||||
mMovementManager->AddMob(this);
|
mMovementManager->AddMob(this);
|
||||||
@ -524,6 +526,16 @@ uint32 Mob::GetAppearanceValue(EmuAppearance iAppearance) {
|
|||||||
return(ANIM_STAND);
|
return(ANIM_STAND);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Mob::GetCloseMobList(std::list<std::pair<Mob *, float>> &m_list)
|
||||||
|
{
|
||||||
|
m_list.clear();
|
||||||
|
auto it = close_mobs.begin();
|
||||||
|
while (it != close_mobs.end()) {
|
||||||
|
m_list.push_back(std::make_pair(it->first, it->second));
|
||||||
|
++it;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Mob::SetInvisible(uint8 state)
|
void Mob::SetInvisible(uint8 state)
|
||||||
{
|
{
|
||||||
invisible = state;
|
invisible = state;
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
|
||||||
/* EQEMu: Everquest Server Emulator
|
/* EQEMu: Everquest Server Emulator
|
||||||
Copyright (C) 2001-2016 EQEMu Development Team (http://eqemu.org)
|
Copyright (C) 2001-2016 EQEMu Development Team (http://eqemu.org)
|
||||||
|
|
||||||
@ -169,6 +170,12 @@ public:
|
|||||||
|
|
||||||
void DisplayInfo(Mob *mob);
|
void DisplayInfo(Mob *mob);
|
||||||
|
|
||||||
|
std::unordered_map<Mob *, float> close_mobs;
|
||||||
|
Timer mob_scan_close;
|
||||||
|
Timer mob_check_moving_timer;
|
||||||
|
|
||||||
|
void GetCloseMobList(std::list<std::pair<Mob *, float>> &m_list);
|
||||||
|
|
||||||
//Somewhat sorted: needs documenting!
|
//Somewhat sorted: needs documenting!
|
||||||
|
|
||||||
//Attack
|
//Attack
|
||||||
@ -968,7 +975,7 @@ public:
|
|||||||
void SetEntityVariable(const char *id, const char *m_var);
|
void SetEntityVariable(const char *id, const char *m_var);
|
||||||
bool EntityVariableExists(const char *id);
|
bool EntityVariableExists(const char *id);
|
||||||
|
|
||||||
void AI_Event_Engaged(Mob* attacker, bool iYellForHelp = true);
|
void AI_Event_Engaged(Mob* attacker, bool yell_for_help = true);
|
||||||
void AI_Event_NoLongerEngaged();
|
void AI_Event_NoLongerEngaged();
|
||||||
|
|
||||||
FACTION_VALUE GetSpecialFactionCon(Mob* iOther);
|
FACTION_VALUE GetSpecialFactionCon(Mob* iOther);
|
||||||
|
|||||||
132
zone/mob_ai.cpp
132
zone/mob_ai.cpp
@ -378,59 +378,6 @@ bool NPC::AIDoSpellCast(uint8 i, Mob* tar, int32 mana_cost, uint32* oDontDoAgain
|
|||||||
return CastSpell(AIspells[i].spellid, tar->GetID(), EQEmu::spells::CastingSlot::Gem2, AIspells[i].manacost == -2 ? 0 : -1, mana_cost, oDontDoAgainBefore, -1, -1, 0, &(AIspells[i].resist_adjust));
|
return CastSpell(AIspells[i].spellid, tar->GetID(), EQEmu::spells::CastingSlot::Gem2, AIspells[i].manacost == -2 ? 0 : -1, mana_cost, oDontDoAgainBefore, -1, -1, 0, &(AIspells[i].resist_adjust));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool EntityList::AICheckCloseBeneficialSpells(NPC* caster, uint8 iChance, float iRange, uint32 iSpellTypes) {
|
|
||||||
if((iSpellTypes & SPELL_TYPES_DETRIMENTAL) != 0) {
|
|
||||||
//according to live, you can buff and heal through walls...
|
|
||||||
//now with PCs, this only applies if you can TARGET the target, but
|
|
||||||
// according to Rogean, Live NPCs will just cast through walls/floors, no problem..
|
|
||||||
//
|
|
||||||
// This check was put in to address an idle-mob CPU issue
|
|
||||||
LogError("Error: detrimental spells requested from AICheckCloseBeneficialSpells!!");
|
|
||||||
return(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!caster)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if(caster->AI_HasSpells() == false)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if(caster->GetSpecialAbility(NPC_NO_BUFFHEAL_FRIENDS))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (iChance < 100) {
|
|
||||||
uint8 tmp = zone->random.Int(0, 99);
|
|
||||||
if (tmp >= iChance)
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (caster->GetPrimaryFaction() == 0 )
|
|
||||||
return(false); // well, if we dont have a faction set, we're gonna be indiff to everybody
|
|
||||||
|
|
||||||
float iRange2 = iRange*iRange;
|
|
||||||
|
|
||||||
//Only iterate through NPCs
|
|
||||||
for (auto it = npc_list.begin(); it != npc_list.end(); ++it) {
|
|
||||||
NPC* mob = it->second;
|
|
||||||
|
|
||||||
if (mob->GetReverseFactionCon(caster) >= FACTION_KINDLY) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (DistanceSquared(caster->GetPosition(), mob->GetPosition()) > iRange2) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((iSpellTypes & SpellType_Buff) && !RuleB(NPC, BuffFriends)) {
|
|
||||||
if (mob != caster)
|
|
||||||
iSpellTypes = SpellType_Heal;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (caster->AICastSpell(mob, 100, iSpellTypes))
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Mob::AI_Init()
|
void Mob::AI_Init()
|
||||||
{
|
{
|
||||||
pAIControlled = false;
|
pAIControlled = false;
|
||||||
@ -1415,12 +1362,20 @@ void Mob::AI_Process() {
|
|||||||
}
|
}
|
||||||
else if (zone->CanDoCombat() && CastToNPC()->WillAggroNPCs() && AI_scan_area_timer->Check()) {
|
else if (zone->CanDoCombat() && CastToNPC()->WillAggroNPCs() && AI_scan_area_timer->Check()) {
|
||||||
|
|
||||||
/*
|
/**
|
||||||
* NPC to NPC aggro checking, npc needs npc_aggro flag
|
* NPC to NPC aggro (npc_aggro flag set)
|
||||||
*/
|
*/
|
||||||
Mob *temp_target = entity_list.AICheckNPCtoNPCAggro(this, GetAggroRange(), GetAssistRange());
|
for (auto &close_mob : close_mobs) {
|
||||||
if (temp_target) {
|
Mob *mob = close_mob.first;
|
||||||
AddToHateList(temp_target);
|
float distance = close_mob.second;
|
||||||
|
|
||||||
|
if (mob->IsClient()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this->CheckWillAggro(mob)) {
|
||||||
|
this->AddToHateList(mob);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
AI_scan_area_timer->Disable();
|
AI_scan_area_timer->Disable();
|
||||||
@ -1877,47 +1832,46 @@ void NPC::AI_SetupNextWaypoint() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Note: Mob that caused this may not get added to the hate list until after this function call completes
|
/**
|
||||||
void Mob::AI_Event_Engaged(Mob* attacker, bool iYellForHelp) {
|
* @param attacker
|
||||||
if (!IsAIControlled())
|
* @param yell_for_help
|
||||||
|
*/
|
||||||
|
void Mob::AI_Event_Engaged(Mob *attacker, bool yell_for_help)
|
||||||
|
{
|
||||||
|
if (!IsAIControlled()) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
SetAppearance(eaStanding);
|
SetAppearance(eaStanding);
|
||||||
|
|
||||||
/*
|
if (IsNPC()) {
|
||||||
Kick off auto cast timer
|
CastToNPC()->AIautocastspell_timer->Start(300, false);
|
||||||
*/
|
|
||||||
if (this->IsNPC())
|
|
||||||
this->CastToNPC()->AIautocastspell_timer->Start(300, false);
|
|
||||||
|
|
||||||
if (iYellForHelp) {
|
if (yell_for_help) {
|
||||||
if(IsPet()) {
|
if (IsPet()) {
|
||||||
GetOwner()->AI_Event_Engaged(attacker, iYellForHelp);
|
GetOwner()->AI_Event_Engaged(attacker, yell_for_help);
|
||||||
} else if (!HasAssistAggro() && NPCAssistCap() < RuleI(Combat, NPCAssistCap)) {
|
}
|
||||||
entity_list.AIYellForHelp(this, attacker);
|
else if (!HasAssistAggro() && NPCAssistCap() < RuleI(Combat, NPCAssistCap)) {
|
||||||
if (NPCAssistCap() > 0 && !assist_cap_timer.Enabled())
|
CastToNPC()->AIYellForHelp(this, attacker);
|
||||||
assist_cap_timer.Start(RuleI(Combat, NPCAssistCapTimer));
|
if (NPCAssistCap() > 0 && !assist_cap_timer.Enabled()) {
|
||||||
|
assist_cap_timer.Start(RuleI(Combat, NPCAssistCapTimer));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if(IsNPC())
|
if (CastToNPC()->GetGrid() > 0) {
|
||||||
{
|
|
||||||
if(CastToNPC()->GetGrid() > 0)
|
|
||||||
{
|
|
||||||
DistractedFromGrid = true;
|
DistractedFromGrid = true;
|
||||||
}
|
}
|
||||||
if(attacker && !attacker->IsCorpse())
|
if (attacker && !attacker->IsCorpse()) {
|
||||||
{
|
|
||||||
//Because sometimes the AIYellForHelp triggers another engaged and then immediately a not engaged
|
//Because sometimes the AIYellForHelp triggers another engaged and then immediately a not engaged
|
||||||
//if the target dies before it goes off
|
//if the target dies before it goes off
|
||||||
if(attacker->GetHP() > 0)
|
if (attacker->GetHP() > 0) {
|
||||||
{
|
if (!CastToNPC()->GetCombatEvent() && GetHP() > 0) {
|
||||||
if(!CastToNPC()->GetCombatEvent() && GetHP() > 0)
|
|
||||||
{
|
|
||||||
parse->EventNPC(EVENT_COMBAT, CastToNPC(), attacker, "1", 0);
|
parse->EventNPC(EVENT_COMBAT, CastToNPC(), attacker, "1", 0);
|
||||||
uint16 emoteid = GetEmoteID();
|
uint16 emoteid = GetEmoteID();
|
||||||
if(emoteid != 0)
|
if (emoteid != 0) {
|
||||||
CastToNPC()->DoNPCEmote(ENTERCOMBAT,emoteid);
|
CastToNPC()->DoNPCEmote(ENTERCOMBAT, emoteid);
|
||||||
|
}
|
||||||
CastToNPC()->SetCombatEvent(true);
|
CastToNPC()->SetCombatEvent(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1996,7 +1950,7 @@ bool NPC::AI_EngagedCastCheck() {
|
|||||||
// try casting a heal or gate
|
// try casting a heal or gate
|
||||||
if (!AICastSpell(this, AISpellVar.engaged_beneficial_self_chance, SpellType_Heal | SpellType_Escape | SpellType_InCombatBuff)) {
|
if (!AICastSpell(this, AISpellVar.engaged_beneficial_self_chance, SpellType_Heal | SpellType_Escape | SpellType_InCombatBuff)) {
|
||||||
// try casting a heal on nearby
|
// try casting a heal on nearby
|
||||||
if (!entity_list.AICheckCloseBeneficialSpells(this, AISpellVar.engaged_beneficial_other_chance, MobAISpellRange, SpellType_Heal)) {
|
if (!AICheckCloseBeneficialSpells(this, AISpellVar.engaged_beneficial_other_chance, MobAISpellRange, SpellType_Heal)) {
|
||||||
//nobody to heal, try some detrimental spells.
|
//nobody to heal, try some detrimental spells.
|
||||||
if(!AICastSpell(GetTarget(), AISpellVar.engaged_detrimental_chance, SpellType_Nuke | SpellType_Lifetap | SpellType_DOT | SpellType_Dispel | SpellType_Mez | SpellType_Slow | SpellType_Debuff | SpellType_Charm | SpellType_Root)) {
|
if(!AICastSpell(GetTarget(), AISpellVar.engaged_detrimental_chance, SpellType_Nuke | SpellType_Lifetap | SpellType_DOT | SpellType_Dispel | SpellType_Mez | SpellType_Slow | SpellType_Debuff | SpellType_Charm | SpellType_Root)) {
|
||||||
//no spell to cast, try again soon.
|
//no spell to cast, try again soon.
|
||||||
@ -2033,7 +1987,7 @@ bool NPC::AI_IdleCastCheck() {
|
|||||||
if (AIautocastspell_timer->Check(false)) {
|
if (AIautocastspell_timer->Check(false)) {
|
||||||
AIautocastspell_timer->Disable(); //prevent the timer from going off AGAIN while we are casting.
|
AIautocastspell_timer->Disable(); //prevent the timer from going off AGAIN while we are casting.
|
||||||
if (!AICastSpell(this, AISpellVar.idle_beneficial_chance, SpellType_Heal | SpellType_Buff | SpellType_Pet)) {
|
if (!AICastSpell(this, AISpellVar.idle_beneficial_chance, SpellType_Heal | SpellType_Buff | SpellType_Pet)) {
|
||||||
if(!entity_list.AICheckCloseBeneficialSpells(this, 33, MobAISpellRange, SpellType_Heal | SpellType_Buff)) {
|
if(!AICheckCloseBeneficialSpells(this, 33, MobAISpellRange, SpellType_Heal | SpellType_Buff)) {
|
||||||
//if we didnt cast any spells, our autocast timer just resets to the
|
//if we didnt cast any spells, our autocast timer just resets to the
|
||||||
//last duration it was set to... try to put up a more reasonable timer...
|
//last duration it was set to... try to put up a more reasonable timer...
|
||||||
AIautocastspell_timer->Start(RandomTimer(AISpellVar.idle_no_sp_recast_min, AISpellVar.idle_no_sp_recast_max), false);
|
AIautocastspell_timer->Start(RandomTimer(AISpellVar.idle_no_sp_recast_min, AISpellVar.idle_no_sp_recast_max), false);
|
||||||
|
|||||||
224
zone/npc.cpp
224
zone/npc.cpp
@ -704,6 +704,30 @@ bool NPC::Process()
|
|||||||
|
|
||||||
SpellProcess();
|
SpellProcess();
|
||||||
|
|
||||||
|
if (mob_scan_close.Check()) {
|
||||||
|
LogAIScanClose(
|
||||||
|
"is_moving [{}] npc [{}] timer [{}]",
|
||||||
|
moving ? "true" : "false",
|
||||||
|
GetCleanName(),
|
||||||
|
mob_scan_close.GetDuration()
|
||||||
|
);
|
||||||
|
|
||||||
|
entity_list.ScanCloseMobs(close_mobs, this);
|
||||||
|
|
||||||
|
if (moving) {
|
||||||
|
mob_scan_close.Disable();
|
||||||
|
mob_scan_close.Start(RandomTimer(3000, 6000));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
mob_scan_close.Disable();
|
||||||
|
mob_scan_close.Start(RandomTimer(6000, 60000));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mob_check_moving_timer.Check() && moving) {
|
||||||
|
mob_scan_close.Trigger();
|
||||||
|
}
|
||||||
|
|
||||||
if (tic_timer.Check()) {
|
if (tic_timer.Check()) {
|
||||||
parse->EventNPC(EVENT_TICK, this, nullptr, "", 0);
|
parse->EventNPC(EVENT_TICK, this, nullptr, "", 0);
|
||||||
BuffProcess();
|
BuffProcess();
|
||||||
@ -851,7 +875,7 @@ bool NPC::Process()
|
|||||||
|
|
||||||
if (assist_timer.Check() && IsEngaged() && !Charmed() && !HasAssistAggro() &&
|
if (assist_timer.Check() && IsEngaged() && !Charmed() && !HasAssistAggro() &&
|
||||||
NPCAssistCap() < RuleI(Combat, NPCAssistCap)) {
|
NPCAssistCap() < RuleI(Combat, NPCAssistCap)) {
|
||||||
entity_list.AIYellForHelp(this, GetTarget());
|
AIYellForHelp(this, GetTarget());
|
||||||
if (NPCAssistCap() > 0 && !assist_cap_timer.Enabled())
|
if (NPCAssistCap() > 0 && !assist_cap_timer.Enabled())
|
||||||
assist_cap_timer.Start(RuleI(Combat, NPCAssistCapTimer));
|
assist_cap_timer.Start(RuleI(Combat, NPCAssistCapTimer));
|
||||||
}
|
}
|
||||||
@ -2975,6 +2999,11 @@ bool NPC::IsProximitySet()
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param box_size
|
||||||
|
* @param move_distance
|
||||||
|
* @param move_delay
|
||||||
|
*/
|
||||||
void NPC::SetSimpleRoamBox(float box_size, float move_distance, int move_delay)
|
void NPC::SetSimpleRoamBox(float box_size, float move_distance, int move_delay)
|
||||||
{
|
{
|
||||||
AI_SetRoambox(
|
AI_SetRoambox(
|
||||||
@ -2986,3 +3015,196 @@ void NPC::SetSimpleRoamBox(float box_size, float move_distance, int move_delay)
|
|||||||
move_delay
|
move_delay
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param caster
|
||||||
|
* @param chance
|
||||||
|
* @param in_cast_range
|
||||||
|
* @param spell_types
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
bool NPC::AICheckCloseBeneficialSpells(
|
||||||
|
NPC *caster,
|
||||||
|
uint8 chance,
|
||||||
|
float in_cast_range,
|
||||||
|
uint32 spell_types
|
||||||
|
)
|
||||||
|
{
|
||||||
|
if((spell_types & SPELL_TYPES_DETRIMENTAL) != 0) {
|
||||||
|
LogError("Detrimental spells requested from AICheckCloseBeneficialSpells!");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!caster) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!caster->AI_HasSpells()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (caster->GetSpecialAbility(NPC_NO_BUFFHEAL_FRIENDS)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (chance < 100) {
|
||||||
|
uint8 tmp = zone->random.Int(0, 99);
|
||||||
|
if (tmp >= chance) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indifferent
|
||||||
|
*/
|
||||||
|
if (caster->GetPrimaryFaction() == 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cast range
|
||||||
|
*/
|
||||||
|
in_cast_range = (in_cast_range * in_cast_range);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check through close range mobs
|
||||||
|
*/
|
||||||
|
for (auto & close_mob : close_mobs) {
|
||||||
|
Mob *mob = close_mob.first;
|
||||||
|
float cached_close_mob_distance = close_mob.second;
|
||||||
|
|
||||||
|
if (mob->IsClient()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cached_close_mob_distance > in_cast_range) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
LogAICastBeneficialClose(
|
||||||
|
"NPC [{}] Distance [{}] Cast Range [{}] Caster [{}]",
|
||||||
|
mob->GetCleanName(),
|
||||||
|
cached_close_mob_distance,
|
||||||
|
in_cast_range,
|
||||||
|
caster->GetCleanName()
|
||||||
|
);
|
||||||
|
|
||||||
|
if (mob->GetReverseFactionCon(caster) >= FACTION_KINDLY) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((spell_types & SpellType_Buff) && !RuleB(NPC, BuffFriends)) {
|
||||||
|
if (mob != caster) {
|
||||||
|
spell_types = SpellType_Heal;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (caster->AICastSpell(mob, 100, spell_types)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param sender
|
||||||
|
* @param attacker
|
||||||
|
*/
|
||||||
|
void NPC::AIYellForHelp(Mob *sender, Mob *attacker)
|
||||||
|
{
|
||||||
|
if (!sender || !attacker) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If we dont have a faction set, we're gonna be indiff to everybody
|
||||||
|
*/
|
||||||
|
if (sender->GetPrimaryFaction() == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sender->HasAssistAggro())
|
||||||
|
return;
|
||||||
|
|
||||||
|
LogAIYellForHelp(
|
||||||
|
"NPC [{}] ID [{}] is starting to scan",
|
||||||
|
GetCleanName(),
|
||||||
|
GetID()
|
||||||
|
);
|
||||||
|
|
||||||
|
for (auto & close_mob : close_mobs) {
|
||||||
|
Mob *mob = close_mob.first;
|
||||||
|
|
||||||
|
float distance = DistanceSquared(m_Position, mob->GetPosition());
|
||||||
|
|
||||||
|
if (mob->IsClient()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
float assist_range = (mob->GetAssistRange() * mob->GetAssistRange());
|
||||||
|
|
||||||
|
if (distance > assist_range) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
LogAIYellForHelpDetail(
|
||||||
|
"NPC [{}] ID [{}] is scanning - checking against NPC [{}] range [{}] dist [{}]",
|
||||||
|
GetCleanName(),
|
||||||
|
GetID(),
|
||||||
|
mob->GetCleanName(),
|
||||||
|
assist_range,
|
||||||
|
distance
|
||||||
|
);
|
||||||
|
|
||||||
|
if (mob->CheckAggro(attacker)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sender->NPCAssistCap() >= RuleI(Combat, NPCAssistCap)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
mob != sender
|
||||||
|
&& mob != attacker
|
||||||
|
&& mob->GetPrimaryFaction() != 0
|
||||||
|
&& !mob->IsEngaged()
|
||||||
|
&& ((!mob->IsPet()) || (mob->IsPet() && mob->GetOwner() && !mob->GetOwner()->IsClient()))
|
||||||
|
) {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* if they are in range, make sure we are not green...
|
||||||
|
* then jump in if they are our friend
|
||||||
|
*/
|
||||||
|
if (mob->GetLevel() >= 50 || attacker->GetLevelCon(mob->GetLevel()) != CON_GRAY) {
|
||||||
|
bool use_primary_faction = false;
|
||||||
|
if (mob->GetPrimaryFaction() == sender->CastToNPC()->GetPrimaryFaction()) {
|
||||||
|
const NPCFactionList *cf = database.GetNPCFactionEntry(mob->CastToNPC()->GetNPCFactionID());
|
||||||
|
if (cf) {
|
||||||
|
if (cf->assistprimaryfaction != 0) {
|
||||||
|
use_primary_faction = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (use_primary_faction || sender->GetReverseFactionCon(mob) <= FACTION_AMIABLE) {
|
||||||
|
//attacking someone on same faction, or a friend
|
||||||
|
//Father Nitwit: make sure we can see them.
|
||||||
|
if (mob->CheckLosFN(sender)) {
|
||||||
|
mob->AddToHateList(attacker, 25, 0, false);
|
||||||
|
sender->AddAssistCap();
|
||||||
|
|
||||||
|
LogAIYellForHelpDetail(
|
||||||
|
"NPC [{}] is assisting [{}] against target [{}]",
|
||||||
|
mob->GetCleanName(),
|
||||||
|
this->GetCleanName(),
|
||||||
|
attacker->GetCleanName()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -143,6 +143,9 @@ public:
|
|||||||
virtual bool AI_IdleCastCheck();
|
virtual bool AI_IdleCastCheck();
|
||||||
virtual void AI_Event_SpellCastFinished(bool iCastSucceeded, uint16 slot);
|
virtual void AI_Event_SpellCastFinished(bool iCastSucceeded, uint16 slot);
|
||||||
|
|
||||||
|
bool AICheckCloseBeneficialSpells(NPC* caster, uint8 chance, float in_cast_range, uint32 spell_types);
|
||||||
|
void AIYellForHelp(Mob* sender, Mob* attacker);
|
||||||
|
|
||||||
void LevelScale();
|
void LevelScale();
|
||||||
|
|
||||||
virtual void SetTarget(Mob* mob);
|
virtual void SetTarget(Mob* mob);
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user