key exists separately from the inherited name; for Dialog, the per-block-type behaviour table and the DialogList flow assumption; for Simulation, that playerCount and the renderer are placeholders with nothing wired up yet.global.json pinning the SDK to 8.0.417 (the default .NET 10.0.401 SDK crashes compiling Unity's generated csproj on this machine - a Windows/CET incompatibility, not a project issue), and a hand-written CompileCheck.csproj that globs Assets/Scripts/Rokojori and Assets/Scripts/Project directly, since Unity's own generated Assembly-CSharp.csproj only lists files present at its last sync and won't pick up files written outside the Editor. All new code compiles clean against this (2 pre-existing, harmless serialization-analyzer warnings on EventSlot fields, unrelated to this session's code). This has only been compile-checked, never run in Unity - see the new "Verify in Unity Editor" board task.private fields (use public with an underscore prefix, or protected), no parameterized constructors (DialogProcessor changed to a static Create() factory), switch statements replaced with if/else-if chains (CompareInt, ListCondition), and explicit initial values added to all fields that lacked them.DialogProcessor is the engine - it only knows about IDialogDisplay, nothing about UI or DialogManager. DialogManager is the UI-facing singleton - implements IDialogDisplay, and StartDialog() is the only entry point anyone outside this folder should call. DialogPhase (in Simulation/) is a thin wrapper so a dialog can be one step in a turn loop.DialogProcessor.Run( start ) repeats current = await current.Run( display ) until current is null. Each DialogBlock decides its own next step; the base class default is simply "return next" (linear flow), and only PlayerChoice / DialogBranch override that to branch.display.Show(this), awaits it, returns next.display.Show(this), reads answer.selectedOptionIndex, runs that option's action if set, returns that option's next.branch if condition.Evaluate() is true, else the inherited next.blocks in flat order, next. This is an assumption, not confirmed - see outline/tech "Implementation Status". Practical effect: don't put a PlayerChoice/DialogBranch directly inside a DialogList's blocks, its branch target will be silently discarded.next like the default.GameStateCondition (CompareInt, HasObject, ListCondition) and GameStateAction (SetProperty, ModifyProperty, ActionList) both read/write through GameStateManager.instance - they never touch DialogBlock state directly. HasObject currently checks a GameStateBoolVariable flag since there's no inventory/object system to check membership against yet.PlayerChoice.options[i].action is not in the original design outline - added to give choices a way to change game state, since Conditions and Actions were described as a pair but no block explicitly triggered an Action.HeadlessDialogDisplay implements IDialogDisplay without any Unity UI - it logs every block it's shown into visitedBlocks and answers PlayerChoice via the chooseOption delegate (defaults to always picking option 0). Drive a dialog with DialogProcessor.Create( new HeadlessDialogDisplay() ).Run( start ) to test branching logic in isolation.DialogManager.Show() currently GameStateVariable (and its subtypes GameStateNumberVariable / GameStateBoolVariable) are ScriptableObject initialValue and a key. They do not hold the live, current value.GameStateManager - _numberValues and _boolValues, both marked [NonSerialized] since they're pure runtime cache, rebuilt from the variables list in Awake(). Never read or write these dictionaries directly - always go through GameStateManager.instance.GetValue(...) / SetValue(...), which are overloaded per variable type.name (inherited from UnityEngine.Object - the asset's filename). GameStateVariable.key is a separate, deliberately-added field used as the stable identifier in JSON save data, so renaming an asset in the editor doesn't silently break old save files.SetValue updates the dictionary onChanged EventSlot in the same call - there's no separate notify step. Reading via GetValue never fires anything.ToJson() / FromJson( json ) use Unity's JsonUtility, which can't serialize a Dictionary directly - hence the private NumberEntry / BoolEntry / SaveData classes that flatten the dictionaries into lists of key/value pairs first.FromJson finds a saved key that no longer matches any variable in the current variables list, that entry is silently skipped - no error, no warning.GameStateManager.instance - the singleton (via Unique<T>)..GetValue( GameStateNumberVariable ) / .GetValue( GameStateBoolVariable ).SetValue( variable, value ) - updates + fires onChanged.ToJson() / .FromJson( json )GameStateNumberVariable: a new GameStateVariable subclass, a new dictionary + Get/SetValue overload pair on GameStateManager, and a new Entry class for JSON.Unique<T>.Get() is the singleton locator used everywhere (GameStateManager.instance, DialogManager.instance, SimulationManager.instance, SimulationRenderer.instance). It searches the root game objects of all open scenes for a component of type T on first access and caches the result statically - there is no manual registration step. This means exactly one instance is expected to exist in the open scenes; a second instance is never detected or warned about.AddAction, RemoveAction, Once, DispatchEvent( value ). Listeners added or removed _canModify guards this) - safe to unsubscribe yourself from inside your own callback.GameStateNumberVariable.onChanged / GameStateBoolVariable.onChanged..value only dispatches if the new value differs from the old one (via EqualityComparer<T>.Default) - assigning the same value twice fires nothing. SetSilent() changes the backing value without dispatching. Not currently used by GameState/Dialog/Simulation - those roll their own change tracking (GameStateManager's dictionaries + each variable's own EventSlot) instead.Locale is the abstract base (a ScriptableObject with GetLocale( LocaleCode )); LocaleText is the only concrete implementation and currently just returns its en field LocaleManager.currentLocale exists but nothing reads it. DialogLine/PlayerChoice reference Locale assets for their text, so localization can be wired in later without touching Dialog code.Debug.Log/LogWarning/LogError. Takes a leading object plus a params object[] and space-joins everything into one string - RJLog.Log( "value:", 5, someObject ).list.Has( item ) (index-based, works without IEquatable) and list.RemoveList( removals ) (removes every item in one list from another).scene.Get<T>() and SceneExtensions.GetInOpenScenes<T>() search root game objects (and their children) for a component - this is what Unique<T> calls internally.SimulationManager.Run():RunPhases(), which executes phases in order and checks phase.gameFinished after each one. If any phase sets it, RunPhases returns immediately (skipping the rest of Run() skips straight to outro instead of continuing the while-loop.Player type anywhere in the codebase yet. SimulationManager.playerCount is a plain int defaulting to 1 - nothing currently sets it automatically. An intro phase is expected to set it (e.g. from a GameState variable) before the turn loop needs it, but that phase doesn't exist yet either.Execute( GameStateManager, SimulationRenderer ) does the work; set gameFinished = true from inside it to end the game early. No concrete example phases exist yet (ShowTurnStartConditions / SimulateWorld were only described narratively in the outline, no code contract given).SimulationRenderer.Enqueue( command ) currently just returns Task.CompletedTask immediately - there's no rendering/animation pipeline behind it. SimulationCommand is an empty abstract base with no concrete commands defined. A GamePhase can call Enqueue today and it will compile and "work" (complete instantly), but nothing will actually happen on screen.DialogManager.instance.StartDialog( start ) - all real logic stays in Dialog/.