farmbuyer@1: if UnitName"player" ~= "Farmbuyer" then return end farmbuyer@1: local addon = select(2,...) farmbuyer@1: farmbuyer@1: -- We keep some local state bundled up rather than trying to pass it around farmbuyer@1: -- as paramters (which would have entailed creating a ton of closures). farmbuyer@1: local state = {} farmbuyer@1: local tag_lookup_handlers = {} farmbuyer@1: local do_tag_lookup_handler farmbuyer@1: farmbuyer@1: farmbuyer@1: --[[ farmbuyer@1: This is taken from CT_RaidTracker 1.7.32, reconstructing the output from farmbuyer@1: code inspection. No official format documents are available on the web farmbuyer@1: without downloading and installing the EQDKP webserver package. Bah. farmbuyer@1: farmbuyer@1: Case of tag names shouldn't matter, but is preserved here from CT_RaidTracker farmbuyer@1: code for comparison of generated output. farmbuyer@1: farmbuyer@1: There is some waste adding newlines between elements here only to strip them farmbuyer@1: out later, but it's worth the extra cycles for debugging and verification. farmbuyer@1: farmbuyer@1: $TIMESTAMP,$ENDTIME MM/DD/YY HH:mm:ss except we don't have seconds available farmbuyer@1: $REALM GetRealmName() farmbuyer@1: $ZONE raw name, not snarky farmbuyer@1: $RAIDNOTE arbitrary text for the raid event farmbuyer@1: $PHAT_LEWTS all accumulated loot entries farmbuyer@1: ]] farmbuyer@1: local XML = ([====[ farmbuyer@1: farmbuyer@1: 1.4 {In live output, this is missing due to scoping bug in ct_raidtracker.lua:3471} farmbuyer@1: $TIMESTAMP farmbuyer@1: $REALM farmbuyer@1: $TIMESTAMP {Same as the key, apparently?} farmbuyer@1: $ENDTIME {Set by the "end the raid" command in the raid tracker, here just the final entry time} farmbuyer@1: $ZONE {may be optional. first one on the list in case of multiple zones?} farmbuyer@1: {$DIFFICULTY {this scales badly in places like ICC. may be optional?}} farmbuyer@1: farmbuyer@1: {... {guh.}} farmbuyer@1: farmbuyer@1: farmbuyer@1: $BOSS_KILLS farmbuyer@1: farmbuyer@1: farmbuyer@1: { bleh} farmbuyer@1: {Baron Steamroller {only one "next boss" for the whole raid event? huh?}} farmbuyer@1: farmbuyer@1: farmbuyer@1: farmbuyer@1: {...... {specific timestamps per player. meh.}} farmbuyer@1: farmbuyer@1: farmbuyer@1: $PHAT_LEWTS farmbuyer@1: farmbuyer@1: ]====]):gsub('%b{}', "") farmbuyer@1: farmbuyer@1: --[[ farmbuyer@1: See the loot markup, next block. farmbuyer@1: ]] farmbuyer@1: local boss_kills_xml = ([====[ farmbuyer@1: farmbuyer@1: $BOSS_NAME farmbuyer@1: farmbuyer@1: {this is actually empty in the working example...} farmbuyer@1: $DIFFICULTY farmbuyer@1: farmbuyer@1: ]====]):gsub('%b{}', "") farmbuyer@1: farmbuyer@1: local function boss_kills_tag_lookup (tag) farmbuyer@1: if tag == 'N' then farmbuyer@1: return tostring(state.key) farmbuyer@1: elseif tag == 'BOSS_NAME' then farmbuyer@1: return state.entry.bosskill farmbuyer@1: elseif tag == 'BOSS_TIME' then farmbuyer@1: return do_tag_lookup_handler (state.index, state.entry, 'TIME') farmbuyer@1: else farmbuyer@1: return do_tag_lookup_handler (state.index, state.entry, tag) or 'NYI' farmbuyer@1: end farmbuyer@1: end farmbuyer@1: farmbuyer@1: --[[ farmbuyer@1: $N 1-based loop variable for key element farmbuyer@1: $ITEMNAME Without The Brackets farmbuyer@1: $ITEMID Not the ID, actually a full itemstring without the leading "item:" farmbuyer@1: $ICON Last component of texture path? farmbuyer@1: $CLASS,$SUBCLASS ItemType farmbuyer@1: $COLOR GetItemQualityColor, full 8-digit string farmbuyer@1: $COUNT,$BOSS,$ZONE, farmbuyer@1: $PLAYER all self-explanatory farmbuyer@1: $COSTS in DKP points... hmmm farmbuyer@1: $ITEMNOTE take the notes field for this one farmbuyer@1: $TIME another formatted timestamp farmbuyer@1: ]] farmbuyer@1: local phat_lewt_xml = ([====[ farmbuyer@1: farmbuyer@1: $LEWT_GRUNTWORK farmbuyer@1: $ZONE {may be optional} farmbuyer@1: $DIFFICULTY {this scales badly in places like ICC. may be optional?} farmbuyer@1: {zone can be followed by difficulty} farmbuyer@1: farmbuyer@1: ]====]):gsub('%b{}', "") farmbuyer@1: farmbuyer@1: local function phat_lewt_tag_lookup (tag) farmbuyer@1: if tag == 'N' then farmbuyer@1: return tostring(state.key) farmbuyer@1: elseif tag == 'COSTS' farmbuyer@1: then return '1' farmbuyer@1: else farmbuyer@1: return do_tag_lookup_handler (state.index, state.entry, tag) or 'NYI' farmbuyer@1: end farmbuyer@1: end farmbuyer@1: farmbuyer@1: do farmbuyer@1: local gruntwork_tags = { farmbuyer@1: "ItemName", "ItemID", "Icon", "Class", "SubClass", "Color", "Count", farmbuyer@1: "Player", "Costs", "Boss", "Time", farmbuyer@1: } farmbuyer@1: for i,tag in ipairs(gruntwork_tags) do farmbuyer@1: gruntwork_tags[i] = (" <%s>$%s"):format(tag,tag:upper(),tag) farmbuyer@1: end farmbuyer@1: phat_lewt_xml = phat_lewt_xml:gsub('$LEWT_GRUNTWORK', table.concat(gruntwork_tags,'\n')) farmbuyer@1: end farmbuyer@1: farmbuyer@1: farmbuyer@1: local function format_EQDKP_timestamp (day_entry, time_entry) farmbuyer@1: --assert(day_entry.kind == 'time', day_entry.kind .. " passed to MLEQDKP timestamp") farmbuyer@1: return addon:format_timestamp ("$M/$D/$Y $h:$m:00", day_entry, time_entry) farmbuyer@1: end farmbuyer@1: farmbuyer@1: farmbuyer@1: -- Look up tag strings for a particular item, given index and entry table. farmbuyer@1: tag_lookup_handlers.ITEMNAME = farmbuyer@1: function (i, e) farmbuyer@1: return e.itemname farmbuyer@1: end farmbuyer@1: farmbuyer@1: tag_lookup_handlers.ITEMID = farmbuyer@1: function (i, e) farmbuyer@1: return e.itemlink:match("^|c%x+|H(item[%d:]+)|h%[") farmbuyer@1: end farmbuyer@1: farmbuyer@1: tag_lookup_handlers.ICON = farmbuyer@1: function (i, e) farmbuyer@1: local str = e.itexture farmbuyer@1: repeat farmbuyer@1: local s = str:find('\\') farmbuyer@1: if s then str = str:sub(s+1) end farmbuyer@1: until not s farmbuyer@1: return str farmbuyer@1: end farmbuyer@1: farmbuyer@1: tag_lookup_handlers.CLASS = farmbuyer@1: function (i, e) farmbuyer@1: return state.class farmbuyer@1: end farmbuyer@1: farmbuyer@1: tag_lookup_handlers.SUBCLASS = farmbuyer@1: function (i, e) farmbuyer@1: return state.subclass farmbuyer@1: end farmbuyer@1: farmbuyer@1: tag_lookup_handlers.COLOR = farmbuyer@1: function (i, e) farmbuyer@1: local q = select(4, GetItemQualityColor(e.quality)) farmbuyer@1: return q:sub(3) -- skip leading |c farmbuyer@1: end farmbuyer@1: farmbuyer@1: tag_lookup_handlers.COUNT = farmbuyer@1: function (i, e) farmbuyer@1: return e.count and e.count:sub(2) or "1" -- skip the leading "x" farmbuyer@1: end farmbuyer@1: farmbuyer@1: -- maybe combine these next two farmbuyer@1: tag_lookup_handlers.BOSS = farmbuyer@1: function (i, e) farmbuyer@1: while i > 0 and state.loot[i].kind ~= 'boss' do farmbuyer@1: i = i - 1 farmbuyer@1: end farmbuyer@1: if i == 0 then return "No Boss Entry Found, Unknown Boss" end farmbuyer@1: return state.loot[i].bosskill farmbuyer@1: end farmbuyer@1: farmbuyer@1: tag_lookup_handlers.ZONE = farmbuyer@1: function (i, e) farmbuyer@1: while i > 0 and state.loot[i].kind ~= 'boss' do farmbuyer@1: i = i - 1 farmbuyer@1: end farmbuyer@1: if i == 0 then return "No Boss Entry Found, Unknown Zone" end farmbuyer@1: return state.loot[i].instance farmbuyer@1: end farmbuyer@1: farmbuyer@1: tag_lookup_handlers.DIFFICULTY = farmbuyer@1: function (i, e) farmbuyer@1: local tag = tag_lookup_handlers.ZONE(i,e) farmbuyer@1: local N,h = tag:match("%((%d+)(h?)%)") farmbuyer@1: if not N then return "1" end -- maybe signal an error instead? farmbuyer@1: N = tonumber(N) farmbuyer@1: N = ( (N==10) and 1 or 2 ) + ( (h=='h') and 2 or 0 ) farmbuyer@1: return tostring(N) farmbuyer@1: end farmbuyer@1: farmbuyer@1: tag_lookup_handlers.PLAYER = farmbuyer@1: function (i, e) farmbuyer@1: return state.player farmbuyer@1: end farmbuyer@1: farmbuyer@1: tag_lookup_handlers.ITEMNOTE = farmbuyer@1: function (i, e) farmbuyer@1: return state.itemnote farmbuyer@1: end farmbuyer@1: farmbuyer@1: tag_lookup_handlers.TIME = farmbuyer@1: function (i, e) farmbuyer@1: local ti,tl = addon:find_previous_time_entry(i) farmbuyer@1: return format_EQDKP_timestamp(tl,e) farmbuyer@1: end farmbuyer@1: farmbuyer@1: farmbuyer@1: function do_tag_lookup_handler (index, entry, tag) farmbuyer@1: local h = tag_lookup_handlers[tag] farmbuyer@1: if h then farmbuyer@1: return h(index,entry) farmbuyer@1: else farmbuyer@1: error(("MLDKP tag lookup (index %d) on tag %s with no handler"):format(index,tag)) farmbuyer@1: end farmbuyer@1: end farmbuyer@1: farmbuyer@1: local function generator (ttype, loot, last_printed, generated, cache) farmbuyer@1: -- Because it's XML, generated text is "grown" by shoving more crap into farmbuyer@1: -- the middle instead of appending to the end. Only easy way of doing that farmbuyer@1: -- here is regenerating it from scratch each time. farmbuyer@1: generated[ttype] = nil farmbuyer@1: farmbuyer@1: local _ farmbuyer@1: local text = XML farmbuyer@1: state.loot = loot farmbuyer@1: farmbuyer@1: -- TIMESTAMPs farmbuyer@1: do farmbuyer@1: local f,l -- first and last timestamps in the table farmbuyer@1: for i = 1, #loot do farmbuyer@1: if loot[i].kind == 'time' then farmbuyer@1: f = format_EQDKP_timestamp(loot[i]) farmbuyer@1: break farmbuyer@1: end farmbuyer@1: end farmbuyer@1: _,l = addon:find_previous_time_entry(#loot) -- latest timestamp farmbuyer@1: l = format_EQDKP_timestamp(l,loot[#loot]) farmbuyer@1: text = text:gsub('$TIMESTAMP', f):gsub('$ENDTIME', l) farmbuyer@1: end farmbuyer@1: farmbuyer@1: -- Loot farmbuyer@1: do farmbuyer@1: local all_lewts = {} farmbuyer@1: local lewt_template = phat_lewt_xml farmbuyer@1: farmbuyer@1: state.key = 1 farmbuyer@1: for i,e in addon:filtered_loot_iter('loot') do farmbuyer@1: state.index, state.entry = i, e farmbuyer@1: -- no sense doing repeated getiteminfo calls farmbuyer@1: state.class, state.subclass = select(6, GetItemInfo(e.id)) farmbuyer@1: farmbuyer@1: -- similar logic as text_tabs.lua: farmbuyer@1: -- assuming nobody names a toon "offspec" or "gvault" farmbuyer@1: local P, N farmbuyer@1: local disp = e.disposition or e.person farmbuyer@1: if disp == 'offspec' then farmbuyer@1: P,N = e.person, "offspec" farmbuyer@1: elseif disp == 'gvault' then farmbuyer@1: P,N = "guild vault", e.person farmbuyer@1: else farmbuyer@1: P,N = disp, "" farmbuyer@1: end farmbuyer@1: if e.extratext_byhand then farmbuyer@1: N = N .. " -- " .. e.extratext farmbuyer@1: end farmbuyer@1: state.player, state.itemnote = P, N farmbuyer@1: farmbuyer@1: all_lewts[#all_lewts+1] = lewt_template:gsub('%$([%w_]+)', farmbuyer@1: phat_lewt_tag_lookup) farmbuyer@1: state.key = state.key + 1 farmbuyer@1: end farmbuyer@1: farmbuyer@1: text = text:gsub('$PHAT_LEWTS', table.concat(all_lewts, '\n')) farmbuyer@1: end farmbuyer@1: farmbuyer@1: -- Bosses farmbuyer@1: do farmbuyer@1: local all_bosses = {} farmbuyer@1: local boss_template = boss_kills_xml farmbuyer@1: farmbuyer@1: state.key = 1 farmbuyer@1: for i,e in addon:filtered_loot_iter('boss') do farmbuyer@1: if e.reason == 'kill' then -- oh, for a 'continue' statement... farmbuyer@1: state.index, state.entry = i, e farmbuyer@1: all_bosses[#all_bosses+1] = boss_template:gsub('%$([%w_]+)', farmbuyer@1: boss_kills_tag_lookup) farmbuyer@1: state.key = state.key + 1 farmbuyer@1: end farmbuyer@1: end farmbuyer@1: farmbuyer@1: text = text:gsub('$BOSS_KILLS', table.concat(all_bosses, '\n')) farmbuyer@1: end farmbuyer@1: farmbuyer@1: -- In addition to doing the top-level zone, this will also catch any farmbuyer@1: -- leftover $ZONE tags. There could be multiple places in the raid, so farmbuyer@1: -- we default to the first one we saw. farmbuyer@1: do farmbuyer@1: local iter = addon:filtered_loot_iter() -- HACK farmbuyer@1: local first_boss = iter('boss',0) farmbuyer@1: local zone = first_boss and loot[first_boss].instance or "Unknown" farmbuyer@1: text = text:gsub('$ZONE', zone) farmbuyer@1: end farmbuyer@1: farmbuyer@1: -- Misc farmbuyer@1: text = text:gsub('$REALM', (GetRealmName())) farmbuyer@1: --text = text:gsub('$DIFFICULTY', ) farmbuyer@1: text = text:gsub('$RAIDNOTE', "") farmbuyer@1: farmbuyer@1: cache[#cache+1] = "Formatted version (scroll down for unformatted):" farmbuyer@1: cache[#cache+1] = "===========================" farmbuyer@1: cache[#cache+1] = text farmbuyer@1: cache[#cache+1] = '\n' farmbuyer@1: farmbuyer@1: cache[#cache+1] = "Unformatted version:" farmbuyer@1: cache[#cache+1] = "===========================" farmbuyer@1: text = text:gsub('>%s+<', "><") farmbuyer@1: cache[#cache+1] = text farmbuyer@1: cache[#cache+1] = '\n' farmbuyer@1: farmbuyer@1: wipe(state) farmbuyer@1: return true farmbuyer@1: end farmbuyer@1: farmbuyer@1: local function specials (_, editbox, container, mkbutton) farmbuyer@1: local hl = mkbutton("Highlight", farmbuyer@1: [[Highlight the unformatted copy for copy-and-pasting.]]) farmbuyer@1: hl:SetFullWidth(true) farmbuyer@1: hl:SetCallback("OnClick", function(_hl) farmbuyer@1: local txt = editbox:GetText() farmbuyer@1: local _,start = txt:find("Unformatted version:\n=+\n") farmbuyer@1: local _,finish = txt:find("", start) farmbuyer@1: editbox.editBox:HighlightText(start,finish) farmbuyer@1: editbox.editBox:SetCursorPosition(start) farmbuyer@1: end) farmbuyer@1: container:AddChild(hl) farmbuyer@1: end farmbuyer@1: farmbuyer@1: addon:register_text_generator ("mleqdkp", [[ML/EQ-DKP]], [[MLdkp 1.1 EQDKP format]], generator, specials) farmbuyer@1: farmbuyer@1: -- vim:noet