Per-scenario type field driven by project enum (Settings dropdown + JSON peek)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,13 +1,19 @@
|
||||
#include "PS_Editor_GameInstanceSubsystem.h"
|
||||
#include "PS_Editor_SceneLoader.h"
|
||||
#include "PS_Editor_SceneData.h"
|
||||
#include "Kismet/GameplayStatics.h"
|
||||
#include "Misc/FileHelper.h"
|
||||
#include "JsonObjectConverter.h"
|
||||
|
||||
void UPS_Editor_GameInstanceSubsystem::SetPendingScenario(const FString& ScenarioName, const FString& BaseLevel)
|
||||
void UPS_Editor_GameInstanceSubsystem::SetPendingScenario(const FString& ScenarioName, const FString& BaseLevel, uint8 ScenarioType)
|
||||
{
|
||||
PendingScenarioName = ScenarioName;
|
||||
PendingBaseLevel = BaseLevel;
|
||||
PendingScenarioType = ScenarioType;
|
||||
LastEditedScenarioName = ScenarioName;
|
||||
LastEditedBaseLevel = BaseLevel;
|
||||
UE_LOG(LogTemp, Log, TEXT("PS_Editor: Pending scenario set: '%s' (base level: '%s')"), *ScenarioName, *BaseLevel);
|
||||
UE_LOG(LogTemp, Log, TEXT("PS_Editor: Pending scenario set: '%s' (base level: '%s', type: %u)"),
|
||||
*ScenarioName, *BaseLevel, ScenarioType);
|
||||
}
|
||||
|
||||
FString UPS_Editor_GameInstanceSubsystem::ConsumePendingScenario()
|
||||
@@ -45,3 +51,44 @@ void UPS_Editor_GameInstanceSubsystem::ReturnToEditor(const UObject* WorldContex
|
||||
|
||||
OpenEditor(WorldContextObject);
|
||||
}
|
||||
|
||||
bool UPS_Editor_GameInstanceSubsystem::ReadScenarioMetadata(const FString& ScenarioName, uint8& OutScenarioType, FString& OutBaseLevel) const
|
||||
{
|
||||
OutScenarioType = 0;
|
||||
OutBaseLevel.Empty();
|
||||
|
||||
if (ScenarioName.IsEmpty())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const FString FilePath = UPS_Editor_SceneLoader::GetSceneFilePath(ScenarioName);
|
||||
FString JsonString;
|
||||
if (!FFileHelper::LoadFileToString(JsonString, *FilePath))
|
||||
{
|
||||
UE_LOG(LogTemp, Warning, TEXT("PS_Editor: ReadScenarioMetadata — file not found: %s"), *FilePath);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Parse the full SceneData. We could parse just the metadata fields with a custom
|
||||
// reader, but FJsonObjectConverter is fast enough for the scenario sizes we deal with
|
||||
// (tens of actors max), and reusing the same struct keeps version handling consistent.
|
||||
FPS_Editor_SceneData SceneData;
|
||||
if (!FJsonObjectConverter::JsonObjectStringToUStruct(JsonString, &SceneData, 0, 0))
|
||||
{
|
||||
UE_LOG(LogTemp, Warning, TEXT("PS_Editor: ReadScenarioMetadata — failed to parse JSON for '%s'"), *ScenarioName);
|
||||
return false;
|
||||
}
|
||||
|
||||
OutScenarioType = SceneData.ScenarioType;
|
||||
OutBaseLevel = SceneData.LevelName;
|
||||
return true;
|
||||
}
|
||||
|
||||
uint8 UPS_Editor_GameInstanceSubsystem::GetScenarioTypeFromFile(const FString& ScenarioName) const
|
||||
{
|
||||
uint8 Type = 0;
|
||||
FString DummyLevel;
|
||||
ReadScenarioMetadata(ScenarioName, Type, DummyLevel);
|
||||
return Type;
|
||||
}
|
||||
|
||||
@@ -40,9 +40,36 @@ public:
|
||||
UPROPERTY(BlueprintReadWrite, Category = "PS_Editor")
|
||||
bool bIsPlayMode = false;
|
||||
|
||||
/** Set pending scenario + base level (for opening the editor or gameplay). */
|
||||
/** Pending scenario type as uint8 — index into UPS_Editor_MasterCatalog::ScenarioTypeEnum.
|
||||
* Set by the Play button alongside PendingScenarioName. The gameplay GameMode reads it
|
||||
* (Byte to Enum) and routes its scenario-type-specific logic. Default 0 = first enum entry. */
|
||||
UPROPERTY(BlueprintReadWrite, Category = "PS_Editor")
|
||||
uint8 PendingScenarioType = 0;
|
||||
|
||||
/** Set pending scenario + base level + type (for opening the editor or gameplay). */
|
||||
UFUNCTION(BlueprintCallable, Category = "PS_Editor")
|
||||
void SetPendingScenario(const FString& ScenarioName, const FString& BaseLevel);
|
||||
void SetPendingScenario(const FString& ScenarioName, const FString& BaseLevel, uint8 ScenarioType = 0);
|
||||
|
||||
/**
|
||||
* Pre-parse a scenario file from disk (without spawning anything) to extract its
|
||||
* persisted metadata: scenario type and base level. Use this from PROSERVE GameMode
|
||||
* BeginPlay (or earlier) to know what type a scenario is BEFORE deciding to load it,
|
||||
* when the standard Play-button flow hasn't populated PendingScenarioType.
|
||||
*
|
||||
* @param ScenarioName Name of the scenario file (without extension).
|
||||
* @param OutScenarioType Set to the scenario's saved type (uint8 index into MasterCatalog::ScenarioTypeEnum). 0 on failure.
|
||||
* @param OutBaseLevel Set to the scenario's saved base level name. Empty on failure.
|
||||
* @return True if the file was found and parsed successfully.
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "PS_Editor")
|
||||
bool ReadScenarioMetadata(const FString& ScenarioName, uint8& OutScenarioType, FString& OutBaseLevel) const;
|
||||
|
||||
/**
|
||||
* Convenience: read just the scenario type from a file. Returns 0 if the file isn't
|
||||
* found / can't be parsed (which is also the default for a saved-but-untyped scenario).
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "PS_Editor")
|
||||
uint8 GetScenarioTypeFromFile(const FString& ScenarioName) const;
|
||||
|
||||
/** Consume the pending scenario (reads and clears). */
|
||||
UFUNCTION(BlueprintCallable, Category = "PS_Editor")
|
||||
|
||||
@@ -68,4 +68,10 @@ struct FPS_Editor_SceneData
|
||||
* cooked Static nav, which has zero runtime cost. Default: false. */
|
||||
UPROPERTY()
|
||||
bool bUseDynamicNavigation = false;
|
||||
|
||||
/** Project-specific scenario type as a uint8 (index into UPS_Editor_MasterCatalog::ScenarioTypeEnum).
|
||||
* Selected by the user via the Settings popup dropdown. Defaults to 0 (= first enum entry) for
|
||||
* legacy scenarios that were saved before this field existed. */
|
||||
UPROPERTY()
|
||||
uint8 ScenarioType = 0;
|
||||
};
|
||||
|
||||
@@ -171,6 +171,7 @@ bool UPS_Editor_SceneSerializer::SaveScene(const FString& SceneName, UPS_Editor_
|
||||
if (SpawnManager)
|
||||
{
|
||||
SceneData.bUseDynamicNavigation = SpawnManager->bUseDynamicNavigation;
|
||||
SceneData.ScenarioType = SpawnManager->ScenarioType;
|
||||
}
|
||||
|
||||
// Convert to JSON
|
||||
@@ -450,6 +451,7 @@ bool UPS_Editor_SceneSerializer::LoadScene(const FString& SceneName, UPS_Editor_
|
||||
if (SpawnManager)
|
||||
{
|
||||
SpawnManager->bUseDynamicNavigation = SceneData.bUseDynamicNavigation;
|
||||
SpawnManager->ScenarioType = SceneData.ScenarioType;
|
||||
if (APS_Editor_PlayerController* EditorPC = Cast<APS_Editor_PlayerController>(GetOuter()))
|
||||
{
|
||||
PS_Editor_NavUtils::SetDynamicNavigationEnabled(EditorPC->GetWorld(), SpawnManager->bUseDynamicNavigation);
|
||||
|
||||
@@ -118,4 +118,16 @@ public:
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "PS_Editor")
|
||||
TArray<FPS_Editor_BaseLevelEntry> BaseLevels;
|
||||
|
||||
/**
|
||||
* Optional project-specific enum used to categorize scenarios. The runtime editor's
|
||||
* "Scenario Type" dropdown (Settings popup) reads this enum to populate its choices.
|
||||
* The picked value is saved per-scenario as a uint8 (index into the enum) and exposed
|
||||
* at runtime via UPS_Editor_GameInstanceSubsystem::PendingScenarioType — the gameplay
|
||||
* GameMode reads it (Byte to Enum) and dispatches its scenario-type-specific logic.
|
||||
*
|
||||
* Leave null if your project doesn't use scenario typing — the dropdown is hidden.
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "PS_Editor")
|
||||
TObjectPtr<UEnum> ScenarioTypeEnum;
|
||||
};
|
||||
|
||||
@@ -102,6 +102,12 @@ public:
|
||||
* Persisted in the scenario JSON; default false. */
|
||||
bool bUseDynamicNavigation = false;
|
||||
|
||||
/** Project-specific scenario type as uint8 (index into MasterCatalog::ScenarioTypeEnum).
|
||||
* Set via the Settings popup dropdown, persisted in the scenario JSON, pushed into
|
||||
* PS_Editor_GameInstanceSubsystem::PendingScenarioType at Play click for gameplay
|
||||
* GameMode to consume. Default 0 (first enum entry). */
|
||||
uint8 ScenarioType = 0;
|
||||
|
||||
// ---- Session-only UI preferences (NOT persisted) ----
|
||||
|
||||
/** If true, the navmesh is rendered at runtime (mimics the editor's P key). Personal
|
||||
|
||||
@@ -591,6 +591,69 @@ TSharedRef<SWidget> UPS_Editor_MainWidget::RebuildWidget()
|
||||
]
|
||||
]
|
||||
]
|
||||
// Scenario Type dropdown — visible only when MasterCatalog::ScenarioTypeEnum is set.
|
||||
+ SVerticalBox::Slot().AutoHeight().Padding(0.0f, 8.0f, 0.0f, 4.0f)
|
||||
[
|
||||
SNew(SHorizontalBox)
|
||||
.Visibility_Lambda([this]() -> EVisibility
|
||||
{
|
||||
if (APS_Editor_PlayerController* PC = Cast<APS_Editor_PlayerController>(GetOwningPlayer()))
|
||||
{
|
||||
if (PC->MasterCatalogAsset && PC->MasterCatalogAsset->ScenarioTypeEnum)
|
||||
{
|
||||
return EVisibility::Visible;
|
||||
}
|
||||
}
|
||||
return EVisibility::Collapsed;
|
||||
})
|
||||
+ SHorizontalBox::Slot().FillWidth(0.45f).VAlign(VAlign_Center)
|
||||
[
|
||||
SNew(SVerticalBox)
|
||||
+ SVerticalBox::Slot().AutoHeight()
|
||||
[
|
||||
SNew(STextBlock)
|
||||
.Text(FText::FromString(TEXT("Scenario Type")))
|
||||
.Font(FCoreStyle::GetDefaultFontStyle("Bold", 11))
|
||||
.ColorAndOpacity(FLinearColor::White)
|
||||
]
|
||||
+ SVerticalBox::Slot().AutoHeight()
|
||||
[
|
||||
SNew(STextBlock)
|
||||
.Text(FText::FromString(TEXT("Saved per-scenario; pushed to PendingScenarioType at Play.")))
|
||||
.Font(FCoreStyle::GetDefaultFontStyle("Regular", 9))
|
||||
.ColorAndOpacity(FLinearColor(0.7f, 0.7f, 0.7f))
|
||||
.AutoWrapText(true)
|
||||
]
|
||||
]
|
||||
+ SHorizontalBox::Slot().FillWidth(0.55f).VAlign(VAlign_Center).Padding(8.0f, 0.0f, 0.0f, 0.0f)
|
||||
[
|
||||
SAssignNew(ScenarioTypeCombo, SComboBox<TSharedPtr<FString>>)
|
||||
.OptionsSource(&ScenarioTypeOptions)
|
||||
.OnComboBoxOpening_Lambda([this]() { RefreshScenarioTypeOptions(); })
|
||||
.OnGenerateWidget_Lambda([](TSharedPtr<FString> Item) -> TSharedRef<SWidget>
|
||||
{
|
||||
return SNew(STextBlock).Text(FText::FromString(Item.IsValid() ? *Item : FString()));
|
||||
})
|
||||
.OnSelectionChanged_Lambda([this](TSharedPtr<FString> NewItem, ESelectInfo::Type SelectType)
|
||||
{
|
||||
if (!NewItem.IsValid()) return;
|
||||
const int32 Idx = ScenarioTypeOptions.IndexOfByKey(NewItem);
|
||||
if (Idx == INDEX_NONE) return;
|
||||
if (UPS_Editor_SpawnManager* SM = SpawnManager.Get())
|
||||
{
|
||||
SM->ScenarioType = (uint8)Idx;
|
||||
bSceneDirty = true;
|
||||
}
|
||||
})
|
||||
[
|
||||
SNew(STextBlock)
|
||||
.Text_Lambda([this]() -> FText
|
||||
{
|
||||
return FText::FromString(GetCurrentScenarioTypeDisplayName());
|
||||
})
|
||||
]
|
||||
]
|
||||
]
|
||||
+ SVerticalBox::Slot().AutoHeight().HAlign(HAlign_Center).Padding(0.0f, 16.0f, 0.0f, 0.0f)
|
||||
[
|
||||
SNew(SButton)
|
||||
@@ -1170,7 +1233,8 @@ TSharedRef<SWidget> UPS_Editor_MainWidget::BuildSceneButtons()
|
||||
{
|
||||
if (UPS_Editor_GameInstanceSubsystem* Sub = GI->GetSubsystem<UPS_Editor_GameInstanceSubsystem>())
|
||||
{
|
||||
Sub->SetPendingScenario(SceneName, BaseLevel);
|
||||
const uint8 TypeToSend = SpawnManager.IsValid() ? SpawnManager->ScenarioType : 0;
|
||||
Sub->SetPendingScenario(SceneName, BaseLevel, TypeToSend);
|
||||
Sub->bIsPlayMode = true;
|
||||
if (Sub->EditorMapName.IsEmpty())
|
||||
{
|
||||
@@ -3336,10 +3400,57 @@ void UPS_Editor_MainWidget::OpenSettingsPopup()
|
||||
{
|
||||
if (SettingsPopup.IsValid())
|
||||
{
|
||||
// Refresh enum-driven options each time the popup opens — covers the case where the
|
||||
// MasterCatalogAsset (or its ScenarioTypeEnum) wasn't set at widget Construct time.
|
||||
RefreshScenarioTypeOptions();
|
||||
SettingsPopup->SetVisibility(EVisibility::Visible);
|
||||
}
|
||||
}
|
||||
|
||||
void UPS_Editor_MainWidget::RefreshScenarioTypeOptions()
|
||||
{
|
||||
ScenarioTypeOptions.Empty();
|
||||
if (APS_Editor_PlayerController* PC = Cast<APS_Editor_PlayerController>(GetOwningPlayer()))
|
||||
{
|
||||
if (PC->MasterCatalogAsset && PC->MasterCatalogAsset->ScenarioTypeEnum)
|
||||
{
|
||||
UEnum* E = PC->MasterCatalogAsset->ScenarioTypeEnum;
|
||||
// NumEnums()-1 to skip the auto-generated _MAX entry on regular UENUMs.
|
||||
// User-defined enums (UUserDefinedEnum) don't have _MAX but the last "MAX" entry
|
||||
// is hidden behind the `bIsHidden` meta — we drop the last one defensively.
|
||||
const int32 Count = FMath::Max(0, E->NumEnums() - 1);
|
||||
for (int32 i = 0; i < Count; ++i)
|
||||
{
|
||||
ScenarioTypeOptions.Add(MakeShared<FString>(E->GetDisplayNameTextByIndex(i).ToString()));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (ScenarioTypeCombo.IsValid())
|
||||
{
|
||||
ScenarioTypeCombo->RefreshOptions();
|
||||
}
|
||||
}
|
||||
|
||||
FString UPS_Editor_MainWidget::GetCurrentScenarioTypeDisplayName() const
|
||||
{
|
||||
if (UPS_Editor_SpawnManager* SM = SpawnManager.Get())
|
||||
{
|
||||
if (APS_Editor_PlayerController* PC = Cast<APS_Editor_PlayerController>(GetOwningPlayer()))
|
||||
{
|
||||
if (PC->MasterCatalogAsset && PC->MasterCatalogAsset->ScenarioTypeEnum)
|
||||
{
|
||||
UEnum* E = PC->MasterCatalogAsset->ScenarioTypeEnum;
|
||||
const int32 Count = FMath::Max(0, E->NumEnums() - 1);
|
||||
if (SM->ScenarioType < Count)
|
||||
{
|
||||
return E->GetDisplayNameTextByIndex(SM->ScenarioType).ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return FString();
|
||||
}
|
||||
|
||||
void UPS_Editor_MainWidget::CloseSettingsPopup()
|
||||
{
|
||||
if (SettingsPopup.IsValid())
|
||||
|
||||
@@ -134,13 +134,19 @@ private:
|
||||
void ConfirmBaseLevelSelection();
|
||||
void CancelBaseLevelPopup();
|
||||
|
||||
// Global Settings popup (per-scenario nav option + session-only navmesh visu).
|
||||
// Global Settings popup (per-scenario nav option + session-only navmesh visu + scenario type).
|
||||
TSharedPtr<SWidget> SettingsPopup;
|
||||
void OpenSettingsPopup();
|
||||
void CloseSettingsPopup();
|
||||
void OnUseDynamicNavigationToggled(ECheckBoxState State);
|
||||
void OnShowNavMeshToggled(ECheckBoxState State);
|
||||
|
||||
// Scenario type dropdown — populated from MasterCatalog::ScenarioTypeEnum, hidden when null.
|
||||
TArray<TSharedPtr<FString>> ScenarioTypeOptions;
|
||||
TSharedPtr<SComboBox<TSharedPtr<FString>>> ScenarioTypeCombo;
|
||||
void RefreshScenarioTypeOptions();
|
||||
FString GetCurrentScenarioTypeDisplayName() const;
|
||||
|
||||
// Transform fields
|
||||
TSharedPtr<SEditableTextBox> LocX, LocY, LocZ;
|
||||
TSharedPtr<SEditableTextBox> RotX, RotY, RotZ;
|
||||
|
||||
@@ -550,6 +550,69 @@ TSharedRef<SWidget> UPS_Editor_MainWidget_Legacy::RebuildWidget()
|
||||
]
|
||||
]
|
||||
]
|
||||
// Scenario Type dropdown — visible only when MasterCatalog::ScenarioTypeEnum is set.
|
||||
+ SVerticalBox::Slot().AutoHeight().Padding(0.0f, 8.0f, 0.0f, 4.0f)
|
||||
[
|
||||
SNew(SHorizontalBox)
|
||||
.Visibility_Lambda([this]() -> EVisibility
|
||||
{
|
||||
if (APS_Editor_PlayerController* PC = Cast<APS_Editor_PlayerController>(GetOwningPlayer()))
|
||||
{
|
||||
if (PC->MasterCatalogAsset && PC->MasterCatalogAsset->ScenarioTypeEnum)
|
||||
{
|
||||
return EVisibility::Visible;
|
||||
}
|
||||
}
|
||||
return EVisibility::Collapsed;
|
||||
})
|
||||
+ SHorizontalBox::Slot().FillWidth(0.45f).VAlign(VAlign_Center)
|
||||
[
|
||||
SNew(SVerticalBox)
|
||||
+ SVerticalBox::Slot().AutoHeight()
|
||||
[
|
||||
SNew(STextBlock)
|
||||
.Text(FText::FromString(TEXT("Scenario Type")))
|
||||
.Font(FCoreStyle::GetDefaultFontStyle("Bold", 11))
|
||||
.ColorAndOpacity(FLinearColor::White)
|
||||
]
|
||||
+ SVerticalBox::Slot().AutoHeight()
|
||||
[
|
||||
SNew(STextBlock)
|
||||
.Text(FText::FromString(TEXT("Saved per-scenario; pushed to PendingScenarioType at Play.")))
|
||||
.Font(FCoreStyle::GetDefaultFontStyle("Regular", 9))
|
||||
.ColorAndOpacity(FLinearColor(0.7f, 0.7f, 0.7f))
|
||||
.AutoWrapText(true)
|
||||
]
|
||||
]
|
||||
+ SHorizontalBox::Slot().FillWidth(0.55f).VAlign(VAlign_Center).Padding(8.0f, 0.0f, 0.0f, 0.0f)
|
||||
[
|
||||
SAssignNew(ScenarioTypeCombo, SComboBox<TSharedPtr<FString>>)
|
||||
.OptionsSource(&ScenarioTypeOptions)
|
||||
.OnComboBoxOpening_Lambda([this]() { RefreshScenarioTypeOptions(); })
|
||||
.OnGenerateWidget_Lambda([](TSharedPtr<FString> Item) -> TSharedRef<SWidget>
|
||||
{
|
||||
return SNew(STextBlock).Text(FText::FromString(Item.IsValid() ? *Item : FString()));
|
||||
})
|
||||
.OnSelectionChanged_Lambda([this](TSharedPtr<FString> NewItem, ESelectInfo::Type SelectType)
|
||||
{
|
||||
if (!NewItem.IsValid()) return;
|
||||
const int32 Idx = ScenarioTypeOptions.IndexOfByKey(NewItem);
|
||||
if (Idx == INDEX_NONE) return;
|
||||
if (UPS_Editor_SpawnManager* SM = SpawnManager.Get())
|
||||
{
|
||||
SM->ScenarioType = (uint8)Idx;
|
||||
bSceneDirty = true;
|
||||
}
|
||||
})
|
||||
[
|
||||
SNew(STextBlock)
|
||||
.Text_Lambda([this]() -> FText
|
||||
{
|
||||
return FText::FromString(GetCurrentScenarioTypeDisplayName());
|
||||
})
|
||||
]
|
||||
]
|
||||
]
|
||||
// Close button
|
||||
+ SVerticalBox::Slot().AutoHeight().HAlign(HAlign_Center).Padding(0.0f, 16.0f, 0.0f, 0.0f)
|
||||
[
|
||||
@@ -1011,7 +1074,8 @@ TSharedRef<SWidget> UPS_Editor_MainWidget_Legacy::BuildSceneButtons()
|
||||
{
|
||||
if (UPS_Editor_GameInstanceSubsystem* Sub = GI->GetSubsystem<UPS_Editor_GameInstanceSubsystem>())
|
||||
{
|
||||
Sub->SetPendingScenario(SceneName, BaseLevel);
|
||||
const uint8 TypeToSend = SpawnManager.IsValid() ? SpawnManager->ScenarioType : 0;
|
||||
Sub->SetPendingScenario(SceneName, BaseLevel, TypeToSend);
|
||||
Sub->bIsPlayMode = true;
|
||||
if (Sub->EditorMapName.IsEmpty())
|
||||
{
|
||||
@@ -3010,10 +3074,54 @@ void UPS_Editor_MainWidget_Legacy::OpenSettingsPopup()
|
||||
{
|
||||
if (SettingsPopup.IsValid())
|
||||
{
|
||||
// Refresh enum-driven options each time the popup opens — covers the case where the
|
||||
// MasterCatalogAsset (or its ScenarioTypeEnum) wasn't set at widget Construct time.
|
||||
RefreshScenarioTypeOptions();
|
||||
SettingsPopup->SetVisibility(EVisibility::Visible);
|
||||
}
|
||||
}
|
||||
|
||||
void UPS_Editor_MainWidget_Legacy::RefreshScenarioTypeOptions()
|
||||
{
|
||||
ScenarioTypeOptions.Empty();
|
||||
if (APS_Editor_PlayerController* PC = Cast<APS_Editor_PlayerController>(GetOwningPlayer()))
|
||||
{
|
||||
if (PC->MasterCatalogAsset && PC->MasterCatalogAsset->ScenarioTypeEnum)
|
||||
{
|
||||
UEnum* E = PC->MasterCatalogAsset->ScenarioTypeEnum;
|
||||
const int32 Count = FMath::Max(0, E->NumEnums() - 1);
|
||||
for (int32 i = 0; i < Count; ++i)
|
||||
{
|
||||
ScenarioTypeOptions.Add(MakeShared<FString>(E->GetDisplayNameTextByIndex(i).ToString()));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (ScenarioTypeCombo.IsValid())
|
||||
{
|
||||
ScenarioTypeCombo->RefreshOptions();
|
||||
}
|
||||
}
|
||||
|
||||
FString UPS_Editor_MainWidget_Legacy::GetCurrentScenarioTypeDisplayName() const
|
||||
{
|
||||
if (UPS_Editor_SpawnManager* SM = SpawnManager.Get())
|
||||
{
|
||||
if (APS_Editor_PlayerController* PC = Cast<APS_Editor_PlayerController>(GetOwningPlayer()))
|
||||
{
|
||||
if (PC->MasterCatalogAsset && PC->MasterCatalogAsset->ScenarioTypeEnum)
|
||||
{
|
||||
UEnum* E = PC->MasterCatalogAsset->ScenarioTypeEnum;
|
||||
const int32 Count = FMath::Max(0, E->NumEnums() - 1);
|
||||
if (SM->ScenarioType < Count)
|
||||
{
|
||||
return E->GetDisplayNameTextByIndex(SM->ScenarioType).ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return FString();
|
||||
}
|
||||
|
||||
void UPS_Editor_MainWidget_Legacy::CloseSettingsPopup()
|
||||
{
|
||||
if (SettingsPopup.IsValid())
|
||||
|
||||
@@ -107,7 +107,7 @@ private:
|
||||
void ConfirmBaseLevelSelection();
|
||||
void CancelBaseLevelPopup();
|
||||
|
||||
// Global Settings popup (per-scenario nav option + session-only navmesh visu).
|
||||
// Global Settings popup (per-scenario nav option + session-only navmesh visu + scenario type).
|
||||
TSharedPtr<SWidget> SettingsPopup;
|
||||
void OpenSettingsPopup();
|
||||
void CloseSettingsPopup();
|
||||
@@ -116,6 +116,12 @@ private:
|
||||
/** Toggle the navmesh visualization (P key handler). */
|
||||
void ToggleShowNavMesh();
|
||||
|
||||
// Scenario type dropdown — populated from MasterCatalog::ScenarioTypeEnum, hidden when null.
|
||||
TArray<TSharedPtr<FString>> ScenarioTypeOptions;
|
||||
TSharedPtr<SComboBox<TSharedPtr<FString>>> ScenarioTypeCombo;
|
||||
void RefreshScenarioTypeOptions();
|
||||
FString GetCurrentScenarioTypeDisplayName() const;
|
||||
|
||||
// Transform fields
|
||||
TSharedPtr<SEditableTextBox> LocX, LocY, LocZ;
|
||||
TSharedPtr<SEditableTextBox> RotX, RotY, RotZ;
|
||||
|
||||
Reference in New Issue
Block a user