comparison TeamOptimizer.lua @ 57:01b63b8ed811 v21

total rewrite to version 21
author yellowfive
date Fri, 05 Jun 2015 11:05:15 -0700
parents
children adec0972d4e1
comparison
equal deleted inserted replaced
56:75431c084aa0 57:01b63b8ed811
1 local Amr = LibStub("AceAddon-3.0"):GetAddon("AskMrRobot")
2 local L = LibStub("AceLocale-3.0"):GetLocale("AskMrRobot", true)
3 local AceGUI = LibStub("AceGUI-3.0")
4
5 local _panelSplash
6 local _panelStartLoot
7 local _lblStartLoot
8 local _btnStartLoot
9 local _scrollHistory
10 local _tabs
11
12 local _messagePrefixes = {
13 RosterRequestGear = "_TRR",
14 RosterGear = "_TRG",
15 ItemExportRequestGear = "_TLR",
16 ItemExportGear = "_TLG",
17 ItemExportLoot = "_TLL",
18 SyncRequest = "_TSR",
19 Sync = "_TSS"
20 }
21
22 Amr.LootMessagePrefixes = {
23 Start = "_TCS",
24 Roll = "_TCR",
25 Veto = "_TCV",
26 Rand = "_TCD",
27 Give = "_TCG",
28 Finish = "_TCF"
29 }
30
31 local function renderExportWindow(container, instructions, text)
32
33 local bg = Amr:RenderCoverChrome(container, 800, 450)
34
35 local lbl = AceGUI:Create("AmrUiLabel")
36 lbl:SetWidth(750)
37 lbl:SetText(L.TeamExportHelp)
38 lbl:SetPoint("TOP", bg.content, "TOP", 0, -10)
39 bg:AddChild(lbl)
40
41 local lbl2 = AceGUI:Create("AmrUiLabel")
42 lbl2:SetWidth(750)
43 lbl2:SetText(instructions)
44 lbl2:SetPoint("TOP", lbl.frame, "BOTTOM", 0, -10)
45 bg:AddChild(lbl2)
46
47 local txt = AceGUI:Create("AmrUiTextarea")
48 txt:SetWidth(750)
49 txt:SetHeight(300)
50 txt:SetPoint("TOP", lbl2.frame, "BOTTOM", 0, -10)
51 txt:SetFont(Amr.CreateFont("Regular", 12, Amr.Colors.Text))
52 txt:SetText(text)
53 bg:AddChild(txt)
54
55 local btn = AceGUI:Create("AmrUiButton")
56 btn:SetText(L.TeamButtonExportClose)
57 btn:SetBackgroundColor(Amr.Colors.Green)
58 btn:SetFont(Amr.CreateFont("Bold", 16, Amr.Colors.White))
59 btn:SetWidth(120)
60 btn:SetHeight(28)
61 btn:SetPoint("TOPLEFT", txt.frame, "BOTTOMLEFT", 0, -10)
62 btn:SetCallback("OnClick", function(widget) Amr:HideCover() end)
63 bg:AddChild(btn)
64
65 return txt
66 end
67
68 local function renderImportWindow(container)
69
70 local bg = Amr:RenderCoverChrome(container, 700, 450)
71
72 local lbl = AceGUI:Create("AmrUiLabel")
73 lbl:SetWidth(600)
74 lbl:SetText(L.TeamImportRankingsHeader)
75 lbl:SetPoint("TOP", bg.content, "TOP", 0, -10)
76 bg:AddChild(lbl)
77
78 local txt = AceGUI:Create("AmrUiTextarea")
79 txt:SetWidth(600)
80 txt:SetHeight(300)
81 txt:SetPoint("TOP", lbl.frame, "BOTTOM", 0, -10)
82 txt:SetFont(Amr.CreateFont("Regular", 12, Amr.Colors.Text))
83 bg:AddChild(txt)
84
85 local btnImportOk = AceGUI:Create("AmrUiButton")
86 btnImportOk:SetText(L.ImportButtonOk)
87 btnImportOk:SetBackgroundColor(Amr.Colors.Green)
88 btnImportOk:SetFont(Amr.CreateFont("Bold", 16, Amr.Colors.White))
89 btnImportOk:SetWidth(120)
90 btnImportOk:SetHeight(28)
91 btnImportOk:SetPoint("TOPLEFT", txt.frame, "BOTTOMLEFT", 0, -10)
92 bg:AddChild(btnImportOk)
93
94 local btnImportCancel = AceGUI:Create("AmrUiButton")
95 btnImportCancel:SetText(L.ImportButtonCancel)
96 btnImportCancel:SetBackgroundColor(Amr.Colors.Green)
97 btnImportCancel:SetFont(Amr.CreateFont("Bold", 16, Amr.Colors.White))
98 btnImportCancel:SetWidth(120)
99 btnImportCancel:SetHeight(28)
100 btnImportCancel:SetPoint("LEFT", btnImportOk.frame, "RIGHT", 20, 0)
101 btnImportCancel:SetCallback("OnClick", function(widget) Amr:HideCover() end)
102 bg:AddChild(btnImportCancel)
103
104 local lblErr = AceGUI:Create("AmrUiLabel")
105 lblErr:SetWidth(600)
106 lblErr:SetFont(Amr.CreateFont("Bold", 14, Amr.Colors.Red))
107 lblErr:SetText("")
108 lblErr:SetPoint("TOPLEFT", btnImportOk.frame, "BOTTOMLEFT", 0, -20)
109 bg:AddChild(lblErr)
110
111 btnImportOk:SetCallback("OnClick", function(widget)
112 local msg = txt:GetText()
113 local err = Amr:ImportRankings(msg)
114 if err then
115 lblErr:SetText(err)
116 txt:SetFocus(true)
117 else
118 Amr:HideCover()
119 Amr:RefreshTeamUi()
120 end
121 end)
122
123 return txt
124 end
125
126 local function renderVersionWindow(container)
127
128 local windowWidth = 500
129 local lbl, lbl2
130 local bg, border = Amr:RenderCoverChrome(container, windowWidth, 600)
131
132 lbl = AceGUI:Create("AmrUiLabel")
133 lbl:SetWidth(windowWidth - 60)
134 lbl:SetJustifyH("CENTER")
135 lbl:SetFont(Amr.CreateFont("Bold", 24, Amr.Colors.TextHeaderActive))
136 lbl:SetText(L.TeamVersionTitle)
137 lbl:SetPoint("TOP", bg.content, "TOP", 0, -10)
138 bg:AddChild(lbl)
139
140 if not IsInGroup() and not IsInRaid() then
141 lbl2 = AceGUI:Create("AmrUiLabel")
142 lbl2:SetWidth(windowWidth - 20)
143 lbl2:SetJustifyH("CENTER")
144 lbl2:SetFont(Amr.CreateFont("Italic", 16, Amr.Colors.TextTan))
145 lbl2:SetText(L.TeamVersionNoGroup)
146 lbl2:SetPoint("TOP", lbl.frame, "BOTTOM", 0, -25)
147 bg:AddChild(lbl2)
148 border:SetHeight(150)
149 else
150 local units = Amr:GetGroupUnitIdentifiers()
151
152 local missing = {}
153 local tooLow = {}
154
155 for i, unitId in ipairs(units) do
156 local realm, name = Amr:GetRealmAndName(unitId)
157 if realm then
158 local ver = Amr:GetAddonVersion(realm, name)
159 if ver == 0 then
160 table.insert(missing, { unitId, realm, name })
161 elseif ver < Amr.MIN_ADDON_VERSION then
162 table.insert(tooLow, { unitId, realm, name, ver })
163 end
164 end
165 end
166
167 if #missing == 0 and #tooLow == 0 then
168 lbl2 = AceGUI:Create("AmrUiLabel")
169 lbl2:SetWidth(windowWidth - 20)
170 lbl2:SetJustifyH("CENTER")
171 lbl2:SetFont(Amr.CreateFont("Italic", 16, Amr.Colors.TextTan))
172 lbl2:SetText(L.TeamVersionGood)
173 lbl2:SetPoint("TOP", lbl.frame, "BOTTOM", 0, -25)
174 bg:AddChild(lbl2)
175 border:SetHeight(150)
176 else
177 local prev = lbl
178 local h = 0
179
180 -- helper to render a player name
181 local function renderItem(obj, showVer)
182 lbl = AceGUI:Create("AmrUiLabel")
183 lbl:SetWidth(120)
184
185 local cls, clsEn = UnitClass(obj[1])
186 local color = clsEn and Amr.Colors.Classes[clsEn] or Amr.Colors.TextHeaderDisabled
187 lbl:SetFont(Amr.CreateFont("Regular", 14, color))
188
189 lbl:SetText(obj[3])
190 lbl:SetPoint("TOPLEFT", prev.frame, "BOTTOMLEFT", 0, -5)
191 bg:AddChild(lbl)
192 prev = lbl
193 h = h + lbl:GetHeight() + 5
194
195 if showVer then
196 lbl2 = AceGUI:Create("AmrUiLabel")
197 lbl2:SetWidth(60)
198 lbl2:SetFont(Amr.CreateFont("Regular", 14, Amr.Colors.White))
199 lbl2:SetText("v" .. obj[4])
200 lbl2:SetPoint("LEFT", lbl.frame, "RIGHT", 5, 0)
201 bg:AddChild(lbl2)
202 end
203 end
204
205 if #missing > 0 then
206 lbl2 = AceGUI:Create("AmrUiLabel")
207 lbl2:SetWidth(180)
208 lbl2:SetFont(Amr.CreateFont("Bold", 16, Amr.Colors.Red))
209 lbl2:SetText(L.TeamVersionMissing)
210 lbl2:SetJustifyH("CENTER")
211 lbl2:SetPoint("TOP", prev.frame, "BOTTOM", 0, -20)
212 bg:AddChild(lbl2)
213 h = h + lbl2:GetHeight() + 20
214
215 prev = lbl2
216 for i, obj in ipairs(missing) do
217 renderItem(obj)
218 end
219 end
220
221 if #tooLow > 0 then
222 lbl2 = AceGUI:Create("AmrUiLabel")
223 lbl2:SetWidth(180)
224 lbl2:SetFont(Amr.CreateFont("Bold", 16, Amr.Colors.Gold))
225 lbl2:SetText(L.TeamVersionOld)
226 lbl2:SetJustifyH("CENTER")
227 lbl2:SetPoint("TOP", prev.frame, "BOTTOM", 0, -20)
228 bg:AddChild(lbl2)
229 h = h + lbl2:GetHeight() + 20
230
231 prev = lbl2
232 for i, obj in ipairs(tooLow) do
233 renderItem(obj, true)
234 end
235 end
236
237 border:SetHeight(h + 100)
238 end
239 end
240
241 local btn = AceGUI:Create("AmrUiButton")
242 btn:SetText(L.TeamButtonExportClose)
243 btn:SetBackgroundColor(Amr.Colors.Green)
244 btn:SetFont(Amr.CreateFont("Bold", 16, Amr.Colors.White))
245 btn:SetWidth(120)
246 btn:SetHeight(28)
247 btn:SetPoint("BOTTOM", bg.content, "BOTTOM", 0, 10)
248 btn:SetCallback("OnClick", function(widget) Amr:HideCover() end)
249 bg:AddChild(btn)
250 end
251
252 local function onVersionClick()
253 -- show a window with players who do not have the addon or too low a version
254 Amr:ShowCover(renderVersionWindow)
255 end
256
257 local function onExportRosterClick()
258
259 Amr:ShowCover(L.TeamExportRosterLoading)
260
261 Amr:ExportRosterAsync(function(txt)
262 Amr:HideCover()
263
264 if not txt then
265 Amr:ShowAlert(L.TeamAlertNoGroup, L.AlertOk)
266 return
267 end
268
269 Amr:ShowCover(function(container)
270 local textbox = renderExportWindow(container, L.TeamExportRosterText, txt)
271 textbox:SetFocus(true)
272 end)
273 end)
274
275 end
276
277 local function onExportLootClick()
278
279 Amr:ShowCover(L.TeamExportRosterLoading)
280
281 Amr:ExportLootAsync(function(txt)
282 Amr:HideCover()
283
284 if txt == "NOGROUP" then
285 Amr:ShowAlert(L.TeamAlertNoGroup, L.AlertOk)
286 return
287 elseif txt == "NOLOOT" then
288 Amr:ShowAlert(L.TeamAlertNoLoot, L.AlertOk)
289 return
290 else
291 Amr:ShowCover(function(container)
292 local textbox = renderExportWindow(container, L.TeamExportLootText, txt)
293 textbox:SetFocus(true)
294 end)
295 end
296 end)
297 end
298
299 local function onImportRankingsClick()
300 Amr:ShowCover(function(container)
301 local textbox = renderImportWindow(container)
302 textbox:SetFocus(true)
303 end)
304 end
305
306 local function renderTab(tab, container)
307
308 local lbl, lbl2
309
310 if tab == "Member" then
311 local lbl = AceGUI:Create("AmrUiLabel")
312 lbl:SetWidth(500)
313 lbl:SetFont(Amr.CreateFont("Regular", 24, Amr.Colors.TextTan))
314 lbl:SetText(L.TeamMemberText)
315 lbl:SetPoint("TOPLEFT", container.content, "TOPLEFT", 0, -40)
316 container:AddChild(lbl)
317
318 -- if loot is still going on, show a button to re-show the loot window
319 if Amr.db.char.TeamOpt.LootInProgress then
320 lbl2 = AceGUI:Create("AmrUiLabel")
321 lbl2:SetWidth(500)
322 lbl2:SetFont(Amr.CreateFont("Italic", 18, Amr.Colors.TextTan))
323 lbl2:SetText(L.TeamMemberShowLootLabel)
324 lbl2:SetPoint("TOPLEFT", lbl.frame, "BOTTOMLEFT", 0, -60)
325 container:AddChild(lbl2)
326
327 local btn = AceGUI:Create("AmrUiButton")
328 btn:SetWidth(180)
329 btn:SetHeight(26)
330 btn:SetBackgroundColor(Amr.Colors.Blue)
331 btn:SetFont(Amr.CreateFont("Bold", 16, Amr.Colors.White))
332 btn:SetText(L.TeamMemberShowLoot)
333 btn:SetPoint("TOPLEFT", lbl2.frame, "BOTTOMLEFT", 0, -10)
334 btn:SetCallback("OnClick", function(widget)
335 Amr:ShowLootWindow()
336 Amr:RefreshLootWindow()
337 Amr:RefreshLootRolls()
338 end)
339 container:AddChild(btn)
340 end
341
342 elseif tab == "Leader" then
343
344 local lblNum = AceGUI:Create("AmrUiLabel")
345 lblNum:SetFont(Amr.CreateFont("Bold", 26, Amr.Colors.White))
346 lblNum:SetText("0.")
347 lblNum:SetWidth(40)
348 lblNum:SetPoint("TOPLEFT", container.content, "TOPLEFT", 6, -40)
349 container:AddChild(lblNum)
350
351 local btnVersion = AceGUI:Create("AmrUiButton")
352 btnVersion:SetText(L.TeamButtonVersionText)
353 btnVersion:SetBackgroundColor(Amr.Colors.Orange)
354 btnVersion:SetFont(Amr.CreateFont("Bold", 16, Amr.Colors.White))
355 btnVersion:SetWidth(180)
356 btnVersion:SetHeight(26)
357 btnVersion:SetPoint("LEFT", lblNum.frame, "RIGHT", 0, -1)
358 btnVersion:SetCallback("OnClick", onVersionClick)
359 container:AddChild(btnVersion)
360
361 lbl = AceGUI:Create("AmrUiLabel")
362 lbl:SetFont(Amr.CreateFont("Italic", 14, Amr.Colors.TextTan))
363 lbl:SetText(L.TeamExportVersionLabel)
364 lbl:SetWidth(400)
365 lbl:SetPoint("TOPLEFT", btnVersion.frame, "TOPRIGHT", 20, 0)
366 container:AddChild(lbl)
367
368 lblNum = AceGUI:Create("AmrUiLabel")
369 lblNum:SetFont(Amr.CreateFont("Bold", 26, Amr.Colors.White))
370 lblNum:SetText("1.")
371 lblNum:SetWidth(40)
372 lblNum:SetPoint("TOPRIGHT", btnVersion.frame, "BOTTOMLEFT", 0, -39)
373 container:AddChild(lblNum)
374
375 local btnRoster = AceGUI:Create("AmrUiButton")
376 btnRoster:SetText(L.TeamButtonExportRosterText)
377 btnRoster:SetBackgroundColor(Amr.Colors.Orange)
378 btnRoster:SetFont(Amr.CreateFont("Bold", 16, Amr.Colors.White))
379 btnRoster:SetWidth(180)
380 btnRoster:SetHeight(26)
381 btnRoster:SetPoint("LEFT", lblNum.frame, "RIGHT", 0, -1)
382 btnRoster:SetCallback("OnClick", onExportRosterClick)
383 container:AddChild(btnRoster)
384
385 lbl = AceGUI:Create("AmrUiLabel")
386 lbl:SetFont(Amr.CreateFont("Italic", 14, Amr.Colors.TextTan))
387 lbl:SetText(L.TeamExportRosterLabel)
388 lbl:SetWidth(400)
389 lbl:SetPoint("TOPLEFT", btnRoster.frame, "TOPRIGHT", 20, 0)
390 container:AddChild(lbl)
391
392 lblNum = AceGUI:Create("AmrUiLabel")
393 lblNum:SetFont(Amr.CreateFont("Bold", 26, Amr.Colors.White))
394 lblNum:SetText("2.")
395 lblNum:SetWidth(40)
396 lblNum:SetPoint("TOPRIGHT", btnRoster.frame, "BOTTOMLEFT", 0, -89)
397 container:AddChild(lblNum)
398
399 local btnLoot = AceGUI:Create("AmrUiButton")
400 btnLoot:SetText(L.TeamButtonExportLootText)
401 btnLoot:SetBackgroundColor(Amr.Colors.Orange)
402 btnLoot:SetFont(Amr.CreateFont("Bold", 16, Amr.Colors.White))
403 btnLoot:SetWidth(180)
404 btnLoot:SetHeight(26)
405 btnLoot:SetPoint("LEFT", lblNum.frame, "RIGHT", 0, -1)
406 btnLoot:SetCallback("OnClick", onExportLootClick)
407 container:AddChild(btnLoot)
408
409 lbl = AceGUI:Create("AmrUiLabel")
410 lbl:SetFont(Amr.CreateFont("Italic", 14, Amr.Colors.TextTan))
411 lbl:SetText(L.TeamExportLootLabel)
412 lbl:SetWidth(400)
413 lbl:SetPoint("TOPLEFT", btnLoot.frame, "TOPRIGHT", 20, 0)
414 container:AddChild(lbl)
415
416 lbl2 = AceGUI:Create("AmrUiLabel")
417 lbl2:SetFont(Amr.CreateFont("Bold", 14, Amr.Colors.Blue))
418 lbl2:SetText(L.TeamExportLootLabel2)
419 lbl2:SetWidth(400)
420 lbl2:SetPoint("TOPLEFT", lbl.frame, "BOTTOMLEFT", 0, -5)
421 container:AddChild(lbl2)
422
423 lblNum = AceGUI:Create("AmrUiLabel")
424 lblNum:SetFont(Amr.CreateFont("Bold", 26, Amr.Colors.White))
425 lblNum:SetText("3.")
426 lblNum:SetWidth(40)
427 lblNum:SetPoint("TOPRIGHT", btnLoot.frame, "BOTTOMLEFT", 0, -89)
428 container:AddChild(lblNum)
429
430 local btnRank = AceGUI:Create("AmrUiButton")
431 btnRank:SetText(L.TeamButtonImportRankingsText)
432 btnRank:SetBackgroundColor(Amr.Colors.Green)
433 btnRank:SetFont(Amr.CreateFont("Bold", 16, Amr.Colors.White))
434 btnRank:SetWidth(180)
435 btnRank:SetHeight(26)
436 btnRank:SetPoint("LEFT", lblNum.frame, "RIGHT", 0, -1)
437 btnRank:SetCallback("OnClick", onImportRankingsClick)
438 container:AddChild(btnRank)
439
440 lbl = AceGUI:Create("AmrUiLabel")
441 lbl:SetFont(Amr.CreateFont("Italic", 14, Amr.Colors.TextTan))
442 lbl:SetText(L.TeamImportRankingsLabel)
443 lbl:SetWidth(400)
444 lbl:SetPoint("TOPLEFT", btnRank.frame, "TOPRIGHT", 20, 0)
445 container:AddChild(lbl)
446
447 _panelStartLoot = AceGUI:Create("AmrUiPanel")
448 _panelStartLoot:SetLayout("None")
449 _panelStartLoot:SetBackgroundColor(Amr.Colors.Black, 0)
450 _panelStartLoot:SetPoint("TOPLEFT", lblNum.frame, "BOTTOMLEFT", 0, -90)
451 container:AddChild(_panelStartLoot)
452 _panelStartLoot:SetVisible(false)
453
454 lblNum = AceGUI:Create("AmrUiLabel")
455 lblNum:SetFont(Amr.CreateFont("Bold", 26, Amr.Colors.White))
456 lblNum:SetText("4.")
457 lblNum:SetWidth(40)
458 lblNum:SetPoint("TOPLEFT", _panelStartLoot.content, "TOPLEFT")
459 _panelStartLoot:AddChild(lblNum)
460
461 _btnStartLoot = AceGUI:Create("AmrUiButton")
462 _btnStartLoot:SetText(L.TeamButtonStartLootText)
463 _btnStartLoot:SetBackgroundColor(Amr.Colors.Blue)
464 _btnStartLoot:SetFont(Amr.CreateFont("Bold", 16, Amr.Colors.White))
465 _btnStartLoot:SetWidth(180)
466 _btnStartLoot:SetHeight(26)
467 _btnStartLoot:SetPoint("LEFT", lblNum.frame, "RIGHT", 0, -1)
468 _btnStartLoot:SetCallback("OnClick", function(widget)
469 if Amr.db.char.TeamOpt.LootInProgress then
470 Amr:ShowLootWindow()
471 Amr:RefreshLootWindow()
472 Amr:RefreshLootRolls()
473 else
474 Amr:StartLoot()
475 end
476 end)
477 _panelStartLoot:AddChild(_btnStartLoot)
478
479 _lblStartLoot = AceGUI:Create("AmrUiLabel")
480 _lblStartLoot:SetFont(Amr.CreateFont("Bold", 16, Amr.Colors.Text))
481 _lblStartLoot:SetWidth(400)
482 _lblStartLoot:SetPoint("LEFT", _btnStartLoot.frame, "RIGHT", 20, 0)
483 _panelStartLoot:AddChild(_lblStartLoot)
484 end
485
486 -- loot history shows on either tab
487 lbl = AceGUI:Create("AmrUiLabel")
488 lbl:SetFont(Amr.CreateFont("Regular", 16, Amr.Colors.TextTan))
489 lbl:SetText(L.TeamHistoryTitle)
490 lbl:SetWidth(280)
491 lbl:SetPoint("TOPRIGHT", container.content, "TOPRIGHT", 0, -12)
492 container:AddChild(lbl)
493
494 local panelHistory = AceGUI:Create("AmrUiPanel")
495 panelHistory:SetLayout("Fill")
496 panelHistory:SetBackgroundColor(Amr.Colors.Black, 0.3)
497 panelHistory:SetPoint("TOPRIGHT", lbl.frame, "BOTTOMRIGHT", 0, -5)
498 panelHistory:SetPoint("BOTTOMLEFT", container.content, "BOTTOMRIGHT", -280, 0)
499 container:AddChild(panelHistory)
500
501 _scrollHistory = AceGUI:Create("AmrUiScrollFrame")
502 _scrollHistory:SetLayout("List")
503 panelHistory:AddChild(_scrollHistory)
504 end
505
506 local function renderHistory()
507 if not _scrollHistory then return end
508 _scrollHistory:ReleaseChildren()
509
510 -- history is list of objects with:
511 -- link, result, class, name
512
513 local history = Amr.db.char.TeamOpt.History
514 local historyWidth = 260
515
516 local emptyMsg = nil
517 if not IsInGroup() and not IsInRaid() then
518 emptyMsg = L.TeamHistoryNoGroup
519 elseif not history or #history == 0 then
520 emptyMsg = L.TeamHistoryEmpty
521 end
522
523 if emptyMsg then
524 local panel = AceGUI:Create("AmrUiPanel")
525 panel:SetLayout("None")
526 panel:SetBackgroundColor(Amr.Colors.Black, 0)
527 panel:SetWidth(historyWidth)
528 panel:SetHeight(30)
529 _scrollHistory:AddChild(panel)
530
531 local lbl = AceGUI:Create("AmrUiLabel")
532 lbl:SetWidth(historyWidth)
533 lbl:SetJustifyH("CENTER")
534 lbl:SetFont(Amr.CreateFont("Italic", 14, Amr.Colors.TextTan))
535 lbl:SetText(emptyMsg)
536 lbl:SetPoint("LEFT", panel.content, "LEFT", 8, 0)
537 panel:AddChild(lbl)
538 else
539 for i = #history, 1, -1 do
540 local obj = history[i]
541 local itemLink = obj.link
542
543 local panel = AceGUI:Create("AmrUiPanel")
544 panel:SetLayout("None")
545 panel:SetBackgroundColor(Amr.Colors.Black, 0)
546 panel:SetWidth(historyWidth)
547 panel:SetHeight(45)
548 _scrollHistory:AddChild(panel)
549
550 local lbl = AceGUI:Create("AmrUiLabel")
551 lbl:SetWidth(historyWidth - 5)
552 lbl:SetWordWrap(false)
553 lbl:SetFont(Amr.CreateFont("Regular", 14, Amr.Colors.Text))
554 lbl:SetPoint("TOPLEFT", panel.content, "TOPLEFT", 5, -5)
555 panel:AddChild(lbl)
556
557 Amr.GetItemInfo(itemLink, function(obj, name, link)
558 -- set item name, tooltip
559 obj:SetText(link:gsub("%[", ""):gsub("%]", ""))
560 Amr:SetItemTooltip(obj, link, "ANCHOR_BOTTOMRIGHT", 0, obj.frame:GetHeight())
561 end, lbl)
562
563 lbl = AceGUI:Create("AmrUiLabel")
564 lbl:SetWidth(historyWidth - 5)
565 lbl:SetWordWrap(false)
566 lbl:SetFont(Amr.CreateFont("Italic", 12, Amr.Colors.White))
567
568 if obj.result == "Disenchant" then
569 lbl:SetFont(Amr.CreateFont("Italic", 12, Amr.Colors.TextHeaderDisabled))
570 lbl:SetText(L.TeamLootOptionDisenchant)
571 else
572 local color = obj.class and Amr.Colors.Classes[obj.class] or Amr.Colors.TextHeaderDisabled
573 lbl:SetText((obj.result == "??" and "" or L["TeamLootOption" .. obj.result] .. ": ") .."|c" .. Amr.ColorToHex(color, 1) .. obj.name .. "|r")
574 end
575
576 lbl:SetPoint("BOTTOMLEFT", panel.content, "BOTTOMLEFT", 5, 8)
577 panel:AddChild(lbl)
578
579 local line = AceGUI:Create("AmrUiPanel")
580 line:SetBackgroundColor(Amr.Colors.Black, 1)
581 line:SetWidth(historyWidth)
582 line:SetHeight(1)
583 line:SetPoint("BOTTOM", panel.content, "BOTTOM")
584 panel:AddChild(line)
585 end
586 end
587 end
588
589 local function onTabSelected(container, event, group)
590 container:ReleaseChildren()
591
592 -- clear references to tab elements
593 _panelStartLoot = nil
594 _lblStartLoot = nil
595 _btnStartLoot = nil
596 _scrollHistory = nil
597
598 Amr.db.char.TeamOpt.Role = group
599 renderTab(group, container)
600 Amr:RefreshTeamUi()
601 end
602
603 -- renders the main UI for the Team Optimizer tab
604 function Amr:RenderTabTeam(container)
605
606 -- splash screen to customize team optimizer ui for the user
607 if not Amr.db.char.TeamOpt.Role then
608 _panelSplash = AceGUI:Create("AmrUiPanel")
609 _panelSplash:SetLayout("None")
610 _panelSplash:SetBackgroundColor(Amr.Colors.Black, 0)
611 _panelSplash:SetPoint("TOPLEFT", container.content, "TOPLEFT")
612 _panelSplash:SetPoint("BOTTOMRIGHT", container.content, "BOTTOMRIGHT")
613 container:AddChild(_panelSplash)
614
615 local lblSplash = AceGUI:Create("AmrUiLabel")
616 lblSplash:SetWidth(800)
617 lblSplash:SetJustifyH("CENTER")
618 lblSplash:SetFont(Amr.CreateFont("Regular", 24, Amr.Colors.Text))
619 lblSplash:SetText(L.TeamSplashHeader)
620 lblSplash:SetPoint("TOP", _panelSplash.content, "TOP", 0, -40)
621 _panelSplash:AddChild(lblSplash)
622
623 local btn = AceGUI:Create("AmrUiButton")
624 btn:SetText(L.TeamTabLeaderText)
625 btn:SetBackgroundColor(Amr.Colors.Orange)
626 btn:SetFont(Amr.CreateFont("Bold", 24, Amr.Colors.White))
627 btn:SetWidth(280)
628 btn:SetHeight(60)
629 btn:SetPoint("TOPRIGHT", lblSplash.frame, "BOTTOM", -50, -50)
630 btn:SetCallback("OnClick", function(widget)
631 Amr.db.char.TeamOpt.Role = "Leader"
632 _panelSplash:SetVisible(false)
633 _tabs:SetVisible(true)
634 _tabs:SelectTab("Leader")
635 end)
636 _panelSplash:AddChild(btn)
637
638 local lbl = AceGUI:Create("AmrUiLabel")
639 lbl:SetWidth(280)
640 lbl:SetJustifyH("CENTER")
641 lbl:SetFont(Amr.CreateFont("Italic", 16, Amr.Colors.TextTan))
642 lbl:SetText(L.TeamSplashLeaderLabel)
643 lbl:SetPoint("TOP", btn.frame, "BOTTOM", 0, -20)
644 _panelSplash:AddChild(lbl)
645
646 btn = AceGUI:Create("AmrUiButton")
647 btn:SetText(L.TeamTabMemberText)
648 btn:SetBackgroundColor(Amr.Colors.Orange)
649 btn:SetFont(Amr.CreateFont("Bold", 24, Amr.Colors.White))
650 btn:SetWidth(280)
651 btn:SetHeight(60)
652 btn:SetPoint("TOPLEFT", lblSplash.frame, "BOTTOM", 50, -50)
653 btn:SetCallback("OnClick", function(widget)
654 Amr.db.char.TeamOpt.Role = "Member"
655 _panelSplash:SetVisible(false)
656 _tabs:SetVisible(true)
657 _tabs:SelectTab("Member")
658 end)
659 _panelSplash:AddChild(btn)
660
661 lbl = AceGUI:Create("AmrUiLabel")
662 lbl:SetWidth(280)
663 lbl:SetJustifyH("CENTER")
664 lbl:SetFont(Amr.CreateFont("Italic", 16, Amr.Colors.TextTan))
665 lbl:SetText(L.TeamSplashMemberLabel)
666 lbl:SetPoint("TOP", btn.frame, "BOTTOM", 0, -20)
667 _panelSplash:AddChild(lbl)
668 end
669
670 -- tabstrip
671 _tabs = AceGUI:Create("AmrUiTabGroup")
672 _tabs:SetLayout("None")
673 _tabs:SetTabs({
674 {text=L.TeamTabLeaderText, value="Leader", style="bold"},
675 {text=L.TeamTabMemberText, value="Member", style="bold"}
676 })
677 _tabs:SetPoint("TOPLEFT", container.content, "TOPLEFT", 6, -30)
678 _tabs:SetPoint("BOTTOMRIGHT", container.content, "BOTTOMRIGHT")
679 _tabs:SetCallback("OnGroupSelected", onTabSelected)
680 container:AddChild(_tabs)
681
682 local role = Amr.db.char.TeamOpt.Role
683
684 _tabs:SetVisible(not not role)
685 if role then
686 -- if a role has been chosen, select the proper tab (which will also refresh the UI)
687 _tabs:SelectTab(role)
688 else
689 -- no role, refresh the UI manually
690 self:RefreshTeamUi()
691 end
692
693 end
694
695 function Amr:ReleaseTabTeam()
696 _panelSplash = nil
697 _panelStartLoot = nil
698 _lblStartLoot = nil
699 _btnStartLoot = nil
700 _scrollHistory = nil
701 _tabs = nil
702 end
703
704 function Amr:RefreshTeamUi()
705
706 -- if rankings have been loaded, render the 'start loot' panel
707 if _panelStartLoot then
708 local rankString = Amr.db.global.TeamOpt.RankingString
709 if rankString then
710 _panelStartLoot:SetVisible(true)
711 _lblStartLoot:SetText(L.TeamStartLootLabel(#Amr.db.global.TeamOpt.Rankings))
712 _btnStartLoot:SetText(Amr.db.char.TeamOpt.LootInProgress and L.TeamButtonResumeLootText or L.TeamButtonStartLootText)
713 else
714 _panelStartLoot:SetVisible(false)
715 end
716 end
717
718 -- render loot history
719 renderHistory()
720 end
721
722 local function getItemIdsFromLinks(all, list)
723 for i, v in ipairs(list) do
724 local obj = Amr.ParseItemLink(v)
725 local id = Amr.GetItemUniqueId(obj)
726 if id then
727 table.insert(all, id)
728 end
729 end
730 end
731
732 -- update AllItems, used to determine when a new item is actually a new equippable item
733 local function snapshotAllItems(data)
734
735 local all = {}
736 for k, v in pairs(data.Equipped[data.ActiveSpec]) do
737 local obj = Amr.ParseItemLink(v)
738 local id = Amr.GetItemUniqueId(obj)
739 if id then
740 table.insert(all, id)
741 end
742 end
743 getItemIdsFromLinks(all, data.BagItems)
744 getItemIdsFromLinks(all, data.BankItems)
745 getItemIdsFromLinks(all, data.VoidItems)
746
747 table.sort(all)
748 return all
749 end
750
751 local function sendGear(prefix, empty)
752
753 local region = Amr.RegionNames[GetCurrentRegion()]
754 local realm = GetRealmName()
755 local name = UnitName("player")
756
757 -- get all data, including inventory
758 local txt = "_"
759 if not empty then
760 local data = Amr:ExportCharacter()
761 txt = Amr.Serializer:SerializePlayerData(data, true)
762
763 -- snapshot items when gear is sent
764 Amr.db.char.TeamOpt.AllItems = snapshotAllItems(data)
765 end
766
767 local msg = string.format("%s\n%s\n%s\n%s\n%s", prefix, region, realm, name, txt)
768 Amr:SendAmrCommMessage(msg)
769 end
770
771 local function toPlayerKey(realm, name)
772 return name .. "-" .. realm
773 end
774
775
776 ------------------------------------------------------------------------------------------------
777 -- Loot Export
778 ------------------------------------------------------------------------------------------------
779
780 -- prune out any characters no longer in the player's group
781 local function pruneGearForItemExport()
782
783 local newInfo = {}
784 local units = Amr:GetGroupUnitIdentifiers()
785
786 for i, unitId in ipairs(units) do
787 local realm, name = Amr:GetRealmAndName(unitId)
788 if realm then
789 local key = toPlayerKey(realm, name)
790 newInfo[key] = Amr.db.global.TeamOpt.LootGear[key]
791 end
792 end
793
794 Amr.db.global.TeamOpt.LootGear = newInfo
795 end
796
797 local function scanMasterLoot()
798 -- only care if we are in a raid or group
799 if not IsInGroup() and not IsInRaid() then return end
800
801 -- we only care about the master looter
802 if not IsMasterLooter() then return end
803
804 -- guid of the unit being looted
805 local npcGuid = UnitGUID("target")
806 if not npcGuid then
807 -- this could wack shit out... but no raid bosses drop loot from containers right now, so should be fine
808 npcGuid = "container"
809 end
810
811 -- if we already have loot data for this unit, then we can ignore
812 if Amr.db.char.TeamOpt.LootGuid == npcGuid then return end
813
814 local loot = {}
815 for i = 1, GetNumLootItems() do
816 --local texture, item, quantity, quality, locked = GetLootSlotInfo(i)
817 local lootType = GetLootSlotType(i)
818 if lootType == 1 then
819 local link = GetLootSlotLink(i)
820 table.insert(loot, link)
821 end
822 end
823
824 Amr.db.char.TeamOpt.LootGuid = npcGuid
825 Amr.db.char.TeamOpt.Loot = loot
826
827 -- publish loot information to everyone else running the addon in case team optimizer user is not the master looter
828 local msg = _messagePrefixes.ItemExportLoot .. "\n" .. npcGuid .. "\n" .. table.concat(loot, "\n")
829 Amr:SendAmrCommMessage(msg)
830 end
831
832 local function onLootReceived(parts)
833
834 Amr.db.char.TeamOpt.LootGuid = parts[2]
835
836 local loot = {}
837 for i = 3, #parts do
838 table.insert(loot, parts[i])
839 end
840 Amr.db.char.TeamOpt.Loot = loot
841 end
842
843 local function onLeaveGroup()
844 -- if the current player is no longer in a group or raid, finish any looting in progress
845 Amr:FinishLoot(true)
846
847 -- clear loot when leave a group
848 Amr.db.char.TeamOpt.Loot = {}
849 Amr.db.char.TeamOpt.LootGuid = nil
850 Amr.db.global.TeamOpt.LootGear = {}
851 end
852
853 local function onGroupChanged()
854
855 if not IsInGroup() and not IsInRaid() then
856 onLeaveGroup()
857 end
858 end
859
860
861 local _lootExPlayersRemaining = 0
862 local _lootExRoster = nil
863 local _lootExCallback = nil
864
865 local function serializeLootExport()
866 if not IsInGroup() and not IsInRaid() then return "NOGROUP" end
867
868 local loot = Amr.db.char.TeamOpt.Loot
869 if not loot or #loot == 0 then return "NOLOOT" end
870
871 local itemObjects = {}
872 for i, link in ipairs(loot) do
873 local obj = Amr.ParseItemLink(link)
874 if obj then
875 table.insert(itemObjects, obj)
876 end
877 end
878
879 -- DEBUG: just grab all currently equipped items
880 --[[
881 itemObjects = {}
882 local blah = Amr:ExportCharacter()
883 for k, v in pairs(blah.Equipped[blah.ActiveSpec]) do
884 local obj = Amr.ParseItemLink(v)
885 if obj then
886 table.insert(itemObjects, obj)
887 end
888 end
889 ]]
890
891 local parts = {}
892
893 -- unique ids of items
894 local lootPart = {}
895 for i, obj in ipairs(itemObjects) do
896 table.insert(lootPart, Amr.GetItemUniqueId(obj))
897 end
898 table.insert(parts, table.concat(lootPart, ";"))
899
900 -- gear for players who have gained loot since the last item import or roster export
901 pruneGearForItemExport()
902 local lootGear = Amr.db.global.TeamOpt.LootGear
903 for k, v in pairs(lootGear) do
904 table.insert(parts, v)
905 end
906
907 return table.concat(parts, "\n")
908 end
909
910 local function onLootExportCompleted()
911
912 -- fill in LootGear with just those players who have changed
913 Amr.db.global.TeamOpt.LootGear = _lootExRoster
914
915 if _lootExCallback then
916 local txt = serializeLootExport()
917 _lootExCallback(txt)
918 end
919
920 -- reset state
921 _lootExPlayersRemaining = 0
922 _lootExRoster = nil
923 _lootExCallback = nil
924 end
925
926 -- called when this player's gear info has been requested by someone exporting loot
927 local function onGearForLootExportRequested()
928
929 local hasNewItem = false
930 local oldItems = Amr.db.char.TeamOpt.AllItems
931
932 if oldItems and #oldItems > 0 then
933 -- see if any new equippable items have been gained by comparing to the last snapshot
934 local data = Amr:ExportCharacter()
935 local allItems = snapshotAllItems(data)
936
937 if #oldItems ~= #allItems then
938 hasNewItem = true
939 else
940 -- go through items from front to back, if there are any that don't match then something has changed
941 for i = 1, #allItems do
942 local oldItem = oldItems[i]
943 local newItem = allItems[i]
944 if oldItem ~= newItem then
945 hasNewItem = true
946 break
947 end
948 end
949 end
950 end
951
952 -- whenever a new item is received, send out updated gear information that should be added to the next item export
953 sendGear(_messagePrefixes.ItemExportGear, not hasNewItem)
954 end
955
956 local function onGearForLootExportReceived(region, realm, name, data)
957 -- if I am not listening for incoming gear data for an item export, then ignore this message
958 if _lootExPlayersRemaining == 0 then return end
959
960 local key = toPlayerKey(realm, name)
961 if not data or data == "_" then
962 _lootExRoster[key] = nil
963 else
964 _lootExRoster[key] = data
965 end
966
967 _lootExPlayersRemaining = _lootExPlayersRemaining - 1
968 if _lootExPlayersRemaining <= 0 then
969 onLootExportCompleted()
970 end
971 end
972
973 -- Export the current loot, including any known gear data for players with the in-game addon, but only if it has changed since the last snapshot.
974 -- This is asynchronous because it needs to wait for gear data to arrive from each player.
975 function Amr:ExportLootAsync(callback)
976
977 if not IsInGroup() and not IsInRaid() then
978 callback("NOGROUP")
979 end
980
981 local loot = Amr.db.char.TeamOpt.Loot
982 if not loot or #loot == 0 then
983 callback("NOLOOT")
984 end
985
986 local playersNoGear = {}
987 _lootExPlayersRemaining = 0
988
989 local units = self:GetGroupUnitIdentifiers()
990 for i, unitId in ipairs(units) do
991 local realm, name = self:GetRealmAndName(unitId)
992 if realm then
993 local ver = self:GetAddonVersion(realm, name)
994 local key = toPlayerKey(realm, name)
995
996 if ver >= Amr.MIN_ADDON_VERSION then
997 _lootExPlayersRemaining = _lootExPlayersRemaining + 1
998 else
999 table.insert(playersNoGear, unitId)
1000 end
1001 end
1002 end
1003
1004 _lootExRoster = {}
1005 _lootExCallback = callback
1006
1007 if _lootExPlayersRemaining > 0 then
1008 -- send a message to receive player data, when the last player is received onLootExportCompleted will be called
1009 Amr:SendAmrCommMessage(_messagePrefixes.ItemExportRequestGear)
1010 else
1011 -- don't need to wait for anybody, just call immediately
1012 onLootExportCompleted()
1013 end
1014 end
1015
1016
1017 ------------------------------------------------------------------------------------------------
1018 -- Roster Export
1019 ------------------------------------------------------------------------------------------------
1020
1021 local _rosterPlayersRemaining = 0
1022 local _roster = nil
1023 local _rosterCallback = nil
1024
1025 local function onRosterCompleted()
1026
1027 if _rosterCallback then
1028 -- serialize the roster
1029 local parts = {}
1030 for key, data in pairs(_roster) do
1031 table.insert(parts, data)
1032 end
1033 local msg = table.concat(parts, "\n")
1034
1035 -- send to callback
1036 _rosterCallback(msg)
1037 end
1038
1039 -- reset state
1040 _rosterPlayersRemaining = 0
1041 _roster = nil
1042 _rosterCallback = nil
1043
1044 -- clear out loot gear needed, an export will refresh everyone at the time of export
1045 Amr.db.global.TeamOpt.LootGear = {}
1046 end
1047
1048 -- called when this player's gear info has been requested by someone exporting the raid roster
1049 local function onGearForRosterRequested()
1050
1051 sendGear(_messagePrefixes.RosterGear)
1052 end
1053
1054 local function onGearForRosterReceived(region, realm, name, data)
1055 -- if I am not listening for incoming gear data for the roster, then ignore this message
1056 if _rosterPlayersRemaining == 0 then return end
1057
1058 local key = toPlayerKey(realm, name)
1059 _roster[key] = data
1060
1061 _rosterPlayersRemaining = _rosterPlayersRemaining - 1
1062 if _rosterPlayersRemaining <= 0 then
1063 onRosterCompleted()
1064 end
1065 end
1066
1067 -- Export the current roster, including any known gear data for players with the in-game addon.
1068 -- This is asynchronous because it needs to wait for gear data to arrive from each player.
1069 function Amr:ExportRosterAsync(callback)
1070 if not IsInGroup() and not IsInRaid() then
1071 callback()
1072 return
1073 end
1074
1075 local playersNoGear = {}
1076 _rosterPlayersRemaining = 0
1077
1078 local units = self:GetGroupUnitIdentifiers()
1079 for i, unitId in ipairs(units) do
1080 local realm, name = self:GetRealmAndName(unitId)
1081 if realm then
1082 local ver = self:GetAddonVersion(realm, name)
1083 local key = toPlayerKey(realm, name)
1084
1085 if ver >= Amr.MIN_ADDON_VERSION then
1086 _rosterPlayersRemaining = _rosterPlayersRemaining + 1
1087 else
1088 table.insert(playersNoGear, unitId)
1089 end
1090 end
1091 end
1092
1093 -- fill the roster with any players who can't send us data
1094 _roster = {}
1095 for i, unitId in ipairs(playersNoGear) do
1096 local realm, name = self:GetRealmAndName(unitId)
1097 if realm then
1098 local key = toPlayerKey(realm, name)
1099 local obj = {
1100 Region = Amr.RegionNames[GetCurrentRegion()],
1101 Realm = realm,
1102 Name = name
1103 }
1104 _roster[key] = Amr.Serializer:SerializePlayerIdentity(obj)
1105 end
1106 end
1107
1108 _rosterCallback = callback
1109
1110 if _rosterPlayersRemaining > 0 then
1111 -- send a message to receive player data, when the last player is received onRosterCompleted will be called
1112 Amr:SendAmrCommMessage(_messagePrefixes.RosterRequestGear)
1113 else
1114 -- don't need to wait for anybody, just call immediately
1115 onRosterCompleted()
1116 end
1117 end
1118
1119
1120 ------------------------------------------------------------------------------------------------
1121 -- Ranking Import
1122 ------------------------------------------------------------------------------------------------
1123
1124 -- helper to parse import item identifier format into an item object
1125 local function parseItemIdentifier(ident)
1126
1127 local parts = { strsplit(":", ident) }
1128 local item = {}
1129 item.id = tonumber(parts[1])
1130 item.enchantId = 0
1131 item.gemIds = { 0, 0, 0, 0 }
1132 item.suffixId = math.abs(tonumber(parts[2]))
1133 item.upgradeId = tonumber(parts[3])
1134
1135 if #parts > 3 then
1136 item.bonusIds = {}
1137 for b = 4, #parts do
1138 table.insert(item.bonusIds, tonumber(parts[b]))
1139 end
1140 table.sort(item.bonusIds)
1141 end
1142
1143 return item
1144 end
1145
1146 function Amr:ParseRankingString(data)
1147 local rankings = {}
1148
1149 local player = Amr:ExportCharacter()
1150 local myUnitId = Amr:GetUnitId(player.Realm, player.Name)
1151 local ml = IsMasterLooter()
1152
1153 local itemList = { strsplit("\n", data) }
1154 for i = 1, #itemList do
1155 local ranking = {}
1156
1157 local itemParts = { strsplit("_", itemList[i]) }
1158
1159 -- first part has the item identifier
1160 ranking.item = parseItemIdentifier(itemParts[1])
1161
1162 -- second part has item info
1163 local infoParts = { strsplit(";", itemParts[2]) }
1164 ranking.itemInfo = {
1165 slot = infoParts[1],
1166 subclass = infoParts[2],
1167 weaponType = infoParts[3],
1168 armorType = infoParts[4]
1169 }
1170
1171 local meInList = false
1172
1173 -- parse each ranking
1174 ranking.ranks = {}
1175 for j = 3, #itemParts do
1176 local rankParts = { strsplit(";", itemParts[j]) }
1177
1178 local rank = {}
1179 rank.realm = rankParts[1]
1180 rank.name = rankParts[2]
1181 rank.specId = tonumber(rankParts[3])
1182 if rankParts[4] ~= "--" then
1183 rank.equipped = parseItemIdentifier(rankParts[4])
1184 end
1185 rank.score = tonumber(rankParts[5])
1186 rank.isEquipped = rankParts[6] == "t"
1187 rank.notRanked = rankParts[7] == "t"
1188 rank.offspec = rankParts[8] == "t"
1189 rank.enchantingSkill = tonumber(rankParts[9])
1190
1191 table.insert(ranking.ranks, rank)
1192
1193 if myUnitId == Amr:GetUnitId(rank.realm, rank.name) then
1194 meInList = true
1195 rank.isMasterLooter = ml
1196 end
1197 end
1198
1199 -- if the current player is the master looter and he is not in the list, then add him at the end
1200 if ml and not meInList then
1201 local rank = {
1202 realm = player.Realm,
1203 name = player.Name,
1204 specId = player.Specs[player.ActiveSpec],
1205 equipped = "--",
1206 score = 0,
1207 isEquipped = false,
1208 notRanked = true,
1209 offspec = false,
1210 enchantingSkill = 0,
1211 isMasterLooter = true
1212 }
1213 table.insert(ranking.ranks, rank)
1214 end
1215
1216 table.insert(rankings, ranking)
1217 end
1218
1219 return rankings
1220 end
1221
1222 -- import rankings from the website, save into the database, returns a string error if can't import for some reason
1223 function Amr:ImportRankings(data)
1224
1225 if not data or string.len(data) == 0 then
1226 return L.ImportErrorEmpty
1227 end
1228
1229 local success, rankings = pcall(Amr.ParseRankingString, self, data)
1230
1231 if not success then
1232 return L.ImportErrorFormat
1233 end
1234
1235 -- finish any looting in progress, effectively canceling it, user will have to press Start Loot again
1236 Amr:FinishLoot()
1237
1238 -- save the rankings
1239 Amr.db.global.TeamOpt.Rankings = rankings
1240 Amr.db.global.TeamOpt.RankingString = data
1241
1242 -- clear loot gear needed on successful ranking import
1243 Amr.db.global.TeamOpt.LootGear = {}
1244 end
1245
1246
1247 ------------------------------------------------------------------------------------------------
1248 -- Loot Distribution
1249 ------------------------------------------------------------------------------------------------
1250
1251 function Amr:StartLoot()
1252
1253 if not IsInGroup() and not IsInRaid() then
1254 Amr:ShowAlert(L.TeamAlertNoGroup, L.AlertOk)
1255 return
1256 end
1257
1258 -- broadcast the loot data to everyone, this triggers the loot window to show
1259 local msg = string.format("%s\n%s", Amr.LootMessagePrefixes.Start, Amr.db.global.TeamOpt.RankingString)
1260 Amr:SendAmrCommMessage(msg)
1261 end
1262
1263 function Amr:FinishLoot(clearHistory)
1264
1265 -- reset all state
1266 Amr.db.char.TeamOpt.LootInProgress = false
1267
1268 -- don't reset these for now... only reset these if someone leaves a group
1269 --Amr.db.char.TeamOpt.Loot = {}
1270 --Amr.db.char.TeamOpt.LootGuid = nil
1271 --Amr.db.global.TeamOpt.LootGear = {}
1272
1273 Amr.db.global.TeamOpt.Rankings = {}
1274 Amr.db.global.TeamOpt.RankingString = nil
1275
1276 Amr.db.char.TeamOpt.Rolls = {}
1277
1278 if clearHistory then
1279 Amr.db.char.TeamOpt.History = {}
1280 end
1281
1282 -- close the loot window
1283 Amr:HideLootWindow()
1284
1285 -- re-render the team optimizer UI
1286 Amr:RefreshTeamUi()
1287 end
1288
1289
1290 ------------------------------------------------------------------------------------------------
1291 -- Synchronization
1292 ------------------------------------------------------------------------------------------------
1293 local _waitingForSync = false
1294
1295 -- check if we need to synchronize
1296 local function checkSync()
1297 -- if loot is in progress and this person is not the ML, send a request to synchronize on startup, this player may have missed some data
1298 if not IsMasterLooter() and Amr.db.char.TeamOpt.LootInProgress then
1299 _waitingForSync = true
1300 Amr:SendAmrCommMessage(_messagePrefixes.SyncRequest)
1301 end
1302 end
1303
1304 -- send data to anyone who needs to synchronize their loot data: history, rolls, rankings
1305 local function sendSyncData()
1306 -- only the master looter sends sync data to ensure that everyone gets the same stuff and we don't spam
1307 if not IsMasterLooter() then return end
1308
1309 local msgParts = {}
1310 table.insert(msgParts, _messagePrefixes.Sync)
1311 table.insert(msgParts, Amr:Serialize(Amr.db.char.TeamOpt.History))
1312 table.insert(msgParts, Amr:Serialize(Amr.db.char.TeamOpt.Rolls))
1313 table.insert(msgParts, Amr:Serialize(Amr.db.global.TeamOpt.Rankings))
1314
1315 Amr:SendAmrCommMessage(table.concat(msgParts, "\n"))
1316 end
1317
1318 local function receiveSyncData(parts)
1319 if not _waitingForSync then return end
1320 _waitingForSync = false
1321
1322 local success, obj = Amr:Deserialize(parts[2])
1323 if success then
1324 Amr.db.char.TeamOpt.History = obj
1325 end
1326
1327 success, obj = Amr:Deserialize(parts[3])
1328 if success then
1329 Amr.db.char.TeamOpt.Rolls = obj
1330 end
1331
1332 success, obj = Amr:Deserialize(parts[4])
1333 if success then
1334 Amr.db.global.TeamOpt.Rankings = obj
1335 end
1336
1337 -- refresh any windows that may be visible
1338 Amr:RefreshTeamUi()
1339 Amr:RefreshLootWindow()
1340 Amr:RefreshLootRolls()
1341 end
1342
1343
1344 function Amr:ProcessTeamMessage(message)
1345
1346 local parts = {}
1347 for part in string.gmatch(message, "([^\n]+)") do
1348 table.insert(parts, part)
1349 end
1350
1351 local prefix = parts[1]
1352
1353 if prefix == _messagePrefixes.RosterRequestGear then
1354 -- request for me to send my gear data
1355 onGearForRosterRequested()
1356 elseif prefix == _messagePrefixes.ItemExportRequestGear then
1357 -- request for me to send my gear data
1358 onGearForLootExportRequested()
1359 elseif prefix == _messagePrefixes.ItemExportLoot then
1360 -- the last loot that dropped
1361 onLootReceived(parts)
1362 elseif prefix == _messagePrefixes.SyncRequest then
1363 sendSyncData()
1364 elseif prefix == _messagePrefixes.Sync then
1365 receiveSyncData(parts)
1366 elseif prefix == Amr.LootMessagePrefixes.Start then
1367 Amr:OnStartLootReceived(parts)
1368 elseif prefix == Amr.LootMessagePrefixes.Roll then
1369 Amr:OnLootRollReceived(parts)
1370 elseif prefix == Amr.LootMessagePrefixes.Veto then
1371 Amr:OnLootVetoReceived(parts)
1372 elseif prefix == Amr.LootMessagePrefixes.Rand then
1373 Amr:OnLootRandReceived(parts)
1374 elseif prefix == Amr.LootMessagePrefixes.Give then
1375 Amr:OnLootGiveReceived(parts)
1376 elseif prefix == Amr.LootMessagePrefixes.Finish then
1377 Amr:FinishLoot()
1378 else
1379 -- message will be of format: prefix\nregion\nrealm\nname\n[stuff]
1380 local region = parts[2]
1381 local realm = parts[3]
1382 local name = parts[4]
1383 local data = parts[5]
1384
1385 if prefix == _messagePrefixes.RosterGear then
1386 -- receive gear data from someone
1387 onGearForRosterReceived(region, realm, name, data)
1388 elseif prefix == _messagePrefixes.ItemExportGear then
1389 -- receive gear data for item export
1390 onGearForLootExportReceived(region, realm, name, data)
1391 end
1392 end
1393 end
1394
1395 function Amr:InitializeTeamOpt()
1396
1397 if not IsInGroup() and not IsInRaid() then
1398 onLeaveGroup()
1399 end
1400
1401 Amr:AddEventHandler("LOOT_OPENED", scanMasterLoot)
1402 Amr:AddEventHandler("GROUP_ROSTER_UPDATE", onGroupChanged)
1403
1404 checkSync()
1405 end