local GlobalAddonName, ExRT = ... local module = ExRT:New("RaidGroups",ExRT.L.RaidGroups) local ELib,L = ExRT.lib,ExRT.L local LibDeflate = LibStub:GetLibrary("LibDeflate") local UnitGroupRolesAssigned = UnitGroupRolesAssigned or ExRT.NULLfunc local VMRT = nil function module.main:ADDON_LOADED() VMRT = _G.VMRT VMRT.RaidGroups = VMRT.RaidGroups or {} VMRT.RaidGroups.profiles = VMRT.RaidGroups.profiles or {} if not VMRT.RaidGroups.upd4550 then VMRT.RaidGroups.KeepPosInGroup = true VMRT.RaidGroups.upd4550 = true end if not VMRT.RaidGroups.SplitGroups then VMRT.RaidGroups.SplitGroups = {true,true,true,true} end if not VMRT.RaidGroups.SplitParts then VMRT.RaidGroups.SplitParts = 2 end module:RegisterSlash() end function module.options:Load() self:CreateTilte() local function EditOnDragStart(self) if self:IsMovable() then self:StartMoving() end end local function EditOnDragStop(self) self:StopMovingOrSizing() for i=1,#module.options.edits do local edit2 = module.options.edits[i] if edit2:IsMouseOver() and edit2 ~= self then local textSelf = self:GetText() local textMO = edit2:GetText() self:SetText(textMO) edit2:SetText(textSelf) self:SetCursorPosition(1) edit2:SetCursorPosition(1) module.options:SaveCurrentSave() break end end self:NewPoint("TOPLEFT",module.options,10+((self.index-1) % 10 < 5 and 0 or 165),-40-((self.index-1) % 5)*22-floor((self.index-1)/10)*132) self:ClearFocus() end local function EditOnDragStopNotInList(self) self:StopMovingOrSizing() for i=1,#module.options.edits do local edit2 = module.options.edits[i] if edit2:IsMouseOver() and edit2 ~= self then local textSelf = self.preName or self:GetText() edit2:SetText(textSelf) edit2:SetCursorPosition(1) module.options:SaveCurrentSave() break end end self:NewPoint("TOPLEFT",module.options,355,-40-(self.index-1)*14) self:ClearFocus() module.options:UpdateNotInList() end local function EditOnChange(self,isUser) local name = self:GetText() if name and UnitName(name) then local r,g,b = ExRT.F.classColorNum(select(2,UnitClass(name))) self:SetTextColor(r,g,b,1) self:ColorBorder() elseif self.preclass then local r,g,b = ExRT.F.classColorNum(self.preclass) self:SetTextColor(r,g,b,1) self:ColorBorder() else self:SetTextColor(.7,.7,.7,1) if name and #name > 0 then self:ColorBorder(0.5,0.25,0.3,1) else self:ColorBorder() end end if name and UnitName(name) and UnitGroupRolesAssigned(name) then local role = UnitGroupRolesAssigned(name) if role == "HEALER" then self.roleIcon:SetAtlas("groupfinder-icon-role-large-heal") self.roleIcon:Show() elseif role == "DAMAGER" then self.roleIcon:SetAtlas("groupfinder-icon-role-large-dps") self.roleIcon:Show() elseif role == "TANK" then self.roleIcon:SetAtlas("groupfinder-icon-role-large-tank") self.roleIcon:Show() else self.roleIcon:Hide() end else self.roleIcon:Hide() end if isUser then module.options:UpdateNotInList() module.options:SaveCurrentSave() end end function self:UpdateTextColors() for i=1,40 do EditOnChange(self.edits[i],false) end end self.edits = {} for i=1,40 do local edit = ELib:Edit(self):Size(150,18) self.edits[i] = edit edit:SetText(i) edit.index = i edit:Point("TOPLEFT",10+((i-1) % 10 < 5 and 0 or 165),-40-((i-1) % 5)*22-floor((i-1)/10)*132) edit:OnChange(EditOnChange) edit.roleIcon = edit:CreateTexture(nil, "BACKGROUND") edit.roleIcon:SetPoint("RIGHT") edit.roleIcon:SetSize(16,16) edit.roleIcon:Hide() edit:SetMovable(true) edit:RegisterForDrag("LeftButton") edit:SetScript("OnDragStart", EditOnDragStart) edit:SetScript("OnDragStop", EditOnDragStop) end for i=1,4 do for j=1,2 do ELib:Text(self,GROUP.." "..((i-1)*2+j)):Point("TOPLEFT",10+(j == 1 and 0 or 165)+3,-40-(i-1)*132+14):Color():Shadow() end end ELib:Text(self,L.RaidGroupsTextMovable,8):Point("TOPLEFT",12,-547):Color():Shadow() self.scrollNotInList = ELib:ScrollBar(self):Point("TOPLEFT",478,-40):Size(12,558):SetMinMaxValues(0,1):SetValue(0):SetObey(true):OnChange(function() self:UpdateNotInList() end) self.edits_notinlist = {} function self:UpdateNotInList() local inList,notInList = {},{} local scrollShow for i=1,40 do local name = module.options.edits[i]:GetText() if name then inList[name] = true end end if self.isGuild then local myLvl = UnitLevel("player") local guildList = {} for i=1, GetNumGuildMembers() do local name, _, rankIndex, level, _, _, _, _, _, _, class = GetGuildRosterInfo(i) if select(2,strsplit("-",name)) == ExRT.SDB.realmKey then name = strsplit("-",name) end if not inList[name] and (level or 0) >= myLvl then guildList[#guildList+1] = {name,class,rankIndex,"|cffbbbbbb["..rankIndex.."]|r "..name} end end sort(guildList,function(a,b) if a[3] == b[3] then return a[1] < b[1] else return a[3] < b[3] end end) local start = floor(self.scrollNotInList:GetValue()+0.5) for i=start+1,#guildList do notInList[#notInList+1] = guildList[i] end if #guildList > 40 then scrollShow = #guildList - 40 end else for i=1,GetNumGroupMembers() do local name = GetRaidRosterInfo(i) if not inList[name] then if not name:find("%-") or not inList[strsplit("-",name)] then notInList[#notInList+1] = name end end end end if scrollShow then self.scrollNotInList:SetMinMaxValues(0,scrollShow) end self.scrollNotInList:SetShown(scrollShow and true or false) for i=1,min(#notInList,40) do local edit = self.edits_notinlist[i] if not edit then edit = ELib:Edit(self):Size(120,12) self.edits_notinlist[i] = edit edit.index = i edit:Point("TOPLEFT",355,-40-(i-1)*14):OnChange(EditOnChange) edit.roleIcon = edit:CreateTexture(nil, "BACKGROUND") edit.roleIcon:SetPoint("RIGHT") edit.roleIcon:SetSize(10,10) edit.roleIcon:Hide() edit:SetFont(edit:GetFont(),10,"") edit:SetEnabled(false) edit:SetMovable(true) edit:RegisterForDrag("LeftButton") edit:SetScript("OnDragStart", EditOnDragStart) edit:SetScript("OnDragStop", EditOnDragStopNotInList) end if type(notInList[i]) == "table" then edit:SetText("") edit:SetText(notInList[i][4]) edit.preclass = notInList[i][2] edit.preName = notInList[i][1] else edit:SetText("") edit:SetText(notInList[i]) edit.preclass = nil edit.preName = nil end edit:SetCursorPosition(1) edit:Show() end for i=#notInList+1,#self.edits_notinlist do self.edits_notinlist[i]:Hide() end end self.chk_raid = ELib:Radio(self,RAID,true):Point("TOPLEFT",355,-20):OnClick(function(self) module.options.isGuild = false self:SetChecked(true) module.options.chk_guild:SetChecked(false) module.options:UpdateNotInList() end) self.chk_guild = ELib:Radio(self,GUILD):Point("BOTTOM",self.chk_raid,"TOP",0,-2):OnClick(function(self) module.options.isGuild = true self:SetChecked(true) module.options.chk_raid:SetChecked(false) C_GuildInfo.GuildRoster() module.options:UpdateNotInList() C_Timer.After(1,function() module.options:UpdateNotInList() end) end) self.updateRoster = ELib:Button(self,L.RaidGroupsCurrentRoster):Size(ExRT.isClassic and not ExRT.isCata and 315 or 155,20):Point("TOPLEFT",10,-565):OnClick(function() module.options:ResetCurrentSave() local roster = {} for i=1,8 do roster[i] = {} end for i=1,GetNumGroupMembers() do local name, rank, subgroup = GetRaidRosterInfo(i) tinsert(roster[subgroup],name) end for i=1,8 do for j=1,5 do local edit = module.options.edits[(i-1)*5+j] edit:SetText(roster[i][j] or "") edit:SetCursorPosition(1) end end module.options:UpdateNotInList() end) self.splitRoster = ELib:Button(self,L.RaidGroupsSplitRoster):Size(135,20):Point("LEFT",self.updateRoster,"RIGHT",5,0):Shown(not ExRT.isClassic or ExRT.isCata):OnClick(function() module.options:SplitRoster() end) self.splitRosterOptions = ELib:DropDownButton(self,"|TInterface\\AddOns\\"..GlobalAddonName.."\\media\\DiesalGUIcons16x256x128:14:14:0:0:256:128:16:32:64:80|t",200,-1):Size(20,20):Point("LEFT",self.splitRoster,"RIGHT",0,0):Shown(not ExRT.isClassic or ExRT.isCata) do local gfs = {} local function gfs_check(_,g) VMRT.RaidGroups.SplitGroups[g] = not VMRT.RaidGroups.SplitGroups[g] ELib.ScrollDropDown.UpdateChecks() end for i=1,8 do gfs[#gfs+1] = { text = (GROUP or "Group").." "..i, checkable = true, func = gfs_check, arg1 = i, } end local nfs = {} local function nfs_check(_,g) VMRT.RaidGroups.SplitParts = g ELib.ScrollDropDown.UpdateChecks() end for i=1,8 do nfs[#nfs+1] = { text = i, radio = true, func = nfs_check, arg1 = i, } end local sr = {} local function sr_check(_,g) VMRT.RaidGroups.SplitRule = g ELib.ScrollDropDown.UpdateChecks() end sr[#sr+1] = { text = "1,2,3,4 vs 5,6,7,8", radio = true, func = sr_check, arg1 = 1, } sr[#sr+1] = { text = "1,3,5,7 vs 2,4,6,8", radio = true, func = sr_check, arg1 = 2, } self.splitRosterOptions.List = { { text = L.RaidGroupsSplitGroups, subMenu = gfs, }, { text = L.RaidGroupsSplitParts, subMenu = nfs, }, { text = L.RaidGroupsSplitRule, subMenu = sr, }, { text = CLOSE, func = function() ELib.ScrollDropDown.Close() end, notCheckable = true }, } function self.splitRosterOptions:PreUpdate() for i=1,#gfs do local line = gfs[i] line.checkState = VMRT.RaidGroups.SplitGroups[i] end for i=1,#nfs do local line = nfs[i] line.checkState = i == VMRT.RaidGroups.SplitParts if line.checkState then line.colorCode = "|cff00ff00" else line.colorCode = nil end end for i=1,#sr do local line = sr[i] line.checkState = (i == VMRT.RaidGroups.SplitRule) or (i==1 and not VMRT.RaidGroups.SplitRule) if line.checkState then line.colorCode = "|cff00ff00" else line.colorCode = nil end end end function self.splitRosterOptions:additionalToggle() self:PreUpdate() end end local splitClassPrio = { [1]=15,--WARRIOR [2]=14,--PALADIN [4]=13,--ROGUE [6]=12,--DEATHKNIGHT [12]=11,--DEMONHUNTER [7]=10,--SHAMAN [10]=9,--MONK [11]=8,--DRUID [3]=7,--HUNTER [5]=6,--PRIEST [8]=5,--MAGE [9]=4,--WARLOCK [13]=3,--EVOKER [14]=1, [15]=2, } function module.options:SplitRoster() local GROUPS_OPT = VMRT.RaidGroups.SplitGroups local SPLIT_MAX = VMRT.RaidGroups.SplitParts local roster = {} local groups_opted_max = 0 for i=1,8 do if GROUPS_OPT[i] then groups_opted_max = groups_opted_max + 1 for j=1,5 do local edit = module.options.edits[(i-1)*5+j] local name = edit:GetText() if name and name:trim() ~= "" then local prio = 0 if UnitName(name) then prio = prio + 100 end local role = UnitGroupRolesAssigned(name) if role == "TANK" then prio = prio + 60 elseif role == "DAMAGER" then prio = prio + 40 elseif role == "HEALER" then prio = prio + 20 end local classNum = select(3,UnitClass(name)) if classNum then prio = prio + splitClassPrio[classNum] or classNum end roster[#roster+1] = {name = name, prio = prio} end end end end if (groups_opted_max / SPLIT_MAX) % 1 ~= 0 then print('Groups are imbalanced, sorting is impossible.') print('Change number of parts or number of affected groups.') return end sort(roster,function(a,b) if a.prio == b.prio then return a.name < b.name else return a.prio > b.prio end end) local splits = {} local groups_opted_num = 0 for i=1,8 do if GROUPS_OPT[i] then groups_opted_num = groups_opted_num + 1 local split_num if VMRT.RaidGroups.SplitRule == 2 then split_num = ((groups_opted_num - 1) % SPLIT_MAX) + 1 else split_num = floor((groups_opted_num - 1) / ceil(groups_opted_max / SPLIT_MAX)) + 1 end if not splits[split_num] then splits[split_num] = { groups = {},curr = 1 } end tinsert(splits[split_num].groups, i) end end --DevTools_Dump(splits) local pos = 1 for i=1,groups_opted_num*5 do local s = splits[pos] local group = s.groups[ floor((s.curr - 1) / 5) + 1 ] local gpos = ((s.curr - 1) % 5) + 1 local edit = module.options.edits[(group-1)*5+gpos] edit:SetText(roster[i] and roster[i].name or "") edit:SetCursorPosition(1) s.curr = s.curr + 1 pos = pos + 1 if pos > SPLIT_MAX then pos = 1 end end for _,s in pairs(splits) do for i=s.curr,#s.groups*5 do local group = s.groups[ floor((i - 1) / 5) + 1 ] local gpos = ((i - 1) % 5) + 1 local edit = module.options.edits[(group-1)*5+gpos] edit:SetText("") edit:SetCursorPosition(1) end end module.options:UpdateNotInList() end self.processRoster = ELib:Button(self,L.RaidGroupsSetGroups):Size(315,30):Point("TOPLEFT",self.updateRoster,"BOTTOMLEFT",0,-5):OnClick(function(self) local list = {} for i=1,40 do list[i] = module.options.edits[i]:GetText() end module:ApplyGroups(list) end) self.keepPosCheck = ELib:Check(self,L.RaidGroupsKeepPosInGroup,VMRT.RaidGroups.KeepPosInGroup):Point("BOTTOMLEFT",self.processRoster,"BOTTOMRIGHT",5,0):OnClick(function(self) if self:GetChecked() then VMRT.RaidGroups.KeepPosInGroup = true else VMRT.RaidGroups.KeepPosInGroup = nil end end):Shown(ExRT.isClassic) self.presetList = ELib:ScrollList(self):Size(190,505-25):Point("TOPRIGHT",-10,-40):AddDrag() ELib:Text(self,L.RaidGroupsQuickLoad..":"):Point("BOTTOMLEFT",self.presetList,"TOPLEFT",5,3):Color():Shadow() self.presetList.ButtonRemove = CreateFrame("Button",nil,self.presetList) self.presetList.ButtonRemove:Hide() self.presetList.ButtonRemove:SetSize(8,8) self.presetList.ButtonRemove:SetScript("OnClick",function(self) for i=#VMRT.RaidGroups.profiles,1,-1 do local entry = VMRT.RaidGroups.profiles[i] if entry == self.data then tremove(VMRT.RaidGroups.profiles,i) break end end module.options:UpdateList() module.options:ResetCurrentSave() end) self.presetList.ButtonRemove:SetScript("OnEnter",function(self) GameTooltip:SetOwner(self,"ANCHOR_RIGHT") GameTooltip:AddLine(DELETE) GameTooltip:Show() end) self.presetList.ButtonRemove:SetScript("OnLeave",function(self) GameTooltip_Hide() end) self.presetList.ButtonRemove.i = self.presetList.ButtonRemove:CreateTexture() self.presetList.ButtonRemove.i:SetPoint("CENTER") self.presetList.ButtonRemove.i:SetSize(16,16) self.presetList.ButtonRemove.i:SetTexture("Interface\\AddOns\\"..GlobalAddonName.."\\media\\DiesalGUIcons16x256x128") self.presetList.ButtonRemove.i:SetTexCoord(0.5,0.5625,0.5,0.625) self.presetList.ButtonRemove.i:SetVertexColor(.8,0,0,1) function self:ResetCurrentSave() module.options.CURR_SAVE = nil module.options.presetList.selected = nil module.options.presetList:Update() end function self:SaveCurrentSave() if not module.options.CURR_SAVE then return end local new = module.options:CreateCurrentData() for i=1,40 do module.options.CURR_SAVE[i] = new[i] end module.options.CURR_SAVE.time = new.time end function self:SetCurrentSave(data) if not data then self:ResetCurrentSave() return end module.options.CURR_SAVE = data module.options.presetList.selected = nil for i=1,#module.options.presetList.L do if module.options.presetList.L[i][2] == data then module.options.presetList.selected = i break end end if not module.options.presetList.selected then module.options.CURR_SAVE = nil end module.options:UpdateList() end function self.presetList:SetListValue(index) local data = self.L[index][2] for i=1,40 do local edit = module.options.edits[i] if data[i] then edit:SetText(data[i]) edit:SetCursorPosition(1) else edit:SetText("") end end module.options:UpdateNotInList() module.options.LAST_CLICK = index if not VMRT.RaidGroups.SaveCurrent then module.options.presetList.selected = nil else module.options.CURR_SAVE = data end module.options.presetList:Update() end function self.presetList:HoverListValue(isEnter,index,obj) if not isEnter then GameTooltip_Hide() if not self.ButtonRemove:IsMouseOver() then self.ButtonRemove:Hide() end else local data = obj.table[2] GameTooltip:SetOwner(obj,"ANCHOR_CURSOR") GameTooltip:AddLine(data.name) GameTooltip:AddLine(date("%x %X",data.time)) GameTooltip:Show() self.ButtonRemove.data = data self.ButtonRemove:SetPoint("RIGHT",obj,"RIGHT",-5,0) self.ButtonRemove:SetParent(obj) self.ButtonRemove:Show() end end function self.presetList:OnDragFunction(obj1,obj2) local data1,data2 = obj1.table[2],obj2.table[2] local tmp if obj1.index < obj2.index then for i=#VMRT.RaidGroups.profiles,1,-1 do if VMRT.RaidGroups.profiles[i] == data1 then tmp = VMRT.RaidGroups.profiles[i] VMRT.RaidGroups.profiles[i] = VMRT.RaidGroups.profiles[i-1] elseif VMRT.RaidGroups.profiles[i] == data2 then VMRT.RaidGroups.profiles[i] = tmp break elseif tmp then VMRT.RaidGroups.profiles[i] = VMRT.RaidGroups.profiles[i-1] end end else for i=1,#VMRT.RaidGroups.profiles do if VMRT.RaidGroups.profiles[i] == data1 then tmp = VMRT.RaidGroups.profiles[i] VMRT.RaidGroups.profiles[i] = VMRT.RaidGroups.profiles[i+1] elseif VMRT.RaidGroups.profiles[i] == data2 then VMRT.RaidGroups.profiles[i] = tmp break elseif tmp then VMRT.RaidGroups.profiles[i] = VMRT.RaidGroups.profiles[i+1] end end end module.options:UpdateList() end function self:UpdateList() local list = {} for i=#VMRT.RaidGroups.profiles,1,-1 do local entry = VMRT.RaidGroups.profiles[i] list[#list+1] = {entry.name,entry} end module.options.presetList.L = list module.options.presetList:Update() end function self:CreateCurrentData() local new = { time = time(), } for i=1,40 do local text = module.options.edits[i]:GetText() if text and string.trim(text) ~= "" then new[i] = text end end return new end local function SaveData(_,name) if not name or string.trim(name) == "" then return end local new = module.options:CreateCurrentData() new.name = name VMRT.RaidGroups.profiles[#VMRT.RaidGroups.profiles+1] = new module.options:UpdateList() module.options:SetCurrentSave(new) end local function SaveInputOnEdit(self) local text = self:GetText() if text and string.trim(text) ~= "" then self:GetParent().OK:Enable() else self:GetParent().OK:Disable() end end self.chkSaveCurrent = ELib:Check(self,L.RaidGroupsSaveCurrent,VMRT.RaidGroups.SaveCurrent):Point("TOPLEFT",self.presetList,"BOTTOMLEFT",0,-5):Tooltip(L.RaidGroupsSaveCurrentTip):OnClick(function(self) if self:GetChecked() then VMRT.RaidGroups.SaveCurrent = true else VMRT.RaidGroups.SaveCurrent = nil module.options:ResetCurrentSave() end end) self.presetListSave = ELib:Button(self,L.RaidGroupsSave):Size(192,20):Point("TOPLEFT",self.chkSaveCurrent,"BOTTOMLEFT",0,-5):OnClick(function(self) ExRT.F.ShowInput(L.RaidGroupsEnterName,SaveData,nil,nil,nil,SaveInputOnEdit) end) self.importWindow, self.exportWindow = ExRT.F.CreateImportExportWindows() do self.importWindow:SetHeight(180) self.importWindow.Edit:Point("TOP",0,-100) ELib:DecorationLine(self.importWindow):Point("TOPLEFT",0,-17):Point("BOTTOMRIGHT",'x',"TOPRIGHT",0,-18) ELib:DecorationLine(self.importWindow):Point("TOPLEFT",0,-95):Point("BOTTOMRIGHT",'x',"TOPRIGHT",0,-96) local importWindow = self.importWindow local function ButtonOnClick(self) local index = 1 local c = importWindow["c"..index] while c do c:Enable() if self == c then importWindow.importType = index VMRT.RaidGroups.importType = index end index = index + 1 c = importWindow["c"..index] end self:Disable() end local function CreateButton() local self = ELib:Template("ExRTButtonTransparentTemplate",importWindow) self:SetSize(importWindow:GetWidth()/4,77) self.DisabledTexture = self:CreateTexture() self.DisabledTexture:SetColorTexture(0.20,0.21,0.25,0.5) self.DisabledTexture:SetPoint("TOPLEFT") self.DisabledTexture:SetPoint("BOTTOMRIGHT") self:SetDisabledTexture(self.DisabledTexture) self:SetScript("OnClick",ButtonOnClick) return self end importWindow.c1 = CreateButton() importWindow.c1:SetPoint("TOPLEFT",0,-18) importWindow.c1:SetText("G1 G2\nG3 G4\nG5 G6\nG7 G8") importWindow.c2 = CreateButton() importWindow.c2:SetPoint("LEFT",importWindow.c1,"RIGHT",0,0) importWindow.c2:SetText("G1 G2 G3 G4 G5") importWindow.c3 = CreateButton() importWindow.c3:SetPoint("LEFT",importWindow.c2,"RIGHT",0,0) importWindow.c3:SetText("G1\nG2\nG3\nG4\nG5") importWindow.c4 = CreateButton() importWindow.c4:SetPoint("LEFT",importWindow.c3,"RIGHT",0,0) importWindow.c4:SetText("From ExRT export string") local importType = VMRT.RaidGroups.importType or 4 local c = importWindow["c"..importType] if c then c:Click() end end do self.exportWindow:SetHeight(130) self.exportWindow.Edit:Point("TOP",0,-100) ELib:DecorationLine(self.exportWindow):Point("TOPLEFT",0,-17):Point("BOTTOMRIGHT",'x',"TOPRIGHT",0,-18) ELib:DecorationLine(self.exportWindow):Point("TOPLEFT",0,-95):Point("BOTTOMRIGHT",'x',"TOPRIGHT",0,-96) local exportWindow = self.exportWindow local function ButtonOnClick(self) local index = 1 local c = exportWindow["c"..index] while c do c:Enable() if self == c then exportWindow.exportType = index VMRT.RaidGroups.exportType = index end index = index + 1 c = exportWindow["c"..index] end self:Disable() module.options:RecordToText() if exportWindow:IsShown() then exportWindow.Edit:SetFocus() exportWindow.Edit:HighlightText() end end local function CreateButton() local self = ELib:Template("ExRTButtonTransparentTemplate",exportWindow) self:SetSize(exportWindow:GetWidth()/4,77) self.DisabledTexture = self:CreateTexture() self.DisabledTexture:SetColorTexture(0.20,0.21,0.25,0.5) self.DisabledTexture:SetPoint("TOPLEFT") self.DisabledTexture:SetPoint("BOTTOMRIGHT") self:SetDisabledTexture(self.DisabledTexture) self:SetScript("OnClick",ButtonOnClick) return self end exportWindow.c1 = CreateButton() exportWindow.c1:SetPoint("TOPLEFT",0,-18) exportWindow.c1:SetText("G1 G2\nG3 G4\nG5 G6\nG7 G8") exportWindow.c2 = CreateButton() exportWindow.c2:SetPoint("LEFT",exportWindow.c1,"RIGHT",0,0) exportWindow.c2:SetText("G1 G2 G3 G4 G5") exportWindow.c3 = CreateButton() exportWindow.c3:SetPoint("LEFT",exportWindow.c2,"RIGHT",0,0) exportWindow.c3:SetText("G1\nG2\nG3\nG4\nG5") exportWindow.c4 = CreateButton() exportWindow.c4:SetPoint("LEFT",exportWindow.c3,"RIGHT",0,0) exportWindow.c4:SetText("MRT export string") local exportType = VMRT.RaidGroups.exportType or 4 local c = exportWindow["c"..exportType] if c then c:Disable() end exportWindow.exportType = exportType end local function SaveDataFromTable(rec,name) if not name or string.trim(name) == "" then return end rec.name = name rec.time = time() VMRT.RaidGroups.profiles[#VMRT.RaidGroups.profiles+1] = rec module.options:UpdateList() module.options:ResetCurrentSave() end function self.importWindow:ImportFunc(str) if self.importType == 4 then local headerLen = str:sub(1,4) == "EXRT" and 8 or 7 local header = str:sub(1,headerLen) if (header:sub(1,headerLen-1) ~= "EXRTRGR" and header:sub(1,headerLen-1) ~= "MRTRGR") or (header:sub(headerLen,headerLen) ~= "0" and header:sub(headerLen,headerLen) ~= "1") then StaticPopupDialogs["EXRT_RAIDGROUP_IMPORT"] = { text = "|cffff0000"..ERROR_CAPS.."|r "..L.ProfilesFail3, button1 = OKAY, timeout = 0, whileDead = true, hideOnEscape = true, preferredIndex = 3, } StaticPopup_Show("EXRT_RAIDGROUP_IMPORT") return end module.options:TextToRecord(str:sub(headerLen+1),header:sub(headerLen,headerLen)=="0") elseif self.importType == 1 then local res = {} local group = 1 local posInGroup = 0 local lines = {strsplit("\n",str)} for i=1,#lines do local left,right = strsplit(" ",lines[i]:gsub(" +"," "):gsub('"',''),nil) if left and left:trim() ~= "" then left = left:trim() else left = nil end if right and right:trim() ~= "" then right = right:trim() else right = nil end posInGroup = posInGroup + 1 if posInGroup > 5 and (left or right) then posInGroup = 1 group = group + 2 end if posInGroup <= 5 and group <= 8 then res[(group-1)*5+posInGroup] = left res[(group-0)*5+posInGroup] = right end end ExRT.F.ShowInput(L.RaidGroupsEnterName,SaveDataFromTable,res,nil,nil,SaveInputOnEdit) elseif self.importType == 3 then local res = {} local group = 1 local posInGroup = 0 local lines = {strsplit("\n",str)} for i=1,#lines do local left = lines[i] if left and left:trim() ~= "" then left = left:trim():gsub('"','') else left = nil end posInGroup = posInGroup + 1 if posInGroup > 5 and left then posInGroup = 1 group = group + 1 end if posInGroup <= 5 and group <= 8 then res[(group-1)*5+posInGroup] = left end end ExRT.F.ShowInput(L.RaidGroupsEnterName,SaveDataFromTable,res,nil,nil,SaveInputOnEdit) elseif self.importType == 2 then local res = {} local lines = {strsplit("\n",str)} for i=1,#lines do local group = 0 local name,line = strsplit(" ",lines[i]:gsub(" +"," "):gsub('"',''),2) while name do group = group + 1 if i <= 5 and group <= 8 then res[(group-1)*5+i] = name:trim() end if line then name,line = strsplit(" ",line,2) else break end end end ExRT.F.ShowInput(L.RaidGroupsEnterName,SaveDataFromTable,res,nil,nil,SaveInputOnEdit) end end function self:TextToRecord(str,uncompressed) local decoded = LibDeflate:DecodeForPrint(str) local decompressed if uncompressed then decompressed = decoded else decompressed = LibDeflate:DecompressDeflate(decoded) end decoded = nil local _,tableData = strsplit(",",decompressed,2) decompressed = nil local successful, res = pcall(ExRT.F.TextToTable,tableData) if ExRT.isDev then module.db.lastImportDB = res if module.db.exportTable and type(res)=="table" then module.db.diffTable = {} print("Compare table",ExRT.F.table_compare(res,module.db.exportTable,module.db.diffTable)) end end if successful and res then ExRT.F.ShowInput(L.RaidGroupsEnterName,SaveDataFromTable,res,nil,nil,SaveInputOnEdit) else StaticPopupDialogs["EXRT_RAIDGROUP_IMPORT"] = { text = L.ProfilesFail1..(res and "\nError code: "..res or ""), button1 = OKAY, timeout = 0, whileDead = true, hideOnEscape = true, preferredIndex = 3, } StaticPopup_Show("EXRT_RAIDGROUP_IMPORT") end end function self:RecordToText() local new = {} for i=1,40 do local text = module.options.edits[i]:GetText() if text and string.trim(text) ~= "" then new[i] = text end end local exportWindow = module.options.exportWindow if exportWindow.exportType == 1 then local text = "" for i=1,8,2 do for j=1,5 do text = text .. (new[(i-1)*5+j] or "") .. "\t" .. (new[(i-0)*5+j] or "") .. "\n" end end exportWindow.Edit:SetText(text) elseif exportWindow.exportType == 2 then local text = "" for j=1,5 do for i=1,8 do text = text .. (new[(i-1)*5+j] or "") .. "\t" end text = text .. "\n" end exportWindow.Edit:SetText(text) elseif exportWindow.exportType == 3 then local text = "" for i=1,8 do for j=1,5 do text = text .. (new[(i-1)*5+j] or "") .. "\n" end end exportWindow.Edit:SetText(text) else local strlist = ExRT.F.TableToText(new) strlist[1] = "0,"..strlist[1] local str = table.concat(strlist) local compressed if #str < 1000000 then compressed = LibDeflate:CompressDeflate(str,{level = 5}) end local encoded = "MRTRGR"..(compressed and "1" or "0")..LibDeflate:EncodeForPrint(compressed or str) ExRT.F.dprint("Str len:",#str,"Encoded len:",#encoded) if ExRT.isDev then module.db.exportTable = new end exportWindow.Edit:SetText(encoded) end exportWindow:Show() end self.butExport = ELib:Button(self,L.RaidGroupsExport):Size(192,20):Point("TOP",self.presetListSave,"BOTTOM",0,-5):OnClick(function(self) module.options:RecordToText() end) self.butImport = ELib:Button(self,L.Import):Size(192,20):Point("TOP",self.butExport,"BOTTOM",0,-5):OnClick(function(self) module.options.importWindow:NewPoint("CENTER",UIParent,0,0) module.options.importWindow:Show() end) self:UpdateList() self.updateRoster:Click() function self:OnShow() module.options:UpdateTextColors() module.options:UpdateNotInList() end local GRU = CreateFrame("Frame",nil,self) GRU:SetPoint("TOPLEFT") GRU:SetSize(1,1) GRU:RegisterEvent("GROUP_ROSTER_UPDATE") GRU:SetScript("OnEvent",function() if not self:IsVisible() then return end module.options:UpdateTextColors() module.options:UpdateNotInList() end) end function module:ApplyGroups(list) if not IsInRaid() then return end local UnitsInCombat for i=1,40 do local unit = "raid"..i if UnitAffectingCombat(unit) then UnitsInCombat = (UnitsInCombat or "") .. (UnitsInCombat and "," or "") .. UnitName(unit) end end if UnitsInCombat then print("|cffff0000"..ERROR_CAPS..".|r "..L.RaidGroupsPlayersInCombat..": "..UnitsInCombat) return end local needGroup = {} local needPosInGroup = {} local lockedUnit = {} local RLName,_,RLGroup = GetRaidRosterInfo(1) local isRLfound = false for i=1,8 do local pos = 1 for j=1,5 do local name = list[(i-1)*5+j] if name == RLName then needGroup[name] = i needPosInGroup[name] = pos pos = pos + 1 isRLfound = true break end end for j=1,5 do local name = list[(i-1)*5+j] if name and name ~= RLName and UnitName(name) then needGroup[name] = i needPosInGroup[name] = pos pos = pos + 1 end end end --reports saying that too many swap events can trigger disconnect on classic, unconfirmed cause: other addons or pure swap events if ExRT.isClassic and not VMRT.RaidGroups.KeepPosInGroup then wipe(needPosInGroup) end module.db.needGroup = needGroup module.db.needPosInGroup = needPosInGroup module.db.lockedUnit = lockedUnit module.db.groupsReady = false module.db.groupWithRL = isRLfound and 0 or RLGroup if module.options.processRoster then module.options.processRoster:Disable() end module:ProcessRoster() end local function Debug(currentGroup,currentPos,needGroup,needPosInGroup) local s = "" for k,v in pairs(currentGroup) do s = s..k.."["..v..":"..currentPos[k]..","..(needGroup[k] or "")..":"..(needPosInGroup[k] or "").."]" end print(s) end function module:ProcessRoster() local UnitsInCombat for i=1,40 do local unit = "raid"..i if UnitAffectingCombat(unit) then UnitsInCombat = (UnitsInCombat or "") .. (UnitsInCombat and "," or "") .. UnitName(unit) end end if UnitsInCombat then print("|cffff0000"..ERROR_CAPS..".|r "..L.RaidGroupsCombatStarted..": "..UnitsInCombat) module.db.needGroup = nil if module.options.processRoster then module.options.processRoster:Enable() end module:UnregisterEvents('GROUP_ROSTER_UPDATE') return end local needGroup = module.db.needGroup local needPosInGroup = module.db.needPosInGroup local lockedUnit = module.db.lockedUnit if not needGroup then return end module:RegisterEvents('GROUP_ROSTER_UPDATE') local currentGroup = {} local currentPos = {} local nameToID = {} local groupSize = {} wipe(currentGroup) for i=1,8 do groupSize[i] = 0 end for i=1,GetNumGroupMembers() do local name, rank, subgroup = GetRaidRosterInfo(i) if not needGroup[name] and name:find("%-") and needGroup[strsplit("-",name)] then name = strsplit("-",name) end currentGroup[name] = subgroup nameToID[name] = i groupSize[subgroup] = groupSize[subgroup] + 1 currentPos[name] = groupSize[subgroup] end --Debug(currentGroup,currentPos,needGroup,needPosInGroup) if not module.db.groupsReady then local WaitForGroup = false for unit,group in pairs(needGroup) do if currentGroup[unit] and currentGroup[unit] ~= needGroup[unit] then local currGroupUnit = currentGroup[unit] local needGroupUnit = needGroup[unit] if groupSize[ needGroupUnit ] < 5 then SetRaidSubgroup(nameToID[unit],needGroupUnit) groupSize[ currGroupUnit ] = groupSize[ currGroupUnit ] - 1 groupSize[ needGroupUnit ] = groupSize[ needGroupUnit ] + 1 WaitForGroup = true end end end if WaitForGroup then return end local SetToSwap = {} local WaitForSwap = false for unit,group in pairs(needGroup) do if not SetToSwap[unit] and currentGroup[unit] and currentGroup[unit] ~= group then local currGroupUnit = currentGroup[unit] local unitToSwap for unit2,group2 in pairs(currentGroup) do if not SetToSwap[unit2] and group2 == group and needGroup[unit2] ~= group2 then unitToSwap = unit2 break end end if unitToSwap then SwapRaidSubgroup(nameToID[unit], nameToID[unitToSwap]) WaitForSwap = true SetToSwap[unit] = true SetToSwap[unitToSwap] = true end end end if WaitForSwap then return end module.db.groupsReady = true end do local SetToSwap = {} local WaitForSwap = false for unit,pos in pairs(needPosInGroup) do if currentGroup[unit] == module.db.groupWithRL then pos = pos + 1 end if not lockedUnit[unit] and currentPos[unit] and currentPos[unit] ~= pos and nameToID[unit] ~= 1 and not SetToSwap[unit] then local currGroupUnit = currentGroup[unit] local unitToSwapBridge for unit2,group2 in pairs(currentGroup) do if group2 ~= currentGroup[unit] and nameToID[unit2] ~= 1 and not SetToSwap[unit2] then unitToSwapBridge = unit2 break end end local unitToSwap for unit2,pos2 in pairs(currentPos) do if currentGroup[unit2] == currentGroup[unit] and pos2 == pos and nameToID[unit2] ~= 1 and not SetToSwap[unit2] then unitToSwap = unit2 break end end if unitToSwap and unitToSwapBridge then lockedUnit[unit] = true SwapRaidSubgroup(nameToID[unit], nameToID[unitToSwapBridge]) SwapRaidSubgroup(nameToID[unitToSwapBridge], nameToID[unitToSwap]) SwapRaidSubgroup(nameToID[unit], nameToID[unitToSwapBridge]) WaitForSwap = true SetToSwap[unit] = true SetToSwap[unitToSwap] = true SetToSwap[unitToSwapBridge] = true end end end if WaitForSwap then return end end module.db.needGroup = nil if module.options.processRoster then module.options.processRoster:Enable() end module:UnregisterEvents('GROUP_ROSTER_UPDATE') end function module.main:GROUP_ROSTER_UPDATE() if self.timer then self.timer:Cancel() end self.timer = C_Timer.NewTimer(0.5,function() self.timer = nil module:ProcessRoster() end) end function module:slash(arg) if arg and arg:find("^raidgroup ") then local name = arg:match("^raidgroup (.-)$") if name then for i=#VMRT.RaidGroups.profiles,1,-1 do local entry = VMRT.RaidGroups.profiles[i] if entry.name == name then module:ApplyGroups(entry) return end end end elseif arg == "help" then print("|cff00ff00/rt raidgroup |cffffff00GROUPNAME|r|r - apply raid groups with name GROUPNAME") end end