release: v1.3.15-alpha — Cast All gate + stop-after-reload crash - #39
Merged
Conversation
Cast All gate - Cast All was greyed out whenever the currently-selected character had no spells in the active preset, even when other party members had spells in the same preset. Cast Character was correctly char-scoped. - New BfBot.UI._CanCastAll() walks every portrait slot and matches the cross-party scope of BuildQueueFromPreset; the bbCast button now uses _CanCastAll while bbCastChar keeps the char-scoped _CanCast. Stop-after-reload crash (#38, reported by sov_) - After reloading a save mid-cast, BfBot.Exec._casters[].sprite still held freed CGameSprite pointers from the pre-reload party; pressing Stop dereferenced them via EEex_Action_QueueResponseStringOnAIBase, which segfaults at the engine level. pcall does NOT catch C++ access violations. - Stop() and _Complete() now re-resolve the caster sprite from the current portrait slot in their cleanup loops, so they never deref the freed pointer. BFBTCR is a no-op on targets without an active BFBTCH effect, so the cleanup is safe even when the slot now holds a different character. - BfBot.Exec._HardReset clears state without touching cached sprites. - BfBot.Exec._IsStateStale compares cached caster names against the live portrait sprites and proactively hard-resets execution state from _SafetyTick when party composition changed across the reload, so Cast / Cast Character re-enable themselves on the next safety tick instead of leaving the user stuck pressing Stop. - Userdata equality on EEex_Sprite_GetInPortrait is unreliable: two consecutive calls return distinct userdata wrappers and the __eq metamethod evaluates them as not equal even when they point at the same CGameSprite. The implementation deliberately avoids sprite identity in favor of name comparison + portrait re-resolution. Tests - New BfBot.Test.StaleState phase covers _HardReset, _IsStateStale, Stop / _Complete / _SafetyTick stale recovery, and Stop's re-resolve safety on a same-save reload (8 assertions, all pass). Versions - BfBot.VERSION 1.3.14-alpha → 1.3.15-alpha - setup-buffbot.tp2 VERSION ~v1.3.14-alpha~ → ~v1.3.15-alpha~ Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Cast Allis no longer greyed out when the currently-selected character has no spells in the active preset; the newBfBot.UI._CanCastAll()walks every portrait slot, matching the cross-party scope ofBuildQueueFromPreset.Cast Characterkeeps the original char-scoped gate.Stopafter reloading a save mid-cast no longer crashes. The cleanup loops inBfBot.Exec.StopandBfBot.Exec._Completenow re-resolve the caster sprite from the live portrait slot instead of dereferencing the cachedcaster.sprite, which holds a freedCGameSpritepointer post-reload (andpcalldoesn't catch C++ access violations).BfBot.Exec._SafetyTickproactively detects party-composition changes via cached-name vs live-portrait-name comparison and hard-resets execution state, so the user doesn't have to pressStopto unstick the UI.Closes #38.
Why name-based stale detection (not userdata
==)Empirically verified in this branch: two consecutive calls to
EEex_Sprite_GetInPortrait(0)return distinct userdata wrappers, and the__eqmetamethod evaluates them as not-equal even when they point at the sameCGameSprite. Sprite identity is unreliable; name comparison + portrait re-resolution is robust.Test Plan
BfBot.Test.StaleState— 8 assertions, all pass (_HardReset,_IsStateStalefor idle / matching / non-matching, Stop /_Complete/_SafetyTickstale recovery, Stop's re-resolve safety on same-save reload).BfBot.Test.RunAll— Stale State PASS. Pre-existing failures only: Classification (Classifier: Tracking (SPCL922) scores as BUFF instead of NOT BUFF #34, SPCL922 Tracking) and Movable Panel (MovablePanel test: resize handle position/width assertions fail #35); both unrelated to this PR.Cast All, mid-cast main-menu → Load → previous save, open BuffBot panel, press Stop → no crash; status logs "Stale execution state from save reload — resetting"; Cast / Cast Character re-enable.BfBotCor.VERSION = "1.3.15-alpha"andsetup-buffbot.tp2 VERSION ~v1.3.15-alpha~match (CIversion-check.ymlwill guard).🤖 Generated with Claude Code