if not WeakAuras.IsCorrectVersion() or not WeakAuras.IsLibsOK() then return end local AddonName, OptionsPrivate = ... -- Lua APIs local rad = rad -- WoW APIs local CreateFrame = CreateFrame local AceGUI = LibStub("AceGUI-3.0") local WeakAuras = WeakAuras local L = WeakAuras.L local modelPicker local function GetAll(baseObject, path, property, default) local valueFromPath = OptionsPrivate.Private.ValueFromPath if not property then return default end local result = default local first = true for child in OptionsPrivate.Private.TraverseLeafsOrAura(baseObject) do local childObject = valueFromPath(child, path) if childObject and childObject[property] then if first then result = childObject[property] first = false else if result ~= childObject[property] then return default end end end end return result end local function ConstructModelPicker(frame) local function RecurseSetFilter(tree, filter) for k, v in ipairs(tree) do if v.children == nil and v.text then v.visible = not filter or filter == "" or v.text:find(filter, 1, true) ~= nil else RecurseSetFilter(v.children, filter) end end end local group = AceGUI:Create("InlineGroup"); group.frame:SetParent(frame); group.frame:SetPoint("BOTTOMRIGHT", frame, "BOTTOMRIGHT", -17, 87); group.frame:SetPoint("TOPLEFT", frame, "TOPLEFT", 17, -15); group.frame:Hide(); group:SetLayout("flow"); local filterInput = CreateFrame("EditBox", "WeakAurasFilterInput", group.frame, "SearchBoxTemplate") filterInput:SetScript("OnTextChanged", function(self) SearchBoxTemplate_OnTextChanged(self) local filterText = filterInput:GetText() RecurseSetFilter(group.modelTree.tree, filterText) group.modelTree.filter = filterText ~= nil and filterText ~= "" group.modelTree:RefreshTree() end) filterInput:SetHeight(15) filterInput:SetPoint("TOP", group.frame, "TOP", 0, 1) filterInput:SetPoint("LEFT", group.frame, "LEFT", 7, 0) filterInput:SetWidth(200) filterInput:SetFont(STANDARD_TEXT_FONT, 10) group.frame.filterInput = filterInput -- Old X Y Z controls local modelPickerZ = AceGUI:Create("Slider"); modelPickerZ:SetSliderValues(-20, 20, 0.05); modelPickerZ:SetLabel(L["Z Offset"]); modelPickerZ.frame:SetParent(group.frame); modelPickerZ:SetCallback("OnValueChanged", function() group:Pick(nil, nil, modelPickerZ:GetValue()); end); local modelPickerX = AceGUI:Create("Slider"); modelPickerX:SetSliderValues(-20, 20, 0.05); modelPickerX:SetLabel(L["X Offset"]); modelPickerX.frame:SetParent(group.frame); modelPickerX:SetCallback("OnValueChanged", function() group:Pick(nil, nil, nil, modelPickerX:GetValue()); end); local modelPickerY = AceGUI:Create("Slider"); modelPickerY:SetSliderValues(-20, 20, 0.05); modelPickerY:SetLabel(L["Y Offset"]); modelPickerY.frame:SetParent(group.frame); modelPickerY:SetCallback("OnValueChanged", function() group:Pick(nil, nil, nil, nil, modelPickerY:GetValue()); end); local modelPickerRotation = AceGUI:Create("Slider"); modelPickerRotation:SetSliderValues(0, 360, 0.05); modelPickerRotation:SetLabel(L["Rotation"]); modelPickerRotation.frame:SetParent(group.frame); modelPickerRotation:SetCallback("OnValueChanged", function() group:Pick(nil, nil, nil, nil, nil, modelPickerRotation:GetValue()); end); -- New TX TY TZ, RX, RY, RZ, US controls local modelPickerTX = AceGUI:Create("Slider"); modelPickerTX:SetSliderValues(-1000, 1000, 1); modelPickerTX:SetLabel(L["X Offset"]); modelPickerTX.frame:SetParent(group.frame); modelPickerTX:SetCallback("OnValueChanged", function() group:PickSt(nil, nil, modelPickerTX:GetValue()); end); local modelPickerTY = AceGUI:Create("Slider"); modelPickerTY:SetSliderValues(-1000, 1000, 1); modelPickerTY:SetLabel(L["Y Offset"]); modelPickerTY.frame:SetParent(group.frame); modelPickerTY:SetCallback("OnValueChanged", function() group:PickSt(nil, nil, nil, modelPickerTY:GetValue()); end); local modelPickerTZ = AceGUI:Create("Slider"); modelPickerTZ:SetSliderValues(-1000, 1000, 1); modelPickerTZ:SetLabel(L["Z Offset"]); modelPickerTZ.frame:SetParent(group.frame); modelPickerTZ:SetCallback("OnValueChanged", function() group:PickSt(nil, nil, nil, nil, modelPickerTZ:GetValue()); end); local modelPickerRX = AceGUI:Create("Slider"); modelPickerRX:SetSliderValues(0, 360, 1); modelPickerRX:SetLabel(L["X Rotation"]); modelPickerRX.frame:SetParent(group.frame); modelPickerRX:SetCallback("OnValueChanged", function() group:PickSt(nil, nil, nil, nil, nil, modelPickerRX:GetValue()); end); local modelPickerRY = AceGUI:Create("Slider"); modelPickerRY:SetSliderValues(0, 360, 1); modelPickerRY:SetLabel(L["Y Rotation"]); modelPickerRY.frame:SetParent(group.frame); modelPickerRY:SetCallback("OnValueChanged", function() group:PickSt(nil, nil, nil, nil, nil, nil, modelPickerRY:GetValue()); end); local modelPickerRZ = AceGUI:Create("Slider"); modelPickerRZ:SetSliderValues(0, 360, 1); modelPickerRZ:SetLabel(L["Z Rotation"]); modelPickerRZ.frame:SetParent(group.frame); modelPickerRZ:SetCallback("OnValueChanged", function() group:PickSt(nil, nil, nil, nil, nil, nil, nil, modelPickerRZ:GetValue()); end); local modelPickerUS = AceGUI:Create("Slider"); modelPickerUS:SetSliderValues(5, 1000, 1); modelPickerUS:SetLabel(L["Scale"]); modelPickerUS.frame:SetParent(group.frame); modelPickerUS:SetCallback("OnValueChanged", function() group:PickSt(nil, nil, nil, nil, nil, nil, nil, nil, modelPickerUS:GetValue()); end); local modelTree = AceGUI:Create("WeakAurasTreeGroup"); group.modelTree = modelTree; group.frame:SetScript("OnSizeChanged", function() local frameWidth = frame:GetWidth(); local sliderWidth = (frameWidth - 50) / 4; local narrowSliderWidth = (frameWidth - 50) / 7; modelTree:SetTreeWidth(frameWidth - 370); modelPickerZ.frame:SetPoint("bottomleft", frame, "bottomleft", 15, 43); modelPickerZ.frame:SetPoint("bottomright", frame, "bottomleft", 15 + sliderWidth, 43); modelPickerX.frame:SetPoint("bottomleft", frame, "bottomleft", 25 + sliderWidth, 43); modelPickerX.frame:SetPoint("bottomright", frame, "bottomleft", 25 + (2 * sliderWidth), 43); modelPickerY.frame:SetPoint("bottomleft", frame, "bottomleft", 35 + (2 * sliderWidth), 43); modelPickerY.frame:SetPoint("bottomright", frame, "bottomleft", 35 + (3 * sliderWidth), 43); modelPickerRotation.frame:SetPoint("bottomleft", frame, "bottomleft", 45 + (3 * sliderWidth), 43); modelPickerRotation.frame:SetPoint("bottomright", frame, "bottomleft", 45 + (4 * sliderWidth), 43); -- New controls modelPickerTX.frame:SetPoint("bottomleft", frame, "bottomleft", 15, 43); modelPickerTX.frame:SetPoint("bottomright", frame, "bottomleft", 15 + narrowSliderWidth, 43); modelPickerTY.frame:SetPoint("bottomleft", frame, "bottomleft", 20 + narrowSliderWidth, 43); modelPickerTY.frame:SetPoint("bottomright", frame, "bottomleft", 20 + (2 * narrowSliderWidth), 43); modelPickerTZ.frame:SetPoint("bottomleft", frame, "bottomleft", 25 + (2 * narrowSliderWidth), 43); modelPickerTZ.frame:SetPoint("bottomright", frame, "bottomleft", 25 + (3 * narrowSliderWidth), 43); modelPickerRX.frame:SetPoint("bottomleft", frame, "bottomleft", 30 + (3 * narrowSliderWidth), 43); modelPickerRX.frame:SetPoint("bottomright", frame, "bottomleft", 30 + (4 * narrowSliderWidth), 43); modelPickerRY.frame:SetPoint("bottomleft", frame, "bottomleft", 35 + (4 * narrowSliderWidth), 43); modelPickerRY.frame:SetPoint("bottomright", frame, "bottomleft", 35 + (5 * narrowSliderWidth), 43); modelPickerRZ.frame:SetPoint("bottomleft", frame, "bottomleft", 40 + (5 * narrowSliderWidth), 43); modelPickerRZ.frame:SetPoint("bottomright", frame, "bottomleft", 40 + (6 * narrowSliderWidth), 43); modelPickerUS.frame:SetPoint("bottomleft", frame, "bottomleft", 45 + (6 * narrowSliderWidth), 43); modelPickerUS.frame:SetPoint("bottomright", frame, "bottomleft", 45 + (7 * narrowSliderWidth), 43); end); group:SetLayout("fill"); modelTree:SetTree(WeakAuras.ModelPaths); modelTree:SetCallback("OnGroupSelected", function(self, event, value, fileId) local path = string.gsub(value, "\001", "/"); if(string.lower(string.sub(path, -3, -1)) == ".m2") then local model_path = path; if (group.selectedValues.api) then group:PickSt(model_path, fileId); else group:Pick(model_path, fileId); end end end); group:AddChild(modelTree); local model = CreateFrame("PlayerModel", nil, group.content); model:SetAllPoints(modelTree.content); model:SetFrameStrata("FULLSCREEN"); group.model = model; local startX, rotation local function OnUpdateScript() local uiScale, x = UIParent:GetEffectiveScale(), GetCursorPosition() local screenW, screenH = GetScreenWidth(), GetScreenHeight() local diffX = startX/uiScale - x/uiScale rotation = (rotation + 180 / screenW * diffX) % 360 model:SetFacing(rad(rotation)) end model:EnableMouse() model:SetScript("OnMouseDown", function(self) if not group.selectedValues.api then startX = GetCursorPosition() rotation = group.selectedValues.rotation or 0 self:SetScript("OnUpdate", OnUpdateScript) end end) model:SetScript("OnMouseUp", function(self) if not group.selectedValues.api then self:SetScript("OnUpdate", nil) group:Pick(nil, nil, nil, nil, nil, rotation) end end) local function SetStOnObject(object, model_path, model_fileId, model_tx, model_ty, model_tz, model_rx, model_ry, model_rz, model_us) if model_path then object.model_path = model_path end if model_fileId then object.model_fileId = model_fileId end if model_tx then object.model_st_tx = model_tx end if model_ty then object.model_st_ty = model_ty end if model_tz then object.model_st_tz = model_tz end if model_rx then object.model_st_rx = model_rx end if model_ry then object.model_st_ry = model_ry end if model_rz then object.model_st_rz = model_rz end if model_us then object.model_st_us = model_us end end function group.PickSt(self, model_path, model_fileId, model_tx, model_ty, model_tz, model_rx, model_ry, model_rz, model_us) local valueFromPath = OptionsPrivate.Private.ValueFromPath self.selectedValues.model_path = model_path or self.selectedValues.model_path self.selectedValues.model_fileId = model_fileId or self.selectedValues.model_fileId self.selectedValues.model_st_tx = model_tx or self.selectedValues.model_st_tx self.selectedValues.model_st_ty = model_ty or self.selectedValues.model_st_ty self.selectedValues.model_st_tz = model_tz or self.selectedValues.model_st_tz self.selectedValues.model_st_rx = model_rx or self.selectedValues.model_st_rx; self.selectedValues.model_st_ry = model_ry or self.selectedValues.model_st_ry; self.selectedValues.model_st_rz = model_rz or self.selectedValues.model_st_rz; self.selectedValues.model_st_us = model_us or self.selectedValues.model_st_us; WeakAuras.SetModel(self.model, self.selectedValues.model_path, self.selectedValues.model_fileId) self.model:SetTransform(self.selectedValues.model_st_tx / 1000, self.selectedValues.model_st_ty / 1000, self.selectedValues.model_st_tz / 1000, rad(self.selectedValues.model_st_rx), rad(self.selectedValues.model_st_ry), rad(self.selectedValues.model_st_rz), self.selectedValues.model_st_us / 1000); for child in OptionsPrivate.Private.TraverseLeafsOrAura(self.baseObject) do local object = valueFromPath(child, self.path) if(object) then SetStOnObject(object, model_path, model_fileId, model_tx, model_ty, model_tz, model_rx, model_ry, model_rz, model_us) WeakAuras.Add(child); WeakAuras.UpdateThumbnail(child); end end end local function SetOnObject(object, model_path, model_fileId, model_z, model_x, model_y, rotation) if model_path then object.model_path = model_path end if model_fileId then object.model_fileId = model_fileId end if model_z then object.model_z = model_z end if model_x then object.model_x = model_x end if model_y then object.model_y = model_y end if rotation then object.rotation = rotation end end function group.Pick(self, model_path, model_fileId, model_z, model_x, model_y, rotation) local valueFromPath = OptionsPrivate.Private.ValueFromPath self.selectedValues.model_path = model_path or self.selectedValues.model_path self.selectedValues.model_fileId = model_fileId or self.selectedValues.model_fileId self.selectedValues.model_x = model_x or self.selectedValues.model_x self.selectedValues.model_y = model_y or self.selectedValues.model_y self.selectedValues.model_z = model_z or self.selectedValues.model_z self.selectedValues.rotation = rotation or self.selectedValues.rotation WeakAuras.SetModel(self.model, self.selectedValues.model_path, self.selectedValues.model_fileId) self.model:ClearTransform(); self.model:SetPosition(self.selectedValues.model_z, self.selectedValues.model_x, self.selectedValues.model_y); self.model:SetFacing(rad(self.selectedValues.rotation)); for child in OptionsPrivate.Private.TraverseLeafsOrAura(self.baseObject) do local object = valueFromPath(child, self.path) if(object) then SetOnObject(object, model_path, model_fileId, model_z, model_x, model_y, rotation) WeakAuras.Add(child) WeakAuras.UpdateThumbnail(child) end end end function group.Open(self, baseObject, path) local valueFromPath = OptionsPrivate.Private.ValueFromPath self.baseObject = baseObject self.path = path self.selectedValues = {} self.selectedValues.model_path = GetAll(baseObject, path, "model_path", "spells/arcanepower_state_chest.m2") self.selectedValues.model_fileId = GetAll(baseObject, path, "model_fileId", "122968") WeakAuras.SetModel(self.model, self.selectedValues.model_path, self.selectedValues.model_fileId) self.selectedValues.api = GetAll(baseObject, path, "api", false) self.selectedValues.model_st_tx = GetAll(baseObject, path, "model_st_tx", 0) self.selectedValues.model_st_ty = GetAll(baseObject, path, "model_st_ty", 0) self.selectedValues.model_st_tz = GetAll(baseObject, path, "model_st_tz", 0) self.selectedValues.model_st_rx = GetAll(baseObject, path, "model_st_rx", 0) self.selectedValues.model_st_ry = GetAll(baseObject, path, "model_st_ry", 0) self.selectedValues.model_st_rz = GetAll(baseObject, path, "model_st_rz", 0) self.selectedValues.model_st_us = GetAll(baseObject, path, "model_st_us", 0) self.selectedValues.model_x = GetAll(baseObject, path, "model_x", 0) self.selectedValues.model_y = GetAll(baseObject, path, "model_y", 0) self.selectedValues.model_z = GetAll(baseObject, path, "model_z", 0) self.selectedValues.rotation = GetAll(baseObject, path, "rotation", 0) if (self.selectedValues.api) then self.model:SetTransform(self.selectedValues.model_st_tx / 1000, self.selectedValues.model_st_ty / 1000, self.selectedValues.model_st_tz / 1000, rad(self.selectedValues.model_st_rx), rad(self.selectedValues.model_st_ry), rad(self.selectedValues.model_st_rz), self.selectedValues.model_st_us / 1000); modelPickerTX:SetValue(self.selectedValues.model_st_tx); modelPickerTX.editbox:SetText(("%.2f"):format(self.selectedValues.model_st_tx)); modelPickerTY:SetValue(self.selectedValues.model_st_ty); modelPickerTY.editbox:SetText(("%.2f"):format(self.selectedValues.model_st_ty)); modelPickerTZ:SetValue(self.selectedValues.model_st_tz); modelPickerTZ.editbox:SetText(("%.2f"):format(self.selectedValues.model_st_tz)); modelPickerRX:SetValue(self.selectedValues.model_st_rx); modelPickerRX.editbox:SetText(("%.2f"):format(self.selectedValues.model_st_rx)); modelPickerRY:SetValue(self.selectedValues.model_st_ry); modelPickerRY.editbox:SetText(("%.2f"):format(self.selectedValues.model_st_ry)); modelPickerRZ:SetValue(self.selectedValues.model_st_rz); modelPickerRZ.editbox:SetText(("%.2f"):format(self.selectedValues.model_st_rz)); modelPickerUS:SetValue(self.selectedValues.model_st_us); modelPickerUS.editbox:SetText(("%.2f"):format(self.selectedValues.model_st_us)); modelPickerZ.frame:Hide(); modelPickerY.frame:Hide(); modelPickerX.frame:Hide(); modelPickerRotation.frame:Hide(); modelPickerTX.frame:Show(); modelPickerTY.frame:Show(); modelPickerTZ.frame:Show(); modelPickerRX.frame:Show(); modelPickerRY.frame:Show(); modelPickerRZ.frame:Show(); modelPickerUS.frame:Show(); else self.model:ClearTransform(); self.model:SetPosition(self.selectedValues.model_z, self.selectedValues.model_x, self.selectedValues.model_y); self.model:SetFacing(rad(self.selectedValues.rotation)); modelPickerZ:SetValue(self.selectedValues.model_z); modelPickerZ.editbox:SetText(("%.2f"):format(self.selectedValues.model_z)); modelPickerX:SetValue(self.selectedValues.model_x); modelPickerX.editbox:SetText(("%.2f"):format(self.selectedValues.model_x)); modelPickerY:SetValue(self.selectedValues.model_y); modelPickerY.editbox:SetText(("%.2f"):format(self.selectedValues.model_y)); modelPickerRotation:SetValue(self.selectedValues.rotation); modelPickerRotation.editbox:SetText(("%.2f"):format(self.selectedValues.rotation)); modelPickerZ.frame:Show(); modelPickerY.frame:Show(); modelPickerX.frame:Show(); modelPickerRotation.frame:Show(); modelPickerTX.frame:Hide(); modelPickerTY.frame:Hide(); modelPickerTZ.frame:Hide(); modelPickerRX.frame:Hide(); modelPickerRY.frame:Hide(); modelPickerRZ.frame:Hide(); modelPickerUS.frame:Hide(); end if(baseObject.controlledChildren) then self.givenModel = {}; self.givenModelId = {}; self.givenApi = {}; self.givenZ = {}; self.givenX = {}; self.givenY = {}; self.givenRotation = {}; self.givenTX = {}; self.givenTY = {}; self.givenTZ = {}; self.givenRX = {}; self.givenRY = {}; self.givenRZ = {}; self.givenUS = {}; for child in OptionsPrivate.Private.TraverseLeafs(baseObject) do local childId = child.id local object = valueFromPath(child, path) if(object) then self.givenModel[childId] = object.model_path; self.givenModelId[childId] = object.model_fileId; self.givenApi[childId] = object.api; if (object.api) then self.givenTX[childId] = object.model_st_tx; self.givenTY[childId] = object.model_st_ty; self.givenTZ[childId] = object.model_st_tz; self.givenRX[childId] = object.model_st_rx; self.givenRY[childId] = object.model_st_ry; self.givenRZ[childId] = object.model_st_rz; self.givenUS[childId] = object.model_st_us; else self.givenZ[childId] = object.model_z; self.givenX[childId] = object.model_x; self.givenY[childId] = object.model_y; self.givenRotation[childId] = object.rotation; end end end else local object = valueFromPath(baseObject, path) self.givenModel = object.model_path; self.givenModelId = object.model_fileId; self.givenApi = object.api; if (object.api) then self.givenTX = object.model_st_tx; self.givenTY = object.model_st_ty; self.givenTZ = object.model_st_tz; self.givenRX = object.model_st_rx; self.givenRY = object.model_st_ry; self.givenRZ = object.model_st_rz; self.givenUS = object.model_st_us; else self.givenZ = object.model_z; self.givenX = object.model_x; self.givenY = object.model_y; self.givenRotation = object.rotation; end end frame.window = "model"; frame:UpdateFrameVisible() end function group.Close() frame.window = "default" frame:UpdateFrameVisible() WeakAuras.FillOptions() end function group.CancelClose() local valueFromPath = OptionsPrivate.Private.ValueFromPath if(group.baseObject.controlledChildren) then for child in OptionsPrivate.Private.TraverseLeafs(group.baseObject) do local childId = child.id local object = valueFromPath(child, group.path) if(object) then object.model_path = group.givenModel[childId]; object.model_fileId = group.givenModelId[childId]; object.api = group.givenApi[childId]; if (object.api) then object.model_st_tx = group.givenTX[childId]; object.model_st_ty = group.givenTY[childId]; object.model_st_tz = group.givenTZ[childId]; object.model_st_rx = group.givenRX[childId]; object.model_st_ry = group.givenRY[childId]; object.model_st_rz = group.givenRZ[childId]; object.model_st_us = group.givenUS[childId]; else object.model_z = group.givenZ[childId]; object.model_x = group.givenX[childId]; object.model_y = group.givenY[childId]; object.rotation = group.givenRotation[childId]; end WeakAuras.Add(child); WeakAuras.UpdateThumbnail(child); end end else local object = valueFromPath(group.baseObject, group.path) if(object) then object.model_path = group.givenModel object.model_fileId = group.givenModelId object.api = group.givenApi if (object.api) then object.model_st_tx = group.givenTX object.model_st_ty = group.givenTY object.model_st_tz = group.givenTZ object.model_st_rx = group.givenRX object.model_st_ry = group.givenRY object.model_st_rz = group.givenRZ object.model_st_us = group.givenUS else object.model_z = group.givenZ object.model_x = group.givenX object.model_y = group.givenY object.rotation = group.givenRotation end WeakAuras.Add(group.baseObject); WeakAuras.UpdateThumbnail(group.baseObject); end end group.Close(); end local cancel = CreateFrame("Button", nil, group.frame, "UIPanelButtonTemplate"); cancel:SetScript("OnClick", group.CancelClose); cancel:SetPoint("bottomright", frame, "bottomright", -27, 16); cancel:SetHeight(20); cancel:SetWidth(100); cancel:SetText(L["Cancel"]); local close = CreateFrame("Button", nil, group.frame, "UIPanelButtonTemplate"); close:SetScript("OnClick", group.Close); close:SetPoint("RIGHT", cancel, "LEFT", -10, 0); close:SetHeight(20); close:SetWidth(100); close:SetText(L["Okay"]); return group end function OptionsPrivate.ModelPicker(frame) modelPicker = modelPicker or ConstructModelPicker(frame) return modelPicker end