Mercurial > wow > ouroloot
changeset 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 | 4fba9c6b5d3d |
children | 952c3ac0e783 |
files | Ouro_Loot.toc core.lua gui.lua mleqdkp.lua |
diffstat | 4 files changed, 287 insertions(+), 58 deletions(-) [+] |
line wrap: on
line diff
--- a/Ouro_Loot.toc Fri Jun 17 20:30:46 2011 +0000 +++ b/Ouro_Loot.toc Tue Jun 28 07:36:26 2011 +0000 @@ -1,4 +1,4 @@ -## Interface: 40000 +## Interface: 40100 ## Title: Ouro Loot ## Version: @project-version@ ## Notes: Raid loot tracking and text generation
--- a/core.lua Fri Jun 17 20:30:46 2011 +0000 +++ b/core.lua Tue Jun 28 07:36:26 2011 +0000 @@ -158,9 +158,8 @@ local opts = nil local pairs, ipairs, tinsert, tremove, tonumber = pairs, ipairs, table.insert, table.remove, tonumber - local pprint, tabledump = addon.pprint, flib.tabledump - +local GetNumRaidMembers = GetNumRaidMembers -- En masse forward decls of symbols defined inside local blocks local _register_bossmod local makedate, create_new_cache, _init @@ -251,10 +250,11 @@ do local caches = {} local cleanup_group = AnimTimerFrame:CreateAnimationGroup() + local time = _G.time cleanup_group:SetLooping("REPEAT") cleanup_group:SetScript("OnLoop", function(cg) addon.dprint('cache',"OnLoop firing") - local now = GetTime() + local now = time() local alldone = true -- this is ass-ugly for _,c in ipairs(caches) do @@ -275,7 +275,7 @@ end) local function _add (cache, x) - tinsert(cache, {t=GetTime(),m=x}) + tinsert(cache, {t=time(),m=x}) if not cleanup_group:IsPlaying() then addon.dprint('cache', cache.name, "STARTING animation group") cache.cleanup:SetDuration(2) -- hmmm @@ -355,8 +355,8 @@ end function addon:OnEnable() - self:RegisterEvent "PLAYER_LOGOUT" - self:RegisterEvent "RAID_ROSTER_UPDATE" + self:RegisterEvent("PLAYER_LOGOUT") + self:RegisterEvent("RAID_ROSTER_UPDATE") -- Cribbed from Talented. I like the way jerry thinks: the first argument -- can be a format spec for the remainder of the arguments. (The new @@ -401,6 +401,7 @@ end function addon:PLAYER_LOGOUT() if (#g_loot > 0) or g_loot.saved + -- someday make this smarter or (g_loot.forum and g_loot.forum ~= "") or (g_loot.attend and g_loot.attend ~= "") then @@ -423,26 +424,113 @@ OuroLootSV_hist = self.history_all end -function addon:RAID_ROSTER_UPDATE (event) - if GetNumRaidMembers() > 0 then +do + local IsInInstance, UnitName, UnitIsConnected, UnitClass, UnitRace, UnitSex, + UnitLevel, UnitInRaid, UnitIsVisible, GetGuildInfo = + IsInInstance, UnitName, UnitIsConnected, UnitClass, UnitRace, UnitSex, + UnitLevel, UnitInRaid, UnitIsVisible, GetGuildInfo + local time, difftime = time, difftime + local R_ACTIVE, R_OFFLINE, R_LEFT = 1, 2, 3 + + local lastevent, now = 0, 0 + local timer_handle + + function addon:CheckRoster (leaving_p, now_a) + if not g_loot.raiders then return end -- bad transition + + now = now_a or time() + + if leaving_p then + for name,r in pairs(g_loot.raiders) do + r.leave = r.leave or now + end + return + end + + for name,r in pairs(g_loot.raiders) do + if r.online ~= R_LEFT and not UnitInRaid(name) then + r.online = R_LEFT + r.leave = now + end + end + + local redo = false + for i = 1, GetNumRaidMembers() do + local unit = 'raid'..i + local name = UnitName(unit) + -- No, that's not my typo, it really is "uknownbeing" in Blizzard's code. + if name and name ~= UNKNOWN and name ~= UNKNOWNOBJECT and name ~= UKNOWNBEING then + if not g_loot.raiders[name] then + g_loot.raiders[name] = { needinfo=true } + end + local r = g_loot.raiders[name] + if r.needinfo and UnitIsVisible(unit) then + r.needinfo = nil + r.class = select(2,UnitClass(unit)) + r.race = select(2,UnitRace(unit)) + r.sex = UnitSex(unit) + r.level = UnitLevel(unit) + r.guild = GetGuildInfo(unit) + end + local connected = UnitIsConnected(unit) + if connected and r.online ~= R_ACTIVE then + r.join = r.join or now + r.online = R_ACTIVE + elseif (not connected) and r.online ~= R_OFFLINE then + r.leave = now + r.online = R_OFFLINE + end + redo = redo or r.needinfo + end + end + if redo then + timer_handle = self:ScheduleRepeatingTimer("RAID_ROSTER_UPDATE", 60) + elseif timer_handle then + self:CancelTimer(timer_handle) + timer_handle = nil + end + end + + function addon:RAID_ROSTER_UPDATE (event) + if GetNumRaidMembers() == 0 then + -- Because of PLAYER_ENTERING_WORLD, this code also executes on load + -- screens while soloing and in regular groups. Take care. + if self.enabled then + self.popped = nil + self:UnregisterEvent("CHAT_MSG_LOOT") + self:CheckRoster(--[[leaving raid]]true) + end + return + end + local inside,whatkind = IsInInstance() if inside and (whatkind == "pvp" or whatkind == "arena") then return self.dprint('flow', "got RRU event but in pvp zone, bailing") end + + local docheck = self.enabled if event == "Activate" then -- dispatched manually from Activate - self:RegisterEvent "CHAT_MSG_LOOT" + self:RegisterEvent("CHAT_MSG_LOOT") _register_bossmod(self) + docheck = true elseif event == "RAID_ROSTER_UPDATE" then + -- hot code path, be careful + -- event registration from onload, joined a raid, maybe show popup - if opts.popup_on_join and not self.popped then + if (not self.popped) and opts.popup_on_join then self.popped = StaticPopup_Show "OUROL_REMIND" self.popped.data = self + return end end - else - self:UnregisterEvent "CHAT_MSG_LOOT" - self.popped = nil + if docheck and not InCombatLockdown() then + now = time() + if difftime(now,lastevent) > 45 then + lastevent = now + self:CheckRoster(false,now) + end + end end end @@ -645,12 +733,13 @@ ------ On/off function addon:Activate (opt_threshold, opt_bcast_only) - self:RegisterEvent "RAID_ROSTER_UPDATE" + self:RegisterEvent("RAID_ROSTER_UPDATE") + self:RegisterEvent("PLAYER_ENTERING_WORLD","RAID_ROSTER_UPDATE") self.popped = true if GetNumRaidMembers() > 0 then self:RAID_ROSTER_UPDATE("Activate") elseif self.debug.notraid then - self:RegisterEvent "CHAT_MSG_LOOT" + self:RegisterEvent("CHAT_MSG_LOOT") _register_bossmod(self) elseif g_restore_p then g_restore_p = nil @@ -677,8 +766,9 @@ function addon:Deactivate() self.enabled = false self.rebroadcast = false - self:UnregisterEvent "RAID_ROSTER_UPDATE" - self:UnregisterEvent "CHAT_MSG_LOOT" + self:UnregisterEvent("RAID_ROSTER_UPDATE") + self:UnregisterEvent("PLAYER_ENTERING_WORLD") + self:UnregisterEvent("CHAT_MSG_LOOT") self:Print("Ouro Raid Loot deactivated.") end @@ -819,7 +909,7 @@ -- FIXME printed could be too large if entries were deleted, how much do we care? self.sharder = g_loot.autoshard else - g_loot = { printed = {} } + g_loot = { printed = {}, raiders = {} } g_loot.saved = g_saved_tmp; g_saved_tmp = nil -- potentially restore across a clear end @@ -1093,10 +1183,11 @@ if not done_todays_date then do_todays_date() end local h, m = GetGameTime() - local localuptime = math.floor(GetTime()) + --local localuptime = math.floor(GetTime()) + local time_t = time() e.hour = h e.minute = m - e.stamp = localuptime + e.stamp = time_t --localuptime local index = #g_loot + 1 g_loot[index] = e return index @@ -1126,9 +1217,10 @@ name = name, date = makedate(), count = #g_loot, - forum = g_loot.forum, - attend = g_loot.attend, } + for text in self:registered_textgen_iter() do + save[text] = g_loot[text] + end self:Print("Saving current loot texts to #%d '%s'", n, name) g_loot.saved[n] = save return self:save_list() @@ -1144,8 +1236,9 @@ self:Clear(--[[verbose_p=]]false) -- Clear will already have displayed the window, and re-selected the first -- tab. Set these up for when the text tabs are clicked. - g_loot.forum = save.forum - g_loot.attend = save.attend + for text in self:registered_textgen_iter() do + g_loot[text] = save[text] + end end function addon:save_delete(num)
--- a/gui.lua Fri Jun 17 20:30:46 2011 +0000 +++ b/gui.lua Tue Jun 28 07:36:26 2011 +0000 @@ -93,6 +93,10 @@ end end + function addon:registered_textgen_iter() + return pairs(text_gen_funcs) + end + -- This function is called during load, so be careful! function addon:register_text_generator (text_type, title, description, generator, opt_specgen) if type(generator) ~= 'function' then @@ -1400,8 +1404,25 @@ simple:AddChild(w) end grp:AddChild(simple) + + simple = GUI:Create("SimpleGroup") + simple:SetLayout("Flow") + simple:SetRelativeWidth(0.85) + w = mkbutton("MidS-H", [[not exactly an Easter egg, with sound]]) + w:SetRelativeWidth(0.15) + w:SetCallback("OnClick", function() + PlaySoundFile[[Sound\Music\WorldEvents\HordeFirepole.mp3]] + end) + simple:AddChild(w) + w = mkbutton("MidS-A", [[not exactly an Easter egg, with sound]]) + w:SetRelativeWidth(0.15) + w:SetCallback("OnClick", function() + PlaySoundFile[[Sound\Music\WorldEvents\AllianceFirepole.mp3]] + end) + simple:AddChild(w) + grp:AddChild(simple) + grp:ResumeLayout() - container:AddChild(grp) GUI:ClearFocus() end @@ -1437,33 +1458,36 @@ grp:SetFullWidth(true) grp:SetTitle("User Options [these are saved across sessions]") + -- The relative width fields used to be done to take up less vertical space, but + -- that turned out to look messy. Now they're just a straight line for the most part. + -- reminder popup - w = mkoption ('popup_on_join', "Show reminder popup", 0.35, + w = mkoption ('popup_on_join', "Show reminder popup", 0.95, [[When joining a raid and not already tracking, display a dialog asking for instructions.]]) grp:AddChild(w) -- toggle scroll-to-bottom on first tab - w = mkoption('scroll_to_bottom', "Scroll to bottom when opening display", 0.60, + w = mkoption('scroll_to_bottom', "Scroll to bottom when opening display", 0.95, [[Scroll to the bottom of the loot window (most recent entries) when displaying the GUI.]]) grp:AddChild(w) -- /loot option - w = mkoption('register_slashloot', "Register /loot slash command on login", 0.45, + w = mkoption('register_slashloot', "Register /loot slash command on login", 0.95, [[Register "/loot" as a slash command in addition to the normal "/ouroloot". Relog to take effect.]]) grp:AddChild(w) -- chatty mode - w = mkoption('chatty_on_kill', "Be chatty on boss kill", 0.30, + w = mkoption('chatty_on_kill', "Be chatty on boss kill", 0.95, [[Print something to chat output when DBM tells Ouro Loot about a successful boss kill.]]) grp:AddChild(w) -- less noise in main panel - w = mkoption('no_tracking_wipes', "Do not track wipes", 0.25, + w = mkoption('no_tracking_wipes', "Do not track wipes", 0.95, [[Do not add 'wipe' entries on the main loot grid, or generate any text for them.]]) grp:AddChild(w) -- cutesy abbrevs - w = mkoption('snarky_boss', "Use snarky boss names", 0.35, + w = mkoption('snarky_boss', "Use snarky boss names", 0.95, [[Irreverent replacement names for boss events.]]) grp:AddChild(w) @@ -1471,7 +1495,7 @@ do local pair = GUI:Create("SimpleGroup") pair:SetLayout("Flow") - pair:SetRelativeWidth(0.6) + pair:SetRelativeWidth(0.95) local editbox, checkbox editbox = mkbutton("EditBox", nil, OuroLootSV_opts.keybinding_text, [[Keybinding text format is fragile! Relog to take effect.]])
--- a/mleqdkp.lua Fri Jun 17 20:30:46 2011 +0000 +++ b/mleqdkp.lua Tue Jun 28 07:36:26 2011 +0000 @@ -1,5 +1,14 @@ -if UnitName"player" ~= "Farmbuyer" then return end +-- This file is one gigantic exercise in abusing the garbage collector via +-- string manipulation. A real XML-handling library would be cleaner, alas, +-- we can't load third-party .so/.dll inside the WoW client. Life is hard. + local addon = select(2,...) +local pairs, ipairs, tinsert, tremove, tconcat = pairs, ipairs, table.insert, table.remove, table.concat +local tostring, tonumber = tostring, tonumber + +local banner_formatted = "Formatted version (scroll down for unformatted):" +local banner_unformatted = "Unformatted version:" +local banner_sep = "===========================" -- We keep some local state bundled up rather than trying to pass it around -- as paramters (which would have entailed creating a ton of closures). @@ -9,7 +18,7 @@ --[[ -This is taken from CT_RaidTracker 1.7.32, reconstructing the output from +This is based on CT_RaidTracker 1.7.32, reconstructing the output from code inspection. No official format documents are available on the web without downloading and installing the EQDKP webserver package. Bah. @@ -31,22 +40,32 @@ <key>$TIMESTAMP</key> <realm>$REALM</realm> <start>$TIMESTAMP</start> {Same as the key, apparently?} -<end>$ENDTIME</end> {Set by the "end the raid" command in the raid tracker, here just the final entry time} +<end>$ENDTIME</end> {Set by the "end the raid" command in CTRT, here it is just the final entry time} <zone>$ZONE</zone> {may be optional. first one on the list in case of multiple zones?} {<difficulty>$DIFFICULTY</difficulty> {this scales badly in places like ICC. may be optional?}} -{<PlayerInfos>... {guh.}} +<PlayerInfos> + $PLAYER_INFOS +</PlayerInfos> <BossKills> $BOSS_KILLS </BossKills> -{<Wipes> bleh} -{<NextBoss>Baron Steamroller</NextBoss> {only one "next boss" for the whole raid event? huh?}} +<Wipes> + $WIPES +</Wipes> +{<NextBoss>Baron Steamroller</NextBoss> {only one "next boss" for the whole + raid event? presumably where to pick up next time?}} <note><![CDATA[$RAIDNOTE - Zone: $ZONE]]></note> -{<Join>...</Join><Leave>...</Leave> {specific timestamps per player. meh.}} +<Join> + $JOINS +</Join> +<Leave> + $LEAVES +</Leave> <Loot> $PHAT_LEWTS @@ -54,7 +73,7 @@ </RaidInfo>]====]):gsub('%b{}', "") --[[ -See the loot markup, next block. +See the loot markup below. ]] local boss_kills_xml = ([====[ <key$N> @@ -78,9 +97,52 @@ end --[[ +Handles the PlayerInfo, Join, and Leave tags. +]] +local joinleave_xml +local player_info_xml = ([====[ + <key$N> +$PLAYER_GRUNTWORK + </key$N> +]====]):gsub('%b{}', "") + +local function player_info_tag_lookup (tag) + if tag == 'N' then + return tostring(state.key) + elseif tag == 'NAME' then + return state.index + elseif tag == 'TIME' then + return state.time + end + local ltag = tag:lower() + if state.entry[ltag] then + -- handles race, guild, sex, class, level + return state.entry[ltag] + end + return "?" +end + +do + local pi_xml_save = player_info_xml + local gruntwork_tags = { + "name", "race", "guild", "sex", "class", "level" + } + for i,tag in ipairs(gruntwork_tags) do + gruntwork_tags[i] = (" <%s>$%s</%s>"):format(tag,tag:upper(),tag) + end + player_info_xml = player_info_xml:gsub('$PLAYER_GRUNTWORK', table.concat(gruntwork_tags,'\n')) + + -- The join/leave blocks use "player" instead of "name". They don't have a + -- guild tag, but they do have a time tag. + gruntwork_tags[1] = " <player>$NAME</player>" + gruntwork_tags[3] = " <time>$TIME</time>" + joinleave_xml = pi_xml_save:gsub('$PLAYER_GRUNTWORK', table.concat(gruntwork_tags,'\n')) +end + +--[[ $N 1-based loop variable for key element -$ITEMNAME Without The Brackets -$ITEMID Not the ID, actually a full itemstring without the leading "item:" +$ITEMNAME Without The Square Brackets of the Whale +$ITEMID Not the numeric ID, actually a full itemstring without the leading "item:" $ICON Last component of texture path? $CLASS,$SUBCLASS ItemType $COLOR GetItemQualityColor, full 8-digit string @@ -169,7 +231,7 @@ return e.count and e.count:sub(2) or "1" -- skip the leading "x" end --- maybe combine these next two +-- should combine these next two tag_lookup_handlers.BOSS = function (i, e) while i > 0 and state.loot[i].kind ~= 'boss' do @@ -224,6 +286,7 @@ end end + local function generator (ttype, loot, last_printed, generated, cache) -- Because it's XML, generated text is "grown" by shoving more crap into -- the middle instead of appending to the end. Only easy way of doing that @@ -280,25 +343,49 @@ state.key = state.key + 1 end - text = text:gsub('$PHAT_LEWTS', table.concat(all_lewts, '\n')) + text = text:gsub('$PHAT_LEWTS', tconcat(all_lewts, '\n')) end - -- Bosses + -- Player info, join times, leave times do - local all_bosses = {} + local all_players, all_joins, all_leaves = {}, {}, {} + local player_template, joinleave_template = player_info_xml, joinleave_xml + local date = date + + state.key = 1 + if type(loot.raiders) == 'table' then for name,r in pairs(loot.raiders) do + state.index, state.entry = name, r + all_players[#all_players+1] = player_template:gsub('%$([%w_]+)', player_info_tag_lookup) + state.time = date ("%m/%d/%y %H:%M:00", r.join) + all_joins[#all_joins+1] = joinleave_template:gsub('%$([%w_]+)', player_info_tag_lookup) + state.time = date ("%m/%d/%y %H:%M:00", r.leave) + all_leaves[#all_leaves+1] = joinleave_template:gsub('%$([%w_]+)', player_info_tag_lookup) + state.key = state.key + 1 + end end + text = text:gsub('$PLAYER_INFOS', tconcat(all_players, '\n')) + :gsub('$JOINS', tconcat(all_joins, '\n')) + :gsub('$LEAVES', tconcat(all_leaves, '\n')) + end + + -- Bosses and wipes (does anybody really use the latter?) + do + local all_bosses, all_wipes = {}, {} local boss_template = boss_kills_xml state.key = 1 for i,e in addon:filtered_loot_iter('boss') do - if e.reason == 'kill' then -- oh, for a 'continue' statement... + if e.reason == 'kill' then state.index, state.entry = i, e all_bosses[#all_bosses+1] = boss_template:gsub('%$([%w_]+)', boss_kills_tag_lookup) state.key = state.key + 1 + elseif e.reason == 'wipe' then + all_wipes[#all_wipes+1] = ('<Wipe>%d</Wipe>'):format(e.stamp) end end - text = text:gsub('$BOSS_KILLS', table.concat(all_bosses, '\n')) + text = text:gsub('$BOSS_KILLS', tconcat(all_bosses, '\n')) + :gsub('$WIPES', tconcat(all_wipes, '\n')) end -- In addition to doing the top-level zone, this will also catch any @@ -316,13 +403,13 @@ --text = text:gsub('$DIFFICULTY', ) text = text:gsub('$RAIDNOTE', "") - cache[#cache+1] = "Formatted version (scroll down for unformatted):" - cache[#cache+1] = "===========================" + cache[#cache+1] = banner_formatted + cache[#cache+1] = banner_sep cache[#cache+1] = text cache[#cache+1] = '\n' - cache[#cache+1] = "Unformatted version:" - cache[#cache+1] = "===========================" + cache[#cache+1] = banner_unformatted + cache[#cache+1] = banner_sep text = text:gsub('>%s+<', "><") cache[#cache+1] = text cache[#cache+1] = '\n' @@ -332,17 +419,41 @@ end local function specials (_, editbox, container, mkbutton) - local hl = mkbutton("Highlight", + local b = mkbutton("Highlight", [[Highlight the unformatted copy for copy-and-pasting.]]) - hl:SetFullWidth(true) - hl:SetCallback("OnClick", function(_hl) + b:SetFullWidth(true) + b:SetCallback("OnClick", function(_b) + local _,start,finish local txt = editbox:GetText() - local _,start = txt:find("Unformatted version:\n=+\n") - local _,finish = txt:find("</RaidInfo>", start) + _,start = txt:find(banner_unformatted..'\n'..banner_sep..'\n') + _,finish = txt:find("</RaidInfo>", start) editbox.editBox:HighlightText(start,finish) editbox.editBox:SetCursorPosition(start) end) - container:AddChild(hl) + container:AddChild(b) + + local b = mkbutton("Re-Unformat", + [[Regenerate only the unformatted copy at the bottom <*from*> the formatted copy at the top.]]) + b:SetFullWidth(true) + b:SetCallback("OnClick", function(_b) + local _,start,finish + local txt = editbox:GetText() + _,start = txt:find(banner_formatted..'\n'..banner_sep..'\n', --[[init=]]1, --[[plain=]]true) + _,finish = txt:find("</RaidInfo>", start, true) + txt = txt:sub(start+1,finish) + txt = banner_formatted .. '\n' + .. banner_sep .. '\n' + .. txt .. '\n\n\n' + .. banner_unformatted .. '\n' + .. banner_sep .. '\n' + .. txt:gsub('>%s+<', "><") .. '\n' + -- This would normally screw up the cached version, but we're regenerating + -- everything on each new display for this tab anyhow. + editbox.editBox:SetText(txt) + _,start = txt:find(banner_unformatted..'\n'..banner_sep..'\n', --[[init=]]1, --[[plain=]]true) + editbox.editBox:SetCursorPosition(start) + end) + container:AddChild(b) end addon:register_text_generator ("mleqdkp", [[ML/EQ-DKP]], [[MLdkp 1.1 EQDKP format]], generator, specials)