├── .github └── workflows │ └── curse.yml ├── .gitignore ├── .gitmodules ├── .images ├── example.jpg ├── screenshot.jpg ├── weixin.jpg └── zhifubao.jpg ├── .lua-format ├── CHANGELOG.md ├── Core ├── Action.lua ├── Addon.lua ├── Condition.lua ├── Core.xml ├── Director.lua ├── Manager │ ├── BattleCacheManager.lua │ ├── PluginManager.lua │ └── ScriptManager.lua ├── Prototype │ ├── BattleCache.lua │ └── Plugin.lua ├── Script.lua ├── Share.lua ├── Stack.lua └── Util.lua ├── Extension ├── Actions.lua ├── Conditions.lua ├── Extension.xml ├── Played.lua ├── Round.lua └── Snippets.lua ├── LICENSE.md ├── Libs ├── AceAddon-3.0 │ ├── AceAddon-3.0.lua │ └── AceAddon-3.0.xml ├── AceConfig-3.0 │ ├── AceConfig-3.0.lua │ ├── AceConfig-3.0.xml │ ├── AceConfigCmd-3.0 │ │ ├── AceConfigCmd-3.0.lua │ │ └── AceConfigCmd-3.0.xml │ ├── AceConfigDialog-3.0 │ │ ├── AceConfigDialog-3.0.lua │ │ └── AceConfigDialog-3.0.xml │ └── AceConfigRegistry-3.0 │ │ ├── AceConfigRegistry-3.0.lua │ │ └── AceConfigRegistry-3.0.xml ├── AceDB-3.0 │ ├── AceDB-3.0.lua │ └── AceDB-3.0.xml ├── AceEvent-3.0 │ ├── AceEvent-3.0.lua │ └── AceEvent-3.0.xml ├── AceGUI-3.0 │ ├── AceGUI-3.0.lua │ ├── AceGUI-3.0.xml │ └── widgets │ │ ├── AceGUIContainer-BlizOptionsGroup.lua │ │ ├── AceGUIContainer-DropDownGroup.lua │ │ ├── AceGUIContainer-Frame.lua │ │ ├── AceGUIContainer-InlineGroup.lua │ │ ├── AceGUIContainer-ScrollFrame.lua │ │ ├── AceGUIContainer-SimpleGroup.lua │ │ ├── AceGUIContainer-TabGroup.lua │ │ ├── AceGUIContainer-TreeGroup.lua │ │ ├── AceGUIContainer-Window.lua │ │ ├── AceGUIWidget-Button.lua │ │ ├── AceGUIWidget-CheckBox.lua │ │ ├── AceGUIWidget-ColorPicker.lua │ │ ├── AceGUIWidget-DropDown-Items.lua │ │ ├── AceGUIWidget-DropDown.lua │ │ ├── AceGUIWidget-EditBox.lua │ │ ├── AceGUIWidget-Heading.lua │ │ ├── AceGUIWidget-Icon.lua │ │ ├── AceGUIWidget-InteractiveLabel.lua │ │ ├── AceGUIWidget-Keybinding.lua │ │ ├── AceGUIWidget-Label.lua │ │ ├── AceGUIWidget-MultiLineEditBox.lua │ │ └── AceGUIWidget-Slider.lua ├── AceHook-3.0 │ ├── AceHook-3.0.lua │ └── AceHook-3.0.xml ├── AceLocale-3.0 │ ├── AceLocale-3.0.lua │ └── AceLocale-3.0.xml ├── AceSerializer-3.0 │ ├── AceSerializer-3.0.lua │ └── AceSerializer-3.0.xml ├── AceTimer-3.0 │ ├── AceTimer-3.0.lua │ └── AceTimer-3.0.xml ├── CallbackHandler-1.0 │ ├── CallbackHandler-1.0.lua │ └── CallbackHandler-1.0.xml ├── Embeds.xml ├── LibBase64-1.0 │ ├── LibBase64-1.0.lua │ ├── LibBase64-1.0.toc │ └── lib.xml ├── LibDBIcon-1.0 │ └── LibDBIcon-1.0.lua ├── LibDataBroker-1.1 │ └── LibDataBroker-1.1.lua ├── LibStub │ └── LibStub.lua └── LibWindow-1.1 │ ├── CHANGES.txt │ ├── LibStub.lua │ ├── LibWindow-1.1.toc │ └── LibWindow-1.1 │ └── LibWindow-1.1.lua ├── Load.xml ├── Localization ├── Localization.xml ├── deDE.lua ├── enUS.lua ├── esES.lua ├── frFR.lua ├── itIT.lua ├── koKR.lua ├── ptBR.lua ├── ruRU.lua ├── zhCN.lua └── zhTW.lua ├── Plugins ├── AllInOne.lua ├── Base.lua ├── FirstEnemy.lua └── Plugins.xml ├── README.md ├── Share ├── Share.xml ├── Version0.lua ├── Version1.lua └── Version2.lua ├── UI ├── Import.lua ├── MainPanel.lua ├── Minimap.lua ├── Option.lua ├── PetBattle.lua ├── UI.lua ├── UI.xml └── Widgets │ ├── AutoCompleteBox.lua │ ├── AutoCompleteItem.lua │ ├── ScriptEditor.lua │ └── ScriptItem.lua ├── package.json └── tdBattlePetScript.toc /.github/workflows/curse.yml: -------------------------------------------------------------------------------- 1 | name: Curseforge 2 | on: 3 | push: 4 | tags: 5 | - v* 6 | 7 | jobs: 8 | publish: 9 | runs-on: ubuntu-latest 10 | 11 | steps: 12 | - uses: actions/checkout@v3 13 | with: 14 | submodules: true 15 | 16 | - uses: actions/setup-node@v3 17 | with: 18 | node-version: '16' 19 | 20 | - run: npm install --location=global wow-curse-tools 21 | - run: wct publish --token ${{ secrets.CURSE_TOKEN }} -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.vscode 2 | /.tags 3 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "Libs/LibCRC32-1.0"] 2 | path = Libs/LibCRC32-1.0 3 | url = https://github.com/DengSir/LibCRC32-1.0.git 4 | [submodule "Libs/tdGUI"] 5 | path = Libs/tdGUI 6 | url = https://github.com/DengSir/tdGUI.git 7 | -------------------------------------------------------------------------------- /.images/example.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DengSir/tdBattlePetScript/58d19ec8c2fc1708fd9af187113f752cb13b27ba/.images/example.jpg -------------------------------------------------------------------------------- /.images/screenshot.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DengSir/tdBattlePetScript/58d19ec8c2fc1708fd9af187113f752cb13b27ba/.images/screenshot.jpg -------------------------------------------------------------------------------- /.images/weixin.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DengSir/tdBattlePetScript/58d19ec8c2fc1708fd9af187113f752cb13b27ba/.images/weixin.jpg -------------------------------------------------------------------------------- /.images/zhifubao.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DengSir/tdBattlePetScript/58d19ec8c2fc1708fd9af187113f752cb13b27ba/.images/zhifubao.jpg -------------------------------------------------------------------------------- /.lua-format: -------------------------------------------------------------------------------- 1 | column_limit: 120 2 | keep_simple_control_block_one_line: false 3 | keep_simple_function_one_line: false 4 | extra_sep_at_table_end: true 5 | double_quote_to_single_quote: true 6 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## [10.0.0] 2 | 3 | - Updated for Dragonflight. 4 | - Added `collected` to the Pets API (`true` if the pet is in your collection). 5 | - Added `trap` to the Status API (`true` if the trap is usable (or potentially usable if enemy hp is low enough)). 6 | - Fixed condition behavior when the condition includes a non-existent pet or ability. This allows (among others) using ability `(Ghostly Bite:654) [enemy.ability (Mudslide:572).duration < 5]`, even if the current pet does not have Mudslide, to be used in generic scripts. See the discussion in [this issue](https://github.com/DengSir/tdBattlePetScript/issues/26) for more detail and exact semantics. 7 | - General bug fixes 8 | -------------------------------------------------------------------------------- /Core/Action.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | Action.lua 3 | @Author : DengSir (tdaddon@163.com) 4 | @Link : https://dengsir.github.io 5 | ]] 6 | 7 | local ns = select(2, ...) 8 | local Util = ns.Util 9 | local Addon = ns.Addon 10 | local Action = {} ns.Action = Action 11 | 12 | Action.apis = {} 13 | 14 | function Addon:RegisterAction(...) 15 | local last = select('#', ...) 16 | local method = select(last, ...) 17 | 18 | if last < 2 or type(method) ~= 'function' then 19 | error('Usage: :RegisterAction(name, [name2, ...], method)') 20 | end 21 | 22 | for i = 1, last - 1 do 23 | Action.apis[select(i, ...)] = method 24 | end 25 | end 26 | 27 | function Action:Run(action) 28 | local cmd, value = self:ParseAction(action) 29 | 30 | local fn = self.apis[cmd] 31 | return fn and fn(value) 32 | end 33 | 34 | function Action:ParseAction(action) 35 | Util.assert(type(action) == 'string', 'Invalid Action: `%s`', action) 36 | 37 | if action:find('^%-%-') then 38 | return '--', action 39 | end 40 | 41 | local cmd, value = Util.ParseQuote(action) 42 | 43 | Util.assert(cmd, 'Invalid Action: `%s`', action) 44 | Util.assert(self.apis[cmd], 'Invalid Action: `%s` (Not found command)', action) 45 | 46 | return cmd, value ~= '' and value or nil 47 | end 48 | -------------------------------------------------------------------------------- /Core/Addon.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | Addon.lua 3 | @Author : DengSir (tdaddon@163.com) 4 | @Link : https://dengsir.github.io 5 | ]] 6 | 7 | local ADDON, ns = ... 8 | local Addon = LibStub('AceAddon-3.0'):NewAddon('tdBattlePetScript', 'AceEvent-3.0', 'LibClass-2.0') 9 | local GUI = LibStub('tdGUI-1.0') 10 | 11 | ns.Addon = Addon 12 | ns.UI = {} 13 | ns.L = LibStub('AceLocale-3.0'):GetLocale('tdBattlePetScript', true) 14 | ns.ICON = [[Interface\Icons\INV_Misc_PenguinPet]] 15 | 16 | _G.tdBattlePetScript = Addon 17 | 18 | function Addon:OnInitialize() 19 | local defaults = { 20 | global = { 21 | scripts = { 22 | 23 | }, 24 | notifies = { 25 | 26 | } 27 | }, 28 | profile = { 29 | pluginDisabled = {}, 30 | pluginOrders = {}, 31 | settings = { 32 | autoSelect = true, 33 | hideNoScript = true, 34 | noWaitDeleteScript = false, 35 | editorFontFace = STANDARD_TEXT_FONT, 36 | editorFontSize = 14, 37 | autoButtonHotKey = 'A', 38 | testBreak = true, 39 | lockScriptSelector = false, 40 | }, 41 | minimap = { 42 | minimapPos = 50, 43 | }, 44 | position = { 45 | point = 'CENTER', x = 0, y = 0, width = 350, height = 450, 46 | }, 47 | scriptSelectorPosition = { 48 | point = 'TOP', x = 0, y = -60, 49 | } 50 | } 51 | } 52 | 53 | self.db = LibStub('AceDB-3.0'):New('TD_DB_BATTLEPETSCRIPT_GLOBAL', defaults, true) 54 | 55 | self.db.RegisterCallback(self, 'OnDatabaseShutdown') 56 | end 57 | 58 | function Addon:OnEnable() 59 | self:RegisterMessage('PET_BATTLE_SCRIPT_SCRIPT_ADDED') 60 | self:RegisterMessage('PET_BATTLE_SCRIPT_SCRIPT_REMOVED') 61 | self:InitSettings() 62 | self:UpdateDatabase() 63 | end 64 | 65 | function Addon:InitSettings() 66 | for key, value in pairs(self.db.profile.settings) do 67 | self:SetSetting(key, value) 68 | end 69 | end 70 | 71 | function Addon:UpdateDatabase() 72 | local oldVersion = self.db.global.version or 0 73 | local newVersion = tonumber(GetAddOnMetadata(ADDON, 'Version')) or 99999.99 74 | 75 | if oldVersion ~= newVersion then 76 | self.db.global.version = newVersion 77 | 78 | C_Timer.After(0.9, function() 79 | GUI:Notify{ 80 | text = format('%s\n|cff00ffff%s%s|r', ADDON, ns.L['Update to version: '], newVersion), 81 | icon = ns.ICON, 82 | help = '' 83 | } 84 | end) 85 | end 86 | end 87 | 88 | function Addon:OnModuleCreated(module) 89 | local name = module:GetName() 90 | if name:find('^UI%.') then 91 | ns.UI[name:match('^UI%.(.+)$')] = module 92 | else 93 | ns[name] = module 94 | end 95 | end 96 | 97 | function Addon:OnDatabaseShutdown() 98 | self:SendMessage('PET_BATTLE_SCRIPT_DB_SHUTDOWN') 99 | end 100 | 101 | function Addon:PET_BATTLE_SCRIPT_SCRIPT_ADDED(_, plugin, key, script) 102 | self.db.global.scripts[plugin:GetPluginName()][key] = script:GetDB() 103 | end 104 | 105 | function Addon:PET_BATTLE_SCRIPT_SCRIPT_REMOVED(_, plugin, key) 106 | self.db.global.scripts[plugin:GetPluginName()][key] = nil 107 | end 108 | 109 | function Addon:GetSetting(key) 110 | return self.db.profile.settings[key] 111 | end 112 | 113 | function Addon:SetSetting(key, value) 114 | self.db.profile.settings[key] = value 115 | self:SendMessage('PET_BATTLE_SCRIPT_SETTING_CHANGED', key, value) 116 | self:SendMessage('PET_BATTLE_SCRIPT_SETTING_CHANGED_' .. key, value) 117 | end 118 | 119 | function Addon:ResetSetting(key) 120 | if type(self.db.profile[key]) == 'table' then 121 | wipe(self.db.profile[key]) 122 | 123 | for k, v in pairs(self.db.defaults.profile[key]) do 124 | if type(v) == 'table' then 125 | self.db.profile[key][k] = CopyTable(v) 126 | else 127 | self.db.profile[key][k] = v 128 | end 129 | end 130 | else 131 | error('not support') 132 | end 133 | end 134 | 135 | function Addon:ResetFrames() 136 | self:ResetSetting('position') 137 | self:ResetSetting('scriptSelectorPosition') 138 | self:SendMessage('PET_BATTLE_SCRIPT_RESET_FRAMES') 139 | end 140 | -------------------------------------------------------------------------------- /Core/Condition.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | Condition.lua 3 | @Author : DengSir (tdaddon@163.com) 4 | @Link : https://dengsir.github.io 5 | ]] 6 | 7 | local ns = select(2, ...) 8 | local Util = ns.Util 9 | local Addon = ns.Addon 10 | local Condition = {} ns.Condition = Condition 11 | 12 | Condition.apis = {} 13 | Condition.opts = setmetatable({ 14 | __default = { 15 | owner = true, 16 | pet = true, 17 | arg = true, 18 | type = 'compare', 19 | } 20 | }, { 21 | __newindex = function(t, k, v) 22 | rawset(t, k, setmetatable(v, {__index = t.__default})) 23 | end, 24 | __index = function(t) 25 | return t.__default 26 | end, 27 | }) 28 | 29 | local opTabler = { 30 | compare = { 31 | ['='] = function(a, b) return a == b end, 32 | ['=='] = function(a, b) return a == b end, 33 | ['!='] = function(a, b) return a ~= b end, 34 | ['>'] = function(a, b) return a > b end, 35 | ['<'] = function(a, b) return a < b end, 36 | ['>='] = function(a, b) return a >= b end, 37 | ['<='] = function(a, b) return a <= b end, 38 | ['~'] = function(a, v) return v[a] end, 39 | ['!~'] = function(a, v) return not v[a] end, 40 | }, 41 | boolean = { 42 | ['='] = function(a) return a end, 43 | ['!'] = function(a) return not a end, 44 | }, 45 | equality = { 46 | ['='] = function(a, b) return a == b end, 47 | ['=='] = function(a, b) return a == b end, 48 | ['!='] = function(a, b) return a ~= b end, 49 | ['~'] = function(a, v) return v[a] end, 50 | ['!~'] = function(a, v) return not v[a] end, 51 | } 52 | } 53 | 54 | local multiTabler = { 55 | ['~'] = true, 56 | ['!~'] = true, 57 | } 58 | 59 | local parses = { 60 | 'valueParse', 61 | 'argParse', 62 | } 63 | 64 | local function trynumber(value) 65 | return tonumber(value) or value 66 | end 67 | 68 | local function trynil(value) 69 | return value ~= '' and value or nil 70 | end 71 | 72 | function Addon:RegisterCondition(name, opts, api) 73 | if opts then 74 | if opts.type and not opTabler[opts.type] then 75 | error([[Bad argument opts.type (expect compare/boolean/equality)]], 2) 76 | end 77 | 78 | for i, v in ipairs(parses) do 79 | if opts[v] and type(opts[v]) ~= 'function' then 80 | error(format([[Bad argument opts.%s (expect function)]], v), 2) 81 | end 82 | end 83 | end 84 | 85 | Condition.apis[name] = api 86 | Condition.opts[name] = opts 87 | end 88 | 89 | function Condition:Run(condition) 90 | if not condition then 91 | return true 92 | end 93 | if type(condition) == 'string' then 94 | return self:RunCondition(condition) 95 | elseif type(condition) == 'table' then 96 | for _, v in ipairs(condition) do 97 | if not self:Run(v) then 98 | return false 99 | end 100 | end 101 | return true 102 | else 103 | Util.assert(false, 'Invalid Condition: `%s` (type error)', condition) 104 | end 105 | end 106 | 107 | function Condition:RunCondition(condition) 108 | local owner, pet, cmd, arg, op, value = self:ParseCondition(condition) 109 | 110 | local fn = self.apis[cmd] 111 | local opts = self.opts[cmd] 112 | if not fn then 113 | error('Big Bang !!!!!!') 114 | end 115 | 116 | local res = fn(owner, pet, arg) 117 | return opTabler[opts.type][op](res, value) 118 | end 119 | 120 | function Condition:ParsePet(str) 121 | if not str then 122 | return 123 | end 124 | 125 | local owner, pet = Util.ParseQuote(str) 126 | owner = Util.ParsePetOwner(owner) 127 | if not owner then 128 | return 129 | end 130 | 131 | petInputed = not not pet 132 | pet = Util.ParsePetIndex(owner, pet) 133 | return owner, pet, petInputed 134 | end 135 | 136 | function Condition:ParseCmd(major, minor) 137 | if not major then 138 | return 139 | end 140 | 141 | local cmd, arg = Util.ParseQuote(major) 142 | return minor and format('%s.%s', cmd, minor) or cmd, arg, not not arg 143 | end 144 | 145 | function Condition:ParseApi(str) 146 | if not str then 147 | return 148 | end 149 | 150 | local inQuote = false 151 | local args = {''} 152 | 153 | for char in str:gmatch('.') do 154 | if char == '.' and not inQuote then 155 | tinsert(args, '') 156 | else 157 | args[#args] = args[#args] .. char 158 | end 159 | 160 | if char == '(' then 161 | inQuote = true 162 | elseif char == ')' then 163 | inQuote = false 164 | end 165 | end 166 | 167 | local owner, pet, petInputed = self:ParsePet(args[1]) 168 | local cmd, arg, argInputed = self:ParseCmd(unpack(args, owner and 2 or 1)) 169 | 170 | return owner, pet, cmd, arg, petInputed, argInputed 171 | end 172 | 173 | function Condition:ParseCondition(condition) 174 | local non, args, op, value = condition:match('^(!?)([^!=<>~]+)%s*([!=<>~]*)%s*(.*)$') 175 | 176 | Util.assert(non, 'Invalid Condition: `%s` (Can`t parse)', condition) 177 | 178 | local owner, pet, cmd, arg, petInputed, argInputed = self:ParseApi(args:trim()) 179 | 180 | Util.assert(cmd, 'Invalid Condition: `%s` (Can`t parse)', condition) 181 | Util.assert(self.apis[cmd], 'Invalid Condition: `%s` (Not found cmd: `%s`)', condition, cmd) 182 | 183 | op = trynil(op) 184 | value = trynil(value) 185 | non = trynil(non) 186 | 187 | local opts = self.opts[cmd] 188 | 189 | if opts.type == 'compare' or opts.type == 'equality' then 190 | Util.assert(not non, 'Invalid Condition: `%s` (Not need non)', condition) 191 | Util.assert(op, 'Invalid Condition: `%s` (Require op)', condition) 192 | Util.assert(value, 'Invalid Condition: `%s` (Require value)', condition) 193 | elseif opts.type == 'boolean' then 194 | Util.assert(not op, 'Invalid Condition: `%s` (Not need op)', condition) 195 | Util.assert(not value, 'Invalid Condition: `%s` (Not need value)', condition) 196 | 197 | value = nil 198 | op = non or '=' 199 | else 200 | Util.assert(true) 201 | end 202 | 203 | Util.assert(opTabler[opts.type][op], 'Invalid Condition: `%s` (Invalid op)', condition) 204 | 205 | if value then 206 | if multiTabler[op] then 207 | local values = {strsplit(',', value)} 208 | value = {} 209 | 210 | for i, v in ipairs(values) do 211 | v = trynumber(v:trim()) 212 | if opts.valueParse then 213 | v = Util.assert(opts.valueParse(v), 'Invalid Condition: `%s` (Error value)', condition) 214 | end 215 | value[v] = true 216 | end 217 | else 218 | value = trynumber(value) 219 | if opts.valueParse then 220 | value = Util.assert(opts.valueParse(value), 'Invalid Condition: `%s` (Error value)', condition) 221 | end 222 | end 223 | end 224 | 225 | if not opts.owner then 226 | Util.assert(not owner, 'Invalid Condition: `%s` (Not need owner)', condition) 227 | end 228 | 229 | if not opts.pet then 230 | Util.assert(not petInputed, 'Invalid Condition: `%s` (Not need pet)', condition) 231 | end 232 | 233 | if not opts.arg then 234 | Util.assert(not argInputed, 'Invalid Condition: `%s` (Not need arg)', condition) 235 | else 236 | arg = trynumber(arg) 237 | if opts.argParse then 238 | arg = opts.argParse(owner, pet, arg) 239 | end 240 | end 241 | return owner, pet, cmd, arg, op, value 242 | end 243 | -------------------------------------------------------------------------------- /Core/Core.xml: -------------------------------------------------------------------------------- 1 | 3 |