local mod = klhtm local me = { } mod.net = me --[[ KTM_Net.lua This module has all the code for operating the chat channel and sending KLHTM messages on it. Note that it doesn't deal with parsing / interpreting messages received from the chat channel - this is all done in KTM_NetIn.lua. ]] me.channelname = "" me.channelnumber = 0 me.source = "" me.manualchannel = nil -- manual override me.lastmtsender = "" -- name of the player who sent the last mt me.lastmtstring = "" -- name of the last mt, as sent by the player me.lastmttime = 0 -- time you last updated MT me.mtrepeattime = 10.0 -- Special onupdate method from Core.lua me.onupdate = function() me.updatethreattoraid() me.checkversionquery() me.checkadvertise() -- check to repeat mt string if me.lastmtsender == UnitName("player") then -- check if you are still an assistant if mod.unit.isplayerofficer(UnitName("player")) == nil then me.lastmtsender = "" elseif GetTime() > me.lastmttime + me.mtrepeattime then -- resend and reset timer me.lastmttime = GetTime() -- Now, was it a clear or a name? if me.lastmtstring == "clear" then -- temporary: only send this to new versions (or else old versions might get spammed!) if mod.isnewwowversion then me.clearmastertarget() end else -- temporary: only send this to new versions (or else old versions might get spammed!) if mod.isnewwowversion then me.sendmessage("mtpoll " .. me.lastmtstring) end end end end end ------------------------------------------------------------------------------------------------ --------------------------------- -- Chat Channel Operation -- --------------------------------- -- return: number, name, method me.getchannel = function() local name local number local test -- use the current channel, if that works number, name = GetChannelName(me.channelname) if number > 0 then return number, name, me.source end -- Channel broken. look for oRA, CT_RA, manual set channels. -- First priority: manual override if me.manualchannel then number, name = GetChannelName(me.manualchannel) if number > 0 then me.channelnumber = number me.channelname = name me.source = mod.string.get("print", "network", "channel", "manual") return number, name, me.source end end -- Second priority: oRA if oRA_Core then test = oRA_Core:GetOpt("channel") if test then number, name = GetChannelName(test) if number > 0 then me.channelnumber = number me.channelname = name me.source = mod.string.get("print", "network", "channel", "ora") return number, name, me.source end end end -- Third priority: CT_RA if CT_RA_Channel then number, name = GetChannelName(CT_RA_Channel) if number > 0 then me.channelnumber = number me.channelname = name me.source = mod.string.get("print", "network", "channel", "ctra") return number, name, me.source end end -- can't find it! return 0 end --[[ mod.net.sendmessage(message) Sends a message to the KLHTM chat channel. is a string. Don't put the "KLHTM " header on it; that's what this method does. Return: true if the mod could find the right channel, false otherwise. ]] me.sendmessage = function(message) -- 1.12 override if mod.isnewwowversion then if GetNumRaidMembers() > 0 then SendAddonMessage("KLHTM", message, "RAID") elseif GetNumPartyMembers() > 0 then SendAddonMessage("KLHTM", message, "PARTY") else -- Send directly to our input handler mod.netin.messagein(UnitName("player"), message, 1) end return true end local channelnumber = me.getchannel() if channelnumber > 0 then -- add KLHTM header message = "KLHTM " .. message -- send SendChatMessage(message, "CHANNEL", nil, channelnumber) -- debug if mod.out.checktrace("info", me, "message") then mod.out.printtrace(string.format("Sent this message: %s.", message)) end return true else return false end end ------------------------------------------------------------------------------------------------ --------------------------------- -- Special Raid Commands -- --------------------------------- --[[ me.checkpermission() Returns: non-nil iff you are allowed to send special commands (raid assistant / party leader, etc) ]] me.checkpermission = function() if mod.unit.isplayerofficer(UnitName("player")) == true then return true else mod.out.print(mod.string.get("print", "network", "raidpermission")) return end end --[[ mod.net.clearmastertarget() Sends a message to clear the master target. This is called from "/ktm mastertarget", or clicking the master target button when you have no target. ]] me.clearmastertarget = function() if me.checkpermission() == nil then return end me.sendmessage("cleartarget") end --[[ mod.net.sendmastertarget() Sends a message to set the master target to your current target. The name of your current target, as you see it, is sent along with the command, in case someone is out of (targetting) range of your target. This is called from "/ktm mastertarget", or clicking the master target button when you have a target. ]] me.sendmastertarget = function() if me.checkpermission() == nil then return end if UnitName("target") == nil then mod.out.print(mod.string.get("print", "network", "needtarget")) return end me.sendmessage("target " .. UnitName("target")) end --[[ mod.net.clearraidthreat() Commands everyone in the raid to reset their threat. Called when you type "/ktm resetraid" or click the "clear threat" button on the GUI (which is not visible by default). ]] me.clearraidthreat = function() if me.checkpermission() == nil then return end me.sendmessage("clear") end -- Commands in the Boss Section me.startspellreporting = function() if me.checkpermission() == nil then return end me.sendmessage("spellstart") end me.stopspellreporting = function() if me.checkpermission() == nil then return end me.sendmessage("spellstop") end me.reportspelleffect = function(spellname, bossname, result, value1, value2) if result == "miss" then me.sendmessage(string.format("spelleffect \"%s\" \"%s\" %s", spellname, bossname, result)) else me.sendmessage(string.format("spelleffect \"%s\" \"%s\" %s %s %s", spellname, bossname, result, value1, value2)) end end me.setspellvalue = function(spellid, bossid, parameter, value) if me.checkpermission() == nil then return end me.sendmessage(string.format("spellvalue %s %s %s %s", spellid, bossid, parameter, value)) end -- syntax: --[[ if it succeeds, it will return just the value that is set. if it fails, it will return nil, then the error message. ]] me.checkspellvaluesyntax = function(allvalues) local x, spellid, bossid, parameter, value, key, message -- Check their first argument, , is valid spellid = allvalues[1] if (spellid == nil) or (mod.boss.bossattacks[spellid] == nil) then message = "The argument |cffffff00" .. tostring(spellid) .. "|r does not match any boss spell id. Valid spellids are|cffffff00" for key, value in mod.boss.bossattacks do message = message .. " " .. key end message = message .. "|r." return nil, message end -- Check their second argument, , is valid local dataset = mod.boss.bossattacks[spellid] bossid = allvalues[2] if (bossid == nil) or (dataset[bossid] == nil) then message = "The argument |cffffff00" .. tostring(bossid) .. "|r does not match any boss that uses the spell |cffffff00" .. mod.string.get("boss", "spell", spellid) .. "|r. Valid bossids are|cffffff00" for key, value in dataset do message = message .. " " .. key end message = message .. "|r." return nil, message end -- Check their third argument, , is valid dataset = dataset[bossid] parameter = allvalues[3] if (parameter == nil) or (dataset[parameter] == nil) then message = "The argument |cffffff00" .. tostring(parameter) .. "|r does not match any parameter that can be set. Valid parameters are|cffffff00" for key, valud in dataset do message = message .. " " .. key end message = message .. "|r." return nil, message end -- 4th parameter is value -- multiplier / addition: need number if (parameter == "addition") or (parameter == "multiplier") then value = tonumber(allvalues[4]) if value == nil then message = "The argument |cffffff00" .. tostring(allvalues[4]) .. "|r is not a number." return nil, message end elseif parameter == "ticks" then value = tonumber(allvalues[4]) if (value == nil) or (math.floor(value) ~= value) or (value < 1) then message = "The argument |cffffff00" .. tostring(allvalues[4]) .. "|r is not a positive integer." return nil, message end elseif parameter == "effectonmiss" then if value == "true" then value = true elseif value == "false" then value = false else message = "The argument |cffffff00" .. tostring(allvalues[4]) .. "|r is not a boolean value." return nil, message end elseif parameter == "type" then if (value ~= "physical") or (value ~= "debuff") or (value ~= "spell") then message = "The argument |cffffff00" .. tostring(allvalues[4]) .. "|r is not one of |cffffff00 physical debuff spell|r." return nil, message end end -- it worked! return value end me.versionnotify = function() if me.checkpermission() == nil then return end me.sendmessage(string.format("version %d.%d", mod.release, mod.revision)) mod.out.print(mod.string.get("print", "network", "upgradenote")) end me.versionquery = function() if me.checkpermission() == nil then return end -- clear the version table local key for key, _ in me.raidversions do table.remove(me.raidversions, key) end -- set the timeout for responses me.versionquerytimeout = GetTime() + 3 -- Notify the user mod.out.print(mod.string.get("print", "network", "versionrequest")) -- send the message me.sendmessage("versionquery") end me.setraidknockbackvalue = function(value, attack) if me.checkpermission() == nil then return end if mod.boss.isknockbackdiscoveryactive == false then mod.out.print(mod.string.get("print", "network", "knockbackinactive")) else me.sendmessage("knockbackvalue " .. value .. " " .. attack) end end me.sendknockbackevent = function(enemy, ability, value) if (mod.boss.isknockbackdiscoveryactive == true) and (enemy == mod.boss.mastertarget) then me.sendmessage("knockaway " .. math.ceil(0.5 + mod.table.getraidthreat())) me.sendmessage(string.format("knockawaytext {%s, %s, %s}", enemy, ability, value)) end end -- don't send an event if one has already been received and confirmed recently. me.sendevent = function(event) me.sendmessage("event " .. event) end -- Version Querying Stuff. Key = release number, value = array of names me.raidversions = { } me.versionquerytimeout = 0 -- 0 = inactive, > 0 = active. Return value of GetTime() me.addversionresponse = function(playername, version) local versionstring = tostring(version) -- ignore unless we are checking versions if me.versionquerytimeout > 0 then if me.raidversions[versionstring] == nil then me.raidversions[versionstring] = { } end me.raidversions[versionstring][playername] = true end end -- When we do "/ktm version query", the rest of the raid has 3 seconds to respond. me.checkversionquery = function() if me.versionquerytimeout == 0 then return end if GetTime() > me.versionquerytimeout then -- print it out and stuff me.versionquerytimeout = 0 local message local key local value local key2 local namesfound = { } for key, value in me.raidversions do message = string.format(mod.string.get("print", "network", "versionrecent"), key) for key2, _ in value do message = message .. key2 .. ", " namesfound[key2] = true end message = message .. " }." mod.out.print(message) table.remove(me.raidversions, key) end -- Now print the people who have out of date versions message = mod.string.get("print", "network", "versionold") for key, _ in mod.table.raiddata do if namesfound[key] == nil and mod.unit.isplayeringroup(key) == true then namesfound[key] = true message = message .. key .. ", " end end message = message .. " }." mod.out.print(message) -- Now print out people who are not talking to us message = mod.string.get("print", "network", "versionnone") for value = 1, 40 do key = GetRaidRosterInfo(value) if (key ~= nil) and (namesfound[key] == nil) then namesfound[key] = true message = message .. key .. ", " end end message = message .. " }." mod.out.print(message) end end me.lastthreatupdate = 0 -- value of GetTime(). When we last posted our threat to the raid me.minimumupdateinterval = 0.5 -- minimum time, in seconds, between threat updates to the raid me.idleupdateinterval = 10.0 -- how often to update when our value is not changing me.lastthreatvaluesent = -1 -- the name says it all, really --[[ me.updatethreattoraid() Posts your threat value to the KLHTM channel if necessary. You will send your threat at most once every = 0.5 seconds. If your threat value is unchanged, you will repeat it once every 10 seconds only. If you aren't in a party or raid, you won't repeat your value if it is constant. ]] me.updatethreattoraid = function() local interval local myraidthreat if mod.my.states.incombat.value == true then myraidthreat = math.floor(0.5 + mod.table.getraidthreat()) else myraidthreat = 0 end if myraidthreat == me.lastthreatvaluesent then interval = me.idleupdateinterval -- check for solo, then no updates if GetNumRaidMembers() == 0 and GetNumPartyMembers() == 0 then return end -- also don't send idle updates when afk if mod.my.states.afk.value == true then return end else interval = me.minimumupdateinterval end -- check update frequency if GetTime() < me.lastthreatupdate + interval then return -- only just sent an update. Wait a bit to send the next one. end -- OK. Send. local message = "threat " .. myraidthreat if mod.isnewwowversion then message = "t " .. myraidthreat end if me.sendmessage(message) == nil then if mod.out.checktrace("warning", me, "channel") then mod.out.printtrace("Threat was not updated because the CTRA Channel is not set up properly.") end end me.lastthreatupdate = GetTime() me.lastthreatvaluesent = myraidthreat end -------------------------------------------------------------------------------------------------- ------------------------------------------------------- -- Advertising KLHTM to people who pull aggro! -- ------------------------------------------------------- me.isadvertising = false me.lastadvert = 0 me.advertinterval = 300 -- seconds --[[ mod.net.toggleadvertise() Switch the advertising function on or off. ]] me.toggleadvertise = function() me.isadvertising = not me.isadvertising if me.isadvertising == true then mod.out.print(mod.string.get("print", "network", "advertisestart")) else mod.out.print(mod.string.get("print", "network", "advertisestop")) end end me.raidnumbers = { } -- {1, 2, 3, ..., 40} for x = 1, 40 do me.raidnumbers[x] = x end --[[ me.checkadvertise() Looks for people who have pulled aggro but don't have KLHTM, and tells them to get it. A message will be sent no more than once every = 5 minutes. Messages will be sent to non-warriors who are being targetted by a mob, so it's not a complete guarantee that they have aggro. ]] me.checkadvertise = function() if me.isadvertising == false then return end -- don't spam if GetTime() < me.lastadvert + me.advertinterval then return end -- look for someone who has aggro, is not a warrior, is not in the raid threat if GetNumRaidMembers() <= 0 then return end local target = "" local player = "" local x -- make it a random permutation of the raid, to stop bugging one person me.scrambleraid(me.raidnumbers) for y = 1, 40 do -- get x from scrambled array x = me.raidnumbers[y] -- there is a raid player target = "raid" .. x if UnitExists(target) == 1 then -- the player is targetting a mob target = target .. "target" if UnitExists(target) == 1 and UnitIsFriend("player", target) ~= 1 then -- mob has another player targetted player = target .. "target" if UnitIsFriend("player", player) == 1 then -- player is not yourself, not a warrior, is a player if (UnitIsPlayer(player) == 1) and (UnitName("player") ~= UnitName(player)) then local _, class = UnitClass(player) if class ~= "WARRIOR" then -- check they arne't using the meter if mod.table.raiddata[UnitName(player)] == nil then -- send them a message SendChatMessage(string.format(mod.string.get("print", "network", "advertisemessage"), UnitName(target)), "WHISPER", nil, UnitName(player)) me.lastadvert = GetTime() return end end end end end end end end --[[ me.scrambleraid(numbers) Slightly scrambles the array . Picks 10 random pairs and swaps them. Since this scramble method gets called each time we check, the array will be sufficiently inconstant all the time. ]] me.scrambleraid = function(numbers) local x local temp local box1 local box2 for x = 1, 10 do box1 = math.random(1, 40) box2 = math.random(1, 40) temp = numbers[box1] numbers[box1] = numbers[box2] numbers[box2] = temp end end