Skip to content

Commit c99601c

Browse files
MahtraDRclaude
andauthored
fix(combat-trainer): regression when all weapon skills mindlocked (elanthia-online#7345)
## Summary Fixes regression from elanthia-online#7341 where `all_weapons_locked` disabled attacks when all weapon skills reached 34/34 mid-hunt, breaking FA and skinning training. Also fixes a pre-existing bug where `combat_trainer_ignore_weapon_mindstate: true` caused rapid weapon cycling instead of action-count-based rotation. ### Root cause PR elanthia-online#7341 added an `all_weapons_locked` flag that disabled attacks in `AttackProcess` when every weapon skill hit 34/34. This was intended to prevent bare-fisted jab spam at script startup, but it also disabled attacks **mid-hunt** when a weapon was already equipped -- breaking FA, skinning, and any other non-weapon training that requires killing. Users who tried `combat_trainer_ignore_weapon_mindstate: true` as a workaround hit a second bug: `skill_done?` returns `true` immediately at exp==34 (line 5131), triggering `determine_next_to_train` to switch weapons every cycle -- resulting in an infinite load/aim/unload/swap loop without ever firing. ### What changed #### 1. Removed `all_weapons_locked` entirely - Removed the flag from `attr_accessor`, `initialize`, and the `AttackProcess` condition - A weapon is now always selected (see 5a/5b below), so attacks are never disabled by this flag #### 2. All weapons at 34 with weapon equipped (scenario 5a) - The all-locked guard in `determine_next_to_train` returns early **without switching weapons** - Attacks continue with the current weapon so the player can train FA, skinning, etc. - One-time user message: ``` *** combat-trainer: All weapon skills are at 34/34 mindstate. *** combat-trainer: Continuing to attack with Bow for FA/skinning training. *** combat-trainer: Set combat_trainer_ignore_weapon_mindstate: true to cycle weapons by action count instead. ``` #### 3. All weapons at 34 with no weapon equipped at startup (scenario 5b) - Previously returned early from the all-locked guard, leaving `weapon_skill` nil -- causing bare-fisted jab spam (the original pre-elanthia-online#7341 bug) - Now falls through to weapon selection, picks the first available weapon, then behaves as 5a - One-time user message: ``` *** combat-trainer: All weapon skills are at 34/34 mindstate. *** combat-trainer: Selecting initial weapon to begin attacking. *** combat-trainer: Set combat_trainer_ignore_weapon_mindstate: true to cycle weapons by action count instead. ``` #### 4. `ignore_weapon_mindstate: true` now works correctly - Passed `ignore_weapon_mindstate` into `GameState` so `skill_done?` can check it directly (SRP -- the decision about whether training is complete belongs in the method making that decision) - When the flag is true, `skill_done?` skips **all** mindstate-driven logic: - No gain_check blacklisting - No early return at exp==34 - No focus_threshold adjustments - No `target_increment` comparison - Only evaluates: `action_count >= target_action_count` - Weapons cycle indefinitely by action count regardless of mindstate - One-time user message when all weapons reach 34: ``` *** combat-trainer: All weapon skills at 34/34 mindstate -- ignore_weapon_mindstate is active. *** combat-trainer: Cycling weapons by combat_trainer_action_count only. ``` #### 5. Blank/nil `weapon_training` guard - New guard at the top of `determine_next_to_train`: `unless weapon_training&.any?` - If no weapons are configured, attacks are disabled and the character dances (bob/weave/circle) for defensive skills only - Added `weapon_skill.nil?` check to `AttackProcess` so a missing weapon falls into the dance path instead of attempting bare-fisted attacks - One-time user message: ``` *** combat-trainer: No weapons configured in weapon_training. *** combat-trainer: Attacks are disabled. Only defensive dance actions (bob/weave/circle) will be used. *** combat-trainer: Add entries to weapon_training in your YAML if you want to train weapon skills. ``` #### 6. Spec file consolidation - Renamed `combat_trainer_bundle_spec.rb` to `combat_trainer_spec.rb` (one spec file per script convention) - Added `GameState` and `SetupProcess` specs covering: - `skill_done?` with `ignore_weapon_mindstate` true/false at exp values 0, 17, and 34 - `determine_next_to_train` for 5a, 5b, mixed exp, fresh start, ignore_mindstate, skill_done? false - Blank and nil `weapon_training` guard - One-time messaging for all scenarios (5a, 5b, ignore_mindstate, blank weapon_training) - Adversarial: messaging does NOT fire when not all weapons are at 34 ## Test plan - [x] 53 specs pass (`rspec spec/combat_trainer_spec.rb`) - [x] `rubocop -A` clean on both files - [ ] In-game: all weapons capped, no `ignore_weapon_mindstate` -- verify attacks continue with current weapon (5a), verify message appears once - [ ] In-game: script start with all weapons already capped -- verify a weapon is selected and attacks proceed (5b), verify message appears once - [ ] In-game: `ignore_weapon_mindstate: true` with all weapons capped -- verify cycling by action count with no rapid swap loop, verify message appears once - [ ] In-game: `ignore_weapon_mindstate: true` with one weapon configured -- verify attacks continue indefinitely - [ ] In-game: blank `weapon_training` -- verify dance-only behavior with message - [ ] In-game: normal multi-weapon cycling unaffected when skills are not all at 34 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 4f5827e commit c99601c

2 files changed

Lines changed: 432 additions & 14 deletions

File tree

combat-trainer.lic

Lines changed: 52 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -163,23 +163,55 @@ class SetupProcess
163163
end
164164

165165
def determine_next_to_train(game_state, weapon_training, ending_ranged)
166+
# No weapons configured: nothing to equip or train. The character will
167+
# dance (bob/weave/circle) for defensive skills only.
168+
unless weapon_training&.any?
169+
unless @no_weapons_warned
170+
@no_weapons_warned = true
171+
DRC.message('*** combat-trainer: No weapons configured in weapon_training.')
172+
DRC.message('*** combat-trainer: Attacks are disabled. Only defensive dance actions (bob/weave/circle) will be used.')
173+
DRC.message('*** combat-trainer: Add entries to weapon_training in your YAML if you want to train weapon skills.')
174+
end
175+
return
176+
end
177+
166178
return unless game_state.skill_done? || !weapon_training[game_state.weapon_skill] || ending_ranged
167179

168180
# skip_all_weapon_max_check is a kludge for a transient condition from blacklisting a skill with
169181
# all remaining skills at 34
170182
unless @ignore_weapon_mindstate || game_state.skip_all_weapon_max_check
171-
# check if all weapons are maxed exp to prevent rapid cycling
183+
# All weapons at 34: stay on current weapon to prevent rapid cycling.
184+
# If no weapon is equipped yet (script startup with all capped), fall
185+
# through to weapon selection so one gets picked (5b becomes 5a).
172186
if weapon_training.reject { |skill, _| DRSkill.getxp(skill) == 34 }.empty?
173-
echo 'all weapons locked, not switching' if $debug_mode_ct
174-
unless game_state.all_weapons_locked
175-
game_state.all_weapons_locked = true
176-
echo '*** All weapon skills are at 34/34 mindstate -- no weapon will be equipped.'
177-
echo '*** Attacks are disabled. Set combat_trainer_ignore_weapon_mindstate: true if any skills are permanently capped.'
187+
if game_state.weapon_skill
188+
unless @all_locked_warned
189+
@all_locked_warned = true
190+
DRC.message('*** combat-trainer: All weapon skills are at 34/34 mindstate.')
191+
DRC.message("*** combat-trainer: Continuing to attack with #{game_state.weapon_skill} for FA/skinning training.")
192+
DRC.message('*** combat-trainer: Set combat_trainer_ignore_weapon_mindstate: true to cycle weapons by action count instead.')
193+
end
194+
return
195+
else
196+
unless @all_locked_selecting_warned
197+
@all_locked_selecting_warned = true
198+
DRC.message('*** combat-trainer: All weapon skills are at 34/34 mindstate.')
199+
DRC.message('*** combat-trainer: Selecting initial weapon to begin attacking.')
200+
DRC.message('*** combat-trainer: Set combat_trainer_ignore_weapon_mindstate: true to cycle weapons by action count instead.')
201+
end
178202
end
179-
return
180203
end
181204
end
182-
game_state.all_weapons_locked = false
205+
206+
# With ignore_weapon_mindstate, skill_done? only triggers on action count.
207+
# Notify the user once when all weapons reach 34 that cycling continues by action count.
208+
if @ignore_weapon_mindstate && !@ignore_mindstate_warned
209+
if weapon_training.reject { |skill, _| DRSkill.getxp(skill) == 34 }.empty?
210+
@ignore_mindstate_warned = true
211+
DRC.message('*** combat-trainer: All weapon skills at 34/34 mindstate -- ignore_weapon_mindstate is active.')
212+
DRC.message('*** combat-trainer: Cycling weapons by combat_trainer_action_count only.')
213+
end
214+
end
183215
game_state.skip_all_weapon_max_check = false
184216

185217
echo('new skill needed for training') if $debug_mode_ct
@@ -3703,7 +3735,7 @@ class AttackProcess
37033735
game_state.no_stab_current_mob = false
37043736
end
37053737

3706-
if game_state.dancing? || game_state.all_weapons_locked || game_state.weapon_skill == 'Targeted Magic' || !game_state.is_offense_allowed?
3738+
if game_state.dancing? || game_state.weapon_skill.nil? || game_state.weapon_skill == 'Targeted Magic' || !game_state.is_offense_allowed?
37073739
if game_state.finish_killing?
37083740
echo('AttackProcess::clean_up') if $debug_mode_ct
37093741
game_state.next_clean_up_step
@@ -4364,7 +4396,7 @@ class GameState
43644396
:clean_up_step, :constructs, :cooldown_timers, :current_weapon_skill,
43654397
:currently_whirlwinding, :dance_queue, :dancing, :danger, :focus_threshold_active, :hide_on_cast, :last_action_count, :last_exp, :last_offhand_skill,
43664398
:last_regalia_type, :last_weapon_skill, :loaded, :mob_died, :need_bundle,
4367-
:all_weapons_locked, :no_loot, :no_skins, :no_stab_current_mob, :no_stab_mobs, :offhand_last_exp, :parrying, :prepare_cfb, :prepare_cfw, :prepare_nr,
4399+
:no_loot, :no_skins, :no_stab_current_mob, :no_stab_mobs, :offhand_last_exp, :parrying, :prepare_cfb, :prepare_cfw, :prepare_nr,
43684400
:prepare_consume, :regalia_cancel, :reset_stance, :retreating, :skip_all_weapon_max_check, :starlight_values,
43694401
:swap_regalia_type, :target_weapon_skill, :use_charged_maneuvers,
43704402
:whirlwind_trainables, :wounds
@@ -4422,7 +4454,6 @@ class GameState
44224454
@constructs = []
44234455
@no_stab_mobs = []
44244456
@no_loot = []
4425-
@all_weapons_locked = false
44264457
@dancing = false
44274458
@retreating = false
44284459
@action_count = 0
@@ -4475,6 +4506,9 @@ class GameState
44754506
echo(" @target_increment: #{@target_increment}") if $debug_mode_ct
44764507
@target_increment_original = @target_increment
44774508

4509+
@ignore_weapon_mindstate = settings.combat_trainer_ignore_weapon_mindstate
4510+
echo(" @ignore_weapon_mindstate: #{@ignore_weapon_mindstate}") if $debug_mode_ct
4511+
44784512
@gain_check = settings.combat_trainer_gain_check
44794513
echo(" @gain_check: #{@gain_check}") if $debug_mode_ct
44804514

@@ -5108,6 +5142,13 @@ class GameState
51085142
echo("last_exp: #{@last_exp}") if $debug_mode_ct
51095143
echo("last_action_count: #{@last_action_count}") if $debug_mode_ct
51105144

5145+
# When ignoring weapon mindstate, cycle weapons purely by action count.
5146+
# Skip all mindstate-driven logic: gain_check, exp==34, focus_threshold,
5147+
# and target_increment comparison.
5148+
if @ignore_weapon_mindstate
5149+
return @action_count >= @target_action_count
5150+
end
5151+
51115152
# gain_check code: logic to account for a skill that is not gaining any MS at all
51125153
if (@gain_check > 0) && !weapon_skill.nil?
51135154
# only check for mindstate movement if there was actually some action in the last CT cycle

0 commit comments

Comments
 (0)