updated plan

This commit is contained in:
koritsa
2026-05-23 00:40:02 +03:00
parent e1b04f1310
commit 3f266e12d5
2 changed files with 146 additions and 82 deletions
+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 ### 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. - **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.
- **Equip / unequip / drop event** — `Clothing/ClothingManager.cpp:88-172` (slot-based, broadcasts `OnClothingEquip / Unequip / Dropped`). - **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`).
- **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. - **Progression path enum (GDD §5)** — `Progression/ProgressionPath.h` defines `EProgressionPath { None, Slut, Exhibitionist, Slave }`. Used on `UClothingItem::ProgressionPath`.
- **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. - **Body-part enum (GDD §6.3)** — `Clothing/BodyPart.h` defines `EBodyPart { None, Boobs, Ass, Genitals }`. Used on `UClothingItem::CanExpose`.
- **Mission framework** — `Mission``MissionGoal``GoalRestriction` composition (`MissionBuilder/`). Two goals (`FlashGoal`, `MinTimeGoal`), three restrictions (`EquipClothing`, `ExposeBodyPart`, `Location`). - **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). - **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). - **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) ### 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 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.
- **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`. - **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)** — `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++. - **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.1)** — `ClothingManager::IsBodyTypeExposed` (`ClothingManager.cpp:14-25`) is binary. No coverage float, no underwear halving, no max-of-covering-garments math. - **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 parts (§6.3)** — only `FrontTop / FrontBottom / BackBottom` exist (`Player/PrivateBodyPartType.h:14-20`). GDD references boobs, ass, genitals as distinct parts. - **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 (`BeFullyNakedNearNPCs`, `WalkNakedDistance`, `MoveDistanceFromClothing`, `BeObservedByNPCType`, `TakePhotoAtLocation`, `DeliverItemTo`). Missions are hand-authored in `MissionsConfig`. - **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** — affects spawn caps (`NPCSpawner.cpp:40-41`) but not embarrassment rate, NPC type weights, or police spawning. - **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 ### 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. - **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 (§5)** — no path enum on `UClothingItem`, no Path XP, no path levels, no path-gated content. - **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. - **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. - **Forum (§13)** — no `UCommissionTemplate`, no procedural generation, no weekly missions distinct from daily, no profile / followers, no posting photos.
- **Photo & livestream** — absent. - **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. - **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. - **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)** — clothing drops broadcast a delegate but no `AItemActor` exists. - **Item-world AActor (§6.1)** — no `AItemActor` / `AClothingPickup` base.
- **Bag inventory (§6.4)** — absent. - **Bag inventory (§6.4)** — absent.
- **Rip & tear, theft (§6.3.3-4)** — 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.
- **Expose action (§6.3.5)** — absent. - **Rip & tear (§6.3.5)** — absent. `UClothingItemInstance::Condition` is read-only in code; no decrement source.
- **Restrictive clothing (§6.3.6)** — absent. - **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. - **Adult shop, gym, beauty salon, cafe, convenience store** — none.
- **Food / consumables (§6.6)** — absent. - **Food / consumables (§6.7)** — absent.
- **Recognition, wanted state, news (§7.57.6, §11.1)** — absent. - **Recognition, wanted state, news (§7.6–§7.7, §11.1)** — absent.
- **Underwear selling (§15.1)** — absent. - **Underwear selling (§15.1)** — absent.
- **Endless mode flag (§3.3)** — 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 ### 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. - **`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.)
- **`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. - **`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.
- **`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. - **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.
- **`NakedDesireGameMode::RefreshDailyMissions`** indexes `MissionsConfig->DailyMissions[DaysPassed]` — crashes past the authored array and contradicts §13.4 procedural generation. - **`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.
- **`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. - **`USaveSubsystem::LoadGame` ignores the loaded object** — the static returns a populated `UGlobalSaveGameData`, the subsystem stores nothing and applies nothing.
- **Save constant `SLOT_NAME "Slot2"`** (`Global/Constants.h:5`) — magic single-slot save, no multi-save support. - **`UClothingList`** forward-declared in `GlobalSaveGameData.h:11` and `Wardrobe` / save records no longer use it. Dead reference; remove.
- **`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. - **`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 ## 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 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`. Most of the structural work is done. What remains is connecting the pieces so an item modified at runtime survives a save / load cycle.
- 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.
**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) ### Phase 2 — World item actors + drop / pickup (1 week)
- `AItemActor` base wrapping a `UItemInstance*`. `AClothingPickup : AItemActor`. - `AItemActor` base wrapping a `UItemInstance*` (in `Items/`). Spawn transform, owning instance pointer, SaveGame-tagged.
- Hook `ClothingManager::DropClothing` to spawn `AClothingPickup` at the player location with the actual instance reference (no template copy). - `AClothingPickup : AItemActor` with a visual representation (use `UClothingItem::StaticMesh` when set, fallback to skeletal mesh impostor).
- Pickup interaction via existing `InteractionManager`. - 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) ### Phase 3 — Session system + loss resolver (1 week)
- `USessionManager` (GameMode component). Apartment `LocationTrigger` flag drives session start / end. - `USessionManager` (subsystem on `UNakedDesireGameInstance` or a `UWorldSubsystem`). Apartment `ALocationTrigger` flag drives session start / end. Emits `OnSessionStart` / `OnSessionEnd` with cause.
- `USessionLossResolver` — single class, per §4 implementation note. Takes the loss cause and runs §4.4 deterministically over the new world-item registry. - `USessionLossResolver` — single class, one method `ResolveLoss(ESessionLossCause Cause)`. Implements GDD §4.4:
- Wire `StatsManager::IncreaseEmbarrassment` max-hit and energy-zero into the resolver. - 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). - 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, Recognition, Reputation, Followers, Wanted. - 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).
- Move observation-driven embarrassment gain out of BP into the C++ pipeline (use the existing `CanBeSeenFrom` result + per-NPC type weights). - 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`.
- Add Lust + masturbate action + blackout vision effect. - Lust masturbate action (Slut-path gated, §7.2). Block the action in BP / quick-action when the player's Slut path level is 0.
- Add Recognition with face-cover bypass + Wanted bool. - 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) ### Phase 5 — Time + calendar + rent + sleep (34 days)
- `UTimeOfDaySubsystem` replacing the BP-implementable time on `NakedDesireGameMode`. 90-day calendar, week boundary, rent transaction. - `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: time-skip + energy restore + autosave. - 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.
- Endless-mode flag on the save. - 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) ### Phase 6 — NPC types + recognition pipeline (12 weeks)
- `ENPCType` enum, type-specific BT branches or per-type controller subclasses. - `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).
- Walker / Stalker / Paparazzi / Snitch / Harasser / Helper / Police. - 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.
- Recognition flow: Paparazzi photo → article → news site → "report" reduces recognition. - 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) ### Phase 7 — Commission template system (12 weeks)
- Replace `MissionsConfig`'s hand-authored daily list with `UCommissionTemplate` + procedural generation per §13.4. - 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`). - Add typed objective steps as new `UMissionGoal` subclasses:
- Weekly vs. daily generation pass. - `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. **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) ### Phase 8 — Phone + forum UI (23 weeks)
- Phone as a holdable / pocketable `AItemActor`. - Phone as an `AItemActor` (Phase 2 base) with hand / pocket / bag location semantics (§9).
- Apps: Camera + Gallery, Livestream (`StreamSession` tick object), Bank, Feetex, Maps, Health Tracker, Forum. - 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: pick render-to-texture (§17.6) — recommended for in-fiction photo posts. - 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) ### Phase 9 — Path progression + content authoring (2 weeks)
- Path XP per path, level gating on clothing / missions / attribute upgrades. - 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 ### Phase 10 — Remaining systems & polish
- Bag inventory (§6.4). - Bag inventory (§6.4) — uses Phase 2's `AItemActor` for world placement.
- Food / cooking (§6.6). - Food / cooking (§6.7) — three minigames per the GDD (slice, stir, cook).
- Gym / beauty salon / adult shop (§10.4). - Gym / beauty salon / adult shop / cafe / convenience store (§10.4).
- Rip and tear (§6.3.4). - Rip & tear (§6.3.5).
- Theft timer (§6.3.3). - 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.5). - Expose action (§6.3.6) — read `CanExpose` per garment, blocked by overlapping coverage.
- Restrictive-clothing flow (§6.3.6, §10.4.1). - Restrictive-clothing flow (§6.3.7 / §10.4.1) — Key + timed unlock action. Lock hand-dependent actions while restrained.
- Underwear selling (§15.1). - Underwear selling (§15.1) — Feetex drop-box or location drop-off variant.
- Wanted-poster takedown (§10.3). - 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. 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_