mirror of
https://github.com/mod-playerbots/mod-playerbots
synced 2025-11-29 15:58:20 +08:00
558 lines
14 KiB
C++
558 lines
14 KiB
C++
/*
|
|
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
|
|
*/
|
|
|
|
#include "GenericTriggers.h"
|
|
#include "BattlegroundWS.h"
|
|
#include "ObjectGuid.h"
|
|
#include "Playerbots.h"
|
|
#include "SharedDefines.h"
|
|
#include "TemporarySummon.h"
|
|
#include <string>
|
|
|
|
bool LowManaTrigger::IsActive()
|
|
{
|
|
return AI_VALUE2(bool, "has mana", "self target") && AI_VALUE2(uint8, "mana", "self target") < sPlayerbotAIConfig->lowMana;
|
|
}
|
|
|
|
bool MediumManaTrigger::IsActive()
|
|
{
|
|
return AI_VALUE2(bool, "has mana", "self target") && AI_VALUE2(uint8, "mana", "self target") < sPlayerbotAIConfig->mediumMana;
|
|
}
|
|
|
|
bool NoPetTrigger::IsActive()
|
|
{
|
|
return (bot->GetMinionGUID().IsEmpty()) &&
|
|
(!AI_VALUE(Unit*, "pet target")) &&
|
|
(!bot->GetGuardianPet()) &&
|
|
(!bot->GetFirstControlled()) &&
|
|
(!AI_VALUE2(bool, "mounted", "self target"));
|
|
}
|
|
|
|
bool HasPetTrigger::IsActive() {
|
|
return (AI_VALUE(Unit*, "pet target")) && !AI_VALUE2(bool, "mounted", "self target");;
|
|
}
|
|
|
|
bool HighManaTrigger::IsActive()
|
|
{
|
|
return AI_VALUE2(bool, "has mana", "self target") && AI_VALUE2(uint8, "mana", "self target") < 65;
|
|
}
|
|
|
|
bool AlmostFullManaTrigger::IsActive()
|
|
{
|
|
return AI_VALUE2(bool, "has mana", "self target") && AI_VALUE2(uint8, "mana", "self target") > 85;
|
|
}
|
|
|
|
bool RageAvailable::IsActive()
|
|
{
|
|
return AI_VALUE2(uint8, "rage", "self target") >= amount;
|
|
}
|
|
|
|
bool EnergyAvailable::IsActive()
|
|
{
|
|
return AI_VALUE2(uint8, "energy", "self target") >= amount;
|
|
}
|
|
|
|
bool ComboPointsAvailableTrigger::IsActive()
|
|
{
|
|
return AI_VALUE2(uint8, "combo", "current target") >= amount;
|
|
}
|
|
|
|
bool LoseAggroTrigger::IsActive()
|
|
{
|
|
return !AI_VALUE2(bool, "has aggro", "current target");
|
|
}
|
|
|
|
bool HasAggroTrigger::IsActive()
|
|
{
|
|
return AI_VALUE2(bool, "has aggro", "current target");
|
|
}
|
|
|
|
bool PanicTrigger::IsActive()
|
|
{
|
|
return AI_VALUE2(uint8, "health", "self target") < sPlayerbotAIConfig->criticalHealth &&
|
|
(!AI_VALUE2(bool, "has mana", "self target") || AI_VALUE2(uint8, "mana", "self target") < sPlayerbotAIConfig->lowMana);
|
|
}
|
|
|
|
bool OutNumberedTrigger::IsActive()
|
|
{
|
|
if (bot->GetMap() && (bot->GetMap()->IsDungeon() || bot->GetMap()->IsRaid()))
|
|
return false;
|
|
|
|
if (bot->GetGroup() && bot->GetGroup()->isRaidGroup())
|
|
return false;
|
|
|
|
int32 botLevel = bot->getLevel();
|
|
uint32 friendPower = 200;
|
|
uint32 foePower = 0;
|
|
for (auto& attacker : botAI->GetAiObjectContext()->GetValue<GuidVector>("attackers")->Get())
|
|
{
|
|
Creature* creature = botAI->GetCreature(attacker);
|
|
if (!creature)
|
|
continue;
|
|
|
|
int32 dLevel = creature->getLevel() - botLevel;
|
|
if (dLevel > -10)
|
|
foePower = std::max(100 + 10 * dLevel, dLevel * 200);
|
|
}
|
|
|
|
if (!foePower)
|
|
return false;
|
|
|
|
for (auto& helper : botAI->GetAiObjectContext()->GetValue<GuidVector>("nearest friendly players")->Get())
|
|
{
|
|
Unit* player = botAI->GetUnit(helper);
|
|
if (!player || player == bot)
|
|
continue;
|
|
|
|
int32 dLevel = player->getLevel() - botLevel;
|
|
|
|
if (dLevel > -10 && bot->GetDistance(player) < 10.0f)
|
|
friendPower += std::max(200 + 20 * dLevel, dLevel * 200);
|
|
}
|
|
|
|
return friendPower < foePower;
|
|
}
|
|
|
|
bool BuffTrigger::IsActive()
|
|
{
|
|
Unit* target = GetTarget();
|
|
return SpellTrigger::IsActive() && !botAI->HasAura(spell, target, false, checkIsOwner);
|
|
}
|
|
|
|
Value<Unit*>* BuffOnPartyTrigger::GetTargetValue()
|
|
{
|
|
return context->GetValue<Unit*>("party member without aura", spell);
|
|
}
|
|
|
|
bool ProtectPartyMemberTrigger::IsActive()
|
|
{
|
|
return AI_VALUE(Unit*, "party member to protect");
|
|
}
|
|
|
|
Value<Unit*>* DebuffOnAttackerTrigger::GetTargetValue()
|
|
{
|
|
return context->GetValue<Unit*>("attacker without aura", spell);
|
|
}
|
|
|
|
bool NoAttackersTrigger::IsActive()
|
|
{
|
|
return !AI_VALUE(Unit*, "current target") && AI_VALUE(uint8, "my attacker count") > 0;
|
|
}
|
|
|
|
bool InvalidTargetTrigger::IsActive()
|
|
{
|
|
return AI_VALUE2(bool, "invalid target", "current target");
|
|
}
|
|
|
|
bool NoTargetTrigger::IsActive()
|
|
{
|
|
return !AI_VALUE(Unit*, "current target");
|
|
}
|
|
|
|
bool MyAttackerCountTrigger::IsActive()
|
|
{
|
|
return AI_VALUE2(bool, "combat", "self target") && AI_VALUE(uint8, "my attacker count") >= amount;
|
|
}
|
|
|
|
bool AoeTrigger::IsActive()
|
|
{
|
|
Unit* current_target = AI_VALUE(Unit*, "current target");
|
|
if (!current_target) {
|
|
return false;
|
|
}
|
|
GuidVector attackers = context->GetValue<GuidVector>("attackers")->Get();
|
|
int attackers_count = 0;
|
|
for (ObjectGuid const guid : attackers)
|
|
{
|
|
Unit* unit = botAI->GetUnit(guid);
|
|
if (!unit || !unit->IsAlive())
|
|
continue;
|
|
|
|
if (unit->GetExactDist2d(current_target) <= range) {
|
|
attackers_count++;
|
|
}
|
|
}
|
|
return attackers_count >= amount;
|
|
}
|
|
|
|
bool NoFoodTrigger::IsActive()
|
|
{
|
|
bool isRandomBot = sRandomPlayerbotMgr->IsRandomBot(bot);
|
|
if (isRandomBot && sPlayerbotAIConfig->freeFood)
|
|
return false;
|
|
|
|
return AI_VALUE2(std::vector<Item*>, "inventory items", "conjured food").empty();
|
|
}
|
|
|
|
bool NoDrinkTrigger::IsActive()
|
|
{
|
|
bool isRandomBot = sRandomPlayerbotMgr->IsRandomBot(bot);
|
|
if (isRandomBot && sPlayerbotAIConfig->freeFood)
|
|
return false;
|
|
|
|
return AI_VALUE2(std::vector<Item*>, "inventory items", "conjured water").empty();
|
|
}
|
|
|
|
bool TargetInSightTrigger::IsActive()
|
|
{
|
|
return AI_VALUE(Unit*, "grind target");
|
|
}
|
|
|
|
bool DebuffTrigger::IsActive()
|
|
{
|
|
return BuffTrigger::IsActive() && AI_VALUE2(uint8, "health", GetTargetName()) > life_bound;
|
|
}
|
|
|
|
bool SpellTrigger::IsActive()
|
|
{
|
|
return GetTarget();
|
|
}
|
|
|
|
bool SpellCanBeCastTrigger::IsActive()
|
|
{
|
|
Unit* target = GetTarget();
|
|
return target && botAI->CanCastSpell(spell, target);
|
|
}
|
|
|
|
bool SpellNoCooldownTrigger::IsActive()
|
|
{
|
|
uint32 spellId = AI_VALUE2(uint32, "spell id", name);
|
|
if (!spellId)
|
|
return false;
|
|
|
|
return !bot->HasSpellCooldown(spellId);
|
|
}
|
|
|
|
RandomTrigger::RandomTrigger(PlayerbotAI* botAI, std::string const name, int32 probability) : Trigger(botAI, name), probability(probability), lastCheck(time(nullptr))
|
|
{
|
|
}
|
|
|
|
bool RandomTrigger::IsActive()
|
|
{
|
|
if (time(nullptr) - lastCheck < sPlayerbotAIConfig->repeatDelay / 1000)
|
|
return false;
|
|
|
|
lastCheck = time(nullptr);
|
|
int32 k = (int32)(probability / sPlayerbotAIConfig->randomChangeMultiplier);
|
|
if (k < 1)
|
|
k = 1;
|
|
return (rand() % k) == 0;
|
|
}
|
|
|
|
bool AndTrigger::IsActive()
|
|
{
|
|
return ls && rs && ls->IsActive() && rs->IsActive();
|
|
}
|
|
|
|
std::string const AndTrigger::getName()
|
|
{
|
|
std::string name(ls->getName());
|
|
name = name + " and ";
|
|
name = name + rs->getName();
|
|
return name;
|
|
}
|
|
|
|
bool TwoTriggers::IsActive()
|
|
{
|
|
if (name1.empty() || name2.empty())
|
|
return false;
|
|
|
|
Trigger* trigger1 = botAI->GetAiObjectContext()->GetTrigger(name1);
|
|
Trigger* trigger2 = botAI->GetAiObjectContext()->GetTrigger(name2);
|
|
|
|
if (!trigger1 || !trigger2)
|
|
return false;
|
|
|
|
return trigger1->IsActive() && trigger2->IsActive();
|
|
}
|
|
|
|
std::string const TwoTriggers::getName()
|
|
{
|
|
std::string name;
|
|
name = name1 + " and " + name2;
|
|
return name;
|
|
}
|
|
|
|
bool BoostTrigger::IsActive()
|
|
{
|
|
return BuffTrigger::IsActive() && AI_VALUE(uint8, "balance") <= balance;
|
|
}
|
|
|
|
bool ItemCountTrigger::IsActive()
|
|
{
|
|
return AI_VALUE2(uint32, "item count", item) < count;
|
|
}
|
|
|
|
bool InterruptSpellTrigger::IsActive()
|
|
{
|
|
return SpellTrigger::IsActive() && botAI->IsInterruptableSpellCasting(GetTarget(), getName());
|
|
}
|
|
|
|
bool DeflectSpellTrigger::IsActive()
|
|
{
|
|
Unit* target = GetTarget();
|
|
if (!target)
|
|
return false;
|
|
|
|
if (!target->IsNonMeleeSpellCast(true))
|
|
return false;
|
|
|
|
if (target->GetTarget() != bot->GetGUID())
|
|
return false;
|
|
|
|
uint32 spellid = context->GetValue<uint32>("spell id", spell)->Get();
|
|
if (!spellid)
|
|
return false;
|
|
|
|
SpellInfo const* deflectSpell = sSpellMgr->GetSpellInfo(spellid);
|
|
if (!deflectSpell)
|
|
return false;
|
|
|
|
// warrior deflects all
|
|
if (spell == "spell reflection")
|
|
return true;
|
|
|
|
// human priest feedback
|
|
if (spell == "feedback")
|
|
return true;
|
|
|
|
SpellSchoolMask deflectSchool = SpellSchoolMask(deflectSpell->Effects[EFFECT_0].MiscValue);
|
|
SpellSchoolMask attackSchool = SPELL_SCHOOL_MASK_NONE;
|
|
|
|
if (Spell* spell = target->GetCurrentSpell(CURRENT_GENERIC_SPELL))
|
|
{
|
|
if (SpellInfo const* tarSpellInfo = spell->GetSpellInfo())
|
|
{
|
|
attackSchool = tarSpellInfo->GetSchoolMask();
|
|
if (deflectSchool == attackSchool)
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool AttackerCountTrigger::IsActive()
|
|
{
|
|
return AI_VALUE(uint8, "attacker count") >= amount;
|
|
}
|
|
|
|
bool HasAuraTrigger::IsActive()
|
|
{
|
|
return botAI->HasAura(getName(), GetTarget(), false, false, -1, true);
|
|
}
|
|
|
|
bool HasAuraStackTrigger::IsActive()
|
|
{
|
|
Aura *aura = botAI->GetAura(getName(), GetTarget(), false, true, stack);
|
|
// sLog->outMessage("playerbot", LOG_LEVEL_DEBUG, "HasAuraStackTrigger::IsActive %s %d", getName(), aura ? aura->GetStackAmount() : -1);
|
|
return aura;
|
|
}
|
|
|
|
bool TimerTrigger::IsActive()
|
|
{
|
|
if (time(nullptr) != lastCheck)
|
|
{
|
|
lastCheck = time(nullptr);
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool HasNoAuraTrigger::IsActive()
|
|
{
|
|
return !botAI->HasAura(getName(), GetTarget());
|
|
}
|
|
|
|
bool TankAssistTrigger::IsActive()
|
|
{
|
|
if (!AI_VALUE(uint8, "attacker count"))
|
|
return false;
|
|
|
|
Unit* currentTarget = AI_VALUE(Unit*, "current target");
|
|
if (!currentTarget)
|
|
return true;
|
|
|
|
Unit* tankTarget = AI_VALUE(Unit*, "tank target");
|
|
if (!tankTarget || currentTarget == tankTarget)
|
|
return false;
|
|
|
|
return currentTarget->GetVictim() == AI_VALUE(Unit*, "self target");
|
|
}
|
|
|
|
bool IsBehindTargetTrigger::IsActive()
|
|
{
|
|
Unit* target = AI_VALUE(Unit*, "current target");
|
|
return target && AI_VALUE2(bool, "behind", "current target");
|
|
}
|
|
|
|
bool IsNotBehindTargetTrigger::IsActive()
|
|
{
|
|
Unit* target = AI_VALUE(Unit*, "current target");
|
|
return target && !AI_VALUE2(bool, "behind", "current target");
|
|
}
|
|
|
|
bool IsNotFacingTargetTrigger::IsActive()
|
|
{
|
|
return !AI_VALUE2(bool, "facing", "current target");
|
|
}
|
|
|
|
bool HasCcTargetTrigger::IsActive()
|
|
{
|
|
return AI_VALUE2(Unit*, "cc target", getName()) && !AI_VALUE2(Unit*, "current cc target", getName());
|
|
}
|
|
|
|
bool NoMovementTrigger::IsActive()
|
|
{
|
|
return !AI_VALUE2(bool, "moving", "self target");
|
|
}
|
|
|
|
bool NoPossibleTargetsTrigger::IsActive()
|
|
{
|
|
GuidVector targets = AI_VALUE(GuidVector, "possible targets");
|
|
return !targets.size();
|
|
}
|
|
|
|
bool PossibleAddsTrigger::IsActive()
|
|
{
|
|
return AI_VALUE(bool, "possible adds") && !AI_VALUE(ObjectGuid, "pull target");
|
|
}
|
|
|
|
bool NotDpsTargetActiveTrigger::IsActive()
|
|
{
|
|
Unit* dps = AI_VALUE(Unit*, "dps target");
|
|
Unit* target = AI_VALUE(Unit*, "current target");
|
|
Unit* enemy = AI_VALUE(Unit*, "enemy player target");
|
|
|
|
// do not switch if enemy target
|
|
if (target && target == enemy && target->IsAlive())
|
|
return false;
|
|
|
|
return dps && target != dps;
|
|
}
|
|
|
|
bool NotDpsAoeTargetActiveTrigger::IsActive()
|
|
{
|
|
Unit* dps = AI_VALUE(Unit*, "dps aoe target");
|
|
Unit* target = AI_VALUE(Unit*, "current target");
|
|
Unit* enemy = AI_VALUE(Unit*, "enemy player target");
|
|
|
|
// do not switch if enemy target
|
|
if (target && target == enemy && target->IsAlive())
|
|
return false;
|
|
|
|
return dps && target != dps;
|
|
}
|
|
|
|
bool IsSwimmingTrigger::IsActive()
|
|
{
|
|
return AI_VALUE2(bool, "swimming", "self target");
|
|
}
|
|
|
|
bool HasNearestAddsTrigger::IsActive()
|
|
{
|
|
GuidVector targets = AI_VALUE(GuidVector, "nearest adds");
|
|
return targets.size();
|
|
}
|
|
|
|
bool HasItemForSpellTrigger::IsActive()
|
|
{
|
|
std::string const spell = getName();
|
|
uint32 spellId = AI_VALUE2(uint32, "spell id", spell);
|
|
return spellId && AI_VALUE2(Item*, "item for spell", spellId);
|
|
}
|
|
|
|
bool TargetChangedTrigger::IsActive()
|
|
{
|
|
Unit* oldTarget = context->GetValue<Unit*>("old target")->Get();
|
|
Unit* target = context->GetValue<Unit*>("current target")->Get();
|
|
return target && oldTarget != target;
|
|
}
|
|
|
|
Value<Unit*>* InterruptEnemyHealerTrigger::GetTargetValue()
|
|
{
|
|
return context->GetValue<Unit*>("enemy healer target", spell);
|
|
}
|
|
|
|
bool RandomBotUpdateTrigger::IsActive()
|
|
{
|
|
return RandomTrigger::IsActive() && AI_VALUE(bool, "random bot update");
|
|
}
|
|
|
|
bool NoNonBotPlayersAroundTrigger::IsActive()
|
|
{
|
|
return !botAI->HasPlayerNearby();
|
|
/*if (!bot->InBattleground())
|
|
return AI_VALUE(GuidVector, "nearest non bot players").empty();
|
|
|
|
return false;
|
|
*/
|
|
}
|
|
|
|
bool NewPlayerNearbyTrigger::IsActive()
|
|
{
|
|
return AI_VALUE(ObjectGuid, "new player nearby");
|
|
}
|
|
|
|
bool CollisionTrigger::IsActive()
|
|
{
|
|
return AI_VALUE2(bool, "collision", "self target");
|
|
}
|
|
|
|
bool GiveItemTrigger::IsActive()
|
|
{
|
|
return AI_VALUE2(Unit*, "party member without item", item) && AI_VALUE2(uint32, "item count", item);
|
|
}
|
|
|
|
bool GiveFoodTrigger::IsActive()
|
|
{
|
|
return AI_VALUE(Unit*, "party member without food") && AI_VALUE2(uint32, "item count", item);
|
|
}
|
|
|
|
bool GiveWaterTrigger::IsActive()
|
|
{
|
|
return AI_VALUE(Unit*, "party member without water") && AI_VALUE2(uint32, "item count", item);
|
|
}
|
|
|
|
Value<Unit*>* SnareTargetTrigger::GetTargetValue()
|
|
{
|
|
return context->GetValue<Unit*>("snare target", spell);
|
|
}
|
|
|
|
bool StayTimeTrigger::IsActive()
|
|
{
|
|
time_t stayTime = AI_VALUE(time_t, "stay time");
|
|
time_t now = time(nullptr);
|
|
return delay && stayTime && now > stayTime + 2 * delay / 1000;
|
|
}
|
|
|
|
bool IsMountedTrigger::IsActive()
|
|
{
|
|
return AI_VALUE2(bool, "mounted", "self target");
|
|
}
|
|
|
|
bool CorpseNearTrigger::IsActive()
|
|
{
|
|
return bot->GetCorpse() && bot->GetCorpse()->IsWithinDistInMap(bot, CORPSE_RECLAIM_RADIUS, true);
|
|
}
|
|
|
|
bool IsFallingTrigger::IsActive()
|
|
{
|
|
return bot->HasUnitMovementFlag(MOVEMENTFLAG_FALLING);
|
|
}
|
|
|
|
bool IsFallingFarTrigger::IsActive()
|
|
{
|
|
return bot->HasUnitMovementFlag(MOVEMENTFLAG_FALLING_FAR);
|
|
}
|
|
|
|
bool HasAreaDebuffTrigger::IsActive()
|
|
{
|
|
return AI_VALUE2(bool, "has area debuff", "self target");
|
|
}
|
|
|
|
Value<Unit*>* BuffOnMainTankTrigger::GetTargetValue()
|
|
{
|
|
return context->GetValue<Unit*>("main tank", spell);
|
|
} |