## Fire Mage ## https://github.com/simulationcraft/simc/ ## June 29, 2021 ## Changes: ## - Use fight_remains rather than target.time_to_die for last-second ability usages. ## - Loosen cast requirements for Combustion (i.e., don't hold it for a fresh Meteor cast). ## - Enable strict checking on a few forks in the APL to reduce workload. ## - Adjust a Pyroblast entry that does not model well in-game (sim tested at no significant DPS impact). ## - Convert all buff.X.react entries to buff.X.up, an almost insignificant DPS gain in sims. ## - Use hardcoded variables to reduce CPU workload. ## Executed before combat begins. Accepts non-harmful actions only. actions.precombat+=/arcane_intellect ## APL Variable Option: If set to a non-zero value, the Combustion action and cooldowns that are constrained to only be used when Combustion is up will not be used during the simulation. ## ## actions.precombat+=/variable,name=disable_combustion,op=reset # APL Variable Option: This variable specifies whether Combustion should be used during Firestarter. ## actions.precombat+=/variable,name=firestarter_combustion,op=set,if=!talent.pyroclasm,value=1,value_else=0 # APL Variable Option: This variable specifies the number of targets at which Hot Streak Flamestrikes outside of Combustion should be used. ## actions.precombat+=/variable,name=hot_streak_flamestrike,op=set,if=talent.flame_patch,value=2,value_else=3 # APL Variable Option: This variable specifies the number of targets at which Hard Cast Flamestrikes outside of Combustion should be used as filler. ## actions.precombat+=/variable,name=hard_cast_flamestrike,op=set,if=talent.flame_patch,value=2,value_else=3 # APL Variable Option: This variable specifies the number of targets at which Hot Streak Flamestrikes are used during Combustion. ## actions.precombat+=/variable,name=combustion_flamestrike,op=set,if=talent.flame_patch,value=3,value_else=6 # APL Variable Option: This variable specifies the number of targets at which Arcane Explosion outside of Combustion should be used. ## actions.precombat+=/variable,name=arcane_explosion,op=set,if=talent.flame_patch,value=99,value_else=2 # APL Variable Option: This variable specifies the percentage of mana below which Arcane Explosion will not be used. ## actions.precombat+=/variable,name=arcane_explosion_mana,default=40,op=reset # APL Variable Option: The number of targets Shifting Power should be used on during Combustion. ## actions.precombat+=/variable,name=combustion_shifting_power,default=2,op=reset # APL Variable Option: The time remaining on a cast when Combustion can be used in seconds. ## actions.precombat+=/variable,name=combustion_cast_remains,default=0.7,op=reset # APL Variable Option: This variable specifies the number of seconds of Fire Blast that should be pooled past the default amount. ## actions.precombat+=/variable,name=overpool_fire_blasts,default=0,op=reset # APL Variable Option: How long before Combustion should Empyreal Ordnance be used? ## actions.precombat+=/variable,name=empyreal_ordnance_delay,default=18,op=reset ## If Combustion is disabled, schedule the first Combustion far after the fight ends. ## ## actions.precombat+=/variable,name=time_to_combustion,value=fight_remains+100,if=variable.disable_combustion # The duration of a Sun King's Blessing Combustion. ## actions.precombat+=/variable,name=skb_duration,op=set,value=5 # Whether a usable item used to buff Combustion is equipped. ## actions.precombat+=/variable,name=combustion_on_use,value=equipped.gladiators_badge|equipped.macabre_sheet_music|equipped.inscrutable_quantum_device|equipped.sunblood_amethyst|equipped.empyreal_ordnance|equipped.flame_of_battle|equipped.wakeners_frond|equipped.instructors_divine_bell|equipped.shadowed_orb_of_torment # How long before Combustion should trinkets that trigger a shared category cooldown on other trinkets not be used? ## actions.precombat+=/variable,name=on_use_cutoff,op=set,value=20,if=variable.combustion_on_use ## actions.precombat+=/variable,name=on_use_cutoff,op=set,value=25,if=equipped.macabre_sheet_music ## actions.precombat+=/variable,name=on_use_cutoff,op=set,value=20+variable.empyreal_ordnance_delay,if=equipped.empyreal_ordnance actions.precombat+=/use_item,name=soul_igniter,if=!variable.combustion_on_use&!equipped.dreadfire_vessel&!talent.firestarter actions.precombat+=/mirror_image actions.precombat+=/pyroblast,if=!prev.pyroblast ## Executed every time the actor is available. actions=counterspell,if=!runeforge.disciplinary_command ## The combustion_timing action list schedules when Combustion will be used and stores the result in variable.time_to_combustion. ## actions+=/call_action_list,name=combustion_timing # Variable that estimates whether Shifting Power will be used before Combustion is ready. ## actions+=/variable,name=shifting_power_before_combustion,op=set,value=(active_enemiescooldown.shifting_power.duration)&variable.time_to_combustion-cooldown.shifting_power.remains>action.shifting_power.full_reduction&(cooldown.rune_of_power.remains-cooldown.shifting_power.remains>5|!talent.rune_of_power) actions+=/shifting_power,if=buff.combustion.down&action.fire_blast.charges<=1&!(buff.infernal_cascade.up&buff.hot_streak.react)&variable.shifting_power_before_combustion actions+=/radiant_spark,if=buff.combustion.down&(variable.time_to_combustioncooldown-10) actions+=/deathborne,if=buff.combustion.down&buff.rune_of_power.down&variable.time_to_combustioncooldown-5 actions+=/use_item,name=empyreal_ordnance,if=variable.time_to_combustion<=variable.empyreal_ordnance_delay&variable.time_to_combustion>variable.empyreal_ordnance_delay-5 actions+=/use_item,name=shadowed_orb_of_torment,if=(variable.time_to_combustion<=variable.combustion_precast_time+2|fight_remains=variable.on_use_cutoff actions+=/use_item,name=macabre_sheet_music,if=variable.time_to_combustion<=5 # If using a steroid on-use item, always use Dreadfire Vessel outside of Combustion. Otherwise, prioritize using Dreadfire Vessel with Combustion only if Infernal Cascade is enabled and a usage won't be lost over the duration of the fight. This adds a small value to the cooldown of Dreadfire Vessel when doing this calculation because it is unrealstic to assume that it will be used perfectly on cooldown. actions+=/use_item,name=dreadfire_vessel,if=variable.time_to_combustion>=variable.on_use_cutoff&(buff.infernal_cascade.stack=buff.infernal_cascade.max_stack|!conduit.infernal_cascade|variable.combustion_on_use|variable.time_to_combustion>interpolated_fight_remains%%(cooldown+10)) ## New Soul Igniter entry, relies on shared item CD information that is not yet supported. Replaces the previous line when functional. actions+=/use_item,name=soul_igniter,if=(variable.time_to_combustion>=30*(variable.on_use_cutoff>0)|(!trinket.1.is.soul_igniter&trinket.1.usable&trinket.1.cooldown.remains|!trinket.2.is.soul_igniter&trinket.2.usable&trinket.2.cooldown.remains))&(!equipped.dreadfire_vessel|cooldown.dreadfire_vessel.remains>5) # Trigger Soul Igniter early with Infernal Cascade or when it was precast. actions+=/cancel_buff,name=soul_ignition,if=!conduit.infernal_cascade&time<5|buff.infernal_cascade.stack=buff.infernal_cascade.max_stack # Items that do not benefit Combustion should just be used outside of Combustion at some point. actions+=/use_item,name=inscrutable_quantum_device,if=equipped.gladiators_badge&variable.time_to_combustion>=variable.on_use_cutoff actions+=/use_item,name=flame_of_battle,if=equipped.gladiators_badge&variable.time_to_combustion>=variable.on_use_cutoff actions+=/use_item,name=wakeners_frond,if=equipped.gladiators_badge&variable.time_to_combustion>=variable.on_use_cutoff actions+=/use_item,name=instructors_divine_bell,if=equipped.gladiators_badge&variable.time_to_combustion>=variable.on_use_cutoff actions+=/use_item,name=sunblood_amethyst,if=equipped.gladiators_badge&variable.time_to_combustion>=variable.on_use_cutoff actions+=/use_items,if=variable.time_to_combustion>=variable.on_use_cutoff # Use Frost Nova to trigger Grisly Icicle. actions+=/frost_nova,if=runeforge.grisly_icicle&buff.combustion.down&(variable.time_to_combustion>cooldown|variable.time_to_combustioncooldown.buff_disciplinary_command.duration|variable.time_to_combustion<5) actions+=/arcane_explosion,if=runeforge.disciplinary_command&cooldown.buff_disciplinary_command.ready&buff.disciplinary_command_arcane.down&!buff.disciplinary_command.up&(variable.time_to_combustion+execute_time+action.frostbolt.cast_time>cooldown.buff_disciplinary_command.duration|variable.time_to_combustion<5&!talent.rune_of_power) actions+=/frostbolt,if=runeforge.disciplinary_command&cooldown.buff_disciplinary_command.remainscooldown.buff_disciplinary_command.duration|variable.time_to_combustion<5) actions+=/frost_nova,if=runeforge.disciplinary_command&cooldown.buff_disciplinary_command.ready&buff.disciplinary_command_frost.down&!buff.disciplinary_command.up&(variable.time_to_combustion>cooldown.buff_disciplinary_command.duration|variable.time_to_combustion<5) actions+=/call_action_list,name=combustion_phase,if=variable.time_to_combustion<=0|variable.time_to_combustion=buff.rune_of_power.duration&variable.time_to_combustion>action.fire_blast.full_recharge_time|variable.time_to_combustion>fight_remains) # Pool as many Fire Blasts as possible for Combustion. Subtract out of the fractional component of the number of Fire Blasts that will naturally recharge during the Combustion phase because pooling anything past that will not grant an extra Fire Blast during Combustion. ## actions+=/variable,use_off_gcd=1,use_while_casting=1,name=fire_blast_pooling,value=action.fire_blast.charges_fractional+(variable.time_to_combustion+action.shifting_power.full_reduction*variable.shifting_power_before_combustion)%cooldown.fire_blast.duration-1=variable.combustion_flamestrike,value=variable.time_to_combustion0 # Adjust the variable that controls Fire Blast usage to ensure its charges are also pooled for Rune of Power. ## actions+=/variable,use_off_gcd=1,use_while_casting=1,name=fire_blast_pooling,op=set,value=cooldown.rune_of_power.remains0&active_enemies>=variable.hard_cast_flamestrike&!firestarter.active&!buff.hot_streak.up&(buff.heating_up.up&action.flamestrike.execute_remains<0.5|charges_fractional>=2) # During Firestarter, Fire Blasts are used similarly to during Combustion. Generally, they are used to generate Hot Streaks when crits will not be wasted and with Blaster Master, they should be spread out to maintain the Blaster Master buff. actions+=/fire_blast,use_off_gcd=1,use_while_casting=1,if=firestarter.active&charges>=1&!variable.fire_blast_pooling&(!action.fireball.executing&!action.pyroblast.in_flight&buff.heating_up.up|action.fireball.executing&!buff.hot_streak.up|action.pyroblast.in_flight&buff.heating_up.up&!buff.hot_streak.up) # Avoid capping Fire Blast charges while channeling Shifting Power actions+=/fire_blast,use_while_casting=1,if=action.shifting_power.executing&full_recharge_time10 actions+=/call_action_list,name=standard_rotation,if=variable.time_to_combustion>0&buff.rune_of_power.down actions+=/scorch actions.active_talents=living_bomb,if=active_enemies>1&buff.combustion.down&(variable.time_to_combustion>cooldown.living_bomb.duration|variable.time_to_combustion<=0) actions.active_talents+=/meteor,if=variable.time_to_combustion<=0|(cooldown.meteor.durationaction.meteor.cooldown|fight_remains1.5*gcd.max*(buff.sun_kings_blessing.max_stack-buff.sun_kings_blessing.stack)) actions.combustion_phase+=/bag_of_tricks,if=buff.combustion.down actions.combustion_phase+=/living_bomb,if=active_enemies>1&buff.combustion.down # Without Infernal Cascade, just use Fire Blasts when they won't munch crits and when Firestorm is down. actions.combustion_phase+=/fire_blast,use_off_gcd=1,use_while_casting=1,if=!conduit.infernal_cascade&charges>=1&buff.combustion.up&!buff.firestorm.up&!buff.hot_streak.up&hot_streak_spells_in_flight+buff.heating_up.up<2 # With Infernal Cascade, Fire Blast use should be additionally constrained so that it is not be used unless Infernal Cascade is about to expire or there are more than enough Fire Blasts to extend Infernal Cascade to the end of Combustion. ## actions.combustion_phase+=/variable,use_off_gcd=1,use_while_casting=1,name=expected_fire_blasts,op=set,value=action.fire_blast.charges_fractional+(variable.extended_combustion_remains-buff.infernal_cascade.duration)%cooldown.fire_blast.duration,if=conduit.infernal_cascade ## actions.combustion_phase+=/variable,use_off_gcd=1,use_while_casting=1,name=needed_fire_blasts,op=set,value=ceil(variable.extended_combustion_remains%(buff.infernal_cascade.duration-gcd.max)),if=conduit.infernal_cascade actions.combustion_phase+=/fire_blast,use_off_gcd=1,use_while_casting=1,if=conduit.infernal_cascade&charges>=1&(variable.expected_fire_blasts>=variable.needed_fire_blasts|variable.extended_combustion_remains<=buff.infernal_cascade.duration|buff.infernal_cascade.stack<2|buff.infernal_cascade.remains=variable.combustion_shifting_power&covenant.night_fae)&buff.combustion.up&(!buff.firestorm.up|buff.infernal_cascade.remains<0.5)&!buff.hot_streak.up&hot_streak_spells_in_flight+buff.heating_up.up<2 actions.combustion_phase+=/call_action_list,name=active_talents actions.combustion_phase+=/combustion,use_off_gcd=1,use_while_casting=1,if=buff.combustion.down&variable.time_to_combustion<=0&(!runeforge.disciplinary_command|buff.disciplinary_command.up|buff.disciplinary_command_frost.up&talent.rune_of_power&cooldown.buff_disciplinary_command.ready)&(!runeforge.grisly_icicle|debuff.grisly_icicle.up)&(!covenant.necrolord|cooldown.deathborne.remains|buff.deathborne.up)&(!covenant.venthyr|cooldown.mirrors_of_torment.remains)&(action.meteor.in_flight&action.meteor.in_flight_remains<=variable.combustion_cast_remains|action.scorch.executing&action.scorch.execute_remains8|cooldown.combustion.remains<5 actions.combustion_phase+=/flamestrike,if=(buff.hot_streak.up&active_enemies>=variable.combustion_flamestrike)|(buff.firestorm.up&active_enemies>=variable.combustion_flamestrike-runeforge.firestorm) actions.combustion_phase+=/pyroblast,if=buff.sun_kings_blessing_ready.up&buff.sun_kings_blessing_ready.remains>cast_time actions.combustion_phase+=/pyroblast,if=buff.firestorm.up actions.combustion_phase+=/pyroblast,if=buff.pyroclasm.up&buff.pyroclasm.remains>cast_time&(buff.combustion.remains>cast_time|buff.combustion.down)&active_enemies=variable.combustion_shifting_power&action.phoenix_flames.full_recharge_time>full_reduction,interrupt_if=action.fire_blast.charges=action.fire_blast.max_charges actions.combustion_phase+=/phoenix_flames,if=buff.combustion.up&travel_time1)&buff.heating_up.up+hot_streak_spells_in_flight<2 actions.combustion_phase+=/flamestrike,if=buff.combustion.down&cooldown.combustion.remains=variable.combustion_flamestrike actions.combustion_phase+=/fireball,if=buff.combustion.down&cooldown.combustion.remainscast_time&buff.combustion.up|buff.combustion.down&cooldown.combustion.remains1 actions.combustion_phase+=/dragons_breath,if=buff.combustion.remains=variable.combustion_flamestrike)-variable.combustion_cast_remains ## actions.combustion_timing+=/variable,name=combustion_time,value=variable.combustion_ready_time # Delay Combustion for after Firestarter unless variable.firestarter_combustion is set. ## actions.combustion_timing+=/variable,name=combustion_time,op=max,value=firestarter.remains,if=talent.firestarter&!variable.firestarter_combustion # Delay Combustion for Radiant Spark if it will come off cooldown soon. ## actions.combustion_timing+=/variable,name=combustion_time,op=max,value=cooldown.radiant_spark.remains,if=covenant.kyrian&cooldown.radiant_spark.remains-10=2 # Delay Combustion for the Empyreal Ordnance buff if the player is using that trinket. ## actions.combustion_timing+=/variable,name=combustion_time,op=max,value=variable.empyreal_ordnance_delay-(cooldown.empyreal_ordnance.duration-cooldown.empyreal_ordnance.remains)*!cooldown.empyreal_ordnance.ready,if=equipped.empyreal_ordnance # Delay Combustion for Gladiators Badge, unless it would be delayed longer than 20 seconds. ## actions.combustion_timing+=/variable,name=combustion_time,op=max,value=cooldown.gladiators_badge.remains,if=equipped.gladiators_badge&cooldown.gladiators_badge.remains-20=3&raid_event.adds.duration>15 # Raid Events: Always use Combustion with vulnerability raid events, override any delays listed above to make sure it gets used here. ## actions.combustion_timing+=/variable,name=combustion_time,value=raid_event.vulnerable.in*!raid_event.vulnerable.up,if=raid_event.vulnerable.exists&variable.combustion_ready_timefight_remains-20 # Add the current time to the scheduled Combustion to put it in absolute time so that it is still accurate after a little time passes. ## actions.combustion_timing+=/variable,name=combustion_time,op=add,value=time # Finally, convert from absolute time and store the relative time in variable.time_to_combustion. Unlike the rest of the calculations, which happen less frequently to speed up the simulation, this happens off-GCD and while casting. ## actions.combustion_timing+=/variable,use_off_gcd=1,use_while_casting=1,name=time_to_combustion,value=(variable.combustion_time-time)*buff.combustion.down actions.rop_phase=flamestrike,if=active_enemies>=variable.hot_streak_flamestrike&(buff.hot_streak.up|buff.firestorm.up) actions.rop_phase+=/fireball,if=buff.deathborne.up&runeforge.deaths_fathom&variable.time_to_combustion=2 actions.rop_phase+=/pyroblast,if=buff.sun_kings_blessing_ready.up&buff.sun_kings_blessing_ready.remains>cast_time actions.rop_phase+=/pyroblast,if=buff.firestorm.up actions.rop_phase+=/pyroblast,if=buff.hot_streak.up # Use one Fire Blast early in RoP if you don't have either Heating Up or Hot Streak yet and either: (a) have more than two already, (b) have Alexstrasza's Fury ready to use, or (c) Searing Touch is active. Don't do this while hard casting Flamestrikes or when Sun King's Blessing is ready. actions.rop_phase+=/fire_blast,use_off_gcd=1,use_while_casting=1,if=!variable.fire_blast_pooling&buff.sun_kings_blessing_ready.down&active_enemies=2|(talent.alexstraszas_fury&cooldown.dragons_breath.ready)|searing_touch.active)) # Use Fire Blast either during a Fireball/Pyroblast cast when Heating Up is active or during execute with Searing Touch. actions.rop_phase+=/fire_blast,use_off_gcd=1,use_while_casting=1,if=!variable.fire_blast_pooling&!firestarter.active&(((action.fireball.executing&(action.fireball.execute_remains<0.5|!runeforge.firestorm)|action.pyroblast.executing&(action.pyroblast.execute_remains<0.5|!runeforge.firestorm))&buff.heating_up.up)|(searing_touch.active&(buff.heating_up.up&!action.scorch.executing|!buff.hot_streak.up&!buff.heating_up.up&action.scorch.executing&!hot_streak_spells_in_flight))) actions.rop_phase+=/call_action_list,name=active_talents actions.rop_phase+=/pyroblast,if=buff.pyroclasm.up&cast_time=variable.hard_cast_flamestrike|active_enemies>=variable.hot_streak_flamestrike) actions.rop_phase+=/scorch,if=searing_touch.active actions.rop_phase+=/dragons_breath,if=active_enemies>2 actions.rop_phase+=/arcane_explosion,if=active_enemies>=variable.arcane_explosion&mana.pct>=variable.arcane_explosion_mana&target.within10 # With enough targets, it is a gain to cast Flamestrike as filler instead of Fireball. actions.rop_phase+=/flamestrike,if=active_enemies>=variable.hard_cast_flamestrike actions.rop_phase+=/fireball actions.standard_rotation=flamestrike,if=active_enemies>=variable.hot_streak_flamestrike&(buff.hot_streak.up|buff.firestorm.up) actions.standard_rotation+=/fireball,if=buff.deathborne.up&runeforge.deaths_fathom&variable.time_to_combustion=2 actions.standard_rotation+=/pyroblast,if=buff.firestorm.up actions.standard_rotation+=/pyroblast,if=buff.hot_streak.up&buff.hot_streak.remainsbuff.sun_kings_blessing_ready.remains|!talent.rune_of_power)&variable.time_to_combustion+cast_time>buff.sun_kings_blessing_ready.remains actions.standard_rotation+=/pyroblast,if=buff.hot_streak.up&searing_touch.active actions.standard_rotation+=/pyroblast,if=buff.pyroclasm.up&cast_time1)&(active_dot.ignite<2|active_enemies>=variable.hard_cast_flamestrike|active_enemies>=variable.hot_streak_flamestrike) actions.standard_rotation+=/call_action_list,name=active_talents actions.standard_rotation+=/dragons_breath,if=active_enemies>1 actions.standard_rotation+=/scorch,if=searing_touch.active actions.standard_rotation+=/arcane_explosion,if=active_enemies>=variable.arcane_explosion&mana.pct>=variable.arcane_explosion_mana&target.within10 # With enough targets, it is a gain to cast Flamestrike as filler instead of Fireball. actions.standard_rotation+=/flamestrike,if=active_enemies>=variable.hard_cast_flamestrike actions.standard_rotation+=/fireball