LifePlay-Revival/Docs/Migration/etat/ST/STG/STG-C7a-VAL.md
edujiro5972 8084622450 LifePlay UE 5.8 : état de développement au 2026-10-19
Tickets : REP1-T4

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-19 22:57:42 +02:00

17 KiB
Raw Blame History

Fiche STG-C7a-VAL — Niveau N1 — Modèle prévu (agent lp-validator) | utilisé claude-sonnet-5-5 — Agent lp-validator — Skills sk-protocol, sk-recount, debug-verification, lp-validate

STG-C7a-VAL : joueur posé hors du siège qu'un Blueprint libère sans marche (2026-10-14)

VAL statique (sans UE, sans compilation). Aucune écriture hors de ce livrable. Copie D:\Game\LifePlay-master 5.8, branche ue5.8.

1. Fiches amont relues

  • Docs\Migration\etat\ST\STG\STG-C7a-IMPL.md (lu en entier, 97 lignes).
  • Docs\Migration\etat\ST\STG\STG-C7a.diff (lu en entier, 156 lignes).
  • Docs\Migration\etat\ST\STG\STG-COMMUN7-DIAG.md § A (motif : sections A.1 à A.6 repérées ; A.5 = C7-a).
  • Instantané : D:\Game\LifePlay_Backup_5.5\STG-C7a\Source\LifePlay\ + manifest.csv.
  • Pas de SPEC distincte : l'IMPL cite les gardes de Big Boss ; elles sont reprises dans la fiche VAL.

2. Points à confirmer des IMPL (IMPL § 5)

# Point (IMPL § 5) Résultat
1 Chambre 9, house viewing : ligne C7-a … devant a 80 cm, William sur la chaise, aucun CLOSE reporté (essai en jeu, build COMMUN-8)
2 D6 : s'asseoir puis ZQSD, le joueur se lève, aucune ligne C7-a reporté (essai) ; la logique est PASS-STATIC, § 3 point D6
3 Sexe depuis une chaise : aucune ligne C7-a, placement du couple inchangé reporté (essai) ; la logique est PASS-STATIC, § 3 point sexe
4 KO3c : comptoir du newsagent, sans régression reporté (essai) ; EscapeOverlaps / UpdateEscape absents du diff (§ 4)

3. Critères rejoués (commande → résultat brut)

3.1 MD5 (point 1 de la fiche) : PASS

Commande : md5sum sur les deux fichiers actuels puis sur l'instantané.

1a74d6b724bd63f04e04fda42682446d *Source/LifePlay/LPDirectControlSubsystem.cpp
519e23dc9eb2809fa36c1bbf942a7667 *Source/LifePlay/LPDirectControlSubsystem.h
44ace47d29424101b4127ba12e789beb *D:/Game/LifePlay_Backup_5.5/STG-C7a/Source/LifePlay/LPDirectControlSubsystem.cpp
acff924ae6e03548546ef3fb2e430eb8 *D:/Game/LifePlay_Backup_5.5/STG-C7a/Source/LifePlay/LPDirectControlSubsystem.h

manifest.csv : LPDirectControlSubsystem.cpp 48459 44ACE47D29424101B4127BA12E789BEB ; .h 3195 ACFF924AE6E03548546EF3FB2E430EB8. Actuels = IMPL § 6 (1A74D6B7… / 519E23DC…) ; instantané = 44ACE47D… / ACFF924A… : confirmé. Contrôle de périmètre : find Source Config -type f -newer manifest.csv ne rend que ces deux fichiers.

3.2 Diff régénéré (point 2) : PASS, un écart de comptage

Commande : diff -u <instantané> <actuel> pour .cpp et .h, lignes +/-/@ sans en-têtes, CR retirés, MD5 de l'ensemble, comparé au même filtre sur STG-C7a.diff :

ce0c484a4d6f96f625d405de216f20c0 *-      (régénéré)
ce0c484a4d6f96f625d405de216f20c0 *-      (STG-C7a.diff)

Identique. Comptes : grep -E '^\+' STG-C7a.diff | grep -vE '^\+\+\+' | wc -l = 117 ; ^- hors --- = 0. Écart E1 : l'IMPL § 2 annonce « 116 lignes ajoutées » ; le recompte donne 117 (la ligne vide après la fonction, diff l. 120, n'est pas comptée). Sans conséquence sur le code. Le diff ne contient que : appel dans Tick (1 ligne), fonction WatchSeatRelease (cpp), remise à vide dans Walk (4 lignes dont un commentaire), déclaration et 3 membres dans le .h. Rien d'autre que l'IMPL § 2. Le fichier .diff est en LF alors que les sources sont en CRLF : c'est un diff dont les CR sont retirés, il n'est pas appliqué tel quel (information, pas un écart).

3.3 Gardes, par lecture (point 3)

Lectures : LPDirectControlSubsystem.cpp l. 863-905 (Tick), 962-1070 (WatchSeatRelease), 1278-1363 (Walk), 147-151 (DirectActor), 167 (AxisValue), 465-469 (ScenePlaying), 529-533 (CapsuleInside), 567-600 (LeaveSeat), 604-622 (UpdateEscape). Résultat : PASS-STATIC pour chaque garde.

  • Sexe : ScenePlaying(GI) (lit GI.SexOn) est testée l. 976 à chaque tick, avant la pose de l'attente (l. 985) et avant l'action. Vrai : PendingSeat.Reset(), PendingSeatTime = -1, retour. LastSeat est mis à jour avant ce retour (l. 975), donc une libération pendant le sexe ne devient jamais « libérée » au tick suivant. Attente annulée aussi si SexOn apparaît dans les 0,5 s.
  • Marche : l. 1010, les axes MoveForward et MoveRight (AxisValue, mêmes constantes que Walk l. 1321-1322) doivent être nuls et Velocity.Size2D() <= 1.0, sinon retour sans action. L'attente est de toute façon consommée avant (l. 998 : PendingSeatTime = -1), donc aucune reprise ultérieure.
  • D6 : LeaveSeat n'est appelé qu'à Walk l. 1350 (Grep : une seule occurrence d'appel). Ordre dans Tick : WatchSeatRelease l. 873, Walk l. 901 (seule branche else, hors menu et hors SexOn). Au tick où LeaveSeat a lieu, WatchSeatRelease (déjà passé) a vu OccupyingFnt valide : bSeated, aucune libération détectée. Juste après LeaveSeat, l. 1352-1354 remettent LastSeat, PendingSeat et PendingSeatTime à vide. Au tick suivant, OccupyingFnt est nul mais LastSeat est vide : bWasSeated faux, Released nul, PendingSeatTime < 0 : retour. Une place quittée en marchant ne déclenche donc jamais C7-a, y compris au tick du LeaveSeat. Cas voisin : si un Blueprint libère la place et qu'une touche est tenue au même tick, WatchSeatRelease met l'attente (l. 985) puis Walk la remet à vide (l. 1352-1354) : conforme. Cas où LeaveSeat échoue (fonction introuvable, l. 582-590) : OccupyingFnt reste valide, LastSeat est réarmé au tick suivant, pas de libération vue : pas d'action.
  • Hors meuble : pour chaque point, balayage de la capsule (ses réponses, Capsule->GetCollisionResponseToChannels()) de +30 cm à -demi-hauteur, rejet si départ enfoncé ou sol à plus de 30 cm de la hauteur actuelle (l. 1032-1036) ; puis OverlapBlockingTestByChannel avec la capsule réduite de 2 cm (l. 1038) : tout recouvrement bloquant rejette le point. Aussi, avant tout, CapsuleInside(Player, Furniture) (l. 1006) : si le joueur n'est plus dans le meuble, aucune action.
  • Hors personne : boucle TActorIterator<ACharacter> (l. 1044-1051) : rejet si un autre personnage non caché est à moins de 50 + 2 × rayon (distance 2D entre centres). L'approximation « bord à bord = 50 cm » suppose des rayons égaux. Les personnages cachés ne comptent pas par ce test (le test de recouvrement bloquant les voit s'ils bloquent le Pawn).
  • Ligne au journal, deux issues : succès l. 1056-1057 (C7-a : place %s liberee sans marche, joueur pose %s a %.0f cm du siege) ; échec des 4 points l. 1060-1061 (… aucun point libre a %.0f cm, joueur laisse en place), le joueur n'est pas déplacé. Les sorties anticipées « pièce changée / meuble détruit / déjà sorti / marche » (l. 1007-1013) n'écrivent pas de ligne (voir risque R5).
  • Changement de pièce / meuble détruit : Furniture est un TWeakObjectPtr : détruit, Get() rend nul et le test !Furniture (l. 1006) sort. Pièce changée : Furniture->GetOwner() != Room sort. Meuble détruit pendant que le joueur est assis : IsValid(Seat) faux, LastSeat.IsValid() faux, pas de « libération ». Aucun déplacement dans ces cas.

3.4 Logique de l'attente (point 4) : PASS-STATIC

  • Valide → nul : bWasSeated && !bSeated (l. 973-974) donne Released ; Released non nul : PendingSeat = Released, PendingSeatTime = 0, retour (l. 985-987). Si la valeur précédente n'est pas un UPrimitiveComponent (Cast nul), Released est nul, rien n'est mis en attente (fonctionnellement : ne réagit que pour les composants de Room, ce qui est attendu pour OccupyingFnt).
  • Annulation : rassis (bSeated), SexOn, ou pas de joueur : l. 976-981, attente remise à -1.
  • Délai 0,5 s : PendingSeatTime += DeltaTime, < SeatReleaseDelay (0.5f) : retour ; sinon action.
  • PendingSeatTime >= 0 ne reste jamais indéfiniment : chaque tick, soit l. 976-981 (remise à -1), soit incrément jusqu'à 0,5 s puis -1 (l. 998) avant tous les tests de garde. Seul risque théorique : DeltaTime nul en continu (le Ticker du sous-système est à intervalle 0, le delta est positif) ; sans effet nuisible de toute façon (aucune action sans le délai).
  • Une action par libération : PendingSeatTime = -1 et PendingSeat.Reset() sont posés (l. 998-1000) avant l'action ; il n'y a pas d'autre moyen de réarmer que la transition valide → nul suivante. Pas de déplacement répété.

3.5 Compilabilité par lecture (point 5) : PASS-STATIC (non compilé)

Signatures relues dans UE 5.8 (D:\Game\Launcher\Engine\UE_5.8\Engine\Source\Runtime\…) :

Élément Source lue Constat
UWorld::OverlapBlockingTestByChannel(const FVector&, const FQuat&, ECollisionChannel, const FCollisionShape&, const FCollisionQueryParams&, const FCollisionResponseParams&) Engine\Classes\Engine\World.h:2364 appel conforme (Pos, Rot, canal, forme, Query, Responses)
UWorld::SweepSingleByChannel(FHitResult&, Start, End, FQuat, ECollisionChannel, Shape, Params, ResponseParam) World.h:2286 conforme
UCapsuleComponent::GetCollisionShape(float Inflation = 0.0f) Engine\Classes\Components\CapsuleComponent.h:86 GetCollisionShape() et (-2.f) valides
GetCollisionResponseToChannels() -> const FCollisionResponseContainer& PrimitiveComponent.h:2647 valide
GetCollisionObjectType() -> ECollisionChannel PrimitiveComponent.h:2639 passe comme canal
FCollisionResponseParams(const FCollisionResponseContainer&), non explicite Engine\Public\CollisionQueryParams.h:336 construction par Responses(…) valide
UE_ARRAY_COUNT Core\Public\Templates\UnrealTemplate.h:222 int32 < UE_ARRAY_COUNT : même forme déjà compilée (LPKeyboardSubsystem.cpp:108, LPHairstyleSubsystem.cpp:619)
FRotationMatrix::GetUnitAxis déjà employé Walk l. 1356-1357 (même fichier) valide
SetActorLocation(Target, false, nullptr, ETeleportType::TeleportPhysics) signature d'AActor (Vector, bSweep, OutHit, Teleport) conforme
TWeakObjectPtr<UObject> = UObject* et = nullptr Core\Public\UObject\WeakObjectPtrTemplates.h:106 (operator=(U*)) et :49 le ternaire bSeated ? Seat : nullptr a le type UObject* : valide
Reset(), Get(), IsValid() sur TWeakObjectPtr WeakObjectPtrTemplates.h:93, 141 ; IsValid() hérité de FWeakObjectPtr (WeakObjectPtr.h:300) valides
Cast<UPrimitiveComponent>(LastSeat.Get()) Get() rend UObject* valide
AActor::IsHidden() const Engine\Classes\GameFramework\Actor.h:4523 valide
AxisValue(const APlayerController*, FName) LPDirectControlSubsystem.cpp:167 ; PC déclaré const APlayerController* (l. 1002 du diff : const APlayerController* PC) constance respectée
Fonctions du namespace anonyme DirectActor l. 147, AxisValue l. 167, ScenePlaying l. 465, CapsuleInside l. 529, constantes ForwardAxis/RightAxis l. 54-55 toutes définies avant WatchSeatRelease (l. 962)
Inclusions EngineUtils.h (l. 29), Components/CapsuleComponent.h (l. 20), GameFramework/Character.h (l. 32), Engine/World.h (l. 28) présentes
Collisions de noms (unity) Grep Source\ : WatchSeatRelease, LastSeat, PendingSeat*, LPSeatRelease n'existent que dans LPDirectControlSubsystem.cpp/.h ; SCENE_QUERY_STAT des autres fichiers emploie d'autres noms (LPCameraOcclusion, LPCameraWitness, LPPairSpot, WalkCamera) aucune collision
Noms locaux vs membres du .h Grep des noms locaux (Location, Capsule, Furniture, Query, Shape, Inner, Names, Offsets, …) dans le .h : aucun membre du même nom pas de masquage

Non établi : la compilation elle-même (aucun avertissement C4018 / C4458 ne peut être exclu sans build ; le motif int32 < UE_ARRAY_COUNT est déjà employé dans trois fichiers compilés).

3.6 Fins de ligne, ASCII, IMPL (point 6) : PASS

Commandes : tr -cd '\n', tr -cd '\r' (Git Bash), et comptage des octets par PowerShell ([IO.File]::ReadAllBytes) ; git ls-files --eol ; grep -nP '[^\x00-\x7F]'.

Source/LifePlay/LPDirectControlSubsystem.cpp LF=1594 CR=1594 ; CRLF=1594 LFseul=0 CRseul=0 ; i/crlf w/crlf
Source/LifePlay/LPDirectControlSubsystem.h   LF=96   CR=96   ; CRLF=96   LFseul=0 CRseul=0 ; i/crlf w/crlf
Docs/…/STG-C7a-IMPL.md                       LF=97   CR=0
Docs/…/STG-C7a.diff                          LF=156  CR=0

Aucun BOM (premiers octets 2f 2f 20 / 23 20 46 / 2d 2d 2d). Non-ASCII dans le C++ : 2 lignes (cpp l. 454 « et l. 513 §), toutes deux dans l'instantané (même grep sur la copie d'avant, mêmes lignes) ; non-ASCII dans les lignes ajoutées : diff -u … | grep -P '^[-+].*[^\x00-\x7F]' | wc -l = 0. IMPL : en LF ; en-tête complet à la forme de sk-protocol § 4 (Fiche, Niveau, Modèle prévu | utilisé, Agent, Skills) ; grep -c 'C:\\Users' = 0. Écart E2 (forme) : l'IMPL ne suit pas le gabarit IMPL de lp-doc/gabarits.md (sections 1 « Fichiers modifiés », 2 « Écarts à la SPEC », 3 « Instantané », 4 « Ce qui n'a pas été fait », 5 « Points à confirmer en VAL », 6 « Fichiers à commiter » : ici l'instantané est § 1, les changements § 2, une section « D6, KO3c » § 3, pas de section « Écarts à la SPEC », « MD5 après » et « Retour arrière » en plus). Le contenu demandé est présent ; la section « Écarts à la SPEC » est absente (il n'y a pas de SPEC distincte).

4. Diff lu en entier

  • Fichiers : 2 (LPDirectControlSubsystem.cpp, LPDirectControlSubsystem.h), 4 blocs (3 dans le .cpp, 2 dans le .h).
  • Lignes : +117, -0 (recompté ; l'IMPL dit 116, écart E1).
  • LeaveSeat, EscapeOverlaps, UpdateEscape, CapsuleInside, ScenePlaying : absents du diff (inchangés).
  • Ajout non demandé : aucun. Les seules lignes touchant du code existant sont l'appel de Tick (1 ligne) et les 3 remises à vide de Walk (après LeaveSeat).

5. Verdict

PASS-STATIC. Tous les points de la fiche sont établis par lecture ou par commande : MD5, diff, gardes, attente, signatures moteur, fins de ligne. Rien n'est compilé ni exécuté : les 4 essais de l'IMPL § 5 sont reportés au build COMMUN-8. Écarts : E1 (117 lignes ajoutées et non 116), E2 (IMPL hors gabarit). Aucun écart bloquant.

6. Points à confirmer par l'utilisateur (cases) et risques

Risques relevés (aucun ne bloque la VAL statique) :

  • R1 — Blueprint qui libère puis réassoit le joueur après plus de 0,5 s. Le joueur est déplacé à 80 cm au bout de 0,5 s, puis le Blueprint le réassoit (téléportation visible, peut-être déplacement à la chaise). À lire dans le journal du build COMMUN-8 : une ligne C7-a suivie d'un nouvel OccupyingFnt valide.
  • R2 — Scène où le joueur doit rester exactement sur sa chaise (caméra, animation d'assise) : la pose à 80 cm s'applique quand même si le Blueprint libère sans sexe ni marche. Le sexe est exclu par SexOn seul : une scène de dialogue sans SexOn n'est pas exclue.
  • R3 — Action « se lever » du menu (PollInteract, hors marche) : si elle appelle unOccupyFnt, le joueur reste immobile, dans le meuble, puis est posé à 80 cm après 0,5 s. Probablement voulu ; à confirmer.
  • R4 — Pas de contrôle de ligne de vue entre la place et le point (balayage vertical seulement) : un point à 80 cm derrière une cloison ou un meuble mince peut être retenu (pièce voisine, extérieur). Le test de recouvrement garantit seulement que la capsule d'arrivée est libre. Non couvert par l'essai 1 de l'IMPL.
  • R5 — Silence au journal pour les sorties anticipées (joueur déjà hors du meuble, pièce changée, marche en cours) : la consigne « une ligne au journal » est tenue pour les deux issues de l'action (pose ou aucun point libre), pas pour l'abandon avant. Confirmer que c'est le sens voulu par Big Boss.
  • R6 — Capsule déjà hors meuble : aucune action, cas A.6 du DIAG non traité (IMPL § 4). Confirmé par lecture (l. 1006 CapsuleInside).
  • R7 — Volumes en Ignore (KO3c) : si un composant est en Pawn Ignore (liste EscapeVolumes) au moment de l'arrivée, le test de recouvrement ne le voit pas ; le point peut être dans un volume que UpdateEscape rendra Block. Cas rare.
  • R8 — Personnes cachées (IsHidden) ignorées par le test « personne » ; couvertes seulement par le test de recouvrement.
  • R9 — Essais 1 à 4 de l'IMPL § 5 à rejouer au build COMMUN-8 ; en particulier le cas house_viewing (distance de William, règle des 10 cm).

Fichiers écrits par cette VAL : D:\Game\LifePlay-master 5.8\Docs\Migration\etat\ST\STG\STG-C7a-VAL.md. Fichiers à commiter : A Docs/Migration/etat/ST/STG/STG-C7a-VAL.md (dépôt du jeu, copie 5.8). Aucun autre fichier modifié.

Tours : 13 / 30