LifePlay-Revival/Docs/Migration/etat/REL/KEY2/KEY2-IMPL-01.md
edujiro5972 56dfcfc3d2 LifePlay UE 5.8 : état de développement au 2026-10-20
Tickets : PUB-20261020-T1

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-20 17:35:10 +02:00

302 lines
29 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

Fiche KEY2-IMPL-01 — Niveau N3 — Modèle prévu non précisé par la fiche (N3) | utilisé Opus 5.5 (claude-opus-5-5) — Agent A-IMPL-H (lp-impl) — Skills sk-protocol, ue-cpp-build, lp-build, lp-ue-compat (préchargés) ; sk-change-safety demandé par la fiche mais absent du contexte (instantané déjà pris par l'orchestratrice)
# KEY2-IMPL-01 : panneau de touches K2, K5, K6, migration Version=2 et consommateurs C++ (2026-10-20)
Ordre : GO de Big Boss, fiche transmise par l'orchestratrice. Hors copie UE : aucune compilation, aucun éditeur, aucun lancement. SPEC faisant foi : `KEY2-SPEC-01.md` révision 7 (§ 1 décisions 1 à 14 ; § 4, 5, 5b, 7, 8, 8b, 8c, 9, 10) et `KEY2-CART-02.md` § 2 à 5 et § 10.
Niveaux de preuve : **établi** (fichier:ligne relu ou commande rejouée) ; **estimé** (déduit, à éprouver au build ou en jeu).
## 1. Fichiers modifiés
### 1.1 Empreintes et fins de ligne
Commande : `md5sum` sur les 5 fichiers avant toute écriture, puis après ; référence = `scratchpad\key2_base` (copie de l'orchestratrice, MD5 identiques à la copie de travail au départ, établi). MD5 de départ de `LPCameraModeSubsystem.cpp` attendu par la fiche : `406fbf1d74eeff5b7af95998eaf2accf`, relevé identique (établi) : écriture autorisée.
| Fichier | MD5 avant | MD5 après | Lignes avant → après | Fins de ligne (CR comptés par `tr -cd '\r' \| wc -c`) |
|---|---|---|---|---|
| `Config/DefaultInput.ini` | `20952b1fe933b283a414975f79a34317` | `1a8e89a1ac4acc462280e654ffe4ffd8` | 171 → 169 | CRLF gardé (169 CR / 169 lignes) |
| `Source/LifePlay/LPKeyboardSubsystem.h` | `eed95e9860b26c5a7327ce4e3f90d8c8` | `06793e5749102b5a943ee8bbc8a1f625` | 68 → 86 | CRLF gardé (86 / 86) |
| `Source/LifePlay/LPKeyboardSubsystem.cpp` | `5d60d3f8ffe721698820fe68e55f938c` | `94575a8eef58548484844ea3070a358e` | 958 → 1463 | CRLF gardé (1463 / 1463) |
| `Source/LifePlay/LPCameraModeSubsystem.h` | `9aee8d04cf35c0c6eedf226e3b0a86c3` | `36feac2a77e13529a8ea689be74c954f` | 206 → 214 | LF gardé (0 CR) |
| `Source/LifePlay/LPCameraModeSubsystem.cpp` | `406fbf1d74eeff5b7af95998eaf2accf` | `cd6427aa03bc6c530cfe7748eee2a4bb` | 2275 → 2380 | LF gardé (0 CR) |
`git diff --no-index --numstat` contre la référence (ajouts, retraits) : DefaultInput.ini 5 / 7 ; LPKeyboardSubsystem.h 19 / 1 ; LPKeyboardSubsystem.cpp 519 / 14 ; LPCameraModeSubsystem.h 8 / 0 ; LPCameraModeSubsystem.cpp 105 / 0 (`105 insertions(+)`, aucun retrait).
Toutes les écritures par l'outil Edit (aucun script, aucun `sed -i`).
### 1.2 `Config/DefaultInput.ini` (SPEC § 9 ; § 4, § 5, § 5b, § 8b)
- Retirées (5 + 2) : `SpeedUp NumPadSix`, `SpeedDown NumPadFour` (anciennes l. 113-114), `TestScenes Tilde` (l. 121), `SpeedUp Equals`, `SpeedDown Hyphen` (l. 123-124), `MoveForward Z` (l. 155), `MoveRight Q −1` (l. 158).
- Ajoutées : `+ActionMappings=(ActionName="ActionPanel",…,Key=Q)` après `Interact` ; `+AxisMappings` `CameraHeight` G (+1) / B (−1) et `CoupleMoveY` V (+1) / X (−1) après `PenisRotation`.
- Gardées : `SpeedUp Add`, `SpeedDown Subtract`, `Favorite4/6` (Four, Six, NumPadFour, NumPadSix), `TestScenes Insert`, `ConsoleKeys`. Résultat : déplacement W S A D seul (préréglage QWERTY, décision 12).
### 1.3 `Source/LifePlay/LPKeyboardSubsystem.h`
- Publics ajoutés (l. 40-50) : `ApplyLayout(const FString&)` (§ 4), `RefuseLeftClick()` et `CommitWheel(...)` (§ 7), `IsCoupleDragByKeys()` (accesseur lu par LPCameraModeSubsystem) et `SetCoupleDragByKeys(bool)` (§ 8b), `GetLayout()`.
- Privés : `LoadSaved()` n'est plus `const` (il lit et migre la section) ; `ApplyPreset(bool, TArray<FString>&)` (l. 64-65) ; `IdleStatus()` ; membres `Layout` (défaut `QWERTY`) et `bCoupleDragKeys` (l. 83-85).
### 1.4 `Source/LifePlay/LPKeyboardSubsystem.cpp`
| § SPEC | Lieu (lignes après) | Changement |
|---|---|---|
| § 4, § 5 | l. 36-74 (espace anonyme) | constantes `Kb*` : section `LPKeyboard`, `QWERTY` / `AZERTY` / `Custom`, `KbVersion = 2`, noms d'actions et d'axes ; `KbIsMoveGroup`, `KbLayoutPreset` (QWERTY : W +1, S −1, A −1, D +1, ActionPanel Q ; AZERTY : Z, S, Q, D, ActionPanel A ; Interact non touché), `KbGameReadKey` (LeftShift, RightShift, LeftControl, RightControl, F12, PageDown, Y, N, LeftMouseButton, RightMouseButton) |
| § 8 | `ActionLabel`, `AxisLabel` | « Action Panel » ; « Camera Height Up / Down » ; « Couple Right / Left » |
| § 8 | l. 198-249 `FBindGroup`, `GroupFor` | champs `Sub` et `SubTitle` ; section « Scene » (ordre 8) en 4 blocs : « Camera » (LeftRight, DownUp, ScrollDownUp, RotateCamera, SwitchCamera, CameraHeight), « Characters » (ManualAnimating, l'axe de rotation existant, CoupleMoveY), « Actions » (ActionPanel), « Other » (TestScenes, Recommended) |
| § 7, 4 | `ChordText` | paramètre `bCmd`, préfixe « Cmd+ » ; appel de la ligne de touche mis à jour |
| § 8 | l. 284-299 `KbAddMissing` ; l. 479-484 | ligne « (no key) » avec Add pour ActionPanel, CameraHeight ±, CoupleMoveY ± quand le fichier du joueur ne les a pas (écart E1) |
| § 4 | l. 325-328, l. 352-388, l. 615-620, l. 686-689 | `SComboBox<TSharedPtr<FString>>` (QWERTY, AZERTY), bouton « Apply », texte « Layout: QWERTY / AZERTY / custom » en tête du panneau |
| § 8, § 8b | l. 505-531 (Rebuild), l. 645-672 | tri par groupe puis bloc ; sous-titres de bloc ; option « Move couple with » (bouton « Mouse and arrows » / « Arrows only ») en tête du bloc « Characters » |
| § 7, 1-3 | l. 703-804 (`FKeyboardProcessor`) | modificateur seul retenu à l'appui (hors répétition) et enregistré au relâchement (`HandleKeyUpEvent`) si rien d'autre n'a été pressé ; `HandleMouseButtonDownEvent` : droit, milieu, Thumb1, Thumb2 seuls, gauche seulement avec Shift / Ctrl / Alt / Cmd, sinon `RefuseLeftClick` ; `HandleMouseWheelOrGestureEvent` : `CommitWheel` (MouseScrollUp / MouseScrollDown) |
| § 4, § 7, 5 | l. 1001-1040 (`CommitKey`) | groupe de déplacement touché (axe remplacé ou touche retirée d'un axe de déplacement) → `Layout = Custom` ; avertissement « Also used by the game: <touche>. » sans refus |
| § 4 | l. 1059 (`RemoveBinding`) | retrait d'une liaison de déplacement → `Custom` |
| § 4 | l. 1067-1144 | `ApplyPreset` (étapes 1 à 3 du § 4 : retire MoveForward, MoveRight, ActionPanel ; retire la même touche sans modificateur des autres actions et axes, avec annonce ; ajoute les 5 liaisons) ; `ApplyLayout` (+ `Layout`, `ApplyToPlayer` qui enregistre, ligne d'état, ligne `LogLifePlay`) |
| § 7 | l. 1146-1161 | `RefuseLeftClick` (message, la capture continue) ; `CommitWheel` (refus pour un axe : les axes gardent `MouseWheelAxis`) |
| § 8b | l. 1163-1172 | `SetCoupleDragByKeys` (enregistre aussitôt) |
| § 4 | l. 1174-1187, appels dans `TakeEmbeddedPage` / `OpenKeyboardMenu` | `IdleStatus` : « Action Panel has no key: choose a layout and press Apply, or use Add. » si ActionPanel est sans touche |
| § 4, décision 12 | l. 1252-1263 (`ResetToDefault`) | après DefaultInput, réapplique AZERTY si `Layout=AZERTY` (gardé) ; sinon `Layout = QWERTY` |
| § 5, § 5b | l. 1306-1366, l. 1394-1398 (`LoadSaved`) | lit `Layout`, `CoupleDrag`, `Version` ; si `Version` < 2 (absente = 0) : retire les 5 liaisons exactes (SpeedUp NumPadSix, SpeedDown NumPadFour, SpeedUp Equals, SpeedDown Hyphen, TestScenes Tilde, modificateurs tous faux), ajoute ActionPanel=Q seulement si Q n'est lié à rien (actions et axes du fichier) et si ActionPanel n'y est pas, `Layout = Custom`, une ligne `LogLifePlay` avec le compte, puis réécrit le fichier |
| § 4, § 5, § 8b | l. 1430-1433 (`SaveSaved`) | section `[LPKeyboard]` : `Layout`, `Version=2`, `CoupleDrag=Mouse|Keys` |
### 1.5 `Source/LifePlay/LPCameraModeSubsystem.h` (gel CAM2 : 8 lignes ajoutées en fin de classe, rien d'autre)
Déclaration de `TickSceneKeys(APlayerController*, AActor* View, const UObject* Room, const TArray<AActor*>& Couple, bool bGrabbing, float Step)` et membres `SceneCameraLift`, `bLiftC3Was` (l. 206-213).
### 1.6 `Source/LifePlay/LPCameraModeSubsystem.cpp` (gel CAM2 : ajouts seulement, aucun bloc existant modifié ni déplacé)
Blocs ajoutés : constantes KEY2 (l. 208-220 : `CameraHeightSpeed = 100` cm/s, borne relative `[-40, 150]`, `ECoupleMoveSpace` + constante unique `CoupleMoveSpace = World`, pas `CoupleMoveYStep = 5`) ; aide `ActionJustPressed` (l. 312-334, modificateurs exacts comme `InteractPressed`) ; un appel dans `TickSexCamera` (l. 1248-1249, en scène, hors menu, après `TickCoupleMouse`) ; fonction `TickSceneKeys` (l. 1344-1400) ; garde « Arrows only » dans `TickCoupleMouse` (l. 1627-1633, clic gauche seulement). Aides existantes réutilisées : `AxisValue`, `ReadObject`, `ReadBool`, `MappedKeys`, `PCLeftMouseSexOn`, `RoomSceneCamera`, `PCSexC3`, `LastSexView` ; garde de menu = le `return` existant de `TickSexCamera` sur `bMenuOnScreen`.
Diff unifié complet contre la référence (`diff -u` ; code de sortie 1 = différences ; 0 ligne `-`) :
```diff
--- key2_base/LPCameraModeSubsystem.cpp
+++ Source/LifePlay/LPCameraModeSubsystem.cpp
@@ -205,6 +205,20 @@
const float HelpIconSize = 28.f;
const int32 HelpZOrder = 60;
+ // KEY2 (KEY2-SPEC-01 § 6, § 8b, § 8c) : touche d'action (meme evenement que G-g), hauteur de la camera de scene,
+ // couple sur Y. Actions et axes de DefaultInput.ini, remappables dans le menu Clavier.
+ const FName ActionPanelAction(TEXT("ActionPanel"));
+ const FName AxisCameraHeight(TEXT("CameraHeight"));
+ const FName AxisCoupleMoveY(TEXT("CoupleMoveY"));
+ const float CameraHeightSpeed = 100.f; // cm par seconde (a regler a l'essai)
+ // Hauteur ajoutee bornee comme C2Height de Room ([-40, 150], KEY2-CART-02 § 5), depuis le dernier recadrage.
+ const float CameraHeightMin = -40.f;
+ const float CameraHeightMax = 150.f;
+ // Repere de CoupleMoveY (Q12, decision 14) : World = Y du monde (comme le X de PC), Couple = droite du premier acteur.
+ enum class ECoupleMoveSpace : uint8 { World, Couple };
+ const ECoupleMoveSpace CoupleMoveSpace = ECoupleMoveSpace::World;
+ const float CoupleMoveYStep = 5.f; // par unite d'axe et par image, comme le X de PC (KEY2-CART-02 § 5)
+
// Copie de LPDirectControlSubsystem.cpp (MenuClasses) : menus pendant lesquels le texte est masque.
const TCHAR* const MenuClasses[] = {
TEXT("Journal_C"), TEXT("MainMenu_C"), TEXT("CityMenu_C"), TEXT("StatMenu_C"),
@@ -295,6 +309,30 @@
return FMath::Clamp(Value, -1.f, 1.f);
}
+ // KEY2 : une touche de l'action Name vient d'etre pressee, modificateurs exacts (comme InteractPressed et
+ // ModifiersMatch de LPDirectControlSubsystem.cpp), mappages et remappages suivis.
+ bool ActionJustPressed(const APlayerController* PC, FName Action)
+ {
+ const UInputSettings* Settings = UInputSettings::GetInputSettings();
+ if (!PC || !PC->PlayerInput || !Settings)
+ {
+ return false;
+ }
+ const bool bCtrl = PC->IsInputKeyDown(EKeys::LeftControl) || PC->IsInputKeyDown(EKeys::RightControl);
+ const bool bShift = PC->IsInputKeyDown(EKeys::LeftShift) || PC->IsInputKeyDown(EKeys::RightShift);
+ const bool bAlt = PC->IsInputKeyDown(EKeys::LeftAlt) || PC->IsInputKeyDown(EKeys::RightAlt);
+ const bool bCmd = PC->IsInputKeyDown(EKeys::LeftCommand) || PC->IsInputKeyDown(EKeys::RightCommand);
+ for (const FInputActionKeyMapping& Map : Settings->GetActionMappings())
+ {
+ if (Map.ActionName == Action && Map.bCtrl == bCtrl && Map.bShift == bShift && Map.bAlt == bAlt && Map.bCmd == bCmd
+ && PC->PlayerInput->WasJustPressed(Map.Key))
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+
// Noms des touches du clavier de l'action ou de l'axe Name, mappages en cours (comme AxisValue, remappages suivis) ;
// Sign : 1 ou -1 = echelle de l'axe de ce signe, 0 = toutes. Souris, manette et axes analogiques exclus.
FString MappedKeys(FName Name, int32 Sign)
@@ -1207,6 +1245,8 @@
// Prise du couple a la souris (B2p-D) ; pendant la prise, la camera est figee et ZQSD ne la deplace pas.
const bool bGrabbing = TickCoupleMouse(GI, PC, View, Room, Couple, Pivot);
const AActor* SceneCamera = Cast<AActor>(LPCameraModeLocal::ReadObject(Room, LPCameraModeLocal::RoomSceneCamera));
+ // KEY2 (KEY2-SPEC-01 § 8c) : touche d'action, hauteur de la camera de scene, couple sur Y.
+ TickSceneKeys(PC, View, Room, Couple, bGrabbing, Step);
if (!bGrabbing && bKeys && Couple.Num() > 0 && View == SceneCamera)
{
// Mode Scene : la camera tourne autour du couple du meme angle que son lacet (cadrage garde, comme B2n),
@@ -1301,6 +1341,64 @@
bMiddleWasDown = bMiddle;
}
+void ULPCameraModeSubsystem::TickSceneKeys(APlayerController* PC, AActor* View, const UObject* Room, const TArray<AActor*>& Couple, bool bGrabbing, float Step)
+{
+ // KEY2 (§ 8c, 1) : la touche d'action bascule le panneau d'action, par le meme evenement que TickPanelClick.
+ if (LPCameraModeLocal::ActionJustPressed(PC, LPCameraModeLocal::ActionPanelAction))
+ {
+ UFunction* PanelToggle = PC->FindFunction(LPCameraModeLocal::PCLeftMouseSexOn);
+ if (PanelToggle && PanelToggle->NumParms == 0)
+ {
+ UE_LOG(LogLifePlay, Log, TEXT("Scene : panneau d'action bascule (ActionPanel, touche %s)"), *LPCameraModeLocal::MappedKeys(LPCameraModeLocal::ActionPanelAction, 0));
+ PC->ProcessEvent(PanelToggle, nullptr);
+ }
+ else
+ {
+ UE_LOG(LogLifePlay, Log, TEXT("Scene : panneau d'action non bascule, evenement LeftMouseSexOn absent du PC"));
+ }
+ }
+
+ // KEY2 (§ 8c, 5) : CameraHeight monte ou baisse SceneCamera ; hauteur ajoutee bornee, rien pendant une prise.
+ AActor* LiftCamera = Cast<AActor>(LPCameraModeLocal::ReadObject(Room, LPCameraModeLocal::RoomSceneCamera));
+ const bool bLiftC3 = LPCameraModeLocal::ReadBool(PC, LPCameraModeLocal::PCSexC3);
+ if (!IsValid(LiftCamera) || View != LiftCamera || LastSexView.Get() != LiftCamera || (bLiftC3 && !bLiftC3Was))
+ {
+ // Hors vue Scene, ou recadrage a venir (entree en Scene, passage en C3) : la camera reprend sa hauteur par defaut.
+ SceneCameraLift = 0.f;
+ }
+ else if (!bGrabbing)
+ {
+ const float HeightAxis = LPCameraModeLocal::AxisValue(PC, LPCameraModeLocal::AxisCameraHeight);
+ if (!FMath::IsNearlyZero(HeightAxis))
+ {
+ const float NewLift = FMath::Clamp(SceneCameraLift + HeightAxis * LPCameraModeLocal::CameraHeightSpeed * Step,
+ LPCameraModeLocal::CameraHeightMin, LPCameraModeLocal::CameraHeightMax);
+ if (!FMath::IsNearlyEqual(NewLift, SceneCameraLift))
+ {
+ LiftCamera->AddActorWorldOffset(FVector(0.0, 0.0, static_cast<double>(NewLift - SceneCameraLift)));
+ SceneCameraLift = NewLift;
+ }
+ }
+ }
+ bLiftC3Was = bLiftC3;
+
+ // KEY2 (§ 8c, 4) : CoupleMoveY, piece cachee seulement (meme garde que le X de PC), pas fixe par image.
+ const AActor* MoveRoom = Cast<AActor>(Room);
+ const float MoveAxis = LPCameraModeLocal::AxisValue(PC, LPCameraModeLocal::AxisCoupleMoveY);
+ if (bGrabbing || FMath::IsNearlyZero(MoveAxis) || Couple.Num() == 0 || !MoveRoom || !MoveRoom->IsHidden())
+ {
+ return;
+ }
+ const FVector CoupleRight = Couple[0]->GetActorRightVector();
+ const FVector Side = LPCameraModeLocal::CoupleMoveSpace == LPCameraModeLocal::ECoupleMoveSpace::World
+ ? FVector(0.0, 1.0, 0.0) : FVector(CoupleRight.X, CoupleRight.Y, 0.0).GetSafeNormal();
+ const FVector MoveStep = Side * static_cast<double>(MoveAxis * LPCameraModeLocal::CoupleMoveYStep);
+ for (AActor* Person : Couple)
+ {
+ Person->SetActorLocation(Person->GetActorLocation() + MoveStep);
+ }
+}
+
void ULPCameraModeSubsystem::KeepMouseFree(APlayerController* PC, float DeltaTime)
{
// COMMUN-12 : a la fermeture du panneau, le PC repasse en GameOnly (CapturePermanently, LockOnCapture, mouvement
@@ -1526,6 +1624,13 @@
{
// Maj tenu : la voie d'origine (Maj + bouton + souris, Room) garde la main, aucune prise.
const bool bShiftHeld = Input && (Input->IsPressed(EKeys::LeftShift) || Input->IsPressed(EKeys::RightShift));
+ // KEY2 (KEY2-SPEC-01 § 8b, decision 13) : « Arrows only » (CoupleDrag=Keys), le clic gauche ne prend plus le
+ // couple et reste au jeu ; le clic droit garde la rotation.
+ const ULPKeyboardSubsystem* DragKeys = GI->GetSubsystem<ULPKeyboardSubsystem>();
+ if (DragKeys && DragKeys->IsCoupleDragByKeys() && bLeftPressed)
+ {
+ return false;
+ }
if ((!bLeftPressed && !bRightPressed) || bShiftHeld)
{
return false;
```
### 1.7 API moteur relues (UE 5.8, `D:\Game\Launcher\Engine\UE_5.8\Engine\Source`, établi)
- `IInputProcessor` : `HandleKeyUpEvent` (`Runtime/Slate/Public/Framework/Application/IInputProcessor.h:29`), `HandleMouseButtonDownEvent` (:38), `HandleMouseWheelOrGestureEvent(FSlateApplication&, const FPointerEvent&, const FPointerEvent*)` (:47). Les préprocesseurs passent avant les widgets et un `true` arrête l'événement : `SlateApplication.cpp:5097`, `:5324`, `:6232`.
- `FPointerEvent::GetEffectingButton` (`Runtime/SlateCore/Public/Input/Events.h:1078`), `GetWheelDelta` (:1081) ; `FInputEvent::IsRepeat` (:210), `IsShiftDown` (:220), `IsControlDown` (:244), `IsAltDown` (:268), `IsCommandDown` (:292).
- `SComboBox` : `SLATE_ITEMS_SOURCE_ARGUMENT(OptionType, OptionsSource)` (`Runtime/Slate/Public/Widgets/Input/SComboBox.h:151`, surcharge `const TArray<ArgType>*` dans `Widgets/Views/IItemsSource.h:32`), `OnSelectionChanged` / `OnGenerateWidget` (:152-153 ; signatures `Framework/SlateDelegates.h:157-162` et `:205-210`), `InitiallySelectedItem` (:162), slot de contenu (:137). Styles « ComboBox » et « ComboBox.Row » présents dans le style du jeu (`Runtime/SlateCore/Private/Styling/StarshipCoreStyle.cpp:1595`, `:1646`).
- `EKeys::MouseScrollUp/Down` (`Runtime/InputCore/Classes/InputCoreTypes.h:352-353`, détails `MouseButton | ButtonAxis` : `InputCoreTypes.cpp:539-540`), `ThumbMouseButton`, `ThumbMouseButton2` (`.h:359-360`) ; touches de modificateur = Shift, Ctrl, Alt, Cmd gauches et droites (`InputCoreTypes.cpp:646-653`).
- `FConfigFile` (`Runtime/Core/Public/Misc/ConfigCacheIni.h:574`) : `GetString(Section, Key, FString&)` (:815), `GetInt(Section, Key, int32&)` (:833), `AddToSection(Section, FName, const FString&)` (:945).
- `UPlayerInput::WasJustPressed` lit `EventCounts[IE_Pressed]` (`Runtime/Engine/Private/UserInterface/PlayerInput.cpp:2111-2130`), remis à zéro au début de `ProcessInputStack` (:1285, :1335) : lisible par un ticker du noyau après le tick du monde, comme `InteractPressed` (`LPDirectControlSubsystem.cpp:189-204`, ticker 0 s `:832`) (estimé pour l'ordre exact des ticks).
## 2. Écarts à la SPEC (obligatoire)
1. **E1, ajout** : lignes « (no key) » avec Add pour ActionPanel, CameraHeight ± et CoupleMoveY ± quand aucune liaison ne les porte (`KbAddMissing`). Motif : le menu ne montre que les liaisons existantes ; la migration § 5 n'ajoute ni CameraHeight ni CoupleMoveY, et ActionPanel reste sans touche si Q est pris : sans ces lignes, ces entrées seraient invisibles et non remappables pour un joueur déjà équipé d'un `LPKeyboard.ini`.
2. **E2, interprétation** : le bloc caméra quitte le groupe « Camera » (rang 1) pour la section « Scene » (rang 8, place de l'ancien groupe Scene), comme le demande l'ordre du § 8 ; la caméra apparaît donc plus bas dans la liste qu'avant.
3. **E3, sous-titres** : blocs « Camera », « Characters », « Actions » (ActionPanel seul), « Other » (TestScenes, Recommended, que la SPEC ne place pas). Libellés neutres seulement (décision 10).
4. **E4, Reset d'un joueur Custom** : après Reset, les touches sont celles de DefaultInput (QWERTY) et `Layout=QWERTY` est écrit. « Layout est gardé » (§ 4) est appliqué au seul cas AZERTY.
5. **E5, clic gauche seul pendant une capture** : refusé avec message et **consommé** (la capture continue, Escape l'annule). Choix : un clic laissé à l'interface pendant la reconstruction du panneau aurait pu viser un widget détruit.
6. **E6, option « Move couple with »** : un bouton bascule qui affiche la valeur en cours (« Mouse and arrows » / « Arrows only »), pas une liste ; enregistré aussitôt (`CoupleDrag=Mouse|Keys`).
7. **E7, CameraHeight** : translation verticale seule de SceneCamera (sans réorientation) ; vitesse 100 cm/s (SPEC : à fixer à l'essai) ; borne `[-40, 150]` sur la hauteur ajoutée depuis le dernier recadrage (remise à zéro hors vue Scene, à l'entrée en Scene et au passage en C3). L'avance ou le recul (MoveForward) met à l'échelle le décalage de la caméra : la hauteur ajoutée réelle peut s'écarter un peu du compteur (estimé).
8. **E8, CoupleMoveY** : ignoré pendant une prise à la souris (prudence, non demandé) ; aucune borne en Y (le X de PC n'est borné qu'en X).
9. **E9, CoupleDrag=Keys** : un appui gauche et droit dans la même image ne prend rien (cas limite ; garde ajoutée sans toucher à la ligne `CoupleGrabMode = bLeftPressed ? 1 : 2`).
10. **E10, migration** : `Version` absente vaut 0 ; ActionPanel n'est ajouté que si le fichier porte des actions (sinon les actions de DefaultInput, Q compris, restent) ; « Q lié à rien » est testé sur toutes les liaisons du fichier, tous modificateurs confondus.
11. **E11, Custom** : seul le groupe de déplacement (MoveForward, MoveRight) passe la disposition en Custom ; changer ActionPanel à la main ne la change pas (lecture stricte du § 4).
12. **E12, journal** : une ligne `LogLifePlay` à chaque Apply (« Clavier : disposition … appliquee »), non demandée.
## 3. Instantané
Pris par l'orchestratrice avant la fiche : `Tools\Snapshot.ps1 -Lot KEY2-IMPL` (manifeste non relu ici) ; copie de référence `scratchpad\key2_base` (5 fichiers, MD5 = colonne « avant » du § 1.1, relevés au départ). Retour arrière proposé seulement : `Tools\Restore.ps1 -Lot KEY2-IMPL`, ou recopie des 5 fichiers de `key2_base` (attention : `LPCameraModeSubsystem.*` portent aussi les modifications non commitées de CAM2).
## 4. Ce qui n'a pas été fait
- Aucune compilation ni aucun essai (fiche) : tout le code est « proposé, non compilé ».
- Texte d'aide du jeu `BuildControlsHelp` (`LPCameraModeSubsystem.cpp`) non mis à jour (Arrows only, touche d'action, G / B, V / X) : fichier en gel, hors des blocs demandés.
- Migration : pas d'ajout de CameraHeight G / B ni de CoupleMoveY V / X aux fichiers existants (non prévu au § 5) ; voir E1. Question pour Big Boss : faut-il les ajouter si les touches sont libres, comme ActionPanel ?
- ActionPanel hors scène (`UI/Action`) : non (§ 8c, 1). Remappage de Y / N : build 2 (décision 11). `ConsoleKeys` non touchés (§ 5b).
- Aucun Blueprint modifié ; nom technique de l'axe de rotation dans le C++ (§ 8, signalé) non traité.
## 5. Points à confirmer en VAL (un par ligne)
- V1 Build `LifePlayEditor` puis Development (`Tools\Build.ps1`) à code 0, aucun avertissement nouveau ; `LPKeyboardSubsystem.cpp` et `LPCameraModeSubsystem.cpp` dans les unités compilées.
- V2 Compilation de `SComboBox<TSharedPtr<FString>>` : `.OptionsSource(&LayoutOptions)` (choix de surcharge), `OnGenerateWidget_Lambda` et `OnSelectionChanged_Lambda` (paramètres `TSharedPtr<FString>` par valeur).
- V3 Unity : noms `Kb*` uniques (grep sur `Source/` et `Plugins/` : seul `LPKeyboardSubsystem.cpp`) ; aucun C2374 / C2084.
- V4 Aucun C4456 / C4458 (ombrage) ni C4702 (constante `CoupleMoveSpace` en ternaire, comme `bSexWheelZoom`) avec les réglages V7 de la copie 5.8.
- V5 Initialisations d'agrégat `FBindGroup` à 4 champs (`{KbSceneOrder, TEXT("Scene"), 0, TEXT("Camera")}`) et tableau local `FRetiredKey` (FKey copiés d'`EKeys`).
- V6 Premier lancement sans `LPKeyboard.ini` : `[LPKeyboard]` `Layout=QWERTY`, `Version=2`, `CoupleDrag=Mouse` écrits ; W A S D déplacent ; Z et Q (gauche) n'agissent plus ; « Layout: QWERTY ».
- V7 Migration (copie d'un ancien `LPKeyboard.ini` avec les 5 liaisons) : les 5 disparaissent, journal « … passe de la version 0 a 2 : 5 liaisons retirees … », `Version=2`, `Layout=Custom`, autres liaisons intactes (diff avant / après).
- V8 Migration d'un fichier issu de l'ancien DefaultInput (MoveRight Q −1 présent) : Q pris → ActionPanel sans touche, ligne « (no key) » visible, ligne d'état « Action Panel has no key… » ; Apply QWERTY ou AZERTY la pose.
- V9 Apply QWERTY puis AZERTY : le diff de `LPKeyboard.ini` montre les 5 liaisons du préréglage et `Layout` ; en AZERTY Z Q S D déplacent, W n'agit plus, A ouvre le panneau d'action en scène, E interagit ; en QWERTY W A S D, Z n'agit plus, Q ouvre le panneau.
- V10 Apply annonce les touches retirées ailleurs (« Removed from: … ») quand une touche du préréglage était liée sans modificateur à une autre action.
- V11 Modification manuelle de MoveForward ou MoveRight (Change, Add, Remove, ou touche reprise par une autre action) → « Layout: custom » et `Layout=Custom`.
- V12 Reset : joueur AZERTY → touches de DefaultInput puis AZERTY, « AZERTY layout kept » ; joueur QWERTY ou Custom → QWERTY.
- V13 K5 : clic droit, bouton du milieu, Thumb1, Thumb2, Ctrl+clic gauche s'enregistrent ; clic gauche seul → message, capture maintenue.
- V14 K5 : molette haut / bas sur une action → « Mouse Wheel Up / Down » enregistré et affiché (passe `IsRemappable` : à vérifier, `ButtonAxis` non relu au niveau de `FKeyDetails::IsAnalog`) ; molette pendant la capture d'un axe → message.
- V15 K5 : Ctrl seul (appui puis relâchement sans autre touche) → « Left Ctrl saved » ; Ctrl+E pendant la capture → Ctrl+E, pas Ctrl seul.
- V16 K5 : avertissement « Also used by the game: Y » (et Shift, Ctrl, F12, PageDown, N, clics gauche et droit) sans refus.
- V17 K6 : section « Scene » dans l'ordre Camera (LeftRight, DownUp, ScrollDownUp, RotateCamera, SwitchCamera, CameraHeight), Characters (option « Move couple with », CoupleMoveY, ManualAnimating, axe de rotation), Actions (Action Panel), Other ; aucun libellé adulte ajouté au C++ (grep).
- V18 Liste déroulante en jeu : s'ouvre, se ferme, ne laisse pas le focus clavier bloqué ; fonctionne en page intégrée (réglages) et en fenêtre.
- V19 ActionPanel en scène : Q (QWERTY) ou A (AZERTY) bascule le panneau d'action (journal « Scene : panneau d'action bascule ») ; Ctrl+Q ne le bascule pas ; rien hors scène ni menu ouvert ; une saisie dans le filtre du panneau ne le rebascule pas.
- V20 CameraHeight en vue Scene : G monte, B baisse, arrêt à +150 / −40 cm, remise à zéro à l'entrée en Scene et en C3 ; rien pendant une prise à la souris.
- V21 CoupleMoveY : V / X déplacent le couple sur Y du monde de 5 par image, seulement quand `Room` est cachée (garde reprise du X de PC) ; à vérifier : si la pièce n'est pas cachée dans une scène ordinaire, V / X n'agissent pas (même comportement que DownUp dans PC).
- V22 « Arrows only » : clic gauche sur le couple → aucune prise, le clic reste au jeu ; clic droit → rotation ; `CoupleDrag=Keys` relu après redémarrage.
- V23 Aucun `Accessed None` nouveau ; Ctrl+C / Ctrl+E (§ 3) et Add / Subtract, pavé 4 / 6 sur les favoris seulement (§ 10, 4).
## 6. Fichiers à commiter (dépôt du jeu, branche `ue5.8`)
- M `Config/DefaultInput.ini`
- M `Source/LifePlay/LPKeyboardSubsystem.h`
- M `Source/LifePlay/LPKeyboardSubsystem.cpp`
- M `Source/LifePlay/LPCameraModeSubsystem.h` (porte aussi les changements non commités de CAM2 : ordre des commits à fixer par Big Boss)
- M `Source/LifePlay/LPCameraModeSubsystem.cpp` (idem)
- A `Docs/Migration/etat/REL/KEY2/KEY2-IMPL-01.md`
Tours : 25 / 30 (estimé par l'agent ; `FicheStats.py` fait foi)
## Constats
- L'ancien DefaultInput liait Q à MoveRight (−1) : presque tous les fichiers existants ont Q pris, donc la migration laissera ActionPanel sans touche (comportement voulu par le § 4) ; E1 rend l'entrée visible.
- `LPCameraModeSubsystem.cpp` reste en LF, les fichiers clavier et `DefaultInput.ini` en CRLF : l'outil Edit a gardé chaque convention (comptes du § 1.1).
- PubCheck du livrable (`pwsh -NoProfile -Command "& 'D:\Game\LifePlay-master 5.8\Tools\PubCheck.ps1' -Fichiers @('…\KEY2-IMPL-01.md')"`, avant l'ajout de cette ligne) : `PubCheck : 0 constat(s)`, code 0 ; livrable en LF (288 lignes, 0 CR).
## 7. Complément de l'orchestratrice (2026-10-20, arbitrages de Big Boss)
Arbitrages de Big Boss : E1, E4, E5, E6 et E7 **acceptés**. Migration des anciens `LPKeyboard.ini` : ajouter G / B, V / X et ActionPanel quand la touche n'est liée à rien. Texte d'aide `BuildControlsHelp` à faire par KEY2, seul écrivain du fichier jusqu'au commit (gel CAM2).
Instantané frais avant écriture : `Tools\Snapshot.ps1 -Lot KEY2-IMPL2` (LPKeyboardSubsystem.cpp 94575a8e, LPCameraModeSubsystem.cpp cd6427aa).
| Fichier | Changement | MD5 avant → après |
|---|---|---|
| `Source/LifePlay/LPKeyboardSubsystem.cpp` (CRLF gardé : 1476 lignes, 1476 CR) | migration Version=2 : une aide `KeyFree` (touche utilisée par aucune action ni aucun axe du fichier du joueur) ; ActionPanel sur Q si libre ; CameraHeight G (+1) / B (−1) et CoupleMoveY V (+1) / X (−1), chaque sens ajouté seulement s'il n'est pas déjà lié et si sa touche est libre ; journal avec le nombre de touches d'axe ajoutées | 94575a8e → 7501ec6a |
| `Source/LifePlay/LPCameraModeSubsystem.cpp` (LF) | `BuildControlsHelp` : 3 lignes d'aide (hauteur de la caméra, couple sur le côté, panneau d'action) et leurs 5 arguments ; une seule ligne existante remplacée (fin de la liste d'arguments, `);` déplacé) | cd6427aa → f15c2b9b |
Diff contre la référence 406fbf1d : `git diff --no-index --numstat` → **112 / 1** (105 / 0 de la première passe, plus 7 / 1 de l'aide). La seule ligne retirée est la dernière ligne d'arguments de `BuildControlsHelp`, prolongée ; aucun bloc de CAM2 (A1, R-B1f, F2) n'est touché.
Toujours non compilé : la compilation est KEY2-BUILD-01.