Mercurial > wow > ouroloot
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 |
