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

25 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 EClothingSlotType. Aligns with GDD §17.6.
  • 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 (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 / 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) 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 / 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.
  • Adult shop, gym, beauty salon, cafe, convenience store — none.
  • 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

  • SaveGame UPROPERTY typoGlobalSaveGameData.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 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.
  • 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 representationANakedDesireCharacter::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 / factoryClothingItem 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 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, 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.


3. Phased roadmap

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

Phase 1 — Finish item identity + save round-trip (1 week)

Most of the structural work is done. What remains is connecting the pieces so an item modified at runtime survives a save / load cycle.

  • 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 UClothingItemInstances (Guid, definition soft pointer, condition, parent guid).
  • Make USaveSubsystem::LoadGame actually apply the loaded data: rebuild UClothingItemInstances 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* (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 AClothingPickups as FItemSaveRecords with world transform.

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 (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: 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 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:

  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, 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 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 / 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: 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:
    • 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 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. 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 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. Slut-path-locked actions are correctly hidden / gated.

Phase 10 — Remaining systems & polish

  • 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).

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 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