178 lines
7 KiB
C++
178 lines
7 KiB
C++
// Lot MAPDL1 (MAPDL1-SPEC-05 v3, etape E1) : etats des villes dans le jeu et menu des villes. C++ seulement, aucun Blueprint modifie.
|
|
//
|
|
// Trois etats par ville (nom d'asset du jeu, ex. "Manila") :
|
|
// - Installed : ville de base (liste "base" de l'index), ou ville dont un pack lpm_city_* monte redirige a la fois
|
|
// /Game/Heavy/StreetMap/<V>.<V> et /Game/Heavy/StreetMap/Static/<V>.<V> (ULPAssetRegistryHelpers::ResolveCityPath) ;
|
|
// - Downloadable : pas installee, mais l'index (Saved/LPCityMaps.json, sinon Content/Data/LPCityMaps.json) a une entree
|
|
// avec au moins une URL, un sha256 de 64 chiffres hexadecimaux et une plage de versions du jeu qui contient la version ;
|
|
// - Unavailable : ni l'un ni l'autre.
|
|
// Les packs sont montes au demarrage et le cache de ResolveCityPath n'est jamais invalide : l'etat est calcule a la demande
|
|
// et garde pour la session (un pack pose en cours de session ne change rien avant le lancement suivant).
|
|
//
|
|
// Ce que le sous-systeme fait :
|
|
// - C2 : pose un filtre dans ModdingExtra (ModdingExtraSetFileListFilter). GetAllFilesFromFolder("Heavy/StreetMap","uasset"),
|
|
// que lit UI/CityMenu pour compter les deux .uasset d'une ville "Installed", perd les entrees des villes non installees :
|
|
// le calcul d'origine du Blueprint affiche alors "Not Installed" et bloque le clic (decision D4) ;
|
|
// - C3 : tant que CityMenu.CityScrollBox est a l'ecran, tri stable des lignes City_C (installees, telechargeables,
|
|
// indisponibles), texte et couleur de InstalledText, bouton desactive pour les indisponibles, relais du clic des lignes
|
|
// telechargeables (ULPCityRowClick). GI.Cities n'est pas modifie ;
|
|
// - C4 : bandeau de 10 s quand une partie sauvegardee charge le gabarit d'une ville non installee (OnCityMapResolved).
|
|
// Etape E5 (C6) : le clic d'une ligne telechargeable ouvre la fenetre "City map: <ville>" (LPCityMapsWindow), qui pilote
|
|
// le telechargement HTTP, le controle SHA-256 et la pose du pack (LPCityMapsDownload). Apres une pose reussie, la ville passe a
|
|
// RestartRequired pour le reste de la session ("Restart required", clic sans effet). OpenCityPage (navigateur) ne sert plus
|
|
// que de recours, depuis la fenetre.
|
|
|
|
#pragma once
|
|
|
|
#include "CoreMinimal.h"
|
|
#include "Containers/Ticker.h"
|
|
#include "Subsystems/GameInstanceSubsystem.h"
|
|
#include "LPCityMapsSubsystem.generated.h"
|
|
|
|
class SWidget;
|
|
class UScrollBox;
|
|
class UUserWidget;
|
|
class ULPCityMapsSubsystem;
|
|
class FLPCityMapsWindow;
|
|
|
|
UENUM(BlueprintType)
|
|
enum class ELPCityMapState : uint8
|
|
{
|
|
Installed,
|
|
Downloadable,
|
|
Unavailable,
|
|
/** Pack pose dans cette session : actif au prochain lancement du jeu (E5). */
|
|
RestartRequired
|
|
};
|
|
|
|
/** Un renvoi "licences" de l'index : titre court affiche, fichier du texte complet (sous Licenses/). */
|
|
struct FLPCityMapLicence
|
|
{
|
|
FString Titre;
|
|
FString Fichier;
|
|
};
|
|
|
|
/** Une entree "villes" de l'index (format 2). Les URL non HTTPS sont ecartees a la lecture. */
|
|
struct FLPCityMapEntry
|
|
{
|
|
FString Ville;
|
|
FString Pack;
|
|
FString Version;
|
|
FString JeuMin;
|
|
FString JeuMax;
|
|
FString Zip;
|
|
int64 Octets = 0;
|
|
FString Sha256;
|
|
TArray<FString> Urls;
|
|
FString Page;
|
|
TArray<FString> Licences;
|
|
FString Source;
|
|
};
|
|
|
|
/** Etat calcule d'une ville : EntryIndex designe l'entree de l'index retenue (Downloadable seulement). */
|
|
struct FLPCityStateInfo
|
|
{
|
|
ELPCityMapState State = ELPCityMapState::Unavailable;
|
|
int32 EntryIndex = INDEX_NONE;
|
|
};
|
|
|
|
/** Relais du clic d'une ligne telechargeable du menu des villes (un objet par bouton, garde par le sous-systeme). */
|
|
UCLASS()
|
|
class LIFEPLAY_API ULPCityRowClick : public UObject
|
|
{
|
|
GENERATED_BODY()
|
|
|
|
public:
|
|
/** Nom d'asset de la ville de la ligne. */
|
|
FString Ville;
|
|
TWeakObjectPtr<ULPCityMapsSubsystem> Owner;
|
|
|
|
UFUNCTION()
|
|
void OnClicked();
|
|
};
|
|
|
|
UCLASS()
|
|
class LIFEPLAY_API ULPCityMapsSubsystem : public UGameInstanceSubsystem
|
|
{
|
|
GENERATED_BODY()
|
|
|
|
public:
|
|
virtual void Initialize(FSubsystemCollectionBase& Collection) override;
|
|
virtual void Deinitialize() override;
|
|
|
|
/** Etat d'une ville (nom d'asset, casse ignoree), calcule au premier appel puis garde pour la session. */
|
|
UFUNCTION(BlueprintCallable, Category = "LifePlay|Cities")
|
|
ELPCityMapState GetCityState(const FString& Ville);
|
|
|
|
/** Taille en octets du pack de la ville (champ "octets" de l'entree retenue) ; 0 si la ville n'est pas telechargeable. */
|
|
UFUNCTION(BlueprintCallable, Category = "LifePlay|Cities")
|
|
int64 GetDownloadBytes(const FString& Ville);
|
|
|
|
/** URL HTTPS du pack, dans l'ordre de l'index (vide si la ville n'est pas telechargeable). C++ seulement. */
|
|
TArray<FString> GetDownloadUrls(const FString& Ville);
|
|
|
|
/** Entree de l'index retenue pour une ville telechargeable (nulle sinon). Valable jusqu'au prochain appel qui change l'index. */
|
|
const FLPCityMapEntry* FindDownloadEntry(const FString& Ville);
|
|
|
|
/** Clic d'une ligne telechargeable : ouvre la fenetre "City map: <ville>" (une seule a la fois). */
|
|
void OpenCityWindow(const FString& Ville);
|
|
|
|
/** Une ville dont le pack vient d'etre pose : "Restart required" jusqu'a la fin de la session. */
|
|
void MarkRestartRequired(const FString& Ville);
|
|
|
|
/** Ouvre "page" (sinon urls[0]) dans le navigateur. Recours de la fenetre (la fenetre l'appelle elle-meme par LaunchURL). */
|
|
void OpenCityPage(const FString& Ville);
|
|
|
|
private:
|
|
/** Lit l'index (Saved/LPCityMaps.json, sinon Content/Data/LPCityMaps.json). */
|
|
void LoadIndex();
|
|
const FLPCityStateInfo& EvaluateCity(const FString& Ville);
|
|
bool IsGameVersionInRange(const FLPCityMapEntry& Entry) const;
|
|
|
|
/** Filtre pose dans ModdingExtra (C2). */
|
|
void FilterFileList(const FString& RootFolder, const FString& Ext, TArray<FString>& Files);
|
|
|
|
/** Chaque image : liste des villes (C3) et fin du bandeau (C4). */
|
|
bool Tick(float DeltaTime);
|
|
UScrollBox* FindCityList() const;
|
|
bool IsListOnScreen(const UScrollBox* List) const;
|
|
void SortRows(UScrollBox* List);
|
|
void DecorateRows(UScrollBox* List);
|
|
|
|
/** Bandeau des parties sauvegardees (C4). */
|
|
void HandleCityMapResolved(const FString& Requested, const FString& Resolved);
|
|
bool ShowBanner(const FString& Text);
|
|
void CloseBanner();
|
|
|
|
TArray<FString> BaseCities;
|
|
TMap<FString, FLPCityMapLicence> Licences;
|
|
TArray<FLPCityMapEntry> Entries;
|
|
FString IndexPath;
|
|
|
|
/** Nom de ville en minuscules -> etat. */
|
|
TMap<FString, FLPCityStateInfo> StateCache;
|
|
|
|
FTSTicker::FDelegateHandle TickHandle;
|
|
FDelegateHandle ResolvedHandle;
|
|
|
|
/** Liste du menu vue en dernier (une instance par ouverture) et prochaine recherche (secondes, horloge de PlatformTime). */
|
|
TWeakObjectPtr<UScrollBox> ActiveList;
|
|
double NextListSearch = 0.0;
|
|
int32 SortedChildCount = -1;
|
|
bool bSortLogged = false;
|
|
|
|
/** Relais de clic de la liste courante, et boutons deja relies (objets faibles : une adresse reutilisee ne trompe pas). */
|
|
UPROPERTY()
|
|
TArray<TObjectPtr<ULPCityRowClick>> RowClicks;
|
|
TSet<TWeakObjectPtr<UObject>> BoundButtons;
|
|
|
|
TSet<FString> BannerShown;
|
|
TSharedPtr<SWidget> Banner;
|
|
double BannerExpiry = 0.0;
|
|
|
|
/** Fenetre de telechargement (E5) : une seule a la fois. Liberee par Tick une fois fermee (jamais depuis son propre Close). */
|
|
TSharedPtr<FLPCityMapsWindow> Window;
|
|
|
|
/** Villes (minuscules) dont le pack a ete pose dans cette session. */
|
|
TSet<FString> RestartCities;
|
|
};
|