Quantcast

Teach OvalePaperDoll to manage snapshot state for the simulator.

Johnny C. Lam [11-17-13 - 16:30]
Teach OvalePaperDoll to manage snapshot state for the simulator.

git-svn-id: svn://svn.curseforge.net/wow/ovale/mainline/trunk@1192 d5049fe3-3747-40f7-a4b5-f36d6801af5f
Filename
OvaleBestAction.lua
OvaleCooldown.lua
OvalePaperDoll.lua
OvaleRunes.lua
conditions/BuffExpires.lua
conditions/Damage.lua
conditions/DamageMultiplier.lua
conditions/Level.lua
conditions/PaperDoll.lua
conditions/RelativeLevel.lua
conditions/Snapshot.lua
conditions/TimeWithHaste.lua
conditions/WeaponDamage.lua
conditions/conditions.xml
diff --git a/OvaleBestAction.lua b/OvaleBestAction.lua
index b1ff5f6..c77bdce 100644
--- a/OvaleBestAction.lua
+++ b/OvaleBestAction.lua
@@ -23,7 +23,6 @@ local OvaleCooldown = nil
 local OvaleData = nil
 local OvaleEquipement = nil
 local OvaleFuture = nil
-local OvalePaperDoll = nil
 local OvalePower = nil
 local OvaleSpellBook = nil
 local OvaleStance = nil
@@ -138,9 +137,9 @@ local function ComputeAction(element)
 			-- "canStopChannelling=N" means that there are N total ticks in the channelled spell.
 			local numTicks, scaling
 			if si.haste == "spell" then
-				scaling = OvalePaperDoll:GetSpellHasteMultiplier()
+				scaling = state:GetSpellHasteMultiplier()
 			elseif si.haste == "melee" then
-				scaling = OvalePaperDoll:GetMeleeHasteMultiplier()
+				scaling = state:GetMeleeHasteMultiplier()
 			else
 				scaling = 1
 			end
@@ -712,7 +711,6 @@ function OvaleBestAction:OnInitialize()
 	OvaleData = Ovale.OvaleData
 	OvaleEquipement = Ovale.OvaleEquipement
 	OvaleFuture = Ovale.OvaleFuture
-	OvalePaperDoll = Ovale.OvalePaperDoll
 	OvalePower = Ovale.OvalePower
 	OvaleSpellBook = Ovale.OvaleSpellBook
 	OvaleStance = Ovale.OvaleStance
diff --git a/OvaleCooldown.lua b/OvaleCooldown.lua
index 8712fa4..6b62c8e 100644
--- a/OvaleCooldown.lua
+++ b/OvaleCooldown.lua
@@ -151,9 +151,9 @@ function OvaleCooldown:ApplySpellAfterCast(state, spellId, startCast, endCast, n
 			-- Adjust cooldown duration if it is affected by haste: "cd_haste=melee" or "cd_haste=spell".
 			if cd.duration > 0 and si.cd_haste then
 				if si.cd_haste == "melee" then
-					cd.duration = cd.duration / OvalePaperDoll:GetMeleeHasteMultiplier()
+					cd.duration = cd.duration / state:GetMeleeHasteMultiplier()
 				elseif si.cd_haste == "spell" then
-					cd.duration = cd.duration / OvalePaperDoll:GetSpellHasteMultiplier()
+					cd.duration = cd.duration / state:GetSpellHasteMultiplier()
 				end
 			end

diff --git a/OvalePaperDoll.lua b/OvalePaperDoll.lua
index 00b5505..4a95fdd 100644
--- a/OvalePaperDoll.lua
+++ b/OvalePaperDoll.lua
@@ -19,6 +19,7 @@ local OvalePoolRefCount = Ovale.OvalePoolRefCount
 -- Forward declarations for module dependencies.
 local OvaleEquipement = nil
 local OvaleStance = nil
+local OvaleState = nil

 local select = select
 local tonumber = tonumber
@@ -139,6 +140,7 @@ function OvalePaperDoll:OnInitialize()
 	-- Resolve module dependencies.
 	OvaleEquipement = Ovale.OvaleEquipement
 	OvaleStance = Ovale.OvaleStance
+	OvaleState = Ovale.OvaleState

 	-- Initialize latest snapshot table.
 	if self.snapshot then
@@ -171,9 +173,11 @@ function OvalePaperDoll:OnEnable()
 	self:RegisterEvent("UNIT_STATS")
 	self:RegisterMessage("Ovale_EquipmentChanged", "UpdateDamage")
 	self:RegisterMessage("Ovale_StanceChanged", "UpdateDamage")
+	OvaleState:RegisterState(self, self.statePrototype)
 end

 function OvalePaperDoll:OnDisable()
+	OvaleState:UnregisterState(self)
 	self:UnregisterEvent("ACTIVE_TALENT_GROUP_CHANGED")
 	self:UnregisterEvent("COMBAT_RATING_UPDATE")
 	self:UnregisterEvent("MASTERY_UPDATE")
@@ -471,3 +475,60 @@ function OvalePaperDoll:Debug(snapshot)
 	Ovale:FormatPrint("%s: %f", self.SNAPSHOT_STATS["offHandWeaponDamage"].description, snapshot.offHandWeaponDamage)
 end
 --</public-static-methods>
+
+--[[----------------------------------------------------------------------------
+	State machine for simulator.
+--]]----------------------------------------------------------------------------
+
+--<public-static-properties>
+OvalePaperDoll.statePrototype = {
+	level = nil,
+	specialization = nil,
+	snapshot = nil,
+}
+--</public-static-properties>
+
+--<public-static-methods>
+-- Initialize the state.
+function OvalePaperDoll:InitializeState(state)
+	state.level = nil
+	state.specialization = nil
+	state.snapshot = nil
+end
+
+-- Reset the state to the current conditions.
+function OvalePaperDoll:ResetState(state)
+	state.level = self.level
+	state.specialization = self.specialization
+	if state.snapshot and state.snapshot.snapshotTime < OvaleState.now then
+		state.snapshot:ReleaseReference()
+		state.snapshot = nil
+	end
+	state.snapshot = state.snapshot or self.snapshot:GetReference()
+end
+--</public-static-methods>
+
+-- Mix-in methods for simulator state.
+do
+	local statePrototype = OvalePaperDoll.statePrototype
+
+	function statePrototype:GetMasteryMultiplier()
+		local state = self
+		return 1 + state.snapshot.masteryEffect / 100
+	end
+
+	function statePrototype:GetMeleeHasteMultiplier()
+		local state = self
+		return 1 + state.snapshot.meleeHaste / 100
+	end
+
+	function statePrototype:GetRangedHasteMultiplier()
+		local state = self
+		return 1 + state.snapshot.rangedHaste / 100
+	end
+
+	function statePrototype:GetSpellHasteMultiplier()
+		local state = self
+		return 1 + state.snapshot.spellHaste / 100
+	end
+end
\ No newline at end of file
diff --git a/OvaleRunes.lua b/OvaleRunes.lua
index af69526..b62c4a3 100644
--- a/OvaleRunes.lua
+++ b/OvaleRunes.lua
@@ -19,7 +19,6 @@ Ovale.OvaleRunes = OvaleRunes
 --<private-static-properties>
 -- Forward declarations for module dependencies.
 local OvaleData = nil
-local OvalePaperDoll = nil
 local OvalePower = nil
 local OvaleSpellBook = nil
 local OvaleStance = nil
@@ -86,7 +85,6 @@ OvaleRunes.RUNE_TYPE = RUNE_TYPE
 function OvaleRunes:OnInitialize()
 	-- Resolve module dependencies.
 	OvaleData = Ovale.OvaleData
-	OvalePaperDoll = Ovale.OvalePaperDoll
 	OvalePower = Ovale.OvalePower
 	OvaleSpellBook = Ovale.OvaleSpellBook
 	OvaleStance = Ovale.OvaleStance
@@ -285,7 +283,7 @@ do
 					start = rune.endCooldown
 				end
 			end
-			local duration = 10 / OvalePaperDoll:GetSpellHasteMultiplier()
+			local duration = 10 / state:GetSpellHasteMultiplier()
 			if OvaleStance:IsStance("death_knight_blood_presence") and OvaleSpellBook:IsKnownSpell(IMPROVED_BLOOD_PRESENCE) then
 				-- Improved Blood Presence increases rune regeneration rate by 20%.
 				duration = duration / 1.2
diff --git a/conditions/BuffExpires.lua b/conditions/BuffExpires.lua
index 5234d6e..8441d5a 100644
--- a/conditions/BuffExpires.lua
+++ b/conditions/BuffExpires.lua
@@ -12,21 +12,21 @@ local _, Ovale = ...

 do
 	local OvaleCondition = Ovale.OvaleCondition
-	local OvalePaperDoll = Ovale.PaperDoll
 	local OvaleState = Ovale.OvaleState

 	local ParseCondition = OvaleCondition.ParseCondition

 	local function TimeWithHaste(t, haste)
+		local state = OvaleState.state
 		if not t then
 			t = 0
 		end
 		if not haste then
 			return t
 		elseif haste == "spell" then
-			return t / OvalePaperDoll:GetSpellHasteMultiplier()
+			return t / state:GetSpellHasteMultiplier()
 		elseif haste == "melee" then
-			return t / OvalePaperDoll:GetMeleeHasteMultiplier()
+			return t / state:GetMeleeHasteMultiplier()
 		else
 			Ovale:Logf("Unknown haste parameter haste=%s", haste)
 			return t
diff --git a/conditions/Damage.lua b/conditions/Damage.lua
index 8049f83..ecee004 100644
--- a/conditions/Damage.lua
+++ b/conditions/Damage.lua
@@ -12,7 +12,6 @@ local _, Ovale = ...
 do
 	local OvaleCondition = Ovale.OvaleCondition
 	local OvaleData = Ovale.OvaleData
-	local OvalePaperDoll = Ovale.OvalePaperDoll
 	local OvaleState = Ovale.OvaleState

 	local Compare = OvaleCondition.Compare
@@ -21,11 +20,11 @@ do
 	local function GetDamage(spellId)
 		local state = OvaleState.state
 		-- TODO: Use target's debuffs in this calculation.
-		local ap = OvalePaperDoll.snapshot.attackPower or 0
-		local sp = OvalePaperDoll.snapshot.spellBonusDamage or 0
-		local mh = OvalePaperDoll.snapshot.mainHandWeaponDamage or 0
-		local oh = OvalePaperDoll.snapshot.offHandWeaponDamage or 0
-		local bdm = OvalePaperDoll.snapshot.baseDamageMultiplier or 1
+		local ap = state.snapshot.attackPower or 0
+		local sp = state.snapshot.spellBonusDamage or 0
+		local mh = state.snapshot.mainHandWeaponDamage or 0
+		local oh = state.snapshot.offHandWeaponDamage or 0
+		local bdm = state.snapshot.baseDamageMultiplier or 1
 		local dm = state:GetDamageMultiplier(spellId) or 1
 		local combo = state.combo or 0
 		return OvaleData:GetDamage(spellId, ap, sp, mh, oh, combo) * bdm * dm
diff --git a/conditions/DamageMultiplier.lua b/conditions/DamageMultiplier.lua
index e49fceb..9610356 100644
--- a/conditions/DamageMultiplier.lua
+++ b/conditions/DamageMultiplier.lua
@@ -11,7 +11,6 @@ local _, Ovale = ...

 do
 	local OvaleCondition = Ovale.OvaleCondition
-	local OvalePaperDoll = Ovale.OvalePaperDoll
 	local OvaleState = Ovale.OvaleState

 	local Compare = OvaleCondition.Compare
@@ -33,7 +32,7 @@ do
 	local function DamageMultiplier(condition)
 		local spellId, comparator, limit = condition[1], condition[2], condition[3]
 		local state = OvaleState.state
-		local bdm = OvalePaperDoll.snapshot.baseDamageMultiplier
+		local bdm = state.snapshot.baseDamageMultiplier
 		local dm = state:GetDamageMultiplier(spellId)
 		local value = bdm * dm
 		return Compare(value, comparator, limit)
diff --git a/conditions/Level.lua b/conditions/Level.lua
index 0bf816a..a9d9c9d 100644
--- a/conditions/Level.lua
+++ b/conditions/Level.lua
@@ -11,7 +11,7 @@ local _, Ovale = ...

 do
 	local OvaleCondition = Ovale.OvaleCondition
-	local OvalePaperDoll = Ovale.OvalePaperDoll
+	local OvaleState = Ovale.OvaleState

 	local API_UnitLevel = UnitLevel
 	local Compare = OvaleCondition.Compare
@@ -34,9 +34,10 @@ do
 	local function Level(condition)
 		local comparator, limit = condition[1], condition[2]
 		local target = ParseCondition(condition)
+		local state = OvaleState.state
 		local value
 		if target == "player" then
-			value = OvalePaperDoll.level
+			value = state.level
 		else
 			value = API_UnitLevel(target)
 		end
diff --git a/conditions/PaperDoll.lua b/conditions/PaperDoll.lua
deleted file mode 100644
index 43e655f..0000000
--- a/conditions/PaperDoll.lua
+++ /dev/null
@@ -1,253 +0,0 @@
---[[--------------------------------------------------------------------
-    Ovale Spell Priority
-    Copyright (C) 2013 Johnny C. Lam
-
-    This program is free software; you can redistribute it and/or modify
-    it under the terms of the GNU General Public License in the LICENSE
-    file accompanying this program.
---]]--------------------------------------------------------------------
-
-local _, Ovale = ...
-
-do
-	local OvaleCondition = Ovale.OvaleCondition
-	local OvalePaperDoll = Ovale.OvalePaperDoll
-
-	local Compare = OvaleCondition.Compare
-
-	-- Returns the value of the given paper-doll stat.
-	local function PaperDoll(statName, defaultValue, condition)
-		local comparator, limit = condition[1], condition[2]
-		local value = OvalePaperDoll.snapshot[statName]
-		value = value or defaultValue
-		return Compare(value, comparator, limit)
-	end
-
-	-- Returns the critical strike chance of the given paper-doll stat.
-	local function PaperDollCritChance(statName, defaultValue, condition)
-		local comparator, limit = condition[1], condition[2]
-		local value = OvalePaperDoll.snapshot[statName]
-		value = value or defaultValue
-		if condition.unlimited ~= 1 and value > 100 then
-			value = 100
-		end
-		return Compare(value, comparator, limit)
-	end
-
-	--- Get the current agility of the player.
-	-- @name Agility
-	-- @paramsig number or boolean
-	-- @param operator Optional. Comparison operator: less, atMost, equal, atLeast, more.
-	-- @param number Optional. The number to compare against.
-	-- @return The current agility.
-	-- @return A boolean value for the result of the comparison.
-
-	local function Agility(condition)
-		return PaperDoll("agility", 0, condition)
-	end
-
-	--- Get the current attack power of the player.
-	-- @name AttackPower
-	-- @paramsig number or boolean
-	-- @param operator Optional. Comparison operator: less, atMost, equal, atLeast, more.
-	-- @param number Optional. The number to compare against.
-	-- @return The current attack power.
-	-- @return A boolean value for the result of the comparison.
-	-- @see LastAttackPower
-	-- @usage
-	-- if AttackPower() >10000 Spell(rake)
-	-- if AttackPower(more 10000) Spell(rake)
-
-	local function AttackPower(condition)
-		return PaperDoll("attackPower", 0, condition)
-	end
-
-	--- Get the current intellect of the player.
-	-- @name Intellect
-	-- @paramsig number or boolean
-	-- @param operator Optional. Comparison operator: less, atMost, equal, atLeast, more.
-	-- @param number Optional. The number to compare against.
-	-- @return The current intellect.
-	-- @return A boolean value for the result of the comparison.
-
-	local function Intellect(condition)
-		return PaperDoll("intellect", 0, condition)
-	end
-
-	--- Get the current mastery effect of the player.
-	-- Mastery effect is the effect of the player's mastery, typically a percent-increase to damage
-	-- or a percent-increase to chance to trigger some effect.
-	-- @name MasteryEffect
-	-- @paramsig number or boolean
-	-- @param operator Optional. Comparison operator: less, atMost, equal, atLeast, more.
-	-- @param number Optional. The number to compare against.
-	-- @return The current mastery effect.
-	-- @return A boolean value for the result of the comparison.
-	-- @see LastMasteryEffect
-	-- @usage
-	-- if {DamageMultiplier(rake) * {1 + MasteryEffect()/100}} >1.8
-	--     Spell(rake)
-
-	local function MasteryEffect(condition)
-		return PaperDoll("masteryEffect", 0, condition)
-	end
-
-	--- Get the current melee critical strike chance of the player.
-	-- @name MeleeCritChance
-	-- @paramsig number or boolean
-	-- @param operator Optional. Comparison operator: less, atMost, equal, atLeast, more.
-	-- @param number Optional. The number to compare against.
-	-- @param unlimited Optional. Set unlimited=1 to allow critical strike chance to exceed 100%.
-	--     Defaults to unlimited=0.
-	--     Valid values: 0, 1
-	-- @return The current critical strike chance (in percent).
-	-- @return A boolean value for the result of the comparison.
-	-- @see LastMeleeCritChance
-	-- @usage
-	-- if MeleeCritChance() >90 Spell(rip)
-
-	local function MeleeCritChance(condition)
-		return PaperDollCritChance("meleeCrit", 0, condition)
-	end
-
-	--- Get the current ranged critical strike chance of the player.
-	-- @name RangedCritChance
-	-- @paramsig number or boolean
-	-- @param operator Optional. Comparison operator: less, atMost, equal, atLeast, more.
-	-- @param number Optional. The number to compare against.
-	-- @param unlimited Optional. Set unlimited=1 to allow critical strike chance to exceed 100%.
-	--     Defaults to unlimited=0.
-	--     Valid values: 0, 1
-	-- @return The current critical strike chance (in percent).
-	-- @return A boolean value for the result of the comparison.
-	-- @see LastRangedCritChance
-	-- @usage
-	-- if RangedCritChance() >90 Spell(serpent_sting)
-
-	local function RangedCritChance(condition)
-		return PaperDollCritChance("rangedCrit", 0, condition)
-	end
-
-	--- Get the current spell critical strike chance of the player.
-	-- @name SpellCritChance
-	-- @paramsig number or boolean
-	-- @param operator Optional. Comparison operator: less, atMost, equal, atLeast, more.
-	-- @param number Optional. The number to compare against.
-	-- @param unlimited Optional. Set unlimited=1 to allow critical strike chance to exceed 100%.
-	--     Defaults to unlimited=0.
-	--     Valid values: 0, 1
-	-- @return The current critical strike chance (in percent).
-	-- @return A boolean value for the result of the comparison.
-	-- @see CritChance, LastSpellCritChance
-	-- @usage
-	-- if SpellCritChance() >30 Spell(immolate)
-
-	local function SpellCritChance(condition)
-		return PaperDollCritChance("spellCrit", 0, condition)
-	end
-
-	--- Get the current percent increase to spell haste of the player.
-	-- @name SpellHaste
-	-- @paramsig number or boolean
-	-- @param operator Optional. Comparison operator: less, atMost, equal, atLeast, more.
-	-- @param number Optional. The number to compare against.
-	-- @return The current percent increase to spell haste.
-	-- @return A boolean value for the result of the comparison.
-	-- @see BuffSpellHaste
-	-- @usage
-	-- if SpellHaste() >target.DebuffSpellHaste(moonfire) Spell(moonfire)
-
-	local function SpellHaste(condition)
-		return PaperDoll("spellHaste", 0, condition)
-	end
-
-	--- Get the current spellpower of the player.
-	-- @name Spellpower
-	-- @paramsig number or boolean
-	-- @param operator Optional. Comparison operator: less, atMost, equal, atLeast, more.
-	-- @param number Optional. The number to compare against.
-	-- @return The current spellpower.
-	-- @return A boolean value for the result of the comparison.
-	-- @see LastSpellpower
-	-- @usage
-	-- if {Spellpower() / LastSpellpower(living_bomb)} >1.25
-	--     Spell(living_bomb)
-
-	local function Spellpower(condition)
-		return PaperDoll("spellBonusDamage", 0, condition)
-	end
-
-	--- Get the current spirit of the player.
-	-- @name Spirit
-	-- @paramsig number or boolean
-	-- @param operator Optional. Comparison operator: less, atMost, equal, atLeast, more.
-	-- @param number Optional. The number to compare against.
-	-- @return The current spirit.
-	-- @return A boolean value for the result of the comparison.
-
-	local function Spirit(condition)
-		return PaperDoll("spirit", 0, condition)
-	end
-
-	--- Get the current stamina of the player.
-	-- @name Stamina
-	-- @paramsig number or boolean
-	-- @param operator Optional. Comparison operator: less, atMost, equal, atLeast, more.
-	-- @param number Optional. The number to compare against.
-	-- @return The current stamina.
-	-- @return A boolean value for the result of the comparison.
-
-	local function Stamina(condition)
-		return PaperDoll("stamina", 0, condition)
-	end
-
-	--- Get the current strength of the player.
-	-- @name Strength
-	-- @paramsig number or boolean
-	-- @param operator Optional. Comparison operator: less, atMost, equal, atLeast, more.
-	-- @param number Optional. The number to compare against.
-	-- @return The current strength.
-	-- @return A boolean value for the result of the comparison.
-
-	local function Strength(condition)
-		return PaperDoll("strength", 0, condition)
-	end
-
-	OvaleCondition:RegisterCondition("agility", false, Agility)
-	OvaleCondition:RegisterCondition("attackpower", false, AttackPower)
-	OvaleCondition:RegisterCondition("intellect", false, Intellect)
-	OvaleCondition:RegisterCondition("mastery", false, MasteryEffect)
-	OvaleCondition:RegisterCondition("masteryeffect", false, MasteryEffect)
-	OvaleCondition:RegisterCondition("meleecritchance", false, MeleeCritChance)
-	OvaleCondition:RegisterCondition("rangedcritchance", false, RangedCritChance)
-	OvaleCondition:RegisterCondition("spellcritchance", false, SpellCritChance)
-	OvaleCondition:RegisterCondition("spellhaste", false, SpellHaste)
-	OvaleCondition:RegisterCondition("spellpower", false, Spellpower)
-	OvaleCondition:RegisterCondition("spirit", false, Spirit)
-	OvaleCondition:RegisterCondition("stamina", false, Stamina)
-	OvaleCondition:RegisterCondition("strength", false, Strength)
-end
-
-do
-	local OvaleScripts = Ovale.OvaleScripts
-
-	local name = "Regression: PaperDoll"
-	local code = [[
-# Add a separate icon for each paper-doll stat.
-# Need to manually verify numbers against the in-game paper-doll.
-#
-AddIcon help=agility { Agility() }
-AddIcon help=attackPower { AttackPower() }
-AddIcon help=intellect { Intellect() }
-AddIcon help=masteryEffect { MasteryEffect() }
-AddIcon help=meleeCrit { MeleeCritChance() }
-AddIcon help=rangedCrit { RangedCritChance() }
-AddIcon help=spellCrit { SpellCritChance() }
-AddIcon help=spellHaste { SpellHaste() }
-AddIcon help=spellpower { Spellpower() }
-AddIcon help=spirit { Spirit() }
-AddIcon help=stamina { Stamina() }
-AddIcon help=strength { Strength() }
-]]
-	OvaleScripts:RegisterScript(nil, name, nil, code, "regression")
-end
diff --git a/conditions/RelativeLevel.lua b/conditions/RelativeLevel.lua
index bfc2430..88eaed0 100644
--- a/conditions/RelativeLevel.lua
+++ b/conditions/RelativeLevel.lua
@@ -12,7 +12,7 @@ local _, Ovale = ...

 do
 	local OvaleCondition = Ovale.OvaleCondition
-	local OvalePaperDoll = Ovale.OvalePaperDoll
+	local OvaleState = Ovale.OvaleState

 	local API_UnitLevel = UnitLevel
 	local Compare = OvaleCondition.Compare
@@ -37,9 +37,10 @@ do
 	local function RelativeLevel(condition)
 		local comparator, limit = condition[1], condition[2]
 		local target = ParseCondition(condition)
+		local state = OvaleState.state
 		local value, level
 		if target == "player" then
-			level = OvalePaperDoll.level
+			level = state.level
 		else
 			level = API_UnitLevel(target)
 		end
@@ -47,7 +48,7 @@ do
 			-- World boss, so treat it as three levels higher.
 			value = 3
 		else
-			value = level - OvalePaperDoll.level
+			value = level - state.level
 		end
 		return Compare(value, comparator, limit)
 	end
diff --git a/conditions/Snapshot.lua b/conditions/Snapshot.lua
new file mode 100644
index 0000000..d38b02e
--- /dev/null
+++ b/conditions/Snapshot.lua
@@ -0,0 +1,253 @@
+--[[--------------------------------------------------------------------
+    Ovale Spell Priority
+    Copyright (C) 2013 Johnny C. Lam
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License in the LICENSE
+    file accompanying this program.
+--]]--------------------------------------------------------------------
+
+local _, Ovale = ...
+
+do
+	local OvaleCondition = Ovale.OvaleCondition
+	local OvaleState = Ovale.OvaleState
+
+	local Compare = OvaleCondition.Compare
+
+	-- Returns the value of the given snapshot stat.
+	local function Snapshot(statName, defaultValue, condition)
+		local comparator, limit = condition[1], condition[2]
+		local state = OvaleState.state
+		local value = state.snapshot[statName] or defaultValue
+		return Compare(value, comparator, limit)
+	end
+
+	-- Returns the critical strike chance of the given snapshot stat.
+	local function SnapshotCritChance(statName, defaultValue, condition)
+		local comparator, limit = condition[1], condition[2]
+		local state = OvaleState.state
+		local value = state.snapshot[statName] or defaultValue
+		if condition.unlimited ~= 1 and value > 100 then
+			value = 100
+		end
+		return Compare(value, comparator, limit)
+	end
+
+	--- Get the current agility of the player.
+	-- @name Agility
+	-- @paramsig number or boolean
+	-- @param operator Optional. Comparison operator: less, atMost, equal, atLeast, more.
+	-- @param number Optional. The number to compare against.
+	-- @return The current agility.
+	-- @return A boolean value for the result of the comparison.
+
+	local function Agility(condition)
+		return Snapshot("agility", 0, condition)
+	end
+
+	--- Get the current attack power of the player.
+	-- @name AttackPower
+	-- @paramsig number or boolean
+	-- @param operator Optional. Comparison operator: less, atMost, equal, atLeast, more.
+	-- @param number Optional. The number to compare against.
+	-- @return The current attack power.
+	-- @return A boolean value for the result of the comparison.
+	-- @see LastAttackPower
+	-- @usage
+	-- if AttackPower() >10000 Spell(rake)
+	-- if AttackPower(more 10000) Spell(rake)
+
+	local function AttackPower(condition)
+		return Snapshot("attackPower", 0, condition)
+	end
+
+	--- Get the current intellect of the player.
+	-- @name Intellect
+	-- @paramsig number or boolean
+	-- @param operator Optional. Comparison operator: less, atMost, equal, atLeast, more.
+	-- @param number Optional. The number to compare against.
+	-- @return The current intellect.
+	-- @return A boolean value for the result of the comparison.
+
+	local function Intellect(condition)
+		return Snapshot("intellect", 0, condition)
+	end
+
+	--- Get the current mastery effect of the player.
+	-- Mastery effect is the effect of the player's mastery, typically a percent-increase to damage
+	-- or a percent-increase to chance to trigger some effect.
+	-- @name MasteryEffect
+	-- @paramsig number or boolean
+	-- @param operator Optional. Comparison operator: less, atMost, equal, atLeast, more.
+	-- @param number Optional. The number to compare against.
+	-- @return The current mastery effect.
+	-- @return A boolean value for the result of the comparison.
+	-- @see LastMasteryEffect
+	-- @usage
+	-- if {DamageMultiplier(rake) * {1 + MasteryEffect()/100}} >1.8
+	--     Spell(rake)
+
+	local function MasteryEffect(condition)
+		return Snapshot("masteryEffect", 0, condition)
+	end
+
+	--- Get the current melee critical strike chance of the player.
+	-- @name MeleeCritChance
+	-- @paramsig number or boolean
+	-- @param operator Optional. Comparison operator: less, atMost, equal, atLeast, more.
+	-- @param number Optional. The number to compare against.
+	-- @param unlimited Optional. Set unlimited=1 to allow critical strike chance to exceed 100%.
+	--     Defaults to unlimited=0.
+	--     Valid values: 0, 1
+	-- @return The current critical strike chance (in percent).
+	-- @return A boolean value for the result of the comparison.
+	-- @see LastMeleeCritChance
+	-- @usage
+	-- if MeleeCritChance() >90 Spell(rip)
+
+	local function MeleeCritChance(condition)
+		return SnapshotCritChance("meleeCrit", 0, condition)
+	end
+
+	--- Get the current ranged critical strike chance of the player.
+	-- @name RangedCritChance
+	-- @paramsig number or boolean
+	-- @param operator Optional. Comparison operator: less, atMost, equal, atLeast, more.
+	-- @param number Optional. The number to compare against.
+	-- @param unlimited Optional. Set unlimited=1 to allow critical strike chance to exceed 100%.
+	--     Defaults to unlimited=0.
+	--     Valid values: 0, 1
+	-- @return The current critical strike chance (in percent).
+	-- @return A boolean value for the result of the comparison.
+	-- @see LastRangedCritChance
+	-- @usage
+	-- if RangedCritChance() >90 Spell(serpent_sting)
+
+	local function RangedCritChance(condition)
+		return SnapshotCritChance("rangedCrit", 0, condition)
+	end
+
+	--- Get the current spell critical strike chance of the player.
+	-- @name SpellCritChance
+	-- @paramsig number or boolean
+	-- @param operator Optional. Comparison operator: less, atMost, equal, atLeast, more.
+	-- @param number Optional. The number to compare against.
+	-- @param unlimited Optional. Set unlimited=1 to allow critical strike chance to exceed 100%.
+	--     Defaults to unlimited=0.
+	--     Valid values: 0, 1
+	-- @return The current critical strike chance (in percent).
+	-- @return A boolean value for the result of the comparison.
+	-- @see CritChance, LastSpellCritChance
+	-- @usage
+	-- if SpellCritChance() >30 Spell(immolate)
+
+	local function SpellCritChance(condition)
+		return SnapshotCritChance("spellCrit", 0, condition)
+	end
+
+	--- Get the current percent increase to spell haste of the player.
+	-- @name SpellHaste
+	-- @paramsig number or boolean
+	-- @param operator Optional. Comparison operator: less, atMost, equal, atLeast, more.
+	-- @param number Optional. The number to compare against.
+	-- @return The current percent increase to spell haste.
+	-- @return A boolean value for the result of the comparison.
+	-- @see BuffSpellHaste
+	-- @usage
+	-- if SpellHaste() >target.DebuffSpellHaste(moonfire) Spell(moonfire)
+
+	local function SpellHaste(condition)
+		return Snapshot("spellHaste", 0, condition)
+	end
+
+	--- Get the current spellpower of the player.
+	-- @name Spellpower
+	-- @paramsig number or boolean
+	-- @param operator Optional. Comparison operator: less, atMost, equal, atLeast, more.
+	-- @param number Optional. The number to compare against.
+	-- @return The current spellpower.
+	-- @return A boolean value for the result of the comparison.
+	-- @see LastSpellpower
+	-- @usage
+	-- if {Spellpower() / LastSpellpower(living_bomb)} >1.25
+	--     Spell(living_bomb)
+
+	local function Spellpower(condition)
+		return Snapshot("spellBonusDamage", 0, condition)
+	end
+
+	--- Get the current spirit of the player.
+	-- @name Spirit
+	-- @paramsig number or boolean
+	-- @param operator Optional. Comparison operator: less, atMost, equal, atLeast, more.
+	-- @param number Optional. The number to compare against.
+	-- @return The current spirit.
+	-- @return A boolean value for the result of the comparison.
+
+	local function Spirit(condition)
+		return Snapshot("spirit", 0, condition)
+	end
+
+	--- Get the current stamina of the player.
+	-- @name Stamina
+	-- @paramsig number or boolean
+	-- @param operator Optional. Comparison operator: less, atMost, equal, atLeast, more.
+	-- @param number Optional. The number to compare against.
+	-- @return The current stamina.
+	-- @return A boolean value for the result of the comparison.
+
+	local function Stamina(condition)
+		return Snapshot("stamina", 0, condition)
+	end
+
+	--- Get the current strength of the player.
+	-- @name Strength
+	-- @paramsig number or boolean
+	-- @param operator Optional. Comparison operator: less, atMost, equal, atLeast, more.
+	-- @param number Optional. The number to compare against.
+	-- @return The current strength.
+	-- @return A boolean value for the result of the comparison.
+
+	local function Strength(condition)
+		return Snapshot("strength", 0, condition)
+	end
+
+	OvaleCondition:RegisterCondition("agility", false, Agility)
+	OvaleCondition:RegisterCondition("attackpower", false, AttackPower)
+	OvaleCondition:RegisterCondition("intellect", false, Intellect)
+	OvaleCondition:RegisterCondition("mastery", false, MasteryEffect)
+	OvaleCondition:RegisterCondition("masteryeffect", false, MasteryEffect)
+	OvaleCondition:RegisterCondition("meleecritchance", false, MeleeCritChance)
+	OvaleCondition:RegisterCondition("rangedcritchance", false, RangedCritChance)
+	OvaleCondition:RegisterCondition("spellcritchance", false, SpellCritChance)
+	OvaleCondition:RegisterCondition("spellhaste", false, SpellHaste)
+	OvaleCondition:RegisterCondition("spellpower", false, Spellpower)
+	OvaleCondition:RegisterCondition("spirit", false, Spirit)
+	OvaleCondition:RegisterCondition("stamina", false, Stamina)
+	OvaleCondition:RegisterCondition("strength", false, Strength)
+end
+
+do
+	local OvaleScripts = Ovale.OvaleScripts
+
+	local name = "Regression: Snapshot"
+	local code = [[
+# Add a separate icon for each paper-doll stat.
+# Need to manually verify numbers against the in-game paper-doll.
+#
+AddIcon help=agility { Agility() }
+AddIcon help=attackPower { AttackPower() }
+AddIcon help=intellect { Intellect() }
+AddIcon help=masteryEffect { MasteryEffect() }
+AddIcon help=meleeCrit { MeleeCritChance() }
+AddIcon help=rangedCrit { RangedCritChance() }
+AddIcon help=spellCrit { SpellCritChance() }
+AddIcon help=spellHaste { SpellHaste() }
+AddIcon help=spellpower { Spellpower() }
+AddIcon help=spirit { Spirit() }
+AddIcon help=stamina { Stamina() }
+AddIcon help=strength { Strength() }
+]]
+	OvaleScripts:RegisterScript(nil, name, nil, code, "regression")
+end
diff --git a/conditions/TimeWithHaste.lua b/conditions/TimeWithHaste.lua
index f4b05cb..6a4af7a 100644
--- a/conditions/TimeWithHaste.lua
+++ b/conditions/TimeWithHaste.lua
@@ -11,7 +11,7 @@ local _, Ovale = ...

 do
 	local OvaleCondition = Ovale.OvaleCondition
-	local OvalePaperDoll = Ovale.OvalePaperDoll
+	local OvaleState = Ovale.OvaleState

 	local Compare = OvaleCondition.Compare

@@ -34,12 +34,13 @@ do
 	local function TimeWithHaste(condition)
 		local seconds, comparator, limit = condition[1], condition[2], condition[3]
 		local haste = condition.haste or "spell"
+		local state = OvaleState.state
 		seconds = seconds or 0
 		local value = seconds
 		if haste == "spell" then
-			value = seconds / OvalePaperDoll:GetSpellHasteMultiplier()
+			value = seconds / state:GetSpellHasteMultiplier()
 		elseif haste == "melee" then
-			value = seconds / OvalePaperDoll:GetMeleeHasteMultiplier()
+			value = seconds / state:GetMeleeHasteMultiplier()
 		else
 			Ovale:Logf("Unknown haste parameter haste=%s", haste)
 		end
diff --git a/conditions/WeaponDamage.lua b/conditions/WeaponDamage.lua
index 94b0dcd..b01b272 100644
--- a/conditions/WeaponDamage.lua
+++ b/conditions/WeaponDamage.lua
@@ -11,7 +11,7 @@ local _, Ovale = ...

 do
 	local OvaleCondition = Ovale.OvaleCondition
-	local OvalePaperDoll = Ovale.OvalePaperDoll
+	local OvaleState = Ovale.OvaleState

 	local Compare = OvaleCondition.Compare

@@ -33,16 +33,17 @@ do
 	local function WeaponDamage(condition)
 		local hand = condition[1]
 		local comparator, limit
+		local state = OvaleState.state
 		local value = 0
 		if hand == "offhand" or hand == "off" then
 			comparator, limit = condition[2], condition[3]
-			value = OvalePaperDoll.snapshot.offHandWeaponDamage
+			value = state.snapshot.offHandWeaponDamage
 		elseif hand == "mainhand" or hand == "main" then
 			comparator, limit = condition[2], condition[3]
-			value = OvalePaperDoll.snapshot.mainHandWeaponDamage
+			value = state.snapshot.mainHandWeaponDamage
 		else
 			comparator, limit = condition[1], condition[2]
-			value = OvalePaperDoll.snapshot.mainHandWeaponDamage
+			value = state.snapshot.mainHandWeaponDamage
 		end
 		return Compare(value, comparator, limit)
 	end
diff --git a/conditions/conditions.xml b/conditions/conditions.xml
index c6a80dc..27362f7 100644
--- a/conditions/conditions.xml
+++ b/conditions/conditions.xml
@@ -65,7 +65,6 @@
 	<Script file="ManaPercent.lua" />
 	<Script file="NextTick.lua" />
 	<Script file="PTR.lua" />
-	<Script file="PaperDoll.lua" />
 	<Script file="PetPresent.lua" />
 	<Script file="Power.lua" />
 	<Script file="PowerCost.lua" />
@@ -75,6 +74,7 @@
 	<Script file="RemainingCastTime.lua" />
 	<Script file="RuneCount.lua" />
 	<Script file="Runes.lua" />
+	<Script file="Snapshot.lua" />
 	<Script file="Speed.lua" />
 	<Script file="SpellChargeCooldown.lua" />
 	<Script file="SpellCharges.lua" />