205 lines
6.1 KiB
C++
205 lines
6.1 KiB
C++
// © 2025 Naked People Team. All Rights Reserved.
|
|
|
|
|
|
#include "ClothingManager.h"
|
|
#include "ClothingItemDefinition.h"
|
|
#include "ClothingItemInstance.h"
|
|
#include "GameFramework/Character.h"
|
|
#include "Kismet/GameplayStatics.h"
|
|
#include "NakedDesire/Interactables/ItemPickup.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();
|
|
}
|
|
|
|
void UClothingManager::SetClothingSlotItem(const EClothingSlotType ClothingSlotType, UClothingItemInstance* ClothingItemInstance)
|
|
{
|
|
if (ClothingItemInstance)
|
|
EquippedClothing.Add(ClothingSlotType, ClothingItemInstance);
|
|
else
|
|
EquippedClothing.Remove(ClothingSlotType);
|
|
}
|
|
|
|
TArray<UClothingItemInstance*> UClothingManager::GetEquippedClothing() const
|
|
{
|
|
TArray<TObjectPtr<UClothingItemInstance>> Items;
|
|
EquippedClothing.GenerateValueArray(Items);
|
|
return Items;
|
|
}
|
|
|
|
UClothingItemInstance* UClothingManager::GetSlotClothing(const EClothingSlotType SlotType)
|
|
{
|
|
if (EquippedClothing.Contains(SlotType))
|
|
return EquippedClothing[SlotType];
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
bool UClothingManager::IsBodyPartExposed(const EBodyPart BodyPart)
|
|
{
|
|
for (const auto& [Key, Value] : EquippedClothing)
|
|
{
|
|
if (Value->GetClothingItemDefinition()->HiddenBodyParts.Contains(BodyPart))
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
float UClothingManager::GetEffectiveCoverage(const EBodyPart BodyPart)
|
|
{
|
|
// GDD §6.3.2: effective coverage of a part = max() across garments that include it.
|
|
// A garment that doesn't cover the part contributes nothing; nothing covering it = 0.
|
|
float MaxCoverage = 0.0f;
|
|
for (const auto& [Slot, Instance] : EquippedClothing)
|
|
{
|
|
if (!Instance)
|
|
continue;
|
|
|
|
const UClothingItemDefinition* Definition = Instance->GetClothingItemDefinition();
|
|
if (!Definition)
|
|
continue;
|
|
|
|
for (const FBodyPartCoverage& Coverage : Definition->CoveredBodyParts)
|
|
{
|
|
if (Coverage.BodyPart == BodyPart)
|
|
MaxCoverage = FMath::Max(MaxCoverage, Coverage.Coverage);
|
|
}
|
|
}
|
|
|
|
return MaxCoverage;
|
|
}
|
|
|
|
void UClothingManager::HydrateClothing()
|
|
{
|
|
USaveSubsystem* SaveSubsystem = UGameplayStatics::GetGameInstance(GetWorld())->GetSubsystem<USaveSubsystem>();
|
|
for (const FItemSaveRecord& ItemSaveRecord : SaveSubsystem->GetCurrentSave()->GetEquippedItems())
|
|
{
|
|
UClothingItemInstance* ClothingItemInstance = Cast<UClothingItemInstance>(UItemInstance::CreateFromRecord(this, ItemSaveRecord));
|
|
if (!ClothingItemInstance)
|
|
continue;
|
|
|
|
PutOnClothing(ClothingItemInstance);
|
|
}
|
|
}
|
|
|
|
float UClothingManager::GetHeelHeight()
|
|
{
|
|
if (!EquippedClothing.Contains(EClothingSlotType::Footwear))
|
|
return 0;
|
|
|
|
const UClothingItemInstance* Footwear = EquippedClothing[EClothingSlotType::Footwear];
|
|
|
|
return Footwear->GetClothingItemDefinition()->ShoesOffset;
|
|
}
|
|
|
|
void UClothingManager::SpawnClothingPickup(UClothingItemInstance* ItemInstance)
|
|
{
|
|
if (!ItemPickupActor)
|
|
{
|
|
UE_LOG(LogTemp, Warning, TEXT("UClothingManager::SpawnClothingPickup ItemPickupActor is not set"));
|
|
return;
|
|
}
|
|
|
|
AItemPickup* NewItemPickup = GetWorld()->SpawnActor<AItemPickup>(ItemPickupActor, GetOwner()->GetActorTransform());
|
|
if (!NewItemPickup)
|
|
{
|
|
UE_LOG(LogTemp, Warning, TEXT("UClothingManager::SpawnClothingPickup NewItemPickup == nullptr"));
|
|
return;
|
|
}
|
|
|
|
USaveSubsystem* SaveSubsystem = UGameplayStatics::GetGameInstance(GetWorld())->GetSubsystem<USaveSubsystem>();
|
|
SaveSubsystem->GetCurrentSave()->AddWorldItem(ItemInstance, NewItemPickup->GetActorTransform());
|
|
|
|
NewItemPickup->SetItem(ItemInstance);
|
|
}
|
|
|
|
void UClothingManager::PutOnClothing(UClothingItemInstance* ClothingItemInstance)
|
|
{
|
|
if (!ClothingItemInstance)
|
|
return;
|
|
|
|
const EClothingSlotType ClothingSlotType = ClothingItemInstance->GetClothingItemDefinition()->SlotType;
|
|
|
|
SetClothingSlotItem(ClothingSlotType, ClothingItemInstance);
|
|
|
|
const UClothingItemDefinition* ClothingItem = ClothingItemInstance->GetClothingItemDefinition();
|
|
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)
|
|
{
|
|
const EClothingSlotType SlotType = ClothingItemInstance->GetClothingItemDefinition()->SlotType;
|
|
if (EquippedClothing.Contains(SlotType))
|
|
{
|
|
DropClothing(SlotType);
|
|
}
|
|
|
|
SetClothingSlotItem(SlotType, ClothingItemInstance);
|
|
|
|
USaveSubsystem* SaveSubsystem = UGameplayStatics::GetGameInstance(GetWorld())->GetSubsystem<USaveSubsystem>();
|
|
SaveSubsystem->GetCurrentSave()->AddEquippedItem(ClothingItemInstance);
|
|
|
|
PutOnClothing(ClothingItemInstance);
|
|
}
|
|
|
|
UClothingItemInstance* UClothingManager::RemoveClothing(const EClothingSlotType ClothingSlotType)
|
|
{
|
|
if (!EquippedClothing.Contains(ClothingSlotType))
|
|
return nullptr;
|
|
|
|
UClothingItemInstance* ExistingItem = EquippedClothing[ClothingSlotType];
|
|
|
|
SetClothingSlotItem(ClothingSlotType, nullptr);
|
|
|
|
OnClothingUnequip.Broadcast(ExistingItem);
|
|
|
|
USaveSubsystem* SaveSubsystem = UGameplayStatics::GetGameInstance(GetWorld())->GetSubsystem<USaveSubsystem>();
|
|
SaveSubsystem->GetCurrentSave()->RemoveEquippedItem(ExistingItem);
|
|
|
|
return ExistingItem;
|
|
}
|
|
|
|
void UClothingManager::DropClothing(const EClothingSlotType ClothingType)
|
|
{
|
|
UClothingItemInstance* ClothingItemInstance = RemoveClothing(ClothingType);
|
|
if (!ClothingItemInstance)
|
|
return;
|
|
|
|
SpawnClothingPickup(ClothingItemInstance);
|
|
|
|
OnClothingDropped.Broadcast(ClothingItemInstance);
|
|
}
|
|
|
|
bool UClothingManager::IsClothingTypeOn(const EClothingSlotType ClothingType)
|
|
{
|
|
return EquippedClothing.Contains(ClothingType);
|
|
}
|