Quantcast

* 2.3.7

Alar of Daggerspine [02-27-15 - 15:04]
* 2.3.7
** Fix: Party display for In Progress missions sometimes was not not
showing the right followers.
** Fix: Matchmaking was trying too hard te get the lowest usefull
follower, sometimes not achieving the best possible score

Signed-off-by: Alar of Daggerspine <alar@aspide.it>
Filename
CHANGELOG.txt
GarrisonCommander.lua
GarrisonCommander.toc
Init.lua
MatchMaker.lua
PartyCache.lua
diff --git a/CHANGELOG.txt b/CHANGELOG.txt
index 052b4e2..02513bc 100644
--- a/CHANGELOG.txt
+++ b/CHANGELOG.txt
@@ -1,8 +1,4 @@
 ==GarrisonCommander helps you when choosing the right follower for the right mission==
-* 2.3.6.2
-** Fix: 132x ...rfaceGarrisonCommander\GarrisonCommander-2.3.6.1.lua:3123: attempt to index field 'xp' (a nil value)
-* 2.3.6
-** Fix: Removed on mission follower time info because given by Blizzards
-** Fix: A lua error was appearing on mission completion
-** Fix: Mission control was not considering some generic reward missions
-** Feature: On failure in completion, mission rewards are grayed out
+* 2.3.7
+** Fix: Party display for In Progress missions sometimes was not not showing the right followers.
+** Fix: Matchmaking was trying too hard te get the lowest usefull follower, sometimes not achieving the best possible score
\ No newline at end of file
diff --git a/GarrisonCommander.lua b/GarrisonCommander.lua
index 1fd40c2..c5d91cb 100644
--- a/GarrisonCommander.lua
+++ b/GarrisonCommander.lua
@@ -570,7 +570,7 @@ function addon:AddFollowersToTooltip(missionID)
 			GameTooltip:AddDoubleLine(self:GetFollowerData(followerID,'fullname','none'),self:GetFollowerStatus(followerID,true,true))
 		end
 	end
-	local perc=self:GetParty(missionID,'perc')
+	local perc=party.perc
 	local q=self:GetDifficultyColor(perc)
 	GameTooltip:AddDoubleLine(GARRISON_MISSION_SUCCESS,format(GARRISON_MISSION_PERCENT_CHANCE,perc),nil,nil,nil,q.r,q.g,q.b)
 	for _,i in pairs (dbcache.ignored[missionID]) do
@@ -663,16 +663,6 @@ function addon:CreatePrivateDb()
 					['*']={
 					}
 				},
-				running={
-					["*"]={
-						followers={},
-						started=0,
-						duration=0
-					}
-				},
-				runningIndex={
-					["*"]=0
-				},
 				missionControl={
 					allowedRewards = {
 						followerEquip=true,
@@ -711,6 +701,8 @@ function addon:CreatePrivateDb()
 	true)
 	dbcache=self.privatedb.profile
 	cache=self.private.profile
+	cache.running=nil
+	cache.runningIndex=nil
 end
 function addon:SetClean()
 	dirty=false
@@ -765,27 +757,9 @@ function addon:EventGARRISON_MISSION_STARTED(event,missionID,...)
 --@debug@
 	ns.xprint(event,missionID,...)
 --@end-debug@
---				running={
---					["*"]={
---						followers={},
---						started=0,
---						duration=0
---					}
---				},
-	dbcache.running[missionID].started=time()
-	dbcache.running[missionID].duration=select(2,G.GetPartyMissionInfo(missionID))
-	wipe(dbcache.running[missionID].followers)
 	wipe(dbcache.ignored[missionID])
 	local party=self:GetParty(missionID)
-	if party then
-		for i=1,#party.members do
-			local m=party.members[i]
-			if (m) then
-				tinsert(dbcache.running.followers,m)
-				dbcache.runningIndex[m]=missionID
-			end
-		end
-	end
+	wipe(party.members) -- I remove preset party, so PartyCache will refill it with the ones from the actual mission
 	local v=dbcache.seen[missionID]
 	local span=time()-(tonumber(v) or time())
 	if (span > db.lifespan[missionID]) then
@@ -837,7 +811,6 @@ function addon:EventGARRISON_MISSION_COMPLETE_RESPONSE(event,missionID,completed
 --@end-debug@
 	dbcache.history[missionID][time()]={result=100,success=rewards}
 	dbcache.seen[missionID]=nil
-	dbcache.running[missionID]=nil
 	dbcache.seen[missionID]=nil
 end
 -----------------------------------------------------
@@ -1795,131 +1768,6 @@ do
 			end
 		end
 	end
-	function addon:RenderFollowerPageMissionListOld(frame,followerID,force)
-		ns.xprint("hook",followerID,force)
-		if (followerID==lastFollowerID and not force) then return end
-		lastFollowerID=followerID
-		local i=0
-		if (not self:IsFollowerList()) then return end
-		if (not GCFMissions.Missions) then GCFMissions.Missions={} end
-		if (not Busystatusmessage) then Busystatusmessage=C(BUSY_MESSAGE,"Red)") end
-		-- frame has every info you can need on a follower, but here I dont really need them, maybe just counters
-		--ns.xdump(table.Counters)
-		ns.xprint("Passed",followerID)
-		local followerName=self:GetFollowerData(followerID,'name',true)
-		repeat -- a poor man goto
-			if (type(frame.followerID)=="number") then
-				GCFBusyStatus:SetText(NOT_COLLECTED)
-				GCFBusyStatus:SetTextColor(C.Red())
-				self:RenderFollowerButton(GCFMissions.Header,followerID)
-				break
-			end
-
-			local index=new()
-			local partyIndex=new()
-
-			local status=self:GetFollowerStatus(followerID)
-			local list
-			local m1,m2,m3,perc,numFollowers=nil,nil,nil,0,""
-			if (status ~= AVAILABLE and status ~= GARRISON_FOLLOWER_IN_PARTY) then
-				if (status==GARRISON_FOLLOWER_ON_MISSION) then
-					local missionID=dbcache.runningIndex[followerID]
-					if (not missionID) then return end
-					list=GMF.MissionTab.MissionList.inProgressMissions
-					m1=followerID
-					perc=select(4,G.GetPartyMissionInfo(missionID))
-					for j=1,#list do
-						index[list[j].missionID]=j
-					end
-					local pos=index[missionID]
-					if (not pos) then return end
-					numFollowers=#list[index[missionID]].followers
-					tinsert(partyIndex,-missionID)
-					GCFBusyStatus:SetText(GARRISON_FOLLOWER_ON_MISSION)
-				else
-					GCFBusyStatus:SetText(self:GetFollowerStatus(followerID,false,true)) -- no time, colored
-				end
-				GCFBusyStatus:SetTextColor(C.Red())
-
-			else
-				GCFBusyStatus:SetText(Busystatusmessage)
-				list=GMF.MissionTab.MissionList.availableMissions
-				for j=1,#list do
-					index[list[j].missionID]=j
-				end
-				for k,_ in pairs(parties) do
-					tinsert(partyIndex,k)
-				end
-				table.sort(partyIndex,function(a,b) return parties[a].perc > parties[b].perc end)
-			end
-			self:RenderFollowerButton(GCFMissions.Header,followerID)
-			-- Scanning mission list
-			for z = 1,#partyIndex do
-				local missionID=partyIndex[z]
-				if not(tonumber(missionID)) then
-	--@debug@
-					ns.xprint("missionid not a number",missionID)
-					self:Dump("partyIndex table",partyIndex)
-	--@end-debug@
-					perc=-1 --(lowering perc  to ignore this mission
-				end
-
-				if (missionID>0) then
-					local p=parties[missionID]
-					m1,m2,m3,perc=p.members[1],p.members[2],p.members[3],tonumber(p.perc) or 0
-					if (m3) then
-						numFollowers=3
-					elseif(m2) then
-						numFollowers=2
-					else
-						numFollowers=1
-					end
-				else
-					missionID=abs(missionID)
-				end
-				if (perc>MINPERC and ( m1 == followerID or m2==followerID or m3==followerID)) then
-					i=i+1
-					local mission=list[index[missionID]]
-					local panel=GCFMissions.Missions[i]
-					if (not panel) then
-						panel=CreateFrame("Button",nil,GCFMissions,"GarrisonCommanderMissionListButtonTemplate")
-						self:SafeHookScript(panel,"OnClick","OnClick_GarrisonMissionButton",true)
-
-						if (i==1) then
-							panel:SetPoint("TOPLEFT",GCFMissions.Header,"BOTTOMLEFT")
-						else
-							panel:SetPoint("TOPLEFT",GCFMissions.Missions[i-1],"BOTTOMLEFT")
-							panel:SetPoint("TOPRIGHT",GCFMissions.Missions[i-1],"BOTTOMRIGHT")
-						end
-						tinsert(GCFMissions.Missions,panel)
-						--Creo una riga nuova
-						panel:SetScale(0.6)
-						panel.fromFollowerPage=true
-						panel.LocBG:SetPoint("LEFT")
-					end
-					panel.info=mission
-					--panel.id=index[missionID]
-					self:BuildMissionButton(panel)
-					local q=self:GetDifficultyColor(perc)
-					panel.Percent:SetFormattedText(GARRISON_MISSION_PERCENT_CHANCE,perc)
-					panel.Percent:SetTextColor(q.r,q.g,q.b)
-					panel.NumMembers:SetFormattedText(GARRISON_MISSION_TOOLTIP_NUM_REQUIRED_FOLLOWERS,numFollowers)
-					panel:Show()
-					if (i>= MAXMISSIONS) then break end
-				end
-			end
-			del(partyIndex)
-			del(index)
-		until true
-		if (i>0) then
-			GCFBusyStatus:SetPoint("TOPLEFT",GCFMissions.Missions[i],"BOTTOMLEFT",0,-5)
-		else
-			GCFBusyStatus:SetPoint("TOPLEFT",GCFMissions.Header,"BOTTOMLEFT",0,-5)
-		end
-		i=i+1
-		for x=i,#GCFMissions.Missions do GCFMissions.Missions[x]:Hide() end
-	end
-
 end
 ---
 --Initial one time setup
@@ -3045,7 +2893,6 @@ function addon:AddThreatsToButton(button,mission,missionID,bigscreen)
 					button.Env:SetScript("OnEnter",nil)
 					button.Env:Hide()
 				end
-				--local counteredThreats=P:CalculateThreats(party.members,missionID)
 				for i=1,#mission.enemies do
 					local enemy=mission.enemies[i]
 					for mechanicID, mechanic in pairs(enemy.mechanics) do
diff --git a/GarrisonCommander.toc b/GarrisonCommander.toc
index 25248d9..e1d9d7d 100644
--- a/GarrisonCommander.toc
+++ b/GarrisonCommander.toc
@@ -5,7 +5,7 @@
 ## Notes-frFR: Vous aide au moment de choisir le droit utilisateur pour la bonne mission
 ## Author: Alar of Daggerspine
 ## Version: @project-version@
-## X-Version: 2.3.5
+## X-Version: 2.3.7
 ## X-Revision: @project-abbreviated-hash@
 ## eMail: alar@aspide.it
 ## URL: http://wow.aspide.it
@@ -36,7 +36,3 @@ MatchMaker.lua
 FollowerRecruiting.lua
 GarrisonCommander.xml
 RelNotes.lua
-#@do-not-package@
-Debug.lua
-#@end-do-not-package@
-
diff --git a/Init.lua b/Init.lua
index 4b952fb..f8105a3 100644
--- a/Init.lua
+++ b/Init.lua
@@ -113,8 +113,241 @@ ns.OnLeave=function() GameTooltip:Hide() end

 -------------------- to be estracted to CountersCache
 --
-local G=C_Garrison
-ns.Abilities=setmetatable({},{
-	__index=function(t,k) rawset(t,k,G.GetFollowerAbilityName(k)) return rawget(t,k) end
-})
+--local G=C_Garrison
+--ns.Abilities=setmetatable({},{
+--	__index=function(t,k) rawset(t,k,G.GetFollowerAbilityName(k)) return rawget(t,k) end
+--})
+--
+--
+
+--[===[@non-debug@
+if true then return end
+--@end-non-debug@]===]
+--@do-not-package@
+local me, ns = ...
+local addon=ns.addon --#addon
+local L=ns.L
+local D=ns.D
+local C=ns.C
+local AceGUI=ns.AceGUI
+local _G=_G
+local pp=print
+_G.GAC=addon
+
+--- Enable a trace for every function call. It's a VERY heavy debug
+--
+if ns.HD then
+local memorysinks={}
+local callstack={}
+local lib=LibStub("LibInit")
+for k,v in pairs(addon) do
+	if (type(v))=="function" and not lib[k] then
+		local wrapped
+		do
+			local original=addon[k]
+			wrapped=function(...)
+				pp(k)
+				tinsert(callstack,k)
+				local membefore=GetAddOnMemoryUsage("GarrisonCommander")
+				local a1,a2,a3,a4,a5,a6,a7,a8,a9=original(...)
+				local memafter=GetAddOnMemoryUsage("GarrisonCommander")
+				tremove(callstack)
+				memorysinks[k].mem=memorysinks[k].mem+memafter-membefore
+				memorysinks[k].calls=memorysinks[k].calls+1
+				if (#callstack) then
+					memorysinks[k].callers=strjoin("->",unpack(callstack))
+				else
+					memorysinks[k].callers="main"
+				end
+				if (memafter-membefore > 20) then
+					pp(C(k,'Red'),'used ',memafter-membefore)
+				end
+				return a1,a2,a3,a4,a5,a6,a7,a8,a9
+			end
+		end
+		addon[k]=wrapped
+		memorysinks[k]={mem=0,calls=0,callers=""}
+	end
+end
+function addon:ResetSinks()
+	for k,v in pairs(memorysinks) do
+		memorysinks[k].mem=0
+		memorysinks[k].calls=0
+	end
+end
+local sorted={}
+function addon:DumpSinks()
+	local scroll=self:GetScroller("Sinks",nil,400,1000)
+	wipe(sorted)
+	for k,v in pairs(memorysinks) do
+		if v.mem then
+			tinsert(sorted,format("Mem %06d (calls: %03d) Mem per call:%03.2f  Callstack:%s(%s)",v.mem,v.calls,v.mem/v.calls,C(k,"Orange"),v.callers))
+		end
+	end
+	table.sort(sorted,function(a,b) return a>b end)
+	self:cutePrint(scroll,sorted)
+end
+end
+function addon:GetScroller(title,type,h,w)
+	h=h or 800
+	w=w or 400
+	type=type or "Frame"
+	local scrollerWindow=AceGUI:Create("Frame")
+	scrollerWindow:SetTitle(title)
+	scrollerWindow:SetLayout("Fill")
+	--local scrollcontainer = AceGUI:Create("SimpleGroup") -- "InlineGroup" is also good
+	--scrollcontainer:SetFullWidth(true)
+	--scrollcontainer:SetFullHeight(true) -- probably?
+	--scrollcontainer:SetLayout("Fill") -- important!
+	--scrollerWindow:AddChild(scrollcontainer)
+	local scroll = AceGUI:Create("ScrollFrame")
+	scroll:SetLayout("Flow") -- probably?
+	scroll:SetFullWidth(true)
+	scroll:SetFullHeight(true)
+	scrollerWindow:AddChild(scroll)
+	scrollerWindow:SetCallback("OnClose","Release")
+	scrollerWindow:SetHeight(h)
+	scrollerWindow:SetWidth(w)
+	scrollerWindow:SetPoint("CENTER")
+	scrollerWindow:Show()
+	scroll.AddRow=function (self,...) return addon:AddRow(self,...) end
+	scroll.addRow=scroll.AddRow
+	return scroll
+end
+function addon:AddRow(obj,text,...)
+--@debug@
+	assert(obj)
+--@end-debug@
+	if (obj) then
+		local l=AceGUI:Create("Label")
+		l:SetText(text)
+		l:SetColor(...)
+		l:SetFullWidth(true)
+		obj:AddChild(l)
+	end
+end
+function addon:cutePrint(scroll,level,k,v)
+	if (type(level)=="table") then
+		for k,v in pairs(level) do
+			self:cutePrint(scroll,"",k,v)
+		end
+		return
+	end
+	if (type(v)=="table") then
+		self:AddRow(scroll,level..C(k,"Azure")..":" ..C("Table","Orange"))
+		for kk,vv in pairs(v) do
+			self:cutePrint(scroll,level .. "  ",kk,vv)
+		end
+	else
+		if (type(v)=="string" and v:sub(1,2)=='0x') then
+			v=v.. " " ..tostring(self:GetFollowerData(v,'name'))
+		end
+		self:AddRow(scroll,level..C(k,"White")..":" ..C(v,"Yellow"))
+	end
+end
+function addon:DumpFollower(name)
+	local follower=self:GetFollowerData(name)
+	if (follower) then
+		local scroll=self:GetScroller(follower.name)
+		self:cutePrint(scroll,follower)
+	end
+
+end
+function addon:DumpStatus(title)
+	local scroll=self:GetScroller(title)
+	for i=1,#followersCache do
+		local followerID=followersCache[i].followerID
+		scroll:AddRow(format("%s (%s): %d",self:GetFollowerData(followerID,'fullname'),self:GetFollowerData(followerID,'followerID'),G.GetFollowerXP(followerID)))
+	end
+	scroll:AddRow("Garrison resources: " .. select(2,GetCurrencyInfo(GARRISON_CURRENCY)))
+	scroll:AddRow("Money: " .. GetMoneyString(GetMoney()))
+end
+function addon:DumpFollowers()
+	local scroll=self:GetScroller("Followers Cache (" .. #followersCache ..")"  )
+	self:cutePrint(scroll,followersCache)
+end
+function addon:DumpFollowerMissions(missionID)
+	local scroll=self:GetScroller("FollowerMissions " .. self:GetMissionData(missionID,'name'))
+	self:cutePrint(scroll,followerMissions.missions[missionID])
+end
+function addon:DumpIgnored()
+	local scroll=self:GetScroller("Ignored")
+	self:cutePrint(scroll,self.privatedb.profile.ignored)
+end
+function addon:DumpMission(missionID)
+	local scroll=self:GetScroller("MissionCache " .. self:GetMissionData(missionID,'name'))
+	self:cutePrint(scroll,cache.missions[missionID])
+end
+function addon:DumpMissions()
+	local scroll=self:GetScroller("MissionCache")
+	for id,data in pairs(cache.missions) do
+		self:cutePrint(scroll,id .. '.'..data.name)
+	end
+end
+---
+-- Debug function
+--@param missionID Identificativo missione
+function addon:DumpCounters(missionID)
+	local scroll=self:GetScroller("Counters " .. self:GetMissionData(missionID,'name'))
+	self:cutePrint(scroll,counters[missionID])
+	self:cutePrint(scroll,"Lista per follower","","")
+	self:cutePrint(scroll,counterFollowerIndex[missionID])
+	self:cutePrint(scroll,"Lista per threat","","")
+	self:cutePrint(scroll,counterThreatIndex[missionID])
+end
+function addon:Dump(title,data)
+	local scroll=self:GetScroller(title)
+	self:cutePrint(scroll,data)
+	return scroll
+end
+function addon:DumpCounterers(missionID)
+	local scroll=self:GetScroller("Counterers " .. self:GetMissionData(missionID,'name'))
+	self:cutePrint(scroll,cache.missions[missionID].counterers)
+end
+function addon:DumpParty(missionID)
+	local scroll=self:GetScroller("Party " .. self:GetMissionData(missionID,'name'))
+	self:cutePrint(scroll,parties[missionID])
+end
+function addon:DumpAgeDb()
+	local t=new()
+	for i,v in pairs(dbcache.seen) do
+		tinsert(t,format("%80s %s %d",self:GetMissionData(i,'name'),date("%d/%m/%y %H:%M:%S",v),ns.wowhead[i]))
+	end
+	local scroll=self:GetScroller("Expire db")
+	self:cutePrint(scroll,t)
+	del(t)
+end
+_G.GCF=GCF
+_G.MW=173
+--[[
+PlaySound("UI_Garrison_CommandTable_Open");
+	PlaySound("UI_Garrison_CommandTable_Close");
+	PlaySound("UI_Garrison_Nav_Tabs");
+	PlaySound("UI_Garrison_Nav_Tabs");
+	PlaySound("UI_Garrison_CommandTable_SelectMission");
+			PlaySound("UI_Garrison_CommandTable_IncreaseSuccess");
+			PlaySound("UI_Garrison_CommandTable_100Success");
+			PlaySound("UI_Garrison_CommandTable_ReducedSuccessChance");
+				PlaySound("UI_Garrison_Mission_Threat_Countered");
+			PlaySoundKitID(43507);	-- 100% chance reached
+	PlaySound("UI_Garrison_CommandTable_AssignFollower");
+			PlaySound("UI_Garrison_CommandTable_UnassignFollower");
+		PlaySound("UI_Garrison_Mission_Threat_Countered");
+	PlaySound("UI_Garrison_CommandTable_MissionStart");
+	PlaySound("UI_Garrison_CommandTable_ViewMissionReport");
+		PlaySound("UI_Garrison_Mission_Complete_Encounter_Chance");
+	PlaySound("UI_Garrison_CommandTable_Nav_Next");
+		PlaySound("UI_Garrison_CommandTable_ChestUnlock_Gold_Success");
+		PlaySound("UI_Garrison_Mission_Threat_Countered");
+		PlaySound("UI_Garrison_MissionEncounter_Animation_Generic");
+			PlaySoundKitID(currentAnim.castSoundID);
+		PlaySoundKitID(currentAnim.impactSoundID);
+			PlaySound("UI_Garrison_Mission_Complete_Encounter_Fail");
+			PlaySound("UI_Garrison_Mission_Complete_Mission_Success");
+		PlaySound("UI_Garrison_CommandTable_MissionSuccess_Stinger");
+		PlaySound("UI_Garrison_Mission_Complete_MissionFail_Stinger");
+		PlaySound("UI_Garrison_CommandTable_ChestUnlock");
+				PlaySound("UI_Garrison_CommandTable_Follower_LevelUp");

+--]]
+--@end-do-not-package@
diff --git a/MatchMaker.lua b/MatchMaker.lua
index d248934..c927c79 100644
--- a/MatchMaker.lua
+++ b/MatchMaker.lua
@@ -71,6 +71,8 @@ filters.gold=filters.generic
 filters.equip=filters.generic
 filters.followerEquip=filters.generic
 filters.epic=filters.generic
+local nop={addRow=function() end}
+local scroller=nop
 local function CreateFilter(missionClass)
 	local code = [[
 	local filters,xprint,pairs = ...
@@ -100,18 +102,27 @@ local function AddMoreFollowers(self,mission,scores,justdo)
 	local missionID=mission.missionID
 	local filter=filters[mission.class]
 	local missionScore=self:MissionScore(mission)
+
 	for p=1,P:FreeSlots() do
-		xprint("Assigning slot ",p+1, " starting with mission score ",missionScore)
+		if dbg then
+			scroller:AddRow("--------------------- Slot " .. P:CurrentSlot() .. " ------------------")
+		end
 		local candidate=nil
 		local candidateScore=missionScore
 		for i=1,#scores do
 			local score,followerID=strsplit('|',scores[i])
-			xprint(C(score,'yellow'),G.GetFollowerName(followerID),filter(followerID,missionID))
 			if (not filter(followerID,missionID) and not P:IsIn(followerID)) then
 				P:AddFollower(followerID)
 				local newScore=self:MissionScore(mission)
-				xprint(C(score,'yellow'),G.GetFollowerName(followerID),"changes score from ",candidateScore,"to",newScore)
-				if (newScore > missionScore or justdo) then
+				if dbg then
+					local c1,c2="green","red"
+					if newScore > candidateScore or justdo then
+						c1="red"
+						c2="green"
+					end
+					scroller:AddRow(addon:GetFollowerData(followerID,'fullname') .." changes score from " .. C(candidateScore,c1).." to "..C(newScore,c2))
+				end
+				if (newScore > candidateScore or justdo) then
 					candidate=followerID
 					candidateScore=newScore
 				end
@@ -119,8 +130,10 @@ local function AddMoreFollowers(self,mission,scores,justdo)
 			end
 		end
 		if candidate then
-			xprint(C(score,'yellow'),G.GetFollowerName(candidate),"assigned to slot",p+1)
-			P:AddFollower(candidate)
+			local slot=P:CurrentSlot()
+			if P:AddFollower(candidate) and dbg then
+				scroller:addRow(C("Slot " .. slot..":","Green").. " " .. addon:GetFollowerData(candidate,'fullname'))
+			end
 			candidate=nil
 		end
 	end
@@ -160,22 +173,26 @@ local function MatchMaker(self,missionID,party,includeBusy,onlyBest)
 		local firstmember
 		table.sort(scores)
 		if (dbg) then
-			local s=self:GetScroller("Scores")
-			self:cutePrint(s,scores)
+			for i=1,#scores do
+				local score,followerID=strsplit('|',scores[i])
+				local t=score:gsub("(%d%d%d)(%d)(%d)(%d)(%d*)","%1%% r:%2 t:%3d b:%4 Rank:%5 ") .. addon:GetFollowerData(followerID,'fullname') .. " " .. tostring(G.GetFollowerStatus(followerID))
+				scroller:addRow(t)
+			end
+		else
+			scroller=nop
 		end
-		xprint("           First member")
 		for i=#scores,1,-1 do
 			local score,followerID=strsplit('|',scores[i])
-			xprint(C(score,'yellow'),G.GetFollowerName(followerID))
 			if not firstmember and not filter(followerID,missionID) then
 				firstmember=followerID
 				break
 			end
 		end
 		if firstmember then
-			P:AddFollower(firstmember)
+			if P:AddFollower(firstmember) and dbg then
+				scroller:AddRow(C("Slot 1:","Green").. " " .. addon:GetFollowerData(firstmember,'fullname'))
+			end
 			if mission.numFollowers > 1 then
-				xprint("           AddMore 1")
 				AddMoreFollowers(self,mission,scores)
 			end
 		end
@@ -224,7 +241,16 @@ function addon:MatchMaker(missionID,party,includeBusy)
 end
 function addon:TestMission(missionID,includeBusy)
 	dbg=true
-	self:MatchMaker(missionID,nil,includeBusy)
+	scroller=self:GetScroller("Scores")
+	local party=new()
+	party.members=new()
+	self:MatchMaker(missionID,party,includeBusy)
+--@debug@
+	DevTools_Dump(party)
+--@end-debug@
+	del(party.members)
+	del(party)
+	scroller=nop
 	dbg=false
 end
 function addon:MatchDebug(d)
diff --git a/PartyCache.lua b/PartyCache.lua
index dbb3590..409e5cc 100644
--- a/PartyCache.lua
+++ b/PartyCache.lua
@@ -10,6 +10,9 @@ local wipe=wipe
 local tremove=tremove
 local tinsert=tinsert
 local pcall=pcall
+local type=type
+local pairs=pairs
+local format=format

 --
 -- Temporary party management
@@ -23,7 +26,11 @@ local parties=setmetatable({},{
 			followerUpgrade=0,
 			xpBonus=0,
 			gold=0,
+			goldMultiplier=1,
+			materialMultiplier=1,
 			resources=0,
+			totalTimeString="Not Running",
+			totalTimeSeconds=0
 		}) return t[k] end
 })
 function ns.inParty(missionID,followerID)
@@ -34,6 +41,19 @@ end
 local followerMissions=setmetatable({},{
 	__index=function(t,k)  rawset(t,k,{}) return t[k] end
 })
+local function addPartyMissionInfo(desttable,missionID)
+	if type(desttable) == "table" then
+		desttable.totalTimeString,
+		desttable.totalTimeSeconds,
+		desttable.isMissionTimeImproved,
+		desttable.perc,
+		desttable.partyBuffs,
+		desttable.isEnvMechanicCountered,
+		desttable.xpBonus,
+		desttable.materialMultiplier,
+		desttable.goldMultiplier = G.GetPartyMissionInfo(missionID)
+	end
+end
 ns.party={}
 local party=ns.party --#party
 local ID,maxFollowers,members,ignored,threats=0,1,{},{},{}
@@ -63,6 +83,9 @@ end
 function party:FreeSlots()
 	return maxFollowers-#members
 end
+function party:CurrentSlot()
+	return #members +1
+end
 function party:IsEmpty()
 	return maxFollowers>0 and #members==0
 end
@@ -137,15 +160,7 @@ function party:Close(desttable)
 		end
 	end
 	if (desttable) then
-		desttable.totalTimeString,
-		desttable.totalTimeSeconds,
-		desttable.isMissionTimeImproved,
-		desttable.perc,
-		desttable.partyBuffs,
-		desttable.isEnvMechanicCountered,
-		desttable.xpBonus,
-		desttable.materialMultiplier,
-		desttable.goldMultiplier = G.GetPartyMissionInfo(ID)
+		addPartyMissionInfo(desttable,ID)
 		if (ns.toc < 60100) then
 			desttable.goldMultiplier = 1
 		end
@@ -176,56 +191,6 @@ function party:Close(desttable)
 	wipe(threats)
 	return perc or 0
 end
-function party:CalculateThreats(followers,missionID)
-	local threats = {};
-	threats.full = {};
-	threats.partial = {};
-	threats.away = {};
-	threats.worker = {};
-	missionID=missionID or ID
-	local followerList=followers or members
-	for i = 1, #followerList do
-		local followerID = followerList[i];
-		local status=G.GetFollowerStatus(followerID)
-		local bias = G.GetFollowerBiasForMission(missionID, followerID);
-		if ( bias > -1.0 ) then
-			local abilities = G.GetFollowerAbilities(followerID);
-			for j = 1, #abilities do
-				for counterMechanicID in pairs(abilities[j].counters) do
-					if ( status ) then
-						if ( status == GARRISON_FOLLOWER_ON_MISSION ) then
-							local time = G.GetFollowerMissionTimeLeftSeconds(followerID);
-							if ( not threats.away[counterMechanicID] ) then
-								threats.away[counterMechanicID] = {};
-							end
-							table.insert(threats.away[counterMechanicID], time);
-						elseif ( status == GARRISON_FOLLOWER_WORKING ) then
-							threats.worker[counterMechanicID] = (threats.worker[counterMechanicID] or 0) + 1;
-						end
-					else
-						local isFullCounter = G.IsMechanicFullyCountered(missionID, followerID, counterMechanicID, abilities[j].id);
-						if ( isFullCounter ) then
-							threats.full[counterMechanicID] = (threats.full[counterMechanicID] or 0) + 1;
-						else
-							threats.partial[counterMechanicID] = (threats.partial[counterMechanicID] or 0) + 1;
-						end
-					end
-				end
-			end
-		end
-	end
-
-	for counter, times in pairs(threats.away) do
-		table.sort(times);
-	end
-	return threats;
-end
-function addon:GetBusyParty(missionID)
-	return self:GetParty(missionID).busy
-end
-function addon:GetReadyParty(missionID,key)
-	return self:GetParty(missionID)
-end
 function addon:GetParties()
 	return self:GetParty()
 end
@@ -233,12 +198,12 @@ function addon:GetParty(missionID,key)
 	if not missionID then return parties end
 	local party=parties[missionID]
 	if #party.members==0 and G.GetNumFollowersOnMission(missionID)>0 then
+		--Running Mission, taking followers from mission data
 		local followers=self:GetMissionData(missionID,'followers')
-		party.perc=select(4,G.GetPartyMissionInfo(missionID))
+		addPartyMissionInfo(party,missionID)
 		for i=1,#followers do
 			party.members[i]=followers[i]
 		end
-		--Running Mission, taking followers from mission data
 	end
 	if key then
 		return party[key]