From 6c93cbdf784d937e9c50165458f3416b531b45cf Mon Sep 17 00:00:00 2001 From: mostlikely4r Date: Thu, 2 Feb 2023 18:32:45 +0100 Subject: [PATCH 01/26] -Build fix: Proper parh wotlk core. --- .github/workflows/cmangos-wotlk-windows-release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/cmangos-wotlk-windows-release.yml b/.github/workflows/cmangos-wotlk-windows-release.yml index 84e63a714..e65696ada 100644 --- a/.github/workflows/cmangos-wotlk-windows-release.yml +++ b/.github/workflows/cmangos-wotlk-windows-release.yml @@ -36,7 +36,7 @@ jobs: uses: actions/checkout@v2 with: ref: ${{env.CORE_BRANCH}} - repository: ${{env.CORE_REPO_OWNER}}mangos-wotlk + repository: ${{env.CORE_REPO_OWNER}}/mangos-wotlk path: ${{env.REPO_DIR}} # submodules: recursive From 04fb9b6873b11f2247cb1c0e9f4b56d3fb48d6c0 Mon Sep 17 00:00:00 2001 From: mostlikely4r Date: Fri, 3 Feb 2023 15:52:50 +0100 Subject: [PATCH 02/26] -Druid fix: Stop druids jumping out of bear/cat form to cast a spell only to jump back in before casting it. --- playerbot/strategy/druid/BearTankDruidStrategy.cpp | 2 +- playerbot/strategy/druid/FeralDruidStrategy.cpp | 2 +- .../strategy/druid/GenericDruidNonCombatStrategy.cpp | 8 ++++---- playerbot/strategy/druid/MeleeDruidStrategy.cpp | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/playerbot/strategy/druid/BearTankDruidStrategy.cpp b/playerbot/strategy/druid/BearTankDruidStrategy.cpp index ebc56aabc..c68460d39 100644 --- a/playerbot/strategy/druid/BearTankDruidStrategy.cpp +++ b/playerbot/strategy/druid/BearTankDruidStrategy.cpp @@ -139,7 +139,7 @@ void BearTankDruidStrategy::InitCombatTriggers(std::list &triggers triggers.push_back(new TriggerNode( "thorns", - NextAction::array(0, new NextAction("thorns", ACTION_HIGH + 9), NULL))); + NextAction::array(0, new NextAction("thorns", 66.0f), NULL))); //High prio to stop going back to bear form. triggers.push_back(new TriggerNode( "bear form", diff --git a/playerbot/strategy/druid/FeralDruidStrategy.cpp b/playerbot/strategy/druid/FeralDruidStrategy.cpp index 5b47d237c..6d7520678 100644 --- a/playerbot/strategy/druid/FeralDruidStrategy.cpp +++ b/playerbot/strategy/druid/FeralDruidStrategy.cpp @@ -97,5 +97,5 @@ void FeralDruidStrategy::InitCombatTriggers(std::list &triggers) triggers.push_back(new TriggerNode( "omen of clarity", - NextAction::array(0, new NextAction("omen of clarity", ACTION_HIGH + 6), NULL))); + NextAction::array(0, new NextAction("omen of clarity", 66.0f), NULL))); //High priority to avoid going back to cat form. } \ No newline at end of file diff --git a/playerbot/strategy/druid/GenericDruidNonCombatStrategy.cpp b/playerbot/strategy/druid/GenericDruidNonCombatStrategy.cpp index 2d10ef46f..8cff43fbb 100644 --- a/playerbot/strategy/druid/GenericDruidNonCombatStrategy.cpp +++ b/playerbot/strategy/druid/GenericDruidNonCombatStrategy.cpp @@ -20,28 +20,28 @@ class GenericDruidNonCombatStrategyActionNodeFactory : public NamedObjectFactory static ActionNode* thorns(PlayerbotAI* ai) { return new ActionNode("thorns", - /*P*/ NULL, //NextAction::array(0, new NextAction("caster form"), NULL), + /*P*/ NextAction::array(0, new NextAction("caster form"), NULL), /*A*/ NULL, /*C*/ NULL); } static ActionNode* thorns_on_party(PlayerbotAI* ai) { return new ActionNode("thorns on party", - /*P*/ NULL, //NextAction::array(0, new NextAction("caster form"), NULL), + /*P*/ NextAction::array(0, new NextAction("caster form"), NULL), /*A*/ NULL, /*C*/ NULL); } static ActionNode* mark_of_the_wild(PlayerbotAI* ai) { return new ActionNode ("mark of the wild", - /*P*/ NULL, //NextAction::array(0, new NextAction("caster form"), NULL), + /*P*/ NextAction::array(0, new NextAction("caster form"), NULL), /*A*/ NULL, /*C*/ NULL); } static ActionNode* mark_of_the_wild_on_party(PlayerbotAI* ai) { return new ActionNode ("mark of the wild on party", - /*P*/ NULL, //NextAction::array(0, new NextAction("caster form"), NULL), + /*P*/ NextAction::array(0, new NextAction("caster form"), NULL), /*A*/ NULL, /*C*/ NULL); } diff --git a/playerbot/strategy/druid/MeleeDruidStrategy.cpp b/playerbot/strategy/druid/MeleeDruidStrategy.cpp index c0bd79a0c..e62079b54 100644 --- a/playerbot/strategy/druid/MeleeDruidStrategy.cpp +++ b/playerbot/strategy/druid/MeleeDruidStrategy.cpp @@ -23,5 +23,5 @@ void MeleeDruidStrategy::InitCombatTriggers(std::list &triggers) triggers.push_back(new TriggerNode( "omen of clarity", - NextAction::array(0, new NextAction("omen of clarity", ACTION_HIGH + 9), NULL))); + NextAction::array(0, new NextAction("omen of clarity", 66.0f), NULL))); //High priority to avoid druid going back to bear/cat form. } From 19470a69d8015045aa35323df3489b43d5930125 Mon Sep 17 00:00:00 2001 From: mostlikely4r Date: Fri, 3 Feb 2023 15:54:15 +0100 Subject: [PATCH 03/26] -Movement fix: Stop bots moving back and forth between master corpse and master ghost. --- playerbot/strategy/actions/MovementActions.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/playerbot/strategy/actions/MovementActions.cpp b/playerbot/strategy/actions/MovementActions.cpp index e46297924..2684caf7f 100644 --- a/playerbot/strategy/actions/MovementActions.cpp +++ b/playerbot/strategy/actions/MovementActions.cpp @@ -1235,6 +1235,7 @@ bool MovementAction::Follow(Unit* target, float distance, float angle) if(botPos.fDist(cPos) > sPlayerbotAIConfig.spellDistance) return MoveTo(cPos.getMapId(),cPos.getX(),cPos.getY(), cPos.getZ()); + return false; } } From 0291d42d212d1f285750ce1427524f16a1c32422 Mon Sep 17 00:00:00 2001 From: mostlikely4r Date: Fri, 3 Feb 2023 15:55:13 +0100 Subject: [PATCH 04/26] -Druid fix: Stop druids from jumping out of travel form to cast a spell only to move back into travel form. --- playerbot/strategy/generic/WorldPacketHandlerStrategy.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/playerbot/strategy/generic/WorldPacketHandlerStrategy.cpp b/playerbot/strategy/generic/WorldPacketHandlerStrategy.cpp index 772c0556d..5c94347df 100644 --- a/playerbot/strategy/generic/WorldPacketHandlerStrategy.cpp +++ b/playerbot/strategy/generic/WorldPacketHandlerStrategy.cpp @@ -7,7 +7,6 @@ using namespace ai; WorldPacketHandlerStrategy::WorldPacketHandlerStrategy(PlayerbotAI* ai) : PassTroughStrategy(ai) { supported.push_back("loot start roll"); - supported.push_back("check mount state"); supported.push_back("quest objective completed"); supported.push_back("party command"); supported.push_back("ready check"); @@ -23,6 +22,10 @@ void WorldPacketHandlerStrategy::InitNonCombatTriggers(std::list & { PassTroughStrategy::InitNonCombatTriggers(triggers); + triggers.push_back(new TriggerNode( + "check mount state", + NextAction::array(0, new NextAction("check mount state", 2.0f), NULL))); + triggers.push_back(new TriggerNode( "group invite", NextAction::array(0, new NextAction("accept invitation", relevance), NULL))); From 608472513b02054692b48fb7e33abb50c9b2736a Mon Sep 17 00:00:00 2001 From: mostlikely4r Date: Fri, 3 Feb 2023 22:46:46 +0100 Subject: [PATCH 05/26] -Bot group fix: Bots now are less likely to wander off in dungeons with bot-leaders. --- .../actions/ChooseRpgTargetAction.cpp | 25 ++++++++++--------- 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/playerbot/strategy/actions/ChooseRpgTargetAction.cpp b/playerbot/strategy/actions/ChooseRpgTargetAction.cpp index 774a4166b..772a30b32 100644 --- a/playerbot/strategy/actions/ChooseRpgTargetAction.cpp +++ b/playerbot/strategy/actions/ChooseRpgTargetAction.cpp @@ -390,6 +390,19 @@ bool ChooseRpgTargetAction::isFollowValid(Player* bot, WorldPosition pos) else return true; + + //Check if bot is in dungeon with master. + bool inDungeon = false; + if (master->IsInWorld() && master->GetMap()->IsDungeon()) + { + if (bot->GetMapId() == master->GetMapId()) + inDungeon = true; + } + + //Restrict distance in combat and in dungeons. + if ((inDungeon || master->IsInCombat()) && distance > 5.0f) + return false; + //With a bot master bots have more freedom. if (!ai->HasActivePlayerMaster()) { @@ -406,18 +419,6 @@ bool ChooseRpgTargetAction::isFollowValid(Player* bot, WorldPosition pos) return false; } - //Check if bot is in dungeon with master. - bool inDungeon = false; - if (realMaster->IsInWorld() && realMaster->GetMap()->IsDungeon()) - { - if (bot->GetMapId() == realMaster->GetMapId()) - inDungeon = true; - } - - //Restrict distance in combat and in dungeons. - if ((inDungeon || master->IsInCombat()) && (realMaster == master) && distance > 5.0f) - return false; - //Increase distance as master is standing still. Formation* formation = AI_VALUE(Formation*, "formation"); float maxDist = formation->GetMaxDistance(); From 90659695b32e7c51d3af1234fc26e3afaea0ac51 Mon Sep 17 00:00:00 2001 From: mostlikely4r Date: Sat, 4 Feb 2023 22:02:50 +0100 Subject: [PATCH 06/26] -Free command: New command 'free' which disabled stay, follow and guard and allows bots to move where they want. --- .../strategy/actions/ChatActionContext.h | 2 ++ .../strategy/actions/ChatShortcutActions.cpp | 22 +++++++++++++------ .../strategy/actions/ChatShortcutActions.h | 7 ++++++ .../actions/ChooseRpgTargetAction.cpp | 11 ++++++++-- .../generic/ChatCommandHandlerStrategy.cpp | 4 ++++ .../strategy/triggers/ChatTriggerContext.h | 2 ++ sql/playerbot/playerbot.sql | 3 ++- 7 files changed, 41 insertions(+), 10 deletions(-) diff --git a/playerbot/strategy/actions/ChatActionContext.h b/playerbot/strategy/actions/ChatActionContext.h index 95163f1a6..1ee373b35 100644 --- a/playerbot/strategy/actions/ChatActionContext.h +++ b/playerbot/strategy/actions/ChatActionContext.h @@ -133,6 +133,7 @@ namespace ai creators["follow chat shortcut"] = &ChatActionContext::follow_chat_shortcut; creators["stay chat shortcut"] = &ChatActionContext::stay_chat_shortcut; creators["guard chat shortcut"] = &ChatActionContext::guard_chat_shortcut; + creators["free chat shortcut"] = &ChatActionContext::free_chat_shortcut; creators["flee chat shortcut"] = &ChatActionContext::flee_chat_shortcut; creators["runaway chat shortcut"] = &ChatActionContext::runaway_chat_shortcut; creators["grind chat shortcut"] = &ChatActionContext::grind_chat_shortcut; @@ -215,6 +216,7 @@ namespace ai static Action* runaway_chat_shortcut(PlayerbotAI* ai) { return new GoawayChatShortcutAction(ai); } static Action* stay_chat_shortcut(PlayerbotAI* ai) { return new StayChatShortcutAction(ai); } static Action* follow_chat_shortcut(PlayerbotAI* ai) { return new FollowChatShortcutAction(ai); } + static Action* free_chat_shortcut(PlayerbotAI* ai) { return new FreeChatShortcutAction(ai); } static Action* guard_chat_shortcut(PlayerbotAI* ai) { return new GuardChatShortcutAction(ai); } static Action* gb(PlayerbotAI* ai) { return new GuildBankAction(ai); } static Action* bank(PlayerbotAI* ai) { return new BankAction(ai); } diff --git a/playerbot/strategy/actions/ChatShortcutActions.cpp b/playerbot/strategy/actions/ChatShortcutActions.cpp index 44a5761d3..efd1502b9 100644 --- a/playerbot/strategy/actions/ChatShortcutActions.cpp +++ b/playerbot/strategy/actions/ChatShortcutActions.cpp @@ -100,9 +100,6 @@ bool StayChatShortcutAction::Execute(Event& event) SetPosition(bot, "stay"); MEM_AI_VALUE(WorldPosition, "master position")->Reset(); - MotionMaster& mm = *bot->GetMotionMaster(); - mm.Clear(); - ai->TellError(BOT_TEXT("staying")); return true; } @@ -119,15 +116,26 @@ bool GuardChatShortcutAction::Execute(Event& event) SetPosition(bot); SetPosition(bot, "guard"); - MEM_AI_VALUE(WorldPosition, "master position")->Reset(); - - MotionMaster& mm = *bot->GetMotionMaster(); - mm.Clear(); + MEM_AI_VALUE(WorldPosition, "master position")->Reset(); ai->TellError(BOT_TEXT("guarding")); return true; } +bool FreeChatShortcutAction::Execute(Event& event) +{ + Player* master = GetMaster(); + if (!master) + return false; + + ai->Reset(); + ai->ChangeStrategy("-stay,-guard,-follow,-passive", BotState::BOT_STATE_NON_COMBAT); + ai->ChangeStrategy("-stay,-guard,-follow,-passive", BotState::BOT_STATE_COMBAT); + + ai->TellError(BOT_TEXT("free_moving")); + return true; +} + bool FleeChatShortcutAction::Execute(Event& event) { Player* master = GetMaster(); diff --git a/playerbot/strategy/actions/ChatShortcutActions.h b/playerbot/strategy/actions/ChatShortcutActions.h index 2bfa61ee2..5a8ccb4d1 100644 --- a/playerbot/strategy/actions/ChatShortcutActions.h +++ b/playerbot/strategy/actions/ChatShortcutActions.h @@ -34,6 +34,13 @@ namespace ai virtual bool Execute(Event& event) override; }; + class FreeChatShortcutAction : public ReturnPositionResetAction + { + public: + FreeChatShortcutAction(PlayerbotAI* ai) : ReturnPositionResetAction(ai, "free chat shortcut") {} + virtual bool Execute(Event& event) override; + }; + class FleeChatShortcutAction : public ReturnPositionResetAction { public: diff --git a/playerbot/strategy/actions/ChooseRpgTargetAction.cpp b/playerbot/strategy/actions/ChooseRpgTargetAction.cpp index 772a30b32..6980a9846 100644 --- a/playerbot/strategy/actions/ChooseRpgTargetAction.cpp +++ b/playerbot/strategy/actions/ChooseRpgTargetAction.cpp @@ -379,6 +379,7 @@ bool ChooseRpgTargetAction::isFollowValid(Player* bot, WorldPosition pos) float distance; PositionMap& posMap = AI_VALUE(PositionMap&, "position"); + bool freeMove = false; //Set distance relative to focus position. if (ai->HasStrategy("follow", BotState::BOT_STATE_NON_COMBAT)) @@ -388,7 +389,10 @@ bool ChooseRpgTargetAction::isFollowValid(Player* bot, WorldPosition pos) else if (ai->HasStrategy("guard", BotState::BOT_STATE_NON_COMBAT) && posMap["guard"].isSet()) distance = sqrt(pos.sqDistance2d(posMap["guard"].Get())); else - return true; + { + distance = sqrt(pos.sqDistance2d(bot)); + freeMove = true; + } //Check if bot is in dungeon with master. @@ -429,7 +433,10 @@ bool ChooseRpgTargetAction::isFollowValid(Player* bot, WorldPosition pos) maxDist += (lastMasterMove - 30); if (maxDist > sPlayerbotAIConfig.reactDistance) - maxDist = sPlayerbotAIConfig.reactDistance; + if (freeMove) + return true; + else + maxDist = sPlayerbotAIConfig.reactDistance; if (distance < maxDist) return true; diff --git a/playerbot/strategy/generic/ChatCommandHandlerStrategy.cpp b/playerbot/strategy/generic/ChatCommandHandlerStrategy.cpp index 995d65249..11d6ae659 100644 --- a/playerbot/strategy/generic/ChatCommandHandlerStrategy.cpp +++ b/playerbot/strategy/generic/ChatCommandHandlerStrategy.cpp @@ -186,6 +186,10 @@ void ChatCommandHandlerStrategy::InitReactionTriggers(std::list &t "guard", NextAction::array(0, new NextAction("guard chat shortcut", relevance), NULL))); + triggers.push_back(new TriggerNode( + "free", + NextAction::array(0, new NextAction("free chat shortcut", relevance), NULL))); + triggers.push_back(new TriggerNode( "wait for attack time", NextAction::array(0, new NextAction("wait for attack time", relevance), NULL))); diff --git a/playerbot/strategy/triggers/ChatTriggerContext.h b/playerbot/strategy/triggers/ChatTriggerContext.h index f302c4a9c..8baa2f72f 100644 --- a/playerbot/strategy/triggers/ChatTriggerContext.h +++ b/playerbot/strategy/triggers/ChatTriggerContext.h @@ -69,6 +69,7 @@ namespace ai creators["follow"] = &ChatTriggerContext::follow; creators["stay"] = &ChatTriggerContext::stay; creators["guard"] = &ChatTriggerContext::guard; + creators["free"] = &ChatTriggerContext::free; creators["wait for attack time"] = &ChatTriggerContext::wait_for_attack_time; creators["flee"] = &ChatTriggerContext::flee; creators["grind"] = &ChatTriggerContext::grind; @@ -157,6 +158,7 @@ namespace ai static Trigger* tank_attack(PlayerbotAI* ai) { return new ChatCommandTrigger(ai, "tank attack"); } static Trigger* stay(PlayerbotAI* ai) { return new ChatCommandTrigger(ai, "stay"); } static Trigger* guard(PlayerbotAI* ai) { return new ChatCommandTrigger(ai, "guard"); } + static Trigger* free(PlayerbotAI* ai) { return new ChatCommandTrigger(ai, "free"); } static Trigger* wait_for_attack_time(PlayerbotAI* ai) { return new ChatCommandTrigger(ai, "wait for attack time"); } static Trigger* follow(PlayerbotAI* ai) { return new ChatCommandTrigger(ai, "follow"); } static Trigger* gb(PlayerbotAI* ai) { return new ChatCommandTrigger(ai, "gb"); } diff --git a/sql/playerbot/playerbot.sql b/sql/playerbot/playerbot.sql index 1fd293c23..95bab61ab 100644 --- a/sql/playerbot/playerbot.sql +++ b/sql/playerbot/playerbot.sql @@ -918,7 +918,8 @@ INSERT INTO `ai_playerbot_texts` (`id`, `name`, `text`, `say_type`, `reply_type` (533, 'error_gbank_found', 'Can not find a guild bank nearby', 0, 0, '', '', '', '', '', '', '', 'Не могу найти гильд банк рядом'), (534, 'error_cant_put', 'I can\'t put ', 0, 0, '', '', '', '', '', '', '', 'Я не могу положить'), (535, 'error_gbank_rights', 'I have no rights to put items in the first guild bank tab', 0, 0, '', '', '', '', '', '', '', 'Нет прав чтобы класть вещи в первую вкладку гильд банка'), - (536, 'gbank_put', ' put to guild bank', 0, 0, '', '', '', '', '', '', '', ' теперь в гильд банке'); + (536, 'gbank_put', ' put to guild bank', 0, 0, '', '', '', '', '', '', '', ' теперь в гильд банке'), + (536, 'free_moving', ' free moving', 0, 0, '', '', '', '', '', '', '', ''); /*!40000 ALTER TABLE `ai_playerbot_texts` ENABLE KEYS */; -- Dumping structure for table tbcplayerbots.ai_playerbot_texts_chance From 360da0bba303353b1578c950c5ef10989c7916a9 Mon Sep 17 00:00:00 2001 From: mostlikely4r Date: Sat, 4 Feb 2023 22:03:43 +0100 Subject: [PATCH 07/26] -Pull fix: Stop bots getting stuck trying to pull. --- playerbot/strategy/generic/PullStrategy.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/playerbot/strategy/generic/PullStrategy.cpp b/playerbot/strategy/generic/PullStrategy.cpp index c53cdbdb4..6844aae21 100644 --- a/playerbot/strategy/generic/PullStrategy.cpp +++ b/playerbot/strategy/generic/PullStrategy.cpp @@ -222,7 +222,8 @@ void PullStrategy::RequestPull(Unit* target, bool resetTime) { SetTarget(target); pendingToStart = true; - pullStartTime = time(0); + if(resetTime) + pullStartTime = time(0); } float PullMultiplier::GetValue(Action* action) From 2df4ce32a178c9e15eeac8bf6463bf9bde4e79d5 Mon Sep 17 00:00:00 2001 From: mostlikely4r Date: Sun, 5 Feb 2023 20:06:15 +0100 Subject: [PATCH 08/26] -Follow fix: Bots now stop following the moment the master takes a flight path. --- playerbot/AiFactory.cpp | 2 +- playerbot/strategy/actions/ActionContext.h | 2 ++ playerbot/strategy/actions/FollowActions.h | 7 +++++ .../strategy/actions/MovementActions.cpp | 22 ++++++-------- .../strategy/generic/FollowMasterStrategy.cpp | 7 +++++ .../strategy/generic/FollowMasterStrategy.h | 1 + playerbot/strategy/triggers/RangeTriggers.h | 29 +++++++++++++++++-- playerbot/strategy/triggers/TriggerContext.h | 2 ++ 8 files changed, 55 insertions(+), 17 deletions(-) diff --git a/playerbot/AiFactory.cpp b/playerbot/AiFactory.cpp index 1d6c45246..556153649 100644 --- a/playerbot/AiFactory.cpp +++ b/playerbot/AiFactory.cpp @@ -688,7 +688,7 @@ Engine* AiFactory::createDeadEngine(Player* player, PlayerbotAI* const facade, A void AiFactory::AddDefaultReactionStrategies(Player* player, PlayerbotAI* const facade, ReactionEngine* reactionEngine) { - reactionEngine->addStrategies("react", "chat", "avoid aoe", "potions", NULL); + reactionEngine->addStrategies("react", "chat", "avoid aoe", "potions", "follow", NULL); } ReactionEngine* AiFactory::createReactionEngine(Player* player, PlayerbotAI* const facade, AiObjectContext* AiObjectContext) { diff --git a/playerbot/strategy/actions/ActionContext.h b/playerbot/strategy/actions/ActionContext.h index c4770288b..ca2cb6a53 100644 --- a/playerbot/strategy/actions/ActionContext.h +++ b/playerbot/strategy/actions/ActionContext.h @@ -109,6 +109,7 @@ namespace ai creators["auto loot roll"] = &ActionContext::auto_loot_roll; creators["shoot"] = &ActionContext::shoot; creators["follow"] = &ActionContext::follow; + creators["stop follow"] = &ActionContext::stop_follow; creators["flee to master"] = &ActionContext::flee_to_master; creators["runaway"] = &ActionContext::runaway; creators["stay"] = &ActionContext::stay; @@ -363,6 +364,7 @@ namespace ai static Action* sit(PlayerbotAI* ai) { return new SitAction(ai); } static Action* runaway(PlayerbotAI* ai) { return new RunAwayAction(ai); } static Action* follow(PlayerbotAI* ai) { return new FollowAction(ai); } + static Action* stop_follow(PlayerbotAI* ai) { return new StopFollowAction(ai); } static Action* flee_to_master(PlayerbotAI* ai) { return new FleeToMasterAction(ai); } static Action* add_gathering_loot(PlayerbotAI* ai) { return new AddGatheringLootAction(ai); } static Action* add_loot(PlayerbotAI* ai) { return new AddLootAction(ai); } diff --git a/playerbot/strategy/actions/FollowActions.h b/playerbot/strategy/actions/FollowActions.h index 3cec06bb6..c165ed244 100644 --- a/playerbot/strategy/actions/FollowActions.h +++ b/playerbot/strategy/actions/FollowActions.h @@ -13,6 +13,12 @@ namespace ai virtual bool CanDeadFollow(Unit* target); }; + class StopFollowAction : public MovementAction { + public: + StopFollowAction(PlayerbotAI* ai, string name = "stop follow") : MovementAction(ai, name) {} + virtual bool Execute(Event& event) { ai->StopMoving(); return true; }; + }; + class FleeToMasterAction : public FollowAction { public: FleeToMasterAction(PlayerbotAI* ai) : FollowAction(ai, "flee to master") {} @@ -20,4 +26,5 @@ namespace ai virtual bool Execute(Event& event); virtual bool isUseful(); }; + } diff --git a/playerbot/strategy/actions/MovementActions.cpp b/playerbot/strategy/actions/MovementActions.cpp index 2684caf7f..52c13ac34 100644 --- a/playerbot/strategy/actions/MovementActions.cpp +++ b/playerbot/strategy/actions/MovementActions.cpp @@ -348,7 +348,7 @@ bool MovementAction::MoveTo(uint32 mapId, float x, float y, float z, bool idle, } #endif - bool detailedMove = ai->AllowActivity(DETAILED_MOVE_ACTIVITY); + bool detailedMove = ai->AllowActivity(DETAILED_MOVE_ACTIVITY,true); if (!detailedMove) { @@ -843,7 +843,6 @@ bool MovementAction::MoveTo(uint32 mapId, float x, float y, float z, bool idle, ai->StopMoving(); else if (mover) mover->InterruptMoving(true); - mm.Clear(); } else { @@ -855,7 +854,6 @@ bool MovementAction::MoveTo(uint32 mapId, float x, float y, float z, bool idle, ai->StopMoving(); else mover->InterruptMoving(true); - mm.Clear(); } } @@ -1239,7 +1237,7 @@ bool MovementAction::Follow(Unit* target, float distance, float angle) } } - if (sServerFacade.IsDistanceGreaterOrEqualThan(sServerFacade.GetDistance2d(bot, target), sPlayerbotAIConfig.sightDistance) || (target->IsFlying() && !bot->IsFreeFlying())) + if (sServerFacade.IsDistanceGreaterOrEqualThan(sServerFacade.GetDistance2d(bot, target), sPlayerbotAIConfig.sightDistance) || (target->IsFlying() && !bot->IsFreeFlying()) || target->IsTaxiFlying()) { if (target->GetObjectGuid().IsPlayer()) { @@ -1262,18 +1260,16 @@ bool MovementAction::Follow(Unit* target, float distance, float angle) } } } - else + + if (pTarget->IsTaxiFlying()) //Move to where the player is flying to. { - if (pTarget->IsTaxiFlying()) //Move to where the player is flying to. + const Taxi::Map tMap = pTarget->GetTaxiPathSpline(); + if (!tMap.empty()) { - const Taxi::Map tMap = pTarget->GetTaxiPathSpline(); - if (!tMap.empty()) - { - auto tEnd = tMap.back(); + auto tEnd = tMap.back(); - if (tEnd) - return MoveTo(tEnd->mapid, tEnd->x, tEnd->y, tEnd->z); - } + if (tEnd) + return MoveTo(tEnd->mapid, tEnd->x, tEnd->y, tEnd->z); } } } diff --git a/playerbot/strategy/generic/FollowMasterStrategy.cpp b/playerbot/strategy/generic/FollowMasterStrategy.cpp index 909e3fa7a..c92161baf 100644 --- a/playerbot/strategy/generic/FollowMasterStrategy.cpp +++ b/playerbot/strategy/generic/FollowMasterStrategy.cpp @@ -25,6 +25,13 @@ void FollowMasterStrategy::InitDeadTriggers(std::list& triggers) InitNonCombatTriggers(triggers); } +void FollowMasterStrategy::InitReactionTriggers(std::list& triggers) +{ + triggers.push_back(new TriggerNode( + "stop follow", + NextAction::array(0, new NextAction("stop follow", 100.0f), NULL))); +} + void FollowMasterStrategy::OnStrategyRemoved(BotState state) { if (state == ai->GetState() && ai->GetBot()->GetMotionMaster()->GetCurrentMovementGeneratorType() == FOLLOW_MOTION_TYPE) diff --git a/playerbot/strategy/generic/FollowMasterStrategy.h b/playerbot/strategy/generic/FollowMasterStrategy.h index 137668ad9..58c4478b8 100644 --- a/playerbot/strategy/generic/FollowMasterStrategy.h +++ b/playerbot/strategy/generic/FollowMasterStrategy.h @@ -20,6 +20,7 @@ namespace ai void InitNonCombatTriggers(std::list &triggers) override; void InitCombatTriggers(std::list& triggers) override; void InitDeadTriggers(std::list& triggers) override; + void InitReactionTriggers(std::list& triggers) override; virtual void OnStrategyRemoved(BotState state) override; }; diff --git a/playerbot/strategy/triggers/RangeTriggers.h b/playerbot/strategy/triggers/RangeTriggers.h index 60e7b2f6f..58b553e3b 100644 --- a/playerbot/strategy/triggers/RangeTriggers.h +++ b/playerbot/strategy/triggers/RangeTriggers.h @@ -294,7 +294,7 @@ namespace ai class NotNearMasterTrigger : public OutOfReactRangeTrigger { public: - NotNearMasterTrigger(PlayerbotAI* ai, string name = "not near master") : OutOfReactRangeTrigger(ai, name, 5.0f, 2) {} + NotNearMasterTrigger(PlayerbotAI* ai, string name = "not near master", int checkInterval = 2) : OutOfReactRangeTrigger(ai, name, 5.0f, checkInterval) {} virtual bool IsActive() { @@ -305,7 +305,7 @@ namespace ai class UpdateFollowTrigger : public NotNearMasterTrigger { public: - UpdateFollowTrigger(PlayerbotAI* ai) : NotNearMasterTrigger(ai, "update follow") {} + UpdateFollowTrigger(PlayerbotAI* ai) : NotNearMasterTrigger(ai, "update follow", 3) {} virtual bool IsActive() { @@ -319,7 +319,7 @@ namespace ai return false; //We need to land or liftoff. - if (followTarget->IsFlying() != bot->IsFlying()) + if (followTarget->IsFlying() != bot->IsFlying() || followTarget->IsTaxiFlying()) return true; Formation* formation = AI_VALUE(Formation*, "formation"); @@ -332,6 +332,29 @@ namespace ai } }; + class StopFollowTrigger : public Trigger + { + public: + StopFollowTrigger(PlayerbotAI* ai) : Trigger(ai, "stop follow", 1) {} + + virtual bool IsActive() + { + if (bot->GetMotionMaster()->GetCurrentMovementGeneratorType() != FOLLOW_MOTION_TYPE) + return false; + + Unit* followTarget = AI_VALUE(Unit*, "follow target"); + + if (!followTarget) + return true; + + //We need to land or liftoff. + if (followTarget->IsTaxiFlying()) + return true; + + return false; + } + }; + class WaitForAttackSafeDistanceTrigger : public Trigger { public: diff --git a/playerbot/strategy/triggers/TriggerContext.h b/playerbot/strategy/triggers/TriggerContext.h index faf32f3a4..bf5aabda8 100644 --- a/playerbot/strategy/triggers/TriggerContext.h +++ b/playerbot/strategy/triggers/TriggerContext.h @@ -125,6 +125,7 @@ namespace ai creators["far from master"] = &TriggerContext::far_from_master; creators["not near master"] = &TriggerContext::not_near_master; creators["update follow"] = &TriggerContext::update_follow; + creators["stop follow"] = &TriggerContext::stop_follow; creators["far from loot target"] = &TriggerContext::far_from_loot_target; creators["can loot"] = &TriggerContext::can_loot; creators["swimming"] = &TriggerContext::swimming; @@ -302,6 +303,7 @@ namespace ai static Trigger* far_from_master(PlayerbotAI* ai) { return new FarFromMasterTrigger(ai); } static Trigger* not_near_master(PlayerbotAI* ai) { return new NotNearMasterTrigger(ai); } static Trigger* update_follow(PlayerbotAI* ai) { return new UpdateFollowTrigger(ai); } + static Trigger* stop_follow(PlayerbotAI* ai) { return new StopFollowTrigger(ai); } static Trigger* behind_target(PlayerbotAI* ai) { return new IsBehindTargetTrigger(ai); } static Trigger* not_behind_target(PlayerbotAI* ai) { return new IsNotBehindTargetTrigger(ai); } static Trigger* not_facing_target(PlayerbotAI* ai) { return new IsNotFacingTargetTrigger(ai); } From 0b5968e544ed6716c322fdd2ed31a0521f72379b Mon Sep 17 00:00:00 2001 From: mostlikely4r Date: Sun, 5 Feb 2023 20:08:44 +0100 Subject: [PATCH 09/26] -Guard/Free improvement: Bots now get a 20y base range when they enter guard or free. Bots also no longer decrease their range once they are far from the master. --- .../actions/ChooseRpgTargetAction.cpp | 31 ++++++++++++------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/playerbot/strategy/actions/ChooseRpgTargetAction.cpp b/playerbot/strategy/actions/ChooseRpgTargetAction.cpp index 6980a9846..0a94c40f3 100644 --- a/playerbot/strategy/actions/ChooseRpgTargetAction.cpp +++ b/playerbot/strategy/actions/ChooseRpgTargetAction.cpp @@ -373,7 +373,7 @@ bool ChooseRpgTargetAction::isFollowValid(Player* bot, WorldPosition pos) Player* realMaster = ai->GetMaster(); AiObjectContext* context = ai->GetAiObjectContext(); - if (!master || bot == master || master->IsBeingTeleported()) + if (!master || bot == master || master->IsBeingTeleported() || master->GetMapId() != bot->GetMapId()) return true; float distance; @@ -424,19 +424,28 @@ bool ChooseRpgTargetAction::isFollowValid(Player* bot, WorldPosition pos) } //Increase distance as master is standing still. - Formation* formation = AI_VALUE(Formation*, "formation"); - float maxDist = formation->GetMaxDistance(); + float maxDist = INTERACTION_DISTANCE; - uint32 lastMasterMove = MEM_AI_VALUE(WorldPosition, "master position")->LastChangeDelay(); + if (freeMove || ai->HasStrategy("guard", BotState::BOT_STATE_NON_COMBAT)) //Free and guard start with a base 20y range. + maxDist += sPlayerbotAIConfig.lootDistance; - if (lastMasterMove > 30.0f) //After 30 seconds increase the range by 1y each second. - maxDist += (lastMasterMove - 30); + if (WorldPosition(bot).fDist(master) < sPlayerbotAIConfig.reactDistance) + { + uint32 lastMasterMove = MEM_AI_VALUE(WorldPosition, "master position")->LastChangeDelay(); - if (maxDist > sPlayerbotAIConfig.reactDistance) - if (freeMove) - return true; - else - maxDist = sPlayerbotAIConfig.reactDistance; + if (lastMasterMove > 30.0f) //After 30 seconds increase the range by 1y each second. + maxDist += (lastMasterMove - 30); + + if (maxDist > sPlayerbotAIConfig.reactDistance) + if (freeMove) + return true; + else + maxDist = sPlayerbotAIConfig.reactDistance; + } + else if (freeMove) + return true; + else + maxDist = sPlayerbotAIConfig.reactDistance; if (distance < maxDist) return true; From 576d2f138e4f1e573c327f469daf6ca5fb81cf49 Mon Sep 17 00:00:00 2001 From: mostlikely4r Date: Sun, 5 Feb 2023 20:09:37 +0100 Subject: [PATCH 10/26] -Travel fix: Bots no longer tell they are traveling when in free unless the destination is in range. --- playerbot/strategy/actions/ChooseTravelTargetAction.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/playerbot/strategy/actions/ChooseTravelTargetAction.cpp b/playerbot/strategy/actions/ChooseTravelTargetAction.cpp index 31dcb0659..8fb1ccce3 100644 --- a/playerbot/strategy/actions/ChooseTravelTargetAction.cpp +++ b/playerbot/strategy/actions/ChooseTravelTargetAction.cpp @@ -5,6 +5,7 @@ #include "../../PlayerbotAIConfig.h" #include "../../TravelMgr.h" #include +#include "ChooseRpgTargetAction.h" using namespace ai; @@ -177,7 +178,8 @@ void ChooseTravelTargetAction::setNewTarget(TravelTarget* newTarget, TravelTarge return; } - ReportTravelTarget(newTarget, oldTarget); + if(ChooseRpgTargetAction::isFollowValid(bot, *newTarget->getPosition())) + ReportTravelTarget(newTarget, oldTarget); //If we are heading to a creature/npc clear it from the ignore list. if (oldTarget && oldTarget == newTarget && newTarget->getEntry()) From 902ad5dea5ab0d75c1cb66ac9970c31ec626db86 Mon Sep 17 00:00:00 2001 From: mostlikely4r Date: Tue, 7 Feb 2023 15:46:56 +0100 Subject: [PATCH 11/26] -Internal: Add itemQualifier which can store itemId, randomProperty and gems for proper statweight caculations. --- playerbot/strategy/values/ItemUsageValue.cpp | 76 +++++++++++++------- playerbot/strategy/values/ItemUsageValue.h | 29 +++++++- 2 files changed, 77 insertions(+), 28 deletions(-) diff --git a/playerbot/strategy/values/ItemUsageValue.cpp b/playerbot/strategy/values/ItemUsageValue.cpp index 3302eb33c..12e63dd8c 100644 --- a/playerbot/strategy/values/ItemUsageValue.cpp +++ b/playerbot/strategy/values/ItemUsageValue.cpp @@ -11,9 +11,36 @@ using namespace ai; -ItemUsage ItemUsageValue::Calculate() +ItemQualifier::ItemQualifier(string qualifier) { - uint32 itemId = atoi(qualifier.c_str()); + vector numbers = Qualified::getMultiQualifiers(qualifier, ":"); + + itemId = stoi(numbers[0]); + if (numbers.size() > 2) + randomPropertyId = stoi(numbers[2]); + else + randomPropertyId = 0; + + if (numbers.size() > 7) + { + gem1 = stoi(numbers[4]); + gem2 = stoi(numbers[5]); + gem3 = stoi(numbers[6]); + gem4 = stoi(numbers[7]); + } + else + { + gem1 = 0; + gem2 = 0; + gem3 = 0; + gem4 = 0; + } +} + +ItemUsage ItemUsageValue::Calculate() +{ + ItemQualifier itemQualifier(qualifier); + uint32 itemId = itemQualifier.GetId(); if (!itemId) return ITEM_USAGE_NONE; @@ -83,7 +110,7 @@ ItemUsage ItemUsageValue::Calculate() if (bot->GetGuildId() && sGuildTaskMgr.IsGuildTaskItem(itemId, bot->GetGuildId())) return ITEM_USAGE_GUILD_TASK; - ItemUsage equip = QueryItemUsageForEquip(proto); + ItemUsage equip = QueryItemUsageForEquip(itemQualifier); if (equip != ITEM_USAGE_NONE) return equip; @@ -187,8 +214,10 @@ ItemUsage ItemUsageValue::Calculate() return ITEM_USAGE_NONE; } -ItemUsage ItemUsageValue::QueryItemUsageForEquip(ItemPrototype const* itemProto) +ItemUsage ItemUsageValue::QueryItemUsageForEquip(ItemQualifier& itemQualifier) { + ItemPrototype const* itemProto = itemQualifier.GetProto(); + if (bot->CanUseItem(itemProto) != EQUIP_ERR_OK) return ITEM_USAGE_NONE; @@ -198,7 +227,6 @@ ItemUsage ItemUsageValue::QueryItemUsageForEquip(ItemPrototype const* itemProto) uint16 dest; list items = AI_VALUE2(list, "inventory items", chat->formatItem(itemProto)); - InventoryResult result; if (!items.empty()) { @@ -237,7 +265,7 @@ ItemUsage ItemUsageValue::QueryItemUsageForEquip(ItemPrototype const* itemProto) uint32 specId = sRandomItemMgr.GetPlayerSpecId(bot); - uint32 statWeight = sRandomItemMgr.GetLiveStatWeight(bot, itemProto->ItemId, specId); + uint32 statWeight = sRandomItemMgr.ItemStatWeight(bot, itemQualifier); if (statWeight) shouldEquip = true; @@ -249,7 +277,7 @@ ItemUsage ItemUsageValue::QueryItemUsageForEquip(ItemPrototype const* itemProto) Item* oldItem = bot->GetItemByPos(dest); //No item equiped - if (!oldItem) + if (!oldItem) if (shouldEquip) return ITEM_USAGE_EQUIP; else @@ -257,19 +285,16 @@ ItemUsage ItemUsageValue::QueryItemUsageForEquip(ItemPrototype const* itemProto) const ItemPrototype* oldItemProto = oldItem->GetProto(); - if (oldItem) - { - if (AI_VALUE2(ForceItemUsage, "force item usage", oldItemProto->ItemId) == ForceItemUsage::FORCE_USAGE_EQUIP) //Current equip is forced. Do not unequip. - if(AI_VALUE2(ForceItemUsage, "force item usage", itemProto->ItemId) == ForceItemUsage::FORCE_USAGE_EQUIP) - return ITEM_USAGE_KEEP; - else - return ITEM_USAGE_NONE; + if (AI_VALUE2(ForceItemUsage, "force item usage", oldItemProto->ItemId) == ForceItemUsage::FORCE_USAGE_EQUIP) //Current equip is forced. Do not unequip. + if (AI_VALUE2(ForceItemUsage, "force item usage", itemProto->ItemId) == ForceItemUsage::FORCE_USAGE_EQUIP) + return ITEM_USAGE_KEEP; + else + return ITEM_USAGE_NONE; - uint32 oldStatWeight = sRandomItemMgr.GetLiveStatWeight(bot, oldItemProto->ItemId, specId); - if (statWeight || oldStatWeight) - { - shouldEquip = statWeight >= oldStatWeight; - } + uint32 oldStatWeight = sRandomItemMgr.ItemStatWeight(bot, oldItem); + if (statWeight || oldStatWeight) + { + shouldEquip = statWeight >= oldStatWeight; } if (AI_VALUE2(ForceItemUsage, "force item usage", itemProto->ItemId) == ForceItemUsage::FORCE_USAGE_EQUIP) //New item is forced. Always equip it. @@ -283,21 +308,18 @@ ItemUsage ItemUsageValue::QueryItemUsageForEquip(ItemPrototype const* itemProto) ITEM_USAGE_NONE; bool existingShouldEquip = true; - if (oldItemProto->Class == ITEM_CLASS_WEAPON && !sRandomItemMgr.CanEquipWeapon(bot->getClass(), oldItemProto)) + if (oldItemProto->Class == ITEM_CLASS_WEAPON && !oldStatWeight) existingShouldEquip = false; - if (oldItemProto->Class == ITEM_CLASS_ARMOR && !sRandomItemMgr.CanEquipArmor(bot->getClass(), specId, bot->GetLevel(), oldItemProto)) + if (oldItemProto->Class == ITEM_CLASS_ARMOR && !statWeight) existingShouldEquip = false; - uint32 oldItemPower = sRandomItemMgr.GetLiveStatWeight(bot, oldItemProto->ItemId); - uint32 newItemPower = sRandomItemMgr.GetLiveStatWeight(bot, itemProto->ItemId); - //Compare items based on item level, quality or itemId. bool isBetter = false; - if (newItemPower > oldItemPower) + if (statWeight > oldStatWeight) isBetter = true; - else if (newItemPower == oldItemPower && itemProto->Quality > oldItemProto->Quality) + else if (statWeight == oldStatWeight && itemProto->Quality > oldItemProto->Quality) isBetter = true; - else if (newItemPower == oldItemPower && itemProto->Quality == oldItemProto->Quality && itemProto->ItemId > oldItemProto->ItemId) + else if (statWeight == oldStatWeight && itemProto->Quality == oldItemProto->Quality && itemProto->ItemId > oldItemProto->ItemId) isBetter = true; Item* item = CurrentItem(itemProto); diff --git a/playerbot/strategy/values/ItemUsageValue.h b/playerbot/strategy/values/ItemUsageValue.h index d51fa3521..cf6bf1d98 100644 --- a/playerbot/strategy/values/ItemUsageValue.h +++ b/playerbot/strategy/values/ItemUsageValue.h @@ -4,6 +4,33 @@ namespace ai { + class ItemQualifier + { + public: + ItemQualifier() { itemId = 0; randomPropertyId = 0; gem1 = 0; gem2 = 0; gem3 = 0; gem4 = 0; }; + ItemQualifier(uint32 itemId, int32 randomPropertyId = 0, uint32 gem1 = 0, uint32 gem2 = 0, uint32 gem3 = 0, uint32 gem4 = 0) : itemId(itemId), randomPropertyId(randomPropertyId), gem1(gem1), gem2(gem2), gem3(gem3), gem4(gem4) {}; + ItemQualifier(Item* item) { itemId = item->GetProto()->ItemId; randomPropertyId = item->GetItemRandomPropertyId(); gem1 = 0; gem2 = 0; gem3 = 0; gem4 = 0; }; //todo Gems. + ItemQualifier(LootItem item) { itemId = item.itemId; randomPropertyId = item.randomPropertyId; gem1 = 0; gem2 = 0; gem3 = 0; gem4 = 0; }; //todo Gems. + + ItemQualifier(string qualifier); + uint32 GetId() { return itemId; } + uint32 GetRandomPropertyId() { return randomPropertyId; } + uint32 GetGem1() { return gem1; } + uint32 GetGem2() { return gem2; } + uint32 GetGem3() { return gem3; } + uint32 GetGem4() { return gem4; } + string GetQualifier() { return to_string(itemId) + ":0:" + to_string(randomPropertyId) + ":0:" + to_string(gem1) + ":" + to_string(gem2) + ":" + to_string(gem3) + ":" + to_string(gem4); } + + ItemPrototype const* GetProto() { ItemPrototype const* proto = sItemStorage.LookupEntry(itemId); return proto; }; + private: + uint32 itemId; + int32 randomPropertyId; + uint32 gem1; + uint32 gem2; + uint32 gem3; + uint32 gem4; + }; + enum ItemUsage { ITEM_USAGE_NONE = 0, @@ -41,7 +68,7 @@ namespace ai virtual ItemUsage Calculate(); private: - ItemUsage QueryItemUsageForEquip(ItemPrototype const * proto); + ItemUsage QueryItemUsageForEquip(ItemQualifier& itemQualifier); uint32 GetSmallestBagSize(); bool IsItemUsefulForQuest(Player* player, ItemPrototype const* proto, bool ignoreInventory = false); From 92b46b3593fba6b29f555c18a5cccfe5172c5133 Mon Sep 17 00:00:00 2001 From: mostlikely4r Date: Tue, 7 Feb 2023 15:47:52 +0100 Subject: [PATCH 12/26] -Internal: Add calculations for statweight calculation for uncached items and randomProperty --- playerbot/RandomItemMgr.cpp | 103 ++++++++++++++++++++++++++++++++++++ playerbot/RandomItemMgr.h | 9 ++++ 2 files changed, 112 insertions(+) diff --git a/playerbot/RandomItemMgr.cpp b/playerbot/RandomItemMgr.cpp index c3eb009af..bb39f2c57 100644 --- a/playerbot/RandomItemMgr.cpp +++ b/playerbot/RandomItemMgr.cpp @@ -128,6 +128,12 @@ RandomItemMgr::RandomItemMgr() weightStatLink["int"] = ITEM_MOD_INTELLECT; weightStatLink["spi"] = ITEM_MOD_SPIRIT; + ItemStatLink[STAT_STAMINA] = "sta"; + ItemStatLink[STAT_STRENGTH] = "str"; + ItemStatLink[STAT_AGILITY] = "agi"; + ItemStatLink[STAT_INTELLECT] = "int"; + ItemStatLink[STAT_SPIRIT] = "spi"; + #ifdef MANGOSBOT_TWO weightStatLink["splpwr"] = ITEM_MOD_SPELL_POWER; weightStatLink["atkpwr"] = ITEM_MOD_ATTACK_POWER; @@ -2009,6 +2015,103 @@ uint32 RandomItemMgr::CalculateStatWeight(uint8 playerclass, uint8 spec, ItemPro return statWeight; } +uint32 RandomItemMgr::CalculateRandomPropertyWeight(uint8 playerclass, uint8 spec, uint32 randomPropertyId) +{ + uint32 weight = 0; + if (randomPropertyId > 0) + { + ItemRandomPropertiesEntry const* item_rand = sItemRandomPropertiesStore.LookupEntry(randomPropertyId); + if (item_rand) + { + for (uint32 i = PROP_ENCHANTMENT_SLOT_0; i < PROP_ENCHANTMENT_SLOT_0 + 3; ++i) + { + if (SpellItemEnchantmentEntry const* pEnchant = sSpellItemEnchantmentStore.LookupEntry(item_rand->enchant_id[i - PROP_ENCHANTMENT_SLOT_0])) + { + for (int s = 0; s < 3; ++s) + { + switch (pEnchant->type[s]) + { + case 1: //Proc //TODO add proc values? + break; + case 2: //Damage + if (!pEnchant->amount[s]) + continue; + weight += CalculateSingleStatWeight(playerclass, spec, "mledps", pEnchant->amount[s]); + break; + case 3: + { + if (!pEnchant->spellid[s]) + continue; + + SpellEntry const* spellInfo = sSpellTemplate.LookupEntry(pEnchant->spellid[s]); + + if (!spellInfo) + continue; + + for (uint32 j = 0; j < MAX_EFFECT_INDEX; ++j) + { + if (spellInfo->Effect[j] != SPELL_EFFECT_APPLY_AURA) + continue; + if (spellInfo->EffectApplyAuraName[j] != SPELL_AURA_MOD_STAT) + continue; + + uint32 stat = spellInfo->EffectMiscValue[j]; + uint32 value = spellInfo->EffectBasePoints[j] + 1; + + if (!value) + continue; + + if (ItemStatLink.find(stat) == ItemStatLink.end()) + continue; + + weight += CalculateSingleStatWeight(playerclass, spec, ItemStatLink[stat], value); + } + break; + } + case 4: //Armor + if (!pEnchant->amount[s]) + continue; + weight += CalculateSingleStatWeight(playerclass, spec, "armor", pEnchant->amount[s]); + break; + } + + } + } + } + } + } + return weight; +} + + +uint32 RandomItemMgr::ItemStatWeight(Player* player, ItemPrototype const* proto, uint32 randomPropertyId, uint32 gem1, uint32 gem2, uint32 gem3, uint32 gem4) +{ + ItemSpecType itSpec; + uint32 weight = CalculateStatWeight(player->getClass(), GetPlayerSpecId(player), proto, itSpec); + if (randomPropertyId) + weight += CalculateRandomPropertyWeight(player->getClass(), GetPlayerSpecId(player), randomPropertyId); + + //Todo gems. + + return weight; +} + +uint32 RandomItemMgr::ItemStatWeight(Player* player, uint32 itemId, uint32 randomPropertyId, uint32 gem1, uint32 gem2, uint32 gem3, uint32 gem4) +{ + const ItemPrototype* proto = sObjectMgr.GetItemPrototype(itemId); + return ItemStatWeight(player, proto, randomPropertyId, gem1, gem2, gem3, gem4); +} + +uint32 RandomItemMgr::ItemStatWeight(Player* player, ItemQualifier& qualifier) +{ + return ItemStatWeight(player, qualifier.GetId(), qualifier.GetRandomPropertyId(), qualifier.GetGem1(), qualifier.GetGem2(), qualifier.GetGem3(), qualifier.GetGem4()); +} + +uint32 RandomItemMgr::ItemStatWeight(Player* player, Item* item) +{ + return ItemStatWeight(player, ItemQualifier(item)); +} + uint32 RandomItemMgr::CalculateSingleStatWeight(uint8 playerclass, uint8 spec, std::string stat, uint32 value) { uint32 statWeight = 0; diff --git a/playerbot/RandomItemMgr.h b/playerbot/RandomItemMgr.h index eea2a7bd8..c299e8b9d 100644 --- a/playerbot/RandomItemMgr.h +++ b/playerbot/RandomItemMgr.h @@ -10,6 +10,7 @@ #ifdef MANGOS #include "Object/Player.h" #endif +#include "strategy/values/ItemUsageValue.h" using namespace std; @@ -164,7 +165,14 @@ class RandomItemMgr uint32 GetFood(uint32 level, uint32 category); uint32 GetRandomTrade(uint32 level); vector GetGemsList(); + + uint32 CalculateRandomPropertyWeight(uint8 playerclass, uint8 spec, uint32 randomPropertyId); uint32 CalculateStatWeight(uint8 playerclass, uint8 spec, ItemPrototype const* proto, ItemSpecType &itSpec); + uint32 ItemStatWeight(Player* player, ItemPrototype const* proto, uint32 randomPropertyId = 0, uint32 gem1 = 0, uint32 gem2 = 0, uint32 gem3 = 0, uint32 gem4 = 0); + uint32 ItemStatWeight(Player* player, uint32 itemId, uint32 randomPropertyId = 0, uint32 gem1 = 0, uint32 gem2 = 0, uint32 gem3 = 0, uint32 gem4 = 0); + uint32 ItemStatWeight(Player* player, ItemQualifier& qualifier); + uint32 ItemStatWeight(Player* player, Item* item); + uint32 CalculateSingleStatWeight(uint8 playerclass, uint8 spec, std::string stat, uint32 value); bool CanEquipArmor(uint8 clazz, uint8 spec, uint32 level, ItemPrototype const* proto); bool ShouldEquipArmorForSpec(uint8 playerclass, uint8 spec, ItemPrototype const* proto); @@ -203,6 +211,7 @@ class RandomItemMgr map rarityCache; map m_weightScales; map weightStatLink; + map ItemStatLink; map weightRatingLink; map itemInfoCache; }; From a8e58e14fc163c63196e2dce108f3fac375de451 Mon Sep 17 00:00:00 2001 From: mostlikely4r Date: Tue, 7 Feb 2023 15:49:17 +0100 Subject: [PATCH 13/26] -Internal: Add support for parsing itemQualifiers to extract itemId, RandomProperty and gems from item links. --- playerbot/ChatHelper.cpp | 23 +++++++++++++++++++++++ playerbot/ChatHelper.h | 1 + 2 files changed, 24 insertions(+) diff --git a/playerbot/ChatHelper.cpp b/playerbot/ChatHelper.cpp index 0b2adaee0..b2ba4505f 100644 --- a/playerbot/ChatHelper.cpp +++ b/playerbot/ChatHelper.cpp @@ -274,6 +274,29 @@ ItemIds ChatHelper::parseItems(string& text) return itemIds; } +set ChatHelper::parseItemQualifiers(string& text) +{ + set qualifiers; + + uint8 pos = 0; + while (true) + { + int i = text.find("Hitem:", pos); + if (i == -1) + break; + pos = i + 6; + int endPos = text.find('|', pos); + if (endPos == -1) + break; + + string qualifierString = text.substr(pos, endPos - pos); + + qualifiers.insert(qualifierString); + } + + return qualifiers; +} + string ChatHelper::formatQuest(Quest const* quest) { ostringstream out; diff --git a/playerbot/ChatHelper.h b/playerbot/ChatHelper.h index cf21b877f..a416c6088 100644 --- a/playerbot/ChatHelper.h +++ b/playerbot/ChatHelper.h @@ -21,6 +21,7 @@ namespace ai static string formatItem(ItemPrototype const * proto, int count = 0, int total = 0); static string formatQItem(uint32 itemId); static ItemIds parseItems(string& text); + static set parseItemQualifiers(string& text); static uint32 parseItemQuality(string text); static bool parseItemClass(string text, uint32* itemClass, uint32* itemSubClass); static uint32 parseSlot(string text); From a8c9ab06ea4c6c3197cc6d28617f75adf607377d Mon Sep 17 00:00:00 2001 From: mostlikely4r Date: Tue, 7 Feb 2023 15:51:09 +0100 Subject: [PATCH 14/26] -Statweight: Add support for random properties when using 'q' query item usage. --- .../strategy/actions/QueryItemUsageAction.cpp | 78 +++++++++---------- .../strategy/actions/QueryItemUsageAction.h | 12 +-- 2 files changed, 42 insertions(+), 48 deletions(-) diff --git a/playerbot/strategy/actions/QueryItemUsageAction.cpp b/playerbot/strategy/actions/QueryItemUsageAction.cpp index a35849bbe..cedddc472 100644 --- a/playerbot/strategy/actions/QueryItemUsageAction.cpp +++ b/playerbot/strategy/actions/QueryItemUsageAction.cpp @@ -39,11 +39,12 @@ bool QueryItemUsageAction::Execute(Event& event) data >> count; // data >> invCount; // [-ZERO] count of items in inventory - ItemPrototype const *item = sItemStorage.LookupEntry(itemId); - if (!item) + ItemQualifier itemQualifier(itemId, itemRandomPropertyId); + + if (!itemQualifier.GetProto()) return false; - ai->TellMaster(QueryItem(item, count, GetCount(item)), PlayerbotSecurityLevel::PLAYERBOT_SECURITY_ALLOW_ALL, false); + ai->TellMaster(QueryItem(itemQualifier, count, GetCount(itemQualifier)), PlayerbotSecurityLevel::PLAYERBOT_SECURITY_ALLOW_ALL, false); if (sPlayerbotAIConfig.hasLog("bot_events.csv")) { @@ -84,21 +85,21 @@ bool QueryItemUsageAction::Execute(Event& event) } string text = event.getParam(); - ItemIds items = chat->parseItems(text); - for (ItemIds::iterator i = items.begin(); i != items.end(); i++) + set qualifiers = chat->parseItemQualifiers(text); + for (auto qualifier : qualifiers) { - ItemPrototype const *item = sItemStorage.LookupEntry(*i); - if (!item) continue; + ItemQualifier itemQualifier(qualifier); + if (!itemQualifier.GetProto()) continue; - ai->TellMaster(QueryItem(item, 0, GetCount(item)), PlayerbotSecurityLevel::PLAYERBOT_SECURITY_ALLOW_ALL, false); + ai->TellMaster(QueryItem(itemQualifier, 0, GetCount(itemQualifier)), PlayerbotSecurityLevel::PLAYERBOT_SECURITY_ALLOW_ALL, false); } return true; } -uint32 QueryItemUsageAction::GetCount(ItemPrototype const *item) +uint32 QueryItemUsageAction::GetCount(ItemQualifier& qualifier) { uint32 total = 0; - list items = ai->InventoryParseItems(item->Name1); + list items = ai->InventoryParseItems(qualifier.GetProto()->Name1); if (!items.empty()) { for (list::iterator i = items.begin(); i != items.end(); ++i) @@ -109,18 +110,18 @@ uint32 QueryItemUsageAction::GetCount(ItemPrototype const *item) return total; } -string QueryItemUsageAction::QueryItem(ItemPrototype const *item, uint32 count, uint32 total) +string QueryItemUsageAction::QueryItem(ItemQualifier& qualifier, uint32 count, uint32 total) { ostringstream out; #ifdef CMANGOS - string usage = QueryItemUsage(item); + string usage = QueryItemUsage(qualifier); #endif #ifdef MANGOS bool usage = QueryItemUsage(item); #endif - string quest = QueryQuestItem(item->ItemId); - string price = QueryItemPrice(item); - string power = QueryItemPower(item->ItemId); + string quest = QueryQuestItem(qualifier.GetId()); + string price = QueryItemPrice(qualifier); + string power = QueryItemPower(qualifier); #ifdef CMANGOS if (usage.empty()) #endif @@ -129,7 +130,7 @@ string QueryItemUsageAction::QueryItem(ItemPrototype const *item, uint32 count, #endif usage = (quest.empty() ? "Useless" : "Quest"); - out << chat->formatItem(item, count, total) << ": " << usage; + out << chat->formatItem(qualifier.GetProto(), count, total) << ": " << usage; if (!quest.empty()) out << ", " << quest; if (!price.empty()) @@ -138,15 +139,10 @@ string QueryItemUsageAction::QueryItem(ItemPrototype const *item, uint32 count, out << ", " << power; return out.str(); } -#ifdef CMANGOS -string QueryItemUsageAction::QueryItemUsage(ItemPrototype const *item) -#endif -#ifdef MANGOS -bool QueryItemUsageAction::QueryItemUsage(ItemPrototype const *item) -#endif + +string QueryItemUsageAction::QueryItemUsage(ItemQualifier& qualifier) { - ostringstream out; out << item->ItemId; - ItemUsage usage = AI_VALUE2(ItemUsage, "item usage", out.str()); + ItemUsage usage = AI_VALUE2(ItemUsage, "item usage", qualifier.GetQualifier()); switch (usage) { case ITEM_USAGE_EQUIP: @@ -178,16 +174,16 @@ bool QueryItemUsageAction::QueryItemUsage(ItemPrototype const *item) return ""; } -string QueryItemUsageAction::QueryItemPrice(ItemPrototype const *item) +string QueryItemUsageAction::QueryItemPrice(ItemQualifier& qualifier) { if (!sRandomPlayerbotMgr.IsRandomBot(bot)) return ""; - if (item->Bonding == BIND_WHEN_PICKED_UP) + if (qualifier.GetProto()->Bonding == BIND_WHEN_PICKED_UP) return ""; ostringstream msg; - list items = ai->InventoryParseItems(item->Name1); + list items = ai->InventoryParseItems(qualifier.GetProto()->Name1); int32 sellPrice = 0; if (!items.empty()) { @@ -201,12 +197,11 @@ string QueryItemUsageAction::QueryItemPrice(ItemPrototype const *item) msg << "Sell: " << chat->formatMoney(sellPrice); } - ostringstream out; out << item->ItemId; - ItemUsage usage = AI_VALUE2(ItemUsage, "item usage", out.str()); + ItemUsage usage = AI_VALUE2(ItemUsage, "item usage", qualifier.GetQualifier()); if (usage == ITEM_USAGE_NONE) return msg.str(); - int32 buyPrice = auctionbot.GetBuyPrice(item) * sRandomPlayerbotMgr.GetBuyMultiplier(bot); + int32 buyPrice = auctionbot.GetBuyPrice(qualifier.GetProto()) * sRandomPlayerbotMgr.GetBuyMultiplier(bot); if (buyPrice) { if (sellPrice) msg << " "; @@ -259,23 +254,20 @@ string QueryItemUsageAction::QueryQuestItem(uint32 itemId, const Quest *questTem return ""; } -string QueryItemUsageAction::QueryItemPower(uint32 itemId) +string QueryItemUsageAction::QueryItemPower(ItemQualifier& qualifier) { - uint32 power = sRandomItemMgr.GetLiveStatWeight(bot, itemId); + uint32 power = sRandomItemMgr.ItemStatWeight(bot, qualifier); - ItemPrototype const* proto = ObjectMgr::GetItemPrototype(itemId); + ItemPrototype const* proto = ObjectMgr::GetItemPrototype(qualifier.GetId()); if (power) - if (proto) - { - ostringstream out; - char color[32]; - sprintf(color, "%x", ItemQualityColors[proto->Quality]); - out << "power: |h|c" << color << "|h" << to_string(power) << "|h|cffffffff"; - return out.str().c_str(); - } - else - return "power: " + to_string(power); + { + ostringstream out; + char color[32]; + sprintf(color, "%x", ItemQualityColors[qualifier.GetProto()->Quality]); + out << "power: |h|c" << color << "|h" << to_string(power) << "|h|cffffffff"; + return out.str().c_str(); + } return ""; } \ No newline at end of file diff --git a/playerbot/strategy/actions/QueryItemUsageAction.h b/playerbot/strategy/actions/QueryItemUsageAction.h index 59fd7fc7c..d5cb645a9 100644 --- a/playerbot/strategy/actions/QueryItemUsageAction.h +++ b/playerbot/strategy/actions/QueryItemUsageAction.h @@ -3,6 +3,8 @@ namespace ai { + class ItemQualifier; + class QueryItemUsageAction : public ChatCommandAction { public: @@ -10,13 +12,13 @@ namespace ai protected: virtual bool Execute(Event& event) override; - uint32 GetCount(ItemPrototype const *item); - string QueryItem(ItemPrototype const *item, uint32 count, uint32 total); - string QueryItemUsage(ItemPrototype const *item); - string QueryItemPrice(ItemPrototype const *item); + uint32 GetCount(ItemQualifier& qualifier); + string QueryItem(ItemQualifier& qualifier, uint32 count, uint32 total); + string QueryItemUsage(ItemQualifier& qualifier); + string QueryItemPrice(ItemQualifier& qualifier); string QueryQuestItem(uint32 itemId, const Quest *questTemplate, const QuestStatusData *questStatus); string QueryQuestItem(uint32 itemId); - string QueryItemPower(uint32 itemId); + string QueryItemPower(ItemQualifier& qualifier); private: ostringstream out; From 55192355cbe23721c192bfcba6a6100b5afa88e2 Mon Sep 17 00:00:00 2001 From: mostlikely4r Date: Tue, 7 Feb 2023 15:52:36 +0100 Subject: [PATCH 15/26] -Statweight: Add support for random properties when using chatfilters. --- playerbot/ChatFilter.cpp | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/playerbot/ChatFilter.cpp b/playerbot/ChatFilter.cpp index ab9c55f7b..e6569245c 100644 --- a/playerbot/ChatFilter.cpp +++ b/playerbot/ChatFilter.cpp @@ -765,12 +765,12 @@ class UsageChatFilter : public ChatFilter if (item.empty()) return message; - ItemIds ids = ChatHelper::parseItems(item); + set qualifiers = ChatHelper::parseItemQualifiers(item); - if(ids.empty()) + if(qualifiers.empty()) return message; - ItemUsage usage = AI_VALUE2(ItemUsage, "item usage", *ids.begin()); + ItemUsage usage = AI_VALUE2(ItemUsage, "item usage", ItemQualifier(*qualifiers.begin()).GetQualifier()); if (usage != ITEM_USAGE_NONE && usage != ITEM_USAGE_AH && usage != ITEM_USAGE_VENDOR) { @@ -786,12 +786,12 @@ class UsageChatFilter : public ChatFilter if (item.empty()) return message; - ItemIds ids = ChatHelper::parseItems(item); + set qualifiers = ChatHelper::parseItemQualifiers(item); - if (ids.empty()) + if (qualifiers.empty()) return message; - ItemUsage usage = AI_VALUE2(ItemUsage, "item usage", *ids.begin()); + ItemUsage usage = AI_VALUE2(ItemUsage, "item usage", ItemQualifier(*qualifiers.begin()).GetQualifier()); if (usage == ITEM_USAGE_AH || usage == ITEM_USAGE_VENDOR) { @@ -807,14 +807,14 @@ class UsageChatFilter : public ChatFilter if (item.empty()) return message; - ItemIds ids = ChatHelper::parseItems(item); + set qualifiers = ChatHelper::parseItemQualifiers(item); - if (ids.empty()) + if (qualifiers.empty()) return message; - ItemUsage usage = AI_VALUE2(ItemUsage, "item usage", *ids.begin()); + ItemUsage usage = AI_VALUE2(ItemUsage, "item usage", ItemQualifier(*qualifiers.begin()).GetQualifier()); - if (usage == ITEM_USAGE_EQUIP || usage == ITEM_USAGE_REPLACE || usage == ITEM_USAGE_GUILD_TASK || usage == ITEM_USAGE_BAD_EQUIP || (usage == ITEM_USAGE_FORCE && AI_VALUE2(ForceItemUsage, "force item usage", *ids.begin()) == ForceItemUsage::FORCE_USAGE_NEED)) + if (usage == ITEM_USAGE_EQUIP || usage == ITEM_USAGE_REPLACE || usage == ITEM_USAGE_GUILD_TASK || usage == ITEM_USAGE_BAD_EQUIP || (usage == ITEM_USAGE_FORCE && AI_VALUE2(ForceItemUsage, "force item usage", ItemQualifier(*qualifiers.begin()).GetId()) == ForceItemUsage::FORCE_USAGE_NEED)) { return FilterLink(message); } @@ -828,14 +828,14 @@ class UsageChatFilter : public ChatFilter if (item.empty()) return message; - ItemIds ids = ChatHelper::parseItems(item); + set qualifiers = ChatHelper::parseItemQualifiers(item); - if (ids.empty()) + if (qualifiers.empty()) return message; - ItemUsage usage = AI_VALUE2(ItemUsage, "item usage", *ids.begin()); + ItemUsage usage = AI_VALUE2(ItemUsage, "item usage", ItemQualifier(*qualifiers.begin()).GetQualifier()); - if (usage == ITEM_USAGE_SKILL || usage == ITEM_USAGE_USE || usage == ITEM_USAGE_DISENCHANT || usage == ITEM_USAGE_AH || usage == ITEM_USAGE_VENDOR || (usage == ITEM_USAGE_FORCE && AI_VALUE2(ForceItemUsage, "force item usage", *ids.begin()) == ForceItemUsage::FORCE_USAGE_GREED)) + if (usage == ITEM_USAGE_SKILL || usage == ITEM_USAGE_USE || usage == ITEM_USAGE_DISENCHANT || usage == ITEM_USAGE_AH || usage == ITEM_USAGE_VENDOR || (usage == ITEM_USAGE_FORCE && AI_VALUE2(ForceItemUsage, "force item usage", ItemQualifier(*qualifiers.begin()).GetId()) == ForceItemUsage::FORCE_USAGE_GREED)) { return FilterLink(message); } From 630b507eb79d9bff5699dd21e3d33c8ed4062df1 Mon Sep 17 00:00:00 2001 From: mostlikely4r Date: Tue, 7 Feb 2023 15:52:51 +0100 Subject: [PATCH 16/26] -Statweight: Add support for random properties when using auto equip. --- playerbot/strategy/actions/EquipAction.cpp | 27 ++++++++++++++-------- playerbot/strategy/actions/EquipAction.h | 3 +-- 2 files changed, 19 insertions(+), 11 deletions(-) diff --git a/playerbot/strategy/actions/EquipAction.cpp b/playerbot/strategy/actions/EquipAction.cpp index c09c892f4..9905f4af9 100644 --- a/playerbot/strategy/actions/EquipAction.cpp +++ b/playerbot/strategy/actions/EquipAction.cpp @@ -119,20 +119,29 @@ bool EquipUpgradesAction::Execute(Event& event) return false; } - ListItemsVisitor visitor; + + list items; + + FindItemUsageVisitor visitor(bot, ITEM_USAGE_EQUIP); + ai->InventoryIterateItems(&visitor, ITERATE_ITEMS_IN_BAGS); + visitor.SetUsage(ITEM_USAGE_REPLACE); + ai->InventoryIterateItems(&visitor, ITERATE_ITEMS_IN_BAGS); + visitor.SetUsage(ITEM_USAGE_BAD_EQUIP); ai->InventoryIterateItems(&visitor, ITERATE_ITEMS_IN_BAGS); + items = visitor.GetResult(); - ItemIds items; - for (map::iterator i = visitor.items.begin(); i != visitor.items.end(); ++i) + bool didEquip = false; + + for (auto& item : items) { - ItemUsage usage = AI_VALUE2(ItemUsage, "item usage", i->first); + ItemUsage usage = AI_VALUE2(ItemUsage, "item usage", ItemQualifier(item).GetQualifier()); if (usage == ITEM_USAGE_EQUIP || usage == ITEM_USAGE_REPLACE || usage == ITEM_USAGE_BAD_EQUIP) { - sLog.outDetail("Bot #%d <%s> auto equips item %d (%s)", bot->GetGUIDLow(), bot->GetName(), i->first, usage == 1 ? "no item in slot" : usage == 2 ? "replace" : usage == 3 ? "wrong item but empty slot" : ""); - items.insert(i->first); + sLog.outDetail("Bot #%d <%s> auto equips item %d (%s)", bot->GetGUIDLow(), bot->GetName(), item->GetProto()->ItemId, usage == 1 ? "no item in slot" : usage == 2 ? "replace" : usage == 3 ? "wrong item but empty slot" : ""); + EquipItem(*item); + didEquip = true; } } - - EquipItems(items); - return true; + + return didEquip; } diff --git a/playerbot/strategy/actions/EquipAction.h b/playerbot/strategy/actions/EquipAction.h index 771211cc7..896fd3696 100644 --- a/playerbot/strategy/actions/EquipAction.h +++ b/playerbot/strategy/actions/EquipAction.h @@ -9,11 +9,10 @@ namespace ai EquipAction(PlayerbotAI* ai, string name = "equip") : ChatCommandAction(ai, name) {} virtual bool Execute(Event& event) override; void EquipItems(ItemIds ids); - + void EquipItem(Item& item); private: void EquipItem(FindItemVisitor* visitor); uint8 GetSmallestBagSlot(); - void EquipItem(Item& item); }; class EquipUpgradesAction : public EquipAction From e1833a3befae18c7d85778faf94a252a60015560 Mon Sep 17 00:00:00 2001 From: mostlikely4r Date: Tue, 7 Feb 2023 15:53:36 +0100 Subject: [PATCH 17/26] -Internal: Usage item visitor now has support for random properties. --- playerbot/strategy/ItemVisitors.h | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/playerbot/strategy/ItemVisitors.h b/playerbot/strategy/ItemVisitors.h index 45b9fd9f4..fdd6c8124 100644 --- a/playerbot/strategy/ItemVisitors.h +++ b/playerbot/strategy/ItemVisitors.h @@ -21,7 +21,7 @@ namespace ai virtual bool Visit(Item* item) { - if (!Accept(item->GetProto())) + if (!Accept(item->GetProto()) && !Accept(item)) return true; result.push_back(item); @@ -32,6 +32,7 @@ namespace ai protected: virtual bool Accept(const ItemPrototype* proto) = 0; + virtual bool Accept(Item* item) { return false; }; private: list result; @@ -484,9 +485,13 @@ namespace ai public: FindItemUsageVisitor(Player* bot, ItemUsage usage = ITEM_USAGE_NONE) : FindItemVisitor(), bot(bot), usage(usage) { context = bot->GetPlayerbotAI()->GetAiObjectContext();}; - virtual bool Accept(const ItemPrototype* proto) + void SetUsage(ItemUsage newUsage = ITEM_USAGE_NONE) { usage = newUsage; } + + virtual bool Accept(const ItemPrototype* proto) { return false; } + + virtual bool Accept(Item* item) { - if (AI_VALUE2_LAZY(ItemUsage, "item usage", proto->ItemId) == usage) + if (AI_VALUE2_LAZY(ItemUsage, "item usage", ItemQualifier(item).GetQualifier()) == usage) return true; return false; From 8aa01bb179b2ed63cf2edc6338358fbb9d130722 Mon Sep 17 00:00:00 2001 From: mostlikely4r Date: Tue, 7 Feb 2023 15:55:12 +0100 Subject: [PATCH 18/26] -Statweight: Add support for random properties when selling to a vendor. --- playerbot/strategy/actions/SellAction.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/playerbot/strategy/actions/SellAction.cpp b/playerbot/strategy/actions/SellAction.cpp index d4b7660e0..5821aeaea 100644 --- a/playerbot/strategy/actions/SellAction.cpp +++ b/playerbot/strategy/actions/SellAction.cpp @@ -47,9 +47,9 @@ class SellVendorItemsVisitor : public SellItemsVisitor virtual bool Visit(Item* item) { - RESET_AI_VALUE2(ItemUsage, "item usage", item->GetEntry()); //Recheck if we still want to sell this. + RESET_AI_VALUE2(ItemUsage, "item usage", ItemQualifier(item).GetQualifier()); //Recheck if we still want to sell this. - ItemUsage usage = AI_VALUE2(ItemUsage, "item usage", item->GetEntry()); + ItemUsage usage = AI_VALUE2(ItemUsage, "item usage", ItemQualifier(item).GetQualifier()); if (usage != ITEM_USAGE_VENDOR) return true; From 2a78c502ce68fe50f3ead0e79c52d7624b23d0b3 Mon Sep 17 00:00:00 2001 From: mostlikely4r Date: Tue, 7 Feb 2023 15:55:24 +0100 Subject: [PATCH 19/26] -Statweight: Add support for random properties when buying/selling on AH. --- playerbot/strategy/actions/AhAction.cpp | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/playerbot/strategy/actions/AhAction.cpp b/playerbot/strategy/actions/AhAction.cpp index d3ad29041..b85845b74 100644 --- a/playerbot/strategy/actions/AhAction.cpp +++ b/playerbot/strategy/actions/AhAction.cpp @@ -56,10 +56,8 @@ bool AhAction::ExecuteCommand(string text, Unit* auctioneer) for (auto item : items) { - ItemPrototype const* proto = item->GetProto(); - - RESET_AI_VALUE2(ItemUsage, "item usage", proto->ItemId); - if(AI_VALUE2(ItemUsage, "item usage", proto->ItemId) != ITEM_USAGE_AH) + RESET_AI_VALUE2(ItemUsage, "item usage", ItemQualifier(item).GetQualifier()); + if(AI_VALUE2(ItemUsage, "item usage", ItemQualifier(item).GetQualifier()) != ITEM_USAGE_AH) continue; uint32 deposit = AuctionHouseMgr::GetAuctionDeposit(auctionHouseEntry, time, item); @@ -70,7 +68,7 @@ bool AhAction::ExecuteCommand(string text, Unit* auctioneer) if (deposit > freeMoney) return false; - uint32 price = GetSellPrice(proto); + uint32 price = GetSellPrice(item->GetProto()); price *= item->GetCount(); @@ -196,7 +194,7 @@ bool AhBidAction::ExecuteCommand(string text, Unit* auctioneer) uint32 cost = std::min(auction->buyout, uint32(std::max(auction->bid, auction->startbid) * frand(1.05f, 1.25f))); - ItemUsage usage = AI_VALUE2(ItemUsage, "item usage", auction->itemTemplate); + ItemUsage usage = AI_VALUE2(ItemUsage, "item usage", ItemQualifier(auction->itemTemplate, auction->itemRandomPropertyId).GetQualifier()); if (freeMoney.find(usage) == freeMoney.end() || cost > AI_VALUE2(uint32, "free money for", freeMoney[usage])) continue; @@ -242,7 +240,7 @@ bool AhBidAction::ExecuteCommand(string text, Unit* auctioneer) if (!auction) continue; - ItemUsage usage = AI_VALUE2(ItemUsage, "item usage", auction->itemTemplate); + ItemUsage usage = AI_VALUE2(ItemUsage, "item usage", ItemQualifier(auction->itemTemplate, auction->itemRandomPropertyId).GetQualifier()); uint32 price = std::min(auction->buyout, uint32(std::max(auction->bid, auction->startbid) * frand(1.05f, 1.25f))); From 224f7b772b3fc6ac4b53ba5dcf22dcc9bc4f1a9c Mon Sep 17 00:00:00 2001 From: mostlikely4r Date: Tue, 7 Feb 2023 15:56:09 +0100 Subject: [PATCH 20/26] -Statweight: Add support for random properties when looting and rolling on loot. --- playerbot/LootObjectStack.h | 4 +- playerbot/strategy/actions/LootAction.cpp | 18 +++++---- playerbot/strategy/actions/LootAction.h | 2 +- playerbot/strategy/actions/LootRollAction.cpp | 37 +++++++++---------- playerbot/strategy/actions/LootRollAction.h | 4 +- .../strategy/actions/LootStrategyAction.cpp | 18 ++++----- .../strategy/values/LootStrategyValue.cpp | 19 +++++----- playerbot/strategy/values/LootValues.cpp | 2 +- 8 files changed, 53 insertions(+), 51 deletions(-) diff --git a/playerbot/LootObjectStack.h b/playerbot/LootObjectStack.h index 2bec88a22..9d9b540d8 100644 --- a/playerbot/LootObjectStack.h +++ b/playerbot/LootObjectStack.h @@ -4,12 +4,14 @@ using namespace std; namespace ai { + class ItemQualifier; + class LootStrategy { public: LootStrategy() {} virtual ~LootStrategy() {}; - virtual bool CanLoot(ItemPrototype const *proto, AiObjectContext *context) = 0; + virtual bool CanLoot(ItemQualifier& itemQualifier, AiObjectContext *context) = 0; virtual string GetName() = 0; }; diff --git a/playerbot/strategy/actions/LootAction.cpp b/playerbot/strategy/actions/LootAction.cpp index 65e7080e8..bf6acbb20 100644 --- a/playerbot/strategy/actions/LootAction.cpp +++ b/playerbot/strategy/actions/LootAction.cpp @@ -11,6 +11,7 @@ #include "../../GuildTaskMgr.h" #include "../../ServerFacade.h" #include "../values/LootStrategyValue.h" +#include "../values/ItemUsageValue.h" #include "../../ServerFacade.h" @@ -260,6 +261,7 @@ bool StoreLootAction::Execute(Event& event) for (uint8 i = 0; i < items; ++i) { uint32 itemid; + uint32 randomPropertyId; uint32 itemcount; uint8 lootslot_type; uint8 itemindex; @@ -270,7 +272,7 @@ bool StoreLootAction::Execute(Event& event) p >> itemcount; p.read_skip(); // display id p.read_skip(); // randomSuffix - p.read_skip(); // randomPropertyId + p >> randomPropertyId; // randomPropertyId p >> lootslot_type; // 0 = can get, 1 = look only, 2 = master get if (lootslot_type != LOOT_SLOT_NORMAL @@ -280,7 +282,7 @@ bool StoreLootAction::Execute(Event& event) ) continue; - if (loot_type != LOOT_SKINNING && !IsLootAllowed(itemid, ai)) + if (loot_type != LOOT_SKINNING && !IsLootAllowed(ItemQualifier(itemid, randomPropertyId), ai)) continue; if (AI_VALUE2(uint32, "stack space for item", itemid) < itemcount) @@ -333,21 +335,21 @@ bool StoreLootAction::Execute(Event& event) return true; } -bool StoreLootAction::IsLootAllowed(uint32 itemid, PlayerbotAI *ai) +bool StoreLootAction::IsLootAllowed(ItemQualifier& itemQualifier, PlayerbotAI *ai) { AiObjectContext *context = ai->GetAiObjectContext(); LootStrategy* lootStrategy = AI_VALUE(LootStrategy*, "loot strategy"); - ItemPrototype const* proto = sObjectMgr.GetItemPrototype(itemid); + ItemPrototype const* proto = sObjectMgr.GetItemPrototype(itemQualifier.GetId()); if (!proto) return false; set& lootItems = AI_VALUE(set&, "always loot list"); - if (lootItems.find(itemid) != lootItems.end()) + if (lootItems.find(itemQualifier.GetId()) != lootItems.end()) return true; uint32 max = proto->MaxCount; - if (max > 0 && ai->GetBot()->HasItemCount(itemid, max, true)) + if (max > 0 && ai->GetBot()->HasItemCount(itemQualifier.GetId(), max, true)) return false; if (proto->StartQuest) @@ -367,7 +369,7 @@ bool StoreLootAction::IsLootAllowed(uint32 itemid, PlayerbotAI *ai) for (int i = 0; i < 4; i++) { - if (quest->ReqItemId[i] == itemid) + if (quest->ReqItemId[i] == itemQualifier.GetId()) { if (ai->GetMaster() && sPlayerbotAIConfig.syncQuestWithPlayer) return false; //Quest is autocomplete for the bot so no item needed. @@ -383,7 +385,7 @@ bool StoreLootAction::IsLootAllowed(uint32 itemid, PlayerbotAI *ai) // proto->Class == ITEM_CLASS_QUEST) //{ - bool canLoot = lootStrategy->CanLoot(proto, context); + bool canLoot = lootStrategy->CanLoot(itemQualifier, context); //if (canLoot && proto->Bonding == BIND_WHEN_PICKED_UP && ai->HasActivePlayerMaster()) // canLoot = sPlayerbotAIConfig.IsInRandomAccountList(sObjectMgr.GetPlayerAccountIdByGUID(ai->GetBot()->GetObjectGuid())); diff --git a/playerbot/strategy/actions/LootAction.h b/playerbot/strategy/actions/LootAction.h index 5a867ad26..92d619ee3 100644 --- a/playerbot/strategy/actions/LootAction.h +++ b/playerbot/strategy/actions/LootAction.h @@ -30,7 +30,7 @@ namespace ai public: StoreLootAction(PlayerbotAI* ai) : Action(ai, "store loot") {} virtual bool Execute(Event& event) override; - static bool IsLootAllowed(uint32 itemid, PlayerbotAI *ai); + static bool IsLootAllowed(ItemQualifier& itemQualifier, PlayerbotAI *ai); }; class ReleaseLootAction : public MovementAction diff --git a/playerbot/strategy/actions/LootRollAction.cpp b/playerbot/strategy/actions/LootRollAction.cpp index c4586ba9e..a1a6ae003 100644 --- a/playerbot/strategy/actions/LootRollAction.cpp +++ b/playerbot/strategy/actions/LootRollAction.cpp @@ -84,17 +84,17 @@ bool RollAction::Execute(Event& event) for (auto roll : lootRolls) { - ItemPrototype const* proto = GetRollItem(roll.first, roll.second); + ItemQualifier itemQualifier = GetRollItem(roll.first, roll.second); - if (!proto) + if (!itemQualifier.GetId()) continue; - if (!ids.empty() && ids.find(proto->ItemId) == ids.end()) + if (!ids.empty() && ids.find(itemQualifier.GetId()) == ids.end()) continue; RollVote doVote = vote; if (doVote == ROLL_NOT_VALID) //Auto - doVote = CalculateRollVote(proto); + doVote = CalculateRollVote(itemQualifier); rolledItems += RollOnItemInSlot(doVote, roll.first, roll.second); } @@ -107,24 +107,23 @@ bool RollAction::isPossible() return bot->GetGroup() && !AI_VALUE(LootRollMap, "active rolls").empty(); } -ItemPrototype const* RollAction::GetRollItem(ObjectGuid lootGuid, uint32 slot) +ItemQualifier RollAction::GetRollItem(ObjectGuid lootGuid, uint32 slot) { Loot* loot = sLootMgr.GetLoot(bot, lootGuid); if (!loot) - return nullptr; + return ItemQualifier(); LootItem* item = loot->GetLootItemInSlot(slot); if (!item) - return nullptr; + return ItemQualifier(); - return sItemStorage.LookupEntry(item->itemId); + return ItemQualifier(*item); } -RollVote RollAction::CalculateRollVote(ItemPrototype const* proto) +RollVote RollAction::CalculateRollVote(ItemQualifier& itemQualifier) { - ostringstream out; out << proto->ItemId; - ItemUsage usage = AI_VALUE2(ItemUsage, "item usage", out.str()); + ItemUsage usage = AI_VALUE2(ItemUsage, "item usage", itemQualifier.GetQualifier()); RollVote needVote = ROLL_PASS; switch (usage) @@ -143,10 +142,10 @@ RollVote RollAction::CalculateRollVote(ItemPrototype const* proto) needVote = ROLL_GREED; break; case ITEM_USAGE_FORCE: - needVote = (AI_VALUE2(ForceItemUsage, "force item usage", proto->ItemId) == ForceItemUsage::FORCE_USAGE_NEED) ? ROLL_NEED : ROLL_GREED; + needVote = (AI_VALUE2(ForceItemUsage, "force item usage", itemQualifier.GetId()) == ForceItemUsage::FORCE_USAGE_NEED) ? ROLL_NEED : ROLL_GREED; break; } - return StoreLootAction::IsLootAllowed(proto->ItemId, bot->GetPlayerbotAI()) ? needVote : ROLL_PASS; + return StoreLootAction::IsLootAllowed(itemQualifier, bot->GetPlayerbotAI()) ? needVote : ROLL_PASS; } bool RollAction::RollOnItemInSlot(RollVote vote, ObjectGuid lootGuid, uint32 slot) @@ -191,12 +190,12 @@ bool LootRollAction::Execute(Event& event) p >> slot; //number of players invited to roll p >> rollType; //need,greed or pass on roll - ItemPrototype const* proto = GetRollItem(guid, slot); + ItemQualifier itemQualifier = GetRollItem(guid, slot); - if (!proto) + if (!itemQualifier.GetId()) return false; - RollVote vote = CalculateRollVote(proto); + RollVote vote = CalculateRollVote(itemQualifier); return RollOnItemInSlot(vote, guid, slot); } @@ -209,12 +208,12 @@ bool AutoLootRollAction::Execute(Event& event) currentRoll = std::next(currentRoll, urand(0, lootRolls.size() - 1)); - ItemPrototype const* proto = GetRollItem(currentRoll->first, currentRoll->second); + ItemQualifier itemQualifier = GetRollItem(currentRoll->first, currentRoll->second); - if (!proto) + if (!itemQualifier.GetId()) return false; - RollVote vote = CalculateRollVote(proto); + RollVote vote = CalculateRollVote(itemQualifier); return RollOnItemInSlot(vote, currentRoll->first, currentRoll->second); } \ No newline at end of file diff --git a/playerbot/strategy/actions/LootRollAction.h b/playerbot/strategy/actions/LootRollAction.h index 3cba01705..28caf6992 100644 --- a/playerbot/strategy/actions/LootRollAction.h +++ b/playerbot/strategy/actions/LootRollAction.h @@ -47,8 +47,8 @@ namespace ai #endif protected: - virtual ItemPrototype const* GetRollItem(ObjectGuid lootGuid, uint32 slot); - virtual RollVote CalculateRollVote(ItemPrototype const* proto); + virtual ItemQualifier GetRollItem(ObjectGuid lootGuid, uint32 slot); + virtual RollVote CalculateRollVote(ItemQualifier& itemQualifier); virtual bool RollOnItemInSlot(RollVote type, ObjectGuid lootGuid, uint32 slot); }; diff --git a/playerbot/strategy/actions/LootStrategyAction.cpp b/playerbot/strategy/actions/LootStrategyAction.cpp index 5d474f913..6203783f6 100644 --- a/playerbot/strategy/actions/LootStrategyAction.cpp +++ b/playerbot/strategy/actions/LootStrategyAction.cpp @@ -2,6 +2,7 @@ #include "../../playerbot.h" #include "LootStrategyAction.h" #include "../values/LootStrategyValue.h" +#include "../values/ItemUsageValue.h" #include "LootAction.h" #include "PlayerbotAIAware.h" @@ -41,9 +42,9 @@ bool LootStrategyAction::Execute(Event& event) } else { - ItemIds items = chat->parseItems(strategy); + set itemQualifiers = chat->parseItemQualifiers(strategy); - if (items.size() == 0) + if (itemQualifiers.size() == 0) { lootStrategy->Set(LootStrategyValue::instance(strategy)); ostringstream out; @@ -54,22 +55,21 @@ bool LootStrategyAction::Execute(Event& event) bool remove = strategy.size() > 1 && strategy.substr(0, 1) == "-"; bool query = strategy.size() > 1 && strategy.substr(0, 1) == "?"; - for (ItemIds::iterator i = items.begin(); i != items.end(); i++) + for (auto& qualifier : itemQualifiers) { - uint32 itemid = *i; + ItemQualifier itemQualifier(qualifier); if (query) { - ItemPrototype const *proto = sObjectMgr.GetItemPrototype(itemid); - if (proto) + if (itemQualifier.GetProto()) { ostringstream out; - out << (StoreLootAction::IsLootAllowed(itemid, ai) ? "|cFF000000Will loot " : "|c00FF0000Won't loot ") << ChatHelper::formatItem(proto); + out << (StoreLootAction::IsLootAllowed(itemQualifier, ai) ? "|cFF000000Will loot " : "|c00FF0000Won't loot ") << ChatHelper::formatItem(itemQualifier.GetProto()); ai->TellMaster(out.str()); } } else if (remove) { - set::iterator j = alwaysLootItems.find(itemid); + set::iterator j = alwaysLootItems.find(itemQualifier.GetId()); if (j != alwaysLootItems.end()) alwaysLootItems.erase(j); @@ -77,7 +77,7 @@ bool LootStrategyAction::Execute(Event& event) } else { - alwaysLootItems.insert(itemid); + alwaysLootItems.insert(itemQualifier.GetId()); ai->TellMaster("Item(s) added to always loot list"); } } diff --git a/playerbot/strategy/values/LootStrategyValue.cpp b/playerbot/strategy/values/LootStrategyValue.cpp index 1018bea2b..17226e691 100644 --- a/playerbot/strategy/values/LootStrategyValue.cpp +++ b/playerbot/strategy/values/LootStrategyValue.cpp @@ -11,10 +11,9 @@ namespace ai class NormalLootStrategy : public LootStrategy { public: - virtual bool CanLoot(ItemPrototype const *proto, AiObjectContext *context) + virtual bool CanLoot(ItemQualifier& itemQualifier, AiObjectContext *context) { - ostringstream out; out << proto->ItemId; - ItemUsage usage = AI_VALUE2(ItemUsage, "item usage", out.str()); + ItemUsage usage = AI_VALUE2(ItemUsage, "item usage", itemQualifier.GetQualifier()); return usage != ITEM_USAGE_NONE; } virtual string GetName() { return "normal"; } @@ -23,9 +22,9 @@ namespace ai class GrayLootStrategy : public NormalLootStrategy { public: - virtual bool CanLoot(ItemPrototype const *proto, AiObjectContext *context) + virtual bool CanLoot(ItemQualifier& itemQualifier, AiObjectContext *context) { - return NormalLootStrategy::CanLoot(proto, context) || proto->Quality == ITEM_QUALITY_POOR; + return NormalLootStrategy::CanLoot(itemQualifier, context) || itemQualifier.GetProto()->Quality == ITEM_QUALITY_POOR; } virtual string GetName() { return "gray"; } }; @@ -33,11 +32,11 @@ namespace ai class DisenchantLootStrategy : public NormalLootStrategy { public: - virtual bool CanLoot(ItemPrototype const *proto, AiObjectContext *context) + virtual bool CanLoot(ItemQualifier& itemQualifier, AiObjectContext *context) { - return NormalLootStrategy::CanLoot(proto, context) || - (proto->Quality >= ITEM_QUALITY_UNCOMMON && proto->Bonding != BIND_WHEN_PICKED_UP && - (proto->Class == ITEM_CLASS_ARMOR || proto->Class == ITEM_CLASS_WEAPON)); + return NormalLootStrategy::CanLoot(itemQualifier, context) || + (itemQualifier.GetProto()->Quality >= ITEM_QUALITY_UNCOMMON && itemQualifier.GetProto()->Bonding != BIND_WHEN_PICKED_UP && + (itemQualifier.GetProto()->Class == ITEM_CLASS_ARMOR || itemQualifier.GetProto()->Class == ITEM_CLASS_WEAPON)); } virtual string GetName() { return "disenchant"; } }; @@ -45,7 +44,7 @@ namespace ai class AllLootStrategy : public LootStrategy { public: - virtual bool CanLoot(ItemPrototype const *proto, AiObjectContext *context) + virtual bool CanLoot(ItemQualifier& itemQualifier, AiObjectContext *context) { return true; } diff --git a/playerbot/strategy/values/LootValues.cpp b/playerbot/strategy/values/LootValues.cpp index 1f9da0199..b091c7b50 100644 --- a/playerbot/strategy/values/LootValues.cpp +++ b/playerbot/strategy/values/LootValues.cpp @@ -240,7 +240,7 @@ bool ShouldLootObject::Calculate() if (canLootAmount < lItem.count) continue; - if (lootAccess->m_lootType != LOOT_SKINNING && !StoreLootAction::IsLootAllowed(lItem.itemId, ai)) + if (lootAccess->m_lootType != LOOT_SKINNING && !StoreLootAction::IsLootAllowed(ItemQualifier(lItem), ai)) continue; return true; From fab826331e18ea3d14ed28e837760612feb27c11 Mon Sep 17 00:00:00 2001 From: mostlikely4r Date: Tue, 7 Feb 2023 15:56:22 +0100 Subject: [PATCH 21/26] -Statweight: Add support for random properties when trading items. --- playerbot/strategy/actions/TradeStatusAction.cpp | 3 +-- playerbot/strategy/values/TradeValues.cpp | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/playerbot/strategy/actions/TradeStatusAction.cpp b/playerbot/strategy/actions/TradeStatusAction.cpp index 9c5b34643..dc7dd07b3 100644 --- a/playerbot/strategy/actions/TradeStatusAction.cpp +++ b/playerbot/strategy/actions/TradeStatusAction.cpp @@ -211,8 +211,7 @@ bool TradeStatusAction::CheckTrade() item = trader->GetTradeData()->GetItem((TradeSlots)slot); if (item) { - ostringstream out; out << item->GetProto()->ItemId; - ItemUsage usage = AI_VALUE2(ItemUsage, "item usage", out.str()); + ItemUsage usage = AI_VALUE2(ItemUsage, "item usage", ItemQualifier(item).GetQualifier()); if ((botMoney && !auctionbot.GetBuyPrice(item->GetProto())) || usage == ITEM_USAGE_NONE) { ostringstream out; diff --git a/playerbot/strategy/values/TradeValues.cpp b/playerbot/strategy/values/TradeValues.cpp index a60018132..804cad779 100644 --- a/playerbot/strategy/values/TradeValues.cpp +++ b/playerbot/strategy/values/TradeValues.cpp @@ -62,7 +62,7 @@ list ItemsUsefulToGiveValue::Calculate() continue; } - ItemUsage otherUsage = PAI_VALUE2(ItemUsage, "item usage", item->GetEntry()); + ItemUsage otherUsage = PAI_VALUE2(ItemUsage, "item usage", ItemQualifier(item).GetQualifier()); if (std::find(myUsages.begin(), myUsages.end(), otherUsage) == myUsages.end()) giveItems.push_back(item); From 82fd77eb413e5df15ba7ad070d13a598e9269563 Mon Sep 17 00:00:00 2001 From: mostlikely4r Date: Tue, 7 Feb 2023 15:56:55 +0100 Subject: [PATCH 22/26] -Self bot fix: Selfbot no longer switches master to a bot. --- playerbot/PlayerbotAI.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/playerbot/PlayerbotAI.cpp b/playerbot/PlayerbotAI.cpp index 1bb16b56f..cf91fd33c 100644 --- a/playerbot/PlayerbotAI.cpp +++ b/playerbot/PlayerbotAI.cpp @@ -1355,7 +1355,7 @@ void PlayerbotAI::DoNextAction(bool min) Group *group = bot->GetGroup(); // test BG master set - if ((!master || !HasActivePlayerMaster()) && group) + if ((!master || !HasActivePlayerMaster()) && group && !IsRealPlayer()) { PlayerbotAI* ai = bot->GetPlayerbotAI(); From 5d38c218ae5f73c4ea8db8ad508fb40c1d066927 Mon Sep 17 00:00:00 2001 From: mostlikely4r Date: Wed, 8 Feb 2023 22:08:48 +0100 Subject: [PATCH 23/26] -Item link fix: Bots can now chat item-links with proper enchants, random enchantments and gems. --- ahbot/AhBot.cpp | 2 +- playerbot/ChatHelper.cpp | 33 ++++++++++++++++--- playerbot/ChatHelper.h | 4 +++ playerbot/strategy/actions/AhAction.cpp | 8 ++--- playerbot/strategy/actions/BankAction.cpp | 4 +-- playerbot/strategy/actions/BuffAction.cpp | 2 +- .../actions/CastCustomSpellAction.cpp | 2 +- .../strategy/actions/DestroyItemAction.cpp | 2 +- playerbot/strategy/actions/EquipAction.cpp | 22 ++++++------- playerbot/strategy/actions/EquipAction.h | 2 +- playerbot/strategy/actions/GiveItemAction.cpp | 4 +-- .../strategy/actions/GuildBankAction.cpp | 4 +-- playerbot/strategy/actions/LootAction.cpp | 6 ++-- playerbot/strategy/actions/LootRollAction.cpp | 2 +- .../strategy/actions/LootStrategyAction.cpp | 2 +- playerbot/strategy/actions/MailAction.cpp | 4 +-- .../strategy/actions/QueryItemUsageAction.cpp | 9 ++--- playerbot/strategy/actions/RpgSubActions.cpp | 6 ++-- playerbot/strategy/actions/SellAction.cpp | 2 +- playerbot/strategy/actions/SendMailAction.cpp | 4 +-- .../strategy/actions/TradeStatusAction.cpp | 4 +-- playerbot/strategy/actions/UnequipAction.cpp | 12 +++---- playerbot/strategy/actions/UnequipAction.h | 2 +- playerbot/strategy/actions/UseItemAction.cpp | 12 +++---- playerbot/strategy/actions/WhoAction.cpp | 2 +- 25 files changed, 94 insertions(+), 62 deletions(-) diff --git a/ahbot/AhBot.cpp b/ahbot/AhBot.cpp index f0e48951f..58ec866a9 100644 --- a/ahbot/AhBot.cpp +++ b/ahbot/AhBot.cpp @@ -1170,7 +1170,7 @@ void AhBot::CheckSendMail(uint32 bidder, uint32 price, AuctionEntry *entry) Item *item = sAuctionMgr.GetAItem(entry->itemGuidLow); if (!item) return; - body << "I see you posted " << ChatHelper::formatItem(item->GetProto(), item->GetCount()); + body << "I see you posted " << ChatHelper::formatItem(item, item->GetCount()); body << " to the AH and I really need that at the moment. Could you lower your price at least to "; body << ChatHelper::formatMoney(PricingStrategy::RoundPrice(price)) << "? I'll buy it then.\n"; body << "\n"; diff --git a/playerbot/ChatHelper.cpp b/playerbot/ChatHelper.cpp index b2ba4505f..a6f8a959d 100644 --- a/playerbot/ChatHelper.cpp +++ b/playerbot/ChatHelper.cpp @@ -2,6 +2,7 @@ #include "playerbot.h" #include "ChatHelper.h" #include "AiFactory.h" +#include "strategy/values/ItemUsageValue.h" using namespace ai; using namespace std; @@ -397,9 +398,10 @@ string ChatHelper::formatSpell(SpellEntry const *sInfo) return out.str(); } -string ChatHelper::formatItem(ItemPrototype const * proto, int count, int total) +string ChatHelper::formatItem(ItemQualifier& itemQualifier, int count, int total) { char color[32]; + ItemPrototype const* proto = itemQualifier.GetProto(); sprintf(color, "%x", ItemQualityColors[proto->Quality]); ostringstream out; @@ -408,12 +410,25 @@ string ChatHelper::formatItem(ItemPrototype const * proto, int count, int total) if (loc_idx >= 0) { std::string tname; - sObjectMgr.GetItemLocaleStrings(proto->ItemId, loc_idx, &tname); + sObjectMgr.GetItemLocaleStrings(itemQualifier.GetId(), loc_idx, &tname); if (!tname.empty()) name = tname; } - out << "|c" << color << "|Hitem:" << proto->ItemId - << ":0:0:0:0:0:0:0" << "|h[" << name + + if (itemQualifier.GetRandomPropertyId()) + { + ItemRandomPropertiesEntry const* item_rand = sItemRandomPropertiesStore.LookupEntry(abs(itemQualifier.GetRandomPropertyId())); + + if (item_rand) + { + if (loc_idx < 0) + loc_idx = 0; + string suffix = item_rand->nameSuffix[loc_idx]; + name += " " + suffix; + } + } + + out << "|c" << color << "|Hitem:" << itemQualifier.GetLinkQualifier() << "|h[" << name << "]|h|r"; if (count > 1) @@ -425,6 +440,16 @@ string ChatHelper::formatItem(ItemPrototype const * proto, int count, int total) return out.str(); } +string ChatHelper::formatItem(ItemPrototype const* proto, int count, int total) +{ + return formatItem(ItemQualifier(proto->ItemId), count, total); +} + +string ChatHelper::formatItem(Item* item, int count, int total) +{ + return formatItem(ItemQualifier(item), count, total); +} + string ChatHelper::formatQItem(uint32 itemId) { char color[32]; diff --git a/playerbot/ChatHelper.h b/playerbot/ChatHelper.h index a416c6088..36578fd9d 100644 --- a/playerbot/ChatHelper.h +++ b/playerbot/ChatHelper.h @@ -7,6 +7,8 @@ typedef set SpellIds; namespace ai { + class ItemQualifier; + class ChatHelper : public PlayerbotAIAware { public: @@ -18,7 +20,9 @@ namespace ai static string formatQuest(Quest const* quest); + static string formatItem(ItemQualifier& itemQualifier, int count = 0, int total = 0); static string formatItem(ItemPrototype const * proto, int count = 0, int total = 0); + static string formatItem(Item* item, int count = 0, int total = 0); static string formatQItem(uint32 itemId); static ItemIds parseItems(string& text); static set parseItemQualifiers(string& text); diff --git a/playerbot/strategy/actions/AhAction.cpp b/playerbot/strategy/actions/AhAction.cpp index b85845b74..ce35cfe80 100644 --- a/playerbot/strategy/actions/AhAction.cpp +++ b/playerbot/strategy/actions/AhAction.cpp @@ -121,7 +121,7 @@ bool AhAction::PostItem(Item* item, uint32 price, Unit* auctioneer, uint32 time) return false; ostringstream out; - out << "Posting " << ChatHelper::formatItem(proto, cnt) << " for " << ChatHelper::formatMoney(price) << " to the AH"; + out << "Posting " << ChatHelper::formatItem(item, cnt) << " for " << ChatHelper::formatMoney(price) << " to the AH"; ai->TellMasterNoFacing(out.str(), PlayerbotSecurityLevel::PLAYERBOT_SECURITY_ALLOW_ALL, false); return true; } @@ -194,7 +194,7 @@ bool AhBidAction::ExecuteCommand(string text, Unit* auctioneer) uint32 cost = std::min(auction->buyout, uint32(std::max(auction->bid, auction->startbid) * frand(1.05f, 1.25f))); - ItemUsage usage = AI_VALUE2(ItemUsage, "item usage", ItemQualifier(auction->itemTemplate, auction->itemRandomPropertyId).GetQualifier()); + ItemUsage usage = AI_VALUE2(ItemUsage, "item usage", ItemQualifier(auction).GetQualifier()); if (freeMoney.find(usage) == freeMoney.end() || cost > AI_VALUE2(uint32, "free money for", freeMoney[usage])) continue; @@ -240,7 +240,7 @@ bool AhBidAction::ExecuteCommand(string text, Unit* auctioneer) if (!auction) continue; - ItemUsage usage = AI_VALUE2(ItemUsage, "item usage", ItemQualifier(auction->itemTemplate, auction->itemRandomPropertyId).GetQualifier()); + ItemUsage usage = AI_VALUE2(ItemUsage, "item usage", ItemQualifier(auction).GetQualifier()); uint32 price = std::min(auction->buyout, uint32(std::max(auction->bid, auction->startbid) * frand(1.05f, 1.25f))); @@ -327,7 +327,7 @@ bool AhBidAction::BidItem(AuctionEntry* auction, uint32 price, Unit* auctioneer) { ItemPrototype const* proto = sObjectMgr.GetItemPrototype(auction->itemTemplate); ostringstream out; - out << "Bidding " << ChatHelper::formatMoney(price) << " on " << ChatHelper::formatItem(proto, auction->itemCount) << " on the AH"; + out << "Bidding " << ChatHelper::formatMoney(price) << " on " << ChatHelper::formatItem(ItemQualifier(auction), auction->itemCount) << " on the AH"; ai->TellMasterNoFacing(out.str(), PlayerbotSecurityLevel::PLAYERBOT_SECURITY_ALLOW_ALL, false); return true; } diff --git a/playerbot/strategy/actions/BankAction.cpp b/playerbot/strategy/actions/BankAction.cpp index f12515eb3..e1b4e9b98 100644 --- a/playerbot/strategy/actions/BankAction.cpp +++ b/playerbot/strategy/actions/BankAction.cpp @@ -79,7 +79,7 @@ bool BankAction::Withdraw(const uint32 itemid) bot->StoreItem(dest, pItem, true); std::ostringstream out; - out << "got " << chat->formatItem(pItem->GetProto(), pItem->GetCount()) << " from bank"; + out << "got " << chat->formatItem(pItem, pItem->GetCount()) << " from bank"; ai->TellMaster(out.str()); return true; } @@ -99,7 +99,7 @@ bool BankAction::Deposit(Item* pItem) bot->RemoveItem(pItem->GetBagSlot(), pItem->GetSlot(), true); bot->BankItem(dest, pItem, true); - out << "put " << chat->formatItem(pItem->GetProto(), pItem->GetCount()) << " to bank"; + out << "put " << chat->formatItem(pItem, pItem->GetCount()) << " to bank"; ai->TellMaster(out.str()); return true; } diff --git a/playerbot/strategy/actions/BuffAction.cpp b/playerbot/strategy/actions/BuffAction.cpp index 3f8958f9a..a7c052868 100644 --- a/playerbot/strategy/actions/BuffAction.cpp +++ b/playerbot/strategy/actions/BuffAction.cpp @@ -106,7 +106,7 @@ bool BuffAction::Execute(Event& event) { Item* item = *j; ostringstream out; - out << chat->formatItem(item->GetProto(), item->GetCount()); + out << chat->formatItem(item, item->GetCount()); ai->TellMaster(out); } } diff --git a/playerbot/strategy/actions/CastCustomSpellAction.cpp b/playerbot/strategy/actions/CastCustomSpellAction.cpp index 1de3be189..a2c6f9001 100644 --- a/playerbot/strategy/actions/CastCustomSpellAction.cpp +++ b/playerbot/strategy/actions/CastCustomSpellAction.cpp @@ -108,7 +108,7 @@ bool CastCustomSpellAction::Execute(Event& event) ostringstream spellName; spellName << ChatHelper::formatSpell(pSpellInfo) << " on "; if (bot->GetTrader()) spellName << "trade item"; - else if (itemTarget) spellName << chat->formatItem(itemTarget->GetProto()); + else if (itemTarget) spellName << chat->formatItem(itemTarget); else if (target == bot) spellName << "self"; else spellName << target->GetName(); diff --git a/playerbot/strategy/actions/DestroyItemAction.cpp b/playerbot/strategy/actions/DestroyItemAction.cpp index dc3e48445..71ab4e6af 100644 --- a/playerbot/strategy/actions/DestroyItemAction.cpp +++ b/playerbot/strategy/actions/DestroyItemAction.cpp @@ -26,7 +26,7 @@ void DestroyItemAction::DestroyItem(FindItemVisitor* visitor) for (list::iterator i = items.begin(); i != items.end(); ++i) { Item* item = *i; - ostringstream out; out << chat->formatItem(item->GetProto()) << " destroyed"; + ostringstream out; out << chat->formatItem(item) << " destroyed"; bot->DestroyItem(item->GetBagSlot(),item->GetSlot(), true); ai->TellMaster(out, PlayerbotSecurityLevel::PLAYERBOT_SECURITY_ALLOW_ALL, false); } diff --git a/playerbot/strategy/actions/EquipAction.cpp b/playerbot/strategy/actions/EquipAction.cpp index 9905f4af9..a4c031cce 100644 --- a/playerbot/strategy/actions/EquipAction.cpp +++ b/playerbot/strategy/actions/EquipAction.cpp @@ -28,7 +28,7 @@ void EquipAction::EquipItem(FindItemVisitor* visitor) { ai->InventoryIterateItems(visitor); list items = visitor->GetResult(); - if (!items.empty()) EquipItem(**items.begin()); + if (!items.empty()) EquipItem(*items.begin()); } //Return the bag slot with smallest bag @@ -54,20 +54,20 @@ uint8 EquipAction::GetSmallestBagSlot() return curBag; } -void EquipAction::EquipItem(Item& item) +void EquipAction::EquipItem(Item* item) { - uint8 bagIndex = item.GetBagSlot(); - uint8 slot = item.GetSlot(); - uint32 itemId = item.GetProto()->ItemId; + uint8 bagIndex = item->GetBagSlot(); + uint8 slot = item->GetSlot(); + uint32 itemId = item->GetProto()->ItemId; - if (item.GetProto()->InventoryType == INVTYPE_AMMO) + if (item->GetProto()->InventoryType == INVTYPE_AMMO) { bot->SetAmmo(itemId); } else { bool equipedBag = false; - if (item.GetProto()->Class == ITEM_CLASS_CONTAINER || item.GetProto()->Class == ITEM_CLASS_QUIVER) + if (item->GetProto()->Class == ITEM_CLASS_CONTAINER || item->GetProto()->Class == ITEM_CLASS_QUIVER) { Bag* pBag = (Bag*)&item; uint8 newBagSlot = GetSmallestBagSlot(); @@ -75,7 +75,7 @@ void EquipAction::EquipItem(Item& item) { uint16 src = ((bagIndex << 8) | slot); - if (newBagSlot == item.GetBagSlot()) //The new bag is in the slots of the old bag. Move it to the pack first. + if (newBagSlot == item->GetBagSlot()) //The new bag is in the slots of the old bag. Move it to the pack first. { uint16 dst = ((INVENTORY_SLOT_BAG_0 << 8) | INVENTORY_SLOT_ITEM_START); bot->SwapItem(src, dst); @@ -96,9 +96,9 @@ void EquipAction::EquipItem(Item& item) } } - sPlayerbotAIConfig.logEvent(ai, "EquipAction", item.GetProto()->Name1, to_string(item.GetProto()->ItemId)); + sPlayerbotAIConfig.logEvent(ai, "EquipAction", item->GetProto()->Name1, to_string(item->GetProto()->ItemId)); - ostringstream out; out << "equipping " << chat->formatItem(item.GetProto()); + ostringstream out; out << "equipping " << chat->formatItem(item); ai->TellMaster(out, PlayerbotSecurityLevel::PLAYERBOT_SECURITY_ALLOW_ALL, false); } @@ -138,7 +138,7 @@ bool EquipUpgradesAction::Execute(Event& event) if (usage == ITEM_USAGE_EQUIP || usage == ITEM_USAGE_REPLACE || usage == ITEM_USAGE_BAD_EQUIP) { sLog.outDetail("Bot #%d <%s> auto equips item %d (%s)", bot->GetGUIDLow(), bot->GetName(), item->GetProto()->ItemId, usage == 1 ? "no item in slot" : usage == 2 ? "replace" : usage == 3 ? "wrong item but empty slot" : ""); - EquipItem(*item); + EquipItem(item); didEquip = true; } } diff --git a/playerbot/strategy/actions/EquipAction.h b/playerbot/strategy/actions/EquipAction.h index 896fd3696..b2c0e659d 100644 --- a/playerbot/strategy/actions/EquipAction.h +++ b/playerbot/strategy/actions/EquipAction.h @@ -9,7 +9,7 @@ namespace ai EquipAction(PlayerbotAI* ai, string name = "equip") : ChatCommandAction(ai, name) {} virtual bool Execute(Event& event) override; void EquipItems(ItemIds ids); - void EquipItem(Item& item); + void EquipItem(Item* item); private: void EquipItem(FindItemVisitor* visitor); uint8 GetSmallestBagSlot(); diff --git a/playerbot/strategy/actions/GiveItemAction.cpp b/playerbot/strategy/actions/GiveItemAction.cpp index 8c3eba1af..8e7c22ac3 100644 --- a/playerbot/strategy/actions/GiveItemAction.cpp +++ b/playerbot/strategy/actions/GiveItemAction.cpp @@ -42,13 +42,13 @@ bool GiveItemAction::Execute(Event& event) moved = true; ostringstream out; - out << "Got " << chat->formatItem(item->GetProto(), item->GetCount()) << " from " << bot->GetName(); + out << "Got " << chat->formatItem(item, item->GetCount()) << " from " << bot->GetName(); receiverAi->TellMasterNoFacing(out.str(), PlayerbotSecurityLevel::PLAYERBOT_SECURITY_ALLOW_ALL, false); } else { ostringstream out; - out << "Cannot get " << chat->formatItem(item->GetProto(), item->GetCount()) << " from " << bot->GetName() << "- my bags are full"; + out << "Cannot get " << chat->formatItem(item, item->GetCount()) << " from " << bot->GetName() << "- my bags are full"; receiverAi->TellError(out.str()); } } diff --git a/playerbot/strategy/actions/GuildBankAction.cpp b/playerbot/strategy/actions/GuildBankAction.cpp index c3f6a2e1e..ae4b95f5e 100644 --- a/playerbot/strategy/actions/GuildBankAction.cpp +++ b/playerbot/strategy/actions/GuildBankAction.cpp @@ -69,10 +69,10 @@ bool GuildBankAction::MoveFromCharToBank(Item* item, GameObject* bank) // check source pos rights (item moved to bank) if (!guild->IsMemberHaveRights(bot->GetGUIDLow(), 0, GUILD_BANK_RIGHT_DEPOSIT_ITEM)) - out << BOT_TEXT("error_cant_put") << chat->formatItem(item->GetProto()) << BOT_TEXT("error_gbank_rights"); + out << BOT_TEXT("error_cant_put") << chat->formatItem(item) << BOT_TEXT("error_gbank_rights"); else { - out << chat->formatItem(item->GetProto()) << BOT_TEXT("gbank_put"); + out << chat->formatItem(item) << BOT_TEXT("gbank_put"); guild->MoveFromCharToBank(bot, playerBag, playerSlot, 0, 255, 0); } diff --git a/playerbot/strategy/actions/LootAction.cpp b/playerbot/strategy/actions/LootAction.cpp index bf6acbb20..0b9781197 100644 --- a/playerbot/strategy/actions/LootAction.cpp +++ b/playerbot/strategy/actions/LootAction.cpp @@ -275,6 +275,8 @@ bool StoreLootAction::Execute(Event& event) p >> randomPropertyId; // randomPropertyId p >> lootslot_type; // 0 = can get, 1 = look only, 2 = master get + ItemQualifier itemQualifier(itemid, ((int32)randomPropertyId)); + if (lootslot_type != LOOT_SLOT_NORMAL #ifndef MANGOSBOT_ZERO && lootslot_type != LOOT_SLOT_OWNER @@ -282,7 +284,7 @@ bool StoreLootAction::Execute(Event& event) ) continue; - if (loot_type != LOOT_SKINNING && !IsLootAllowed(ItemQualifier(itemid, randomPropertyId), ai)) + if (loot_type != LOOT_SKINNING && !IsLootAllowed(itemQualifier, ai)) continue; if (AI_VALUE2(uint32, "stack space for item", itemid) < itemcount) @@ -317,7 +319,7 @@ bool StoreLootAction::Execute(Event& event) if (proto->Quality > ITEM_QUALITY_NORMAL && !urand(0, 50) && ai->HasStrategy("emote", BotState::BOT_STATE_NON_COMBAT)) ai->PlayEmote(TEXTEMOTE_CHEER); if (proto->Quality >= ITEM_QUALITY_RARE && !urand(0, 1) && ai->HasStrategy("emote", BotState::BOT_STATE_NON_COMBAT)) ai->PlayEmote(TEXTEMOTE_CHEER); - ostringstream out; out << "Looting " << chat->formatItem(proto); + ostringstream out; out << "Looting " << chat->formatItem(itemQualifier); ai->TellMasterNoFacing(out.str(), PlayerbotSecurityLevel::PLAYERBOT_SECURITY_ALLOW_ALL, false); diff --git a/playerbot/strategy/actions/LootRollAction.cpp b/playerbot/strategy/actions/LootRollAction.cpp index a1a6ae003..9b1c535f8 100644 --- a/playerbot/strategy/actions/LootRollAction.cpp +++ b/playerbot/strategy/actions/LootRollAction.cpp @@ -13,7 +13,7 @@ bool LootStartRollAction::Execute(Event& event) uint32 itemSlot; uint32 itemId; uint32 randomSuffix; - uint32 randomPropertyId; + int32 randomPropertyId; #ifdef MANGOSBOT_TWO uint32 mapId; uint32 count; diff --git a/playerbot/strategy/actions/LootStrategyAction.cpp b/playerbot/strategy/actions/LootStrategyAction.cpp index 6203783f6..12fd8f70b 100644 --- a/playerbot/strategy/actions/LootStrategyAction.cpp +++ b/playerbot/strategy/actions/LootStrategyAction.cpp @@ -63,7 +63,7 @@ bool LootStrategyAction::Execute(Event& event) if (itemQualifier.GetProto()) { ostringstream out; - out << (StoreLootAction::IsLootAllowed(itemQualifier, ai) ? "|cFF000000Will loot " : "|c00FF0000Won't loot ") << ChatHelper::formatItem(itemQualifier.GetProto()); + out << (StoreLootAction::IsLootAllowed(itemQualifier, ai) ? "|cFF000000Will loot " : "|c00FF0000Won't loot ") << ChatHelper::formatItem(itemQualifier); ai->TellMaster(out.str()); } } diff --git a/playerbot/strategy/actions/MailAction.cpp b/playerbot/strategy/actions/MailAction.cpp index 874db6d5f..016158e8e 100644 --- a/playerbot/strategy/actions/MailAction.cpp +++ b/playerbot/strategy/actions/MailAction.cpp @@ -43,7 +43,7 @@ class TellMailProcessor : public MailProcessor ItemPrototype const *proto = sObjectMgr.GetItemPrototype(i->item_template); if (proto) { - out << ChatHelper::formatItem(proto, count); + out << ChatHelper::formatItem(item, count); if (!mail->subject.empty()) out << " |cffa0a0a0(" << mail->subject << ")"; } } @@ -112,7 +112,7 @@ class TakeMailProcessor : public MailProcessor #endif Item* item = bot->GetMItem(*i); ostringstream out; - out << mail->subject << ", " << ChatHelper::formatItem(item->GetProto()) << "|cff00ff00 processed"; + out << mail->subject << ", " << ChatHelper::formatItem(item) << "|cff00ff00 processed"; bot->GetSession()->HandleMailTakeItem(packet); ai->TellMaster(out.str(), PlayerbotSecurityLevel::PLAYERBOT_SECURITY_ALLOW_ALL, false); diff --git a/playerbot/strategy/actions/QueryItemUsageAction.cpp b/playerbot/strategy/actions/QueryItemUsageAction.cpp index cedddc472..247bb543e 100644 --- a/playerbot/strategy/actions/QueryItemUsageAction.cpp +++ b/playerbot/strategy/actions/QueryItemUsageAction.cpp @@ -23,7 +23,8 @@ bool QueryItemUsageAction::Execute(Event& event) if (guid != bot->GetObjectGuid()) return false; - uint32 received, created, isShowChatMessage, notUsed, itemId,suffixFactor, itemRandomPropertyId, count; + uint32 received, created, isShowChatMessage, slotId, itemId,suffixFactor, count; + uint32 itemRandomPropertyId; //uint32 invCount; uint8 bagSlot; @@ -32,14 +33,14 @@ bool QueryItemUsageAction::Execute(Event& event) data >> isShowChatMessage; // IsShowChatMessage data >> bagSlot; // item slot, but when added to stack: 0xFFFFFFFF - data >> notUsed; + data >> slotId; data >> itemId; data >> suffixFactor; data >> itemRandomPropertyId; data >> count; // data >> invCount; // [-ZERO] count of items in inventory - ItemQualifier itemQualifier(itemId, itemRandomPropertyId); + ItemQualifier itemQualifier(itemId, (int32)itemRandomPropertyId); if (!itemQualifier.GetProto()) return false; @@ -130,7 +131,7 @@ string QueryItemUsageAction::QueryItem(ItemQualifier& qualifier, uint32 count, u #endif usage = (quest.empty() ? "Useless" : "Quest"); - out << chat->formatItem(qualifier.GetProto(), count, total) << ": " << usage; + out << chat->formatItem(qualifier, count, total) << ": " << usage; if (!quest.empty()) out << ", " << quest; if (!price.empty()) diff --git a/playerbot/strategy/actions/RpgSubActions.cpp b/playerbot/strategy/actions/RpgSubActions.cpp index a39807a2c..2aebb82fc 100644 --- a/playerbot/strategy/actions/RpgSubActions.cpp +++ b/playerbot/strategy/actions/RpgSubActions.cpp @@ -267,7 +267,7 @@ bool RpgTradeUsefulAction::Execute(Event& event) param << chat->formatWorldobject(player); param << " "; - param << chat->formatItem(item->GetProto()); + param << chat->formatItem(item); if (ai->IsRealPlayer() && !bot->GetTradeData()) //Start the trade from the other side to open the window { @@ -286,9 +286,9 @@ bool RpgTradeUsefulAction::Execute(Event& event) if (IsTradingItem(item->GetEntry())) //Did we manage to add the item to the trade? { if (bot->GetGroup() && bot->GetGroup()->IsMember(guidP)) - ai->TellMasterNoFacing("You can use this " + chat->formatItem(item->GetProto()) + " better than me, " + player->GetName() + ".", PlayerbotSecurityLevel::PLAYERBOT_SECURITY_ALLOW_ALL, false); + ai->TellMasterNoFacing("You can use this " + chat->formatItem(item) + " better than me, " + player->GetName() + ".", PlayerbotSecurityLevel::PLAYERBOT_SECURITY_ALLOW_ALL, false); else - bot->Say("You can use this " + chat->formatItem(item->GetProto()) + " better than me, " + player->GetName() + ".", (bot->GetTeam() == ALLIANCE ? LANG_COMMON : LANG_ORCISH)); + bot->Say("You can use this " + chat->formatItem(item) + " better than me, " + player->GetName() + ".", (bot->GetTeam() == ALLIANCE ? LANG_COMMON : LANG_ORCISH)); if (!urand(0, 4) || items.size() < 2) //Complete the trade if we have no more items to trade. { diff --git a/playerbot/strategy/actions/SellAction.cpp b/playerbot/strategy/actions/SellAction.cpp index 5821aeaea..4080220bf 100644 --- a/playerbot/strategy/actions/SellAction.cpp +++ b/playerbot/strategy/actions/SellAction.cpp @@ -119,7 +119,7 @@ void SellAction::Sell(Item* item) bot->SetMoney(botMoney); } - out << "Selling " << chat->formatItem(item->GetProto()); + out << "Selling " << chat->formatItem(item); bot->PlayDistanceSound(120); ai->TellMaster(out, PlayerbotSecurityLevel::PLAYERBOT_SECURITY_ALLOW_ALL, false); break; diff --git a/playerbot/strategy/actions/SendMailAction.cpp b/playerbot/strategy/actions/SendMailAction.cpp index bc85a5629..66410c8ea 100644 --- a/playerbot/strategy/actions/SendMailAction.cpp +++ b/playerbot/strategy/actions/SendMailAction.cpp @@ -116,7 +116,7 @@ bool SendMailAction::Execute(Event& event) if (item->IsSoulBound() || item->IsConjuredConsumable()) { ostringstream out; - out << "Cannot send " << ChatHelper::formatItem(item->GetProto()); + out << "Cannot send " << ChatHelper::formatItem(item); bot->Whisper(out.str(), LANG_UNIVERSAL, tellTo->GetObjectGuid()); continue; } @@ -133,7 +133,7 @@ bool SendMailAction::Execute(Event& event) if (!price) { ostringstream out; - out << ChatHelper::formatItem(item->GetProto()) << ": it is not for sale"; + out << ChatHelper::formatItem(item) << ": it is not for sale"; bot->Whisper(out.str(), LANG_UNIVERSAL, tellTo->GetObjectGuid()); return false; } diff --git a/playerbot/strategy/actions/TradeStatusAction.cpp b/playerbot/strategy/actions/TradeStatusAction.cpp index dc7dd07b3..55da1a5f1 100644 --- a/playerbot/strategy/actions/TradeStatusAction.cpp +++ b/playerbot/strategy/actions/TradeStatusAction.cpp @@ -202,7 +202,7 @@ bool TradeStatusAction::CheckTrade() if (item && !auctionbot.GetSellPrice(item->GetProto())) { ostringstream out; - out << chat->formatItem(item->GetProto()) << " - This is not for sale"; + out << chat->formatItem(item) << " - This is not for sale"; ai->TellMaster(out); ai->PlaySound(TEXTEMOTE_NO); return false; @@ -215,7 +215,7 @@ bool TradeStatusAction::CheckTrade() if ((botMoney && !auctionbot.GetBuyPrice(item->GetProto())) || usage == ITEM_USAGE_NONE) { ostringstream out; - out << chat->formatItem(item->GetProto()) << " - I don't need this"; + out << chat->formatItem(item) << " - I don't need this"; ai->TellMaster(out); ai->PlaySound(TEXTEMOTE_NO); return false; diff --git a/playerbot/strategy/actions/UnequipAction.cpp b/playerbot/strategy/actions/UnequipAction.cpp index dfd4670ff..5b1e73208 100644 --- a/playerbot/strategy/actions/UnequipAction.cpp +++ b/playerbot/strategy/actions/UnequipAction.cpp @@ -22,7 +22,7 @@ bool UnequipAction::Execute(Event& event) if (slot != EQUIPMENT_SLOT_END) { Item* const pItem = bot->GetItemByPos(INVENTORY_SLOT_BAG_0, slot); - if (pItem) UnequipItem(*pItem); + if (pItem) UnequipItem(pItem); } } } @@ -42,13 +42,13 @@ void UnequipAction::UnequipItem(FindItemVisitor* visitor) { ai->InventoryIterateItems(visitor, ITERATE_ALL_ITEMS); list items = visitor->GetResult(); - if (!items.empty()) UnequipItem(**items.begin()); + if (!items.empty()) UnequipItem(*items.begin()); } -void UnequipAction::UnequipItem(Item& item) +void UnequipAction::UnequipItem(Item* item) { - uint8 bagIndex = item.GetBagSlot(); - uint8 slot = item.GetSlot(); + uint8 bagIndex = item->GetBagSlot(); + uint8 slot = item->GetSlot(); uint8 dstBag = NULL_BAG; @@ -56,7 +56,7 @@ void UnequipAction::UnequipItem(Item& item) packet << bagIndex << slot << dstBag; bot->GetSession()->HandleAutoStoreBagItemOpcode(packet); - ostringstream out; out << chat->formatItem(item.GetProto()) << " unequipped"; + ostringstream out; out << chat->formatItem(item) << " unequipped"; ai->TellMaster(out, PlayerbotSecurityLevel::PLAYERBOT_SECURITY_ALLOW_ALL, false); } \ No newline at end of file diff --git a/playerbot/strategy/actions/UnequipAction.h b/playerbot/strategy/actions/UnequipAction.h index c9adca20d..ac48dd33c 100644 --- a/playerbot/strategy/actions/UnequipAction.h +++ b/playerbot/strategy/actions/UnequipAction.h @@ -10,7 +10,7 @@ namespace ai virtual bool Execute(Event& event) override; private: - void UnequipItem(Item& item); + void UnequipItem(Item* item); void UnequipItem(FindItemVisitor* visitor); }; } \ No newline at end of file diff --git a/playerbot/strategy/actions/UseItemAction.cpp b/playerbot/strategy/actions/UseItemAction.cpp index 1a7e93e1f..3ec061bbf 100644 --- a/playerbot/strategy/actions/UseItemAction.cpp +++ b/playerbot/strategy/actions/UseItemAction.cpp @@ -375,7 +375,7 @@ bool UseItemAction::UseItem(Item* item, ObjectGuid goGuid, Item* itemTarget, Uni #endif bool targetSelected = false; - ostringstream out; out << "Using " << chat->formatItem(item->GetProto()); + ostringstream out; out << "Using " << chat->formatItem(item); if ((int)item->GetProto()->Stackable > 1) { uint32 count = item->GetCount(); @@ -419,7 +419,7 @@ bool UseItemAction::UseItem(Item* item, ObjectGuid goGuid, Item* itemTarget, Uni targetFlag = TARGET_FLAG_ITEM; packet << targetFlag; packet.appendPackGUID(itemTarget->GetObjectGuid()); - out << " on " << chat->formatItem(itemTarget->GetProto()); + out << " on " << chat->formatItem(itemTarget); targetSelected = true; #ifndef MANGOSBOT_ZERO } @@ -564,7 +564,7 @@ bool UseItemAction::UseItem(Item* item, ObjectGuid goGuid, Item* itemTarget, Uni packet << targetFlag; packet.appendPackGUID(itemForSpell->GetObjectGuid()); targetSelected = true; - out << " on " << chat->formatItem(itemForSpell->GetProto()); + out << " on " << chat->formatItem(itemForSpell); } Spell *spell = new Spell(bot, pSpellInfo, false); @@ -596,7 +596,7 @@ bool UseItemAction::UseItem(Item* item, ObjectGuid goGuid, Item* itemTarget, Uni void UseItemAction::TellConsumableUse(Item* item, string action, float percent) { ostringstream out; - out << action << " " << chat->formatItem(item->GetProto()); + out << action << " " << chat->formatItem(item); if ((int)item->GetProto()->Stackable > 1) out << "/x" << item->GetCount(); out << " (" << round(percent) << "%)"; ai->TellMasterNoFacing(out.str(), PlayerbotSecurityLevel::PLAYERBOT_SECURITY_ALLOW_ALL, false); @@ -651,8 +651,8 @@ bool UseItemAction::SocketItem(Item* item, Item* gem, bool replace) if (fits) { - ostringstream out; out << "Socketing " << chat->formatItem(item->GetProto()); - out << " with " << chat->formatItem(gem->GetProto()); + ostringstream out; out << "Socketing " << chat->formatItem(item); + out << " with " << chat->formatItem(gem); ai->TellMaster(out, PlayerbotSecurityLevel::PLAYERBOT_SECURITY_ALLOW_ALL, false); bot->GetSession()->HandleSocketOpcode(*packet); diff --git a/playerbot/strategy/actions/WhoAction.cpp b/playerbot/strategy/actions/WhoAction.cpp index ffe003c3b..053c45dce 100644 --- a/playerbot/strategy/actions/WhoAction.cpp +++ b/playerbot/strategy/actions/WhoAction.cpp @@ -81,7 +81,7 @@ string WhoAction::QueryTrade(string text) if (!sellPrice) continue; - out << "Selling " << chat->formatItem(sell->GetProto(), sell->GetCount()) << " for " << chat->formatMoney(sellPrice); + out << "Selling " << chat->formatItem(sell, sell->GetCount()) << " for " << chat->formatMoney(sellPrice); return out.str(); } From 5f3f90d58d226c09da6c7c0c75a12d3494e67f50 Mon Sep 17 00:00:00 2001 From: mostlikely4r Date: Wed, 8 Feb 2023 22:09:59 +0100 Subject: [PATCH 24/26] -Stat weight: Added proper values for gems and stat enchants. --- playerbot/RandomItemMgr.cpp | 222 ++++++++++++++----- playerbot/RandomItemMgr.h | 8 +- playerbot/strategy/values/ItemUsageValue.cpp | 64 ++++-- playerbot/strategy/values/ItemUsageValue.h | 31 ++- 4 files changed, 233 insertions(+), 92 deletions(-) diff --git a/playerbot/RandomItemMgr.cpp b/playerbot/RandomItemMgr.cpp index bb39f2c57..64255aaa8 100644 --- a/playerbot/RandomItemMgr.cpp +++ b/playerbot/RandomItemMgr.cpp @@ -2015,96 +2015,198 @@ uint32 RandomItemMgr::CalculateStatWeight(uint8 playerclass, uint8 spec, ItemPro return statWeight; } -uint32 RandomItemMgr::CalculateRandomPropertyWeight(uint8 playerclass, uint8 spec, uint32 randomPropertyId) +uint32 RandomItemMgr::CalculateEnchantWeight(uint8 playerclass, uint8 spec, uint32 enchantId) { + if (!enchantId) + return 0; + + SpellItemEnchantmentEntry const* pEnchant = sSpellItemEnchantmentStore.LookupEntry(enchantId); + + if (!pEnchant) + return 0; + uint32 weight = 0; - if (randomPropertyId > 0) + + for (int s = 0; s < 3; ++s) { - ItemRandomPropertiesEntry const* item_rand = sItemRandomPropertiesStore.LookupEntry(randomPropertyId); - if (item_rand) + switch (pEnchant->type[s]) { - for (uint32 i = PROP_ENCHANTMENT_SLOT_0; i < PROP_ENCHANTMENT_SLOT_0 + 3; ++i) - { - if (SpellItemEnchantmentEntry const* pEnchant = sSpellItemEnchantmentStore.LookupEntry(item_rand->enchant_id[i - PROP_ENCHANTMENT_SLOT_0])) - { - for (int s = 0; s < 3; ++s) - { - switch (pEnchant->type[s]) - { - case 1: //Proc //TODO add proc values? - break; - case 2: //Damage - if (!pEnchant->amount[s]) - continue; - weight += CalculateSingleStatWeight(playerclass, spec, "mledps", pEnchant->amount[s]); - break; - case 3: - { - if (!pEnchant->spellid[s]) - continue; + case 1: //Proc //TODO add proc values? + break; + case 2: //Damage + if (!pEnchant->amount[s]) + continue; + weight += CalculateSingleStatWeight(playerclass, spec, "mledps", pEnchant->amount[s]); + break; + case 3: + { + if (!pEnchant->spellid[s]) + continue; - SpellEntry const* spellInfo = sSpellTemplate.LookupEntry(pEnchant->spellid[s]); + SpellEntry const* spellInfo = sSpellTemplate.LookupEntry(pEnchant->spellid[s]); - if (!spellInfo) - continue; + if (!spellInfo) + continue; - for (uint32 j = 0; j < MAX_EFFECT_INDEX; ++j) - { - if (spellInfo->Effect[j] != SPELL_EFFECT_APPLY_AURA) - continue; - if (spellInfo->EffectApplyAuraName[j] != SPELL_AURA_MOD_STAT) - continue; + for (uint32 j = 0; j < MAX_EFFECT_INDEX; ++j) + { + if (spellInfo->Effect[j] != SPELL_EFFECT_APPLY_AURA) + continue; + if (spellInfo->EffectApplyAuraName[j] != SPELL_AURA_MOD_STAT) + continue; - uint32 stat = spellInfo->EffectMiscValue[j]; - uint32 value = spellInfo->EffectBasePoints[j] + 1; + uint32 stat = spellInfo->EffectMiscValue[j]; + uint32 value = spellInfo->EffectBasePoints[j] + 1; - if (!value) - continue; + if (!value) + continue; - if (ItemStatLink.find(stat) == ItemStatLink.end()) - continue; + if (ItemStatLink.find(stat) == ItemStatLink.end()) + continue; - weight += CalculateSingleStatWeight(playerclass, spec, ItemStatLink[stat], value); - } - break; - } - case 4: //Armor - if (!pEnchant->amount[s]) - continue; - weight += CalculateSingleStatWeight(playerclass, spec, "armor", pEnchant->amount[s]); - break; - } + weight += CalculateSingleStatWeight(playerclass, spec, ItemStatLink[stat], value); + } + break; + } + case 4: //Armor + if (!pEnchant->amount[s]) + continue; + weight += CalculateSingleStatWeight(playerclass, spec, "armor", pEnchant->amount[s]); + break; + case 5: //Stat + { + for (auto& statLink : weightStatLink) + { + if (statLink.second != pEnchant->spellid[s]) + continue; - } - } + weight += CalculateSingleStatWeight(playerclass, spec, statLink.first, pEnchant->amount[s]); } + break; + } + case 6: //Totem + break; + case 7: //Use Spell + break; + case 8: //Prismatic socket + break; } } + return weight; } -uint32 RandomItemMgr::ItemStatWeight(Player* player, ItemPrototype const* proto, uint32 randomPropertyId, uint32 gem1, uint32 gem2, uint32 gem3, uint32 gem4) +uint32 RandomItemMgr::CalculateRandomPropertyWeight(uint8 playerclass, uint8 spec, int32 randomPropertyId) { - ItemSpecType itSpec; - uint32 weight = CalculateStatWeight(player->getClass(), GetPlayerSpecId(player), proto, itSpec); + uint32 weight = 0; if (randomPropertyId) - weight += CalculateRandomPropertyWeight(player->getClass(), GetPlayerSpecId(player), randomPropertyId); - - //Todo gems. + { + ItemRandomPropertiesEntry const* item_rand = sItemRandomPropertiesStore.LookupEntry(abs(randomPropertyId)); + if (item_rand) + { + for (uint32 i = PROP_ENCHANTMENT_SLOT_0; i < PROP_ENCHANTMENT_SLOT_0 + 3; ++i) + { + uint32 enchantId = item_rand->enchant_id[i - PROP_ENCHANTMENT_SLOT_0]; + weight += CalculateEnchantWeight(playerclass, spec, enchantId); + } + } + } return weight; } -uint32 RandomItemMgr::ItemStatWeight(Player* player, uint32 itemId, uint32 randomPropertyId, uint32 gem1, uint32 gem2, uint32 gem3, uint32 gem4) +uint32 RandomItemMgr::CalculateGemWeight(uint8 playerclass, uint8 spec, uint32 gemId) { - const ItemPrototype* proto = sObjectMgr.GetItemPrototype(itemId); - return ItemStatWeight(player, proto, randomPropertyId, gem1, gem2, gem3, gem4); +#ifdef MANGOSBOT_ZERO + return 0; +#else + if (!gemId) + return 0; + + return CalculateEnchantWeight(playerclass, spec, gemId); +#endif } +uint32 RandomItemMgr::CalculateSocketWeight(uint8 playerclass, ItemQualifier& qualifier, uint8 spec) +{ +#ifdef MANGOSBOT_ZERO + return 0; +#else + + vector gems = { qualifier.GetGem1() , qualifier.GetGem2(), qualifier.GetGem3(), qualifier.GetGem4() }; + + ItemPrototype const* proto = qualifier.GetProto(); + + bool hasGem = false; + for (auto gem : gems) + if(gem) + hasGem = true; + + if (!hasGem) + return 0; + + for (int i = 0; i < MAX_GEM_SOCKETS; ++i) + { + uint8 SocketColor = proto->Socket[i].Color; + + if (!SocketColor) + continue; + + uint8 GemColor = 0; + + if (!gems[i]) + return 0; + + SpellItemEnchantmentEntry const* pEnchant = sSpellItemEnchantmentStore.LookupEntry(gems[i]); + + if (!pEnchant) + return 0; + + uint32 gemid = pEnchant->GemID; + if (!gemid) + return 0; + + ItemPrototype const* gemProto = sItemStorage.LookupEntry(gemid); + if (!gemProto) + return 0; + + GemPropertiesEntry const* gemProperty = sGemPropertiesStore.LookupEntry(gemProto->GemProperties); + + if (!gemProperty) + return 0; + + GemColor = gemProperty->color; + + if (!(GemColor & SocketColor)) + return 0; + } + + return CalculateEnchantWeight(playerclass, spec, proto->socketBonus); +#endif; +} + + uint32 RandomItemMgr::ItemStatWeight(Player* player, ItemQualifier& qualifier) { - return ItemStatWeight(player, qualifier.GetId(), qualifier.GetRandomPropertyId(), qualifier.GetGem1(), qualifier.GetGem2(), qualifier.GetGem3(), qualifier.GetGem4()); + ItemSpecType itSpec; + uint32 weight = CalculateStatWeight(player->getClass(), GetPlayerSpecId(player), qualifier.GetProto(), itSpec); + if(qualifier.GetEnchantId()) + weight += CalculateEnchantWeight(player->getClass(), GetPlayerSpecId(player), qualifier.GetEnchantId()); + if (qualifier.GetRandomPropertyId()) + weight += CalculateRandomPropertyWeight(player->getClass(), GetPlayerSpecId(player), qualifier.GetRandomPropertyId()); + if(qualifier.GetGem1()) + weight += CalculateGemWeight(player->getClass(), GetPlayerSpecId(player), qualifier.GetGem1()); + if (qualifier.GetGem2()) + weight += CalculateGemWeight(player->getClass(), GetPlayerSpecId(player), qualifier.GetGem2()); + if (qualifier.GetGem3()) + weight += CalculateGemWeight(player->getClass(), GetPlayerSpecId(player), qualifier.GetGem3()); + if (qualifier.GetGem4()) + weight += CalculateGemWeight(player->getClass(), GetPlayerSpecId(player), qualifier.GetGem4()); + + weight += CalculateSocketWeight(player->getClass(), qualifier, GetPlayerSpecId(player)); + + return weight; } uint32 RandomItemMgr::ItemStatWeight(Player* player, Item* item) diff --git a/playerbot/RandomItemMgr.h b/playerbot/RandomItemMgr.h index c299e8b9d..125061a32 100644 --- a/playerbot/RandomItemMgr.h +++ b/playerbot/RandomItemMgr.h @@ -166,10 +166,12 @@ class RandomItemMgr uint32 GetRandomTrade(uint32 level); vector GetGemsList(); - uint32 CalculateRandomPropertyWeight(uint8 playerclass, uint8 spec, uint32 randomPropertyId); + uint32 CalculateEnchantWeight(uint8 playerclass, uint8 spec, uint32 enchantId); + uint32 CalculateRandomPropertyWeight(uint8 playerclass, uint8 spec, int32 randomPropertyId); + uint32 CalculateGemWeight(uint8 playerclass, uint8 spec, uint32 gemId); + uint32 CalculateSocketWeight(uint8 playerclass, ItemQualifier& qualifier, uint8 spec); + uint32 CalculateStatWeight(uint8 playerclass, uint8 spec, ItemPrototype const* proto, ItemSpecType &itSpec); - uint32 ItemStatWeight(Player* player, ItemPrototype const* proto, uint32 randomPropertyId = 0, uint32 gem1 = 0, uint32 gem2 = 0, uint32 gem3 = 0, uint32 gem4 = 0); - uint32 ItemStatWeight(Player* player, uint32 itemId, uint32 randomPropertyId = 0, uint32 gem1 = 0, uint32 gem2 = 0, uint32 gem3 = 0, uint32 gem4 = 0); uint32 ItemStatWeight(Player* player, ItemQualifier& qualifier); uint32 ItemStatWeight(Player* player, Item* item); diff --git a/playerbot/strategy/values/ItemUsageValue.cpp b/playerbot/strategy/values/ItemUsageValue.cpp index 12e63dd8c..7a1716d6a 100644 --- a/playerbot/strategy/values/ItemUsageValue.cpp +++ b/playerbot/strategy/values/ItemUsageValue.cpp @@ -11,35 +11,61 @@ using namespace ai; -ItemQualifier::ItemQualifier(string qualifier) +ItemQualifier::ItemQualifier(string qualifier, bool linkQualifier) { vector numbers = Qualified::getMultiQualifiers(qualifier, ":"); itemId = stoi(numbers[0]); - if (numbers.size() > 2) - randomPropertyId = stoi(numbers[2]); - else - randomPropertyId = 0; + enchantId = 0; + randomPropertyId = 0; + gem1 = 0; + gem2 = 0; + gem3 = 0; + gem4 = 0; + proto = nullptr; + +#ifdef MANGOSBOT_ZERO + uint32 propertyPosition = linkQualifier ? 2 : 5; +#else + uint32 propertyPosition = linkQualifier ? 6 : 5; +#endif - if (numbers.size() > 7) - { - gem1 = stoi(numbers[4]); - gem2 = stoi(numbers[5]); - gem3 = stoi(numbers[6]); - gem4 = stoi(numbers[7]); - } - else + if (numbers.size() > 1) + enchantId = stoi(numbers[1]); + + if (numbers.size() > propertyPosition) + randomPropertyId = stoi(numbers[propertyPosition]); + +#ifndef MANGOSBOT_ZERO + uint8 gemPosition = linkQualifier ? 2 : 1; + + if (numbers.size() > gemPosition + 3) { - gem1 = 0; - gem2 = 0; - gem3 = 0; - gem4 = 0; + gem1 = stoi(numbers[gemPosition]); + gem2 = stoi(numbers[gemPosition+1]); + gem3 = stoi(numbers[gemPosition+2]); + gem4 = stoi(numbers[gemPosition+3]); } +#endif; +} + +uint32 ItemQualifier::GemId(Item* item, uint8 gemSlot) +{ +#ifdef MANGOSBOT_ZERO + return 0; +#else + uint32 enchantId = item->GetEnchantmentId(EnchantmentSlot(SOCK_ENCHANTMENT_SLOT + gemSlot)); + + if (!enchantId) + return 0; + + return enchantId; +#endif } ItemUsage ItemUsageValue::Calculate() { - ItemQualifier itemQualifier(qualifier); + ItemQualifier itemQualifier(qualifier,false); uint32 itemId = itemQualifier.GetId(); if (!itemId) return ITEM_USAGE_NONE; @@ -226,7 +252,7 @@ ItemUsage ItemUsageValue::QueryItemUsageForEquip(ItemQualifier& itemQualifier) uint16 dest; - list items = AI_VALUE2(list, "inventory items", chat->formatItem(itemProto)); + list items = AI_VALUE2(list, "inventory items", chat->formatItem(itemQualifier)); InventoryResult result; if (!items.empty()) { diff --git a/playerbot/strategy/values/ItemUsageValue.h b/playerbot/strategy/values/ItemUsageValue.h index cf6bf1d98..e520c00ee 100644 --- a/playerbot/strategy/values/ItemUsageValue.h +++ b/playerbot/strategy/values/ItemUsageValue.h @@ -7,28 +7,39 @@ namespace ai class ItemQualifier { public: - ItemQualifier() { itemId = 0; randomPropertyId = 0; gem1 = 0; gem2 = 0; gem3 = 0; gem4 = 0; }; - ItemQualifier(uint32 itemId, int32 randomPropertyId = 0, uint32 gem1 = 0, uint32 gem2 = 0, uint32 gem3 = 0, uint32 gem4 = 0) : itemId(itemId), randomPropertyId(randomPropertyId), gem1(gem1), gem2(gem2), gem3(gem3), gem4(gem4) {}; - ItemQualifier(Item* item) { itemId = item->GetProto()->ItemId; randomPropertyId = item->GetItemRandomPropertyId(); gem1 = 0; gem2 = 0; gem3 = 0; gem4 = 0; }; //todo Gems. - ItemQualifier(LootItem item) { itemId = item.itemId; randomPropertyId = item.randomPropertyId; gem1 = 0; gem2 = 0; gem3 = 0; gem4 = 0; }; //todo Gems. - - ItemQualifier(string qualifier); + ItemQualifier() { itemId = 0; enchantId = 0; randomPropertyId = 0; gem1 = 0; gem2 = 0; gem3 = 0; gem4 = 0; proto = nullptr; }; + ItemQualifier(uint32 itemId, int32 randomPropertyId = 0, uint32 enchantId = 0, uint32 gem1 = 0, uint32 gem2 = 0, uint32 gem3 = 0, uint32 gem4 = 0) : itemId(itemId), randomPropertyId(randomPropertyId), enchantId(enchantId), gem1(gem1), gem2(gem2), gem3(gem3), gem4(gem4) { proto = nullptr; }; + ItemQualifier(Item* item) { itemId = item->GetProto()->ItemId; enchantId = item->GetEnchantmentId(PERM_ENCHANTMENT_SLOT); randomPropertyId = item->GetItemRandomPropertyId(); gem1 = GemId(item, 0); gem2 = GemId(item, 1); gem3 = GemId(item, 2); gem4 = GemId(item, 3); proto = item->GetProto();}; + ItemQualifier(LootItem item) { itemId = item.itemId; enchantId = 0; randomPropertyId = item.randomPropertyId; gem1 = 0; gem2 = 0; gem3 = 0; gem4 = 0; proto = nullptr; }; + ItemQualifier(AuctionEntry* auction) { itemId = auction->itemTemplate; enchantId = 0; randomPropertyId = auction->itemRandomPropertyId; gem1 = 0; gem2 = 0; gem3 = 0; gem4 = 0; proto = nullptr;}; + ItemQualifier(string qualifier, bool linkQualifier = true); + uint32 GetId() { return itemId; } - uint32 GetRandomPropertyId() { return randomPropertyId; } + uint32 GetEnchantId() { return enchantId; } + int32 GetRandomPropertyId() { return randomPropertyId; } uint32 GetGem1() { return gem1; } uint32 GetGem2() { return gem2; } uint32 GetGem3() { return gem3; } - uint32 GetGem4() { return gem4; } - string GetQualifier() { return to_string(itemId) + ":0:" + to_string(randomPropertyId) + ":0:" + to_string(gem1) + ":" + to_string(gem2) + ":" + to_string(gem3) + ":" + to_string(gem4); } + uint32 GetGem4() { return gem4; } + +#ifdef MANGOSBOT_ZERO + string GetLinkQualifier() { return to_string(itemId) + ":" + to_string(enchantID) + ":" + to_string(randomPropertyId) + ":0"; } +#else + string GetLinkQualifier() { return to_string(itemId) + ":" + to_string(enchantId) + ":" + to_string(gem1) + ":" + to_string(gem2) + ":" + to_string(gem3) + ":" + to_string(gem4) + ":" + to_string(randomPropertyId) + ":0"; } +#endif + string GetQualifier() { return to_string(itemId) + ((enchantId || gem1 || gem2 || gem3 || gem4 || randomPropertyId) ? ":" + to_string(enchantId) + ":" + to_string(gem1) + ":" + to_string(gem2) + ":" + to_string(gem3) + ":" + to_string(gem4) + ":" + to_string(randomPropertyId) : ""); } - ItemPrototype const* GetProto() { ItemPrototype const* proto = sItemStorage.LookupEntry(itemId); return proto; }; + ItemPrototype const* GetProto() { if (!proto) proto = sItemStorage.LookupEntry(itemId); return proto; }; + static uint32 GemId(Item* item, uint8 gemSlot = 0); private: uint32 itemId; + uint32 enchantId; int32 randomPropertyId; uint32 gem1; uint32 gem2; uint32 gem3; uint32 gem4; + ItemPrototype const* proto; }; enum ItemUsage From 5f3510daba2dab75a34005b1107dc176eef00f93 Mon Sep 17 00:00:00 2001 From: mostlikely4r Date: Wed, 8 Feb 2023 22:11:22 +0100 Subject: [PATCH 25/26] -Far formation change: Far formation now makes bots move behind the master. --- playerbot/strategy/values/Formations.cpp | 84 ++++++------------------ 1 file changed, 20 insertions(+), 64 deletions(-) diff --git a/playerbot/strategy/values/Formations.cpp b/playerbot/strategy/values/Formations.cpp index 0e134c416..565a42ec0 100644 --- a/playerbot/strategy/values/Formations.cpp +++ b/playerbot/strategy/values/Formations.cpp @@ -348,77 +348,33 @@ namespace ai { public: FarFormation(PlayerbotAI* ai) : FollowFormation(ai, "far") {} - virtual WorldLocation GetLocation() - { - float range = sPlayerbotAIConfig.farDistance; - float followRange = ai->GetRange("follow"); - + virtual string GetTargetName() { return "master target"; } + virtual float GetAngle() override + { Player* master = ai->GetGroupMaster(); - if (!master || master == bot) - return Formation::NullLocation; - if (sServerFacade.GetDistance2d(bot, master) <= range) - return Formation::NullLocation; + float currentAngle = WorldPosition(master).getAngleTo(bot) - master->GetOrientation(); + float followAngle = sServerFacade.GetChaseAngle(bot); - float angle = master->GetAngle(bot); - float followAngle = GetFollowAngle(); + float delta = (currentAngle - followAngle); - float x = master->GetPositionX() + cos(angle) * range + cos(followAngle) * followRange; - float y = master->GetPositionY() + sin(angle) * range + sin(followAngle) * followRange; - float z = master->GetPositionZ(); -#ifdef MANGOSBOT_TWO - float ground = master->GetMap()->GetHeight(master->GetPhaseMask(), x, y, z); -#else - float ground = master->GetMap()->GetHeight(x, y, z); -#endif - if (ground <= INVALID_HEIGHT) - { - float minDist = 0, minX = 0, minY = 0; - for (double angle = 0.0f; angle <= 2 * M_PI; angle += M_PI / 16.0f) - { - x = master->GetPositionX() + cos(angle) * range + cos(followAngle) * followRange; - y = master->GetPositionY() + sin(angle) * range + sin(followAngle) * followRange; - float dist = sServerFacade.GetDistance2d(bot, x, y); -#ifdef MANGOSBOT_TWO - float ground = master->GetMap()->GetHeight(master->GetPhaseMask(), x, y, z); -#else - float ground = master->GetMap()->GetHeight(x, y, z); -#endif - if (ground > INVALID_HEIGHT && (!minDist || minDist > dist)) - { - minDist = dist; - minX = x; - minY = y; - } - } - if (minDist) - { - if (!bot->IsFlying() && !bot->IsFreeFlying()) - { - z += CONTACT_DISTANCE; - bot->UpdateAllowedPositionZ(minX, minY, z); - } - return WorldLocation(bot->GetMapId(), minX, minY, z); - } + if (delta > M_PI_F) + delta -= M_PI_F * 2.0f; + + if (fabs(delta) > 0.2) + return followAngle + delta; - return Formation::NullLocation; - } + if (followAngle < 0) + followAngle += M_PI_F * 2.0f; - // prevent going into terrain - float ox, oy, oz; - master->GetPosition(ox, oy, oz); -#ifdef MANGOSBOT_TWO - master->GetMap()->GetHitPosition(ox, oy, oz + bot->GetCollisionHeight(), x, y, z, bot->GetPhaseMask(), -0.5f); -#else - master->GetMap()->GetHitPosition(ox, oy, oz + bot->GetCollisionHeight(), x, y, z, -0.5f); -#endif - if (!bot->IsFlying() && !bot->IsFreeFlying()) - { - z += CONTACT_DISTANCE; - bot->UpdateAllowedPositionZ(x, y, z); - } - return WorldLocation(bot->GetMapId(), x, y, z); + delta = (M_PI_F - followAngle) * 0.5; + + if (fabs(delta) < 0.01) + delta = 0; + + return followAngle + delta; } + virtual float GetOffset() override { return ai->GetRange("follow"); } }; class CustomFormation : public MoveAheadFormation From 0ab3ac9558ca21dba6172fbf86202440661be701 Mon Sep 17 00:00:00 2001 From: mostlikely4r Date: Wed, 8 Feb 2023 22:14:13 +0100 Subject: [PATCH 26/26] -Rpg change: Random spells no longer require a game object but can now also be cast on/near players or units. --- playerbot/strategy/actions/CastCustomSpellAction.cpp | 9 ++++++--- playerbot/strategy/triggers/RpgTriggers.cpp | 8 +------- 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/playerbot/strategy/actions/CastCustomSpellAction.cpp b/playerbot/strategy/actions/CastCustomSpellAction.cpp index a2c6f9001..0de9e6d16 100644 --- a/playerbot/strategy/actions/CastCustomSpellAction.cpp +++ b/playerbot/strategy/actions/CastCustomSpellAction.cpp @@ -199,6 +199,8 @@ bool CastRandomSpellAction::Execute(Event& event) if (target && ai->CanCastSpell(spellId, target, true)) spellList.push_back(make_pair(spellId,make_pair(spellPriority, target))); + if (target && ai->CanCastSpell(spellId, target->GetPositionX(), target->GetPositionY(), target->GetPositionZ(), true)) + spellList.push_back(make_pair(spellId, make_pair(spellPriority, target))); if (got && ai->CanCastSpell(spellId, got->GetPositionX(), got->GetPositionY(), got->GetPositionZ(), true)) spellList.push_back(make_pair(spellId, make_pair(spellPriority, got))); if (ai->CanCastSpell(spellId, bot, true)) @@ -249,9 +251,10 @@ bool CastRandomSpellAction::Execute(Event& event) bool CastRandomSpellAction::castSpell(uint32 spellId, WorldObject* wo) { if (wo->GetObjectGuid().IsUnit()) - return ai->CastSpell(spellId, (Unit*)(wo)); - else - return ai->CastSpell(spellId, wo->GetPositionX(), wo->GetPositionY(), wo->GetPositionZ()); + if (ai->CastSpell(spellId, (Unit*)(wo))) + return true; + + return ai->CastSpell(spellId, wo->GetPositionX(), wo->GetPositionY(), wo->GetPositionZ()); } bool DisEnchantRandomItemAction::Execute(Event& event) diff --git a/playerbot/strategy/triggers/RpgTriggers.cpp b/playerbot/strategy/triggers/RpgTriggers.cpp index 518999db3..a9804f1a1 100644 --- a/playerbot/strategy/triggers/RpgTriggers.cpp +++ b/playerbot/strategy/triggers/RpgTriggers.cpp @@ -474,13 +474,7 @@ bool RpgUseTrigger::IsActive() bool RpgSpellTrigger::IsActive() { - GuidPosition guidP(getGuidP()); - - if (guidP.IsPlayer()) - return false; - - if (!guidP.GetWorldObject()) - return false; + //GuidPosition guidP(getGuidP()); return true; }