Compare commits
26 Commits
98dce2f855
...
GASCombatS
| Author | SHA1 | Date | |
|---|---|---|---|
| 432741af52 | |||
| 84f552b4fa | |||
| 1308338953 | |||
| 21d42d9846 | |||
| 2895889e2b | |||
| 1114ddbb11 | |||
| 9a430dabc3 | |||
| a3f2fa9c06 | |||
| 1f2441bde7 | |||
| 216e580548 | |||
| de294f4075 | |||
| 4d737829f5 | |||
| 341e6895c9 | |||
| 5f4c88637f | |||
| 77d52a703b | |||
| 9f5ccd3a05 | |||
| c0f6d32d95 | |||
| f21d059b67 | |||
| e87b2cec6c | |||
| 9422522d6d | |||
| c3b32bc1a1 | |||
| cda5fea776 | |||
| 16de963301 | |||
| 8d73cc9df8 | |||
| d5402216a2 | |||
| ff75ad92eb |
@@ -71,6 +71,13 @@ ScreenSetAsset=/Game/ZMMO/UI/InGame/DA_InGameScreenSet.DA_InGameScreenSet
|
||||
; (UWorldSubsystem) subscribe nos delegates do UZeusNetworkSubsystem.
|
||||
+ComponentClasses=/Script/ZeusGAS.ZeusGASComponent
|
||||
|
||||
; === GAS Cue paths (Batch 2.5 — S_ABILITY_CUE multicast cosmetico) ===
|
||||
; UGameplayCueManager scaneia esses paths no boot pra mapear FGameplayTag
|
||||
; -> AGameplayCueNotify_Actor BP. UZeusGASComponent::DispatchAbilityCue chama
|
||||
; ASC->ExecuteGameplayCueLocal(CueTag, params) -> manager resolve pelo path.
|
||||
[/Script/GameplayAbilities.AbilitySystemGlobals]
|
||||
+GameplayCueNotifyPaths="/Game/ZMMO/GAS/Cues"
|
||||
|
||||
[/Script/Engine.AssetManagerSettings]
|
||||
-PrimaryAssetTypesToScan=(PrimaryAssetType="Map",AssetBaseClass=/Script/Engine.World,bHasBlueprintClasses=False,bIsEditorOnly=True,Directories=((Path="/Game/Maps")),SpecificAssets=,Rules=(Priority=-1,ChunkId=-1,bApplyRecursively=True,CookRule=Unknown))
|
||||
-PrimaryAssetTypesToScan=(PrimaryAssetType="PrimaryAssetLabel",AssetBaseClass=/Script/Engine.PrimaryAssetLabel,bHasBlueprintClasses=False,bIsEditorOnly=True,Directories=((Path="/Game")),SpecificAssets=,Rules=(Priority=-1,ChunkId=-1,bApplyRecursively=True,CookRule=Unknown))
|
||||
@@ -84,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
|
||||
19
Config/DefaultZeusV1.ini
Normal file
19
Config/DefaultZeusV1.ini
Normal file
@@ -0,0 +1,19 @@
|
||||
; DefaultZeusV1.ini
|
||||
; Config inicial pra UZeusNetworkingClientSubsystem (P9-6/P9-7 plugin).
|
||||
; Editar via UE Project Settings -> Plugins -> Zeus V1 OU editar este arquivo
|
||||
; e relancar o editor.
|
||||
;
|
||||
; PORT 7777: ZeusGateway. NodeService popula routes via charId lookup quando
|
||||
; cliente conecta (validado: handshake routed charId=7 -> SP_01 cell 0). Fluxo
|
||||
; padrao de producao -- UE conecta no Gateway, Gateway resolve cell ownership
|
||||
; via lease em Valkey + encaminha pro ZS correto.
|
||||
;
|
||||
; ALTERNATIVO (debug only -- bug aberto pendente investigacao):
|
||||
; ServerPort=9001 conecta DIRETO em SP_01 mas hoje pacote UE nao chega no
|
||||
; server (FUdpSocketBuilder + Windows multi-NIC loopback issue). Use 7777.
|
||||
|
||||
[/Script/ZeusNetwork.ZeusNetworkingClientSubsystem]
|
||||
ServerHost=127.0.0.1
|
||||
ServerPort=7777
|
||||
bUseZeusNetworkingV1=True
|
||||
bAutoConnectOnStart=True
|
||||
114
Config/Tags/ZeusServerTags.ini
Normal file
114
Config/Tags/ZeusServerTags.ini
Normal file
@@ -0,0 +1,114 @@
|
||||
; 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="")
|
||||
GameplayTagList=(Tag="Zeus.Attribute.Atk",DevComment="")
|
||||
GameplayTagList=(Tag="Zeus.Attribute.BaseExp",DevComment="")
|
||||
GameplayTagList=(Tag="Zeus.Attribute.BaseLevel",DevComment="")
|
||||
GameplayTagList=(Tag="Zeus.Attribute.Crit",DevComment="")
|
||||
GameplayTagList=(Tag="Zeus.Attribute.Def",DevComment="")
|
||||
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="")
|
||||
GameplayTagList=(Tag="Zeus.Attribute.Luk",DevComment="")
|
||||
GameplayTagList=(Tag="Zeus.Attribute.Matk",DevComment="")
|
||||
GameplayTagList=(Tag="Zeus.Attribute.MatkMax",DevComment="")
|
||||
GameplayTagList=(Tag="Zeus.Attribute.MatkMin",DevComment="")
|
||||
GameplayTagList=(Tag="Zeus.Attribute.MaxHp",DevComment="")
|
||||
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="")
|
||||
GameplayTagList=(Tag="Zeus.Combat",DevComment="")
|
||||
GameplayTagList=(Tag="Zeus.Combat.Blocking",DevComment="")
|
||||
GameplayTagList=(Tag="Zeus.Combat.Dodging",DevComment="")
|
||||
GameplayTagList=(Tag="Zeus.Combat.InCombat",DevComment="")
|
||||
GameplayTagList=(Tag="Zeus.Combat.LockOn",DevComment="")
|
||||
GameplayTagList=(Tag="Zeus.Combo",DevComment="")
|
||||
GameplayTagList=(Tag="Zeus.Combo.Slot1",DevComment="")
|
||||
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.Cooldown.FireballRain",DevComment="")
|
||||
GameplayTagList=(Tag="Zeus.Damage",DevComment="")
|
||||
GameplayTagList=(Tag="Zeus.Damage.Earth",DevComment="")
|
||||
GameplayTagList=(Tag="Zeus.Damage.Fire",DevComment="")
|
||||
GameplayTagList=(Tag="Zeus.Damage.Holy",DevComment="")
|
||||
GameplayTagList=(Tag="Zeus.Damage.Magical",DevComment="")
|
||||
GameplayTagList=(Tag="Zeus.Damage.Physical",DevComment="")
|
||||
GameplayTagList=(Tag="Zeus.Damage.Poison",DevComment="")
|
||||
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="")
|
||||
GameplayTagList=(Tag="Zeus.Status.CC.Disarmed",DevComment="")
|
||||
GameplayTagList=(Tag="Zeus.Status.CC.Feared",DevComment="")
|
||||
GameplayTagList=(Tag="Zeus.Status.CC.Frozen",DevComment="")
|
||||
GameplayTagList=(Tag="Zeus.Status.CC.KnockedDown",DevComment="")
|
||||
GameplayTagList=(Tag="Zeus.Status.CC.Rooted",DevComment="")
|
||||
GameplayTagList=(Tag="Zeus.Status.CC.Silenced",DevComment="")
|
||||
GameplayTagList=(Tag="Zeus.Status.CC.Stunned",DevComment="")
|
||||
GameplayTagList=(Tag="Zeus.Status.Dead",DevComment="")
|
||||
GameplayTagList=(Tag="Zeus.Status.Debuff",DevComment="")
|
||||
GameplayTagList=(Tag="Zeus.Status.Invulnerable",DevComment="")
|
||||
GameplayTagList=(Tag="Zeus.Status.Regen",DevComment="")
|
||||
GameplayTagList=(Tag="Zeus.Status.Regen.Blocked",DevComment="")
|
||||
GameplayTagList=(Tag="Zeus.Status.Regen.Boosted",DevComment="")
|
||||
GameplayTagList=(Tag="Zeus.Status.Regen.Poisoned",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.
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.
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_Dash.uasset
Normal file
BIN
Content/Input/Actions/IA_Dash.uasset
Normal file
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.
BIN
Content/ZMMO/Core/Player/BP_PlayerState.uasset
Normal file
BIN
Content/ZMMO/Core/Player/BP_PlayerState.uasset
Normal file
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.
BIN
Content/ZMMO/Data/Abilities/DT_Abilities.uasset
Normal file
BIN
Content/ZMMO/Data/Abilities/DT_Abilities.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.
Binary file not shown.
BIN
Content/ZMMO/GAS/Abilities/GA_Dash.uasset
Normal file
BIN
Content/ZMMO/GAS/Abilities/GA_Dash.uasset
Normal file
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.
BIN
Content/ZMMO/GAS/Cues/GC_Dash.uasset
Normal file
BIN
Content/ZMMO/GAS/Cues/GC_Dash.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.
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/Abilities/SA_Dash.uasset
Normal file
BIN
Content/ZMMO/Sounds/Abilities/SA_Dash.uasset
Normal file
Binary file not shown.
BIN
Content/ZMMO/Sounds/Abilities/SC_Dash.uasset
Normal file
BIN
Content/ZMMO/Sounds/Abilities/SC_Dash.uasset
Normal file
Binary file not shown.
BIN
Content/ZMMO/Sounds/Abilities/SW_Dash_Source.uasset
Normal file
BIN
Content/ZMMO/Sounds/Abilities/SW_Dash_Source.uasset
Normal file
Binary file not shown.
BIN
Content/ZMMO/Sounds/Abilities/SW_Dash_Source.wav
Normal file
BIN
Content/ZMMO/Sounds/Abilities/SW_Dash_Source.wav
Normal file
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.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Content/ZMMO/VFX/Abilities/Dash/NS_Dash_Burst.uasset
Normal file
BIN
Content/ZMMO/VFX/Abilities/Dash/NS_Dash_Burst.uasset
Normal file
Binary file not shown.
BIN
Content/ZMMO/VFX/Abilities/Dash/T_DashShard_Source.png
Normal file
BIN
Content/ZMMO/VFX/Abilities/Dash/T_DashShard_Source.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.8 KiB |
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.
@@ -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
|
||||
};
|
||||
@@ -17,10 +17,16 @@
|
||||
#include "Subsystems/WorldSubsystem.h"
|
||||
#include "UObject/ConstructorHelpers.h"
|
||||
#include "ZMMO.h"
|
||||
#include "GameplayTagContainer.h"
|
||||
#include "GameplayTagsManager.h"
|
||||
#include "ZeusGASComponent.h"
|
||||
#include "ZeusAOIComponent.h"
|
||||
#include "ZeusPlayerState.h"
|
||||
#include "Blueprint/UserWidget.h"
|
||||
#include "GameFramework/PlayerController.h"
|
||||
#include "ZeusWorldSubsystem.h"
|
||||
#include "ZeusNetworkSubsystem.h"
|
||||
#include "ZeusNetworkingClientSubsystem.h"
|
||||
#include "UI/FrontEnd/UIFrontEndFlowSubsystem.h"
|
||||
|
||||
DEFINE_LOG_CATEGORY(LogZeusPlayer);
|
||||
@@ -55,6 +61,14 @@ AZeusCharacter::AZeusCharacter()
|
||||
FollowCamera->SetupAttachment(CameraBoom, USpringArmComponent::SocketName);
|
||||
FollowCamera->bUsePawnControlRotation = false;
|
||||
|
||||
// AOI do cliente (+ overlays de debug). Implementa IZeusAOIDebugTarget; o
|
||||
// Zeus Admin Tools acha este componente no pawn e dirige os overlays.
|
||||
AOIComponent = CreateDefaultSubobject<UZeusAOIComponent>(TEXT("ZeusAOIComponent"));
|
||||
|
||||
// AdminPanelClass NAO e' resolvido aqui via FClassFinder: na criacao do CDO
|
||||
// (load do modulo) o asset registry pode nao estar pronto e o static cacheia
|
||||
// null pra sempre. Resolvido lazy em ToggleAdminPanel (LoadClass em runtime).
|
||||
|
||||
// AttributeComponent migrou pro PlayerState (AZeusPlayerState + Component
|
||||
// Registry config-driven). Pawn fica leve — so' movement/input/camera.
|
||||
|
||||
@@ -101,11 +115,14 @@ AZeusCharacter::AZeusCharacter()
|
||||
TEXT("/Game/Input/Actions/IA_MouseLook.IA_MouseLook"));
|
||||
static ConstructorHelpers::FObjectFinder<UInputAction> JumpActionAsset(
|
||||
TEXT("/Game/Input/Actions/IA_Jump.IA_Jump"));
|
||||
static ConstructorHelpers::FObjectFinder<UInputAction> DashActionAsset(
|
||||
TEXT("/Game/Input/Actions/IA_Dash.IA_Dash"));
|
||||
|
||||
if (MoveActionAsset.Succeeded()) { MoveAction = MoveActionAsset.Object; }
|
||||
if (LookActionAsset.Succeeded()) { LookAction = LookActionAsset.Object; }
|
||||
if (MouseLookActionAsset.Succeeded()) { MouseLookAction = MouseLookActionAsset.Object; }
|
||||
if (JumpActionAsset.Succeeded()) { JumpAction = JumpActionAsset.Object; }
|
||||
if (DashActionAsset.Succeeded()) { DashAction = DashActionAsset.Object; }
|
||||
}
|
||||
|
||||
void AZeusCharacter::BeginPlay()
|
||||
@@ -156,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);
|
||||
@@ -166,11 +189,71 @@ void AZeusCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputCompo
|
||||
|
||||
EnhancedInputComponent->BindAction(LookAction, ETriggerEvent::Triggered, this, &AZeusCharacter::Look);
|
||||
EnhancedInputComponent->BindAction(MouseLookAction, ETriggerEvent::Triggered, this, &AZeusCharacter::Look);
|
||||
|
||||
if (DashAction)
|
||||
{
|
||||
EnhancedInputComponent->BindAction(DashAction, ETriggerEvent::Triggered, this, &AZeusCharacter::OnDashTriggered);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
UE_LOG(LogZeusPlayer, Error, TEXT("'%s' Failed to find an Enhanced Input component."), *GetNameSafe(this));
|
||||
}
|
||||
|
||||
// Debug: F8 abre/fecha o Zeus Admin Tools (AIO Debug). Legacy BindKey
|
||||
// convive com Enhanced Input. Trocar a tecla aqui se conflitar.
|
||||
PlayerInputComponent->BindKey(EKeys::F8, IE_Pressed, this, &AZeusCharacter::ToggleAdminPanel);
|
||||
}
|
||||
|
||||
void AZeusCharacter::ToggleAdminPanel()
|
||||
{
|
||||
APlayerController* PC = Cast<APlayerController>(GetController());
|
||||
if (!PC)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Aberto -> fecha + devolve input pro jogo.
|
||||
if (AdminPanelInstance && AdminPanelInstance->IsInViewport())
|
||||
{
|
||||
AdminPanelInstance->RemoveFromParent();
|
||||
PC->SetInputMode(FInputModeGameOnly());
|
||||
PC->bShowMouseCursor = false;
|
||||
return;
|
||||
}
|
||||
|
||||
// Lazy load (runtime): no F8 o asset ja' esta carregavel; evita o pitfall do
|
||||
// FClassFinder na criacao do CDO. Path do generated class (_C).
|
||||
// A UI vive no CONTENT DO PLUGIN (mount /ZeusAdminTools/), nao no /Game do projeto:
|
||||
// o plugin ZeusAdminTools e' autocontido (C++ + UI no repo Server).
|
||||
if (!AdminPanelClass)
|
||||
{
|
||||
AdminPanelClass = LoadClass<UUserWidget>(nullptr,
|
||||
TEXT("/ZeusAdminTools/UI/WBP_AdminToolsAIO.WBP_AdminToolsAIO_C"));
|
||||
}
|
||||
if (!AdminPanelClass)
|
||||
{
|
||||
UE_LOG(LogZeusPlayer, Warning, TEXT("[AdminPanel] AdminPanelClass nao resolvido (WBP_AdminToolsAIO ausente?)."));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!AdminPanelInstance)
|
||||
{
|
||||
AdminPanelInstance = CreateWidget<UUserWidget>(PC, AdminPanelClass);
|
||||
}
|
||||
if (AdminPanelInstance)
|
||||
{
|
||||
AdminPanelInstance->AddToViewport(1000);
|
||||
|
||||
// UIOnly: trava a acao do jogo enquanto o menu esta aberto e garante que
|
||||
// os cliques vao 100% pra UI (no GameAndUI o viewport rouba o clique).
|
||||
// O mundo continua renderizando + os overlays de debug seguem desenhando.
|
||||
FInputModeUIOnly Mode;
|
||||
Mode.SetWidgetToFocus(AdminPanelInstance->TakeWidget());
|
||||
Mode.SetLockMouseToViewportBehavior(EMouseLockMode::DoNotLock);
|
||||
PC->SetInputMode(Mode);
|
||||
PC->bShowMouseCursor = true;
|
||||
}
|
||||
}
|
||||
|
||||
void AZeusCharacter::Move(const FInputActionValue& Value)
|
||||
@@ -203,6 +286,40 @@ void AZeusCharacter::OnJumpReleased()
|
||||
DoJumpEnd();
|
||||
}
|
||||
|
||||
void AZeusCharacter::OnDashTriggered()
|
||||
{
|
||||
// Resolve UZeusGASComponent do PlayerState. Component vive la' via
|
||||
// AZeusPlayerState Component Registry (config-driven). Sem el, ASC ausente
|
||||
// e RequestActivateAbilityByTag falha — log warning.
|
||||
const APlayerState* PS = GetPlayerState();
|
||||
if (!PS)
|
||||
{
|
||||
UE_LOG(LogZeusPlayer, Warning, TEXT("OnDashTriggered: PlayerState nullptr — ignorado"));
|
||||
return;
|
||||
}
|
||||
UZeusGASComponent* Comp = PS->FindComponentByClass<UZeusGASComponent>();
|
||||
if (!Comp)
|
||||
{
|
||||
UE_LOG(LogZeusPlayer, Warning, TEXT("OnDashTriggered: UZeusGASComponent nao achado"));
|
||||
return;
|
||||
}
|
||||
|
||||
// Tag canonica casa com row do DT + JSON do server. Hash FNV calculado
|
||||
// dentro do componente (RequestActivateAbilityByTag).
|
||||
const FGameplayTag DashTag = UGameplayTagsManager::Get().RequestGameplayTag(
|
||||
FName(TEXT("Zeus.Ability.Movement.Dash")));
|
||||
if (!DashTag.IsValid())
|
||||
{
|
||||
UE_LOG(LogZeusPlayer, Warning,
|
||||
TEXT("OnDashTriggered: tag Zeus.Ability.Movement.Dash nao registrada no cliente"));
|
||||
return;
|
||||
}
|
||||
|
||||
const bool bSent = Comp->RequestActivateAbilityByTag(DashTag);
|
||||
UE_LOG(LogZeusPlayer, Log, TEXT("OnDashTriggered -> RequestActivateAbilityByTag = %s"),
|
||||
bSent ? TEXT("OK (C_ABILITY_TRY_ACTIVATE enviado)") : TEXT("FALHOU"));
|
||||
}
|
||||
|
||||
void AZeusCharacter::DoMove(const float Right, const float Forward)
|
||||
{
|
||||
PendingMoveRight = FMath::Clamp(Right, -1.0f, 1.0f);
|
||||
@@ -252,7 +369,7 @@ void AZeusCharacter::SetEntityRelevant(bool /*bRelevant*/)
|
||||
|
||||
void AZeusCharacter::ResolveZeusNetworkSubsystem()
|
||||
{
|
||||
if (ZeusNetwork)
|
||||
if (ZeusNetwork && NetClient)
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -263,7 +380,14 @@ void AZeusCharacter::ResolveZeusNetworkSubsystem()
|
||||
return;
|
||||
}
|
||||
|
||||
if (!ZeusNetwork)
|
||||
{
|
||||
ZeusNetwork = GI->GetSubsystem<UZeusNetworkSubsystem>();
|
||||
}
|
||||
if (!NetClient)
|
||||
{
|
||||
NetClient = GI->GetSubsystem<UZeusNetworkingClientSubsystem>();
|
||||
}
|
||||
}
|
||||
|
||||
void AZeusCharacter::BindZeusSpawnDelegate()
|
||||
@@ -272,49 +396,71 @@ void AZeusCharacter::BindZeusSpawnDelegate()
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (!ZeusNetwork)
|
||||
if (!NetClient && !ZeusNetwork)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ZeusNetwork->OnPlayerSpawned.AddDynamic(this, &AZeusCharacter::HandleZeusPlayerSpawned);
|
||||
// Self-entity (entityId do proprio char) vem do sistema de rede novo.
|
||||
if (NetClient)
|
||||
{
|
||||
NetClient->OnSelfEntityAssigned.AddDynamic(this, &AZeusCharacter::HandleSelfEntityAssigned);
|
||||
}
|
||||
// CHAR_INFO (nome/guild) ainda nao tem equivalente no sistema novo -> legacy.
|
||||
if (ZeusNetwork)
|
||||
{
|
||||
ZeusNetwork->OnCharInfoReceived.AddDynamic(this, &AZeusCharacter::HandleZeusCharInfo);
|
||||
}
|
||||
bSpawnDelegateBound = true;
|
||||
}
|
||||
|
||||
void AZeusCharacter::UnbindZeusSpawnDelegate()
|
||||
{
|
||||
if (!bSpawnDelegateBound || !ZeusNetwork)
|
||||
if (!bSpawnDelegateBound)
|
||||
{
|
||||
bSpawnDelegateBound = false;
|
||||
return;
|
||||
}
|
||||
ZeusNetwork->OnPlayerSpawned.RemoveDynamic(this, &AZeusCharacter::HandleZeusPlayerSpawned);
|
||||
if (NetClient)
|
||||
{
|
||||
NetClient->OnSelfEntityAssigned.RemoveDynamic(this, &AZeusCharacter::HandleSelfEntityAssigned);
|
||||
}
|
||||
if (ZeusNetwork)
|
||||
{
|
||||
ZeusNetwork->OnCharInfoReceived.RemoveDynamic(this, &AZeusCharacter::HandleZeusCharInfo);
|
||||
}
|
||||
bSpawnDelegateBound = false;
|
||||
}
|
||||
|
||||
void AZeusCharacter::TryRegisterLocalEntityFromCachedSpawn()
|
||||
{
|
||||
if (!ZeusNetwork)
|
||||
// Self-entity vem do sistema de rede novo (ENT_SELF). O subsystem e'
|
||||
// per-GameInstance e sobrevive ao OpenLevel, entao o entityId pode ja' ter
|
||||
// chegado antes deste pawn existir -- puxa o cache agora. Caso ainda nao
|
||||
// tenha chegado, o bind em OnSelfEntityAssigned cobre quando chegar.
|
||||
// A pos/yaw vem do proprio pawn (ja posicionado via PendingSpawnPose do DB);
|
||||
// o sistema novo so' carrega o entityId.
|
||||
if (NetClient)
|
||||
{
|
||||
return;
|
||||
const int64 SelfId = NetClient->GetLocalEntityId();
|
||||
if (SelfId != 0)
|
||||
{
|
||||
HandleLocalSpawnReady(SelfId, GetActorLocation(), GetActorRotation().Yaw, 0);
|
||||
}
|
||||
}
|
||||
|
||||
int64 CachedEntityId = 0;
|
||||
FVector CachedPosCm = FVector::ZeroVector;
|
||||
float CachedYawDeg = 0.0f;
|
||||
int64 CachedServerTimeMs = 0;
|
||||
if (ZeusNetwork->TryGetLastLocalSpawn(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.
|
||||
// Race fix CHAR_INFO (ainda legacy): o S_CHAR_INFO pode ter chegado antes do
|
||||
// pawn existir; aplica o ultimo nome/guild cacheado.
|
||||
TryApplyCachedCharInfo();
|
||||
}
|
||||
|
||||
void AZeusCharacter::HandleSelfEntityAssigned(const int64 InEntityId)
|
||||
{
|
||||
// Sistema novo informou o entityId do proprio char (ENT_SELF). Reusa o mesmo
|
||||
// caminho do spawn local legacy (seta ZeusEntityId + RegisterLocalEntity).
|
||||
// pos/yaw vem do pawn (ja posicionado); o sistema novo so' traz o entityId.
|
||||
HandleLocalSpawnReady(InEntityId, GetActorLocation(), GetActorRotation().Yaw, 0);
|
||||
}
|
||||
|
||||
void AZeusCharacter::HandleZeusPlayerSpawned(const int64 InEntityId, const bool bIsLocal,
|
||||
const FVector PosCm, const float YawDeg, const int64 ServerTimeMs)
|
||||
{
|
||||
@@ -388,6 +534,17 @@ void AZeusCharacter::HandleZeusCharInfo(const int64 InEntityId, const FString& C
|
||||
TEXT("AZeusCharacter: S_CHAR_INFO recebido EntityId=%lld name=%s guild=%s"),
|
||||
InEntityId, *CharName, *GuildName);
|
||||
|
||||
// 2026-06-03 fix: server envia S_CHAR_INFO de TODOS os players (proprio +
|
||||
// catch-up de proxies pre-existentes). Sem este filtro, o nome do ultimo
|
||||
// proxy recebido sobrescrevia o do pawn local (ex: player Mateuus loga e
|
||||
// ve "Olatudook" em si mesmo porque o catch-up do Olatudook chegou depois
|
||||
// do S_CHAR_INFO proprio). Proxies remotos sao roteados pelo registry no
|
||||
// UZeusWorldSubsystem (futuro nameplate por EntityId).
|
||||
if (ZeusEntityId != 0 && InEntityId != ZeusEntityId)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
APlayerState* PS = GetPlayerState();
|
||||
if (!PS)
|
||||
{
|
||||
@@ -427,7 +584,10 @@ void AZeusCharacter::TryApplyCachedCharInfo()
|
||||
void AZeusCharacter::FlushInputAxisToServer(const float DeltaSeconds)
|
||||
{
|
||||
ResolveZeusNetworkSubsystem();
|
||||
if (!ZeusNetwork || !ZeusNetwork->IsConnected())
|
||||
// Sistema de rede novo (canonico) tem prioridade; legacy e' fallback.
|
||||
const bool bV1 = (NetClient
|
||||
&& NetClient->GetConnectionState() == EZeusV1ConnectionState::Accepted);
|
||||
if (!bV1 && (!ZeusNetwork || !ZeusNetwork->IsConnected()))
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -436,7 +596,19 @@ void AZeusCharacter::FlushInputAxisToServer(const float DeltaSeconds)
|
||||
SendAccumulatorSec += DeltaSeconds;
|
||||
TimeSinceLastSendSec += DeltaSeconds;
|
||||
|
||||
const bool bMovingInput = !FMath::IsNearlyZero(PendingMoveForward) || !FMath::IsNearlyZero(PendingMoveRight);
|
||||
// Movimento p/ efeitos de rate NAO e' so' o input (W/A/S/D): o CMC do dono
|
||||
// continua desacelerando (braking) por ~0.3s depois de soltar a tecla. Se
|
||||
// pararmos de enviar no rate rapido assim que o input zera, o servidor fica
|
||||
// congelado na ultima `simpleVelCmS` alta e o proxy remoto extrapola ~1m alem
|
||||
// (NewPos = Last.Pos + Last.Vel*ExtrapSec) ate o heartbeat corrigir -> overshoot
|
||||
// + snap-back ("velocidade grande que demora a zerar" + jitter). Tratar a
|
||||
// velocidade residual como movimento mantem o envio a InputSendRateHz ate o
|
||||
// dono realmente parar, alimentando o proxy com a curva de desaceleracao.
|
||||
const bool bHasInputAxis = !FMath::IsNearlyZero(PendingMoveForward) || !FMath::IsNearlyZero(PendingMoveRight);
|
||||
const FVector OwnerVelNow = GetVelocity();
|
||||
constexpr float kResidualSpeedSqCmS = 25.0f * 25.0f; // abaixo de 25 cm/s tratamos como parado
|
||||
const bool bHasResidualVel = OwnerVelNow.SizeSquared() > kResidualSpeedSqCmS;
|
||||
const bool bMovingInput = bHasInputAxis || bHasResidualVel;
|
||||
const bool bJumpEvent = bPendingJumpPressed || bPendingJumpReleased;
|
||||
const bool bRateReached = SendAccumulatorSec >= SendInterval;
|
||||
const bool bHeartbeatReached = TimeSinceLastSendSec >= HeartbeatIntervalSec;
|
||||
@@ -450,6 +622,23 @@ void AZeusCharacter::FlushInputAxisToServer(const float DeltaSeconds)
|
||||
const bool bFallingChanged = (bIsFalling != bPreviousFalling);
|
||||
|
||||
const bool bShouldSend = bJumpEvent || bFallingChanged || (bMovingInput && bRateReached) || (!bMovingInput && bHeartbeatReached);
|
||||
// DEBUG-INPUT (2026-06-12): log ~1x/s COM o PIE. Remover apos achar o gap.
|
||||
{
|
||||
static float DbgInputAcc = 0.0f;
|
||||
DbgInputAcc += DeltaSeconds;
|
||||
if (DbgInputAcc >= 1.0f)
|
||||
{
|
||||
DbgInputAcc = 0.0f;
|
||||
const int32 PieId = (GetWorld() && GetWorld()->GetOutermost())
|
||||
? GetWorld()->GetOutermost()->GetPIEInstanceID() : -1;
|
||||
UE_LOG(LogZeusPlayer, Warning,
|
||||
TEXT("[PIE%d][InputDbg] bV1=%d state=%d moving=%d rate=%d should=%d hbReached=%d fwd=%.2f right=%.2f"),
|
||||
PieId, bV1 ? 1 : 0,
|
||||
NetClient ? static_cast<int32>(NetClient->GetConnectionState()) : -99,
|
||||
bMovingInput ? 1 : 0, bRateReached ? 1 : 0, bShouldSend ? 1 : 0,
|
||||
bHeartbeatReached ? 1 : 0, PendingMoveForward, PendingMoveRight);
|
||||
}
|
||||
}
|
||||
if (!bShouldSend)
|
||||
{
|
||||
return;
|
||||
@@ -457,18 +646,55 @@ void AZeusCharacter::FlushInputAxisToServer(const float DeltaSeconds)
|
||||
|
||||
++InputSequence;
|
||||
const int32 ClientTimeMs = static_cast<int32>(FPlatformTime::Seconds() * 1000.0);
|
||||
// ADR 0040: yaw da camara/controller para que o servidor consiga rotar o
|
||||
// input (referencial-camara) para velocidade mundial. Fallback para o yaw
|
||||
// do actor se nao houver controller (e.g. tela de loading).
|
||||
const float ViewYawDeg = (GetController()
|
||||
? static_cast<float>(GetController()->GetControlRotation().Yaw)
|
||||
: static_cast<float>(GetActorRotation().Yaw));
|
||||
// YAW DO CORPO (nao da mira): GetActorRotation().Yaw reflete o orient-to-movement
|
||||
// do CMC (bOrientRotationToMovement=true, RotationRate=(0,500,0)) -> o corpo vira
|
||||
// pra direcao do MOVIMENTO (WASD), suave a 500 deg/s, e o MOUSE nao gira o corpo
|
||||
// (bUseControllerRotationYaw=false). O servidor usa este yaw direto e o proxy nos
|
||||
// outros clientes o replica -> o proxy vira IDENTICO ao corpo do dono.
|
||||
//
|
||||
// (Historico bug 2026-06-13: antes mandava GetControlRotation().Yaw -- a MIRA --
|
||||
// e o servidor fazia atan2(vel) INSTANTANEO quando movia (proxy "virava demais"
|
||||
// 45 graus) e usava a mira quando parado (proxy seguia o mouse). ADR 0040: o
|
||||
// servidor NAO rotaciona mais o input por este yaw (ADR 0041 manda pos+vel
|
||||
// mundiais), entao trocar a mira pelo corpo e' seguro. Quando precisar da MIRA
|
||||
// real (aim/combate), adicionar um campo aimYawDeg dedicado -- nao reusar este.)
|
||||
const float BodyYawDeg = static_cast<float>(GetActorRotation().Yaw);
|
||||
// ADR 0041: cliente autoritativo da posicao XYZ + velocidade XY. O CMC ja
|
||||
// integrou o input local antes deste flush, portanto `GetActorLocation` e
|
||||
// `GetVelocity` reflectem o estado real que o servidor deve replicar para
|
||||
// os outros clientes (sujeito ao clamp anti-cheat).
|
||||
const FVector PosCm = GetActorLocation();
|
||||
const FVector Vel = GetVelocity();
|
||||
if (bV1)
|
||||
{
|
||||
// Sistema novo: INPUT 6077 com a pose absoluta (ADR 0040/0041).
|
||||
const bool bSent = NetClient->EmitInput(
|
||||
PendingMoveForward,
|
||||
PendingMoveRight,
|
||||
bPendingJumpPressed,
|
||||
bPendingJumpReleased,
|
||||
InputSequence,
|
||||
ClientTimeMs,
|
||||
BodyYawDeg,
|
||||
PosCm,
|
||||
FVector2D(Vel.X, Vel.Y),
|
||||
bIsFalling,
|
||||
static_cast<float>(Vel.Z));
|
||||
// DEBUG-INPUT (2026-06-12): confirma chamada + retorno do SendPacket.
|
||||
static float DbgEmitAcc = 0.0f;
|
||||
DbgEmitAcc += DeltaSeconds;
|
||||
if (DbgEmitAcc >= 1.0f)
|
||||
{
|
||||
DbgEmitAcc = 0.0f;
|
||||
const int32 PieId = (GetWorld() && GetWorld()->GetOutermost())
|
||||
? GetWorld()->GetOutermost()->GetPIEInstanceID() : -1;
|
||||
UE_LOG(LogZeusPlayer, Warning,
|
||||
TEXT("[PIE%d][InputDbg] EmitInput seq=%d sent=%d pos=%s"),
|
||||
PieId, InputSequence, bSent ? 1 : 0, *PosCm.ToString());
|
||||
}
|
||||
}
|
||||
else if (ZeusNetwork)
|
||||
{
|
||||
ZeusNetwork->SendInputAxis(
|
||||
PendingMoveForward,
|
||||
PendingMoveRight,
|
||||
@@ -476,11 +702,12 @@ void AZeusCharacter::FlushInputAxisToServer(const float DeltaSeconds)
|
||||
bPendingJumpReleased,
|
||||
InputSequence,
|
||||
ClientTimeMs,
|
||||
ViewYawDeg,
|
||||
BodyYawDeg,
|
||||
PosCm,
|
||||
FVector2D(Vel.X, Vel.Y),
|
||||
bIsFalling,
|
||||
static_cast<float>(Vel.Z));
|
||||
}
|
||||
|
||||
SendAccumulatorSec = 0.0f;
|
||||
TimeSinceLastSendSec = 0.0f;
|
||||
|
||||
@@ -11,6 +11,9 @@ class USpringArmComponent;
|
||||
class UCameraComponent;
|
||||
class UInputAction;
|
||||
class UZeusNetworkSubsystem;
|
||||
class UZeusNetworkingClientSubsystem;
|
||||
class UZeusAOIComponent;
|
||||
class UUserWidget;
|
||||
struct FInputActionValue;
|
||||
|
||||
DECLARE_LOG_CATEGORY_EXTERN(LogZeusPlayer, Log, All);
|
||||
@@ -46,6 +49,10 @@ class ZMMO_API AZeusCharacter : public ACharacter, public IZeusEntityInterface
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Components", meta = (AllowPrivateAccess = "true"))
|
||||
UCameraComponent* FollowCamera;
|
||||
|
||||
/** AOI do cliente (+ overlays de debug dirigidos pelo Zeus Admin Tools). */
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Zeus|AOI", meta = (AllowPrivateAccess = "true"))
|
||||
TObjectPtr<UZeusAOIComponent> AOIComponent;
|
||||
|
||||
// GAS Component vive no PlayerState (AZeusPlayerState + Component Registry).
|
||||
// Acesso: GetPlayerState<AZeusPlayerState>()->GetZeusComponent<UZeusGASComponent>().
|
||||
|
||||
@@ -62,6 +69,16 @@ protected:
|
||||
UPROPERTY(EditAnywhere, Category = "Input")
|
||||
UInputAction* MouseLookAction;
|
||||
|
||||
/// IA_Dash — dispara C_ABILITY_TRY_ACTIVATE pra Zeus.Ability.Movement.Dash
|
||||
/// via UZeusGASComponent. Bind padrao no IMC_Default: LeftShift -> Pressed.
|
||||
UPROPERTY(EditAnywhere, Category = "Input")
|
||||
UInputAction* DashAction;
|
||||
|
||||
/** Painel Zeus Admin Tools (AIO Debug), aberto/fechado por F8. Default =
|
||||
* WBP_AdminToolsAIO (resolvido no construtor). */
|
||||
UPROPERTY(EditAnywhere, Category = "Zeus|Admin")
|
||||
TSubclassOf<UUserWidget> AdminPanelClass;
|
||||
|
||||
/** Cadencia de envio dos pacotes `C_INPUT_AXIS` (Hz). */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Zeus|Networking", meta = (ClampMin = "1", ClampMax = "120"))
|
||||
int32 InputSendRateHz = 30;
|
||||
@@ -104,6 +121,14 @@ protected:
|
||||
void OnJumpPressed();
|
||||
void OnJumpReleased();
|
||||
|
||||
/// IA_Dash Triggered -> resolve UZeusGASComponent do PlayerState e chama
|
||||
/// RequestActivateAbilityByTag("Zeus.Ability.Movement.Dash"). Server valida
|
||||
/// + responde S_ABILITY_ACTIVATED (HP/SP atualizado, montage roda local).
|
||||
void OnDashTriggered();
|
||||
|
||||
/** F8: cria/mostra ou esconde o painel Zeus Admin Tools (input mode UI + cursor). */
|
||||
void ToggleAdminPanel();
|
||||
|
||||
public:
|
||||
/** Wrappers expostos a Blueprint (UI / mobile / automation). */
|
||||
UFUNCTION(BlueprintCallable, Category = "Input")
|
||||
@@ -140,6 +165,11 @@ private:
|
||||
UFUNCTION()
|
||||
void HandleZeusPlayerSpawned(int64 InEntityId, bool bIsLocal, FVector PosCm, float YawDeg, int64 ServerTimeMs);
|
||||
|
||||
// Self-entity do sistema de rede novo (ENT_SELF): o cliente aprende o
|
||||
// proprio entityId. Substitui o caminho legacy OnPlayerSpawned(bIsLocal=true).
|
||||
UFUNCTION()
|
||||
void HandleSelfEntityAssigned(int64 InEntityId);
|
||||
|
||||
UFUNCTION()
|
||||
void HandleZeusCharInfo(int64 InEntityId, const FString& CharName, const FString& GuildName);
|
||||
|
||||
@@ -148,6 +178,14 @@ private:
|
||||
UPROPERTY()
|
||||
TObjectPtr<UZeusNetworkSubsystem> ZeusNetwork;
|
||||
|
||||
// Sistema de rede novo (canonico): fonte do self-entity (ENT_SELF).
|
||||
UPROPERTY()
|
||||
TObjectPtr<UZeusNetworkingClientSubsystem> NetClient;
|
||||
|
||||
/** Instancia viva do painel admin (criada lazy no primeiro F8). */
|
||||
UPROPERTY(Transient)
|
||||
TObjectPtr<UUserWidget> AdminPanelInstance;
|
||||
|
||||
bool bSpawnDelegateBound = false;
|
||||
|
||||
/** Identidade autoritativa atribuida quando o servidor envia S_SPAWN_PLAYER local. */
|
||||
|
||||
@@ -132,11 +132,18 @@ void AZeusPlayerProxy::Tick(const float DeltaSeconds)
|
||||
// conhecida, clampada por `MaxExtrapolationSeconds` para nao deixar
|
||||
// o proxy fugir se a rede ficar muda.
|
||||
const FZeusProxySnapshot& Last = SnapshotBuffer.Last();
|
||||
const float ExtrapSec = FMath::Clamp(
|
||||
static_cast<float>(RenderMs - Last.ServerTimeMs) / 1000.0f,
|
||||
0.0f, MaxExtrapolationSeconds);
|
||||
const float RawExtrapSec =
|
||||
static_cast<float>(RenderMs - Last.ServerTimeMs) / 1000.0f;
|
||||
const float ExtrapSec = FMath::Clamp(RawExtrapSec, 0.0f, MaxExtrapolationSeconds);
|
||||
NewPos = Last.PosCm + Last.VelCmS * ExtrapSec;
|
||||
NewVel = Last.VelCmS;
|
||||
// STARVATION (buffer=1 por muito tempo -- ex: 1o login sem DELTA_BATCH por
|
||||
// segundos): a extrapolacao satura no clamp e a POSE congela, mas manter a
|
||||
// velocidade faz o AnimBP rodar locomocao PARADO ("andar no lugar"/moonwalk
|
||||
// = o "jitter" reportado). Se ja passamos bem do clamp, zera a vel visual
|
||||
// pra o proxy ir pra Idle ate' os deltas voltarem (em vez de animar
|
||||
// andando travado). Quando os deltas chegam, o ramo de interpolacao retoma.
|
||||
const bool bStarved = RawExtrapSec > (MaxExtrapolationSeconds + 0.15f);
|
||||
NewVel = bStarved ? FVector::ZeroVector : Last.VelCmS;
|
||||
bExtrapolating = ExtrapSec > KINDA_SMALL_NUMBER;
|
||||
}
|
||||
else
|
||||
@@ -150,12 +157,32 @@ void AZeusPlayerProxy::Tick(const float DeltaSeconds)
|
||||
NewVel = FMath::Lerp(A.VelCmS, B.VelCmS, InterpAlpha);
|
||||
}
|
||||
|
||||
// Yaw deriva da velocidade interpolada: locomotion mantem face na direcao
|
||||
// do movimento; em idle preserva o yaw atual para nao "snappar".
|
||||
// ORIENTACAO DO CORPO = REPLICA do yaw autoritativo do servidor (Snapshot.YawDeg),
|
||||
// interpolado shortest-path entre os MESMOS A/B da posicao. Esse yaw agora carrega
|
||||
// o yaw do CORPO do dono (orient-to-movement do CMC: vira pra direcao do MOVIMENTO
|
||||
// /WASD, suave a 500 deg/s; parado mantem; o MOUSE nao gira o corpo) -- o cliente
|
||||
// dono manda GetActorRotation().Yaw e o servidor repassa direto. Por isso o proxy
|
||||
// so' REPLICA: vira identico ao corpo do dono, sem re-derivar nada.
|
||||
//
|
||||
// NAO derivamos de Atan2(velocidade): isso era a fonte do bug 2026-06-13 ("virava
|
||||
// demais 45 graus") -- a direcao da velocidade salta, o yaw do corpo nao. A
|
||||
// suavizacao correta ja' aconteceu no CMC do dono; aqui so' interpolamos amostras.
|
||||
//
|
||||
// FUTURO -- Motion Matching/aim: quando "a mira dita a locomocao" (strafe/aim
|
||||
// offset), o servidor passara a mandar um aimYawDeg separado e o AnimBP usara a
|
||||
// velocidade relativa a este yaw pra escolher fwd/back/left/right. Por ORA, 1 yaw.
|
||||
FRotator NewRot = GetActorRotation();
|
||||
if (FVector(NewVel.X, NewVel.Y, 0.0f).SizeSquared() > 1.0f)
|
||||
if (IdxB == INDEX_NONE)
|
||||
{
|
||||
NewRot.Yaw = FMath::RadiansToDegrees(FMath::Atan2(NewVel.Y, NewVel.X));
|
||||
NewRot.Yaw = SnapshotBuffer.Last().YawDeg; // extrapolando: segura o ultimo yaw
|
||||
}
|
||||
else
|
||||
{
|
||||
const float YawA = SnapshotBuffer[IdxB - 1].YawDeg;
|
||||
const float YawB = SnapshotBuffer[IdxB].YawDeg;
|
||||
// FindDeltaAngleDegrees -> menor arco (-180..180); evita girar "o lado longo".
|
||||
const float DeltaYaw = FMath::FindDeltaAngleDegrees(YawA, YawB);
|
||||
NewRot.Yaw = YawA + DeltaYaw * InterpAlpha;
|
||||
}
|
||||
NewRot.Pitch = 0.0f;
|
||||
NewRot.Roll = 0.0f;
|
||||
@@ -207,10 +234,26 @@ void AZeusPlayerProxy::Tick(const float DeltaSeconds)
|
||||
LastDiagLogSec = NowSec;
|
||||
const int64 NewestMs = SnapshotBuffer.Last().ServerTimeMs;
|
||||
const int64 RenderLagMs = ServerNowMs - NewestMs;
|
||||
UE_LOG(LogZMMO, Verbose,
|
||||
TEXT("ZeusPlayerProxy[%lld] buffer=%d renderLagMs=%lld interpAlpha=%.2f extrap=%d delayMs=%.0f"),
|
||||
EntityId, SnapshotBuffer.Num(), RenderLagMs, InterpAlpha,
|
||||
bExtrapolating ? 1 : 0, InterpolationDelayMs);
|
||||
// === DIAG PERMANENTE -- jitter por FOME DE DELTAS ========================
|
||||
// Bug investigado/resolvido 2026-06-13 (ver memoria
|
||||
// project_proxy_delta_starvation_jitter). Este Warning FICA DE PROPOSITO
|
||||
// (decisao do dono: NAO rebaixar p/ Verbose nem remover) -- e' a sonda pra
|
||||
// cacar regressao do jitter de proxy. Como ler:
|
||||
// secsSinceSnap = ha quanto tempo o proxy NAO recebe snapshot. Sobe = FOME
|
||||
// (player remoto parado -> ComputeDelta suprime no server -> sem DELTA_BATCH).
|
||||
// newestMs = serverTimeMs do ultimo snapshot; CONGELA na fome (deveria avancar).
|
||||
// renderLagMs = ServerNowMs - newestMs; cresce 1:1 com o wall-clock na fome.
|
||||
// speed=0 na fome = a mitigacao (zera a vel visual -> Idle, sem "moonwalk") agindo.
|
||||
// Ao o player voltar a se mover sai "buffer STALE ... reset+reseed" (anti-stale)
|
||||
// e renderLagMs/secsSinceSnap voltam a ~0 (snap limpo, sem salto/jitter).
|
||||
// =========================================================================
|
||||
const double SecsSinceSnap =
|
||||
(LastSnapshotRecvSec > 0.0) ? (NowSec - LastSnapshotRecvSec) : -1.0;
|
||||
UE_LOG(LogZMMO, Warning,
|
||||
TEXT("ZeusPlayerProxy[%lld] buffer=%d renderLagMs=%lld newestMs=%lld secsSinceSnap=%.1f interpAlpha=%.2f extrap=%d speed=%.0f"),
|
||||
EntityId, SnapshotBuffer.Num(), RenderLagMs, NewestMs, SecsSinceSnap, InterpAlpha,
|
||||
bExtrapolating ? 1 : 0,
|
||||
FVector(VisualVelocity.X, VisualVelocity.Y, 0.0f).Size());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -222,18 +265,65 @@ void AZeusPlayerProxy::SetZeusIdentity(const int64 InEntityId, const EZeusEntity
|
||||
|
||||
void AZeusPlayerProxy::ApplyEntitySnapshot(const FZeusEntitySnapshot& Snapshot)
|
||||
{
|
||||
// Estabiliza o offset de relogio na primeira amostra. Subsequente nao
|
||||
// re-sincroniza para evitar saltos visuais; um esquema mais sofisticado
|
||||
// (ema do offset) cabe quando tivermos ping/RTT estabilizado por sessao.
|
||||
LastSnapshotRecvSec = FPlatformTime::Seconds(); // DIAG: marca chegada (mede a fome no Tick)
|
||||
|
||||
// 2026-06-06 H3 fix — disciplina o ServerClockOffsetMs via EMA com clamp.
|
||||
// Antes: offset congelado na 1a amostra. Em cross-server handoff a fonte
|
||||
// do ServerTimeMs muda (publisher antigo -> novo) e o offset fica
|
||||
// permanentemente skewed, alimentando tremor pos-handoff e drift cumulativo
|
||||
// de clock cliente vs server. Agora cada amostra recalcula candidato e
|
||||
// suaviza (alpha=0.02, ~50 amostras pra convergir) com clamp max 2ms/snapshot
|
||||
// pra evitar salto visual durante transicao.
|
||||
const int64 LocalNowMs = static_cast<int64>(FPlatformTime::Seconds() * 1000.0);
|
||||
const int64 OffsetCandidate = LocalNowMs - Snapshot.ServerTimeMs;
|
||||
|
||||
// Anti-stale (2026-06-13): se este snapshot esta MUITO a frente do mais novo
|
||||
// do buffer, o proxy ficou "mudo" por segundos -- player remoto parado ->
|
||||
// ComputeDelta suprime -> sem DELTA_BATCH novo -> renderLagMs disparou pra
|
||||
// dezenas de segundos. Manter os snapshots velhos faz a interpolacao usar um
|
||||
// span gigante (pose de ~100s atras -> agora) quando o player volta a se mover,
|
||||
// causando rasteio/salto (o jitter reportado). Descarta o buffer obsoleto +
|
||||
// re-bootstrapa o relogio pra a interpolacao recomecar limpa neste keyframe.
|
||||
if (SnapshotBuffer.Num() > 0
|
||||
&& (Snapshot.ServerTimeMs - SnapshotBuffer.Last().ServerTimeMs) > 1000)
|
||||
{
|
||||
UE_LOG(LogZMMO, Warning,
|
||||
TEXT("ZeusPlayerProxy[%lld] buffer STALE gap=%lldms -> reset+reseed"),
|
||||
EntityId, Snapshot.ServerTimeMs - SnapshotBuffer.Last().ServerTimeMs);
|
||||
SnapshotBuffer.Reset();
|
||||
ServerClockOffsetMs = OffsetCandidate;
|
||||
}
|
||||
|
||||
if (SnapshotBuffer.Num() == 0)
|
||||
{
|
||||
const int64 LocalNowMs = static_cast<int64>(FPlatformTime::Seconds() * 1000.0);
|
||||
ServerClockOffsetMs = LocalNowMs - Snapshot.ServerTimeMs;
|
||||
// Primeira amostra: bootstrap direto (nao tem baseline pra suavizar).
|
||||
ServerClockOffsetMs = OffsetCandidate;
|
||||
// DIAG (jitter 1o login): confirma o seed do relogio. serverTimeMs deve ser
|
||||
// != 0 e o offset razoavel; se vier 0 aqui, o keyframe/catch-up corrompeu o
|
||||
// bootstrap (hipotese descartada, mas o log fecha a questao em campo).
|
||||
UE_LOG(LogZMMO, Warning,
|
||||
TEXT("ZeusPlayerProxy[%lld] SEED clock serverTimeMs=%lld localNowMs=%lld offset=%lld"),
|
||||
EntityId, Snapshot.ServerTimeMs, LocalNowMs, ServerClockOffsetMs);
|
||||
}
|
||||
else
|
||||
{
|
||||
const double Alpha = 0.02;
|
||||
const int64 RawTarget = static_cast<int64>(
|
||||
static_cast<double>(ServerClockOffsetMs) * (1.0 - Alpha)
|
||||
+ static_cast<double>(OffsetCandidate) * Alpha);
|
||||
// Clamp max 2ms por amostra — em 30Hz isso converge a 60ms/s, mais que
|
||||
// o suficiente pra absorver drift normal sem causar tremor visual.
|
||||
const int64 Delta = RawTarget - ServerClockOffsetMs;
|
||||
const int64 ClampedDelta = (Delta > 2) ? 2
|
||||
: (Delta < -2) ? -2
|
||||
: Delta;
|
||||
ServerClockOffsetMs += ClampedDelta;
|
||||
}
|
||||
|
||||
FZeusProxySnapshot S;
|
||||
S.PosCm = Snapshot.PositionCm;
|
||||
S.VelCmS = Snapshot.VelocityCmS;
|
||||
S.YawDeg = Snapshot.YawDeg; // yaw do CORPO do dono (orient-to-movement; mouse nao gira)
|
||||
S.bGrounded = Snapshot.bGrounded;
|
||||
S.ServerTimeMs = Snapshot.ServerTimeMs;
|
||||
|
||||
|
||||
@@ -42,6 +42,15 @@ struct FZeusProxySnapshot
|
||||
UPROPERTY()
|
||||
FVector VelCmS = FVector::ZeroVector;
|
||||
|
||||
/** Yaw autoritativo do servidor (graus) = o MESMO yaw que o player LOCAL aplicou
|
||||
* ao virar. O proxy replica EXATAMENTE a viradinha do local (apertou D = mesma
|
||||
* virada nos dois). Sem armazenar isto, o proxy derivava o yaw da velocidade
|
||||
* (Atan2) e "virava totalmente pra direcao do movimento" (bug 2026-06-13).
|
||||
* NAO confundir com "mira dita a locomocao" (strafe / Motion Matching) -- isso
|
||||
* esta' DESABILITADO por ora (ver AVISO em ZeusPlayerProxy.cpp::Tick). */
|
||||
UPROPERTY()
|
||||
float YawDeg = 0.0f;
|
||||
|
||||
UPROPERTY()
|
||||
bool bGrounded = true;
|
||||
|
||||
@@ -140,6 +149,12 @@ protected:
|
||||
*/
|
||||
int64 ServerClockOffsetMs = 0;
|
||||
|
||||
/** DIAG (2026-06-13): instante (s, monotonic) do ultimo snapshot recebido em
|
||||
* ApplyEntitySnapshot. Usado no log do Tick pra medir "secsSinceSnap" = quanto
|
||||
* tempo o proxy ficou MUDO (sem delta). secsSinceSnap grande == fome (player
|
||||
* remoto parado -> ComputeDelta suprime). 0 = nenhum snapshot ainda. */
|
||||
double LastSnapshotRecvSec = 0.0;
|
||||
|
||||
/** Aceleracao derivada (com smoothing) escrita no CMC custom para alimentar
|
||||
* o AnimBP (`ShouldMove`/`Jump_Start` exigem Acceleration!=0). */
|
||||
FVector LastDerivedAccelerationCmS2 = FVector::ZeroVector;
|
||||
|
||||
@@ -5,10 +5,47 @@
|
||||
#include "ZeusPlayerController.h"
|
||||
#include "ZeusPlayerState.h"
|
||||
|
||||
#include "UObject/ConstructorHelpers.h"
|
||||
|
||||
AZeusGameMode::AZeusGameMode()
|
||||
{
|
||||
// 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: 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();
|
||||
}
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
#include "ZeusPlayerState.h"
|
||||
|
||||
#include "AbilitySystemComponent.h"
|
||||
#include "Components/ActorComponent.h"
|
||||
#include "Misc/ConfigCacheIni.h"
|
||||
#include "Net/UnrealNetwork.h"
|
||||
#include "ZMMO.h"
|
||||
#include "ZeusAbilitySystemComponent.h"
|
||||
#include "ZeusAttributeSet.h"
|
||||
#include "ZeusGASComponent.h"
|
||||
|
||||
AZeusPlayerState::AZeusPlayerState()
|
||||
{
|
||||
@@ -98,3 +102,32 @@ void AZeusPlayerState::SetCharInfo(const FString& InCharName, const FString& InG
|
||||
CharName = InCharName;
|
||||
GuildName = InGuildName;
|
||||
}
|
||||
|
||||
UAbilitySystemComponent* AZeusPlayerState::GetAbilitySystemComponent() const
|
||||
{
|
||||
// ASC vive como subobject do UZeusGASComponent (que esta no Component
|
||||
// Registry). UE5 GAS nodes BP ("Get Ability System Component", "Apply
|
||||
// Gameplay Effect To Target", etc.) chamam isto via IAbilitySystemInterface.
|
||||
// O retorno e o ponteiro upcastado pra UAbilitySystemComponent (base) —
|
||||
// mas em runtime e' UZeusAbilitySystemComponent (cast tipado via
|
||||
// GetZeusAbilitySystemComponent abaixo).
|
||||
return GetZeusAbilitySystemComponent();
|
||||
}
|
||||
|
||||
UZeusAbilitySystemComponent* AZeusPlayerState::GetZeusAbilitySystemComponent() const
|
||||
{
|
||||
if (UZeusGASComponent* GAS = GetZeusComponent<UZeusGASComponent>())
|
||||
{
|
||||
return GAS->AbilitySystemComponent.Get();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
UZeusAttributeSet* AZeusPlayerState::GetZeusAttributeSet() const
|
||||
{
|
||||
if (UZeusGASComponent* GAS = GetZeusComponent<UZeusGASComponent>())
|
||||
{
|
||||
return GAS->AttributeSet.Get();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "AbilitySystemInterface.h"
|
||||
#include "CoreMinimal.h"
|
||||
#include "GameFramework/PlayerState.h"
|
||||
#include "Templates/SubclassOf.h"
|
||||
@@ -7,6 +8,8 @@
|
||||
|
||||
class FLifetimeProperty;
|
||||
class UActorComponent;
|
||||
class UZeusAbilitySystemComponent;
|
||||
class UZeusAttributeSet;
|
||||
|
||||
/**
|
||||
* PlayerState do MMO. Pattern UE5 idiomatico: o GameMode atribui
|
||||
@@ -45,13 +48,36 @@ class UActorComponent;
|
||||
* ou o helper `GetZeusComponent<T>()`.
|
||||
*/
|
||||
UCLASS(Config = Game, BlueprintType)
|
||||
class ZMMO_API AZeusPlayerState : public APlayerState
|
||||
class ZMMO_API AZeusPlayerState : public APlayerState, public IAbilitySystemInterface
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
AZeusPlayerState();
|
||||
|
||||
// === IAbilitySystemInterface (UE5 GAS) ===
|
||||
//
|
||||
// Padrao Lyra: PlayerState implementa a interface; o ASC vive como
|
||||
// subobject do UZeusGASComponent (Component Registry). Isso permite que
|
||||
// qualquer Actor que receba este PlayerState resolva o ASC via cast
|
||||
// pra IAbilitySystemInterface OU via node BP "Get Ability System
|
||||
// Component" (que faz o mesmo internamente).
|
||||
//
|
||||
// Acesso BP: "Get Ability System Component" + pin do PlayerState.
|
||||
// Acesso C++: `Cast<IAbilitySystemInterface>(PlayerState)->GetAbilitySystemComponent()`.
|
||||
virtual UAbilitySystemComponent* GetAbilitySystemComponent() const override;
|
||||
|
||||
/// Versao tipada do ASC custom — retorna direto UZeusAbilitySystemComponent
|
||||
/// (os 12 overrides que falam com o servidor custom via opcodes ZeusNetwork).
|
||||
/// Prefira este sobre o getter da interface no BP — evita cast + expoe a API
|
||||
/// do ASC custom (RequestActivateAbility, etc.).
|
||||
UFUNCTION(BlueprintPure, Category = "Zeus|GAS")
|
||||
UZeusAbilitySystemComponent* GetZeusAbilitySystemComponent() const;
|
||||
|
||||
/// Helper BP-friendly pro AttributeSet tipado (template C++ nao expoe pra BP).
|
||||
UFUNCTION(BlueprintPure, Category = "Zeus|GAS")
|
||||
UZeusAttributeSet* GetZeusAttributeSet() const;
|
||||
|
||||
// === Identidade publica ===
|
||||
//
|
||||
// Todos os campos publicos sao Replicated — hoje o cliente UE5 roda em
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user