Guest User

BGSpy

a guest
Oct 29th, 2016
33
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. -- List of talent spec displays
  2. local displays = {
  3. ["DEATHKNIGHT"] = {
  4. ["Blood"] = "Blood DK",
  5. ["Frost"] = "Frost DK",
  6. ["Unholy"] = "Unholy DK",
  7. },
  8. ["DRUID"] = {
  9. ["Balance"] = "Moonkin",
  10. ["Feral"] = "Cat Druid",
  11. ["Guardian"] = "Bear Druid",
  12. ["Restoration"] = "Resto Druid",
  13. },
  14. ["HUNTER"] = {
  15. ["Beast Mastery"] = "BM Hunter",
  16. ["Marksmanship"] = "MM Hunter",
  17. ["Survival"] = "Surv Hunter",
  18. },
  19. ["MAGE"] = {
  20. ["Arcane"] = "Arcane Mage",
  21. ["Fire"] = "Fire Mage",
  22. ["Frost"] = "Frost Mage",
  23. },
  24. ["MONK"] = {
  25. ["Brewmaster"] = "Brew Monk",
  26. ["Mistweaver"] = "Mist Monk",
  27. ["Windwalker"] = "Wind Monk",
  28. },
  29. ["PALADIN"] = {
  30. ["Holy"] = "Holy Pally",
  31. ["Protection"] = "Prot Pally",
  32. ["Retribution"] = "Ret Pally",
  33. },
  34. ["PRIEST"] = {
  35. ["Discipline"] = "Disc Priest",
  36. ["Holy"] = "Holy Priest",
  37. ["Shadow"] = "Shadow Priest",
  38. },
  39. ["ROGUE"] = {
  40. ["Assassination"] = "Mute Rogue",
  41. ["Combat"] = "Combat Rogue",
  42. ["Subtlety"] = "Sub Rogue",
  43. },
  44. ["SHAMAN"] = {
  45. ["Elemental"] = "Ele Shaman",
  46. ["Enhancement"] = "Enhance Shaman",
  47. ["Restoration"] = "Resto Shaman",
  48. },
  49. ["WARLOCK"] = {
  50. ["Affliction"] = "Aff Lock",
  51. ["Demonology"] = "Demo Lock",
  52. ["Destruction"] = "Destro Lock",
  53. },
  54. ["WARRIOR"] = {
  55. ["Arms"] = "Arms Warr",
  56. ["Fury"] = "Fury Warr",
  57. ["Protection"] = "Prot Warr",
  58. },
  59. ["AMBIGUOUS"] = {
  60. ["Frost"] = "Frost Something",
  61. ["Holy"] = "Holy Something",
  62. ["Protection"] = "Prot Something",
  63. ["Restoration"] = "Resto Something",
  64. },
  65. }
  66.  
  67. -- List of talent spec roles
  68. local roles = {
  69. ["DEATHKNIGHT"] = {
  70. ["Blood"] = "Tank",
  71. ["Frost"] = "Melee",
  72. ["Unholy"] = "Melee",
  73. },
  74. ["DRUID"] = {
  75. ["Balance"] = "Knockback",
  76. ["Feral"] = "Melee",
  77. ["Guardian"] = "Tank",
  78. ["Restoration"] = "Healer",
  79. },
  80. ["HUNTER"] = {
  81. ["Beast Mastery"] = "Knockback",
  82. ["Marksmanship"] = "Ranged",
  83. ["Survival"] = "Ranged",
  84. },
  85. ["MAGE"] = {
  86. ["Arcane"] = "Knockback",
  87. ["Fire"] = "Ranged",
  88. ["Frost"] = "Ranged",
  89. },
  90. ["MONK"] = {
  91. ["Brewmaster"] = "Tank",
  92. ["Mistweaver"] = "Healer",
  93. ["Windwalker"] = "Melee",
  94. },
  95. ["PALADIN"] = {
  96. ["Holy"] = "Healer",
  97. ["Protection"] = "Tank",
  98. ["Retribution"] = "Melee",
  99. },
  100. ["PRIEST"] = {
  101. ["Discipline"] = "Healer",
  102. ["Holy"] = "Healer",
  103. ["Shadow"] = "Ranged",
  104. },
  105. ["ROGUE"] = {
  106. ["Assassination"] = "Melee",
  107. ["Combat"] = "Melee",
  108. ["Subtlety"] = "Melee",
  109. },
  110. ["SHAMAN"] = {
  111. ["Elemental"] = "Knockback",
  112. ["Enhancement"] = "Melee",
  113. ["Restoration"] = "Healer",
  114. },
  115. ["WARLOCK"] = {
  116. ["Affliction"] = "Knockback",
  117. ["Demonology"] = "Knockback",
  118. ["Destruction"] = "Knockback",
  119. },
  120. ["WARRIOR"] = {
  121. ["Arms"] = "Melee",
  122. ["Fury"] = "Melee",
  123. ["Protection"] = "Tank",
  124. },
  125. ["AMBIGUOUS"] = {
  126. ["Frost"] = "Ranged", -- Ranged Frost DKs?
  127. ["Holy"] = "Healer",
  128. ["Protection"] = "Tank",
  129. ["Restoration"] = "Healer",
  130. },
  131. }
  132.  
  133. -- List of talent spec classes
  134. local classes = {
  135. ["Blood"] = "DEATHKNIGHT",
  136. ["Unholy"] = "DEATHKNIGHT",
  137. ["Balance"] = "DRUID",
  138. ["Feral"] = "DRUID",
  139. ["Guardian"] = "DRUID",
  140. ["Beast Mastery"] = "HUNTER",
  141. ["Marksmanship"] = "HUNTER",
  142. ["Survival"] = "HUNTER",
  143. ["Arcane"] = "MAGE",
  144. ["Fire"] = "MAGE",
  145. ["Brewmaster"] = "MONK",
  146. ["Mistweaver"] = "MONK",
  147. ["Windwalker"] = "MONK",
  148. ["Retribution"] = "PALADIN",
  149. ["Discipline"] = "PRIEST",
  150. ["Shadow"] = "PRIEST",
  151. ["Assassination"] = "ROGUE",
  152. ["Combat"] = "ROGUE",
  153. ["Subtlety"] = "ROGUE",
  154. ["Elemental"] = "SHAMAN",
  155. ["Enhancement"] = "SHAMAN",
  156. ["Affliction"] = "WARLOCK",
  157. ["Demonology"] = "WARLOCK",
  158. ["Destruction"] = "WARLOCK",
  159. ["Arms"] = "WARRIOR",
  160. ["Fury"] = "WARRIOR",
  161. ["Frost"] = "AMBIGUOUS",
  162. ["Holy"] = "AMBIGUOUS",
  163. ["Protection"] = "AMBIGUOUS",
  164. ["Restoration"] = "AMBIGUOUS",
  165. }
  166.  
  167. -- List of class colours
  168. local colours = {
  169. ["DEATHKNIGHT"] = "|cFFC41F3B",
  170. ["DRUID"] = "|cFFFF7D0A",
  171. ["HUNTER"] = "|cFFABD473",
  172. ["MAGE"] = "|cFF69CCF0",
  173. ["MONK"] = "|cFF00FF96",
  174. ["PALADIN"] = "|cFFF58CBA",
  175. ["PRIEST"] = "|cFFFFFFFF",
  176. ["ROGUE"] = "|cFFFFF569",
  177. ["SHAMAN"] = "|cFF0070DE",
  178. ["WARLOCK"] = "|cFF9482C9",
  179. ["WARRIOR"] = "|cFFC79C6E",
  180. ["AMBIGUOUS"] = "|cFF7F7F7F",
  181. }
  182.  
  183. local function charcount(s, c)
  184. local _, n = gsub(s, c, c)
  185. return n
  186. end
  187.  
  188. local function charselect(s, i)
  189. -- Returns false on no character
  190. local c = strsub(s, i, i)
  191. return c ~= "" and c
  192. end
  193.  
  194. local realm = GetRealmName()
  195. local function ParseName(identifier)
  196. -- Pattern assumes identifier is formatted as "name" or "name-server"
  197. -- Name is composed of every character except for '-'
  198. -- Server is any characters after an optional '-' character
  199. -- When no '-' character is present, the player is from the local realm
  200. local name, server = strmatch(identifier, "^([^%-]+)-?(.*)")
  201. return name, server == "" and realm or server
  202. end
  203.  
  204. local function DisplayMessage(report, message, ...)
  205. if report then
  206. SendChatMessage(gsub(message, "%%s", ""), "INSTANCE_CHAT")
  207. else
  208. print(format(message, ...))
  209. end
  210. end
  211.  
  212. local function BuildList(side)
  213. -- Show scores for players of both factions
  214. SetBattlefieldScoreFaction()
  215.  
  216. -- Build list of players
  217. local players, specless = {}, 0
  218. for i = 1, GetNumBattlefieldScores() do
  219. local identifier, _, _, _, _, faction, race, _, classToken, _, _, _, _, _, _, talentSpec = GetBattlefieldScore(i)
  220. if faction == side then
  221. if talentSpec then
  222. local name, server = ParseName(identifier)
  223. local player = {
  224. ["name"] = name,
  225. ["server"] = server,
  226. ["spec"] = talentSpec,
  227. }
  228. -- GetBattlefieldScore occasionally returns nil for race, class, and classToken
  229. -- Assume all three are absent together
  230. if race then
  231. player["race"] = race
  232. player["class"] = classToken
  233. player["colour"] = colours[classToken]
  234. player["display"] = displays[classToken][talentSpec]
  235. player["role"] = roles[classToken][talentSpec]
  236. else
  237. player["race"] = "Unknown"
  238. player["class"] = classes[talentSpec]
  239. player["colour"] = colours[classes[talentSpec]]
  240. player["display"] = displays[classes[talentSpec]][talentSpec]
  241. player["role"] = roles[classes[talentSpec]][talentSpec]
  242. end
  243. tinsert(players, player)
  244. else
  245. specless = specless+1
  246. end
  247. end
  248. end
  249.  
  250. -- Check for any talented players on the team
  251. local n = #players
  252. if n == 0 then
  253. print(format("%s have 0 players with talents and %d players with no talents.", side == 0 and "Horde" or "Alliance", specless))
  254. return
  255. end
  256.  
  257. -- Sort by class then spec then name
  258. sort(players, function(a, b) return a.class == b.class and ( a.spec == b.spec and a.name < b.name or a.spec < b.spec ) or a.class < b.class end)
  259.  
  260. return players, n, specless
  261. end
  262.  
  263. local function DisplaySummary(report)
  264. DisplayMessage(report, "BG Spy: Team Summary")
  265. for side = 0, 1 do
  266. local players, n, specless = BuildList(side)
  267. if not players then
  268. return
  269. end
  270.  
  271. -- Role totals
  272. local total = {
  273. Tank = 0,
  274. Healer = 0,
  275. Melee = 0,
  276. Ranged = 0,
  277. Knockback = 0,
  278. }
  279.  
  280. -- Calculate the role totals
  281. for i = 1, n do
  282. total[players[i].role] = total[players[i].role]+1
  283. end
  284.  
  285. -- Display role totals
  286. DisplayMessage(report, format("%s: %d melee, %d ranged, %d healer%s.%s", side == 0 and "Horde" or "Alliance", total.Tank+total.Melee, total.Ranged+total.Knockback, total.Healer, total.Healer == 1 and "" or "s", specless > 0 and format(" (%d with no talents)", specless) or ""))
  287. end
  288. end
  289.  
  290. local function DisplaySpecs(side, report)
  291. local players, n, specless = BuildList(side)
  292. if not players then
  293. return
  294. end
  295.  
  296. -- Role totals
  297. local total = {
  298. Tank = 0,
  299. Healer = 0,
  300. Melee = 0,
  301. Ranged = 0,
  302. Knockback = 0,
  303. }
  304.  
  305. -- Display list
  306. local x, last, names = 1, players[1].display, players[1].name
  307. total[players[1].role] = 1
  308. local function out(i)
  309. DisplayMessage(report, format("%dx %%s%s (%s)", x, last, names), players[i].colour)
  310. end
  311.  
  312. for i = 2, n do
  313. local current = players[i].display
  314. if current ~= last then
  315. out(i-1)
  316. x = 1
  317. last = current
  318. names = players[i].name
  319. else
  320. x = x+1
  321. names = format("%s, %s", names, players[i].name)
  322. end
  323. total[players[i].role] = total[players[i].role]+1
  324. end
  325. out(n)
  326.  
  327. -- Display role totals
  328. DisplayMessage(report, format("%s: %d melee, %d ranged, %d healer%s.%s", side == 0 and "Horde" or "Alliance", total.Tank+total.Melee, total.Ranged+total.Knockback, total.Healer, total.Healer == 1 and "" or "s", specless > 0 and format(" (%d with no talents)", specless) or ""))
  329. end
  330.  
  331. -- Role listing tables
  332. local member = {
  333. t = function(role)
  334. return role == "Tank"
  335. end,
  336. h = function(role)
  337. return role == "Healer"
  338. end,
  339. d = function(role)
  340. return role == "Melee" or role == "Ranged" or role == "Knockback"
  341. end,
  342. k = function(role)
  343. return role == "Knockback"
  344. end,
  345. }
  346. local listname = {
  347. t = "tank",
  348. h = "healer",
  349. d = "DPS",
  350. k = "knockback",
  351. }
  352.  
  353. local function DisplayRole(side, report, list)
  354. local players, n = BuildList(side)
  355. if not players then
  356. return
  357. end
  358.  
  359. -- Display list
  360. local total = 0
  361. for i = 1, n do
  362. if member[list](players[i].role) then
  363. DisplayMessage(report, format("%s - %s %%s%s", players[i].name, players[i].race, players[i].display), players[i].colour)
  364. total = total+1
  365. end
  366. end
  367. DisplayMessage(report, format("%s have %d %s%s.", side == 0 and "Horde" or "Alliance", total, listname[list], list ~= "d" and total ~= 1 and "s" or ""))
  368. end
  369.  
  370. local function printHelp()
  371. print("BG Spy 1.2.6 commands:")
  372. print("/bgs - Include any additional commands.")
  373. print("help - Display this help.")
  374. print("r - Report list to Battleground chat.")
  375. print("f - List players of friendly team.")
  376. print("s - List talent spec numbers on team.")
  377. print("t - List tanks on team.")
  378. print("h - List healers on team.")
  379. print("d - List DPS on team.")
  380. print("k - List players possible of knockback effects on team.")
  381. print("Example - /bgs rfh - Report friendly healers.")
  382. end
  383.  
  384. local function printCommand(nr, nf, list)
  385. local r = nr == 1 and "Report" or "List"
  386. local f = nf == 1 and "friendly" or "enemy"
  387. if list == "s" then
  388. print(format("BG Spy: %s %s talent specs.", r, f))
  389. elseif list then
  390. local listType = listname[list] .. (list ~= "d" and "s" or "")
  391. print(format("BG Spy: %s %s %s.", r, f, listType))
  392. else
  393. print(format("BG Spy: %s team summaries.", r))
  394. end
  395. end
  396.  
  397. local inBattleground
  398. local scoresInvalid
  399. local reportOnUpdate
  400. local nextReport = {}
  401.  
  402. local function DisplayReport()
  403. local list = nextReport.list
  404.  
  405. -- Set the arguments for the list functions
  406. local side = nextReport.nf == GetBattlefieldArenaFaction() and 1 or 0
  407. local report = nextReport.nr == 1
  408.  
  409. -- Run a list function
  410. if list == "s" then
  411. DisplaySpecs(side, report)
  412. elseif list then
  413. DisplayRole(side, report, list)
  414. else
  415. DisplaySummary(report)
  416. end
  417. end
  418.  
  419. SLASH_BGSPY1, SLASH_BGSPY2 = "/bgspy", "/bgs"
  420. SlashCmdList.BGSPY = function(msg)
  421.  
  422. -- Normalize case
  423. msg = strlower(msg)
  424.  
  425. -- Check for help
  426. if msg == "help" then
  427. printHelp()
  428. return
  429. end
  430.  
  431. -- Check for any invalid characters
  432. if not strfind(msg, "[^rfsthdk ]") then
  433.  
  434. -- Check for invalid combinations of list types
  435. local ilist = strfind(msg, "[sthdk]") or 0
  436. if not strfind(msg, "[sthdk]", ilist+1) then
  437.  
  438. -- Check for repetitions of control characters
  439. local nf = charcount(msg, "f")
  440. local nr = charcount(msg, "r")
  441. if nf <= 1 and nr <= 1 then
  442.  
  443. -- Get the list character
  444. local list = charselect(msg, ilist)
  445.  
  446. -- Set next report if in a Battlegound
  447. if inBattleground then
  448. nextReport.nr = nr
  449. nextReport.nf = nf
  450. nextReport.list = list
  451.  
  452. if scoresInvalid > GetTime() or GetBattlefieldWinner() then
  453. DisplayReport()
  454. else
  455. reportOnUpdate = true
  456. RequestBattlefieldScoreData()
  457. end
  458.  
  459. -- Print command explanation if not in a Battlegound
  460. else
  461. printCommand(nr, nf, list)
  462. end
  463.  
  464. return
  465. end
  466. end
  467. end
  468.  
  469. print("BG Spy: Invalid command. Type \'/bgs help\' to list possible options.")
  470. end
  471.  
  472. local events = {}
  473.  
  474. function events:PLAYER_ENTERING_WORLD()
  475. inBattleground = false
  476. end
  477.  
  478. function events:PLAYER_ENTERING_BATTLEGROUND()
  479. reportOnUpdate = false
  480. scoresInvalid = GetTime()
  481. inBattleground = true
  482. end
  483.  
  484. function events:UPDATE_BATTLEFIELD_SCORE()
  485. if GetNumBattlefieldScores() > 0 then
  486. scoresInvalid = GetTime()+5
  487. end
  488.  
  489. if reportOnUpdate then
  490. reportOnUpdate = false
  491.  
  492. DisplayReport()
  493. end
  494. end
  495.  
  496. local frame = CreateFrame("frame")
  497.  
  498. frame:SetScript("OnEvent", function(self, event, ...)
  499. events[event](self, ...)
  500. end)
  501. for k in pairs(events) do
  502. frame:RegisterEvent(k)
  503. end
RAW Paste Data