WoWInterface SVN AutoReply

Compare Revisions

  • This comparison shows the changes necessary to convert path
    /
    from Rev 15 to Rev 16
    Reverse comparison

Rev 15 → Rev 16

trunk/AutoReply.toc
1,7 → 1,7
## Interface: 40300
## Author: Animor
## Title: Animor's AutoReply
## Version: 0.9.04
## Version: 1.0
## Notes: Auto reply whispers in specific combat related situations.
## OptionalDeps: Ace3, Recount, LibDataBroker-1.1
## X-Embeds: Ace3, LibDataBroker-1.1
trunk/BossFight.lua
14,6 → 14,10
----------------------------------------
-- Local variables
----------------------------------------
 
-- Debug messages
local debug = nil
 
-- Boss IDs (for encounters without INSTANCE_ENCOUNTER_ENGAGE_UNIT)
local bossIDList = {
54431, -- Echo of Baine
32,6 → 36,21
46254, -- Hogger
}
 
-- Extract encounter name from mobs names that appear in boss frame
local bossMobs = {
-- Warmaster Blackhorn
[56598] = L["Warmaster Blackhorn"], -- The Skyfire
 
-- Spine of Deathwing
[53879] = L["Spine of Deathwing"], -- Deathwing
 
-- Madness of Deathwing
[56846] = L["Madness of Deathwing"], -- Arm Tentacle
[56167] = L["Madness of Deathwing"], -- Arm Tentacle
[56471] = L["Madness of Deathwing"], -- Mutated Corruption
[56168] = L["Madness of Deathwing"], -- Wing Tentacle
}
 
--======================================
--======================================
-- BOSS FIGHT FUNCTIONS
42,7 → 61,6
-- Unit died or distroyed
----------------------------------------
function AutoReply:COMBAT_LOG_EVENT_UNFILTERED(_, _, subevent, ...)
-- self:Print(subevent)
if subevent == "UNIT_DIED" or subevent == "UNIT_DESTROYED" then
self:CheckBossKill(subevent)
end
77,39 → 95,42
-- Check if boss fight started
----------------------------------------
function AutoReply:CheckBossFightStart()
local bossAlive, targetExist = self:BossAlive()
if bossAlive then
-- Boss fight start
-- self:Print("boss fight start")
self.bossEngaged = true
self:RegisterEvent("COMBAT_LOG_EVENT_UNFILTERED")
self:RegisterEvent("CHAT_MSG_MONSTER_YELL", "CheckBossKill")
self:RegisterEvent("CHAT_MSG_MONSTER_EMOTE", "CheckBossKill")
self:RegisterEvent("RAID_BOSS_EMOTE", "CheckBossKill")
self:RegisterEvent("CHAT_MSG_MONSTER_SAY", "CheckBossKill")
 
-- self:Print("ScheduleTimer CheckBossWipe - first")
self:ScheduleTimer("CheckBossWipe", 3)
elseif (not targetExist) and UnitAffectingCombat("player") then
-- If boss encounter starts before the boss was targetted, schedule boss fight check again.
-- self:Print("ScheduleTimer CheckBossFightStart")
self:ScheduleTimer("CheckBossFightStart", 3)
if not self.bossEngaged then
local bossEngaged, targetInCombat = self:BossEngaged()
if bossEngaged then
-- Boss fight start
if debug then self:Print("boss fight start") end
self.bossEngaged = true
self:RegisterEvent("COMBAT_LOG_EVENT_UNFILTERED")
self:RegisterEvent("CHAT_MSG_MONSTER_YELL", "CheckBossKill")
self:RegisterEvent("CHAT_MSG_MONSTER_EMOTE", "CheckBossKill")
self:RegisterEvent("RAID_BOSS_EMOTE", "CheckBossKill")
self:RegisterEvent("CHAT_MSG_MONSTER_SAY", "CheckBossKill")
-- self:Print("ScheduleTimer CheckBossWipe - first")
self:ScheduleTimer("CheckBossWipe", 3)
elseif (not targetInCombat) and UnitAffectingCombat("player") then
-- If boss encounter starts before the boss was targetted or before the boss is in combat, schedule boss fight check again.
-- self:Print("ScheduleTimer CheckBossFightStart")
self:ScheduleTimer("CheckBossFightStart", 3)
end
end
end
 
----------------------------------------
-- Check for boss fight wipe
----------------------------------------
function AutoReply:CheckBossWipe(confirm)
 
function AutoReply:CheckBossWipe(confirm)
if self.bossEngaged then
if self:GroupInCombat() then
-- Still in combat, schedule check again
local allBossesDead, bossFound = self:AllBossesDead()
if self:GroupInCombat() or (bossFound and not allBossesDead) then
-- No wipe (schedule wipe check again) if:
-- 1) Still in combat.
-- 2) Encountered started and Boss appeared, but out of combat yet.
-- self:Print("ScheduleTimer CheckBossWipe")
self:ScheduleTimer("CheckBossWipe", 3)
elseif confirm then
-- Boss fight wipe
-- self:Print("boss fight: wipe")
if debug then self:Print("boss fight: wipe") end
self:ReplyAllMissedBoss("wipe")
self:HandleBossFightEnd()
else
124,9 → 145,11
-- Perform when boss fight ends (wipe/kill)
--------------------------------------------
function AutoReply:HandleBossFightEnd()
self.bossEngaged = false
self.bossID = nil
self.bossName = "Unknown"
self.bossEngaged = false
self.bossNum = nil
self.bossName = L["Unknown"]
self:CancelAllTimers()
self.checkBossKillTimer = nil
self:UnregisterEvent("COMBAT_LOG_EVENT_UNFILTERED")
self:UnregisterEvent("CHAT_MSG_MONSTER_YELL")
self:UnregisterEvent("CHAT_MSG_MONSTER_EMOTE")
137,27 → 160,43
----------------------------------------
-- Check if boss is alive
----------------------------------------
function AutoReply:BossAlive()
local bossAlive = false
local targetExist = false
 
function AutoReply:BossEngaged()
local bossEngaged = false
local targetInCombat = false
local bossNum = ""
local bossID = ""
 
-- Check for boss frame
for i = 1, MAX_BOSS_FRAMES do
if (UnitExists("boss"..i) and not UnitIsDead("boss"..i)) then
bossAlive = true
targetExist = true
self.bossID = "boss"..i
self.bossName = UnitName("boss"..i)
bossNum = "boss"..i
if UnitExists(bossNum) and not UnitIsDead(bossNum) then -- no need to check UnitAffectingCombat for boss frames (some of them may be friendly).
bossEngaged = true
if UnitAffectingCombat(bossNum) then
targetInCombat = true
end
self.bossNum = bossNum
 
-- Try to match mob name to real boss name
bossID = self:GetUnitId(bossNum)
for mobID, bossName in pairs(bossMobs) do
if mobID == bossID then
self.bossName = bossName
break
end
end
 
-- If mob name wasn't matched, use unit name.
if self.bossName == L["Unknown"] then self.bossName = UnitName(bossNum) end
break
end
end
 
-- Manual check targets, for bosses without boss frame
if not bossAlive then
bossAlive, targetExist = self:CheckTargetBoss()
if not bossEngaged then
bossEngaged, targetInCombat = self:CheckTargetBoss()
end
 
return bossAlive, targetExist
return bossEngaged, targetInCombat
end
 
----------------------------------------
167,7 → 206,6
local allBossesDead = true
local bossFound = false
local bossAlive = false
local targetExist = false
 
-- Check for boss frame
for i = 1, MAX_BOSS_FRAMES do
181,8 → 219,8
 
-- Manual check targets, for bosses without boss frame
if not bossFound then
bossAlive, targetExist, bossFound = self:CheckTargetBoss()
if bossAlive or (not bossFound) then
_, _, bossFound, bossAlive = self:CheckTargetBoss()
if bossAlive or not bossFound then
allBossesDead = false
end
end
190,21 → 228,80
return allBossesDead, bossFound
end
 
---------------------------------------------
-- Manual check targets for boss
-- Returns bossEngaged, targetInCombat, bossFound, bossAlive
---------------------------------------------
function AutoReply:CheckTargetBoss()
local targetInCombat = false
local bossFound = false
local bossAlive = false
local unitId = ((GetNumRaidMembers() == 0) and "party") or "raid"
local target = nil
local targetUnitID = nil
 
for i = 0, math.max(GetNumRaidMembers(), GetNumPartyMembers()) do
target = (i == 0 and "playertarget") or unitId..i.."target"
if UnitExists(target) and not UnitIsPlayer(target) then
if UnitAffectingCombat(target) then
targetInCombat = true
end
if UnitClassification(target) == "worldboss" then
-- worldboss
bossFound = true -- boss found (dead/alive/in combat)
if not UnitIsDead(target) then
bossAlive = true -- boss is alive
end
if UnitAffectingCombat(target) then -- boss is in combat
if self.bossName == L["Unknown"] then self.bossName = UnitName(target) end
return true, targetInCombat, bossFound, bossAlive
end
else
-- check against boss ID list
targetUnitID = self:GetUnitId(target)
-- for _, bossID in pairs(bossIDList) do
for i = 1, #bossIDList do
if targetUnitID == bossIDList[i] then
bossFound = true -- boss found (dead/alive/in combat)
if not UnitIsDead(target) then
bossAlive = true -- boss is alive
end
if UnitAffectingCombat(target) then -- boss is in combat
if self.bossName == L["Unknown"] then self.bossName = UnitName(target) end
return true, targetInCombat, bossFound, bossAlive
end
end
end
end
end
end
 
return false, targetInCombat, bossFound, bossAlive
end
 
----------------------------------------
-- Check for boss fight end
----------------------------------------
function AutoReply:CheckBossKill(eventname)
-- self:Print(eventname)
if debug then self:Print(eventname) end
if self.bossEngaged and (not UnitAffectingCombat("player")) and (self.db.profile.raidEn or self.db.profile.dungeonEn) then
-- self:Print("CheckBossKill")
if debug then self:Print("CheckBossKill") end -- debug
local allBossesDead, bossFound = self:AllBossesDead()
if allBossesDead or (self:GroupSurvived() and not bossFound) then
-- self:Print("boss fight: kill")
if debug then self:Print(allBossesDead) end -- debug
if allBossesDead or ((not bossFound) and self:GroupSurvived()) then
if debug then self:Print("boss fight: kill") end -- debug
self.numKilledBosses = self.numKilledBosses + 1
self:ReplyAllMissedBoss("kill")
self:ReplyAllMissedBoss("kill")
self:HandleBossFightEnd()
-- Reschedule kill check in case:
-- 1) Player is dead (so no PLAYER_REGEN_DISABLED will trigger when out of combat).
-- 2) Boss is down and its frame/body vanish
-- 3) Other players are still in combat for some time until the encounter ends.
elseif (not bossFound) and self:GroupInCombat() and
((not self:TimeLeft(self.checkBossKillTimer)) or self:TimeLeft(self.checkBossKillTimer) <= 0.1) then -- Do not schedule more than one at a time.
self.checkBossKillTimer = self:ScheduleTimer("CheckBossKill", 0.5)
end
end
end
end
 
--------------------------------------
214,7 → 311,7
local unitId = ((GetNumRaidMembers() == 0) and "party") or "raid"
for i = 0, math.max(GetNumRaidMembers(), GetNumPartyMembers()) do
local id = (i == 0 and "player") or unitId..i
if UnitAffectingCombat(id) and not UnitIsDeadOrGhost(id) then
if UnitAffectingCombat(id) and UnitIsVisible(id) and not UnitIsDeadOrGhost(id) then
return true
end
end
232,56 → 329,14
-- Check if player is out of combat and didn't use any feign death ability to reset the boss.
-- Check in addition if player is in 100 yard range.
if UnitIsVisible(id) and not (UnitAffectingCombat(id) or UnitIsDeadOrGhost(id) or UnitIsFeignDeath(id) or
UnitAura(id, "Vanish") or UnitAura(id, "Invisibility") or UnitAura(id, "Shadowmeld")) then
-- self:Print(UnitName(id))
UnitAura(id, "Vanish") or UnitAura(id, "Invisibility") or UnitAura(id, "Shadowmeld") or UnitAura(id, "Spirit of Redemption")) then
if debug then self:Print(GetUnitName(id)) end
return true
end
end
return false
end
 
---------------------------------------------
-- Manual check targets for boss
-- Returns bossAlive, targetExist, bossFound
---------------------------------------------
function AutoReply:CheckTargetBoss()
local targetExist = false
local bossFound = false
local unitId = ((GetNumRaidMembers() == 0) and "party") or "raid"
local target = nil
local targetUnitID = nil
 
for i = 0, math.max(GetNumRaidMembers(), GetNumPartyMembers()) do
target = (i == 0 and "playertarget") or unitId..i.."target"
if UnitExists(target) and not UnitIsPlayer(target) then
-- if UnitExists(target) and not UnitIsDead(target) and not UnitIsPlayer(target) then
targetExist = true
if UnitClassification(target) == "worldboss" then
-- worldboss
bossFound = true
if not UnitIsDead(target) then
if self.bossName == "Unknown" then self.bossName = UnitName(target) end
return true, targetExist, bossFound
end
else
-- check against boss ID list
targetUnitID = self:GetUnitId(target)
for _, unitID in pairs(bossIDList) do
if targetUnitID == unitID then
bossFound = true
if not UnitIsDead(target) then
if self.bossName == "Unknown" then self.bossName = UnitName(target) end
return true, targetExist, bossFound
end
end
end
end
end
end
 
return false, targetExist, bossFound
end
 
----------------------------------------
-- Get unit ID
----------------------------------------
trunk/Core.lua
138,8 → 138,8
local locationName = ""
local instanceType = ""
 
-- Check that message doesn't start with addons prefix.
if not (msg:find(self.msgPrefix) or msg:find("^<DBM>") or msg:find("^<Deadly Boss Mods>") or msg:find("^<BW>")) then
-- Check that message doesn't start with addons prefix.
if not (msg:find("^<.*>") or msg:find("^%[.*%]")) then
locationName, instanceType = GetInstanceInfo()
-- Open world location
if instanceType == "none" then
248,8 → 248,11
----------------------------------------
-- Recieve whisper event
----------------------------------------
function AutoReply:CHAT_MSG_WHISPER(_, msg, sender)
self:HandleWhisper(msg, sender)
function AutoReply:CHAT_MSG_WHISPER(_, msg, sender, _, _, _, status)
-- Do nothing with whispers from GM
if status ~= "GM" then
self:HandleWhisper(msg, sender)
end
end
 
----------------------------------------
262,7 → 265,7
end
 
----------------------------------------
-- Reply to all away missed whisperes
-- Reply to all away missed whisperers
----------------------------------------
function AutoReply:PLAYER_FLAGS_CHANGED()
-- Reply whispers to all missed away senders
294,22 → 297,22
msg = msg:gsub("%%TN", GetUnitName("target", true))
msg = msg:gsub("%%THP", math.floor(UnitHealth("target")/UnitHealthMax("target")*100).."%%")
else
msg = msg:gsub("%%TN", "unknown")
msg = msg:gsub("%%TN", L["Unknown"])
msg = msg:gsub("%s*%(?%%BHP%)?", "")
end
 
-- Boss
if self.bossEngaged then
msg = msg:gsub("%%BN", self.bossName)
if self.bossID then
msg = msg:gsub("%%BHP", math.floor(UnitHealth(self.bossID)/UnitHealthMax(self.bossID)*100).."%%")
if self.bossNum and (UnitName(self.bossNum) == self.bossName) then
-- Set boss health only if boss frame shows the correct boss name
msg = msg:gsub("%%BHP", math.floor(UnitHealth(self.bossNum)/UnitHealthMax(self.bossNum)*100).."%%")
else
msg = msg:gsub("%s*%(?%%BHP%)?", "")
end
end
 
msg = self.msgPrefix..msg
--self:Print(msg)
return msg
end
 
317,7 → 320,6
-- Send reply
----------------------------------------
function AutoReply:SendReply(replyMsg, target)
--self:Print(replyMsg)
if type(target) == "number" and select(7, BNGetFriendInfoByID(target)) then -- check if BN friend is online
BNSendWhisper(target, replyMsg)
elseif type(target) == "string" then
420,9 → 422,9
if AutoReplyHistory:IsShown() then
-- If already shown, then hide
AutoReplyHistory:Hide()
AutoReplyHistory.messageFrame:Clear()
else
-- Add messages from DB in history window
AutoReplyHistory.messageFrame:Clear()
-- Add messages from DB in history window
for idx, value in pairs(dbChar.msgHistory) do
if (value.sender == "_") then
AutoReplyHistory.messageFrame:AddMessage(string.rep("_", 30))
460,6 → 462,7
----------------------------------------
function AutoReply:ClearHistory()
wipe (dbChar.msgHistory)
AutoReplyHistory.messageFrame:Clear()
self:Print(L["Missed whispers history was cleared."])
end
 
631,9 → 634,9
local numEnemyRemain = tonumber((team2Remain or ""):match("(%d+)")) or 0
 
if myBGFaction == 1 then
myArenaFaction = "Gold"
myArenaFaction = L["Gold"]
else
myArenaFaction = "Green"
myArenaFaction = L["Green"]
end
 
if team2Remain:match(myArenaFaction) then
701,7 → 704,7
local _, id, _, _, instanceName, _, _, _, _, completedEncounters = GetLFGProposal()
self.numKilledBosses = completedEncounters
if id == 416 or id == 417 then -- Siege of Wyrmrest Temple or Fall of Deathwing
self.locationName = "Dragon Soul"
self.locationName = L["Dragon Soul"] -- preset self.locationName in order to maintain self.numKilledBosses when changing zone.
else
self.locationName = instanceName
end
715,7 → 718,7
 
----------------------------------------
-- Out of combat event:
-- 1) Reply to all missed whisperes
-- 1) Reply to all missed whisperers
-- 2) Check for boss kill
----------------------------------------
function AutoReply:PLAYER_REGEN_ENABLED()
729,22 → 732,27
----------------------------------------
-- Zone Change event:
-- 1) Reset numKilledBosses on new instance
-- 2) Reply to all missed whisperes
-- 2) Reply to all missed whisperers
----------------------------------------
function AutoReply:ZONE_CHANGED_NEW_AREA()
 
-- If new instance, reset numKilledBosses
if db.raidEn or db.dungeonEn then
 
-- If new instance, reset numKilledBosses
local locationName, instanceType = GetInstanceInfo()
if instanceType ~= "none" and self.locationName ~= locationName then
self.numKilledBosses = 0
end
end
 
-- Reply whispers to all missed senders
if not self:CheckPlayerState ("", "") and not UnitIsDeadOrGhost("player") then
self:ReplyAllMissed()
end
-- Reply whispers to all missed senders
if not self:CheckPlayerState ("", "") and not UnitIsDeadOrGhost("player") then
self:ReplyAllMissed()
end
 
-- Clear PvP status for next time
myBGFaction = nil
myBGName = ""
BGWinner = nil
end
 
----------------------------------------
794,7 → 802,7
 
-- Send to missed senders list
for sender in pairs(self.missedSenders) do
if type(sender) == "number" or IsInInstance() or not sender:find("-") then -- Do not reply cross-realm whisperes when out of instance
if type(sender) == "number" or IsInInstance() or not sender:find("-") then -- Do not reply cross-realm whisperers when out of instance
self:SendReply(replyMsg, sender)
end
end
932,9 → 940,10
 
-- Init boss fight trakcing
self.bossEngaged = false
self.bossName = "Unknown"
self.bossID = nil
self.bossName = L["Unknown"]
self.bossNum = nil
self.locationName = ""
self.checkBossKillTimer = nil
 
-- Init PvP status for reply message
myBGFaction = nil
968,6 → 977,5
self:UnregisterEvent("UPDATE_BATTLEFIELD_STATUS")
self:UnregisterEvent("PLAYER_ALIVE")
self:UnregisterEvent("PLAYER_UNGHOST")
self:HandleBossFightEnd() -- unregister boss fight events
self:CancelAllTimers()
self:HandleBossFightEnd() -- unregister boss fight events, cancel all timers
end
trunk/Options.lua
83,8 → 83,8
replyWhisperersEn = {
order = 3,
type = "toggle",
name = L["Notify missed whisperes when available"],
desc = L["Notify missed whisperes when becoming available (except for AFK)."],
name = L["Notify missed whisperers when available"],
desc = L["Notify missed whisperers when becoming available (except for AFK)."],
width = "double",
disabled = function(info) return not AutoReply.db.profile.globalEn end,
},
136,8 → 136,8
replyAwayWhisperersEn = {
order = 7.4,
type = "toggle",
name = L["Notify missed whisperes when no longer AFK"],
desc = L["Notify missed whisperes when no longer AFK"],
name = L["Notify missed whisperers when no longer AFK"],
desc = L["Notify missed whisperers when no longer AFK"],
width = "double",
disabled = function(info) return not (AutoReply.db.profile.awayHistoryEn and AutoReply.db.profile.globalEn) end,
},
trunk/HistoryFrame.lua
8,7 → 8,8
----------------------------------------
-- Libs
----------------------------------------
local L = LibStub("AceLocale-3.0"):GetLocale("AutoReply")
local AutoReply = LibStub("AceAddon-3.0"):GetAddon("AutoReply")
local L = LibStub("AceLocale-3.0"):GetLocale("AutoReply")
 
----------------------------------------
-- Backdrop templates
105,6 → 106,44
frame.closeButton = closeButton
 
----------------------------------------
-- Clear history button
----------------------------------------
local clearButton = CreateFrame("Button", nil, frame, "UIPanelButtonTemplate")
 
clearButton.buttonUpTexture = clearButton:CreateTexture(nil, nil, "UIPanelButtonUpTexture")
clearButton.buttonUpTexture:SetAlpha(0.7)
clearButton:SetNormalTexture(clearButton.buttonUpTexture)
 
clearButton.buttonDownTexture = clearButton:CreateTexture(nil, nil, "UIPanelButtonDownTexture")
clearButton.buttonDownTexture:SetAlpha(0.7)
clearButton:SetPushedTexture(clearButton.buttonDownTexture)
 
clearButton:SetNormalFontObject("GameFontDisable")
-- clearButton:SetHighlightFontObject("GameFontHighlight")
 
clearButton:SetPoint("BOTTOMLEFT", 15, 15)
clearButton:SetHeight(22)
clearButton:SetWidth(95)
clearButton:SetText(L["Clear History"])
clearButton:SetScript("OnClick", function(self)
StaticPopupDialogs["AutoReply clear history"] = {
text = L["Are you sure you want to clear the history?"],
button1 = ACCEPT,
button2 = CANCEL,
timeout = 0,
whileDead = true,
hideOnEscape= true,
OnAccept = function()
AutoReply:ClearHistory()
end,
}
AutoReplyHistoryDialog = StaticPopup_Show("AutoReply clear history")
AutoReplyHistoryDialog:SetFrameStrata("TOOLTIP")
end)
 
frame.clearButton = clearButton
 
----------------------------------------
-- Inner frame
----------------------------------------
local innerFRame = CreateFrame("Frame", nil, frame)
trunk/Locales/enUS.lua
25,8 → 25,8
L["Auto reply same whisperer only once per busy session"] = true
L["Also to party/raid members"] = true
L["Auto reply also to party/raid members"] = true
L["Notify missed whisperes when available"] = true
L["Notify missed whisperes when becoming available (except for AFK)."]= true
L["Notify missed whisperers when available"] = true
L["Notify missed whisperers when becoming available (except for AFK)."]= true
L["Hide minimap icon"] = true
L["Hide auto reply from chat"] = true
L["Hide auto reply from the player's chat frame"] = true
35,7 → 35,7
L["AFK"] = true
L["Save missed whispers also when AFK"] = true
L["Save missed whispers also when away/AFK"] = true
L["Notify missed whisperes when no longer AFK"] = true
L["Notify missed whisperers when no longer AFK"] = true
L[" is no longer AFK."] = true
 
-- Death
82,6 → 82,11
L["Uncheck this if you run Boss mods and want to avoid double auto replies"]= true
L["Reply message during raid boss fights:"] = true
L["%PN (%PHP) is busy raiding %LOC, now fighting boss %BN (%BHP)."]= true
L["Unknown"] = true
L["Dragon Soul"] = true -- raid name
L["Warmaster Blackhorn"] = true
L["Spine of Deathwing"] = true
L["Madness of Deathwing"] = true
 
-- 5men Dungeons
L["Dungeons"] = true
129,6 → 134,8
L["Status: on preparation"] = true
L["Status: team: %d/%d, enemy: %d/%d"] = true
L["%s%s %s the Arena match!"] = true
L["Gold"] = true -- arend team color
L["Green"] = true -- arena team color
 
-- Done messages
L[" has been defeated by "] = true