Merge branch 'liyunfan1223:master' into master

This commit is contained in:
fuzzdeveloper
2024-07-05 00:06:24 +10:00
committed by GitHub
25 changed files with 215 additions and 72 deletions

View File

@@ -6,6 +6,8 @@ Welcome to the Playerbots Module for AzerothCore, a work in progress project bas
If you encounter any errors or experience crashes, we kindly request that you report them as GitHub issues. Your valuable feedback will help us improve and enhance this project collaboratively. If you encounter any errors or experience crashes, we kindly request that you report them as GitHub issues. Your valuable feedback will help us improve and enhance this project collaboratively.
You can also get more information in our [discord](https://discord.gg/NQm5QShwf9).
## Installation ## Installation
Please note that this module requires specific custom changes to AzerothCore. To ensure compatibility, you must compile it with a custom branch from my fork, which can be found here: [liyunfan1223/azerothcore-wotlk/tree/Playerbot](https://github.com/liyunfan1223/azerothcore-wotlk/tree/Playerbot). Please note that this module requires specific custom changes to AzerothCore. To ensure compatibility, you must compile it with a custom branch from my fork, which can be found here: [liyunfan1223/azerothcore-wotlk/tree/Playerbot](https://github.com/liyunfan1223/azerothcore-wotlk/tree/Playerbot).
@@ -48,7 +50,9 @@ It's essential to note that there is still a significant amount of work to be do
## Addon ## Addon
For enhanced control over the bots and to simplify command usage, you can also make use of our addon: [Unbot Addon](https://github.com/liyunfan1223/unbot-addon). Currently, this addon offers better support only for Simplified Chinese client. For enhanced control over the bots and to simplify command usage, you can also make use of our addon:
- Chinese version: [Unbot Addon (zh)](https://github.com/liyunfan1223/unbot-addon).
- English version (maintained by @Revision): [Unbot Addon (en)](https://github.com/noisiver/unbot-addon/tree/english).
## Frequently Asked Questions ## Frequently Asked Questions

View File

@@ -97,6 +97,10 @@ AiPlayerbot.SummonWhenGroup = 1
AiPlayerbot.RandomBotShowHelmet = 1 AiPlayerbot.RandomBotShowHelmet = 1
AiPlayerbot.RandomBotShowCloak = 1 AiPlayerbot.RandomBotShowCloak = 1
# Fix the level of random bot (won't level up by grinding)
# Default: 0 (disable)
AiPlayerbot.RandomBotFixedLevel = 0
# Disable random levels for randombots # Disable random levels for randombots
# Every bots started on the specified level and level up by killing mobs. # Every bots started on the specified level and level up by killing mobs.
AiPlayerbot.DisableRandomLevels = 0 AiPlayerbot.DisableRandomLevels = 0
@@ -125,7 +129,7 @@ AiPlayerbot.MinEnchantingBotLevel = 60
AiPlayerbot.LimitEnchantExpansion = 1 AiPlayerbot.LimitEnchantExpansion = 1
# Randombots checking players gear score level and deny the group invite if it's too low # Randombots checking players gear score level and deny the group invite if it's too low
# Default: 1 (enabled) # Default: 0 (disabled)
AiPlayerbot.GearScoreCheck = 0 AiPlayerbot.GearScoreCheck = 0
# Quest that will be completed and rewarded to all random bots # Quest that will be completed and rewarded to all random bots
@@ -338,8 +342,8 @@ AiPlayerbot.AutoSaveMana = 1
AiPlayerbot.SaveManaThreshold = 60 AiPlayerbot.SaveManaThreshold = 60
# Enable auto avoid aoe (experimental) # Enable auto avoid aoe (experimental)
# Default: 0 (disable) # Default: 1 (enable)
AiPlayerbot.AutoAvoidAoe = 0 AiPlayerbot.AutoAvoidAoe = 1
# Tell which spell is avoiding (experimental) # Tell which spell is avoiding (experimental)
# Default: 1 (enable) # Default: 1 (enable)

View File

@@ -91,7 +91,7 @@ bool PlayerbotAIConfig::Initialize()
mediumMana = sConfigMgr->GetOption<int32>("AiPlayerbot.MediumMana", 40); mediumMana = sConfigMgr->GetOption<int32>("AiPlayerbot.MediumMana", 40);
autoSaveMana = sConfigMgr->GetOption<bool>("AiPlayerbot.AutoSaveMana", true); autoSaveMana = sConfigMgr->GetOption<bool>("AiPlayerbot.AutoSaveMana", true);
saveManaThreshold = sConfigMgr->GetOption<int32>("AiPlayerbot.SaveManaThreshold", 60); saveManaThreshold = sConfigMgr->GetOption<int32>("AiPlayerbot.SaveManaThreshold", 60);
autoAvoidAoe = sConfigMgr->GetOption<bool>("AiPlayerbot.AutoAvoidAoe", false); autoAvoidAoe = sConfigMgr->GetOption<bool>("AiPlayerbot.AutoAvoidAoe", true);
tellWhenAvoidAoe = sConfigMgr->GetOption<bool>("AiPlayerbot.TellWhenAvoidAoe", true); tellWhenAvoidAoe = sConfigMgr->GetOption<bool>("AiPlayerbot.TellWhenAvoidAoe", true);
randomGearLoweringChance = sConfigMgr->GetOption<float>("AiPlayerbot.RandomGearLoweringChance", 0.15f); randomGearLoweringChance = sConfigMgr->GetOption<float>("AiPlayerbot.RandomGearLoweringChance", 0.15f);
@@ -265,6 +265,7 @@ bool PlayerbotAIConfig::Initialize()
// SPP switches // SPP switches
enableGreet = sConfigMgr->GetOption<bool>("AiPlayerbot.EnableGreet", true); enableGreet = sConfigMgr->GetOption<bool>("AiPlayerbot.EnableGreet", true);
summonWhenGroup = sConfigMgr->GetOption<bool>("AiPlayerbot.SummonWhenGroup", true); summonWhenGroup = sConfigMgr->GetOption<bool>("AiPlayerbot.SummonWhenGroup", true);
randomBotFixedLevel = sConfigMgr->GetOption<bool>("AiPlayerbot.RandomBotFixedLevel", false);
disableRandomLevels = sConfigMgr->GetOption<bool>("AiPlayerbot.DisableRandomLevels", false); disableRandomLevels = sConfigMgr->GetOption<bool>("AiPlayerbot.DisableRandomLevels", false);
randomBotRandomPassword = sConfigMgr->GetOption<bool>("AiPlayerbot.RandomBotRandomPassword", true); randomBotRandomPassword = sConfigMgr->GetOption<bool>("AiPlayerbot.RandomBotRandomPassword", true);
downgradeMaxLevelBot = sConfigMgr->GetOption<bool>("AiPlayerbot.DowngradeMaxLevelBot", true); downgradeMaxLevelBot = sConfigMgr->GetOption<bool>("AiPlayerbot.DowngradeMaxLevelBot", true);

View File

@@ -174,6 +174,7 @@ class PlayerbotAIConfig
bool summonWhenGroup; bool summonWhenGroup;
bool randomBotShowHelmet; bool randomBotShowHelmet;
bool randomBotShowCloak; bool randomBotShowCloak;
bool randomBotFixedLevel;
bool disableRandomLevels; bool disableRandomLevels;
uint32 playerbotsXPrate; uint32 playerbotsXPrate;
bool disableDeathKnightLogin; bool disableDeathKnightLogin;

View File

@@ -2502,8 +2502,7 @@ void PlayerbotFactory::InitAmmo()
case ITEM_SUBCLASS_WEAPON_CROSSBOW: case ITEM_SUBCLASS_WEAPON_CROSSBOW:
subClass = ITEM_SUBCLASS_ARROW; subClass = ITEM_SUBCLASS_ARROW;
break; break;
case ITEM_SUBCLASS_WEAPON_THROWN: default:
subClass = ITEM_SUBCLASS_THROWN;
break; break;
} }
@@ -2512,7 +2511,7 @@ void PlayerbotFactory::InitAmmo()
uint32 entry = sRandomItemMgr->GetAmmo(level, subClass); uint32 entry = sRandomItemMgr->GetAmmo(level, subClass);
uint32 count = bot->GetItemCount(entry); uint32 count = bot->GetItemCount(entry);
uint32 maxCount = 5000; uint32 maxCount = 6000;
if (count < maxCount / 2) if (count < maxCount / 2)
{ {
@@ -3879,10 +3878,9 @@ float PlayerbotFactory::CalculateItemScore(uint32 item_id, Player* bot)
score *= 0.1; score *= 0.1;
} }
// spec with double hand // spec with double hand
// fury with titan's grip, fury without duel wield, arms, bear, retribution, blood dk // fury without duel wield, arms, bear, retribution, blood dk
if (isDoubleHand && if (isDoubleHand &&
((cls == CLASS_WARRIOR && tab == WARRIOR_TAB_FURY && bot->CanTitanGrip()) || ((cls == CLASS_WARRIOR && tab == WARRIOR_TAB_FURY && !bot->CanDualWield()) ||
(cls == CLASS_WARRIOR && tab == WARRIOR_TAB_FURY && !bot->CanDualWield()) ||
(cls == CLASS_WARRIOR && tab == WARRIOR_TAB_ARMS) || (cls == CLASS_WARRIOR && tab == WARRIOR_TAB_ARMS) ||
(cls == CLASS_DRUID && tab == 1) || (cls == CLASS_DRUID && tab == 1) ||
(cls == CLASS_PALADIN && tab == 2) || (cls == CLASS_PALADIN && tab == 2) ||
@@ -3890,6 +3888,11 @@ float PlayerbotFactory::CalculateItemScore(uint32 item_id, Player* bot)
(cls == CLASS_SHAMAN && tab == 1 && !bot->CanDualWield()))) { (cls == CLASS_SHAMAN && tab == 1 && !bot->CanDualWield()))) {
score *= 10; score *= 10;
} }
// fury with titan's grip
if (isDoubleHand && proto->SubClass != ITEM_SUBCLASS_WEAPON_POLEARM &&
(cls == CLASS_WARRIOR && tab == WARRIOR_TAB_FURY && bot->CanTitanGrip())) {
score *= 10;
}
} }
if (proto->Class == ITEM_CLASS_WEAPON) { if (proto->Class == ITEM_CLASS_WEAPON) {
if (cls == CLASS_HUNTER && proto->SubClass == ITEM_SUBCLASS_WEAPON_THROWN) { if (cls == CLASS_HUNTER && proto->SubClass == ITEM_SUBCLASS_WEAPON_THROWN) {

View File

@@ -448,6 +448,12 @@ void PlayerbotHolder::OnBotLogin(Player* const bot)
uint32 accountId = bot->GetSession()->GetAccountId(); uint32 accountId = bot->GetSession()->GetAccountId();
bool isRandomAccount = sPlayerbotAIConfig->IsInRandomAccountList(accountId); bool isRandomAccount = sPlayerbotAIConfig->IsInRandomAccountList(accountId);
if (isRandomAccount && sPlayerbotAIConfig->randomBotFixedLevel) {
bot->SetPlayerFlag(PLAYER_FLAGS_NO_XP_GAIN);
} else if (isRandomAccount && !sPlayerbotAIConfig->randomBotFixedLevel) {
bot->RemovePlayerFlag(PLAYER_FLAGS_NO_XP_GAIN);
}
bot->SaveToDB(false, false); bot->SaveToDB(false, false);
if (master && isRandomAccount && master->GetLevel() < bot->GetLevel()) { if (master && isRandomAccount && master->GetLevel() < bot->GetLevel()) {
// PlayerbotFactory factory(bot, master->getLevel()); // PlayerbotFactory factory(bot, master->getLevel());

View File

@@ -2159,6 +2159,9 @@ void RandomItemMgr::BuildEquipCacheNew()
if (IsTestItem(itemId)) { if (IsTestItem(itemId)) {
continue; continue;
} }
if (itemId == 22784) { // Sunwell Orb
continue;
}
equipCacheNew[proto->RequiredLevel][proto->InventoryType].push_back(itemId); equipCacheNew[proto->RequiredLevel][proto->InventoryType].push_back(itemId);
} }
} }
@@ -2199,9 +2202,7 @@ RandomItemList RandomItemMgr::Query(uint32 level, uint8 clazz, uint8 slot, uint3
void RandomItemMgr::BuildAmmoCache() void RandomItemMgr::BuildAmmoCache()
{ {
uint32 maxLevel = sPlayerbotAIConfig->randomBotMaxLevel; uint32 maxLevel = sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL);
if (maxLevel > sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL))
maxLevel = sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL);
LOG_INFO("server.loading", "Building ammo cache for {} levels", maxLevel); LOG_INFO("server.loading", "Building ammo cache for {} levels", maxLevel);
@@ -2238,9 +2239,9 @@ uint32 RandomItemMgr::GetAmmo(uint32 level, uint32 subClass)
void RandomItemMgr::BuildPotionCache() void RandomItemMgr::BuildPotionCache()
{ {
uint32 maxLevel = sPlayerbotAIConfig->randomBotMaxLevel; uint32 maxLevel = sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL);
if (maxLevel > sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL)) // if (maxLevel > sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL))
maxLevel = sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL); // maxLevel = sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL);
LOG_INFO("server.loading", "Building potion cache for {} levels", maxLevel); LOG_INFO("server.loading", "Building potion cache for {} levels", maxLevel);

View File

@@ -1190,6 +1190,10 @@ void RandomPlayerbotMgr::RandomTeleport(Player* bot, std::vector<WorldLocation>&
AreaTableEntry const* zone = sAreaTableStore.LookupEntry(map->GetZoneId(bot->GetPhaseMask(), x, y, z)); AreaTableEntry const* zone = sAreaTableStore.LookupEntry(map->GetZoneId(bot->GetPhaseMask(), x, y, z));
if (!zone) if (!zone)
continue; continue;
AreaTableEntry const* area = sAreaTableStore.LookupEntry(map->GetAreaId(bot->GetPhaseMask(), x, y, z));
if (!area)
continue;
// Do not teleport to enemy zones if level is low // Do not teleport to enemy zones if level is low
if (zone->team == 4 && bot->GetTeamId() == TEAM_ALLIANCE) if (zone->team == 4 && bot->GetTeamId() == TEAM_ALLIANCE)
@@ -1218,9 +1222,14 @@ void RandomPlayerbotMgr::RandomTeleport(Player* bot, std::vector<WorldLocation>&
if (bot->GetLevel() <= 18 && (loc.GetMapId() != pInfo->mapId || dis > 10000.0f)) { if (bot->GetLevel() <= 18 && (loc.GetMapId() != pInfo->mapId || dis > 10000.0f)) {
continue; continue;
} }
LocaleConstant locale = sWorld->GetDefaultDbcLocale();
LOG_INFO("playerbots", "Random teleporting bot {} (level {}) to {} {},{},{} ({}/{} locations)",
bot->GetName().c_str(), bot->GetLevel(), zone->area_name[0], x, y, z, i + 1, tlocs.size()); LOG_INFO("playerbots", "Random teleporting bot {} (level {}) to Map: {} ({}) Zone: {} ({}) Area: {} ({}) {},{},{} ({}/{} locations)",
bot->GetName().c_str(), bot->GetLevel(),
map->GetId(), map->GetMapName(),
zone->ID, zone->area_name[locale],
area->ID, area->area_name[locale],
x, y, z, i + 1, tlocs.size());
if (hearth) if (hearth)
{ {
@@ -1248,9 +1257,7 @@ void RandomPlayerbotMgr::RandomTeleport(Player* bot, std::vector<WorldLocation>&
void RandomPlayerbotMgr::PrepareTeleportCache() void RandomPlayerbotMgr::PrepareTeleportCache()
{ {
uint8 maxLevel = sPlayerbotAIConfig->randomBotMaxLevel; uint32 maxLevel = sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL);
if (maxLevel > sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL))
maxLevel = sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL);
LOG_INFO("playerbots", "Preparing random teleport caches for {} levels...", maxLevel); LOG_INFO("playerbots", "Preparing random teleport caches for {} levels...", maxLevel);

View File

@@ -1495,6 +1495,8 @@ void TravelTarget::setStatus(TravelStatus status)
break; break;
case TRAVEL_STATUS_COOLDOWN: case TRAVEL_STATUS_COOLDOWN:
statusTime = tDestination->getCooldownDelay(); statusTime = tDestination->getCooldownDelay();
default:
break;
} }
} }
@@ -3687,7 +3689,7 @@ void TravelMgr::LoadQuestTravelTable()
} }
} }
else else
"all"; out << "all";
out << "\n"; out << "\n";
} }

View File

@@ -144,7 +144,7 @@ bool Engine::DoNextAction(Unit* unit, uint32 depth, bool minimal)
ActionBasket* basket = nullptr; ActionBasket* basket = nullptr;
time_t currentTime = time(nullptr); time_t currentTime = time(nullptr);
aiObjectContext->Update(); // aiObjectContext->Update();
ProcessTriggers(minimal); ProcessTriggers(minimal);
PushDefaultActions(); PushDefaultActions();
@@ -469,7 +469,7 @@ bool Engine::HasStrategy(std::string const name)
void Engine::ProcessTriggers(bool minimal) void Engine::ProcessTriggers(bool minimal)
{ {
std::unordered_map<Trigger*, Event> fires; // std::unordered_map<Trigger*, Event> fires;
for (std::vector<TriggerNode*>::iterator i = triggers.begin(); i != triggers.end(); i++) for (std::vector<TriggerNode*>::iterator i = triggers.begin(); i != triggers.end(); i++)
{ {
TriggerNode* node = *i; TriggerNode* node = *i;
@@ -499,21 +499,22 @@ void Engine::ProcessTriggers(bool minimal)
if (!event) if (!event)
continue; continue;
fires[trigger] = event; // fires[trigger] = event;
LogAction("T:%s", trigger->getName().c_str()); LogAction("T:%s", trigger->getName().c_str());
MultiplyAndPush(node->getHandlers(), 0.0f, false, event, "trigger");
} }
} }
for (std::vector<TriggerNode*>::iterator i = triggers.begin(); i != triggers.end(); i++) // for (std::vector<TriggerNode*>::iterator i = triggers.begin(); i != triggers.end(); i++)
{ // {
TriggerNode* node = *i; // TriggerNode* node = *i;
Trigger* trigger = node->getTrigger(); // Trigger* trigger = node->getTrigger();
Event event = fires[trigger]; // if (fires.find(trigger) == fires.end())
if (!event) // continue;
continue;
MultiplyAndPush(node->getHandlers(), 0.0f, false, event, "trigger"); // Event event = fires[trigger];
}
// }
for (std::vector<TriggerNode*>::iterator i = triggers.begin(); i != triggers.end(); i++) for (std::vector<TriggerNode*>::iterator i = triggers.begin(); i != triggers.end(); i++)
{ {

View File

@@ -23,10 +23,15 @@ bool ChangeTalentsAction::Execute(Event event)
if (param.find("help") != std::string::npos) { if (param.find("help") != std::string::npos) {
out << TalentsHelp(); out << TalentsHelp();
} else if (param.find("switch") != std::string::npos) { } else if (param.find("switch") != std::string::npos) {
if (param.find("switch 1")) { if (param.find("switch 1") != std::string::npos) {
bot->ActivateSpec(0); bot->ActivateSpec(0);
out << "Active first talent"; out << "Active first talent";
} else if (param.find("switch 2")) { } else if (param.find("switch 2") != std::string::npos) {
if (bot->GetSpecsCount() == 1 && bot->GetLevel() >= sWorld->getIntConfig(CONFIG_MIN_DUALSPEC_LEVEL))
{
bot->CastSpell(bot, 63680, true, nullptr, nullptr, bot->GetGUID());
bot->CastSpell(bot, 63624, true, nullptr, nullptr, bot->GetGUID());
}
bot->ActivateSpec(1); bot->ActivateSpec(1);
out << "Active second talent"; out << "Active second talent";
} }

View File

@@ -115,6 +115,7 @@ class ChatActionContext : public NamedObjectContext<Action>
creators["de"] = &ChatActionContext::dead; creators["de"] = &ChatActionContext::dead;
creators["trainer"] = &ChatActionContext::trainer; creators["trainer"] = &ChatActionContext::trainer;
creators["maintenance"] = &ChatActionContext::maintenance; creators["maintenance"] = &ChatActionContext::maintenance;
creators["remove glyph"] = &ChatActionContext::remove_glyph;
creators["autogear"] = &ChatActionContext::autogear; creators["autogear"] = &ChatActionContext::autogear;
creators["equip upgrade"] = &ChatActionContext::equip_upgrade; creators["equip upgrade"] = &ChatActionContext::equip_upgrade;
creators["attack my target"] = &ChatActionContext::attack_my_target; creators["attack my target"] = &ChatActionContext::attack_my_target;
@@ -215,6 +216,7 @@ class ChatActionContext : public NamedObjectContext<Action>
static Action* attack_my_target(PlayerbotAI* botAI) { return new AttackMyTargetAction(botAI); } static Action* attack_my_target(PlayerbotAI* botAI) { return new AttackMyTargetAction(botAI); }
static Action* trainer(PlayerbotAI* botAI) { return new TrainerAction(botAI); } static Action* trainer(PlayerbotAI* botAI) { return new TrainerAction(botAI); }
static Action* maintenance(PlayerbotAI* botAI) { return new MaintenanceAction(botAI); } static Action* maintenance(PlayerbotAI* botAI) { return new MaintenanceAction(botAI); }
static Action* remove_glyph(PlayerbotAI* botAI) { return new RemoveGlyphAction(botAI); }
static Action* autogear(PlayerbotAI* botAI) { return new AutoGearAction(botAI); } static Action* autogear(PlayerbotAI* botAI) { return new AutoGearAction(botAI); }
static Action* equip_upgrade(PlayerbotAI* botAI) { return new EquipUpgradeAction(botAI); } static Action* equip_upgrade(PlayerbotAI* botAI) { return new EquipUpgradeAction(botAI); }
static Action* co(PlayerbotAI* botAI) { return new ChangeCombatStrategyAction(botAI); } static Action* co(PlayerbotAI* botAI) { return new ChangeCombatStrategyAction(botAI); }

View File

@@ -33,6 +33,7 @@ bool FollowChatShortcutAction::Execute(Event event)
botAI->Reset(); botAI->Reset();
botAI->ChangeStrategy("+follow,-passive,-grind", BOT_STATE_NON_COMBAT); botAI->ChangeStrategy("+follow,-passive,-grind", BOT_STATE_NON_COMBAT);
botAI->ChangeStrategy("-follow,-passive,-grind", BOT_STATE_COMBAT); botAI->ChangeStrategy("-follow,-passive,-grind", BOT_STATE_COMBAT);
botAI->GetAiObjectContext()->GetValue<GuidVector>("prioritized targets")->Set({});
PositionMap& posMap = context->GetValue<PositionMap&>("position")->Get(); PositionMap& posMap = context->GetValue<PositionMap&>("position")->Get();
PositionInfo pos = posMap["return"]; PositionInfo pos = posMap["return"];

View File

@@ -1645,34 +1645,107 @@ bool AvoidAoeAction::AvoidUnitWithDamageAura()
return false; return false;
} }
bool AvoidAoeAction::FleePosition(Position pos, float radius, std::string name) Position AvoidAoeAction::BestPositionForMelee(Position pos, float radius)
{ {
Unit* currentTarget = AI_VALUE(Unit*, "current target"); Unit* currentTarget = AI_VALUE(Unit*, "current target");
std::vector<float> possibleAngles; std::vector<CheckAngle> possibleAngles;
if (currentTarget) { if (currentTarget) {
// Normally, move to left or right is the best position // Normally, move to left or right is the best position
float angleLeft = bot->GetAngle(currentTarget) + M_PI / 2; bool isTanking = (currentTarget->CanFreeMove()) && (currentTarget->GetVictim() == bot);
float angleRight = bot->GetAngle(currentTarget) - M_PI / 2; float angle = bot->GetAngle(currentTarget);
possibleAngles.push_back(angleLeft); float angleLeft = angle + (float)M_PI / 2;
possibleAngles.push_back(angleRight); float angleRight = angle - (float)M_PI / 2;
possibleAngles.push_back({angleLeft, false});
possibleAngles.push_back({angleRight, false});
possibleAngles.push_back({angle, true});
if (isTanking) {
possibleAngles.push_back({angle + (float)M_PI, false});
possibleAngles.push_back({bot->GetAngle(&pos) - (float)M_PI, false});
}
} else { } else {
float angleTo = bot->GetAngle(&pos) - M_PI; float angleTo = bot->GetAngle(&pos) - (float)M_PI;
possibleAngles.push_back(angleTo); possibleAngles.push_back({angleTo, false});
} }
float farestDis = 0.0f; float farestDis = 0.0f;
Position bestPos; Position bestPos;
for (float &angle : possibleAngles) { for (CheckAngle &checkAngle : possibleAngles) {
float angle = checkAngle.angle;
bool strict = checkAngle.strict;
float fleeDis = sPlayerbotAIConfig->fleeDistance; float fleeDis = sPlayerbotAIConfig->fleeDistance;
Position fleePos{bot->GetPositionX() + cos(angle) * fleeDis, Position fleePos{bot->GetPositionX() + cos(angle) * fleeDis,
bot->GetPositionY() + sin(angle) * fleeDis, bot->GetPositionY() + sin(angle) * fleeDis,
bot->GetPositionZ()}; bot->GetPositionZ()};
// todo (Yunfan): check carefully if (strict && currentTarget
&& fleePos.GetExactDist(currentTarget) - currentTarget->GetCombatReach() > sPlayerbotAIConfig->tooCloseDistance) {
continue;
}
if (pos.GetExactDist(fleePos) > farestDis) { if (pos.GetExactDist(fleePos) > farestDis) {
farestDis = pos.GetExactDist(fleePos); farestDis = pos.GetExactDist(fleePos);
bestPos = fleePos; bestPos = fleePos;
} }
} }
if (farestDis > 0.0f) { if (farestDis > 0.0f) {
return bestPos;
}
return Position();
}
Position AvoidAoeAction::BestPositionForRanged(Position pos, float radius)
{
Unit* currentTarget = AI_VALUE(Unit*, "current target");
std::vector<CheckAngle> possibleAngles;
float angleToTarget = 0.0f;
float angleFleeFromCenter = bot->GetAngle(&pos) - (float)M_PI;
if (currentTarget) {
// Normally, move to left or right is the best position
angleToTarget = bot->GetAngle(currentTarget);
float angleLeft = angleToTarget + (float)M_PI / 2;
float angleRight = angleToTarget - (float)M_PI / 2;
possibleAngles.push_back({angleLeft, false});
possibleAngles.push_back({angleRight, false});
possibleAngles.push_back({angleToTarget + (float)M_PI, true});
possibleAngles.push_back({angleToTarget, true});
possibleAngles.push_back({angleFleeFromCenter, true});
} else {
possibleAngles.push_back({angleFleeFromCenter, false});
}
float farestDis = 0.0f;
Position bestPos;
for (CheckAngle &checkAngle : possibleAngles) {
float angle = checkAngle.angle;
bool strict = checkAngle.strict;
float fleeDis = sPlayerbotAIConfig->fleeDistance;
Position fleePos{bot->GetPositionX() + cos(angle) * fleeDis,
bot->GetPositionY() + sin(angle) * fleeDis,
bot->GetPositionZ()};
if (strict && currentTarget
&& fleePos.GetExactDist(currentTarget) - currentTarget->GetCombatReach() > sPlayerbotAIConfig->spellDistance) {
continue;
}
if (strict && currentTarget
&& fleePos.GetExactDist(currentTarget) - currentTarget->GetCombatReach() < (sPlayerbotAIConfig->tooCloseDistance)) {
continue;
}
if (pos.GetExactDist(fleePos) > farestDis) {
farestDis = pos.GetExactDist(fleePos);
bestPos = fleePos;
}
}
if (farestDis > 0.0f) {
return bestPos;
}
return Position();
}
bool AvoidAoeAction::FleePosition(Position pos, float radius, std::string name)
{
Position bestPos;
if (botAI->IsMelee(bot)) {
bestPos = BestPositionForMelee(pos, radius);
} else {
bestPos = BestPositionForRanged(pos, radius);
}
if (bestPos != Position()) {
if (MoveTo(bot->GetMapId(), bestPos.GetPositionX(), bestPos.GetPositionY(), bestPos.GetPositionZ(), false, false, true)) { if (MoveTo(bot->GetMapId(), bestPos.GetPositionX(), bestPos.GetPositionY(), bestPos.GetPositionZ(), false, false, true)) {
if (sPlayerbotAIConfig->tellWhenAvoidAoe && lastTellTimer < time(NULL) - 10) { if (sPlayerbotAIConfig->tellWhenAvoidAoe && lastTellTimer < time(NULL) - 10) {
lastTellTimer = time(NULL); lastTellTimer = time(NULL);

View File

@@ -78,11 +78,14 @@ class AvoidAoeAction : public MovementAction
bool AvoidAuraWithDynamicObj(); bool AvoidAuraWithDynamicObj();
bool AvoidGameObjectWithDamage(); bool AvoidGameObjectWithDamage();
bool AvoidUnitWithDamageAura(); bool AvoidUnitWithDamageAura();
// Position PositionForTank(Position pos, float radius); Position BestPositionForMelee(Position pos, float radius);
// Position PositionForMelee(Position pos, float radius); Position BestPositionForRanged(Position pos, float radius);
// Position PositionForRanged(Position pos, float radius);
bool FleePosition(Position pos, float radius, std::string name); bool FleePosition(Position pos, float radius, std::string name);
time_t lastTellTimer = 0; time_t lastTellTimer = 0;
struct CheckAngle {
float angle;
bool strict;
};
}; };
class RunAwayAction : public MovementAction class RunAwayAction : public MovementAction

View File

@@ -175,6 +175,15 @@ bool MaintenanceAction::Execute(Event event)
return true; return true;
} }
bool RemoveGlyphAction::Execute(Event event)
{
for (uint32 slotIndex = 0; slotIndex < MAX_GLYPH_SLOT_INDEX; ++slotIndex)
{
bot->SetGlyph(slotIndex, 0, true);
}
return true;
}
bool AutoGearAction::Execute(Event event) bool AutoGearAction::Execute(Event event)
{ {
if (!sPlayerbotAIConfig->autoGearCommand) { if (!sPlayerbotAIConfig->autoGearCommand) {

View File

@@ -35,6 +35,13 @@ class MaintenanceAction : public Action
bool Execute(Event event) override; bool Execute(Event event) override;
}; };
class RemoveGlyphAction : public Action
{
public:
RemoveGlyphAction(PlayerbotAI* botAI) : Action(botAI, "remove glyph") { }
bool Execute(Event event) override;
};
class AutoGearAction : public Action class AutoGearAction : public Action
{ {
public: public:

View File

@@ -81,8 +81,10 @@ bool SummonAction::Execute(Event event)
pet->GetCharmInfo()->IsReturning(); pet->GetCharmInfo()->IsReturning();
} }
if (master->GetSession()->GetSecurity() >= SEC_PLAYER) if (master->GetSession()->GetSecurity() >= SEC_PLAYER) {
botAI->GetAiObjectContext()->GetValue<GuidVector>("prioritized targets")->Set({});
return Teleport(master, bot); return Teleport(master, bot);
}
if (SummonUsingGos(master, bot) || SummonUsingNpcs(master, bot)) if (SummonUsingGos(master, bot) || SummonUsingNpcs(master, bot))
{ {

View File

@@ -4,6 +4,7 @@
#include "XpGainAction.h" #include "XpGainAction.h"
#include "Event.h" #include "Event.h"
#include "PlayerbotAIConfig.h"
#include "Playerbots.h" #include "Playerbots.h"
bool XpGainAction::Execute(Event event) bool XpGainAction::Execute(Event event)

View File

@@ -89,6 +89,7 @@ ChatCommandHandlerStrategy::ChatCommandHandlerStrategy(PlayerbotAI* botAI) : Pas
supported.push_back("de"); supported.push_back("de");
supported.push_back("trainer"); supported.push_back("trainer");
supported.push_back("maintenance"); supported.push_back("maintenance");
supported.push_back("remove glyph");
supported.push_back("autogear"); supported.push_back("autogear");
supported.push_back("equip upgrade"); supported.push_back("equip upgrade");
supported.push_back("chat"); supported.push_back("chat");

View File

@@ -55,6 +55,7 @@ class ChatTriggerContext : public NamedObjectContext<Trigger>
creators["de"] = &ChatTriggerContext::dead; creators["de"] = &ChatTriggerContext::dead;
creators["trainer"] = &ChatTriggerContext::trainer; creators["trainer"] = &ChatTriggerContext::trainer;
creators["maintenance"] = &ChatTriggerContext::maintenance; creators["maintenance"] = &ChatTriggerContext::maintenance;
creators["remove glyph"] = &ChatTriggerContext::remove_glyph;
creators["autogear"] = &ChatTriggerContext::autogear; creators["autogear"] = &ChatTriggerContext::autogear;
creators["equip upgrade"] = &ChatTriggerContext::equip_upgrade; creators["equip upgrade"] = &ChatTriggerContext::equip_upgrade;
creators["attack"] = &ChatTriggerContext::attack; creators["attack"] = &ChatTriggerContext::attack;
@@ -168,6 +169,7 @@ class ChatTriggerContext : public NamedObjectContext<Trigger>
static Trigger* attack(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "attack"); } static Trigger* attack(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "attack"); }
static Trigger* trainer(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "trainer"); } static Trigger* trainer(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "trainer"); }
static Trigger* maintenance(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "maintenance"); } static Trigger* maintenance(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "maintenance"); }
static Trigger* remove_glyph(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "remove glyph"); }
static Trigger* autogear(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "autogear"); } static Trigger* autogear(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "autogear"); }
static Trigger* equip_upgrade(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "equip upgrade"); } static Trigger* equip_upgrade(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "equip upgrade"); }
static Trigger* co(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "co"); } static Trigger* co(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "co"); }

View File

@@ -125,7 +125,8 @@ Aura* AreaDebuffValue::Calculate()
Unit::AuraEffectList const& aurasPeriodicDamagePercent = bot->GetAuraEffectsByType(SPELL_AURA_PERIODIC_DAMAGE_PERCENT); Unit::AuraEffectList const& aurasPeriodicDamagePercent = bot->GetAuraEffectsByType(SPELL_AURA_PERIODIC_DAMAGE_PERCENT);
Unit::AuraEffectList const& aurasPeriodicTriggerSpell = bot->GetAuraEffectsByType(SPELL_AURA_PERIODIC_TRIGGER_SPELL); Unit::AuraEffectList const& aurasPeriodicTriggerSpell = bot->GetAuraEffectsByType(SPELL_AURA_PERIODIC_TRIGGER_SPELL);
Unit::AuraEffectList const& aurasPeriodicTriggerWithValueSpell = bot->GetAuraEffectsByType(SPELL_AURA_PERIODIC_TRIGGER_SPELL_WITH_VALUE); Unit::AuraEffectList const& aurasPeriodicTriggerWithValueSpell = bot->GetAuraEffectsByType(SPELL_AURA_PERIODIC_TRIGGER_SPELL_WITH_VALUE);
for (const Unit::AuraEffectList& list : {aurasPeriodicDamage, aurasPeriodicDamagePercent, aurasPeriodicTriggerSpell, aurasPeriodicTriggerWithValueSpell}) { Unit::AuraEffectList const& aurasDummy = bot->GetAuraEffectsByType(SPELL_AURA_DUMMY);
for (const Unit::AuraEffectList& list : {aurasPeriodicDamage, aurasPeriodicDamagePercent, aurasPeriodicTriggerSpell, aurasPeriodicTriggerWithValueSpell, aurasDummy}) {
for (auto i = list.begin(); i != list.end(); ++i) for (auto i = list.begin(); i != list.end(); ++i)
{ {
AuraEffect* aurEff = *i; AuraEffect* aurEff = *i;

View File

@@ -104,7 +104,7 @@ void AttackersValue::RemoveNonThreating(std::unordered_set<Unit*>& targets)
for(std::unordered_set<Unit *>::iterator tIter = targets.begin(); tIter != targets.end();) for(std::unordered_set<Unit *>::iterator tIter = targets.begin(); tIter != targets.end();)
{ {
Unit* unit = *tIter; Unit* unit = *tIter;
if(bot->GetMapId() != unit->GetMapId() || !hasRealThreat(unit) || !IsValidTarget(unit, bot) || !bot->IsWithinLOSInMap(unit)) if(bot->GetMapId() != unit->GetMapId() || !hasRealThreat(unit) || !IsValidTarget(unit, bot))
{ {
std::unordered_set<Unit *>::iterator tIter2 = tIter; std::unordered_set<Unit *>::iterator tIter2 = tIter;
++tIter; ++tIter;
@@ -113,15 +113,6 @@ void AttackersValue::RemoveNonThreating(std::unordered_set<Unit*>& targets)
else else
++tIter; ++tIter;
} }
// Unit* unit = *tIter;
// if (!IsValidTarget(unit, bot) || !bot->IsWithinLOSInMap(unit))
// {
// std::unordered_set<Unit*>::iterator tIter2 = tIter;
// ++tIter;
// targets.erase(tIter2);
// }
// else
// ++tIter;
} }
bool AttackersValue::hasRealThreat(Unit *attacker) bool AttackersValue::hasRealThreat(Unit *attacker)
@@ -160,7 +151,7 @@ bool AttackersValue::IsPossibleTarget(Unit* attacker, Player* bot, float range)
// bool inCannon = botAI->IsInVehicle(false, true); // bool inCannon = botAI->IsInVehicle(false, true);
// bool enemy = botAI->GetAiObjectContext()->GetValue<Unit*>("enemy player target")->Get(); // bool enemy = botAI->GetAiObjectContext()->GetValue<Unit*>("enemy player target")->Get();
return attacker && return attacker && attacker->IsVisible() &&
attacker->IsInWorld() && attacker->IsInWorld() &&
attacker->GetMapId() == bot->GetMapId() && attacker->GetMapId() == bot->GetMapId() &&
!attacker->isDead() && !attacker->isDead() &&
@@ -183,10 +174,9 @@ bool AttackersValue::IsPossibleTarget(Unit* attacker, Player* bot, float range)
bool AttackersValue::IsValidTarget(Unit *attacker, Player *bot) bool AttackersValue::IsValidTarget(Unit *attacker, Player *bot)
{ {
return attacker->IsVisible() && return IsPossibleTarget(attacker, bot) && bot->IsWithinLOSInMap(attacker);
IsPossibleTarget(attacker, bot) && // (attacker->GetThreatMgr().getCurrentVictim() || attacker->GetGuidValue(UNIT_FIELD_TARGET) ||
(attacker->GetThreatMgr().getCurrentVictim() || attacker->GetGuidValue(UNIT_FIELD_TARGET) || // attacker->GetGUID().IsPlayer() || attacker->GetGUID() == GET_PLAYERBOT_AI(bot)->GetAiObjectContext()->GetValue<ObjectGuid>("pull target")->Get());
attacker->GetGUID().IsPlayer() || attacker->GetGUID() == GET_PLAYERBOT_AI(bot)->GetAiObjectContext()->GetValue<ObjectGuid>("pull target")->Get());
} }
bool PossibleAddsValue::Calculate() bool PossibleAddsValue::Calculate()

View File

@@ -71,6 +71,12 @@ class CasterFindTargetSmartStrategy : public FindTargetStrategy
void CheckAttacker(Unit* attacker, ThreatMgr* threatMgr) override void CheckAttacker(Unit* attacker, ThreatMgr* threatMgr) override
{ {
if (Group* group = botAI->GetBot()->GetGroup())
{
ObjectGuid guid = group->GetTargetIcon(4);
if (guid && attacker->GetGUID() == guid)
return;
}
if (!attacker->IsAlive()) { if (!attacker->IsAlive()) {
return; return;
} }
@@ -138,6 +144,12 @@ class NonCasterFindTargetSmartStrategy : public FindTargetStrategy
void CheckAttacker(Unit* attacker, ThreatMgr* threatMgr) override void CheckAttacker(Unit* attacker, ThreatMgr* threatMgr) override
{ {
if (Group* group = botAI->GetBot()->GetGroup())
{
ObjectGuid guid = group->GetTargetIcon(4);
if (guid && attacker->GetGUID() == guid)
return;
}
if (!attacker->IsAlive()) { if (!attacker->IsAlive()) {
return; return;
} }
@@ -193,6 +205,12 @@ class ComboFindTargetSmartStrategy : public FindTargetStrategy
void CheckAttacker(Unit* attacker, ThreatMgr* threatMgr) override void CheckAttacker(Unit* attacker, ThreatMgr* threatMgr) override
{ {
if (Group* group = botAI->GetBot()->GetGroup())
{
ObjectGuid guid = group->GetTargetIcon(4);
if (guid && attacker->GetGUID() == guid)
return;
}
if (!attacker->IsAlive()) { if (!attacker->IsAlive()) {
return; return;
} }

View File

@@ -27,9 +27,7 @@ bool InvalidTargetValue::Calculate()
target->isFeared() || target->isFeared() ||
target->HasUnitState(UNIT_STATE_ISOLATED) || target->HasUnitState(UNIT_STATE_ISOLATED) ||
target->IsFriendlyTo(bot) || target->IsFriendlyTo(bot) ||
!AttackersValue::IsValidTarget(target, bot) || !AttackersValue::IsValidTarget(target, bot);
// !bot->IsWithinDistInMap(target, sPlayerbotAIConfig->sightDistance) ||
!bot->IsWithinLOSInMap(target);
} }
return !target; return !target;