From 078980ccc8ecf9e957d4e72074471c6fd2d49710 Mon Sep 17 00:00:00 2001 From: "Johnny C. Lam" Date: Thu, 15 May 2014 23:18:55 +0000 Subject: [PATCH] Fix GetRunesCooldown() calculation. Also remove the death condition from all rune conditions. git-svn-id: svn://svn.curseforge.net/wow/ovale/mainline/trunk@1452 d5049fe3-3747-40f7-a4b5-f36d6801af5f --- OvaleBestAction.lua | 2 +- OvaleRunes.lua | 226 ++++++++++++++++++++++++++++------------------ conditions/RuneCount.lua | 20 +--- conditions/Runes.lua | 28 +----- 4 files changed, 148 insertions(+), 128 deletions(-) diff --git a/OvaleBestAction.lua b/OvaleBestAction.lua index 7a7ed8c..1e46c8d 100644 --- a/OvaleBestAction.lua +++ b/OvaleBestAction.lua @@ -800,7 +800,7 @@ function OvaleBestAction:GetActionInfo(element, state) end end if needRunes then - local ending = state.currentTime + state:GetRunesCooldown(si.blood, si.unholy, si.frost, si.death, false) + local ending = state.currentTime + state:GetRunesCooldown(si.blood, si.unholy, si.frost, si.death) if ending > actionCooldownStart + actionCooldownDuration then actionCooldownDuration = ending - actionCooldownStart end diff --git a/OvaleRunes.lua b/OvaleRunes.lua index dc23e0f..699f63e 100644 --- a/OvaleRunes.lua +++ b/OvaleRunes.lua @@ -24,6 +24,7 @@ local OvaleSpellBook = nil local OvaleStance = nil local OvaleState = nil +--local debugprint = print local ipairs = ipairs local pairs = pairs local API_GetRuneCooldown = GetRuneCooldown @@ -128,7 +129,7 @@ function OvaleRunes:OnEnable() -- Initialize rune database. for runeType, slots in ipairs(RUNE_SLOTS) do for _, slot in pairs(slots) do - self.rune[slot] = { slotType = runeType, IsActiveRune = IsActiveRune } + self.rune[slot] = { slot = slot, slotType = runeType, IsActiveRune = IsActiveRune } end end self:RegisterEvent("PLAYER_ENTERING_WORLD", "UpdateAllRunes") @@ -369,7 +370,7 @@ end -- count The number of currently active runes of the given type. -- startCooldown The time at which the next rune of the given type went on cooldown. -- endCooldown The time at which the next rune of the given type will be active. -statePrototype.RuneCount = function(state, name, deathCondition, atTime) +statePrototype.RuneCount = function(state, name, atTime) -- Default to checking the rune count at the end of the current spellcast in the -- simulator, or at the current time if no spell is being cast. if not atTime then @@ -383,27 +384,14 @@ statePrototype.RuneCount = function(state, name, deathCondition, atTime) local startCooldown, endCooldown = math.huge, math.huge local runeType = RUNE_TYPE[name] if runeType ~= DEATH_RUNE then - if deathCondition == "any" or deathCondition == 1 then - -- Match runes of the given type or any death runes. - for slot, rune in ipairs(state.rune) do - if rune.type == runeType or rune.type == DEATH_RUNE then - if rune:IsActiveRune(atTime) then - count = count + 1 - elseif rune.endCooldown < endCooldown then - startCooldown, endCooldown = rune.startCooldown, rune.endCooldown - end - end - end - else - -- Match only the runes of the given type. - for _, slot in ipairs(RUNE_SLOTS[runeType]) do - local rune = state.rune[slot] - if not deathCondition or ((deathCondition == "none" or deathCondition == 0) and rune.type ~= DEATH_RUNE) then - if rune:IsActiveRune(atTime) then - count = count + 1 - elseif rune.endCooldown < endCooldown then - startCooldown, endCooldown = rune.startCooldown, rune.endCooldown - end + -- Match only the runes of the given type. + for _, slot in ipairs(RUNE_SLOTS[runeType]) do + local rune = state.rune[slot] + if not deathCondition or ((deathCondition == "none" or deathCondition == 0) and rune.type ~= DEATH_RUNE) then + if rune:IsActiveRune(atTime) then + count = count + 1 + elseif rune.endCooldown < endCooldown then + startCooldown, endCooldown = rune.startCooldown, rune.endCooldown end end end @@ -425,90 +413,156 @@ end -- Returns the number of seconds before all of the required runes are available. statePrototype.GetRunesCooldown = nil do - -- If the rune is active, then return the remaining active runes count requirement. - -- Also return the time of the next rune becoming active. - local function MatchingRune(rune, count, endCooldown) - if count > 0 then - count = count - 1 - if rune.endCooldown > endCooldown then - endCooldown = rune.endCooldown - end - else - if rune.endCooldown < endCooldown then - endCooldown = rune.endCooldown + -- The remaining count requirements, indexed by rune type. + count = {} + usedRune = {} + + statePrototype.GetRunesCooldown = function(state, blood, unholy, frost, death, atTime) + -- Default to checking runes at the end of the current spellcast in the + -- simulator, or at the current time if no spell is being cast. + if not atTime then + if state.endCast and state.endCast > state.currentTime then + atTime = state.endCast + else + atTime = state.currentTime end end - return count, endCooldown - end - -- The remaining count requirements, indexed by rune type. - local runeCount = {} - -- The latest time till a rune of that type is off cooldown, indexed by rune type. - local runeEndCooldown = {} - - statePrototype.GetRunesCooldown = function(state, blood, unholy, frost, death, deathCondition) -- Initialize static variables. - runeCount[BLOOD_RUNE] = blood or 0 - runeCount[UNHOLY_RUNE] = unholy or 0 - runeCount[FROST_RUNE] = frost or 0 - runeCount[DEATH_RUNE] = death or 0 - runeEndCooldown[BLOOD_RUNE] = 0 - runeEndCooldown[UNHOLY_RUNE] = 0 - runeEndCooldown[FROST_RUNE] = 0 - runeEndCooldown[DEATH_RUNE] = 0 - - -- Use regular runes to meet the count requirements. - for slot, rune in ipairs(state.rune) do - if rune.type ~= DEATH_RUNE then - local runeType = rune.type - local count, endCooldown = MatchingRune(rune, runeCount[runeType], runeEndCooldown[runeType]) - runeCount[runeType] = count - runeEndCooldown[runeType] = endCooldown + count[BLOOD_RUNE] = blood or 0 + count[UNHOLY_RUNE] = unholy or 0 + count[FROST_RUNE] = frost or 0 + count[DEATH_RUNE] = death or 0 + wipe(usedRune) + + for runeType in pairs(RUNE_SLOTS) do + -- Match active, regular runes. + for _, slot in pairs(RUNE_SLOTS[runeType]) do + if count[runeType] == 0 then break end + local rune = state.rune[slot] + if not usedRune[rune] and rune.type ~= DEATH_RUNE and IsActiveRune(rune, atTime) then + --debugprint(string.format(" [1] Match active regular rune in slot %d to %s", slot, RUNE_NAME[runeType])) + usedRune[rune] = true + count[runeType] = count[runeType] - 1 + end + end + -- Match active death runes of the same socket type. + for _, slot in pairs(RUNE_SLOTS[runeType]) do + if count[runeType] == 0 then break end + local rune = state.rune[slot] + if not usedRune[rune] and rune.type == DEATH_RUNE and IsActiveRune(rune, atTime) then + --debugprint(string.format(" [2] Match active death rune in slot %d to %s, type = %s", slot, RUNE_NAME[slotType])) + usedRune[rune] = true + count[runeType] = count[runeType] - 1 + end end end - -- Use death runes of the matching rune type to meet the count requirements. - if deathCondition ~= "none" and deathCondition ~= 0 then - for slot, rune in ipairs(state.rune) do - if rune.type == DEATH_RUNE then - local runeType = rune.slotType - local count, endCooldown = MatchingRune(rune, runeCount[runeType], runeEndCooldown[runeType]) - runeCount[runeType] = count - runeEndCooldown[runeType] = endCooldown + -- Match active death runes in DEATH_RUNE_PRIORITY order to meet death count requirements. + for _, slot in ipairs(DEATH_RUNE_PRIORITY) do + if count[DEATH_RUNE] == 0 then break end + local rune = state.rune[slot] + if not usedRune[rune] and rune.type == DEATH_RUNE and IsActiveRune(rune, atTime) then + --debugprint(string.format(" [3] Match active death rune in slot %d", slot)) + usedRune[rune] = true + count[DEATH_RUNE] = count[DEATH_RUNE] - 1 + end + end + -- At this point, if count[runeType] > 0 then there are no active runes of the appropriate type that match that requirement. + -- Match active death runes in ANY_RUNE_PRIORITY order to meet remaining count requirements. + for _, runeType in pairs(RUNE_TYPE) do + for _, slot in ipairs(ANY_RUNE_PRIORITY) do + if count[runeType] == 0 then break end + local rune = state.rune[slot] + if not usedRune[rune] and rune.type == DEATH_RUNE and IsActiveRune(rune, atTime) then + --debugprint(string.format(" [4] Match active death rune in slot %d to %s", slot, RUNE_NAME[runeType])) + usedRune[rune] = true + count[runeType] = count[runeType] - 1 end end end - -- Remaining rune requirements that have not yet been met. - local remainingCount = 0 - for runeType = 1, 4 do - remainingCount = remainingCount + runeCount[runeType] + -- At this point, there are no more active runes, death or otherwise, that can satisfy count requirements. + for runeType, slotList in pairs(RUNE_SLOTS) do + -- Match regenerating runes of the appropriate socket type. + if count[runeType] > 0 then + local slot1, slot2 = slotList[1], slotList[2] + local rune1, rune2 = state.rune[slot1], state.rune[slot2] + if count[runeType] == 1 then + local rune, slot + if not usedRune[rune1] and not usedRune[rune2] then + rune = (rune1.endCooldown < rune2.endCooldown) and rune1 or rune2 + slot = (rune1.endCooldown < rune2.endCooldown) and slot1 or slot2 + elseif not usedRune[rune1] then + rune = rune1 + slot = slot1 + elseif not usedRune[rune2] then + rune = rune2 + slot = slot2 + end + if rune then + --debugprint(string.format(" [5] Match regenerating rune in slot %d to %s", slot, RUNE_NAME[runeType])) + usedRune[rune] = true + count[runeType] = 0 + end + else -- if count[runeType] == 2 then + if not usedRune[rune1] and not usedRune[rune2] then + --debugprint(string.format(" [5] Match regenerating rune in slot %d to %s", slot1, RUNE_NAME[runeType])) + --debugprint(string.format(" [5] Match regenerating rune in slot %d to %s", slot2, RUNE_NAME[runeType])) + usedRune[rune1] = true + usedRune[rune2] = true + count[runeType] = 0 + elseif not usedRune[rune1] then + --debugprint(string.format(" [5] Match regenerating rune in slot %d to %s", slot1, RUNE_NAME[runeType])) + usedRune[rune1] = true + count[runeType] = 1 + elseif not usedRune[rune2] then + --debugprint(string.format(" [5] Match regenerating rune in slot %d to %s", slot2, RUNE_NAME[runeType])) + usedRune[rune2] = true + count[runeType] = 1 + end + end + end + -- Match any unused, regenerating death runes. + for slot, rune in pairs(state.rune) do + if count[runeType] == 0 then break end + if not usedRune[rune] and rune.type == DEATH_RUNE then + --debugprint(string.format(" [6] Match regenerating rune in slot %d to %s", slot, RUNE_NAME[runeType])) + usedRune[rune] = true + count[runeType] = count[runeType] - 1 + end + end end - -- Use death runes of any type to meet any remaining count requirements. - if deathCondition == "any" or deathCondition == 1 then - for _, slot in ipairs(DEATH_RUNE_PRIORITY) do - local rune = state.rune[slot] - local runeType = DEATH_RUNE - local count, endCooldown = MatchingRune(rune, remainingCount, runeEndCooldown[runeType]) - remainingCount = count - runeEndCooldown[runeType] = endCooldown + -- Replace any used runes with a regenerating death rune with a shorter cooldown. + for slot, rune in pairs(state.rune) do + if not usedRune[rune] and rune.type == DEATH_RUNE then + for used in pairs(usedRune) do + if rune.endCooldown < used.endCooldown then + --debugprint(string.format(" [7] Replacing matched rune in slot %d with regenerating rune in slot %d", used.slot, slot)) + usedRune[used] = nil + usedRune[rune] = true + break + end + end end end -- This shouldn't happen because it means the rune requirements will never be met. - if remainingCount > 0 then - Ovale:Logf("Impossible rune count requirements: blood=%d, unholy=%d, frost=%d, death=%d", blood, unholy, frost, death) - return math.huge + for _, runeType in pairs(RUNE_TYPE) do + if count[runeType] > 0 then + Ovale:Logf("Impossible rune count requirements: blood=%d, unholy=%d, frost=%d, death=%d", blood, unholy, frost, death) + return math.huge + end end local maxEndCooldown = 0 - for runeType = 1, 4 do - if runeEndCooldown[runeType] > maxEndCooldown then - maxEndCooldown = runeEndCooldown[runeType] + for rune in pairs(usedRune) do + if maxEndCooldown < rune.endCooldown then + maxEndCooldown = rune.endCooldown end end - if maxEndCooldown > 0 then - return maxEndCooldown - state.currentTime + if maxEndCooldown > atTime then + return maxEndCooldown - atTime end return 0 end diff --git a/conditions/RuneCount.lua b/conditions/RuneCount.lua index ceafdde..35dc81f 100644 --- a/conditions/RuneCount.lua +++ b/conditions/RuneCount.lua @@ -25,12 +25,6 @@ do -- Valid values: blood, frost, unholy, death -- @param operator Optional. Comparison operator: less, atMost, equal, atLeast, more. -- @param number Optional. The number to compare against. - -- @param death Sets how death runes are used to fulfill the rune count requirements. - -- If not set, then only death runes of the proper rune type are used. - -- If set with "death=0", then no death runes are used. - -- If set with "death=1", then death runes of any rune type are used. - -- Default is unset. - -- Valid values: unset, 0, 1 -- @return The number of runes. -- @return A boolean value for the result of the comparison. -- @see Rune @@ -40,9 +34,7 @@ do local function RuneCount(condition) local name, comparator, limit = condition[1], condition[2], condition[3] - local deathCondition = condition.death - - local count, startCooldown, endCooldown = state:RuneCount(name, deathCondition) + local count, startCooldown, endCooldown = state:RuneCount(name) if startCooldown < math.huge then local start, ending = startCooldown, endCooldown return TestValue(start, ending, count, start, 0, comparator, limit) @@ -57,12 +49,6 @@ do -- Valid values: blood, frost, unholy, death -- @param operator Optional. Comparison operator: less, atMost, equal, atLeast, more. -- @param number Optional. The number to compare against. - -- @param death Sets how death runes are used to fulfill the rune count requirements. - -- If not set, then only death runes of the proper rune type are used. - -- If set with "death=0", then no death runes are used. - -- If set with "death=1", then death runes of any rune type are used. - -- Default is unset. - -- Valid values: unset, 0, 1 -- @return The number of runes. -- @return A boolean value for the result of the comparison. -- @see RuneCount @@ -71,9 +57,7 @@ do local function Rune(condition) local name, comparator, limit = condition[1], condition[2], condition[3] - local deathCondition = condition.death - - local count, startCooldown, endCooldown = state:RuneCount(name, deathCondition) + local count, startCooldown, endCooldown = state:RuneCount(name) if startCooldown < math.huge then local origin = startCooldown local rate = 1 / (endCooldown - startCooldown) diff --git a/conditions/Runes.lua b/conditions/Runes.lua index c15f53c..0ef8feb 100644 --- a/conditions/Runes.lua +++ b/conditions/Runes.lua @@ -33,19 +33,7 @@ do runes[name] = runes[name] + count k = k + 1 end - local deathCondition - if condition.death == 0 then - deathCondition = "none" - elseif condition.death == 1 then - deathCondition = "any" - end - -- Legacy parameter "nodeath"; no longer documented. - if not condition.death and condition.nodeath == 1 then - deathCondition = "none" - elseif condition.nodeath == 0 then - deathCondition = "any" - end - return runes.blood, runes.unholy, runes.frost, runes.death, deathCondition + return runes.blood, runes.unholy, runes.frost, runes.death end end @@ -58,19 +46,13 @@ do -- Valid values: blood, frost, unholy, death -- @param number The number of runes -- @param ... Optional. Additional "type number" pairs for minimum rune requirements. - -- @param death Sets how death runes are used to fulfill the rune count requirements. - -- If not set, then only death runes of the proper rune type are used. - -- If set with "death=0", then no death runes are used. - -- If set with "death=1", then death runes of any rune type are used. - -- Default is unset. - -- Valid values: unset, 0, 1 -- @return A boolean value. -- @usage -- if Runes(frost 1) Spell(howling_blast) local function Runes(condition) - local blood, unholy, frost, death, deathCondition = ParseRuneCondition(condition) - local seconds = state:GetRunesCooldown(blood, unholy, frost, death, deathCondition) + local blood, unholy, frost, death = ParseRuneCondition(condition) + local seconds = state:GetRunesCooldown(blood, unholy, frost, death) return state.currentTime + seconds, math.huge end @@ -93,8 +75,8 @@ do -- @return The number of seconds. local function RunesCooldown(condition) - local blood, unholy, frost, death, deathCondition = ParseRuneCondition(condition) - local seconds = state:GetRunesCooldown(blood, unholy, frost, death, deathCondition) + local blood, unholy, frost, death = ParseRuneCondition(condition) + local seconds = state:GetRunesCooldown(blood, unholy, frost, death) return 0, state.currentTime + seconds, seconds, state.currentTime, -1 end -- 1.7.9.5