Compare commits
10 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 84f552b4fa | |||
| 1308338953 | |||
| 21d42d9846 | |||
| 2895889e2b | |||
| 1114ddbb11 | |||
| 9a430dabc3 | |||
| a3f2fa9c06 | |||
| 1f2441bde7 | |||
| 216e580548 | |||
| 9422522d6d |
@@ -91,3 +91,12 @@ bShouldAcquireMissingChunksOnLoad=False
|
|||||||
bShouldWarnAboutInvalidAssets=True
|
bShouldWarnAboutInvalidAssets=True
|
||||||
MetaDataTagsForAssetRegistry=()
|
MetaDataTagsForAssetRegistry=()
|
||||||
|
|
||||||
|
|
||||||
|
[/Script/GameplayAbilities.AbilitySystemGlobals]
|
||||||
|
bUseDebugTargetFromHud=true
|
||||||
|
|
||||||
|
[ConsoleVariables]
|
||||||
|
; mostra cooldowns, ativações, falhas
|
||||||
|
AbilitySystem.LogAbilityTriggers=1
|
||||||
|
; mostra effects aplicados/removidos com magnitude
|
||||||
|
AbilitySystem.DebugAbilityTags=1
|
||||||
@@ -5,9 +5,13 @@ GameplayTagList=(Tag="Zeus",DevComment="")
|
|||||||
GameplayTagList=(Tag="Zeus.Ability",DevComment="")
|
GameplayTagList=(Tag="Zeus.Ability",DevComment="")
|
||||||
GameplayTagList=(Tag="Zeus.Ability.Casting",DevComment="")
|
GameplayTagList=(Tag="Zeus.Ability.Casting",DevComment="")
|
||||||
GameplayTagList=(Tag="Zeus.Ability.Channeling",DevComment="")
|
GameplayTagList=(Tag="Zeus.Ability.Channeling",DevComment="")
|
||||||
|
GameplayTagList=(Tag="Zeus.Ability.Melee",DevComment="")
|
||||||
|
GameplayTagList=(Tag="Zeus.Ability.Melee.Kick",DevComment="")
|
||||||
GameplayTagList=(Tag="Zeus.Ability.Movement",DevComment="")
|
GameplayTagList=(Tag="Zeus.Ability.Movement",DevComment="")
|
||||||
GameplayTagList=(Tag="Zeus.Ability.Movement.Dash",DevComment="")
|
GameplayTagList=(Tag="Zeus.Ability.Movement.Dash",DevComment="")
|
||||||
GameplayTagList=(Tag="Zeus.Ability.Recovery",DevComment="")
|
GameplayTagList=(Tag="Zeus.Ability.Recovery",DevComment="")
|
||||||
|
GameplayTagList=(Tag="Zeus.Ability.Spell",DevComment="")
|
||||||
|
GameplayTagList=(Tag="Zeus.Ability.Spell.FireballRain",DevComment="")
|
||||||
GameplayTagList=(Tag="Zeus.Attribute",DevComment="")
|
GameplayTagList=(Tag="Zeus.Attribute",DevComment="")
|
||||||
GameplayTagList=(Tag="Zeus.Attribute.Agi",DevComment="")
|
GameplayTagList=(Tag="Zeus.Attribute.Agi",DevComment="")
|
||||||
GameplayTagList=(Tag="Zeus.Attribute.Aspd",DevComment="")
|
GameplayTagList=(Tag="Zeus.Attribute.Aspd",DevComment="")
|
||||||
@@ -32,6 +36,7 @@ GameplayTagList=(Tag="Zeus.Attribute.MaxSp",DevComment="")
|
|||||||
GameplayTagList=(Tag="Zeus.Attribute.Mdef",DevComment="")
|
GameplayTagList=(Tag="Zeus.Attribute.Mdef",DevComment="")
|
||||||
GameplayTagList=(Tag="Zeus.Attribute.SkillPoint",DevComment="")
|
GameplayTagList=(Tag="Zeus.Attribute.SkillPoint",DevComment="")
|
||||||
GameplayTagList=(Tag="Zeus.Attribute.Sp",DevComment="")
|
GameplayTagList=(Tag="Zeus.Attribute.Sp",DevComment="")
|
||||||
|
GameplayTagList=(Tag="Zeus.Attribute.Stamina",DevComment="")
|
||||||
GameplayTagList=(Tag="Zeus.Attribute.StatusPoint",DevComment="")
|
GameplayTagList=(Tag="Zeus.Attribute.StatusPoint",DevComment="")
|
||||||
GameplayTagList=(Tag="Zeus.Attribute.Str",DevComment="")
|
GameplayTagList=(Tag="Zeus.Attribute.Str",DevComment="")
|
||||||
GameplayTagList=(Tag="Zeus.Attribute.Vit",DevComment="")
|
GameplayTagList=(Tag="Zeus.Attribute.Vit",DevComment="")
|
||||||
@@ -46,7 +51,10 @@ GameplayTagList=(Tag="Zeus.Combo.Slot2",DevComment="")
|
|||||||
GameplayTagList=(Tag="Zeus.Combo.Slot3",DevComment="")
|
GameplayTagList=(Tag="Zeus.Combo.Slot3",DevComment="")
|
||||||
GameplayTagList=(Tag="Zeus.Combo.Window",DevComment="")
|
GameplayTagList=(Tag="Zeus.Combo.Window",DevComment="")
|
||||||
GameplayTagList=(Tag="Zeus.Cooldown",DevComment="")
|
GameplayTagList=(Tag="Zeus.Cooldown",DevComment="")
|
||||||
|
GameplayTagList=(Tag="Zeus.Cooldown.FireballRain",DevComment="")
|
||||||
GameplayTagList=(Tag="Zeus.Cue",DevComment="")
|
GameplayTagList=(Tag="Zeus.Cue",DevComment="")
|
||||||
|
GameplayTagList=(Tag="Zeus.Cue.Melee",DevComment="")
|
||||||
|
GameplayTagList=(Tag="Zeus.Cue.Melee.Kick",DevComment="")
|
||||||
GameplayTagList=(Tag="Zeus.Cue.Movement",DevComment="")
|
GameplayTagList=(Tag="Zeus.Cue.Movement",DevComment="")
|
||||||
GameplayTagList=(Tag="Zeus.Cue.Movement.Dash",DevComment="")
|
GameplayTagList=(Tag="Zeus.Cue.Movement.Dash",DevComment="")
|
||||||
GameplayTagList=(Tag="Zeus.Damage",DevComment="")
|
GameplayTagList=(Tag="Zeus.Damage",DevComment="")
|
||||||
@@ -60,10 +68,16 @@ GameplayTagList=(Tag="Zeus.Damage.Shadow",DevComment="")
|
|||||||
GameplayTagList=(Tag="Zeus.Damage.Water",DevComment="")
|
GameplayTagList=(Tag="Zeus.Damage.Water",DevComment="")
|
||||||
GameplayTagList=(Tag="Zeus.Damage.Wind",DevComment="")
|
GameplayTagList=(Tag="Zeus.Damage.Wind",DevComment="")
|
||||||
GameplayTagList=(Tag="Zeus.Effect",DevComment="")
|
GameplayTagList=(Tag="Zeus.Effect",DevComment="")
|
||||||
|
GameplayTagList=(Tag="Zeus.Effect.Buff",DevComment="")
|
||||||
|
GameplayTagList=(Tag="Zeus.Effect.Buff.StrUp",DevComment="")
|
||||||
GameplayTagList=(Tag="Zeus.Effect.Cooldown",DevComment="")
|
GameplayTagList=(Tag="Zeus.Effect.Cooldown",DevComment="")
|
||||||
GameplayTagList=(Tag="Zeus.Effect.Cooldown.Dash",DevComment="")
|
GameplayTagList=(Tag="Zeus.Effect.Cooldown.Dash",DevComment="")
|
||||||
|
GameplayTagList=(Tag="Zeus.Effect.Cooldown.FireballRain",DevComment="")
|
||||||
|
GameplayTagList=(Tag="Zeus.Effect.Cooldown.Kick",DevComment="")
|
||||||
GameplayTagList=(Tag="Zeus.Effect.Cost",DevComment="")
|
GameplayTagList=(Tag="Zeus.Effect.Cost",DevComment="")
|
||||||
GameplayTagList=(Tag="Zeus.Effect.Cost.Sp_10",DevComment="")
|
GameplayTagList=(Tag="Zeus.Effect.Cost.Sp_10",DevComment="")
|
||||||
|
GameplayTagList=(Tag="Zeus.Effect.Cost.Sp_50",DevComment="")
|
||||||
|
GameplayTagList=(Tag="Zeus.Effect.Cost.Stamina_5",DevComment="")
|
||||||
GameplayTagList=(Tag="Zeus.Status",DevComment="")
|
GameplayTagList=(Tag="Zeus.Status",DevComment="")
|
||||||
GameplayTagList=(Tag="Zeus.Status.Buff",DevComment="")
|
GameplayTagList=(Tag="Zeus.Status.Buff",DevComment="")
|
||||||
GameplayTagList=(Tag="Zeus.Status.CC",DevComment="")
|
GameplayTagList=(Tag="Zeus.Status.CC",DevComment="")
|
||||||
|
|||||||
Binary file not shown.
BIN
Content/Input/Actions/IA_Kick.uasset
Normal file
BIN
Content/Input/Actions/IA_Kick.uasset
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Content/ZMMO/Core/Player/BP_ZeusPlayerCharacter.uasset
Normal file
BIN
Content/ZMMO/Core/Player/BP_ZeusPlayerCharacter.uasset
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Content/ZMMO/GAS/Abilities/Kick/Animations/AS_Kick.uasset
Normal file
BIN
Content/ZMMO/GAS/Abilities/Kick/Animations/AS_Kick.uasset
Normal file
Binary file not shown.
BIN
Content/ZMMO/GAS/Abilities/Kick/GA_Kick.uasset
Normal file
BIN
Content/ZMMO/GAS/Abilities/Kick/GA_Kick.uasset
Normal file
Binary file not shown.
BIN
Content/ZMMO/GAS/Cues/GC_Kick.uasset
Normal file
BIN
Content/ZMMO/GAS/Cues/GC_Kick.uasset
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -173,6 +173,12 @@ void AZeusCharacter::Tick(const float DeltaSeconds)
|
|||||||
|
|
||||||
void AZeusCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
|
void AZeusCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
|
||||||
{
|
{
|
||||||
|
// IMPORTANTE: chamar Super:: pra que BPs filhos consigam auto-bindar
|
||||||
|
// nodes EnhancedInputAction no Event Graph (ex: GA_Kick / IA_Kick no
|
||||||
|
// BP_ThirdPersonCharacter). Sem isso, bindings em BP ficam orfaos --
|
||||||
|
// so' bindings C++ (Move/Look/Jump/Dash abaixo) recebem input.
|
||||||
|
Super::SetupPlayerInputComponent(PlayerInputComponent);
|
||||||
|
|
||||||
if (UEnhancedInputComponent* EnhancedInputComponent = Cast<UEnhancedInputComponent>(PlayerInputComponent))
|
if (UEnhancedInputComponent* EnhancedInputComponent = Cast<UEnhancedInputComponent>(PlayerInputComponent))
|
||||||
{
|
{
|
||||||
EnhancedInputComponent->BindAction(JumpAction, ETriggerEvent::Started, this, &AZeusCharacter::OnJumpPressed);
|
EnhancedInputComponent->BindAction(JumpAction, ETriggerEvent::Started, this, &AZeusCharacter::OnJumpPressed);
|
||||||
|
|||||||
@@ -5,10 +5,47 @@
|
|||||||
#include "ZeusPlayerController.h"
|
#include "ZeusPlayerController.h"
|
||||||
#include "ZeusPlayerState.h"
|
#include "ZeusPlayerState.h"
|
||||||
|
|
||||||
|
#include "UObject/ConstructorHelpers.h"
|
||||||
|
|
||||||
AZeusGameMode::AZeusGameMode()
|
AZeusGameMode::AZeusGameMode()
|
||||||
{
|
{
|
||||||
DefaultPawnClass = AZeusCharacter::StaticClass();
|
// DefaultPawnClass: usar o BP filho (BP_ThirdPersonCharacter) em vez do
|
||||||
|
// C++ puro. BPs filhos so' sao instanciados se o pawn possessed for da
|
||||||
|
// classe BP -- caso contrario o engine cria a classe C++ direta e perde
|
||||||
|
// TUDO que o designer fez no BP (EnhancedInputAction events, AnimBP wire,
|
||||||
|
// hooks de ability, componentes adicionados via editor).
|
||||||
|
//
|
||||||
|
// Fallback pra AZeusCharacter pura se o asset BP nao existir (asset
|
||||||
|
// renomeado/deletado por engano -- nao deve acontecer em prod).
|
||||||
|
static ConstructorHelpers::FClassFinder<APawn> DefaultPawnBP(
|
||||||
|
TEXT("/Game/ZMMO/Core/Player/BP_ZeusPlayerCharacter"));
|
||||||
|
if (DefaultPawnBP.Succeeded())
|
||||||
|
{
|
||||||
|
DefaultPawnClass = DefaultPawnBP.Class;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
DefaultPawnClass = AZeusCharacter::StaticClass();
|
||||||
|
}
|
||||||
|
|
||||||
PlayerControllerClass = AZeusPlayerController::StaticClass();
|
PlayerControllerClass = AZeusPlayerController::StaticClass();
|
||||||
PlayerStateClass = AZeusPlayerState::StaticClass();
|
|
||||||
|
// PlayerStateClass: usar BP filho pelo mesmo motivo do DefaultPawnClass --
|
||||||
|
// componentes BP-added (ex: UZeusGASComponent listado em DefaultGame.ini
|
||||||
|
// ComponentClasses), eventos no Event Graph e overrides do designer so'
|
||||||
|
// existem na classe BP. C++ direto (AZeusPlayerState) perde tudo isso.
|
||||||
|
// TODO: renomear o asset pra BP_ZeusPlayerState pra alinhar com a convencao
|
||||||
|
// (BP_ZeusPlayerCharacter) -- quando renomear, atualizar o path aqui.
|
||||||
|
static ConstructorHelpers::FClassFinder<APlayerState> PlayerStateBP(
|
||||||
|
TEXT("/Game/ZMMO/Core/Player/BP_PlayerState"));
|
||||||
|
if (PlayerStateBP.Succeeded())
|
||||||
|
{
|
||||||
|
PlayerStateClass = PlayerStateBP.Class;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
PlayerStateClass = AZeusPlayerState::StaticClass();
|
||||||
|
}
|
||||||
|
|
||||||
HUDClass = AZeusHUD::StaticClass();
|
HUDClass = AZeusHUD::StaticClass();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
#include "ZeusAOIComponent.h"
|
#include "ZeusAOIComponent.h"
|
||||||
|
|
||||||
#include "ZeusNetworkingClientSubsystem.h" // V1 canonico: OnDebugAoiInfo (6161) + EmitDebugAoiRequest (6160)
|
#include "ZeusNetworkingClientSubsystem.h" // V1 canonico: OnDebugAoiInfo (6161) + EmitDebugAoiRequest (6160)
|
||||||
|
#include "ZeusFrontierOverlayActor.h" // ator que desenha as paredes de fronteira
|
||||||
#include "ZMMONetLog.h" // Batch 2.5: LogZeusAOI (categoria do modulo ZMMO)
|
#include "ZMMONetLog.h" // Batch 2.5: LogZeusAOI (categoria do modulo ZMMO)
|
||||||
|
|
||||||
#include "DrawDebugHelpers.h"
|
#include "DrawDebugHelpers.h"
|
||||||
@@ -23,6 +24,8 @@ static TAutoConsoleVariable<int32> CVarShowHandoff(
|
|||||||
TEXT("zeus.debug.handoff"), 0, TEXT("Desenha a linha de handoff (placeholder)."), ECVF_Default);
|
TEXT("zeus.debug.handoff"), 0, TEXT("Desenha a linha de handoff (placeholder)."), ECVF_Default);
|
||||||
static TAutoConsoleVariable<int32> CVarShowDrift(
|
static TAutoConsoleVariable<int32> CVarShowDrift(
|
||||||
TEXT("zeus.debug.drift"), 0, TEXT("Desenha o drift autoritativo (TODO)."), ECVF_Default);
|
TEXT("zeus.debug.drift"), 0, TEXT("Desenha o drift autoritativo (TODO)."), ECVF_Default);
|
||||||
|
static TAutoConsoleVariable<int32> CVarShowFrontier(
|
||||||
|
TEXT("zeus.debug.frontier"), 0, TEXT("Desenha as paredes das fronteiras do mesh (own<->neighbor)."), ECVF_Default);
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
@@ -62,6 +65,14 @@ void UZeusAOIComponent::BeginPlay()
|
|||||||
if (UZeusNetworkingClientSubsystem* NetC = GI->GetSubsystem<UZeusNetworkingClientSubsystem>())
|
if (UZeusNetworkingClientSubsystem* NetC = GI->GetSubsystem<UZeusNetworkingClientSubsystem>())
|
||||||
{
|
{
|
||||||
AoiConfigHandle_ = NetC->OnDebugAoiInfo.AddUObject(this, &UZeusAOIComponent::HandleAoiConfig);
|
AoiConfigHandle_ = NetC->OnDebugAoiInfo.AddUObject(this, &UZeusAOIComponent::HandleAoiConfig);
|
||||||
|
FrontierConfigHandle_ = NetC->OnDebugFrontierInfo.AddUObject(this, &UZeusAOIComponent::HandleFrontierConfig);
|
||||||
|
|
||||||
|
// 1.4 -- visibility tracking. Delegates DYNAMIC do subsystem V1
|
||||||
|
// precisam AddDynamic + UFUNCTION nas handlers (assim como no
|
||||||
|
// ZeusWorldSubsystem que ja faz spawn/despawn de proxies).
|
||||||
|
NetC->OnEntitySpawned.AddDynamic(this, &UZeusAOIComponent::HandleEntitySpawnedV1);
|
||||||
|
NetC->OnEntityDespawned.AddDynamic(this, &UZeusAOIComponent::HandleEntityDespawnedV1);
|
||||||
|
NetC->OnSelfEntityAssigned.AddDynamic(this, &UZeusAOIComponent::HandleSelfEntityAssignedV1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -73,7 +84,7 @@ void UZeusAOIComponent::BeginPlay()
|
|||||||
|
|
||||||
void UZeusAOIComponent::EndPlay(const EEndPlayReason::Type EndPlayReason)
|
void UZeusAOIComponent::EndPlay(const EEndPlayReason::Type EndPlayReason)
|
||||||
{
|
{
|
||||||
if (AoiConfigHandle_.IsValid())
|
if (AoiConfigHandle_.IsValid() || FrontierConfigHandle_.IsValid())
|
||||||
{
|
{
|
||||||
if (UWorld* World = GetWorld())
|
if (UWorld* World = GetWorld())
|
||||||
{
|
{
|
||||||
@@ -81,11 +92,23 @@ void UZeusAOIComponent::EndPlay(const EEndPlayReason::Type EndPlayReason)
|
|||||||
{
|
{
|
||||||
if (UZeusNetworkingClientSubsystem* NetC = GI->GetSubsystem<UZeusNetworkingClientSubsystem>())
|
if (UZeusNetworkingClientSubsystem* NetC = GI->GetSubsystem<UZeusNetworkingClientSubsystem>())
|
||||||
{
|
{
|
||||||
NetC->OnDebugAoiInfo.Remove(AoiConfigHandle_);
|
if (AoiConfigHandle_.IsValid()) { NetC->OnDebugAoiInfo.Remove(AoiConfigHandle_); }
|
||||||
|
if (FrontierConfigHandle_.IsValid()) { NetC->OnDebugFrontierInfo.Remove(FrontierConfigHandle_); }
|
||||||
|
// 1.4 -- unbind dos delegates DYNAMIC de tracking.
|
||||||
|
NetC->OnEntitySpawned.RemoveDynamic(this, &UZeusAOIComponent::HandleEntitySpawnedV1);
|
||||||
|
NetC->OnEntityDespawned.RemoveDynamic(this, &UZeusAOIComponent::HandleEntityDespawnedV1);
|
||||||
|
NetC->OnSelfEntityAssigned.RemoveDynamic(this, &UZeusAOIComponent::HandleSelfEntityAssignedV1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
AoiConfigHandle_.Reset();
|
AoiConfigHandle_.Reset();
|
||||||
|
FrontierConfigHandle_.Reset();
|
||||||
|
}
|
||||||
|
VisibleEntityIds_.Empty();
|
||||||
|
if (FrontierActor_)
|
||||||
|
{
|
||||||
|
FrontierActor_->Destroy();
|
||||||
|
FrontierActor_ = nullptr;
|
||||||
}
|
}
|
||||||
Super::EndPlay(EndPlayReason);
|
Super::EndPlay(EndPlayReason);
|
||||||
}
|
}
|
||||||
@@ -95,6 +118,26 @@ void UZeusAOIComponent::TickComponent(float DeltaTime, ELevelTick TickType,
|
|||||||
{
|
{
|
||||||
Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
|
Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
|
||||||
|
|
||||||
|
// Frontier: sincroniza o overlay com o estado atual. Pega o liga/desliga via
|
||||||
|
// CVar zeus.debug.frontier (que nao passa por SetOverlayEnabled) e garante que
|
||||||
|
// ao desligar as paredes sejam REMOVIDAS (UpdateFrontierOverlay -> ClearOverlay).
|
||||||
|
if (ResolveOverlay(EZeusAOIOverlay::FrontierCells) != bFrontierApplied_)
|
||||||
|
{
|
||||||
|
UpdateFrontierOverlay();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Re-pede o frontier periodicamente enquanto ligado -> o overlay reflete
|
||||||
|
// split/merge da topologia dinamica (debug client-side, baixa freq, so' ON).
|
||||||
|
if (ResolveOverlay(EZeusAOIOverlay::FrontierCells))
|
||||||
|
{
|
||||||
|
FrontierRepollAccumSec_ += DeltaTime;
|
||||||
|
if (FrontierRepollAccumSec_ >= FrontierRepollIntervalSec)
|
||||||
|
{
|
||||||
|
FrontierRepollAccumSec_ = 0.0f;
|
||||||
|
RequestFrontierConfig();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (AnyOverlayActive())
|
if (AnyOverlayActive())
|
||||||
{
|
{
|
||||||
DrawOverlays();
|
DrawOverlays();
|
||||||
@@ -127,6 +170,64 @@ void UZeusAOIComponent::HandleAoiConfig(float InterestCm, float DespawnCm)
|
|||||||
DespawnCm, DespawnCm / 100.0f);
|
DespawnCm, DespawnCm / 100.0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void UZeusAOIComponent::RequestFrontierConfig()
|
||||||
|
{
|
||||||
|
if (UWorld* World = GetWorld())
|
||||||
|
{
|
||||||
|
if (UGameInstance* GI = World->GetGameInstance())
|
||||||
|
{
|
||||||
|
if (UZeusNetworkingClientSubsystem* NetC = GI->GetSubsystem<UZeusNetworkingClientSubsystem>())
|
||||||
|
{
|
||||||
|
NetC->EmitDebugFrontierRequest(); // V1: no-op se ainda nao Accepted
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void UZeusAOIComponent::HandleFrontierConfig(const ZeusV1::FDebugFrontierInfo& Info)
|
||||||
|
{
|
||||||
|
FrontierInfo_ = Info;
|
||||||
|
bFrontierReceived_ = true;
|
||||||
|
UE_LOG(LogZeusAOI, Display,
|
||||||
|
TEXT("Frontier cfg recv own=%d neighbors=%d"),
|
||||||
|
Info.OwnedCells.Num(), Info.NeighborCells.Num());
|
||||||
|
UpdateFrontierOverlay();
|
||||||
|
}
|
||||||
|
|
||||||
|
AZeusFrontierOverlayActor* UZeusAOIComponent::EnsureFrontierActor()
|
||||||
|
{
|
||||||
|
if (FrontierActor_) { return FrontierActor_; }
|
||||||
|
UWorld* World = GetWorld();
|
||||||
|
if (!World) { return nullptr; }
|
||||||
|
FActorSpawnParameters Params;
|
||||||
|
Params.Owner = GetOwner();
|
||||||
|
Params.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AlwaysSpawn;
|
||||||
|
FrontierActor_ = World->SpawnActor<AZeusFrontierOverlayActor>(
|
||||||
|
AZeusFrontierOverlayActor::StaticClass(), FVector::ZeroVector, FRotator::ZeroRotator, Params);
|
||||||
|
return FrontierActor_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void UZeusAOIComponent::UpdateFrontierOverlay()
|
||||||
|
{
|
||||||
|
const bool bOn = ResolveOverlay(EZeusAOIOverlay::FrontierCells);
|
||||||
|
bFrontierApplied_ = bOn;
|
||||||
|
if (!bOn)
|
||||||
|
{
|
||||||
|
if (FrontierActor_) { FrontierActor_->ClearOverlay(); }
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!bFrontierReceived_)
|
||||||
|
{
|
||||||
|
// Ainda nao temos as cells; pede e espera o 6163 (HandleFrontierConfig re-chama).
|
||||||
|
RequestFrontierConfig();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
AZeusFrontierOverlayActor* Actor = EnsureFrontierActor();
|
||||||
|
if (!Actor) { return; }
|
||||||
|
const float AnchorZ = GetOwner() ? GetOwner()->GetActorLocation().Z : 0.0f;
|
||||||
|
Actor->RebuildFromFrontier(FrontierInfo_, AnchorZ);
|
||||||
|
}
|
||||||
|
|
||||||
bool UZeusAOIComponent::ResolveOverlay(EZeusAOIOverlay Overlay) const
|
bool UZeusAOIComponent::ResolveOverlay(EZeusAOIOverlay Overlay) const
|
||||||
{
|
{
|
||||||
switch (Overlay)
|
switch (Overlay)
|
||||||
@@ -137,6 +238,7 @@ bool UZeusAOIComponent::ResolveOverlay(EZeusAOIOverlay Overlay) const
|
|||||||
case EZeusAOIOverlay::ProxyLabels: return bShowProxyLabels || CVarShowProxy.GetValueOnGameThread() > 0;
|
case EZeusAOIOverlay::ProxyLabels: return bShowProxyLabels || CVarShowProxy.GetValueOnGameThread() > 0;
|
||||||
case EZeusAOIOverlay::HandoffLine: return bShowHandoffLine || CVarShowHandoff.GetValueOnGameThread() > 0;
|
case EZeusAOIOverlay::HandoffLine: return bShowHandoffLine || CVarShowHandoff.GetValueOnGameThread() > 0;
|
||||||
case EZeusAOIOverlay::AuthDrift: return bShowAuthDrift || CVarShowDrift.GetValueOnGameThread() > 0;
|
case EZeusAOIOverlay::AuthDrift: return bShowAuthDrift || CVarShowDrift.GetValueOnGameThread() > 0;
|
||||||
|
case EZeusAOIOverlay::FrontierCells: return bShowFrontier || CVarShowFrontier.GetValueOnGameThread() > 0;
|
||||||
default: return false;
|
default: return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -148,7 +250,8 @@ bool UZeusAOIComponent::AnyOverlayActive() const
|
|||||||
|| ResolveOverlay(EZeusAOIOverlay::CellBounds)
|
|| ResolveOverlay(EZeusAOIOverlay::CellBounds)
|
||||||
|| ResolveOverlay(EZeusAOIOverlay::ProxyLabels)
|
|| ResolveOverlay(EZeusAOIOverlay::ProxyLabels)
|
||||||
|| ResolveOverlay(EZeusAOIOverlay::HandoffLine)
|
|| ResolveOverlay(EZeusAOIOverlay::HandoffLine)
|
||||||
|| ResolveOverlay(EZeusAOIOverlay::AuthDrift);
|
|| ResolveOverlay(EZeusAOIOverlay::AuthDrift)
|
||||||
|
|| ResolveOverlay(EZeusAOIOverlay::FrontierCells);
|
||||||
}
|
}
|
||||||
|
|
||||||
void UZeusAOIComponent::DrawOverlays()
|
void UZeusAOIComponent::DrawOverlays()
|
||||||
@@ -198,6 +301,7 @@ void UZeusAOIComponent::SetOverlayEnabled_Implementation(EZeusAOIOverlay Overlay
|
|||||||
case EZeusAOIOverlay::ProxyLabels: bShowProxyLabels = bEnabled; break;
|
case EZeusAOIOverlay::ProxyLabels: bShowProxyLabels = bEnabled; break;
|
||||||
case EZeusAOIOverlay::HandoffLine: bShowHandoffLine = bEnabled; break;
|
case EZeusAOIOverlay::HandoffLine: bShowHandoffLine = bEnabled; break;
|
||||||
case EZeusAOIOverlay::AuthDrift: bShowAuthDrift = bEnabled; break;
|
case EZeusAOIOverlay::AuthDrift: bShowAuthDrift = bEnabled; break;
|
||||||
|
case EZeusAOIOverlay::FrontierCells: bShowFrontier = bEnabled; break;
|
||||||
default: break;
|
default: break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -207,17 +311,27 @@ void UZeusAOIComponent::SetOverlayEnabled_Implementation(EZeusAOIOverlay Overlay
|
|||||||
{
|
{
|
||||||
RequestAoiConfig();
|
RequestAoiConfig();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Frontier: ao ligar pede as cells (6162) e reconstroi; ao desligar limpa as paredes.
|
||||||
|
if (Overlay == EZeusAOIOverlay::FrontierCells)
|
||||||
|
{
|
||||||
|
if (bEnabled) { RequestFrontierConfig(); }
|
||||||
|
UpdateFrontierOverlay();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void UZeusAOIComponent::SetAllOverlays_Implementation(bool bEnabled)
|
void UZeusAOIComponent::SetAllOverlays_Implementation(bool bEnabled)
|
||||||
{
|
{
|
||||||
bShowAOIRadius = bShowDespawnZone = bShowCellBounds = bEnabled;
|
bShowAOIRadius = bShowDespawnZone = bShowCellBounds = bEnabled;
|
||||||
bShowProxyLabels = bShowHandoffLine = bShowAuthDrift = bEnabled;
|
bShowProxyLabels = bShowHandoffLine = bShowAuthDrift = bEnabled;
|
||||||
|
bShowFrontier = bEnabled;
|
||||||
|
|
||||||
if (bEnabled && InterestRadiusCm <= 0.0f)
|
if (bEnabled && InterestRadiusCm <= 0.0f)
|
||||||
{
|
{
|
||||||
RequestAoiConfig();
|
RequestAoiConfig();
|
||||||
}
|
}
|
||||||
|
if (bEnabled) { RequestFrontierConfig(); }
|
||||||
|
UpdateFrontierOverlay();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool UZeusAOIComponent::IsOverlayEnabled_Implementation(EZeusAOIOverlay Overlay) const
|
bool UZeusAOIComponent::IsOverlayEnabled_Implementation(EZeusAOIOverlay Overlay) const
|
||||||
@@ -239,5 +353,67 @@ float UZeusAOIComponent::GetDespawnRadiusCm_Implementation() const
|
|||||||
int32 UZeusAOIComponent::GetEnabledOverlayCount() const
|
int32 UZeusAOIComponent::GetEnabledOverlayCount() const
|
||||||
{
|
{
|
||||||
return (bShowAOIRadius ? 1 : 0) + (bShowDespawnZone ? 1 : 0) + (bShowCellBounds ? 1 : 0)
|
return (bShowAOIRadius ? 1 : 0) + (bShowDespawnZone ? 1 : 0) + (bShowCellBounds ? 1 : 0)
|
||||||
+ (bShowProxyLabels ? 1 : 0) + (bShowHandoffLine ? 1 : 0) + (bShowAuthDrift ? 1 : 0);
|
+ (bShowProxyLabels ? 1 : 0) + (bShowHandoffLine ? 1 : 0) + (bShowAuthDrift ? 1 : 0)
|
||||||
|
+ (bShowFrontier ? 1 : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===========================================================================
|
||||||
|
// Gameplay AOI: visibility tracking (1.4 do roteiro PvP)
|
||||||
|
// ===========================================================================
|
||||||
|
//
|
||||||
|
// Espelha ENT_SPAWN/ENT_DESPAWN do subsystem V1 num TSet local. Fires os
|
||||||
|
// delegates BlueprintAssignable pra game systems escutarem. Self-entity
|
||||||
|
// filtrada (o proprio char nao entra no tracking nem fires).
|
||||||
|
|
||||||
|
void UZeusAOIComponent::HandleSelfEntityAssignedV1(int64 EntityId)
|
||||||
|
{
|
||||||
|
SelfEntityId_ = EntityId;
|
||||||
|
// Se ja' havia "spawnado" antes do SELF chegar (race possivel), remove agora.
|
||||||
|
if (EntityId != 0 && VisibleEntityIds_.Contains(EntityId))
|
||||||
|
{
|
||||||
|
VisibleEntityIds_.Remove(EntityId);
|
||||||
|
OnEntityExitedAOI.Broadcast(EntityId, /*Reason=*/0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void UZeusAOIComponent::HandleEntitySpawnedV1(int64 EntityId, int32 /*Kind*/,
|
||||||
|
FVector /*PosCm*/, float /*YawDeg*/,
|
||||||
|
FVector /*VelCmS*/, bool /*bGrounded*/,
|
||||||
|
int64 /*ServerTimeMs*/)
|
||||||
|
{
|
||||||
|
if (EntityId == 0) { return; }
|
||||||
|
if (EntityId == SelfEntityId_) { return; } // nunca trackear self
|
||||||
|
|
||||||
|
bool bAdded = false;
|
||||||
|
VisibleEntityIds_.Add(EntityId, &bAdded);
|
||||||
|
if (bAdded)
|
||||||
|
{
|
||||||
|
OnEntityEnteredAOI.Broadcast(EntityId);
|
||||||
|
}
|
||||||
|
// Re-spawn (entity ja' visivel) = no-op silencioso. Pode acontecer em
|
||||||
|
// keyframe pos-handoff cross-server.
|
||||||
|
}
|
||||||
|
|
||||||
|
void UZeusAOIComponent::HandleEntityDespawnedV1(int64 EntityId, int32 Reason)
|
||||||
|
{
|
||||||
|
if (EntityId == 0) { return; }
|
||||||
|
if (VisibleEntityIds_.Remove(EntityId) > 0)
|
||||||
|
{
|
||||||
|
OnEntityExitedAOI.Broadcast(EntityId, Reason);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int32 UZeusAOIComponent::GetVisibleEntityCount() const
|
||||||
|
{
|
||||||
|
return VisibleEntityIds_.Num();
|
||||||
|
}
|
||||||
|
|
||||||
|
TArray<int64> UZeusAOIComponent::GetVisibleEntityIds() const
|
||||||
|
{
|
||||||
|
return VisibleEntityIds_.Array();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool UZeusAOIComponent::IsEntityVisible(int64 EntityId) const
|
||||||
|
{
|
||||||
|
return VisibleEntityIds_.Contains(EntityId);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,8 +5,20 @@
|
|||||||
#include "CoreMinimal.h"
|
#include "CoreMinimal.h"
|
||||||
#include "Components/ActorComponent.h"
|
#include "Components/ActorComponent.h"
|
||||||
#include "ZeusAOIDebugTarget.h" // plugin ZeusAdminToolsRuntime: interface + EZeusAOIOverlay
|
#include "ZeusAOIDebugTarget.h" // plugin ZeusAdminToolsRuntime: interface + EZeusAOIOverlay
|
||||||
|
#include "ZeusV1Protocol.h" // ZeusV1::FDebugFrontierInfo (overlay de fronteira)
|
||||||
#include "ZeusAOIComponent.generated.h"
|
#include "ZeusAOIComponent.generated.h"
|
||||||
|
|
||||||
|
class AZeusFrontierOverlayActor;
|
||||||
|
|
||||||
|
// Delegates BlueprintAssignable pra game systems reagirem a entrada/saida de
|
||||||
|
// outras entidades na AOI do player local. Disparados quando ENT_SPAWN /
|
||||||
|
// ENT_DESPAWN chega do servidor (V1 canonico). Self-entity filtrada -- o
|
||||||
|
// proprio char NAO fires aqui.
|
||||||
|
// - Entered: 1 param (EntityId)
|
||||||
|
// - Exited: 2 params (EntityId, Reason). Reason vem do payload ENT_DESPAWN.
|
||||||
|
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FZeusAOIEntityEnter, int64, EntityId);
|
||||||
|
DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FZeusAOIEntityExit, int64, EntityId, int32, Reason);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* UZeusAOIComponent (cliente / jogo)
|
* UZeusAOIComponent (cliente / jogo)
|
||||||
*
|
*
|
||||||
@@ -53,6 +65,9 @@ public:
|
|||||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Zeus|AOI Debug|Overlays") bool bShowProxyLabels = false;
|
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Zeus|AOI Debug|Overlays") bool bShowProxyLabels = false;
|
||||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Zeus|AOI Debug|Overlays") bool bShowHandoffLine = false;
|
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Zeus|AOI Debug|Overlays") bool bShowHandoffLine = false;
|
||||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Zeus|AOI Debug|Overlays") bool bShowAuthDrift = false;
|
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Zeus|AOI Debug|Overlays") bool bShowAuthDrift = false;
|
||||||
|
// Fronteiras do mesh: paredes own<->neighbor com label "Cell <id>", renderizadas
|
||||||
|
// por AZeusFrontierOverlayActor a partir do DEBUG_FRONTIER_INFO (6163).
|
||||||
|
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Zeus|AOI Debug|Overlays") bool bShowFrontier = false;
|
||||||
|
|
||||||
// === Parametros ===
|
// === Parametros ===
|
||||||
// Raios reais vem do servidor (S_DEBUG_AOI_INFO). 0 = ainda nao recebido (loading).
|
// Raios reais vem do servidor (S_DEBUG_AOI_INFO). 0 = ainda nao recebido (loading).
|
||||||
@@ -64,6 +79,22 @@ public:
|
|||||||
UFUNCTION(BlueprintPure, Category = "Zeus|AOI Debug")
|
UFUNCTION(BlueprintPure, Category = "Zeus|AOI Debug")
|
||||||
int32 GetEnabledOverlayCount() const;
|
int32 GetEnabledOverlayCount() const;
|
||||||
|
|
||||||
|
// === Gameplay AOI: visibility tracking (1.4 do roteiro PvP) ============
|
||||||
|
//
|
||||||
|
// Espelha quem o servidor mandou ENT_SPAWN/ENT_DESPAWN pro player local.
|
||||||
|
// Cliente NAO faz histérese própria -- server já filtra com
|
||||||
|
// margem de ~2048cm ([[project_aoi_crossserver_hysteresis]]). Game systems
|
||||||
|
// (HUD, audio, ability target filtering) escutam estes delegates pra
|
||||||
|
// reagir a entrada/saída. Self-entity filtrada (o próprio char
|
||||||
|
// não fires nem entra no tracking).
|
||||||
|
UPROPERTY(BlueprintAssignable, Category = "Zeus|AOI") FZeusAOIEntityEnter OnEntityEnteredAOI;
|
||||||
|
UPROPERTY(BlueprintAssignable, Category = "Zeus|AOI") FZeusAOIEntityExit OnEntityExitedAOI;
|
||||||
|
|
||||||
|
UFUNCTION(BlueprintPure, Category = "Zeus|AOI") int32 GetVisibleEntityCount() const;
|
||||||
|
UFUNCTION(BlueprintPure, Category = "Zeus|AOI") TArray<int64> GetVisibleEntityIds() const;
|
||||||
|
UFUNCTION(BlueprintPure, Category = "Zeus|AOI") bool IsEntityVisible(int64 EntityId) const;
|
||||||
|
UFUNCTION(BlueprintPure, Category = "Zeus|AOI") int64 GetSelfEntityId() const { return SelfEntityId_; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void DrawOverlays();
|
void DrawOverlays();
|
||||||
bool ResolveOverlay(EZeusAOIOverlay Overlay) const;
|
bool ResolveOverlay(EZeusAOIOverlay Overlay) const;
|
||||||
@@ -76,6 +107,43 @@ private:
|
|||||||
/** Bind de UZeusNetworkSubsystem::OnDebugAoiInfo: armazena os raios reais. */
|
/** Bind de UZeusNetworkSubsystem::OnDebugAoiInfo: armazena os raios reais. */
|
||||||
void HandleAoiConfig(float InterestCm, float DespawnCm);
|
void HandleAoiConfig(float InterestCm, float DespawnCm);
|
||||||
|
|
||||||
|
/** Pede o frontier do mesh ao servidor (DEBUG_FRONTIER_REQUEST 6162). */
|
||||||
|
void RequestFrontierConfig();
|
||||||
|
/** Bind de OnDebugFrontierInfo: guarda as cells e (se overlay on) reconstroi as paredes. */
|
||||||
|
void HandleFrontierConfig(const ZeusV1::FDebugFrontierInfo& Info);
|
||||||
|
/** Spawna (lazy) o ator que desenha as paredes + labels. */
|
||||||
|
AZeusFrontierOverlayActor* EnsureFrontierActor();
|
||||||
|
/** Liga / reconstroi / limpa o overlay de fronteira conforme bShowFrontier + dados. */
|
||||||
|
void UpdateFrontierOverlay();
|
||||||
|
|
||||||
/** Handle do bind do OnDebugAoiInfo (pra remover no EndPlay). */
|
/** Handle do bind do OnDebugAoiInfo (pra remover no EndPlay). */
|
||||||
FDelegateHandle AoiConfigHandle_;
|
FDelegateHandle AoiConfigHandle_;
|
||||||
|
|
||||||
|
/** Handle do bind do OnDebugFrontierInfo (frontier overlay). */
|
||||||
|
FDelegateHandle FrontierConfigHandle_;
|
||||||
|
/** Ultimo frontier recebido do server (own cells + neighbors). */
|
||||||
|
ZeusV1::FDebugFrontierInfo FrontierInfo_;
|
||||||
|
bool bFrontierReceived_ = false;
|
||||||
|
/** Estado do overlay JA aplicado (paredes desenhadas). O TickComponent compara
|
||||||
|
* com ResolveOverlay p/ pegar liga/desliga via CVar tambem (nao so' o toggle). */
|
||||||
|
bool bFrontierApplied_ = false;
|
||||||
|
/** Re-poll periodico do frontier enquanto ligado -> overlay reflete split/merge
|
||||||
|
* da topologia dinamica (o 6163 traz o estado atual do topology:current). */
|
||||||
|
float FrontierRepollIntervalSec = 2.0f;
|
||||||
|
float FrontierRepollAccumSec_ = 0.0f;
|
||||||
|
/** Ator que desenha as paredes + labels (spawn lazy ao ligar o overlay). */
|
||||||
|
UPROPERTY() TObjectPtr<AZeusFrontierOverlayActor> FrontierActor_ = nullptr;
|
||||||
|
|
||||||
|
// === Visibility tracking (1.4) ==========================================
|
||||||
|
UFUNCTION() void HandleEntitySpawnedV1(int64 EntityId, int32 Kind, FVector PosCm,
|
||||||
|
float YawDeg, FVector VelCmS, bool bGrounded,
|
||||||
|
int64 ServerTimeMs);
|
||||||
|
UFUNCTION() void HandleEntityDespawnedV1(int64 EntityId, int32 Reason);
|
||||||
|
UFUNCTION() void HandleSelfEntityAssignedV1(int64 EntityId);
|
||||||
|
|
||||||
|
/** EntityIds atualmente visíveis (espelho do server). TSet pra O(1) check.
|
||||||
|
* Não inclui self. Limpo no EndPlay. */
|
||||||
|
TSet<int64> VisibleEntityIds_;
|
||||||
|
/** EntityId do próprio char (recebido via ENT_SELF). 0 = ainda não atribuído. */
|
||||||
|
int64 SelfEntityId_ = 0;
|
||||||
};
|
};
|
||||||
|
|||||||
218
Source/ZMMO/Game/Network/ZeusFrontierOverlayActor.cpp
Normal file
218
Source/ZMMO/Game/Network/ZeusFrontierOverlayActor.cpp
Normal file
@@ -0,0 +1,218 @@
|
|||||||
|
// Copyright Zeus Server Engine. All rights reserved.
|
||||||
|
|
||||||
|
#include "ZeusFrontierOverlayActor.h"
|
||||||
|
|
||||||
|
#include "ZMMONetLog.h" // LogZeusAOI (categoria do modulo ZMMO)
|
||||||
|
|
||||||
|
#include "ProceduralMeshComponent.h"
|
||||||
|
#include "Materials/MaterialInterface.h"
|
||||||
|
#include "Engine/World.h"
|
||||||
|
#include "DrawDebugHelpers.h"
|
||||||
|
#include "UObject/ConstructorHelpers.h"
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
const TCHAR* kWallMaterialPath =
|
||||||
|
TEXT("/Game/ZMMO/Materials/VFX/Portal/GridBoundary/Instancia/MI_VFX_Portal_GridBoundary_01.MI_VFX_Portal_GridBoundary_01");
|
||||||
|
|
||||||
|
const FColor kLabelAlive(93, 213, 255); // ciano -- server online
|
||||||
|
const FColor kLabelDead(255, 90, 90); // vermelho -- server offline / cell vazia
|
||||||
|
|
||||||
|
// Decodifica o cellId na notacao "raiz fora, splits no array" da k-d tree
|
||||||
|
// binaria: raiz pura -> "0"; derivada -> "0,[0,1]". Espelha o layout
|
||||||
|
// bit-packed de Server/ZeusServices/Shared/types/src/cell-id.ts:
|
||||||
|
// bit 30 = flag (1 = cell derivada de split)
|
||||||
|
// bits 29..26 = depth (profundidade de split)
|
||||||
|
// bits 25..15 = rootId (cell raiz do baseGrid)
|
||||||
|
// bits 14..0 = path (1 bit por nivel, MSB-first)
|
||||||
|
FString DecodeCellPath(uint32 CellId)
|
||||||
|
{
|
||||||
|
constexpr uint32 kFlagBit = 1u << 30;
|
||||||
|
if ((CellId & kFlagBit) == 0u)
|
||||||
|
{
|
||||||
|
return FString::Printf(TEXT("%u"), CellId); // raiz pura
|
||||||
|
}
|
||||||
|
const uint32 Depth = (CellId >> 26) & 0xFu;
|
||||||
|
const uint32 Root = (CellId >> 15) & 0x7FFu;
|
||||||
|
const uint32 Packed = CellId & 0x7FFFu;
|
||||||
|
FString Path;
|
||||||
|
for (uint32 i = 0; i < Depth; ++i)
|
||||||
|
{
|
||||||
|
if (i > 0)
|
||||||
|
{
|
||||||
|
Path += TEXT(",");
|
||||||
|
}
|
||||||
|
Path += ((Packed >> (14u - i)) & 1u) ? TEXT("1") : TEXT("0");
|
||||||
|
}
|
||||||
|
return FString::Printf(TEXT("%u,[%s]"), Root, *Path);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Aresta compartilhada entre dois retangulos axis-aligned (no plano XY).
|
||||||
|
bool SharedEdge(float aMinX, float aMinY, float aMaxX, float aMaxY,
|
||||||
|
float bMinX, float bMinY, float bMaxX, float bMaxY,
|
||||||
|
float Eps, FVector2D& OutA, FVector2D& OutB)
|
||||||
|
{
|
||||||
|
const float yLo = FMath::Max(aMinY, bMinY), yHi = FMath::Min(aMaxY, bMaxY);
|
||||||
|
const float xLo = FMath::Max(aMinX, bMinX), xHi = FMath::Min(aMaxX, bMaxX);
|
||||||
|
|
||||||
|
if (FMath::Abs(aMaxX - bMinX) <= Eps && yHi > yLo) { OutA = {aMaxX, yLo}; OutB = {aMaxX, yHi}; return true; }
|
||||||
|
if (FMath::Abs(aMinX - bMaxX) <= Eps && yHi > yLo) { OutA = {aMinX, yLo}; OutB = {aMinX, yHi}; return true; }
|
||||||
|
if (FMath::Abs(aMaxY - bMinY) <= Eps && xHi > xLo) { OutA = {xLo, aMaxY}; OutB = {xHi, aMaxY}; return true; }
|
||||||
|
if (FMath::Abs(aMinY - bMaxY) <= Eps && xHi > xLo) { OutA = {xLo, aMinY}; OutB = {xHi, aMinY}; return true; }
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
AZeusFrontierOverlayActor::AZeusFrontierOverlayActor()
|
||||||
|
{
|
||||||
|
PrimaryActorTick.bCanEverTick = true;
|
||||||
|
PrimaryActorTick.bStartWithTickEnabled = true;
|
||||||
|
|
||||||
|
WallMesh = CreateDefaultSubobject<UProceduralMeshComponent>(TEXT("WallMesh"));
|
||||||
|
SetRootComponent(WallMesh);
|
||||||
|
WallMesh->bUseAsyncCooking = false;
|
||||||
|
WallMesh->SetCollisionEnabled(ECollisionEnabled::NoCollision);
|
||||||
|
WallMesh->SetCastShadow(false);
|
||||||
|
|
||||||
|
static ConstructorHelpers::FObjectFinder<UMaterialInterface> MatFinder(kWallMaterialPath);
|
||||||
|
if (MatFinder.Succeeded())
|
||||||
|
{
|
||||||
|
WallMaterial = MatFinder.Object;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AZeusFrontierOverlayActor::RebuildFromFrontier(const ZeusV1::FDebugFrontierInfo& Info, float AnchorZCm)
|
||||||
|
{
|
||||||
|
if (!WallMesh) { return; }
|
||||||
|
AnchorZ_ = AnchorZCm;
|
||||||
|
|
||||||
|
// 1) Junta own + neighbors numa lista unica de cells (grid inteiro). Dedup por cellId.
|
||||||
|
Cells_.Reset();
|
||||||
|
auto AddCell = [this](uint32 CellId, int32 MinX, int32 MinY, int32 MaxX, int32 MaxY,
|
||||||
|
const FString& InOwner, bool bIsDead)
|
||||||
|
{
|
||||||
|
for (const FCellView& Ex : Cells_) { if (Ex.CellId == CellId) { return; } }
|
||||||
|
FCellView V;
|
||||||
|
V.CellId = CellId;
|
||||||
|
V.MinX = static_cast<float>(MinX); V.MinY = static_cast<float>(MinY);
|
||||||
|
V.MaxX = static_cast<float>(MaxX); V.MaxY = static_cast<float>(MaxY);
|
||||||
|
V.Owner = InOwner; V.bDead = bIsDead;
|
||||||
|
Cells_.Add(MoveTemp(V));
|
||||||
|
};
|
||||||
|
for (const ZeusV1::FFrontierOwnCell& C : Info.OwnedCells)
|
||||||
|
{
|
||||||
|
AddCell(C.CellId, C.MinXCm, C.MinYCm, C.MaxXCm, C.MaxYCm, C.OwnerName, false);
|
||||||
|
}
|
||||||
|
for (const ZeusV1::FFrontierNeighborCell& Nb : Info.NeighborCells)
|
||||||
|
{
|
||||||
|
AddCell(Nb.CellId, Nb.MinXCm, Nb.MinYCm, Nb.MaxXCm, Nb.MaxYCm, Nb.OwnerName, Nb.bDead);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2) Paredes: 1 quad por aresta compartilhada entre QUALQUER par (i<j -> dedup).
|
||||||
|
const float BaseZ = AnchorZCm - WallHeightCm * 0.5f;
|
||||||
|
const float TopZ = AnchorZCm + WallHeightCm * 0.5f;
|
||||||
|
|
||||||
|
TArray<FVector> Verts;
|
||||||
|
TArray<int32> Tris;
|
||||||
|
TArray<FVector> Normals;
|
||||||
|
TArray<FVector2D> UVs;
|
||||||
|
TArray<FProcMeshTangent> Tangents;
|
||||||
|
TArray<FLinearColor> Colors;
|
||||||
|
|
||||||
|
for (int32 i = 0; i < Cells_.Num(); ++i)
|
||||||
|
{
|
||||||
|
for (int32 j = i + 1; j < Cells_.Num(); ++j)
|
||||||
|
{
|
||||||
|
const FCellView& A = Cells_[i];
|
||||||
|
const FCellView& B = Cells_[j];
|
||||||
|
FVector2D P0, P1;
|
||||||
|
if (!SharedEdge(A.MinX, A.MinY, A.MaxX, A.MaxY,
|
||||||
|
B.MinX, B.MinY, B.MaxX, B.MaxY, EdgeEpsilonCm, P0, P1))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const int32 Base = Verts.Num();
|
||||||
|
Verts.Add(FVector(P0.X, P0.Y, BaseZ));
|
||||||
|
Verts.Add(FVector(P1.X, P1.Y, BaseZ));
|
||||||
|
Verts.Add(FVector(P1.X, P1.Y, TopZ));
|
||||||
|
Verts.Add(FVector(P0.X, P0.Y, TopZ));
|
||||||
|
|
||||||
|
const FVector2D Dir2D = (P1 - P0).GetSafeNormal();
|
||||||
|
const FVector N(-Dir2D.Y, Dir2D.X, 0.f);
|
||||||
|
Normals.Add(N); Normals.Add(N); Normals.Add(N); Normals.Add(N);
|
||||||
|
|
||||||
|
UVs.Add(FVector2D(0.f, 1.f));
|
||||||
|
UVs.Add(FVector2D(1.f, 1.f));
|
||||||
|
UVs.Add(FVector2D(1.f, 0.f));
|
||||||
|
UVs.Add(FVector2D(0.f, 0.f));
|
||||||
|
|
||||||
|
const FProcMeshTangent Tan(FVector(Dir2D.X, Dir2D.Y, 0.f), false);
|
||||||
|
Tangents.Add(Tan); Tangents.Add(Tan); Tangents.Add(Tan); Tangents.Add(Tan);
|
||||||
|
|
||||||
|
const FLinearColor C = (A.bDead || B.bDead) ? FLinearColor(kLabelDead) : FLinearColor(kLabelAlive);
|
||||||
|
Colors.Add(C); Colors.Add(C); Colors.Add(C); Colors.Add(C);
|
||||||
|
|
||||||
|
Tris.Add(Base + 0); Tris.Add(Base + 1); Tris.Add(Base + 2);
|
||||||
|
Tris.Add(Base + 0); Tris.Add(Base + 2); Tris.Add(Base + 3);
|
||||||
|
Tris.Add(Base + 0); Tris.Add(Base + 2); Tris.Add(Base + 1);
|
||||||
|
Tris.Add(Base + 0); Tris.Add(Base + 3); Tris.Add(Base + 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
WallMesh->ClearAllMeshSections();
|
||||||
|
WallMesh->SetVisibility(true);
|
||||||
|
if (Verts.Num() > 0)
|
||||||
|
{
|
||||||
|
WallMesh->CreateMeshSection_LinearColor(
|
||||||
|
/*SectionIndex=*/0, Verts, Tris, Normals, UVs, Colors, Tangents, /*bCreateCollision=*/false);
|
||||||
|
if (WallMaterial) { WallMesh->SetMaterial(0, WallMaterial); }
|
||||||
|
}
|
||||||
|
bHasOverlay_ = (Cells_.Num() > 0);
|
||||||
|
|
||||||
|
UE_LOG(LogZeusAOI, Display,
|
||||||
|
TEXT("Frontier overlay rebuilt: cells=%d walls=%d anchorZ=%.0f%s"),
|
||||||
|
Cells_.Num(), Verts.Num() / 4, AnchorZCm,
|
||||||
|
WallMaterial ? TEXT("") : TEXT(" [WARN: material nula]"));
|
||||||
|
}
|
||||||
|
|
||||||
|
void AZeusFrontierOverlayActor::ClearOverlay()
|
||||||
|
{
|
||||||
|
if (WallMesh)
|
||||||
|
{
|
||||||
|
WallMesh->ClearAllMeshSections();
|
||||||
|
WallMesh->SetVisibility(false);
|
||||||
|
}
|
||||||
|
Cells_.Reset();
|
||||||
|
bHasOverlay_ = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AZeusFrontierOverlayActor::DrawCellLabels()
|
||||||
|
{
|
||||||
|
if (!bHasOverlay_ || Cells_.Num() == 0) { return; }
|
||||||
|
UWorld* World = GetWorld();
|
||||||
|
if (!World) { return; }
|
||||||
|
|
||||||
|
for (const FCellView& C : Cells_)
|
||||||
|
{
|
||||||
|
const float Cx = (C.MinX + C.MaxX) * 0.5f;
|
||||||
|
const float Cy = (C.MinY + C.MaxY) * 0.5f;
|
||||||
|
const FVector Pos(Cx, Cy, AnchorZ_ + LabelHeightCm);
|
||||||
|
|
||||||
|
// "Cell 0" / "Cell 0,2" (notacao da quadtree) + nome do server dono.
|
||||||
|
FString Txt = FString::Printf(TEXT("Cell %s"), *DecodeCellPath(C.CellId));
|
||||||
|
if (!C.Owner.IsEmpty()) { Txt += TEXT("\n") + C.Owner; }
|
||||||
|
const FColor Col = C.bDead ? kLabelDead : kLabelAlive;
|
||||||
|
|
||||||
|
// DrawDebugString: billboard de tela (visivel de qualquer lado, nao gira,
|
||||||
|
// tamanho de tela constante). Re-desenhado a cada frame (Duration 0).
|
||||||
|
DrawDebugString(World, Pos, Txt, /*TestBaseActor=*/nullptr, Col,
|
||||||
|
/*Duration=*/0.0f, /*bDrawShadow=*/true, LabelFontScale);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AZeusFrontierOverlayActor::Tick(float DeltaSeconds)
|
||||||
|
{
|
||||||
|
Super::Tick(DeltaSeconds);
|
||||||
|
DrawCellLabels();
|
||||||
|
}
|
||||||
70
Source/ZMMO/Game/Network/ZeusFrontierOverlayActor.h
Normal file
70
Source/ZMMO/Game/Network/ZeusFrontierOverlayActor.h
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
// Copyright Zeus Server Engine. All rights reserved.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "CoreMinimal.h"
|
||||||
|
#include "GameFramework/Actor.h"
|
||||||
|
#include "ZeusV1Protocol.h" // ZeusV1::FDebugFrontierInfo (plugin ZeusNetwork)
|
||||||
|
#include "ZeusFrontierOverlayActor.generated.h"
|
||||||
|
|
||||||
|
class UProceduralMeshComponent;
|
||||||
|
class UMaterialInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* AZeusFrontierOverlayActor (cliente / debug)
|
||||||
|
*
|
||||||
|
* Desenha no MUNDO as "paredes" de TODAS as fronteiras do grid de server-meshing
|
||||||
|
* (todas as arestas compartilhadas entre quaisquer duas cells, com dedup), usando
|
||||||
|
* a material de portal MI_VFX_Portal_GridBoundary_01. As cells vem do
|
||||||
|
* DEBUG_FRONTIER_INFO (6163), que reflete a topologia DINAMICA (split/merge).
|
||||||
|
*
|
||||||
|
* Label: no CENTRO de cada cell, um texto via DrawDebugString (billboard de tela,
|
||||||
|
* visivel de qualquer lado, nao gira). Mostra a notacao da cell:
|
||||||
|
* raiz -> "Cell 0", "Cell 1", ...
|
||||||
|
* split -> "Cell 0,0", "Cell 0,1", ... (pai,quadrante) -- reflete a quadtree.
|
||||||
|
* + o nome do servidor dono. NAO e' replicado: visual no cliente local.
|
||||||
|
*/
|
||||||
|
UCLASS()
|
||||||
|
class ZMMO_API AZeusFrontierOverlayActor : public AActor
|
||||||
|
{
|
||||||
|
GENERATED_BODY()
|
||||||
|
|
||||||
|
public:
|
||||||
|
AZeusFrontierOverlayActor();
|
||||||
|
|
||||||
|
virtual void Tick(float DeltaSeconds) override;
|
||||||
|
|
||||||
|
/** (Re)constroi as paredes do grid + guarda as cells pros labels.
|
||||||
|
* AnchorZCm ancora a altura das paredes/labels (tipicamente o Z do player). */
|
||||||
|
void RebuildFromFrontier(const ZeusV1::FDebugFrontierInfo& Info, float AnchorZCm);
|
||||||
|
|
||||||
|
/** Limpa a geometria (overlay desligado). */
|
||||||
|
void ClearOverlay();
|
||||||
|
|
||||||
|
// === Parametros das paredes ===
|
||||||
|
UPROPERTY(EditAnywhere, Category = "Zeus|Frontier") float WallHeightCm = 12000.0f; // 120m
|
||||||
|
UPROPERTY(EditAnywhere, Category = "Zeus|Frontier") float EdgeEpsilonCm = 4.0f; // tolerancia "compartilha aresta"
|
||||||
|
|
||||||
|
// === Label "Cell X,Y" no centro de cada cell (DrawDebugString) ===
|
||||||
|
UPROPERTY(EditAnywhere, Category = "Zeus|Frontier|Label") float LabelFontScale = 1.4f; // tamanho na tela (screen-space)
|
||||||
|
UPROPERTY(EditAnywhere, Category = "Zeus|Frontier|Label") float LabelHeightCm = 250.0f; // altura do label acima do anchor
|
||||||
|
|
||||||
|
private:
|
||||||
|
UPROPERTY() UProceduralMeshComponent* WallMesh = nullptr;
|
||||||
|
UPROPERTY() UMaterialInterface* WallMaterial = nullptr;
|
||||||
|
|
||||||
|
/// Snapshot leve das cells (own + neighbors) pros labels.
|
||||||
|
struct FCellView
|
||||||
|
{
|
||||||
|
uint32 CellId = 0;
|
||||||
|
float MinX = 0.f, MinY = 0.f, MaxX = 0.f, MaxY = 0.f;
|
||||||
|
FString Owner; // "Server1" (ou vazio)
|
||||||
|
bool bDead = false;
|
||||||
|
};
|
||||||
|
TArray<FCellView> Cells_;
|
||||||
|
float AnchorZ_ = 0.0f;
|
||||||
|
bool bHasOverlay_ = false;
|
||||||
|
|
||||||
|
/// Desenha "Cell X,Y\n<server>" no centro de cada cell (chamado no Tick).
|
||||||
|
void DrawCellLabels();
|
||||||
|
};
|
||||||
@@ -19,6 +19,7 @@ public class ZMMO : ModuleRules
|
|||||||
"CommonInput",
|
"CommonInput",
|
||||||
"GameplayTags",
|
"GameplayTags",
|
||||||
"GameplayAbilities", // GAS: UAttributeSet, FGameplayAttributeData
|
"GameplayAbilities", // GAS: UAttributeSet, FGameplayAttributeData
|
||||||
|
"ProceduralMeshComponent", // paredes do overlay de fronteira (AZeusFrontierOverlayActor)
|
||||||
"ZeusNetwork",
|
"ZeusNetwork",
|
||||||
"ZeusGAS", // UZeusGASComponent, UZeusAttributeSet, FZeusAttributesSnapshot
|
"ZeusGAS", // UZeusGASComponent, UZeusAttributeSet, FZeusAttributesSnapshot
|
||||||
"ZeusJobs",
|
"ZeusJobs",
|
||||||
|
|||||||
@@ -76,6 +76,10 @@
|
|||||||
{
|
{
|
||||||
"Name": "CommonUI",
|
"Name": "CommonUI",
|
||||||
"Enabled": true
|
"Enabled": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "GameplayAbilities",
|
||||||
|
"Enabled": true
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"AdditionalPluginDirectories": [
|
"AdditionalPluginDirectories": [
|
||||||
|
|||||||
Reference in New Issue
Block a user