mirror of
https://github.com/mod-playerbots/mod-playerbots.git
synced 2026-02-03 10:53:48 +00:00
Compare commits
3 Commits
c9cc4324d3
...
9ae457d069
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9ae457d069 | ||
|
|
c9e98a6b4e | ||
|
|
3d9623f119 |
@@ -437,7 +437,7 @@ void PlayerbotAI::UpdateAIGroupMaster()
|
||||
|
||||
void PlayerbotAI::UpdateAIInternal([[maybe_unused]] uint32 elapsed, bool minimal)
|
||||
{
|
||||
if (bot->IsBeingTeleported() || !bot->IsInWorld())
|
||||
if (!bot || bot->IsBeingTeleported() || !bot->IsInWorld())
|
||||
return;
|
||||
|
||||
std::string const mapString = WorldPosition(bot).isOverworld() ? std::to_string(bot->GetMapId()) : "I";
|
||||
@@ -516,23 +516,37 @@ void PlayerbotAI::UpdateAIInternal([[maybe_unused]] uint32 elapsed, bool minimal
|
||||
void PlayerbotAI::HandleCommands()
|
||||
{
|
||||
ExternalEventHelper helper(aiObjectContext);
|
||||
|
||||
for (auto it = chatCommands.begin(); it != chatCommands.end();)
|
||||
{
|
||||
time_t& checkTime = it->GetTime();
|
||||
if (checkTime && time(0) < checkTime)
|
||||
if (checkTime && time(nullptr) < checkTime)
|
||||
{
|
||||
++it;
|
||||
continue;
|
||||
}
|
||||
|
||||
const std::string& command = it->GetCommand();
|
||||
Player* owner = it->GetOwner();
|
||||
if (!owner)
|
||||
{
|
||||
it = chatCommands.erase(it);
|
||||
continue;
|
||||
}
|
||||
|
||||
const std::string& command = it->GetCommand();
|
||||
if (command.empty())
|
||||
{
|
||||
it = chatCommands.erase(it);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!helper.ParseChatCommand(command, owner) && it->GetType() == CHAT_MSG_WHISPER)
|
||||
{
|
||||
// ostringstream out; out << "Unknown command " << command;
|
||||
// TellPlayer(out);
|
||||
// helper.ParseChatCommand("help");
|
||||
}
|
||||
|
||||
it = chatCommands.erase(it);
|
||||
}
|
||||
}
|
||||
@@ -540,6 +554,9 @@ void PlayerbotAI::HandleCommands()
|
||||
std::map<std::string, ChatMsg> chatMap;
|
||||
void PlayerbotAI::HandleCommand(uint32 type, const std::string& text, Player& fromPlayer, const uint32 lang)
|
||||
{
|
||||
if (!bot)
|
||||
return;
|
||||
|
||||
std::string filtered = text;
|
||||
|
||||
if (!IsAllowedCommand(filtered) && !GetSecurity()->CheckLevelFor(PlayerbotSecurityLevel::PLAYERBOT_SECURITY_INVITE,
|
||||
@@ -711,65 +728,82 @@ void PlayerbotAI::HandleCommand(uint32 type, const std::string& text, Player& fr
|
||||
|
||||
void PlayerbotAI::HandleTeleportAck()
|
||||
{
|
||||
if (!bot || !bot->GetSession())
|
||||
return;
|
||||
|
||||
// only for bots
|
||||
if (IsRealPlayer())
|
||||
return;
|
||||
|
||||
// Clearing motion generators and stopping movement which prevents
|
||||
// conflicts between teleport and any active motion (walk, run, swim, flight, etc.)
|
||||
bot->GetMotionMaster()->Clear(true);
|
||||
bot->StopMoving();
|
||||
|
||||
// Near teleport (within map/instance)
|
||||
if (bot->IsBeingTeleportedNear())
|
||||
{
|
||||
// Previous versions manually added the bot to the map if it was not in the world.
|
||||
// not needed: HandleMoveTeleportAckOpcode() safely attaches the player to the map
|
||||
// and clears IsBeingTeleportedNear().
|
||||
|
||||
Player* plMover = bot->m_mover->ToPlayer();
|
||||
if (!plMover)
|
||||
return;
|
||||
|
||||
// Send the near teleport ACK packet
|
||||
WorldPacket p(MSG_MOVE_TELEPORT_ACK, 20);
|
||||
p << plMover->GetPackGUID();
|
||||
p << uint32(0);
|
||||
p << uint32(0);
|
||||
bot->GetSession()->HandleMoveTeleportAck(p);
|
||||
|
||||
// Simulate teleport latency and prevent AI from running too early (used cmangos delays)
|
||||
SetNextCheckDelay(urand(1000, 2000));
|
||||
}
|
||||
|
||||
// Far teleport (worldport / different map)
|
||||
/*
|
||||
* FAR TELEPORT (worldport / map change)
|
||||
* Player may NOT be in world or grid here.
|
||||
* Handle this FIRST.
|
||||
*/
|
||||
if (bot->IsBeingTeleportedFar())
|
||||
{
|
||||
// Handle far teleport ACK:
|
||||
// Moves the bot to the new map, clears IsBeingTeleportedFar(), updates session/map references
|
||||
bot->GetSession()->HandleMoveWorldportAck();
|
||||
|
||||
// Ensure bot now has a valid map. If this fails, there is a core/session bug?
|
||||
// after worldport ACK the player should be in a valid map
|
||||
if (!bot->GetMap())
|
||||
{
|
||||
LOG_ERROR("playerbot", "Bot {} has no map after worldport ACK", bot->GetGUID().ToString());
|
||||
return;
|
||||
}
|
||||
|
||||
// Instance strategies after teleport
|
||||
// apply instance-related strategies after map attach
|
||||
if (sPlayerbotAIConfig->applyInstanceStrategies)
|
||||
ApplyInstanceStrategies(bot->GetMapId(), true);
|
||||
|
||||
// healer DPS strategies if restrictions are enabled
|
||||
if (sPlayerbotAIConfig->restrictHealerDPS)
|
||||
EvaluateHealerDpsStrategy();
|
||||
|
||||
// Reset AI state to to before teleport conditions
|
||||
// reset AI state after teleport
|
||||
Reset(true);
|
||||
|
||||
// Slightly longer delay to simulate far teleport latency (used cmangos delays)
|
||||
// clear movement only AFTER teleport is finalized and bot is in world
|
||||
if (bot->IsInWorld() && bot->GetMotionMaster())
|
||||
{
|
||||
bot->GetMotionMaster()->Clear(true);
|
||||
bot->StopMoving();
|
||||
}
|
||||
|
||||
// simulate far teleport latency (cmangos-style)
|
||||
SetNextCheckDelay(urand(2000, 5000));
|
||||
return;
|
||||
}
|
||||
|
||||
SetNextCheckDelay(sPlayerbotAIConfig->globalCoolDown);
|
||||
/*
|
||||
* NEAR TELEPORT (same map / instance)
|
||||
* Player MUST be in world (and in grid).
|
||||
*/
|
||||
if (bot->IsBeingTeleportedNear())
|
||||
{
|
||||
if (!bot->IsInWorld())
|
||||
return;
|
||||
|
||||
Player* plMover = bot->m_mover ? bot->m_mover->ToPlayer() : nullptr;
|
||||
if (!plMover)
|
||||
return;
|
||||
|
||||
WorldPacket p(MSG_MOVE_TELEPORT_ACK, 20);
|
||||
p << plMover->GetPackGUID();
|
||||
p << uint32(0); // flags
|
||||
p << uint32(0); // time
|
||||
|
||||
bot->GetSession()->HandleMoveTeleportAck(p);
|
||||
|
||||
// clear movement after successful relocation
|
||||
if (bot->GetMotionMaster())
|
||||
{
|
||||
bot->GetMotionMaster()->Clear(true);
|
||||
bot->StopMoving();
|
||||
}
|
||||
|
||||
// simulate near teleport latency
|
||||
SetNextCheckDelay(urand(1000, 2000));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void PlayerbotAI::Reset(bool full)
|
||||
@@ -1286,7 +1320,12 @@ void PlayerbotAI::SpellInterrupted(uint32 spellid)
|
||||
Spell* spell = bot->GetCurrentSpell((CurrentSpellTypes)type);
|
||||
if (!spell)
|
||||
continue;
|
||||
if (spell->GetSpellInfo()->Id == spellid)
|
||||
|
||||
SpellInfo const* spellInfo = spell->GetSpellInfo();
|
||||
if (!spellInfo)
|
||||
continue;
|
||||
|
||||
if (spellInfo->Id == spellid)
|
||||
bot->InterruptSpell((CurrentSpellTypes)type);
|
||||
}
|
||||
// LastSpellCast& lastSpell = aiObjectContext->GetValue<LastSpellCast&>("last spell cast")->Get();
|
||||
@@ -1728,6 +1767,7 @@ bool PlayerbotAI::IsRanged(Player* player, bool bySpec)
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1821,10 +1861,9 @@ bool PlayerbotAI::IsRangedDpsAssistantOfIndex(Player* player, int index)
|
||||
|
||||
bool PlayerbotAI::HasAggro(Unit* unit)
|
||||
{
|
||||
if (!unit)
|
||||
{
|
||||
if (!IsValidUnit(unit))
|
||||
return false;
|
||||
}
|
||||
|
||||
bool isMT = IsMainTank(bot);
|
||||
Unit* victim = unit->GetVictim();
|
||||
if (victim && (victim->GetGUID() == bot->GetGUID() || (!isMT && victim->ToPlayer() && IsTank(victim->ToPlayer()))))
|
||||
@@ -2822,6 +2861,9 @@ bool PlayerbotAI::TellMaster(std::string const text, PlayerbotSecurityLevel secu
|
||||
|
||||
bool IsRealAura(Player* bot, AuraEffect const* aurEff, Unit const* unit)
|
||||
{
|
||||
if (!unit || !unit->IsInWorld() || unit->IsDuringRemoveFromWorld())
|
||||
return false;
|
||||
|
||||
if (!aurEff)
|
||||
return false;
|
||||
|
||||
@@ -2829,6 +2871,8 @@ bool IsRealAura(Player* bot, AuraEffect const* aurEff, Unit const* unit)
|
||||
return true;
|
||||
|
||||
SpellInfo const* spellInfo = aurEff->GetSpellInfo();
|
||||
if (!spellInfo)
|
||||
return false;
|
||||
|
||||
uint32 stacks = aurEff->GetBase()->GetStackAmount();
|
||||
if (stacks >= spellInfo->StackAmount)
|
||||
@@ -2844,7 +2888,7 @@ bool IsRealAura(Player* bot, AuraEffect const* aurEff, Unit const* unit)
|
||||
bool PlayerbotAI::HasAura(std::string const name, Unit* unit, bool maxStack, bool checkIsOwner, int maxAuraAmount,
|
||||
bool checkDuration)
|
||||
{
|
||||
if (!unit)
|
||||
if (!IsValidUnit(unit))
|
||||
return false;
|
||||
|
||||
std::wstring wnamepart;
|
||||
@@ -2940,7 +2984,7 @@ bool PlayerbotAI::HasAura(uint32 spellId, Unit const* unit)
|
||||
|
||||
Aura* PlayerbotAI::GetAura(std::string const name, Unit* unit, bool checkIsOwner, bool checkDuration, int checkStack)
|
||||
{
|
||||
if (!unit)
|
||||
if (!IsValidUnit(unit))
|
||||
return nullptr;
|
||||
|
||||
std::wstring wnamepart;
|
||||
@@ -2958,6 +3002,9 @@ Aura* PlayerbotAI::GetAura(std::string const name, Unit* unit, bool checkIsOwner
|
||||
for (AuraEffect const* aurEff : auras)
|
||||
{
|
||||
SpellInfo const* spellInfo = aurEff->GetSpellInfo();
|
||||
if (!spellInfo)
|
||||
continue;
|
||||
|
||||
std::string const& auraName = spellInfo->SpellName[0];
|
||||
|
||||
// Directly skip if name mismatch (both length and content)
|
||||
@@ -3038,6 +3085,9 @@ bool PlayerbotAI::CanCastSpell(uint32 spellid, Unit* target, bool checkHasSpell,
|
||||
if (!target)
|
||||
target = bot;
|
||||
|
||||
if (!IsValidUnit(target))
|
||||
return false;
|
||||
|
||||
if (Pet* pet = bot->GetPet())
|
||||
if (pet->HasSpell(spellid))
|
||||
return true;
|
||||
@@ -3299,6 +3349,9 @@ bool PlayerbotAI::CanCastSpell(uint32 spellid, float x, float y, float z, bool c
|
||||
|
||||
bool PlayerbotAI::CastSpell(std::string const name, Unit* target, Item* itemTarget)
|
||||
{
|
||||
if (!IsValidUnit(target))
|
||||
return false;
|
||||
|
||||
bool result = CastSpell(aiObjectContext->GetValue<uint32>("spell id", name)->Get(), target, itemTarget);
|
||||
if (result)
|
||||
{
|
||||
@@ -3311,15 +3364,19 @@ bool PlayerbotAI::CastSpell(std::string const name, Unit* target, Item* itemTarg
|
||||
bool PlayerbotAI::CastSpell(uint32 spellId, Unit* target, Item* itemTarget)
|
||||
{
|
||||
if (!spellId)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!target)
|
||||
target = bot;
|
||||
|
||||
Pet* pet = bot->GetPet();
|
||||
if (!IsValidUnit(target))
|
||||
return false;
|
||||
|
||||
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId);
|
||||
if (!spellInfo)
|
||||
return false;
|
||||
|
||||
Pet* pet = bot->GetPet();
|
||||
if (pet && pet->HasSpell(spellId))
|
||||
{
|
||||
// List of spell IDs for which we do NOT want to toggle auto-cast or send message
|
||||
@@ -3722,6 +3779,9 @@ bool PlayerbotAI::CanCastVehicleSpell(uint32 spellId, Unit* target)
|
||||
if (!spellId)
|
||||
return false;
|
||||
|
||||
if (!IsValidUnit(target))
|
||||
return false;
|
||||
|
||||
Vehicle* vehicle = bot->GetVehicle();
|
||||
if (!vehicle)
|
||||
return false;
|
||||
@@ -3732,12 +3792,12 @@ bool PlayerbotAI::CanCastVehicleSpell(uint32 spellId, Unit* target)
|
||||
return false;
|
||||
|
||||
Unit* vehicleBase = vehicle->GetBase();
|
||||
|
||||
Unit* spellTarget = target;
|
||||
|
||||
if (!spellTarget)
|
||||
spellTarget = vehicleBase;
|
||||
|
||||
if (!spellTarget)
|
||||
if (!IsValidUnit(spellTarget))
|
||||
return false;
|
||||
|
||||
if (vehicleBase->HasSpellCooldown(spellId))
|
||||
@@ -3804,6 +3864,9 @@ bool PlayerbotAI::CastVehicleSpell(uint32 spellId, Unit* target)
|
||||
if (!spellId)
|
||||
return false;
|
||||
|
||||
if (!IsValidUnit(target))
|
||||
return false;
|
||||
|
||||
Vehicle* vehicle = bot->GetVehicle();
|
||||
if (!vehicle)
|
||||
return false;
|
||||
@@ -3814,12 +3877,12 @@ bool PlayerbotAI::CastVehicleSpell(uint32 spellId, Unit* target)
|
||||
return false;
|
||||
|
||||
Unit* vehicleBase = vehicle->GetBase();
|
||||
|
||||
Unit* spellTarget = target;
|
||||
|
||||
if (!spellTarget)
|
||||
spellTarget = vehicleBase;
|
||||
|
||||
if (!spellTarget)
|
||||
if (!IsValidUnit(spellTarget))
|
||||
return false;
|
||||
|
||||
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId);
|
||||
@@ -3972,9 +4035,13 @@ bool PlayerbotAI::IsInVehicle(bool canControl, bool canCast, bool canAttack, boo
|
||||
|
||||
void PlayerbotAI::WaitForSpellCast(Spell* spell)
|
||||
{
|
||||
if (!spell)
|
||||
return;
|
||||
|
||||
SpellInfo const* spellInfo = spell->GetSpellInfo();
|
||||
uint32 castTime = spell->GetCastTime();
|
||||
if (spellInfo->IsChanneled())
|
||||
|
||||
if (spellInfo && spellInfo->IsChanneled())
|
||||
{
|
||||
int32 duration = spellInfo->GetDuration();
|
||||
bot->ApplySpellMod(spellInfo->Id, SPELLMOD_DURATION, duration);
|
||||
@@ -4022,6 +4089,9 @@ void PlayerbotAI::RemoveAura(std::string const name)
|
||||
|
||||
bool PlayerbotAI::IsInterruptableSpellCasting(Unit* target, std::string const spell)
|
||||
{
|
||||
if (!IsValidUnit(target))
|
||||
return false;
|
||||
|
||||
uint32 spellid = aiObjectContext->GetValue<uint32>("spell id", spell)->Get();
|
||||
if (!spellid || !target->IsNonMeleeSpellCast(true))
|
||||
return false;
|
||||
@@ -4050,17 +4120,25 @@ bool PlayerbotAI::IsInterruptableSpellCasting(Unit* target, std::string const sp
|
||||
|
||||
bool PlayerbotAI::HasAuraToDispel(Unit* target, uint32 dispelType)
|
||||
{
|
||||
if (!target->IsInWorld())
|
||||
{
|
||||
if (!IsValidUnit(target) || !target->IsAlive())
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!IsValidPlayer(bot))
|
||||
return false;
|
||||
|
||||
bool isFriend = bot->IsFriendlyTo(target);
|
||||
|
||||
Unit::VisibleAuraMap const* visibleAuras = target->GetVisibleAuras();
|
||||
if (!visibleAuras)
|
||||
return false;
|
||||
|
||||
for (Unit::VisibleAuraMap::const_iterator itr = visibleAuras->begin(); itr != visibleAuras->end(); ++itr)
|
||||
{
|
||||
Aura* aura = itr->second->GetBase();
|
||||
if (!itr->second)
|
||||
continue;
|
||||
|
||||
if (aura->IsPassive())
|
||||
Aura* aura = itr->second->GetBase();
|
||||
if (!aura || aura->IsPassive() || aura->IsRemoved())
|
||||
continue;
|
||||
|
||||
if (sPlayerbotAIConfig->dispelAuraDuration && aura->GetDuration() &&
|
||||
@@ -4068,6 +4146,8 @@ bool PlayerbotAI::HasAuraToDispel(Unit* target, uint32 dispelType)
|
||||
continue;
|
||||
|
||||
SpellInfo const* spellInfo = aura->GetSpellInfo();
|
||||
if (!spellInfo)
|
||||
continue;
|
||||
|
||||
bool isPositiveSpell = spellInfo->IsPositive();
|
||||
if (isPositiveSpell && isFriend)
|
||||
@@ -4079,6 +4159,7 @@ bool PlayerbotAI::HasAuraToDispel(Unit* target, uint32 dispelType)
|
||||
if (canDispel(spellInfo, dispelType))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -5709,7 +5790,7 @@ void PlayerbotAI::ImbueItem(Item* item) { ImbueItem(item, TARGET_FLAG_NONE, Obje
|
||||
// item on unit
|
||||
void PlayerbotAI::ImbueItem(Item* item, Unit* target)
|
||||
{
|
||||
if (!target)
|
||||
if (!IsValidUnit(target))
|
||||
return;
|
||||
|
||||
ImbueItem(item, TARGET_FLAG_UNIT, target->GetGUID());
|
||||
@@ -5851,16 +5932,38 @@ int32 PlayerbotAI::GetNearGroupMemberCount(float dis)
|
||||
|
||||
bool PlayerbotAI::CanMove()
|
||||
{
|
||||
// do not allow if not vehicle driver
|
||||
if (IsInVehicle() && !IsInVehicle(true))
|
||||
// Most common checks: confused, stunned, fleeing, jumping, charging. All these
|
||||
// states are set when handling certain aura effects. We don't check against
|
||||
// UNIT_STATE_ROOT here, because this state is used by vehicles.
|
||||
if (bot->HasUnitState(UNIT_STATE_LOST_CONTROL))
|
||||
return false;
|
||||
|
||||
if (bot->isFrozen() || bot->IsPolymorphed() || (bot->isDead() && !bot->HasPlayerFlag(PLAYER_FLAGS_GHOST)) ||
|
||||
bot->IsBeingTeleported() || bot->HasRootAura() || bot->HasSpiritOfRedemptionAura() || bot->HasConfuseAura() ||
|
||||
bot->IsCharmed() || bot->HasStunAura() || bot->IsInFlight() || bot->HasUnitState(UNIT_STATE_LOST_CONTROL))
|
||||
// Death state (w/o spirit release) and Spirit of Redemption aura (priest)
|
||||
if ((bot->isDead() && !bot->HasPlayerFlag(PLAYER_FLAGS_GHOST)) || bot->HasSpiritOfRedemptionAura())
|
||||
return false;
|
||||
|
||||
return bot->GetMotionMaster()->GetCurrentMovementGeneratorType() != FLIGHT_MOTION_TYPE;
|
||||
// Common CC effects, ordered by frequency: rooted > charmed > frozen > polymorphed.
|
||||
// NOTE: Can't find proper way to check if bot is rooted or charmed w/o additional
|
||||
// vehicle check -- when a passenger is added, they become rooted and charmed.
|
||||
if (!bot->GetVehicle() && (bot->IsRooted() || bot->IsCharmed()))
|
||||
return false;
|
||||
if (bot->isFrozen() || bot->IsPolymorphed())
|
||||
return false;
|
||||
|
||||
// Check for the MM controlled slot types: feared, confused, fleeing, etc.
|
||||
if (bot->GetMotionMaster()->GetMotionSlotType(MOTION_SLOT_CONTROLLED) != NULL_MOTION_TYPE)
|
||||
return false;
|
||||
|
||||
// Traveling state: taxi flight and being teleported (relatively rare)
|
||||
if (bot->IsInFlight() || bot->GetMotionMaster()->GetCurrentMovementGeneratorType() == FLIGHT_MOTION_TYPE ||
|
||||
bot->IsBeingTeleported())
|
||||
return false;
|
||||
|
||||
// Vehicle state: is in the vehicle and can control it (rare, content-specific)
|
||||
if ((bot->GetVehicle() && !IsInVehicle(true)))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PlayerbotAI::IsInRealGuild()
|
||||
|
||||
@@ -616,7 +616,15 @@ private:
|
||||
void HandleCommands();
|
||||
void HandleCommand(uint32 type, const std::string& text, Player& fromPlayer, const uint32 lang = LANG_UNIVERSAL);
|
||||
bool _isBotInitializing = false;
|
||||
|
||||
inline bool IsValidUnit(const Unit* unit) const
|
||||
{
|
||||
return unit && unit->IsInWorld() && !unit->IsDuringRemoveFromWorld();
|
||||
}
|
||||
inline bool IsValidPlayer(const Player* player) const
|
||||
{
|
||||
return player && player->GetSession() && player->IsInWorld() && !player->IsDuringRemoveFromWorld() &&
|
||||
!player->IsBeingTeleported();
|
||||
}
|
||||
protected:
|
||||
Player* bot;
|
||||
Player* master;
|
||||
|
||||
@@ -59,7 +59,7 @@ bool AcceptInvitationAction::Execute(Event event)
|
||||
|
||||
if (sPlayerbotAIConfig->summonWhenGroup && bot->GetDistance(inviter) > sPlayerbotAIConfig->sightDistance)
|
||||
{
|
||||
Teleport(inviter, bot);
|
||||
Teleport(inviter, bot, true);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -159,7 +159,7 @@ bool AttackAction::Attack(Unit* target, bool /*with_pet*/ /*true*/)
|
||||
bot->StopMoving();
|
||||
}
|
||||
|
||||
if (IsMovingAllowed() && !bot->HasInArc(CAST_ANGLE_IN_FRONT, target))
|
||||
if (botAI->CanMove() && !bot->HasInArc(CAST_ANGLE_IN_FRONT, target))
|
||||
sServerFacade->SetFacingTo(bot, target);
|
||||
|
||||
botAI->ChangeEngine(BOT_STATE_COMBAT);
|
||||
|
||||
@@ -946,36 +946,7 @@ bool MovementAction::IsWaitingForLastMove(MovementPriority priority)
|
||||
|
||||
bool MovementAction::IsMovingAllowed()
|
||||
{
|
||||
// Most common checks: confused, stunned, fleeing, jumping, charging. All these
|
||||
// states are set when handling certain aura effects. We don't check against
|
||||
// UNIT_STATE_ROOT here, because this state is used by vehicles.
|
||||
if (bot->HasUnitState(UNIT_STATE_LOST_CONTROL))
|
||||
return false;
|
||||
|
||||
// Death state (w/o spirit release) and Spirit of Redemption aura (priest)
|
||||
if ((bot->isDead() && !bot->HasPlayerFlag(PLAYER_FLAGS_GHOST)) || bot->HasSpiritOfRedemptionAura())
|
||||
return false;
|
||||
|
||||
// Common CC effects, ordered by frequency: rooted > frozen > polymorphed
|
||||
if (bot->IsRooted() || bot->isFrozen() || bot->IsPolymorphed())
|
||||
return false;
|
||||
|
||||
// Check for the MM controlled slot types: feared, confused, fleeing, etc.
|
||||
if (bot->GetMotionMaster()->GetMotionSlotType(MOTION_SLOT_CONTROLLED) != NULL_MOTION_TYPE)
|
||||
return false;
|
||||
|
||||
// Traveling state: taxi flight and being teleported (relatively rare)
|
||||
if (bot->IsInFlight() || bot->GetMotionMaster()->GetCurrentMovementGeneratorType() == FLIGHT_MOTION_TYPE ||
|
||||
bot->IsBeingTeleported())
|
||||
return false;
|
||||
|
||||
// Vehicle state: is in the vehicle and can control it (rare, content-specific).
|
||||
// We need to check charmed state AFTER vehicle one, cuz that's how it works:
|
||||
// passengers are set to charmed by vehicle with CHARM_TYPE_VEHICLE.
|
||||
if ((bot->GetVehicle() && !botAI->IsInVehicle(true)) || bot->IsCharmed())
|
||||
return false;
|
||||
|
||||
return true;
|
||||
return botAI->CanMove();
|
||||
}
|
||||
|
||||
bool MovementAction::Follow(Unit* target, float distance) { return Follow(target, distance, GetFollowAngle()); }
|
||||
|
||||
@@ -52,7 +52,7 @@ bool UseMeetingStoneAction::Execute(Event event)
|
||||
if (!goInfo || goInfo->entry != 179944)
|
||||
return false;
|
||||
|
||||
return Teleport(master, bot);
|
||||
return Teleport(master, bot, false);
|
||||
}
|
||||
|
||||
bool SummonAction::Execute(Event event)
|
||||
@@ -70,16 +70,16 @@ bool SummonAction::Execute(Event event)
|
||||
{
|
||||
// botAI->GetAiObjectContext()->GetValue<GuidVector>("prioritized targets")->Set({});
|
||||
AI_VALUE(std::list<FleeInfo>&, "recently flee info").clear();
|
||||
return Teleport(master, bot);
|
||||
return Teleport(master, bot, true);
|
||||
}
|
||||
|
||||
if (SummonUsingGos(master, bot) || SummonUsingNpcs(master, bot))
|
||||
if (SummonUsingGos(master, bot, true) || SummonUsingNpcs(master, bot, true))
|
||||
{
|
||||
botAI->TellMasterNoFacing("Hello!");
|
||||
return true;
|
||||
}
|
||||
|
||||
if (SummonUsingGos(bot, master) || SummonUsingNpcs(bot, master))
|
||||
if (SummonUsingGos(bot, master, true) || SummonUsingNpcs(bot, master, true))
|
||||
{
|
||||
botAI->TellMasterNoFacing("Welcome!");
|
||||
return true;
|
||||
@@ -88,7 +88,7 @@ bool SummonAction::Execute(Event event)
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SummonAction::SummonUsingGos(Player* summoner, Player* player)
|
||||
bool SummonAction::SummonUsingGos(Player* summoner, Player* player, bool preserveAuras)
|
||||
{
|
||||
std::list<GameObject*> targets;
|
||||
AnyGameObjectInObjectRangeCheck u_check(summoner, sPlayerbotAIConfig->sightDistance);
|
||||
@@ -98,14 +98,14 @@ bool SummonAction::SummonUsingGos(Player* summoner, Player* player)
|
||||
for (GameObject* go : targets)
|
||||
{
|
||||
if (go->isSpawned() && go->GetGoType() == GAMEOBJECT_TYPE_MEETINGSTONE)
|
||||
return Teleport(summoner, player);
|
||||
return Teleport(summoner, player, preserveAuras);
|
||||
}
|
||||
|
||||
botAI->TellError(summoner == bot ? "There is no meeting stone nearby" : "There is no meeting stone near you");
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SummonAction::SummonUsingNpcs(Player* summoner, Player* player)
|
||||
bool SummonAction::SummonUsingNpcs(Player* summoner, Player* player, bool preserveAuras)
|
||||
{
|
||||
if (!sPlayerbotAIConfig->summonAtInnkeepersEnabled)
|
||||
return false;
|
||||
@@ -139,7 +139,7 @@ bool SummonAction::SummonUsingNpcs(Player* summoner, Player* player)
|
||||
Spell spell(player, spellInfo, TRIGGERED_NONE);
|
||||
spell.SendSpellCooldown();
|
||||
|
||||
return Teleport(summoner, player);
|
||||
return Teleport(summoner, player, preserveAuras);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -147,7 +147,7 @@ bool SummonAction::SummonUsingNpcs(Player* summoner, Player* player)
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SummonAction::Teleport(Player* summoner, Player* player)
|
||||
bool SummonAction::Teleport(Player* summoner, Player* player, bool preserveAuras)
|
||||
{
|
||||
// Player* master = GetMaster();
|
||||
if (!summoner)
|
||||
@@ -208,7 +208,11 @@ bool SummonAction::Teleport(Player* summoner, Player* player)
|
||||
|
||||
player->GetMotionMaster()->Clear();
|
||||
AI_VALUE(LastMovement&, "last movement").clear();
|
||||
player->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_TELEPORTED | AURA_INTERRUPT_FLAG_CHANGE_MAP);
|
||||
|
||||
if (!preserveAuras)
|
||||
player->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_TELEPORTED |
|
||||
AURA_INTERRUPT_FLAG_CHANGE_MAP);
|
||||
|
||||
player->TeleportTo(mapId, x, y, z, 0);
|
||||
|
||||
if (botAI->HasStrategy("stay", botAI->GetState()))
|
||||
|
||||
@@ -19,9 +19,9 @@ public:
|
||||
bool Execute(Event event) override;
|
||||
|
||||
protected:
|
||||
bool Teleport(Player* summoner, Player* player);
|
||||
bool SummonUsingGos(Player* summoner, Player* player);
|
||||
bool SummonUsingNpcs(Player* summoner, Player* player);
|
||||
bool Teleport(Player* summoner, Player* player, bool preserveAuras);
|
||||
bool SummonUsingGos(Player* summoner, Player* player, bool preserveAuras);
|
||||
bool SummonUsingNpcs(Player* summoner, Player* player, bool preserveAuras);
|
||||
};
|
||||
|
||||
class UseMeetingStoneAction : public SummonAction
|
||||
|
||||
Reference in New Issue
Block a user