mirror of
https://github.com/mod-playerbots/mod-playerbots.git
synced 2026-02-17 09:04:36 +00:00
[HOT FIX] MS build issues regarding folder / command lenght usage or rc.exe (#2038)
This commit is contained in:
443
src/Ai/World/Rpg/Action/NewRpgAction.cpp
Normal file
443
src/Ai/World/Rpg/Action/NewRpgAction.cpp
Normal file
@@ -0,0 +1,443 @@
|
||||
#include "NewRpgAction.h"
|
||||
|
||||
#include <cmath>
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
|
||||
#include "BroadcastHelper.h"
|
||||
#include "ChatHelper.h"
|
||||
#include "DBCStores.h"
|
||||
#include "G3D/Vector2.h"
|
||||
#include "GossipDef.h"
|
||||
#include "IVMapMgr.h"
|
||||
#include "NewRpgInfo.h"
|
||||
#include "NewRpgStrategy.h"
|
||||
#include "Object.h"
|
||||
#include "ObjectAccessor.h"
|
||||
#include "ObjectDefines.h"
|
||||
#include "ObjectGuid.h"
|
||||
#include "ObjectMgr.h"
|
||||
#include "PathGenerator.h"
|
||||
#include "Player.h"
|
||||
#include "PlayerbotAI.h"
|
||||
#include "Playerbots.h"
|
||||
#include "Position.h"
|
||||
#include "QuestDef.h"
|
||||
#include "Random.h"
|
||||
#include "RandomPlayerbotMgr.h"
|
||||
#include "SharedDefines.h"
|
||||
#include "StatsWeightCalculator.h"
|
||||
#include "Timer.h"
|
||||
#include "TravelMgr.h"
|
||||
#include "World.h"
|
||||
|
||||
bool TellRpgStatusAction::Execute(Event event)
|
||||
{
|
||||
Player* owner = event.getOwner();
|
||||
if (!owner)
|
||||
return false;
|
||||
std::string out = botAI->rpgInfo.ToString();
|
||||
bot->Whisper(out.c_str(), LANG_UNIVERSAL, owner);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool StartRpgDoQuestAction::Execute(Event event)
|
||||
{
|
||||
Player* owner = event.getOwner();
|
||||
if (!owner)
|
||||
return false;
|
||||
|
||||
std::string const text = event.getParam();
|
||||
PlayerbotChatHandler ch(owner);
|
||||
uint32 questId = ch.extractQuestId(text);
|
||||
const Quest* quest = sObjectMgr->GetQuestTemplate(questId);
|
||||
if (quest)
|
||||
{
|
||||
botAI->rpgInfo.ChangeToDoQuest(questId, quest);
|
||||
bot->Whisper("Start to do quest " + std::to_string(questId), LANG_UNIVERSAL, owner);
|
||||
return true;
|
||||
}
|
||||
bot->Whisper("Invalid quest " + text, LANG_UNIVERSAL, owner);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool NewRpgStatusUpdateAction::Execute(Event event)
|
||||
{
|
||||
NewRpgInfo& info = botAI->rpgInfo;
|
||||
switch (info.status)
|
||||
{
|
||||
case RPG_IDLE:
|
||||
{
|
||||
return RandomChangeStatus({RPG_GO_CAMP, RPG_GO_GRIND, RPG_WANDER_RANDOM, RPG_WANDER_NPC, RPG_DO_QUEST,
|
||||
RPG_TRAVEL_FLIGHT, RPG_REST});
|
||||
}
|
||||
case RPG_GO_GRIND:
|
||||
{
|
||||
WorldPosition& originalPos = info.go_grind.pos;
|
||||
assert(info.go_grind.pos != WorldPosition());
|
||||
// GO_GRIND -> WANDER_RANDOM
|
||||
if (bot->GetExactDist(originalPos) < 10.0f)
|
||||
{
|
||||
info.ChangeToWanderRandom();
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case RPG_GO_CAMP:
|
||||
{
|
||||
WorldPosition& originalPos = info.go_camp.pos;
|
||||
assert(info.go_camp.pos != WorldPosition());
|
||||
// GO_CAMP -> WANDER_NPC
|
||||
if (bot->GetExactDist(originalPos) < 10.0f)
|
||||
{
|
||||
info.ChangeToWanderNpc();
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case RPG_WANDER_RANDOM:
|
||||
{
|
||||
// WANDER_RANDOM -> IDLE
|
||||
if (info.HasStatusPersisted(statusWanderRandomDuration))
|
||||
{
|
||||
info.ChangeToIdle();
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case RPG_WANDER_NPC:
|
||||
{
|
||||
if (info.HasStatusPersisted(statusWanderNpcDuration))
|
||||
{
|
||||
info.ChangeToIdle();
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case RPG_DO_QUEST:
|
||||
{
|
||||
// DO_QUEST -> IDLE
|
||||
if (info.HasStatusPersisted(statusDoQuestDuration))
|
||||
{
|
||||
info.ChangeToIdle();
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case RPG_TRAVEL_FLIGHT:
|
||||
{
|
||||
if (info.flight.inFlight && !bot->IsInFlight())
|
||||
{
|
||||
// flight arrival
|
||||
info.ChangeToIdle();
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case RPG_REST:
|
||||
{
|
||||
// REST -> IDLE
|
||||
if (info.HasStatusPersisted(statusRestDuration))
|
||||
{
|
||||
info.ChangeToIdle();
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool NewRpgGoGrindAction::Execute(Event event)
|
||||
{
|
||||
if (SearchQuestGiverAndAcceptOrReward())
|
||||
return true;
|
||||
|
||||
return MoveFarTo(botAI->rpgInfo.go_grind.pos);
|
||||
}
|
||||
|
||||
bool NewRpgGoCampAction::Execute(Event event)
|
||||
{
|
||||
if (SearchQuestGiverAndAcceptOrReward())
|
||||
return true;
|
||||
|
||||
return MoveFarTo(botAI->rpgInfo.go_camp.pos);
|
||||
}
|
||||
|
||||
bool NewRpgWanderRandomAction::Execute(Event event)
|
||||
{
|
||||
if (SearchQuestGiverAndAcceptOrReward())
|
||||
return true;
|
||||
|
||||
return MoveRandomNear();
|
||||
}
|
||||
|
||||
bool NewRpgWanderNpcAction::Execute(Event event)
|
||||
{
|
||||
NewRpgInfo& info = botAI->rpgInfo;
|
||||
if (!info.wander_npc.npcOrGo)
|
||||
{
|
||||
// No npc can be found, switch to IDLE
|
||||
ObjectGuid npcOrGo = ChooseNpcOrGameObjectToInteract();
|
||||
if (npcOrGo.IsEmpty())
|
||||
{
|
||||
info.ChangeToIdle();
|
||||
return true;
|
||||
}
|
||||
info.wander_npc.npcOrGo = npcOrGo;
|
||||
info.wander_npc.lastReach = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
WorldObject* object = ObjectAccessor::GetWorldObject(*bot, info.wander_npc.npcOrGo);
|
||||
if (object && IsWithinInteractionDist(object))
|
||||
{
|
||||
if (!info.wander_npc.lastReach)
|
||||
{
|
||||
info.wander_npc.lastReach = getMSTime();
|
||||
if (bot->CanInteractWithQuestGiver(object))
|
||||
InteractWithNpcOrGameObjectForQuest(info.wander_npc.npcOrGo);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (info.wander_npc.lastReach && GetMSTimeDiffToNow(info.wander_npc.lastReach) < npcStayTime)
|
||||
return false;
|
||||
|
||||
// has reached the npc for more than `npcStayTime`, select the next target
|
||||
info.wander_npc.npcOrGo = ObjectGuid();
|
||||
info.wander_npc.lastReach = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
return MoveWorldObjectTo(info.wander_npc.npcOrGo);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool NewRpgDoQuestAction::Execute(Event event)
|
||||
{
|
||||
if (SearchQuestGiverAndAcceptOrReward())
|
||||
return true;
|
||||
|
||||
NewRpgInfo& info = botAI->rpgInfo;
|
||||
uint32 questId = RPG_INFO(quest, questId);
|
||||
const Quest* quest = RPG_INFO(quest, quest);
|
||||
uint8 questStatus = bot->GetQuestStatus(questId);
|
||||
switch (questStatus)
|
||||
{
|
||||
case QUEST_STATUS_INCOMPLETE:
|
||||
return DoIncompleteQuest();
|
||||
case QUEST_STATUS_COMPLETE:
|
||||
return DoCompletedQuest();
|
||||
default:
|
||||
break;
|
||||
}
|
||||
botAI->rpgInfo.ChangeToIdle();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool NewRpgDoQuestAction::DoIncompleteQuest()
|
||||
{
|
||||
uint32 questId = RPG_INFO(do_quest, questId);
|
||||
if (botAI->rpgInfo.do_quest.pos != WorldPosition())
|
||||
{
|
||||
/// @TODO: extract to a new function
|
||||
int32 currentObjective = botAI->rpgInfo.do_quest.objectiveIdx;
|
||||
// check if the objective has completed
|
||||
Quest const* quest = sObjectMgr->GetQuestTemplate(questId);
|
||||
const QuestStatusData& q_status = bot->getQuestStatusMap().at(questId);
|
||||
bool completed = true;
|
||||
if (currentObjective < QUEST_OBJECTIVES_COUNT)
|
||||
{
|
||||
if (q_status.CreatureOrGOCount[currentObjective] < quest->RequiredNpcOrGoCount[currentObjective])
|
||||
completed = false;
|
||||
}
|
||||
else if (currentObjective < QUEST_OBJECTIVES_COUNT + QUEST_ITEM_OBJECTIVES_COUNT)
|
||||
{
|
||||
if (q_status.ItemCount[currentObjective - QUEST_OBJECTIVES_COUNT] <
|
||||
quest->RequiredItemCount[currentObjective - QUEST_OBJECTIVES_COUNT])
|
||||
completed = false;
|
||||
}
|
||||
// the current objective is completed, clear and find a new objective later
|
||||
if (completed)
|
||||
{
|
||||
botAI->rpgInfo.do_quest.lastReachPOI = 0;
|
||||
botAI->rpgInfo.do_quest.pos = WorldPosition();
|
||||
botAI->rpgInfo.do_quest.objectiveIdx = 0;
|
||||
}
|
||||
}
|
||||
if (botAI->rpgInfo.do_quest.pos == WorldPosition())
|
||||
{
|
||||
std::vector<POIInfo> poiInfo;
|
||||
if (!GetQuestPOIPosAndObjectiveIdx(questId, poiInfo))
|
||||
{
|
||||
// can't find a poi pos to go, stop doing quest for now
|
||||
botAI->rpgInfo.ChangeToIdle();
|
||||
return true;
|
||||
}
|
||||
uint32 rndIdx = urand(0, poiInfo.size() - 1);
|
||||
G3D::Vector2 nearestPoi = poiInfo[rndIdx].pos;
|
||||
int32 objectiveIdx = poiInfo[rndIdx].objectiveIdx;
|
||||
|
||||
float dx = nearestPoi.x, dy = nearestPoi.y;
|
||||
|
||||
// z = MAX_HEIGHT as we do not know accurate z
|
||||
float dz = std::max(bot->GetMap()->GetHeight(dx, dy, MAX_HEIGHT), bot->GetMap()->GetWaterLevel(dx, dy));
|
||||
|
||||
// double check for GetQuestPOIPosAndObjectiveIdx
|
||||
if (dz == INVALID_HEIGHT || dz == VMAP_INVALID_HEIGHT_VALUE)
|
||||
return false;
|
||||
|
||||
WorldPosition pos(bot->GetMapId(), dx, dy, dz);
|
||||
botAI->rpgInfo.do_quest.lastReachPOI = 0;
|
||||
botAI->rpgInfo.do_quest.pos = pos;
|
||||
botAI->rpgInfo.do_quest.objectiveIdx = objectiveIdx;
|
||||
}
|
||||
|
||||
if (bot->GetDistance(botAI->rpgInfo.do_quest.pos) > 10.0f && !botAI->rpgInfo.do_quest.lastReachPOI)
|
||||
{
|
||||
return MoveFarTo(botAI->rpgInfo.do_quest.pos);
|
||||
}
|
||||
// Now we are near the quest objective
|
||||
// kill mobs and looting quest should be done automatically by grind strategy
|
||||
|
||||
if (!botAI->rpgInfo.do_quest.lastReachPOI)
|
||||
{
|
||||
botAI->rpgInfo.do_quest.lastReachPOI = getMSTime();
|
||||
return true;
|
||||
}
|
||||
// stayed at this POI for more than 5 minutes
|
||||
if (GetMSTimeDiffToNow(botAI->rpgInfo.do_quest.lastReachPOI) >= poiStayTime)
|
||||
{
|
||||
bool hasProgression = false;
|
||||
int32 currentObjective = botAI->rpgInfo.do_quest.objectiveIdx;
|
||||
// check if the objective has progression
|
||||
Quest const* quest = sObjectMgr->GetQuestTemplate(questId);
|
||||
const QuestStatusData& q_status = bot->getQuestStatusMap().at(questId);
|
||||
if (currentObjective < QUEST_OBJECTIVES_COUNT)
|
||||
{
|
||||
if (q_status.CreatureOrGOCount[currentObjective] != 0 && quest->RequiredNpcOrGoCount[currentObjective])
|
||||
hasProgression = true;
|
||||
}
|
||||
else if (currentObjective < QUEST_OBJECTIVES_COUNT + QUEST_ITEM_OBJECTIVES_COUNT)
|
||||
{
|
||||
if (q_status.ItemCount[currentObjective - QUEST_OBJECTIVES_COUNT] != 0 &&
|
||||
quest->RequiredItemCount[currentObjective - QUEST_OBJECTIVES_COUNT])
|
||||
hasProgression = true;
|
||||
}
|
||||
if (!hasProgression)
|
||||
{
|
||||
// we has reach the poi for more than 5 mins but no progession
|
||||
// may not be able to complete this quest, marked as abandoned
|
||||
/// @TODO: It may be better to make lowPriorityQuest a global set shared by all bots (or saved in db)
|
||||
botAI->lowPriorityQuest.insert(questId);
|
||||
botAI->rpgStatistic.questAbandoned++;
|
||||
LOG_DEBUG("playerbots", "[New RPG] {} marked as abandoned quest {}", bot->GetName(), questId);
|
||||
botAI->rpgInfo.ChangeToIdle();
|
||||
return true;
|
||||
}
|
||||
// clear and select another poi later
|
||||
botAI->rpgInfo.do_quest.lastReachPOI = 0;
|
||||
botAI->rpgInfo.do_quest.pos = WorldPosition();
|
||||
botAI->rpgInfo.do_quest.objectiveIdx = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
return MoveRandomNear(20.0f);
|
||||
}
|
||||
|
||||
bool NewRpgDoQuestAction::DoCompletedQuest()
|
||||
{
|
||||
uint32 questId = RPG_INFO(quest, questId);
|
||||
const Quest* quest = RPG_INFO(quest, quest);
|
||||
|
||||
if (RPG_INFO(quest, objectiveIdx) != -1)
|
||||
{
|
||||
// if quest is completed, back to poi with -1 idx to reward
|
||||
BroadcastHelper::BroadcastQuestUpdateComplete(botAI, bot, quest);
|
||||
botAI->rpgStatistic.questCompleted++;
|
||||
std::vector<POIInfo> poiInfo;
|
||||
if (!GetQuestPOIPosAndObjectiveIdx(questId, poiInfo, true))
|
||||
{
|
||||
// can't find a poi pos to reward, stop doing quest for now
|
||||
botAI->rpgInfo.ChangeToIdle();
|
||||
return false;
|
||||
}
|
||||
assert(poiInfo.size() > 0);
|
||||
// now we get the place to get rewarded
|
||||
float dx = poiInfo[0].pos.x, dy = poiInfo[0].pos.y;
|
||||
// z = MAX_HEIGHT as we do not know accurate z
|
||||
float dz = std::max(bot->GetMap()->GetHeight(dx, dy, MAX_HEIGHT), bot->GetMap()->GetWaterLevel(dx, dy));
|
||||
|
||||
// double check for GetQuestPOIPosAndObjectiveIdx
|
||||
if (dz == INVALID_HEIGHT || dz == VMAP_INVALID_HEIGHT_VALUE)
|
||||
return false;
|
||||
|
||||
WorldPosition pos(bot->GetMapId(), dx, dy, dz);
|
||||
botAI->rpgInfo.do_quest.lastReachPOI = 0;
|
||||
botAI->rpgInfo.do_quest.pos = pos;
|
||||
botAI->rpgInfo.do_quest.objectiveIdx = -1;
|
||||
}
|
||||
|
||||
if (botAI->rpgInfo.do_quest.pos == WorldPosition())
|
||||
return false;
|
||||
|
||||
if (bot->GetDistance(botAI->rpgInfo.do_quest.pos) > 10.0f && !botAI->rpgInfo.do_quest.lastReachPOI)
|
||||
return MoveFarTo(botAI->rpgInfo.do_quest.pos);
|
||||
|
||||
// Now we are near the qoi of reward
|
||||
// the quest should be rewarded by SearchQuestGiverAndAcceptOrReward
|
||||
if (!botAI->rpgInfo.do_quest.lastReachPOI)
|
||||
{
|
||||
botAI->rpgInfo.do_quest.lastReachPOI = getMSTime();
|
||||
return true;
|
||||
}
|
||||
// stayed at this POI for more than 5 minutes
|
||||
if (GetMSTimeDiffToNow(botAI->rpgInfo.do_quest.lastReachPOI) >= poiStayTime)
|
||||
{
|
||||
// e.g. Can not reward quest to gameobjects
|
||||
/// @TODO: It may be better to make lowPriorityQuest a global set shared by all bots (or saved in db)
|
||||
botAI->lowPriorityQuest.insert(questId);
|
||||
botAI->rpgStatistic.questAbandoned++;
|
||||
LOG_DEBUG("playerbots", "[New RPG] {} marked as abandoned quest {}", bot->GetName(), questId);
|
||||
botAI->rpgInfo.ChangeToIdle();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool NewRpgTravelFlightAction::Execute(Event event)
|
||||
{
|
||||
if (bot->IsInFlight())
|
||||
{
|
||||
botAI->rpgInfo.flight.inFlight = true;
|
||||
return false;
|
||||
}
|
||||
Creature* flightMaster = ObjectAccessor::GetCreature(*bot, botAI->rpgInfo.flight.fromFlightMaster);
|
||||
if (!flightMaster || !flightMaster->IsAlive())
|
||||
{
|
||||
botAI->rpgInfo.ChangeToIdle();
|
||||
return true;
|
||||
}
|
||||
const TaxiNodesEntry* entry = sTaxiNodesStore.LookupEntry(botAI->rpgInfo.flight.toNode);
|
||||
if (bot->GetDistance(flightMaster) > INTERACTION_DISTANCE)
|
||||
{
|
||||
return MoveFarTo(flightMaster);
|
||||
}
|
||||
std::vector<uint32> nodes = {botAI->rpgInfo.flight.fromNode, botAI->rpgInfo.flight.toNode};
|
||||
|
||||
botAI->RemoveShapeshift();
|
||||
if (bot->IsMounted())
|
||||
{
|
||||
bot->Dismount();
|
||||
}
|
||||
if (!bot->ActivateTaxiPathTo(nodes, flightMaster, 0))
|
||||
{
|
||||
LOG_DEBUG("playerbots", "[New RPG] {} active taxi path {} (from {} to {}) failed", bot->GetName(),
|
||||
flightMaster->GetEntry(), nodes[0], nodes[1]);
|
||||
botAI->rpgInfo.ChangeToIdle();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
106
src/Ai/World/Rpg/Action/NewRpgAction.h
Normal file
106
src/Ai/World/Rpg/Action/NewRpgAction.h
Normal file
@@ -0,0 +1,106 @@
|
||||
#ifndef _PLAYERBOT_NEWRPGACTION_H
|
||||
#define _PLAYERBOT_NEWRPGACTION_H
|
||||
|
||||
#include "Duration.h"
|
||||
#include "MovementActions.h"
|
||||
#include "NewRpgBaseAction.h"
|
||||
#include "NewRpgInfo.h"
|
||||
#include "NewRpgStrategy.h"
|
||||
#include "Object.h"
|
||||
#include "ObjectDefines.h"
|
||||
#include "ObjectGuid.h"
|
||||
#include "PlayerbotAI.h"
|
||||
#include "QuestDef.h"
|
||||
#include "TravelMgr.h"
|
||||
|
||||
class TellRpgStatusAction : public Action
|
||||
{
|
||||
public:
|
||||
TellRpgStatusAction(PlayerbotAI* botAI) : Action(botAI, "rpg status") {}
|
||||
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class StartRpgDoQuestAction : public Action
|
||||
{
|
||||
public:
|
||||
StartRpgDoQuestAction(PlayerbotAI* botAI) : Action(botAI, "start rpg do quest") {}
|
||||
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class NewRpgStatusUpdateAction : public NewRpgBaseAction
|
||||
{
|
||||
public:
|
||||
NewRpgStatusUpdateAction(PlayerbotAI* botAI) : NewRpgBaseAction(botAI, "new rpg status update")
|
||||
{
|
||||
// int statusCount = RPG_STATUS_END - 1;
|
||||
|
||||
// transitionMat.resize(statusCount, std::vector<int>(statusCount, 0));
|
||||
|
||||
// transitionMat[RPG_IDLE][RPG_GO_GRIND] = 20;
|
||||
// transitionMat[RPG_IDLE][RPG_GO_CAMP] = 15;
|
||||
// transitionMat[RPG_IDLE][RPG_WANDER_NPC] = 30;
|
||||
// transitionMat[RPG_IDLE][RPG_DO_QUEST] = 35;
|
||||
}
|
||||
bool Execute(Event event) override;
|
||||
|
||||
protected:
|
||||
// static NewRpgStatusTransitionProb transitionMat;
|
||||
const int32 statusWanderNpcDuration = 5 * 60 * 1000;
|
||||
const int32 statusWanderRandomDuration = 5 * 60 * 1000;
|
||||
const int32 statusRestDuration = 30 * 1000;
|
||||
const int32 statusDoQuestDuration = 30 * 60 * 1000;
|
||||
};
|
||||
|
||||
class NewRpgGoGrindAction : public NewRpgBaseAction
|
||||
{
|
||||
public:
|
||||
NewRpgGoGrindAction(PlayerbotAI* botAI) : NewRpgBaseAction(botAI, "new rpg go grind") {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class NewRpgGoCampAction : public NewRpgBaseAction
|
||||
{
|
||||
public:
|
||||
NewRpgGoCampAction(PlayerbotAI* botAI) : NewRpgBaseAction(botAI, "new rpg go camp") {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class NewRpgWanderRandomAction : public NewRpgBaseAction
|
||||
{
|
||||
public:
|
||||
NewRpgWanderRandomAction(PlayerbotAI* botAI) : NewRpgBaseAction(botAI, "new rpg wander random") {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class NewRpgWanderNpcAction : public NewRpgBaseAction
|
||||
{
|
||||
public:
|
||||
NewRpgWanderNpcAction(PlayerbotAI* botAI) : NewRpgBaseAction(botAI, "new rpg move npcs") {}
|
||||
bool Execute(Event event) override;
|
||||
|
||||
const uint32 npcStayTime = 8 * 1000;
|
||||
};
|
||||
|
||||
class NewRpgDoQuestAction : public NewRpgBaseAction
|
||||
{
|
||||
public:
|
||||
NewRpgDoQuestAction(PlayerbotAI* botAI) : NewRpgBaseAction(botAI, "new rpg do quest") {}
|
||||
bool Execute(Event event) override;
|
||||
|
||||
protected:
|
||||
bool DoIncompleteQuest();
|
||||
bool DoCompletedQuest();
|
||||
|
||||
const uint32 poiStayTime = 5 * 60 * 1000;
|
||||
};
|
||||
|
||||
class NewRpgTravelFlightAction : public NewRpgBaseAction
|
||||
{
|
||||
public:
|
||||
NewRpgTravelFlightAction(PlayerbotAI* botAI) : NewRpgBaseAction(botAI, "new rpg travel flight") {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
#endif
|
||||
1231
src/Ai/World/Rpg/Action/NewRpgBaseAction.cpp
Normal file
1231
src/Ai/World/Rpg/Action/NewRpgBaseAction.cpp
Normal file
File diff suppressed because it is too large
Load Diff
67
src/Ai/World/Rpg/Action/NewRpgBaseAction.h
Normal file
67
src/Ai/World/Rpg/Action/NewRpgBaseAction.h
Normal file
@@ -0,0 +1,67 @@
|
||||
#ifndef _PLAYERBOT_NEWRPGBASEACTION_H
|
||||
#define _PLAYERBOT_NEWRPGBASEACTION_H
|
||||
|
||||
#include "Duration.h"
|
||||
#include "LastMovementValue.h"
|
||||
#include "MovementActions.h"
|
||||
#include "NewRpgInfo.h"
|
||||
#include "NewRpgStrategy.h"
|
||||
#include "Object.h"
|
||||
#include "ObjectDefines.h"
|
||||
#include "ObjectGuid.h"
|
||||
#include "PlayerbotAI.h"
|
||||
#include "QuestDef.h"
|
||||
#include "TravelMgr.h"
|
||||
|
||||
struct POIInfo
|
||||
{
|
||||
G3D::Vector2 pos;
|
||||
int32 objectiveIdx;
|
||||
};
|
||||
|
||||
/// A base (composition) class for all new rpg actions
|
||||
/// All functions that may be shared by multiple actions should be declared here
|
||||
/// And we should make all actions composable instead of inheritable
|
||||
class NewRpgBaseAction : public MovementAction
|
||||
{
|
||||
public:
|
||||
NewRpgBaseAction(PlayerbotAI* botAI, std::string name) : MovementAction(botAI, name) {}
|
||||
|
||||
protected:
|
||||
/* MOVEMENT RELATED */
|
||||
bool MoveFarTo(WorldPosition dest);
|
||||
bool MoveWorldObjectTo(ObjectGuid guid, float distance = INTERACTION_DISTANCE);
|
||||
bool MoveRandomNear(float moveStep = 50.0f, MovementPriority priority = MovementPriority::MOVEMENT_NORMAL);
|
||||
bool ForceToWait(uint32 duration, MovementPriority priority = MovementPriority::MOVEMENT_NORMAL);
|
||||
|
||||
/* QUEST RELATED CHECK */
|
||||
ObjectGuid ChooseNpcOrGameObjectToInteract(bool questgiverOnly = false, float distanceLimit = 0.0f);
|
||||
bool HasQuestToAcceptOrReward(WorldObject* object);
|
||||
bool InteractWithNpcOrGameObjectForQuest(ObjectGuid guid);
|
||||
bool CanInteractWithQuestGiver(Object* questGiver);
|
||||
bool IsWithinInteractionDist(Object* object);
|
||||
uint32 BestRewardIndex(Quest const* quest);
|
||||
bool IsQuestWorthDoing(Quest const* quest);
|
||||
bool IsQuestCapableDoing(Quest const* quest);
|
||||
|
||||
/* QUEST RELATED ACTION */
|
||||
bool SearchQuestGiverAndAcceptOrReward();
|
||||
bool AcceptQuest(Quest const* quest, ObjectGuid guid);
|
||||
bool TurnInQuest(Quest const* quest, ObjectGuid guid);
|
||||
bool OrganizeQuestLog();
|
||||
|
||||
protected:
|
||||
bool GetQuestPOIPosAndObjectiveIdx(uint32 questId, std::vector<POIInfo>& poiInfo, bool toComplete = false);
|
||||
static WorldPosition SelectRandomGrindPos(Player* bot);
|
||||
static WorldPosition SelectRandomCampPos(Player* bot);
|
||||
bool SelectRandomFlightTaxiNode(ObjectGuid& flightMaster, uint32& fromNode, uint32& toNode);
|
||||
bool RandomChangeStatus(std::vector<NewRpgStatus> candidateStatus);
|
||||
bool CheckRpgStatusAvailable(NewRpgStatus status);
|
||||
|
||||
protected:
|
||||
/* FOR MOVE FAR */
|
||||
const float pathFinderDis = 70.0f;
|
||||
const uint32 stuckTime = 5 * 60 * 1000;
|
||||
};
|
||||
|
||||
#endif
|
||||
139
src/Ai/World/Rpg/NewRpgInfo.cpp
Normal file
139
src/Ai/World/Rpg/NewRpgInfo.cpp
Normal file
@@ -0,0 +1,139 @@
|
||||
#include "NewRpgInfo.h"
|
||||
|
||||
#include <cmath>
|
||||
|
||||
#include "Timer.h"
|
||||
|
||||
void NewRpgInfo::ChangeToGoGrind(WorldPosition pos)
|
||||
{
|
||||
Reset();
|
||||
status = RPG_GO_GRIND;
|
||||
go_grind = GoGrind();
|
||||
go_grind.pos = pos;
|
||||
}
|
||||
|
||||
void NewRpgInfo::ChangeToGoCamp(WorldPosition pos)
|
||||
{
|
||||
Reset();
|
||||
status = RPG_GO_CAMP;
|
||||
go_camp = GoCamp();
|
||||
go_camp.pos = pos;
|
||||
}
|
||||
|
||||
void NewRpgInfo::ChangeToWanderNpc()
|
||||
{
|
||||
Reset();
|
||||
status = RPG_WANDER_NPC;
|
||||
wander_npc = WanderNpc();
|
||||
}
|
||||
|
||||
void NewRpgInfo::ChangeToWanderRandom()
|
||||
{
|
||||
Reset();
|
||||
status = RPG_WANDER_RANDOM;
|
||||
WANDER_RANDOM = WanderRandom();
|
||||
}
|
||||
|
||||
void NewRpgInfo::ChangeToDoQuest(uint32 questId, const Quest* quest)
|
||||
{
|
||||
Reset();
|
||||
status = RPG_DO_QUEST;
|
||||
do_quest = DoQuest();
|
||||
do_quest.questId = questId;
|
||||
do_quest.quest = quest;
|
||||
}
|
||||
|
||||
void NewRpgInfo::ChangeToTravelFlight(ObjectGuid fromFlightMaster, uint32 fromNode, uint32 toNode)
|
||||
{
|
||||
Reset();
|
||||
status = RPG_TRAVEL_FLIGHT;
|
||||
flight = TravelFlight();
|
||||
flight.fromFlightMaster = fromFlightMaster;
|
||||
flight.fromNode = fromNode;
|
||||
flight.toNode = toNode;
|
||||
}
|
||||
|
||||
void NewRpgInfo::ChangeToRest()
|
||||
{
|
||||
Reset();
|
||||
status = RPG_REST;
|
||||
rest = Rest();
|
||||
}
|
||||
|
||||
void NewRpgInfo::ChangeToIdle()
|
||||
{
|
||||
Reset();
|
||||
status = RPG_IDLE;
|
||||
}
|
||||
|
||||
bool NewRpgInfo::CanChangeTo(NewRpgStatus status) { return true; }
|
||||
|
||||
void NewRpgInfo::Reset()
|
||||
{
|
||||
*this = NewRpgInfo();
|
||||
startT = getMSTime();
|
||||
}
|
||||
|
||||
void NewRpgInfo::SetMoveFarTo(WorldPosition pos)
|
||||
{
|
||||
nearestMoveFarDis = FLT_MAX;
|
||||
stuckTs = 0;
|
||||
stuckAttempts = 0;
|
||||
moveFarPos = pos;
|
||||
}
|
||||
|
||||
std::string NewRpgInfo::ToString()
|
||||
{
|
||||
std::stringstream out;
|
||||
out << "Status: ";
|
||||
switch (status)
|
||||
{
|
||||
case RPG_GO_GRIND:
|
||||
out << "GO_GRIND";
|
||||
out << "\nGrindPos: " << go_grind.pos.GetMapId() << " " << go_grind.pos.GetPositionX() << " "
|
||||
<< go_grind.pos.GetPositionY() << " " << go_grind.pos.GetPositionZ();
|
||||
out << "\nlastGoGrind: " << startT;
|
||||
break;
|
||||
case RPG_GO_CAMP:
|
||||
out << "GO_CAMP";
|
||||
out << "\nCampPos: " << go_camp.pos.GetMapId() << " " << go_camp.pos.GetPositionX() << " "
|
||||
<< go_camp.pos.GetPositionY() << " " << go_camp.pos.GetPositionZ();
|
||||
out << "\nlastGoCamp: " << startT;
|
||||
break;
|
||||
case RPG_WANDER_NPC:
|
||||
out << "WANDER_NPC";
|
||||
out << "\nnpcOrGoEntry: " << wander_npc.npcOrGo.GetCounter();
|
||||
out << "\nlastWanderNpc: " << startT;
|
||||
out << "\nlastReachNpcOrGo: " << wander_npc.lastReach;
|
||||
break;
|
||||
case RPG_WANDER_RANDOM:
|
||||
out << "WANDER_RANDOM";
|
||||
out << "\nlastWanderRandom: " << startT;
|
||||
break;
|
||||
case RPG_IDLE:
|
||||
out << "IDLE";
|
||||
break;
|
||||
case RPG_REST:
|
||||
out << "REST";
|
||||
out << "\nlastRest: " << startT;
|
||||
break;
|
||||
case RPG_DO_QUEST:
|
||||
out << "DO_QUEST";
|
||||
out << "\nquestId: " << do_quest.questId;
|
||||
out << "\nobjectiveIdx: " << do_quest.objectiveIdx;
|
||||
out << "\npoiPos: " << do_quest.pos.GetMapId() << " " << do_quest.pos.GetPositionX() << " "
|
||||
<< do_quest.pos.GetPositionY() << " " << do_quest.pos.GetPositionZ();
|
||||
out << "\nlastReachPOI: " << do_quest.lastReachPOI ? GetMSTimeDiffToNow(do_quest.lastReachPOI) : 0;
|
||||
break;
|
||||
case RPG_TRAVEL_FLIGHT:
|
||||
out << "TRAVEL_FLIGHT";
|
||||
out << "\nfromFlightMaster: " << flight.fromFlightMaster.GetEntry();
|
||||
out << "\nfromNode: " << flight.fromNode;
|
||||
out << "\ntoNode: " << flight.toNode;
|
||||
out << "\ninFlight: " << flight.inFlight;
|
||||
break;
|
||||
default:
|
||||
out << "UNKNOWN";
|
||||
}
|
||||
return out.str();
|
||||
}
|
||||
133
src/Ai/World/Rpg/NewRpgInfo.h
Normal file
133
src/Ai/World/Rpg/NewRpgInfo.h
Normal file
@@ -0,0 +1,133 @@
|
||||
#ifndef _PLAYERBOT_NEWRPGINFO_H
|
||||
#define _PLAYERBOT_NEWRPGINFO_H
|
||||
|
||||
#include "Define.h"
|
||||
#include "ObjectGuid.h"
|
||||
#include "ObjectMgr.h"
|
||||
#include "QuestDef.h"
|
||||
#include "Strategy.h"
|
||||
#include "Timer.h"
|
||||
#include "TravelMgr.h"
|
||||
|
||||
using NewRpgStatusTransitionProb = std::vector<std::vector<int>>;
|
||||
|
||||
struct NewRpgInfo
|
||||
{
|
||||
NewRpgInfo() {}
|
||||
|
||||
// RPG_GO_GRIND
|
||||
struct GoGrind
|
||||
{
|
||||
WorldPosition pos{};
|
||||
};
|
||||
// RPG_GO_CAMP
|
||||
struct GoCamp
|
||||
{
|
||||
WorldPosition pos{};
|
||||
};
|
||||
// RPG_WANDER_NPC
|
||||
struct WanderNpc
|
||||
{
|
||||
ObjectGuid npcOrGo{};
|
||||
uint32 lastReach{0};
|
||||
};
|
||||
// RPG_WANDER_RANDOM
|
||||
struct WanderRandom
|
||||
{
|
||||
WanderRandom() = default;
|
||||
};
|
||||
// RPG_DO_QUEST
|
||||
struct DoQuest
|
||||
{
|
||||
const Quest* quest{nullptr};
|
||||
uint32 questId{0};
|
||||
int32 objectiveIdx{0};
|
||||
WorldPosition pos{};
|
||||
uint32 lastReachPOI{0};
|
||||
};
|
||||
// RPG_TRAVEL_FLIGHT
|
||||
struct TravelFlight
|
||||
{
|
||||
ObjectGuid fromFlightMaster{};
|
||||
uint32 fromNode{0};
|
||||
uint32 toNode{0};
|
||||
bool inFlight{false};
|
||||
};
|
||||
// RPG_REST
|
||||
struct Rest
|
||||
{
|
||||
Rest() = default;
|
||||
};
|
||||
struct Idle
|
||||
{
|
||||
};
|
||||
NewRpgStatus status{RPG_IDLE};
|
||||
|
||||
uint32 startT{0}; // start timestamp of the current status
|
||||
|
||||
// MOVE_FAR
|
||||
float nearestMoveFarDis{FLT_MAX};
|
||||
uint32 stuckTs{0};
|
||||
uint32 stuckAttempts{0};
|
||||
WorldPosition moveFarPos;
|
||||
// END MOVE_FAR
|
||||
|
||||
union
|
||||
{
|
||||
GoGrind go_grind;
|
||||
GoCamp go_camp;
|
||||
WanderNpc wander_npc;
|
||||
WanderRandom WANDER_RANDOM;
|
||||
DoQuest do_quest;
|
||||
Rest rest;
|
||||
DoQuest quest;
|
||||
TravelFlight flight;
|
||||
};
|
||||
|
||||
bool HasStatusPersisted(uint32 maxDuration) { return GetMSTimeDiffToNow(startT) > maxDuration; }
|
||||
void ChangeToGoGrind(WorldPosition pos);
|
||||
void ChangeToGoCamp(WorldPosition pos);
|
||||
void ChangeToWanderNpc();
|
||||
void ChangeToWanderRandom();
|
||||
void ChangeToDoQuest(uint32 questId, const Quest* quest);
|
||||
void ChangeToTravelFlight(ObjectGuid fromFlightMaster, uint32 fromNode, uint32 toNode);
|
||||
void ChangeToRest();
|
||||
void ChangeToIdle();
|
||||
bool CanChangeTo(NewRpgStatus status);
|
||||
void Reset();
|
||||
void SetMoveFarTo(WorldPosition pos);
|
||||
std::string ToString();
|
||||
};
|
||||
|
||||
struct NewRpgStatistic
|
||||
{
|
||||
uint32 questAccepted{0};
|
||||
uint32 questCompleted{0};
|
||||
uint32 questAbandoned{0};
|
||||
uint32 questRewarded{0};
|
||||
uint32 questDropped{0};
|
||||
NewRpgStatistic operator+(const NewRpgStatistic& other) const
|
||||
{
|
||||
NewRpgStatistic result;
|
||||
result.questAccepted = this->questAccepted + other.questAccepted;
|
||||
result.questCompleted = this->questCompleted + other.questCompleted;
|
||||
result.questAbandoned = this->questAbandoned + other.questAbandoned;
|
||||
result.questRewarded = this->questRewarded + other.questRewarded;
|
||||
result.questDropped = this->questDropped + other.questDropped;
|
||||
return result;
|
||||
}
|
||||
NewRpgStatistic& operator+=(const NewRpgStatistic& other)
|
||||
{
|
||||
this->questAccepted += other.questAccepted;
|
||||
this->questCompleted += other.questCompleted;
|
||||
this->questAbandoned += other.questAbandoned;
|
||||
this->questRewarded += other.questRewarded;
|
||||
this->questDropped += other.questDropped;
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
// not sure is it necessary but keep it for now
|
||||
#define RPG_INFO(x, y) botAI->rpgInfo.x.y
|
||||
|
||||
#endif
|
||||
75
src/Ai/World/Rpg/Strategy/NewRpgStrategy.cpp
Normal file
75
src/Ai/World/Rpg/Strategy/NewRpgStrategy.cpp
Normal file
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* 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 "NewRpgStrategy.h"
|
||||
|
||||
#include "Playerbots.h"
|
||||
|
||||
NewRpgStrategy::NewRpgStrategy(PlayerbotAI* botAI) : Strategy(botAI) {}
|
||||
|
||||
std::vector<NextAction> NewRpgStrategy::getDefaultActions()
|
||||
{
|
||||
// the releavance should be greater than grind
|
||||
return {
|
||||
NextAction("new rpg status update", 11.0f)
|
||||
};
|
||||
}
|
||||
|
||||
void NewRpgStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||
{
|
||||
triggers.push_back(
|
||||
new TriggerNode(
|
||||
"go grind status",
|
||||
{
|
||||
NextAction("new rpg go grind", 3.0f)
|
||||
}
|
||||
)
|
||||
);
|
||||
triggers.push_back(
|
||||
new TriggerNode(
|
||||
"go camp status",
|
||||
{
|
||||
NextAction("new rpg go camp", 3.0f)
|
||||
}
|
||||
)
|
||||
);
|
||||
triggers.push_back(
|
||||
new TriggerNode(
|
||||
"wander random status",
|
||||
{
|
||||
NextAction("new rpg wander random", 3.0f)
|
||||
}
|
||||
)
|
||||
);
|
||||
triggers.push_back(
|
||||
new TriggerNode(
|
||||
"wander npc status",
|
||||
{
|
||||
NextAction("new rpg wander npc", 3.0f)
|
||||
}
|
||||
)
|
||||
);
|
||||
triggers.push_back(
|
||||
new TriggerNode(
|
||||
"do quest status",
|
||||
{
|
||||
NextAction("new rpg do quest", 3.0f)
|
||||
}
|
||||
)
|
||||
);
|
||||
triggers.push_back(
|
||||
new TriggerNode(
|
||||
"travel flight status",
|
||||
{
|
||||
NextAction("new rpg travel flight", 3.0f)
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
void NewRpgStrategy::InitMultipliers(std::vector<Multiplier*>& multipliers)
|
||||
{
|
||||
|
||||
}
|
||||
26
src/Ai/World/Rpg/Strategy/NewRpgStrategy.h
Normal file
26
src/Ai/World/Rpg/Strategy/NewRpgStrategy.h
Normal file
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef _PLAYERBOT_NEWRPGSTRATEGY_H
|
||||
#define _PLAYERBOT_NEWRPGSTRATEGY_H
|
||||
|
||||
#include "Strategy.h"
|
||||
#include "TravelMgr.h"
|
||||
#include "NewRpgInfo.h"
|
||||
|
||||
class PlayerbotAI;
|
||||
|
||||
class NewRpgStrategy : public Strategy
|
||||
{
|
||||
public:
|
||||
NewRpgStrategy(PlayerbotAI* botAI);
|
||||
|
||||
std::string const getName() override { return "new rpg"; }
|
||||
std::vector<NextAction> getDefaultActions() override;
|
||||
void InitTriggers(std::vector<TriggerNode*>& triggers) override;
|
||||
void InitMultipliers(std::vector<Multiplier*>& multipliers) override;
|
||||
};
|
||||
|
||||
#endif
|
||||
4
src/Ai/World/Rpg/Trigger/NewRpgTrigger.cpp
Normal file
4
src/Ai/World/Rpg/Trigger/NewRpgTrigger.cpp
Normal file
@@ -0,0 +1,4 @@
|
||||
#include "NewRpgTriggers.h"
|
||||
#include "PlayerbotAI.h"
|
||||
|
||||
bool NewRpgStatusTrigger::IsActive() { return status == botAI->rpgInfo.status; }
|
||||
20
src/Ai/World/Rpg/Trigger/NewRpgTriggers.h
Normal file
20
src/Ai/World/Rpg/Trigger/NewRpgTriggers.h
Normal file
@@ -0,0 +1,20 @@
|
||||
#ifndef _PLAYERBOT_NEWRPGTRIGGERS_H
|
||||
#define _PLAYERBOT_NEWRPGTRIGGERS_H
|
||||
|
||||
#include "NewRpgStrategy.h"
|
||||
#include "Trigger.h"
|
||||
|
||||
class NewRpgStatusTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
NewRpgStatusTrigger(PlayerbotAI* botAI, NewRpgStatus status = RPG_IDLE)
|
||||
: Trigger(botAI, "new rpg status"), status(status)
|
||||
{
|
||||
}
|
||||
bool IsActive() override;
|
||||
|
||||
protected:
|
||||
NewRpgStatus status;
|
||||
};
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user