Clothing system refactor

This commit is contained in:
2026-05-29 22:13:09 +03:00
parent c6eff4d076
commit cfecd1f4c6
59 changed files with 417 additions and 227 deletions
@@ -1,4 +0,0 @@
// © 2025 Naked People Team. All Rights Reserved.
#include "ClothingItem.h"
@@ -0,0 +1,11 @@
// © 2025 Naked People Team. All Rights Reserved.
#include "ClothingItemDefinition.h"
#include "ClothingItemInstance.h"
TSubclassOf<UItemInstance> UClothingItemDefinition::GetInstanceClass() const
{
return UClothingItemInstance::StaticClass();
}
@@ -6,8 +6,9 @@
#include "BodyPart.h"
#include "ClothingSlotType.h"
#include "Engine/DataAsset.h"
#include "NakedDesire/Items/ItemDefinition.h"
#include "NakedDesire/Progression/ProgressionPath.h"
#include "ClothingItem.generated.h"
#include "ClothingItemDefinition.generated.h"
USTRUCT(BlueprintType)
struct NAKEDDESIRE_API FBodyPartCoverage
@@ -68,17 +69,13 @@ struct NAKEDDESIRE_API FGarmentContainerSlot
};
UCLASS(BlueprintType)
class NAKEDDESIRE_API UClothingItem : public UPrimaryDataAsset
class NAKEDDESIRE_API UClothingItemDefinition : public UItemDefinition
{
GENERATED_BODY()
public:
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly)
FText Name;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly)
TObjectPtr<UTexture2D> Icon;
virtual TSubclassOf<UItemInstance> GetInstanceClass() const override;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly)
EClothingSlotType SlotType;
@@ -88,9 +85,6 @@ public:
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly)
TMap<FName, UMaterialInstance*> Materials;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly)
UStaticMesh* StaticMesh;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly)
int BasePrice;
@@ -109,9 +103,6 @@ public:
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly)
TArray<EBodyPart> CanExpose;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly)
float Coverage = 1.0f;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly)
TArray<FGarmentContainerSlot> ContainerSlots;
@@ -1,27 +1,24 @@
#include "ClothingItemInstance.h"
#include "ClothingItem.h"
#include "NakedDesire/SaveGame/ItemSaveRecord.h"
#include "ClothingItemDefinition.h"
#include "StructUtils/InstancedStruct.h"
void UClothingItemInstance::Init(UClothingItem* InClothingItem)
void UClothingItemInstance::Init(UClothingItemDefinition* InClothingItem)
{
ClothingItem = InClothingItem;
ItemDefinition = InClothingItem;
}
void UClothingItemInstance::Setup(UClothingItem* InClothingItem, const TArray<UItemInstance*>& InStoredItems,
const float InCondition, const FGuid InInstanceId)
void UClothingItemInstance::CaptureState(FInstancedStruct& OutState) const
{
this->ClothingItem = InClothingItem;
this->StoredItems = InStoredItems;
this->Condition = InCondition;
this->InstanceId = InInstanceId;
FClothingInstanceState ClothingState;
ClothingState.Condition = Condition;
OutState.InitializeAs<FClothingInstanceState>(ClothingState);
}
UClothingItemInstance* UClothingItemInstance::CreateFromSave(UObject* Outer, const FItemSaveRecord& ItemSaveRecord)
void UClothingItemInstance::ApplyState(const FInstancedStruct& InState)
{
UClothingItemInstance* NewItemInstance = NewObject<UClothingItemInstance>(Outer);
NewItemInstance->Setup(ItemSaveRecord.Definition.Get(), {}, ItemSaveRecord.Condition, ItemSaveRecord.InstanceId);
return NewItemInstance;
}
if (const FClothingInstanceState* ClothingState = InState.GetPtr<FClothingInstanceState>())
{
Condition = ClothingState->Condition;
}
}
@@ -1,32 +1,39 @@
#pragma once
#include "CoreMinimal.h"
#include "ClothingItemDefinition.h"
#include "NakedDesire/Items/ItemInstance.h"
#include "ClothingItemInstance.generated.h"
struct FItemSaveRecord;
class UClothingItem;
class UClothingItemDefinition;
/** Per-instance mutable state for clothing. */
USTRUCT()
struct FClothingInstanceState : public FItemInstanceState
{
GENERATED_BODY()
UPROPERTY(SaveGame)
float Condition = 1.0f;
};
UCLASS(BlueprintType)
class NAKEDDESIRE_API UClothingItemInstance : public UItemInstance
{
GENERATED_BODY()
public:
UPROPERTY(BlueprintReadOnly, Category = "Clothing Item")
float Condition = 1.0f;
UClothingItem* GetClothingItem() const { return ClothingItem; }
void Init(UClothingItem* InClothingItem);
void Setup(UClothingItem* InClothingItem, const TArray<UItemInstance*>& InStoredItems, float InCondition, FGuid InInstanceId);
static UClothingItemInstance* CreateFromSave(UObject* Outer, const FItemSaveRecord& ItemSaveRecord);
UClothingItemDefinition* GetClothingItemDefinition() const { return Cast<UClothingItemDefinition>(ItemDefinition); }
void Init(UClothingItemDefinition* InClothingItem);
protected:
UPROPERTY(BlueprintReadOnly, Category = "Clothing Item")
TObjectPtr<UClothingItem> ClothingItem;
virtual void CaptureState(FInstancedStruct& OutState) const override;
virtual void ApplyState(const FInstancedStruct& InState) override;
UPROPERTY(BlueprintReadOnly, Category = "Clothing Item")
TArray<TObjectPtr<UItemInstance>> StoredItems;
};
};
+15 -12
View File
@@ -2,7 +2,7 @@
#include "ClothingManager.h"
#include "ClothingItem.h"
#include "ClothingItemDefinition.h"
#include "ClothingItemInstance.h"
#include "GameFramework/Character.h"
#include "Kismet/GameplayStatics.h"
@@ -51,7 +51,7 @@ bool UClothingManager::IsBodyPartExposed(const EBodyPart BodyPart)
{
for (const auto& [Key, Value] : EquippedClothing)
{
if (Value->GetClothingItem()->HiddenBodyParts.Contains(BodyPart))
if (Value->GetClothingItemDefinition()->HiddenBodyParts.Contains(BodyPart))
return false;
}
@@ -63,7 +63,10 @@ void UClothingManager::HydrateClothing()
USaveSubsystem* SaveSubsystem = UGameplayStatics::GetGameInstance(GetWorld())->GetSubsystem<USaveSubsystem>();
for (const FItemSaveRecord& ItemSaveRecord : SaveSubsystem->GetCurrentSave()->GetEquippedItems())
{
UClothingItemInstance* ClothingItemInstance = UClothingItemInstance::CreateFromSave(this, ItemSaveRecord);
UClothingItemInstance* ClothingItemInstance = Cast<UClothingItemInstance>(UItemInstance::CreateFromRecord(this, ItemSaveRecord));
if (!ClothingItemInstance)
continue;
PutOnClothing(ClothingItemInstance);
}
}
@@ -75,7 +78,7 @@ float UClothingManager::GetHeelHeight()
const UClothingItemInstance* Footwear = EquippedClothing[EClothingSlotType::Footwear];
return Footwear->GetClothingItem()->ShoesOffset;
return Footwear->GetClothingItemDefinition()->ShoesOffset;
}
USkeletalMeshComponent* UClothingManager::GetMeshComponent(const EClothingSlotType SlotType) const
@@ -135,25 +138,25 @@ void UClothingManager::PutOnClothing(UClothingItemInstance* ClothingItemInstance
if (!ClothingItemInstance)
return;
const EClothingSlotType ClothingSlotType = ClothingItemInstance->GetClothingItem()->SlotType;
const EClothingSlotType ClothingSlotType = ClothingItemInstance->GetClothingItemDefinition()->SlotType;
USkeletalMeshComponent* MeshComponent = GetMeshComponent(ClothingSlotType);
MeshComponent->SetSkeletalMesh(ClothingItemInstance->GetClothingItem()->SkeletalMesh);
if (!ClothingItemInstance->GetClothingItem()->Materials.IsEmpty())
MeshComponent->SetSkeletalMesh(ClothingItemInstance->GetClothingItemDefinition()->SkeletalMesh);
if (!ClothingItemInstance->GetClothingItemDefinition()->Materials.IsEmpty())
{
for (const TPair<FName, UMaterialInstance*>& Material : ClothingItemInstance->GetClothingItem()->Materials)
for (const TPair<FName, UMaterialInstance*>& Material : ClothingItemInstance->GetClothingItemDefinition()->Materials)
{
MeshComponent->SetMaterialByName(Material.Key, Material.Value);
}
}
SetClothingSlotItem(ClothingSlotType, ClothingItemInstance);
if (ClothingItemInstance->GetClothingItem()->UseLeaderPose)
if (ClothingItemInstance->GetClothingItemDefinition()->UseLeaderPose)
{
MeshComponent->SetLeaderPoseComponent(Cast<ACharacter>(GetOwner())->GetMesh());
}
const UClothingItem* ClothingItem = ClothingItemInstance->GetClothingItem();
const UClothingItemDefinition* ClothingItem = ClothingItemInstance->GetClothingItemDefinition();
if (ClothingItem->SlotType == EClothingSlotType::Bodysuit)
{
DropClothing(EClothingSlotType::Top);
@@ -174,7 +177,7 @@ void UClothingManager::PutOnClothing(UClothingItemInstance* ClothingItemInstance
void UClothingManager::TakeClothing(UClothingItemInstance* ClothingItemInstance)
{
const EClothingSlotType SlotType = ClothingItemInstance->GetClothingItem()->SlotType;
const EClothingSlotType SlotType = ClothingItemInstance->GetClothingItemDefinition()->SlotType;
if (EquippedClothing.Contains(SlotType))
{
DropClothing(SlotType);
@@ -200,7 +203,7 @@ UClothingItemInstance* UClothingManager::RemoveClothing(const EClothingSlotType
USkeletalMeshComponent* MeshComponent = GetMeshComponent(ClothingSlotType);
MeshComponent->SetSkeletalMesh(nullptr);
if (ExistingItem->GetClothingItem()->UseLeaderPose)
if (ExistingItem->GetClothingItemDefinition()->UseLeaderPose)
{
MeshComponent->SetLeaderPoseComponent(nullptr);
}