latest github + bot fix

This commit is contained in:
mikx
2025-04-20 13:24:05 -04:00
parent 98f7a2ccfe
commit 3a5893003a
50 changed files with 6828 additions and 142 deletions

View File

@@ -14,11 +14,11 @@ class mxwow_bosskill : public PlayerScript
public:
mxwow_bosskill() : PlayerScript("mxwow_bosskill") { }
mxwow_bosskill() : PlayerScript("mxwow_bosskill") {}
void OnPlayerLogin(Player* player) override
{
}
void OnPlayerCreatureKill(Player* player, Creature* boss) {
@@ -39,37 +39,94 @@ public:
float pLife;
bool pGrouped;
if (boss->isWorldBoss() || boss->IsDungeonBoss()) {
if (boss->isWorldBoss()) { mType = "Raid"; } else { mType = "Donjon"; }
if (boss->isWorldBoss()) { mType = "Raid"; }
else { mType = "Donjon"; }
uint32 contentDifId;
bName = boss->GetName();
map = player->GetMap();
pName = player->GetName();
piLevel = player->GetAverageItemLevelForDF();
pMapName = map->GetMapName();
pGrouped = player->GetGroup();
pLife = player->GetHealthPct();
int RandIndex = rand() % 8;
int RandIndex = rand() % 8;
if (player->GetMap()->IsHeroic()) {
contentDifficulty = "|cffff0000";
contentDifName = "|cffff0000Heroic";
contentDifId = 1;
}
else {
contentDifficulty = "|cff00ff00";
contentDifName = "|cff00ff00Normal";
contentDifId = 0;
}
if (sConfigMgr->GetOption<bool>("MxWoW_BossKill.Enabled", true))
{
if (!pGrouped) {
KillManager(player, boss, map);
//ss << "|cffabeeff[MxW] [" << GetPlayerColor(player) << pName << "|cffabeeff][iL"<< piLevel <<"][" << pLife << "%] a tué [|cffff1100" << bName << "|cffabeeff][" << contentDifficulty << pMapName << "|cffabeeff]";
/*pName = p->GetName();
line00 << "|cffabeeff[MxW][" << mType << "]";
line01 << "|cffabeeff[" << contentDifficulty << ""<< pMapName <<"|cffabeeff]["<< contentDifName <<"|cffabeeff]";
line02 << "|cffabeeff["<< GetPlayerColor(player)<< pName <<"|cffabeeff][iL"<< piLevel <<"]["<< pLife <<"%]";
line03 << "|cffabeeff[|cffff1100"<< bName <<"|cffabeeff] "<< GenKillMessage(RandIndex) <<".";
line01 << "|cffabeeff[" << contentDifficulty << "" << pMapName << "|cffabeeff][" << contentDifName << "|cffabeeff]";
line02 << "|cffabeeff[" << GetPlayerColor(player) << pName << "|cffabeeff][iL" << piLevel << "][" << pLife << "%]";
line03 << "|cffabeeff[|cffff1100" << bName << "|cffabeeff] " << GenKillMessage(RandIndex) << ".";
sWorldSessionMgr->SendServerMessage(SERVER_MSG_STRING, line00.str().c_str());
sWorldSessionMgr->SendServerMessage(SERVER_MSG_STRING, line01.str().c_str());
sWorldSessionMgr->SendServerMessage(SERVER_MSG_STRING, line02.str().c_str());
sWorldSessionMgr->SendServerMessage(SERVER_MSG_STRING, line03.str().c_str());
ChatHandler(p->GetSession()).PSendSysMessage(ss.str().c_str());*/
}
}
else {
Group* group = player->GetGroup();
Group::MemberSlotList const& groupSlot = group->GetMemberSlots();
for (Group::member_citerator itr = groupSlot.begin(); itr != groupSlot.end(); itr++)
{
Player* p = ObjectAccessor::FindPlayer(itr->guid);
KillManager(p, boss, map);
/*pName = p->GetName();
line00 << "|cffabeeff[MxW][" << mType << "]";
line01 << "|cffabeeff[" << contentDifficulty << "" << pMapName << "|cffabeeff][" << contentDifName << "|cffabeeff]";
line02 << "|cffabeeff[" << GetPlayerColor(player) << pName << "|cffabeeff][iL" << piLevel << "][" << pLife << "%]";
line03 << "|cffabeeff[|cffff1100" << bName << "|cffabeeff] " << GenKillMessage(RandIndex) << ".";
sWorldSessionMgr->SendServerMessage(SERVER_MSG_STRING, line00.str().c_str());
sWorldSessionMgr->SendServerMessage(SERVER_MSG_STRING, line01.str().c_str());
sWorldSessionMgr->SendServerMessage(SERVER_MSG_STRING, line02.str().c_str());
sWorldSessionMgr->SendServerMessage(SERVER_MSG_STRING, line03.str().c_str());
ChatHandler(p->GetSession()).PSendSysMessage(ss.str().c_str());*/
}
}
}
}
void KillManager(Player* player, Creature* boss, Map* map) {
uint32 aId = player->GetSession()->GetAccountId();
uint32 pId = player->GetGUID().GetRawValue();
uint32 mapId = map->GetId();
uint32 cId = boss->GetCreatureTemplate()->Entry;
uint32 contentDifId;
if (player->GetMap()->IsHeroic()) {
contentDifId = 1;
}
else {
contentDifId = 0;
}
QueryResult queryBossKill = LoginDatabase.Query("SELECT * FROM mxw_boss_kill WHERE accountid = {} AND charid = {} AND creatureid = {} AND instanceid = {} AND difficulty = {}", aId, pId, cId, mapId, contentDifId);
if (queryBossKill) {
uint32 killCount = (*queryBossKill)[5].Get<uint32>();
killCount++;
LoginDatabase.Execute("UPDATE mxw_boss_kill SET killcount = {} WHERE accountid = {} AND charid = {} AND creatureid = {} AND instanceid = {} AND difficulty = {}", killCount, aId, pId, cId, mapId, contentDifId);
}
else {
LoginDatabase.Execute("INSERT INTO mxw_boss_kill (accountid,charid,creatureid,instanceid,difficulty,killcount) VALUES ({}, {}, {}, {}, {}, {})", aId, pId, cId, mapId, contentDifId, 1);
}
QueryResult queryInstanceKill = LoginDatabase.Query("SELECT * FROM mxw_boss_instance WHERE instanceid = {} AND difficulty = {} AND bossid = {}", mapId, contentDifId, cId);
if (queryInstanceKill) {
uint32 killCount = (*queryInstanceKill)[3].Get<uint32>();
killCount++;
LoginDatabase.Execute("UPDATE mxw_boss_instance SET killcount = {} WHERE instanceid = {} AND difficulty = {} AND bossid = {} ", killCount, mapId, contentDifId, cId);
}
else {
LoginDatabase.Execute("INSERT INTO mxw_boss_instance (instanceid,difficulty,bossid,killcount) VALUES ({}, {}, {}, {})", mapId, contentDifId, cId, 1);
}
}
std::string GenKillMessage(const int i)

View File

@@ -1,19 +1,7 @@
[worldserver]
#####################################################################################################
# #
# MxWoW - Boss Kill (Enable / Disable) #
# Description: Enables or disable the script for mxwow_bosskill #
# Default: 1 (Enable) #
# 0 (Disable) #
# #
## MxWoW - Bounty (Enable / Disable)
MxWoW_Bounty.Enabled = 1
#####################################################################################################
#####################################################################################################
# #
# MxWoW - Boss Kill (Enable / Disable) #
# Description: Enables or disable the script for mxwow_bosskill #
# Default: 1 (Enable) #
# 0 (Disable) #
# #
MxWoW_Bounty.Base.Copper = 100
#####################################################################################################
## Value in copper for bounty reward.
MxWoW_Bounty.Base.Copper = 1250
## Hour for daily bounty reset (0,23 / 6 = 6am / 20 = 8pm)
MxWoW_Bounty.Daily.ResetHour = 6

View File

@@ -8,6 +8,8 @@
#include "Player.h"
#include "Chat.h"
#include <WorldSessionMgr.h>
#include <ZoneManager.h>
#include <EventManager.h>
class mxwow_bounty : public PlayerScript
{
@@ -18,7 +20,18 @@ mxwow_bounty() : PlayerScript("mxwow_bounty") { }
void OnPlayerLogin(Player* player) override
{
std::ostringstream ss;
uint32 randZone = rand() % 9;
ss << "Random Zone: " << ZoneManager::RandomZoneId(randZone) << " / " << ZoneManager::RandomZoneNameLocale_frFR(randZone);
ChatHandler(player->GetSession()).PSendSysMessage(ss.str().c_str());
//EventManager::CreateNewEvent(player);
}
void OnPlayerSave(Player* player) {
/*if (EventManager::CheckBountyReset()) {
EventManager::CleanLastEvent();
EventManager::CreateNewEvent(player);
}*/
}
void OnPlayerCreatureKill(Player* player, Creature* creature) {

View File

@@ -73,7 +73,7 @@ public:
expMulti = 10;
break;
}
amount = amount * expMulti;
amount *= expMulti;
if (sConfigMgr->GetOption<bool>("MxWoW_ToonMaster.Verbose", true) && plevel < 80)
{
ss << "|cffabeeff[MxW][ToonMaster]["<< cMPL <<"xNiv.80] Bonus: EXPx"<<expMulti;

View File

@@ -544,6 +544,8 @@ AiPlayerbot.DisableDeathKnightLogin = 0
# Default: 0
AiPlayerbot.LimitTalentsExpansion = 0
# Allow random bots to trade
AiPlayerbot.EnableRandomBotTrading = 1
#
#
#
@@ -636,10 +638,10 @@ AiPlayerbot.HunterWolfPet = 0
#
#
# Specify percent of active bots
# The default is 10. With 10% of all bots going active or inactive each minute. Regardless
# This value is only applied to inactive areas where no real-players are detected, when
# real-players are nearby, friend, group, guild, bg, instances etc the value is always
# enforced to 100%
# The default is 100%, but would be automatically adjusted if botActiveAloneSmartScale
# is enabled. Regardless, this value is only applied to inactive areas where no real-players
# are detected. When real-players are nearby, friend, group, guild, BGs, instances etc,
# the value is always enforced to 100%
AiPlayerbot.BotActiveAlone = 100
# Force botActiveAlone when bot is ... of real player

View File

@@ -374,7 +374,9 @@ void PlayerbotAI::UpdateAIGroupMembership()
PlayerbotAI* leaderAI = GET_PLAYERBOT_AI(leader);
if (leaderAI && !leaderAI->IsRealPlayer())
{
bot->RemoveFromGroup();
WorldPacket* packet = new WorldPacket(CMSG_GROUP_DISBAND);
bot->GetSession()->QueuePacket(packet);
// bot->RemoveFromGroup();
ResetStrategies();
}
}
@@ -399,7 +401,9 @@ void PlayerbotAI::UpdateAIGroupMembership()
}
if (!hasRealPlayer)
{
bot->RemoveFromGroup();
WorldPacket* packet = new WorldPacket(CMSG_GROUP_DISBAND);
bot->GetSession()->QueuePacket(packet);
// bot->RemoveFromGroup();
ResetStrategies();
}
}
@@ -1534,7 +1538,7 @@ void PlayerbotAI::ApplyInstanceStrategies(uint32 mapId, bool tellMaster)
strategyName = "wotlk-hol"; // Halls of Lightning
break;
case 603:
strategyName = "uld";
strategyName = "uld"; // Ulduar
break;
case 604:
strategyName = "wotlk-gd"; // Gundrak
@@ -1551,8 +1555,11 @@ void PlayerbotAI::ApplyInstanceStrategies(uint32 mapId, bool tellMaster)
case 619:
strategyName = "wotlk-ok"; // Ahn'kahet: The Old Kingdom
break;
case 624:
strategyName = "voa"; // Vault of Archavon
break;
case 631:
strategyName = "icc";
strategyName = "icc"; // Icecrown Citadel
break;
case 632:
strategyName = "wotlk-fos"; // The Forge of Souls

View File

@@ -505,6 +505,7 @@ bool PlayerbotAIConfig::Initialize()
limitGearExpansion = sConfigMgr->GetOption<int32>("AiPlayerbot.LimitGearExpansion", 1);
randombotStartingLevel = sConfigMgr->GetOption<int32>("AiPlayerbot.RandombotStartingLevel", 5);
enablePeriodicOnlineOffline = sConfigMgr->GetOption<bool>("AiPlayerbot.EnablePeriodicOnlineOffline", false);
enableRandomBotTrading = sConfigMgr->GetOption<bool>("AiPlayerbot.EnableRandomBotTrading", true);
periodicOnlineOfflineRatio = sConfigMgr->GetOption<float>("AiPlayerbot.PeriodicOnlineOfflineRatio", 2.0);
gearscorecheck = sConfigMgr->GetOption<bool>("AiPlayerbot.GearScoreCheck", false);
randomBotPreQuests = sConfigMgr->GetOption<bool>("AiPlayerbot.PreQuests", true);

View File

@@ -313,6 +313,7 @@ public:
bool autoLearnQuestSpells;
bool autoTeleportForLevel;
bool randomBotGroupNearby;
bool enableRandomBotTrading;
uint32 tweakValue; // Debugging config
uint32 randomBotArenaTeamCount;

View File

@@ -27,6 +27,8 @@
#include "raids/obsidiansanctum/RaidOsActionContext.h"
#include "raids/obsidiansanctum/RaidOsTriggerContext.h"
#include "raids/eyeofeternity/RaidEoEActionContext.h"
#include "raids/vaultofarchavon/RaidVoATriggerContext.h"
#include "raids/vaultofarchavon/RaidVoAActionContext.h"
#include "raids/eyeofeternity/RaidEoETriggerContext.h"
#include "raids/moltencore/RaidMcActionContext.h"
#include "raids/moltencore/RaidMcTriggerContext.h"
@@ -54,6 +56,7 @@ AiObjectContext::AiObjectContext(PlayerbotAI* botAI) : PlayerbotAIAware(botAI)
actionContexts.Add(new RaidNaxxActionContext());
actionContexts.Add(new RaidOsActionContext());
actionContexts.Add(new RaidEoEActionContext());
actionContexts.Add(new RaidVoAActionContext());
actionContexts.Add(new RaidUlduarActionContext());
actionContexts.Add(new RaidIccActionContext());
actionContexts.Add(new WotlkDungeonUKActionContext());
@@ -79,6 +82,7 @@ AiObjectContext::AiObjectContext(PlayerbotAI* botAI) : PlayerbotAIAware(botAI)
triggerContexts.Add(new RaidNaxxTriggerContext());
triggerContexts.Add(new RaidOsTriggerContext());
triggerContexts.Add(new RaidEoETriggerContext());
triggerContexts.Add(new RaidVoATriggerContext());
triggerContexts.Add(new RaidUlduarTriggerContext());
triggerContexts.Add(new RaidIccTriggerContext());
triggerContexts.Add(new WotlkDungeonUKTriggerContext());

View File

@@ -31,7 +31,10 @@ bool AttackEnemyFlagCarrierAction::isUseful()
bool AttackAnythingAction::isUseful()
{
if (!botAI->AllowActivity(GRIND_ACTIVITY)) // Bot not allowed to be active
if (!bot || !botAI) // Prevents invalid accesses
return false;
if (!botAI->AllowActivity(GRIND_ACTIVITY)) // Bot cannot be active
return false;
if (botAI->HasStrategy("stay", BOT_STATE_NON_COMBAT))
@@ -41,19 +44,17 @@ bool AttackAnythingAction::isUseful()
return false;
Unit* target = GetTarget();
if (!target)
if (!target || !target->IsInWorld()) // Checks if the target is valid and in the world
return false;
std::string const name = std::string(target->GetName());
// Check for invalid targets: Dummy, Charge Target, Melee Target, Ranged Target
if (!name.empty() &&
(name.find("Dummy") != std::string::npos ||
name.find("Charge Target") != std::string::npos ||
name.find("Melee Target") != std::string::npos ||
name.find("Ranged Target") != std::string::npos))
{
return false; // Target is one of the disallowed types
return false;
}
return true;

View File

@@ -53,6 +53,9 @@ bool InviteNearbyToGroupAction::Execute(Event event)
if (!player)
continue;
if (!player->GetMapId() != bot->GetMapId())
continue;
if (player->GetGroup())
continue;

View File

@@ -12,6 +12,7 @@
#include "Opcodes.h"
#include "Playerbots.h"
#include "World.h"
#include "WorldPacket.h"
using namespace lfg;
@@ -179,9 +180,12 @@ bool LfgRoleCheckAction::Execute(Event event)
// if (currentRoles == newRoles)
// return false;
sLFGMgr->SetRoles(bot->GetGUID(), newRoles);
sLFGMgr->UpdateRoleCheck(group->GetGUID(), bot->GetGUID(), newRoles);
WorldPacket* packet = new WorldPacket(CMSG_LFG_SET_ROLES);
*packet << (uint8)newRoles;
bot->GetSession()->QueuePacket(packet);
// sLFGMgr->SetRoles(bot->GetGUID(), newRoles);
// sLFGMgr->UpdateRoleCheck(group->GetGUID(), bot->GetGUID(), newRoles);
LOG_INFO("playerbots", "Bot {} {}:{} <{}>: LFG roles checked", bot->GetGUID().ToString().c_str(),
bot->GetTeamId() == TEAM_ALLIANCE ? "A" : "H", bot->GetLevel(), bot->GetName().c_str());
@@ -206,11 +210,13 @@ bool LfgAcceptAction::Execute(Event event)
if (bot->IsInCombat() || bot->isDead())
{
/// @FIXME: Race condition
LOG_INFO("playerbots", "Bot {} {}:{} <{}> is in combat and refuses LFG proposal {}",
bot->GetGUID().ToString().c_str(), bot->GetTeamId() == TEAM_ALLIANCE ? "A" : "H", bot->GetLevel(),
bot->GetName().c_str(), id);
sLFGMgr->UpdateProposal(id, bot->GetGUID(), true);
WorldPacket* packet = new WorldPacket(CMSG_LFG_PROPOSAL_RESULT);
*packet << (uint32)id << (bool)false;
bot->GetSession()->QueuePacket(packet);
// sLFGMgr->UpdateProposal(id, bot->GetGUID(), true);
return true;
}
@@ -220,8 +226,10 @@ bool LfgAcceptAction::Execute(Event event)
botAI->GetAiObjectContext()->GetValue<uint32>("lfg proposal")->Set(0);
bot->ClearUnitState(UNIT_STATE_ALL_STATE);
/// @FIXME: Race condition
sLFGMgr->UpdateProposal(id, bot->GetGUID(), true);
WorldPacket* packet = new WorldPacket(CMSG_LFG_PROPOSAL_RESULT);
*packet << (uint32)id << (bool)true;
bot->GetSession()->QueuePacket(packet);
// sLFGMgr->UpdateProposal(id, bot->GetGUID(), true);
if (sRandomPlayerbotMgr->IsRandomBot(bot) && !bot->GetGroup())
{
@@ -259,7 +267,9 @@ bool LfgLeaveAction::Execute(Event event)
if (sLFGMgr->GetState(bot->GetGUID()) > LFG_STATE_QUEUED)
return false;
sLFGMgr->LeaveLfg(bot->GetGUID());
WorldPacket* packet = new WorldPacket(CMSG_LFG_LEAVE);
bot->GetSession()->QueuePacket(packet);
// sLFGMgr->LeaveLfg(bot->GetGUID());
return true;
}
@@ -278,7 +288,10 @@ bool LfgTeleportAction::Execute(Event event)
bot->ClearUnitState(UNIT_STATE_ALL_STATE);
sLFGMgr->TeleportPlayer(bot, out);
WorldPacket* packet = new WorldPacket(CMSG_LFG_TELEPORT);
*packet << out;
bot->GetSession()->QueuePacket(packet);
// sLFGMgr->TeleportPlayer(bot, out);
return true;
}

View File

@@ -32,6 +32,12 @@ bool TradeStatusAction::Execute(Event event)
return false;
}
if (!sPlayerbotAIConfig->enableRandomBotTrading && sRandomPlayerbotMgr->IsRandomBot(bot))
{
bot->Whisper("Trading is disabled", LANG_UNIVERSAL, trader);
return false;
}
// Allow trades from group members or bots
if ((!bot->GetGroup() || !bot->GetGroup()->IsMember(trader->GetGUID())) &&
(trader != master || !botAI->GetSecurity()->CheckLevelFor(PLAYERBOT_SECURITY_ALLOW_ALL, true, master)) &&
@@ -49,7 +55,7 @@ bool TradeStatusAction::Execute(Event event)
uint32 status;
p >> status;
if (status == TRADE_STATUS_TRADE_ACCEPT)
if (status == TRADE_STATUS_TRADE_ACCEPT || (status == TRADE_STATUS_BACK_TO_TRADE && trader->GetTradeData() && trader->GetTradeData()->IsAccepted()))
{
WorldPacket p;
uint32 status = 0;
@@ -122,12 +128,13 @@ bool TradeStatusAction::Execute(Event event)
void TradeStatusAction::BeginTrade()
{
Player* trader = bot->GetTrader();
if (!trader || GET_PLAYERBOT_AI(bot->GetTrader()))
return;
WorldPacket p;
bot->GetSession()->HandleBeginTradeOpcode(p);
if (GET_PLAYERBOT_AI(bot->GetTrader()))
return;
ListItemsVisitor visitor;
IterateItems(&visitor);
@@ -149,7 +156,7 @@ void TradeStatusAction::BeginTrade()
bool TradeStatusAction::CheckTrade()
{
Player* trader = bot->GetTrader();
if (!bot->GetTradeData() || !trader->GetTradeData())
if (!bot->GetTradeData() || !trader || !trader->GetTradeData())
return false;
if (!botAI->HasActivePlayerMaster() && GET_PLAYERBOT_AI(bot->GetTrader()))

View File

@@ -48,8 +48,8 @@ bool UseMeetingStoneAction::Execute(Event event)
return false;
GameObjectTemplate const* goInfo = gameObject->GetGOInfo();
if (!goInfo || goInfo->type != GAMEOBJECT_TYPE_SUMMONING_RITUAL)
return false;
if (!goInfo || goInfo->entry != 179944)
return false;
return Teleport(master, bot);
}
@@ -217,7 +217,11 @@ bool SummonAction::Teleport(Player* summoner, Player* player)
if (bot->isDead() && revive)
{
if (!botAI->IsSafe(player) || !botAI->IsSafe(summoner))
return false;
bot->ResurrectPlayer(1.0f, false);
bot->SpawnCorpseBones();
botAI->TellMasterNoFacing("I live, again!");
botAI->GetAiObjectContext()->GetValue<GuidVector>("prioritized targets")->Reset();
}
@@ -240,6 +244,7 @@ bool SummonAction::Teleport(Player* summoner, Player* player)
}
}
botAI->TellError("Not enough place to summon");
if(summoner != player)
botAI->TellError("Not enough place to summon");
return false;
}

View File

@@ -6,8 +6,8 @@ bool MoveFromWhirlwindAction::Execute(Event event)
{
Unit* boss = nullptr;
uint8 faction = bot->GetTeamId();
float targetDist = 10.0f; // Whirlwind has range of 8, add a couple for safety buffer
float targetDist = 10.0f; // Whirlwind has a range of 8, adding a safety buffer
switch (bot->GetMap()->GetDifficulty())
{
case DUNGEON_DIFFICULTY_NORMAL:
@@ -15,7 +15,7 @@ bool MoveFromWhirlwindAction::Execute(Event event)
{
boss = AI_VALUE2(Unit*, "find target", "horde commander");
}
else //if (faction == TEAM_HORDE)
else // TEAM_HORDE
{
boss = AI_VALUE2(Unit*, "find target", "alliance commander");
}
@@ -25,7 +25,7 @@ bool MoveFromWhirlwindAction::Execute(Event event)
{
boss = AI_VALUE2(Unit*, "find target", "commander kolurg");
}
else //if (faction == TEAM_HORDE)
else // TEAM_HORDE
{
boss = AI_VALUE2(Unit*, "find target", "commander stoutbeard");
}
@@ -33,11 +33,22 @@ bool MoveFromWhirlwindAction::Execute(Event event)
default:
break;
}
float bossDistance = bot->GetExactDist2d(boss->GetPosition());
if (!boss || bossDistance > targetDist)
// Ensure boss is valid before accessing its methods
if (!boss)
{
return false;
}
float bossDistance = bot->GetExactDist2d(boss->GetPosition());
// Check if the bot is already at a safe distance
if (bossDistance > targetDist)
{
return false;
}
// Move away from the boss to avoid Whirlwind
return MoveAway(boss, targetDist - bossDistance);
}

View File

@@ -10,6 +10,7 @@
#include "RaidMcStrategy.h"
#include "RaidAq20Strategy.h"
#include "RaidIccStrategy.h"
#include "RaidVoAStrategy.h"
class RaidStrategyContext : public NamedObjectContext<Strategy>
{
@@ -25,6 +26,7 @@ public:
creators["naxx"] = &RaidStrategyContext::naxx;
creators["wotlk-os"] = &RaidStrategyContext::wotlk_os;
creators["wotlk-eoe"] = &RaidStrategyContext::wotlk_eoe;
creators["voa"] = &RaidStrategyContext::voa;
creators["uld"] = &RaidStrategyContext::uld;
creators["icc"] = &RaidStrategyContext::icc;
}
@@ -36,6 +38,7 @@ private:
static Strategy* naxx(PlayerbotAI* botAI) { return new RaidNaxxStrategy(botAI); }
static Strategy* wotlk_os(PlayerbotAI* botAI) { return new RaidOsStrategy(botAI); }
static Strategy* wotlk_eoe(PlayerbotAI* botAI) { return new RaidEoEStrategy(botAI); }
static Strategy* voa(PlayerbotAI* botAI) { return new RaidVoAStrategy(botAI); }
static Strategy* uld(PlayerbotAI* botAI) { return new RaidUlduarStrategy(botAI); }
static Strategy* icc(PlayerbotAI* botAI) { return new RaidIccStrategy(botAI); }
};

View File

@@ -1255,32 +1255,36 @@ bool HodirMoveSnowpackedIcicleAction::Execute(Event event)
bool HodirBitingColdJumpAction::Execute(Event event)
{
// This needs improving but maybe it should be done in the playerbot core.
int mapId = bot->GetMap()->GetId();
int x = bot->GetPositionX();
int y = bot->GetPositionY();
int z = bot->GetPositionZ() + 3.98f;
float speed = 7.96f;
UpdateMovementState();
if (!IsMovingAllowed(mapId, x, y, z))
{
return false;
}
MovementPriority priority;
if (IsWaitingForLastMove(priority))
{
return false;
}
MotionMaster& mm = *bot->GetMotionMaster();
mm.Clear();
mm.MoveJump(x, y, z, speed, speed, 1, AI_VALUE(Unit*, "current target"));
mm.MoveFall(0, true);
AI_VALUE(LastMovement&, "last movement").Set(mapId, x, y, z, bot->GetOrientation(), 1000, priority);
bot->RemoveAurasDueToSpell(SPELL_BITING_COLD_PLAYER_AURA);
return true;
// Backup when the overall strategy without cheat will be more vialable
// int mapId = bot->GetMap()->GetId();
// int x = bot->GetPositionX();
// int y = bot->GetPositionY();
// int z = bot->GetPositionZ() + 3.98f;
// float speed = 7.96f;
// UpdateMovementState();
// if (!IsMovingAllowed(mapId, x, y, z))
//{
// return false;
// }
// MovementPriority priority;
// if (IsWaitingForLastMove(priority))
//{
// return false;
// }
// MotionMaster& mm = *bot->GetMotionMaster();
// mm.Clear();
// mm.MoveJump(x, y, z, speed, speed, 1, AI_VALUE(Unit*, "current target"));
// mm.MoveFall(0, true);
// AI_VALUE(LastMovement&, "last movement").Set(mapId, x, y, z, bot->GetOrientation(), 1000, priority);
// return true;
}
bool FreyaMoveAwayNatureBombAction::isUseful()

View File

@@ -287,13 +287,12 @@ bool HodirBitingColdTrigger::IsActive()
return false;
}
// Override if boss is casting Flash Freeze
if (!boss->HasUnitState(UNIT_STATE_CASTING) || !boss->FindCurrentSpellBySpellId(SPELL_FLASH_FREEZE))
{
return true;
}
Player* master = botAI->GetMaster();
if (!master || !master->IsAlive())
return false;
return boss && botAI->GetAura("biting cold", bot, false, false, 2);
return botAI->GetAura("biting cold", bot, false, false, 2) &&
!botAI->GetAura("biting cold", master, false, false, 2);
}
//Snowpacked Icicle Target

View File

@@ -21,6 +21,7 @@ enum UlduarIDs
NPC_SNOWPACKED_ICICLE = 33174,
NPC_TOASTY_FIRE = 33342,
SPELL_FLASH_FREEZE = 61968,
SPELL_BITING_COLD_PLAYER_AURA = 62039,
// Freya
NPC_EONARS_GIFT = 33228,

View File

@@ -242,7 +242,7 @@ bool NewRpgMoveNpcAction::Execute(Event event)
}
WorldObject* object = ObjectAccessor::GetWorldObject(*bot, info.near_npc.npcOrGo);
if (object && bot->GetDistance(object) <= INTERACTION_DISTANCE)
if (object && IsWithinInteractionDist(object))
{
if (!info.near_npc.lastReach)
{

View File

@@ -247,6 +247,134 @@ bool NewRpgBaseAction::InteractWithNpcOrGameObjectForQuest(ObjectGuid guid)
return true;
}
bool NewRpgBaseAction::CanInteractWithQuestGiver(Object* questGiver)
{
// This is a variant of Player::CanInteractWithQuestGiver
// that removes the distance check and keeps all other checks
switch (questGiver->GetTypeId())
{
case TYPEID_UNIT:
{
ObjectGuid guid = questGiver->GetGUID();
uint32 npcflagmask = UNIT_NPC_FLAG_QUESTGIVER;
// unit checks
if (!guid)
return false;
if (!bot->IsInWorld())
return false;
if (bot->IsInFlight())
return false;
// exist (we need look pets also for some interaction (quest/etc)
Creature* creature = ObjectAccessor::GetCreatureOrPetOrVehicle(*bot, guid);
if (!creature)
return false;
// Deathstate checks
if (!bot->IsAlive() && !(creature->GetCreatureTemplate()->type_flags & CREATURE_TYPE_FLAG_VISIBLE_TO_GHOSTS))
return false;
// alive or spirit healer
if (!creature->IsAlive() && !(creature->GetCreatureTemplate()->type_flags & CREATURE_TYPE_FLAG_INTERACT_WHILE_DEAD))
return false;
// appropriate npc type
if (npcflagmask && !creature->HasNpcFlag(NPCFlags(npcflagmask)))
return false;
// not allow interaction under control, but allow with own pets
if (creature->GetCharmerGUID())
return false;
// xinef: perform better check
if (creature->GetReactionTo(bot) <= REP_UNFRIENDLY)
return false;
// pussywizard: many npcs have missing conditions for class training and rogue trainer can for eg. train dual wield to a shaman :/ too many to change in sql and watch in the future
// pussywizard: this function is not used when talking, but when already taking action (buy spell, reset talents, show spell list)
if (npcflagmask & (UNIT_NPC_FLAG_TRAINER | UNIT_NPC_FLAG_TRAINER_CLASS) && creature->GetCreatureTemplate()->trainer_type == TRAINER_TYPE_CLASS && !bot->IsClass((Classes)creature->GetCreatureTemplate()->trainer_class, CLASS_CONTEXT_CLASS_TRAINER))
return false;
return true;
}
case TYPEID_GAMEOBJECT:
{
ObjectGuid guid = questGiver->GetGUID();
GameobjectTypes type = GAMEOBJECT_TYPE_QUESTGIVER;
if (GameObject* go = bot->GetMap()->GetGameObject(guid))
{
if (go->GetGoType() == type)
{
// Players cannot interact with gameobjects that use the "Point" icon
if (go->GetGOInfo()->IconName == "Point")
{
return false;
}
return true;
}
}
return false;
}
// unused for now
// case TYPEID_PLAYER:
// return bot->IsAlive() && questGiver->ToPlayer()->IsAlive();
// case TYPEID_ITEM:
// return bot->IsAlive();
default:
break;
}
return false;
}
bool NewRpgBaseAction::IsWithinInteractionDist(Object* questGiver)
{
// This is a variant of Player::CanInteractWithQuestGiver
// that only keep the distance check
switch (questGiver->GetTypeId())
{
case TYPEID_UNIT:
{
ObjectGuid guid = questGiver->GetGUID();
// unit checks
if (!guid)
return false;
// exist (we need look pets also for some interaction (quest/etc)
Creature* creature = ObjectAccessor::GetCreatureOrPetOrVehicle(*bot, guid);
if (!creature)
return false;
if (!creature->IsWithinDistInMap(bot, INTERACTION_DISTANCE))
return false;
return true;
}
case TYPEID_GAMEOBJECT:
{
ObjectGuid guid = questGiver->GetGUID();
GameobjectTypes type = GAMEOBJECT_TYPE_QUESTGIVER;
if (GameObject* go = bot->GetMap()->GetGameObject(guid))
{
if (go->IsWithinDistInMap(bot))
{
return true;
}
}
return false;
}
// case TYPEID_PLAYER:
// return bot->IsAlive() && questGiver->ToPlayer()->IsAlive();
// case TYPEID_ITEM:
// return bot->IsAlive();
default:
break;
}
return false;
}
bool NewRpgBaseAction::AcceptQuest(Quest const* quest, ObjectGuid guid)
{
WorldPacket p(CMSG_QUESTGIVER_ACCEPT_QUEST);
@@ -283,7 +411,7 @@ bool NewRpgBaseAction::TurnInQuest(Quest const* quest, ObjectGuid guid)
}
else
{
uint32 bestId = BestReward(quest);
uint32 bestId = BestRewardIndex(quest);
p << bestId;
bot->GetSession()->HandleQuestgiverChooseRewardOpcode(p);
}
@@ -291,7 +419,7 @@ bool NewRpgBaseAction::TurnInQuest(Quest const* quest, ObjectGuid guid)
return true;
}
uint32 NewRpgBaseAction::BestReward(Quest const* quest)
uint32 NewRpgBaseAction::BestRewardIndex(Quest const* quest)
{
ItemIds returnIds;
ItemUsage bestUsage = ITEM_USAGE_NONE;
@@ -486,7 +614,7 @@ ObjectGuid NewRpgBaseAction::ChooseNpcOrGameObjectToInteract(bool questgiverOnly
if (distanceLimit && bot->GetDistance(object) > distanceLimit)
continue;
if (HasQuestToAcceptOrReward(object))
if (CanInteractWithQuestGiver(object) && HasQuestToAcceptOrReward(object))
{
if (!nearestObject || bot->GetExactDist(nearestObject) > bot->GetExactDist(object))
nearestObject = object;
@@ -504,7 +632,7 @@ ObjectGuid NewRpgBaseAction::ChooseNpcOrGameObjectToInteract(bool questgiverOnly
if (distanceLimit && bot->GetDistance(object) > distanceLimit)
continue;
if (HasQuestToAcceptOrReward(object))
if (CanInteractWithQuestGiver(object) && HasQuestToAcceptOrReward(object))
{
if (!nearestObject || bot->GetExactDist(nearestObject) > bot->GetExactDist(object))
nearestObject = object;

View File

@@ -32,16 +32,19 @@ protected:
bool MoveRandomNear(float moveStep = 50.0f, MovementPriority priority = MovementPriority::MOVEMENT_NORMAL);
bool ForceToWait(uint32 duration, MovementPriority priority = MovementPriority::MOVEMENT_NORMAL);
// QUEST RELATED
bool SearchQuestGiverAndAcceptOrReward();
// QUEST RELATED CHECK
ObjectGuid ChooseNpcOrGameObjectToInteract(bool questgiverOnly = false, float distanceLimit = 0.0f);
bool HasQuestToAcceptOrReward(WorldObject* object);
bool InteractWithNpcOrGameObjectForQuest(ObjectGuid guid);
bool AcceptQuest(Quest const* quest, ObjectGuid guid);
bool TurnInQuest(Quest const* quest, ObjectGuid guid);
uint32 BestReward(Quest const* quest);
bool CanInteractWithQuestGiver(Object* questGiver);
bool IsWithinInteractionDist(Object* object);
uint32 BestRewardIndex(Quest const* quest);
bool IsQuestWorthDoing(Quest const* quest);
bool IsQuestCapableDoing(Quest const* quest);
// QUEST RELATED ACTION
bool SearchQuestGiverAndAcceptOrReward();
bool AcceptQuest(Quest const* quest, ObjectGuid guid);
bool TurnInQuest(Quest const* quest, ObjectGuid guid);
bool OrganizeQuestLog();
protected:

View File

@@ -21,6 +21,9 @@ Unit* FlagCarrierValue::Calculate()
{
BattlegroundWS* bg = (BattlegroundWS*)botAI->GetBot()->GetBattleground();
if (!bg)
return nullptr;
if ((!sameTeam && bot->GetTeamId() == TEAM_HORDE || (sameTeam && bot->GetTeamId() == TEAM_ALLIANCE)) &&
!bg->GetFlagPickerGUID(TEAM_HORDE).IsEmpty())
carrier = ObjectAccessor::GetPlayer(bg->GetBgMap(), bg->GetFlagPickerGUID(TEAM_HORDE));
@@ -44,6 +47,9 @@ Unit* FlagCarrierValue::Calculate()
{
BattlegroundEY* bg = (BattlegroundEY*)botAI->GetBot()->GetBattleground();
if (!bg)
return nullptr;
if (bg->GetFlagPickerGUID().IsEmpty())
return nullptr;