LifePlay-Revival/Docs/Migration/etat/ST/STC/STC-T3-IMPL.md

407 lines
23 KiB
Markdown
Raw Normal View History

Fiche STC-T3 — Niveau N3 — Modèle prévu lp-impl | utilisé claude-opus-5-5 — Agent lp-impl — Skills lp-ue-compat, ue-cpp-build, lp-build, sk-protocol
# STC-T3-IMPL : C++ ULPUserSettings (GameUserSettings.ini) et style des sliders de Settings (2026-10-10)
Copie `D:\Game\LifePlay-master 5.8`, branche `ue5.8`, HEAD `20620f6`. SPEC : `STC-SPEC.md` § 0-2, 7 (lue en entier).
Non compilé (fiche : BUILD par lp-builder, éditeur ouvert). Aucun asset, config, Build.cs ni git touché.
## 1. Fichiers modifiés (chemins, lignes)
| Fichier | État | Lignes (`wc -l`) | Fins de ligne (`git ls-files --eol` / `-o --eol`) | MD5 |
|---|---|---|---|---|
| `D:\Game\LifePlay-master 5.8\Source\LifePlay\LPUserSettings.h` | neuf | 59 | `w/lf` (comme le modèle `LPModSettingsLibrary.h/.cpp`, `i/lf w/lf`) | `7d67c3f755930bcf1c0fd4c65aced897` |
| `D:\Game\LifePlay-master 5.8\Source\LifePlay\LPUserSettings.cpp` | neuf | 115 | `w/lf` | `ba74f9e3e6dbaa4a02579be89ea2e037` |
| `D:\Game\LifePlay-master 5.8\Source\LifePlay\LPSettingsSubsystem.cpp` | modifié, +52 / -0 (`git diff --stat`) | 835 → 887 | `i/crlf w/crlf` (inchangé, CRLF gardé par Edit) | `2fe96f77…` → `ede28e6d9aeff9a27f1adb988c03ae36` |
- `LPSettingsSubsystem.h` non modifié (MD5 `0f98c055…` = instantané). `LifePlay.Build.cs` non modifié : `Slate`, `SlateCore`,
`UMG` déjà en privé (`LifePlay.Build.cs:38`), `CoreUObject`/`Engine` en public (`:17`) ; aucune dépendance manquante (estimé :
toutes les API utilisées sont dans ces modules).
- Dossier `Source/LifePlay` : 18 fichiers CRLF, 35 LF (`git ls-files --eol -- Source/LifePlay`) ; les neufs suivent leur modèle (LF).
- Aucun caractère hors ASCII dans les deux fichiers neufs (Grep `[^\x00-\x7F]` : 0 résultat).
- Emplacements dans `LPSettingsSubsystem.cpp` (après modification) : include `Components/Slider.h` l. 19 ; `CollectSliders`
l. 281-303 ; couleurs l. 346-351 ; collecte l. 355 et 360 ; boucle de style l. 403-420 (`SetWidgetStyle` l. 415,
`SetSliderBarColor` l. 417, `SetSliderHandleColor` l. 418, Grep relu).
## 2. Écarts à la SPEC (obligatoire)
- E1 (style, § 2.3) : les couleurs sont portées par la **teinte des pinceaux** du style (`NormalBarImage` 0.06/0.06/0.06/0.95,
`HoveredBarImage` 0.12/0.12/0.12/0.98, `NormalThumbImage` 0.2/0.45/0.9/1, `HoveredThumbImage` 0.3/0.55/1/1) et
`SetSliderBarColor` / `SetSliderHandleColor` reçoivent **blanc** (multiplicateur neutre). Motif : `SSlider::OnPaint` dessine
teinte du pinceau × couleur (`SSlider.cpp:125, 137`) ; mettre les couleurs dans les pinceaux garde un état survolé distinct
(`GetBarImage`/`GetThumbImage`, `SSlider.cpp:483-513`). Les teintes de survol (0.12 et 0.3/0.55/1) ne sont pas dans la SPEC
(reprises de `SpinBgHover` et `CheckedHover`). Base = style propre du slider (`GetWidgetStyle`), pas `FCoreStyle` comme les
SpinBox : pinceaux, `BarThickness` (2 px par défaut) et taille de poignée (8×14) gardés. Seuls appels d'écriture : les trois
permis par la SPEC ; aucun `SetValue`, ni `SetIsEnabled`/`SetRenderOpacity`.
- E2 (portée du style, § 2.3) : appliqué, comme écrit, à **tous** les `USlider` de `SettingsScrollBox` : donc aussi aux 5 lignes
`LPColor*` (`LPColorGradeSubsystem.cpp:298-302`), aux sliders `LPVideo*` (`LPVideoQualitySubsystem.cpp:392`) et à tout slider
Blueprint de la liste. Effet visible au-delà du slider caméra : à confirmer par l'orchestrateur (une restriction au nom
`SliderCameraSpeed` serait un changement d'une ligne).
- E3 (propriété GI absente) : interprétation de « avertissement, rien d'autre ». `ApplyCameraSpeedAtStartup` : avertissement
puis retour, **ini non écrit** dans les deux branches (la reprise du premier lancement sera retentée). `SetCameraSpeed` :
avertissement pour GI, **ini quand même mis à jour** (l'ini fait foi, D4). GameInstance nul = même traitement.
- E4 : `SetCameraSpeed` écrit une ligne `Display` à chaque écriture de l'ini (`Camera : vitesse N enregistree dans
GameUserSettings.ini`) ; non demandé, utile à V5 ; au plus une ligne par cran réellement changé.
- E5 : formule de la SPEC gardée telle quelle, sans garde NaN : `FMath::Clamp` = `Max(Min(X, Max), Min)`
(`UnrealMathUtility.h:592-595`), donc NaN → 7 (estimé par lecture du code, non essayé).
- E6 : broche `V` gardée (nom de la SPEC) pour `ClampCameraSpeed` et `SetCameraSpeed`.
- Aucun autre écart : signatures, catégorie `LifePlay|Settings`, `UCLASS(config = GameUserSettings)`, valeurs par défaut (3, faux),
`SaveConfig()` seulement si valeur changée ou `bCameraSpeedSet` faux, lecture/écriture GI par réflexion `FDoubleProperty`
puis `FFloatProperty`.
## 3. Instantané (lot, manifeste)
- Lot `STC`, manifeste `D:\Game\LifePlay_Backup_5.5\STC\manifest.csv` (fait par l'orchestrateur, 2026-10-10T09:11:26) :
`LPUserSettings.h` et `.cpp` `Existed=False` ; `LPSettingsSubsystem.cpp` MD5 `2FE96F776A0F3DFDA803F6E0EB1BC73B` et `.h`
`0F98C05568AE789C5C0B78F6EC580503`, identiques aux fichiers avant modification (`md5sum`, relu avant la première écriture).
- Retour arrière (proposé, non fait) : `Tools\Restore.ps1 -Lot STC` (déplace les deux neufs, restaure le `.cpp`).
## 4. Ce qui n'a pas été fait
- Compilation (BUILD par lp-builder, éditeur fermé) ; aucun build 5.5 de contrôle (le lot vit dans la copie 5.8).
- Blueprints `MainMenu` (§ 3) et `GI` (§ 4), CHG, ini, `GameSettings.sav` : hors fiche.
- Aucun commit, aucun `git add`, aucune écriture dans `Docs/` de la copie 5.8, aucun script.
## 5. Points à confirmer en VAL
- BUILD 5.8 : code 0 ; `LPUserSettings.gen.cpp` produit par UHT ; `LPUserSettings.cpp` et `LPSettingsSubsystem.cpp` compilés ; 0 avertissement nouveau (règles V7 : ombrage, code inatteignable).
- Unity build : espace de noms `LPUserSettingsGi` (nommé, unique : Grep `LPUserSettings` sur Source, Plugins et addon 5.8 avant écriture = 0) sans C2374.
- Palette Blueprint : 4 nœuds sous `LifePlay|Settings` avec les broches de la table « Signatures » (Constats).
- V8 : journal `LogLifePlay` `Display` « Camera : vitesse N reprise de GI (GameSettings.sav : X), ecrite dans GameUserSettings.ini (premier lancement) » et section `[/Script/LifePlay.LPUserSettings]` avec `CameraSpeed=…` et `bCameraSpeedSet=True`.
- V8 (risque) : valeur de `CameraSpeed` dans `GameSettings.sav` avant migration ; si elle vaut 0.2 (cause (c)), la reprise donne 1, pas 3 (`Round(0.2)=0` puis borne 1) ; attendu SPEC V8 = 3.
- V6 : au relancement, ligne « Camera : vitesse 5 lue dans GameUserSettings.ini (ini 5.00), recopiee dans GI (avant X) » et `GI.CameraSpeed = 5`.
- V5 : une ligne « enregistree dans GameUserSettings.ini » par cran changé, aucune quand la valeur ne change pas ; fichier écrit sur disque (`SaveConfig` → `Flush`, `Obj.cpp:3942-3944`).
- V9 : seule la section `LPUserSettings` ajoutée dans `GameUserSettings.ini`.
- Style (E1, E2) : slider caméra lisible sur fond clair (barre 2 px, poignée 8×14 inchangées) ; aspect des sliders `LPColor*` / `LPVideo*` / Blueprint acceptable ; aucune valeur de slider modifiée à l'ouverture du menu.
- Cases et SpinBox : rendu inchangé (aucune ligne de leur traitement modifiée, diff relu).
## 6. Fichiers à commiter
Dépôt jeu `D:\Game\LifePlay-master 5.8`, branche `ue5.8` :
A Source/LifePlay/LPUserSettings.h
A Source/LifePlay/LPUserSettings.cpp
M Source/LifePlay/LPSettingsSubsystem.cpp
Tours : 56 / 60
## Constats
### Signatures telles qu'un Blueprint les verra (estimé : affichage par `FName::NameToDisplayString`, à confirmer en VAL)
Cible : `LPUser Settings Library` (classe `ULPUserSettingsLibrary`, statiques, aucune broche Target). Catégorie `LifePlay|Settings`.
| Nœud (titre) | C++ | Type | Broches d'entrée (nom interne → affiché) | Broches de sortie |
|---|---|---|---|---|
| Clamp Camera Speed | `static float ClampCameraSpeed(float V)` | `BlueprintPure` | `V` → « V » (Float simple précision) | `ReturnValue` → « Return Value » (Float) |
| Apply Camera Speed At Startup | `static void ApplyCameraSpeedAtStartup(UObject* GameInstance)` | `BlueprintCallable` | `execute` ; `GameInstance` → « Game Instance » (Object Reference) | `then` |
| Set Camera Speed | `static void SetCameraSpeed(UObject* GameInstance, float V)` | `BlueprintCallable` | `execute` ; `GameInstance` → « Game Instance » ; `V` → « V » (Float) | `then` |
| Get Camera Speed | `static float GetCameraSpeed()` | `BlueprintPure` | — | `ReturnValue` → « Return Value » (Float) |
Pas de `DefaultToSelf` : la broche `GameInstance` doit être reliée (`self` dans GI, `Get Game Instance` dans MainMenu).
Classe de configuration : `ULPUserSettings` (`UCLASS(config = GameUserSettings)`, `UPROPERTY(config) float CameraSpeed = 3.f`,
`UPROPERTY(config) bool bCameraSpeedSet = false`), non visible des Blueprints (pas de spécificateur Blueprint, conforme SPEC 2.1).
### Choix faits
- Accès à l'ini : `GetMutableDefault<ULPUserSettings>()` (objet par défaut chargé de l'ini à sa création), écriture
`SaveConfig()` sans argument : fichier = nom de config de la classe (`Obj.cpp:3697`, `GetConfigFilename` `Obj.cpp:2332-2338`,
`UClass::GetConfigName` `Class.cpp:7144-7149` : `GameUserSettings` est un nom de config connu), puis `Flush` sur disque
(`Obj.cpp:3942-3944`).
- Réflexion GI : `FindPropertyByName("CameraSpeed")`, `CastField<FDoubleProperty>` puis `CastField<FFloatProperty>`
(`UnrealType.h:2507, 2547` ; `CastField` const `Field.h:1133`) ; `Get/SetPropertyValue_InContainer` const (`UnrealType.h:1663, 1673`).
Tout autre type ou absence : `Warning` « pas de propriete reelle CameraSpeed dans <classe>, GI non touche ».
- `ApplyCameraSpeedAtStartup` : une ligne `Display` par appel réussi (source ini avec valeur ini et valeur GI d'avant, ou reprise
depuis GI avec valeur du sav) ; `SaveConfig` seulement dans la branche premier lancement.
- `SetCameraSpeed` : GI toujours écrit (valeur bornée) ; ini et `SaveConfig` seulement si `!bCameraSpeedSet || CameraSpeed != V`
(comparaison exacte : entiers 1-7 exacts en float).
- `GetCameraSpeed` : `bCameraSpeedSet ? ClampCameraSpeed(CameraSpeed) : 3`.
- Helpers dans l'espace de noms nommé `LPUserSettingsGi` (piège unity C2374, `lp-build` § 8) ; `FName` en `static` local (comme `LPModSettingsLibrary.cpp:13`).
- Compatibilité 5.5 (estimé, non compilé) : API du slider présentes en 5.5 (`UE_5.5/.../Components/Slider.h:147, 150, 184, 191`) ;
aucune garde de version nécessaire ; aucun correctif UE4 (`LPStringCompat`, `LPMeshCompat`, `LPNavigationHelpers`…) touché.
### API moteur 5.8 lues (établi)
- `UE_5.8/Engine/Source/Runtime/UMG/Public/Components/Slider.h:151` `GetWidgetStyle`, `:154` `SetWidgetStyle`, `:188`
`SetSliderBarColor(FLinearColor)`, `:195` `SetSliderHandleColor(FLinearColor)` ; accès direct aux membres déprécié 5.1 (`:51, 61, 66`).
- `UMG/Private/Components/Slider.cpp:210-217, 284-305` : ces setters ne touchent pas `Value` ; `:30` style d'exécution par défaut =
`FDefaultStyleCache::GetRuntime().GetSliderStyle()` (`SlateCore/Private/Styling/DefaultStyleCache.cpp:60` → `UMGCoreStyle.cpp:723-731` :
barre `FSlateColorBrush` blanche, poignée `Common/Button` 8×14, épaisseur 2).
- `SlateCore/Public/Styling/SlateTypes.h:1339-1386` : `FSliderStyle` (`NormalBarImage`, `HoveredBarImage`, `DisabledBarImage`,
`NormalThumbImage`, `HoveredThumbImage`, `DisabledThumbImage`, `BarThickness`).
- `Slate/Private/Widgets/Input/SSlider.cpp:119-138` (peinture : teinte × couleur × teinte du widget), `:483-513` (pinceau survolé).
- `CoreUObject/Public/UObject/Object.h:1283` `SaveConfig(uint64 RequiredPropertyFlags = CPF_Config, const TCHAR* Filename = nullptr, …)`.
- `Core/Public/GenericPlatform/GenericPlatformMath.h:332` `RoundToFloat(float)` ; `Core/Public/Math/UnrealMathUtility.h:592` `Clamp`.
Aucune API supposée par la SPEC absente en 5.8 (arrêt rubrique 7 non déclenché).
### Diff complet
`Source/LifePlay/LPSettingsSubsystem.cpp` (`git diff`, CR retirés à l'affichage) :
```diff
@@ -16,6 +16,7 @@
#include "Components/OverlaySlot.h"
#include "Components/ScrollBox.h"
#include "Components/SizeBox.h"
+#include "Components/Slider.h"
#include "Components/SpinBox.h"
#include "Components/TextBlock.h"
#include "Components/PanelWidget.h"
@@ -277,6 +278,30 @@ namespace LPSettingsUi
}
}
+ void CollectSliders(UWidget* Widget, TArray<USlider*>& Out)
+ {
+ if (!Widget)
+ {
+ return;
+ }
+ if (USlider* Slider = Cast<USlider>(Widget))
+ {
+ Out.Add(Slider);
+ return;
+ }
+ if (UUserWidget* User = Cast<UUserWidget>(Widget))
+ {
+ CollectSliders(User->GetRootWidget(), Out);
+ }
+ if (UPanelWidget* Panel = Cast<UPanelWidget>(Widget))
+ {
+ for (int32 Index = 0; Index < Panel->GetChildrenCount(); ++Index)
+ {
+ CollectSliders(Panel->GetChildAt(Index), Out);
+ }
+ }
+ }
+
void TintBrush(FSlateBrush& Brush, const FLinearColor& Tint)
{
Brush.TintColor = FSlateColor(Tint);
@@ -318,12 +343,21 @@ namespace LPSettingsUi
TintBrush(SpinStyle.ActiveFillBrush, FLinearColor(0.2f, 0.45f, 0.9f, 0.85f));
SpinStyle.ForegroundColor = FSlateColor(FLinearColor(1.f, 1.f, 1.f, 1.f));
+ // Lot STC : sliders (vitesse de camera, lignes LPColor* et LPVideo*). Barre sombre comme le fond des spinboxes,
+ // poignee bleue comme leur remplissage. Style et couleurs seulement : aucune valeur ecrite (pas de SetValue).
+ const FLinearColor SliderBar(0.06f, 0.06f, 0.06f, 0.95f);
+ const FLinearColor SliderBarHover(0.12f, 0.12f, 0.12f, 0.98f);
+ const FLinearColor SliderHandle(0.2f, 0.45f, 0.9f, 1.f);
+ const FLinearColor SliderHandleHover(0.3f, 0.55f, 1.f, 1.f);
+
TArray<UCheckBox*> Boxes;
TArray<USpinBox*> Spins;
+ TArray<USlider*> Sliders;
for (int32 Index = 0; Index < Scroll->GetChildrenCount(); ++Index)
{
CollectCheckBoxes(Scroll->GetChildAt(Index), Boxes);
CollectSpinBoxes(Scroll->GetChildAt(Index), Spins);
+ CollectSliders(Scroll->GetChildAt(Index), Sliders);
}
for (UCheckBox* Box : Boxes)
@@ -366,6 +400,24 @@ namespace LPSettingsUi
Spin->SetFont(Font);
}
+ for (USlider* Slider : Sliders)
+ {
+ if (!Slider)
+ {
+ continue;
+ }
+ // Style propre du slider (pinceaux et epaisseur gardes), seules les teintes changent.
+ FSliderStyle SliderStyle = Slider->GetWidgetStyle();
+ TintBrush(SliderStyle.NormalBarImage, SliderBar);
+ TintBrush(SliderStyle.HoveredBarImage, SliderBarHover);
+ TintBrush(SliderStyle.NormalThumbImage, SliderHandle);
+ TintBrush(SliderStyle.HoveredThumbImage, SliderHandleHover);
+ Slider->SetWidgetStyle(SliderStyle);
+ // SSlider::OnPaint multiplie la teinte du pinceau par ces couleurs : blanc = teintes ci-dessus telles quelles.
+ Slider->SetSliderBarColor(FLinearColor::White);
+ Slider->SetSliderHandleColor(FLinearColor::White);
+ }
+
if (UWidget* ShowFPS = [&]() -> UWidget*
{
for (int32 Index = 0; Index < Scroll->GetChildrenCount(); ++Index)
```
`Source/LifePlay/LPUserSettings.h` (fichier neuf, 59 lignes, contenu intégral) :
```cpp
// Lot STC - vitesse de camera adossee a GameUserSettings.ini (STC-SPEC, partie 2).
//
// La valeur de reference de la vitesse de camera est la section [/Script/LifePlay.LPUserSettings] de
// GameUserSettings.ini (PIE : Saved/Config/WindowsEditor/, jeu : Saved/Config/Windows/). GI.CameraSpeed n'en est
// qu'une recopie : ApplyCameraSpeedAtStartup la remplit au demarrage (apres GI.LoadSettings), SetCameraSpeed la met a
// jour quand le joueur bouge le slider du menu Settings. L'ini n'est ecrit que si la valeur change, ou une fois au
// premier lancement (bCameraSpeedSet faux) : la valeur de GI, issue de GameSettings.sav, est alors reprise et bornee.
// GameSettings.sav garde son champ CameraSpeed, recopie de GI par GI.SaveSettings.
// Valeurs permises : entiers de 1 a 7 (slider a pas de 1), 3 par defaut.
#pragma once
#include "CoreMinimal.h"
#include "Kismet/BlueprintFunctionLibrary.h"
#include "UObject/Object.h"
#include "LPUserSettings.generated.h"
/** Reglages du joueur propres a LifePlay, enregistres dans GameUserSettings.ini (objet par defaut seulement). */
UCLASS(config = GameUserSettings)
class LIFEPLAY_API ULPUserSettings : public UObject
{
GENERATED_BODY()
public:
/** Vitesse de camera choisie dans le menu Settings (1 a 7). */
UPROPERTY(config)
float CameraSpeed = 3.f;
/** Faux tant que l'entree n'a jamais ete ecrite (premier lancement : reprise de GI.CameraSpeed). */
UPROPERTY(config)
bool bCameraSpeedSet = false;
};
UCLASS()
class LIFEPLAY_API ULPUserSettingsLibrary : public UBlueprintFunctionLibrary
{
GENERATED_BODY()
public:
/** Arrondit a l'entier le plus proche et borne entre 1 et 7. */
UFUNCTION(BlueprintPure, Category = "LifePlay|Settings")
static float ClampCameraSpeed(float V);
/**
* A appeler au demarrage, apres GI.LoadSettings : recopie dans GI.CameraSpeed la valeur de GameUserSettings.ini.
* Premier lancement (entree jamais ecrite) : reprend GI.CameraSpeed (lu dans GameSettings.sav), la borne, l'ecrit
* dans GI et dans l'ini.
*/
UFUNCTION(BlueprintCallable, Category = "LifePlay|Settings")
static void ApplyCameraSpeedAtStartup(UObject* GameInstance);
/** Slider du menu Settings : borne V, l'ecrit dans GI.CameraSpeed et, si elle change, dans GameUserSettings.ini. */
UFUNCTION(BlueprintCallable, Category = "LifePlay|Settings")
static void SetCameraSpeed(UObject* GameInstance, float V);
/** Vitesse de camera enregistree dans GameUserSettings.ini, bornee (3 si jamais ecrite). */
UFUNCTION(BlueprintPure, Category = "LifePlay|Settings")
static float GetCameraSpeed();
};
```
`Source/LifePlay/LPUserSettings.cpp` (fichier neuf, 115 lignes, contenu intégral) :
```cpp
// Lot STC - voir LPUserSettings.h
#include "LPUserSettings.h"
#include "LifePlay.h"
#include "UObject/UnrealType.h"
namespace LPUserSettingsGi
{
constexpr float DefaultCameraSpeed = 3.f;
// GI.CameraSpeed est une variable Blueprint : reel double en 5.x (FDoubleProperty), float accepte aussi.
// Propriete absente ou d'un autre type : avertissement, GI n'est pas touche.
FProperty* FindCameraSpeed(UObject* GameInstance, const TCHAR* Caller)
{
static const FName CameraSpeedName(TEXT("CameraSpeed"));
if (!GameInstance)
{
UE_LOG(LogLifePlay, Warning, TEXT("Camera : %s sans GameInstance, GI.CameraSpeed non touche"), Caller);
return nullptr;
}
FProperty* Prop = GameInstance->GetClass()->FindPropertyByName(CameraSpeedName);
if (!CastField<FDoubleProperty>(Prop) && !CastField<FFloatProperty>(Prop))
{
UE_LOG(LogLifePlay, Warning, TEXT("Camera : %s, pas de propriete reelle CameraSpeed dans %s, GI non touche"),
Caller, *GameInstance->GetClass()->GetName());
return nullptr;
}
return Prop;
}
float ReadValue(const FProperty* Prop, const UObject* GameInstance)
{
if (const FDoubleProperty* AsDouble = CastField<FDoubleProperty>(Prop))
{
return static_cast<float>(AsDouble->GetPropertyValue_InContainer(GameInstance));
}
if (const FFloatProperty* AsFloat = CastField<FFloatProperty>(Prop))
{
return AsFloat->GetPropertyValue_InContainer(GameInstance);
}
return DefaultCameraSpeed;
}
void WriteValue(const FProperty* Prop, UObject* GameInstance, float Value)
{
if (const FDoubleProperty* AsDouble = CastField<FDoubleProperty>(Prop))
{
AsDouble->SetPropertyValue_InContainer(GameInstance, static_cast<double>(Value));
}
else if (const FFloatProperty* AsFloat = CastField<FFloatProperty>(Prop))
{
AsFloat->SetPropertyValue_InContainer(GameInstance, Value);
}
}
}
float ULPUserSettingsLibrary::ClampCameraSpeed(float V)
{
return FMath::Clamp(FMath::RoundToFloat(V), 1.f, 7.f);
}
void ULPUserSettingsLibrary::ApplyCameraSpeedAtStartup(UObject* GameInstance)
{
ULPUserSettings* Settings = GetMutableDefault<ULPUserSettings>();
FProperty* Prop = LPUserSettingsGi::FindCameraSpeed(GameInstance, TEXT("ApplyCameraSpeedAtStartup"));
if (!Prop)
{
// Avertissement deja ecrit. Ini inchange : au premier lancement, la reprise depuis GI sera retentee.
return;
}
if (Settings->bCameraSpeedSet)
{
const float Value = ClampCameraSpeed(Settings->CameraSpeed);
const float Before = LPUserSettingsGi::ReadValue(Prop, GameInstance);
LPUserSettingsGi::WriteValue(Prop, GameInstance, Value);
UE_LOG(LogLifePlay, Display, TEXT("Camera : vitesse %.0f lue dans GameUserSettings.ini (ini %.2f), recopiee dans GI (avant %.2f)"),
Value, Settings->CameraSpeed, Before);
return;
}
// Premier lancement : entree jamais ecrite. GI.CameraSpeed vient de GameSettings.sav (GI.LoadSettings).
const float FromGi = LPUserSettingsGi::ReadValue(Prop, GameInstance);
const float Value = ClampCameraSpeed(FromGi);
LPUserSettingsGi::WriteValue(Prop, GameInstance, Value);
Settings->CameraSpeed = Value;
Settings->bCameraSpeedSet = true;
Settings->SaveConfig();
UE_LOG(LogLifePlay, Display, TEXT("Camera : vitesse %.0f reprise de GI (GameSettings.sav : %.2f), ecrite dans GameUserSettings.ini (premier lancement)"),
Value, FromGi);
}
void ULPUserSettingsLibrary::SetCameraSpeed(UObject* GameInstance, float V)
{
const float Value = ClampCameraSpeed(V);
if (FProperty* Prop = LPUserSettingsGi::FindCameraSpeed(GameInstance, TEXT("SetCameraSpeed")))
{
LPUserSettingsGi::WriteValue(Prop, GameInstance, Value);
}
// L'ini fait foi : ecrit seulement si la valeur change, ou si l'entree n'a jamais ete ecrite.
ULPUserSettings* Settings = GetMutableDefault<ULPUserSettings>();
if (Settings->bCameraSpeedSet && Settings->CameraSpeed == Value)
{
return;
}
Settings->CameraSpeed = Value;
Settings->bCameraSpeedSet = true;
Settings->SaveConfig();
UE_LOG(LogLifePlay, Display, TEXT("Camera : vitesse %.0f enregistree dans GameUserSettings.ini"), Value);
}
float ULPUserSettingsLibrary::GetCameraSpeed()
{
const ULPUserSettings* Settings = GetDefault<ULPUserSettings>();
return Settings->bCameraSpeedSet ? ClampCameraSpeed(Settings->CameraSpeed) : LPUserSettingsGi::DefaultCameraSpeed;
}
```
### Commandes de contrôle rejouables
- `git -C "D:/Game/LifePlay-master 5.8" status --short` → ` M Source/LifePlay/LPSettingsSubsystem.cpp`, `?? Source/LifePlay/LPUserSettings.cpp`,
`?? Source/LifePlay/LPUserSettings.h` (+ `?? Docs/Migration/32_Organisation_Agents.md`, préexistant, non touché).
- `git -C "D:/Game/LifePlay-master 5.8" ls-files --eol -- Source/LifePlay/LPSettingsSubsystem.cpp` → `i/crlf w/crlf` ;
`git … ls-files -o --eol -- Source/LifePlay/` → `w/lf` pour les deux neufs.
## Addendum orchestrateur (2026-10-10, après remise du livrable)
Écart E2 tranché : le style ne s'applique qu'au slider `SliderCameraSpeed` (les sliders `LPColor*` / `LPVideo*` gardent
leur aspect). Code actuel de la boucle : `if (!Slider || Slider->GetFName() != TEXT("SliderCameraSpeed")) { continue; }` et
commentaire « slider de vitesse de camera seulement ». MD5 actuel de `LPSettingsSubsystem.cpp` : `b72671af…` (le MD5
`ede28e6d…` cité plus haut est celui de la version remise par lp-impl). Le diff de ce livrable décrit la version
antérieure ; la version qui fait foi est celle relue en STC-VAL (contrôle a).