mirror of
https://github.com/liyunfan1223/mod-playerbots.git
synced 2026-06-20 15:39:25 +02:00
Wait for attack strategy migration (#2211)
## Pull Request Description Migration of "wait for attack" strategy from cmangos playerbots. Resolves: https://github.com/mod-playerbots/mod-playerbots/issues/990 ## Feature Evaluation Optional strategy for bots which are in party with real player. ## How to Test the Changes - add strategy to bot "nc +wait for attack" and "co +wait for attack" - set time via command "wait for attack time x" where x is time which they wait in seconds (you should get response from bot) - attack any target (for example dummy in main city)(bot should wait with attack) ## Impact Assessment - [ ] No, not at all - [x] Minimal impact (**explain below**) - [ ] Moderate impact (**explain below**) Performance wise only bots having this optinal strategy have additional cost in multiplier which check every attack action that should be execute. - Does this change modify default bot behavior? - [x] No - [ ] Yes (**explain why**) - Does this change add new decision branches or increase maintenance complexity? - [x] No - [ ] Yes (**explain below**) ## Messages to Translate Does this change add bot messages to translate? - [ ] No - [x] Yes (**list messages in the table**) | Message key | Default message | | --------------- | ------------------ | 1740 | Please provide a time to set (in seconds) 1741 | Please provide valid time to set (in seconds) between 0 and 99 1742 | Wait for attack time set to %new_time seconds ## AI Assistance Was AI assistance used while working on this change? - [ ] No - [x] Yes (**explain below**) <!-- If yes, please specify: - Purpose of usage (e.g. brainstorming, refactoring, documentation, code generation). - Which parts of the change were influenced or generated, and whether it was thoroughly reviewed. --> Copilot CLI - help with migration ## Final Checklist - [x] Stability is not compromised. - [x] Performance impact is understood, tested, and acceptable. - [x] Added logic complexity is justified and explained. - [x] Documentation updated if needed (Conf comments, WiKi commands). ## Notes for Reviewers <!-- Anything else that's helpful to review or test your pull request. -->
This commit is contained in:
parent
b172e88010
commit
bbd9d3e37a
@ -0,0 +1,104 @@
|
|||||||
|
-- #########################################################
|
||||||
|
-- Playerbots - Add texts for SetWaitForAttackTimeAction
|
||||||
|
-- Localized for all WotLK locales (koKR, frFR, deDE, zhCN,
|
||||||
|
-- zhTW, esES, esMX, ruRU)
|
||||||
|
-- #########################################################
|
||||||
|
|
||||||
|
DELETE FROM ai_playerbot_texts WHERE name IN ('wait_for_attack_provide_time', 'wait_for_attack_invalid_time', 'wait_for_attack_time_set');
|
||||||
|
DELETE FROM ai_playerbot_texts_chance WHERE name IN ('wait_for_attack_provide_time', 'wait_for_attack_invalid_time', 'wait_for_attack_time_set');
|
||||||
|
|
||||||
|
-- ---------------------------------------------------------
|
||||||
|
-- wait_for_attack_provide_time
|
||||||
|
-- Please provide a time to set (in seconds)
|
||||||
|
-- ---------------------------------------------------------
|
||||||
|
INSERT INTO `ai_playerbot_texts`
|
||||||
|
(`id`, `name`, `text`, `say_type`, `reply_type`,
|
||||||
|
`text_loc1`, `text_loc2`, `text_loc3`, `text_loc4`,
|
||||||
|
`text_loc5`, `text_loc6`, `text_loc7`, `text_loc8`)
|
||||||
|
VALUES (
|
||||||
|
1740,
|
||||||
|
'wait_for_attack_provide_time',
|
||||||
|
'Please provide a time to set (in seconds)',
|
||||||
|
0, 0,
|
||||||
|
-- koKR
|
||||||
|
'설정할 시간을 입력해 주세요 (초 단위)',
|
||||||
|
-- frFR
|
||||||
|
'Veuillez indiquer un temps à définir (en secondes)',
|
||||||
|
-- deDE
|
||||||
|
'Bitte gib eine Zeit an (in Sekunden)',
|
||||||
|
-- zhCN
|
||||||
|
'请提供要设置的时间(以秒为单位)',
|
||||||
|
-- zhTW
|
||||||
|
'請提供要設定的時間(以秒為單位)',
|
||||||
|
-- esES
|
||||||
|
'Por favor, indica un tiempo a establecer (en segundos)',
|
||||||
|
-- esMX
|
||||||
|
'Por favor, indica un tiempo a establecer (en segundos)',
|
||||||
|
-- ruRU
|
||||||
|
'Пожалуйста, укажите время (в секундах)');
|
||||||
|
|
||||||
|
INSERT INTO ai_playerbot_texts_chance (name, probability) VALUES ('wait_for_attack_provide_time', 100);
|
||||||
|
|
||||||
|
-- ---------------------------------------------------------
|
||||||
|
-- wait_for_attack_invalid_time
|
||||||
|
-- Please provide valid time to set (in seconds) between 0 and 99
|
||||||
|
-- ---------------------------------------------------------
|
||||||
|
INSERT INTO `ai_playerbot_texts`
|
||||||
|
(`id`, `name`, `text`, `say_type`, `reply_type`,
|
||||||
|
`text_loc1`, `text_loc2`, `text_loc3`, `text_loc4`,
|
||||||
|
`text_loc5`, `text_loc6`, `text_loc7`, `text_loc8`)
|
||||||
|
VALUES (
|
||||||
|
1741,
|
||||||
|
'wait_for_attack_invalid_time',
|
||||||
|
'Please provide valid time to set (in seconds) between 0 and 99',
|
||||||
|
0, 0,
|
||||||
|
-- koKR
|
||||||
|
'0에서 99 사이의 유효한 시간을 입력해 주세요 (초 단위)',
|
||||||
|
-- frFR
|
||||||
|
'Veuillez indiquer un temps valide (en secondes) entre 0 et 99',
|
||||||
|
-- deDE
|
||||||
|
'Bitte gib eine gültige Zeit an (in Sekunden) zwischen 0 und 99',
|
||||||
|
-- zhCN
|
||||||
|
'请提供有效的时间(以秒为单位),范围为 0 到 99',
|
||||||
|
-- zhTW
|
||||||
|
'請提供有效的時間(以秒為單位),範圍為 0 到 99',
|
||||||
|
-- esES
|
||||||
|
'Por favor, indica un tiempo válido (en segundos) entre 0 y 99',
|
||||||
|
-- esMX
|
||||||
|
'Por favor, indica un tiempo válido (en segundos) entre 0 y 99',
|
||||||
|
-- ruRU
|
||||||
|
'Пожалуйста, укажите допустимое время (в секундах) от 0 до 99');
|
||||||
|
|
||||||
|
INSERT INTO ai_playerbot_texts_chance (name, probability) VALUES ('wait_for_attack_invalid_time', 100);
|
||||||
|
|
||||||
|
-- ---------------------------------------------------------
|
||||||
|
-- wait_for_attack_time_set
|
||||||
|
-- Wait for attack time set to %new_time seconds
|
||||||
|
-- ---------------------------------------------------------
|
||||||
|
INSERT INTO `ai_playerbot_texts`
|
||||||
|
(`id`, `name`, `text`, `say_type`, `reply_type`,
|
||||||
|
`text_loc1`, `text_loc2`, `text_loc3`, `text_loc4`,
|
||||||
|
`text_loc5`, `text_loc6`, `text_loc7`, `text_loc8`)
|
||||||
|
VALUES (
|
||||||
|
1742,
|
||||||
|
'wait_for_attack_time_set',
|
||||||
|
'Wait for attack time set to %new_time seconds',
|
||||||
|
0, 0,
|
||||||
|
-- koKR
|
||||||
|
'공격 대기 시간이 %new_time초로 설정되었습니다',
|
||||||
|
-- frFR
|
||||||
|
'Temps d''attente avant l''attaque défini à %new_time secondes',
|
||||||
|
-- deDE
|
||||||
|
'Wartezeit vor dem Angriff auf %new_time Sekunden gesetzt',
|
||||||
|
-- zhCN
|
||||||
|
'等待攻击时间已设置为 %new_time 秒',
|
||||||
|
-- zhTW
|
||||||
|
'等待攻擊時間已設定為 %new_time 秒',
|
||||||
|
-- esES
|
||||||
|
'Tiempo de espera para atacar establecido en %new_time segundos',
|
||||||
|
-- esMX
|
||||||
|
'Tiempo de espera para atacar establecido en %new_time segundos',
|
||||||
|
-- ruRU
|
||||||
|
'Время ожидания атаки установлено на %new_time секунд');
|
||||||
|
|
||||||
|
INSERT INTO ai_playerbot_texts_chance (name, probability) VALUES ('wait_for_attack_time_set', 100);
|
||||||
@ -65,6 +65,7 @@
|
|||||||
#include "NewRpgAction.h"
|
#include "NewRpgAction.h"
|
||||||
#include "FishingAction.h"
|
#include "FishingAction.h"
|
||||||
#include "CancelChannelAction.h"
|
#include "CancelChannelAction.h"
|
||||||
|
#include "WaitForAttackAction.h"
|
||||||
|
|
||||||
class PlayerbotAI;
|
class PlayerbotAI;
|
||||||
|
|
||||||
@ -263,6 +264,7 @@ public:
|
|||||||
creators["new rpg wander npc"] = &ActionContext::new_rpg_wander_npc;
|
creators["new rpg wander npc"] = &ActionContext::new_rpg_wander_npc;
|
||||||
creators["new rpg do quest"] = &ActionContext::new_rpg_do_quest;
|
creators["new rpg do quest"] = &ActionContext::new_rpg_do_quest;
|
||||||
creators["new rpg travel flight"] = &ActionContext::new_rpg_travel_flight;
|
creators["new rpg travel flight"] = &ActionContext::new_rpg_travel_flight;
|
||||||
|
creators["wait for attack keep safe distance"] = &ActionContext::wait_for_attack_keep_safe_distance;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@ -458,6 +460,7 @@ private:
|
|||||||
static Action* new_rpg_wander_npc(PlayerbotAI* ai) { return new NewRpgWanderNpcAction(ai); }
|
static Action* new_rpg_wander_npc(PlayerbotAI* ai) { return new NewRpgWanderNpcAction(ai); }
|
||||||
static Action* new_rpg_do_quest(PlayerbotAI* ai) { return new NewRpgDoQuestAction(ai); }
|
static Action* new_rpg_do_quest(PlayerbotAI* ai) { return new NewRpgDoQuestAction(ai); }
|
||||||
static Action* new_rpg_travel_flight(PlayerbotAI* ai) { return new NewRpgTravelFlightAction(ai); }
|
static Action* new_rpg_travel_flight(PlayerbotAI* ai) { return new NewRpgTravelFlightAction(ai); }
|
||||||
|
static Action* wait_for_attack_keep_safe_distance(PlayerbotAI* ai) { return new WaitForAttackKeepSafeDistanceAction(ai); }
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@ -14,6 +14,7 @@
|
|||||||
#include "ServerFacade.h"
|
#include "ServerFacade.h"
|
||||||
#include "SharedDefines.h"
|
#include "SharedDefines.h"
|
||||||
#include "Unit.h"
|
#include "Unit.h"
|
||||||
|
#include "WaitForAttackStrategy.h"
|
||||||
|
|
||||||
bool AttackAction::Execute(Event /*event*/)
|
bool AttackAction::Execute(Event /*event*/)
|
||||||
{
|
{
|
||||||
@ -164,7 +165,8 @@ bool AttackAction::Attack(Unit* target, bool /*with_pet*/ /*true*/)
|
|||||||
|
|
||||||
botAI->ChangeEngine(BOT_STATE_COMBAT);
|
botAI->ChangeEngine(BOT_STATE_COMBAT);
|
||||||
|
|
||||||
bot->Attack(target, shouldMelee);
|
if (!WaitForAttackStrategy::ShouldWait(botAI))
|
||||||
|
bot->Attack(target, shouldMelee);
|
||||||
/* prevent pet dead immediately in group */
|
/* prevent pet dead immediately in group */
|
||||||
// if (bot->GetMap()->IsDungeon() && bot->GetGroup() && !target->IsInCombat())
|
// if (bot->GetMap()->IsDungeon() && bot->GetGroup() && !target->IsInCombat())
|
||||||
// {
|
// {
|
||||||
|
|||||||
166
src/Ai/Base/Actions/WaitForAttackAction.cpp
Normal file
166
src/Ai/Base/Actions/WaitForAttackAction.cpp
Normal file
@ -0,0 +1,166 @@
|
|||||||
|
/*
|
||||||
|
* 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 "WaitForAttackAction.h"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cctype>
|
||||||
|
|
||||||
|
#include "ObjectAccessor.h"
|
||||||
|
#include "PlayerbotAI.h"
|
||||||
|
#include "PlayerbotTextMgr.h"
|
||||||
|
#include "Playerbots.h"
|
||||||
|
#include "ServerFacade.h"
|
||||||
|
#include "TravelMgr.h"
|
||||||
|
#include "WaitForAttackStrategy.h"
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
|
||||||
|
WorldPosition GetBestPoint(AiObjectContext* context, Player* bot, Unit* target,
|
||||||
|
float minDistance, float maxDistance)
|
||||||
|
{
|
||||||
|
WorldPosition botPosition(bot);
|
||||||
|
WorldPosition targetPosition(target);
|
||||||
|
|
||||||
|
int8 startDir = urand(0, 1) * 2 - 1;
|
||||||
|
float const radiansIncrement = (5.0f / 180.0f) * static_cast<float>(M_PI);
|
||||||
|
float startAngle = targetPosition.getAngleTo(botPosition) +
|
||||||
|
frand(0.0f, radiansIncrement) * startDir;
|
||||||
|
float distance = frand(minDistance, maxDistance);
|
||||||
|
|
||||||
|
GuidVector enemies = AI_VALUE(GuidVector, "possible targets no los");
|
||||||
|
|
||||||
|
for (float tryAngle = 0.0f; tryAngle < static_cast<float>(M_PI); tryAngle += radiansIncrement)
|
||||||
|
{
|
||||||
|
for (int8 tryDir = -1; tryAngle && tryDir < 1; tryDir += 2)
|
||||||
|
{
|
||||||
|
float pointAngle = startAngle + tryAngle * startDir * tryDir;
|
||||||
|
|
||||||
|
float x = targetPosition.GetPositionX() + distance * cos(pointAngle);
|
||||||
|
float y = targetPosition.GetPositionY() + distance * sin(pointAngle);
|
||||||
|
float z = targetPosition.GetPositionZ() + 1.0f;
|
||||||
|
|
||||||
|
WorldPosition point(targetPosition.GetMapId(), x, y, z);
|
||||||
|
point.setZ(point.getHeight());
|
||||||
|
|
||||||
|
// Check line of sight to target
|
||||||
|
if (!target->IsWithinLOS(point.GetPositionX(), point.GetPositionY(),
|
||||||
|
point.GetPositionZ() + bot->GetCollisionHeight()))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// Check if enemies are close to this point
|
||||||
|
bool enemyClose = false;
|
||||||
|
for (ObjectGuid const& enemyGUID : enemies)
|
||||||
|
{
|
||||||
|
Unit* enemy = ObjectAccessor::GetUnit(*bot, enemyGUID);
|
||||||
|
if (enemy && enemy->IsWithinLOSInMap(bot) && enemy->IsHostileTo(bot))
|
||||||
|
{
|
||||||
|
float enemyAttackRange = enemy->GetCombatReach() + ATTACK_DISTANCE;
|
||||||
|
WorldPosition enemyPos(enemy);
|
||||||
|
if (enemyPos.sqDistance(point) <= (enemyAttackRange * enemyAttackRange))
|
||||||
|
{
|
||||||
|
enemyClose = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (enemyClose)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// Check if bot can path to this point
|
||||||
|
if (!botPosition.canPathTo(point, bot))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
return point;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return botPosition;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
bool WaitForAttackKeepSafeDistanceAction::Execute(Event /*event*/)
|
||||||
|
{
|
||||||
|
Unit* target = AI_VALUE(Unit*, "current target");
|
||||||
|
|
||||||
|
// If our target is moving towards a stationary unit, use that unit as anchor
|
||||||
|
if (target && !target->IsStopped())
|
||||||
|
{
|
||||||
|
ObjectGuid targetGuid = target->GetTarget();
|
||||||
|
if (targetGuid)
|
||||||
|
{
|
||||||
|
Unit* targetsTarget = ObjectAccessor::GetUnit(*target, targetGuid);
|
||||||
|
if (targetsTarget && targetsTarget->IsStopped())
|
||||||
|
target = targetsTarget;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (target && target->IsAlive())
|
||||||
|
{
|
||||||
|
float safeDistance = std::max(
|
||||||
|
target->GetCombatReach() + ATTACK_DISTANCE,
|
||||||
|
WaitForAttackStrategy::GetSafeDistance());
|
||||||
|
float safeDistanceThreshold = WaitForAttackStrategy::GetSafeDistanceThreshold();
|
||||||
|
|
||||||
|
WorldPosition bestPoint = GetBestPoint(context, bot, target,
|
||||||
|
safeDistance - safeDistanceThreshold, safeDistance);
|
||||||
|
|
||||||
|
if (bestPoint)
|
||||||
|
return MoveTo(bestPoint.GetMapId(), bestPoint.GetPositionX(),
|
||||||
|
bestPoint.GetPositionY(), bestPoint.GetPositionZ());
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SetWaitForAttackTimeAction::Execute(Event event)
|
||||||
|
{
|
||||||
|
std::string newTimeStr = event.getParam();
|
||||||
|
|
||||||
|
if (newTimeStr.empty())
|
||||||
|
{
|
||||||
|
std::string const text = PlayerbotTextMgr::instance().GetBotTextOrDefault(
|
||||||
|
"wait_for_attack_provide_time",
|
||||||
|
"Please provide a time to set (in seconds)",
|
||||||
|
std::map<std::string, std::string>());
|
||||||
|
botAI->TellMaster(text);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!std::all_of(newTimeStr.begin(), newTimeStr.end(), ::isdigit))
|
||||||
|
{
|
||||||
|
std::string const text = PlayerbotTextMgr::instance().GetBotTextOrDefault(
|
||||||
|
"wait_for_attack_invalid_time",
|
||||||
|
"Please provide valid time to set (in seconds) between 0 and 99",
|
||||||
|
std::map<std::string, std::string>());
|
||||||
|
botAI->TellMaster(text);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int newTime = std::stoi(newTimeStr);
|
||||||
|
if (newTime < 0 || newTime > 99)
|
||||||
|
{
|
||||||
|
std::string const text = PlayerbotTextMgr::instance().GetBotTextOrDefault(
|
||||||
|
"wait_for_attack_invalid_time",
|
||||||
|
"Please provide valid time to set (in seconds) between 0 and 99",
|
||||||
|
std::map<std::string, std::string>());
|
||||||
|
botAI->TellMaster(text);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
context->GetValue<uint8>("wait for attack time")->Set(static_cast<uint8>(newTime));
|
||||||
|
|
||||||
|
std::map<std::string, std::string> placeholders;
|
||||||
|
placeholders["%new_time"] = std::to_string(newTime);
|
||||||
|
std::string const text = PlayerbotTextMgr::instance().GetBotTextOrDefault(
|
||||||
|
"wait_for_attack_time_set",
|
||||||
|
"Wait for attack time set to %new_time seconds",
|
||||||
|
placeholders);
|
||||||
|
botAI->TellMaster(text);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
31
src/Ai/Base/Actions/WaitForAttackAction.h
Normal file
31
src/Ai/Base/Actions/WaitForAttackAction.h
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
/*
|
||||||
|
* 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_WAITFORATTACKACTION_H
|
||||||
|
#define _PLAYERBOT_WAITFORATTACKACTION_H
|
||||||
|
|
||||||
|
#include "MovementActions.h"
|
||||||
|
|
||||||
|
class PlayerbotAI;
|
||||||
|
|
||||||
|
class WaitForAttackKeepSafeDistanceAction : public MovementAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
WaitForAttackKeepSafeDistanceAction(PlayerbotAI* botAI)
|
||||||
|
: MovementAction(botAI, "wait for attack keep safe distance") {}
|
||||||
|
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class SetWaitForAttackTimeAction : public Action
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
SetWaitForAttackTimeAction(PlayerbotAI* botAI)
|
||||||
|
: Action(botAI, "wait for attack time") {}
|
||||||
|
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
@ -84,6 +84,7 @@
|
|||||||
#include "TellGlyphsAction.h"
|
#include "TellGlyphsAction.h"
|
||||||
#include "EquipGlyphsAction.h"
|
#include "EquipGlyphsAction.h"
|
||||||
#include "PetsAction.h"
|
#include "PetsAction.h"
|
||||||
|
#include "WaitForAttackAction.h"
|
||||||
|
|
||||||
class ChatActionContext : public NamedObjectContext<Action>
|
class ChatActionContext : public NamedObjectContext<Action>
|
||||||
{
|
{
|
||||||
@ -199,6 +200,7 @@ public:
|
|||||||
creators["pet"] = &ChatActionContext::pet;
|
creators["pet"] = &ChatActionContext::pet;
|
||||||
creators["pet attack"] = &ChatActionContext::pet_attack;
|
creators["pet attack"] = &ChatActionContext::pet_attack;
|
||||||
creators["roll"] = &ChatActionContext::roll_action;
|
creators["roll"] = &ChatActionContext::roll_action;
|
||||||
|
creators["wait for attack time"] = &ChatActionContext::wait_for_attack_time;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@ -311,6 +313,7 @@ private:
|
|||||||
static Action* pet(PlayerbotAI* botAI) { return new PetsAction(botAI); }
|
static Action* pet(PlayerbotAI* botAI) { return new PetsAction(botAI); }
|
||||||
static Action* pet_attack(PlayerbotAI* botAI) { return new PetsAction(botAI, "attack"); }
|
static Action* pet_attack(PlayerbotAI* botAI) { return new PetsAction(botAI, "attack"); }
|
||||||
static Action* roll_action(PlayerbotAI* botAI) { return new RollAction(botAI); }
|
static Action* roll_action(PlayerbotAI* botAI) { return new RollAction(botAI); }
|
||||||
|
static Action* wait_for_attack_time(PlayerbotAI* botAI) { return new SetWaitForAttackTimeAction(botAI); }
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@ -145,6 +145,7 @@ public:
|
|||||||
creators["pet"] = &ChatTriggerContext::pet;
|
creators["pet"] = &ChatTriggerContext::pet;
|
||||||
creators["pet attack"] = &ChatTriggerContext::pet_attack;
|
creators["pet attack"] = &ChatTriggerContext::pet_attack;
|
||||||
creators["roll"] = &ChatTriggerContext::roll_action;
|
creators["roll"] = &ChatTriggerContext::roll_action;
|
||||||
|
creators["wait for attack time"] = &ChatTriggerContext::wait_for_attack_time;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@ -269,6 +270,7 @@ private:
|
|||||||
static Trigger* pet(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "pet"); }
|
static Trigger* pet(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "pet"); }
|
||||||
static Trigger* pet_attack(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "pet attack"); }
|
static Trigger* pet_attack(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "pet attack"); }
|
||||||
static Trigger* roll_action(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "roll"); }
|
static Trigger* roll_action(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "roll"); }
|
||||||
|
static Trigger* wait_for_attack_time(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "wait for attack time"); }
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@ -199,4 +199,5 @@ ChatCommandHandlerStrategy::ChatCommandHandlerStrategy(PlayerbotAI* botAI) : Pas
|
|||||||
supported.push_back("glyph equip"); // Added for custom Glyphs
|
supported.push_back("glyph equip"); // Added for custom Glyphs
|
||||||
supported.push_back("pet");
|
supported.push_back("pet");
|
||||||
supported.push_back("pet attack");
|
supported.push_back("pet attack");
|
||||||
|
supported.push_back("wait for attack time");
|
||||||
}
|
}
|
||||||
|
|||||||
93
src/Ai/Base/Strategy/WaitForAttackStrategy.cpp
Normal file
93
src/Ai/Base/Strategy/WaitForAttackStrategy.cpp
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
/*
|
||||||
|
* 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 "WaitForAttackStrategy.h"
|
||||||
|
|
||||||
|
#include "Action.h"
|
||||||
|
#include "PlayerbotAI.h"
|
||||||
|
#include "PlayerbotAIConfig.h"
|
||||||
|
#include "Playerbots.h"
|
||||||
|
#include "Strategy.h"
|
||||||
|
|
||||||
|
void WaitForAttackStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||||
|
{
|
||||||
|
triggers.push_back(new TriggerNode(
|
||||||
|
"wait for attack safe distance",
|
||||||
|
{
|
||||||
|
NextAction("wait for attack keep safe distance", ACTION_RAID)
|
||||||
|
}
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
void WaitForAttackStrategy::InitMultipliers(std::vector<Multiplier*>& multipliers)
|
||||||
|
{
|
||||||
|
multipliers.push_back(new WaitForAttackMultiplier(botAI));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool WaitForAttackStrategy::ShouldWait(PlayerbotAI* botAI)
|
||||||
|
{
|
||||||
|
if (botAI->HasStrategy("wait for attack", BOT_STATE_COMBAT))
|
||||||
|
{
|
||||||
|
Player* bot = botAI->GetBot();
|
||||||
|
if (bot->GetGroup() && botAI->HasRealPlayerMaster())
|
||||||
|
{
|
||||||
|
// Don't wait if the current target is an enemy player
|
||||||
|
Unit* target = botAI->GetAiObjectContext()->GetValue<Unit*>("current target")->Get();
|
||||||
|
if (target && target->IsPlayer())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
AiObjectContext* context = botAI->GetAiObjectContext();
|
||||||
|
time_t combatStartTime = context->GetValue<time_t>("combat start time")->Get();
|
||||||
|
|
||||||
|
if (bot->IsInCombat())
|
||||||
|
{
|
||||||
|
if (combatStartTime == 0)
|
||||||
|
{
|
||||||
|
combatStartTime = time(nullptr);
|
||||||
|
context->GetValue<time_t>("combat start time")->Set(combatStartTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
time_t elapsedTime = time(nullptr) - combatStartTime;
|
||||||
|
return elapsedTime < GetWaitTime(botAI);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (combatStartTime != 0)
|
||||||
|
context->GetValue<time_t>("combat start time")->Set(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8 WaitForAttackStrategy::GetWaitTime(PlayerbotAI* botAI)
|
||||||
|
{
|
||||||
|
return botAI->GetAiObjectContext()->GetValue<uint8>("wait for attack time")->Get();
|
||||||
|
}
|
||||||
|
|
||||||
|
float WaitForAttackStrategy::GetSafeDistance()
|
||||||
|
{
|
||||||
|
return sPlayerbotAIConfig.spellDistance;
|
||||||
|
}
|
||||||
|
|
||||||
|
float WaitForAttackMultiplier::GetValue(Action* action)
|
||||||
|
{
|
||||||
|
std::string const& actionName = action->getName();
|
||||||
|
|
||||||
|
if (actionName != "wait for attack keep safe distance" &&
|
||||||
|
actionName != "dps assist" &&
|
||||||
|
actionName != "set facing" &&
|
||||||
|
actionName != "pull my target" &&
|
||||||
|
actionName != "pull rti target" &&
|
||||||
|
actionName != "pull start" &&
|
||||||
|
actionName != "pull action" &&
|
||||||
|
actionName != "pull end")
|
||||||
|
{
|
||||||
|
return WaitForAttackStrategy::ShouldWait(botAI) ? 0.0f : 1.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1.0f;
|
||||||
|
}
|
||||||
39
src/Ai/Base/Strategy/WaitForAttackStrategy.h
Normal file
39
src/Ai/Base/Strategy/WaitForAttackStrategy.h
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
/*
|
||||||
|
* 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_WAITFORATTACKSTRATEGY_H
|
||||||
|
#define _PLAYERBOT_WAITFORATTACKSTRATEGY_H
|
||||||
|
|
||||||
|
#include "Multiplier.h"
|
||||||
|
#include "Strategy.h"
|
||||||
|
|
||||||
|
class PlayerbotAI;
|
||||||
|
|
||||||
|
class WaitForAttackStrategy : public Strategy
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
WaitForAttackStrategy(PlayerbotAI* botAI) : Strategy(botAI) {}
|
||||||
|
|
||||||
|
std::string const getName() override { return "wait for attack"; }
|
||||||
|
|
||||||
|
static bool ShouldWait(PlayerbotAI* botAI);
|
||||||
|
static uint8 GetWaitTime(PlayerbotAI* botAI);
|
||||||
|
static float GetSafeDistance();
|
||||||
|
static float GetSafeDistanceThreshold() { return 2.5f; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
void InitTriggers(std::vector<TriggerNode*>& triggers) override;
|
||||||
|
void InitMultipliers(std::vector<Multiplier*>& multipliers) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class WaitForAttackMultiplier : public Multiplier
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
WaitForAttackMultiplier(PlayerbotAI* botAI) : Multiplier(botAI, "wait for attack") {}
|
||||||
|
|
||||||
|
float GetValue(Action* action) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
@ -50,6 +50,7 @@
|
|||||||
#include "TravelStrategy.h"
|
#include "TravelStrategy.h"
|
||||||
#include "UseFoodStrategy.h"
|
#include "UseFoodStrategy.h"
|
||||||
#include "UsePotionsStrategy.h"
|
#include "UsePotionsStrategy.h"
|
||||||
|
#include "WaitForAttackStrategy.h"
|
||||||
#include "WorldPacketHandlerStrategy.h"
|
#include "WorldPacketHandlerStrategy.h"
|
||||||
|
|
||||||
class StrategyContext : public NamedObjectContext<Strategy>
|
class StrategyContext : public NamedObjectContext<Strategy>
|
||||||
@ -124,6 +125,7 @@ public:
|
|||||||
creators["worldbuff"] = &StrategyContext::world_buff;
|
creators["worldbuff"] = &StrategyContext::world_buff;
|
||||||
creators["use bobber"] = &StrategyContext::bobber_strategy;
|
creators["use bobber"] = &StrategyContext::bobber_strategy;
|
||||||
creators["master fishing"] = &StrategyContext::master_fishing;
|
creators["master fishing"] = &StrategyContext::master_fishing;
|
||||||
|
creators["wait for attack"] = &StrategyContext::wait_for_attack;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@ -195,6 +197,7 @@ private:
|
|||||||
static Strategy* world_buff(PlayerbotAI* botAI) { return new WorldBuffStrategy(botAI); }
|
static Strategy* world_buff(PlayerbotAI* botAI) { return new WorldBuffStrategy(botAI); }
|
||||||
static Strategy* bobber_strategy(PlayerbotAI* botAI) { return new UseBobberStrategy(botAI); }
|
static Strategy* bobber_strategy(PlayerbotAI* botAI) { return new UseBobberStrategy(botAI); }
|
||||||
static Strategy* master_fishing(PlayerbotAI* botAI) { return new MasterFishingStrategy(botAI); }
|
static Strategy* master_fishing(PlayerbotAI* botAI) { return new MasterFishingStrategy(botAI); }
|
||||||
|
static Strategy* wait_for_attack(PlayerbotAI* botAI) { return new WaitForAttackStrategy(botAI); }
|
||||||
};
|
};
|
||||||
|
|
||||||
class MovementStrategyContext : public NamedObjectContext<Strategy>
|
class MovementStrategyContext : public NamedObjectContext<Strategy>
|
||||||
|
|||||||
49
src/Ai/Base/Trigger/WaitForAttackTriggers.h
Normal file
49
src/Ai/Base/Trigger/WaitForAttackTriggers.h
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
/*
|
||||||
|
* 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_WAITFORATTACKTRIGGERS_H
|
||||||
|
#define _PLAYERBOT_WAITFORATTACKTRIGGERS_H
|
||||||
|
|
||||||
|
#include "PlayerbotAIConfig.h"
|
||||||
|
#include "Playerbots.h"
|
||||||
|
#include "ServerFacade.h"
|
||||||
|
#include "Trigger.h"
|
||||||
|
#include "WaitForAttackStrategy.h"
|
||||||
|
|
||||||
|
class PlayerbotAI;
|
||||||
|
|
||||||
|
class WaitForAttackSafeDistanceTrigger : public Trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
WaitForAttackSafeDistanceTrigger(PlayerbotAI* botAI)
|
||||||
|
: Trigger(botAI, "wait for attack safe distance") {}
|
||||||
|
|
||||||
|
bool IsActive() override
|
||||||
|
{
|
||||||
|
if (!WaitForAttackStrategy::ShouldWait(botAI))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Do not move if stay strategy is set
|
||||||
|
if (botAI->HasStrategy("stay", botAI->GetState()))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Do not move if currently being targeted
|
||||||
|
if (!bot->getAttackers().empty())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Unit* target = AI_VALUE(Unit*, "current target");
|
||||||
|
if (!target)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
float safeDistance = WaitForAttackStrategy::GetSafeDistance();
|
||||||
|
float safeDistanceThreshold = WaitForAttackStrategy::GetSafeDistanceThreshold();
|
||||||
|
float distanceToTarget = ServerFacade::instance().GetDistance2d(bot, target);
|
||||||
|
|
||||||
|
return (distanceToTarget > (safeDistance + safeDistanceThreshold)) ||
|
||||||
|
(distanceToTarget < (safeDistance - safeDistanceThreshold));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
@ -20,6 +20,7 @@
|
|||||||
#include "RtiTriggers.h"
|
#include "RtiTriggers.h"
|
||||||
#include "StuckTriggers.h"
|
#include "StuckTriggers.h"
|
||||||
#include "TravelTriggers.h"
|
#include "TravelTriggers.h"
|
||||||
|
#include "WaitForAttackTriggers.h"
|
||||||
|
|
||||||
class PlayerbotAI;
|
class PlayerbotAI;
|
||||||
|
|
||||||
@ -231,6 +232,7 @@ public:
|
|||||||
creators["can fish"] = &TriggerContext::can_fish;
|
creators["can fish"] = &TriggerContext::can_fish;
|
||||||
creators["can use fishing bobber"] = &TriggerContext::can_use_fishing_bobber;
|
creators["can use fishing bobber"] = &TriggerContext::can_use_fishing_bobber;
|
||||||
creators["new pet"] = &TriggerContext::new_pet;
|
creators["new pet"] = &TriggerContext::new_pet;
|
||||||
|
creators["wait for attack safe distance"] = &TriggerContext::wait_for_attack_safe_distance;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@ -434,6 +436,7 @@ private:
|
|||||||
static Trigger* can_fish(PlayerbotAI* ai) { return new CanFishTrigger(ai); }
|
static Trigger* can_fish(PlayerbotAI* ai) { return new CanFishTrigger(ai); }
|
||||||
static Trigger* can_use_fishing_bobber(PlayerbotAI* ai) { return new CanUseFishingBobberTrigger(ai); }
|
static Trigger* can_use_fishing_bobber(PlayerbotAI* ai) { return new CanUseFishingBobberTrigger(ai); }
|
||||||
static Trigger* new_pet(PlayerbotAI* ai) { return new NewPetTrigger(ai); }
|
static Trigger* new_pet(PlayerbotAI* ai) { return new NewPetTrigger(ai); }
|
||||||
|
static Trigger* wait_for_attack_safe_distance(PlayerbotAI* ai) { return new WaitForAttackSafeDistanceTrigger(ai); }
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
25
src/Ai/Base/Value/WaitForAttackTimeValue.h
Normal file
25
src/Ai/Base/Value/WaitForAttackTimeValue.h
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
/*
|
||||||
|
* 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_WAITFORATTACKTIMEVALUE_H
|
||||||
|
#define _PLAYERBOT_WAITFORATTACKTIMEVALUE_H
|
||||||
|
|
||||||
|
#include "Value.h"
|
||||||
|
|
||||||
|
class PlayerbotAI;
|
||||||
|
|
||||||
|
class WaitForAttackTimeValue : public ManualSetValue<uint8>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
WaitForAttackTimeValue(PlayerbotAI* botAI) : ManualSetValue<uint8>(botAI, 10, "wait for attack time") {}
|
||||||
|
};
|
||||||
|
|
||||||
|
class CombatStartTimeValue : public ManualSetValue<time_t>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CombatStartTimeValue(PlayerbotAI* botAI) : ManualSetValue<time_t>(botAI, 0, "combat start time") {}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
@ -91,6 +91,7 @@
|
|||||||
#include "ThreatValues.h"
|
#include "ThreatValues.h"
|
||||||
#include "TradeValues.h"
|
#include "TradeValues.h"
|
||||||
#include "Value.h"
|
#include "Value.h"
|
||||||
|
#include "WaitForAttackTimeValue.h"
|
||||||
|
|
||||||
class PlayerbotAI;
|
class PlayerbotAI;
|
||||||
|
|
||||||
@ -322,6 +323,8 @@ public:
|
|||||||
creators["can fish"] = &ValueContext::can_fish;
|
creators["can fish"] = &ValueContext::can_fish;
|
||||||
creators["can use fishing bobber"] = &ValueContext::can_use_fishing_bobber;
|
creators["can use fishing bobber"] = &ValueContext::can_use_fishing_bobber;
|
||||||
creators["fishing spot"] = &ValueContext::fishing_spot;
|
creators["fishing spot"] = &ValueContext::fishing_spot;
|
||||||
|
creators["wait for attack time"] = &ValueContext::wait_for_attack_time;
|
||||||
|
creators["combat start time"] = &ValueContext::combat_start_time;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@ -578,6 +581,8 @@ private:
|
|||||||
{
|
{
|
||||||
return new ManualSetValue<bool>(ai, false, "custom_glyphs");
|
return new ManualSetValue<bool>(ai, false, "custom_glyphs");
|
||||||
}
|
}
|
||||||
|
static UntypedValue* wait_for_attack_time(PlayerbotAI* ai) { return new WaitForAttackTimeValue(ai); }
|
||||||
|
static UntypedValue* combat_start_time(PlayerbotAI* ai) { return new CombatStartTimeValue(ai); }
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user