// © 2025 Naked People Team. All Rights Reserved. #include "InventorySubsystem.h" #include "Kismet/GameplayStatics.h" #include "NakedDesire/Clothing/ClothingItemDefinition.h" #include "NakedDesire/Clothing/ClothingItemInstance.h" #include "NakedDesire/Clothing/ClothingManager.h" #include "NakedDesire/Items/ItemInstance.h" #include "NakedDesire/Player/NakedDesireCharacter.h" #include "NakedDesire/SaveGame/GlobalSaveGameData.h" #include "NakedDesire/SaveGame/ItemSaveRecord.h" #include "NakedDesire/SaveGame/SaveSubsystem.h" const TArray>& UInventorySubsystem::GetWardrobeItems() { EnsureHydrated(); return WardrobeItems; } void UInventorySubsystem::AddToWardrobe(UItemInstance* Item) { EnsureHydrated(); StoreItem(Item); OnWardrobeChanged.Broadcast(); } void UInventorySubsystem::RemoveFromWardrobe(UItemInstance* Item) { EnsureHydrated(); UnstoreItem(Item); OnWardrobeChanged.Broadcast(); } void UInventorySubsystem::EquipFromWardrobe(UItemInstance* Item) { EnsureHydrated(); UClothingItemInstance* Clothing = Cast(Item); if (!Clothing || !Clothing->GetClothingItemDefinition()) return; UClothingManager* ClothingManager = GetPlayerClothingManager(); if (!ClothingManager) return; const EClothingSlotType SlotType = Clothing->GetClothingItemDefinition()->SlotType; // Return the current occupant of the target slot to the wardrobe (never drop it to the world). if (UClothingItemInstance* Occupant = ClothingManager->GetSlotClothing(SlotType)) { ClothingManager->RemoveClothing(SlotType); StoreItem(Occupant); } // Bodysuit exclusion (§6.5): vacate conflicting slots back into the wardrobe. for (const EClothingSlotType ExcludedSlot : UClothingManager::GetBodysuitExcludedSlots(SlotType)) { if (UClothingItemInstance* Excluded = ClothingManager->GetSlotClothing(ExcludedSlot)) { ClothingManager->RemoveClothing(ExcludedSlot); StoreItem(Excluded); } } UnstoreItem(Clothing); ClothingManager->EquipSlot(Clothing); OnWardrobeChanged.Broadcast(); } void UInventorySubsystem::UnequipToWardrobe(UItemInstance* Item) { EnsureHydrated(); UClothingItemInstance* Clothing = Cast(Item); if (!Clothing || !Clothing->GetClothingItemDefinition()) return; UClothingManager* ClothingManager = GetPlayerClothingManager(); if (!ClothingManager) return; UClothingItemInstance* Removed = ClothingManager->RemoveClothing(Clothing->GetClothingItemDefinition()->SlotType); if (!Removed) return; StoreItem(Removed); OnWardrobeChanged.Broadcast(); } void UInventorySubsystem::EnsureHydrated() { UGlobalSaveGameData* Save = GetSave(); if (!Save) return; // Re-hydrate only when the underlying save object changes (fresh game / load game). if (HydratedSave.Get() == Save) return; HydratedSave = Save; WardrobeItems.Reset(); for (const FItemSaveRecord& Record : Save->GetWardrobeItems()) { if (UItemInstance* Instance = UItemInstance::CreateFromRecord(this, Record)) WardrobeItems.Add(Instance); } } void UInventorySubsystem::StoreItem(UItemInstance* Item) { if (!Item || WardrobeItems.Contains(Item)) return; WardrobeItems.Add(Item); if (UGlobalSaveGameData* Save = GetSave()) Save->AddWardrobeItem(Item); } void UInventorySubsystem::UnstoreItem(UItemInstance* Item) { if (!Item) return; WardrobeItems.Remove(Item); if (UGlobalSaveGameData* Save = GetSave()) Save->RemoveWardrobeItem(Item); } UClothingManager* UInventorySubsystem::GetPlayerClothingManager() const { const ANakedDesireCharacter* Player = Cast(UGameplayStatics::GetPlayerCharacter(GetGameInstance(), 0)); return Player ? Player->ClothingManager : nullptr; } UGlobalSaveGameData* UInventorySubsystem::GetSave() const { USaveSubsystem* SaveSubsystem = GetGameInstance() ? GetGameInstance()->GetSubsystem() : nullptr; return SaveSubsystem ? SaveSubsystem->GetCurrentSave() : nullptr; }