Completed phase 1
This commit is contained in:
@@ -580,7 +580,7 @@ Sleeping at home fast-forwards 8 hours.
|
|||||||
- **Café** — pre-made food.
|
- **Café** — pre-made food.
|
||||||
- **Clothing shops** — various types, each with a different inventory mix.
|
- **Clothing shops** — various types, each with a different inventory mix.
|
||||||
- **Gym** — increase max energy. Costs energy in the process. Costs money.
|
- **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).
|
- **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.
|
- **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.
|
- **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 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.
|
- 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)
|
## 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 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
|
### 13.4 Commission generation
|
||||||
Commissions should be procedural with template-driven content. Recommended template structure:
|
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.
|
- Drop bag / pick up bag.
|
||||||
- Masturbate (Slut-path unlock, §5.1 — entry hidden until unlocked).
|
- Masturbate (Slut-path unlock, §5.1 — entry hidden until unlocked).
|
||||||
- Crouch / stand.
|
- Crouch / stand.
|
||||||
|
- Open emote wheel (§14.5).
|
||||||
|
|
||||||
### 14.2 HUD (in-session)
|
### 14.2 HUD (in-session)
|
||||||
- Attribute bars (energy, stamina, embarrassment, lust) — minimal/peripheral when low, more prominent when nearing thresholds.
|
- 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
|
### 14.3 Out-of-session UI
|
||||||
Forum, bank, gallery, shops are all in-fiction screens (phone/PC). Avoid out-of-fiction menus where possible.
|
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
|
### 14.4 Accessibility & comfort
|
||||||
- Subtitle support for any voiced dialogue.
|
- Subtitle support for any voiced dialogue.
|
||||||
- Camera "place" via voice command must have a non-voice fallback (hotkey).
|
- Camera "place" via voice command must have a non-voice fallback (hotkey).
|
||||||
|
|||||||
@@ -1 +1,27 @@
|
|||||||
#include "ClothingItemInstance.h"
|
#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<UItemInstance*>& 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<UClothingItemInstance>(Outer);
|
||||||
|
|
||||||
|
NewItemInstance->Setup(ItemSaveRecord.Definition.Get(), {}, ItemSaveRecord.Condition, ItemSaveRecord.InstanceId);
|
||||||
|
|
||||||
|
return NewItemInstance;
|
||||||
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
#include "NakedDesire/Items/ItemInstance.h"
|
#include "NakedDesire/Items/ItemInstance.h"
|
||||||
#include "ClothingItemInstance.generated.h"
|
#include "ClothingItemInstance.generated.h"
|
||||||
|
|
||||||
|
struct FItemSaveRecord;
|
||||||
class UClothingItem;
|
class UClothingItem;
|
||||||
|
|
||||||
UCLASS(BlueprintType)
|
UCLASS(BlueprintType)
|
||||||
@@ -17,6 +18,11 @@ public:
|
|||||||
|
|
||||||
UClothingItem* GetClothingItem() const { return ClothingItem; }
|
UClothingItem* GetClothingItem() const { return ClothingItem; }
|
||||||
|
|
||||||
|
void Init(UClothingItem* InClothingItem);
|
||||||
|
void Setup(UClothingItem* InClothingItem, const TArray<UItemInstance*>& InStoredItems, float InCondition, FGuid InInstanceId);
|
||||||
|
|
||||||
|
static UClothingItemInstance* CreateFromSave(UObject* Outer, const FItemSaveRecord& ItemSaveRecord);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
UPROPERTY(BlueprintReadOnly, Category = "Clothing Item")
|
UPROPERTY(BlueprintReadOnly, Category = "Clothing Item")
|
||||||
TObjectPtr<UClothingItem> ClothingItem;
|
TObjectPtr<UClothingItem> ClothingItem;
|
||||||
|
|||||||
@@ -5,12 +5,23 @@
|
|||||||
#include "ClothingItem.h"
|
#include "ClothingItem.h"
|
||||||
#include "ClothingItemInstance.h"
|
#include "ClothingItemInstance.h"
|
||||||
#include "GameFramework/Character.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()
|
UClothingManager::UClothingManager()
|
||||||
{
|
{
|
||||||
PrimaryComponentTick.bCanEverTick = false;
|
PrimaryComponentTick.bCanEverTick = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void UClothingManager::BeginPlay()
|
||||||
|
{
|
||||||
|
Super::BeginPlay();
|
||||||
|
|
||||||
|
HydrateClothing();
|
||||||
|
}
|
||||||
|
|
||||||
bool UClothingManager::IsBodyTypeExposed(const EBodyPart BodyPart)
|
bool UClothingManager::IsBodyTypeExposed(const EBodyPart BodyPart)
|
||||||
{
|
{
|
||||||
for (const auto& ClothingSlot : ClothingSlots)
|
for (const auto& ClothingSlot : ClothingSlots)
|
||||||
@@ -63,13 +74,14 @@ TArray<UClothingItemInstance*> UClothingManager::GetEquippedClothing()
|
|||||||
return EquippedClothingItems;
|
return EquippedClothingItems;
|
||||||
}
|
}
|
||||||
|
|
||||||
void UClothingManager::HydrateClothing(UGlobalSaveGameData* SaveGameData)
|
void UClothingManager::HydrateClothing()
|
||||||
{
|
{
|
||||||
// for (const FClothingItemSaveData& ClothingItemSaveData : SaveGameData->PlayerClothing)
|
USaveSubsystem* SaveSubsystem = UGameplayStatics::GetGameInstance(GetWorld())->GetSubsystem<USaveSubsystem>();
|
||||||
// {
|
for (const FItemSaveRecord& ItemSaveRecord : SaveSubsystem->GetCurrentSave()->EquippedItems)
|
||||||
// UClothingItemData* ClothingItemData = UClothingItemData::CreateFromSaveData(ClothingItemSaveData);
|
{
|
||||||
// PutOnClothing(ClothingItemData);
|
UClothingItemInstance* ClothingItemInstance = UClothingItemInstance::CreateFromSave(this, ItemSaveRecord);
|
||||||
// }
|
PutOnClothing(ClothingItemInstance);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
float UClothingManager::GetHeelHeight()
|
float UClothingManager::GetHeelHeight()
|
||||||
@@ -110,6 +122,22 @@ void UClothingManager::PutOnClothing(UClothingItemInstance* ClothingItemInstance
|
|||||||
ClothingSlotData.MeshComponent->SetLeaderPoseComponent(Cast<ACharacter>(GetOwner())->GetMesh());
|
ClothingSlotData.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);
|
OnClothingEquip.Broadcast(ClothingItemInstance);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -125,6 +153,16 @@ void UClothingManager::TakeClothing(UClothingItemInstance* ClothingItemInstance)
|
|||||||
|
|
||||||
ClothingSlotData.ClothingItemInstance = ClothingItemInstance;
|
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);
|
PutOnClothing(ClothingItemInstance);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -150,6 +188,12 @@ UClothingItemInstance* UClothingManager::RemoveClothing(const EClothingSlotType
|
|||||||
|
|
||||||
OnClothingUnequip.Broadcast(ClothingItemInstance);
|
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;
|
return ClothingItemInstance;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -159,6 +203,11 @@ void UClothingManager::DropClothing(const EClothingSlotType ClothingType)
|
|||||||
if (!ClothingItemInstance)
|
if (!ClothingItemInstance)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
USaveSubsystem* SaveSubsystem = UGameplayStatics::GetGameInstance(GetWorld())->GetSubsystem<USaveSubsystem>();
|
||||||
|
FItemSaveRecord ItemSaveRecord;
|
||||||
|
ItemSaveRecord.Init(ClothingItemInstance);
|
||||||
|
SaveSubsystem->GetCurrentSave()->DroppedItems.Push(ItemSaveRecord);
|
||||||
|
|
||||||
OnClothingDropped.Broadcast(ClothingItemInstance);
|
OnClothingDropped.Broadcast(ClothingItemInstance);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -22,6 +22,8 @@ class NAKEDDESIRE_API UClothingManager : public UActorComponent
|
|||||||
public:
|
public:
|
||||||
UClothingManager();
|
UClothingManager();
|
||||||
|
|
||||||
|
virtual void BeginPlay() override;
|
||||||
|
|
||||||
UPROPERTY(BlueprintReadWrite, Category = "Clothing Manager|Clothing")
|
UPROPERTY(BlueprintReadWrite, Category = "Clothing Manager|Clothing")
|
||||||
USkeletalMeshComponent* RootMesh = nullptr;
|
USkeletalMeshComponent* RootMesh = nullptr;
|
||||||
|
|
||||||
@@ -37,7 +39,6 @@ public:
|
|||||||
UPROPERTY(BlueprintAssignable)
|
UPROPERTY(BlueprintAssignable)
|
||||||
FOnClothingChangeSignature OnClothingDropped;
|
FOnClothingChangeSignature OnClothingDropped;
|
||||||
|
|
||||||
|
|
||||||
UFUNCTION(BlueprintCallable)
|
UFUNCTION(BlueprintCallable)
|
||||||
void PutOnClothing(UClothingItemInstance* ClothingItemInstance);
|
void PutOnClothing(UClothingItemInstance* ClothingItemInstance);
|
||||||
|
|
||||||
@@ -68,7 +69,7 @@ public:
|
|||||||
UFUNCTION(BlueprintCallable)
|
UFUNCTION(BlueprintCallable)
|
||||||
TArray<UClothingItemInstance*> GetEquippedClothing();
|
TArray<UClothingItemInstance*> GetEquippedClothing();
|
||||||
|
|
||||||
void HydrateClothing(UGlobalSaveGameData* SaveGameData);
|
void HydrateClothing();
|
||||||
|
|
||||||
UFUNCTION(BlueprintPure)
|
UFUNCTION(BlueprintPure)
|
||||||
float GetHeelHeight();
|
float GetHeelHeight();
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ enum class EClothingSlotType : uint8
|
|||||||
Neck,
|
Neck,
|
||||||
Face,
|
Face,
|
||||||
Eyes,
|
Eyes,
|
||||||
BodySuit,
|
Bodysuit,
|
||||||
Top,
|
Top,
|
||||||
Bottom,
|
Bottom,
|
||||||
UnderwearTop,
|
UnderwearTop,
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
#include "NakedDesire/MissionBuilder/MissionsManager.h"
|
#include "NakedDesire/MissionBuilder/MissionsManager.h"
|
||||||
#include "NakedDesire/Player/NakedDesireCharacter.h"
|
#include "NakedDesire/Player/NakedDesireCharacter.h"
|
||||||
#include "NakedDesire/SaveGame/GlobalSaveGameData.h"
|
#include "NakedDesire/SaveGame/GlobalSaveGameData.h"
|
||||||
|
#include "NakedDesire/SaveGame/SaveSubsystem.h"
|
||||||
|
|
||||||
void ANakedDesireGameMode::RestartGame()
|
void ANakedDesireGameMode::RestartGame()
|
||||||
{
|
{
|
||||||
@@ -23,7 +24,8 @@ AWardrobe* ANakedDesireGameMode::GetWardrobe() const
|
|||||||
|
|
||||||
void ANakedDesireGameMode::BuyItem(UClothingItemInstance* ClothingItemInstance)
|
void ANakedDesireGameMode::BuyItem(UClothingItemInstance* ClothingItemInstance)
|
||||||
{
|
{
|
||||||
UGlobalSaveGameData* SaveGame = UGlobalSaveGameData::LoadGame();
|
USaveSubsystem* SaveSubsystem = UGameplayStatics::GetGameInstance(GetWorld())->GetSubsystem<USaveSubsystem>();
|
||||||
|
UGlobalSaveGameData* SaveGame = SaveSubsystem->GetCurrentSave();
|
||||||
if (!SaveGame)
|
if (!SaveGame)
|
||||||
{
|
{
|
||||||
UE_LOG(LogTemp, Error, TEXT("ANakedDesireGameMode::BuyItem Couldn't load save game"));
|
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)
|
if (SaveGame->Money < ClothingItemInstance->GetClothingItem()->BasePrice)
|
||||||
{
|
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
|
|
||||||
SaveGame->Money -= ClothingItemInstance->GetClothingItem()->BasePrice;
|
SaveGame->Money -= ClothingItemInstance->GetClothingItem()->BasePrice;
|
||||||
Wardrobe->ClothingItems.Add(ClothingItemInstance);
|
Wardrobe->AddItem(ClothingItemInstance);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ANakedDesireGameMode::OnHourChanged(int32 Hour)
|
void ANakedDesireGameMode::OnHourChanged(int32 Hour)
|
||||||
{
|
{
|
||||||
|
USaveSubsystem* SaveSubsystem = UGameplayStatics::GetGameInstance(GetWorld())->GetSubsystem<USaveSubsystem>();
|
||||||
|
UGlobalSaveGameData* SaveGame = SaveSubsystem->GetCurrentSave();
|
||||||
|
if (!SaveGame)
|
||||||
|
{
|
||||||
|
UE_LOG(LogTemp, Error, TEXT("ANakedDesireGameMode::BuyItem Couldn't load save game"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (Hour == 4)
|
if (Hour == 4)
|
||||||
{
|
{
|
||||||
DaysPassed++;
|
SaveGame->DaysPassed++;
|
||||||
RefreshDailyMissions();
|
RefreshDailyMissions();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -63,12 +71,12 @@ void ANakedDesireGameMode::BeginPlay()
|
|||||||
|
|
||||||
void ANakedDesireGameMode::RefreshDailyMissions()
|
void ANakedDesireGameMode::RefreshDailyMissions()
|
||||||
{
|
{
|
||||||
ANakedDesireCharacter* Player = Cast<ANakedDesireCharacter>(UGameplayStatics::GetPlayerCharacter(GetWorld(), 0));
|
const ANakedDesireCharacter* Player = Cast<ANakedDesireCharacter>(UGameplayStatics::GetPlayerCharacter(GetWorld(), 0));
|
||||||
if (!Player)
|
if (!Player)
|
||||||
{
|
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
|
|
||||||
int ClampedIndex = FMath::Clamp(DaysPassed, 0, MissionsConfig->DailyMissions.Num() - 1);
|
USaveSubsystem* SaveSubsystem = UGameplayStatics::GetGameInstance(GetWorld())->GetSubsystem<USaveSubsystem>();
|
||||||
|
|
||||||
|
int ClampedIndex = FMath::Clamp(SaveSubsystem->GetCurrentSave()->DaysPassed , 0, MissionsConfig->DailyMissions.Num() - 1);
|
||||||
Player->MissionsManager->RefreshDailyMissions(MissionsConfig->DailyMissions[ClampedIndex].Missions);
|
Player->MissionsManager->RefreshDailyMissions(MissionsConfig->DailyMissions[ClampedIndex].Missions);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,8 +21,6 @@ class ANakedDesireGameMode : public AGameModeBase
|
|||||||
UPROPERTY(EditDefaultsOnly)
|
UPROPERTY(EditDefaultsOnly)
|
||||||
UMissionsConfig* MissionsConfig;
|
UMissionsConfig* MissionsConfig;
|
||||||
|
|
||||||
int32 DaysPassed = 0;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
int NoticeCount = 0;
|
int NoticeCount = 0;
|
||||||
|
|
||||||
@@ -31,9 +29,6 @@ public:
|
|||||||
UFUNCTION(BlueprintPure, BlueprintImplementableEvent)
|
UFUNCTION(BlueprintPure, BlueprintImplementableEvent)
|
||||||
FTimecode GetCurrentTime() const;
|
FTimecode GetCurrentTime() const;
|
||||||
|
|
||||||
UFUNCTION(BlueprintPure, BlueprintImplementableEvent)
|
|
||||||
int32 GetDaysElapsed() const;
|
|
||||||
|
|
||||||
UFUNCTION(BlueprintImplementableEvent, BlueprintCallable)
|
UFUNCTION(BlueprintImplementableEvent, BlueprintCallable)
|
||||||
void SetCurrentTime(FTimecode TimeCode);
|
void SetCurrentTime(FTimecode TimeCode);
|
||||||
|
|
||||||
|
|||||||
@@ -30,6 +30,5 @@ class NAKEDDESIRE_API AInteractableBase : public AActor, public IInteractionTarg
|
|||||||
public:
|
public:
|
||||||
AInteractableBase();
|
AInteractableBase();
|
||||||
|
|
||||||
protected:
|
|
||||||
virtual void Tick(float DeltaSeconds) override;
|
virtual void Tick(float DeltaSeconds) override;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -3,3 +3,42 @@
|
|||||||
|
|
||||||
#include "Wardrobe.h"
|
#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<USaveSubsystem>();
|
||||||
|
|
||||||
|
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<USaveSubsystem>();
|
||||||
|
|
||||||
|
SaveSubsystem->GetCurrentSave()->WardrobeItems.RemoveAll([ClothingItemInstance](const FItemSaveRecord& Item)
|
||||||
|
{
|
||||||
|
return Item.InstanceId == ClothingItemInstance->GetInstanceId();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void AWardrobe::BeginPlay()
|
||||||
|
{
|
||||||
|
Super::BeginPlay();
|
||||||
|
|
||||||
|
USaveSubsystem* SaveSubsystem = UGameplayStatics::GetGameInstance(GetWorld())->GetSubsystem<USaveSubsystem>();
|
||||||
|
UGlobalSaveGameData* SaveGame = SaveSubsystem->GetCurrentSave();
|
||||||
|
|
||||||
|
for (const FItemSaveRecord& ItemSaveRecord : SaveGame->WardrobeItems)
|
||||||
|
{
|
||||||
|
UClothingItemInstance* NewItemInstance = UClothingItemInstance::CreateFromSave(this, ItemSaveRecord);
|
||||||
|
ClothingItems.Push(NewItemInstance);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -17,4 +17,9 @@ class NAKEDDESIRE_API AWardrobe : public AInteractableBase
|
|||||||
public:
|
public:
|
||||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Instanced)
|
UPROPERTY(EditAnywhere, BlueprintReadWrite, Instanced)
|
||||||
TArray<TObjectPtr<UClothingItemInstance>> ClothingItems;
|
TArray<TObjectPtr<UClothingItemInstance>> ClothingItems;
|
||||||
|
|
||||||
|
void AddItem(UClothingItemInstance* ClothingItemInstance);
|
||||||
|
void RemoveItem(UClothingItemInstance* ClothingItemInstance) const;
|
||||||
|
|
||||||
|
virtual void BeginPlay() override;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -6,12 +6,12 @@ void UItemInstance::PostInitProperties()
|
|||||||
|
|
||||||
if (HasAnyFlags(RF_ClassDefaultObject | RF_ArchetypeObject | RF_NeedLoad))
|
if (HasAnyFlags(RF_ClassDefaultObject | RF_ArchetypeObject | RF_NeedLoad))
|
||||||
return;
|
return;
|
||||||
if (!InstanceID.IsValid())
|
if (!InstanceId.IsValid())
|
||||||
InstanceID = FGuid::NewGuid();
|
InstanceId = FGuid::NewGuid();
|
||||||
}
|
}
|
||||||
|
|
||||||
void UItemInstance::PostDuplicate(EDuplicateMode::Type DuplicateMode)
|
void UItemInstance::PostDuplicate(EDuplicateMode::Type DuplicateMode)
|
||||||
{
|
{
|
||||||
Super::PostDuplicate(DuplicateMode);
|
Super::PostDuplicate(DuplicateMode);
|
||||||
InstanceID = FGuid::NewGuid();
|
InstanceId = FGuid::NewGuid();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,9 +14,9 @@ public:
|
|||||||
virtual void PostDuplicate(EDuplicateMode::Type DuplicateMode) override;
|
virtual void PostDuplicate(EDuplicateMode::Type DuplicateMode) override;
|
||||||
|
|
||||||
UFUNCTION(BlueprintPure, Category = "Item")
|
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"))
|
UPROPERTY(VisibleAnywhere, SaveGame, BlueprintReadOnly, Category = "Item", meta = (AllowPrivateAccess = "true"))
|
||||||
FGuid InstanceID;
|
FGuid InstanceId;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -11,18 +11,18 @@ class NAKEDDESIRE_API USexToyItem : public UPrimaryDataAsset
|
|||||||
GENERATED_BODY()
|
GENERATED_BODY()
|
||||||
|
|
||||||
public:
|
public:
|
||||||
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Sed Toy")
|
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Sex Toy")
|
||||||
float LustModifier = 0.0f;
|
float LustModifier = 0.0f;
|
||||||
|
|
||||||
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Sed Toy")
|
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Sex Toy")
|
||||||
float EmbarrassmentModifier = 0.0f;
|
float EmbarrassmentModifier = 0.0f;
|
||||||
|
|
||||||
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Sed Toy")
|
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Sex Toy")
|
||||||
float PulseModifier = 0.0f;
|
float PulseModifier = 0.0f;
|
||||||
|
|
||||||
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Sed Toy")
|
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Sex Toy")
|
||||||
bool HasVibration = false;
|
bool HasVibration = false;
|
||||||
|
|
||||||
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Sed Toy")
|
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Sex Toy")
|
||||||
EClothingSlotType SlotType = EClothingSlotType::Anal;
|
EClothingSlotType SlotType = EClothingSlotType::Anal;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -404,9 +404,9 @@ void ANakedDesireCharacter::SetupClothingSlots()
|
|||||||
LOCTEXT("Eyes", "Eyes")));
|
LOCTEXT("Eyes", "Eyes")));
|
||||||
ClothingManager->ClothingSlots.Add(
|
ClothingManager->ClothingSlots.Add(
|
||||||
FClothingSlotData(
|
FClothingSlotData(
|
||||||
BodySuitMeshComponent, EClothingSlotType::BodySuit,
|
BodySuitMeshComponent, EClothingSlotType::Bodysuit,
|
||||||
nullptr,
|
nullptr,
|
||||||
LOCTEXT("BodySuit", "BodySuit")));
|
LOCTEXT("Bodysuit", "Bodysuit")));
|
||||||
ClothingManager->ClothingSlots.Add(
|
ClothingManager->ClothingSlots.Add(
|
||||||
FClothingSlotData(
|
FClothingSlotData(
|
||||||
TopMeshComponent, EClothingSlotType::Top,
|
TopMeshComponent, EClothingSlotType::Top,
|
||||||
|
|||||||
@@ -88,7 +88,7 @@ USkeletalMeshComponent* APlayerCinematic::GetMeshByType(const EClothingSlotType
|
|||||||
return FaceMeshComponent;
|
return FaceMeshComponent;
|
||||||
case EClothingSlotType::Eyes:
|
case EClothingSlotType::Eyes:
|
||||||
return EyesMeshComponent;
|
return EyesMeshComponent;
|
||||||
case EClothingSlotType::BodySuit:
|
case EClothingSlotType::Bodysuit:
|
||||||
return BodyMeshComponent;
|
return BodyMeshComponent;
|
||||||
case EClothingSlotType::Top:
|
case EClothingSlotType::Top:
|
||||||
return TopMeshComponent;
|
return TopMeshComponent;
|
||||||
|
|||||||
@@ -93,7 +93,7 @@ USkeletalMeshComponent* APlayerImpostor::GetMeshByType(const EClothingSlotType S
|
|||||||
return FaceMeshComponent;
|
return FaceMeshComponent;
|
||||||
case EClothingSlotType::Eyes:
|
case EClothingSlotType::Eyes:
|
||||||
return EyesMeshComponent;
|
return EyesMeshComponent;
|
||||||
case EClothingSlotType::BodySuit:
|
case EClothingSlotType::Bodysuit:
|
||||||
return BodyMeshComponent;
|
return BodyMeshComponent;
|
||||||
case EClothingSlotType::Top:
|
case EClothingSlotType::Top:
|
||||||
return TopMeshComponent;
|
return TopMeshComponent;
|
||||||
|
|||||||
@@ -8,11 +8,11 @@
|
|||||||
UGlobalSaveGameData* UGlobalSaveGameData::CreateNewSaveGame()
|
UGlobalSaveGameData* UGlobalSaveGameData::CreateNewSaveGame()
|
||||||
{
|
{
|
||||||
UGlobalSaveGameData* NewSave = Cast<UGlobalSaveGameData>(UGameplayStatics::CreateSaveGameObject(StaticClass()));
|
UGlobalSaveGameData* NewSave = Cast<UGlobalSaveGameData>(UGameplayStatics::CreateSaveGameObject(StaticClass()));
|
||||||
NewSave->Money = STARTING_MONEY;
|
|
||||||
|
|
||||||
if (!NewSave)
|
if (!NewSave)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
|
NewSave->Money = STARTING_MONEY;
|
||||||
|
|
||||||
return NewSave;
|
return NewSave;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -41,12 +41,7 @@ UGlobalSaveGameData* UGlobalSaveGameData::LoadGame(const FString& SlotName)
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool UGlobalSaveGameData::SaveGame(const FString& SlotName)
|
bool UGlobalSaveGameData::SaveGame(UGlobalSaveGameData* SaveGameData, const FString& SlotName)
|
||||||
{
|
{
|
||||||
if (UGlobalSaveGameData* Save = CreateNewSaveGame())
|
return UGameplayStatics::SaveGameToSlot(SaveGameData, SlotName, SLOT_PLAYER);
|
||||||
{
|
|
||||||
return UGameplayStatics::SaveGameToSlot(Save, SlotName, SLOT_PLAYER);
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ class NAKEDDESIRE_API UGlobalSaveGameData : public USaveGame
|
|||||||
public:
|
public:
|
||||||
static UGlobalSaveGameData* LoadOrCreateSaveGame(const FString& SlotName = DefaultSaveSlotName);
|
static UGlobalSaveGameData* LoadOrCreateSaveGame(const FString& SlotName = DefaultSaveSlotName);
|
||||||
static UGlobalSaveGameData* LoadGame(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)
|
UPROPERTY(SaveGame)
|
||||||
bool HaveSeenTutorial = false;
|
bool HaveSeenTutorial = false;
|
||||||
@@ -31,6 +31,9 @@ public:
|
|||||||
UPROPERTY(SaveGame)
|
UPROPERTY(SaveGame)
|
||||||
TArray<FItemSaveRecord> EquippedItems;
|
TArray<FItemSaveRecord> EquippedItems;
|
||||||
|
|
||||||
|
UPROPERTY(SaveGame)
|
||||||
|
TArray<FItemSaveRecord> DroppedItems;
|
||||||
|
|
||||||
UPROPERTY(SaveGame)
|
UPROPERTY(SaveGame)
|
||||||
int32 DaysPassed = 0;
|
int32 DaysPassed = 0;
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "CoreMinimal.h"
|
#include "CoreMinimal.h"
|
||||||
|
#include "NakedDesire/Clothing/ClothingItemInstance.h"
|
||||||
|
#include "NakedDesire/Clothing/ClothingItem.h"
|
||||||
#include "ItemSaveRecord.generated.h"
|
#include "ItemSaveRecord.generated.h"
|
||||||
|
|
||||||
class UClothingItem;
|
class UClothingItemInstance;
|
||||||
|
|
||||||
USTRUCT()
|
USTRUCT()
|
||||||
struct NAKEDDESIRE_API FItemSaveRecord
|
struct NAKEDDESIRE_API FItemSaveRecord
|
||||||
@@ -21,4 +23,16 @@ struct NAKEDDESIRE_API FItemSaveRecord
|
|||||||
|
|
||||||
UPROPERTY(SaveGame)
|
UPROPERTY(SaveGame)
|
||||||
FGuid ParentId;
|
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;
|
||||||
|
}
|
||||||
|
|||||||
@@ -4,12 +4,12 @@
|
|||||||
|
|
||||||
void USaveSubsystem::LoadGame(const FString& SlotName)
|
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)
|
void USaveSubsystem::AddItem(const FItemSaveRecord& Record)
|
||||||
@@ -31,3 +31,13 @@ void USaveSubsystem::Initialize(FSubsystemCollectionBase& Collection)
|
|||||||
|
|
||||||
LoadGame();
|
LoadGame();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
UGlobalSaveGameData* USaveSubsystem::GetCurrentSave()
|
||||||
|
{
|
||||||
|
if (!CurrentSave)
|
||||||
|
{
|
||||||
|
CurrentSave = UGlobalSaveGameData::LoadOrCreateSaveGame(ActiveSlotName);
|
||||||
|
}
|
||||||
|
|
||||||
|
return CurrentSave;
|
||||||
|
}
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ class NAKEDDESIRE_API USaveSubsystem : public UGameInstanceSubsystem
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
void LoadGame(const FString& SlotName = DefaultSaveSlotName);
|
void LoadGame(const FString& SlotName = DefaultSaveSlotName);
|
||||||
bool SaveGame(const FString& SlotName = DefaultSaveSlotName);
|
bool SaveGame(const FString& SlotName = DefaultSaveSlotName) const;
|
||||||
|
|
||||||
const TArray<FItemSaveRecord>& GetItems() const { return Items; }
|
const TArray<FItemSaveRecord>& GetItems() const { return Items; }
|
||||||
void AddItem(const FItemSaveRecord& Record);
|
void AddItem(const FItemSaveRecord& Record);
|
||||||
@@ -23,6 +23,8 @@ public:
|
|||||||
|
|
||||||
virtual void Initialize(FSubsystemCollectionBase& Collection) override;
|
virtual void Initialize(FSubsystemCollectionBase& Collection) override;
|
||||||
|
|
||||||
|
UGlobalSaveGameData* GetCurrentSave();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
UPROPERTY()
|
UPROPERTY()
|
||||||
FString ActiveSlotName = DefaultSaveSlotName;
|
FString ActiveSlotName = DefaultSaveSlotName;
|
||||||
|
|||||||
Reference in New Issue
Block a user