vanilla-wow-addons – Blame information for rev 1

Subversion Repositories:
Rev:
Rev Author Line No. Line
1 office 1 --[[------------------------------------------------------------------------
2 DMREPORTER.LUA
3  
4 This file is designed to be executed by a Lua interpreter. For security
5 reasons, no executable is included with this package. However, Lua
6 executables are freely available on the interweb for all platforms that
7 WoW runs on:
8  
9 http://lua-users.org/wiki/LuaBinaries
10  
11 For example, on Windows you need to put Lua.exe into the DamageMeters
12 directory (or in the path, etc), go to the DamageMeters directory
13 in a console window, and enter:
14  
15 lua.exe DMReporter.lua
16  
17 ----
18  
19 As delivered, this file will create .txt and .csv files with various
20 reports based upon the data saved in your SavedVariables.lua file.
21 There is nothing hidden here, so if you desire a particular type of report
22 not included here and aren't afraid of a little Lua programming, why
23 not give it a shot?
24  
25 The Basics:
26 - You have access to the data: DamageMeters_tables.
27 - You can call some of the report functions that the mod uses, ie. DamageMeters_DoReport.
28 - If you choose to use the built-in report functions, not that they will
29 call the function DamageMeters_SendReportMsg, located in this file.
30 - You have access to everything in in Localization.lua.
31  
32 ------------------------------------------------------------------------]]--
33 ------------------------------------------------------------------------
34  
35 -------------
36 -- OPTIONS --
37 -------------
38  
39 -- For convenience the following options below can be toggled to control
40 -- the output.
41  
42 if (nil == useSavedTable) then
43 --useSavedTable = true; -- Whether or not to use the main or memory table.
44 end
45  
46 outputFilenameBase = "DMReport"; -- The base filename for all generated files.
47 --useTimeStamp = true; -- Adds date and time to the filename.
48 useDateStamp = true; -- Adds date and time to the filename.
49 useSessionLabel = true; -- Adds the name of the session to the filename.
50 filenameSuffix = "" -- Added to the end of the filename.
51  
52 doTotalReport = true; -- Outputs a list of total values for each player.
53 doLeaderReport = true; -- Outputs lists of the leading players for each value.
54 doEventReport = true; -- For this to be meaningful, you need to run with event data collection.
55 createCSVFile = true; -- Outputs the table to a CSV file (for reading by Excel).
56 createCSVEventFile = true; -- Outputs events to a CSV file.
57 createClassCSVFile = true; -- Outputs class-by-class summary to a CSV file.
58 createPercentagesCSVFile = true; -- Outputs percentage summary to a CSV file.
59 createEventTotalsCSVFile = true; -- Outputs event-totals CSV file.
60  
61 --createWinrarArchive = true; -- Uses WinRAR (assuming you have it installed) to zip all created reports.
62  
63 -----------
64 -- FILES --
65 -----------
66 dofile("DMReporter_settings.lua");
67 dofile("DamageMeters_Globals.lua");
68 dofile(savedVariablesPath);
69 dofile("localization.lua");
70 dofile("DamageMeters_Report.lua");
71  
72 ------------------------------------------------------------------------
73 -- FUNCTIONS --
74 ------------------------------------------------------------------------
75  
76 -- Placeholders for built-in WOW functions.
77 function GetTime() return 0; end
78 function UnitName(unit) return "["..unit.."]"; end
79  
80 function DMPrint(msg, color, bSecondChatWindow)
81 io.write(msg.."\n");
82 end
83  
84 ------------------------------------------------------------------------
85  
86  
87 function DamageMeters_SendReportMsg(msg)
88 -- Send the message to the output file.
89 outputFileHandle:write(msg.."\n");
90 end
91  
92 ------------------------------------------------------------------------
93  
94 -- This is a convenient function for output the main table data to a CSV file.
95 function DMReporter_OutputToCSV(outputFilename)
96 -- Open the file.
97 outputFileHandle = io.open(outputFilename, "w+");
98 if (nil == outputFileHandle) then
99 io.write("Error opening "..outputFilename);
100 return;
101 end
102 io.write("(CSV) Reporting to "..outputFilename.."...\n");
103  
104 -- Generate header string.
105 local msg, quant;
106 msg = "Player,Class";
107 for quant = 1, DMI_REPORT_MAX do
108 msg = msg..","..DM_QUANTDEFS[quant].name..",Hits,Crits";
109 end
110 msg = msg..",,Net Dmg,Net Healing";
111 DamageMeters_SendReportMsg(msg);
112  
113 -- Generate per-player strings.
114 local index;
115 for index = 1, table.getn(DamageMeters_tables[DMT_ACTIVE]) do
116 local struct = DamageMeters_tables[DMT_ACTIVE][index];
117 msg = struct.player..",";
118 if (struct.class) then
119 msg = msg..struct.class;
120 end
121  
122 local quant;
123 for quant = 1, DMI_REPORT_MAX do
124 msg = msg..","..struct.dmiData[quant].q..","..struct.dmiData[quant].hitCount..","..struct.dmiData[quant].critCount;
125 end
126  
127 local netDamage = struct.dmiData[DMI_DAMAGE].q - struct.dmiData[DMI_DAMAGED].q;
128 local netHealing = struct.dmiData[DMI_HEALING].q - struct.dmiData[DMI_HEALED].q;
129 msg = msg..",,"..netDamage..","..netHealing
130  
131 DamageMeters_SendReportMsg(msg);
132 end
133  
134 -- Close the file.
135 outputFileHandle:close();
136 end
137  
138 function DMReporter_OutputEventsToCSV(outputFilename)
139 -- Open the file.
140 outputFileHandle = io.open(outputFilename, "w+");
141 if (nil == outputFileHandle) then
142 io.write("Error opening "..outputFilename);
143 return;
144 end
145 io.write("(CSV Events) Reporting to "..outputFilename.."...\n");
146  
147 -- Generate header string.
148 local msg, quant;
149 msg = "Player,Class,Quantity,Event,Total,Hits,Crits,Crit Pct,Avg,Pct of Player,Pct of Group,Dmg Type,Avg Res.";
150 DamageMeters_SendReportMsg(msg);
151  
152 groupTotals = {};
153 local ii;
154 for ii = 1, table.getn(DamageMeters_tables[DMT_ACTIVE]) do
155 local q;
156 for q = 1, DMI_MAX do
157 if (groupTotals[q] == nil) then
158 groupTotals[q] = 0;
159 end
160 groupTotals[q] = groupTotals[q] + DamageMeters_tables[DMT_ACTIVE][ii].dmiData[q].q;
161 end
162 end
163  
164 -- Generate per-event strings.
165 local struct, player, playerStruct, quantity, quantityStruct, spell, spellStruct;
166 for index, struct in DamageMeters_tables[DMT_ACTIVE] do
167 player = struct.player;
168 for dmi = 1, DMI_MAX do
169 quantityStruct = struct.dmiData[dmi].events;
170 if (quantityStruct) then
171 local playerClass = "";
172 local fr = "";
173 local playerIndex = DamageMeters_GetPlayerIndex(player);
174 if (playerIndex and playerIndex > 0) then
175 playerClass = DamageMeters_tables[DMT_ACTIVE][playerIndex].class;
176 end
177 if (nil == playerClass) then
178 playerClass = "";
179 end
180  
181 local quantityTotal = 0;
182 for spell, spellStruct in quantityStruct.spellTable do
183 if (string.sub(spell, -1) ~= "*") then
184 quantityTotal = quantityTotal + spellStruct.value;
185 end
186 end
187  
188 for spell, spellStruct in quantityStruct.spellTable do
189 local average = DM_GetFraction(spellStruct.value, spellStruct.counts[1]);
190 local pctCrit = 100 * DM_GetFraction(spellStruct.counts[2], spellStruct.counts[1]);
191 local pctOfPlayer = 100 * DM_GetFraction(spellStruct.value, quantityTotal);
192 local pctOfGroup = 100 * DM_GetFraction(spellStruct.value, groupTotals[dmi]);
193 local avgRes = "";
194 if (spellStruct.resistanceCount > 0) then
195 avgRes = string.format("%.1f", DM_GetFraction(spellStruct.resistanceSum, spellStruct.resistanceCount));
196 end
197  
198 msg = string.format("%s,%s,%s,%s,%d,%d,%.1f,%d,%.1f,%.1f,%.1f,%s,%s",
199 player,
200 playerClass,
201 DMI_NAMES[dmi],
202 spell,
203 spellStruct.value,
204 spellStruct.counts[1],
205 spellStruct.counts[2],
206 pctCrit,
207 average,
208 pctOfPlayer,
209 pctOfGroup,
210 (DM_DMGTYPE_DEFAULT == spellStruct.damageType) and "" or DM_DMGTYPENAMES[spellStruct.damageType],
211 avgRes);
212 DamageMeters_SendReportMsg(msg);
213 end
214 end
215 end
216 end
217  
218 -- Close the file.
219 outputFileHandle:close();
220 end
221  
222 function DMReporter_OutputEventsTotalsCSV(outputFilename)
223 -- Open the file.
224 outputFileHandle = io.open(outputFilename, "w+");
225 if (nil == outputFileHandle) then
226 io.write("Error opening "..outputFilename);
227 return;
228 end
229 io.write("(CSV Events) Reporting to "..outputFilename.."...\n");
230  
231 local spellTable = {};
232 local quantityTotals = {};
233 for quantity = 1, DMI_MAX do
234 spellTable[quantity] = {};
235 quantityTotals[quantity] = 0;
236 end
237  
238 -- Add up all the spells.
239 local index, player, playerStruct, quantity, quantityStruct, spell, spellStruct;
240 for index, playerStruct in DamageMeters_tables[DMT_ACTIVE] do
241 for quantity = 1, DMI_MAX do
242 eventStruct = playerStruct.dmiData[quantity].events;
243 if (eventStruct) then
244 for spell, spellStruct in eventStruct.spellTable do
245 if (string.sub(spell, -1) ~= "*") then
246 if (nil == spellTable[quantity][spell]) then
247 spellTable[quantity][spell] = {};
248 spellTable[quantity][spell].players = 0;
249 spellTable[quantity][spell].value = 0;
250 spellTable[quantity][spell].counts = {};
251 spellTable[quantity][spell].counts[1] = 0;
252 spellTable[quantity][spell].counts[2] = 0;
253 end
254 spellTable[quantity][spell].players = spellTable[quantity][spell].players + 1;
255 spellTable[quantity][spell].value = spellTable[quantity][spell].value + spellStruct.value;
256 spellTable[quantity][spell].counts[1] = spellTable[quantity][spell].counts[1] + spellStruct.counts[1];
257 spellTable[quantity][spell].counts[2] = spellTable[quantity][spell].counts[2] + spellStruct.counts[2];
258  
259 quantityTotals[quantity] = quantityTotals[quantity] + spellStruct.value;
260 end
261 end
262 end
263 end
264 end
265  
266 local msg = "Quantity,Spell,Players,Total,Hits,Crits,Average,Pct. Of Total";
267 DamageMeters_SendReportMsg(msg);
268  
269 for quantity = 1, DMI_MAX do
270 local quantityStruct = spellTable[quantity];
271 for spell, spellStruct in quantityStruct do
272 local average = DM_GetFraction(spellStruct.value, spellStruct.counts[1]);
273 local percent = 100 * DM_GetFraction(spellStruct.value, quantityTotals[quantity]);
274  
275 local msg = string.format("%s,%s,%d,%d,%d,%d,%.1f,%.1f",
276 DMI_NAMES[quantity],
277 spell,
278 spellStruct.players,
279 spellStruct.value,
280 spellStruct.counts[1],
281 spellStruct.counts[2],
282 average,
283 percent);
284 DamageMeters_SendReportMsg(msg);
285 end
286 end
287  
288 -- Close the file.
289 outputFileHandle:close();
290 end
291  
292 function DMReporter_OuptutClassReportCSV(outputFilename)
293 -- Open the file.
294 outputFileHandle = io.open(outputFilename, "w+");
295 if (nil == outputFileHandle) then
296 io.write("Error opening "..outputFilename);
297 return;
298 end
299 io.write("(CSV Class Report) Reporting to "..outputFilename.."...\n");
300  
301 --------------------
302  
303 local classTable = {};
304  
305 local ix;
306 local class, classInfo;
307 for ix = 1, table.getn(DamageMeters_tables[DMT_ACTIVE]) do
308 local playerInfo = DamageMeters_tables[DMT_ACTIVE][ix];
309 if (playerInfo.class and playerInfo.class ~= "") then
310 class = DamageMeters_tables[DMT_ACTIVE][ix].class;
311 if (classTable[class] == nil) then
312 classTable[class] = {};
313 classTable[class].count = 0;
314 classTable[class].quants = {0, 0, 0, 0};
315 end
316 classTable[class].count = classTable[class].count + 1;
317  
318 local quant;
319 for quant = 1, DMI_REPORT_MAX do
320 classTable[class].quants[quant] = classTable[class].quants[quant] + playerInfo.dmiData[quant].q;
321 end
322 end
323 end
324  
325  
326 -- Generate header string.
327 local msg, quant;
328 msg = "Class,Count";
329 for quant = 1, DMI_REPORT_MAX do
330 msg = msg..","..DM_QUANTDEFS[quant].name..",Avg "..DM_QUANTDEFS[quant].name;
331 end
332 DamageMeters_SendReportMsg(msg);
333  
334 for class, classInfo in classTable do
335 local msg = string.format("%s,%d,%d,%d,%d,%d,%d,%d,%d,%d", class, classInfo.count,
336 classInfo.quants[1], DM_GetFraction(classInfo.quants[1],classInfo.count),
337 classInfo.quants[2], DM_GetFraction(classInfo.quants[2],classInfo.count),
338 classInfo.quants[3], DM_GetFraction(classInfo.quants[3],classInfo.count),
339 classInfo.quants[4], DM_GetFraction(classInfo.quants[4],classInfo.count));
340 DamageMeters_SendReportMsg(msg);
341 end
342  
343 -- Close the file.
344 outputFileHandle:close();
345 end
346  
347 function DMReporter_OutputPercentagesReportCSV(outputFilename)
348 -- Open the file.
349 outputFileHandle = io.open(outputFilename, "w+");
350 if (nil == outputFileHandle) then
351 io.write("Error opening "..outputFilename);
352 return;
353 end
354 io.write("(CSV Class Report) Reporting to "..outputFilename.."...\n");
355  
356 --------------------
357  
358 local msg = "Player,Class"
359 for ii = 1, DMI_REPORT_MAX do
360 msg = string.format("%s,%s,Pct Of Total,Pct Of Leader",
361 msg, DM_QUANTDEFS[ii].name);
362 end
363 DamageMeters_SendReportMsg(msg);
364  
365 -- Calculate totals.
366 local index;
367 local info;
368 local totals = {0, 0, 0, 0, 0, 0};
369 local peaks = {0, 0, 0, 0};
370 for index,info in DamageMeters_tables[DMT_ACTIVE] do
371 local ii;
372 for ii = 1, DMI_REPORT_MAX do
373 msg = string.format(",%s,%s,Pct Of Total,Pct Of Leader",
374 msg, DM_QUANTDEFS[ii].name);
375 totals[ii] = totals[ii] + info.dmiData[ii].q;
376 if (info.dmiData[ii].q > peaks[ii]) then
377 peaks[ii] = info.dmiData[ii].q;
378 end
379 end
380 totals[5] = totals[5] + info.dmiData[DMI_DAMAGE].hitCount;
381 totals[6] = totals[6] + info.dmiData[DMI_DAMAGE].critCount;
382 end
383 DamageMeters_DetermineRanks(DMT_ACTIVE);
384  
385 for index = 1, table.getn(DamageMeters_tables[DMT_ACTIVE]) do
386 local struct = DamageMeters_tables[DMT_ACTIVE][index];
387  
388 local class = struct.class and struct.class or "";
389 msg = string.format("%s,%s", struct.player, class);
390  
391 for quant = 1, DMI_REPORT_MAX do
392 local percentOfTotal = 100 * DM_GetFraction(struct.dmiData[quant].q, totals[quant]);
393 local percentOfLeader = 100 * DM_GetFraction(struct.dmiData[quant].q, peaks[quant]);
394  
395 msg = string.format("%s,%d,%.2f,%.2f",
396 msg, struct.dmiData[quant].q, percentOfTotal, percentOfLeader);
397 end
398 DamageMeters_SendReportMsg(msg);
399 end
400  
401 -- Close the file.
402 outputFileHandle:close();
403 end
404  
405 ------------------------------------------------------------------------
406 -- REPORT CODE GOES HERE --
407 ------------------------------------------------------------------------
408  
409 -- See if we are using the "saved" (memory) table, rather than the main table.
410 if (useSavedTable) then
411 io.write("Using Saved Table: Switching active index from "..DMT_ACTIVE.." to ");
412 DMT_ACTIVE = (DMT_ACTIVE == DM_TABLE_B) and DM_TABLE_A or DM_TABLE_B;
413 io.write(DMT_ACTIVE.."\n");
414 end
415  
416 -- Sort functions use the visible table, so make sure it refers
417 -- to the active table, not the fight table.
418 DMT_VISIBLE = DMT_ACTIVE;
419  
420 -- Update DMI_MAX to reflect plugin data.
421 for plugin, dmi in DamageMeters_pluginDMITable do
422 if (dmi > DMI_MAX) then
423 DMI_NAMES[dmi] = plugin;
424 DMI_MAX = dmi;
425 end
426 end
427  
428 -- Verify that the table has data.
429 if (nil == DamageMeters_tables[DMT_ACTIVE] or 0 == table.getn(DamageMeters_tables[DMT_ACTIVE])) then
430 io.write("Table is empty.");
431 return;
432 end
433  
434 local filenameList = {};
435 local sessionLabel = "";
436 if (useSessionLabel) then
437 if (DamageMeters_tableInfo[DMT_VISIBLE].sessionLabel) then
438 sessionLabel = "_"..DamageMeters_tableInfo[DMT_VISIBLE].sessionLabel;
439 end
440 end
441  
442 -- Build a string which contains the current date and time so that we can
443 -- uniquely name our report files.
444 local dateTimeString = "";
445 if (useDateStamp or useTimeStamp) then
446 local dateTable = os.date("*t");
447 if (useDateStamp) then
448 dateTimeString = string.format("_%d%02d%02d", dateTable.year, dateTable.month, dateTable.day);
449 end
450 if (useTimeStamp) then
451 dateTimeString = dateTimeString..string.format("_%02d%02d%02d", dateTable.hour, dateTable.min, dateTable.sec);
452 end
453 end
454  
455 local fullSuffix = dateTimeString..sessionLabel..filenameSuffix;
456  
457 if (doTotalReport or doLeaderReport or doEventReport) then
458  
459 -- Open a file for writing to.
460 outputFilename = outputFilenameBase..fullSuffix..".txt";
461 outputFileHandle = io.open(outputFilename, "w+");
462 if (nil == outputFileHandle) then
463 io.write("Error opening "..outputFilename);
464 return;
465 end
466 table.insert(filenameList, outputFilename);
467 io.write("Writing report to "..outputFilename.."...\n");
468  
469 ----------------
470  
471 -- Total Report:
472 if (doTotalReport) then
473 DamageMeters_DoReport(DamageMeters_ReportQuantity_Total, "BUFFER", false, 1, table.getn(DamageMeters_tables[DMT_ACTIVE]), nil);
474 DamageMeters_SendReportMsg("\n");
475 end
476  
477 -- Leader Report:
478 if (doLeaderReport) then
479 DamageMeters_DoReport(DamageMeters_ReportQuantity_Leaders, "BUFFER", false, 1, table.getn(DamageMeters_tables[DMT_ACTIVE]), nil);
480 DamageMeters_SendReportMsg("\n");
481 end
482  
483 -- Event Report:
484 if (doEventReport) then
485 DamageMeters_DoReport(DamageMeters_ReportQuantity_Events, "BUFFER", false, 1, table.getn(DamageMeters_tables[DMT_ACTIVE]), nil);
486 DamageMeters_SendReportMsg("\n");
487 end
488  
489 -- Close the file.
490 outputFileHandle:close();
491 end
492  
493 -- Dump table to a CSV file.
494 if (createCSVFile) then
495 outputFilename = outputFilenameBase..fullSuffix..".csv";
496 table.insert(filenameList, outputFilename);
497 DMReporter_OutputToCSV(outputFilename);
498 end
499  
500 -- Dump event table to a CSV file.
501 if (createCSVEventFile) then
502 outputFilename = outputFilenameBase.."_Events"..fullSuffix..".csv";
503 table.insert(filenameList, outputFilename);
504 DMReporter_OutputEventsToCSV(outputFilename);
505 end
506  
507 -- Dump Class Info to a CSV file.
508 if (createClassCSVFile) then
509 outputFilename = outputFilenameBase.."_Class"..fullSuffix..".csv";
510 table.insert(filenameList, outputFilename);
511 DMReporter_OuptutClassReportCSV(outputFilename);
512 end
513  
514 -- Dump Percentage Info to a CSV file
515 if (createPercentagesCSVFile) then
516 outputFilename = outputFilenameBase.."_Percentages"..fullSuffix..".csv";
517 table.insert(filenameList, outputFilename);
518 DMReporter_OutputPercentagesReportCSV(outputFilename);
519 end
520  
521 -- Dump event-totals to a CSV file.
522 if (createEventTotalsCSVFile) then
523 outputFilename = outputFilenameBase.."_EventTotals"..fullSuffix..".csv";
524 table.insert(filenameList, outputFilename);
525 DMReporter_OutputEventsTotalsCSV(outputFilename);
526 end
527  
528  
529 ---------------------------------
530  
531 if (createWinrarArchive) then
532 local archiveName = outputFilenameBase..fullSuffix..".zip";
533 local commandLine = "start winrar.exe a -df -- "..archiveName;
534 for index, filename in filenameList do
535 commandLine = commandLine.." "..filename;
536 end
537 io.write("Creating archive using command line:\n");
538 io.write(commandLine.."\n");
539 os.execute(commandLine);
540 end
541  
542 ---------------------------------
543  
544 if (nil ~= DM_eventCaseTableDebug) then
545 outputFileHandle = io.open("DM_eventCaseTableDebug.txt", "w+");
546  
547 outputFileHandle:write("Unparsed Messages in EventCaseTable:\n");
548 for msgType, msgTable in DM_eventCaseTableDebug do
549 outputFileHandle:write(" "..msgType.."\n");
550 for event, eventTable in msgTable do
551 --DM_DUMP_TABLE(eventTable);
552  
553 outputFileHandle:write(" "..event.." ("..eventTable.parseCount.."/"..eventTable.hitCount..")\n");
554 for caseIndex, case in eventTable do
555 if (type(caseIndex) == "number") then
556 outputFileHandle:write(" "..case.n.." ("..case.hitCount..")\n");
557 if (nil ~= case.parsed) then
558 for parseIx, parse in case.parsed do
559 outputFileHandle:write(" "..parse.."\n");
560 end
561 end
562 end
563 end
564  
565 if (eventTable.unparsed ~= nil) then
566 outputFileHandle:write(" [Unparsed]:\n");
567 for index, message in eventTable.unparsed do
568 outputFileHandle:write(" "..message.."\n");
569 end
570 end
571 end
572 end
573  
574 io.close(outputFileHandle);
575 end
576  
577 ---------------------------------