From 43ddad9217ba5eb719cc187253e39ed5c351a3b6 Mon Sep 17 00:00:00 2001 From: koritsa Date: Mon, 25 May 2026 14:14:52 +0300 Subject: [PATCH] Completed phase 1 --- README.md | 50 ++++++++++++++- .../Clothing/ClothingItemInstance.cpp | 26 ++++++++ .../Clothing/ClothingItemInstance.h | 6 ++ .../NakedDesire/Clothing/ClothingManager.cpp | 61 +++++++++++++++++-- Source/NakedDesire/Clothing/ClothingManager.h | 5 +- .../NakedDesire/Clothing/ClothingSlotType.h | 2 +- .../Global/NakedDesireGameMode.cpp | 26 +++++--- .../NakedDesire/Global/NakedDesireGameMode.h | 5 -- .../Interactables/InteractableBase.h | 1 - Source/NakedDesire/Interactables/Wardrobe.cpp | 39 ++++++++++++ Source/NakedDesire/Interactables/Wardrobe.h | 5 ++ Source/NakedDesire/Items/ItemInstance.cpp | 6 +- Source/NakedDesire/Items/ItemInstance.h | 6 +- Source/NakedDesire/Items/SexToyItem.h | 10 +-- .../Player/NakedDesireCharacter.cpp | 4 +- Source/NakedDesire/Player/PlayerCinematic.cpp | 2 +- Source/NakedDesire/Player/PlayerImpostor.cpp | 2 +- .../SaveGame/GlobalSaveGameData.cpp | 13 ++-- .../NakedDesire/SaveGame/GlobalSaveGameData.h | 5 +- Source/NakedDesire/SaveGame/ItemSaveRecord.h | 16 ++++- Source/NakedDesire/SaveGame/SaveSubsystem.cpp | 16 ++++- Source/NakedDesire/SaveGame/SaveSubsystem.h | 4 +- 22 files changed, 255 insertions(+), 55 deletions(-) diff --git a/README.md b/README.md index 744df4b2..73d30fa4 100644 --- a/README.md +++ b/README.md @@ -580,7 +580,7 @@ Sleeping at home fast-forwards 8 hours. - **Café** — pre-made food. - **Clothing shops** — various types, each with a different inventory mix. - **Gym** — increase max energy. Costs energy in the process. Costs money. -- **Beauty salon** — boobs size, ass size, makeup, hairstyle, hair color. +- **Beauty salon** — boobs size, ass size, makeup, hairstyle, hair color. See §10.4.3 for makeup details. - **Adult shop** — Buy sex toys for the `Nipples`, `Anal`, and `Vagina` toy slots (§6.5). - **Electronics shop** — Buy upgraded phone models. Sells the Mid and Pro tiers (§9.9). In-person only; no online variant. - **Streets / parks / alleys** — commission space. @@ -629,6 +629,28 @@ The mixed pacing is deliberate: slots are the "fast burn," tables are the "time - Casino is both an income source and a money sink. Net EV per session is negative on average; spikes are possible. - Casino income is logged in the Bank app's income breakdown (§9.4) as a distinct line item; net losses appear in the corresponding spending breakdown. +#### 10.4.3 Beauty salon — makeup + +Makeup is a persistent appearance state on the player. It has no gameplay effect — no stat changes, no recognition modifiers, no path requirements — it is purely visual. + +**Presets:** Five fixed options, selected from a menu at the salon counter: + +| Preset | Description | +|-----------|--------------------------| +| `None` | No makeup (bare face). | +| `Natural` | Subtle, everyday look. | +| `Glam` | Full glam / bold lips. | +| `Gothic` | Dark tones, heavy liner. | +| `Flashy` | Vivid color, expressive. | + +**Cost:** A single universal "change makeup" fee (flat Yen amount, TBD §21), charged whenever the player switches to any preset including `None`. Leaving with the same preset is a no-op and costs nothing. + +**Persistence:** The active preset is stored as a single value in the save game and restored on load. It stays until changed at the salon — there is no time limit or degradation. + +**Visual implementation:** Each preset maps to a material parameter on the player mesh. C++ stores and broadcasts the value; Blueprint applies the material change. No mesh/material logic lives in C++. + +**Scope:** Beauty salon only. No apartment mirror, no phone app, no mid-session change. Keep it simple. + --- ## 11. PC (at home) @@ -685,6 +707,8 @@ The forum is accessed via phone or PC. It is both diegetic and the primary missi > **Forum scope:** The forum surface is intentionally minimal — the **commission board** (§13.1 / §13.2) and the **player's own profile**. There are no other users to browse, no threads, no popular-posts feed. The forum exists to drive the gameplay loop, not to simulate a social network. +> **Forum lore — commission posters:** Each commission listing on the board displays a username of the forum member who posted it. This is purely a lore / atmosphere detail — it has no gameplay effect, cannot be interacted with, and does not affect rewards or progression. The forum has a fixed, limited pool of fictional usernames; each generated commission is attributed to one of them at generation time. The same username may appear on multiple commissions. The pool exists only to make the board feel like a living community rather than an anonymous feed. + ### 13.4 Commission generation Commissions should be procedural with template-driven content. Recommended template structure: ``` @@ -749,6 +773,7 @@ Radial or hotbar accessible mid-session. Actions: - Drop bag / pick up bag. - Masturbate (Slut-path unlock, §5.1 — entry hidden until unlocked). - Crouch / stand. +- Open emote wheel (§14.5). ### 14.2 HUD (in-session) - Attribute bars (energy, stamina, embarrassment, lust) — minimal/peripheral when low, more prominent when nearing thresholds. @@ -760,6 +785,29 @@ Radial or hotbar accessible mid-session. Actions: ### 14.3 Out-of-session UI Forum, bank, gallery, shops are all in-fiction screens (phone/PC). Avoid out-of-fiction menus where possible. +### 14.5 Emote wheel + +A dedicated radial wheel for selecting a facial expression / pose. Available at any time during a session, not limited to photo mode. + +**Emotes (fixed set):** + +| Emote | Description | +|------------|------------------------------------| +| `Neutral` | Default resting expression. | +| `Smile` | Warm, natural smile. | +| `Sultry` | Half-lidded, confident look. | +| `Shy` | Eyes down, flustered expression. | +| `Playful` | Grin, light mischief energy. | + +**Lifecycle:** +- Opening the wheel **pauses movement** for the moment of selection; closing without selecting does nothing. +- On selection the emote plays and a timer starts (duration TBD §21). When the timer expires the player returns to their default expression automatically. +- Any **movement input** (walk, run, crouch) cancels the emote immediately. +- Taking a **photo or being live on stream** does not cancel the emote — whatever expression is active at shutter time is captured. This is the primary motivation for the system. +- Only one emote can be active at a time; opening the wheel and selecting a new one replaces the current one and resets the timer. + +**Implementation notes:** C++ stores the active `EPlayerEmote` enum value and owns the timer. On change it fires a delegate; the Animation Blueprint listens and blends to the matching facial animation / morph target. No gameplay stats are affected. + ### 14.4 Accessibility & comfort - Subtitle support for any voiced dialogue. - Camera "place" via voice command must have a non-voice fallback (hotkey). diff --git a/Source/NakedDesire/Clothing/ClothingItemInstance.cpp b/Source/NakedDesire/Clothing/ClothingItemInstance.cpp index 1317952f..e6388fa2 100644 --- a/Source/NakedDesire/Clothing/ClothingItemInstance.cpp +++ b/Source/NakedDesire/Clothing/ClothingItemInstance.cpp @@ -1 +1,27 @@ #include "ClothingItemInstance.h" + +#include "ClothingItem.h" +#include "NakedDesire/SaveGame/ItemSaveRecord.h" + +void UClothingItemInstance::Init(UClothingItem* InClothingItem) +{ + ClothingItem = InClothingItem; +} + +void UClothingItemInstance::Setup(UClothingItem* InClothingItem, const TArray& InStoredItems, + const float InCondition, const FGuid InInstanceId) +{ + this->ClothingItem = InClothingItem; + this->StoredItems = InStoredItems; + this->Condition = InCondition; + this->InstanceId = InInstanceId; +} + +UClothingItemInstance* UClothingItemInstance::CreateFromSave(UObject* Outer, const FItemSaveRecord& ItemSaveRecord) +{ + UClothingItemInstance* NewItemInstance = NewObject(Outer); + + NewItemInstance->Setup(ItemSaveRecord.Definition.Get(), {}, ItemSaveRecord.Condition, ItemSaveRecord.InstanceId); + + return NewItemInstance; +} diff --git a/Source/NakedDesire/Clothing/ClothingItemInstance.h b/Source/NakedDesire/Clothing/ClothingItemInstance.h index b9e3e297..66bb02df 100644 --- a/Source/NakedDesire/Clothing/ClothingItemInstance.h +++ b/Source/NakedDesire/Clothing/ClothingItemInstance.h @@ -4,6 +4,7 @@ #include "NakedDesire/Items/ItemInstance.h" #include "ClothingItemInstance.generated.h" +struct FItemSaveRecord; class UClothingItem; UCLASS(BlueprintType) @@ -17,6 +18,11 @@ public: UClothingItem* GetClothingItem() const { return ClothingItem; } + void Init(UClothingItem* InClothingItem); + void Setup(UClothingItem* InClothingItem, const TArray& InStoredItems, float InCondition, FGuid InInstanceId); + + static UClothingItemInstance* CreateFromSave(UObject* Outer, const FItemSaveRecord& ItemSaveRecord); + protected: UPROPERTY(BlueprintReadOnly, Category = "Clothing Item") TObjectPtr ClothingItem; diff --git a/Source/NakedDesire/Clothing/ClothingManager.cpp b/Source/NakedDesire/Clothing/ClothingManager.cpp index d54faa6f..9d4bc102 100644 --- a/Source/NakedDesire/Clothing/ClothingManager.cpp +++ b/Source/NakedDesire/Clothing/ClothingManager.cpp @@ -5,12 +5,23 @@ #include "ClothingItem.h" #include "ClothingItemInstance.h" #include "GameFramework/Character.h" +#include "Kismet/GameplayStatics.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) @@ -63,13 +74,14 @@ TArray UClothingManager::GetEquippedClothing() return EquippedClothingItems; } -void UClothingManager::HydrateClothing(UGlobalSaveGameData* SaveGameData) +void UClothingManager::HydrateClothing() { - // for (const FClothingItemSaveData& ClothingItemSaveData : SaveGameData->PlayerClothing) - // { - // UClothingItemData* ClothingItemData = UClothingItemData::CreateFromSaveData(ClothingItemSaveData); - // PutOnClothing(ClothingItemData); - // } + USaveSubsystem* SaveSubsystem = UGameplayStatics::GetGameInstance(GetWorld())->GetSubsystem(); + for (const FItemSaveRecord& ItemSaveRecord : SaveSubsystem->GetCurrentSave()->EquippedItems) + { + UClothingItemInstance* ClothingItemInstance = UClothingItemInstance::CreateFromSave(this, ItemSaveRecord); + PutOnClothing(ClothingItemInstance); + } } float UClothingManager::GetHeelHeight() @@ -109,6 +121,22 @@ void UClothingManager::PutOnClothing(UClothingItemInstance* ClothingItemInstance { ClothingSlotData.MeshComponent->SetLeaderPoseComponent(Cast(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); } @@ -125,6 +153,16 @@ void UClothingManager::TakeClothing(UClothingItemInstance* ClothingItemInstance) ClothingSlotData.ClothingItemInstance = ClothingItemInstance; + USaveSubsystem* SaveSubsystem = UGameplayStatics::GetGameInstance(GetWorld())->GetSubsystem(); + 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); } @@ -149,6 +187,12 @@ UClothingItemInstance* UClothingManager::RemoveClothing(const EClothingSlotType } OnClothingUnequip.Broadcast(ClothingItemInstance); + + USaveSubsystem* SaveSubsystem = UGameplayStatics::GetGameInstance(GetWorld())->GetSubsystem(); + SaveSubsystem->GetCurrentSave()->EquippedItems.RemoveAll([ClothingItemInstance](const FItemSaveRecord& Item) + { + return Item.InstanceId == ClothingItemInstance->GetInstanceId(); + }); return ClothingItemInstance; } @@ -158,6 +202,11 @@ void UClothingManager::DropClothing(const EClothingSlotType ClothingType) const UClothingItemInstance* ClothingItemInstance = RemoveClothing(ClothingType); if (!ClothingItemInstance) return; + + USaveSubsystem* SaveSubsystem = UGameplayStatics::GetGameInstance(GetWorld())->GetSubsystem(); + FItemSaveRecord ItemSaveRecord; + ItemSaveRecord.Init(ClothingItemInstance); + SaveSubsystem->GetCurrentSave()->DroppedItems.Push(ItemSaveRecord); OnClothingDropped.Broadcast(ClothingItemInstance); } diff --git a/Source/NakedDesire/Clothing/ClothingManager.h b/Source/NakedDesire/Clothing/ClothingManager.h index 055c0c05..8ba5e4eb 100644 --- a/Source/NakedDesire/Clothing/ClothingManager.h +++ b/Source/NakedDesire/Clothing/ClothingManager.h @@ -21,6 +21,8 @@ class NAKEDDESIRE_API UClothingManager : public UActorComponent public: UClothingManager(); + + virtual void BeginPlay() override; UPROPERTY(BlueprintReadWrite, Category = "Clothing Manager|Clothing") USkeletalMeshComponent* RootMesh = nullptr; @@ -36,7 +38,6 @@ public: UPROPERTY(BlueprintAssignable) FOnClothingChangeSignature OnClothingDropped; - UFUNCTION(BlueprintCallable) void PutOnClothing(UClothingItemInstance* ClothingItemInstance); @@ -68,7 +69,7 @@ public: UFUNCTION(BlueprintCallable) TArray GetEquippedClothing(); - void HydrateClothing(UGlobalSaveGameData* SaveGameData); + void HydrateClothing(); UFUNCTION(BlueprintPure) float GetHeelHeight(); diff --git a/Source/NakedDesire/Clothing/ClothingSlotType.h b/Source/NakedDesire/Clothing/ClothingSlotType.h index 20251e50..a1ecbdcf 100644 --- a/Source/NakedDesire/Clothing/ClothingSlotType.h +++ b/Source/NakedDesire/Clothing/ClothingSlotType.h @@ -14,7 +14,7 @@ enum class EClothingSlotType : uint8 Neck, Face, Eyes, - BodySuit, + Bodysuit, Top, Bottom, UnderwearTop, diff --git a/Source/NakedDesire/Global/NakedDesireGameMode.cpp b/Source/NakedDesire/Global/NakedDesireGameMode.cpp index 667fcc29..5bb5f4ce 100644 --- a/Source/NakedDesire/Global/NakedDesireGameMode.cpp +++ b/Source/NakedDesire/Global/NakedDesireGameMode.cpp @@ -10,6 +10,7 @@ #include "NakedDesire/MissionBuilder/MissionsManager.h" #include "NakedDesire/Player/NakedDesireCharacter.h" #include "NakedDesire/SaveGame/GlobalSaveGameData.h" +#include "NakedDesire/SaveGame/SaveSubsystem.h" void ANakedDesireGameMode::RestartGame() { @@ -23,7 +24,8 @@ AWardrobe* ANakedDesireGameMode::GetWardrobe() const void ANakedDesireGameMode::BuyItem(UClothingItemInstance* ClothingItemInstance) { - UGlobalSaveGameData* SaveGame = UGlobalSaveGameData::LoadGame(); + USaveSubsystem* SaveSubsystem = UGameplayStatics::GetGameInstance(GetWorld())->GetSubsystem(); + UGlobalSaveGameData* SaveGame = SaveSubsystem->GetCurrentSave(); if (!SaveGame) { UE_LOG(LogTemp, Error, TEXT("ANakedDesireGameMode::BuyItem Couldn't load save game")); @@ -31,19 +33,25 @@ void ANakedDesireGameMode::BuyItem(UClothingItemInstance* ClothingItemInstance) } if (SaveGame->Money < ClothingItemInstance->GetClothingItem()->BasePrice) - { return; - } SaveGame->Money -= ClothingItemInstance->GetClothingItem()->BasePrice; - Wardrobe->ClothingItems.Add(ClothingItemInstance); + Wardrobe->AddItem(ClothingItemInstance); } void ANakedDesireGameMode::OnHourChanged(int32 Hour) { + USaveSubsystem* SaveSubsystem = UGameplayStatics::GetGameInstance(GetWorld())->GetSubsystem(); + UGlobalSaveGameData* SaveGame = SaveSubsystem->GetCurrentSave(); + if (!SaveGame) + { + UE_LOG(LogTemp, Error, TEXT("ANakedDesireGameMode::BuyItem Couldn't load save game")); + return; + } + if (Hour == 4) { - DaysPassed++; + SaveGame->DaysPassed++; RefreshDailyMissions(); } } @@ -63,12 +71,12 @@ void ANakedDesireGameMode::BeginPlay() void ANakedDesireGameMode::RefreshDailyMissions() { - ANakedDesireCharacter* Player = Cast(UGameplayStatics::GetPlayerCharacter(GetWorld(), 0)); + const ANakedDesireCharacter* Player = Cast(UGameplayStatics::GetPlayerCharacter(GetWorld(), 0)); if (!Player) - { return; - } - int ClampedIndex = FMath::Clamp(DaysPassed, 0, MissionsConfig->DailyMissions.Num() - 1); + USaveSubsystem* SaveSubsystem = UGameplayStatics::GetGameInstance(GetWorld())->GetSubsystem(); + + int ClampedIndex = FMath::Clamp(SaveSubsystem->GetCurrentSave()->DaysPassed , 0, MissionsConfig->DailyMissions.Num() - 1); Player->MissionsManager->RefreshDailyMissions(MissionsConfig->DailyMissions[ClampedIndex].Missions); } diff --git a/Source/NakedDesire/Global/NakedDesireGameMode.h b/Source/NakedDesire/Global/NakedDesireGameMode.h index 351458b4..e4f2db8e 100644 --- a/Source/NakedDesire/Global/NakedDesireGameMode.h +++ b/Source/NakedDesire/Global/NakedDesireGameMode.h @@ -21,8 +21,6 @@ class ANakedDesireGameMode : public AGameModeBase UPROPERTY(EditDefaultsOnly) UMissionsConfig* MissionsConfig; - int32 DaysPassed = 0; - public: int NoticeCount = 0; @@ -30,9 +28,6 @@ public: UFUNCTION(BlueprintPure, BlueprintImplementableEvent) FTimecode GetCurrentTime() const; - - UFUNCTION(BlueprintPure, BlueprintImplementableEvent) - int32 GetDaysElapsed() const; UFUNCTION(BlueprintImplementableEvent, BlueprintCallable) void SetCurrentTime(FTimecode TimeCode); diff --git a/Source/NakedDesire/Interactables/InteractableBase.h b/Source/NakedDesire/Interactables/InteractableBase.h index 65f1c225..4ed7e6b7 100644 --- a/Source/NakedDesire/Interactables/InteractableBase.h +++ b/Source/NakedDesire/Interactables/InteractableBase.h @@ -30,6 +30,5 @@ class NAKEDDESIRE_API AInteractableBase : public AActor, public IInteractionTarg public: AInteractableBase(); -protected: virtual void Tick(float DeltaSeconds) override; }; diff --git a/Source/NakedDesire/Interactables/Wardrobe.cpp b/Source/NakedDesire/Interactables/Wardrobe.cpp index 95b018f7..b49a6a1d 100644 --- a/Source/NakedDesire/Interactables/Wardrobe.cpp +++ b/Source/NakedDesire/Interactables/Wardrobe.cpp @@ -3,3 +3,42 @@ #include "Wardrobe.h" +#include "Kismet/GameplayStatics.h" +#include "NakedDesire/SaveGame/GlobalSaveGameData.h" +#include "NakedDesire/SaveGame/ItemSaveRecord.h" +#include "NakedDesire/SaveGame/SaveSubsystem.h" + +void AWardrobe::AddItem(UClothingItemInstance* ClothingItemInstance) +{ + USaveSubsystem* SaveSubsystem = UGameplayStatics::GetGameInstance(GetWorld())->GetSubsystem(); + + ClothingItems.Push(ClothingItemInstance); + FItemSaveRecord ItemSaveRecord; + ItemSaveRecord.Init(ClothingItemInstance); + + SaveSubsystem->GetCurrentSave()->WardrobeItems.Push(ItemSaveRecord); +} + +void AWardrobe::RemoveItem(UClothingItemInstance* ClothingItemInstance) const +{ + USaveSubsystem* SaveSubsystem = UGameplayStatics::GetGameInstance(GetWorld())->GetSubsystem(); + + SaveSubsystem->GetCurrentSave()->WardrobeItems.RemoveAll([ClothingItemInstance](const FItemSaveRecord& Item) + { + return Item.InstanceId == ClothingItemInstance->GetInstanceId(); + }); +} + +void AWardrobe::BeginPlay() +{ + Super::BeginPlay(); + + USaveSubsystem* SaveSubsystem = UGameplayStatics::GetGameInstance(GetWorld())->GetSubsystem(); + UGlobalSaveGameData* SaveGame = SaveSubsystem->GetCurrentSave(); + + for (const FItemSaveRecord& ItemSaveRecord : SaveGame->WardrobeItems) + { + UClothingItemInstance* NewItemInstance = UClothingItemInstance::CreateFromSave(this, ItemSaveRecord); + ClothingItems.Push(NewItemInstance); + } +} diff --git a/Source/NakedDesire/Interactables/Wardrobe.h b/Source/NakedDesire/Interactables/Wardrobe.h index 6f8852fc..176d9a2f 100644 --- a/Source/NakedDesire/Interactables/Wardrobe.h +++ b/Source/NakedDesire/Interactables/Wardrobe.h @@ -17,4 +17,9 @@ class NAKEDDESIRE_API AWardrobe : public AInteractableBase public: UPROPERTY(EditAnywhere, BlueprintReadWrite, Instanced) TArray> ClothingItems; + + void AddItem(UClothingItemInstance* ClothingItemInstance); + void RemoveItem(UClothingItemInstance* ClothingItemInstance) const; + + virtual void BeginPlay() override; }; diff --git a/Source/NakedDesire/Items/ItemInstance.cpp b/Source/NakedDesire/Items/ItemInstance.cpp index a7939240..9fe2a8d0 100644 --- a/Source/NakedDesire/Items/ItemInstance.cpp +++ b/Source/NakedDesire/Items/ItemInstance.cpp @@ -6,12 +6,12 @@ void UItemInstance::PostInitProperties() if (HasAnyFlags(RF_ClassDefaultObject | RF_ArchetypeObject | RF_NeedLoad)) return; - if (!InstanceID.IsValid()) - InstanceID = FGuid::NewGuid(); + if (!InstanceId.IsValid()) + InstanceId = FGuid::NewGuid(); } void UItemInstance::PostDuplicate(EDuplicateMode::Type DuplicateMode) { Super::PostDuplicate(DuplicateMode); - InstanceID = FGuid::NewGuid(); + InstanceId = FGuid::NewGuid(); } diff --git a/Source/NakedDesire/Items/ItemInstance.h b/Source/NakedDesire/Items/ItemInstance.h index ccd34264..b3d48ed4 100644 --- a/Source/NakedDesire/Items/ItemInstance.h +++ b/Source/NakedDesire/Items/ItemInstance.h @@ -14,9 +14,9 @@ public: virtual void PostDuplicate(EDuplicateMode::Type DuplicateMode) override; UFUNCTION(BlueprintPure, Category = "Item") - FGuid GetInstanceID() const { return InstanceID; } + FGuid GetInstanceId() const { return InstanceId; } -private: +protected: UPROPERTY(VisibleAnywhere, SaveGame, BlueprintReadOnly, Category = "Item", meta = (AllowPrivateAccess = "true")) - FGuid InstanceID; + FGuid InstanceId; }; diff --git a/Source/NakedDesire/Items/SexToyItem.h b/Source/NakedDesire/Items/SexToyItem.h index 5d4735d6..ad006c0f 100644 --- a/Source/NakedDesire/Items/SexToyItem.h +++ b/Source/NakedDesire/Items/SexToyItem.h @@ -11,18 +11,18 @@ class NAKEDDESIRE_API USexToyItem : public UPrimaryDataAsset GENERATED_BODY() public: - UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Sed Toy") + UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Sex Toy") float LustModifier = 0.0f; - UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Sed Toy") + UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Sex Toy") float EmbarrassmentModifier = 0.0f; - UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Sed Toy") + UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Sex Toy") float PulseModifier = 0.0f; - UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Sed Toy") + UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Sex Toy") bool HasVibration = false; - UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Sed Toy") + UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Sex Toy") EClothingSlotType SlotType = EClothingSlotType::Anal; }; diff --git a/Source/NakedDesire/Player/NakedDesireCharacter.cpp b/Source/NakedDesire/Player/NakedDesireCharacter.cpp index 93356881..8dfa4300 100644 --- a/Source/NakedDesire/Player/NakedDesireCharacter.cpp +++ b/Source/NakedDesire/Player/NakedDesireCharacter.cpp @@ -404,9 +404,9 @@ void ANakedDesireCharacter::SetupClothingSlots() LOCTEXT("Eyes", "Eyes"))); ClothingManager->ClothingSlots.Add( FClothingSlotData( - BodySuitMeshComponent, EClothingSlotType::BodySuit, + BodySuitMeshComponent, EClothingSlotType::Bodysuit, nullptr, - LOCTEXT("BodySuit", "BodySuit"))); + LOCTEXT("Bodysuit", "Bodysuit"))); ClothingManager->ClothingSlots.Add( FClothingSlotData( TopMeshComponent, EClothingSlotType::Top, diff --git a/Source/NakedDesire/Player/PlayerCinematic.cpp b/Source/NakedDesire/Player/PlayerCinematic.cpp index 54f08ac4..882b9fe0 100644 --- a/Source/NakedDesire/Player/PlayerCinematic.cpp +++ b/Source/NakedDesire/Player/PlayerCinematic.cpp @@ -88,7 +88,7 @@ USkeletalMeshComponent* APlayerCinematic::GetMeshByType(const EClothingSlotType return FaceMeshComponent; case EClothingSlotType::Eyes: return EyesMeshComponent; - case EClothingSlotType::BodySuit: + case EClothingSlotType::Bodysuit: return BodyMeshComponent; case EClothingSlotType::Top: return TopMeshComponent; diff --git a/Source/NakedDesire/Player/PlayerImpostor.cpp b/Source/NakedDesire/Player/PlayerImpostor.cpp index ddfa7104..90e1f810 100644 --- a/Source/NakedDesire/Player/PlayerImpostor.cpp +++ b/Source/NakedDesire/Player/PlayerImpostor.cpp @@ -93,7 +93,7 @@ USkeletalMeshComponent* APlayerImpostor::GetMeshByType(const EClothingSlotType S return FaceMeshComponent; case EClothingSlotType::Eyes: return EyesMeshComponent; - case EClothingSlotType::BodySuit: + case EClothingSlotType::Bodysuit: return BodyMeshComponent; case EClothingSlotType::Top: return TopMeshComponent; diff --git a/Source/NakedDesire/SaveGame/GlobalSaveGameData.cpp b/Source/NakedDesire/SaveGame/GlobalSaveGameData.cpp index 368fc300..5803c25f 100644 --- a/Source/NakedDesire/SaveGame/GlobalSaveGameData.cpp +++ b/Source/NakedDesire/SaveGame/GlobalSaveGameData.cpp @@ -8,10 +8,10 @@ UGlobalSaveGameData* UGlobalSaveGameData::CreateNewSaveGame() { UGlobalSaveGameData* NewSave = Cast(UGameplayStatics::CreateSaveGameObject(StaticClass())); - NewSave->Money = STARTING_MONEY; - if (!NewSave) return nullptr; + + NewSave->Money = STARTING_MONEY; return NewSave; } @@ -41,12 +41,7 @@ UGlobalSaveGameData* UGlobalSaveGameData::LoadGame(const FString& SlotName) return nullptr; } -bool UGlobalSaveGameData::SaveGame(const FString& SlotName) +bool UGlobalSaveGameData::SaveGame(UGlobalSaveGameData* SaveGameData, const FString& SlotName) { - if (UGlobalSaveGameData* Save = CreateNewSaveGame()) - { - return UGameplayStatics::SaveGameToSlot(Save, SlotName, SLOT_PLAYER); - } - - return false; + return UGameplayStatics::SaveGameToSlot(SaveGameData, SlotName, SLOT_PLAYER); } diff --git a/Source/NakedDesire/SaveGame/GlobalSaveGameData.h b/Source/NakedDesire/SaveGame/GlobalSaveGameData.h index dc7eefa2..2ae0dd7e 100644 --- a/Source/NakedDesire/SaveGame/GlobalSaveGameData.h +++ b/Source/NakedDesire/SaveGame/GlobalSaveGameData.h @@ -17,7 +17,7 @@ class NAKEDDESIRE_API UGlobalSaveGameData : public USaveGame public: static UGlobalSaveGameData* LoadOrCreateSaveGame(const FString& SlotName = DefaultSaveSlotName); static UGlobalSaveGameData* LoadGame(const FString& SlotName = DefaultSaveSlotName); - static bool SaveGame(const FString& SlotName = DefaultSaveSlotName); + static bool SaveGame(UGlobalSaveGameData* SaveGameData, const FString& SlotName = DefaultSaveSlotName); UPROPERTY(SaveGame) bool HaveSeenTutorial = false; @@ -31,6 +31,9 @@ public: UPROPERTY(SaveGame) TArray EquippedItems; + UPROPERTY(SaveGame) + TArray DroppedItems; + UPROPERTY(SaveGame) int32 DaysPassed = 0; diff --git a/Source/NakedDesire/SaveGame/ItemSaveRecord.h b/Source/NakedDesire/SaveGame/ItemSaveRecord.h index e0bfcfd3..8a0bbe54 100644 --- a/Source/NakedDesire/SaveGame/ItemSaveRecord.h +++ b/Source/NakedDesire/SaveGame/ItemSaveRecord.h @@ -1,9 +1,11 @@ #pragma once #include "CoreMinimal.h" +#include "NakedDesire/Clothing/ClothingItemInstance.h" +#include "NakedDesire/Clothing/ClothingItem.h" #include "ItemSaveRecord.generated.h" -class UClothingItem; +class UClothingItemInstance; USTRUCT() struct NAKEDDESIRE_API FItemSaveRecord @@ -21,4 +23,16 @@ struct NAKEDDESIRE_API FItemSaveRecord 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; +} diff --git a/Source/NakedDesire/SaveGame/SaveSubsystem.cpp b/Source/NakedDesire/SaveGame/SaveSubsystem.cpp index dbbfac6c..e0011309 100644 --- a/Source/NakedDesire/SaveGame/SaveSubsystem.cpp +++ b/Source/NakedDesire/SaveGame/SaveSubsystem.cpp @@ -4,12 +4,12 @@ void USaveSubsystem::LoadGame(const FString& SlotName) { - UGlobalSaveGameData* Save = UGlobalSaveGameData::LoadGame(SlotName); + CurrentSave = UGlobalSaveGameData::LoadGame(SlotName); } -bool USaveSubsystem::SaveGame(const FString& SlotName) +bool USaveSubsystem::SaveGame(const FString& SlotName) const { - return UGlobalSaveGameData::SaveGame(SlotName); + return UGlobalSaveGameData::SaveGame(CurrentSave, SlotName); } void USaveSubsystem::AddItem(const FItemSaveRecord& Record) @@ -31,3 +31,13 @@ void USaveSubsystem::Initialize(FSubsystemCollectionBase& Collection) LoadGame(); } + +UGlobalSaveGameData* USaveSubsystem::GetCurrentSave() +{ + if (!CurrentSave) + { + CurrentSave = UGlobalSaveGameData::LoadOrCreateSaveGame(ActiveSlotName); + } + + return CurrentSave; +} diff --git a/Source/NakedDesire/SaveGame/SaveSubsystem.h b/Source/NakedDesire/SaveGame/SaveSubsystem.h index d8cf254c..fcf2dab1 100644 --- a/Source/NakedDesire/SaveGame/SaveSubsystem.h +++ b/Source/NakedDesire/SaveGame/SaveSubsystem.h @@ -15,7 +15,7 @@ class NAKEDDESIRE_API USaveSubsystem : public UGameInstanceSubsystem public: void LoadGame(const FString& SlotName = DefaultSaveSlotName); - bool SaveGame(const FString& SlotName = DefaultSaveSlotName); + bool SaveGame(const FString& SlotName = DefaultSaveSlotName) const; const TArray& GetItems() const { return Items; } void AddItem(const FItemSaveRecord& Record); @@ -23,6 +23,8 @@ public: virtual void Initialize(FSubsystemCollectionBase& Collection) override; + UGlobalSaveGameData* GetCurrentSave(); + private: UPROPERTY() FString ActiveSlotName = DefaultSaveSlotName;