Compare commits
8 Commits
main
...
GASCombatS
| Author | SHA1 | Date | |
|---|---|---|---|
| 432741af52 | |||
| 84f552b4fa | |||
| 1308338953 | |||
| 21d42d9846 | |||
| 2895889e2b | |||
| 1114ddbb11 | |||
| 9a430dabc3 | |||
| a3f2fa9c06 |
@@ -91,3 +91,12 @@ bShouldAcquireMissingChunksOnLoad=False
|
||||
bShouldWarnAboutInvalidAssets=True
|
||||
MetaDataTagsForAssetRegistry=()
|
||||
|
||||
|
||||
[/Script/GameplayAbilities.AbilitySystemGlobals]
|
||||
bUseDebugTargetFromHud=true
|
||||
|
||||
[ConsoleVariables]
|
||||
; mostra cooldowns, ativações, falhas
|
||||
AbilitySystem.LogAbilityTriggers=1
|
||||
; mostra effects aplicados/removidos com magnitude
|
||||
AbilitySystem.DebugAbilityTags=1
|
||||
@@ -1,13 +1,29 @@
|
||||
; Gerado por ZeusTool tags sync -- tags do ZeusServer p/ o cliente UE.
|
||||
; NAO editar a mao: regenere com `ZeusTool tags sync <json> <ini>`.
|
||||
[/Script/GameplayTags.GameplayTagsList]
|
||||
GameplayTagList=(Tag="GameplayCue",DevComment="")
|
||||
GameplayTagList=(Tag="GameplayCue.Zeus",DevComment="")
|
||||
GameplayTagList=(Tag="GameplayCue.Zeus.Melee",DevComment="")
|
||||
GameplayTagList=(Tag="GameplayCue.Zeus.Melee.Kick",DevComment="")
|
||||
GameplayTagList=(Tag="GameplayCue.Zeus.Combat",DevComment="Categoria root de cues compartilhados por TODO melee (reutilizaveis)")
|
||||
GameplayTagList=(Tag="GameplayCue.Zeus.Combat.Hit",DevComment="Familia de cues do impacto de hit")
|
||||
GameplayTagList=(Tag="GameplayCue.Zeus.Combat.Hit.Impact",DevComment="Broadcast AOI - SFX impact + VFX sparks na location (todos ouvem)")
|
||||
GameplayTagList=(Tag="GameplayCue.Zeus.Combat.Hit.React",DevComment="Broadcast AOI - AnimMontage de hit react no target (todos veem)")
|
||||
GameplayTagList=(Tag="GameplayCue.Zeus.Combat.Hit.Self",DevComment="Unicast - feedback privado do alvo (camera shake, hit flash, rumble)")
|
||||
GameplayTagList=(Tag="GameplayCue.Zeus.Melee.Kick.Target",DevComment="")
|
||||
GameplayTagList=(Tag="GameplayCue.Zeus.Movement",DevComment="")
|
||||
GameplayTagList=(Tag="GameplayCue.Zeus.Movement.Dash",DevComment="")
|
||||
GameplayTagList=(Tag="Zeus",DevComment="")
|
||||
GameplayTagList=(Tag="Zeus.Ability",DevComment="")
|
||||
GameplayTagList=(Tag="Zeus.Ability.Casting",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.Dash",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.Agi",DevComment="")
|
||||
GameplayTagList=(Tag="Zeus.Attribute.Aspd",DevComment="")
|
||||
@@ -20,6 +36,7 @@ GameplayTagList=(Tag="Zeus.Attribute.Dex",DevComment="")
|
||||
GameplayTagList=(Tag="Zeus.Attribute.Flee",DevComment="")
|
||||
GameplayTagList=(Tag="Zeus.Attribute.Hit",DevComment="")
|
||||
GameplayTagList=(Tag="Zeus.Attribute.Hp",DevComment="")
|
||||
GameplayTagList=(Tag="Zeus.Attribute.HpRegenRate",DevComment="")
|
||||
GameplayTagList=(Tag="Zeus.Attribute.Int",DevComment="")
|
||||
GameplayTagList=(Tag="Zeus.Attribute.JobExp",DevComment="")
|
||||
GameplayTagList=(Tag="Zeus.Attribute.JobLevel",DevComment="")
|
||||
@@ -32,6 +49,9 @@ GameplayTagList=(Tag="Zeus.Attribute.MaxSp",DevComment="")
|
||||
GameplayTagList=(Tag="Zeus.Attribute.Mdef",DevComment="")
|
||||
GameplayTagList=(Tag="Zeus.Attribute.SkillPoint",DevComment="")
|
||||
GameplayTagList=(Tag="Zeus.Attribute.Sp",DevComment="")
|
||||
GameplayTagList=(Tag="Zeus.Attribute.SpRegenRate",DevComment="")
|
||||
GameplayTagList=(Tag="Zeus.Attribute.Stamina",DevComment="")
|
||||
GameplayTagList=(Tag="Zeus.Attribute.StaminaRegenRate",DevComment="")
|
||||
GameplayTagList=(Tag="Zeus.Attribute.StatusPoint",DevComment="")
|
||||
GameplayTagList=(Tag="Zeus.Attribute.Str",DevComment="")
|
||||
GameplayTagList=(Tag="Zeus.Attribute.Vit",DevComment="")
|
||||
@@ -46,9 +66,7 @@ GameplayTagList=(Tag="Zeus.Combo.Slot2",DevComment="")
|
||||
GameplayTagList=(Tag="Zeus.Combo.Slot3",DevComment="")
|
||||
GameplayTagList=(Tag="Zeus.Combo.Window",DevComment="")
|
||||
GameplayTagList=(Tag="Zeus.Cooldown",DevComment="")
|
||||
GameplayTagList=(Tag="Zeus.Cue",DevComment="")
|
||||
GameplayTagList=(Tag="Zeus.Cue.Movement",DevComment="")
|
||||
GameplayTagList=(Tag="Zeus.Cue.Movement.Dash",DevComment="")
|
||||
GameplayTagList=(Tag="Zeus.Cooldown.FireballRain",DevComment="")
|
||||
GameplayTagList=(Tag="Zeus.Damage",DevComment="")
|
||||
GameplayTagList=(Tag="Zeus.Damage.Earth",DevComment="")
|
||||
GameplayTagList=(Tag="Zeus.Damage.Fire",DevComment="")
|
||||
@@ -60,10 +78,23 @@ GameplayTagList=(Tag="Zeus.Damage.Shadow",DevComment="")
|
||||
GameplayTagList=(Tag="Zeus.Damage.Water",DevComment="")
|
||||
GameplayTagList=(Tag="Zeus.Damage.Wind",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.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.Sp_10",DevComment="")
|
||||
GameplayTagList=(Tag="Zeus.Effect.Cost.Sp_50",DevComment="")
|
||||
GameplayTagList=(Tag="Zeus.Effect.Cost.Stamina_5",DevComment="")
|
||||
GameplayTagList=(Tag="Zeus.Effect.Damage",DevComment="")
|
||||
GameplayTagList=(Tag="Zeus.Effect.Damage.Kick",DevComment="")
|
||||
GameplayTagList=(Tag="Zeus.State",DevComment="")
|
||||
GameplayTagList=(Tag="Zeus.State.NoHpRegen",DevComment="")
|
||||
GameplayTagList=(Tag="Zeus.State.NoRegen",DevComment="")
|
||||
GameplayTagList=(Tag="Zeus.State.NoSpRegen",DevComment="")
|
||||
GameplayTagList=(Tag="Zeus.State.NoStaminaRegen",DevComment="")
|
||||
GameplayTagList=(Tag="Zeus.Status",DevComment="")
|
||||
GameplayTagList=(Tag="Zeus.Status.Buff",DevComment="")
|
||||
GameplayTagList=(Tag="Zeus.Status.CC",DevComment="")
|
||||
|
||||
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.
Binary file not shown.
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/Camera/CS_HitReact.uasset
Normal file
BIN
Content/ZMMO/GAS/Cues/Camera/CS_HitReact.uasset
Normal file
Binary file not shown.
BIN
Content/ZMMO/GAS/Cues/GC_Combat_Hit_Impact.uasset
Normal file
BIN
Content/ZMMO/GAS/Cues/GC_Combat_Hit_Impact.uasset
Normal file
Binary file not shown.
BIN
Content/ZMMO/GAS/Cues/GC_Combat_Hit_React.uasset
Normal file
BIN
Content/ZMMO/GAS/Cues/GC_Combat_Hit_React.uasset
Normal file
Binary file not shown.
BIN
Content/ZMMO/GAS/Cues/GC_Combat_Hit_Self.uasset
Normal file
BIN
Content/ZMMO/GAS/Cues/GC_Combat_Hit_Self.uasset
Normal file
Binary file not shown.
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.
BIN
Content/ZMMO/GAS/Cues/HitReact/AM_HitReact_Heavy.uasset
Normal file
BIN
Content/ZMMO/GAS/Cues/HitReact/AM_HitReact_Heavy.uasset
Normal file
Binary file not shown.
BIN
Content/ZMMO/GAS/Cues/HitReact/AM_HitReact_Light.uasset
Normal file
BIN
Content/ZMMO/GAS/Cues/HitReact/AM_HitReact_Light.uasset
Normal file
Binary file not shown.
BIN
Content/ZMMO/GAS/Cues/HitReact/AM_HitReact_Medium.uasset
Normal file
BIN
Content/ZMMO/GAS/Cues/HitReact/AM_HitReact_Medium.uasset
Normal file
Binary file not shown.
BIN
Content/ZMMO/GAS/Cues/HitReact/Animations/AS_HitReact.uasset
Normal file
BIN
Content/ZMMO/GAS/Cues/HitReact/Animations/AS_HitReact.uasset
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Content/ZMMO/Sounds/Combat/SW_Punch_Cartoon.uasset
Normal file
BIN
Content/ZMMO/Sounds/Combat/SW_Punch_Cartoon.uasset
Normal file
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,46 @@
|
||||
// Copyright Zeus Server Engine. All rights reserved.
|
||||
|
||||
#include "ZeusAnimNotifyState_BodyHitWindow.h"
|
||||
|
||||
#include "Components/SkeletalMeshComponent.h"
|
||||
#include "GameFramework/Actor.h"
|
||||
#include "Game/Entity/ZeusCharacter.h"
|
||||
|
||||
void UZeusAnimNotifyState_BodyHitWindow::NotifyBegin(USkeletalMeshComponent* MeshComp,
|
||||
UAnimSequenceBase* /*Animation*/, float /*TotalDuration*/,
|
||||
const FAnimNotifyEventReference& /*EventReference*/)
|
||||
{
|
||||
if (!MeshComp) { return; }
|
||||
if (AZeusCharacter* Char = Cast<AZeusCharacter>(MeshComp->GetOwner()))
|
||||
{
|
||||
Char->SetHitColliderEnabled(Source, /*bEnabled*/ true);
|
||||
}
|
||||
}
|
||||
|
||||
void UZeusAnimNotifyState_BodyHitWindow::NotifyEnd(USkeletalMeshComponent* MeshComp,
|
||||
UAnimSequenceBase* /*Animation*/, const FAnimNotifyEventReference& /*EventReference*/)
|
||||
{
|
||||
if (!MeshComp) { return; }
|
||||
if (AZeusCharacter* Char = Cast<AZeusCharacter>(MeshComp->GetOwner()))
|
||||
{
|
||||
Char->SetHitColliderEnabled(Source, /*bEnabled*/ false);
|
||||
}
|
||||
}
|
||||
|
||||
#if WITH_EDITOR
|
||||
FString UZeusAnimNotifyState_BodyHitWindow::GetNotifyName_Implementation() const
|
||||
{
|
||||
const TCHAR* SourceLabel;
|
||||
switch (Source)
|
||||
{
|
||||
case EZeusHitSource::FootR: SourceLabel = TEXT("FootR"); break;
|
||||
case EZeusHitSource::FootL: SourceLabel = TEXT("FootL"); break;
|
||||
case EZeusHitSource::HandR: SourceLabel = TEXT("HandR"); break;
|
||||
case EZeusHitSource::HandL: SourceLabel = TEXT("HandL"); break;
|
||||
case EZeusHitSource::WeaponR: SourceLabel = TEXT("WeaponR"); break;
|
||||
case EZeusHitSource::WeaponL: SourceLabel = TEXT("WeaponL"); break;
|
||||
default: SourceLabel = TEXT("?"); break;
|
||||
}
|
||||
return FString::Printf(TEXT("Zeus Body Hit (%s)"), SourceLabel);
|
||||
}
|
||||
#endif
|
||||
74
Source/ZMMO/Game/Combat/ZeusAnimNotifyState_BodyHitWindow.h
Normal file
74
Source/ZMMO/Game/Combat/ZeusAnimNotifyState_BodyHitWindow.h
Normal file
@@ -0,0 +1,74 @@
|
||||
// Copyright Zeus Server Engine. All rights reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Animation/AnimNotifies/AnimNotifyState.h"
|
||||
#include "Game/Entity/ZeusCharacter.h" // EZeusHitSource
|
||||
#include "ZeusAnimNotifyState_BodyHitWindow.generated.h"
|
||||
|
||||
class USkeletalMeshComponent;
|
||||
class UAnimSequenceBase;
|
||||
|
||||
/**
|
||||
* AnimNotifyState que liga o HitCollider de uma parte do corpo durante a sua
|
||||
* janela no montage. NotifyBegin liga + NotifyEnd desliga -- define o HIT
|
||||
* WINDOW do golpe melee/arma branca.
|
||||
*
|
||||
* Generaliza para qualquer parte do corpo via EZeusHitSource:
|
||||
* - FootR / FootL -> chute (capsule attachado em foot_*_Socket)
|
||||
* - HandR / HandL -> soco (capsule em HandGrip_*)
|
||||
* - WeaponR / WeaponL -> arma branca (capsule em weapon_*)
|
||||
*
|
||||
* === Como usar ===
|
||||
*
|
||||
* No editor de um AnimMontage (ex: AM_Kick_Montage):
|
||||
* 1. Encontrar o frame onde o membro comeca o movimento de impacto.
|
||||
* 2. Right-click na track de notifies -> Add Notify State -> Zeus Body Hit Window.
|
||||
* 3. Definir Source = FootR (ou HandR pra soco, WeaponR pra arma, etc).
|
||||
* 4. Arrastar o range pra cobrir o arco completo do golpe (ex: frame 8 a 18
|
||||
* em 30fps = ~330ms de janela).
|
||||
*
|
||||
* Pq AnimNotifyState e nao AnimNotify pontual:
|
||||
* - AnimNotify dispara em UM frame -> nao da' pra detectar entradas no
|
||||
* capsule durante a janela.
|
||||
* - AnimNotifyState tem NotifyBegin/NotifyTick/NotifyEnd. Pra "ligar
|
||||
* collider de frame X ate Y", e' o pattern correto.
|
||||
*
|
||||
* === Replicacao ===
|
||||
*
|
||||
* Roda LOCAL em cada cliente. Cliente do ATIVADOR liga capsule local + reporta
|
||||
* overlap via opcode MELEE_HIT_REPORT pro server. Server valida e aplica dano +
|
||||
* dispatcha cue Kick.Target pra todos no AOI do alvo.
|
||||
*
|
||||
* Proxies de outros clientes tocam o montage via GameplayCue replicado (visual)
|
||||
* mas o capsule deles roda no PROXY que e' Pawn local de outra coisa -- nao
|
||||
* importa, o reportador autoritativo eh quem ativou a ability.
|
||||
*/
|
||||
UCLASS(meta = (DisplayName = "Zeus Body Hit Window"))
|
||||
class ZMMO_API UZeusAnimNotifyState_BodyHitWindow : public UAnimNotifyState
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
/// Qual parte do corpo (capsule) tem o hit ligado durante a janela.
|
||||
/// Default = FootR (chute direito e' o golpe mais comum em montages
|
||||
/// melee unarmed). Trocar pra HandR pra soco, WeaponR pra arma, etc.
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Zeus|Combat")
|
||||
EZeusHitSource Source = EZeusHitSource::FootR;
|
||||
|
||||
// === AnimNotifyState overrides ===
|
||||
|
||||
virtual void NotifyBegin(USkeletalMeshComponent* MeshComp, UAnimSequenceBase* Animation,
|
||||
float TotalDuration, const FAnimNotifyEventReference& EventReference) override;
|
||||
|
||||
virtual void NotifyEnd(USkeletalMeshComponent* MeshComp, UAnimSequenceBase* Animation,
|
||||
const FAnimNotifyEventReference& EventReference) override;
|
||||
|
||||
#if WITH_EDITOR
|
||||
/// Mostra "Zeus Body Hit (FootR/HandL/WeaponR/...)" no editor de montage em
|
||||
/// vez do nome generico da classe -- ajuda a identificar qual parte sem
|
||||
/// abrir details.
|
||||
virtual FString GetNotifyName_Implementation() const override;
|
||||
#endif
|
||||
};
|
||||
@@ -173,6 +173,12 @@ void AZeusCharacter::Tick(const float DeltaSeconds)
|
||||
|
||||
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))
|
||||
{
|
||||
EnhancedInputComponent->BindAction(JumpAction, ETriggerEvent::Started, this, &AZeusCharacter::OnJumpPressed);
|
||||
|
||||
@@ -5,10 +5,47 @@
|
||||
#include "ZeusPlayerController.h"
|
||||
#include "ZeusPlayerState.h"
|
||||
|
||||
#include "UObject/ConstructorHelpers.h"
|
||||
|
||||
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();
|
||||
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();
|
||||
}
|
||||
|
||||
@@ -66,6 +66,13 @@ void UZeusAOIComponent::BeginPlay()
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -87,12 +94,17 @@ void UZeusAOIComponent::EndPlay(const EEndPlayReason::Type EndPlayReason)
|
||||
{
|
||||
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();
|
||||
FrontierConfigHandle_.Reset();
|
||||
}
|
||||
VisibleEntityIds_.Empty();
|
||||
if (FrontierActor_)
|
||||
{
|
||||
FrontierActor_->Destroy();
|
||||
@@ -344,3 +356,64 @@ int32 UZeusAOIComponent::GetEnabledOverlayCount() const
|
||||
+ (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);
|
||||
}
|
||||
|
||||
@@ -10,6 +10,15 @@
|
||||
|
||||
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)
|
||||
*
|
||||
@@ -70,6 +79,22 @@ public:
|
||||
UFUNCTION(BlueprintPure, Category = "Zeus|AOI Debug")
|
||||
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:
|
||||
void DrawOverlays();
|
||||
bool ResolveOverlay(EZeusAOIOverlay Overlay) const;
|
||||
@@ -108,4 +133,17 @@ private:
|
||||
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;
|
||||
};
|
||||
|
||||
@@ -18,20 +18,33 @@ namespace
|
||||
const FColor kLabelAlive(93, 213, 255); // ciano -- server online
|
||||
const FColor kLabelDead(255, 90, 90); // vermelho -- server offline / cell vazia
|
||||
|
||||
// Offset do cellId de filha de split (espelha CHILD_CELLID_OFFSET do server).
|
||||
constexpr uint32 kChildCellIdOffset = 1000000u;
|
||||
|
||||
// Decodifica o cellId int na notacao de quadtree: raiz -> "0".."3"; filha ->
|
||||
// "<pai>,<quadrante>" (ex "0,2"); neto -> "0,2,1" (recursivo). Espelha
|
||||
// childCellId = offset + pai*4 + quadrante do SplitCoordinator.
|
||||
// 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)
|
||||
{
|
||||
if (CellId < kChildCellIdOffset)
|
||||
constexpr uint32 kFlagBit = 1u << 30;
|
||||
if ((CellId & kFlagBit) == 0u)
|
||||
{
|
||||
return FString::Printf(TEXT("%u"), CellId);
|
||||
return FString::Printf(TEXT("%u"), CellId); // raiz pura
|
||||
}
|
||||
const uint32 Rel = CellId - kChildCellIdOffset;
|
||||
return DecodeCellPath(Rel / 4u) + FString::Printf(TEXT(",%u"), Rel % 4u);
|
||||
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).
|
||||
|
||||
@@ -76,6 +76,10 @@
|
||||
{
|
||||
"Name": "CommonUI",
|
||||
"Enabled": true
|
||||
},
|
||||
{
|
||||
"Name": "GameplayAbilities",
|
||||
"Enabled": true
|
||||
}
|
||||
],
|
||||
"AdditionalPluginDirectories": [
|
||||
|
||||
Reference in New Issue
Block a user