Started rework of clothing slots and UI

This commit is contained in:
2026-05-26 18:52:37 +03:00
parent 38161ee4a2
commit 878060d7ac
37 changed files with 280 additions and 152 deletions
+1 -1
View File
@@ -77,7 +77,7 @@ public:
FText Name;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly)
UTexture2D* Icon;
TObjectPtr<UTexture2D> Icon;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly)
EClothingSlotType SlotType;
@@ -6,6 +6,7 @@
#include "ClothingItemInstance.h"
#include "GameFramework/Character.h"
#include "Kismet/GameplayStatics.h"
#include "NakedDesire/Player/NakedDesireCharacter.h"
#include "NakedDesire/SaveGame/GlobalSaveGameData.h"
#include "NakedDesire/SaveGame/ItemSaveRecord.h"
#include "NakedDesire/SaveGame/SaveSubsystem.h"
@@ -97,6 +98,37 @@ float UClothingManager::GetHeelHeight()
return 0;
}
USkeletalMeshComponent* UClothingManager::GetMeshComponent(const EClothingSlotType SlotType) const
{
const ANakedDesireCharacter* Player = Cast<ANakedDesireCharacter>(GetOwner());
if (!Player)
{
return nullptr;
}
switch (SlotType)
{
case EClothingSlotType::Nipples: return Player->NipplesMeshComponent;
case EClothingSlotType::Anal: return Player->AnalMeshComponent;
case EClothingSlotType::Vagina: return Player->VaginaMeshComponent;
case EClothingSlotType::Head: return Player->HeadMeshComponent;
case EClothingSlotType::Neck: return Player->NeckMeshComponent;
case EClothingSlotType::Face: return Player->FaceMeshComponent;
case EClothingSlotType::Eyes: return Player->EyesMeshComponent;
case EClothingSlotType::Bodysuit: return Player->BodySuitMeshComponent;
case EClothingSlotType::Top: return Player->TopMeshComponent;
case EClothingSlotType::Bottom: return Player->BottomMeshComponent;
case EClothingSlotType::UnderwearTop: return Player->UnderwearTopMeshComponent;
case EClothingSlotType::UnderwearBottom:return Player->UnderwearBottomMeshComponent;
case EClothingSlotType::Socks: return Player->SocksMeshComponent;
case EClothingSlotType::Footwear: return Player->FootwearMeshComponent;
case EClothingSlotType::Outerwear: return Player->OuterwearMeshComponent;
case EClothingSlotType::WristRestraint: return Player->WristRestraintMeshComponent;
case EClothingSlotType::AnkleRestraint: return Player->AnkleRestraintMeshComponent;
case EClothingSlotType::NeckRestraint: return Player->NeckRestraintMeshComponent;
default: return nullptr;
}
}
void UClothingManager::PutOnClothing(UClothingItemInstance* ClothingItemInstance)
{
if (!ClothingItemInstance)
@@ -107,19 +139,20 @@ void UClothingManager::PutOnClothing(UClothingItemInstance* ClothingItemInstance
FClothingSlotData ClothingSlotData;
GetClothingSlotData(ClothingSlotType, ClothingSlotData);
ClothingSlotData.MeshComponent->SetSkeletalMesh(ClothingItemInstance->GetClothingItem()->SkeletalMesh);
USkeletalMeshComponent* MeshComponent = GetMeshComponent(ClothingSlotType);
MeshComponent->SetSkeletalMesh(ClothingItemInstance->GetClothingItem()->SkeletalMesh);
if (!ClothingItemInstance->GetClothingItem()->Materials.IsEmpty())
{
for (const TPair<FName, UMaterialInstance*>& Material : ClothingItemInstance->GetClothingItem()->Materials)
{
ClothingSlotData.MeshComponent->SetMaterialByName(Material.Key, Material.Value);
MeshComponent->SetMaterialByName(Material.Key, Material.Value);
}
}
SetClothingSlotItem(ClothingSlotType, ClothingItemInstance);
if (ClothingItemInstance->GetClothingItem()->UseLeaderPose)
{
ClothingSlotData.MeshComponent->SetLeaderPoseComponent(Cast<ACharacter>(GetOwner())->GetMesh());
MeshComponent->SetLeaderPoseComponent(Cast<ACharacter>(GetOwner())->GetMesh());
}
UClothingItem* ClothingItem = ClothingItemInstance->GetClothingItem();
@@ -177,13 +210,13 @@ UClothingItemInstance* UClothingManager::RemoveClothing(const EClothingSlotType
UClothingItemInstance* ClothingItemInstance = ClothingSlotData.ClothingItemInstance;
SetClothingSlotItem(ClothingSlotType, nullptr);
USkeletalMeshComponent* MeshComponent = ClothingSlotData.MeshComponent;
USkeletalMeshComponent* MeshComponent = GetMeshComponent(ClothingSlotType);
MeshComponent->SetSkeletalMesh(nullptr);
if (ClothingItemInstance->GetClothingItem()->UseLeaderPose)
{
ClothingSlotData.MeshComponent->SetLeaderPoseComponent(nullptr);
MeshComponent->SetLeaderPoseComponent(nullptr);
}
OnClothingUnequip.Broadcast(ClothingItemInstance);
@@ -27,7 +27,7 @@ public:
UPROPERTY(BlueprintReadWrite, Category = "Clothing Manager|Clothing")
USkeletalMeshComponent* RootMesh = nullptr;
UPROPERTY(BlueprintReadWrite, Category = "Clothing Manager|Clothing")
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Clothing Manager|Clothing")
TArray<FClothingSlotData> ClothingSlots;
UPROPERTY(BlueprintAssignable)
@@ -73,4 +73,7 @@ public:
UFUNCTION(BlueprintPure)
float GetHeelHeight();
private:
USkeletalMeshComponent* GetMeshComponent(EClothingSlotType SlotType) const;
};
+9 -14
View File
@@ -9,37 +9,32 @@
class UClothingItemInstance;
enum class EClothingSlotType : uint8;
/**
*
*/
USTRUCT(BlueprintType)
struct NAKEDDESIRE_API FClothingSlotData
{
GENERATED_BODY()
FClothingSlotData()
: MeshComponent(nullptr), ClothingSlotType(EClothingSlotType::Anal), ClothingItemInstance(nullptr), Name(FText::GetEmpty())
{
}
FClothingSlotData(USkeletalMeshComponent* MeshComponent, const EClothingSlotType ClothingSlotType, UClothingItemInstance* ClothingItemInstance, const FText& Name)
FClothingSlotData(const EClothingSlotType ClothingSlotType, const FText& Name, UTexture2D* Icon = nullptr)
{
this->MeshComponent = MeshComponent;
this->ClothingSlotType = ClothingSlotType;
this->ClothingItemInstance = ClothingItemInstance;
this->Name = Name;
this->Icon = Icon;
}
UPROPERTY(BlueprintReadWrite, Category = "Clothing")
TObjectPtr<USkeletalMeshComponent> MeshComponent = nullptr;
UPROPERTY(BlueprintReadWrite, Category = "Clothing")
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Clothing")
EClothingSlotType ClothingSlotType = EClothingSlotType::Anal;
UPROPERTY(BlueprintReadWrite, Category = "Clothing")
UPROPERTY(BlueprintReadOnly, Category = "Clothing")
TObjectPtr<UClothingItemInstance> ClothingItemInstance = nullptr;
UPROPERTY(BlueprintReadWrite, Category = "Clothing")
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Clothing")
FText Name = FText::GetEmpty();
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Clothing")
TObjectPtr<UTexture2D> Icon = nullptr;
};
@@ -0,0 +1,4 @@
// © 2025 Naked People Team. All Rights Reserved.
#include "ClothingSlotsData.h"
@@ -0,0 +1,34 @@
// © 2025 Naked People Team. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "ClothingSlotType.h"
#include "Engine/DataAsset.h"
#include "ClothingSlotsData.generated.h"
USTRUCT()
struct NAKEDDESIRE_API FClothingSlotData
{
GENERATED_BODY()
FClothingSlotData()
{
}
UPROPERTY(EditDefaultsOnly, Category = "Clothing")
UTexture2D Icon;
UPROPERTY(EditDefaultsOnly, Category = "Clothing")
FText Name;
};
UCLASS()
class NAKEDDESIRE_API UClothingSlotsData : public UPrimaryDataAsset
{
GENERATED_BODY()
public:
UPROPERTY(EditDefaultsOnly, Category = "Clothing")
TMap<EClothingSlotType, FClothingSlotData> ClothingSlotsData;
};
@@ -18,6 +18,7 @@
#include "NakedDesire/Global/Constants.h"
#include "NakedDesire/Global/NakedDesireUserSettings.h"
#include "NakedDesire/UI/RadialMenu/RadialMenuController.h"
#include "NakedDesire/Clothing/ClothingSlotWidgetsInfo.h"
#include "Perception/AIPerceptionStimuliSourceComponent.h"
#include "Perception/AISense_Sight.h"
@@ -163,8 +164,6 @@ void ANakedDesireCharacter::BeginPlay()
StimuliSourceComponent->RegisterForSense(TSubclassOf<UAISense_Sight>());
StimuliSourceComponent->RegisterWithPerceptionSystem();
SetupClothingSlots();
ClothingManager->OnClothingEquip.AddUniqueDynamic(this, &ANakedDesireCharacter::OnClothingEquip);
ClothingManager->OnClothingUnequip.AddUniqueDynamic(this, &ANakedDesireCharacter::OnClothingUnequip);
@@ -368,105 +367,25 @@ void ANakedDesireCharacter::OnCrouchToggle(const FInputActionValue& Value)
void ANakedDesireCharacter::OnEquipmentPress(const FInputActionValue& Value)
{
BuildRadialMenuEntries();
RadialMenuController->OpenMenu();
}
void ANakedDesireCharacter::SetupClothingSlots()
void ANakedDesireCharacter::BuildRadialMenuEntries()
{
#define LOCTEXT_NAMESPACE "ClothingSlots"
TArray<FRadialMenuEntry> Entries;
ClothingManager->ClothingSlots.Add(
FClothingSlotData(
NipplesMeshComponent, EClothingSlotType::Nipples,
nullptr,
LOCTEXT("Nipples", "Nipples")));
ClothingManager->ClothingSlots.Add(
FClothingSlotData(
AnalMeshComponent, EClothingSlotType::Anal,
nullptr,
LOCTEXT("Anal", "Anal")));
ClothingManager->ClothingSlots.Add(
FClothingSlotData(
VaginaMeshComponent, EClothingSlotType::Vagina,
nullptr,
LOCTEXT("Vagina", "Vagina")));
ClothingManager->ClothingSlots.Add(
FClothingSlotData(
HeadMeshComponent, EClothingSlotType::Head,
nullptr,
LOCTEXT("Head", "Head")));
ClothingManager->ClothingSlots.Add(
FClothingSlotData(
NeckMeshComponent, EClothingSlotType::Neck,
nullptr,
LOCTEXT("Neck", "Neck")));
ClothingManager->ClothingSlots.Add(
FClothingSlotData(
FaceMeshComponent, EClothingSlotType::Face,
nullptr,
LOCTEXT("Face", "Face")));
ClothingManager->ClothingSlots.Add(
FClothingSlotData(
EyesMeshComponent, EClothingSlotType::Eyes,
nullptr,
LOCTEXT("Eyes", "Eyes")));
ClothingManager->ClothingSlots.Add(
FClothingSlotData(
BodySuitMeshComponent, EClothingSlotType::Bodysuit,
nullptr,
LOCTEXT("Bodysuit", "Bodysuit")));
ClothingManager->ClothingSlots.Add(
FClothingSlotData(
TopMeshComponent, EClothingSlotType::Top,
nullptr,
LOCTEXT("Top", "Top")));
ClothingManager->ClothingSlots.Add(
FClothingSlotData(
BottomMeshComponent, EClothingSlotType::Bottom,
nullptr,
LOCTEXT("Bottom", "Bottom")));
ClothingManager->ClothingSlots.Add(
FClothingSlotData(
UnderwearTopMeshComponent, EClothingSlotType::UnderwearTop,
nullptr,
LOCTEXT("UnderwearTop", "UnderwearTop")));
ClothingManager->ClothingSlots.Add(
FClothingSlotData(
UnderwearBottomMeshComponent, EClothingSlotType::UnderwearBottom,
nullptr,
LOCTEXT("UnderwearBottom", "UnderwearBottom")));
ClothingManager->ClothingSlots.Add(
FClothingSlotData(
SocksMeshComponent, EClothingSlotType::Socks,
nullptr,
LOCTEXT("Socks", "Socks")));
ClothingManager->ClothingSlots.Add(
FClothingSlotData(
FootwearMeshComponent, EClothingSlotType::Footwear,
nullptr,
LOCTEXT("Footwear", "Footwear")));
ClothingManager->ClothingSlots.Add(
FClothingSlotData(
FootwearMeshComponent, EClothingSlotType::Outerwear,
nullptr,
LOCTEXT("Outerwear", "Outerwear")));
ClothingManager->ClothingSlots.Add(
FClothingSlotData(
FootwearMeshComponent, EClothingSlotType::WristRestraint,
nullptr,
LOCTEXT("WristRestraint", "WristRestraint")));
ClothingManager->ClothingSlots.Add(
FClothingSlotData(
FootwearMeshComponent, EClothingSlotType::AnkleRestraint,
nullptr,
LOCTEXT("AnkleRestraint", "AnkleRestraint")));
ClothingManager->ClothingSlots.Add(
FClothingSlotData(
FootwearMeshComponent, EClothingSlotType::NeckRestraint,
nullptr,
LOCTEXT("NeckRestraint", "NeckRestraint")));
#undef LOCTEXT_NAMESPACE
for (const FClothingSlotData& SlotData : ClothingManager->ClothingSlots)
{
FRadialMenuEntry Entry;
Entry.bEnabled = SlotData.ClothingItemInstance != nullptr;
Entry.DisplayName = SlotData.Name;
Entry.Icon = SlotData.ClothingItemInstance ? SlotData.ClothingItemInstance->GetClothingItem()->Icon : SlotData.Icon;
Entry.ItemId = FName(SlotData.Name.ToString());
Entries.Push(Entry);
}
RadialMenuController->Entries = Entries;
}
void ANakedDesireCharacter::NotifyNoticed(ANPCAIController* NPCController)
@@ -20,6 +20,7 @@ struct FClothingSlotData;
class UInteractionManager;
class UClothingPickerWidget;
class UClothingManager;
class UClothingSlotInfo;
class UStatsManager;
class UMissionsManager;
class ANPCAIController;
@@ -122,6 +123,9 @@ public:
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Censorship")
UStaticMeshComponent* AnalCensorship;
UPROPERTY(EditDefaultsOnly, Category = "Clothing")
TObjectPtr<UClothingSlotInfo> SlotInfo;
// Components
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Clothing")
UClothingManager* ClothingManager;
@@ -201,7 +205,7 @@ private:
void OnCrouchToggle(const FInputActionValue& Value);
void OnEquipmentPress(const FInputActionValue& Value);
void SetupClothingSlots();
void BuildRadialMenuEntries();
bool CheckSight(const FVector& StartLocation, const FVector& EndLocation, FHitResult& HitResult, const AActor* IgnoreActor);
};
@@ -0,0 +1,4 @@
// © 2025 Naked People Team. All Rights Reserved.
#include "EquipmentPanelWidget.h"
@@ -0,0 +1,16 @@
// © 2025 Naked People Team. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "CommonUserWidget.h"
#include "EquipmentPanelWidget.generated.h"
/**
*
*/
UCLASS()
class NAKEDDESIRE_API UEquipmentPanelWidget : public UCommonUserWidget
{
GENERATED_BODY()
};
@@ -0,0 +1,35 @@
// © 2025 Naked People Team. All Rights Reserved.
#include "EquipmentSlotWidget.h"
#include "NakedDesire/Clothing/ClothingItem.h"
#include "NakedDesire/Clothing/ClothingItemInstance.h"
#include "NakedDesire/Clothing/ClothingSlotsData.h"
void UEquipmentSlotWidget::SetItem(UClothingItemInstance* InItem)
{
ClothingItemInstance = InItem;
IconImage->SetBrushFromTexture(InItem->GetClothingItem()->Icon);
}
void UEquipmentSlotWidget::ClearItem()
{
ClothingItemInstance = nullptr;
}
void UEquipmentSlotWidget::NativeOnClicked()
{
Super::NativeOnClicked();
}
void UEquipmentSlotWidget::NativePreConstruct()
{
Super::NativePreConstruct();
if (!ClothingSlotsData->ClothingSlotsData.Contains(SlotType))
return;
FClothingSlotData& SlotData = ClothingSlotsData->ClothingSlotsData[SlotType];
PlaceholderImage->SetBrushFromTexture(&SlotData.Icon);
}
@@ -0,0 +1,43 @@
// © 2025 Naked People Team. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "CommonButtonBase.h"
#include "Components/Image.h"
#include "NakedDesire/Clothing/BodyPart.h"
#include "NakedDesire/Clothing/ClothingSlotType.h"
#include "EquipmentSlotWidget.generated.h"
class UClothingSlotsData;
class UClothingItemInstance;
UCLASS(Abstract)
class NAKEDDESIRE_API UEquipmentSlotWidget : public UCommonButtonBase
{
GENERATED_BODY()
public:
void SetItem(UClothingItemInstance* InItem);
void ClearItem();
protected:
virtual void NativeOnClicked() override;
virtual void NativePreConstruct() override;
UPROPERTY(EditDefaultsOnly)
TObjectPtr<UClothingSlotsData> ClothingSlotsData;
private:
UPROPERTY(meta = (BindWIdget))
TObjectPtr<UImage> PlaceholderImage;
UPROPERTY(meta = (BindWIdget))
TObjectPtr<UImage> IconImage;
UPROPERTY()
TObjectPtr<UClothingItemInstance> ClothingItemInstance = nullptr;
UPROPERTY()
EClothingSlotType SlotType = EClothingSlotType::Anal;
};
@@ -0,0 +1,4 @@
// © 2025 Naked People Team. All Rights Reserved.
#include "InventoryPanelWidget.h"
@@ -0,0 +1,16 @@
// © 2025 Naked People Team. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "CommonUserWidget.h"
#include "InventoryPanelWidget.generated.h"
/**
*
*/
UCLASS()
class NAKEDDESIRE_API UInventoryPanelWidget : public UCommonUserWidget
{
GENERATED_BODY()
};
@@ -0,0 +1 @@
#include "InventoryScreenWidget.h"
@@ -0,0 +1,11 @@
#pragma once
#include "CoreMinimal.h"
#include "CommonActivatableWidget.h"
#include "InventoryScreenWidget.generated.h"
UCLASS()
class NAKEDDESIRE_API UInventoryScreenWidget : public UCommonActivatableWidget
{
GENERATED_BODY()
};
@@ -103,7 +103,6 @@ public:
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Radial Menu|Tuning")
TSubclassOf<URadialMenuWidget> WidgetClass;
protected:
virtual void TickComponent(float DeltaTime, ELevelTick TickType,
FActorComponentTickFunction* ThisTickFunction) override;