Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions game/RacingGame/Source/RacingGame/Game/RaceGameMode.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include "RaceMenuWidget.h"
#include "RaceSelectWidget.h"
#include "RaceSelectData.h"
#include "RaceResultsWidget.h"
#include "RaceHudModel.h"
#include "RaceHudWidget.h"
#include "RaceTrack.h"
Expand Down Expand Up @@ -256,6 +257,7 @@ void ARaceGameMode::EnterDefaultRaceState()
}
bInputReleased = false;
bLoggedCountdown = false;
bResultsShown = false;
TWeakObjectPtr<ARaceManager> WeakManager(Manager);
GetWorldTimerManager().SetTimer(GreenPollHandle, [this, WeakManager]()
{
Expand Down Expand Up @@ -285,6 +287,30 @@ void ARaceGameMode::EnterDefaultRaceState()
}
}, 0.1f, true);

// Results screen appears once the manager finalizes results, even if
// parked participants never finish: the table covers finishers only
// by manager design, so headless runs still exercise this path.
TWeakObjectPtr<ARaceManager> WeakResultsManager(Manager);
GetWorldTimerManager().SetTimer(ResultsPollHandle, [this, WeakResultsManager]()
{
if (bResultsShown || !WeakResultsManager.IsValid() || !WeakResultsManager->HasResults())
{
return;
}
bResultsShown = true;
if (HudWidget)
{
HudWidget->RemoveFromParent();
}
if (URaceResultsWidget* Results = CreateWidget<URaceResultsWidget>(GetWorld(), URaceResultsWidget::StaticClass()))
{
ResultsWidget = Results;
ResultsWidget->AddToViewport(200);
ResultsWidget->ShowResults(WeakResultsManager.Get());
}
GetWorldTimerManager().ClearTimer(ResultsPollHandle);
}, 0.5f, true);

if (APlayerController* PC = UGameplayStatics::GetPlayerController(GetWorld(), 0))
{
PC->SetInputMode(FInputModeGameOnly());
Expand Down
8 changes: 8 additions & 0 deletions game/RacingGame/Source/RacingGame/Game/RaceGameMode.h
Original file line number Diff line number Diff line change
Expand Up @@ -67,4 +67,12 @@ class RACINGGAME_API ARaceGameMode : public AGameModeBase
FTimerHandle GreenPollHandle;
bool bInputReleased = false;
bool bLoggedCountdown = false;

// Results screen (PR5): shown once when the manager finalizes results.
// Display-only; restart flow belongs to PR6.
UPROPERTY()
TObjectPtr<class URaceResultsWidget> ResultsWidget = nullptr;

FTimerHandle ResultsPollHandle;
bool bResultsShown = false;
};
71 changes: 71 additions & 0 deletions game/RacingGame/Source/RacingGame/Game/RaceResultsWidget.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
// See header.

#include "RaceResultsWidget.h"
#include "RaceManager.h"
#include "Blueprint/WidgetTree.h"
#include "Components/CanvasPanel.h"
#include "Components/CanvasPanelSlot.h"
#include "Components/TextBlock.h"
#include "Components/VerticalBox.h"
#include "Components/VerticalBoxSlot.h"

TSharedRef<SWidget> URaceResultsWidget::RebuildWidget()
{
if (WidgetTree->RootWidget == nullptr)
{
BuildResultsTree();
}
return Super::RebuildWidget();
}

void URaceResultsWidget::BuildResultsTree()
{
UCanvasPanel* Root = WidgetTree->ConstructWidget<UCanvasPanel>(UCanvasPanel::StaticClass());
WidgetTree->RootWidget = Root;

UVerticalBox* Box = WidgetTree->ConstructWidget<UVerticalBox>(UVerticalBox::StaticClass());
if (UCanvasPanelSlot* RootSlot = Root->AddChildToCanvas(Box))
{
RootSlot->SetAnchors(FAnchors(0.5f, 0.5f, 0.5f, 0.5f));
RootSlot->SetAlignment(FVector2D(0.5f, 0.5f));
RootSlot->SetAutoSize(true);
}

UTextBlock* Title = WidgetTree->ConstructWidget<UTextBlock>(UTextBlock::StaticClass());
Title->SetText(FText::FromString(TEXT("RESULTS")));
Title->SetJustification(ETextJustify::Center);
if (UVerticalBoxSlot* TitleSlot = Box->AddChildToVerticalBox(Title))
{
TitleSlot->SetPadding(FMargin(0.0f, 0.0f, 0.0f, 24.0f));
}

RowsBox = WidgetTree->ConstructWidget<UVerticalBox>(UVerticalBox::StaticClass());
Box->AddChildToVerticalBox(RowsBox);

UE_LOG(LogTemp, Display, TEXT("RACEGAME: results widget constructed"));
}

void URaceResultsWidget::ShowResults(ARaceManager* Manager)
{
if (!Manager || !Manager->HasResults() || !RowsBox)
{
return;
}
RowsBox->ClearChildren();
const FRaceResults& Snapshot = Manager->GetResults();
int32 Place = 0;
for (const FRaceResultEntry& E : Snapshot.Ordered)
{
++Place;
const FString Line = FString::Printf(TEXT("%d. P%d%s - %d laps - best %.2f s"),
Place, E.ParticipantIndex + 1,
E.ParticipantIndex == 0 ? TEXT(" (YOU)") : TEXT(""),
E.CompletedLaps, E.BestLapTime);
UTextBlock* Row = WidgetTree->ConstructWidget<UTextBlock>(UTextBlock::StaticClass());
Row->SetText(FText::FromString(Line));
Row->SetJustification(ETextJustify::Center);
RowsBox->AddChildToVerticalBox(Row);
UE_LOG(LogTemp, Display, TEXT("RACEGAME: results row %s"), *Line);
}
UE_LOG(LogTemp, Display, TEXT("RACEGAME: results shown entries=%d"), Snapshot.Ordered.Num());
}
37 changes: 37 additions & 0 deletions game/RacingGame/Source/RacingGame/Game/RaceResultsWidget.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// Production results screen for the vertical slice (PR5).
// Pure C++ UMG: no editor assets. Display-only; restart flow belongs to
// PR6, so this screen has no buttons. Rows are built from the manager's
// finish snapshot at show time, which is immutable by then.

#pragma once

#include "CoreMinimal.h"
#include "Blueprint/UserWidget.h"
#include "RaceResultsWidget.generated.h"

class ARaceManager;
class UTextBlock;
class UVerticalBox;
class SWidget;

UCLASS()
class RACINGGAME_API URaceResultsWidget : public UUserWidget
{
GENERATED_BODY()

public:
// Builds the static tree on first slate build (same engine constraint
// as the other production widgets: the root must exist before the
// first build resolves it).
virtual TSharedRef<SWidget> RebuildWidget() override;

// Fills the rows from the manager snapshot. Safe to call once the
// manager reports HasResults; entries never change afterward.
void ShowResults(ARaceManager* Manager);

private:
void BuildResultsTree();

UPROPERTY()
TObjectPtr<UVerticalBox> RowsBox = nullptr;
};
Loading