diff --git a/Locale/enUS.lua b/Locale/enUS.lua index 8984377..57a51ae 100644 --- a/Locale/enUS.lua +++ b/Locale/enUS.lua @@ -26,6 +26,8 @@ L["Summoning %p, please click the portal"] = true L["raidinfo"] = "Summons are available. Type %t for summons, -%t to cancel, %t to reset to requested on summon failure" L["clickersnag"] = "Need more clickers at %l, %z" L["clicknag"] = "Click the summoning portal!" +L["You are in the summon queue. Whisper me an alt name if you don't want to wait online."] = true +L["You are near the top of the summon queue. Get online now."] = true -- summon.lua L["Warlock"] = true @@ -34,6 +36,7 @@ L["requested"] = true L["Buffed"] = true L["B"] = true L["Prioritized"] = true +L["P"] = true L["Last"] = true L["L"] = true L["Normal"] = true @@ -48,6 +51,7 @@ L["summoned"] = true L["Location: %subzone, %zone"] = true L["Destination: %subzone, %zone"] = true L["Next"] = true +L["Alt:"] = true -- optionsgui.lua L["Raid summon instructions"] = true @@ -119,6 +123,23 @@ L["Use AddOn Broadcast Communications"] = true L["broadcastdesc"] = "Get and send summoning status updates. Gives you notifications of summon status changes, and helps to keep the summoning list alive through relogs. Makes the Next button more effective with multiple warlock summoners i.e. please don't turn this off." L["Version Details"] = true L["SteaSummon version %s\n\nWoW client\nversion: %v\nbuild: %b\ndate: %d\ntocversion: %t"] = true +L["Alt Support"] = true +L["Alt Character Options"] = true +L["altdesc"] = "These options allow players to log on another character while waiting for their summon to be ready." +L["Enable only when player buffed"] = true +L["altbuffeddesc"] = "Alt support only triggers the instructional whisper when the player is buffed (all players can still use alt support)" +L["Enable only when the queue spot reaches"] = true +L["qspotdesc"] = "Alt support only triggers the instructional whisper when the player is at or lower in the queue than this value" +L["Whisper alts when they reach this queue spot"] = true +L["Automatically register your characters for alt support"] = true +L["These are the characters you might be on when your summon is ready"] = true +L["qspotreadydesc"] = "Alt support only triggers ready whisper when the player reaches this spot in the queue" +L["When out of online players to summon, whisper this many extra alts"] = true +L["qspotboostreadydesc"] = "Raise players from their alts to keep summons going" +L["Whisper instructional text"] = true +L["a sentence that is whispered to instruct the player how to add an alt for their summon"] = true +L["Whisper summon ready text"] = true +L["a sentence that is whispered to tell an alt their summon is ready"] = true -- gossip.lua L["There is a newer version available."] = true diff --git a/SteaSummon.toc b/SteaSummon.toc index 2a711eb..dc31fce 100755 --- a/SteaSummon.toc +++ b/SteaSummon.toc @@ -2,9 +2,9 @@ ## Title: SteaSummon ## Notes: One button summoning, shared summoning list... ## Author: Stea -## Version: 0.57 +## Version: 0.58 ## SavedVariablesPerCharacter: SteaSummonSave, SteaDEBUG -## x-SteaSummon-Protocol-version: 0.4 +## x-SteaSummon-Protocol-version: 0.5 Libs\embeds.xml Locale\Locales.xml @@ -19,6 +19,7 @@ raid.lua summon.lua monitor.lua chat.lua +alt.lua events.lua main.lua diff --git a/alt.lua b/alt.lua new file mode 100644 index 0000000..105e016 --- /dev/null +++ b/alt.lua @@ -0,0 +1,131 @@ +local _, addonData = ... + +local summon +local util +local chat +local gossip + +local alt = { + me = "", + playersInConversation = {}, + + init = function(self) + addonData.debug:registerCategory("alt") + self.me, _ = UnitName("player") + summon = addonData.summon + util = addonData.util + chat = addonData.chat + gossip = addonData.gossip + end, + + newPlayer = function(self, player) + if player == self.me then + for _,v in pairs(SteaSummonSave.alttoons) do + db("alt", "mytoon:", v) + end + if #SteaSummonSave.alttoons > 0 then + db("alt", "adding my own toons as alts") + gossip:alts(player, SteaSummonSave.alttoons) + end + return + end + end, + + listWhisper = function(_, upTo, boost) + -- maybe some convos get triggered + local i = 1 + local boosted = 0 + + if upTo == nil then + upTo = summon.numwaiting + end + + db("alt", "checking list for offlines with alts") + while(i <= summon.numwaiting and i <= upTo) do + local rec = summon.waiting[i] + local player = summon:recPlayer(rec) + + db("alt", "player", player) + if summon:recStatus(rec) == "offline" and summon:recAltWhispered(rec) == "" and #summon:recAlts(rec) > 0 then + db("alt", "offline with alts") + for _,alt in pairs(summon:recAlts(rec)) do + db("alt", "whispering", alt) + chat:whisper(SteaSummonSave.altGetOnlineWhisper, alt) + summon:recAltWhispered(rec, summon:recAltWhispered(rec) .. alt .. ",") + gossip:altWhispered(summon:recPlayer(rec), alt) + end + boosted = boosted + 1 + if boost and boosted == boost then + break + end + end + i = i + 1 + end + end, + + listShorter = function(self) + local upTo = SteaSummonSave.qspot + if upTo == 0 or upTo == 41 then + return + end + + self:listWhisper(upTo) + end, + + listBoost = function(self) + local boost = SteaSummonSave.qboost + if boost == 0 then + return + end + + self:listWhisper(nil, boost) + end, + + whispered = function(_, player, text) + db("alt", "whispered", text, "from", player) + local wait = summon:findWaitingPlayer(player) + if not wait or summon:recStatus(wait) ~= "requested" then + -- filter out thank yous and such (because the world is asynchronous) + -- and random whispers, because the whole world is not in your raid + return + end + + -- let's see if this is a single word or a list of words + local alts = {} + local words = util:multiLineToTable(text, ",") + for _, v in pairs(words) do + local alt = strtrim(v) + if string.find(v, " ") then + -- probably indicates something other than an alt or list of alts + -- otoh, failure conditions include lmao, plz, and other drivel, let's hope those aren't real toons + db("alt", "spaces found") + return + end + table.insert(alts, alt) + end + + if #alts > 0 then + -- probably have an alt list, or some very angry people called plz and lmao + gossip:alts(player, alts) + end + end, + + askForAlts = function(_, player) + local idx = summon:findWaitingPlayerIdx(player) + + if gossip.netList[player] then + --return -- addon users can configure if they want alts + end + + if SteaSummonSave.altbuffed and #summon:recBuffs(summon.waiting[idx]) == 0 then + return + end + + if SteaSummonSave.initialQspot <= idx then + -- we need to talk + chat:whisper(SteaSummonSave.altWhisper, player) + end + end, +} + +addonData.alt = alt \ No newline at end of file diff --git a/buffs.lua b/buffs.lua index c7a0da3..463dc81 100644 --- a/buffs.lua +++ b/buffs.lua @@ -75,20 +75,33 @@ buffs = { local out = "" local spacer = "" for _,v in pairs(buffs) do - if #v then + if #v > 0 then + db("buffs", v[1]) out = out .. spacer .. v[1] .. "~" .. v[2] spacer = "&" end end + db("buffs", "buffs marshalled", out) return out end, unmarshallBuffs = function(self, marshalled) local out = {} + db("buffs", "unmarshalling", marshalled) + if not marshalled or marshalled == "" then + return out + end + local tmpOut = { strsplit("&", marshalled) } for i,v in pairs(tmpOut) do - out[i] = { strsplit("~", tmpOut[i]) } + if not (v == nil or v == "") then + db("buffs", "unmarshalling", v) + out[i] = { strsplit("~", v) } + end + + db("buffs", "buff unmarshalled", out[i][1], out[i][2]) end + return out end, } diff --git a/chat.lua b/chat.lua index 0d06309..802fda8 100644 --- a/chat.lua +++ b/chat.lua @@ -83,6 +83,8 @@ local chat = { if IsInGroup(player) or (player == me and settings:debug()) then gossip:add(player, event == "CHAT_MSG_WHISPER" ) end + elseif event == "CHAT_MSG_WHISPER" then + addonData.alt:whispered(player, msg) end end end, diff --git a/debug.lua b/debug.lua index 48fd70e..333f193 100644 --- a/debug.lua +++ b/debug.lua @@ -34,6 +34,15 @@ local debug = { if not SteaDEBUG or not SteaDEBUG.log then self:reset() end + if SteaDEBUG.loglength == nil then + SteaDEBUG.loglength = 3000 + end + + if #SteaDEBUG.log > SteaDEBUG.loglength then + for i = #SteaDEBUG.log, SteaDEBUG.loglength, -1 do + table.remove(SteaDEBUG.log) + end + end local f = CreateFrame("Frame", "DBFrame", UIParent, "AnimatedShineTemplate") f:SetPoint("CENTER") @@ -166,6 +175,7 @@ local debug = { SteaDEBUG = {} SteaDEBUG.log = {} SteaDEBUG.on = false + SteaDEBUG.loglength = 3000 end, show = function(self, show) diff --git a/events.lua b/events.lua index dc105d0..e33f3b9 100644 --- a/events.lua +++ b/events.lua @@ -85,6 +85,7 @@ function loaded(_, event, ...) addonData.chat:init() addonData.util:init() addonData.buffs:init() + addonData.alt:init() -- wait to set up debug categories until all categories are registered -- otherwise the category or its children may not be registered for chat debug messages @@ -92,6 +93,8 @@ function loaded(_, event, ...) addonData.debug:chatCat("summon.spellcast") addonData.debug:chatCat("summon.display") addonData.debug:chatCat("gossip") + addonData.debug:chatCat("raid") + addonData.debug:chatCat("alt") addonData.debug:chatCat("buffs") addonData.debug:chatCatSwitch(true) -- strictly this is unnecessary, but I want to see the output diff --git a/gossip.lua b/gossip.lua index ec0d15f..9019222 100644 --- a/gossip.lua +++ b/gossip.lua @@ -22,6 +22,9 @@ local L = LibStub("AceLocale-3.0"):GetLocale("SteaSummon") -- edone election result -- v old version -- version SteaSummon version Broadcast +-- alt alt list for player +-- wsp player alt whispered +-- n nags local DEFAULT_NETLIST_TIME = 15 @@ -51,6 +54,7 @@ local gossip = { adjLocks = 0, raidInfoTimer = nil, clickersNagTimer = nil, + inRaid = false, --------------------------------- @@ -144,7 +148,7 @@ local gossip = { return end - local msg = "netlist " .. addonData.util:tableToMultiLine(self.netList) + local msg = "netlist " .. addonData.util:tableToMultiLine(self.netList, "\n") db("gossip", ">> netlist send >> WHISPER", player) self:SendCommMessage(self.channel, msg, "WHISPER", player) end, @@ -155,8 +159,9 @@ local gossip = { return end - db("gossip", "group joined") - if self.inInit and not self.versionBad then + if self.inInit and not self.versionBad and not self.inRaid then + db("gossip", "group joined") + self.inRaid = true -- 1. On first raidJoin, request network list db("gossip", ">> netreq >>", self:groupText()) self:SendCommMessage(self.channel, "netreq ".. self.votes.. "+" @@ -167,26 +172,33 @@ local gossip = { -- make ourselves leader temporarily so we can replay messages to the leader when network established table.insert(self.netList, 1, self.me) if SteaSummonSave.raidinfotimer then - self.raidInfoTimer = addonData.monitor:create(30, self.raidInfo, false) + self.raidInfoTimer = addonData.monitor:create(SteaSummonSave.raidinfotimer * 60, self.raidInfo, false) end if SteaSummonSave.clickersnagtimer then - self.clickersNagTimer = addonData.monitor:create(30, self.clickerNag, false) + self.clickersNagTimer = addonData.monitor:create(SteaSummonSave.clickersnagtimer * 60, self.clickerNag, false) end + self:SteaSummonVersion() end - self:SteaSummonVersion() end, --------------------------------- raidInfo = function(self) self = addonData.gossip - db("gossip", "raid info") - if not self.inInit and IsInGroup(LE_PARTY_CATEGORY_HOME) and self:isLeader() and + if addonData.summon.infoSend and not self.inInit and IsInGroup(LE_PARTY_CATEGORY_HOME) and self:isLeader() and ((self.adjLocks and self.adjLocks + self.adjClicks > 2) or (self.locksCount and self.atDestCount - self.locksCount > 2)) and addonData.summon.zone ~= "" then - addonData.chat:raid(SteaSummonSave.raidinfo, self.me) + local msg = SteaSummonSave.raidinfo + local z, l = addonData.summon:getDestination() + db("gossip", "raid info") + local patterns = { + ["%%l"] = l, + ["%%z"] = z + } + msg = tstring(msg, patterns) + addonData.chat:raid(msg, self.me) end if SteaSummonSave.raidinfotimer then @@ -197,14 +209,22 @@ local gossip = { --------------------------------- clickerNag = function(self) self = addonData.gossip - db("gossip", "clicker nag") - if not self.inInit and IsInGroup(LE_PARTY_CATEGORY_HOME) and self:isLeader() and + if addonData.summon.infoSend and not self.inInit and IsInGroup(LE_PARTY_CATEGORY_HOME) and self:isLeader() and not ((self.adjLocks and self.adjLocks + self.adjClicks > 2) or (self.locksCount and self.atDestCount - self.locksCount > 2)) and addonData.summon.zone ~= "" then - addonData.chat:raid(SteaSummonSave.clickersnag, self.me) + db("gossip", "clicker nag") + local msg = SteaSummonSave.clickersnag + local z, l = addonData.summon:getDestination() + db("gossip", "raid info") + local patterns = { + ["%%l"] = l, + ["%%z"] = z + } + msg = tstring(msg, patterns) + addonData.chat:raid(msg, self.me) end if SteaSummonSave.clickersnagtimer then @@ -218,10 +238,11 @@ local gossip = { for i,v in pairs(self.netList) do if not UnitIsConnected(v) then table.insert(killList, 1, i) + db("gossip", v, "is offline without notification, removing from list pos", i) end end - for _, v in pairs(killList) do + for i, v in pairs(killList) do table.remove(self.netList, v) end end, @@ -270,6 +291,7 @@ local gossip = { self.netlistTimer:Cancel() end addonData.summon:listClear() + self.inRaid = false self.recvElections = 0 self.inInit = true wipe(self.netList) @@ -297,6 +319,22 @@ local gossip = { return self.netList[1] == self.me end, + --------------------------------- + nag = function(self, set) + if set then + set = "true" + else + set = "false" + end + if self:isLeader() then + db("gossip", ">> nag >>", self:groupText(), set) + self:SendCommMessage(self.channel, "n " .. set, self:groupText(), self.netList[1]) + else + db("gossip", ">> nag >> WHISPER", set) + self:SendCommMessage(self.channel, "n " .. set, "WHISPER", self.netList[1]) + end + end, + --------------------------------- status = function(self, player, status) self:offlineCheck() @@ -315,6 +353,9 @@ local gossip = { self.atDest[player] = nil self:updateCounts(false, player) end + if addonData.summon.needBoost then + addonData.alt:listBoost() + end end end else @@ -338,6 +379,7 @@ local gossip = { end db("gossip", ">> arrived >>", self:groupText(), player) self:SendCommMessage(self.channel, "a " .. player, self:groupText()) + addonData.alt:listShorter() end else if self:noComms() then @@ -363,10 +405,12 @@ local gossip = { local rec = addonData.summon:recMarshal(addonData.summon.waiting[idx]) db("gossip", ">> adrec >>", self:groupText(), player) self:SendCommMessage(self.channel, "adrec " .. tostring(idx) .. "_" .. rec, self:groupText()) + addonData.alt:newPlayer(player) else addonData.summon:addWaiting(player, true) self:status(player, "requested") end + addonData.alt:askForAlts(player) else if self:noComms() then return @@ -378,6 +422,47 @@ local gossip = { end end, + --------------------------------- + alts = function(self, player, playeralts) + self:offlineCheck() + local alts + + if self:isLeader() then + local rec = addonData.summon:findWaitingPlayer(player) + if rec then + local mergedAlts = addonData.summon:recMergeAlts(rec, playeralts) + for _, v in pairs(mergedAlts) do + db("gossip", v) + end + alts = addonData.summon:marshallAlts(mergedAlts) + db("gossip", alts) + + if self:noComms() then + return + end + + db("gossip", ">> alt >>", self:groupText(), player, alts) + self:SendCommMessage(self.channel, "alt " .. player .. "+" .. alts, self:groupText()) + end + else + if self:noComms() then + return + end + alts = addonData.summon:marshallAlts(playeralts) + db("gossip", ">> alts >> WHISPER", self.netList[1], player, alts) + self:SendCommMessage(self.channel, "alt " .. player .. "+" .. alts, "WHISPER", self.netList[1]) + end + end, + + --------------------------------- + altWhispered = function(self, player, alt) + local rec = addonData.summon:findWaitingPlayer(player) + + if self:isLeader() then + self:SendCommMessage(self.channel, "wsp " .. player .. "+" .. alt, self:groupText()) + end + end, + --------------------------------- destination = function(self, zone, location, noSet, player) self:offlineCheck() @@ -472,7 +557,7 @@ local gossip = { if addonData.summon:isAtDestination() then local fished = addonData.raid:fishedClickers() - for i,v in pairs(fished) do + for _,v in pairs(fished) do if self.atDest[v] == nil then if addonData.util:playerCanSummon(v) then locksCount = locksCount + 1 @@ -656,6 +741,7 @@ local gossip = { local ununmarshalledRec = addonData.summon:recUnMarshal(rec) if not addonData.summon:findWaitingPlayer(addonData.summon:recPlayer(ununmarshalledRec)) then addonData.summon:recAdd(ununmarshalledRec, tonumber(i)) + addonData.alt:newPlayer(addonData.summon:recPlayer(ununmarshalledRec)) addonData.summon:showSummons() end @@ -677,7 +763,7 @@ local gossip = { db("gossip", "<< initialize request <<") if addonData.summon.numwaiting then local data = addonData.util:marshalWaitingTable() - local dl = addonData.util:tableToMultiLine(self.atDest) + local dl = addonData.util:tableToMultiLine(self.atDest, "\n") db("gossip", ">> initialize reply >>") -- first set their destination self:destination(addonData.summon.zone, addonData.summon.location, nil, sender) @@ -687,11 +773,45 @@ local gossip = { -- next waiting list db("gossip", ">> waiting list >> WHISPER", sender, data) self:SendCommMessage(self.channel, "l " .. data, "WHISPER", sender, "BULK") + local toggle = "true" + if addonData.summon.infoSend == false then + toggle = "false" + end + db("gossip", ">> nag >> WHISPER", sender, toggle) + self:SendCommMessage(self.channel, "n " .. toggle, "WHISPER", sender) end end + --- alt list + elseif cmd == "alt" then + db("gossip", "<< alt list <<", sender) + local player, altmarshalled = strsplit("+", subcmd) + local alts = addonData.summon:unmarshallAlts(altmarshalled) + db("gossip", player, alts) + + if self:isLeader() then + self:alts(player, alts) + else + local rec = addonData.summon:findWaitingPlayer(player) + if rec then + addonData.summon:recMergeAlts(rec, alts) + end + end + + --- alt whispered + elseif cmd == "wsp" then + db("gossip", "<< alt whispered <<", sender) + local player, alt = strsplit("+", subcmd) + local rec = addonData.summon:findWaitingPlayer(player) + if rec then + addonData.summon:recAltWhispered(rec, alt) + end + --- leave netgroup (turned off comms or had a bad version) elseif cmd == "retire" then + if sender == self.me then + db("gossip", "ignoring my own retire from before reload") + end if self.inInit then return -- group messages get saved up on reload... end @@ -701,7 +821,7 @@ local gossip = { --- destination list elseif cmd == "dl" then db("gossip", "<< at destination list <<") - self.atDest = addonData.util:multiLineToMap(subcmd) + self.atDest = addonData.util:multiLineToMap(subcmd, "\n") self.atDestCount = 0 self.locksCount = 0 for i,_ in pairs(self.atDest) do @@ -715,6 +835,11 @@ local gossip = { addonData.util:unmarshalWaitingTable(subcmd) self:replayMessageLog(self.replayLog) + --- nag toggle + elseif cmd == "n" then + db("gossip", "<< nag <<", subcmd) + addonData.summon:setRaidNags(subcmd == "true") + --- destination change elseif cmd == "d" then local destination = string.gsub(subcmd, "_", " ") @@ -764,8 +889,12 @@ local gossip = { --- netgroup list elseif cmd == "netlist" then - db("gossip", "<< netlist <<") - self.netList = addonData.util:multiLineToTable(subcmd) + db("gossip", "<< netlist <<", sender) + if sender == self.me then -- on replay + db("gossip", "ignored netlist from myself") + return + end + self.netList = addonData.util:multiLineToTable(subcmd, "\n") if self.inInit then -- 2. if within 5 seconds a network list is received, request initialize from leader self.inInit = false @@ -784,7 +913,11 @@ local gossip = { --- request for netgroup list elseif cmd == "netreq" then - db("gossip", "<< netreq <<") + if sender == self.me then -- on replay + db("gossip", "ignored netreq from myself") + return + end + db("gossip", "<< netreq <<", sender) -- 4. if while waiting for network list, you receive a request for the network list, -- whisper election and add the requester to the voting list, local votes, version = strsplit("+", subcmd) @@ -848,6 +981,10 @@ local gossip = { --- SteaSummon version broadcast GUILD elseif cmd=="version" then + if sender == self.me then -- on replay + db("gossip", "ignored version request from myself") + return + end db("gossip", "<< version <<", sender) local reported_version = tonumber(subcmd) db("gossip", "my version:", self.SSversion, sender, "version:", reported_version) @@ -859,6 +996,8 @@ local gossip = { self.SSversion_notified = true cprint(L["There is a newer version available."]) end + else + db("gossip", "Unknown protocol command received:", cmd) end end } diff --git a/optionsgui.lua b/optionsgui.lua index e5a4e60..41dadfc 100644 --- a/optionsgui.lua +++ b/optionsgui.lua @@ -293,7 +293,7 @@ local summonwords = { SteaSummonSave.summonWords = addonData.util:multiLineToTable(val) end, get = function() - return addonData.util:tableToMultiLine(SteaSummonSave.summonWords) + return addonData.util:tableToMultiLine(SteaSummonSave.summonWords, "\n") end } } @@ -392,6 +392,126 @@ local priorities = { }, } +local alts = { + name = L["Alt Support"], + type = "group", + order = 6, + args = { + header = { + order = 0, + type = "header", + name = L["Alt Character Options"] + }, + desc = { + order = 1, + type = "description", + name = L["altdesc"] + }, + buffed = { + order = 2, + name = L["Enable only when player buffed"], + desc = L["altbuffeddesc"], + type = "toggle", + width = "full", + descStyle = "inline", + set = function(_, val) + SteaSummonSave.altbuffed = val + end, + get = function() + return SteaSummonSave.altbuffed + end + }, + q = { + order = 3, + name = L["Enable only when the queue spot reaches"], + desc = L["qspotdesc"], + type = "range", + width = "full", + min = 2, + max = 41, + step = 1, + set = function(_, val) + SteaSummonSave.initialQspot = val + end, + get = function() + return SteaSummonSave.initialQspot + end + }, + whisper = { + order = 4, + name = L["Whisper alts when they reach this queue spot"], + desc = L["qspotreadydesc"], + type = "range", + width = "full", + min = 0, + max = 41, + step = 1, + set = function(_, val) + SteaSummonSave.qspot = val + end, + get = function() + return SteaSummonSave.qspot + end + }, + whisperBoost = { + order = 5, + name = L["When out of online players to summon, whisper this many extra alts"], + desc = L["qspotboostreadydesc"], + type = "range", + width = "full", + min = 0, + max = 10, + step = 1, + set = function(_, val) + SteaSummonSave.qboost = val + end, + get = function() + return SteaSummonSave.qboost + end + }, + instrwhisper = { + order = 11, + name = L["Whisper instructional text"], + desc = L["a sentence that is whispered to instruct the player how to add an alt for their summon"], + type = "input", + width = "full", + set = function(_, val) + SteaSummonSave.altWhisper = val + end, + get = function() + return SteaSummonSave.altWhisper + end + }, + getonlinewhisper = { + order = 11, + name = L["Whisper summon ready text"], + desc = L["a sentence that is whispered to tell an alt their summon is ready"], + type = "input", + width = "full", + set = function(_, val) + SteaSummonSave.altGetOnlineWhisper = val + end, + get = function() + return SteaSummonSave.altGetOnlineWhisper + end + }, + toons = { + order = 7, + name = L["Automatically register your characters for alt support"], + desc = L["These are the characters you might be on when your summon is ready"], + width = "full", + type = "input", + multiline = 5, + set = function(_, val) + SteaSummonSave.alttoons = addonData.util:multiLineToTable(addonData.util:case(val, "\n")) + end, + get = function() + return addonData.util:tableToMultiLine(SteaSummonSave.alttoons) + end + }, + }, +} + local advanced = { name = L["Advanced"], type = "group", @@ -515,16 +635,21 @@ function rummage() end function optionsgui.init() - LibStub("AceConfig-3.0"):RegisterOptionsTable("SteaSummon", optionsgui.options, "ss") - LibStub("AceConfig-3.0"):RegisterOptionsTable("SteaSummonMessages", chat, "sschat") - LibStub("AceConfig-3.0"):RegisterOptionsTable("SteaSummonWords", summonwords, "sswords") - LibStub("AceConfig-3.0"):RegisterOptionsTable("SteaSummonPrios", priorities, "ssprio") + local me = UnitName("player") + local name = "SteaSummon (" .. me .. ")" + + LibStub("AceConfig-3.0"):RegisterOptionsTable(name, optionsgui.options, "ss") + LibStub("AceConfig-3.0"):RegisterOptionsTable("SteaSummonMessages", chat, "ss-chat") + LibStub("AceConfig-3.0"):RegisterOptionsTable("SteaSummonWords", summonwords, "ss-words") + LibStub("AceConfig-3.0"):RegisterOptionsTable("SteaSummonPrios", priorities, "ss-prio") + LibStub("AceConfig-3.0"):RegisterOptionsTable("SteaSummonAlts", alts, "ss-alts") LibStub("AceConfig-3.0"):RegisterOptionsTable("SteaSummonAdv", advanced) - LibStub("AceConfigDialog-3.0"):AddToBlizOptions("SteaSummon") - LibStub("AceConfigDialog-3.0"):AddToBlizOptions("SteaSummonMessages", L["Messages"], "SteaSummon") - LibStub("AceConfigDialog-3.0"):AddToBlizOptions("SteaSummonWords", L["Triggers"], "SteaSummon") - LibStub("AceConfigDialog-3.0"):AddToBlizOptions("SteaSummonPrios", L["Priorities"], "SteaSummon") - LibStub("AceConfigDialog-3.0"):AddToBlizOptions("SteaSummonAdv", L["Advanced"], "SteaSummon") + LibStub("AceConfigDialog-3.0"):AddToBlizOptions(name) + LibStub("AceConfigDialog-3.0"):AddToBlizOptions("SteaSummonMessages", L["Messages"], name) + LibStub("AceConfigDialog-3.0"):AddToBlizOptions("SteaSummonWords", L["Triggers"], name) + LibStub("AceConfigDialog-3.0"):AddToBlizOptions("SteaSummonPrios", L["Priorities"], name) + LibStub("AceConfigDialog-3.0"):AddToBlizOptions("SteaSummonAlts", L["Alt Support"], name) + LibStub("AceConfigDialog-3.0"):AddToBlizOptions("SteaSummonAdv", L["Advanced"], name) end diff --git a/raid.lua b/raid.lua index d2bfa6e..74529fe 100644 --- a/raid.lua +++ b/raid.lua @@ -1,35 +1,15 @@ -- Raid tracking local _, addonData = ... -local L = LibStub("AceLocale-3.0"):GetLocale("SteaSummon") - --- events of interest --- GROUP_ROSTER_UPDATE --- PARTY_LEADER_CHANGED --- NAME_PLATE_UNIT_REMOVED --- NAME_PLATE_UNIT_ADDED - ---GetRaidRosterInfo(1..40) ---UnitInRaid("unit") - -local old, new = {}, {} - -local dead = {} - local raid = { inzone = {}, caninvite = {}, - groupInit = true, roster = {}, rosterOld = {}, clickers = {}, - init = function(self) + init = function(_) addonData.debug:registerCategory("raid.event") - if IsInGroup(LE_PARTY_CATEGORY_HOME) then - self.groupInit = false - self:updateRaid() - end end, callback = function(self, event, ...) @@ -38,28 +18,21 @@ local raid = { self = addonData.raid if (event == "GROUP_ROSTER_UPDATE" or event == "RAID_ROSTER_UPDATE") then - addonData.raid.groupInit = false self:updateRaid() end if (event == "PARTY_LEADER_CHANGED") then - addonData.summon:postInitSetup() if IsInGroup(LE_PARTY_CATEGORY_HOME) then - addonData.raid.groupInit = false self:updateRaid() end end end, updateRaid = function(self) - if addonData.raid.groupInit then - return - end self.roster, self.rosterOld = self.rosterOld, self.roster wipe(self.roster) if not IsInGroup(LE_PARTY_CATEGORY_HOME) then - addonData.raid.groupInit = true addonData.summon:listClear() addonData.gossip:raidLeft() wipe(self.rosterOld) @@ -67,25 +40,23 @@ local raid = { end for i = 1, GetNumGroupMembers() do - local name, rank, subgroup, level, class, fileName, zone, online, isDead, role, loot = GetRaidRosterInfo(i) - --db("raid", "enum:", name, rank, subgroup, level, class, fileName, zone, online, isDead, role, loot) - if name == nil then - break - end - self.roster[name] = 1 - - if self.rosterOld[name] == nil then - db("raid", name, "joined the raid.") - local myName, _ = UnitName("player") - if myName == name then - addonData.gossip:raidJoined() + local name, rank = GetRaidRosterInfo(i) + if name ~= nil then + self.roster[name] = 1 + + if self.rosterOld[name] == nil then + db("raid", name, "joined the raid.") + local myName, _ = UnitName("player") + if myName == name then + addonData.gossip:raidJoined() + end end - end - if rank > 0 then - self.caninvite[name] = true - else - self.caninvite[name] = false + if rank > 0 then + self.caninvite[name] = true + else + self.caninvite[name] = false + end end end diff --git a/settings.lua b/settings.lua index 6d00bd7..4f9c0ee 100644 --- a/settings.lua +++ b/settings.lua @@ -72,10 +72,20 @@ settings = { SteaSummonSave.raidinfo = L["raidinfo"] SteaSummonSave.clickersnag = L["clickersnag"] SteaSummonSave.clicknag = L["clicknag"] - SteaSummonSave.clickersnagtimer = 1 - SteaSummonSave.raidinfotimer = 2 + SteaSummonSave.clickersnagtimer = 5 + SteaSummonSave.raidinfotimer = 5 SteaSummonSave.clicknagtimer = 10 end + + if SteaSummonSave.altbuffed == nil then + SteaSummonSave.altbuffed = false + SteaSummonSave.qspot = 2 + SteaSummonSave.initialQspot = 6 + SteaSummonSave.alttoons = {} + SteaSummonSave.qboost = 2 + SteaSummonSave.altWhisper = L["You are in the summon queue. Whisper me an alt name if you don't want to wait online."] + SteaSummonSave.altGetOnlineWhisper = L["You are near the top of the summon queue. Get online now."] + end end, reset = function() @@ -108,6 +118,13 @@ settings = { SteaSummonSave.clickersnagtimer = 1 SteaSummonSave.raidinfotimer = 2 SteaSummonSave.clicknagtimer = 10 + SteaSummonSave.altbuffed = false + SteaSummonSave.qspot = 2 + SteaSummonSave.initialQspot = 6 + SteaSummonSave.alttoons = {} + SteaSummonSave.qboost = 4 + SteaSummonSave.altWhisper = L["You are in the summon queue. Whisper me an alt name if you don't want to wait online."] + SteaSummonSave.altGetOnlineWhisper = L["You are near the top of the summon queue. Get online now."] end, saveOnLogout = function(self, event, ...) diff --git a/summon.lua b/summon.lua index ac94757..f95d2df 100644 --- a/summon.lua +++ b/summon.lua @@ -23,10 +23,12 @@ local summon = { ["Warlock"] = L["W"], ["Buffed"] = L["B"], ["Normal"] = L["N"], + ["Prioritized"] = L["P"], ["Last"] = L["L"] }, localLocks = 0, localClickers = 0, + needBoost = false, --------------------------------- init = function(self) @@ -47,8 +49,8 @@ local summon = { -- there seems to be no good event/time to check if we are in a group -- group roster changes fail to tell us when we are NOT in a group - -- so we're gonna bodge this one - C_Timer.After(1, self.postInitSetup) + -- so we're gonna bodge this one, worst case is it takes a while to clear the list + C_Timer.After(10, self.postInitSetup) -- bumped to 10 seconds, after laggy server experiences, an eternity I know end, --------------------------------- @@ -62,23 +64,20 @@ local summon = { end self.postInit = false - -- sanity debug - for i,v in pairs(SteaSummonSave.waiting) do - db("waitlist", i, v) - end - local ts = GetTime() if not IsInGroup(LE_PARTY_CATEGORY_HOME) or SteaSummonSave.waitingKeepTime == 0 - or ts - SteaSummonSave.timeStamp > SteaSummonSave.waitingKeepTime * 60 then + or ts - 10 - SteaSummonSave.timeStamp > SteaSummonSave.waitingKeepTime * 60 then -- -10 for the bodge factor db("wiping wait list") - db("saved mins", (ts - SteaSummonSave.timeStamp)/60, "keep mins", SteaSummonSave.waitingKeepTime) + db("saved mins", (ts - 10 - SteaSummonSave.timeStamp)/60, "keep mins", SteaSummonSave.waitingKeepTime) db("group status:", IsInGroup(LE_PARTY_CATEGORY_HOME)) self:listClear() + else + addonData.gossip:raidJoined() end - -- good time for a version check + -- good time for a version check -- this goes to guild if not in raid addonData.gossip:SteaSummonVersion() end, @@ -88,7 +87,6 @@ local summon = { self.localClickers = tonumber(clicks) if SteaSummonFrame then if IsInGroup(LE_PARTY_CATEGORY_HOME) and self.location ~= "" and self.zone ~= nil then - local color = "" if self.localLocks and self.localLocks + self.localClickers > 2 then SteaSummonFrame.status:SetTextColor(0,1,0,.5) else @@ -113,15 +111,17 @@ local summon = { if dirty ~= nil then self.dirty = dirty end + addonData.monitor:start() return self.dirty end, --------------------------------- - waitRecord = function(self, player, time, status, prioReason) + waitRecord = function(self, player, time, status, prioReason, buffs, alts, altwhispered) local rec - rec = {player, time, status, prioReason, true} + rec = {player, time, status, prioReason, true, buffs or {}, alts or {}, altwhispered or ""} db("summon.waitlist.record","Created record {", - self:recPlayer(rec), self:recTime(rec), self:recStatus(rec), self:recPrio(rec), true, "}") + self:recPlayer(rec), self:recTime(rec), self:recStatus(rec), self:recPrio(rec), true, + self:recBuffs(rec), self:recAlts(rec), self:recAltWhispered(rec), "}") return rec end, @@ -133,14 +133,17 @@ local summon = { .. "+" .. self:recStatus(rec) .. "+" .. self:recPrio(rec) .. "+" .. addonData.buffs:marshallBuffs(self:recBuffs(rec)) + .. "+" .. self:recMarshallAlts(rec) + .. "+" .. self:recAltWhispered(rec) end, --------------------------------- recUnMarshal = function(self, data) if data then - local player, time, status, prio, buffs = strsplit("+", data) - if player and time and status and prio and buffs then - return self:waitRecord(player, time, status, prio, addonData.buffs:unmarshallBuffs(buffs)) + local player, time, status, prio, buffs, alts, altwhispered = strsplit("+", data) + if player and time and status and prio and buffs and alts and altwhispered then + return self:waitRecord(player, time, status, prio, addonData.buffs:unmarshallBuffs(buffs), + self:unmarshallAlts(alts), altwhispered) else db("summon.waitlist.record", "unmarshalled data contains nil", player, time, status, prio, buffs) end @@ -217,6 +220,92 @@ local summon = { return rec[6] or {} end, + --------------------------------- + recAlts = function(self, rec, val) + if val ~= nil then + self:listDirty(true) + db("summon.waitlist.record","setting record alts value:", val) + rec[7] = {} + rec[7] = self:recMergeAlts(rec, val) -- duplicate proof add + end + return rec[7] or {} + end, + + --------------------------------- + marshallAlts = function(_, alts) + local out = "" + local spacer = "" + for _,v in pairs(alts) do + if v and v ~= "" then + out = out .. spacer .. v + spacer = "&" + end + end + return out + end, + + --------------------------------- + recMarshallAlts = function(self, rec) + return self:marshallAlts(rec[7]) + end, + + --------------------------------- + unmarshallAlts = function(_, marshalled) + local out = {} + local tmpOut = { strsplit("&", marshalled) } + for i,v in pairs(tmpOut) do + if v and v ~= "" then + db("summon.waitlist.record", "unmarshalling", v) + out[i] = v + end + end + return out + end, + + --------------------------------- + recMergeAlts = function(_, rec, alts) + db("summon.waitlist.record","merging alts:", alts) + local oldAlts = rec[7] or {} + local newAlts = {} + local altMap = {} -- for duplicates in new map + local newIdx = 1 + + for _,alt in pairs(alts) do + if not altMap[alt] then + local add = true + for _,oldAlt in pairs(oldAlts) do + if alt == oldAlt then + add = false + break + end + end + if add then + altMap[alt] = true + newAlts[newIdx] = alt + newIdx = newIdx + 1 + end + end + end + + for _,v in pairs(newAlts) do + db("summon.waitlist.record", "merging alt", v) + table.insert(oldAlts, v) + end + + rec[7] = oldAlts -- in case we made it + return rec[7] + end, + + --------------------------------- + recAltWhispered = function(self, rec, val) + if val ~= nil then + self:listDirty(true) + db("summon.waitlist.record","setting record alt whispered:", val) + rec[8] = val + end + return rec[8] + end, + --------------------------------- recRemove = function(self, player) local ret = false @@ -284,7 +373,7 @@ local summon = { -- Prio warlock if SteaSummonSave.warlocks and addonData.util:playerCanSummon(player) - and #buffs == 0 and self.localLocks <= SteaSummonSave.maxLocks then + and #buffs == 0 and self.localLocks < SteaSummonSave.maxLocks then for k, wait in pairs(self.waiting) do if self:recPrio(wait) ~= "Warlock" then db("summon.waitlist", "Warlock", player, "gets prio") @@ -303,8 +392,8 @@ local summon = { -- Prio buffs if not inserted and SteaSummonSave.buffs == true and #buffs > 0 then for k, wait in pairs(self.waiting) do - if not self:recPrio(wait) == "Warlock" - or (self:recPrio(wait) == "Buffed" and #self:recBuffs(wait) >= #buffs) then + if (self:recPrio(wait) ~= "Warlock" and self:recPrio(wait) ~= "Buffed") + or (self:recPrio(wait) == "Buffed" and #self:recBuffs(wait) < #buffs) then self:recAdd(self:waitRecord(player, 0, "requested", "Buffed"), k) db("summon.waitlist", "Buffed " .. player .. " gets prio") inserted = true @@ -355,10 +444,14 @@ local summon = { self:recAdd(self:waitRecord(player, 0, "requested", "Normal"), i) end + local rec = self:findWaitingPlayer(player) + self:recBuffs(rec, buffs) + db("summon.waitlist", player .. " added to waiting list") self:showSummons() end, + --------------------------------- timerSecondTick = function(self) --- update timers -- yea this is dumb, but time doesnt really work in wow @@ -468,7 +561,7 @@ local summon = { if SteaSummonShardIcon then SteaSummonShardIcon:Show() end end - if pos["height"] < 26 then + if pos["height"] < 28 then if SteaSummonToButton then SteaSummonToButton:Hide() end else if SteaSummonToButton then SteaSummonToButton:Show() end @@ -543,6 +636,7 @@ local summon = { end end self.infoSend = not self.infoSend + addonData.gossip:nag(self.infoSend) end --- summon to button @@ -615,6 +709,7 @@ local summon = { ------------------------------------------------------------ --- update buttons local next = false + local listActive = false for i=1, 37 do local player local summonClick @@ -712,12 +807,14 @@ local summon = { if self.waiting[i] then --- Next Button - if not next and (self:recStatus(self.waiting[i]) == "requested" - and addonData.util:playerCanSummon() or self.isCasting) then - next = true - SetMacro(38) - addonData.buttons[38].Button:SetScript("OnMouseUp", summonClick) - addonData.buttons[38].Button:Show() + if self:recStatus(self.waiting[i]) == "requested" then + if not next and (addonData.util:playerCanSummon() or self.isCasting) then + next = true + SetMacro(38) + addonData.buttons[38].Button:SetScript("OnMouseUp", summonClick) + addonData.buttons[38].Button:Show() + end + listActive = true end --- Time @@ -744,15 +841,19 @@ local summon = { end end - if not next and not self.isCasting then - -- all summons left are pending, disable the next button - addonData.buttons[38].Button:Hide() + self.needBoost = not listActive + + if not next then + if not self.isCasting then + -- all summons left are pending, disable the next button + addonData.buttons[38].Button:Hide() + end end end -- skip visual updates --- summonTo if SteaSummonToButton then - if IsInGroup(LE_PARTY_CATEGORY_HOME) then + if IsInGroup(LE_PARTY_CATEGORY_HOME) and SteaSummonFrame:GetHeight() >= 28 then SteaSummonToButton:Show() else SteaSummonToButton:Hide() @@ -1115,18 +1216,19 @@ local summon = { offline = function(self, player) local offline = not UnitIsConnected(player) - local idx = self:findWaitingPlayerIdx(player) - if idx then + local rec = self:findWaitingPlayer(player) + + if rec then local state = "" - if offline and not self:recStatus(self.waiting[idx]) == "offline" then + if offline and self:recStatus(rec) ~= "offline" then db("summon.waitlist", "setting status of " .. player .. " to offline") state = "offline" - elseif not online and self:recStatus(self.waiting[idx]) == "offline" then + elseif not offline and self:recStatus(rec) == "offline" then db("summon.waitlist", "setting status of " .. player .. " from offline to requested") state = "requested" end if state ~= "" then - self:recStatus(self.waiting[idx], state) + self:recStatus(rec, state) end end return offline @@ -1174,6 +1276,21 @@ local summon = { end end, + --------------------------------- + setRaidNags = function(self, nag) + if nag then + self.infoSend = true + if SteaSummonToButton then + SteaSummonToButton:SetNormalTexture("Interface\\Buttons\\UI-GuildButton-MOTD-Up") + end + else + self.infoSend = false + if SteaSummonToButton then + SteaSummonToButton:SetNormalTexture("Interface\\Buttons\\UI-GuildButton-MOTD-Disabled") + end + end + end, + --------------------------------- getCurrentLocation = function(self) return self.myZone, self.myLocation @@ -1189,10 +1306,6 @@ local summon = { SteaSummonFrame.destination:SetTextColor(0,1,0,.5) SteaSummonFrame.location:SetTextColor(0,1,0,.5) end - if SteaSummonToButton then - self.infoSend = true - SteaSummonToButton:SetNormalTexture("Interface\\Buttons\\UI-GuildButton-MOTD-Up") - end if oldZone ~= self.myZone or oldLocation ~= self.myLocation then -- we changed location to destination addonData.gossip:atDestination(true) @@ -1202,10 +1315,6 @@ local summon = { SteaSummonFrame.destination:SetTextColor(1,1,1,.5) SteaSummonFrame.location:SetTextColor(0,1,0,.5) end - if SteaSummonToButton then - SteaSummonToButton:SetNormalTexture("Interface\\Buttons\\UI-GuildButton-MOTD-Disabled") - self.infoSend = false - end if self.zone ~= "" and self.location ~= "" then -- destination is set if oldZone ~= self.myZone or oldLocation ~= self.myLocation then -- we changed location @@ -1246,27 +1355,22 @@ local summon = { SteaSummonFrame.destination:SetText(s) end if self:isAtDestination() then - if SteaSummonToButton then - SteaSummonToButton:SetNormalTexture("Interface\\Buttons\\UI-GuildButton-MOTD-Up") - end - self.infoSend = true addonData.gossip:atDestination(true) - else - if SteaSummonToButton then - SteaSummonToButton:SetNormalTexture("Interface\\Buttons\\UI-GuildButton-MOTD-Disabled") - end - self.infoSend = false end else if SteaSummonFrame then SteaSummonFrame.destination:SetText("") end - self.infoSend = false end end, --------------------------------- - isAtDestination= function(self) + getDestination = function(self) + return self.zone, self.location + end, + + --------------------------------- + isAtDestination = function(self) return self.zone == self.myZone and self.location == self.myLocation end, @@ -1329,7 +1433,7 @@ local summon = { self:shardIncrementBy(-1) end elseif event == "UNIT_SPELLCAST_CHANNEL_START" or - event == "UNIT_SPELLCAST_START" then + event == "UNIT_SPELLCAST_START" then if spellId == 698 then self.isCasting = true end diff --git a/util.lua b/util.lua index 1725fc9..75355dc 100644 --- a/util.lua +++ b/util.lua @@ -9,26 +9,53 @@ local util = { return (s:gsub("^%s*(.-)%s*$", "%1")) end, - tableToMultiLine = function(_, table) + tableToMultiLine = function(_, table, sep) local text = "" local word + if sep == nil then + sep = ", " + end + local ins = "" for key, val in pairs(table) do if type(val) == "string" then word = val else word = key end - text = text .. word .. "\n" + if word ~= "" then + text = text .. ins .. word + if ins == "" then + ins = sep + end + end end return text end, - multiLineToTable = function(_, text) - return { strsplit("\n", text) } + multiLineToTable = function(_, text, sep) + -- might be comma or carriage return delimited + if not sep and string.find(text, ",") then + sep = "," + else + sep = "\n" + end + local out = {strsplit(sep, text)} + + -- sanitize + local idx = 1 + for _,v in pairs(out) do + local sanitized = strtrim(v) + if sanitized ~= "" then + out[idx] = sanitized + idx = idx + 1 + end + end + + return out end, - multiLineToMap = function(self,text) - local tbl = self:multiLineToTable(text) + multiLineToMap = function(self,text, sep) + local tbl = self:multiLineToTable(text, sep) local map = {} for _,v in pairs(tbl) do @@ -92,6 +119,7 @@ local util = { addonData.summon.waiting = waiting addonData.summon.numwaiting = numwaiting addonData.summon:listDirty(true) + addonData.summon:showSummons() end, sortWaitingTableByTime = function()