local mod = klhtm local me = { } mod.my = me --[[ KTM_My.lua This file stores data that specifically relates to the current player, and methods that change these data sets. Most of the data sets are updated by polling, that is frequently recalculating their values. me.states - a set of flags, e.g. "player in combat" / "afk" / "feigning death" me.spellranks - list of ranks the player has for the important threat causing spells / abilities me.mods - currently active modifiers to threat / abilities from talents, gear, buffs, etc me.globalthreat - buffs that affect all threat the player generates ]] ---------------------------------------------------------------------------------------- me.feigndeathresisttime = 0 -- return value of GetTime() me.lastfadevalue = 0 -- mod.my.class is the unlocalised lower case representation. e.g. "warrior", "rogue", no matter what locale you are in. _, me.class = UnitClass("player") me.class = string.lower(me.class) ----------------------------------------- -- Special Methods from Core.lua -- ----------------------------------------- me.parserset = { } -- onupdate me.onload = function() -- make our parser local parserdata for _, parserdata in me.parserconstructor do mod.regex.addparsestring(me.parserset, parserdata[1], parserdata[2], parserdata[3]) end end me.parserconstructor = { {"buffstart", "AURAADDEDSELFHELPFUL", "CHAT_MSG_SPELL_PERIODIC_SELF_BUFFS"}, -- "You gain %s." {"debuffstart", "AURAADDEDSELFHARMFUL", "CHAT_MSG_SPELL_PERIODIC_SELF_DAMAGE"}, -- "You are afflicated by %s." {"weaponbuff", "ITEMENCHANTMENTADDSELFSELF", "CHAT_MSG_SPELL_ITEM_ENCHANTMENTS"}, -- "You cast %s on your %s." {"buffend", "AURAREMOVEDSELF", "CHAT_MSG_SPELL_AURA_GONE_SELF"}, -- "%s fades from you." } -- The "UI_ERROR_MESSAGE" event is used to detect Feign Death resists. me.myevents = { "UI_ERROR_MESSAGE", "CHAT_MSG_SPELL_PERIODIC_SELF_DAMAGE", "CHAT_MSG_SPELL_AURA_GONE_SELF", "CHAT_MSG_SPELL_ITEM_ENCHANTMENTS", "CHAT_MSG_SPELL_PERIODIC_SELF_BUFFS"} me.onevent = function() local ability -- 1) Check for Feign Death resisted if event == "UI_ERROR_MESSAGE" then if arg1 == ERR_FEIGN_DEATH_RESISTED then if mod.out.checktrace("info", me, "feigndeath") then mod.out.printtrace("Feign Death Resist message intercepted.") end me.feigndeathresisttime = GetTime() end return end -- 2) Check for various buffs local output = mod.regex.parse(me.parserset, arg1, event) if output.hit == nil then return end me.parserstagetwo[output.parser.identifier](output.final[1], output.final[2], output.final[3], output.final[4]) end --[[ This is the continuation of the combat log parsing from me.onevent above. From that method, we know the identifier of the event that has been parsed, and all the arguments that go with it. We put all the identifiers into a table whose value is a method to handle that event. ]] me.parserstagetwo = { ["debuffstart"] = function(buff) if buff == mod.string.get("boss", "spell", "burningadrenaline") then me.setstate("burningadrenaline", true) end end, ["buffend"] = function(buff) if buff == mod.string.get("boss", "spell", "burningadrenaline") then me.setstate("burningadrenaline", false) elseif buff == mod.string.get("spell", "arcaneshroud") then me.setstate("arcaneshroud", false) elseif buff == mod.string.get("spell", "fade") then -- retract fade mod.table.raidthreatoffset = mod.table.raidthreatoffset + me.lastfadevalue me.setstate("fade", false) end end, ["weaponbuff"] = function(buff, weapon) -- only care about rockbiter == shaman if me.class ~= "shaman" then return end if string.find(buff, mod.string.get("spell", "rockbiter")) then -- get rank local rank _, _, rank = string.find(buff, ".-(%d+).-") -- set rank in mods me.mods.shaman.rockbiter = tonumber(rank) else -- added an enchant that was not rockbiter me.mods.shaman.rockbiter = 0 end end, ["buffstart"] = function(buff) if buff == mod.string.get("spell", "arcaneshroud") then -- fetish of the sand reaver activation mod.my.setstate("arcaneshroud", true) elseif buff == mod.string.get("spell", "vanish") then mod.table.resetraidthreat() elseif buff == mod.string.get("spell", "fade") then local threat = mod.my.ability("fade", "threat") mod.table.raidthreatoffset = mod.table.raidthreatoffset - threat mod.my.lastfadevalue = threat mod.my.setstate("fade", true) end end, } me.lastupdatetime = 0.0 -- return value of GetTime() me.longupdateinterval = 1.0 -- at least this time in seconds will pass between long updates me.lastmegaupdatetime = 0.0 me.megaupdateinterval = 10.0 --[[ Special onupdate() function, called from Core.lua me.states - frequent update me.spellranks - long time update me.mods - long time update me.globalthreat - frequent update ]] me.onupdate = function() -- short updates me.redostates() -- check for long updates local timenow = GetTime() -- long updates if timenow > me.lastupdatetime + me.longupdateinterval then me.lastupdatetime = timenow me.redomods() me.redoglobalthreat() end -- mega updates if timenow > me.lastmegaupdatetime + me.megaupdateinterval then me.lastmegaupdatetime = timenow me.redospellranks() end -- check status of rockbiter enchant for Shaman, i.e. whether it has run out. if GetWeaponEnchantInfo() == nil then me.mods.shaman.rockbiter = 0 end end ---------------------------------------------------------------------------------------- ---------------------------- -- General Methods -- ---------------------------- --[[ mod.my.ability(name, value) Returns the current value for some property of your spells and abilities. is the mod's internal name for the ability. is the name of the property, e.g. "threat", "multiplier", "rage". These are the indexes of values in the mod.data.spells structure. --> multiplier: return nil if nil --> rage: return 0 if nil --> threat / nextattack: throw error if nil This method will use me.spellranks to get the data that applies to your specific rank of the ability. Then it will apply any modifiers from me.mods that are appropriate. ]] me.ability = function(spellid, parameter) if (spellid == nil) or (mod.data.spells[spellid] == nil) then if mod.out.checktrace("error", me, "ability") then mod.out.printtrace(string.format("No ability |cffffff00%s|r found.", tostring(spellid))) end return 0 end local value local data = mod.data.spells[spellid] -- "rage" and "multiplier" parameters are easy, since they if parameter == "rage" then if data.rage == nil then return 0 else return data.rage end elseif parameter == "multiplier" then return data.multiplier -- may be nil end -- to get here, is either "threat" or "nextattack" -- Item abilities only have one value for threat, no ranks. if data.class == "item" then return data.threat end -- Now check the spell has a known rank local spellrank = me.spellranks[spellid] if spellrank == nil then if mod.out.checktrace("error", me, "ability") then mod.out.printtrace(string.format("No spell rank defined for |cffffff00%s.", tostring(spellid))) end return 0 end -- Check there is a value in data for our parameter (should always be) value = data[spellrank][parameter] if value == nil then if mod.out.checktrace("error", me, "ability") then mod.out.printtrace(string.format("No value of |cffffff00%s for rank |cffffff00%s of |cffffff00%s.", tostring(parameter), tostring(spellrankg), tostring(spellid))) end return 0 end -- to get here, there were no errors. we got a value. Now we have to look for class mods that would affect it. if spellid == "sunder" then if parameter == "rage" then value = value + me.mods.warrior.sundercost elseif parameter == "threat" then value = value * me.mods.warrior.sunderthreat end elseif spellid == "heroicstrike" then if parameter == "rage" then value = value + me.mods.warrior.heroicstrikecost end elseif spellid == "feint" then if parameter == "threat" then value = value * me.mods.rogue.feintthreat end elseif spellid == "maul" then if parameter == "rage" then value = value + me.mods.druid.ferocity end elseif spellid == "swipe" then if parameter == "rage" then value = value + me.mods.druid.ferocity end end return value end --[[ mod.my.testthreat() Print out your threat properties for debug purposes. ]] me.testthreat = function() -- 1) Print out spell ranks local key local value for key, value in me.spellranks do mod.out.print(string.format(mod.string.get("print", "data", "abilityrank"), key, value)) end -- 2) Print out global threat mod.out.print(string.format(mod.string.get("print", "data", "globalthreat"), me.globalthreat.value)) for key, value in me.globalthreat.modifiers do if value.isactive == false then break end mod.out.print(string.format(mod.string.get("print", "data", "globalthreatmod"), value.reason, value.value)) end -- 3) Print out threat for specific abilities if me.class == "priest" then mod.out.print(string.format(mod.string.get("print", "data", "multiplier"), UnitClass("player"), mod.string.get("talent", "silentresolve"), 1.0 + me.mods.priest.silentresolve)) mod.out.print(string.format(mod.string.get("print", "data", "multiplier"), UnitClass("player"), SPELL_SCHOOL5_CAP, me.mods.priest.shadowaffinity)) elseif me.class == "warlock" then mod.out.print(string.format(mod.string.get("print", "data", "setactive"), mod.string.get("sets", "nemesis"), 8, mod.out.booltostring(me.mods.warlock.nemesis))) elseif me.class == "mage" then mod.out.print(string.format(mod.string.get("print", "data", "setactive"), mod.string.get("sets", "netherwind"), 3, mod.out.booltostring(me.mods.mage.netherwind))) mod.out.print(string.format(mod.string.get("print", "data", "multiplier"), UnitClass("player"), SPELL_SCHOOL6_CAP, 1.0 + me.mods.mage.arcanethreat)) mod.out.print(string.format(mod.string.get("print", "data", "multiplier"), UnitClass("player"), SPELL_SCHOOL4_CAP, 1.0 + me.mods.mage.frostthreat)) mod.out.print(string.format(mod.string.get("print", "data", "multiplier"), UnitClass("player"), SPELL_SCHOOL2_CAP, 1.0 + me.mods.mage.firethreat)) elseif me.class == "paladin" then mod.out.print(string.format(mod.string.get("print", "data", "multiplier"), UnitClass("player"), mod.string.get("print", "data", "holyspell"), me.mods.paladin.righteousfury)) mod.out.print(string.format(mod.string.get("print", "data", "healing"), mod.data.threatconstants.healing * me.mods.paladin.healing)) elseif me.class == "warrior" then mod.out.print(string.format(mod.string.get("print", "data", "multiplier"), UnitClass("player"), mod.string.get("spell", "sunder"), me.mods.warrior.sunderthreat)) elseif me.class == "druid" then mod.out.print(string.format(mod.string.get("print", "data", "multiplier"), UnitClass("player"), mod.string.get("talent", "tranquility"), me.mods.druid.tranquilitythreat)) elseif me.class == "shaman" then mod.out.print(string.format(mod.string.get("print", "data", "healing"), mod.data.threatconstants.healing * me.mods.shaman.healing)) if me.mods.shaman.rockbiter > 0 then mod.out.print(string.format(mod.string.get("print", "data", "rockbiter"), me.mods.shaman.rockbiter, UnitAttackSpeed("player") * mod.data.rockbiter[me.mods.shaman.rockbiter])) end end end ---------------------------------------------------------------------------------------- ---------------------------- -- States -- ---------------------------- --[[ each state is represented by a key-value pair. The key is a string, which is a description of the state, e.g. "incombat", "afk". The value is a list with properties ["value"], boolean, whether the state is on or off ["lastchange"], return value of GetTime() (seconds + decimal), when the value last changed ["duration"], OPTIONAL, known duration of the state. This is used to turn the state off just in case the normal mechanism to detect it does not work. To set a state's value, call the method mod.my.setstate(, ). If this causes the state to change, the method will return non-nil, and an event KLHTM_STATECHANGE_ will be raised. ]] me.states = { ["feigndeath"] = { ["value"] = false, ["lastchange"] = 0, }, ["arcaneshroud"] = -- from Fetish of the Sandreaver { ["value"] = false, ["lastchange"] = 0, ["duration"] = 20.0 }, ["incombat"] = { ["value"] = false, ["lastchange"] = 0, }, ["playercharmed"] = -- whether you have been mind controlled { ["value"] = false, ["lastchange"] = 0, }, ["burningadrenaline"] = { ["value"] = false, ["lastchange"] = 0, ["duration"] = 20.0, }, ["afk"] = { ["value"] = false, ["lastchange"] = 0, }, ["fade"] = { ["value"] = false, ["lastchange"] = 0, ["duration"] = 10.0, }, } --[[ mod.my.setstate(state, value) Sets the value of one of the state variables above. is a string, a key to the me.states list, e.g. "afk", "incombat", etc. is a boolean, true or false. Return: true if the set represents a change, otherwise nil ]] me.setstate = function(state, value) -- verify if me.states[state] == nil then if mod.out.checktrace("error", me, "state") then mod.out.printtrace(string.format("There is no state |cffffff00%s|r.", tostring(state))) end return end -- update if me.states[state].value ~= value then me.states[state].value = value me.states[state].lastchange = GetTime() return true end end --[[ me.redostates() Checks for changes to state variables that we poll for (e.g. "incombat" / "ischarmed"). Also checks for states that have run out, but whose end events were not detected (e.g. burning adrenaline). This last should not be relied upon though. ]] me.redostates = function() -- hunter: check for FD if mod.my.class == "hunter" then -- Check for feign death debuff local currentfd = mod.data.isbuffpresent("Interface\\Icons\\Ability_Rogue_FeignDeath") if me.setstate("feigndeath", currentfd) and (currentfd == true) then -- first check for resist. if math.abs(me.feigndeathresisttime - GetTime()) < 1.0 then --resisted. Nothing happens if mod.out.checktrace("info", me, "feigndeath") then mod.out.printtrace("feigndeathdebug", "Feign Death was resisted!") end else -- wipe threat mod.table.resetraidthreat() end end end -- update charmed state if UnitIsCharmed("player") == 1 then me.setstate("playercharmed", true) else me.setstate("playercharmed", false) end -- update combat state if UnitAffectingCombat("player") == 1 then if me.setstate("incombat", true) then -- player has just joined combat if mod.out.checktrace("info", me, "incombat") then mod.out.printtrace("You entered combat.") end mod.table.resetraidthreat() local key local value for key, value in mod.combat.recentattacks do mod.table.raidthreatoffset = mod.table.raidthreatoffset + value[2] end end else if (me.states.playercharmed.value == true) or (GetTime() - me.states.playercharmed.lastchange < 2.0) then me.setstate("incombat", true) -- should not be a change else if me.setstate("incombat", false) then if mod.out.checktrace("info", me, "incombat") then mod.out.printtrace("You left combat.") end me.lastfadevalue = 0 -- stop fade end end end -- update states with a "duration" parameter. They are all designed to deactivate on their own, but e.g. -- burning adrenaline isn't working, so we put in this mechanism as a backup local state local data for state, data in me.states do if data.duration and (data.value == true) and (GetTime() > data.lastchange + data.duration + 1.0) then me.setstate(state, false) if mod.out.checktrace("info", me, "state") then mod.out.printtrace(string.format("Deactivated the state %s because it had passed its duration.", state)) end end end end ---------------------------------------------------------------------------------------- ---------------------------- -- Spell Ranks -- ---------------------------- --[[ A collection of key-value pairs. The key is a string, the name of a spell / ability, e.g. "Sunder Armor". It is localised. The value is an integer, the rank of that spell you have. The only spells recorded are those which are cause or remove threat by known amounts, i.e. Sunder Armor, Revenge, Maul, Feint, etc. We want to know the what rank of the spell the player has, because most abilities have different numbers for different spell ranks. e.g. Heroic Strike does more damage and threat for each new rank. Some players will have the extra rank from AQ20, some will not. These ranks will probably not change over the course of a session, but we poll just in case. ]] me.spellranks = { } --[[ me.redospellranks() Redetermines the ranks of all special threat abilities you have. ]] me.redospellranks = function() local index = 0 local name, rankstring, rank, spellid local rankpattern = mod.string.get("misc", "spellrank") -- e.g. "Rank %d" in english. while true do index = index + 1 name, rankstring = GetSpellName(index, "spell") if name == nil then break end -- get the internal spell ID spellid = mod.string.unlocalise("spell", name) if spellid and mod.data.spells[spellid] then rank = 0 _, _, rank = string.find(rankstring, rankpattern) if rank then me.spellranks[spellid] = rank end end end end ---------------------------------------------------------------------------------------- --------------------------------------- -- Threat And Ability Modifers -- --------------------------------------- me.mods = { ["warrior"] = { ["defiance"] = 0.0, -- modifier to global threat. e.g. "+0.15" for 5 points. ["sunderthreat"] = 1.0, -- multiplier. e.g. "1.15" for 8/8 Might. ["sundercost"] = 0.0, -- modifier of rage cost. e.g. "-3" for 3/3 improved sunder talent. ["heroicstrikecost"] = 0.0,-- modifier of rage cost. e.g. "-3" for 3/3 improved heroic strike talent. ["impale"] = 0.0 -- critical strike bonus damage for abilities. e.g. "0.2" for 2/2 impale talent. }, ["rogue"] = { ["feintthreat"] = 1.0, -- multiplier. e.g. "1.25" for 5/8 Bloodfang. }, ["druid"] = { ["feralinstinct"] = 0.0, -- defiance for druids. ["savagefury"] = 1.0, -- multiplier to damage for druid abilities ["subtlety"] = 1.0, -- multiplier to healing threat. e.g. "0.8" for all talents. ["ferocity"] = 0.0, -- modifier to rage cost of maul and swipe. e.g. "-5" for all talents. ["tranquilitythreat"] = 1.0-- multiplier, from Improved Tranquility Talent }, ["mage"] = { ["arcanist"] = false, -- 8 piece bonus ["netherwind"] = false, -- 3 piece ["arcanethreat"] = 0.0, -- almost a modifier to global threat (spells only) e.g. "-0.4" for 2/2 talent points. ["frostthreat"] = 0.0, -- almost a modifier to global threat (spells only) e.g. "-0.3" for 3/3 talent points. ["firethreat"] = 0.0, -- almost a modifier to global threat (spells only) e.g. "-0.3" for 2/2 talent points. }, ["warlock"] = { ["masterdemo"] = 0.0, -- modifier to global threat. e.g. "-0.2" for imp out, 5/5. ["nemesis"] = false, -- 8 piece bonus }, ["priest"] = { ["silentresolve"] = 0.0, -- almost a modifier to global threat (spells only) e.g. "-0.2" for fully talented. ["shadowaffinity"] = 1.0, -- multiplier for shadow damage. e.g. "0.8". }, ["paladin"] = { ["righteousfury"] = 1.0, -- multiplier for threat from holy damage / abilities. ["healing"] = 0.5, -- this is actually constant, but really fits best in this variable. }, ["shaman"] = { ["rockbiter"] = 0, -- current rank of rockbiter that is active, 0 for none. ["healing"] = 1.0, -- multiplier. e.g. 0.85 for 3/3 Healing Grace. } } me.redomods = function() -- talent / gear searching local info local rank local key local value -- warrior if mod.my.class == "warrior" then -- might 8/8 if mod.data.getsetpieces("might") == 8 then me.mods.warrior.sunderthreat = 1.15 else me.mods.warrior.sunderthreat = 1.0 end -- improved sunder armor rank = mod.data.gettalentrank("sunder") me.mods.warrior.sundercost = -rank -- improved heroic strike rank = mod.data.gettalentrank("heroicstrike") me.mods.warrior.heroicstrikecost = -rank -- defiance rank = mod.data.gettalentrank("defiance") me.mods.warrior.defiance = 0.03 * rank -- impale rank = mod.data.gettalentrank("impale") me.mods.warrior.impale = 0.05 * rank -- rogue elseif mod.my.class == "rogue" then if mod.data.getsetpieces("bloodfang") > 4 then me.mods.rogue.feint = 1.25 else me.mods.rogue.feint = 1.0 end -- priest elseif mod.my.class == "priest" then -- silent resolve rank = mod.data.gettalentrank("silentresolve") me.mods.priest.silentresolve = -0.04 * rank -- shadow affinity rank = mod.data.gettalentrank("shadowaffinity") me.mods.priest.shadowaffinity = 1 - rank * 0.25 / 3 -- druid elseif mod.my.class == "druid" then -- subtlety rank = mod.data.gettalentrank("druidsubtlety") me.mods.druid.subtlety = 1 - 0.04 * rank -- feral instinct rank = mod.data.gettalentrank("feralinstinct") me.mods.druid.feralinstinct = 0.03 * rank -- ferocity rank = mod.data.gettalentrank("ferocity") me.mods.druid.ferocity = -rank -- savage fury rank = mod.data.gettalentrank("savagefury") me.mods.druid.savagefury = rank * 0.1 -- improved tranquility rank = mod.data.gettalentrank("tranquility") me.mods.druid.tranquilitythreat = 1.0 - rank * 0.4 -- mage elseif mod.my.class == "mage" then -- arcanist 8 piece if mod.data.getsetpieces("arcanist") == 8 then me.mods.mage.arcanist = true else me.mods.mage.arcanist = false end -- arcane subtelty rank = mod.data.gettalentrank("arcanesubtlety") me.mods.mage.arcanethreat = 0.0 - 0.2 * rank -- frost channeling rank = mod.data.gettalentrank("frostchanneling") me.mods.mage.frostthreat = 0.0 - 0.1 * rank -- burning soul rank = mod.data.gettalentrank("burningsoul") me.mods.mage.firethreat = 0.0 - 0.15 * rank -- netherwind 3 piece if mod.data.getsetpieces("netherwind") > 2 then me.mods.mage.netherwind = true else me.mods.mage.netherwind = false end -- warlock elseif mod.my.class == "warlock" then -- master demonologist rank = mod.data.gettalentrank("masterdemonologist") -- check for imp if UnitCreatureFamily("pet") == mod.string.get("misc", "imp") then me.mods.warlock.masterdemo = -0.04 * rank else me.mods.warlock.masterdemo = 0 end -- nemesis 8 piece if mod.data.getsetpieces("nemesis") == 8 then me.mods.warlock.nemesis = true else me.mods.warlock.nemesis = false end -- shaman elseif mod.my.class == "shaman" then -- healing grace rank = mod.data.gettalentrank("healinggrace") me.mods.shaman.healing = 1.0 - 0.05 * rank -- paladin elseif mod.my.class == "paladin" then local multi = 1.0 -- righteous fury buff: if mod.data.isbuffpresent("Interface\\Icons\\Spell_Holy_SealOfFury") then multi = multi + 0.6 -- talents rank = mod.data.gettalentrank("righteousfury") multi = multi + (0.5 * rank / 3) end me.mods.paladin.righteousfury = multi end end ---------------------------------------------------------------------------------------- ---------------------------- -- Global Threat Mods -- ---------------------------- --[[ There are certain threat modifiers that are applied to all threat done, and that stack additively. For instance with the "Blessing of Salvation" buff, all your threat is reduced by 30%. Wit the Arcanist 8 piece bonus, threat is reduced by 15%, but these bonuses add directly, so that with both bonuses, your threat is reduced by 45%. mod.my.globalthreat provides a list of all your global bonuses that are currently active. The data is designed to be easily printed to the user. The data is updated frequently, because the modifiers can come on and off at any time. This means once every OnUpdate, roughly 40 times per second. As a result we take pains to avoid the creation of new lists, which would otherwise generate excess heap memory. me.globalthreat.modifiers is an ARRAY, i.e. a list keyed by [1], [2], [3], etc. Each item of the array the value is a list with properties ["value"] - the fractional reduction in threat. -30% from Blessing of Salvation would be "-0.3". ["reason"] - a description of the effect, for the user's benefit. e.g. "Blessing of Salvation". ["isactive"] - if false, this value and all further values should be ignored. Since we don't want to keep recreating lists in a frequently called procedure, when a threat modifier is no longer active, is set to false, and that value is ignored. A newly added buff will overwrite the other data fields, but set to true. There is one final part me.globalthreat, a key-value pair. The key is "value" and the value is your total modifier, i.e. the sum of all the modifiers. For a normal character with no buffs / talents / items, this value would be 1.0, e.g. me.globalthreat = { ["value"] = 1.0, ["modifiers"] = { [1] = { ["value"] = 1.0, ["reason"] = "Base Value", ["isactive"] = true, } } } ]] me.globalthreat = { ["value"] = 0.0, ["modifiers"] = { } } --[[ me.modifyglobalthreat(value, reason) Adds a global threat modifier to me.globalthreat. and are as defined in me.globalthreat (see comments above). This method will first look for a value in the me.globalthreat array that is labeled inactive (unused memory) and write the values there. Otherwise it will create a new value and append it to the array. Heap memory creation occurs a maximum of n times, where n is the largest number of +- threat buffs you get. This is on the order of 5, and for the whole running time, so negligable. ]] me.modifyglobalthreat = function(value, reason) -- update the total / sum -- 1.12: multiplicative threat if mod.isnewwowversion then me.globalthreat.value = me.globalthreat.value * (1.0 + value) value = 1.0 + value else me.globalthreat.value = me.globalthreat.value + value end -- look for an unused array position local x local count = table.getn(me.globalthreat.modifiers) for x = 1, count do if me.globalthreat.modifiers[x].isactive == false then -- found an inactive array index. Activate it and write values me.globalthreat.modifiers[x].value = value me.globalthreat.modifiers[x].reason = reason me.globalthreat.modifiers[x].isactive = true return end end -- all the slots in the array are being used. Add a new value to the end table.insert(me.globalthreat.modifiers, {["value"] = value, ["reason"] = reason, ["isactive"] = true}) end --[[ me.redoglobalthreat() Recalculates all your global threat modifiers. ]] me.redoglobalthreat = function() -- 1) reset local x for x = 1, table.getn(me.globalthreat.modifiers) do me.globalthreat.modifiers[x].isactive = false end -- 1.12 change: all threat is multiplicative if mod.isnewwowversion then me.globalthreat.value = 1.0 me.modifyglobalthreat(0.0, mod.string.get("threatmod", "basevalue")) else me.globalthreat.value = 0.0 me.modifyglobalthreat(1.0, mod.string.get("threatmod", "basevalue")) end -- 2) rebuild -- Tranquil Air if mod.data.isbuffpresent("Interface\\Icons\\Spell_Nature_Brilliance") == true then me.modifyglobalthreat(-0.2, mod.string.get("threatmod", "tranquilair")) end -- Blessing of Salvation if mod.data.isbuffpresent("Interface\\Icons\\Spell_Holy_SealOfSalvation") == true then me.modifyglobalthreat(-0.3, mod.string.get("threatmod", "salvation")) elseif mod.data.isbuffpresent("Interface\\Icons\\Spell_Holy_GreaterBlessingofSalvation") == true then me.modifyglobalthreat(-0.3, mod.string.get("threatmod", "salvation")) end -- Burning Adrenaline if me.states["burningadrenaline"].value == true then me.modifyglobalthreat(-0.75, mod.string.get("boss", "spell", "burningadrenaline")) end -- Fetish of the Sand Reaver if me.states["arcaneshroud"].value == true then me.modifyglobalthreat(-0.7, mod.string.get("spell", "arcaneshroud")) end local linkstring local enchant -- +2% threat enchant to gloves linkstring = GetInventoryItemLink("player", 10) or "" _, _, enchant = string.find(linkstring, ".-item:%d+:(%d+).*") if enchant == "2613" then me.modifyglobalthreat(0.02, mod.string.get("threatmod", "glovethreatenchant")) end -- -2% threat enchant to back linkstring = GetInventoryItemLink("player", 15) or "" _, _, enchant = string.find(linkstring, ".-item:%d+:(%d+).*") if enchant == "2621" then me.modifyglobalthreat(-0.02, mod.string.get("threatmod", "backthreatenchant")) end local stance -- Warrior if mod.my.class == "warrior" then stance = me.getstanceindex() if stance == 1 then me.modifyglobalthreat(-0.2, mod.string.get("threatmod", "battlestance")) elseif stance == 2 then me.modifyglobalthreat(0.3, mod.string.get("threatmod", "defensivestance")) me.modifyglobalthreat(me.mods.warrior.defiance, mod.string.get("talent", "defiance")) elseif stance == 3 then me.modifyglobalthreat(-0.2, mod.string.get("threatmod", "berserkerstance")) end -- Druid Stances elseif mod.my.class == "druid" then stance = me.getstanceindex() if stance == 1 then me.modifyglobalthreat(0.3, mod.string.get("threatmod", "bearform")) me.modifyglobalthreat(me.mods.druid.feralinstinct, mod.string.get("talent", "feralinstinct")) elseif stance == 3 then me.modifyglobalthreat(-0.2, mod.string.get("threatmod", "catform")) end -- Rogue elseif mod.my.class == "rogue" then me.modifyglobalthreat(-0.2, UnitClass("player")) -- Arcanist elseif (mod.my.class == "mage") and (me.mods.mage.arcanist == true) then me.modifyglobalthreat(-0.15, mod.string.get("sets", "arcanist") .. " 8/8") -- Warlock elseif (mod.my.class == "warlock") and (me.mods.warlock.masterdemo ~= 0) then me.modifyglobalthreat(me.mods.warlock.masterdemo, mod.string.get("talent", "masterdemonologist")) end end --[[ me.getstanceindex() For Druids and Warriors, returns an integer saying which stance you are in. ]] me.getstanceindex = function() local x = 0 local isactive local texture while true do x = x + 1 texture, _, isactive = GetShapeshiftFormInfo(x) if texture == nil then return 0 end if isactive == 1 then return x end end end