From 075689dce481742dcd0dc67c2d80c51dd449a3c0 Mon Sep 17 00:00:00 2001 From: nathan Date: Sun, 17 May 2026 20:02:22 -0700 Subject: [PATCH] Add cinematic startup credits --- AGRARIAN_DEVELOPMENT_ROADMAP.md | 1 + Config/DefaultGame.ini | 2 +- Scripts/InstallWindowsDemoLaunchers.bat | 2 +- Scripts/verify_startup_credits_sequence.py | 60 +++++ Source/AgrarianGame/AgrarianDemoNoticeActor.h | 4 +- .../AgrarianGame/AgrarianDemoNoticeWidget.cpp | 243 +++++++++++++++++- .../AgrarianGame/AgrarianDemoNoticeWidget.h | 31 ++- 7 files changed, 337 insertions(+), 6 deletions(-) create mode 100644 Scripts/verify_startup_credits_sequence.py diff --git a/AGRARIAN_DEVELOPMENT_ROADMAP.md b/AGRARIAN_DEVELOPMENT_ROADMAP.md index b813938..a191ada 100644 --- a/AGRARIAN_DEVELOPMENT_ROADMAP.md +++ b/AGRARIAN_DEVELOPMENT_ROADMAP.md @@ -710,6 +710,7 @@ Target deliverable: A small group can join a server, spawn into one biome, gathe - [ ] Add footstep placeholders. - [ ] Add gathering sounds. - [ ] Add fire sounds. +- [ ] Add forest-fire risk and spread from irresponsible campfire and open-flame placement, including server-authoritative ignition checks, vegetation fuel rules, suppression hooks, and save/load support. - [ ] Add weather sounds. - [ ] Add wildlife sounds. - [ ] Add UI sounds. diff --git a/Config/DefaultGame.ini b/Config/DefaultGame.ini index fdec87c..7d1e73b 100644 --- a/Config/DefaultGame.ini +++ b/Config/DefaultGame.ini @@ -3,7 +3,7 @@ ProjectID=1C37CF214211E5FA6177D497FE103AC4 ProjectName=Agrarian ProjectDisplayedTitle=NSLOCTEXT("[/Script/EngineSettings]", "AgrarianProjectDisplayedTitle", "Agrarian") ProjectDebugTitleInfo=NSLOCTEXT("[/Script/EngineSettings]", "AgrarianProjectDebugTitle", "Agrarian - Development Build") -ProjectVersion=0.1.E-investor.20260517 +ProjectVersion=0.1.H-investor.20260517 Description=Persistent generational civilization survival prototype. CompanyName=Agrarian Studio CompanyDistinguishedName=Agrarian Studio diff --git a/Scripts/InstallWindowsDemoLaunchers.bat b/Scripts/InstallWindowsDemoLaunchers.bat index 5b61f11..3d43670 100644 --- a/Scripts/InstallWindowsDemoLaunchers.bat +++ b/Scripts/InstallWindowsDemoLaunchers.bat @@ -19,7 +19,7 @@ set "DX12_LAUNCHER=%PACKAGE_DIR%\Start Agrarian Demo - DX12.cmd" set "DX11_LAUNCHER=%PACKAGE_DIR%\Start Agrarian Demo - Compatibility DX11.cmd" set "PREREQ_LAUNCHER=%PACKAGE_DIR%\Install Prerequisites.cmd" set "README_FILE=%PACKAGE_DIR%\README-Investor-Demo.txt" -set "DEMO_VERSION=Investor Demo v0.1.E - Build 2026.05.17" +set "DEMO_VERSION=Investor Demo v0.1.H - Build 2026.05.17" > "%DEFAULT_LAUNCHER%" echo @echo off >> "%DEFAULT_LAUNCHER%" echo cd /d "%%~dp0" diff --git a/Scripts/verify_startup_credits_sequence.py b/Scripts/verify_startup_credits_sequence.py new file mode 100644 index 0000000..76a349d --- /dev/null +++ b/Scripts/verify_startup_credits_sequence.py @@ -0,0 +1,60 @@ +from pathlib import Path + + +ROOT = Path(__file__).resolve().parents[1] + +REQUIRED = { + ROOT / "Source" / "AgrarianGame" / "AgrarianDemoNoticeActor.h": [ + "float NoticeDurationSeconds = 24.0f;", + "Investor Demo v0.1.H - Build 2026.05.17", + ], + ROOT / "Source" / "AgrarianGame" / "AgrarianDemoNoticeWidget.h": [ + "virtual void NativeConstruct() override;", + "DrawCinematicCredits", + "DrawCreditIllustration", + "CreditsStartTimeSeconds", + "Investor Demo v0.1.H - Build 2026.05.17", + ], + ROOT / "Source" / "AgrarianGame" / "AgrarianDemoNoticeWidget.cpp": [ + "Nathan Slaven", + "Lead Developer", + "Hunter Slaven", + "Junior Developer", + "Lisa Reiley", + "Quality Control", + "River Slaven", + "Alpha Game Tester", + "Fisher Slaven", + "Beta Game Tester", + "Cherished individuals, Pacificao seed funding, and Lina Family Investment Funds", + "SlamSeconds", + "DrawCreditIllustration", + ], + ROOT / "Config" / "DefaultGame.ini": [ + "ProjectVersion=0.1.H-investor.20260517", + ], + ROOT / "Scripts" / "InstallWindowsDemoLaunchers.bat": [ + "DEMO_VERSION=Investor Demo v0.1.H - Build 2026.05.17", + ], + ROOT / "AGRARIAN_DEVELOPMENT_ROADMAP.md": [ + "Add forest-fire risk and spread from irresponsible campfire and open-flame placement", + ], +} + + +def main(): + missing = [] + for path, snippets in REQUIRED.items(): + text = path.read_text(encoding="utf-8") + for snippet in snippets: + if snippet not in text: + missing.append(f"{path.relative_to(ROOT)} missing {snippet!r}") + + if missing: + raise SystemExit("Startup credits verification failed:\n" + "\n".join(missing)) + + print("PASS: startup credits sequence and forest-fire roadmap follow-up are present.") + + +if __name__ == "__main__": + main() diff --git a/Source/AgrarianGame/AgrarianDemoNoticeActor.h b/Source/AgrarianGame/AgrarianDemoNoticeActor.h index cf0bd26..b3b6c20 100644 --- a/Source/AgrarianGame/AgrarianDemoNoticeActor.h +++ b/Source/AgrarianGame/AgrarianDemoNoticeActor.h @@ -20,10 +20,10 @@ public: TSubclassOf NoticeWidgetClass; UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Agrarian|Demo", meta = (ClampMin = "1.0")) - float NoticeDurationSeconds = 9.0f; + float NoticeDurationSeconds = 24.0f; UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Agrarian|Demo") - FText VersionLabel = FText::FromString(TEXT("Investor Demo v0.1.E - Build 2026.05.17")); + FText VersionLabel = FText::FromString(TEXT("Investor Demo v0.1.H - Build 2026.05.17")); UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Agrarian|Demo") FText DemoNotice = FText::FromString(TEXT("Beta prototype build - not for public distribution")); diff --git a/Source/AgrarianGame/AgrarianDemoNoticeWidget.cpp b/Source/AgrarianGame/AgrarianDemoNoticeWidget.cpp index 55c918f..9972d1b 100644 --- a/Source/AgrarianGame/AgrarianDemoNoticeWidget.cpp +++ b/Source/AgrarianGame/AgrarianDemoNoticeWidget.cpp @@ -2,9 +2,43 @@ #include "AgrarianDemoNoticeWidget.h" +#include "Engine/World.h" #include "Rendering/DrawElements.h" #include "Styling/CoreStyle.h" +namespace +{ +struct FAgrarianCreditCard +{ + const TCHAR* Name; + const TCHAR* Role; + FLinearColor AccentColor; + int32 IllustrationIndex; + float HoldSeconds; +}; + +const FAgrarianCreditCard CreditCards[] = { + { TEXT("Nathan Slaven"), TEXT("Lead Developer"), FLinearColor(0.70f, 0.90f, 0.46f, 1.0f), 0, 2.05f }, + { TEXT("Hunter Slaven"), TEXT("Junior Developer"), FLinearColor(0.36f, 0.72f, 0.96f, 1.0f), 1, 2.05f }, + { TEXT("Lisa Reiley"), TEXT("Quality Control"), FLinearColor(0.95f, 0.72f, 0.42f, 1.0f), 2, 2.05f }, + { TEXT("River Slaven"), TEXT("Alpha Game Tester"), FLinearColor(0.56f, 0.82f, 0.66f, 1.0f), 3, 2.05f }, + { TEXT("Fisher Slaven"), TEXT("Beta Game Tester"), FLinearColor(0.56f, 0.62f, 0.98f, 1.0f), 4, 2.05f }, + { TEXT("Funding"), TEXT("Cherished individuals, Pacificao seed funding, and Lina Family Investment Funds"), FLinearColor(0.92f, 0.84f, 0.54f, 1.0f), 5, 3.0f }, +}; + +float SmoothStep(float Value) +{ + const float ClampedValue = FMath::Clamp(Value, 0.0f, 1.0f); + return ClampedValue * ClampedValue * (3.0f - (2.0f * ClampedValue)); +} +} + +void UAgrarianDemoNoticeWidget::NativeConstruct() +{ + Super::NativeConstruct(); + CreditsStartTimeSeconds = GetWorld() ? GetWorld()->GetTimeSeconds() : 0.0f; +} + int32 UAgrarianDemoNoticeWidget::NativePaint( const FPaintArgs& Args, const FGeometry& AllottedGeometry, @@ -17,7 +51,7 @@ int32 UAgrarianDemoNoticeWidget::NativePaint( LayerId = Super::NativePaint(Args, AllottedGeometry, MyCullingRect, OutDrawElements, LayerId, InWidgetStyle, bParentEnabled); const FVector2D Size = AllottedGeometry.GetLocalSize(); - const float PanelWidth = FMath::Min(860.0f, Size.X - 96.0f); + const float PanelWidth = FMath::Min(860.0f, FMath::Max(320.0f, Size.X - 96.0f)); const float PanelHeight = 210.0f; const FVector2D PanelPosition((Size.X - PanelWidth) * 0.5f, 42.0f); @@ -46,6 +80,8 @@ int32 UAgrarianDemoNoticeWidget::NativePaint( DrawCenteredText(OutDrawElements, LayerId, AllottedGeometry, DemoNotice, PanelPosition.Y + 126.0f, NoticeFont, FLinearColor(0.78f, 0.82f, 0.75f, 1.0f)); DrawCenteredText(OutDrawElements, LayerId, AllottedGeometry, CopyrightNotice, PanelPosition.Y + 158.0f, NoticeFont, FLinearColor(0.66f, 0.70f, 0.64f, 1.0f)); + DrawCinematicCredits(OutDrawElements, LayerId, AllottedGeometry); + return LayerId; } @@ -72,3 +108,208 @@ void UAgrarianDemoNoticeWidget::DrawCenteredText( ESlateDrawEffect::None, Color); } + +void UAgrarianDemoNoticeWidget::DrawTextAt( + FSlateWindowElementList& OutDrawElements, + int32& LayerId, + const FGeometry& AllottedGeometry, + const FText& Text, + const FVector2D& Position, + float Width, + const FSlateFontInfo& Font, + const FLinearColor& Color) const +{ + FSlateDrawElement::MakeText( + OutDrawElements, + ++LayerId, + AllottedGeometry.ToPaintGeometry(FVector2f(FMath::Max(96.0f, Width), Font.Size + 18.0f), FSlateLayoutTransform(FVector2f(Position))), + Text, + Font, + ESlateDrawEffect::None, + Color); +} + +void UAgrarianDemoNoticeWidget::DrawCinematicCredits( + FSlateWindowElementList& OutDrawElements, + int32& LayerId, + const FGeometry& AllottedGeometry) const +{ + const UWorld* World = GetWorld(); + if (!World) + { + return; + } + + const FVector2D Size = AllottedGeometry.GetLocalSize(); + const float Elapsed = World->GetTimeSeconds() - CreditsStartTimeSeconds; + const float IntroDelay = 0.55f; + const float SlamSeconds = 0.28f; + const float ExitSeconds = 0.48f; + const float GapSeconds = 0.16f; + + float Cursor = IntroDelay; + int32 ActiveIndex = INDEX_NONE; + float LocalTime = 0.0f; + for (int32 Index = 0; Index < UE_ARRAY_COUNT(CreditCards); ++Index) + { + const float CardDuration = SlamSeconds + CreditCards[Index].HoldSeconds + ExitSeconds + GapSeconds; + if (Elapsed >= Cursor && Elapsed < Cursor + CardDuration) + { + ActiveIndex = Index; + LocalTime = Elapsed - Cursor; + break; + } + Cursor += CardDuration; + } + + if (ActiveIndex == INDEX_NONE) + { + return; + } + + const FAgrarianCreditCard& Card = CreditCards[ActiveIndex]; + const bool bEntering = LocalTime < SlamSeconds; + const bool bExiting = LocalTime > SlamSeconds + Card.HoldSeconds; + const float EnterAlpha = bEntering ? SmoothStep(LocalTime / SlamSeconds) : 1.0f; + const float ExitAlpha = bExiting ? 1.0f - SmoothStep((LocalTime - SlamSeconds - Card.HoldSeconds) / ExitSeconds) : 1.0f; + const float Alpha = FMath::Clamp(EnterAlpha * ExitAlpha, 0.0f, 1.0f); + const float ImpactOvershoot = bEntering ? FMath::Sin((LocalTime / SlamSeconds) * PI) * 28.0f : 0.0f; + + const float PanelWidth = FMath::Min(1120.0f, FMath::Max(420.0f, Size.X - 112.0f)); + const float PanelHeight = FMath::Min(330.0f, FMath::Max(220.0f, Size.Y - 340.0f)); + const FVector2D TargetPosition((Size.X - PanelWidth) * 0.5f, FMath::Max(275.0f, (Size.Y - PanelHeight) * 0.5f + 72.0f)); + const float EnterOffset = bEntering ? FMath::Lerp(-Size.X * 0.85f, ImpactOvershoot, SmoothStep(LocalTime / SlamSeconds)) : 0.0f; + const float ExitOffset = bExiting ? FMath::Lerp(0.0f, Size.X * 0.9f, SmoothStep((LocalTime - SlamSeconds - Card.HoldSeconds) / ExitSeconds)) : 0.0f; + const FVector2D PanelPosition(TargetPosition.X + EnterOffset + ExitOffset, TargetPosition.Y); + + FSlateDrawElement::MakeBox( + OutDrawElements, + ++LayerId, + AllottedGeometry.ToPaintGeometry(FVector2f(Size), FSlateLayoutTransform(FVector2f::ZeroVector)), + FCoreStyle::Get().GetBrush(TEXT("WhiteBrush")), + ESlateDrawEffect::None, + FLinearColor(0.0f, 0.0f, 0.0f, 0.34f * Alpha)); + + FSlateDrawElement::MakeBox( + OutDrawElements, + ++LayerId, + AllottedGeometry.ToPaintGeometry(FVector2f(PanelWidth, PanelHeight), FSlateLayoutTransform(FVector2f(PanelPosition))), + FCoreStyle::Get().GetBrush(TEXT("WhiteBrush")), + ESlateDrawEffect::None, + FLinearColor(0.015f, 0.018f, 0.014f, 0.92f * Alpha)); + + FSlateDrawElement::MakeBox( + OutDrawElements, + ++LayerId, + AllottedGeometry.ToPaintGeometry(FVector2f(PanelWidth, 5.0f), FSlateLayoutTransform(FVector2f(PanelPosition))), + FCoreStyle::Get().GetBrush(TEXT("WhiteBrush")), + ESlateDrawEffect::None, + FLinearColor(Card.AccentColor.R, Card.AccentColor.G, Card.AccentColor.B, Alpha)); + + const FVector2D ArtSize(FMath::Min(275.0f, PanelWidth * 0.32f), PanelHeight - 64.0f); + const FVector2D ArtPosition(PanelPosition.X + 32.0f, PanelPosition.Y + 32.0f); + DrawCreditIllustration(OutDrawElements, LayerId, AllottedGeometry, ArtPosition, ArtSize, Card.IllustrationIndex, Card.AccentColor, Alpha); + + const float TextX = ArtPosition.X + ArtSize.X + 42.0f; + const float TextWidth = PanelPosition.X + PanelWidth - TextX - 36.0f; + const FSlateFontInfo NameFont = FCoreStyle::GetDefaultFontStyle("Bold", ActiveIndex == 5 ? 46 : 56); + const FSlateFontInfo RoleFont = FCoreStyle::GetDefaultFontStyle("Regular", ActiveIndex == 5 ? 22 : 28); + const FSlateFontInfo LabelFont = FCoreStyle::GetDefaultFontStyle("Regular", 16); + const float NameY = PanelPosition.Y + (ActiveIndex == 5 ? 76.0f : 92.0f); + + DrawTextAt(OutDrawElements, LayerId, AllottedGeometry, FText::FromString(Card.Name), FVector2D(TextX, NameY), TextWidth, NameFont, FLinearColor(0.94f, 0.98f, 0.88f, Alpha)); + DrawTextAt(OutDrawElements, LayerId, AllottedGeometry, FText::FromString(Card.Role), FVector2D(TextX + 4.0f, NameY + 76.0f), TextWidth, RoleFont, FLinearColor(Card.AccentColor.R, Card.AccentColor.G, Card.AccentColor.B, Alpha)); + DrawTextAt(OutDrawElements, LayerId, AllottedGeometry, FText::FromString(TEXT("Agrarian startup credits")), FVector2D(TextX + 6.0f, PanelPosition.Y + PanelHeight - 58.0f), TextWidth, LabelFont, FLinearColor(0.64f, 0.70f, 0.60f, 0.82f * Alpha)); +} + +void UAgrarianDemoNoticeWidget::DrawCreditIllustration( + FSlateWindowElementList& OutDrawElements, + int32& LayerId, + const FGeometry& AllottedGeometry, + const FVector2D& Position, + const FVector2D& Size, + int32 IllustrationIndex, + const FLinearColor& AccentColor, + float Alpha) const +{ + FSlateDrawElement::MakeBox( + OutDrawElements, + ++LayerId, + AllottedGeometry.ToPaintGeometry(FVector2f(Size), FSlateLayoutTransform(FVector2f(Position))), + FCoreStyle::Get().GetBrush(TEXT("WhiteBrush")), + ESlateDrawEffect::None, + FLinearColor(0.07f, 0.08f, 0.065f, 0.96f * Alpha)); + + const FVector2D Center = Position + (Size * 0.5f); + const FLinearColor SoftAccent(AccentColor.R, AccentColor.G, AccentColor.B, 0.28f * Alpha); + const FLinearColor StrongAccent(AccentColor.R, AccentColor.G, AccentColor.B, Alpha); + + auto DrawBox = [&](const FVector2D& BoxPosition, const FVector2D& BoxSize, const FLinearColor& Color) + { + FSlateDrawElement::MakeBox( + OutDrawElements, + ++LayerId, + AllottedGeometry.ToPaintGeometry(FVector2f(BoxSize), FSlateLayoutTransform(FVector2f(BoxPosition))), + FCoreStyle::Get().GetBrush(TEXT("WhiteBrush")), + ESlateDrawEffect::None, + Color); + }; + + auto DrawLine = [&](const TArray& Points, const FLinearColor& Color, float Thickness) + { + FSlateDrawElement::MakeLines( + OutDrawElements, + ++LayerId, + AllottedGeometry.ToPaintGeometry(), + Points, + ESlateDrawEffect::None, + Color, + true, + Thickness); + }; + + DrawBox(Position + FVector2D(16.0f, 16.0f), FVector2D(Size.X - 32.0f, Size.Y - 32.0f), FLinearColor(0.0f, 0.0f, 0.0f, 0.22f * Alpha)); + + if (IllustrationIndex == 0) + { + DrawBox(Center + FVector2D(-72.0f, -46.0f), FVector2D(144.0f, 82.0f), SoftAccent); + DrawBox(Center + FVector2D(-62.0f, -36.0f), FVector2D(124.0f, 62.0f), FLinearColor(0.02f, 0.03f, 0.025f, Alpha)); + DrawLine({ FVector2f(Center.X - 42.0f, Center.Y - 8.0f), FVector2f(Center.X - 8.0f, Center.Y - 28.0f), FVector2f(Center.X + 42.0f, Center.Y + 18.0f) }, StrongAccent, 4.0f); + DrawBox(Center + FVector2D(-34.0f, 48.0f), FVector2D(68.0f, 10.0f), StrongAccent); + } + else if (IllustrationIndex == 1) + { + DrawLine({ FVector2f(Center.X, Center.Y + 58.0f), FVector2f(Center.X, Center.Y - 48.0f) }, StrongAccent, 5.0f); + DrawLine({ FVector2f(Center.X, Center.Y - 8.0f), FVector2f(Center.X - 48.0f, Center.Y - 42.0f), FVector2f(Center.X - 74.0f, Center.Y - 20.0f) }, StrongAccent, 5.0f); + DrawLine({ FVector2f(Center.X, Center.Y + 2.0f), FVector2f(Center.X + 52.0f, Center.Y - 34.0f), FVector2f(Center.X + 80.0f, Center.Y - 10.0f) }, StrongAccent, 5.0f); + DrawBox(Center + FVector2D(-84.0f, 64.0f), FVector2D(168.0f, 8.0f), SoftAccent); + } + else if (IllustrationIndex == 2) + { + DrawBox(Center + FVector2D(-58.0f, -74.0f), FVector2D(116.0f, 148.0f), FLinearColor(0.93f, 0.88f, 0.72f, 0.22f * Alpha)); + DrawBox(Center + FVector2D(-34.0f, -88.0f), FVector2D(68.0f, 20.0f), StrongAccent); + DrawLine({ FVector2f(Center.X - 32.0f, Center.Y - 26.0f), FVector2f(Center.X - 12.0f, Center.Y - 6.0f), FVector2f(Center.X + 42.0f, Center.Y - 48.0f) }, StrongAccent, 5.0f); + DrawBox(Center + FVector2D(-34.0f, 28.0f), FVector2D(68.0f, 5.0f), StrongAccent); + } + else if (IllustrationIndex == 3) + { + DrawBox(Center + FVector2D(-72.0f, -28.0f), FVector2D(144.0f, 56.0f), SoftAccent); + DrawLine({ FVector2f(Center.X - 70.0f, Center.Y), FVector2f(Center.X - 18.0f, Center.Y - 58.0f), FVector2f(Center.X + 32.0f, Center.Y + 44.0f), FVector2f(Center.X + 78.0f, Center.Y - 18.0f) }, StrongAccent, 5.0f); + DrawBox(Center + FVector2D(-16.0f, -16.0f), FVector2D(32.0f, 32.0f), FLinearColor(0.92f, 0.96f, 0.86f, 0.80f * Alpha)); + } + else if (IllustrationIndex == 4) + { + DrawBox(Center + FVector2D(-64.0f, -58.0f), FVector2D(128.0f, 116.0f), SoftAccent); + DrawLine({ FVector2f(Center.X - 44.0f, Center.Y - 8.0f), FVector2f(Center.X - 8.0f, Center.Y + 28.0f), FVector2f(Center.X + 54.0f, Center.Y - 36.0f) }, StrongAccent, 5.0f); + DrawBox(Center + FVector2D(-74.0f, 74.0f), FVector2D(148.0f, 8.0f), StrongAccent); + DrawBox(Center + FVector2D(-74.0f, -82.0f), FVector2D(148.0f, 8.0f), StrongAccent); + } + else + { + DrawLine({ FVector2f(Center.X, Center.Y + 72.0f), FVector2f(Center.X, Center.Y - 36.0f) }, StrongAccent, 5.0f); + DrawLine({ FVector2f(Center.X, Center.Y - 12.0f), FVector2f(Center.X - 52.0f, Center.Y - 50.0f), FVector2f(Center.X - 80.0f, Center.Y - 28.0f) }, StrongAccent, 5.0f); + DrawLine({ FVector2f(Center.X, Center.Y - 6.0f), FVector2f(Center.X + 52.0f, Center.Y - 54.0f), FVector2f(Center.X + 84.0f, Center.Y - 28.0f) }, StrongAccent, 5.0f); + DrawBox(Center + FVector2D(-92.0f, 78.0f), FVector2D(184.0f, 8.0f), SoftAccent); + DrawBox(Center + FVector2D(-48.0f, 56.0f), FVector2D(96.0f, 8.0f), StrongAccent); + } +} diff --git a/Source/AgrarianGame/AgrarianDemoNoticeWidget.h b/Source/AgrarianGame/AgrarianDemoNoticeWidget.h index 69d3789..b4b36ef 100644 --- a/Source/AgrarianGame/AgrarianDemoNoticeWidget.h +++ b/Source/AgrarianGame/AgrarianDemoNoticeWidget.h @@ -12,11 +12,13 @@ class AGRARIANGAME_API UAgrarianDemoNoticeWidget : public UUserWidget GENERATED_BODY() public: + virtual void NativeConstruct() override; + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Agrarian|Demo") FText Motto = FText::FromString(TEXT("What survives after you are gone?")); UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Agrarian|Demo") - FText VersionLabel = FText::FromString(TEXT("Investor Demo v0.1.E - Build 2026.05.17")); + FText VersionLabel = FText::FromString(TEXT("Investor Demo v0.1.H - Build 2026.05.17")); UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Agrarian|Demo") FText DemoNotice = FText::FromString(TEXT("Beta prototype build - not for public distribution")); @@ -35,6 +37,8 @@ protected: bool bParentEnabled) const override; private: + float CreditsStartTimeSeconds = 0.0f; + void DrawCenteredText( FSlateWindowElementList& OutDrawElements, int32& LayerId, @@ -43,4 +47,29 @@ private: float Y, const FSlateFontInfo& Font, const FLinearColor& Color) const; + + void DrawTextAt( + FSlateWindowElementList& OutDrawElements, + int32& LayerId, + const FGeometry& AllottedGeometry, + const FText& Text, + const FVector2D& Position, + float Width, + const FSlateFontInfo& Font, + const FLinearColor& Color) const; + + void DrawCinematicCredits( + FSlateWindowElementList& OutDrawElements, + int32& LayerId, + const FGeometry& AllottedGeometry) const; + + void DrawCreditIllustration( + FSlateWindowElementList& OutDrawElements, + int32& LayerId, + const FGeometry& AllottedGeometry, + const FVector2D& Position, + const FVector2D& Size, + int32 IllustrationIndex, + const FLinearColor& AccentColor, + float Alpha) const; };