changes:
- Mob::IsEngaged() now returns AggroCount() for Clients
- updated handling of #bot spawn and #bot summon commands
- complete re-write of IsEngaged() logic in Bot::AI_Process()
- Bot::AI_Process() now allows bots to engage hostile, charmed clients
- implemented Bot::GetOwnerID() so Perl interface will work with bots
Code:
Index: bot.cpp
===================================================================
--- bot.cpp (revision 2503)
+++ bot.cpp (working copy)
@@ -3579,34 +3579,35 @@
return;
}
- if(!IsEngaged()) {
- if(GetFollowID()) {
- if(BotOwner && BotOwner->CastToClient()->AutoAttackEnabled() && BotOwner->GetTarget() &&
- BotOwner->GetTarget()->IsNPC() && BotOwner->GetTarget()->GetHateAmount(BotOwner)) {
- AddToHateList(BotOwner->GetTarget(), 1);
-
- if(HasPet())
- GetPet()->AddToHateList(BotOwner->GetTarget(), 1);
- }
- else {
- Group* g = GetGroup();
-
- if(g) {
- for(int counter = 0; counter < g->GroupCount(); counter++) {
- if(g->members[counter]) {
- if(g->members[counter]->IsEngaged() && g->members[counter]->GetTarget()) {
- AddToHateList(g->members[counter]->GetTarget(), 1);
-
- if(HasPet())
- GetPet()->AddToHateList(g->members[counter]->GetTarget(), 1);
-
- break;
- }
- }
+ // if bot not currently engaged, determine if bot should engage anything
+ // NOTE: has been changed to allow bots to target hostile, charmed clients
+ if (!IsEngaged() && GetFollowID()) {
+ Mob *target = 0;
+ // if owner is engaged with an AI-controlled mob, get owner's target
+ if (BotOwner && BotOwner->CastToClient()->AutoAttackEnabled()) {
+ target = BotOwner->GetTarget();
+ if (target && !target->GetHateAmount(BotOwner)) { target = 0; }
+ }
+ // if group member is engaged with an AI-controlled mob, get member's target
+ else {
+ Group* group = GetGroup();
+ if (group) {
+ for (int i = 0; i < MAX_GROUP_MEMBERS; i++) {
+ Mob* member = group->members[i];
+ if (!member || !member->IsEngaged()) { continue; }
+ if (!member->IsClient() || member->CastToClient()->AutoAttackEnabled() ) {
+ Mob *target = member->GetTarget();
+ if (target && target->GetHateAmount(member)) { break; }
+ else if (target) { target = 0; }
}
}
}
}
+ // engage target, if found
+ if (target) {
+ AddToHateList(target, 1);
+ if (HasPet()) { GetPet()->AddToHateList(target, 1); }
+ }
}
if(IsEngaged())
@@ -12317,7 +12316,7 @@
}
if(c->GetFeigned()) {
- c->Message(0, "You can't summon bots while you are feigned.");
+ c->Message(0, "You can't spawn bots while you are feigned.");
return;
}
@@ -12325,7 +12324,7 @@
BotRaids *br = entity_list.GetBotRaidByMob(c->CastToMob());
if(br) {
if(br->GetBotRaidAggro()) {
- c->Message(15, "You can't summon bots while you are engaged.");
+ c->Message(15, "You can't spawn bots while you are engaged.");
return;
}
}
@@ -12333,22 +12332,21 @@
if(c->IsGrouped()) {
Group *g = entity_list.GetGroupByClient(c);
+ if (!g)
+ return;
for (int i=0; i<MAX_GROUP_MEMBERS; i++) {
- if(g && g->members[i] && !g->members[i]->qglobal && (g->members[i]->GetAppearance() != eaDead)
- && (g->members[i]->IsEngaged() || (g->members[i]->IsClient() && g->members[i]->CastToClient()->GetAggroCount()))) {
- c->Message(0, "You can't summon bots while you are engaged.");
+ Mob* member = g->members[i];
+ if (!member || member->qglobal)
return;
- }
- if(g && g->members[i] && g->members[i]->qglobal) {
+ if (member->IsEngaged() && member->GetAppearance() != eaDead) {
+ c->Message(0, "You can't spawn bots while you are engaged.");
return;
}
}
}
- else {
- if(c->GetAggroCount() > 0) {
- c->Message(0, "You can't spawn bots while you are engaged.");
- return;
- }
+ else if (c->IsEngaged()) {
+ c->Message(0, "You can't spawn bots while you are engaged.");
+ return;
}
Mob* TempBotMob = entity_list.GetMobByBotID(botId);
@@ -15022,21 +15020,17 @@
// Skip invalid group members.
if(!g->members[i]) { continue; }
- // Fail if current group member is client and has aggro
- // OR has a popuplated hate list (assume bot).
- if((g->members[i]->IsClient() && g->members[i]->CastToClient()->GetAggroCount())
- || g->members[i]->IsEngaged()) {
+ // Fail if current group member is engaged.
+ if(g->members[i]->IsEngaged()) {
c->Message(0, "You can't spawn bots while your group is engaged.");
return;
}
}
}
// Fail if ungrouped client has aggro.
- else {
- if(c->GetAggroCount() > 0) {
- c->Message(0, "You can't spawn bots while you are engaged.");
- return;
- }
+ else if(c->IsEngaged()) {
+ c->Message(0, "You can't spawn bots while you are engaged.");
+ return;
}
// Parse botgroup name.
Index: bot.h
===================================================================
--- bot.h (revision 2503)
+++ bot.h (working copy)
@@ -427,6 +427,7 @@
// "GET" Class Methods
uint32 GetBotID() const { return _botID; }
uint32 GetBotOwnerCharacterID() { return _botOwnerCharacterID; }
+ inline virtual uint16 GetOwnerID() const { return _botOwner ? _botOwner->GetID() : 0; }
uint32 GetBotSpellID() { return npc_spells_id; }
Mob* GetBotOwner() { return this->_botOwner; }
uint32 GetBotArcheryRange();
Index: mob.cpp
===================================================================
--- mob.cpp (revision 2503)
+++ mob.cpp (working copy)
@@ -4477,3 +4477,14 @@
}
return false;
}
+
+bool Mob::IsEngaged() {
+
+ return (
+ IsClient() && !IsAIControlled()
+ // non-charmed client checks aggro count
+ ? CastToClient()->GetAggroCount() > 0
+ // npc, bot, merc, or charmed client checks hate list
+ : !hate_list.IsEmpty()
+ );
+}
Index: mob.h
===================================================================
--- mob.h (revision 2503)
+++ mob.h (working copy)
@@ -399,7 +399,7 @@
Mob* GetHateDamageTop(Mob* other) { return hate_list.GetDamageTop(other);}
Mob* GetHateRandom() { return hate_list.GetRandom();}
Mob* GetHateMost() { return hate_list.GetMostHate();}
- bool IsEngaged() { return(!hate_list.IsEmpty()); }
+ bool IsEngaged();
bool HateSummon();
void FaceTarget(Mob* MobToFace = 0);
void SetHeading(float iHeading) { if(heading != iHeading) { pLastChange = Timer::GetCurrentTime();
@@ -415,6 +415,7 @@
bool CheckLosFN(float posX, float posY, float posZ, float mobSize);
inline void SetChanged() { pLastChange = Timer::GetCurrentTime(); }
inline const uint32 LastChange() const { return pLastChange; }
@@ -586,7 +587,7 @@
bool IsAnimation() const { return(typeofpet == petAnimation); }
bool IsCharmed() const { return(typeofpet == petCharmed); }
void SetOwnerID(uint16 NewOwnerID);
- inline uint16 GetOwnerID() const { return ownerid; }
+ inline virtual uint16 GetOwnerID() const { return ownerid; }
inline virtual bool HasOwner() { if(GetOwnerID()==0){return false;} return( entity_list.GetMob(GetOwnerID()) != 0); }
inline virtual bool IsPet() { return(HasOwner() && !IsMerc()); }
inline bool HasPet() const { if(GetPetID()==0){return false;} return (entity_list.GetMob(GetPetID()) != 0);}
compiled and tested.
$mob->IsEngaged() will now return true if $mob is a Client.
$mob->GetOwnerID() will now return owner's entity id if $mob is a Bot.
i had to re-write part of Bot::Process_AI() because the change to Mob::IsEngaged() was causing bots to run off and engage whatever the client had aggroed and targeted when it got within a certain range (may have been LoS, as i was on top of an incline), regardless if auto-attack enabled or not. the other parts that were changed were just streamlining logic by using the new IsEngaged(), and correcting a couple of message typos.
i'd probably look into adding IsEngaged() checks to the bot's idle casting logic (for heals/buffs) as well. as it stands, if you are idle and something is beating on you and you get healed or buffed by a bot, the casting bot aggros the aggressor, and then any other bots in the group follow suit. this obviously would allow for afk exp in some areas, and that's no fun!