Compare commits

..

1 Commits

Author SHA1 Message Date
bash
5f05d1a278 Update README.md 2025-10-20 21:46:35 +02:00
5 changed files with 138 additions and 166 deletions

3
.gitignore vendored
View File

@@ -48,5 +48,4 @@ local.properties
.loadpath .loadpath
.project .project
.cproject .cproject
.vscode .vscode
.idea

View File

@@ -1769,6 +1769,80 @@ bool PlayerbotAI::IsCombo(Player* player)
(player->getClass() == CLASS_DRUID && player->HasAura(768)); // cat druid (player->getClass() == CLASS_DRUID && player->HasAura(768)); // cat druid
} }
bool PlayerbotAI::IsRangedDps(Player* player, bool bySpec) { return IsRanged(player, bySpec) && IsDps(player, bySpec); }
bool PlayerbotAI::IsHealAssistantOfIndex(Player* player, int index)
{
Group* group = player->GetGroup();
if (!group)
{
return false;
}
int counter = 0;
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
{
Player* member = ref->GetSource();
if (!member)
{
continue;
}
if (IsHeal(member)) // Check if the member is a healer
{
bool isAssistant = group->IsAssistant(member->GetGUID());
// Check if the index matches for both assistant and non-assistant healers
if ((isAssistant && index == counter) || (!isAssistant && index == counter))
{
return player == member;
}
counter++;
}
}
return false;
}
bool PlayerbotAI::IsRangedDpsAssistantOfIndex(Player* player, int index)
{
Group* group = player->GetGroup();
if (!group)
{
return false;
}
int counter = 0;
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
{
Player* member = ref->GetSource();
if (!member)
{
continue;
}
if (IsRangedDps(member)) // Check if the member is a ranged DPS
{
bool isAssistant = group->IsAssistant(member->GetGUID());
// Check the index for both assistant and non-assistant ranges
if ((isAssistant && index == counter) || (!isAssistant && index == counter))
{
return player == member;
}
counter++;
}
}
return false;
}
bool PlayerbotAI::HasAggro(Unit* unit) bool PlayerbotAI::HasAggro(Unit* unit)
{ {
if (!unit) if (!unit)
@@ -2112,6 +2186,7 @@ bool PlayerbotAI::IsMainTank(Player* player)
} }
ObjectGuid mainTank = ObjectGuid(); ObjectGuid mainTank = ObjectGuid();
Group::MemberSlotList const& slots = group->GetMemberSlots();
for (Group::member_citerator itr = slots.begin(); itr != slots.end(); ++itr) for (Group::member_citerator itr = slots.begin(); itr != slots.end(); ++itr)
{ {
@@ -2223,109 +2298,50 @@ uint32 PlayerbotAI::GetGroupTankNum(Player* player)
return result; return result;
} }
bool PlayerbotAI::IsHealAssistantOfIndex(Player* player, int index)
{
Group* group = player->GetGroup();
if (!group)
return false;
Group::MemberSlotList const& slots = group->GetMemberSlots();
int counter = 0;
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
{
Player* member = ref->GetSource();
if (member && group->IsAssistant(member->GetGUID()) && IsHeal(member))
{
if (index == counter)
return player == member;
counter++;
}
}
// Non-assistants
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
{
Player* member = ref->GetSource();
if (member && !group->IsAssistant(member->GetGUID()) && IsHeal(member))
{
if (index == counter)
return player == member;
counter++;
}
}
return false;
}
bool PlayerbotAI::IsRangedDps(Player* player, bool bySpec) { return IsRanged(player, bySpec) && IsDps(player, bySpec); }
bool PlayerbotAI::IsRangedDpsAssistantOfIndex(Player* player, int index)
{
Group* group = player->GetGroup();
if (!group)
return false;
Group::MemberSlotList const& slots = group->GetMemberSlots();
int counter = 0;
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
{
Player* member = ref->GetSource();
if (member && group->IsAssistant(member->GetGUID()) && IsRangedDps(member))
{
if (index == counter)
return player == member;
counter++;
}
}
// Non-assistants
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
{
Player* member = ref->GetSource();
if (member && !group->IsAssistant(member->GetGUID()) && IsRangedDps(member))
{
if (index == counter)
return player == member;
counter++;
}
}
return false;
}
bool PlayerbotAI::IsAssistTank(Player* player) { return IsTank(player) && !IsMainTank(player); } bool PlayerbotAI::IsAssistTank(Player* player) { return IsTank(player) && !IsMainTank(player); }
bool PlayerbotAI::IsAssistTankOfIndex(Player* player, int index) bool PlayerbotAI::IsAssistTankOfIndex(Player* player, int index)
{ {
Group* group = player->GetGroup(); Group* group = player->GetGroup();
if (!group) if (!group)
{
return false; return false;
}
Group::MemberSlotList const& slots = group->GetMemberSlots();
int counter = 0; int counter = 0;
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
{ {
Player* member = ref->GetSource(); Player* member = ref->GetSource();
if (member && group->IsAssistant(member->GetGUID()) && IsAssistTank(member))
if (!member)
{
continue;
}
if (group->IsAssistant(member->GetGUID()) && IsAssistTank(member))
{ {
if (index == counter) if (index == counter)
{
return player == member; return player == member;
}
counter++; counter++;
} }
} }
// Non-assistants // not enough
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
{ {
Player* member = ref->GetSource(); Player* member = ref->GetSource();
if (member && !group->IsAssistant(member->GetGUID()) && IsAssistTank(member))
if (!member)
{
continue;
}
if (!group->IsAssistant(member->GetGUID()) && IsAssistTank(member))
{ {
if (index == counter) if (index == counter)
{
return player == member; return player == member;
}
counter++; counter++;
} }
} }

View File

@@ -220,8 +220,7 @@ public:
bool OnPlayerBeforeAchievementComplete(Player* player, AchievementEntry const* achievement) override bool OnPlayerBeforeAchievementComplete(Player* player, AchievementEntry const* achievement) override
{ {
if ((sRandomPlayerbotMgr->IsRandomBot(player) || sRandomPlayerbotMgr->IsAddclassBot(player)) && if (sRandomPlayerbotMgr->IsRandomBot(player) && (achievement->flags == 256 || achievement->flags == 768))
(achievement->flags & (ACHIEVEMENT_FLAG_REALM_FIRST_REACH | ACHIEVEMENT_FLAG_REALM_FIRST_KILL)))
{ {
return false; return false;
} }

View File

@@ -199,8 +199,7 @@ bool MovementAction::MoveTo(uint32 mapId, float x, float y, float z, bool idle,
{ {
VehicleSeatEntry const* seat = vehicle->GetSeatForPassenger(bot); VehicleSeatEntry const* seat = vehicle->GetSeatForPassenger(bot);
Unit* vehicleBase = vehicle->GetBase(); Unit* vehicleBase = vehicle->GetBase();
// If the mover (vehicle) can fly, we DO NOT want an mmaps path (2D ground) => disable pathfinding generatePath = vehicleBase->CanFly();
generatePath = !vehicleBase || !vehicleBase->CanFly();
if (!vehicleBase || !seat || !seat->CanControl()) // is passenger and cant move anyway if (!vehicleBase || !seat || !seat->CanControl()) // is passenger and cant move anyway
return false; return false;
@@ -208,20 +207,14 @@ bool MovementAction::MoveTo(uint32 mapId, float x, float y, float z, bool idle,
if (distance > 0.01f) if (distance > 0.01f)
{ {
MotionMaster& mm = *vehicleBase->GetMotionMaster(); // need to move vehicle, not bot MotionMaster& mm = *vehicleBase->GetMotionMaster(); // need to move vehicle, not bot
// Disable ground pathing if the bot/master/vehicle are flying
auto isFlying = [](Unit* u){ return u && (u->HasUnitMovementFlag(MOVEMENTFLAG_FLYING) || u->IsInFlight()); };
bool allowPathVeh = generatePath;
Unit* masterVeh = botAI ? botAI->GetMaster() : nullptr;
if (isFlying(vehicleBase) || isFlying(bot) || isFlying(masterVeh))
allowPathVeh = false;
mm.Clear(); mm.Clear();
if (!backwards) if (!backwards)
{ {
mm.MovePoint(0, x, y, z, FORCED_MOVEMENT_NONE, 0.0f, 0.0f, allowPathVeh); mm.MovePoint(0, x, y, z, FORCED_MOVEMENT_NONE, 0.0f, 0.0f, generatePath);
} }
else else
{ {
mm.MovePointBackwards(0, x, y, z, allowPathVeh); mm.MovePointBackwards(0, x, y, z, generatePath);
} }
float speed = backwards ? vehicleBase->GetSpeed(MOVE_RUN_BACK) : vehicleBase->GetSpeed(MOVE_RUN); float speed = backwards ? vehicleBase->GetSpeed(MOVE_RUN_BACK) : vehicleBase->GetSpeed(MOVE_RUN);
float delay = 1000.0f * (distance / speed); float delay = 1000.0f * (distance / speed);
@@ -248,22 +241,15 @@ bool MovementAction::MoveTo(uint32 mapId, float x, float y, float z, bool idle,
// bot->CastStop(); // bot->CastStop();
// botAI->InterruptSpell(); // botAI->InterruptSpell();
// } // }
MotionMaster& mm = *bot->GetMotionMaster(); MotionMaster& mm = *bot->GetMotionMaster();
// No ground pathfinding if the bot/master are flying => allow true 3D (Z) movement
auto isFlying = [](Unit* u){ return u && (u->HasUnitMovementFlag(MOVEMENTFLAG_FLYING) || u->IsInFlight()); };
bool allowPath = generatePath;
Unit* master = botAI ? botAI->GetMaster() : nullptr;
if (isFlying(bot) || isFlying(master))
allowPath = false;
mm.Clear(); mm.Clear();
if (!backwards) if (!backwards)
{ {
mm.MovePoint(0, x, y, z, FORCED_MOVEMENT_NONE, 0.0f, 0.0f, allowPath); mm.MovePoint(0, x, y, z, FORCED_MOVEMENT_NONE, 0.0f, 0.0f, generatePath);
} }
else else
{ {
mm.MovePointBackwards(0, x, y, z, allowPath); mm.MovePointBackwards(0, x, y, z, generatePath);
} }
float delay = 1000.0f * MoveDelay(distance, backwards); float delay = 1000.0f * MoveDelay(distance, backwards);
if (lessDelay) if (lessDelay)
@@ -296,23 +282,16 @@ bool MovementAction::MoveTo(uint32 mapId, float x, float y, float z, bool idle,
// bot->CastStop(); // bot->CastStop();
// botAI->InterruptSpell(); // botAI->InterruptSpell();
// } // }
MotionMaster& mm = *bot->GetMotionMaster(); MotionMaster& mm = *bot->GetMotionMaster();
G3D::Vector3 endP = path.back(); G3D::Vector3 endP = path.back();
// No ground pathfinding if the bot/master are flying => allow true 3D (Z) movement
auto isFlying = [](Unit* u){ return u && (u->HasUnitMovementFlag(MOVEMENTFLAG_FLYING) || u->IsInFlight()); };
bool allowPath = generatePath;
Unit* master = botAI ? botAI->GetMaster() : nullptr;
if (isFlying(bot) || isFlying(master))
allowPath = false;
mm.Clear(); mm.Clear();
if (!backwards) if (!backwards)
{ {
mm.MovePoint(0, x, y, z, FORCED_MOVEMENT_NONE, 0.0f, 0.0f, allowPath); mm.MovePoint(0, x, y, z, FORCED_MOVEMENT_NONE, 0.0f, 0.0f, generatePath);
} }
else else
{ {
mm.MovePointBackwards(0, x, y, z, allowPath); mm.MovePointBackwards(0, x, y, z, generatePath);
} }
float delay = 1000.0f * MoveDelay(distance, backwards); float delay = 1000.0f * MoveDelay(distance, backwards);
if (lessDelay) if (lessDelay)
@@ -1034,49 +1013,18 @@ void MovementAction::UpdateMovementState()
bot->GetMapWaterOrGroundLevel(bot->GetPositionX(), bot->GetPositionY(), bot->GetPositionZ()) + 1.0f; bot->GetMapWaterOrGroundLevel(bot->GetPositionX(), bot->GetPositionY(), bot->GetPositionZ()) + 1.0f;
// Keep bot->SendMovementFlagUpdate() withing the if statements to not intefere with bot behavior on ground/(shallow) waters // Keep bot->SendMovementFlagUpdate() withing the if statements to not intefere with bot behavior on ground/(shallow) waters
if (!bot->HasUnitMovementFlag(MOVEMENTFLAG_FLYING) &&
bool hasFlightAura = bot->HasAuraType(SPELL_AURA_MOD_INCREASE_MOUNTED_FLIGHT_SPEED) || bot->HasAuraType(SPELL_AURA_FLY); bot->HasAuraType(SPELL_AURA_MOD_INCREASE_MOUNTED_FLIGHT_SPEED) && !onGround)
if (hasFlightAura)
{ {
bool changed = false; bot->AddUnitMovementFlag(MOVEMENTFLAG_FLYING);
if (!bot->HasUnitMovementFlag(MOVEMENTFLAG_CAN_FLY)) bot->SendMovementFlagUpdate();
{
bot->AddUnitMovementFlag(MOVEMENTFLAG_CAN_FLY);
changed = true;
}
if (!bot->HasUnitMovementFlag(MOVEMENTFLAG_DISABLE_GRAVITY))
{
bot->AddUnitMovementFlag(MOVEMENTFLAG_DISABLE_GRAVITY);
changed = true;
}
if (!bot->HasUnitMovementFlag(MOVEMENTFLAG_FLYING))
{
bot->AddUnitMovementFlag(MOVEMENTFLAG_FLYING);
changed = true;
}
if (changed)
bot->SendMovementFlagUpdate();
} }
else if (!hasFlightAura)
else if (bot->HasUnitMovementFlag(MOVEMENTFLAG_FLYING) &&
(!bot->HasAuraType(SPELL_AURA_MOD_INCREASE_MOUNTED_FLIGHT_SPEED) || onGround))
{ {
bool changed = false; bot->RemoveUnitMovementFlag(MOVEMENTFLAG_FLYING);
if (bot->HasUnitMovementFlag(MOVEMENTFLAG_FLYING)) bot->SendMovementFlagUpdate();
{
bot->RemoveUnitMovementFlag(MOVEMENTFLAG_FLYING);
changed = true;
}
if (bot->HasUnitMovementFlag(MOVEMENTFLAG_DISABLE_GRAVITY))
{
bot->RemoveUnitMovementFlag(MOVEMENTFLAG_DISABLE_GRAVITY);
changed = true;
}
if (bot->HasUnitMovementFlag(MOVEMENTFLAG_CAN_FLY))
{
bot->RemoveUnitMovementFlag(MOVEMENTFLAG_CAN_FLY);
changed = true;
}
if (changed)
bot->SendMovementFlagUpdate();
} }
// See if the bot is currently slowed, rooted, or otherwise unable to move // See if the bot is currently slowed, rooted, or otherwise unable to move
@@ -1174,6 +1122,13 @@ bool MovementAction::Follow(Unit* target, float distance, float angle)
if (!target) if (!target)
return false; return false;
if (!bot->InBattleground() && sServerFacade->IsDistanceLessOrEqualThan(sServerFacade->GetDistance2d(bot, target),
sPlayerbotAIConfig->followDistance))
{
// botAI->TellError("No need to follow");
return false;
}
/* /*
if (!bot->InBattleground() if (!bot->InBattleground()
&& sServerFacade->IsDistanceLessOrEqualThan(sServerFacade->GetDistance2d(bot, target->GetPositionX(), && sServerFacade->IsDistanceLessOrEqualThan(sServerFacade->GetDistance2d(bot, target->GetPositionX(),
@@ -1291,17 +1246,21 @@ bool MovementAction::Follow(Unit* target, float distance, float angle)
return MoveTo(target, sPlayerbotAIConfig->followDistance); return MoveTo(target, sPlayerbotAIConfig->followDistance);
} }
if (sServerFacade->IsDistanceLessOrEqualThan(sServerFacade->GetDistance2d(bot, target),
sPlayerbotAIConfig->followDistance))
{
// botAI->TellError("No need to follow");
return false;
}
if (target->IsFriendlyTo(bot) && bot->IsMounted() && AI_VALUE(GuidVector, "all targets").empty()) if (target->IsFriendlyTo(bot) && bot->IsMounted() && AI_VALUE(GuidVector, "all targets").empty())
distance += angle; distance += angle;
// Do not cancel follow if the 2D distance is short but the Z still differs (e.g., master above). if (!bot->InBattleground() && sServerFacade->IsDistanceLessOrEqualThan(sServerFacade->GetDistance2d(bot, target),
float dz1 = fabs(bot->GetPositionZ() - target->GetPositionZ()); sPlayerbotAIConfig->followDistance))
if (!bot->InBattleground()
&& sServerFacade->IsDistanceLessOrEqualThan(sServerFacade->GetDistance2d(bot, target), sPlayerbotAIConfig->followDistance)
&& dz1 < sPlayerbotAIConfig->contactDistance)
{ {
// botAI->TellError("No need to follow"); // botAI->TellError("No need to follow");
return false; // truly in range (2D and Z) => no need to move return false;
} }
bot->HandleEmoteCommand(0); bot->HandleEmoteCommand(0);

View File

@@ -205,18 +205,17 @@ void TalkToQuestGiverAction::RewardMultipleItem(Quest const* quest, Object* ques
} }
else else
{ {
// Try to pick the usable item. If multiple, list usable rewards. // Try to pick the usable item. If multiple list usable rewards.
bestIds = BestRewards(quest); bestIds = BestRewards(quest);
if (!bestIds.empty())
if (bestIds.size() > 1) {
AskToSelectReward(quest, out, true); AskToSelectReward(quest, out, true);
}
else if (!bestIds.empty()) else
{ {
// Pick the first item // Pick the first item
uint32 firstId = *bestIds.begin(); ItemTemplate const* item = sObjectMgr->GetItemTemplate(quest->RewardChoiceItemId[*bestIds.begin()]);
ItemTemplate const* item = sObjectMgr->GetItemTemplate(quest->RewardChoiceItemId[firstId]); bot->RewardQuest(quest, *bestIds.begin(), questGiver, true);
bot->RewardQuest(quest, firstId, questGiver, true);
out << "Rewarded " << ChatHelper::FormatItem(item); out << "Rewarded " << ChatHelper::FormatItem(item);
} }