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] 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 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 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 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 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. - [ ] 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 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. - [ ] 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 ## 0.2.G Homesteading Knowledge Progression
- [ ] Define early profession paths: farmer, herder, carpenter, mason, cook, medic, hunter, fisher, trapper, trader, and scout. - [ ] 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": [ "AgrarianMvpFrontendWidget.h": [
"ConfirmActiveScreen", "ConfirmActiveScreen",
"BackFromActiveScreen", "BackFromActiveScreen",
"SaveGame",
"SaveAndQuit", "SaveAndQuit",
"QuitWithoutSaving",
"Settings",
"GameSaved",
], ],
"AgrarianMvpFrontendWidget.cpp": [ "AgrarianMvpFrontendWidget.cpp": [
"UButton::StaticClass()", "UButton::StaticClass()",
"HandleMaleCharacterClicked", "HandleMaleCharacterClicked",
"HandleFemaleCharacterClicked", "HandleFemaleCharacterClicked",
"OnClicked.AddDynamic", "OnClicked.AddDynamic",
"Save & Quit", "Save Game",
"Settings",
"Save & Exit",
"Quit Without Saving",
"Saving World", "Saving World",
"Game Saved",
"Player Options",
"HandleSaveGameClicked",
"HandleSettingsClicked",
"HandleQuitWithoutSavingClicked",
"ExecuteSaveGame",
"ExecuteQuitWithoutSaving",
"ConsoleCommand(TEXT(\"AgrarianSaveWorld\"))", "ConsoleCommand(TEXT(\"AgrarianSaveWorld\"))",
"ConsoleCommand(TEXT(\"quit\"))", "ConsoleCommand(TEXT(\"quit\"))",
"AAgrarianGamePlayerController* AgrarianPlayerController", "AAgrarianGamePlayerController* AgrarianPlayerController",
@@ -23,8 +23,12 @@ def main() -> None:
roadmap = ROADMAP.read_text(encoding="utf-8") roadmap = ROADMAP.read_text(encoding="utf-8")
for token in ( for token in (
"Settings",
"GameSaved",
"SavingAndQuit", "SavingAndQuit",
"SaveGame",
"ExecuteSaveAndQuit", "ExecuteSaveAndQuit",
"QuitWithoutSaving",
): ):
require(token in header, f"missing segmented flow declaration: {token}") require(token in header, f"missing segmented flow declaration: {token}")
@@ -34,11 +38,20 @@ def main() -> None:
"Loading Segment", "Loading Segment",
"Pause Menu", "Pause Menu",
"Gameplay is paused while this menu is active.", "Gameplay is paused while this menu is active.",
"Save Game",
"Settings",
"Quit Without Saving",
"Game Saved",
"Player Options",
"Saving World", "Saving World",
"Writing the current world state", "Writing the current world state",
"SetActiveScreen(EAgrarianMvpFrontendScreen::GameSaved)",
"SetActiveScreen(EAgrarianMvpFrontendScreen::Settings)",
"SetActiveScreen(EAgrarianMvpFrontendScreen::SavingAndQuit)", "SetActiveScreen(EAgrarianMvpFrontendScreen::SavingAndQuit)",
"GetTimerManager().SetTimer", "GetTimerManager().SetTimer",
"ExecuteSaveGame",
"ExecuteSaveAndQuit", "ExecuteSaveAndQuit",
"ExecuteQuitWithoutSaving",
"ConsoleCommand(TEXT(\"AgrarianSaveWorld\"))", "ConsoleCommand(TEXT(\"AgrarianSaveWorld\"))",
"ConsoleCommand(TEXT(\"quit\"))", "ConsoleCommand(TEXT(\"quit\"))",
): ):
@@ -87,6 +87,33 @@ FReply UAgrarianMvpFrontendWidget::NativeOnKeyDown(const FGeometry& InGeometry,
SaveAndQuit(); SaveAndQuit();
return FReply::Handled(); 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) else if (ActiveScreen == EAgrarianMvpFrontendScreen::SavingAndQuit)
{ {
@@ -147,6 +174,17 @@ void UAgrarianMvpFrontendWidget::SaveAndQuit()
ExecuteSaveAndQuit(); ExecuteSaveAndQuit();
} }
void UAgrarianMvpFrontendWidget::SaveGame()
{
ExecuteSaveGame();
SetActiveScreen(EAgrarianMvpFrontendScreen::GameSaved);
}
void UAgrarianMvpFrontendWidget::QuitWithoutSaving()
{
ExecuteQuitWithoutSaving();
}
void UAgrarianMvpFrontendWidget::ExecuteSaveAndQuit() void UAgrarianMvpFrontendWidget::ExecuteSaveAndQuit()
{ {
if (APlayerController* PlayerController = GetOwningPlayer()) 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() void UAgrarianMvpFrontendWidget::ContinueFromActiveScreen()
{ {
if (ActiveScreen == EAgrarianMvpFrontendScreen::CharacterSelection) if (ActiveScreen == EAgrarianMvpFrontendScreen::CharacterSelection)
@@ -181,6 +235,12 @@ void UAgrarianMvpFrontendWidget::ContinueFromActiveScreen()
return; return;
} }
if (ActiveScreen == EAgrarianMvpFrontendScreen::Settings || ActiveScreen == EAgrarianMvpFrontendScreen::GameSaved)
{
SetActiveScreen(EAgrarianMvpFrontendScreen::MainMenu);
return;
}
if (ActiveScreen == EAgrarianMvpFrontendScreen::MainMenu) if (ActiveScreen == EAgrarianMvpFrontendScreen::MainMenu)
{ {
CompleteFrontendFlow(); CompleteFrontendFlow();
@@ -200,6 +260,11 @@ void UAgrarianMvpFrontendWidget::ReturnFromActiveScreen()
{ {
CompleteFrontendFlow(); CompleteFrontendFlow();
} }
if (ActiveScreen == EAgrarianMvpFrontendScreen::Settings || ActiveScreen == EAgrarianMvpFrontendScreen::GameSaved)
{
SetActiveScreen(EAgrarianMvpFrontendScreen::MainMenu);
}
} }
void UAgrarianMvpFrontendWidget::CompleteFrontendFlow() 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, 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, 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 = AddButton(Panel, FText::FromString(TEXT("Resume")), ButtonColor, ButtonHoverColor, 16.0f * Scale);
PrimaryFocusButton->OnClicked.AddDynamic(this, &UAgrarianMvpFrontendWidget::HandlePrimaryActionClicked); 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); 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; return;
} }
@@ -390,6 +465,26 @@ void UAgrarianMvpFrontendWidget::RebuildFrontendTree()
return; 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("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("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); 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() void UAgrarianMvpFrontendWidget::HandleMaleCharacterClicked()
{ {
DeferFrontendAction([this]() DeferFrontendAction([this]()
@@ -17,6 +17,8 @@ enum class EAgrarianMvpFrontendScreen : uint8
CharacterSelection, CharacterSelection,
JoinServer, JoinServer,
Loading, Loading,
Settings,
GameSaved,
SavingAndQuit SavingAndQuit
}; };
@@ -75,9 +77,15 @@ public:
UFUNCTION(BlueprintCallable, Category = "Agrarian|MVP UI") UFUNCTION(BlueprintCallable, Category = "Agrarian|MVP UI")
void BackFromActiveScreen(); void BackFromActiveScreen();
UFUNCTION(BlueprintCallable, Category = "Agrarian|MVP UI")
void SaveGame();
UFUNCTION(BlueprintCallable, Category = "Agrarian|MVP UI") UFUNCTION(BlueprintCallable, Category = "Agrarian|MVP UI")
void SaveAndQuit(); void SaveAndQuit();
UFUNCTION(BlueprintCallable, Category = "Agrarian|MVP UI")
void QuitWithoutSaving();
protected: protected:
virtual void NativeConstruct() override; virtual void NativeConstruct() override;
@@ -96,6 +104,8 @@ private:
UFUNCTION() UFUNCTION()
void ExecuteSaveAndQuit(); void ExecuteSaveAndQuit();
void ExecuteSaveGame();
void ExecuteQuitWithoutSaving();
UFUNCTION() UFUNCTION()
void HandlePrimaryActionClicked(); void HandlePrimaryActionClicked();
@@ -106,6 +116,15 @@ private:
UFUNCTION() UFUNCTION()
void HandleSaveAndQuitClicked(); void HandleSaveAndQuitClicked();
UFUNCTION()
void HandleSaveGameClicked();
UFUNCTION()
void HandleSettingsClicked();
UFUNCTION()
void HandleQuitWithoutSavingClicked();
UFUNCTION() UFUNCTION()
void HandleMaleCharacterClicked(); void HandleMaleCharacterClicked();