added commission tracker to HUD

This commit is contained in:
2026-06-07 20:14:03 +03:00
parent 61d5a57d8d
commit 96392cd451
43 changed files with 199 additions and 38 deletions
+1 -1
View File
@@ -1,7 +1,7 @@
[/Script/EngineSettings.GameMapsSettings]
EditorStartupMap=/Game/Test/Maps/L_Test.L_Test
EditorStartupMap=/Game/Test/Maps/L_TestCity.L_TestCity
LocalMapOptions=
TransitionMap=None
bUseSplitscreen=True
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+4
View File
@@ -121,6 +121,10 @@
{
"Name": "VisualStudioCodeSourceCodeAccess",
"Enabled": false
},
{
"Name": "PCG",
"Enabled": true
}
]
}
+1 -1
View File
@@ -18,7 +18,7 @@ A pass over the actual tree on 2026-05-30 shows the code is **well ahead** of §
- **Save — rearchitected, the "empty save" bug is gone.** `UGlobalSaveGameData` is now the durable state container with granular `Add/Update/Remove` for `WardrobeItems`/`EquippedItems`/`WorldItems` (all `FItemSaveRecord`). `USaveSubsystem::GetCurrentSave` creates + seeds a new save from `StartingSaveData`. `GameMode::BeginPlay` rehydrates world `AItemPickup`s from records with transforms. Round-trip architecture is in place.
- **Phase 2 — done.** `Interactables/ItemPickup` is the world item actor; drop/pickup + interaction (`Interaction/InteractionComponent`) exist.
- **Phase 3 — done.** `SessionManagerSubsystem` + `SessionLossResolver` as previously noted.
- **New since snapshot:** `Censorship/CensorshipComponent`, `Clothing/ClothingVisualsComponent` (runtime per-slot mesh spawning) + `ClothingSlotsData`, GASP-style movement enums (`Global/Gait|Stance|AirMode|MovementState`), and a full **UI layer** (`UI/HUDWidget`, `GameLayoutWidget`, `Inventory/*` panels, **`RadialMenu/*` = the §14.1 quick-action menu**).
- **New since snapshot:** `Censorship/CensorshipComponent`, `Clothing/ClothingVisualsComponent` (runtime per-slot mesh spawning) + `ClothingSlotsData`, GASP-style movement enums (`Global/Gait|Stance|AirMode|MovementState`), and a full **UI layer** (`UI/HUDWidget`, `UI/HUDCommissionTrackerWidget` = on-screen active-commission tracker, `GameLayoutWidget`, `Inventory/*` panels, **`RadialMenu/*` = the §14.1 quick-action menu**).
- **Minor spec drift to fix when convenient:** `ERestrictionType` spells `BlockPhoneUse` as `BlockPhoneUsage` and splits the GDD's single `BlockExposeAction` into `BlockBoobsExpose`/`BlockVaginaExpose`/`BlockAnalExpose`. `FClothingRestriction` fields lack `EditDefaultsOnly` so they aren't author-editable in the asset. Not blocking the slice.
§1 should be rewritten in a later pass; until then, trust §0.1 over §1 where they disagree.
@@ -198,7 +198,7 @@ ENPCType UNPCDirectorSubsystem::PickWeightedClass() const
return ENPCType::None;
}
bool UNPCDirectorSubsystem::FindSpawnPoint(const FVector& Around, FVector& OutLocation) const
bool UNPCDirectorSubsystem::FindSpawnPoint(const FVector& Around, FVector& OutLocation)
{
UNavigationSystemV1* NavSys = UNavigationSystemV1::GetCurrent(GetWorld());
const UNPCDirectorConfig* Config = GetConfig();
@@ -214,7 +214,9 @@ bool UNPCDirectorSubsystem::FindSpawnPoint(const FVector& Around, FVector& OutLo
if (NavSys->GetRandomReachablePointInRadius(Around, Config->SpawnRadiusMax, NavLocation) &&
FVector::DistSquared(NavLocation.Location, Around) >= MinRadiusSq)
{
OutLocation = NavLocation.Location;
FVector SpawnLocation = NavLocation.Location;
CorrectSpawnLocation(SpawnLocation);
OutLocation = SpawnLocation;
return true;
}
}
@@ -227,6 +229,11 @@ TSubclassOf<ANPC> UNPCDirectorSubsystem::GetRandomNPCClass(TArray<TSubclassOf<AN
return InNPCClasses[RandomIndex];
}
void UNPCDirectorSubsystem::CorrectSpawnLocation(FVector& OutVector)
{
OutVector.Z += 50.0f;
}
APawn* UNPCDirectorSubsystem::GetPlayerPawn() const
{
return UGameplayStatics::GetPlayerPawn(GetWorld(), 0);
@@ -42,8 +42,9 @@ private:
ANPC* TakeFromPool();
void ReturnToPool(ANPC* NPC);
ENPCType PickWeightedClass() const;
bool FindSpawnPoint(const FVector& Around, FVector& OutLocation) const;
bool FindSpawnPoint(const FVector& Around, FVector& OutLocation);
TSubclassOf<ANPC> GetRandomNPCClass(TArray<TSubclassOf<ANPC>> InNPCClasses);
void CorrectSpawnLocation(FVector& OutVector);
APawn* GetPlayerPawn() const;
UNPCDirectorConfig* GetConfig() const;
@@ -0,0 +1,61 @@
// © 2025 Naked People Team. All Rights Reserved.
#include "HUDCommissionTrackerWidget.h"
#include "Components/PanelWidget.h"
#include "NakedDesire/Commissions/Commission.h"
#include "NakedDesire/Commissions/MissionSubsystem.h"
#include "NakedDesire/UI/Phone/Apps/ForumCommissionWidget.h"
void UHUDCommissionTrackerWidget::NativeConstruct()
{
Super::NativeConstruct();
if (UMissionSubsystem* Missions = GetMissionSubsystem())
Missions->OnBoardChanged.AddUniqueDynamic(this, &UHUDCommissionTrackerWidget::Rebuild);
Rebuild();
}
void UHUDCommissionTrackerWidget::NativeDestruct()
{
if (UMissionSubsystem* Missions = GetMissionSubsystem())
Missions->OnBoardChanged.RemoveDynamic(this, &UHUDCommissionTrackerWidget::Rebuild);
Super::NativeDestruct();
}
void UHUDCommissionTrackerWidget::Rebuild()
{
if (!CommissionContainer || !CommissionEntryClass)
return;
CommissionContainer->ClearChildren();
UMissionSubsystem* Missions = GetMissionSubsystem();
if (!Missions)
return;
// Only accepted commissions are "active" — offered / completed / expired stay on the forum.
for (UCommission* Commission : Missions->GetAcceptedCommissions())
{
if (!Commission)
continue;
UForumCommissionWidget* Entry = CreateWidget<UForumCommissionWidget>(this, CommissionEntryClass);
if (!Entry)
continue;
// SetCommission collapses the accept control and keeps the row's objective progress polling live;
// the HUD row BP simply omits the accept / abandon buttons (both optional binds).
Entry->SetCommission(Commission);
CommissionContainer->AddChild(Entry);
}
}
UMissionSubsystem* UHUDCommissionTrackerWidget::GetMissionSubsystem() const
{
const UWorld* World = GetWorld();
return World ? World->GetSubsystem<UMissionSubsystem>() : nullptr;
}
@@ -0,0 +1,39 @@
// © 2025 Naked People Team. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "CommonUserWidget.h"
#include "HUDCommissionTrackerWidget.generated.h"
class UPanelWidget;
class UForumCommissionWidget;
class UMissionSubsystem;
// On-screen tracker for accepted commissions (GDD §13 / Phase 7 objective-tracker), so the player can
// read their active objectives during play without opening the phone forum. Mirrors the forum's accepted
// section: reads the live board from UMissionSubsystem, rebuilds on OnBoardChanged, and reuses the forum
// row class for per-objective progress polling. Display-only — no accept / abandon controls.
UCLASS(Abstract)
class NAKEDDESIRE_API UHUDCommissionTrackerWidget : public UCommonUserWidget
{
GENERATED_BODY()
protected:
virtual void NativeConstruct() override;
virtual void NativeDestruct() override;
private:
// Holds one row per accepted commission.
UPROPERTY(meta = (BindWidget))
TObjectPtr<UPanelWidget> CommissionContainer;
UPROPERTY(EditDefaultsOnly, Category = "HUD")
TSubclassOf<UForumCommissionWidget> CommissionEntryClass;
// Bound to UMissionSubsystem::OnBoardChanged (a dynamic delegate, hence UFUNCTION).
UFUNCTION()
void Rebuild();
UMissionSubsystem* GetMissionSubsystem() const;
};
@@ -48,7 +48,8 @@ private:
UPROPERTY(meta = (BindWidget))
TObjectPtr<UCommonTextBlock> DescriptionText;
UPROPERTY(meta = (BindWidget))
// Optional so display-only hosts (e.g. the HUD commission tracker) can reuse this row without a button.
UPROPERTY(meta = (BindWidgetOptional))
TObjectPtr<UButton> AcceptButton;
UPROPERTY(meta = (BindWidgetOptional))