Clothing system refactor
This commit is contained in:
@@ -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