diff core.lua @ 56:fcc0d0ff5832

- instance_tag() also returns max instance size as a plain number, adjust call sites - clean up .raiders table, add some new fields, use copies of this instead of a single string - make sure datarev field is properly updated when it's already present - avoid multiple GetRaidRosterInfo loops scattered throughout the addon, instead just traverse the .raiders list, regularly updated - make the 'loot' and 'boss' broadcasts versioned. Handle receiving older. - new format for plaintext attendance output
author Farmbuyer of US-Kilrogg <farmbuyer@gmail.com>
date Fri, 13 Apr 2012 04:28:46 +0000
parents ac57a4342812
children 81d5449621f8
line wrap: on
line diff
--- a/core.lua	Sat Apr 07 05:40:20 2012 +0000
+++ b/core.lua	Fri Apr 13 04:28:46 2012 +0000
@@ -6,6 +6,18 @@
 - forum			saved text from forum markup window, default nil
 - attend		saved text from raid attendence window, default nil
 - printed.FOO	last loot index formatted into text window FOO, default 0
+- raiders		accumulating raid roster data as we see raid members; indexed
+				by player name with subtable fields:
+-    class		capitalized English codename ("WARRIOR", "DEATHKNIGHT", etc)
+-    subgroup	1-8
+-    race		English codename ("BloodElf", etc)
+-    sex		1 = unknown/error, 2 = male, 3 = female
+-    level		can be 0 if player was offline at the time
+-    guild		guild name, or missing if unguilded
+-    online		1 = online, 2 = offline, 3 = no longer in raid
+	[both of these next two fields use time_t values:]
+-    join		time player joined the raid (or first time we've seen them)
+-    leave		time player left the raid (or time we've left the raid)
 
 Common g_loot entry indices:
 - kind			time/boss/loot
@@ -19,26 +31,29 @@
 
 Boss specific g_loot indices:
 - bossname		name of boss/encounter;
-- 				may be changed if "snarky boss names" option is enabled
+ 				may be changed if "snarky boss names" option is enabled
 - reason		wipe/kill ("pull" does not generate an entry)
 - instance		name of instance, including size and difficulty
-- duration		in seconds; may be missing
-- raiderlist	"Able, Baker, Charlie"; may be missing
+- maxsize		5/10/25, presumably also 15 and 40 could show up; can be
+				0 if we're outside an instance and the player inside has
+				an older version
+- duration		in seconds; may be missing (only present if local)
+- raidersnap	copy of g_loot.raiders at the time of the boss event
 
 Loot specific g_loot indices:
 - person		recipient
 - person_class	class of recipient if available; may be missing;
-- 				will be classID-style (e.g., DEATHKNIGHT)
+ 				will be classID-style (e.g., DEATHKNIGHT)
 - itemname		not including square brackets
 - id			itemID as number
 - itemlink		full clickable link
 - itexture		icon path (e.g., Interface\Icons\INV_Misc_Rune_01)
 - quality		ITEM_QUALITY_* number
 - disposition	offspec/gvault/shard; missing otherwise; can be set from
--				the extratext field
+				the extratext field
 - count			e.g., "x3"; missing otherwise; can be set/removed from
--				extratext; triggers only for a stack of items, not "the boss
--				dropped double axes today"
+				extratext; triggers only for a stack of items, not "the boss
+				dropped double axes today"
 - is_heroic		true if item is heroic; missing otherwise
 - cache_miss	if GetItemInfo failed; SHOULD be missing (changes other fields)
 - bcast_from	if rebroadcast from another player; missing otherwise
@@ -88,7 +103,7 @@
 
 ------ Constants
 local option_defaults = {
-	['datarev'] = 15,    -- cheating, this isn't actually an option
+	['datarev'] = 16,    -- cheating, this isn't actually an option
 	['popup_on_join'] = true,
 	['register_slashloot'] = true,
 	['scroll_to_bottom'] = true,
@@ -138,7 +153,7 @@
 -- Play cute games with namespaces here just to save typing.  WTB Lua 5.2 PST.
 do local _G = _G setfenv (1, addon)
 
-	commrev			= 15  -- number
+	commrev			= '16'
 	revision		= _G.GetAddOnMetadata(nametag,"Version") or "?"  -- "x.yy.z", etc
 	ident			= "OuroLoot2"
 	identTg			= "OuroLoot2Tg"
@@ -233,10 +248,10 @@
 local g_boss_signpost	= nil
 local opts				= nil
 
-local pairs, ipairs, tinsert, tremove, tonumber, wipe =
-	pairs, ipairs, table.insert, table.remove, tonumber, table.wipe
+local pairs, ipairs, tinsert, tremove, tostring, tonumber, wipe =
+	pairs, ipairs, table.insert, table.remove, tostring, tonumber, table.wipe
 local pprint, tabledump = addon.pprint, flib.tabledump
-local GetNumRaidMembers = GetNumRaidMembers
+local CopyTable, GetNumRaidMembers = CopyTable, GetNumRaidMembers
 -- En masse forward decls of symbols defined inside local blocks
 local _register_bossmod, makedate, create_new_cache, _init, _log
 
@@ -323,29 +338,30 @@
 	end
 end
 
--- Returns an instance name or abbreviation
+-- Returns an instance name or abbreviation, followed by the raid size
 local function instance_tag()
 	local name, typeof, diffcode, diffstr, _, perbossheroic, isdynamic = GetInstanceInfo()
-	local t
+	local t, r
 	name = addon.instance_abbrev[name] or name
-	if typeof == "none" then return name end
+	if typeof == "none" then return name, MAX_RAID_MEMBERS end
 	-- diffstr is "5 Player", "10 Player (Heroic)", etc.  ugh.
 	if (GetLFGMode()) and (GetLFGModeType() == 'raid') then
-		t = 'LFR'
+		t,r = 'LFR', 25
 	elseif diffcode == 1 then
-		t = ((GetNumRaidMembers()>0) and "10" or "5")
+		t,r = (GetNumRaidMembers()>0) and "10",10 or "5",5
 	elseif diffcode == 2 then
-		t = ((GetNumRaidMembers()>0) and "25" or "5h")
+		t,r = (GetNumRaidMembers()>0) and "25",25 or "5h",5
 	elseif diffcode == 3 then
-		t = "10h"
+		t,r = "10h", 10
 	elseif diffcode == 4 then
-		t = "25h"
+		t,r = "25h", 25
 	end
 	-- dynamic difficulties always return normal "codes"
 	if isdynamic and perbossheroic == 1 then
 		t = t .. "h"
 	end
-	return name .. "(" .. t .. ")"
+	pprint("instance_tag final", t, r)
+	return name .. "(" .. t .. ")", r
 end
 addon.instance_tag = instance_tag   -- grumble
 addon.latest_instance = nil         -- spelling reminder, assigned elsewhere
@@ -448,6 +464,7 @@
 			opts[opt] = default
 		end
 	end
+	opts.datarev = option_defaults.datarev
 
 	-- transition&remove old options
 	opts['forum_use_itemid'] = nil
@@ -510,18 +527,33 @@
 	self.history_all.HISTFORMAT = nil   -- don't keep this in live data
 	--OuroLootSV_hist = nil
 
-	-- Handle changes to the stored data format, in stages from oldest to
-	-- newest.  This won't look coherent until multiple stages are happening.
-	if stored_datarev == nil then
-		self:Print("Transitioning saved data format to 15...")
-		for i,e in ipairs(OuroLootSV) do
-			if e.bosskill then
-				e.bossname, e.bosskill = e.bosskill, nil
+	-- Handle changes to the stored data format in stages from oldest to newest.
+	if OuroLootSV then
+		local dirty = false
+		if stored_datarev == nil then
+			self:Print("Transitioning saved data format to 15..."); dirty = true
+			for i,e in ipairs(OuroLootSV) do
+				if e.bosskill then
+					e.bossname, e.bosskill = e.bosskill, nil
+				end
 			end
+			stored_datarev = 15
 		end
-		stored_datarev = 15
+		if stored_datarev == 15 then
+			self:Print("Transitioning saved data format to 16..."); dirty = true
+			for i,e in ipairs(OuroLootSV) do
+				if e.kind == 'boss' then
+					e.maxsize, e.raiderlist, e.raidersnap = 0, nil, {}
+				end
+			end
+			OuroLootSV.raiders = OuroLootSV.raiders or {}
+			for name,r in pairs(OuroLootSV.raiders) do
+				r.subgroup = 0
+			end
+			stored_datarev = 16
+		end
+		if dirty then self:Print("Saved data has been massaged into shape.") end
 	end
-	--if stored_datarev == 15 then.... 
 
 	_init(self)
 	self.dprint('flow', "version strings:", revision_large, self.status_text)
@@ -681,9 +713,9 @@
 
 do
 	local IsInInstance, UnitName, UnitIsConnected, UnitClass, UnitRace, UnitSex,
-				UnitLevel, UnitInRaid, UnitIsVisible, GetGuildInfo =
+				UnitLevel, UnitInRaid, UnitIsVisible, GetGuildInfo, GetRaidRosterInfo =
 	      IsInInstance, UnitName, UnitIsConnected, UnitClass, UnitRace, UnitSex,
-		  		UnitLevel, UnitInRaid, UnitIsVisible, GetGuildInfo
+		  		UnitLevel, UnitInRaid, UnitIsVisible, GetGuildInfo, GetRaidRosterInfo
 	local time, difftime = time, difftime
 	local R_ACTIVE, R_OFFLINE, R_LEFT = 1, 2, 3
 
@@ -727,15 +759,21 @@
 					g_loot.raiders[name] = { needinfo=true }
 				end
 				local r = g_loot.raiders[name]
+				-- We grab a bunch of return values here, but only pay attention to
+				-- them under specific circumstances.
+				local grri_name, connected, subgroup, level, class, _
+				grri_name, _, subgroup, level, _, class, connected = GetRaidRosterInfo(i)
+				assert(name==grri_name, "UnitName =/= grri_name of same raidindex")
+				r.subgroup = subgroup
 				if r.needinfo and UnitIsVisible(unit) then
 					r.needinfo = nil
-					r.class    = select(2,UnitClass(unit))
+					r.class    = class    --select(2,UnitClass(unit))
 					r.race     = select(2,UnitRace(unit))
 					r.sex      = UnitSex(unit)
-					r.level    = UnitLevel(unit)
+					r.level    = level    --UnitLevel(unit)
 					r.guild    = GetGuildInfo(unit)
 				end
-				local connected = UnitIsConnected(unit)
+				--local connected = UnitIsConnected(unit)
 				if connected and r.online ~= R_ACTIVE then
 					r.join = r.join or now
 					r.online = R_ACTIVE
@@ -870,7 +908,7 @@
 		-- This is only a 'while' to make jumping out of it easy and still do cleanup below.
 		while local_override or ((iquality >= self.threshold) and not opts.itemfilter[itemid]) do
 			if (self.rebroadcast and (not from)) and not local_override then
-				self:broadcast('loot', recipient, itemid, count)
+				self:vbroadcast('loot', recipient, itemid, count)
 			end
 			if (not self.enabled) and (not local_override) then break end
 			local signature = recipient .. iname .. (count or "")
@@ -1211,32 +1249,6 @@
 	addon.sender_list.namesI = byindex
 end
 
--- Message sending.
--- See OCR_funcs.tag at the end of this file for incoming message treatment.
-do
-	local function assemble(...)
-		local msg = ...
-		for i = 2, select('#',...) do
-			msg = msg .. '\a' .. (select(i,...) or "")
-		end
-		return msg
-	end
-
-	-- broadcast('tag', <stuff>)
-	function addon:broadcast(...)
-		local msg = assemble(...)
-		self.dprint('comm', "<broadcast>:", msg)
-		-- the "GUILD" here is just so that we can also pick up on it
-		self:SendCommMessage(self.ident, msg, self.debug.comm and "GUILD" or "RAID")
-	end
-	-- whispercast(<to>, 'tag', <stuff>)
-	function addon:whispercast(to,...)
-		local msg = assemble(...)
-		self.dprint('comm', "<whispercast>@", to, ":", msg)
-		self:SendCommMessage(self.identTg, msg, "WHISPER", to)
-	end
-end
-
 function addon:DoPing()
 	self:Print("Give me a ping, Vasili. One ping only, please.")
 	self.sender_list.active = {}
@@ -1361,7 +1373,7 @@
 		possible_st:SetData(g_loot)
 	end
 
-	self.status_text = ("%s communicating as ident %s commrev %d"):format(self.revision,self.ident,self.commrev)
+	self.status_text = ("%s communicating as ident %s commrev %s"):format(self.revision,self.ident,self.commrev)
 	self:RegisterComm(self.ident)
 	self:RegisterComm(self.identTg, "OnCommReceivedNocache")
 
@@ -1418,11 +1430,11 @@
 	addon.recent_boss = create_new_cache ('boss', 10, fixup_durations)
 
 	-- Similar to _do_loot, but duration+ parms only present when locally generated.
-	local function _do_boss (self, reason, bossname, intag, duration, raiders)
-		self.dprint('loot',">>_do_boss, R:", reason, "B:", bossname, "T:", intag,
-		            "D:", duration, "RL:", (raiders and #raiders or 'nil'))
+	local function _do_boss (self, reason, bossname, intag, maxsize, duration)
+		self.dprint('loot',">>_do_boss, R:", reason, "B:", bossname,
+		            "T:", intag, "MS:", maxsize, "D:", duration)
 		if self.rebroadcast and duration then
-			self:broadcast('boss', reason, bossname, intag)
+			self:vbroadcast('boss', reason, bossname, intag, maxsize)
 		end
 		-- This is only a loop to make jumping out of it easy, and still do cleanup below.
 		while self.enabled do
@@ -1447,8 +1459,9 @@
 					bossname	= bossname,
 					reason		= reason,
 					instance	= intag,
-					duration	= duration,      -- these two deliberately may be nil
-					raiderlist	= raiders and table.concat(raiders, ", ")
+					duration	= duration,      -- deliberately may be nil
+					raidersnap	= CopyTable(g_loot.raiders),
+					maxsize		= maxsize,
 				}
 				tinsert(candidates,c)
 			end
@@ -1456,7 +1469,7 @@
 		end
 		self.dprint('loot',"<<_do_boss out")
 	end
-	-- No wrapping layer for now
+	-- This exposes the function to OCR, and can be a wrapper layer later.
 	addon.on_boss_broadcast = _do_boss
 
 	function addon:_mark_boss_kill (index)
@@ -2028,6 +2041,45 @@
 
 ------ Player communication
 do
+	local select, tconcat, strsplit = select, table.concat, strsplit
+	--[[ old way:  repeated string concatenations, BAD
+		 new way:  new table on every call, BAD
+	local msg = ...
+	for i = 2, select('#',...) do
+		msg = msg .. '\a' .. (select(i,...) or "")
+	end
+	return msg
+	]]
+	local function assemble(t,...)
+		if select('#',...) > 0 then
+			local msg = {t,...}
+			-- tconcat requires strings, but T is known to be one already
+			for i = 2, #msg do
+				msg[i] = tostring(msg[i]) or ""
+			end
+			return tconcat (msg, '\a')
+		end
+		return t
+	end
+
+	-- broadcast('tag', <stuff>)
+	-- vbroadcast('tag', <stuff>)
+	function addon:vbroadcast(tag,...)
+		return self:broadcast(self.commrev..tag,...)
+	end
+	function addon:broadcast(tag,...)
+		local msg = assemble(tag,...)
+		self.dprint('comm', "<broadcast>:", msg)
+		-- the "GUILD" here is just so that we can also pick up on it
+		self:SendCommMessage(self.ident, msg, self.debug.comm and "GUILD" or "RAID")
+	end
+	-- whispercast(<to>, 'tag', <stuff>)
+	function addon:whispercast(to,...)
+		local msg = assemble(...)
+		self.dprint('comm', "<whispercast>@", to, ":", msg)
+		self:SendCommMessage(self.identTg, msg, "WHISPER", to)
+	end
+
 	local function adduser (name, status, active)
 		if status then addon.sender_list.names[name] = status end
 		if active then addon.sender_list.active[name] = active end
@@ -2059,12 +2111,21 @@
 		adduser (sender, nil, true)
 		addon:CHAT_MSG_LOOT ("broadcast", recip, item, count, sender, extratext)
 	end
+	OCR_funcs['16loot'] = OCR_funcs.loot
 
 	OCR_funcs.boss = function (sender, _, reason, bossname, instancetag)
-		addon.dprint('comm', "DOTboss, sender", sender, "reason", reason, "name", bossname, "it", instancetag)
+		addon.dprint('comm', "DOTboss, sender", sender, "reason", reason,
+			"name", bossname, "it", instancetag)
 		if not addon.enabled then return end
 		adduser (sender, nil, true)
-		addon:on_boss_broadcast (reason, bossname, instancetag)
+		addon:on_boss_broadcast (reason, bossname, instancetag, --[[maxsize=]]0)
+	end
+	OCR_funcs['16boss'] = function (sender, _, reason, bossname, instancetag, maxsize)
+		addon.dprint('comm', "DOTboss16, sender", sender, "reason", reason,
+			"name", bossname, "it", instancetag, "size", maxsize)
+		if not addon.enabled then return end
+		adduser (sender, nil, true)
+		addon:on_boss_broadcast (reason, bossname, instancetag, maxsize)
 	end
 
 	OCR_funcs.bcast_req = function (sender)