Files
mod-playerbots/src/strategy/actions/RpgSubActions.cpp
2022-09-22 16:08:21 -06:00

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;
}