Skip to content

Releases: TheGeebus/SimpleQuest

v0.3.5] — Stale Quest Tags Polish

26 Apr 05:07
0adc826

Choose a tag to compare

Pre-release

[0.3.5] — 2026-04-25 — Stale Quest Tags Polish

A polish pass on the Stale Quest Tags panel that shipped a few hours
earlier in 0.3.4, plus designer-facing log clarity improvements across
BindToQuestEvent, the resolution subsystem, and the panel's own UX
failure paths. The panel gains multi-row mass-clear with a per-
source confirmation breakdown and atomic undo (single transaction
wraps the batch), a sortable + filterable Level column with per-
source semantics, and near-instant undo on cleared instance
entries — was multi-second on Full Project Scan history; now sub-
millisecond regardless of scan history. The Refresh button is
renamed Scan Loaded for symmetric pairing with Full Project Scan,
and the Full Project Scan progress bar now advances on completion
instead of jumping to 100% before the work begins.

Added

Stale Quest Tags panel — multi-row mass-clear

  • Multi-row selection (ESelectionMode::Multi) on the SListView.
    Standard Ctrl+Click / Shift+Click multi-select gestures
  • New header button "Clear Selected (N)" with live label binding
    on ListView->GetNumItemsSelected() and IsEnabled bound to the
    same count. Disabled when nothing is selected
  • Confirmation dialog with per-source breakdown: shows count of
    Loaded / BP CDO / Unloaded entries in the selection plus the
    number of unique packages affected, before any mutation
  • Single FScopedTransaction wraps the entire batch — Ctrl+Z
    restores all Loaded + Unloaded clears as one atomic operation
  • ClearOneEntry helper extracted from HandleClearClicked so
    single-row and bulk paths share the source-specific dirty-resolution
    logic (Loaded → MarkPackageDirty on actor; BP CDO → outer-chain
    walk to UBlueprint + MarkBlueprintAsModified; Unloaded →
    MarkPackageDirty on the unloaded-level actor)

Stale Quest Tags panel — Level column

  • New sortable + filterable Level column between Source and
    Actor (140px fixed width)
  • Per-source display: leaf umap name (via FPackageName::GetShortName)
    with full-path tooltip on hover for Loaded / Unloaded entries; em-
    dash with muted color and "Not applicable" tooltip for BP CDO
    entries (BP defaults aren't level-bound)
  • Underlying sort/filter value is the full umap path for Loaded /
    Unloaded and empty for BP CDO — typing partial path text matches
    level rows but never matches BP CDO rows; em-dash entries cluster
    at one end of any Level sort
  • Backend reuses FStaleQuestTagEntry::PackagePath (already populated
    for all three sources by the Tier 2 scanner); no schema change needed

Stale Quest Tags panel — fast PostUndo via per-actor targeted rescan

  • New ActorsTouchedByClear map (per-actor Source + PackagePath)
    populated on every Clear (single + bulk). BP CDO entries opt out —
    their "actor" is a CDO, not a level instance
  • New UpdateFromAffectedActors method drops AllEntries for tracked
    actors then re-emits via ScanActorForStaleTags
  • PostUndo / PostRedo call UpdateFromAffectedActors instead of
    Refresh(LastScope). Net effect: undo of a cleared instance entry
    is now sub-millisecond regardless of LastScope. Sibling entries
    on the same actor are preserved automatically because the rescan
    emits all stale tags fresh with valid weak ptrs

Stale Quest Tags panel — designer-visible BP CDO permanence warnings

  • Per-row Clear tooltip on BP CDO entries calls out "Cannot be
    undone — the mutation propagates permanently to the Blueprint's
    class state. Use the Blueprint editor to manually re-add the tag if
    needed."
  • Clear Selected (N) header button tooltip distinguishes Loaded /
    Unloaded undo (Ctrl+Z works, single transaction wraps the batch)
    from BP CDO permanence (Ctrl+Z is a no-op for those rows)
  • Confirmation dialog conditionally appends a [Warning] block
    when the selection contains any BP CDO entries — clean dialog
    otherwise so the warning isn't always present

FSimpleQuestEditorUtilities::ScanActorForStaleTags (new public utility)

  • Promoted from anonymous-namespace helper to public static method
    on FSimpleQuestEditorUtilities. Walks an actor's components,
    dispatches by component type (Giver / Target / Watcher), emits
    one FStaleQuestTagEntry per stale tag found. Useful for any
    targeted recovery / scan flow that wants to re-derive entries
    for a specific actor without re-walking the entire project

Changed

  • "Refresh" button renamed "Scan Loaded" for symmetric pairing
    with Full Project Scan. Method renamed HandleRefreshClicked
    HandleScanLoadedClicked for internal consistency. Tooltip
    clarifies the Tier-1 scope and references Full Project Scan as
    the alternative
  • Slow-task progress bar in Full Project Scan now advances on
    completion (SlowTask.EnterProgressFrame(1.f) moved to after
    Refresh) instead of jumping to 100% before the scan begins.
    UE's secondary asset-loading bar continues to show real-time
    progress during sync-loads
  • UQuestResolutionSubsystem resolution-recording log promoted
    Verbose → Log. Quest resolution is a high-signal event; designers
    debugging quest flow now see "recorded" entries in the Output Log
    without enabling Verbose
  • SStaleQuestTagsPanel actor-not-found-post-load log promoted
    Verbose → Log with more actionable wording: "actor may have been
    renamed or removed since the scan." Class-prefix added for
    consistency with other panel logs
  • UQuestEventSubscription subsystem-resolution failure Warning
    gained a "Common causes:" hint — BindToQuestEvent fired pre-init,
    or the WorldContextObject pin is wired to an actor whose UWorld
    isn't valid

Removed

  • Anonymous-namespace ScanActorForStaleTags in
    SimpleQuestEditorUtils.cpp — replaced by the public version above
  • SimpleQuest.Debug.ScanBlueprintCDOs and
    SimpleQuest.Debug.ScanUnloadedLevels console commands
    — original
    author's note explicitly slated these for removal once Phase 4's
    panel button was in place. Phase 4 + Phase 5 (commandlet) both
    shipped in 0.3.4; the console commands were strictly inferior to
    the panel + commandlet surfaces

Known Limitations

  • BP CDO undo is a no-op. Clear on a BP CDO row persists
    permanently across save (the underlying mutation propagates to
    the Blueprint's class state). Ctrl+Z does not visually restore
    the row, and the underlying tag stays cleared on the BP. Two
    approaches were tried during development and both failed —
    force-recompile in PostUndo (didn't fix the visibility issue
    and added several seconds of lag per touched BP) and shadow-of-
    cleared-entries (the predicate's component weak-ptr check failed
    post-undo, dropping sibling rows on unrelated actors). Root
    cause is the BP-side template / CDO propagation contract — needs
    a refactor that routes through FProperty::PreEditChange /
    PostEditChange flow rather than direct container mutation.
    Slated for a focused investigation post-0.4.0; not blocking.
    Designer-visible warnings landed alongside on the per-row tooltip,
    Clear Selected (N) header tooltip, and bulk-clear confirmation
    dialog so designers see the limitation BEFORE the click rather
    than discovering it after
  • Slow-task progress bar lacks per-step granularity. Outer bar
    stays at 0% during the scan and advances to 100% on completion;
    UE's secondary asset-loading bar shows real-time progress during
    sync-loads. Cosmetic. Plumbing per-step progress through
    ScanActorBlueprintCDOs / ScanUnloadedLevels /
    ScanWorldPartitionActors requires threading a callback through
    the scan internals — minor lift but no immediate user-pain driver

v0.3.4 — Stale Quest Tags Tier 2 (Project-Wide Scanning)

25 Apr 14:46
61c3e26

Choose a tag to compare

[0.3.4] — 2026-04-25 — Stale Quest Tags Tier 2 (Project-Wide Scanning)

Project-wide stale quest-tag scanning. The Stale Quest Tags panel and a
new headless commandlet now cover the full project surface — loaded
levels (Tier 1, unchanged), Actor Blueprint defaults, and unloaded
levels including World Partition. Designers click Full Project Scan
in the panel to surface stale references that a normal pass wouldn't
catch; ship pipelines and CI runs invoke the commandlet directly via a
Windows .bat helper or a UnrealEditor-Cmd.exe -run=StaleQuestTagsScan
line, get structured JSON output, and gate on exit code: 0 clean, 1
stale references found, 2 infra failure (couldn't init, JSON write
failed, etc.). Designed as the pre-flight + post-flight validator for
tag-identity work — particularly the SimpleQuest.* root namespace
consolidation slated for 0.4.0 — but useful any time a project ships.

The new scan tiers are opt-in and source-aware. The panel keeps its
sub-second Tier 1 refresh as the default; clicking Full Project Scan
fans out to Blueprint CDOs and unloaded levels (with a comprehensive-
vs-class-filtered World Partition toggle) wrapped in a slow-task with
progress notifications. Each row carries a Source badge — Loaded /
BP CDO / Unloaded — and the navigation affordance morphs to match:
Find frames the actor in its viewport for loaded entries, Open BP
opens the Blueprint editor for CDO entries, Open Level loads the
containing umap for unloaded entries. Per-row Clear works on all three
sources; affected packages roll into a panel-header Save All
Modified (N)
button so the designer can review and save in bulk.

Added

Stale Quest Tags panel — Tier 2 surfaces

  • Full Project Scan button alongside the existing Refresh control.
    Refresh stays at Tier 1 (loaded levels — sub-second); Full Project
    Scan fans out to Tiers 1+2+3 wrapped in FScopedSlowTask with
    progress notifications. The panel caches the last-used scope so
    PostUndo / PostRedo re-scan against the same view (stops Ctrl+Z
    from silently narrowing the scope back to Tier 1 and dropping any
    Tier 2 rows the designer pulled in)
  • Source-aware row morphing: per-row icon + a Source column with
    Loaded / BP CDO / Unloaded badges. Find button morphs into
    Open BP for CDO entries (opens the Blueprint editor) and Open
    Level
    for unloaded entries (loads the containing umap)
  • Save All Modified (N) button in the panel header. Per-row Clear
    marks the affected package dirty and surfaces it here as a
    TSet<TWeakObjectPtr<UPackage>>; one click saves all of them and
    drops them from the tracking set. Stale entries pointing at packages
    that have been re-loaded since the last scan are dropped cleanly via
    the weak pointer
  • Comprehensive-vs-class-filtered World Partition scan mode toggle
    (default: comprehensive — loads every WP actor; class-filtered:
    loads only descriptors whose actor class is in the quest-component
    class set, much faster at the cost of missing per-instance component
    additions)

Stale Quest Tags scan commandlet

  • UStaleQuestTagsScanCommandlet (UCommandlet subclass; IsEditor= true, IsClient/IsServer=false, LogToConsole=true,
    ShowErrorCount=true). Run via
    UnrealEditor-Cmd.exe <project>.uproject -run=StaleQuestTagsScan
  • Args:
    • -OutputJson=<path> writes structured output. JSON shape:
      { totalCount, openCount, bpCDOCount, unloadedCount, entries: [{ source, actor, component, field, tag, package }] }
    • -FastWP runs WP iteration in class-filtered mode (skips actor
      descriptors whose class can't carry a quest component); default
      is comprehensive (loads every WP actor)
  • Exit codes for CI gating:
    • 0 — no stale references found
    • 1 — one or more stale references found
    • 2 — commandlet itself failed (init error, JSON write failure,
      Asset Registry timeout, etc.)
  • Asset Registry is primed at the top of Main via
    FAssetRegistryModule::Get().SearchAllAssets(/*synchronous*/ true)
    before any scan runs — without this the freshly-spawned commandlet
    editor reports zero AR entries and every scan finds nothing
  • Per-source summary line on stdout
    (StaleQuestTagsScan: summary — Open=N, BPCDOs=M, Unloaded=K, Total=T) plus one Warning-verbosity log line per stale entry
    (StaleQuestTagsScan: [<Source>] actor=X component=Y field=Z tag=Q.R.S package=/Game/Foo) so log-only runs without
    -OutputJson are still actionable

Windows .bat helper

  • SimpleQuestDemo/Scripts/RunStaleQuestTagsScan.bat — resolves
    UE_PATH (inline assignment in the script OR the env var), finds
    the project's .uproject, invokes UnrealEditor-Cmd.exe with
    -unattended -nopause -stdout and forwards any extra args to the
    commandlet (e.g. RunStaleQuestTagsScan.bat -OutputJson=stale-tags.json -FastWP). Forwards the commandlet's
    exit code to the caller for CI / make-script consumption

Backend scan surfaces

  • FSimpleQuestEditorUtilities::FStaleTagScanScope — boolean flag
    struct (bLoadedLevels / bActorBlueprintCDOs / bUnloadedLevels
    • bComprehensiveWPScan). Default-constructed scope =
      {bLoadedLevels=true}, preserving Tier 1 caller behavior bit-for-
      bit
  • FSimpleQuestEditorUtilities::EStaleQuestTagSourceLoaded /
    ActorBlueprintCDO / UnloadedLevelInstance. Carried on every
    FStaleQuestTagEntry so the panel and commandlet output can
    attribute each stale reference to its discovery surface
  • ScanActorForStaleTags — single helper consolidating the per-
    component walk logic shared across all three scan surfaces.
    Replaces the duplicated walk that previously lived inline in the
    Tier 1 scan path
  • ScanActorBlueprintCDOs — walks every UBlueprint asset via
    Asset Registry filter, hydrates each generated class's CDO, runs
    ScanActorForStaleTags against actor-derived CDOs only.
    Non-actor BPs short-circuit before generated-class load
  • ScanUnloadedLevels — iterates every UWorld asset in the AR,
    builds a skip set from currently-loaded editor world packages
    (Tier 1's territory), sync-loads each remaining umap, and
    dispatches by world type:
    • Non-WP world: walks PersistentLevel->Actors directly
    • WP world: hands off to ScanWorldPartitionActors
  • ScanWorldPartitionActors — uses
    FWorldPartitionHelpers::ForEachActorWithLoading (the cooker's
    per-actor load/unload iteration pattern) wrapped in
    FScopedEditorWorld for commandlet-mode lifecycle discipline.
    Optional class-filter set built lazily from
    BuildQuestComponentClassSet (FastWP mode); default is
    comprehensive (loads every WP actor)

Changed

  • Stale-tag scan summary log lines (per-tier and overall) bumped from
    Verbose to Display verbosity. The commandlet's stdout pipeline
    now produces actionable per-world descriptor counts and final
    per-source totals without needing a -LogCmds="LogSimpleQuest Verbose" override
  • The Stale Quest Tags panel's status line summarizes both the
    current visible-row count AND the last scope used (so it's clear
    at a glance whether the panel reflects a Tier-1-only refresh or a
    Full Project Scan)

Fixed

  • Commandlet-mode World Partition iteration crashed during the
    helper's per-batch GC.

    FWorldPartitionHelpers::DoCollectGarbage calls CollectGarbage( IsRunningCommandlet() ? RF_NoFlags : GARBAGE_COLLECTION_KEEPFLAGS, true) — in commandlet mode it passes RF_NoFlags as KeepFlags,
    which means only root-set objects survive the GC pass.
    RF_Standalone doesn't protect, asset-package ownership doesn't
    protect. A manually sync-loaded UWorld therefore becomes
    unreachable mid-iteration; UWorldPartition::BeginDestroy then
    asserts because the WP we just initialized isn't in Uninitialized
    state, and the sibling UWorldPartitionSubsystem (a tickable world
    subsystem) ensures because it was destroyed while still
    initialized. The fix: route every sync-loaded world through
    FScopedEditorWorld (engine's RAII helper at
    Editor/UnrealEd/Public/EditorWorldUtils.h, used internally by
    the WP convert commandlet). Construction handles AddToRoot,
    GWorld + EditorWorldContext swap, InitWorld,
    UpdateModelComponents, UpdateWorldComponents,
    UpdateLevelStreaming. Destruction handles GEditor->Cleanse,
    DestroyWorld (which routes through CleanupWorld + per-
    subsystem Deinitialize + WP::Uninitialize), RemoveFromRoot,
    GWorld + EditorWorldContext restore. The dispatch branches on
    bIsWorldInitialized so resident-from-prior-run worlds and
    externally-owned worlds (which the helper would assert against)
    scan directly without the wrapper

v0.3.3 - Catch-Up Outcome Recovery + Two-Layer State Foundations

25 Apr 07:07
eccb184

Choose a tag to compare

[0.3.3] — 2026-04-25 — Catch-Up Outcome Recovery + Two-Layer State Foundations

A targeted release that closes the catch-up outcome recovery gap left by
0.3.2's BindToQuestEvent work. Subscriptions and watchers that bind to an
already-resolved quest now recover the actual OutcomeTag — not the
previous EmptyTag placeholder — by reading from a new
UQuestResolutionSubsystem rich-record store keyed by quest tag. This
release also formalizes a two-layer state-architecture pattern: WorldState
remains the fast boolean-fact layer ("did X happen?" in O(1)); per-plugin
subsystems hold typed rich-record state ("what are the details?" in O(1)).

Three follow-on BindToQuestEvent reliability fixes ship in the same
release because they're inseparable from the catch-up behavior contract:
catch-up deferral to the next tick (fixes Accessed-None on user-cached
proxy references), per-phase duplicate-broadcast guards (closes the
one-tick race window opened by the deferral), and RegisterWithGameInstance
on the factory (canonical lifetime anchor; removes fragile dependency on
caller-side BP variable references).

Added

Two-Layer State Architecture (MVP)

  • UQuestResolutionSubsystem — new UGameInstanceSubsystem exposing a
    read-only public API for quest resolution post-mortems:
    GetQuestResolution(QuestTag), HasResolved(QuestTag),
    GetResolutionCount(QuestTag). Writes are private and gated by
    friend class UQuestManagerSubsystem — consumers physically can't
    mutate the registry. Preserves the manager's black-box doctrine:
    the manager remains the sole owner of orchestration; rich-data
    queries route through specialized read-only subsystems
  • FQuestResolutionRecordUSTRUCT(BlueprintType) with three
    BlueprintReadOnly fields: OutcomeTag, ResolutionTime (double,
    world time at resolution), ResolutionCount (per-session repeat
    counter — subsumes the prior QuestCompletionCounts map)
  • UQuestManagerSubsystem::SetQuestResolved writes the WorldState
    boolean fact AND the registry record atomically — single choke point
    preserves the two-layer write invariant. Never touch one layer
    without the other
  • Lifetime is GameInstance-scoped, mirroring UWorldStateSubsystem and
    UQuestManagerSubsystem. Records reset naturally on PIE transitions

Catch-Up Outcome Recovery

  • UQuestEventSubscription::RunCatchUp queries the registry for the
    recovered OutcomeTag instead of broadcasting FGameplayTag::EmptyTag
    on the no-filter path. Listeners that bind to an already-resolved
    quest now receive the actual outcome on OnCompleted
  • UQuestWatcherComponent::RegisterQuestWatcher's bWatchEnd catch-up
    block replaces its dual-path WorldState probing (per-filter-tag
    probes when filter set / EmptyTag fallback when not) with a single
    registry lookup followed by post-hoc OutcomeFilter matching.
    Mirrors the live WatchedQuestCompletedEvent decision path; the
    EmptyTag fallback is gone

Fixed

  • BindToQuestEvent: Accessed None on cached proxy from catch-up.
    UK2Node_AsyncAction's standard expansion calls Activate() before
    firing the user's Then exec output. Designers wiring the AsyncTask
    pin into a Set off the primary Then chain hadn't cached it yet
    at the moment Activate ran. If Activate() fired a lifecycle delegate
    synchronously inside RunCatchUp (quest already resolved), the
    designer's downstream chain (e.g. Print → Cancel(<var>)) read a
    null reference. Fixed by deferring RunCatchUp to next tick via
    SetTimerForNextTick with a weak-pointer-protected lambda — same
    pattern as engine async tasks like UAsyncTaskDownloadImage. The
    K2 node's standard expansion now reliably completes (Activate
    returns → ThenOut fires → user's Set node runs) before any catch-up
    delegate fires
  • BindToQuestEvent: duplicate broadcast in deferral window. The
    one-tick deferral introduced a narrow window during which a live
    signal could fire and, on next tick, catch-up could observe the
    same WorldState fact and broadcast the same lifecycle phase a
    second time. Closed via per-phase bSawLive* flags on
    UQuestEventSubscription set inside each Handle* after the
    bCancelled early-out and checked in RunCatchUp before each
    phase's broadcast. Listeners now receive exactly one broadcast per
    state transition. Documented edge case: a parent-tag subscription
    with the parent's own Completed fact also set during a child's
    live completion will suppress catch-up for the parent (the listener
    already received an OnCompleted for the child and would inspect
    QuestTag to differentiate). Accepted tradeoff vs. double-broadcast
  • BindToQuestEvent: missing RegisterWithGameInstance on factory.
    USimpleQuestBlueprintLibrary::BindToQuestEvent constructed the
    proxy with NewObject<UQuestEventSubscription>() but never called
    RegisterWithGameInstance(WorldContextObject) — the canonical
    UBlueprintAsyncActionBase lifetime anchor. Without it, the
    action's lifetime depended on whatever strong references happened
    to exist (BP member variable, exec stack mid-fire), SetReadyToDestroy
    was a no-op, and fire-and-forget patterns risked premature GC.
    Fix: factory now anchors via RegisterWithGameInstance. GameInstance
    owns the strong reference until SetReadyToDestroy (called by
    Cancel); PIE exit cleans up automatically when the GameInstance
    tears down

Changed

  • UQuestManagerSubsystem::QuestCompletionCounts removed —
    UQuestResolutionSubsystem::GetResolutionCount is the new
    authoritative count source. GetQuestCompletionCount on the manager
    delegates to the subsystem (back-compat for any internal callers;
    external code should switch to GetResolutionCount directly)

Full Changelog: v0.3.2...v.0.3.3

v0.3.2 - Authoring Diagnostics + Runtime Hardening

24 Apr 13:58
0896e66

Choose a tag to compare

SimpleQuest v0.3.2 — Authoring Diagnostics + Runtime Hardening

A focused patch release that closes the gap between what the compiler catches and what actually breaks at runtime. Adds two new authoring-diagnostic surfaces, a big asset-footprint win, and the first-class Blueprint subscription path for quest lifecycle events.

What's new

Authoring diagnostics

  • Prereq Tag Validator — new toolbar action on the questline graph editor. Project-wide scan for broken prereq leaves, orphan Rule Exits, and unused Rule Entries. Reports to a dedicated Quest Validator message log with clickable node navigation.
  • Stale Quest Tags panel — new nomad tab under Window → Developer Tools → Debug. Walks loaded levels for quest-component fields referencing unregistered tags; per-row Find (select + frame in viewport) and Clear (removes the stale tag, marks the actor dirty). Filterable + sortable.

Graph editor quality-of-life

  • Comment blocks on all questline graph tiers (top-level, Quest inner, LinkedQuestline view). Press C with nodes selected to wrap them. Undo/redo work correctly thanks to a new FEditorUndoClient hook that covers any third-party node type.
  • Duplicate-Outcome compile warning — fires when a single pin on a content node reaches multiple Outcome terminals sharing an OutcomeTag. Tokenized warning with navigation to every involved node.

Event subscription

  • Bind To Quest Event — new Blueprint async action with four output exec pins (Activated / Started / Completed / Deactivated). Supports hierarchical tag subscription, so subscribing on a parent tag receives events from every descendant quest. Catch-up on activation for already-asserted states. Paired C++ template SubscribeToQuestEvent<T> for direct handle-based subscriptions.

Runtime hardening

  • Defensive guards against stale gameplay tags in giver / target / watcher components. Fixes a ~10 second editor-freezing ensure() when stale tags leaked into Blueprint tag-container iteration (FGameplayTag::MatchesAny).
  • New FQuestStateTagUtils::IsTagRegisteredInRuntime + FilterToRegisteredTags helpers underpin the fix and are available to user code.

Asset footprint reduction

  • Soft class references across the Step authoring + runtime chain (ObjectiveClass, RewardClass, TargetClasses). In testing, a populated questline asset dropped from ~500 MB to ~54 KiB — roughly 10,000× smaller. Designer-authored classes now load lazily at step activation instead of being pulled in with the questline asset.

Migration

Resave any existing questline asset that authors the affected class fields. TSubclassOf and TSoftClassPtr share the same serialization shape, so UE reinterprets data transparently — the resave just drops the stale hard-dependency records from each package. No authored data lost.

Known issues

  • Async-action K2 node icon customization is impractical in UE 5.6 due to UK2Node_AsyncAction's hardcoded icon and class-iteration registration. The Bind To Quest Event node ships with the default async icon.

See CHANGELOG.md for the full detail.

v0.3.0 - Visual Graph Quest Authoring

21 Apr 08:14

Choose a tag to compare

Pre-release

Compiler, portal vocabulary, inspection surfaces, PIE debug overlay.

A major design iteration centered on the visual graph editor. The graph now compiles into runtime quest data. Designers author nested prerequisite expressions, named outcomes, reusable prerequisite rules, and hierarchical activation groups entirely in-graph. Four inspection surfaces expose runtime state during PIE. SimpleCore graduates to a multi-module plugin.

Added

Compiler + Runtime Bridge

  • FQuestlineGraphCompiler — authored graph → runtime nodes + native Gameplay Tags, persisted to Config/SimpleQuest/CompiledTags.ini
  • Compile toolbar action + Compile All menu command
  • Cross-graph parallel-path compile-time warning

Graph Authoring

  • Named outcomes replace binary Success / Failure
  • Nested prerequisite expressions — AND / OR / NOT combinators
  • Reusable Prerequisite Rules via Entry/Exit portal pair
  • Activation Groups via Entry/Exit portal pair
  • Step-level nodes with inline objective pickers
  • LinkedQuestline node embedding external questline assets
  • Custom Slate widgets for combinator, group, utility, and step nodes
  • Tagged content handles — QuestGuid survives rename; navigation stable across renames

Inspection Surfaces

  • Questline Outliner tab with double-click graph navigation
  • Entry Source panel — effective-source walk via Details customization
  • Group Examiner — activation + prereq group listings
  • Prereq Expression Examiner — nested algebraic layout with per-operator color inheritance and collapsible combinators
  • PIE Graph Debug Overlay (Tier 1) — per-state node halos during PIE
  • Prereq Examiner PIE coloring (Tier 2) — live leaf satisfaction
  • WorldState Facts panel (Tier 2) — new SimpleCoreEditor module,
    case-insensitive string filter, alphabetical sort, live refresh

Quest Event Context Model

  • FQuestObjectiveContext, FQuestNodeInfo, FQuestEventContext
  • FInstancedStruct CustomData for game-specific extension
  • All outbound events (Started, Ended, Enabled, Deactivated, Progress) carry FQuestEventContext

Runtime

  • UCountingQuestObjective subclass — counter state extracted from base UQuestObjective
  • FQuestProgressEvent — per-trigger, not just on completion
  • QuestState.<Tag>.* fact namespace (.Active, .Completed, .PendingGiver, .Deactivated, .Blocked)
  • Autowire: rule-aware priority walker with Deactivation pin auto-expansion

SimpleCore

  • UWorldStateSubsystem::GetAllFacts() — read-only accessor for inspection surfaces
  • SubscribeRawMessage<T> / PublishRawMessageFInstancedStruct delivery without type-slicing
  • New SimpleCoreEditor module — editor-side PIE debug channel + WorldState Facts panel, usable without SimpleQuest

Presentation

  • Plugin Slate style set + SVG class icons (64/16px)
  • Custom K2 node: K2Node_CompleteObjectiveWithOutcome

Changed

Breaking renames

  • Terminal: Entry → Start, Exit → Outcome
  • Portal: Group Setter/Getter → Entry/Exit
  • WorldState: Quest.State.*QuestState.*
  • Runtime classes: GroupSignalSetterNodeActivationGroupSetterNode

Graph Schema

  • Unified signal-identity model (AnyOutcome absorbs specifics on same node; different nodes never collide)
  • Self-loop rules tightened — only QuestOutcome / QuestActivation may loop back to own Activate
  • AutowireNewNode hoisted to UQuestlineNodeBase (per-node hook)

Event Payloads

  • FSignalEventBase dissolved — all events plain USTRUCTs
  • Routing tag explicit on PublishMessage
  • Tag-hierarchy walk on publish — subscriber on a parent tag receives descendant tag events

Class Hierarchy

  • UQuestNodeBase / UQuestlineNodeBase restructured as base classes
  • UQuestlineGraph.FriendlyName FText (preferred over asset name in node titles and tooltips)
  • PostEditUndo broadcasts NotifyGraphChanged (fixes dynamic-pin widget rebuilds)

Removed

Legacy events: FQuestPrerequisiteCheckFailed, FQuestRegistrationEvent, FQuestRewardEvent, FQuestStep* event family, FQuestTryStartEvent, FQuestlineEndedEvent, FSignalEventBase base class.

Legacy nodes: UQuestlineNode_Exit_Success / UQuestlineNode_Exit_Failure — replaced by single UQuestlineNode_Exit (Outcome) with designer-picked OutcomeTag.

Dead code: UQuestSignalSubsystem (renamed + moved to SimpleCore), PossibleOutcomes UPROPERTY, SignalTypes.h, SignalUtilities.h, legacy Config/Tags/SimpleQuestCompiledTags.ini location (auto-migrated).

Fixed

Undo / redo crash on dynamic-pin operations, self-loop connection validation, auto-knot insertion on illegal connections, stale tag cleanup on recompile, group setter forward-reach dedupe, hover halo off-screen paint, Group Examiner pin-role drift after portal rename, PIE debug channel subsystem resolution for Simulate In Editor, compile toolbar button no-op. See CHANGELOG.md for per-item detail.


See CHANGELOG.md for full detail.

v0.2.0 - Visual Graph Editor: Schema & Connections

29 Mar 00:36
bc0bcb1

Choose a tag to compare

Added

  • Full connection validation in UQuestlineGraphSchema::CanCreateConnection:
    • Prevents duplicate signal paths from the same source quest node
    • Prevents parallel paths to the same destination through reroute nodes
    • Enforces exit node rules: a quest node may not route multiple outputs
      to the same exit node, directly or through reroutes
    • Enforces that a single output pin leads to at most one exit node
  • Custom wire rendering via FQuestlineConnectionDrawingPolicy:
    • Green wires for Quest Success paths
    • Red wires for Quest Failure paths
    • White wires for Any Outcome and Activation paths
    • Dashed wire rendering for Prerequisites connections
    • Spline hover detection for all wire types
  • Double-clicking a wire now inserts a Reroute node in-place
  • Hotkey node placement matching standard Blueprint conventions:
    • Q + click — place Quest node
    • S + click — place Quest Success exit node
    • F + click — place Quest Failure exit node
    • R + click — place Reroute node
    • Pressing a valid key while dragging a wire places and connects
      the corresponding node immediately at the cursor
      • Node placement by hotkey aligns the cursor with the node's
        relevant input pin, matching standard Blueprint behavior
  • SQuestlineGraphPanel wrapper widget providing graph-aware input
    handling with correct Slate focus lifecycle management

v0.1.0 — Visual Graph Editor: Scaffolding

29 Mar 00:35

Choose a tag to compare

Added

  • UQuestlineGraphSchema — custom graph schema for the questline editor
  • Editor node types:
    • UQuestlineNode_Entry — questline start node (non-deletable)
    • UQuestlineNode_Quest — represents a single quest; pins: Activate,
      Prerequisites (input), Success, Failure, Any Outcome (output)
    • UQuestlineNode_Exit_Success / UQuestlineNode_Exit_Failure — terminal
      outcome nodes
    • UQuestlineNode_Knot — reroute/passthrough node with dynamic
      type propagation matching the first connected wire
  • FQuestlineGraphEditor — asset editor toolkit hosting the graph viewport
  • UQuestlineGraph — editor graph asset containing the UEdGraph
  • Basic node auto-wiring (AutowireNewNode) on all node types:
    • Output pin drags connect to Activate by default on Quest nodes
    • Input pin drags connect from Any Outcome by default on Quest nodes
    • Exit nodes only accept connections from output pins