Skip to content

AutoStory: story step types and engine (split from #122, layer 1 of 3) - #312

Draft
dayfine wants to merge 11 commits into
nav/sharedfrom
nav/story-engine
Draft

dayfine wants to merge 11 commits into
nav/sharedfrom
nav/story-engine

Conversation

@dayfine

@dayfine dayfine commented Nov 10, 2026 •

Copy link
Copy Markdown
Owner

Depends on #114.

The bottom of three layers split from #122: the shared story step data types (Types/: steps, checkpoints, args, step kinds, check levels), the StoryStepExecutor interface that turns a checkpoint into engine events, the story-step and authored-checkpoint JSON codecs (Data/) with their portable CMake tests, the shared story log lines, and the run options (StoryRangeOptions, StoryRunOptions, per-game language lists in Pokemon_GameLanguages). Screen state and interrupts are #313; #122 keeps only where steps meet screen state. The StoryEvents/StorySegments edits here still have to move to #114 (open questions in the review threads).

Testing: StoryStepExecutor, StoryStepJson, AuthoredCheckpointJson, StoryArgs, StoryLog, StoryRangeOptions, StoryEvents and StorySegments suites pass in test mode with the isolated profile, here and on #313 and #122.

🤖 Generated with Claude Code

https://claude.ai/code/session_013VUubhHeYSMGSSDXZkKZss

The story step vocabulary, the checkpoint builder and executor interface,
the variant parser, the shared story-step JSON codec and authored
checkpoint envelope, the story run options, and the StoryEvents/
StorySegments additions (changed_nothing, game_not_running), taken as
they are at nav/story-steps fd603ca. Screen state and interrupt
tracking follow in their own layer.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013VUubhHeYSMGSSDXZkKZss
…ace, spacing

Review of #312:
- The plain story data types move out of Pokemon_StorySteps.h into
  Pokemon/AutoStory/Types/ (StoryStepKind, StoryChecks, StoryStep with
  StoryLeg and StoryItem, StoryCheckpoint). Pokemon_StorySteps.h still
  includes them, so its users build unchanged; the JSON codec and the
  run options now include only the types.
- StoryStepExecutor is an interface with no data and no protected or
  private members: begin_attempt(), run() (pure) and holds(). The 15
  per-kind virtuals and dispatch_story_step() go: a game dispatches by
  kind in run(), as the StoryActionTable of the next layer does. The
  executor's checks level and language go too; nothing read them, and
  the program passes StoryRunOptions' values to the game's own steps.
- Read-only sequence parameters take std::span.
- Spacing: header comment blocks, blank lines after includes, around
  namespaces and between definitions, one statement per line.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013VUubhHeYSMGSSDXZkKZss
dayfine added a commit that referenced this pull request Nov 10, 2026
…ta-free executor)

# Conflicts:
#	SerialPrograms/cmake/SourceFiles.cmake
dayfine added a commit that referenced this pull request Nov 10, 2026
dayfine added a commit that referenced this pull request Nov 10, 2026
#312's StoryStepExecutor no longer has a virtual per step kind, so a step
the table has no action for is unsupported_step(). A binding that still
does some kinds itself overrides run_action().

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013VUubhHeYSMGSSDXZkKZss
dayfine and others added 4 commits November 9, 2026 20:42
A step's variant carried its action's arguments as free text ("mart
CeruleanCity_Mart budget=1500 ..."), parsed by each action. Steps now
carry them in `args` (flags, whole numbers, words), which the shared JSON
codec reads and writes; actions declare StoryArgSpecs and a game checks
args against them at load. split_legacy_variant() reads an old variant
into a name and args during the migration; the variant word helpers are
deprecated. Design: docs/design/AutoStory.md section 10.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013VUubhHeYSMGSSDXZkKZss
StoryRangeOptions is the start/end checkpoint dropdowns over one list with
the start-before-end check; StoryRunOptions is now that group plus the run
policy, with the same keys and public members. Pokemon_StoryLog holds the
checkpoint start/attempt/pass/reset lines, event_log_line and a stats
counts helper, with no engine dependency; run_checkpoint, run_segment and
record_story_stats use it, with unchanged output.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013VUubhHeYSMGSSDXZkKZss
SV reads its end checkpoint after each checkpoint and leaves the end
dropdown unlocked during a run; the range group now takes that lock mode.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013VUubhHeYSMGSSDXZkKZss
dayfine and others added 2 commits November 9, 2026 20:55
Keeps the unit test runner's list short as the stacked layers add suites.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013VUubhHeYSMGSSDXZkKZss


// One argument's value: a flag, a whole number or a word.
using StoryArgValue = std::variant<bool, int64_t, std::string>;

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is it a StoryArg or StoryActionArg?

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

They are the action's arguments (the action is picked by variant), so StoryActionArg(s) reads better. Not renamed yet: it depends on how you want the step redesigned (see the StoryStep thread). Should I rename now?

using StoryArgs = std::map<std::string, StoryArgValue, std::less<>>;


// The number `key` holds; nothing if absent or not a number.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do we really need these helpers?

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FRLG's actions call them about 35 times, mostly story_arg_number and story_arg_text, each in place of a find plus std::get_if. I kept them. Drop them anyway?


// The type an action declares for an argument.
enum class StoryArgType{
FLAG,

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FLAG -> BOOLEAN?

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in e8833b3: StoryArgType::BOOLEAN.

TEXT,
};

// An argument an action takes.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

how static / dynamic is this?

I suppose for built-in actions the schema is uniform, but for game specific actions this needs to be sepcifically defiend -- still, sounds like it can be a config file instead of C++ class. I am trying to see if this is more beneficial to do in C++?

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed per action, at compile time: an action declares its specs next to the C++ code that reads the args, and the game checks every step's args when it loads the table. A config file would hold a second copy of what the code reads. A JSON schema only pays off if PokemonSimulation should check the args before the C++ sees them. Do you want that?


// Why `args` don't fit `specs`: a key no spec names, a value of another
// type, or a required one missing. Empty when they fit.
std::string story_args_error(const StoryArgs& args, std::span<const StoryArgSpec> specs);

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should this return a vector, one for each error?

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in e8833b3: story_args_errors() returns a std::vector<std::string> with every problem. #122's action-table lint (a03bfe6) reports one problem per error.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is deprecated in favor of args, right? can we drop?

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, it's deprecated, but FRLG, BDSP and PLZA still parse old variants with variant_words/name/pair/value/number (dozens of calls in their actions). Dropping it here would break them until their tables move to args. Should I keep it until those PRs have moved, or drop it now and make each game migrate on its next merge?

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is a prework PR that should go with #45 maybe?

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Possibly: it's prework that SV (#320) needs too. I'd put StoryRangeOptions and StoryLog in a new prep PR on top of #45; that needs only AutoStorySelect. #320 could then stack on it rather than on this engine. Should I do that? It would also move StoryLog out of this PR.

return database;
}

const EnumDropdownDatabase<Language>& StoryLanguage_Database(){

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe we can get this in a separate PR / module?

So we can name this Gen8ToGen9Languages? or something something that's specific and correct -- check all the games in the gen too

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in e8833b3 as a module: Pokemon/Options/Pokemon_GameLanguages has Gen8ToGen9Languages_Database() and FireRedLeafGreenLanguages_Database(). Gen 8/9 is SwSh, BDSP, PLA, SV and PLZA, all with the same nine languages. FRLG has six (no Korean/Chinese, matching upstream's FRLG readers), so the old shared list was wrong for it. StoryRunOptions now takes the game's database as a second constructor argument.

// joined: a section's entry i is then story_index(true, i, first of the
// section). The program keeps it in a function-local static: the options
// hold a reference.
template <typename Row>

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this part merged upstream?

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No. make_story_select_database() (Pokemon_AutoStorySelect.h) is upstream; story_checkpoint_database() is fork-only, from this PR. It is no longer a template either (e8833b3).

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is it supposed to be shareable - then it should go in a pre-122 PR (45 or like)

dayfine and others added 2 commits November 9, 2026 22:35
…utor, StoryChecks to StoryCheckLevel

File moves and path updates only (#312 review): the story-step and
authored-checkpoint JSON modules are I/O, so they go under
Pokemon/AutoStory/Data/; Pokemon_StorySteps is named for what it holds,
the StoryStepExecutor; Types/Pokemon_StoryChecks becomes
Pokemon_StoryCheckLevel ahead of the type's rename.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013VUubhHeYSMGSSDXZkKZss
- StoryStepExecutor.h keeps the executor and building checkpoints:
  tile_pose() and facing_of() are in Pokemon_TilePose (merged from #114),
  record_story_stats() becomes SegmentReport::story_counts() for
  record_story_counts(), and build_story_checkpoint() takes an optional
  PreparedSteps* instead of a second overload.
- StoryLog: default arguments instead of overloads; the segment's reload
  line is segment_reload_line().
- StoryChecks is StoryCheckLevel; names come back as std::string, and
  GAME_NOT_RUNNING is a std::string_view constant: no char* in new code.
- StoryArgType::FLAG is BOOLEAN; story_args_errors() returns every error.
- Authored checkpoint JSON: validate_json_fields(), `game`, and an
  EmbeddedJsonFile alias for the embedded resources.
- StoryRunOptions takes the game's languages from the new
  Pokemon_GameLanguages (generations 8-9, and FireRed/LeafGreen), and the
  checkpoint dropdown is built from a span of checkpoints without the
  StoryCheckpointEntry adapter.
- Read-only strings and sequences as std::string_view and std::span.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013VUubhHeYSMGSSDXZkKZss
dayfine added a commit that referenced this pull request Nov 10, 2026
They build and read a TilePose, so they live with it instead of in the
story step executor header (#312 review).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013VUubhHeYSMGSSDXZkKZss
dayfine added a commit that referenced this pull request Nov 10, 2026
story_args_errors() now returns each error (#312 review); the action table's
checkpoint lint reports one problem per error.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013VUubhHeYSMGSSDXZkKZss
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant