From dba84da6f3884b452062d66639919be00ca49a9a Mon Sep 17 00:00:00 2001 From: Yunfan Li Date: Mon, 2 Dec 2024 00:35:23 +0800 Subject: [PATCH] [New Rpg] Implement GO_INNKEEPER and NEAR_NPC status --- src/strategy/actions/ActionContext.h | 4 + src/strategy/actions/NewRpgAction.cpp | 209 +++++++++++++++--- src/strategy/actions/NewRpgAction.h | 25 ++- .../actions/ReviveFromCorpseAction.cpp | 2 +- src/strategy/generic/NewRpgStrategy.cpp | 6 + src/strategy/generic/NewRpgStrategy.h | 25 ++- src/strategy/triggers/TriggerContext.h | 4 + 7 files changed, 240 insertions(+), 35 deletions(-) diff --git a/src/strategy/actions/ActionContext.h b/src/strategy/actions/ActionContext.h index 163731bf..0ffe08eb 100644 --- a/src/strategy/actions/ActionContext.h +++ b/src/strategy/actions/ActionContext.h @@ -244,7 +244,9 @@ public: creators["new rpg status update"] = &ActionContext::new_rpg_status_update; creators["new rpg go grind"] = &ActionContext::new_rpg_go_grind; + creators["new rpg go innkeeper"] = &ActionContext::new_rpg_go_innkeeper; creators["new rpg move random"] = &ActionContext::new_rpg_move_random; + creators["new rpg move npc"] = &ActionContext::new_rpg_move_npc; } private: @@ -423,7 +425,9 @@ private: static Action* new_rpg_status_update(PlayerbotAI* ai) { return new NewRpgStatusUpdateAction(ai); } static Action* new_rpg_go_grind(PlayerbotAI* ai) { return new NewRpgGoGrindAction(ai); } + static Action* new_rpg_go_innkeeper(PlayerbotAI* ai) { return new NewRpgGoInnKeeperAction(ai); } static Action* new_rpg_move_random(PlayerbotAI* ai) { return new NewRpgMoveRandomAction(ai); } + static Action* new_rpg_move_npc(PlayerbotAI* ai) { return new NewRpgMoveNpcAction(ai); } }; #endif diff --git a/src/strategy/actions/NewRpgAction.cpp b/src/strategy/actions/NewRpgAction.cpp index 6fe6e488..a5e61dea 100644 --- a/src/strategy/actions/NewRpgAction.cpp +++ b/src/strategy/actions/NewRpgAction.cpp @@ -3,6 +3,7 @@ #include #include "NewRpgStrategy.h" +#include "ObjectDefines.h" #include "ObjectGuid.h" #include "PathGenerator.h" #include "Player.h" @@ -28,27 +29,45 @@ bool NewRpgStatusUpdateAction::Execute(Event event) { case NewRpgStatus::IDLE: { - // // IDLE -> NEAR_NPC - // if (!info.lastNearNpc || info.lastNearNpc + setNpcInterval < getMSTime() && urand(1, 100) <= 50) - // { - // info.lastNearNpc = getMSTime(); - // GuidVector possibleTargets = AI_VALUE(GuidVector, "possible rpg targets"); - // if (possibleTargets.empty()) - // break; - // info.status = NewRpgStatus::NEAR_NPC; - // } - // IDLE -> GO_GRIND - if (!info.lastGrind || info.lastGrind + setGrindInterval < getMSTime()) + uint32 roll = urand(1, 100); + // IDLE -> NEAR_NPC + // if ((!info.lastNearNpc || info.lastNearNpc + setNpcInterval < getMSTime()) && roll <= 30) + if (roll <= 20) + { + info.lastNearNpc = getMSTime(); + GuidVector possibleTargets = AI_VALUE(GuidVector, "possible rpg targets"); + if (possibleTargets.empty()) + break; + info.status = NewRpgStatus::NEAR_NPC; + return true; + } + // IDLE -> GO_INNKEEPER + if (bot->GetLevel() >= 6 && roll <= 30) + { + WorldPosition pos = SelectRandomInnKeeperPos(); + if (pos == WorldPosition() || bot->GetExactDist(pos) < 50.0f) + break; + info.lastGoInnKeeper = getMSTime(); + info.status = NewRpgStatus::GO_INNKEEPER; + info.innKeeperPos = pos; + return true; + } + // IDLE -> GO_GRIND + if (roll <= 90) { - info.lastGrind = getMSTime(); WorldPosition pos = SelectRandomGrindPos(); if (pos == WorldPosition()) break; + info.lastGoGrind = getMSTime(); info.status = NewRpgStatus::GO_GRIND; info.grindPos = pos; return true; } - break; + // IDLE -> REST + info.status = NewRpgStatus::REST; + info.lastRest = getMSTime(); + bot->SetStandState(UNIT_STAND_STATE_SIT); + return true; } case NewRpgStatus::GO_GRIND: { @@ -58,33 +77,62 @@ bool NewRpgStatusUpdateAction::Execute(Event event) if (bot->GetExactDist(originalPos) < 10.0f) { info.status = NewRpgStatus::NEAR_RANDOM; + info.lastNearRandom = getMSTime(); info.grindPos = WorldPosition(); return true; } - // just choose another grindPos - if (!info.lastGrind || info.lastGrind + setGrindInterval < getMSTime()) + // // just choose another grindPos + // if (!info.lastGoGrind || info.lastGoGrind + setGrindInterval < getMSTime()) + // { + // WorldPosition pos = SelectRandomGrindPos(); + // if (pos == WorldPosition()) + // break; + // info.status = NewRpgStatus::GO_GRIND; + // info.lastGoGrind = getMSTime(); + // info.grindPos = pos; + // return true; + // } + break; + } + case NewRpgStatus::GO_INNKEEPER: + { + WorldPosition& originalPos = info.innKeeperPos; + assert(info.grindPos != WorldPosition()); + // GO_INNKEEPER -> NEAR_NPC + if (bot->GetExactDist(originalPos) < 10.0f) { - WorldPosition pos = SelectRandomGrindPos(); - if (pos == WorldPosition()) - break; - info.status = NewRpgStatus::GO_GRIND; - info.lastGrind = getMSTime(); - info.grindPos = pos; + info.lastNearNpc = getMSTime(); + info.status = NewRpgStatus::NEAR_NPC; + info.innKeeperPos = WorldPosition(); return true; } break; } case NewRpgStatus::NEAR_RANDOM: { - // NEAR_RANDOM -> GO_GRIND - if (!info.lastGrind || info.lastGrind + setGrindInterval < getMSTime()) + // NEAR_RANDOM -> IDLE + if (info.lastNearRandom + statusNearRandomDuration < getMSTime()) { - WorldPosition pos = SelectRandomGrindPos(); - if (pos == WorldPosition()) - break; - info.lastGrind = getMSTime(); - botAI->rpgInfo.status = NewRpgStatus::GO_GRIND; - botAI->rpgInfo.grindPos = pos; + info.status = NewRpgStatus::IDLE; + return true; + } + break; + } + case NewRpgStatus::NEAR_NPC: + { + if (info.lastNearNpc + statusNearNpcDuration < getMSTime()) + { + info.status = NewRpgStatus::IDLE; + return true; + } + break; + } + case NewRpgStatus::REST: + { + // REST -> IDLE + if (info.lastRest + statusRestDuration < getMSTime()) + { + info.status = NewRpgStatus::IDLE; return true; } break; @@ -131,6 +179,35 @@ WorldPosition NewRpgStatusUpdateAction::SelectRandomGrindPos() return dest; } +WorldPosition NewRpgStatusUpdateAction::SelectRandomInnKeeperPos() +{ + const std::vector& locs = IsAlliance(bot->getRace()) + ? sRandomPlayerbotMgr->allianceInnkeeperPerLevelCache[bot->GetLevel()] + : sRandomPlayerbotMgr->hordeInnkeeperPerLevelCache[bot->GetLevel()]; + std::vector prepared_locs; + for (auto& loc : locs) + { + if (bot->GetMapId() != loc.GetMapId()) + continue; + + float range = bot->GetLevel() <= 5 ? 500.0f : 2500.0f; + if (bot->GetExactDist(loc) < range) + { + prepared_locs.push_back(loc); + } + } + WorldPosition dest; + if (!prepared_locs.empty()) + { + uint32 idx = urand(0, prepared_locs.size() - 1); + dest = prepared_locs[idx]; + } + LOG_INFO("playerbots", "[New Rpg] Bot {} select random inn keeper pos Map:{} X:{} Y:{} Z:{} ({} available in {})", + bot->GetName(), dest.GetMapId(), dest.GetPositionX(), dest.GetPositionY(), dest.GetPositionZ(), + prepared_locs.size(), locs.size()); + return dest; +} + bool NewRpgGoFarAwayPosAction::MoveFarTo(WorldPosition dest) { float dis = bot->GetExactDist(dest); @@ -184,6 +261,8 @@ bool NewRpgGoFarAwayPosAction::MoveFarTo(WorldPosition dest) bool NewRpgGoGrindAction::Execute(Event event) { return MoveFarTo(botAI->rpgInfo.grindPos); } +bool NewRpgGoInnKeeperAction::Execute(Event event) { return MoveFarTo(botAI->rpgInfo.innKeeperPos); } + bool NewRpgMoveRandomAction::Execute(Event event) { float distance = rand_norm() * moveStep; @@ -211,4 +290,76 @@ bool NewRpgMoveRandomAction::Execute(Event event) } return false; +} + +bool NewRpgMoveNpcAction::Execute(Event event) +{ + NewRpgInfo& info = botAI->rpgInfo; + if (!info.npcPos) + { + GuidVector possibleTargets = AI_VALUE(GuidVector, "possible rpg targets"); + if (possibleTargets.empty()) + return false; + int idx = urand(0, possibleTargets.size() - 1); + ObjectGuid guid = possibleTargets[idx]; + Unit* unit = botAI->GetUnit(guid); + if (unit) + { + info.npcPos = GuidPosition(unit); + info.lastReachNpc = 0; + } + else + return false; + } + + if (bot->GetDistance(info.npcPos) <= INTERACTION_DISTANCE) + { + if (!info.lastReachNpc) + { + info.lastReachNpc = getMSTime(); + return true; + } + + if (info.lastReachNpc && info.lastReachNpc + stayTime > getMSTime()) + return false; + + info.npcPos = GuidPosition(); + info.lastReachNpc = 0; + } + else + { + assert(info.npcPos); + Unit* unit = botAI->GetUnit(info.npcPos); + if (!unit) + return false; + float x = unit->GetPositionX(); + float y = unit->GetPositionY(); + float z = unit->GetPositionZ(); + float mapId = unit->GetMapId(); + float angle = 0.f; + if (bot->IsWithinLOS(x, y, z)) + { + if (!unit->isMoving()) + angle = unit->GetAngle(bot) + (M_PI * irand(-25, 25) / 100.0); // Closest 45 degrees towards the target + else + angle = unit->GetOrientation() + + (M_PI * irand(-25, 25) / 100.0); // 45 degrees infront of target (leading it's movement) + } + else + angle = 2 * M_PI * rand_norm(); // A circle around the target. + + x += cos(angle) * INTERACTION_DISTANCE * rand_norm(); + y += sin(angle) * INTERACTION_DISTANCE * rand_norm(); + bool exact = true; + if (!unit->GetMap()->CheckCollisionAndGetValidCoords(unit, unit->GetPositionX(), unit->GetPositionY(), + unit->GetPositionZ(), x, y, z)) + { + x = unit->GetPositionX(); + y = unit->GetPositionY(); + z = unit->GetPositionZ(); + exact = false; + } + return MoveTo(mapId, x, y, z, false, false, false, exact); + } + return true; } \ No newline at end of file diff --git a/src/strategy/actions/NewRpgAction.h b/src/strategy/actions/NewRpgAction.h index a2ea84a5..bb07581a 100644 --- a/src/strategy/actions/NewRpgAction.h +++ b/src/strategy/actions/NewRpgAction.h @@ -21,9 +21,13 @@ public: NewRpgStatusUpdateAction(PlayerbotAI* botAI) : Action(botAI, "new rpg status update") {} bool Execute(Event event) override; protected: - const int32 setGrindInterval = 5 * 60 * 1000; - const int32 setNpcInterval = 5 * 60 * 1000; + // const int32 setGrindInterval = 5 * 60 * 1000; + // const int32 setNpcInterval = 1 * 60 * 1000; + const int32 statusNearNpcDuration = 3 * 60 * 1000; + const int32 statusNearRandomDuration = 3 * 60 * 1000; + const int32 statusRestDuration = 1 * 60 * 1000; WorldPosition SelectRandomGrindPos(); + WorldPosition SelectRandomInnKeeperPos(); }; class NewRpgGoFarAwayPosAction : public MovementAction @@ -45,6 +49,14 @@ public: bool Execute(Event event) override; }; +class NewRpgGoInnKeeperAction : public NewRpgGoFarAwayPosAction +{ +public: + NewRpgGoInnKeeperAction(PlayerbotAI* botAI) : NewRpgGoFarAwayPosAction(botAI, "new rpg go innkeeper") {} + bool Execute(Event event) override; +}; + + class NewRpgMoveRandomAction : public MovementAction { public: @@ -54,4 +66,13 @@ protected: const float moveStep = 50.0f; }; +class NewRpgMoveNpcAction : public MovementAction +{ +public: + NewRpgMoveNpcAction(PlayerbotAI* botAI) : MovementAction(botAI, "new rpg move npcs") {} + bool Execute(Event event) override; +protected: + const uint32 stayTime = 8 * 1000; +}; + #endif \ No newline at end of file diff --git a/src/strategy/actions/ReviveFromCorpseAction.cpp b/src/strategy/actions/ReviveFromCorpseAction.cpp index 1f61df3b..6cab4206 100644 --- a/src/strategy/actions/ReviveFromCorpseAction.cpp +++ b/src/strategy/actions/ReviveFromCorpseAction.cpp @@ -188,7 +188,7 @@ bool FindCorpseAction::Execute(Event event) if (!moved) { - moved = botAI->DoSpecificAction("spirit healer"); + moved = botAI->DoSpecificAction("spirit healer", Event(), true); } } } diff --git a/src/strategy/generic/NewRpgStrategy.cpp b/src/strategy/generic/NewRpgStrategy.cpp index a2a38c5c..82bd03ba 100644 --- a/src/strategy/generic/NewRpgStrategy.cpp +++ b/src/strategy/generic/NewRpgStrategy.cpp @@ -18,9 +18,15 @@ void NewRpgStrategy::InitTriggers(std::vector& triggers) { triggers.push_back( new TriggerNode("go grind status", NextAction::array(0, new NextAction("new rpg go grind", 1.0f), nullptr))); + + triggers.push_back( + new TriggerNode("go innkeeper status", NextAction::array(0, new NextAction("new rpg go innkeeper", 1.0f), nullptr))); triggers.push_back( new TriggerNode("near random status", NextAction::array(0, new NextAction("new rpg move random", 1.0f), nullptr))); + + triggers.push_back( + new TriggerNode("near npc status", NextAction::array(0, new NextAction("new rpg move npc", 1.0f), nullptr))); } void NewRpgStrategy::InitMultipliers(std::vector& multipliers) diff --git a/src/strategy/generic/NewRpgStrategy.h b/src/strategy/generic/NewRpgStrategy.h index c23ee9b2..52d990c8 100644 --- a/src/strategy/generic/NewRpgStrategy.h +++ b/src/strategy/generic/NewRpgStrategy.h @@ -20,16 +20,30 @@ enum class NewRpgStatus // Exploring nearby NEAR_RANDOM, NEAR_NPC, - // Idling + // Taking a break + REST, + // Initial status IDLE }; struct NewRpgInfo { NewRpgStatus status{NewRpgStatus::IDLE}; + // NewRpgStatus::GO_GRIND WorldPosition grindPos{}; - uint32 lastGrind{0}; + uint32 lastGoGrind{0}; + // NewRpgStatus::GO_INNKEEPER + WorldPosition innKeeperPos{}; + uint32 lastGoInnKeeper{0}; + // NewRpgStatus::NEAR_NPC + GuidPosition npcPos{}; uint32 lastNearNpc{0}; + uint32 lastReachNpc{0}; + // NewRpgStatus::NEAR_RANDOM + uint32 lastNearRandom{0}; + // NewRpgStatus::REST + uint32 lastRest{0}; + std::string ToString() { std::stringstream out; @@ -51,9 +65,14 @@ struct NewRpgInfo case NewRpgStatus::IDLE: out << "IDLE"; break; + case NewRpgStatus::REST: + out << "REST"; + break; + default: + out << "UNKNOWN"; } out << "\nGrindPos: " << grindPos.GetMapId() << " " << grindPos.GetPositionX() << " " << grindPos.GetPositionY() << " " << grindPos.GetPositionZ(); - out << "\nLastGrind: " << lastGrind; + out << "\nlastGoGrind: " << lastGoGrind; return out.str(); } }; diff --git a/src/strategy/triggers/TriggerContext.h b/src/strategy/triggers/TriggerContext.h index 3bcc44c8..2a18c0a1 100644 --- a/src/strategy/triggers/TriggerContext.h +++ b/src/strategy/triggers/TriggerContext.h @@ -216,7 +216,9 @@ public: creators["rpg trade useful"] = &TriggerContext::rpg_trade_useful; creators["rpg duel"] = &TriggerContext::rpg_duel; creators["go grind status"] = &TriggerContext::go_grind_status; + creators["go innkeeper status"] = &TriggerContext::go_innkeeper_status; creators["near random status"] = &TriggerContext::near_random_status; + creators["near npc status"] = &TriggerContext::near_npc_status; } private: @@ -407,7 +409,9 @@ private: static Trigger* rpg_trade_useful(PlayerbotAI* botAI) { return new RpgTradeUsefulTrigger(botAI); } static Trigger* rpg_duel(PlayerbotAI* botAI) { return new RpgDuelTrigger(botAI); } static Trigger* go_grind_status(PlayerbotAI* botAI) { return new NewRpgStatusTrigger(botAI, NewRpgStatus::GO_GRIND); } + static Trigger* go_innkeeper_status(PlayerbotAI* botAI) { return new NewRpgStatusTrigger(botAI, NewRpgStatus::GO_INNKEEPER); } static Trigger* near_random_status(PlayerbotAI* botAI) { return new NewRpgStatusTrigger(botAI, NewRpgStatus::NEAR_RANDOM); } + static Trigger* near_npc_status(PlayerbotAI* botAI) { return new NewRpgStatusTrigger(botAI, NewRpgStatus::NEAR_NPC); } }; #endif