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

29 KiB
Raw Blame History

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: . » 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

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 -) :

--- 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.