Compare commits

...

2 Commits

Author SHA1 Message Date
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
14 changed files with 117 additions and 94 deletions

View File

@@ -57,6 +57,29 @@ 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

View File

@@ -20,11 +20,11 @@ public:
#if WITH_EDITORONLY_DATA
protected:
/** Icon visible in the editor viewport. Override the sprite in a derived Blueprint if needed. */
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "ASTERION|PS_Editor")
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "PS_Editor")
TObjectPtr<class UBillboardComponent> SpriteComponent;
/** Arrow showing the view direction in the editor viewport. */
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "ASTERION|PS_Editor")
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "PS_Editor")
TObjectPtr<class UArrowComponent> ArrowComponent;
#endif
};

View File

@@ -18,41 +18,41 @@ class PS_EDITOR_API UPS_Editor_GameInstanceSubsystem : public UGameInstanceSubsy
public:
/** Scenario name to load on the next level. Set by PROSERVE or Play button, consumed on load. */
UPROPERTY(BlueprintReadWrite, Category = "ASTERION|PS_Editor")
UPROPERTY(BlueprintReadWrite, Category = "PS_Editor")
FString PendingScenarioName;
/** Base level name associated with the pending scenario. */
UPROPERTY(BlueprintReadWrite, Category = "ASTERION|PS_Editor")
UPROPERTY(BlueprintReadWrite, Category = "PS_Editor")
FString PendingBaseLevel;
/** Last scenario/base level edited. Persists after consume — used by ReturnToEditor. */
UPROPERTY(BlueprintReadOnly, Category = "ASTERION|PS_Editor")
UPROPERTY(BlueprintReadOnly, Category = "PS_Editor")
FString LastEditedScenarioName;
UPROPERTY(BlueprintReadOnly, Category = "ASTERION|PS_Editor")
UPROPERTY(BlueprintReadOnly, Category = "PS_Editor")
FString LastEditedBaseLevel;
/** Name of the editor map to return to. Set this once at startup. */
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "ASTERION|PS_Editor")
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "PS_Editor")
FString EditorMapName;
/** True when the Play button was pressed (gameplay mode). False when just loading/editing a scenario. */
UPROPERTY(BlueprintReadWrite, Category = "ASTERION|PS_Editor")
UPROPERTY(BlueprintReadWrite, Category = "PS_Editor")
bool bIsPlayMode = false;
/** Set pending scenario + base level (for opening the editor or gameplay). */
UFUNCTION(BlueprintCallable, Category = "ASTERION|PS_Editor")
UFUNCTION(BlueprintCallable, Category = "PS_Editor")
void SetPendingScenario(const FString& ScenarioName, const FString& BaseLevel);
/** Consume the pending scenario (reads and clears). */
UFUNCTION(BlueprintCallable, Category = "ASTERION|PS_Editor")
UFUNCTION(BlueprintCallable, Category = "PS_Editor")
FString ConsumePendingScenario();
/** Open the editor with the current pending scenario + base level. */
UFUNCTION(BlueprintCallable, Category = "ASTERION|PS_Editor", meta = (WorldContext = "WorldContextObject"))
UFUNCTION(BlueprintCallable, Category = "PS_Editor", meta = (WorldContext = "WorldContextObject"))
void OpenEditor(const UObject* WorldContextObject);
/** Return to the editor, keeping the current base level and scenario. */
UFUNCTION(BlueprintCallable, Category = "ASTERION|PS_Editor", meta = (WorldContext = "WorldContextObject"))
UFUNCTION(BlueprintCallable, Category = "PS_Editor", meta = (WorldContext = "WorldContextObject"))
void ReturnToEditor(const UObject* WorldContextObject);
};

View File

@@ -17,6 +17,6 @@ public:
APS_Editor_GameMode();
/** Whether the editor is currently in edit mode (vs play/preview mode). */
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "ASTERION|PS_Editor")
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "PS_Editor")
bool bIsEditMode = true;
};

View File

@@ -30,58 +30,58 @@ class PS_EDITOR_API APS_Editor_PlayerController : public APlayerController
public:
APS_Editor_PlayerController();
UFUNCTION(BlueprintCallable, Category = "ASTERION|PS_Editor")
UFUNCTION(BlueprintCallable, Category = "PS_Editor")
UPS_Editor_SelectionManager* GetSelectionManager() const { return SelectionManager; }
UFUNCTION(BlueprintCallable, Category = "ASTERION|PS_Editor")
UFUNCTION(BlueprintCallable, Category = "PS_Editor")
UPS_Editor_UndoManager* GetUndoManager() const { return UndoManager; }
UFUNCTION(BlueprintCallable, Category = "ASTERION|PS_Editor")
UFUNCTION(BlueprintCallable, Category = "PS_Editor")
UPS_Editor_SpawnManager* GetSpawnManager() const { return SpawnManager; }
UFUNCTION(BlueprintCallable, Category = "ASTERION|PS_Editor")
UFUNCTION(BlueprintCallable, Category = "PS_Editor")
UPS_Editor_SceneSerializer* GetSceneSerializer() const { return SceneSerializer; }
/** Master catalog grouping all sub-catalogs (Tools, Characters, Props...). */
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "ASTERION|PS_Editor")
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "PS_Editor")
TObjectPtr<UPS_Editor_MasterCatalog> MasterCatalogAsset;
/** Current base level for scene save/load filtering. Auto-detected at startup, switchable via UI. */
UPROPERTY(BlueprintReadWrite, Category = "ASTERION|PS_Editor")
UPROPERTY(BlueprintReadWrite, Category = "PS_Editor")
FString CurrentBaseLevel;
/** Fired when the editor Close button is pressed (after optional save).
* Bind this in Blueprint to override the default behavior (quit application).
* If nothing is bound, the application exits. */
UPROPERTY(BlueprintAssignable, Category = "ASTERION|PS_Editor")
UPROPERTY(BlueprintAssignable, Category = "PS_Editor")
FOnEditorClosed OnEditorClosed;
/** Call to close the editor. Broadcasts OnEditorClosed; if unbound, quits the app. */
UFUNCTION(BlueprintCallable, Category = "ASTERION|PS_Editor")
UFUNCTION(BlueprintCallable, Category = "PS_Editor")
void CloseEditor();
// ---- Simulate Mode ----
/** Start simulation: enable AI, store actor positions, disable saving.
* Cancels any in-flight deferred RequestStopSimulation. */
UFUNCTION(BlueprintCallable, Category = "ASTERION|PS_Editor")
UFUNCTION(BlueprintCallable, Category = "PS_Editor")
void StartSimulation();
/** Stop simulation: disable AI, restore actor positions, re-enable saving.
* Synchronous — fires OnEditorSimulateStop and cuts AI immediately. Callers that want
* a BT grace period (with OnEditorBeforeSimulateStop) should use RequestStopSimulation. */
UFUNCTION(BlueprintCallable, Category = "ASTERION|PS_Editor")
UFUNCTION(BlueprintCallable, Category = "PS_Editor")
void StopSimulation();
/** Request a deferred stop: fires OnEditorBeforeSimulateStop immediately, then schedules
* the real StopSimulation after a grace period (500ms by default) so BT has time to
* react to whatever the BP handler does in OnEditorBeforeSimulateStop.
* Timeline Stop and the Simulate toolbar button both go through this. */
UFUNCTION(BlueprintCallable, Category = "ASTERION|PS_Editor")
UFUNCTION(BlueprintCallable, Category = "PS_Editor")
void RequestStopSimulation();
/** True while simulation is running. */
UFUNCTION(BlueprintCallable, Category = "ASTERION|PS_Editor")
UFUNCTION(BlueprintCallable, Category = "PS_Editor")
bool IsSimulating() const { return bSimulating; }
/**
@@ -89,7 +89,7 @@ public:
* saved-transforms state. Used by the timeline's Pause so the characters freeze
* in place instead of snapping back to their pre-play positions.
*/
UFUNCTION(BlueprintCallable, Category = "ASTERION|PS_Editor")
UFUNCTION(BlueprintCallable, Category = "PS_Editor")
void SetSimulationPaused(bool bPaused);
/** Load a base level as sublevel for visual context. Unloads previous sublevel.
@@ -98,7 +98,7 @@ public:
* If empty, LevelName is used as-is (legacy behavior).
* @param ExtraSublevels Additional sublevels to load alongside (e.g. always-loaded streaming levels).
*/
UFUNCTION(BlueprintCallable, Category = "ASTERION|PS_Editor")
UFUNCTION(BlueprintCallable, Category = "PS_Editor")
void LoadBaseLevelAsSublevel(const FString& LevelName, const FString& MapPath, const TArray<FString>& ExtraSublevels);
/** Overload without extra sublevels. */
@@ -106,7 +106,7 @@ public:
// ---- Editor Mode (Normal / SplinePlacement) ----
UFUNCTION(BlueprintCallable, Category = "ASTERION|PS_Editor")
UFUNCTION(BlueprintCallable, Category = "PS_Editor")
EPS_Editor_EditorMode GetEditorMode() const { return EditorMode; }
/** Enter point-placement mode for any actor implementing IPS_Editor_PointPlaceable. */

View File

@@ -45,7 +45,7 @@ public:
* @param bStripEditorComponents If true, removes SpawnableComponent and EditableComponent after spawn (cleaner for gameplay).
* @return True if the scene was loaded successfully.
*/
UFUNCTION(BlueprintCallable, Category = "ASTERION|PS_Editor", meta = (WorldContext = "WorldContextObject"))
UFUNCTION(BlueprintCallable, Category = "PS_Editor", meta = (WorldContext = "WorldContextObject"))
static bool LoadScene(const UObject* WorldContextObject, const FString& SceneName, TArray<AActor*>& OutActors,
bool bStripEditorComponents = true, EPS_Editor_SceneLoadFilter Filter = EPS_Editor_SceneLoadFilter::All);
@@ -59,37 +59,37 @@ public:
/**
* Get the directory where scenes are stored.
*/
UFUNCTION(BlueprintCallable, Category = "ASTERION|PS_Editor")
UFUNCTION(BlueprintCallable, Category = "PS_Editor")
static FString GetScenesDirectory();
/**
* Get the full file path for a scene name.
*/
UFUNCTION(BlueprintCallable, Category = "ASTERION|PS_Editor")
UFUNCTION(BlueprintCallable, Category = "PS_Editor")
static FString GetSceneFilePath(const FString& SceneName);
/** Get the full file path for a scene name and its base level. Uses level-specific subdirectory. */
UFUNCTION(BlueprintCallable, Category = "ASTERION|PS_Editor")
UFUNCTION(BlueprintCallable, Category = "PS_Editor")
static FString GetSceneFilePathForLevel(const FString& SceneName, const FString& LevelName);
/**
* Get all saved scene names. Optionally filter by level name.
* @param LevelFilter If not empty, only return scenes associated with this level.
*/
UFUNCTION(BlueprintCallable, Category = "ASTERION|PS_Editor")
UFUNCTION(BlueprintCallable, Category = "PS_Editor")
static TArray<FString> GetSavedSceneNames(const FString& LevelFilter = TEXT(""));
/**
* Get the level name associated with a saved scene (reads the JSON header without spawning).
*/
UFUNCTION(BlueprintCallable, Category = "ASTERION|PS_Editor")
UFUNCTION(BlueprintCallable, Category = "PS_Editor")
static FString GetSceneLevelName(const FString& SceneName);
/**
* Returns true while a scene is being loaded (actors are being spawned from JSON).
* Use in BeginPlay to skip default initialization — wait for OnPropertiesLoaded instead.
*/
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "ASTERION|PS_Editor")
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "PS_Editor")
static bool IsLoadingFromScene(const AActor* Actor);
/** Static flag: true while SceneLoader or SceneSerializer is spawning actors. */
@@ -99,13 +99,13 @@ public:
static bool bIsEditorModeActive;
/** Returns true if the PS_Editor runtime editor is currently active. Callable from any BP. */
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "ASTERION|PS_Editor")
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "PS_Editor")
static bool IsInEditorMode() { return bIsEditorModeActive; }
/**
* Get all saved scenes for the current level.
*/
UFUNCTION(BlueprintCallable, Category = "ASTERION|PS_Editor", meta = (WorldContext = "WorldContextObject"))
UFUNCTION(BlueprintCallable, Category = "PS_Editor", meta = (WorldContext = "WorldContextObject"))
static TArray<FString> GetSavedSceneNamesForCurrentLevel(const UObject* WorldContextObject);
private:

View File

@@ -25,47 +25,47 @@ class PS_EDITOR_API IPS_Editor_ChildMovable
public:
/** Number of movable sub-elements. */
UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "ASTERION|PS_Editor|Child Movable")
UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "PS_Editor")
int32 GetMovableChildCount() const;
/** World location of the sub-element at the given index. */
UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "ASTERION|PS_Editor|Child Movable")
UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "PS_Editor")
FVector GetChildLocation(int32 Index) const;
/** Move the sub-element at the given index to a new world position. */
UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "ASTERION|PS_Editor|Child Movable")
UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "PS_Editor")
void MoveChild(int32 Index, const FVector& NewWorldPosition);
/** Return all sub-element positions (used for undo snapshots). */
UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "ASTERION|PS_Editor|Child Movable")
UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "PS_Editor")
TArray<FVector> GetAllChildLocations() const;
/** Restore all sub-element positions (used for undo). */
UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "ASTERION|PS_Editor|Child Movable")
UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "PS_Editor")
void SetAllChildLocations(const TArray<FVector>& Positions);
/** Index of the sub-element under the cursor ray (-1 if none).
* Implement with sphere-ray intersection or any hit detection that suits your visuals. */
UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "ASTERION|PS_Editor|Child Movable")
UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "PS_Editor")
int32 GetChildIndexUnderCursor(const FVector& RayOrigin, const FVector& RayDirection) const;
/** Show or hide visual handles for the sub-elements. */
UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "ASTERION|PS_Editor|Child Movable")
UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "PS_Editor")
void SetChildHandlesVisible(bool bVisible);
/** Highlight a specific sub-element (e.g. change handle color). */
UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "ASTERION|PS_Editor|Child Movable")
UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "PS_Editor")
void HighlightChild(int32 Index);
/** Clear all sub-element highlights. */
UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "ASTERION|PS_Editor|Child Movable")
UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "PS_Editor")
void ClearChildHighlight();
/** Export sub-element positions to the CustomProperties map for scene saving. */
UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "ASTERION|PS_Editor|Child Movable")
UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "PS_Editor")
void ExportChildLocations(TMap<FString, FString>& OutProperties) const;
/** Import sub-element positions from the CustomProperties map on scene load. */
UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "ASTERION|PS_Editor|Child Movable")
UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "PS_Editor")
void ImportChildLocations(const TMap<FString, FString>& Properties);
};

View File

@@ -15,25 +15,25 @@ struct FPS_Editor_EditablePropertyEntry
/**
* Property path: "ComponentName.PropertyName" or just "PropertyName" for actor-level.
*/
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "ASTERION|PS_Editor")
UPROPERTY(EditAnywhere, BlueprintReadWrite)
FName Path;
/** Display name override for the runtime editor UI. If empty, the property name is used. */
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "ASTERION|PS_Editor")
UPROPERTY(EditAnywhere, BlueprintReadWrite)
FString DisplayName;
/**
* If true, calls OnEditorPropertiesChanged (IPS_Editor_EditableInterface)
* after changing this property, so the actor can re-run its initialization logic.
*/
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "ASTERION|PS_Editor")
UPROPERTY(EditAnywhere, BlueprintReadWrite)
bool bNeedsReinit = false;
/**
* If true, the actor is destroyed and respawned with all current properties applied,
* then OnEditorPropertiesChanged is called. Use when reinit alone is not enough.
*/
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "ASTERION|PS_Editor")
UPROPERTY(EditAnywhere, BlueprintReadWrite)
bool bNeedsRespawn = false;
/**
@@ -43,7 +43,7 @@ struct FPS_Editor_EditablePropertyEntry
* Replicated (ideally ReplicatedUsing=OnRep_X) — the server-side timeline writes the value
* and standard UE replication pushes it to clients.
*/
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "ASTERION|PS_Editor")
UPROPERTY(EditAnywhere, BlueprintReadWrite)
bool bAnimatable = false;
};
@@ -56,7 +56,7 @@ struct FPS_Editor_EditablePropertyEntry
* 2. Pick a property from "Add Property" (filtered to that component)
* 3. It auto-adds to "Editable Properties" as "Component.Property"
*/
UCLASS(ClassGroup = "ASTERION|PS Editor", meta = (BlueprintSpawnableComponent, DisplayName = "PS Editor Editable"))
UCLASS(ClassGroup = "PS Editor", meta = (BlueprintSpawnableComponent, DisplayName = "PS Editor Editable"))
class PS_EDITOR_API UPS_Editor_EditableComponent : public UActorComponent
{
GENERATED_BODY()

View File

@@ -15,7 +15,7 @@ struct FPS_Editor_PropertyOption
GENERATED_BODY()
/** Label shown in the runtime editor combo box. */
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "ASTERION|PS_Editor")
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "PS_Editor")
FString DisplayName;
/**
@@ -24,7 +24,7 @@ struct FPS_Editor_PropertyOption
* the full class path; for an enum you'd use the NameString; for a free FString,
* any value you want stored.
*/
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "ASTERION|PS_Editor")
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "PS_Editor")
FString Value;
};
@@ -51,21 +51,21 @@ public:
* The new value is already applied when this is called.
* @param PropertyPath The property that changed (e.g. "CharacterType" or "MyComponent.SomeProperty").
*/
UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "ASTERION|PS_Editor")
UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "PS_Editor")
void OnEditorPropertyChanged(const FString& PropertyPath);
/**
* Called when a property with bNeedsReinit is changed.
* The new value is already applied. Use this to re-run initialization logic.
*/
UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "ASTERION|PS_Editor")
UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "PS_Editor")
void OnEditorNeedsReinit();
/**
* Called on the NEW actor after a respawn triggered by bNeedsRespawn.
* All properties have been applied. Use this for post-respawn setup.
*/
UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "ASTERION|PS_Editor")
UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "PS_Editor")
void OnEditorNeedsRespawn();
/**
@@ -73,7 +73,7 @@ public:
* Fires both in editor (SceneSerializer) and in gameplay (SceneLoader).
* Use this to re-initialize your actor based on the loaded property values.
*/
UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "ASTERION|PS_Editor")
UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "PS_Editor")
void OnPropertiesLoaded();
/**
@@ -81,7 +81,7 @@ public:
* Use this to show/hide editor-only helpers (VR guides, placement aids, debug visuals, etc.).
* @param bIsEditing True when entering edit mode, false when leaving (Play, return to game).
*/
UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "ASTERION|PS_Editor")
UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "PS_Editor")
void OnEditorModeChanged(bool bIsEditing);
/**
@@ -90,7 +90,7 @@ public:
* the hood). Use this to kick off behaviour that should only happen while AI is live
* (spawn FX, reset state counters, unlock input, etc.).
*/
UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "ASTERION|PS_Editor")
UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "PS_Editor")
void OnEditorSimulateStart();
/**
@@ -102,7 +102,7 @@ public:
* Timing: fires at the "AI cut" moment, AFTER any grace period the timeline applied
* (typically 500ms after Stop was clicked). BT is still live but about to be disabled.
*/
UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "ASTERION|PS_Editor")
UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "PS_Editor")
void OnEditorSimulateStop();
/**
@@ -130,7 +130,7 @@ public:
* event still fires + grace period still granted)
* Does NOT fire for direct StopSimulation calls (rare — e.g. forced editor close).
*/
UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "ASTERION|PS_Editor")
UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "PS_Editor")
void OnEditorBeforeSimulateStop();
/**
@@ -138,7 +138,7 @@ public:
* Use this to update visuals dynamically based on property changes, animate helpers, etc.
* @param DeltaTime Time since last frame.
*/
UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "ASTERION|PS_Editor")
UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "PS_Editor")
void OnEditorTick(float DeltaTime);
/**
@@ -150,7 +150,7 @@ public:
* @return List of option strings. Empty = use default widget / fall through to the
* structured variant below.
*/
UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "ASTERION|PS_Editor")
UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "PS_Editor")
TArray<FString> GetCustomOptionsForProperty(const FString& PropertyPath) const;
/**
@@ -164,7 +164,7 @@ public:
* @param PropertyPath The property path (e.g. "WeaponName" or "MyComponent.WeaponType").
* @return List of display/value pairs. Empty = fall back to the flat-string variant.
*/
UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "ASTERION|PS_Editor")
UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "PS_Editor")
TArray<FPS_Editor_PropertyOption> GetCustomOptionsWithValuesForProperty(const FString& PropertyPath) const;
/**
@@ -185,6 +185,6 @@ public:
* @param PropertyPath The property path (e.g. "Weapon" or "MyComponent.WeaponType").
* @return DisplayName to show in the combo, or empty string to fall back.
*/
UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "ASTERION|PS_Editor")
UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "PS_Editor")
FString GetCurrentDisplayForProperty(const FString& PropertyPath) const;
};

View File

@@ -14,7 +14,7 @@ struct FPS_Editor_SpawnEntry
GENERATED_BODY()
/** The Blueprint class to spawn. Must have a PS_Editor_SpawnableComponent for name/category. */
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "ASTERION|PS_Editor")
UPROPERTY(EditAnywhere, BlueprintReadWrite)
TSubclassOf<AActor> ActorClass;
};
@@ -34,12 +34,12 @@ class PS_EDITOR_API UPS_Editor_SpawnCatalog : public UPrimaryDataAsset
public:
/** List of spawnable actors. */
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "ASTERION|PS_Editor")
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "PS_Editor")
TArray<FPS_Editor_SpawnEntry> Entries;
#if WITH_EDITOR
/** Captures UE-generated thumbnails for all Blueprints in the catalog and assigns them to their SpawnableComponents. */
UFUNCTION(CallInEditor, Category = "ASTERION|PS_Editor")
UFUNCTION(CallInEditor, Category = "PS_Editor")
void CaptureAllThumbnails();
#endif
};
@@ -55,18 +55,18 @@ struct FPS_Editor_BaseLevelEntry
GENERATED_BODY()
/** Display name shown in the editor UI and used for scene filtering. */
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "ASTERION|PS_Editor")
UPROPERTY(EditAnywhere, BlueprintReadWrite)
FString DisplayName;
/**
* Full map path used for OpenLevel and sublevel loading.
* Example: "/Game/Maps/Warehouse"
*/
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "ASTERION|PS_Editor")
UPROPERTY(EditAnywhere, BlueprintReadWrite)
FString MapPath;
/** Thumbnail image shown in the BaseLevel selection popup. */
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "ASTERION|PS_Editor")
UPROPERTY(EditAnywhere, BlueprintReadWrite)
TObjectPtr<UTexture2D> Thumbnail;
/**
@@ -75,7 +75,7 @@ struct FPS_Editor_BaseLevelEntry
* but not automatically loaded when the base level is a sublevel itself.
* Full map paths, e.g. "/Game/Maps/Warehouse_Lighting"
*/
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "ASTERION|PS_Editor")
UPROPERTY(EditAnywhere, BlueprintReadWrite)
TArray<FString> AdditionalSublevels;
/**
@@ -83,7 +83,7 @@ struct FPS_Editor_BaseLevelEntry
* Their entries are merged with the global catalogs (MasterCatalog::Catalogs).
* Empty = only global catalogs are shown.
*/
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "ASTERION|PS_Editor")
UPROPERTY(EditAnywhere, BlueprintReadWrite)
TArray<TObjectPtr<UPS_Editor_SpawnCatalog>> Catalogs;
/**
@@ -92,7 +92,7 @@ struct FPS_Editor_BaseLevelEntry
* are, the first one wins; if none is, scenarios without a base level keep whatever
* sublevel is currently loaded.
*/
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "ASTERION|PS_Editor")
UPROPERTY(EditAnywhere, BlueprintReadWrite)
bool bIsDefault = false;
};
@@ -108,7 +108,7 @@ class PS_EDITOR_API UPS_Editor_MasterCatalog : public UPrimaryDataAsset
public:
/** Sub-catalogs to load. Each one contributes its entries to the runtime spawn UI. */
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "ASTERION|PS_Editor")
UPROPERTY(EditAnywhere, BlueprintReadWrite)
TArray<TObjectPtr<UPS_Editor_SpawnCatalog>> Catalogs;
/**
@@ -116,6 +116,6 @@ public:
* DisplayName is used for UI display and scene filtering.
* MapPath is the full package path for OpenLevel (e.g. "/Game/Maps/Warehouse").
*/
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "ASTERION|PS_Editor")
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "PS_Editor")
TArray<FPS_Editor_BaseLevelEntry> BaseLevels;
};

View File

@@ -9,7 +9,7 @@
* Marker component for Blueprints that can be spawned via the PS_Editor catalogue.
* Add this component to any Blueprint to make it appear in the runtime spawn catalogue.
*/
UCLASS(ClassGroup = "ASTERION|PS Editor", meta = (BlueprintSpawnableComponent, DisplayName = "PS Editor Spawnable"))
UCLASS(ClassGroup = "PS Editor", meta = (BlueprintSpawnableComponent, DisplayName = "PS Editor Spawnable"))
class PS_EDITOR_API UPS_Editor_SpawnableComponent : public UActorComponent
{
GENERATED_BODY()

View File

@@ -31,7 +31,7 @@ public:
virtual int32 GetCurrentPointCount() const override { return GetNumPoints(); }
/** Get the underlying USplineComponent (for AI path following, etc.). */
UFUNCTION(BlueprintCallable, Category = "ASTERION|PS_Editor|Spline")
UFUNCTION(BlueprintCallable, Category = "PS_Editor")
USplineComponent* GetSplineComponent() const { return SplineComp; }
/** Spline display color (override in Blueprint subclasses for variants). */
@@ -75,7 +75,7 @@ public:
virtual void UpdateVisualization() override;
/** Re-apply SplineColor to materials (call after changing SplineColor at runtime). */
UFUNCTION(BlueprintCallable, Category = "ASTERION|PS_Editor|Spline")
UFUNCTION(BlueprintCallable, Category = "PS_Editor")
virtual void UpdateSplineMaterials() override;
// ---- Handle interaction ----

View File

@@ -46,39 +46,39 @@ public:
// ---- Playback controls ----
/** Start / resume playback from CurrentTime. */
UFUNCTION(BlueprintCallable, Category = "ASTERION|PS_Editor|Timeline")
UFUNCTION(BlueprintCallable, Category = "PS_Editor")
void Play();
/** Pause playback, keeping CurrentTime. Simulation keeps running. */
UFUNCTION(BlueprintCallable, Category = "ASTERION|PS_Editor|Timeline")
UFUNCTION(BlueprintCallable, Category = "PS_Editor")
void Pause();
/** Pause playback, rewind to CurrentTime=0, stop simulation and apply t=0 values. */
UFUNCTION(BlueprintCallable, Category = "ASTERION|PS_Editor|Timeline")
UFUNCTION(BlueprintCallable, Category = "PS_Editor")
void Stop();
/** Rewind to t=0 and immediately replay from the start (re-enables simulation). */
UFUNCTION(BlueprintCallable, Category = "ASTERION|PS_Editor|Timeline")
UFUNCTION(BlueprintCallable, Category = "PS_Editor")
void Restart();
/** Jump to a specific time (seconds, clamped to [0, Duration]). Applies values immediately. */
UFUNCTION(BlueprintCallable, Category = "ASTERION|PS_Editor|Timeline")
UFUNCTION(BlueprintCallable, Category = "PS_Editor")
void Seek(float Time);
/** True while Tick is advancing CurrentTime. */
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "ASTERION|PS_Editor|Timeline")
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "PS_Editor")
bool IsPlaying() const { return bIsPlaying; }
/** Get current playback time (seconds). */
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "ASTERION|PS_Editor|Timeline")
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "PS_Editor")
float GetCurrentTime() const { return CurrentTime; }
/** Get the configured duration (seconds). */
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "ASTERION|PS_Editor|Timeline")
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "PS_Editor")
float GetDuration() const { return Data.Duration; }
/** Change the timeline duration. Values <= 0 are clamped to 0.1. */
UFUNCTION(BlueprintCallable, Category = "ASTERION|PS_Editor|Timeline")
UFUNCTION(BlueprintCallable, Category = "PS_Editor")
void SetDuration(float NewDuration);
// ---- Tracks / keys ----

View File

@@ -35,16 +35,16 @@ class PS_EDITOR_API APS_Editor_HUD : public AHUD
public:
/** Which UI implementation to instantiate. Set via Details panel on the HUD Blueprint. */
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "ASTERION|PS_Editor|UI")
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "PS_Editor")
EPS_Editor_UIMode UIMode = EPS_Editor_UIMode::NewSlate;
/** UMG Widget Blueprint class to spawn when UIMode == UMG.
* Must derive from UPS_Editor_MainWidget_UMG. */
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "ASTERION|PS_Editor|UI", meta = (EditCondition = "UIMode == EPS_Editor_UIMode::UMG"))
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "PS_Editor", meta = (EditCondition = "UIMode == EPS_Editor_UIMode::UMG"))
TSoftClassPtr<UPS_Editor_MainWidget_UMG> UMGWidgetClass;
/** Returns the active main editor widget (cast as needed). */
UFUNCTION(BlueprintCallable, Category = "ASTERION|PS_Editor|UI")
UFUNCTION(BlueprintCallable, Category = "PS_Editor")
UUserWidget* GetMainWidget() const { return MainWidget; }
/** Backward-compat accessor for the refactored Slate widget (returns nullptr in Legacy/UMG modes). */