Releases: TheGeebus/SimpleQuest
v0.3.5] — Stale Quest Tags Polish
[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
onListView->GetNumItemsSelected()andIsEnabledbound 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
FScopedTransactionwraps the entire batch — Ctrl+Z
restores all Loaded + Unloaded clears as one atomic operation ClearOneEntryhelper extracted fromHandleClearClickedso
single-row and bulk paths share the source-specific dirty-resolution
logic (Loaded →MarkPackageDirtyon actor; BP CDO → outer-chain
walk to UBlueprint +MarkBlueprintAsModified; Unloaded →
MarkPackageDirtyon 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
ActorsTouchedByClearmap (per-actorSource+PackagePath)
populated on every Clear (single + bulk). BP CDO entries opt out —
their "actor" is a CDO, not a level instance - New
UpdateFromAffectedActorsmethod dropsAllEntriesfor tracked
actors then re-emits viaScanActorForStaleTags PostUndo/PostRedocallUpdateFromAffectedActorsinstead of
Refresh(LastScope). Net effect: undo of a cleared instance entry
is now sub-millisecond regardless ofLastScope. 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
onFSimpleQuestEditorUtilities. Walks an actor's components,
dispatches by component type (Giver / Target / Watcher), emits
oneFStaleQuestTagEntryper 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 renamedHandleRefreshClicked→
HandleScanLoadedClickedfor 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 UQuestResolutionSubsystemresolution-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 VerboseSStaleQuestTagsPanelactor-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 logsUQuestEventSubscriptionsubsystem-resolution failure Warning
gained a "Common causes:" hint —BindToQuestEventfired pre-init,
or theWorldContextObjectpin is wired to an actor whose UWorld
isn't valid
Removed
- Anonymous-namespace
ScanActorForStaleTagsin
SimpleQuestEditorUtils.cpp— replaced by the public version above SimpleQuest.Debug.ScanBlueprintCDOsand
SimpleQuest.Debug.ScanUnloadedLevelsconsole 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 inPostUndo(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 throughFProperty::PreEditChange/
PostEditChangeflow 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/
ScanWorldPartitionActorsrequires 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)
[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 inFScopedSlowTaskwith
progress notifications. The panel caches the last-used scope so
PostUndo/PostRedore-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(UCommandletsubclass;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 }] }-FastWPruns 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 found1— one or more stale references found2— commandlet itself failed (init error, JSON write failure,
Asset Registry timeout, etc.)
- Asset Registry is primed at the top of
Mainvia
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
-OutputJsonare 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, invokesUnrealEditor-Cmd.exewith
-unattended -nopause -stdoutand 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/bUnloadedLevelsbComprehensiveWPScan). Default-constructed scope =
{bLoadedLevels=true}, preserving Tier 1 caller behavior bit-for-
bit
FSimpleQuestEditorUtilities::EStaleQuestTagSource—Loaded/
ActorBlueprintCDO/UnloadedLevelInstance. Carried on every
FStaleQuestTagEntryso the panel and commandlet output can
attribute each stale reference to its discovery surfaceScanActorForStaleTags— 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 pathScanActorBlueprintCDOs— walks everyUBlueprintasset via
Asset Registry filter, hydrates each generated class's CDO, runs
ScanActorForStaleTagsagainst actor-derived CDOs only.
Non-actor BPs short-circuit before generated-class loadScanUnloadedLevels— iterates everyUWorldasset 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->Actorsdirectly - WP world: hands off to
ScanWorldPartitionActors
- Non-WP world: walks
ScanWorldPartitionActors— uses
FWorldPartitionHelpers::ForEachActorWithLoading(the cooker's
per-actor load/unload iteration pattern) wrapped in
FScopedEditorWorldfor 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
VerbosetoDisplayverbosity. 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 Tagspanel'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::DoCollectGarbagecallsCollectGarbage( IsRunningCommandlet() ? RF_NoFlags : GARBAGE_COLLECTION_KEEPFLAGS, true)— in commandlet mode it passesRF_NoFlagsasKeepFlags,
which means only root-set objects survive the GC pass.
RF_Standalonedoesn't protect, asset-package ownership doesn't
protect. A manually sync-loadedUWorldtherefore becomes
unreachable mid-iteration;UWorldPartition::BeginDestroythen
asserts because the WP we just initialized isn't inUninitialized
state, and the siblingUWorldPartitionSubsystem(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 handlesAddToRoot,
GWorld+EditorWorldContextswap,InitWorld,
UpdateModelComponents,UpdateWorldComponents,
UpdateLevelStreaming. Destruction handlesGEditor->Cleanse,
DestroyWorld(which routes throughCleanupWorld+ per-
subsystemDeinitialize+WP::Uninitialize),RemoveFromRoot,
GWorld+EditorWorldContextrestore. The dispatch branches on
bIsWorldInitializedso 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
[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— newUGameInstanceSubsystemexposing 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 subsystemsFQuestResolutionRecord—USTRUCT(BlueprintType)with three
BlueprintReadOnlyfields:OutcomeTag,ResolutionTime(double,
world time at resolution),ResolutionCount(per-session repeat
counter — subsumes the priorQuestCompletionCountsmap)UQuestManagerSubsystem::SetQuestResolvedwrites 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
UWorldStateSubsystemand
UQuestManagerSubsystem. Records reset naturally on PIE transitions
Catch-Up Outcome Recovery
UQuestEventSubscription::RunCatchUpqueries the registry for the
recoveredOutcomeTaginstead of broadcastingFGameplayTag::EmptyTag
on the no-filter path. Listeners that bind to an already-resolved
quest now receive the actual outcome onOnCompletedUQuestWatcherComponent::RegisterQuestWatcher'sbWatchEndcatch-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-hocOutcomeFiltermatching.
Mirrors the liveWatchedQuestCompletedEventdecision path; the
EmptyTag fallback is gone
Fixed
- BindToQuestEvent: Accessed None on cached proxy from catch-up.
UK2Node_AsyncAction's standard expansion callsActivate()before
firing the user'sThenexec output. Designers wiring the AsyncTask
pin into aSetoff the primaryThenchain hadn't cached it yet
at the moment Activate ran. IfActivate()fired a lifecycle delegate
synchronously insideRunCatchUp(quest already resolved), the
designer's downstream chain (e.g.Print → Cancel(<var>)) read a
null reference. Fixed by deferringRunCatchUpto next tick via
SetTimerForNextTickwith a weak-pointer-protected lambda — same
pattern as engine async tasks likeUAsyncTaskDownloadImage. 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-phasebSawLive*flags on
UQuestEventSubscriptionset inside eachHandle*after the
bCancelledearly-out and checked inRunCatchUpbefore each
phase's broadcast. Listeners now receive exactly one broadcast per
state transition. Documented edge case: a parent-tag subscription
with the parent's ownCompletedfact also set during a child's
live completion will suppress catch-up for the parent (the listener
already received anOnCompletedfor the child and would inspect
QuestTagto differentiate). Accepted tradeoff vs. double-broadcast - BindToQuestEvent: missing
RegisterWithGameInstanceon factory.
USimpleQuestBlueprintLibrary::BindToQuestEventconstructed the
proxy withNewObject<UQuestEventSubscription>()but never called
RegisterWithGameInstance(WorldContextObject)— the canonical
UBlueprintAsyncActionBaselifetime 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 viaRegisterWithGameInstance.GameInstance
owns the strong reference untilSetReadyToDestroy(called by
Cancel); PIE exit cleans up automatically when the GameInstance
tears down
Changed
UQuestManagerSubsystem::QuestCompletionCountsremoved —
UQuestResolutionSubsystem::GetResolutionCountis the new
authoritative count source.GetQuestCompletionCounton the manager
delegates to the subsystem (back-compat for any internal callers;
external code should switch toGetResolutionCountdirectly)
Full Changelog: v0.3.2...v.0.3.3
v0.3.2 - Authoring Diagnostics + Runtime Hardening
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
Cwith nodes selected to wrap them. Undo/redo work correctly thanks to a newFEditorUndoClienthook 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+FilterToRegisteredTagshelpers 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
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 toConfig/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 —
QuestGuidsurvives 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
SimpleCoreEditormodule,
case-insensitive string filter, alphabetical sort, live refresh
Quest Event Context Model
FQuestObjectiveContext,FQuestNodeInfo,FQuestEventContextFInstancedStruct CustomDatafor game-specific extension- All outbound events (Started, Ended, Enabled, Deactivated, Progress) carry
FQuestEventContext
Runtime
UCountingQuestObjectivesubclass — counter state extracted from baseUQuestObjectiveFQuestProgressEvent— per-trigger, not just on completionQuestState.<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 surfacesSubscribeRawMessage<T>/PublishRawMessage—FInstancedStructdelivery 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:
GroupSignalSetterNode→ActivationGroupSetterNode
Graph Schema
- Unified signal-identity model (
AnyOutcomeabsorbs specifics on same node; different nodes never collide) - Self-loop rules tightened — only
QuestOutcome/QuestActivationmay loop back to ownActivate AutowireNewNodehoisted toUQuestlineNodeBase(per-node hook)
Event Payloads
FSignalEventBasedissolved — 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/UQuestlineNodeBaserestructured as base classesUQuestlineGraph.FriendlyNameFText(preferred over asset name in node titles and tooltips)PostEditUndobroadcastsNotifyGraphChanged(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
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 nodeS + click— place Quest Success exit nodeF + click— place Quest Failure exit nodeR + 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
- Node placement by hotkey aligns the cursor with the node's
SQuestlineGraphPanelwrapper widget providing graph-aware input
handling with correct Slate focus lifecycle management
v0.1.0 — Visual Graph Editor: Scaffolding
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 nodesUQuestlineNode_Knot— reroute/passthrough node with dynamic
type propagation matching the first connected wire
FQuestlineGraphEditor— asset editor toolkit hosting the graph viewportUQuestlineGraph— editor graph asset containing theUEdGraph- 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