Setup NPC director

This commit is contained in:
2026-06-01 16:26:15 +03:00
parent f63c98d5d7
commit 192bd94bb7
17 changed files with 590 additions and 50 deletions
+3 -2
View File
@@ -51,7 +51,7 @@ Each `VS-n` is a vertical task with a concrete exit check. VS-1 is the gate —
- **VS-2 — Lust + Pulse (minimal).** Add `Lust`/`MaxLust` and `Pulse`/baseline to `StatsManager`. Passive lust gain; pulse rises on run / exposure events / observed-while-exposed and decays at rest; pulse multiplies embarrassment **and** lust gain (§7.5). Masturbate quick-action resets lust. **Gating (§23 #25): home masturbation is always available; in-session masturbation is Slut-path-gated.** For the slice the in-session entry can ship open if path investment isn't wired yet, but keep the home-vs-session split in mind so it's a config flip later, not a rewrite. **Exit:** the three attributes visibly interact in a playtest — running spikes pulse, which accelerates embarrassment gain; masturbating drops lust.
- **VS-3 — Expose action.** Radial-menu entry per equipped garment exposing the parts in its `CanExpose`; temporarily counts those parts as uncovered for VS-1 math; blocked when another garment covers the same part (§6.3.6). Reuses the existing `RadialMenu` + `ClothingManager`. **Exit:** flashing a coat in front of an NPC produces an embarrassment spike that ends when the expose ends.
- **VS-4 — Commission accept + instant reward.** Add the Accept lifecycle (commit on accept, no penalty when un-accepted) and **instant reward crediting on `OnMissionCompleted`** — money to the save, XP to the shared pool, followers stub (§13.1/§13.2/§23 #23). `MissionsManager` currently completes a mission but credits nothing (`MissionsManager.cpp:27-32`) — wire crediting here. Ensure the typed goals the slice needs exist (`BeFullyNaked`, `BeFullyNakedNearNPCs`, `ExposeBodyPart` — partially covered by `FlashGoal`/`ExposeBodyPartRestriction`). Surface it through a **minimal forum/board widget** (accept + active-objective tracker) reusing the existing UI layer. **Exit:** accept a commission, satisfy it in the world, see money/XP/followers tick up at the moment of completion with no return-home step.
- **VS-5 — NPC types + the session-loss threat.** Differentiate at least **Walker / Stalker / Blogger** by observation weight + behavior (Stalker = sustained stare, high embarrassment; Blogger = photo → recognition stub), plus a **Snitch → Police** beat that drives a real escape: Snitch report sets a chase, Police capture routes through the existing `SessionLossResolver` (`PoliceCapture`, `bPoliceChaseActive`). This is what makes the street a risk space (Pillar 1). **Exit:** the three civilian types feel distinct; a Snitch can trigger a police chase that ends in a §4.4 capture resolution.
- **VS-5 — NPC types + the session-loss threat. IN PROGRESS.** Differentiate at least **Walker / Stalker / Blogger** by observation weight + behavior (Stalker = sustained stare, high embarrassment; Blogger = photo → recognition stub), plus a **Snitch → Police** beat that drives a real escape: Snitch report sets a chase, Police capture routes through the existing `SessionLossResolver` (`PoliceCapture`, `bPoliceChaseActive`). This is what makes the street a risk space (Pillar 1). **Done:** the data-driven NPC type model (`ENPCType`, `UNPCTypeDefinition`, `ANPC` accessors) and the Walker/Stalker mechanical split via per-observer observation weight in `StatsManager` (see §1.3). **Remaining:** author the `DA_Walker`/`DA_Stalker` assets + BT branch on `ShouldStopToObserve`; then Blogger (recognition stub) and the Snitch→Police chase. **Exit:** the three civilian types feel distinct; a Snitch can trigger a police chase that ends in a §4.4 capture resolution.
- **VS-6 — Day loop.** A lightweight `TimeOfDaySubsystem` (or formalize the BP-driven `GameMode::OnHourChanged`): day phase 08:0020:00, NPC density gated by phase (fix the `0921` window in `NPCSpawner.cpp:40` and the `Hour==4` day-roll in `GameMode.cpp:54`). Sleep at the apartment bed advances the day, restores energy, and autosaves. Rent stays stubbed off. **Exit:** play a full day, sleep, wake on the next day with state intact.
- **VS-7 — Save round-trip across the loop (gate).** Validate equip/unequip, world drop, wardrobe, money, day index, and attribute snapshot survive save → quit → reload, exercising the loop end-to-end (this is the existing Phase 1 exit criterion, re-run against the live loop). **Exit:** §6.6 inventory table holds after each session-loss cause, and a mid-run reload reproduces the player's exact state.
@@ -121,7 +121,8 @@ State of the C++ module as of the latest pass. File references use `Source/Naked
- **`PhoneSubsystem` (§17.1)** — tickable subsystem owning phone state (battery %, active app, livestream session lifecycle, charger interaction). Does not yet exist.
- **Forum (§13)** — no `UCommissionTemplate`, no procedural generation, no weekly missions distinct from daily, no profile (incl. weekly follower-income summary), no posting photos. Forum scope is locked minimal (board + own profile only, no threads / no other-users feed) — that's a non-build, but worth recording.
- **Photo & livestream** — absent.
- **NPC types (§10.2)** — only one generic `ANPC` class. No Walker / Stalker / Blogger / Snitch / Harasser, no Police, no Wanted-poster mechanic.
- **NPC types (§10.2) — data model + Walker/Stalker landed (VS-5, partial).** `NPC/NPCType.h` defines the full `ENPCType` vocabulary (`Walker, Stalker, Blogger, Snitch, Harasser, Police`). `NPC/NPCTypeDefinition` (`UPrimaryDataAsset`, §17.4) carries `Type`, `ObservationWeight`, `bStopsToObserve`, `ObserveDurationSeconds`, `ReactionMontage`. `ANPC` holds a `NPCTypeDefinition` and exposes BlueprintPure `GetNPCType`/`GetObservationWeight`/`ShouldStopToObserve`/`GetObserveDuration` (Walker-ish fallbacks when null). The mechanical Walker/Stalker split is live: `UStatsManager::SetObserved` now takes a per-observer `Weight`, stored on the `FObserverEntry` and multiplied into `ComputeObservedExposureRate` so a Stalker's stare contributes more embarrassment than a Walker's glance; `ANPCAIController` reads the weight from the possessed `ANPC`'s type. **Still BP / later phases:** the BT branch on `ShouldStopToObserve` (Walker walks past vs Stalker stops & stares), the `DA_Walker`/`DA_Stalker` assets + BP variants, reaction montages, and the remaining types — Blogger needs Recognition (§7.6), Snitch/Police need wanted + chase (§10.3), Harasser needs the grope/evade beat. No Wanted-poster mechanic.
- **NPC crowd director (§17.1 `NPCManager`, §19) — landed.** `NPC/NPCDirectorSubsystem` (`UWorldSubsystem`, mirrors `UMissionSubsystem`) is the single authority for the live crowd around the player. **Mesh-agnostic** (NPCs will be lightweight skeletal meshes, **no MetaHumans / no motion matching** — decided 2026-06-01). On a light timer (`UpdateInterval`, default 0.5s) it reconciles population: recycles NPCs past `DespawnRadius`, trims to target when the phase target drops, and activates pooled NPCs at `UNavigationSystemV1::GetRandomReachablePointInRadius` points in the `[SpawnRadiusMin, SpawnRadiusMax]` ring (rejection-sampled outside the inner radius). Density target is day/night-driven via `UTimeOfDaySubsystem::OnPhaseChanged` (`TargetCountDay`/`TargetCountNight`). **Pooling:** `MaxNPCs` actors are prewarmed from `UNPCDirectorConfig::SpawnTable` (weighted `TSubclassOf<ANPC>` entries → composition) and recycled, never destroyed — no spawn hitch / GC churn. Config is `UNPCDirectorConfig` (`UPrimaryDataAsset`) on `UNakedDesireGameInstance::NPCDirector`. `ANPC` gained `ActivateFromPool`/`DeactivateToPool` (transform/visibility/collision/movement + `OnActivatedFromPool`/`OnDeactivatedToPool` BlueprintImplementableEvents) and crowd anim hygiene (`VisibilityBasedAnimTickOption = OnlyTickPoseWhenRendered` + URO). `ANPCAIController::ClearObservation` drops observation when an NPC is pooled out. **BP contract / follow-ups:** author `BP_NPC_*` classes (mesh + `UNPCTypeDefinition`) + a `DA_NPCDirector` config and assign it on the GI; the NPC BT must **start in `OnActivatedFromPool` and stop in `OnDeactivatedToPool`** (not on possess) so pooled NPCs don't tick AI; **remove `BPA_NPCSpawner` from the level** — the director replaces it. Liveliness behaviors (wander/destinations, idle-at-POI, reaction montages) remain BP, director-assisted. Background/instanced crowd layer deferred (handful density target).
- **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. 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.