vanilla-wow-addons – Rev 1
?pathlinks?

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.
<name> is the mod's internal name for the ability.
<value> 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, <parameter> 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(<string name>, <boolean value>). If this causes the
state to change, the method will return non-nil, and an event KLHTM_STATECHANGE_<NAME> 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.
<state> is a string, a key to the me.states list, e.g. "afk", "incombat", etc.
<value> is a boolean, true or false.
Return: true if the set represents a change, otherwise nil
]]
me.setstate = function(state, value)
-- verify <state>
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,
<isactive> is set to false, and that value is ignored. A newly added buff will overwrite the
other data fields, but set <isactive> 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.
<value> and <reason> 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
Generated by GNU Enscript 1.6.5.90.