Compare commits
2 Commits
87d9ca4dae
...
079d05c117
| Author | SHA1 | Date | |
|---|---|---|---|
| 079d05c117 | |||
| 90484876f0 |
@@ -35,6 +35,12 @@ ScreenSetAsset=/Game/ZMMO/UI/FrontEnd/DA_FrontEndScreenSet.DA_FrontEndScreenSet
|
|||||||
; cliente resolve `mapId -> ClientLevel/spawns` via FZMMOMapDef rows.
|
; cliente resolve `mapId -> ClientLevel/spawns` via FZMMOMapDef rows.
|
||||||
MapsTableAsset=/Game/ZMMO/Data/World/DT_Maps.DT_Maps
|
MapsTableAsset=/Game/ZMMO/Data/World/DT_Maps.DT_Maps
|
||||||
|
|
||||||
|
; Loading dinâmico (etapas + dicas). DA_LoadingProfiles tem 1 perfil por
|
||||||
|
; contexto (EZMMOLoadingContext); DT_LoadingTips rotaciona durante o load.
|
||||||
|
; Cliente local marca etapas via eventos (sem opcode novo).
|
||||||
|
LoadingProfilesAsset=/Game/ZMMO/Data/UI/Loading/DA_LoadingProfiles.DA_LoadingProfiles
|
||||||
|
LoadingTipsAsset=/Game/ZMMO/Data/UI/Loading/DT_LoadingTips.DT_LoadingTips
|
||||||
|
|
||||||
; -----------------------------------------------------------------------------
|
; -----------------------------------------------------------------------------
|
||||||
; UI in-game (PR 19+). Espelho do front-end pattern:
|
; UI in-game (PR 19+). Espelho do front-end pattern:
|
||||||
; - ScreenSetAsset mapeia EZMMOInGameUIState -> WBP (Playing -> WBP_HUD,
|
; - ScreenSetAsset mapeia EZMMOInGameUIState -> WBP (Playing -> WBP_HUD,
|
||||||
|
|||||||
BIN
Content/ZMMO/Data/UI/Loading/DA_LoadingProfiles.uasset
Normal file
BIN
Content/ZMMO/Data/UI/Loading/DA_LoadingProfiles.uasset
Normal file
Binary file not shown.
BIN
Content/ZMMO/Data/UI/Loading/DT_LoadingTips.uasset
Normal file
BIN
Content/ZMMO/Data/UI/Loading/DT_LoadingTips.uasset
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Content/ZMMO/UI/Shared/Loading/WBP_Loading.uasset
Normal file
BIN
Content/ZMMO/UI/Shared/Loading/WBP_Loading.uasset
Normal file
Binary file not shown.
66
Source/ZMMO/Data/UI/LoadingTypes.h
Normal file
66
Source/ZMMO/Data/UI/LoadingTypes.h
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "CoreMinimal.h"
|
||||||
|
#include "LoadingTypes.generated.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Contexto do loading screen genérico. Cada contexto resolve um perfil
|
||||||
|
* (lista de etapas) no UZMMOLoadingProfilesDataAsset.
|
||||||
|
*
|
||||||
|
* Cliente local rastreia o progresso assinando eventos próprios
|
||||||
|
* (HandlePostLoadMap, OnPlayerSpawned, etc) — server não envia opcode de
|
||||||
|
* progresso, ver decisão em [[project_ui_loading_dynamic]].
|
||||||
|
*/
|
||||||
|
UENUM(BlueprintType)
|
||||||
|
enum class EZMMOLoadingContext : uint8
|
||||||
|
{
|
||||||
|
None,
|
||||||
|
FrontEndEnteringWorld, ///< FrontEnd → mundo (handoff CharServer→WorldServer + spawn).
|
||||||
|
InGameEnteringInstance, ///< Mundo → dungeon/instance (futuro).
|
||||||
|
InGameRespawn, ///< Pós-morte, retorno ao mundo (futuro).
|
||||||
|
};
|
||||||
|
|
||||||
|
UENUM(BlueprintType)
|
||||||
|
enum class EZMMOLoadingStepStatus : uint8
|
||||||
|
{
|
||||||
|
Pending, ///< Ainda não começou.
|
||||||
|
Running, ///< Em andamento (o "barber pole").
|
||||||
|
Done, ///< Concluída.
|
||||||
|
Failed, ///< Falhou (com reason em Status).
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Uma etapa do loading. StepId é o identificador estável usado por código
|
||||||
|
* pra chamar MarkStepDone(StepId); Label é o texto localizado mostrado.
|
||||||
|
*/
|
||||||
|
USTRUCT(BlueprintType)
|
||||||
|
struct FZMMOLoadingStepDef
|
||||||
|
{
|
||||||
|
GENERATED_BODY()
|
||||||
|
|
||||||
|
/** Id estável (ex.: "Travel", "MapLoaded", "Connect", "Spawn"). */
|
||||||
|
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Loading")
|
||||||
|
FName StepId;
|
||||||
|
|
||||||
|
/** Texto localizado ("Carregando mapa...", "Conectando..."). */
|
||||||
|
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Loading")
|
||||||
|
FText Label;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Perfil de loading por contexto. Designer edita no DA — código só
|
||||||
|
* consulta StepId pra avançar etapa.
|
||||||
|
*/
|
||||||
|
USTRUCT(BlueprintType)
|
||||||
|
struct FZMMOLoadingProfile
|
||||||
|
{
|
||||||
|
GENERATED_BODY()
|
||||||
|
|
||||||
|
/** Etapas em ordem. ProgressBar = #Done / #Steps. */
|
||||||
|
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Loading")
|
||||||
|
TArray<FZMMOLoadingStepDef> Steps;
|
||||||
|
|
||||||
|
/** Título da tela (vazio = usa default da classe). */
|
||||||
|
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Loading")
|
||||||
|
FText TitleOverride;
|
||||||
|
};
|
||||||
@@ -18,6 +18,7 @@
|
|||||||
#include "UObject/ConstructorHelpers.h"
|
#include "UObject/ConstructorHelpers.h"
|
||||||
#include "ZMMO.h"
|
#include "ZMMO.h"
|
||||||
#include "ZMMOAttributeComponent.h"
|
#include "ZMMOAttributeComponent.h"
|
||||||
|
#include "ZMMOPlayerState.h"
|
||||||
#include "ZMMOWorldSubsystem.h"
|
#include "ZMMOWorldSubsystem.h"
|
||||||
#include "ZeusNetworkSubsystem.h"
|
#include "ZeusNetworkSubsystem.h"
|
||||||
#include "UI/FrontEnd/UIFrontEndFlowSubsystem.h"
|
#include "UI/FrontEnd/UIFrontEndFlowSubsystem.h"
|
||||||
@@ -278,6 +279,7 @@ void AZMMOPlayerCharacter::BindZeusSpawnDelegate()
|
|||||||
}
|
}
|
||||||
|
|
||||||
ZeusNetwork->OnPlayerSpawned.AddDynamic(this, &AZMMOPlayerCharacter::HandleZeusPlayerSpawned);
|
ZeusNetwork->OnPlayerSpawned.AddDynamic(this, &AZMMOPlayerCharacter::HandleZeusPlayerSpawned);
|
||||||
|
ZeusNetwork->OnCharInfoReceived.AddDynamic(this, &AZMMOPlayerCharacter::HandleZeusCharInfo);
|
||||||
bSpawnDelegateBound = true;
|
bSpawnDelegateBound = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -289,6 +291,7 @@ void AZMMOPlayerCharacter::UnbindZeusSpawnDelegate()
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
ZeusNetwork->OnPlayerSpawned.RemoveDynamic(this, &AZMMOPlayerCharacter::HandleZeusPlayerSpawned);
|
ZeusNetwork->OnPlayerSpawned.RemoveDynamic(this, &AZMMOPlayerCharacter::HandleZeusPlayerSpawned);
|
||||||
|
ZeusNetwork->OnCharInfoReceived.RemoveDynamic(this, &AZMMOPlayerCharacter::HandleZeusCharInfo);
|
||||||
bSpawnDelegateBound = false;
|
bSpawnDelegateBound = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -307,6 +310,10 @@ void AZMMOPlayerCharacter::TryRegisterLocalEntityFromCachedSpawn()
|
|||||||
{
|
{
|
||||||
HandleLocalSpawnReady(CachedEntityId, CachedPosCm, CachedYawDeg, CachedServerTimeMs);
|
HandleLocalSpawnReady(CachedEntityId, CachedPosCm, CachedYawDeg, CachedServerTimeMs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Race fix: o S_CHAR_INFO chega ANTES de S_SPAWN_PLAYER mas o pawn pode
|
||||||
|
// nao existir ainda (OpenLevel em andamento). Aplica o ultimo cacheado.
|
||||||
|
TryApplyCachedCharInfo();
|
||||||
}
|
}
|
||||||
|
|
||||||
void AZMMOPlayerCharacter::HandleZeusPlayerSpawned(const int32 InEntityId, const bool bIsLocal,
|
void AZMMOPlayerCharacter::HandleZeusPlayerSpawned(const int32 InEntityId, const bool bIsLocal,
|
||||||
@@ -349,25 +356,74 @@ void AZMMOPlayerCharacter::HandleLocalSpawnReady(const int32 InEntityId, const F
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Seed do EntityId no AttributeComponent do PlayerState. O componente
|
// Identidade publica (EntityId) + seed no AttributeComponent. CharName/Guild
|
||||||
// vive no AZMMOPlayerState via Component Registry — busca via PlayerState
|
// vem separado via S_CHAR_INFO -> HandleZeusCharInfo. AttributeComponent vive
|
||||||
// (sobrevive ao despawn do Pawn). S_ATTRIBUTE_SNAPSHOT_FULL chega via
|
// como subobject e recebe seed pra que o ZMMOAttributeNetworkHandler consiga
|
||||||
// UDP e e' rouado pelo ZMMOAttributeNetworkHandler via lookup por
|
// rotear o snapshot por EntityId desde o primeiro pacote.
|
||||||
// EntityId no PlayerArray do GameState.
|
|
||||||
if (APlayerState* PS = GetPlayerState())
|
if (APlayerState* PS = GetPlayerState())
|
||||||
{
|
{
|
||||||
|
if (AZMMOPlayerState* ZMMOPs = Cast<AZMMOPlayerState>(PS))
|
||||||
|
{
|
||||||
|
ZMMOPs->SetPublicIdentity(EntityIdAsInt64, /*CharId*/ FString(),
|
||||||
|
/*BaseLevel*/ 1, /*ClassId*/ 0);
|
||||||
|
}
|
||||||
if (UZMMOAttributeComponent* AttrComp = PS->FindComponentByClass<UZMMOAttributeComponent>())
|
if (UZMMOAttributeComponent* AttrComp = PS->FindComponentByClass<UZMMOAttributeComponent>())
|
||||||
{
|
{
|
||||||
AttrComp->SeedEntityId(InEntityId);
|
AttrComp->SeedEntityId(InEntityId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Aplica char info cacheada (caso S_CHAR_INFO tenha chegado antes do PS existir).
|
||||||
|
TryApplyCachedCharInfo();
|
||||||
|
|
||||||
// UI in-game e' responsabilidade do AZMMOHUD (GameMode.HUDClass), nao do
|
// UI in-game e' responsabilidade do AZMMOHUD (GameMode.HUDClass), nao do
|
||||||
// Pawn. AHUD::BeginPlay chama UUIInGameFlowSubsystem::StartInGame, que
|
// Pawn. AHUD::BeginPlay chama UUIInGameFlowSubsystem::StartInGame, que
|
||||||
// pushea WBP_HUD em UI.Layer.Game. UZMMOHudWidget::NativeOnActivated auto-binda
|
// pushea WBP_HUD em UI.Layer.Game. UZMMOHudWidget::NativeOnActivated auto-binda
|
||||||
// no UZMMOAttributeComponent via PlayerState.
|
// no UZMMOAttributeComponent via PlayerState.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AZMMOPlayerCharacter::HandleZeusCharInfo(const int32 InEntityId, const FString& CharName, const FString& GuildName)
|
||||||
|
{
|
||||||
|
UE_LOG(LogZMMOPlayer, Log,
|
||||||
|
TEXT("AZMMOPlayerCharacter: S_CHAR_INFO recebido EntityId=%d name=%s guild=%s"),
|
||||||
|
InEntityId, *CharName, *GuildName);
|
||||||
|
|
||||||
|
APlayerState* PS = GetPlayerState();
|
||||||
|
if (!PS)
|
||||||
|
{
|
||||||
|
// Race fix: pawn pode nao ter PlayerState ainda. Subsystem ja cacheou
|
||||||
|
// (LastLocalCharInfoCache); TryApplyCachedCharInfo aplica em
|
||||||
|
// HandleLocalSpawnReady ou em outra oportunidade.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (AZMMOPlayerState* ZMMOPs = Cast<AZMMOPlayerState>(PS))
|
||||||
|
{
|
||||||
|
ZMMOPs->SetCharInfo(CharName, GuildName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AZMMOPlayerCharacter::TryApplyCachedCharInfo()
|
||||||
|
{
|
||||||
|
if (!ZeusNetwork)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
int32 CachedEntityId = 0;
|
||||||
|
FString CachedCharName;
|
||||||
|
FString CachedGuildName;
|
||||||
|
if (!ZeusNetwork->TryGetLastLocalCharInfo(CachedEntityId, CachedCharName, CachedGuildName))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (APlayerState* PS = GetPlayerState())
|
||||||
|
{
|
||||||
|
if (AZMMOPlayerState* ZMMOPs = Cast<AZMMOPlayerState>(PS))
|
||||||
|
{
|
||||||
|
ZMMOPs->SetCharInfo(CachedCharName, CachedGuildName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void AZMMOPlayerCharacter::FlushInputAxisToServer(const float DeltaSeconds)
|
void AZMMOPlayerCharacter::FlushInputAxisToServer(const float DeltaSeconds)
|
||||||
{
|
{
|
||||||
ResolveZeusNetworkSubsystem();
|
ResolveZeusNetworkSubsystem();
|
||||||
|
|||||||
@@ -133,12 +133,19 @@ private:
|
|||||||
/**
|
/**
|
||||||
* Memoriza `EntityId` autoritativo e regista o pawn no `UZMMOWorldSubsystem`
|
* Memoriza `EntityId` autoritativo e regista o pawn no `UZMMOWorldSubsystem`
|
||||||
* para o registry partilhado por proxies remotos (filtra snapshots locais).
|
* para o registry partilhado por proxies remotos (filtra snapshots locais).
|
||||||
|
* Metadados publicos do char (Name/Guild) chegam via S_CHAR_INFO em
|
||||||
|
* HandleZeusCharInfo (servidor envia ANTES de S_SPAWN_PLAYER).
|
||||||
*/
|
*/
|
||||||
void HandleLocalSpawnReady(int32 InEntityId, FVector PosCm, float YawDeg, int64 ServerTimeMs);
|
void HandleLocalSpawnReady(int32 InEntityId, FVector PosCm, float YawDeg, int64 ServerTimeMs);
|
||||||
|
|
||||||
UFUNCTION()
|
UFUNCTION()
|
||||||
void HandleZeusPlayerSpawned(int32 InEntityId, bool bIsLocal, FVector PosCm, float YawDeg, int64 ServerTimeMs);
|
void HandleZeusPlayerSpawned(int32 InEntityId, bool bIsLocal, FVector PosCm, float YawDeg, int64 ServerTimeMs);
|
||||||
|
|
||||||
|
UFUNCTION()
|
||||||
|
void HandleZeusCharInfo(int32 InEntityId, const FString& CharName, const FString& GuildName);
|
||||||
|
|
||||||
|
void TryApplyCachedCharInfo();
|
||||||
|
|
||||||
UPROPERTY()
|
UPROPERTY()
|
||||||
TObjectPtr<UZeusNetworkSubsystem> ZeusNetwork;
|
TObjectPtr<UZeusNetworkSubsystem> ZeusNetwork;
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
#include "Components/ActorComponent.h"
|
#include "Components/ActorComponent.h"
|
||||||
#include "Misc/ConfigCacheIni.h"
|
#include "Misc/ConfigCacheIni.h"
|
||||||
|
#include "Net/UnrealNetwork.h"
|
||||||
#include "ZMMO.h"
|
#include "ZMMO.h"
|
||||||
|
|
||||||
AZMMOPlayerState::AZMMOPlayerState()
|
AZMMOPlayerState::AZMMOPlayerState()
|
||||||
@@ -72,6 +73,17 @@ AZMMOPlayerState::AZMMOPlayerState()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AZMMOPlayerState::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
|
||||||
|
{
|
||||||
|
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
|
||||||
|
DOREPLIFETIME(AZMMOPlayerState, ZMMOEntityId);
|
||||||
|
DOREPLIFETIME(AZMMOPlayerState, CharId);
|
||||||
|
DOREPLIFETIME(AZMMOPlayerState, CharName);
|
||||||
|
DOREPLIFETIME(AZMMOPlayerState, BaseLevel);
|
||||||
|
DOREPLIFETIME(AZMMOPlayerState, ClassId);
|
||||||
|
DOREPLIFETIME(AZMMOPlayerState, GuildName);
|
||||||
|
}
|
||||||
|
|
||||||
void AZMMOPlayerState::SetPublicIdentity(int64 InEntityId, const FString& InCharId,
|
void AZMMOPlayerState::SetPublicIdentity(int64 InEntityId, const FString& InCharId,
|
||||||
int32 InBaseLevel, int32 InClassId)
|
int32 InBaseLevel, int32 InClassId)
|
||||||
{
|
{
|
||||||
@@ -80,3 +92,9 @@ void AZMMOPlayerState::SetPublicIdentity(int64 InEntityId, const FString& InChar
|
|||||||
BaseLevel = InBaseLevel;
|
BaseLevel = InBaseLevel;
|
||||||
ClassId = InClassId;
|
ClassId = InClassId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AZMMOPlayerState::SetCharInfo(const FString& InCharName, const FString& InGuildName)
|
||||||
|
{
|
||||||
|
CharName = InCharName;
|
||||||
|
GuildName = InGuildName;
|
||||||
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
#include "Templates/SubclassOf.h"
|
#include "Templates/SubclassOf.h"
|
||||||
#include "ZMMOPlayerState.generated.h"
|
#include "ZMMOPlayerState.generated.h"
|
||||||
|
|
||||||
|
class FLifetimeProperty;
|
||||||
class UActorComponent;
|
class UActorComponent;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -52,36 +53,55 @@ public:
|
|||||||
AZMMOPlayerState();
|
AZMMOPlayerState();
|
||||||
|
|
||||||
// === Identidade publica ===
|
// === Identidade publica ===
|
||||||
|
//
|
||||||
|
// Todos os campos publicos sao Replicated — hoje o cliente UE5 roda em
|
||||||
|
// Standalone (servidor C++ standalone, sem replicacao UE5 ativa), entao
|
||||||
|
// a marca nao tem efeito em runtime; deixa a classe pronta caso o projeto
|
||||||
|
// passe a usar dedicated UE5 no futuro.
|
||||||
|
|
||||||
/** EntityId autoritativo do server. 0 ate o spawn local confirmar. */
|
/** EntityId autoritativo do server. 0 ate o spawn local confirmar. */
|
||||||
UPROPERTY(VisibleInstanceOnly, BlueprintReadOnly, Category = "ZMMO|Identity")
|
UPROPERTY(Replicated, VisibleInstanceOnly, BlueprintReadOnly, Category = "ZMMO|Identity")
|
||||||
int64 ZMMOEntityId = 0;
|
int64 ZMMOEntityId = 0;
|
||||||
|
|
||||||
/** CharId (BIGINT no DB; FString pra preservar 64 bits em Blueprint). */
|
/** CharId (BIGINT no DB; FString pra preservar 64 bits em Blueprint). */
|
||||||
UPROPERTY(VisibleInstanceOnly, BlueprintReadOnly, Category = "ZMMO|Identity")
|
UPROPERTY(Replicated, VisibleInstanceOnly, BlueprintReadOnly, Category = "ZMMO|Identity")
|
||||||
FString CharId;
|
FString CharId;
|
||||||
|
|
||||||
|
/** Nome do char vindo do DB (`characters.name`). Setado por
|
||||||
|
* AZMMOPlayerCharacter::HandleLocalSpawnReady com o payload do
|
||||||
|
* S_SPAWN_PLAYER. UI prefere isto a APlayerState::GetPlayerName(). */
|
||||||
|
UPROPERTY(Replicated, VisibleInstanceOnly, BlueprintReadOnly, Category = "ZMMO|Identity")
|
||||||
|
FString CharName;
|
||||||
|
|
||||||
// === Progressao publica ===
|
// === Progressao publica ===
|
||||||
|
|
||||||
UPROPERTY(VisibleInstanceOnly, BlueprintReadOnly, Category = "ZMMO|Progress")
|
UPROPERTY(Replicated, VisibleInstanceOnly, BlueprintReadOnly, Category = "ZMMO|Progress")
|
||||||
int32 BaseLevel = 1;
|
int32 BaseLevel = 1;
|
||||||
|
|
||||||
UPROPERTY(VisibleInstanceOnly, BlueprintReadOnly, Category = "ZMMO|Progress")
|
UPROPERTY(Replicated, VisibleInstanceOnly, BlueprintReadOnly, Category = "ZMMO|Progress")
|
||||||
int32 ClassId = 0;
|
int32 ClassId = 0;
|
||||||
|
|
||||||
// === Guild (futuro) ===
|
// === Guild (futuro) ===
|
||||||
|
|
||||||
UPROPERTY(VisibleInstanceOnly, BlueprintReadOnly, Category = "ZMMO|Guild")
|
UPROPERTY(Replicated, VisibleInstanceOnly, BlueprintReadOnly, Category = "ZMMO|Guild")
|
||||||
FString GuildName;
|
FString GuildName;
|
||||||
|
|
||||||
|
virtual void GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const override;
|
||||||
|
|
||||||
// === Helpers ===
|
// === Helpers ===
|
||||||
|
|
||||||
/// Atualizado quando S_ATTRIBUTE_SNAPSHOT_FULL chega — UZMMOHudWidget chama
|
/// Setado por AZMMOPlayerCharacter::HandleLocalSpawnReady (pose/EntityId)
|
||||||
/// isso pra refletir os dados publicos do snapshot no PlayerState.
|
/// e atualizado por handlers de snapshot/level up para refletir dados
|
||||||
|
/// publicos do char no PlayerState.
|
||||||
UFUNCTION(BlueprintCallable, Category = "ZMMO|Identity")
|
UFUNCTION(BlueprintCallable, Category = "ZMMO|Identity")
|
||||||
void SetPublicIdentity(int64 InEntityId, const FString& InCharId,
|
void SetPublicIdentity(int64 InEntityId, const FString& InCharId,
|
||||||
int32 InBaseLevel, int32 InClassId);
|
int32 InBaseLevel, int32 InClassId);
|
||||||
|
|
||||||
|
/// Setado pelo handler do S_CHAR_INFO (chega antes de S_SPAWN_PLAYER) —
|
||||||
|
/// metadados publicos do char vindos do servidor.
|
||||||
|
UFUNCTION(BlueprintCallable, Category = "ZMMO|Identity")
|
||||||
|
void SetCharInfo(const FString& InCharName, const FString& InGuildName);
|
||||||
|
|
||||||
/// Template helper: PS->GetZMMOComponent<UZMMOAttributeComponent>()
|
/// Template helper: PS->GetZMMOComponent<UZMMOAttributeComponent>()
|
||||||
template <typename T>
|
template <typename T>
|
||||||
T* GetZMMOComponent() const
|
T* GetZMMOComponent() const
|
||||||
|
|||||||
10
Source/ZMMO/Game/UI/Common/UILoadingProfilesDataAsset.cpp
Normal file
10
Source/ZMMO/Game/UI/Common/UILoadingProfilesDataAsset.cpp
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
#include "UILoadingProfilesDataAsset.h"
|
||||||
|
|
||||||
|
FZMMOLoadingProfile UZMMOLoadingProfilesDataAsset::GetProfile(EZMMOLoadingContext Context) const
|
||||||
|
{
|
||||||
|
if (const FZMMOLoadingProfile* Found = Profiles.Find(Context))
|
||||||
|
{
|
||||||
|
return *Found;
|
||||||
|
}
|
||||||
|
return FZMMOLoadingProfile();
|
||||||
|
}
|
||||||
29
Source/ZMMO/Game/UI/Common/UILoadingProfilesDataAsset.h
Normal file
29
Source/ZMMO/Game/UI/Common/UILoadingProfilesDataAsset.h
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "CoreMinimal.h"
|
||||||
|
#include "Engine/DataAsset.h"
|
||||||
|
#include "UI/LoadingTypes.h"
|
||||||
|
#include "UILoadingProfilesDataAsset.generated.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mapa data-driven contexto → perfil (etapas + título). Designer edita o
|
||||||
|
* asset; código consulta por EZMMOLoadingContext na hora de configurar a
|
||||||
|
* tela. Asset canônico: DA_LoadingProfiles em /Game/ZMMO/Data/UI/Loading/.
|
||||||
|
*
|
||||||
|
* Configurado em DefaultGame.ini:
|
||||||
|
* [/Script/ZMMO.UIFrontEndFlowSubsystem]
|
||||||
|
* LoadingProfilesAsset=/Game/ZMMO/Data/UI/Loading/DA_LoadingProfiles.DA_LoadingProfiles
|
||||||
|
*/
|
||||||
|
UCLASS(BlueprintType)
|
||||||
|
class ZMMO_API UZMMOLoadingProfilesDataAsset : public UDataAsset
|
||||||
|
{
|
||||||
|
GENERATED_BODY()
|
||||||
|
|
||||||
|
public:
|
||||||
|
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Loading")
|
||||||
|
TMap<EZMMOLoadingContext, FZMMOLoadingProfile> Profiles;
|
||||||
|
|
||||||
|
/** Retorna o perfil do contexto ou um perfil vazio (sem etapas). */
|
||||||
|
UFUNCTION(BlueprintCallable, Category = "Loading")
|
||||||
|
FZMMOLoadingProfile GetProfile(EZMMOLoadingContext Context) const;
|
||||||
|
};
|
||||||
235
Source/ZMMO/Game/UI/Common/UILoadingScreen_Base.cpp
Normal file
235
Source/ZMMO/Game/UI/Common/UILoadingScreen_Base.cpp
Normal file
@@ -0,0 +1,235 @@
|
|||||||
|
#include "UILoadingScreen_Base.h"
|
||||||
|
|
||||||
|
#include "Components/ProgressBar.h"
|
||||||
|
#include "CommonTextBlock.h"
|
||||||
|
#include "Engine/DataTable.h"
|
||||||
|
#include "Engine/World.h"
|
||||||
|
#include "TimerManager.h"
|
||||||
|
#include "UILoadingProfilesDataAsset.h"
|
||||||
|
#include "UILoadingTipRow.h"
|
||||||
|
|
||||||
|
void UUILoadingScreen_Base::Configure(EZMMOLoadingContext InContext,
|
||||||
|
UZMMOLoadingProfilesDataAsset* Profiles,
|
||||||
|
UDataTable* TipsTable)
|
||||||
|
{
|
||||||
|
Context_ = InContext;
|
||||||
|
bCompleteFired_ = false;
|
||||||
|
|
||||||
|
// Perfil (etapas + título).
|
||||||
|
Profile_ = (Profiles != nullptr)
|
||||||
|
? Profiles->GetProfile(InContext)
|
||||||
|
: FZMMOLoadingProfile();
|
||||||
|
|
||||||
|
StepStatus_.Reset();
|
||||||
|
for (const FZMMOLoadingStepDef& Step : Profile_.Steps)
|
||||||
|
{
|
||||||
|
StepStatus_.Add(Step.StepId, EZMMOLoadingStepStatus::Pending);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pool de dicas filtrada por contexto (None = qualquer).
|
||||||
|
TipPool_.Reset();
|
||||||
|
if (TipsTable != nullptr)
|
||||||
|
{
|
||||||
|
TipsTable->ForeachRow<FZMMOLoadingTipRow>(TEXT("UUILoadingScreen_Base::Configure"),
|
||||||
|
[this](const FName& /*Key*/, const FZMMOLoadingTipRow& Row)
|
||||||
|
{
|
||||||
|
if (Row.Context == EZMMOLoadingContext::None || Row.Context == Context_)
|
||||||
|
{
|
||||||
|
if (!Row.Tip.IsEmpty())
|
||||||
|
{
|
||||||
|
TipPool_.Add(Row.Tip);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
NextTipIndex_ = 0;
|
||||||
|
|
||||||
|
RefreshUI();
|
||||||
|
|
||||||
|
if (IsActivated())
|
||||||
|
{
|
||||||
|
StartTipTimer();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void UUILoadingScreen_Base::NativeOnActivated()
|
||||||
|
{
|
||||||
|
Super::NativeOnActivated();
|
||||||
|
RefreshUI();
|
||||||
|
StartTipTimer();
|
||||||
|
}
|
||||||
|
|
||||||
|
void UUILoadingScreen_Base::NativeOnDeactivated()
|
||||||
|
{
|
||||||
|
StopTipTimer();
|
||||||
|
Super::NativeOnDeactivated();
|
||||||
|
}
|
||||||
|
|
||||||
|
void UUILoadingScreen_Base::MarkStepRunning(FName StepId)
|
||||||
|
{
|
||||||
|
if (EZMMOLoadingStepStatus* Status = StepStatus_.Find(StepId))
|
||||||
|
{
|
||||||
|
// Já Done não regride.
|
||||||
|
if (*Status != EZMMOLoadingStepStatus::Done && *Status != EZMMOLoadingStepStatus::Failed)
|
||||||
|
{
|
||||||
|
*Status = EZMMOLoadingStepStatus::Running;
|
||||||
|
RefreshUI();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void UUILoadingScreen_Base::MarkStepDone(FName StepId)
|
||||||
|
{
|
||||||
|
if (EZMMOLoadingStepStatus* Status = StepStatus_.Find(StepId))
|
||||||
|
{
|
||||||
|
*Status = EZMMOLoadingStepStatus::Done;
|
||||||
|
RefreshUI();
|
||||||
|
|
||||||
|
if (!bCompleteFired_ && AreAllStepsDone())
|
||||||
|
{
|
||||||
|
bCompleteFired_ = true;
|
||||||
|
OnLoadingComplete.Broadcast();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void UUILoadingScreen_Base::MarkStepFailed(FName StepId, const FText& Reason)
|
||||||
|
{
|
||||||
|
if (EZMMOLoadingStepStatus* Status = StepStatus_.Find(StepId))
|
||||||
|
{
|
||||||
|
*Status = EZMMOLoadingStepStatus::Failed;
|
||||||
|
if (Text_Status && !Reason.IsEmpty())
|
||||||
|
{
|
||||||
|
Text_Status->SetText(Reason);
|
||||||
|
}
|
||||||
|
RefreshUI();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool UUILoadingScreen_Base::AreAllStepsDone() const
|
||||||
|
{
|
||||||
|
if (Profile_.Steps.Num() == 0)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for (const FZMMOLoadingStepDef& Step : Profile_.Steps)
|
||||||
|
{
|
||||||
|
const EZMMOLoadingStepStatus* Status = StepStatus_.Find(Step.StepId);
|
||||||
|
if (Status == nullptr || *Status != EZMMOLoadingStepStatus::Done)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int32 UUILoadingScreen_Base::FindStepIndex(FName StepId) const
|
||||||
|
{
|
||||||
|
for (int32 i = 0; i < Profile_.Steps.Num(); ++i)
|
||||||
|
{
|
||||||
|
if (Profile_.Steps[i].StepId == StepId)
|
||||||
|
{
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return INDEX_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
EZMMOLoadingStepStatus UUILoadingScreen_Base::GetStepStatus(int32 Index) const
|
||||||
|
{
|
||||||
|
if (!Profile_.Steps.IsValidIndex(Index)) return EZMMOLoadingStepStatus::Pending;
|
||||||
|
const EZMMOLoadingStepStatus* Status = StepStatus_.Find(Profile_.Steps[Index].StepId);
|
||||||
|
return Status ? *Status : EZMMOLoadingStepStatus::Pending;
|
||||||
|
}
|
||||||
|
|
||||||
|
FText UUILoadingScreen_Base::ComputeCurrentStatusText() const
|
||||||
|
{
|
||||||
|
// Prioridade: Running > último Done > primeiro Pending > Status default.
|
||||||
|
int32 LastDone = INDEX_NONE;
|
||||||
|
for (int32 i = 0; i < Profile_.Steps.Num(); ++i)
|
||||||
|
{
|
||||||
|
const EZMMOLoadingStepStatus St = GetStepStatus(i);
|
||||||
|
if (St == EZMMOLoadingStepStatus::Running)
|
||||||
|
{
|
||||||
|
return Profile_.Steps[i].Label;
|
||||||
|
}
|
||||||
|
if (St == EZMMOLoadingStepStatus::Done)
|
||||||
|
{
|
||||||
|
LastDone = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (LastDone != INDEX_NONE)
|
||||||
|
{
|
||||||
|
return Profile_.Steps[LastDone].Label;
|
||||||
|
}
|
||||||
|
if (Profile_.Steps.Num() > 0)
|
||||||
|
{
|
||||||
|
return Profile_.Steps[0].Label;
|
||||||
|
}
|
||||||
|
return StatusDefault;
|
||||||
|
}
|
||||||
|
|
||||||
|
float UUILoadingScreen_Base::ComputeProgress01() const
|
||||||
|
{
|
||||||
|
const int32 N = Profile_.Steps.Num();
|
||||||
|
if (N == 0) return 0.f;
|
||||||
|
float Acc = 0.f;
|
||||||
|
for (int32 i = 0; i < N; ++i)
|
||||||
|
{
|
||||||
|
const EZMMOLoadingStepStatus St = GetStepStatus(i);
|
||||||
|
if (St == EZMMOLoadingStepStatus::Done) Acc += 1.f;
|
||||||
|
else if (St == EZMMOLoadingStepStatus::Running) Acc += 0.5f;
|
||||||
|
}
|
||||||
|
return FMath::Clamp(Acc / static_cast<float>(N), 0.f, 1.f);
|
||||||
|
}
|
||||||
|
|
||||||
|
void UUILoadingScreen_Base::RefreshUI()
|
||||||
|
{
|
||||||
|
if (Text_Title)
|
||||||
|
{
|
||||||
|
const FText& Title = Profile_.TitleOverride.IsEmpty() ? TitleDefault : Profile_.TitleOverride;
|
||||||
|
Text_Title->SetText(Title);
|
||||||
|
}
|
||||||
|
if (Text_Status)
|
||||||
|
{
|
||||||
|
Text_Status->SetText(ComputeCurrentStatusText());
|
||||||
|
}
|
||||||
|
if (ProgressBar_Progress)
|
||||||
|
{
|
||||||
|
ProgressBar_Progress->SetPercent(ComputeProgress01());
|
||||||
|
}
|
||||||
|
if (Text_Tip && TipPool_.Num() > 0 && Text_Tip->GetText().IsEmpty())
|
||||||
|
{
|
||||||
|
// Primeira dica na inicialização (timer cuida das próximas).
|
||||||
|
Text_Tip->SetText(TipPool_[0]);
|
||||||
|
NextTipIndex_ = TipPool_.Num() > 1 ? 1 : 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void UUILoadingScreen_Base::RotateTip()
|
||||||
|
{
|
||||||
|
if (!Text_Tip || TipPool_.Num() == 0) return;
|
||||||
|
Text_Tip->SetText(TipPool_[NextTipIndex_]);
|
||||||
|
NextTipIndex_ = (NextTipIndex_ + 1) % TipPool_.Num();
|
||||||
|
}
|
||||||
|
|
||||||
|
void UUILoadingScreen_Base::StartTipTimer()
|
||||||
|
{
|
||||||
|
StopTipTimer();
|
||||||
|
if (TipRotationIntervalSeconds <= 0.f || TipPool_.Num() <= 1) return;
|
||||||
|
if (UWorld* W = GetWorld())
|
||||||
|
{
|
||||||
|
W->GetTimerManager().SetTimer(TipTimerHandle_, this,
|
||||||
|
&UUILoadingScreen_Base::RotateTip,
|
||||||
|
TipRotationIntervalSeconds, /*bLoop=*/true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void UUILoadingScreen_Base::StopTipTimer()
|
||||||
|
{
|
||||||
|
if (UWorld* W = GetWorld())
|
||||||
|
{
|
||||||
|
W->GetTimerManager().ClearTimer(TipTimerHandle_);
|
||||||
|
}
|
||||||
|
TipTimerHandle_.Invalidate();
|
||||||
|
}
|
||||||
124
Source/ZMMO/Game/UI/Common/UILoadingScreen_Base.h
Normal file
124
Source/ZMMO/Game/UI/Common/UILoadingScreen_Base.h
Normal file
@@ -0,0 +1,124 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "CoreMinimal.h"
|
||||||
|
#include "UIActivatableScreen_Base.h"
|
||||||
|
#include "UI/LoadingTypes.h"
|
||||||
|
#include "UILoadingScreen_Base.generated.h"
|
||||||
|
|
||||||
|
class UCommonTextBlock;
|
||||||
|
class UProgressBar;
|
||||||
|
class UDataTable;
|
||||||
|
class UZMMOLoadingProfilesDataAsset;
|
||||||
|
|
||||||
|
DECLARE_DYNAMIC_MULTICAST_DELEGATE(FZMMOOnLoadingComplete);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tela de loading genérica. Reusada pelo FrontEnd (handoff CharServer →
|
||||||
|
* WorldServer) e pelo InGame (entrada em instance/dungeon, respawn). Cada
|
||||||
|
* uso passa um EZMMOLoadingContext em Configure(), que resolve o perfil
|
||||||
|
* no UZMMOLoadingProfilesDataAsset (lista de etapas + título).
|
||||||
|
*
|
||||||
|
* Quem orquestra (UIFrontEndFlowSubsystem, futuro UIInGameFlowSubsystem)
|
||||||
|
* assina os próprios eventos (HandlePostLoadMap, OnPlayerSpawned, etc) e
|
||||||
|
* chama MarkStepDone(StepId) — server NÃO envia opcode de progresso (ver
|
||||||
|
* [[project_ui_loading_dynamic]]).
|
||||||
|
*
|
||||||
|
* Dismiss: a tela emite OnLoadingComplete quando todas as etapas estão
|
||||||
|
* Done. Caller decide quando popar (geralmente após pequeno fade-out).
|
||||||
|
*
|
||||||
|
* WBP concreto herda DIRETO desta classe. Subclasses C++ específicas só
|
||||||
|
* surgem quando houver necessidade real de estilo divergente.
|
||||||
|
*/
|
||||||
|
UCLASS(Abstract, Blueprintable)
|
||||||
|
class ZMMO_API UUILoadingScreen_Base : public UUIActivatableScreen_Base
|
||||||
|
{
|
||||||
|
GENERATED_BODY()
|
||||||
|
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Configura a tela: carrega o perfil do contexto, popula a pool de
|
||||||
|
* dicas filtrada e reseta estado das etapas. Pode ser chamado depois
|
||||||
|
* de NativeOnActivated (estado é reaplicado em RefreshUI).
|
||||||
|
*/
|
||||||
|
UFUNCTION(BlueprintCallable, Category = "Loading")
|
||||||
|
void Configure(EZMMOLoadingContext InContext,
|
||||||
|
UZMMOLoadingProfilesDataAsset* Profiles,
|
||||||
|
UDataTable* TipsTable);
|
||||||
|
|
||||||
|
UFUNCTION(BlueprintCallable, Category = "Loading")
|
||||||
|
void MarkStepRunning(FName StepId);
|
||||||
|
|
||||||
|
UFUNCTION(BlueprintCallable, Category = "Loading")
|
||||||
|
void MarkStepDone(FName StepId);
|
||||||
|
|
||||||
|
UFUNCTION(BlueprintCallable, Category = "Loading")
|
||||||
|
void MarkStepFailed(FName StepId, const FText& Reason);
|
||||||
|
|
||||||
|
UFUNCTION(BlueprintPure, Category = "Loading")
|
||||||
|
bool AreAllStepsDone() const;
|
||||||
|
|
||||||
|
/** Disparado UMA vez quando todas as etapas viram Done. */
|
||||||
|
UPROPERTY(BlueprintAssignable, Category = "Loading")
|
||||||
|
FZMMOOnLoadingComplete OnLoadingComplete;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual void NativeOnActivated() override;
|
||||||
|
virtual void NativeOnDeactivated() override;
|
||||||
|
|
||||||
|
/** Reaplica todo o estado aos widgets bindados (texto, progress, tip). */
|
||||||
|
void RefreshUI();
|
||||||
|
void RotateTip();
|
||||||
|
void StartTipTimer();
|
||||||
|
void StopTipTimer();
|
||||||
|
|
||||||
|
/** Acha índice da etapa por StepId; -1 se não existir. */
|
||||||
|
int32 FindStepIndex(FName StepId) const;
|
||||||
|
|
||||||
|
/** Status atual da etapa em [Steps_]; Pending se não rastreada. */
|
||||||
|
EZMMOLoadingStepStatus GetStepStatus(int32 Index) const;
|
||||||
|
|
||||||
|
/** Texto exibido em Text_Status — etapa Running atual ou última Done. */
|
||||||
|
FText ComputeCurrentStatusText() const;
|
||||||
|
|
||||||
|
/** Progresso 0..1: #Done / max(1,#Steps). Running conta como meio passo. */
|
||||||
|
float ComputeProgress01() const;
|
||||||
|
|
||||||
|
// ---- Bind widgets (BindWidgetOptional — WBP pode omitir qualquer) ----
|
||||||
|
UPROPERTY(meta = (BindWidgetOptional))
|
||||||
|
TObjectPtr<UCommonTextBlock> Text_Title;
|
||||||
|
|
||||||
|
UPROPERTY(meta = (BindWidgetOptional))
|
||||||
|
TObjectPtr<UCommonTextBlock> Text_Status;
|
||||||
|
|
||||||
|
UPROPERTY(meta = (BindWidgetOptional))
|
||||||
|
TObjectPtr<UProgressBar> ProgressBar_Progress;
|
||||||
|
|
||||||
|
UPROPERTY(meta = (BindWidgetOptional))
|
||||||
|
TObjectPtr<UCommonTextBlock> Text_Tip;
|
||||||
|
|
||||||
|
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Loading")
|
||||||
|
FText TitleDefault = FText::FromString(TEXT("Carregando"));
|
||||||
|
|
||||||
|
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Loading")
|
||||||
|
FText StatusDefault = FText::FromString(TEXT("Entrando no mundo..."));
|
||||||
|
|
||||||
|
/** Intervalo entre dicas. 0 = sem rotação (mostra a primeira dica fixa). */
|
||||||
|
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Loading")
|
||||||
|
float TipRotationIntervalSeconds = 6.f;
|
||||||
|
|
||||||
|
private:
|
||||||
|
EZMMOLoadingContext Context_ = EZMMOLoadingContext::None;
|
||||||
|
|
||||||
|
UPROPERTY(Transient)
|
||||||
|
FZMMOLoadingProfile Profile_;
|
||||||
|
|
||||||
|
UPROPERTY(Transient)
|
||||||
|
TMap<FName, EZMMOLoadingStepStatus> StepStatus_;
|
||||||
|
|
||||||
|
UPROPERTY(Transient)
|
||||||
|
TArray<FText> TipPool_;
|
||||||
|
|
||||||
|
int32 NextTipIndex_ = 0;
|
||||||
|
bool bCompleteFired_ = false;
|
||||||
|
FTimerHandle TipTimerHandle_;
|
||||||
|
};
|
||||||
30
Source/ZMMO/Game/UI/Common/UILoadingTipRow.h
Normal file
30
Source/ZMMO/Game/UI/Common/UILoadingTipRow.h
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "CoreMinimal.h"
|
||||||
|
#include "Engine/DataTable.h"
|
||||||
|
#include "GameplayTagContainer.h"
|
||||||
|
#include "UI/LoadingTypes.h"
|
||||||
|
#include "UILoadingTipRow.generated.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Row do DT_LoadingTips. Cada row = uma dica mostrada (rotaciona durante o
|
||||||
|
* loading). FText é localizável (basta extrair pelo gather de tradução do
|
||||||
|
* UE). Filtra por contexto: Context=None significa "qualquer loading".
|
||||||
|
*/
|
||||||
|
USTRUCT(BlueprintType)
|
||||||
|
struct FZMMOLoadingTipRow : public FTableRowBase
|
||||||
|
{
|
||||||
|
GENERATED_BODY()
|
||||||
|
|
||||||
|
/** Texto da dica (localizável). */
|
||||||
|
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Loading")
|
||||||
|
FText Tip;
|
||||||
|
|
||||||
|
/** Restringe a dica a um contexto. None = todos. */
|
||||||
|
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Loading")
|
||||||
|
EZMMOLoadingContext Context = EZMMOLoadingContext::None;
|
||||||
|
|
||||||
|
/** Tag opcional pra categorizar (PvE/PvP/Crafting...) — futuro filtro. */
|
||||||
|
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Loading")
|
||||||
|
FGameplayTag Category;
|
||||||
|
};
|
||||||
@@ -5,6 +5,8 @@
|
|||||||
#include "UIFrontEndScreenSet.h"
|
#include "UIFrontEndScreenSet.h"
|
||||||
#include "UIManagerSubsystem.h"
|
#include "UIManagerSubsystem.h"
|
||||||
#include "UIPrimaryGameLayout_Base.h"
|
#include "UIPrimaryGameLayout_Base.h"
|
||||||
|
#include "UI/Common/UILoadingProfilesDataAsset.h"
|
||||||
|
#include "UI/Common/UILoadingScreen_Base.h"
|
||||||
#include "UI/UILayerTags.h"
|
#include "UI/UILayerTags.h"
|
||||||
#include "ZMMOGameInstance.h"
|
#include "ZMMOGameInstance.h"
|
||||||
#include "ZMMOThemeSubsystem.h"
|
#include "ZMMOThemeSubsystem.h"
|
||||||
@@ -212,13 +214,27 @@ void UUIFrontEndFlowSubsystem::ResolveAndPushScreen(EZMMOFrontEndState State)
|
|||||||
const FSoftObjectPath Path = Soft.ToSoftObjectPath();
|
const FSoftObjectPath Path = Soft.ToSoftObjectPath();
|
||||||
UAssetManager::GetStreamableManager().RequestAsyncLoad(
|
UAssetManager::GetStreamableManager().RequestAsyncLoad(
|
||||||
Path,
|
Path,
|
||||||
FStreamableDelegate::CreateWeakLambda(this, [this, Soft, Layer]()
|
FStreamableDelegate::CreateWeakLambda(this, [this, Soft, Layer, State]()
|
||||||
{
|
{
|
||||||
if (UUIManagerSubsystem* M = GetUIManager())
|
if (UUIManagerSubsystem* M = GetUIManager())
|
||||||
{
|
{
|
||||||
if (UClass* Cls = Soft.Get())
|
if (UClass* Cls = Soft.Get())
|
||||||
{
|
{
|
||||||
M->PushScreenToLayer(Layer, Cls);
|
UCommonActivatableWidget* Pushed = M->PushScreenToLayer(Layer, Cls);
|
||||||
|
|
||||||
|
// Loading: configura logo após o push e marca etapa "Travel"
|
||||||
|
// (chegamos aqui via HandleServerTravelRequested → SetState).
|
||||||
|
if (State == EZMMOFrontEndState::EnteringWorld)
|
||||||
|
{
|
||||||
|
if (UUILoadingScreen_Base* Loading = Cast<UUILoadingScreen_Base>(Pushed))
|
||||||
|
{
|
||||||
|
ActiveLoadingScreen = Loading;
|
||||||
|
Loading->Configure(EZMMOLoadingContext::FrontEndEnteringWorld,
|
||||||
|
GetLoadingProfiles(), GetLoadingTips());
|
||||||
|
Loading->OnLoadingComplete.AddDynamic(this, &UUIFrontEndFlowSubsystem::HandleLoadingComplete);
|
||||||
|
Loading->MarkStepDone(TEXT("Travel"));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
@@ -283,6 +299,7 @@ void UUIFrontEndFlowSubsystem::BindNetwork()
|
|||||||
if (UZeusNetworkSubsystem* Zeus = GetZeusNetwork())
|
if (UZeusNetworkSubsystem* Zeus = GetZeusNetwork())
|
||||||
{
|
{
|
||||||
Zeus->OnServerTravelRequested.AddDynamic(this, &UUIFrontEndFlowSubsystem::HandleServerTravelRequested);
|
Zeus->OnServerTravelRequested.AddDynamic(this, &UUIFrontEndFlowSubsystem::HandleServerTravelRequested);
|
||||||
|
Zeus->OnPlayerSpawned.AddDynamic(this, &UUIFrontEndFlowSubsystem::HandlePlayerSpawned);
|
||||||
}
|
}
|
||||||
bNetBound = true;
|
bNetBound = true;
|
||||||
}
|
}
|
||||||
@@ -302,6 +319,7 @@ void UUIFrontEndFlowSubsystem::UnbindNetwork()
|
|||||||
if (UZeusNetworkSubsystem* Zeus = GetZeusNetwork())
|
if (UZeusNetworkSubsystem* Zeus = GetZeusNetwork())
|
||||||
{
|
{
|
||||||
Zeus->OnServerTravelRequested.RemoveDynamic(this, &UUIFrontEndFlowSubsystem::HandleServerTravelRequested);
|
Zeus->OnServerTravelRequested.RemoveDynamic(this, &UUIFrontEndFlowSubsystem::HandleServerTravelRequested);
|
||||||
|
Zeus->OnPlayerSpawned.RemoveDynamic(this, &UUIFrontEndFlowSubsystem::HandlePlayerSpawned);
|
||||||
}
|
}
|
||||||
bNetBound = false;
|
bNetBound = false;
|
||||||
}
|
}
|
||||||
@@ -390,13 +408,67 @@ void UUIFrontEndFlowSubsystem::HandleServerTravelRequested(const FString& MapNam
|
|||||||
|
|
||||||
void UUIFrontEndFlowSubsystem::HandlePostLoadMap(UWorld* /*LoadedWorld*/)
|
void UUIFrontEndFlowSubsystem::HandlePostLoadMap(UWorld* /*LoadedWorld*/)
|
||||||
{
|
{
|
||||||
if (bTravelingToWorld)
|
if (!bTravelingToWorld)
|
||||||
{
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Loading dinâmico: marca a etapa "MapLoaded" e espera o spawn antes de
|
||||||
|
// liberar o InWorld (a tela limpa via OnLoadingComplete). Fallback: se
|
||||||
|
// não há screen ativa (DA não configurado), comportamento legado —
|
||||||
|
// transita já pra InWorld pra não travar.
|
||||||
|
if (UUILoadingScreen_Base* Loading = ActiveLoadingScreen.Get())
|
||||||
|
{
|
||||||
|
Loading->MarkStepDone(TEXT("MapLoaded"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
bTravelingToWorld = false;
|
bTravelingToWorld = false;
|
||||||
SetState(EZMMOFrontEndState::InWorld);
|
SetState(EZMMOFrontEndState::InWorld);
|
||||||
|
}
|
||||||
|
|
||||||
|
void UUIFrontEndFlowSubsystem::HandlePlayerSpawned(int32 /*EntityId*/, bool bIsLocal,
|
||||||
|
FVector /*PosCm*/, float /*YawDeg*/,
|
||||||
|
int64 /*ServerTimeMs*/)
|
||||||
|
{
|
||||||
|
if (!bIsLocal) return;
|
||||||
|
if (UUILoadingScreen_Base* Loading = ActiveLoadingScreen.Get())
|
||||||
|
{
|
||||||
|
Loading->MarkStepDone(TEXT("Spawn"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void UUIFrontEndFlowSubsystem::HandleLoadingComplete()
|
||||||
|
{
|
||||||
|
if (UUILoadingScreen_Base* Loading = ActiveLoadingScreen.Get())
|
||||||
|
{
|
||||||
|
Loading->OnLoadingComplete.RemoveDynamic(this, &UUIFrontEndFlowSubsystem::HandleLoadingComplete);
|
||||||
|
}
|
||||||
|
ActiveLoadingScreen.Reset();
|
||||||
|
bTravelingToWorld = false;
|
||||||
|
SetState(EZMMOFrontEndState::InWorld);
|
||||||
|
}
|
||||||
|
|
||||||
|
UZMMOLoadingProfilesDataAsset* UUIFrontEndFlowSubsystem::GetLoadingProfiles()
|
||||||
|
{
|
||||||
|
if (LoadingProfiles) return LoadingProfiles;
|
||||||
|
if (!LoadingProfilesAsset.IsNull())
|
||||||
|
{
|
||||||
|
LoadingProfiles = LoadingProfilesAsset.LoadSynchronous();
|
||||||
|
}
|
||||||
|
return LoadingProfiles;
|
||||||
|
}
|
||||||
|
|
||||||
|
UDataTable* UUIFrontEndFlowSubsystem::GetLoadingTips()
|
||||||
|
{
|
||||||
|
if (LoadingTips) return LoadingTips;
|
||||||
|
if (!LoadingTipsAsset.IsNull())
|
||||||
|
{
|
||||||
|
LoadingTips = LoadingTipsAsset.LoadSynchronous();
|
||||||
|
}
|
||||||
|
return LoadingTips;
|
||||||
|
}
|
||||||
|
|
||||||
void UUIFrontEndFlowSubsystem::HandleServerHelloTheme(FName ThemeId)
|
void UUIFrontEndFlowSubsystem::HandleServerHelloTheme(FName ThemeId)
|
||||||
{
|
{
|
||||||
// Dívida técnica (D5): ainda não há gancho no ZeusNetworkSubsystem para o
|
// Dívida técnica (D5): ainda não há gancho no ZeusNetworkSubsystem para o
|
||||||
|
|||||||
@@ -8,6 +8,8 @@
|
|||||||
|
|
||||||
class UUIFrontEndScreenSet;
|
class UUIFrontEndScreenSet;
|
||||||
class UUIManagerSubsystem;
|
class UUIManagerSubsystem;
|
||||||
|
class UUILoadingScreen_Base;
|
||||||
|
class UZMMOLoadingProfilesDataAsset;
|
||||||
class UZeusNetworkSubsystem;
|
class UZeusNetworkSubsystem;
|
||||||
class UZeusCharServerSubsystem;
|
class UZeusCharServerSubsystem;
|
||||||
struct FZMMOMapDef;
|
struct FZMMOMapDef;
|
||||||
@@ -147,6 +149,24 @@ protected:
|
|||||||
UPROPERTY(Config, EditDefaultsOnly, Category = "FrontEnd|Maps")
|
UPROPERTY(Config, EditDefaultsOnly, Category = "FrontEnd|Maps")
|
||||||
TSoftObjectPtr<UDataTable> MapsTableAsset;
|
TSoftObjectPtr<UDataTable> MapsTableAsset;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DataAsset com os perfis de loading (etapas por contexto). Configure em
|
||||||
|
* DefaultGame.ini:
|
||||||
|
* [/Script/ZMMO.UIFrontEndFlowSubsystem]
|
||||||
|
* LoadingProfilesAsset=/Game/ZMMO/Data/UI/Loading/DA_LoadingProfiles.DA_LoadingProfiles
|
||||||
|
*/
|
||||||
|
UPROPERTY(Config, EditDefaultsOnly, Category = "FrontEnd|Loading")
|
||||||
|
TSoftObjectPtr<UZMMOLoadingProfilesDataAsset> LoadingProfilesAsset;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DataTable com dicas (rows = FZMMOLoadingTipRow). Opcional — vazio
|
||||||
|
* significa "sem dicas". Configure em DefaultGame.ini:
|
||||||
|
* [/Script/ZMMO.UIFrontEndFlowSubsystem]
|
||||||
|
* LoadingTipsAsset=/Game/ZMMO/Data/UI/Loading/DT_LoadingTips.DT_LoadingTips
|
||||||
|
*/
|
||||||
|
UPROPERTY(Config, EditDefaultsOnly, Category = "FrontEnd|Loading")
|
||||||
|
TSoftObjectPtr<UDataTable> LoadingTipsAsset;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void BindNetwork();
|
void BindNetwork();
|
||||||
void UnbindNetwork();
|
void UnbindNetwork();
|
||||||
@@ -170,8 +190,26 @@ private:
|
|||||||
UFUNCTION()
|
UFUNCTION()
|
||||||
void HandleServerTravelRequested(const FString& MapName, const FString& MapPath);
|
void HandleServerTravelRequested(const FString& MapName, const FString& MapPath);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Spawn do player local — fim da última etapa do loading EnteringWorld.
|
||||||
|
* Assinatura espelha FZeusOnPlayerSpawned (FiveParams).
|
||||||
|
*/
|
||||||
|
UFUNCTION()
|
||||||
|
void HandlePlayerSpawned(int32 EntityId, bool bIsLocal, FVector PosCm,
|
||||||
|
float YawDeg, int64 ServerTimeMs);
|
||||||
|
|
||||||
|
/** Disparado pela tela de loading quando todas as etapas viram Done. */
|
||||||
|
UFUNCTION()
|
||||||
|
void HandleLoadingComplete();
|
||||||
|
|
||||||
void HandlePostLoadMap(UWorld* LoadedWorld);
|
void HandlePostLoadMap(UWorld* LoadedWorld);
|
||||||
|
|
||||||
|
/** Resolve+carrega o DA de perfis (sync). */
|
||||||
|
UZMMOLoadingProfilesDataAsset* GetLoadingProfiles();
|
||||||
|
|
||||||
|
/** Resolve+carrega o DT de dicas (sync). */
|
||||||
|
UDataTable* GetLoadingTips();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Dívida técnica (D5): ServerHello traz ThemeId, mas o UZeusNetworkSubsystem
|
* Dívida técnica (D5): ServerHello traz ThemeId, mas o UZeusNetworkSubsystem
|
||||||
* ainda não expõe um delegate/getter dedicado. Quando expuser, ligar aqui
|
* ainda não expõe um delegate/getter dedicado. Quando expuser, ligar aqui
|
||||||
@@ -199,6 +237,18 @@ private:
|
|||||||
UPROPERTY(Transient)
|
UPROPERTY(Transient)
|
||||||
TObjectPtr<UUIFrontEndScreenSet> ScreenSet;
|
TObjectPtr<UUIFrontEndScreenSet> ScreenSet;
|
||||||
|
|
||||||
|
UPROPERTY(Transient)
|
||||||
|
TObjectPtr<UZMMOLoadingProfilesDataAsset> LoadingProfiles;
|
||||||
|
|
||||||
|
UPROPERTY(Transient)
|
||||||
|
TObjectPtr<UDataTable> LoadingTips;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tela de loading atualmente em Modal. Weak porque o stack do CommonUI
|
||||||
|
* é dono — se popar por outro caminho não queremos dangling.
|
||||||
|
*/
|
||||||
|
TWeakObjectPtr<UUILoadingScreen_Base> ActiveLoadingScreen;
|
||||||
|
|
||||||
bool bNetBound = false;
|
bool bNetBound = false;
|
||||||
bool bTravelingToWorld = false;
|
bool bTravelingToWorld = false;
|
||||||
FDelegateHandle PostLoadMapHandle;
|
FDelegateHandle PostLoadMapHandle;
|
||||||
|
|||||||
@@ -11,6 +11,7 @@
|
|||||||
#include "ZMMOAttributeComponent.h"
|
#include "ZMMOAttributeComponent.h"
|
||||||
#include "ZMMOJobDataAsset.h"
|
#include "ZMMOJobDataAsset.h"
|
||||||
#include "ZMMOJobsSubsystem.h"
|
#include "ZMMOJobsSubsystem.h"
|
||||||
|
#include "ZMMOPlayerState.h"
|
||||||
#include "UI/InGame/UIInGameFlowSubsystem.h"
|
#include "UI/InGame/UIInGameFlowSubsystem.h"
|
||||||
#include "UI/InGameTypes.h"
|
#include "UI/InGameTypes.h"
|
||||||
#include "UI/Widgets/UIPlayerStatus_BonusRow.h"
|
#include "UI/Widgets/UIPlayerStatus_BonusRow.h"
|
||||||
@@ -87,10 +88,20 @@ void UUIPlayerStatus_Window::BindToLocalPlayer()
|
|||||||
APlayerController* PC = World->GetFirstPlayerController();
|
APlayerController* PC = World->GetFirstPlayerController();
|
||||||
if (!PC || !PC->PlayerState) return;
|
if (!PC || !PC->PlayerState) return;
|
||||||
|
|
||||||
// CharName vem do PlayerState (estável por sessão) — popula uma vez.
|
// CharName vem do AZMMOPlayerState (setado pelo S_SPAWN_PLAYER no spawn
|
||||||
|
// local). Fallback pra APlayerState::GetPlayerName() se o cast falhar ou
|
||||||
|
// se CharName ainda nao chegou (race com handler que dispara o widget).
|
||||||
if (Text_CharName)
|
if (Text_CharName)
|
||||||
{
|
{
|
||||||
Text_CharName->SetText(FText::FromString(PC->PlayerState->GetPlayerName()));
|
FString DisplayName = PC->PlayerState->GetPlayerName();
|
||||||
|
if (const AZMMOPlayerState* ZMMOPs = Cast<AZMMOPlayerState>(PC->PlayerState))
|
||||||
|
{
|
||||||
|
if (!ZMMOPs->CharName.IsEmpty())
|
||||||
|
{
|
||||||
|
DisplayName = ZMMOPs->CharName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Text_CharName->SetText(FText::FromString(DisplayName));
|
||||||
}
|
}
|
||||||
|
|
||||||
UZMMOAttributeComponent* Comp = PC->PlayerState->FindComponentByClass<UZMMOAttributeComponent>();
|
UZMMOAttributeComponent* Comp = PC->PlayerState->FindComponentByClass<UZMMOAttributeComponent>();
|
||||||
|
|||||||
Reference in New Issue
Block a user