Compare commits

...

28 Commits

Author SHA1 Message Date
6eca4fc94b Add editor-only floor cut to hide primitives above the MandatoryAnchor
Per-component visibility/collision toggle gated on bounds-min Z vs
(AnchorZ + Height). Snapshotted before mutation and restored on
disable, so the feature is non-destructive. Wired into the toolbars of
both Slate widgets and exposed as Blueprint methods on the UMG widget.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-22 12:07:28 +02:00
4a98c6402b Rename LoadScene/LoadSceneAdditive to LoadScenario/UnloadScenario
The old naming suggested UE level loading; what these calls actually do
is spawn actors described by a .pss scenario file into the already-loaded
UE level. Renamed for clarity:

  SceneLoader::LoadScene         -> LoadScenario
  SceneLoader::LoadSceneFromData -> LoadScenarioFromData
  SceneLoader::LoadSceneAdditive -> dropped (merged into LoadScenario)
  SceneLoader::UnloadSceneAdditive -> UnloadScenario
  SceneSerializer::LoadScene     -> LoadScenario
  AdditiveSpawnedActors          -> TrackedSpawnedActors

LoadScenario now branches on the filter: when Filter == PreloadOnly the
timeline subsystem is skipped entirely (no SetData, no ActorId
registration, no auto-play) so a briefing scene can drop in player
placement markers without disturbing its own timeline. All other filter
values keep the full gameplay behaviour (timeline + AI-readiness poll).

Every load tracks its actors so a follow-up UnloadScenario() can destroy
them — supports the briefing flow where the player toggles between
scenarios at runtime.

Breaking change for external BP callers: any node binding the old
LoadScene / LoadSceneAdditive / UnloadSceneAdditive functions will show
up as missing on next BP compile; replace with LoadScenario /
UnloadScenario (identical or simpler signatures).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-20 15:42:57 +02:00
f6efc00c73 Add LoadSceneAdditive / UnloadSceneAdditive for briefing scene preload
Lets a briefing scene materialize only the preload-tagged actors from a
scenario .pss (player placement markers, briefing props) without
clearing the current world, pushing timeline data, registering ActorIds,
or starting any auto-play. The actors spawned by each LoadSceneAdditive
call are tracked in a static TWeakObjectPtr array; UnloadSceneAdditive
destroys every still-valid one and clears the array.

Typical BP usage on scenario change:
  UnloadSceneAdditive(this) -> LoadSceneAdditive(this, NewSceneName)

No BP-side bookkeeping required. Filter defaults to PreloadOnly which
matches the intended briefing use case; the parameter stays exposed for
other niche cases (ExcludePreload, All).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-20 15:10:52 +02:00
34280de44a Update memory notes for UE 5.7 host project (was 5.5)
Host project migrated from PROSERVE_UE_5_5 to PROSERVE_UE_5_7 in April
2026. CLAUDE.md, memory index, project notes, and build reference now
point at the new path / target / engine version. The 5.5 host is
flagged deprecated to avoid accidental builds against it.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-20 15:09:17 +02:00
ac91e4e8f9 Bug Fix 2026-05-06 17:53:21 +02:00
c7ec3e9653 Per-scenario type field driven by project enum (Settings dropdown + JSON peek)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-05 19:04:59 +02:00
d9ed016297 Reset bIsEditorModeActive on editor-PC EndPlay and SceneLoader entry
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-05 13:28:39 +02:00
67ac3ffab7 Demote diagnostic logs to Verbose + drop redundant ones
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-02 23:29:36 +02:00
8f7476d2db Mandatory actors auto-spawn + navmesh surface overlay (custom material)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-30 16:14:27 +02:00
b9ec67f9ff Runtime nav settings + custom debug viz (P key) + editable restore on stop
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-30 11:46:27 +02:00
2a53d12e68 Per-scenario dynamic nav opt-in + Settings popup + timeline restore fixes
Dynamic navigation toggle (per scenario):
- New PS_Editor_NavUtils helpers wrap UNavigationSystemV1 / RecastNavMesh
  protected APIs (RuntimeGeneration field + bEnableDrawing) via the C++
  using-trick so we can switch the level's nav mode and toggle visualization
  without engine modifications.
- FPS_Editor_SceneData gets bUseDynamicNavigation persisted in JSON.
- SpawnManager carries the live state plus a session-only bShowNavMesh
  preference (never saved).
- SceneSerializer reads/writes the flag and applies it on load (editor mode).
- SceneLoader applies it on the gameplay path before spawning actors so
  scenarios that need runtime nav rebuild get it both in editor and at
  runtime via the project's GameMode + SceneLoader::LoadScene flow.

UI:
- New "Settings" toolbar button in both Legacy and UMG widgets opens a popup
  with two checkboxes:
    * Use Dynamic Navigation - saved per scenario, live toggle
    * Show NavMesh (P) - session only, mimics editor's P key (green nav
      visualization)
- Pawn binds P to IA_ToggleNavMesh -> SpawnManager.bShowNavMesh, in sync
  with the popup checkbox.

Timeline restore correctness:
- AddOrUpdateKeyAtCurrentTime, RemoveKey, SetKeyTime, SetKeyInterpolate now
  invalidate LastAppliedValues[TrackIndex] so a key edit isn't masked by a
  stale per-track cache entry from a previous apply.
- RestoreBaseline now prefers a key at t=0 (within epsilon) over the
  captured pre-animation baseline. Updating a t=0 key + Stop now restores
  the new value instead of snapping back to the original. Tracks without a
  t=0 key still fall back to the pre-animation baseline.

Restart simulation rewind:
- Restart() used to call Stop() + Play() but Play immediately cancelled the
  pending RequestStopSimulation timer set by Stop, so transforms were never
  restored - characters stayed where they were.
- RequestStopSimulation now takes bImmediate; when true, OnEditorBeforeSimulateStop
  still fires (so BP behaviour-reset hooks run) but StopSimulation runs
  synchronously, restoring transforms before Play() recaptures them.
- Restart inlines the reset (RestoreBaseline + immediate stop + Play) so
  rewinding actually rewinds.

Build:
- Re-add NavigationSystem to PS_Editor.Build.cs (needed for ARecastNavMesh
  + ANavigationData access in NavUtils).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-30 10:40:18 +02:00
49cb480e15 Add F key to frame the current selection (Maya/Blender style)
Pressing F computes the combined bounding box of every selected actor,
then animates the camera over 0.25s (smoothstep ease) to a distance that
fits the box in view while keeping the current viewing direction. The
orbit focal point and distance are synced so a subsequent Alt+drag
pivots around the new center.

No-ops cleanly when nothing is selected. Falls back to actor pivots
when no component reports valid bounds, and clamps the radius to a
minimum so point-like actors don't pull the camera onto them.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-29 18:00:54 +02:00
7f2600f0b8 ChildMovable: rotation support + ChildMovable-aware help bar + clear previous highlight
- IPS_Editor_ChildMovable gains GetChildRotation / RotateChild and the
  GetAll/SetAll rotation pair for undo snapshots. BP impls can clamp axes
  (e.g. Yaw-only) inside RotateChild.
- Pawn captures initial rotations at gizmo drag start and applies the
  rotate gizmo's delta-quat onto the active child only (no longer
  rotating every selected actor when a child handle is active).
- FPS_Editor_SplinePointAction carries optional Old/NewRotations arrays
  applied alongside positions on Undo/Redo for ChildMovable actors.
- Pawn now calls ClearChildHighlight before HighlightChild so picking a
  new child no longer leaves the previous one stuck in highlight state.
- MainWidget + Legacy widget detect when ActivePlacementActor is a
  ChildMovable in SplinePlacement mode and swap the help bar text
  ("CHILDREN: ...") + hide the irrelevant Delete-point button.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-29 16:07:39 +02:00
c520f3ccd9 Group BP interface functions under "PS Editor|Editable" / "PS Editor|Child Movable"
The two BlueprintNativeEvent interfaces previously dumped every function
into a flat "PS Editor" category, so the BP class members panel listed
them all together. Use the pipe-separated category to put each interface
under its own submenu.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-29 13:44:25 +02:00
2f8633d23b Flatten BP categories: drop "ASTERION|" prefix and inner-struct categories
Two cleanups in one pass:

1. Replace `Category = "ASTERION|PS_Editor"` (and its sub-pipe variants like
   "...|Timeline", "...|Spline", "...|UI", "...|Child Movable") with a single
   flat `Category = "PS_Editor"` everywhere — UFUNCTIONs and class-level
   UPROPERTYs alike. The pipe-separated hierarchy was redundant (the plugin
   already lives in a "PS_Editor" namespace), and the BP right-click search
   gets a cleaner single-segment grouping.

2. Drop the `Category` attribute from struct field UPROPERTYs in
   FPS_Editor_SpawnEntry, FPS_Editor_BaseLevelEntry, and
   FPS_Editor_EditablePropertyEntry. Struct fields don't need a category —
   they inherit display from the parent property's category. Keeping a
   category on inner fields was causing the SpawnCatalog details panel to
   render the Entries array TWICE (once under the literal "ASTERION|PS Editor"
   header pulled from the inner ActorClass field, once under the proper
   "ASTERION > PS_Editor" hierarchy from the outer Entries property).

Also normalize the two `UCLASS(ClassGroup = "ASTERION|PS Editor")` declarations
on EditableComponent and SpawnableComponent to `ClassGroup = "PS Editor"` for
consistency.

Touches 13 header files; no behavior change — Categories are purely cosmetic
(Details panel grouping, BP right-click menu organization).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-28 15:43:34 +02:00
98de4dff6f Document PIE-only nav quirk: AI testing requires Standalone Game
Several attempts were made to fix a PIE-specific RecastNavMesh registration
failure that occurs when the editor's "Play" button calls OpenLevel(BaseLevel)
from inside a PIE session — the level's RecastNavMesh ends up pending-kill
and UE silently substitutes an empty AbstractNavData fallback, breaking AI
pathfinding. Workarounds tried (and reverted): cascading re-registrations
through RegisterNavData / OnInitializeActors / AddNavigationSystemToWorld /
Build / SpawnMissingNavigationData; intermediate transition map; duplicated
BaseLevel sublevel suffixed _default. None worked — the cause is internal
to PIE's OpenLevel handling.

Standalone Game and packaged builds initialize the nav system correctly,
so production is unaffected. Document the workflow so future contributors
don't waste time chasing this PIE-only bug.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 12:38:03 +02:00
b51f063d52 Pivot-anchor fallback for no-collision spawns + always-snap on catalog
Ground snap:
- When an actor has no colliding components at all (e.g. a VR start
  marker, placement helper, or any BP whose collision is only enabled
  on entering editor mode), SnapActorToGround now anchors the PIVOT
  directly on the ground instead of falling back to the full-bounds
  bbox bottom. The full-bounds bottom could be far below the pivot
  (helper meshes, arrows, visual markers) and was lifting objects
  hundreds of units into the air.
- Actors with collision keep the original behavior (collision-bbox
  bottom rests on the ground).

Catalog spawn:
- Always snap to ground (drop the bAutoSnapToGround gate) — a user
  clicking in the catalog expects the object to land on the ground
  every time; the flag is kept for gameplay-side spawning via
  SceneLoader.
- SpawnActor now uses AlwaysSpawn instead of
  AdjustIfPossibleButAlwaysSpawn so UE never pushes the actor
  vertically to resolve overlaps — SnapActorToGround is the
  authoritative placement step.

CLAUDE.md workflow rule:
- Document that behavior / logic changes require explaining the plan
  first and waiting for go-ahead; only diagnostic-only additions
  (temporary UE_LOG) are fine without approval.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 12:53:01 +02:00
93b9c7d927 template demo 2026-04-24 10:44:44 +02:00
050ea4eb2f Gizmo follow, cooker-friendly assets, and proper ground snapping
Gizmo usability:
- Refresh the gizmo every tick while simulation is running so it glues to
  animated / AI-driven / timeline-driven actors instead of freezing at the
  pose they had when selected
- Clear the pawn's active sub-element drag state on FinishPointPlacement /
  CancelPointPlacement — previously, after clicking Done on a spline, the
  gizmo jumped to the center cube but the internal ActiveSplinePointIndex
  stayed on the last clicked CP, so dragging the gizmo moved that CP
  instead of the whole actor
- Expose UpdateGizmo() publicly on the SelectionManager so the
  PlayerController Tick can drive it

Package cooking (fix gray gizmos / missing outline in packaged builds):
- Convert every runtime LoadObject<> soft-string path into a UPROPERTY
  TObjectPtr<> filled via ConstructorHelpers::FObjectFinder at CDO
  construction time. Hard refs are picked up by the cooker so plugin
  Content assets (M_PS_Editor_Gizmo, M_PS_Editor_SelectionOutline,
  M_PS_Editor_SplineLine, and the engine basic shapes the spline actor
  uses) are reliably staged
- Touches APS_Editor_Gizmo, APS_Editor_Pawn (outline post-process
  material), APS_Editor_SplineActor (gizmo material, spline line
  material, center cube mesh, handle sphere mesh)

Ground placement (PS_Editor_GroundSnap):
- New PS_Editor_GroundSnap utility centralising placement logic for
  catalogue spawns, End-key snap-to-ground, and spline CP placement
- ProjectCameraRayToGround: aim down from a point in front of the camera
  when the camera ray misses (no more "looking at sky spawns object in
  mid-air"); classify the primary hit by normal to re-cast from below a
  ceiling / step back from a wall so the object lands on the floor
  underneath / at the foot of the surface rather than embedded in it
- SnapActorToGround: start the downward ray from INSIDE the actor's
  volume (pivot + small buffer), ignoring the actor itself — avoids the
  "raised a bit high, pressed End, teleports to upper story" bug where
  starting above the actor's bbox could cross the current room's ceiling
  and find the upper floor as the first floor-like surface
- Uses GetActorBounds(bOnlyCollidingComponents=true) with a fallback so
  characters snap from their capsule bounds rather than bbox including
  non-colliding meshes / attached weapons / child actors that used to
  lift pawns into the air
- Multi-hit trace + normal.Z >= 0.3 filter skips walls, ceilings, and
  backfaces when finding the first true floor below

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 10:43:45 +02:00
9877c1b6de Fix (missing) timeline tracks + lost editable properties on 2nd scene load
FindActorById now skips stale TWeakObjectPtr entries instead of returning
the first null match, RegisterActorId prunes stale + same-GUID entries
before adding, and Clear() resets the ActorIds map so a fresh scenario
starts with no ghost references.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 16:08:26 +02:00
721c9bb5c3 Unify simulate/timeline controls + BeforeStop event + AI auto-possess safety net
- Remove the Simulate toolbar button (both Legacy + UMG widgets). Play /
  Pause / Restart / Stop now live exclusively on the Timeline panel, and
  the panel is visible by default (no more toggle button on the toolbar).
  Keeps a global Play button on the Legacy toolbar that opens the level
  outside the editor.

- Move the grace-period / deferred-stop logic out of the TimelineSubsystem
  and into APS_Editor_PlayerController::RequestStopSimulation. This lets
  the timeline Stop button AND the UMG topbar simulate buttons share the
  same event sequence: OnEditorBeforeSimulateStop fires immediately (after
  RestoreBaseline), then a 500ms BT grace period, then StopSimulation
  fires OnEditorSimulateStop and cuts AI. New event
  OnEditorBeforeSimulateStop added to the editable interface with full
  timing docs.

- Drop the early-return in TimelineSubsystem::Play when Tracks.Num()==0 —
  the timeline Play button is now the only way to start the sim, so it
  must drive StartSimulation unconditionally.

- StartSimulation now force-calls SpawnDefaultController() on any pawn
  that enters sim without a Controller but has an AIControllerClass set,
  so AutoPossessAI=Disabled or race-lost possessions don't leave the BT
  unreachable.

- Route the UMG topbar Play / Simulate buttons and the "simulate" command
  through RequestStopSimulation too, so every path emits the full event
  sequence consistently.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-22 18:52:48 +02:00
e999a11349 Timeline: pre-animation baseline restore on Stop + display cache
Stop now rewinds animated properties to the value they had before the
timeline first acted on them — not "first keyframe's value" as before.

- New per-track baseline stored runtime-only in a TMap keyed by
  (ActorId, PropertyPath), so it survives track reordering and isn't
  persisted to JSON (avoids stale data in legacy scenarios).
- Captured once: at track creation (first "+" on a fresh property) or
  at SetData time for tracks loaded from a scenario. Never re-captured,
  so scrub-then-Play doesn't shift the baseline to whatever the cursor
  happened to be parked on.
- Stop writes the baselines back via ImportText, fires OnEditorPropertyChanged
  + RepNotify per track, then defers the actual StopSimulation by 100ms
  so the BehaviorTree has a grace period to see the restored values via
  OnRep / OnEditorPropertyChanged and transition to a neutral state before
  AI tick is cut.
- Play cancels any in-flight deferred StopSimulation (Restart = Stop +
  Play keeps the simulation running across the handoff).
- SetData and Clear reset the baseline map so loading a new scenario
  doesn't inherit the previous one's baselines.

Also cache GetCurrentDisplayForProperty calls in RefreshDynamicPropertyValues:
the property panel was invoking the BP interface every tick per visible
combo. Now each row stores LastExportedValue + LastDisplayToShow and
short-circuits if the property's exported text is unchanged — BP only
runs on actual value transitions (seek, timeline keyframe, manual edit).
Duplicated across MainWidget and MainWidget_Legacy.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-22 16:13:37 +02:00
bc491b7b94 Display-vs-value custom dropdown options + UI mode parity docs
Extend the EditableInterface with two new BlueprintNativeEvents so property
combos can show human-friendly labels while storing technical values:

- GetCustomOptionsWithValuesForProperty returns TArray<FPS_Editor_PropertyOption>
  where each pair carries a DisplayName (shown in the combo) and a Value
  (written to the property via ImportText). Wins over the legacy flat-string
  variant when non-empty. Ideal for TSubclassOf / TSoftClassPtr properties
  driven by a DataTable lookup where the stored value is a class path.
- GetCurrentDisplayForProperty lets the actor authoritatively resolve the
  DisplayName from the current stored value, for cases where the automatic
  reverse lookup (normalize class path, exact-match) isn't reliable — e.g.
  several Values collapse to the same label, or the format is user-specific.

UI refresh now asks the actor first, then falls back to a normalized match
(strips the `TypeName'...'` wrapper from ExportText output so it compares
apples to apples with a plain /Game/Path.X_C string).

Same logic duplicated across both widget implementations (MainWidget and
MainWidget_Legacy) since the project can switch UI mode at runtime. The new
CLAUDE.md rule makes that requirement explicit.

Also documented in CLAUDE.md:
- Editor-vs-gameplay execution boundary: which interface methods fire where,
  including the gotcha that OnEditorPropertyChanged *does* fire in gameplay
  on the server when the timeline animates a property.
- UI modes: keep all MainWidget* implementations in lockstep to avoid the
  "I edited the file but nothing changed" class of bug.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-22 12:43:40 +02:00
b069c20af3 Fix character rotation in editor + defer timeline play until AI ready
- Save pawn rotation/movement settings (bUseControllerRotation*, bOrient-
  RotationToMovement, MovementMode) on SpawnableComponent when editor
  disables AI, then restore them exactly in StartSimulation
- Re-apply rotation/movement overrides on StopSimulation so the character
  doesn't auto-rotate after returning from Simulate
- Poll spawned pawns' BrainComponent on scene load (gameplay via
  SceneLoader); start timeline playback only once every AI BT is running.
  Fixes timeline keys firing before the AI-driven sequence (walk) starts

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-21 18:27:42 +02:00
d9d358e694 Add ui elements 2026-04-20 07:36:23 +02:00
5b9174bdcd hud 2026-04-18 20:20:57 +02:00
bc226338e0 hud 2026-04-18 20:13:33 +02:00
bdceecbe56 Add 3-way UI mode switch: Legacy / NewSlate / UMG
Introduce a switchable UI stack so the original Slate widget stays available
as a fallback while the new CodeDesign-inspired redesign and a future UMG
Widget Blueprint implementation coexist.

- PS_Editor_MainWidget_Legacy: pre-refactor Slate widget, restored from HEAD
- PS_Editor_MainWidget: refactored floating-HUD Slate widget with style asset,
  9-slice panels/buttons, rounded brushes, icon pack, Play/Pause/Stop icons,
  spec-aligned palette and radii, File dropdown, outliner per-item icons,
  command palette
- PS_Editor_MainWidget_UMG: abstract base class exposing a Blueprint-callable
  API (SetTransformMode, SaveScenario, Simulate, Selection...) and delegates
  (OnToolChanged, OnSelectionChanged...) so a WBP deriving from it can drive
  the editor end-to-end
- APS_Editor_HUD: UIMode enum + UMGWidgetClass soft ref, picks the right
  widget at BeginPlay; separate typed accessors for each flavor

Adds UI style system (UPS_Editor_UIStyleAsset DataAsset + FPS_Editor_UIStyle
helper) with runtime PNG loading for icons and 9-slice brushes from
Content/UI/icons/ and Content/UI/unreal_9slice/.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 19:57:04 +02:00
532 changed files with 11210 additions and 903 deletions

View File

@@ -1,4 +1,4 @@
- [PS_Editor plugin project](project_ps_editor.md) — Runtime editor plugin for UE 5.5, Phases 1-4 done, outline material pending
- [PS_Editor plugin project](project_ps_editor.md) — Runtime editor plugin for UE 5.7 (migrated from 5.5 in April 2026), Phases 1-4 done, outline material pending
- [Underscore naming](feedback_naming.md) — Always use PS_Editor_ prefix with underscores in file/class names
- [UE5 build process](reference_build.md) — CLI build command for the project (Build.bat path, target name, flags)
- [UE5 build process](reference_build.md) — CLI build command for PROSERVE_UE_5_7 (5.5 host deprecated), Build.bat path, target name, junction check
- [Build workflow](feedback_build_workflow.md) — Prefer Live Coding (Ctrl+Alt+F11) when UE is open, CLI build only when UE is closed

View File

@@ -1,10 +1,10 @@
---
name: PS_Editor plugin project
description: Runtime editor plugin for UE 5.5 - object placement, scene editing, JSON save/load in packaged builds
description: Runtime editor plugin for UE 5.7 - object placement, scene editing, JSON save/load in packaged builds
type: project
---
PS_Editor is a runtime editor plugin for Unreal Engine 5.5 for the PROSERVE project.
PS_Editor is a runtime editor plugin for Unreal Engine 5.7 for the PROSERVE project. The plugin was started on UE 5.5; the host project migrated to UE 5.7 in April 2026 and the plugin follows. Build details in reference_build.md.
Key decisions:
- UI: UMG driven by C++ (Slate)

View File

@@ -1,18 +1,30 @@
---
name: UE5 build process
description: How to build the PS_ProserveEditor UE 5.5 project from command line on Windows
description: How to build the PROSERVE host project (currently UE 5.7) from command line on Windows
type: reference
---
UE 5.5 is installed at `C:\Program Files\Epic Games\UE_5.5` (found via registry key `HKLM\SOFTWARE\EpicGames\Unreal Engine\5.5`).
The PS_Editor plugin is developed in `E:\ASTERION\GIT\PS_Editor\Unreal\Plugins\PS_Editor` and consumed by the PROSERVE host project via an NTFS junction. Always build the host project, not the plugin in isolation.
**Current host project (April 2026):** `E:\ASTERION\SVN\DEV\PROSERVE_UE_5_7\PROSERVE_UE_5_7.uproject` (Unreal Engine 5.7)
The previous 5.5 host (`PROSERVE_UE_5_5`) is deprecated — do not use unless explicitly asked.
UE 5.7 is installed at `C:\Program Files\Epic Games\UE_5.7` (found via registry key `HKLM\SOFTWARE\EpicGames\Unreal Engine\5.7`).
Build command (Editor target, Development):
```
"C:/Program Files/Epic Games/UE_5.5/Engine/Build/BatchFiles/Build.bat" PS_ProserveEditorEditor Win64 Development -Project="C:/ASTERION/GIT/PS_ProserveEditor/Unreal/PS_ProserveEditor.uproject" -WaitMutex -FromMsBuild
"C:/Program Files/Epic Games/UE_5.7/Engine/Build/BatchFiles/Build.bat" PROSERVE_UE_5_7Editor Win64 Development -Project="E:/ASTERION/SVN/DEV/PROSERVE_UE_5_7/PROSERVE_UE_5_7.uproject" -WaitMutex -FromMsBuild
```
**How to apply:** Use this command to compile after code changes. Timeout should be set to 600000ms (10 min) for safety. The target name is `PS_ProserveEditorEditor` (Editor suffix) for editor builds.
**How to apply:** Use this command to compile after code changes. Timeout should be set to 600000ms (10 min) for safety. The target name is `PROSERVE_UE_5_7Editor` (Editor suffix) for editor builds.
**When to use CLI vs Live Coding:**
- Live Coding (Ctrl+Alt+F11): .cpp only changes (no new UCLASS, USTRUCT, UENUM, UPROPERTY in headers)
- Full CLI build (UE must be closed): new files, header changes with new reflected types, Build.cs changes
**Plugin junction verification:**
```
ls -la E:/ASTERION/SVN/DEV/PROSERVE_UE_5_7/Plugins/ | grep PS_Editor
```
Should show a symlink pointing to `/e/ASTERION/GIT/PS_Editor/Unreal/Plugins/PS_Editor`. If broken, the host project will build against a stale or missing plugin copy.

110
CLAUDE.md
View File

@@ -1,7 +1,7 @@
# PS_ProserveEditor - Project Guidelines
## Project Overview
Unreal Engine 5.5 project with runtime editor plugin (PS_Editor). The plugin enables runtime scene editing in packaged builds: object placement, property editing, scenario definition, splines, lighting, and JSON scene save/load.
Unreal Engine 5.7 project with runtime editor plugin (PS_Editor). The plugin enables runtime scene editing in packaged builds: object placement, property editing, scenario definition, splines, lighting, and JSON scene save/load. The plugin was started on UE 5.5; the host project migrated to UE 5.7 in April 2026.
## Architecture
- **Plugin**: PS_Editor (runtime plugin, must work in packaged builds)
@@ -10,6 +10,91 @@ Unreal Engine 5.5 project with runtime editor plugin (PS_Editor). The plugin ena
- **Serialization**: JSON via Json/JsonUtilities modules (FJsonObjectConverter)
- **Input**: Enhanced Input system (AZERTY: arrows + A/Q movement, E/R/T modes)
## Editor vs. Gameplay execution boundary — CRITICAL
The plugin runs in two distinct modes and it's essential never to leak editor-only code
into the gameplay runtime loaded via `UPS_Editor_SceneLoader::LoadScene` (standalone).
### Editor runtime mode
Activated when the `APS_Editor_PlayerController` + `APS_Editor_HUD` + `UPS_Editor_MainWidget*`
stack is created (typical during authoring — the user is in the runtime editor).
- `UPS_Editor_SceneLoader::bIsEditorModeActive == true`
- The HUD widget drives property editing, catalogue spawn, save/load, simulate, timeline
- Interface methods called **only** by the widget (never in gameplay):
- `GetCustomOptionsForProperty` / `GetCustomOptionsWithValuesForProperty` / `GetCurrentDisplayForProperty`
- `OnEditorModeChanged`, `OnEditorTick`
- `OnEditorNeedsReinit`, `OnEditorNeedsRespawn`
### Gameplay mode
The project (PROSERVE) loads a scenario via `UPS_Editor_SceneLoader::LoadScene` from a GameMode
BeginPlay, without ever instantiating the editor PlayerController / HUD / widget.
- `UPS_Editor_SceneLoader::bIsEditorModeActive == false`
- Actors spawn, their properties are applied from JSON, `OnPropertiesLoaded` fires once
- The timeline subsystem auto-plays if the scenario has tracks, driven **server-side only**
### Interface methods that fire in BOTH modes
- `OnPropertiesLoaded` — after every scene load, editor or gameplay (applied once post-spawn)
- `OnEditorPropertyChanged` — fires both in editor (on UI edit via `ApplyPropertyFromUI`) AND
in gameplay (on server when the timeline applies a new keyframe value via
`TimelineSubsystem::ApplyTrackAtTime`). Despite the name, the "Editor" refers to the
PS_Editor plugin — the event is the canonical hook for "a property changed at runtime".
In gameplay only the **server** invokes it; client-side reaction must go through
`ReplicatedUsing=OnRep_X` RepNotify on the replicated property.
### Rule when adding new features
- Anything tied to property panel UI (new interface method, new widget logic, dropdown
helpers, etc.) lives in the widget only — no extra work to scope it out of gameplay
- Anything added to the `SceneLoader::LoadScene` path runs in gameplay and must handle
`NM_Client`, replication, and server-authority correctly (the timeline subsystem is the
reference pattern)
- Expensive BP implementations of editor-UI interface methods (DataTable lookups, class
resolution, etc.) can stay expensive — they never run outside the authoring session
### How to guard code that could run in either mode
```cpp
if (UPS_Editor_SceneLoader::IsInEditorMode())
{
// editor-only behaviour
}
```
### Known PIE-only AI quirk — use Standalone for AI testing
When the editor's "Play" button calls `UGameplayStatics::OpenLevel(BaseLevel)` from a PIE
session, the BaseLevel's `RecastNavMesh` actor ends up pending-kill at registration time
(UE logs `RegistrationFailed_DataPendingKill` ×2 and silently spawns an empty
`AbstractNavData-Default` as fallback). All `FindPathAsync` / `ProjectPointToNavigation`
queries return empty after that — pathfinding looks broken with no error.
This is **PIE-specific**:
- Standalone Game (Window → Standalone) — works correctly
- Packaged builds — work correctly
- PIE direct on BaseLevel (no editor map / OpenLevel involved) — works correctly
- PIE on editor map → click Play (OpenLevel transition inside PIE) — broken
Multiple workaround attempts have all failed (cascading re-registrations through
`RegisterNavData` / `OnInitializeActors` / `AddNavigationSystemToWorld` / `Build` /
`SpawnMissingNavigationData`; transition map; duplicated sublevel suffixed `_default`).
The cause is internal to PIE's OpenLevel handling and cannot be fixed from the plugin
side without engine modifications.
**Workflow:** for AI iteration, run **Standalone Game** instead of PIE. It exercises the
exact same code path as packaged builds.
or gate on `bIsCurrentlyLoading` to skip BP init during JSON restore.
## UI Modes — IMPORTANT for widget changes
The runtime editor ships with **multiple widget implementations** that must all stay in sync:
- `PS_Editor_MainWidget.cpp/.h` — modern UMG-based widget (primary target long-term)
- `PS_Editor_MainWidget_Legacy.cpp/.h` — Slate-only legacy widget (**currently active** as of April 2026)
- `PS_Editor_MainWidget_UMG.cpp/.h` — UMG variant
The user can switch between them via a `UIMode` setting. At any given time only one is running, but the project keeps all three compiled.
**Rule:** when changing any behavior inside a widget (new interface calls, new property row logic, new UI controls, bug fixes in RebuildDynamicProperties / ApplyPropertyFromUI / RefreshDynamicPropertyValues, etc.), **apply the change to ALL widget implementations at the same time**. Otherwise the user's current UIMode gets the fix and the others don't — the "it's like you didn't change anything" bug.
Symptom that the wrong widget was edited: logs you added don't appear in the Output Log despite the build being clean and the DLL being correctly loaded. Always grep for the function name across all `PS_Editor_MainWidget*.cpp` files before assuming a single edit is sufficient.
## Naming Conventions
- All plugin files and classes use the `PS_Editor_` prefix (e.g., `PS_Editor_GameMode`, `PS_Editor_Pawn`)
- Code and comments in English
@@ -21,15 +106,28 @@ Unreal Engine 5.5 project with runtime editor plugin (PS_Editor). The plugin ena
- When fixing a bug, fix the root cause, not the symptom.
- If something requires error handling or validation to work reliably, include it without asking.
## Workflow — CRITICAL
- **Do not make behavior / logic changes without first explaining the plan and waiting for the
user's go-ahead.** Explain what the change will be, why it fixes the issue, and what side
effects it could have. Only after the user approves (or asks to proceed), apply the edit.
- Diagnostic-only additions (temporary `UE_LOG`, read-only inspections) are fine without
approval because they can't regress the project.
- Refactors, algorithm changes, comportement changes, new defaults, removed features — all
require approval first, even when they seem obvious.
- When revealing new data (e.g. logs) suggests a fix, STATE the proposed fix in plain words
and wait. Don't pre-emptively write + apply the code change in the same turn.
## Build
- Engine: Unreal Engine 5.5
- Plugin location: `Unreal/Plugins/PS_Editor/`
- Game module: `PS_ProserveEditor` (in `Unreal/Source/PS_ProserveEditor/`)
- Engine: Unreal Engine 5.7 (host project migrated from 5.5 in April 2026)
- Plugin location: `Unreal/Plugins/PS_Editor/` (consumed via NTFS junction from the PROSERVE host project's `Plugins/` directory)
- Host project: `E:\ASTERION\SVN\DEV\PROSERVE_UE_5_7\PROSERVE_UE_5_7.uproject` — always build this, not the plugin in isolation
- Game module: `PROSERVE_UE_5_7` (in the host project's `Source/`)
- CLI build command (Editor, Development):
```
"<UE_INSTALL>/Engine/Build/BatchFiles/Build.bat" PS_ProserveEditorEditor Win64 Development -Project="<REPO>/Unreal/PS_ProserveEditor.uproject" -WaitMutex -FromMsBuild
"C:/Program Files/Epic Games/UE_5.7/Engine/Build/BatchFiles/Build.bat" PROSERVE_UE_5_7Editor Win64 Development -Project="E:/ASTERION/SVN/DEV/PROSERVE_UE_5_7/PROSERVE_UE_5_7.uproject" -WaitMutex -FromMsBuild
```
UE install path can be found via registry: `HKLM\SOFTWARE\EpicGames\Unreal Engine\5.5` > `InstalledDirectory`
UE install path can be found via registry: `HKLM\SOFTWARE\EpicGames\Unreal Engine\5.7` > `InstalledDirectory`
- The 5.5 host (`PROSERVE_UE_5_5`) is deprecated — do not build against it unless explicitly asked.
## Project Memory
Shared project memory is stored in `.claude/memory/` (versioned in the repository). When starting a new session on any machine, read these files to restore context:

BIN
UI/BP_PS_Editor_HUD.uasset Normal file

Binary file not shown.

BIN
UI/assets/gizmo_rotate.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 71 KiB

BIN
UI/assets/gizmo_scale.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

BIN
UI/assets/thumb_fx.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

BIN
UI/assets/thumb_npc.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

BIN
UI/assets/thumb_prop.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

BIN
UI/assets/thumb_spline.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

BIN
UI/assets/thumb_weapon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

BIN
UI/icons/Bell.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

BIN
UI/icons/Box.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

BIN
UI/icons/Camera.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.0 KiB

BIN
UI/icons/Check.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

BIN
UI/icons/ChevronDown.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

BIN
UI/icons/ChevronLeft.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

BIN
UI/icons/ChevronRight.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

BIN
UI/icons/Command.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.3 KiB

BIN
UI/icons/Diamond.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

BIN
UI/icons/Download.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

BIN
UI/icons/Eye.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.8 KiB

BIN
UI/icons/EyeOff.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.7 KiB

BIN
UI/icons/Film.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

BIN
UI/icons/Flag.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

BIN
UI/icons/Folder.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

BIN
UI/icons/Globe.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

BIN
UI/icons/Grid.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

BIN
UI/icons/Help.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

BIN
UI/icons/Layers.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

BIN
UI/icons/Lightbulb.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.0 KiB

BIN
UI/icons/Lock.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

BIN
UI/icons/Map.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

BIN
UI/icons/Minus.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

BIN
UI/icons/More.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

BIN
UI/icons/Move.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

BIN
UI/icons/PanelBottom.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

BIN
UI/icons/PanelRight.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

BIN
UI/icons/Pause.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

BIN
UI/icons/Play.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

BIN
UI/icons/Plus.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

BIN
UI/icons/Record.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

BIN
UI/icons/Rotate.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

BIN
UI/icons/Save.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

BIN
UI/icons/Scale.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

BIN
UI/icons/Search.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.7 KiB

BIN
UI/icons/Settings.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

BIN
UI/icons/Sidebar.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

BIN
UI/icons/SkipBack.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

BIN
UI/icons/SkipFwd.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

BIN
UI/icons/Snap.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

BIN
UI/icons/Spline.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

BIN
UI/icons/Stop.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

BIN
UI/icons/Target.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

BIN
UI/icons/Unlock.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

BIN
UI/icons/User.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.5 KiB

BIN
UI/icons/Zap.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

BIN
UI/icons/icons_16/Bell.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 324 B

BIN
UI/icons/icons_16/Box.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 408 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 374 B

BIN
UI/icons/icons_16/Check.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 231 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 218 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 216 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 218 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 340 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 262 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 292 B

BIN
UI/icons/icons_16/Eye.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 397 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 382 B

BIN
UI/icons/icons_16/Film.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 302 B

BIN
UI/icons/icons_16/Flag.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 246 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 250 B

BIN
UI/icons/icons_16/Globe.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 461 B

BIN
UI/icons/icons_16/Grid.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 338 B

BIN
UI/icons/icons_16/Help.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 441 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 382 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 338 B

BIN
UI/icons/icons_16/Lock.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 320 B

BIN
UI/icons/icons_16/Map.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 349 B

BIN
UI/icons/icons_16/Minus.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 145 B

BIN
UI/icons/icons_16/More.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 220 B

BIN
UI/icons/icons_16/Move.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 370 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 226 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 239 B

BIN
UI/icons/icons_16/Pause.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 206 B

BIN
UI/icons/icons_16/Play.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 292 B

BIN
UI/icons/icons_16/Plus.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 200 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 287 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 375 B

BIN
UI/icons/icons_16/Save.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 335 B

BIN
UI/icons/icons_16/Scale.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 307 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 389 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 441 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 241 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 307 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 299 B

BIN
UI/icons/icons_16/Snap.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 315 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 345 B

Some files were not shown because too many files have changed in this diff Show More