25 KiB
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.hdefinesUItemInstanceas an abstractUObjectwith a stableFGuid InstanceID, auto-assigned inPostInitPropertiesand refreshed inPostDuplicate.Clothing/ClothingItemInstance.hinherits from it and holds per-instanceCondition, a pointer to the immutableUClothingItemdefinition, and aStoredItemsarray for container slots. - Definition / instance split (GDD §6.1, §17.4) —
Clothing/ClothingItem.his the immutableUPrimaryDataAssetdefinition with GDD-aligned fields:Coverage,IsUnderwear,CanExpose,IsRestrictive,ContainerSlots(typed S/M/L viaEGarmentContainerSlotType+FGarmentContainerSlot),ProgressionPath, plus rendering data (SkeletalMesh,Materials,StaticMesh). - Progression path enum (GDD §5) —
Progression/ProgressionPath.hdefinesEProgressionPath { None, Slut, Exhibitionist, Slave }. Used onUClothingItem::ProgressionPath. - Body-part enum (GDD §6.3) —
Clothing/BodyPart.hdefinesEBodyPart { None, Boobs, Ass, Genitals }. Used onUClothingItem::CanExpose. - Save scaffolding (GDD §16) —
SaveGame/SaveSubsystem.h/.cpp(UGameInstanceSubsystem),SaveGame/ItemSaveRecord.h(generic{ InstanceId, Definition, Condition, ParentId }).Global/Constants.hnow usesDefaultSaveSlotNameinstead of a singleSLOT_NAMEmacro. - Observation-driven embarrassment (GDD §7.1) —
NPC/NPCAIController.cpp:56-69listens toOnTargetPerceptionUpdatedand forwards bSensed transitions toStats/StatsManager::SetObserved.Stats/StatsManager::TickComponent(StatsManager.cpp:23-38) now gates gain vs. decay onObserverCountinstead of unconditional decay. Foundation for Phase 4. - Modular character w/ per-slot meshes —
Player/NakedDesireCharacter.cpp:40-67creates 14 skeletal mesh components, one perEClothingSlotType. Aligns with GDD §17.6. - Equip / unequip / drop event —
Clothing/ClothingManager.cpp:88-163(slot-based, broadcastsOnClothingEquip / Unequip / DroppedwithUClothingItemInstance*). - Censorship toggle — compliance feature on
NakedDesireCharacter(BoobL/R,Front/BackBottomstatic meshes), driven byUNakedDesireUserSettings. - AI sight + behavior tree —
NPC/NPCAIController.cppruns a BT withPlayer/TargetLocation/SpawnLocationblackboard keys.NPC/NPCSpawner.cppproximity-gated spawn with day / night caps. - Mission framework —
Mission→MissionGoal→GoalRestrictioncomposition (MissionBuilder/). Two goals (FlashGoal,MinTimeGoal), three restrictions (EquipClothing,ExposeBodyPart,Location). Iterate-and-mutate bug inMissionsManager::RefreshDailyMissionsresolved (MissionsManager.cpp:53-68). - Daily-mission OOB guarded —
NakedDesireGameMode::RefreshDailyMissionsnow clampsDaysPassedto the authored array bounds (NakedDesireGameMode.cpp:70-71). Still a hand-authored list (see §1.3). - Location triggers —
Locations/LocationTrigger,Locations/LocationDatavia gameplay tags (GDD §10.4 area foundation). - Movement —
EnhancedInput, walk / run / crouch (NakedDesireCharacter.cpp:115-127), stamina-gated run (Ticklines 91-113). - Wardrobe interactable —
Interactables/Wardrobe.hholdsTArray<TObjectPtr<UClothingItemInstance>> ClothingItems;NakedDesireGameMode::BuyItemcharges 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 toUGlobalSaveGameData::SaveGame, which creates a fresh emptyUGlobalSaveGameData(GlobalSaveGameData.cpp:45-53) and writes it — i.e., it does not capture the live player / wardrobe / world state at all.USaveSubsystem::LoadGamecalls 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) —
UItemInstanceGUIDs exist, but no item has been promoted to a worldAActor.ClothingManager::DropClothing(ClothingManager.cpp:156-163) still only broadcastsOnClothingDropped— no spawn, no parent reassignment in any registry. - Attributes (§7) —
StatsManagercovers 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 at0.0fwith a TODO marker (StatsManager.cpp:30-32). - Coverage (§6.3.2) —
UClothingItem::CoverageandIsUnderwearexist on the definition, butClothingManager::IsBodyTypeExposed(ClothingManager.cpp:14-25) is still binary. NoGetEffectiveCoverage(EBodyPart)function, no underwear-halving, no sum-of-garments math. - Body-part enums — duplicated — both
Player/PrivateBodyPartType.h(EPrivateBodyPartType { FrontBottom, BackBottom, FrontTop }) andClothing/BodyPart.h(EBodyPart { Boobs, Ass, Genitals }) exist.UClothingItem::CoveredBodyPartsuses the old enum;UClothingItem::CanExposeuses 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 inMissionsConfig::DailyMissionskeyed by day index — no procedural generation. - Day / night —
NPCSpawner.cpp:38-41readsGetCurrentTime().Hoursand 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, noUSessionLossResolver. Game-over on embarrassment is a singleEndGameEmbarrassedBP event inNakedDesireGameMode; 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
ANPCclass. No Walker / Stalker / Blogger / Snitch / Harasser, no Police, no Wanted-poster mechanic. - Calendar, rent, sleep (§2.4, §15.2) —
DaysPassedincrements inNakedDesireGameMode::OnHourChanged(4), but no week, no rent, no eviction, no sleep action, no apartment bed interaction. - Item-world AActor (§6.1) — no
AItemActor/AClothingPickupbase. - Bag inventory (§6.4) — absent.
- Theft (§6.3.4) — new chance-based model (per-tick
P_theftafter grace period) not implemented. No theft timer / probability code at all. - Rip & tear (§6.3.5) — absent.
UClothingItemInstance::Conditionis read-only in code; no decrement source. - Expose action (§6.3.6) —
CanExposedata exists on the definition; runtime action absent. - Restrictive clothing (§6.3.7 / §10.4.1) —
IsRestrictivefield 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) —
StatsManageris still a bespokeUActorComponent; noUAttributeSet, noGameplayEffect-driven modifiers.
1.4 Conflicts to resolve
SaveGameUPROPERTY typo —GlobalSaveGameData.h:35declaresUPROPERTY(SaceGame)instead ofSaveGameonDaysPassed. The field will not serialize. (Likely also blocks Unreal Header Tool warnings; needs a clean rebuild after the fix.)EClothingSlotType(Clothing/ClothingSlotType.h) still includesNipples,Anal,Vaginaas equipment slots — those are body parts per the GDD. Slot list also lacksOuterwear,Accessory,Restraint(GDD §6.3 / §21 Q1). The character builds 14 skeletal mesh components against these values (NakedDesireCharacter.cpp:40-67), so the cleanup needs a matching content / BP pass.- Two competing body-part enums —
EPrivateBodyPartTypevs.EBodyPart.ClothingItem::CoveredBodyPartsuses one,ClothingItem::CanExposeuses the other. The censorship / observation code paths inNakedDesireCharacterandClothingManagerare still onEPrivateBodyPartType. Pick one (recommendEBodyPart) and migrate. USaveSubsystem::SaveGamewrites 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::LoadGameignores the loaded object — the static returns a populatedUGlobalSaveGameData, the subsystem stores nothing and applies nothing.UClothingListforward-declared inGlobalSaveGameData.h:11andWardrobe/ save records no longer use it. Dead reference; remove.Moneyrepresentation —ANakedDesireCharacter::Moneyisint(NakedDesireCharacter.h:132),UGlobalSaveGameData::Moneyisfloat(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_MONEYmacro added toConstants.h:8but no code references it. Either wire it into the player / save init, or remove.ClothingItemInstancehas no public constructor / factory —ClothingItempointer isprotectedwith noInit(UClothingItem*). Wardrobe / BuyItem flows currentlyEditAnywhere Instancedpopulate the array at design-time, which works for the existing test level but won't support runtime purchase or save-driven hydration.StatsManager::TickComponentenergy drain — drains energy at a flat0.9fper tick (StatsManager.cpp:26) regardless of activity, indoor / outdoor, or modifiers. GDD §7.3 specifies per-activity modifiers (energyDrainRunModifier, etc.).NPCSpawnerday window — usesHours >= 9 && < 21for "day" (NPCSpawner.cpp:40), GDD §10.1 says the day phase is08: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-tickP_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
SaceGameUPROPERTY typo onDaysPassed(GlobalSaveGameData.h:35). - Add
UClothingItemInstance::Init(UClothingItem* Definition)(or a static factory) so instances can be constructed at runtime — not justInstancedat design-time. - Make
USaveSubsystem::SaveGameactually pull live state into aUGlobalSaveGameData: player money, equipped items, wardrobe items, calendar, observation-attribute snapshot. CoverWardrobeItemsandEquippedItemsarrays viaFItemSaveRecordpopulated from currentUClothingItemInstances (Guid, definition soft pointer, condition, parent guid). - Make
USaveSubsystem::LoadGameactually apply the loaded data: rebuildUClothingItemInstances from records, push into the wardrobe array andClothingManagerslots. Replace the commented-outClothingManager::HydrateClothingwith the real hydration. - Unify
EPrivateBodyPartTypeandEBodyPart(recommend keepingEBodyPart, retiring the legacy enum). UpdateClothingItem::CoveredBodyParts,ClothingManager::IsBodyTypeExposed, censorship checks inNakedDesireCharacter, and the BT / observation paths. - Slot vocabulary pass: drop
Nipples/Anal/VaginafromEClothingSlotType, addOuterwear/Accessory/Restraint. Update the character's mesh component layout and any BP references in lockstep. - Money: pick one authoritative location (
UGlobalSaveGameDatais the natural choice;ANakedDesireCharacter::Moneybecomes a cached read-through). ApplySTARTING_MONEYat new-save creation. Convert character field to match save type. - Remove the dead
UClothingListforward declarations.
Exit criteria:
- Item dropped at runtime, condition modified, save → quit → load reproduces the same
FGuid, condition value, and slot / wardrobe membership. EBodyPartis the only body-part enum in use.- Slot list matches GDD §6.3 / §21 Q1 decision.
- A new game starts with
STARTING_MONEYand rent / purchase flows debit the canonical money field.
Phase 2 — World item actors + drop / pickup (1 week)
AItemActorbase wrapping aUItemInstance*(inItems/). Spawn transform, owning instance pointer, SaveGame-tagged.AClothingPickup : AItemActorwith a visual representation (useUClothingItem::StaticMeshwhen set, fallback to skeletal mesh impostor).- Hook
ClothingManager::DropClothingto spawnAClothingPickupat 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. USaveSubsystemextension: serializeAClothingPickups asFItemSaveRecords 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 onUNakedDesireGameInstanceor aUWorldSubsystem). ApartmentALocationTriggerflag drives session start / end. EmitsOnSessionStart/OnSessionEndwith cause.USessionLossResolver— single class, one methodResolveLoss(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::IncreaseEmbarrassmentmax-hit and energy-zero intoUSessionLossResolver. ReplaceEndGameEmbarrassedBP-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 (1–2 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 whatUGameplayEffectis 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)onClothingManagerper §6.3.2 (sum of garments covering the part, underwear halved, clamped to 1.0). Replace theCoverageWeight = 0.0fstub inStatsManager::TickComponent. - Lust → masturbate action (Slut-path gated, §7.2). Block the action in BP / quick-action when the player's Slut path level is 0.
- Pulse simulation: rises with running, masturbation, exposure events; decays toward baseline at rest. Modifies embarrassment / lust gain (§7.5).
- Recognition rise from Blogger photo events; face-cover bypass; reduce via news-site reporting (§11.1).
- Debug overlay extension: live values + active modifier sources for every attribute.
Exit criteria:
- Observed-while-exposed embarrassment gain reads coverage and modifiers from the attribute system (no more
0.0fstub). - Lust + Pulse simulate correctly and visibly influence embarrassment in a playtest scenario.
- Recognition increases after a (mocked) photo event and decreases after a news-report action.
Phase 5 — Time + calendar + rent + sleep (3–4 days)
UTimeOfDaySubsystemreplacing the BP-implementable time onNakedDesireGameMode. 90-day calendar, week boundary, day phase08:00–20:00(fix the 09–21 mismatch inNPCSpawner.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 (1–2 weeks)
ENPCTypeenum, 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 (1–2 weeks)
- Replace
MissionsConfig's hand-authored daily list withUCommissionTemplate+ procedural generation per §13.4. - Add typed objective steps as new
UMissionGoalsubclasses: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 (2–3 weeks)
- Phone as an
AItemActor(Phase 2 base) with hand / pocket / bag location semantics (§9). - Apps: Camera + Gallery, Livestream (
UStreamSessiontickable 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
TakePhotoAtLocationcommission 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
AItemActorfor 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_baggrace timers and per-tickP_theftchance roll. No ramp. - Expose action (§6.3.6) — read
CanExposeper 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/OnClothingUnequipreadsEPrivateBodyPartType. Migration toEBodyPartmust update that path or the censorship visibility will silently desync. - Slot-vocabulary cleanup will break BP references — coordinate before deleting
Nipples/Anal/VaginafromEClothingSlotType. The character constructor creates skeletal-mesh components keyed to those names; expect BPs inContent/Blueprints/Playerto need a touch. - empty beyond this point