Compare commits

..

5 Commits

Author SHA1 Message Date
olehhapuk e6a4fc7e06 Test 2026-05-29 23:11:25 +03:00
olehhapuk 5ecbf24998 Added censorship to player impostor and player cinematic 2026-05-29 23:02:53 +03:00
olehhapuk a61d4e2bd0 Refactor clothing visuals 2026-05-29 22:54:28 +03:00
olehhapuk 0cccb12b72 Refactor censorship 2026-05-29 22:27:32 +03:00
olehhapuk 1c8ee03083 Clothing system refactor 2026-05-29 22:13:09 +03:00
70 changed files with 815 additions and 767 deletions
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+4
View File
@@ -42,6 +42,10 @@
"Win64"
],
"MarketplaceURL": "com.epicgames.launcher://ue/marketplace/product/362651520df94e4fa65492dbcba44ae2"
},
{
"Name": "StructUtils",
"Enabled": true
}
]
}
@@ -0,0 +1,93 @@
// © 2025 Naked People Team. All Rights Reserved.
#include "CensorshipComponent.h"
#include "Components/StaticMeshComponent.h"
#include "NakedDesire/Clothing/BodyPart.h"
#include "NakedDesire/Clothing/ClothingManager.h"
#include "NakedDesire/Global/Constants.h"
#include "NakedDesire/Global/NakedDesireUserSettings.h"
UCensorshipComponent::UCensorshipComponent()
{
PrimaryComponentTick.bCanEverTick = false;
}
void UCensorshipComponent::Initialize(UClothingManager* InClothingManager,
UStaticMeshComponent* InBoobL, UStaticMeshComponent* InBoobR,
UStaticMeshComponent* InVagina, UStaticMeshComponent* InAnal)
{
ClothingManager = InClothingManager;
BoobLCensorship = InBoobL;
BoobRCensorship = InBoobR;
VaginaCensorship = InVagina;
AnalCensorship = InAnal;
if (ClothingManager)
{
ClothingManager->OnClothingEquip.AddUniqueDynamic(this, &UCensorshipComponent::HandleClothingChanged);
ClothingManager->OnClothingUnequip.AddUniqueDynamic(this, &UCensorshipComponent::HandleClothingChanged);
}
if (UNakedDesireUserSettings* Settings = UNakedDesireUserSettings::GetNakedDesireUserSettings())
{
Settings->OnSettingsChanged.AddUniqueDynamic(this, &UCensorshipComponent::HandleSettingsChanged);
}
RefreshCensorship();
}
void UCensorshipComponent::RefreshCensorship()
{
if (!ClothingManager)
return;
const bool bCensor = ShouldCensor();
SetBodyPartCensored(EBodyPart::Boobs, bCensor && ClothingManager->IsBodyPartExposed(EBodyPart::Boobs));
SetBodyPartCensored(EBodyPart::Genitals, bCensor && ClothingManager->IsBodyPartExposed(EBodyPart::Genitals));
SetBodyPartCensored(EBodyPart::Ass, bCensor && ClothingManager->IsBodyPartExposed(EBodyPart::Ass));
}
void UCensorshipComponent::HandleClothingChanged(UClothingItemInstance* ClothingItemInstance)
{
RefreshCensorship();
}
void UCensorshipComponent::HandleSettingsChanged(UNakedDesireUserSettings* Settings)
{
RefreshCensorship();
}
bool UCensorshipComponent::ShouldCensor() const
{
if (IS_DEMO)
return true;
const UNakedDesireUserSettings* Settings = UNakedDesireUserSettings::GetNakedDesireUserSettings();
return Settings && Settings->GetIsCensorshipEnabled();
}
void UCensorshipComponent::SetBodyPartCensored(EBodyPart BodyPart, bool bCensored)
{
switch (BodyPart)
{
case EBodyPart::Boobs:
if (BoobLCensorship)
BoobLCensorship->SetVisibility(bCensored);
if (BoobRCensorship)
BoobRCensorship->SetVisibility(bCensored);
break;
case EBodyPart::Genitals:
if (VaginaCensorship)
VaginaCensorship->SetVisibility(bCensored);
break;
case EBodyPart::Ass:
if (AnalCensorship)
AnalCensorship->SetVisibility(bCensored);
break;
default:
break;
}
}
@@ -0,0 +1,66 @@
// © 2025 Naked People Team. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "Components/ActorComponent.h"
#include "CensorshipComponent.generated.h"
class UClothingManager;
class UClothingItemInstance;
class UNakedDesireUserSettings;
class UStaticMeshComponent;
enum class EBodyPart : uint8;
/**
* Owns the show/hide logic for the body-part censorship meshes. The meshes
* themselves stay on the character (BP-authored assets + fitted transforms);
* this component just drives their visibility from the equipped set + setting.
*/
UCLASS(ClassGroup=(Custom), meta=(BlueprintSpawnableComponent))
class NAKEDDESIRE_API UCensorshipComponent : public UActorComponent
{
GENERATED_BODY()
public:
UCensorshipComponent();
/**
* Wire dependencies and apply the initial censorship state. Call from the
* owning character's BeginPlay *after* Super::BeginPlay, so clothing
* hydration has already populated the equipped set.
*/
void Initialize(UClothingManager* InClothingManager,
UStaticMeshComponent* InBoobL, UStaticMeshComponent* InBoobR,
UStaticMeshComponent* InVagina, UStaticMeshComponent* InAnal);
/** Recompute every censor mesh from the current equipped set + setting. Idempotent. */
void RefreshCensorship();
private:
UFUNCTION()
void HandleClothingChanged(UClothingItemInstance* ClothingItemInstance);
UFUNCTION()
void HandleSettingsChanged(UNakedDesireUserSettings* Settings);
/** Censorship is forced on in the demo build regardless of the user setting. */
bool ShouldCensor() const;
void SetBodyPartCensored(EBodyPart BodyPart, bool bCensored);
UPROPERTY()
TObjectPtr<UClothingManager> ClothingManager;
UPROPERTY()
TObjectPtr<UStaticMeshComponent> BoobLCensorship;
UPROPERTY()
TObjectPtr<UStaticMeshComponent> BoobRCensorship;
UPROPERTY()
TObjectPtr<UStaticMeshComponent> VaginaCensorship;
UPROPERTY()
TObjectPtr<UStaticMeshComponent> AnalCensorship;
};
@@ -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,16 +69,12 @@ 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,11 +1,21 @@
#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
@@ -16,16 +26,13 @@ public:
UPROPERTY(BlueprintReadOnly, Category = "Clothing Item")
float Condition = 1.0f;
UClothingItem* GetClothingItem() const { return ClothingItem; }
UClothingItemDefinition* GetClothingItemDefinition() const { return Cast<UClothingItemDefinition>(ItemDefinition); }
void Init(UClothingItem* InClothingItem);
void Setup(UClothingItem* InClothingItem, const TArray<UItemInstance*>& InStoredItems, float InCondition, FGuid InInstanceId);
static UClothingItemInstance* CreateFromSave(UObject* Outer, const FItemSaveRecord& ItemSaveRecord);
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;
+10 -60
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,38 +78,7 @@ float UClothingManager::GetHeelHeight()
const UClothingItemInstance* Footwear = EquippedClothing[EClothingSlotType::Footwear];
return Footwear->GetClothingItem()->ShoesOffset;
}
USkeletalMeshComponent* UClothingManager::GetMeshComponent(const EClothingSlotType SlotType) const
{
const ANakedDesireCharacter* Player = Cast<ANakedDesireCharacter>(GetOwner());
if (!Player)
{
return nullptr;
}
switch (SlotType)
{
case EClothingSlotType::Nipples: return Player->NipplesMeshComponent;
case EClothingSlotType::Anal: return Player->AnalMeshComponent;
case EClothingSlotType::Vagina: return Player->VaginaMeshComponent;
case EClothingSlotType::Head: return Player->HeadMeshComponent;
case EClothingSlotType::Neck: return Player->NeckMeshComponent;
case EClothingSlotType::Face: return Player->FaceMeshComponent;
case EClothingSlotType::Eyes: return Player->EyesMeshComponent;
case EClothingSlotType::Bodysuit: return Player->BodySuitMeshComponent;
case EClothingSlotType::Top: return Player->TopMeshComponent;
case EClothingSlotType::Bottom: return Player->BottomMeshComponent;
case EClothingSlotType::UnderwearTop: return Player->UnderwearTopMeshComponent;
case EClothingSlotType::UnderwearBottom:return Player->UnderwearBottomMeshComponent;
case EClothingSlotType::Socks: return Player->SocksMeshComponent;
case EClothingSlotType::Footwear: return Player->FootwearMeshComponent;
case EClothingSlotType::Outerwear: return Player->OuterwearMeshComponent;
case EClothingSlotType::WristRestraint: return Player->WristRestraintMeshComponent;
case EClothingSlotType::AnkleRestraint: return Player->AnkleRestraintMeshComponent;
case EClothingSlotType::NeckRestraint: return Player->NeckRestraintMeshComponent;
default: return nullptr;
}
return Footwear->GetClothingItemDefinition()->ShoesOffset;
}
void UClothingManager::SpawnClothingPickup(UClothingItemInstance* ItemInstance)
@@ -135,25 +107,11 @@ void UClothingManager::PutOnClothing(UClothingItemInstance* ClothingItemInstance
if (!ClothingItemInstance)
return;
const EClothingSlotType ClothingSlotType = ClothingItemInstance->GetClothingItem()->SlotType;
USkeletalMeshComponent* MeshComponent = GetMeshComponent(ClothingSlotType);
MeshComponent->SetSkeletalMesh(ClothingItemInstance->GetClothingItem()->SkeletalMesh);
if (!ClothingItemInstance->GetClothingItem()->Materials.IsEmpty())
{
for (const TPair<FName, UMaterialInstance*>& Material : ClothingItemInstance->GetClothingItem()->Materials)
{
MeshComponent->SetMaterialByName(Material.Key, Material.Value);
}
}
const EClothingSlotType ClothingSlotType = ClothingItemInstance->GetClothingItemDefinition()->SlotType;
SetClothingSlotItem(ClothingSlotType, ClothingItemInstance);
if (ClothingItemInstance->GetClothingItem()->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 +132,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);
@@ -197,14 +155,6 @@ UClothingItemInstance* UClothingManager::RemoveClothing(const EClothingSlotType
SetClothingSlotItem(ClothingSlotType, nullptr);
USkeletalMeshComponent* MeshComponent = GetMeshComponent(ClothingSlotType);
MeshComponent->SetSkeletalMesh(nullptr);
if (ExistingItem->GetClothingItem()->UseLeaderPose)
{
MeshComponent->SetLeaderPoseComponent(nullptr);
}
OnClothingUnequip.Broadcast(ExistingItem);
USaveSubsystem* SaveSubsystem = UGameplayStatics::GetGameInstance(GetWorld())->GetSubsystem<USaveSubsystem>();
@@ -53,7 +53,6 @@ public:
float GetHeelHeight();
private:
USkeletalMeshComponent* GetMeshComponent(EClothingSlotType SlotType) const;
void SpawnClothingPickup(UClothingItemInstance* ItemInstance);
UPROPERTY()
@@ -0,0 +1,98 @@
// © 2025 Naked People Team. All Rights Reserved.
#include "ClothingVisualsComponent.h"
#include "ClothingItemDefinition.h"
#include "ClothingItemInstance.h"
#include "ClothingManager.h"
#include "Components/SkeletalMeshComponent.h"
#include "Materials/MaterialInstance.h"
UClothingVisualsComponent::UClothingVisualsComponent()
{
PrimaryComponentTick.bCanEverTick = false;
}
void UClothingVisualsComponent::Initialize(USkeletalMeshComponent* InBodyMesh, UClothingManager* InClothingManager)
{
BodyMesh = InBodyMesh;
ClothingManager = InClothingManager;
if (!ClothingManager)
return;
ClothingManager->OnClothingEquip.AddUniqueDynamic(this, &UClothingVisualsComponent::HandleClothingEquip);
ClothingManager->OnClothingUnequip.AddUniqueDynamic(this, &UClothingVisualsComponent::HandleClothingUnequip);
for (const UClothingItemInstance* Equipped : ClothingManager->GetEquippedClothing())
{
Apply(Equipped);
}
}
void UClothingVisualsComponent::Apply(const UClothingItemInstance* ClothingItemInstance)
{
if (!ClothingItemInstance)
return;
const UClothingItemDefinition* Definition = ClothingItemInstance->GetClothingItemDefinition();
if (!Definition)
return;
USkeletalMeshComponent* SlotMesh = GetOrCreateSlotMesh(Definition->SlotType);
if (!SlotMesh)
return;
SlotMesh->SetSkeletalMesh(Definition->SkeletalMesh);
if (Definition->UseLeaderPose)
{
SlotMesh->SetLeaderPoseComponent(BodyMesh);
}
for (const TPair<FName, UMaterialInstance*>& Material : Definition->Materials)
{
SlotMesh->SetMaterialByName(Material.Key, Material.Value);
}
}
void UClothingVisualsComponent::ClearSlot(EClothingSlotType SlotType)
{
if (const TObjectPtr<USkeletalMeshComponent>* Found = SlotMeshes.Find(SlotType))
{
(*Found)->SetLeaderPoseComponent(nullptr);
(*Found)->SetSkeletalMesh(nullptr);
}
}
void UClothingVisualsComponent::HandleClothingEquip(UClothingItemInstance* ClothingItemInstance)
{
Apply(ClothingItemInstance);
}
void UClothingVisualsComponent::HandleClothingUnequip(UClothingItemInstance* ClothingItemInstance)
{
if (ClothingItemInstance && ClothingItemInstance->GetClothingItemDefinition())
{
ClearSlot(ClothingItemInstance->GetClothingItemDefinition()->SlotType);
}
}
USkeletalMeshComponent* UClothingVisualsComponent::GetOrCreateSlotMesh(EClothingSlotType SlotType)
{
if (const TObjectPtr<USkeletalMeshComponent>* Found = SlotMeshes.Find(SlotType))
{
return *Found;
}
if (!BodyMesh)
return nullptr;
const FName ComponentName = *FString::Printf(TEXT("ClothingSlotMesh_%d"), static_cast<int32>(SlotType));
USkeletalMeshComponent* SlotMesh = NewObject<USkeletalMeshComponent>(GetOwner(), ComponentName);
SlotMesh->SetupAttachment(BodyMesh);
SlotMesh->RegisterComponent();
SlotMeshes.Add(SlotType, SlotMesh);
return SlotMesh;
}
@@ -0,0 +1,56 @@
// © 2025 Naked People Team. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "Components/ActorComponent.h"
#include "ClothingSlotType.h"
#include "ClothingVisualsComponent.generated.h"
class UClothingManager;
class UClothingItemInstance;
class USkeletalMeshComponent;
/**
* Renders equipped clothing by lazily spawning one skeletal mesh component per
* occupied slot, parented to a target body mesh. Reacts to a UClothingManager's
* equip/unequip events, so the character, mirror impostor, and cinematic double
* share one implementation instead of hand-maintained per-slot components.
*/
UCLASS(ClassGroup=(Custom), meta=(BlueprintSpawnableComponent))
class NAKEDDESIRE_API UClothingVisualsComponent : public UActorComponent
{
GENERATED_BODY()
public:
UClothingVisualsComponent();
/**
* Bind to a clothing manager and a body mesh (the leader-pose source and
* attach parent for spawned slot meshes), then apply the current equipped set.
* Call after the manager has hydrated (e.g. owning actor's BeginPlay).
*/
void Initialize(USkeletalMeshComponent* InBodyMesh, UClothingManager* InClothingManager);
void Apply(const UClothingItemInstance* ClothingItemInstance);
void ClearSlot(EClothingSlotType SlotType);
private:
UFUNCTION()
void HandleClothingEquip(UClothingItemInstance* ClothingItemInstance);
UFUNCTION()
void HandleClothingUnequip(UClothingItemInstance* ClothingItemInstance);
/** Returns the slot's mesh component, creating + attaching it on first use. */
USkeletalMeshComponent* GetOrCreateSlotMesh(EClothingSlotType SlotType);
UPROPERTY()
TObjectPtr<USkeletalMeshComponent> BodyMesh;
UPROPERTY()
TObjectPtr<UClothingManager> ClothingManager;
UPROPERTY()
TMap<EClothingSlotType, TObjectPtr<USkeletalMeshComponent>> SlotMeshes;
};
@@ -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;
}
+32
View File
@@ -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;
};
+38
View File
@@ -1,5 +1,9 @@
#include "ItemInstance.h"
#include "ItemDefinition.h"
#include "StructUtils/InstancedStruct.h"
#include "NakedDesire/SaveGame/ItemSaveRecord.h"
void UItemInstance::PostInitProperties()
{
Super::PostInitProperties();
@@ -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;
}
+36
View File
@@ -4,6 +4,20 @@
#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
{
@@ -16,7 +30,29 @@ public:
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;
}
}
+23 -5
View File
@@ -2,9 +2,21 @@
#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
@@ -12,9 +24,15 @@ class NAKEDDESIRE_API USexToyInstance : public UItemInstance
GENERATED_BODY()
public:
USexToyItem* GetSexToyItem() const { return SexToyItem; }
USexToyItem* GetSexToyItem() const { return Cast<USexToyItem>(ItemDefinition); }
private:
UPROPERTY()
TObjectPtr<USexToyItem> SexToyItem;
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;
};
+7
View File
@@ -1 +1,8 @@
#include "SexToyItem.h"
#include "SexToyInstance.h"
TSubclassOf<UItemInstance> USexToyItem::GetInstanceClass() const
{
return USexToyInstance::StaticClass();
}
+4 -2
View File
@@ -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"
+1 -1
View File
@@ -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,8 +9,10 @@
#include "EnhancedInputSubsystems.h"
#include "Kismet/GameplayStatics.h"
#include "Internationalization/Text.h"
#include "NakedDesire/Clothing/ClothingItem.h"
#include "NakedDesire/Censorship/CensorshipComponent.h"
#include "NakedDesire/Clothing/ClothingItemDefinition.h"
#include "NakedDesire/Clothing/ClothingItemInstance.h"
#include "NakedDesire/Clothing/ClothingVisualsComponent.h"
#include "NakedDesire/Global/Constants.h"
#include "NakedDesire/Global/NakedDesireHUD.h"
#include "NakedDesire/Global/NakedDesireUserSettings.h"
@@ -32,42 +34,7 @@ ANakedDesireCharacter::ANakedDesireCharacter()
StatsManager = CreateDefaultSubobject<UStatsManager>("Stats Manager");
MissionsManager = CreateDefaultSubobject<UMissionsManager>("Missions Manager");
NipplesMeshComponent = CreateDefaultSubobject<USkeletalMeshComponent>("Nipples");
NipplesMeshComponent->SetupAttachment(GetMesh());
AnalMeshComponent = CreateDefaultSubobject<USkeletalMeshComponent>("Anal");
AnalMeshComponent->SetupAttachment(GetMesh());
VaginaMeshComponent = CreateDefaultSubobject<USkeletalMeshComponent>("Vagina");
VaginaMeshComponent->SetupAttachment(GetMesh());
HeadMeshComponent = CreateDefaultSubobject<USkeletalMeshComponent>("Head");
HeadMeshComponent->SetupAttachment(GetMesh());
NeckMeshComponent = CreateDefaultSubobject<USkeletalMeshComponent>("Neck");
NeckMeshComponent->SetupAttachment(GetMesh());
FaceMeshComponent = CreateDefaultSubobject<USkeletalMeshComponent>("Face");
FaceMeshComponent->SetupAttachment(GetMesh());
EyesMeshComponent = CreateDefaultSubobject<USkeletalMeshComponent>("Eyes");
EyesMeshComponent->SetupAttachment(GetMesh());
BodySuitMeshComponent = CreateDefaultSubobject<USkeletalMeshComponent>("Body Suit");
BodySuitMeshComponent->SetupAttachment(GetMesh());
TopMeshComponent = CreateDefaultSubobject<USkeletalMeshComponent>("Top");
TopMeshComponent->SetupAttachment(GetMesh());
BottomMeshComponent = CreateDefaultSubobject<USkeletalMeshComponent>("Bottom");
BottomMeshComponent->SetupAttachment(GetMesh());
UnderwearTopMeshComponent = CreateDefaultSubobject<USkeletalMeshComponent>("Underwear Top");
UnderwearTopMeshComponent->SetupAttachment(GetMesh());
UnderwearBottomMeshComponent = CreateDefaultSubobject<USkeletalMeshComponent>("Underwear Bottom");
UnderwearBottomMeshComponent->SetupAttachment(GetMesh());
SocksMeshComponent = CreateDefaultSubobject<USkeletalMeshComponent>("Socks");
SocksMeshComponent->SetupAttachment(GetMesh());
FootwearMeshComponent = CreateDefaultSubobject<USkeletalMeshComponent>("Footwear");
FootwearMeshComponent->SetupAttachment(GetMesh());
OuterwearMeshComponent = CreateDefaultSubobject<USkeletalMeshComponent>("Outerwear");
OuterwearMeshComponent->SetupAttachment(GetMesh());
WristRestraintMeshComponent = CreateDefaultSubobject<USkeletalMeshComponent>("Wrist Restraint");
WristRestraintMeshComponent->SetupAttachment(GetMesh());
AnkleRestraintMeshComponent = CreateDefaultSubobject<USkeletalMeshComponent>("Ankle Restraint");
AnkleRestraintMeshComponent->SetupAttachment(GetMesh());
NeckRestraintMeshComponent = CreateDefaultSubobject<USkeletalMeshComponent>("Neck Restraint");
NeckRestraintMeshComponent->SetupAttachment(GetMesh());
ClothingVisualsComponent = CreateDefaultSubobject<UClothingVisualsComponent>(TEXT("Clothing Visuals Component"));
BoobLCensorship = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("Boob L Censorship"));
BoobLCensorship->SetupAttachment(GetMesh(), FName(TEXT("boob_l")));
@@ -78,6 +45,8 @@ ANakedDesireCharacter::ANakedDesireCharacter()
AnalCensorship = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("Anal Censorship"));
AnalCensorship->SetupAttachment(GetMesh(), FName(TEXT("pelvis")));
CensorshipComponent = CreateDefaultSubobject<UCensorshipComponent>(TEXT("Censorship Component"));
StimuliSourceComponent = CreateDefaultSubobject<UAIPerceptionStimuliSourceComponent>(TEXT("Stimuli Source Component"));
InteractionComponent = CreateDefaultSubobject<UInteractionComponent>(TEXT("Interaction Component"));
}
@@ -153,12 +122,12 @@ void ANakedDesireCharacter::BeginPlay()
StimuliSourceComponent->RegisterForSense(TSubclassOf<UAISense_Sight>());
StimuliSourceComponent->RegisterWithPerceptionSystem();
ClothingManager->OnClothingEquip.AddUniqueDynamic(this, &ANakedDesireCharacter::OnClothingEquip);
ClothingManager->OnClothingUnequip.AddUniqueDynamic(this, &ANakedDesireCharacter::OnClothingUnequip);
// Initialize after Super::BeginPlay so clothing hydration has populated the
// equipped set; both components sync from that final state.
ClothingVisualsComponent->Initialize(GetMesh(), ClothingManager);
CensorshipComponent->Initialize(ClothingManager, BoobLCensorship, BoobRCensorship, VaginaCensorship, AnalCensorship);
HUD = Cast<ANakedDesireHUD>(UGameplayStatics::GetPlayerController(GetWorld(), 0)->GetHUD());
UNakedDesireUserSettings::GetNakedDesireUserSettings()->OnSettingsChanged.AddUniqueDynamic(this, &ANakedDesireCharacter::OnSettingsChanged);
}
UAISense_Sight::EVisibilityResult ANakedDesireCharacter::CanBeSeenFrom(const FCanBeSeenFromContext& Context,
@@ -225,70 +194,6 @@ void ANakedDesireCharacter::LogTest()
UE_LOG(LogTemp, Warning, TEXT("ANakedDesireCharacter::LogTest %s"), *ClothingItemInstance->GetInstanceId().ToString());
}
void ANakedDesireCharacter::OnClothingEquip(UClothingItemInstance* ClothingItemInstance)
{
if (ClothingItemInstance->GetClothingItem()->HiddenBodyParts.Contains(EBodyPart::Ass))
{
AnalCensorship->SetVisibility(false);
}
if (ClothingItemInstance->GetClothingItem()->HiddenBodyParts.Contains(EBodyPart::Genitals))
{
VaginaCensorship->SetVisibility(false);
}
if (ClothingItemInstance->GetClothingItem()->HiddenBodyParts.Contains(EBodyPart::Boobs))
{
BoobLCensorship->SetVisibility(false);
BoobRCensorship->SetVisibility(false);
}
}
void ANakedDesireCharacter::OnClothingUnequip(UClothingItemInstance* ClothingItemInstance)
{
if (!UNakedDesireUserSettings::GetNakedDesireUserSettings()->GetIsCensorshipEnabled() && !IS_DEMO)
return;
if (ClothingManager->IsBodyPartExposed(EBodyPart::Ass))
{
AnalCensorship->SetVisibility(true);
}
if (ClothingManager->IsBodyPartExposed(EBodyPart::Genitals))
{
VaginaCensorship->SetVisibility(true);
}
if (ClothingManager->IsBodyPartExposed(EBodyPart::Boobs))
{
BoobLCensorship->SetVisibility(true);
BoobRCensorship->SetVisibility(true);
}
}
void ANakedDesireCharacter::OnSettingsChanged(UNakedDesireUserSettings* Settings)
{
if (Settings->GetIsCensorshipEnabled())
{
if (ClothingManager->IsBodyPartExposed(EBodyPart::Ass))
{
AnalCensorship->SetVisibility(true);
}
if (ClothingManager->IsBodyPartExposed(EBodyPart::Genitals))
{
VaginaCensorship->SetVisibility(true);
}
if (ClothingManager->IsBodyPartExposed(EBodyPart::Boobs))
{
BoobLCensorship->SetVisibility(true);
BoobRCensorship->SetVisibility(true);
}
}
else if (!IS_DEMO)
{
AnalCensorship->SetVisibility(false);
VaginaCensorship->SetVisibility(false);
BoobLCensorship->SetVisibility(false);
BoobRCensorship->SetVisibility(false);
}
}
void ANakedDesireCharacter::OnLook(const FInputActionValue& Value)
{
const FVector2D MouseValue = Value.Get<FVector2D>();
@@ -14,11 +14,13 @@
class UInteractionComponent;
class ANakedDesireHUD;
class UClothingItem;
class UClothingItemDefinition;
class UClothingItemInstance;
class UClothingSlotsData;
class UAIPerceptionStimuliSourceComponent;
class UClothingManager;
class UClothingVisualsComponent;
class UCensorshipComponent;
class UStatsManager;
class UMissionsManager;
class ANPCAIController;
@@ -56,60 +58,9 @@ public:
UPROPERTY(EditDefaultsOnly, Category = "Input")
UInputAction* InteractAction;
// Clothing
// Clothing slot meshes are spawned per equipped slot at runtime by ClothingVisualsComponent.
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Clothing")
USkeletalMeshComponent* NipplesMeshComponent;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Clothing")
USkeletalMeshComponent* AnalMeshComponent;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Clothing")
USkeletalMeshComponent* VaginaMeshComponent;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Clothing")
USkeletalMeshComponent* HeadMeshComponent;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Clothing")
USkeletalMeshComponent* NeckMeshComponent;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Clothing")
USkeletalMeshComponent* FaceMeshComponent;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Clothing")
USkeletalMeshComponent* EyesMeshComponent;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Clothing")
USkeletalMeshComponent* BodySuitMeshComponent;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Clothing")
USkeletalMeshComponent* TopMeshComponent;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Clothing")
USkeletalMeshComponent* BottomMeshComponent;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Clothing")
USkeletalMeshComponent* UnderwearTopMeshComponent;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Clothing")
USkeletalMeshComponent* UnderwearBottomMeshComponent;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Clothing")
USkeletalMeshComponent* SocksMeshComponent;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Clothing")
USkeletalMeshComponent* FootwearMeshComponent;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Clothing")
USkeletalMeshComponent* OuterwearMeshComponent;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Clothing")
USkeletalMeshComponent* WristRestraintMeshComponent;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Clothing")
USkeletalMeshComponent* AnkleRestraintMeshComponent;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Clothing")
USkeletalMeshComponent* NeckRestraintMeshComponent;
UClothingVisualsComponent* ClothingVisualsComponent;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Censorship")
UStaticMeshComponent* BoobLCensorship;
@@ -123,6 +74,9 @@ public:
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Censorship")
UStaticMeshComponent* AnalCensorship;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Censorship")
UCensorshipComponent* CensorshipComponent;
UPROPERTY(EditDefaultsOnly, Category = "Clothing")
TObjectPtr<UClothingSlotsData> SlotsData;
@@ -172,15 +126,6 @@ private:
EGait Gait = EGait::Walk;
EStance Stance = EStance::Stand;
UFUNCTION()
void OnClothingEquip(UClothingItemInstance* ClothingItemInstance);
UFUNCTION()
void OnClothingUnequip(UClothingItemInstance* ClothingItemInstance);
UFUNCTION()
void OnSettingsChanged(UNakedDesireUserSettings* Settings);
void OnLook(const FInputActionValue& Value);
void OnMove(const FInputActionValue& Value);
void OnRunPress(const FInputActionValue& Value);
+18 -111
View File
@@ -1,44 +1,28 @@
#include "PlayerCinematic.h"
#include "PlayerCinematic.h"
#include "NakedDesireCharacter.h"
#include "Kismet/GameplayStatics.h"
#include "NakedDesire/Clothing/ClothingItem.h"
#include "NakedDesire/Clothing/ClothingItemInstance.h"
#include "NakedDesire/Censorship/CensorshipComponent.h"
#include "NakedDesire/Clothing/ClothingManager.h"
#include "NakedDesire/Clothing/ClothingVisualsComponent.h"
// Sets default values
APlayerCinematic::APlayerCinematic()
{
PrimaryActorTick.bCanEverTick = false;
NipplesMeshComponent = CreateDefaultSubobject<USkeletalMeshComponent>(TEXT("Nipples"));
NipplesMeshComponent->SetupAttachment(GetMesh());
AnalMeshComponent = CreateDefaultSubobject<USkeletalMeshComponent>(TEXT("Anal"));
AnalMeshComponent->SetupAttachment(GetMesh());
VaginaMeshComponent = CreateDefaultSubobject<USkeletalMeshComponent>(TEXT("Vagina"));
VaginaMeshComponent->SetupAttachment(GetMesh());
HeadMeshComponent = CreateDefaultSubobject<USkeletalMeshComponent>(TEXT("Head"));
HeadMeshComponent->SetupAttachment(GetMesh());
NeckMeshComponent = CreateDefaultSubobject<USkeletalMeshComponent>(TEXT("Neck"));
NeckMeshComponent->SetupAttachment(GetMesh());
FaceMeshComponent = CreateDefaultSubobject<USkeletalMeshComponent>(TEXT("Face"));
FaceMeshComponent->SetupAttachment(GetMesh());
EyesMeshComponent = CreateDefaultSubobject<USkeletalMeshComponent>(TEXT("Eyes"));
EyesMeshComponent->SetupAttachment(GetMesh());
BodyMeshComponent = CreateDefaultSubobject<USkeletalMeshComponent>(TEXT("Body"));
BodyMeshComponent->SetupAttachment(GetMesh());
TopMeshComponent = CreateDefaultSubobject<USkeletalMeshComponent>(TEXT("Top"));
TopMeshComponent->SetupAttachment(GetMesh());
BottomMeshComponent = CreateDefaultSubobject<USkeletalMeshComponent>(TEXT("Bottom"));
BottomMeshComponent->SetupAttachment(GetMesh());
BraMeshComponent = CreateDefaultSubobject<USkeletalMeshComponent>(TEXT("Bra"));
BraMeshComponent->SetupAttachment(GetMesh());
PantiesMeshComponent = CreateDefaultSubobject<USkeletalMeshComponent>(TEXT("Panties"));
PantiesMeshComponent->SetupAttachment(GetMesh());
SocksMeshComponent = CreateDefaultSubobject<USkeletalMeshComponent>(TEXT("Socks"));
SocksMeshComponent->SetupAttachment(GetMesh());
ShoesMeshComponent = CreateDefaultSubobject<USkeletalMeshComponent>(TEXT("Shoes"));
ShoesMeshComponent->SetupAttachment(GetMesh());
ClothingVisualsComponent = CreateDefaultSubobject<UClothingVisualsComponent>(TEXT("Clothing Visuals Component"));
BoobLCensorship = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("Boob L Censorship"));
BoobLCensorship->SetupAttachment(GetMesh(), FName(TEXT("boob_l")));
BoobRCensorship = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("Boob R Censorship"));
BoobRCensorship->SetupAttachment(GetMesh(), FName(TEXT("boob_r")));
VaginaCensorship = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("Vagina Censorship"));
VaginaCensorship->SetupAttachment(GetMesh(), FName(TEXT("pelvis")));
AnalCensorship = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("Anal Censorship"));
AnalCensorship->SetupAttachment(GetMesh(), FName(TEXT("pelvis")));
CensorshipComponent = CreateDefaultSubobject<UCensorshipComponent>(TEXT("Censorship Component"));
}
void APlayerCinematic::BeginPlay()
@@ -51,83 +35,6 @@ void APlayerCinematic::BeginPlay()
return;
}
Player->ClothingManager->OnClothingEquip.AddUniqueDynamic(this, &APlayerCinematic::OnClothingEquip);
Player->ClothingManager->OnClothingUnequip.AddUniqueDynamic(this, &APlayerCinematic::OnClothingUnequip);
for (const UClothingItemInstance* ClothingItemInstance : Player->ClothingManager->GetEquippedClothing())
{
EquipClothing(ClothingItemInstance);
}
}
void APlayerCinematic::OnClothingEquip(UClothingItemInstance* ClothingItemInstance)
{
EquipClothing(ClothingItemInstance);
}
void APlayerCinematic::OnClothingUnequip(UClothingItemInstance* ClothingItemInstance)
{
UnequipClothing(ClothingItemInstance);
}
USkeletalMeshComponent* APlayerCinematic::GetMeshByType(const EClothingSlotType SlotType) const
{
switch (SlotType)
{
case EClothingSlotType::Nipples:
return NipplesMeshComponent;
case EClothingSlotType::Anal:
return AnalMeshComponent;
case EClothingSlotType::Vagina:
return VaginaMeshComponent;
case EClothingSlotType::Head:
return HeadMeshComponent;
case EClothingSlotType::Neck:
return NeckMeshComponent;
case EClothingSlotType::Face:
return FaceMeshComponent;
case EClothingSlotType::Eyes:
return EyesMeshComponent;
case EClothingSlotType::Bodysuit:
return BodyMeshComponent;
case EClothingSlotType::Top:
return TopMeshComponent;
case EClothingSlotType::Bottom:
return BottomMeshComponent;
case EClothingSlotType::UnderwearTop:
return BraMeshComponent;
case EClothingSlotType::UnderwearBottom:
return PantiesMeshComponent;
case EClothingSlotType::Socks:
return SocksMeshComponent;
case EClothingSlotType::Footwear:
return ShoesMeshComponent;
default:
return NipplesMeshComponent;
}
}
void APlayerCinematic::EquipClothing(const UClothingItemInstance* ClothingItemInstance)
{
USkeletalMeshComponent* MeshComponent = GetMeshByType(ClothingItemInstance->GetClothingItem()->SlotType);
MeshComponent->SetSkeletalMesh(ClothingItemInstance->GetClothingItem()->SkeletalMesh);
if (ClothingItemInstance->GetClothingItem()->UseLeaderPose)
{
MeshComponent->SetLeaderPoseComponent(GetMesh());
}
if (!ClothingItemInstance->GetClothingItem()->Materials.IsEmpty())
{
for (const TPair<FName, UMaterialInstance*>& Material : ClothingItemInstance->GetClothingItem()->Materials)
{
MeshComponent->SetMaterialByName(Material.Key, Material.Value);
}
}
}
void APlayerCinematic::UnequipClothing(const UClothingItemInstance* ClothingItemInstance)
{
USkeletalMeshComponent* MeshComponent = GetMeshByType(ClothingItemInstance->GetClothingItem()->SlotType);
MeshComponent->SetSkeletalMesh(nullptr);
MeshComponent->SetLeaderPoseComponent(nullptr);
ClothingVisualsComponent->Initialize(GetMesh(), Player->ClothingManager);
CensorshipComponent->Initialize(Player->ClothingManager, BoobLCensorship, BoobRCensorship, VaginaCensorship, AnalCensorship);
}
+15 -49
View File
@@ -1,12 +1,13 @@
#pragma once
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Character.h"
#include "NakedDesire/Clothing/ClothingSlotType.h"
#include "PlayerCinematic.generated.h"
class UClothingItemInstance;
class ANakedDesireCharacter;
class UClothingVisualsComponent;
class UCensorshipComponent;
class UStaticMeshComponent;
UCLASS()
class NAKEDDESIRE_API APlayerCinematic : public ACharacter
@@ -14,46 +15,22 @@ class NAKEDDESIRE_API APlayerCinematic : public ACharacter
GENERATED_BODY()
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, meta = (AllowPrivateAccess = true), Category = "Clothing")
USkeletalMeshComponent* NipplesMeshComponent;
UClothingVisualsComponent* ClothingVisualsComponent;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, meta = (AllowPrivateAccess = true), Category = "Clothing")
USkeletalMeshComponent* AnalMeshComponent;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, meta = (AllowPrivateAccess = true), Category = "Censorship")
UStaticMeshComponent* BoobLCensorship;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, meta = (AllowPrivateAccess = true), Category = "Clothing")
USkeletalMeshComponent* VaginaMeshComponent;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, meta = (AllowPrivateAccess = true), Category = "Censorship")
UStaticMeshComponent* BoobRCensorship;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, meta = (AllowPrivateAccess = true), Category = "Clothing")
USkeletalMeshComponent* HeadMeshComponent;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, meta = (AllowPrivateAccess = true), Category = "Censorship")
UStaticMeshComponent* VaginaCensorship;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, meta = (AllowPrivateAccess = true), Category = "Clothing")
USkeletalMeshComponent* NeckMeshComponent;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, meta = (AllowPrivateAccess = true), Category = "Censorship")
UStaticMeshComponent* AnalCensorship;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, meta = (AllowPrivateAccess = true), Category = "Clothing")
USkeletalMeshComponent* FaceMeshComponent;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, meta = (AllowPrivateAccess = true), Category = "Clothing")
USkeletalMeshComponent* EyesMeshComponent;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, meta = (AllowPrivateAccess = true), Category = "Clothing")
USkeletalMeshComponent* BodyMeshComponent;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, meta = (AllowPrivateAccess = true), Category = "Clothing")
USkeletalMeshComponent* TopMeshComponent;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, meta = (AllowPrivateAccess = true), Category = "Clothing")
USkeletalMeshComponent* BottomMeshComponent;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, meta = (AllowPrivateAccess = true), Category = "Clothing")
USkeletalMeshComponent* BraMeshComponent;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, meta = (AllowPrivateAccess = true), Category = "Clothing")
USkeletalMeshComponent* PantiesMeshComponent;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, meta = (AllowPrivateAccess = true), Category = "Clothing")
USkeletalMeshComponent* SocksMeshComponent;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, meta = (AllowPrivateAccess = true), Category = "Clothing")
USkeletalMeshComponent* ShoesMeshComponent;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, meta = (AllowPrivateAccess = true), Category = "Censorship")
UCensorshipComponent* CensorshipComponent;
UPROPERTY()
ANakedDesireCharacter* Player;
@@ -62,15 +39,4 @@ public:
APlayerCinematic();
virtual void BeginPlay() override;
private:
UFUNCTION()
void OnClothingEquip(UClothingItemInstance* ClothingItemInstance);
UFUNCTION()
void OnClothingUnequip(UClothingItemInstance* ClothingItemInstance);
USkeletalMeshComponent* GetMeshByType(const EClothingSlotType SlotType) const;
void EquipClothing(const UClothingItemInstance* ClothingItemInstance);
void UnequipClothing(const UClothingItemInstance* ClothingItemInstance);
};
+16 -108
View File
@@ -1,9 +1,10 @@
#include "PlayerImpostor.h"
#include "PlayerImpostor.h"
#include "NakedDesireCharacter.h"
#include "Kismet/GameplayStatics.h"
#include "NakedDesire/Clothing/ClothingItem.h"
#include "NakedDesire/Clothing/ClothingItemInstance.h"
#include "NakedDesire/Censorship/CensorshipComponent.h"
#include "NakedDesire/Clothing/ClothingManager.h"
#include "NakedDesire/Clothing/ClothingVisualsComponent.h"
APlayerImpostor::APlayerImpostor()
@@ -16,44 +17,18 @@ APlayerImpostor::APlayerImpostor()
Mesh = CreateDefaultSubobject<USkeletalMeshComponent>(TEXT("RootMesh"));
Mesh->SetupAttachment(RootComponent);
NipplesMeshComponent = CreateDefaultSubobject<USkeletalMeshComponent>(TEXT("Nipples"));
NipplesMeshComponent->SetupAttachment(GetMesh());
AnalMeshComponent = CreateDefaultSubobject<USkeletalMeshComponent>(TEXT("Anal"));
AnalMeshComponent->SetupAttachment(GetMesh());
VaginaMeshComponent = CreateDefaultSubobject<USkeletalMeshComponent>(TEXT("Vagina"));
VaginaMeshComponent->SetupAttachment(GetMesh());
HeadMeshComponent = CreateDefaultSubobject<USkeletalMeshComponent>(TEXT("Head"));
HeadMeshComponent->SetupAttachment(GetMesh());
NeckMeshComponent = CreateDefaultSubobject<USkeletalMeshComponent>(TEXT("Neck"));
NeckMeshComponent->SetupAttachment(GetMesh());
FaceMeshComponent = CreateDefaultSubobject<USkeletalMeshComponent>(TEXT("Face"));
FaceMeshComponent->SetupAttachment(GetMesh());
EyesMeshComponent = CreateDefaultSubobject<USkeletalMeshComponent>(TEXT("Eyes"));
EyesMeshComponent->SetupAttachment(GetMesh());
BodyMeshComponent = CreateDefaultSubobject<USkeletalMeshComponent>(TEXT("Body"));
BodyMeshComponent->SetupAttachment(GetMesh());
TopMeshComponent = CreateDefaultSubobject<USkeletalMeshComponent>(TEXT("Top"));
TopMeshComponent->SetupAttachment(GetMesh());
BottomMeshComponent = CreateDefaultSubobject<USkeletalMeshComponent>(TEXT("Bottom"));
BottomMeshComponent->SetupAttachment(GetMesh());
BraMeshComponent = CreateDefaultSubobject<USkeletalMeshComponent>(TEXT("Bra"));
BraMeshComponent->SetupAttachment(GetMesh());
PantiesMeshComponent = CreateDefaultSubobject<USkeletalMeshComponent>(TEXT("Panties"));
PantiesMeshComponent->SetupAttachment(GetMesh());
SocksMeshComponent = CreateDefaultSubobject<USkeletalMeshComponent>(TEXT("Socks"));
SocksMeshComponent->SetupAttachment(GetMesh());
ShoesMeshComponent = CreateDefaultSubobject<USkeletalMeshComponent>(TEXT("Shoes"));
ShoesMeshComponent->SetupAttachment(GetMesh());
}
ClothingVisualsComponent = CreateDefaultSubobject<UClothingVisualsComponent>(TEXT("Clothing Visuals Component"));
void APlayerImpostor::OnClothingEquip(UClothingItemInstance* ClothingItemInstance)
{
EquipClothing(ClothingItemInstance);
}
BoobLCensorship = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("Boob L Censorship"));
BoobLCensorship->SetupAttachment(GetMesh(), FName(TEXT("boob_l")));
BoobRCensorship = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("Boob R Censorship"));
BoobRCensorship->SetupAttachment(GetMesh(), FName(TEXT("boob_r")));
VaginaCensorship = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("Vagina Censorship"));
VaginaCensorship->SetupAttachment(GetMesh(), FName(TEXT("pelvis")));
AnalCensorship = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("Anal Censorship"));
AnalCensorship->SetupAttachment(GetMesh(), FName(TEXT("pelvis")));
void APlayerImpostor::OnClothingUnequip(UClothingItemInstance* ClothingItemInstance)
{
UnequipClothing(ClothingItemInstance);
CensorshipComponent = CreateDefaultSubobject<UCensorshipComponent>(TEXT("Censorship Component"));
}
void APlayerImpostor::BeginPlay()
@@ -66,73 +41,6 @@ void APlayerImpostor::BeginPlay()
return;
}
Player->ClothingManager->OnClothingEquip.AddUniqueDynamic(this, &APlayerImpostor::OnClothingEquip);
Player->ClothingManager->OnClothingUnequip.AddUniqueDynamic(this, &APlayerImpostor::OnClothingUnequip);
for (const UClothingItemInstance* ClothingItemInstance : Player->ClothingManager->GetEquippedClothing())
{
EquipClothing(ClothingItemInstance);
}
}
USkeletalMeshComponent* APlayerImpostor::GetMeshByType(const EClothingSlotType SlotType) const
{
switch (SlotType)
{
case EClothingSlotType::Nipples:
return NipplesMeshComponent;
case EClothingSlotType::Anal:
return AnalMeshComponent;
case EClothingSlotType::Vagina:
return VaginaMeshComponent;
case EClothingSlotType::Head:
return HeadMeshComponent;
case EClothingSlotType::Neck:
return NeckMeshComponent;
case EClothingSlotType::Face:
return FaceMeshComponent;
case EClothingSlotType::Eyes:
return EyesMeshComponent;
case EClothingSlotType::Bodysuit:
return BodyMeshComponent;
case EClothingSlotType::Top:
return TopMeshComponent;
case EClothingSlotType::Bottom:
return BottomMeshComponent;
case EClothingSlotType::UnderwearTop:
return BraMeshComponent;
case EClothingSlotType::UnderwearBottom:
return PantiesMeshComponent;
case EClothingSlotType::Socks:
return SocksMeshComponent;
case EClothingSlotType::Footwear:
return ShoesMeshComponent;
default:
return NipplesMeshComponent;
}
}
void APlayerImpostor::EquipClothing(const UClothingItemInstance* ClothingItemInstance)
{
USkeletalMeshComponent* MeshComponent = GetMeshByType(ClothingItemInstance->GetClothingItem()->SlotType);
MeshComponent->SetSkeletalMesh(ClothingItemInstance->GetClothingItem()->SkeletalMesh);
if (ClothingItemInstance->GetClothingItem()->UseLeaderPose)
{
MeshComponent->SetLeaderPoseComponent(GetMesh());
}
if (!ClothingItemInstance->GetClothingItem()->Materials.IsEmpty())
{
for (const TPair<FName, UMaterialInstance*>& Material : ClothingItemInstance->GetClothingItem()->Materials)
{
MeshComponent->SetMaterialByName(Material.Key, Material.Value);
}
}
}
void APlayerImpostor::UnequipClothing(const UClothingItemInstance* ClothingItemInstance)
{
USkeletalMeshComponent* MeshComponent = GetMeshByType(ClothingItemInstance->GetClothingItem()->SlotType);
MeshComponent->SetSkeletalMesh(nullptr);
MeshComponent->SetLeaderPoseComponent(nullptr);
ClothingVisualsComponent->Initialize(GetMesh(), Player->ClothingManager);
CensorshipComponent->Initialize(Player->ClothingManager, BoobLCensorship, BoobRCensorship, VaginaCensorship, AnalCensorship);
}
+17 -49
View File
@@ -1,12 +1,13 @@
#pragma once
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "NakedDesire/Clothing/ClothingSlotType.h"
#include "GameFramework/Pawn.h"
#include "PlayerImpostor.generated.h"
class UClothingItemInstance;
class ANakedDesireCharacter;
class UClothingVisualsComponent;
class UCensorshipComponent;
class UStaticMeshComponent;
UCLASS()
class NAKEDDESIRE_API APlayerImpostor : public APawn
@@ -20,46 +21,22 @@ class NAKEDDESIRE_API APlayerImpostor : public APawn
USkeletalMeshComponent* Mesh;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, meta = (AllowPrivateAccess = true), Category = "Clothing")
USkeletalMeshComponent* NipplesMeshComponent;
UClothingVisualsComponent* ClothingVisualsComponent;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, meta = (AllowPrivateAccess = true), Category = "Clothing")
USkeletalMeshComponent* AnalMeshComponent;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, meta = (AllowPrivateAccess = true), Category = "Censorship")
UStaticMeshComponent* BoobLCensorship;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, meta = (AllowPrivateAccess = true), Category = "Clothing")
USkeletalMeshComponent* VaginaMeshComponent;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, meta = (AllowPrivateAccess = true), Category = "Censorship")
UStaticMeshComponent* BoobRCensorship;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, meta = (AllowPrivateAccess = true), Category = "Clothing")
USkeletalMeshComponent* HeadMeshComponent;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, meta = (AllowPrivateAccess = true), Category = "Censorship")
UStaticMeshComponent* VaginaCensorship;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, meta = (AllowPrivateAccess = true), Category = "Clothing")
USkeletalMeshComponent* NeckMeshComponent;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, meta = (AllowPrivateAccess = true), Category = "Censorship")
UStaticMeshComponent* AnalCensorship;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, meta = (AllowPrivateAccess = true), Category = "Clothing")
USkeletalMeshComponent* FaceMeshComponent;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, meta = (AllowPrivateAccess = true), Category = "Clothing")
USkeletalMeshComponent* EyesMeshComponent;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, meta = (AllowPrivateAccess = true), Category = "Clothing")
USkeletalMeshComponent* BodyMeshComponent;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, meta = (AllowPrivateAccess = true), Category = "Clothing")
USkeletalMeshComponent* TopMeshComponent;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, meta = (AllowPrivateAccess = true), Category = "Clothing")
USkeletalMeshComponent* BottomMeshComponent;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, meta = (AllowPrivateAccess = true), Category = "Clothing")
USkeletalMeshComponent* BraMeshComponent;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, meta = (AllowPrivateAccess = true), Category = "Clothing")
USkeletalMeshComponent* PantiesMeshComponent;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, meta = (AllowPrivateAccess = true), Category = "Clothing")
USkeletalMeshComponent* SocksMeshComponent;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, meta = (AllowPrivateAccess = true), Category = "Clothing")
USkeletalMeshComponent* ShoesMeshComponent;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, meta = (AllowPrivateAccess = true), Category = "Censorship")
UCensorshipComponent* CensorshipComponent;
UPROPERTY()
ANakedDesireCharacter* Player;
@@ -70,14 +47,5 @@ public:
virtual void BeginPlay() override;
private:
UFUNCTION()
void OnClothingEquip(UClothingItemInstance* ClothingItemInstance);
UFUNCTION()
void OnClothingUnequip(UClothingItemInstance* ClothingItemInstance);
USkeletalMeshComponent* GetMesh() const { return Mesh; };
USkeletalMeshComponent* GetMeshByType(const EClothingSlotType SlotType) const;
void EquipClothing(const UClothingItemInstance* ClothingItemInstance);
void UnequipClothing(const UClothingItemInstance* ClothingItemInstance);
USkeletalMeshComponent* GetMesh() const { return Mesh; }
};
@@ -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,21 +34,20 @@ 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;
}
}
@@ -55,7 +55,7 @@ bool UGlobalSaveGameData::UpdateWardrobeItem(UClothingItemInstance* ItemInstance
return false;
}
bool UGlobalSaveGameData::RemoveWardrobeItem(UClothingItemInstance* ItemInstance)
bool UGlobalSaveGameData::RemoveWardrobeItem(UItemInstance* ItemInstance)
{
const int32 RemovedElementsCount = WardrobeItems.RemoveAll([ItemInstance](const FItemSaveRecord& Item)
{
@@ -65,21 +65,25 @@ bool UGlobalSaveGameData::RemoveWardrobeItem(UClothingItemInstance* ItemInstance
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;
}
}
@@ -87,7 +91,7 @@ bool UGlobalSaveGameData::UpdateEquippedItem(UClothingItemInstance* ItemInstance
return false;
}
bool UGlobalSaveGameData::RemoveEquippedItem(UClothingItemInstance* ItemInstance)
bool UGlobalSaveGameData::RemoveEquippedItem(UItemInstance* ItemInstance)
{
const int32 RemovedElementsCount = EquippedItems.RemoveAll([ItemInstance](const FItemSaveRecord& Item)
{
@@ -97,22 +101,21 @@ bool UGlobalSaveGameData::RemoveEquippedItem(UClothingItemInstance* ItemInstance
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;
}
@@ -121,7 +124,7 @@ bool UGlobalSaveGameData::UpdateWorldItem(UClothingItemInstance* ItemInstance, F
return false;
}
bool UGlobalSaveGameData::RemoveWorldItem(UClothingItemInstance* ItemInstance)
bool UGlobalSaveGameData::RemoveWorldItem(UItemInstance* ItemInstance)
{
const int32 RemovedElementsCount = WorldItems.RemoveAll([ItemInstance](const FItemSaveRecord& Item)
{
@@ -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)
+11 -14
View File
@@ -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
{
@@ -16,23 +22,14 @@ struct NAKEDDESIRE_API FItemSaveRecord
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();