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:
kadeshar 2026-03-27 18:38:46 +01:00 committed by GitHub
parent b172e88010
commit bbd9d3e37a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
15 changed files with 530 additions and 1 deletions

View File

@ -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);

View File

@ -65,6 +65,7 @@
#include "NewRpgAction.h"
#include "FishingAction.h"
#include "CancelChannelAction.h"
#include "WaitForAttackAction.h"
class PlayerbotAI;
@ -263,6 +264,7 @@ public:
creators["new rpg wander npc"] = &ActionContext::new_rpg_wander_npc;
creators["new rpg do quest"] = &ActionContext::new_rpg_do_quest;
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:
@ -458,6 +460,7 @@ private:
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_travel_flight(PlayerbotAI* ai) { return new NewRpgTravelFlightAction(ai); }
static Action* wait_for_attack_keep_safe_distance(PlayerbotAI* ai) { return new WaitForAttackKeepSafeDistanceAction(ai); }
};
#endif

View File

@ -14,6 +14,7 @@
#include "ServerFacade.h"
#include "SharedDefines.h"
#include "Unit.h"
#include "WaitForAttackStrategy.h"
bool AttackAction::Execute(Event /*event*/)
{
@ -164,7 +165,8 @@ bool AttackAction::Attack(Unit* target, bool /*with_pet*/ /*true*/)
botAI->ChangeEngine(BOT_STATE_COMBAT);
bot->Attack(target, shouldMelee);
if (!WaitForAttackStrategy::ShouldWait(botAI))
bot->Attack(target, shouldMelee);
/* prevent pet dead immediately in group */
// if (bot->GetMap()->IsDungeon() && bot->GetGroup() && !target->IsInCombat())
// {

View 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;
}

View 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

View File

@ -84,6 +84,7 @@
#include "TellGlyphsAction.h"
#include "EquipGlyphsAction.h"
#include "PetsAction.h"
#include "WaitForAttackAction.h"
class ChatActionContext : public NamedObjectContext<Action>
{
@ -199,6 +200,7 @@ public:
creators["pet"] = &ChatActionContext::pet;
creators["pet attack"] = &ChatActionContext::pet_attack;
creators["roll"] = &ChatActionContext::roll_action;
creators["wait for attack time"] = &ChatActionContext::wait_for_attack_time;
}
private:
@ -311,6 +313,7 @@ private:
static Action* pet(PlayerbotAI* botAI) { return new PetsAction(botAI); }
static Action* pet_attack(PlayerbotAI* botAI) { return new PetsAction(botAI, "attack"); }
static Action* roll_action(PlayerbotAI* botAI) { return new RollAction(botAI); }
static Action* wait_for_attack_time(PlayerbotAI* botAI) { return new SetWaitForAttackTimeAction(botAI); }
};
#endif

View File

@ -145,6 +145,7 @@ public:
creators["pet"] = &ChatTriggerContext::pet;
creators["pet attack"] = &ChatTriggerContext::pet_attack;
creators["roll"] = &ChatTriggerContext::roll_action;
creators["wait for attack time"] = &ChatTriggerContext::wait_for_attack_time;
}
private:
@ -269,6 +270,7 @@ private:
static Trigger* pet(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "pet"); }
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* wait_for_attack_time(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "wait for attack time"); }
};
#endif

View File

@ -199,4 +199,5 @@ ChatCommandHandlerStrategy::ChatCommandHandlerStrategy(PlayerbotAI* botAI) : Pas
supported.push_back("glyph equip"); // Added for custom Glyphs
supported.push_back("pet");
supported.push_back("pet attack");
supported.push_back("wait for attack time");
}

View 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;
}

View 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

View File

@ -50,6 +50,7 @@
#include "TravelStrategy.h"
#include "UseFoodStrategy.h"
#include "UsePotionsStrategy.h"
#include "WaitForAttackStrategy.h"
#include "WorldPacketHandlerStrategy.h"
class StrategyContext : public NamedObjectContext<Strategy>
@ -124,6 +125,7 @@ public:
creators["worldbuff"] = &StrategyContext::world_buff;
creators["use bobber"] = &StrategyContext::bobber_strategy;
creators["master fishing"] = &StrategyContext::master_fishing;
creators["wait for attack"] = &StrategyContext::wait_for_attack;
}
private:
@ -195,6 +197,7 @@ private:
static Strategy* world_buff(PlayerbotAI* botAI) { return new WorldBuffStrategy(botAI); }
static Strategy* bobber_strategy(PlayerbotAI* botAI) { return new UseBobberStrategy(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>

View 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

View File

@ -20,6 +20,7 @@
#include "RtiTriggers.h"
#include "StuckTriggers.h"
#include "TravelTriggers.h"
#include "WaitForAttackTriggers.h"
class PlayerbotAI;
@ -231,6 +232,7 @@ public:
creators["can fish"] = &TriggerContext::can_fish;
creators["can use fishing bobber"] = &TriggerContext::can_use_fishing_bobber;
creators["new pet"] = &TriggerContext::new_pet;
creators["wait for attack safe distance"] = &TriggerContext::wait_for_attack_safe_distance;
}
private:
@ -434,6 +436,7 @@ private:
static Trigger* can_fish(PlayerbotAI* ai) { return new CanFishTrigger(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* wait_for_attack_safe_distance(PlayerbotAI* ai) { return new WaitForAttackSafeDistanceTrigger(ai); }
};
#endif

View 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

View File

@ -91,6 +91,7 @@
#include "ThreatValues.h"
#include "TradeValues.h"
#include "Value.h"
#include "WaitForAttackTimeValue.h"
class PlayerbotAI;
@ -322,6 +323,8 @@ public:
creators["can fish"] = &ValueContext::can_fish;
creators["can use fishing bobber"] = &ValueContext::can_use_fishing_bobber;
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:
@ -578,6 +581,8 @@ private:
{
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