Merge pull request #2172 from mod-playerbots/test-staging

Update master from Test staging and Core Update
This commit is contained in:
kadeshar
2026-02-27 22:55:19 +01:00
committed by GitHub
33 changed files with 489 additions and 267 deletions

View File

@@ -6,10 +6,6 @@ on:
- reopened
- synchronize
- ready_for_review
paths:
- src/**
- "!README.md"
- "!docs/**"
concurrency:
group: "codestyle-cppcheck-${{ github.event.pull_request.number }}"
@@ -22,13 +18,27 @@ jobs:
if: github.event.pull_request.draft == false
steps:
- uses: actions/checkout@v4
- uses: dorny/paths-filter@v3
id: filter
with:
filters: |
cpp:
- 'src/**'
- '!README.md'
- '!docs/**'
- name: Setup python
if: steps.filter.outputs.cpp == 'true'
uses: actions/setup-python@v5
with:
python-version: '3.10'
- name: AzerothCore codestyle
if: steps.filter.outputs.cpp == 'true'
run: python ./apps/codestyle/codestyle-cpp.py
- name: C++ Advanced
if: steps.filter.outputs.cpp == 'true'
run: |
sudo apt update -y
sudo apt install -y cppcheck

View File

@@ -192,9 +192,12 @@ AiPlayerbot.AutoInitOnly = 0
# Default: 1.0 (same with the player)
AiPlayerbot.AutoInitEquipLevelLimitRatio = 1.0
# Bot automatically trains spells when talking to trainer
# yes = train all available spells as long as the bot has the money, free = auto trains with no money cost, no = only list spells
AiPlayerbot.AutoTrainSpells = yes
#
# AllowLearnTrainerSpells
# Description: Allow the bot to learn trainers' spells as long as it has the money.
# Default: 1 - (Enabled)
# 0 - (Disabled)
AiPlayerbot.AllowLearnTrainerSpells = 1
#
#
@@ -563,6 +566,34 @@ AiPlayerbot.AutoGearScoreLimit = 0
# Default: food, taxi, and raid are enabled
AiPlayerbot.BotCheats = "food,taxi,raid"
# Attunement quests (comma-separated list of quest IDs)
# Default:
# Caverns of Time - Part 1
# - 10279, To The Master's Lair
# - 10277, The Caverns of Time
#
# Caverns of Time - Part 2 (Escape from Durnholde Keep)
# - 10282, Old Hillsbrad
# - 10283, Taretha's Diversion
# - 10284, Escape from Durnholde
# - 10285, Return to Andormu
#
# Caverns of Time - Part 2 (The Black Morass)
# - 10296, The Black Morass
# - 10297, The Opening of the Dark Portal
# - 10298, Hero of the Brood
#
# Magister's Terrace Attunement
# - 11481, Crisis at the Sunwell
# - 11482, Duty Calls
# - 11488, Magisters' Terrace
# - 11490, The Scryer's Scryer
# - 11492, Hard to Kill
#
# Serpentshrine Cavern
# - 10901, The Cudgel of Kar'desh
AiPlayerbot.AttunementQuests = 10279,10277,10282,10283,10284,10285,10296,10297,10298,11481,11482,11488,11490,11492,10901
#
#
#

View File

@@ -125,6 +125,7 @@ public:
creators["runaway"] = &ActionContext::runaway;
creators["stay"] = &ActionContext::stay;
creators["sit"] = &ActionContext::sit;
creators["aggressive target"] = &ActionContext::aggressive_target;
creators["attack anything"] = &ActionContext::attack_anything;
creators["attack least hp target"] = &ActionContext::attack_least_hp_target;
creators["attack enemy player"] = &ActionContext::attack_enemy_player;
@@ -315,6 +316,7 @@ private:
static Action* suggest_what_to_do(PlayerbotAI* botAI) { return new SuggestWhatToDoAction(botAI); }
static Action* suggest_trade(PlayerbotAI* botAI) { return new SuggestTradeAction(botAI); }
static Action* suggest_dungeon(PlayerbotAI* botAI) { return new SuggestDungeonAction(botAI); }
static Action* aggressive_target(PlayerbotAI* botAI) { return new AggressiveTargetAction(botAI); }
static Action* attack_anything(PlayerbotAI* botAI) { return new AttackAnythingAction(botAI); }
static Action* attack_least_hp_target(PlayerbotAI* botAI) { return new AttackLeastHpTargetAction(botAI); }
static Action* attack_enemy_player(PlayerbotAI* botAI) { return new AttackEnemyPlayerAction(botAI); }

View File

@@ -30,6 +30,14 @@ bool AttackEnemyFlagCarrierAction::isUseful()
PlayerHasFlag::IsCapturingFlag(bot);
}
bool AggressiveTargetAction::isUseful()
{
if (bot->IsInCombat())
return false;
return true;
}
bool DropTargetAction::Execute(Event /*event*/)
{
Unit* target = context->GetValue<Unit*>("current target")->Get();

View File

@@ -35,6 +35,15 @@ public:
std::string const GetTargetName() override { return "tank target"; }
};
class AggressiveTargetAction : public AttackAction
{
public:
AggressiveTargetAction(PlayerbotAI* botAI) : AttackAction(botAI, "aggressive target") {}
std::string const GetTargetName() override { return "aggressive target"; }
bool isUseful() override;
};
class AttackAnythingAction : public AttackAction
{
public:

View File

@@ -7,10 +7,13 @@
#include "PlayerbotAI.h"
#include "InstancePackets.h"
bool ResetInstancesAction::Execute(Event /*event*/)
{
WorldPacket packet(CMSG_RESET_INSTANCES, 0);
bot->GetSession()->HandleResetInstancesOpcode(packet);
WorldPackets::Instance::ResetInstances resetInstance(std::move(packet));
bot->GetSession()->HandleResetInstancesOpcode(resetInstance);
return true;
}

View File

@@ -5,6 +5,7 @@
#include "RpgSubActions.h"
#include "BudgetValues.h"
#include "ChooseRpgTargetAction.h"
#include "EmoteAction.h"
#include "Formations.h"
@@ -51,10 +52,15 @@ GuidPosition RpgHelper::guidP() { return AI_VALUE(GuidPosition, "rpg target"); }
ObjectGuid RpgHelper::guid() { return (ObjectGuid)guidP(); }
bool RpgHelper::InRange()
{
return guidP() ? (guidP().sqDistance2d(bot) < INTERACTION_DISTANCE * INTERACTION_DISTANCE) : false;
}
bool RpgHelper::InRange()
{
GuidPosition targetGuid = guidP();
if (!targetGuid)
return false;
return bot->GetExactDist2dSq(targetGuid.GetPositionX(), targetGuid.GetPositionY()) <
INTERACTION_DISTANCE * INTERACTION_DISTANCE;
}
void RpgHelper::setFacingTo(GuidPosition guidPosition)
{
@@ -250,6 +256,60 @@ Event RpgSellAction::ActionEvent(Event /*event*/) { return Event("rpg action", "
std::string const RpgRepairAction::ActionName() { return "repair"; }
bool RpgTrainAction::isUseful()
{
if (!rpg->InRange())
return false;
Creature* creature = rpg->guidP().GetCreature();
if (!creature)
return false;
if (!creature->IsInWorld() || creature->IsDuringRemoveFromWorld() || !creature->IsAlive())
return false;
return true;
}
bool RpgTrainAction::isPossible()
{
GuidPosition gp = rpg->guidP();
CreatureTemplate const* cinfo = gp.GetCreatureTemplate();
if (!cinfo)
return false;
Trainer::Trainer* trainer = sObjectMgr->GetTrainer(cinfo->Entry);
if (!trainer)
return false;
if (!trainer->IsTrainerValidForPlayer(bot))
return false;
FactionTemplateEntry const* factionTemplate = sFactionTemplateStore.LookupEntry(cinfo->faction);
float reputationDiscount = bot->GetReputationPriceDiscount(factionTemplate);
uint32 currentGold = AI_VALUE2(uint32, "free money for", (uint32)NeedMoneyFor::spells);
for (auto& spell : trainer->GetSpells())
{
Trainer::Spell const* trainerSpell = trainer->GetSpell(spell.SpellId);
if (!trainerSpell)
continue;
if (!trainer->CanTeachSpell(bot, trainerSpell))
continue;
if (currentGold < static_cast<uint32>(floor(trainerSpell->MoneyCost * reputationDiscount)))
continue;
// we only check if at least one spell can be learned from the trainer;
// otherwise, the train action should not be allowed
return true;
}
return false;
}
std::string const RpgTrainAction::ActionName() { return "trainer"; }
bool RpgHealAction::Execute(Event /*event*/)

View File

@@ -165,6 +165,9 @@ class RpgTrainAction : public RpgSubAction
public:
RpgTrainAction(PlayerbotAI* botAI, std::string const name = "rpg train") : RpgSubAction(botAI, name) {}
bool isPossible() override;
bool isUseful() override;
private:
std::string const ActionName() override;
};

View File

@@ -9,77 +9,120 @@
#include "Event.h"
#include "PlayerbotFactory.h"
#include "Playerbots.h"
#include "Trainer.h"
void TrainerAction::Learn(uint32 cost, const Trainer::Spell tSpell, std::ostringstream& msg)
bool TrainerAction::Execute(Event event)
{
if (sPlayerbotAIConfig.autoTrainSpells != "free" && !botAI->HasCheat(BotCheatMask::gold))
{
if (AI_VALUE2(uint32, "free money for", (uint32)NeedMoneyFor::spells) < cost)
{
msg << " - too expensive";
return;
}
std::string const param = event.getParam();
bot->ModifyMoney(-int32(cost));
}
Creature* target = GetCreatureTarget();
if (!target)
return false;
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(tSpell.SpellId);
if (!spellInfo)
return;
Trainer::Trainer* trainer = sObjectMgr->GetTrainer(target->GetEntry());
if (!trainer)
return false;
bool learned = false;
for (uint8 j = 0; j < 3; ++j)
{
if (spellInfo->Effects[j].Effect == SPELL_EFFECT_LEARN_SPELL)
{
uint32 learnedSpell = spellInfo->Effects[j].TriggerSpell;
if (!bot->HasSpell(learnedSpell))
{
bot->learnSpell(learnedSpell);
learned = true;
}
}
}
// NOTE: Original version uses SpellIds here, but occasionally only inserts
// a single spell ID value from parameters. If someone wants to impl multiple
// spells as parameters, check SkipSpellsListAction::parseIds as an example.
uint32 spellId = chat->parseSpell(param);
if (!learned && !bot->HasSpell(tSpell.SpellId))
bot->learnSpell(tSpell.SpellId);
bool learnSpells = param.find("learn") != std::string::npos || sRandomPlayerbotMgr.IsRandomBot(bot) ||
(sPlayerbotAIConfig.allowLearnTrainerSpells &&
// TODO: Rewrite to only exclude start primary profession skills and make config dependent.
(trainer->GetTrainerType() != Trainer::Type::Tradeskill || !botAI->HasActivePlayerMaster()));
msg << " - learned";
Iterate(target, learnSpells, spellId);
return true;
}
void TrainerAction::Iterate(Creature* creature, TrainerSpellAction action, SpellIds& spells)
bool TrainerAction::isUseful()
{
Creature* target = GetCreatureTarget();
if (!target)
return false;
if (!target->IsInWorld() || target->IsDuringRemoveFromWorld() || !target->IsAlive())
return false;
return target->IsTrainer();
}
bool TrainerAction::isPossible()
{
Creature* target = GetCreatureTarget();
if (!target)
return false;
Trainer::Trainer* trainer = sObjectMgr->GetTrainer(target->GetEntry());
if (!trainer)
return false;
if (!trainer->IsTrainerValidForPlayer(bot))
return false;
if (trainer->GetSpells().empty())
return false;
return true;
}
Unit* TrainerAction::GetTarget()
{
// There are just two scenarios: the bot has a master or it doesn't. If the
// bot has a master, the master should target a unit; otherwise, the bot
// should target the unit itself.
if (Player* master = GetMaster())
return master->GetSelectedUnit();
return bot->GetSelectedUnit();
}
Creature* TrainerAction::GetCreatureTarget()
{
Unit* target = GetTarget();
return target ? target->ToCreature() : nullptr;
}
void TrainerAction::Iterate(Creature* creature, bool learnSpells, uint32 spellId)
{
TellHeader(creature);
Trainer::Trainer* trainer = sObjectMgr->GetTrainer(creature->GetEntry());
if (!trainer)
return;
float fDiscountMod = bot->GetReputationPriceDiscount(creature);
float reputationDiscount = bot->GetReputationPriceDiscount(creature);
uint32 totalCost = 0;
for (auto& spell : trainer->GetSpells())
{
if (!trainer->CanTeachSpell(bot, trainer->GetSpell(spell.SpellId)))
// simplified version of Trainer::TeachSpell method
Trainer::Spell const* trainerSpell = trainer->GetSpell(spell.SpellId);
if (!trainerSpell)
continue;
if (!spells.empty() && spells.find(spell.SpellId) == spells.end())
if (!trainer->CanTeachSpell(bot, trainerSpell))
continue;
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spell.SpellId);
if (spellId && trainerSpell->SpellId != spellId)
continue;
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(trainerSpell->SpellId);
if (!spellInfo)
continue;
uint32 cost = uint32(floor(spell.MoneyCost * fDiscountMod));
uint32 cost = static_cast<uint32>(floor(trainerSpell->MoneyCost * reputationDiscount));
totalCost += cost;
std::ostringstream out;
out << chat->FormatSpell(spellInfo) << chat->formatMoney(cost);
if (action)
(this->*action)(cost, spell, out);
if (learnSpells)
Learn(spellInfo, cost, out);
botAI->TellMaster(out);
}
@@ -87,55 +130,25 @@ void TrainerAction::Iterate(Creature* creature, TrainerSpellAction action, Spell
TellFooter(totalCost);
}
bool TrainerAction::Execute(Event event)
void TrainerAction::Learn(SpellInfo const* spellInfo, uint32 cost, std::ostringstream& out)
{
std::string const text = event.getParam();
Player* master = GetMaster();
Creature* creature = botAI->GetCreature(bot->GetTarget());
if (master)
if (!botAI->HasCheat(BotCheatMask::gold))
{
creature = master->GetSelectedUnit() ? master->GetSelectedUnit()->ToCreature() : nullptr;
}
// if (AI_VALUE(GuidPosition, "rpg target") != bot->GetTarget())
// if (master)
// creature = botAI->GetCreature(master->GetTarget());
// else
// return false;
if (AI_VALUE2(uint32, "free money for", (uint32)NeedMoneyFor::spells) < cost)
{
out << " - too expensive";
return;
}
if (!creature || !creature->IsTrainer())
return false;
Trainer::Trainer* trainer = sObjectMgr->GetTrainer(creature->GetEntry());
if (!trainer || !trainer->IsTrainerValidForPlayer(bot))
return false;
std::vector<Trainer::Spell> trainer_spells = trainer->GetSpells();
if (trainer_spells.empty())
{
botAI->TellError("No spells can be learned from this trainer");
return false;
bot->ModifyMoney(-static_cast<int32>(cost));
}
uint32 spell = chat->parseSpell(text);
SpellIds spells;
if (spell)
spells.insert(spell);
if (text.find("learn") != std::string::npos || sRandomPlayerbotMgr.IsRandomBot(bot) ||
(sPlayerbotAIConfig.autoTrainSpells != "no" &&
(trainer->GetTrainerType() != Trainer::Type::Tradeskill ||
!botAI->HasActivePlayerMaster()))) // Todo rewrite to only exclude start primary profession skills and make
// config dependent.
Iterate(creature, &TrainerAction::Learn, spells);
if (spellInfo->HasEffect(SPELL_EFFECT_LEARN_SPELL))
bot->CastSpell(bot, spellInfo->Id, true);
else
Iterate(creature, nullptr, spells);
bot->learnSpell(spellInfo->Id, false);
return true;
out << " - learned";
}
void TrainerAction::TellHeader(Creature* creature)
@@ -245,7 +258,8 @@ bool MaintenanceAction::Execute(Event /*event*/)
if (sPlayerbotAIConfig.altMaintenanceKeyring)
factory.InitKeyring();
if (sPlayerbotAIConfig.altMaintenanceGemsEnchants && bot->GetLevel() >= sPlayerbotAIConfig.minEnchantingBotLevel)
if (sPlayerbotAIConfig.altMaintenanceGemsEnchants &&
bot->GetLevel() >= sPlayerbotAIConfig.minEnchantingBotLevel)
factory.ApplyEnchantAndGemsNew();
}

View File

@@ -8,7 +8,6 @@
#include "Action.h"
#include "ChatHelper.h"
#include "Trainer.h"
class Creature;
class PlayerbotAI;
@@ -21,11 +20,14 @@ public:
TrainerAction(PlayerbotAI* botAI) : Action(botAI, "trainer") {}
bool Execute(Event event) override;
bool isUseful() override;
bool isPossible() override;
Unit* GetTarget() override;
private:
typedef void (TrainerAction::*TrainerSpellAction)(uint32, const Trainer::Spell, std::ostringstream& msg);
void Iterate(Creature* creature, TrainerSpellAction action, SpellIds& spells);
void Learn(uint32 cost, const Trainer::Spell tSpell, std::ostringstream& msg);
Creature* GetCreatureTarget();
void Iterate(Creature* creature, bool learnSpells, uint32 spellId);
void Learn(SpellInfo const* spellInfo, uint32 cost, std::ostringstream& out);
void TellHeader(Creature* creature);
void TellFooter(uint32 totalCost);
};

View File

@@ -0,0 +1,20 @@
/*
* 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 "AggressiveStrategy.h"
#include "Playerbots.h"
void AggressiveStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
{
triggers.push_back(
new TriggerNode(
"no target",
{
NextAction("aggressive target", 4.0f)
}
)
);
}

View File

@@ -0,0 +1,22 @@
/*
* 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.
*/
#ifndef _PLAYERBOT_AGGRESSIVESTRATEGY_H
#define _PLAYERBOT_AGGRESSIVESTRATEGY_H
#include "NonCombatStrategy.h"
class PlayerbotAI;
class AggressiveStrategy : public NonCombatStrategy
{
public:
AggressiveStrategy(PlayerbotAI* botAI) : NonCombatStrategy(botAI) {}
std::string const getName() override { return "aggressive"; }
void InitTriggers(std::vector<TriggerNode*>& triggers) override;
};
#endif

View File

@@ -6,6 +6,7 @@
#ifndef _PLAYERBOT_STRATEGYCONTEXT_H
#define _PLAYERBOT_STRATEGYCONTEXT_H
#include "AggressiveStrategy.h"
#include "AttackEnemyPlayersStrategy.h"
#include "BattlegroundStrategy.h"
#include "CastTimeStrategy.h"
@@ -61,6 +62,7 @@ public:
creators["gather"] = &StrategyContext::gather;
creators["emote"] = &StrategyContext::emote;
creators["passive"] = &StrategyContext::passive;
creators["aggressive"] = &StrategyContext::aggressive;
creators["save mana"] = &StrategyContext::auto_save_mana;
creators["food"] = &StrategyContext::food;
creators["chat"] = &StrategyContext::chat;
@@ -144,6 +146,7 @@ private:
static Strategy* gather(PlayerbotAI* botAI) { return new GatherStrategy(botAI); }
static Strategy* emote(PlayerbotAI* botAI) { return new EmoteStrategy(botAI); }
static Strategy* passive(PlayerbotAI* botAI) { return new PassiveStrategy(botAI); }
static Strategy* aggressive(PlayerbotAI* botAI) { return new AggressiveStrategy(botAI); }
// static Strategy* conserve_mana(PlayerbotAI* botAI) { return new ConserveManaStrategy(botAI); }
static Strategy* auto_save_mana(PlayerbotAI* botAI) { return new HealerAutoSaveManaStrategy(botAI); }
static Strategy* food(PlayerbotAI* botAI) { return new UseFoodStrategy(botAI); }

View File

@@ -163,52 +163,19 @@ bool RpgRepairTrigger::IsActive()
return false;
}
bool RpgTrainTrigger::IsTrainerOf(CreatureTemplate const* cInfo, Player* pPlayer)
{
Trainer::Trainer* trainer = sObjectMgr->GetTrainer(cInfo->Entry);
if (trainer->GetTrainerType() == Trainer::Type::Mount && trainer->GetTrainerRequirement() != pPlayer->getRace())
{
if (FactionTemplateEntry const* faction_template = sFactionTemplateStore.LookupEntry(cInfo->faction))
if (pPlayer->GetReputationRank(faction_template->faction) == REP_EXALTED)
return true;
return false;
}
return trainer->IsTrainerValidForPlayer(pPlayer);
}
bool RpgTrainTrigger::IsActive()
{
GuidPosition guidP(getGuidP());
if (!guidP.HasNpcFlag(UNIT_NPC_FLAG_TRAINER))
GuidPosition gp = getGuidP();
if (!gp)
return false;
CreatureTemplate const* cInfo = guidP.GetCreatureTemplate();
if (!IsTrainerOf(cInfo, bot))
if (!gp.HasNpcFlag(UNIT_NPC_FLAG_TRAINER))
return false;
Trainer::Trainer* trainer = sObjectMgr->GetTrainer(cInfo->Entry);
FactionTemplateEntry const* factionTemplate = sFactionTemplateStore.LookupEntry(cInfo->faction);
float fDiscountMod = bot->GetReputationPriceDiscount(factionTemplate);
if (!AI_VALUE(bool, "can train"))
return false;
for (auto& spell : trainer->GetSpells())
{
if (!trainer->CanTeachSpell(bot, trainer->GetSpell(spell.SpellId)))
continue;
uint32 cost = uint32(floor(spell.MoneyCost * fDiscountMod));
if (cost > AI_VALUE2(uint32, "free money for", (uint32)NeedMoneyFor::spells))
continue;
return true;
}
return false;
return true;
}
bool RpgHealTrigger::IsActive()

View File

@@ -134,8 +134,6 @@ class RpgTrainTrigger : public RpgTrigger
public:
RpgTrainTrigger(PlayerbotAI* botAI, std::string const name = "rpg train") : RpgTrigger(botAI, name) {}
static bool IsTrainerOf(CreatureTemplate const* cInfo, Player* pPlayer);
bool IsActive() override;
};

View File

@@ -0,0 +1,66 @@
/*
* 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 "AggressiveTargetValue.h"
#include "Playerbots.h"
#include "ServerFacade.h"
#include "SharedDefines.h"
Unit* AggressiveTargetValue::Calculate()
{
Player* master = GetMaster();
if (master && (master == bot || master->GetMapId() != bot->GetMapId() || master->IsBeingTeleported() ||
!GET_PLAYERBOT_AI(master)))
master = nullptr;
GuidVector targets = AI_VALUE(GuidVector, "possible targets");
if (targets.empty())
return nullptr;
float aggroRange = sPlayerbotAIConfig.aggroDistance;
float distance = 0;
Unit* result = nullptr;
for (ObjectGuid const guid : targets)
{
Unit* unit = botAI->GetUnit(guid);
if (!unit || !unit->IsAlive())
continue;
if (!unit->IsInWorld() || unit->IsDuringRemoveFromWorld())
continue;
if (unit->ToCreature() && !unit->ToCreature()->GetCreatureTemplate()->lootid &&
bot->GetReactionTo(unit) >= REP_NEUTRAL)
continue;
if (!bot->IsHostileTo(unit) && unit->GetNpcFlags() != UNIT_NPC_FLAG_NONE)
continue;
if (abs(bot->GetPositionZ() - unit->GetPositionZ()) > INTERACTION_DISTANCE)
continue;
if (!bot->InBattleground() && master && botAI->HasStrategy("follow", BotState::BOT_STATE_NON_COMBAT) &&
ServerFacade::instance().GetDistance2d(master, unit) > aggroRange)
continue;
if (!bot->IsWithinLOSInMap(unit))
continue;
if (bot->GetDistance(unit) > aggroRange)
continue;
float newdistance = bot->GetDistance(unit);
if (!result || (newdistance < distance))
{
distance = newdistance;
result = unit;
}
}
return result;
}

View File

@@ -0,0 +1,22 @@
/*
* 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.
*/
#ifndef _PLAYERBOT_AGGRESSIVETARGETVALUE_H
#define _PLAYERBOT_AGGRESSIVETARGETVALUE_H
#include "TargetValue.h"
class PlayerbotAI;
class Unit;
class AggressiveTargetValue : public TargetValue
{
public:
AggressiveTargetValue(PlayerbotAI* botAI, std::string const name = "aggressive target") : TargetValue(botAI, name) {}
Unit* Calculate() override;
};
#endif

View File

@@ -9,7 +9,8 @@
uint32 MaxGearRepairCostValue::Calculate()
{
uint32 TotalCost = 0;
uint32 totalCost = 0;
for (int i = EQUIPMENT_SLOT_START; i < INVENTORY_SLOT_ITEM_END; ++i)
{
uint16 pos = ((INVENTORY_SLOT_BAG_0 << 8) | i);
@@ -43,15 +44,16 @@ uint32 MaxGearRepairCostValue::Calculate()
uint32 costs = uint32(maxDurability * dmultiplier * double(dQualitymodEntry->quality_mod));
TotalCost += costs;
totalCost += costs;
}
return TotalCost;
return totalCost;
}
uint32 RepairCostValue::Calculate()
{
uint32 TotalCost = 0;
uint32 totalCost = 0;
for (int i = EQUIPMENT_SLOT_START; i < INVENTORY_SLOT_ITEM_END; ++i)
{
uint16 pos = ((INVENTORY_SLOT_BAG_0 << 8) | i);
@@ -86,45 +88,49 @@ uint32 RepairCostValue::Calculate()
dcost->multiplier[ItemSubClassToDurabilityMultiplierId(ditemProto->Class, ditemProto->SubClass)];
uint32 costs = uint32(LostDurability * dmultiplier * double(dQualitymodEntry->quality_mod));
TotalCost += costs;
totalCost += costs;
}
return TotalCost;
return totalCost;
}
uint32 TrainCostValue::Calculate()
{
uint32 TotalCost = 0;
uint32 totalCost = 0;
std::set<uint32> spells;
std::unordered_set<uint32> spells;
if (CreatureTemplateContainer const* creatures = sObjectMgr->GetCreatureTemplates())
CreatureTemplateContainer const* ctc = sObjectMgr->GetCreatureTemplates();
for (CreatureTemplateContainer::const_iterator itr = ctc->begin(); itr != ctc->end(); ++itr)
{
for (CreatureTemplateContainer::const_iterator itr = creatures->begin(); itr != creatures->end(); ++itr)
if (!(itr->second.npcflag & UNIT_NPC_FLAG_TRAINER))
continue;
Trainer::Trainer* trainer = sObjectMgr->GetTrainer(itr->first);
if (!trainer)
continue;
if (trainer->GetTrainerType() != Trainer::Type::Class || !trainer->IsTrainerValidForPlayer(bot))
continue;
for (auto& spell : trainer->GetSpells())
{
Trainer::Trainer* trainer = sObjectMgr->GetTrainer(itr->first);
if (!trainer)
Trainer::Spell const* trainerSpell = trainer->GetSpell(spell.SpellId);
if (!trainerSpell)
continue;
if (trainer->GetTrainerType() != Trainer::Type::Class || !trainer->IsTrainerValidForPlayer(bot))
if (!trainer->CanTeachSpell(bot, trainerSpell))
continue;
for (auto& spell : trainer->GetSpells())
{
if (!trainer->CanTeachSpell(bot, trainer->GetSpell(spell.SpellId)))
continue;
if (spells.find(trainerSpell->SpellId) != spells.end())
continue;
if (spells.find(spell.SpellId) != spells.end())
continue;
TotalCost += spell.MoneyCost;
spells.insert(spell.SpellId);
}
totalCost += trainerSpell->MoneyCost;
spells.insert(trainerSpell->SpellId);
}
}
return TotalCost;
return totalCost;
}
uint32 MoneyNeededForValue::Calculate()

View File

@@ -44,6 +44,11 @@ bool CanSellValue::Calculate()
AI_VALUE2(uint32, "item count", "usage " + std::to_string(ITEM_USAGE_AH))) > 1;
}
bool CanTrainValue::Calculate()
{
return AI_VALUE2(uint32, "free money for", (uint32)NeedMoneyFor::spells) > 0;
}
bool CanFightEqualValue::Calculate() { return AI_VALUE(uint8, "durability") > 20; }
bool CanFightEliteValue::Calculate()

View File

@@ -58,6 +58,14 @@ public:
bool Calculate() override;
};
class CanTrainValue : public BoolCalculatedValue
{
public:
CanTrainValue(PlayerbotAI* botAI) : BoolCalculatedValue(botAI, "can train", 2 * 2000) {}
bool Calculate() override;
};
class CanFightEqualValue : public BoolCalculatedValue
{
public:

View File

@@ -7,6 +7,7 @@
#define _PLAYERBOT_VALUECONTEXT_H
#include "ActiveSpellValue.h"
#include "AggressiveTargetValue.h"
#include "AlwaysLootListValue.h"
#include "AoeHealValues.h"
#include "AoeValues.h"
@@ -51,6 +52,7 @@
#include "LineTargetValue.h"
#include "LogLevelValue.h"
#include "LootStrategyValue.h"
#include "LootValues.h"
#include "MaintenanceValues.h"
#include "ManaSaveLevelValue.h"
#include "NearestAdsValue.h"
@@ -143,6 +145,7 @@ public:
creators["pet target"] = &ValueContext::pet_target;
creators["old target"] = &ValueContext::old_target;
creators["grind target"] = &ValueContext::grind_target;
creators["aggressive target"] = &ValueContext::aggressive_target;
creators["rti target"] = &ValueContext::rti_target;
creators["rti cc target"] = &ValueContext::rti_cc_target;
creators["duel target"] = &ValueContext::duel_target;
@@ -272,6 +275,7 @@ public:
creators["can repair"] = &ValueContext::can_repair;
creators["should sell"] = &ValueContext::should_sell;
creators["can sell"] = &ValueContext::can_sell;
creators["can train"] = &ValueContext::can_train;
creators["can fight equal"] = &ValueContext::can_fight_equal;
creators["can fight elite"] = &ValueContext::can_fight_elite;
creators["can fight boss"] = &ValueContext::can_fight_boss;
@@ -457,6 +461,7 @@ private:
static UntypedValue* current_cc_target(PlayerbotAI* botAI) { return new CurrentCcTargetValue(botAI); }
static UntypedValue* pet_target(PlayerbotAI* botAI) { return new PetTargetValue(botAI); }
static UntypedValue* grind_target(PlayerbotAI* botAI) { return new GrindTargetValue(botAI); }
static UntypedValue* aggressive_target(PlayerbotAI* botAI) { return new AggressiveTargetValue(botAI); }
static UntypedValue* rti_target(PlayerbotAI* botAI) { return new RtiTargetValue(botAI); }
static UntypedValue* rti_cc_target(PlayerbotAI* botAI) { return new RtiCcTargetValue(botAI); }
static UntypedValue* duel_target(PlayerbotAI* botAI) { return new DuelTargetValue(botAI); }
@@ -519,6 +524,7 @@ private:
static UntypedValue* can_repair(PlayerbotAI* botAI) { return new CanRepairValue(botAI); }
static UntypedValue* should_sell(PlayerbotAI* botAI) { return new ShouldSellValue(botAI); }
static UntypedValue* can_sell(PlayerbotAI* botAI) { return new CanSellValue(botAI); }
static UntypedValue* can_train(PlayerbotAI* botAI) { return new CanTrainValue(botAI); }
static UntypedValue* can_fight_equal(PlayerbotAI* botAI) { return new CanFightEqualValue(botAI); }
static UntypedValue* can_fight_elite(PlayerbotAI* botAI) { return new CanFightEliteValue(botAI); }
static UntypedValue* can_fight_boss(PlayerbotAI* botAI) { return new CanFightBossValue(botAI); }

View File

@@ -201,8 +201,7 @@ bool IckAndKrickAction::ExplosiveBarrage(bool explosiveBarrage, Unit* boss)
continue;
// Check if position is within maximum allowed distance from boss
if (boss && sqrt(pow(potentialPos.GetPositionX() - boss->GetPositionX(), 2) +
pow(potentialPos.GetPositionY() - boss->GetPositionY(), 2)) > MAX_BOSS_DISTANCE)
if (boss && boss->GetDistance2d(potentialPos.GetPositionX(), potentialPos.GetPositionY()) > MAX_BOSS_DISTANCE)
continue;
// Score this position based on:
@@ -215,8 +214,7 @@ bool IckAndKrickAction::ExplosiveBarrage(bool explosiveBarrage, Unit* boss)
float minOrbDist = std::numeric_limits<float>::max();
for (Unit* orb : orbs)
{
float orbDist = sqrt(pow(potentialPos.GetPositionX() - orb->GetPositionX(), 2) +
pow(potentialPos.GetPositionY() - orb->GetPositionY(), 2));
float orbDist = orb->GetDistance2d(potentialPos.GetPositionX(), potentialPos.GetPositionY());
minOrbDist = std::min(minOrbDist, orbDist);
}
score += minOrbDist * 2.0f; // Weight orb distance more heavily
@@ -232,8 +230,7 @@ bool IckAndKrickAction::ExplosiveBarrage(bool explosiveBarrage, Unit* boss)
// Factor in proximity to boss (closer is better, as long as we're safe from orbs)
if (boss)
{
float bossDist = sqrt(pow(potentialPos.GetPositionX() - boss->GetPositionX(), 2) +
pow(potentialPos.GetPositionY() - boss->GetPositionY(), 2));
float bossDist = boss->GetDistance2d(potentialPos.GetPositionX(), potentialPos.GetPositionY());
// Add points for being closer to boss (inverse relationship)
// but only if we're safely away from orbs
if (minOrbDist > SAFE_DISTANCE)

View File

@@ -128,7 +128,7 @@ namespace GruulsLairHelpers
Unit* krosh = botAI->GetAiObjectContext()->GetValue<Unit*>("find target", "krosh firehand")->Get();
if (krosh && krosh->IsAlive())
{
float dist = sqrt(pow(pos.GetPositionX() - krosh->GetPositionX(), 2) + pow(pos.GetPositionY() - krosh->GetPositionY(), 2));
float dist = krosh->GetDistance2d(pos.GetPositionX(), pos.GetPositionY());
if (dist < KROSH_SAFE_DISTANCE)
isSafe = false;
}
@@ -136,7 +136,7 @@ namespace GruulsLairHelpers
Unit* maulgar = botAI->GetAiObjectContext()->GetValue<Unit*>("find target", "high king maulgar")->Get();
if (botAI->IsRanged(bot) && maulgar && maulgar->IsAlive())
{
float dist = sqrt(pow(pos.GetPositionX() - maulgar->GetPositionX(), 2) + pow(pos.GetPositionY() - maulgar->GetPositionY(), 2));
float dist = maulgar->GetDistance2d(pos.GetPositionX(), pos.GetPositionY());
if (dist < MAULGAR_SAFE_DISTANCE)
isSafe = false;
}
@@ -182,7 +182,7 @@ namespace GruulsLairHelpers
if (IsPositionSafe(botAI, bot, candidatePos))
{
float movementDistance = sqrt(pow(destX - bot->GetPositionX(), 2) + pow(destY - bot->GetPositionY(), 2));
float movementDistance = bot->GetDistance2d(destX, destY);
if (movementDistance < bestScore)
{
bestScore = movementDistance;

View File

@@ -1800,8 +1800,7 @@ bool IccRotfaceTankPositionAction::HandleBigOozePositioning(Unit*)
Unit* puddle = botAI->GetUnit(puddleGuid);
if (puddle && botAI->GetAura("Ooze Flood", puddle))
{
float puddleDistance = std::sqrt(std::pow(newX - puddle->GetPositionX(), 2) +
std::pow(newY - puddle->GetPositionY(), 2));
float puddleDistance = puddle->GetDistance2d(newX, newY);
if (puddleDistance < puddleSafeDistance)
{
isSafeFromPuddles = false;
@@ -1921,8 +1920,7 @@ bool IccRotfaceGroupPositionAction::MoveAwayFromPuddle(Unit* boss, Unit* puddle,
float moveZ = bot->GetPositionZ();
// Check distances and line of sight
float newPuddleDistance =
sqrt(pow(moveX - puddle->GetPositionX(), 2) + pow(moveY - puddle->GetPositionY(), 2));
float newPuddleDistance = puddle->GetDistance2d(moveX, moveY);
float newCenterDistance = sqrt(pow(moveX - ICC_ROTFACE_CENTER_POSITION.GetPositionX(), 2) +
pow(moveY - ICC_ROTFACE_CENTER_POSITION.GetPositionY(), 2));
@@ -2125,8 +2123,7 @@ bool IccRotfaceGroupPositionAction::FindAndMoveFromClosestMember(Unit* boss, Uni
// Ensure the target position is at least 30 yards away from the puddle
if (puddle)
{
float puddleDistance = std::sqrt(std::pow(targetX - puddle->GetPositionX(), 2) +
std::pow(targetY - puddle->GetPositionY(), 2));
float puddleDistance = puddle->GetDistance2d(targetX, targetY);
if (puddleDistance < puddleSafeDistance)
{
// Adjust the target position to move further away from the puddle
@@ -2203,8 +2200,7 @@ bool IccRotfaceMoveAwayFromExplosionAction::MoveToRandomSafeLocation()
if (!puddle || !botAI->HasAura("Ooze Flood", puddle))
continue;
float puddleDistance =
std::sqrt(std::pow(moveX - puddle->GetPositionX(), 2) + std::pow(moveY - puddle->GetPositionY(), 2));
float puddleDistance = puddle->GetDistance2d(moveX, moveY);
if (puddleDistance < 30.0f)
{
// Adjust the position to move further away from the puddle
@@ -2407,7 +2403,7 @@ bool IccPutricideGrowingOozePuddleAction::IsPositionTooCloseToOtherPuddles(float
if (Aura* grow = unit->GetAura(SPELL_GROW_AURA))
safeDistance += (grow->GetStackAmount() * STACK_MULTIPLIER);
float dist = sqrt(pow(x - unit->GetPositionX(), 2) + pow(y - unit->GetPositionY(), 2));
float dist = unit->GetDistance2d(x, y);
if (dist < safeDistance)
return true;
}
@@ -2650,7 +2646,7 @@ bool IccPutricideGasCloudAction::HandleGaseousBloatMovement(Unit* gasCloud)
float minGasBombDist = FLT_MAX;
for (Unit* bomb : gasBombs)
{
float bombDist = sqrt(pow(testX - bomb->GetPositionX(), 2) + pow(testY - bomb->GetPositionY(), 2));
float bombDist = bomb->GetDistance2d(testX, testY);
if (bombDist < minGasBombDist)
minGasBombDist = bombDist;
}
@@ -2717,8 +2713,7 @@ bool IccPutricideGasCloudAction::HandleGaseousBloatMovement(Unit* gasCloud)
float minEmergencyGasBombDist = FLT_MAX;
for (Unit* bomb : gasBombs)
{
float bombDist = sqrt(pow(emergencyPos.GetPositionX() - bomb->GetPositionX(), 2) +
pow(emergencyPos.GetPositionY() - bomb->GetPositionY(), 2));
float bombDist = bomb->GetDistance2d(emergencyPos.GetPositionX(), emergencyPos.GetPositionY());
if (bombDist < minEmergencyGasBombDist)
minEmergencyGasBombDist = bombDist;
}
@@ -4487,8 +4482,7 @@ bool IccBqlGroupPositionAction::HandleGroupPosition(Unit* boss, Aura* frenzyAura
// Maintain minimum distance from center position (if too close to center, move out)
float centerX = ICC_BQL_CENTER_POSITION.GetPositionX();
float centerY = ICC_BQL_CENTER_POSITION.GetPositionY();
float centerDist =
std::sqrt(std::pow(bot->GetPositionX() - centerX, 2) + std::pow(bot->GetPositionY() - centerY, 2));
float centerDist = bot->GetDistance2d(centerX, centerY);
if (centerDist < MIN_CENTER_DISTANCE && !((boss->GetPositionZ() - bot->GetPositionZ()) > 5.0f))
{
float dx = bot->GetPositionX() - centerX;
@@ -6904,7 +6898,7 @@ bool IccLichKingShadowTrapAction::Execute(Event /*event*/)
Unit* trap = botAI->GetUnit(trapGuid);
if (!trap)
continue;
float distToTrap = sqrt(pow(testX - trap->GetPositionX(), 2) + pow(testY - trap->GetPositionY(), 2));
float distToTrap = trap->GetDistance2d(testX, testY);
if (distToTrap < SAFE_DISTANCE)
{
isSafe = false;

View File

@@ -132,7 +132,7 @@ namespace MagtheridonHelpers
}
for (Unit* hazard : debrisHazards)
{
float dist = std::sqrt(std::pow(x - hazard->GetPositionX(), 2) + std::pow(y - hazard->GetPositionY(), 2));
float dist = hazard->GetDistance2d(x, y);
if (dist < 9.0f)
return false;
}
@@ -145,7 +145,7 @@ namespace MagtheridonHelpers
if (!go || go->GetEntry() != GO_BLAZE)
continue;
float dist = std::sqrt(std::pow(x - go->GetPositionX(), 2) + std::pow(y - go->GetPositionY(), 2));
float dist = go->GetDistance2d(x, y);
if (dist < 5.0f)
return false;
}

View File

@@ -262,10 +262,10 @@ bool NewRpgBaseAction::CanInteractWithQuestGiver(Object* questGiver)
// that removes the distance check and keeps all other checks
switch (questGiver->GetTypeId())
{
case TYPEID_UNIT:
case TYPEID_UNIT: // Player::GetNPCIfCanInteractWith
{
ObjectGuid guid = questGiver->GetGUID();
uint32 npcflagmask = UNIT_NPC_FLAG_QUESTGIVER;
// unit checks
if (!guid)
return false;
@@ -292,7 +292,7 @@ bool NewRpgBaseAction::CanInteractWithQuestGiver(Object* questGiver)
return false;
// appropriate npc type
if (npcflagmask && !creature->HasNpcFlag(NPCFlags(npcflagmask)))
if (!creature->HasNpcFlag(UNIT_NPC_FLAG_QUESTGIVER))
return false;
// not allow interaction under control, but allow with own pets
@@ -303,35 +303,24 @@ bool NewRpgBaseAction::CanInteractWithQuestGiver(Object* questGiver)
if (creature->GetReactionTo(bot) <= REP_UNFRIENDLY)
return false;
Trainer::Trainer* trainer = sObjectMgr->GetTrainer(creature->GetEntry());
// 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) &&
trainer->GetTrainerType() == Trainer::Type::Class &&
!trainer->IsTrainerValidForPlayer(bot))
return false;
return true;
}
case TYPEID_GAMEOBJECT:
case TYPEID_GAMEOBJECT: // Player::GetGameObjectIfCanInteractWith
{
ObjectGuid guid = questGiver->GetGUID();
GameobjectTypes type = GAMEOBJECT_TYPE_QUESTGIVER;
if (GameObject* go = bot->GetMap()->GetGameObject(guid))
{
if (go->GetGoType() == type)
if (go->GetGoType() == GAMEOBJECT_TYPE_QUESTGIVER)
{
// Players cannot interact with gameobjects that use the "Point" icon
if (go->GetGOInfo()->IconName == "Point")
{
return false;
}
return true;
}
}
return false;
}
// unused for now

View File

@@ -4739,38 +4739,13 @@ void PlayerbotFactory::InitAttunementQuests()
uint32 currentXP = bot->GetUInt32Value(PLAYER_XP);
// List of attunement quest IDs
std::list<uint32> attunementQuestsTBC = {
// Caverns of Time - Part 1
10279, // To The Master's Lair
10277, // The Caverns of Time
// Caverns of Time - Part 2 (Escape from Durnholde Keep)
10282, // Old Hillsbrad
10283, // Taretha's Diversion
10284, // Escape from Durnholde
10285, // Return to Andormu
// Caverns of Time - Part 2 (The Black Morass)
10296, // The Black Morass
10297, // The Opening of the Dark Portal
10298, // Hero of the Brood
// Magister's Terrace Attunement
11481, // Crisis at the Sunwell
11482, // Duty Calls
11488, // Magisters' Terrace
11490, // The Scryer's Scryer
11492 // Hard to Kill
};
// Complete all level-appropriate attunement quests for the bot
if (level >= 60)
{
std::list<uint32> questsToComplete;
// Check each quest status before adding to the completion list
for (uint32 questId : attunementQuestsTBC)
for (uint32 questId : sPlayerbotAIConfig.attunementQuests)
{
QuestStatus questStatus = bot->GetQuestStatus(questId);

View File

@@ -45,18 +45,18 @@ void StatsCollector::CollectItemStats(ItemTemplate const* proto)
switch (proto->Spells[j].SpellTrigger)
{
case ITEM_SPELLTRIGGER_ON_USE:
CollectSpellStats(proto->Spells[j].SpellId, 1.0f, proto->Spells[j].SpellCooldown);
CollectSpellStats(proto->Spells[j].SpellId, 1.0f, Milliseconds(proto->Spells[j].SpellCooldown));
break;
case ITEM_SPELLTRIGGER_ON_EQUIP:
CollectSpellStats(proto->Spells[j].SpellId, 1.0f, 0);
CollectSpellStats(proto->Spells[j].SpellId, 1.0f, Milliseconds(0));
break;
case ITEM_SPELLTRIGGER_CHANCE_ON_HIT:
if (type_ & CollectorType::MELEE)
{
if (proto->Spells[j].SpellPPMRate > 0.01f)
CollectSpellStats(proto->Spells[j].SpellId, 1.0f, 60000 / proto->Spells[j].SpellPPMRate);
CollectSpellStats(proto->Spells[j].SpellId, 1.0f, Milliseconds(static_cast<int>(60000 / proto->Spells[j].SpellPPMRate)));
else
CollectSpellStats(proto->Spells[j].SpellId, 1.0f, 60000 / 1.8f); // Default PPM = 1.8
CollectSpellStats(proto->Spells[j].SpellId, 1.0f, Milliseconds(static_cast<int>(60000 / 1.8f))); // Default PPM = 1.8
}
break;
default:
@@ -71,7 +71,7 @@ void StatsCollector::CollectItemStats(ItemTemplate const* proto)
}
}
void StatsCollector::CollectSpellStats(uint32 spellId, float multiplier, int32 spellCooldown)
void StatsCollector::CollectSpellStats(uint32 spellId, float multiplier, Milliseconds spellCooldown)
{
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId);
@@ -81,21 +81,21 @@ void StatsCollector::CollectSpellStats(uint32 spellId, float multiplier, int32 s
if (SpecialSpellFilter(spellId))
return;
const SpellProcEventEntry* eventEntry = sSpellMgr->GetSpellProcEvent(spellInfo->Id);
const SpellProcEntry* eventEntry = sSpellMgr->GetSpellProcEntry(spellInfo->Id);
uint32 triggerCooldown = eventEntry ? eventEntry->cooldown : 0;
Milliseconds triggerCooldown = eventEntry ? eventEntry->Cooldown : 0ms;
bool canNextTrigger = true;
uint32 procFlags;
uint32 procChance;
if (eventEntry && eventEntry->procFlags)
procFlags = eventEntry->procFlags;
if (eventEntry && eventEntry->ProcFlags)
procFlags = eventEntry->ProcFlags;
else
procFlags = spellInfo->ProcFlags;
if (eventEntry && eventEntry->customChance)
procChance = eventEntry->customChance;
if (eventEntry && eventEntry->Chance)
procChance = eventEntry->Chance;
else
procChance = spellInfo->ProcChance;
bool lowChance = procChance <= 5;
@@ -142,11 +142,11 @@ void StatsCollector::CollectSpellStats(uint32 spellId, float multiplier, int32 s
break;
float coverage;
if (spellCooldown <= 2000 || spellInfo->GetDuration() == -1)
if (spellCooldown.count() <= 2000 || spellInfo->GetDuration() == -1)
coverage = 1.0f;
else
coverage =
std::min(1.0f, (float)spellInfo->GetDuration() / (spellInfo->GetDuration() + spellCooldown));
std::min(1.0f, (float)spellInfo->GetDuration() / (spellInfo->GetDuration() + spellCooldown.count()));
multiplier *= coverage;
HandleApplyAura(effectInfo, multiplier, canNextTrigger, triggerCooldown);
@@ -155,9 +155,9 @@ void StatsCollector::CollectSpellStats(uint32 spellId, float multiplier, int32 s
case SPELL_EFFECT_HEAL:
{
/// @todo Handle spell without cooldown
if (!spellCooldown)
if (!spellCooldown.count())
break;
float normalizedCd = std::max((float)spellCooldown / 1000, 5.0f);
float normalizedCd = std::max((float)spellCooldown.count() / 1000, 5.0f);
int32 val = AverageValue(effectInfo);
float transfer_multiplier = 1;
stats[STATS_TYPE_HEAL_POWER] += (float)val / normalizedCd * multiplier * transfer_multiplier;
@@ -166,11 +166,11 @@ void StatsCollector::CollectSpellStats(uint32 spellId, float multiplier, int32 s
case SPELL_EFFECT_ENERGIZE:
{
/// @todo Handle spell without cooldown
if (!spellCooldown)
if (!spellCooldown.count())
break;
if (effectInfo.MiscValue != POWER_MANA)
break;
float normalizedCd = std::max((float)spellCooldown / 1000, 5.0f);
float normalizedCd = std::max((float)spellCooldown.count() / 1000, 5.0f);
int32 val = AverageValue(effectInfo);
float transfer_multiplier = 0.2;
stats[STATS_TYPE_MANA_REGENERATION] += (float)val / normalizedCd * multiplier * transfer_multiplier;
@@ -179,9 +179,9 @@ void StatsCollector::CollectSpellStats(uint32 spellId, float multiplier, int32 s
case SPELL_EFFECT_SCHOOL_DAMAGE:
{
/// @todo Handle spell without cooldown
if (!spellCooldown)
if (!spellCooldown.count())
break;
float normalizedCd = std::max((float)spellCooldown / 1000, 5.0f);
float normalizedCd = std::max((float)spellCooldown.count() / 1000, 5.0f);
int32 val = AverageValue(effectInfo);
if (type_ & (CollectorType::MELEE | CollectorType::RANGED))
{
@@ -352,12 +352,12 @@ bool StatsCollector::SpecialEnchantFilter(uint32 enchantSpellId)
bool StatsCollector::CanBeTriggeredByType(SpellInfo const* spellInfo, uint32 procFlags, bool strict)
{
const SpellProcEventEntry* eventEntry = sSpellMgr->GetSpellProcEvent(spellInfo->Id);
const SpellProcEntry* eventEntry = sSpellMgr->GetSpellProcEntry(spellInfo->Id);
uint32 spellFamilyName = 0;
if (eventEntry)
{
spellFamilyName = eventEntry->spellFamilyName;
flag96 spellFamilyMask = eventEntry->spellFamilyMask;
spellFamilyName = eventEntry->SpellFamilyName;
flag96 spellFamilyMask = eventEntry->SpellFamilyMask;
if (spellFamilyName != 0)
{
if (!CheckSpellValidation(spellFamilyName, spellFamilyMask, strict))
@@ -548,7 +548,7 @@ void StatsCollector::CollectByItemStatType(uint32 itemStatType, int32 val)
}
void StatsCollector::HandleApplyAura(const SpellEffectInfo& effectInfo, float multiplier, bool canNextTrigger,
uint32 triggerCooldown)
Milliseconds triggerCooldown)
{
if (effectInfo.Effect != SPELL_EFFECT_APPLY_AURA)
return;

View File

@@ -65,7 +65,7 @@ public:
StatsCollector(StatsCollector& stats) = default;
void Reset();
void CollectItemStats(ItemTemplate const* proto);
void CollectSpellStats(uint32 spellId, float multiplier = 1.0f, int32 spellCooldown = -1);
void CollectSpellStats(uint32 spellId, float multiplier = 1.0f, Milliseconds spellCooldown = -1ms);
void CollectEnchantStats(SpellItemEnchantmentEntry const* enchant, uint32 default_enchant_amount = 0);
bool CanBeTriggeredByType(SpellInfo const* spellInfo, uint32 procFlags, bool strict = true);
bool CheckSpellValidation(uint32 spellFamilyName, flag96 spelFalimyFlags, bool strict = true);
@@ -79,7 +79,7 @@ private:
bool SpecialEnchantFilter(uint32 enchantSpellId);
void HandleApplyAura(const SpellEffectInfo& effectInfo, float multiplier, bool canNextTrigger,
uint32 triggerCooldown);
Milliseconds triggerCooldown);
float AverageValue(const SpellEffectInfo& effectInfo);
private:

View File

@@ -228,13 +228,13 @@ WorldPosition WorldPosition::offset(WorldPosition* center)
float WorldPosition::size()
{
return sqrt(pow(GetPositionX(), 2.0) + pow(GetPositionY(), 2.0) + pow(GetPositionZ(), 2.0));
return GetExactDist(0.0f, 0.0f, 0.0f);
}
float WorldPosition::distance(WorldPosition* center)
{
if (GetMapId() == center->GetMapId())
return relPoint(center).size();
return GetExactDist(center->GetPositionX(), center->GetPositionY(), center->GetPositionZ());
// this -> mapTransfer | mapTransfer -> center
return TravelMgr::instance().mapTransDistance(*this, *center);
@@ -243,7 +243,7 @@ float WorldPosition::distance(WorldPosition* center)
float WorldPosition::fDist(WorldPosition* center)
{
if (GetMapId() == center->GetMapId())
return sqrt(sqDistance2d(center));
return GetExactDist2d(center->GetPositionX(), center->GetPositionY());
// this -> mapTransfer | mapTransfer -> center
return TravelMgr::instance().fastMapTransDistance(*this, *center);

View File

@@ -179,8 +179,7 @@ public:
// Quick square distance in 2d plane.
float sqDistance2d(WorldPosition center)
{
return (GetPositionX() - center.GetPositionX()) * (GetPositionX() - center.GetPositionX()) +
(GetPositionY() - center.GetPositionY()) * (GetPositionY() - center.GetPositionY());
return GetExactDist2dSq(center.GetPositionX(), center.GetPositionY());
}
// Quick square distance calculation without map check. Used for getting the minimum distant points.
@@ -193,8 +192,7 @@ public:
float sqDistance2d(WorldPosition* center)
{
return (GetPositionX() - center->GetPositionX()) * (GetPositionX() - center->GetPositionX()) +
(GetPositionY() - center->GetPositionY()) * (GetPositionY() - center->GetPositionY());
return GetExactDist2dSq(center->GetPositionX(), center->GetPositionY());
}
float sqDistance(WorldPosition* center)

View File

@@ -179,6 +179,9 @@ bool PlayerbotAIConfig::Initialize()
"179490,141596,160836,160845,179516,176224,181085,176112,128308,128403,"
"165739,165738,175245,175970,176325,176327,123329,2560"),
disallowedGameObjects);
LoadSet<std::set<uint32>>(
sConfigMgr->GetOption<std::string>("AiPlayerbot.AttunementQuests", "10279,10277,10282,10283,10284,10285,10296,10297,10298,11481,11482,11488,11490,11492,10901"),
attunementQuests);
botAutologin = sConfigMgr->GetOption<bool>("AiPlayerbot.BotAutologin", false);
randomBotAutologin = sConfigMgr->GetOption<bool>("AiPlayerbot.RandomBotAutologin", true);
minRandomBots = sConfigMgr->GetOption<int32>("AiPlayerbot.MinRandomBots", 500);
@@ -620,7 +623,7 @@ bool PlayerbotAIConfig::Initialize()
syncQuestWithPlayer = sConfigMgr->GetOption<bool>("AiPlayerbot.SyncQuestWithPlayer", true);
syncQuestForPlayer = sConfigMgr->GetOption<bool>("AiPlayerbot.SyncQuestForPlayer", false);
dropObsoleteQuests = sConfigMgr->GetOption<bool>("AiPlayerbot.DropObsoleteQuests", true);
autoTrainSpells = sConfigMgr->GetOption<std::string>("AiPlayerbot.AutoTrainSpells", "yes");
allowLearnTrainerSpells = sConfigMgr->GetOption<bool>("AiPlayerbot.AllowLearnTrainerSpells", true);
autoPickTalents = sConfigMgr->GetOption<bool>("AiPlayerbot.AutoPickTalents", true);
autoUpgradeEquip = sConfigMgr->GetOption<bool>("AiPlayerbot.AutoUpgradeEquip", false);
hunterWolfPet = sConfigMgr->GetOption<int32>("AiPlayerbot.HunterWolfPet", 0);

View File

@@ -98,6 +98,7 @@ public:
std::set<uint32> aoeAvoidSpellWhitelist;
bool tellWhenAvoidAoe;
std::set<uint32> disallowedGameObjects;
std::set<uint32> attunementQuests;
uint32 openGoSpell;
bool randomBotAutologin;
@@ -352,7 +353,7 @@ public:
bool syncQuestWithPlayer;
bool syncQuestForPlayer;
bool dropObsoleteQuests;
std::string autoTrainSpells;
bool allowLearnTrainerSpells;
bool autoPickTalents;
bool autoUpgradeEquip;
int32 hunterWolfPet;