comparison mleqdkp.lua @ 10:67b8537e8432

More work on ML/EQDKP generator and its spawned subprojects.
author Farmbuyer of US-Kilrogg <farmbuyer@gmail.com>
date Tue, 28 Jun 2011 07:36:26 +0000
parents 822b6ca3ef89
children 40ff9c63badc
comparison
equal deleted inserted replaced
9:4fba9c6b5d3d 10:67b8537e8432
1 if UnitName"player" ~= "Farmbuyer" then return end 1 -- This file is one gigantic exercise in abusing the garbage collector via
2 -- string manipulation. A real XML-handling library would be cleaner, alas,
3 -- we can't load third-party .so/.dll inside the WoW client. Life is hard.
4
2 local addon = select(2,...) 5 local addon = select(2,...)
6 local pairs, ipairs, tinsert, tremove, tconcat = pairs, ipairs, table.insert, table.remove, table.concat
7 local tostring, tonumber = tostring, tonumber
8
9 local banner_formatted = "Formatted version (scroll down for unformatted):"
10 local banner_unformatted = "Unformatted version:"
11 local banner_sep = "==========================="
3 12
4 -- We keep some local state bundled up rather than trying to pass it around 13 -- We keep some local state bundled up rather than trying to pass it around
5 -- as paramters (which would have entailed creating a ton of closures). 14 -- as paramters (which would have entailed creating a ton of closures).
6 local state = {} 15 local state = {}
7 local tag_lookup_handlers = {} 16 local tag_lookup_handlers = {}
8 local do_tag_lookup_handler 17 local do_tag_lookup_handler
9 18
10 19
11 --[[ 20 --[[
12 This is taken from CT_RaidTracker 1.7.32, reconstructing the output from 21 This is based on CT_RaidTracker 1.7.32, reconstructing the output from
13 code inspection. No official format documents are available on the web 22 code inspection. No official format documents are available on the web
14 without downloading and installing the EQDKP webserver package. Bah. 23 without downloading and installing the EQDKP webserver package. Bah.
15 24
16 Case of tag names shouldn't matter, but is preserved here from CT_RaidTracker 25 Case of tag names shouldn't matter, but is preserved here from CT_RaidTracker
17 code for comparison of generated output. 26 code for comparison of generated output.
29 <RaidInfo> 38 <RaidInfo>
30 <Version>1.4</Version> {In live output, this is missing due to scoping bug in ct_raidtracker.lua:3471} 39 <Version>1.4</Version> {In live output, this is missing due to scoping bug in ct_raidtracker.lua:3471}
31 <key>$TIMESTAMP</key> 40 <key>$TIMESTAMP</key>
32 <realm>$REALM</realm> 41 <realm>$REALM</realm>
33 <start>$TIMESTAMP</start> {Same as the key, apparently?} 42 <start>$TIMESTAMP</start> {Same as the key, apparently?}
34 <end>$ENDTIME</end> {Set by the "end the raid" command in the raid tracker, here just the final entry time} 43 <end>$ENDTIME</end> {Set by the "end the raid" command in CTRT, here it is just the final entry time}
35 <zone>$ZONE</zone> {may be optional. first one on the list in case of multiple zones?} 44 <zone>$ZONE</zone> {may be optional. first one on the list in case of multiple zones?}
36 {<difficulty>$DIFFICULTY</difficulty> {this scales badly in places like ICC. may be optional?}} 45 {<difficulty>$DIFFICULTY</difficulty> {this scales badly in places like ICC. may be optional?}}
37 46
38 {<PlayerInfos>... {guh.}} 47 <PlayerInfos>
48 $PLAYER_INFOS
49 </PlayerInfos>
39 50
40 <BossKills> 51 <BossKills>
41 $BOSS_KILLS 52 $BOSS_KILLS
42 </BossKills> 53 </BossKills>
43 54
44 {<Wipes> bleh} 55 <Wipes>
45 {<NextBoss>Baron Steamroller</NextBoss> {only one "next boss" for the whole raid event? huh?}} 56 $WIPES
57 </Wipes>
58 {<NextBoss>Baron Steamroller</NextBoss> {only one "next boss" for the whole
59 raid event? presumably where to pick up next time?}}
46 60
47 <note><![CDATA[$RAIDNOTE - Zone: $ZONE]]></note> 61 <note><![CDATA[$RAIDNOTE - Zone: $ZONE]]></note>
48 62
49 {<Join>...</Join><Leave>...</Leave> {specific timestamps per player. meh.}} 63 <Join>
64 $JOINS
65 </Join>
66 <Leave>
67 $LEAVES
68 </Leave>
50 69
51 <Loot> 70 <Loot>
52 $PHAT_LEWTS 71 $PHAT_LEWTS
53 </Loot> 72 </Loot>
54 </RaidInfo>]====]):gsub('%b{}', "") 73 </RaidInfo>]====]):gsub('%b{}', "")
55 74
56 --[[ 75 --[[
57 See the loot markup, next block. 76 See the loot markup below.
58 ]] 77 ]]
59 local boss_kills_xml = ([====[ 78 local boss_kills_xml = ([====[
60 <key$N> 79 <key$N>
61 <name>$BOSS_NAME</name> 80 <name>$BOSS_NAME</name>
62 <time>$BOSS_TIME</time> 81 <time>$BOSS_TIME</time>
76 return do_tag_lookup_handler (state.index, state.entry, tag) or 'NYI' 95 return do_tag_lookup_handler (state.index, state.entry, tag) or 'NYI'
77 end 96 end
78 end 97 end
79 98
80 --[[ 99 --[[
100 Handles the PlayerInfo, Join, and Leave tags.
101 ]]
102 local joinleave_xml
103 local player_info_xml = ([====[
104 <key$N>
105 $PLAYER_GRUNTWORK
106 </key$N>
107 ]====]):gsub('%b{}', "")
108
109 local function player_info_tag_lookup (tag)
110 if tag == 'N' then
111 return tostring(state.key)
112 elseif tag == 'NAME' then
113 return state.index
114 elseif tag == 'TIME' then
115 return state.time
116 end
117 local ltag = tag:lower()
118 if state.entry[ltag] then
119 -- handles race, guild, sex, class, level
120 return state.entry[ltag]
121 end
122 return "?"
123 end
124
125 do
126 local pi_xml_save = player_info_xml
127 local gruntwork_tags = {
128 "name", "race", "guild", "sex", "class", "level"
129 }
130 for i,tag in ipairs(gruntwork_tags) do
131 gruntwork_tags[i] = (" <%s>$%s</%s>"):format(tag,tag:upper(),tag)
132 end
133 player_info_xml = player_info_xml:gsub('$PLAYER_GRUNTWORK', table.concat(gruntwork_tags,'\n'))
134
135 -- The join/leave blocks use "player" instead of "name". They don't have a
136 -- guild tag, but they do have a time tag.
137 gruntwork_tags[1] = " <player>$NAME</player>"
138 gruntwork_tags[3] = " <time>$TIME</time>"
139 joinleave_xml = pi_xml_save:gsub('$PLAYER_GRUNTWORK', table.concat(gruntwork_tags,'\n'))
140 end
141
142 --[[
81 $N 1-based loop variable for key element 143 $N 1-based loop variable for key element
82 $ITEMNAME Without The Brackets 144 $ITEMNAME Without The Square Brackets of the Whale
83 $ITEMID Not the ID, actually a full itemstring without the leading "item:" 145 $ITEMID Not the numeric ID, actually a full itemstring without the leading "item:"
84 $ICON Last component of texture path? 146 $ICON Last component of texture path?
85 $CLASS,$SUBCLASS ItemType 147 $CLASS,$SUBCLASS ItemType
86 $COLOR GetItemQualityColor, full 8-digit string 148 $COLOR GetItemQualityColor, full 8-digit string
87 $COUNT,$BOSS,$ZONE, 149 $COUNT,$BOSS,$ZONE,
88 $PLAYER all self-explanatory 150 $PLAYER all self-explanatory
167 tag_lookup_handlers.COUNT = 229 tag_lookup_handlers.COUNT =
168 function (i, e) 230 function (i, e)
169 return e.count and e.count:sub(2) or "1" -- skip the leading "x" 231 return e.count and e.count:sub(2) or "1" -- skip the leading "x"
170 end 232 end
171 233
172 -- maybe combine these next two 234 -- should combine these next two
173 tag_lookup_handlers.BOSS = 235 tag_lookup_handlers.BOSS =
174 function (i, e) 236 function (i, e)
175 while i > 0 and state.loot[i].kind ~= 'boss' do 237 while i > 0 and state.loot[i].kind ~= 'boss' do
176 i = i - 1 238 i = i - 1
177 end 239 end
221 return h(index,entry) 283 return h(index,entry)
222 else 284 else
223 error(("MLDKP tag lookup (index %d) on tag %s with no handler"):format(index,tag)) 285 error(("MLDKP tag lookup (index %d) on tag %s with no handler"):format(index,tag))
224 end 286 end
225 end 287 end
288
226 289
227 local function generator (ttype, loot, last_printed, generated, cache) 290 local function generator (ttype, loot, last_printed, generated, cache)
228 -- Because it's XML, generated text is "grown" by shoving more crap into 291 -- Because it's XML, generated text is "grown" by shoving more crap into
229 -- the middle instead of appending to the end. Only easy way of doing that 292 -- the middle instead of appending to the end. Only easy way of doing that
230 -- here is regenerating it from scratch each time. 293 -- here is regenerating it from scratch each time.
278 all_lewts[#all_lewts+1] = lewt_template:gsub('%$([%w_]+)', 341 all_lewts[#all_lewts+1] = lewt_template:gsub('%$([%w_]+)',
279 phat_lewt_tag_lookup) 342 phat_lewt_tag_lookup)
280 state.key = state.key + 1 343 state.key = state.key + 1
281 end 344 end
282 345
283 text = text:gsub('$PHAT_LEWTS', table.concat(all_lewts, '\n')) 346 text = text:gsub('$PHAT_LEWTS', tconcat(all_lewts, '\n'))
284 end 347 end
285 348
286 -- Bosses 349 -- Player info, join times, leave times
287 do 350 do
288 local all_bosses = {} 351 local all_players, all_joins, all_leaves = {}, {}, {}
352 local player_template, joinleave_template = player_info_xml, joinleave_xml
353 local date = date
354
355 state.key = 1
356 if type(loot.raiders) == 'table' then for name,r in pairs(loot.raiders) do
357 state.index, state.entry = name, r
358 all_players[#all_players+1] = player_template:gsub('%$([%w_]+)', player_info_tag_lookup)
359 state.time = date ("%m/%d/%y %H:%M:00", r.join)
360 all_joins[#all_joins+1] = joinleave_template:gsub('%$([%w_]+)', player_info_tag_lookup)
361 state.time = date ("%m/%d/%y %H:%M:00", r.leave)
362 all_leaves[#all_leaves+1] = joinleave_template:gsub('%$([%w_]+)', player_info_tag_lookup)
363 state.key = state.key + 1
364 end end
365 text = text:gsub('$PLAYER_INFOS', tconcat(all_players, '\n'))
366 :gsub('$JOINS', tconcat(all_joins, '\n'))
367 :gsub('$LEAVES', tconcat(all_leaves, '\n'))
368 end
369
370 -- Bosses and wipes (does anybody really use the latter?)
371 do
372 local all_bosses, all_wipes = {}, {}
289 local boss_template = boss_kills_xml 373 local boss_template = boss_kills_xml
290 374
291 state.key = 1 375 state.key = 1
292 for i,e in addon:filtered_loot_iter('boss') do 376 for i,e in addon:filtered_loot_iter('boss') do
293 if e.reason == 'kill' then -- oh, for a 'continue' statement... 377 if e.reason == 'kill' then
294 state.index, state.entry = i, e 378 state.index, state.entry = i, e
295 all_bosses[#all_bosses+1] = boss_template:gsub('%$([%w_]+)', 379 all_bosses[#all_bosses+1] = boss_template:gsub('%$([%w_]+)',
296 boss_kills_tag_lookup) 380 boss_kills_tag_lookup)
297 state.key = state.key + 1 381 state.key = state.key + 1
382 elseif e.reason == 'wipe' then
383 all_wipes[#all_wipes+1] = ('<Wipe>%d</Wipe>'):format(e.stamp)
298 end 384 end
299 end 385 end
300 386
301 text = text:gsub('$BOSS_KILLS', table.concat(all_bosses, '\n')) 387 text = text:gsub('$BOSS_KILLS', tconcat(all_bosses, '\n'))
388 :gsub('$WIPES', tconcat(all_wipes, '\n'))
302 end 389 end
303 390
304 -- In addition to doing the top-level zone, this will also catch any 391 -- In addition to doing the top-level zone, this will also catch any
305 -- leftover $ZONE tags. There could be multiple places in the raid, so 392 -- leftover $ZONE tags. There could be multiple places in the raid, so
306 -- we default to the first one we saw. 393 -- we default to the first one we saw.
314 -- Misc 401 -- Misc
315 text = text:gsub('$REALM', (GetRealmName())) 402 text = text:gsub('$REALM', (GetRealmName()))
316 --text = text:gsub('$DIFFICULTY', ) 403 --text = text:gsub('$DIFFICULTY', )
317 text = text:gsub('$RAIDNOTE', "") 404 text = text:gsub('$RAIDNOTE', "")
318 405
319 cache[#cache+1] = "Formatted version (scroll down for unformatted):" 406 cache[#cache+1] = banner_formatted
320 cache[#cache+1] = "===========================" 407 cache[#cache+1] = banner_sep
321 cache[#cache+1] = text 408 cache[#cache+1] = text
322 cache[#cache+1] = '\n' 409 cache[#cache+1] = '\n'
323 410
324 cache[#cache+1] = "Unformatted version:" 411 cache[#cache+1] = banner_unformatted
325 cache[#cache+1] = "===========================" 412 cache[#cache+1] = banner_sep
326 text = text:gsub('>%s+<', "><") 413 text = text:gsub('>%s+<', "><")
327 cache[#cache+1] = text 414 cache[#cache+1] = text
328 cache[#cache+1] = '\n' 415 cache[#cache+1] = '\n'
329 416
330 wipe(state) 417 wipe(state)
331 return true 418 return true
332 end 419 end
333 420
334 local function specials (_, editbox, container, mkbutton) 421 local function specials (_, editbox, container, mkbutton)
335 local hl = mkbutton("Highlight", 422 local b = mkbutton("Highlight",
336 [[Highlight the unformatted copy for copy-and-pasting.]]) 423 [[Highlight the unformatted copy for copy-and-pasting.]])
337 hl:SetFullWidth(true) 424 b:SetFullWidth(true)
338 hl:SetCallback("OnClick", function(_hl) 425 b:SetCallback("OnClick", function(_b)
426 local _,start,finish
339 local txt = editbox:GetText() 427 local txt = editbox:GetText()
340 local _,start = txt:find("Unformatted version:\n=+\n") 428 _,start = txt:find(banner_unformatted..'\n'..banner_sep..'\n')
341 local _,finish = txt:find("</RaidInfo>", start) 429 _,finish = txt:find("</RaidInfo>", start)
342 editbox.editBox:HighlightText(start,finish) 430 editbox.editBox:HighlightText(start,finish)
343 editbox.editBox:SetCursorPosition(start) 431 editbox.editBox:SetCursorPosition(start)
344 end) 432 end)
345 container:AddChild(hl) 433 container:AddChild(b)
434
435 local b = mkbutton("Re-Unformat",
436 [[Regenerate only the unformatted copy at the bottom <*from*> the formatted copy at the top.]])
437 b:SetFullWidth(true)
438 b:SetCallback("OnClick", function(_b)
439 local _,start,finish
440 local txt = editbox:GetText()
441 _,start = txt:find(banner_formatted..'\n'..banner_sep..'\n', --[[init=]]1, --[[plain=]]true)
442 _,finish = txt:find("</RaidInfo>", start, true)
443 txt = txt:sub(start+1,finish)
444 txt = banner_formatted .. '\n'
445 .. banner_sep .. '\n'
446 .. txt .. '\n\n\n'
447 .. banner_unformatted .. '\n'
448 .. banner_sep .. '\n'
449 .. txt:gsub('>%s+<', "><") .. '\n'
450 -- This would normally screw up the cached version, but we're regenerating
451 -- everything on each new display for this tab anyhow.
452 editbox.editBox:SetText(txt)
453 _,start = txt:find(banner_unformatted..'\n'..banner_sep..'\n', --[[init=]]1, --[[plain=]]true)
454 editbox.editBox:SetCursorPosition(start)
455 end)
456 container:AddChild(b)
346 end 457 end
347 458
348 addon:register_text_generator ("mleqdkp", [[ML/EQ-DKP]], [[MLdkp 1.1 EQDKP format]], generator, specials) 459 addon:register_text_generator ("mleqdkp", [[ML/EQ-DKP]], [[MLdkp 1.1 EQDKP format]], generator, specials)
349 460
350 -- vim:noet 461 -- vim:noet