Clothing system refactor
This commit is contained in:
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.
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.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
@@ -42,6 +42,10 @@
|
||||
"Win64"
|
||||
],
|
||||
"MarketplaceURL": "com.epicgames.launcher://ue/marketplace/product/362651520df94e4fa65492dbcba44ae2"
|
||||
},
|
||||
{
|
||||
"Name": "StructUtils",
|
||||
"Enabled": true
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
+5
-14
@@ -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;
|
||||
};
|
||||
};
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
#include "NakedDesireGameMode.h"
|
||||
#include "Kismet/GameplayStatics.h"
|
||||
#include "NakedDesire/Clothing/ClothingItem.h"
|
||||
#include "NakedDesire/Clothing/ClothingItemDefinition.h"
|
||||
#include "NakedDesire/Clothing/ClothingItemInstance.h"
|
||||
#include "NakedDesire/Interactables/ItemPickup.h"
|
||||
#include "UObject/ConstructorHelpers.h"
|
||||
@@ -34,10 +34,10 @@ void ANakedDesireGameMode::BuyItem(UClothingItemInstance* ClothingItemInstance)
|
||||
return;
|
||||
}
|
||||
|
||||
if (SaveGame->Money < ClothingItemInstance->GetClothingItem()->BasePrice)
|
||||
if (SaveGame->Money < ClothingItemInstance->GetClothingItemDefinition()->BasePrice)
|
||||
return;
|
||||
|
||||
SaveGame->Money -= ClothingItemInstance->GetClothingItem()->BasePrice;
|
||||
SaveGame->Money -= ClothingItemInstance->GetClothingItemDefinition()->BasePrice;
|
||||
Wardrobe->AddItem(ClothingItemInstance);
|
||||
}
|
||||
|
||||
@@ -71,12 +71,12 @@ void ANakedDesireGameMode::BeginPlay()
|
||||
}
|
||||
|
||||
USaveSubsystem* SaveSubsystem = UGameplayStatics::GetGameInstance(GetWorld())->GetSubsystem<USaveSubsystem>();
|
||||
for (const auto& Item : SaveSubsystem->GetCurrentSave()->GetWorldItems())
|
||||
for (const FItemSaveRecord& Item : SaveSubsystem->GetCurrentSave()->GetWorldItems())
|
||||
{
|
||||
UClothingItemInstance* NewItemInstance = NewObject<UClothingItemInstance>(this);
|
||||
NewItemInstance->Init(Item.Definition.Get());
|
||||
NewItemInstance->Condition = Item.Condition;
|
||||
NewItemInstance->SetInstanceId(Item.InstanceId);
|
||||
UClothingItemInstance* NewItemInstance = Cast<UClothingItemInstance>(UItemInstance::CreateFromRecord(this, Item));
|
||||
if (!NewItemInstance)
|
||||
continue;
|
||||
|
||||
AItemPickup* NewItemPickup = GetWorld()->SpawnActor<AItemPickup>(ItemPickupClass, Item.WorldTransform);
|
||||
NewItemPickup->SetItem(NewItemInstance);
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
#include "Components/BoxComponent.h"
|
||||
#include "Components/WidgetComponent.h"
|
||||
#include "Kismet/GameplayStatics.h"
|
||||
#include "NakedDesire/Clothing/ClothingItem.h"
|
||||
#include "NakedDesire/Clothing/ClothingItemDefinition.h"
|
||||
#include "NakedDesire/Clothing/ClothingItemInstance.h"
|
||||
#include "NakedDesire/Clothing/ClothingManager.h"
|
||||
#include "NakedDesire/Player/NakedDesireCharacter.h"
|
||||
@@ -71,7 +71,14 @@ void AItemPickup::SetItem(UClothingItemInstance* InItem)
|
||||
{
|
||||
ClothingItemInstance = InItem;
|
||||
|
||||
Mesh->SetStaticMesh(InItem->GetClothingItem()->StaticMesh);
|
||||
Mesh->SetStaticMesh(InItem->GetClothingItemDefinition()->StaticMesh);
|
||||
if (!InItem->GetClothingItemDefinition()->Materials.IsEmpty())
|
||||
{
|
||||
for (const TPair<FName, UMaterialInstance*>& Material : InItem->GetClothingItemDefinition()->Materials)
|
||||
{
|
||||
Mesh->SetMaterialByName(Material.Key, Material.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AItemPickup::ApplyOutline(UStaticMeshComponent* InMesh, bool bEnabled, int32 StencilValue)
|
||||
|
||||
@@ -25,7 +25,6 @@ public:
|
||||
virtual void ShowInteractionFocusHint_Implementation() override;
|
||||
virtual void ShowInteractionProximityHint_Implementation() override;
|
||||
|
||||
|
||||
void SetItem(UClothingItemInstance* InItem);
|
||||
UClothingItemInstance* GetItem() const { return ClothingItemInstance; }
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
#include "Wardrobe.h"
|
||||
#include "Kismet/GameplayStatics.h"
|
||||
#include "NakedDesire/Clothing/ClothingItemInstance.h"
|
||||
#include "NakedDesire/SaveGame/GlobalSaveGameData.h"
|
||||
#include "NakedDesire/SaveGame/ItemSaveRecord.h"
|
||||
#include "NakedDesire/SaveGame/SaveSubsystem.h"
|
||||
@@ -29,7 +30,10 @@ void AWardrobe::BeginPlay()
|
||||
|
||||
for (const FItemSaveRecord& ItemSaveRecord : SaveGame->GetWardrobeItems())
|
||||
{
|
||||
UClothingItemInstance* NewItemInstance = UClothingItemInstance::CreateFromSave(this, ItemSaveRecord);
|
||||
UClothingItemInstance* NewItemInstance = Cast<UClothingItemInstance>(UItemInstance::CreateFromRecord(this, ItemSaveRecord));
|
||||
if (!NewItemInstance)
|
||||
continue;
|
||||
|
||||
ClothingItems.Push(NewItemInstance);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
// © 2025 Naked People Team. All Rights Reserved.
|
||||
|
||||
|
||||
#include "ItemDefinition.h"
|
||||
|
||||
#include "ItemInstance.h"
|
||||
|
||||
UItemInstance* UItemDefinition::CreateInstance(UObject* Outer) const
|
||||
{
|
||||
const TSubclassOf<UItemInstance> InstanceClass = GetInstanceClass();
|
||||
if (!InstanceClass)
|
||||
return nullptr;
|
||||
|
||||
UItemInstance* Instance = NewObject<UItemInstance>(Outer, InstanceClass);
|
||||
Instance->SetItemDefinition(const_cast<UItemDefinition*>(this));
|
||||
return Instance;
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
// © 2025 Naked People Team. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Engine/DataAsset.h"
|
||||
#include "Templates/SubclassOf.h"
|
||||
#include "ItemDefinition.generated.h"
|
||||
|
||||
class UItemInstance;
|
||||
|
||||
UCLASS(Abstract)
|
||||
class NAKEDDESIRE_API UItemDefinition : public UPrimaryDataAsset
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
/** The UItemInstance subclass that runtime instances of this definition use. */
|
||||
virtual TSubclassOf<UItemInstance> GetInstanceClass() const PURE_VIRTUAL(UItemDefinition::GetInstanceClass, return nullptr;);
|
||||
|
||||
/** Mint a fresh runtime instance of this definition (new GUID, default per-type state). */
|
||||
UItemInstance* CreateInstance(UObject* Outer) const;
|
||||
|
||||
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly)
|
||||
FText Name;
|
||||
|
||||
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly)
|
||||
TObjectPtr<UTexture2D> Icon;
|
||||
|
||||
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly)
|
||||
UStaticMesh* StaticMesh;
|
||||
};
|
||||
@@ -1,9 +1,13 @@
|
||||
#include "ItemInstance.h"
|
||||
|
||||
#include "ItemDefinition.h"
|
||||
#include "StructUtils/InstancedStruct.h"
|
||||
#include "NakedDesire/SaveGame/ItemSaveRecord.h"
|
||||
|
||||
void UItemInstance::PostInitProperties()
|
||||
{
|
||||
Super::PostInitProperties();
|
||||
|
||||
|
||||
if (HasAnyFlags(RF_ClassDefaultObject | RF_ArchetypeObject | RF_NeedLoad))
|
||||
return;
|
||||
if (!InstanceId.IsValid())
|
||||
@@ -20,3 +24,37 @@ void UItemInstance::SetInstanceId(FGuid InId)
|
||||
{
|
||||
InstanceId = InId;
|
||||
}
|
||||
|
||||
FItemSaveRecord UItemInstance::ToSaveRecord() const
|
||||
{
|
||||
FItemSaveRecord Record;
|
||||
Record.InstanceId = InstanceId;
|
||||
Record.Definition = ItemDefinition;
|
||||
CaptureState(Record.State);
|
||||
return Record;
|
||||
}
|
||||
|
||||
UItemInstance* UItemInstance::CreateFromRecord(UObject* Outer, const FItemSaveRecord& Record)
|
||||
{
|
||||
UItemDefinition* Definition = Record.Definition.LoadSynchronous();
|
||||
if (!Definition)
|
||||
{
|
||||
UE_LOG(LogTemp, Warning, TEXT("UItemInstance::CreateFromRecord: failed to load definition for instance %s"),
|
||||
*Record.InstanceId.ToString());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const TSubclassOf<UItemInstance> InstanceClass = Definition->GetInstanceClass();
|
||||
if (!InstanceClass)
|
||||
{
|
||||
UE_LOG(LogTemp, Warning, TEXT("UItemInstance::CreateFromRecord: %s returned no instance class"),
|
||||
*Definition->GetName());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
UItemInstance* Instance = NewObject<UItemInstance>(Outer, InstanceClass);
|
||||
Instance->ItemDefinition = Definition;
|
||||
Instance->InstanceId = Record.InstanceId.IsValid() ? Record.InstanceId : FGuid::NewGuid();
|
||||
Instance->ApplyState(Record.State);
|
||||
return Instance;
|
||||
}
|
||||
@@ -4,19 +4,55 @@
|
||||
#include "UObject/Object.h"
|
||||
#include "ItemInstance.generated.h"
|
||||
|
||||
class UItemDefinition;
|
||||
struct FItemSaveRecord;
|
||||
struct FInstancedStruct;
|
||||
|
||||
/**
|
||||
* Base for per-instance mutable state carried inside FItemSaveRecord::State.
|
||||
* Subclass per item type (see FClothingInstanceState, FSexToyInstanceState).
|
||||
*/
|
||||
USTRUCT()
|
||||
struct NAKEDDESIRE_API FItemInstanceState
|
||||
{
|
||||
GENERATED_BODY()
|
||||
};
|
||||
|
||||
UCLASS(Abstract)
|
||||
class NAKEDDESIRE_API UItemInstance : public UObject
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
|
||||
public:
|
||||
virtual void PostInitProperties() override;
|
||||
virtual void PostDuplicate(EDuplicateMode::Type DuplicateMode) override;
|
||||
|
||||
FGuid GetInstanceId() const { return InstanceId; }
|
||||
void SetInstanceId(FGuid InId);
|
||||
|
||||
|
||||
UItemDefinition* GetItemDefinition() const { return ItemDefinition; }
|
||||
void SetItemDefinition(UItemDefinition* InDefinition) { ItemDefinition = InDefinition; }
|
||||
|
||||
/** Serialize identity + definition + per-type state into a record. */
|
||||
FItemSaveRecord ToSaveRecord() const;
|
||||
|
||||
/**
|
||||
* Reconstruct the correct UItemInstance subclass from a saved record,
|
||||
* driven by the definition's GetInstanceClass(). Returns nullptr if the
|
||||
* definition fails to load.
|
||||
*/
|
||||
static UItemInstance* CreateFromRecord(UObject* Outer, const FItemSaveRecord& Record);
|
||||
|
||||
protected:
|
||||
/** Override to pack this type's mutable state into OutState. */
|
||||
virtual void CaptureState(FInstancedStruct& OutState) const {}
|
||||
|
||||
/** Override to restore this type's mutable state from InState. */
|
||||
virtual void ApplyState(const FInstancedStruct& InState) {}
|
||||
|
||||
UPROPERTY(VisibleAnywhere, SaveGame, BlueprintReadOnly, Category = "Item", meta = (AllowPrivateAccess = "true"))
|
||||
FGuid InstanceId;
|
||||
};
|
||||
|
||||
UPROPERTY()
|
||||
TObjectPtr<UItemDefinition> ItemDefinition;
|
||||
};
|
||||
@@ -1 +1,20 @@
|
||||
#include "SexToyInstance.h"
|
||||
|
||||
#include "StructUtils/InstancedStruct.h"
|
||||
|
||||
void USexToyInstance::CaptureState(FInstancedStruct& OutState) const
|
||||
{
|
||||
FSexToyInstanceState State;
|
||||
State.bActive = bActive;
|
||||
State.Battery = Battery;
|
||||
OutState.InitializeAs<FSexToyInstanceState>(State);
|
||||
}
|
||||
|
||||
void USexToyInstance::ApplyState(const FInstancedStruct& InState)
|
||||
{
|
||||
if (const FSexToyInstanceState* State = InState.GetPtr<FSexToyInstanceState>())
|
||||
{
|
||||
bActive = State->bActive;
|
||||
Battery = State->Battery;
|
||||
}
|
||||
}
|
||||
@@ -2,19 +2,37 @@
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "ItemInstance.h"
|
||||
#include "SexToyItem.h"
|
||||
#include "SexToyInstance.generated.h"
|
||||
|
||||
class USexToyItem;
|
||||
/** Per-instance mutable state for sex toys. */
|
||||
USTRUCT()
|
||||
struct FSexToyInstanceState : public FItemInstanceState
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
UPROPERTY(SaveGame)
|
||||
bool bActive = false;
|
||||
|
||||
UPROPERTY(SaveGame)
|
||||
float Battery = 1.0f;
|
||||
};
|
||||
|
||||
UCLASS()
|
||||
class NAKEDDESIRE_API USexToyInstance : public UItemInstance
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
|
||||
public:
|
||||
USexToyItem* GetSexToyItem() const { return SexToyItem; }
|
||||
|
||||
private:
|
||||
UPROPERTY()
|
||||
TObjectPtr<USexToyItem> SexToyItem;
|
||||
};
|
||||
USexToyItem* GetSexToyItem() const { return Cast<USexToyItem>(ItemDefinition); }
|
||||
|
||||
protected:
|
||||
virtual void CaptureState(FInstancedStruct& OutState) const override;
|
||||
virtual void ApplyState(const FInstancedStruct& InState) override;
|
||||
|
||||
UPROPERTY(BlueprintReadOnly, Category = "Sex Toy")
|
||||
bool bActive = false;
|
||||
|
||||
UPROPERTY(BlueprintReadOnly, Category = "Sex Toy")
|
||||
float Battery = 1.0f;
|
||||
};
|
||||
@@ -1 +1,8 @@
|
||||
#include "SexToyItem.h"
|
||||
|
||||
#include "SexToyInstance.h"
|
||||
|
||||
TSubclassOf<UItemInstance> USexToyItem::GetInstanceClass() const
|
||||
{
|
||||
return USexToyInstance::StaticClass();
|
||||
}
|
||||
@@ -1,16 +1,18 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Engine/DataAsset.h"
|
||||
#include "NakedDesire/Clothing/ClothingSlotType.h"
|
||||
#include "NakedDesire/Items/ItemDefinition.h"
|
||||
#include "SexToyItem.generated.h"
|
||||
|
||||
UCLASS()
|
||||
class NAKEDDESIRE_API USexToyItem : public UPrimaryDataAsset
|
||||
class NAKEDDESIRE_API USexToyItem : public UItemDefinition
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
|
||||
public:
|
||||
virtual TSubclassOf<UItemInstance> GetInstanceClass() const override;
|
||||
|
||||
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Sex Toy")
|
||||
float LustModifier = 0.0f;
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
|
||||
#include "EquipClothingRestriction.h"
|
||||
#include "NakedDesire/Clothing/ClothingItem.h"
|
||||
#include "NakedDesire/Clothing/ClothingItemDefinition.h"
|
||||
#include "NakedDesire/Clothing/ClothingItemInstance.h"
|
||||
#include "NakedDesire/Clothing/ClothingManager.h"
|
||||
#include "NakedDesire/Player/NakedDesireCharacter.h"
|
||||
@@ -65,9 +65,9 @@ FText UEquipClothingRestriction::GetDescription() const
|
||||
|
||||
void UEquipClothingRestriction::OnClothingEquipped(UClothingItemInstance* ClothingItemInstance)
|
||||
{
|
||||
const bool IsTargetClothing = ClothingItems.FindByPredicate([&ClothingItemInstance](const UClothingItem* Item)
|
||||
const bool IsTargetClothing = ClothingItems.FindByPredicate([&ClothingItemInstance](const UClothingItemDefinition* Item)
|
||||
{
|
||||
return Item && Item->Name.EqualTo(ClothingItemInstance->GetClothingItem()->Name);
|
||||
return Item && Item->Name.EqualTo(ClothingItemInstance->GetClothingItemDefinition()->Name);
|
||||
}) != nullptr;
|
||||
if (IsTargetClothing)
|
||||
{
|
||||
@@ -77,9 +77,9 @@ void UEquipClothingRestriction::OnClothingEquipped(UClothingItemInstance* Clothi
|
||||
|
||||
void UEquipClothingRestriction::OnClothingUnequipped(UClothingItemInstance* ClothingItemInstance)
|
||||
{
|
||||
const bool IsTargetClothing = ClothingItems.FindByPredicate([&ClothingItemInstance](const UClothingItem* Item)
|
||||
const bool IsTargetClothing = ClothingItems.FindByPredicate([&ClothingItemInstance](const UClothingItemDefinition* Item)
|
||||
{
|
||||
return Item && Item->Name.EqualTo(ClothingItemInstance->GetClothingItem()->Name);
|
||||
return Item && Item->Name.EqualTo(ClothingItemInstance->GetClothingItemDefinition()->Name);
|
||||
}) != nullptr;
|
||||
if (IsTargetClothing)
|
||||
{
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
#include "EquipClothingRestriction.generated.h"
|
||||
|
||||
class UClothingItemInstance;
|
||||
class UClothingItem;
|
||||
class UClothingItemDefinition;
|
||||
|
||||
UCLASS(EditInlineNew)
|
||||
class NAKEDDESIRE_API UEquipClothingRestriction : public UGoalRestriction
|
||||
@@ -16,7 +16,7 @@ class NAKEDDESIRE_API UEquipClothingRestriction : public UGoalRestriction
|
||||
|
||||
UPROPERTY(EditDefaultsOnly, meta = (ToolTip =
|
||||
"One of provided clothing items should be equipped by player. If multiple clothing items required provide multiple restrictions"))
|
||||
TArray<UClothingItem*> ClothingItems;
|
||||
TArray<UClothingItemDefinition*> ClothingItems;
|
||||
|
||||
public:
|
||||
virtual void Init(ANakedDesireCharacter* PlayerCharacter) override;
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
#include "ExposeBodyPartRestriction.h"
|
||||
|
||||
#include "NakedDesire/Clothing/ClothingItem.h"
|
||||
#include "NakedDesire/Clothing/ClothingItemDefinition.h"
|
||||
#include "NakedDesire/Clothing/ClothingItemInstance.h"
|
||||
#include "NakedDesire/Player/NakedDesireCharacter.h"
|
||||
#include "NakedDesire/Clothing/ClothingManager.h"
|
||||
|
||||
@@ -11,7 +11,7 @@ public class NakedDesire : ModuleRules
|
||||
PublicDependencyModuleNames.AddRange(new string[]
|
||||
{
|
||||
"Core", "CoreUObject", "Engine", "InputCore", "EnhancedInput", "UMG", "CommonUI", "NavigationSystem",
|
||||
"AIModule", "GameplayTags", "Slate", "SlateCore"
|
||||
"AIModule", "GameplayTags", "Slate", "SlateCore", "StructUtils"
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -9,7 +9,7 @@
|
||||
#include "EnhancedInputSubsystems.h"
|
||||
#include "Kismet/GameplayStatics.h"
|
||||
#include "Internationalization/Text.h"
|
||||
#include "NakedDesire/Clothing/ClothingItem.h"
|
||||
#include "NakedDesire/Clothing/ClothingItemDefinition.h"
|
||||
#include "NakedDesire/Clothing/ClothingItemInstance.h"
|
||||
#include "NakedDesire/Global/Constants.h"
|
||||
#include "NakedDesire/Global/NakedDesireHUD.h"
|
||||
@@ -227,15 +227,15 @@ void ANakedDesireCharacter::LogTest()
|
||||
|
||||
void ANakedDesireCharacter::OnClothingEquip(UClothingItemInstance* ClothingItemInstance)
|
||||
{
|
||||
if (ClothingItemInstance->GetClothingItem()->HiddenBodyParts.Contains(EBodyPart::Ass))
|
||||
if (ClothingItemInstance->GetClothingItemDefinition()->HiddenBodyParts.Contains(EBodyPart::Ass))
|
||||
{
|
||||
AnalCensorship->SetVisibility(false);
|
||||
}
|
||||
if (ClothingItemInstance->GetClothingItem()->HiddenBodyParts.Contains(EBodyPart::Genitals))
|
||||
if (ClothingItemInstance->GetClothingItemDefinition()->HiddenBodyParts.Contains(EBodyPart::Genitals))
|
||||
{
|
||||
VaginaCensorship->SetVisibility(false);
|
||||
}
|
||||
if (ClothingItemInstance->GetClothingItem()->HiddenBodyParts.Contains(EBodyPart::Boobs))
|
||||
if (ClothingItemInstance->GetClothingItemDefinition()->HiddenBodyParts.Contains(EBodyPart::Boobs))
|
||||
{
|
||||
BoobLCensorship->SetVisibility(false);
|
||||
BoobRCensorship->SetVisibility(false);
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
|
||||
class UInteractionComponent;
|
||||
class ANakedDesireHUD;
|
||||
class UClothingItem;
|
||||
class UClothingItemDefinition;
|
||||
class UClothingItemInstance;
|
||||
class UClothingSlotsData;
|
||||
class UAIPerceptionStimuliSourceComponent;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#include "PlayerCinematic.h"
|
||||
#include "NakedDesireCharacter.h"
|
||||
#include "Kismet/GameplayStatics.h"
|
||||
#include "NakedDesire/Clothing/ClothingItem.h"
|
||||
#include "NakedDesire/Clothing/ClothingItemDefinition.h"
|
||||
#include "NakedDesire/Clothing/ClothingItemInstance.h"
|
||||
#include "NakedDesire/Clothing/ClothingManager.h"
|
||||
|
||||
@@ -109,16 +109,16 @@ USkeletalMeshComponent* APlayerCinematic::GetMeshByType(const EClothingSlotType
|
||||
|
||||
void APlayerCinematic::EquipClothing(const UClothingItemInstance* ClothingItemInstance)
|
||||
{
|
||||
USkeletalMeshComponent* MeshComponent = GetMeshByType(ClothingItemInstance->GetClothingItem()->SlotType);
|
||||
MeshComponent->SetSkeletalMesh(ClothingItemInstance->GetClothingItem()->SkeletalMesh);
|
||||
if (ClothingItemInstance->GetClothingItem()->UseLeaderPose)
|
||||
USkeletalMeshComponent* MeshComponent = GetMeshByType(ClothingItemInstance->GetClothingItemDefinition()->SlotType);
|
||||
MeshComponent->SetSkeletalMesh(ClothingItemInstance->GetClothingItemDefinition()->SkeletalMesh);
|
||||
if (ClothingItemInstance->GetClothingItemDefinition()->UseLeaderPose)
|
||||
{
|
||||
MeshComponent->SetLeaderPoseComponent(GetMesh());
|
||||
}
|
||||
|
||||
if (!ClothingItemInstance->GetClothingItem()->Materials.IsEmpty())
|
||||
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);
|
||||
}
|
||||
@@ -127,7 +127,7 @@ void APlayerCinematic::EquipClothing(const UClothingItemInstance* ClothingItemIn
|
||||
|
||||
void APlayerCinematic::UnequipClothing(const UClothingItemInstance* ClothingItemInstance)
|
||||
{
|
||||
USkeletalMeshComponent* MeshComponent = GetMeshByType(ClothingItemInstance->GetClothingItem()->SlotType);
|
||||
USkeletalMeshComponent* MeshComponent = GetMeshByType(ClothingItemInstance->GetClothingItemDefinition()->SlotType);
|
||||
MeshComponent->SetSkeletalMesh(nullptr);
|
||||
MeshComponent->SetLeaderPoseComponent(nullptr);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#include "PlayerImpostor.h"
|
||||
#include "NakedDesireCharacter.h"
|
||||
#include "Kismet/GameplayStatics.h"
|
||||
#include "NakedDesire/Clothing/ClothingItem.h"
|
||||
#include "NakedDesire/Clothing/ClothingItemDefinition.h"
|
||||
#include "NakedDesire/Clothing/ClothingItemInstance.h"
|
||||
#include "NakedDesire/Clothing/ClothingManager.h"
|
||||
|
||||
@@ -114,16 +114,16 @@ USkeletalMeshComponent* APlayerImpostor::GetMeshByType(const EClothingSlotType S
|
||||
|
||||
void APlayerImpostor::EquipClothing(const UClothingItemInstance* ClothingItemInstance)
|
||||
{
|
||||
USkeletalMeshComponent* MeshComponent = GetMeshByType(ClothingItemInstance->GetClothingItem()->SlotType);
|
||||
MeshComponent->SetSkeletalMesh(ClothingItemInstance->GetClothingItem()->SkeletalMesh);
|
||||
if (ClothingItemInstance->GetClothingItem()->UseLeaderPose)
|
||||
USkeletalMeshComponent* MeshComponent = GetMeshByType(ClothingItemInstance->GetClothingItemDefinition()->SlotType);
|
||||
MeshComponent->SetSkeletalMesh(ClothingItemInstance->GetClothingItemDefinition()->SkeletalMesh);
|
||||
if (ClothingItemInstance->GetClothingItemDefinition()->UseLeaderPose)
|
||||
{
|
||||
MeshComponent->SetLeaderPoseComponent(GetMesh());
|
||||
}
|
||||
|
||||
if (!ClothingItemInstance->GetClothingItem()->Materials.IsEmpty())
|
||||
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);
|
||||
}
|
||||
@@ -132,7 +132,7 @@ void APlayerImpostor::EquipClothing(const UClothingItemInstance* ClothingItemIns
|
||||
|
||||
void APlayerImpostor::UnequipClothing(const UClothingItemInstance* ClothingItemInstance)
|
||||
{
|
||||
USkeletalMeshComponent* MeshComponent = GetMeshByType(ClothingItemInstance->GetClothingItem()->SlotType);
|
||||
USkeletalMeshComponent* MeshComponent = GetMeshByType(ClothingItemInstance->GetClothingItemDefinition()->SlotType);
|
||||
MeshComponent->SetSkeletalMesh(nullptr);
|
||||
MeshComponent->SetLeaderPoseComponent(nullptr);
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
|
||||
#include "ItemSaveRecord.h"
|
||||
#include "NakedDesire/Global/Constants.h"
|
||||
#include "NakedDesire/Items/ItemInstance.h"
|
||||
#include "Kismet/GameplayStatics.h"
|
||||
|
||||
UGlobalSaveGameData* UGlobalSaveGameData::CreateNewSaveGame()
|
||||
@@ -33,100 +34,102 @@ bool UGlobalSaveGameData::SaveGame(UGlobalSaveGameData* SaveGameData, const FStr
|
||||
return UGameplayStatics::SaveGameToSlot(SaveGameData, SlotName, SLOT_PLAYER);
|
||||
}
|
||||
|
||||
FItemSaveRecord UGlobalSaveGameData::AddWardrobeItem(const UClothingItemInstance* ItemInstance)
|
||||
FItemSaveRecord UGlobalSaveGameData::AddWardrobeItem(const UItemInstance* ItemInstance)
|
||||
{
|
||||
FItemSaveRecord NewSaveRecord;
|
||||
NewSaveRecord.Init(ItemInstance);
|
||||
FItemSaveRecord NewSaveRecord = ItemInstance->ToSaveRecord();
|
||||
WardrobeItems.Push(NewSaveRecord);
|
||||
return NewSaveRecord;
|
||||
}
|
||||
|
||||
bool UGlobalSaveGameData::UpdateWardrobeItem(UClothingItemInstance* ItemInstance)
|
||||
bool UGlobalSaveGameData::UpdateWardrobeItem(UItemInstance* ItemInstance)
|
||||
{
|
||||
for (auto& ItemSaveRecord : WardrobeItems)
|
||||
{
|
||||
if (ItemSaveRecord.InstanceId == ItemInstance->GetInstanceId())
|
||||
{
|
||||
ItemSaveRecord.Init(ItemInstance);
|
||||
ItemSaveRecord = ItemInstance->ToSaveRecord();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool UGlobalSaveGameData::RemoveWardrobeItem(UClothingItemInstance* ItemInstance)
|
||||
bool UGlobalSaveGameData::RemoveWardrobeItem(UItemInstance* ItemInstance)
|
||||
{
|
||||
const int32 RemovedElementsCount = WardrobeItems.RemoveAll([ItemInstance](const FItemSaveRecord& Item)
|
||||
{
|
||||
return Item.InstanceId == ItemInstance->GetInstanceId();
|
||||
});
|
||||
|
||||
|
||||
return RemovedElementsCount > 0;
|
||||
}
|
||||
|
||||
FItemSaveRecord UGlobalSaveGameData::AddEquippedItem(const UClothingItemInstance* ItemInstance)
|
||||
FItemSaveRecord UGlobalSaveGameData::AddEquippedItem(const UItemInstance* ItemInstance)
|
||||
{
|
||||
FItemSaveRecord NewSaveRecord;
|
||||
NewSaveRecord.Init(ItemInstance);
|
||||
FItemSaveRecord NewSaveRecord = ItemInstance->ToSaveRecord();
|
||||
EquippedItems.Push(NewSaveRecord);
|
||||
return NewSaveRecord;
|
||||
}
|
||||
|
||||
bool UGlobalSaveGameData::UpdateEquippedItem(UClothingItemInstance* ItemInstance)
|
||||
void UGlobalSaveGameData::AddEquippedItem(const FItemSaveRecord& ItemRecord)
|
||||
{
|
||||
EquippedItems.Push(ItemRecord);
|
||||
}
|
||||
|
||||
bool UGlobalSaveGameData::UpdateEquippedItem(UItemInstance* ItemInstance)
|
||||
{
|
||||
for (auto& ItemSaveRecord : EquippedItems)
|
||||
{
|
||||
if (ItemSaveRecord.InstanceId == ItemInstance->GetInstanceId())
|
||||
{
|
||||
ItemSaveRecord.Init(ItemInstance);
|
||||
ItemSaveRecord = ItemInstance->ToSaveRecord();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool UGlobalSaveGameData::RemoveEquippedItem(UClothingItemInstance* ItemInstance)
|
||||
bool UGlobalSaveGameData::RemoveEquippedItem(UItemInstance* ItemInstance)
|
||||
{
|
||||
const int32 RemovedElementsCount = EquippedItems.RemoveAll([ItemInstance](const FItemSaveRecord& Item)
|
||||
{
|
||||
return Item.InstanceId == ItemInstance->GetInstanceId();
|
||||
});
|
||||
|
||||
|
||||
return RemovedElementsCount > 0;
|
||||
}
|
||||
|
||||
FItemSaveRecord UGlobalSaveGameData::AddWorldItem(const UClothingItemInstance* ItemInstance, FTransform Transform)
|
||||
FItemSaveRecord UGlobalSaveGameData::AddWorldItem(const UItemInstance* ItemInstance, FTransform Transform)
|
||||
{
|
||||
FItemSaveRecord NewSaveRecord;
|
||||
NewSaveRecord.Init(ItemInstance);
|
||||
FItemSaveRecord NewSaveRecord = ItemInstance->ToSaveRecord();
|
||||
NewSaveRecord.WorldTransform = Transform;
|
||||
WorldItems.Push(NewSaveRecord);
|
||||
return NewSaveRecord;
|
||||
}
|
||||
|
||||
bool UGlobalSaveGameData::UpdateWorldItem(UClothingItemInstance* ItemInstance, FTransform Transform)
|
||||
bool UGlobalSaveGameData::UpdateWorldItem(UItemInstance* ItemInstance, FTransform Transform)
|
||||
{
|
||||
for (auto& ItemSaveRecord : WorldItems)
|
||||
{
|
||||
if (ItemSaveRecord.InstanceId == ItemInstance->GetInstanceId())
|
||||
{
|
||||
ItemSaveRecord.Init(ItemInstance);
|
||||
ItemSaveRecord = ItemInstance->ToSaveRecord();
|
||||
ItemSaveRecord.WorldTransform = Transform;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool UGlobalSaveGameData::RemoveWorldItem(UClothingItemInstance* ItemInstance)
|
||||
bool UGlobalSaveGameData::RemoveWorldItem(UItemInstance* ItemInstance)
|
||||
{
|
||||
const int32 RemovedElementsCount = WorldItems.RemoveAll([ItemInstance](const FItemSaveRecord& Item)
|
||||
{
|
||||
return Item.InstanceId == ItemInstance->GetInstanceId();
|
||||
});
|
||||
|
||||
|
||||
return RemovedElementsCount > 0;
|
||||
}
|
||||
}
|
||||
@@ -8,7 +8,7 @@
|
||||
#include "ItemSaveRecord.h"
|
||||
#include "GlobalSaveGameData.generated.h"
|
||||
|
||||
class UClothingItemInstance;
|
||||
class UItemInstance;
|
||||
|
||||
UCLASS()
|
||||
class NAKEDDESIRE_API UGlobalSaveGameData : public USaveGame
|
||||
@@ -26,19 +26,20 @@ public:
|
||||
UPROPERTY(SaveGame)
|
||||
float Money = 0;
|
||||
|
||||
FItemSaveRecord AddWardrobeItem(const UClothingItemInstance* ItemInstance);
|
||||
bool UpdateWardrobeItem(UClothingItemInstance* ItemInstance);
|
||||
bool RemoveWardrobeItem(UClothingItemInstance* ItemInstance);
|
||||
FItemSaveRecord AddWardrobeItem(const UItemInstance* ItemInstance);
|
||||
bool UpdateWardrobeItem(UItemInstance* ItemInstance);
|
||||
bool RemoveWardrobeItem(UItemInstance* ItemInstance);
|
||||
TArray<FItemSaveRecord> GetWardrobeItems() const { return WardrobeItems; }
|
||||
|
||||
FItemSaveRecord AddEquippedItem(const UClothingItemInstance* ItemInstance);
|
||||
bool UpdateEquippedItem(UClothingItemInstance* ItemInstance);
|
||||
bool RemoveEquippedItem(UClothingItemInstance* ItemInstance);
|
||||
|
||||
FItemSaveRecord AddEquippedItem(const UItemInstance* ItemInstance);
|
||||
void AddEquippedItem(const FItemSaveRecord& ItemRecord);
|
||||
bool UpdateEquippedItem(UItemInstance* ItemInstance);
|
||||
bool RemoveEquippedItem(UItemInstance* ItemInstance);
|
||||
TArray<FItemSaveRecord> GetEquippedItems() const { return EquippedItems; }
|
||||
|
||||
FItemSaveRecord AddWorldItem(const UClothingItemInstance* ItemInstance, FTransform Transform);
|
||||
bool UpdateWorldItem(UClothingItemInstance* ItemInstance, FTransform Transform);
|
||||
bool RemoveWorldItem(UClothingItemInstance* ItemInstance);
|
||||
|
||||
FItemSaveRecord AddWorldItem(const UItemInstance* ItemInstance, FTransform Transform);
|
||||
bool UpdateWorldItem(UItemInstance* ItemInstance, FTransform Transform);
|
||||
bool RemoveWorldItem(UItemInstance* ItemInstance);
|
||||
TArray<FItemSaveRecord> GetWorldItems() const { return WorldItems; }
|
||||
|
||||
UPROPERTY(SaveGame)
|
||||
@@ -50,7 +51,7 @@ public:
|
||||
private:
|
||||
UPROPERTY(SaveGame)
|
||||
TArray<FItemSaveRecord> WardrobeItems;
|
||||
|
||||
|
||||
UPROPERTY(SaveGame)
|
||||
TArray<FItemSaveRecord> EquippedItems;
|
||||
|
||||
|
||||
@@ -1,12 +1,18 @@
|
||||
// © 2025 Naked People Team. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "NakedDesire/Clothing/ClothingItemInstance.h"
|
||||
#include "NakedDesire/Clothing/ClothingItem.h"
|
||||
#include "StructUtils/InstancedStruct.h"
|
||||
#include "ItemSaveRecord.generated.h"
|
||||
|
||||
class UClothingItemInstance;
|
||||
class UItemDefinition;
|
||||
|
||||
/**
|
||||
* Type-agnostic save record for any UItemInstance.
|
||||
* Per-type mutable state lives in State as an FItemInstanceState subclass
|
||||
* (e.g. FClothingInstanceState), so new item types add no fields here.
|
||||
*/
|
||||
USTRUCT()
|
||||
struct NAKEDDESIRE_API FItemSaveRecord
|
||||
{
|
||||
@@ -14,25 +20,16 @@ struct NAKEDDESIRE_API FItemSaveRecord
|
||||
|
||||
UPROPERTY(SaveGame)
|
||||
FGuid InstanceId;
|
||||
|
||||
|
||||
UPROPERTY(SaveGame)
|
||||
TSoftObjectPtr<UClothingItem> Definition;
|
||||
|
||||
TSoftObjectPtr<UItemDefinition> Definition;
|
||||
|
||||
UPROPERTY(SaveGame)
|
||||
float Condition = 1.0f;
|
||||
|
||||
FInstancedStruct State;
|
||||
|
||||
UPROPERTY(SaveGame)
|
||||
FGuid ParentId;
|
||||
|
||||
|
||||
UPROPERTY(SaveGame)
|
||||
FTransform WorldTransform;
|
||||
|
||||
void Init(const UClothingItemInstance* ClothingItemInstance);
|
||||
};
|
||||
|
||||
inline void FItemSaveRecord::Init(const UClothingItemInstance* ClothingItemInstance)
|
||||
{
|
||||
InstanceId = ClothingItemInstance->GetInstanceId();
|
||||
Definition = ClothingItemInstance->GetClothingItem();
|
||||
Condition = ClothingItemInstance->Condition;
|
||||
}
|
||||
};
|
||||
@@ -6,8 +6,8 @@
|
||||
#include "GlobalSaveGameData.h"
|
||||
#include "ItemSaveRecord.h"
|
||||
#include "StartingSaveData.h"
|
||||
#include "NakedDesire/Clothing/ClothingItem.h"
|
||||
#include "NakedDesire/Clothing/ClothingItemInstance.h"
|
||||
#include "NakedDesire/Items/ItemDefinition.h"
|
||||
#include "NakedDesire/Items/ItemInstance.h"
|
||||
#include "NakedDesire/Global/NakedDesireGameInstance.h"
|
||||
|
||||
void USaveSubsystem::LoadGame(const FString& SlotName)
|
||||
@@ -62,14 +62,15 @@ void USaveSubsystem::PopulateStartingData(UGlobalSaveGameData* Save) const
|
||||
if (!GameInstance || !GameInstance->StartingSaveData)
|
||||
return;
|
||||
|
||||
for (UClothingItem* ClothingDef : GameInstance->StartingSaveData->StartingClothing)
|
||||
for (UItemDefinition* Definition : GameInstance->StartingSaveData->StartingItems)
|
||||
{
|
||||
if (!ClothingDef)
|
||||
if (!Definition)
|
||||
continue;
|
||||
|
||||
UItemInstance* Instance = Definition->CreateInstance(Save);
|
||||
if (!Instance)
|
||||
continue;
|
||||
|
||||
// TODO: Refactor. Skip converting to ClothingItemInstance
|
||||
UClothingItemInstance* Instance = NewObject<UClothingItemInstance>(Save);
|
||||
Instance->Init(ClothingDef);
|
||||
Save->AddEquippedItem(Instance);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
#include "Engine/DataAsset.h"
|
||||
#include "StartingSaveData.generated.h"
|
||||
|
||||
class UClothingItem;
|
||||
class UItemDefinition;
|
||||
|
||||
UCLASS()
|
||||
class NAKEDDESIRE_API UStartingSaveData : public UPrimaryDataAsset
|
||||
@@ -15,5 +15,5 @@ class NAKEDDESIRE_API UStartingSaveData : public UPrimaryDataAsset
|
||||
|
||||
public:
|
||||
UPROPERTY(EditDefaultsOnly, Category = "Starting State")
|
||||
TArray<TObjectPtr<UClothingItem>> StartingClothing;
|
||||
TArray<TObjectPtr<UItemDefinition>> StartingItems;
|
||||
};
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
#include "EquipmentSlotWidget.h"
|
||||
#include "Kismet/GameplayStatics.h"
|
||||
#include "NakedDesire/Clothing/ClothingItem.h"
|
||||
#include "NakedDesire/Clothing/ClothingItemDefinition.h"
|
||||
#include "NakedDesire/Clothing/ClothingItemInstance.h"
|
||||
#include "NakedDesire/Clothing/ClothingManager.h"
|
||||
#include "NakedDesire/Clothing/ClothingSlotsData.h"
|
||||
@@ -13,7 +13,7 @@ void UEquipmentSlotWidget::SetItem(UClothingItemInstance* InItem)
|
||||
{
|
||||
ClothingItemInstance = InItem;
|
||||
|
||||
IconImage->SetBrushFromTexture(InItem->GetClothingItem()->Icon);
|
||||
IconImage->SetBrushFromTexture(InItem->GetClothingItemDefinition()->Icon);
|
||||
IconImage->SetVisibility(ESlateVisibility::SelfHitTestInvisible);
|
||||
PlaceholderImage->SetVisibility(ESlateVisibility::Hidden);
|
||||
SetIsEnabled(true);
|
||||
@@ -45,7 +45,7 @@ void UEquipmentSlotWidget::NativePreConstruct()
|
||||
|
||||
void UEquipmentSlotWidget::OnClothingEquip(UClothingItemInstance* InClothingItemInstance)
|
||||
{
|
||||
if (InClothingItemInstance->GetClothingItem()->SlotType != SlotType)
|
||||
if (InClothingItemInstance->GetClothingItemDefinition()->SlotType != SlotType)
|
||||
return;
|
||||
|
||||
SetItem(InClothingItemInstance);
|
||||
@@ -53,7 +53,7 @@ void UEquipmentSlotWidget::OnClothingEquip(UClothingItemInstance* InClothingItem
|
||||
|
||||
void UEquipmentSlotWidget::OnClothingUnequip(UClothingItemInstance* InClothingItemInstance)
|
||||
{
|
||||
if (InClothingItemInstance->GetClothingItem()->SlotType != SlotType)
|
||||
if (InClothingItemInstance->GetClothingItemDefinition()->SlotType != SlotType)
|
||||
return;
|
||||
|
||||
ClearItem();
|
||||
|
||||
Reference in New Issue
Block a user