diff --git a/.gitignore b/.gitignore index dd378f31da..33e6ef8c83 100644 --- a/.gitignore +++ b/.gitignore @@ -33,4 +33,5 @@ Settings/* Unlockers/mmb.lua Unlockers/daemonic.lua Unlockers/icc.lua -Rotations/Rogue/Outlaw/OutlawRavens\.lua \ No newline at end of file +Rotations/Rogue/Outlaw/OutlawRavens\.lua +Deprecated/* \ No newline at end of file diff --git a/3rd Party Files/NoName/_BRLoader.lua b/3rd Party Files/NoName/_BRLoader.lua index eb421c193d..e0caad9068 100644 --- a/3rd Party Files/NoName/_BRLoader.lua +++ b/3rd Party Files/NoName/_BRLoader.lua @@ -1,4 +1,5 @@ local Nn = ... + -- if --[[(...).name ~= "Nn" or]] Nn == nil then return end -- local read = Nn.Utils.Storage.read -- local write = Nn.Utils.Storage.write @@ -6,9 +7,19 @@ local Nn = ... -- local AceGUI = Nn.Utils.AceGUI --Only load BR if in retail 10.2.7 (UI Update); Classic/Cata Not Supported. -if select(4,GetBuildInfo()) < 100207 then return end +-- if select(4,GetBuildInfo()) < 100207 then return end + +local toc = "" +local tocPath = '/scripts/BadRotations/BadRotations.toc' +local FileExists = _G["FileExists"] +local ReadFile = _G["ReadFile"] +if not FileExists(tocPath) then print(tocPath, 'does not exist') return end +if FileExists(tocPath) then + toc = ReadFile(tocPath) +else + print('TOC file does not exist:', tocPath) +end -local toc = ReadFile('/scripts/BadRotations/BadRotations.toc') local br = {} br.files = {} @@ -51,46 +62,50 @@ br.files[#br.files+1] = {file = 'BadRotations/Libs/DiesalStyle-1.0/Media/UbuntuM br.files[#br.files+1] = {file = 'BadRotations/Libs/DiesalStyle-1.0/Media/UbuntuMono-R.ttf', load = true} -- Add Lua Files from .toc -for line in toc:gmatch("([^\n]*)\n?") do - local thisLine = line:trim() - if thisLine:find('#') == nil and thisLine:len() > 0 - and thisLine:find('.lua') ~= nil - then - thisLine = thisLine:gsub("\\","/") - thisLine = thisLine:gsub(" ","") - thisLine = thisLine:gsub(".lua","") - thisLine = 'BadRotations/'..thisLine - br.files[#br.files+1] = { - file = thisLine, - load = true - } - -- Lua Files loaded via XML Files - if thisLine == "BadRotations/Libs/DiesalStyle-1.0/DiesalStyle-1.0" then - br.files[#br.files+1] = {file = 'BadRotations/Libs/DiesalGUI-1.0/DiesalGUI-1.0', load = true} - br.files[#br.files+1] = {file = 'BadRotations/Libs/DiesalGUI-1.0/Objects/Window', load = true} - br.files[#br.files+1] = {file = 'BadRotations/Libs/DiesalGUI-1.0/Objects/ScrollFrame', load = true} - br.files[#br.files+1] = {file = 'BadRotations/Libs/DiesalGUI-1.0/Objects/ScrollingEditBox', load = true} - br.files[#br.files+1] = {file = 'BadRotations/Libs/DiesalGUI-1.0/Objects/ScrollingMessageFrame', load = true} - br.files[#br.files+1] = {file = 'BadRotations/Libs/DiesalGUI-1.0/Objects/CheckBox', load = true} - br.files[#br.files+1] = {file = 'BadRotations/Libs/DiesalGUI-1.0/Objects/Button', load = true} - br.files[#br.files+1] = {file = 'BadRotations/Libs/DiesalGUI-1.0/Objects/Spinner', load = true} - br.files[#br.files+1] = {file = 'BadRotations/Libs/DiesalGUI-1.0/Objects/Input', load = true} - br.files[#br.files+1] = {file = 'BadRotations/Libs/DiesalGUI-1.0/Objects/Dropdown', load = true} - br.files[#br.files+1] = {file = 'BadRotations/Libs/DiesalGUI-1.0/Objects/DropdownItem', load = true} - br.files[#br.files+1] = {file = 'BadRotations/Libs/DiesalGUI-1.0/Objects/ComboBox', load = true} - br.files[#br.files+1] = {file = 'BadRotations/Libs/DiesalGUI-1.0/Objects/ComboBoxItem', load = true} - br.files[#br.files+1] = {file = 'BadRotations/Libs/DiesalGUI-1.0/Objects/Accordian', load = true} - br.files[#br.files+1] = {file = 'BadRotations/Libs/DiesalGUI-1.0/Objects/AccordianSection', load = true} - br.files[#br.files+1] = {file = 'BadRotations/Libs/DiesalGUI-1.0/Objects/Tree', load = true} - br.files[#br.files+1] = {file = 'BadRotations/Libs/DiesalGUI-1.0/Objects/Branch', load = true} - br.files[#br.files+1] = {file = 'BadRotations/Libs/DiesalGUI-1.0/Objects/Bar', load = true} - br.files[#br.files+1] = {file = 'BadRotations/Libs/DiesalMenu-1.0/DiesalMenu-1.0', load = true} - br.files[#br.files+1] = {file = 'BadRotations/Libs/DiesalMenu-1.0/Objects/MenuItem', load = true} - br.files[#br.files+1] = {file = 'BadRotations/Libs/DiesalMenu-1.0/Objects/Menu', load = true} +function LoadFilesFromTOC(tocFile) + for line in tocFile:gmatch("([^\n]*)\n?") do + local thisLine = line:trim() + if thisLine:find('#') == nil and thisLine:len() > 0 + and thisLine:find('.lua') ~= nil + then + thisLine = thisLine:gsub("\\","/") + thisLine = thisLine:gsub(" ","") + thisLine = thisLine:gsub(".lua","") + thisLine = 'BadRotations/'..thisLine + br.files[#br.files+1] = { + file = thisLine, + load = true + } + -- Lua Files loaded via XML Files + if thisLine == "BadRotations/Libs/DiesalStyle-1.0/DiesalStyle-1.0" then + br.files[#br.files+1] = {file = 'BadRotations/Libs/DiesalGUI-1.0/DiesalGUI-1.0', load = true} + br.files[#br.files+1] = {file = 'BadRotations/Libs/DiesalGUI-1.0/Objects/Window', load = true} + br.files[#br.files+1] = {file = 'BadRotations/Libs/DiesalGUI-1.0/Objects/ScrollFrame', load = true} + br.files[#br.files+1] = {file = 'BadRotations/Libs/DiesalGUI-1.0/Objects/ScrollingEditBox', load = true} + br.files[#br.files+1] = {file = 'BadRotations/Libs/DiesalGUI-1.0/Objects/ScrollingMessageFrame', load = true} + br.files[#br.files+1] = {file = 'BadRotations/Libs/DiesalGUI-1.0/Objects/CheckBox', load = true} + br.files[#br.files+1] = {file = 'BadRotations/Libs/DiesalGUI-1.0/Objects/Button', load = true} + br.files[#br.files+1] = {file = 'BadRotations/Libs/DiesalGUI-1.0/Objects/Spinner', load = true} + br.files[#br.files+1] = {file = 'BadRotations/Libs/DiesalGUI-1.0/Objects/Input', load = true} + br.files[#br.files+1] = {file = 'BadRotations/Libs/DiesalGUI-1.0/Objects/Dropdown', load = true} + br.files[#br.files+1] = {file = 'BadRotations/Libs/DiesalGUI-1.0/Objects/DropdownItem', load = true} + br.files[#br.files+1] = {file = 'BadRotations/Libs/DiesalGUI-1.0/Objects/ComboBox', load = true} + br.files[#br.files+1] = {file = 'BadRotations/Libs/DiesalGUI-1.0/Objects/ComboBoxItem', load = true} + br.files[#br.files+1] = {file = 'BadRotations/Libs/DiesalGUI-1.0/Objects/Accordian', load = true} + br.files[#br.files+1] = {file = 'BadRotations/Libs/DiesalGUI-1.0/Objects/AccordianSection', load = true} + br.files[#br.files+1] = {file = 'BadRotations/Libs/DiesalGUI-1.0/Objects/Tree', load = true} + br.files[#br.files+1] = {file = 'BadRotations/Libs/DiesalGUI-1.0/Objects/Branch', load = true} + br.files[#br.files+1] = {file = 'BadRotations/Libs/DiesalGUI-1.0/Objects/Bar', load = true} + br.files[#br.files+1] = {file = 'BadRotations/Libs/DiesalMenu-1.0/DiesalMenu-1.0', load = true} + br.files[#br.files+1] = {file = 'BadRotations/Libs/DiesalMenu-1.0/Objects/MenuItem', load = true} + br.files[#br.files+1] = {file = 'BadRotations/Libs/DiesalMenu-1.0/Objects/Menu', load = true} + end end end end +LoadFilesFromTOC(toc) + -- Unlocker Lua Files br.files[#br.files+1] = {file = 'BadRotations/Unlockers/tinkr', load = false} br.files[#br.files+1] = {file = 'BadRotations/Unlockers/nn', load = true} @@ -109,4 +124,4 @@ end if UnitExists('player') then print "[Nn] Loading BR" br.load() -end +end \ No newline at end of file diff --git a/BR2-ROADMAP.md b/BR2-ROADMAP.md deleted file mode 100644 index 7a68124cd0..0000000000 --- a/BR2-ROADMAP.md +++ /dev/null @@ -1,69 +0,0 @@ -# ROADMAP - -## Code Standards -### Files -1. **FILES** should be descriptive of the functionality. Examples: BRDebug contains functions related to debugging like print to chat, output to debug window, maybe logging to file. If the code being developed relates to any form of debug logging it must exist within the BRDebug file. - -### Code Annotation -1. Each module should be specific to a single type of functionality and be defined as a class using ---@class -2. Classes need to be initialized using the BRLoader:CreateModule([ModuleName]) method and be retrieved using the BRLoader:LoadModule([ModuleName]) method. - Example: - -``` -local _,br,_ = ... - ----@class BRDebug -local BRDebug = BRLoader:CreateModule("BRDebug") - ----@type BRConstants -local BRConstants = BRLoader:LoadModule("BRConstants") - ----@param message string @Message to log ----@return nil -function BRDebug:Log(message) - br._G.print(BRConstants.colors.purple .. "[BR2] " .. BRConstants.colors.white .. message .. "|r") -end -``` - -3. Each method inside of the class must be annotated with ---@params, etc. See documentation here: https://github.com/LuaLS/lua-language-server/wiki/Annotations - -### Environment -1. Each file has three enivonment objects passed to which should be retrieved at the beginning of the file. These are assigned during injection. The developer should primarily be concerned with the second argument which is the global br object. The third is access directly to the unlocker but given the agnostic approach of all modules this should not be used within the core code. Access to the unlocker should only be made in code within the "init' directory. This is currently in play only during initial development and **WILL BE REMOVED** before general consumption. -``` -local _,br,_ = ... -``` - -### Naming -3. "**br**" remains the global add-on variable -4. **Functions** should be named with PascalCase. That is fully capitilized full words, example: RunSomethingReallyCool() -5. **Variables** should be named with CamelCase. That is a lower case inital word followed by capitalized words. example: br.someStorageVariable = nil - -## Overall Goals -1. Make system compatible with classic, Cataclysm, and retail without separate builds or branches -2. Focus on human style rotation logic. This will include randomized pulses, facing of targets, dynamic targeting, etc. -3. Support the three current Unlockers, Nil-Name, Tinkr, and Daemonic (if it ever resurfaces, currently not supported) -4. Focus on rotations only. Don't port over any code that doesn't related to Damage, tank, or healing rotations. (i.e. no fishing, quest, movement, etc.) This may change in the future but the first pass will attempt to reduce the addOn's footprint and reduce latency. -5. Remove dependency on manually reading the WOW table of contents file and instead handle injection of code in unlocker specific files. Unlockers that do not support injecting code will not be supported. -6. Change settings files to JSON. All current unlockers support JSON code/decode. - -## Future Nice-To-Haves -1. UI Improvements: Object Browser, Object Highlighting/lines, Damage/DPS meter/graph, -2. UI Improvements: wider configuration to avoid clipping -3. HTTP Request : Possibly allow loading of routine from a location - -## Current Progress -- [x] Remove TOC -- [x] Move injection of files to unlocker specific code, minimize _BRLoader.lua code to three or four lines, just enough to spawn project unlocker initialization. -- [x] Specific unlocker loaders, configurations -- [x] Implement NilName Unlocker for initial development -- [x] Implement specific loading of files based on client version (Classic, Cata, Retail) -- [x] Implement Object Manager copy -- [ ] Re-do file injection to support pre/unlocked post/unlocked category -- [ ] Create TOC files, including per-client TOCs, maybe use scripting to build this so it's always up to date. -- [ ] Implement Object Browser for development -- [ ] Implement Settings class to handle global and rotation specific settings in JSON files -- [ ] Implement Object Filter for Units, and Unit tracking -- [ ] Implement Aura tracking -- [ ] Implement Spell system maintaining separation of client versions - - diff --git a/BadRotations API.md b/BadRotations API.md index 4c16ac56db..1748ade55a 100644 --- a/BadRotations API.md +++ b/BadRotations API.md @@ -13,54 +13,54 @@ --- -`br.UnitBuffID(UnitID, SpellID, Filter) - 1/nil` Returns 1 if Buff found on target -`br.UnitDebuffID(UnitID, SpellID, Filter) - 1/nil` Returns 1 if Debuff found on target +`br.functions.aura:UnitBuffID(UnitID, SpellID, Filter) - 1/nil` Returns 1 if Buff found on target +`br.functions.aura:UnitDebuffID(UnitID, SpellID, Filter) - 1/nil` Returns 1 if Debuff found on target **CAN - Booleans** --- `canAttack(Unit1,Unit2) - Bool` Returns true if Unit1 can attack Unit2 -`br.canDispel(Unit,spellID) - Bool` Retruns true if the Unit have a valid Dispel +`br.functions.aura:canDispel(Unit,spellID) - Bool` Retruns true if the Unit have a valid Dispel `canHeal(Unit) - Bool` Returns true if we can heal that Unit -`br.canInterrupt(spellID,percentint) - Bool` Returns true if we can Interrupt that target +`br.functions.spell:canInterrupt(spellID,percentint) - Bool` Returns true if we can Interrupt that target `canPrepare() - Bool` Returns true if ready to buff(ooc) -`br.canRun() - Bool` Returns true if we can Run profile(incombat) -`br.canUseItem(itemID) - Bool` Returns true if the item can be used +`br.functions.action:canRun() - Bool` Returns true if we can Run profile(incombat) +`br.functions.item:canUseItem(itemID) - Bool` Returns true if the item can be used **CAST - Function Methods** --- `castGround(Unit,SpellID,maxDistance)` Used to cast Spells on ground. Returns true if everything passes. `castGroundBetween(Unit,SpellID,maxDistance) ` Used to ground cast between yourself and target. Returns true if everything passes. -`castHealGround(SpellID,Radius,Health,NumberOfPlayers)` Used to ground cast between lowest br.friend members. Returns true if everything passes. +`castHealGround(SpellID,Radius,Health,NumberOfPlayers)` Used to ground cast between lowest br.engines.healingEngine.friend members. Returns true if everything passes. [castSpell(Unit,SpellID,FacingCheck,MovementCheck,SpamAllowed,KnownSkip)](https://github.com/CuteOne/BadRotations/wiki/castSpell-Method#castspell) Used to cast Spells. Returns true if everything passes. **GET - Mixed** --- `getAllies(Target,Radius) - Table` Returns a table of the allies found within Radius of the target -`br.getBuffRemain(Unit,BuffID) - Num` Returns how long remain on this Unit buff. -`br.getBuffStacks(Unit,BuffID) - Num` Returns number of stacks of this Units buff. -`br.getCombatTime() - Num` Returns time since combat started. +`br.functions.aura:getBuffRemain(Unit,BuffID) - Num` Returns how long remain on this Unit buff. +`br.functions.aura:getBuffStacks(Unit,BuffID) - Num` Returns number of stacks of this Units buff. +`br.functions.combat:getCombatTime() - Num` Returns time since combat started. `getCreatureType(Unit) - Bool` Returns true if Unit is not a pet battle or a totem. `getCombo() - Num` Returns combo points on current target. -`br.getDebuffRemain(Unit,DebuffID) - Num` Returns how long remain on this Units Debuff. -`br.getDebuffStacks(Unit,DebuffID) - Num` Returns number of stacks of this Units Debuff. -`br.getDistance(Unit1,Unit2) - Num` Returns distance to target in yards. -`br.getEnemies(Target,Radius) - Table` Returns a table of the Enemies found within Radius of the target -`br.getFacing(Unit1,Unit2) - Bool` Returns true if Unit1 is facing Unit2 +`br.functions.aura:getDebuffRemain(Unit,DebuffID) - Num` Returns how long remain on this Units Debuff. +`br.functions.aura:getDebuffStacks(Unit,DebuffID) - Num` Returns number of stacks of this Units Debuff. +`br.functions.range:getDistance(Unit1,Unit2) - Num` Returns distance to target in yards. +`br.engines.enemiesEngineFunctions:getEnemies(Target,Radius) - Table` Returns a table of the Enemies found within Radius of the target +`br.functions.unit:getFacing(Unit1,Unit2) - Bool` Returns true if Unit1 is facing Unit2 `getFallTime() - Num` Returns the time the character has been falling. `getGround(Unit) - Bool` Returns true if ground is found under the target -`br.getHP(Unit) - Num` Returns Unit HP % -`br.getLineOfSight(Unit1,Unit2) - Bool` Returns true if the Unit1 can see Unit2 -`getLowAllies(Value) - Num` Returns number of units under Value HP in br.friend +`br.functions.unit:getHP(Unit) - Num` Returns Unit HP % +`br.functions.misc:getLineOfSight(Unit1,Unit2) - Bool` Returns true if the Unit1 can see Unit2 +`getLowAllies(Value) - Num` Returns number of units under Value HP in br.engines.healingEngine.friend `getMana(Unit) - Num` Returns Unit Mana % `getNumEnemies(Target,Radius) - Num` Returns number of Enemies found within Radius of the target `getPetLineOfSight(Unit) - Bool` Returns true if our pet is in sight of target -`br.getPower(Unit) - Num` Returns Unit Power % -`br.getRegen(Unit) - Num` Returns power regen rate of Unit +`br.functions.power:getPower(Unit) - Num` Returns Unit Power % +`br.functions.power:getRegen(Unit) - Num` Returns power regen rate of Unit `round2(num, idp) - Num` Used to round numbers -`br.getSpellCD(SpellID) - Num` Returns how long remain until the CD is ready +`br.functions.spell:getSpellCD(SpellID) - Num` Returns how long remain until the CD is ready `getTimeToDie(unit) - Num` Returns approximative Time To Die for Unit `getTimeToMax(Unit) - Num` Returns how long it will take until our ressources are maxed `getTotemDistance(Unit1) - Num` Returns distance from totem to target @@ -74,18 +74,18 @@ --- -`br.isAlive(Unit) - Bool` Returns true if Unit is alive -`br.isBoss() - Bool` Returns true if a boss is found in boss 1-2-3-4-5 +`br.functions.unit:isAlive(Unit) - Bool` Returns true if Unit is alive +`br.functions.unit:isBoss() - Bool` Returns true if a boss is found in boss 1-2-3-4-5 `isBuffed(UnitID,SpellID,TimeLeft) - Bool` Rturns true if Unit have at least Timeleft remaining on SpellId Buff `isCasting(SpellID,Unit) - Bool` Returns true if Unit is casting given spell -`br.isCastingSpell(spellID) - Bool` returns true if WE are casting spellid -`br.isDummy(Unit) - Bool` Returns true if Unit is a dummy +`br.functions.cast:isCastingSpell(spellID) - Bool` returns true if WE are casting spellid +`br.functions.unit:isDummy(Unit) - Bool` Returns true if Unit is a dummy `isEnnemy(Unit) - Bool` Returns true if we can attack the Unit `isGarrMCd(Unit) - Bool` Returns true if Unit if affected by Garrosh MC `isInCombat(Unit) - Bool` Returns true if Unit is in combat `isInMelee(Unit) - Bool` Returns true if we are withing 4 yard of the Unit `isInPvP() - Bool` Returns true if we are in PvP -`br.isKnown(spellID) - Bool` Returns true if we know this spell(via spellbook check) +`br.functions.spell:isKnown(spellID) - Bool` Returns true if we know this spell(via spellbook check) `isLooting() - Bool` Returns true if we are currently looting `isMoving(Unit) - Num` Returns Unit movement speed `IsMovingTime(time) - Bool` Returns true if we have been moving for time seconds @@ -95,19 +95,19 @@ --- -`makeEnemiesTable()` - does not return anything but create br.enemy that hold br.enemy[i].unit br.enemy[i].distance and br.enemy[i].hp of all enemies in 40 yards +`makeEnemiesTable()` - does not return anything but create br.engines.enemiesEngine.enemy that hold br.engines.enemiesEngine.enemy[i].unit br.engines.enemiesEngine.enemy[i].distance and br.engines.enemiesEngine.enemy[i].hp of all enemies in 40 yards `nDbDmg(tar, spellID, player) - Num` Returns tooltip damage `pause() - Bool` Returns true if pause is engaged `castingUnit() - Bool` Returns true is said unit is casting. -`br.useItem(itemID)` Use item via ID +`br.functions.item:useItem(itemID)` Use item via ID `shouldStopCasting(SpellID) - Bool` Built into casts methods. Prevents spell locking. **Config Queries - Mixed** --- -`br.isChecked(Value) - Bool` Returns true if Value Checkbox is checked in UI +`br.functions.misc:isChecked(Value) - Bool` Returns true if Value Checkbox is checked in UI `isSelected(Value) - Bool` Returns true if Value Checkbox is checked in UI and CD requirements are met. -`br.getValue(Value) - Num` Returns drop or box Value from UI +`br.functions.misc:getValue(Value) - Num` Returns drop or box Value from UI `CreateNewCheck(value, textString, tip1, state)` - Used to create checkboxes in UI diff --git a/BadRotations.lua b/BadRotations.lua index 80b17baf23..f295b1fd69 100644 --- a/BadRotations.lua +++ b/BadRotations.lua @@ -1,208 +1,90 @@ -- [[ Global Variables and Tables Initialization ]] -- -- define br global that will hold the bot global background features local _, br = ... + +local loadedIn = false +-- Guard to make br.load idempotent when multiple loaders invoke it +local _br_loaded = false br._G = setmetatable({}, { __index = _G }) -br._G.GetSpellInfo = function(spellIdentifier) - local spellInfo = br._G.C_Spell.GetSpellInfo(spellIdentifier) - if spellInfo then - ---@diagnostic disable-next-line: redundant-return-value - return spellInfo.name, _, spellInfo.iconID, spellInfo.castTime, spellInfo.minRange, - ---@diagnostic disable-next-line: redundant-return-value - spellInfo.maxRange, spellInfo.spellID, spellInfo.originalIconID - end -end -br.unlock = {} -br.data = {} -br.data.settings = {} -br.addonName = "BadRotations" -br.auraMaxStacks = {} -br.castPosition = { x = 0, y = 0, z = 0 } -br.commandHelp = {} -br.deadPet = false --- developers debug, use /run br.data.settings[br.selectedSpec].toggles["isDebugging"] = true -br.debug = {} -br.dropOptions = {} ---br.dropOptions.Toggle = {"LeftCtrl","LeftShift","RightCtrl","RightShift","RightAlt","None"} -br.dropOptions.Toggle = { - "LeftCtrl", - "LeftShift", - "RightCtrl", - "RightShift", - "RightAlt", - "None", - "MMouse", - "Mouse4", - "Mouse5" -} -br.dropOptions.CD = { "Never", "CDs", "Always" } -br.engines = {} -br.loadedIn = false -br.loadFile = false -br.pauseCast = br._G.GetTime() -br.prevQueueWindow = br._G.GetCVar("SpellQueueWindow") -br.profile = {} -br.rotations = {} -br.selectedSpec = "None" -br.selectedSpecID = 0 -br.selectedProfile = 1 -br.selectedProfileName = "None" -br.settingsDir = "\\" -br.settingsFile = "None.lua" -br.unlocker = "None" --- [[ Class Colors Definition ]] -- --- The colors Duke, the colors! -br.classColors = { - [1] = { class = "Warrior", B = 0.43, G = 0.61, R = 0.78, hex = "c79c6e" }, - [2] = { class = "Paladin", B = 0.73, G = 0.55, R = 0.96, hex = "f58cba" }, - [3] = { class = "Hunter", B = 0.45, G = 0.83, R = 0.67, hex = "abd473" }, - [4] = { class = "Rogue", B = 0.41, G = 0.96, R = 1, hex = "fff569" }, - [5] = { class = "Priest", B = 1, G = 1, R = 1, hex = "ffffff" }, - [6] = { class = "Deathknight", B = 0.23, G = 0.12, R = 0.77, hex = "c41f3b" }, - [7] = { class = "Shaman", B = 0.87, G = 0.44, R = 0, hex = "0070de" }, - [8] = { class = "Mage", B = 0.94, G = 0.8, R = 0.41, hex = "69ccf0" }, - [9] = { class = "Warlock", B = 0.79, G = 0.51, R = 0.58, hex = "9482c9" }, - [10] = { class = "Monk", B = 0.59, G = 1, R = 0, hex = "00ff96" }, - [11] = { class = "Druid", B = 0.04, G = 0.49, R = 1, hex = "ff7d0a" }, - [12] = { class = "Demonhunter", B = 0.79, G = 0.19, R = 0.64, hex = "a330c9" }, - [13] = { class = "Evoker", B = 0.50, G = 0.58, R = 0.20, hex = "33937f" } -} -br.classColor = tostring("|cff" .. br.classColors[select(3, br._G.UnitClass("player"))].hex) -br.qualityColors = { - blue = "0070dd", - green = "1eff00", - white = "ffffff", - grey = "9d9d9d" -} -br.druid = {} -br.evoker = {} +-- WoW API Compatibility Layer +-- Expansion-specific API wrappers are loaded via wowapi.lua -> retail.lua or mop.lua +-- This allows all API differences to be managed in one place per expansion +-- Access API functions via: br.api.wow.FunctionName() or br._G.FunctionName() -local nameSet = false --- [[ Functions Definition ]] -- -function br.setAddonName() - if not nameSet then - nameSet = true +-- Override br._G to use compatibility layer when available +local originalIndex = getmetatable(br._G).__index +setmetatable(br._G, { + __index = function(t, k) + -- First check if we have a compatibility wrapper + if br.api and br.api.wow and br.api.wow[k] then + return br.api.wow[k] + end + -- Fall back to global _G + return originalIndex[k] end -end +}) +-- System Table Initialization +br.api = br.api or {} -- Don't overwrite if already initialized by wowapi.lua +br.engines = {} +br.functions = {} +br.lists = {} +br.loader = {} +br.misc = {} +br.readers = {} +br.ui = {} +br.debug = {} +br.slashCommands = {} +br.unlockers = {} --- Custom Print -function br.debugPrint(message) - if br.data.settings[br.selectedSpec].toggles["isDebugging"] == true then - br._G.print(message) - end -end +-- Other Initialization +br.data = {} +br.data.settings = {} +br.loader.addonName = "BadRotations" +br.loader.rotations = {} +br.loader.selectedSpec = "None" +br.loader.selectedSpecID = 0 +br.loader.selectedProfile = 1 +br.loader.selectedProfileName = "None" +br.loader.settingsFile = "None.lua" +-- developers debug, use /run br.data.settings[br.loader.selectedSpec].toggles["isDebugging"] = true -- Run function br.Run() - if br.selectedSpec == nil then - br.selectedSpecID, br.selectedSpec = br._G.GetSpecializationInfo(br._G.GetSpecialization()) - if br.selectedSpec == "" then - br.selectedSpec = "Initial" + if br.loader.selectedSpec == nil then + br.loader.selectedSpecID, br.loader.selectedSpec = br._G.C_SpecializationInfo.GetSpecializationInfo(br._G.C_SpecializationInfo.GetSpecialization()) + if br.loader.selectedSpec == "" then + br.loader.selectedSpec = "Initial" end end -- add minimap fire icon - br:MinimapButton() + br.ui:MinimapButton() -- Build up pulse frame (hearth) - if not br.loadedIn then + if not loadedIn then -- Start Logs -- combat log - br.read.combatLog() + br.readers.combatLog:combatLog() -- other readers - br.read.commonReaders() + br.readers.common:commonReaders() -- Start Engines - br:Engine() - br:ObjectManager() - br:ObjectTracker() - -- Complete Loadin - br.ChatOverlay("-= BadRotations Loaded =-") - br._G.print("Initialization Complete, Finding Previous Settings.") - br.loadedIn = true - end -end - --- Default Settings -function br.defaultSettings() - br._G.C_Timer.After( - 2, - function() - if br.ui.window.config == nil then - br.ui.window.config = {} - end - if br.ui.window.config.frame == nil then - br.ui:createConfigWindow() - end - br.ui:toggleWindow("config") - br.ui:closeWindow("config") - end - ) - -- Settings Per Spec - if br.data.settings[br.selectedSpec].toggles == nil then - br.data.settings[br.selectedSpec].toggles = {} - end - if br.data.settings[br.selectedSpec]["RotationDrop"] == nil then - br.selectedProfile = 1 - else - br.selectedProfile = br.data.settings[br.selectedSpec]["RotationDrop"] - end - if br.data.settings[br.selectedSpec][br.selectedProfile] == nil then - br.data.settings[br.selectedSpec][br.selectedProfile] = {} - end - -- Define Main Button if no settings exist - if (br.data.settings and br.data.settings.mainButton == nil) then - br.data.settings.mainButton = { - pos = { - anchor = "CENTER", - x = -75, - y = -200 - } - } - br.data.settings.buttonSize = 32 - br.data.settings.font = "Fonts/arialn.ttf" - br.data.settings.fontsize = 16 - br.data.settings.wiped = true - end - -- Define Minimap Button if no settings exist - if (br.data.settings and br.data.settings.minimapButton == nil) then - br.data.settings.minimapButton = { - pos = { - x = 75.70, - y = -6.63 - } - } - end -end - --- Load Saved Settings -function br.loadSavedSettings() - if br.initializeSettings then - br.initOM = true - br.loader.loadProfiles() - br:loadLastProfileTracker() - if br.data.settings[br.selectedSpec]["RotationDropValue"] then - br:loadSettings(nil, nil, nil, br.data.settings[br.selectedSpec]["RotationDropValue"]) - elseif br.rotations[br.selectedSpec] then - br:loadSettings(nil, nil, nil, br.rotations[br.selectedSpec][1].name) - else - if not br.rotations[br.selectedSpec] then - br.initializeSettings = false - return - end + br.engines:Main() + br.engines:ObjectManager() + br.engines:ObjectTracker() + -- Initialize Loot Engine + if br.engines.lootEngine and br.engines.lootEngine.init then + br.engines.lootEngine:init() end - br.defaultSettings() - -- Build the Toggles - br.TogglesFrame() - -- Restore Minimap Button Position - br.BadRotationsButton:SetPoint("CENTER", br.data.settings.minimapButton.pos.x, - br.data.settings.minimapButton.pos.y) - br.initializeSettings = false + loadedIn = true end end function br.load() + if _br_loaded then return end + _br_loaded = true -- Update Selected Spec - br.selectedSpecID, br.selectedSpec = br._G.GetSpecializationInfo(br._G.GetSpecialization()) - if br.selectedSpec == "" then - br.selectedSpec = "Initial" + br.loader.selectedSpecID, br.loader.selectedSpec = br._G.C_SpecializationInfo.GetSpecializationInfo(br._G.C_SpecializationInfo.GetSpecialization()) + if br.loader.selectedSpec == "" then + br.loader.selectedSpec = "Initial" end if br.data == nil then br.data = {} @@ -216,22 +98,20 @@ function br.load() if br.data.ui == nil then br.data.ui = {} end - if br.data.settings[br.selectedSpec] == nil then - br.data.settings[br.selectedSpec] = {} + if br.data.settings[br.loader.selectedSpec] == nil then + br.data.settings[br.loader.selectedSpec] = {} end if not br.unlocked then - br.initializeSettings = true - print(br.classColor .. "[BadRotations] |cffFFFFFFInitializing Please Wait...") + br.ui.settingsManagement.initializeSettings = true + print(br.ui.colors.class .. "[BadRotations] |cffFFFFFFInitializing Please Wait...") end - br.equipHasChanged = true - if not br.loadedIn then - if br.damaged == nil then - br.damaged = {} + if not loadedIn then + if br.engines.enemiesEngine.damaged == nil then + br.engines.enemiesEngine.damaged = {} end - br.bagsUpdated = true + -- br.bagsUpdated = true br:Run() end - br.timeOfLastLoadingScreen = br._G.GetTime() end -- [[ Event Listeners ]] -- @@ -252,23 +132,29 @@ local function OnEvent(self, event) br.disablePulse = false end if event == "PLAYER_LOGOUT" then - if br.unlocked then - -- Return queue window to previous setting + -- Attempt to save settings and UI state on logout regardless of unlock status. + -- Guard calls to avoid errors if subsystems are not available. + -- Return queue window to previous setting if possible + if br._G and br._G.C_CVar and br._G.GetCVar and br._G.RunMacroText then if br._G.C_CVar.GetCVar("SpellQueueWindow") == "0" then - br._G.RunMacroText("/console SpellQueueWindow " .. br.prevQueueWindow) + br._G.RunMacroText("/console SpellQueueWindow " .. br._G.GetCVar("SpellQueueWindow")) end + end + if br.ui and br.ui.settingsManagement then br.ui:saveWindowPosition() - br:cleanSettings() - table.wipe(br.data.settings[br.selectedSpec][br.selectedProfile]["PageList"]) - table.wipe(br.ui) - if br.getOptionCheck("Reset Options") then + br.ui.settingsManagement:cleanSettings() + if br.data and br.data.settings and br.data.settings[br.loader.selectedSpec] and br.data.settings[br.loader.selectedSpec][br.loader.selectedProfile] and br.data.settings[br.loader.selectedSpec][br.loader.selectedProfile]["PageList"] then + table.wipe(br.data.settings[br.loader.selectedSpec][br.loader.selectedProfile]["PageList"]) + end + if br.functions and br.functions.misc and br.functions.misc:getOptionCheck("Reset Options") then -- Reset Settings - br:saveSettings(nil, nil, br.selectedSpec, br.selectedProfileName, true) + br.ui.settingsManagement:saveSettings(nil, nil, br.loader.selectedSpec, br.loader.selectedProfileName, true) else -- Save Settings - br:saveSettings(nil, nil, br.selectedSpec, br.selectedProfileName) + br.ui.settingsManagement:saveSettings(nil, nil, br.loader.selectedSpec, br.loader.selectedProfileName) end - br.saveLastProfileTracker() + br.ui.settingsManagement:saveLastProfileTracker() + table.wipe(br.ui) end end if event == "PLAYER_ENTERING_WORLD" then diff --git a/BadRotations.toc b/BadRotations.toc index 117a9b8be0..816e64a612 100644 --- a/BadRotations.toc +++ b/BadRotations.toc @@ -44,6 +44,13 @@ Libs\DiesalLibs.lua #@end-no-lib-strip@ +## WoW API Compatibility +System\wowapi.lua +System\Expansions\retail.lua +System\Expansions\mop.lua +System\Expansions\tbc.lua +System\Expansions\classic.lua + BadRotations.lua System\UI\UI.lua System\UI\MinimapButton.lua @@ -58,8 +65,6 @@ misc.xml System\Unlockers.lua System\Core.lua System\Debugging.lua -System\LootManager.lua -System\UsefulFeatures.lua System\SlashCommands.lua ## Functions @@ -78,10 +83,13 @@ System\Functions\Range.lua System\Functions\Spell.lua System\Functions\Unit.lua System\Functions\SmartQueue.lua +System\Functions\Glyph.lua +System\Functions\Totem.lua ## Lists System\Lists\AoEDamage.lua +System\Lists\Bosses.lua System\Lists\BurnUnits.lua System\Lists\CCUnits.lua System\Lists\Defensives.lua @@ -118,12 +126,17 @@ System\Engines\HealingEngineFrame.lua System\Engines\HealingEngineFunctions.lua #System\Engines\InterruptsFrame.lua -- Unused System\Engines\Interrupts.lua +System\Engines\LootEngine.lua +System\Engines\LootEngineFunctions.lua System\Engines\ttdTable.lua System\Engines\QuestTracker.lua System\Engines\Tracker.lua ## BadRotations UI +System\UI\Tooltips.lua +System\UI\Colors.lua +System\UI\DropOptions.lua System\UI\SettingsManagement.lua System\UI\ChatOverlay.lua #System\UI\MinimapButton.lua @@ -160,6 +173,7 @@ System\API\Debuff.lua System\API\Enemies.lua System\API\Essence.lua System\API\Equiped.lua +System\API\Glyph.lua System\API\Has.lua System\API\Item.lua System\API\Module.lua @@ -168,6 +182,7 @@ System\API\Power.lua # System\API\Runeforge.lua System\API\Spell.lua System\API\Talent.lua +System\API\Totem.lua System\API\UI.lua System\API\Unit.lua System\API\Units.lua @@ -203,8 +218,6 @@ System\Updater.lua # System\Unlockers.lua # System\Core.lua # System\Debugging.lua -# System\LootManager.lua -# System\UsefulFeatures.lua # System\SlashCommands.lua # ## Functions @@ -281,12 +294,15 @@ System\Updater.lua # System\Engines\HealingEngineFunctions.lua # #System\Engines\InterruptsFrame.lua -- Unused # System\Engines\Interrupts.lua +# System\Engines\LootEngine.lua +# System\Engines\LootEngineFunctions.lua # System\Engines\ttdTable.lua # System\Engines\QuestTracker.lua # System\Engines\Tracker.lua # ## BadRotations UI +# System\UI\Tooltips.lua # System\UI\SettingsManagement.lua # System\UI\ChatOverlay.lua # System\UI\MinimapButton.lua diff --git a/Libs/!LibDraw/LibDraw.lua b/Libs/!LibDraw/LibDraw.lua index 5961a1d712..7ec597fba0 100644 --- a/Libs/!LibDraw/LibDraw.lua +++ b/Libs/!LibDraw/LibDraw.lua @@ -1,12 +1,12 @@ -- LubDraw by docbrown on fh-wow.com -local _, br = ... +local Nn, br = ... local LibDraw local sin, cos, atan, atan2, sqrt, rad = math.sin, math.cos, math.atan, math.atan2, math.sqrt, math.rad local tinsert, tremove = tinsert, tremove local function WorldToScreen (wX, wY, wZ) - if wZ == nil then wZ = select(3,br.GetObjectPosition("player")) end + if wZ == nil then wZ = select(3,br.functions.unit:GetObjectPosition("player")) end -- for k,v in pairs(br._G) do -- br._G.print("K: "..tostring(k)..", V: "..tostring(v)) -- end @@ -14,12 +14,25 @@ local function WorldToScreen (wX, wY, wZ) if not sX or sX == 0 then return false, false end - if sX and sY and br.unlocker ~= "WA" and br.unlocker ~= "LuaBox" then - return sX, -(WorldFrame:GetTop() - sY); - else + -- if sX and sY and br.unlockers.selected ~= "WA" and br.unlockers.selected ~= "LuaBox" then + -- return sX, -(WorldFrame:GetTop() - sY); + -- else return sX, sY; - end + -- end end +-- local UIParent = UIParent +-- local function WorldToScreen(sx, sy, sz) +-- local x, y = Nn.WorldToScreen(sx, sy, sz) +-- if x == 0 and y == 0 then +-- return +-- end +-- local scale = UIParent:GetScale() +-- local width = UIParent:GetWidth() * scale +-- local height = UIParent:GetHeight() * scale +-- x = x * width +-- y = -(height - (y * height)) +-- return x, y +-- end if LibStub then -- LibStub version control @@ -135,8 +148,8 @@ function LibDraw.Array(vectors, x, y, z, rotationX, rotationY, rotationZ) ex, ey, ez = LibDraw.rotateZ(x, y, z, ex, ey, ez, rotationZ) end - local sx, sy = WorldToScreen(sx, sy, sz) - local ex, ey = WorldToScreen(ex, ey, ez) + sx, sy = WorldToScreen(sx, sy, sz) + ex, ey = WorldToScreen(ex, ey, ez) LibDraw.Draw2DLine(sx, sy, ex, ey) end end diff --git a/Libs/DiesalGUI-1.0/DiesalGUI-1.0.lua b/Libs/DiesalGUI-1.0/DiesalGUI-1.0.lua index 96bc3ae86c..761bd8d8c3 100644 --- a/Libs/DiesalGUI-1.0/DiesalGUI-1.0.lua +++ b/Libs/DiesalGUI-1.0/DiesalGUI-1.0.lua @@ -120,16 +120,43 @@ end -- ~~| Object Diesal Base |~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ObjectBase.CreateRegion = function(self, regionType, regionName, parentRegion, defaultFontObject) if regionType == 'FontString' then - local fontString = parentRegion:CreateFontString() + -- Try creating with explicit parameters + local fontString = parentRegion:CreateFontString(nil, "OVERLAY") + + -- CRITICAL: Set font IMMEDIATELY and DON'T let anything override it if it fails + -- Start with GameFontNormal which is guaranteed to work + fontString:SetFontObject(_G.GameFontNormal) + -- set Default font properties if defaultFontObject then fontString.defaultFontObject = defaultFontObject else - fontString.defaultFontObject = DiesalFontNormal + fontString.defaultFontObject = _G.DiesalFontNormal or _G.GameFontNormal + end + + -- ONLY try to apply custom font if we can verify it's valid first + if fontString.defaultFontObject and fontString.defaultFontObject ~= _G.GameFontNormal and type(fontString.defaultFontObject.GetFont) == "function" then + local success, fontPath, fontSize, fontFlags = pcall(fontString.defaultFontObject.GetFont, fontString.defaultFontObject) + -- Only apply if we got a valid font path + if success and fontPath and fontPath ~= "" then + local fontSuccess = pcall(fontString.SetFont, fontString, fontPath, fontSize or 12, fontFlags or "") + -- If custom font failed, font object fallback is still active from SetFontObject above + end + end + + -- Set colors and spacing safely + if fontString.defaultFontObject and type(fontString.defaultFontObject.GetTextColor) == "function" then + local success, r, g, b, a = pcall(fontString.defaultFontObject.GetTextColor, fontString.defaultFontObject) + if success and r then + pcall(fontString.SetTextColor, fontString, r, g, b, a or 1) + end + end + if fontString.defaultFontObject and type(fontString.defaultFontObject.GetSpacing) == "function" then + local success, spacing = pcall(fontString.defaultFontObject.GetSpacing, fontString.defaultFontObject) + if success and spacing then + pcall(fontString.SetSpacing, fontString, spacing) + end end - fontString:SetFont(fontString.defaultFontObject:GetFont()) - fontString:SetTextColor(fontString.defaultFontObject:GetTextColor()) - fontString:SetSpacing(fontString.defaultFontObject:GetSpacing()) self[regionName] = fontString self.fontStrings[regionName] = fontString @@ -141,15 +168,47 @@ ObjectBase.CreateRegion = function(self, regionType, regionName, parentRegion, d end if regionType == 'EditBox' then local editBox = CreateFrame(regionType, nil, parentRegion) + + -- CRITICAL: Set a font IMMEDIATELY with multiple fallbacks + if _G.GameFontNormal then + pcall(editBox.SetFontObject, editBox, _G.GameFontNormal) + end + pcall(editBox.SetFont, editBox, "Fonts\\FRIZQT__.TTF", 12, "") + + local testFont = editBox:GetFont() + if not testFont and _G.ChatFontNormal then + pcall(editBox.SetFontObject, editBox, _G.ChatFontNormal) + end + -- set Default font properties if defaultFontObject then editBox.defaultFontObject = defaultFontObject else - editBox.defaultFontObject = DiesalFontNormal + editBox.defaultFontObject = _G.DiesalFontNormal or _G.GameFontNormal + end + + -- Try to apply custom font if available + if editBox.defaultFontObject and type(editBox.defaultFontObject.GetFont) == "function" then + local success, fontPath, fontSize, fontFlags = pcall(editBox.defaultFontObject.GetFont, editBox.defaultFontObject) + if success and fontPath then + pcall(editBox.SetFont, editBox, fontPath, fontSize or 11, fontFlags or "") + end end - editBox:SetFont(editBox.defaultFontObject:GetFont()) - editBox:SetTextColor(editBox.defaultFontObject:GetTextColor()) - editBox:SetSpacing(editBox.defaultFontObject:GetSpacing()) + + -- Set colors and spacing safely + if editBox.defaultFontObject and type(editBox.defaultFontObject.GetTextColor) == "function" then + local success, r, g, b, a = pcall(editBox.defaultFontObject.GetTextColor, editBox.defaultFontObject) + if success and r then + pcall(editBox.SetTextColor, editBox, r, g, b, a or 1) + end + end + if editBox.defaultFontObject and type(editBox.defaultFontObject.GetSpacing) == "function" then + local success, spacing = pcall(editBox.defaultFontObject.GetSpacing, editBox.defaultFontObject) + if success and spacing then + pcall(editBox.SetSpacing, editBox, spacing) + end + end + editBox:HookScript('OnEscapePressed', function(this) DiesalGUI:ClearFocus(); end) editBox:HookScript('OnEditFocusGained', function(this) DiesalGUI:SetFocus(this); GameTooltip:Hide(); @@ -160,15 +219,46 @@ ObjectBase.CreateRegion = function(self, regionType, regionName, parentRegion, d end if regionType == 'ScrollingMessageFrame' then local srollingMessageFrame = CreateFrame(regionType, nil, parentRegion) + + -- CRITICAL: Set a font IMMEDIATELY with multiple fallbacks + if _G.GameFontNormal then + pcall(srollingMessageFrame.SetFontObject, srollingMessageFrame, _G.GameFontNormal) + end + pcall(srollingMessageFrame.SetFont, srollingMessageFrame, "Fonts\\FRIZQT__.TTF", 12, "") + + local testFont = srollingMessageFrame:GetFont() + if not testFont and _G.ChatFontNormal then + pcall(srollingMessageFrame.SetFontObject, srollingMessageFrame, _G.ChatFontNormal) + end + -- set Default font properties if defaultFontObject then srollingMessageFrame.defaultFontObject = defaultFontObject else - srollingMessageFrame.defaultFontObject = DiesalFontNormal + srollingMessageFrame.defaultFontObject = _G.DiesalFontNormal or _G.GameFontNormal + end + + -- Try to apply custom font if available + if srollingMessageFrame.defaultFontObject and type(srollingMessageFrame.defaultFontObject.GetFont) == "function" then + local success, fontPath, fontSize, fontFlags = pcall(srollingMessageFrame.defaultFontObject.GetFont, srollingMessageFrame.defaultFontObject) + if success and fontPath then + pcall(srollingMessageFrame.SetFont, srollingMessageFrame, fontPath, fontSize or 11, fontFlags or "") + end + end + + -- Set colors and spacing safely + if srollingMessageFrame.defaultFontObject and type(srollingMessageFrame.defaultFontObject.GetTextColor) == "function" then + local success, r, g, b, a = pcall(srollingMessageFrame.defaultFontObject.GetTextColor, srollingMessageFrame.defaultFontObject) + if success and r then + pcall(srollingMessageFrame.SetTextColor, srollingMessageFrame, r, g, b, a or 1) + end + end + if srollingMessageFrame.defaultFontObject and type(srollingMessageFrame.defaultFontObject.GetSpacing) == "function" then + local success, spacing = pcall(srollingMessageFrame.defaultFontObject.GetSpacing, srollingMessageFrame.defaultFontObject) + if success and spacing then + pcall(srollingMessageFrame.SetSpacing, srollingMessageFrame, spacing) + end end - srollingMessageFrame:SetFont(srollingMessageFrame.defaultFontObject:GetFont()) - srollingMessageFrame:SetTextColor(srollingMessageFrame.defaultFontObject:GetTextColor()) - srollingMessageFrame:SetSpacing(srollingMessageFrame.defaultFontObject:GetSpacing()) self[regionName] = srollingMessageFrame return srollingMessageFrame @@ -179,7 +269,13 @@ ObjectBase.CreateRegion = function(self, regionType, regionName, parentRegion, d end ObjectBase.ResetFonts = function(self) for name, fontString in pairs(self.fontStrings) do - fontString:SetFont(fontString.defaultFontObject:GetFont()) + -- Safe font initialization with fallback + local fontPath, fontSize, fontFlags = fontString.defaultFontObject:GetFont() + if not fontPath then + -- Fallback to WoW default if GetFont returns nil + fontPath, fontSize, fontFlags = "Fonts\\FRIZQT__.TTF", 11, "" + end + fontString:SetFont(fontPath, fontSize, fontFlags) fontString:SetTextColor(fontString.defaultFontObject:GetTextColor()) fontString:SetSpacing(fontString.defaultFontObject:GetSpacing()) end diff --git a/Libs/DiesalGUI-1.0/DiesalGUI-1.0.xml b/Libs/DiesalGUI-1.0/DiesalGUI-1.0.xml index 23722d185d..878b297db4 100644 --- a/Libs/DiesalGUI-1.0/DiesalGUI-1.0.xml +++ b/Libs/DiesalGUI-1.0/DiesalGUI-1.0.xml @@ -2,6 +2,7 @@ ..\FrameXML\UI.xsd">