Compare commits

...

10 Commits

Author SHA1 Message Date
koritsa 3f266e12d5 updated plan 2026-05-23 00:40:02 +03:00
koritsa e1b04f1310 updated readme 2026-05-23 00:13:00 +03:00
koritsa a86f163573 updated readme 2026-05-22 23:39:51 +03:00
olehhapuk efa280b94d Updated readme 2026-05-22 23:24:57 +03:00
koritsa defc5decf9 Added save subsystem 2026-05-22 16:23:59 +03:00
koritsa d1fd754f3b Added container slots 2026-05-22 10:57:16 +03:00
koritsa 8434d11b37 Added ClothingItemInstance and cleanup the project 2026-05-20 23:26:26 +03:00
koritsa 1b2d9f9098 Add EProgressionPath enum 2026-05-20 00:07:45 +03:00
koritsa c4bfb80cd1 Introduce EBodyPart enum 2026-05-20 00:04:33 +03:00
koritsa 4ce6b32607 Define UItemInstance base UObject with stable GUID 2026-05-19 23:59:58 +03:00
61 changed files with 751 additions and 592 deletions
File diff suppressed because one or more lines are too long
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+2
View File
@@ -0,0 +1,2 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003APLCrashReporter_002EBuild_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003F_002E_002E_003FShared_003FEpic_0020Games_003FUE_005F5_002E7_003FEngine_003FSource_003FThirdParty_003FPLCrashReporter_003FPLCrashReporter_002EBuild_002Ecs/@EntryIndexedValue">ForceIncluded</s:String></wpf:ResourceDictionary>
+144 -82
View File
@@ -12,63 +12,86 @@ State of the C++ module as of the latest pass. File references use `Source/Naked
### 1.1 Implemented
- **Modular character w/ per-slot meshes** — `Player/NakedDesireCharacter.cpp:40-67` creates 14 skeletal mesh components, one per clothing slot. Aligns with GDD §17.6.
- **Equip / unequip / drop event** — `Clothing/ClothingManager.cpp:88-172` (slot-based, broadcasts `OnClothingEquip / Unequip / Dropped`).
- **Basic perception** — `Player/NakedDesireCharacter.cpp:167-203` implements `IAISightTargetInterface::CanBeSeenFrom` against `boobs_root` and `pelvis` bones with line-of-sight checks. Foundation for GDD §7.1.
- **AI sight + behavior tree** — `NPC/NPCAIController.cpp` runs a BT with `Player` / `TargetLocation` blackboard. `NPC/NPCSpawner.cpp` does proximity-gated spawning with day / night caps.
- **Mission framework** — `Mission``MissionGoal``GoalRestriction` composition (`MissionBuilder/`). Two goals (`FlashGoal`, `MinTimeGoal`), three restrictions (`EquipClothing`, `ExposeBodyPart`, `Location`).
- **Item identity scaffold (GDD §6.1)** — `Items/ItemInstance.h` defines `UItemInstance` as an abstract `UObject` with a stable `FGuid InstanceID`, auto-assigned in `PostInitProperties` and refreshed in `PostDuplicate`. `Clothing/ClothingItemInstance.h` inherits from it and holds per-instance `Condition`, a pointer to the immutable `UClothingItem` definition, and a `StoredItems` array for container slots.
- **Definition / instance split (GDD §6.1, §17.4)** — `Clothing/ClothingItem.h` is the immutable `UPrimaryDataAsset` definition with GDD-aligned fields: `Coverage`, `IsUnderwear`, `CanExpose`, `IsRestrictive`, `ContainerSlots` (typed S/M/L via `EGarmentContainerSlotType` + `FGarmentContainerSlot`), `ProgressionPath`, plus rendering data (`SkeletalMesh`, `Materials`, `StaticMesh`).
- **Progression path enum (GDD §5)** — `Progression/ProgressionPath.h` defines `EProgressionPath { None, Slut, Exhibitionist, Slave }`. Used on `UClothingItem::ProgressionPath`.
- **Body-part enum (GDD §6.3)** — `Clothing/BodyPart.h` defines `EBodyPart { None, Boobs, Ass, Genitals }`. Used on `UClothingItem::CanExpose`.
- **Save scaffolding (GDD §16)** — `SaveGame/SaveSubsystem.h/.cpp` (`UGameInstanceSubsystem`), `SaveGame/ItemSaveRecord.h` (generic `{ InstanceId, Definition, Condition, ParentId }`). `Global/Constants.h` now uses `DefaultSaveSlotName` instead of a single `SLOT_NAME` macro.
- **Observation-driven embarrassment (GDD §7.1)** — `NPC/NPCAIController.cpp:56-69` listens to `OnTargetPerceptionUpdated` and forwards bSensed transitions to `Stats/StatsManager::SetObserved`. `Stats/StatsManager::TickComponent` (`StatsManager.cpp:23-38`) now gates gain vs. decay on `ObserverCount` instead of unconditional decay. Foundation for Phase 4.
- **Modular character w/ per-slot meshes** — `Player/NakedDesireCharacter.cpp:40-67` creates 14 skeletal mesh components, one per `EClothingSlotType`. Aligns with GDD §17.6.
- **Equip / unequip / drop event** — `Clothing/ClothingManager.cpp:88-163` (slot-based, broadcasts `OnClothingEquip / Unequip / Dropped` with `UClothingItemInstance*`).
- **Censorship toggle** — compliance feature on `NakedDesireCharacter` (`BoobL/R`, `Front/BackBottom` static meshes), driven by `UNakedDesireUserSettings`.
- **AI sight + behavior tree** — `NPC/NPCAIController.cpp` runs a BT with `Player` / `TargetLocation` / `SpawnLocation` blackboard keys. `NPC/NPCSpawner.cpp` proximity-gated spawn with day / night caps.
- **Mission framework** — `Mission``MissionGoal``GoalRestriction` composition (`MissionBuilder/`). Two goals (`FlashGoal`, `MinTimeGoal`), three restrictions (`EquipClothing`, `ExposeBodyPart`, `Location`). Iterate-and-mutate bug in `MissionsManager::RefreshDailyMissions` resolved (`MissionsManager.cpp:53-68`).
- **Daily-mission OOB guarded** — `NakedDesireGameMode::RefreshDailyMissions` now clamps `DaysPassed` to the authored array bounds (`NakedDesireGameMode.cpp:70-71`). Still a hand-authored list (see §1.3).
- **Location triggers** — `Locations/LocationTrigger`, `Locations/LocationData` via gameplay tags (GDD §10.4 area foundation).
- **Censorship toggle** — compliance feature on `NakedDesireCharacter` (BoobL/R, Front/BackBottom static meshes), driven by user settings.
- **Movement** — `EnhancedInput`, walk / run / crouch (`NakedDesireCharacter.cpp:115-127`), stamina-gated run (`Tick` lines 91-113).
- **Wardrobe interactable** — `Interactables/Wardrobe.h` holds `ClothingItems[]`; `NakedDesireGameMode::BuyItem` charges money and pushes into wardrobe.
- **Wardrobe interactable** — `Interactables/Wardrobe.h` holds `TArray<TObjectPtr<UClothingItemInstance>> ClothingItems`; `NakedDesireGameMode::BuyItem` charges money and pushes into the wardrobe.
### 1.2 Partially implemented (deviates from GDD)
- **Item identity (§6.1)** — `Clothing/ClothingItemData.h:14-27` exists as an "instance" UObject but has no unique ID and no per-instance state (no condition, color, coverage float, isUnderwear, canExpose, isRestrictive, containerSlots). `SaveGame/ClothingItemSaveData.h` only stores a `TSoftObjectPtr<UClothingItem>` — it saves the *template*, not the instance. Directly violates the "every item is a unique physical instance" rule.
- **Save system (§16)** — `SaveGame/GlobalSaveGameData.h` only persists `HaveSeenTutorial`, `Money`, and two clothing lists. No world items, NPCs, time, mission state, recognition / reputation / followers, or wanted flag. Save fires only on explicit Blueprint call; load only happens in `ANakedDesireCharacter::BeginPlay`.
- **Attributes (§7)** — `Stats/StatsManager.h:17-31` has only Embarrassment, Energy, Stamina. Missing Lust, Recognition, Reputation, Followers, Wanted. No GAS. `TickComponent` (`StatsManager.cpp:23-28`) unconditionally drains energy and decays embarrassment — observation-driven gain is not wired in C++.
- **Coverage (§6.3.1)** — `ClothingManager::IsBodyTypeExposed` (`ClothingManager.cpp:14-25`) is binary. No coverage float, no underwear halving, no max-of-covering-garments math.
- **Body parts (§6.3)** — only `FrontTop / FrontBottom / BackBottom` exist (`Player/PrivateBodyPartType.h:14-20`). GDD references boobs, ass, genitals as distinct parts.
- **Mission system** — composable goals work but lacks the typed objective steps from §13.4 (`BeFullyNakedNearNPCs`, `WalkNakedDistance`, `MoveDistanceFromClothing`, `BeObservedByNPCType`, `TakePhotoAtLocation`, `DeliverItemTo`). Missions are hand-authored in `MissionsConfig`.
- **Day / night** — affects spawn caps (`NPCSpawner.cpp:40-41`) but not embarrassment rate, NPC type weights, or police spawning.
- **Save subsystem (GDD §16)** — scaffolded but **not actually wired through gameplay**. `USaveSubsystem::SaveGame` (`SaveSubsystem.cpp:10-13`) delegates to `UGlobalSaveGameData::SaveGame`, which creates a *fresh empty* `UGlobalSaveGameData` (`GlobalSaveGameData.cpp:45-53`) and writes it — i.e., it does not capture the live player / wardrobe / world state at all. `USaveSubsystem::LoadGame` calls the static loader but discards the result without applying it (`SaveSubsystem.cpp:5-8`). `ClothingManager::HydrateClothing` (`ClothingManager.cpp:66-73`) is an empty stub with the previous logic commented out. Exit criterion of Phase 1 (round-trip an item with modified condition) is not met yet.
- **Item identity → world (§6.1)** — `UItemInstance` GUIDs exist, but no item has been promoted to a world `AActor`. `ClothingManager::DropClothing` (`ClothingManager.cpp:156-163`) still only broadcasts `OnClothingDropped` — no spawn, no parent reassignment in any registry.
- **Attributes (§7)** — `StatsManager` covers Embarrassment, Energy, Stamina with observation-driven gain now correctly tied to NPC perception. Missing: Lust, Pulse (new, §7.5), Recognition, Reputation, Followers, Wanted. Coverage weighting in the gain formula is stubbed at `0.0f` with a TODO marker (`StatsManager.cpp:30-32`).
- **Coverage (§6.3.2)** — `UClothingItem::Coverage` and `IsUnderwear` exist on the definition, but `ClothingManager::IsBodyTypeExposed` (`ClothingManager.cpp:14-25`) is still binary. No `GetEffectiveCoverage(EBodyPart)` function, no underwear-halving, no sum-of-garments math.
- **Body-part enums — duplicated** — both `Player/PrivateBodyPartType.h` (`EPrivateBodyPartType { FrontBottom, BackBottom, FrontTop }`) and `Clothing/BodyPart.h` (`EBodyPart { Boobs, Ass, Genitals }`) exist. `UClothingItem::CoveredBodyParts` uses the **old** enum; `UClothingItem::CanExpose` uses the **new** one. Half-migrated.
- **Mission system** — composable goals work but lacks the typed objective steps from §13.4 (`BeFullyNaked`, `BeFullyNakedNearNPCs`, `WalkNakedDistance`, `MoveDistanceFromClothing`, `BeObservedByNPCType`, `TakePhotoAtLocation`, `DeliverItemTo`). Missions still hand-authored in `MissionsConfig::DailyMissions` keyed by day index — no procedural generation.
- **Day / night** — `NPCSpawner.cpp:38-41` reads `GetCurrentTime().Hours` and gates spawn cap, but does not affect embarrassment gain, NPC type weighting, or police spawning (see §1.3).
### 1.3 Missing
- **Session system (§4)** — no `USessionManager`, no session start / end events, no `SessionLossResolver`. Game-over on embarrassment is a single `EndGameEmbarrassed` Blueprint event in `NakedDesireGameMode`; none of §4.4's item-drop logic is implemented.
- **Three progression paths (§5)** — no path enum on `UClothingItem`, no Path XP, no path levels, no path-gated content.
- **Session system (GDD §4)** — no `USessionManager`, no session start / end events, no `USessionLossResolver`. Game-over on embarrassment is a single `EndGameEmbarrassed` BP event in `NakedDesireGameMode`; none of §4.4's behavior (energy-zero cutscene to home, bag-placed-in-world loss, sleep-deterministic clothing loss) is implemented.
- **Three progression paths runtime (§5)** — enum exists; no path XP, no per-path level, no path-gated unlocks at runtime, no level-up flow.
- **Phone (§9)** — entire system absent: camera, gallery, livestream, bank, Feetex, maps, health tracker.
- **Forum (§13)** — no `UCommissionTemplate`, no procedural generation, no weekly missions distinct from daily, no profile / followers, no posting photos.
- **Photo & livestream** — absent.
- **NPC types (§10.2)** — only one generic `ANPC` class. No Walker / Stalker / Paparazzi / Snitch / Harasser / Helper, no Police, no Wanted-poster mechanic.
- **Calendar, rent, sleep (§2.4, §15.2)** — `DaysPassed` increments in `NakedDesireGameMode::OnHourChanged(4)`, but no week, no rent, no eviction, no sleep action.
- **Item-world AActor (§6.1)** — clothing drops broadcast a delegate but no `AItemActor` exists.
- **NPC types (§10.2)** — only one generic `ANPC` class. No Walker / Stalker / Blogger / Snitch / Harasser, no Police, no Wanted-poster mechanic.
- **Calendar, rent, sleep (§2.4, §15.2)** — `DaysPassed` increments in `NakedDesireGameMode::OnHourChanged(4)`, but no week, no rent, no eviction, no sleep action, no apartment bed interaction.
- **Item-world AActor (§6.1)** — no `AItemActor` / `AClothingPickup` base.
- **Bag inventory (§6.4)** — absent.
- **Rip & tear, theft (§6.3.3-4)** — absent.
- **Expose action (§6.3.5)** — absent.
- **Restrictive clothing (§6.3.6)** — absent.
- **Theft (§6.3.4)** — new chance-based model (per-tick `P_theft` after grace period) not implemented. No theft timer / probability code at all.
- **Rip & tear (§6.3.5)** — absent. `UClothingItemInstance::Condition` is read-only in code; no decrement source.
- **Expose action (§6.3.6)** — `CanExpose` data exists on the definition; runtime action absent.
- **Restrictive clothing (§6.3.7 / §10.4.1)** — `IsRestrictive` field exists; no enforcement of hand-action locks, no Key-based unlock flow.
- **Adult shop, gym, beauty salon, cafe, convenience store** — none.
- **Food / consumables (§6.6)** — absent.
- **Recognition, wanted state, news (§7.57.6, §11.1)** — absent.
- **Food / consumables (§6.7)** — absent.
- **Recognition, wanted state, news (§7.6–§7.7, §11.1)** — absent.
- **Underwear selling (§15.1)** — absent.
- **Endless mode flag (§3.3)** — absent.
- **Pulse attribute (§7.5)** — absent (new in the updated GDD).
- **GAS adoption (§17.2)** — `StatsManager` is still a bespoke `UActorComponent`; no `UAttributeSet`, no `GameplayEffect`-driven modifiers.
### 1.4 Conflicts to resolve
- **`EClothingSlotType` (`Clothing/ClothingSlotType.h`)** treats `Nipples / Anal / Vagina` as equipment slots; GDD treats those as body parts. Slot list also lacks `Outerwear`, `Accessory`, `Restraint`. Slot vocabulary needs a cleanup pass.
- **`Money` lives on `ANakedDesireCharacter`** (`NakedDesireCharacter.h:139`) as `int Money = 300000`, and also as a field on `UGlobalSaveGameData`. Duplicate state. Default 300k vs. ~20k weekly rent (§15.3) — test value.
- **`MissionsManager::RefreshDailyMissions`** (`MissionBuilder/MissionsManager.cpp:53-68`) has a remove-while-iterating bug; `OnComplete.RemoveAll(this)` is called on the *mission*, not on the manager handle.
- **`NakedDesireGameMode::RefreshDailyMissions`** indexes `MissionsConfig->DailyMissions[DaysPassed]` — crashes past the authored array and contradicts §13.4 procedural generation.
- **`StatsManager::TickComponent` unconditionally decays embarrassment** (`StatsManager.cpp:27`). §7.1 says decay should happen only when not observed; today no observation-driven gain offsets it.
- **Save constant `SLOT_NAME "Slot2"`** (`Global/Constants.h:5`) — magic single-slot save, no multi-save support.
- **`UClothingItemData` is `EditInlineNew`** and stored inline in `UClothingList::ClothingItems`. Instances live inside the list asset, not as standalone objects with stable identity. Direct conflict with §6.1.
- **`SaveGame` UPROPERTY typo** — `GlobalSaveGameData.h:35` declares `UPROPERTY(SaceGame)` instead of `SaveGame` on `DaysPassed`. The field will not serialize. (Likely also blocks Unreal Header Tool warnings; needs a clean rebuild after the fix.)
- **`EClothingSlotType`** (`Clothing/ClothingSlotType.h`) still includes `Nipples`, `Anal`, `Vagina` as equipment slots — those are body parts per the GDD. Slot list also lacks `Outerwear`, `Accessory`, `Restraint` (GDD §6.3 / §21 Q1). The character builds 14 skeletal mesh components against these values (`NakedDesireCharacter.cpp:40-67`), so the cleanup needs a matching content / BP pass.
- **Two competing body-part enums** — `EPrivateBodyPartType` vs. `EBodyPart`. `ClothingItem::CoveredBodyParts` uses one, `ClothingItem::CanExpose` uses the other. The censorship / observation code paths in `NakedDesireCharacter` and `ClothingManager` are still on `EPrivateBodyPartType`. Pick one (recommend `EBodyPart`) and migrate.
- **`USaveSubsystem::SaveGame` writes an empty save** — it does not pull state from the live world before writing (`SaveSubsystem.cpp:10-13`, `GlobalSaveGameData.cpp:45-53`). Any "save" call at present destroys progress rather than persisting it. Highest-priority correctness bug.
- **`USaveSubsystem::LoadGame` ignores the loaded object** — the static returns a populated `UGlobalSaveGameData`, the subsystem stores nothing and applies nothing.
- **`UClothingList`** forward-declared in `GlobalSaveGameData.h:11` and `Wardrobe` / save records no longer use it. Dead reference; remove.
- **`Money` representation** — `ANakedDesireCharacter::Money` is `int` (`NakedDesireCharacter.h:132`), `UGlobalSaveGameData::Money` is `float` (`GlobalSaveGameData.h:27`). The earlier "Resolve Money duplication" commit consolidated the field locations but the type mismatch and the routing-through-save story are unfinished — verify which is authoritative before adding economy systems on top.
- **`STARTING_MONEY`** macro added to `Constants.h:8` but no code references it. Either wire it into the player / save init, or remove.
- **`ClothingItemInstance` has no public constructor / factory** — `ClothingItem` pointer is `protected` with no `Init(UClothingItem*)`. Wardrobe / BuyItem flows currently `EditAnywhere Instanced` populate the array at design-time, which works for the existing test level but won't support runtime purchase or save-driven hydration.
- **`StatsManager::TickComponent` energy drain** — drains energy at a flat `0.9f` per tick (`StatsManager.cpp:26`) regardless of activity, indoor / outdoor, or modifiers. GDD §7.3 specifies per-activity modifiers (`energyDrainRunModifier`, etc.).
- **`NPCSpawner` day window** — uses `Hours >= 9 && < 21` for "day" (`NPCSpawner.cpp:40`), GDD §10.1 says the day phase is `08:00 → 20:00`. Off-by-one and an hour offset.
- **GDD shifts not yet reflected in code:**
- Phase 4-era loss resolver work must avoid stripping equipped clothing — GDD §4.4 now says equipped items are **never** force-removed on loss.
- Energy = 0 must trigger a "cutscene to apartment + sleep" flow, not a teleport (GDD §4.4).
- Theft is now a chance-based model with grace periods (`T_grace`, `T_grace_bag`) and flat per-tick `P_theft`, not a ramping curve (GDD §6.3.4).
- Sleep is the deterministic side of clothing loss — guaranteed loss of every item left outside the apartment (GDD §4.4).
- No Helper NPC, no adult-shop unlock; restraint removal is Key + timed unlock only (GDD §10.4.1).
- Masturbation gating is Slut-path only (GDD §5.1 / §7.2 / §14.1).
- Police spawn day **and** night while `wanted` (GDD §7.7 / §10.3) — current spawner has no police path at all, but record this when it's built.
---
## 2. Architectural premise
The item-identity rule (§6.1) and the save contract (§16.3) are load-bearing. Clothing condition, world drops, session loss, theft, the bag, and the forum's "post a photo of *this* specific worn item" all depend on stable per-instance state.
The item-identity rule (§6.1) and the save contract (§16.3) are still load-bearing. With the definition / instance split in place, the next bottleneck is **wiring serialization through gameplay**: condition mutation, world drops, container parent links, hydration on load. Until that round-trip works, content on top of the current system has no durable home.
Until Phase 1 lands, content authored on top of the current system needs migration. Do not build the higher phases first.
The session-system + loss resolver (Phase 3) is the second pin. Several GDD shifts (equipped-never-dropped, energy-zero cutscene, sleep-deterministic clothing loss) move complexity from §4.4 prose into a single deterministic resolver. Build it once, well.
GAS adoption (§17.2) remains deferred until Phase 4. Adding new attributes to the bespoke `StatsManager` in the meantime is fine — keep them additive and easy to migrate.
---
@@ -76,93 +99,129 @@ Until Phase 1 lands, content authored on top of the current system needs migrati
Phase estimates are rough and assume one engineer. Adjust as we go.
### Phase 1 — Item identity + save foundation (12 weeks)
### Phase 1 — Finish item identity + save round-trip (1 week)
- Split `UClothingItem` (immutable definition, `UPrimaryDataAsset`) from a new `UItemInstance` UObject with `FGuid InstanceId` and mutable state (`Condition`, `Color`, container parent, world transform). Make `UClothingItemInstance : UItemInstance`.
- Add to the definition: `Coverage` float, `IsUnderwear`, `CanExpose` list, `IsRestrictive`, `ContainerSlots`, `ProgressionPath` enum.
- Enumerate slots cleanly — split slot vs. body-part vocabulary (resolves §1.4 conflict).
- Replace `FClothingItemSaveData` with a generic `FItemSaveRecord { Guid, DefinitionPath, InstanceState, Location, ParentRef }`.
- Introduce `USaveSubsystem` (GameInstance subsystem) that orchestrates serialization across player, world items, time, missions, attributes, recognition.
- Convert single-slot `SLOT_NAME` to a slot-name argument.
Most of the structural work is done. What remains is connecting the pieces so an item modified at runtime survives a save / load cycle.
**Exit criteria:** an item dropped in the world, with its condition modified, survives save / load with the same GUID and condition value.
- Fix the `SaceGame` UPROPERTY typo on `DaysPassed` (`GlobalSaveGameData.h:35`).
- Add `UClothingItemInstance::Init(UClothingItem* Definition)` (or a static factory) so instances can be constructed at runtime — not just `Instanced` at design-time.
- Make `USaveSubsystem::SaveGame` actually pull live state into a `UGlobalSaveGameData`: player money, equipped items, wardrobe items, calendar, observation-attribute snapshot. Cover `WardrobeItems` and `EquippedItems` arrays via `FItemSaveRecord` populated from current `UClothingItemInstance`s (Guid, definition soft pointer, condition, parent guid).
- Make `USaveSubsystem::LoadGame` actually apply the loaded data: rebuild `UClothingItemInstance`s from records, push into the wardrobe array and `ClothingManager` slots. Replace the commented-out `ClothingManager::HydrateClothing` with the real hydration.
- Unify `EPrivateBodyPartType` and `EBodyPart` (recommend keeping `EBodyPart`, retiring the legacy enum). Update `ClothingItem::CoveredBodyParts`, `ClothingManager::IsBodyTypeExposed`, censorship checks in `NakedDesireCharacter`, and the BT / observation paths.
- Slot vocabulary pass: drop `Nipples` / `Anal` / `Vagina` from `EClothingSlotType`, add `Outerwear` / `Accessory` / `Restraint`. Update the character's mesh component layout and any BP references in lockstep.
- Money: pick one authoritative location (`UGlobalSaveGameData` is the natural choice; `ANakedDesireCharacter::Money` becomes a cached read-through). Apply `STARTING_MONEY` at new-save creation. Convert character field to match save type.
- Remove the dead `UClothingList` forward declarations.
**Exit criteria:**
1. Item dropped at runtime, condition modified, save → quit → load reproduces the same `FGuid`, condition value, and slot / wardrobe membership.
2. `EBodyPart` is the only body-part enum in use.
3. Slot list matches GDD §6.3 / §21 Q1 decision.
4. A new game starts with `STARTING_MONEY` and rent / purchase flows debit the canonical money field.
### Phase 2 — World item actors + drop / pickup (1 week)
- `AItemActor` base wrapping a `UItemInstance*`. `AClothingPickup : AItemActor`.
- Hook `ClothingManager::DropClothing` to spawn `AClothingPickup` at the player location with the actual instance reference (no template copy).
- Pickup interaction via existing `InteractionManager`.
- `AItemActor` base wrapping a `UItemInstance*` (in `Items/`). Spawn transform, owning instance pointer, SaveGame-tagged.
- `AClothingPickup : AItemActor` with a visual representation (use `UClothingItem::StaticMesh` when set, fallback to skeletal mesh impostor).
- Hook `ClothingManager::DropClothing` to spawn `AClothingPickup` at the player location with the actual instance reference — *not* a template copy. Reparent the instance from "equipped slot" to "world".
- Pickup interaction via existing `UInteractionManager` + `IInteractionTarget`. Pickup re-parents the instance into wardrobe or directly into the matching slot.
- `USaveSubsystem` extension: serialize `AClothingPickup`s as `FItemSaveRecord`s with world transform.
**Exit criteria:** unequip → world actor appears → re-pick-up restores the same instance to the slot.
**Exit criteria:** unequip → world pickup actor appears → save / reload → pickup is still there at the same transform with the same instance ID → pick up restores the same instance to the slot.
### Phase 3 — Session system + loss resolver (1 week)
- `USessionManager` (GameMode component). Apartment `LocationTrigger` flag drives session start / end.
- `USessionLossResolver` — single class, per §4 implementation note. Takes the loss cause and runs §4.4 deterministically over the new world-item registry.
- Wire `StatsManager::IncreaseEmbarrassment` max-hit and energy-zero into the resolver.
- `USessionManager` (subsystem on `UNakedDesireGameInstance` or a `UWorldSubsystem`). Apartment `ALocationTrigger` flag drives session start / end. Emits `OnSessionStart` / `OnSessionEnd` with cause.
- `USessionLossResolver` — single class, one method `ResolveLoss(ESessionLossCause Cause)`. Implements GDD §4.4:
- Equipped clothing **stays** equipped (do not strip).
- Bag placed in world: mark its world record for deletion.
- Loose clothing on ground: leave as-is (theft chance has been running through Phase ?-7 logic; sleep step is what finalizes).
- Energy=0 path: trigger a cutscene → apartment → sleep cycle → guaranteed loss of every world clothing outside the apartment safe zone.
- Embarrassment-max / police-capture path: fade to apartment, no sleep cycle, no forced clothing loss.
- Police-capture: deduct money penalty, queue skipped days for the unpaid remainder, clear `wanted`.
- Wire `StatsManager::IncreaseEmbarrassment` max-hit and energy-zero into `USessionLossResolver`. Replace `EndGameEmbarrassed` BP-event with the C++ path.
- Add a debug overlay that shows the current loss state and what would be lost if a given cause fired.
**Exit criteria:** lose to embarrassment → equipped clothing spawns as world actors at the loss location, persists through save.
**Exit criteria:** all four code paths (safe end, embarrassment max, energy zero, police capture) produce the §4.4 outcomes deterministically. Inventory state after each loss matches the §6.6 summary table 1:1.
### Phase 4 — Attributes refactor (1 week)
### Phase 4 — Attributes refactor (12 weeks)
- Decide GAS vs. extended `StatsManager` (recommend GAS per §17.2; embarrassment formula has many modifiers that GAS effects model cleanly).
- Add Lust, Recognition, Reputation, Followers, Wanted.
- Move observation-driven embarrassment gain out of BP into the C++ pipeline (use the existing `CanBeSeenFrom` result + per-NPC type weights).
- Add Lust + masturbate action + blackout vision effect.
- Add Recognition with face-cover bypass + Wanted bool.
- Decision: GAS vs. extended `StatsManager`. Recommend GAS — the embarrassment formula now has Pulse, Recognition, Coverage, Day/Night, and per-NPC-type weights as modifiers (§7.1), which is exactly what `UGameplayEffect` is shaped for.
- Add Lust (§7.2), Pulse (§7.5, **new**), Recognition (§7.6), Wanted (§7.7), Reputation (§7.8), Followers (§7.9). Money should be the only attribute that also lives in the save schema directly (since it has non-attribute side effects).
- Implement `GetEffectiveCoverage(EBodyPart)` on `ClothingManager` per §6.3.2 (sum of garments covering the part, underwear halved, clamped to 1.0). Replace the `CoverageWeight = 0.0f` stub in `StatsManager::TickComponent`.
- Lust masturbate action (Slut-path gated, §7.2). Block the action in BP / quick-action when the player's Slut path level is 0.
- Pulse simulation: rises with running, masturbation, exposure events; decays toward baseline at rest. Modifies embarrassment / lust gain (§7.5).
- Recognition rise from Blogger photo events; face-cover bypass; reduce via news-site reporting (§11.1).
- Debug overlay extension: live values + active modifier sources for every attribute.
**Exit criteria:** observed-while-exposed gains embarrassment from C++; unobserved decays; both rates are inspectable in a debug overlay.
**Exit criteria:**
1. Observed-while-exposed embarrassment gain reads coverage and modifiers from the attribute system (no more `0.0f` stub).
2. Lust + Pulse simulate correctly and visibly influence embarrassment in a playtest scenario.
3. Recognition increases after a (mocked) photo event and decreases after a news-report action.
### Phase 5 — Time + calendar + rent + sleep (34 days)
- `UTimeOfDaySubsystem` replacing the BP-implementable time on `NakedDesireGameMode`. 90-day calendar, week boundary, rent transaction.
- Sleep action on apartment bed: time-skip + energy restore + autosave.
- Endless-mode flag on the save.
- `UTimeOfDaySubsystem` replacing the BP-implementable time on `NakedDesireGameMode`. 90-day calendar, week boundary, day phase `08:0020:00` (fix the 0921 mismatch in `NPCSpawner.cpp:40`).
- Sleep action on apartment bed: triggers the same path as energy-zero (§4.4) for the "items left outside" cleanup, restores energy, autosaves, advances calendar.
- Weekly rent transaction at week boundary; eviction if money insufficient (`game over (run)`, §3.3).
- Endless-mode flag on `UGlobalSaveGameData`; rent-eviction branch checks it.
**Exit criteria:** play through 7 in-game days, get charged rent at week boundary, eviction triggers on insufficient funds.
**Exit criteria:** play through 7 in-game days, get charged rent at the week boundary, eviction triggers on insufficient funds, endless-mode disables eviction.
### Phase 6 — NPC types + recognition pipeline (12 weeks)
- `ENPCType` enum, type-specific BT branches or per-type controller subclasses.
- Walker / Stalker / Paparazzi / Snitch / Harasser / Helper / Police.
- Recognition flow: Paparazzi photo → article → news site → "report" reduces recognition.
- `ENPCType` enum, type-specific BT branches or per-type controller subclasses: Walker / Stalker / Blogger / Snitch / Harasser. **No Helper** (cuff removal is Key-only per §10.4.1).
- Police as a separate AI controller / spawn pipeline. Patrols spawn day and night while `wanted` (§7.7 / §10.3). Detection logic (`face hidden?` + coverage threshold) → chase → break-LOS timer.
- Wanted-poster mechanic: spawn posters in the city; tearing all down clears `wanted` (§10.3).
- Recognition flow: Blogger sight + photo event → article → news-site article state → "report" reduces recognition.
- Wire Snitch's "report to police" → immediate police spawn this session + sets `wanted` (§7.7).
**Exit criteria:** each NPC type observably distinct behavior; recognition rises after paparazzi exposure and can be reduced via news report.
**Exit criteria:** every listed NPC type observably distinct; recognition rises after a Blogger event and can be reduced via news report; Snitch report triggers police spawn and the `wanted` tag that persists across sessions until posters torn down or capture.
### Phase 7 — Commission template system (12 weeks)
- Replace `MissionsConfig`'s hand-authored daily list with `UCommissionTemplate` + procedural generation per §13.4.
- Add typed objective steps as new `UMissionGoal` subclasses (`BeFullyNakedNearNPCs`, `WalkNakedDistance`, `MoveDistanceFromClothing`, `BeObservedByNPCType`, `TakePhotoAtLocation`).
- Weekly vs. daily generation pass.
- Add typed objective steps as new `UMissionGoal` subclasses:
- `BeFullyNaked(duration)` (new in §13.4)
- `BeFullyNakedNearNPCs(count, duration)`
- `WalkNakedDistance(meters)`
- `MoveDistanceFromClothing(meters)`
- `BeObservedByNPCType(type, durationOrCount)`
- `TakePhotoAtLocation(locationTag)` (requires phone work from Phase 8 — gate this objective until then)
- `DeliverItemTo(npcOrLocation)`
- Path-tagged XP (§5). Generation weights against player path progression.
- Weekly-vs-daily generation pass.
**Exit criteria:** daily commissions regenerate from templates each in-game day; weekly arc is distinct; failed commissions deduct reputation.
### Phase 8 — Phone + forum UI (23 weeks)
- Phone as a holdable / pocketable `AItemActor`.
- Apps: Camera + Gallery, Livestream (`StreamSession` tick object), Bank, Feetex, Maps, Health Tracker, Forum.
- Photo system: pick render-to-texture (§17.6) — recommended for in-fiction photo posts.
- Phone as an `AItemActor` (Phase 2 base) with hand / pocket / bag location semantics (§9).
- Apps: Camera + Gallery, Livestream (`UStreamSession` tickable UObject — works while phone is held *or* placed, §9.1.1 exception), Bank, Feetex, Maps, Health Tracker, Forum.
- Photo system: render-to-texture (§17.6 / §21 Q6 — recommended).
- Wire `TakePhotoAtLocation` commission step to actual photo events.
**Exit criteria:** end-to-end photo loop — equip phone, capture photo, view in gallery, post to forum, follower count updates.
**Exit criteria:** end-to-end photo loop — equip phone, capture photo, view in gallery, post to forum, follower count updates. Livestream can be started, run while the phone is set down, and ended with payout deposited.
### Phase 9 — Path progression + content authoring (2 weeks)
- Path XP per path, level gating on clothing / missions / attribute upgrades.
- Author the vertical-slice content set from §18.1.
- Author the vertical-slice content set from GDD §18.1 (15-20 clothing items, 3 bag variants, 8 food items, 20 commission templates, 1 functional district).
- Masturbation gating (§5.1, §7.2) — actually hide the quick-action entry until Slut path level 1.
**Exit criteria:** all three paths have at least 5 unlockable items and 3 path-specific commission templates.
**Exit criteria:** all three paths have at least 5 unlockable items and 3 path-specific commission templates. Slut-path-locked actions are correctly hidden / gated.
### Phase 10 — Remaining systems & polish
- Bag inventory (§6.4).
- Food / cooking (§6.6).
- Gym / beauty salon / adult shop (§10.4).
- Rip and tear (§6.3.4).
- Theft timer (§6.3.3).
- Expose action (§6.3.5).
- Restrictive-clothing flow (§6.3.6, §10.4.1).
- Underwear selling (§15.1).
- Wanted-poster takedown (§10.3).
- Bag inventory (§6.4) — uses Phase 2's `AItemActor` for world placement.
- Food / cooking (§6.7) — three minigames per the GDD (slice, stir, cook).
- Gym / beauty salon / adult shop / cafe / convenience store (§10.4).
- Rip & tear (§6.3.5).
- Theft model (§6.3.4) — implement `T_grace` / `T_grace_bag` grace timers and per-tick `P_theft` chance roll. No ramp.
- Expose action (§6.3.6) — read `CanExpose` per garment, blocked by overlapping coverage.
- Restrictive-clothing flow (§6.3.7 / §10.4.1) — Key + timed unlock action. Lock hand-dependent actions while restrained.
- Underwear selling (§15.1) — Feetex drop-box or location drop-off variant.
- Wanted-poster takedown (§10.3) — interaction + spawn placement pass.
- Final tuning: `T_grace`, `T_grace_bag`, `P_theft`, embarrassment / lust / energy / pulse rates (§21).
---
@@ -170,4 +229,7 @@ Phase estimates are rough and assume one engineer. Adjust as we go.
Use this section for in-flight decisions, blockers, and open questions that emerge during a phase. Move resolved items into the README's §20 "Resolved Design Decisions" or into git history.
- _empty_
- **Phase 1 critical path:** the save-write-without-state bug is the most damaging current issue — any "save" call destroys progress. Fix the SaveSubsystem before anything else builds on save behaviour.
- **Body-part enum migration:** the existing censorship code in `NakedDesireCharacter::OnClothingEquip` / `OnClothingUnequip` reads `EPrivateBodyPartType`. Migration to `EBodyPart` must update that path or the censorship visibility will silently desync.
- **Slot-vocabulary cleanup will break BP references** — coordinate before deleting `Nipples` / `Anal` / `Vagina` from `EClothingSlotType`. The character constructor creates skeletal-mesh components keyed to those names; expect BPs in `Content/Blueprints/Player` to need a touch.
- _empty beyond this point_
+136 -95
View File
@@ -70,7 +70,7 @@ Move → observe NPC density and types → make a coverage/exposure decision →
- **Win (campaign):** Survive 90 days. Endings are gated by progression path level and total followers (TBD specific thresholds).
- **Endless mode:** Unlocked after first campaign completion. Disables rent-based eviction. All other systems (session loss, embarrassment, lust, recognition, etc.) remain active. Lets players keep playing their character indefinitely. Implementation cost is minimal — a single flag on the run.
- **Lose (run):** Cannot pay rent → evicted → game over. Disabled in endless mode.
- **Lose (session):** Energy → 0, OR embarrassment → max, OR caught by police. Session loss has consequences (see §6.5) but does not end the run.
- **Lose (session):** Energy → 0, OR embarrassment → max, OR caught by police. Session loss has consequences (see §4.4 and the summary table in §6.6) but does not end the run.
---
@@ -88,16 +88,20 @@ Triggered by leaving the apartment. Snapshots current equipment, attributes, and
- The player can freely re-enter the apartment to safely end the session at any time.
### 4.3 Session end (safe)
Player returns home. All carried items persist. Rewards from completed commissions are finalized. Embarrassment resets toward baseline. Energy partially restored (full restore requires sleep).
Player returns home. All carried items persist. Rewards from completed commissions are finalized. Embarrassment resets toward baseline. If a Snitch reported the player to police during the session (§10.2), the player keeps the `wanted` tag even after a safe return — see §7.7.
### 4.4 Session end (loss)
Triggered by any of the four loss conditions. On loss:
- The player is teleported home (or to a fail-state location — TBD).
- **All currently equipped clothing is forcibly dropped** at the loss location.
- **A bag currently placed in the world** (not carried) is lost along with its contents.
- **Unequipped clothing already lying on the ground anywhere** is lost.
- Reputation penalty (TBD magnitude).
- If lost to police: `wanted` flag set, requires action to clear (see §10.3).
Triggered by any session-loss condition (§3.3). On loss:
- **The player returns to the apartment.** The route home depends on the cause:
- **Energy = 0** — a cutscene plays of the player trudging home, exhausted, and going to bed. The player never sleeps outside; the apartment bed is the only sleep location. Time advances by one sleep cycle.
- **Embarrassment = max** — fade to apartment (or a dedicated fail-state location — TBD).
- **Police capture** — the player is processed (money penalty below) and returned home.
- **Equipped clothing is never forcibly removed by loss.** Whatever the player was wearing at the moment of failure stays equipped on arrival home.
- **A bag placed in the world** (not carried) is lost along with its contents — the player did not retrieve it before the failure.
- **Unequipped clothing left on the ground** can be lost two ways:
1. **In-session theft** — a chance-based roll while the player is away from the item. See §6.3.4 for the model.
2. **Sleep** — any clothing left outside the apartment when the player sleeps is **guaranteed** lost. This fires both for the energy-zero cutscene (forced sleep) and for voluntary sleep at home with items still outside.
- **Police capture** during pursuit applies a money penalty. If the player can't cover it, they skip days until the debt clears; the number of skipped days scales with the unpaid amount. Capture also clears the `wanted` tag (§7.7).
> **Implementation note:** session loss must be a clearly defined transactional state. All "what gets lost" logic should live in a single SessionLossResolver to keep this deterministic and debuggable.
@@ -138,11 +142,11 @@ Each path has a level (e.g., 110). Level gates clothing, missions, and attrib
- Items in the world are AActors. Items in containers are owned by the container's inventory component but retain identity.
- Saving and loading must preserve item identity, position, and per-instance state.
### 6.2 Item categories
### 6.2 Carriable item categories
- **Clothing** (§6.3)
- **Bags** (§6.4)
- **Phone** (one — special, §9)
- **Consumables** (food, §6.6)
- **Consumables** (food, §6.7)
- **Gadgets** (TBD beyond phone)
### 6.3 Clothing
@@ -158,64 +162,80 @@ Attributes per clothing instance:
- `isRestrictive` — bool. If true, restricts hand use; requires NPC to remove.
- `containerSlots` — optional inventory (e.g., pants pocket for phone).
#### 6.3.1 Coverage resolution
#### 6.3.1 Container slots
Container slot allows player to carry items there.
- `size`: S, M, L - Size of the container slot
- `count`: Count of the container slots
#### 6.3.2 Coverage resolution
For each body part `b`:
- Find the set of equipped garments covering `b`.
- Effective coverage of `b` = `max(effectiveCoverage of garments covering b)` where `effectiveCoverage = isUnderwear ? coverage/2 : coverage`.
- If a non-underwear garment covers `b`, underwear coverage of `b` is ignored.
- Effective coverage of `b` = `min(sum(effectiveCoverage of garments covering b), 100%)` where `effectiveCoverage = isUnderwear ? coverage/2 : coverage`.
- Body part is "exposed" if effective coverage = 0.
- Lower total coverage → faster embarrassment gain when observed (§7.1).
#### 6.3.2 Equipping & unequipping
#### 6.3.3 Equipping & unequipping
- Equip/unequip anywhere in the world via the quick action menu.
- Unequipped clothing becomes a world AActor at the player's location.
- Clothing on the ground can be picked up when the player is in range.
#### 6.3.3 Theft
- Clothing left unattended (no player nearby, not in a bag) for X minutes (TBD) is at risk of being stolen by an NPC.
- Clothing inside a bag (even if the bag is on the ground) is safer but the **bag itself** can be lost if the player loses the session while the bag is placed in the world.
#### 6.3.4 Theft
Theft applies to loose clothing on the ground and to bags placed in the world (see §6.4). The model has two layers — a chance-based in-session roll and a deterministic sleep-loss handled in §4.4. Tuning placeholders below are resolved in §21.
#### 6.3.4 Rip & tear
- **Grace period.** Loose clothing on the ground is safe for `T_grace` minutes (TBD) after the player leaves it unattended. Bagged contents (bag set down in the world) get a longer grace period `T_grace_bag` (TBD, longer than `T_grace`); the bag protects its contents until then.
- **Chance roll.** Once an item is past its grace period, each check tick is an independent roll at a flat probability `P_theft` (TBD %). The probability does **not** ramp with time — leaving an item out for 20 minutes is not strictly riskier per minute than leaving it out for 5 minutes; the only thing time does is give more independent rolls.
- **Resets.** The grace timer resets when the player re-enters proximity of the item or when the player is inside a safe zone (apartment).
- **Sleep is not handled here.** Sleeping (voluntary at home or via the energy-zero cutscene) is the deterministic side of the model — any clothing left outside the apartment at sleep time is guaranteed lost, regardless of where it sits in the grace / roll cycle. See §4.4.
#### 6.3.5 Rip & tear
- Moving through bushes, against walls, or other rough collisions decrements `condition`.
- Damaged clothing renders visually torn and exposes additional body parts (coverage drops).
- Damage is irreversible. Damaged clothing cannot be repaired.
- When `condition` reaches 0, the item disappears.
- New clothing always starts at `condition = 1.0`.
#### 6.3.5 Exposing
#### 6.3.6 Exposing
- Some clothing (e.g., coat, pleated skirt) supports a per-garment expose action.
- Triggering expose temporarily reveals listed body parts without removing the garment.
- Blocked if another garment covers the same part (e.g., pants block coat's ass-expose).
- While exposed, the body part counts as fully uncovered for embarrassment math.
#### 6.3.6 Restrictive clothing
- Cuffs and similar items lock hand-dependent actions (using phone, picking up items, masturbating).
- Removal options (see §10.4.1):
- **Adult shop** — paid, reliable, fixed location.
- **Helper NPC** — free, rare random spawn, not reliable.
#### 6.3.7 Restrictive clothing
- Cuffs, restrain bars and similar items lock hand-dependent actions (using phone, picking up items, masturbating).
- Can be removed by having key and spending some time to remove it.
### 6.4 Bags
- Bag is an equippable item with its own inventory.
- Player can equip **one** bag at a time.
- Bag can be placed in the world (set down) — persists as an AActor.
- Default carry capacity without a bag: equipped clothing + one phone-in-hand.
- Bag lost if session ends in loss while the bag is placed in the world (not equipped).
- Bags follow the same theft rules as clothing (§6.3.4) and the same session-loss rules as other items (§4.4 / §6.6).
### 6.5 Item loss summary table
### 6.5 Inventory & Equipment
- **Clothing slots** — one per equipment slot type (see §6.3 `type` field for the slot vocabulary; final list is TBD per §21 Q1).
- **Bag slot** — 1 (see §6.4).
- **Hand slot** — 1. Holds one carriable item at a time (phone, food, key, etc.). Restrictive clothing (§6.3.7) disables all hand-dependent actions while equipped.
- **Container slots** — additional storage exposed by equipped clothing (e.g., pants pocket) and by the equipped bag. Each container slot has a size class (S / M / L, §6.3.1) that constrains what fits.
- There is no abstract "backpack" inventory. Total carry capacity = sum of container slots provided by currently equipped items.
### 6.6 Item loss summary table
| Situation | Outcome |
|---|---|
| Session ends safely | All carried/equipped/placed items persist. |
| Session lost, item equipped | Item forcibly dropped at loss location. |
| Session lost, item in equipped bag | Item persists (bag is on you). |
| Session lost, bag placed in world | Bag and contents lost. |
| Session lost, clothing on ground (unattended) | Lost. |
| Clothing unattended too long, no loss | At risk of NPC theft. |
| Session ends safely | All carried / equipped / placed items persist. |
| Session lost, item equipped | Item persists, fully equipped. Loss never forcibly removes worn clothing (§4.4). |
| Session lost, item in equipped bag | Item persists (bag is on the player). |
| Session lost, bag placed in world | Bag and contents are subject to the same theft chance as clothing (§6.3.4) and to sleep-loss if the player sleeps before retrieving them. |
| In-session, clothing past the grace period | Each check is a flat chance of theft (§6.3.4). Picking the item back up resets the grace timer. |
| Sleep with clothing left outside the apartment (voluntary or forced energy-zero cutscene) | **Guaranteed** loss of every clothing item left outside, regardless of grace / chance state. |
### 6.6 Consumables — Food
### 6.7 Consumables — Food
- Restores energy.
- May grant timed buffs (e.g., ramen: +stamina regen for 3h in-game).
- Buffs are typed and stackable per design (specifics TBD).
- Some foods require cooking (ingredients from convenience store), some are pre-made (from cafe).
- Food is cooked via mini-games
- Slice ingredients via fruit ninja
- Stir ingredients via clicker(Mortal Kombat - Test your strength)
- Cook ingredients via Genshin Impact cooking minigame(DBD generator fix minigame)
---
@@ -239,11 +259,12 @@ All attributes update in real time. Indoor and outdoor behavior is identical —
### 7.2 Lust
- Increases passively in the background (rate TBD, possibly tied to coverage).
- Higher pulse increases lust gain rate.
- At maximum:
- Blurred peripheral vision.
- Player cannot tell whether NPCs are calling police or taking photos.
- No indicator of which NPCs noticed them.
- Reset by **masturbating** (consumes more energy than usual, modified by `energyDrainMasturbateModifier`).
- Reset by **masturbating** (consumes more energy than usual, modified by `energyDrainMasturbateModifier`). Masturbation is a Slut-path unlock (§5.1) — players who haven't taken that path cannot reset lust within a session and must accept the max-lust handicaps above until they sleep.
### 7.3 Energy
- Drained by all activity. Base rate plus modifiers:
@@ -255,46 +276,64 @@ All attributes update in real time. Indoor and outdoor behavior is identical —
- Recovered during sleep at `energyRecoveryRate`.
- Hard floor at 0 → session lost.
- Max raised by gym training.
- Low energy reduces effective max stamina (see §7.4).
### 7.4 Stamina
- Used for sprinting and other burst physical actions.
- Recovers at `staminaRecoveryRate` when not sprinting.
- Max raised by Slave-path leveling.
- Distinct from energy: stamina is short-term, energy is daily budget.
- Used for running and other burst physical actions.
- Recovers at `staminaRecoveryRate` when not running.
- Base max raised by Slave-path leveling.
- Distinct from energy: stamina is short-term, energy is the daily budget.
- **Effective max stamina** is derived from base max in two passes (applied in order):
1. **Food buffs** — additive; can raise effective max above base.
2. **Energy multiplier** — scales the result by `staminaEnergyFactor`, where 0% energy → 50%, 80100% energy → 100% (linear in between).
- Net effect: a well-fed but exhausted player has lower effective max stamina than a well-fed, rested player; food buffs cannot fully compensate for low energy.
### 7.5 Recognition
- Each photo taken by a Paparazzi NPC and published increases recognition.
### 7.5 Pulse
- Simulated heart-rate / arousal-rate attribute. Tracked alongside the other attributes in §7.
- Rises with:
- Physical exertion (running, masturbation, sex acts).
- Exposure events (a body part becoming uncovered, an `expose` action triggering).
- Being observed while exposed.
- Decays toward a personal baseline when at rest.
- **Modifies** embarrassment gain (§7.1) and lust gain (§7.2) — both scale with pulse.
- Exhibitionist-path attribute pool: leveling can raise / lower baseline pulse and adjust pulse-response curves (tuning TBD).
### 7.6 Recognition
- Each photo taken by a Blogger NPC and published increases recognition.
- Higher recognition → faster embarrassment gain (NPCs recognize the player).
- Reducible by reporting articles on the city news site (PC, §11.1).
- Can be **bypassed** by hiding the face (mask, hat-and-glasses combo — TBD specifics).
### 7.6 Wanted
- Boolean. Set when caught by police or when triggered by specific commission failures.
- Affects police patrol density and aggression.
- Cleared by tearing down wanted posters in the city (§10.3).
### 7.7 Wanted
- Boolean. Set when a Snitch (§10.2) successfully reports the player to police during a session. The tag persists even if the session ends safely afterward (§4.3).
- While set, police patrols spawn during **both day and night** until the tag is cleared (§10.3). The day/night cycle does not gate patrols once the player is `wanted`.
- Cleared by either:
- Tearing down every wanted poster in the city (§10.3), or
- Being caught by police (§4.4) — capture also clears the tag along with applying the money penalty.
### 7.7 Reputation
### 7.8 Reputation
- Earned by completing commissions; lost by failing or skipping them.
- Degrades passively over time, faster at higher values.
- Affects follower gain: positive/neutral → followers grow; negative → followers shrink.
### 7.8 Followers & Money
### 7.9 Followers & Money
- **Followers** — running total of forum followers. Used in subscriber-style follower-gain math when posting photos.
- Followers generate money each week.
- More followers means more money for completing commissions.
- **Money (Yen)** — earned from commissions, livestream donations, selling worn underwear. Spent on rent, clothing, food, gym, beauty salon.
### 7.9 Attribute leveling
### 7.10 Attribute leveling
Path XP unlocks the *ability* to upgrade attributes within that path's pool. Money or a separate currency may be required per level-up (TBD — recommend keeping it tied to XP only for clarity, but flag for playtest).
---
## 8. Movement
States: `stand`, `walk`, `run`, `crouch`, `crouchWalk`, `crouchRun`.
States: `stand`, `walk`, `run`, `crouch`, `crouchWalk`.
Design implications:
- Crouching reduces NPC detection radius (TBD multipliers).
- Running drains stamina and energy faster but is required for escape.
- Crouch-run is a compromise state for traversing risky exposed areas.
Movement should be implemented as a state machine on the character with clear transitions. UE5's enhanced input + ALS-style or GASP-style animation graph is appropriate (Oleh has prior GASP/Motion Matching context).
@@ -302,7 +341,7 @@ Movement should be implemented as a state machine on the character with clear tr
## 9. Phone
The phone is the player's diegetic UI hub. It can be used if it is in: hand, pocket (pants with phone slot), or equipped bag.
The phone is the player's diegetic UI hub. It can be used if it is in: hand, pocket (pants with phone-sized container slot), or equipped bag. **Exception:** the livestream app (§9.1.1) continues to operate while the phone is placed in the world, so the player can keep streaming after setting the phone down for a back-camera shot.
### 9.1 Camera
- **Front camera** — held in hand, framed selfie shot.
@@ -340,56 +379,56 @@ The phone is the primary access point for the forum. See §13.
## 10. The City
### 10.1 Day / Night
| Phase | In-game time | Real time | Effects |
|---|---|---|---|
| Phase | In-game time | Real time | Effects |
|---|---|---|---------------------------------------------------------------------------------------------------|
| Day | 08:00 → 20:00 | ~45 min | High NPC density. Faster embarrassment gain. Most shops open. |
| Night | 20:00 → 08:00 | ~45 min | Low NPC density. Slower embarrassment gain. Police patrols spawn. Most shops closed. |
| Night | 20:00 → 08:00 | ~45 min | Low NPC density. Slower embarrassment gain. Most shops closed. |
Police patrols ignore the day/night cycle: when the player has the `wanted` tag (§7.7), patrols spawn in both phases (§10.3).
Sleeping at home fast-forwards 8 hours.
### 10.2 NPC types
| NPC | Behavior |
|---|---|
| **Walker** | Doesn't stop; reacts (animation/audio) but keeps walking. Low embarrassment contribution. |
| **Stalker** | Stops, stares. Sustained observation → faster embarrassment gain. |
| **Paparazzi** | Stops, takes photo. Photo → recognition increase. |
| **Snitch** | Stops, calls police. Triggers police spawn / wanted state if observed long enough. |
| **Harasser** | Walks toward player, attempts to grope. Player must evade. |
| **Helper** (rare) | Friendly NPC. Can remove restrictive clothing (cuffs) for free. Random spawn, not guaranteed to appear when needed. See §10.4. |
| **Police** | See §10.3. |
| NPC | Behavior |
|-------------------|---|
| **Walker** | Doesn't stop; reacts (animation/audio) but keeps walking. Low embarrassment contribution. |
| **Stalker** | Stops, stares. Sustained observation → faster embarrassment gain. |
| **Blogger** | Stops, takes photo. Photo → recognition increase. |
| **Snitch** | Stops, calls police. Triggers police spawn / wanted state if observed long enough. |
| **Harasser** | Walks toward player, attempts to grope. Player must evade. |
| **Police** | See §10.3. |
### 10.3 Police
- Spawn each night, patrol routes.
- Spawn during both day and night while the player has the `wanted` tag (§7.7). Patrol fixed routes.
- Detect player if face not hidden, or if revealing/no clothing.
- On detection: chase begins.
- If player breaks line of sight: police patrol last known position for a timer, then disengage.
- `wanted` persists across sessions until cleared.
- **Wanted poster mechanic:** wanted posters spawn in the city. Tearing them all down stops police spawning until further story triggers.
- **Wanted poster mechanic:** wanted posters spawn in the city. Tearing them all down clears the `wanted` tag and stops police spawning until a new Snitch report retriggers it.
### 10.4 Locations
- **Apartment** — safe zone. Wardrobe, bed, PC, kitchen, home gym (optional unlock).
- **Apartment** — safe zone. Wardrobe, bed, PC, kitchen.
- **Convenience store** — cooking ingredients.
- **Cafe** — pre-made food.
- **Clothing shops** — various types, each with a different inventory mix.
- **Gym** — increase max energy. Costs energy in the process. Possibly costs money.
- **Gym** — increase max energy. Costs energy in the process. Costs money.
- **Beauty salon** — boobs size, ass size, makeup, hair style, hair color.
- **Adult shop** — paid service to remove restrictive clothing (cuffs, etc.). Reliable but costs money and requires the player to travel there while restricted. Unlocked at a TBD Slave-path level (since cuffs are Slave-path content, the shop being a Slave-path service makes thematic and gating sense).
- **Adult shop** — Buy adult toys.
- **Streets / parks / alleys** — commission space.
- **(TBD)** beach, train station, school exterior, hot springs, etc. — design pass needed for variety.
#### 10.4.1 Cuff / restraint removal
Two paths for removing restrictive clothing:
1. **Adult shop** — reliable, costs money, fixed location. Player must travel there while restricted (which is the cost — both monetary and risk-of-exposure since hands are limited en route).
2. **Helper NPC** — rare random spawn, free. Cannot be relied upon. Creates emergent "lucky encounter" moments and a sandbox-style alternative.
This gives restrictive clothing a real tactical weight: equip cuffs near the adult shop with money in pocket = low risk; equip them far from home with no money = a real situation.
Restrictive clothing (§6.3.7) is removed by **Key + timed unlock action**:
- The player must possess the matching key for the restraint instance.
- Performing the unlock takes time; movement and all other actions are disabled for the duration.
- There is no Helper NPC, no adult-shop unlock service, and no break-out option. If the player loses the key, the restraint stays on until a key is acquired again.
---
## 11. PC (at home)
### 11.1 City News
- Random news plus articles about the player (posted by Paparazzi-driven events).
- Random news plus articles about the player (posted by Blogger-driven events).
- Player can **report** articles with their photo → reduces recognition.
### 11.2 Clothing shop (online)
@@ -402,13 +441,13 @@ This gives restrictive clothing a real tactical weight: equip cuffs near the adu
## 12. Shops & Services Summary
| Service | In-person | Online | Notes |
|---|---|---|---|
| Service | In-person | Online | Notes |
|---|---|----------|---|
| Clothing | Yes (multiple shop types) | Yes (PC) | Online has 1-day delay. |
| Food (ingredients) | Convenience store | Yes (PC) | Cooking required. |
| Food (pre-made) | Cafe | Yes (PC) | Eat immediately. |
| Gym | Yes (city) | Home gym (optional) | Increases max energy. |
| Beauty salon | Yes (city) | No | Body, hair, makeup customization. |
| Gym | Yes (city) | No | Increases max energy. |
| Beauty salon | Yes (city) | No | Body, hair, makeup customization. |
---
@@ -450,6 +489,7 @@ CommissionTemplate {
```
Typed objective steps (initial set):
- `ExposeBodyPart(part, durationSeconds)`
- `BeFullyNaked(durationSeconds)` — fully unclothed for the duration; no NPC requirement.
- `BeFullyNakedNearNPCs(count, durationSeconds)`
- `WalkNakedDistance(meters)`
- `MoveDistanceFromClothing(meters)`
@@ -458,11 +498,13 @@ Typed objective steps (initial set):
- `TakePhotoAtLocation(locationTag)`
- `DeliverItemTo(npcOrLocation)`
Steps compose: a single commission step list may chain primitives that must all be satisfied (concurrently or sequentially as specified by the template).
Example commissions from the brief that should be representable:
- "Get naked in front of 1 person" → `BeFullyNakedNearNPCs(1, ~3s)`.
- "Walk with breasts exposed for 10 seconds" → `ExposeBodyPart(boobs, 10)`.
- "Walk naked for 30 seconds" → `BeFullyNaked(30)` (composite step).
- "Move 50m away from your clothing while naked" → `BeFullyNaked` + `MoveDistanceFromClothing(50)`.
- "Walk naked for 30 seconds" → `BeFullyNaked(30)`.
- "Move 50m away from your clothing while naked" → `BeFullyNaked(durationSeconds)` + `MoveDistanceFromClothing(50)` (both must hold).
### 13.5 Subscriber math (rough)
```
@@ -480,7 +522,7 @@ Radial or hotbar accessible mid-session. Actions:
- Expose body part (per garment with `canExpose`).
- Open phone.
- Drop bag / pick up bag.
- Masturbate.
- Masturbate (Slut-path unlock, §5.1 — entry hidden until unlocked).
- Crouch / stand.
### 14.2 HUD (in-session)
@@ -509,15 +551,14 @@ Forum, bank, gallery, shops are all in-fiction screens (phone/PC). Avoid out-of-
- **Selling worn underwear** — two delivery methods:
- **Feetex shipping** — drop the item in a Feetex shipping box at the post office or convenience store. Payment arrives 1 in-game day later (mirrors Feetex's existing 1-day delivery delay).
- **Drop-off** — travel to a specified location (varies per order) and leave the underwear there. Immediate payment, but the location may be in a high-risk area depending on the order. Tactical trade-off: convenience vs. drop-off-style commission tension.
- Photo posts (indirect — followers → ongoing income? TBD).
- Photo posts (indirect) and accumulated followers — drive the weekly **passive follower income** (§7.9). Higher follower count → larger weekly payout and larger commission payouts.
### 15.2 Costs
- Weekly rent — **flat across the campaign**. No escalation, no event-driven spikes. The pressure comes from the 90-day timer and other expenses, not from a moving rent target.
- Clothing.
- Food / ingredients.
- Gym membership / beauty salon.
- Adult shop (cuff removal service).
- Phone repairs (if introduced).
- Adult shop.
### 15.3 Tuning targets (placeholder)
| Tier | Daily income | Weekly rent | Notes |
@@ -578,7 +619,7 @@ GAS is well-suited here: attributes, modifiers (clothing-based, recognition-base
- `GameplayCues` for visual/audio reactions.
### 17.3 Replication
Single-player. Multiplayer is **out of scope**. Code should still use server-authoritative-style patterns where it costs little (helps separation of concerns, mirrors HRPG codebase habits, makes future co-op less painful) but avoid premature networking complexity.
None
### 17.4 Data-driven content
Clothing, commissions, food, NPC templates all live as `DataAssets` (or DataTables for tabular content). Designers (eventually) should be able to add items without touching C++.
@@ -601,7 +642,7 @@ Clothing, commissions, food, NPC templates all live as `DataAssets` (or DataTabl
- 1520 clothing items spread across paths (5 Slut, 5 Exhibitionist, 5 Slave, 5 neutral).
- 3 bag variants.
- 8 food items (4 cooking ingredients, 4 pre-made).
- 5 NPC types (Walker, Stalker, Paparazzi, Snitch, Harasser) + Police + Helper.
- 5 NPC types (Walker, Stalker, Blogger, Snitch, Harasser) + Police.
- 1 functional city district with all shop types.
- 20 commission templates yielding ~5x procedural variation.
- Apartment + PC + phone fully functional.
@@ -641,7 +682,7 @@ Decisions previously open, now fixed:
6. **Selling worn underwear:** Feetex shipping (1-day, convenient) or specified drop-off location (immediate, risk-based). See §15.1.
7. **Rent:** Flat. No escalation.
8. **Run length:** 90-day campaign. Endless mode unlocked after first completion (rent-eviction disabled, all other systems intact). See §3.3.
9. **Cuff/restraint removal:** Adult shop (paid, reliable) + Helper NPC (rare random spawn, free). See §10.4.1.
9. **Cuff/restraint removal:** Key + timed unlock action only. No Helper NPC, no paid adult-shop service. See §10.4.1.
10. **Voice commands:** Not used. Hotkey-driven only.
## 21. Open Design Questions
@@ -649,15 +690,15 @@ Decisions previously open, now fixed:
These remain genuinely unresolved and should be addressed during implementation:
1. Final slot list for clothing equipment (top, bottom, underwear-top, underwear-bottom, outerwear, footwear, accessory, restraint — confirm and lock).
2. NPC theft timer for unattended clothing (X minutes, see §6.3.3).
3. Concrete tuning numbers for embarrassment / lust / energy / stamina rates.
2. NPC theft tuning: grace periods (`T_grace`, `T_grace_bag`), per-tick chance (`P_theft`), and check-tick interval — see §6.3.4.
3. Concrete tuning numbers for embarrassment / lust / energy / stamina / pulse rates.
4. Specific ending conditions for the 90-day campaign (path level + follower thresholds).
5. Manual save in apartment: enabled or auto-save only?
6. Photo system implementation: render-to-texture vs. screenshot-with-overlay (§17.6).
7. Crowd density: full pawns vs. instanced extras with awareness-radius promotion (§17.6).
8. Modular character system specifics — base mesh, layering scheme, attachment sockets.
9. Helper NPC spawn rate and conditions (city-wide chance per session? specific zones?).
10. Photo-post follower decay curve and per-photo cap.
9. Photo-post follower decay curve and per-photo cap.
10. Stamina ↔ energy multiplier curve specifics (linear vs. eased between 0% and 80% energy, §7.4).
---
@@ -668,10 +709,10 @@ These remain genuinely unresolved and should be addressed during implementation:
- **Mission** — weekly task, generated at week start.
- **Session** — the active outdoor play state between leaving and returning to the apartment.
- **Path** — Slut / Exhibitionist / Slave progression track.
- **Pulse** — heart-rate / arousal-rate attribute. Higher pulse multiplies embarrassment and lust gain. Exhibitionist-path attribute pool. See §7.5.
- **Recognition** — how known the player is in the city. Multiplies embarrassment gain.
- **Wanted** — binary police-target flag.
- **Helper** — rare friendly NPC who can remove restrictive clothing for free.
- **Feetex** — in-fiction package tracking and shipping service.
- **Wanted** — binary police-target flag. Set by Snitch reports; cleared by tearing down posters or by capture. See §7.7.
- **Feetex** — in-fiction package tracking and shipping service. Handles both incoming deliveries (1-day delay) and outgoing worn-underwear shipping.
- **Endless mode** — post-campaign unlock; rent eviction disabled, all other systems active.
---
+15
View File
@@ -0,0 +1,15 @@
// © 2025 Naked People Team. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "BodyPart.generated.h"
UENUM(BlueprintType)
enum class EBodyPart : uint8
{
None = 0,
Boobs = 1,
Ass = 2,
Genitals = 3,
};
+41 -4
View File
@@ -3,14 +3,33 @@
#pragma once
#include "CoreMinimal.h"
#include "BodyPart.h"
#include "ClothingSlotType.h"
#include "Engine/DataAsset.h"
#include "NakedDesire/Player/PrivateBodyPartType.h"
#include "NakedDesire/Progression/ProgressionPath.h"
#include "ClothingItem.generated.h"
/**
*
*/
UENUM(BlueprintType)
enum class EGarmentContainerSlotType : uint8
{
S UMETA(DisplayName = "Small"),
M UMETA(DisplayName = "Medium"),
L UMETA(DisplayName = "Large"),
};
USTRUCT(BlueprintType)
struct NAKEDDESIRE_API FGarmentContainerSlot
{
GENERATED_BODY()
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly)
EGarmentContainerSlotType SlotType = EGarmentContainerSlotType::S;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, meta = (ClampMin = 1))
int32 Count = 1;
};
UCLASS(BlueprintType)
class NAKEDDESIRE_API UClothingItem : public UPrimaryDataAsset
{
@@ -39,11 +58,29 @@ public:
int BasePrice;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly)
TArray<EPrivateBodyPartType> CoveredBodyParts;
TArray<EPrivateBodyPartType> CoveredBodyParts; // TODO: Add coverage per body part
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, meta = (EditCondition = "SlotType == EClothingSlotType::Shoes", Category = "Shoes"))
float ShoesOffset = 0.0f;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly)
bool UseLeaderPose = false;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly)
EProgressionPath ProgressionPath;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly)
TArray<EBodyPart> CanExpose;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly)
bool IsRestrictive;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly)
float Coverage = 1.0f;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly)
bool IsUnderwear;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly)
TArray<FGarmentContainerSlot> ContainerSlots;
};
@@ -1,25 +0,0 @@
// © 2025 Naked People Team. All Rights Reserved.
#include "ClothingItemData.h"
#include "../SaveGame/ClothingItemSaveData.h"
FClothingItemSaveData UClothingItemData::ToSaveData() const
{
FClothingItemSaveData SaveData;
SaveData.ClothingItem = Info;
return SaveData;
}
UClothingItemData* UClothingItemData::CreateFromSaveData(const FClothingItemSaveData& SaveData)
{
UClothingItem* ClothingItem = SaveData.ClothingItem.LoadSynchronous();
UClothingItemData* ClothingItemData = NewObject<UClothingItemData>();
ClothingItemData->Info = ClothingItem;
return ClothingItemData;
}
@@ -1,27 +0,0 @@
// © 2025 Naked People Team. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "ClothingItem.h"
#include "ClothingItemData.generated.h"
struct FClothingItemSaveData;
/**
*
*/
UCLASS(EditInlineNew, BlueprintType)
class NAKEDDESIRE_API UClothingItemData : public UObject
{
GENERATED_BODY()
public:
UPROPERTY(BlueprintReadOnly, EditDefaultsOnly)
UClothingItem* Info = nullptr;
UFUNCTION(BlueprintCallable)
FClothingItemSaveData ToSaveData() const;
UFUNCTION(BlueprintCallable)
static UClothingItemData* CreateFromSaveData(const FClothingItemSaveData& SaveData);
};
@@ -0,0 +1 @@
#include "ClothingItemInstance.h"
@@ -0,0 +1,26 @@
#pragma once
#include "CoreMinimal.h"
#include "NakedDesire/Items/ItemInstance.h"
#include "ClothingItemInstance.generated.h"
class UClothingItem;
UCLASS(BlueprintType)
class NAKEDDESIRE_API UClothingItemInstance : public UItemInstance
{
GENERATED_BODY()
public:
UPROPERTY(BlueprintReadOnly, Category = "Clothing Item")
float Condition = 1.0f;
UClothingItem* GetClothingItem() const { return ClothingItem; }
protected:
UPROPERTY(BlueprintReadOnly, Category = "Clothing Item")
TObjectPtr<UClothingItem> ClothingItem;
UPROPERTY(BlueprintReadOnly, Category = "Clothing Item")
TArray<TObjectPtr<UItemInstance>> StoredItems;
};
@@ -1,5 +0,0 @@
// © 2025 Naked People Team. All Rights Reserved.
#include "ClothingList.h"
@@ -1,22 +0,0 @@
// © 2025 Naked People Team. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "Engine/DataAsset.h"
#include "ClothingList.generated.h"
class UClothingItemData;
/**
*
*/
UCLASS()
class NAKEDDESIRE_API UClothingList : public UPrimaryDataAsset
{
GENERATED_BODY()
public:
UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Instanced)
TArray<UClothingItemData*> ClothingItems;
};
+41 -50
View File
@@ -2,9 +2,9 @@
#include "ClothingManager.h"
#include "ClothingItemData.h"
#include "ClothingItem.h"
#include "ClothingItemInstance.h"
#include "GameFramework/Character.h"
#include "NakedDesire/SaveGame/GlobalSaveGameData.h"
UClothingManager::UClothingManager()
{
@@ -15,7 +15,7 @@ bool UClothingManager::IsBodyTypeExposed(const EPrivateBodyPartType PrivateBodyP
{
for (const auto& ClothingSlot : ClothingSlots)
{
if (ClothingSlot.ClothingData && ClothingSlot.ClothingData->Info->CoveredBodyParts.Contains(PrivateBodyPartType))
if (ClothingSlot.ClothingItemInstance && ClothingSlot.ClothingItemInstance->GetClothingItem()->CoveredBodyParts.Contains(PrivateBodyPartType))
{
return false;
}
@@ -37,26 +37,26 @@ bool UClothingManager::GetClothingSlotData(const EClothingSlotType ClothingSlotT
return false;
}
void UClothingManager::SetClothingSlotItem(const EClothingSlotType ClothingSlotType, UClothingItemData* ClothingItem)
void UClothingManager::SetClothingSlotItem(const EClothingSlotType ClothingSlotType, UClothingItemInstance* ClothingItemInstance)
{
for (FClothingSlotData& ClothingSlot : ClothingSlots)
{
if (ClothingSlot.ClothingSlotType == ClothingSlotType)
{
ClothingSlot.ClothingData = ClothingItem;
ClothingSlot.ClothingItemInstance = ClothingItemInstance;
}
}
}
TArray<UClothingItemData*> UClothingManager::GetEquippedClothing()
TArray<UClothingItemInstance*> UClothingManager::GetEquippedClothing()
{
TArray<UClothingItemData*> EquippedClothingItems;
TArray<UClothingItemInstance*> EquippedClothingItems;
for (FClothingSlotData ClothingSlot : ClothingSlots)
{
if (ClothingSlot.ClothingData)
if (ClothingSlot.ClothingItemInstance)
{
EquippedClothingItems.Add(ClothingSlot.ClothingData);
EquippedClothingItems.Add(ClothingSlot.ClothingItemInstance);
}
}
@@ -65,110 +65,101 @@ TArray<UClothingItemData*> UClothingManager::GetEquippedClothing()
void UClothingManager::HydrateClothing(UGlobalSaveGameData* SaveGameData)
{
for (const FClothingItemSaveData& ClothingItemSaveData : SaveGameData->PlayerClothing)
{
UClothingItemData* ClothingItemData = UClothingItemData::CreateFromSaveData(ClothingItemSaveData);
PutOnClothing(ClothingItemData);
}
// for (const FClothingItemSaveData& ClothingItemSaveData : SaveGameData->PlayerClothing)
// {
// UClothingItemData* ClothingItemData = UClothingItemData::CreateFromSaveData(ClothingItemSaveData);
// PutOnClothing(ClothingItemData);
// }
}
float UClothingManager::GetHeelHeight()
{
if (FClothingSlotData ClothingSlotData; GetClothingSlotData(EClothingSlotType::Shoes, ClothingSlotData))
{
if (ClothingSlotData.ClothingData)
if (ClothingSlotData.ClothingItemInstance)
{
return ClothingSlotData.ClothingData->Info->ShoesOffset;
return ClothingSlotData.ClothingItemInstance->GetClothingItem()->ShoesOffset;
}
}
return 0;
}
void UClothingManager::PutOnClothing(UClothingItemData* ClothingData)
void UClothingManager::PutOnClothing(UClothingItemInstance* ClothingItemInstance)
{
if (!ClothingData)
{
if (!ClothingItemInstance)
return;
}
const EClothingSlotType ClothingSlotType = ClothingData->Info->SlotType;
const EClothingSlotType ClothingSlotType = ClothingItemInstance->GetClothingItem()->SlotType;
FClothingSlotData ClothingSlotData;
GetClothingSlotData(ClothingSlotType, ClothingSlotData);
ClothingSlotData.MeshComponent->SetSkeletalMesh(ClothingData->Info->SkeletalMesh);
if (!ClothingData->Info->Materials.IsEmpty())
ClothingSlotData.MeshComponent->SetSkeletalMesh(ClothingItemInstance->GetClothingItem()->SkeletalMesh);
if (!ClothingItemInstance->GetClothingItem()->Materials.IsEmpty())
{
for (const TPair<FName, UMaterialInstance*>& Material : ClothingData->Info->Materials)
for (const TPair<FName, UMaterialInstance*>& Material : ClothingItemInstance->GetClothingItem()->Materials)
{
ClothingSlotData.MeshComponent->SetMaterialByName(Material.Key, Material.Value);
}
}
SetClothingSlotItem(ClothingSlotType, ClothingData);
if (ClothingData->Info->UseLeaderPose)
SetClothingSlotItem(ClothingSlotType, ClothingItemInstance);
if (ClothingItemInstance->GetClothingItem()->UseLeaderPose)
{
ClothingSlotData.MeshComponent->SetLeaderPoseComponent(Cast<ACharacter>(GetOwner())->GetMesh());
}
OnClothingEquip.Broadcast(ClothingData);
OnClothingEquip.Broadcast(ClothingItemInstance);
}
void UClothingManager::TakeClothing(UClothingItemData* ClothingData)
void UClothingManager::TakeClothing(UClothingItemInstance* ClothingItemInstance)
{
if (!ClothingData->Info)
{
return;
}
FClothingSlotData ClothingSlotData;
GetClothingSlotData(ClothingData->Info->SlotType, ClothingSlotData);
GetClothingSlotData(ClothingItemInstance->GetClothingItem()->SlotType, ClothingSlotData);
if (ClothingSlotData.ClothingData->Info)
if (ClothingSlotData.ClothingItemInstance->GetClothingItem())
{
DropClothing(ClothingData->Info->SlotType);
DropClothing(ClothingItemInstance->GetClothingItem()->SlotType);
}
ClothingSlotData.ClothingData = ClothingData;
ClothingSlotData.ClothingItemInstance = ClothingItemInstance;
PutOnClothing(ClothingData);
PutOnClothing(ClothingItemInstance);
}
UClothingItemData* UClothingManager::RemoveClothing(const EClothingSlotType ClothingSlotType)
UClothingItemInstance* UClothingManager::RemoveClothing(const EClothingSlotType ClothingSlotType)
{
FClothingSlotData ClothingSlotData;
if (!GetClothingSlotData(ClothingSlotType, ClothingSlotData) || !ClothingSlotData.ClothingData)
if (!GetClothingSlotData(ClothingSlotType, ClothingSlotData) || !ClothingSlotData.ClothingItemInstance)
{
UE_LOG(LogTemp, Error, TEXT("Couldn't find clothing slot"));
return nullptr;
}
UClothingItemData* ClothingData = ClothingSlotData.ClothingData;
UClothingItemInstance* ClothingItemInstance = ClothingSlotData.ClothingItemInstance;
SetClothingSlotItem(ClothingSlotType, nullptr);
USkeletalMeshComponent* MeshComponent = ClothingSlotData.MeshComponent;
MeshComponent->SetSkeletalMesh(nullptr);
if (ClothingData->Info->UseLeaderPose)
if (ClothingItemInstance->GetClothingItem()->UseLeaderPose)
{
ClothingSlotData.MeshComponent->SetLeaderPoseComponent(nullptr);
}
OnClothingUnequip.Broadcast(ClothingData);
OnClothingUnequip.Broadcast(ClothingItemInstance);
return ClothingData;
return ClothingItemInstance;
}
void UClothingManager::DropClothing(const EClothingSlotType ClothingType)
{
const UClothingItemData* ClothingData = RemoveClothing(ClothingType);
if (!ClothingData)
{
const UClothingItemInstance* ClothingItemInstance = RemoveClothing(ClothingType);
if (!ClothingItemInstance)
return;
}
OnClothingDropped.Broadcast(ClothingData);
OnClothingDropped.Broadcast(ClothingItemInstance);
}
bool UClothingManager::IsClothingTypeOn(const EClothingSlotType ClothingType)
@@ -176,7 +167,7 @@ bool UClothingManager::IsClothingTypeOn(const EClothingSlotType ClothingType)
FClothingSlotData ClothingSlotData;
GetClothingSlotData(ClothingType, ClothingSlotData);
const bool IsTypeOn = ClothingSlotData.ClothingData != nullptr;
const bool IsTypeOn = ClothingSlotData.ClothingItemInstance != nullptr;
return IsTypeOn;
}
@@ -12,9 +12,9 @@
class UGlobalSaveGameData;
class AClothingPickup;
class UClothingItemData;
class UClothingItemInstance;
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnClothingChangeSignature, const UClothingItemData*, ClothingData);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnClothingChangeSignature, const UClothingItemInstance*, ClothingItemInstance);
UCLASS( ClassGroup=(Custom), meta=(BlueprintSpawnableComponent) )
class NAKEDDESIRE_API UClothingManager : public UActorComponent
@@ -29,9 +29,7 @@ public:
UPROPERTY(BlueprintReadWrite, Category = "Clothing Manager|Clothing")
TArray<FClothingSlotData> ClothingSlots;
UPROPERTY(BlueprintAssignable)
FOnClothingChangeSignature OnClothingEquip;
@@ -43,7 +41,7 @@ public:
UFUNCTION(BlueprintCallable)
void PutOnClothing(UClothingItemData* ClothingData);
void PutOnClothing(UClothingItemInstance* ClothingItemInstance);
UFUNCTION(BlueprintCallable)
void DropClothing(const EClothingSlotType ClothingType);
@@ -58,19 +56,19 @@ public:
bool IsBodyTypeExposed(EPrivateBodyPartType PrivateBodyPartType);
UFUNCTION(BlueprintCallable)
void TakeClothing(UClothingItemData* ClothingData);
void TakeClothing(UClothingItemInstance* ClothingItemInstance);
UFUNCTION(BlueprintCallable)
UClothingItemData* RemoveClothing(EClothingSlotType ClothingSlotType);
UClothingItemInstance* RemoveClothing(EClothingSlotType ClothingSlotType);
UFUNCTION(BlueprintCallable)
bool GetClothingSlotData(EClothingSlotType ClothingSlotType, FClothingSlotData& OutClothingSlotData);
UFUNCTION(BlueprintCallable)
void SetClothingSlotItem(const EClothingSlotType ClothingSlotType, UClothingItemData* ClothingItem);
void SetClothingSlotItem(const EClothingSlotType ClothingSlotType, UClothingItemInstance* ClothingItemInstance);
UFUNCTION(BlueprintCallable)
TArray<UClothingItemData*> GetEquippedClothing();
TArray<UClothingItemInstance*> GetEquippedClothing();
void HydrateClothing(UGlobalSaveGameData* SaveGameData);
@@ -6,8 +6,8 @@
#include "ClothingSlotType.h"
#include "ClothingSlotData.generated.h"
class UClothingItemInstance;
enum class EClothingSlotType : uint8;
class UClothingItemData;
/**
*
@@ -18,26 +18,26 @@ struct NAKEDDESIRE_API FClothingSlotData
GENERATED_BODY()
FClothingSlotData()
: MeshComponent(nullptr), ClothingSlotType(EClothingSlotType::Anal), ClothingData(nullptr), Name(FText::GetEmpty())
: MeshComponent(nullptr), ClothingSlotType(EClothingSlotType::Anal), ClothingItemInstance(nullptr), Name(FText::GetEmpty())
{
}
FClothingSlotData(USkeletalMeshComponent* MeshComponent, const EClothingSlotType ClothingSlotType, UClothingItemData* ClothingData, const FText& Name)
FClothingSlotData(USkeletalMeshComponent* MeshComponent, const EClothingSlotType ClothingSlotType, UClothingItemInstance* ClothingItemInstance, const FText& Name)
{
this->MeshComponent = MeshComponent;
this->ClothingSlotType = ClothingSlotType;
this->ClothingData = ClothingData;
this->ClothingItemInstance = ClothingItemInstance;
this->Name = Name;
}
UPROPERTY(BlueprintReadWrite, Category = "Clothing")
USkeletalMeshComponent* MeshComponent = nullptr;
TObjectPtr<USkeletalMeshComponent> MeshComponent = nullptr;
UPROPERTY(BlueprintReadWrite, Category = "Clothing")
EClothingSlotType ClothingSlotType = EClothingSlotType::Anal;
UPROPERTY(BlueprintReadWrite, Category = "Clothing")
UClothingItemData* ClothingData = nullptr;
TObjectPtr<UClothingItemInstance> ClothingItemInstance = nullptr;
UPROPERTY(BlueprintReadWrite, Category = "Clothing")
FText Name = FText::GetEmpty();
@@ -4,9 +4,6 @@
#include "CoreMinimal.h"
/**
*
*/
UENUM(BlueprintType)
enum class EClothingSlotType : uint8
{
@@ -1,14 +0,0 @@
// © 2025 Naked People Team. All Rights Reserved.
#pragma once
#include "ClothingSlotWidgetData.generated.h"
USTRUCT(BlueprintType)
struct NAKEDDESIRE_API FClothingSlotWidgetData
{
GENERATED_BODY()
UPROPERTY(EditDefaultsOnly)
UTexture2D* PlaceholderIcon = nullptr;
};
@@ -3,14 +3,19 @@
#pragma once
#include "CoreMinimal.h"
#include "ClothingSlotWidgetData.h"
#include "ClothingSlotType.h"
#include "Engine/DataAsset.h"
#include "ClothingSlotWidgetsInfo.generated.h"
/**
*
*/
USTRUCT(BlueprintType)
struct NAKEDDESIRE_API FClothingSlotWidgetData
{
GENERATED_BODY()
UPROPERTY(EditDefaultsOnly)
UTexture2D* PlaceholderIcon = nullptr;
};
UCLASS()
class NAKEDDESIRE_API UClothingSlotInfo : public UPrimaryDataAsset
{
+1 -1
View File
@@ -2,7 +2,7 @@
#pragma once
#define SLOT_NAME "Slot2"
inline const FString DefaultSaveSlotName = TEXT("Slot1");
#define SLOT_PLAYER 0
#define IS_DEMO false
#define STARTING_MONEY 1000
@@ -2,7 +2,8 @@
#include "NakedDesireGameMode.h"
#include "Kismet/GameplayStatics.h"
#include "NakedDesire/Clothing/ClothingItemData.h"
#include "NakedDesire/Clothing/ClothingItem.h"
#include "NakedDesire/Clothing/ClothingItemInstance.h"
#include "UObject/ConstructorHelpers.h"
#include "NakedDesire/Interactables/Wardrobe.h"
#include "NakedDesire/MissionBuilder/MissionsConfig.h"
@@ -19,7 +20,7 @@ AWardrobe* ANakedDesireGameMode::GetWardrobe() const
return Wardrobe;
}
void ANakedDesireGameMode::BuyItem(UClothingItemData* ClothingItem)
void ANakedDesireGameMode::BuyItem(UClothingItemInstance* ClothingItemInstance)
{
ANakedDesireCharacter* Player = Cast<ANakedDesireCharacter>(UGameplayStatics::GetPlayerCharacter(GetWorld(), 0));
if (!Player)
@@ -27,13 +28,13 @@ void ANakedDesireGameMode::BuyItem(UClothingItemData* ClothingItem)
return;
}
if (Player->Money < ClothingItem->Info->BasePrice)
if (Player->Money < ClothingItemInstance->GetClothingItem()->BasePrice)
{
return;
}
Player->Money -= ClothingItem->Info->BasePrice;
Wardrobe->ClothingItems.Add(ClothingItem);
Player->Money -= ClothingItemInstance->GetClothingItem()->BasePrice;
Wardrobe->ClothingItems.Add(ClothingItemInstance);
}
void ANakedDesireGameMode::OnHourChanged(int32 Hour)
@@ -6,9 +6,9 @@
#include "GameFramework/GameModeBase.h"
#include "NakedDesireGameMode.generated.h"
class UClothingItemInstance;
class UMissionsConfig;
class AWardrobe;
class UClothingItemData;
UCLASS(minimalapi)
class ANakedDesireGameMode : public AGameModeBase
@@ -25,8 +25,6 @@ class ANakedDesireGameMode : public AGameModeBase
public:
int NoticeCount = 0;
void RestartGame();
@@ -46,7 +44,7 @@ public:
void EndGameEmbarrassed();
UFUNCTION(BlueprintCallable)
void BuyItem(UClothingItemData* ClothingItem);
void BuyItem(UClothingItemInstance* ClothingItemInstance);
UFUNCTION(BlueprintCallable)
void OnHourChanged(int32 Hour);
@@ -8,9 +8,6 @@
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnSettingsChanged, class UNakedDesireUserSettings*, Settings);
/**
*
*/
UCLASS()
class NAKEDDESIRE_API UNakedDesireUserSettings : public UGameUserSettings
{
+2 -2
View File
@@ -7,7 +7,7 @@
#include "GameFramework/Actor.h"
#include "Wardrobe.generated.h"
class UClothingItemData;
class UClothingItemInstance;
UCLASS(Blueprintable)
class NAKEDDESIRE_API AWardrobe : public AInteractableBase
@@ -16,5 +16,5 @@ class NAKEDDESIRE_API AWardrobe : public AInteractableBase
public:
UPROPERTY(EditAnywhere, BlueprintReadWrite, Instanced)
TArray<UClothingItemData*> ClothingItems;
TArray<TObjectPtr<UClothingItemInstance>> ClothingItems;
};
+17
View File
@@ -0,0 +1,17 @@
#include "ItemInstance.h"
void UItemInstance::PostInitProperties()
{
Super::PostInitProperties();
if (HasAnyFlags(RF_ClassDefaultObject | RF_ArchetypeObject | RF_NeedLoad))
return;
if (!InstanceID.IsValid())
InstanceID = FGuid::NewGuid();
}
void UItemInstance::PostDuplicate(EDuplicateMode::Type DuplicateMode)
{
Super::PostDuplicate(DuplicateMode);
InstanceID = FGuid::NewGuid();
}
+22
View File
@@ -0,0 +1,22 @@
#pragma once
#include "CoreMinimal.h"
#include "UObject/Object.h"
#include "ItemInstance.generated.h"
UCLASS(Abstract)
class NAKEDDESIRE_API UItemInstance : public UObject
{
GENERATED_BODY()
public:
virtual void PostInitProperties() override;
virtual void PostDuplicate(EDuplicateMode::Type DuplicateMode) override;
UFUNCTION(BlueprintPure, Category = "Item")
FGuid GetInstanceID() const { return InstanceID; }
private:
UPROPERTY(VisibleAnywhere, SaveGame, BlueprintReadOnly, Category = "Item", meta = (AllowPrivateAccess = "true"))
FGuid InstanceID;
};
@@ -2,9 +2,8 @@
#include "EquipClothingRestriction.h"
#include "NakedDesire/Clothing/ClothingItem.h"
#include "NakedDesire/Clothing/ClothingItemData.h"
#include "NakedDesire/Clothing/ClothingItemInstance.h"
#include "NakedDesire/Clothing/ClothingManager.h"
#include "NakedDesire/Player/NakedDesireCharacter.h"
@@ -64,11 +63,11 @@ FText UEquipClothingRestriction::GetDescription() const
});
}
void UEquipClothingRestriction::OnClothingEquipped(const UClothingItemData* ClothingData)
void UEquipClothingRestriction::OnClothingEquipped(const UClothingItemInstance* ClothingItemInstance)
{
const bool IsTargetClothing = ClothingItems.FindByPredicate([&ClothingData](const UClothingItem* Item)
const bool IsTargetClothing = ClothingItems.FindByPredicate([&ClothingItemInstance](const UClothingItem* Item)
{
return Item && Item->Name.EqualTo(ClothingData->Info->Name);
return Item && Item->Name.EqualTo(ClothingItemInstance->GetClothingItem()->Name);
}) != nullptr;
if (IsTargetClothing)
{
@@ -76,11 +75,11 @@ void UEquipClothingRestriction::OnClothingEquipped(const UClothingItemData* Clot
}
}
void UEquipClothingRestriction::OnClothingUnequipped(const UClothingItemData* ClothingData)
void UEquipClothingRestriction::OnClothingUnequipped(const UClothingItemInstance* ClothingItemInstance)
{
const bool IsTargetClothing = ClothingItems.FindByPredicate([&ClothingData](const UClothingItem* Item)
const bool IsTargetClothing = ClothingItems.FindByPredicate([&ClothingItemInstance](const UClothingItem* Item)
{
return Item && Item->Name.EqualTo(ClothingData->Info->Name);
return Item && Item->Name.EqualTo(ClothingItemInstance->GetClothingItem()->Name);
}) != nullptr;
if (IsTargetClothing)
{
@@ -92,14 +91,14 @@ void UEquipClothingRestriction::CheckClothing()
{
for (const FClothingSlotData& ClothingSlot : Player->ClothingManager->ClothingSlots)
{
if (!ClothingSlot.ClothingData)
if (!ClothingSlot.ClothingItemInstance)
{
continue;
}
const bool IsTargetClothing = ClothingItems.FindByPredicate([&ClothingSlot](const UClothingItem* Item)
{
return Item && Item->Name.EqualTo(ClothingSlot.ClothingData->Info->Name);
return Item && Item->Name.EqualTo(ClothingSlot.ClothingItemInstance->GetClothingItem()->Name);
}) != nullptr;
if (IsTargetClothing)
{
@@ -6,11 +6,9 @@
#include "NakedDesire/MissionBuilder/GoalRestriction.h"
#include "EquipClothingRestriction.generated.h"
class UClothingItemInstance;
class UClothingItem;
class UClothingItemData;
/**
*
*/
UCLASS(EditInlineNew)
class NAKEDDESIRE_API UEquipClothingRestriction : public UGoalRestriction
{
@@ -30,10 +28,10 @@ private:
FDelegateHandle ClothingUnequippedDelegateHandle;
UFUNCTION()
void OnClothingEquipped(const UClothingItemData* ClothingData);
void OnClothingEquipped(const UClothingItemInstance* ClothingItemInstance);
UFUNCTION()
void OnClothingUnequipped(const UClothingItemData* ClothingData);
void OnClothingUnequipped(const UClothingItemInstance* ClothingItemInstance);
void CheckClothing();
};
@@ -3,7 +3,8 @@
#include "ExposeBodyPartRestriction.h"
#include "NakedDesire/Clothing/ClothingItemData.h"
#include "NakedDesire/Clothing/ClothingItem.h"
#include "NakedDesire/Clothing/ClothingItemInstance.h"
#include "NakedDesire/Player/NakedDesireCharacter.h"
#include "NakedDesire/Clothing/ClothingManager.h"
@@ -40,20 +41,18 @@ FText UExposeBodyPartRestriction::GetDescription() const
});
}
void UExposeBodyPartRestriction::EquipClothing(const UClothingItemData* ClothingData)
void UExposeBodyPartRestriction::EquipClothing(const UClothingItemInstance* ClothingItemInstance)
{
if (IsSuccess && ClothingData->Info->CoveredBodyParts.Contains(BodyPart))
if (IsSuccess && ClothingItemInstance->GetClothingItem()->CoveredBodyParts.Contains(BodyPart))
{
Init(Player);
}
}
void UExposeBodyPartRestriction::UnequipClothing(const UClothingItemData* ClothingData)
void UExposeBodyPartRestriction::UnequipClothing(const UClothingItemInstance* ClothingItemInstance)
{
if (IsSuccess)
{
return;
}
CheckClothing();
}
@@ -67,9 +66,9 @@ void UExposeBodyPartRestriction::CheckClothing()
const FClothingSlotData* TargetClothingItem = Player->ClothingManager->ClothingSlots.FindByPredicate([this](const FClothingSlotData& ClothingSlot)
{
if (ClothingSlot.ClothingData)
if (ClothingSlot.ClothingItemInstance)
{
return ClothingSlot.ClothingData->Info->CoveredBodyParts.Contains(BodyPart);
return ClothingSlot.ClothingItemInstance->GetClothingItem()->CoveredBodyParts.Contains(BodyPart);
}
return false;
@@ -7,10 +7,8 @@
#include "NakedDesire/MissionBuilder/GoalRestriction.h"
#include "ExposeBodyPartRestriction.generated.h"
class UClothingItemData;
/**
*
*/
class UClothingItemInstance;
UCLASS(EditInlineNew)
class NAKEDDESIRE_API UExposeBodyPartRestriction : public UGoalRestriction
{
@@ -29,10 +27,10 @@ protected:
private:
UFUNCTION()
void EquipClothing(const UClothingItemData* ClothingData);
void EquipClothing(const UClothingItemInstance* ClothingItemInstance);
UFUNCTION()
void UnequipClothing(const UClothingItemData* ClothingData);
void UnequipClothing(const UClothingItemInstance* ClothingItemInstance);
void CheckClothing();
};
@@ -13,10 +13,10 @@
#include "EnhancedInputSubsystems.h"
#include "Kismet/GameplayStatics.h"
#include "Internationalization/Text.h"
#include "NakedDesire/Clothing/ClothingItemData.h"
#include "NakedDesire/Clothing/ClothingItem.h"
#include "NakedDesire/Clothing/ClothingItemInstance.h"
#include "NakedDesire/Global/Constants.h"
#include "NakedDesire/Global/NakedDesireUserSettings.h"
#include "NakedDesire/SaveGame/GlobalSaveGameData.h"
#include "Perception/AIPerceptionStimuliSourceComponent.h"
#include "Perception/AISense_Sight.h"
@@ -152,21 +152,12 @@ void ANakedDesireCharacter::BeginPlay()
StimuliSourceComponent->RegisterForSense(TSubclassOf<UAISense_Sight>());
StimuliSourceComponent->RegisterWithPerceptionSystem();
UGlobalSaveGameData* SaveGameData = UGlobalSaveGameData::LoadOrCreateSaveGame(DefaultPlayerClothing, DefaultWardrobeClothing);
if (SaveGameData)
{
Money = FMath::RoundToInt(SaveGameData->Money);
}
SetupClothingSlots();
ClothingManager->OnClothingEquip.AddUniqueDynamic(this, &ANakedDesireCharacter::OnClothingEquip);
ClothingManager->OnClothingUnequip.AddUniqueDynamic(this, &ANakedDesireCharacter::OnClothingUnequip);
UNakedDesireUserSettings::GetNakedDesireUserSettings()->OnSettingsChanged.AddUniqueDynamic(this, &ANakedDesireCharacter::OnSettingsChanged);
ClothingManager->HydrateClothing(SaveGameData);
}
UAISense_Sight::EVisibilityResult ANakedDesireCharacter::CanBeSeenFrom(const FCanBeSeenFromContext& Context,
@@ -257,29 +248,27 @@ void ANakedDesireCharacter::OnEndOverlap(UPrimitiveComponent* OverlappedComponen
}
}
void ANakedDesireCharacter::OnClothingEquip(const UClothingItemData* ClothingItemData)
void ANakedDesireCharacter::OnClothingEquip(const UClothingItemInstance* ClothingItemInstance)
{
if (ClothingItemData->Info->CoveredBodyParts.Contains(EPrivateBodyPartType::BackBottom))
if (ClothingItemInstance->GetClothingItem()->CoveredBodyParts.Contains(EPrivateBodyPartType::BackBottom))
{
BackBottomCensorship->SetVisibility(false);
}
if (ClothingItemData->Info->CoveredBodyParts.Contains(EPrivateBodyPartType::FrontBottom))
if (ClothingItemInstance->GetClothingItem()->CoveredBodyParts.Contains(EPrivateBodyPartType::FrontBottom))
{
FrontBottomCensorship->SetVisibility(false);
}
if (ClothingItemData->Info->CoveredBodyParts.Contains(EPrivateBodyPartType::FrontTop))
if (ClothingItemInstance->GetClothingItem()->CoveredBodyParts.Contains(EPrivateBodyPartType::FrontTop))
{
BoobLCensorship->SetVisibility(false);
BoobRCensorship->SetVisibility(false);
}
}
void ANakedDesireCharacter::OnClothingUnequip(const UClothingItemData* ClothingItemData)
void ANakedDesireCharacter::OnClothingUnequip(const UClothingItemInstance* ClothingItemInstance)
{
if (!UNakedDesireUserSettings::GetNakedDesireUserSettings()->GetIsCensorshipEnabled() && !IS_DEMO)
{
return;
}
if (ClothingManager->IsBodyTypeExposed(EPrivateBodyPartType::BackBottom))
{
@@ -13,7 +13,6 @@
#include "NakedDesireCharacter.generated.h"
class UAIPerceptionStimuliSourceComponent;
class UClothingItemData;
class UClothingList;
struct FClothingSlotData;
class UInteractionManager;
@@ -52,12 +51,6 @@ public:
UInputAction* CrouchAction;
// Clothing
UPROPERTY(EditDefaultsOnly, Category = "Clothing Manager|Clothing")
UClothingList* DefaultPlayerClothing = nullptr;
UPROPERTY(EditDefaultsOnly, Category = "Clothing Manager|Clothing")
UClothingList* DefaultWardrobeClothing = nullptr;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Clothing")
USkeletalMeshComponent* NipplesMeshComponent;
@@ -157,13 +150,12 @@ public:
UFUNCTION(BlueprintPure)
EStance GetStance() const;
protected:
virtual void Tick(float DeltaTime) override;
virtual void SetupPlayerInputComponent(UInputComponent* PlayerInputComponent) override;
virtual void NotifyControllerChanged() override;
virtual void BeginPlay() override;
virtual UAISense_Sight::EVisibilityResult CanBeSeenFrom(const FCanBeSeenFromContext& Context, FVector& OutSeenLocation, int32& OutNumberOfLoSChecksPerformed, int32& OutNumberOfAsyncLosCheckRequested, float& OutSightStrength, int32* UserData = nullptr, const FOnPendingVisibilityQueryProcessedDelegate* Delegate = nullptr) override;
private:
EGait Gait = EGait::Walk;
EStance Stance = EStance::Stand;
@@ -177,10 +169,10 @@ private:
int32 OtherBodyIndex);
UFUNCTION()
void OnClothingEquip(const UClothingItemData* ClothingItemData);
void OnClothingEquip(const UClothingItemInstance* ClothingItemInstance);
UFUNCTION()
void OnClothingUnequip(const UClothingItemData* ClothingItemData);
void OnClothingUnequip(const UClothingItemInstance* ClothingItemInstance);
UFUNCTION()
void OnSettingsChanged(UNakedDesireUserSettings* Settings);
+17 -20
View File
@@ -1,11 +1,8 @@
// Fill out your copyright notice in the Description page of Project Settings.
#include "PlayerCinematic.h"
#include "PlayerCinematic.h"
#include "NakedDesireCharacter.h"
#include "Kismet/GameplayStatics.h"
#include "NakedDesire/Clothing/ClothingItemData.h"
#include "NakedDesire/Clothing/ClothingItem.h"
#include "NakedDesire/Clothing/ClothingItemInstance.h"
#include "NakedDesire/Clothing/ClothingManager.h"
@@ -57,20 +54,20 @@ void APlayerCinematic::BeginPlay()
Player->ClothingManager->OnClothingEquip.AddUniqueDynamic(this, &APlayerCinematic::OnClothingEquip);
Player->ClothingManager->OnClothingUnequip.AddUniqueDynamic(this, &APlayerCinematic::OnClothingUnequip);
for (const UClothingItemData* ClothingItemData : Player->ClothingManager->GetEquippedClothing())
for (const UClothingItemInstance* ClothingItemInstance : Player->ClothingManager->GetEquippedClothing())
{
EquipClothing(ClothingItemData);
EquipClothing(ClothingItemInstance);
}
}
void APlayerCinematic::OnClothingEquip(const UClothingItemData* ClothingData)
void APlayerCinematic::OnClothingEquip(const UClothingItemInstance* ClothingItemInstance)
{
EquipClothing(ClothingData);
EquipClothing(ClothingItemInstance);
}
void APlayerCinematic::OnClothingUnequip(const UClothingItemData* ClothingData)
void APlayerCinematic::OnClothingUnequip(const UClothingItemInstance* ClothingItemInstance)
{
UnequipClothing(ClothingData);
UnequipClothing(ClothingItemInstance);
}
USkeletalMeshComponent* APlayerCinematic::GetMeshByType(const EClothingSlotType SlotType) const
@@ -110,27 +107,27 @@ USkeletalMeshComponent* APlayerCinematic::GetMeshByType(const EClothingSlotType
}
}
void APlayerCinematic::EquipClothing(const UClothingItemData* ClothingData)
void APlayerCinematic::EquipClothing(const UClothingItemInstance* ClothingItemInstance)
{
USkeletalMeshComponent* MeshComponent = GetMeshByType(ClothingData->Info->SlotType);
MeshComponent->SetSkeletalMesh(ClothingData->Info->SkeletalMesh);
if (ClothingData->Info->UseLeaderPose)
USkeletalMeshComponent* MeshComponent = GetMeshByType(ClothingItemInstance->GetClothingItem()->SlotType);
MeshComponent->SetSkeletalMesh(ClothingItemInstance->GetClothingItem()->SkeletalMesh);
if (ClothingItemInstance->GetClothingItem()->UseLeaderPose)
{
MeshComponent->SetLeaderPoseComponent(GetMesh());
}
if (!ClothingData->Info->Materials.IsEmpty())
if (!ClothingItemInstance->GetClothingItem()->Materials.IsEmpty())
{
for (const TPair<FName, UMaterialInstance*>& Material : ClothingData->Info->Materials)
for (const TPair<FName, UMaterialInstance*>& Material : ClothingItemInstance->GetClothingItem()->Materials)
{
MeshComponent->SetMaterialByName(Material.Key, Material.Value);
}
}
}
void APlayerCinematic::UnequipClothing(const UClothingItemData* ClothingData)
void APlayerCinematic::UnequipClothing(const UClothingItemInstance* ClothingItemInstance)
{
USkeletalMeshComponent* MeshComponent = GetMeshByType(ClothingData->Info->SlotType);
USkeletalMeshComponent* MeshComponent = GetMeshByType(ClothingItemInstance->GetClothingItem()->SlotType);
MeshComponent->SetSkeletalMesh(nullptr);
MeshComponent->SetLeaderPoseComponent(nullptr);
}
+6 -8
View File
@@ -1,13 +1,11 @@
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Character.h"
#include "NakedDesire/Clothing/ClothingSlotType.h"
#include "PlayerCinematic.generated.h"
class UClothingItemData;
class UClothingItemInstance;
class ANakedDesireCharacter;
UCLASS()
@@ -68,12 +66,12 @@ protected:
private:
UFUNCTION()
void OnClothingEquip(const UClothingItemData* ClothingData);
void OnClothingEquip(const UClothingItemInstance* ClothingItemInstance);
UFUNCTION()
void OnClothingUnequip(const UClothingItemData* ClothingData);
void OnClothingUnequip(const UClothingItemInstance* ClothingItemInstance);
USkeletalMeshComponent* GetMeshByType(const EClothingSlotType SlotType) const;
void EquipClothing(const UClothingItemData* ClothingData);
void UnequipClothing(const UClothingItemData* ClothingData);
void EquipClothing(const UClothingItemInstance* ClothingItemInstance);
void UnequipClothing(const UClothingItemInstance* ClothingItemInstance);
};
+17 -19
View File
@@ -1,10 +1,8 @@
// Fill out your copyright notice in the Description page of Project Settings.
#include "PlayerImpostor.h"
#include "PlayerImpostor.h"
#include "NakedDesireCharacter.h"
#include "Kismet/GameplayStatics.h"
#include "NakedDesire/Clothing/ClothingItem.h"
#include "NakedDesire/Clothing/ClothingItemInstance.h"
#include "NakedDesire/Clothing/ClothingManager.h"
@@ -48,14 +46,14 @@ APlayerImpostor::APlayerImpostor()
ShoesMeshComponent->SetupAttachment(GetMesh());
}
void APlayerImpostor::OnClothingEquip(const UClothingItemData* ClothingData)
void APlayerImpostor::OnClothingEquip(const UClothingItemInstance* ClothingItemInstance)
{
EquipClothing(ClothingData);
EquipClothing(ClothingItemInstance);
}
void APlayerImpostor::OnClothingUnequip(const UClothingItemData* ClothingData)
void APlayerImpostor::OnClothingUnequip(const UClothingItemInstance* ClothingItemInstance)
{
UnequipClothing(ClothingData);
UnequipClothing(ClothingItemInstance);
}
void APlayerImpostor::BeginPlay()
@@ -71,9 +69,9 @@ void APlayerImpostor::BeginPlay()
Player->ClothingManager->OnClothingEquip.AddUniqueDynamic(this, &APlayerImpostor::OnClothingEquip);
Player->ClothingManager->OnClothingUnequip.AddUniqueDynamic(this, &APlayerImpostor::OnClothingUnequip);
for (const UClothingItemData* ClothingItemData : Player->ClothingManager->GetEquippedClothing())
for (const UClothingItemInstance* ClothingItemInstance : Player->ClothingManager->GetEquippedClothing())
{
EquipClothing(ClothingItemData);
EquipClothing(ClothingItemInstance);
}
}
@@ -114,27 +112,27 @@ USkeletalMeshComponent* APlayerImpostor::GetMeshByType(const EClothingSlotType S
}
}
void APlayerImpostor::EquipClothing(const UClothingItemData* ClothingData)
void APlayerImpostor::EquipClothing(const UClothingItemInstance* ClothingItemInstance)
{
USkeletalMeshComponent* MeshComponent = GetMeshByType(ClothingData->Info->SlotType);
MeshComponent->SetSkeletalMesh(ClothingData->Info->SkeletalMesh);
if (ClothingData->Info->UseLeaderPose)
USkeletalMeshComponent* MeshComponent = GetMeshByType(ClothingItemInstance->GetClothingItem()->SlotType);
MeshComponent->SetSkeletalMesh(ClothingItemInstance->GetClothingItem()->SkeletalMesh);
if (ClothingItemInstance->GetClothingItem()->UseLeaderPose)
{
MeshComponent->SetLeaderPoseComponent(GetMesh());
}
if (!ClothingData->Info->Materials.IsEmpty())
if (!ClothingItemInstance->GetClothingItem()->Materials.IsEmpty())
{
for (const TPair<FName, UMaterialInstance*>& Material : ClothingData->Info->Materials)
for (const TPair<FName, UMaterialInstance*>& Material : ClothingItemInstance->GetClothingItem()->Materials)
{
MeshComponent->SetMaterialByName(Material.Key, Material.Value);
}
}
}
void APlayerImpostor::UnequipClothing(const UClothingItemData* ClothingData)
void APlayerImpostor::UnequipClothing(const UClothingItemInstance* ClothingItemInstance)
{
USkeletalMeshComponent* MeshComponent = GetMeshByType(ClothingData->Info->SlotType);
USkeletalMeshComponent* MeshComponent = GetMeshByType(ClothingItemInstance->GetClothingItem()->SlotType);
MeshComponent->SetSkeletalMesh(nullptr);
MeshComponent->SetLeaderPoseComponent(nullptr);
}
+6 -9
View File
@@ -1,13 +1,11 @@
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "NakedDesire/Clothing/ClothingItemData.h"
#include "NakedDesire/Clothing/ClothingSlotType.h"
#include "PlayerImpostor.generated.h"
class UClothingItemInstance;
class ANakedDesireCharacter;
UCLASS()
@@ -69,18 +67,17 @@ class NAKEDDESIRE_API APlayerImpostor : public APawn
public:
APlayerImpostor();
protected:
virtual void BeginPlay() override;
private:
UFUNCTION()
void OnClothingEquip(const UClothingItemData* ClothingData);
void OnClothingEquip(const UClothingItemInstance* ClothingItemInstance);
UFUNCTION()
void OnClothingUnequip(const UClothingItemData* ClothingData);
void OnClothingUnequip(const UClothingItemInstance* ClothingItemInstance);
USkeletalMeshComponent* GetMesh() const { return Mesh; };
USkeletalMeshComponent* GetMeshByType(const EClothingSlotType SlotType) const;
void EquipClothing(const UClothingItemData* ClothingData);
void UnequipClothing(const UClothingItemData* ClothingData);
void EquipClothing(const UClothingItemInstance* ClothingItemInstance);
void UnequipClothing(const UClothingItemInstance* ClothingItemInstance);
};
@@ -0,0 +1,15 @@
// © 2025 Naked People Team. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "ProgressionPath.generated.h"
UENUM(BlueprintType)
enum class EProgressionPath : uint8
{
None = 0,
Slut = 1,
Exhibitionist = 2,
Slave = 3,
};
@@ -1,20 +0,0 @@
// © 2025 Naked People Team. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "UObject/Object.h"
#include "ClothingItemSaveData.generated.h"
class UClothingItem;
/**
*
*/
USTRUCT(BlueprintType)
struct FClothingItemSaveData
{
GENERATED_BODY()
UPROPERTY()
TSoftObjectPtr<UClothingItem> ClothingItem;
};
@@ -2,67 +2,51 @@
#include "GlobalSaveGameData.h"
#include "NakedDesire/Global/Constants.h"
#include "Kismet/GameplayStatics.h"
#include "NakedDesire/Clothing/ClothingItemData.h"
#include "NakedDesire/Clothing/ClothingList.h"
UGlobalSaveGameData* UGlobalSaveGameData::CreateNewSaveGame(TArray<UClothingItemData*> CurrentWardrobeClothing, TArray<UClothingItemData*> CurrentPlayerClothing, float InMoney)
UGlobalSaveGameData* UGlobalSaveGameData::CreateNewSaveGame()
{
UGlobalSaveGameData* NewSave = Cast<UGlobalSaveGameData>(
UGameplayStatics::CreateSaveGameObject(UGlobalSaveGameData::StaticClass())
);
UGlobalSaveGameData* NewSave = Cast<UGlobalSaveGameData>(UGameplayStatics::CreateSaveGameObject(StaticClass()));
if (!NewSave)
{
return nullptr;
}
NewSave->Money = InMoney;
for (const UClothingItemData* Item : CurrentWardrobeClothing)
{
NewSave->WardrobeClothing.Add(Item->ToSaveData());
}
for (const UClothingItemData* Item : CurrentPlayerClothing)
{
NewSave->PlayerClothing.Add(Item->ToSaveData());
}
return NewSave;
}
UGlobalSaveGameData* UGlobalSaveGameData::LoadOrCreateSaveGame(UClothingList* DefaultPlayerClothing, UClothingList* DefaultWardrobeClothing)
UGlobalSaveGameData* UGlobalSaveGameData::LoadOrCreateSaveGame(const FString& SlotName)
{
if (UGameplayStatics::DoesSaveGameExist(SLOT_NAME, SLOT_PLAYER))
{
return Cast<UGlobalSaveGameData>(
UGameplayStatics::LoadGameFromSlot(SLOT_NAME, SLOT_PLAYER)
);
}
if (UGlobalSaveGameData* ExistingSave = LoadGame(SlotName))
return ExistingSave;
UGlobalSaveGameData* NewSave = nullptr;
if (DefaultWardrobeClothing && DefaultPlayerClothing)
{
NewSave = CreateNewSaveGame(DefaultWardrobeClothing->ClothingItems, DefaultPlayerClothing->ClothingItems, STARTING_MONEY);
}
UGlobalSaveGameData* NewSave = CreateNewSaveGame();
if (NewSave)
{
UGameplayStatics::SaveGameToSlot(NewSave, SLOT_NAME, SLOT_PLAYER);
UGameplayStatics::SaveGameToSlot(NewSave, SlotName, SLOT_PLAYER);
}
return NewSave;
}
bool UGlobalSaveGameData::SaveGame(const TArray<UClothingItemData*> CurrentWardrobeClothing,
const TArray<UClothingItemData*> CurrentPlayerClothing, int32 InMoney)
UGlobalSaveGameData* UGlobalSaveGameData::LoadGame(const FString& SlotName)
{
if (UGlobalSaveGameData* Save = CreateNewSaveGame(CurrentWardrobeClothing, CurrentPlayerClothing, (float)InMoney))
if (UGameplayStatics::DoesSaveGameExist(SlotName, SLOT_PLAYER))
{
return UGameplayStatics::SaveGameToSlot(Save, SLOT_NAME, SLOT_PLAYER);
return Cast<UGlobalSaveGameData>(UGameplayStatics::LoadGameFromSlot(SlotName, SLOT_PLAYER));
}
return nullptr;
}
bool UGlobalSaveGameData::SaveGame(const FString& SlotName)
{
if (UGlobalSaveGameData* Save = CreateNewSaveGame())
{
return UGameplayStatics::SaveGameToSlot(Save, SlotName, SLOT_PLAYER);
}
return false;
@@ -3,39 +3,41 @@
#pragma once
#include "CoreMinimal.h"
#include "ClothingItemSaveData.h"
#include "GameFramework/SaveGame.h"
#include "NakedDesire/Global/Constants.h"
#include "GlobalSaveGameData.generated.h"
struct FItemSaveRecord;
class UClothingList;
class UClothingItemData;
/**
*
*/
UCLASS()
class NAKEDDESIRE_API UGlobalSaveGameData : public USaveGame
{
GENERATED_BODY()
public:
UPROPERTY(BlueprintReadWrite, SaveGame)
static UGlobalSaveGameData* LoadOrCreateSaveGame(const FString& SlotName = DefaultSaveSlotName);
static UGlobalSaveGameData* LoadGame(const FString& SlotName = DefaultSaveSlotName);
static bool SaveGame(const FString& SlotName = DefaultSaveSlotName);
UPROPERTY(SaveGame)
bool HaveSeenTutorial = false;
UPROPERTY(BlueprintReadWrite, SaveGame)
UPROPERTY(SaveGame)
float Money = 0;
UPROPERTY(BlueprintReadWrite)
TArray<FClothingItemSaveData> WardrobeClothing;
UPROPERTY(BlueprintReadWrite)
TArray<FClothingItemSaveData> PlayerClothing;
UFUNCTION(BlueprintCallable)
static UGlobalSaveGameData* LoadOrCreateSaveGame(UClothingList* DefaultPlayerClothing, UClothingList* DefaultWardrobeClothing);
UFUNCTION(BlueprintCallable)
static bool SaveGame(TArray<UClothingItemData*> CurrentWardrobeClothing, TArray<UClothingItemData*> CurrentPlayerClothing, int32 InMoney);
UPROPERTY(SaveGame)
TArray<FItemSaveRecord> WardrobeItems;
UPROPERTY(SaveGame)
TArray<FItemSaveRecord> EquippedItems;
UPROPERTY(SaceGame)
int32 DaysPassed = 0;
UPROPERTY(SaveGame)
float HourOfDay = 0.0f;
private:
static UGlobalSaveGameData* CreateNewSaveGame(TArray<UClothingItemData*> CurrentWardrobeClothing, TArray<UClothingItemData*> CurrentPlayerClothing, float InMoney);
static UGlobalSaveGameData* CreateNewSaveGame();
};
@@ -0,0 +1,24 @@
#pragma once
#include "CoreMinimal.h"
#include "ItemSaveRecord.generated.h"
class UClothingItem;
USTRUCT()
struct NAKEDDESIRE_API FItemSaveRecord
{
GENERATED_BODY()
UPROPERTY(SaveGame)
FGuid InstanceId;
UPROPERTY(SaveGame)
TSoftObjectPtr<UClothingItem> Definition;
UPROPERTY(SaveGame)
float Condition = 1.0f;
UPROPERTY(SaveGame)
FGuid ParentId;
};
@@ -0,0 +1,37 @@
#include "SaveSubsystem.h"
#include "GlobalSaveGameData.h"
#include "ItemSaveRecord.h"
void USaveSubsystem::LoadGame(const FString& SlotName)
{
UGlobalSaveGameData* Save = UGlobalSaveGameData::LoadGame(SlotName);
}
bool USaveSubsystem::SaveGame(const FString& SlotName)
{
return UGlobalSaveGameData::SaveGame(SlotName);
}
void USaveSubsystem::AddItem(const FItemSaveRecord& Record)
{
Items.Add(Record);
}
void USaveSubsystem::RemoveItem(const FGuid& InstanceId)
{
FItemSaveRecord* ItemSaveRecord = Items.FindByPredicate([InstanceId](FItemSaveRecord ItemSaveRecord)
{
return ItemSaveRecord.InstanceId == InstanceId;
});
if (ItemSaveRecord)
{
Items.Remove(*ItemSaveRecord);
}
}
void USaveSubsystem::Initialize(FSubsystemCollectionBase& Collection)
{
Super::Initialize(Collection);
LoadGame();
}
@@ -0,0 +1,35 @@
#pragma once
#include "CoreMinimal.h"
#include "NakedDesire/Global/Constants.h"
#include "Subsystems/GameInstanceSubsystem.h"
#include "SaveSubsystem.generated.h"
class UGlobalSaveGameData;
struct FItemSaveRecord;
UCLASS()
class NAKEDDESIRE_API USaveSubsystem : public UGameInstanceSubsystem
{
GENERATED_BODY()
public:
void LoadGame(const FString& SlotName = DefaultSaveSlotName);
bool SaveGame(const FString& SlotName = DefaultSaveSlotName);
const TArray<FItemSaveRecord>& GetItems() const { return Items; }
void AddItem(const FItemSaveRecord& Record);
void RemoveItem(const FGuid& InstanceId);
virtual void Initialize(FSubsystemCollectionBase& Collection) override;
private:
UPROPERTY()
FString ActiveSlotName = DefaultSaveSlotName;
UPROPERTY()
TArray<FItemSaveRecord> Items;
UPROPERTY()
TObjectPtr<UGlobalSaveGameData> CurrentSave;
};