Quantcast

6.1.0 update

Alar of Daggerspine [02-24-15 - 15:34]
6.1.0 update

Signed-off-by: Alar of Daggerspine <alar@aspide.it>
Filename
Debug.lua
FollowerCache.lua
GarrisonCommander-Broker/ldb.lua
GarrisonCommander.lua
GarrisonCommander.toc
Init.lua
MatchMaker.lua
MissionCache.lua
MissionCompletion.lua
MissionControl.lua
PartyCache.lua
doc.txt
libs/LibDeformat-3.0/LibDeformat-3.0.lua
diff --git a/Debug.lua b/Debug.lua
index 4afd57c..b8c6587 100644
--- a/Debug.lua
+++ b/Debug.lua
@@ -1,3 +1,4 @@
+if true then return end
 --@do-not-package@
 local me, ns = ...
 local addon=ns.addon --#addon
@@ -89,9 +90,9 @@ function addon:GetScroller(title,type,h,w)
 	return scroll
 end
 function addon:AddRow(obj,text,...)
---@debug@
+--[===[@debug@
 	assert(obj)
---@end-debug@
+--@end-debug@]===]
 	if (obj) then
 		local l=AceGUI:Create("Label")
 		l:SetText(text)
@@ -172,7 +173,7 @@ 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'))
diff --git a/FollowerCache.lua b/FollowerCache.lua
index e69de29..ff9f9df 100644
--- a/FollowerCache.lua
+++ b/FollowerCache.lua
@@ -0,0 +1,133 @@
+local me,ns=...
+local addon=ns.addon --#addon
+local holdEvents,releaseEvents=addon.holdEvents,addon.releaseEvents
+local xdump=ns.xdump
+--upvalue
+local C=ns.C
+local G=C_Garrison
+local GMF=GarrisonMissionFrame
+local type=type
+local select=select
+local pairs=pairs
+local tonumber=tonumber
+local tinsert=tinsert
+local Mbase = GarrisonMissionFrameFollowers
+local GARRISON_FOLLOWER_MAX_UPGRADE_QUALITY=GARRISON_FOLLOWER_MAX_UPGRADE_QUALITY
+local GARRISON_FOLLOWER_MAX_LEVEL=GARRISON_FOLLOWER_MAX_LEVEL
+local format=format
+local tostring=tostring
+local GetItemInfo=GetItemInfo
+local index={}
+local sorted={}
+local function keyToIndex(key)
+	if (not Mbase.followers or not next(Mbase.followers)) then
+		Mbase.dirtyList=false
+		Mbase.followers = G.GetFollowers();
+	end
+	local idx=key and index[key] or nil
+	if (idx and idx <= #Mbase.followers) then
+		if Mbase.followers[idx].followerID==key then
+			return idx
+		else
+			idx=nil
+		end
+	end
+	wipe(index)
+	wipe(sorted)
+	for i=1,#Mbase.followers do
+		if Mbase.followers[i].isCollected then
+			index[Mbase.followers[i].followerID]=i
+			tinsert(sorted,i)
+			if Mbase.followers[i].followerID==key then
+				idx=i
+			end
+		end
+	end
+	return idx
+end
+local maxrank=GARRISON_FOLLOWER_MAX_UPGRADE_QUALITY*1000+GARRISON_FOLLOWER_MAX_LEVEL
+local function AddExtraData(follower,refreshrank)
+	follower.rank=follower.level < GARRISON_FOLLOWER_MAX_LEVEL and follower.level or follower.iLevel
+	follower.qLevel=follower.quality*1000+follower.level
+	follower.coloredname=C(follower.name,tostring(follower.quality))
+	follower.fullname=format("%3d %s",follower.rank,follower.coloredname)
+	follower.maxed=follower.qLevel>=maxrank
+	local weaponItemID, weaponItemLevel, armorItemID, armorItemLevel = G.GetFollowerItems(follower.followerID);
+	follower.weaponItemID=weaponItemID
+	follower.weaponItemLevel=weaponItemLevel
+	follower.armorItemID=armorItemID
+	follower.armorItemLevel=armorItemLevel
+	follower.weaponQuality=select(3,GetItemInfo(weaponItemID))
+	follower.armorQuality=select(3,GetItemInfo(armorItemID))
+	follower.abilities=G.GetFollowerAbilities(follower.followerID)
+end
+function addon:FollowerCacheInit()
+	GarrisonFollowerList_UpdateFollowers(Mbase)
+end
+function addon:CanCounter(followerID,id)
+	local abilities=self:GetFollowerData(followerID,'abilities')
+	for i=1,#abilities do
+		local ability=abilities[i]
+		for k,v in pairs(ability.counter) do
+			if (k==trait or v.name==trait) then
+				return true
+			end
+		end
+	end
+end
+function addon:HasTrait(followerID,trait)
+	local abilities=self:GetFollowerData(followerID,'abilities')
+	for i=1,#abilities do
+		local ability=abilities[i]
+		if ability.isTrait then
+			if ability.ID==trait then
+				return true
+			end
+		end
+	end
+end
+function addon:GetFollowerData(followerID,key,default)
+	local idx=keyToIndex(followerID)
+	local follower=Mbase.followers[idx]
+	if (not follower) then
+		ns.dprint("Not found",followerID,key,"at",idx,"len",#Mbase.followers)
+	end
+	if (key==nil) then
+		return follower
+	end
+	if (type(follower[key])~='nil') then
+		return follower[key]
+	end
+	AddExtraData(follower)
+	return follower[key] or default
+end
+local sorters={}
+sorters.leveldesc = function(a,b)
+	return (Mbase.followers[a].iLevel * 10 + Mbase.followers[a].level) >  (Mbase.followers[b].iLevel * 10 + Mbase.followers[b].level)
+end
+sorters.levelasc = function(a,b)
+	return (Mbase.followers[a].iLevel * 10 + Mbase.followers[a].level) <  (Mbase.followers[b].iLevel * 10 + Mbase.followers[b].level)
+end
+
+
+---@function
+-- Iterator function
+-- @param func type of sorting (can be mitted if we dont care)
+--
+function addon:GetFollowerIterator(func)
+	keyToIndex()
+	if (func) then
+		table.sort(sorted,sorters[func])
+	end
+	local f=Mbase.followers
+	return function(sorted,i)
+		i=i+1
+		local x = sorted[i]
+		if x then
+			local v=f[x] and f[x].followerID or nil
+			if v then
+				return i,v
+			end
+		end
+	end,sorted,0
+end
\ No newline at end of file
diff --git a/GarrisonCommander-Broker/ldb.lua b/GarrisonCommander-Broker/ldb.lua
index 46b94e1..824735a 100644
--- a/GarrisonCommander-Broker/ldb.lua
+++ b/GarrisonCommander-Broker/ldb.lua
@@ -1,8 +1,8 @@
 local me, ns = ...
 if (not LibStub:GetLibrary("LibDataBroker-1.1",true)) then
-	--@debug@
+	--[===[@debug@
 	print("Missing libdatabroker")
-	--@end-debug@
+	--@end-debug@]===]
 	return
 end
 if (LibDebug) then LibDebug() end
@@ -103,8 +103,8 @@ end
 function dataobj:OnLeave()
 	GameTooltip:Hide()
 end
---@debug@
+--[===[@debug@
 _G.GACDB=addon
---@end-debug@
+--@end-debug@]===]
 --function dataobj:OnClick(button)
 --end
diff --git a/GarrisonCommander.lua b/GarrisonCommander.lua
index 127d502..09a1795 100644
--- a/GarrisonCommander.lua
+++ b/GarrisonCommander.lua
@@ -24,7 +24,6 @@ local wipe=wipe
 local format=format
 local tostring=tostring
 local collectgarbage=collectgarbage
-local bigscreen=true
 local GMM=false
 local MP=false
 local MPGoodGuy=false
@@ -35,6 +34,10 @@ local pin=false
 local baseHeight
 local minHeight
 if (LibDebug) then LibDebug() end
+ns.bigscreen=true
+-- Blizzard functions override support
+local orig={} --#originals
+local over={} --#overridden
 local backdrop = {
 		--bgFile="Interface\\TutorialFrame\\TutorialFrameBackground",
 		bgFile="Interface\\DialogFrame\\UI-DialogBox-Background-Dark",
@@ -97,6 +100,8 @@ local GARRISON_MISSION_PERCENT_CHANCE="%d%%"-- GARRISON_MISSION_PERCENT_CHANCE
 --local GARRISON_CURRENCY=GARRISON_CURRENCY  --824
 --local GARRISON_FOLLOWER_MAX_UPGRADE_QUALITY=GARRISON_FOLLOWER_MAX_UPGRADE_QUALITY -- 4
 --local GARRISON_FOLLOWER_MAX_LEVEL=GARRISON_FOLLOWER_MAX_LEVEL -- 100
+local GARRISON_CURRENCY=GARRISON_CURRENCY
+local GetMoneyString=GetMoneyString
 local SHORTDATE=SHORTDATE.. " %s"
 local LEVEL=LEVEL -- Level
 local MISSING=ADDON_MISSING
@@ -117,7 +122,7 @@ local GMFMissionListButtons=GMF.MissionTab.MissionList.listScroll.buttons
 local GarrisonFollowerTooltip=GarrisonFollowerTooltip
 local GarrisonMissionFrameMissionsListScrollFrame=GarrisonMissionFrameMissionsListScrollFrame
 local IGNORE_UNAIVALABLE_FOLLOWERS=IGNORE.. ' ' .. UNAVAILABLE
-local IGNORE_UNAIVALABLE_FOLLOWERS_DETAIL=IGNORE.. ' ' .. GARRISON_FOLLOWER_INACTIVE .. ',' .. GARRISON_FOLLOWER_ON_MISSION ..',' .. GARRISON_FOLLOWER_WORKING.. ' ' .. GARRISON_FOLLOWERS
+local IGNORE_UNAIVALABLE_FOLLOWERS_DETAIL= GARRISON_FOLLOWER_ON_MISSION ..',' .. GARRISON_FOLLOWER_WORKING .. ' ' .. GARRISON_FOLLOWERS .. '. ' .. GARRISON_FOLLOWER_INACTIVE .. " are always ignored"
 local PARTY=PARTY -- "Party"
 local SPELL_TARGET_TYPE1_DESC=capitalize(SPELL_TARGET_TYPE1_DESC) -- any
 local SPELL_TARGET_TYPE4_DESC=capitalize(SPELL_TARGET_TYPE4_DESC) -- party member
@@ -176,13 +181,6 @@ local GCFMissions
 local GCFBusyStatus
 local GameTooltip=GameTooltip
 -- Want to know what I call!!
---local GarrisonMissionButton_OnEnter=GarrisonMissionButton_OnEnter
-local GarrisonFollowerList_UpdateFollowers=GarrisonFollowerList_UpdateFollowers
-local GarrisonMissionList_UpdateMissions=GarrisonMissionList_UpdateMissions
-local GarrisonMissionPage_ClearFollower=GarrisonMissionPage_ClearFollower
-local GarrisonMissionPage_UpdateMissionForParty=GarrisonMissionPage_UpdateMissionForParty
-local GarrisonMissionPage_SetFollower=GarrisonMissionPage_SetFollower
-local GarrisonMissionButton_SetRewards=GarrisonMissionButton_SetRewards
 local GetItemInfo=GetItemInfo
 local type=type
 local ITEM_QUALITY_COLORS=ITEM_QUALITY_COLORS
@@ -269,99 +267,109 @@ end


 --
---@debug@
-local origGarrisonMissionButton_OnEnter = _G.GarrisonMissionButton_OnEnter
-function _G.GarrisonMissionButton_OnEnter(this,button)
-	origGarrisonMissionButton_OnEnter(this,button)
-	GameTooltip:AddDoubleLine("ID:",this.info.missionID)
-	GameTooltip:Show()
-end
---@end-debug@
+
 -- These local will became conf var
 -- locally upvalued, doing my best to not interfere with other sorting modules,
 -- First time i am called to verride it I save it, so I give other modules a chance to hook it, too
 -- Could even do a trick and secureHook it at the expense of a double sort...
 local origGarrison_SortMissions
-
-function addon.Garrison_SortMissions_Chance(missionsList)
-	xprint("Sorting on chance")
-	local comparison
-	do
-		function comparison(mission1, mission2)
-			if type(mission1)~="table" or type(mission2) ~="table" then return end
-			local p1=parties[mission1.missionID]
-			local p2=parties[mission2.missionID]
-			if (p2.full and not p1.full) then return false end
-			if (p1.full and not p2.full) then return true end
-			if (p1.perc==p2.perc) then
-				return strcmputf8i(mission1.name, mission2.name) < 0
-			else
-				return p1.perc > p2.perc
-			end
+local sorters={}
+sorters.EndTime=function (mission1, mission2)
+	if type(mission1)~="table" or type(mission2) ~="table" then return end
+	if ns.toc >=60100 then
+		local p1=G.GetFollowerMissionTimeLeftSeconds(mission1.followers[1])
+		local p2=G.GetFollowerMissionTimeLeftSeconds(mission2.followers[1])
+		if (p1==p2) then
+			return strcmputf8i(mission1.name, mission2.name) < 0
+		else
+			return p1 < p2
+		end
+	else
+		local running=addon.privatedb.profile.running
+		local p1=tonumber(running[mission1.missionID].started)+tonumber(running[mission1.missionID].duration)
+		local p2=tonumber(running[mission2.missionID].started)+tonumber(running[mission2.missionID].duration)
+		if (p1==p2) then
+			return strcmputf8i(mission1.name, mission2.name) < 0
+		else
+			return p1 < p2
 		end
 	end
-	table.sort(missionsList, comparison);
 end
-function addon.Garrison_SortMissions_Age(missionsList)
-	local comparison
-	do
-		function comparison(mission1, mission2)
-			if type(mission1)~="table" or type(mission2) ~="table" then return end
-			local p1=parties[mission1.missionID]
-			local p2=parties[mission2.missionID]
-			if (p2.full and not p1.full) then return false end
-			if (p1.full and not p2.full) then return true end
-			local age1=tonumber(dbcache.seen[mission1.missionID]) or 0
-			local age2=tonumber(dbcache.seen[mission2.missionID]) or 0
-			if (age1==age2) then
-				return strcmputf8i(mission1.name, mission2.name) < 0
-			else
-				return age1 < age2
-			end
-		end
+sorters.Chance=function (mission1, mission2)
+	if type(mission1)~="table" or type(mission2) ~="table" then return end
+	local p1=addon:GetParty(mission1.missionID)
+	local p2=addon:GetParty(mission2.missionID)
+	if (p2.full and not p1.full) then return false end
+	if (p1.full and not p2.full) then return true end
+	if (p1.perc==p2.perc) then
+		return strcmputf8i(mission1.name, mission2.name) < 0
+	else
+		return p1.perc > p2.perc
 	end
-	table.sort(missionsList, comparison);
 end
-function addon.Garrison_SortMissions_Followers(missionsList)
-	local comparison
-	do
-		function comparison(mission1, mission2)
-			if type(mission1)~="table" or type(mission2) ~="table" then return end
-			local p1=mission1.numFollowers
-			local p2=mission2.numFollowers
-			if (p1==p2) then
-				return strcmputf8i(mission1.name, mission2.name) < 0
-			else
-				return p1 < p2
-			end
-		end
+sorters.Age=function (mission1, mission2)
+	if type(mission1)~="table" or type(mission2) ~="table" then return end
+	local p1=addon:GetMissionData(mission1.missionID,'offerEndTime')
+	local p2=addon:GetMissionData(mission2.missionID,'offerEndTime')
+	if (p1==p2) then
+		return strcmputf8i(mission1.name, mission2.name) < 0
+	else
+		return p1 < p2
 	end
-	table.sort(missionsList, comparison);
 end
-function addon.Garrison_SortMissions_Xp(missionsList)
-	local comparison
-	do
-		function comparison(mission1, mission2)
-			if type(mission1)~="table" or type(mission2) ~="table" then return end
-			local p1=addon:GetMissionData(mission1.missionID,'globalXp')
-			local p2=addon:GetMissionData(mission2.missionID,'globalXp')
-			if (p1==p2) then
-				return strcmputf8i(mission1.name, mission2.name) < 0
-			else
-				return p2 < p1
-			end
-		end
+sorters.Followers=function(mission1, mission2)
+	if type(mission1)~="table" or type(mission2) ~="table" then return end
+	local p1=addon:GetMissionData(mission1.missionID,'numFollowers')
+	local p2=addon:GetMissionData(mission2.missionID,'numFollowers')
+	if (p1==p2) then
+		return strcmputf8i(mission1.name, mission2.name) < 0
+	else
+		return p1 < p2
+	end
+end
+sorters.Xp=function (mission1, mission2)
+	if type(mission1)~="table" or type(mission2) ~="table" then return end
+	local p1=addon:GetMissionData(mission1.missionID,'globalXp')
+	local p2=addon:GetMissionData(mission2.missionID,'globalXp')
+	if (p1==p2) then
+		return strcmputf8i(mission1.name, mission2.name) < 0
+	else
+		return p2 < p1
 	end
-	table.sort(missionsList, comparison);
+end
+function addon.Garrison_SortMissions_Chance(missionsList)
+	xprint("Sorting on chance")
+	addon:OnAllMissions(function(missionID) addon:MatchMaker(missionID) end)
+	table.sort(missionsList, sorters.Chance);
+end
+function addon.Garrison_SortMissions_Age(missionsList)
+	xprint("Sorting on age")
+	addon:OnAllMissions(function(missionID) addon:MatchMaker(missionID) end)
+	table.sort(missionsList, sorters.Age);
+end
+function addon.Garrison_SortMissions_Followers(missionsList)
+	xprint("Sorting on followers")
+	addon:OnAllMissions(function(missionID) addon:MatchMaker(missionID) end)
+	table.sort(missionsList, sorters.Followers);
+end
+function addon.Garrison_SortMissions_Xp(missionsList)
+	xprint("Sorting on xp")
+	addon:OnAllMissions(function(missionID) addon:MatchMaker(missionID) end)
+	table.sort(missionsList, sorters.Xp);
+end
+function addon.Garrison_SortMissions_Original(missionsList)
+	xprint("Sorting on original")
+	addon:OnAllMissions(function(missionID) addon:MatchMaker(missionID) end)
+	origGarrison_SortMissions(missionsList)
 end

 function addon:OnInitialized()
---@debug@
+--[===[@debug@
 	ns.xprint("OnInitialized")
 	self.evdebug=CreateFrame("Frame")
 	self.evdebug:SetScript("OnEvent",function(...) return print('|cffff2020event|r',...) end)
---@end-debug@
-	parties=self:GetParty()
+--@end-debug@]===]
+	--parties=self:GetParty()
 	for _,b in ipairs(GMFMissionsListScrollFrame.buttons) do
 		local scale=0.8
 		local f,h,s=b.Title:GetFont()
@@ -383,27 +391,26 @@ function addon:OnInitialized()
 	self:AddToggle("IGM",true,IGNORE_UNAIVALABLE_FOLLOWERS,IGNORE_UNAIVALABLE_FOLLOWERS_DETAIL)
 	self:AddToggle("IGP",true,L['Ignore "maxed"'],L["Level 100 epic followers are not used for xp only missions."])
 	self:AddToggle("NOFILL",false,L["No mission prefill"],L["Disables automatic population of mission page screen. You can also press control while clicking to disable it for a single mission"])
-	self:AddToggle("DAYS",false,L["Show since"],L["Show esxpiration as days since first seen"])
 	self:AddSelect("MSORT","Garrison_SortMissions_Original",
 	{
 		Garrison_SortMissions_Original=L["Original method"],
 		Garrison_SortMissions_Chance=L["Success Chance"],
 		Garrison_SortMissions_Followers=L["Number of followers"],
-		Garrison_SortMissions_Age=L["Days since first seen"],
+		Garrison_SortMissions_Age=L["Expiration Time"],
 		Garrison_SortMissions_Xp=L["Global approx. xp reward"],
 	},
 	L["Sort missions by:"],L["Original sort restores original sorting method, whatever it was (If you have another addon sorting mission, it should kick in again)"])
-	bigscreen=self:GetBoolean("BIGSCREEN")
+	ns.bigscreen=self:GetBoolean("BIGSCREEN")
 	self:AddLabel("Followers Panel")
 	self:AddSlider("MAXMISSIONS",5,1,8,L["Mission shown for follower"],nil,1)
 	self:AddSlider("MINPERC",50,0,100,L["Minimun chance success under which ignore missions"],nil,5)
 	self:AddToggle("ILV",true,L["Show weapon/armor level"],L["When checked, show on each follower button weapon and armor level for maxed followers"])
 	--self:AddPrivateAction("ShowMissionControl",L["Mission control"],L["You can choose some criteria and have GC autosumbit missions for you"])
---@debug@
+--[===[@debug@
 	self:AddLabel("Developers options")
 	self:AddToggle("DBG",false, "Enable Debug")
 	self:AddToggle("TRC",false, "Enable Trace")
---@end-debug@
+--@end-debug@]===]
 	self:Trigger("MSORT")
 	return true
 end
@@ -428,12 +435,11 @@ end
 function addon:CheckGMM()
 	if (IsAddOnLoaded("GarrisonMissionManager")) then
 		GMM=true
-		self:RefreshMission()
+		self:RefreshMissions()
 	end
 end
 function addon:ApplyIGM(value)
-	self:BuildMissionsCache(false,true)
-	self:RefreshMission()
+	self:RefreshMissions()
 end
 function addon:ApplyCKMP(value)
 	if (HD) then self:Clock() end
@@ -444,7 +450,7 @@ function addon:ApplyCKMP(value)
 			MasterPlanMissionList:Show()
 		end
 	end
-	self:RefreshMission()
+	self:RefreshMissions()
 end
 function addon:ApplyDBG(value)
 	dbg=value
@@ -467,10 +473,10 @@ function addon:ApplyBIGSCREEN(value)
 		)
 end
 function addon:ApplyIGP(value)
-	self:BuildMissionsCache(false,true)
-	self:RefreshMission()
+	self:RefreshMissions()
 end
 function addon:ApplyMSORT(value)
+	ns.xprint("Sorting by ",value)
 	if (not origGarrison_SortMissions) then
 		origGarrison_SortMissions=Garrison_SortMissions
 	end
@@ -478,9 +484,10 @@ function addon:ApplyMSORT(value)
 	if (type(func)=="function") then
 		_G.Garrison_SortMissions=self[value]
 	else
+		print("Could not found ",value," in addon")
 		_G.Garrison_SortMissions=origGarrison_SortMissions
 	end
-	self:RefreshMission()
+	self:RefreshMissions()
 end
 function addon:ApplyMAXMISSIONS(value)
 	MAXMISSIONS=value
@@ -491,458 +498,6 @@ function addon:ApplyMINPERC(value)
 	BUSY_MESSAGE=format(BUSY_MESSAGE_FORMAT,MAXMISSIONS,MINPERC)
 end

-
---[[
-function addon:RestoreTooltip()
-	local self = GMF.MissionTab.MissionList;
-	local scrollFrame = self.listScroll;
-	local buttons = scrollFrame.buttons;
-	for i =1,#buttons do
-		buttons[i]:SetScript("OnEnter",GarrisonMissionButton_OnEnter)
-	end
-end
---]]
---[[
-	[12]={
-		description="Iron Horde raiders have descended on nearby draenei villages. Find the raiders' camp and raid it. Turnabout, they say, is fair play.",
-		cost=15,
-		duration="4 hr",
-		slots={
-			["Minion Swarms"]=1,
-			Type=1,
-			["Deadly Minions"]=1
-		},
-		durationSeconds=14400,
-		party={
-			party2="<empty>",
-			party1="<empty>"
-		},
-		level=100,
-		type="Combat",
-		counters={
-			["0x00000000001BE95D"]={
-				[1]={
-					counterIcon="Interface\\ICONS\\Ability_Rogue_FanofKnives.blp",
-					name="Minion Swarms",
-					counterName="Fan of Knives",
-					icon="Interface\\ICONS\\Spell_DeathKnight_ArmyOfTheDead.blp",
-					description="An enemy with many allies.  Susceptible to area-of-effect damage."
-				}
-			},
-			["0x00000000002D61EB"]={
-				[1]={
-					counterIcon="Interface\\ICONS\\Spell_Shaman_Hex.blp",
-					name="Deadly Minions",
-					counterName="Hex",
-					icon="Interface\\ICONS\\Achievement_Boss_TwinOrcBrutes.blp",
-					description="An enemy with powerful allies that should be neutralized."
-				}
-			}
-		},
-		traits={
-			["0x00000000001BE95D"]={
-				[1]={
-					traitID=236,
-					icon="Interface\\ICONS\\Item_Hearthstone_Card.blp"
-				}
-			}
-		},
-		locPrefix="GarrMissionLocation-Nagrand",
-		rewards={
-			[795]={
-				itemID=120301,
-				quantity=1
-			}
-		},
-		numRewards=1,
-		numFollowers=2,
-		state=-2,
-		iLevel=0,
-		name="Raiding the Raiders",
-		followers={
-		},
-		location="Nagrand",
-		isRare=false,
-		typeAtlas="GarrMission_MissionIcon-Combat",
-		missionID=380
-	}
-}
---]]
--- True manda a davani
-local function cmp(a,b)
-
-	if (a.mechanic and b.trait) then return true end
-	if (a.trait and b.mechanic) then return false end
-	if (a.name==a.name) then
-		if a.bias==b.bias then
-			if (a.rank==b.rank) then
-				return a.quality < b.quality
-			else
-				return a.rank < b.rank
-			end
-		else
-			return a.bias > b.bias
-		end
-	else
-		return a.name < b.name
-	end
-	--if (a.name~=b.name) then return a.name < b.name end
-	--if (a.bias==-1) then return false end
-	--if (b.bias==-1) then return true end
-	--if (a.bias~=b.bias) then return (a.bias>b.bias) end
-	--if (a.rank ~= b.rank) then return (a.rank < b.rank) end
-	return a.quality < b.quality
-end
-
-
-function addon:FillCounters(missionID,mission)
-	if (not mission) then mission=self:GetMissionData(missionID) end
-	local slots=mission.slots
-	local missioncounters=counters[missionID]
-	wipe(missioncounters)
-	local t=G.GetBuffedFollowersForMission(missionID)
-	if t then
-		for id,d in pairs(t) do
-			local rank=self:GetFollowerData(id,'rank')
-			local quality=self:GetFollowerData(id,'quality')
-			local bias= G.GetFollowerBiasForMission(missionID,id);
-			for i,l in pairs(d) do
-				-- i is meaningful
-				-- l.counterIcon
-				-- l.name
-				-- l.counterName
-				-- l.icon
-				-- l.description
-				tinsert(missioncounters,{k=cleanicon(l.icon),mechanic=true,name=l.name,followerID=id,bias=bias,rank=rank,quality=quality,icon=l.icon})
-			end
-		end
-	end
-	t= G.GetFollowersTraitsForMission(missionID)
-	if t then
-		for id,d in pairs(t) do
-			local bias= G.GetFollowerBiasForMission(missionID,id);
-			local rank=self:GetFollowerData(id,'rank')
-			local quality=self:GetFollowerData(id,'quality')
-			for i,l in pairs(d) do
-				--l.traitID
-				--l.icon
-				tinsert(missioncounters,{k=cleanicon(l.icon),trait=true,name=l.traitID,followerID=id,bias=bias,rank=rank,quality=quality,icon=l.icon})
-			end
-		end
-		table.sort(missioncounters,cmp)
-		local cf=wipe(counterFollowerIndex[missionID])
-		local ct=wipe(counterThreatIndex[missionID])
-		for i=1,#missioncounters do
-			tinsert(cf[missioncounters[i].followerID],i)
-			tinsert(ct[missioncounters[i].k],i)
-		end
-	end
-end
-
---[[
-Matchmaker debug for Spell Check
-Slots
-["Danger Zones"]=1,
-Type="Interface\\ICONS\\Achievement_Reputation_Ogre.blp",
-["Interface\\ICONS\\Achievement_Reputation_Ogre.blp"]=1,
-["Powerful Spell"]=1
-[1]={
-	name="Danger Zones",
-	icon="Interface\\ICONS\\spell_Shaman_Earthquake.blp",
-	quality=4,
-	bias=-0.66666668653488,
-	follower="0x00000000002D57C4",
-	rank=96
-},
-[2]={
-	name="Interface\\ICONS\\Achievement_Reputation_Ogre.blp",
-	icon="Interface\\ICONS\\Achievement_Reputation_Ogre.blp",
-	quality=4,
-	bias=0.66666668653488,
-	follower="0x00000000001978B6",
-	rank=600
-},
-[3]={
-	name="Interface\\ICONS\\Achievement_Reputation_Ogre.blp",
-	icon="Interface\\ICONS\\Achievement_Reputation_Ogre.blp",
-	quality=4,
-	bias=-0.66666668653488,
-	follower="0x00000000002D57C4",
-	rank=96
-},
-[4]={
-	name="Interface\\ICONS\\Item_Hearthstone_Card.blp",
-	icon="Interface\\ICONS\\Item_Hearthstone_Card.blp",
-	quality=2,
-	bias=0.66666668653488,
-	follower="0x00000000001BE95D",
-	rank=600
-}
-Preparying party
-Considering  Shelly Hamby for Danger Zones
-Considering  Qiana Moonshadow for Interface\ICONS\Achievement_Reputation_Ogre.blp
-Considering  Shelly Hamby for Interface\ICONS\Achievement_Reputation_Ogre.blp
-Considering  Bruma Swiftstone for Interface\ICONS\Item_Hearthstone_Card.blp
-Dopo check per nil
-["Danger Zones"]={
-},
-["Interface\\ICONS\\Achievement_Reputation_Ogre.blp"]={
-},
-["Interface\\ICONS\\Item_Hearthstone_Card.blp"]={
-}
-Party filling
-Party is not full
-Verifying Delvar Ironfist 0 600 3
-Verifying Rangari Chel 0 91 3
-Party filling
-Party is not full
-Verifying Rangari Chel 0 91 3
-Party filling
-Matchmaker end
-
---]]
---[[
-Button fields
-LocBG table
-HighlightBR table
-Highlight table
-inProgressFresh boolean
-Party table
-gcPANEL table
-HighlightB table
-HighlightTR table
-Level table
-Expire table
-MissionType table
-Overlay table
-info table
-ItemLevel table
-id number
-HighlightBL table
-Rewards table
-Threats table
-HighlightT table
-0 userdata
-RareText table
-IconBG table
-Title table
-Projections table
-RareOverlay table
-Summary table
-HighlightTL table
---]]
-local epicMountTrait=221
-local extraTrainingTrait=80 --all followers +35
-local fastLearnerTrait=29 -- only this follower +50
-local hearthStoneProTrait=236 -- all followers +36
-local scavengerTrait=79 -- More resources
-local function best(fid1,fid2,counters,mission)
-	if (not fid1) then return fid2 end
-	if (not fid2) then return fid1 end
-	local f1,f2=counters[fid1],counters[fid2]
-	if (dbg) then
-		print("Current",fid1,n[f1.followerID]," vs Candidate",fid2,n[f2.followerID])
-	end
-	if (P:IsIn(f1.followerID)) then return fid1 end
-	if (f2.bias<0) then return fid1 end
-	if (f2.bias>f1.bias) then return fid2 end
-	if (f1.bias == f2.bias) then
-		if (mission.resources > 0 ) then
-			if addon:HasTrait(f1.followerID,scavengerTrait) then
-				return fid1
-			end
-		end
-		if (f2.quality < f1.quality or f2.rank < f1.rank) then return fid2 end
-	end
-	return fid1
-end
-function addon:MatchMaker(missionID,mission,party,fromMissionControl)
-	if (GMFRewardSplash:IsShown()) then return end
-	if (not mission) then mission=self:GetMissionData(missionID) end
-	if (not party) then party=self:GetParty(missionID) end
-	local skipBusy=self:GetBoolean("IGM")
-	local skipMaxed=self:GetBoolean("IGP")
-	dbg=missionID==(tonumber(_G.MW) or 0)
-	local slots=mission.slots
-	local missionCounters=counters[missionID]
-	local ct=counterThreatIndex[missionID]
-	P:Open(missionID,mission.numFollowers)
-	-- Preloading skipped ones in party table.
-	if (dbg) then print(C("Matchmaking mission","Red"),missionID,mission.name) end
-	if (missionCounters) then
-		for i=1,#missionCounters do
-			local followerID=missionCounters[i].followerID
-			if (not followerID) then
-				if (dbg) then print("Trying to use [",followerID,"]") end
-			else
-				if (self:IsIgnored(followerID,missionID)) then
-					if (dbg) then print("Skipped",n[followerID],"due to ignored" ) end
-					P:Ignore(followerID,true)
-				elseif not self:IsFollowerAvailableForMission(followerID,skipBusy) then
-					if (dbg) then print("Skipped",n[followerID],"due to busy" ) end
-					P:Ignore(followerID)
-				elseif (skipMaxed and mission.xpOnly and self:GetFollowerData(followerID,'maxed')) then
-					if (dbg) then print("Skipped",n[followerID],"due to maxed" ) end
-					P:Ignore(followerID,true)
-				end
-			end
-		end
-		if (type(slots)=='table') then
-			for i=1,#slots do
-				local threat=cleanicon(slots[i].icon)
-				local candidates=ct[threat]
-				local choosen
-				if (dbg) then print("Checking ",threat) end
-				for i=1,#candidates do
-					local followerID=missionCounters[candidates[i]].followerID
-					if P:IsIn(followerID) then
-						if dbg then print("Countered by",n[followerID],"which is already in party") end
-						choosen=nil
-						break
-					end
-					if followerID then
-						if(not P:IsIgnored(followerID)) then
-							choosen=best(choosen,candidates[i],missionCounters,mission)
-							if (dbg) then print("Taken",n[missionCounters[choosen].followerID]) end
-						else
-							if (dbg) then print("Party Ignored",n[followerID]) end
-						end
-					end
-				end
-				if (choosen) then
-					if dbg then print("Adding to party",n[missionCounters[choosen].followerID]," still need ",P:FreeSlots()) end
-					P:AddFollower(missionCounters[choosen].followerID)
-				end
-				if (P:FreeSlots()==0) then
-					break
-				end
-			end
-		else
-			ns.xprint("Mission",missionID,"has no slots????")
-		end
-		if P:FreeSlots() > 0 then self:AddTraitsToParty(missionID,mission) end
-	end
-	if P:FreeSlots() > 0 then self:CompleteParty(missionID,mission,skipBusy,skipMaxed) end
-	if (not fromMissionControl and not P:IsEmpty()) then
-		if P:FreeSlots() > 0 then self:CompleteParty(missionID,mission,skipBusy,false) end
-	end
-	P:StoreFollowers(party.members)
-	party.full= P:FreeSlots()==0
-	party.perc=P:Close()
-end
-function addon:AddTraitsToParty(missionID,mission,skipBusy,skipMaxed)
-	local t=counters[missionID]
-	if (t) then
-		for i=1,#t do
-			local follower=t[i]
-			if (follower.trait and not P:IsIgnored(follower.followerID) and not P:IsIn(follower.followerID)) then
-				if mission.resources > 0 and follower.name==scavengerTrait then
-					P:AddFollower(follower.followerID)
-				elseif mission.xpOnly  and (follower.name==extraTrainingTrait or follower.name==hearthStoneProTrait) then
-					P:AddFollower(follower.followerID)
-				elseif mission.durationSeconds > GARRISON_LONG_MISSION_TIME  and follower.name==epicMountTrait then
-					P:AddFollower(follower.followerID)
-				end
-			end
-		end
-	end
-end
-function addon:CompleteParty(missionID,mission,skipBusy,skipMaxed)
-	local perc=select(4,G.GetPartyMissionInfo(missionID)) -- If percentage is already 100, I'll try and add the most useless character
-	local candidateMissions=10000
-	local candidateRank=10000
-	local candidateQuality=9999
-	if (dbg) then
-		print("Attemptin to fill party, so far perc is ",perc, "and party is")
-		P:Dump()
-	end
-	for x=1,P:FreeSlots() do
-		local candidate
-		local candidatePerc=perc
-		if (dbg) then print("            Perc to beat",perc, "Going for spot ",P:FreeSlots()) end
-		local totFollowers=#followersCache
-		for i=1,totFollowers do
-			local data=followersCache[i]
-			local followerID=data.followerID
-			if (dbg) then print("evaluating",data.fullname) end
-			repeat
-				if P:IsIgnored(followerID) then
-					if (dbg) then print("Skipped due to party ignored") end
-					break
-				end
-				if P:IsIn(followerID) then
-					if (dbg) then print("Skipped due to already in party") end
-					break
-				end
-				if self:IsIgnored(followerID,missionID) then
-					if (dbg) then print("Skipped due to ignored") end
-					break
-				end
-				if (skipMaxed and data.maxed and mission.xpOnly) then
-					if (dbg) then print("Skipped due to maxed",skipMaxed,mission.xpOnly) end
-					break
-				end
-				if (not self:IsFollowerAvailableForMission(followerID,skipBusy)) then
-					if (dbg) then print("Skipped due to busy") end
-					break
-				end
-				local rank=data.rank
-				local quality=data.quality
-				perc=tonumber(perc) or 0
-				if ((perc) <100) then
-					P:AddFollower(followerID)
-					local newperc=select(4,G.GetPartyMissionInfo(missionID))
-					newperc=tonumber(newperc) or 0
-					candidatePerc=tonumber(candidatePerc) or 0
-					P:RemoveFollower(followerID)
-					if (newperc > candidatePerc) then
-						candidatePerc=newperc
-						candidate=followerID
-						candidateRank=rank
-						candidateQuality=quality
-						break -- continue
-					end
-				else
-					-- This candidate is not improving success chance or we are already at 100%, minimize
-					if (i < totFollowers  and data.maxed) then
-						break  -- Pointless using a maxed follower if we  have more follower to try
-					end
-					if(rank<candidateRank) then
-						candidate=followerID
-						candidateRank=rank
-						candidateQuality=quality
-					elseif(rank==candidateRank and quality<candidateQuality) then
-						candidate=followerID
-						candidateRank=rank
-						candidateQuality=quality
-					elseif (not candidate) then
-						candidate=followerID
-						candidateRank=rank
-						candidateQuality=quality
-					end
-				end
-			until true -- A poor man continue implementation using break
-		end
-		if (candidate) then
-			if (dbg) then print("Attempting to add to party") end
-			P:AddFollower(candidate)
-			if (dbg) then
-				print("Added member to party")
-				P:Dump()
-			end
-			perc=select(4,G.GetPartyMissionInfo(missionID))
-			if (dbg) then print("New perc is",perc) end
-		end
-	end
-end
-function addon:TestMission(missionID)
-	local party=new()
-	_G.MW=missionID
-	self:MatchMaker(missionID)
-	_G.MW=nil
-
-end
-
 function addon:IsIgnored(followerID,missionID)
 	if (dbcache.ignored[missionID][followerID]) then return true end
 	if (dbcache.totallyignored[followerID]) then return true end
@@ -989,74 +544,51 @@ function addon:AddLine(name,status)
 	end
 	GameTooltip:AddDoubleLine(name, status,nil,nil,nil,r2,g2,b2)
 end
-function addon:SetThreatColor(obj,missionID)
-	local bias=self:GetCounterBias(missionID,obj.Icon:GetTexture())
-	local color=self:GetBiasColor(bias,nil,"Green")
-	local c=C[color]
-	obj.Border:SetVertexColor(c())
+function addon:SetThreatColor(obj,threat)
+	if type(threat)=="string" then
+		local _,_,bias,follower,name=strsplit(":",threat)
+		local color=self:GetBiasColor(tonumber(bias) or -1,nil,"Green")
+		local c=C[color]
+		obj.Border:SetVertexColor(c())
+	else
+		obj.Border:SetVertexColor(C.red())
+	end
+
 end

 function addon:HookedGarrisonMissionButton_AddThreatsToTooltip(missionID)
 	if (GMC:IsShown()) then return end
 	return self:RenderTooltip(missionID)
 end
-function addon:RenderTooltip(missionID)
+function addon:AddFollowersToTooltip(missionID)
 	local f=GarrisonMissionListTooltipThreatsFrame
-	if (not f.Env) then
-		f.Env=CreateFrame("Frame",nil,f,"GarrisonAbilityCounterTemplate")
-		f.Env:SetWidth(20)
-		f.Env:SetHeight(20)
-		f.Env:SetPoint("LEFT",f)
-	end
-	local t=f.EnvIcon:GetTexture();
-	f.EnvIcon:Hide()
-	f.Env.Icon:SetTexture(t)
-	f.Env.Icon:SetWidth(20)
-	f.Env.Icon:SetHeight(20)
-	self:SetThreatColor(f.Env,missionID)
-	--local bias,who=self:GetCounterBias(missionID,t)
-	--local color=self:GetBiasColor(bias,nil,"Green")
-	--local c=C[color]
-	--f.Env.Border:SetVertexColor(c())
-	for i=1,#f.Threats do
-		local th=f.Threats[i]
-		self:SetThreatColor(th,missionID)
-	end
 	-- Adding All available followers
-	local fullnames=new()
-	local party=new()
-	local biascolors=new()
-	local partystring=strjoin("|",tostringall(unpack(parties[missionID].members)))
-	for followerID,refs in pairs(counterFollowerIndex[missionID]) do
-		local fullname= self:GetFollowerData(followerID,'fullname')
-		for i=1,#refs do
-			fullname=fullname .." |T" .. tostring(counters[missionID][refs[i]].icon) ..":16|t"
-		end
-		if (not partystring:find(followerID)) then
-			tinsert(fullnames,fullname)
-		else
-			tinsert(party,fullname)
-		end
-		biascolors[fullname]={self:GetBiasColor(followerID,missionID,"White"),self:GetFollowerStatus(followerID,true)}
-	end
-	table.sort(fullnames)
+	local party=self:GetParty(missionID)
+	local members=party.members
+	local partystring=strjoin("|",tostringall(unpack(members)))
 	GameTooltip:AddLine(L["Other useful followers"])
-	for i=1,#fullnames do
-		local fullname=fullnames[i]
-		local info=biascolors[fullname]
-		self:AddLine(fullname,info[2],info[1])
+	for followerID,_ in pairs(G.GetFollowersTraitsForMission(missionID)) do
+		GameTooltip:AddDoubleLine(self:GetFollowerData(followerID,'fullname','none'),self:GetFollowerStatus(followerID,true,true))
 	end
-
-	del(party)
-	del(fullnames)
-	del(biascolors)
-	local perc=parties[missionID].perc
+	for followerID,_ in pairs(G.GetBuffedFollowersForMission(missionID)) do
+		GameTooltip:AddDoubleLine(self:GetFollowerData(followerID,'fullname','none'),self:GetFollowerStatus(followerID,true,true))
+	end
+	local perc=self:GetParty(missionID,'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
 		GameTooltip:AddLine(L["You have ignored followers"])
 		break;
 	end
+	if party.goldMultiplier>1 and party.class=='gold' then
+		GameTooltip:AddDoubleLine(L["Gold incremented!"],party.goldMultiplier..'x',C.Green())
+	end
+	if party.materialMultiplier>1 and party.class == 'resources' then
+		GameTooltip:AddDoubleLine(L["Resource incremented!"],party.materialMultiplier..'x',C.Green())
+	end
+	if party.xpBonus>0 then
+		GameTooltip:AddDoubleLine(L["Xp incremented!"],'+'..party.xpBonus,C.Green())
+	end
 	if (dbcache.history[missionID]) then
 		local tot,success=0,0
 		for d,r in pairs(dbcache.history[missionID]) do
@@ -1081,50 +613,14 @@ local function switch(flag)
 		end
 	end
 end
-function addon:RefreshMission(missionID)
-	if (self:IsAvailableMissionPage()) then
-		if (tonumber(missionID)) then
-			self:FillCounters(missionID)
-			self:MatchMaker(missionID)
-		end
+function addon:RefreshMissions(missionID)
+	if (ns.toc < 60100) then
+		GarrisonMissionFrame_OnUpdate()
+	else
 		GarrisonMissionList_UpdateMissions()
 	end
 end

-function addon:BuildMissionsCacheRem(fc,mm,OnEvent)
---cache.missions
---@debug@
-	local start=GetTime()
-	ns.xprint("Start Full Cache Rebuild")
---@end-debug@
-	local t=new()
-	self:holdEvents()
-	G.GetAvailableMissions(t)
-	for index=1,#t do
-		local missionID=t[index].missionID
-		if (not	dbcache.seen[missionID]) then
-			dbcache.seen[missionID]=(OnEvent and time()) or dbcache.lastseen
-		end
-		self:BuildMissionCache(missionID,t[index])
-		if fc then self:FillCounters(missionID) end
-		if mm then self:MatchMaker(missionID) end
-	end
-	for k,v in pairs(dbcache.seen) do
-		if (not cache.missions[k]) then
-			local span=time()-(tonumber(v) or time())
-			if (span > db.lifespan[k]) then
-				db.lifespan[k]=span
-			end
-			dbcache.seen[k]=nil
-		end
-	end
-	self:releaseEvents()
-	del(t)
-	collectgarbage("step",10)
---@debug@
-	ns.xprint("Done in",GetTime()-start)
---@end-debug@
-end
 --[[
 GARRISON_DURATION_DAYS = "%d |4day:days;"; changed to dual form
 GARRISON_DURATION_DAYS_HOURS = "%d |4day:days; %d hr"; changed to dual form
@@ -1153,145 +649,6 @@ local function GarrisonTimeStringToSeconds(text)
 	return 3600*24

 end
-function addon:BuildRunningMissionsCache()
-	local t=new()
-	G.GetInProgressMissions(t)
-	for index=1,#t do
-		local mission=t[index]
-		local missionID=mission.missionID
-		local running =dbcache.running[missionID]
-		running.duration=mission.durationSeconds
-		running.timeLeft=mission.timeLeft
-		if ((tonumber(running.started) or 0)==0) then running.started = time() - GarrisonTimeStringToSeconds(mission.timeLeft) end
-		running.followers={}
-		for i=1,mission.numFollowers do
-			running.followers[i]=mission.followers[i]
-			dbcache.runningIndex[mission.followers[i]]=missionID
-		end
-	end
-	del(t)
-end
---[[
-{
-	missionID=0,
-	counters={}, calculated
-	countered={ calculated
-		["*"]={}
-	},
-	counterers={ calculated
-		["*"]={}
-	},
-	slots={  calculated
-		["*"]=0
-	},
-	numFollowers=0, -> GetMissionMaxFollowers()
-	name="<newmission>", GetMissionName
-	basePerc=0, 		GetPartyMissionInfo
-	durationSeconds=0,  GePartyMissionInfo()
-	rewards={},
-	level=0,
-	iLevel=0,
-	rank=0,
-	locPrefix=false
-}
-GetBasicInfo Table Format={
-	description="With infernals and felguard invading Talador, the rest of the Burning Legion can't be far behind. Defeat them, and try to find their source.",
-	cost=15,
-	duration="8 hr",
-	durationSeconds=28800,
-	level=100,
-	type="Combat",
-	locPrefix="GarrMissionLocation-Talador",
-	rewards={
-		[248]={
-			itemID=114057,
-			quantity=1
-		}
-	},
-	numRewards=1,
-	numFollowers=3,
-	state=-2,
-	iLevel=0,
-	name="Burning Legion Vanguard",
-	followers={
-	},
-	location="Talador",
-	isRare=false,
-	typeAtlas="GarrMission_MissionIcon-Combat",
-	missionID=113
-}
-
---]]
-function addon:BuildMissionCacheRem(id,data)
-	local mission=cache.missions[id]
-	if (not mission) then
---@dedbug@
-		ns.xprint("Generating new data for ",id)
---@end-debug@
-		mission = data or G.GetBasicMissionInfo(id)
-		if (not mission) then return end
-		cache.missions[id]=mission
-		if dbg then print("Retrieved",id,mission.name) end
-	end
-	local _,xp,type,typeDesc,typeIcon,locPrefix,_,enemies=G.GetMissionInfo(id)
-	mission.rank=mission.level < 100 and mission.level or mission.iLevel
-	mission.xp=xp
-	mission.xpBonus=0
-	mission.resources=0
-	mission.gold=0
-	mission.followerUpgrade=0
-	mission.itemLevel=0
-	for k,v in pairs(mission.rewards) do
-		if (v.followerXP) then mission.xpBonus=mission.xpBonus+v.followerXP end
-		if (v.currencyID and v.currencyID==GARRISON_CURRENCY) then mission.resources=v.quantity end
-		if (v.currencyID and v.currencyID==0) then mission.gold =mission.gold+v.quantity/10000 end
-		if (v.itemID) then
-			if (v.itemID~=120205) then
-				local itemName, itemLink, itemRarity, itemLevel, itemMinLevel, itemType, itemSubType, itemStackCount,itemEquipLoc, itemTexture, itemSellPrice = GetItemInfo(v.itemID)
-				if (itemName) then
-					if (itemLevel > 1 ) then
-						mission.itemLevel=itemLevel
-					else
-						mission.followerUpgrade=itemRarity
-					end
-				end
-			end
-		end
-	end
-	mission.totalXp=(tonumber(mission.xp) or 0) + (tonumber(mission.xpBonus) or 0)
-	mission.globalXp=mission.totalXp*mission.numFollowers
-	if (mission.resources==0 and mission.gold==0 and mission.itemLevel==0 and mission.followerUpgrade==0) then
-		mission.xpOnly=true
-	else
-		mission.xpOnly=false
-	end
-	mission.locPrefix=locPrefix
-	if (not type) then
---@debug@
-		ns.xprint("No type",id,data.name)
---@end-debug@
-	else
-		if (not self.db.global.types[type]) then
-			self.db.global.types[type]={name=type,description=typeDesc,icon=typeIcon}
-		end
-	end
-	mission.slots={}
-	local slots=mission.slots
-
-	for i=1,#enemies do
-		local mechanics=enemies[i].mechanics
-		for i,mechanic in pairs(mechanics) do
-			tinsert(slots,mechanic)
-			self.db.global.abilities[mechanic.name]=mechanic
-		end
-	end
-	if (type) then
-		tinsert(slots,{name=TYPE,key=type,icon=typeIcon})
-	end
-	--collectgarbage("step",100)
-	mission.basePerc=select(4,G.GetPartyMissionInfo(id))
-
-end
 function addon:SetDbDefaults(default)
 	default.global=default.global or {}
 	default.global["*"]={}
@@ -1324,15 +681,28 @@ function addon:CreatePrivateDb()
 					["*"]=0
 				},
 				missionControl={
-					allowedRewards = {["*"]=true},
-					rewardChance={["*"]=100},
+					allowedRewards = {
+						followerEquip=true,
+						gold=true,
+						equip=true,
+						resources=true,
+						xp=true
+					},
+					rewardChance={
+						followerEquip=100,
+						gold=100,
+						equip=100,
+						resources=100,
+						xp=100
+					},
 					useOneChance=true,
 					itemPrio = {},
 					minimumChance = 100,
 					minDuration = 0,
 					maxDuration = 24,
 					epicExp = false,
-					skipRare=true
+					skipRare=true,
+					skipEpic=true
 				}
 			}
 		},
@@ -1366,15 +736,15 @@ end
 -- Fires after GarrisonMissionFrame OnShow. Pretty useless

 function addon:EventGARRISON_MISSION_NPC_OPENED(event,...)
---@debug@
+--[===[@debug@
 	ns.xprint(event,...)
---@end-debug@
+--@end-debug@]===]
 	if (GCF) then GCF:Show() end
 end
 function addon:EventGARRISON_MISSION_NPC_CLOSED(event,...)
---@debug@
+--[===[@debug@
 	ns.xprint(event,...)
---@end-debug@
+--@end-debug@]===]
 	if (GCF) then
 		self:RemoveMenu()
 		GCF:Hide()
@@ -1386,15 +756,11 @@ function addon:EventGARRISON_MISSION_LIST_UPDATE(event)
 	ns.xprint("GARRISON_MISSION_LIST_UPDATE",n,date("%d/%m/%y %H:%M:%S"))
 	local t=new()
 	G.GetAvailableMissions(t)
-	local justseen={}
 	local now=time()
 	for i=1,#t do
-		justseen[t[i].missionID]=true
-	end
-	for missionID,_ in pairs(justseen) do
+		local missionID=t[i].missionID
 		if (not tonumber(db.seen[missionID])) then db.seen[missionID]=now end
 	end
-	self:BuildMissionsCache(false,false,true)
 	del(t)
 end
 ---
@@ -1403,9 +769,9 @@ end
 -- After this events fires also GARRISON_MISSION_LIST_UPDATE and GARRISON_FOLLOWER_LIST_UPDATE

 function addon:EventGARRISON_MISSION_STARTED(event,missionID,...)
---@debug@
+--[===[@debug@
 	ns.xprint(event,missionID,...)
---@end-debug@
+--@end-debug@]===]
 --				running={
 --					["*"]={
 --						followers={},
@@ -1413,15 +779,19 @@ function addon:EventGARRISON_MISSION_STARTED(event,missionID,...)
 --						duration=0
 --					}
 --				},
+	table.sort(GMFMissions.inProgressMissions,sorters.EndTime)
 	dbcache.running[missionID].started=time()
 	dbcache.running[missionID].duration=select(2,G.GetPartyMissionInfo(missionID))
 	wipe(dbcache.running[missionID].followers)
 	wipe(dbcache.ignored[missionID])
-	for i=1,3 do
-		local m=parties[missionID].members[i]
-		if (m) then
-			tinsert(dbcache.running.followers,m)
-			dbcache.runningIndex[m]=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
 	local v=dbcache.seen[missionID]
@@ -1430,11 +800,7 @@ function addon:EventGARRISON_MISSION_STARTED(event,missionID,...)
 		db.lifespan[missionID]=span
 		-- IF we started it.. it was alive, so it's expire time is at least the time we waited before starting it
 	end
-
 	dbcache.seen[missionID]=nil
-	counters[missionID]=nil
-	parties[missionID]=nil
-	--self:BuildMissionsCache(true,true)
 	self:RefreshFollowerStatus()
 end
 ---
@@ -1447,23 +813,19 @@ end
 --

 function addon:EventGARRISON_MISSION_FINISHED(event,missionID,...)
---@debug@
+--[===[@debug@
 	ns.xprint(event,missionID,...)
 	ns.xdump(G.GetPartyMissionInfo(missionID))
---@end-debug@
+--@end-debug@]===]
 end
 function addon:EventGARRISON_FOLLOWER_LIST_UPDATE(event)
 --We need to update all followers, maybe this could be done in an onupdate handle
-	wipe(followersCache)
-	wipe(followersCacheIndex)
-	wipe(parties)
-	ns.xprint("Follower cache cleaned")
 end

 function addon:EventGARRISON_MISSION_BONUS_ROLL_LOOT(event,missionID,completed,success)
---@debug@
+--[===[@debug@
 	ns.xprint('evt',event,missionID,completed,success)
---@end-debug@
+--@end-debug@]===]
 	self:RefreshFollowerStatus()
 end
 ---
@@ -1478,23 +840,13 @@ end
 --GARRISON_MISSION_BONUS_ROLL_LOOY missionID nil
 --
 function addon:EventGARRISON_MISSION_COMPLETE_RESPONSE(event,missionID,completed,rewards)
---@debug@
+--[===[@debug@
 	ns.xprint('evt',event,missionID,completed,rewards)
---@end-debug@
+--@end-debug@]===]
 	dbcache.history[missionID][time()]={result=100,success=rewards}
 	dbcache.seen[missionID]=nil
 	dbcache.running[missionID]=nil
 	dbcache.seen[missionID]=nil
-	counters[missionID]=nil
-	parties[missionID]=nil
-end
-function addon:EventGARRISON_FOLLOWER_ADDED()
-	wipe(followersCache)
-	wipe(followersCacheIndex)
-end
-function addon:EventGARRISON_FOLLOWER_REMOVED()
-	wipe(followersCache)
-	wipe(followersCacheIndex)
 end
 -----------------------------------------------------
 -- Coroutines data and clock management
@@ -1508,11 +860,7 @@ local coroutines={
 	},
 }

-local lastmin=0
 local MPShown=nil
-local dbgFrame
-local lastCPU=0
-local startTime=GetTime()
 -- Keeping it as a nice example of coroutine management.. but not using it anymore
 function addon:Clock()
 	if (GMFMissions.showInProgress)	 then
@@ -1520,34 +868,6 @@ function addon:Clock()
 	else
 		--collectgarbage("step",100)
 	end
---@debug@
-	if (not dbgFrame) then
-		dbgFrame=AceGUI:Create("Window")
-		dbgFrame:SetTitle("GC Performance")
-		dbgFrame:SetPoint("LEFT")
-		dbgFrame:SetWidth(300)
-		dbgFrame:SetLayout("flow")
-		dbgFrame.Text=AceGUI:Create("Label")
-		dbgFrame.Text:SetColor(1,1,0)
-		dbgFrame.Text:SetFullWidth(true)
-		dbgFrame.Text1=AceGUI:Create("Label")
-		dbgFrame.Text1:SetColor(1,1,0)
-		dbgFrame.Text1:SetFullWidth(true)
-		dbgFrame:AddChild(dbgFrame.Text)
-		dbgFrame:AddChild(dbgFrame.Text1)
-	end
-	local h,m=GetGameTime()
-	if (m~=lastmin) then
-		lastmin=m
-	end
-	UpdateAddOnMemoryUsage()
-	dbgFrame.Text:SetText(format("Garrison Commander Mem %4.3fMB ",
-		GetAddOnMemoryUsage("GarrisonCommander")/1024)
-	)
-	dbgFrame.Text1:SetText(format("Garrison Broker   Mem %4.3fMB ",
-		GetAddOnMemoryUsage("GarrisonCommander-Broker")/1024)
-	)
---@end-debug@
 	dbcache.lastseen=time()
 	if (not MP or MPGoodGuy) then return  end
 	MPShown=not self:GetBoolean("CKMP")
@@ -1569,7 +889,7 @@ function addon:Clock()
 		GarrisonMissionFrameMissionsListScrollFrame:Show()
 		GarrisonMissionFrameMissionsListScrollFrame:SetParent(GMFMissions)
 	end
---@debug@
+--[===[@debug@
 	for k,d in pairs(coroutines) do
 		local  co=coroutines[k]
 		if (not co.func) then
@@ -1584,7 +904,7 @@ function addon:Clock()
 			co.paused=co.func(self)
 		end
 	end
---@end-debug@
+--@end-debug@]===]
 end
 function addon:ActivateButton(button,OnClick,Tooltiptext,persistent)
 	button:SetScript("OnClick",function(...) self[OnClick](self,...) end )
@@ -1592,9 +912,9 @@ function addon:ActivateButton(button,OnClick,Tooltiptext,persistent)
 		button.tooltip=Tooltiptext
 		button:SetScript("OnEnter",ShowTT)
 		if persistent then
-			button:SetScript("OnLeave",function() GameTooltip:FadeOut() end)
+			button:SetScript("OnLeave",ns.OnLeave)
 		else
-			button:SetScript("OnLeave",function() GameTooltip:Hide() end)
+			button:SetScript("OnLeave",ns.OnLeave)
 		end
 	else
 		button:SetScript("OnEnter",nil)
@@ -1614,15 +934,16 @@ function addon:Shrink(button)
 end
 do
 	local s=setmetatable({},{__index=function(t,k) return 0 end})
-	local FOLLOWER_STATUS_FORMAT=(bigscreen and "Followers status " or "" )..
+	local FOLLOWER_STATUS_FORMAT=(ns.bigscreen and "Followers status " or "" )..
 								C(AVAILABLE..':%d ','green') ..
 								C(GARRISON_FOLLOWER_WORKING .. ":%d ",'cyan') ..
 								C(GARRISON_FOLLOWER_ON_MISSION .. ":%d ",'red') ..
 								C(GARRISON_FOLLOWER_INACTIVE .. ":%d","silver")
 	function addon:RefreshFollowerStatus()
+
 		wipe(s)
-		for i=1,#followersCache do
-			local status=self:GetFollowerStatus(followersCache[i].followerID)
+		for _,followerID in self:GetFollowerIterator() do
+			local status=self:GetFollowerStatus(followerID)
 			s[status]=s[status]+1
 		end
 		if (GMF.FollowerStatusInfo) then
@@ -1641,7 +962,9 @@ do
 	end
 end
 function addon:ShowMissionControl()
-	if ns.missionautocompleting then return end
+	ns.xprint("Click1")
+	--if ns.missionautocompleting then return end
+	ns.xprint("Click2")
 	if (not GMC:IsShown()) then
 		GarrisonMissionFrame_SelectTab(999)
 		GMF.FollowerTab:Hide()
@@ -1771,106 +1094,6 @@ function addon:GenerateMissionsWidgets()
 	self:GenerateMissionContainer()
 	self:GenerateMissionButton()
 end
-local generated
-function addon:GenerateMissionCompleteList(title)
-	if not generated then
-		generated=true
-		self:GenerateContainer()
-		do
-			local Type="GCMCList"
-			local Version=1
-			local m={}
-			local function onEnter(self)
-				if (self.itemlink) then
-					GameTooltip:SetHyperlink(self.itemlink)
-					GameTooltip:Show()
-				end
-			end
-			function m:OnAcquire()
-			end
-			function m:Show()
-				self.frame:Show()
-			end
-			function m:Hide()
-				self.frame:Hide()
-				self:Release()
-			end
-			function m:AddMissionName(name,result)
-				local obj=self.scroll
-				local l=AceGUI:Create("Label")
-				l:SetFontObject(QuestFontNormalSmall)
-				l:SetColor(C:Yellow())
-				l:SetText(format("%s: %s",name,result))
-				l:SetFullWidth(true)
-				obj:AddChild(l)
-			end
-			function m:AddRow(data,...)
-				local obj=self.scroll
-				local l=AceGUI:Create("InteractiveLabel")
-				l:SetFontObject(GameFontNormalSmall)
-				l:SetText(data)
-				l:SetColor(...)
-				l:SetFullWidth(true)
-				obj:AddChild(l)
-			end
-			function m:AddFollower(followerID,data)
-				if (data[1]==0) then
-					return self:AddRow(format("%s is already at maximum xp",addon:GetFollowerData(followerID,'fullname')))
-				end
-				local quality=G.GetFollowerQuality(followerID) or data[4]
-				local level=G.GetFollowerLevel(followerID) or data[3]
-				ns.xprint(followerID,quality,level,data[3],data[4])
-				local levelup=((quality > data[4]) or (level > data[3]))
-				--local levelup=true
-				if levelup then
-					PlaySound("UI_Garrison_CommandTable_Follower_LevelUp");
-				else
-
-				end
-				--local text=format("%d %s gained %d xp",self:GetFo)
-				return self:AddRow(format("%s gained %d xp%s",addon:GetFollowerData(followerID,'fullname',true),data[1],
-				levelup and ". **Level Up!!!**" or format(". %d to go.",addon:GetFollowerData(followerID,'levelXP')-addon:GetFollowerData(followerID,'xp'))))
-			end
-			function m:AddIconText(icon,text,qt)
-				local obj=self.scroll
-				local l=AceGUI:Create("Label")
-				l:SetFontObject(GameFontNormalSmall)
-				if (qt) then
-					l:SetText(format("%s x %s",text,qt))
-				else
-					l:SetText(text)
-				end
-				l:SetImage(icon)
-				l:SetImageSize(24,24)
-				l:SetFullWidth(true)
-				obj:AddChild(l)
-				return l
-			end
-			function m:AddItem(itemID,qt)
-				local obj=self.scroll
-				local _,itemlink,itemquality,_,_,_,_,_,_,itemtexture=GetItemInfo(itemID)
-				self:AddIconText(itemtexture,itemlink,qt).itemlink=itemlink
-			end
-			local function Constructor()
-				local widget=AceGUI:Create("GCGUIContainer")
-				widget:SetLayout("Fill")
-				local scroll = AceGUI:Create("ScrollFrame")
-				scroll:SetLayout("Flow") -- probably?
-				scroll:SetFullWidth(true)
-				scroll:SetFullHeight(true)
-				widget:AddChild(scroll)
-				for k,v in pairs(m) do widget[k]=v end
-				widget:Show()
-				widget.scroll=scroll
-				return widget
-			end
-			AceGUI:RegisterWidgetType(Type,Constructor,Version)
-		end
-	end
-	local w=AceGUI:Create("GCMCList")
-	w:SetTitle(title)
-	return w
-end
 do
 local generated
 function addon:GenerateContainer()
@@ -1894,7 +1117,7 @@ function addon:GenerateContainer()
 			for _,f in pairs({frame:GetRegions()}) do
 				if (f:GetObjectType()=="Texture" and f:GetAtlas()=="Garr_WoodFrameCorner") then f:Hide() end
 			end
-			local widget={frame=frame}
+			local widget={frame=frame,missions={}}
 			widget.type=Type
 			widget.SetTitle=function(self,...) self.frame.TitleText:SetText(...) end
 			widget.SetTitleColor=function(self,...) self.frame.TitleText:SetTextColor(...) end
@@ -2012,82 +1235,72 @@ end

 function addon:GenerateMissionButton()
 	do
-		local Type="GMCMissionButton"
+		local Type1="GMCMissionButton"
+		local Type2="GMCSlimMissionButton"
 		local Version=1
 		local unique=0
-		local function OnAcquire(self)
+		local m={}
+		function m:OnAcquire()
 			local frame=self.frame
 			frame.info=nil
-			frame:SetHeight(80)
+			frame:SetHeight(self.type==Type1 and 80 or 80)
 			self.frame:SetAlpha(1)
 			self.frame:Enable()
+			for i=1,#self.scripts do
+				self.frame:SetScript(self.scripts[i],nil)
+			end
+			wipe(self.scripts)
 			return self.frame:SetScale(1.0)
 		end
-		local function Show(self)
+		function m:Show()
 			return self.frame:Show()
 		end
-		local function Hide(self)
+		function m:SetHeight(h)
+			return self.frame:SetHeight(h)
+		end
+		function m:Hide()
 			self.frame:SetHeight(1)
 			self.frame:SetAlpha(0)
 			return self.frame:Disable()
 		end
-		local function SetScript(self,...)
-			return self.frame:SetScript(...)
+		function m:SetScript(name,method)
+			tinsert(self.scripts,name)
+			return self.frame:SetScript(name,method)
 		end
-		local function SetScale(self,...)
-			return self.frame:SetScale(...)
+		function m:SetScale(s)
+			return self.frame:SetScale(s)
 		end
-		local function SetMission(self,mission,missionID,party)
+		function m:SetMission(mission,party)
 			self.frame.info=mission
-			addon:BuildMissionButton(self.frame,true)
+			self.frame.fromFollowerPage=true
 			self.frame:EnableMouse(true)
-			--self.frame:SetScript("OnEnter",GarrisonMissionPageFollowerFrame_OnEnter)
-			--self.frame:SetScript("OnLeave",GarrisonMissionPageFollowerFrame_OnLeave)
-			self.frame:SetScript("OnEnter",GarrisonMissionButton_OnEnter)
-			self.frame:SetScript("OnLeave",function() GameTooltip:FadeOut() end)
-			if (not party) then
-				for i=1,#GMC.ml.Parties do
-					party=GMC.ml.Parties[i]
-					if party.missionID==missionID then
-						break
-					end
-				end
-			end
-			self.frame.Percent:SetFormattedText("%d%%",party.perc)
-			self.frame.Percent:SetTextColor(addon:GetDifficultyColors(party.perc))
-			local x=1
-			for i=1,#party.members do
-				x=i+1
-				addon:RenderFollowerButton(self.frame.members[i],party.members[i],missionID)
-				self.frame.members[i]:SetScript("OnEnter",GarrisonMissionPageFollowerFrame_OnEnter)
-				self.frame.members[i]:SetScript("OnLeave",GarrisonMissionPageFollowerFrame_OnLeave)
-				self.frame.members[i]:Show()
+			self.frame.party=party
+			if self.type==Type1 then
+				addon:DrawSingleButton(false,self.frame,false,false)
+				self.frame:SetScript("OnEnter",GarrisonMissionButton_OnEnter)
+				self.frame:SetScript("OnLeave",ns.OnLeave)
+			else
+				addon:DrawSingleSlimButton(false,self.frame,false,false)
+				self.frame:SetScript("OnEnter",nil)
+				self.frame:SetScript("OnLeave",nil)
 			end
-			for i=x,3 do
-				self.frame.members[i]:SetScript("OnEnter",nil)
-				self.frame.members[i]:Hide()
+			if self.type==Type2 then
+				self.frame.Percent:SetFormattedText("%d%%",party.perc)
+				self.frame.Percent:SetTextColor(addon:GetDifficultyColors(party.perc))
 			end
 		end

 		local function Constructor()
 			unique=unique+1
 			local frame=CreateFrame("Button",nil,nil,"GarrisonMissionListButtonTemplate") --"GarrisonCommanderMissionListButtonTemplate")
-			local indicators=CreateFrame("Frame",nil,frame,"GarrisonCommanderIndicators")
 			frame.Title:SetFontObject("QuestFont_Shadow_Small")
 			frame.Summary:SetFontObject("QuestFont_Shadow_Small")
-			if (bigscreen) then
-				indicators.Percent:SetJustifyH("RIGHT")
-			else
-				indicators.Percent:SetJustifyH("LEFT")
-			end
-			indicators:SetPoint("LEFT",65,0)
-			indicators.Age:Hide()
-			frame.Percent=indicators.Percent
 			frame:SetScript("OnEnter",nil)
 			frame:SetScript("OnLeave",nil)
 			frame:SetScript("OnClick",function(self,button) return self.obj:Fire("OnClick",self,button) end)
 			frame.LocBG:SetPoint("LEFT")
 			frame.MissionType:SetPoint("TOPLEFT",5,-2)
+			--[[
 			frame.members={}
 			for i=1,3 do
 				local f=CreateFrame("Button",nil,frame,"GarrisonCommanderMissionPageFollowerTemplateSmall" )
@@ -2095,47 +1308,51 @@ function addon:GenerateMissionButton()
 				f:SetPoint("BOTTOMRIGHT",-65 -65 *i,5)
 				f:SetScale(0.8)
 			end
+			--]]
 			local widget={}
 			setmetatable(widget,{__index=frame})
-			widget.type=Type
 			widget.frame=frame
+			widget.scripts={}
 			frame.obj=widget
-			widget.OnAcquire=OnAcquire
-			widget.Show=Show
-			widget.Hide=Hide
-			widget.SetScript=SetScript
-			widget.SetMission=SetMission
-			widget.SetScale=SetScale
+			for k,v in pairs(m) do widget[k]=v end
+			return widget
+		end
+		local function Constructor1()
+			local widget=Constructor()
+			widget.type=Type1
 			return AceGUI:RegisterAsWidget(widget)
-
 		end
-		AceGUI:RegisterWidgetType(Type,Constructor,Version)
+		local function Constructor2()
+			local widget=Constructor()
+			local frame=widget.frame
+			widget.type=Type2
+			local indicators=CreateFrame("Frame",nil,frame,"GarrisonCommanderIndicators")
+			indicators.Percent:SetJustifyH("LEFT")
+			indicators.Percent:SetJustifyV("CENTER")
+			indicators:SetPoint("LEFT",70,-20)
+			indicators.Age:Hide()
+			frame.Percent=indicators.Percent
+			frame.Failure=frame:CreateFontString()
+			frame.Success=frame:CreateFontString()
+			frame.Failure:SetFontObject("GameFontRedLarge")
+			frame.Success:SetFontObject("GameFontGreenLarge")
+			frame.Failure:SetText(FAILED)
+			frame.Success:SetText(SUCCESS)
+			frame.Failure:Hide()
+			frame.Success:Hide()
+			frame.Title:SetPoint("LEFT",frame.Percent,"RIGHT",-20,30)
+			frame.Success:SetPoint("LEFT",frame.Percent,"RIGHT",-20,0)
+			frame.Summary:SetPoint("LEFT",frame.Percent,"RIGHT",-20,-30)
+
+			--widget.frame.MissionType:Hide()
+			--widget.frame.IconBG:Hide()
+			return AceGUI:RegisterAsWidget(widget)
+		end
+		AceGUI:RegisterWidgetType(Type1,Constructor1,Version)
+		AceGUI:RegisterWidgetType(Type2,Constructor2,Version)
+
 	end
 end
-function addon:__GMCBuildMiniMissionButton(frame,i,mission,scale,perc,offset)
-	offset=offset or 20
-	scale=scale or 0.6
-	local panel=frame.Missions[i]
-	if (not panel) then
-		panel=CreateFrame("Button",nil,frame,"GarrisonCommanderMissionListButtonTemplate")
-		panel:SetPoint("TOPLEFT",frame,1,-((i-1)*panel:GetHeight() +offset))
-		panel:SetPoint("TOPRIGHT",frame,-1,-((i-1)*panel:GetHeight()-offset))
-		panel.orderId=i
-		tinsert(frame.Missions,panel)
-		--Creo una riga nuova
-		panel:SetScale(scale)
-		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,mission.numFollowers)
-	panel:Show()
-	return panel
-end

 function addon:CreateOptionsLayer(...)
 	local o=AceGUI:Create("SimpleGroup") -- a transparent frame
@@ -2210,7 +1427,7 @@ function addon:Options()
 	end
 	GCF:SetFrameStrata(GMF:GetFrameStrata())
 	GCF:SetFrameLevel(GMF:GetFrameLevel()-2)
-	if (not bigscreen) then GCF:SetHeight(GCF:GetHeight()+35) end
+	if (not ns.bigscreen) then GCF:SetHeight(GCF:GetHeight()+35) end
 	baseHeight=GCF:GetHeight()
 	minHeight=47
 	GCF.CloseButton:SetScript("OnClick",nil)
@@ -2270,7 +1487,7 @@ function addon:Options()
 	GCF:RegisterForDrag("LeftButton")
 	GCF:SetScript("OnDragStart",function(frame)if (self:GetBoolean("MOVEPANEL")) then frame:StartMoving() end end)
 	GCF:SetScript("OnDragStop",function(frame) frame:StopMovingOrSizing() end)
-	if (bigscreen) then
+	if (ns.bigscreen) then
 		--MinimizeButton
 		-- It's not working well, now I dont have time to fix it
 		if (false) then
@@ -2304,9 +1521,9 @@ function addon:Options()
 end

 function addon:ScriptTrace(hook,frame,...)
---@debug@
+--[===[@debug@
 	ns.xprint("Triggered " .. C(hook,"red").." script on",C(frame,"Azure"),...)
---@end-debug@
+--@end-debug@]===]
 end
 function addon:IsProgressMissionPage()
 	return GMF:IsShown() and GMF.MissionTab and GMF.MissionTab.MissionList.showInProgress
@@ -2319,7 +1536,7 @@ function addon:IsFollowerList()
 end
 --GMFMissions.CompleteDialog
 function addon:IsRewardPage()
-	return GMF:IsShown() and GMF.MissionComplete:IsShown()
+	return GMF:IsShown() and GMFMissions.CompleteDialog:IsShown()

 end
 function addon:IsMissionPage()
@@ -2347,7 +1564,7 @@ do
 	end
 end
 function addon:HookedGarrisonMissionFrame_HideCompleteMissions()
-	self:BuildMissionsCache(true,true)
+	xprint("Complete missions closed")
 end


@@ -2388,15 +1605,13 @@ function addon:HookedGarrisonFollowerButton_UpdateCounters(frame,follower,showCo
 end
 function addon:RenderFollowerPageFollowerButton(frame,follower,showCounters)
 	if not frame.GCIt then
+		frame.GCIt=frame:CreateFontString(nil,"ARTWORK","GameFontHighlightSmall")
+		frame.GCIt:SetPoint("BOTTOMLEFT",frame.Name,"TOPLEFT",0,2)
 		if not MP then
 			frame.GCTime=frame:CreateFontString(nil,"ARTWORK","GameFontHighlightSmall")
 			frame.GCTime:SetPoint("TOPLEFT",frame.Status,"TOPRIGHT",5,0)
 			frame.GCXp=frame:CreateFontString(nil,"ARTWORK","GameFontHighlightSmall")
-			frame.GCXp:SetPoint("LEFT",frame.Name,"LEFT",0,0)
-			frame.GCXp:SetPoint("BOTTOM",frame,"BOTTOM",0,2)
 		end
-		frame.GCIt=frame:CreateFontString(nil,"ARTWORK","GameFontHighlightSmall")
-		frame.GCIt:SetPoint("BOTTOMLEFT",frame.Name,"TOPLEFT",0,2)
 	end
 	if not frame.isCollected then
 		if not MP then
@@ -2406,7 +1621,6 @@ function addon:RenderFollowerPageFollowerButton(frame,follower,showCounters)
 		frame.GCIt:Hide()
 		return
 	end
-	addon:RecalculateFollower(follower)
 	if not MP then
 		if (frame.Status:GetText() == GARRISON_FOLLOWER_ON_MISSION) then
 			frame.GCTime:SetText(self:GetFollowerStatus(follower.followerID,true,true))
@@ -2417,37 +1631,40 @@ function addon:RenderFollowerPageFollowerButton(frame,follower,showCounters)
 		if (follower.level >= GARRISON_FOLLOWER_MAX_LEVEL and follower.quality >= GARRISON_FOLLOWER_MAX_UPGRADE_QUALITY) then
 			frame.GCXp:Hide()
 		else
-			frame.GCXp:SetFormattedText("Xp to next upgrade: %d",follower.levelXP-follower.xp)
+			frame.GCXp:SetFormattedText(L["To go: %d"],follower.levelXP-follower.xp)
 			frame.GCXp:Show()
 		end
 	end
 	if (follower.level >= GARRISON_FOLLOWER_MAX_LEVEL and self:GetToggle("ILV") ) then
 		local c1=ITEM_QUALITY_COLORS[follower.weaponQuality or 1]
 		local c2=ITEM_QUALITY_COLORS[follower.armorQuality or 1]
-		frame.GCIt:SetFormattedText("W:%s%3d|r A:%s%3d|r",c1.hex,follower.weaponItemLevel,c2.hex,follower.armorItemLevel)
+		frame.GCIt:SetFormattedText("W:%s%3d|r A:%s%3d|r",c1.hex,self:GetFollowerData(follower.followerID,"weaponItemLevel"),c2.hex,self:GetFollowerData(follower.followerID,"armorItemLevel"))
 		frame.GCIt:Show()
+		frame.GCXp:SetPoint("LEFT",frame.GCIt,"RIGHT",2,0)
 	else
 		frame.GCIt:Hide()
+		frame.GCXp:SetPoint("LEFT",frame.Name,"LEFT",0,20)
 	end

 end
 function addon:HookedGarrisonFollowerListButton_OnClick(frame,button)
 	if (frame.info.isCollected) then
 		if (button=="LeftButton") then
-			if (bigscreen and frame and frame.info and frame.info.followerID)  then self:HookedGarrisonFollowerPage_ShowFollower(frame.info,frame.info.followerID) end
+			if (ns.bigscreen and frame and frame.info and frame.info.followerID)  then self:HookedGarrisonFollowerPage_ShowFollower(frame.info,frame.info.followerID) end
 		end
 		self:ScheduleTimer("HookedGarrisonFollowerButton_UpdateCounters",0.2,frame,frame.info,false)
 	end
 end
 -- Shamelessly stolen from Blizzard Code
+local fakepage={newMissionIDs={}}
 function addon:BuildMissionButton(button,gmc,...)
+	if true then return self:DrawSingleButton(fakepage,button,false,false) end
 	local mission=button.info
 	if (not mission or not mission.name) then
 		if (button.missionID) then
 			mission=self:GetMissionData(button.missionID)
 		end
 	end
-	if (not mission) then return end
 	button.Title:SetWidth(0);
 	button.Title:SetText(mission.name);
 	button.Level:SetText(mission.level);
@@ -2468,6 +1685,7 @@ function addon:BuildMissionButton(button,gmc,...)
 		button.Summary:ClearAllPoints();
 		button.Summary:SetPoint("TOPLEFT", button.Title, "BOTTOMLEFT", 0, -4);
 	end
+	if (not mission) then return end
 	if gmc then button.Title:SetPoint("LEFT",70,25) end
 	if ( mission.locPrefix ) then
 		button.LocBG:Show();
@@ -2510,66 +1728,13 @@ function addon:BuildMissionButton(button,gmc,...)
 	button:Show();

 end
--- Ripped bleeding from Blizzard code. In mini buttons I cant suffer MP staff
-function addon:ClonedGarrisonMissionButton_SetRewards(rewards, numRewards)
-	if (numRewards > 0) then
-		local index = 1;
-		for id, reward in pairs(rewards) do
-			if (not self.Rewards[index]) then
-				self.Rewards[index] = CreateFrame("Frame", nil, self, "GarrisonMissionListButtonRewardTemplate");
-				self.Rewards[index]:SetPoint("RIGHT", self.Rewards[index-1], "LEFT", 0, 0);
-			end
-			local Reward = self.Rewards[index];
-			Reward.Quantity:Hide();
-			Reward.itemID = nil;
-			Reward.currencyID = nil;
-			Reward.tooltip = nil;
-			if (reward.itemID) then
-				Reward.itemID = reward.itemID;
-				GarrisonMissionFrame_SetItemRewardDetails(Reward);
-				if ( reward.quantity > 1 ) then
-					Reward.Quantity:SetText(reward.quantity);
-					Reward.Quantity:Show();
-				end
-			else
-				Reward.Icon:SetTexture(reward.icon);
-				Reward.title = reward.title
-				if (reward.currencyID and reward.quantity) then
-					if (reward.currencyID == 0) then
-						Reward.tooltip = GetMoneyString(reward.quantity);
-					else
-						Reward.currencyID = reward.currencyID;
-						Reward.Quantity:SetText(reward.quantity);
-						Reward.Quantity:Show();
-					end
-				else
-					Reward.tooltip = reward.tooltip;
-				end
-			end
-			Reward:Show();
-			index = index + 1;
-		end
-	end
-
-	for i = (numRewards + 1), #self.Rewards do
-		self.Rewards[i]:Hide();
-	end
-end
-function addon:ClonedGarrisonMissionMechanic_OnEnter(missionID,this,...)
+function addon.ClonedGarrisonMissionMechanic_OnEnter(this)
 	local tip=GameTooltip
-	tip:SetOwner(this, "ANCHOR_CURSOR_RIGHT");
-	tip:AddLine(this.info.name,C.White())
-	--tip:AddTexture(this.Icon:GetTexture())
-	tip:AddLine(this.info.description,C.Orange())
-	local t=new()
-	self:GetAllCounters(missionID,this.Icon:GetTexture(),t)
-	if( #t > 0) then
-		tip:AddLine(GARRISON_MISSION_COUNTER_FROM)
-		for i=1,#t do
-			tip:AddLine(self:GetFollowerData(t[i],'fullname'),C[self:GetBiasColor(t[i],missionID,C.White())]())
-		end
-	end
-	del(t)
+	local button=this:GetParent()
+	tip:SetOwner(button, "ANCHOR_CURSOR_RIGHT");
+	tip:AddLine(this.Name,C.White())
+	tip:AddTexture(this.Icon:GetTexture())
+	tip:AddLine(this.Description,C.Orange())
 	tip:Show()
 end
 function addon:HookedGarrisonFollowerPage_ShowFollower(frame,followerID,force)
@@ -2589,8 +1754,9 @@ do
 		addon:OnClick_GarrisonMissionButton(this.frame,"Leftup")
 		lastTab=2
 	end
-	function addon:RenderFollowerPageMissionListBroken(frame,followerID,force)
-		if not bigscreen then return end
+	function addon:RenderFollowerPageMissionList(frame,followerID,force)
+		if not ns.bigscreen then return end
+		if not GMFFollowers:IsShown() then return end
 		if (not ml) then
 			ml=AceGUI:Create("GMCLayer")
 			ml:SetTitle("Ninso")
@@ -2625,28 +1791,26 @@ do
 			return
 		end
 		wipe(partyIndex)
-		if (#parties==0) then
-			self:BuildMissionsCache(true,true)
-		end
+		local parties=self:GetParty()
 		for missionID,party in pairs(parties) do
-			if (tContains(party.members,followerID)) then print("Found mission",missionID) tinsert(partyIndex,missionID) end
+			if (tContains(party.members,followerID)) then ns.xprint("Found mission",missionID) tinsert(partyIndex,missionID) end
 		end
 		table.sort(partyIndex,function(a,b) return parties[a].perc > parties[b].perc end)
 		for i=1,#partyIndex do
 			local missionID=partyIndex[i]
 			local party=parties[missionID]
-			local mission=cache.missions[missionID]
+			local mission=self:GetMissionData(missionID)
 			if (mission) then
 				local mb=AceGUI:Create("GMCMissionButton")
 				mb:SetScale(0.6)
 				ml:PushChild(mb,missionID)
 				mb:SetFullWidth(true)
-				mb:SetMission(mission,missionID,party)
+				mb:SetMission(mission,party)
 				mb:SetCallback("OnClick",MissionOnClick)
 			end
 		end
 	end
-	function addon:RenderFollowerPageMissionList(frame,followerID,force)
+	function addon:RenderFollowerPageMissionListOld(frame,followerID,force)
 		ns.xprint("hook",followerID,force)
 		if (followerID==lastFollowerID and not force) then return end
 		lastFollowerID=followerID
@@ -2708,10 +1872,10 @@ do
 			for z = 1,#partyIndex do
 				local missionID=partyIndex[z]
 				if not(tonumber(missionID)) then
-	--@debug@
+	--[===[@debug@
 					ns.xprint("missionid not a number",missionID)
 					self:Dump("partyIndex table",partyIndex)
-	--@end-debug@
+	--@end-debug@]===]
 					perc=-1 --(lowering perc  to ignore this mission
 				end

@@ -2775,9 +1939,10 @@ end
 ---
 --Initial one time setup
 function addon:SetUp(...)
---@debug@
-	ns.xprint("Setup")
---@end-debug@
+	self:FollowerCacheInit()
+--[===[@debug@
+	ns.dprint("Setup")
+--@end-debug@]===]
 --@alpha@
 	if (not db.alfa.v220) then
 		self:Popup(L["You are using an Alpha version of Garrison Commander. Please post bugs on Curse if you find them"],10)
@@ -2790,7 +1955,7 @@ function addon:SetUp(...)
 	self:CheckGMM()
 	self:Options()
 	self:GenerateMissionsWidgets()
-	GMC=self:GMCBuildPanel(bigscreen)
+	GMC=self:GMCBuildPanel(ns.bigscreen)
 	local tabMC=CreateFrame("CheckButton",nil,GMF,"SpellBookSkillLineTabTemplate")
 	GMF.tabMC=tabMC
 	tabMC.tooltip="Open Garrison Commander Mission Control"
@@ -2825,7 +1990,7 @@ function addon:SetUp(...)
 	bt:SetText(L["Garrison Comander Quick Mission Completion"])
 	bt:SetPoint("CENTER",0,-50)
 	addon:ActivateButton(bt,"MissionComplete","Complete all missions without confirmation")
-	self:StartUp()
+	return self:StartUp()
 	--collectgarbage("step",10)
 --/Interface/FriendsFrame/UI-Toast-FriendOnlineIcon
 end
@@ -2833,12 +1998,15 @@ function addon:AddMenu()
 	local menu,size=self:CreateOptionsLayer(MP and 'CKMP' or nil,'BIGSCREEN','MOVEPANEL','IGM','IGP','NOFILL','MSORT')
 	--self:AddOptionToOptionsLayer(GCF.Menu,'MSORT')
 	--self:AddOptionToOptionsLayer(GCF.Menu,'ShowMissionControl')
---@debug@
+--[===[@debug@
 	self:AddOptionToOptionsLayer(menu,'DBG')
 	self:AddOptionToOptionsLayer(menu,'TRC')
---@end-debug@
+--@end-debug@]===]
 	local frame=menu.frame
 	frame:Show()
+	frame:SetParent(GCF)
+	frame:SetFrameStrata(GCF:GetFrameStrata())
+	frame:SetFrameLevel(GCF:GetFrameLevel()+2)
 	menu:ClearAllPoints()
 	menu:SetPoint("TOPLEFT",GCF,"TOPLEFT",25,-18)
 	menu:SetWidth(GCF:GetWidth()-50)
@@ -2863,9 +2031,9 @@ end
 -- This method is called every time garrison mission panel is open because
 -- when it closes, I remove most of used hooks
 function addon:StartUp(...)
---@debug@
-	ns.xprint("Startup")
---@end-debug@
+--[===[@debug@
+	ns.dprint("Startup")
+--@end-debug@]===]
 	self:GrowPanel()
 	self:Unhook(GMF,"OnShow")
 	if (self:GetBoolean("PIN")) then
@@ -2875,10 +2043,9 @@ function addon:StartUp(...)
 		GCF:SetHeight(minHeight)
 	end
 	self:PermanentEvents()
-	self:SafeSecureHook("GarrisonMissionButton_AddThreatsToTooltip")
-	self:SafeSecureHook("GarrisonMissionButton_SetRewards")
+	--self:SafeSecureHook("GarrisonMissionButton_AddThreatsToTooltip")
 	self:SafeSecureHook("GarrisonFollowerListButton_OnClick") -- used both to update follower mission list and itemlevel display
-	if (bigscreen) then
+	if (ns.bigscreen) then
 		self:SafeSecureHook("GarrisonFollowerPage_ShowFollower")--,function(...) ns.xprint("GarrisonFollowerPage_ShowFollower",...) end)
 		self:SafeSecureHook("GarrisonFollowerTooltipTemplate_SetGarrisonFollower")
 	end
@@ -2898,16 +2065,14 @@ function addon:StartUp(...)
 	for i=1,#GMFMissionListButtons do
 		local b=GMFMissionListButtons[i]
 		self:SafeHookScript(b,"OnClick","OnClick_GarrisonMissionButton",true)
---@debug@
-		self:SafeHookScript(b,"OnEnter","AddMissionId",true)
-	self:ScheduleRepeatingTimer("Clock",1)
---@end-debug@
 	end
-	--self:BuildMissionsCache(true,true)
-	--self:BuildRunningMissionsCache()
+	--self:ScheduleRepeatingTimer("Clock",1)
 	self:RefreshFollowerStatus()
 	self:Trigger("MSORT")
 	self:Trigger("CKMP")
+	GMFMissions.listScroll.update = over.GarrisonMissionList_Update
+	table.sort(GMFMissions.inProgressMissions,sorters.EndTime)
+	return self:RefreshMissions()
 end
 function addon:MarkAsNew(obj,key,message)
 	if (not db.news[key]) then
@@ -2934,29 +2099,29 @@ function addon:PermanentEvents()
 end
 function addon:checkMethod(method,hook)
 	if (type(self[method])=="function") then
---@debug@
+--[===[@debug@
 		--ns.xprint("Hooking ",hook,"to self:" .. method)
---@end-debug@
+--@end-debug@]===]
 		return true
---@debug@
+--[===[@debug@
 	else
 		--ns.xprint("Hooking ",hook,"to print")
---@end-debug@
+--@end-debug@]===]
 	end
 end
 function addon:SafeRegisterEvent(event)
---@debug@
+--[===[@debug@
 	if not self.evdebug:IsEventRegistered(event) then
 		self.evdebug:RegisterEvent(event)
 	end
---@end-debug@
+--@end-debug@]===]
 	local method="Event"..event
 	if (self:checkMethod(method,event)) then
 		return self:RegisterEvent(event,method)
---@debug@
+--[===[@debug@
 	else
 		return self:RegisterEvent(event,ns.xprint)
---@end-debug@
+--@end-debug@]===]
 	end
 end
 function addon:SafeSecureHook(tobehooked,method)
@@ -2964,13 +2129,13 @@ function addon:SafeSecureHook(tobehooked,method)
 	method=method or "Hooked"..tobehooked
 	if (self:checkMethod(method,tobehooked)) then
 		return self:SecureHook(tobehooked,method)
---@debug@
+--[===[@debug@
 	else
 		do
 			local hooked=tobehooked
 			return self:SecureHook(tobehooked,function(...) ns.xprint(hooked,...) end)
 		end
---@end-debug@
+--@end-debug@]===]
 	end
 end
 function addon:SafeHookScript(frame,hook,method,postHook)
@@ -3011,9 +2176,9 @@ function addon:CleanUp()
 		GarrisonFollowerTooltip.fs:Hide()
 	end
 	--collectgarbage("collect")
---@debug@
+--[===[@debug@
 	ns.xprint("Cleaning up")
---@end-debug@
+--@end-debug@]===]
 end
 function addon:EventGARRISON_FOLLOWER_XP_CHANGED(event,followerID,iLevel,xp,level,quality)
 	local i=followersCacheIndex[followerID]
@@ -3031,116 +2196,43 @@ function addon:EventGARRISON_FOLLOWER_XP_CHANGED(event,followerID,iLevel,xp,leve
 	wipe(followersCache)
 	self:GetFollowerData(followerID)  -- triggering a full cache refresh
 end
-
-function addon:RecalculateFollower(follower,refreshrank)
-	if (refreshrank) then
-		follower.level=G.GetFollowerLevel(follower.followerID)
-		follower.quality=G.GetFollowerQuality(follower.followerID)
-	end
-	follower.rank=follower.level==100 and follower.iLevel or follower.level
-	follower.coloredname=C(follower.name,tostring(follower.quality))
-	follower.fullname=format("%3d %s",follower.rank,follower.coloredname)
-	follower.maxed=follower.quality >= GARRISON_FOLLOWER_MAX_UPGRADE_QUALITY and follower.level >=GARRISON_FOLLOWER_MAX_LEVEL
-	local weaponItemID, weaponItemLevel, armorItemID, armorItemLevel = G.GetFollowerItems(follower.followerID);
-	follower.weaponItemID=weaponItemID
-	follower.weaponItemLevel=weaponItemLevel
-	follower.armorItemID=armorItemID
-	follower.armorItemLevel=armorItemLevel
-	follower.weaponQuality=select(3,GetItemInfo(weaponItemID))
-	follower.armorQuality=select(3,GetItemInfo(armorItemID))
-end
-function addon:CanCounter(followerID,id)
-	local abilities=self:GetFollowerData(followerID,abilities)
-	for i=1,#abilities do
-		local ability=abilities[i]
-		for k,v in pairs(ability.counter) do
-			if (k==trait or v.name==trait) then
-				return true
-			end
-		end
+function addon:IsFollowerAvailableForMission(followerID,skipbusy)
+	if self:GMCBusy(followerID) then
+		return false
 	end
-end
-function addon:HasTrait(followerID,trait)
-	local abilities=self:GetFollowerData(followerID,abilities)
-	for i=1,#abilities do
-		local ability=abilities[i]
-		if ability.isTrait then
-			if ability.ID==trait then
-				return true
-			end
-		end
+	if (not skipbusy) then
+		return self:GetFollowerStatus(followerID) ~= GARRISON_FOLLOWER_WORKING
+	else
+		return self:GetFollowerStatus(followerID) == AVAILABLE
 	end
 end
-function addon:GetFollowerData(key,subkey,refresh)
-	local k=followersCacheIndex[key]
-	if (not followersCache[1]) then
-		followersCache=G.GetFollowers()
-		for i,follower in pairs(followersCache) do
-			if (not follower.isCollected) then
-				followersCache[i]=nil
-			else
-				self:RecalculateFollower(follower)
-				follower.abilities=G.GetFollowerAbilities(follower.followerID)
-			end
-		end
-		refresh=false
-	end
-	local t=followersCache
-	if (not k) then
-		for i=1,#t do
-			if (t[i] and (t[i].followerID == key or t[i].name==key)) then
-				followersCacheIndex[t[i].followerID]=i
-				followersCacheIndex[t[i].name]=i
-				k=i
-				break
-			end
+if ns.toc < 60100 then
+	G.GetFollowerMissionTimeLeft= function(followerID)
+		local running=dbcache.running[dbcache.runningIndex[followerID]]
+		if (running) then
+			return SecondsToTime(running.started + running.duration - time(),true)
+		else
+			return 0
 		end
 	end
-	if (k and type(t[k]=='table')) then
-		if (refresh) then
-			self:RecalculateFollower(t[k],true)
-		end
-		if (subkey) then
-			return t[k][subkey]
+	G.GetFollowerMissionTimeLeftSeconds=function(followerID)
+		local running=dbcache.running[dbcache.runningIndex[followerID]]
+		if (running) then
+			return running.started + running.duration - time()
 		else
-			return t[k]
+			return 0
 		end
-	else
-		return nil
-	end
-end
-function addon:GetMissionDataRem(missionID,subkey)
-	local missionCache=cache.missions[missionID]
-	if (not missionCache) then
---@debug@
-		ns.xprint("Found a new mission",missionID,"Refreshing it")
---@end-debug@
-		self:BuildMissionCache(missionID)
-		self:FillCounters(missionID,cache.missions[missionID])
-		self:MatchMaker(missionID,cache.missions[missionID])
-	end
-	if (subkey) then
-		if not missionCache then return 0 end
-		return missionCache[subkey]
-	end
-	return missionCache
-end
-function addon:IsFollowerAvailableForMission(followerID,skipbusy)
-	if self:GMCBusy(followerID) then
-		return false
 	end
-	if (not skipbusy) then
-		return true
-	else
-		return self:GetFollowerStatus(followerID) == AVAILABLE
+	G.IsMechanicFullyCountered=function(missionID,followerID)
+		return G.GetFollowerBiasForMission(missionID,followerID)>0.99
 	end
 end
 function addon:GetFollowerStatus(followerID,withTime,colored)
+	--C_Garrison.GetFollowerMissionTimeLeftSeconds(follower.followerID)
 	if (not followerID) then return UNAVAILABLE end
 	local status=G.GetFollowerStatus(followerID)
 	if (status and status== GARRISON_FOLLOWER_ON_MISSION and withTime) then
-		local running=dbcache.running[dbcache.runningIndex[followerID]]
-		status=SecondsToTime(running.started + running.duration - time() ,true)
+		status=G.GetFollowerMissionTimeLeft(followerID)
 	end
 -- The only case a follower appears in party is after a refused mission due to refresh triggered before GARRISON_FOLLOWER_LIST_UPDATE
 	if (status and status~=GARRISON_FOLLOWER_IN_PARTY) then
@@ -3160,14 +2252,12 @@ function addon:FillMissionPage(missionInfo)
 	if( IsControlKeyDown()) then ns.xprint("Shift key, ignoring mission prefill") return end
 	if (self:GetBoolean("NOFILL")) then return end
 	local missionID=missionInfo.missionID
---@debug@
+--[===[@debug@
 	ns.xprint("UpdateMissionPage for",missionID,missionInfo.name,missionInfo.numFollowers)
---@end-debug@
-	--ns.xdump(missionInfo)
-	--self:BuildMissionData(missionInfo.missionID.missionInfo)
+--@end-debug@]===]
 	self:holdEvents()
 	GarrisonMissionPage_ClearParty()
-	local party=parties[missionID]
+	local party=self:GetParty(missionID)
 	if (party) then
 		local members=party.members
 		for i=1,missionInfo.numFollowers do
@@ -3176,9 +2266,9 @@ function addon:FillMissionPage(missionInfo)
 				local status=G.GetFollowerStatus(followerID)
 				if (false and status) then
 					if status == GARRISON_FOLLOWER_IN_PARTY then -- Left from a previous assignment?
---@debug@
+--[===[@debug@
 						ns.xprint(followerID,self:GetFollowerData(followerID,"name"),"was already on mission")
---@end-debug@
+--@end-debug@]===]
 						self:RemoveFromAllMissions(followerID)
 						GarrisonMissionPage_AddFollower(followerID)
 					else
@@ -3192,7 +2282,11 @@ function addon:FillMissionPage(missionInfo)
 			end
 		end
 	end
-	GarrisonMissionPage_UpdateMissionForParty()
+	if ns.toc>=60100 then
+		GarrisonMissionPage_UpdateParty()
+	else
+		GarrisonMissionPage_UpdateMissionForParty()
+	end
 	self:releaseEvents()
 end
 local firstcall=true
@@ -3202,7 +2296,7 @@ local firstcall=true
 --
 function addon:GrowPanel()
 	GCF:Show()
-	if (bigscreen) then
+	if (ns.bigscreen) then
 --		GMF:ClearAllPoints()
 --		GMF:SetPoint("TOPLEFT",GCF,"BOTTOMLEFT")
 --		GMF:SetPoint("TOPRIGHT",GCF,"BOTTOMRIGHT")
@@ -3254,7 +2348,7 @@ function addon:GetBiasColor(followerID,missionID,goodcolor)
 	end
 	return goodcolor
 end
-function addon:RenderFollowerButton(frame,followerID,missionID)
+function addon:RenderFollowerButton(frame,followerID,missionID,b,t)
 	if (not frame) then return end
 	if (frame.Threats) then
 		for i=1,#frame.Threats do
@@ -3284,6 +2378,7 @@ function addon:RenderFollowerButton(frame,followerID,missionID)
 		frame.info=nil
 		return
 	end
+	frame:EnableMouse(true)
 	frame.PortraitFrame.Level:SetTextColor(1,1,1,1)
 	frame.PortraitFrame.Portrait:Show()
 	local info=self:GetFollowerData(followerID)
@@ -3314,22 +2409,27 @@ function addon:RenderFollowerButton(frame,followerID,missionID)
 		frame.PortraitFrame.LevelBorder:SetWidth(58);
 		showItemLevel = false;
 	end
-	GarrisonMissionFrame_SetFollowerPortrait(frame.PortraitFrame, info, showItemLevel);
+	GarrisonMissionFrame_SetFollowerPortrait(frame.PortraitFrame, info, false);
 	-- Counters icon
-	if (frame.Name) then
-		if (missionID and not GMF.MissionTab.MissionList.showInProgress) then
+	if (frame.Name and frame.Threats) then
+		if (missionID and not GMFMissions.showInProgress) then
 			local tohide=1
-			local missionCounters=counters[missionID]
-			local index=counterFollowerIndex[missionID][followerID]
-			for i=1,#index do
-				local k=index[i]
-				local t=frame.Threats[i]
-				local tx=missionCounters[k].icon
-				t.Icon:SetTexture(tx)
-				local color=self:GetBiasColor(missionCounters[k].bias,nil,"Green")
-				t.Border:SetVertexColor(C[color]())
-				t:Show()
-				tohide=i+1
+			if b and b[followerID] then
+				for i=1,#b[followerID] do
+					local th=frame.Threats[tohide]
+					th.Icon:SetTexture(b[followerID][i].icon)
+					th:Show()
+					tohide=tohide+1
+				end
+			end
+			if t and t[followerID] then
+				for i=1,#t[followerID] do
+					if tohide>#frame.Threats then break end
+					local th=frame.Threats[tohide]
+					th.Icon:SetTexture(t[followerID][i].icon)
+					th:Show()
+					tohide=tohide+1
+				end
 			end
 		else
 			frame.Status:Hide()
@@ -3338,11 +2438,12 @@ function addon:RenderFollowerButton(frame,followerID,missionID)
 end
 -- pseudo static
 local scale=0.9
-function addon:BuildFollowersButtons(button,bg,limit)
+
+function addon:BuildFollowersButtons(button,bg,limit,bigscreen)
 	if (bg.Party) then return end
 	bg.Party={}
 	for numMembers=1,3 do
-		local f=CreateFrame("Button",nil,bg,bigscreen and "GarrisonCommanderMissionPageFollowerTemplate" or "GarrisonCommanderMissionPageFollowerTemplateSmall" )
+		local f=CreateFrame("Button","fol_"..button.info.missionID.."_"..numMembers,bg,bigscreen and "GarrisonCommanderMissionPageFollowerTemplate" or "GarrisonCommanderMissionPageFollowerTemplateSmall" )
 		if (numMembers==1) then
 			f:SetPoint("BOTTOMLEFT",button.Rewards[1],"BOTTOMRIGHT",10,0)
 		else
@@ -3354,8 +2455,7 @@ function addon:BuildFollowersButtons(button,bg,limit)
 		end
 		tinsert(bg.Party,f)
 		f:EnableMouse(true)
-		f:SetScript("OnEnter",GarrisonMissionPageFollowerFrame_OnEnter)
-		f:SetScript("OnLeave",GarrisonMissionPageFollowerFrame_OnLeave)
+		f.missionID=button.info.missionID
 		f:RegisterForClicks("AnyUp")
 		f:SetScript("OnClick",function(...) self:OnClick_PartyMember(...) end)
 		if (bigscreen) then
@@ -3381,93 +2481,6 @@ function addon:BuildFollowersButtons(button,bg,limit)
 		button.Threats[1]:SetPoint("TOPLEFT",165,0)
 	end
 end
-function addon:RenderExtraButton(button,numRewards)
-	local panel=button.gcINDICATOR
-	local missionInfo=button.info
-	local missionID=missionInfo.missionID
-	panel.missionID=missionID
-	local mission=missionInfo
-	if not mission then return end -- something went wrong while refreshing
-	if (not bigscreen) then
-		local index=mission.numFollowers+numRewards-3
-		local position=(index * -65) - 130
-		button.gcPANEL.Party[1]:ClearAllPoints()
-		button.gcPANEL.Party[1]:SetPoint("BOTTOMLEFT",button.Rewards[1],"BOTTOMLEFT", position,0)
-	end
-	if (GMF.MissionTab.MissionList.showInProgress) then
-		local perc=select(4,G.GetPartyMissionInfo(missionID))
-		panel.Percent:SetFormattedText(GARRISON_MISSION_PERCENT_CHANCE,perc)
-		panel.Percent:SetJustifyV("CENTER")
-		panel.Age:Hide()
-		panel.Percent:SetTextColor(self:GetDifficultyColors(perc))
-		for i=1,3 do
-			local frame=button.gcPANEL.Party[i]
-			if (missionInfo.followers[i]) then
-				self:RenderFollowerButton(frame,missionInfo.followers[i],missionID)
-				frame:Show()
-			else
-				frame:Hide()
-			end
-		end
-		return
-	else
-		panel.Percent:SetJustifyV("BOTTOM")
-	end
-	local party=parties[missionID]
-	if (#party.members==0) then
-		local mission=self:GetMissionData(missionID) -- matchmaker and fillcounters need our enriched mission
-		self:FillCounters(missionID,mission)
-		self:MatchMaker(missionID,mission,party)
-	end
-	local perc=party.perc
-	local age=tonumber(dbcache.seen[missionID])
-	local notFull=false
-	for i=1,3 do
-		local frame=button.gcPANEL.Party[i]
-		if (i>mission.numFollowers) then
-			frame:Hide()
-		else
-			if (party.members[i]) then
-				self:RenderFollowerButton(frame,party.members[i],missionID)
-				if (bigscreen) then frame.NotFull:Hide() end
-			else
-				self:RenderFollowerButton(frame,false)
-				if (bigscreen) then frame.NotFull:Show() end
-			end
-			frame:Show()
-		end
-	end
-	panel=button.gcINDICATOR
-	panel.Percent:SetFormattedText(GARRISON_MISSION_PERCENT_CHANCE,perc)
-	panel.Percent:SetTextColor(self:GetDifficultyColors(perc))
-	panel.Percent:SetWidth(80)
-	panel.Percent:SetJustifyH("RIGHT")
-	local text
-	if (age) then
-		local expire=ns.wowhead[missionID]
-		if (expire==9999999) then
-			panel.Age:SetText("Expires: Far far away")
-			panel.Age:SetTextColor(C.White())
-		elseif (expire==0) then
-			panel.Age:SetText("Expires: " .. UNKNOWN)
-			panel.Age:SetTextColor(C.White())
-		else
-			local age=(age+(expire*2)-time())/60
-			if age < 0 then age=0 end
-			local hours=(floor((age/60)/6)+1)*6
-			local q=self:GetDifficultyColor(hours+20,true)
-			panel.Age:SetFormattedText("Expires in less than %d hr",hours)
-			panel.Age:SetTextColor(q.r,q.g,q.b)
-		end
-	else
-		panel.Age:SetText(UNKNOWN)
-	end
---@debug@
-	panel.Age:SetText(panel.Age:GetText().. " " .. date("%m/%d/%y %H:%M:%S",age))
---@end-debug@
-	panel.Age:Show()
-	panel.Percent:Show()
-end
 function addon:CheckExpire(missionID)
 	local age=tonumber(dbcache.seen[missionID])
 	local expire=ns.wowhead[missionID]
@@ -3477,19 +2490,8 @@ function addon:CheckExpire(missionID)
 	print("Age+expire",date("%m/%d/%y %H:%M:%S",age+expire))
 	print("Delta",age+expire-time())
 end
-function addon:BuildExtraButton(button)
-	local bg=CreateFrame("Button",nil,button,"GarrisonCommanderMissionButton")
-	local indicators=CreateFrame("Frame",nil,button,"GarrisonCommanderIndicators")
-	indicators:SetPoint("LEFT",70,0)
-	bg:SetPoint("RIGHT")
-	bg.button=button
-	bg:SetScript("OnEnter",function(this) GarrisonMissionButton_OnEnter(this.button) end)
-	bg:SetScript("OnLeave",function() GameTooltip:FadeOut() end)
-	bg:RegisterForClicks("AnyUp")
-	bg:SetScript("OnClick",function(...) self:OnClick_GCMissionButton(...) end)
-	button.gcPANEL=bg
-	button.gcINDICATOR=indicators
-	if (not bg.Party) then self:BuildFollowersButtons(button,bg,3) end
+function addon:BuildExtraButton(button,bigscreen)
+
 end
 function addon:OnShow_FollowerPage(page)
 	if not GCFMissions then return end
@@ -3559,7 +2561,7 @@ function addon:IgnoreFollower(table,missionID,followerID,flag)
 	end
 	-- full ignore disabled for now
 	dbcache.totallyignored[followerID]=nil
-	self:RefreshMission(missionID)
+	self:RefreshMissions(missionID)
 end
 function addon:UnignoreFollower(table,missionID,followerID,flag)
 	if (followerID=='all') then
@@ -3567,7 +2569,7 @@ function addon:UnignoreFollower(table,missionID,followerID,flag)
 	else
 		dbcache.ignored[missionID][followerID]=nil
 	end
-	self:RefreshMission(missionID)
+	self:RefreshMissions(missionID)
 end
 function addon:OpenFollowersTab()
 	GarrisonMissionFrame_SelectTab(2)
@@ -3588,9 +2590,9 @@ function addon:OnClick_GarrisonMissionButton(tab,button)
 		return
 	end
 	if (type(tab.info)~="table") then return end
---@debug@
+--[===[@debug@
 	ns.xprint("Clicked GarrisonMissionButton")
---@end-debug@
+--@end-debug@]===]
 	if (tab.fromFollowerPage) then
 		if (#tab.info.followers>0) then
 			return
@@ -3609,78 +2611,493 @@ function addon:OnClick_GCMissionButton(frame,button)
 	end
 end

-function addon:HookedGarrisonMissionButton_SetRewards(button,rewards,numRewards)
-	if GMF.MissionTab.MissionList.showInProgress and button.info.missionID==button.lastMissionID then
+--[[
+addon.oldSetUp=addon.SetUp
+function addon:ExperimentalSetUp()
+
+end
+addon.SetUp=addon.ExperimentalSetUp
+--]]
+
+
+-- Blizzard functions override
+local function override(blizfunc)
+	if not orig[blizfunc] then
+		orig[blizfunc]=_G[blizfunc]
+		_G[blizfunc]=over[blizfunc]
+	end
+--[===[@debug@
+	print("Overriding ",blizfunc)
+--@end-debug@]===]
+end
+function over.StolenGarrisonMissionPageFollowerFrame_OnEnter(self)
+	if not self.info then
+		return;
+	end
+
+	GarrisonFollowerTooltip:ClearAllPoints();
+	GarrisonFollowerTooltip:SetPoint("TOPLEFT", self, "BOTTOMRIGHT");
+	GarrisonFollowerTooltip_Show(self.info.garrFollowerID,
+		self.info.isCollected,
+		C_Garrison.GetFollowerQuality(self.info.followerID),
+		C_Garrison.GetFollowerLevel(self.info.followerID),
+		C_Garrison.GetFollowerXP(self.info.followerID),
+		C_Garrison.GetFollowerLevelXP(self.info.followerID),
+		C_Garrison.GetFollowerItemLevelAverage(self.info.followerID),
+		C_Garrison.GetFollowerAbilityAtIndex(self.info.followerID, 1),
+		C_Garrison.GetFollowerAbilityAtIndex(self.info.followerID, 2),
+		C_Garrison.GetFollowerAbilityAtIndex(self.info.followerID, 3),
+		C_Garrison.GetFollowerAbilityAtIndex(self.info.followerID, 4),
+		C_Garrison.GetFollowerTraitAtIndex(self.info.followerID, 1),
+		C_Garrison.GetFollowerTraitAtIndex(self.info.followerID, 2),
+		C_Garrison.GetFollowerTraitAtIndex(self.info.followerID, 3),
+		C_Garrison.GetFollowerTraitAtIndex(self.info.followerID, 4),
+		true,
+		C_Garrison.GetFollowerBiasForMission(self.missionID, self.info.followerID) < 0.0
+		);
+end
+
+function over.GarrisonMissionFrame_SetFollowerPortrait(portraitFrame, followerInfo, forMissionPage)
+	local color = ITEM_QUALITY_COLORS[followerInfo.quality];
+	portraitFrame.PortraitRingQuality:SetVertexColor(color.r, color.g, color.b);
+	portraitFrame.LevelBorder:SetVertexColor(color.r, color.g, color.b);
+	if ( forMissionPage ) then
+		local boosted = false;
+		local followerLevel = followerInfo.level;
+		if ( MISSION_PAGE_FRAME.mentorLevel and MISSION_PAGE_FRAME.mentorLevel > followerLevel ) then
+			followerLevel = MISSION_PAGE_FRAME.mentorLevel;
+			boosted = true;
+		end
+		if ( MISSION_PAGE_FRAME.showItemLevel and followerLevel == GarrisonMissionFrame.followerMaxLevel ) then
+			local followerItemLevel = followerInfo.iLevel;
+			if ( MISSION_PAGE_FRAME.mentorItemLevel and MISSION_PAGE_FRAME.mentorItemLevel > followerItemLevel ) then
+				followerItemLevel = MISSION_PAGE_FRAME.mentorItemLevel;
+				boosted = true;
+			end
+			portraitFrame.Level:SetFormattedText(GARRISON_FOLLOWER_ITEM_LEVEL, followerItemLevel);
+			portraitFrame.LevelBorder:SetAtlas("GarrMission_PortraitRing_iLvlBorder");
+			portraitFrame.LevelBorder:SetWidth(70);
+		else
+			portraitFrame.Level:SetText(followerLevel);
+			portraitFrame.LevelBorder:SetAtlas("GarrMission_PortraitRing_LevelBorder");
+			portraitFrame.LevelBorder:SetWidth(58);
+		end
+		local followerBias = C_Garrison.GetFollowerBiasForMission(MISSION_PAGE_FRAME.missionInfo.missionID, followerInfo.followerID);
+		if ( followerBias == -1 ) then
+			portraitFrame.Level:SetTextColor(1, 0.1, 0.1);
+		elseif ( followerBias < 0 ) then
+			portraitFrame.Level:SetTextColor(1, 0.5, 0.25);
+		elseif ( boosted ) then
+			portraitFrame.Level:SetTextColor(0.1, 1, 0.1);
+		else
+			portraitFrame.Level:SetTextColor(1, 1, 1);
+		end
+		portraitFrame.Caution:SetShown(followerBias < 0);
+
+	else
+		portraitFrame.Level:SetText(followerInfo.level);
+	end
+	if ( followerInfo.displayID ) then
+		GarrisonFollowerPortrait_Set(portraitFrame.Portrait, followerInfo.portraitIconID);
+	end
+end
+
+function over.GarrisonMissionPage_Close(self)
+	GarrisonMissionPage_ClearParty();
+	GarrisonMissionFrame.MissionTab.MissionPage:Hide();
+	GarrisonMissionFrame.followerCounters = nil;
+	GarrisonMissionFrame.MissionTab.MissionPage.missionInfo = nil;
+	if (lastTab) then
+		GarrisonMissionFrame_SelectTab(lastTab)
+	end
+	-- I hooked this handler, so I dont want it to be called in the middle of cleanup operations
+	GarrisonMissionFrame.MissionTab.MissionList:Show();
+end
+function over.GarrisonMissionButton_SetRewards(self, rewards, numRewards)
+	if (numRewards > 0) then
+		local index = 1;
+		local party=self.party
+		local mission=self.info
+		for id, reward in pairs(rewards) do
+			if (not self.Rewards[index]) then
+				self.Rewards[index] = CreateFrame("Frame", nil, self, "GarrisonMissionListButtonRewardTemplate");
+				self.Rewards[index]:SetPoint("RIGHT", self.Rewards[index-1], "LEFT", 0, 0);
+			end
+			local Reward = self.Rewards[index];
+			Reward.Quantity:Hide();
+			Reward.itemID = nil;
+			Reward.currencyID = nil;
+			Reward.tooltip = nil;
+			Reward.Quantity:SetTextColor(C.White())
+			if (reward.itemID) then
+				Reward.itemID = reward.itemID;
+				GarrisonMissionFrame_SetItemRewardDetails(Reward);
+				if ( reward.quantity > 1 ) then
+					Reward.Quantity:SetText(reward.quantity);
+					Reward.Quantity:Show();
+				elseif reward.itemID==120205 then
+					Reward.Quantity:SetText(self.info.xp);
+					Reward.Quantity:Show();
+				else
+					local name,link,quality,iLevel,level=GetItemInfo(reward.itemID)
+					if (name) then
+						Reward.Quantity:SetText(iLevel==1 and level or iLevel);
+						Reward.Quantity:SetTextColor(ITEM_QUALITY_COLORS[quality].r,ITEM_QUALITY_COLORS[quality].g,ITEM_QUALITY_COLORS[quality].b)
+						Reward.Quantity:Show();
+					end
+
+				end
+			else
+				Reward.Icon:SetTexture(reward.icon);
+				Reward.title = reward.title
+				if (reward.currencyID and reward.quantity) then
+					if (reward.currencyID == 0) then
+						local multi=party.goldMultiplier or 1
+						Reward.tooltip = GetMoneyString(reward.quantity);
+						Reward.Quantity:SetText(reward.quantity/10000 *multi);
+						Reward.Quantity:Show();
+						if multi >1 then
+							Reward.Quantity:SetTextColor(C:Green())
+						else
+							Reward.Quantity:SetTextColor(C:Gold())
+						end
+					elseif (reward.currencyID == GARRISON_CURRENCY) then
+						local multi=party.materialMultiplier or 1
+						Reward.tooltip = GetMoneyString(reward.quantity);
+						Reward.Quantity:SetText(reward.quantity * multi);
+						Reward.Quantity:Show();
+						if multi >1 then
+							Reward.Quantity:SetTextColor(C:Green())
+						else
+							Reward.Quantity:SetTextColor(C:Gold())
+						end
+					else
+						Reward.currencyID = reward.currencyID;
+						Reward.Quantity:SetText(reward.quantity);
+						Reward.Quantity:Show();
+						Reward.Quantity:SetTextColor(C:Gold())
+					end
+				else
+					if reward.followerXP then
+						Reward.Quantity:SetText(reward.followerXP);
+						Reward.Quantity:Show();
+						if party.xpBonus and party.xpBonus > 0 then
+							Reward.Quantity:SetTextColor(C:Green())
+						else
+							Reward.Quantity:SetTextColor(C:Gold())
+						end
+					end
+					Reward.tooltip = reward.tooltip;
+				end
+			end
+			Reward:Show();
+			index = index + 1;
+		end
+	end
+
+	for i = (numRewards + 1), #self.Rewards do
+		self.Rewards[i]:Hide();
+	end
+end
+do
+	local lastcall=math.floor(GetTime()*10)
+	local progressing
+	function over.GarrisonMissionList_Update()
+		local self = GarrisonMissionFrame.MissionTab.MissionList;
+		local missions;
+		if (self.showInProgress) then
+			-- Ten times in a second is enough...
+			local tick=math.floor(GetTime()*10)
+			if (tick == lastcall) then
+			else
+				collectgarbage("step",100)
+				lastcall=tick
+				return
+			end
+			table.sort(self.inProgressMissions,sorters.EndTime)
+			missions = self.inProgressMissions;
+		else
+			progressing=false
+			missions = self.availableMissions;
+		end
+		local numMissions = #missions;
+		local scrollFrame = self.listScroll;
+		local offset = HybridScrollFrame_GetOffset(scrollFrame);
+		local buttons = scrollFrame.buttons;
+		local numButtons = #buttons;
+
+		if (numMissions == 0) then
+			self.EmptyListString:Show();
+		else
+			self.EmptyListString:Hide();
+		end
+		for i = 1, numButtons do
+			dbg=i==1
+			local button = buttons[i];
+			local index = offset + i; -- adjust index
+			if ( index <= numMissions) then
+				local mission = missions[index];
+				button.id = index;
+				button.info = mission;
+				button.party=addon:GetParty(mission.missionID)
+			else
+				button.id=0
+				button.info=nil
+				button.party=nil
+			end
+			addon:DrawSingleButton(self,button,false,ns.bigscreen)
+		end
+		local totalHeight = numMissions * scrollFrame.buttonHeight;
+		local displayedHeight = numButtons * scrollFrame.buttonHeight;
+		HybridScrollFrame_Update(scrollFrame, totalHeight, displayedHeight);
+	end
+end
+function over.GarrisonMissionPageFollowerFrame_OnEnter(self)
+	if not self.info then
+		return;
+	end
+	GarrisonFollowerTooltip:ClearAllPoints();
+	GarrisonFollowerTooltip:SetPoint("TOPLEFT", self, "BOTTOMRIGHT");
+	GarrisonFollowerTooltip_Show(self.info.garrFollowerID,
+		self.info.isCollected,
+		C_Garrison.GetFollowerQuality(self.info.followerID),
+		C_Garrison.GetFollowerLevel(self.info.followerID),
+		C_Garrison.GetFollowerXP(self.info.followerID),
+		C_Garrison.GetFollowerLevelXP(self.info.followerID),
+		C_Garrison.GetFollowerItemLevelAverage(self.info.followerID),
+		C_Garrison.GetFollowerAbilityAtIndex(self.info.followerID, 1),
+		C_Garrison.GetFollowerAbilityAtIndex(self.info.followerID, 2),
+		C_Garrison.GetFollowerAbilityAtIndex(self.info.followerID, 3),
+		C_Garrison.GetFollowerAbilityAtIndex(self.info.followerID, 4),
+		C_Garrison.GetFollowerTraitAtIndex(self.info.followerID, 1),
+		C_Garrison.GetFollowerTraitAtIndex(self.info.followerID, 2),
+		C_Garrison.GetFollowerTraitAtIndex(self.info.followerID, 3),
+		C_Garrison.GetFollowerTraitAtIndex(self.info.followerID, 4),
+		true,
+		self.missionID and self.info.followerID and
+		C_Garrison.GetFollowerBiasForMission(self.missionID, self.info.followerID) < 0.0
+		or false
+		);
+end
+function over.GarrisonMissionButton_OnEnter(self, button)
+	if ns.toc < 60100 then
 --[===[@non-debug@
-		collectgarbage("step",50)
+		return orig.GarrisonMissionButton_OnEnter(self,button)
 --@end-non-debug@]===]
-		return
+	addon:RenderTooltip(self.info.missionID)
+--[===[@debug@
+	orig.GarrisonMissionButton_OnEnter(self,button)
+	GameTooltip:AddDoubleLine("MissionID",self.info.missionID)
+	GameTooltip:AddDoubleLine("xp",addon:GetMissionData(self.info.missionID,'xp'))
+	GameTooltip:AddDoubleLine("xpBonus",addon:GetMissionData(self.info.missionID,'xpBonus'))
+
+	GameTooltip:Show()
+	return
+--@end-debug@]===]
+	end
+	if (self.info == nil) then
+		return;
 	end
-	button.lastMissionID=button.info.missionID
-	return self:RenderButton(button,rewards,numRewards)
+
+	GameTooltip:SetOwner(self, "ANCHOR_CURSOR_RIGHT");
+
+	if(self.info.inProgress) then
+		GarrisonMissionButton_SetInProgressTooltip(self.info);
+	else
+		GameTooltip:SetText(self.info.name);
+		GameTooltip:AddLine(string.format(GARRISON_MISSION_TOOLTIP_NUM_REQUIRED_FOLLOWERS, self.info.numFollowers), 1, 1, 1);
+		GarrisonMissionButton_AddThreatsToTooltip(self.info.missionID);
+		--if (self.info.isRare) then
+		if ns.toc >=60100 then
+			GameTooltip:AddLine(GARRISON_MISSION_AVAILABILITY);
+			GameTooltip:AddLine(self.info.offerTimeRemaining, 1, 1, 1);
+		end
+		if not C_Garrison.IsOnGarrisonMap() then
+			GameTooltip:AddLine(" ");
+			GameTooltip:AddLine(GARRISON_MISSION_TOOLTIP_RETURN_TO_START, nil, nil, nil, 1);
+		end
+		addon:AddFollowersToTooltip(self.info.missionID)
+		GameTooltip:Show()
+	end
+--@debu@
+	GameTooltip:AddDoubleLine("MissionID",self.info.missionID)
+--@end-debug@]===]
+	GameTooltip:Show();
+	GarrisonMissionFrame.MissionTab.MissionList.newMissionIDs[self.info.missionID] = nil;
+	--GarrisonMissionList_Update();
 end
-function addon:RenderButton(button,rewards,numRewards)
-	if (not button or not button.Title) then
---@debug@
-		error(strconcat("Called on I dunno what ",tostring(button)," ", tostring(button:GetName())))
-		return
---@end-debug@
+---@function
+-- Main mission button draw routine.
+-- @param page GarrisonMissionFrameMissions or nil.
+-- @param button scrolllist element
+-- @param progressing true if at second iteration of progress page
+-- @param bigscreen enlarge button or not
+
+function addon:DrawSingleButton(page,button,progressing,bigscreen)
+	local mission=button.info
+	if mission then
+		local missionID=mission.missionID
+		if not button.party then button.party=self:GetParty(missionID) end
+		self:AddStandardDataToButton(page,button,mission,missionID,bigscreen)
+		self:AddIndicatorToButton(button,mission,missionID,bigscreen)
+		over.GarrisonMissionButton_SetRewards(button, mission.rewards, mission.numRewards);
+		self:AddFollowersToButton(button,mission,missionID,bigscreen)
+		if page and not self:IsRewardPage() then
+			addon:AddThreatsToButton(button,mission,missionID,bigscreen)
+		end
+		local a1,f,a2,h,v=button.Title:GetPoint(1)
+		v=v+10
+		button.Title:ClearAllPoints()
+		button.Title:SetPoint(a1,f,a2,h,v)
+		button:Show();
+
+	else
+		button:Hide();
+		button.info=nil
+	end
+end
+function addon:DrawSingleSlimButton(page,button,progressing,bigscreen)
+	local mission=button.info
+	if mission then
+		local missionID=mission.missionID
+		local frame=button
+		self:AddStandardDataToButton(page,button,mission,missionID,bigscreen)
+		over.GarrisonMissionButton_SetRewards(button, mission.rewards, mission.numRewards);
+		self:AddFollowersToButton(button,mission,missionID,bigscreen)
+		frame.Title:SetPoint("TOPLEFT",frame.Percent,"TOPLEFT",0,5)
+		frame.Success:SetPoint("LEFT",frame.Percent,"RIGHT",0,0)
+		frame.Failure:SetPoint("LEFT",frame.Percent,"RIGHT",0,0)
+		frame.Summary:ClearAllPoints()
+		frame.Summary:SetPoint("BOTTOMLEFT",frame.Title,"BOTTOMRIGHT",0,0)
+		button:Show();
+	else
+		button:Hide();
+		button.info=nil
+	end
+end
+function addon:AddStandardDataToButton(page,button,mission,missionID,bigscreen)
+	button.Title:SetWidth(0);
+	button.Title:SetText(mission.name);
+	button.Level:SetText(mission.level);
+	if ( mission.durationSeconds >= GARRISON_LONG_MISSION_TIME ) then
+		local duration = format(GARRISON_LONG_MISSION_TIME_FORMAT, mission.duration);
+		button.Summary:SetFormattedText(PARENS_TEMPLATE, duration);
+	else
+		button.Summary:SetFormattedText(PARENS_TEMPLATE, mission.duration);
+	end
+	if ( mission.locPrefix ) then
+		button.LocBG:Show();
+		button.LocBG:SetAtlas(mission.locPrefix.."-List");
+	else
+		button.LocBG:Hide();
+	end
+	if (mission.isRare) then
+		button.RareOverlay:Show();
+		button.RareText:Show();
+		button.IconBG:SetVertexColor(0, 0.012, 0.291, 0.4)
+	else
+		button.RareOverlay:Hide();
+		button.RareText:Hide();
+		button.IconBG:SetVertexColor(0, 0, 0, 0.4)
+	end
+	local showingItemLevel = false;
+	if ( mission.level == GARRISON_FOLLOWER_MAX_LEVEL and mission.iLevel > 0 ) then
+		button.ItemLevel:SetFormattedText(NUMBER_IN_PARENTHESES, mission.iLevel);
+		button.ItemLevel:Show();
+		showingItemLevel = true;
+	else
+		button.ItemLevel:Hide();
+	end
+	if ( showingItemLevel and mission.isRare ) then
+		button.Level:SetPoint("CENTER", button, "TOPLEFT", 40, -22);
+	else
+		button.Level:SetPoint("CENTER", button, "TOPLEFT", 40, -36);
+	end
+
+	button:Enable();
+	if (mission.inProgress) then
+		button.Overlay:Show();
+		button.Summary:SetText(mission.timeLeft.." "..RED_FONT_COLOR_CODE..GARRISON_MISSION_IN_PROGRESS..FONT_COLOR_CODE_CLOSE);
+	else
+		button.Overlay:Hide();
 	end
-	local missionID=button.info.missionID
 	if (bigscreen) then
+		button.Rewards[1]:SetPoint("RIGHT",button,"RIGHT",-500 - (GMM and 40 or 0),0)
 		local width=GMF.MissionTab.MissionList.showInProgress and BIGBUTTON or SMALLBUTTON
 		button:SetWidth(width+600)
 		button.Rewards[1]:SetPoint("RIGHT",button,"RIGHT",-500 - (GMM and 40 or 0),0)
 	end
-	if (not button.xp) then
-		button.xp=button:CreateFontString(nil, "ARTWORK", "QuestMaprewardsFont")
-		button.xp:SetPoint("TOPRIGHT",button.Rewards[1],"TOPRIGHT")
-		button.xp:SetJustifyH("RIGHT")
-	end
 	button.MissionType:SetPoint("TOPLEFT",5,-2)
-	button.xp:SetWidth(0)
-	if (not GMF.MissionTab.MissionList.showInProgress) then
-		button.xp:SetFormattedText("Xp: %d (approx)",self:GetMissionData(missionID,'globalXp'))
-		button.xp:SetTextColor(self:GetDifficultyColors(self:GetMissionData(missionID,'totalXp')/3000*100))
-		button.xp:Show()
-	else
-		button.xp:Hide()
+	if page and ns.toc>=60100 then
+		local isNewMission = page.newMissionIDs[mission.missionID];
+		if (isNewMission) then
+			if (not button.NewHighlight) then
+				button.NewHighlight = CreateFrame("Frame", nil, button, "GarrisonMissionListButtonNewHighlightTemplate");
+				button.NewHighlight:SetPoint("TOPLEFT", button, "TOPLEFT", 0, 0);
+				button.NewHighlight:SetPoint("BOTTOMRIGHT", button, "BOTTOMRIGHT", 0, 0);
+			end
+			button.NewHighlight:Show();
+		else
+			if (button.NewHighlight) then
+				button.NewHighlight:Hide();
+			end
+		end
 	end
-	--button.Title:SetText("123456789012345678901234567890123456789012345678901234567890") -- Used for design
-	local offset= bigscreen and (numRewards *65) or (button.info.numFollowers+numRewards) *65
-	local tw=button:GetWidth() - 165
-	if (button.Title:GetWidth() + button.Summary:GetWidth() + button.xp:GetWidth() < (tw -offset) ) then
-		button.Title:SetPoint("LEFT", 165, 5);
+	local n=mission.numRewards
+	local w=button:GetWidth()-175 -- 655 for standard 830 button
+	if button:GetWidth()<1000 then n=n+mission.numFollowers end
+	--print(w,button.Title:GetWidth() + button.Summary:GetWidth() + 8,n,w - n * 65)
+	if ( button.Title:GetWidth() + button.Summary:GetWidth() + 8 < w - n * 65 ) then
+		button.Title:SetPoint("LEFT", 165, 0);
 		button.Summary:ClearAllPoints();
 		button.Summary:SetPoint("BOTTOMLEFT", button.Title, "BOTTOMRIGHT", 8, 0);
 	else
-		button.Title:SetPoint("LEFT", 165, 25);
-		button.Title:SetWidth(tw - offset);
+		button.Title:SetPoint("LEFT", 165, 10);
+		button.Title:SetWidth(w - n * 65);
 		button.Summary:ClearAllPoints();
 		button.Summary:SetPoint("TOPLEFT", button.Title, "BOTTOMLEFT", 0, -4);
 	end
+	button.MissionType:SetAtlas(mission.typeAtlas);
+end
+if ns.toc < 60100 then
+function GarrisonMissionButton_CheckTooltipThreat()
+	--print("checktooltip placeholder")
+end
+end
+function addon:AddThreatsToButton(button,mission,missionID,bigscreen)
 	local threatIndex=1
 	if (not GMF.MissionTab.MissionList.showInProgress) then
 		if (not button.Threats) then -- I am a good guy. If MP present, I dont make my own threat indicator (Only MP <= 1.8)
-			if (not button.Env) then
-				button.Env=CreateFrame("Frame",nil,button,"GarrisonAbilityCounterTemplate")
-				button.Env:SetWidth(20)
-				button.Env:SetHeight(20)
-				button.Env:SetPoint("BOTTOMLEFT",button,165,8)
-				button.GcThreats={}
-			end
-			local slots=self:GetMissionData(missionID,'slots')
 			if (not GMF.MissionTab.MissionList.showInProgress) then
-				button.Env:Show()
-				for i=1,#slots do
-					local slot=slots[i]
-					if (slot.name==TYPE) then
-						button.Env.Icon:SetTexture(slot.icon)
-						self:SetThreatColor(button.Env,missionID)
-						button.Env.info=self.db.global.types[slot.key]
-						button.Env:SetScript("OnEnter",function(...) addon:ClonedGarrisonMissionMechanic_OnEnter(missionID,...) end)
-						button.Env:SetScript("OnLeave",function() GameTooltip:Hide() end)
+				if (not button.Env) then
+					button.Env=CreateFrame("Frame",nil,button,"GarrisonAbilityCounterTemplate")
+					button.Env:SetWidth(20)
+					button.Env:SetHeight(20)
+					button.Env:SetPoint("BOTTOMLEFT",button,165,8)
+					button.GcThreats={}
+				end
+				local party=self:GetParty(missionID)
+				if mission.typeIcon then
+					button.Env.IsEnv=true
+					button.Env:Show()
+					button.Env.Icon:SetTexture(mission.typeIcon)
+					if (party.isEnvMechanicCountered) then
+						button.Env.Border:SetVertexColor(C.Green())
 					else
+						button.Env.Border:SetVertexColor(C.Red())
+					end
+					button.Env.Description=mission.typeDesc
+					button.Env.Name=mission.type
+					button.Env:SetScript("OnEnter",addon.ClonedGarrisonMissionMechanic_OnEnter)
+					button.Env:SetScript("OnLeave",function() GameTooltip:Hide() end)
+				else
+					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
 						local th=button.GcThreats[threatIndex]
 						if (not th) then
 							th=CreateFrame("Frame",nil,button,"GarrisonAbilityCounterTemplate")
@@ -3689,45 +3106,17 @@ function addon:RenderButton(button,rewards,numRewards)
 							th:SetPoint("BOTTOMLEFT",button,165 + 35 * threatIndex,8)
 							button.GcThreats[threatIndex]=th
 						end
-						th.info=slot
-						threatIndex=threatIndex+1
-						th.Icon:SetTexture(slot.icon)
-						self:SetThreatColor(th,missionID)
+						self:SetThreatColor(th,self:GetParty(missionID,'threats')[threatIndex])
+						th.Icon:SetTexture(mechanic.icon)
+						th.Name=mechanic.name
+						th.Description=mechanic.description
+						--GarrisonMissionButton_CheckTooltipThreat(th,missionID,mechanicID,counteredThreats)
 						th:Show()
-						th:SetScript("OnEnter",function(...) addon:ClonedGarrisonMissionMechanic_OnEnter(missionID,...) end)
+						th:SetScript("OnEnter",addon.ClonedGarrisonMissionMechanic_OnEnter)
 						th:SetScript("OnLeave",function() GameTooltip:Hide() end)
+						threatIndex=threatIndex+1
 					end
 				end
-			else
-				button.Env:Hide()
-			end
-		end
-		if (numRewards > 0) then
-			local index=1
-			for id,reward in pairs(rewards) do
-				local Reward = button.Rewards[index];
-				Reward.Quantity:SetTextColor(C.Yellow())
-				if (reward.followerXP) then
-					Reward.Quantity:SetText(reward.followerXP)
-					Reward.Quantity:Show()
-				elseif (reward.currencyID==0) then
-					Reward.Quantity:SetFormattedText("%d",reward.quantity/10000)
-					Reward.Quantity:Show()
-				elseif (reward.itemID and reward.itemID==120205) then
-					Reward.Quantity:SetFormattedText("%d",self:GetMissionData(missionID,'xp') or 1)
-					Reward.Quantity:Show()
-				elseif (reward.itemID and reward.quantity==1) then
-					local _,_,q,i=GetItemInfo(reward.itemID)
-					Reward.Quantity:SetText(i)
-					local c=ITEM_QUALITY_COLORS[q]
-					if (not c) then
-						Reward.Quantity:SetTextColor(1,1,1)
-					else
-						Reward.Quantity:SetTextColor(c.r,c.g,c.b)
-					end
-					Reward.Quantity:Show()
-				end
-				index=index+1
 			end
 		end
 	else
@@ -3738,33 +3127,108 @@ function addon:RenderButton(button,rewards,numRewards)
 			button.GcThreats[i]:Hide()
 		end
 	end
-	if (button.fromFollowerPage) then
-		return
+end
+function addon:CalculateAge(missionID)
+	return "Available in 6.1"
+end
+function addon:AddIndicatorToButton(button,mission,missionID,bigscreen)
+	if not button.gcINDICATOR then
+		local indicators=CreateFrame("Frame",nil,button,"GarrisonCommanderIndicators")
+		indicators:SetPoint("LEFT",70,0)
+		button.gcINDICATOR=indicators
 	end
-	if (not button.gcPANEL) then
-		self:BuildExtraButton(button)
+	local panel=button.gcINDICATOR
+	local perc=select(4,G.GetPartyMissionInfo(missionID))
+	if button.party then perc=button.party.perc end
+	panel.Percent:SetFormattedText(GARRISON_MISSION_PERCENT_CHANCE,perc)
+	panel.Percent:SetTextColor(self:GetDifficultyColors(perc))
+	panel.Percent:SetWidth(80)
+	panel.Percent:Show()
+	if (GMFMissions.showInProgress) then
+		panel.Percent:SetJustifyV("CENTER")
+		panel.Age:Hide()
+	else
+		panel.Percent:SetJustifyH("RIGHT")
+		panel.Age:SetFormattedText("Expires in \n%s",mission.offerTimeRemaining or self:CalculateAge(missionID))
+		panel.Age:SetTextColor(C.White())
+		local age=(mission.offerEndTime or GetTime() + 3600*24) -GetTime()
+		if age < 0 then age=0 end
+		local hours=floor(age/3600)
+		local q=self:GetDifficultyColor(hours+20,true)
+		panel.Age:SetTextColor(q.r,q.g,q.b)
+		panel.Age:Show()
+	end
+-- XP display
+	if (not GMFMissions.showInProgress) then
+		if (not button.xp) then
+			button.xp=button:CreateFontString(nil, "ARTWORK", "QuestMaprewardsFont")
+			button.xp:SetPoint("TOP",button.Rewards[1],"TOP")
+			button.xp:SetJustifyH("CENTER")
+		end
+		button.xp:SetWidth(0)
+		local xp=(self:GetMissionData(missionID,'xp')+self:GetMissionData(missionID,'xpBonus')+(self:GetParty(missionID)['xpBonus'] or 0) )*button.info.numFollowers
+		button.xp:SetFormattedText("Xp: %d",xp)
+		button.xp:SetTextColor(self:GetDifficultyColors(xp/3000*100))
+		button.xp:Show()
+	else
+		button.xp:Hide()
 	end
-	return self:RenderExtraButton(button,numRewards)
 end
-
---[[
-addon.oldSetUp=addon.SetUp
-function addon:ExperimentalSetUp()
-
+function addon:AddFollowersToButton(button,mission,missionID,bigscreen)
+	if (not button.gcPANEL) then
+		local bg=CreateFrame("Button",nil,button,"GarrisonCommanderMissionButton")
+		bg:SetPoint("RIGHT")
+		bg.button=button
+		bg:SetScript("OnEnter",function(this) GarrisonMissionButton_OnEnter(this.button) end)
+		bg:SetScript("OnLeave",function() GameTooltip:FadeOut() end)
+		bg:RegisterForClicks("AnyUp")
+		bg:SetScript("OnClick",function(...) self:OnClick_GCMissionButton(...) end)
+		button.gcPANEL=bg
+		if (not bg.Party) then self:BuildFollowersButtons(button,bg,3,bigscreen) end
+	end
+	local missionInfo=button.info
+	local missionID=missionInfo.missionID
+	local mission=missionInfo
+	if not mission then ns.print("Non ho la missione") return end -- something went wrong while refreshing
+	if (not bigscreen) then
+		local index=mission.numFollowers+mission.numRewards-3
+		local position=(index * -65) - 130
+		button.gcPANEL.Party[1]:ClearAllPoints()
+		button.gcPANEL.Party[1]:SetPoint("BOTTOMLEFT",button.Rewards[1],"BOTTOMLEFT", position,0)
+	end
+	local party=button.party
+	if not party then party=self:GetParty(missionID) end
+	local t,b
+	if not GMFMissions.showInProgress then
+		b=G.GetBuffedFollowersForMission(missionID)
+		t=G.GetFollowersTraitsForMission(missionID)
+	end
+	for i=1,3 do
+		local frame=button.gcPANEL.Party[i]
+		if (i>mission.numFollowers) then
+			frame:Hide()
+		else
+			if (party.members[i]) then
+				self:RenderFollowerButton(frame,party.members[i],missionID,b,t)
+				if (frame.NotFull) then frame.NotFull:Hide() end
+			else
+				self:RenderFollowerButton(frame,false)
+				if (frame.NotFull) then frame.NotFull:Show() end
+			end
+			frame:Show()
+		end
+	end
 end
-addon.SetUp=addon.ExperimentalSetUp
---]]
--- Need to overrid in order to stop events while cleaning missions

-function GarrisonMissionPage_Close(self)
+--hooksecurefunc("GarrisonMissionList_Update",function(...)print("Original GarrisonMissionList_Update",...)end)
+override("GarrisonMissionPage_Close")
+override("GarrisonMissionList_Update")
+override("GarrisonMissionButton_SetRewards")
+override("GarrisonMissionButton_OnEnter")
+override("GarrisonMissionPageFollowerFrame_OnEnter")

-	GarrisonMissionFrame.MissionTab.MissionPage:Hide();
-	GarrisonMissionFrame.MissionTab.MissionList:Show();
-	GarrisonMissionPage_ClearParty();
-	GarrisonMissionFrame.followerCounters = nil;
-	GarrisonMissionFrame.MissionTab.MissionPage.missionInfo = nil;
-	if (lastTab) then
-		GarrisonMissionFrame_SelectTab(lastTab)
-	end
+GMF.MissionTab.MissionPage.CloseButton:SetScript("OnClick",over.GarrisonMissionPage_Close)
+for i=1,#GMFMissionListButtons do
+	local b=GMFMissionListButtons[i]
+	b:SetScript("OnEnter",over.GarrisonMissionButton_OnEnter)
 end
-GMF.MissionTab.MissionPage.CloseButton:SetScript("OnClick",GarrisonMissionPage_Close)
diff --git a/GarrisonCommander.toc b/GarrisonCommander.toc
index f381854..3e2ddae 100644
--- a/GarrisonCommander.toc
+++ b/GarrisonCommander.toc
@@ -4,7 +4,7 @@
 ## Notes-itIT: Ti aiuta a scegliere il giusto seguace per la giusta missione
 ## Notes-frFR: Vous aide au moment de choisir le droit utilisateur pour la bonne mission
 ## Author: Alar of Daggerspine
-## Version: @project-version@ @project-abbreviated-hash@
+## Version: @project-version@
 ## X-Version: 2.3.0-Beta4
 ## X-Revision: @project-abbreviated-hash@
 ## eMail: alar@aspide.it
diff --git a/Init.lua b/Init.lua
index b036a73..99cb6d9 100644
--- a/Init.lua
+++ b/Init.lua
@@ -1,11 +1,26 @@
 local me, ns = ...
 local _G=_G
 local pp=print
---@debug@
+local setmetatable=setmetatable
+local next=next
+local pairs=pairs
+local wipe=wipe
+local GetChatFrame=GetChatFrame
+local format=format
+local GetTime=GetTime
+local strjoin=strjoin
+local tostringall=tostringall
+--[===[@debug@
 LoadAddOn("Blizzard_DebugTools")
---@end-debug@
+--@end-debug@]===]
 ns.addon=LibStub("LibInit"):NewAddon(me,'AceHook-3.0','AceTimer-3.0','AceEvent-3.0','AceBucket-3.0')
+local chatframe=ns.addon:GetChatFrame("aDebug")
+local function pd(...)
+	--if (chatframe) then chatframe:AddMessage(format("GC:%6.3f %s",GetTime(),strjoin(' ',tostringall(...)))) end
+	pp(format("|cff808080GC:%6.3f|r %s",GetTime(),strjoin(' ',tostringall(...))))
+end
 local addon=ns.addon --#addon
+ns.toc=select(4,GetBuildInfo())
 ns.AceGUI=LibStub("AceGUI-3.0")
 ns.D=LibStub("LibDeformat-3.0")
 ns.C=ns.addon:GetColorTable()
@@ -16,32 +31,28 @@ ns.trace=ns.addon:Wrap("Trace")
 ns.xprint=function() end
 ns.xdump=function() end
 ns.xtrace=function() end
---@debug@
-ns.xprint=function(...) pp("|cffff9900DBG|r",...) end
-ns.xdump=function(d,t) pp("|cffff9900DMP|r",t) DevTools_Dump(d) end
-ns.xtrace=ns.trace
---@end-debug@
-local setmetatable=setmetatable
-local next=next
-local pairs=pairs
-local wipe=wipe
+--[===[@debug@
+--ns.xprint=function(...) pd("|cffff9900DBG|r",...) end
+--ns.xdump=function(d,t) pp("|cffff9900DMP|r",t) DevTools_Dump(d) end
+--ns.xtrace=ns.trace
+--@end-debug@]===]
 do
-	--@debug@
+	--[===[@debug@
 	local newcount, delcount,createdcount,cached = 0,0,0
-	--@end-debug@
+	--@end-debug@]===]
 	local pool = setmetatable({},{__mode="k"})
 	function ns.new()
-	--@debug@
+	--[===[@debug@
 		newcount = newcount + 1
-	--@end-debug@
+	--@end-debug@]===]
 		local t = next(pool)
 		if t then
 			pool[t] = nil
 			return t
 		else
-	--@debug@
+	--[===[@debug@
 			createdcount = createdcount + 1
-	--@end-debug@
+	--@end-debug@]===]
 			return {}
 		end
 	end
@@ -53,13 +64,13 @@ do
 		return c
 	end
 	function ns.del(t)
-	--@debug@
+	--[===[@debug@
 		delcount = delcount + 1
-	--@end-debug@
+	--@end-debug@]===]
 		wipe(t)
 		pool[t] = true
 	end
-	--@debug@
+	--[===[@debug@
 	function cached()
 		local n = 0
 		for k in pairs(pool) do
@@ -73,7 +84,7 @@ do
 		ns.print("Released:",delcount)
 		ns.print("Cached:",cached())
 	end
-	--@end-debug@
+	--@end-debug@]===]
 end

 local stacklevel=0
@@ -98,3 +109,12 @@ function addon:releaseEvents()
 	end
 end
 local holdEvents,releaseEvents=addon.holdEvents,addon.releaseEvents
+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
+})
+
diff --git a/MatchMaker.lua b/MatchMaker.lua
index 1b73738..0d8f7dc 100644
--- a/MatchMaker.lua
+++ b/MatchMaker.lua
@@ -1,5 +1,4 @@
 local me,ns=...
-local xprint=ns.xprint
 local pp=print
 local addon=ns.addon --#addon
 local C=ns.C
@@ -15,335 +14,224 @@ local format=format
 local tonumber=tonumber
 local tinsert=tinsert
 local tremove=tremove
-local dbg
+local loadstring=loadstring
+local assert=assert
+local rawset=rawset
+local strsplit=strsplit
 local epicMountTrait=221
 local extraTrainingTrait=80 --all followers +35
 local fastLearnerTrait=29 -- only this follower +50
 local hearthStoneProTrait=236 -- all followers +36
 local scavengerTrait=79 -- More resources
-local function best(fid1,fid2,counters,mission)
-	if (not fid1) then return fid2 end
-	if (not fid2) then return fid1 end
-	local f1,f2=counters[fid1],counters[fid2]
-	if (dbg) then
-		xprint("Current",fid1,n[f1.followerID]," vs Candidate",fid2,n[f2.followerID])
+local dbg
+local xprint=function(...) if dbg then ns.xprint(...) end end
+local xdump=function(...) if dbg then ns.xdump(...) end end
+function addon:MissionScore(mission)
+	local totalTimeString, totalTimeSeconds, isMissionTimeImproved, successChance, partyBuffs, isEnvMechanicCountered, xpBonus, materialMultiplier,goldMultiplier = G.GetPartyMissionInfo(mission.missionID)
+	local r=mission.class=='resource' and materialMultiplier or xpBonus/10
+	local t=isMissionTimeImproved and 1 or 0
+	if r > 9999 then r= 999 end
+	return format("%03d%03d%01d",successChance,r,t)
+end
+function addon:FollowerScore(mission,followerID)
+	local totalTimeString, totalTimeSeconds, isMissionTimeImproved, successChance, partyBuffs, isEnvMechanicCountered, xpBonus, materialMultiplier,goldMultiplier = G.GetPartyMissionInfo(mission.missionID)
+	local r=0
+	if mission.class=='resource' then r=materialMultiplier
+	--elseif mission.class=='xp' then r= xpBonus -- all time bonus
+	elseif mission.class=='gold' then r= goldMultiplier
+	else r=xpBonus>0 and 1 or 0
 	end
-	if (P:IsIn(f1.followerID)) then return fid1 end
-	if (f2.bias<0) then return fid1 end
-	if (f2.bias>f1.bias) then return fid2 end
-	if (f1.bias == f2.bias) then
-		if (mission.resources > 0 ) then
-			if addon:HasTrait(f1.followerID,scavengerTrait) then
-				return fid1
+
+	local t=isMissionTimeImproved and 1 or 0
+	local c=(isEnvMechanicCountered and 1 or 0)+#partyBuffs
+	return format("%03d%01d%01d%01d%04d",successChance,r,c,t,10000-self:GetFollowerData(followerID,'rank',0))
+end
+local filters={skipMaxed=false,skipBusy=false}
+function filters.nop(followerID)
+	return true
+end
+function filters.maxed(followerID,missionID)
+	return filters.skipMaxed and addon:GetFollowerData(followerID,'maxed') or false
+end
+function filters.busy(followerID,missionID)
+	return not addon:IsFollowerAvailableForMission(followerID,filters.skipBusy)
+end
+function filters.ignored(followerID,missionID)
+	return addon:IsIgnored(followerID,missionID)
+end
+function filters.generic(followerID,missionID)
+	return filters.busy(followerID,missionID) or filters.ignored(followerID,missionID)
+end
+function filters.xp(followerID,missionID)
+	return filters.maxed(followerID,missionID) or filters.generic(followerID,missionID)
+end
+--alias
+filters.resources=filters.generic
+filters.gold=filters.generic
+filters.equip=filters.generic
+filters.followerequip=filters.generic
+filters.epic=filters.generic
+local function CreateFilter(missionClass)
+	local code = [[
+	local filters,xprint,pairs = ...
+	local function filterdata(followers,missionID)
+		for followerID,_ in pairs(followers) do
+			if TEST then
+				xprint("Removing",C_Garrison.GetFollowerName(followerID),"due to TEST", TEST)
+				followers[followerID] = nil
+			else
+				xprint("Keeping",C_Garrison.GetFollowerName(followerID),"due to TEST", TEST)
 			end
 		end
-		if (f2.quality < f1.quality or f2.rank < f1.rank) then return fid2 end
 	end
-	return fid1
+	return filterdata
+	]]
+	code = code:gsub("TEST", " filters." ..missionClass .."(followerID,missionID)")
+	xprint("Compiling ",missionClass,"filter")
+	return assert(loadstring(code, "Filter for " .. missionClass))(filters,xprint,pairs)
 end
-local function filter()
-	if (missionCounters) then
-		for i=1,#missionCounters do
-			local followerID=missionCounters[i].followerID
-			if (not followerID) then
-				if (dbg) then xprint("Trying to use [",followerID,"]") end
-			else
-				if (self:IsIgnored(followerID,missionID)) then
-					if (dbg) then xprint("Skipped",n[followerID],"due to ignored" ) end
-					P:Ignore(followerID,true)
-				elseif not self:IsFollowerAvailableForMission(followerID,skipBusy) then
-					if (dbg) then xprint("Skipped",n[followerID],"due to busy" ) end
-					P:Ignore(followerID)
-				elseif (skipMaxed and mission.xpOnly and self:GetFollowerData(followerID,'maxed')) then
-					if (dbg) then print("Skipped",n[followerID],"due to maxed" ) end
-					P:Ignore(followerID,true)
+
+local filterTypes = setmetatable({}, {__index=function(self, missionClass)
+	local filter = CreateFilter(missionClass)
+	rawset(self, missionClass, CreateFilter(missionClass))
+	return filter
+end})
+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)
+		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
+					candidate=followerID
+					candidateScore=newScore
 				end
+				P:RemoveFollower(followerID)
 			end
 		end
-	end
-end
-function addon:MissionScore(mission)
-	local totalTimeString, totalTimeSeconds, isMissionTimeImproved, successChance, partyBuffs, isEnvMechanicCountered, xpBonus, materialMultiplier = G.GetPartyMissionInfo(mission.missionID)
-	return format("%03d%01d%01d%01d",successChance,isEnvMechanicCountered and 1 or 0,materialMultiplier*mission.resources,#partyBuffs)
-end
-function addon:FollowerScore(mission)
-	local totalTimeString, totalTimeSeconds, isMissionTimeImproved, successChance, partyBuffs, isEnvMechanicCountered, xpBonus, materialMultiplier = G.GetPartyMissionInfo(mission.missionID)
-	return format("%03d%01d%02d%01d",successChance,isEnvMechanicCountered and 1 or 0,(mission.resources * (materialMultiplier-1)) , (isMissionTimeImproved and 1 or 0))
-end
-local filters={}
-function filters.Xp(data)
-	for k,_ in pairs(data) do
-		if (addon:GetFollowerData(k,'maxed')) then
-			data[k]=nil
+		if candidate then
+			xprint(C(score,'yellow'),G.GetFollowerName(candidate),"assigned to slot",p+1)
+			P:AddFollower(candidate)
+			candidate=nil
 		end
 	end
 end
-function addon:MatchMakerEpic(missionID,mission,party,filter)
-	if (GMFRewardSplash:IsShown()) then return end
-	if (not mission) then mission=self:GetMissionData(missionID) end
-	if (not party) then party=self:GetParty(missionID) end
-	local skipBusy=self:GetBoolean("IGM")
-	local skipMaxed=self:GetBoolean("IGP")
+local function MatchMaker(self,missionID,party,includeBusy,onlyBest)
+	local mission=self:GetMissionData(missionID)
+	local class=self:GetMissionData(missionID,'class')
+	xprint(C(format("MATCHMAKER %s (%d) class: %s",mission.name,missionID,class),'Orange'),includeBusy and "Busy" or "Ready")
+	local filter=filters[class]
+	filters.skipMaxed=self:GetBoolean("IGP")
+	if (includeBusy==nil) then
+		filters.skipBusy=self:GetBoolean("IGM")
+	else
+		filters.skipBusy=not includeBusy
+	end
 	local scores=new()
-	local traits=new()
-	dbg=missionID==(tonumber(_G.MW) or 0)
-	if (dbg) then xprint(C("Matchmaking mission","Red"),missionID,mission.name) end
 	local buffed=G.GetBuffedFollowersForMission(missionID)
-	if (filter) then
-		filters[filter](buffed)
-	end
+	local traits=G.GetFollowersTraitsForMission(missionID)
+	local buffeds=0
 	local mechanics=G.GetMissionUncounteredMechanics(missionID)
 	P:Open(missionID,mission.numFollowers)
 	--G.GetFollowerBiasForMission(missionID,followerID)
 	for followerID,_ in pairs(buffed) do
 		P:AddFollower(followerID)
-		tinsert(scores,format("%s|%s",self:FollowerScore(mission),followerID))
-		if (mission.numFollowers>1) then
-			for k,d in pairs(G.GetFollowersTraitsForMission(missionID)) do
-				if not buffed[k] then
-					traits[k]=d
-				end
-			end
-		end
-		P:RemoveFollower(followerID)
-	end
-	if (filter) then
-		filters[filter](traits)
-	end
-	for followerID,_ in pairs(traits) do
-		P:AddFollower(followerID)
-		tinsert(scores,format("%s|%s",self:FollowerScore(mission),followerID))
-		xprint(G.GetFollowerName(followerID),scores[#scores])
+		tinsert(scores,format("%010d1|%s",self:FollowerScore(mission,followerID),followerID))
 		P:RemoveFollower(followerID)
+		buffeds=buffeds+1
 	end
-	table.sort(scores)
-	for i=#scores,1,-1 do
-		local score,followerID=strsplit('|',scores[i])
-		xprint(score,G.GetFollowerName(followerID))
+	for _,followerID in self:GetFollowerIterator() do
+		if (not buffed[followerID]) then
+			P:AddFollower(followerID)
+			tinsert(scores,format("%010d0|%s",self:FollowerScore(mission,followerID),followerID))
+			P:RemoveFollower(followerID)
+		end
 	end
-	local missionScore=self:MissionScore(mission)
-	for p=1,mission.numFollowers do
-		xprint("Slot",p)
-		local delete=0
-		local candidate=nil
-		local candidateScore=nil
+	if #scores > 0 then
+		local firstmember
+		table.sort(scores)
+		if (dbg) then
+			local s=self:GetScroller("Scores")
+			self:cutePrint(s,scores)
+		end
+		xprint("           First member")
 		for i=#scores,1,-1 do
 			local score,followerID=strsplit('|',scores[i])
-			P:AddFollower(followerID)
-			candidateScore=self:MissionScore(mission)
-			xprint(G.GetFollowerName(followerID),candidateScore,"pos",i)
-			if (p>1) then
-				if (missionScore<candidateScore) then
-					missionScore=candidateScore
-					candidate=followerID
-					delete=i
-					xprint("Candidate:",G.GetFollowerName(candidate),candidateScore,"will delete",delete)
-				end
-				P:RemoveFollower(followerID)
-			else
-				delete=i
-				xprint("Adding first",G.GetFollowerName(followerID),candidateScore,"will delete",delete)
+			xprint(C(score,'yellow'),G.GetFollowerName(followerID))
+			if not firstmember and not filter(followerID,missionID) then
+				firstmember=followerID
 				break
 			end
 		end
-		if (delete>0) then
-			xprint("scores contiene",#scores)
-			xprint("rimuovo",delete)
-			tremove(scores,delete)
-			xprint("scores contiene",#scores)
-		end
-		if candidate then
-			P:AddFollower(candidate)
-			xprint("Adding",G.GetFollowerName(candidate),candidateScore)
-		end
-		if P:FreeSlots()==0 then break end
-	end
-	P:Dump()
-	xprint("Final score",self:MissionScore(mission))
-	P:StoreFollowers(party.members)
-	party.full= P:FreeSlots()==0
-	party.perc=P:Close()
-end
-
-function addon:MatchMakerOld(missionID,mission,party,fromMissionControl)
-	if (GMFRewardSplash:IsShown()) then return end
-	if (not mission) then mission=self:GetMissionData(missionID) end
-	if (not party) then party=self:GetParty(missionID) end
-	local skipBusy=self:GetBoolean("IGM")
-	local skipMaxed=self:GetBoolean("IGP")
-	dbg=missionID==(tonumber(_G.MW) or 0)
-	local slots=mission.slots
-	local missionCounters=counters[missionID]
-	local ct=counterThreatIndex[missionID]
-	P:Open(missionID,mission.numFollowers)
-	-- Preloading skipped ones in party table.
-	if (dbg) then xprint(C("Matchmaking mission","Red"),missionID,mission.name) end
-	if (missionCounters) then
-		for i=1,#missionCounters do
-			local followerID=missionCounters[i].followerID
-			if (not followerID) then
-				if (dbg) then xprint("Trying to use [",followerID,"]") end
-			else
-				if (self:IsIgnored(followerID,missionID)) then
-					if (dbg) then xprint("Skipped",n[followerID],"due to ignored" ) end
-					P:Ignore(followerID,true)
-				elseif not self:IsFollowerAvailableForMission(followerID,skipBusy) then
-					if (dbg) then xprint("Skipped",n[followerID],"due to busy" ) end
-					P:Ignore(followerID)
-				elseif (skipMaxed and mission.xpOnly and self:GetFollowerData(followerID,'maxed')) then
-					if (dbg) then print("Skipped",n[followerID],"due to maxed" ) end
-					P:Ignore(followerID,true)
-				end
+		if firstmember then
+			P:AddFollower(firstmember)
+			if mission.numFollowers > 1 then
+				xprint("           AddMore 1")
+				AddMoreFollowers(self,mission,scores)
 			end
 		end
-		if (type(slots)=='table') then
-			for i=1,#slots do
-				local threat=cleanicon(slots[i].icon)
-				local candidates=ct[threat]
-				local choosen
-				if (dbg) then xprint("Checking ",threat) end
-				for i=1,#candidates do
-					local followerID=missionCounters[candidates[i]].followerID
-					if P:IsIn(followerID) then
-						if dbg then xprint("Countered by",n[followerID],"which is already in party") end
-						choosen=nil
-						break
-					end
-					if followerID then
-						if(not P:IsIgnored(followerID)) then
-							choosen=best(choosen,candidates[i],missionCounters,mission)
-							if (dbg) then xprint("Taken",n[missionCounters[choosen].followerID]) end
-						else
-							if (dbg) then xprint("Party Ignored",n[followerID]) end
-						end
-					end
-				end
-				if (choosen) then
-					if dbg then xprint("Adding to party",n[missionCounters[choosen].followerID]," still need ",P:FreeSlots()) end
-					P:AddFollower(missionCounters[choosen].followerID)
-				end
-				if (P:FreeSlots()==0) then
-					break
-				end
-			end
-		else
-			ns.xprint("Mission",missionID,"has no slots????")
+	end
+	if P:FreeSlots() > 0 then
+		if not onlyBest then
+			filters.skipMaxed=false
+			xprint("           AddMore 1 with skipmaxed false",filters.skipMaxed)
+			AddMoreFollowers(self,mission,scores)
 		end
-		if P:FreeSlots() > 0 then self:AddTraitsToParty(missionID,mission) end
 	end
-	if P:FreeSlots() > 0 then self:CompleteParty(missionID,mission,skipBusy,skipMaxed) end
-	if (not fromMissionControl and not P:IsEmpty()) then
-		if P:FreeSlots() > 0 then self:CompleteParty(missionID,mission,skipBusy,false) end
+	if P:FreeSlots() > 0 then
+		filters.skipMaxed=false
+		xprint("           AddMore 1 with just do true")
+		AddMoreFollowers(self,mission,scores,true)
+	end
+	if dbg then P:Dump() end
+	xprint("Final score",self:MissionScore(mission))
+	if not party.class then
+		party.class=class
+		party.itemLevel=mission.itemLevel
+		party.followerUpgrade=mission.followerUpgrade
+		party.xpBonus=mission.xpBonus
+		party.gold=mission.gold
+		party.resources=mission.resources
 	end
 	P:StoreFollowers(party.members)
-	party.full= P:FreeSlots()==0
-	party.perc=P:Close()
+	P:Close(party)
+	del(buffed)
 end
-function addon:AddTraitsToParty(missionID,mission,skipBusy,skipMaxed)
-	local t=counters[missionID]
-	if (t) then
-		for i=1,#t do
-			local follower=t[i]
-			if (follower.trait and not P:IsIgnored(follower.followerID) and not P:IsIn(follower.followerID)) then
-				if mission.resources > 0 and follower.name==scavengerTrait then
-					P:AddFollower(follower.followerID)
-				elseif mission.xpOnly  and (follower.name==extraTrainingTrait or follower.name==hearthStoneProTrait) then
-					P:AddFollower(follower.followerID)
-				elseif mission.durationSeconds > GARRISON_LONG_MISSION_TIME  and follower.name==epicMountTrait then
-					P:AddFollower(follower.followerID)
-				end
+function addon:MCMatchMaker(missionID,party)
+	MatchMaker(self,missionID,party,false)
+	if (self:GetMissionData(missionID,'class')=='xp') then
+		for i=1,#party.members do
+			if not self:GetFollowerData(party.members[i],'maxed') then
+				return
 			end
 		end
+		party.full=false
+		wipe(party.members)
 	end
 end
-function addon:CompleteParty(missionID,mission,skipBusy,skipMaxed)
-	local perc=select(4,G.GetPartyMissionInfo(missionID)) -- If percentage is already 100, I'll try and add the most useless character
-	local candidateMissions=10000
-	local candidateRank=10000
-	local candidateQuality=9999
-	if (dbg) then
-		print("Attemptin to fill party, so far perc is ",perc, "and party is")
-		P:Dump()
-	end
-	for x=1,P:FreeSlots() do
-		local candidate
-		local candidatePerc=perc
-		if (dbg) then print("            Perc to beat",perc, "Going for spot ",P:FreeSlots()) end
-		local totFollowers=#followersCache
-		for i=1,totFollowers do
-			local data=followersCache[i]
-			local followerID=data.followerID
-			if (dbg) then print("evaluating",data.fullname) end
-			repeat
-				if P:IsIgnored(followerID) then
-					if (dbg) then print("Skipped due to party ignored") end
-					break
-				end
-				if P:IsIn(followerID) then
-					if (dbg) then print("Skipped due to already in party") end
-					break
-				end
-				if self:IsIgnored(followerID,missionID) then
-					if (dbg) then print("Skipped due to ignored") end
-					break
-				end
-				if (skipMaxed and data.maxed and mission.xpOnly) then
-					if (dbg) then print("Skipped due to maxed",skipMaxed,mission.xpOnly) end
-					break
-				end
-				if (not self:IsFollowerAvailableForMission(followerID,skipBusy)) then
-					if (dbg) then print("Skipped due to busy") end
-					break
-				end
-				local rank=data.rank
-				local quality=data.quality
-				perc=tonumber(perc) or 0
-				if ((perc) <100) then
-					P:AddFollower(followerID)
-					local newperc=select(4,G.GetPartyMissionInfo(missionID))
-					newperc=tonumber(newperc) or 0
-					candidatePerc=tonumber(candidatePerc) or 0
-					P:RemoveFollower(followerID)
-					if (newperc > candidatePerc) then
-						candidatePerc=newperc
-						candidate=followerID
-						candidateRank=rank
-						candidateQuality=quality
-						break -- continue
-					end
-				else
-					-- This candidate is not improving success chance or we are already at 100%, minimize
-					if (i < totFollowers  and data.maxed) then
-						break  -- Pointless using a maxed follower if we  have more follower to try
-					end
-					if(rank<candidateRank) then
-						candidate=followerID
-						candidateRank=rank
-						candidateQuality=quality
-					elseif(rank==candidateRank and quality<candidateQuality) then
-						candidate=followerID
-						candidateRank=rank
-						candidateQuality=quality
-					elseif (not candidate) then
-						candidate=followerID
-						candidateRank=rank
-						candidateQuality=quality
-					end
-				end
-			until true -- A poor man continue implementation using break
-		end
-		if (candidate) then
-			if (dbg) then print("Attempting to add to party") end
-			P:AddFollower(candidate)
-			if (dbg) then
-				print("Added member to party")
-				P:Dump()
-			end
-			perc=select(4,G.GetPartyMissionInfo(missionID))
-			if (dbg) then print("New perc is",perc) end
-		end
-	end
+function addon:MatchMaker(missionID,party,includeBusy)
+	if (not party) then party=self:GetParty(missionID) end
+	MatchMaker(self,missionID,party,includeBusy)
+end
+function addon:TestMission(missionID,includeBusy)
+	dbg=true
+	self:MatchMaker(missionID,nil,includeBusy)
+	dbg=false
 end
+function addon:MatchDebug(d)
+	dbg=d
+end
+
+
 --@do-not-package@
 --[[
 Dump value=GetBuffedFollowersForMission(315)
@@ -376,6 +264,19 @@ Dump value=GetBuffedFollowersForMission(315)
 		}
 	}
 }
+Dump: value=C_Garrison.GetFollowersTraitsForMission(109)
+{
+	["0x00000000001BE95D"]={
+		[1]={
+			traitID=236,
+			icon="Interface\\ICONS\\Item_Hearthstone_Card.blp"
+		},
+		[2]={
+			traitID=76,
+			icon="Interface\\ICONS\\Spell_Holy_WordFortitude.blp"
+		}
+	}
+}
 Enemies
 Dump: value=GAC:GetMissionData(315,"enemies")
 {
diff --git a/MissionCache.lua b/MissionCache.lua
index b771bb2..e642d4f 100644
--- a/MissionCache.lua
+++ b/MissionCache.lua
@@ -1,7 +1,6 @@
 local me,ns=...
 local addon=ns.addon --#addon
 local holdEvents,releaseEvents=addon.holdEvents,addon.releaseEvents
-local xprint=ns.xprint
 local xdump=ns.xdump
 --upvalue
 local G=C_Garrison
@@ -11,16 +10,19 @@ local type=type
 local select=select
 local pairs=pairs
 local tonumber=tonumber
-local tinser=tinsert
+local tinsert=tinsert
+local wipe=wipe
 local GARRISON_CURRENCY=GARRISON_CURRENCY
-local Mbase = GMF.MissionTab.MissionList
+local GARRISON_FOLLOWER_MAX_LEVEL=GARRISON_FOLLOWER_MAX_LEVEL
+local Mbase = GMFMissions
 -- self=Mbase
 --	C_Garrison.GetInProgressMissions(self.inProgressMissions);
 --	C_Garrison.GetAvailableMissions(self.availableMissions);
-local missionIndex={}
+local Index={}
+local sorted={}
 local AddExtraData
 local function keyToIndex(key)
-	local idx=missionIndex[key]
+	local idx=Index[key]
 	if (idx and idx <= #Mbase.availableMissions) then
 		if Mbase.availableMissions[idx].missionID==key then
 			return idx
@@ -28,18 +30,28 @@ local function keyToIndex(key)
 			idx=nil
 		end
 	end
-	wipe(missionIndex)
+	wipe(Index)
+	wipe(sorted)
 	for i=1,#Mbase.availableMissions do
-		missionIndex[Mbase.availableMissions[i].missionID]=i
+		Index[Mbase.availableMissions[i].missionID]=i
+		tinsert(sorted,i)
 		if Mbase.availableMissions[i].missionID==key then
-			return i
+			idx=i
 		end
 	end
+	return idx
 end
-function addon:GetMissionData(missionID,key)
+function addon:GetMissionData(missionID,key,default)
 	local idx=keyToIndex(missionID)
-	xprint("Mission",missionID," is ",idx,"of",#Mbase.availableMissions)
 	local mission=Mbase.availableMissions[idx]
+	if not mission then
+		for i=1,#Mbase.inProgressMissions do
+			if missionID==Mbase.inProgressMissions[i].missionID then
+				mission=Mbase.inProgressMissions[i]
+				break
+			end
+		end
+	end
 	if (key==nil) then
 		return mission
 	end
@@ -47,34 +59,38 @@ function addon:GetMissionData(missionID,key)
 		return mission[key]
 	end
 	if (key=='rank') then
-		mission.rank=mission.level < 100 and mission.level or mission.iLevel
+		mission.rank=mission.level < GARRISON_FOLLOWER_MAX_LEVEL and mission.level or mission.iLevel
 		return mission.rank
 	elseif(key=='basePerc') then
 		mission.basePerc=select(4,G.GetPartyMissionInfo(missionID))
 		return mission.basePerc
 	else
 		AddExtraData(mission)
-		return mission[key]
+		return mission[key] or default
 	end
 end
 function AddExtraData(mission)
 	local _
 	_,mission.xp,mission.type,mission.typeDesc,mission.typeIcon,mission.locPrefix,_,mission.enemies=G.GetMissionInfo(mission.missionID)
 	mission.rank=mission.level < 100 and mission.level or mission.iLevel
-	mission.xpBonus=0
 	mission.resources=0
 	mission.gold=0
 	mission.followerUpgrade=0
 	mission.itemLevel=0
+	mission.xpBonus=0
+	local numrewards=0
 	for k,v in pairs(mission.rewards) do
-		if (v.followerXP) then mission.xpBonus=mission.xpBonus+v.followerXP end
+		numrewards=numrewards+1
+		if (k==615 and v.followerXP) then mission.xpBonus=mission.xpBonus+v.followerXP end
 		if (v.currencyID and v.currencyID==GARRISON_CURRENCY) then mission.resources=v.quantity end
 		if (v.currencyID and v.currencyID==0) then mission.gold =mission.gold+v.quantity/10000 end
-		if (v.itemID) then
+		if (v.icon=="Interface\\Icons\\XPBonus_Icon" and v.followerXP) then
+			mission.xpBonus=mission.xpBonus+v.followerXP
+		elseif (v.itemID) then
 			if (v.itemID~=120205) then
 				local itemName, itemLink, itemRarity, itemLevel, itemMinLevel, itemType, itemSubType, itemStackCount,itemEquipLoc, itemTexture, itemSellPrice = GetItemInfo(v.itemID)
-				if (itemName) then
-					if (itemLevel > 1 ) then
+				if (itemName and (not v.quantity or v.quantity==1) and not v.followerXP ) then
+					if (itemLevel > 1 and itemMinLevel >=90 ) then
 						mission.itemLevel=itemLevel
 					else
 						mission.followerUpgrade=itemRarity
@@ -83,23 +99,49 @@ function AddExtraData(mission)
 			end
 		end
 	end
-	mission.totalXp=(tonumber(mission.xp) or 0) + (tonumber(mission.xpBonus) or 0)
-	mission.globalXp=mission.totalXp*mission.numFollowers
-	if (mission.resources==0 and mission.gold==0 and mission.itemLevel==0 and mission.followerUpgrade==0) then
+	if (mission.resources==0 and mission.gold==0 and mission.itemLevel==0 and mission.followerUpgrade==0 and numrewards <2) then
 		mission.xpOnly=true
+		mission.class="xp"
 	else
 		mission.xpOnly=false
+		if mission.resources > 0 then
+			mission.class='resources'
+		elseif mission.gold >0 then
+			mission.class='gold'
+		elseif mission.itemLevel >0 then
+			mission.class='equip'
+		elseif mission.followerUpgrade>0 then
+			mission.class='followerequip'
+		elseif mission.itemLevel>=645 then
+			mission.class='epic'
+		else
+			mission.class='generic'
+		end
 	end
-	mission.slots={}
-	local slots=mission.slots
-
-	for i=1,#mission.enemies do
-		local mechanics=mission.enemies[i].mechanics
-		for i,mechanic in pairs(mechanics) do
-			tinsert(slots,mechanic)
+end
+function addon:OnAllMissions(func,inProgress)
+	local list=inProgress and Mbase.inProgressMissions or Mbase.availableMissions
+	if type(list)=='table' then
+		for i=1,#list do
+			func(list[i].missionID)
 		end
 	end
-	if (type) then
-		tinsert(slots,{name=TYPE,key=mission.type,icon=mission.typeIcon})
+end
+local sorters={}
+
+function addon:GetMissionIterator(func)
+	if (func) then
+		table.sort(sorted,sorters[func])
 	end
+	local f=Mbase.availableMissions
+	return function(sorted,i)
+		i=i+1
+		local x = sorted[i]
+		if x then
+			local v=f[x] and f[x].missionID or nil
+			if v then
+				return i,v
+			end
+		end
+	end,sorted,0
 end
diff --git a/MissionCompletion.lua b/MissionCompletion.lua
index 13c3908..5cb7821 100644
--- a/MissionCompletion.lua
+++ b/MissionCompletion.lua
@@ -9,18 +9,149 @@ local new, del, copy =ns.new,ns.del,ns.copy
 local GMF=GarrisonMissionFrame
 local GMFMissions=GarrisonMissionFrameMissions
 local G=C_Garrison
+local GARRISON_CURRENCY=GARRISON_CURRENCY
 ns.missionautocompleting=false
+local generated
+function addon:GenerateMissionCompleteList(title)
+	if not generated then
+		generated=true
+		self:GenerateContainer()
+		do
+			local Type="GCMCList"
+			local Version=1
+			local m={} --#Widget
+			local function onEnter(self)
+				if (self.itemlink) then
+					GameTooltip:SetHyperlink(self.itemlink)
+					GameTooltip:Show()
+				end
+			end
+			function m:OnAcquire()
+				wipe(self.missions)
+			end
+			function m:Show()
+				self.frame:Show()
+			end
+			function m:Hide()
+				self.frame:Hide()
+				self:Release()
+			end
+			function m:AddMissionButton(mission)
+				local obj=self.scroll
+				local b=AceGUI:Create("GMCSlimMissionButton")
+				b:SetMission(mission,addon:GetParty(mission.missionID))
+				b:SetScale(0.7)
+				b:SetFullWidth(true)
+				self.missions[mission.missionID]=b
+				obj:AddChild(b)
+			end
+			function m:AddMissionName(missionID,success)
+				print("Addmissioname",missionID,success)
+				local mission=self.missions[missionID]
+				if mission then
+					if success then
+						mission.frame.Success:Show()
+						mission.frame.Failure:Hide()
+					else
+						mission.frame.Success:Hide()
+						mission.frame.Failure:Show()
+					end
+				end
+			end
+			function m:AddRow(data,...)
+				local obj=self.scroll
+				local l=AceGUI:Create("InteractiveLabel")
+				l:SetFontObject(GameFontNormalSmall)
+				l:SetText(data)
+				l:SetColor(...)
+				l:SetFullWidth(true)
+				obj:AddChild(l)
+				if (obj.scrollbar and obj.scrollbar:IsShown()) then
+					obj.scrollbar.ScrollDownButton:Click()
+				end
+
+			end
+			function m:AddFollower(followerID,xp,levelup)
+				local follower=addon:GetFollowerData(followerID)
+				if follower.maxed then
+					return self:AddRow(format("%s is already at maximum xp",addon:GetFollowerData(followerID,'fullname')))
+				end
+				local quality=G.GetFollowerQuality(followerID) or follower.quality
+				local level=G.GetFollowerLevel(followerID) or follower.level
+				if levelup then
+					PlaySound("UI_Garrison_CommandTable_Follower_LevelUp");
+				end
+				return self:AddRow(format("%s gained %d xp%s%s",addon:GetFollowerData(followerID,'fullname',true),xp,
+				levelup and " *** Level Up ***." or ".",
+				format(" %d to go.",addon:GetFollowerData(followerID,'levelXP')-addon:GetFollowerData(followerID,'xp'))))
+			end
+			function m:AddIconText(icon,text,qt)
+				local obj=self.scroll
+				local l=AceGUI:Create("Label")
+				l:SetFontObject(GameFontNormalSmall)
+				if (qt) then
+					l:SetText(format("%s x %s",text,qt))
+				else
+					l:SetText(text)
+				end
+				l:SetImage(icon)
+				l:SetImageSize(24,24)
+				l:SetFullWidth(true)
+				obj:AddChild(l)
+				if (obj.scrollbar and obj.scrollbar:IsShown()) then
+					obj.scrollbar.ScrollDownButton:Click()
+				end
+				return l
+			end
+			function m:AddItem(itemID,qt)
+				local obj=self.scroll
+				local _,itemlink,itemquality,_,_,_,_,_,_,itemtexture=GetItemInfo(itemID)
+				if not itemlink then
+					self:AddIconText(itemtexture,itemID)
+				else
+					self:AddIconText(itemtexture,itemlink)
+				end
+			end
+			local function Constructor()
+				local widget=AceGUI:Create("GCGUIContainer")
+				widget:SetLayout("Fill")
+				widget.missions={}
+				local scroll = AceGUI:Create("ScrollFrame")
+				scroll:SetLayout("List") -- probably?
+				scroll:SetFullWidth(true)
+				scroll:SetFullHeight(true)
+				widget:AddChild(scroll)
+				for k,v in pairs(m) do widget[k]=v end
+				widget:Show()
+				widget.scroll=scroll
+				return widget
+			end
+			AceGUI:RegisterWidgetType(Type,Constructor,Version)
+		end
+	end
+	local w=AceGUI:Create("GCMCList")
+	w:SetTitle(title)
+	return w
+end

+--TODO: Portare la stampa dei risultati in fondo, e togliere il delay fra le missioni
 do
 	local missions={}
 	local states={}
 	local currentMission
+	local rewards={
+		items=setmetatable({},{__index=function() return 0 end}),
+		followerBase={},
+		followerXP=setmetatable({},{__index=function() return 0 end}),
+		currencies=setmetatable({},{__index=function(t,k) rawset(t,k,{icon="",qt=0}) return t[k] end}),
+	}
 	local scroller
 	local report
 	local timer
-	local function startTimer(delay)
+	local function startTimer(delay,event)
 		delay=delay or 0.2
-		addon:ScheduleTimer("MissionAutoComplete",delay,"LOOP")
+		event=event or "LOOP"
+		addon:ScheduleTimer("MissionAutoComplete",delay,event)
 	end
 	local function stopTimer()
 		timer=nil
@@ -73,11 +204,13 @@ do
 			report:SetPoint("BOTTOM",GMF)
 			report:SetWidth(500)
 			report:SetCallback("OnClose",function() return addon:MissionsCleanup() end)
+			wipe(rewards.followerBase)
+			wipe(rewards.followerXP)
+			wipe(rewards.currencies)
+			wipe(rewards.items)
 			for i=1,#missions do
-				missions[i].followerXp={}
-				missions[i].items={}
 				for k,v in pairs(missions[i].followers) do
-					missions[i].followerXp[v]={0,G.GetFollowerXP(v),self:GetFollowerData(v,'level'),self:GetFollowerData(v,'quality')}
+					rewards.followerBase[v]=self:GetFollowerData(v,'qLevel')
 				end
 			end
 			currentMission=tremove(missions)
@@ -88,12 +221,16 @@ do
 	function addon:MissionAutoComplete(event,ID,arg1,arg2,arg3,arg4)
 -- C_Garrison.MarkMissionComplete Mark mission as complete and prepare it for bonus roll, da chiamare solo in caso di successo
 -- C_Garrison.MissionBonusRoll
-	--@debug@
+	--[===[@debug@
 		print("evt",event,ID,arg1,arg2,agr3)
-	--@end-debug@
+	--@end-debug@]===]
 		if self['Event'..event] then
 			self['Event'..event](self,event,ID,arg1,arg2,arg3,arg4)
 		end
+		if event=="LOOT" then
+			return addon:MissionsPrintResults()
+		end
+
 		if (event =="LOOP" ) then
 			ID=currentMission and currentMission.missionID or "none"
 			arg1=currentMission and currentMission.state or "none"
@@ -102,16 +239,16 @@ do
 		if (event=="GARRISON_FOLLOWER_XP_CHANGED") then
 			if (arg1 > 0) then
 				--report:AddFollower(ID,arg1,arg2)
-				currentMission.followerXp[ID][1]=currentMission.followerXp[ID][1]+arg1
+				rewards.followerXP[ID]=rewards.followerXP[ID]+tonumber(arg1) or 0
 			end
 			return
 		-- GET_ITEM_INFO_RECEIVED: itemID
 		elseif (event=="GET_ITEM_INFO_RECEIVED") then
-			currentMission.items[ID]=1
+			rewards.items[format("%d:%s",currentMission.missionID,ID)]=1
 			return
 		-- GET_ITEM_INFO_RECEIVED: itemID
 		elseif (event=="GARRISON_MISSION_BONUS_ROLL_LOOT") then
-			currentMission.items[ID]=1
+			rewards.items[format("%d:%s",currentMission.missionID,ID)]=1
 			return
 		-- GARRISON_MISSION_COMPLETE_RESPONSE: missionID, requestCompleted, succeeded
 		elseif (event=="GARRISON_MISSION_COMPLETE_RESPONSE") then
@@ -122,7 +259,7 @@ do
 				currentMission.state=1
 			else -- failure, just print results
 				currentMission.state=2
-				startTimer(0.6)
+				startTimer(0.1)
 				return
 			end
 			startTimer(0.1)
@@ -134,7 +271,7 @@ do
 				currentMission.state=1
 			else
 				currentMission.state=3
-				startTimer(0.6)
+				startTimer(0.1)
 				return
 			end
 			startTimer(0.1)
@@ -146,15 +283,17 @@ do
 					step=0
 					currentMission.state=0
 					local _
-					_,_,_,currentMission.successChance,_,_,currentMission.xpBonus,currentMission.multiplier=G.GetPartyMissionInfo(currentMission.missionID)
+					_,_,_,currentMission.successChance,_,_,currentMission.xpBonus,currentMission.multiplier,currentMission.golds=G.GetPartyMissionInfo(currentMission.missionID)
+					currentMission.golds=currentMission.golds or 1
 					currentMission.xp=select(2,G.GetMissionInfo(currentMission.missionID))
+					report:AddMissionButton(currentMission)
 				end
 				if (step==0) then
 					G.MarkMissionComplete(currentMission.missionID)
 				elseif (step==1) then
 					G.MissionBonusRoll(currentMission.missionID)
 				elseif (step>=2) then
-					self:MissionPrintResults(step==3)
+					self:GetMissionResults(step==3)
 					self:RefreshFollowerStatus()
 					currentMission=tremove(missions)
 					startTimer()
@@ -163,59 +302,62 @@ do
 				currentMission.state=step
 			else
 				report:AddRow(DONE)
+				startTimer(0.3,"LOOT")
 			end
 		end
 	end
-	function addon:MissionPrintResults(success)
+	function addon:GetMissionResults(success)
 		stopTimer()
-
 		if (success) then
-			report:AddMissionName(currentMission.name,C(format("Succeeded. (Chance was: %s%%)", currentMission.successChance),"Green"))
+			report:AddMissionName(currentMission.missionID,true)
 			PlaySound("UI_Garrison_Mission_Complete_Mission_Success")
 		else
+			report:AddMissionName(currentMission.missionID,false)
 			PlaySound("UI_Garrison_Mission_Complete_Encounter_Fail")
-			report:AddMissionName(currentMission.name,C(format("Failed. (Chance was: %s%%", currentMission.successChance),"Red"))
 		end
---@debug@
-		--report:AddRow(format("Resource multiplier: %d Xp Bonus:%d",currentMission.multiplier,currentMission.xpBonus))
-		--report:AddRow(format("ID: %d",currentMission.missionID))
---@end-debug@
 		if success then
 			for k,v in pairs(currentMission.rewards) do
+				print("Reward",k,v)
 				v.quantity=v.quantity or 0
 				v.multiplier=v.multiplier or 1
---@debug@
---				ns.xprint(format("Reward type: = %s",k))
---				for field,value in pairs(v) do
---					ns.xprint(format("   %s = %s",field,value),C.Silver())
---				end
---@end-debug@
+				v.golds=v.golds or 1
 				if v.currencyID then
+					rewards.currencies[v.currencyID].icon=v.icon
 					if v.currencyID == 0 then
-							-- Money reward
-							report:AddIconText(v.icon,GetMoneyString(v.quantity))
+						rewards.currencies[v.currencyID].qt=rewards.currencies[v.currencyID].qt+v.quantity * v.golds
 					elseif v.currencyID == GARRISON_CURRENCY then
-							-- Garrison currency reward
-							report:AddIconText(v.icon,GetCurrencyLink(v.currencyID),v.quantity * v.multiplier)
+						rewards.currencies[v.currencyID].qt=rewards.currencies[v.currencyID].qt+v.quantity * v.multiplier
 					else
-							-- Other currency reward
-							report:AddIconText(v.icon,GetCurrencyLink(v.currencyID),v.quantity )
+						rewards.currencies[v.currencyID].qt=rewards.currencies[v.currencyID].qt+v.quantity
 					end
 				elseif v.itemID then
-						-- Item reward
-						report:AddItem(v.itemID,1)
-						currentMission.items[v.itemID]=nil
-				else
-						-- Follower XP reward
-						--report:AddIconText(v.icon,v.name)
+					rewards.items[format("%d:%s",currentMission.missionID,v.itemID)]=1
 				end
 			end
 		end
-		for k,v in pairs(currentMission.items) do
-			report:AddItem(k,v)
+	end
+	function addon:MissionsPrintResults(success)
+		stopTimer()
+		self:FollowerCacheInit()
+		self:Dump("Ended Mission",rewards)
+		for k,v in pairs(rewards.currencies) do
+			if k == 0 then
+				-- Money reward
+				report:AddIconText(v.icon,GetMoneyString(v.qt))
+			elseif k == GARRISON_CURRENCY then
+				-- Garrison currency reward
+				report:AddIconText(v.icon,GetCurrencyLink(k),v.qt)
+			else
+				-- Other currency reward
+				report:AddIconText(v.icon,GetCurrencyLink(k),v.qt)
+			end
+		end
+		for k,v in pairs(rewards.items) do
+			local missionid,itemid=strsplit(":",k)
+			report:AddItem(itemid,v)
 		end
-		for k,v in pairs(currentMission.followers) do
-			report:AddFollower(v,currentMission.followerXp[v])
+		for k,v in pairs(rewards.followerXP) do
+			report:AddFollower(k,v,self:GetFollowerData(k,'qLevel') > rewards.followerBase[k])
 		end
 	end
 end
diff --git a/MissionControl.lua b/MissionControl.lua
index ba3144c..804a92d 100644
--- a/MissionControl.lua
+++ b/MissionControl.lua
@@ -16,7 +16,6 @@ local aMissions={}
 local dbcache
 local cache
 local db
-local parties
 local GMC
 local GMF=GarrisonMissionFrame
 local G=C_Garrison
@@ -24,9 +23,9 @@ local GMCUsedFollowers={}
 local wipe=wipe
 local pairs=pairs
 local tinsert=tinsert
---@debug@
+--[===[@debug@
 if LibDebug then LibDebug() end
---@end-debug@
+--@end-debug@]===]
 local dbg
 function addon:GMCBusy(followerID)
 	return GMCUsedFollowers[followerID]
@@ -36,20 +35,23 @@ function addon:GMCCreateMissionList(workList)
 	local settings=self.privatedb.profile.missionControl
 	local ar=settings.allowedRewards
 	wipe(workList)
-	for missionID,mission in pairs(cache.missions) do
+	for _,missionID in self:GetMissionIterator() do
 		local discarded=false
 		repeat
-			if (mission.durationSeconds > settings.maxDuration * 3600 or mission.durationSeconds <  settings.minDuration * 3600) then
-				ns.xprint(missionID,"discarded due to len",mission.durationSeconds /3600)
+			ns.xprint("|cffff0000",'Examing',self:GetMissionData(missionID,"name"),self:GetMissionData(missionID,"class"),"|r")
+			local durationSeconds=self:GetMissionData(missionID,'durationSeconds')
+			if (durationSeconds > settings.maxDuration * 3600 or durationSeconds <  settings.minDuration * 3600) then
+				ns.xprint(missionID,"discarded due to len",durationSeconds /3600)
 				break
 			end -- Mission too long, out of here
-			if (mission.isRare and settings.skipRare) then
+			if (self:GetMissionData(missionID,'isRare') and settings.skipRare) then
 				ns.xprint(missionID,"discarded due to rarity")
 				break
 			end
 			for k,v in pairs(ar) do
 				if (not v) then
-					if (mission[k] and mission[k]~=0) then -- we have a forbidden reward
+					if (self:GetMissionData(missionID,"class")==k) then -- we have a forbidden reward
+						ns.xprint(missionID,"discarded due to class == ", k)
 						discarded=true
 						break
 					end
@@ -60,24 +62,23 @@ function addon:GMCCreateMissionList(workList)
 			end
 		until true
 	end
+	local parties=self:GetParty()
 	local function msort(i1,i2)
-		local m1=addon:GetMissionData(i1)
-		local m2=addon:GetMissionData(i2)
 		for i=1,#GMC.settings.itemPrio do
 			local criterium=GMC.settings.itemPrio[i]
 			if (criterium) then
-				if (m1[criterium] ~= m2[criterium]) then
-					return m1[criterium] > m2[criterium]
+				if addon:GetMissionData(i1,criterium) ~= addon:GetMissionData(i2,criterium) then
+					return addon:GetMissionData(i1,criterium) > addon:GetMissionData(i2,criterium)
 				end
 			end
 		end
-		if (parties[m1.missionID].perc and parties[m2.missionID].perc) then
-			return parties[m1.missionID].perc > parties[m2.missionID].perc
+		if (parties[i1].perc and parties[i2].perc) then
+			return parties[i1].perc > parties[i2].perc
 		end
-		return m1.level > m2.level
+		return addon:GetMissionData(i1,'level') > addon:GetMissionData(i2,'level')
 	end
 	table.sort(workList,msort)
-	--@debug@
+	--[===[@debug@
 	ns.xprint("Sorted list")
 	local x=new()
 	for i=1,#workList do
@@ -92,7 +93,7 @@ function addon:GMCCreateMissionList(workList)
 	local scroll=self:GetScroller("Sorted missions",nil,600,600)
 	self:cutePrint(scroll,x)
 	del(x)
-	--@end-debug@
+	--@end-debug@]===]

 end
 --- This routine can be called both as coroutin and as a standard one
@@ -160,27 +161,22 @@ do
 			else
 				GMC.ml.widget:SetFormattedTitle("Processing mission %d of %d",currentMission,#aMissions)
 				local missionID=aMissions[currentMission]
-				if (dbg) then print(C("Processing ","Red"),missionID) end
-				local party={members={},perc=0}
-				local mission=self:GetMissionData(missionID)
-				if (not mission ) then
-					if dbg then print ("NO data for",missionID) end
-					return
-				end
-				self:MatchMaker(missionID,mission,party) -- I need my mission data
+				local class=self:GetMissionData(missionID,"class")
+				local checkprio=class
+				if checkprio=="itemLevel" then class="equip" end
+				if checkprio=="followerUpgrade" then class= "followerEquip" end
+				ns.xprint(C("Processing ","Red"),missionID,addon:GetMissionData(missionID,"name"))
 				local minimumChance=0
 				if (GMC.settings.useOneChance) then
-					minimumChance=GMC.settings.minimumChance
-				end
-				for prio,enabled in pairs(GMC.settings.allowedRewards) do
-					if (dbg) then print("Chance from ",prio,"=",GMC.settings.rewardChance[prio],enabled) end
-					if (enabled and (tonumber(self:GetMissionData(missionID,prio)) or 0) >0) then
-						minimumChance=math.max(GMC.settings.rewardChance[prio],minimumChance)
-					end
+					minimumChance=tonumber(GMC.settings.minimumChance) or 100
+				else
+					minimumChance=tonumber(GMC.settings.rewardChance[checkprio]) or 100
 				end
-				if (dbg) then print ("Missionid",missionID,"Chance",minimumChance,"chance",party.perc) end
+				local party={members={},perc=0}
+				self:MCMatchMaker(missionID,party,false,false)
+				ns.xprint ("                           Requested",minimumChance,"Mission",party.perc,party.full)
 				if ( party.full and party.perc >= minimumChance) then
-					if (dbg) then print("Preparing button for",missionID) end
+					ns.xprint("                           Mission accepted")
 					local mb=AceGUI:Create("GMCMissionButton")
 					for i=1,#party.members do
 						GMCUsedFollowers[party.members[i]]=true
@@ -189,7 +185,7 @@ do
 					tinsert(GMC.ml.Parties,party)
 					GMC.ml.widget:PushChild(mb,missionID)
 					mb:SetFullWidth(true)
-					mb:SetMission(mission)
+					mb:SetMission(self:GetMissionData(missionID),party)
 					mb:SetCallback("OnClick",function(...)
 						addon:GMCRunMission(missionID)
 						GMC.ml.widget:RemoveChild(missionID)
@@ -222,13 +218,12 @@ function addon:GMC_OnClick_Run(this,button)
 end
 function addon:GMC_OnClick_Start(this,button)
 	ns.xprint(C("-------------------------------------------------","Yellow"))
-	this:Disable()
 	GMC.ml.widget:ClearChildren()
 	if (self:GetTotFollowers(AVAILABLE) == 0) then
-		self:Popup("All followers are busy",10)
-		this:Enable()
+		GMC.ml.widget:SetTitle("All followers are busy")
 		return
 	end
+	this:Disable()
 	addon:GMCCreateMissionList(aMissions)
 	wipe(GMCUsedFollowers)
 	wipe(GMC.ml.Parties)
@@ -247,7 +242,6 @@ function addon:GMCBuildPanel(bigscreen)
 	db=self.db.global
 	dbcache=self.privatedb.profile
 	cache=self.private.profile
-	parties=self:GetParty()
 	chestTexture='GarrMission-'..UnitFactionGroup('player').. 'Chest'
 	GMC = CreateFrame('FRAME', 'GMCOptions', GMF)
 	GMC.settings=dbcache.missionControl
@@ -444,15 +438,31 @@ function addon:GMCBuildRewards()
 	local t = {
 		{t = 'Enable/Disable money rewards.', i = 'Interface\\Icons\\inv_misc_coin_01', key = 'gold'},
 		{t = 'Enable/Disable other currency awards. (Resources/Seals)', i= 'Interface\\Icons\\inv_garrison_resource', key = 'resources'},
-		{t = 'Enable/Disable Follower XP Bonus rewards.', i = 'Interface\\Icons\\XPBonus_Icon', key = 'xpBonus'},
-		{t = 'Enable/Disable follower equip enhancement.', i = 'Interface\\ICONS\\Garrison_ArmorUpgrade', key = 'followerUpgrade'},
-		{t = 'Enable/Disable item tokens.', i = "Interface\\ICONS\\INV_Bracer_Cloth_Reputation_C_01", key = 'itemLevel'}
+		{t = 'Enable/Disable Follower XP Bonus rewards.', i = 'Interface\\Icons\\XPBonus_Icon', key = 'xp'},
+		{t = 'Enable/Disable follower equip enhancement.', i = 'Interface\\ICONS\\Garrison_ArmorUpgrade', key = 'followerEquip'},
+		{t = 'Enable/Disable item tokens.', i = "Interface\\ICONS\\INV_Bracer_Cloth_Reputation_C_01", key = 'equip'}
 	}
 	local scale=1.1
 	GMC.ignoreFrames = {}
 	local ref
 	local h=37 -- itemButtonTemplate standard size
 	local gap=5
+	-- converting from old data
+	local ar=GMC.settings.allowedRewards
+	local rc=GMC.settings.rewardChance
+	if ar.xpBonus then ar.xp=true end
+	ar.xpBonus=nil
+	if ar.followerUpgrade then ar.followerequip=true end
+	ar.followerUpgrade=nil
+	if ar.itemLevel then ar.equip=true end
+	ar.itemLevel=nil
+	if rc.xpBonus then rc.xp=rc.xpbonus or 100 end
+	rc.xpBonus=nil
+	if rc.followerUpgrade then rc.followerequip=rc.followerUpgrade or 100 end
+	rc.followerUpgrade=nil
+	if rc.itemLevel then rc.equip=rc.itemLevel or 100 end
+	rc.itemLevel=nil
+
 	for i = 1, #t do
 			local frame = CreateFrame('BUTTON', nil, GMC.aif, 'ItemButtonTemplate')
 			frame:SetScale(scale)
@@ -505,9 +515,9 @@ local addPriorityRule,prioRefresh,removePriorityRule,prioMenu,prioTitles,prioChe
 do
 -- 1 = item, 2 = folitem, 3 = exp, 4 = money, 5 = resource
 	prioTitles={
-		itemLevel="Gear Items",
-		followerUpgrade="Upgrade Items",
-		xpBonus="Follower XP Bonus",
+		itemLevel="Equipment",
+		followerUpgrade="Followr Upgrade",
+		xp="Xp gain",
 		gold="Gold Reward",
 		resources="Resource Rewards"
 	}
@@ -613,7 +623,7 @@ function addon:GMCBuildPriorities()
 		wipe(prioMenu)
 		tinsert(prioMenu,{text = L["Select an item to add as priority."], isTitle = true, isNotRadio=true,disabled=true, notCheckable=true,notClickable=true})
 		for k,v in pairs(prioTitles) do
-			tinsert(prioMenu,{text = v, func = addPriorityRule, notCheckable=true, isNotRadio=true, arg1 = k , disabled=inTable(GMC.settings.itemPrio,k)})
+			tinsert(prioMenu,{text = v, func = addPriorityRule, notCheckable=true, isNotRadio=true, arg1 = k , disabled=tContains(GMC.settings.itemPrio,k)})
 		end
 		EasyMenu(prioMenu, GMC.pmf, "cursor", 0 , 0, "MENU")
 		end
diff --git a/PartyCache.lua b/PartyCache.lua
index 2b78b94..e3c4305 100644
--- a/PartyCache.lua
+++ b/PartyCache.lua
@@ -14,7 +14,17 @@ local pcall=pcall
 --
 -- Temporary party management
 local parties=setmetatable({},{
-	__index=function(t,k)  rawset(t,k,{members={},perc=0,full=false}) return t[k] end
+	__index=function(t,k)  rawset(t,k,
+		{
+			members={},
+			threats={},
+			perc=0,
+			itemLevel=0,
+			followerUpgrade=0,
+			xpBonus=0,
+			gold=0,
+			resources=0,
+		}) return t[k] end
 })
 function ns.inParty(missionID,followerID)
 	return tContains(ns.parties[missionID].members,followerID)
@@ -26,10 +36,15 @@ local followerMissions=setmetatable({},{
 })
 ns.party={}
 local party=ns.party --#party
-local ID,maxFollowers,members,ignored=0,1,{},{}
+local ID,maxFollowers,members,ignored,threats=0,1,{},{},{}
 function party:Open(missionID,followers)
 	maxFollowers=followers
 	ID=missionID
+	for enemy,menaces in pairs(G.GetMissionUncounteredMechanics(ID)) do
+		for i=1,#menaces do
+			tinsert(threats,format("%d:%d",enemy,menaces[i]))
+		end
+	end
 	holdEvents()
 end
 function party:Ignore(followerID)
@@ -42,6 +57,9 @@ end
 function party:IsIn(followerID)
 	return tContains(members,followerID)
 end
+function party:MaxSlots()
+	return maxFollowers
+end
 function party:FreeSlots()
 	return maxFollowers-#members
 end
@@ -50,10 +68,9 @@ function party:IsEmpty()
 end

 function party:Dump()
-	for i=1,3 do
-		if (members[i]) then
-			ns.xprint(i,addon:GetFollowerData(members[i],'fullname'))
-		end
+	ns.xprint("Dumping party for mission",ID)
+	for i=1,#members do
+		ns.xprint(addon:GetFollowerData(members[i],'fullname'),G.GetFollowerStatus(members[i] or 1))
 	end
 	ns.xprint(G.GetPartyMissionInfo(ID))
 end
@@ -62,13 +79,19 @@ function party:AddFollower(followerID)
 	if (followerID:sub(1,2) ~= '0x') then ns.xtrace(followerID .. "is not an id") end
 	if (self:FreeSlots()>0) then
 		local rc,code=pcall (G.AddFollowerToMission,ID,followerID)
+		if (not rc and code==false) then
+			pcall(G.RemoveFollowerFromMission,ID,followerID)
+			rc,code=pcall (G.AddFollowerToMission,ID,followerID)
+		end
 		if (rc and code) then
 			tinsert(members,followerID)
 			return true
---@debug@
+--[===[@debug@
 		else
-			ns.xprint("Unable to add", G.GetFollowerName(followerID),"to",ID,code)
---@end-debug@
+			ns.xprint("Unable to add",followerID, G.GetFollowerName(followerID),"to",ID,code,self:IsIn(followerID),G.GetFollowerStatus(followerID))
+			ns.xprint(members[1],members[2],members[3])
+			ns.xprint(debugstack(1,6,0))
+--@end-debug@]===]
 		end
 	end
 end
@@ -77,9 +100,9 @@ function party:RemoveFollower(followerID)
 		if (followerID==members[i]) then
 			tremove(members,i)
 			local rc,code=pcall(G.RemoveFollowerFromMission,ID,followerID)
---@debug@
+--[===[@debug@
 			if (not rc) then trace("Unable to remove", G.GetFollowerName(members[i]),"from",ID,code) end
---@end-debug@
+--@end-debug@]===]
 		return true end
 	end
 end
@@ -91,14 +114,57 @@ function party:StoreFollowers(table)
 	end
 	return #table
 end
-function party:Close()
-	local perc=select(4,G.GetPartyMissionInfo(ID))
+local function fsort(a,b)
+	return addon:GetFollowerData(a,"rank")>addon:GetFollowerData(b,"rank")
+end
+function party:Close(desttable)
+	local perc
+	table.sort(members,fsort)
+	for i=1,#members do
+		local bias=G.GetFollowerBiasForMission(ID,members[i])
+		for _id,ability in pairs(G.GetFollowerAbilities(members[i])) do
+			if not ability.isTrait then
+				for counter,data in pairs(ability.counters) do
+					for j=1,#threats do
+						local enemy,threat,oldbias,follower,name=strsplit(":",threats[j])
+						oldbias=tonumber(oldbias) or -2
+						if bias >oldbias and tonumber(threat)==tonumber(counter) then
+							threats[j]=format("%d:%d:%f:%s:%s",enemy,threat,bias or -2,members[i],G.GetFollowerName(members[i]))
+						end
+					end
+				end
+			end
+		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)
+		if (ns.toc < 60100) then
+			desttable.goldMultiplier = 1
+		end
+		desttable.full=self:FreeSlots()==0
+		desttable.threats=desttable.threats or {}
+		wipe(desttable.threats)
+		for i=1,#threats do
+			tinsert(desttable.threats,threats[i])
+		end
+		perc=desttable.perc
+	else
+		perc=select(4,G.GetPartyMissionInfo(ID))
+	end
 	for i=1,3 do
 		if (members[i]) then
 			local rc,code=pcall(G.RemoveFollowerFromMission,ID,members[i])
---@debug@
+--[===[@debug@
 			if (not rc) then ns.xtrace("Unable to pop", G.GetFollowerName(members[i])," from ",ID,code) end
---@end-debug@
+--@end-debug@]===]

 		else
 			break
@@ -107,12 +173,77 @@ function party:Close()
 	releaseEvents()
 	wipe(members)
 	wipe(ignored)
+	wipe(threats)
 	return perc or 0
 end
-function addon:GetParty(missionID)
-	if (missionID) then
-		return parties[missionID]
+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
+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
+		local followers=self:GetMissionData(missionID,'followers')
+		party.perc=select(4,G.GetPartyMissionInfo(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]
 	else
-		return parties
+		return party
 	end
+
 end
\ No newline at end of file
diff --git a/doc.txt b/doc.txt
index 83ed7f0..9e14563 100644
--- a/doc.txt
+++ b/doc.txt
@@ -291,3 +291,154 @@ Xp bonus:
 570
 593

+------- Methods
+AddFollowerToMission",
+AssignFollowerToBuilding",
+CanGenerateRecruits",
+CanOpenMissionChest",
+CanSetRecruitmentPreference",
+CanUpgradeGarrison",
+CancelConstruction",
+CastSpellOnFollower",
+CastSpellOnMission",
+CloseArchitect",
+CloseGarrisonTradeskillNPC",
+CloseMissionNPC",
+CloseRecruitmentNPC",
+CloseTradeskillCrafter",
+GenerateRecruits",
+GetAllEncounterThreats() Returns the full list of possibile threats
+GetAvailableMissions",
+GetAvailableRecruits",
+GetBasicMissionInfo",
+GetBuildingInfo",
+GetBuildingLockInfo",
+GetBuildingSizes",
+GetBuildingSpecInfo",
+GetBuildingTimeRemaining",
+GetBuildingTooltip",
+GetBuildingUpgradeInfo",
+GetBuildings",
+GetBuildingsForPlot",
+GetBuildingsForSize",
+GetCompleteMissions",
+GetFollowerAbilities(followerID),
+GetFollowerAbilityAtIndex",
+GetFollowerAbilityAtIndexByID",
+GetFollowerAbilityCounterMechanicInfo",
+GetFollowerAbilityDescription",
+GetFollowerAbilityIcon",
+GetFollowerAbilityIsTrait",
+GetFollowerAbilityLink",
+GetFollowerAbilityName",
+GetFollowerActivationCost",
+GetFollowerBiasForMission",
+GetFollowerClassSpec",
+GetFollowerClassSpecAtlas",
+GetFollowerClassSpecByID",
+GetFollowerClassSpecName",
+GetFollowerDisplayID",
+GetFollowerDisplayIDByID",
+GetFollowerInfo",
+GetFollowerInfoForBuilding",
+GetFollowerItemLevelAverage",
+GetFollowerItems",
+GetFollowerLevel",
+GetFollowerLevelXP",
+GetFollowerLink",
+GetFollowerLinkByID",
+GetFollowerMissionCompleteInfo"
+GetFollowerMissionTimeLeft",
+GetFollowerMissionTimeLeftSeconds",
+GetFollowerModelItems",
+GetFollowerName",
+GetFollowerNameByID",
+GetFollowerPortraitIconIDByID"
+GetFollowerQuality",
+GetFollowerQualityTable",
+GetFollowerSoftCap",
+GetFollowerSourceTextByID",
+GetFollowerStatus"
+GetFollowerTraitAtIndex",
+GetFollowerTraitAtIndexByID",
+GetFollowerXP",
+GetFollowerXPTable",
+GetFollowersTraitsForMission"
+GetGarrisonInfo",
+GetGarrisonUpgradeCost",
+GetInProgressMissions",
+GetLandingPageItems",
+GetLandingPageShipmentCount",
+GetLandingPageShipmentInfo",
+GetMissionCompleteEncounters",
+GetMissionDisplayIDs",
+GetMissionInfo",
+GetMissionLink",
+GetMissionMaxFollowers",
+GetMissionName",
+GetMissionRewardInfo",
+GetMissionSuccessChance",
+GetMissionTimes",
+GetMissionUncounteredMechanics",
+GetNumActiveFollowers",
+GetNumFollowerActivationsRemaining",
+GetNumFollowerDailyActivations",
+GetNumFollowers",
+GetNumFollowersForMechanic",
+GetNumFollowersOnMission",
+GetNumPendingShipments",
+GetNumShipmentReagents",
+GetOwnedBuildingInfo",
+GetOwnedBuildingInfoAbbrev",
+GetPartyBuffs",
+GetPartyMentorLevels",
+GetPartyMissionInfo",
+GetPendingShipmentInfo",
+GetPlots",
+GetPlotsForBuilding",
+GetPossibleFollowersForBuilding"
+GetRecruitAbilities",
+GetRecruiterAbilityCategories",
+GetRecruiterAbilityList",
+GetRecruitmentPreferences",
+GetRewardChance",
+GetShipmentContainerInfo",
+GetShipmentItemInfo",
+GetShipmentReagentCurrencyInfo",
+GetShipmentReagentInfo",
+GetShipmentReagentItemLink",
+GetSpecChangeCost",
+GetTabForPlot",
+IsAboveFollowerSoftCap",
+IsFollowerCollected",
+IsFollowerUnique",
+IsInvasionAvailable",
+IsMechanicFullyCountered",
+IsOnGarrisonMap",
+IsOnShipmentQuestForNPC",
+IsUsingPartyGarrison",
+IsVisitGarrisonAvailable",
+MarkMissionComplete",
+MissionBonusRoll",
+PlaceBuilding",
+RecruitFollower",
+RemoveFollower",
+RemoveFollowerFromBuilding",
+RemoveFollowerFromMission",
+RequestGarrisonUpgradeable",
+RequestLandingPageShipmentInfo",
+RequestShipmentCreation",
+RequestShipmentInfo",
+SearchForFollower",
+SetBuildingActive",
+SetBuildingSpecialization",
+SetFollowerFavorite",
+SetFollowerInactive",
+SetRecruitmentPreferences",
+SetUsingPartyGarrison",
+StartMission",
+SwapBuildings",
+TargetSpellHasFollowerItemLevelUpgrade",
+UpgradeBuilding",
+UpgradeGarrison",
+
diff --git a/libs/LibDeformat-3.0/LibDeformat-3.0.lua b/libs/LibDeformat-3.0/LibDeformat-3.0.lua
index f4bcb52..6580517 100644
--- a/libs/LibDeformat-3.0/LibDeformat-3.0.lua
+++ b/libs/LibDeformat-3.0/LibDeformat-3.0.lua
@@ -211,7 +211,7 @@ function LibDeformat.Deformat(text, pattern)
     return get_deformat_function(pattern)(text)
 end

---@debug@
+--[===[@debug@
 function LibDeformat.Test()
     local function tuple(success, ...)
         if success then
@@ -267,6 +267,6 @@ function LibDeformat.Test()
     test("Hello, friend", "Cost: $%d", nil)
     print("LibDeformat-3.0: Tests completed.")
 end
---@end-debug@
+--@end-debug@]===]

 setmetatable(LibDeformat, { __call = function(self, ...) return self.Deformat(...) end })
\ No newline at end of file