Files
mod-playerbots/src/Ai/Base/Actions/UseMeetingStoneAction.cpp
bashermens 13fff46fa0 Improper singletons migration to clean Meyer's singletons (cherry-pick) (#2082)
# Pull Request

- Applies the clean and corrected singletons, Meyer pattern. (cherry
picked from @SmashingQuasar )

Testing by just playing the game in various ways. Been tested by myself
@Celandriel and @SmashingQuasar
---

## Complexity & Impact

- Does this change add new decision branches?
    - [x] No
    - [ ] Yes (**explain below**)

- Does this change increase per-bot or per-tick processing?
    - [x] No
    - [ ] Yes (**describe and justify impact**)

- Could this logic scale poorly under load?
    - [x] No
    - [ ] Yes (**explain why**)

---

## Defaults & Configuration

- Does this change modify default bot behavior?
    - [x] No
    - [ ] Yes (**explain why**)

---

## AI Assistance

- Was AI assistance (e.g. ChatGPT or similar tools) used while working
on this change?
    - [x] No
    - [ ] Yes (**explain below**)
---

## Final Checklist

- [x] Stability is not compromised
- [x] Performance impact is understood, tested, and acceptable
- [x] Added logic complexity is justified and explained
- [x] Documentation updated if needed

---

## Notes for Reviewers

Anything that significantly improves realism at the cost of stability or
performance should be carefully discussed
before merging.

---------

Co-authored-by: Nicolas Lebacq <nicolas.cordier@outlook.com>
Co-authored-by: Keleborn <22352763+Celandriel@users.noreply.github.com>
2026-01-30 21:49:37 +01:00

237 lines
7.9 KiB
C++

/*
* 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 "UseMeetingStoneAction.h"
#include "CellImpl.h"
#include "Event.h"
#include "GridNotifiers.h"
#include "GridNotifiersImpl.h"
#include "NearestGameObjects.h"
#include "PlayerbotAIConfig.h"
#include "Playerbots.h"
#include "PositionValue.h"
bool UseMeetingStoneAction::Execute(Event event)
{
Player* master = GetMaster();
if (!master)
return false;
WorldPacket p(event.getPacket());
p.rpos(0);
ObjectGuid guid;
p >> guid;
if (master->GetTarget() && master->GetTarget() != bot->GetGUID())
return false;
if (!master->GetTarget() && master->GetGroup() != bot->GetGroup())
return false;
if (master->IsBeingTeleported())
return false;
if (bot->IsInCombat())
{
botAI->TellError("I am in combat");
return false;
}
Map* map = master->GetMap();
if (!map)
return false;
GameObject* gameObject = map->GetGameObject(guid);
if (!gameObject)
return false;
GameObjectTemplate const* goInfo = gameObject->GetGOInfo();
if (!goInfo || goInfo->entry != 179944)
return false;
return Teleport(master, bot, false);
}
bool SummonAction::Execute(Event event)
{
Player* master = GetMaster();
if (!master)
return false;
if (Pet* pet = bot->GetPet())
{
botAI->PetFollow();
}
if (master->GetSession()->GetSecurity() >= SEC_PLAYER)
{
// botAI->GetAiObjectContext()->GetValue<GuidVector>("prioritized targets")->Set({});
AI_VALUE(std::list<FleeInfo>&, "recently flee info").clear();
return Teleport(master, bot, true);
}
if (SummonUsingGos(master, bot, true) || SummonUsingNpcs(master, bot, true))
{
botAI->TellMasterNoFacing("Hello!");
return true;
}
if (SummonUsingGos(bot, master, true) || SummonUsingNpcs(bot, master, true))
{
botAI->TellMasterNoFacing("Welcome!");
return true;
}
return false;
}
bool SummonAction::SummonUsingGos(Player* summoner, Player* player, bool preserveAuras)
{
std::list<GameObject*> targets;
AnyGameObjectInObjectRangeCheck u_check(summoner, sPlayerbotAIConfig.sightDistance);
Acore::GameObjectListSearcher<AnyGameObjectInObjectRangeCheck> searcher(summoner, targets, u_check);
Cell::VisitObjects(summoner, searcher, sPlayerbotAIConfig.sightDistance);
for (GameObject* go : targets)
{
if (go->isSpawned() && go->GetGoType() == GAMEOBJECT_TYPE_MEETINGSTONE)
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 preserveAuras)
{
if (!sPlayerbotAIConfig.summonAtInnkeepersEnabled)
return false;
std::list<Unit*> targets;
Acore::AnyUnitInObjectRangeCheck u_check(summoner, sPlayerbotAIConfig.sightDistance);
Acore::UnitListSearcher<Acore::AnyUnitInObjectRangeCheck> searcher(summoner, targets, u_check);
Cell::VisitObjects(summoner, searcher, sPlayerbotAIConfig.sightDistance);
for (Unit* unit : targets)
{
if (unit && unit->HasNpcFlag(UNIT_NPC_FLAG_INNKEEPER))
{
if (!player->HasItemCount(6948, 1, false))
{
botAI->TellError(player == bot ? "I have no hearthstone" : "You have no hearthstone");
return false;
}
if (player->HasSpellCooldown(8690))
{
botAI->TellError(player == bot ? "My hearthstone is not ready" : "Your hearthstone is not ready");
return false;
}
// Trigger cooldown
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(8690);
if (!spellInfo)
return false;
Spell spell(player, spellInfo, TRIGGERED_NONE);
spell.SendSpellCooldown();
return Teleport(summoner, player, preserveAuras);
}
}
botAI->TellError(summoner == bot ? "There are no innkeepers nearby" : "There are no innkeepers near you");
return false;
}
bool SummonAction::Teleport(Player* summoner, Player* player, bool preserveAuras)
{
// Player* master = GetMaster();
if (!summoner || summoner == player)
return false;
if (player->GetVehicle())
{
botAI->TellError("You cannot summon me while I'm on a vehicle");
return false;
}
if (!summoner->IsBeingTeleported() && !player->IsBeingTeleported())
{
float followAngle = GetFollowAngle();
for (float angle = followAngle - M_PI; angle <= followAngle + M_PI; angle += M_PI / 4)
{
uint32 mapId = summoner->GetMapId();
float x = summoner->GetPositionX() + cos(angle) * sPlayerbotAIConfig.followDistance;
float y = summoner->GetPositionY() + sin(angle) * sPlayerbotAIConfig.followDistance;
float z = summoner->GetPositionZ();
if (summoner->IsWithinLOS(x, y, z))
{
if (sPlayerbotAIConfig.botRepairWhenSummon) // .conf option to repair bot gear when summoned 0 = off, 1 = on
bot->DurabilityRepairAll(false, 1.0f, false);
if (summoner->IsInCombat() && !sPlayerbotAIConfig.allowSummonInCombat)
{
botAI->TellError("You cannot summon me while you're in combat");
return false;
}
if (!summoner->IsAlive() && !sPlayerbotAIConfig.allowSummonWhenMasterIsDead)
{
botAI->TellError("You cannot summon me while you're dead");
return false;
}
if (bot->isDead() && !bot->HasPlayerFlag(PLAYER_FLAGS_GHOST) &&
!sPlayerbotAIConfig.allowSummonWhenBotIsDead)
{
botAI->TellError("You cannot summon me while I'm dead, you need to release my spirit first");
return false;
}
bool revive =
sPlayerbotAIConfig.reviveBotWhenSummoned == 2 ||
(sPlayerbotAIConfig.reviveBotWhenSummoned == 1 && !summoner->IsInCombat() && summoner->IsAlive());
if (bot->isDead() && revive)
{
bot->ResurrectPlayer(1.0f, false);
bot->SpawnCorpseBones();
botAI->TellMasterNoFacing("I live, again!");
botAI->GetAiObjectContext()->GetValue<GuidVector>("prioritized targets")->Reset();
}
player->GetMotionMaster()->Clear();
AI_VALUE(LastMovement&, "last movement").clear();
if (!preserveAuras)
player->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_TELEPORTED |
AURA_INTERRUPT_FLAG_CHANGE_MAP);
player->TeleportTo(mapId, x, y, z, 0);
if (player->GetPet())
player->GetPet()->NearTeleportTo(x, y, z, player->GetOrientation());
if (player->GetGuardianPet())
player->GetGuardianPet()->NearTeleportTo(x, y, z, player->GetOrientation());
if (botAI->HasStrategy("stay", botAI->GetState()))
{
PositionMap& posMap = AI_VALUE(PositionMap&, "position");
PositionInfo stayPosition = posMap["stay"];
stayPosition.Set(x,y, z, mapId);
posMap["stay"] = stayPosition;
}
return true;
}
}
}
if (summoner != player)
botAI->TellError("Not enough place to summon");
return false;
}