mirror of
https://github.com/mod-playerbots/mod-playerbots.git
synced 2026-01-13 17:09:08 +00:00
Noticed that if you ask a bot to duel in a PVP prohibited area, it will accept, and do nothing. I thought about making the bot reject the request, but if you (the real player) want to duel with it, the duel should happen. This is just a minor fix to allow bots to duel if you ask them to in such areas. Tested with bots in party, random bots of the same faction, and random bots of the opposite faction. All behaved the same before and after fix. An example place to test is Zim'Torga in Zul'Drak which is by default is a PVP prohibited area. - Before fix, you challenge a bot, they accept and turn red, then they either just stay where they are or wander off. - After fix, bot attacks you within the PVP prohibited area when the duel starts.
307 lines
12 KiB
C++
307 lines
12 KiB
C++
/*
|
|
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
|
|
* and/or modify it under version 3 of the License, or (at your option), any later version.
|
|
*/
|
|
|
|
#include "PetsAction.h"
|
|
|
|
#include "CharmInfo.h"
|
|
#include "Creature.h"
|
|
#include "CreatureAI.h"
|
|
#include "Pet.h"
|
|
#include "Player.h"
|
|
#include "PlayerbotAI.h"
|
|
#include "SharedDefines.h"
|
|
|
|
bool PetsAction::Execute(Event event)
|
|
{
|
|
// Extract the command parameter from the event (e.g., "aggressive", "defensive", "attack", etc.)
|
|
std::string param = event.getParam();
|
|
if (param.empty() && !defaultCmd.empty())
|
|
param = defaultCmd;
|
|
|
|
if (param.empty())
|
|
{
|
|
// If no parameter is provided, show usage instructions and return.
|
|
std::string text = sPlayerbotTextMgr->GetBotTextOrDefault(
|
|
"pet_usage_error", "Usage: pet <aggressive|defensive|passive|stance|attack|follow|stay>", {});
|
|
botAI->TellError(text);
|
|
return false;
|
|
}
|
|
|
|
Player* bot = botAI->GetBot();
|
|
|
|
// Collect all controlled pets and guardians, except totems, into the targets vector.
|
|
std::vector<Creature*> targets;
|
|
Pet* pet = bot->GetPet();
|
|
if (pet)
|
|
targets.push_back(pet);
|
|
|
|
for (Unit::ControlSet::const_iterator itr = bot->m_Controlled.begin(); itr != bot->m_Controlled.end(); ++itr)
|
|
{
|
|
Creature* creature = dynamic_cast<Creature*>(*itr);
|
|
if (!creature)
|
|
continue;
|
|
if (pet && creature == pet)
|
|
continue;
|
|
if (creature->IsTotem())
|
|
continue;
|
|
targets.push_back(creature);
|
|
}
|
|
|
|
// If no pets or guardians are found, notify and return.
|
|
if (targets.empty())
|
|
{
|
|
std::string text = sPlayerbotTextMgr->GetBotTextOrDefault(
|
|
"pet_no_pet_error", "You have no pet or guardian pet.", {});
|
|
botAI->TellError(text);
|
|
return false;
|
|
}
|
|
|
|
ReactStates react;
|
|
std::string stanceText;
|
|
|
|
// Handle stance commands: aggressive, defensive, or passive.
|
|
if (param == "aggressive")
|
|
{
|
|
react = REACT_AGGRESSIVE;
|
|
stanceText = sPlayerbotTextMgr->GetBotTextOrDefault(
|
|
"pet_stance_aggressive", "aggressive", {});
|
|
}
|
|
else if (param == "defensive")
|
|
{
|
|
react = REACT_DEFENSIVE;
|
|
stanceText = sPlayerbotTextMgr->GetBotTextOrDefault(
|
|
"pet_stance_defensive", "defensive", {});
|
|
}
|
|
else if (param == "passive")
|
|
{
|
|
react = REACT_PASSIVE;
|
|
stanceText = sPlayerbotTextMgr->GetBotTextOrDefault(
|
|
"pet_stance_passive", "passive", {});
|
|
}
|
|
// The "stance" command simply reports the current stance of each pet/guardian.
|
|
else if (param == "stance")
|
|
{
|
|
for (Creature* target : targets)
|
|
{
|
|
std::string type = target->IsPet() ?
|
|
sPlayerbotTextMgr->GetBotTextOrDefault("pet_type_pet", "pet", {}) :
|
|
sPlayerbotTextMgr->GetBotTextOrDefault("pet_type_guardian", "guardian", {});
|
|
std::string name = target->GetName();
|
|
std::string stance;
|
|
switch (target->GetReactState())
|
|
{
|
|
case REACT_AGGRESSIVE:
|
|
stance = sPlayerbotTextMgr->GetBotTextOrDefault(
|
|
"pet_stance_aggressive", "aggressive", {});
|
|
break;
|
|
case REACT_DEFENSIVE:
|
|
stance = sPlayerbotTextMgr->GetBotTextOrDefault(
|
|
"pet_stance_defensive", "defensive", {});
|
|
break;
|
|
case REACT_PASSIVE:
|
|
stance = sPlayerbotTextMgr->GetBotTextOrDefault(
|
|
"pet_stance_passive", "passive", {});
|
|
break;
|
|
default:
|
|
stance = sPlayerbotTextMgr->GetBotTextOrDefault(
|
|
"pet_stance_unknown", "unknown", {});
|
|
break;
|
|
}
|
|
std::string text = sPlayerbotTextMgr->GetBotTextOrDefault(
|
|
"pet_stance_report", "Current stance of %type \"%name\": %stance.",
|
|
{{"type", type}, {"name", name}, {"stance", stance}});
|
|
botAI->TellMaster(text);
|
|
}
|
|
return true;
|
|
}
|
|
// The "attack" command forces pets/guardians to attack the master's selected target.
|
|
else if (param == "attack")
|
|
{
|
|
// Try to get the master's selected target.
|
|
Player* master = botAI->GetMaster();
|
|
Unit* targetUnit = nullptr;
|
|
|
|
if (master)
|
|
{
|
|
ObjectGuid masterTargetGuid = master->GetTarget();
|
|
if (!masterTargetGuid.IsEmpty())
|
|
targetUnit = botAI->GetUnit(masterTargetGuid);
|
|
}
|
|
|
|
// If no valid target is selected, show an error and return.
|
|
if (!targetUnit)
|
|
{
|
|
std::string text = sPlayerbotTextMgr->GetBotTextOrDefault(
|
|
"pet_no_target_error", "No valid target selected by master.", {});
|
|
botAI->TellError(text);
|
|
return false;
|
|
}
|
|
if (!targetUnit->IsAlive())
|
|
{
|
|
std::string text = sPlayerbotTextMgr->GetBotTextOrDefault(
|
|
"pet_target_dead_error", "Target is not alive.", {});
|
|
botAI->TellError(text);
|
|
return false;
|
|
}
|
|
if (!bot->IsValidAttackTarget(targetUnit))
|
|
{
|
|
std::string text = sPlayerbotTextMgr->GetBotTextOrDefault(
|
|
"pet_invalid_target_error", "Target is not a valid attack target for the bot.", {});
|
|
botAI->TellError(text);
|
|
return false;
|
|
}
|
|
if (sPlayerbotAIConfig->IsPvpProhibited(bot->GetZoneId(), bot->GetAreaId()) &&
|
|
(targetUnit->IsPlayer() || targetUnit->IsPet()) &&
|
|
(!bot->duel || bot->duel->Opponent != targetUnit))
|
|
{
|
|
std::string text = sPlayerbotTextMgr->GetBotTextOrDefault(
|
|
"pet_pvp_prohibited_error", "I cannot command my pet to attack players in PvP prohibited areas.", {});
|
|
botAI->TellError(text);
|
|
return false;
|
|
}
|
|
|
|
bool didAttack = false;
|
|
// For each controlled pet/guardian, command them to attack the selected target.
|
|
for (Creature* petCreature : targets)
|
|
{
|
|
CharmInfo* charmInfo = petCreature->GetCharmInfo();
|
|
if (!charmInfo)
|
|
continue;
|
|
|
|
petCreature->ClearUnitState(UNIT_STATE_FOLLOW);
|
|
// Only command attack if not already attacking the target, or if not currently under command attack.
|
|
if (petCreature->GetVictim() != targetUnit ||
|
|
(petCreature->GetVictim() == targetUnit && !charmInfo->IsCommandAttack()))
|
|
{
|
|
if (petCreature->GetVictim())
|
|
petCreature->AttackStop();
|
|
|
|
if (!petCreature->IsPlayer() && petCreature->ToCreature()->IsAIEnabled)
|
|
{
|
|
// For AI-enabled creatures (NPC pets/guardians): issue attack command and set flags.
|
|
charmInfo->SetIsCommandAttack(true);
|
|
charmInfo->SetIsAtStay(false);
|
|
charmInfo->SetIsFollowing(false);
|
|
charmInfo->SetIsCommandFollow(false);
|
|
charmInfo->SetIsReturning(false);
|
|
|
|
petCreature->ToCreature()->AI()->AttackStart(targetUnit);
|
|
|
|
didAttack = true;
|
|
}
|
|
else // For charmed player pets/guardians
|
|
{
|
|
if (petCreature->GetVictim() && petCreature->GetVictim() != targetUnit)
|
|
petCreature->AttackStop();
|
|
|
|
charmInfo->SetIsCommandAttack(true);
|
|
charmInfo->SetIsAtStay(false);
|
|
charmInfo->SetIsFollowing(false);
|
|
charmInfo->SetIsCommandFollow(false);
|
|
charmInfo->SetIsReturning(false);
|
|
|
|
petCreature->Attack(targetUnit, true);
|
|
didAttack = true;
|
|
}
|
|
}
|
|
}
|
|
// Inform the master if the command succeeded or failed.
|
|
if (didAttack && sPlayerbotAIConfig->petChatCommandDebug == 1)
|
|
{
|
|
std::string text = sPlayerbotTextMgr->GetBotTextOrDefault(
|
|
"pet_attack_success", "Pet commanded to attack your target.", {});
|
|
botAI->TellMaster(text);
|
|
}
|
|
else if (!didAttack)
|
|
{
|
|
std::string text = sPlayerbotTextMgr->GetBotTextOrDefault(
|
|
"pet_attack_failed", "Pet did not attack. (Already attacking or unable to attack target)", {});
|
|
botAI->TellError(text);
|
|
}
|
|
return didAttack;
|
|
}
|
|
// The "follow" command makes all pets/guardians follow the bot.
|
|
else if (param == "follow")
|
|
{
|
|
botAI->PetFollow();
|
|
if (sPlayerbotAIConfig->petChatCommandDebug == 1)
|
|
{
|
|
std::string text = sPlayerbotTextMgr->GetBotTextOrDefault(
|
|
"pet_follow_success", "Pet commanded to follow.", {});
|
|
botAI->TellMaster(text);
|
|
}
|
|
return true;
|
|
}
|
|
// The "stay" command causes all pets/guardians to stop and stay in place.
|
|
else if (param == "stay")
|
|
{
|
|
for (Creature* target : targets)
|
|
{
|
|
// If not already in controlled motion, stop movement and set to idle.
|
|
bool controlledMotion =
|
|
target->GetMotionMaster()->GetMotionSlotType(MOTION_SLOT_CONTROLLED) != NULL_MOTION_TYPE;
|
|
if (!controlledMotion)
|
|
{
|
|
target->StopMovingOnCurrentPos();
|
|
target->GetMotionMaster()->Clear(false);
|
|
target->GetMotionMaster()->MoveIdle();
|
|
}
|
|
|
|
CharmInfo* charmInfo = target->GetCharmInfo();
|
|
if (charmInfo)
|
|
{
|
|
// Set charm/pet state flags for "stay".
|
|
charmInfo->SetCommandState(COMMAND_STAY);
|
|
charmInfo->SetIsCommandAttack(false);
|
|
charmInfo->SetIsCommandFollow(false);
|
|
charmInfo->SetIsFollowing(false);
|
|
charmInfo->SetIsReturning(false);
|
|
charmInfo->SetIsAtStay(!controlledMotion);
|
|
charmInfo->SaveStayPosition(controlledMotion);
|
|
if (target->ToPet())
|
|
target->ToPet()->ClearCastWhenWillAvailable();
|
|
|
|
charmInfo->SetForcedSpell(0);
|
|
charmInfo->SetForcedTargetGUID();
|
|
}
|
|
}
|
|
if (sPlayerbotAIConfig->petChatCommandDebug == 1)
|
|
{
|
|
std::string text = sPlayerbotTextMgr->GetBotTextOrDefault(
|
|
"pet_stay_success", "Pet commanded to stay.", {});
|
|
botAI->TellMaster(text);
|
|
}
|
|
return true;
|
|
}
|
|
// Unknown command: show usage instructions and return.
|
|
else
|
|
{
|
|
std::string text = sPlayerbotTextMgr->GetBotTextOrDefault(
|
|
"pet_unknown_command_error", "Unknown pet command: %param. Use: pet <aggressive|defensive|passive|stance|attack|follow|stay>",
|
|
{{"param", param}});
|
|
botAI->TellError(text);
|
|
return false;
|
|
}
|
|
|
|
// For stance commands, apply the chosen stance to all targets.
|
|
for (Creature* target : targets)
|
|
{
|
|
target->SetReactState(react);
|
|
CharmInfo* charmInfo = target->GetCharmInfo();
|
|
if (charmInfo)
|
|
charmInfo->SetPlayerReactState(react);
|
|
}
|
|
|
|
// Inform the master of the new stance if debug is enabled.
|
|
if (sPlayerbotAIConfig->petChatCommandDebug == 1)
|
|
{
|
|
std::string text = sPlayerbotTextMgr->GetBotTextOrDefault(
|
|
"pet_stance_set_success", "Pet stance set to %stance.",
|
|
{{"stance", stanceText}});
|
|
botAI->TellMaster(text);
|
|
}
|
|
|
|
return true;
|
|
} |