Add MVP character archetype choice
This commit is contained in:
@@ -795,7 +795,7 @@ Target deliverable: A small group can join a server, spawn into one biome, gathe
|
|||||||
|
|
||||||
- [x] Add main menu placeholder. Added native `UAgrarianMvpFrontendWidget` with a scalable painted main menu panel and local player-controller spawning so the MVP frontend has an owned placeholder instead of relying on template UI.
|
- [x] Add main menu placeholder. Added native `UAgrarianMvpFrontendWidget` with a scalable painted main menu panel and local player-controller spawning so the MVP frontend has an owned placeholder instead of relying on template UI.
|
||||||
- [x] After splash/startup screens, land on an MVP character selection landing page. The local player controller now opens the MVP frontend on `CharacterSelection`, and the native frontend widget paints a scalable character-selection landing page before gameplay.
|
- [x] After splash/startup screens, land on an MVP character selection landing page. The local player controller now opens the MVP frontend on `CharacterSelection`, and the native frontend widget paints a scalable character-selection landing page before gameplay.
|
||||||
- [ ] Let players choose a realistic young adult male or female character with average proportions for the MVP.
|
- [x] Let players choose a realistic young adult male or female character with average proportions for the MVP. Added a selectable MVP character archetype enum, visible selected state, keyboard selection, and `AgrarianSelectCharacter male|female` dev command while keeping both choices on the same MVP survival baseline until real character art arrives.
|
||||||
- [ ] Add join server screen.
|
- [ ] Add join server screen.
|
||||||
- [ ] Add loading screen.
|
- [ ] Add loading screen.
|
||||||
- [~] Add HUD.
|
- [~] Add HUD.
|
||||||
|
|||||||
@@ -0,0 +1,61 @@
|
|||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
|
||||||
|
ROOT = Path(__file__).resolve().parents[1]
|
||||||
|
FILES = {
|
||||||
|
"AgrarianMvpFrontendWidget.h": ROOT / "Source" / "AgrarianGame" / "AgrarianMvpFrontendWidget.h",
|
||||||
|
"AgrarianMvpFrontendWidget.cpp": ROOT / "Source" / "AgrarianGame" / "AgrarianMvpFrontendWidget.cpp",
|
||||||
|
"AgrarianGamePlayerController.h": ROOT / "Source" / "AgrarianGame" / "AgrarianGamePlayerController.h",
|
||||||
|
"AgrarianGamePlayerController.cpp": ROOT / "Source" / "AgrarianGame" / "AgrarianGamePlayerController.cpp",
|
||||||
|
"AGRARIAN_DEVELOPMENT_ROADMAP.md": ROOT / "AGRARIAN_DEVELOPMENT_ROADMAP.md",
|
||||||
|
}
|
||||||
|
|
||||||
|
EXPECTED = {
|
||||||
|
"AgrarianMvpFrontendWidget.h": [
|
||||||
|
"EAgrarianMvpCharacterArchetype",
|
||||||
|
"YoungAdultMale",
|
||||||
|
"YoungAdultFemale",
|
||||||
|
"SelectedCharacterArchetype",
|
||||||
|
"SetSelectedCharacterArchetype",
|
||||||
|
"NativeOnKeyDown",
|
||||||
|
],
|
||||||
|
"AgrarianMvpFrontendWidget.cpp": [
|
||||||
|
"EKeys::Left",
|
||||||
|
"EKeys::Right",
|
||||||
|
"EKeys::A",
|
||||||
|
"EKeys::D",
|
||||||
|
"Selected",
|
||||||
|
"Use Left/Right or A/D to choose",
|
||||||
|
"GetSelectedCharacterLabel",
|
||||||
|
],
|
||||||
|
"AgrarianGamePlayerController.h": [
|
||||||
|
"AgrarianSelectCharacter",
|
||||||
|
],
|
||||||
|
"AgrarianGamePlayerController.cpp": [
|
||||||
|
"AgrarianSelectCharacter",
|
||||||
|
"SetSelectedCharacterArchetype(EAgrarianMvpCharacterArchetype::YoungAdultMale)",
|
||||||
|
"SetSelectedCharacterArchetype(EAgrarianMvpCharacterArchetype::YoungAdultFemale)",
|
||||||
|
],
|
||||||
|
"AGRARIAN_DEVELOPMENT_ROADMAP.md": [
|
||||||
|
"[x] Let players choose a realistic young adult male or female character with average proportions for the MVP.",
|
||||||
|
"`AgrarianSelectCharacter male|female`",
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
missing = []
|
||||||
|
for label, path in FILES.items():
|
||||||
|
text = path.read_text(encoding="utf-8")
|
||||||
|
for snippet in EXPECTED[label]:
|
||||||
|
if snippet not in text:
|
||||||
|
missing.append(f"{label}: {snippet}")
|
||||||
|
|
||||||
|
if missing:
|
||||||
|
raise RuntimeError("MVP character archetype choice verification failed: " + "; ".join(missing))
|
||||||
|
|
||||||
|
print("Agrarian MVP character archetype choice verification complete.")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
@@ -280,6 +280,31 @@ void AAgrarianGamePlayerController::AgrarianCraftStatus()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AAgrarianGamePlayerController::AgrarianSelectCharacter(FName Archetype)
|
||||||
|
{
|
||||||
|
if (!MvpFrontendWidget)
|
||||||
|
{
|
||||||
|
ClientMessage(TEXT("No MVP frontend widget is active."));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Archetype == TEXT("male") || Archetype == TEXT("YoungAdultMale"))
|
||||||
|
{
|
||||||
|
MvpFrontendWidget->SetSelectedCharacterArchetype(EAgrarianMvpCharacterArchetype::YoungAdultMale);
|
||||||
|
ClientMessage(TEXT("Selected MVP young adult male character archetype."));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Archetype == TEXT("female") || Archetype == TEXT("YoungAdultFemale"))
|
||||||
|
{
|
||||||
|
MvpFrontendWidget->SetSelectedCharacterArchetype(EAgrarianMvpCharacterArchetype::YoungAdultFemale);
|
||||||
|
ClientMessage(TEXT("Selected MVP young adult female character archetype."));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ClientMessage(TEXT("Usage: AgrarianSelectCharacter male|female"));
|
||||||
|
}
|
||||||
|
|
||||||
void AAgrarianGamePlayerController::AgrarianTravel(float X, float Y, float Z)
|
void AAgrarianGamePlayerController::AgrarianTravel(float X, float Y, float Z)
|
||||||
{
|
{
|
||||||
ServerAgrarianTravel(FVector(X, Y, Z));
|
ServerAgrarianTravel(FVector(X, Y, Z));
|
||||||
|
|||||||
@@ -91,6 +91,9 @@ public:
|
|||||||
UFUNCTION(Exec)
|
UFUNCTION(Exec)
|
||||||
void AgrarianCraftStatus();
|
void AgrarianCraftStatus();
|
||||||
|
|
||||||
|
UFUNCTION(Exec)
|
||||||
|
void AgrarianSelectCharacter(FName Archetype);
|
||||||
|
|
||||||
UFUNCTION(Exec)
|
UFUNCTION(Exec)
|
||||||
void AgrarianTravel(float X, float Y, float Z);
|
void AgrarianTravel(float X, float Y, float Z);
|
||||||
|
|
||||||
|
|||||||
@@ -2,15 +2,50 @@
|
|||||||
|
|
||||||
#include "AgrarianMvpFrontendWidget.h"
|
#include "AgrarianMvpFrontendWidget.h"
|
||||||
|
|
||||||
|
#include "InputCoreTypes.h"
|
||||||
#include "Rendering/DrawElements.h"
|
#include "Rendering/DrawElements.h"
|
||||||
#include "Styling/CoreStyle.h"
|
#include "Styling/CoreStyle.h"
|
||||||
|
|
||||||
|
void UAgrarianMvpFrontendWidget::NativeConstruct()
|
||||||
|
{
|
||||||
|
Super::NativeConstruct();
|
||||||
|
bIsFocusable = true;
|
||||||
|
SetKeyboardFocus();
|
||||||
|
}
|
||||||
|
|
||||||
|
FReply UAgrarianMvpFrontendWidget::NativeOnKeyDown(const FGeometry& InGeometry, const FKeyEvent& InKeyEvent)
|
||||||
|
{
|
||||||
|
if (ActiveScreen == EAgrarianMvpFrontendScreen::CharacterSelection)
|
||||||
|
{
|
||||||
|
const FKey Key = InKeyEvent.GetKey();
|
||||||
|
if (Key == EKeys::Left || Key == EKeys::A)
|
||||||
|
{
|
||||||
|
SetSelectedCharacterArchetype(EAgrarianMvpCharacterArchetype::YoungAdultMale);
|
||||||
|
return FReply::Handled();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Key == EKeys::Right || Key == EKeys::D)
|
||||||
|
{
|
||||||
|
SetSelectedCharacterArchetype(EAgrarianMvpCharacterArchetype::YoungAdultFemale);
|
||||||
|
return FReply::Handled();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Super::NativeOnKeyDown(InGeometry, InKeyEvent);
|
||||||
|
}
|
||||||
|
|
||||||
void UAgrarianMvpFrontendWidget::SetActiveScreen(EAgrarianMvpFrontendScreen NewScreen)
|
void UAgrarianMvpFrontendWidget::SetActiveScreen(EAgrarianMvpFrontendScreen NewScreen)
|
||||||
{
|
{
|
||||||
ActiveScreen = NewScreen;
|
ActiveScreen = NewScreen;
|
||||||
InvalidateLayoutAndVolatility();
|
InvalidateLayoutAndVolatility();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void UAgrarianMvpFrontendWidget::SetSelectedCharacterArchetype(EAgrarianMvpCharacterArchetype NewArchetype)
|
||||||
|
{
|
||||||
|
SelectedCharacterArchetype = NewArchetype;
|
||||||
|
InvalidateLayoutAndVolatility();
|
||||||
|
}
|
||||||
|
|
||||||
int32 UAgrarianMvpFrontendWidget::NativePaint(
|
int32 UAgrarianMvpFrontendWidget::NativePaint(
|
||||||
const FPaintArgs& Args,
|
const FPaintArgs& Args,
|
||||||
const FGeometry& AllottedGeometry,
|
const FGeometry& AllottedGeometry,
|
||||||
@@ -122,7 +157,7 @@ void UAgrarianMvpFrontendWidget::DrawCharacterSelection(
|
|||||||
const FVector2D MaleCardPosition(ContentX, PanelPosition.Y + (142.0f * Scale));
|
const FVector2D MaleCardPosition(ContentX, PanelPosition.Y + (142.0f * Scale));
|
||||||
const FVector2D FemaleCardPosition(ContentX + CardSize.X + (24.0f * Scale), MaleCardPosition.Y);
|
const FVector2D FemaleCardPosition(ContentX + CardSize.X + (24.0f * Scale), MaleCardPosition.Y);
|
||||||
|
|
||||||
auto DrawCharacterCard = [&](const FVector2D& CardPosition, const FText& Title, const FText& Summary, const FLinearColor& AccentColor)
|
auto DrawCharacterCard = [&](const FVector2D& CardPosition, const FText& Title, const FText& Summary, const FLinearColor& AccentColor, bool bSelected)
|
||||||
{
|
{
|
||||||
FSlateDrawElement::MakeBox(
|
FSlateDrawElement::MakeBox(
|
||||||
OutDrawElements,
|
OutDrawElements,
|
||||||
@@ -140,6 +175,17 @@ void UAgrarianMvpFrontendWidget::DrawCharacterSelection(
|
|||||||
ESlateDrawEffect::None,
|
ESlateDrawEffect::None,
|
||||||
AccentColor);
|
AccentColor);
|
||||||
|
|
||||||
|
if (bSelected)
|
||||||
|
{
|
||||||
|
FSlateDrawElement::MakeBox(
|
||||||
|
OutDrawElements,
|
||||||
|
++LayerId,
|
||||||
|
AllottedGeometry.ToPaintGeometry(FVector2f(CardSize.X, CardSize.Y), FSlateLayoutTransform(FVector2f(CardPosition))),
|
||||||
|
FCoreStyle::Get().GetBrush(TEXT("WhiteBrush")),
|
||||||
|
ESlateDrawEffect::None,
|
||||||
|
FLinearColor(AccentColor.R, AccentColor.G, AccentColor.B, 0.16f));
|
||||||
|
}
|
||||||
|
|
||||||
const FVector2D PortraitPosition = CardPosition + FVector2D(24.0f * Scale, 42.0f * Scale);
|
const FVector2D PortraitPosition = CardPosition + FVector2D(24.0f * Scale, 42.0f * Scale);
|
||||||
const FVector2D PortraitSize(72.0f * Scale, 112.0f * Scale);
|
const FVector2D PortraitSize(72.0f * Scale, 112.0f * Scale);
|
||||||
FSlateDrawElement::MakeBox(
|
FSlateDrawElement::MakeBox(
|
||||||
@@ -152,12 +198,20 @@ void UAgrarianMvpFrontendWidget::DrawCharacterSelection(
|
|||||||
|
|
||||||
DrawTextAt(OutDrawElements, LayerId, AllottedGeometry, Title, CardPosition + FVector2D(116.0f * Scale, 42.0f * Scale), CardSize.X - (140.0f * Scale), CardTitleFont, FLinearColor(0.92f, 0.98f, 0.84f, 1.0f));
|
DrawTextAt(OutDrawElements, LayerId, AllottedGeometry, Title, CardPosition + FVector2D(116.0f * Scale, 42.0f * Scale), CardSize.X - (140.0f * Scale), CardTitleFont, FLinearColor(0.92f, 0.98f, 0.84f, 1.0f));
|
||||||
DrawTextAt(OutDrawElements, LayerId, AllottedGeometry, Summary, CardPosition + FVector2D(116.0f * Scale, 82.0f * Scale), CardSize.X - (140.0f * Scale), LabelFont, FLinearColor(0.72f, 0.78f, 0.68f, 1.0f));
|
DrawTextAt(OutDrawElements, LayerId, AllottedGeometry, Summary, CardPosition + FVector2D(116.0f * Scale, 82.0f * Scale), CardSize.X - (140.0f * Scale), LabelFont, FLinearColor(0.72f, 0.78f, 0.68f, 1.0f));
|
||||||
|
DrawTextAt(OutDrawElements, LayerId, AllottedGeometry, bSelected ? FText::FromString(TEXT("Selected")) : FText::FromString(TEXT("Available")), CardPosition + FVector2D(116.0f * Scale, 144.0f * Scale), CardSize.X - (140.0f * Scale), LabelFont, bSelected ? AccentColor : FLinearColor(0.58f, 0.62f, 0.54f, 1.0f));
|
||||||
};
|
};
|
||||||
|
|
||||||
DrawCharacterCard(MaleCardPosition, FText::FromString(TEXT("Young adult male")), FText::FromString(TEXT("Average proportions, survival baseline, placeholder visual.")), FLinearColor(0.36f, 0.58f, 0.78f, 1.0f));
|
DrawCharacterCard(MaleCardPosition, FText::FromString(TEXT("Young adult male")), FText::FromString(TEXT("Average proportions, survival baseline, placeholder visual.")), FLinearColor(0.36f, 0.58f, 0.78f, 1.0f), SelectedCharacterArchetype == EAgrarianMvpCharacterArchetype::YoungAdultMale);
|
||||||
DrawCharacterCard(FemaleCardPosition, FText::FromString(TEXT("Young adult female")), FText::FromString(TEXT("Average proportions, survival baseline, placeholder visual.")), FLinearColor(0.56f, 0.68f, 0.46f, 1.0f));
|
DrawCharacterCard(FemaleCardPosition, FText::FromString(TEXT("Young adult female")), FText::FromString(TEXT("Average proportions, survival baseline, placeholder visual.")), FLinearColor(0.56f, 0.68f, 0.46f, 1.0f), SelectedCharacterArchetype == EAgrarianMvpCharacterArchetype::YoungAdultFemale);
|
||||||
|
|
||||||
DrawTextAt(OutDrawElements, LayerId, AllottedGeometry, FText::FromString(TEXT("Selection is visual-only for this milestone; both choices enter the same MVP survival baseline.")), FVector2D(ContentX, PanelPosition.Y + PanelSize.Y - (46.0f * Scale)), ContentWidth, LabelFont, FLinearColor(0.62f, 0.68f, 0.58f, 1.0f));
|
DrawTextAt(OutDrawElements, LayerId, AllottedGeometry, FText::Format(FText::FromString(TEXT("Selected: {0}. Use Left/Right or A/D to choose; both choices enter the same MVP survival baseline.")), GetSelectedCharacterLabel()), FVector2D(ContentX, PanelPosition.Y + PanelSize.Y - (46.0f * Scale)), ContentWidth, LabelFont, FLinearColor(0.62f, 0.68f, 0.58f, 1.0f));
|
||||||
|
}
|
||||||
|
|
||||||
|
FText UAgrarianMvpFrontendWidget::GetSelectedCharacterLabel() const
|
||||||
|
{
|
||||||
|
return SelectedCharacterArchetype == EAgrarianMvpCharacterArchetype::YoungAdultFemale
|
||||||
|
? FText::FromString(TEXT("young adult female"))
|
||||||
|
: FText::FromString(TEXT("young adult male"));
|
||||||
}
|
}
|
||||||
|
|
||||||
void UAgrarianMvpFrontendWidget::DrawTextAt(
|
void UAgrarianMvpFrontendWidget::DrawTextAt(
|
||||||
|
|||||||
@@ -15,6 +15,13 @@ enum class EAgrarianMvpFrontendScreen : uint8
|
|||||||
Loading
|
Loading
|
||||||
};
|
};
|
||||||
|
|
||||||
|
UENUM(BlueprintType)
|
||||||
|
enum class EAgrarianMvpCharacterArchetype : uint8
|
||||||
|
{
|
||||||
|
YoungAdultMale,
|
||||||
|
YoungAdultFemale
|
||||||
|
};
|
||||||
|
|
||||||
UCLASS()
|
UCLASS()
|
||||||
class AGRARIANGAME_API UAgrarianMvpFrontendWidget : public UUserWidget
|
class AGRARIANGAME_API UAgrarianMvpFrontendWidget : public UUserWidget
|
||||||
{
|
{
|
||||||
@@ -24,6 +31,9 @@ public:
|
|||||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Agrarian|MVP UI")
|
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Agrarian|MVP UI")
|
||||||
EAgrarianMvpFrontendScreen ActiveScreen = EAgrarianMvpFrontendScreen::MainMenu;
|
EAgrarianMvpFrontendScreen ActiveScreen = EAgrarianMvpFrontendScreen::MainMenu;
|
||||||
|
|
||||||
|
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Agrarian|MVP UI")
|
||||||
|
EAgrarianMvpCharacterArchetype SelectedCharacterArchetype = EAgrarianMvpCharacterArchetype::YoungAdultMale;
|
||||||
|
|
||||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Agrarian|MVP UI")
|
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Agrarian|MVP UI")
|
||||||
FText MainMenuTitle = FText::FromString(TEXT("Agrarian"));
|
FText MainMenuTitle = FText::FromString(TEXT("Agrarian"));
|
||||||
|
|
||||||
@@ -42,7 +52,14 @@ public:
|
|||||||
UFUNCTION(BlueprintCallable, Category = "Agrarian|MVP UI")
|
UFUNCTION(BlueprintCallable, Category = "Agrarian|MVP UI")
|
||||||
void SetActiveScreen(EAgrarianMvpFrontendScreen NewScreen);
|
void SetActiveScreen(EAgrarianMvpFrontendScreen NewScreen);
|
||||||
|
|
||||||
|
UFUNCTION(BlueprintCallable, Category = "Agrarian|MVP UI")
|
||||||
|
void SetSelectedCharacterArchetype(EAgrarianMvpCharacterArchetype NewArchetype);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
virtual void NativeConstruct() override;
|
||||||
|
|
||||||
|
virtual FReply NativeOnKeyDown(const FGeometry& InGeometry, const FKeyEvent& InKeyEvent) override;
|
||||||
|
|
||||||
virtual int32 NativePaint(
|
virtual int32 NativePaint(
|
||||||
const FPaintArgs& Args,
|
const FPaintArgs& Args,
|
||||||
const FGeometry& AllottedGeometry,
|
const FGeometry& AllottedGeometry,
|
||||||
@@ -69,6 +86,8 @@ private:
|
|||||||
const FVector2D& PanelSize,
|
const FVector2D& PanelSize,
|
||||||
float Scale) const;
|
float Scale) const;
|
||||||
|
|
||||||
|
FText GetSelectedCharacterLabel() const;
|
||||||
|
|
||||||
void DrawTextAt(
|
void DrawTextAt(
|
||||||
FSlateWindowElementList& OutDrawElements,
|
FSlateWindowElementList& OutDrawElements,
|
||||||
int32& LayerId,
|
int32& LayerId,
|
||||||
|
|||||||
Reference in New Issue
Block a user