260 lines
8.6 KiB
C++
260 lines
8.6 KiB
C++
// © 2025 Naked People Team. All Rights Reserved.
|
|
|
|
|
|
#include "ClothingManager.h"
|
|
#include "ClothingItem.h"
|
|
#include "ClothingItemInstance.h"
|
|
#include "GameFramework/Character.h"
|
|
#include "Kismet/GameplayStatics.h"
|
|
#include "NakedDesire/Player/NakedDesireCharacter.h"
|
|
#include "NakedDesire/SaveGame/GlobalSaveGameData.h"
|
|
#include "NakedDesire/SaveGame/ItemSaveRecord.h"
|
|
#include "NakedDesire/SaveGame/SaveSubsystem.h"
|
|
|
|
UClothingManager::UClothingManager()
|
|
{
|
|
PrimaryComponentTick.bCanEverTick = false;
|
|
}
|
|
|
|
void UClothingManager::BeginPlay()
|
|
{
|
|
Super::BeginPlay();
|
|
|
|
HydrateClothing();
|
|
}
|
|
|
|
bool UClothingManager::IsBodyTypeExposed(const EBodyPart BodyPart)
|
|
{
|
|
for (const auto& ClothingSlot : ClothingSlots)
|
|
{
|
|
if (ClothingSlot.ClothingItemInstance) // TODO: Add covered body part resolution
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool UClothingManager::GetClothingSlotData(const EClothingSlotType ClothingSlotType, FClothingSlotData& OutClothingSlotData)
|
|
{
|
|
for (const auto& ClothingSlot : ClothingSlots)
|
|
{
|
|
if (ClothingSlot.ClothingSlotType == ClothingSlotType)
|
|
{
|
|
OutClothingSlotData = ClothingSlot;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
void UClothingManager::SetClothingSlotItem(const EClothingSlotType ClothingSlotType, UClothingItemInstance* ClothingItemInstance)
|
|
{
|
|
for (FClothingSlotData& ClothingSlot : ClothingSlots)
|
|
{
|
|
if (ClothingSlot.ClothingSlotType == ClothingSlotType)
|
|
{
|
|
ClothingSlot.ClothingItemInstance = ClothingItemInstance;
|
|
}
|
|
}
|
|
}
|
|
|
|
TArray<UClothingItemInstance*> UClothingManager::GetEquippedClothing()
|
|
{
|
|
TArray<UClothingItemInstance*> EquippedClothingItems;
|
|
|
|
for (FClothingSlotData ClothingSlot : ClothingSlots)
|
|
{
|
|
if (ClothingSlot.ClothingItemInstance)
|
|
{
|
|
EquippedClothingItems.Add(ClothingSlot.ClothingItemInstance);
|
|
}
|
|
}
|
|
|
|
return EquippedClothingItems;
|
|
}
|
|
|
|
void UClothingManager::HydrateClothing()
|
|
{
|
|
USaveSubsystem* SaveSubsystem = UGameplayStatics::GetGameInstance(GetWorld())->GetSubsystem<USaveSubsystem>();
|
|
for (const FItemSaveRecord& ItemSaveRecord : SaveSubsystem->GetCurrentSave()->EquippedItems)
|
|
{
|
|
UClothingItemInstance* ClothingItemInstance = UClothingItemInstance::CreateFromSave(this, ItemSaveRecord);
|
|
PutOnClothing(ClothingItemInstance);
|
|
}
|
|
}
|
|
|
|
float UClothingManager::GetHeelHeight()
|
|
{
|
|
if (FClothingSlotData ClothingSlotData; GetClothingSlotData(EClothingSlotType::Footwear, ClothingSlotData))
|
|
{
|
|
if (ClothingSlotData.ClothingItemInstance)
|
|
{
|
|
return ClothingSlotData.ClothingItemInstance->GetClothingItem()->ShoesOffset;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
USkeletalMeshComponent* UClothingManager::GetMeshComponent(const EClothingSlotType SlotType) const
|
|
{
|
|
const ANakedDesireCharacter* Player = Cast<ANakedDesireCharacter>(GetOwner());
|
|
if (!Player)
|
|
{
|
|
return nullptr;
|
|
}
|
|
switch (SlotType)
|
|
{
|
|
case EClothingSlotType::Nipples: return Player->NipplesMeshComponent;
|
|
case EClothingSlotType::Anal: return Player->AnalMeshComponent;
|
|
case EClothingSlotType::Vagina: return Player->VaginaMeshComponent;
|
|
case EClothingSlotType::Head: return Player->HeadMeshComponent;
|
|
case EClothingSlotType::Neck: return Player->NeckMeshComponent;
|
|
case EClothingSlotType::Face: return Player->FaceMeshComponent;
|
|
case EClothingSlotType::Eyes: return Player->EyesMeshComponent;
|
|
case EClothingSlotType::Bodysuit: return Player->BodySuitMeshComponent;
|
|
case EClothingSlotType::Top: return Player->TopMeshComponent;
|
|
case EClothingSlotType::Bottom: return Player->BottomMeshComponent;
|
|
case EClothingSlotType::UnderwearTop: return Player->UnderwearTopMeshComponent;
|
|
case EClothingSlotType::UnderwearBottom:return Player->UnderwearBottomMeshComponent;
|
|
case EClothingSlotType::Socks: return Player->SocksMeshComponent;
|
|
case EClothingSlotType::Footwear: return Player->FootwearMeshComponent;
|
|
case EClothingSlotType::Outerwear: return Player->OuterwearMeshComponent;
|
|
case EClothingSlotType::WristRestraint: return Player->WristRestraintMeshComponent;
|
|
case EClothingSlotType::AnkleRestraint: return Player->AnkleRestraintMeshComponent;
|
|
case EClothingSlotType::NeckRestraint: return Player->NeckRestraintMeshComponent;
|
|
default: return nullptr;
|
|
}
|
|
}
|
|
|
|
void UClothingManager::PutOnClothing(UClothingItemInstance* ClothingItemInstance)
|
|
{
|
|
if (!ClothingItemInstance)
|
|
return;
|
|
|
|
const EClothingSlotType ClothingSlotType = ClothingItemInstance->GetClothingItem()->SlotType;
|
|
|
|
FClothingSlotData ClothingSlotData;
|
|
GetClothingSlotData(ClothingSlotType, ClothingSlotData);
|
|
|
|
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);
|
|
}
|
|
}
|
|
|
|
SetClothingSlotItem(ClothingSlotType, ClothingItemInstance);
|
|
if (ClothingItemInstance->GetClothingItem()->UseLeaderPose)
|
|
{
|
|
MeshComponent->SetLeaderPoseComponent(Cast<ACharacter>(GetOwner())->GetMesh());
|
|
}
|
|
|
|
UClothingItem* ClothingItem = ClothingItemInstance->GetClothingItem();
|
|
if (ClothingItem->SlotType == EClothingSlotType::Bodysuit)
|
|
{
|
|
DropClothing(EClothingSlotType::Top);
|
|
DropClothing(EClothingSlotType::Bottom);
|
|
DropClothing(EClothingSlotType::UnderwearTop);
|
|
DropClothing(EClothingSlotType::UnderwearBottom);
|
|
}
|
|
else if (ClothingItem->SlotType == EClothingSlotType::Top ||
|
|
ClothingItem->SlotType == EClothingSlotType::Bottom ||
|
|
ClothingItem->SlotType == EClothingSlotType::UnderwearTop ||
|
|
ClothingItem->SlotType == EClothingSlotType::UnderwearBottom)
|
|
{
|
|
DropClothing(EClothingSlotType::Bodysuit);
|
|
}
|
|
|
|
OnClothingEquip.Broadcast(ClothingItemInstance);
|
|
}
|
|
|
|
void UClothingManager::TakeClothing(UClothingItemInstance* ClothingItemInstance)
|
|
{
|
|
FClothingSlotData ClothingSlotData;
|
|
GetClothingSlotData(ClothingItemInstance->GetClothingItem()->SlotType, ClothingSlotData);
|
|
|
|
if (ClothingSlotData.ClothingItemInstance->GetClothingItem())
|
|
{
|
|
DropClothing(ClothingItemInstance->GetClothingItem()->SlotType);
|
|
}
|
|
|
|
ClothingSlotData.ClothingItemInstance = ClothingItemInstance;
|
|
|
|
USaveSubsystem* SaveSubsystem = UGameplayStatics::GetGameInstance(GetWorld())->GetSubsystem<USaveSubsystem>();
|
|
FItemSaveRecord ItemSaveRecord;
|
|
ItemSaveRecord.Init(ClothingItemInstance);
|
|
|
|
SaveSubsystem->GetCurrentSave()->EquippedItems.Push(ItemSaveRecord);
|
|
SaveSubsystem->GetCurrentSave()->DroppedItems.RemoveAll([ClothingItemInstance](const FItemSaveRecord& Item)
|
|
{
|
|
return Item.InstanceId == ClothingItemInstance->GetInstanceId();
|
|
});
|
|
|
|
PutOnClothing(ClothingItemInstance);
|
|
}
|
|
|
|
UClothingItemInstance* UClothingManager::RemoveClothing(const EClothingSlotType ClothingSlotType)
|
|
{
|
|
FClothingSlotData ClothingSlotData;
|
|
if (!GetClothingSlotData(ClothingSlotType, ClothingSlotData) || !ClothingSlotData.ClothingItemInstance)
|
|
{
|
|
UE_LOG(LogTemp, Error, TEXT("Couldn't find clothing slot"));
|
|
return nullptr;
|
|
}
|
|
|
|
UClothingItemInstance* ClothingItemInstance = ClothingSlotData.ClothingItemInstance;
|
|
SetClothingSlotItem(ClothingSlotType, nullptr);
|
|
|
|
USkeletalMeshComponent* MeshComponent = GetMeshComponent(ClothingSlotType);
|
|
MeshComponent->SetSkeletalMesh(nullptr);
|
|
|
|
if (ClothingItemInstance->GetClothingItem()->UseLeaderPose)
|
|
{
|
|
MeshComponent->SetLeaderPoseComponent(nullptr);
|
|
}
|
|
|
|
OnClothingUnequip.Broadcast(ClothingItemInstance);
|
|
|
|
USaveSubsystem* SaveSubsystem = UGameplayStatics::GetGameInstance(GetWorld())->GetSubsystem<USaveSubsystem>();
|
|
SaveSubsystem->GetCurrentSave()->EquippedItems.RemoveAll([ClothingItemInstance](const FItemSaveRecord& Item)
|
|
{
|
|
return Item.InstanceId == ClothingItemInstance->GetInstanceId();
|
|
});
|
|
|
|
return ClothingItemInstance;
|
|
}
|
|
|
|
void UClothingManager::DropClothing(const EClothingSlotType ClothingType)
|
|
{
|
|
const UClothingItemInstance* ClothingItemInstance = RemoveClothing(ClothingType);
|
|
if (!ClothingItemInstance)
|
|
return;
|
|
|
|
USaveSubsystem* SaveSubsystem = UGameplayStatics::GetGameInstance(GetWorld())->GetSubsystem<USaveSubsystem>();
|
|
FItemSaveRecord ItemSaveRecord;
|
|
ItemSaveRecord.Init(ClothingItemInstance);
|
|
SaveSubsystem->GetCurrentSave()->DroppedItems.Push(ItemSaveRecord);
|
|
|
|
OnClothingDropped.Broadcast(ClothingItemInstance);
|
|
}
|
|
|
|
bool UClothingManager::IsClothingTypeOn(const EClothingSlotType ClothingType)
|
|
{
|
|
FClothingSlotData ClothingSlotData;
|
|
GetClothingSlotData(ClothingType, ClothingSlotData);
|
|
|
|
const bool IsTypeOn = ClothingSlotData.ClothingItemInstance != nullptr;
|
|
return IsTypeOn;
|
|
}
|
|
|
|
bool UClothingManager::IsPartiallyNaked()
|
|
{
|
|
return IsBodyTypeExposed(EBodyPart::Ass) || IsBodyTypeExposed(EBodyPart::Genitals) || IsBodyTypeExposed(EBodyPart::Boobs);
|
|
}
|