int EntityList::GetHatedCount(Mob *attacker, Mob *exclude) { // Return a list of how many non-feared, non-mezzed, non-green mobs, within aggro range, hate *attacker if (!attacker) return 0; int Count = 0; for (auto it = npc_list.begin(); it != npc_list.end(); ++it) { NPC *mob = it->second; if (!mob || (mob == exclude)) continue; if (!mob->IsEngaged()) continue; if (mob->IsFeared() || mob->IsMezzed()) continue; if (attacker->GetLevelCon(mob->GetLevel()) == CON_GRAY) continue; if (!mob->CheckAggro(attacker)) continue; float AggroRange = mob->GetAggroRange(); // Square it because we will be using DistNoRoot AggroRange *= AggroRange; if (DistanceSquared(mob->GetPosition(), attacker->GetPosition()) > AggroRange) continue; Count++; } return Count; }
int EntityList::GetHatedCount(Mob *attacker, Mob *exclude) { // Return a list of how many non-feared, non-mezzed, non-green mobs, within aggro range, hate *attacker if(!attacker) return 0; int Count = 0; LinkedListIterator<NPC*> iterator(npc_list); for(iterator.Reset(); iterator.MoreElements(); iterator.Advance()) { NPC* mob = iterator.GetData(); if(!mob || (mob == exclude)) continue; if(!mob->IsEngaged()) continue; if(mob->IsFeared() || mob->IsMezzed()) continue; if(attacker->GetLevelCon(mob->GetLevel()) == CON_GREEN) continue; if(!mob->CheckAggro(attacker)) continue; float AggroRange = mob->GetAggroRange(); // Square it because we will be using DistNoRoot AggroRange = AggroRange * AggroRange; if(mob->DistNoRoot(*attacker) > AggroRange) continue; Count++; } return Count; }
Mob* EntityList::AICheckCloseAggro(Mob* sender, float iAggroRange, float iAssistRange) { if (!sender || !sender->IsNPC()) return(nullptr); #ifdef REVERSE_AGGRO //with reverse aggro, npc->client is checked elsewhere, no need to check again auto it = npc_list.begin(); while (it != npc_list.end()) { #else auto it = mob_list.begin(); while (it != mob_list.end()) { #endif Mob *mob = it->second; if (sender->CheckWillAggro(mob)) return mob; ++it; } //LogFile->write(EQEMuLog::Debug, "Check aggro for %s no target.", sender->GetName()); return nullptr; } int EntityList::GetHatedCount(Mob *attacker, Mob *exclude) { // Return a list of how many non-feared, non-mezzed, non-green mobs, within aggro range, hate *attacker if (!attacker) return 0; int Count = 0; for (auto it = npc_list.begin(); it != npc_list.end(); ++it) { NPC *mob = it->second; if (!mob || (mob == exclude)) continue; if (!mob->IsEngaged()) continue; if (mob->IsFeared() || mob->IsMezzed()) continue; if (attacker->GetLevelCon(mob->GetLevel()) == CON_GREEN) continue; if (!mob->CheckAggro(attacker)) continue; float AggroRange = mob->GetAggroRange(); // Square it because we will be using DistNoRoot AggroRange *= AggroRange; if (DistanceSquared(mob->GetPosition(), attacker->GetPosition()) > AggroRange) continue; Count++; } 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(attacker->GetLevelCon(mob->GetLevel()) != CON_GREEN) { 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) Log.Out(Logs::General, Logs::None, "AIYellForHelp(\"%s\",\"%s\") %s attacking %s Dist %f Z %f", sender->GetName(), attacker->GetName(), mob->GetName(), attacker->GetName(), DistanceSquared(mob->GetPosition(), sender->GetPosition()), fabs(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) { Mob *mob1, *mob2, *tempmob; Client *c1, *c2, *becomenpc; // NPC *npc1, *npc2; int reverse; if(!zone->CanDoCombat()) return false; // some special cases if(!target) return false; if(this == target) // you can attack yourself return true; if(target->GetSpecialAbility(NO_HARM_FROM_CLIENT)){ return false; } // can't damage own pet (applies to everthing) Mob *target_owner = target->GetOwner(); Mob *our_owner = GetOwner(); if(target_owner && target_owner == this) return false; else if(our_owner && our_owner == target) return false; // invalidate for swarm pets for later on if their owner is a corpse if (IsNPC() && CastToNPC()->GetSwarmInfo() && our_owner && our_owner->IsCorpse() && !our_owner->IsPlayerCorpse()) our_owner = nullptr; if (target->IsNPC() && target->CastToNPC()->GetSwarmInfo() && target_owner && target_owner->IsCorpse() && !target_owner->IsPlayerCorpse()) target_owner = nullptr; //cannot hurt untargetable mobs bodyType bt = target->GetBodyType(); if(bt == BT_NoTarget || bt == BT_NoTarget2) { if (RuleB(Pets, UnTargetableSwarmPet)) { if (target->IsNPC()) { if (!target->CastToNPC()->GetSwarmOwner()) { return(false); } } else { return(false); } } else { return(false); } } if(!isSpellAttack) { if(GetClass() == LDON_TREASURE) { return false; } } // the format here is a matrix of mob type vs mob type. // redundant ones are omitted and the reverse is tried if it falls through. // first figure out if we're pets. we always look at the master's flags. // no need to compare pets to anything mob1 = our_owner ? our_owner : this; mob2 = target_owner ? target_owner : target; reverse = 0; do { if(_CLIENT(mob1)) { if(_CLIENT(mob2)) // client vs client { c1 = mob1->CastToClient(); c2 = mob2->CastToClient(); if // if both are pvp they can fight ( c1->GetPVP() && c2->GetPVP() ) return true; else if // if they're dueling they can go at it ( c1->IsDueling() && c2->IsDueling() && c1->GetDuelTarget() == c2->GetID() && c2->GetDuelTarget() == c1->GetID() ) return true; else return false; } else if(_NPC(mob2)) // client vs npc { return true; } else if(_BECOMENPC(mob2)) // client vs becomenpc { c1 = mob1->CastToClient(); becomenpc = mob2->CastToClient(); if(c1->GetLevel() > becomenpc->GetBecomeNPCLevel()) return false; else return true; } else if(_CLIENTCORPSE(mob2)) // client vs client corpse { return false; } else if(_NPCCORPSE(mob2)) // client vs npc corpse { return false; } } else if(_NPC(mob1)) { if(_NPC(mob2)) // npc vs npc { /* this says that an NPC can NEVER attack a faction ally... this is stupid... somebody else should check this rule if they want to enforce it, this just says 'can they possibly fight based on their type', in which case, the answer is yes. */ /* npc1 = mob1->CastToNPC(); npc2 = mob2->CastToNPC(); if ( npc1->GetPrimaryFaction() != 0 && npc2->GetPrimaryFaction() != 0 && ( npc1->GetPrimaryFaction() == npc2->GetPrimaryFaction() || npc1->IsFactionListAlly(npc2->GetPrimaryFaction()) ) ) return false; else */ return true; } else if(_BECOMENPC(mob2)) // npc vs becomenpc { return true; } else if(_CLIENTCORPSE(mob2)) // npc vs client corpse { return false; } else if(_NPCCORPSE(mob2)) // npc vs npc corpse { return false; } } else if(_BECOMENPC(mob1)) { if(_BECOMENPC(mob2)) // becomenpc vs becomenpc { return true; } else if(_CLIENTCORPSE(mob2)) // becomenpc vs client corpse { return false; } else if(_NPCCORPSE(mob2)) // becomenpc vs npc corpse { return false; } } else if(_CLIENTCORPSE(mob1)) { if(_CLIENTCORPSE(mob2)) // client corpse vs client corpse { return false; } else if(_NPCCORPSE(mob2)) // client corpse vs npc corpse { return false; } } else if(_NPCCORPSE(mob1)) { if(_NPCCORPSE(mob2)) // npc corpse vs npc corpse { return false; } } #ifdef BOTS bool HasRuleDefined = false; bool IsBotAttackAllowed = false; IsBotAttackAllowed = Bot::IsBotAttackAllowed(mob1, mob2, HasRuleDefined); if(HasRuleDefined) return IsBotAttackAllowed; #endif //BOTS // we fell through, now we swap the 2 mobs and run through again once more tempmob = mob1; mob1 = mob2; mob2 = tempmob; } while( reverse++ == 0 ); Log.Out(Logs::General, Logs::None, "Mob::IsAttackAllowed: don't have a rule for this - %s vs %s\n", this->GetName(), target->GetName()); return false; }