Stabilize investor frontend entry
This commit is contained in:
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,57 @@
|
|||||||
|
# Investor Readiness Audit - 2026-05-19
|
||||||
|
|
||||||
|
## Current Verdict
|
||||||
|
|
||||||
|
The current build should not be presented as investor visual MVP until it passes
|
||||||
|
a real-GPU packaged-client visual and stability check. The systems are further
|
||||||
|
along than the presentation, but the player-facing first impression is still at
|
||||||
|
risk.
|
||||||
|
|
||||||
|
## Findings
|
||||||
|
|
||||||
|
- Packaged crash logs show a Slate/UI paint crash while the character-selection
|
||||||
|
frontend is active. The crash callstack centers on `SButton::OnPaint` and
|
||||||
|
`FSlateDrawElement::MakeBox`, so the immediate fix removes custom button brush
|
||||||
|
styling, defers frontend actions to the next tick, and shortens the investor
|
||||||
|
path from character selection directly into Ground Zero.
|
||||||
|
- The project content library still contains only placeholder meshes plus the
|
||||||
|
Manny/Quinn mannequin assets for characters. The current character and object
|
||||||
|
visuals are proxy-quality, not final investor-quality realism.
|
||||||
|
- Ground Zero editor verification confirms landscape, water/resource actors,
|
||||||
|
foliage variation actors, and environment materials exist, but this does not
|
||||||
|
prove the packaged executable looks good through the real GPU path.
|
||||||
|
- Cooked runtime logs warned that tree, shrub, and grass materials were missing
|
||||||
|
instanced-static-mesh usage. Those flags are now set on the assets and in the
|
||||||
|
repeatable map setup script.
|
||||||
|
- The generated investor roadmap/report can show old milestone completion state
|
||||||
|
if it is not regenerated from the current roadmap after fixes. The source
|
||||||
|
roadmap still contains explicit visual QA acceptance items that remain open
|
||||||
|
until real packaged screenshots or clips are captured.
|
||||||
|
|
||||||
|
## Required Before Investor-Ready Label
|
||||||
|
|
||||||
|
- Launch the packaged Windows demo through the real GPU desktop path.
|
||||||
|
- Capture startup credits, character selection, first spawn, terrain, vegetation,
|
||||||
|
water, campfire, shelter, pause menu, and save/quit.
|
||||||
|
- Confirm selecting a character and entering Ground Zero does not crash.
|
||||||
|
- Confirm the first player view is not ground/legs, the menu does not appear
|
||||||
|
after gameplay starts, water is visible, foliage is visible, and debug-looking
|
||||||
|
primitives are not the dominant read.
|
||||||
|
- Replace proxy/mannequin art with real or production-directed free/internal art
|
||||||
|
assets before calling the build visually investor-ready.
|
||||||
|
|
||||||
|
## Verification Completed In This Pass
|
||||||
|
|
||||||
|
- `verify_mvp_menu_input_and_quit_flow.py`
|
||||||
|
- `verify_mvp_character_archetype_choice.py`
|
||||||
|
- `verify_mvp_character_proxies.py`
|
||||||
|
- `verify_mvp_frontend_umg_flow.py`
|
||||||
|
- `verify_ground_zero_natural_environment_pass.py`
|
||||||
|
- Windows package BuildCookRun completed successfully after the fixes.
|
||||||
|
|
||||||
|
## Remaining Risk
|
||||||
|
|
||||||
|
The packaged build still needs a real interactive visual pass through
|
||||||
|
Sunshine/Moonlight or direct Windows display access. Static checks and editor
|
||||||
|
commandlets are not enough to clear the user-reported crash and visual quality
|
||||||
|
concerns.
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
import unreal
|
||||||
|
|
||||||
|
MATERIAL_PATHS = [
|
||||||
|
"/Game/Agrarian/Materials/M_AGR_GZ_Tree_CoastalOak",
|
||||||
|
"/Game/Agrarian/Materials/M_AGR_GZ_Shrub_CoyoteBrush",
|
||||||
|
"/Game/Agrarian/Materials/M_AGR_GZ_Grass_DryCoastal",
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def set_instanced_usage(material):
|
||||||
|
applied = False
|
||||||
|
for property_name in (
|
||||||
|
"used_with_instanced_static_meshes",
|
||||||
|
"bUsedWithInstancedStaticMeshes",
|
||||||
|
):
|
||||||
|
try:
|
||||||
|
material.set_editor_property(property_name, True)
|
||||||
|
applied = True
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
return applied
|
||||||
|
|
||||||
|
|
||||||
|
dirty_assets = []
|
||||||
|
for material_path in MATERIAL_PATHS:
|
||||||
|
material = unreal.EditorAssetLibrary.load_asset(material_path)
|
||||||
|
if not material:
|
||||||
|
raise RuntimeError(f"Missing material: {material_path}")
|
||||||
|
|
||||||
|
if not set_instanced_usage(material):
|
||||||
|
raise RuntimeError(f"Could not set instanced static mesh usage on {material_path}")
|
||||||
|
|
||||||
|
unreal.MaterialEditingLibrary.recompile_material(material)
|
||||||
|
dirty_assets.append(material_path)
|
||||||
|
|
||||||
|
for material_path in dirty_assets:
|
||||||
|
if not unreal.EditorAssetLibrary.save_asset(material_path, only_if_is_dirty=False):
|
||||||
|
raise RuntimeError(f"Failed to save updated material: {material_path}")
|
||||||
|
|
||||||
|
print("Updated instanced static mesh usage for Ground Zero foliage materials:")
|
||||||
|
for material_path in dirty_assets:
|
||||||
|
print(f" - {material_path}")
|
||||||
@@ -52,16 +52,19 @@ ENVIRONMENT_MATERIALS = {
|
|||||||
"path": f"{MATERIAL_FOLDER}/M_AGR_GZ_Tree_CoastalOak",
|
"path": f"{MATERIAL_FOLDER}/M_AGR_GZ_Tree_CoastalOak",
|
||||||
"color": unreal.LinearColor(0.18, 0.31, 0.16, 1.0),
|
"color": unreal.LinearColor(0.18, 0.31, 0.16, 1.0),
|
||||||
"roughness": 0.88,
|
"roughness": 0.88,
|
||||||
|
"used_with_instanced_static_meshes": True,
|
||||||
},
|
},
|
||||||
"shrub": {
|
"shrub": {
|
||||||
"path": f"{MATERIAL_FOLDER}/M_AGR_GZ_Shrub_CoyoteBrush",
|
"path": f"{MATERIAL_FOLDER}/M_AGR_GZ_Shrub_CoyoteBrush",
|
||||||
"color": unreal.LinearColor(0.31, 0.39, 0.20, 1.0),
|
"color": unreal.LinearColor(0.31, 0.39, 0.20, 1.0),
|
||||||
"roughness": 0.9,
|
"roughness": 0.9,
|
||||||
|
"used_with_instanced_static_meshes": True,
|
||||||
},
|
},
|
||||||
"grass": {
|
"grass": {
|
||||||
"path": f"{MATERIAL_FOLDER}/M_AGR_GZ_Grass_DryCoastal",
|
"path": f"{MATERIAL_FOLDER}/M_AGR_GZ_Grass_DryCoastal",
|
||||||
"color": unreal.LinearColor(0.47, 0.42, 0.23, 1.0),
|
"color": unreal.LinearColor(0.47, 0.42, 0.23, 1.0),
|
||||||
"roughness": 0.95,
|
"roughness": 0.95,
|
||||||
|
"used_with_instanced_static_meshes": True,
|
||||||
},
|
},
|
||||||
"wood_resource": {
|
"wood_resource": {
|
||||||
"path": f"{MATERIAL_FOLDER}/M_AGR_GZ_Wood_Resource",
|
"path": f"{MATERIAL_FOLDER}/M_AGR_GZ_Wood_Resource",
|
||||||
@@ -807,6 +810,10 @@ def ensure_environment_materials():
|
|||||||
unreal.MaterialEditingLibrary.recompile_material(material)
|
unreal.MaterialEditingLibrary.recompile_material(material)
|
||||||
unreal.EditorAssetLibrary.save_asset(spec["path"])
|
unreal.EditorAssetLibrary.save_asset(spec["path"])
|
||||||
unreal.log(f"Created Ground Zero environment material: {spec['path']}")
|
unreal.log(f"Created Ground Zero environment material: {spec['path']}")
|
||||||
|
if spec.get("used_with_instanced_static_meshes"):
|
||||||
|
material.set_editor_property("used_with_instanced_static_meshes", True)
|
||||||
|
unreal.MaterialEditingLibrary.recompile_material(material)
|
||||||
|
unreal.EditorAssetLibrary.save_asset(spec["path"], only_if_is_dirty=False)
|
||||||
created_or_loaded[key] = material
|
created_or_loaded[key] = material
|
||||||
|
|
||||||
return created_or_loaded
|
return created_or_loaded
|
||||||
|
|||||||
@@ -37,9 +37,8 @@ def main() -> None:
|
|||||||
"UButton::StaticClass()",
|
"UButton::StaticClass()",
|
||||||
"UTextBlock::StaticClass()",
|
"UTextBlock::StaticClass()",
|
||||||
"OnClicked.AddDynamic",
|
"OnClicked.AddDynamic",
|
||||||
"OnHovered.AddDynamic",
|
"SetBackgroundColor",
|
||||||
"ButtonStyle.Hovered.TintColor",
|
"DeferFrontendAction",
|
||||||
"ButtonStyle.Pressed.TintColor",
|
|
||||||
"SetIsFocusable(true)",
|
"SetIsFocusable(true)",
|
||||||
"SetKeyboardFocus()",
|
"SetKeyboardFocus()",
|
||||||
"BackFromActiveScreen()",
|
"BackFromActiveScreen()",
|
||||||
|
|||||||
@@ -17,22 +17,6 @@
|
|||||||
#include "Styling/CoreStyle.h"
|
#include "Styling/CoreStyle.h"
|
||||||
#include "TimerManager.h"
|
#include "TimerManager.h"
|
||||||
|
|
||||||
namespace
|
|
||||||
{
|
|
||||||
FButtonStyle MakeAgrarianButtonStyle(const FLinearColor& NormalColor, const FLinearColor& HoveredColor)
|
|
||||||
{
|
|
||||||
FButtonStyle ButtonStyle = FCoreStyle::Get().GetWidgetStyle<FButtonStyle>(TEXT("Button"));
|
|
||||||
ButtonStyle.Normal.TintColor = FSlateColor(NormalColor);
|
|
||||||
ButtonStyle.Hovered.TintColor = FSlateColor(HoveredColor);
|
|
||||||
ButtonStyle.Pressed.TintColor = FSlateColor(FLinearColor(
|
|
||||||
FMath::Min(HoveredColor.R + 0.12f, 1.0f),
|
|
||||||
FMath::Min(HoveredColor.G + 0.12f, 1.0f),
|
|
||||||
FMath::Min(HoveredColor.B + 0.12f, 1.0f),
|
|
||||||
HoveredColor.A));
|
|
||||||
return ButtonStyle;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void UAgrarianMvpFrontendWidget::NativeConstruct()
|
void UAgrarianMvpFrontendWidget::NativeConstruct()
|
||||||
{
|
{
|
||||||
Super::NativeConstruct();
|
Super::NativeConstruct();
|
||||||
@@ -175,7 +159,7 @@ void UAgrarianMvpFrontendWidget::ContinueFromActiveScreen()
|
|||||||
{
|
{
|
||||||
if (ActiveScreen == EAgrarianMvpFrontendScreen::CharacterSelection)
|
if (ActiveScreen == EAgrarianMvpFrontendScreen::CharacterSelection)
|
||||||
{
|
{
|
||||||
SetActiveScreen(EAgrarianMvpFrontendScreen::JoinServer);
|
CompleteFrontendFlow();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -201,11 +185,6 @@ void UAgrarianMvpFrontendWidget::ContinueFromActiveScreen()
|
|||||||
CompleteFrontendFlow();
|
CompleteFrontendFlow();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ActiveScreen == EAgrarianMvpFrontendScreen::SavingAndQuit)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void UAgrarianMvpFrontendWidget::ReturnFromActiveScreen()
|
void UAgrarianMvpFrontendWidget::ReturnFromActiveScreen()
|
||||||
@@ -226,7 +205,7 @@ void UAgrarianMvpFrontendWidget::CompleteFrontendFlow()
|
|||||||
{
|
{
|
||||||
if (APlayerController* PlayerController = GetOwningPlayer())
|
if (APlayerController* PlayerController = GetOwningPlayer())
|
||||||
{
|
{
|
||||||
if (ActiveScreen == EAgrarianMvpFrontendScreen::Loading)
|
if (ActiveScreen == EAgrarianMvpFrontendScreen::CharacterSelection || ActiveScreen == EAgrarianMvpFrontendScreen::Loading)
|
||||||
{
|
{
|
||||||
PlayerController->ConsoleCommand(SelectedCharacterArchetype == EAgrarianMvpCharacterArchetype::YoungAdultFemale
|
PlayerController->ConsoleCommand(SelectedCharacterArchetype == EAgrarianMvpCharacterArchetype::YoungAdultFemale
|
||||||
? TEXT("AgrarianSelectCharacter female")
|
? TEXT("AgrarianSelectCharacter female")
|
||||||
@@ -291,7 +270,6 @@ void UAgrarianMvpFrontendWidget::RebuildFrontendTree()
|
|||||||
AddText(Panel, FText::FromString(TEXT("Gameplay is paused while this menu is active.")), FMath::RoundToInt(22.0f * Scale), false, MutedTextColor, 72.0f * Scale);
|
AddText(Panel, FText::FromString(TEXT("Gameplay is paused while this menu is active.")), FMath::RoundToInt(22.0f * Scale), false, MutedTextColor, 72.0f * Scale);
|
||||||
PrimaryFocusButton = AddButton(Panel, FText::FromString(TEXT("Resume")), ButtonColor, ButtonHoverColor, 16.0f * Scale);
|
PrimaryFocusButton = AddButton(Panel, FText::FromString(TEXT("Resume")), ButtonColor, ButtonHoverColor, 16.0f * Scale);
|
||||||
PrimaryFocusButton->OnClicked.AddDynamic(this, &UAgrarianMvpFrontendWidget::HandlePrimaryActionClicked);
|
PrimaryFocusButton->OnClicked.AddDynamic(this, &UAgrarianMvpFrontendWidget::HandlePrimaryActionClicked);
|
||||||
PrimaryFocusButton->OnHovered.AddDynamic(this, &UAgrarianMvpFrontendWidget::FocusPrimaryButton);
|
|
||||||
|
|
||||||
UButton* QuitButton = AddButton(Panel, FText::FromString(TEXT("Save & Quit")), QuitButtonColor, FLinearColor(0.58f, 0.28f, 0.22f, 1.0f), 34.0f * Scale);
|
UButton* QuitButton = AddButton(Panel, FText::FromString(TEXT("Save & Quit")), QuitButtonColor, FLinearColor(0.58f, 0.28f, 0.22f, 1.0f), 34.0f * Scale);
|
||||||
QuitButton->OnClicked.AddDynamic(this, &UAgrarianMvpFrontendWidget::HandleSaveAndQuitClicked);
|
QuitButton->OnClicked.AddDynamic(this, &UAgrarianMvpFrontendWidget::HandleSaveAndQuitClicked);
|
||||||
@@ -314,7 +292,7 @@ void UAgrarianMvpFrontendWidget::RebuildFrontendTree()
|
|||||||
const bool bMaleSelected = SelectedCharacterArchetype == EAgrarianMvpCharacterArchetype::YoungAdultMale;
|
const bool bMaleSelected = SelectedCharacterArchetype == EAgrarianMvpCharacterArchetype::YoungAdultMale;
|
||||||
const bool bFemaleSelected = SelectedCharacterArchetype == EAgrarianMvpCharacterArchetype::YoungAdultFemale;
|
const bool bFemaleSelected = SelectedCharacterArchetype == EAgrarianMvpCharacterArchetype::YoungAdultFemale;
|
||||||
UButton* MaleButton = WidgetTree->ConstructWidget<UButton>(UButton::StaticClass(), TEXT("MalePioneerButton"));
|
UButton* MaleButton = WidgetTree->ConstructWidget<UButton>(UButton::StaticClass(), TEXT("MalePioneerButton"));
|
||||||
MaleButton->SetStyle(MakeAgrarianButtonStyle(bMaleSelected ? ButtonHoverColor : SecondaryButtonColor, ButtonHoverColor));
|
MaleButton->SetBackgroundColor(bMaleSelected ? ButtonHoverColor : SecondaryButtonColor);
|
||||||
MaleButton->OnClicked.AddDynamic(this, &UAgrarianMvpFrontendWidget::HandleMaleCharacterClicked);
|
MaleButton->OnClicked.AddDynamic(this, &UAgrarianMvpFrontendWidget::HandleMaleCharacterClicked);
|
||||||
UVerticalBox* MaleStack = WidgetTree->ConstructWidget<UVerticalBox>(UVerticalBox::StaticClass(), TEXT("MalePioneerStack"));
|
UVerticalBox* MaleStack = WidgetTree->ConstructWidget<UVerticalBox>(UVerticalBox::StaticClass(), TEXT("MalePioneerStack"));
|
||||||
MaleButton->SetContent(MaleStack);
|
MaleButton->SetContent(MaleStack);
|
||||||
@@ -328,7 +306,7 @@ void UAgrarianMvpFrontendWidget::RebuildFrontendTree()
|
|||||||
}
|
}
|
||||||
|
|
||||||
UButton* FemaleButton = WidgetTree->ConstructWidget<UButton>(UButton::StaticClass(), TEXT("FemalePioneerButton"));
|
UButton* FemaleButton = WidgetTree->ConstructWidget<UButton>(UButton::StaticClass(), TEXT("FemalePioneerButton"));
|
||||||
FemaleButton->SetStyle(MakeAgrarianButtonStyle(bFemaleSelected ? ButtonHoverColor : SecondaryButtonColor, ButtonHoverColor));
|
FemaleButton->SetBackgroundColor(bFemaleSelected ? ButtonHoverColor : SecondaryButtonColor);
|
||||||
FemaleButton->OnClicked.AddDynamic(this, &UAgrarianMvpFrontendWidget::HandleFemaleCharacterClicked);
|
FemaleButton->OnClicked.AddDynamic(this, &UAgrarianMvpFrontendWidget::HandleFemaleCharacterClicked);
|
||||||
UVerticalBox* FemaleStack = WidgetTree->ConstructWidget<UVerticalBox>(UVerticalBox::StaticClass(), TEXT("FemalePioneerStack"));
|
UVerticalBox* FemaleStack = WidgetTree->ConstructWidget<UVerticalBox>(UVerticalBox::StaticClass(), TEXT("FemalePioneerStack"));
|
||||||
FemaleButton->SetContent(FemaleStack);
|
FemaleButton->SetContent(FemaleStack);
|
||||||
@@ -341,9 +319,8 @@ void UAgrarianMvpFrontendWidget::RebuildFrontendTree()
|
|||||||
FemaleSlot->SetSize(FSlateChildSize(ESlateSizeRule::Fill));
|
FemaleSlot->SetSize(FSlateChildSize(ESlateSizeRule::Fill));
|
||||||
}
|
}
|
||||||
|
|
||||||
PrimaryFocusButton = AddButton(Panel, FText::FromString(TEXT("Continue")), ButtonColor, ButtonHoverColor, 10.0f * Scale);
|
PrimaryFocusButton = AddButton(Panel, FText::FromString(TEXT("Enter Ground Zero")), ButtonColor, ButtonHoverColor, 10.0f * Scale);
|
||||||
PrimaryFocusButton->OnClicked.AddDynamic(this, &UAgrarianMvpFrontendWidget::HandlePrimaryActionClicked);
|
PrimaryFocusButton->OnClicked.AddDynamic(this, &UAgrarianMvpFrontendWidget::HandlePrimaryActionClicked);
|
||||||
PrimaryFocusButton->OnHovered.AddDynamic(this, &UAgrarianMvpFrontendWidget::FocusPrimaryButton);
|
|
||||||
AddText(Panel, FText::Format(FText::FromString(TEXT("Selected {0}: {1}. Click a card or use Left/Right, then continue.")), GetSelectedRoleLabel(), GetSelectedCharacterLabel()), FMath::RoundToInt(15.0f * Scale), false, MutedTextColor, 0.0f);
|
AddText(Panel, FText::Format(FText::FromString(TEXT("Selected {0}: {1}. Click a card or use Left/Right, then continue.")), GetSelectedRoleLabel(), GetSelectedCharacterLabel()), FMath::RoundToInt(15.0f * Scale), false, MutedTextColor, 0.0f);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -363,9 +340,8 @@ void UAgrarianMvpFrontendWidget::RebuildFrontendTree()
|
|||||||
}
|
}
|
||||||
|
|
||||||
PrimaryFocusButton = WidgetTree->ConstructWidget<UButton>(UButton::StaticClass(), TEXT("ContinueToLoadingButton"));
|
PrimaryFocusButton = WidgetTree->ConstructWidget<UButton>(UButton::StaticClass(), TEXT("ContinueToLoadingButton"));
|
||||||
PrimaryFocusButton->SetStyle(MakeAgrarianButtonStyle(ButtonColor, ButtonHoverColor));
|
PrimaryFocusButton->SetBackgroundColor(ButtonColor);
|
||||||
PrimaryFocusButton->OnClicked.AddDynamic(this, &UAgrarianMvpFrontendWidget::HandlePrimaryActionClicked);
|
PrimaryFocusButton->OnClicked.AddDynamic(this, &UAgrarianMvpFrontendWidget::HandlePrimaryActionClicked);
|
||||||
PrimaryFocusButton->OnHovered.AddDynamic(this, &UAgrarianMvpFrontendWidget::FocusPrimaryButton);
|
|
||||||
UTextBlock* ContinueLabel = AddText(nullptr, FText::FromString(TEXT("Continue to loading")), FMath::RoundToInt(20.0f * Scale), true, TextColor, 0.0f);
|
UTextBlock* ContinueLabel = AddText(nullptr, FText::FromString(TEXT("Continue to loading")), FMath::RoundToInt(20.0f * Scale), true, TextColor, 0.0f);
|
||||||
PrimaryFocusButton->SetContent(ContinueLabel);
|
PrimaryFocusButton->SetContent(ContinueLabel);
|
||||||
if (UButtonSlot* ContinueLabelSlot = Cast<UButtonSlot>(ContinueLabel->Slot))
|
if (UButtonSlot* ContinueLabelSlot = Cast<UButtonSlot>(ContinueLabel->Slot))
|
||||||
@@ -380,7 +356,7 @@ void UAgrarianMvpFrontendWidget::RebuildFrontendTree()
|
|||||||
}
|
}
|
||||||
|
|
||||||
UButton* BackButton = WidgetTree->ConstructWidget<UButton>(UButton::StaticClass(), TEXT("BackButton"));
|
UButton* BackButton = WidgetTree->ConstructWidget<UButton>(UButton::StaticClass(), TEXT("BackButton"));
|
||||||
BackButton->SetStyle(MakeAgrarianButtonStyle(SecondaryButtonColor, ButtonHoverColor));
|
BackButton->SetBackgroundColor(SecondaryButtonColor);
|
||||||
BackButton->OnClicked.AddDynamic(this, &UAgrarianMvpFrontendWidget::HandleBackClicked);
|
BackButton->OnClicked.AddDynamic(this, &UAgrarianMvpFrontendWidget::HandleBackClicked);
|
||||||
UTextBlock* BackLabel = AddText(nullptr, FText::FromString(TEXT("Back")), FMath::RoundToInt(20.0f * Scale), true, TextColor, 0.0f);
|
UTextBlock* BackLabel = AddText(nullptr, FText::FromString(TEXT("Back")), FMath::RoundToInt(20.0f * Scale), true, TextColor, 0.0f);
|
||||||
BackButton->SetContent(BackLabel);
|
BackButton->SetContent(BackLabel);
|
||||||
@@ -413,7 +389,6 @@ void UAgrarianMvpFrontendWidget::RebuildFrontendTree()
|
|||||||
AddText(Panel, FText::Format(FText::FromString(TEXT("{0}: {1} | Server: {2}")), GetSelectedRoleLabel(), GetSelectedCharacterLabel(), JoinServerAddress), FMath::RoundToInt(18.0f * Scale), false, TextColor, 34.0f * Scale);
|
AddText(Panel, FText::Format(FText::FromString(TEXT("{0}: {1} | Server: {2}")), GetSelectedRoleLabel(), GetSelectedCharacterLabel(), JoinServerAddress), FMath::RoundToInt(18.0f * Scale), false, TextColor, 34.0f * Scale);
|
||||||
PrimaryFocusButton = AddButton(Panel, FText::FromString(TEXT("Enter Ground Zero")), ButtonColor, ButtonHoverColor, 14.0f * Scale);
|
PrimaryFocusButton = AddButton(Panel, FText::FromString(TEXT("Enter Ground Zero")), ButtonColor, ButtonHoverColor, 14.0f * Scale);
|
||||||
PrimaryFocusButton->OnClicked.AddDynamic(this, &UAgrarianMvpFrontendWidget::HandlePrimaryActionClicked);
|
PrimaryFocusButton->OnClicked.AddDynamic(this, &UAgrarianMvpFrontendWidget::HandlePrimaryActionClicked);
|
||||||
PrimaryFocusButton->OnHovered.AddDynamic(this, &UAgrarianMvpFrontendWidget::FocusPrimaryButton);
|
|
||||||
AddText(Panel, FText::FromString(TEXT("Click or press Enter to close the MVP menu and begin testing.")), FMath::RoundToInt(15.0f * Scale), false, MutedTextColor, 0.0f);
|
AddText(Panel, FText::FromString(TEXT("Click or press Enter to close the MVP menu and begin testing.")), FMath::RoundToInt(15.0f * Scale), false, MutedTextColor, 0.0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -440,7 +415,7 @@ UTextBlock* UAgrarianMvpFrontendWidget::AddText(UVerticalBox* Parent, const FTex
|
|||||||
UButton* UAgrarianMvpFrontendWidget::AddButton(UVerticalBox* Parent, const FText& Text, const FLinearColor& NormalColor, const FLinearColor& HoveredColor, float BottomPadding)
|
UButton* UAgrarianMvpFrontendWidget::AddButton(UVerticalBox* Parent, const FText& Text, const FLinearColor& NormalColor, const FLinearColor& HoveredColor, float BottomPadding)
|
||||||
{
|
{
|
||||||
UButton* Button = WidgetTree->ConstructWidget<UButton>(UButton::StaticClass());
|
UButton* Button = WidgetTree->ConstructWidget<UButton>(UButton::StaticClass());
|
||||||
Button->SetStyle(MakeAgrarianButtonStyle(NormalColor, HoveredColor));
|
Button->SetBackgroundColor(NormalColor);
|
||||||
Button->SetClickMethod(EButtonClickMethod::MouseDown);
|
Button->SetClickMethod(EButtonClickMethod::MouseDown);
|
||||||
Button->SetTouchMethod(EButtonTouchMethod::Down);
|
Button->SetTouchMethod(EButtonTouchMethod::Down);
|
||||||
|
|
||||||
@@ -480,28 +455,67 @@ void UAgrarianMvpFrontendWidget::FocusPrimaryButton()
|
|||||||
}
|
}
|
||||||
|
|
||||||
void UAgrarianMvpFrontendWidget::HandlePrimaryActionClicked()
|
void UAgrarianMvpFrontendWidget::HandlePrimaryActionClicked()
|
||||||
|
{
|
||||||
|
DeferFrontendAction([this]()
|
||||||
{
|
{
|
||||||
ConfirmActiveScreen();
|
ConfirmActiveScreen();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void UAgrarianMvpFrontendWidget::HandleBackClicked()
|
void UAgrarianMvpFrontendWidget::HandleBackClicked()
|
||||||
|
{
|
||||||
|
DeferFrontendAction([this]()
|
||||||
{
|
{
|
||||||
BackFromActiveScreen();
|
BackFromActiveScreen();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void UAgrarianMvpFrontendWidget::HandleSaveAndQuitClicked()
|
void UAgrarianMvpFrontendWidget::HandleSaveAndQuitClicked()
|
||||||
|
{
|
||||||
|
DeferFrontendAction([this]()
|
||||||
{
|
{
|
||||||
SaveAndQuit();
|
SaveAndQuit();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void UAgrarianMvpFrontendWidget::HandleMaleCharacterClicked()
|
void UAgrarianMvpFrontendWidget::HandleMaleCharacterClicked()
|
||||||
|
{
|
||||||
|
DeferFrontendAction([this]()
|
||||||
{
|
{
|
||||||
SetSelectedCharacterArchetype(EAgrarianMvpCharacterArchetype::YoungAdultMale);
|
SetSelectedCharacterArchetype(EAgrarianMvpCharacterArchetype::YoungAdultMale);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void UAgrarianMvpFrontendWidget::HandleFemaleCharacterClicked()
|
void UAgrarianMvpFrontendWidget::HandleFemaleCharacterClicked()
|
||||||
|
{
|
||||||
|
DeferFrontendAction([this]()
|
||||||
{
|
{
|
||||||
SetSelectedCharacterArchetype(EAgrarianMvpCharacterArchetype::YoungAdultFemale);
|
SetSelectedCharacterArchetype(EAgrarianMvpCharacterArchetype::YoungAdultFemale);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void UAgrarianMvpFrontendWidget::DeferFrontendAction(TFunction<void()> Action)
|
||||||
|
{
|
||||||
|
if (!Action)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (UWorld* World = GetWorld())
|
||||||
|
{
|
||||||
|
FTimerDelegate DeferredAction;
|
||||||
|
DeferredAction.BindLambda([WeakThis = TWeakObjectPtr<UAgrarianMvpFrontendWidget>(this), Action = MoveTemp(Action)]() mutable
|
||||||
|
{
|
||||||
|
if (WeakThis.IsValid())
|
||||||
|
{
|
||||||
|
Action();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
World->GetTimerManager().SetTimerForNextTick(DeferredAction);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Action();
|
||||||
}
|
}
|
||||||
|
|
||||||
FText UAgrarianMvpFrontendWidget::GetSelectedCharacterLabel() const
|
FText UAgrarianMvpFrontendWidget::GetSelectedCharacterLabel() const
|
||||||
|
|||||||
@@ -112,6 +112,8 @@ private:
|
|||||||
UFUNCTION()
|
UFUNCTION()
|
||||||
void HandleFemaleCharacterClicked();
|
void HandleFemaleCharacterClicked();
|
||||||
|
|
||||||
|
void DeferFrontendAction(TFunction<void()> Action);
|
||||||
|
|
||||||
UPROPERTY()
|
UPROPERTY()
|
||||||
TObjectPtr<UButton> PrimaryFocusButton;
|
TObjectPtr<UButton> PrimaryFocusButton;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user