vanilla-wow-addons – Blame information for rev 1

Subversion Repositories:
Rev:
Rev Author Line No. Line
1 office 1  
2 ---------------
3 -- CONSTANTS --
4 ---------------
5  
6 -- NOTE: "DMI"s are the legacy name for quantities that DM tracks.
7 -- However, the term "quantity" refers to a quantity that is displayed.
8 -- There are more Quantities than there are DMIs.
9 -- Better names for these two things would be "Real" vs. "Virtual" or "Display" quantities.
10  
11 DMI_DAMAGE = 1;
12 DMI_HEALING = 2;
13 DMI_DAMAGED = 3;
14 DMI_HEALED = 4;
15 DMI_REPORT_MAX = 4;
16 DMI_CURING = 5;
17 DMI_OVERHEAL = 6;
18 DMI_ABSHEAL = 7;
19 DMI_BUILTIN_MAX = 7;
20 DMI_MAX = 7;
21  
22 -- Just legacy crap.
23 DMI_1 = DMI_DAMAGE;
24 DMI_2 = DMI_HEALING;
25 DMI_3 = DMI_DAMAGED;
26 DMI_4 = DMI_HEALED;
27  
28 -- Defined Quantities.
29 -- These numbers index into the DM_QUANTDEFS table.
30 DamageMeters_Quantity_DAMAGE = 1;
31 DamageMeters_Quantity_HEALING = 2;
32 DamageMeters_Quantity_DAMAGED = 3;
33 DamageMeters_Quantity_HEALINGRECEIVED = 4;
34 DamageMeters_Quantity_CURING = 5;
35 DamageMeters_Quantity_OVERHEAL = 6;
36 DamageMeters_Quantity_ABSHEAL = 7;
37  
38 DamageMeters_Quantity_DPS = 8;
39 DamageMeters_Quantity_HPS = 9;
40 DamageMeters_Quantity_DTPS = 10;
41 DamageMeters_Quantity_HTPS = 11;
42 DamageMeters_Quantity_CURINGFIGHT = 12;
43 DamageMeters_Quantity_OVERHEALFIGHT = 13;
44 DamageMeters_Quantity_ABSHEALFIGHT = 14;
45  
46 DamageMeters_Quantity_TIME = 15;
47 DamageMeters_Quantity_NETDAMAGE = 16;
48 DamageMeters_Quantity_NETHEALING = 17;
49 DamageMeters_Quantity_DAMAGEPLUSHEALING = 18;
50 DamageMeters_Quantity_HEALTH = 19;
51 DamageMeters_Quantity_OVERHEALPERCENTAGE = 20;
52  
53 -- While this is assigned here, this number is actually CALCULATED when
54 -- DamageMeters is loaded. (It is set to the length of DM_QUANTDEFS). This was intended to
55 -- allow other mods to add quantities, but who knows if its really possible.
56 DamageMeters_Quantity_MAX = 18;
57  
58 -- These aren't actualy quantities, but rather just flags to DoReport.
59 DamageMeters_ReportQuantity_Total = -1;
60 DamageMeters_ReportQuantity_Leaders = -2;
61 DamageMeters_ReportQuantity_Events = -3;
62  
63 -------------------------------------------------------------------------------
64 -- Virtual Quantity Definitions --
65  
66 --[[
67 name - The localized string name of this quantity.
68 psName - [Optional] The string name of the quantity when it is per-second.
69 defaultColor - The default color of the frame when the quantity is visible.
70 dmi - [Optional] The DMI that this quantity is based on.
71 bUsesFightTable - True if this quantity uses the Fight table instead of the Active one.
72 toggleQuant - The quantity to switch to when we are toggled. (Note that toggling really means between fight/regular).
73 filterDefault - Default quantity filter setting.
74 pfnGetValue - function(playerStruct). A quantityDef can specify a function that the system is to call
75 to get a given player's value for this quantity.
76 titanAbbrev - A short string (ideally a few characters) to be shown in the titan panel for this quantity.
77 ]]--
78  
79 DM_QUANTDEFS = {};
80  
81 DM_QUANTDEFS[DamageMeters_Quantity_DAMAGE] = {
82 name = DM_QUANTSTRING_DAMAGEDONE,
83 psName = nil,
84 defaultColor = {1.0, 0.0, 0.0, 0.8},
85 dmi = DMI_DAMAGE,
86 bUsesFightTable = false,
87 toggleQuant = DamageMeters_Quantity_DPS,
88 filterDefault = true,
89 titanAbbrev = DM_QUANTABBREV_DAMAGEDONE;
90 };
91 DM_QUANTDEFS[DamageMeters_Quantity_HEALING] = {
92 name = DM_QUANTSTRING_HEALINGDONE,
93 psName = nil,
94 defaultColor = {0.0, 1.0, 0.0, 0.8},
95 dmi = DMI_HEALING,
96 bUsesFightTable = false,
97 toggleQuant = DamageMeters_Quantity_HPS,
98 filterDefault = true,
99 titanAbbrev = DM_QUANTABBREV_HEALINGDONE;
100 };
101 DM_QUANTDEFS[DamageMeters_Quantity_DAMAGED] = {
102 name = DM_QUANTSTRING_DAMAGETAKEN,
103 psName = nil,
104 defaultColor = {1.0, 0.5, 0.0, 0.8},
105 dmi = DMI_DAMAGED,
106 bUsesFightTable = false,
107 toggleQuant = DamageMeters_Quantity_DTPS,
108 filterDefault = true,
109 titanAbbrev = DM_QUANTABBREV_DAMAGETAKEN;
110 };
111 DM_QUANTDEFS[DamageMeters_Quantity_HEALINGRECEIVED] = {
112 name = DM_QUANTSTRING_HEALINGTAKEN,
113 psName = nil,
114 defaultColor = {0.0, 0.0, 1.0, 0.8},
115 dmi = DMI_HEALED,
116 bUsesFightTable = false,
117 toggleQuant = DamageMeters_Quantity_HTPS,
118 filterDefault = true,
119 titanAbbrev = DM_QUANTABBREV_HEALINGTAKEN;
120 };
121 DM_QUANTDEFS[DamageMeters_Quantity_CURING] = {
122 name = DM_QUANTSTRING_CURING,
123 psName = nil,
124 defaultColor = {0.6, 0.7, 0.8, 0.8},
125 dmi = DMI_CURING,
126 bUsesFightTable = false,
127 toggleQuant = DamageMeters_Quantity_CURINGFIGHT,
128 filterDefault = true,
129 titanAbbrev = DM_QUANTABBREV_CURING;
130 };
131 DM_QUANTDEFS[DamageMeters_Quantity_OVERHEAL] = {
132 name = DM_QUANTSTRING_OVERHEAL,
133 psName = nil,
134 defaultColor = {1.0, 1.0, 0.0, 0.8},
135 dmi = DMI_OVERHEAL,
136 bUsesFightTable = false,
137 filterDefault = true,
138 toggleQuant = DamageMeters_Quantity_OVERHEALFIGHT,
139 titanAbbrev = DM_QUANTABBREV_OVERHEAL;
140 };
141 DM_QUANTDEFS[DamageMeters_Quantity_ABSHEAL] = {
142 name = DM_QUANTSTRING_ABSHEAL,
143 psName = nil,
144 defaultColor = {1.0, 0.0, 1.0, 0.8},
145 dmi = DMI_ABSHEAL,
146 bUsesFightTable = false,
147 filterDefault = false,
148 toggleQuant = DamageMeters_Quantity_ABSHEALFIGHT,
149 titanAbbrev = DM_QUANTABBREV_ABSHEAL;
150 };
151  
152 DM_QUANTDEFS[DamageMeters_Quantity_DPS] = {
153 name = DM_QUANTSTRING_DAMAGEDONE_FIGHT,
154 psName = DM_QUANTSTRING_DAMAGEDONE_PS,
155 defaultColor = {0.8, 0.0, 0.0, 0.8},
156 dmi = DMI_DAMAGE,
157 bUsesFightTable = true,
158 toggleQuant = DamageMeters_Quantity_DAMAGE,
159 filterDefault = true,
160 titanAbbrev = DM_QUANTABBREV_DAMAGEDONE_PS;
161 };
162 DM_QUANTDEFS[DamageMeters_Quantity_HPS] = {
163 name = DM_QUANTSTRING_HEALINGDONE_FIGHT,
164 psName = DM_QUANTSTRING_HEALINGDONE_PS,
165 defaultColor = {0.0, 0.8, 0.0, 0.8},
166 dmi = DMI_HEALING,
167 bUsesFightTable = true,
168 toggleQuant = DamageMeters_Quantity_HEALING,
169 filterDefault = true,
170 titanAbbrev = DM_QUANTABBREV_HEALINGDONE_PS;
171 };
172 DM_QUANTDEFS[DamageMeters_Quantity_DTPS] = {
173 name = DM_QUANTSTRING_DAMAGETAKEN_FIGHT,
174 psName = DM_QUANTSTRING_DAMAGETAKEN_PS,
175 defaultColor = {1.0, 0.7, 0.2, 0.8},
176 dmi = DMI_DAMAGED,
177 bUsesFightTable = true,
178 toggleQuant = DamageMeters_Quantity_DAMAGED,
179 filterDefault = true,
180 titanAbbrev = DM_QUANTABBREV_DAMAGETAKEN_PS,
181 };
182 DM_QUANTDEFS[DamageMeters_Quantity_HTPS] = {
183 name = DM_QUANTSTRING_HEALINGTAKEN_FIGHT,
184 psName = DM_QUANTSTRING_HEALINGTAKEN_PS,
185 defaultColor = {0.0, 0.0, 0.8, 0.8},
186 dmi = DMI_HEALED,
187 bUsesFightTable = true,
188 toggleQuant = DamageMeters_Quantity_HEALINGRECEIVED,
189 filterDefault = true,
190 titanAbbrev = DM_QUANTABBREV_HEALINGTAKEN_PS,
191 };
192 DM_QUANTDEFS[DamageMeters_Quantity_CURINGFIGHT] = {
193 name = DM_QUANTSTRING_CURING_FIGHT,
194 psName = DM_QUANTSTRING_CURING_PS,
195 defaultColor = {0.4, 0.5, 0.6, 0.8},
196 dmi = DMI_CURING,
197 bUsesFightTable = true,
198 toggleQuant = DamageMeters_Quantity_CURING,
199 filterDefault = true,
200 titanAbbrev = DM_QUANTABBREV_CURING_PS,
201 };
202 DM_QUANTDEFS[DamageMeters_Quantity_OVERHEALFIGHT] = {
203 name = DM_QUANTSTRING_OVERHEAL_FIGHT,
204 psName = DM_QUANTSTRING_OVERHEAL_PS,
205 defaultColor = {0.4, 0.4, 0.0, 0.8},
206 dmi = DMI_OVERHEAL,
207 bUsesFightTable = true,
208 filterDefault = true,
209 toggleQuant = DamageMeters_Quantity_OVERHEALPERCENTAGE,
210 titanAbbrev = DM_QUANTABBREV_OVERHEAL_PS,
211 };
212 DM_QUANTDEFS[DamageMeters_Quantity_ABSHEALFIGHT] = {
213 name = DM_QUANTSTRING_ABSHEAL_FIGHT,
214 psName = DM_QUANTSTRING_ABSHEAL_PS,
215 defaultColor = {0.8, 0.0, 0.8, 0.8},
216 dmi = DMI_ABSHEAL,
217 bUsesFightTable = true,
218 filterDefault = false,
219 toggleQuant = DamageMeters_Quantity_ABSHEAL,
220 titanAbbrev = DM_QUANTABBREV_ABSHEAL_PS,
221 };
222  
223 DM_QUANTDEFS[DamageMeters_Quantity_TIME] = {
224 name = DM_QUANTSTRING_IDLETIME,
225 psName = nil,
226 defaultColor = {0.0, 0.4, 1.0, 0.8},
227 dmi = nil,
228 bUsesFightTable = false,
229 filterDefault = true,
230 pfnGetValue = function(playerStruct) return GetTime() - playerStruct.lastTime; end,
231 formatString = "%.1f",
232 titanAbbrev = DM_QUANTABBREV_IDLETIME;
233 };
234  
235 DM_QUANTDEFS[DamageMeters_Quantity_NETDAMAGE] = {
236 name = DM_QUANTSTRING_NETDAMAGE,
237 psName = nil,
238 defaultColor = {0.5, 0.0, 0.0, 0.8},
239 dmi = nil,
240 bUsesFightTable = false,
241 filterDefault = false,
242 pfnGetValue = function(playerStruct) return playerStruct.dmiData[DMI_DAMAGE].q - playerStruct.dmiData[DMI_DAMAGED].q; end,
243 titanAbbrev = DM_QUANTABBREV_NETDAMAGE;
244 };
245 DM_QUANTDEFS[DamageMeters_Quantity_NETHEALING] = {
246 name = DM_QUANTSTRING_NETHEALING,
247 psName = nil,
248 defaultColor = {0.0, 0.5, 0.0, 0.8},
249 dmi = nil,
250 bUsesFightTable = false,
251 filterDefault = false,
252 pfnGetValue = function(playerStruct) return playerStruct.dmiData[DMI_HEALING].q - playerStruct.dmiData[DMI_HEALED].q; end,
253 titanAbbrev = DM_QUANTABBREV_NETHEALING;
254 };
255 DM_QUANTDEFS[DamageMeters_Quantity_DAMAGEPLUSHEALING] = {
256 name = DM_QUANTSTRING_DAMAGEPLUSHEALING,
257 psName = nil,
258 defaultColor = {0.5, 0.5, 0.5, 0.8},
259 dmi = nil,
260 bUsesFightTable = false,
261 filterDefault = false,
262 pfnGetValue = function(playerStruct) return playerStruct.dmiData[DMI_DAMAGE].q + playerStruct.dmiData[DMI_HEALING].q; end,
263 titanAbbrev = DM_QUANTABBREV_DAMAGEPLUSHEALING;
264 };
265 DM_QUANTDEFS[DamageMeters_Quantity_HEALTH] = {
266 name = DM_QUANTSTRING_HEALTH;
267 psName = nil,
268 defaultColor = {0.5, 0.3, 0.7, 0.8},
269 dmi = nil,
270 bUsesFightTable = false,
271 filterDefault = false,
272 pfnGetValue = function(playerStruct) return playerStruct.health; end;
273 titanAbbrev = DM_QUANTABBREV_HEALTH;
274 };
275 DM_QUANTDEFS[DamageMeters_Quantity_OVERHEALPERCENTAGE] = {
276 name = DM_QUANTSTRING_OVERHEAL_PERCENTAGE,
277 psName = nil,
278 defaultColor = {0.5, 0.5, 0.7, 0.8},
279 dmi = nil,
280 bUsesFightTable = false,
281 filterDefault = false,
282 pfnGetValue = function(playerStruct) return floor(100 * DM_GetFraction(playerStruct.dmiData[DMI_OVERHEAL].q, playerStruct.dmiData[DMI_HEALING].q + playerStruct.dmiData[DMI_OVERHEAL].q)); end,
283 toggleQuant = DamageMeters_Quantity_OVERHEAL,
284 titanAbbrev = DM_QUANTABBREV_OVERHEAL_PERCENTAGE;
285 };
286 --[[
287 DM_QUANTDEFS[DamageMeters_Quantity_DANDERATING] = {
288 name = DM_QUANTSTRING_DANDERATING,
289 psName = nil,
290 defaultColor = {1.0, 0.6, 0.6, 0.8},
291 dmi = nil,
292 bUsesFightTable = false,
293 filterDefault = false,
294 pfnGetValue =
295 function(playerStruct)
296 local dmgIndex = ( DamageMeters_totalsTable[DMT_ACTIVE] ~= nil and
297 DamageMeters_totalsTable[DMT_ACTIVE][DMI_DAMAGE] ~= nil and
298 DamageMeters_totalsTable[DMT_ACTIVE][DMI_DAMAGE] > 0)
299 and
300 floor(playerStruct.dmiData[DMI_DAMAGE].q * 500.0 / DamageMeters_totalsTable[DMT_ACTIVE][DMI_DAMAGE])
301 or
302 0;
303 local healIndex = ( DamageMeters_totalsTable[DMT_ACTIVE] ~= nil and
304 DamageMeters_totalsTable[DMT_ACTIVE][DMI_HEALING] ~= nil and
305 DamageMeters_totalsTable[DMT_ACTIVE][DMI_HEALING] > 0)
306 and
307 floor(playerStruct.dmiData[DMI_HEALING].q * 500.0 / DamageMeters_totalsTable[DMT_ACTIVE][DMI_HEALING])
308 or
309 0;
310 return dmgIndex + healIndex;
311 end,
312 };
313 ]]--
314  
315 -------------
316 -- GLOBALS --
317 -------------
318 DamageMeters_rankTables = {};
319 DamageMeters_rankTables[DM_TABLE_A] = {};
320 DamageMeters_rankTables[DM_TABLE_B] = {};
321 DamageMeters_rankTables[DMT_FIGHT] = {};
322  
323 DamageMeters_totalsTable = {};
324 DamageMeters_totalsTable[DM_TABLE_A] = {};
325 DamageMeters_totalsTable[DM_TABLE_B] = {};
326 DamageMeters_totalsTable[DMT_FIGHT] = {};
327  
328 -------------------------------------------------
329 -- FUNCTIONS --
330 -------------------------------------------------
331  
332 function DamageMeters_IsQuantityFight(quantity)
333 if (DM_QUANTDEFS[quantity]) then
334 return DM_QUANTDEFS[quantity].bUsesFightTable;
335 end
336  
337 -- DMReporter can call this with plug-in quantities. In those cases the quantdefs aren't filled in.
338 -- This could be a problem if a plugin uses a quantity that isn't just a simple DMI.
339 return false;
340 end
341  
342 function DamageMeters_IsQuantityPS(quantity, forcePSFlag)
343 return (DamageMeters_flags[DMFLAG_showFightAsPS] or forcePSFlag) and (DM_QUANTDEFS[quantity].bUsesFightTable);
344 end
345  
346 function DamageMeters_GetQuantityDMI(quantity)
347 if (nil == quantity) then
348 DMPrintD("DamageMeters_GetQuantityDMI: quantity = nil");
349 end
350  
351 return DM_QUANTDEFS[quantity].dmi;
352 end
353  
354 -- This function is about as fast as I know how to make it. It doesn't protect
355 -- against invalid inputs at all.
356 function DamageMeters_GetQuantityValue(quantity, tableIndex, playerIndex)
357 local playerStruct = DamageMeters_tables[tableIndex][playerIndex];
358  
359 if (DamageMeters_Quantity_TIME == DamageMeters_quantity) then
360 return GetTime() - playerStruct.lastTime;
361 else
362 local quantDef = DM_QUANTDEFS[quantity];
363  
364 local value = 0;
365 if (nil ~= playerStruct) then
366 if (nil ~= quantDef.pfnGetValue) then
367 value = quantDef.pfnGetValue(playerStruct);
368 else
369 value = playerStruct.dmiData[quantDef.dmi].q;
370 end
371  
372 if (DamageMeters_IsQuantityPS(quantity)) then
373 value = DamageMeters_GetCombatValuePS(value);
374 end
375 end
376  
377 return value;
378 end
379 end
380  
381 -- This is a slow function. Not used much, I think.
382 function DamageMeters_GetQuantityValueString(quantity, playerName)
383  
384 local quantDef = DM_QUANTDEFS[quantity];
385 local tableIndex;
386 if (quantDef.bUsesFightTable) then
387 tableIndex = DMT_FIGHT;
388 else
389 tableIndex = DMT_ACTIVE;
390 end
391  
392 local playerIndex = DamageMeters_GetPlayerIndex(playerName, tableIndex);
393 if (playerIndex ~= nil) then
394 local value = DamageMeters_GetQuantityValue(quantity, tableIndex, playerIndex);
395  
396 local formatString;
397 if (quantDef.formatString) then
398 formatString = quantDef.formatString;
399 else
400 if (DamageMeters_IsQuantityPS(quantity)) then
401 formatString = "%0.1f";
402 else
403 formatString = "%d";
404 end
405 end
406  
407 return string.format(formatString, value);
408 else
409 return "-";
410 end
411 end
412  
413 function DamageMeters_GetQuantityString(quantity, forcePSFlag)
414 if (DamageMeters_IsQuantityPS(quantity, forcePSFlag)) then
415 return DM_QUANTDEFS[quantity].psName;
416 end
417  
418 return DM_QUANTDEFS[quantity].name;
419 end
420  
421 function DamageMeters_GetPlayerIndex(player, searchTableIndex)
422 if (nil == searchTableIndex) then
423 searchTableIndex = DMT_ACTIVE;
424 end
425  
426 local i;
427 for i = 1, table.getn(DamageMeters_tables[searchTableIndex]) do
428 if (DamageMeters_tables[searchTableIndex][i].player == player) then
429 return i;
430 end
431 end
432  
433 return nil;
434 end
435  
436 DMSortTemp_sortType = 1;
437 DMSortTemp_quantity = 1;
438 DMSortTemp_dmi = 1;
439  
440 -- This could be broken into two separate increasing/decreasing functions
441 -- for increased performance.
442 function DMSortFunc_Default(a,b)
443 local result;
444 if (a.dmiData[DMSortTemp_dmi].q == b.dmiData[DMSortTemp_dmi].q) then
445 if (DMSortTemp_sortType == DamageMeters_Sort_INCREASING) then
446 result = a.player > b.player;
447 else
448 result = a.player < b.player;
449 end
450 else
451 if (DMSortTemp_sortType == DamageMeters_Sort_INCREASING) then
452 result = a.dmiData[DMSortTemp_dmi].q < b.dmiData[DMSortTemp_dmi].q;
453 else
454 result = a.dmiData[DMSortTemp_dmi].q > b.dmiData[DMSortTemp_dmi].q;
455 end
456 end
457  
458 return result;
459 end
460  
461 function DMSortFunc_Alphabetical(a, b)
462 return a.player < b.player;
463 end
464  
465 function DMSortFunc_Time(a, b)
466 local result;
467 if (a.lastTime == b.lastTime) then
468 result = a.player < b.player;
469 else
470 result = a.lastTime < b.lastTime;
471 end
472  
473 if (DMSortTemp_sortType == DamageMeters_Sort_INCREASING) then
474 result = not result;
475 end
476 return result;
477 end
478  
479 function DMSortFunc_Custom(a, b)
480 local valfunc = DM_QUANTDEFS[DMSortTemp_quantity].pfnGetValue;
481 local aValue = valfunc(a);
482 local bValue = valfunc(b);
483 local result;
484 if (aValue == bValue) then
485 if (DMSortTemp_sortType == DamageMeters_Sort_INCREASING) then
486 result = a.player > b.player;
487 else
488 result = a.player < b.player;
489 end
490 else
491 if (DMSortTemp_sortType == DamageMeters_Sort_INCREASING) then
492 result = aValue < bValue;
493 else
494 result = aValue > bValue;
495 end
496 end
497  
498 return result;
499 end
500  
501 -- In the case of a tie, sort alphabetically.
502 function DamageMeters_DoSort(tableToSort, quantity, sortType)
503 if (DM_Bypass["Sort"] == true) then
504 return;
505 end
506  
507 if (nil == sortType) then
508 sortType = DamageMeters_sort;
509 end
510 DMSortTemp_sortType = sortType;
511 DMSortTemp_quantity = quantity;
512 DMSortTemp_dmi = DM_QUANTDEFS[quantity].dmi;
513  
514 if (DamageMeters_Sort_ALPHABETICAL == sortType) then
515 table.sort(tableToSort, DMSortFunc_Alphabetical);
516 return;
517 end
518  
519 if (quantity == DamageMeters_Quantity_TIME) then
520 table.sort(tableToSort, DMSortFunc_Time);
521 return;
522 elseif (DM_QUANTDEFS[quantity].pfnGetValue ~= nil) then
523 table.sort(tableToSort, DMSortFunc_Custom);
524 return;
525 else
526 DMSortTemp_quantity = DamageMeters_GetQuantityDMI(quantity);
527 table.sort(tableToSort, DMSortFunc_Default);
528 return;
529 end
530 end
531  
532 -- No reason to believe this doesn't work but it has never been used.
533 --[[
534 function DamageMeters_BuildSortHash(sortType)
535 DamageMeters_tableSortHash = {};
536  
537 -- Build the unsorted hash.
538 local quant;
539 for quant = 1, DamageMeters_Quantity_MAX do
540 DamageMeters_tableSortHash[quant] = {};
541  
542 local quantTable = (DM_QUANTDEFS[quant].bUsesFightTable) and DMT_FIGHT or DMT_ACTIVE;
543  
544 -- Populate the table for this quantity.
545 local playerIndex;
546 for playerIndex = 1, table.getn(DamageMeters_tables[quantTable]) do
547 DamageMeters_tableSortHash[quant][playerIndex] = {};
548 DamageMeters_tableSortHash[quant][playerIndex].playerName = DamageMeters_tables[quantTable][playerIndex].player;
549 DamageMeters_tableSortHash[quant][playerIndex].playerIndex = playerIndex;
550 DamageMeters_tableSortHash[quant][playerIndex].value = DamageMeters_GetQuantityValue(quant, quantTable, playerIndex);
551 end
552  
553 -- Sort this quantity.
554 table.sort(DamageMeters_tableSortHash[quant],
555 function(a,b)
556 if (a.value == b.value or DamageMeters_Sort_ALPHABETICAL == sortType) then
557 return a.playerName < b.playerName;
558 elseif (DamageMeters_Sort_INCREASING == sortType) then
559 return a.value > b.value;
560 else -- DamageMeters_Sort_DECREASING implied.
561 return a.value < b.value;
562 end
563 end
564 );
565 end
566 end
567 ]]--
568  
569  
570 DM_fakeTables = {};
571 DM_fakeTablesInitialized = false;
572  
573 function DamageMeters_InitFakeTables()
574 for ix = 1, 2 do
575 DM_fakeTables[ix] = {};
576 for index = 1, DamageMeters_TABLE_MAX do
577 DM_fakeTables[ix][index] = {};
578 DM_fakeTables[ix][index].player = nil;
579 DM_fakeTables[ix][index].lastTime = -1;
580 DM_fakeTables[ix][index].dmiData = {};
581 for dmi = 1, DMI_MAX do
582 DM_fakeTables[ix][index].dmiData[dmi] = {};
583 DM_fakeTables[ix][index].dmiData[dmi].q = 0;
584 end
585 end
586 end
587  
588 DM_fakeTablesInitialized = true;
589 end
590  
591  
592 -- The bRankQuantities parameter is a hack. Its only used by TitanDamageMeters.lua.
593 -- It will only work as expected if the tableIx isn't DMT_FIGHT already.
594 -- If true, this function will determine the ranks of -quantities-, not DMIs.
595 function DamageMeters_DetermineRanks(tableIx, bRankQuantities)
596 if (DM_Bypass["Determine Ranks"] == true) then
597 return;
598 end
599  
600 if (not DM_fakeTablesInitialized) then
601 DamageMeters_InitFakeTables();
602 end
603  
604 local index, playerStruct;
605 local indexMax = table.getn(DamageMeters_tables[tableIx]);
606 for index = 1, indexMax do
607 if (nil == DM_fakeTables[1][index]) then
608 DM_fakeTables[1][index] = {};
609 DM_fakeTables[1][index].dmiData = {};
610 for dmi = 1, DMI_MAX do
611 DM_fakeTables[1][index].dmiData[dmi] = {};
612 end
613 end
614  
615 DM_fakeTables[1][index].player = DamageMeters_tables[tableIx][index].player;
616 DM_fakeTables[1][index].lastTime = DamageMeters_tables[tableIx][index].lastTime;
617 local dmi;
618 for dmi = 1, DMI_MAX do
619 DM_fakeTables[1][index].dmiData[dmi].q = DamageMeters_tables[tableIx][index].dmiData[dmi].q;
620 end
621 end
622 for index = (indexMax + 1), DamageMeters_TABLE_MAX do
623 DM_fakeTables[1][index] = nil;
624 end
625  
626 if (bRankQuantities) then
627 local indexMax = table.getn(DamageMeters_tables[DMT_FIGHT]);
628 for index = 1, indexMax do
629 if (nil == DM_fakeTables[2][index]) then
630 DM_fakeTables[2][index] = {};
631 DM_fakeTables[2][index].dmiData = {};
632 for dmi = 1, DMI_MAX do
633 DM_fakeTables[2][index].dmiData[dmi] = {};
634 end
635 end
636  
637 DM_fakeTables[2][index].player = DamageMeters_tables[DMT_FIGHT][index].player;
638 DM_fakeTables[2][index].lastTime = DamageMeters_tables[DMT_FIGHT][index].lastTime;
639 local dmi;
640 for dmi = 1, DMI_MAX do
641 DM_fakeTables[2][index].dmiData[dmi].q = DamageMeters_tables[DMT_FIGHT][index].dmiData[dmi].q;
642 end
643 end
644 for index = (indexMax + 1), DamageMeters_TABLE_MAX do
645 DM_fakeTables[2][index] = nil;
646 end
647 end
648  
649 -----------
650 if (DM_Bypass["Determine Ranks 1"] == true) then
651 return;
652 end
653  
654 local count = table.getn(DamageMeters_tables[tableIx]);
655 local countPS = table.getn(DamageMeters_tables[DMT_FIGHT]);
656  
657 local quantity;
658 local max = bRankQuantities and DamageMeters_Quantity_MAX or DMI_MAX;
659 for quantity = 1, max do
660 local fakeTableToUseIndex;
661 local countToUse;
662 local tableIxToUse;
663 if (bRankQuantities and DamageMeters_IsQuantityPS(quantity)) then
664 fakeTableToUseIndex = 2;
665 countToUse = countPS;
666 tableIxToUse = DMT_FIGHT;
667 else
668 fakeTableToUseIndex = 1;
669 countToUse = count;
670 tableIxToUse = tableIx;
671 end
672  
673 DamageMeters_DoSort(DM_fakeTables[fakeTableToUseIndex], quantity, DamageMeters_Sort_DECREASING);
674  
675 -- Mark all players as unused in the rank table. We'll mark them used as we
676 -- need to in the next loop.
677 for playerName, struct in DamageMeters_rankTables[tableIxToUse] do
678 struct._used = false;
679 end
680  
681 local index;
682 for index = 1, countToUse do
683 local playerName = DM_fakeTables[fakeTableToUseIndex][index].player;
684 if (DamageMeters_rankTables[tableIxToUse][playerName] == nil) then
685 DamageMeters_rankTables[tableIxToUse][playerName] = {};
686 end
687 DamageMeters_rankTables[tableIxToUse][playerName][quantity] = index;
688 DamageMeters_rankTables[tableIxToUse][playerName]._used = true;
689 end
690  
691 -- Remove any players from the rank table we aren't using.
692 for playerName, struct in DamageMeters_rankTables[tableIxToUse] do
693 if (struct._used == false) then
694 DamageMeters_rankTables[tableIxToUse][playerName] = nil;
695 end
696 end
697 end
698 end
699  
700 function DamageMeters_DetermineTotals()
701 --DamageMeters_totalsTable = {};
702 DamageMeters_DetermineTotalsForATable(DMT_ACTIVE);
703 DamageMeters_DetermineTotalsForATable(DMT_FIGHT);
704 end
705  
706 function DamageMeters_DetermineTotalsForATable(tableIx)
707 --DamageMeters_totalsTable[tableIx] = {};
708 local dmi;
709 for dmi = 1, DMI_MAX do
710 DamageMeters_totalsTable[tableIx][dmi] = 0;
711 end
712  
713 local index, info;
714 for index,info in DamageMeters_tables[tableIx] do
715 for dmi = 1, DMI_MAX do
716 DamageMeters_totalsTable[tableIx][dmi] = DamageMeters_totalsTable[tableIx][dmi] + info.dmiData[dmi].q;
717 end
718 end
719 end
720  
721 function DamageMeters_DoReport(reportQuantity, destination, invert, start, count, tellTarget)
722 local msg;
723  
724 if (destination == "BUFFER") then
725 DamageMeters_reportBuffer = "";
726 elseif (destination == "PARTY") then
727 if (GetNumPartyMembers() == 0) then
728 DMPrint(DM_ERROR_NOPARTY);
729 return;
730 end
731 elseif (destination == "RAID") then
732 if (GetNumRaidMembers() == 0) then
733 DMPrint(DM_ERROR_NORAID);
734 return;
735 end
736 end
737  
738 local tableIx = DMT_ACTIVE;
739 if (DamageMeters_IsQuantityFight(reportQuantity)) then
740 tableIx = DMT_FIGHT;
741 end
742  
743 -- Determine bounds.
744 local finish = start + count - 1;
745 if (finish > table.getn(DamageMeters_tables[tableIx])) then
746 finish = table.getn(DamageMeters_tables[tableIx]);
747 end
748  
749 local step = 1;
750 if (invert) then
751 start = finish;
752 finish = 1;
753 step = -1;
754 end
755  
756 ------------
757 -- Header --
758 ------------
759 if (reportQuantity == DamageMeters_ReportQuantity_Total) then
760 msg = string.format(DM_MSG_FULLREPORTHEADER1, count, table.getn(DamageMeters_tables[tableIx]));
761 DamageMeters_SendReportMsg(msg, destination, tellTarget);
762 DamageMeters_SendReportMsg(DM_MSG_FULLREPORTHEADER2, destination, tellTarget);
763 msg = DM_MSG_FULLREPORTHEADER3;
764 elseif (reportQuantity == DamageMeters_ReportQuantity_Leaders) then
765 local header = string.format(DM_MSG_LEADERREPORTHEADER, count, table.getn(DamageMeters_tables[tableIx]));
766 local q;
767 for q = 1, DMI_REPORT_MAX do
768 header = string.format("%s| %-21s", header, DM_QUANTDEFS[q].name);
769 end
770 msg = header.."\n-------------------------------------------------------------------------------------------";
771 elseif (reportQuantity == DamageMeters_ReportQuantity_Events) then
772 msg = string.format(DM_MSG_EVENTREPORTHEADER, count, table.getn(DamageMeters_tables[tableIx]));
773 else
774 msg = string.format(DM_MSG_REPORTHEADER, DamageMeters_GetQuantityString(reportQuantity), count, table.getn(DamageMeters_tables[tableIx]));
775 end
776 DamageMeters_SendReportMsg(msg, destination, tellTarget);
777  
778 if (reportQuantity > 0) then
779 DamageMeters_DoSort(DamageMeters_tables[tableIx], reportQuantity);
780 end
781  
782 local reportQuantityDMI = nil;
783 if (reportQuantity > 0) then
784 reportQuantityDMI = DamageMeters_GetQuantityDMI(reportQuantity);
785 end
786  
787 -- Calculate totals.
788 local totalValue = 0;
789 local index;
790 local info;
791 local totals = {0, 0, 0, 0, 0, 0, 0};
792 if (reportQuantity > 0) then
793 if (DamageMeters_Quantity_TIME ~= reportQuantity) then
794 local playerIndex;
795 for playerIndex = 1, table.getn(DamageMeters_tables[tableIx]) do
796 totalValue = totalValue + DamageMeters_GetQuantityValue(reportQuantity, tableIx, playerIndex);
797 end
798 end
799 elseif (reportQuantity == DamageMeters_ReportQuantity_Total or
800 reportQuantity == DamageMeters_ReportQuantity_Leaders) then
801 for index,info in DamageMeters_tables[tableIx] do
802 totals[1] = totals[1] + info.dmiData[DMI_1].q;
803 totals[2] = totals[2] + info.dmiData[DMI_2].q;
804 totals[3] = totals[3] + info.dmiData[DMI_3].q;
805 totals[4] = totals[4] + info.dmiData[DMI_4].q;
806 totals[5] = totals[5] + info.dmiData[DMI_DAMAGE].hitCount;
807 totals[6] = totals[6] + info.dmiData[DMI_DAMAGE].critCount;
808 end
809  
810 DamageMeters_DetermineRanks(tableIx);
811 end
812  
813  
814 ---------------
815 -- Main Loop --
816 ---------------
817 local formatStrTotalMain_A = "%-12s %7d[%2d] %7d[%2d] %7d[%2d] %7d[%2d] %7d %7d";
818 local formatStrTotalTotals = "%-12s %11d %11d %11d %11d %7d %7d";
819 -- Careful here--if there isn't a space after each | it can lock up.
820 local formatStrTotalMain_B = "%2d| %8d %-12s| %8d %-12s| %8d %-12s| %8d %-12s";
821 local formatStrLeaderTotals= " =| %-20d | %-20d | %-20d | %-20d";
822 local currentTime = GetTime();
823 local i;
824 local showThis;
825 local visibleTotal = 0;
826 --DMPrintD("start = "..start..", finish = "..finish..", quantity = "..reportQuantity);
827 if (count > 0) then
828 for i = start, finish, step do
829 local value;
830 msg = "";
831  
832 if (reportQuantity == DamageMeters_ReportQuantity_Total) then
833 msg = string.format(formatStrTotalMain_A, DamageMeters_tables[tableIx][i].player,
834 DamageMeters_tables[tableIx][i].dmiData[DMI_1].q, DamageMeters_rankTables[tableIx][DamageMeters_tables[tableIx][i].player][1],
835 DamageMeters_tables[tableIx][i].dmiData[DMI_2].q, DamageMeters_rankTables[tableIx][DamageMeters_tables[tableIx][i].player][2],
836 DamageMeters_tables[tableIx][i].dmiData[DMI_3].q, DamageMeters_rankTables[tableIx][DamageMeters_tables[tableIx][i].player][3],
837 DamageMeters_tables[tableIx][i].dmiData[DMI_4].q, DamageMeters_rankTables[tableIx][DamageMeters_tables[tableIx][i].player][4],
838 DamageMeters_tables[tableIx][i].dmiData[DMI_DAMAGE].hitCount, DamageMeters_tables[tableIx][i].dmiData[DMI_DAMAGE].critCount);
839 elseif(reportQuantity == DamageMeters_ReportQuantity_Leaders) then
840 local leaders = {};
841 local leaderIndexes = {};
842 local qIx;
843 for qIx = 2, DMI_MAX do
844 local leaderName, rank;
845 for leaderName, rank in DamageMeters_rankTables[tableIx] do
846 --DMPrint(i.." "..leaderName.." "..rank[qIx]);
847 if (rank[qIx] == i) then
848 leaders[qIx] = leaderName;
849 leaderIndexes[qIx] = DamageMeters_GetPlayerIndex(leaderName);
850 break;
851 end
852 end
853 end
854  
855 msg = string.format(formatStrTotalMain_B, i,
856 DamageMeters_tables[tableIx][i].dmiData[DMI_1].q, DamageMeters_tables[tableIx][i].player,
857 DamageMeters_tables[tableIx][leaderIndexes[2]].dmiData[DMI_2].q, leaders[2],
858 DamageMeters_tables[tableIx][leaderIndexes[3]].dmiData[DMI_3].q, leaders[3],
859 DamageMeters_tables[tableIx][leaderIndexes[4]].dmiData[DMI_4].q, leaders[4]);
860 elseif (reportQuantity == DamageMeters_ReportQuantity_Events) then
861 DamageMeters_DumpPlayerEvents(DamageMeters_tables[tableIx][i].player, destination, true, i, totals);
862 else
863 if (DamageMeters_Quantity_TIME == reportQuantity) then
864 local idleTime = currentTime - DamageMeters_tables[tableIx][i].lastTime;
865 msg = string.format("#%.2d: %-16s %d:%.02d", i, DamageMeters_tables[tableIx][i].player, idleTime / 60, math.mod(idleTime, 60));
866 else
867 local value = DamageMeters_GetQuantityValue(reportQuantity, tableIx, i);
868 -- 4/2/06: Removed this check. Was never necessary, and now that some quantitites can be
869 -- negative its detrimental.
870 --if (value > 0) then
871 visibleTotal = visibleTotal + value;
872 if (DamageMeters_IsQuantityPS(reportQuantity)) then
873 msg = string.format("#%.2d: %-16s %.1f", i, DamageMeters_tables[tableIx][i].player, value);
874 else
875 local percentage = (totalValue > 0) and (100 * value / totalValue) or 0;
876 msg = string.format("#%.2d: %.2f%% %-16s %d", i, percentage, DamageMeters_tables[tableIx][i].player, value);
877 end
878 --end
879 end
880 end
881  
882 if (msg ~= "") then
883 DamageMeters_SendReportMsg(msg, destination, tellTarget);
884 end
885 end
886 end
887  
888 ------------
889 -- Totals --
890 ------------
891 if (reportQuantity == DamageMeters_ReportQuantity_Total or
892 reportQuantity == DamageMeters_ReportQuantity_Leaders) then
893  
894 if (reportQuantity == DamageMeters_ReportQuantity_Total) then
895 msg = string.format(formatStrTotalTotals, DM_MSG_TOTAL, totals[1], totals[2], totals[3], totals[4], totals[5], totals[6]);
896 else
897 msg = string.format(formatStrLeaderTotals, totals[1], totals[2], totals[3], totals[4], totals[5], totals[6]);
898 end
899 DamageMeters_SendReportMsg(msg, destination, tellTarget);
900  
901 -- Print a list of contributors.
902 if (DamageMeters_flags[DMFLAG_haveContributors]) then
903 msg = string.format(DM_MSG_COLLECTORS, UnitName("Player"));
904 for contrib, unused in DamageMeters_contributorList do
905 msg = msg..", "..contrib;
906 end
907 DamageMeters_SendReportMsg(msg, destination, tellTarget);
908 end
909 elseif (DamageMeters_Quantity_DPS == reportQuantity) then
910 msg = string.format(DM_MSG_COMBATDURATION, DamageMeters_combatEndTime - DamageMeters_combatStartTime);
911 DamageMeters_SendReportMsg(msg, destination, tellTarget);
912 end
913  
914 if (reportQuantity > 0) then
915 if (DamageMeters_Quantity_TIME == reportQuantity) then
916 -- No total.
917 elseif (DamageMeters_Quantity_DPS == reportQuantity) then
918 msg = string.format(DM_MSG_REPORTTOTALDPS, totalValue, visibleTotal);
919 DamageMeters_SendReportMsg(msg, destination, tellTarget);
920 else
921 msg = string.format(DM_MSG_REPORTTOTAL, totalValue, visibleTotal);
922 DamageMeters_SendReportMsg(msg, destination, tellTarget);
923 end
924 end
925 end
926  
927 function DamageMeters_DumpPlayerEvents(player, destination, bIncludeName, index, totals, singleQuantity)
928 local tellTarget = nil;
929  
930 local playerIndex = DamageMeters_GetPlayerIndex(player, DMT_VISIBLE);
931 --[[ removed when changed event system
932 local playerEventStruct = DamageMeters_tables[DMT_VISIBLE][playerIndex].events;
933 if (nil == playerEventStruct) then
934 -- Makes spam.
935 --DMPrintD("Player "..player.." not found.", nil, true);
936 return;
937 end
938 ]]--
939  
940 local prefix = "";
941 if (bIncludeName) then
942 if (index) then
943 str = index..": "..player..":";
944 else
945 str = player..":";
946 end
947 prefix = " ";
948 DamageMeters_SendReportMsg(str, destination, tellTarget);
949 end
950  
951 if (totals == nil) then
952 totals = {};
953 local ii;
954 for ii = 1, table.getn(DamageMeters_tables[DMT_VISIBLE]) do
955 local q;
956 for q = 1, DMI_MAX do
957 if (totals[q] == nil) then
958 totals[q] = 0;
959 end
960 totals[q] = totals[q] + DamageMeters_tables[DMT_VISIBLE][ii].dmiData[q].q;
961 end
962 end
963 end
964  
965 --[[ Remove firstMsg functionality. Pounds the GC.
966 if (DamageMeters_debugEnabled) then
967 if (playerIndex and DamageMeters_tables[DMT_VISIBLE][playerIndex].firstMsg and DamageMeters_tables[DMT_VISIBLE][playerIndex].firstMsg["event"]) then
968 local msg = "Last message: "..DamageMeters_tables[DMT_VISIBLE][playerIndex].firstMsg.event.." ("..DamageMeters_tables[DMT_VISIBLE][playerIndex].firstMsg.desc..")";
969 DamageMeters_SendReportMsg(msg, destination, tellTarget);
970 msg = " "..DamageMeters_tables[DMT_VISIBLE][playerIndex].firstMsg.fullMsg.."\n";
971 DamageMeters_SendReportMsg(msg, destination, tellTarget);
972 end
973 end
974 ]]--
975  
976 local headerDest = destination;
977 if (headerDest == "TOOLTIP") then
978 headerDest = "TOOLTIP_TITLE";
979 end
980  
981 local dmi, eventStruct;
982 if (singleQuantity) then
983 dmi = DM_QUANTDEFS[singleQuantity].dmi;
984 if (dmi) then
985 eventStruct = DamageMeters_tables[DMT_VISIBLE][playerIndex].dmiData[dmi].events;
986 if (eventStruct) then
987 DamageMeters_DumpQuantityEvents(player, dmi, eventStruct, totals, headerDest, destination, tellTarget, prefix);
988 end
989 end
990 else
991 for dmi = 1, DMI_MAX do
992 eventStruct = DamageMeters_tables[DMT_VISIBLE][playerIndex].dmiData[dmi].events;
993 if (eventStruct) then
994 DamageMeters_DumpQuantityEvents(player, dmi, eventStruct, totals, headerDest, destination, tellTarget, prefix);
995 end
996 end
997 end
998  
999 DamageMeters_SendReportMsg("", destination, tellTarget);
1000 end
1001  
1002 function DamageMeters_DumpQuantityEvents(player, dmi, eventStruct, totals, headerDest, destination, tellTarget, prefix)
1003 if (eventStruct == nil) then
1004 return;
1005 end
1006  
1007 -- Calculate total.
1008 local total = 0;
1009 for spell, spellStruct in eventStruct.spellTable do
1010 -- Spells with "*" at the end are duplicates, and are not to be counted towards the total.
1011 if (string.sub(spell, -1) ~= "*") then
1012 total = total + spellStruct.value;
1013 end
1014 end
1015 local percentageOfTotal = (totals[dmi] > 0) and (100 * total / totals[dmi]) or (0);
1016 local rank = (DamageMeters_rankTables[DMT_ACTIVE][player] and DamageMeters_rankTables[DMT_ACTIVE][player][dmi]) and " #"..tostring(DamageMeters_rankTables[DMT_ACTIVE][player][dmi]) or "";
1017 str = string.format("%s = %s (%.1f%%)%s", prefix..DMI_NAMES[dmi], total, percentageOfTotal, rank);
1018 DamageMeters_SendReportMsg(str, headerDest, tellTarget);
1019  
1020 -- Cannot sort spells, as that table is indexed by string.
1021 if (eventStruct.dirty) then
1022 DamageMeters_BuildSpellHash(eventStruct);
1023 end
1024  
1025 -- Print
1026 for hashIndex = 1, table.getn(eventStruct.hash) do
1027 local spell = eventStruct.hash[hashIndex].spell;
1028 spellStruct = eventStruct.spellTable[spell];
1029 --DMPrint("Hash "..hashIndex.." = "..spell);
1030  
1031 local percentageOfTotal = (total > 0) and (100 * spellStruct.value / total) or (0);
1032 local critPercentage = (spellStruct.counts[DM_HIT] > 0) and (100 * spellStruct.counts[DM_CRT] / spellStruct.counts[DM_HIT]) or 0.0;
1033 local average = string.format("%.1f Avg", (spellStruct.counts[DM_HIT] and spellStruct.value / spellStruct.counts[DM_HIT] or 0));
1034  
1035 str = string.format(prefix.." %s = %d (%.1f%%) %d/%d %.1f%% %s", spell, spellStruct.value, percentageOfTotal, spellStruct.counts[DM_CRT], spellStruct.counts[DM_HIT], critPercentage, average);
1036  
1037 -- Show resistance info.
1038 if (dmi == DMI_DAMAGED and
1039 (spellStruct.damageType <= DM_DMGTYPE_RESISTMAX or spellStruct.resistanceSum > 0)) then
1040 local avgResist = spellStruct.resistanceSum / spellStruct.resistanceCount;
1041 str = string.format("%s (%d %s)", str, avgResist, DM_DMGTYPENAMES[spellStruct.damageType]);
1042 end
1043  
1044 DamageMeters_SendReportMsg(str, destination, tellTarget);
1045 end
1046 end
1047  
1048 function DamageMeters_BuildSpellHash(eventStruct)
1049 if (DM_Bypass["BuildSpellHash"] == true) then
1050 return;
1051 end
1052  
1053 if (nil == eventStruct.hash) then
1054 eventStruct.hash = {};
1055 end
1056  
1057 local hashIndex = 1;
1058 for spell, spellStruct in eventStruct.spellTable do
1059 eventStruct.hash[hashIndex] = {};
1060 eventStruct.hash[hashIndex].spell = spell;
1061 eventStruct.hash[hashIndex].value = spellStruct.value;
1062 hashIndex = hashIndex + 1;
1063 end
1064  
1065 table.sort(eventStruct.hash,
1066 function(a,b)
1067 return a.value > b.value;
1068 end
1069 );
1070  
1071 eventStruct.dirty = false;
1072 end