ServerSelect (Fase 1): - UIServerSelectScreen_Base: subscribe OnRawMessage + C_WORLD_LIST_REQUEST - WBP_ServerCard instanciado dinamicamente em GridPanel CardContainer (2 cols) - UIServerCard_Base: SetFromEntry + OnCardPressed delegate + UI_Button_Master - Push real-time S_WORLD_STATUS_UPDATE (opcode 2062) atualiza card in-place - FZMMOWorldEntry struct, EZMMOWorldState enum no wire (offline/online/maint) - Sem hardcap de slots — cards usam toda largura via ColumnFill weights Lobby/CharSelect (Fase 1.5): - UIUserLobbyScreen_Base + WBP_UserLobby (WidgetSwitcher: lista + create) - C_CHAR_LIST_REQUEST filtrado por SelectedWorldId do FlowSubsystem - UICharCard_Base + WBP_CharCard com Select/Delete + Accept/Cancel quando delete agendado; Text_DeleteCountdown atualiza em tempo real (timer 1s) - UICharacterCreatePage_Base + WBP_CharacterCreate (Name + ComboBox class) - Handlers S_CHAR_SELECT_OK (handoff parse), CHAR_CREATE_OK/REJECT, CHAR_DELETE_ACK/ACCEPT_ACK/CANCEL_ACK (refresh lista) Auxiliares + fixes: - ARQUITETURA_SERVER_SELECT.md + ARQUITETURA_CHARACTER_MODEL.md - UIFrontEndFlowSubsystem: SelectedWorldId state + transicao Lobby - DA_FrontEndScreenSet: Lobby -> WBP_UserLobby_C - UICheckBox_Base + UICommonText_Base (componentes auxiliares) - BSB_Button_Transparent.Disabled DrawAs=IMAGE -> NoDraw (fix bg branco) - WBP_ServerCard layout: HBox para chips Region/Ping/Status, SizeBox 10x10 no Dot verde (anti-overflow); SelfHitTestInvisible em containers (hover)
152 lines
4.4 KiB
C++
152 lines
4.4 KiB
C++
#include "UICharCard_Base.h"
|
|
|
|
#include "ZMMO.h"
|
|
#include "CommonTextBlock.h"
|
|
#include "Engine/World.h"
|
|
#include "TimerManager.h"
|
|
#include "UI/Widgets/UIButton_Base.h"
|
|
|
|
void UUICharCard_Base::NativeConstruct()
|
|
{
|
|
Super::NativeConstruct();
|
|
if (!bBound)
|
|
{
|
|
if (Btn_Select) Btn_Select->OnClicked().AddUObject(this, &UUICharCard_Base::HandleSelectClicked);
|
|
if (Btn_Delete) Btn_Delete->OnClicked().AddUObject(this, &UUICharCard_Base::HandleDeleteClicked);
|
|
if (Btn_AcceptDelete) Btn_AcceptDelete->OnClicked().AddUObject(this, &UUICharCard_Base::HandleAcceptDeleteClicked);
|
|
if (Btn_CancelDelete) Btn_CancelDelete->OnClicked().AddUObject(this, &UUICharCard_Base::HandleCancelDeleteClicked);
|
|
bBound = true;
|
|
}
|
|
}
|
|
|
|
void UUICharCard_Base::NativeDestruct()
|
|
{
|
|
StopCountdownTimer();
|
|
if (bBound)
|
|
{
|
|
if (Btn_Select) Btn_Select->OnClicked().RemoveAll(this);
|
|
if (Btn_Delete) Btn_Delete->OnClicked().RemoveAll(this);
|
|
if (Btn_AcceptDelete) Btn_AcceptDelete->OnClicked().RemoveAll(this);
|
|
if (Btn_CancelDelete) Btn_CancelDelete->OnClicked().RemoveAll(this);
|
|
bBound = false;
|
|
}
|
|
Super::NativeDestruct();
|
|
}
|
|
|
|
void UUICharCard_Base::SetFromSummary(const FZMMOCharSummary& InSummary)
|
|
{
|
|
Summary = InSummary;
|
|
const bool bDeleteScheduled = Summary.DeleteScheduledAtMs > 0;
|
|
|
|
if (Text_Name)
|
|
{
|
|
Text_Name->SetText(FText::FromString(Summary.Name));
|
|
}
|
|
if (Text_Level)
|
|
{
|
|
Text_Level->SetText(FText::FromString(FString::Printf(TEXT("Lv %d / %d"), Summary.BaseLevel, Summary.JobLevel)));
|
|
}
|
|
if (Text_Class)
|
|
{
|
|
Text_Class->SetText(FText::FromString(FString::Printf(TEXT("Classe %d"), Summary.ClassId)));
|
|
}
|
|
|
|
const ESlateVisibility ActionVis = bDeleteScheduled ? ESlateVisibility::Collapsed : ESlateVisibility::Visible;
|
|
const ESlateVisibility ConfirmVis = bDeleteScheduled ? ESlateVisibility::Visible : ESlateVisibility::Collapsed;
|
|
if (Btn_Select) Btn_Select->SetVisibility(ActionVis);
|
|
if (Btn_Delete) Btn_Delete->SetVisibility(ActionVis);
|
|
if (Btn_AcceptDelete) Btn_AcceptDelete->SetVisibility(ConfirmVis);
|
|
if (Btn_CancelDelete) Btn_CancelDelete->SetVisibility(ConfirmVis);
|
|
|
|
if (Text_DeleteCountdown)
|
|
{
|
|
Text_DeleteCountdown->SetVisibility(bDeleteScheduled
|
|
? ESlateVisibility::HitTestInvisible
|
|
: ESlateVisibility::Collapsed);
|
|
}
|
|
|
|
if (bDeleteScheduled)
|
|
{
|
|
TickCountdown();
|
|
StartCountdownTimer();
|
|
}
|
|
else
|
|
{
|
|
StopCountdownTimer();
|
|
}
|
|
|
|
OnSummaryApplied(bDeleteScheduled);
|
|
}
|
|
|
|
void UUICharCard_Base::TickCountdown()
|
|
{
|
|
if (!Text_DeleteCountdown || Summary.DeleteScheduledAtMs <= 0) return;
|
|
|
|
// FDateTime::UtcNow().ToUnixTimestamp() retorna segundos UNIX UTC.
|
|
// Multiplicamos por 1000 pra equiparar com Summary.DeleteScheduledAtMs (ms).
|
|
const int64 NowMs = FDateTime::UtcNow().ToUnixTimestamp() * 1000LL;
|
|
const int64 RemainingMs = Summary.DeleteScheduledAtMs - NowMs;
|
|
|
|
if (RemainingMs <= 0)
|
|
{
|
|
Text_DeleteCountdown->SetText(FText::FromString(TEXT("Pronto para deletar")));
|
|
// Pode habilitar visualmente o Btn_AcceptDelete aqui se quiser feedback
|
|
// (ja esta enabled por default; futuramente pode mudar variant cor).
|
|
StopCountdownTimer();
|
|
return;
|
|
}
|
|
|
|
const int64 TotalSec = RemainingMs / 1000;
|
|
if (TotalSec >= 60)
|
|
{
|
|
const int64 Mins = TotalSec / 60;
|
|
const int64 Secs = TotalSec % 60;
|
|
Text_DeleteCountdown->SetText(FText::FromString(FString::Printf(
|
|
TEXT("Delete em %lldm %02llds"), static_cast<long long>(Mins), static_cast<long long>(Secs))));
|
|
}
|
|
else
|
|
{
|
|
Text_DeleteCountdown->SetText(FText::FromString(FString::Printf(
|
|
TEXT("Delete em %llds"), static_cast<long long>(TotalSec))));
|
|
}
|
|
}
|
|
|
|
void UUICharCard_Base::StartCountdownTimer()
|
|
{
|
|
UWorld* W = GetWorld();
|
|
if (!W) return;
|
|
if (CountdownTimerHandle.IsValid()) return;
|
|
W->GetTimerManager().SetTimer(CountdownTimerHandle, this,
|
|
&UUICharCard_Base::TickCountdown, 1.0f, true /* loop */, 1.0f /* first delay */);
|
|
}
|
|
|
|
void UUICharCard_Base::StopCountdownTimer()
|
|
{
|
|
if (!CountdownTimerHandle.IsValid()) return;
|
|
if (UWorld* W = GetWorld())
|
|
{
|
|
W->GetTimerManager().ClearTimer(CountdownTimerHandle);
|
|
}
|
|
CountdownTimerHandle.Invalidate();
|
|
}
|
|
|
|
void UUICharCard_Base::HandleSelectClicked()
|
|
{
|
|
OnCardSelected.Broadcast(Summary.CharId);
|
|
}
|
|
|
|
void UUICharCard_Base::HandleDeleteClicked()
|
|
{
|
|
OnCardDeleteRequest.Broadcast(Summary.CharId);
|
|
}
|
|
|
|
void UUICharCard_Base::HandleAcceptDeleteClicked()
|
|
{
|
|
OnCardDeleteAccept.Broadcast(Summary.CharId);
|
|
}
|
|
|
|
void UUICharCard_Base::HandleCancelDeleteClicked()
|
|
{
|
|
OnCardDeleteCancel.Broadcast(Summary.CharId);
|
|
}
|