Compare commits
2 Commits
9a57f87d02
...
fd040d6a01
| Author | SHA1 | Date | |
|---|---|---|---|
| fd040d6a01 | |||
| 6feb7dd478 |
Binary file not shown.
Binary file not shown.
@@ -80,7 +80,7 @@ State of the C++ module as of the latest pass. File references use `Source/Naked
|
||||
|
||||
### 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.
|
||||
- **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`, and a pointer to the immutable `UClothingItem` definition. (The legacy per-garment `StoredItems` container array was removed per GDD §27 — only bags hold items; the item list will live on a future bag `UItemInstance` subclass.)
|
||||
- **Definition / instance split (GDD §6.1, §17.4)** — `Clothing/ClothingItem.h` is the immutable `UPrimaryDataAsset` definition. Most fields align with the current GDD: `Coverage`, `CanExpose`, `ContainerSlots` (typed S/M/L via `EGarmentContainerSlotType` + `FGarmentContainerSlot`), `ProgressionPath`, plus rendering data (`SkeletalMesh`, `Materials`, `StaticMesh`). The `IsUnderwear` and `IsRestrictive` fields also exist but **no longer match the locked spec** (§20 #20, §20 #21) — see §1.4 for the removal / migration work.
|
||||
- **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`.
|
||||
@@ -96,7 +96,7 @@ State of the C++ module as of the latest pass. File references use `Source/Naked
|
||||
- **Session manager (GDD §4.1–§4.4)** — `Global/SessionManagerSubsystem.h/.cpp` (`UWorldSubsystem`). Tracks `bSessionActive`, emits `OnSessionStart` / `OnSessionEnd(ESessionLossCause)`; `ESessionLossCause = { SafeReturn, EmbarrassmentMax, EnergyZero, PoliceCapture }`. Apartment `ALocationTrigger` starts a session on exit and safely ends it on re-entry. Subscribes (next-tick after world begin play) to `UStatsManager::EmbarrassmentUpdate` (max-hit → `EmbarrassmentMax`) and `EnergyUpdate` (≤0 → `EnergyZero`). Exposes `bPoliceChaseActive` (+ setter / getter) for the §4.4 loss-precedence rule the resolver owns. Replaces the old `EndGameEmbarrassed` GameMode BP call, which `UStatsManager::IncreaseEmbarrassment` no longer invokes. The `EndGameEmbarrassed` BlueprintImplementableEvent declaration still exists on `ANakedDesireGameMode` but is now dead from C++ and should be removed once BP no longer references it.
|
||||
- **Session loss resolver (GDD §4.4)** — `Global/SessionLossResolver.h/.cpp` (`UWorldSubsystem`). Single entry point `ResolveLoss(ESessionLossCause)`, bound to `USessionManagerSubsystem::OnSessionEnd`. Applies the police-chase precedence override (any cause → `PoliceCapture` while `bPoliceChaseActive`), then per cause: `EmbarrassmentMax` no-cost; `EnergyZero` destroys every world `AItemPickup` + clears its save record (guaranteed sleep loss); `PoliceCapture` deducts `PoliceCaptureMoneyPenalty` if affordable else flags a holding-cell outcome; `SafeReturn` no loss. Never strips equipped clothing. Autosaves, then broadcasts `OnSessionLossResolved(FinalCause, bWentToHoldingCell)` for the BP presentation / time-skip layer. See §1.3 for the pieces still delegated to BP / later phases.
|
||||
- **Movement** — `EnhancedInput`, walk / run / crouch (`NakedDesireCharacter.cpp:115-127`), stamina-gated run (`Tick` lines 91-113).
|
||||
- **Wardrobe storage + management** — `Inventory/InventorySubsystem` (`UGameInstanceSubsystem`) is the single runtime owner of the off-body store. It holds live `UItemInstance`s mirrored from `UGlobalSaveGameData::WardrobeItems` and exposes the atomic moves `AddToWardrobe` / `RemoveFromWardrobe` / `EquipFromWardrobe` / `UnequipToWardrobe`, each mutating the wardrobe + equipped save buckets together and broadcasting `OnWardrobeChanged`. `AClothingManager` stays the body-state authority (owns the `EquippedItems` bucket via the new `EquipSlot` + existing `RemoveClothing`); the bodysuit exclusion rule is now the shared static `UClothingManager::GetBodysuitExcludedSlots` and routes displaced garments back to the wardrobe instead of dropping them to the world. `AWardrobe` is reduced to an interaction shell that forwards to the subsystem (its stale `ClothingItems` array is gone). UI: `WardrobeScreenWidget` inits the inventory list and hosts the `EquipmentSlotMenuWidget` popup (same plumbing as `InventoryScreenWidget`); `WardrobeInventoryWidget` renders the live list and re-renders on `OnWardrobeChanged`; clicking a wardrobe item calls `EquipFromWardrobe`. The slot menu has a single Remove button whose `Init(slot, bAtWardrobe)` flag decides the action — store via `UnequipToWardrobe` when opened at the wardrobe, drop to the world otherwise. **Follow-ups:** `BuyItem` buy-flow not reattached (no caller yet); world pickup (`ItemPickup`→`TakeClothing`) still doesn't clear the `WorldItems` record; non-clothing wardrobe items (phones/toys) are stored but not yet rendered.
|
||||
- **Wardrobe storage + management** — `Inventory/InventorySubsystem` (`UGameInstanceSubsystem`) is the single runtime owner of the off-body store. It holds live `UItemInstance`s mirrored from `UGlobalSaveGameData::WardrobeItems` and exposes the atomic moves `AddToWardrobe` / `RemoveFromWardrobe` / `EquipFromWardrobe` / `UnequipToWardrobe`, each mutating the wardrobe + equipped save buckets together and broadcasting `OnWardrobeChanged`. `AClothingManager` stays the body-state authority (owns the `EquippedItems` bucket via the new `EquipSlot` + existing `RemoveClothing`); the bodysuit exclusion rule is now the shared static `UClothingManager::GetBodysuitExcludedSlots` and routes displaced garments back to the wardrobe instead of dropping them to the world. `AWardrobe` is reduced to an interaction shell that forwards to the subsystem (its stale `ClothingItems` array is gone). UI: `WardrobeScreenWidget` inits the inventory list and hosts the `EquipmentSlotMenuWidget` popup (same plumbing as `InventoryScreenWidget`); `WardrobeInventoryWidget` renders the live list and re-renders on `OnWardrobeChanged`; clicking a wardrobe item calls `EquipFromWardrobe`. The slot menu has a single Remove button whose `Init(slot, bAtWardrobe)` flag decides the action — store via `UnequipToWardrobe` when opened at the wardrobe, drop to the world otherwise. **Home-storage model (GDD §6.5 / §10.4 / §28):** the wardrobe is the general home stockpile for **all non-food** items (clothing, sex toys, phones, keys, spare bags) — `WardrobeItems` is already a generic `UItemInstance` list, so this matches with no change. Food is **not** stored in the wardrobe; it lives in the **fridge** (separate fixture, pending — see §1.3). **Follow-ups:** `BuyItem` buy-flow not reattached (no caller yet); world pickup (`ItemPickup`→`TakeClothing`) still doesn't clear the `WorldItems` record; non-clothing wardrobe items (phones/toys) are stored but not yet rendered in the wardrobe UI.
|
||||
|
||||
### 1.2 Partially implemented (deviates from GDD)
|
||||
|
||||
@@ -123,7 +123,10 @@ State of the C++ module as of the latest pass. File references use `Source/Naked
|
||||
- **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)** — **core landed (Phase 5):** `UTimeOfDaySubsystem` (`Global/TimeOfDaySubsystem`) owns the clock, rolls the calendar at 04:00, fires `OnHourChanged`/`OnDayChanged`/`OnPhaseChanged`, charges `WEEKLY_RENT` every 7th roll with immediate eviction via `OnCampaignEnded(Evicted)`, and exposes `Sleep()`/`SkipToNextMorning()`/`SkipTime()`. Mission refresh moved to `GameMode::HandleDayChanged`. The apartment **bed interactable** (`Interactables/Bed`) is in — interacting calls `USessionLossResolver::ResolveSleepLoss()` (outside-clothing loss) then `UTimeOfDaySubsystem::Sleep()`, with a BP `PlaySleepTransition` fade hook. **Still to do:** rewire the UltraDynamicSky actor to follow `SetCurrentTime` (and stop the old BP clock from advancing independently — otherwise the clock double-advances); the §4.4 cutscenes calling into the new skip API; the ending/eviction screen reacting to `OnCampaignEnded`; daily follower deposit is stubbed (no follower count until Phase 8); phone charge-to-100% on sleep stubbed (Phase 9). `WEEKLY_RENT` is a §21 tuning placeholder.
|
||||
- **Item-world AActor (§6.1)** — no `AItemActor` / `AClothingPickup` base.
|
||||
- **Bag inventory (§6.4)** — absent.
|
||||
- **Bag inventory (§6.4)** — absent. Per the locked model (GDD §6.4 / §27): bags are the **only** container, capacity is a flat per-bag **item count** (no S/M/L size classes), and any item type can be stored. The container concept is removed from clothing — the per-garment `UClothingItemInstance::StoredItems` array has been deleted; the item list will live on a future bag `UItemInstance` subclass.
|
||||
- **Dedicated carry slots (§6.5 / §27)** — `Phone`, `Bag`, `Key` are single-item slots on the player, separate from the 18 clothing/accessory/restraint/toy slots and from the old (removed) hand slot. Deliberately **not** in `EClothingSlotType` (it's clothing-named); each carry slot is modeled independently.
|
||||
- **Phone slot + phone item — done (data + slot).** `Phone/PhoneItemDefinition` (`UItemDefinition`) + `Phone/PhoneItemInstance` (`UItemInstance`, per-instance `CurrentBattery` in `FPhoneInstanceState`; `MaxBattery` on the definition). The slot lives on `UInventorySubsystem`: `EquippedPhone`, `GetEquippedPhone()`, `OnPhoneChanged`, hot-swap on equip (§9.9). `EquipFromWardrobe`/`UnequipToWardrobe` route phones to `EquipPhone`/`UnequipPhone`; the equipped phone persists via the shared `EquippedItems` save bucket and re-hydrates in `EnsureHydrated` (clothing in that bucket is still hydrated by `ClothingManager`). **Follow-ups:** no phone-slot UI widget yet and the wardrobe list (`WardrobeInventoryWidget`) renders clothing only, so a phone is seedable via `StartingSaveData.Phone` / code but not yet equippable via the wardrobe screen; battery drain/charge is Phase 8/9.
|
||||
- **Bag + key slots — pending** (deferred with the bag system, see above).
|
||||
- **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.
|
||||
@@ -131,7 +134,8 @@ State of the C++ module as of the latest pass. File references use `Source/Naked
|
||||
- **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.
|
||||
- **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. No `UFoodItemDefinition` / `UFoodItemInstance` types yet.
|
||||
- **Fridge — food home storage (§6.5 / §10.4 / §28)** — absent. Per the home-storage model, food is stored **only** in the apartment fridge (never the wardrobe). Needs: an `AFridge` interactable actor (mirror `AWardrobe`), a `FridgeItems` save bucket + `Add/Remove/Get` on `UGlobalSaveGameData`, fridge storage + `OnFridgeChanged` on `UInventorySubsystem`, the minimal food item types above, and a fridge screen widget. Tracks with the Phase 10 food system.
|
||||
- **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.
|
||||
@@ -345,12 +349,12 @@ Phase estimates are rough and assume one engineer. Adjust as we go.
|
||||
|
||||
### Phase 8 — Phone + forum UI + battery + livestream (3–4 weeks)
|
||||
|
||||
- Phone as an `AItemActor` (Phase 2 base) with hand / pocket / bag location semantics (§9).
|
||||
- Phone as an `AItemActor` (Phase 2 base). Usable from the dedicated **phone slot** (§6.5 / §27); placed-in-world streaming exception still applies (§9.1.1).
|
||||
- `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.
|
||||
- **Portable powerbank**: consumable `UItemInstance` purchasable at Convenience Store (§10.4). Single-use, restores fixed % when consumed. Stored in the equipped bag like any other item (no size classes, §6.4 / §27).
|
||||
- **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.
|
||||
@@ -385,7 +389,7 @@ Phase estimates are rough and assume one engineer. Adjust as we go.
|
||||
|
||||
### Phase 10 — Remaining systems & polish
|
||||
|
||||
- Bag inventory (§6.4) — uses Phase 2's `AItemActor` for world placement.
|
||||
- Bag inventory (§6.4) — uses Phase 2's `AItemActor` for world placement. Bag `UItemInstance` subclass holds a count-limited list of any `UItemInstance` (no size classes); add the `Phone` / `Bag` / `Key` carry slots (§6.5 / §27).
|
||||
- **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).
|
||||
- Universal built-in effect: every food restores effective max energy to base max (hunger reset; §7.3 hookpoint from Phase 4).
|
||||
|
||||
@@ -152,6 +152,7 @@ Each path has a level (e.g., 1–10). Level gates clothing, missions, and attrib
|
||||
- **Phone** (one active at a time, but the player may own multiple models simultaneously — §9, §9.9)
|
||||
- **Consumables** (food, §6.7)
|
||||
- **Toys** — sex toys for the toy slots (§6.5). Bought at the Adult Shop (§10.4).
|
||||
- **Keys** — unlock restraints (§10.4.1). Carried in the dedicated key slot (§6.5).
|
||||
|
||||
There are no other gadget categories. The phone is the only electronic device the player carries.
|
||||
|
||||
@@ -164,12 +165,9 @@ Attributes per clothing instance:
|
||||
- `condition` — float [0,1]. Starts at 1.0 (new). Decreases via §6.3.4.
|
||||
- `canExpose` — list of body parts this garment can momentarily reveal without unequipping (e.g., coat → boobs, ass).
|
||||
- `restrictions` — list of restriction entries that fire while this item is equipped (e.g., wrist cuffs block phone use and changing top clothing; ankle cuffs block running). Most items have an empty list. See §6.3.7 for the full vocabulary.
|
||||
- `containerSlots` — optional inventory (e.g., pants pocket for phone).
|
||||
|
||||
#### 6.3.1 Container slots
|
||||
Container slot allows player to carry items there.
|
||||
- `size`: S, M, L - Size of the container slot
|
||||
- `count`: Count of the container slots
|
||||
#### 6.3.1 No clothing inventory
|
||||
Clothing provides no storage of its own — there are no pockets or per-garment container slots. The only item container in the game is the equipped bag (§6.4). All carriable items go in a bag; the phone, bag, and key have their own dedicated equipment slots (§6.5).
|
||||
|
||||
#### 6.3.2 Coverage resolution
|
||||
For each body part `b`:
|
||||
@@ -239,14 +237,16 @@ Authored examples:
|
||||
**Commission interaction:** an accepted commission whose objective needs a blocked action (e.g., `PerformAction(masturbate)` with `BlockMasturbate` active) does not auto-fail; it simply cannot progress until the restriction is cleared. The commission's day / week deadline still applies normally.
|
||||
|
||||
### 6.4 Bags
|
||||
- Bag is an equippable item with its own inventory.
|
||||
- Player can equip **one** bag at a time.
|
||||
- Bag can be placed in the world (set down) — persists as an AActor.
|
||||
- A bag is an equippable item with its own inventory — the **only** container in the game.
|
||||
- Player can equip **one** bag at a time (the bag slot, §6.5).
|
||||
- **Any** item type can be stored in a bag — clothing, phone, food, toys, keys, powerbanks, etc.
|
||||
- Capacity is a flat **maximum item count**, authored per bag — different bags hold different numbers of items. There are no item size classes (no S / M / L): one slot holds one item regardless of what it is.
|
||||
- Bag can be placed in the world (set down) — persists as an AActor with its contents.
|
||||
- Bags follow the same theft rules as clothing (§6.3.4) and the same session-loss rules as other items (§4.4 / §6.6).
|
||||
|
||||
### 6.5 Inventory & Equipment
|
||||
|
||||
The player has 18 equipment slots in four groups, plus a bag slot, a hand slot, and container slots exposed by what's currently equipped.
|
||||
The player has 18 equipment slots in four groups, plus three dedicated carry slots — **phone**, **bag**, and **key**. There is no hand slot and no per-garment container slots; in-session carry storage is the equipped bag's inventory (§6.4), while owned items rest in apartment home storage (wardrobe / fridge) between sessions — see **Home storage** below.
|
||||
|
||||
#### Equipment slots
|
||||
|
||||
@@ -294,19 +294,30 @@ Restraint slots are governed by §6.3.7 (effects) and §10.4.1 (removal). `NeckR
|
||||
|
||||
Sex toys are bought from the Adult Shop (§10.4) and follow the standard item-identity rules (§6.1). Toys do **not** contribute to coverage (§6.3.2); a butt plug under panties leaves `UnderwearBottom`'s coverage value unchanged and the ass remains "covered" by panties for embarrassment math. Toys can be hidden beneath clothing or worn while otherwise nude — the slot is orthogonal to the body-clothing layering rules. Effects (lust modifier, embarrassment modifier, pulse modifier, audible vibration that NPCs may detect) are per-toy and authored on the toy's `UItemInstance`.
|
||||
|
||||
#### Other equipment
|
||||
- **Bag slot** — 1 (see §6.4).
|
||||
- **Hand slot** — 1. Holds one carriable item at a time (phone, food, key, etc.). Hand-dependent actions (phone use, item pickup, masturbate, expose action) are disabled when an equipped item declares the corresponding `Block*` restriction (§6.3.7); wrist cuffs are the canonical case.
|
||||
#### Dedicated carry slots
|
||||
- **Phone slot** — 1. Holds the active phone (§9, §9.9). The phone is usable directly from this slot; an empty slot means no phone is available. `BlockPhoneUse` (§6.3.7) still disables it while a restraint is on.
|
||||
- **Bag slot** — 1. Holds one bag (§6.4). The bag's inventory is the only general carry storage during a session.
|
||||
- **Key slot** — 1. Holds the key used for the restraint unlock action (§10.4.1).
|
||||
|
||||
#### Container slots
|
||||
- Each equipped clothing item may expose container slots (e.g., pants pocket). The equipped bag exposes container slots as well. Each container slot has a size class (S / M / L, §6.3.1) that constrains what fits.
|
||||
- There is no abstract "backpack" inventory. Total carry capacity = sum of container slots provided by currently equipped items.
|
||||
Each of these slots accepts only its own item type — a phone in the phone slot, a bag in the bag slot, a key in the key slot. Everything else the player carries lives in the equipped bag. Actions that previously implied a free hand (item pickup, masturbate, expose action) are gated only by the relevant `Block*` restriction (§6.3.7); wrist cuffs are the canonical case.
|
||||
|
||||
#### Carry storage (in-session)
|
||||
- The only thing the player carries items in is the equipped bag's inventory (§6.4). Clothing exposes no container slots.
|
||||
- Capacity is the equipped bag's authored item-count limit. With no bag equipped, the player carries only what is worn plus what occupies the phone, bag, and key slots.
|
||||
- There are no item size classes — one bag slot holds one item of any type.
|
||||
|
||||
#### Home storage (apartment)
|
||||
Owned items rest in the apartment between sessions, in two fixtures (§10.4):
|
||||
- **Wardrobe** — stores every **non-food** item: clothing, sex toys, phones, keys, and spare bags. It is the general home stockpile and the loadout point — equip clothing, swap the active phone (§9.9), grab a key or bag before leaving.
|
||||
- **Fridge** — stores **food / consumables only** (§6.7). Food cannot be kept in the wardrobe.
|
||||
|
||||
Home storage is separate from carry storage: the wardrobe and fridge are the apartment stockpiles where owned items live, whereas the equipped bag is the limited inventory carried into a session. Items move between home storage and the player at the fixture.
|
||||
|
||||
### 6.6 Item loss summary table
|
||||
| Situation | Outcome |
|
||||
|-------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| Session ends safely | All carried / equipped / placed items persist. |
|
||||
| Session lost, item equipped | Item persists, fully equipped. Loss never forcibly removes worn clothing (§4.4). |
|
||||
| Session lost, item in a slot on the player (worn, phone, bag, key) | Item persists, fully equipped. Loss never forcibly removes anything held in a slot on the player (§4.4). |
|
||||
| Session lost, item in equipped bag | Item persists (bag is on the player). |
|
||||
| Session lost, bag placed in world | Bag and contents are subject to the same theft chance as clothing (§6.3.4) and to sleep-loss if the player sleeps before retrieving them. |
|
||||
| In-session, clothing past the grace period | Each check is a flat chance of theft (§6.3.4). Picking the item back up resets the grace timer. |
|
||||
@@ -452,7 +463,7 @@ Movement should be implemented as a state machine on the character with clear tr
|
||||
|
||||
## 9. Phone
|
||||
|
||||
The phone is the player's diegetic UI hub. It can be used if it is in: hand, pocket (pants with phone-sized container slot), or equipped bag. **Exception:** the livestream app (§9.1.1) continues to operate while the phone is placed in the world, so the player can keep streaming after setting the phone down for a back-camera shot.
|
||||
The phone is the player's diegetic UI hub. It is usable whenever it occupies the player's **phone slot** (§6.5) — that is the active phone (§9.9). **Exception:** the livestream app (§9.1.1) continues to operate while the phone is placed in the world, so the player can keep streaming after setting the phone down for a back-camera shot.
|
||||
|
||||
The phone is battery-powered (§9.8). A dead phone is unusable until charged — no forum, no camera, no maps. Plan accordingly.
|
||||
|
||||
@@ -509,7 +520,7 @@ The phone has a finite battery that drains while powered on and recharges at the
|
||||
- Livestream: ~5× — heaviest drain. Livestream is the only app that ticks in the background and stacks its drain on top of whatever app is in the foreground.
|
||||
- **Charging.**
|
||||
- **Apartment charger** — primary source. A charger spot in the apartment recharges the phone at a fixed rate (TBD) while the phone is placed on it.
|
||||
- **Portable powerbank** — purchasable consumable (Convenience Store, §10.4). Single-use; consuming one restores a fixed percentage of charge (TBD). Occupies one container slot (size S, §6.3.1) in the player's inventory and follows normal item-identity / theft rules.
|
||||
- **Portable powerbank** — purchasable consumable (Convenience Store, §10.4). Single-use; consuming one restores a fixed percentage of charge (TBD). Stored in the equipped bag like any other item (§6.4) and follows normal item-identity / theft rules.
|
||||
- **Dead battery (0%).** Hard shutdown:
|
||||
- All apps lock. Opening the phone shows a "dead battery" overlay; no input passes through.
|
||||
- An active livestream **ends immediately**. Earnings accrued up to the cut-off are deposited; further viewers / tips that would have arrived are forfeit.
|
||||
@@ -575,7 +586,7 @@ Sleeping at home fast-forwards 8 hours.
|
||||
- **Wanted poster mechanic:** wanted posters spawn in the city. Tearing them all down clears the `wanted` tag and stops police spawning until a new Snitch report retriggers it.
|
||||
|
||||
### 10.4 Locations
|
||||
- **Apartment** — safe zone. Wardrobe, bed, PC, kitchen.
|
||||
- **Apartment** — safe zone. **Wardrobe** (home storage for all non-food items — clothing, sex toys, phones, keys, spare bags; §6.5), **Fridge** (home storage for food / consumables only; §6.5 / §6.7), bed, PC, kitchen.
|
||||
- **Convenience store** — cooking ingredients; portable phone powerbanks (§9.8).
|
||||
- **Café** — pre-made food.
|
||||
- **Clothing shops** — various types, each with a different inventory mix.
|
||||
@@ -960,11 +971,11 @@ Decisions previously open, now fixed:
|
||||
8. **Run length:** 90-day campaign. Endless mode unlocked after first completion (rent-eviction disabled, all other systems intact). See §3.3.
|
||||
9. **Cuff/restraint removal:** Key + timed unlock action only. No Helper NPC, no paid adult-shop service. See §10.4.1.
|
||||
10. **Voice commands:** Not used. Hotkey-driven only.
|
||||
11. **Equipment slot list:** 18 slots, locked. Body clothing (8): `Outerwear`, `Top`, `Bottom`, `UnderwearTop`, `UnderwearBottom`, `Bodysuit` (exclusive with the prior four), `Socks`, `Footwear`. Accessories (4): `Head`, `Face`, `Eyes`, `Neck`. Restraints (3, independent): `WristRestraint`, `AnkleRestraint`, `NeckRestraint`. Toys (3, independent): `Nipples`, `Anal`, `Vagina`. Face-cover bypass for recognition is driven by `Face` and `Eyes`. Toys do not contribute to coverage. See §6.5 for the full table.
|
||||
11. **Equipment slot list:** 18 slots, locked. Body clothing (8): `Outerwear`, `Top`, `Bottom`, `UnderwearTop`, `UnderwearBottom`, `Bodysuit` (exclusive with the prior four), `Socks`, `Footwear`. Accessories (4): `Head`, `Face`, `Eyes`, `Neck`. Restraints (3, independent): `WristRestraint`, `AnkleRestraint`, `NeckRestraint`. Toys (3, independent): `Nipples`, `Anal`, `Vagina`. Face-cover bypass for recognition is driven by `Face` and `Eyes`. Toys do not contribute to coverage. The phone, bag, and key are carried in three separate dedicated slots (§27), not part of the 18. See §6.5 for the full table.
|
||||
12. **City location list:** locked for launch. Apartment, Convenience Store, Café, Clothing Shops, Gym, Beauty Salon, Adult Shop, Electronics Shop, Streets / Parks / Alleys, **Beach**, **Train Station**, **Casino**. School exterior and hot springs (onsen) were considered and cut. Vertical slice (§18.1) covers the basic shop set; Beach, Train Station, Casino, and Electronics Shop are full-launch additions. See §10.4.
|
||||
13. **Attribute level-up cost:** XP only. XP is a single shared pool (not per-path); the player chooses which path's attribute pool to spend it on, and may eventually max all three. No money, no separate currency. See §7.10.
|
||||
14. **Food effect vocabulary:** locked. Two instant effects (energy restore, lust decrease) plus four timed buffs (stamina regen +, max stamina +, embarrassment-gain resistance, lust-gain resistance). Stacking: different types parallel; same type additive up to a per-type cap. No pulse buffs, no caffeine trickle. See §6.7.
|
||||
15. **Carriable categories:** locked. Clothing, Bags, Phone, Consumables (food), Toys. No other gadget categories beyond the phone. See §6.2.
|
||||
15. **Carriable categories:** locked. Clothing, Bags, Phone, Consumables (food), Toys, Keys. No other gadget categories beyond the phone. See §6.2.
|
||||
16. **Casino:** added to the city (§10.4, §10.4.2). Main floor (slots, blackjack, roulette — money only). VIP room — **gated by a per-day entrance fee** (no permanent unlock; pay each day you want in). Permissive dress code; strip-game blackjack and roulette tables that bet clothing. No poker. Mixed pacing: slots instant, table games take in-game minutes. Net EV negative — variance is the appeal.
|
||||
17. **Police capture, can't pay:** short non-interactive holding-cell cutscene, then time fast-forwards to the next morning. The night served settles the debt; no money owed afterward. Replaces the earlier "skip days proportional to unpaid amount" rule. See §4.4.
|
||||
18. **Loss precedence during a police chase:** if the player is actively being chased by police (per §10.3) and any other loss condition fires (embarrassment max, energy zero), the loss resolves as **police capture** — the chase wins. The cops catch the player as they collapse. Applies even without a prior `wanted` tag. See §4.4.
|
||||
@@ -976,6 +987,8 @@ Decisions previously open, now fixed:
|
||||
24. **Hunger via max-energy decay.** Effective max energy decays over time (a hunger rate); eating any food restores effective max to base max as a built-in universal effect (no per-food authoring). Sleep restores current energy but does NOT reset hunger — only eating does. Floors at a TBD fraction of base max so the player can't be starved into a forced game-over. See §7.3 / §6.7.
|
||||
25. **Follower income cadence — daily, not weekly.** Passive follower income accrues and auto-deposits to the bank **every in-game day** at the 04:00 day-roll, not in a weekly lump. Resolves the prior §7.9 ↔ §9.4 / §13.3 contradiction in favor of §7.9's daily model. Rent remains a separate **weekly** charge (§15.2); the two cadences are intentionally offset. See §7.9 / §9.4 / §13.3 / §15.1.
|
||||
26. **Masturbation gating — home vs. public.** Home masturbation (in the apartment) is **always available to every player, regardless of path**. Masturbating *during a session* (outside the apartment) is a **Slut-path unlock** (§5.1). The §14.1 quick-action shows the masturbate entry unconditionally at home; in-session it is hidden until the player has Slut-path investment. Non-Slut players therefore cannot reset lust mid-session and rely on lust-decrease food (§6.7) or returning home. Commission `PerformAction(masturbate)` objectives are public and thus Slut-gated by the generator (§13.4). Resolves the prior §5.1 ↔ §7.2 contradiction. See §7.2 / §5.1 / §14.1.
|
||||
27. **Bags are the only carry container; clothing has no pockets.** Per-garment container slots and item size classes (S / M / L) are removed. The only thing the player *carries* items in is an equipped bag — capacity is a flat per-bag **item count** (no size constraints), and any item type can go in any bag slot. The player also gains three dedicated single-item carry slots — **phone**, **bag**, and **key** — replacing the old generic hand slot; the phone is usable directly from the phone slot. See §6.3.1 / §6.4 / §6.5.
|
||||
28. **Home storage is two fixtures: wardrobe + fridge.** Owned items rest in the apartment between sessions. The **wardrobe** holds every non-food item (clothing, sex toys, phones, keys, spare bags) and is the loadout point; the **fridge** holds food / consumables only, and food cannot be kept in the wardrobe. Home storage (the apartment stockpile) is distinct from carry storage (the count-limited equipped bag, §27). See §6.5 / §10.4.
|
||||
|
||||
## 21. Open Design Questions
|
||||
|
||||
|
||||
@@ -33,7 +33,4 @@ public:
|
||||
protected:
|
||||
virtual void CaptureState(FInstancedStruct& OutState) const override;
|
||||
virtual void ApplyState(const FInstancedStruct& InState) override;
|
||||
|
||||
UPROPERTY(BlueprintReadOnly, Category = "Clothing Item")
|
||||
TArray<TObjectPtr<UItemInstance>> StoredItems;
|
||||
};
|
||||
@@ -7,7 +7,10 @@
|
||||
#include "NakedDesire/Clothing/ClothingItemDefinition.h"
|
||||
#include "NakedDesire/Clothing/ClothingItemInstance.h"
|
||||
#include "NakedDesire/Clothing/ClothingManager.h"
|
||||
#include "NakedDesire/Items/ItemDefinition.h"
|
||||
#include "NakedDesire/Items/ItemInstance.h"
|
||||
#include "NakedDesire/Phone/PhoneItemDefinition.h"
|
||||
#include "NakedDesire/Phone/PhoneItemInstance.h"
|
||||
#include "NakedDesire/Player/NakedDesireCharacter.h"
|
||||
#include "NakedDesire/SaveGame/GlobalSaveGameData.h"
|
||||
#include "NakedDesire/SaveGame/ItemSaveRecord.h"
|
||||
@@ -19,6 +22,12 @@ const TArray<TObjectPtr<UItemInstance>>& UInventorySubsystem::GetWardrobeItems()
|
||||
return WardrobeItems;
|
||||
}
|
||||
|
||||
UPhoneItemInstance* UInventorySubsystem::GetEquippedPhone()
|
||||
{
|
||||
EnsureHydrated();
|
||||
return EquippedPhone;
|
||||
}
|
||||
|
||||
void UInventorySubsystem::AddToWardrobe(UItemInstance* Item)
|
||||
{
|
||||
EnsureHydrated();
|
||||
@@ -37,6 +46,12 @@ void UInventorySubsystem::EquipFromWardrobe(UItemInstance* Item)
|
||||
{
|
||||
EnsureHydrated();
|
||||
|
||||
if (UPhoneItemInstance* Phone = Cast<UPhoneItemInstance>(Item))
|
||||
{
|
||||
EquipPhone(Phone);
|
||||
return;
|
||||
}
|
||||
|
||||
UClothingItemInstance* Clothing = Cast<UClothingItemInstance>(Item);
|
||||
if (!Clothing || !Clothing->GetClothingItemDefinition())
|
||||
return;
|
||||
@@ -74,6 +89,12 @@ void UInventorySubsystem::UnequipToWardrobe(UItemInstance* Item)
|
||||
{
|
||||
EnsureHydrated();
|
||||
|
||||
if (UPhoneItemInstance* Phone = Cast<UPhoneItemInstance>(Item))
|
||||
{
|
||||
UnequipPhone(Phone);
|
||||
return;
|
||||
}
|
||||
|
||||
UClothingItemInstance* Clothing = Cast<UClothingItemInstance>(Item);
|
||||
if (!Clothing || !Clothing->GetClothingItemDefinition())
|
||||
return;
|
||||
@@ -90,6 +111,45 @@ void UInventorySubsystem::UnequipToWardrobe(UItemInstance* Item)
|
||||
OnWardrobeChanged.Broadcast();
|
||||
}
|
||||
|
||||
void UInventorySubsystem::EquipPhone(UPhoneItemInstance* Phone)
|
||||
{
|
||||
if (!Phone || Phone == EquippedPhone)
|
||||
return;
|
||||
|
||||
UGlobalSaveGameData* Save = GetSave();
|
||||
|
||||
// Hot-swap: the previously equipped phone returns to the wardrobe (§9.9).
|
||||
if (EquippedPhone)
|
||||
{
|
||||
if (Save)
|
||||
Save->RemoveEquippedItem(EquippedPhone);
|
||||
StoreItem(EquippedPhone);
|
||||
}
|
||||
|
||||
UnstoreItem(Phone);
|
||||
EquippedPhone = Phone;
|
||||
if (Save)
|
||||
Save->AddEquippedItem(Phone);
|
||||
|
||||
OnPhoneChanged.Broadcast(EquippedPhone);
|
||||
OnWardrobeChanged.Broadcast();
|
||||
}
|
||||
|
||||
void UInventorySubsystem::UnequipPhone(UPhoneItemInstance* Phone)
|
||||
{
|
||||
if (!Phone || EquippedPhone != Phone)
|
||||
return;
|
||||
|
||||
if (UGlobalSaveGameData* Save = GetSave())
|
||||
Save->RemoveEquippedItem(Phone);
|
||||
|
||||
EquippedPhone = nullptr;
|
||||
StoreItem(Phone);
|
||||
|
||||
OnPhoneChanged.Broadcast(nullptr);
|
||||
OnWardrobeChanged.Broadcast();
|
||||
}
|
||||
|
||||
void UInventorySubsystem::EnsureHydrated()
|
||||
{
|
||||
UGlobalSaveGameData* Save = GetSave();
|
||||
@@ -108,6 +168,23 @@ void UInventorySubsystem::EnsureHydrated()
|
||||
if (UItemInstance* Instance = UItemInstance::CreateFromRecord(this, Record))
|
||||
WardrobeItems.Add(Instance);
|
||||
}
|
||||
|
||||
// Only the phone is hydrated from the equipped bucket here; equipped clothing is owned and
|
||||
// hydrated by the per-character UClothingManager. Pre-check the definition type so we don't
|
||||
// mint throwaway clothing instances.
|
||||
EquippedPhone = nullptr;
|
||||
for (const FItemSaveRecord& Record : Save->GetEquippedItems())
|
||||
{
|
||||
const UItemDefinition* Definition = Record.Definition.LoadSynchronous();
|
||||
if (!Definition || !Definition->IsA<UPhoneItemDefinition>())
|
||||
continue;
|
||||
|
||||
if (UPhoneItemInstance* Phone = Cast<UPhoneItemInstance>(UItemInstance::CreateFromRecord(this, Record)))
|
||||
{
|
||||
EquippedPhone = Phone;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UInventorySubsystem::StoreItem(UItemInstance* Item)
|
||||
|
||||
@@ -9,8 +9,10 @@
|
||||
class UItemInstance;
|
||||
class UClothingManager;
|
||||
class UGlobalSaveGameData;
|
||||
class UPhoneItemInstance;
|
||||
|
||||
DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnWardrobeChangedSignature);
|
||||
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnPhoneChangedSignature, UPhoneItemInstance*, Phone);
|
||||
|
||||
/**
|
||||
* Runtime owner of the off-body item store (the apartment wardrobe, GDD §6.5 / §10.4).
|
||||
@@ -31,9 +33,16 @@ public:
|
||||
UPROPERTY(BlueprintAssignable)
|
||||
FOnWardrobeChangedSignature OnWardrobeChanged;
|
||||
|
||||
// Fires when the phone slot changes; broadcasts the newly-equipped phone, or nullptr when emptied.
|
||||
UPROPERTY(BlueprintAssignable)
|
||||
FOnPhoneChangedSignature OnPhoneChanged;
|
||||
|
||||
/** Live wardrobe instances (off-body, mirrored from the save). */
|
||||
const TArray<TObjectPtr<UItemInstance>>& GetWardrobeItems();
|
||||
|
||||
/** The phone occupying the dedicated phone slot (§6.5 / §9.9), or nullptr if none is equipped. */
|
||||
UPhoneItemInstance* GetEquippedPhone();
|
||||
|
||||
/** Bring an item into the wardrobe (purchase, world-return). */
|
||||
void AddToWardrobe(UItemInstance* Item);
|
||||
|
||||
@@ -51,12 +60,18 @@ private:
|
||||
void StoreItem(UItemInstance* Item); // live list + save bucket, no broadcast
|
||||
void UnstoreItem(UItemInstance* Item); // live list + save bucket, no broadcast
|
||||
|
||||
void EquipPhone(UPhoneItemInstance* Phone); // hot-swaps the previous phone back to the wardrobe
|
||||
void UnequipPhone(UPhoneItemInstance* Phone); // stores the phone back in the wardrobe
|
||||
|
||||
UClothingManager* GetPlayerClothingManager() const;
|
||||
UGlobalSaveGameData* GetSave() const;
|
||||
|
||||
UPROPERTY()
|
||||
TArray<TObjectPtr<UItemInstance>> WardrobeItems;
|
||||
|
||||
UPROPERTY()
|
||||
TObjectPtr<UPhoneItemInstance> EquippedPhone;
|
||||
|
||||
// The save the live list was built from; re-hydrate when this changes (e.g. load game).
|
||||
TWeakObjectPtr<UGlobalSaveGameData> HydratedSave;
|
||||
};
|
||||
@@ -0,0 +1,11 @@
|
||||
// © 2025 Naked People Team. All Rights Reserved.
|
||||
|
||||
|
||||
#include "PhoneItemDefinition.h"
|
||||
|
||||
#include "PhoneItemInstance.h"
|
||||
|
||||
TSubclassOf<UItemInstance> UPhoneItemDefinition::GetInstanceClass() const
|
||||
{
|
||||
return UPhoneItemInstance::StaticClass();
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
// © 2025 Naked People Team. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "NakedDesire/Items/ItemDefinition.h"
|
||||
#include "PhoneItemDefinition.generated.h"
|
||||
|
||||
// Immutable definition for a phone (GDD §6.2 / §9.9). A phone is a regular UItemInstance item that
|
||||
// lives in the wardrobe / bag and occupies the dedicated phone slot when active (§6.5).
|
||||
// Phone-tier stats (camera / livestream / battery-capacity multipliers, §9.9) are deferred to Phase 8/9.
|
||||
UCLASS(BlueprintType)
|
||||
class NAKEDDESIRE_API UPhoneItemDefinition : public UItemDefinition
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
virtual TSubclassOf<UItemInstance> GetInstanceClass() const override;
|
||||
|
||||
UPROPERTY(EditDefaultsOnly, Category = "Phone")
|
||||
float MaxBattery = 100.0f;
|
||||
};
|
||||
@@ -0,0 +1,22 @@
|
||||
// © 2025 Naked People Team. All Rights Reserved.
|
||||
|
||||
|
||||
#include "PhoneItemInstance.h"
|
||||
|
||||
#include "PhoneItemDefinition.h"
|
||||
#include "StructUtils/InstancedStruct.h"
|
||||
|
||||
void UPhoneItemInstance::CaptureState(FInstancedStruct& OutState) const
|
||||
{
|
||||
FPhoneInstanceState PhoneState;
|
||||
PhoneState.CurrentBattery = CurrentBattery;
|
||||
OutState.InitializeAs<FPhoneInstanceState>(PhoneState);
|
||||
}
|
||||
|
||||
void UPhoneItemInstance::ApplyState(const FInstancedStruct& InState)
|
||||
{
|
||||
if (const FPhoneInstanceState* PhoneState = InState.GetPtr<FPhoneInstanceState>())
|
||||
{
|
||||
CurrentBattery = PhoneState->CurrentBattery;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
// © 2025 Naked People Team. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "PhoneItemDefinition.h"
|
||||
#include "NakedDesire/Items/ItemInstance.h"
|
||||
#include "PhoneItemInstance.generated.h"
|
||||
|
||||
/** Per-instance mutable state for a phone. Battery is per-phone-instance (§9.9). */
|
||||
USTRUCT()
|
||||
struct FPhoneInstanceState : public FItemInstanceState
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
UPROPERTY(SaveGame)
|
||||
float CurrentBattery = 100.0f;
|
||||
};
|
||||
|
||||
UCLASS(BlueprintType)
|
||||
class NAKEDDESIRE_API UPhoneItemInstance : public UItemInstance
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
// Charge of this specific phone, [0,1]. Drain / charge logic lands with the phone battery system (Phase 8/9).
|
||||
UPROPERTY(BlueprintReadOnly, Category = "Phone Item")
|
||||
float CurrentBattery = 100.0f;
|
||||
|
||||
UPhoneItemDefinition* GetPhoneItemDefinition() const { return Cast<UPhoneItemDefinition>(ItemDefinition); }
|
||||
|
||||
protected:
|
||||
virtual void CaptureState(FInstancedStruct& OutState) const override;
|
||||
virtual void ApplyState(const FInstancedStruct& InState) override;
|
||||
};
|
||||
@@ -62,7 +62,7 @@ void USaveSubsystem::PopulateStartingData(UGlobalSaveGameData* Save) const
|
||||
if (!GameInstance || !GameInstance->StartingSaveData)
|
||||
return;
|
||||
|
||||
for (UItemDefinition* Definition : GameInstance->StartingSaveData->StartingItems)
|
||||
for (UItemDefinition* Definition : GameInstance->StartingSaveData->EquippedItems)
|
||||
{
|
||||
if (!Definition)
|
||||
continue;
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include "Engine/DataAsset.h"
|
||||
#include "StartingSaveData.generated.h"
|
||||
|
||||
class UPhoneItemDefinition;
|
||||
class UItemDefinition;
|
||||
|
||||
UCLASS()
|
||||
@@ -15,5 +16,11 @@ class NAKEDDESIRE_API UStartingSaveData : public UPrimaryDataAsset
|
||||
|
||||
public:
|
||||
UPROPERTY(EditDefaultsOnly, Category = "Starting State")
|
||||
TArray<TObjectPtr<UItemDefinition>> StartingItems;
|
||||
TArray<TObjectPtr<UItemDefinition>> EquippedItems;
|
||||
|
||||
UPROPERTY(EditDefaultsOnly, Category = "Starting State")
|
||||
TArray<TObjectPtr<UItemDefinition>> WardrobeItems;
|
||||
|
||||
UPROPERTY(EditDefaultsOnly, Category = "Starting State")
|
||||
TObjectPtr<UPhoneItemDefinition> Phone;
|
||||
};
|
||||
Reference in New Issue
Block a user