Advertisement
Guest User

Untitled

a guest
Jan 24th, 2017
369
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 30.13 KB | None | 0 0
  1. local E, L, V, P, G = unpack(ElvUI);
  2. local NP = E:GetModule('NamePlates')
  3. local ipairs = ipairs
  4. local print = print
  5. local select = select
  6. local strsplit = strsplit
  7. local strtrim = strtrim
  8. local string = string
  9. local table = table
  10. local _GetTime = GetTime
  11.  
  12. local loaded = false
  13. local quantity = 0
  14. local lastKill = {0} -- To be populated later, do not remove the initial value. The zero means inconclusive/invalid data.
  15. local currentPullUpdateTimer = 0
  16. local activeNameplates = {}
  17. local LSM = LibStub('LibSharedMedia-3.0');
  18.  
  19. mppDebug = false
  20.  
  21. mppSimulationMode = false
  22. local simulationMax = 220
  23. local simulationCurrent = 28
  24.  
  25. local version = "0.3e"
  26. local numVersion = 6
  27. local defaultSettings = {
  28. version = version,
  29. dbVersion = numVersion,
  30. enabled = true,
  31.  
  32. inconclusiveDataThreshold = 100, -- Mobs killed within this span of time (in milliseconds) will not be processed since we might not get the criteria update fast enough to know which mob gave what progress. Well, that's the theory anyway.
  33. maxTimeSinceKill = 600, -- Lag tolerance between a mob dying and the progress criteria updating, in milliseconds.
  34.  
  35. enableTooltip = true,
  36. tooltipColor = "82E0FF", -- Color of the tooltip. Duh.
  37.  
  38. enablePullEstimate = true,
  39. pullEstimateCombatOnly = false,
  40.  
  41. nameplateUpdateRate = 200, -- Rate (in milliseconds) at which we update the progress we get from the current pull, as estimated by active name plates you're in combat with. Also the update rate of getting new values for nameplate text overlay if enabled.
  42.  
  43. enableNameplateText = true,
  44. nameplateTextColor = "FFFFFF",
  45. }
  46.  
  47. local warnings = {}
  48.  
  49. mppDefaultValues = {{1,40,"Test NPC"},{2,35,"Test NPC"},{104277,4,"Legion Hound"},{102253,4,"Understone Demolisher"},{98954,4,"Felsworn Myrmidon"},{97185,10,"The Grimewalker"},{113537,4,"Unknown"},{102094,4,"Risen Swordsman"},{104246,4,"Duskwatch Guard"},{104278,3,"Felbound Enforcer"},{105617,4,"Eredar Chaosbringer"},{105952,6,"Withered Manawraith"},{97043,4,"Seacursed Slaver"},{102430,1,"Tarspitter Slug"},{98366,4,"Ghostly Retainer"},{95832,2,"Valarjar Shieldmaiden"},{104295,1,"Blazing Imp"},{97171,10,"Hatecoil Arcanist"},{102287,4,"Unknown"},{98733,4,"Withered Fiend"},{105921,4,"Nightborne Spellsword"},{96247,1,"Vileshard Crawler"},{98813,4,"Bloodscent Felhound"},{95769,4,"Mindshattered Screecher"},{113699,8,"Forgotten Spirit"},{91785,2,"Wandering Shellback"},{100216,4,"Hatecoil Wrangler"},{97172,1,"Saltsea Droplet"},{98973,1,"Skeletal Warrior"},{105651,4,"Dreadborne Seer"},{92168,8,"Target Dummy Test2"},{105699,3,"Mana Saber"},{105715,4,"Watchful Inquisitor"},{98368,4,"Ghostly Protector"},{95834,2,"Valarjar Mystic"},{91786,1,"Gritslime Snail"},{98177,12,"Glayvianna Soulrender"},{97173,4,"Restless Tides"},{96934,2,"Valarjar Trapper"},{105636,4,"Understone Drudge"},{95771,4,"Dreadsoul Ruiner"},{96584,4,"Immoliant Fury"},{97365,4,"Seacursed Mistmender"},{91006,4,"Rockback Gnasher"},{96664,2,"Valarjar Runecarver"},{105876,1,"Enchanted Broodling"},{95947,4,"Mak'rana Hardshell"},{95772,4,"Frenzied Nightclaw"},{99358,4,"Rotheart Dryad"},{101414,2,"Saltscale Skulker"},{98370,4,"Ghostly Councilor"},{111901,3,"Unknown"},{105845,1,"Glowing Spiderling"},{98243,4,"Soul-Torn Champion"},{98275,4,"Risen Archer"},{99359,3,"Rotheart Keeper"},{106786,1,"Bitterbrine Slave"},{91008,4,"Rockbound Pelter"},{104300,4,"Shadow Mistress"},{98706,6,"Commander Shemdah'sohn"},{98770,4,"Wrathguard Felblade"},{105703,1,"Mana Wyrm"},{100364,4,"Spirit of Vengeance"},{99360,9,"Vilethorn Blossom"},{97097,4,"Helarjar Champion"},{91790,4,"Mak'rana Siltwalker"},{98691,4,"Risen Scout"},{105720,4,"Understone Drudge"},{104270,8,"Guardian Construct"},{101991,4,"Nightmare Dweller"},{98963,1,"Blazing Imp"},{98756,4,"Arcane Anomaly"},{98533,10,"Foul Mother"},{105705,4,"Bound Energy"},{99649,12,"Dreadlord Mendacius"},{98900,4,"Wyrmtongue Trickster"},{98677,1,"Rook Spiderling"},{92350,4,"Understone Drudge"},{102566,12,"Grimhorn the Enslaver"},{102104,4,"Enslaved Shieldmaiden"},{102375,3,"Runecarver Slave"},{96574,1,"Stormforged Sentinel"},{102232,4,"Rockbound Trapper"},{91793,1,"Seaspray Crab"},{100527,3,"Dreadfire Imp"},{98280,4,"Risen Arcanist"},{95842,2,"Valarjar Thundercaller"},{91794,1,"Saltscale Lurker"},{104251,4,"Duskwatch Sentry"},{91332,4,"Stoneclaw Hunter"},{102351,1,"Mana Wyrm"},{102584,4,"Malignant Defiler"},{96480,1,"Viletongue Belcher"},{98759,4,"Vicious Manafang"},{100441,1,"Unknown"},{98926,4,"Shadow Hunter"},{95779,10,"Festerhide Grizzly"},{99365,4,"Taintheart Stalker"},{96608,2,"Ebonclaw Worg"},{96640,2,"Valarjar Marksman"},{90998,4,"Blightshard Shaper"},{106059,4,"Warp Shade"},{98425,4,"Unstable Amalgamation"},{102788,4,"Felspite Dominator"},{97182,6,"Night Watch Mariner"},{101839,4,"Risen Companion"},{100529,1,"Hatespawn Slime"},{99307,12,"Skjal"},{98521,10,"Lord Etheldrin Ravencrest"},{98792,4,"Wyrmtongue Scavenger"},{102404,4,"Stoneclaw Grubmaster"},{100526,1,"Tormented Bloodseeker"},{106787,1,"Bitterbrine Slave"},{99366,4,"Taintheart Summoner"},{97087,2,"Valarjar Champion"},{101438,4,"Vileshard Chunk"},{97119,1,"Shroud Hound"},{90997,4,"Mightstone Breaker"},{91796,10,"Skrog Wavecrasher"},{97677,1,"Barbed Spiderling"},{113998,4,"Mightstone Breaker"},{95920,2,"Animated Storm"},{91781,4,"Hatecoil Warrior"},{95861,4,"Hatecoil Oracle"},{99188,4,"Waterlogged Soul Guard"},{91792,10,"Stormwake Hydra"},{95939,10,"Skrog Tidestomper"},{91783,4,"Hatecoil Stormweaver"},{106785,1,"Bitterbrine Slave"},{111563,4,"Duskwatch Guard"},{98681,6,"Rook Spinner"},{97197,2,"Valarjar Purifier"},{91000,8,"Vileshard Hulk"},{100451,3,"Target Dummy Test"},{91782,10,"Hatecoil Crusher"},{97678,8,"Aranasi Broodmother"},{96587,4,"Felsworn Infester"},{97200,4,"Seacursed Soulkeeper"},{100531,8,"Bloodtainted Fury"},{101549,1,"Arcane Minion"},{99033,4,"Helarjar Mistcaller"},{105629,1,"Wyrmtongue Scavenger"},{98810,6,"Wrathguard Bladelord"},{95766,4,"Crazed Razorbeak"},{98919,4,"Seacursed Swiftblade"},{105682,8,"Felguard Destroyer"},{101679,4,"Dreadsoul Poisoner"},{105915,4,"Nightborne Reclaimer"},{91001,4,"Tarspitter Lurker"},{98732,1,"Plagued Rat"},} -- Yeah ok look I know this is far from optimal, but I'm tired and this was a super quick way of just getting it in there.
  50.  
  51. local dbFixes = {}
  52. dbFixes[4] = {{1, 40, "Test NPC"}, {99307, 12, "Skjal"}}
  53. dbFixes[5] = {{2, 35, "Test NPC"}, {95947, 4, "Mak'rana Hardshell"}}
  54.  
  55. -- New to lua, don't judge too hard pls.
  56.  
  57. function mppGetLastKill()
  58. return lastKill
  59. end
  60.  
  61. --
  62. -- GENERAL ADDON UTILITY
  63. -- And by "utility" I mostly mean creating a bunch of shit that should really be built-in.
  64.  
  65. local function debugLog(s)
  66. if mppDebug then
  67. print(s)
  68. end
  69. end
  70.  
  71. local function split(str)
  72. a = {}
  73. for s in string.gmatch(str, "%S+") do
  74. table.insert(a, s)
  75. end
  76. return a
  77. end
  78.  
  79. local function round(number, decimals)
  80. return (("%%.%df"):format(decimals)):format(number)
  81. end
  82.  
  83. local function tlen(t)
  84. local length = 0
  85. for _ in pairs(t) do
  86. length = length + 1
  87. end
  88. return length
  89. end
  90.  
  91. local function GetTime()
  92. return _GetTime() * 1000
  93. end
  94.  
  95. local function hasWarned(message)
  96. for _,warning in pairs(warnings) do
  97. if warning == message then
  98. return true
  99. end
  100. end
  101. return false
  102. end
  103.  
  104. local function warning(message)
  105. if not hasWarned(message) then
  106. print(message)
  107. table.insert(warnings, message)
  108. return true
  109. end
  110. return false
  111. end
  112.  
  113. local function getSetting(setting)
  114. if (not setting or MythicPlusProgressDB["settings"][setting] == nil) then
  115. warning("MPP attempted to get missing setting: " .. setting or "nil")
  116. return
  117. end
  118. return MythicPlusProgressDB["settings"][setting]
  119. end
  120.  
  121. function mppGetSetting(setting)
  122. return getSetting(setting)
  123. end
  124.  
  125. local function setSetting(setting, value)
  126. if (not setting or MythicPlusProgressDB["settings"][setting] == nil) then
  127. warning("MPP attempted to set missing setting: " .. setting or "nil")
  128. return
  129. end
  130. MythicPlusProgressDB["settings"][setting] = value
  131. return value
  132. end
  133.  
  134. local function toggleSetting(setting)
  135. return setSetting(setting, not getSetting(setting))
  136. end
  137.  
  138. --
  139. -- WOW GENERAL WRAPPERS/EZUTILITIES
  140. --
  141.  
  142. local function getNPCID(guid)
  143. local targetType, _,_,_,_, npcID = strsplit("-", guid)
  144. if targetType == "Creature" or targetType == "Vehicle" and npcID then
  145. return tonumber(npcID)
  146. end
  147. end
  148.  
  149. -- TODO: Figure out how to filter out bosses.
  150. local function isValidTarget(targetToken)
  151. if UnitCanAttack("player", targetToken) and not UnitIsDead(targetToken) then
  152. return true
  153. end
  154. end
  155.  
  156. local function getSteps()
  157. return select(3, C_Scenario.GetStepInfo())
  158. end
  159.  
  160. local function isDungeonFinished()
  161. if mppSimulationMode then return false end
  162. return (getSteps() and getSteps() < 1)
  163. end
  164.  
  165. -- Will also return true in challenge modes if those are ever re-implemented as M+ is basically recycled Challenge Mode.
  166. local function isMythicPlus()
  167. if mppSimulationMode then return true end
  168. local difficulty = select(3, GetInstanceInfo()) or -1
  169. if difficulty == 8 and not isDungeonFinished() then
  170. return true
  171. else
  172. return false
  173. end
  174. end
  175.  
  176. -- DEPRECATED
  177. local function isTeeming()
  178. warning("Deprecated function isTeeming called!")
  179. return
  180. --[[local affixes = select(2, C_ChallengeMode.GetActiveKeystoneInfo())
  181. if affixes then
  182. for k,v in pairs(affixes) do
  183. if v == 5 then
  184. return true
  185. end
  186. return false
  187. end
  188. end--]]
  189. end
  190.  
  191. local function getProgressInfo()
  192. if isMythicPlus() then
  193. local numSteps = select(3, C_Scenario.GetStepInfo()) -- GetStepInfo tells us how many steps there are.
  194. if numSteps and numSteps > 0 then
  195. local info = {C_Scenario.GetCriteriaInfo(numSteps)} -- It should be the last step.
  196. if info[13] == true then -- if isWeightedProgress
  197. return info
  198. end
  199. end
  200. end
  201. end
  202.  
  203. function mppGetProgressInfo()
  204. return getProgressInfo()
  205. end
  206.  
  207. local function getMaxQuantity()
  208. if mppSimulationMode then return simulationMax end
  209. local progInfo = getProgressInfo()
  210. if progInfo then
  211. return getProgressInfo()[5]
  212. end
  213. end
  214.  
  215. local function getCurrentQuantity()
  216. if mppSimulationMode then return simulationCurrent end
  217. return strtrim(getProgressInfo()[8], "%")
  218. end
  219.  
  220. local function getEnemyForcesProgress()
  221. -- debugLog("getEnemyForcesProgress called.")
  222. -- Returns exact float value of current enemies killed progress (1-100).
  223. local quantity, maxQuantity = getCurrentQuantity(), getMaxQuantity()
  224. local progress = quantity / maxQuantity
  225. return progress * 100
  226. end
  227.  
  228. --
  229. -- DB READ/WRITES
  230. --
  231.  
  232. local function getValue(npcID)
  233. debugLog("getValue called. Args: " .. npcID)
  234. local npcData = MythicPlusProgressDB["npcData"][npcID]
  235. if npcData then
  236. local hiValue, hiOccurrence = nil, -1
  237. for value, occurrence in pairs(npcData["values"]) do
  238. if occurrence > hiOccurrence then
  239. hiValue, hiOccurrence = value, occurrence
  240. end
  241. end
  242. if hiValue ~= nil then
  243. return hiValue
  244. end
  245. end
  246. end
  247.  
  248. local function deleteEntry(npcID)
  249. local exists = (MythicPlusProgressDB["npcData"][npcID] ~= nil)
  250. MythicPlusProgressDB["npcData"][npcID] = nil
  251. return exists
  252. end
  253.  
  254. local function updateValue(npcID, value, npcName, forceQuantity)
  255. --debugLog("updateValue called. Args: " .. npcID or "nil" .. ", " .. value or "nil" .. ", " .. npcName or "nil" .. ", " .. forceQuantity or "nil")
  256. if value <= 0 then
  257. debugLog("Discarding update for " .. toString(npcName) .. "(" .. npcID .. ") due to value being " .. tovalue)
  258. return
  259. end
  260. local npcData = MythicPlusProgressDB["npcData"][npcID]
  261. if not npcData then
  262. MythicPlusProgressDB["npcData"][npcID] = {values={}, name=npcName or "Unknown"}
  263. return updateValue(npcID, value, npcName, forceQuantity)
  264. end
  265. local values = npcData["values"]
  266. if values[value] == nil then
  267. values[value] = (forceQuantity or 1)
  268. else
  269. values[value] = values[value] + (forceQuantity or 1)
  270. end
  271. for val, occurrence in pairs(values) do
  272. if val ~= value then
  273. values[val] = occurrence * 0.75 -- Newer values will quickly overtake old ones
  274. end
  275. end
  276. end
  277.  
  278. -- Temp testing global access
  279. function mppUpdateValue(npcID, value, npcName, forceQuantity)
  280. return updateValue(npcID, value, npcName, forceQuantity)
  281. end
  282.  
  283. -- Temp testing global access
  284. function mppGetValue(npcID, value)
  285. return getValue(npcID)
  286. end
  287.  
  288. local function exportData()
  289. local a = string.format("Export ver %s (%s) - %i mobs: {", getSetting("version"), getSetting("dbVersion"), tlen(MythicPlusProgressDB["npcData"]))
  290. for npcID,t in pairs(MythicPlusProgressDB["npcData"]) do
  291. local value = getValue(npcID)
  292. local name = t["name"]
  293. a = a .. "{".. npcID..","..value..",\""..name.."\"},"
  294. end
  295. a = a .. "}"
  296. local f = CreateFrame('EditBox', "MPPExportBox", UIParent, "InputBoxTemplate")
  297. f:SetSize(100, 50)
  298. f:SetPoint("CENTER", 0, 150)
  299. f:SetScript("OnEnterPressed", f.Hide)
  300. f:SetText(a)
  301. end
  302.  
  303. --
  304. -- Light DB wrap
  305. --
  306.  
  307. -- Returns a nil-100 number representing the percentual progress that npcID is expected to give you.
  308. local function getEstimatedProgress(npcID)
  309. debugLog("getEstimatedProgress called. Args: " .. npcID)
  310. local npcValue, maxQuantity = getValue(npcID), getMaxQuantity()
  311. if npcValue and maxQuantity then
  312. return (npcValue / maxQuantity) * 100
  313. end
  314. end
  315.  
  316. --
  317. -- TRIGGERS/HOOKS
  318. --
  319.  
  320. -- Called when our enemy forces criteria increases, no matter how small the increase (but >0).
  321. local function onProgressUpdated(deltaProgress)
  322. debugLog("onProgressUpdated called. Args: " .. deltaProgress)
  323. if currentQuantity == getMaxQuantity() then
  324. return -- Disregard data that caps us as we don't know if we got the full value.
  325. end
  326. local timestamp, npcID, npcName, isDataUseful = unpack(lastKill) -- See what the last mob we killed was
  327. if timestamp and npcID and deltaProgress and isDataUseful then -- Assert that we have some useful data to work with
  328. local timeSinceKill = GetTime() - timestamp
  329. debugLog("timeSinceKill: " .. timestamp .. " Current Time: " .. GetTime() .. "Timestamp of kill: " .. timeSinceKill)
  330. if timeSinceKill <= getSetting("maxTimeSinceKill") then
  331. debugLog(string.format("Gained %f%s. Last mob killed was %s (%i) %fs ago", deltaProgress, "%", npcName, npcID, timeSinceKill/1000))
  332. updateValue(npcID, deltaProgress, npcName) -- Looks like we have ourselves a valid entry. Set this in our database/list/whatever.
  333. else
  334. debugLog(string.format("Gained %f%s. Last mob killed was %s (%i) %fs ago (PAST CUTOFF!)", deltaProgress, "%", npcName, npcID, timeSinceKill))
  335. end
  336. end
  337. end
  338.  
  339. -- Called directly by our hook
  340. local function onCriteriaUpdate()
  341. debugLog("onCriteriaUpdate called")
  342. if not currentQuantity then
  343. currentQuantity = 0
  344. end
  345. if not isMythicPlus() or not loaded or not getSetting("enabled") then return end
  346. newQuantity = getCurrentQuantity()
  347. deltaQuantity = newQuantity - currentQuantity
  348. if deltaQuantity > 0 then
  349. currentQuantity = newQuantity
  350. onProgressUpdated(deltaQuantity)
  351. end
  352.  
  353. end
  354.  
  355. -- Called directly by our hook
  356. local function onCombatLogEvent(args)
  357. --local _,combatType,_,_,_,_,_, destGUID, destName = unpack(args)
  358. --if combatType == "UNIT_DIED" then
  359. local timestamp, combatType, something, srcGUID, srcName, srcFlags, something2, destGUID, destName, destFlags = unpack(args)
  360. if combatType == "PARTY_KILL" then
  361. if not isMythicPlus() then return end
  362. if mppDebug then
  363. --foreach(args, print)
  364. end
  365. local npcID = getNPCID(destGUID)
  366. if npcID then
  367. local isDataUseful = true
  368. local timeSinceLastKill = GetTime() - lastKill[1]
  369. if timeSinceLastKill <= getSetting("inconclusiveDataThreshold") then
  370. debugLog("Data not useful: " .. timeSinceLastKill .. " - " .. lastKill[1] .. " - " .. GetTime())
  371. isDataUseful = false
  372. end
  373. lastKill = {GetTime(), npcID, destName, isDataUseful} -- timestamp is not at all accurate, we use GetTime() instead.
  374. if mppDebug then
  375. foreach(lastKill, print)
  376. end
  377. end
  378. end
  379. end
  380.  
  381. local function verifySettings(forceWipe)
  382. for setting, value in pairs(defaultSettings) do
  383. if MythicPlusProgressDB["settings"][setting] == nil or forceWipe then
  384. MythicPlusProgressDB["settings"][setting] = value
  385. end
  386. end
  387. -- At last, update version string
  388.  
  389. setSetting("version", version)
  390. end
  391.  
  392. local function upgradeDB()
  393. local oldVer = MythicPlusProgressDB["settings"]["dbVersion"]
  394. for ver, fixes in pairs(dbFixes) do
  395. if ver > oldVer then
  396. for _,fixTable in pairs(fixes) do
  397. local npcID, value, name = unpack(fixTable)
  398. deleteEntry(npcID)
  399. updateValue(npcID, value, name)
  400. end
  401. end
  402. end
  403. setSetting("dbVersion", numVersion)
  404. end
  405.  
  406.  
  407. local function verifyDB(forceWipe)
  408. if not MythicPlusProgressDB or not MythicPlusProgressDB["settings"] or not MythicPlusProgressDB["npcData"] or forceWipe then
  409. print("Running first time MPP setup. This should only happen once. Enjoy! ;)")
  410. MythicPlusProgressDB = {}
  411. MythicPlusProgressDB["settings"] = {}
  412. MythicPlusProgressDB["settings"]["dbVersion"] = defaultSettings["dbVersion"]
  413. MythicPlusProgressDB["npcData"] = {}
  414. else
  415. MythicPlusProgressDB["settings"]["dbVersion"] = MythicPlusProgressDB["settings"]["dbVersion"] or 0
  416. end
  417. verifySettings()
  418. if mppDefaultValues ~= nil then
  419. for k,v in pairs(mppDefaultValues) do
  420. local npcID, value, name = unpack(v)
  421. if getValue(npcID) == nil then
  422. updateValue(npcID, value, name, 1)
  423. end
  424. end
  425. end
  426.  
  427. -- DB Fixes per version
  428. upgradeDB()
  429. end
  430.  
  431. -- Called directly by our hook
  432. local function onAddonLoad(addonName)
  433. if addonName == "MythicPlusProgress" then
  434. verifyDB()
  435. if isMythicPlus() then
  436. quantity = getEnemyForcesProgress()
  437. debugLog("MPP Loaded in progress: " .. quantity .. "in.")
  438. else
  439. quantity = 0
  440. debugLog("MPP loaded not in progress.")
  441. end
  442. loaded = true
  443. end
  444. end
  445.  
  446. ---
  447. --- TOOLTIPS
  448. ---
  449.  
  450. local function addLineToTooltip(str)
  451. GameTooltip:AddDoubleLine(str)
  452. GameTooltip:Show()
  453. end
  454.  
  455. local function shouldAddTooltip(unit)
  456. if loaded and getSetting("enabled") and getSetting("enableTooltip") and isMythicPlus() and isValidTarget(unit) then
  457. return true
  458. end
  459. return false
  460. end
  461.  
  462. local function getTooltipMessage(npcID)
  463. local tempMessage = "|cFF"..getSetting("tooltipColor").."M+Progress: "
  464. local estProg = getEstimatedProgress(npcID)
  465. if not estProg then
  466. return tempMessage .. "No record."
  467. end
  468. mobsLeft = (100 - getEnemyForcesProgress()) / estProg
  469. tempMessage = string.format("%s%.2f%s (%i left)", tempMessage, estProg, "%", math.ceil(mobsLeft))
  470. return tempMessage
  471. end
  472.  
  473. local function onNPCTooltip(self)
  474. local unit = select(2, self:GetUnit())
  475. if unit then
  476. local guid = UnitGUID(unit)
  477. npcID = getNPCID(guid)
  478. if npcID and shouldAddTooltip(unit) then
  479. local tooltipMessage = getTooltipMessage(npcID)
  480. if tooltipMessage then
  481. addLineToTooltip(tooltipMessage)
  482. end
  483. end
  484. end
  485. end
  486.  
  487. ---
  488. --- SHITTY CURRENT PULL FRAME
  489. ---
  490.  
  491. currentPullFrame = CreateFrame("frame", "currentPullFrame12", UIParent)
  492. mppFrame = currentPullFrame
  493. currentPullFrame:SetPoint("CENTER", UIParent, 400, 300)
  494. currentPullFrame:EnableMouse(true)
  495. currentPullFrame:SetMovable(true)
  496. currentPullFrame:RegisterForDrag("LeftButton")
  497. currentPullFrame:SetScript("OnDragStart", currentPullFrame.StartMoving)
  498. currentPullFrame:SetScript("OnDragStop", currentPullFrame.StopMovingOrSizing)
  499. currentPullFrame:SetWidth(50)
  500. currentPullFrame:SetHeight(50)
  501. --currentPullFrame:SetAllPoints()
  502. --currentPullFrame:SetClampRectInsets(200, 400, 200, 500)
  503. currentPullText = currentPullFrame:CreateFontString("currentPullString", "BACKGROUND", "GameFontHighlightLarge")
  504. currentPullText:SetPoint("CENTER");
  505. currentPullText:SetText("MPP String Uninitialized.")
  506.  
  507. ---
  508. --- NAMEPLATES
  509. ---
  510.  
  511. local function isTargetPulled(target)
  512. -- debugLog("isTargetPulled with args: " ..target)
  513. local threat = UnitThreatSituation("player", target) or -1 -- Is nil if we're not on their aggro table, so make it -1 instead.
  514. if isValidTarget(target) and (threat >= 0 or UnitPlayerControlled(target.."target")) then
  515. return true
  516. end
  517. return false
  518. end
  519.  
  520. local function getPulledUnits()
  521. local tempList = {}
  522. for _, nameplate in pairs(C_NamePlate.GetNamePlates()) do
  523. if nameplate.UnitFrame.unitExists then
  524. if isTargetPulled(nameplate.UnitFrame.displayedUnit) then
  525. table.insert(tempList, UnitGUID(nameplate.UnitFrame.displayedUnit))
  526. end
  527. end
  528. end
  529. return tempList
  530. end
  531.  
  532. local function getPulledProgress(pulledUnits)
  533. local estProg = 0
  534. for _, guid in pairs(pulledUnits) do
  535. npcID = getNPCID(guid)
  536. if npcID then
  537. estProg = estProg + (getEstimatedProgress(npcID) or 0)
  538. end
  539. end
  540. return estProg
  541. end
  542.  
  543. local function shouldShowCurrentPullEstimate()
  544. if getSetting("enabled") and getSetting("enablePullEstimate") and isMythicPlus() and not isDungeonFinished() then
  545. if getSetting("pullEstimateCombatOnly") and not UnitAffectingCombat("player") then
  546. return false
  547. end
  548. return true
  549. end
  550. return false
  551. end
  552.  
  553. local function setCurrentPullEstimateLabel(s)
  554. currentPullString:SetText(s)
  555. currentPullFrame:SetWidth(currentPullString:GetStringWidth())
  556. currentPullFrame:SetHeight(currentPullString:GetStringHeight())
  557. --print(currentPullFrame:GetCenter())
  558. end
  559.  
  560. local function updateCurrentPullEstimate()
  561. if not shouldShowCurrentPullEstimate() then
  562. currentPullFrame:Hide()
  563. return
  564. else
  565. currentPullFrame:Show()
  566. end
  567. local pulledUnits = getPulledUnits()
  568. local estProg = getPulledProgress(pulledUnits)
  569. -- debugLog(tlen(pulledUnits) .. "/" .. tlen(activeNameplates).." active nameplates for an estimated " .. estProg .. "%")
  570. local curProg = getEnemyForcesProgress()
  571. local totProg = estProg + curProg
  572. if estProg == 0 then
  573. tempMessage = "No recorded mobs pulled or nameplates inactive."
  574. else
  575. tempMessage = string.format("Current pull: %.2f%s + %.2f%s = %.2f%s", curProg, "%", estProg, "%", (math.floor(totProg*100)/100), "%")
  576. end
  577. setCurrentPullEstimateLabel(tempMessage)
  578. end
  579.  
  580. local function createNameplateText(token)
  581. local npcID = getNPCID(UnitGUID(token))
  582. if npcID then
  583. if activeNameplates[token] then
  584. activeNameplates[token]:Hide() -- This should never happen...
  585. end
  586. local nameplate;
  587. if not E.private.nameplates.enable then
  588. nameplate = C_NamePlate.GetNamePlateForUnit(token)
  589. else
  590. nameplate = nameplate or NP:GetNamePlateForUnit(token);
  591. end
  592. if nameplate then
  593. if not E.private.nameplates.enable then
  594. activeNameplates[token] = nameplate:CreateFontString(token.."mppProgress", nameplate.UnitFrame.healthBar, "GameFontHighlightSmall")
  595. else
  596. activeNameplates[token] = nameplate:CreateFontString(token.."mppProgress", nameplate.UnitFrame.HealthBar, "GameFontHighlightSmall")
  597. end
  598. activeNameplates[token]:SetText("+?%")
  599. end
  600. end
  601. end
  602.  
  603. local function removeNameplateText(token)
  604. if activeNameplates[token] ~= nil then
  605. activeNameplates[token]:SetText("")
  606. activeNameplates[token]:Hide()
  607. activeNameplates[token] = nil
  608. end
  609. end
  610.  
  611. local function updateNameplateValue(token)
  612. local npcID = getNPCID(UnitGUID(token))
  613. if npcID then
  614. local estProg = getEstimatedProgress(npcID)
  615. if estProg and estProg > 0 then
  616. local tempMessage = "|cFF"..getSetting("nameplateTextColor").."+"
  617. tempMessage = string.format("%s%.2f%s", tempMessage, estProg, "%")
  618. activeNameplates[token]:SetText(tempMessage)
  619. activeNameplates[token]:Show()
  620. return true
  621. end
  622. end
  623. if activeNameplates[token] then -- If mob dies, a new nameplate is created but not shown, and this ui widget will then not exist.
  624. activeNameplates[token]:SetText("")
  625. activeNameplates[token]:Hide()
  626. end
  627. return false
  628. end
  629.  
  630. local function updateNameplateValues()
  631. for token,_ in pairs(activeNameplates) do
  632. updateNameplateValue(token)
  633. end
  634. end
  635.  
  636. local function updateNameplatePosition(token)
  637. local nameplate;
  638. if not E.private.nameplates.enable then
  639. nameplate = C_NamePlate.GetNamePlateForUnit(token)
  640.  
  641. if nameplate.UnitFrame.unitExists and activeNameplates[token] ~= nil then
  642. activeNameplates[token]:SetPoint("LEFT", nameplate.UnitFrame.name, "LEFT", nameplate.UnitFrame.name:GetWidth(), 0)
  643. else
  644. removeNameplateText(token)
  645. debugLog("Token " .. token or "nil" .. "does not seem to exist. Why are we trying to update it?")
  646. end
  647. else
  648. nameplate = nameplate or NP:GetNamePlateForUnit(token);
  649.  
  650. if activeNameplates[token] ~= nil then
  651. activeNameplates[token]:SetPoint("LEFT", nameplate.UnitFrame.HealthBar, "LEFT", nameplate.UnitFrame.HealthBar:GetWidth(), 0)
  652. else
  653. removeNameplateText(token)
  654. debugLog("Token " .. token or "nil" .. "does not seem to exist. Why are we trying to update it?")
  655. end
  656.  
  657. end
  658. end
  659.  
  660. local function shouldShowNameplateTexts()
  661. if getSetting("enabled") and getSetting("enableNameplateText") and isMythicPlus() and not isDungeonFinished() then
  662. return true
  663. end
  664. return false
  665. end
  666.  
  667. local function onAddNameplate(token)
  668. if shouldShowNameplateTexts() then
  669. createNameplateText(token)
  670. updateNameplateValue(token)
  671. updateNameplatePosition(token)
  672. end
  673. end
  674.  
  675. local function onRemoveNameplate(token)
  676. removeNameplateText(token)
  677. activeNameplates[token] = nil -- This line has been made superflous tbh.
  678. end
  679.  
  680. local function removeNameplates()
  681. for token,_ in pairs(activeNameplates) do
  682. removeNameplateText(token)
  683. end
  684. end
  685.  
  686.  
  687. local function updateNameplates()
  688. if shouldShowNameplateTexts() then
  689. for token,_ in pairs(activeNameplates) do
  690. updateNameplatePosition(token)
  691. end
  692. else
  693. removeNameplates()
  694. end
  695. end
  696.  
  697. ---
  698. --- SET UP HOOKS
  699. ---
  700.  
  701. local frame = CreateFrame("FRAME")
  702. frame:RegisterEvent("ADDON_LOADED")
  703. frame:RegisterEvent("SCENARIO_CRITERIA_UPDATE")
  704. frame:RegisterEvent("COMBAT_LOG_EVENT_UNFILTERED")
  705.  
  706.  
  707. --frame:RegisterEvent("NAME_PLATE_CREATED")
  708. frame:RegisterEvent("NAME_PLATE_UNIT_ADDED")
  709. frame:RegisterEvent("NAME_PLATE_UNIT_REMOVED")
  710.  
  711. function frame:OnEvent(event, ...)
  712. args={...}
  713. if event == "ADDON_LOADED" then
  714. onAddonLoad(args[1])
  715. elseif event == "SCENARIO_CRITERIA_UPDATE" then
  716. onCriteriaUpdate()
  717. elseif event == "COMBAT_LOG_EVENT_UNFILTERED" then
  718. onCombatLogEvent(args)
  719. elseif event == "NAME_PLATE_CREATED" then
  720. --onCreateNameplate(...)
  721. elseif event == "NAME_PLATE_UNIT_ADDED" then
  722. onAddNameplate(...)
  723. elseif event == "NAME_PLATE_UNIT_REMOVED" then
  724. onRemoveNameplate(...)
  725. else
  726. if warning("MythicPlusProgress unhandled event: " .. event) then
  727. for k,v in ipairs(args) do
  728. print(k,v)
  729. end
  730. end
  731. end
  732. end
  733.  
  734. function frame:OnUpdate(elapsed)
  735. currentPullUpdateTimer = currentPullUpdateTimer + elapsed * 1000 -- Not using milliseconds in 2016? WutFace
  736. if currentPullUpdateTimer >= getSetting("nameplateUpdateRate") then
  737. currentPullUpdateTimer = 0
  738. updateCurrentPullEstimate()
  739. updateNameplateValues()
  740. end
  741. updateNameplates()
  742. end
  743.  
  744. frame:SetScript("OnEvent", frame.OnEvent)
  745. frame:SetScript("OnUpdate", frame.OnUpdate)
  746. GameTooltip:HookScript("OnTooltipSetUnit", onNPCTooltip)
  747.  
  748. SLASH_MYTHICPLUSPROGRESS1,SLASH_MYTHICPLUSPROGRESS2,SLASH_MYTHICPLUSPROGRESS3 = "/mpp","/mypp","/mythicplusprogress"
  749.  
  750. -- TODO: Redo this entire thing that I threw together in 2 min
  751. function SlashCmdList.MYTHICPLUSPROGRESS(args, editbox)
  752. args = split(args)
  753. args[1] = string.lower(args[1] or "")
  754.  
  755. if args[1] == "toggle" then
  756. print("MythicPlusProgress toggled ".. (toggleSetting("enabled") and "ON" or "OFF"))
  757.  
  758. elseif args[1] == "reset" then
  759. currentPullFrame:SetPoint("CENTER", UIParent, 50, 50)
  760. print("MythicPlusProgress' position reset.")
  761.  
  762. elseif args[1] == "sim" then
  763. mppSimulationMode = not mppSimulationMode
  764. print("MPP simulation mode toggled ".. (mppSimulationMode and "ON" or "OFF"))
  765.  
  766. elseif args[1] == "debug" then
  767. mppDebug = not mppDebug
  768. print("MPP debug toggled ".. (mppDebug and "ON" or "OFF"))
  769.  
  770. elseif args[1] == "updatevalue" then
  771. updateValue(getNPCID(UnitGUID("target")), tonumber(args[2]), UnitName("target") .. ("(Manual)"))
  772.  
  773. elseif args[1] == "getvalue" then
  774. print(getValue(tonumber(args[2])) or "No Data")
  775.  
  776. elseif args[1] == "getvalues" then
  777. local npcData = MythicPlusProgressDB["npcData"][tonumber(args[2])]
  778. if npcData then
  779. print(tostring(npcData["name"]))
  780. foreach(npcData["values"], print)
  781. else
  782. print("No data.")
  783. end
  784.  
  785. elseif args[1] == "getsetting" then
  786. print(getSetting(args[2]))
  787.  
  788. elseif args[1] == "simmax" then
  789. simulationMax = tonumber(args[2])
  790.  
  791. elseif args[1] == "simcur" then
  792. simulationCurrent = tonumber(args[2])
  793.  
  794. elseif args[1] == "wipeall" then
  795. verifyDB(true)
  796. print("RIP Database.")
  797.  
  798. elseif args[1] == "wipesettings" then
  799. verifySettings(true)
  800.  
  801. elseif args[1] == "tooltip" then
  802. setSetting("enableTooltip", not getSetting("enableTooltip"))
  803. print("Toggled tooltips ".. (getSetting("enableTooltip") and "on" or "off"))
  804.  
  805. elseif args[1] == "currentpull" then
  806. setSetting("enablePullEstimate", not getSetting("enablePullEstimate"))
  807. print("Toggled \"current pull\" display ".. (getSetting("enablePullEstimate") and "on" or "off"))
  808.  
  809. elseif args[1] == "dbinfo" then
  810. print("Mobs recorded: " .. tlen(MythicPlusProgressDB["npcData"]))
  811.  
  812. elseif args[1] == "combatonly" then
  813. setSetting("pullEstimateCombatOnly", not getSetting("pullEstimateCombatOnly"))
  814. print("Toggled combat only \"current pull\" display ".. (getSetting("pullEstimateCombatOnly") and "on" or "off"))
  815.  
  816. elseif args[1] == "version" then
  817. print("MythicPlusProgress Version: " .. getSetting("version") .. " - " .. "DB Version: " .. getSetting("dbVersion"))
  818.  
  819. elseif args[1] == "nameplates" then
  820. print("Nameplate text overlay toggled ".. (toggleSetting("enableNameplateText") and "ON" or "OFF"))
  821.  
  822. elseif args[1] == "export" then
  823. exportData()
  824. print("Data export opened, copy it to clipboard then hit enter to close.")
  825.  
  826. else
  827. print("MythicPlusProgress commands:")
  828. print("/mpp toggle (Toggles ALL addon visibility on/off, it will still record npc data while in Mythic+)")
  829. print("/mpp reset (reset position of 'current pull' text, it likes to run away)")
  830. print("/mpp version (displays current version of addon)")
  831. print("/mpp tooltip (toggles the tooltip functionality on/off)")
  832. print("/mpp currentpull (toggles the \"current pull\" functionality on/off)")
  833. print("/mpp dbinfo (Shows how many unique mobs you've recorded)")
  834. print("/mpp combatonly (Toggles only showing the current pull estimate while you or one of your party members are in combat)")
  835. print("/mpp nameplates (Toggles nameplate text overlay on/off)")
  836. print("/mpp wipesettings (Resets all settings to default (but not mob data!))")
  837. end
  838. end
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement