LifePlay-Revival/Source/LifePlay/LPCameraModeSubsystem.h
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

214 lines
12 KiB
C++

// Lot CAM-B2c - indicateur du mode de camera (SPEC : Docs/Migration/etat/CAM/CAM-B2c-SPEC.md).
//
// Le joueur ne sait pas toujours quelle camera est active (C1, C2, C3, carte de la ville, studio,
// constructeur). Toutes les 0,1 s, ce sous-systeme lit la cible de vue du PlayerController et quelques
// variables de GI et de Room par reflexion, en deduit le mode (table du paragraphe 2 de la SPEC) et
// l'affiche en petit, en bas a gauche de l'ecran. Le texte est masque quand un menu est ouvert (meme
// test que LPDirectControlSubsystem). Aucun Blueprint ni asset n'est modifie ; propriete absente ou
// reference nulle : rien n'est affiche.
#pragma once
#include "CoreMinimal.h"
#include "Containers/Ticker.h"
#include "Subsystems/GameInstanceSubsystem.h"
#include "LPCameraModeSubsystem.generated.h"
class AActor;
class APlayerController;
class UGameInstance;
class SWidget;
class STextBlock;
class UGameViewportClient;
class UButton;
class UTextBlock;
class UUserWidget;
UCLASS()
class LIFEPLAY_API ULPCameraModeSubsystem : public UGameInstanceSubsystem
{
GENERATED_BODY()
public:
virtual void Initialize(FSubsystemCollectionBase& Collection) override;
virtual void Deinitialize() override;
private:
bool Tick(float DeltaTime);
/** Libelle du mode courant ; vide si aucun mode n'est reconnu. */
FString ComputeLabel() const;
/** Cree le texte et l'ajoute une fois au viewport de ce GameInstance ; faux sans viewport. */
bool EnsureWidget();
/** Retire le texte du viewport qui le porte, s'il existe encore. */
void RemoveWidget();
void SetShown(bool bShow);
/** Temoin C2 (CAM-B2f, journal seulement) : camera trop pres de la tete, et ce que la sonde Camera touche. */
void WitnessC2(float DeltaTime);
/** Temoin touche C (CAM-B2j, journal seulement) : a chaque image, front montant d'une touche de SwitchCamera. */
bool TickSwitchKey(float DeltaTime);
/** Ecrit les drapeaux qui gardent la touche C (PC, GI, Room) et la vue courante. */
void LogSwitchKey(const APlayerController* PC) const;
FTSTicker::FDelegateHandle TickHandle;
FTSTicker::FDelegateHandle SwitchKeyHandle;
/** Une touche de SwitchCamera etait enfoncee a l'image precedente. */
bool bSwitchKeyDown = false;
/** Vue deja signalee par « Indicateur de camera : aucun » (une ligne par changement de vue). */
FString LastEmptyView;
/** Delai avant la prochaine ligne du temoin C2 (au plus une par seconde). */
float WitnessCooldown = 0.f;
/** Temoin d'occlusion C2 (CAM-Tocc, journal seulement) : un composant visible entre la camera et la tete. */
void WitnessOcclusion(float DeltaTime);
/** Delai avant le prochain trace d'occlusion (0,5 s), ou la prochaine ligne apres un masquage (1 s). */
float OcclusionCooldown = 0.f;
/** Sortie de CharGen (CAM-B2r) : remet GI.SpaceOn (et GI.MainMenuOn si le menu principal est ferme) une fois
* la vue revenue sur une camera de piece ; rien si la vue reste sur la carte de la ville. */
void RepairAfterCharGen(const FString& Label, bool bMenu, float DeltaTime);
/** GI.CharGenOn a ete vu vrai ; la reparation reste a faire. */
bool bCharGenSeen = false;
/** Delai restant pour trouver une camera de piece apres CharGen (10 s). */
float CharGenRepairTimeout = 0.f;
/** Temoin C1 (CAM-B2r, journal seulement) : camera loin des yeux, penchee ou tournee par rapport au controle. */
void WitnessC1(float DeltaTime);
/** Delai avant la prochaine mesure du temoin C1 (0,5 s), ou la prochaine ligne (1 s). */
float C1Cooldown = 0.f;
/** Camera de scene au clavier (CAM-B2p, P3), a chaque image : pendant le sexe, ZQSD pilote la camera en vue
* (orbite et distance en mode Scene, regard en POV) ; temoin des touches (au plus une ligne par seconde). */
void TickSexCamera(float DeltaTime);
/** Menu a l'ecran au dernier passage de Tick (0,1 s) : le parcours des widgets n'est pas refait a chaque image. */
bool bMenuOnScreen = false;
/** Delai avant la prochaine ligne « Sexe : touches ». */
float SexKeysCooldown = 0.f;
/** Couple a la souris (CAM-B2p-D), mode Scene : clic gauche maintenu sur le couple = glisser-deposer sur la
* surface visee (lit ou sol, borne au sol de Room), clic droit maintenu = rotation ; camera figee pendant la
* prise. Vrai tant qu'une prise est en cours. */
bool TickCoupleMouse(UGameInstance* GI, APlayerController* PC, AActor* View, const UObject* Room, const TArray<AActor*>& Couple, const FVector& Pivot);
/** Leve toute prise (hors sexe, menu, vue invalide) : les clics reviennent au jeu tels quels. */
void ReleaseCoupleGrab();
/** 0 : aucune prise ; 1 : deplacement (clic gauche) ; 2 : rotation (clic droit). */
int32 CoupleGrabMode = 0;
bool bLeftWasDown = false;
bool bRightWasDown = false;
/** Le curseur a bouge d'au moins 5 px depuis la prise (le couple ne bouge pas sur un simple clic). */
bool bGrabMoved = false;
/** Ecart entre la racine du maillage la plus basse et la surface sous le couple, pris a la selection (journal
* seulement depuis CAM-B2p-H : la hauteur de pose est la surface visee + 3 cm). */
bool bGrabHasHeight = false;
double GrabHeight = 0.0;
/** Decalage XY entre le pivot et le point vise a la selection. */
FVector GrabOffset = FVector::ZeroVector;
FVector2D GrabMouseStart = FVector2D::ZeroVector;
float GrabLastMouseX = 0.f;
float GrabTurned = 0.f;
/** Camera en vue figee pendant la prise, et sa pose au moment du clic. */
TWeakObjectPtr<AActor> GrabCamera;
FVector GrabCameraLocation = FVector::ZeroVector;
FRotator GrabCameraRotation = FRotator::ZeroRotator;
/** « curseur sans position » deja ecrit pour cette scene. */
bool bCursorWarned = false;
/** Borne dure du couple (CAM-B2p-E), a chaque image pendant le sexe, menus compris : pivot dans le sol de Room,
* racine du maillage entre le dessus du sol et un plafond estime ; temoin des deplacements hors prise. */
void TickCoupleBounds(float DeltaTime);
FVector LastCouplePivot = FVector::ZeroVector;
bool bHasLastPivot = false;
/** « pas de borne » (piece cachee ou sans sol) deja ecrit pour cette scene. */
bool bHiddenRoomWarned = false;
float CoupleBoundsCooldown = 0.f;
float CoupleMoveCooldown = 0.f;
/** CAM-B2p-F : une prise vient de finir dans cette image (le temoin hors prise ne compte pas son dernier pas). */
bool bGrabJustEnded = false;
/** Bornes retenues deja ecrites pour cette scene (F2). */
bool bBoundsLogged = false;
/** Dernier composant qui a refuse une place au glisser (une ligne par changement). */
FString LastSpotBlocker;
/** Derniere surface refusee au glisser (CAM-B2p-H, G-f : ni sol ni lit ; une ligne par changement). */
FString LastSurfaceRefused;
/** Focus clavier (H1, H2) : premiere image du sexe deja traitee ; bouton de souris tenu a l'image precedente. */
bool bSceneFocusChecked = false;
bool bFocusClickWasDown = false;
/** Place du meneur a la prise : au relacher, s'il a bouge, LPShowCouple reprend sa place sure (G3, STE). */
FVector GrabLeadStart = FVector::ZeroVector;
/** Camera de scene (CAM-B2p-G) : recadrage a l'entree en Scene et en C3 (variable SexC3 du PC), suivi du couple
* hors glisser, distance bornee. Appele par TickCoupleBounds, a chaque image pendant le sexe. */
void TickSceneCamera(UGameInstance* GI, AActor* Room, const TArray<AActor*>& Couple, const FVector& Pivot, bool bGrabEnded);
/** Place SceneCamera a distance par defaut du couple, dans la piece, devant le premier obstacle, regard sur le couple. */
void RecenterSceneCamera(UGameInstance* GI, AActor* Room, AActor* SceneCamera, const TArray<AActor*>& Couple, const FVector& Pivot, const TCHAR* Reason);
/** Vue a l'image precedente (detection de l'entree en Scene). */
TWeakObjectPtr<AActor> LastSexView;
/** PC.SexC3 a l'image precedente (front montant = passage en C3). */
bool bSexC3Was = false;
/** Clic molette court pendant le sexe (CAM-B2p-H, G-g) : appui puis relacher en 0,2 s au plus, curseur immobile
* a 5 px pres = PC.LeftMouseSexOn (bascule du panneau des positions). Appele seulement pendant le sexe, hors menu. */
void TickPanelClick(APlayerController* PC, float DeltaTime);
bool bMiddleWasDown = false;
bool bMiddleHadMouse = false;
bool bMiddleMoved = false;
float MiddleHeld = 0.f;
FVector2D MiddleStart = FVector2D::ZeroVector;
/** Aide des controles du sexe (CAM-B2p-I), toutes les 0,1 s : icone « i » dans le panneau des actions (Cursor), a cote
* du filtre SearchAction (survol = info-bulle, clic = boite d'aide), 4 lignes d'origine de SexInterface.TextBlock_1
* rendues invisibles quand le bloc porte ce texte. Hors du sexe : icone repliee, boite fermee. */
void TickControlsHelp();
/** Texte d'aide en anglais (langue du jeu), touches lues dans les mappages en cours. */
FString BuildControlsHelp() const;
/** Insere l'icone dans le parent de SearchAction ; nul (et une ligne de temoin) si SearchAction ou son parent manque. */
UButton* AttachHelpIcon(UUserWidget* ActionCursor);
UFUNCTION()
void ToggleControlsHelp();
bool EnsureHelpBox();
void RemoveHelpBox();
/** Instance du panneau deja traitee (insertion tentee une fois, reussie ou non). */
TWeakObjectPtr<UUserWidget> HelpCursor;
TWeakObjectPtr<UButton> HelpButton;
/** Bloc des 4 lignes rendu invisible, et son opacite d'avant. */
TWeakObjectPtr<UTextBlock> FadedHelpLines;
float FadedLinesOpacity = 1.f;
bool bHelpBoxOpen = false;
/** Dernier texte pose dans l'info-bulle de l'icone. */
FString HelpTextShown;
TSharedPtr<SWidget> HelpRoot;
TSharedPtr<STextBlock> HelpText;
TWeakObjectPtr<UGameViewportClient> HelpViewport;
/** Panneau bascule par Alt (et non par le clic molette) a l'appui en cours. */
bool bPanelByAlt = false;
/** CAM-B2p-I (COMMUN-12) : pendant le sexe, hors menu et sans bouton tenu, remet la souris libre (GameAndUI,
* DoNotLock) quand le PC l'a verrouillee (GameOnly) : sinon le curseur est fige et le glisser vaut 0. */
void KeepMouseFree(APlayerController* PC, float DeltaTime);
/** Debut et fin de scene : empreinte des touches ; a la fin, chaque etat touche par CAM est rendu, une ligne. */
void BeginSexScene();
void EndSexScene();
bool bSexStateWas = false;
FString MappingsAtSexStart;
bool bMouseFreed = false;
/** Etat de la souris au debut de la scene (EMouseCaptureMode, EMouseLockMode, curseur cache a l'appui), rendu a la fin. */
bool bSavedMouseState = false;
uint8 SavedCaptureMode = 0;
uint8 SavedLockMode = 0;
bool bSavedHideCursor = false;
int32 MouseFreeCount = 0;
float MouseFreeCooldown = 0.f;
/** Curseur de la prise : position reelle quand elle bouge, sinon deltas bruts de la souris (souris verrouillee). */
FVector2D GrabCursor = FVector2D::ZeroVector;
FVector2D GrabLastReal = FVector2D::ZeroVector;
int32 GrabRawFrames = 0;
bool bGrabAimWarned = false;
/** Pivot apres la derniere action de CAM pendant la prise (temoin des deplacements venus d'ailleurs). */
FVector GrabPivotAfterCam = FVector::ZeroVector;
double ExternalMoveLogTime = 0.0;
double SelectMissLogTime = 0.0;
TSharedPtr<SWidget> Root;
TSharedPtr<STextBlock> Text;
/** Viewport auquel Root a ete ajoute. */
TWeakObjectPtr<UGameViewportClient> Viewport;
/** Libelle actuellement ecrit dans Text (mis a jour seulement quand il change). */
FString ShownLabel;
bool bShown = false;
/** KEY2 (KEY2-SPEC-01 § 8c), en scene, hors menu, a chaque image : ActionPanel bascule le panneau d'action
* (PC.LeftMouseSexOn), CameraHeight monte ou baisse SceneCamera (hauteur ajoutee bornee), CoupleMoveY deplace le
* couple sur Y (piece cachee seulement, comme le X de PC). Rien ne bouge pendant une prise a la souris. */
void TickSceneKeys(APlayerController* PC, AActor* View, const UObject* Room, const TArray<AActor*>& Couple, bool bGrabbing, float Step);
/** Hauteur ajoutee a SceneCamera par CameraHeight depuis le dernier recadrage (cm). */
float SceneCameraLift = 0.f;
/** PC.SexC3 a l'image precedente, pour CameraHeight (le recadrage de C3 remet la hauteur ajoutee a zero). */
bool bLiftC3Was = false;
};