407 lines
23 KiB
Markdown
407 lines
23 KiB
Markdown
|
|
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).
|