Files
Naked-Desire/PLAN.md
T
2026-06-03 15:16:20 +03:00

36 KiB
Raw Blame History

Development Plan

Working document for Naked Desire implementation. Tracks current state vs. the GDD (README.md) and the phased roadmap. Update this file as state changes — code is the source of truth, this file is the navigation chart.

When the GDD changes, update README.md first, then revisit this plan. When this plan changes, keep phase exit criteria concrete so we know when to move on.


1. Implementation status snapshot

State of the C++ module as of the latest pass. File references use Source/NakedDesire/... paths.

1.1 Implemented

  • 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 meshesPlayer/NakedDesireCharacter.cpp:40-67 creates 14 skeletal mesh components, one per current EClothingSlotType value. Aligns with GDD §17.6 in spirit; the specific slot list needs reshaping per §1.4 (locked 18-slot list now lives in GDD §6.5).
  • Equip / unequip / drop eventClothing/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 treeNPC/NPCAIController.cpp runs a BT with Player / TargetLocation / SpawnLocation blackboard keys. NPC/NPCSpawner.cpp proximity-gated spawn with day / night caps.
  • Mission frameworkMissionMissionGoalGoalRestriction 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 guardedNakedDesireGameMode::RefreshDailyMissions now clamps DaysPassed to the authored array bounds (NakedDesireGameMode.cpp:70-71). Still a hand-authored list (see §1.3).
  • Location triggersLocations/LocationTrigger, Locations/LocationData via gameplay tags (GDD §10.4 area foundation).
  • MovementEnhancedInput, walk / run / crouch (NakedDesireCharacter.cpp:115-127), stamina-gated run (Tick lines 91-113).
  • Wardrobe interactableInteractables/Wardrobe.h holds TArray<TObjectPtr<UClothingItemInstance>> ClothingItems; NakedDesireGameMode::BuyItem charges money and pushes into the wardrobe.

1.2 Partially implemented (deviates from GDD)

  • 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 (§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). Food-buff hookpoints (per-rate multipliers for stamina regen, embarrassment-gain resistance, lust-gain resistance — see GDD §6.7) are not in place; future Phase 10 work depends on the attribute simulation accepting external multipliers cleanly.
  • 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, no Accept lifecycle (§13.1 / §13.2), no path-filtering on the generator (§13.4).
  • Day / nightNPCSpawner.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 (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, embarrassment-max → apartment with no extras) is implemented.
  • Three progression paths runtime (§5) — enum exists; no XP pool, no per-path level derived from investment, no path-gated unlocks at runtime, no level-up flow. XP is a single shared pool, not per-path (GDD §5, §7.10).
  • Phone (§9) — entire system absent: camera, gallery, livestream, bank, Feetex, maps, health tracker. Includes the new sub-systems:
    • Battery (§9.8) — passive base + per-app multiplier drain; apartment charger; portable powerbank consumable (Convenience Store); hard shutdown at 0%; mid-livestream cutoff with earnings-to-date deposited; sleep always charges to 100%.
    • Livestream tip requests (§9.1.1) — viewer-driven action requests with phone popup (Accept / Decline + countdown); fail = viewer count drops, no rep hit.
    • Livestream follower trickle (§9.1.1)streamQualityScore ticks through FollowerGainCalculator per tick.
    • Bank app income breakdown (§9.4) — line items by source incl. the weekly follower auto-deposit at week boundary.
  • PhoneSubsystem (§17.1) — tickable subsystem owning phone state (battery %, active app, livestream session lifecycle, charger interaction). Does not yet exist.
  • Forum (§13) — no UCommissionTemplate, no procedural generation, no weekly missions distinct from daily, no profile (incl. weekly follower-income summary), no posting photos. Forum scope is locked minimal (board + own profile only, no threads / no other-users feed) — that's a non-build, but worth recording.
  • Photo & livestream — absent.
  • 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.
  • 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.
  • Bodysuit exclusion rule (§6.5) — when implementing the new slot enum, ClothingManager must auto-unequip Top/Bottom/UnderwearTop/UnderwearBottom on bodysuit equip, and vice versa.
  • Toy slots (§6.5)Nipples, Anal, Vagina are now toy slots (not body-clothing slots, not body-parts conflict). Independent; all three can be active. Items in these slots are sex toys from the Adult Shop (§10.4), follow standard item-identity rules, do not contribute to coverage, and may modify lust / embarrassment / pulse plus add audible vibration NPCs may detect.
  • Adult shop, gym, beauty salon, café, convenience store — none.
  • Food / consumables (§6.7) — absent. Vocabulary now locked: 2 instant effects (energy restore, lust decrease), 4 timed buffs (stamina regen +, max stamina +, embarrassment-gain resistance, lust-gain resistance). Stacking: different types parallel; same type additive up to per-type cap. Cooking minigames (slice / stir / cook) modulate buff strength; never poison.
  • Recognition, wanted state, news (§7.6–§7.7, §11.1) — absent. Face-cover bypass via Face and Eyes slots is part of this work.
  • Underwear selling (§15.1) — absent. Backed by the DeliverItemTo typed objective (player-owned source only, §13.4).
  • 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

  • SaveGame UPROPERTY typoGlobalSaveGameData.h:35 declares UPROPERTY(SaceGame) instead of SaveGame on DaysPassed. The field will not serialize.

  • USaveSubsystem::SaveGame writes an empty save — 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. Highest-priority correctness bug.

  • USaveSubsystem::LoadGame ignores the loaded object — static returns a populated UGlobalSaveGameData; subsystem stores / applies nothing.

  • EClothingSlotType — concrete reshape to the locked 18-slot list (GDD §6.5)

    Current enum (Clothing/ClothingSlotType.h, 14 values): Nipples, Anal, Vagina, Head, Neck, Face, Eyes, Body, Top, Bottom, Bra, Panties, Socks, Shoes.

    Target (18 slots, 4 groups): see GDD §6.5. Migration table:

    Current Target Action
    Top Top Keep
    Bottom Bottom Keep
    Bra UnderwearTop Rename
    Panties UnderwearBottom Rename
    Body Bodysuit Rename + enforce exclusion with Top/Bottom/UnderwearTop/UnderwearBottom
    Socks Socks Keep
    Shoes Footwear Rename
    Outerwear Add
    Head Head Keep
    Face Face Keep (now a face-cover slot per §7.6)
    Eyes Eyes Keep (now a face-cover slot per §7.6)
    Neck Neck Keep
    WristRestraint Add
    AnkleRestraint Add
    NeckRestraint Add (distinct from cosmetic Neck; auto-exclusion is not enforced — content authoring handles visual collision)
    Nipples Nipples Keep — now a toy slot, not a body-clothing slot
    Anal Anal Keep — now a toy slot
    Vagina Vagina Keep — now a toy slot

    Net: 14 → 18 (4 renames, 4 additions, 3 semantic repurposes). NakedDesireCharacter::SetupClothingSlots (NakedDesireCharacter.cpp:357-433) needs four new mesh components added and four existing components renamed in lockstep with BP references in Content/Blueprints/Player.

    Bodysuit exclusion is a runtime rule on ClothingManager::PutOnClothing — equipping a bodysuit must un-equip the four body slots; equipping any of those four must un-equip a present bodysuit.

    Toy slots are independent and stackable; their items are sex toys (not clothing) but use the same UItemInstance model. The UClothingItem data class is named for clothing — a USexToyItem (or similar) UItemInstance subclass may be cleaner than reusing UClothingItem for toys, since the toy effects (lust modifier, embarrassment modifier, pulse modifier, NPC-audible vibration) don't map onto clothing's coverage / CanExpose / IsRestrictive model.

  • Two competing body-part enumsEPrivateBodyPartType 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.

  • UClothingList forward-declared in GlobalSaveGameData.h:11; Wardrobe and save records no longer use it. Dead reference; remove.

  • Money representationANakedDesireCharacter::Money is int (NakedDesireCharacter.h:132); UGlobalSaveGameData::Money is float (GlobalSaveGameData.h:27). Pick one authoritative location and one type.

  • STARTING_MONEY macro defined in Constants.h:8 but unreferenced. Either wire it into save / character init or remove.

  • ClothingItemInstance has no public constructor / factoryClothingItem pointer is protected with no Init(UClothingItem*). Wardrobe / BuyItem flows rely on EditAnywhere Instanced design-time population, which 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. GDD §7.3 specifies per-activity modifiers.

  • NPCSpawner day window — uses Hours >= 9 && < 21 for "day" (NPCSpawner.cpp:40); GDD §10.1 says 08:0020:00. Off by one hour on each end.

  • GDD shifts not yet reflected in code:

    • Loss resolver must not strip equipped clothing (§4.4).
    • Energy = 0 must trigger a "cutscene → apartment → sleep" flow, not a teleport (§4.4).
    • Embarrassment = max must fade to apartment with no extra cost (no time skip, no money penalty, no rep hit) — GDD §4.4.
    • Theft is a chance-based model with grace periods and flat per-tick P_theft, not a ramping curve (§6.3.4).
    • Sleep is the deterministic side of clothing loss — guaranteed loss of every clothing item left outside the apartment (§4.4).
    • No Helper NPC; restraint removal is Key + timed unlock only (§10.4.1).
    • Masturbation is Slut-path gated — generator must filter PerformAction(masturbate) for non-Slut players; quick-action entry must hide until unlocked (§5.1 / §7.2 / §14.1).
    • Police spawn day and night while wanted (§7.7 / §10.3).
    • XP is a single shared pool, not per-path — Phase 9 implementation must use a shared XP counter; a path's level is derived from how many upgrades the player has bought from that path's attribute pool (§5, §7.10).
    • Commission lifecycle: explicit Accept required for daily and weekly; un-accepted commissions carry no penalty; weekly rewards now include followers (§13.1 / §13.2).
    • Commission generator must respect pathRequirement (incl. None = path-neutral) and filter templates whose steps reference path-locked actions (§13.4). failurePenalty field is canonical; generator supplies tier-scaled default when empty.
    • DeliverItemTo must source from player inventory only; commission does not auto-issue items. Worn-underwear sales (§15.1) are the canonical use case (§13.4).
    • Livestream → follower trickle via FollowerGainCalculator per tick; signed by reputation. Tip requests give an additional bonus on completion (§9.1.1 / §13.5).
    • Locations locked for launch: Beach + Train Station are in; School exterior and Hot springs were cut. Vertical slice is the basic shop set only (§10.4, §18.1).
    • Food vocabulary locked: 2 instant effects + 4 timed buffs; same-type stacks additively up to a cap; never poison (§6.7).

2. Architectural premise

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.

The session-system + loss resolver (Phase 3) is the second pin. Several GDD shifts (equipped-never-dropped, energy-zero cutscene, embarrassment-max no-extras, 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. Food buffs (§6.7) are external multipliers on attribute rates, so design the per-rate interface with that in mind even before GAS lands.


3. Phased roadmap

Phase estimates are rough and assume one engineer. Adjust as we go.

Phase 1 — Finish item identity + save round-trip; reshape slot enum (12 weeks)

  • Fix the SaceGame UPROPERTY typo on DaysPassed (GlobalSaveGameData.h:35).
  • Add UClothingItemInstance::Init(UClothingItem* Definition) (or static factory) so instances can be constructed at runtime.
  • Make USaveSubsystem::SaveGame actually capture live state into UGlobalSaveGameData: player money, equipped items, wardrobe items, calendar, observation-attribute snapshot. Populate FItemSaveRecord from current instances.
  • Make USaveSubsystem::LoadGame actually apply the loaded data: rebuild instances from records, push into wardrobe + ClothingManager slots. Replace the commented-out ClothingManager::HydrateClothing with real hydration.
  • Unify EPrivateBodyPartType and EBodyPart on EBodyPart. Migrate ClothingItem::CoveredBodyParts, ClothingManager::IsBodyTypeExposed, censorship paths in NakedDesireCharacter, BT / observation paths.
  • Reshape EClothingSlotType to the locked 18-slot list (§1.4 table). Add Outerwear, WristRestraint, AnkleRestraint, NeckRestraint. Rename Bra → UnderwearTop, Panties → UnderwearBottom, Body → Bodysuit, Shoes → Footwear. Add four new USkeletalMeshComponents in NakedDesireCharacter for the new slots. Update BP references in lockstep.
  • Implement Bodysuit exclusion in ClothingManager::PutOnClothing (auto-unequip Top/Bottom/UnderwearTop/UnderwearBottom; vice versa).
  • Define toy items: either a new USexToyItem : UItemInstance (recommended) or extend UClothingItem with a toy variant. Toy items target Nipples / Anal / Vagina slots, carry lust / embarrassment / pulse modifier fields, and an optional NPC-audible-vibration flag. Do not contribute to coverage.
  • Money: pick one authoritative location (UGlobalSaveGameData), make ANakedDesireCharacter::Money a read-through. Apply STARTING_MONEY at new-save creation. Reconcile int vs. float.
  • Remove 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. EClothingSlotType matches the §6.5 18-slot list. Bodysuit exclusion is enforced at runtime. Toy slots work with at least one placeholder toy item.
  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* (in Items/). Spawn transform, owning instance pointer, SaveGame-tagged.
  • AClothingPickup : AItemActor with a visual representation (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 AClothingPickups as FItemSaveRecords with world transform.

Exit criteria: unequip → world pickup actor appears → save / reload → pickup 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 (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 at this stage; sleep step finalizes any guaranteed loss.
    • Energy = 0: trigger a cutscene → apartment → sleep cycle → guaranteed loss of every world clothing outside the apartment.
    • Embarrassment = max: fade to apartment. No extra cost (no time skip, no money, no rep).
    • Police capture: fade to apartment, 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 showing the current loss state and what would be lost if a given cause fired.

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 (12 weeks)

  • Decision: GAS vs. extended StatsManager. Recommend GAS — the embarrassment formula has Pulse, Recognition, Coverage, Day/Night, per-NPC-type weights, and food-buff multipliers (§7.1, §6.7), which is exactly what UGameplayEffect is shaped for.
  • Add Lust (§7.2), Pulse (§7.5), Recognition (§7.6), Wanted (§7.7), Reputation (§7.8), Followers (§7.9). Money remains in the save schema directly (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 0.0f stub in StatsManager::TickComponent.
  • Lust → masturbate action (Slut-path gated, §7.2). Block the action in BP / quick-action when the player has no Slut investment.
  • Pulse simulation: rises with running, masturbation, exposure events; decays toward baseline at rest. Modifies embarrassment and lust gain rates (§7.5).
  • Recognition rise from Blogger photo events; face-cover bypass via Face + Eyes slots (§7.6); reduction via news-site reporting (§11.1).
  • Define the per-rate multiplier interface that food buffs (§6.7) and toy effects (§6.5) will hook into. Don't ship the buffs here; just leave the seams.
  • Debug overlay extension: live values + active modifier sources for every attribute.

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. Face-cover slot occupancy reduces recognition gain.

Phase 5 — Time + calendar + rent + sleep (34 days)

  • 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).
  • Weekly follower-income auto-deposit to the bank at week boundary (§7.9, §9.4).
  • Endless-mode flag on UGlobalSaveGameData; rent-eviction branch checks it.

Exit criteria: play through 7 in-game days, get charged rent at the week boundary, eviction triggers on insufficient funds, endless-mode disables eviction, weekly follower income lands in the bank.

Phase 6 — NPC types + recognition pipeline (12 weeks)

  • ENPCType enum, type-specific BT branches or per-type controller subclasses: Walker / Stalker / Blogger / Snitch / Harasser. No Helper.
  • 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: 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 + accept lifecycle (12 weeks)

  • Replace MissionsConfig's hand-authored daily list with UCommissionTemplate + procedural generation per §13.4.
  • Add typed objective steps as new UMissionGoal subclasses:
    • BeFullyNaked(duration)
    • BeFullyNakedNearNPCs(count, duration)
    • WalkNakedDistance(meters)
    • MoveDistanceFromClothing(meters)
    • BeObservedByNPCType(type, durationOrCount)
    • TakePhotoAtLocation(locationTag) (gated by Phase 8 phone work)
    • DeliverItemTo(npcOrLocation)player-owned inventory only (§13.4); the commission does not auto-issue items.
    • PerformAction(actionId)generator filters path-locked actions (e.g., masturbate is Slut-only).
  • Accept lifecycle (§13.1 / §13.2):
    • Both daily and weekly require explicit Accept on the forum.
    • Un-accepted commissions carry no penalty; accepted-but-failed apply the template's failurePenalty.
    • Weekly rewards include followers; weekly failurePenalty is heavier than daily.
  • Generator rules: respect pathRequirement (incl. None); filter path-locked-action templates by player path investment; substitute a tier-scaled default penalty when failurePenalty is empty.

Exit criteria: daily commissions regenerate from templates each in-game day; weekly arc is distinct; explicit Accept committed flow works end-to-end; failed accepts deduct the template penalty; un-accepted commissions never penalize the player.

Phase 8 — Phone + forum UI + battery + livestream (34 weeks)

  • Phone as an AItemActor (Phase 2 base) with hand / pocket / bag location semantics (§9).
  • PhoneSubsystem (§17.1): tickable; owns battery %, active app, livestream session lifecycle, charger interaction.
  • Battery (§9.8):
    • Passive base drain + per-app multiplier (Forum/Bank/Maps/Health Tracker/Gallery ~1×, back-camera ~3×, livestream ~5×). Livestream stacks on top of foreground app.
    • Apartment charger interactable: place phone on it, recharges at a fixed rate.
    • Portable powerbank: consumable UItemInstance purchasable at Convenience Store (§10.4). Single-use, restores fixed % when consumed. S-size container slot.
    • Hard shutdown at 0%: all apps lock; livestream ends and deposits earnings-to-date; future tips forfeit. Phone wakes from a consumed powerbank.
    • Sleep auto-charges to 100% (voluntary or energy-zero cutscene).
    • Battery % is part of the phone's UItemInstance state; persists across saves.
  • Apps: Camera + Gallery, Livestream, Bank, Feetex, Maps, Health Tracker, Forum.
  • Bank app income breakdown (§9.4): line items by source — commissions, livestream donations, in-stream tip-request completions, underwear sales, weekly follower auto-deposit.
  • Livestream (§9.1.1):
    • UStreamSession tickable UObject — works while phone is held OR placed.
    • Per-tick streamQualityScoreFollowerGainCalculator for the follower trickle (signed by reputation; shrinks at negative rep).
    • End-of-stream deposits both money and accumulated follower delta.
    • Tip requests: phone popup with Accept / Decline + countdown. Accept = spawn live objective tracker (reuse Phase 7 primitives). Complete = tip lands + small follower bonus. Fail = viewer count drops (less passive income for the remainder); no rep hit. Decline / timeout = no effect.
  • Photo system: render-to-texture (§17.6 / §21 Q5 — recommended).
  • Wire TakePhotoAtLocation commission step to actual photo events.

Exit criteria: end-to-end photo loop (capture → gallery → post → follower count updates). Livestream can be started, run while the phone is placed, generate trickle followers + donations, accept and complete a tip request for a bonus, end with payout. Battery drain visibly different per app; charger and powerbank both restore charge; 0% hard-shutdown behaves correctly.

Phase 9 — Path progression + content authoring (2 weeks)

  • Single shared XP pool — not per-path. Add XP to the save schema. Player invests XP via the forum profile (§13.3) into any path's attribute pool.
  • A path's level is derived from how many upgrades the player has purchased from that path's pool (§5.4 / §7.10) — there is no per-path XP counter.
  • Level-gating on clothing (§5.15.3) and on path-tagged commission templates (§7).
  • Author the vertical-slice content set from GDD §18.1 (1520 clothing items, 3 bag variants, 8 food items, 20 commission templates, 1 functional district).
  • Masturbation gating (§5.1, §7.2) — hide the quick-action entry until the player has at least one Slut-path upgrade.

Exit criteria: all three paths have at least 5 unlockable items and 3 path-specific commission templates. Slut-path-locked actions correctly hidden until the player invests. XP spent into one path raises that path's level without consuming XP that could go to another path.

Phase 10 — Remaining systems & polish

  • Bag inventory (§6.4) — uses Phase 2's AItemActor for world placement.
  • Food (§6.7):
    • Implement the locked vocabulary: 2 instant effects (energy restore, lust decrease) + 4 timed buffs (stamina regen +, max stamina +, embarrassment-gain resistance, lust-gain resistance).
    • Stacking: different types parallel; same type additive to a per-type cap.
    • Three cooking minigames (slice / stir / cook); scoring modulates buff strength; never poison.
  • Gym / beauty salon / adult shop / café / convenience store (§10.4). Convenience Store stocks powerbanks (Phase 8 dep). Adult Shop stocks toy items for the three toy slots (Phase 1 dep).
  • 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. Backed by DeliverItemTo(npcOrLocation) from Phase 7.
  • Wanted-poster takedown (§10.3) — interaction + spawn placement pass.
  • Beach + Train Station locations (§10.4) for full-launch scope.
  • Final tuning of all §21 numbers (theft, food caps, battery, tip-request distribution, embarrassment / lust / energy / pulse rates, face-cover magnitudes, etc.).

4. Working notes

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.

  • 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 reshape will break BP references — coordinate before renaming/adding values in EClothingSlotType. The character constructor creates skeletal-mesh components keyed to those names; expect BPs in Content/Blueprints/Player to need a touch.
  • Toy items vs. clothing items: the UClothingItem schema (coverage, CanExpose, IsRestrictive) doesn't fit sex toys cleanly. Strongly recommend a separate USexToyItem : UItemInstance (with LustModifier, EmbarrassmentModifier, PulseModifier, VibrationAudible fields) rather than overloading UClothingItem. Decide before Phase 1 toy work begins.
  • XP is a single shared pool (GDD §5, §7.10). Do not introduce per-path XP counters in Phase 9 or earlier — a path's level is derived from how much the player has invested into that path's attribute pool.
  • Food buff hookpoints belong in Phase 4 (attribute multiplier interface), even though the food items themselves ship in Phase 10. Leave the seams; don't retrofit later.
  • empty beyond this point