dr-scripts icon indicating copy to clipboard operation
dr-scripts copied to clipboard

[data][base-spells] Remove expire from certain ranger spells

Open Raykyn55 opened this issue 2 years ago • 5 comments

So a number of spells in our lexicon do NOT show up on active spells list, some examples being Zephyr and Ethereal Fissure. In order for combat trainer to maintain these spells, it assigns a flag to that spell when cast at the start of combat, so that it knows when the spell falls (since it can't be tracked with active_spells). Flag sees expire, triggers recast. However, if a spell IS present when checking active_spells, CT can treat that spell like any other, periodic checks against active spells, recast #, etc.

So far so great. What happens when a spell is on both lists? Spells like SK and WOTP, for example?

What happens is that, regardless of whether you have that spell up, CT is going to start combat buffing by FIRST checking whether a spell has an expire message, and then FORCE a cast of that spell. This is functionally necessary for most of these spells, but for spells on your active_spells list, it will attempt to recast it even if it's up, even if it was cast as part of your prehunt_buffs, even if there is a version still running. This is why, for rangers, WOTP is almost always cast out the gate in CT, despite it being present on the active_spells list.

This is a problem for two reasons:

  1. Magic tert attunement is pretty awful, every wasted cast, esp a big buff, slows everything else down.
  2. Buffing what's already buffed means NOT buffing something else, NOT casting TM, NOT casting debil.

Since both of the spells edited here are fetched via active_spells, there is zero reason to create the flag, thus zero reason to have expire messages defined in base-spells.

I suspect Read the Ripples and Nexus are two other examples, but I don't have those spells, so I won't mess with them. Anything with an expire message doesn't need a recast, and vice versa, but it's possible these fields are handled differently for these specific spells in other scripts.

Raykyn55 avatar Sep 05 '22 15:09 Raykyn55

I don't know what you mean - WOTP is in my buff_spells, but if it is already active, it is not force cast at the start of a hunt for me. It waits until the appropriate recast time.

XTyrsin avatar Sep 05 '22 15:09 XTyrsin

I don't know what you mean - WOTP is in my buff_spells, but if it is already active, it is not force cast at the start of a hunt for me. It waits until the appropriate recast time.

so when CT starts, it grabs a list of your buffs, and if any of them have expire messages, it creates a custom flag:

    @buff_spells
      .values
      .select { |data| data['expire'] }
      .each { |data| add_spell_flag(data['abbrev'], data['expire']) }

here:

  def add_spell_flag(name, expire)
    Flags.add("ct-#{name}", expire)
    Flags["ct-#{name}"] = true
  end

Note that this also sets that flag true by default. Next, CT checks for missing buffs:

  def check_buffs(game_state)
  return if game_state.casting
  return if DRStats.mana < @buff_spell_mana_threshold

  recastable_buffs = @buff_spells
                     .select { |_name, data| data['recast'] || data['recast_every'] || data['expire'] }
                     .select { |_name, data| data['expire'] ? Flags["ct-#{data['abbrev']}"] : true }
                     .select { |name, _data| check_buff_conditions?(name, game_state) }
                     .select { |_name, data| data['starlight_threshold'] ? enough_starlight?(game_state, data) : true }
                     .reject { |name, data | data['cyclic'] && DRSpells.active_spells.include?(data['name']) && !data['recast_every'] }

If you run down the list for recastable_buffs, it selects spells which:

  1. have any of: recast, recast every, expire
  2. any with expire that ALSO have an expired flag marked true (note initial behavior above)
  3. if spell is a weapon buff and whether casting said buff is appropriate
  4. trader voodoo
  5. finally any cyclic spells that aren't up and ought to be

That list is then collected we look for the FIRST that meets the criteria (find):

    name, data = recastable_buffs.find do |name, data|
      if data['pet_type']
        check_spell_timer?(data) && DRRoom.npcs.include?(data['pet_type'])
      elsif data['recast_every']
        check_spell_timer?(data)
      elsif data['expire']
        true
      else
        !DRSpells.active_spells[name] || DRSpells.active_spells[name].to_i <= data['recast']
      end
    end

So we determine, in order:

  1. Is this a pet spell, and do we have a pet out?
  2. if it has a recast every, has the timer expired?
  3. This is the important one: if it has an expire key, it's selected
  4. if any of the above is not true, then we determine whether it's on our active_spells list, and if it is, we move to the next.

OTHERWISE:

echo("found buff missing: #{name}") if $debug_mode_ct && name
...
prepare_spell(data, game_state, @buff_force_cambrinth)

If yours does not, you may have set a recast_every for that spell in your yaml.

Raykyn55 avatar Sep 05 '22 17:09 Raykyn55

If yours does not, you may have set a recast_every for that spell in your yaml.

I do not. WOTP is set in buff_spells to recast: 1

XTyrsin avatar Sep 05 '22 19:09 XTyrsin

If yours does not, you may have set a recast_every for that spell in your yaml.

I do not. WOTP is set in buff_spells to recast: 1

I'd be curious to see your yaml. Code seems pretty clear. It's also possible WOTP makes the list, but it's far down below other choices. Do you put up all your buffs before combat, or are some of them added when you enter combat-trainer?

Raykyn55 avatar Sep 05 '22 20:09 Raykyn55

I was able to confirm this behavior with WOTP just now. Buffed prior to combat, then it wanted to recast it once combat started.

asechrest avatar Sep 05 '22 21:09 asechrest