diff --git a/Core/MultiBotEvery.lua b/Core/MultiBotEvery.lua index 6cf3d6c..eebcec9 100644 --- a/Core/MultiBotEvery.lua +++ b/Core/MultiBotEvery.lua @@ -331,4 +331,56 @@ MultiBot.addEvery = function(pFrame, pCombat, pNormal) if(MultiBot.isInside(pNormal, "food")) then pFrame.getButton("Food").setEnable() end if(MultiBot.isInside(pNormal, "loot")) then pFrame.getButton("Loot").setEnable() end if(MultiBot.isInside(pNormal, "gather")) then pFrame.getButton("Gather").setEnable() end +end + +local function sendCommonCombatStrategy(pButton, command) + local botName = pButton and pButton.getName and pButton.getName() or nil + if type(botName) ~= "string" or botName == "" then + return false + end + + if MultiBot.Comm and type(MultiBot.Comm.RunCombatCommand) == "function" then + return MultiBot.Comm.RunCombatCommand("BOT", botName, command) + end + + SendChatMessage(command, "WHISPER", nil, botName) + return true +end + +local function addCommonCombatStrategyButton(pFrame, pCombat, tFrame, buttonName, y, icon, tipKey, strategyName) + if not pFrame or not tFrame or not tFrame.addButton then + return + end + + local plusCommand = "co +" .. strategyName + local minusCommand = "co -" .. strategyName + + local button = tFrame.addButton( + buttonName, + 0, + y, + "Interface\\Icons\\" .. icon, + MultiBot.L(tipKey) + ):setDisable() + + button.doLeft = function(self) + if MultiBot.OnOffSwitch(self) then + sendCommonCombatStrategy(self, plusCommand) + else + sendCommonCombatStrategy(self, minusCommand) + end + end + + if MultiBot.isInside(pCombat, strategyName) then + pFrame.getButton(buttonName).setEnable() + end +end + +function MultiBot.AddCommonCombatStrategyButtons(pFrame, tFrame, pCombat, yOffset) + local y = tonumber(yOffset) or 0 + + addCommonCombatStrategyButton(pFrame, pCombat, tFrame, "AvoidAoe", y, "spell_shadow_antishadow.blp", "tips.every.strategy.avoidaoe", "avoid aoe") + addCommonCombatStrategyButton(pFrame, pCombat, tFrame, "SaveMana", y + 26, "spell_frost_manarecharge.blp", "tips.every.strategy.savemana", "save mana") + addCommonCombatStrategyButton(pFrame, pCombat, tFrame, "Threat", y + 52, "ability_warrior_challange.blp", "tips.every.strategy.threat", "threat") + addCommonCombatStrategyButton(pFrame, pCombat, tFrame, "Behind", y + 78, "ability_backstab.blp", "tips.every.strategy.behind", "behind") end \ No newline at end of file diff --git a/Locales/MultiBotAceLocale-deDE.lua b/Locales/MultiBotAceLocale-deDE.lua index 3997129..e11cbae 100644 --- a/Locales/MultiBotAceLocale-deDE.lua +++ b/Locales/MultiBotAceLocale-deDE.lua @@ -38,6 +38,19 @@ local deDEValues = { ["tips.rti.bot.action.attack.body"] = "Alle Bots mit gespeichertem persönlichem RTI-Symbol greifen ihr RTI-Ziel an.", ["tips.rti.bot.action.pull.title"] = "Pullen", ["tips.rti.bot.action.pull.body"] = "Alle Bots mit gespeichertem persönlichem RTI-Symbol pullen ihr RTI-Ziel.", + ["tips.main.combat"] = "Kampfstrategien", + ["tips.main.combat.party.avoidaoe"] = "Gruppe: Avoid AoE umschalten.", + ["tips.main.combat.raid.avoidaoe"] = "Raid: Avoid AoE umschalten.", + ["tips.main.combat.party.savemana"] = "Gruppe: Save Mana umschalten.", + ["tips.main.combat.raid.savemana"] = "Raid: Save Mana umschalten.", + ["tips.main.combat.party.threat"] = "Gruppe: Threat umschalten.", + ["tips.main.combat.raid.threat"] = "Raid: Threat umschalten.", + ["tips.main.combat.party.behind"] = "Gruppe: Behind umschalten.", + ["tips.main.combat.raid.behind"] = "Raid: Behind umschalten.", + ["tips.every.strategy.avoidaoe"] = "Avoid AoE", + ["tips.every.strategy.savemana"] = "Save Mana", + ["tips.every.strategy.threat"] = "Threat", + ["tips.every.strategy.behind"] = "Behind", ["info.itemdestroyalert"] = "Möchtest du diesen Gegenstand WIRKLICH zerstören?\n%s", ["info.keydestroyalert"] = "Ich verkaufe keine Schlüssel.", ["info.itemsellalert"] = "Ich kann diesen Gegenstand nicht verkaufen.", diff --git a/Locales/MultiBotAceLocale-enGB.lua b/Locales/MultiBotAceLocale-enGB.lua index a0ac2ef..b56a3a9 100644 --- a/Locales/MultiBotAceLocale-enGB.lua +++ b/Locales/MultiBotAceLocale-enGB.lua @@ -38,6 +38,19 @@ local enGBValues = { ["tips.rti.bot.action.attack.body"] = "All bots with a stored personal RTI icon attack their RTI target.", ["tips.rti.bot.action.pull.title"] = "Pull", ["tips.rti.bot.action.pull.body"] = "All bots with a stored personal RTI icon pull their RTI target.", + ["tips.main.combat"] = "Combat strategies", + ["tips.main.combat.party.avoidaoe"] = "Party: toggle Avoid AoE.", + ["tips.main.combat.raid.avoidaoe"] = "Raid: toggle Avoid AoE.", + ["tips.main.combat.party.savemana"] = "Party: toggle Save Mana.", + ["tips.main.combat.raid.savemana"] = "Raid: toggle Save Mana.", + ["tips.main.combat.party.threat"] = "Party: toggle Threat.", + ["tips.main.combat.raid.threat"] = "Raid: toggle Threat.", + ["tips.main.combat.party.behind"] = "Party: toggle Behind.", + ["tips.main.combat.raid.behind"] = "Raid: toggle Behind.", + ["tips.every.strategy.avoidaoe"] = "Avoid AoE", + ["tips.every.strategy.savemana"] = "Save Mana", + ["tips.every.strategy.threat"] = "Threat", + ["tips.every.strategy.behind"] = "Behind", ["info.itemdestroyalert"] = "Do you REALLY want to destroy this item?\n%s", ["info.keydestroyalert"] = "I will not sell Keys.", ["info.itemsellalert"] = "I cant sell this Item.", diff --git a/Locales/MultiBotAceLocale-enUS.lua b/Locales/MultiBotAceLocale-enUS.lua index 03a04b2..f595d59 100644 --- a/Locales/MultiBotAceLocale-enUS.lua +++ b/Locales/MultiBotAceLocale-enUS.lua @@ -38,6 +38,19 @@ local enUSValues = { ["tips.rti.bot.action.attack.body"] = "All bots with a stored personal RTI icon attack their RTI target.", ["tips.rti.bot.action.pull.title"] = "Pull", ["tips.rti.bot.action.pull.body"] = "All bots with a stored personal RTI icon pull their RTI target.", + ["tips.main.combat"] = "Combat strategies", + ["tips.main.combat.party.avoidaoe"] = "Party: toggle Avoid AoE.", + ["tips.main.combat.raid.avoidaoe"] = "Raid: toggle Avoid AoE.", + ["tips.main.combat.party.savemana"] = "Party: toggle Save Mana.", + ["tips.main.combat.raid.savemana"] = "Raid: toggle Save Mana.", + ["tips.main.combat.party.threat"] = "Party: toggle Threat.", + ["tips.main.combat.raid.threat"] = "Raid: toggle Threat.", + ["tips.main.combat.party.behind"] = "Party: toggle Behind.", + ["tips.main.combat.raid.behind"] = "Raid: toggle Behind.", + ["tips.every.strategy.avoidaoe"] = "Avoid AoE", + ["tips.every.strategy.savemana"] = "Save Mana", + ["tips.every.strategy.threat"] = "Threat", + ["tips.every.strategy.behind"] = "Behind", ["info.itemdestroyalert"] = "Do you REALLY want to destroy this item?\n%s", ["info.keydestroyalert"] = "I will not sell Keys.", ["info.itemsellalert"] = "I cant sell this Item.", diff --git a/Locales/MultiBotAceLocale-esES.lua b/Locales/MultiBotAceLocale-esES.lua index e13239a..9a894a4 100644 --- a/Locales/MultiBotAceLocale-esES.lua +++ b/Locales/MultiBotAceLocale-esES.lua @@ -38,6 +38,19 @@ local esESValues = { ["tips.rti.bot.action.attack.body"] = "Todos los bots con un icono RTI personal guardado atacan su objetivo RTI.", ["tips.rti.bot.action.pull.title"] = "Pull", ["tips.rti.bot.action.pull.body"] = "Todos los bots con un icono RTI personal guardado hacen pull a su objetivo RTI.", + ["tips.main.combat"] = "Estrategias de combate", + ["tips.main.combat.party.avoidaoe"] = "Grupo: alternar Avoid AoE.", + ["tips.main.combat.raid.avoidaoe"] = "Banda: alternar Avoid AoE.", + ["tips.main.combat.party.savemana"] = "Grupo: alternar Save Mana.", + ["tips.main.combat.raid.savemana"] = "Banda: alternar Save Mana.", + ["tips.main.combat.party.threat"] = "Grupo: alternar Threat.", + ["tips.main.combat.raid.threat"] = "Banda: alternar Threat.", + ["tips.main.combat.party.behind"] = "Grupo: alternar Behind.", + ["tips.main.combat.raid.behind"] = "Banda: alternar Behind.", + ["tips.every.strategy.avoidaoe"] = "Avoid AoE", + ["tips.every.strategy.savemana"] = "Save Mana", + ["tips.every.strategy.threat"] = "Threat", + ["tips.every.strategy.behind"] = "Behind", ["info.itemdestroyalert"] = "¿REALMENTE quieres destruir este objeto?\n%s", ["info.keydestroyalert"] = "No venderé llaves.", ["info.itemsellalert"] = "No puedo vender este objeto.", diff --git a/Locales/MultiBotAceLocale-frFR.lua b/Locales/MultiBotAceLocale-frFR.lua index a3b1963..21e576e 100644 --- a/Locales/MultiBotAceLocale-frFR.lua +++ b/Locales/MultiBotAceLocale-frFR.lua @@ -38,6 +38,19 @@ local frFRValues = { ["tips.rti.bot.action.attack.body"] = "Tous les bots avec une icône RTI personnelle mémorisée attaquent leur cible RTI.", ["tips.rti.bot.action.pull.title"] = "Pull", ["tips.rti.bot.action.pull.body"] = "Tous les bots avec une icône RTI personnelle mémorisée pull leur cible RTI.", + ["tips.main.combat"] = "Stratégies de combat", + ["tips.main.combat.party.avoidaoe"] = "Groupe : activer/désactiver Avoid AoE.", + ["tips.main.combat.raid.avoidaoe"] = "Raid : activer/désactiver Avoid AoE.", + ["tips.main.combat.party.savemana"] = "Groupe : activer/désactiver Save Mana.", + ["tips.main.combat.raid.savemana"] = "Raid : activer/désactiver Save Mana.", + ["tips.main.combat.party.threat"] = "Groupe : activer/désactiver Threat.", + ["tips.main.combat.raid.threat"] = "Raid : activer/désactiver Threat.", + ["tips.main.combat.party.behind"] = "Groupe : activer/désactiver Behind.", + ["tips.main.combat.raid.behind"] = "Raid : activer/désactiver Behind.", + ["tips.every.strategy.avoidaoe"] = "Avoid AoE", + ["tips.every.strategy.savemana"] = "Save Mana", + ["tips.every.strategy.threat"] = "Threat", + ["tips.every.strategy.behind"] = "Behind", ["info.itemdestroyalert"] = "Voulez-vous VRAIMENT détruire cet objet ?\n%s", ["info.keydestroyalert"] = "Je ne peux pas vendre des clés.", ["info.itemsellalert"] = "Je ne peux pas vendre cet item.", diff --git a/Locales/MultiBotAceLocale-koKR.lua b/Locales/MultiBotAceLocale-koKR.lua index 3d5f1d9..ea7db72 100644 --- a/Locales/MultiBotAceLocale-koKR.lua +++ b/Locales/MultiBotAceLocale-koKR.lua @@ -38,6 +38,19 @@ local koKRValues = { ["tips.rti.bot.action.attack.body"] = "개인 RTI 아이콘이 저장된 모든 봇이 자신의 RTI 대상을 공격합니다.", ["tips.rti.bot.action.pull.title"] = "풀링", ["tips.rti.bot.action.pull.body"] = "개인 RTI 아이콘이 저장된 모든 봇이 자신의 RTI 대상을 풀링합니다.", + ["tips.main.combat"] = "전투 전략", + ["tips.main.combat.party.avoidaoe"] = "파티: Avoid AoE 전환.", + ["tips.main.combat.raid.avoidaoe"] = "레이드: Avoid AoE 전환.", + ["tips.main.combat.party.savemana"] = "파티: Save Mana 전환.", + ["tips.main.combat.raid.savemana"] = "레이드: Save Mana 전환.", + ["tips.main.combat.party.threat"] = "파티: Threat 전환.", + ["tips.main.combat.raid.threat"] = "레이드: Threat 전환.", + ["tips.main.combat.party.behind"] = "파티: Behind 전환.", + ["tips.main.combat.raid.behind"] = "레이드: Behind 전환.", + ["tips.every.strategy.avoidaoe"] = "Avoid AoE", + ["tips.every.strategy.savemana"] = "Save Mana", + ["tips.every.strategy.threat"] = "Threat", + ["tips.every.strategy.behind"] = "Behind", ["info.itemdestroyalert"] = "이 아이템을 정말로 파기하시겠습니까?\n%s", ["info.keydestroyalert"] = "열쇠는 판매하지 않습니다.", ["info.itemsellalert"] = "이 아이템은 판매할 수 없습니다.", diff --git a/Locales/MultiBotAceLocale-ruRU.lua b/Locales/MultiBotAceLocale-ruRU.lua index 4b82e1b..a3f9b41 100644 --- a/Locales/MultiBotAceLocale-ruRU.lua +++ b/Locales/MultiBotAceLocale-ruRU.lua @@ -38,6 +38,19 @@ local ruRUValues = { ["tips.rti.bot.action.attack.body"] = "Все боты с сохранённой личной RTI-иконкой атакуют свою RTI-цель.", ["tips.rti.bot.action.pull.title"] = "Пулл", ["tips.rti.bot.action.pull.body"] = "Все боты с сохранённой личной RTI-иконкой пуллят свою RTI-цель.", + ["tips.main.combat"] = "Боевые стратегии", + ["tips.main.combat.party.avoidaoe"] = "Группа: переключить Avoid AoE.", + ["tips.main.combat.raid.avoidaoe"] = "Рейд: переключить Avoid AoE.", + ["tips.main.combat.party.savemana"] = "Группа: переключить Save Mana.", + ["tips.main.combat.raid.savemana"] = "Рейд: переключить Save Mana.", + ["tips.main.combat.party.threat"] = "Группа: переключить Threat.", + ["tips.main.combat.raid.threat"] = "Рейд: переключить Threat.", + ["tips.main.combat.party.behind"] = "Группа: переключить Behind.", + ["tips.main.combat.raid.behind"] = "Рейд: переключить Behind.", + ["tips.every.strategy.avoidaoe"] = "Avoid AoE", + ["tips.every.strategy.savemana"] = "Save Mana", + ["tips.every.strategy.threat"] = "Threat", + ["tips.every.strategy.behind"] = "Behind", ["info.itemdestroyalert"] = "Вы ДЕЙСТВИТЕЛЬНО хотите уничтожить этот предмет?\n%s", ["info.keydestroyalert"] = "Я не продаю ключи.", ["info.itemsellalert"] = "Я не могу продать этот предмет.", diff --git a/Locales/MultiBotAceLocale-zhCN.lua b/Locales/MultiBotAceLocale-zhCN.lua index ea9866d..4fb7fed 100644 --- a/Locales/MultiBotAceLocale-zhCN.lua +++ b/Locales/MultiBotAceLocale-zhCN.lua @@ -38,6 +38,19 @@ local zhCNValues = { ["tips.rti.bot.action.attack.body"] = "所有保存了个人 RTI 图标的机器人攻击其 RTI 目标。", ["tips.rti.bot.action.pull.title"] = "拉怪", ["tips.rti.bot.action.pull.body"] = "所有保存了个人 RTI 图标的机器人拉取其 RTI 目标。", + ["tips.main.combat"] = "战斗策略", + ["tips.main.combat.party.avoidaoe"] = "小队:切换 Avoid AoE。", + ["tips.main.combat.raid.avoidaoe"] = "团队:切换 Avoid AoE。", + ["tips.main.combat.party.savemana"] = "小队:切换 Save Mana。", + ["tips.main.combat.raid.savemana"] = "团队:切换 Save Mana。", + ["tips.main.combat.party.threat"] = "小队:切换 Threat。", + ["tips.main.combat.raid.threat"] = "团队:切换 Threat。", + ["tips.main.combat.party.behind"] = "小队:切换 Behind。", + ["tips.main.combat.raid.behind"] = "团队:切换 Behind。", + ["tips.every.strategy.avoidaoe"] = "Avoid AoE", + ["tips.every.strategy.savemana"] = "Save Mana", + ["tips.every.strategy.threat"] = "Threat", + ["tips.every.strategy.behind"] = "Behind", ["info.itemdestroyalert"] = "你真的要销毁这个物品吗?\n%s", ["info.keydestroyalert"] = "我不会出售钥匙。", ["info.itemsellalert"] = "我无法出售该物品。", diff --git a/README.md b/README.md index de845e5..cd796c3 100644 --- a/README.md +++ b/README.md @@ -94,6 +94,8 @@ GET~INVENTORY GET~SPELLBOOK GET~GLYPHS GET~OUTFITS +RUN~RTI +RUN~COMBAT ``` Manual playerbot commands are still intentionally preserved for diagnostics and gameplay actions. @@ -157,6 +159,18 @@ The goal is to remove automatic UI-refresh spam. Outfits Bridge-first listing, create/update, reset, equip and replace + + RTI controls + Bridge-first icon assignment and RTI target actions + + + Pull Control + Bridge-first wait, focus, DPS assist, AoE and RTI pull/attack controls + + + Combat strategy fine tuning + Bridge-first avoid AoE, save mana, threat and behind controls + Units / EveryBars Improved login, reload and AddClass refresh behavior @@ -383,6 +397,9 @@ Implemented bridge-first / chatless areas: - Glyph refresh with icons and glyph tooltips. - Outfits refresh and actions through the bridge. - Outfit equip/replace without detailed `Equipping [item] ...` chat spam. +- RTI controls through the bridge. +- Pull Control frame through the bridge. +- Combat strategy fine tuning through the bridge. - Custom glyph socket mapping and apply order. - Talent tab navigation stability after switching between tabs. - Automatic bot reconnect on login/reload for bots already present in the group or raid. @@ -399,11 +416,12 @@ Kept intentionally: # Remaining Work -The Outfits migration is complete. The next step is final stabilization and cleanup. +The Outfits, RTI, Pull Control and Combat Strategy migrations are implemented. The next step is Disperse, followed by final stabilization and cleanup. Planned follow-up work: -- Regression test login, `/reload`, large raid groups, Units, EveryBars, Stats, Inventory, Spellbook, Talents, Glyphs and Outfits. +- Start the Disperse command migration. +- Regression test login, `/reload`, large raid groups, Units, EveryBars, Stats, Inventory, Spellbook, Talents, Glyphs, Outfits, RTI, Pull Control and Combat Strategies. - Verify that `MultiBot.allowLegacyChatFallback = false` prevents automatic legacy refresh spam on all migrated UI paths. - Keep manual diagnostic commands documented and functional. - Remove obsolete debug prints. diff --git a/Strategies/MultiBotDeathKnight.lua b/Strategies/MultiBotDeathKnight.lua index 79c83dc..660f125 100644 --- a/Strategies/MultiBotDeathKnight.lua +++ b/Strategies/MultiBotDeathKnight.lua @@ -91,6 +91,10 @@ MultiBot.addDeathKnight = function(pFrame, pCombat, pNormal) end end + if MultiBot.AddCommonCombatStrategyButtons then + MultiBot.AddCommonCombatStrategyButtons(pFrame, tDpsFrame, pCombat, 104) + end + -- ASSIST -- pFrame.addButton("TankAssist", -60, 0, "ability_warrior_innerrage", MultiBot.L("tips.deathknight.tankAssist")).setDisable() diff --git a/Strategies/MultiBotDruid.lua b/Strategies/MultiBotDruid.lua index 733cc32..bee9517 100644 --- a/Strategies/MultiBotDruid.lua +++ b/Strategies/MultiBotDruid.lua @@ -163,6 +163,10 @@ MultiBot.addDruid = function(pFrame, pCombat, pNormal) end end + if MultiBot.AddCommonCombatStrategyButtons then + MultiBot.AddCommonCombatStrategyButtons(pFrame, tDpsFrame, pCombat, 156) + end + -- ASSIST -- pFrame.addButton("TankAssist", -120, 0, "ability_warrior_innerrage", MultiBot.L("tips.druid.tankAssist")).setDisable() diff --git a/Strategies/MultiBotHunter.lua b/Strategies/MultiBotHunter.lua index 190bbf9..aa76124 100644 --- a/Strategies/MultiBotHunter.lua +++ b/Strategies/MultiBotHunter.lua @@ -162,6 +162,10 @@ MultiBot.addHunter = function(pFrame, pCombat, pNormal) MultiBot.OnOffActionToTarget(pButton, "co +trap weave,?", "co -trap weave,?", pButton.getName()) end + if MultiBot.AddCommonCombatStrategyButtons then + MultiBot.AddCommonCombatStrategyButtons(pFrame, dpsFrame, pCombat, 130) + end + -- ASSIST -- pFrame.addButton("TankAssist", -90, 0, "ability_warrior_innerrage", MultiBot.L("tips.hunter.tankAssist")).setDisable() diff --git a/Strategies/MultiBotMage.lua b/Strategies/MultiBotMage.lua index d0255fe..30302cc 100644 --- a/Strategies/MultiBotMage.lua +++ b/Strategies/MultiBotMage.lua @@ -141,6 +141,10 @@ MultiBot.addMage = function(pFrame, pCombat, pNormal) end end + if MultiBot.AddCommonCombatStrategyButtons then + MultiBot.AddCommonCombatStrategyButtons(pFrame, dpsControlFrame, pCombat, 52) + end + -- ASSIST -- pFrame.addButton("TankAssist", -90, 0, "ability_warrior_innerrage", MultiBot.L("tips.mage.tankAssist")).setDisable() diff --git a/Strategies/MultiBotPaladin.lua b/Strategies/MultiBotPaladin.lua index ca9a2ef..b59f244 100644 --- a/Strategies/MultiBotPaladin.lua +++ b/Strategies/MultiBotPaladin.lua @@ -322,6 +322,10 @@ MultiBot.addPaladin = function(pFrame, pCombat, pNormal) end end + if MultiBot.AddCommonCombatStrategyButtons then + MultiBot.AddCommonCombatStrategyButtons(pFrame, dpsFrame, pCombat, 130) + end + -- ASSIST -- pFrame.addButton("TankAssist", -150, 0, "ability_warrior_innerrage", MultiBot.L("tips.paladin.tankAssist")).setDisable() diff --git a/Strategies/MultiBotPriest.lua b/Strategies/MultiBotPriest.lua index ed326e6..30e442f 100644 --- a/Strategies/MultiBotPriest.lua +++ b/Strategies/MultiBotPriest.lua @@ -118,6 +118,10 @@ MultiBot.addPriest = function(pFrame, pCombat, pNormal) end end + if MultiBot.AddCommonCombatStrategyButtons then + MultiBot.AddCommonCombatStrategyButtons(pFrame, dpsControlFrame, pCombat, 104) + end + -- ASSIST -- pFrame.addButton("TankAssist", -120, 0, "ability_warrior_innerrage", MultiBot.L("tips.priest.tankAssist")).setDisable() diff --git a/Strategies/MultiBotRogue.lua b/Strategies/MultiBotRogue.lua index 8621c2b..d08d4ae 100644 --- a/Strategies/MultiBotRogue.lua +++ b/Strategies/MultiBotRogue.lua @@ -52,6 +52,10 @@ MultiBot.addRogue = function(pFrame, pCombat, pNormal) MultiBot.OnOffActionToTarget(pButton, "co +boost,?", "co -boost,?", pButton.getName()) end + if MultiBot.AddCommonCombatStrategyButtons then + MultiBot.AddCommonCombatStrategyButtons(pFrame, tFrame, pCombat, 156) + end + -- ASSIST -- pFrame.addButton("TankAssist", -30, 0, "ability_warrior_innerrage", MultiBot.L("tips.rogue.tankAssist")).setDisable() diff --git a/Strategies/MultiBotShaman.lua b/Strategies/MultiBotShaman.lua index 9d5cadc..3ff453c 100644 --- a/Strategies/MultiBotShaman.lua +++ b/Strategies/MultiBotShaman.lua @@ -194,6 +194,10 @@ MultiBot.addShaman = function(pFrame, pCombat, pNormal) end end + if MultiBot.AddCommonCombatStrategyButtons then + MultiBot.AddCommonCombatStrategyButtons(pFrame, dpsControlFrame, pCombat, 78) + end + -- ASSIST -- pFrame.addButton("TankAssist", -150, 0, "ability_warrior_innerrage", MultiBot.L("tips.shaman.tankAssist")).setDisable() diff --git a/Strategies/MultiBotWarlock.lua b/Strategies/MultiBotWarlock.lua index c159919..61b7977 100644 --- a/Strategies/MultiBotWarlock.lua +++ b/Strategies/MultiBotWarlock.lua @@ -400,7 +400,7 @@ MultiBot.addWarlock = function(pFrame, pCombat, pNormal) -- META MELEE (Démonologie) -- local btnMeta = tFrame.addButton( - "MetaMelee", 0, 104, "Spell_Shadow_DemonForm", + "MetaMelee", 0, 105, "Spell_Shadow_DemonForm", (MultiBot.L("tips.warlock.dps.metamelee") ~= "tips.warlock.dps.metamelee" and MultiBot.L("tips.warlock.dps.metamelee") or "Meta Melee") ) btnMeta.setDisable() @@ -409,6 +409,10 @@ MultiBot.addWarlock = function(pFrame, pCombat, pNormal) MultiBot.OnOffActionToTarget(pButton, "co +meta melee,?", "co -meta melee,?", pButton.getName()) end + if MultiBot.AddCommonCombatStrategyButtons then + MultiBot.AddCommonCombatStrategyButtons(pFrame, tFrame, pCombat, 131) + end + -- ASSIST -- pFrame.addButton("TankAssist", -60, 0, "ability_warrior_innerrage", MultiBot.L("tips.warlock.tankAssist")).setDisable() diff --git a/Strategies/MultiBotWarrior.lua b/Strategies/MultiBotWarrior.lua index 4cb13c0..1ef87f3 100644 --- a/Strategies/MultiBotWarrior.lua +++ b/Strategies/MultiBotWarrior.lua @@ -23,6 +23,10 @@ MultiBot.addWarrior = function(pFrame, pCombat, pNormal) end end + if MultiBot.AddCommonCombatStrategyButtons then + MultiBot.AddCommonCombatStrategyButtons(pFrame, tFrame, pCombat, 52) + end + -- ASSIST -- pFrame.addButton("TankAssist", -30, 0, "ability_warrior_innerrage", MultiBot.L("tips.warrior.tankAssist")).setDisable() diff --git a/UI/MultiBotMainUI.lua b/UI/MultiBotMainUI.lua index d613cce..d06c3a8 100644 --- a/UI/MultiBotMainUI.lua +++ b/UI/MultiBotMainUI.lua @@ -718,6 +718,143 @@ local function createPullControlFrame(mainFrame, pullButton) return frame end +local function sendMainCombatStrategy(scope, strategyName, enabled) + local command = "co " .. (enabled and "+" or "-") .. strategyName + + if MultiBot.Comm and type(MultiBot.Comm.RunCombatCommand) == "function" then + return MultiBot.Comm.RunCombatCommand(scope, "", command) + end + + if scope == "ALL" then + SendChatMessage(command, GetNumRaidMembers() > 0 and "RAID" or "PARTY") + else + SendChatMessage(command, "PARTY") + end + + return true +end + +local function showMainCombatStrategyTooltip(owner) + if not owner or not GameTooltip then + return + end + + GameTooltip:SetOwner(owner, "ANCHOR_RIGHT") + GameTooltip:SetText(owner._mbTip or "", 1, 1, 1, true) + GameTooltip:Show() +end + +local function hideMainCombatStrategyTooltip() + if GameTooltip then + GameTooltip:Hide() + end +end + +local function addMainCombatStrategyButton(frame, name, x, y, icon, tipKey, scope, strategyName) + if not frame then + return nil + end + + local button = CreateFrame("Button", "MultiBot" .. name, frame) + button:SetSize(28, 28) + button:SetPoint("TOPLEFT", frame, "TOPLEFT", x, y) + button:RegisterForClicks("LeftButtonDown") + button:SetHighlightTexture("Interface\\Buttons\\ButtonHilight-Square", "ADD") + button:SetPushedTexture("Interface\\Buttons\\UI-Quickslot-Depress") + button._mbTip = MultiBot.L(tipKey) + button._mbScope = scope + button._mbStrategyName = strategyName + + button.icon = button:CreateTexture(nil, "BACKGROUND") + button.icon:SetTexture(MultiBot.SafeTexturePath("Interface\\Icons\\" .. icon)) + button.icon:SetAllPoints(button) + + button.border = button:CreateTexture(nil, "ARTWORK") + button.border:SetTexture("Interface\\AddOns\\MultiBot\\Icons\\border.blp") + button.border:SetPoint("CENTER", button, "CENTER", 0, 0) + button.border:SetSize(34, 34) + button.border:Hide() + + button:SetScript("OnEnter", showMainCombatStrategyTooltip) + button:SetScript("OnLeave", hideMainCombatStrategyTooltip) + button:SetScript("OnClick", function(self) + local enabled = not self.state + if sendMainCombatStrategy(self._mbScope, self._mbStrategyName, enabled) then + setPullControlButtonState(self, enabled) + end + end) + + setPullControlButtonState(button, false) + + frame.buttons = frame.buttons or {} + frame.buttons[name] = button + return button +end + +local function createCombatStrategiesHeader(frame, text, x) + local label = frame:CreateFontString(nil, "ARTWORK") + label:SetFont("Fonts\\ARIALN.ttf", 10, "OUTLINE") + label:SetPoint("TOPLEFT", frame, "TOPLEFT", x, -8) + label:SetWidth(34) + label:SetJustifyH("CENTER") + label:SetText(text) + return label +end + +local function createCombatStrategiesButton(mainFrame) + local controlFrame = CreateFrame("Frame", "MultiBotCombatStrategiesControl", mainFrame) + controlFrame:SetSize(92, 138) + controlFrame:SetFrameLevel((mainFrame:GetFrameLevel() or 0) + 20) + controlFrame:EnableMouse(true) + controlFrame._mbDropdownManaged = true + controlFrame:Hide() + + controlFrame.bg = controlFrame:CreateTexture(nil, "BACKGROUND") + controlFrame.bg:SetTexture("Interface\\Buttons\\WHITE8X8") + controlFrame.bg:SetAllPoints(controlFrame) + controlFrame.bg:SetVertexColor(0, 0, 0, 0.78) + + controlFrame.edge = CreateFrame("Frame", nil, controlFrame) + controlFrame.edge:SetPoint("TOPLEFT", controlFrame, "TOPLEFT", -1, 1) + controlFrame.edge:SetPoint("BOTTOMRIGHT", controlFrame, "BOTTOMRIGHT", 1, -1) + controlFrame.edge:SetBackdrop({ + edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border", + edgeSize = 12, + }) + controlFrame.edge:SetBackdropBorderColor(0.85, 0.85, 0.85, 0.85) + + createCombatStrategiesHeader(controlFrame, "Party", 10) + createCombatStrategiesHeader(controlFrame, "Raid", 50) + + addMainCombatStrategyButton(controlFrame, "CombatPartyAvoidAoe", 12, -26, "spell_shadow_antishadow", "tips.main.combat.party.avoidaoe", "GROUP", "avoid aoe") + addMainCombatStrategyButton(controlFrame, "CombatRaidAvoidAoe", 52, -26, "spell_shadow_antishadow.blp", "tips.main.combat.raid.avoidaoe", "ALL", "avoid aoe") + addMainCombatStrategyButton(controlFrame, "CombatPartySaveMana", 12, -54, "spell_frost_manarecharge.blp", "tips.main.combat.party.savemana", "GROUP", "save mana") + addMainCombatStrategyButton(controlFrame, "CombatRaidSaveMana", 52, -54, "spell_frost_manarecharge.blp", "tips.main.combat.raid.savemana", "ALL", "save mana") + addMainCombatStrategyButton(controlFrame, "CombatPartyThreat", 12, -82, "ability_warrior_challange.blp", "tips.main.combat.party.threat", "GROUP", "threat") + addMainCombatStrategyButton(controlFrame, "CombatRaidThreat", 52, -82, "ability_warrior_challange.blp", "tips.main.combat.raid.threat", "ALL", "threat") + addMainCombatStrategyButton(controlFrame, "CombatPartyBehind", 12, -110, "ability_backstab.blp", "tips.main.combat.party.behind", "GROUP", "behind") + addMainCombatStrategyButton(controlFrame, "CombatRaidBehind", 52, -110, "ability_backstab.blp", "tips.main.combat.raid.behind", "ALL", "behind") + + return createMainActionButton(mainFrame, { + name = "CombatStrategies", + y = 340, + icon = "ability_warrior_battleshout", + tip = "tips.main.combat", + doLeft = function(button) + if controlFrame:IsShown() then + controlFrame:Hide() + button:setDisable() + return + end + + controlFrame:ClearAllPoints() + controlFrame:SetPoint("RIGHT", button, "LEFT", -6, 0) + controlFrame:Show() + button:setEnable() + end, + }) +end + local function saveMultiBarPosition() local multiBar = MultiBot.frames and MultiBot.frames["MultiBar"] if not multiBar or not MultiBot.SetSavedLayoutValue or not MultiBot.toPoint then @@ -1086,6 +1223,7 @@ function MultiBot.InitializeMainUI(tMultiBar) "Release", "Stats", "Reward", + "CombatStrategies", "PullControl", "Reset", "Actions", @@ -1255,6 +1393,9 @@ function MultiBot.InitializeMainUI(tMultiBar) local rewardButton = createRewardButton(mainFrame) wireShiftRightSwap(rewardButton, "Reward") + + local combatStrategiesButton = createCombatStrategiesButton(mainFrame) + wireShiftRightSwap(combatStrategiesButton, "CombatStrategies") local pullControlButton = createMainActionButton(mainFrame, { name = "PullControl", diff --git a/docs/multibot_missing_commands_roadmap.md b/docs/multibot_missing_commands_roadmap.md index 68c6570..79ae027 100644 --- a/docs/multibot_missing_commands_roadmap.md +++ b/docs/multibot_missing_commands_roadmap.md @@ -9,7 +9,7 @@ Ce document suit les commandes `mod-playerbots` encore intéressantes à intégr - les commandes serveur/admin à ne pas intégrer dans l'addon ; - les priorités d'intégration bridge-first/chatless. -Le principe reste le même que pour Inventory, Spellbook, Glyphs, Talents, Stats, Quests, Outfits, RTI et Pull Control : +Le principe reste le même que pour Inventory, Spellbook, Glyphs, Talents, Stats, Quests, Outfits, RTI, Pull Control et Combat Strategies : **éviter le spam chat automatique**, utiliser le bridge quand c'est possible, et conserver les commandes manuelles utiles comme `who`, `co ?`, `nc ?`, `ss ?`. --- @@ -31,10 +31,13 @@ Le principe reste le même que pour Inventory, Spellbook, Glyphs, Talents, Stats | Outfits | Fait | Endpoint bridge + commandes outfits intégrées. | | RTI / Target Icons | Fait | UI complète + bridge `RUN~RTI`, scopes `ALL`, `GROUP`, `BOT`. | | Pull Control | Fait / à fignoler | Mini-frame MainBar + bridge `RUN~COMBAT`, séquences de commandes, scopes et presets. | +| Combat Strategies | Fait / à tester large | Toggles individuels dans les EveryBars + mini-frame Party/Raid, via `RUN~COMBAT`. | --- -## Dernier lot terminé : Pull Control +## Derniers lots terminés : Pull Control + Combat Strategies + +### Pull Control Le panneau `Pull Control` a été ajouté comme mini-frame ouverte depuis la MainBar. Il utilise le bridge et ne repose pas sur le parsing chat automatique. @@ -71,11 +74,35 @@ Fonctionnalités terminées : - vérification de chaque preset en conditions réelles donjon/raid ; - éventuelle sauvegarde persistante du dernier scope et de la dernière valeur de wait. +### Combat Strategies + +Le bloc `Combat Strategies` a été ajouté pour les stratégies permanentes ou semi-permanentes qui ne sont pas strictement liées au pull. + +Fonctionnalités terminées : + +- les stratégies déjà couvertes par des boutons dédiés n'ont pas été dupliquées inutilement ; +- les nouvelles stratégies ajoutées sont : + - `co +avoid aoe` / `co -avoid aoe` ; + - `co +save mana` / `co -save mana` ; + - `co +threat` / `co -threat` ; + - `co +behind` / `co -behind` ; +- côté individuel, les toggles sont ajoutés dans le menu/mini-frame déjà lié au bouton `Strategies DPS` de chaque EveryBar ; +- côté groupe/raid, une mini-frame séparée depuis la MainBar permet d'appliquer les mêmes toggles en scope `GROUP` ou `ALL` ; +- le bridge accepte maintenant ces commandes dans la whitelist `RUN~COMBAT` ; +- les commandes sont routées sans parsing automatique du retour `co ?`. + +Choix volontaire : + +- `co +tank face` / `co -tank face` n'a pas été ajouté ici, car le comportement tank a déjà des boutons dédiés dans les EveryBars ; +- `co +healer dps` / `co -healer dps` n'a pas été ajouté ici, car un contrôle dédié existe déjà côté EveryBar ; +- `co +boost` / `co -boost` n'a pas été ajouté ici, car `Boost` est déjà exposé ailleurs. + Notes fonctionnelles importantes : - Les commandes `co ?`, `nc ?`, `ss ?` restent manuelles et ne doivent pas devenir une source de parsing automatique. -- Le fait qu'une stratégie apparaisse ou non dans `co ?` dépend du nom exact réellement reconnu côté playerbots. Pour le Pull Control, on garde les noms qui ont été validés via bridge/serveur. +- Le fait qu'une stratégie apparaisse ou non dans `co ?` dépend du nom exact réellement reconnu côté playerbots. - `wait for attack time X` n'est pas une stratégie `co`, donc il ne faut pas attendre qu'elle apparaisse forcément dans `co ?`. +- Pour les nouvelles stratégies, le test principal est le comportement réel du bot et la présence d'un `COMBAT_ACK` côté bridge, pas uniquement l'affichage dans `co ?`. --- @@ -139,7 +166,7 @@ RUN~RTI~BOT~Dollu~~attack rti target - Le reset remet l'icône par défaut. - `Attack` / `Pull` déclenchent les groupes configurés ou tous les bots selon le scope. -#### Everybar de bot +#### EveryBar de bot ```text [RTI bot] @@ -148,19 +175,21 @@ RUN~RTI~BOT~Dollu~~attack rti target - Chaque bot peut recevoir son RTI personnel. - Le choix est mémorisé visuellement pendant l'ouverture/fermeture des Units. -- Le bouton batch de la main bar permet d'envoyer tous les bots personnels en même temps. +- Le bouton batch de la MainBar permet d'envoyer tous les bots personnels en même temps. -#### Main bar +#### MainBar ```text -[Bot RTI Action] [Pull Control] [Attaque Tank] +[Bot RTI Action] [Pull Control] [Combat Strategies] [Attaque Tank] └─ Bot RTI Action : Attaquer / Pull pour les bots ayant une RTI personnelle └─ Pull Control : mini-frame de pull, wait, focus, assist, AoE et actions RTI + └─ Combat Strategies : mini-frame Party/Raid pour stratégies combat avancées ``` - `Attaquer` envoie `attack rti target` à tous les bots ayant une RTI personnelle mémorisée. - `Pull` envoie `pull rti target` à tous les bots ayant une RTI personnelle mémorisée. - `Pull Control` permet d'envoyer les mêmes actions RTI avec un scope choisi. +- `Combat Strategies` permet d'appliquer les stratégies avancées au groupe ou au raid. --- @@ -217,55 +246,77 @@ RUN~COMBAT~GROUP~~~wait for attack time 3 ### Statut -**Prochaine étape logique.** +**Terminé côté MultiBot + bridge, à tester en conditions réelles.** -Pull Control a posé la base technique : l'addon sait maintenant envoyer des commandes combat simples via `RUN~COMBAT`. La suite naturelle est donc d'exposer les stratégies combat utiles qui ne sont pas strictement liées au pull. +Pull Control a posé la base technique avec `RUN~COMBAT`. Cette étape a été utilisée pour exposer les stratégies combat utiles qui ne sont pas strictement liées au pull. ### Pourquoi -Ces stratégies existent côté playerbots mais ne sont pas toutes exposées clairement dans MultiBot. Elles ont une vraie utilité en raid/donjon, mais ne doivent pas encombrer la MainBar. +Ces stratégies existent côté playerbots mais n'étaient pas toutes exposées clairement dans MultiBot. Elles ont une vraie utilité en raid/donjon, mais ne doivent pas encombrer la MainBar. -### Commandes à couvrir +### Commandes couvertes | Stratégie | Commande | Statut MultiBot | Priorité | Intérêt | |---|---|---:|---:|---| -| Avoid AoE | `co +avoid aoe` / `co -avoid aoe` | À vérifier / probablement partiel | Haute | Évite les AoE dangereuses | -| Save Mana | `co +save mana` / `co -save mana` | Manquant | Haute | Gestion mana healers/casters | -| Threat | `co +threat` / `co -threat` | Manquant | Haute | Limite la prise d'aggro selon comportement playerbots | -| Tank Face | `co +tank face` / `co -tank face` | Manquant | Moyenne | Gestion orientation tank, cleaves, breaths | -| Behind | `co +behind` / `co -behind` | Manquant ou déjà visible selon states | Moyenne | Placement melee derrière la cible | -| Healer DPS | `co +healer dps` / `co -healer dps` | À vérifier | Moyenne | Autorise/interdit le DPS des healers | -| Boost | `co +boost` / `co -boost` | Probablement partiel | Moyenne | Burst cooldowns | +| Avoid AoE | `co +avoid aoe` / `co -avoid aoe` | Fait | Haute | Évite les AoE dangereuses | +| Save Mana | `co +save mana` / `co -save mana` | Fait | Haute | Gestion mana healers/casters | +| Threat | `co +threat` / `co -threat` | Fait | Haute | Limite la prise d'aggro selon comportement playerbots | +| Behind | `co +behind` / `co -behind` | Fait | Moyenne | Placement melee derrière la cible | +| Tank Face | `co +tank face` / `co -tank face` | Non ajouté volontairement | Moyenne | Déjà couvert par boutons dédiés EveryBar | +| Healer DPS | `co +healer dps` / `co -healer dps` | Non ajouté volontairement | Moyenne | Déjà couvert par boutons dédiés EveryBar | +| Boost | `co +boost` / `co -boost` | Non ajouté volontairement | Moyenne | Déjà exposé ailleurs | | Focus | `co +focus` / `co -focus` | Fait via Pull Control | Référence | Focus mono-cible | | DPS Assist | `co +dps assist` / `co -dps assist` | Fait via Pull Control | Référence | Assist DPS | | DPS AoE | `co +dps aoe` / `co -dps aoe` | Fait via Pull Control | Référence | Autorise AoE DPS | | Wait for attack | `wait for attack time X` | Fait via Pull Control | Référence | Pull contrôlé | -### Proposition UI +### UX actuelle -Créer une page ou mini-frame `Advanced Combat`, séparée de Pull Control : +#### EveryBar / bot individuel + +Les stratégies individuelles sont ajoutées au menu existant `Strategies DPS` de chaque bot. ```text -Combat Strategies -├─ Scope: Selected / Party / Raid -├─ Survivability: Avoid AoE, Threat -├─ Positioning: Behind, Tank Face -├─ Resource: Save Mana -├─ Damage policy: Healer DPS, Boost -└─ Apply toggles via RUN~COMBAT +EveryBar bot +└─ Strategies DPS + ├─ Avoid AoE + ├─ Save Mana + ├─ Threat + └─ Behind ``` -Recommandation UX : +Ce choix évite d'ajouter une mini-frame globale pour du fine-tuning individuel : chaque bot reste configurable depuis sa propre barre. + +#### MainBar / groupe et raid + +Une mini-frame séparée sert uniquement aux scopes collectifs. + +```text +MainBar +└─ Combat Strategies + ├─ Scope: Party / Raid + ├─ Avoid AoE + ├─ Save Mana + ├─ Threat + └─ Behind +``` + +Recommandation UX conservée : - ne pas ajouter ces toggles directement sur la MainBar ; -- créer un panneau repliable ou une sous-page accessible depuis un bouton existant de stratégies/combat ; -- réutiliser les scopes `BOT`, `GROUP`, `ALL` déjà validés par Pull Control ; +- garder les actions individuelles dans les EveryBars ; +- garder la frame MainBar pour les actions Party/Raid ; +- réutiliser les scopes `GROUP` et `ALL` déjà validés par Pull Control ; - garder `co ?` comme vérification manuelle, sans parser automatiquement son retour. --- ## Priorité 4 - Disperse +### Statut + +**Prochaine étape logique.** + ### Pourquoi Très utile pour les mécaniques AoE ou les combats où les bots doivent s'espacer. @@ -288,7 +339,7 @@ Disperse distance: [ 8 ] yards ### Note -À faire après `Advanced Combat`, sauf si un besoin immédiat de mécaniques AoE impose de le passer avant. +À faire après stabilisation RTI/Pull Control/Combat Strategies, sauf si un autre besoin gameplay devient prioritaire. --- @@ -296,7 +347,7 @@ Disperse distance: [ 8 ] yards ### Pourquoi -Le contrôle du loot est utile, mais moins prioritaire que RTI/pull/combat. +Le contrôle du loot est utile, mais moins prioritaire que RTI/pull/combat/disperse. ### Commandes à couvrir @@ -445,8 +496,8 @@ Ces commandes sont plutôt serveur/admin/debug ou trop dangereuses pour une UI u |---:|---|---|---:|---:| | 1 | RTI bridge-first | UI + bridge command | Haute | Fait | | 2 | Pull Control avancé | Nouvelle UI + séquences commandes | Haute | Fait / à fignoler | -| 3 | Advanced Combat Strategies | UI toggles réutilisant `RUN~COMBAT` | Haute/Moyenne | Prochaine étape | -| 4 | Disperse | Petite UI + commande combat/mouvement | Moyenne | À faire | +| 3 | Advanced Combat Strategies | EveryBars + mini-frame Party/Raid via `RUN~COMBAT` | Haute/Moyenne | Fait / à tester | +| 4 | Disperse | Petite UI + commande combat/mouvement | Moyenne | Prochaine étape | | 5 | Loot Rules | Petite UI profils | Moyenne | À faire | | 6 | Trainer / Maintenance extras | UI maintenance | Moyenne/Basse | À faire | | 7 | Items avancés | Extensions inventaire | Basse/Moyenne | À faire | @@ -462,15 +513,21 @@ Ces commandes sont plutôt serveur/admin/debug ou trop dangereuses pour une UI u - Pour les commandes qui doivent alimenter une frame, préférer un endpoint structuré dédié. - Les commandes serveur/admin ne doivent pas être exposées dans l'addon utilisateur. - Les boutons ajoutés dans les barres doivent conserver une position cohérente avec `MultiBotLeftCoreUI.lua` et la position par défaut de `MultiBar` dans `MultiBotInit.lua` / reset dans `MultiBotMainUI.lua`. -- Les tooltips nouvellement ajoutés doivent passer par AceLocale, comme les tooltips RTI et Pull Control. +- Les tooltips nouvellement ajoutés doivent passer par AceLocale, comme les tooltips RTI, Pull Control et Combat Strategies. - `RUN~COMBAT` doit rester whitelisté côté bridge : ne pas en faire un exécuteur libre de n'importe quelle commande chat. +- Éviter les doublons UI : si une stratégie dispose déjà d'un bouton EveryBar dédié, ne pas la rajouter dans une nouvelle frame sauf besoin UX clairement identifié. --- ## Point logique suivant -Le prochain bloc logique est **Advanced Combat Strategies**. +Le prochain bloc logique est **Disperse**. + +Raison : les blocs RTI, Pull Control et Combat Strategies couvrent maintenant le ciblage, le pull et les stratégies combat de base. `Disperse` complète naturellement ce trio en ajoutant un contrôle de positionnement collectif très utile contre les AoE et les mécaniques de donjon/raid. -Raison : `RUN~COMBAT` existe maintenant, les scopes sont validés, et Pull Control a déjà prouvé que l'addon peut envoyer proprement des toggles de stratégie sans spam chat. Le plus rentable est donc d'ajouter une UI dédiée aux stratégies combat permanentes ou semi-permanentes : `avoid aoe`, `save mana`, `threat`, `behind`, `tank face`, `healer dps`, `boost`. +Proposition de prochaine itération : -Ce bloc doit rester séparé de Pull Control : Pull Control sert aux séquences de pull, tandis qu'Advanced Combat sert aux comportements généraux des bots pendant les combats. +- vérifier d'abord la syntaxe exacte supportée par playerbots : `disperse set ` et `disperse disable` ; +- décider si l'action doit passer par `RUN~COMBAT` ou par un endpoint plus neutre, par exemple `RUN~MOVE` / `RUN~POSITION` si on veut garder `RUN~COMBAT` réservé aux stratégies combat ; +- ajouter une mini-frame simple avec distance configurable et bouton disable ; +- garder le fonctionnement chatless, sans parsing automatique de réponse.