[New Rpg] Implement GO_INNKEEPER and NEAR_NPC status

This commit is contained in:
Yunfan Li
2024-12-02 00:35:23 +08:00
parent 0fd894176b
commit dba84da6f3
7 changed files with 240 additions and 35 deletions

View File

@@ -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

View File

@@ -3,6 +3,7 @@
#include <cmath>
#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<WorldLocation>& locs = IsAlliance(bot->getRace())
? sRandomPlayerbotMgr->allianceInnkeeperPerLevelCache[bot->GetLevel()]
: sRandomPlayerbotMgr->hordeInnkeeperPerLevelCache[bot->GetLevel()];
std::vector<WorldLocation> 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;
}

View File

@@ -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

View File

@@ -188,7 +188,7 @@ bool FindCorpseAction::Execute(Event event)
if (!moved)
{
moved = botAI->DoSpecificAction("spirit healer");
moved = botAI->DoSpecificAction("spirit healer", Event(), true);
}
}
}

View File

@@ -18,9 +18,15 @@ void NewRpgStrategy::InitTriggers(std::vector<TriggerNode*>& 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<Multiplier*>& multipliers)

View File

@@ -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();
}
};

View File

@@ -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