latest playerbots module
This commit is contained in:
parent
6c74f37aee
commit
98f7a2ccfe
@ -237,6 +237,13 @@ bool CheckMountStateAction::TryForms(Player* master, int32 masterMountType, int3
|
||||
if (!master)
|
||||
return false;
|
||||
|
||||
// If both master and bot are in matching forms or master is mounted with corresponding speed, nothing to do
|
||||
else if
|
||||
((masterInShapeshiftForm == FORM_TRAVEL && botInShapeshiftForm == FORM_TRAVEL) ||
|
||||
((masterInShapeshiftForm == FORM_FLIGHT || (masterMountType == 1 && masterSpeed == 149)) && botInShapeshiftForm == FORM_FLIGHT) ||
|
||||
((masterInShapeshiftForm == FORM_FLIGHT_EPIC || (masterMountType == 1 && masterSpeed == 279)) && botInShapeshiftForm == FORM_FLIGHT_EPIC))
|
||||
return true;
|
||||
|
||||
// Check if master is in Travel Form and bot can do the same
|
||||
if (botAI->CanCastSpell(SPELL_TRAVEL_FORM, bot, true) &&
|
||||
masterInShapeshiftForm == FORM_TRAVEL && botInShapeshiftForm != FORM_TRAVEL)
|
||||
|
@ -4,6 +4,9 @@
|
||||
#include "WorldPacket.h"
|
||||
#include "Player.h"
|
||||
#include "ObjectMgr.h"
|
||||
#include "LootObjectStack.h"
|
||||
#include "AiObjectContext.h"
|
||||
|
||||
bool OpenItemAction::Execute(Event event)
|
||||
{
|
||||
bool foundOpenable = false;
|
||||
@ -27,6 +30,11 @@ void OpenItemAction::OpenItem(Item* item, uint8 bag, uint8 slot)
|
||||
packet << bag << slot;
|
||||
bot->GetSession()->HandleOpenItemOpcode(packet);
|
||||
|
||||
// Store the item GUID as the loot target
|
||||
LootObject lootObject;
|
||||
lootObject.guid = item->GetGUID();
|
||||
botAI->GetAiObjectContext()->GetValue<LootObject>("loot target")->Set(lootObject);
|
||||
|
||||
std::ostringstream out;
|
||||
out << "Opened item: " << item->GetTemplate()->Name1;
|
||||
botAI->TellMaster(out.str());
|
||||
|
@ -969,7 +969,7 @@ std::string ChatReplyAction::GenerateReplyMessage(Player* bot, std::string& inco
|
||||
msg = "ya %s but thats in the past";
|
||||
break;
|
||||
case 2:
|
||||
msg = word[verb_pos - 1] + " will " + word[verb_pos + 1] + " again though %s";
|
||||
msg = word[verb_pos ? verb_pos - 1 : verb_pos + 1] + " will " + word[verb_pos + 1] + " again though %s";
|
||||
break;
|
||||
}
|
||||
msg = std::regex_replace(msg, std::regex("%s"), name);
|
||||
@ -1013,7 +1013,7 @@ std::string ChatReplyAction::GenerateReplyMessage(Player* bot, std::string& inco
|
||||
msg = "%s, what will happen %s?";
|
||||
break;
|
||||
case 2:
|
||||
msg = "are you saying " + word[verb_pos - 1] + " will " + word[verb_pos + 1] + " " + word[verb_pos + 2] + " %s?";
|
||||
msg = "are you saying " + word[verb_pos ? verb_pos - 1 : verb_pos + 1] + " will " + word[verb_pos + 1] + " " + word[verb_pos + 2] + " %s?";
|
||||
break;
|
||||
}
|
||||
msg = std::regex_replace(msg, std::regex("%s"), name);
|
||||
|
@ -16,7 +16,10 @@ Value<Unit*>* CastPolymorphAction::GetTargetValue() { return context->GetValue<U
|
||||
bool CastFrostNovaAction::isUseful()
|
||||
{
|
||||
Unit* target = AI_VALUE(Unit*, "current target");
|
||||
if (target && target->ToCreature() && target->ToCreature()->HasMechanicTemplateImmunity(1 << (MECHANIC_FREEZE - 1)))
|
||||
if (!target || !target->IsInWorld())
|
||||
return false;
|
||||
|
||||
if (target->ToCreature() && target->ToCreature()->HasMechanicTemplateImmunity(1 << (MECHANIC_FREEZE - 1)))
|
||||
return false;
|
||||
|
||||
if (target->isFrozen())
|
||||
|
@ -1,9 +1,7 @@
|
||||
#include "Playerbots.h"
|
||||
#include "RaidEoEActions.h"
|
||||
#include "RaidEoETriggers.h"
|
||||
|
||||
#include "Playerbots.h"
|
||||
|
||||
|
||||
bool MalygosPositionAction::Execute(Event event)
|
||||
{
|
||||
Unit* boss = AI_VALUE2(Unit*, "find target", "malygos");
|
||||
@ -280,10 +278,14 @@ bool EoEDrakeAttackAction::isPossible()
|
||||
Unit* vehicleBase = bot->GetVehicleBase();
|
||||
return (vehicleBase && vehicleBase->GetEntry() == NPC_WYRMREST_SKYTALON);
|
||||
}
|
||||
|
||||
bool EoEDrakeAttackAction::Execute(Event event)
|
||||
{
|
||||
vehicleBase = bot->GetVehicleBase();
|
||||
if (!vehicleBase) { return false; }
|
||||
if (!vehicleBase)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Unit* target = AI_VALUE(Unit*, "current target");
|
||||
Unit* boss = AI_VALUE2(Unit*, "find target", "malygos");
|
||||
@ -295,22 +297,55 @@ bool EoEDrakeAttackAction::Execute(Event event)
|
||||
for (auto& npc : npcs)
|
||||
{
|
||||
Unit* unit = botAI->GetUnit(npc);
|
||||
if (!unit || unit->GetEntry() != NPC_MALYGOS) { continue; }
|
||||
if (!unit || unit->GetEntry() != NPC_MALYGOS)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
boss = unit;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Check this again to see if a target was assigned
|
||||
if (!boss) { return false; }
|
||||
|
||||
if (botAI->IsHeal(bot))
|
||||
if (!boss)
|
||||
{
|
||||
return DrakeHealAction();
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8 numHealers;
|
||||
bot->GetRaidDifficulty() == RAID_DIFFICULTY_25MAN_NORMAL ? numHealers = 10 : numHealers = 4;
|
||||
|
||||
Group* group = bot->GetGroup();
|
||||
if (!group)
|
||||
return false;
|
||||
std::vector<std::pair<ObjectGuid, Player*>> sortedMembers;
|
||||
for (GroupReference* itr = group->GetFirstMember(); itr != nullptr; itr = itr->next())
|
||||
{
|
||||
Player* member = itr->GetSource();
|
||||
sortedMembers.push_back(std::make_pair(member->GetGUID(), member));
|
||||
}
|
||||
std::sort(sortedMembers.begin(), sortedMembers.end());
|
||||
|
||||
int botIndex = -1;
|
||||
for (size_t i = 0; i < sortedMembers.size(); ++i)
|
||||
{
|
||||
if (sortedMembers[i].first == bot->GetGUID())
|
||||
{
|
||||
botIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (botIndex == -1)
|
||||
return false;
|
||||
|
||||
if (botIndex > numHealers)
|
||||
{
|
||||
return DrakeDpsAction(boss);
|
||||
}
|
||||
else
|
||||
{
|
||||
return DrakeDpsAction(boss);
|
||||
return DrakeHealAction();
|
||||
}
|
||||
|
||||
return false;
|
||||
@ -348,37 +383,15 @@ bool EoEDrakeAttackAction::DrakeDpsAction(Unit* target)
|
||||
bool EoEDrakeAttackAction::DrakeHealAction()
|
||||
{
|
||||
Unit* vehicleBase = bot->GetVehicleBase();
|
||||
if (!vehicleBase) { return false; }
|
||||
|
||||
Unit* target = vehicleBase->GetComboTarget();
|
||||
if (!target)
|
||||
if (!vehicleBase)
|
||||
{
|
||||
// Unit* newTarget = nullptr;
|
||||
Unit* newTarget = vehicleBase;
|
||||
GuidVector members = AI_VALUE(GuidVector, "group members");
|
||||
for (auto& member : members)
|
||||
{
|
||||
Unit* unit = botAI->GetUnit(member);
|
||||
if (!unit)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
Unit* drake = unit->GetVehicleBase();
|
||||
if (!drake || drake->IsFullHealth()) { continue; }
|
||||
|
||||
if (!newTarget || drake->GetHealthPct() < newTarget->GetHealthPct() - 5.0f)
|
||||
{
|
||||
newTarget = drake;
|
||||
}
|
||||
}
|
||||
target = newTarget;
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8 comboPoints = vehicleBase->GetComboPoints(target);
|
||||
uint8 comboPoints = vehicleBase->GetComboPoints(vehicleBase);
|
||||
if (comboPoints >= 5)
|
||||
{
|
||||
return CastDrakeSpellAction(target, SPELL_LIFE_BURST, 0);
|
||||
return CastDrakeSpellAction(vehicleBase, SPELL_LIFE_BURST, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -386,6 +399,6 @@ bool EoEDrakeAttackAction::DrakeHealAction()
|
||||
// "botAI->CanCastVehicleSpell()" returns SPELL_FAILED_BAD_TARGETS when targeting drakes.
|
||||
// Forcing the cast attempt seems to succeed, not sure what's going on here.
|
||||
// return CastDrakeSpellAction(target, SPELL_REVIVIFY, 0);
|
||||
return botAI->CastVehicleSpell(SPELL_REVIVIFY, target);
|
||||
return botAI->CastVehicleSpell(SPELL_REVIVIFY, vehicleBase);
|
||||
}
|
||||
}
|
||||
|
@ -3206,13 +3206,45 @@ bool IccLichKingWinterAction::Execute(Event event)
|
||||
|
||||
bool IccLichKingAddsAction::Execute(Event event)
|
||||
{
|
||||
if (bot->HasAura(68985) || !bot->IsAlive()) // Don't process actions if bot is picked up by Val'kyr or is dead
|
||||
return false;
|
||||
|
||||
Unit* boss = AI_VALUE2(Unit*, "find target", "the lich king");
|
||||
Unit* spiritWarden = AI_VALUE2(Unit*, "find target", "spirit warden");
|
||||
|
||||
if (bot->HasAura(30440)) // Random aura tracking whether bot has fallen off edge / been thrown by Val'kyr
|
||||
{
|
||||
if(bot->GetPositionZ() > 779.0f)
|
||||
return JumpTo(bot->GetMapId(), bot->GetPositionX(), bot->GetPositionY(), 740.01f);
|
||||
else
|
||||
{
|
||||
bot->KillPlayer(); // If bot has jumped past the kill Z (780), kill
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
bool hasWinterAura = boss->HasAura(72259) || boss->HasAura(74273) || boss->HasAura(74274) || boss->HasAura(74275);
|
||||
bool hasWinter2Aura = boss->HasAura(68981) || boss->HasAura(74270) || boss->HasAura(74271) || boss->HasAura(74272);
|
||||
|
||||
if (boss && boss->GetHealthPct() < 70 && boss->GetHealthPct() > 40 && !hasWinterAura && !hasWinter2Aura) // If boss is in p2, check if bot has been thrown off platform
|
||||
{
|
||||
float dx = bot->GetPositionX() - 503.0f;
|
||||
float dy = bot->GetPositionY() - (-2124.0f);
|
||||
float distance = sqrt(dx*dx + dy*dy); // Calculate distance from the center of the platform
|
||||
|
||||
if (distance > 52.0f && distance < 70.0f && bot->GetPositionZ() > 844) // If bot has fallen off edge, distance is over 52
|
||||
{
|
||||
bot->AddAura(30440, bot); // Apply random 30 sec aura to track that we've initiated a jump
|
||||
return JumpTo(bot->GetMapId(), bot->GetPositionX(), bot->GetPositionY(), 740.01f); // Start jumping to the abyss
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
//temp solution for bots going underground due to buggy ice platfroms and adds that go underground
|
||||
if (abs(bot->GetPositionZ() - 840.857f) > 1.0f)
|
||||
return bot->TeleportTo(bot->GetMapId(), bot->GetPositionX(),
|
||||
bot->GetPositionY(), 840.857f, bot->GetOrientation());
|
||||
*/
|
||||
|
||||
//temp soultion for bots when they get teleport far away into another dimension (they are unable to attack spirit warden) in heroic they will be in safe spot while player is surviving vile spirits
|
||||
Difficulty diff = bot->GetRaidDifficulty();
|
||||
|
@ -24,7 +24,10 @@ public:
|
||||
creators["razorscale grounded"] = &RaidUlduarActionContext::razorscale_grounded;
|
||||
creators["razorscale harpoon action"] = &RaidUlduarActionContext::razorscale_harpoon_action;
|
||||
creators["razorscale fuse armor action"] = &RaidUlduarActionContext::razorscale_fuse_armor_action;
|
||||
creators["iron assembly lightning tendrils action"] = &RaidUlduarActionContext::iron_assembly_lightning_tendrils_action;
|
||||
creators["iron assembly overload action"] = &RaidUlduarActionContext::iron_assembly_overload_action;
|
||||
creators["hodir move snowpacked icicle"] = &RaidUlduarActionContext::hodir_move_snowpacked_icicle;
|
||||
creators["hodir biting cold jump"] = &RaidUlduarActionContext::hodir_biting_cold_jump;
|
||||
creators["freya move away nature bomb"] = &RaidUlduarActionContext::freya_move_away_nature_bomb;
|
||||
creators["freya mark eonars gift"] = &RaidUlduarActionContext::freya_mark_eonars_gift;
|
||||
}
|
||||
@ -39,7 +42,10 @@ private:
|
||||
static Action* razorscale_grounded(PlayerbotAI* ai) { return new RazorscaleGroundedAction(ai); }
|
||||
static Action* razorscale_harpoon_action(PlayerbotAI* ai) { return new RazorscaleHarpoonAction(ai); }
|
||||
static Action* razorscale_fuse_armor_action(PlayerbotAI* ai) { return new RazorscaleFuseArmorAction(ai); }
|
||||
static Action* iron_assembly_lightning_tendrils_action(PlayerbotAI* ai) { return new IronAssemblyLightningTendrilsAction(ai); }
|
||||
static Action* iron_assembly_overload_action(PlayerbotAI* ai) { return new IronAssemblyOverloadAction(ai); }
|
||||
static Action* hodir_move_snowpacked_icicle(PlayerbotAI* ai) { return new HodirMoveSnowpackedIcicleAction(ai); }
|
||||
static Action* hodir_biting_cold_jump(PlayerbotAI* ai) { return new HodirBitingColdJumpAction(ai); }
|
||||
static Action* freya_move_away_nature_bomb(PlayerbotAI* ai) { return new FreyaMoveAwayNatureBombAction(ai); }
|
||||
static Action* freya_mark_eonars_gift(PlayerbotAI* ai) { return new FreyaMarkEonarsGiftAction(ai); }
|
||||
};
|
||||
|
@ -1166,6 +1166,54 @@ bool RazorscaleFuseArmorAction::Execute(Event event)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IronAssemblyLightningTendrilsAction::isUseful()
|
||||
{
|
||||
IronAssemblyLightningTendrilsTrigger ironAssemblyLightningTendrilsTrigger(botAI);
|
||||
return ironAssemblyLightningTendrilsTrigger.IsActive();
|
||||
}
|
||||
|
||||
bool IronAssemblyLightningTendrilsAction::Execute(Event event)
|
||||
{
|
||||
const float radius = 18.0f + 10.0f; // 18 yards + 10 yards for safety
|
||||
|
||||
Unit* boss = AI_VALUE2(Unit*, "find target", "stormcaller brundir");
|
||||
if (!boss)
|
||||
return false;
|
||||
|
||||
float currentDistance = bot->GetDistance2d(boss);
|
||||
|
||||
if (currentDistance < radius)
|
||||
{
|
||||
return MoveAway(boss, radius - currentDistance);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IronAssemblyOverloadAction::isUseful()
|
||||
{
|
||||
IronAssemblyOverloadTrigger ironAssemblyOverloadTrigger(botAI);
|
||||
return ironAssemblyOverloadTrigger.IsActive();
|
||||
}
|
||||
|
||||
bool IronAssemblyOverloadAction::Execute(Event event)
|
||||
{
|
||||
const float radius = 20.0f + 5.0f; // 20 yards + 5 yards for safety
|
||||
|
||||
Unit* boss = AI_VALUE2(Unit*, "find target", "stormcaller brundir");
|
||||
if (!boss)
|
||||
return false;
|
||||
|
||||
float currentDistance = bot->GetDistance2d(boss);
|
||||
|
||||
if (currentDistance < radius)
|
||||
{
|
||||
return MoveAway(boss, radius - currentDistance);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool HodirMoveSnowpackedIcicleAction::isUseful()
|
||||
{
|
||||
// Check boss and it is alive
|
||||
@ -1175,13 +1223,19 @@ bool HodirMoveSnowpackedIcicleAction::isUseful()
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if boss is casting Flash Freeze
|
||||
if (!boss->HasUnitState(UNIT_STATE_CASTING) || !boss->FindCurrentSpellBySpellId(SPELL_FLASH_FREEZE))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Find the nearest Snowpacked Icicle Target
|
||||
Creature* target = bot->FindNearestCreature(33174, 100.0f);
|
||||
Creature* target = bot->FindNearestCreature(NPC_SNOWPACKED_ICICLE, 100.0f);
|
||||
if (!target)
|
||||
return false;
|
||||
|
||||
// Check that boss is stacked on Snowpacked Icicle
|
||||
if (bot->GetDistance2d(target->GetPositionX(), target->GetPositionY()) <= 3.0f)
|
||||
// Check that bot is stacked on Snowpacked Icicle
|
||||
if (bot->GetDistance2d(target->GetPositionX(), target->GetPositionY()) <= 5.0f)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@ -1191,12 +1245,42 @@ bool HodirMoveSnowpackedIcicleAction::isUseful()
|
||||
|
||||
bool HodirMoveSnowpackedIcicleAction::Execute(Event event)
|
||||
{
|
||||
Creature* target = bot->FindNearestCreature(33174, 100.0f);
|
||||
Creature* target = bot->FindNearestCreature(NPC_SNOWPACKED_ICICLE, 100.0f);
|
||||
if (!target)
|
||||
return false;
|
||||
|
||||
return MoveTo(target->GetMapId(), target->GetPositionX(), target->GetPositionY(), target->GetPositionZ(), false,
|
||||
false, false, true, MovementPriority::MOVEMENT_NORMAL);
|
||||
false, false, true, MovementPriority::MOVEMENT_NORMAL, true);
|
||||
}
|
||||
|
||||
bool HodirBitingColdJumpAction::Execute(Event event)
|
||||
{
|
||||
// This needs improving but maybe it should be done in the playerbot core.
|
||||
|
||||
int mapId = bot->GetMap()->GetId();
|
||||
int x = bot->GetPositionX();
|
||||
int y = bot->GetPositionY();
|
||||
int z = bot->GetPositionZ() + 3.98f;
|
||||
float speed = 7.96f;
|
||||
|
||||
UpdateMovementState();
|
||||
if (!IsMovingAllowed(mapId, x, y, z))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
MovementPriority priority;
|
||||
if (IsWaitingForLastMove(priority))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
MotionMaster& mm = *bot->GetMotionMaster();
|
||||
mm.Clear();
|
||||
mm.MoveJump(x, y, z, speed, speed, 1, AI_VALUE(Unit*, "current target"));
|
||||
mm.MoveFall(0, true);
|
||||
AI_VALUE(LastMovement&, "last movement").Set(mapId, x, y, z, bot->GetOrientation(), 1000, priority);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FreyaMoveAwayNatureBombAction::isUseful()
|
||||
|
@ -114,6 +114,29 @@ public:
|
||||
bool isUseful() override;
|
||||
};
|
||||
|
||||
class IronAssemblyLightningTendrilsAction : public MovementAction
|
||||
{
|
||||
public:
|
||||
IronAssemblyLightningTendrilsAction(PlayerbotAI* botAI) : MovementAction(botAI, "iron assembly lightning tendrils action") {}
|
||||
bool Execute(Event event) override;
|
||||
bool isUseful() override;
|
||||
};
|
||||
|
||||
class IronAssemblyOverloadAction : public MovementAction
|
||||
{
|
||||
public:
|
||||
IronAssemblyOverloadAction(PlayerbotAI* botAI) : MovementAction(botAI, "iron assembly overload action") {}
|
||||
bool Execute(Event event) override;
|
||||
bool isUseful() override;
|
||||
};
|
||||
|
||||
class HodirBitingColdJumpAction : public MovementAction
|
||||
{
|
||||
public:
|
||||
HodirBitingColdJumpAction(PlayerbotAI* ai) : MovementAction(ai, "hodir biting cold jump") {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class FreyaMoveAwayNatureBombAction : public MovementAction
|
||||
{
|
||||
public:
|
||||
|
@ -46,25 +46,38 @@ void RaidUlduarStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||
"razorscale fuse armor trigger",
|
||||
NextAction::array(0, new NextAction("razorscale fuse armor action", ACTION_RAID + 2), nullptr)));
|
||||
|
||||
//
|
||||
// Iron Assembly
|
||||
//
|
||||
triggers.push_back(new TriggerNode(
|
||||
"iron assembly lightning tendrils trigger",
|
||||
NextAction::array(0, new NextAction("iron assembly lightning tendrils action", ACTION_RAID), nullptr)));
|
||||
|
||||
triggers.push_back(new TriggerNode(
|
||||
"iron assembly overload trigger",
|
||||
NextAction::array(0, new NextAction("iron assembly overload action", ACTION_RAID), nullptr)));
|
||||
|
||||
//
|
||||
// Hodir
|
||||
//
|
||||
triggers.push_back(new TriggerNode(
|
||||
"hodir near snowpacked icicle",
|
||||
NextAction::array(0, new NextAction("hodir move snowpacked icicle", ACTION_RAID + 5), nullptr)));
|
||||
NextAction::array(0, new NextAction("hodir move snowpacked icicle", ACTION_RAID + 1), nullptr)));
|
||||
|
||||
triggers.push_back(new TriggerNode(
|
||||
"hodir biting cold", NextAction::array(0, new NextAction("intense cold jump", ACTION_RAID + 4), nullptr)));
|
||||
"hodir biting cold",
|
||||
NextAction::array(0, new NextAction("hodir biting cold jump", ACTION_RAID), nullptr)));
|
||||
|
||||
//
|
||||
// Freya
|
||||
//
|
||||
triggers.push_back(
|
||||
new TriggerNode("freya tank near eonars gift",
|
||||
NextAction::array(0, new NextAction("freya mark eonars gift", ACTION_RAID + 1), nullptr)));
|
||||
triggers.push_back(new TriggerNode(
|
||||
"freya tank near eonars gift",
|
||||
NextAction::array(0, new NextAction("freya mark eonars gift", ACTION_RAID + 1), nullptr)));
|
||||
|
||||
triggers.push_back(
|
||||
new TriggerNode("freya near nature bomb",
|
||||
NextAction::array(0, new NextAction("freya move away nature bomb", ACTION_RAID), nullptr)));
|
||||
triggers.push_back(new TriggerNode(
|
||||
"freya near nature bomb",
|
||||
NextAction::array(0, new NextAction("freya move away nature bomb", ACTION_RAID), nullptr)));
|
||||
}
|
||||
|
||||
void RaidUlduarStrategy::InitMultipliers(std::vector<Multiplier*>& multipliers)
|
||||
|
@ -24,6 +24,8 @@ public:
|
||||
creators["razorscale grounded"] = &RaidUlduarTriggerContext::razorscale_grounded;
|
||||
creators["razorscale harpoon trigger"] = &RaidUlduarTriggerContext::razorscale_harpoon_trigger;
|
||||
creators["razorscale fuse armor trigger"] = &RaidUlduarTriggerContext::razorscale_fuse_armor_trigger;
|
||||
creators["iron assembly lightning tendrils trigger"] = &RaidUlduarTriggerContext::iron_assembly_lightning_tendrils_trigger;
|
||||
creators["iron assembly overload trigger"] = &RaidUlduarTriggerContext::iron_assembly_overload_trigger;
|
||||
creators["hodir biting cold"] = &RaidUlduarTriggerContext::hodir_biting_cold;
|
||||
creators["hodir near snowpacked icicle"] = &RaidUlduarTriggerContext::hodir_near_snowpacked_icicle;
|
||||
creators["freya near nature bomb"] = &RaidUlduarTriggerContext::freya_near_nature_bomb;
|
||||
@ -40,6 +42,8 @@ private:
|
||||
static Trigger* razorscale_grounded(PlayerbotAI* ai) { return new RazorscaleGroundedTrigger(ai); }
|
||||
static Trigger* razorscale_harpoon_trigger(PlayerbotAI* ai) { return new RazorscaleHarpoonAvailableTrigger(ai); }
|
||||
static Trigger* razorscale_fuse_armor_trigger(PlayerbotAI* ai) { return new RazorscaleFuseArmorTrigger(ai); }
|
||||
static Trigger* iron_assembly_lightning_tendrils_trigger(PlayerbotAI* ai) { return new IronAssemblyLightningTendrilsTrigger(ai); }
|
||||
static Trigger* iron_assembly_overload_trigger(PlayerbotAI* ai) { return new IronAssemblyOverloadTrigger(ai); }
|
||||
static Trigger* hodir_biting_cold(PlayerbotAI* ai) { return new HodirBitingColdTrigger(ai); }
|
||||
static Trigger* hodir_near_snowpacked_icicle(PlayerbotAI* ai) { return new HodirNearSnowpackedIcicleTrigger(ai); }
|
||||
static Trigger* freya_near_nature_bomb(PlayerbotAI* ai) { return new FreyaNearNatureBombTrigger(ai); }
|
||||
|
@ -242,10 +242,58 @@ bool RazorscaleFuseArmorTrigger::IsActive()
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IronAssemblyLightningTendrilsTrigger::IsActive()
|
||||
{
|
||||
// Check boss and it is alive
|
||||
Unit* boss = AI_VALUE2(Unit*, "find target", "stormcaller brundir");
|
||||
if (!boss || !boss->IsAlive())
|
||||
return false;
|
||||
|
||||
// Check if bot is within 35 yards of the boss
|
||||
if (boss->GetDistance(bot) > 35.0f)
|
||||
return false;
|
||||
|
||||
// Check if the boss has the Lightning Tendrils aura
|
||||
return boss->HasAura(SPELL_LIGHTNING_TENDRILS_10_MAN) || boss->HasAura(SPELL_LIGHTNING_TENDRILS_25_MAN);
|
||||
}
|
||||
|
||||
bool IronAssemblyOverloadTrigger::IsActive()
|
||||
{
|
||||
// Check if bot is tank
|
||||
if (botAI->IsTank(bot))
|
||||
return false;
|
||||
|
||||
// Check boss and it is alive
|
||||
Unit* boss = AI_VALUE2(Unit*, "find target", "stormcaller brundir");
|
||||
if (!boss || !boss->IsAlive())
|
||||
return false;
|
||||
|
||||
// Check if bot is within 35 yards of the boss
|
||||
if (boss->GetDistance(bot) > 35.0f)
|
||||
return false;
|
||||
|
||||
// Check if the boss has the Overload aura
|
||||
return boss->HasAura(SPELL_OVERLOAD_10_MAN) || boss->HasAura(SPELL_OVERLOAD_25_MAN) ||
|
||||
boss->HasAura(SPELL_OVERLOAD_10_MAN_2) || boss->HasAura(SPELL_OVERLOAD_25_MAN_2);
|
||||
}
|
||||
|
||||
bool HodirBitingColdTrigger::IsActive()
|
||||
{
|
||||
Unit* boss = AI_VALUE2(Unit*, "find target", "hodir");
|
||||
return boss && botAI->GetAura("biting cold", bot, false, false);
|
||||
|
||||
// Check boss and it is alive
|
||||
if (!boss || !boss->IsAlive())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Override if boss is casting Flash Freeze
|
||||
if (!boss->HasUnitState(UNIT_STATE_CASTING) || !boss->FindCurrentSpellBySpellId(SPELL_FLASH_FREEZE))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return boss && botAI->GetAura("biting cold", bot, false, false, 2);
|
||||
}
|
||||
|
||||
//Snowpacked Icicle Target
|
||||
@ -258,9 +306,24 @@ bool HodirNearSnowpackedIcicleTrigger::IsActive()
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if boss is casting Flash Freeze
|
||||
if (!boss->HasUnitState(UNIT_STATE_CASTING) || !boss->FindCurrentSpellBySpellId(SPELL_FLASH_FREEZE))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Find the nearest Snowpacked Icicle Target
|
||||
Creature* target = bot->FindNearestCreature(33174, 100.0f);
|
||||
return target != nullptr;
|
||||
Creature* target = bot->FindNearestCreature(NPC_SNOWPACKED_ICICLE, 100.0f);
|
||||
if (!target)
|
||||
return false;
|
||||
|
||||
// Check that bot is stacked on Snowpacked Icicle
|
||||
if (bot->GetDistance2d(target->GetPositionX(), target->GetPositionY()) <= 5.0f)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FreyaNearNatureBombTrigger::IsActive()
|
||||
|
@ -9,9 +9,21 @@
|
||||
|
||||
enum UlduarIDs
|
||||
{
|
||||
// Iron Assembly
|
||||
SPELL_LIGHTNING_TENDRILS_10_MAN = 61887,
|
||||
SPELL_LIGHTNING_TENDRILS_25_MAN = 63486,
|
||||
SPELL_OVERLOAD_10_MAN = 61869,
|
||||
SPELL_OVERLOAD_25_MAN = 63481,
|
||||
SPELL_OVERLOAD_10_MAN_2 = 63485,
|
||||
SPELL_OVERLOAD_25_MAN_2 = 61886,
|
||||
|
||||
// Hodir
|
||||
NPC_SNOWPACKED_ICICLE = 33174,
|
||||
NPC_TOASTY_FIRE = 33342,
|
||||
SPELL_FLASH_FREEZE = 61968,
|
||||
|
||||
// Freya
|
||||
NPC_EONARS_GIFT = 33228,
|
||||
|
||||
GOBJECT_NATURE_BOMB = 194902,
|
||||
};
|
||||
|
||||
@ -84,6 +96,26 @@ public:
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
//
|
||||
// Iron Assembly
|
||||
//
|
||||
class IronAssemblyLightningTendrilsTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
IronAssemblyLightningTendrilsTrigger(PlayerbotAI* ai) : Trigger(ai, "iron assembly lightning tendrils trigger") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class IronAssemblyOverloadTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
IronAssemblyOverloadTrigger(PlayerbotAI* ai) : Trigger(ai, "iron assembly overload trigger") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
//
|
||||
// Hodir
|
||||
//
|
||||
class HodirBitingColdTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
@ -98,6 +130,9 @@ public:
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
//
|
||||
// Freya
|
||||
//
|
||||
class FreyaNearNatureBombTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
|
@ -8,6 +8,7 @@
|
||||
#include "AiFactory.h"
|
||||
#include "ChatHelper.h"
|
||||
#include "GuildTaskMgr.h"
|
||||
#include "LootObjectStack.h"
|
||||
#include "PlayerbotAIConfig.h"
|
||||
#include "PlayerbotFactory.h"
|
||||
#include "Playerbots.h"
|
||||
@ -112,12 +113,38 @@ ItemUsage ItemUsageValue::Calculate()
|
||||
return ITEM_USAGE_DISENCHANT;
|
||||
}
|
||||
|
||||
// While sync is on, do not loot quest items that are also Useful for master. Master
|
||||
if (!botAI->GetMaster() || !sPlayerbotAIConfig->syncQuestWithPlayer ||
|
||||
!IsItemUsefulForQuest(botAI->GetMaster(), proto))
|
||||
if (IsItemUsefulForQuest(bot, proto))
|
||||
return ITEM_USAGE_QUEST;
|
||||
|
||||
Player* master = botAI->GetMaster();
|
||||
bool isSelfBot = (master == bot);
|
||||
bool botNeedsItemForQuest = IsItemUsefulForQuest(bot, proto);
|
||||
bool masterNeedsItemForQuest = master && sPlayerbotAIConfig->syncQuestWithPlayer && IsItemUsefulForQuest(master, proto);
|
||||
|
||||
// Identify the source of loot
|
||||
LootObject lootObject = AI_VALUE(LootObject, "loot target");
|
||||
|
||||
// Get GUID of loot source
|
||||
ObjectGuid lootGuid = lootObject.guid;
|
||||
|
||||
// Check if loot source is an item
|
||||
bool isLootFromItem = lootGuid.IsItem();
|
||||
|
||||
// If the loot is from an item in the bot’s bags, ignore syncQuestWithPlayer
|
||||
if (isLootFromItem && botNeedsItemForQuest)
|
||||
{
|
||||
return ITEM_USAGE_QUEST;
|
||||
}
|
||||
|
||||
// If the bot is NOT acting alone and the master needs this quest item, defer to the master
|
||||
if (!isSelfBot && masterNeedsItemForQuest)
|
||||
{
|
||||
return ITEM_USAGE_NONE;
|
||||
}
|
||||
|
||||
// If the bot itself needs the item for a quest, allow looting
|
||||
if (botNeedsItemForQuest)
|
||||
{
|
||||
return ITEM_USAGE_QUEST;
|
||||
}
|
||||
|
||||
if (proto->Class == ITEM_CLASS_PROJECTILE && bot->CanUseItem(proto) == EQUIP_ERR_OK)
|
||||
{
|
||||
if (bot->getClass() == CLASS_HUNTER || bot->getClass() == CLASS_ROGUE || bot->getClass() == CLASS_WARRIOR)
|
||||
@ -464,6 +491,10 @@ uint32 ItemUsageValue::GetSmallestBagSize()
|
||||
|
||||
bool ItemUsageValue::IsItemUsefulForQuest(Player* player, ItemTemplate const* proto)
|
||||
{
|
||||
PlayerbotAI* botAI = GET_PLAYERBOT_AI(player);
|
||||
if (!botAI)
|
||||
return false;
|
||||
|
||||
for (uint8 slot = 0; slot < MAX_QUEST_LOG_SIZE; ++slot)
|
||||
{
|
||||
uint32 entry = player->GetQuestSlotQuestId(slot);
|
||||
@ -471,20 +502,52 @@ bool ItemUsageValue::IsItemUsefulForQuest(Player* player, ItemTemplate const* pr
|
||||
if (!quest)
|
||||
continue;
|
||||
|
||||
// Check if the item itself is needed for the quest
|
||||
for (uint8 i = 0; i < 4; i++)
|
||||
{
|
||||
if (quest->RequiredItemId[i] != proto->ItemId)
|
||||
if (quest->RequiredItemId[i] == proto->ItemId)
|
||||
{
|
||||
if (AI_VALUE2(uint32, "item count", proto->Name1) >= quest->RequiredItemCount[i])
|
||||
continue;
|
||||
|
||||
return true; // Item is directly required for a quest
|
||||
}
|
||||
}
|
||||
|
||||
// Check if the item has spells that create a required quest item
|
||||
for (uint8 i = 0; i < MAX_ITEM_SPELLS; i++)
|
||||
{
|
||||
uint32 spellId = proto->Spells[i].SpellId;
|
||||
if (!spellId)
|
||||
continue;
|
||||
|
||||
if (GET_PLAYERBOT_AI(player) &&
|
||||
AI_VALUE2(uint32, "item count", proto->Name1) >= quest->RequiredItemCount[i])
|
||||
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId);
|
||||
if (!spellInfo)
|
||||
continue;
|
||||
|
||||
return true;
|
||||
for (uint8 effectIndex = 0; effectIndex < MAX_SPELL_EFFECTS; effectIndex++)
|
||||
{
|
||||
if (spellInfo->Effects[effectIndex].Effect == SPELL_EFFECT_CREATE_ITEM)
|
||||
{
|
||||
uint32 createdItemId = spellInfo->Effects[effectIndex].ItemType;
|
||||
|
||||
// Check if the created item is required for a quest
|
||||
for (uint8 j = 0; j < 4; j++)
|
||||
{
|
||||
if (quest->RequiredItemId[j] == createdItemId)
|
||||
{
|
||||
if (AI_VALUE2(uint32, "item count", createdItemId) >= quest->RequiredItemCount[j])
|
||||
continue;
|
||||
|
||||
return true; // Item is useful because it creates a required quest item
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
return false; // Item is not useful for any active quests
|
||||
}
|
||||
|
||||
bool ItemUsageValue::IsItemNeededForSkill(ItemTemplate const* proto)
|
||||
|
@ -15,6 +15,15 @@ class NormalLootStrategy : public LootStrategy
|
||||
public:
|
||||
bool CanLoot(ItemTemplate const* proto, AiObjectContext* context) override
|
||||
{
|
||||
// Identify the source of loot, loot it if the source is an item in the bots inventory
|
||||
LootObject lootObject = AI_VALUE(LootObject, "loot target");
|
||||
ObjectGuid lootGuid = lootObject.guid;
|
||||
if (lootGuid.IsItem())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// Otherwise, continue with the normal loot logic
|
||||
std::ostringstream out;
|
||||
out << proto->ItemId;
|
||||
ItemUsage usage = AI_VALUE2(ItemUsage, "item usage", out.str());
|
||||
|
Loading…
x
Reference in New Issue
Block a user