From 47f8eb3e4a133f8691a4c444745e4c46a28f9d9f Mon Sep 17 00:00:00 2001 From: Yunfan Li Date: Mon, 19 Aug 2024 19:30:04 +0800 Subject: [PATCH] Ulduar flame leviathan (normal mode) --- src/strategy/AiObjectContext.cpp | 4 + src/strategy/actions/ListSpellsAction.cpp | 3 + src/strategy/actions/MovementActions.cpp | 14 +- .../actions/UseMeetingStoneAction.cpp | 7 + src/strategy/actions/VehicleActions.cpp | 62 ++- src/strategy/actions/VehicleActions.h | 2 + .../generic/ChatCommandHandlerStrategy.cpp | 4 + src/strategy/raids/RaidStrategyContext.h | 7 +- .../raids/ulduar/RaidUlduarActionContext.h | 8 +- .../raids/ulduar/RaidUlduarActions.cpp | 369 +++++++++++++++++- src/strategy/raids/ulduar/RaidUlduarActions.h | 40 +- .../raids/ulduar/RaidUlduarBossHelper.h | 123 +++--- .../raids/ulduar/RaidUlduarMultipliers.cpp | 7 + .../raids/ulduar/RaidUlduarMultipliers.h | 9 + src/strategy/raids/ulduar/RaidUlduarScripts.h | 1 + .../raids/ulduar/RaidUlduarStrategy.cpp | 18 +- .../raids/ulduar/RaidUlduarStrategy.h | 3 +- .../raids/ulduar/RaidUlduarTriggerContext.h | 10 +- .../raids/ulduar/RaidUlduarTriggers.cpp | 39 ++ .../raids/ulduar/RaidUlduarTriggers.h | 16 +- src/strategy/triggers/ChatTriggerContext.h | 4 + src/strategy/values/ValueContext.h | 2 + 22 files changed, 639 insertions(+), 113 deletions(-) diff --git a/src/strategy/AiObjectContext.cpp b/src/strategy/AiObjectContext.cpp index c844bb51..5c4237d7 100644 --- a/src/strategy/AiObjectContext.cpp +++ b/src/strategy/AiObjectContext.cpp @@ -9,6 +9,8 @@ #include "ChatActionContext.h" #include "ChatTriggerContext.h" #include "Playerbots.h" +#include "RaidUlduarTriggerContext.h" +#include "RaidUlduarActionContext.h" #include "SharedValueContext.h" #include "StrategyContext.h" #include "TriggerContext.h" @@ -34,12 +36,14 @@ AiObjectContext::AiObjectContext(PlayerbotAI* botAI) : PlayerbotAIAware(botAI) actionContexts.Add(new WorldPacketActionContext()); actionContexts.Add(new RaidActionContext()); actionContexts.Add(new RaidNaxxActionContext()); + actionContexts.Add(new RaidUlduarActionContext()); triggerContexts.Add(new TriggerContext()); triggerContexts.Add(new ChatTriggerContext()); triggerContexts.Add(new WorldPacketTriggerContext()); triggerContexts.Add(new RaidTriggerContext()); triggerContexts.Add(new RaidNaxxTriggerContext()); + triggerContexts.Add(new RaidUlduarTriggerContext()); valueContexts.Add(new ValueContext()); diff --git a/src/strategy/actions/ListSpellsAction.cpp b/src/strategy/actions/ListSpellsAction.cpp index 652d22b7..75d25ac9 100644 --- a/src/strategy/actions/ListSpellsAction.cpp +++ b/src/strategy/actions/ListSpellsAction.cpp @@ -139,6 +139,9 @@ std::vector> ListSpellsAction::GetSpellList(std:: { if (itr->second->State == PLAYERSPELL_REMOVED || !itr->second->Active) continue; + + if (!(itr->second->specMask & bot->GetActiveSpecMask())) + continue; SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(itr->first); if (!spellInfo) diff --git a/src/strategy/actions/MovementActions.cpp b/src/strategy/actions/MovementActions.cpp index 641b9cfc..cf51e144 100644 --- a/src/strategy/actions/MovementActions.cpp +++ b/src/strategy/actions/MovementActions.cpp @@ -881,9 +881,9 @@ bool MovementAction::IsMovingAllowed(Unit* target) if (bot->GetMapId() != target->GetMapId()) return false; - float distance = sServerFacade->GetDistance2d(bot, target); - if (!bot->InBattleground() && distance > sPlayerbotAIConfig->reactDistance) - return false; + // float distance = sServerFacade->GetDistance2d(bot, target); + // if (!bot->InBattleground() && distance > sPlayerbotAIConfig->reactDistance) + // return false; return IsMovingAllowed(); } @@ -893,9 +893,10 @@ bool MovementAction::IsMovingAllowed(uint32 mapId, float x, float y, float z) // removed sqrt as means distance limit was effectively 22500 (ReactDistance�) // leaving it commented incase we find ReactDistance limit causes problems // float distance = sqrt(bot->GetDistance(x, y, z)); - float distance = bot->GetDistance(x, y, z); - if (!bot->InBattleground() && distance > sPlayerbotAIConfig->reactDistance) - return false; + + // Remove react distance limit + // if (!bot->InBattleground()) + // return false; return IsMovingAllowed(); } @@ -923,6 +924,7 @@ bool MovementAction::IsWaitingForLastMove(MovementPriority priority) if (lastMove.lastdelayTime + lastMove.msTime > getMSTime()) return true; + return false; } diff --git a/src/strategy/actions/UseMeetingStoneAction.cpp b/src/strategy/actions/UseMeetingStoneAction.cpp index f0df16b8..0c0b7285 100644 --- a/src/strategy/actions/UseMeetingStoneAction.cpp +++ b/src/strategy/actions/UseMeetingStoneAction.cpp @@ -175,6 +175,12 @@ bool SummonAction::Teleport(Player* summoner, Player* player) // } // } // } + if (player->GetVehicle()) + { + botAI->TellError("You cannot summon me while I'm on a vehicle"); + return false; + } + if (!summoner->IsBeingTeleported() && !player->IsBeingTeleported()) { float followAngle = GetFollowAngle(); @@ -222,6 +228,7 @@ bool SummonAction::Teleport(Player* summoner, Player* player) } player->GetMotionMaster()->Clear(); + AI_VALUE(LastMovement&, "last movement").clear(); player->TeleportTo(mapId, x, y, z, 0); return true; } diff --git a/src/strategy/actions/VehicleActions.cpp b/src/strategy/actions/VehicleActions.cpp index 4683ba7a..13612a07 100644 --- a/src/strategy/actions/VehicleActions.cpp +++ b/src/strategy/actions/VehicleActions.cpp @@ -7,8 +7,11 @@ #include "BattlegroundIC.h" #include "ItemVisitors.h" +#include "ObjectDefines.h" #include "Playerbots.h" +#include "QuestValues.h" #include "ServerFacade.h" +#include "Unit.h" #include "Vehicle.h" // TODO methods to enter/exit vehicle should be added to BGTactics or MovementAction (so that we can better control @@ -20,6 +23,21 @@ bool EnterVehicleAction::Execute(Event event) // do not switch vehicles yet if (bot->GetVehicle()) return false; + + Player* master = botAI->GetMaster(); + // Triggered by a chat command + if (event.getOwner() && master && master->GetTarget()) + { + Unit* vehicleBase = botAI->GetUnit(master->GetTarget()); + if (!vehicleBase) + return false; + Vehicle* veh = vehicleBase->GetVehicleKit(); + if (vehicleBase->IsVehicle() && veh && veh->GetAvailableSeatCount()) + { + return EnterVehicle(vehicleBase, false); + } + return false; + } GuidVector npcs = AI_VALUE(GuidVector, "nearest vehicles"); for (GuidVector::iterator i = npcs.begin(); i != npcs.end(); i++) @@ -27,6 +45,9 @@ bool EnterVehicleAction::Execute(Event event) Unit* vehicleBase = botAI->GetUnit(*i); if (!vehicleBase) continue; + + if (vehicleBase->HasUnitFlag(UNIT_FLAG_NOT_SELECTABLE)) + continue; // dont let them get in the cannons as they'll stay forever and do nothing useful // dont let them in catapult they cant use them at all @@ -44,27 +65,36 @@ bool EnterVehicleAction::Execute(Event event) if (vehicleBase->GetVehicleKit()->IsVehicleInUse()) continue; - float dist = sServerFacade->GetDistance2d(bot, vehicleBase); - if (dist > 40.0f) - continue; - - if (dist > INTERACTION_DISTANCE) - return MoveTo(vehicleBase); - - bot->EnterVehicle(vehicleBase); - - if (!bot->IsOnVehicle(vehicleBase)) - return false; - - // dismount because bots can enter vehicle on mount - WorldPacket emptyPacket; - bot->GetSession()->HandleCancelMountAuraOpcode(emptyPacket); - return true; + if (EnterVehicle(vehicleBase, true)) + return true; } return false; } +bool EnterVehicleAction::EnterVehicle(Unit* vehicleBase, bool moveIfFar) +{ + float dist = sServerFacade->GetDistance2d(bot, vehicleBase); + if (dist > 40.0f) + return false; + + if (dist > INTERACTION_DISTANCE && !moveIfFar) + return false; + + if (dist > INTERACTION_DISTANCE) + return MoveTo(vehicleBase); + // Use HandleSpellClick instead of Unit::EnterVehicle to handle special vehicle script (ulduar) + vehicleBase->HandleSpellClick(bot); + + if (!bot->IsOnVehicle(vehicleBase)) + return false; + + // dismount because bots can enter vehicle on mount + WorldPacket emptyPacket; + bot->GetSession()->HandleCancelMountAuraOpcode(emptyPacket); + return true; +} + bool LeaveVehicleAction::Execute(Event event) { Vehicle* myVehicle = bot->GetVehicle(); diff --git a/src/strategy/actions/VehicleActions.h b/src/strategy/actions/VehicleActions.h index b48054c5..fbda01b8 100644 --- a/src/strategy/actions/VehicleActions.h +++ b/src/strategy/actions/VehicleActions.h @@ -17,6 +17,8 @@ public: EnterVehicleAction(PlayerbotAI* botAI, std::string const& name = "enter vehicle") : MovementAction(botAI, name) {} bool Execute(Event event) override; +protected: + bool EnterVehicle(Unit* vehicleBase, bool moveIfFar); }; class LeaveVehicleAction : public MovementAction diff --git a/src/strategy/generic/ChatCommandHandlerStrategy.cpp b/src/strategy/generic/ChatCommandHandlerStrategy.cpp index 58ee9549..c86e774b 100644 --- a/src/strategy/generic/ChatCommandHandlerStrategy.cpp +++ b/src/strategy/generic/ChatCommandHandlerStrategy.cpp @@ -60,6 +60,10 @@ void ChatCommandHandlerStrategy::InitTriggers(std::vector& trigger triggers.push_back( new TriggerNode("talk", NextAction::array(0, new NextAction("gossip hello", relevance), new NextAction("talk to quest giver", relevance), nullptr))); + triggers.push_back( + new TriggerNode("enter vehicle", NextAction::array(0, new NextAction("enter vehicle", relevance), nullptr))); + triggers.push_back( + new TriggerNode("leave vehicle", NextAction::array(0, new NextAction("leave vehicle", relevance), nullptr))); triggers.push_back( new TriggerNode("cast", NextAction::array(0, new NextAction("cast custom spell", relevance), nullptr))); triggers.push_back( diff --git a/src/strategy/raids/RaidStrategyContext.h b/src/strategy/raids/RaidStrategyContext.h index 2d610000..f0092302 100644 --- a/src/strategy/raids/RaidStrategyContext.h +++ b/src/strategy/raids/RaidStrategyContext.h @@ -1,9 +1,10 @@ #ifndef _PLAYERBOT_RAIDSTRATEGYCONTEXT_H_ #define _PLAYERBOT_RAIDSTRATEGYCONTEXT_H_ +#include "RaidUlduarStrategy.h" #include "Strategy.h" -#include "raids/blackwinglair/RaidBwlStrategy.h" -#include "raids/naxxramas/RaidNaxxStrategy.h" +#include "RaidBwlStrategy.h" +#include "RaidNaxxStrategy.h" class RaidStrategyContext : public NamedObjectContext { @@ -12,11 +13,13 @@ public: { creators["naxx"] = &RaidStrategyContext::naxx; creators["bwl"] = &RaidStrategyContext::bwl; + creators["uld"] = &RaidStrategyContext::uld; } private: static Strategy* naxx(PlayerbotAI* botAI) { return new RaidNaxxStrategy(botAI); } static Strategy* bwl(PlayerbotAI* botAI) { return new RaidBwlStrategy(botAI); } + static Strategy* uld(PlayerbotAI* botAI) { return new RaidUlduarStrategy(botAI); } }; #endif \ No newline at end of file diff --git a/src/strategy/raids/ulduar/RaidUlduarActionContext.h b/src/strategy/raids/ulduar/RaidUlduarActionContext.h index 591e8fbd..ddcac039 100644 --- a/src/strategy/raids/ulduar/RaidUlduarActionContext.h +++ b/src/strategy/raids/ulduar/RaidUlduarActionContext.h @@ -13,9 +13,15 @@ class RaidUlduarActionContext : public NamedObjectContext { public: - RaidUlduarActionContext() {} + RaidUlduarActionContext() + { + creators["flame leviathan vehicle"] = &RaidUlduarActionContext::flame_leviathan_vehicle; + creators["flame leviathan enter vehicle"] = &RaidUlduarActionContext::flame_leviathan_enter_vehicle; + } private: + static Action* flame_leviathan_vehicle(PlayerbotAI* ai) { return new FlameLeviathanVehicleAction(ai); } + static Action* flame_leviathan_enter_vehicle(PlayerbotAI* ai) { return new FlameLeviathanEnterVehicleAction(ai); } }; #endif \ No newline at end of file diff --git a/src/strategy/raids/ulduar/RaidUlduarActions.cpp b/src/strategy/raids/ulduar/RaidUlduarActions.cpp index 66828cae..2a230ca6 100644 --- a/src/strategy/raids/ulduar/RaidUlduarActions.cpp +++ b/src/strategy/raids/ulduar/RaidUlduarActions.cpp @@ -1,27 +1,376 @@ #include "RaidUlduarActions.h" +#include + +#include "DBCEnums.h" +#include "GameObject.h" +#include "LastMovementValue.h" +#include "ObjectDefines.h" #include "ObjectGuid.h" #include "PlayerbotAIConfig.h" #include "Playerbots.h" +#include "Position.h" #include "RaidUlduarBossHelper.h" +#include "RaidUlduarScripts.h" #include "RaidUlduarStrategy.h" #include "ScriptedCreature.h" +#include "ServerFacade.h" #include "SharedDefines.h" +#include "Unit.h" +#include "Vehicle.h" -uint32 RotateAroundTheCenterPointAction::FindNearestWaypoint() +const std::vector availableVehicles = {NPC_VEHICLE_CHOPPER, NPC_SALVAGED_DEMOLISHER, + NPC_SALVAGED_DEMOLISHER_TURRET, NPC_SALVAGED_SIEGE_ENGINE, + NPC_SALVAGED_SIEGE_ENGINE_TURRET}; + +const std::vector corners = { + {183.53f, 66.53f, 409.80f}, {383.03f, 75.10f, 411.71f}, {379.74f, -133.05f, 410.88f}, {158.67f, -137.54f, 409.80f}}; + +bool FlameLeviathanVehicleAction::Execute(Event event) { - float minDistance = 0; - int ret = -1; - for (int i = 0; i < intervals; i++) + vehicleBase_ = bot->GetVehicleBase(); + vehicle_ = bot->GetVehicle(); + if (!vehicleBase_ || !vehicle_) + return false; + + GuidVector attackers = context->GetValue("attackers")->Get(); + Unit* target = nullptr; + Unit* flame = nullptr; + for (auto i = attackers.begin(); i != attackers.end(); ++i) { - float w_x = waypoints[i].first, w_y = waypoints[i].second; - float dis = bot->GetDistance2d(w_x, w_y); - if (ret == -1 || dis < minDistance) + Unit* unit = botAI->GetUnit(*i); + if (!unit) + continue; + if (unit->GetEntry() == 33139) // Flame Leviathan Turret + continue; + if (unit->GetEntry() == 33142) // Leviathan Defense Turret + continue; + if (unit->GetEntry() == 33133) // Flame Leviathan + flame = unit; + if (!target || bot->GetExactDist(target) > bot->GetExactDist(unit)) { - ret = i; - minDistance = dis; + target = unit; } } - return ret; + if (!target) + return false; + + // Flame Leviathan is chasing me + if (flame && flame->GetVictim() == vehicleBase_) + if (MoveAvoidChasing(target)) + return true; + + uint32 entry = vehicleBase_->GetEntry(); + switch (entry) + { + case NPC_SALVAGED_DEMOLISHER: + return DemolisherAction(target); + case NPC_SALVAGED_DEMOLISHER_TURRET: + return DemolisherTurretAction(target); + case NPC_SALVAGED_SIEGE_ENGINE: + return SiegeEngineAction(target); + case NPC_SALVAGED_SIEGE_ENGINE_TURRET: + return SiegeEngineTurretAction(target); + case NPC_VEHICLE_CHOPPER: + return ChopperAction(target); + default: + break; + } + return false; +} + +bool FlameLeviathanVehicleAction::MoveAvoidChasing(Unit* target) +{ + if (avoidChaseIdx == -1) + { + for (int i = 0; i < corners.size(); i++) + { + if (bot->GetExactDist(corners[i]) > target->GetExactDist(corners[i])) + continue; + if (avoidChaseIdx == -1 || bot->GetExactDist(corners[i]) > bot->GetExactDist(corners[avoidChaseIdx])) + avoidChaseIdx = i; + } + if (avoidChaseIdx == -1) + avoidChaseIdx = 0; + } + else + { + if (bot->GetExactDist(corners[avoidChaseIdx]) < 5.0f && target->GetExactDist(bot) < 50.0f) + avoidChaseIdx = (avoidChaseIdx + 1) % corners.size(); + } + const Position& to = corners[avoidChaseIdx]; + return MoveTo(bot->GetMap()->GetId(), to.GetPositionX(), to.GetPositionY(), to.GetPositionZ(), false, false, false, + false, MovementPriority::MOVEMENT_COMBAT); +} + +bool FlameLeviathanVehicleAction::DemolisherAction(Unit* target) +{ + Aura* bluePyrite = target->GetAura(68605); + if (!bluePyrite || (bluePyrite->GetStackAmount() <= 6 && vehicleBase_->GetPower(POWER_ENERGY) > 25) || bluePyrite->GetDuration() <= 5000) + { + uint32 spellId = 62490; + if (botAI->CanCastVehicleSpell(spellId, target)) + if (botAI->CastVehicleSpell(spellId, target)) + { + vehicleBase_->AddSpellCooldown(spellId, 0, 1000); + return true; + } + } + uint32 spellId = 62306; + if (botAI->CanCastVehicleSpell(spellId, target)) + if (botAI->CastVehicleSpell(spellId, target)) + { + vehicleBase_->AddSpellCooldown(spellId, 0, 1000); + return true; + } + return false; +} + +bool FlameLeviathanVehicleAction::DemolisherTurretAction(Unit* target) +{ + { + GuidVector npcs = AI_VALUE(GuidVector, "nearest npcs"); + for (auto i = npcs.begin(); i != npcs.end(); i++) + { + Unit* unit = botAI->GetUnit(*i); + if (!unit) + continue; + if (unit->GetEntry() == 33189 && vehicleBase_->GetPower(POWER_ENERGY) <= 25) // Liquid Pyrite + { + uint32 spellId = 62479; + if (botAI->CanCastVehicleSpell(spellId, unit)) + if (botAI->CastVehicleSpell(spellId, unit)) + { + vehicleBase_->AddSpellCooldown(spellId, 0, 1000); + return true; + } + } + } + } + { + GuidVector targets = AI_VALUE(GuidVector, "possible targets"); + for (auto i = targets.begin(); i != targets.end(); i++) + { + Unit* unit = botAI->GetUnit(*i); + if (!unit) + continue; + if (unit->GetEntry() == 33214) // Mechanolift 304-A + { + uint32 spellId = 64979; + if (botAI->CanCastVehicleSpell(spellId, unit)) + if (botAI->CastVehicleSpell(spellId, unit)) + { + vehicleBase_->AddSpellCooldown(spellId, 0, 1000); + return true; + } + } + } + } + uint32 spellId = 62634; + if (botAI->CanCastVehicleSpell(spellId, target)) + if (botAI->CastVehicleSpell(spellId, target)) + { + vehicleBase_->AddSpellCooldown(spellId, 0, 1000); + return true; + } + return false; +} + +bool FlameLeviathanVehicleAction::SiegeEngineAction(Unit* target) +{ + if (target->GetCurrentSpell(CURRENT_CHANNELED_SPELL) || target->HasAura(62396)) + { + uint32 spellId = 62522; + if (botAI->CanCastVehicleSpell(spellId, target)) + if (botAI->CastVehicleSpell(spellId, target)) + { + vehicleBase_->AddSpellCooldown(spellId, 0, 10000); + return true; + } + } + uint32 spellId = 62345; + if (vehicleBase_->GetPower(POWER_ENERGY) >= 80 && botAI->CanCastVehicleSpell(spellId, target)) + if (botAI->CastVehicleSpell(spellId, target)) + { + vehicleBase_->AddSpellCooldown(spellId, 0, 1000); + return true; + } + return false; +} + +bool FlameLeviathanVehicleAction::SiegeEngineTurretAction(Unit* target) +{ + uint32 spellId = 62358; + if (botAI->CanCastVehicleSpell(spellId, target)) + if (botAI->CastVehicleSpell(spellId, target)) + { + vehicleBase_->AddSpellCooldown(spellId, 0, 1000); + return true; + } + return false; +} + +bool FlameLeviathanVehicleAction::ChopperAction(Unit* target) +{ + uint32 spellId = 62286; + if (botAI->CanCastVehicleSpell(spellId, target)) + if (botAI->CastVehicleSpell(spellId, target)) + { + vehicleBase_->AddSpellCooldown(spellId, 0, 15000); + return true; + } + spellId = 62974; + if (botAI->CanCastVehicleSpell(spellId, target)) + if (botAI->CastVehicleSpell(spellId, target)) + { + vehicleBase_->AddSpellCooldown(spellId, 0, 1000); + return true; + } + return false; +} + +bool FlameLeviathanEnterVehicleAction::Execute(Event event) +{ + // do not switch vehicles yet + if (bot->GetVehicle()) + return false; + Unit* vehicleToEnter = nullptr; + GuidVector npcs = AI_VALUE(GuidVector, "nearest vehicles far"); + for (GuidVector::iterator i = npcs.begin(); i != npcs.end(); i++) + { + Unit* vehicleBase = botAI->GetUnit(*i); + if (!vehicleBase) + continue; + + if (vehicleBase->HasUnitFlag(UNIT_FLAG_NOT_SELECTABLE)) + continue; + + if (!ShouldEnter(vehicleBase)) + continue; + + if (!vehicleToEnter || bot->GetExactDist(vehicleToEnter) > bot->GetExactDist(vehicleBase)) + vehicleToEnter = vehicleBase; + } + + if (!vehicleToEnter) + return false; + + if (EnterVehicle(vehicleToEnter, true)) + return true; + + return false; +} + +bool FlameLeviathanEnterVehicleAction::EnterVehicle(Unit* vehicleBase, bool moveIfFar) +{ + float dist = bot->GetDistance(vehicleBase); + + if (dist > INTERACTION_DISTANCE && !moveIfFar) + return false; + + if (dist > INTERACTION_DISTANCE) + return MoveTo(vehicleBase); + + botAI->RemoveShapeshift(); + // Use HandleSpellClick instead of Unit::EnterVehicle to handle special vehicle script (ulduar) + vehicleBase->HandleSpellClick(bot); + + if (!bot->IsOnVehicle(vehicleBase)) + return false; + + // dismount because bots can enter vehicle on mount + WorldPacket emptyPacket; + bot->GetSession()->HandleCancelMountAuraOpcode(emptyPacket); + return true; +} + +bool FlameLeviathanEnterVehicleAction::ShouldEnter(Unit* target) +{ + Vehicle* vehicleKit = target->GetVehicleKit(); + if (!vehicleKit) + return false; + + bool isMelee = botAI->IsMelee(bot); + bool allMain = AllMainVehiclesOnUse(); + bool inUse = vehicleKit->IsVehicleInUse(); + int32 entry = target->GetEntry(); + if (entry != NPC_SALVAGED_DEMOLISHER && entry != NPC_SALVAGED_SIEGE_ENGINE && entry != NPC_VEHICLE_CHOPPER) + return false; + // two phase enter (make all main vehicles in use -> next player enter) + if (!allMain) + { + if (inUse) + return false; + if (entry != NPC_SALVAGED_DEMOLISHER && entry != NPC_SALVAGED_SIEGE_ENGINE) + return false; + if (entry == NPC_SALVAGED_DEMOLISHER && isMelee) + return false; + if (entry == NPC_SALVAGED_SIEGE_ENGINE && !isMelee) + return false; + return true; + } + + if (!vehicleKit->GetAvailableSeatCount()) + return false; + + // do not enter useless seat + if (entry == NPC_SALVAGED_SIEGE_ENGINE) + { + Unit* turret = vehicleKit->GetPassenger(7); + if (!turret) + return false; + Vehicle* turretVehicle = turret->GetVehicleKit(); + if (!turretVehicle) + return false; + if (turretVehicle->IsVehicleInUse()) + return false; + return true; + } + + if (entry == NPC_SALVAGED_DEMOLISHER) + { + if (vehicleKit->GetPassenger(0)) + { + Unit* target2 = vehicleKit->GetPassenger(1); + if (!target2) + return false; + Vehicle* vehicle2 = target2->GetVehicleKit(); + if (!vehicle2) + return false; + if (vehicle2->GetPassenger(0)) + return false; + } + return true; + } + + if (entry == NPC_VEHICLE_CHOPPER && vehicleKit->GetAvailableSeatCount() <= 1) + return false; + + return true; +} + +bool FlameLeviathanEnterVehicleAction::AllMainVehiclesOnUse() +{ + Group* group = bot->GetGroup(); + if (!group) + return false; + int demolisher = 0; + int siege = 0; + for (GroupReference* gref = group->GetFirstMember(); gref; gref = gref->next()) + { + Player* player = gref->GetSource(); + if (!player) + continue; + Unit* vehicleBase = player->GetVehicleBase(); + if (!vehicleBase) + continue; + if (vehicleBase->GetEntry() == NPC_SALVAGED_DEMOLISHER) + ++demolisher; + else if (vehicleBase->GetEntry() == NPC_SALVAGED_SIEGE_ENGINE) + ++siege; + } + Difficulty diff = bot->GetRaidDifficulty(); + int maxC = (diff == RAID_DIFFICULTY_10MAN_NORMAL || diff == RAID_DIFFICULTY_10MAN_HEROIC) ? 2 : 5; + return demolisher >= maxC && siege >= maxC; } \ No newline at end of file diff --git a/src/strategy/raids/ulduar/RaidUlduarActions.h b/src/strategy/raids/ulduar/RaidUlduarActions.h index c2fec2d8..0019ff8a 100644 --- a/src/strategy/raids/ulduar/RaidUlduarActions.h +++ b/src/strategy/raids/ulduar/RaidUlduarActions.h @@ -4,20 +4,42 @@ #include "Action.h" #include "AttackAction.h" #include "GenericActions.h" +#include "GenericSpellActions.h" #include "MovementActions.h" #include "PlayerbotAI.h" #include "Playerbots.h" #include "RaidUlduarBossHelper.h" -#include "RaidUlduarScripts.h" +#include "Vehicle.h" -// just for test -// class TryToGetBossAIAction : public Action -// { -// public: -// TryToGetBossAIAction(PlayerbotAI* ai) : Action(ai, "try to get boss ai") {} +class FlameLeviathanVehicleAction : public MovementAction +{ +public: + FlameLeviathanVehicleAction(PlayerbotAI* botAI) : MovementAction(botAI, "flame leviathan vehicle") {} + bool Execute(Event event) override; -// public: -// virtual bool Execute(Event event); -// }; +protected: + bool MoveAvoidChasing(Unit* target); + bool DemolisherAction(Unit* target); + bool DemolisherTurretAction(Unit* target); + bool SiegeEngineAction(Unit* target); + bool SiegeEngineTurretAction(Unit* target); + bool ChopperAction(Unit* target); + Unit* GetAttacker(); + Unit* vehicleBase_; + Vehicle* vehicle_; + int avoidChaseIdx = -1; +}; + +class FlameLeviathanEnterVehicleAction : public MovementAction +{ +public: + FlameLeviathanEnterVehicleAction(PlayerbotAI* botAI) : MovementAction(botAI, "flame leviathan enter vehicle") {} + bool Execute(Event event); + +protected: + bool EnterVehicle(Unit* vehicleBase, bool moveIfFar); + bool ShouldEnter(Unit* vehicleBase); + bool AllMainVehiclesOnUse(); +}; #endif \ No newline at end of file diff --git a/src/strategy/raids/ulduar/RaidUlduarBossHelper.h b/src/strategy/raids/ulduar/RaidUlduarBossHelper.h index a1585ade..3580444d 100644 --- a/src/strategy/raids/ulduar/RaidUlduarBossHelper.h +++ b/src/strategy/raids/ulduar/RaidUlduarBossHelper.h @@ -11,73 +11,72 @@ #include "Player.h" #include "PlayerbotAI.h" #include "Playerbots.h" -#include "RaidUlduarScripts.h" #include "ScriptedCreature.h" #include "SharedDefines.h" const uint32 ULDUAR_MAP_ID = 603; -template -class GenericBossHelper : public AiObject -{ -public: - GenericBossHelper(PlayerbotAI* botAI, std::string name) : AiObject(botAI), _name(name) {} - virtual bool UpdateBossAI() - { - if (!bot->IsInCombat()) - { - _unit = nullptr; - } - if (_unit && (!_unit->IsInWorld() || !_unit->IsAlive())) - { - _unit = nullptr; - } - if (!_unit) - { - _unit = AI_VALUE2(Unit*, "find target", _name); - if (!_unit) - { - return false; - } - _target = _unit->ToCreature(); - if (!_target) - { - return false; - } - _ai = dynamic_cast(_target->GetAI()); - if (!_ai) - { - return false; - } - _event_map = &_ai->events; - if (!_event_map) - { - return false; - } - } - if (!_event_map) - { - return false; - } - _timer = _event_map->GetTimer(); - return true; - } - virtual void Reset() - { - _unit = nullptr; - _target = nullptr; - _ai = nullptr; - _event_map = nullptr; - _timer = 0; - } +// template +// class GenericBossHelper : public AiObject +// { +// public: +// GenericBossHelper(PlayerbotAI* botAI, std::string name) : AiObject(botAI), _name(name) {} +// virtual bool UpdateBossAI() +// { +// if (!bot->IsInCombat()) +// { +// _unit = nullptr; +// } +// if (_unit && (!_unit->IsInWorld() || !_unit->IsAlive())) +// { +// _unit = nullptr; +// } +// if (!_unit) +// { +// _unit = AI_VALUE2(Unit*, "find target", _name); +// if (!_unit) +// { +// return false; +// } +// _target = _unit->ToCreature(); +// if (!_target) +// { +// return false; +// } +// _ai = dynamic_cast(_target->GetAI()); +// if (!_ai) +// { +// return false; +// } +// _event_map = &_ai->events; +// if (!_event_map) +// { +// return false; +// } +// } +// if (!_event_map) +// { +// return false; +// } +// _timer = _event_map->GetTimer(); +// return true; +// } +// virtual void Reset() +// { +// _unit = nullptr; +// _target = nullptr; +// _ai = nullptr; +// _event_map = nullptr; +// _timer = 0; +// } -protected: - std::string _name; - Unit* _unit = nullptr; - Creature* _target = nullptr; - BossAiType* _ai = nullptr; - EventMap* _event_map = nullptr; - uint32 _timer = 0; -}; +// protected: +// std::string _name; +// Unit* _unit = nullptr; +// Creature* _target = nullptr; +// BossAiType* _ai = nullptr; +// EventMap* _event_map = nullptr; +// uint32 _timer = 0; +// }; #endif diff --git a/src/strategy/raids/ulduar/RaidUlduarMultipliers.cpp b/src/strategy/raids/ulduar/RaidUlduarMultipliers.cpp index 760e3547..16df9c9a 100644 --- a/src/strategy/raids/ulduar/RaidUlduarMultipliers.cpp +++ b/src/strategy/raids/ulduar/RaidUlduarMultipliers.cpp @@ -19,3 +19,10 @@ #include "ShamanActions.h" #include "UseMeetingStoneAction.h" #include "WarriorActions.h" + +float FlameLeviathanMultiplier::GetValue(Action* action) +{ + // if (dynamic_cast(action)) + // return 0.0f; + return 1.0f; +} \ No newline at end of file diff --git a/src/strategy/raids/ulduar/RaidUlduarMultipliers.h b/src/strategy/raids/ulduar/RaidUlduarMultipliers.h index 76ed53c3..aef41be5 100644 --- a/src/strategy/raids/ulduar/RaidUlduarMultipliers.h +++ b/src/strategy/raids/ulduar/RaidUlduarMultipliers.h @@ -5,4 +5,13 @@ #include "Multiplier.h" #include "raids/ulduar/RaidUlduarBossHelper.h" +class FlameLeviathanMultiplier : public Multiplier +{ +public: + FlameLeviathanMultiplier(PlayerbotAI* ai) : Multiplier(ai, "flame leviathan") {} + +public: + virtual float GetValue(Action* action); +}; + #endif \ No newline at end of file diff --git a/src/strategy/raids/ulduar/RaidUlduarScripts.h b/src/strategy/raids/ulduar/RaidUlduarScripts.h index a5085309..404b0dbd 100644 --- a/src/strategy/raids/ulduar/RaidUlduarScripts.h +++ b/src/strategy/raids/ulduar/RaidUlduarScripts.h @@ -3,5 +3,6 @@ // There are no header files for bosses in Ulduar directory //#include "../../../../src/server/scripts/Northrend/Ulduar/Ulduar/" +#include "../../../../src/server/scripts/Northrend/Ulduar/Ulduar/ulduar.h" #endif diff --git a/src/strategy/raids/ulduar/RaidUlduarStrategy.cpp b/src/strategy/raids/ulduar/RaidUlduarStrategy.cpp index c0693cdc..0c6ed182 100644 --- a/src/strategy/raids/ulduar/RaidUlduarStrategy.cpp +++ b/src/strategy/raids/ulduar/RaidUlduarStrategy.cpp @@ -2,6 +2,20 @@ #include "RaidUlduarMultipliers.h" -void RaidUlduarStrategy::InitTriggers(std::vector& triggers) {} +void RaidUlduarStrategy::InitTriggers(std::vector& triggers) +{ + // Flame Leviathan + triggers.push_back(new TriggerNode( + "flame leviathan vehicle near", + NextAction::array(0, new NextAction("flame leviathan enter vehicle", ACTION_RAID + 2), nullptr))); -void RaidUlduarStrategy::InitMultipliers(std::vector& multipliers) {} + triggers.push_back(new TriggerNode( + "flame leviathan on vehicle", + NextAction::array(0, new NextAction("flame leviathan vehicle", ACTION_RAID + 1), nullptr))); + +} + +void RaidUlduarStrategy::InitMultipliers(std::vector& multipliers) +{ + multipliers.push_back(new FlameLeviathanMultiplier(botAI)); +} diff --git a/src/strategy/raids/ulduar/RaidUlduarStrategy.h b/src/strategy/raids/ulduar/RaidUlduarStrategy.h index e523d4b6..6538845c 100644 --- a/src/strategy/raids/ulduar/RaidUlduarStrategy.h +++ b/src/strategy/raids/ulduar/RaidUlduarStrategy.h @@ -4,14 +4,13 @@ #include "AiObjectContext.h" #include "Multiplier.h" -#include "RaidUlduarScripts.h" #include "Strategy.h" class RaidUlduarStrategy : public Strategy { public: RaidUlduarStrategy(PlayerbotAI* ai) : Strategy(ai) {} - virtual std::string const getName() override { return "ulduar"; } + virtual std::string const getName() override { return "uld"; } virtual void InitTriggers(std::vector& triggers) override; virtual void InitMultipliers(std::vector& multipliers) override; }; diff --git a/src/strategy/raids/ulduar/RaidUlduarTriggerContext.h b/src/strategy/raids/ulduar/RaidUlduarTriggerContext.h index aed35cf8..22c0f044 100644 --- a/src/strategy/raids/ulduar/RaidUlduarTriggerContext.h +++ b/src/strategy/raids/ulduar/RaidUlduarTriggerContext.h @@ -13,9 +13,15 @@ class RaidUlduarTriggerContext : public NamedObjectContext { public: - RaidUlduarTriggerContext() {} + RaidUlduarTriggerContext() + { + creators["flame leviathan on vehicle"] = &RaidUlduarTriggerContext::flame_leviathan_on_vehicle; + creators["flame leviathan vehicle near"] = &RaidUlduarTriggerContext::flame_leviathan_vehicle_near; + } private: + static Trigger* flame_leviathan_on_vehicle(PlayerbotAI* ai) { return new FlameLeviathanOnVehicleTrigger(ai); } + static Trigger* flame_leviathan_vehicle_near(PlayerbotAI* ai) { return new FlameLeviathanVehicleNearTrigger(ai); } }; -#endif \ No newline at end of file +#endif diff --git a/src/strategy/raids/ulduar/RaidUlduarTriggers.cpp b/src/strategy/raids/ulduar/RaidUlduarTriggers.cpp index 8ee3d799..b231f716 100644 --- a/src/strategy/raids/ulduar/RaidUlduarTriggers.cpp +++ b/src/strategy/raids/ulduar/RaidUlduarTriggers.cpp @@ -1,6 +1,45 @@ #include "RaidUlduarTriggers.h" #include "EventMap.h" +#include "Object.h" #include "Playerbots.h" +#include "RaidUlduarScripts.h" #include "ScriptedCreature.h" +#include "SharedDefines.h" #include "Trigger.h" +#include "Vehicle.h" + +const std::vector availableVehicles = {NPC_VEHICLE_CHOPPER, NPC_SALVAGED_DEMOLISHER, + NPC_SALVAGED_DEMOLISHER_TURRET, NPC_SALVAGED_SIEGE_ENGINE, + NPC_SALVAGED_SIEGE_ENGINE_TURRET}; + +bool FlameLeviathanOnVehicleTrigger::IsActive() +{ + Unit* vehicleBase = bot->GetVehicleBase(); + Vehicle* vehicle = bot->GetVehicle(); + if (!vehicleBase || !vehicle) + return false; + + uint32 entry = vehicleBase->GetEntry(); + for (uint32 comp : availableVehicles) + { + if (entry == comp) + return true; + } + return false; +} + +bool FlameLeviathanVehicleNearTrigger::IsActive() +{ + if (bot->GetVehicle()) + return false; + + Player* master = botAI->GetMaster(); + if (!master) + return false; + + if (!master->GetVehicle()) + return false; + + return true; +} diff --git a/src/strategy/raids/ulduar/RaidUlduarTriggers.h b/src/strategy/raids/ulduar/RaidUlduarTriggers.h index 7337706a..89b784cf 100644 --- a/src/strategy/raids/ulduar/RaidUlduarTriggers.h +++ b/src/strategy/raids/ulduar/RaidUlduarTriggers.h @@ -5,7 +5,21 @@ #include "GenericTriggers.h" #include "PlayerbotAIConfig.h" #include "RaidUlduarBossHelper.h" -#include "RaidUlduarScripts.h" #include "Trigger.h" + +class FlameLeviathanOnVehicleTrigger : public Trigger +{ +public: + FlameLeviathanOnVehicleTrigger(PlayerbotAI* ai) : Trigger(ai, "flame leviathan on vehicle") {} + bool IsActive() override; +}; + +class FlameLeviathanVehicleNearTrigger : public Trigger +{ +public: + FlameLeviathanVehicleNearTrigger(PlayerbotAI* ai) : Trigger(ai, "flame leviathan vehicle near") {} + bool IsActive() override; +}; + #endif \ No newline at end of file diff --git a/src/strategy/triggers/ChatTriggerContext.h b/src/strategy/triggers/ChatTriggerContext.h index 7def8b14..f146e08f 100644 --- a/src/strategy/triggers/ChatTriggerContext.h +++ b/src/strategy/triggers/ChatTriggerContext.h @@ -77,6 +77,8 @@ public: creators["grind"] = &ChatTriggerContext::grind; creators["tank attack"] = &ChatTriggerContext::tank_attack; creators["talk"] = &ChatTriggerContext::talk; + creators["enter vehicle"] = &ChatTriggerContext::enter_vehicle; + creators["leave vehicle"] = &ChatTriggerContext::leave_vehicle; creators["cast"] = &ChatTriggerContext::cast; creators["castnc"] = &ChatTriggerContext::castnc; creators["invite"] = &ChatTriggerContext::invite; @@ -155,6 +157,8 @@ private: static Trigger* cast(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "cast"); } static Trigger* castnc(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "castnc"); } static Trigger* talk(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "talk"); } + static Trigger* enter_vehicle(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "enter vehicle"); } + static Trigger* leave_vehicle(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "leave vehicle"); } static Trigger* flee(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "flee"); } static Trigger* grind(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "grind"); } static Trigger* tank_attack(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "tank attack"); } diff --git a/src/strategy/values/ValueContext.h b/src/strategy/values/ValueContext.h index 3f3e4f3a..6fb93fdf 100644 --- a/src/strategy/values/ValueContext.h +++ b/src/strategy/values/ValueContext.h @@ -106,6 +106,7 @@ public: creators["nearest npcs"] = &ValueContext::nearest_npcs; creators["nearest totems"] = &ValueContext::nearest_totems; creators["nearest vehicles"] = &ValueContext::nearest_vehicles; + creators["nearest vehicles far"] = &ValueContext::nearest_vehicles_far; creators["nearest friendly players"] = &ValueContext::nearest_friendly_players; creators["closest friendly players"] = &ValueContext::closest_friendly_players; creators["nearest enemy players"] = &ValueContext::nearest_enemy_players; @@ -394,6 +395,7 @@ private: static UntypedValue* nearest_npcs(PlayerbotAI* botAI) { return new NearestNpcsValue(botAI); } static UntypedValue* nearest_totems(PlayerbotAI* botAI) { return new NearestTotemsValue(botAI); } static UntypedValue* nearest_vehicles(PlayerbotAI* botAI) { return new NearestVehiclesValue(botAI); } + static UntypedValue* nearest_vehicles_far(PlayerbotAI* botAI) { return new NearestVehiclesValue(botAI, 200.0f); } static UntypedValue* nearest_friendly_players(PlayerbotAI* botAI) { return new NearestFriendlyPlayersValue(botAI); } static UntypedValue* closest_friendly_players(PlayerbotAI* botAI) {