Advertisement
Guest User

Garrison Mission Manager - new features - merged with v6

a guest
Nov 25th, 2014
180
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 14.63 KB | None | 0 0
  1. -- Confused about mix of CamelCase and_underscores?
  2. -- Camel case comes from copypasta of how Blizzard calls returns/fields in their code and deriveates
  3. -- Underscore are my own variables
  4.  
  5. local dump = DevTools_Dump
  6. local tinsert = table.insert
  7. local wipe = wipe
  8. local pairs = pairs
  9. local GARRISON_CURRENCY = GARRISON_CURRENCY
  10. local GarrisonMissionFrame = GarrisonMissionFrame
  11. local GarrisonLandingPage = GarrisonLandingPage
  12. local GarrisonRecruitSelectFrame = GarrisonRecruitSelectFrame
  13. local MissionPage = GarrisonMissionFrame.MissionTab.MissionPage
  14. local AddFollowerToMission = C_Garrison.AddFollowerToMission
  15. local GetPartyMissionInfo = C_Garrison.GetPartyMissionInfo
  16. local RemoveFollowerFromMission = C_Garrison.RemoveFollowerFromMission
  17. local NUM_OPTIONS = 3
  18.  
  19. local buttons = {}
  20.  
  21. function GMM_dumpl(pattern, ...)
  22.    local names = { strsplit(",", pattern) }
  23.    for idx = 1, select('#', ...) do
  24.       local name = names[idx]
  25.       if name then name = name:gsub("^%s+", ""):gsub("%s+$", "") end
  26.       print(GREEN_FONT_COLOR_CODE, idx, name, FONT_COLOR_CODE_CLOSE)
  27.       dump(select(idx, ...))
  28.    end
  29. end
  30.  
  31. local xp_to_level = {[90]=400, [91]=800, [92]=1200, [93]=1600, [94]=2000,
  32.                      [95]=3000, [96]=3500, [97]=4000,
  33.                      [98]=5400, [99]=6000}
  34. local xp_to_upgrade = {[2]=60000, [3]=120000}
  35. local function follower_progress(mission_level, follower, xp)
  36.    local progress = 0
  37.    if follower.level >= 100 and follower.quality >= 4 then return 0 end -- epic followers at max level cannot gain xp
  38.  
  39.    -- express follower xp as a percentage of next level/upgrade
  40.    if follower.level < 100 then
  41.       progress = xp / xp_to_level[follower.level] * 100
  42.    else
  43.       progress = xp / xp_to_upgrade[follower.quality] * 100
  44.    end
  45.  
  46.    -- scale follower xp by level delta
  47.    local level_delta = mission_level - follower.level
  48.    if level_delta <= 0 then return progress end  -- normal xp gain
  49.    if level_delta <= 2 then return progress / 2 end  -- slightly underlevel followers gain half xp
  50.    return progress / 10  -- very underlevel followers gain 10% xp
  51. end
  52.  
  53. local _, _, garrison_currency_texture = GetCurrencyInfo(GARRISON_CURRENCY)
  54. garrison_currency_texture = "|T" .. garrison_currency_texture .. ":0|t"
  55. local time_texture = "|TInterface\\Icons\\spell_holy_borrowedtime:0|t"
  56.  
  57. local function FindBestFollowersForMission(mission, followers)
  58.    local min, max = {}, {}
  59.    local top = {}
  60.    for i = 1, NUM_OPTIONS do tinsert(top, {}) end
  61.    local followers_count = #followers
  62.  
  63.    local slots = mission.numFollowers
  64.    if slots > followers_count then return top end
  65.  
  66.    GarrisonMissionFrame:UnregisterEvent("GARRISON_FOLLOWER_LIST_UPDATE")
  67.    GarrisonLandingPage:UnregisterEvent("GARRISON_FOLLOWER_LIST_UPDATE")
  68.    GarrisonRecruitSelectFrame:UnregisterEvent("GARRISON_FOLLOWER_LIST_UPDATE")
  69.    if FollowerLocationInfoFrame then FollowerLocationInfoFrame:UnregisterEvent("GARRISON_FOLLOWER_LIST_UPDATE") end
  70.  
  71.    local mission_id = mission.missionID
  72.    if C_Garrison.GetNumFollowersOnMission(mission_id) > 0 then
  73.       for idx = 1, #followers do
  74.          RemoveFollowerFromMission(mission_id, followers[idx].followerID)
  75.       end
  76.    end
  77.  
  78.    for idx = 1, slots do
  79.       max[idx] = followers_count - slots + idx
  80.       min[idx] = nil
  81.    end
  82.    for idx = slots+1, 3 do
  83.       max[idx] = followers_count + 1
  84.       min[idx] = followers_count + 1
  85.    end
  86.  
  87.    local mission_level = C_Garrison.GetBasicMissionInfo(mission_id).level
  88.    local _, baseXP = C_Garrison.GetMissionInfo(mission_id)
  89.  
  90.    local successXP = 0
  91.    local successResources = 0
  92.    local successItem = nil
  93.    for _, reward in pairs(mission.rewards) do
  94.       if reward.currencyID == GARRISON_CURRENCY then successResources = successResources + reward.quantity end
  95.       if reward.followerXP then successXP = successXP + reward.followerXP end
  96.       if reward.itemID and reward.itemID ~= 120205 then successItem = reward.itemID end  -- itemID 120205 is player XP reward
  97.    end
  98.  
  99.    for i1 = 1, max[1] do
  100.       local follower1 = followers[i1]
  101.       local follower1_id = follower1.followerID
  102.       for i2 = min[2] or (i1 + 1), max[2] do
  103.          local follower2 = followers[i2]
  104.          local follower2_id = follower2 and follower2.followerID
  105.          for i3 = min[3] or (i2 + 1), max[3] do
  106.             local follower3 = followers[i3]
  107.             local follower3_id = follower3 and follower3.followerID
  108.  
  109.             -- Assign followers to mission
  110.             if not AddFollowerToMission(mission_id, follower1_id) then --[[ error handling! ]] end
  111.             if follower2 and not AddFollowerToMission(mission_id, follower2_id) then --[[ error handling! ]] end
  112.             if follower3 and not AddFollowerToMission(mission_id, follower3_id) then --[[ error handling! ]] end
  113.  
  114.             -- Calculate result
  115.             local totalTimeString, totalTimeSeconds, isMissionTimeImproved, successChance, partyBuffs, isEnvMechanicCountered, xpBonus, materialMultiplier = GetPartyMissionInfo(mission_id)
  116.  
  117.             local baseProgress = 0
  118.             if follower1 then baseProgress = baseProgress + follower_progress(mission_level, follower1, baseXP + xpBonus) end
  119.             if follower2 then baseProgress = baseProgress + follower_progress(mission_level, follower2, baseXP + xpBonus) end
  120.             if follower3 then baseProgress = baseProgress + follower_progress(mission_level, follower3, baseXP + xpBonus) end
  121.  
  122.             local successProgress = 0
  123.             -- TODO: how does bonusXP apply to success rewards?
  124.             if follower1 then successProgress = successProgress + follower_progress(mission_level, follower1, successXP) end
  125.             if follower2 then successProgress = successProgress + follower_progress(mission_level, follower2, successXP) end
  126.             if follower3 then successProgress = successProgress + follower_progress(mission_level, follower3, successXP) end
  127.  
  128.             local expectedResources = successResources * materialMultiplier * successChance / 100
  129.             local expectedProgress = baseProgress + (successProgress * successChance / 100)
  130.  
  131.             for idx = 1, NUM_OPTIONS do
  132.                local best = top[idx]
  133.                local found
  134.                repeat -- Checking if new candidate for top is better than any top already stored
  135.                   if not best[1] then found = true break end
  136.  
  137.                   -- prioritize expected garrison resources above all else
  138.                   if best.expectedResources < expectedResources then found = true break end
  139.                   if best.expectedResources > expectedResources then break end
  140.  
  141.                   if not successItem then
  142.                      -- prioritize expected xp gain
  143.                      if best.expectedProgress < expectedProgress then found = true break end
  144.                      if best.expectedProgress > expectedProgress then break end
  145.                   end
  146.  
  147.                   -- prioritize winning an item
  148.                   if best.successChance < successChance then found = true break end
  149.                   if best.successChance > successChance then break end
  150.  
  151.                   -- prioritize mission speed
  152.                   if best.totalTimeSeconds > totalTimeSeconds then found = true break end
  153.                   if best.totalTimeSeconds < totalTimeSeconds then break end
  154.  
  155.                   break
  156.                until true
  157.                if found then
  158.                   local new = {}
  159.                   new[1] = follower1
  160.                   new[2] = follower2
  161.                   new[3] = follower3
  162.                   new.successChance = successChance
  163.                   new.expectedResources = expectedResources
  164.                   new.expectedProgress = expectedProgress
  165.                   new.totalTimeSeconds = totalTimeSeconds
  166.                   new.isMissionTimeImproved = isMissionTimeImproved
  167.                   tinsert(top, idx, new)
  168.                   tremove(top, NUM_OPTIONS + 1)
  169.                   break
  170.                end
  171.             end
  172.  
  173.             -- Unasssign
  174.             RemoveFollowerFromMission(mission_id, follower1_id)
  175.             if follower2 then RemoveFollowerFromMission(mission_id, follower2_id) end
  176.             if follower3 then RemoveFollowerFromMission(mission_id, follower3_id) end
  177.          end
  178.       end
  179.    end
  180.    -- dump(top[1])
  181.  
  182.    GarrisonMissionFrame:RegisterEvent("GARRISON_FOLLOWER_LIST_UPDATE")
  183.    GarrisonLandingPage:RegisterEvent("GARRISON_FOLLOWER_LIST_UPDATE")
  184.    GarrisonRecruitSelectFrame:RegisterEvent("GARRISON_FOLLOWER_LIST_UPDATE")
  185.    if FollowerLocationInfoFrame then FollowerLocationInfoFrame:RegisterEvent("GARRISON_FOLLOWER_LIST_UPDATE") end
  186.  
  187.    -- dump(top)
  188.    -- local location, xp, environment, environmentDesc, environmentTexture, locPrefix, isExhausting, enemies = C_Garrison.GetMissionInfo(missionID);
  189.    -- /run GMM_dumpl("location, xp, environment, environmentDesc, environmentTexture, locPrefix, isExhausting, enemies", C_Garrison.GetMissionInfo(GarrisonMissionFrame.MissionTab.MissionPage.missionInfo.missionID))
  190.    -- /run GMM_dumpl("totalTimeString, totalTimeSeconds, isMissionTimeImproved, successChance, partyBuffs, isEnvMechanicCountered, xpBonus, materialMultiplier", GetPartyMissionInfo(GarrisonMissionFrame.MissionTab.MissionPage.missionInfo.missionID))
  191.    return top
  192. end
  193.  
  194. -- TODO: don't update list if it is not dirty
  195. local filtered_followers = {}
  196. local filtered_followers_count
  197. local function GetFilteredFollowers()
  198.    local followers = C_Garrison.GetFollowers()
  199.    wipe(filtered_followers)
  200.    filtered_followers_count = 0
  201.    for idx = 1, #followers do
  202.       local follower = followers[idx]
  203.       repeat
  204.          if not follower.isCollected then break end
  205.          local status = follower.status
  206.          if status then break end
  207.  
  208.          filtered_followers_count = filtered_followers_count + 1
  209.          filtered_followers[filtered_followers_count] = follower
  210.       until true
  211.    end
  212.  
  213.    -- dump(filtered_followers)
  214.    return filtered_followers, filtered_followers_count
  215. end
  216.  
  217. local available_missions = {}
  218. local function BestForCurrentSelectedMission()
  219.    local missionInfo = MissionPage.missionInfo
  220.    local mission_id = missionInfo.missionID
  221.  
  222.    -- print("Mission ID:", mission_id)
  223.  
  224.    local filtered_followers, filtered_followers_count = GetFilteredFollowers()
  225.  
  226.    C_Garrison.GetAvailableMissions(available_missions)
  227.    local mission
  228.    for idx = 1, #available_missions do
  229.       if available_missions[idx].missionID == mission_id then
  230.          mission = available_missions[idx]
  231.          break
  232.       end
  233.    end
  234.  
  235.    -- dump(mission)
  236.  
  237.    local top = FindBestFollowersForMission(mission, filtered_followers)
  238.  
  239.    if not buttons['MissionPage1'] then ButtonsInit() end
  240.    for idx = 1, 3 do
  241.       local button = buttons['MissionPage' .. idx]
  242.       local top_entry = top[idx]
  243.       button[1] = top_entry[1] and top_entry[1].followerID or nil
  244.       button[2] = top_entry[2] and top_entry[2].followerID or nil
  245.       button[3] = top_entry[3] and top_entry[3].followerID or nil
  246.       if top_entry.successChance then
  247.          button:SetFormattedText(
  248.             "%d%%\n%s%s%s",
  249.             top_entry.successChance,
  250.             top_entry.expectedResources > 0 and string.format("%.1f", top_entry.expectedResources) .. garrison_currency_texture or "",
  251.             top_entry.expectedProgress > 0 and string.format("%.2f", top_entry.expectedProgress) .. " |TInterface\\Icons\\XPBonus_Icon:0|t" or "",
  252.             top_entry.isMissionTimeImproved and time_texture or ""
  253.          )
  254.       else
  255.          button:SetText("")
  256.       end
  257.    end
  258.  
  259. end
  260.  
  261. local function PartyButtonOnClick(self)
  262.    if self[1] then
  263.       local MissionPageFollowers = GarrisonMissionFrame.MissionTab.MissionPage.Followers
  264.       for idx = 1, #MissionPageFollowers do
  265.          GarrisonMissionPage_ClearFollower(MissionPageFollowers[idx])
  266.       end
  267.  
  268.       for idx = 1, #MissionPageFollowers do
  269.          local followerFrame = MissionPageFollowers[idx]
  270.          local follower = self[idx]
  271.          if follower then
  272.             local followerInfo = C_Garrison.GetFollowerInfo(follower)
  273.             GarrisonMissionPage_SetFollower(followerFrame, followerInfo)
  274.          end
  275.       end
  276.    end
  277.  
  278.    GarrisonMissionPage_UpdateMissionForParty()
  279. end
  280.  
  281. -- Add more data to mission list over Blizzard's own
  282. -- GarrisonMissionList_Update
  283. local function GarrisonMissionList_Update_More()
  284.    local self = GarrisonMissionFrame.MissionTab.MissionList
  285.    if (self.showInProgress) then return end
  286.  
  287.    local missions = self.availableMissions
  288.    local numMissions = #missions
  289.    if (numMissions == 0) then return end
  290.  
  291.    local missions = self.availableMissions
  292.    local scrollFrame = self.listScroll
  293.    local offset = HybridScrollFrame_GetOffset(scrollFrame)
  294.    local buttons = scrollFrame.buttons
  295.    local numButtons = #buttons
  296.  
  297.    local filtered_followers, filtered_followers_count = GetFilteredFollowers()
  298.  
  299.    for i = 1, numButtons do
  300.       local button = buttons[i]
  301.       local alpha = 1
  302.       local index = offset + i
  303.       if index <= numMissions then
  304.          local mission = missions[index]
  305.          -- dump(mission)
  306.          if mission.numFollowers > filtered_followers_count then
  307.             alpha = 0.3
  308.          else
  309.             -- buttons will be added here
  310.          end
  311.       end
  312.       button:SetAlpha(alpha)
  313.    end
  314. end
  315. --       hooksecurefunc("GarrisonMissionList_Update", GarrisonMissionList_Update_More)
  316.  
  317. local function ButtonsInit()
  318.    local prev
  319.    for idx = 1, NUM_OPTIONS do
  320.       if not buttons['MissionPage' .. idx] then
  321.          local set_followers_button = CreateFrame("Button", nil, GarrisonMissionFrame.MissionTab.MissionPage, "UIPanelButtonTemplate")
  322.          set_followers_button:SetText(idx)
  323.          set_followers_button:SetWidth(100)
  324.          set_followers_button:SetHeight(50)
  325.          if not prev then
  326.             set_followers_button:SetPoint("TOPLEFT", GarrisonMissionFrame.MissionTab.MissionPage, "TOPRIGHT", 0, 0)
  327.          else
  328.             set_followers_button:SetPoint("TOPLEFT", prev, "BOTTOMLEFT", 0, 0)
  329.          end
  330.          set_followers_button:SetScript("OnClick", PartyButtonOnClick)
  331.          set_followers_button:Show()
  332.          prev = set_followers_button
  333.          buttons['MissionPage' .. idx] = set_followers_button
  334.       end
  335.    end
  336. end
  337. ButtonsInit()
  338. hooksecurefunc("GarrisonMissionPage_ShowMission", BestForCurrentSelectedMission)
  339. -- local count = 0
  340. -- hooksecurefunc("GarrisonFollowerList_UpdateFollowers", function(self) count = count + 1 print("GarrisonFollowerList_UpdateFollowers", count, self:GetName(), self:GetParent():GetName()) end)
  341.  
  342. -- Globals deliberately exposed for people outside
  343. function GMM_Click(button_name)
  344.    local button = buttons[button_name]
  345.    if button then button:Click() end
  346. end
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement