mirror of
https://github.com/mod-playerbots/mod-playerbots
synced 2025-11-29 15:58:20 +08:00
500 lines
13 KiB
C++
500 lines
13 KiB
C++
/*
|
|
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
|
|
*/
|
|
|
|
#include "RpgSubActions.h"
|
|
#include "ChooseRpgTargetAction.h"
|
|
#include "GuildCreateActions.h"
|
|
#include "EmoteAction.h"
|
|
#include "Formations.h"
|
|
#include "GossipDef.h"
|
|
#include "LastMovementValue.h"
|
|
#include "MovementActions.h"
|
|
#include "Playerbots.h"
|
|
#include "PossibleRpgTargetsValue.h"
|
|
#include "SocialMgr.h"
|
|
|
|
void RpgHelper::OnExecute(std::string nextAction)
|
|
{
|
|
if (botAI->HasRealPlayerMaster() && nextAction == "rpg")
|
|
nextAction = "rpg cancel";
|
|
|
|
SET_AI_VALUE(std::string, "next rpg action", nextAction);
|
|
}
|
|
|
|
void RpgHelper::BeforeExecute()
|
|
{
|
|
OnExecute();
|
|
|
|
bot->SetTarget(guidP());
|
|
|
|
setFacingTo(guidP());
|
|
}
|
|
|
|
void RpgHelper::AfterExecute(bool doDelay, bool waitForGroup)
|
|
{
|
|
OnExecute();
|
|
|
|
bot->SetTarget(guidP());
|
|
|
|
setFacingTo(guidP());
|
|
|
|
if (doDelay)
|
|
setDelay(waitForGroup);
|
|
|
|
setFacing(guidP());
|
|
}
|
|
|
|
GuidPosition RpgHelper::guidP()
|
|
{
|
|
return AI_VALUE(GuidPosition, "rpg target");
|
|
}
|
|
|
|
ObjectGuid RpgHelper::guid()
|
|
{
|
|
return (ObjectGuid)guidP();
|
|
}
|
|
|
|
bool RpgHelper::InRange()
|
|
{
|
|
return guidP() ? (guidP().sqDistance2d(bot) < INTERACTION_DISTANCE * INTERACTION_DISTANCE) : false;
|
|
}
|
|
|
|
void RpgHelper::setFacingTo(GuidPosition guidPosition)
|
|
{
|
|
bot->SetFacingTo(guidPosition.getAngleTo(bot)+ static_cast<float>(M_PI));
|
|
}
|
|
|
|
void RpgHelper::setFacing(GuidPosition guidPosition)
|
|
{
|
|
if (!guidPosition.IsUnit())
|
|
return;
|
|
|
|
if (guidPosition.IsPlayer())
|
|
return;
|
|
|
|
// Unit* unit = guidPosition.GetUnit();
|
|
|
|
// unit->SetFacingTo(unit->GetAngle(bot));
|
|
}
|
|
|
|
void RpgHelper::setDelay(bool waitForGroup)
|
|
{
|
|
if (!botAI->HasRealPlayerMaster() || (waitForGroup && botAI->GetGroupMaster() == bot && bot->GetGroup()))
|
|
botAI->SetNextCheckDelay(sPlayerbotAIConfig->rpgDelay);
|
|
else
|
|
botAI->SetNextCheckDelay(sPlayerbotAIConfig->rpgDelay / 5);
|
|
}
|
|
|
|
bool RpgSubAction::isPossible()
|
|
{
|
|
return rpg->guidP() && rpg->guidP().GetWorldObject();
|
|
}
|
|
|
|
bool RpgSubAction::isUseful()
|
|
{
|
|
return rpg->InRange();
|
|
}
|
|
|
|
bool RpgSubAction::Execute(Event event)
|
|
{
|
|
bool doAction = botAI->DoSpecificAction(ActionName(), ActionEvent(event), true);
|
|
rpg->AfterExecute(doAction, true);
|
|
return doAction;
|
|
}
|
|
|
|
std::string const RpgSubAction::ActionName()
|
|
{
|
|
return "none";
|
|
}
|
|
|
|
Event RpgSubAction::ActionEvent(Event event)
|
|
{
|
|
return event;
|
|
}
|
|
|
|
bool RpgStayAction::isUseful()
|
|
{
|
|
return rpg->InRange() && !botAI->HasRealPlayerMaster();
|
|
}
|
|
|
|
bool RpgStayAction::Execute(Event event)
|
|
{
|
|
bot->PlayerTalkClass->SendCloseGossip();
|
|
|
|
rpg->AfterExecute();
|
|
return true;
|
|
}
|
|
|
|
bool RpgWorkAction::isUseful()
|
|
{
|
|
return rpg->InRange() && !botAI->HasRealPlayerMaster();
|
|
}
|
|
|
|
bool RpgWorkAction::Execute(Event event)
|
|
{
|
|
bot->HandleEmoteCommand(EMOTE_STATE_USE_STANDING);
|
|
rpg->AfterExecute();
|
|
return true;
|
|
}
|
|
|
|
bool RpgEmoteAction::isUseful()
|
|
{
|
|
return rpg->InRange() && !botAI->HasRealPlayerMaster();
|
|
}
|
|
|
|
bool RpgEmoteAction::Execute(Event event)
|
|
{
|
|
uint32 type = TalkAction::GetRandomEmote(rpg->guidP().GetUnit());
|
|
|
|
WorldPacket p1;
|
|
p1 << rpg->guid();
|
|
bot->GetSession()->HandleGossipHelloOpcode(p1);
|
|
|
|
bot->HandleEmoteCommand(type);
|
|
|
|
rpg->AfterExecute();
|
|
|
|
return true;
|
|
}
|
|
|
|
bool RpgCancelAction::Execute(Event event)
|
|
{
|
|
RESET_AI_VALUE(GuidPosition, "rpg target");
|
|
rpg->OnExecute("");
|
|
return true;
|
|
}
|
|
|
|
bool RpgTaxiAction::isUseful()
|
|
{
|
|
return rpg->InRange() && !botAI->HasRealPlayerMaster();
|
|
}
|
|
|
|
bool RpgTaxiAction::Execute(Event event)
|
|
{
|
|
GuidPosition guidP = rpg->guidP();
|
|
|
|
WorldPacket emptyPacket;
|
|
bot->GetSession()->HandleCancelMountAuraOpcode(emptyPacket);
|
|
|
|
uint32 node = sObjectMgr->GetNearestTaxiNode(guidP.getX(), guidP.getY(), guidP.getZ(), guidP.getMapId(), bot->GetTeamId());
|
|
|
|
std::vector<uint32> nodes;
|
|
for (uint32 i = 0; i < sTaxiPathStore.GetNumRows(); ++i)
|
|
{
|
|
TaxiPathEntry const* entry = sTaxiPathStore.LookupEntry(i);
|
|
if (entry && entry->from == node && (bot->m_taxi.IsTaximaskNodeKnown(entry->to) || bot->isTaxiCheater()))
|
|
{
|
|
nodes.push_back(i);
|
|
}
|
|
}
|
|
|
|
if (nodes.empty())
|
|
{
|
|
LOG_ERROR("playerbots", "Bot {} - No flight paths available", bot->GetName());
|
|
return false;
|
|
}
|
|
|
|
uint32 path = nodes[urand(0, nodes.size() - 1)];
|
|
uint32 money = bot->GetMoney();
|
|
bot->SetMoney(money + 100000);
|
|
|
|
TaxiPathEntry const* entry = sTaxiPathStore.LookupEntry(path);
|
|
if (!entry)
|
|
return false;
|
|
|
|
TaxiNodesEntry const* nodeFrom = sTaxiNodesStore.LookupEntry(entry->from);
|
|
TaxiNodesEntry const* nodeTo = sTaxiNodesStore.LookupEntry(entry->to);
|
|
|
|
Creature* flightMaster = bot->GetNPCIfCanInteractWith(guidP, UNIT_NPC_FLAG_FLIGHTMASTER);
|
|
if (!flightMaster)
|
|
{
|
|
LOG_ERROR("playerbots", "Bot {} cannot talk to flightmaster ({} location available)", bot->GetName(), nodes.size());
|
|
return false;
|
|
}
|
|
|
|
if (!bot->ActivateTaxiPathTo({ entry->from, entry->to }, flightMaster, 0))
|
|
{
|
|
LOG_ERROR("playerbots", "Bot {} cannot fly {} ({} location available)", bot->GetName(), path, nodes.size());
|
|
return false;
|
|
}
|
|
|
|
LOG_INFO("playerbots", "Bot {} <{}> is flying from {} to {} ({} location available)",
|
|
bot->GetGUID().ToString().c_str(), bot->GetName(), nodeFrom->name[0], nodeTo->name[0], nodes.size());
|
|
|
|
bot->SetMoney(money);
|
|
|
|
rpg->AfterExecute();
|
|
|
|
return true;
|
|
}
|
|
|
|
bool RpgDiscoverAction::Execute(Event event)
|
|
{
|
|
GuidPosition guidP = rpg->guidP();
|
|
|
|
uint32 node = sObjectMgr->GetNearestTaxiNode(guidP.getX(), guidP.getY(), guidP.getZ(), guidP.getMapId(), bot->GetTeamId());
|
|
|
|
if (!node)
|
|
return false;
|
|
|
|
Creature* flightMaster = bot->GetNPCIfCanInteractWith(guidP, UNIT_NPC_FLAG_FLIGHTMASTER);
|
|
if (!flightMaster)
|
|
return false;
|
|
|
|
return bot->GetSession()->SendLearnNewTaxiNode(flightMaster);
|
|
}
|
|
|
|
std::string const RpgStartQuestAction::ActionName()
|
|
{
|
|
return "accept all quests";
|
|
}
|
|
|
|
Event RpgStartQuestAction::ActionEvent(Event event)
|
|
{
|
|
WorldPacket p(CMSG_QUESTGIVER_ACCEPT_QUEST);
|
|
p << rpg->guid();
|
|
p.rpos(0);
|
|
return Event("rpg action", p);
|
|
}
|
|
|
|
std::string const RpgEndQuestAction::ActionName()
|
|
{
|
|
return "talk to quest giver";
|
|
}
|
|
|
|
Event RpgEndQuestAction::ActionEvent(Event event)
|
|
{
|
|
WorldPacket p(CMSG_QUESTGIVER_COMPLETE_QUEST);
|
|
p << rpg->guid();
|
|
p.rpos(0);
|
|
return Event("rpg action", p);
|
|
}
|
|
|
|
std::string const RpgBuyAction::ActionName()
|
|
{
|
|
return "buy";
|
|
}
|
|
|
|
Event RpgBuyAction::ActionEvent(Event event)
|
|
{
|
|
return Event("rpg action", "vendor");
|
|
}
|
|
|
|
std::string const RpgSellAction::ActionName()
|
|
{
|
|
return "sell";
|
|
}
|
|
|
|
Event RpgSellAction::ActionEvent(Event event)
|
|
{
|
|
return Event("rpg action", "vendor");
|
|
}
|
|
|
|
std::string const RpgRepairAction::ActionName()
|
|
{
|
|
return "repair";
|
|
}
|
|
|
|
std::string const RpgTrainAction::ActionName()
|
|
{
|
|
return "trainer";
|
|
}
|
|
|
|
bool RpgHealAction::Execute(Event event)
|
|
{
|
|
bool retVal = false;
|
|
|
|
switch (bot->getClass())
|
|
{
|
|
case CLASS_PRIEST:
|
|
retVal = botAI->DoSpecificAction("lesser heal on party", Event(), true);
|
|
break;
|
|
case CLASS_DRUID:
|
|
retVal=botAI->DoSpecificAction("healing touch on party", Event(), true);
|
|
break;
|
|
case CLASS_PALADIN:
|
|
retVal=botAI->DoSpecificAction("holy light on party", Event(), true);
|
|
break;
|
|
case CLASS_SHAMAN:
|
|
retVal=botAI->DoSpecificAction("healing wave on party", Event(), true);
|
|
break;
|
|
}
|
|
|
|
return retVal;
|
|
}
|
|
|
|
std::string const RpgHomeBindAction::ActionName()
|
|
{
|
|
return "home";
|
|
}
|
|
|
|
std::string const RpgQueueBgAction::ActionName()
|
|
{
|
|
SET_AI_VALUE(uint32, "bg type", (uint32) AI_VALUE(BattlegroundTypeId, "rpg bg type"));
|
|
return "free bg join";
|
|
}
|
|
|
|
std::string const RpgBuyPetitionAction::ActionName()
|
|
{
|
|
return "buy petition";
|
|
}
|
|
|
|
std::string const RpgUseAction::ActionName()
|
|
{
|
|
return "use";
|
|
}
|
|
|
|
Event RpgUseAction::ActionEvent(Event event)
|
|
{
|
|
return Event("rpg action", chat->FormatWorldobject(rpg->guidP().GetWorldObject()));
|
|
}
|
|
|
|
std::string const RpgSpellAction::ActionName()
|
|
{
|
|
return "cast random spell";
|
|
}
|
|
|
|
Event RpgSpellAction::ActionEvent(Event event)
|
|
{
|
|
return Event("rpg action", chat->FormatWorldobject(rpg->guidP().GetWorldObject()));
|
|
}
|
|
|
|
std::string const RpgCraftAction::ActionName()
|
|
{
|
|
return "craft random item";
|
|
}
|
|
|
|
Event RpgCraftAction::ActionEvent(Event event)
|
|
{
|
|
return Event("rpg action", chat->FormatWorldobject(rpg->guidP().GetWorldObject()));
|
|
}
|
|
|
|
std::vector<Item*> RpgTradeUsefulAction::CanGiveItems(GuidPosition guidPosition)
|
|
{
|
|
Player* player = guidPosition.GetPlayer();
|
|
|
|
std::vector<Item*> giveItems;
|
|
|
|
if (botAI->HasActivePlayerMaster() || !GET_PLAYERBOT_AI(player))
|
|
return giveItems;
|
|
|
|
std::vector<ItemUsage> myUsages = { ITEM_USAGE_NONE , ITEM_USAGE_VENDOR, ITEM_USAGE_AH, ITEM_USAGE_DISENCHANT };
|
|
|
|
for (auto& myUsage : myUsages)
|
|
{
|
|
std::vector<Item*> myItems = AI_VALUE2(std::vector<Item*>, "inventory items", "usage " + std::to_string(myUsage));
|
|
std::reverse(myItems.begin(), myItems.end());
|
|
|
|
for (auto& item : myItems)
|
|
{
|
|
if (!item->CanBeTraded())
|
|
continue;
|
|
|
|
if (bot->GetTradeData() && bot->GetTradeData()->HasItem(item->GetGUID()))
|
|
continue;
|
|
|
|
ItemUsage otherUsage = PAI_VALUE2(ItemUsage, "item usage", item->GetEntry());
|
|
|
|
if (std::find(myUsages.begin(), myUsages.end(), otherUsage) == myUsages.end())
|
|
giveItems.push_back(item);
|
|
}
|
|
}
|
|
|
|
return giveItems;
|
|
}
|
|
|
|
bool RpgTradeUsefulAction::Execute(Event event)
|
|
{
|
|
GuidPosition guidP = AI_VALUE(GuidPosition, "rpg target");
|
|
|
|
Player* player = guidP.GetPlayer();
|
|
|
|
if (!player)
|
|
return false;
|
|
|
|
std::vector<Item*> items = CanGiveItems(guidP);
|
|
|
|
if (items.empty())
|
|
return false;
|
|
|
|
Item* item = items.front();
|
|
|
|
std::ostringstream param;
|
|
|
|
param << chat->FormatWorldobject(player);
|
|
param << " ";
|
|
param << chat->FormatItem(item->GetTemplate());
|
|
|
|
bool hasTraded = botAI->DoSpecificAction("trade", Event("rpg action", param.str().c_str()), true);
|
|
|
|
if (hasTraded || bot->GetTradeData())
|
|
{
|
|
if (bot->GetTradeData() && bot->GetTradeData()->HasItem(item->GetGUID()))
|
|
{
|
|
if (bot->GetGroup() && bot->GetGroup()->IsMember(guidP) && botAI->HasRealPlayerMaster())
|
|
botAI->TellMasterNoFacing("You can use this " + chat->FormatItem(item->GetTemplate()) + " better than me, " + guidP.GetPlayer()->GetName() /*chat->FormatWorldobject(guidP.GetPlayer())*/ + ".");
|
|
else
|
|
bot->Say("You can use this " + chat->FormatItem(item->GetTemplate()) + " better than me, " + player->GetName() /*chat->FormatWorldobject(player)*/ + ".", (bot->GetTeamId() == TEAM_ALLIANCE ? LANG_COMMON : LANG_ORCISH));
|
|
|
|
if (!urand(0, 4) || items.size() < 2)
|
|
{
|
|
//bot->Say("End trade with" + chat->FormatWorldobject(player), (bot->GetTeamId() == TEAM_ALLIANCE ? LANG_COMMON : LANG_ORCISH));
|
|
WorldPacket p;
|
|
uint32 status = TRADE_STATUS_TRADE_ACCEPT;
|
|
p << status;
|
|
bot->GetSession()->HandleAcceptTradeOpcode(p);
|
|
}
|
|
}
|
|
else
|
|
bot->Say("Start trade with" + chat->FormatWorldobject(player), (bot->GetTeamId() == TEAM_ALLIANCE ? LANG_COMMON : LANG_ORCISH));
|
|
|
|
botAI->SetNextCheckDelay(sPlayerbotAIConfig->rpgDelay);
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool RpgDuelAction::isUseful()
|
|
{
|
|
// do not offer duel in non pvp areas
|
|
if (sPlayerbotAIConfig->IsInPvpProhibitedZone(bot->GetAreaId()))
|
|
return false;
|
|
|
|
// Players can only fight a duel with each other outside (=not inside dungeons and not in capital cities)
|
|
AreaTableEntry const* casterAreaEntry = sAreaTableStore.LookupEntry(bot->GetAreaId());
|
|
if (casterAreaEntry && !(casterAreaEntry->flags & AREA_FLAG_ALLOW_DUELS))
|
|
{
|
|
// Dueling isn't allowed here
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool RpgDuelAction::Execute(Event event)
|
|
{
|
|
GuidPosition guidP = AI_VALUE(GuidPosition, "rpg target");
|
|
|
|
Player* player = guidP.GetPlayer();
|
|
|
|
if (!player)
|
|
return false;
|
|
|
|
return botAI->DoSpecificAction("cast custom spell", Event("rpg action", chat->FormatWorldobject(player) + " 7266"), true);
|
|
}
|
|
|
|
|
|
bool RpgMountAnimAction::isUseful()
|
|
{
|
|
return AI_VALUE2(bool, "mounted", "self target") && !AI_VALUE2(bool, "moving", "self target");
|
|
}
|
|
|
|
bool RpgMountAnimAction::Execute(Event event)
|
|
{
|
|
WorldPacket p;
|
|
bot->GetSession()->HandleMountSpecialAnimOpcode(p);
|
|
|
|
return true;
|
|
} |