mirror of
https://github.com/EQEmu/Server.git
synced 2025-12-11 16:51:29 +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,
|
||||
Alert,
|
||||
Notice,
|
||||
AIScanClose,
|
||||
AIYellForHelp,
|
||||
AICastBeneficialClose,
|
||||
MaxCategoryID /* Don't Remove this */
|
||||
};
|
||||
|
||||
@ -172,7 +175,10 @@ namespace Logs {
|
||||
"Critical",
|
||||
"Emergency",
|
||||
"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__);\
|
||||
} 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 {\
|
||||
if (LogSys.log_settings[log_category].is_category_enabled == 1)\
|
||||
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, CriticalDamage, 80, "")
|
||||
RULE_INT(Range, ClientNPCScan, 300, "")
|
||||
RULE_INT(Range, MobCloseScanDistance, 300, "")
|
||||
RULE_CATEGORY_END()
|
||||
|
||||
|
||||
|
||||
@ -189,10 +189,10 @@ namespace WorldserverCommandHandler {
|
||||
|
||||
Json::Value schema;
|
||||
|
||||
schema["server_tables"] = server_tables_json;
|
||||
schema["player_tables"] = player_tables_json;
|
||||
schema["content_tables"] = content_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["version_tables"] = version_tables_json;
|
||||
|
||||
|
||||
110
zone/aggro.cpp
110
zone/aggro.cpp
@ -36,19 +36,6 @@
|
||||
extern Zone* zone;
|
||||
//#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) {
|
||||
float d2 = d*d;
|
||||
|
||||
@ -402,22 +389,6 @@ bool Mob::CheckWillAggro(Mob *mob) {
|
||||
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)
|
||||
{
|
||||
// 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;
|
||||
}
|
||||
|
||||
void EntityList::AIYellForHelp(Mob* sender, Mob* attacker) {
|
||||
if(!sender || !attacker)
|
||||
return;
|
||||
if (sender->GetPrimaryFaction() == 0 )
|
||||
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
|
||||
*/
|
||||
|
||||
/**
|
||||
* @param target
|
||||
* @param isSpellAttack
|
||||
* @return
|
||||
*/
|
||||
bool Mob::IsAttackAllowed(Mob *target, bool isSpellAttack)
|
||||
{
|
||||
|
||||
|
||||
@ -226,7 +226,6 @@ public:
|
||||
Client(EQStreamInterface * ieqs);
|
||||
~Client();
|
||||
|
||||
std::unordered_map<Mob *, float> close_mobs;
|
||||
bool is_client_moving;
|
||||
|
||||
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()) {
|
||||
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));
|
||||
auto &mob_list = entity_list.GetMobList();
|
||||
for (auto itr = mob_list.begin(); itr != mob_list.end(); ++itr) {
|
||||
Mob* mob = itr->second;
|
||||
auto &mob_list = entity_list.GetMobList();
|
||||
|
||||
for (auto itr : mob_list) {
|
||||
Mob *mob = itr.second;
|
||||
float distance = DistanceSquared(m_Position, mob->GetPosition());
|
||||
|
||||
if (mob->IsNPC()) {
|
||||
if (distance <= scan_range) {
|
||||
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);
|
||||
if (it != mob_list.end()) {
|
||||
|
||||
RemoveMobFromClientCloseLists(it->second);
|
||||
RemoveMobFromCloseLists(it->second);
|
||||
|
||||
if (npc_list.count(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
|
||||
bool EntityList::RemoveMob(Mob *delete_mob)
|
||||
{
|
||||
if (delete_mob == 0)
|
||||
if (delete_mob == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
auto it = mob_list.begin();
|
||||
while (it != mob_list.end()) {
|
||||
if (it->second == delete_mob) {
|
||||
RemoveMobFromClientCloseLists(it->second);
|
||||
RemoveMobFromCloseLists(it->second);
|
||||
|
||||
safe_delete(it->second);
|
||||
if (!corpse_list.count(it->first))
|
||||
if (!corpse_list.count(it->first)) {
|
||||
free_ids.push(it->first);
|
||||
}
|
||||
mob_list.erase(it);
|
||||
return true;
|
||||
}
|
||||
@ -2539,28 +2541,62 @@ bool EntityList::RemoveNPC(uint16 delete_id)
|
||||
// make sure its proximity is removed
|
||||
RemoveProximity(delete_id);
|
||||
// remove from client close lists
|
||||
RemoveMobFromClientCloseLists(npc->CastToMob());
|
||||
RemoveMobFromCloseLists(npc->CastToMob());
|
||||
// remove from the list
|
||||
npc_list.erase(it);
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool EntityList::RemoveMobFromClientCloseLists(Mob *mob)
|
||||
/**
|
||||
* @param mob
|
||||
* @return
|
||||
*/
|
||||
bool EntityList::RemoveMobFromCloseLists(Mob *mob)
|
||||
{
|
||||
auto it = client_list.begin();
|
||||
while (it != client_list.end()) {
|
||||
auto it = mob_list.begin();
|
||||
while (it != mob_list.end()) {
|
||||
it->second->close_mobs.erase(mob);
|
||||
++it;
|
||||
}
|
||||
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)
|
||||
{
|
||||
auto it = merc_list.find(delete_id);
|
||||
|
||||
@ -284,7 +284,7 @@ public:
|
||||
bool RemoveTrap(uint16 delete_id);
|
||||
bool RemoveObject(uint16 delete_id);
|
||||
bool RemoveProximity(uint16 delete_npc_id);
|
||||
bool RemoveMobFromClientCloseLists(Mob *mob);
|
||||
bool RemoveMobFromCloseLists(Mob *mob);
|
||||
void RemoveAllMobs();
|
||||
void RemoveAllClients();
|
||||
void RemoveAllNPCs();
|
||||
@ -443,11 +443,7 @@ public:
|
||||
bool LimitCheckBoth(uint32 npc_type, uint32 spawngroup_id, int group_count, int type_count);
|
||||
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);
|
||||
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);
|
||||
Mob* GetTargetForMez(Mob* caster);
|
||||
uint32 CheckNPCsClose(Mob *center);
|
||||
@ -501,6 +497,7 @@ public:
|
||||
void RefreshAutoXTargets(Client *c);
|
||||
void RefreshClientXTargets(Client *c);
|
||||
void SendAlternateAdvancementStats();
|
||||
void ScanCloseMobs(std::unordered_map<Mob *, float> &close_mobs, Mob *scanning_mob);
|
||||
|
||||
void GetTrapInfo(Client* client);
|
||||
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),
|
||||
attack_anim_timer(1000),
|
||||
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->AddMob(this);
|
||||
@ -524,6 +526,16 @@ uint32 Mob::GetAppearanceValue(EmuAppearance iAppearance) {
|
||||
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)
|
||||
{
|
||||
invisible = state;
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
|
||||
/* EQEMu: Everquest Server Emulator
|
||||
Copyright (C) 2001-2016 EQEMu Development Team (http://eqemu.org)
|
||||
|
||||
@ -169,6 +170,12 @@ public:
|
||||
|
||||
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!
|
||||
|
||||
//Attack
|
||||
@ -968,7 +975,7 @@ public:
|
||||
void SetEntityVariable(const char *id, const char *m_var);
|
||||
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();
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
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()
|
||||
{
|
||||
pAIControlled = false;
|
||||
@ -1415,12 +1362,20 @@ void Mob::AI_Process() {
|
||||
}
|
||||
else if (zone->CanDoCombat() && CastToNPC()->WillAggroNPCs() && AI_scan_area_timer->Check()) {
|
||||
|
||||
/*
|
||||
* NPC to NPC aggro checking, npc needs npc_aggro flag
|
||||
*/
|
||||
Mob *temp_target = entity_list.AICheckNPCtoNPCAggro(this, GetAggroRange(), GetAssistRange());
|
||||
if (temp_target) {
|
||||
AddToHateList(temp_target);
|
||||
/**
|
||||
* NPC to NPC aggro (npc_aggro flag set)
|
||||
*/
|
||||
for (auto &close_mob : close_mobs) {
|
||||
Mob *mob = close_mob.first;
|
||||
float distance = close_mob.second;
|
||||
|
||||
if (mob->IsClient()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (this->CheckWillAggro(mob)) {
|
||||
this->AddToHateList(mob);
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
if (!IsAIControlled())
|
||||
/**
|
||||
* @param attacker
|
||||
* @param yell_for_help
|
||||
*/
|
||||
void Mob::AI_Event_Engaged(Mob *attacker, bool yell_for_help)
|
||||
{
|
||||
if (!IsAIControlled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
SetAppearance(eaStanding);
|
||||
|
||||
/*
|
||||
Kick off auto cast timer
|
||||
*/
|
||||
if (this->IsNPC())
|
||||
this->CastToNPC()->AIautocastspell_timer->Start(300, false);
|
||||
if (IsNPC()) {
|
||||
CastToNPC()->AIautocastspell_timer->Start(300, false);
|
||||
|
||||
if (iYellForHelp) {
|
||||
if(IsPet()) {
|
||||
GetOwner()->AI_Event_Engaged(attacker, iYellForHelp);
|
||||
} else if (!HasAssistAggro() && NPCAssistCap() < RuleI(Combat, NPCAssistCap)) {
|
||||
entity_list.AIYellForHelp(this, attacker);
|
||||
if (NPCAssistCap() > 0 && !assist_cap_timer.Enabled())
|
||||
assist_cap_timer.Start(RuleI(Combat, NPCAssistCapTimer));
|
||||
if (yell_for_help) {
|
||||
if (IsPet()) {
|
||||
GetOwner()->AI_Event_Engaged(attacker, yell_for_help);
|
||||
}
|
||||
else if (!HasAssistAggro() && NPCAssistCap() < RuleI(Combat, NPCAssistCap)) {
|
||||
CastToNPC()->AIYellForHelp(this, attacker);
|
||||
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;
|
||||
}
|
||||
if(attacker && !attacker->IsCorpse())
|
||||
{
|
||||
if (attacker && !attacker->IsCorpse()) {
|
||||
//Because sometimes the AIYellForHelp triggers another engaged and then immediately a not engaged
|
||||
//if the target dies before it goes off
|
||||
if(attacker->GetHP() > 0)
|
||||
{
|
||||
if(!CastToNPC()->GetCombatEvent() && GetHP() > 0)
|
||||
{
|
||||
if (attacker->GetHP() > 0) {
|
||||
if (!CastToNPC()->GetCombatEvent() && GetHP() > 0) {
|
||||
parse->EventNPC(EVENT_COMBAT, CastToNPC(), attacker, "1", 0);
|
||||
uint16 emoteid = GetEmoteID();
|
||||
if(emoteid != 0)
|
||||
CastToNPC()->DoNPCEmote(ENTERCOMBAT,emoteid);
|
||||
if (emoteid != 0) {
|
||||
CastToNPC()->DoNPCEmote(ENTERCOMBAT, emoteid);
|
||||
}
|
||||
CastToNPC()->SetCombatEvent(true);
|
||||
}
|
||||
}
|
||||
@ -1996,7 +1950,7 @@ bool NPC::AI_EngagedCastCheck() {
|
||||
// try casting a heal or gate
|
||||
if (!AICastSpell(this, AISpellVar.engaged_beneficial_self_chance, SpellType_Heal | SpellType_Escape | SpellType_InCombatBuff)) {
|
||||
// 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.
|
||||
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.
|
||||
@ -2033,7 +1987,7 @@ bool NPC::AI_IdleCastCheck() {
|
||||
if (AIautocastspell_timer->Check(false)) {
|
||||
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(!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
|
||||
//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);
|
||||
|
||||
224
zone/npc.cpp
224
zone/npc.cpp
@ -704,6 +704,30 @@ bool NPC::Process()
|
||||
|
||||
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()) {
|
||||
parse->EventNPC(EVENT_TICK, this, nullptr, "", 0);
|
||||
BuffProcess();
|
||||
@ -851,7 +875,7 @@ bool NPC::Process()
|
||||
|
||||
if (assist_timer.Check() && IsEngaged() && !Charmed() && !HasAssistAggro() &&
|
||||
NPCAssistCap() < RuleI(Combat, NPCAssistCap)) {
|
||||
entity_list.AIYellForHelp(this, GetTarget());
|
||||
AIYellForHelp(this, GetTarget());
|
||||
if (NPCAssistCap() > 0 && !assist_cap_timer.Enabled())
|
||||
assist_cap_timer.Start(RuleI(Combat, NPCAssistCapTimer));
|
||||
}
|
||||
@ -2975,6 +2999,11 @@ bool NPC::IsProximitySet()
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param box_size
|
||||
* @param move_distance
|
||||
* @param move_delay
|
||||
*/
|
||||
void NPC::SetSimpleRoamBox(float box_size, float move_distance, int move_delay)
|
||||
{
|
||||
AI_SetRoambox(
|
||||
@ -2986,3 +3015,196 @@ void NPC::SetSimpleRoamBox(float box_size, float move_distance, int 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 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();
|
||||
|
||||
virtual void SetTarget(Mob* mob);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user