Simulation = { casts = 500, minDamage = 545, damageRange = 0, includeCrits = false, critChance = 0, castTime = 2.5, spellDamage = 528, multiplier = 3/3.5*.95*1.06, critMultiplier = 2, equipedItems = {"Talisman of Ephemeral Power","Neltharion's Tear"}, hitChance = 1, items = { ["Talisman of Ephemeral Power"] = { name = "Talisman of Ephemeral Power", usable = true, applyBonus = function(self,sim) sim.spellDamage = sim.spellDamage + 175 end, removeBonus = function(self, sim) sim.spellDamage = sim.spellDamage - 175 end, cooldown = 90, duration = 15, sharedCooldown = "damageTrinket", }, ["Neltharion's Tear"] = { name = "Neltharion's Tear", usable = false, applyBonus = function(self, sim) sim.spellDamage = sim.spellDamage + 44 sim.hitChance = sim.hitChance + 0.02 end, removeBonus = function(self, sim) sim.spellDamage = sim.spellDamage - 44 sim.hitChance = sim.hitChance - 0.02 end, }, ["Zandalarian Hero Charm"] = { name = "Zandalarian Hero Charm", usable = true, cooldown = 120, duration = 20, applyBonus = function(self, sim) self.damageBonus = 200 self.beforedamage = sim.spellDamage end, onCast = function(self, sim) sim.spellDamage = self.beforeDamage + self.damageBonus self.damageBonus = self.damageBonus - 25 end, removeBonus = function(self, sim) sim.spellDamage = self.beforeDamage end, sharedCooldown = "damageTrinket", }, ["Mind Quickening Gem"] = { name = "Mind Quickening Gem", usable = true, cooldown = 300, duration = 20, applyBonus = function(self, sim) sim.castTime = sim.castTime * .75 end, removeBonus = function(self, sim) sim.castTime = sim.castTime / .75 end, sharedCooldown = "damageTrinket", bonusAppliedAtStart = true, } } } ---------------------------------------------------------------------------------- local itemClass = {} itemClass.__index = itemClass local activeTypes = {} function itemClass:new(def) setmetatable(def,self) end function itemClass:isReady(sim) return (not self.startTime or sim.currentTime - self.startTime >= self.cooldown) and (not activeTypes[self.sharedCooldown]) end function itemClass:use(sim) if(not self.usable) then error(self.name.." is not a useable item",2) end self.startTime = sim.currentTime self:applyBonus(sim) activeTypes[self.sharedCooldown] = true self.active = true end function itemClass:checkActive(sim) local time = sim.currentTime + (self.bonusAppliedAtStart and 0 or sim.castTime) if(self.active and (time - self.startTime >= self.duration)) then activeTypes[self.sharedCooldown] = nil self:removeBonus(sim) self.active = nil end end ---------------------------------------------------------------------------------- local sim = Simulation for _,item in sim.items do itemClass:new(item) end function sim:getItem(itemName) local item = self.items[itemName] if(not item) then error("No item data for item '"..itemName.."'",3) end return item end function sim:cast() local rand = math.random(0,100)/100 local resist = rand > self.hitChance local crit = rand < self.critChance for _,itemName in self.equipedItems do local item = self:getItem(itemName) if(item.usable and item:isReady(self)) then item:use(self) end if(item.usable and item.active) then item:checkActive(self) end if(resist and item.onResist) then item:onResist(self) elseif(item.onHit) then item:onHit(self) end if(crit and item.onCrit) then item:onCrit(self) end if(item.onCast) then item:onCast(self) end end self.currentTime = self.currentTime + self.castTime local damage = resist and 0 or self.minDamage + rand * self.damageRange + self.multiplier * self.spellDamage local damage = crit and damage * self.critMultiplier or damage self.totalDamage = self.totalDamage + damage return damage end function sim:Run() self.totalDamage = 0 self.currentTime = 0 for _,itemName in self.equipedItems do local item = self:getItem(itemName) if(not item.usable) then item:applyBonus(sim) end end for i=1,self.casts do self:cast() end self:printResults() end local fprint = function(a,b,c,d,e,f) print(string.format(a,b,c,d,e,f)) end function sim:printResults() fprint("Total Time: %10d",self.currentTime) fprint("Total Damage: %10.3f",self.totalDamage) fprint("Average DPS: %10.3f",self.totalDamage/self.currentTime) end