Add pause save exit settings shell

This commit is contained in:
2026-05-22 03:06:27 +00:00
parent e7bd783309
commit 6eb262acc3
5 changed files with 182 additions and 4 deletions
+13
View File
@@ -950,6 +950,7 @@ Required order:
- [x] Replace or upgrade grasses, shrubs, and trees with believable coastal-scrub vegetation assets, density, color variation, scale variation, and LOD/performance limits. Added native generated coastal oak, coyote brush, and dry grass clump mesh assets under `/Game/Agrarian/Environment/Vegetation`, switched the Ground Zero foliage patch off engine basic shapes, rebuilt foliage materials with per-instance color variation, preserved investor-facing density and scale variation, added explicit HISM cull/shadow performance limits, and extended verifiers so basic-shape vegetation or missing cull limits fail.
- [x] Add the Asset acquisition and ingest pipeline before pulling more visuals: created approved staging folders, added `Docs/Art/AssetLicenses.md`, documented the pipeline in `Docs/Art/AgrarianAssetPipeline.md`, added `Scripts/verify_asset_pipeline_policy.py`, defaulted to Fab/free, Quixel, CC0/public-domain, team-created, or Nathan-supplied assets only, rejected random scraped internet assets, and prioritized trees, shrubs, grass, water, rocks, character bodies/outfits, and old abandoned equipment being reclaimed by nature.
- [x] Add the Ground Zero asset acquisition queue for the investor visual pass: documented free-only Fab candidates for shrubs, Mediterranean/coastal plants, grass, rocks/water-support props, and rural/reclaimed set dressing in `Docs/Art/GroundZeroAssetAcquisitionQueue.md`; added `Scripts/verify_ground_zero_asset_queue.py`; tied every candidate to the license register and staging workflow.
- [x] Add stable MVP pause menu save/exit/settings shell: Resume, Save Game, Settings, Save & Exit, and Quit Without Saving now have separate player-facing actions, keyboard shortcuts, and verifier coverage while deeper settings stay roadmapped.
- [ ] Replace or upgrade freshwater visuals with readable water surface, edge treatment, bank dressing, reflection/roughness tuning, and collectability cues.
- [ ] Replace or upgrade character bodies and clothing so selected characters read as realistic near-future post-collapse frontier people rather than template mannequins or proxy stacks.
- [ ] Replace or upgrade resource objects so wood, stone, fiber, edible plants, pickups, and gathered items look like world objects rather than debug primitives.
@@ -1058,6 +1059,18 @@ Required order:
- [ ] Add early business knowledge for bookkeeping, inventory, profit/loss, fair trade, basic credit, risk, and customer trust.
- [ ] Add simple workshop/business ownership rules for homestead-scale production.
## 0.2.F1 Player Options And Settings
- [ ] Define the settings persistence model so options survive save/load, packaged demos, and future multiplayer profile storage.
- [ ] Add preferred units for metric/imperial distance, weight, temperature, speed, volume, and field-size display.
- [ ] Add controls remapping UI for keyboard, mouse, and gamepad while preserving sane defaults for movement, sprint, crouch, prone, interact, menus, and camera.
- [ ] Add gameplay settings for autosave cadence, UI scale, hints, camera behavior, interaction prompts, and accessibility-friendly timing.
- [ ] Add graphics and hardware settings for quality presets, resolution/window mode, frame cap, foliage density, shadows, water, post-process, ray tracing optional toggles, and reset-to-safe defaults.
- [ ] Add audio settings for master, music, effects, ambient, voice, and cinematic volume.
- [ ] Add accessibility settings for subtitles, color/contrast, text scale, hold-versus-toggle interactions, motion comfort, and input assistance.
- [ ] Add account/server preferences for default server, last-used address, privacy-safe telemetry choice, and multiplayer connection display.
- [ ] Add verifier coverage for settings save/load, default migration, reset-to-default behavior, and packaged-demo menu access.
## 0.2.G Homesteading Knowledge Progression
- [ ] Define early profession paths: farmer, herder, carpenter, mason, cook, medic, hunter, fisher, trapper, trader, and scout.
+15 -1
View File
@@ -13,15 +13,29 @@ EXPECTED = {
"AgrarianMvpFrontendWidget.h": [
"ConfirmActiveScreen",
"BackFromActiveScreen",
"SaveGame",
"SaveAndQuit",
"QuitWithoutSaving",
"Settings",
"GameSaved",
],
"AgrarianMvpFrontendWidget.cpp": [
"UButton::StaticClass()",
"HandleMaleCharacterClicked",
"HandleFemaleCharacterClicked",
"OnClicked.AddDynamic",
"Save & Quit",
"Save Game",
"Settings",
"Save & Exit",
"Quit Without Saving",
"Saving World",
"Game Saved",
"Player Options",
"HandleSaveGameClicked",
"HandleSettingsClicked",
"HandleQuitWithoutSavingClicked",
"ExecuteSaveGame",
"ExecuteQuitWithoutSaving",
"ConsoleCommand(TEXT(\"AgrarianSaveWorld\"))",
"ConsoleCommand(TEXT(\"quit\"))",
"AAgrarianGamePlayerController* AgrarianPlayerController",
@@ -23,8 +23,12 @@ def main() -> None:
roadmap = ROADMAP.read_text(encoding="utf-8")
for token in (
"Settings",
"GameSaved",
"SavingAndQuit",
"SaveGame",
"ExecuteSaveAndQuit",
"QuitWithoutSaving",
):
require(token in header, f"missing segmented flow declaration: {token}")
@@ -34,11 +38,20 @@ def main() -> None:
"Loading Segment",
"Pause Menu",
"Gameplay is paused while this menu is active.",
"Save Game",
"Settings",
"Quit Without Saving",
"Game Saved",
"Player Options",
"Saving World",
"Writing the current world state",
"SetActiveScreen(EAgrarianMvpFrontendScreen::GameSaved)",
"SetActiveScreen(EAgrarianMvpFrontendScreen::Settings)",
"SetActiveScreen(EAgrarianMvpFrontendScreen::SavingAndQuit)",
"GetTimerManager().SetTimer",
"ExecuteSaveGame",
"ExecuteSaveAndQuit",
"ExecuteQuitWithoutSaving",
"ConsoleCommand(TEXT(\"AgrarianSaveWorld\"))",
"ConsoleCommand(TEXT(\"quit\"))",
):
@@ -87,6 +87,33 @@ FReply UAgrarianMvpFrontendWidget::NativeOnKeyDown(const FGeometry& InGeometry,
SaveAndQuit();
return FReply::Handled();
}
if (Key == EKeys::S)
{
SaveGame();
return FReply::Handled();
}
if (Key == EKeys::O)
{
SetActiveScreen(EAgrarianMvpFrontendScreen::Settings);
return FReply::Handled();
}
if (Key == EKeys::X)
{
QuitWithoutSaving();
return FReply::Handled();
}
}
else if (ActiveScreen == EAgrarianMvpFrontendScreen::Settings || ActiveScreen == EAgrarianMvpFrontendScreen::GameSaved)
{
const FKey Key = InKeyEvent.GetKey();
if (Key == EKeys::Escape || Key == EKeys::BackSpace || Key == EKeys::Enter || Key == EKeys::SpaceBar)
{
SetActiveScreen(EAgrarianMvpFrontendScreen::MainMenu);
return FReply::Handled();
}
}
else if (ActiveScreen == EAgrarianMvpFrontendScreen::SavingAndQuit)
{
@@ -147,6 +174,17 @@ void UAgrarianMvpFrontendWidget::SaveAndQuit()
ExecuteSaveAndQuit();
}
void UAgrarianMvpFrontendWidget::SaveGame()
{
ExecuteSaveGame();
SetActiveScreen(EAgrarianMvpFrontendScreen::GameSaved);
}
void UAgrarianMvpFrontendWidget::QuitWithoutSaving()
{
ExecuteQuitWithoutSaving();
}
void UAgrarianMvpFrontendWidget::ExecuteSaveAndQuit()
{
if (APlayerController* PlayerController = GetOwningPlayer())
@@ -156,6 +194,22 @@ void UAgrarianMvpFrontendWidget::ExecuteSaveAndQuit()
}
}
void UAgrarianMvpFrontendWidget::ExecuteSaveGame()
{
if (APlayerController* PlayerController = GetOwningPlayer())
{
PlayerController->ConsoleCommand(TEXT("AgrarianSaveWorld"));
}
}
void UAgrarianMvpFrontendWidget::ExecuteQuitWithoutSaving()
{
if (APlayerController* PlayerController = GetOwningPlayer())
{
PlayerController->ConsoleCommand(TEXT("quit"));
}
}
void UAgrarianMvpFrontendWidget::ContinueFromActiveScreen()
{
if (ActiveScreen == EAgrarianMvpFrontendScreen::CharacterSelection)
@@ -181,6 +235,12 @@ void UAgrarianMvpFrontendWidget::ContinueFromActiveScreen()
return;
}
if (ActiveScreen == EAgrarianMvpFrontendScreen::Settings || ActiveScreen == EAgrarianMvpFrontendScreen::GameSaved)
{
SetActiveScreen(EAgrarianMvpFrontendScreen::MainMenu);
return;
}
if (ActiveScreen == EAgrarianMvpFrontendScreen::MainMenu)
{
CompleteFrontendFlow();
@@ -200,6 +260,11 @@ void UAgrarianMvpFrontendWidget::ReturnFromActiveScreen()
{
CompleteFrontendFlow();
}
if (ActiveScreen == EAgrarianMvpFrontendScreen::Settings || ActiveScreen == EAgrarianMvpFrontendScreen::GameSaved)
{
SetActiveScreen(EAgrarianMvpFrontendScreen::MainMenu);
}
}
void UAgrarianMvpFrontendWidget::CompleteFrontendFlow()
@@ -274,13 +339,23 @@ void UAgrarianMvpFrontendWidget::RebuildFrontendTree()
{
AddText(Panel, FText::FromString(TEXT("Pause Menu")), FMath::RoundToInt(20.0f * Scale), true, AccentColor, 6.0f * Scale);
AddText(Panel, MainMenuTitle, FMath::RoundToInt(54.0f * Scale), true, TextColor, 6.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);
AddText(Panel, FText::FromString(TEXT("Gameplay is paused while this menu is active.")), FMath::RoundToInt(22.0f * Scale), false, MutedTextColor, 34.0f * Scale);
PrimaryFocusButton = AddButton(Panel, FText::FromString(TEXT("Resume")), ButtonColor, ButtonHoverColor, 16.0f * Scale);
PrimaryFocusButton->OnClicked.AddDynamic(this, &UAgrarianMvpFrontendWidget::HandlePrimaryActionClicked);
UButton* QuitButton = AddButton(Panel, FText::FromString(TEXT("Save & Quit")), QuitButtonColor, FLinearColor(0.58f, 0.28f, 0.22f, 1.0f), 34.0f * Scale);
UButton* SaveButton = AddButton(Panel, FText::FromString(TEXT("Save Game")), SecondaryButtonColor, ButtonHoverColor, 12.0f * Scale);
SaveButton->OnClicked.AddDynamic(this, &UAgrarianMvpFrontendWidget::HandleSaveGameClicked);
UButton* SettingsButton = AddButton(Panel, FText::FromString(TEXT("Settings")), SecondaryButtonColor, ButtonHoverColor, 12.0f * Scale);
SettingsButton->OnClicked.AddDynamic(this, &UAgrarianMvpFrontendWidget::HandleSettingsClicked);
UButton* QuitButton = AddButton(Panel, FText::FromString(TEXT("Save & Exit")), QuitButtonColor, FLinearColor(0.58f, 0.28f, 0.22f, 1.0f), 12.0f * Scale);
QuitButton->OnClicked.AddDynamic(this, &UAgrarianMvpFrontendWidget::HandleSaveAndQuitClicked);
AddText(Panel, FText::FromString(TEXT("Escape opens this menu. Save & Quit writes the current world save before closing.")), FMath::RoundToInt(16.0f * Scale), false, MutedTextColor, 0.0f);
UButton* QuitWithoutSavingButton = AddButton(Panel, FText::FromString(TEXT("Quit Without Saving")), SecondaryButtonColor, FLinearColor(0.46f, 0.20f, 0.16f, 1.0f), 28.0f * Scale);
QuitWithoutSavingButton->OnClicked.AddDynamic(this, &UAgrarianMvpFrontendWidget::HandleQuitWithoutSavingClicked);
AddText(Panel, FText::FromString(TEXT("Esc resumes. S saves. O opens settings. Q saves and exits. X exits without saving.")), FMath::RoundToInt(16.0f * Scale), false, MutedTextColor, 0.0f);
return;
}
@@ -390,6 +465,26 @@ void UAgrarianMvpFrontendWidget::RebuildFrontendTree()
return;
}
if (ActiveScreen == EAgrarianMvpFrontendScreen::GameSaved)
{
AddText(Panel, FText::FromString(TEXT("Game Saved")), FMath::RoundToInt(20.0f * Scale), true, AccentColor, 6.0f * Scale);
AddText(Panel, FText::FromString(TEXT("World state saved")), FMath::RoundToInt(34.0f * Scale), true, TextColor, 8.0f * Scale);
AddText(Panel, FText::FromString(TEXT("Return to the pause menu before resuming or exiting.")), FMath::RoundToInt(18.0f * Scale), false, MutedTextColor, 28.0f * Scale);
PrimaryFocusButton = AddButton(Panel, FText::FromString(TEXT("Back to Pause Menu")), ButtonColor, ButtonHoverColor, 0.0f);
PrimaryFocusButton->OnClicked.AddDynamic(this, &UAgrarianMvpFrontendWidget::HandleBackClicked);
return;
}
if (ActiveScreen == EAgrarianMvpFrontendScreen::Settings)
{
AddText(Panel, FText::FromString(TEXT("Settings")), FMath::RoundToInt(20.0f * Scale), true, AccentColor, 6.0f * Scale);
AddText(Panel, FText::FromString(TEXT("Player Options")), FMath::RoundToInt(34.0f * Scale), true, TextColor, 8.0f * Scale);
AddText(Panel, FText::FromString(TEXT("Interface scale and high contrast are available now through tester commands. Units, controls, hardware, audio, and accessibility options are queued for the settings roadmap.")), FMath::RoundToInt(18.0f * Scale), false, MutedTextColor, 28.0f * Scale);
PrimaryFocusButton = AddButton(Panel, FText::FromString(TEXT("Back to Pause Menu")), ButtonColor, ButtonHoverColor, 0.0f);
PrimaryFocusButton->OnClicked.AddDynamic(this, &UAgrarianMvpFrontendWidget::HandleBackClicked);
return;
}
AddText(Panel, FText::FromString(TEXT("Loading Segment")), FMath::RoundToInt(18.0f * Scale), true, AccentColor, 6.0f * Scale);
AddText(Panel, FText::FromString(TEXT("Preparing Ground Zero")), FMath::RoundToInt(34.0f * Scale), true, TextColor, 8.0f * Scale);
AddText(Panel, FText::FromString(TEXT("Loading terrain, weather, survival state, and server session data.")), FMath::RoundToInt(18.0f * Scale), false, MutedTextColor, 70.0f * Scale);
@@ -485,6 +580,30 @@ void UAgrarianMvpFrontendWidget::HandleSaveAndQuitClicked()
});
}
void UAgrarianMvpFrontendWidget::HandleSaveGameClicked()
{
DeferFrontendAction([this]()
{
SaveGame();
});
}
void UAgrarianMvpFrontendWidget::HandleSettingsClicked()
{
DeferFrontendAction([this]()
{
SetActiveScreen(EAgrarianMvpFrontendScreen::Settings);
});
}
void UAgrarianMvpFrontendWidget::HandleQuitWithoutSavingClicked()
{
DeferFrontendAction([this]()
{
QuitWithoutSaving();
});
}
void UAgrarianMvpFrontendWidget::HandleMaleCharacterClicked()
{
DeferFrontendAction([this]()
@@ -17,6 +17,8 @@ enum class EAgrarianMvpFrontendScreen : uint8
CharacterSelection,
JoinServer,
Loading,
Settings,
GameSaved,
SavingAndQuit
};
@@ -75,9 +77,15 @@ public:
UFUNCTION(BlueprintCallable, Category = "Agrarian|MVP UI")
void BackFromActiveScreen();
UFUNCTION(BlueprintCallable, Category = "Agrarian|MVP UI")
void SaveGame();
UFUNCTION(BlueprintCallable, Category = "Agrarian|MVP UI")
void SaveAndQuit();
UFUNCTION(BlueprintCallable, Category = "Agrarian|MVP UI")
void QuitWithoutSaving();
protected:
virtual void NativeConstruct() override;
@@ -96,6 +104,8 @@ private:
UFUNCTION()
void ExecuteSaveAndQuit();
void ExecuteSaveGame();
void ExecuteQuitWithoutSaving();
UFUNCTION()
void HandlePrimaryActionClicked();
@@ -106,6 +116,15 @@ private:
UFUNCTION()
void HandleSaveAndQuitClicked();
UFUNCTION()
void HandleSaveGameClicked();
UFUNCTION()
void HandleSettingsClicked();
UFUNCTION()
void HandleQuitWithoutSavingClicked();
UFUNCTION()
void HandleMaleCharacterClicked();