diff --git a/.gitignore b/.gitignore index 730b96b..34ed06f 100644 --- a/.gitignore +++ b/.gitignore @@ -29,3 +29,6 @@ desktop.ini *.log *.pdb .vscode/ + +# Hyper / ZeusUMGForge web converter outputs (assets gerados, não versionar) +Content/AutoCreated/ diff --git a/.mcp.json b/.mcp.json index e12937c..bc8ca7d 100644 --- a/.mcp.json +++ b/.mcp.json @@ -10,6 +10,16 @@ { "type": "http", "url": "http://localhost:5354/mcp" + }, + "ZeusIntelligence": + { + "type": "http", + "url": "http://127.0.0.1:5058/mcp" + }, + "zeus": + { + "type": "http", + "url": "http://127.0.0.1:5058/mcp" } } } \ No newline at end of file diff --git a/Config/DefaultEditor.ini b/Config/DefaultEditor.ini index e4c9bb4..6efe961 100644 --- a/Config/DefaultEditor.ini +++ b/Config/DefaultEditor.ini @@ -12,3 +12,21 @@ bBlueprintIsNotBlueprintType= true +Profiles=(ProfileName="Grey Wireframe",bSharedProfile=True,bIsEngineDefaultProfile=True,bUseSkyLighting=True,DirectionalLightIntensity=1.000000,DirectionalLightColor=(R=1.000000,G=1.000000,B=1.000000,A=1.000000),SkyLightIntensity=1.000000,bRotateLightingRig=False,bShowEnvironment=False,bShowFloor=False,bShowGrid=True,EnvironmentColor=(R=0.039216,G=0.039216,B=0.039216,A=1.000000),EnvironmentIntensity=1.000000,EnvironmentCubeMapPath="/Engine/EditorMaterials/AssetViewer/EpicQuadPanorama_CC+EV1.EpicQuadPanorama_CC+EV1",bPostProcessingEnabled=False,PostProcessingSettings=(bOverride_TemperatureType=False,bOverride_WhiteTemp=False,bOverride_WhiteTint=False,bOverride_ColorSaturation=False,bOverride_ColorContrast=False,bOverride_ColorGamma=False,bOverride_ColorGain=False,bOverride_ColorOffset=False,bOverride_ColorSaturationShadows=False,bOverride_ColorContrastShadows=False,bOverride_ColorGammaShadows=False,bOverride_ColorGainShadows=False,bOverride_ColorOffsetShadows=False,bOverride_ColorSaturationMidtones=False,bOverride_ColorContrastMidtones=False,bOverride_ColorGammaMidtones=False,bOverride_ColorGainMidtones=False,bOverride_ColorOffsetMidtones=False,bOverride_ColorSaturationHighlights=False,bOverride_ColorContrastHighlights=False,bOverride_ColorGammaHighlights=False,bOverride_ColorGainHighlights=False,bOverride_ColorOffsetHighlights=False,bOverride_ColorCorrectionShadowsMax=False,bOverride_ColorCorrectionHighlightsMin=False,bOverride_ColorCorrectionHighlightsMax=False,bOverride_BlueCorrection=False,bOverride_ExpandGamut=False,bOverride_ToneCurveAmount=False,bOverride_FilmSlope=False,bOverride_FilmToe=False,bOverride_FilmShoulder=False,bOverride_FilmBlackClip=False,bOverride_FilmWhiteClip=False,bOverride_SceneColorTint=False,bOverride_SceneFringeIntensity=False,bOverride_ChromaticAberrationStartOffset=False,bOverride_bMegaLights=False,bOverride_AmbientCubemapTint=False,bOverride_AmbientCubemapIntensity=False,bOverride_BloomMethod=False,bOverride_BloomIntensity=False,bOverride_BloomGaussianIntensity=False,bOverride_BloomThreshold=False,bOverride_Bloom1Tint=False,bOverride_Bloom1Size=False,bOverride_Bloom2Size=False,bOverride_Bloom2Tint=False,bOverride_Bloom3Tint=False,bOverride_Bloom3Size=False,bOverride_Bloom4Tint=False,bOverride_Bloom4Size=False,bOverride_Bloom5Tint=False,bOverride_Bloom5Size=False,bOverride_Bloom6Tint=False,bOverride_Bloom6Size=False,bOverride_BloomSizeScale=False,bOverride_BloomConvolutionIntensity=False,bOverride_BloomConvolutionTexture=False,bOverride_BloomConvolutionScatterDispersion=False,bOverride_BloomConvolutionSize=False,bOverride_BloomConvolutionCenterUV=False,bOverride_BloomConvolutionPreFilterMin=False,bOverride_BloomConvolutionPreFilterMax=False,bOverride_BloomConvolutionPreFilterMult=False,bOverride_BloomConvolutionBufferScale=False,bOverride_BloomDirtMaskIntensity=False,bOverride_BloomDirtMaskTint=False,bOverride_BloomDirtMask=False,bOverride_CameraShutterSpeed=False,bOverride_CameraISO=False,bOverride_AutoExposureMethod=False,bOverride_AutoExposureLowPercent=False,bOverride_AutoExposureHighPercent=False,bOverride_AutoExposureMinBrightness=False,bOverride_AutoExposureMaxBrightness=False,bOverride_AutoExposureSpeedUp=False,bOverride_AutoExposureSpeedDown=False,bOverride_AutoExposureBias=False,bOverride_AutoExposureBiasCurve=False,bOverride_AutoExposureMeterMask=False,bOverride_AutoExposureApplyPhysicalCameraExposure=False,bOverride_HistogramLogMin=False,bOverride_HistogramLogMax=False,bOverride_LocalExposureMethod=False,bOverride_LocalExposureHighlightContrastScale=False,bOverride_LocalExposureShadowContrastScale=False,bOverride_LocalExposureHighlightContrastCurve=False,bOverride_LocalExposureShadowContrastCurve=False,bOverride_LocalExposureHighlightThreshold=False,bOverride_LocalExposureShadowThreshold=False,bOverride_LocalExposureDetailStrength=False,bOverride_LocalExposureBlurredLuminanceBlend=False,bOverride_LocalExposureBlurredLuminanceKernelSizePercent=False,bOverride_LocalExposureHighlightThresholdStrength=False,bOverride_LocalExposureShadowThresholdStrength=False,bOverride_LocalExposureMiddleGreyBias=False,bOverride_LensFlareIntensity=False,bOverride_LensFlareTint=False,bOverride_LensFlareTints=False,bOverride_LensFlareBokehSize=False,bOverride_LensFlareBokehShape=False,bOverride_LensFlareThreshold=False,bOverride_VignetteIntensity=False,bOverride_Sharpen=False,bOverride_FilmGrainIntensity=False,bOverride_FilmGrainIntensityShadows=False,bOverride_FilmGrainIntensityMidtones=False,bOverride_FilmGrainIntensityHighlights=False,bOverride_FilmGrainShadowsMax=False,bOverride_FilmGrainHighlightsMin=False,bOverride_FilmGrainHighlightsMax=False,bOverride_FilmGrainTexelSize=False,bOverride_FilmGrainTexture=False,bOverride_AmbientOcclusionIntensity=False,bOverride_AmbientOcclusionStaticFraction=False,bOverride_AmbientOcclusionRadius=False,bOverride_AmbientOcclusionFadeDistance=False,bOverride_AmbientOcclusionFadeRadius=False,bOverride_AmbientOcclusionRadiusInWS=False,bOverride_AmbientOcclusionPower=False,bOverride_AmbientOcclusionBias=False,bOverride_AmbientOcclusionQuality=False,bOverride_AmbientOcclusionMipBlend=False,bOverride_AmbientOcclusionMipScale=False,bOverride_AmbientOcclusionMipThreshold=False,bOverride_AmbientOcclusionTemporalBlendWeight=False,bOverride_RayTracingAO=False,bOverride_RayTracingAOSamplesPerPixel=False,bOverride_RayTracingAOIntensity=False,bOverride_RayTracingAORadius=False,bOverride_IndirectLightingColor=False,bOverride_IndirectLightingIntensity=False,bOverride_ColorGradingIntensity=False,bOverride_ColorGradingLUT=False,bOverride_DepthOfFieldFocalDistance=False,bOverride_DepthOfFieldFstop=False,bOverride_DepthOfFieldMinFstop=False,bOverride_DepthOfFieldBladeCount=False,bOverride_DepthOfFieldSensorWidth=False,bOverride_DepthOfFieldSqueezeFactor=False,bOverride_DepthOfFieldDepthBlurRadius=False,bOverride_DepthOfFieldUseHairDepth=False,bOverride_DepthOfFieldPetzvalBokeh=False,bOverride_DepthOfFieldPetzvalBokehFalloff=False,bOverride_DepthOfFieldPetzvalExclusionBoxExtents=False,bOverride_DepthOfFieldPetzvalExclusionBoxRadius=False,bOverride_DepthOfFieldAspectRatioScalar=False,bOverride_DepthOfFieldMatteBoxFlags=False,bOverride_DepthOfFieldBarrelRadius=False,bOverride_DepthOfFieldBarrelLength=False,bOverride_DepthOfFieldDepthBlurAmount=False,bOverride_DepthOfFieldFocalRegion=False,bOverride_DepthOfFieldNearTransitionRegion=False,bOverride_DepthOfFieldFarTransitionRegion=False,bOverride_DepthOfFieldScale=False,bOverride_DepthOfFieldNearBlurSize=False,bOverride_DepthOfFieldFarBlurSize=False,bOverride_MobileHQGaussian=False,bOverride_DepthOfFieldOcclusion=False,bOverride_DepthOfFieldSkyFocusDistance=False,bOverride_DepthOfFieldVignetteSize=False,bOverride_MotionBlurAmount=False,bOverride_MotionBlurMax=False,bOverride_MotionBlurTargetFPS=False,bOverride_MotionBlurPerObjectSize=False,bOverride_ReflectionMethod=False,bOverride_LumenReflectionQuality=False,bOverride_ScreenSpaceReflectionIntensity=False,bOverride_ScreenSpaceReflectionQuality=False,bOverride_ScreenSpaceReflectionMaxRoughness=False,bOverride_ScreenSpaceReflectionRoughnessScale=False,bOverride_UserFlags=False,bOverride_RayTracingReflectionsMaxRoughness=False,bOverride_RayTracingReflectionsMaxBounces=False,bOverride_RayTracingReflectionsSamplesPerPixel=False,bOverride_RayTracingReflectionsShadows=False,bOverride_RayTracingReflectionsTranslucency=False,bOverride_TranslucencyType=False,bOverride_RayTracingTranslucencyMaxRoughness=False,bOverride_RayTracingTranslucencyRefractionRays=False,bOverride_RayTracingTranslucencySamplesPerPixel=False,bOverride_RayTracingTranslucencyShadows=False,bOverride_RayTracingTranslucencyRefraction=False,bOverride_RayTracingTranslucencyMaxPrimaryHitEvents=False,bOverride_RayTracingTranslucencyMaxSecondaryHitEvents=False,bOverride_RayTracingTranslucencyUseRayTracedRefraction=False,bOverride_DynamicGlobalIlluminationMethod=False,bOverride_LumenSceneLightingQuality=False,bOverride_LumenSceneDetail=False,bOverride_LumenSceneViewDistance=False,bOverride_LumenSceneLightingUpdateSpeed=False,bOverride_LumenFinalGatherQuality=False,bOverride_LumenFinalGatherLightingUpdateSpeed=False,bOverride_LumenFinalGatherScreenTraces=False,bOverride_LumenMaxTraceDistance=False,bOverride_LumenDiffuseColorBoost=False,bOverride_LumenSkylightLeaking=False,bOverride_LumenSkylightLeakingTint=False,bOverride_LumenFullSkylightLeakingDistance=False,bOverride_LumenRayLightingMode=False,bOverride_LumenReflectionsScreenTraces=False,bOverride_LumenFrontLayerTranslucencyReflections=False,bOverride_LumenMaxRoughnessToTraceReflections=False,bOverride_LumenMaxReflectionBounces=False,bOverride_LumenMaxRefractionBounces=False,bOverride_LumenSurfaceCacheResolution=False,bOverride_RayTracingGI=False,bOverride_RayTracingGIMaxBounces=False,bOverride_RayTracingGISamplesPerPixel=False,bOverride_PathTracingMaxBounces=False,bOverride_PathTracingSamplesPerPixel=False,bOverride_PathTracingMaxPathIntensity=False,bOverride_PathTracingEnableEmissiveMaterials=False,bOverride_PathTracingEnableReferenceDOF=False,bOverride_PathTracingEnableReferenceAtmosphere=False,bOverride_PathTracingEnableDenoiser=False,bOverride_PathTracingIncludeEmissive=False,bOverride_PathTracingIncludeDiffuse=False,bOverride_PathTracingIncludeIndirectDiffuse=False,bOverride_PathTracingIncludeSpecular=False,bOverride_PathTracingIncludeIndirectSpecular=False,bOverride_PathTracingIncludeVolume=False,bOverride_PathTracingIncludeIndirectVolume=False,bMobileHQGaussian=False,BloomMethod=BM_SOG,AutoExposureMethod=AEM_Histogram,TemperatureType=TEMP_WhiteBalance,WhiteTemp=6500.000000,WhiteTint=0.000000,ColorSaturation=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorContrast=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorGamma=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorGain=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorOffset=(X=0.000000,Y=0.000000,Z=0.000000,W=0.000000),ColorSaturationShadows=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorContrastShadows=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorGammaShadows=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorGainShadows=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorOffsetShadows=(X=0.000000,Y=0.000000,Z=0.000000,W=0.000000),ColorSaturationMidtones=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorContrastMidtones=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorGammaMidtones=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorGainMidtones=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorOffsetMidtones=(X=0.000000,Y=0.000000,Z=0.000000,W=0.000000),ColorSaturationHighlights=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorContrastHighlights=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorGammaHighlights=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorGainHighlights=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorOffsetHighlights=(X=0.000000,Y=0.000000,Z=0.000000,W=0.000000),ColorCorrectionHighlightsMin=0.500000,ColorCorrectionHighlightsMax=1.000000,ColorCorrectionShadowsMax=0.090000,BlueCorrection=0.600000,ExpandGamut=1.000000,ToneCurveAmount=1.000000,FilmSlope=0.880000,FilmToe=0.550000,FilmShoulder=0.260000,FilmBlackClip=0.000000,FilmWhiteClip=0.040000,SceneColorTint=(R=1.000000,G=1.000000,B=1.000000,A=1.000000),SceneFringeIntensity=0.000000,ChromaticAberrationStartOffset=0.000000,BloomIntensity=0.675000,BloomGaussianIntensity=1.000000,BloomThreshold=-1.000000,BloomSizeScale=4.000000,Bloom1Size=0.300000,Bloom2Size=1.000000,Bloom3Size=2.000000,Bloom4Size=10.000000,Bloom5Size=30.000000,Bloom6Size=64.000000,Bloom1Tint=(R=0.346500,G=0.346500,B=0.346500,A=1.000000),Bloom2Tint=(R=0.138000,G=0.138000,B=0.138000,A=1.000000),Bloom3Tint=(R=0.117600,G=0.117600,B=0.117600,A=1.000000),Bloom4Tint=(R=0.066000,G=0.066000,B=0.066000,A=1.000000),Bloom5Tint=(R=0.066000,G=0.066000,B=0.066000,A=1.000000),Bloom6Tint=(R=0.061000,G=0.061000,B=0.061000,A=1.000000),BloomConvolutionIntensity=1.000000,BloomConvolutionScatterDispersion=1.000000,BloomConvolutionSize=1.000000,BloomConvolutionTexture=None,BloomConvolutionCenterUV=(X=0.500000,Y=0.500000),BloomConvolutionPreFilterMin=7.000000,BloomConvolutionPreFilterMax=15000.000000,BloomConvolutionPreFilterMult=15.000000,BloomConvolutionBufferScale=0.133000,BloomDirtMask=None,BloomDirtMaskIntensity=0.000000,BloomDirtMaskTint=(R=0.500000,G=0.500000,B=0.500000,A=1.000000),DynamicGlobalIlluminationMethod=Lumen,IndirectLightingColor=(R=1.000000,G=1.000000,B=1.000000,A=1.000000),IndirectLightingIntensity=1.000000,LumenRayLightingMode=Default,LumenSceneLightingQuality=1.000000,LumenSceneDetail=1.000000,LumenSceneViewDistance=20000.000000,LumenSceneLightingUpdateSpeed=1.000000,LumenFinalGatherQuality=1.000000,LumenFinalGatherLightingUpdateSpeed=1.000000,LumenFinalGatherScreenTraces=True,LumenMaxTraceDistance=20000.000000,LumenDiffuseColorBoost=1.000000,LumenSkylightLeaking=0.000000,LumenSkylightLeakingTint=(R=1.000000,G=1.000000,B=1.000000,A=1.000000),LumenFullSkylightLeakingDistance=1000.000000,LumenSurfaceCacheResolution=1.000000,ReflectionMethod=Lumen,LumenReflectionQuality=1.000000,LumenReflectionsScreenTraces=True,LumenFrontLayerTranslucencyReflections=False,LumenMaxRoughnessToTraceReflections=0.400000,LumenMaxReflectionBounces=1,LumenMaxRefractionBounces=0,ScreenSpaceReflectionIntensity=100.000000,ScreenSpaceReflectionQuality=50.000000,ScreenSpaceReflectionMaxRoughness=0.600000,bMegaLights=True,AmbientCubemapTint=(R=1.000000,G=1.000000,B=1.000000,A=1.000000),AmbientCubemapIntensity=1.000000,AmbientCubemap=None,CameraShutterSpeed=60.000000,CameraISO=100.000000,DepthOfFieldFstop=4.000000,DepthOfFieldMinFstop=1.200000,DepthOfFieldBladeCount=5,AutoExposureBias=1.000000,AutoExposureBiasBackup=0.000000,bOverride_AutoExposureBiasBackup=False,AutoExposureApplyPhysicalCameraExposure=True,AutoExposureBiasCurve=None,AutoExposureMeterMask=None,AutoExposureLowPercent=10.000000,AutoExposureHighPercent=90.000000,AutoExposureMinBrightness=-10.000000,AutoExposureMaxBrightness=20.000000,AutoExposureSpeedUp=3.000000,AutoExposureSpeedDown=1.000000,HistogramLogMin=-10.000000,HistogramLogMax=20.000000,LocalExposureMethod=Bilateral,LocalExposureHighlightContrastScale=1.000000,LocalExposureShadowContrastScale=1.000000,LocalExposureHighlightContrastCurve=None,LocalExposureShadowContrastCurve=None,LocalExposureHighlightThreshold=0.000000,LocalExposureShadowThreshold=0.000000,LocalExposureDetailStrength=1.000000,LocalExposureBlurredLuminanceBlend=0.600000,LocalExposureBlurredLuminanceKernelSizePercent=50.000000,LocalExposureHighlightThresholdStrength=1.000000,LocalExposureShadowThresholdStrength=1.000000,LocalExposureMiddleGreyBias=0.000000,LensFlareIntensity=1.000000,LensFlareTint=(R=1.000000,G=1.000000,B=1.000000,A=1.000000),LensFlareBokehSize=3.000000,LensFlareThreshold=8.000000,LensFlareBokehShape=None,LensFlareTints[0]=(R=1.000000,G=0.800000,B=0.400000,A=0.600000),LensFlareTints[1]=(R=1.000000,G=1.000000,B=0.600000,A=0.530000),LensFlareTints[2]=(R=0.800000,G=0.800000,B=1.000000,A=0.460000),LensFlareTints[3]=(R=0.500000,G=1.000000,B=0.400000,A=0.390000),LensFlareTints[4]=(R=0.500000,G=0.800000,B=1.000000,A=0.310000),LensFlareTints[5]=(R=0.900000,G=1.000000,B=0.800000,A=0.270000),LensFlareTints[6]=(R=1.000000,G=0.800000,B=0.400000,A=0.220000),LensFlareTints[7]=(R=0.900000,G=0.700000,B=0.700000,A=0.150000),VignetteIntensity=0.400000,Sharpen=0.000000,FilmGrainIntensity=0.000000,FilmGrainIntensityShadows=1.000000,FilmGrainIntensityMidtones=1.000000,FilmGrainIntensityHighlights=1.000000,FilmGrainShadowsMax=0.090000,FilmGrainHighlightsMin=0.500000,FilmGrainHighlightsMax=1.000000,FilmGrainTexelSize=1.000000,FilmGrainTexture=None,AmbientOcclusionIntensity=0.500000,AmbientOcclusionStaticFraction=1.000000,AmbientOcclusionRadius=200.000000,AmbientOcclusionRadiusInWS=False,AmbientOcclusionFadeDistance=8000.000000,AmbientOcclusionFadeRadius=5000.000000,AmbientOcclusionPower=2.000000,AmbientOcclusionBias=3.000000,AmbientOcclusionQuality=50.000000,AmbientOcclusionMipBlend=0.600000,AmbientOcclusionMipScale=1.700000,AmbientOcclusionMipThreshold=0.010000,AmbientOcclusionTemporalBlendWeight=0.100000,RayTracingAO=False,RayTracingAOSamplesPerPixel=1,RayTracingAOIntensity=1.000000,RayTracingAORadius=200.000000,ColorGradingIntensity=1.000000,ColorGradingLUT=None,DepthOfFieldSensorWidth=24.576000,DepthOfFieldSqueezeFactor=1.000000,DepthOfFieldFocalDistance=0.000000,DepthOfFieldDepthBlurAmount=1.000000,DepthOfFieldDepthBlurRadius=0.000000,DepthOfFieldUseHairDepth=False,DepthOfFieldPetzvalBokeh=0.000000,DepthOfFieldPetzvalBokehFalloff=1.000000,DepthOfFieldPetzvalExclusionBoxExtents=(X=0.000000,Y=0.000000),DepthOfFieldPetzvalExclusionBoxRadius=0.000000,DepthOfFieldAspectRatioScalar=1.000000,DepthOfFieldBarrelRadius=5.000000,DepthOfFieldBarrelLength=0.000000,DepthOfFieldMatteBoxFlags[0]=(Pitch=0.000000,Roll=0.000000,Length=0.000000),DepthOfFieldMatteBoxFlags[1]=(Pitch=0.000000,Roll=0.000000,Length=0.000000),DepthOfFieldMatteBoxFlags[2]=(Pitch=0.000000,Roll=0.000000,Length=0.000000),DepthOfFieldFocalRegion=0.000000,DepthOfFieldNearTransitionRegion=300.000000,DepthOfFieldFarTransitionRegion=500.000000,DepthOfFieldScale=0.000000,DepthOfFieldNearBlurSize=15.000000,DepthOfFieldFarBlurSize=15.000000,DepthOfFieldOcclusion=0.400000,DepthOfFieldSkyFocusDistance=0.000000,DepthOfFieldVignetteSize=200.000000,MotionBlurAmount=0.500000,MotionBlurMax=5.000000,MotionBlurTargetFPS=30,MotionBlurPerObjectSize=0.000000,TranslucencyType=Raster,RayTracingTranslucencyMaxRoughness=0.600000,RayTracingTranslucencyRefractionRays=3,RayTracingTranslucencySamplesPerPixel=1,RayTracingTranslucencyMaxPrimaryHitEvents=4,RayTracingTranslucencyMaxSecondaryHitEvents=2,RayTracingTranslucencyShadows=Hard_shadows,RayTracingTranslucencyRefraction=True,RayTracingTranslucencyUseRayTracedRefraction=False,PathTracingMaxBounces=32,PathTracingSamplesPerPixel=2048,PathTracingMaxPathIntensity=24.000000,PathTracingEnableEmissiveMaterials=True,PathTracingEnableReferenceDOF=False,PathTracingEnableReferenceAtmosphere=False,PathTracingEnableDenoiser=True,PathTracingIncludeEmissive=True,PathTracingIncludeDiffuse=True,PathTracingIncludeIndirectDiffuse=True,PathTracingIncludeSpecular=True,PathTracingIncludeIndirectSpecular=True,PathTracingIncludeVolume=True,PathTracingIncludeIndirectVolume=True,UserFlags=0,WeightedBlendables=(Array=)),LightingRigRotation=0.000000,RotationSpeed=2.000000,DirectionalLightRotation=(Pitch=-40.000000,Yaw=-67.500000,Roll=0.000000),bEnableToneMapping=False,bShowMeshEdges=True) +Profiles=(ProfileName="Grey Ambient",bSharedProfile=True,bIsEngineDefaultProfile=True,bUseSkyLighting=True,DirectionalLightIntensity=4.000000,DirectionalLightColor=(R=1.000000,G=1.000000,B=1.000000,A=1.000000),SkyLightIntensity=2.000000,bRotateLightingRig=False,bShowEnvironment=True,bShowFloor=True,bShowGrid=True,EnvironmentColor=(R=0.200000,G=0.200000,B=0.200000,A=1.000000),EnvironmentIntensity=1.000000,EnvironmentCubeMapPath="/Engine/EditorMaterials/AssetViewer/T_GreyAmbient",bPostProcessingEnabled=False,PostProcessingSettings=(bOverride_TemperatureType=False,bOverride_WhiteTemp=False,bOverride_WhiteTint=False,bOverride_ColorSaturation=False,bOverride_ColorContrast=False,bOverride_ColorGamma=False,bOverride_ColorGain=False,bOverride_ColorOffset=False,bOverride_ColorSaturationShadows=False,bOverride_ColorContrastShadows=False,bOverride_ColorGammaShadows=False,bOverride_ColorGainShadows=False,bOverride_ColorOffsetShadows=False,bOverride_ColorSaturationMidtones=False,bOverride_ColorContrastMidtones=False,bOverride_ColorGammaMidtones=False,bOverride_ColorGainMidtones=False,bOverride_ColorOffsetMidtones=False,bOverride_ColorSaturationHighlights=False,bOverride_ColorContrastHighlights=False,bOverride_ColorGammaHighlights=False,bOverride_ColorGainHighlights=False,bOverride_ColorOffsetHighlights=False,bOverride_ColorCorrectionShadowsMax=False,bOverride_ColorCorrectionHighlightsMin=False,bOverride_ColorCorrectionHighlightsMax=False,bOverride_BlueCorrection=False,bOverride_ExpandGamut=False,bOverride_ToneCurveAmount=False,bOverride_FilmSlope=False,bOverride_FilmToe=False,bOverride_FilmShoulder=False,bOverride_FilmBlackClip=False,bOverride_FilmWhiteClip=False,bOverride_SceneColorTint=False,bOverride_SceneFringeIntensity=False,bOverride_ChromaticAberrationStartOffset=False,bOverride_bMegaLights=False,bOverride_AmbientCubemapTint=False,bOverride_AmbientCubemapIntensity=False,bOverride_BloomMethod=False,bOverride_BloomIntensity=False,bOverride_BloomGaussianIntensity=False,bOverride_BloomThreshold=False,bOverride_Bloom1Tint=False,bOverride_Bloom1Size=False,bOverride_Bloom2Size=False,bOverride_Bloom2Tint=False,bOverride_Bloom3Tint=False,bOverride_Bloom3Size=False,bOverride_Bloom4Tint=False,bOverride_Bloom4Size=False,bOverride_Bloom5Tint=False,bOverride_Bloom5Size=False,bOverride_Bloom6Tint=False,bOverride_Bloom6Size=False,bOverride_BloomSizeScale=False,bOverride_BloomConvolutionIntensity=False,bOverride_BloomConvolutionTexture=False,bOverride_BloomConvolutionScatterDispersion=False,bOverride_BloomConvolutionSize=False,bOverride_BloomConvolutionCenterUV=False,bOverride_BloomConvolutionPreFilterMin=False,bOverride_BloomConvolutionPreFilterMax=False,bOverride_BloomConvolutionPreFilterMult=False,bOverride_BloomConvolutionBufferScale=False,bOverride_BloomDirtMaskIntensity=False,bOverride_BloomDirtMaskTint=False,bOverride_BloomDirtMask=False,bOverride_CameraShutterSpeed=False,bOverride_CameraISO=False,bOverride_AutoExposureMethod=False,bOverride_AutoExposureLowPercent=False,bOverride_AutoExposureHighPercent=False,bOverride_AutoExposureMinBrightness=False,bOverride_AutoExposureMaxBrightness=False,bOverride_AutoExposureSpeedUp=False,bOverride_AutoExposureSpeedDown=False,bOverride_AutoExposureBias=False,bOverride_AutoExposureBiasCurve=False,bOverride_AutoExposureMeterMask=False,bOverride_AutoExposureApplyPhysicalCameraExposure=False,bOverride_HistogramLogMin=False,bOverride_HistogramLogMax=False,bOverride_LocalExposureMethod=False,bOverride_LocalExposureHighlightContrastScale=False,bOverride_LocalExposureShadowContrastScale=False,bOverride_LocalExposureHighlightContrastCurve=False,bOverride_LocalExposureShadowContrastCurve=False,bOverride_LocalExposureHighlightThreshold=False,bOverride_LocalExposureShadowThreshold=False,bOverride_LocalExposureDetailStrength=False,bOverride_LocalExposureBlurredLuminanceBlend=False,bOverride_LocalExposureBlurredLuminanceKernelSizePercent=False,bOverride_LocalExposureHighlightThresholdStrength=False,bOverride_LocalExposureShadowThresholdStrength=False,bOverride_LocalExposureMiddleGreyBias=False,bOverride_LensFlareIntensity=False,bOverride_LensFlareTint=False,bOverride_LensFlareTints=False,bOverride_LensFlareBokehSize=False,bOverride_LensFlareBokehShape=False,bOverride_LensFlareThreshold=False,bOverride_VignetteIntensity=False,bOverride_Sharpen=False,bOverride_FilmGrainIntensity=False,bOverride_FilmGrainIntensityShadows=False,bOverride_FilmGrainIntensityMidtones=False,bOverride_FilmGrainIntensityHighlights=False,bOverride_FilmGrainShadowsMax=False,bOverride_FilmGrainHighlightsMin=False,bOverride_FilmGrainHighlightsMax=False,bOverride_FilmGrainTexelSize=False,bOverride_FilmGrainTexture=False,bOverride_AmbientOcclusionIntensity=False,bOverride_AmbientOcclusionStaticFraction=False,bOverride_AmbientOcclusionRadius=False,bOverride_AmbientOcclusionFadeDistance=False,bOverride_AmbientOcclusionFadeRadius=False,bOverride_AmbientOcclusionRadiusInWS=False,bOverride_AmbientOcclusionPower=False,bOverride_AmbientOcclusionBias=False,bOverride_AmbientOcclusionQuality=False,bOverride_AmbientOcclusionMipBlend=False,bOverride_AmbientOcclusionMipScale=False,bOverride_AmbientOcclusionMipThreshold=False,bOverride_AmbientOcclusionTemporalBlendWeight=False,bOverride_RayTracingAO=False,bOverride_RayTracingAOSamplesPerPixel=False,bOverride_RayTracingAOIntensity=False,bOverride_RayTracingAORadius=False,bOverride_IndirectLightingColor=False,bOverride_IndirectLightingIntensity=False,bOverride_ColorGradingIntensity=False,bOverride_ColorGradingLUT=False,bOverride_DepthOfFieldFocalDistance=False,bOverride_DepthOfFieldFstop=False,bOverride_DepthOfFieldMinFstop=False,bOverride_DepthOfFieldBladeCount=False,bOverride_DepthOfFieldSensorWidth=False,bOverride_DepthOfFieldSqueezeFactor=False,bOverride_DepthOfFieldDepthBlurRadius=False,bOverride_DepthOfFieldUseHairDepth=False,bOverride_DepthOfFieldPetzvalBokeh=False,bOverride_DepthOfFieldPetzvalBokehFalloff=False,bOverride_DepthOfFieldPetzvalExclusionBoxExtents=False,bOverride_DepthOfFieldPetzvalExclusionBoxRadius=False,bOverride_DepthOfFieldAspectRatioScalar=False,bOverride_DepthOfFieldMatteBoxFlags=False,bOverride_DepthOfFieldBarrelRadius=False,bOverride_DepthOfFieldBarrelLength=False,bOverride_DepthOfFieldDepthBlurAmount=False,bOverride_DepthOfFieldFocalRegion=False,bOverride_DepthOfFieldNearTransitionRegion=False,bOverride_DepthOfFieldFarTransitionRegion=False,bOverride_DepthOfFieldScale=False,bOverride_DepthOfFieldNearBlurSize=False,bOverride_DepthOfFieldFarBlurSize=False,bOverride_MobileHQGaussian=False,bOverride_DepthOfFieldOcclusion=False,bOverride_DepthOfFieldSkyFocusDistance=False,bOverride_DepthOfFieldVignetteSize=False,bOverride_MotionBlurAmount=False,bOverride_MotionBlurMax=False,bOverride_MotionBlurTargetFPS=False,bOverride_MotionBlurPerObjectSize=False,bOverride_ReflectionMethod=False,bOverride_LumenReflectionQuality=False,bOverride_ScreenSpaceReflectionIntensity=False,bOverride_ScreenSpaceReflectionQuality=False,bOverride_ScreenSpaceReflectionMaxRoughness=False,bOverride_ScreenSpaceReflectionRoughnessScale=False,bOverride_UserFlags=False,bOverride_RayTracingReflectionsMaxRoughness=False,bOverride_RayTracingReflectionsMaxBounces=False,bOverride_RayTracingReflectionsSamplesPerPixel=False,bOverride_RayTracingReflectionsShadows=False,bOverride_RayTracingReflectionsTranslucency=False,bOverride_TranslucencyType=False,bOverride_RayTracingTranslucencyMaxRoughness=False,bOverride_RayTracingTranslucencyRefractionRays=False,bOverride_RayTracingTranslucencySamplesPerPixel=False,bOverride_RayTracingTranslucencyShadows=False,bOverride_RayTracingTranslucencyRefraction=False,bOverride_RayTracingTranslucencyMaxPrimaryHitEvents=False,bOverride_RayTracingTranslucencyMaxSecondaryHitEvents=False,bOverride_RayTracingTranslucencyUseRayTracedRefraction=False,bOverride_DynamicGlobalIlluminationMethod=False,bOverride_LumenSceneLightingQuality=False,bOverride_LumenSceneDetail=False,bOverride_LumenSceneViewDistance=False,bOverride_LumenSceneLightingUpdateSpeed=False,bOverride_LumenFinalGatherQuality=False,bOverride_LumenFinalGatherLightingUpdateSpeed=False,bOverride_LumenFinalGatherScreenTraces=False,bOverride_LumenMaxTraceDistance=False,bOverride_LumenDiffuseColorBoost=False,bOverride_LumenSkylightLeaking=False,bOverride_LumenSkylightLeakingTint=False,bOverride_LumenFullSkylightLeakingDistance=False,bOverride_LumenRayLightingMode=False,bOverride_LumenReflectionsScreenTraces=False,bOverride_LumenFrontLayerTranslucencyReflections=False,bOverride_LumenMaxRoughnessToTraceReflections=False,bOverride_LumenMaxReflectionBounces=False,bOverride_LumenMaxRefractionBounces=False,bOverride_LumenSurfaceCacheResolution=False,bOverride_RayTracingGI=False,bOverride_RayTracingGIMaxBounces=False,bOverride_RayTracingGISamplesPerPixel=False,bOverride_PathTracingMaxBounces=False,bOverride_PathTracingSamplesPerPixel=False,bOverride_PathTracingMaxPathIntensity=False,bOverride_PathTracingEnableEmissiveMaterials=False,bOverride_PathTracingEnableReferenceDOF=False,bOverride_PathTracingEnableReferenceAtmosphere=False,bOverride_PathTracingEnableDenoiser=False,bOverride_PathTracingIncludeEmissive=False,bOverride_PathTracingIncludeDiffuse=False,bOverride_PathTracingIncludeIndirectDiffuse=False,bOverride_PathTracingIncludeSpecular=False,bOverride_PathTracingIncludeIndirectSpecular=False,bOverride_PathTracingIncludeVolume=False,bOverride_PathTracingIncludeIndirectVolume=False,bMobileHQGaussian=False,BloomMethod=BM_SOG,AutoExposureMethod=AEM_Histogram,TemperatureType=TEMP_WhiteBalance,WhiteTemp=6500.000000,WhiteTint=0.000000,ColorSaturation=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorContrast=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorGamma=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorGain=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorOffset=(X=0.000000,Y=0.000000,Z=0.000000,W=0.000000),ColorSaturationShadows=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorContrastShadows=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorGammaShadows=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorGainShadows=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorOffsetShadows=(X=0.000000,Y=0.000000,Z=0.000000,W=0.000000),ColorSaturationMidtones=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorContrastMidtones=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorGammaMidtones=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorGainMidtones=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorOffsetMidtones=(X=0.000000,Y=0.000000,Z=0.000000,W=0.000000),ColorSaturationHighlights=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorContrastHighlights=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorGammaHighlights=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorGainHighlights=(X=1.000000,Y=1.000000,Z=1.000000,W=1.000000),ColorOffsetHighlights=(X=0.000000,Y=0.000000,Z=0.000000,W=0.000000),ColorCorrectionHighlightsMin=0.500000,ColorCorrectionHighlightsMax=1.000000,ColorCorrectionShadowsMax=0.090000,BlueCorrection=0.600000,ExpandGamut=1.000000,ToneCurveAmount=1.000000,FilmSlope=0.880000,FilmToe=0.550000,FilmShoulder=0.260000,FilmBlackClip=0.000000,FilmWhiteClip=0.040000,SceneColorTint=(R=1.000000,G=1.000000,B=1.000000,A=1.000000),SceneFringeIntensity=0.000000,ChromaticAberrationStartOffset=0.000000,BloomIntensity=0.675000,BloomGaussianIntensity=1.000000,BloomThreshold=-1.000000,BloomSizeScale=4.000000,Bloom1Size=0.300000,Bloom2Size=1.000000,Bloom3Size=2.000000,Bloom4Size=10.000000,Bloom5Size=30.000000,Bloom6Size=64.000000,Bloom1Tint=(R=0.346500,G=0.346500,B=0.346500,A=1.000000),Bloom2Tint=(R=0.138000,G=0.138000,B=0.138000,A=1.000000),Bloom3Tint=(R=0.117600,G=0.117600,B=0.117600,A=1.000000),Bloom4Tint=(R=0.066000,G=0.066000,B=0.066000,A=1.000000),Bloom5Tint=(R=0.066000,G=0.066000,B=0.066000,A=1.000000),Bloom6Tint=(R=0.061000,G=0.061000,B=0.061000,A=1.000000),BloomConvolutionIntensity=1.000000,BloomConvolutionScatterDispersion=1.000000,BloomConvolutionSize=1.000000,BloomConvolutionTexture=None,BloomConvolutionCenterUV=(X=0.500000,Y=0.500000),BloomConvolutionPreFilterMin=7.000000,BloomConvolutionPreFilterMax=15000.000000,BloomConvolutionPreFilterMult=15.000000,BloomConvolutionBufferScale=0.133000,BloomDirtMask=None,BloomDirtMaskIntensity=0.000000,BloomDirtMaskTint=(R=0.500000,G=0.500000,B=0.500000,A=1.000000),DynamicGlobalIlluminationMethod=Lumen,IndirectLightingColor=(R=1.000000,G=1.000000,B=1.000000,A=1.000000),IndirectLightingIntensity=1.000000,LumenRayLightingMode=Default,LumenSceneLightingQuality=1.000000,LumenSceneDetail=1.000000,LumenSceneViewDistance=20000.000000,LumenSceneLightingUpdateSpeed=1.000000,LumenFinalGatherQuality=1.000000,LumenFinalGatherLightingUpdateSpeed=1.000000,LumenFinalGatherScreenTraces=True,LumenMaxTraceDistance=20000.000000,LumenDiffuseColorBoost=1.000000,LumenSkylightLeaking=0.000000,LumenSkylightLeakingTint=(R=1.000000,G=1.000000,B=1.000000,A=1.000000),LumenFullSkylightLeakingDistance=1000.000000,LumenSurfaceCacheResolution=1.000000,ReflectionMethod=Lumen,LumenReflectionQuality=1.000000,LumenReflectionsScreenTraces=True,LumenFrontLayerTranslucencyReflections=False,LumenMaxRoughnessToTraceReflections=0.400000,LumenMaxReflectionBounces=1,LumenMaxRefractionBounces=0,ScreenSpaceReflectionIntensity=100.000000,ScreenSpaceReflectionQuality=50.000000,ScreenSpaceReflectionMaxRoughness=0.600000,bMegaLights=True,AmbientCubemapTint=(R=1.000000,G=1.000000,B=1.000000,A=1.000000),AmbientCubemapIntensity=1.000000,AmbientCubemap=None,CameraShutterSpeed=60.000000,CameraISO=100.000000,DepthOfFieldFstop=4.000000,DepthOfFieldMinFstop=1.200000,DepthOfFieldBladeCount=5,AutoExposureBias=1.000000,AutoExposureBiasBackup=0.000000,bOverride_AutoExposureBiasBackup=False,AutoExposureApplyPhysicalCameraExposure=True,AutoExposureBiasCurve=None,AutoExposureMeterMask=None,AutoExposureLowPercent=10.000000,AutoExposureHighPercent=90.000000,AutoExposureMinBrightness=-10.000000,AutoExposureMaxBrightness=20.000000,AutoExposureSpeedUp=3.000000,AutoExposureSpeedDown=1.000000,HistogramLogMin=-10.000000,HistogramLogMax=20.000000,LocalExposureMethod=Bilateral,LocalExposureHighlightContrastScale=1.000000,LocalExposureShadowContrastScale=1.000000,LocalExposureHighlightContrastCurve=None,LocalExposureShadowContrastCurve=None,LocalExposureHighlightThreshold=0.000000,LocalExposureShadowThreshold=0.000000,LocalExposureDetailStrength=1.000000,LocalExposureBlurredLuminanceBlend=0.600000,LocalExposureBlurredLuminanceKernelSizePercent=50.000000,LocalExposureHighlightThresholdStrength=1.000000,LocalExposureShadowThresholdStrength=1.000000,LocalExposureMiddleGreyBias=0.000000,LensFlareIntensity=1.000000,LensFlareTint=(R=1.000000,G=1.000000,B=1.000000,A=1.000000),LensFlareBokehSize=3.000000,LensFlareThreshold=8.000000,LensFlareBokehShape=None,LensFlareTints[0]=(R=1.000000,G=0.800000,B=0.400000,A=0.600000),LensFlareTints[1]=(R=1.000000,G=1.000000,B=0.600000,A=0.530000),LensFlareTints[2]=(R=0.800000,G=0.800000,B=1.000000,A=0.460000),LensFlareTints[3]=(R=0.500000,G=1.000000,B=0.400000,A=0.390000),LensFlareTints[4]=(R=0.500000,G=0.800000,B=1.000000,A=0.310000),LensFlareTints[5]=(R=0.900000,G=1.000000,B=0.800000,A=0.270000),LensFlareTints[6]=(R=1.000000,G=0.800000,B=0.400000,A=0.220000),LensFlareTints[7]=(R=0.900000,G=0.700000,B=0.700000,A=0.150000),VignetteIntensity=0.400000,Sharpen=0.000000,FilmGrainIntensity=0.000000,FilmGrainIntensityShadows=1.000000,FilmGrainIntensityMidtones=1.000000,FilmGrainIntensityHighlights=1.000000,FilmGrainShadowsMax=0.090000,FilmGrainHighlightsMin=0.500000,FilmGrainHighlightsMax=1.000000,FilmGrainTexelSize=1.000000,FilmGrainTexture=None,AmbientOcclusionIntensity=0.500000,AmbientOcclusionStaticFraction=1.000000,AmbientOcclusionRadius=200.000000,AmbientOcclusionRadiusInWS=False,AmbientOcclusionFadeDistance=8000.000000,AmbientOcclusionFadeRadius=5000.000000,AmbientOcclusionPower=2.000000,AmbientOcclusionBias=3.000000,AmbientOcclusionQuality=50.000000,AmbientOcclusionMipBlend=0.600000,AmbientOcclusionMipScale=1.700000,AmbientOcclusionMipThreshold=0.010000,AmbientOcclusionTemporalBlendWeight=0.100000,RayTracingAO=False,RayTracingAOSamplesPerPixel=1,RayTracingAOIntensity=1.000000,RayTracingAORadius=200.000000,ColorGradingIntensity=1.000000,ColorGradingLUT=None,DepthOfFieldSensorWidth=24.576000,DepthOfFieldSqueezeFactor=1.000000,DepthOfFieldFocalDistance=0.000000,DepthOfFieldDepthBlurAmount=1.000000,DepthOfFieldDepthBlurRadius=0.000000,DepthOfFieldUseHairDepth=False,DepthOfFieldPetzvalBokeh=0.000000,DepthOfFieldPetzvalBokehFalloff=1.000000,DepthOfFieldPetzvalExclusionBoxExtents=(X=0.000000,Y=0.000000),DepthOfFieldPetzvalExclusionBoxRadius=0.000000,DepthOfFieldAspectRatioScalar=1.000000,DepthOfFieldBarrelRadius=5.000000,DepthOfFieldBarrelLength=0.000000,DepthOfFieldMatteBoxFlags[0]=(Pitch=0.000000,Roll=0.000000,Length=0.000000),DepthOfFieldMatteBoxFlags[1]=(Pitch=0.000000,Roll=0.000000,Length=0.000000),DepthOfFieldMatteBoxFlags[2]=(Pitch=0.000000,Roll=0.000000,Length=0.000000),DepthOfFieldFocalRegion=0.000000,DepthOfFieldNearTransitionRegion=300.000000,DepthOfFieldFarTransitionRegion=500.000000,DepthOfFieldScale=0.000000,DepthOfFieldNearBlurSize=15.000000,DepthOfFieldFarBlurSize=15.000000,DepthOfFieldOcclusion=0.400000,DepthOfFieldSkyFocusDistance=0.000000,DepthOfFieldVignetteSize=200.000000,MotionBlurAmount=0.500000,MotionBlurMax=5.000000,MotionBlurTargetFPS=30,MotionBlurPerObjectSize=0.000000,TranslucencyType=Raster,RayTracingTranslucencyMaxRoughness=0.600000,RayTracingTranslucencyRefractionRays=3,RayTracingTranslucencySamplesPerPixel=1,RayTracingTranslucencyMaxPrimaryHitEvents=4,RayTracingTranslucencyMaxSecondaryHitEvents=2,RayTracingTranslucencyShadows=Hard_shadows,RayTracingTranslucencyRefraction=True,RayTracingTranslucencyUseRayTracedRefraction=False,PathTracingMaxBounces=32,PathTracingSamplesPerPixel=2048,PathTracingMaxPathIntensity=24.000000,PathTracingEnableEmissiveMaterials=True,PathTracingEnableReferenceDOF=False,PathTracingEnableReferenceAtmosphere=False,PathTracingEnableDenoiser=True,PathTracingIncludeEmissive=True,PathTracingIncludeDiffuse=True,PathTracingIncludeIndirectDiffuse=True,PathTracingIncludeSpecular=True,PathTracingIncludeIndirectSpecular=True,PathTracingIncludeVolume=True,PathTracingIncludeIndirectVolume=True,UserFlags=0,WeightedBlendables=(Array=)),LightingRigRotation=0.000000,RotationSpeed=2.000000,DirectionalLightRotation=(Pitch=-40.000000,Yaw=-67.500000,Roll=0.000000),bEnableToneMapping=False,bShowMeshEdges=False) +[/Script/ZeusIntelligenceCore.ZeusSettings] +ActiveProvider=claude_cli +LocalBaseUrl="http://localhost:11434/v1" +LocalModel=qwen2.5:7b +AnthropicApiKey= +AnthropicModel=claude-opus-4-8 +ClaudeCliPath= +OpenAIApiKey= +OpenAIBaseUrl="https://api.openai.com/v1" +OpenAIModel=gpt-4o +CustomBaseUrl= +CustomApiKey= +CustomModel= +Temperature=0.700000 +MaxTokens=2048 +SystemPromptAppend= +McpPort=5058 + diff --git a/Config/DefaultEngine.ini b/Config/DefaultEngine.ini index 265a76e..a8a5ba5 100644 --- a/Config/DefaultEngine.ini +++ b/Config/DefaultEngine.ini @@ -86,6 +86,15 @@ GameViewportClientClassName=/Script/CommonUI.CommonGameViewportClient +ActiveClassRedirects=(OldClassName="TP_ThirdPersonCharacter",NewClassName="ZMMOPlayerCharacter") +ActiveClassRedirects=(OldClassName="ZMMOCharacter",NewClassName="ZMMOPlayerCharacter") +[CoreRedirects] +; Migração v1→v2 do FUIStylePanel: TMap (Enum) → TMap. +; Os campos *_DEPRECATED preservam a leitura do asset antigo; PostSerialize copia +; pros novos ByName e após re-save os legacy ficam vazios. Os redirects podem +; ser removidos quando todos os DTs do projeto tiverem sido re-salvos. ++PropertyRedirects=(OldName="/Script/ZMMO.UIStylePanel.PanelBackground",NewName="/Script/ZMMO.UIStylePanel.PanelBackground_DEPRECATED") ++PropertyRedirects=(OldName="/Script/ZMMO.UIStylePanel.PanelOutline",NewName="/Script/ZMMO.UIStylePanel.PanelOutline_DEPRECATED") ++PropertyRedirects=(OldName="/Script/ZMMO.UIStylePanel.PanelOutlineEffect",NewName="/Script/ZMMO.UIStylePanel.PanelOutlineEffect_DEPRECATED") + [/Script/AndroidFileServerEditor.AndroidFileServerRuntimeSettings] bEnablePlugin=True bAllowNetworkConnection=True @@ -100,3 +109,60 @@ ConnectionType=USBOnly bUseManualIPAddress=False ManualIPAddress= +[/Script/Engine.CollisionProfile] +-Profiles=(Name="NoCollision",CollisionEnabled=NoCollision,ObjectTypeName="WorldStatic",CustomResponses=((Channel="Visibility",Response=ECR_Ignore),(Channel="Camera",Response=ECR_Ignore)),HelpMessage="No collision",bCanModify=False) +-Profiles=(Name="BlockAll",CollisionEnabled=QueryAndPhysics,ObjectTypeName="WorldStatic",CustomResponses=,HelpMessage="WorldStatic object that blocks all actors by default. All new custom channels will use its own default response. ",bCanModify=False) +-Profiles=(Name="OverlapAll",CollisionEnabled=QueryOnly,ObjectTypeName="WorldStatic",CustomResponses=((Channel="WorldStatic",Response=ECR_Overlap),(Channel="Pawn",Response=ECR_Overlap),(Channel="Visibility",Response=ECR_Overlap),(Channel="WorldDynamic",Response=ECR_Overlap),(Channel="Camera",Response=ECR_Overlap),(Channel="PhysicsBody",Response=ECR_Overlap),(Channel="Vehicle",Response=ECR_Overlap),(Channel="Destructible",Response=ECR_Overlap)),HelpMessage="WorldStatic object that overlaps all actors by default. All new custom channels will use its own default response. ",bCanModify=False) +-Profiles=(Name="BlockAllDynamic",CollisionEnabled=QueryAndPhysics,ObjectTypeName="WorldDynamic",CustomResponses=,HelpMessage="WorldDynamic object that blocks all actors by default. All new custom channels will use its own default response. ",bCanModify=False) +-Profiles=(Name="OverlapAllDynamic",CollisionEnabled=QueryOnly,ObjectTypeName="WorldDynamic",CustomResponses=((Channel="WorldStatic",Response=ECR_Overlap),(Channel="Pawn",Response=ECR_Overlap),(Channel="Visibility",Response=ECR_Overlap),(Channel="WorldDynamic",Response=ECR_Overlap),(Channel="Camera",Response=ECR_Overlap),(Channel="PhysicsBody",Response=ECR_Overlap),(Channel="Vehicle",Response=ECR_Overlap),(Channel="Destructible",Response=ECR_Overlap)),HelpMessage="WorldDynamic object that overlaps all actors by default. All new custom channels will use its own default response. ",bCanModify=False) +-Profiles=(Name="IgnoreOnlyPawn",CollisionEnabled=QueryOnly,ObjectTypeName="WorldDynamic",CustomResponses=((Channel="Pawn",Response=ECR_Ignore),(Channel="Vehicle",Response=ECR_Ignore)),HelpMessage="WorldDynamic object that ignores Pawn and Vehicle. All other channels will be set to default.",bCanModify=False) +-Profiles=(Name="OverlapOnlyPawn",CollisionEnabled=QueryOnly,ObjectTypeName="WorldDynamic",CustomResponses=((Channel="Pawn",Response=ECR_Overlap),(Channel="Vehicle",Response=ECR_Overlap),(Channel="Camera",Response=ECR_Ignore)),HelpMessage="WorldDynamic object that overlaps Pawn, Camera, and Vehicle. All other channels will be set to default. ",bCanModify=False) +-Profiles=(Name="Pawn",CollisionEnabled=QueryAndPhysics,ObjectTypeName="Pawn",CustomResponses=((Channel="Visibility",Response=ECR_Ignore)),HelpMessage="Pawn object. Can be used for capsule of any playerable character or AI. ",bCanModify=False) +-Profiles=(Name="Spectator",CollisionEnabled=QueryOnly,ObjectTypeName="Pawn",CustomResponses=((Channel="WorldStatic",Response=ECR_Block),(Channel="Pawn",Response=ECR_Ignore),(Channel="Visibility",Response=ECR_Ignore),(Channel="WorldDynamic",Response=ECR_Ignore),(Channel="Camera",Response=ECR_Ignore),(Channel="PhysicsBody",Response=ECR_Ignore),(Channel="Vehicle",Response=ECR_Ignore),(Channel="Destructible",Response=ECR_Ignore)),HelpMessage="Pawn object that ignores all other actors except WorldStatic.",bCanModify=False) +-Profiles=(Name="CharacterMesh",CollisionEnabled=QueryOnly,ObjectTypeName="Pawn",CustomResponses=((Channel="Pawn",Response=ECR_Ignore),(Channel="Vehicle",Response=ECR_Ignore),(Channel="Visibility",Response=ECR_Ignore)),HelpMessage="Pawn object that is used for Character Mesh. All other channels will be set to default.",bCanModify=False) +-Profiles=(Name="PhysicsActor",CollisionEnabled=QueryAndPhysics,ObjectTypeName="PhysicsBody",CustomResponses=,HelpMessage="Simulating actors",bCanModify=False) +-Profiles=(Name="Destructible",CollisionEnabled=QueryAndPhysics,ObjectTypeName="Destructible",CustomResponses=,HelpMessage="Destructible actors",bCanModify=False) +-Profiles=(Name="InvisibleWall",CollisionEnabled=QueryAndPhysics,ObjectTypeName="WorldStatic",CustomResponses=((Channel="Visibility",Response=ECR_Ignore)),HelpMessage="WorldStatic object that is invisible.",bCanModify=False) +-Profiles=(Name="InvisibleWallDynamic",CollisionEnabled=QueryAndPhysics,ObjectTypeName="WorldDynamic",CustomResponses=((Channel="Visibility",Response=ECR_Ignore)),HelpMessage="WorldDynamic object that is invisible.",bCanModify=False) +-Profiles=(Name="Trigger",CollisionEnabled=QueryOnly,ObjectTypeName="WorldDynamic",CustomResponses=((Channel="WorldStatic",Response=ECR_Overlap),(Channel="Pawn",Response=ECR_Overlap),(Channel="Visibility",Response=ECR_Ignore),(Channel="WorldDynamic",Response=ECR_Overlap),(Channel="Camera",Response=ECR_Overlap),(Channel="PhysicsBody",Response=ECR_Overlap),(Channel="Vehicle",Response=ECR_Overlap),(Channel="Destructible",Response=ECR_Overlap)),HelpMessage="WorldDynamic object that is used for trigger. All other channels will be set to default.",bCanModify=False) +-Profiles=(Name="Ragdoll",CollisionEnabled=QueryAndPhysics,ObjectTypeName="PhysicsBody",CustomResponses=((Channel="Pawn",Response=ECR_Ignore),(Channel="Visibility",Response=ECR_Ignore)),HelpMessage="Simulating Skeletal Mesh Component. All other channels will be set to default.",bCanModify=False) +-Profiles=(Name="Vehicle",CollisionEnabled=QueryAndPhysics,ObjectTypeName="Vehicle",CustomResponses=,HelpMessage="Vehicle object that blocks Vehicle, WorldStatic, and WorldDynamic. All other channels will be set to default.",bCanModify=False) +-Profiles=(Name="UI",CollisionEnabled=QueryOnly,ObjectTypeName="WorldDynamic",CustomResponses=((Channel="WorldStatic",Response=ECR_Overlap),(Channel="Pawn",Response=ECR_Overlap),(Channel="Visibility",Response=ECR_Block),(Channel="WorldDynamic",Response=ECR_Overlap),(Channel="Camera",Response=ECR_Overlap),(Channel="PhysicsBody",Response=ECR_Overlap),(Channel="Vehicle",Response=ECR_Overlap),(Channel="Destructible",Response=ECR_Overlap)),HelpMessage="WorldStatic object that overlaps all actors by default. All new custom channels will use its own default response. ",bCanModify=False) ++Profiles=(Name="NoCollision",CollisionEnabled=NoCollision,bCanModify=False,ObjectTypeName="WorldStatic",CustomResponses=((Channel="Visibility",Response=ECR_Ignore),(Channel="Camera",Response=ECR_Ignore)),HelpMessage="No collision") ++Profiles=(Name="BlockAll",CollisionEnabled=QueryAndPhysics,bCanModify=False,ObjectTypeName="WorldStatic",CustomResponses=,HelpMessage="WorldStatic object that blocks all actors by default. All new custom channels will use its own default response. ") ++Profiles=(Name="OverlapAll",CollisionEnabled=QueryOnly,bCanModify=False,ObjectTypeName="WorldStatic",CustomResponses=((Channel="WorldStatic",Response=ECR_Overlap),(Channel="Pawn",Response=ECR_Overlap),(Channel="Visibility",Response=ECR_Overlap),(Channel="WorldDynamic",Response=ECR_Overlap),(Channel="Camera",Response=ECR_Overlap),(Channel="PhysicsBody",Response=ECR_Overlap),(Channel="Vehicle",Response=ECR_Overlap),(Channel="Destructible",Response=ECR_Overlap)),HelpMessage="WorldStatic object that overlaps all actors by default. All new custom channels will use its own default response. ") ++Profiles=(Name="BlockAllDynamic",CollisionEnabled=QueryAndPhysics,bCanModify=False,ObjectTypeName="WorldDynamic",CustomResponses=,HelpMessage="WorldDynamic object that blocks all actors by default. All new custom channels will use its own default response. ") ++Profiles=(Name="OverlapAllDynamic",CollisionEnabled=QueryOnly,bCanModify=False,ObjectTypeName="WorldDynamic",CustomResponses=((Channel="WorldStatic",Response=ECR_Overlap),(Channel="Pawn",Response=ECR_Overlap),(Channel="Visibility",Response=ECR_Overlap),(Channel="WorldDynamic",Response=ECR_Overlap),(Channel="Camera",Response=ECR_Overlap),(Channel="PhysicsBody",Response=ECR_Overlap),(Channel="Vehicle",Response=ECR_Overlap),(Channel="Destructible",Response=ECR_Overlap)),HelpMessage="WorldDynamic object that overlaps all actors by default. All new custom channels will use its own default response. ") ++Profiles=(Name="IgnoreOnlyPawn",CollisionEnabled=QueryOnly,bCanModify=False,ObjectTypeName="WorldDynamic",CustomResponses=((Channel="Pawn",Response=ECR_Ignore),(Channel="Vehicle",Response=ECR_Ignore)),HelpMessage="WorldDynamic object that ignores Pawn and Vehicle. All other channels will be set to default.") ++Profiles=(Name="OverlapOnlyPawn",CollisionEnabled=QueryOnly,bCanModify=False,ObjectTypeName="WorldDynamic",CustomResponses=((Channel="Pawn",Response=ECR_Overlap),(Channel="Vehicle",Response=ECR_Overlap),(Channel="Camera",Response=ECR_Ignore)),HelpMessage="WorldDynamic object that overlaps Pawn, Camera, and Vehicle. All other channels will be set to default. ") ++Profiles=(Name="Pawn",CollisionEnabled=QueryAndPhysics,bCanModify=False,ObjectTypeName="Pawn",CustomResponses=((Channel="Visibility",Response=ECR_Ignore)),HelpMessage="Pawn object. Can be used for capsule of any playerable character or AI. ") ++Profiles=(Name="Spectator",CollisionEnabled=QueryOnly,bCanModify=False,ObjectTypeName="Pawn",CustomResponses=((Channel="WorldStatic"),(Channel="Pawn",Response=ECR_Ignore),(Channel="Visibility",Response=ECR_Ignore),(Channel="WorldDynamic",Response=ECR_Ignore),(Channel="Camera",Response=ECR_Ignore),(Channel="PhysicsBody",Response=ECR_Ignore),(Channel="Vehicle",Response=ECR_Ignore),(Channel="Destructible",Response=ECR_Ignore)),HelpMessage="Pawn object that ignores all other actors except WorldStatic.") ++Profiles=(Name="CharacterMesh",CollisionEnabled=QueryOnly,bCanModify=False,ObjectTypeName="Pawn",CustomResponses=((Channel="Pawn",Response=ECR_Ignore),(Channel="Vehicle",Response=ECR_Ignore),(Channel="Visibility",Response=ECR_Ignore)),HelpMessage="Pawn object that is used for Character Mesh. All other channels will be set to default.") ++Profiles=(Name="PhysicsActor",CollisionEnabled=QueryAndPhysics,bCanModify=False,ObjectTypeName="PhysicsBody",CustomResponses=,HelpMessage="Simulating actors") ++Profiles=(Name="Destructible",CollisionEnabled=QueryAndPhysics,bCanModify=False,ObjectTypeName="Destructible",CustomResponses=,HelpMessage="Destructible actors") ++Profiles=(Name="InvisibleWall",CollisionEnabled=QueryAndPhysics,bCanModify=False,ObjectTypeName="WorldStatic",CustomResponses=((Channel="Visibility",Response=ECR_Ignore)),HelpMessage="WorldStatic object that is invisible.") ++Profiles=(Name="InvisibleWallDynamic",CollisionEnabled=QueryAndPhysics,bCanModify=False,ObjectTypeName="WorldDynamic",CustomResponses=((Channel="Visibility",Response=ECR_Ignore)),HelpMessage="WorldDynamic object that is invisible.") ++Profiles=(Name="Trigger",CollisionEnabled=QueryOnly,bCanModify=False,ObjectTypeName="WorldDynamic",CustomResponses=((Channel="WorldStatic",Response=ECR_Overlap),(Channel="Pawn",Response=ECR_Overlap),(Channel="Visibility",Response=ECR_Ignore),(Channel="WorldDynamic",Response=ECR_Overlap),(Channel="Camera",Response=ECR_Overlap),(Channel="PhysicsBody",Response=ECR_Overlap),(Channel="Vehicle",Response=ECR_Overlap),(Channel="Destructible",Response=ECR_Overlap)),HelpMessage="WorldDynamic object that is used for trigger. All other channels will be set to default.") ++Profiles=(Name="Ragdoll",CollisionEnabled=QueryAndPhysics,bCanModify=False,ObjectTypeName="PhysicsBody",CustomResponses=((Channel="Pawn",Response=ECR_Ignore),(Channel="Visibility",Response=ECR_Ignore)),HelpMessage="Simulating Skeletal Mesh Component. All other channels will be set to default.") ++Profiles=(Name="Vehicle",CollisionEnabled=QueryAndPhysics,bCanModify=False,ObjectTypeName="Vehicle",CustomResponses=,HelpMessage="Vehicle object that blocks Vehicle, WorldStatic, and WorldDynamic. All other channels will be set to default.") ++Profiles=(Name="UI",CollisionEnabled=QueryOnly,bCanModify=False,ObjectTypeName="WorldDynamic",CustomResponses=((Channel="WorldStatic",Response=ECR_Overlap),(Channel="Pawn",Response=ECR_Overlap),(Channel="Visibility"),(Channel="WorldDynamic",Response=ECR_Overlap),(Channel="Camera",Response=ECR_Overlap),(Channel="PhysicsBody",Response=ECR_Overlap),(Channel="Vehicle",Response=ECR_Overlap),(Channel="Destructible",Response=ECR_Overlap)),HelpMessage="WorldStatic object that overlaps all actors by default. All new custom channels will use its own default response. ") ++Profiles=(Name="WaterBodyCollision",CollisionEnabled=QueryOnly,bCanModify=False,ObjectTypeName="WorldStatic",CustomResponses=((Channel="WorldDynamic",Response=ECR_Overlap),(Channel="Pawn",Response=ECR_Overlap),(Channel="Visibility",Response=ECR_Ignore),(Channel="Camera",Response=ECR_Ignore),(Channel="PhysicsBody",Response=ECR_Overlap),(Channel="Vehicle",Response=ECR_Overlap),(Channel="Destructible",Response=ECR_Overlap)),HelpMessage="Default Water Collision Profile (Created by Water Plugin)") +-ProfileRedirects=(OldName="BlockingVolume",NewName="InvisibleWall") +-ProfileRedirects=(OldName="InterpActor",NewName="IgnoreOnlyPawn") +-ProfileRedirects=(OldName="StaticMeshComponent",NewName="BlockAllDynamic") +-ProfileRedirects=(OldName="SkeletalMeshActor",NewName="PhysicsActor") +-ProfileRedirects=(OldName="InvisibleActor",NewName="InvisibleWallDynamic") ++ProfileRedirects=(OldName="BlockingVolume",NewName="InvisibleWall") ++ProfileRedirects=(OldName="InterpActor",NewName="IgnoreOnlyPawn") ++ProfileRedirects=(OldName="StaticMeshComponent",NewName="BlockAllDynamic") ++ProfileRedirects=(OldName="SkeletalMeshActor",NewName="PhysicsActor") ++ProfileRedirects=(OldName="InvisibleActor",NewName="InvisibleWallDynamic") +-CollisionChannelRedirects=(OldName="Static",NewName="WorldStatic") +-CollisionChannelRedirects=(OldName="Dynamic",NewName="WorldDynamic") +-CollisionChannelRedirects=(OldName="VehicleMovement",NewName="Vehicle") +-CollisionChannelRedirects=(OldName="PawnMovement",NewName="Pawn") ++CollisionChannelRedirects=(OldName="Static",NewName="WorldStatic") ++CollisionChannelRedirects=(OldName="Dynamic",NewName="WorldDynamic") ++CollisionChannelRedirects=(OldName="VehicleMovement",NewName="Vehicle") ++CollisionChannelRedirects=(OldName="PawnMovement",NewName="Pawn") + diff --git a/Config/DefaultGame.ini b/Config/DefaultGame.ini index 11749f3..656ea4d 100644 --- a/Config/DefaultGame.ini +++ b/Config/DefaultGame.ini @@ -17,6 +17,7 @@ ProjectName=Third Person Game Template ; Paths use the /Game prefix (cooked Content namespace). Uncomment after the ; assets are actually created in the editor. ; ----------------------------------------------------------------------------- + [/Script/ZMMO.ZMMOThemeSubsystem] ;DefaultThemeAsset=/Game/ZMMO/UI/Themes/DA_Theme_Default.DA_Theme_Default ;+ThemeRegistry=(("Christmas", "/Game/ZMMO/UI/Themes/DA_Theme_Christmas.DA_Theme_Christmas")) @@ -35,6 +36,12 @@ ScreenSetAsset=/Game/ZMMO/UI/FrontEnd/DA_FrontEndScreenSet.DA_FrontEndScreenSet ; cliente resolve `mapId -> ClientLevel/spawns` via FZMMOMapDef rows. MapsTableAsset=/Game/ZMMO/Data/World/DT_Maps.DT_Maps +; Loading dinâmico (etapas + dicas). DA_LoadingProfiles tem 1 perfil por +; contexto (EZMMOLoadingContext); DT_LoadingTips rotaciona durante o load. +; Cliente local marca etapas via eventos (sem opcode novo). +LoadingProfilesAsset=/Game/ZMMO/Data/UI/Loading/DA_LoadingProfiles.DA_LoadingProfiles +LoadingTipsAsset=/Game/ZMMO/Data/UI/Loading/DT_LoadingTips.DT_LoadingTips + ; ----------------------------------------------------------------------------- ; UI in-game (PR 19+). Espelho do front-end pattern: ; - ScreenSetAsset mapeia EZMMOInGameUIState -> WBP (Playing -> WBP_HUD, @@ -43,6 +50,7 @@ MapsTableAsset=/Game/ZMMO/Data/World/DT_Maps.DT_Maps ; Playing. ; Asset criado via MCP em /Game/ZMMO/UI/InGame/DA_InGameScreenSet. ; ----------------------------------------------------------------------------- + [/Script/ZMMO.UIInGameFlowSubsystem] ScreenSetAsset=/Game/ZMMO/UI/InGame/DA_InGameScreenSet.DA_InGameScreenSet @@ -55,5 +63,20 @@ ScreenSetAsset=/Game/ZMMO/UI/InGame/DA_InGameScreenSet.DA_InGameScreenSet ; Convenção: usa o path /Script/. (sem o "U" do prefixo ; C++; UClass resolve pelo nome curto). ; ----------------------------------------------------------------------------- + [/Script/ZMMO.ZMMOPlayerState] +ComponentClasses=/Script/ZMMOAttributes.ZMMOAttributeComponent + +[/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)) ++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)) ++PrimaryAssetTypesToScan=(PrimaryAssetType="GameFeatureData",AssetBaseClass="/Script/GameFeatures.GameFeatureData",bHasBlueprintClasses=False,bIsEditorOnly=False,Directories=,SpecificAssets=,Rules=(Priority=-1,ChunkId=-1,bApplyRecursively=True,CookRule=AlwaysCook)) +bOnlyCookProductionAssets=False +bShouldManagerDetermineTypeAndName=False +bShouldGuessTypeAndNameInEditor=True +bShouldAcquireMissingChunksOnLoad=False +bShouldWarnAboutInvalidAssets=True +MetaDataTagsForAssetRegistry=() + diff --git a/Content/BP_TestActor.uasset b/Content/BP_TestActor.uasset new file mode 100644 index 0000000..3902f5c Binary files /dev/null and b/Content/BP_TestActor.uasset differ diff --git a/Content/Blueprints/BP_CellBoundaryVolume.uasset b/Content/Blueprints/BP_CellBoundaryVolume.uasset new file mode 100644 index 0000000..30724d3 Binary files /dev/null and b/Content/Blueprints/BP_CellBoundaryVolume.uasset differ diff --git a/Content/Editor/ZeusUMGForge/FontMap.uasset b/Content/Editor/ZeusUMGForge/FontMap.uasset index 7a0d518..9cdac71 100644 Binary files a/Content/Editor/ZeusUMGForge/FontMap.uasset and b/Content/Editor/ZeusUMGForge/FontMap.uasset differ diff --git a/Content/Materials/MI_CellBoundary_InnerFade_Default.uasset b/Content/Materials/MI_CellBoundary_InnerFade_Default.uasset new file mode 100644 index 0000000..a2a385a Binary files /dev/null and b/Content/Materials/MI_CellBoundary_InnerFade_Default.uasset differ diff --git a/Content/Materials/MI_CellBoundary_InnerFade_East.uasset b/Content/Materials/MI_CellBoundary_InnerFade_East.uasset new file mode 100644 index 0000000..839bf7f Binary files /dev/null and b/Content/Materials/MI_CellBoundary_InnerFade_East.uasset differ diff --git a/Content/Materials/MI_CellBoundary_InnerFade_North.uasset b/Content/Materials/MI_CellBoundary_InnerFade_North.uasset new file mode 100644 index 0000000..a3223d5 Binary files /dev/null and b/Content/Materials/MI_CellBoundary_InnerFade_North.uasset differ diff --git a/Content/Materials/MI_CellBoundary_InnerFade_South.uasset b/Content/Materials/MI_CellBoundary_InnerFade_South.uasset new file mode 100644 index 0000000..5adaa92 Binary files /dev/null and b/Content/Materials/MI_CellBoundary_InnerFade_South.uasset differ diff --git a/Content/Materials/MI_CellBoundary_InnerFade_West.uasset b/Content/Materials/MI_CellBoundary_InnerFade_West.uasset new file mode 100644 index 0000000..363e9d4 Binary files /dev/null and b/Content/Materials/MI_CellBoundary_InnerFade_West.uasset differ diff --git a/Content/Materials/M_CellBoundary_FogCard_Surface.uasset b/Content/Materials/M_CellBoundary_FogCard_Surface.uasset new file mode 100644 index 0000000..1425007 Binary files /dev/null and b/Content/Materials/M_CellBoundary_FogCard_Surface.uasset differ diff --git a/Content/Materials/M_CellBoundary_InnerFade.uasset b/Content/Materials/M_CellBoundary_InnerFade.uasset new file mode 100644 index 0000000..97a9715 Binary files /dev/null and b/Content/Materials/M_CellBoundary_InnerFade.uasset differ diff --git a/Content/Materials/M_CellBoundary_InnerFog_Volume.uasset b/Content/Materials/M_CellBoundary_InnerFog_Volume.uasset new file mode 100644 index 0000000..52915a6 Binary files /dev/null and b/Content/Materials/M_CellBoundary_InnerFog_Volume.uasset differ diff --git a/Content/ZMMO/Data/UI/DT_UI_Styles.uasset b/Content/ZMMO/Data/UI/DT_UI_Styles.uasset index 2a65db1..dde015d 100644 Binary files a/Content/ZMMO/Data/UI/DT_UI_Styles.uasset and b/Content/ZMMO/Data/UI/DT_UI_Styles.uasset differ diff --git a/Content/ZMMO/Data/UI/DT_UI_VitalsReadout.uasset b/Content/ZMMO/Data/UI/DT_UI_VitalsReadout.uasset new file mode 100644 index 0000000..9217745 Binary files /dev/null and b/Content/ZMMO/Data/UI/DT_UI_VitalsReadout.uasset differ diff --git a/Content/ZMMO/Data/UI/Loading/DA_LoadingProfiles.uasset b/Content/ZMMO/Data/UI/Loading/DA_LoadingProfiles.uasset new file mode 100644 index 0000000..49a3c2a Binary files /dev/null and b/Content/ZMMO/Data/UI/Loading/DA_LoadingProfiles.uasset differ diff --git a/Content/ZMMO/Data/UI/Loading/DT_LoadingTips.uasset b/Content/ZMMO/Data/UI/Loading/DT_LoadingTips.uasset new file mode 100644 index 0000000..7d7ea6f Binary files /dev/null and b/Content/ZMMO/Data/UI/Loading/DT_LoadingTips.uasset differ diff --git a/Content/ZMMO/Maps/World/L_ZeusIATest.umap b/Content/ZMMO/Maps/World/L_ZeusIATest.umap new file mode 100644 index 0000000..3a521bf Binary files /dev/null and b/Content/ZMMO/Maps/World/L_ZeusIATest.umap differ diff --git a/Content/ZMMO/Sounds/UI/button_click.uasset b/Content/ZMMO/Sounds/UI/button_click.uasset new file mode 100644 index 0000000..e698612 Binary files /dev/null and b/Content/ZMMO/Sounds/UI/button_click.uasset differ diff --git a/Content/ZMMO/UI/CommonUI/Style/Button/BSB_Button_Transparent.uasset b/Content/ZMMO/UI/CommonUI/Style/Button/BSB_Button_Transparent.uasset index 380a8b7..5faf5cf 100644 Binary files a/Content/ZMMO/UI/CommonUI/Style/Button/BSB_Button_Transparent.uasset and b/Content/ZMMO/UI/CommonUI/Style/Button/BSB_Button_Transparent.uasset differ diff --git a/Content/ZMMO/UI/Fonts/Cormorant_Garamond/Cormorant_Garamond.uasset b/Content/ZMMO/UI/Fonts/Cormorant_Garamond/Cormorant_Garamond.uasset new file mode 100644 index 0000000..3aa11a7 Binary files /dev/null and b/Content/ZMMO/UI/Fonts/Cormorant_Garamond/Cormorant_Garamond.uasset differ diff --git a/Content/ZMMO/UI/Fonts/Cormorant_Garamond/static/CormorantGaramond-Bold.uasset b/Content/ZMMO/UI/Fonts/Cormorant_Garamond/static/CormorantGaramond-Bold.uasset new file mode 100644 index 0000000..fa5eae7 Binary files /dev/null and b/Content/ZMMO/UI/Fonts/Cormorant_Garamond/static/CormorantGaramond-Bold.uasset differ diff --git a/Content/ZMMO/UI/Fonts/Cormorant_Garamond/static/CormorantGaramond-BoldItalic.uasset b/Content/ZMMO/UI/Fonts/Cormorant_Garamond/static/CormorantGaramond-BoldItalic.uasset new file mode 100644 index 0000000..5e780df Binary files /dev/null and b/Content/ZMMO/UI/Fonts/Cormorant_Garamond/static/CormorantGaramond-BoldItalic.uasset differ diff --git a/Content/ZMMO/UI/Fonts/Cormorant_Garamond/static/CormorantGaramond-Italic.uasset b/Content/ZMMO/UI/Fonts/Cormorant_Garamond/static/CormorantGaramond-Italic.uasset new file mode 100644 index 0000000..ee7eb3a Binary files /dev/null and b/Content/ZMMO/UI/Fonts/Cormorant_Garamond/static/CormorantGaramond-Italic.uasset differ diff --git a/Content/ZMMO/UI/Fonts/Cormorant_Garamond/static/CormorantGaramond-Light.uasset b/Content/ZMMO/UI/Fonts/Cormorant_Garamond/static/CormorantGaramond-Light.uasset new file mode 100644 index 0000000..f8235ac Binary files /dev/null and b/Content/ZMMO/UI/Fonts/Cormorant_Garamond/static/CormorantGaramond-Light.uasset differ diff --git a/Content/ZMMO/UI/Fonts/Cormorant_Garamond/static/CormorantGaramond-LightItalic.uasset b/Content/ZMMO/UI/Fonts/Cormorant_Garamond/static/CormorantGaramond-LightItalic.uasset new file mode 100644 index 0000000..52adfca Binary files /dev/null and b/Content/ZMMO/UI/Fonts/Cormorant_Garamond/static/CormorantGaramond-LightItalic.uasset differ diff --git a/Content/ZMMO/UI/Fonts/Cormorant_Garamond/static/CormorantGaramond-Medium.uasset b/Content/ZMMO/UI/Fonts/Cormorant_Garamond/static/CormorantGaramond-Medium.uasset new file mode 100644 index 0000000..57694ec Binary files /dev/null and b/Content/ZMMO/UI/Fonts/Cormorant_Garamond/static/CormorantGaramond-Medium.uasset differ diff --git a/Content/ZMMO/UI/Fonts/Cormorant_Garamond/static/CormorantGaramond-MediumItalic.uasset b/Content/ZMMO/UI/Fonts/Cormorant_Garamond/static/CormorantGaramond-MediumItalic.uasset new file mode 100644 index 0000000..0f73761 Binary files /dev/null and b/Content/ZMMO/UI/Fonts/Cormorant_Garamond/static/CormorantGaramond-MediumItalic.uasset differ diff --git a/Content/ZMMO/UI/Fonts/Cormorant_Garamond/static/CormorantGaramond-Regular.uasset b/Content/ZMMO/UI/Fonts/Cormorant_Garamond/static/CormorantGaramond-Regular.uasset new file mode 100644 index 0000000..48e8b08 Binary files /dev/null and b/Content/ZMMO/UI/Fonts/Cormorant_Garamond/static/CormorantGaramond-Regular.uasset differ diff --git a/Content/ZMMO/UI/Fonts/Cormorant_Garamond/static/CormorantGaramond-SemiBold.uasset b/Content/ZMMO/UI/Fonts/Cormorant_Garamond/static/CormorantGaramond-SemiBold.uasset new file mode 100644 index 0000000..304ccdf Binary files /dev/null and b/Content/ZMMO/UI/Fonts/Cormorant_Garamond/static/CormorantGaramond-SemiBold.uasset differ diff --git a/Content/ZMMO/UI/Fonts/Cormorant_Garamond/static/CormorantGaramond-SemiBoldItalic.uasset b/Content/ZMMO/UI/Fonts/Cormorant_Garamond/static/CormorantGaramond-SemiBoldItalic.uasset new file mode 100644 index 0000000..8c55286 Binary files /dev/null and b/Content/ZMMO/UI/Fonts/Cormorant_Garamond/static/CormorantGaramond-SemiBoldItalic.uasset differ diff --git a/Content/ZMMO/UI/Fonts/JetBrains_Mono/JetBrains_Mono.uasset b/Content/ZMMO/UI/Fonts/JetBrains_Mono/JetBrains_Mono.uasset new file mode 100644 index 0000000..b673fd2 Binary files /dev/null and b/Content/ZMMO/UI/Fonts/JetBrains_Mono/JetBrains_Mono.uasset differ diff --git a/Content/ZMMO/UI/Fonts/JetBrains_Mono/static/JetBrainsMono-Bold.uasset b/Content/ZMMO/UI/Fonts/JetBrains_Mono/static/JetBrainsMono-Bold.uasset new file mode 100644 index 0000000..4bd3762 Binary files /dev/null and b/Content/ZMMO/UI/Fonts/JetBrains_Mono/static/JetBrainsMono-Bold.uasset differ diff --git a/Content/ZMMO/UI/Fonts/JetBrains_Mono/static/JetBrainsMono-BoldItalic.uasset b/Content/ZMMO/UI/Fonts/JetBrains_Mono/static/JetBrainsMono-BoldItalic.uasset new file mode 100644 index 0000000..a5ee4d1 Binary files /dev/null and b/Content/ZMMO/UI/Fonts/JetBrains_Mono/static/JetBrainsMono-BoldItalic.uasset differ diff --git a/Content/ZMMO/UI/Fonts/JetBrains_Mono/static/JetBrainsMono-ExtraBold.uasset b/Content/ZMMO/UI/Fonts/JetBrains_Mono/static/JetBrainsMono-ExtraBold.uasset new file mode 100644 index 0000000..ea64d4d Binary files /dev/null and b/Content/ZMMO/UI/Fonts/JetBrains_Mono/static/JetBrainsMono-ExtraBold.uasset differ diff --git a/Content/ZMMO/UI/Fonts/JetBrains_Mono/static/JetBrainsMono-ExtraBoldItalic.uasset b/Content/ZMMO/UI/Fonts/JetBrains_Mono/static/JetBrainsMono-ExtraBoldItalic.uasset new file mode 100644 index 0000000..4e90806 Binary files /dev/null and b/Content/ZMMO/UI/Fonts/JetBrains_Mono/static/JetBrainsMono-ExtraBoldItalic.uasset differ diff --git a/Content/ZMMO/UI/Fonts/JetBrains_Mono/static/JetBrainsMono-ExtraLight.uasset b/Content/ZMMO/UI/Fonts/JetBrains_Mono/static/JetBrainsMono-ExtraLight.uasset new file mode 100644 index 0000000..ec6dbe1 Binary files /dev/null and b/Content/ZMMO/UI/Fonts/JetBrains_Mono/static/JetBrainsMono-ExtraLight.uasset differ diff --git a/Content/ZMMO/UI/Fonts/JetBrains_Mono/static/JetBrainsMono-ExtraLightItalic.uasset b/Content/ZMMO/UI/Fonts/JetBrains_Mono/static/JetBrainsMono-ExtraLightItalic.uasset new file mode 100644 index 0000000..f96424c Binary files /dev/null and b/Content/ZMMO/UI/Fonts/JetBrains_Mono/static/JetBrainsMono-ExtraLightItalic.uasset differ diff --git a/Content/ZMMO/UI/Fonts/JetBrains_Mono/static/JetBrainsMono-Italic.uasset b/Content/ZMMO/UI/Fonts/JetBrains_Mono/static/JetBrainsMono-Italic.uasset new file mode 100644 index 0000000..3de37d0 Binary files /dev/null and b/Content/ZMMO/UI/Fonts/JetBrains_Mono/static/JetBrainsMono-Italic.uasset differ diff --git a/Content/ZMMO/UI/Fonts/JetBrains_Mono/static/JetBrainsMono-Light.uasset b/Content/ZMMO/UI/Fonts/JetBrains_Mono/static/JetBrainsMono-Light.uasset new file mode 100644 index 0000000..895ef0a Binary files /dev/null and b/Content/ZMMO/UI/Fonts/JetBrains_Mono/static/JetBrainsMono-Light.uasset differ diff --git a/Content/ZMMO/UI/Fonts/JetBrains_Mono/static/JetBrainsMono-LightItalic.uasset b/Content/ZMMO/UI/Fonts/JetBrains_Mono/static/JetBrainsMono-LightItalic.uasset new file mode 100644 index 0000000..555ee9d Binary files /dev/null and b/Content/ZMMO/UI/Fonts/JetBrains_Mono/static/JetBrainsMono-LightItalic.uasset differ diff --git a/Content/ZMMO/UI/Fonts/JetBrains_Mono/static/JetBrainsMono-Medium.uasset b/Content/ZMMO/UI/Fonts/JetBrains_Mono/static/JetBrainsMono-Medium.uasset new file mode 100644 index 0000000..470d47f Binary files /dev/null and b/Content/ZMMO/UI/Fonts/JetBrains_Mono/static/JetBrainsMono-Medium.uasset differ diff --git a/Content/ZMMO/UI/Fonts/JetBrains_Mono/static/JetBrainsMono-MediumItalic.uasset b/Content/ZMMO/UI/Fonts/JetBrains_Mono/static/JetBrainsMono-MediumItalic.uasset new file mode 100644 index 0000000..2b1de9c Binary files /dev/null and b/Content/ZMMO/UI/Fonts/JetBrains_Mono/static/JetBrainsMono-MediumItalic.uasset differ diff --git a/Content/ZMMO/UI/Fonts/JetBrains_Mono/static/JetBrainsMono-Regular.uasset b/Content/ZMMO/UI/Fonts/JetBrains_Mono/static/JetBrainsMono-Regular.uasset new file mode 100644 index 0000000..e6df6ee Binary files /dev/null and b/Content/ZMMO/UI/Fonts/JetBrains_Mono/static/JetBrainsMono-Regular.uasset differ diff --git a/Content/ZMMO/UI/Fonts/JetBrains_Mono/static/JetBrainsMono-SemiBold.uasset b/Content/ZMMO/UI/Fonts/JetBrains_Mono/static/JetBrainsMono-SemiBold.uasset new file mode 100644 index 0000000..838b379 Binary files /dev/null and b/Content/ZMMO/UI/Fonts/JetBrains_Mono/static/JetBrainsMono-SemiBold.uasset differ diff --git a/Content/ZMMO/UI/Fonts/JetBrains_Mono/static/JetBrainsMono-SemiBoldItalic.uasset b/Content/ZMMO/UI/Fonts/JetBrains_Mono/static/JetBrainsMono-SemiBoldItalic.uasset new file mode 100644 index 0000000..eadca26 Binary files /dev/null and b/Content/ZMMO/UI/Fonts/JetBrains_Mono/static/JetBrainsMono-SemiBoldItalic.uasset differ diff --git a/Content/ZMMO/UI/Fonts/JetBrains_Mono/static/JetBrainsMono-Thin.uasset b/Content/ZMMO/UI/Fonts/JetBrains_Mono/static/JetBrainsMono-Thin.uasset new file mode 100644 index 0000000..150a9fb Binary files /dev/null and b/Content/ZMMO/UI/Fonts/JetBrains_Mono/static/JetBrainsMono-Thin.uasset differ diff --git a/Content/ZMMO/UI/Fonts/JetBrains_Mono/static/JetBrainsMono-ThinItalic.uasset b/Content/ZMMO/UI/Fonts/JetBrains_Mono/static/JetBrainsMono-ThinItalic.uasset new file mode 100644 index 0000000..8f544f3 Binary files /dev/null and b/Content/ZMMO/UI/Fonts/JetBrains_Mono/static/JetBrainsMono-ThinItalic.uasset differ diff --git a/Content/ZMMO/UI/Fonts/Manrope/Manrope.uasset b/Content/ZMMO/UI/Fonts/Manrope/Manrope.uasset new file mode 100644 index 0000000..e6abfdb Binary files /dev/null and b/Content/ZMMO/UI/Fonts/Manrope/Manrope.uasset differ diff --git a/Content/ZMMO/UI/Fonts/Manrope/static/Manrope-Bold.uasset b/Content/ZMMO/UI/Fonts/Manrope/static/Manrope-Bold.uasset new file mode 100644 index 0000000..c2e6e79 Binary files /dev/null and b/Content/ZMMO/UI/Fonts/Manrope/static/Manrope-Bold.uasset differ diff --git a/Content/ZMMO/UI/Fonts/Manrope/static/Manrope-ExtraBold.uasset b/Content/ZMMO/UI/Fonts/Manrope/static/Manrope-ExtraBold.uasset new file mode 100644 index 0000000..25c885a Binary files /dev/null and b/Content/ZMMO/UI/Fonts/Manrope/static/Manrope-ExtraBold.uasset differ diff --git a/Content/ZMMO/UI/Fonts/Manrope/static/Manrope-ExtraLight.uasset b/Content/ZMMO/UI/Fonts/Manrope/static/Manrope-ExtraLight.uasset new file mode 100644 index 0000000..c8b3d3a Binary files /dev/null and b/Content/ZMMO/UI/Fonts/Manrope/static/Manrope-ExtraLight.uasset differ diff --git a/Content/ZMMO/UI/Fonts/Manrope/static/Manrope-Light.uasset b/Content/ZMMO/UI/Fonts/Manrope/static/Manrope-Light.uasset new file mode 100644 index 0000000..8506558 Binary files /dev/null and b/Content/ZMMO/UI/Fonts/Manrope/static/Manrope-Light.uasset differ diff --git a/Content/ZMMO/UI/Fonts/Manrope/static/Manrope-Medium.uasset b/Content/ZMMO/UI/Fonts/Manrope/static/Manrope-Medium.uasset new file mode 100644 index 0000000..59329e7 Binary files /dev/null and b/Content/ZMMO/UI/Fonts/Manrope/static/Manrope-Medium.uasset differ diff --git a/Content/ZMMO/UI/Fonts/Manrope/static/Manrope-Regular.uasset b/Content/ZMMO/UI/Fonts/Manrope/static/Manrope-Regular.uasset new file mode 100644 index 0000000..ffe9e32 Binary files /dev/null and b/Content/ZMMO/UI/Fonts/Manrope/static/Manrope-Regular.uasset differ diff --git a/Content/ZMMO/UI/Fonts/Manrope/static/Manrope-SemiBold.uasset b/Content/ZMMO/UI/Fonts/Manrope/static/Manrope-SemiBold.uasset new file mode 100644 index 0000000..c8c65b1 Binary files /dev/null and b/Content/ZMMO/UI/Fonts/Manrope/static/Manrope-SemiBold.uasset differ diff --git a/Content/ZMMO/UI/FrontEnd/DA_FrontEndScreenSet.uasset b/Content/ZMMO/UI/FrontEnd/DA_FrontEndScreenSet.uasset index 5cdc89e..99508a4 100644 Binary files a/Content/ZMMO/UI/FrontEnd/DA_FrontEndScreenSet.uasset and b/Content/ZMMO/UI/FrontEnd/DA_FrontEndScreenSet.uasset differ diff --git a/Content/ZMMO/UI/FrontEnd/WBP_Boot.uasset b/Content/ZMMO/UI/FrontEnd/WBP_Boot.uasset index 91d15bb..8fa197c 100644 Binary files a/Content/ZMMO/UI/FrontEnd/WBP_Boot.uasset and b/Content/ZMMO/UI/FrontEnd/WBP_Boot.uasset differ diff --git a/Content/ZMMO/UI/HUD/WBP_HUD.uasset b/Content/ZMMO/UI/HUD/WBP_HUD.uasset index 2c72fab..3d7b3be 100644 Binary files a/Content/ZMMO/UI/HUD/WBP_HUD.uasset and b/Content/ZMMO/UI/HUD/WBP_HUD.uasset differ diff --git a/Content/ZMMO/UI/HUD/WBP_PlayerVitalsPanel.uasset b/Content/ZMMO/UI/HUD/WBP_PlayerVitalsPanel.uasset new file mode 100644 index 0000000..98cde25 Binary files /dev/null and b/Content/ZMMO/UI/HUD/WBP_PlayerVitalsPanel.uasset differ diff --git a/Content/ZMMO/UI/HUD/WBP_StatusWindow.uasset b/Content/ZMMO/UI/HUD/WBP_StatusWindow.uasset deleted file mode 100644 index 53f54bc..0000000 Binary files a/Content/ZMMO/UI/HUD/WBP_StatusWindow.uasset and /dev/null differ diff --git a/Content/ZMMO/UI/InGame/DA_InGameScreenSet.uasset b/Content/ZMMO/UI/InGame/DA_InGameScreenSet.uasset index 1c5275b..b16070b 100644 Binary files a/Content/ZMMO/UI/InGame/DA_InGameScreenSet.uasset and b/Content/ZMMO/UI/InGame/DA_InGameScreenSet.uasset differ diff --git a/Content/ZMMO/UI/Materials/M_UI_Gradient_TopToBottom.uasset b/Content/ZMMO/UI/Materials/M_UI_Gradient_TopToBottom.uasset new file mode 100644 index 0000000..3c7a7d3 Binary files /dev/null and b/Content/ZMMO/UI/Materials/M_UI_Gradient_TopToBottom.uasset differ diff --git a/Content/ZMMO/UI/Screens/PlayerStatus/UI_PlayerStatus_BonusRow.uasset b/Content/ZMMO/UI/Screens/PlayerStatus/UI_PlayerStatus_BonusRow.uasset new file mode 100644 index 0000000..1b74989 Binary files /dev/null and b/Content/ZMMO/UI/Screens/PlayerStatus/UI_PlayerStatus_BonusRow.uasset differ diff --git a/Content/ZMMO/UI/Screens/PlayerStatus/UI_PlayerStatus_StatRow.uasset b/Content/ZMMO/UI/Screens/PlayerStatus/UI_PlayerStatus_StatRow.uasset new file mode 100644 index 0000000..34fbf4b Binary files /dev/null and b/Content/ZMMO/UI/Screens/PlayerStatus/UI_PlayerStatus_StatRow.uasset differ diff --git a/Content/ZMMO/UI/Screens/PlayerStatus/UI_PlayerStatus_Window.uasset b/Content/ZMMO/UI/Screens/PlayerStatus/UI_PlayerStatus_Window.uasset new file mode 100644 index 0000000..1a66a2e Binary files /dev/null and b/Content/ZMMO/UI/Screens/PlayerStatus/UI_PlayerStatus_Window.uasset differ diff --git a/Content/ZMMO/UI/Shared/Loading/WBP_Loading.uasset b/Content/ZMMO/UI/Shared/Loading/WBP_Loading.uasset new file mode 100644 index 0000000..e25bc98 Binary files /dev/null and b/Content/ZMMO/UI/Shared/Loading/WBP_Loading.uasset differ diff --git a/Content/ZMMO/UI/Shared/UI_Button_Master_New.uasset b/Content/ZMMO/UI/Shared/UI_Button_Master_New.uasset new file mode 100644 index 0000000..97c0411 Binary files /dev/null and b/Content/ZMMO/UI/Shared/UI_Button_Master_New.uasset differ diff --git a/Content/ZMMO/UI/Shared/UI_Panel_Master.uasset b/Content/ZMMO/UI/Shared/UI_Panel_Master.uasset index 2670a64..5db62ec 100644 Binary files a/Content/ZMMO/UI/Shared/UI_Panel_Master.uasset and b/Content/ZMMO/UI/Shared/UI_Panel_Master.uasset differ diff --git a/Content/ZMMO/UI/Shared/UI_ProgressBar_Master.uasset b/Content/ZMMO/UI/Shared/UI_ProgressBar_Master.uasset new file mode 100644 index 0000000..708bc69 Binary files /dev/null and b/Content/ZMMO/UI/Shared/UI_ProgressBar_Master.uasset differ diff --git a/Content/ZMMO/UI/Themes/Default/Button/UI_M_Button.uasset b/Content/ZMMO/UI/Themes/Default/Button/UI_M_Button.uasset new file mode 100644 index 0000000..41839a7 Binary files /dev/null and b/Content/ZMMO/UI/Themes/Default/Button/UI_M_Button.uasset differ diff --git a/Content/ZMMO/UI/Themes/Default/Button/UI_M_Button_Frame.uasset b/Content/ZMMO/UI/Themes/Default/Button/UI_M_Button_Frame.uasset new file mode 100644 index 0000000..50b3046 Binary files /dev/null and b/Content/ZMMO/UI/Themes/Default/Button/UI_M_Button_Frame.uasset differ diff --git a/Content/ZMMO/UI/Themes/Default/Button/UI_M_Button_Glow.uasset b/Content/ZMMO/UI/Themes/Default/Button/UI_M_Button_Glow.uasset new file mode 100644 index 0000000..516773d Binary files /dev/null and b/Content/ZMMO/UI/Themes/Default/Button/UI_M_Button_Glow.uasset differ diff --git a/Content/ZMMO/UI/Themes/Default/Button/UI_M_CornerBrackets.uasset b/Content/ZMMO/UI/Themes/Default/Button/UI_M_CornerBrackets.uasset new file mode 100644 index 0000000..01c2003 Binary files /dev/null and b/Content/ZMMO/UI/Themes/Default/Button/UI_M_CornerBrackets.uasset differ diff --git a/Content/ZMMO/UI/Themes/Default/Panel/Outlines/Effects/T_Panel_Outline_Effect_Square_01.uasset b/Content/ZMMO/UI/Themes/Default/Panel/Outlines/Effects/T_Panel_Outline_Effect_Square_01.uasset new file mode 100644 index 0000000..89bcd60 Binary files /dev/null and b/Content/ZMMO/UI/Themes/Default/Panel/Outlines/Effects/T_Panel_Outline_Effect_Square_01.uasset differ diff --git a/Content/ZMMO/UI/Themes/Default/Panel/Outlines/Effects/T_Panel_Outline_Effect_Square_02.uasset b/Content/ZMMO/UI/Themes/Default/Panel/Outlines/Effects/T_Panel_Outline_Effect_Square_02.uasset new file mode 100644 index 0000000..3070192 Binary files /dev/null and b/Content/ZMMO/UI/Themes/Default/Panel/Outlines/Effects/T_Panel_Outline_Effect_Square_02.uasset differ diff --git a/Content/ZMMO/UI/Themes/Default/Panel/Outlines/T_Panel_Outline_Square_01.uasset b/Content/ZMMO/UI/Themes/Default/Panel/Outlines/T_Panel_Outline_Square_01.uasset new file mode 100644 index 0000000..cd9c926 Binary files /dev/null and b/Content/ZMMO/UI/Themes/Default/Panel/Outlines/T_Panel_Outline_Square_01.uasset differ diff --git a/Content/ZMMO/UI/Themes/Default/Panel/T_Panel_Background_Gradient_Square.uasset b/Content/ZMMO/UI/Themes/Default/Panel/T_Panel_Background_Gradient_Square.uasset new file mode 100644 index 0000000..8b72547 Binary files /dev/null and b/Content/ZMMO/UI/Themes/Default/Panel/T_Panel_Background_Gradient_Square.uasset differ diff --git a/Content/ZMMO/UI/Themes/Default/Progressbar/UI_M_ProgressBar.uasset b/Content/ZMMO/UI/Themes/Default/Progressbar/UI_M_ProgressBar.uasset new file mode 100644 index 0000000..85676b1 Binary files /dev/null and b/Content/ZMMO/UI/Themes/Default/Progressbar/UI_M_ProgressBar.uasset differ diff --git a/Content/ZMMO/UI/Themes/Default/Progressbar/UI_M_ProgressBar_BG.uasset b/Content/ZMMO/UI/Themes/Default/Progressbar/UI_M_ProgressBar_BG.uasset new file mode 100644 index 0000000..b38c14d Binary files /dev/null and b/Content/ZMMO/UI/Themes/Default/Progressbar/UI_M_ProgressBar_BG.uasset differ diff --git a/Content/ZMMO/UI/Themes/Default/Progressbar/UI_M_ProgressBar_BaseEXP.uasset b/Content/ZMMO/UI/Themes/Default/Progressbar/UI_M_ProgressBar_BaseEXP.uasset new file mode 100644 index 0000000..a1303a1 Binary files /dev/null and b/Content/ZMMO/UI/Themes/Default/Progressbar/UI_M_ProgressBar_BaseEXP.uasset differ diff --git a/Content/ZMMO/UI/Themes/Default/Progressbar/UI_M_ProgressBar_Edge.uasset b/Content/ZMMO/UI/Themes/Default/Progressbar/UI_M_ProgressBar_Edge.uasset new file mode 100644 index 0000000..3feb2ec Binary files /dev/null and b/Content/ZMMO/UI/Themes/Default/Progressbar/UI_M_ProgressBar_Edge.uasset differ diff --git a/Content/ZMMO/UI/Themes/Default/Progressbar/UI_M_ProgressBar_HP.uasset b/Content/ZMMO/UI/Themes/Default/Progressbar/UI_M_ProgressBar_HP.uasset new file mode 100644 index 0000000..1b30666 Binary files /dev/null and b/Content/ZMMO/UI/Themes/Default/Progressbar/UI_M_ProgressBar_HP.uasset differ diff --git a/Content/ZMMO/UI/Themes/Default/Progressbar/UI_M_ProgressBar_JobEXP.uasset b/Content/ZMMO/UI/Themes/Default/Progressbar/UI_M_ProgressBar_JobEXP.uasset new file mode 100644 index 0000000..307ce41 Binary files /dev/null and b/Content/ZMMO/UI/Themes/Default/Progressbar/UI_M_ProgressBar_JobEXP.uasset differ diff --git a/Content/ZMMO/UI/Themes/Default/Progressbar/UI_M_ProgressBar_Loading.uasset b/Content/ZMMO/UI/Themes/Default/Progressbar/UI_M_ProgressBar_Loading.uasset new file mode 100644 index 0000000..73e0bd2 Binary files /dev/null and b/Content/ZMMO/UI/Themes/Default/Progressbar/UI_M_ProgressBar_Loading.uasset differ diff --git a/Content/ZMMO/UI/Themes/Default/Progressbar/UI_M_ProgressBar_SP.uasset b/Content/ZMMO/UI/Themes/Default/Progressbar/UI_M_ProgressBar_SP.uasset new file mode 100644 index 0000000..4e5600c Binary files /dev/null and b/Content/ZMMO/UI/Themes/Default/Progressbar/UI_M_ProgressBar_SP.uasset differ diff --git a/Content/ZMMO/UI/Themes/Default/Progressbar/UI_M_ProgressBar_Shimmer.uasset b/Content/ZMMO/UI/Themes/Default/Progressbar/UI_M_ProgressBar_Shimmer.uasset new file mode 100644 index 0000000..533f5e9 Binary files /dev/null and b/Content/ZMMO/UI/Themes/Default/Progressbar/UI_M_ProgressBar_Shimmer.uasset differ diff --git a/Content/ZMMO/UI/Themes/Default/Progressbar/UI_M_ProgressBar_TrackGrid.uasset b/Content/ZMMO/UI/Themes/Default/Progressbar/UI_M_ProgressBar_TrackGrid.uasset new file mode 100644 index 0000000..62596e7 Binary files /dev/null and b/Content/ZMMO/UI/Themes/Default/Progressbar/UI_M_ProgressBar_TrackGrid.uasset differ diff --git a/Content/ZeusTemp/M_ProbeVol_dbf9e0.uasset b/Content/ZeusTemp/M_ProbeVol_dbf9e0.uasset new file mode 100644 index 0000000..f8b6a0a Binary files /dev/null and b/Content/ZeusTemp/M_ProbeVol_dbf9e0.uasset differ diff --git a/Content/ZeusTemp/M_Probe_57c18e41.uasset b/Content/ZeusTemp/M_Probe_57c18e41.uasset new file mode 100644 index 0000000..46bd16b Binary files /dev/null and b/Content/ZeusTemp/M_Probe_57c18e41.uasset differ diff --git a/Content/ZeusTemp/M_Probe_c8231c7b.uasset b/Content/ZeusTemp/M_Probe_c8231c7b.uasset new file mode 100644 index 0000000..835a10c Binary files /dev/null and b/Content/ZeusTemp/M_Probe_c8231c7b.uasset differ diff --git a/Content/ZeusTemp/M_Probe_d666a86b.uasset b/Content/ZeusTemp/M_Probe_d666a86b.uasset new file mode 100644 index 0000000..303a644 Binary files /dev/null and b/Content/ZeusTemp/M_Probe_d666a86b.uasset differ diff --git a/Content/ZeusTemp/M_TempProbe_41c02943.uasset b/Content/ZeusTemp/M_TempProbe_41c02943.uasset new file mode 100644 index 0000000..0421a77 Binary files /dev/null and b/Content/ZeusTemp/M_TempProbe_41c02943.uasset differ diff --git a/Content/ZeusTest/BP_TesteZeus.uasset b/Content/ZeusTest/BP_TesteZeus.uasset new file mode 100644 index 0000000..b29c9f7 Binary files /dev/null and b/Content/ZeusTest/BP_TesteZeus.uasset differ diff --git a/Content/ZeusTest/GAS/GE_Veneno.uasset b/Content/ZeusTest/GAS/GE_Veneno.uasset new file mode 100644 index 0000000..6782f73 Binary files /dev/null and b/Content/ZeusTest/GAS/GE_Veneno.uasset differ diff --git a/Content/ZeusTest/PCG/PCG_Espalhar.uasset b/Content/ZeusTest/PCG/PCG_Espalhar.uasset new file mode 100644 index 0000000..0b30c93 Binary files /dev/null and b/Content/ZeusTest/PCG/PCG_Espalhar.uasset differ diff --git a/Content/ZeusTest/Textures/T_BackdropBg.uasset b/Content/ZeusTest/Textures/T_BackdropBg.uasset new file mode 100644 index 0000000..e2e4530 Binary files /dev/null and b/Content/ZeusTest/Textures/T_BackdropBg.uasset differ diff --git a/Content/ZeusTest/Textures/T_PanelBg.uasset b/Content/ZeusTest/Textures/T_PanelBg.uasset new file mode 100644 index 0000000..3bc63b6 Binary files /dev/null and b/Content/ZeusTest/Textures/T_PanelBg.uasset differ diff --git a/Content/ZeusTest/WBP_LoadingScreen.uasset b/Content/ZeusTest/WBP_LoadingScreen.uasset new file mode 100644 index 0000000..04561a5 Binary files /dev/null and b/Content/ZeusTest/WBP_LoadingScreen.uasset differ diff --git a/Content/ZeusTest/WBP_RoStatusModern.uasset b/Content/ZeusTest/WBP_RoStatusModern.uasset new file mode 100644 index 0000000..44ae24c Binary files /dev/null and b/Content/ZeusTest/WBP_RoStatusModern.uasset differ diff --git a/Content/__ExternalActors__/ZMMO/Maps/World/L_TestWorld/2/BR/38W34OMKW0W3L1PGS3Y98G.uasset b/Content/__ExternalActors__/ZMMO/Maps/World/L_TestWorld/2/BR/38W34OMKW0W3L1PGS3Y98G.uasset new file mode 100644 index 0000000..3e4faf3 Binary files /dev/null and b/Content/__ExternalActors__/ZMMO/Maps/World/L_TestWorld/2/BR/38W34OMKW0W3L1PGS3Y98G.uasset differ diff --git a/Content/__ExternalActors__/ZMMO/Maps/World/L_TestWorld/4/1A/FD48EIIJ78873L9WAC6KG0.uasset b/Content/__ExternalActors__/ZMMO/Maps/World/L_TestWorld/4/1A/FD48EIIJ78873L9WAC6KG0.uasset new file mode 100644 index 0000000..f8663ee Binary files /dev/null and b/Content/__ExternalActors__/ZMMO/Maps/World/L_TestWorld/4/1A/FD48EIIJ78873L9WAC6KG0.uasset differ diff --git a/Content/__ExternalActors__/ZMMO/Maps/World/L_TestWorld/4/8L/X3PMRJRGAACIU1WG1P1KJ0.uasset b/Content/__ExternalActors__/ZMMO/Maps/World/L_TestWorld/4/8L/X3PMRJRGAACIU1WG1P1KJ0.uasset new file mode 100644 index 0000000..2552681 Binary files /dev/null and b/Content/__ExternalActors__/ZMMO/Maps/World/L_TestWorld/4/8L/X3PMRJRGAACIU1WG1P1KJ0.uasset differ diff --git a/Content/__ExternalActors__/ZMMO/Maps/World/L_TestWorld/8/L1/C6M65Q91PG5E7ML91VQZX3.uasset b/Content/__ExternalActors__/ZMMO/Maps/World/L_TestWorld/8/L1/C6M65Q91PG5E7ML91VQZX3.uasset new file mode 100644 index 0000000..059ff5a Binary files /dev/null and b/Content/__ExternalActors__/ZMMO/Maps/World/L_TestWorld/8/L1/C6M65Q91PG5E7ML91VQZX3.uasset differ diff --git a/Content/__ExternalActors__/ZMMO/Maps/World/L_TestWorld/9/KB/V28W8B92OQI6DBTUIVKU77.uasset b/Content/__ExternalActors__/ZMMO/Maps/World/L_TestWorld/9/KB/V28W8B92OQI6DBTUIVKU77.uasset index 6543c66..0025214 100644 Binary files a/Content/__ExternalActors__/ZMMO/Maps/World/L_TestWorld/9/KB/V28W8B92OQI6DBTUIVKU77.uasset and b/Content/__ExternalActors__/ZMMO/Maps/World/L_TestWorld/9/KB/V28W8B92OQI6DBTUIVKU77.uasset differ diff --git a/Content/__ExternalActors__/ZMMO/Maps/World/L_TestWorld/A/JJ/F2YIJ9M3YY1NPG7GHRYC5W.uasset b/Content/__ExternalActors__/ZMMO/Maps/World/L_TestWorld/A/JJ/F2YIJ9M3YY1NPG7GHRYC5W.uasset index b5d38e7..1da0eb2 100644 Binary files a/Content/__ExternalActors__/ZMMO/Maps/World/L_TestWorld/A/JJ/F2YIJ9M3YY1NPG7GHRYC5W.uasset and b/Content/__ExternalActors__/ZMMO/Maps/World/L_TestWorld/A/JJ/F2YIJ9M3YY1NPG7GHRYC5W.uasset differ diff --git a/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/0/64/FAEH5OWWST50VZNCDMIDDY.uasset b/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/0/64/FAEH5OWWST50VZNCDMIDDY.uasset new file mode 100644 index 0000000..ad793c5 Binary files /dev/null and b/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/0/64/FAEH5OWWST50VZNCDMIDDY.uasset differ diff --git a/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/0/M2/U9KYS4CUF0XXNNNTIUQVPJ.uasset b/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/0/M2/U9KYS4CUF0XXNNNTIUQVPJ.uasset new file mode 100644 index 0000000..cd76248 Binary files /dev/null and b/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/0/M2/U9KYS4CUF0XXNNNTIUQVPJ.uasset differ diff --git a/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/0/NR/CN1EE1PGQGOFVCYW3NXIDX.uasset b/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/0/NR/CN1EE1PGQGOFVCYW3NXIDX.uasset new file mode 100644 index 0000000..9fe9de6 Binary files /dev/null and b/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/0/NR/CN1EE1PGQGOFVCYW3NXIDX.uasset differ diff --git a/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/0/OL/VET3QZ2KK0KSKCJYKA8TAQ.uasset b/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/0/OL/VET3QZ2KK0KSKCJYKA8TAQ.uasset new file mode 100644 index 0000000..bebf7b3 Binary files /dev/null and b/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/0/OL/VET3QZ2KK0KSKCJYKA8TAQ.uasset differ diff --git a/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/0/RN/QG6XUIOXRESND5Z87U864L.uasset b/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/0/RN/QG6XUIOXRESND5Z87U864L.uasset new file mode 100644 index 0000000..3721e6d Binary files /dev/null and b/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/0/RN/QG6XUIOXRESND5Z87U864L.uasset differ diff --git a/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/0/T9/WEHK1Q0EQ6SO8YG52ONPYW.uasset b/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/0/T9/WEHK1Q0EQ6SO8YG52ONPYW.uasset new file mode 100644 index 0000000..18666de Binary files /dev/null and b/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/0/T9/WEHK1Q0EQ6SO8YG52ONPYW.uasset differ diff --git a/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/0/Z3/QAJWZIXBOL264Q12S9KADL.uasset b/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/0/Z3/QAJWZIXBOL264Q12S9KADL.uasset new file mode 100644 index 0000000..17f82b1 Binary files /dev/null and b/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/0/Z3/QAJWZIXBOL264Q12S9KADL.uasset differ diff --git a/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/1/22/XRM0P8THBCRCLQXRW92B6N.uasset b/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/1/22/XRM0P8THBCRCLQXRW92B6N.uasset new file mode 100644 index 0000000..3a44ba7 Binary files /dev/null and b/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/1/22/XRM0P8THBCRCLQXRW92B6N.uasset differ diff --git a/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/1/A4/LX27OCBDM64ACXQQL1A15L.uasset b/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/1/A4/LX27OCBDM64ACXQQL1A15L.uasset new file mode 100644 index 0000000..c9e7d57 Binary files /dev/null and b/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/1/A4/LX27OCBDM64ACXQQL1A15L.uasset differ diff --git a/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/1/C3/BXF25L2R7IDWRIVYMDE37H.uasset b/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/1/C3/BXF25L2R7IDWRIVYMDE37H.uasset new file mode 100644 index 0000000..177801b Binary files /dev/null and b/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/1/C3/BXF25L2R7IDWRIVYMDE37H.uasset differ diff --git a/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/1/EI/113FKJVPK5I92EG511W69E.uasset b/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/1/EI/113FKJVPK5I92EG511W69E.uasset new file mode 100644 index 0000000..8bd3d64 Binary files /dev/null and b/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/1/EI/113FKJVPK5I92EG511W69E.uasset differ diff --git a/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/1/KP/37HC5F2K743528LX1JS9WI.uasset b/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/1/KP/37HC5F2K743528LX1JS9WI.uasset new file mode 100644 index 0000000..f6f3d26 Binary files /dev/null and b/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/1/KP/37HC5F2K743528LX1JS9WI.uasset differ diff --git a/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/1/MF/XZSHS09JHUZ7SX1N2G3I1O.uasset b/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/1/MF/XZSHS09JHUZ7SX1N2G3I1O.uasset new file mode 100644 index 0000000..c033b0c Binary files /dev/null and b/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/1/MF/XZSHS09JHUZ7SX1N2G3I1O.uasset differ diff --git a/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/1/X4/O5029E34S00URE1N4AWITR.uasset b/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/1/X4/O5029E34S00URE1N4AWITR.uasset new file mode 100644 index 0000000..5f12d52 Binary files /dev/null and b/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/1/X4/O5029E34S00URE1N4AWITR.uasset differ diff --git a/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/2/F0/1D1B2494RZ4EM6ZXBCUSV4.uasset b/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/2/F0/1D1B2494RZ4EM6ZXBCUSV4.uasset new file mode 100644 index 0000000..50ec6bc Binary files /dev/null and b/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/2/F0/1D1B2494RZ4EM6ZXBCUSV4.uasset differ diff --git a/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/2/FT/MO2201X4IECZT1CYON11FS.uasset b/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/2/FT/MO2201X4IECZT1CYON11FS.uasset new file mode 100644 index 0000000..1e788d4 Binary files /dev/null and b/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/2/FT/MO2201X4IECZT1CYON11FS.uasset differ diff --git a/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/2/KJ/9R05C8LFQU83GNGKKKQXJO.uasset b/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/2/KJ/9R05C8LFQU83GNGKKKQXJO.uasset new file mode 100644 index 0000000..9d53512 Binary files /dev/null and b/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/2/KJ/9R05C8LFQU83GNGKKKQXJO.uasset differ diff --git a/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/2/KR/NBY16QATQHL6RRR3C3T4SI.uasset b/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/2/KR/NBY16QATQHL6RRR3C3T4SI.uasset new file mode 100644 index 0000000..831a31e Binary files /dev/null and b/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/2/KR/NBY16QATQHL6RRR3C3T4SI.uasset differ diff --git a/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/2/Q1/9IG6TBOU7KF2UMYJEJ6VW7.uasset b/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/2/Q1/9IG6TBOU7KF2UMYJEJ6VW7.uasset new file mode 100644 index 0000000..0d39f6f Binary files /dev/null and b/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/2/Q1/9IG6TBOU7KF2UMYJEJ6VW7.uasset differ diff --git a/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/2/X8/IF9ESOQUF81DUPYI1NUM8J.uasset b/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/2/X8/IF9ESOQUF81DUPYI1NUM8J.uasset new file mode 100644 index 0000000..40607b7 Binary files /dev/null and b/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/2/X8/IF9ESOQUF81DUPYI1NUM8J.uasset differ diff --git a/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/2/Y1/RZ1P0N4JKTZ14UIYM081ZC.uasset b/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/2/Y1/RZ1P0N4JKTZ14UIYM081ZC.uasset new file mode 100644 index 0000000..a1c5841 Binary files /dev/null and b/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/2/Y1/RZ1P0N4JKTZ14UIYM081ZC.uasset differ diff --git a/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/3/EG/IT4O3PFFJCV8WPS3JLZHSY.uasset b/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/3/EG/IT4O3PFFJCV8WPS3JLZHSY.uasset new file mode 100644 index 0000000..3f56946 Binary files /dev/null and b/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/3/EG/IT4O3PFFJCV8WPS3JLZHSY.uasset differ diff --git a/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/3/GY/9J3KIFLIFF3P0ADN8UNORK.uasset b/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/3/GY/9J3KIFLIFF3P0ADN8UNORK.uasset new file mode 100644 index 0000000..5b691da Binary files /dev/null and b/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/3/GY/9J3KIFLIFF3P0ADN8UNORK.uasset differ diff --git a/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/3/WD/JLXQS1CCZFNKSPOOEOGGRN.uasset b/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/3/WD/JLXQS1CCZFNKSPOOEOGGRN.uasset new file mode 100644 index 0000000..bf116ab Binary files /dev/null and b/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/3/WD/JLXQS1CCZFNKSPOOEOGGRN.uasset differ diff --git a/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/4/29/4I4Y7BLFQZK8TPZ9I3GF8R.uasset b/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/4/29/4I4Y7BLFQZK8TPZ9I3GF8R.uasset new file mode 100644 index 0000000..cc3d537 Binary files /dev/null and b/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/4/29/4I4Y7BLFQZK8TPZ9I3GF8R.uasset differ diff --git a/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/4/AE/CQOOS9Y3747R6FG4N99UZW.uasset b/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/4/AE/CQOOS9Y3747R6FG4N99UZW.uasset new file mode 100644 index 0000000..7d2eb67 Binary files /dev/null and b/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/4/AE/CQOOS9Y3747R6FG4N99UZW.uasset differ diff --git a/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/4/MI/ML6GIBT4GV879J5EU51D3X.uasset b/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/4/MI/ML6GIBT4GV879J5EU51D3X.uasset new file mode 100644 index 0000000..c01d408 Binary files /dev/null and b/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/4/MI/ML6GIBT4GV879J5EU51D3X.uasset differ diff --git a/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/5/64/BCWD4LAATY7Y84APXCAAEG.uasset b/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/5/64/BCWD4LAATY7Y84APXCAAEG.uasset new file mode 100644 index 0000000..bf9a015 Binary files /dev/null and b/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/5/64/BCWD4LAATY7Y84APXCAAEG.uasset differ diff --git a/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/5/CE/QEA10LP121CKKVFP5E1ST3.uasset b/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/5/CE/QEA10LP121CKKVFP5E1ST3.uasset new file mode 100644 index 0000000..c8b5951 Binary files /dev/null and b/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/5/CE/QEA10LP121CKKVFP5E1ST3.uasset differ diff --git a/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/5/D0/YWMTS3D7Z6JF2RTQNIKK0X.uasset b/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/5/D0/YWMTS3D7Z6JF2RTQNIKK0X.uasset new file mode 100644 index 0000000..f86004a Binary files /dev/null and b/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/5/D0/YWMTS3D7Z6JF2RTQNIKK0X.uasset differ diff --git a/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/5/HP/3ZCZ172EMAYWA6IIUYBDQG.uasset b/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/5/HP/3ZCZ172EMAYWA6IIUYBDQG.uasset new file mode 100644 index 0000000..4f8d971 Binary files /dev/null and b/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/5/HP/3ZCZ172EMAYWA6IIUYBDQG.uasset differ diff --git a/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/5/JB/9KI7UFBVI58RZGZ2369ZSD.uasset b/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/5/JB/9KI7UFBVI58RZGZ2369ZSD.uasset new file mode 100644 index 0000000..d4577e2 Binary files /dev/null and b/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/5/JB/9KI7UFBVI58RZGZ2369ZSD.uasset differ diff --git a/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/6/T1/O37ZINTMXVJH413HR8U0YX.uasset b/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/6/T1/O37ZINTMXVJH413HR8U0YX.uasset new file mode 100644 index 0000000..34fca6a Binary files /dev/null and b/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/6/T1/O37ZINTMXVJH413HR8U0YX.uasset differ diff --git a/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/7/78/L055VX5O5OM5NRHVKIZG2R.uasset b/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/7/78/L055VX5O5OM5NRHVKIZG2R.uasset new file mode 100644 index 0000000..a1a64e6 Binary files /dev/null and b/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/7/78/L055VX5O5OM5NRHVKIZG2R.uasset differ diff --git a/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/7/B2/8RUUXDTTP0JBO482EDO01E.uasset b/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/7/B2/8RUUXDTTP0JBO482EDO01E.uasset new file mode 100644 index 0000000..3de8dc2 Binary files /dev/null and b/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/7/B2/8RUUXDTTP0JBO482EDO01E.uasset differ diff --git a/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/7/G3/45YH2KQG15JR515WTHMHQN.uasset b/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/7/G3/45YH2KQG15JR515WTHMHQN.uasset new file mode 100644 index 0000000..5c07d37 Binary files /dev/null and b/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/7/G3/45YH2KQG15JR515WTHMHQN.uasset differ diff --git a/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/7/V3/2SXVNSPCA9KB809V7OZO8I.uasset b/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/7/V3/2SXVNSPCA9KB809V7OZO8I.uasset new file mode 100644 index 0000000..52389e5 Binary files /dev/null and b/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/7/V3/2SXVNSPCA9KB809V7OZO8I.uasset differ diff --git a/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/7/XK/OTWJIG0DO97GKFUXQFXANO.uasset b/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/7/XK/OTWJIG0DO97GKFUXQFXANO.uasset new file mode 100644 index 0000000..0d37ecb Binary files /dev/null and b/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/7/XK/OTWJIG0DO97GKFUXQFXANO.uasset differ diff --git a/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/8/90/MU2Q8Z98ME6IKLS3JNAIMJ.uasset b/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/8/90/MU2Q8Z98ME6IKLS3JNAIMJ.uasset new file mode 100644 index 0000000..e001562 Binary files /dev/null and b/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/8/90/MU2Q8Z98ME6IKLS3JNAIMJ.uasset differ diff --git a/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/8/HQ/UCQ68S4I9GT5CDMYOHUOWW.uasset b/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/8/HQ/UCQ68S4I9GT5CDMYOHUOWW.uasset new file mode 100644 index 0000000..c406d3e Binary files /dev/null and b/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/8/HQ/UCQ68S4I9GT5CDMYOHUOWW.uasset differ diff --git a/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/8/K9/JE2KM4GCIYKQ7WZUG9IVZP.uasset b/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/8/K9/JE2KM4GCIYKQ7WZUG9IVZP.uasset new file mode 100644 index 0000000..d6a22f9 Binary files /dev/null and b/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/8/K9/JE2KM4GCIYKQ7WZUG9IVZP.uasset differ diff --git a/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/8/KQ/89PETUIPJN2PUIL0MPQRFL.uasset b/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/8/KQ/89PETUIPJN2PUIL0MPQRFL.uasset new file mode 100644 index 0000000..fd4c735 Binary files /dev/null and b/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/8/KQ/89PETUIPJN2PUIL0MPQRFL.uasset differ diff --git a/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/8/XH/FC1IDUQN24112995S4X5EJ.uasset b/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/8/XH/FC1IDUQN24112995S4X5EJ.uasset new file mode 100644 index 0000000..d68b852 Binary files /dev/null and b/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/8/XH/FC1IDUQN24112995S4X5EJ.uasset differ diff --git a/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/8/ZH/08GB02PZVSN68PRU9JQWPW.uasset b/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/8/ZH/08GB02PZVSN68PRU9JQWPW.uasset new file mode 100644 index 0000000..f963141 Binary files /dev/null and b/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/8/ZH/08GB02PZVSN68PRU9JQWPW.uasset differ diff --git a/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/8/ZW/JYV8HJ5Y3WXQS423FV7B12.uasset b/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/8/ZW/JYV8HJ5Y3WXQS423FV7B12.uasset new file mode 100644 index 0000000..5ed7f99 Binary files /dev/null and b/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/8/ZW/JYV8HJ5Y3WXQS423FV7B12.uasset differ diff --git a/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/9/0B/1XMFISJ1Z8MPZZLG5YKLMT.uasset b/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/9/0B/1XMFISJ1Z8MPZZLG5YKLMT.uasset new file mode 100644 index 0000000..ed0863c Binary files /dev/null and b/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/9/0B/1XMFISJ1Z8MPZZLG5YKLMT.uasset differ diff --git a/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/9/F8/IGUIUXSEWAKCMYGSUTQ5ID.uasset b/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/9/F8/IGUIUXSEWAKCMYGSUTQ5ID.uasset new file mode 100644 index 0000000..b78b78f Binary files /dev/null and b/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/9/F8/IGUIUXSEWAKCMYGSUTQ5ID.uasset differ diff --git a/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/9/IP/J90IE4XSE3FE4X0ICOIRCS.uasset b/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/9/IP/J90IE4XSE3FE4X0ICOIRCS.uasset new file mode 100644 index 0000000..8232c3e Binary files /dev/null and b/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/9/IP/J90IE4XSE3FE4X0ICOIRCS.uasset differ diff --git a/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/9/JB/H0RUN22EVL7XB9VB2ER1H7.uasset b/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/9/JB/H0RUN22EVL7XB9VB2ER1H7.uasset new file mode 100644 index 0000000..11aff89 Binary files /dev/null and b/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/9/JB/H0RUN22EVL7XB9VB2ER1H7.uasset differ diff --git a/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/9/QG/6Y59M5OYWC4AWXXPXLEIRC.uasset b/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/9/QG/6Y59M5OYWC4AWXXPXLEIRC.uasset new file mode 100644 index 0000000..d2b5d38 Binary files /dev/null and b/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/9/QG/6Y59M5OYWC4AWXXPXLEIRC.uasset differ diff --git a/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/9/SI/5O6XL3B14SX0PTVHWW4LWS.uasset b/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/9/SI/5O6XL3B14SX0PTVHWW4LWS.uasset new file mode 100644 index 0000000..949a6c2 Binary files /dev/null and b/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/9/SI/5O6XL3B14SX0PTVHWW4LWS.uasset differ diff --git a/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/A/52/H24VOCTDSD46Y2HXF75WUX.uasset b/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/A/52/H24VOCTDSD46Y2HXF75WUX.uasset new file mode 100644 index 0000000..1ab2ccb Binary files /dev/null and b/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/A/52/H24VOCTDSD46Y2HXF75WUX.uasset differ diff --git a/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/A/5V/PSL98LPIVATO14M4PXAIZB.uasset b/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/A/5V/PSL98LPIVATO14M4PXAIZB.uasset new file mode 100644 index 0000000..7e5f0da Binary files /dev/null and b/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/A/5V/PSL98LPIVATO14M4PXAIZB.uasset differ diff --git a/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/A/G3/76H807U3OHV0QPU4LUPA6W.uasset b/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/A/G3/76H807U3OHV0QPU4LUPA6W.uasset new file mode 100644 index 0000000..9ea8713 Binary files /dev/null and b/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/A/G3/76H807U3OHV0QPU4LUPA6W.uasset differ diff --git a/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/A/NS/98XTK0GY1B3EWF8XJM36BO.uasset b/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/A/NS/98XTK0GY1B3EWF8XJM36BO.uasset new file mode 100644 index 0000000..bc42398 Binary files /dev/null and b/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/A/NS/98XTK0GY1B3EWF8XJM36BO.uasset differ diff --git a/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/A/VO/YHC75RQYF01QN2FP3B58R3.uasset b/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/A/VO/YHC75RQYF01QN2FP3B58R3.uasset new file mode 100644 index 0000000..e0b41c2 Binary files /dev/null and b/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/A/VO/YHC75RQYF01QN2FP3B58R3.uasset differ diff --git a/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/A/X5/Q6CFN21BSKS0DJWSZOKH8K.uasset b/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/A/X5/Q6CFN21BSKS0DJWSZOKH8K.uasset new file mode 100644 index 0000000..9f93ef9 Binary files /dev/null and b/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/A/X5/Q6CFN21BSKS0DJWSZOKH8K.uasset differ diff --git a/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/A/X5/ZBU4LD1SXU88MOY9FU4XBY.uasset b/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/A/X5/ZBU4LD1SXU88MOY9FU4XBY.uasset new file mode 100644 index 0000000..3ea5fc7 Binary files /dev/null and b/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/A/X5/ZBU4LD1SXU88MOY9FU4XBY.uasset differ diff --git a/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/B/B6/51WUNKXVC4JDJYF8JF4XHV.uasset b/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/B/B6/51WUNKXVC4JDJYF8JF4XHV.uasset new file mode 100644 index 0000000..581bf60 Binary files /dev/null and b/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/B/B6/51WUNKXVC4JDJYF8JF4XHV.uasset differ diff --git a/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/B/DX/J51NYJ1EHHCAYNHK9B78TJ.uasset b/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/B/DX/J51NYJ1EHHCAYNHK9B78TJ.uasset new file mode 100644 index 0000000..a5e8fb1 Binary files /dev/null and b/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/B/DX/J51NYJ1EHHCAYNHK9B78TJ.uasset differ diff --git a/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/B/I4/PQ1FO7X7PGPXXGLIF11XQZ.uasset b/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/B/I4/PQ1FO7X7PGPXXGLIF11XQZ.uasset new file mode 100644 index 0000000..b90ca29 Binary files /dev/null and b/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/B/I4/PQ1FO7X7PGPXXGLIF11XQZ.uasset differ diff --git a/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/B/I9/J75465ANK0Q9ZXQ5X6K8KZ.uasset b/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/B/I9/J75465ANK0Q9ZXQ5X6K8KZ.uasset new file mode 100644 index 0000000..b985fdf Binary files /dev/null and b/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/B/I9/J75465ANK0Q9ZXQ5X6K8KZ.uasset differ diff --git a/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/B/MG/XQT2WO3VYMPCRDVIMVKJH0.uasset b/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/B/MG/XQT2WO3VYMPCRDVIMVKJH0.uasset new file mode 100644 index 0000000..b3b157f Binary files /dev/null and b/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/B/MG/XQT2WO3VYMPCRDVIMVKJH0.uasset differ diff --git a/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/C/7F/BPDJQ2P3CU15QOHB85124R.uasset b/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/C/7F/BPDJQ2P3CU15QOHB85124R.uasset new file mode 100644 index 0000000..6da493c Binary files /dev/null and b/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/C/7F/BPDJQ2P3CU15QOHB85124R.uasset differ diff --git a/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/C/98/OZ6K258ZAQJNNU9FXA6UFG.uasset b/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/C/98/OZ6K258ZAQJNNU9FXA6UFG.uasset new file mode 100644 index 0000000..9b0e592 Binary files /dev/null and b/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/C/98/OZ6K258ZAQJNNU9FXA6UFG.uasset differ diff --git a/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/C/HP/2JOBERLJ9IGBTD7ZAANZK9.uasset b/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/C/HP/2JOBERLJ9IGBTD7ZAANZK9.uasset new file mode 100644 index 0000000..0b0dfbb Binary files /dev/null and b/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/C/HP/2JOBERLJ9IGBTD7ZAANZK9.uasset differ diff --git a/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/C/PN/19J6WXBPP7JA54O6BFBJWC.uasset b/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/C/PN/19J6WXBPP7JA54O6BFBJWC.uasset new file mode 100644 index 0000000..a093fba Binary files /dev/null and b/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/C/PN/19J6WXBPP7JA54O6BFBJWC.uasset differ diff --git a/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/C/R0/JINOEIJKPJ0GV821MVI2E5.uasset b/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/C/R0/JINOEIJKPJ0GV821MVI2E5.uasset new file mode 100644 index 0000000..e738bb5 Binary files /dev/null and b/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/C/R0/JINOEIJKPJ0GV821MVI2E5.uasset differ diff --git a/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/C/W6/XEAZOTYTK888Z1W6TYEMDR.uasset b/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/C/W6/XEAZOTYTK888Z1W6TYEMDR.uasset new file mode 100644 index 0000000..4ab643a Binary files /dev/null and b/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/C/W6/XEAZOTYTK888Z1W6TYEMDR.uasset differ diff --git a/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/D/3W/UCG93XNC9M6816SUQ0XGY1.uasset b/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/D/3W/UCG93XNC9M6816SUQ0XGY1.uasset new file mode 100644 index 0000000..5891c56 Binary files /dev/null and b/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/D/3W/UCG93XNC9M6816SUQ0XGY1.uasset differ diff --git a/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/D/7M/9OCS6T43ATY5MO0DJ72HJI.uasset b/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/D/7M/9OCS6T43ATY5MO0DJ72HJI.uasset new file mode 100644 index 0000000..8bcf6be Binary files /dev/null and b/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/D/7M/9OCS6T43ATY5MO0DJ72HJI.uasset differ diff --git a/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/D/AF/EJZVS28F9IVUV29H3PWEZQ.uasset b/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/D/AF/EJZVS28F9IVUV29H3PWEZQ.uasset new file mode 100644 index 0000000..49f44af Binary files /dev/null and b/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/D/AF/EJZVS28F9IVUV29H3PWEZQ.uasset differ diff --git a/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/D/FW/RL5SFC5WHU6S47JMLAJBT9.uasset b/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/D/FW/RL5SFC5WHU6S47JMLAJBT9.uasset new file mode 100644 index 0000000..2d0de68 Binary files /dev/null and b/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/D/FW/RL5SFC5WHU6S47JMLAJBT9.uasset differ diff --git a/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/D/NW/5TNPTOLFRGVAWAC1194K0N.uasset b/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/D/NW/5TNPTOLFRGVAWAC1194K0N.uasset new file mode 100644 index 0000000..8b0c4d4 Binary files /dev/null and b/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/D/NW/5TNPTOLFRGVAWAC1194K0N.uasset differ diff --git a/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/D/SD/BPASK7GJQX3C0GT4FOZGRJ.uasset b/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/D/SD/BPASK7GJQX3C0GT4FOZGRJ.uasset new file mode 100644 index 0000000..692ad94 Binary files /dev/null and b/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/D/SD/BPASK7GJQX3C0GT4FOZGRJ.uasset differ diff --git a/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/E/7G/CRYB2FV0WUKVKKRFSO26K5.uasset b/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/E/7G/CRYB2FV0WUKVKKRFSO26K5.uasset new file mode 100644 index 0000000..bc26527 Binary files /dev/null and b/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/E/7G/CRYB2FV0WUKVKKRFSO26K5.uasset differ diff --git a/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/E/P4/7VTMUE5SNNW1P9CB7A0G75.uasset b/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/E/P4/7VTMUE5SNNW1P9CB7A0G75.uasset new file mode 100644 index 0000000..096f4d5 Binary files /dev/null and b/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/E/P4/7VTMUE5SNNW1P9CB7A0G75.uasset differ diff --git a/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/E/WJ/T5B53GR28VKXDEXV3AVJQ8.uasset b/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/E/WJ/T5B53GR28VKXDEXV3AVJQ8.uasset new file mode 100644 index 0000000..543400e Binary files /dev/null and b/Content/__ExternalActors__/ZMMO/Maps/World/L_ZeusIATest/E/WJ/T5B53GR28VKXDEXV3AVJQ8.uasset differ diff --git a/Content/__ExternalObjects__/ZMMO/Maps/World/L_ZeusIATest/9/FC/NP1EQLIDLXWZ5CQZAF6FF9.uasset b/Content/__ExternalObjects__/ZMMO/Maps/World/L_ZeusIATest/9/FC/NP1EQLIDLXWZ5CQZAF6FF9.uasset new file mode 100644 index 0000000..2892ce5 Binary files /dev/null and b/Content/__ExternalObjects__/ZMMO/Maps/World/L_ZeusIATest/9/FC/NP1EQLIDLXWZ5CQZAF6FF9.uasset differ diff --git a/Content/__ExternalObjects__/ZMMO/Maps/World/L_ZeusIATest/A/YU/2J0S6FDN7X0VH1QG229LFC.uasset b/Content/__ExternalObjects__/ZMMO/Maps/World/L_ZeusIATest/A/YU/2J0S6FDN7X0VH1QG229LFC.uasset new file mode 100644 index 0000000..db24f7c Binary files /dev/null and b/Content/__ExternalObjects__/ZMMO/Maps/World/L_ZeusIATest/A/YU/2J0S6FDN7X0VH1QG229LFC.uasset differ diff --git a/Source/ZMMO/Data/UI/LoadingTypes.h b/Source/ZMMO/Data/UI/LoadingTypes.h new file mode 100644 index 0000000..11e90e9 --- /dev/null +++ b/Source/ZMMO/Data/UI/LoadingTypes.h @@ -0,0 +1,66 @@ +#pragma once + +#include "CoreMinimal.h" +#include "LoadingTypes.generated.h" + +/** + * Contexto do loading screen genérico. Cada contexto resolve um perfil + * (lista de etapas) no UZMMOLoadingProfilesDataAsset. + * + * Cliente local rastreia o progresso assinando eventos próprios + * (HandlePostLoadMap, OnPlayerSpawned, etc) — server não envia opcode de + * progresso, ver decisão em [[project_ui_loading_dynamic]]. + */ +UENUM(BlueprintType) +enum class EZMMOLoadingContext : uint8 +{ + None, + FrontEndEnteringWorld, ///< FrontEnd → mundo (handoff CharServer→WorldServer + spawn). + InGameEnteringInstance, ///< Mundo → dungeon/instance (futuro). + InGameRespawn, ///< Pós-morte, retorno ao mundo (futuro). +}; + +UENUM(BlueprintType) +enum class EZMMOLoadingStepStatus : uint8 +{ + Pending, ///< Ainda não começou. + Running, ///< Em andamento (o "barber pole"). + Done, ///< Concluída. + Failed, ///< Falhou (com reason em Status). +}; + +/** + * Uma etapa do loading. StepId é o identificador estável usado por código + * pra chamar MarkStepDone(StepId); Label é o texto localizado mostrado. + */ +USTRUCT(BlueprintType) +struct FZMMOLoadingStepDef +{ + GENERATED_BODY() + + /** Id estável (ex.: "Travel", "MapLoaded", "Connect", "Spawn"). */ + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Loading") + FName StepId; + + /** Texto localizado ("Carregando mapa...", "Conectando..."). */ + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Loading") + FText Label; +}; + +/** + * Perfil de loading por contexto. Designer edita no DA — código só + * consulta StepId pra avançar etapa. + */ +USTRUCT(BlueprintType) +struct FZMMOLoadingProfile +{ + GENERATED_BODY() + + /** Etapas em ordem. ProgressBar = #Done / #Steps. */ + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Loading") + TArray Steps; + + /** Título da tela (vazio = usa default da classe). */ + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Loading") + FText TitleOverride; +}; diff --git a/Source/ZMMO/Data/UI/UIStyleTokens.cpp b/Source/ZMMO/Data/UI/UIStyleTokens.cpp new file mode 100644 index 0000000..4f0947b --- /dev/null +++ b/Source/ZMMO/Data/UI/UIStyleTokens.cpp @@ -0,0 +1,48 @@ +#include "UIStyleTokens.h" + +#include "UObject/Class.h" +#include "UObject/UnrealType.h" + +void FUIStylePanel::PostSerialize(const FArchive& Ar) +{ + // Migração v1→v2: TMap (legacy) → TMap (atual). + // Roda no PostLoad de cada FUIStyleRow; depois do primeiro re-save no editor + // os campos *_DEPRECATED ficam vazios e este código vira no-op. + if (!Ar.IsLoading()) + { + return; + } + + const UEnum* EnumPtr = StaticEnum(); + if (EnumPtr == nullptr) + { + return; + } + + auto MigrateOne = [EnumPtr]( + TMap& Legacy, + TMap& ByName) + { + if (Legacy.IsEmpty()) + { + return; + } + for (const TPair& Pair : Legacy) + { + if (Pair.Key == EUIPanelTexture::None) + { + continue; + } + const FName KeyName(*EnumPtr->GetAuthoredNameStringByValue(static_cast(Pair.Key))); + if (!ByName.Contains(KeyName)) + { + ByName.Add(KeyName, Pair.Value); + } + } + Legacy.Reset(); + }; + + MigrateOne(PanelBackground_DEPRECATED, PanelBackgroundByName); + MigrateOne(PanelOutline_DEPRECATED, PanelOutlineByName); + MigrateOne(PanelOutlineEffect_DEPRECATED, PanelOutlineEffectByName); +} diff --git a/Source/ZMMO/Data/UI/UIStyleTokens.h b/Source/ZMMO/Data/UI/UIStyleTokens.h index 68ce067..65db1b2 100644 --- a/Source/ZMMO/Data/UI/UIStyleTokens.h +++ b/Source/ZMMO/Data/UI/UIStyleTokens.h @@ -3,6 +3,7 @@ #include "CoreMinimal.h" #include "Fonts/SlateFontInfo.h" #include "Styling/SlateBrush.h" +#include "Styling/SlateTypes.h" #include "Layout/Margin.h" #include "Templates/SubclassOf.h" #include "Engine/Texture2D.h" @@ -195,117 +196,13 @@ struct ZMMO_API FUITextStyle }; // ===================================================================== -// FUIStyleButton — padrão multi-map (Hyper-style). 3 sub-mapas com a MESMA -// chave (nome da variante: "Primary", "Secondary", "Danger", "Ghost", ou -// customizada). UUIButton_Base.Variant é FName e busca em cada sub-mapa. -// O struct FUIStyleButtonVariant (composto, FLAT) permanece com a forma -// antiga — assim o hook BP_ApplyUIStyle dos WBP_Master continua funcional. +// FUIStyleButton — padrão Hyper "Buttons": 7 sub-mapas independentes +// indexados pela MESMA chave FName de variante (autor define livremente: +// "Default", "Square", "Topbar", "Home_Main_Menu", ...). UUIButton_Base.Variant +// é FName e busca o pedaço necessário em cada sub-mapa. ButtonStyle traz +// FButtonStyle do Slate completo (Normal/Hovered/Pressed/Disabled como +// FSlateBrush, Foregrounds, Padding, Sounds). // ===================================================================== -USTRUCT(BlueprintType) -struct ZMMO_API FUIStyleButtonBackground -{ - GENERATED_BODY() - - UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|Button") - FLinearColor BgNormal = FLinearColor::Transparent; - - UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|Button") - FLinearColor BgHover = FLinearColor::Transparent; - - UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|Button") - FLinearColor BgPressed = FLinearColor::Transparent; - - UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|Button") - FLinearColor BgDisabled = FLinearColor(0.f, 0.f, 0.f, 0.42f); - - /** Textura opcional como background (Hyper-style). */ - UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|Button") - TObjectPtr BackgroundTexture = nullptr; - - UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|Button") - FMargin BackgroundMargin = FMargin(8.f); -}; - -USTRUCT(BlueprintType) -struct ZMMO_API FUIStyleButtonStroke -{ - GENERATED_BODY() - - UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|Button") - FLinearColor BorderNormal = FLinearColor::Transparent; - - UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|Button") - FLinearColor BorderHover = FLinearColor::Transparent; - - UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|Button", meta = (ClampMin = "0")) - float BorderWidth = 1.f; - - UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|Button", meta = (ClampMin = "0")) - float CornerRadius = 8.f; -}; - -USTRUCT(BlueprintType) -struct ZMMO_API FUIStyleButtonFont -{ - GENERATED_BODY() - - UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|Button") - FLinearColor TextColor = FLinearColor::White; - - /** CommonTextStyle define fonte/tamanho/peso; TextColor sobrescreve a cor. */ - UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|Button") - TSubclassOf TextStyle; -}; - -/** - * View FLAT composta da variante de botão (Background + Stroke + Font). - * Mantida com a forma antiga (BgNormal/BgHover/.../TextStyle no topo) para - * NÃO QUEBRAR o hook BP_ApplyUIStyle nos WBP_Master existentes. É construída - * pelo UUIButton_Base::ResolveVariant a partir dos 3 sub-mapas do tema. - */ -USTRUCT(BlueprintType) -struct ZMMO_API FUIStyleButtonVariant -{ - GENERATED_BODY() - - UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|Button") - FLinearColor BgNormal = FLinearColor::Transparent; - - UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|Button") - FLinearColor BgHover = FLinearColor::Transparent; - - UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|Button") - FLinearColor BgPressed = FLinearColor::Transparent; - - UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|Button") - FLinearColor BgDisabled = FLinearColor(0.f, 0.f, 0.f, 0.42f); - - UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|Button") - FLinearColor BorderNormal = FLinearColor::Transparent; - - UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|Button") - FLinearColor BorderHover = FLinearColor::Transparent; - - UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|Button") - FLinearColor TextColor = FLinearColor::White; - - UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|Button") - TSubclassOf TextStyle; - - // Campos extras vindos das sub-categorias (background texture + stroke): - UPROPERTY(BlueprintReadOnly, Category = "UI Style|Button") - TObjectPtr BackgroundTexture = nullptr; - - UPROPERTY(BlueprintReadOnly, Category = "UI Style|Button") - FMargin BackgroundMargin = FMargin(8.f); - - UPROPERTY(BlueprintReadOnly, Category = "UI Style|Button") - float BorderWidth = 1.f; - - UPROPERTY(BlueprintReadOnly, Category = "UI Style|Button") - float CornerRadius = 8.f; -}; - USTRUCT(BlueprintType) struct ZMMO_API FUIStyleButton { @@ -313,163 +210,138 @@ struct ZMMO_API FUIStyleButton FUIStyleButton() { - // ---- Backgrounds (Primary/Secondary/Danger/Ghost) ---- - FUIStyleButtonBackground PriBg; - PriBg.BgNormal = FLinearColor(FColor(201, 167, 90)); // gold - PriBg.BgHover = FLinearColor(FColor(231, 200, 115)); // gold-hi - PriBg.BgPressed = FLinearColor(FColor(201, 167, 90)); - PriBg.BgDisabled = FLinearColor(FColor(201, 167, 90, 107)); - Backgrounds.Add(TEXT("Primary"), PriBg); + // Defaults "no draw" pra não pintar branco quando o brush estiver vazio + // (regra Hyper — ver feedback_slatebrush_empty_white). Autor preenche + // no DT_UI_Styles. + FButtonStyle DefaultStyle; + DefaultStyle.Normal.DrawAs = ESlateBrushDrawType::NoDrawType; + DefaultStyle.Hovered.DrawAs = ESlateBrushDrawType::NoDrawType; + DefaultStyle.Pressed.DrawAs = ESlateBrushDrawType::NoDrawType; + DefaultStyle.Disabled.DrawAs = ESlateBrushDrawType::NoDrawType; + ButtonStyle.Add(TEXT("Default"), DefaultStyle); - FUIStyleButtonBackground SecBg; - SecBg.BgNormal = FLinearColor(FColor(17, 24, 42)); // panel-2 - SecBg.BgHover = FLinearColor(FColor(22, 32, 58)); - SecBg.BgPressed = FLinearColor(FColor(17, 24, 42)); - Backgrounds.Add(TEXT("Secondary"), SecBg); + FSlateBrush DefaultStroke; + DefaultStroke.DrawAs = ESlateBrushDrawType::NoDrawType; + ButtonStroke.Add(TEXT("Default"), DefaultStroke); - FUIStyleButtonBackground DngBg; - DngBg.BgNormal = FLinearColor(FColor(217, 92, 92, 36)); - DngBg.BgHover = FLinearColor(FColor(217, 92, 92, 66)); - DngBg.BgPressed = FLinearColor(FColor(217, 92, 92, 36)); - Backgrounds.Add(TEXT("Danger"), DngBg); + ButtonFont.Add(TEXT("Default"), FSlateFontInfo()); - FUIStyleButtonBackground GhoBg; - GhoBg.BgNormal = FLinearColor::Transparent; - GhoBg.BgHover = FLinearColor(FColor(22, 32, 58, 128)); - GhoBg.BgPressed = FLinearColor::Transparent; - Backgrounds.Add(TEXT("Ghost"), GhoBg); + ButtonTextColorRegular.Add(TEXT("Default"), FLinearColor::White); + ButtonTextColorHovered.Add(TEXT("Default"), FLinearColor::White); + ButtonTextColorSelected.Add(TEXT("Default"), FLinearColor::White); - // ---- Strokes ---- - FUIStyleButtonStroke PriS; - PriS.BorderNormal = FLinearColor(FColor(201, 167, 90)); - PriS.BorderHover = FLinearColor(FColor(231, 200, 115)); - Strokes.Add(TEXT("Primary"), PriS); - - FUIStyleButtonStroke SecS; - SecS.BorderNormal = FLinearColor(FColor(58, 75, 120)); - SecS.BorderHover = FLinearColor(FColor(78, 163, 255)); - Strokes.Add(TEXT("Secondary"), SecS); - - FUIStyleButtonStroke DngS; - DngS.BorderNormal = FLinearColor(FColor(217, 92, 92)); - DngS.BorderHover = FLinearColor(FColor(217, 92, 92)); - Strokes.Add(TEXT("Danger"), DngS); - - FUIStyleButtonStroke GhoS; - GhoS.BorderNormal = FLinearColor(FColor(58, 75, 120, 140)); - GhoS.BorderHover = FLinearColor(FColor(78, 163, 255)); - Strokes.Add(TEXT("Ghost"), GhoS); - - // ---- Fonts ---- - FUIStyleButtonFont PriF; PriF.TextColor = FLinearColor(FColor(10, 10, 18)); - Fonts.Add(TEXT("Primary"), PriF); - FUIStyleButtonFont SecF; SecF.TextColor = FLinearColor(FColor(244, 240, 230)); - Fonts.Add(TEXT("Secondary"), SecF); - FUIStyleButtonFont DngF; DngF.TextColor = FLinearColor(FColor(240, 182, 182)); - Fonts.Add(TEXT("Danger"), DngF); - FUIStyleButtonFont GhoF; GhoF.TextColor = FLinearColor(FColor(174, 182, 200)); - Fonts.Add(TEXT("Ghost"), GhoF); + ButtonIconColor.Add(TEXT("Default"), FLinearColor::White); } - /** Cores/textura de fundo por variante (chave FName). */ + /** FButtonStyle do Slate por variante: Normal/Hovered/Pressed/Disabled + * como FSlateBrush completo + Foregrounds + Padding + Sounds. */ UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|Button") - TMap Backgrounds; + TMap ButtonStyle; - /** Borda (cor, espessura, raio) por variante. */ + /** Stroke/contorno como brush independente (texturizável). Hoje o + * UUIButton_Base não consome este campo automaticamente — quem precisa + * de stroke decora o FSlateBrush dentro de ButtonStyle.Normal via + * OutlineSettings. Reservado pra evolução com 2 Borders empilhados. */ UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|Button") - TMap Strokes; + TMap ButtonStroke; - /** Tipografia (cor de texto, CommonTextStyle) por variante. */ + /** Fonte do texto por variante. */ UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|Button") - TMap Fonts; - - // Globais comuns às variantes: - UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|Button") - FMargin Padding = FMargin(22.f, 12.f); + TMap ButtonFont; UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|Button") - float MinHeight = 48.f; + TMap ButtonTextColorRegular; UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|Button") - float HoverScale = 1.02f; + TMap ButtonTextColorHovered; UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|Button") - float PressedScale = 0.98f; + TMap ButtonTextColorSelected; - /** Tamanho por silhueta (Square/Rectangle/Topbar/Header). */ UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|Button") - TMap ShapeSizes; + TMap ButtonIconColor; }; /** - * Espelha 1:1 o Struct_UI_Style_Panels do Hyper: só os dois mapas de brush - * (fundo + contorno) indexados por EUIPanelTexture. É o que o UI_Panel_Master - * do Hyper expõe editável na instância (Details). EditAnywhere/ReadWrite para - * o designer ajustar brush por widget; o tema (DT) só faz seed/fallback. + * Override manual por instância (modo bUseTheme = false). 3 brushes diretos + * pros 3 Borders do painel — designer edita o brush bruto no Details. + * Default = NoDrawType pra não pintar branco quando vazio (regra Hyper). */ USTRUCT(BlueprintType) struct ZMMO_API FUIPanelBrushSet { GENERATED_BODY() - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Panel|Texture") - TMap PanelBackground; + FUIPanelBrushSet() + { + PanelBackground.DrawAs = ESlateBrushDrawType::NoDrawType; + PanelOutline.DrawAs = ESlateBrushDrawType::NoDrawType; + PanelOutlineEffect.DrawAs = ESlateBrushDrawType::NoDrawType; + } - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Panel|Texture") - TMap PanelOutline; + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Panel|Brush") + FSlateBrush PanelBackground; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Panel|Brush") + FSlateBrush PanelOutline; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Panel|Brush") + FSlateBrush PanelOutlineEffect; }; +/** + * Espelha 1:1 o Struct_UI_Style_Panels do Hyper. 3 brushes por categoria + * (FName livre): fundo, contorno e efeito sobre o contorno. Categorias são + * editadas direto no DT_UI_Styles — UUIPanel_Base resolve via dropdown + * dinâmico (meta=GetOptions). + * + * Migração: assets v1 tinham TMap; os campos + * *_DEPRECATED preservam essas keys via PropertyRedirect no DefaultEngine.ini. + * PostSerialize copia legacy→ByName no PostLoad — após re-save no editor, + * o legacy fica vazio e o asset só carrega o ByName. + */ USTRUCT(BlueprintType) struct ZMMO_API FUIStylePanel { GENERATED_BODY() - UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|Panel") - FLinearColor Bg = FLinearColor(FColor(12, 18, 34, 235)); // Panel - - UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|Panel") - FLinearColor BgRaised = FLinearColor(FColor(17, 24, 42)); // Panel2 - - UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|Panel") - FLinearColor BgSunken = FLinearColor(FColor(14, 20, 36)); // Panel3 - - UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|Panel") - FLinearColor BorderColor = FLinearColor(FColor(58, 75, 120)); - - UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|Panel") - FLinearColor BorderSoftColor = FLinearColor(FColor(58, 75, 120, 140)); - - UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|Panel") - float CornerRadius = 12.f; - - UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|Panel") - float CornerRadiusSmall = 10.f; - - UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|Panel") - float BorderWidth = 1.f; - - UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|Panel") - FMargin Padding = FMargin(30.f); - - UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|Panel") - FMargin PaddingSmall = FMargin(20.f); - - UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|Panel") - FLinearColor CardSelectedBorder = FLinearColor(FColor(201, 167, 90)); // Accent - - UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|Panel") - FLinearColor CardSelectedBg = FLinearColor(FColor(24, 32, 58)); - - /** - * Modo texturizado (espelha Struct_UI_Style_Panels do Hyper): brush por - * EUIPanelTexture. PanelBackground = fundo; PanelOutline = contorno. - * Se vazio p/ a chave, o painel usa o modo cor (RoundedBox) acima. - */ - UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|Panel|Texture") - TMap PanelBackground; + // ---- Novo (FName livre) ------------------------------------------- UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|Panel|Texture") - TMap PanelOutline; + TMap PanelBackgroundByName; + + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|Panel|Texture") + TMap PanelOutlineByName; + + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|Panel|Texture") + TMap PanelOutlineEffectByName; + + // ---- Legacy (carrega assets antigos via CoreRedirect) ------------- + // + // Não aparecem no Details (DeprecatedProperty); persistem só pra leitura + // do asset pré-migração. PostSerialize copia o conteúdo pros campos + // ByName e zera os legacy. + + UPROPERTY(meta = (DeprecatedProperty, DeprecationMessage = "Use PanelBackgroundByName (FName).")) + TMap PanelBackground_DEPRECATED; + + UPROPERTY(meta = (DeprecatedProperty, DeprecationMessage = "Use PanelOutlineByName (FName).")) + TMap PanelOutline_DEPRECATED; + + UPROPERTY(meta = (DeprecatedProperty, DeprecationMessage = "Use PanelOutlineEffectByName (FName).")) + TMap PanelOutlineEffect_DEPRECATED; + + /** Chamado automaticamente após Serialize. Migra dados legacy→ByName. */ + void PostSerialize(const FArchive& Ar); +}; + +template<> +struct TStructOpsTypeTraits : public TStructOpsTypeTraitsBase2 +{ + enum + { + WithPostSerialize = true, + }; }; USTRUCT(BlueprintType) @@ -502,29 +374,78 @@ struct ZMMO_API FUIStyleBorders float RuleSoftThickness = 1.f; }; +/** + * Variante completa de ProgressBar: materiais por layer (Fill/BG/Track/Edge/Shimmer) + * + parametros visuais (DrawStyle, CornerRadius, Border) + toggles de efeito. + * + * Material nulo = nao sobrescreve o brush do WBP (usa o que esta atribuido la). + * Aplicada pelo UUIProgressBar_Base quando bUseTheme=true via Variant FName. + */ +USTRUCT(BlueprintType) +struct ZMMO_API FUIStyleProgressBarVariant +{ + GENERATED_BODY() + + // ---- Materiais (opcionais; nullptr = mantem brush do WBP) ---- + + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|ProgressBar|Materials") + TObjectPtr FillMaterial; + + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|ProgressBar|Materials") + TObjectPtr BackgroundMaterial; + + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|ProgressBar|Materials") + TObjectPtr TrackGridMaterial; + + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|ProgressBar|Materials") + TObjectPtr EdgeMaterial; + + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|ProgressBar|Materials") + TObjectPtr ShimmerMaterial; + + // ---- Style (forma + bordas) ---- + + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|ProgressBar|Style") + EUIProgressBarStyle DrawStyle = EUIProgressBarStyle::Rounded; + + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|ProgressBar|Style", + meta = (ClampMin = "0", ClampMax = "100")) + float CornerRadius = 8.f; + + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|ProgressBar|Style") + bool bShowBorder = false; + + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|ProgressBar|Style") + FLinearColor BorderColour = FLinearColor(0.f, 0.f, 0.f, 0.8f); + + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|ProgressBar|Style", + meta = (ClampMin = "0", ClampMax = "10")) + float BorderWidth = 1.f; + + // ---- Toggles de efeito ---- + + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|ProgressBar|Effects") + bool bShowTrackGrid = false; + + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|ProgressBar|Effects") + bool bShowEdge = false; + + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|ProgressBar|Effects") + bool bShowShimmer = false; +}; + +/** + * ProgressBar — padrao multi-map Hyper-style. Variantes (HP/MP/XP/Cast/...) sao + * editadas direto no DT_UI_Styles; cada UUIProgressBar_Base referencia uma key + * FName (dropdown via meta=GetOptions resolve esse mapa em runtime+editor). + */ USTRUCT(BlueprintType) struct ZMMO_API FUIStyleProgressBar { GENERATED_BODY() UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|ProgressBar") - FLinearColor TrackColor = FLinearColor(FColor(14, 20, 36)); // Panel3 - - UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|ProgressBar") - FLinearColor BorderColor = FLinearColor(FColor(58, 75, 120, 140)); - - UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|ProgressBar") - float Height = 10.f; - - UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|ProgressBar") - float CornerRadius = 6.f; - - UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|ProgressBar") - float BorderWidth = 1.f; - - /** Cor de preenchimento por tipo (Health/Mana/XP...). */ - UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|ProgressBar") - TMap FillByType; + TMap Variants; }; USTRUCT(BlueprintType) diff --git a/Source/ZMMO/Data/UI/UIStyleTypes.h b/Source/ZMMO/Data/UI/UIStyleTypes.h index 826a9cd..029850e 100644 --- a/Source/ZMMO/Data/UI/UIStyleTypes.h +++ b/Source/ZMMO/Data/UI/UIStyleTypes.h @@ -66,6 +66,7 @@ enum class EUIPanelTexture : uint8 Rectangle_Vertical_03 UMETA(DisplayName = "Rectangle Vertical 03"), Rectangle_Vertical_04 UMETA(DisplayName = "Rectangle Vertical 04"), Rectangle_Square_01 UMETA(DisplayName = "Rectangle Square 01"), + Rectangle_Square_Gradient_01 UMETA(DisplayName = "Rectangle Square Gradient 01"), Vertical_Footer_01 UMETA(DisplayName = "Vertical Footer 01") }; @@ -80,6 +81,16 @@ enum class EUIProgressBarType : uint8 Generic UMETA(DisplayName = "Generic") }; +/** Preset que controla Brush.DrawAs em todos os UImages da ProgressBar. */ +UENUM(BlueprintType) +enum class EUIProgressBarStyle : uint8 +{ + /** DrawAs=Image. Sem mascara nos cantos. */ + Box UMETA(DisplayName = "Box"), + /** DrawAs=RoundedBox. Cantos arredondados via Slate nativo (pixel-perfect). */ + Rounded UMETA(DisplayName = "Rounded"), +}; + UENUM(BlueprintType) enum class EUIHeaderType : uint8 { diff --git a/Source/ZMMO/Game/Entity/ZMMOPlayerCharacter.cpp b/Source/ZMMO/Game/Entity/ZMMOPlayerCharacter.cpp index a4a2832..5cbe9f8 100644 --- a/Source/ZMMO/Game/Entity/ZMMOPlayerCharacter.cpp +++ b/Source/ZMMO/Game/Entity/ZMMOPlayerCharacter.cpp @@ -18,6 +18,7 @@ #include "UObject/ConstructorHelpers.h" #include "ZMMO.h" #include "ZMMOAttributeComponent.h" +#include "ZMMOPlayerState.h" #include "ZMMOWorldSubsystem.h" #include "ZeusNetworkSubsystem.h" #include "UI/FrontEnd/UIFrontEndFlowSubsystem.h" @@ -278,6 +279,7 @@ void AZMMOPlayerCharacter::BindZeusSpawnDelegate() } ZeusNetwork->OnPlayerSpawned.AddDynamic(this, &AZMMOPlayerCharacter::HandleZeusPlayerSpawned); + ZeusNetwork->OnCharInfoReceived.AddDynamic(this, &AZMMOPlayerCharacter::HandleZeusCharInfo); bSpawnDelegateBound = true; } @@ -289,6 +291,7 @@ void AZMMOPlayerCharacter::UnbindZeusSpawnDelegate() return; } ZeusNetwork->OnPlayerSpawned.RemoveDynamic(this, &AZMMOPlayerCharacter::HandleZeusPlayerSpawned); + ZeusNetwork->OnCharInfoReceived.RemoveDynamic(this, &AZMMOPlayerCharacter::HandleZeusCharInfo); bSpawnDelegateBound = false; } @@ -299,7 +302,7 @@ void AZMMOPlayerCharacter::TryRegisterLocalEntityFromCachedSpawn() return; } - int32 CachedEntityId = 0; + int64 CachedEntityId = 0; FVector CachedPosCm = FVector::ZeroVector; float CachedYawDeg = 0.0f; int64 CachedServerTimeMs = 0; @@ -307,9 +310,13 @@ void AZMMOPlayerCharacter::TryRegisterLocalEntityFromCachedSpawn() { HandleLocalSpawnReady(CachedEntityId, CachedPosCm, CachedYawDeg, CachedServerTimeMs); } + + // Race fix: o S_CHAR_INFO chega ANTES de S_SPAWN_PLAYER mas o pawn pode + // nao existir ainda (OpenLevel em andamento). Aplica o ultimo cacheado. + TryApplyCachedCharInfo(); } -void AZMMOPlayerCharacter::HandleZeusPlayerSpawned(const int32 InEntityId, const bool bIsLocal, +void AZMMOPlayerCharacter::HandleZeusPlayerSpawned(const int64 InEntityId, const bool bIsLocal, const FVector PosCm, const float YawDeg, const int64 ServerTimeMs) { if (!bIsLocal) @@ -321,7 +328,7 @@ void AZMMOPlayerCharacter::HandleZeusPlayerSpawned(const int32 InEntityId, const HandleLocalSpawnReady(InEntityId, PosCm, YawDeg, ServerTimeMs); } -void AZMMOPlayerCharacter::HandleLocalSpawnReady(const int32 InEntityId, const FVector PosCm, +void AZMMOPlayerCharacter::HandleLocalSpawnReady(const int64 InEntityId, const FVector PosCm, const float YawDeg, const int64 ServerTimeMs) { if (InEntityId == 0) @@ -329,45 +336,94 @@ void AZMMOPlayerCharacter::HandleLocalSpawnReady(const int32 InEntityId, const F return; } - const int64 EntityIdAsInt64 = static_cast(InEntityId); - if (ZMMOEntityId == EntityIdAsInt64) + // PR-HANDOFF-007 — InEntityId é int64 (era int32) + if (ZMMOEntityId == InEntityId) { return; } - ZMMOEntityId = EntityIdAsInt64; + ZMMOEntityId = InEntityId; UE_LOG(LogZMMOPlayer, Log, - TEXT("AZMMOPlayerCharacter: local spawn captured EntityId=%d pos=(%s) yaw=%.1f t=%lld"), + TEXT("AZMMOPlayerCharacter: local spawn captured EntityId=%lld pos=(%s) yaw=%.1f t=%lld"), InEntityId, *PosCm.ToString(), YawDeg, ServerTimeMs); if (UWorld* World = GetWorld()) { if (UZMMOWorldSubsystem* WorldSubsystem = World->GetSubsystem()) { - WorldSubsystem->RegisterLocalEntity(EntityIdAsInt64, this); + WorldSubsystem->RegisterLocalEntity(InEntityId, this); } } - // Seed do EntityId no AttributeComponent do PlayerState. O componente - // vive no AZMMOPlayerState via Component Registry — busca via PlayerState - // (sobrevive ao despawn do Pawn). S_ATTRIBUTE_SNAPSHOT_FULL chega via - // UDP e e' rouado pelo ZMMOAttributeNetworkHandler via lookup por - // EntityId no PlayerArray do GameState. + // Identidade publica (EntityId) + seed no AttributeComponent. CharName/Guild + // vem separado via S_CHAR_INFO -> HandleZeusCharInfo. AttributeComponent vive + // como subobject e recebe seed pra que o ZMMOAttributeNetworkHandler consiga + // rotear o snapshot por EntityId desde o primeiro pacote. if (APlayerState* PS = GetPlayerState()) { + if (AZMMOPlayerState* ZMMOPs = Cast(PS)) + { + ZMMOPs->SetPublicIdentity(InEntityId, /*CharId*/ FString(), + /*BaseLevel*/ 1, /*ClassId*/ 0); + } if (UZMMOAttributeComponent* AttrComp = PS->FindComponentByClass()) { AttrComp->SeedEntityId(InEntityId); } } + // Aplica char info cacheada (caso S_CHAR_INFO tenha chegado antes do PS existir). + TryApplyCachedCharInfo(); + // UI in-game e' responsabilidade do AZMMOHUD (GameMode.HUDClass), nao do // Pawn. AHUD::BeginPlay chama UUIInGameFlowSubsystem::StartInGame, que // pushea WBP_HUD em UI.Layer.Game. UZMMOHudWidget::NativeOnActivated auto-binda // no UZMMOAttributeComponent via PlayerState. } +void AZMMOPlayerCharacter::HandleZeusCharInfo(const int64 InEntityId, const FString& CharName, const FString& GuildName) +{ + UE_LOG(LogZMMOPlayer, Log, + TEXT("AZMMOPlayerCharacter: S_CHAR_INFO recebido EntityId=%lld name=%s guild=%s"), + InEntityId, *CharName, *GuildName); + + APlayerState* PS = GetPlayerState(); + if (!PS) + { + // Race fix: pawn pode nao ter PlayerState ainda. Subsystem ja cacheou + // (LastLocalCharInfoCache); TryApplyCachedCharInfo aplica em + // HandleLocalSpawnReady ou em outra oportunidade. + return; + } + if (AZMMOPlayerState* ZMMOPs = Cast(PS)) + { + ZMMOPs->SetCharInfo(CharName, GuildName); + } +} + +void AZMMOPlayerCharacter::TryApplyCachedCharInfo() +{ + if (!ZeusNetwork) + { + return; + } + int64 CachedEntityId = 0; + FString CachedCharName; + FString CachedGuildName; + if (!ZeusNetwork->TryGetLastLocalCharInfo(CachedEntityId, CachedCharName, CachedGuildName)) + { + return; + } + if (APlayerState* PS = GetPlayerState()) + { + if (AZMMOPlayerState* ZMMOPs = Cast(PS)) + { + ZMMOPs->SetCharInfo(CachedCharName, CachedGuildName); + } + } +} + void AZMMOPlayerCharacter::FlushInputAxisToServer(const float DeltaSeconds) { ResolveZeusNetworkSubsystem(); diff --git a/Source/ZMMO/Game/Entity/ZMMOPlayerCharacter.h b/Source/ZMMO/Game/Entity/ZMMOPlayerCharacter.h index dc6d166..40bd923 100644 --- a/Source/ZMMO/Game/Entity/ZMMOPlayerCharacter.h +++ b/Source/ZMMO/Game/Entity/ZMMOPlayerCharacter.h @@ -133,11 +133,18 @@ private: /** * Memoriza `EntityId` autoritativo e regista o pawn no `UZMMOWorldSubsystem` * para o registry partilhado por proxies remotos (filtra snapshots locais). + * Metadados publicos do char (Name/Guild) chegam via S_CHAR_INFO em + * HandleZeusCharInfo (servidor envia ANTES de S_SPAWN_PLAYER). */ - void HandleLocalSpawnReady(int32 InEntityId, FVector PosCm, float YawDeg, int64 ServerTimeMs); + void HandleLocalSpawnReady(int64 InEntityId, FVector PosCm, float YawDeg, int64 ServerTimeMs); UFUNCTION() - void HandleZeusPlayerSpawned(int32 InEntityId, bool bIsLocal, FVector PosCm, float YawDeg, int64 ServerTimeMs); + void HandleZeusPlayerSpawned(int64 InEntityId, bool bIsLocal, FVector PosCm, float YawDeg, int64 ServerTimeMs); + + UFUNCTION() + void HandleZeusCharInfo(int64 InEntityId, const FString& CharName, const FString& GuildName); + + void TryApplyCachedCharInfo(); UPROPERTY() TObjectPtr ZeusNetwork; diff --git a/Source/ZMMO/Game/Modes/ZMMOPlayerState.cpp b/Source/ZMMO/Game/Modes/ZMMOPlayerState.cpp index e7cfda9..4fb5a2d 100644 --- a/Source/ZMMO/Game/Modes/ZMMOPlayerState.cpp +++ b/Source/ZMMO/Game/Modes/ZMMOPlayerState.cpp @@ -2,6 +2,7 @@ #include "Components/ActorComponent.h" #include "Misc/ConfigCacheIni.h" +#include "Net/UnrealNetwork.h" #include "ZMMO.h" AZMMOPlayerState::AZMMOPlayerState() @@ -72,6 +73,17 @@ AZMMOPlayerState::AZMMOPlayerState() } } +void AZMMOPlayerState::GetLifetimeReplicatedProps(TArray& OutLifetimeProps) const +{ + Super::GetLifetimeReplicatedProps(OutLifetimeProps); + DOREPLIFETIME(AZMMOPlayerState, ZMMOEntityId); + DOREPLIFETIME(AZMMOPlayerState, CharId); + DOREPLIFETIME(AZMMOPlayerState, CharName); + DOREPLIFETIME(AZMMOPlayerState, BaseLevel); + DOREPLIFETIME(AZMMOPlayerState, ClassId); + DOREPLIFETIME(AZMMOPlayerState, GuildName); +} + void AZMMOPlayerState::SetPublicIdentity(int64 InEntityId, const FString& InCharId, int32 InBaseLevel, int32 InClassId) { @@ -80,3 +92,9 @@ void AZMMOPlayerState::SetPublicIdentity(int64 InEntityId, const FString& InChar BaseLevel = InBaseLevel; ClassId = InClassId; } + +void AZMMOPlayerState::SetCharInfo(const FString& InCharName, const FString& InGuildName) +{ + CharName = InCharName; + GuildName = InGuildName; +} diff --git a/Source/ZMMO/Game/Modes/ZMMOPlayerState.h b/Source/ZMMO/Game/Modes/ZMMOPlayerState.h index d7be27f..1d027f0 100644 --- a/Source/ZMMO/Game/Modes/ZMMOPlayerState.h +++ b/Source/ZMMO/Game/Modes/ZMMOPlayerState.h @@ -5,6 +5,7 @@ #include "Templates/SubclassOf.h" #include "ZMMOPlayerState.generated.h" +class FLifetimeProperty; class UActorComponent; /** @@ -52,36 +53,55 @@ public: AZMMOPlayerState(); // === Identidade publica === + // + // Todos os campos publicos sao Replicated — hoje o cliente UE5 roda em + // Standalone (servidor C++ standalone, sem replicacao UE5 ativa), entao + // a marca nao tem efeito em runtime; deixa a classe pronta caso o projeto + // passe a usar dedicated UE5 no futuro. /** EntityId autoritativo do server. 0 ate o spawn local confirmar. */ - UPROPERTY(VisibleInstanceOnly, BlueprintReadOnly, Category = "ZMMO|Identity") + UPROPERTY(Replicated, VisibleInstanceOnly, BlueprintReadOnly, Category = "ZMMO|Identity") int64 ZMMOEntityId = 0; /** CharId (BIGINT no DB; FString pra preservar 64 bits em Blueprint). */ - UPROPERTY(VisibleInstanceOnly, BlueprintReadOnly, Category = "ZMMO|Identity") + UPROPERTY(Replicated, VisibleInstanceOnly, BlueprintReadOnly, Category = "ZMMO|Identity") FString CharId; + /** Nome do char vindo do DB (`characters.name`). Setado por + * AZMMOPlayerCharacter::HandleLocalSpawnReady com o payload do + * S_SPAWN_PLAYER. UI prefere isto a APlayerState::GetPlayerName(). */ + UPROPERTY(Replicated, VisibleInstanceOnly, BlueprintReadOnly, Category = "ZMMO|Identity") + FString CharName; + // === Progressao publica === - UPROPERTY(VisibleInstanceOnly, BlueprintReadOnly, Category = "ZMMO|Progress") + UPROPERTY(Replicated, VisibleInstanceOnly, BlueprintReadOnly, Category = "ZMMO|Progress") int32 BaseLevel = 1; - UPROPERTY(VisibleInstanceOnly, BlueprintReadOnly, Category = "ZMMO|Progress") + UPROPERTY(Replicated, VisibleInstanceOnly, BlueprintReadOnly, Category = "ZMMO|Progress") int32 ClassId = 0; // === Guild (futuro) === - UPROPERTY(VisibleInstanceOnly, BlueprintReadOnly, Category = "ZMMO|Guild") + UPROPERTY(Replicated, VisibleInstanceOnly, BlueprintReadOnly, Category = "ZMMO|Guild") FString GuildName; + virtual void GetLifetimeReplicatedProps(TArray& OutLifetimeProps) const override; + // === Helpers === - /// Atualizado quando S_ATTRIBUTE_SNAPSHOT_FULL chega — UZMMOHudWidget chama - /// isso pra refletir os dados publicos do snapshot no PlayerState. + /// Setado por AZMMOPlayerCharacter::HandleLocalSpawnReady (pose/EntityId) + /// e atualizado por handlers de snapshot/level up para refletir dados + /// publicos do char no PlayerState. UFUNCTION(BlueprintCallable, Category = "ZMMO|Identity") void SetPublicIdentity(int64 InEntityId, const FString& InCharId, int32 InBaseLevel, int32 InClassId); + /// Setado pelo handler do S_CHAR_INFO (chega antes de S_SPAWN_PLAYER) — + /// metadados publicos do char vindos do servidor. + UFUNCTION(BlueprintCallable, Category = "ZMMO|Identity") + void SetCharInfo(const FString& InCharName, const FString& InGuildName); + /// Template helper: PS->GetZMMOComponent() template T* GetZMMOComponent() const diff --git a/Source/ZMMO/Game/Network/ZMMOWorldSubsystem.cpp b/Source/ZMMO/Game/Network/ZMMOWorldSubsystem.cpp index cf72d08..25d3021 100644 --- a/Source/ZMMO/Game/Network/ZMMOWorldSubsystem.cpp +++ b/Source/ZMMO/Game/Network/ZMMOWorldSubsystem.cpp @@ -42,7 +42,7 @@ void UZMMOWorldSubsystem::OnWorldBeginPlay(UWorld& InWorld) // esta pronto (GameMode ativo, WorldPartition cells inicializadas). int32 ReplayCount = 0; ZeusNet->ForEachPendingRemoteSpawn( - [this, &ReplayCount](const int32 EntityId, const FVector PosCm, const float YawDeg, const int64 ServerTimeMs) + [this, &ReplayCount](const int64 EntityId, const FVector PosCm, const float YawDeg, const int64 ServerTimeMs) { HandlePlayerSpawned(EntityId, /*bIsLocal=*/false, PosCm, YawDeg, ServerTimeMs); ++ReplayCount; @@ -85,7 +85,7 @@ void UZMMOWorldSubsystem::RegisterLocalEntity(const int64 EntityId, AActor* Loca EntityId, *GetNameSafe(LocalActor)); } -void UZMMOWorldSubsystem::HandlePlayerSpawned(const int32 EntityId, const bool bIsLocal, +void UZMMOWorldSubsystem::HandlePlayerSpawned(const int64 EntityId, const bool bIsLocal, const FVector PosCm, const float YawDeg, const int64 ServerTimeMs) { if (EntityId == 0) @@ -104,7 +104,10 @@ void UZMMOWorldSubsystem::HandlePlayerSpawned(const int32 EntityId, const bool b if (RemoteEntities.Contains(EntityId)) { - // Ja existe um proxy: reactivar e re-snap. + // Ja existe um proxy: tenta reactivar e re-snap. Se o weak ptr ficou + // stale (actor coletado pelo GC, stream-out do World Partition, etc.), + // remove a entry orfa e cai no caminho de spawn novo — evita SPAWN + // silenciosamente ignorado depois de oscilacoes na AOI. if (AActor* Existing = RemoteEntities[EntityId].Get()) { if (IZMMOEntityInterface* AsEntity = Cast(Existing)) @@ -112,8 +115,12 @@ void UZMMOWorldSubsystem::HandlePlayerSpawned(const int32 EntityId, const bool b AsEntity->SetEntityRelevant(true); } Existing->SetActorLocationAndRotation(PosCm, FRotator(0.0f, YawDeg, 0.0f)); + UE_LOG(LogZMMO, Verbose, TEXT("ZMMOWorldSubsystem: re-snap EntityId=%d"), EntityId); + return; } - return; + // weak ptr stale — limpa entry e cai pro spawn novo abaixo. + RemoteEntities.Remove(EntityId); + UE_LOG(LogZMMO, Log, TEXT("ZMMOWorldSubsystem: stale entry para EntityId=%d, respawnando"), EntityId); } UWorld* World = GetWorld(); @@ -158,7 +165,7 @@ void UZMMOWorldSubsystem::HandlePlayerSpawned(const int32 EntityId, const bool b EntityId, *PosCm.ToString(), YawDeg, ServerTimeMs); } -void UZMMOWorldSubsystem::HandlePlayerDespawned(const int32 EntityId) +void UZMMOWorldSubsystem::HandlePlayerDespawned(const int64 EntityId) { const TWeakObjectPtr* Entry = RemoteEntities.Find(EntityId); if (!Entry) @@ -166,18 +173,26 @@ void UZMMOWorldSubsystem::HandlePlayerDespawned(const int32 EntityId) return; } + // Destruir o actor pra valer + remover do mapa. Antes, despawn apenas + // fazia SetActorHiddenInGame(true) via SetEntityRelevant(false), o que + // deixava o entry no mapa apontando pra um actor escondido. SPAWN + // subsequente do mesmo entityId era detectado como "duplicate" e nada + // acontecia — pawn ficava invisivel ate' o weak ptr ser GC'd. Bug + // confirmado nos logs do AOI ao oscilar entrada/saida da ZI/ZD. if (AActor* Actor = Entry->Get()) { if (IZMMOEntityInterface* AsEntity = Cast(Actor)) { AsEntity->SetEntityRelevant(false); } + Actor->Destroy(); } + RemoteEntities.Remove(EntityId); - UE_LOG(LogZMMO, Log, TEXT("ZMMOWorldSubsystem: despawn EntityId=%d"), EntityId); + UE_LOG(LogZMMO, Log, TEXT("ZMMOWorldSubsystem: despawn EntityId=%d (destroyed)"), EntityId); } -void UZMMOWorldSubsystem::HandlePlayerStateUpdate(const int32 EntityId, const int32 InputSeq, +void UZMMOWorldSubsystem::HandlePlayerStateUpdate(const int64 EntityId, const int32 InputSeq, const FVector PosCm, const FVector VelCmS, const bool bGrounded, const int64 ServerTimeMs) { if (EntityId == 0) diff --git a/Source/ZMMO/Game/Network/ZMMOWorldSubsystem.h b/Source/ZMMO/Game/Network/ZMMOWorldSubsystem.h index 44edfcf..00ae7ce 100644 --- a/Source/ZMMO/Game/Network/ZMMOWorldSubsystem.h +++ b/Source/ZMMO/Game/Network/ZMMOWorldSubsystem.h @@ -72,13 +72,13 @@ protected: private: UFUNCTION() - void HandlePlayerSpawned(int32 EntityId, bool bIsLocal, FVector PosCm, float YawDeg, int64 ServerTimeMs); + void HandlePlayerSpawned(int64 EntityId, bool bIsLocal, FVector PosCm, float YawDeg, int64 ServerTimeMs); UFUNCTION() - void HandlePlayerDespawned(int32 EntityId); + void HandlePlayerDespawned(int64 EntityId); UFUNCTION() - void HandlePlayerStateUpdate(int32 EntityId, int32 InputSeq, FVector PosCm, FVector VelCmS, bool bGrounded, int64 ServerTimeMs); + void HandlePlayerStateUpdate(int64 EntityId, int32 InputSeq, FVector PosCm, FVector VelCmS, bool bGrounded, int64 ServerTimeMs); UClass* ResolveActorClass(EZMMOEntityType EntityType) const; UZeusNetworkSubsystem* ResolveZeusNetworkSubsystem() const; diff --git a/Source/ZMMO/Game/UI/Common/UIDraggableWindow_Base.cpp b/Source/ZMMO/Game/UI/Common/UIDraggableWindow_Base.cpp new file mode 100644 index 0000000..a74fe50 --- /dev/null +++ b/Source/ZMMO/Game/UI/Common/UIDraggableWindow_Base.cpp @@ -0,0 +1,131 @@ +#include "UIDraggableWindow_Base.h" + +#include "Components/Border.h" +#include "Engine/Engine.h" +#include "Engine/GameViewportClient.h" +#include "Input/Reply.h" +#include "UIWindowSaveGame.h" + +void UUIDraggableWindow_Base::NativeOnActivated() +{ + Super::NativeOnActivated(); + ApplySavedPosition(); + // Validacao usa translation + viewport size, ambos disponiveis ja' aqui + // (sem precisar do CachedGeometry/layout pass). + ValidatePositionInViewport(); +} + +void UUIDraggableWindow_Base::ApplySavedPosition() +{ + const FName Id = GetWindowId(); + if (Id.IsNone()) return; + + UZMMOUISaveGame* Save = UZMMOUISaveGame::LoadOrCreate(); + FVector2D Pos; + if (Save && Save->TryGetWindowPosition(Id, Pos)) + { + SetRenderTranslation(Pos); + } +} + +void UUIDraggableWindow_Base::PersistCurrentPosition() +{ + const FName Id = GetWindowId(); + if (Id.IsNone()) return; + + UZMMOUISaveGame* Save = UZMMOUISaveGame::LoadOrCreate(); + if (!Save) return; + Save->SetWindowPosition(Id, GetRenderTransform().Translation); + UZMMOUISaveGame::SaveNow(Save); +} + +bool UUIDraggableWindow_Base::IsDragHandleHit(const FPointerEvent& MouseEvent) const +{ + if (!Border_DragHandle) return false; + const FGeometry HandleGeom = Border_DragHandle->GetCachedGeometry(); + return HandleGeom.IsUnderLocation(MouseEvent.GetScreenSpacePosition()); +} + +FReply UUIDraggableWindow_Base::NativeOnMouseButtonDown(const FGeometry& InGeometry, + const FPointerEvent& InMouseEvent) +{ + if (InMouseEvent.GetEffectingButton() == EKeys::LeftMouseButton && IsDragHandleHit(InMouseEvent)) + { + bMaybeDragging = true; + bDragging = false; + DragStartScreenPos = InMouseEvent.GetScreenSpacePosition(); + WidgetStartTranslation = GetRenderTransform().Translation; + return FReply::Handled().CaptureMouse(TakeWidget()); + } + return Super::NativeOnMouseButtonDown(InGeometry, InMouseEvent); +} + +FReply UUIDraggableWindow_Base::NativeOnMouseMove(const FGeometry& InGeometry, + const FPointerEvent& InMouseEvent) +{ + if (!bMaybeDragging && !bDragging) + { + return Super::NativeOnMouseMove(InGeometry, InMouseEvent); + } + + const FVector2D Delta = InMouseEvent.GetScreenSpacePosition() - DragStartScreenPos; + if (!bDragging && Delta.SizeSquared() < DragStartThresholdPx * DragStartThresholdPx) + { + return FReply::Handled(); + } + bDragging = true; + SetRenderTranslation(WidgetStartTranslation + Delta); + return FReply::Handled(); +} + +FReply UUIDraggableWindow_Base::NativeOnMouseButtonUp(const FGeometry& InGeometry, + const FPointerEvent& InMouseEvent) +{ + if (InMouseEvent.GetEffectingButton() == EKeys::LeftMouseButton && (bMaybeDragging || bDragging)) + { + const bool bWasRealDrag = bDragging; + bMaybeDragging = false; + bDragging = false; + if (bWasRealDrag) + { + PersistCurrentPosition(); + } + return FReply::Handled().ReleaseMouseCapture(); + } + return Super::NativeOnMouseButtonUp(InGeometry, InMouseEvent); +} + +void UUIDraggableWindow_Base::NativeOnMouseLeave(const FPointerEvent& InMouseEvent) +{ + Super::NativeOnMouseLeave(InMouseEvent); +} + +void UUIDraggableWindow_Base::ValidatePositionInViewport() +{ + if (!GEngine || !GEngine->GameViewport) return; + FVector2D ViewportSize; + GEngine->GameViewport->GetViewportSize(ViewportSize); + if (ViewportSize.IsNearlyZero()) return; + + // RenderTransform.Translation esta em coordenadas de viewport (pixels), + // independente de DPI/offset do editor — comparacao direta funciona em + // PIE e em build standalone. Assume slot centralizado (default UMG + // stack); meio-viewport e' o quanto a translation pode crescer antes + // da janela sair completamente. + const FVector2D Tr = GetRenderTransform().Translation; + const float HalfX = ViewportSize.X * 0.5f; + const float HalfY = ViewportSize.Y * 0.5f; + + const bool bOutOfBounds = + FMath::Abs(Tr.X) > HalfX - MinVisiblePixels || + FMath::Abs(Tr.Y) > HalfY - MinVisiblePixels; + + UE_LOG(LogTemp, Verbose, TEXT("DraggableWindow::Validate translation=(%.0f,%.0f) viewport=(%.0f,%.0f) outOfBounds=%d"), + Tr.X, Tr.Y, ViewportSize.X, ViewportSize.Y, bOutOfBounds ? 1 : 0); + + if (bOutOfBounds) + { + SetRenderTranslation(FVector2D::ZeroVector); + PersistCurrentPosition(); + } +} diff --git a/Source/ZMMO/Game/UI/Common/UIDraggableWindow_Base.h b/Source/ZMMO/Game/UI/Common/UIDraggableWindow_Base.h new file mode 100644 index 0000000..a24db37 --- /dev/null +++ b/Source/ZMMO/Game/UI/Common/UIDraggableWindow_Base.h @@ -0,0 +1,90 @@ +#pragma once + +#include "CoreMinimal.h" +#include "CommonActivatableWidget.h" +#include "UIDraggableWindow_Base.generated.h" + +class UBorder; +class UZMMOUISaveGame; + +/** + * Base de janelas in-game movel + persistente. Subclasse define WindowId + * (FName estavel) via override de GetWindowId(); a Base cuida do drag + * (RenderTransform.Translation) e da persistencia (UZMMOUISaveGame). + * + * Convencao do WBP: + * - Bind opcional `Border_DragHandle` cobrindo a area arrastavel (header). + * Sem ele, a janela nao captura drag (vira static). + * - `Border_DragHandle->Visibility = Visible` (precisa receber HitTest). + * + * Por que RenderTransform e nao Slot.Position? + * - Funciona em qualquer layout slot (Overlay, VBox, Canvas), nao so + * CanvasPanelSlot. + * - Nao polui o asset (translation e' transient/visual, nao layout-layout). + * + * Persistencia: na primeira ativacao, carrega a posicao salva do WindowId; + * ao soltar o drag, grava e salva o slot. + */ +UCLASS(Abstract, Blueprintable) +class ZMMO_API UUIDraggableWindow_Base : public UCommonActivatableWidget +{ + GENERATED_BODY() + +public: + /** + * Identificador estavel da janela (chave no save). Subclasse OBRIGADA + * a sobrescrever — default NAME_None desativa persistencia. + */ + virtual FName GetWindowId() const { return NAME_None; } + +protected: + virtual void NativeOnActivated() override; + virtual FReply NativeOnMouseButtonDown(const FGeometry& InGeometry, const FPointerEvent& InMouseEvent) override; + virtual FReply NativeOnMouseMove(const FGeometry& InGeometry, const FPointerEvent& InMouseEvent) override; + virtual FReply NativeOnMouseButtonUp(const FGeometry& InGeometry, const FPointerEvent& InMouseEvent) override; + virtual void NativeOnMouseLeave(const FPointerEvent& InMouseEvent) override; + + /** + * Define se o ponto sob o cursor pertence a area de drag. Default: usa + * Border_DragHandle (se bindado). Subclass pode override pra usar outro + * widget (ex.: Container_Header ja' existente) sem precisar adicionar + * um Border dedicado no WBP. + */ + virtual bool IsDragHandleHit(const FPointerEvent& MouseEvent) const; + + /** Aplica a posicao salva (se houver) como RenderTransform.Translation. */ + void ApplySavedPosition(); + + /** Grava a posicao atual no save e persiste no slot. */ + void PersistCurrentPosition(); + + /** + * Se a janela ficou fora do viewport (ex.: usuario arrastou pra borda e + * depois a resolucao mudou ou ele fechou e reabriu), reseta translation + * pra (0,0). Chamado no proximo tick apos Activated — cached geometry + * so' tem valor depois do primeiro layout pass. + */ + UFUNCTION() + void ValidatePositionInViewport(); + + /** Quanto da janela precisa ficar visivel pra contar como "no viewport". */ + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Drag") + float MinVisiblePixels = 50.f; + + /** + * Bind opcional do WBP. Apenas drags iniciados com o cursor SOBRE + * este border sao aceitos — bloqueia drag por clicar nos rows/botoes. + */ + UPROPERTY(BlueprintReadOnly, meta = (BindWidgetOptional), Category = "Drag") + TObjectPtr Border_DragHandle; + + /** Distancia minima do mouse pra confirmar "drag iniciado" (anti-jitter). */ + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Drag") + float DragStartThresholdPx = 4.f; + +private: + bool bMaybeDragging = false; ///< pressed mas ainda nao passou do threshold + bool bDragging = false; ///< drag confirmado + FVector2D DragStartScreenPos = FVector2D::ZeroVector; + FVector2D WidgetStartTranslation = FVector2D::ZeroVector; +}; diff --git a/Source/ZMMO/Game/UI/Common/UILoadingProfilesDataAsset.cpp b/Source/ZMMO/Game/UI/Common/UILoadingProfilesDataAsset.cpp new file mode 100644 index 0000000..87bf61a --- /dev/null +++ b/Source/ZMMO/Game/UI/Common/UILoadingProfilesDataAsset.cpp @@ -0,0 +1,10 @@ +#include "UILoadingProfilesDataAsset.h" + +FZMMOLoadingProfile UZMMOLoadingProfilesDataAsset::GetProfile(EZMMOLoadingContext Context) const +{ + if (const FZMMOLoadingProfile* Found = Profiles.Find(Context)) + { + return *Found; + } + return FZMMOLoadingProfile(); +} diff --git a/Source/ZMMO/Game/UI/Common/UILoadingProfilesDataAsset.h b/Source/ZMMO/Game/UI/Common/UILoadingProfilesDataAsset.h new file mode 100644 index 0000000..f3d83a2 --- /dev/null +++ b/Source/ZMMO/Game/UI/Common/UILoadingProfilesDataAsset.h @@ -0,0 +1,29 @@ +#pragma once + +#include "CoreMinimal.h" +#include "Engine/DataAsset.h" +#include "UI/LoadingTypes.h" +#include "UILoadingProfilesDataAsset.generated.h" + +/** + * Mapa data-driven contexto → perfil (etapas + título). Designer edita o + * asset; código consulta por EZMMOLoadingContext na hora de configurar a + * tela. Asset canônico: DA_LoadingProfiles em /Game/ZMMO/Data/UI/Loading/. + * + * Configurado em DefaultGame.ini: + * [/Script/ZMMO.UIFrontEndFlowSubsystem] + * LoadingProfilesAsset=/Game/ZMMO/Data/UI/Loading/DA_LoadingProfiles.DA_LoadingProfiles + */ +UCLASS(BlueprintType) +class ZMMO_API UZMMOLoadingProfilesDataAsset : public UDataAsset +{ + GENERATED_BODY() + +public: + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Loading") + TMap Profiles; + + /** Retorna o perfil do contexto ou um perfil vazio (sem etapas). */ + UFUNCTION(BlueprintCallable, Category = "Loading") + FZMMOLoadingProfile GetProfile(EZMMOLoadingContext Context) const; +}; diff --git a/Source/ZMMO/Game/UI/Common/UILoadingScreen_Base.cpp b/Source/ZMMO/Game/UI/Common/UILoadingScreen_Base.cpp new file mode 100644 index 0000000..cf3eb46 --- /dev/null +++ b/Source/ZMMO/Game/UI/Common/UILoadingScreen_Base.cpp @@ -0,0 +1,235 @@ +#include "UILoadingScreen_Base.h" + +#include "Components/ProgressBar.h" +#include "CommonTextBlock.h" +#include "Engine/DataTable.h" +#include "Engine/World.h" +#include "TimerManager.h" +#include "UILoadingProfilesDataAsset.h" +#include "UILoadingTipRow.h" + +void UUILoadingScreen_Base::Configure(EZMMOLoadingContext InContext, + UZMMOLoadingProfilesDataAsset* Profiles, + UDataTable* TipsTable) +{ + Context_ = InContext; + bCompleteFired_ = false; + + // Perfil (etapas + título). + Profile_ = (Profiles != nullptr) + ? Profiles->GetProfile(InContext) + : FZMMOLoadingProfile(); + + StepStatus_.Reset(); + for (const FZMMOLoadingStepDef& Step : Profile_.Steps) + { + StepStatus_.Add(Step.StepId, EZMMOLoadingStepStatus::Pending); + } + + // Pool de dicas filtrada por contexto (None = qualquer). + TipPool_.Reset(); + if (TipsTable != nullptr) + { + TipsTable->ForeachRow(TEXT("UUILoadingScreen_Base::Configure"), + [this](const FName& /*Key*/, const FZMMOLoadingTipRow& Row) + { + if (Row.Context == EZMMOLoadingContext::None || Row.Context == Context_) + { + if (!Row.Tip.IsEmpty()) + { + TipPool_.Add(Row.Tip); + } + } + }); + } + NextTipIndex_ = 0; + + RefreshUI(); + + if (IsActivated()) + { + StartTipTimer(); + } +} + +void UUILoadingScreen_Base::NativeOnActivated() +{ + Super::NativeOnActivated(); + RefreshUI(); + StartTipTimer(); +} + +void UUILoadingScreen_Base::NativeOnDeactivated() +{ + StopTipTimer(); + Super::NativeOnDeactivated(); +} + +void UUILoadingScreen_Base::MarkStepRunning(FName StepId) +{ + if (EZMMOLoadingStepStatus* Status = StepStatus_.Find(StepId)) + { + // Já Done não regride. + if (*Status != EZMMOLoadingStepStatus::Done && *Status != EZMMOLoadingStepStatus::Failed) + { + *Status = EZMMOLoadingStepStatus::Running; + RefreshUI(); + } + } +} + +void UUILoadingScreen_Base::MarkStepDone(FName StepId) +{ + if (EZMMOLoadingStepStatus* Status = StepStatus_.Find(StepId)) + { + *Status = EZMMOLoadingStepStatus::Done; + RefreshUI(); + + if (!bCompleteFired_ && AreAllStepsDone()) + { + bCompleteFired_ = true; + OnLoadingComplete.Broadcast(); + } + } +} + +void UUILoadingScreen_Base::MarkStepFailed(FName StepId, const FText& Reason) +{ + if (EZMMOLoadingStepStatus* Status = StepStatus_.Find(StepId)) + { + *Status = EZMMOLoadingStepStatus::Failed; + if (Text_Status && !Reason.IsEmpty()) + { + Text_Status->SetText(Reason); + } + RefreshUI(); + } +} + +bool UUILoadingScreen_Base::AreAllStepsDone() const +{ + if (Profile_.Steps.Num() == 0) + { + return false; + } + for (const FZMMOLoadingStepDef& Step : Profile_.Steps) + { + const EZMMOLoadingStepStatus* Status = StepStatus_.Find(Step.StepId); + if (Status == nullptr || *Status != EZMMOLoadingStepStatus::Done) + { + return false; + } + } + return true; +} + +int32 UUILoadingScreen_Base::FindStepIndex(FName StepId) const +{ + for (int32 i = 0; i < Profile_.Steps.Num(); ++i) + { + if (Profile_.Steps[i].StepId == StepId) + { + return i; + } + } + return INDEX_NONE; +} + +EZMMOLoadingStepStatus UUILoadingScreen_Base::GetStepStatus(int32 Index) const +{ + if (!Profile_.Steps.IsValidIndex(Index)) return EZMMOLoadingStepStatus::Pending; + const EZMMOLoadingStepStatus* Status = StepStatus_.Find(Profile_.Steps[Index].StepId); + return Status ? *Status : EZMMOLoadingStepStatus::Pending; +} + +FText UUILoadingScreen_Base::ComputeCurrentStatusText() const +{ + // Prioridade: Running > último Done > primeiro Pending > Status default. + int32 LastDone = INDEX_NONE; + for (int32 i = 0; i < Profile_.Steps.Num(); ++i) + { + const EZMMOLoadingStepStatus St = GetStepStatus(i); + if (St == EZMMOLoadingStepStatus::Running) + { + return Profile_.Steps[i].Label; + } + if (St == EZMMOLoadingStepStatus::Done) + { + LastDone = i; + } + } + if (LastDone != INDEX_NONE) + { + return Profile_.Steps[LastDone].Label; + } + if (Profile_.Steps.Num() > 0) + { + return Profile_.Steps[0].Label; + } + return StatusDefault; +} + +float UUILoadingScreen_Base::ComputeProgress01() const +{ + const int32 N = Profile_.Steps.Num(); + if (N == 0) return 0.f; + float Acc = 0.f; + for (int32 i = 0; i < N; ++i) + { + const EZMMOLoadingStepStatus St = GetStepStatus(i); + if (St == EZMMOLoadingStepStatus::Done) Acc += 1.f; + else if (St == EZMMOLoadingStepStatus::Running) Acc += 0.5f; + } + return FMath::Clamp(Acc / static_cast(N), 0.f, 1.f); +} + +void UUILoadingScreen_Base::RefreshUI() +{ + if (Text_Title) + { + const FText& Title = Profile_.TitleOverride.IsEmpty() ? TitleDefault : Profile_.TitleOverride; + Text_Title->SetText(Title); + } + if (Text_Status) + { + Text_Status->SetText(ComputeCurrentStatusText()); + } + if (ProgressBar_Progress) + { + ProgressBar_Progress->SetPercent(ComputeProgress01()); + } + if (Text_Tip && TipPool_.Num() > 0 && Text_Tip->GetText().IsEmpty()) + { + // Primeira dica na inicialização (timer cuida das próximas). + Text_Tip->SetText(TipPool_[0]); + NextTipIndex_ = TipPool_.Num() > 1 ? 1 : 0; + } +} + +void UUILoadingScreen_Base::RotateTip() +{ + if (!Text_Tip || TipPool_.Num() == 0) return; + Text_Tip->SetText(TipPool_[NextTipIndex_]); + NextTipIndex_ = (NextTipIndex_ + 1) % TipPool_.Num(); +} + +void UUILoadingScreen_Base::StartTipTimer() +{ + StopTipTimer(); + if (TipRotationIntervalSeconds <= 0.f || TipPool_.Num() <= 1) return; + if (UWorld* W = GetWorld()) + { + W->GetTimerManager().SetTimer(TipTimerHandle_, this, + &UUILoadingScreen_Base::RotateTip, + TipRotationIntervalSeconds, /*bLoop=*/true); + } +} + +void UUILoadingScreen_Base::StopTipTimer() +{ + if (UWorld* W = GetWorld()) + { + W->GetTimerManager().ClearTimer(TipTimerHandle_); + } + TipTimerHandle_.Invalidate(); +} diff --git a/Source/ZMMO/Game/UI/Common/UILoadingScreen_Base.h b/Source/ZMMO/Game/UI/Common/UILoadingScreen_Base.h new file mode 100644 index 0000000..401afd4 --- /dev/null +++ b/Source/ZMMO/Game/UI/Common/UILoadingScreen_Base.h @@ -0,0 +1,124 @@ +#pragma once + +#include "CoreMinimal.h" +#include "UIActivatableScreen_Base.h" +#include "UI/LoadingTypes.h" +#include "UILoadingScreen_Base.generated.h" + +class UCommonTextBlock; +class UProgressBar; +class UDataTable; +class UZMMOLoadingProfilesDataAsset; + +DECLARE_DYNAMIC_MULTICAST_DELEGATE(FZMMOOnLoadingComplete); + +/** + * Tela de loading genérica. Reusada pelo FrontEnd (handoff CharServer → + * WorldServer) e pelo InGame (entrada em instance/dungeon, respawn). Cada + * uso passa um EZMMOLoadingContext em Configure(), que resolve o perfil + * no UZMMOLoadingProfilesDataAsset (lista de etapas + título). + * + * Quem orquestra (UIFrontEndFlowSubsystem, futuro UIInGameFlowSubsystem) + * assina os próprios eventos (HandlePostLoadMap, OnPlayerSpawned, etc) e + * chama MarkStepDone(StepId) — server NÃO envia opcode de progresso (ver + * [[project_ui_loading_dynamic]]). + * + * Dismiss: a tela emite OnLoadingComplete quando todas as etapas estão + * Done. Caller decide quando popar (geralmente após pequeno fade-out). + * + * WBP concreto herda DIRETO desta classe. Subclasses C++ específicas só + * surgem quando houver necessidade real de estilo divergente. + */ +UCLASS(Abstract, Blueprintable) +class ZMMO_API UUILoadingScreen_Base : public UUIActivatableScreen_Base +{ + GENERATED_BODY() + +public: + /** + * Configura a tela: carrega o perfil do contexto, popula a pool de + * dicas filtrada e reseta estado das etapas. Pode ser chamado depois + * de NativeOnActivated (estado é reaplicado em RefreshUI). + */ + UFUNCTION(BlueprintCallable, Category = "Loading") + void Configure(EZMMOLoadingContext InContext, + UZMMOLoadingProfilesDataAsset* Profiles, + UDataTable* TipsTable); + + UFUNCTION(BlueprintCallable, Category = "Loading") + void MarkStepRunning(FName StepId); + + UFUNCTION(BlueprintCallable, Category = "Loading") + void MarkStepDone(FName StepId); + + UFUNCTION(BlueprintCallable, Category = "Loading") + void MarkStepFailed(FName StepId, const FText& Reason); + + UFUNCTION(BlueprintPure, Category = "Loading") + bool AreAllStepsDone() const; + + /** Disparado UMA vez quando todas as etapas viram Done. */ + UPROPERTY(BlueprintAssignable, Category = "Loading") + FZMMOOnLoadingComplete OnLoadingComplete; + +protected: + virtual void NativeOnActivated() override; + virtual void NativeOnDeactivated() override; + + /** Reaplica todo o estado aos widgets bindados (texto, progress, tip). */ + void RefreshUI(); + void RotateTip(); + void StartTipTimer(); + void StopTipTimer(); + + /** Acha índice da etapa por StepId; -1 se não existir. */ + int32 FindStepIndex(FName StepId) const; + + /** Status atual da etapa em [Steps_]; Pending se não rastreada. */ + EZMMOLoadingStepStatus GetStepStatus(int32 Index) const; + + /** Texto exibido em Text_Status — etapa Running atual ou última Done. */ + FText ComputeCurrentStatusText() const; + + /** Progresso 0..1: #Done / max(1,#Steps). Running conta como meio passo. */ + float ComputeProgress01() const; + + // ---- Bind widgets (BindWidgetOptional — WBP pode omitir qualquer) ---- + UPROPERTY(meta = (BindWidgetOptional)) + TObjectPtr Text_Title; + + UPROPERTY(meta = (BindWidgetOptional)) + TObjectPtr Text_Status; + + UPROPERTY(meta = (BindWidgetOptional)) + TObjectPtr ProgressBar_Progress; + + UPROPERTY(meta = (BindWidgetOptional)) + TObjectPtr Text_Tip; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Loading") + FText TitleDefault = FText::FromString(TEXT("Carregando")); + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Loading") + FText StatusDefault = FText::FromString(TEXT("Entrando no mundo...")); + + /** Intervalo entre dicas. 0 = sem rotação (mostra a primeira dica fixa). */ + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Loading") + float TipRotationIntervalSeconds = 6.f; + +private: + EZMMOLoadingContext Context_ = EZMMOLoadingContext::None; + + UPROPERTY(Transient) + FZMMOLoadingProfile Profile_; + + UPROPERTY(Transient) + TMap StepStatus_; + + UPROPERTY(Transient) + TArray TipPool_; + + int32 NextTipIndex_ = 0; + bool bCompleteFired_ = false; + FTimerHandle TipTimerHandle_; +}; diff --git a/Source/ZMMO/Game/UI/Common/UILoadingTipRow.h b/Source/ZMMO/Game/UI/Common/UILoadingTipRow.h new file mode 100644 index 0000000..2889894 --- /dev/null +++ b/Source/ZMMO/Game/UI/Common/UILoadingTipRow.h @@ -0,0 +1,30 @@ +#pragma once + +#include "CoreMinimal.h" +#include "Engine/DataTable.h" +#include "GameplayTagContainer.h" +#include "UI/LoadingTypes.h" +#include "UILoadingTipRow.generated.h" + +/** + * Row do DT_LoadingTips. Cada row = uma dica mostrada (rotaciona durante o + * loading). FText é localizável (basta extrair pelo gather de tradução do + * UE). Filtra por contexto: Context=None significa "qualquer loading". + */ +USTRUCT(BlueprintType) +struct FZMMOLoadingTipRow : public FTableRowBase +{ + GENERATED_BODY() + + /** Texto da dica (localizável). */ + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Loading") + FText Tip; + + /** Restringe a dica a um contexto. None = todos. */ + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Loading") + EZMMOLoadingContext Context = EZMMOLoadingContext::None; + + /** Tag opcional pra categorizar (PvE/PvP/Crafting...) — futuro filtro. */ + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Loading") + FGameplayTag Category; +}; diff --git a/Source/ZMMO/Game/UI/Common/UIWindowSaveGame.cpp b/Source/ZMMO/Game/UI/Common/UIWindowSaveGame.cpp new file mode 100644 index 0000000..717778d --- /dev/null +++ b/Source/ZMMO/Game/UI/Common/UIWindowSaveGame.cpp @@ -0,0 +1,43 @@ +#include "UIWindowSaveGame.h" + +#include "Kismet/GameplayStatics.h" + +const FString UZMMOUISaveGame::SlotName = TEXT("UIPrefs"); + +UZMMOUISaveGame* UZMMOUISaveGame::LoadOrCreate() +{ + if (UGameplayStatics::DoesSaveGameExist(SlotName, UserIndex)) + { + if (UZMMOUISaveGame* Loaded = Cast( + UGameplayStatics::LoadGameFromSlot(SlotName, UserIndex))) + { + return Loaded; + } + } + return Cast( + UGameplayStatics::CreateSaveGameObject(UZMMOUISaveGame::StaticClass())); +} + +void UZMMOUISaveGame::SaveNow(UZMMOUISaveGame* Save) +{ + if (Save) + { + UGameplayStatics::SaveGameToSlot(Save, SlotName, UserIndex); + } +} + +bool UZMMOUISaveGame::TryGetWindowPosition(FName WindowId, FVector2D& OutPos) const +{ + if (const FVector2D* Found = WindowPositions.Find(WindowId)) + { + OutPos = *Found; + return true; + } + OutPos = FVector2D::ZeroVector; + return false; +} + +void UZMMOUISaveGame::SetWindowPosition(FName WindowId, FVector2D Pos) +{ + WindowPositions.Add(WindowId, Pos); +} diff --git a/Source/ZMMO/Game/UI/Common/UIWindowSaveGame.h b/Source/ZMMO/Game/UI/Common/UIWindowSaveGame.h new file mode 100644 index 0000000..76e6f35 --- /dev/null +++ b/Source/ZMMO/Game/UI/Common/UIWindowSaveGame.h @@ -0,0 +1,46 @@ +#pragma once + +#include "CoreMinimal.h" +#include "GameFramework/SaveGame.h" +#include "UIWindowSaveGame.generated.h" + +/** + * Persistencia de prefs de UI por janela (posicao, e futuramente tamanho, + * estado expandido, etc). Usado por UUIDraggableWindow_Base — toda janela + * movel registra uma chave (FName WindowId) e grava aqui ao soltar o drag. + * + * Arquivo: [Project]/Saved/SaveGames/UIPrefs_0.sav (binary blob via + * UE serializer). Mesmo padrao do [[ZMMOLoginSaveGame]]. + * + * Helpers static abaixo encapsulam load-or-create do slot e Get/Set/Save + * pra que a Base nao precise repetir a cerimonia. + */ +UCLASS() +class ZMMO_API UZMMOUISaveGame : public USaveGame +{ + GENERATED_BODY() + +public: + /** Posicao normalizada (translation absoluta em pixels da viewport) por janela. */ + UPROPERTY() + TMap WindowPositions; + + static const FString SlotName; + static constexpr int32 UserIndex = 0; + + /** + * Carrega o save existente ou cria um novo (nao salva ainda). + * Sempre retorna instancia valida. + */ + static UZMMOUISaveGame* LoadOrCreate(); + + /** Salva o slot atual (sincrono — chamado on-mouse-up, custo baixo). */ + static void SaveNow(UZMMOUISaveGame* Save); + + /** Get com default — se WindowId nao foi gravado retorna FVector2D::ZeroVector. */ + UFUNCTION(BlueprintCallable, Category = "UI|Prefs") + bool TryGetWindowPosition(FName WindowId, FVector2D& OutPos) const; + + UFUNCTION(BlueprintCallable, Category = "UI|Prefs") + void SetWindowPosition(FName WindowId, FVector2D Pos); +}; diff --git a/Source/ZMMO/Game/UI/FrontEnd/CharServerOpcodes.h b/Source/ZMMO/Game/UI/FrontEnd/CharServerOpcodes.h index 3b32cf0..f609dad 100644 --- a/Source/ZMMO/Game/UI/FrontEnd/CharServerOpcodes.h +++ b/Source/ZMMO/Game/UI/FrontEnd/CharServerOpcodes.h @@ -18,10 +18,10 @@ namespace ZMMOCharOp constexpr int32 S_CHAR_AUTH_REJECT = 2002; // uint16(reason) // Listagem/criação/seleção de personagem - constexpr int32 C_CHAR_LIST_REQUEST = 2010; // uint8 hasWorldFilter [+ uint8[16] worldId] + constexpr int32 C_CHAR_LIST_REQUEST = 2010; // uint8 hasRealmFilter [+ uint8[16] realmId] constexpr int32 S_CHAR_LIST = 2011; - constexpr int32 C_CHAR_CREATE = 2020; // uint8[16] worldId + slot + ... + constexpr int32 C_CHAR_CREATE = 2020; // uint8[16] realmId + slot + ... constexpr int32 S_CHAR_CREATE_OK = 2021; constexpr int32 S_CHAR_CREATE_REJECT= 2022; @@ -37,14 +37,15 @@ namespace ZMMOCharOp constexpr int32 S_CHAR_SELECT_OK = 2041; constexpr int32 S_CHAR_SELECT_REJECT= 2042; - // Listagem de mundos (Fase 1 do ARQUITETURA_SERVER_SELECT) - constexpr int32 C_WORLD_LIST_REQUEST = 2060; // payload vazio - constexpr int32 S_WORLD_LIST = 2061; // uint16 count + entries + // Listagem de Realms (renomeado de WORLD_LIST no refator R4 — Server + // Meshing). Server-side: `Server/ZeusCharServer/src/services/realm-list.service.ts`. + constexpr int32 C_REALM_LIST_REQUEST = 2060; // payload vazio + constexpr int32 S_REALM_LIST = 2061; // uint16 count + entries /** - * Push do CharServer com update de UM mundo (state/pop/queueLen). - * Wire: string worldId + uint16 pop + uint16 queueLen + uint8 state + * Push do CharServer com update de UM Realm (state/pop/queueLen). + * Wire: string realmId + uint16 pop + uint16 queueLen + uint8 state */ - constexpr int32 S_WORLD_STATUS_UPDATE = 2062; + constexpr int32 S_REALM_STATUS_UPDATE = 2062; } /** @@ -68,11 +69,13 @@ namespace ZMMOCharRejectReason constexpr uint16 AccountLocked = 5; constexpr uint16 AccountSuspended = 6; constexpr uint16 CredentialsAuthDisabled = 7; - constexpr uint16 WorldOffline = 8; - constexpr uint16 WorldMaintenance = 9; - constexpr uint16 WorldFull = 10; - constexpr uint16 WorldRegionMismatch = 11; - constexpr uint16 WorldNotFound = 12; + constexpr uint16 RealmOffline = 8; + constexpr uint16 RealmMaintenance = 9; + constexpr uint16 RealmFull = 10; + constexpr uint16 RealmRegionMismatch = 11; + constexpr uint16 RealmNotFound = 12; + /** Posicao do char nao esta dentro de nenhuma cell do grid_layout do Realm (dead zone). */ + constexpr uint16 CharOutsideAnyCell = 13; constexpr uint16 NameInUse = 20; constexpr uint16 InvalidName = 21; constexpr uint16 SlotOccupied = 22; @@ -81,8 +84,8 @@ namespace ZMMOCharRejectReason constexpr uint16 DeleteNotScheduled = 25; } -/** Estado do mundo no wire (uint8). Espelha `WireWorldState` em CharOpcodes.ts. */ -namespace ZMMOWireWorldState +/** Estado do Realm no wire (uint8). Espelha `WireRealmState` em CharOpcodes.ts. */ +namespace ZMMOWireRealmState { constexpr uint8 Offline = 0; constexpr uint8 Online = 1; diff --git a/Source/ZMMO/Game/UI/FrontEnd/UICharacterCreatePage_Base.cpp b/Source/ZMMO/Game/UI/FrontEnd/UICharacterCreatePage_Base.cpp index e318910..ac0b166 100644 --- a/Source/ZMMO/Game/UI/FrontEnd/UICharacterCreatePage_Base.cpp +++ b/Source/ZMMO/Game/UI/FrontEnd/UICharacterCreatePage_Base.cpp @@ -55,10 +55,10 @@ void UUICharacterCreatePage_Base::SubmitCreate() UZeusCharServerSubsystem* Char = GI->GetSubsystem(); if (!Flow || !Char) return; - const FString WorldId = Flow->GetSelectedWorldId(); - if (WorldId.IsEmpty()) + const FString RealmId = Flow->GetSelectedRealmId(); + if (RealmId.IsEmpty()) { - if (Text_Error) Text_Error->SetText(FText::FromString(TEXT("Mundo nao selecionado."))); + if (Text_Error) Text_Error->SetText(FText::FromString(TEXT("Realm nao selecionado."))); return; } @@ -73,9 +73,9 @@ void UUICharacterCreatePage_Base::SubmitCreate() const uint8 SlotIdx = 0; TArray Payload; - if (!WriteUuid16(Payload, WorldId)) + if (!WriteUuid16(Payload, RealmId)) { - if (Text_Error) Text_Error->SetText(FText::FromString(TEXT("WorldId invalido."))); + if (Text_Error) Text_Error->SetText(FText::FromString(TEXT("RealmId invalido."))); return; } WriteUInt8(Payload, SlotIdx); diff --git a/Source/ZMMO/Game/UI/FrontEnd/UICharacterCreatePage_Base.h b/Source/ZMMO/Game/UI/FrontEnd/UICharacterCreatePage_Base.h index 68f3e51..084fd8a 100644 --- a/Source/ZMMO/Game/UI/FrontEnd/UICharacterCreatePage_Base.h +++ b/Source/ZMMO/Game/UI/FrontEnd/UICharacterCreatePage_Base.h @@ -19,7 +19,7 @@ DECLARE_DYNAMIC_MULTICAST_DELEGATE(FZMMOOnCharCreateCancelled); * S_CHAR_CREATE_OK/REJECT. * * Wire C_CHAR_CREATE (espelha char-create.service.ts): - * uint8[16] worldId + uint8 slot + string name + uint16 classId + * uint8[16] realmId + uint8 slot + string name + uint16 classId * + uint32 hair + uint32 hairColor + uint32 skinColor + uint8 bodyType */ UCLASS(Abstract, Blueprintable) diff --git a/Source/ZMMO/Game/UI/FrontEnd/UIFrontEndFlowSubsystem.cpp b/Source/ZMMO/Game/UI/FrontEnd/UIFrontEndFlowSubsystem.cpp index cd2393e..0c7aa00 100644 --- a/Source/ZMMO/Game/UI/FrontEnd/UIFrontEndFlowSubsystem.cpp +++ b/Source/ZMMO/Game/UI/FrontEnd/UIFrontEndFlowSubsystem.cpp @@ -5,6 +5,8 @@ #include "UIFrontEndScreenSet.h" #include "UIManagerSubsystem.h" #include "UIPrimaryGameLayout_Base.h" +#include "UI/Common/UILoadingProfilesDataAsset.h" +#include "UI/Common/UILoadingScreen_Base.h" #include "UI/UILayerTags.h" #include "ZMMOGameInstance.h" #include "ZMMOThemeSubsystem.h" @@ -17,7 +19,9 @@ #include "UObject/UObjectGlobals.h" #include "Engine/AssetManager.h" #include "Engine/StreamableManager.h" +#include "Engine/World.h" #include "Kismet/GameplayStatics.h" +#include "Misc/PackageName.h" void UUIFrontEndFlowSubsystem::Initialize(FSubsystemCollectionBase& Collection) { @@ -212,13 +216,44 @@ void UUIFrontEndFlowSubsystem::ResolveAndPushScreen(EZMMOFrontEndState State) const FSoftObjectPath Path = Soft.ToSoftObjectPath(); UAssetManager::GetStreamableManager().RequestAsyncLoad( Path, - FStreamableDelegate::CreateWeakLambda(this, [this, Soft, Layer]() + FStreamableDelegate::CreateWeakLambda(this, [this, Soft, Layer, State]() { if (UUIManagerSubsystem* M = GetUIManager()) { if (UClass* Cls = Soft.Get()) { - M->PushScreenToLayer(Layer, Cls); + UCommonActivatableWidget* Pushed = M->PushScreenToLayer(Layer, Cls); + + // Loading: configura logo após o push e marca etapa "Travel" + // (chegamos aqui via HandleServerTravelRequested → SetState). + if (State == EZMMOFrontEndState::EnteringWorld) + { + if (UUILoadingScreen_Base* Loading = Cast(Pushed)) + { + ActiveLoadingScreen = Loading; + Loading->Configure(EZMMOLoadingContext::FrontEndEnteringWorld, + GetLoadingProfiles(), GetLoadingTips()); + Loading->OnLoadingComplete.AddDynamic(this, &UUIFrontEndFlowSubsystem::HandleLoadingComplete); + Loading->MarkStepDone(TEXT("Travel")); + + // Drena steps que dispararam ANTES do widget existir + // (HandlePostLoadMap/HandlePlayerSpawned podem ter + // rodado durante o RequestAsyncLoad). Sem isso o + // loading fica eterno se o servidor for rápido demais. + // + // Snapshot via MoveTemp pra evitar invalidacao do iterator se + // outro tick mexer no TSet durante o loop: CreateWeakLambda + // garante `this` vivo, NAO garante que membros nao sejam + // realocados entre captura e execucao (crash em + // TConstSetBitIterator::FindFirstSetBit historico). + TSet StepsSnapshot = MoveTemp(PendingLoadingSteps_); + PendingLoadingSteps_.Reset(); + for (const FName StepId : StepsSnapshot) + { + Loading->MarkStepDone(StepId); + } + } + } } } })); @@ -283,6 +318,7 @@ void UUIFrontEndFlowSubsystem::BindNetwork() if (UZeusNetworkSubsystem* Zeus = GetZeusNetwork()) { Zeus->OnServerTravelRequested.AddDynamic(this, &UUIFrontEndFlowSubsystem::HandleServerTravelRequested); + Zeus->OnPlayerSpawned.AddDynamic(this, &UUIFrontEndFlowSubsystem::HandlePlayerSpawned); } bNetBound = true; } @@ -302,6 +338,7 @@ void UUIFrontEndFlowSubsystem::UnbindNetwork() if (UZeusNetworkSubsystem* Zeus = GetZeusNetwork()) { Zeus->OnServerTravelRequested.RemoveDynamic(this, &UUIFrontEndFlowSubsystem::HandleServerTravelRequested); + Zeus->OnPlayerSpawned.RemoveDynamic(this, &UUIFrontEndFlowSubsystem::HandlePlayerSpawned); } bNetBound = false; } @@ -353,24 +390,64 @@ void UUIFrontEndFlowSubsystem::RequestEnterServerSelect() } } -void UUIFrontEndFlowSubsystem::SetSelectedWorldId(const FString& WorldId) +void UUIFrontEndFlowSubsystem::SetSelectedRealmId(const FString& RealmId) { - SelectedWorldId = WorldId; - UE_LOG(LogZMMO, Log, TEXT("FrontEndFlow: mundo selecionado = %s"), *SelectedWorldId); + SelectedRealmId = RealmId; + UE_LOG(LogZMMO, Log, TEXT("FrontEndFlow: realm selecionado = %s"), *SelectedRealmId); } -void UUIFrontEndFlowSubsystem::ClearSelectedWorld() +void UUIFrontEndFlowSubsystem::ClearSelectedRealm() { - if (!SelectedWorldId.IsEmpty()) + if (!SelectedRealmId.IsEmpty()) { - UE_LOG(LogZMMO, Log, TEXT("FrontEndFlow: limpando mundo selecionado (%s)"), *SelectedWorldId); - SelectedWorldId.Reset(); + UE_LOG(LogZMMO, Log, TEXT("FrontEndFlow: limpando realm selecionado (%s)"), *SelectedRealmId); + SelectedRealmId.Reset(); } } void UUIFrontEndFlowSubsystem::HandleServerTravelRequested(const FString& MapName, const FString& MapPath) { UE_LOG(LogZMMO, Log, TEXT("FrontEndFlow: travel pedido pelo servidor → %s (%s)."), *MapName, *MapPath); + + // Idempotencia: se o cliente JA carregou esse mapa via TravelToMapById + // (ex: Lobby após CharSelect resolveu DT_Maps), e o S_TRAVEL_TO_MAP do + // servidor chega DEPOIS confirmando, NAO recarrega. Recarregar destruiria + // HUD, PlayerState e AttributeComponent — perdendo o snapshot ja aplicado. + if (!MapPath.IsEmpty()) + { + if (const UWorld* CurrentWorld = GetWorld()) + { + // CurrentWorld->GetOutermost()->GetName() devolve algo como + // "/Game/ZMMO/Maps/World/UEDPIE_0_L_TestWorld" no PIE. Comparamos + // pelo nome curto pra ignorar prefixo UEDPIE_N_. + const FString CurrentShort = FPackageName::GetShortName(CurrentWorld->GetOutermost()->GetName()); + const FString TargetShort = FPackageName::GetShortName(MapPath); + // PIE prefixa "UEDPIE__" — strip se presente. + FString CurrentClean = CurrentShort; + int32 UndIdx = INDEX_NONE; + if (CurrentClean.StartsWith(TEXT("UEDPIE_")) && CurrentClean.FindChar('_', UndIdx)) + { + const int32 SecondUnd = CurrentClean.Find(TEXT("_"), ESearchCase::CaseSensitive, ESearchDir::FromStart, UndIdx + 1); + if (SecondUnd != INDEX_NONE) + { + CurrentClean = CurrentClean.Mid(SecondUnd + 1); + } + } + if (CurrentClean.Equals(TargetShort, ESearchCase::IgnoreCase)) + { + UE_LOG(LogZMMO, Log, + TEXT("FrontEndFlow: ja estamos em %s — ignorando OpenLevel do S_TRAVEL_TO_MAP (mantem HUD/PlayerState)"), + *TargetShort); + bTravelingToWorld = false; + if (CurrentState != EZMMOFrontEndState::InWorld) + { + SetState(EZMMOFrontEndState::InWorld); + } + return; + } + } + } + bTravelingToWorld = true; SetState(EZMMOFrontEndState::EnteringWorld); @@ -380,9 +457,6 @@ void UUIFrontEndFlowSubsystem::HandleServerTravelRequested(const FString& MapNam // World Settings do .umap podem ter override herdado de L_FrontEnd // (GameModeOverride=ZMMOFrontEndGameMode) — sobrescrevemos via URL // option pra nao depender de cada artista configurar. - // Evolucao natural: quando o WorldServer enviar GameMode no - // S_TRAVEL_TO_MAP (wire estendido), trocar este literal por - // `?game=` + valor recebido do server. const FString Options = TEXT("game=/Script/ZMMO.ZMMOGameMode"); UGameplayStatics::OpenLevel(GetGameInstance(), FName(*MapPath), /*bAbsolute=*/true, Options); } @@ -390,13 +464,79 @@ void UUIFrontEndFlowSubsystem::HandleServerTravelRequested(const FString& MapNam void UUIFrontEndFlowSubsystem::HandlePostLoadMap(UWorld* /*LoadedWorld*/) { + if (!bTravelingToWorld) + { + return; + } + + // Loading dinâmico: marca a etapa "MapLoaded" e espera o spawn antes de + // liberar o InWorld (a tela limpa via OnLoadingComplete). Fallback: se + // não há screen ativa (DA não configurado), comportamento legado — + // transita já pra InWorld pra não travar. + if (UUILoadingScreen_Base* Loading = ActiveLoadingScreen.Get()) + { + Loading->MarkStepDone(TEXT("MapLoaded")); + return; + } + + // Race fix: widget ainda em RequestAsyncLoad; memoiza pra drenar quando + // a tela for criada (ver lambda em ResolveAndPushScreen). Não fallback + // pra InWorld — esperamos a tela existir e o Spawn chegar. + PendingLoadingSteps_.Add(TEXT("MapLoaded")); +} + +void UUIFrontEndFlowSubsystem::HandlePlayerSpawned(int64 /*EntityId*/, bool bIsLocal, + FVector /*PosCm*/, float /*YawDeg*/, + int64 /*ServerTimeMs*/) +{ + if (!bIsLocal) return; + if (UUILoadingScreen_Base* Loading = ActiveLoadingScreen.Get()) + { + Loading->MarkStepDone(TEXT("Spawn")); + return; + } + + // Race fix: spawn pode chegar ANTES do widget existir se o servidor for + // rápido (TryFinalizeEnterWorld) e o RequestAsyncLoad lento. Memoiza + // pra drenar no callback do async load. if (bTravelingToWorld) { - bTravelingToWorld = false; - SetState(EZMMOFrontEndState::InWorld); + PendingLoadingSteps_.Add(TEXT("Spawn")); } } +void UUIFrontEndFlowSubsystem::HandleLoadingComplete() +{ + if (UUILoadingScreen_Base* Loading = ActiveLoadingScreen.Get()) + { + Loading->OnLoadingComplete.RemoveDynamic(this, &UUIFrontEndFlowSubsystem::HandleLoadingComplete); + } + ActiveLoadingScreen.Reset(); + PendingLoadingSteps_.Reset(); + bTravelingToWorld = false; + SetState(EZMMOFrontEndState::InWorld); +} + +UZMMOLoadingProfilesDataAsset* UUIFrontEndFlowSubsystem::GetLoadingProfiles() +{ + if (LoadingProfiles) return LoadingProfiles; + if (!LoadingProfilesAsset.IsNull()) + { + LoadingProfiles = LoadingProfilesAsset.LoadSynchronous(); + } + return LoadingProfiles; +} + +UDataTable* UUIFrontEndFlowSubsystem::GetLoadingTips() +{ + if (LoadingTips) return LoadingTips; + if (!LoadingTipsAsset.IsNull()) + { + LoadingTips = LoadingTipsAsset.LoadSynchronous(); + } + return LoadingTips; +} + void UUIFrontEndFlowSubsystem::HandleServerHelloTheme(FName ThemeId) { // Dívida técnica (D5): ainda não há gancho no ZeusNetworkSubsystem para o diff --git a/Source/ZMMO/Game/UI/FrontEnd/UIFrontEndFlowSubsystem.h b/Source/ZMMO/Game/UI/FrontEnd/UIFrontEndFlowSubsystem.h index 579939b..6f4783c 100644 --- a/Source/ZMMO/Game/UI/FrontEnd/UIFrontEndFlowSubsystem.h +++ b/Source/ZMMO/Game/UI/FrontEnd/UIFrontEndFlowSubsystem.h @@ -8,6 +8,8 @@ class UUIFrontEndScreenSet; class UUIManagerSubsystem; +class UUILoadingScreen_Base; +class UZMMOLoadingProfilesDataAsset; class UZeusNetworkSubsystem; class UZeusCharServerSubsystem; struct FZMMOMapDef; @@ -81,18 +83,18 @@ public: void RequestEnterServerSelect(); /** - * Memoriza o mundo escolhido na ServerSelect (UUID v4 string). Lido pelo + * Memoriza o Realm escolhido na ServerSelect (UUID v4 string). Lido pelo * CharSelect/CharCreate na hora de filtrar/criar personagens. Limpo no * logout/back para ServerSelect. */ - UFUNCTION(BlueprintCallable, Category = "FrontEnd|Worlds") - void SetSelectedWorldId(const FString& WorldId); + UFUNCTION(BlueprintCallable, Category = "FrontEnd|Realms") + void SetSelectedRealmId(const FString& RealmId); - UFUNCTION(BlueprintPure, Category = "FrontEnd|Worlds") - FString GetSelectedWorldId() const { return SelectedWorldId; } + UFUNCTION(BlueprintPure, Category = "FrontEnd|Realms") + FString GetSelectedRealmId() const { return SelectedRealmId; } - UFUNCTION(BlueprintCallable, Category = "FrontEnd|Worlds") - void ClearSelectedWorld(); + UFUNCTION(BlueprintCallable, Category = "FrontEnd|Realms") + void ClearSelectedRealm(); /** * Resolve `MapId` (uint16 recebido do CharServer) → row do DT_Maps. @@ -147,6 +149,24 @@ protected: UPROPERTY(Config, EditDefaultsOnly, Category = "FrontEnd|Maps") TSoftObjectPtr MapsTableAsset; + /** + * DataAsset com os perfis de loading (etapas por contexto). Configure em + * DefaultGame.ini: + * [/Script/ZMMO.UIFrontEndFlowSubsystem] + * LoadingProfilesAsset=/Game/ZMMO/Data/UI/Loading/DA_LoadingProfiles.DA_LoadingProfiles + */ + UPROPERTY(Config, EditDefaultsOnly, Category = "FrontEnd|Loading") + TSoftObjectPtr LoadingProfilesAsset; + + /** + * DataTable com dicas (rows = FZMMOLoadingTipRow). Opcional — vazio + * significa "sem dicas". Configure em DefaultGame.ini: + * [/Script/ZMMO.UIFrontEndFlowSubsystem] + * LoadingTipsAsset=/Game/ZMMO/Data/UI/Loading/DT_LoadingTips.DT_LoadingTips + */ + UPROPERTY(Config, EditDefaultsOnly, Category = "FrontEnd|Loading") + TSoftObjectPtr LoadingTipsAsset; + private: void BindNetwork(); void UnbindNetwork(); @@ -170,8 +190,26 @@ private: UFUNCTION() void HandleServerTravelRequested(const FString& MapName, const FString& MapPath); + /** + * Spawn do player local — fim da última etapa do loading EnteringWorld. + * Assinatura espelha FZeusOnPlayerSpawned (FiveParams). + */ + UFUNCTION() + void HandlePlayerSpawned(int64 EntityId, bool bIsLocal, FVector PosCm, + float YawDeg, int64 ServerTimeMs); + + /** Disparado pela tela de loading quando todas as etapas viram Done. */ + UFUNCTION() + void HandleLoadingComplete(); + void HandlePostLoadMap(UWorld* LoadedWorld); + /** Resolve+carrega o DA de perfis (sync). */ + UZMMOLoadingProfilesDataAsset* GetLoadingProfiles(); + + /** Resolve+carrega o DT de dicas (sync). */ + UDataTable* GetLoadingTips(); + /** * Dívida técnica (D5): ServerHello traz ThemeId, mas o UZeusNetworkSubsystem * ainda não expõe um delegate/getter dedicado. Quando expuser, ligar aqui @@ -184,7 +222,7 @@ private: EZMMOFrontEndState CurrentState = EZMMOFrontEndState::None; UPROPERTY(Transient) - FString SelectedWorldId; + FString SelectedRealmId; /** Pose autoritativa pendente (do S_CHAR_SELECT_OK). */ UPROPERTY(Transient) @@ -199,6 +237,27 @@ private: UPROPERTY(Transient) TObjectPtr ScreenSet; + UPROPERTY(Transient) + TObjectPtr LoadingProfiles; + + UPROPERTY(Transient) + TObjectPtr LoadingTips; + + /** + * Tela de loading atualmente em Modal. Weak porque o stack do CommonUI + * é dono — se popar por outro caminho não queremos dangling. + */ + TWeakObjectPtr ActiveLoadingScreen; + + /** + * Race fix: o widget de loading é criado via RequestAsyncLoad (assíncrono). + * Se o servidor for rápido (TryFinalizeEnterWorld), HandlePostLoadMap e/ou + * HandlePlayerSpawned podem disparar ANTES de ActiveLoadingScreen existir + * — nesse caso o MarkStepDone vira no-op e o loading fica eterno. + * Memoizamos os StepIds aqui e drenamos quando a tela for criada. + */ + TSet PendingLoadingSteps_; + bool bNetBound = false; bool bTravelingToWorld = false; FDelegateHandle PostLoadMapHandle; diff --git a/Source/ZMMO/Game/UI/FrontEnd/UIServerCard_Base.cpp b/Source/ZMMO/Game/UI/FrontEnd/UIServerCard_Base.cpp index 8a357ac..cf88bf9 100644 --- a/Source/ZMMO/Game/UI/FrontEnd/UIServerCard_Base.cpp +++ b/Source/ZMMO/Game/UI/FrontEnd/UIServerCard_Base.cpp @@ -9,12 +9,11 @@ void UUIServerCard_Base::NativePreConstruct() { Super::NativePreConstruct(); - // Reaplica tema do botao antes de qualquer SetIsEnabled — garante que o - // background da variante "Primary" pinta tanto em Normal quanto Disabled. - if (Btn_Enter) - { - Btn_Enter->RefreshUIStyle(); - } + // TODO(ui-system): reativar quando UUIButton_Base voltar a ter RefreshUIStyle. + // if (Btn_Enter) + // { + // Btn_Enter->RefreshUIStyle(); + // } } void UUIServerCard_Base::NativeConstruct() @@ -25,10 +24,11 @@ void UUIServerCard_Base::NativeConstruct() Btn_Enter->OnClicked().AddUObject(this, &UUIServerCard_Base::HandleEnterClicked); bBound = true; } - if (Btn_Enter) - { - Btn_Enter->RefreshUIStyle(); - } + // TODO(ui-system): reativar quando UUIButton_Base voltar a ter RefreshUIStyle. + // if (Btn_Enter) + // { + // Btn_Enter->RefreshUIStyle(); + // } } void UUIServerCard_Base::NativeDestruct() @@ -41,17 +41,17 @@ void UUIServerCard_Base::NativeDestruct() Super::NativeDestruct(); } -void UUIServerCard_Base::SetFromEntry(const FZMMOWorldEntry& InEntry) +void UUIServerCard_Base::SetFromEntry(const FZMMORealmEntry& InEntry) { Entry = InEntry; if (Text_Name) { - Text_Name->SetText(FText::FromString(Entry.WorldName)); + Text_Name->SetText(FText::FromString(Entry.Name)); } if (Text_Pop) { - Text_Pop->SetText(FText::FromString(FString::Printf(TEXT("%d/%d"), Entry.Population, Entry.Capacity))); + Text_Pop->SetText(FText::FromString(FString::Printf(TEXT("%d/%d"), Entry.Pop, Entry.Capacity))); } if (Text_Status) { @@ -60,24 +60,25 @@ void UUIServerCard_Base::SetFromEntry(const FZMMOWorldEntry& InEntry) if (Bar_Pop) { const float Ratio = (Entry.Capacity > 0) - ? FMath::Clamp(static_cast(Entry.Population) / static_cast(Entry.Capacity), 0.f, 1.f) + ? FMath::Clamp(static_cast(Entry.Pop) / static_cast(Entry.Capacity), 0.f, 1.f) : 0.f; Bar_Pop->SetPercent(Ratio); } - // Botao "Selecionar" sempre habilitado — mesmo com world offline, + // Botao "Selecionar" sempre habilitado — mesmo com realm offline, // o usuario pode entrar no Lobby pra ver a lista de personagens, criar/ - // deletar. O gate de "entrar no mundo" fica no proprio Lobby (botao - // "Entrar no Servidor"), que checa World.State antes do handoff. - if (Btn_Enter) - { - Btn_Enter->RefreshUIStyle(); - } + // deletar. O gate de "entrar no realm" fica no proprio Lobby (botao + // "Entrar no Servidor"), que checa Realm.State antes do handoff. + // TODO(ui-system): reativar quando UUIButton_Base voltar a ter RefreshUIStyle. + // if (Btn_Enter) + // { + // Btn_Enter->RefreshUIStyle(); + // } } void UUIServerCard_Base::HandleEnterClicked() { - OnCardPressed.Broadcast(Entry.WorldId, Entry.State); + OnCardPressed.Broadcast(Entry.RealmId, Entry.State); } FText UUIServerCard_Base::FormatStatus_Implementation(uint8 State) const diff --git a/Source/ZMMO/Game/UI/FrontEnd/UIServerCard_Base.h b/Source/ZMMO/Game/UI/FrontEnd/UIServerCard_Base.h index 564c314..5c8c367 100644 --- a/Source/ZMMO/Game/UI/FrontEnd/UIServerCard_Base.h +++ b/Source/ZMMO/Game/UI/FrontEnd/UIServerCard_Base.h @@ -2,7 +2,7 @@ #include "CoreMinimal.h" #include "Blueprint/UserWidget.h" -#include "ZMMOWorldEntry.h" +#include "ZMMORealmEntry.h" #include "UIServerCard_Base.generated.h" class UCommonTextBlock; @@ -11,10 +11,10 @@ class UUIButton_Base; class UProgressBar; /** - * Card de servidor (mundo) instanciado dinamicamente pela ServerSelect. + * Card de servidor (realm) instanciado dinamicamente pela ServerSelect. * - * Recebe um `FZMMOWorldEntry` e renderiza nome/pop/status/bar; ao clicar - * em "Entrar" dispara `OnCardPressed.Broadcast(WorldId, State)`. A tela + * Recebe um `FZMMORealmEntry` e renderiza nome/pop/status/bar; ao clicar + * em "Entrar" dispara `OnCardPressed.Broadcast(RealmId, State)`. A tela * dona (UUIServerSelectScreen_Base) escuta esse delegate. * * WBP filho (`WBP_ServerCard`) precisa expor (via nomes de variavel): @@ -25,7 +25,7 @@ class UProgressBar; * - Btn_Enter : Button * - Bar_Pop : ProgressBar (opcional) */ -DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FZMMOOnServerCardPressed, FString, WorldId, uint8, State); +DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FZMMOOnServerCardPressed, FString, RealmId, uint8, State); UCLASS(Abstract, Blueprintable) class ZMMO_API UUIServerCard_Base : public UUserWidget @@ -34,14 +34,14 @@ class ZMMO_API UUIServerCard_Base : public UUserWidget public: /** - * Aplica os dados de um mundo no card. + * Aplica os dados de um realm no card. */ UFUNCTION(BlueprintCallable, Category = "Zeus|ServerCard") - void SetFromEntry(const FZMMOWorldEntry& Entry); + void SetFromEntry(const FZMMORealmEntry& Entry); /** Dados atuais (copia). */ UPROPERTY(BlueprintReadOnly, Category = "Zeus|ServerCard") - FZMMOWorldEntry Entry; + FZMMORealmEntry Entry; /** * Disparado quando o botao "Entrar" do card e clicado. diff --git a/Source/ZMMO/Game/UI/FrontEnd/UIServerSelectScreen_Base.cpp b/Source/ZMMO/Game/UI/FrontEnd/UIServerSelectScreen_Base.cpp index 671717b..0737cf2 100644 --- a/Source/ZMMO/Game/UI/FrontEnd/UIServerSelectScreen_Base.cpp +++ b/Source/ZMMO/Game/UI/FrontEnd/UIServerSelectScreen_Base.cpp @@ -70,7 +70,7 @@ void UUIServerSelectScreen_Base::NativeOnActivated() } } - RequestWorldList(); + RequestRealmList(); } void UUIServerSelectScreen_Base::NativeOnDeactivated() @@ -110,23 +110,23 @@ void UUIServerSelectScreen_Base::RefreshUIStyle_Implementation() } } -void UUIServerSelectScreen_Base::RequestWorldList() +void UUIServerSelectScreen_Base::RequestRealmList() { UZeusCharServerSubsystem* Char = GetCharServer(); if (!Char || !Char->IsConnected()) { - UE_LOG(LogZMMO, Warning, TEXT("ServerSelect: CharServer indisponivel — pulando RequestWorldList")); + UE_LOG(LogZMMO, Warning, TEXT("ServerSelect: CharServer indisponivel — pulando RequestRealmList")); return; } TArray EmptyPayload; - Char->SendCharRequest(ZMMOCharOp::C_WORLD_LIST_REQUEST, EmptyPayload); + Char->SendCharRequest(ZMMOCharOp::C_REALM_LIST_REQUEST, EmptyPayload); } -void UUIServerSelectScreen_Base::SelectWorldAndProceed(const FString& WorldId) +void UUIServerSelectScreen_Base::SelectRealmAndProceed(const FString& RealmId) { - if (WorldId.IsEmpty()) + if (RealmId.IsEmpty()) { - UE_LOG(LogZMMO, Warning, TEXT("ServerSelect: SelectWorldAndProceed com WorldId vazio.")); + UE_LOG(LogZMMO, Warning, TEXT("ServerSelect: SelectRealmAndProceed com RealmId vazio.")); return; } UGameInstance* GI = GetGameInstance(); @@ -136,18 +136,18 @@ void UUIServerSelectScreen_Base::SelectWorldAndProceed(const FString& WorldId) } if (UUIFrontEndFlowSubsystem* Flow = GI->GetSubsystem()) { - Flow->SetSelectedWorldId(WorldId); + Flow->SetSelectedRealmId(RealmId); Flow->SetState(EZMMOFrontEndState::Lobby); } } void UUIServerSelectScreen_Base::HandleCharRawMessage(int32 Opcode, const TArray& Payload) { - if (Opcode == ZMMOCharOp::S_WORLD_LIST) + if (Opcode == ZMMOCharOp::S_REALM_LIST) { - ParseWorldList(Payload); + ParseRealmList(Payload); } - else if (Opcode == ZMMOCharOp::S_WORLD_STATUS_UPDATE) + else if (Opcode == ZMMOCharOp::S_REALM_STATUS_UPDATE) { ApplyStatusUpdate(Payload); } @@ -155,24 +155,24 @@ void UUIServerSelectScreen_Base::HandleCharRawMessage(int32 Opcode, const TArray void UUIServerSelectScreen_Base::ApplyStatusUpdate(const TArray& Payload) { - // Wire: string worldId + uint16 pop + uint16 queueLen + uint8 state + // Wire: string realmId + uint16 pop + uint16 queueLen + uint8 state int32 Pos = 0; - FString WorldId; + FString RealmId; uint16 Pop = 0; uint16 QueueLen = 0; uint8 State = 0; - if (!ReadStringUtf8(Payload, Pos, WorldId) + if (!ReadStringUtf8(Payload, Pos, RealmId) || !ReadU16(Payload, Pos, Pop) || !ReadU16(Payload, Pos, QueueLen) || !ReadU8(Payload, Pos, State)) { - UE_LOG(LogZMMO, Warning, TEXT("ServerSelect: S_WORLD_STATUS_UPDATE malformado")); + UE_LOG(LogZMMO, Warning, TEXT("ServerSelect: S_REALM_STATUS_UPDATE malformado")); return; } int32 Idx = INDEX_NONE; - for (int32 i = 0; i < Worlds.Num(); ++i) + for (int32 i = 0; i < Realms.Num(); ++i) { - if (Worlds[i].WorldId == WorldId) + if (Realms[i].RealmId == RealmId) { Idx = i; break; @@ -180,76 +180,76 @@ void UUIServerSelectScreen_Base::ApplyStatusUpdate(const TArray& Payload) } if (Idx == INDEX_NONE) { - // Mundo desconhecido (ainda nao recebemos via S_WORLD_LIST). Pede lista - // completa pra trazer dados estaticos (name/host/region/capacity). - UE_LOG(LogZMMO, Log, TEXT("ServerSelect: STATUS_UPDATE de mundo desconhecido (%s) — pedindo lista"), *WorldId); - RequestWorldList(); + // Realm desconhecido (ainda nao recebemos via S_REALM_LIST). Pede lista + // completa pra trazer dados estaticos (name/gatewayEndpoint/region/capacity). + UE_LOG(LogZMMO, Log, TEXT("ServerSelect: STATUS_UPDATE de realm desconhecido (%s) — pedindo lista"), *RealmId); + RequestRealmList(); return; } - FZMMOWorldEntry& Entry = Worlds[Idx]; - Entry.Population = static_cast(Pop); + FZMMORealmEntry& Entry = Realms[Idx]; + Entry.Pop = static_cast(Pop); Entry.QueueLen = static_cast(QueueLen); Entry.State = State; - UE_LOG(LogZMMO, Log, TEXT("ServerSelect: world %s atualizado (state=%d pop=%d)"), - *WorldId, State, Entry.Population); + UE_LOG(LogZMMO, Log, TEXT("ServerSelect: realm %s atualizado (state=%d pop=%d)"), + *RealmId, State, Entry.Pop); // Atualiza apenas o card afetado (preserva animacoes/scroll dos demais). if (SpawnedCards.IsValidIndex(Idx) && SpawnedCards[Idx]) { SpawnedCards[Idx]->SetFromEntry(Entry); } - OnWorldListReceived(); + OnRealmListReceived(); } -void UUIServerSelectScreen_Base::ParseWorldList(const TArray& Payload) +void UUIServerSelectScreen_Base::ParseRealmList(const TArray& Payload) { int32 Pos = 0; uint16 Count = 0; if (!ReadU16(Payload, Pos, Count)) { - UE_LOG(LogZMMO, Warning, TEXT("ServerSelect: S_WORLD_LIST malformado (sem count)")); + UE_LOG(LogZMMO, Warning, TEXT("ServerSelect: S_REALM_LIST malformado (sem count)")); return; } - TArray NewWorlds; - NewWorlds.Reserve(Count); + TArray NewRealms; + NewRealms.Reserve(Count); for (uint16 i = 0; i < Count; ++i) { - FZMMOWorldEntry Entry; + FZMMORealmEntry Entry; FString RegionStr; - uint16 Port = 0; uint16 Cap = 0; uint16 Pop = 0; uint16 QueueLen = 0; uint8 State = 0; - if (!ReadStringUtf8(Payload, Pos, Entry.WorldId) - || !ReadStringUtf8(Payload, Pos, Entry.WorldName) + // Wire: string realmId + string name + string region + string gatewayEndpoint + // + uint16 capacity + uint16 pop + uint16 queueLen + uint8 state + // Server-side: Server/ZeusCharServer/src/services/realm-list.service.ts + if (!ReadStringUtf8(Payload, Pos, Entry.RealmId) + || !ReadStringUtf8(Payload, Pos, Entry.Name) || !ReadStringUtf8(Payload, Pos, RegionStr) - || !ReadStringUtf8(Payload, Pos, Entry.Host) - || !ReadU16(Payload, Pos, Port) + || !ReadStringUtf8(Payload, Pos, Entry.GatewayEndpoint) || !ReadU16(Payload, Pos, Cap) || !ReadU16(Payload, Pos, Pop) || !ReadU16(Payload, Pos, QueueLen) || !ReadU8(Payload, Pos, State)) { - UE_LOG(LogZMMO, Warning, TEXT("ServerSelect: S_WORLD_LIST malformado (entry %d)"), i); + UE_LOG(LogZMMO, Warning, TEXT("ServerSelect: S_REALM_LIST malformado (entry %d)"), i); return; } Entry.Region = RegionStr; - Entry.Port = static_cast(Port); Entry.Capacity = static_cast(Cap); - Entry.Population = static_cast(Pop); + Entry.Pop = static_cast(Pop); Entry.QueueLen = static_cast(QueueLen); Entry.State = State; - NewWorlds.Add(MoveTemp(Entry)); + NewRealms.Add(MoveTemp(Entry)); } - Worlds = MoveTemp(NewWorlds); - UE_LOG(LogZMMO, Log, TEXT("ServerSelect: recebidos %d mundos"), Worlds.Num()); + Realms = MoveTemp(NewRealms); + UE_LOG(LogZMMO, Log, TEXT("ServerSelect: recebidos %d realms"), Realms.Num()); RebuildCards(); - OnWorldListReceived(); + OnRealmListReceived(); } void UUIServerSelectScreen_Base::RebuildCards() @@ -280,15 +280,15 @@ void UUIServerSelectScreen_Base::RebuildCards() const int32 GridColumns = 2; // bate com o mock (ColumnFill[0]/[1]) int32 Index = 0; - for (const FZMMOWorldEntry& W : Worlds) + for (const FZMMORealmEntry& R : Realms) { UUIServerCard_Base* Card = CreateWidget(this, CardClass); if (!Card) { - UE_LOG(LogZMMO, Warning, TEXT("ServerSelect: falha ao criar card para %s"), *W.WorldName); + UE_LOG(LogZMMO, Warning, TEXT("ServerSelect: falha ao criar card para %s"), *R.Name); continue; } - Card->SetFromEntry(W); + Card->SetFromEntry(R); Card->OnCardPressed.AddDynamic(this, &UUIServerSelectScreen_Base::HandleCardPressed); CardContainer->AddChild(Card); @@ -311,14 +311,14 @@ void UUIServerSelectScreen_Base::RebuildCards() } } -void UUIServerSelectScreen_Base::HandleCardPressed(FString WorldId, uint8 State) +void UUIServerSelectScreen_Base::HandleCardPressed(FString RealmId, uint8 State) { - // O ServerSelect permite escolher qualquer mundo (online/maintenance/ - // offline) — o usuario entra no Lobby pra gerenciar chars desse mundo. - // O bloqueio real (handoff -> world) acontece no botao "Entrar no - // Servidor" do Lobby, que checa World.State antes do C_CHAR_SELECT. - UE_LOG(LogZMMO, Log, TEXT("ServerSelect: mundo %s selecionado (state=%d)"), *WorldId, State); - SelectWorldAndProceed(WorldId); + // O ServerSelect permite escolher qualquer realm (online/maintenance/ + // offline) — o usuario entra no Lobby pra gerenciar chars desse realm. + // O bloqueio real (handoff -> gateway) acontece no botao "Entrar no + // Servidor" do Lobby, que checa Realm.State antes do C_CHAR_SELECT. + UE_LOG(LogZMMO, Log, TEXT("ServerSelect: realm %s selecionado (state=%d)"), *RealmId, State); + SelectRealmAndProceed(RealmId); } UZeusCharServerSubsystem* UUIServerSelectScreen_Base::GetCharServer() const diff --git a/Source/ZMMO/Game/UI/FrontEnd/UIServerSelectScreen_Base.h b/Source/ZMMO/Game/UI/FrontEnd/UIServerSelectScreen_Base.h index e9c4895..039ae02 100644 --- a/Source/ZMMO/Game/UI/FrontEnd/UIServerSelectScreen_Base.h +++ b/Source/ZMMO/Game/UI/FrontEnd/UIServerSelectScreen_Base.h @@ -2,7 +2,7 @@ #include "CoreMinimal.h" #include "UIActivatableScreen_Base.h" -#include "ZMMOWorldEntry.h" +#include "ZMMORealmEntry.h" #include "UIServerSelectScreen_Base.generated.h" class UCommonTextBlock; @@ -12,15 +12,15 @@ class UUIServerCard_Base; class UZeusCharServerSubsystem; /** - * Tela de Server Select — Fase 1 do ARQUITETURA_SERVER_SELECT. + * Tela de Realm Select — Fase 1 do ARQUITETURA_SERVER_SELECT. * * Fluxo: * 1. NativeOnActivated subscreve OnRawMessage do UZeusCharServerSubsystem - * 2. Envia C_WORLD_LIST_REQUEST (payload vazio) - * 3. ParseWorldList monta `Worlds` + * 2. Envia C_REALM_LIST_REQUEST (payload vazio) + * 3. ParseRealmList monta `Realms` * 4. RebuildCards apaga filhos de `CardContainer` e instancia 1 WBP_ServerCard - * por mundo, bindando `OnCardPressed -> SelectWorldAndProceed` - * 5. Dispara `OnWorldListReceived` (BIE) pra extensoes em BP + * por realm, bindando `OnCardPressed -> SelectRealmAndProceed` + * 5. Dispara `OnRealmListReceived` (BIE) pra extensoes em BP */ UCLASS(Abstract, Blueprintable) class ZMMO_API UUIServerSelectScreen_Base : public UUIActivatableScreen_Base @@ -29,16 +29,16 @@ class ZMMO_API UUIServerSelectScreen_Base : public UUIActivatableScreen_Base public: UFUNCTION(BlueprintCallable, Category = "Zeus|ServerSelect") - void RequestWorldList(); + void RequestRealmList(); UFUNCTION(BlueprintCallable, Category = "Zeus|ServerSelect") - void SelectWorldAndProceed(const FString& WorldId); + void SelectRealmAndProceed(const FString& RealmId); UPROPERTY(BlueprintReadOnly, Category = "Zeus|ServerSelect") - TArray Worlds; + TArray Realms; UFUNCTION(BlueprintImplementableEvent, Category = "Zeus|ServerSelect") - void OnWorldListReceived(); + void OnRealmListReceived(); protected: virtual void NativePreConstruct() override; @@ -50,10 +50,10 @@ protected: void HandleCharRawMessage(int32 Opcode, const TArray& Payload); UFUNCTION() - void HandleCardPressed(FString WorldId, uint8 State); + void HandleCardPressed(FString RealmId, uint8 State); UZeusCharServerSubsystem* GetCharServer() const; - void ParseWorldList(const TArray& Payload); + void ParseRealmList(const TArray& Payload); void ApplyStatusUpdate(const TArray& Payload); void RebuildCards(); diff --git a/Source/ZMMO/Game/UI/FrontEnd/UIUserLobbyScreen_Base.cpp b/Source/ZMMO/Game/UI/FrontEnd/UIUserLobbyScreen_Base.cpp index 47f1127..e041ffd 100644 --- a/Source/ZMMO/Game/UI/FrontEnd/UIUserLobbyScreen_Base.cpp +++ b/Source/ZMMO/Game/UI/FrontEnd/UIUserLobbyScreen_Base.cpp @@ -66,27 +66,27 @@ void UUIUserLobbyScreen_Base::RequestCharList() UZeusCharServerSubsystem* Char = GetCharServer(); if (!Char || !Char->IsConnected()) return; - FString WorldId; + FString RealmId; if (UGameInstance* GI = GetGameInstance()) { if (UUIFrontEndFlowSubsystem* Flow = GI->GetSubsystem()) { - WorldId = Flow->GetSelectedWorldId(); + RealmId = Flow->GetSelectedRealmId(); } } - // Wire: uint8 hasFilter + (uint8[16] worldId se hasFilter==1) + // Wire: uint8 hasFilter + (uint8[16] realmId se hasFilter==1) TArray Payload; - if (WorldId.IsEmpty()) + if (RealmId.IsEmpty()) { Payload.Add(0); } else { Payload.Add(1); - if (!WriteUuid16(Payload, WorldId)) + if (!WriteUuid16(Payload, RealmId)) { - UE_LOG(LogZMMO, Warning, TEXT("Lobby: SelectedWorldId invalido (%s) — pedindo sem filtro"), *WorldId); + UE_LOG(LogZMMO, Warning, TEXT("Lobby: SelectedRealmId invalido (%s) — pedindo sem filtro"), *RealmId); Payload.Reset(); Payload.Add(0); } @@ -110,7 +110,7 @@ void UUIUserLobbyScreen_Base::BackToServerSelect() if (!GI) return; if (UUIFrontEndFlowSubsystem* Flow = GI->GetSubsystem()) { - Flow->ClearSelectedWorld(); + Flow->ClearSelectedRealm(); Flow->SetState(EZMMOFrontEndState::ServerSelect); } } @@ -160,7 +160,7 @@ void UUIUserLobbyScreen_Base::ParseCharList(const TArray& Payload) uint64 deleteAt = 0; if (!ReadU64(Payload, Pos, charId64) - || !ReadUuid16(Payload, Pos, E.WorldId) + || !ReadUuid16(Payload, Pos, E.RealmId) || !ReadU8(Payload, Pos, slot) || !ReadStringUtf8(Payload, Pos, E.Name) || !ReadU16(Payload, Pos, classId) @@ -328,25 +328,45 @@ void UUIUserLobbyScreen_Base::HandleCharDeleteCancelAck(const TArray& Pay void UUIUserLobbyScreen_Base::HandleCharSelectOk(const TArray& Payload) { // Wire: uint64 charId + uint16 mapId + float x/y/z + float yaw + - // string worldHost + uint16 worldPort + string handoffToken + string region + // string gatewayEndpoint + string handoffToken + string region + // gatewayEndpoint = "host:port" (UMA single string — refator R4 unificou + // host+port). Server-side: char-select.service.ts. int32 Pos = 0; - uint64 CharId64 = 0; FString WorldHost, HandoffToken, Region; + uint64 CharId64 = 0; FString GatewayEndpoint, HandoffToken, Region; uint16 MapId16 = 0; - float Px=0, Py=0, Pz=0, Yaw=0; uint16 WorldPort = 0; + float Px=0, Py=0, Pz=0, Yaw=0; if (!ReadU64(Payload, Pos, CharId64) || !ReadU16(Payload, Pos, MapId16) || !ReadFloat(Payload, Pos, Px) || !ReadFloat(Payload, Pos, Py) || !ReadFloat(Payload, Pos, Pz) || !ReadFloat(Payload, Pos, Yaw) - || !ReadStringUtf8(Payload, Pos, WorldHost) - || !ReadU16(Payload, Pos, WorldPort) + || !ReadStringUtf8(Payload, Pos, GatewayEndpoint) || !ReadStringUtf8(Payload, Pos, HandoffToken) || !ReadStringUtf8(Payload, Pos, Region)) { UE_LOG(LogZMMO, Warning, TEXT("Lobby: S_CHAR_SELECT_OK malformado")); return; } - UE_LOG(LogZMMO, Log, TEXT("Lobby: handoff -> world=%s:%d mapId=%u token=%s..."), - *WorldHost, WorldPort, static_cast(MapId16), *HandoffToken.Left(8)); + UE_LOG(LogZMMO, Log, TEXT("Lobby: handoff -> realm gateway=%s mapId=%u token=%s..."), + *GatewayEndpoint, static_cast(MapId16), *HandoffToken.Left(8)); + + // Split "host:port" para a API legada do ZeusNetworkSubsystem que ainda + // recebe os dois separados. Defensivo: se o server mandar string vazia + // ou sem ':' a gente loga e aborta o handoff em vez de tentar conectar + // em endereco invalido. + FString GwHost; + FString GwPortStr; + if (!GatewayEndpoint.Split(TEXT(":"), &GwHost, &GwPortStr, ESearchCase::CaseSensitive, ESearchDir::FromEnd)) + { + UE_LOG(LogZMMO, Error, TEXT("Lobby: gatewayEndpoint malformado (sem ':') = %s"), *GatewayEndpoint); + return; + } + int32 GwPort = 0; + LexFromString(GwPort, *GwPortStr); + if (GwHost.IsEmpty() || GwPort <= 0) + { + UE_LOG(LogZMMO, Error, TEXT("Lobby: gatewayEndpoint invalido host=%s port=%d"), *GwHost, GwPort); + return; + } UGameInstance* GI = GetGameInstance(); if (!GI) return; @@ -360,13 +380,13 @@ void UUIUserLobbyScreen_Base::HandleCharSelectOk(const TArray& Payload) } // Fase 3: handoff UDP — apresenta o ticket no `C_CONNECT_REQUEST`. O - // WorldServer valida via GETDEL no Valkey regional. WorldServer envia + // Gateway/ZeusServer valida via GETDEL no Valkey regional. Server envia // `S_CONNECT_OK` (sucesso) ou `S_CONNECT_REJECT` (ticket invalido/expirado). // Disparamos PRIMEIRO o connect (nao-bloqueante), depois o OpenLevel — // ZeusNetworkSubsystem e per-GameInstance e persiste cross-travel. if (UZeusNetworkSubsystem* ZeusNet = GI->GetSubsystem()) { - ZeusNet->ConnectToZeusServerWithTicket(WorldHost, static_cast(WorldPort), HandoffToken); + ZeusNet->ConnectToZeusServerWithTicket(GwHost, GwPort, HandoffToken); } else { diff --git a/Source/ZMMO/Game/UI/FrontEnd/UIUserLobbyScreen_Base.h b/Source/ZMMO/Game/UI/FrontEnd/UIUserLobbyScreen_Base.h index 077c0f7..f8c1a34 100644 --- a/Source/ZMMO/Game/UI/FrontEnd/UIUserLobbyScreen_Base.h +++ b/Source/ZMMO/Game/UI/FrontEnd/UIUserLobbyScreen_Base.h @@ -18,13 +18,13 @@ class UZeusCharServerSubsystem; * Tela Lobby (host) — Fase 1.5 do ARQUITETURA_SERVER_SELECT. * * Fluxo: - * 1. NativeOnActivated: pega `SelectedWorldId` do FlowSubsystem, envia - * C_CHAR_LIST_REQUEST filtrado por esse mundo + * 1. NativeOnActivated: pega `SelectedRealmId` do FlowSubsystem, envia + * C_CHAR_LIST_REQUEST filtrado por esse realm * 2. Parse S_CHAR_LIST -> Chars[]; instancia 1 WBP_CharCard por entry * 3. Card "Selecionar" -> C_CHAR_SELECT(charId) -> S_CHAR_SELECT_OK -> - * handoff UDP pro WorldServer + * handoff UDP pro Gateway do Realm * 4. Botao "Criar Personagem" -> mostra page interna `CharacterCreate` - * 5. Botao "Voltar" -> ClearSelectedWorld + Flow.SetState(ServerSelect) + * 5. Botao "Voltar" -> ClearSelectedRealm + Flow.SetState(ServerSelect) * * Pages internas via UWidgetSwitcher: * - PageIndex 0: Lista de chars (CardContainer) @@ -96,6 +96,9 @@ protected: UPROPERTY(meta = (BindWidgetOptional)) TObjectPtr CharCreatePage; UPROPERTY(meta = (BindWidgetOptional)) TObjectPtr Text_Title; + // TODO(R4): renomear o widget Text_WorldName -> Text_RealmName no WBP_UserLobby.uasset + // tambem; aqui mantivemos o nome antigo pra nao quebrar o BindWidgetOptional ate + // alguem reabrir o WBP no editor. UPROPERTY(meta = (BindWidgetOptional)) TObjectPtr Text_WorldName; UPROPERTY(meta = (BindWidgetOptional)) TObjectPtr Btn_Back; UPROPERTY(meta = (BindWidgetOptional)) TObjectPtr Btn_Create; diff --git a/Source/ZMMO/Game/UI/FrontEnd/ZMMOCharSummary.h b/Source/ZMMO/Game/UI/FrontEnd/ZMMOCharSummary.h index c4c6cb6..c765906 100644 --- a/Source/ZMMO/Game/UI/FrontEnd/ZMMOCharSummary.h +++ b/Source/ZMMO/Game/UI/FrontEnd/ZMMOCharSummary.h @@ -43,7 +43,7 @@ struct FZMMOCharSummary FString CharId; UPROPERTY(BlueprintReadOnly, Category = "Zeus|Char") - FString WorldId; + FString RealmId; UPROPERTY(BlueprintReadOnly, Category = "Zeus|Char") int32 Slot = 0; diff --git a/Source/ZMMO/Game/UI/FrontEnd/ZMMORealmEntry.h b/Source/ZMMO/Game/UI/FrontEnd/ZMMORealmEntry.h new file mode 100644 index 0000000..0709953 --- /dev/null +++ b/Source/ZMMO/Game/UI/FrontEnd/ZMMORealmEntry.h @@ -0,0 +1,48 @@ +#pragma once + +#include "CoreMinimal.h" +#include "ZMMORealmEntry.generated.h" + +/** + * Entrada de Realm (cluster Gateway+Provinces) recebida do CharServer via + * S_REALM_LIST. Espelha `RealmRecord` em + * `Server/ZeusCharServer/src/types/realm.types.ts`. + * + * `GatewayEndpoint` e' o endereco "host:port" do Gateway do Realm (UMA + * single string vinda do server — substitui o par host+port da versao + * anterior). Cliente faz split por `:` quando precisa abrir UDP. + * + * State usa o wire raw (0=Offline, 1=Online, 2=Maintenance — ver + * ZMMOWireRealmState em CharServerOpcodes.h). + */ +USTRUCT(BlueprintType) +struct FZMMORealmEntry +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadOnly, Category = "Zeus|Realm") + FString RealmId; + + UPROPERTY(BlueprintReadOnly, Category = "Zeus|Realm") + FString Name; + + UPROPERTY(BlueprintReadOnly, Category = "Zeus|Realm") + FString Region; + + /** Endpoint do Gateway no formato "host:port" (ex: "127.0.0.1:7777"). */ + UPROPERTY(BlueprintReadOnly, Category = "Zeus|Realm") + FString GatewayEndpoint; + + UPROPERTY(BlueprintReadOnly, Category = "Zeus|Realm") + int32 Capacity = 0; + + UPROPERTY(BlueprintReadOnly, Category = "Zeus|Realm") + int32 Pop = 0; + + UPROPERTY(BlueprintReadOnly, Category = "Zeus|Realm") + int32 QueueLen = 0; + + /** 0=Offline, 1=Online, 2=Maintenance (vide ZMMOWireRealmState). */ + UPROPERTY(BlueprintReadOnly, Category = "Zeus|Realm") + uint8 State = 0; +}; diff --git a/Source/ZMMO/Game/UI/FrontEnd/ZMMOWorldEntry.h b/Source/ZMMO/Game/UI/FrontEnd/ZMMOWorldEntry.h deleted file mode 100644 index 34b0b8c..0000000 --- a/Source/ZMMO/Game/UI/FrontEnd/ZMMOWorldEntry.h +++ /dev/null @@ -1,45 +0,0 @@ -#pragma once - -#include "CoreMinimal.h" -#include "ZMMOWorldEntry.generated.h" - -/** - * Entrada de mundo (server) recebida do CharServer via S_WORLD_LIST. - * Espelha `WorldRecord` em `Server/ZeusCharServer/src/types/world.types.ts`. - * - * State usa o wire raw (0=Offline, 1=Online, 2=Maintenance — ver - * ZMMOWireWorldState em CharServerOpcodes.h). - */ -USTRUCT(BlueprintType) -struct FZMMOWorldEntry -{ - GENERATED_BODY() - - UPROPERTY(BlueprintReadOnly, Category = "Zeus|World") - FString WorldId; - - UPROPERTY(BlueprintReadOnly, Category = "Zeus|World") - FString WorldName; - - UPROPERTY(BlueprintReadOnly, Category = "Zeus|World") - FString Region; - - UPROPERTY(BlueprintReadOnly, Category = "Zeus|World") - FString Host; - - UPROPERTY(BlueprintReadOnly, Category = "Zeus|World") - int32 Port = 0; - - UPROPERTY(BlueprintReadOnly, Category = "Zeus|World") - int32 Capacity = 0; - - UPROPERTY(BlueprintReadOnly, Category = "Zeus|World") - int32 Population = 0; - - UPROPERTY(BlueprintReadOnly, Category = "Zeus|World") - int32 QueueLen = 0; - - /** 0=Offline, 1=Online, 2=Maintenance (vide ZMMOWireWorldState). */ - UPROPERTY(BlueprintReadOnly, Category = "Zeus|World") - uint8 State = 0; -}; diff --git a/Source/ZMMO/Game/UI/InGame/UIInGameFlowSubsystem.cpp b/Source/ZMMO/Game/UI/InGame/UIInGameFlowSubsystem.cpp index cb16f33..bd4a318 100644 --- a/Source/ZMMO/Game/UI/InGame/UIInGameFlowSubsystem.cpp +++ b/Source/ZMMO/Game/UI/InGame/UIInGameFlowSubsystem.cpp @@ -81,16 +81,26 @@ void UUIInGameFlowSubsystem::SetState(EZMMOInGameUIState NewState) // (Status/Inventory/Menu) limpando o Layer.GameMenu — HUD em Layer.Game // permanece. Para qualquer outro estado, mantemos HUD e empilhamos no // layer correspondente. + const bool bGoingToPlaying = (NewState == EZMMOInGameUIState::Playing); if (UUIManagerSubsystem* Mgr = GetUIManager()) { - const bool bGoingToPlaying = (NewState == EZMMOInGameUIState::Playing); if (bGoingToPlaying) { Mgr->ClearLayer(ZMMOUITags::UI_Layer_GameMenu.GetTag()); } } - ResolveAndPushScreen(NewState); + // Skip re-push do HUD quando ja' estavamos in-game (Old != None significa + // que StartInGame ja' rodou e o HUD esta no Layer.Game). Senao, cada vez + // que o usuario fechar StatusWindow/Inventory/etc, o HUD seria recriado + // — flicker em todos os textos (HP/SP/etc) por causa do Construct novo. + const bool bSkipRepush = bGoingToPlaying + && OldState != EZMMOInGameUIState::None + && OldState != EZMMOInGameUIState::Playing; + if (!bSkipRepush) + { + ResolveAndPushScreen(NewState); + } OnStateChanged.Broadcast(NewState); } diff --git a/Source/ZMMO/Game/UI/InGame/ZMMOHudPlayerVitalsWidget.cpp b/Source/ZMMO/Game/UI/InGame/ZMMOHudPlayerVitalsWidget.cpp new file mode 100644 index 0000000..f945626 --- /dev/null +++ b/Source/ZMMO/Game/UI/InGame/ZMMOHudPlayerVitalsWidget.cpp @@ -0,0 +1,121 @@ +#include "ZMMOHudPlayerVitalsWidget.h" + +#include "Components/RichTextBlock.h" +#include "Components/TextBlock.h" +#include "UI/Widgets/UIProgressBar_Base.h" +#include "Internationalization/Text.h" + +namespace +{ + float SafeRatio(int32 Current, int32 Max) + { + return Max > 0 ? FMath::Clamp(static_cast(Current) / static_cast(Max), 0.f, 1.f) : 0.f; + } + + /** Ratio 0..1 pras barras de EXP/JOB (int64). ToNext<=0 = cap -> barra cheia. */ + float ExpRatio(int64 Cur, int64 ToNext) + { + return ToNext > 0 + ? FMath::Clamp(static_cast(static_cast(Cur) / static_cast(ToNext)), 0.f, 1.f) + : 1.f; + } + + /** Readout de progressao em "%": numero (Default) + "%" (unit/cinza). + * Cap (ToNext<=0) -> "MAX". Markup resolve no DT_UI_VitalsReadout. */ + FText ExpReadoutText(int64 Cur, int64 ToNext) + { + if (ToNext <= 0) { return FText::FromString(TEXT("MAX")); } + const double Pct = static_cast(Cur) / static_cast(ToNext) * 100.0; + return FText::FromString(FString::Printf(TEXT("%.2f%%"), Pct)); + } +} + +void UZMMOHudPlayerVitalsWidget::ApplySnapshot(const FZMMOAttributesSnapshot& Snapshot) +{ + LastSnapshot = Snapshot; + + // HP / SP (pool efetivo — % real) + ApplyHpSp(Snapshot.Hp, Snapshot.MaxHp, Snapshot.Sp, Snapshot.MaxSp); + + // Level — markup: "LVL " (pct/cinza) + número (num/gold-bright maior) + if (LvlText) + { + LvlText->SetText(FText::FromString( + FString::Printf(TEXT("LVL %d"), Snapshot.BaseLevel))); + } + + // BASE / JOB EXP — barras em % (exp atual / exp-to-next, resolvido pelo + // server via JobsDatabase). Em cap (ToNext=0) a barra enche e o readout + // vira "MAX". Readout exibe a porcentagem (numero + "%" cinza). + if (ExpBar) + { + ExpBar->SetTargetPrimaryLevel(ExpRatio(Snapshot.BaseExp, Snapshot.BaseExpToNext)); + } + if (JobBar) + { + JobBar->SetTargetPrimaryLevel(ExpRatio(Snapshot.JobExp, Snapshot.JobExpToNext)); + } + if (ExpReadout) + { + ExpReadout->SetText(ExpReadoutText(Snapshot.BaseExp, Snapshot.BaseExpToNext)); + } + if (JobReadout) + { + JobReadout->SetText(ExpReadoutText(Snapshot.JobExp, Snapshot.JobExpToNext)); + } + + OnSnapshotApplied(Snapshot); +} + +void UZMMOHudPlayerVitalsWidget::ApplyHpSp(int32 Hp, int32 MaxHp, int32 Sp, int32 MaxSp) +{ + LastSnapshot.Hp = Hp; + LastSnapshot.MaxHp = MaxHp; + LastSnapshot.Sp = Sp; + LastSnapshot.MaxSp = MaxSp; + + if (HpBar) + { + HpBar->SetTargetPrimaryLevel(SafeRatio(Hp, MaxHp)); + } + if (SpBar) + { + SpBar->SetTargetPrimaryLevel(SafeRatio(Sp, MaxSp)); + } + // Markup RichText: atual (Default/branco) + "/" (sep/escuro) + max (max/cinza). + // Tags resolvem no DT_UI_VitalsReadout (TextStyleSet do RichTextBlock). + auto MakeReadout = [](int32 Cur, int32 Max) -> FText + { + return FText::FromString(FString::Printf(TEXT("%s/%s"), + *FText::AsNumber(Cur).ToString(), *FText::AsNumber(Max).ToString())); + }; + if (HpReadout) + { + HpReadout->SetText(MakeReadout(Hp, MaxHp)); + } + if (SpReadout) + { + SpReadout->SetText(MakeReadout(Sp, MaxSp)); + } +} + +void UZMMOHudPlayerVitalsWidget::SetCharName(const FString& InName) +{ + if (NameText) + { + NameText->SetText(FText::FromString(InName)); + } +} + +void UZMMOHudPlayerVitalsWidget::SetClassName(const FString& InClassName) +{ + if (ClassText) + { + ClassText->SetText(FText::FromString(InClassName)); + } +} + +void UZMMOHudPlayerVitalsWidget::OnSnapshotApplied_Implementation(const FZMMOAttributesSnapshot& /*Snapshot*/) +{ + // Default vazio — skin do WBP estende se quiser (low-HP flash, etc.). +} diff --git a/Source/ZMMO/Game/UI/InGame/ZMMOHudPlayerVitalsWidget.h b/Source/ZMMO/Game/UI/InGame/ZMMOHudPlayerVitalsWidget.h new file mode 100644 index 0000000..471197b --- /dev/null +++ b/Source/ZMMO/Game/UI/InGame/ZMMOHudPlayerVitalsWidget.h @@ -0,0 +1,93 @@ +#pragma once + +#include "CommonUserWidget.h" +#include "CoreMinimal.h" +#include "ZMMOAttributeTypes.h" +#include "ZMMOHudPlayerVitalsWidget.generated.h" + +class UUIProgressBar_Base; +class UTextBlock; +class URichTextBlock; + +/** + * Sub-widget do HUD: painel de vitais do player (portrait + nome/job/level + + * barras HP/SP/BASE EXP/JOB EXP). Sem auto-bind no AttributeComponent — o + * parent (UZMMOHudWidget) propaga snapshots via ApplySnapshot/ApplyHpSp. + * + * Barras são UUIProgressBar_Base (material custom Primary/Secondary). HP/SP + * têm pool efetivo no snapshot → % real. BASE/JOB EXP só têm exp acumulado + * (sem "exp-to-next") — barras de EXP ficam pra quando a curva existir; + * por ora o readout mostra o valor absoluto. + * + * WBP filho (WBP_PlayerVitalsPanel) deve ter widgets com EXATAMENTE estes + * nomes: HpBar, SpBar (obrigatórios); ExpBar, JobBar, NameText, ClassText, + * LvlText, HpReadout, SpReadout, ExpReadout, JobReadout (opcionais). + */ +UCLASS(Abstract, Blueprintable, BlueprintType) +class ZMMO_API UZMMOHudPlayerVitalsWidget : public UCommonUserWidget +{ + GENERATED_BODY() + +public: + /** Atualiza todos os campos a partir do snapshot. Chamado pelo UZMMOHudWidget. */ + UFUNCTION(BlueprintCallable, Category = "Zeus|HUD") + void ApplySnapshot(const FZMMOAttributesSnapshot& Snapshot); + + /** Atualiza apenas HP/SP (otimização do tick — sem mexer em level/exp). */ + UFUNCTION(BlueprintCallable, Category = "Zeus|HUD") + void ApplyHpSp(int32 Hp, int32 MaxHp, int32 Sp, int32 MaxSp); + + /** Nome do char — vem do PlayerState (não está no snapshot). */ + UFUNCTION(BlueprintCallable, Category = "Zeus|HUD") + void SetCharName(const FString& InName); + + /** Nome do job/classe — vem do JobsDatabase via ClassId (não é string no snapshot). */ + UFUNCTION(BlueprintCallable, Category = "Zeus|HUD") + void SetClassName(const FString& InClassName); + +protected: + /** Hook BP pra skin reagir (cores low-HP, animações, etc.). */ + UFUNCTION(BlueprintNativeEvent, Category = "Zeus|HUD") + void OnSnapshotApplied(const FZMMOAttributesSnapshot& Snapshot); + virtual void OnSnapshotApplied_Implementation(const FZMMOAttributesSnapshot& Snapshot); + + // === BindWidget — barras (HP/SP obrigatórias) === + UPROPERTY(BlueprintReadOnly, meta = (BindWidget), Category = "Zeus|HUD") + TObjectPtr HpBar; + + UPROPERTY(BlueprintReadOnly, meta = (BindWidget), Category = "Zeus|HUD") + TObjectPtr SpBar; + + UPROPERTY(BlueprintReadOnly, meta = (BindWidgetOptional), Category = "Zeus|HUD") + TObjectPtr ExpBar; + + UPROPERTY(BlueprintReadOnly, meta = (BindWidgetOptional), Category = "Zeus|HUD") + TObjectPtr JobBar; + + // === BindWidget — textos (todos opcionais) === + UPROPERTY(BlueprintReadOnly, meta = (BindWidgetOptional), Category = "Zeus|HUD") + TObjectPtr NameText; + + UPROPERTY(BlueprintReadOnly, meta = (BindWidgetOptional), Category = "Zeus|HUD") + TObjectPtr ClassText; + + UPROPERTY(BlueprintReadOnly, meta = (BindWidgetOptional), Category = "Zeus|HUD") + TObjectPtr LvlText; + + UPROPERTY(BlueprintReadOnly, meta = (BindWidgetOptional), Category = "Zeus|HUD") + TObjectPtr HpReadout; + + UPROPERTY(BlueprintReadOnly, meta = (BindWidgetOptional), Category = "Zeus|HUD") + TObjectPtr SpReadout; + + UPROPERTY(BlueprintReadOnly, meta = (BindWidgetOptional), Category = "Zeus|HUD") + TObjectPtr ExpReadout; + + UPROPERTY(BlueprintReadOnly, meta = (BindWidgetOptional), Category = "Zeus|HUD") + TObjectPtr JobReadout; + +private: + /** Último snapshot recebido (cache pra ApplyHpSp manter MaxHp/MaxSp). */ + UPROPERTY(Transient) + FZMMOAttributesSnapshot LastSnapshot; +}; diff --git a/Source/ZMMO/Game/UI/InGame/ZMMOHudWidget.cpp b/Source/ZMMO/Game/UI/InGame/ZMMOHudWidget.cpp index 1e7c69e..21a8d2b 100644 --- a/Source/ZMMO/Game/UI/InGame/ZMMOHudWidget.cpp +++ b/Source/ZMMO/Game/UI/InGame/ZMMOHudWidget.cpp @@ -10,6 +10,9 @@ #include "ZMMO.h" #include "ZMMOAttributeComponent.h" #include "ZMMOHudHpSpWidget.h" +#include "ZMMOHudPlayerVitalsWidget.h" +#include "ZMMOJobsLibrary.h" +#include "ZMMOPlayerState.h" UZMMOHudWidget::UZMMOHudWidget(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) @@ -98,12 +101,28 @@ void UZMMOHudWidget::UnbindFromComponent() void UZMMOHudWidget::HandleAttributesChanged(const FZMMOAttributesSnapshot& Snapshot) { - // Propaga snapshot aos sub-widgets que ja existem. Em V1 so' HpSpBar. + // Propaga snapshot aos sub-widgets que ja existem. if (HpSpBar) { HpSpBar->ApplySnapshot(Snapshot); } - // Futuros: PlayerInfo->ApplySnapshot, Buffs->RefreshIcons, etc. + if (PlayerVitals) + { + PlayerVitals->ApplySnapshot(Snapshot); + + // Identidade: nome vem do PlayerState (não está no snapshot); job vem + // do JobsSubsystem resolvendo ClassId -> display name. + if (UZMMOAttributeComponent* Comp = BoundComponent.Get()) + { + if (const AZMMOPlayerState* PS = Cast(Comp->GetOwner())) + { + PlayerVitals->SetCharName(PS->CharName); + } + } + PlayerVitals->SetClassName( + UZMMOJobsLibrary::GetJobDisplayName(this, Snapshot.ClassId).ToString()); + } + // Futuros: Buffs->RefreshIcons, etc. } void UZMMOHudWidget::HandleHpSpChanged(int32 Hp, int32 Sp) @@ -115,6 +134,10 @@ void UZMMOHudWidget::HandleHpSpChanged(int32 Hp, int32 Sp) { HpSpBar->ApplyHpSp(Hp, Snap.MaxHp, Sp, Snap.MaxSp); } + if (PlayerVitals) + { + PlayerVitals->ApplyHpSp(Hp, Snap.MaxHp, Sp, Snap.MaxSp); + } } } diff --git a/Source/ZMMO/Game/UI/InGame/ZMMOHudWidget.h b/Source/ZMMO/Game/UI/InGame/ZMMOHudWidget.h index 39f3553..d53b532 100644 --- a/Source/ZMMO/Game/UI/InGame/ZMMOHudWidget.h +++ b/Source/ZMMO/Game/UI/InGame/ZMMOHudWidget.h @@ -10,6 +10,7 @@ class UTextBlock; class UWidget; class UZMMOAttributeComponent; class UZMMOHudHpSpWidget; +class UZMMOHudPlayerVitalsWidget; /** * Root da HUD de gameplay. UCommonActivatableWidget pushed pelo @@ -74,6 +75,12 @@ protected: UPROPERTY(BlueprintReadOnly, meta = (BindWidgetOptional), Category = "Zeus|HUD") UZMMOHudHpSpWidget* HpSpBar = nullptr; + /// Painel de vitais novo (portrait + nome/job/level + HP/SP/EXP/JOB bars). + /// Substitui o HpSpBar legado. BindWidgetOptional — coexiste durante a + /// migração; o WBP_HUD pode ter um ou outro. + UPROPERTY(BlueprintReadOnly, meta = (BindWidgetOptional), Category = "Zeus|HUD") + UZMMOHudPlayerVitalsWidget* PlayerVitals = nullptr; + /// Container do toast "LEVEL UP!" (Fase 3). Hidden por default; em /// `HandleLevelUp` vira visivel por `LevelUpToastDurationSec` segundos /// (ver `Show/HideLevelUpToast`). WBP_HUD coloca um Border + Image + TextBlock diff --git a/Source/ZMMO/Game/UI/InGame/ZMMOStatusWindowWidget.cpp b/Source/ZMMO/Game/UI/InGame/ZMMOStatusWindowWidget.cpp deleted file mode 100644 index c100af9..0000000 --- a/Source/ZMMO/Game/UI/InGame/ZMMOStatusWindowWidget.cpp +++ /dev/null @@ -1,169 +0,0 @@ -#include "ZMMOStatusWindowWidget.h" - -#include "CommonInputTypeEnum.h" -#include "Components/Button.h" -#include "Components/TextBlock.h" -#include "Engine/World.h" -#include "GameFramework/PlayerController.h" -#include "GameFramework/PlayerState.h" -#include "Internationalization/Text.h" -#include "ZMMO.h" -#include "ZMMOAttributeComponent.h" -#include "UI/InGame/UIInGameFlowSubsystem.h" -#include "UI/InGameTypes.h" - -UZMMOStatusWindowWidget::UZMMOStatusWindowWidget(const FObjectInitializer& ObjectInitializer) - : Super(ObjectInitializer) -{ - bIsBackHandler = true; // Esc/B fecha - bAutoActivate = true; -} - -TOptional UZMMOStatusWindowWidget::GetDesiredInputConfig() const -{ - // Modo Menu: cursor visivel, input flui pra UI (clicks nos botões), - // movimento bloqueado. Padrao pra menus modais. - return FUIInputConfig(ECommonInputMode::Menu, EMouseCaptureMode::NoCapture); -} - -void UZMMOStatusWindowWidget::NativeOnActivated() -{ - Super::NativeOnActivated(); - BindToLocalPlayer(); - - if (PlusBtn_Str) PlusBtn_Str->OnClicked.AddDynamic(this, &UZMMOStatusWindowWidget::OnPlusStr); - if (PlusBtn_Agi) PlusBtn_Agi->OnClicked.AddDynamic(this, &UZMMOStatusWindowWidget::OnPlusAgi); - if (PlusBtn_Vit) PlusBtn_Vit->OnClicked.AddDynamic(this, &UZMMOStatusWindowWidget::OnPlusVit); - if (PlusBtn_Int) PlusBtn_Int->OnClicked.AddDynamic(this, &UZMMOStatusWindowWidget::OnPlusInt); - if (PlusBtn_Dex) PlusBtn_Dex->OnClicked.AddDynamic(this, &UZMMOStatusWindowWidget::OnPlusDex); - if (PlusBtn_Luk) PlusBtn_Luk->OnClicked.AddDynamic(this, &UZMMOStatusWindowWidget::OnPlusLuk); - if (CloseBtn) CloseBtn->OnClicked.AddDynamic(this, &UZMMOStatusWindowWidget::OnCloseClicked); - - UE_LOG(LogZMMO, Log, TEXT("ZMMOStatusWindow activated")); -} - -void UZMMOStatusWindowWidget::NativeOnDeactivated() -{ - UnbindFromComponent(); - - if (PlusBtn_Str) PlusBtn_Str->OnClicked.RemoveDynamic(this, &UZMMOStatusWindowWidget::OnPlusStr); - if (PlusBtn_Agi) PlusBtn_Agi->OnClicked.RemoveDynamic(this, &UZMMOStatusWindowWidget::OnPlusAgi); - if (PlusBtn_Vit) PlusBtn_Vit->OnClicked.RemoveDynamic(this, &UZMMOStatusWindowWidget::OnPlusVit); - if (PlusBtn_Int) PlusBtn_Int->OnClicked.RemoveDynamic(this, &UZMMOStatusWindowWidget::OnPlusInt); - if (PlusBtn_Dex) PlusBtn_Dex->OnClicked.RemoveDynamic(this, &UZMMOStatusWindowWidget::OnPlusDex); - if (PlusBtn_Luk) PlusBtn_Luk->OnClicked.RemoveDynamic(this, &UZMMOStatusWindowWidget::OnPlusLuk); - if (CloseBtn) CloseBtn->OnClicked.RemoveDynamic(this, &UZMMOStatusWindowWidget::OnCloseClicked); - - Super::NativeOnDeactivated(); -} - -void UZMMOStatusWindowWidget::BindToLocalPlayer() -{ - UWorld* World = GetWorld(); - if (!World) { return; } - APlayerController* PC = World->GetFirstPlayerController(); - if (!PC || !PC->PlayerState) { return; } - UZMMOAttributeComponent* Comp = PC->PlayerState->FindComponentByClass(); - if (!Comp || Comp == BoundComponent.Get()) { return; } - - UnbindFromComponent(); - BoundComponent = Comp; - Comp->OnAttributesChanged.AddDynamic(this, &UZMMOStatusWindowWidget::HandleAttributesChanged); - Comp->OnStatAllocReply.AddDynamic(this, &UZMMOStatusWindowWidget::HandleStatAllocReply); - RefreshFromSnapshot(Comp->GetSnapshot()); -} - -void UZMMOStatusWindowWidget::UnbindFromComponent() -{ - if (UZMMOAttributeComponent* Old = BoundComponent.Get()) - { - Old->OnAttributesChanged.RemoveDynamic(this, &UZMMOStatusWindowWidget::HandleAttributesChanged); - Old->OnStatAllocReply.RemoveDynamic(this, &UZMMOStatusWindowWidget::HandleStatAllocReply); - } - BoundComponent.Reset(); -} - -void UZMMOStatusWindowWidget::HandleAttributesChanged(const FZMMOAttributesSnapshot& Snapshot) -{ - RefreshFromSnapshot(Snapshot); -} - -void UZMMOStatusWindowWidget::HandleStatAllocReply(bool bAccepted, int32 Reason) -{ - UE_LOG(LogZMMO, Log, TEXT("StatusWindow: alloc reply accepted=%d reason=%d"), - bAccepted ? 1 : 0, Reason); - // Snapshot subsequente vai disparar HandleAttributesChanged e atualizar UI. - // Aqui poderia popup de erro visual quando bAccepted=false (TODO polish). -} - -void UZMMOStatusWindowWidget::RefreshFromSnapshot(const FZMMOAttributesSnapshot& S) -{ - if (StrText) StrText->SetText(FText::FromString(FString::Printf(TEXT("STR %d"), S.Str))); - if (AgiText) AgiText->SetText(FText::FromString(FString::Printf(TEXT("AGI %d"), S.Agi))); - if (VitText) VitText->SetText(FText::FromString(FString::Printf(TEXT("VIT %d"), S.Vit))); - if (IntText) IntText->SetText(FText::FromString(FString::Printf(TEXT("INT %d"), S.Int))); - if (DexText) DexText->SetText(FText::FromString(FString::Printf(TEXT("DEX %d"), S.Dex))); - if (LukText) LukText->SetText(FText::FromString(FString::Printf(TEXT("LUK %d"), S.Luk))); - if (StatusPointText) - { - StatusPointText->SetText(FText::FromString( - FString::Printf(TEXT("Status Points: %d"), S.StatusPoint))); - } - - // Derivados — formato RO "STAT base + equip" (ex.: "ATK 51 + 0"). - // Servidor manda os dois separados; cliente NUNCA recalcula. - if (AtkText) AtkText->SetText(FText::FromString(FString::Printf(TEXT("ATK %d + %d"), S.AtkBase, S.AtkEquipBonus))); - // MATK pre-renewal: range "min ~ max + equip" (rathena status_base_matk_min/max). - if (MatkText) MatkText->SetText(FText::FromString(FString::Printf(TEXT("MATK %d ~ %d + %d"), S.MatkBaseMin, S.MatkBaseMax, S.MatkEquipBonus))); - if (DefText) DefText->SetText(FText::FromString(FString::Printf(TEXT("DEF %d + %d"), S.DefBase, S.DefEquipBonus))); - if (MdefText) MdefText->SetText(FText::FromString(FString::Printf(TEXT("MDEF %d + %d"), S.MdefBase, S.MdefEquipBonus))); - if (HitText) HitText->SetText(FText::FromString(FString::Printf(TEXT("HIT %d + %d"), S.HitBase, S.HitEquipBonus))); - if (FleeText) FleeText->SetText(FText::FromString(FString::Printf(TEXT("FLEE %d + %d"), S.FleeBase, S.FleeEquipBonus))); - if (CritText) - { - // CRIT vem ×10 em ambas as camadas — soma X10 antes de converter pra - // decimal (10 = 1.0). Display: "CRIT base + equip" com 1 casa cada. - const int32 BaseInt = S.CritBaseX10 / 10; - const int32 BaseFrac = S.CritBaseX10 % 10; - const int32 EquipInt = S.CritEquipBonusX10 / 10; - const int32 EquipFrac = S.CritEquipBonusX10 % 10; - CritText->SetText(FText::FromString(FString::Printf( - TEXT("CRIT %d.%d + %d.%d"), BaseInt, BaseFrac, EquipInt, EquipFrac))); - } - // ASPD nao splita (calculo nao-linear server-side via aspd_base × stats). - if (AspdText) AspdText->SetText(FText::FromString(FString::Printf(TEXT("ASPD %d"), S.Aspd))); - // Habilita botões `+` somente se ha status_point disponível. - const bool bCanAlloc = S.StatusPoint > 0; - if (PlusBtn_Str) PlusBtn_Str->SetIsEnabled(bCanAlloc); - if (PlusBtn_Agi) PlusBtn_Agi->SetIsEnabled(bCanAlloc); - if (PlusBtn_Vit) PlusBtn_Vit->SetIsEnabled(bCanAlloc); - if (PlusBtn_Int) PlusBtn_Int->SetIsEnabled(bCanAlloc); - if (PlusBtn_Dex) PlusBtn_Dex->SetIsEnabled(bCanAlloc); - if (PlusBtn_Luk) PlusBtn_Luk->SetIsEnabled(bCanAlloc); -} - -void UZMMOStatusWindowWidget::RequestAlloc(int32 StatId) -{ - if (UZMMOAttributeComponent* Comp = BoundComponent.Get()) - { - Comp->RequestStatAlloc(StatId, /*Amount*/ 1); - } -} - -void UZMMOStatusWindowWidget::OnPlusStr() { RequestAlloc(0); } -void UZMMOStatusWindowWidget::OnPlusAgi() { RequestAlloc(1); } -void UZMMOStatusWindowWidget::OnPlusVit() { RequestAlloc(2); } -void UZMMOStatusWindowWidget::OnPlusInt() { RequestAlloc(3); } -void UZMMOStatusWindowWidget::OnPlusDex() { RequestAlloc(4); } -void UZMMOStatusWindowWidget::OnPlusLuk() { RequestAlloc(5); } - -void UZMMOStatusWindowWidget::OnCloseClicked() -{ - if (UGameInstance* GI = GetGameInstance()) - { - if (UUIInGameFlowSubsystem* Flow = GI->GetSubsystem()) - { - Flow->SetState(EZMMOInGameUIState::Playing); - } - } -} diff --git a/Source/ZMMO/Game/UI/InGame/ZMMOStatusWindowWidget.h b/Source/ZMMO/Game/UI/InGame/ZMMOStatusWindowWidget.h deleted file mode 100644 index af8bcef..0000000 --- a/Source/ZMMO/Game/UI/InGame/ZMMOStatusWindowWidget.h +++ /dev/null @@ -1,106 +0,0 @@ -#pragma once - -#include "CommonActivatableWidget.h" -#include "CoreMinimal.h" -#include "ZMMOAttributeTypes.h" -#include "ZMMOStatusWindowWidget.generated.h" - -class UButton; -class UTextBlock; -class UCommonButtonBase; -class UZMMOAttributeComponent; - -/** - * Tela "Status" — atributos do jogador + alocacao via botões `+`. - * - * Modo Menu: cursor visível, movimento bloqueado, Esc fecha. Push em - * UI.Layer.GameMenu pelo `UUIInGameFlowSubsystem::ToggleScreen(StatusWindow)`. - * HUD continua visível em UI.Layer.Game por baixo. - * - * Composicao do WBP_StatusWindow (BindWidgetOptional pra permitir variantes): - * - Labels de stats primarios: StrText / AgiText / VitText / IntText / DexText / LukText - * - Botões +: PlusBtn_Str / PlusBtn_Agi / PlusBtn_Vit / PlusBtn_Int / PlusBtn_Dex / PlusBtn_Luk - * - Derivados (server-authoritative): AtkText / MatkText / DefText / MdefText / - * HitText / FleeText / CritText / AspdText - * - StatusPointText (pontos disponíveis pra alocar) - * - CloseBtn (fecha — fallback do Esc/back handler) - * - * Workflow: - * 1. NativeOnActivated: busca AttributeComponent (PlayerState do player local) - * 2. Subscreve OnAttributesChanged + OnStatAllocReply - * 3. Refresh inicial via snapshot atual - * 4. Clique no `+X` chama `Comp->RequestStatAlloc(X, 1)` → server valida - * 5. Server responde com S_ATTRIBUTE_STAT_ALLOC_OK + (se ok) snapshot novo - * 6. UI atualiza via delegate - */ -UCLASS(Abstract, Blueprintable, BlueprintType) -class ZMMO_API UZMMOStatusWindowWidget : public UCommonActivatableWidget -{ - GENERATED_BODY() - -public: - UZMMOStatusWindowWidget(const FObjectInitializer& ObjectInitializer); - -protected: - virtual void NativeOnActivated() override; - virtual void NativeOnDeactivated() override; - - /// Modo Menu: cursor visível + bloqueia input de gameplay (movimento). - virtual TOptional GetDesiredInputConfig() const override; - - /// Texto dos stats (label + valor). BindWidgetOptional pra permitir - /// WBPs custom (ex.: sem display de SP em variantes). - UPROPERTY(BlueprintReadOnly, meta = (BindWidgetOptional), Category = "Zeus|Status") UTextBlock* StrText = nullptr; - UPROPERTY(BlueprintReadOnly, meta = (BindWidgetOptional), Category = "Zeus|Status") UTextBlock* AgiText = nullptr; - UPROPERTY(BlueprintReadOnly, meta = (BindWidgetOptional), Category = "Zeus|Status") UTextBlock* VitText = nullptr; - UPROPERTY(BlueprintReadOnly, meta = (BindWidgetOptional), Category = "Zeus|Status") UTextBlock* IntText = nullptr; - UPROPERTY(BlueprintReadOnly, meta = (BindWidgetOptional), Category = "Zeus|Status") UTextBlock* DexText = nullptr; - UPROPERTY(BlueprintReadOnly, meta = (BindWidgetOptional), Category = "Zeus|Status") UTextBlock* LukText = nullptr; - - /// Pontos restantes pra alocar + label "Status Points: N". - UPROPERTY(BlueprintReadOnly, meta = (BindWidgetOptional), Category = "Zeus|Status") UTextBlock* StatusPointText = nullptr; - - /// Derivados (sempre calculados pelo servidor — cliente NUNCA recalcula). - /// Mostra `base + bonus` quando aplicavel (camadas equip/buff vem do server - /// pre-somadas no snapshot). BindWidgetOptional pra suportar HUDs minimalistas. - UPROPERTY(BlueprintReadOnly, meta = (BindWidgetOptional), Category = "Zeus|Status|Derived") UTextBlock* AtkText = nullptr; - UPROPERTY(BlueprintReadOnly, meta = (BindWidgetOptional), Category = "Zeus|Status|Derived") UTextBlock* MatkText = nullptr; - UPROPERTY(BlueprintReadOnly, meta = (BindWidgetOptional), Category = "Zeus|Status|Derived") UTextBlock* DefText = nullptr; - UPROPERTY(BlueprintReadOnly, meta = (BindWidgetOptional), Category = "Zeus|Status|Derived") UTextBlock* MdefText = nullptr; - UPROPERTY(BlueprintReadOnly, meta = (BindWidgetOptional), Category = "Zeus|Status|Derived") UTextBlock* HitText = nullptr; - UPROPERTY(BlueprintReadOnly, meta = (BindWidgetOptional), Category = "Zeus|Status|Derived") UTextBlock* FleeText = nullptr; - UPROPERTY(BlueprintReadOnly, meta = (BindWidgetOptional), Category = "Zeus|Status|Derived") UTextBlock* CritText = nullptr; - UPROPERTY(BlueprintReadOnly, meta = (BindWidgetOptional), Category = "Zeus|Status|Derived") UTextBlock* AspdText = nullptr; - - /// Botoes + por stat. - UPROPERTY(BlueprintReadOnly, meta = (BindWidgetOptional), Category = "Zeus|Status") UButton* PlusBtn_Str = nullptr; - UPROPERTY(BlueprintReadOnly, meta = (BindWidgetOptional), Category = "Zeus|Status") UButton* PlusBtn_Agi = nullptr; - UPROPERTY(BlueprintReadOnly, meta = (BindWidgetOptional), Category = "Zeus|Status") UButton* PlusBtn_Vit = nullptr; - UPROPERTY(BlueprintReadOnly, meta = (BindWidgetOptional), Category = "Zeus|Status") UButton* PlusBtn_Int = nullptr; - UPROPERTY(BlueprintReadOnly, meta = (BindWidgetOptional), Category = "Zeus|Status") UButton* PlusBtn_Dex = nullptr; - UPROPERTY(BlueprintReadOnly, meta = (BindWidgetOptional), Category = "Zeus|Status") UButton* PlusBtn_Luk = nullptr; - - /// Botao Close (fallback do Esc/back). - UPROPERTY(BlueprintReadOnly, meta = (BindWidgetOptional), Category = "Zeus|Status") UButton* CloseBtn = nullptr; - -private: - void BindToLocalPlayer(); - void UnbindFromComponent(); - void RefreshFromSnapshot(const FZMMOAttributesSnapshot& Snap); - - UFUNCTION() void HandleAttributesChanged(const FZMMOAttributesSnapshot& Snapshot); - UFUNCTION() void HandleStatAllocReply(bool bAccepted, int32 Reason); - - UFUNCTION() void OnPlusStr(); - UFUNCTION() void OnPlusAgi(); - UFUNCTION() void OnPlusVit(); - UFUNCTION() void OnPlusInt(); - UFUNCTION() void OnPlusDex(); - UFUNCTION() void OnPlusLuk(); - UFUNCTION() void OnCloseClicked(); - - void RequestAlloc(int32 StatId); - - UPROPERTY(Transient) - TWeakObjectPtr BoundComponent; -}; diff --git a/Source/ZMMO/Game/UI/Widgets/UIButton_Base.cpp b/Source/ZMMO/Game/UI/Widgets/UIButton_Base.cpp index 51bb889..efb3288 100644 --- a/Source/ZMMO/Game/UI/Widgets/UIButton_Base.cpp +++ b/Source/ZMMO/Game/UI/Widgets/UIButton_Base.cpp @@ -1,434 +1,476 @@ #include "UIButton_Base.h" -#include "ZMMOThemeSubsystem.h" -#include "Engine/GameInstance.h" -#include "Engine/World.h" -#include "TimerManager.h" -#include "Components/Border.h" -#include "Styling/SlateBrush.h" -#include "Components/TextBlock.h" -#include "Components/Image.h" -#include "Components/SizeBox.h" -#include "Components/ScaleBox.h" -#include "Components/OverlaySlot.h" #include "CommonTextBlock.h" -#include "CommonActionWidget.h" +#include "Components/Image.h" +#include "Components/OverlaySlot.h" +#include "Engine/DataTable.h" +#include "Materials/MaterialInstanceDynamic.h" +#include "Materials/MaterialInterface.h" +#include "Styling/SlateBrush.h" +#include "Styling/SlateTypes.h" +#include "UObject/ConstructorHelpers.h" +#include "UI/UIStyleRow.h" +#include "UI/UIStyleTokens.h" namespace { - /** Resolve o FUIStyle ativo; fallback p/ defaults (preview de designer). */ - const FUIStyle& ResolveActiveStyle(const UUserWidget* Widget, const FUIStyle& Fallback) + constexpr float MinTransition = 0.001f; + + // Params do material UI_M_Button (Bg). + static const FName P_TopColour("TopColour"); + static const FName P_BottomColour("BottomColour"); + static const FName P_GlowColour("GlowColour"); + static const FName P_GlowIntensity("GlowIntensity"); + static const FName P_CornerColour("CornerColour"); + static const FName P_CornerLength("CornerLength"); + static const FName P_CornerThickness("CornerThickness"); + static const FName P_FrameColour("FrameColour"); + static const FName P_AspectRatio("AspectRatio"); + static const FName P_GlowEdge("GlowEdge"); + static const FName P_GlowSoftness("GlowSoftness"); + + FORCEINLINE float EaseOutCubic(float Alpha) { - if (Widget) - { - if (const UGameInstance* GI = Widget->GetGameInstance()) - { - if (const UZMMOThemeSubsystem* Theme = GI->GetSubsystem()) - { - return Theme->GetActiveUIStyle(); - } - } - } - return Fallback; // sem GameInstance (design-time): Aurora Arcana default + return 1.f - FMath::Pow(1.f - Alpha, 3.f); } } -FUIStyleButtonVariant UUIButton_Base::ResolveVariant(const FUIStyleButton& Button) const +UUIButton_Base::UUIButton_Base(const FObjectInitializer& ObjectInitializer) + : Super(ObjectInitializer) { - // Compõe FLAT a partir dos 3 sub-mapas do tema (Backgrounds/Strokes/Fonts). - // Fallback inline → Defaults (constructor de FUIStyleButton popula - // Primary/Secondary/Danger/Ghost com defaults Aurora Arcana — GC-safe). - static const FUIStyleButton Defaults; - FUIStyleButtonVariant V; + // Materiais default (fallback quando o brush da imagem não traz material). + static ConstructorHelpers::FObjectFinder BgMatFinder( + TEXT("/Game/ZMMO/UI/Themes/Default/Button/UI_M_Button.UI_M_Button")); + if (BgMatFinder.Succeeded()) { BgMaterial = BgMatFinder.Object; } - const FUIStyleButtonBackground* BG = Button.Backgrounds.Find(Variant); - if (!BG) { BG = Defaults.Backgrounds.Find(Variant); } - if (!BG) { BG = Defaults.Backgrounds.Find(TEXT("Primary")); } - if (BG) - { - V.BgNormal = BG->BgNormal; - V.BgHover = BG->BgHover; - V.BgPressed = BG->BgPressed; - V.BgDisabled = BG->BgDisabled; - V.BackgroundTexture = BG->BackgroundTexture; - V.BackgroundMargin = BG->BackgroundMargin; - } + static ConstructorHelpers::FObjectFinder GlowMatFinder( + TEXT("/Game/ZMMO/UI/Themes/Default/Button/UI_M_Button_Glow.UI_M_Button_Glow")); + if (GlowMatFinder.Succeeded()) { GlowMaterial = GlowMatFinder.Object; } - const FUIStyleButtonStroke* ST = Button.Strokes.Find(Variant); - if (!ST) { ST = Defaults.Strokes.Find(Variant); } - if (!ST) { ST = Defaults.Strokes.Find(TEXT("Primary")); } - if (ST) - { - V.BorderNormal = ST->BorderNormal; - V.BorderHover = ST->BorderHover; - V.BorderWidth = ST->BorderWidth; - V.CornerRadius = ST->CornerRadius; - } + static ConstructorHelpers::FObjectFinder CornerMatFinder( + TEXT("/Game/ZMMO/UI/Themes/Default/Button/UI_M_CornerBrackets.UI_M_CornerBrackets")); + if (CornerMatFinder.Succeeded()) { CornerMaterial = CornerMatFinder.Object; } - const FUIStyleButtonFont* F = Button.Fonts.Find(Variant); - if (!F) { F = Defaults.Fonts.Find(Variant); } - if (!F) { F = Defaults.Fonts.Find(TEXT("Primary")); } - if (F) - { - V.TextColor = F->TextColor; - V.TextStyle = F->TextStyle; - } - return V; -} + static ConstructorHelpers::FObjectFinder FrameMatFinder( + TEXT("/Game/ZMMO/UI/Themes/Default/Button/UI_M_Button_Frame.UI_M_Button_Frame")); + if (FrameMatFinder.Succeeded()) { FrameMaterial = FrameMatFinder.Object; } -TArray UUIButton_Base::GetVariantOptions() const -{ - TArray Options; - for (const TCHAR* N : { TEXT("Primary"), TEXT("Secondary"), TEXT("Danger"), TEXT("Ghost") }) - { - Options.Add(N); - } - if (const UDataTable* DT = LoadObject(nullptr, - TEXT("/Game/ZMMO/Data/UI/DT_UI_Styles.DT_UI_Styles"))) - { - if (const FUIStyleRow* Row = DT->FindRow( - FName(TEXT("Default")), TEXT("UIButtonVariantOptions"), false)) - { - for (const TPair& P : Row->Style.Button.Backgrounds) { Options.AddUnique(P.Key.ToString()); } - for (const TPair& P : Row->Style.Button.Strokes) { Options.AddUnique(P.Key.ToString()); } - for (const TPair& P : Row->Style.Button.Fonts) { Options.AddUnique(P.Key.ToString()); } - } - } - return Options; -} + // Defaults = btn-primary (buttons_showcase.html). Cores em valores sRGB + // diretos (mesma convenção do UUIProgressBar_Base — UI sem gamma). -// ---- Hyper "Set Button Text" ---- -void UUIButton_Base::SetButtonText(FText InText) -{ - if (!ButtonText) - { - return; - } - const FText Final = bUppercaseText - ? FText::FromString(InText.ToString().ToUpper()) - : InText; - ButtonText->SetText(Final); -} + // Default (repouso) + DefaultVisual.TopColour = FLinearColor(0.910f, 0.753f, 0.376f, 0.18f); + DefaultVisual.BottomColour = FLinearColor(0.659f, 0.471f, 0.188f, 0.10f); + DefaultVisual.BorderColour = FLinearColor(0.831f, 0.659f, 0.314f, 0.28f); + DefaultVisual.BorderWidth = 1.f; + DefaultVisual.GlowColour = FLinearColor(0.f, 0.f, 0.f, 0.5f); // drop shadow (escura) + DefaultVisual.GlowIntensity = 0.7f; + DefaultVisual.TextColour = FLinearColor(1.f, 0.847f, 0.533f, 1.f); + DefaultVisual.Scale = 1.f; + DefaultVisual.RenderOpacity = 1.f; -// ---- Hyper "Set Number Text" ---- -void UUIButton_Base::SetNumberText() -{ - if (!Number_Text) - { - return; - } - if (bShowNumberText) - { - Number_Text->SetText(FText::AsNumber(NumberTextAmount)); - Number_Text->SetVisibility(ESlateVisibility::Visible); - } - else - { - Number_Text->SetVisibility(ESlateVisibility::Hidden); - } -} + // Hover — gradient mais forte, borda gold, glow aceso, leve zoom. + HoveredVisual.TopColour = FLinearColor(0.910f, 0.753f, 0.376f, 0.32f); + HoveredVisual.BottomColour = FLinearColor(0.659f, 0.471f, 0.188f, 0.18f); + HoveredVisual.BorderColour = FLinearColor(0.910f, 0.753f, 0.439f, 1.f); + HoveredVisual.BorderWidth = 1.f; + HoveredVisual.GlowColour = FLinearColor(0.f, 0.f, 0.f, 0.55f); // sombra mais forte (elevado) + HoveredVisual.GlowIntensity = 0.9f; + HoveredVisual.TextColour = FLinearColor(1.f, 0.847f, 0.533f, 1.f); + HoveredVisual.Scale = 1.02f; + HoveredVisual.RenderOpacity = 1.f; -// ---- Hyper "Set Icon" ---- -void UUIButton_Base::SetIcon() -{ - if (bUseIconInsteadOfText) - { - if (Image_Icon) - { - if (IconImage) - { - Image_Icon->SetBrushResourceObject(IconImage); - } - Image_Icon->SetVisibility(ESlateVisibility::Visible); - } - if (ButtonText) - { - ButtonText->SetVisibility(ESlateVisibility::Collapsed); - } - } - else - { - if (Image_Icon) - { - Image_Icon->SetVisibility(ESlateVisibility::Collapsed); - } - if (ButtonText) - { - ButtonText->SetVisibility(ESlateVisibility::Visible); - } - } -} + // Pressed/Active — gradient invertido, encolhe. + PressedVisual.TopColour = FLinearColor(0.659f, 0.471f, 0.188f, 0.25f); + PressedVisual.BottomColour = FLinearColor(0.910f, 0.753f, 0.376f, 0.15f); + PressedVisual.BorderColour = FLinearColor(0.910f, 0.753f, 0.439f, 1.f); + PressedVisual.BorderWidth = 1.f; + PressedVisual.GlowColour = FLinearColor(0.f, 0.f, 0.f, 0.5f); // sombra menor (afundou) + PressedVisual.GlowIntensity = 0.5f; + PressedVisual.TextColour = FLinearColor(1.f, 0.847f, 0.533f, 1.f); + PressedVisual.Scale = 0.98f; + PressedVisual.RenderOpacity = 1.f; -// ---- Hyper "Set Sizebox Master Dimmensions" ---- -void UUIButton_Base::ApplySizeBox() -{ - if (bDoNotOverrideWithSizebox || !SizeBox_Master) - { - return; - } - SizeBox_Master->SetHeightOverride(InHeightOverride); - SizeBox_Master->SetWidthOverride(InWidthOverride); -} + // Disabled — quase apagado, texto escuro, opacity 0.6. + DisabledVisual.TopColour = FLinearColor(1.f, 1.f, 1.f, 0.02f); + DisabledVisual.BottomColour = FLinearColor(1.f, 1.f, 1.f, 0.02f); + DisabledVisual.BorderColour = FLinearColor(1.f, 1.f, 1.f, 0.06f); + DisabledVisual.BorderWidth = 1.f; + DisabledVisual.GlowColour = FLinearColor(0.f, 0.f, 0.f, 0.25f); // sombra fraca + DisabledVisual.GlowIntensity = 0.3f; + DisabledVisual.TextColour = FLinearColor(0.227f, 0.220f, 0.188f, 1.f); + DisabledVisual.Scale = 1.f; + DisabledVisual.RenderOpacity = 0.6f; -// ---- Hyper "Set InputAction Style" ---- -void UUIButton_Base::ApplyInputActionStyle() -{ - // InputActionWidget é privado em UCommonButtonBase (bind interno). Pega por - // nome — o widget do WBP chama-se "InputActionWidget" (bind do CommonUI). - UCommonActionWidget* IAW = Cast( - GetWidgetFromName(TEXT("InputActionWidget"))); - if (!IAW) - { - return; - } + // Selected (tabs/toggles) — gold suave persistente, sem zoom. + SelectedVisual.TopColour = FLinearColor(0.910f, 0.753f, 0.376f, 0.20f); + SelectedVisual.BottomColour = FLinearColor(0.659f, 0.471f, 0.188f, 0.12f); + SelectedVisual.BorderColour = FLinearColor(0.831f, 0.659f, 0.314f, 0.55f); + SelectedVisual.BorderWidth = 1.f; + SelectedVisual.GlowColour = FLinearColor(0.f, 0.f, 0.f, 0.5f); + SelectedVisual.GlowIntensity = 0.75f; + SelectedVisual.TextColour = FLinearColor(1.f, 0.847f, 0.533f, 1.f); + SelectedVisual.Scale = 1.f; + SelectedVisual.RenderOpacity = 1.f; - if (bShowInputActionWidget) - { - IAW->SetVisibility(ESlateVisibility::Visible); - IAW->SetIsEnabled(true); - } - else - { - IAW->SetVisibility(ESlateVisibility::Collapsed); - IAW->SetIsEnabled(false); - } - - if (UOverlaySlot* OS = Cast(IAW->Slot)) - { - OS->SetPadding(InputPadding); - OS->SetHorizontalAlignment(InputHorzAlignment); - OS->SetVerticalAlignment(InputVertAlignment); - } -} - -// ---- Hyper "Initialize Button" (orquestrador) ---- -void UUIButton_Base::InitializeButton() -{ - SetButtonText(ButtonTextValue); - SetNumberText(); - SetIcon(); - ApplySizeBox(); - RefreshUIStyle(); -} - -// ---- Hyper "Update Button Style" / "Set Brush" → nossa camada FUIStyle ---- -void UUIButton_Base::RefreshUIStyle() -{ - const FUIStyle Fallback; // defaults do struct = paleta Aurora Arcana - const FUIStyle& ActiveStyle = ResolveActiveStyle(this, Fallback); - const FUIStyleButtonVariant& VariantStyle = ResolveVariant(ActiveStyle.Button); - - // Fundo + borda no estado Normal (hover/pressed/disabled via NativeOn*). - ApplyVisualState(EUIButtonVisual::Normal); - - if (ButtonText) - { - // Tipografia: CommonTextStyle da variante (padrão Hyper/CommonUI). - if (VariantStyle.TextStyle) - { - ButtonText->SetStyle(VariantStyle.TextStyle); - } - // Cor: FUIStyle manda na cor por tema (sobrepõe o CommonTextStyle). - ButtonText->SetColorAndOpacity(FSlateColor(VariantStyle.TextColor)); - - // Alinhamento L/C/R dentro da largura do botão. - ButtonText->SetJustification(TextAlign); - } - - // Texto adapta ao tamanho do botão (ScaleToFit) ou usa tamanho fixo. - if (TextScaleBox) - { - TextScaleBox->SetStretch(bAutoSizeText ? EStretch::ScaleToFit : EStretch::None); - - // Posiciona o bloco de texto L/C/R dentro do botão (slot do Overlay). - if (UOverlaySlot* OS = Cast(TextScaleBox->Slot)) - { - EHorizontalAlignment H = HAlign_Center; - switch (TextAlign) - { - case ETextJustify::Left: H = HAlign_Left; break; - case ETextJustify::Right: H = HAlign_Right; break; - default: H = HAlign_Center; break; - } - OS->SetHorizontalAlignment(H); - OS->SetVerticalAlignment(VAlign_Center); - } - } - - BP_ApplyUIStyle(ActiveStyle.Button, VariantStyle); -} - -void UUIButton_Base::ApplyVisualState(EUIButtonVisual State) -{ - if (!Background) - { - return; - } - const FUIStyle Fallback; - const FUIStyle& AS = ResolveActiveStyle(this, Fallback); - const FUIStyleButtonVariant& V = ResolveVariant(AS.Button); - - FLinearColor Fill, Outline; - switch (State) - { - case EUIButtonVisual::Hovered: Fill = V.BgHover; Outline = V.BorderHover; break; - case EUIButtonVisual::Pressed: Fill = V.BgPressed; Outline = V.BorderHover; break; - case EUIButtonVisual::Disabled: Fill = V.BgDisabled; Outline = V.BorderNormal; break; - default: Fill = V.BgNormal; Outline = V.BorderNormal; break; - } - - FSlateBrush Brush = Background->Background; - Brush.TintColor = FSlateColor(Fill); - if (bRoundedBackground) - { - // CornerRadius/BorderWidth agora vêm da variante composta (Stroke). - const float R = V.CornerRadius; - Brush.DrawAs = ESlateBrushDrawType::RoundedBox; - Brush.OutlineSettings = FSlateBrushOutlineSettings( - FVector4(R, R, R, R), FSlateColor(Outline), V.BorderWidth); - Brush.OutlineSettings.RoundingType = ESlateBrushRoundingType::FixedRadius; - } - else - { - Brush.DrawAs = ESlateBrushDrawType::Box; - Brush.OutlineSettings = FSlateBrushOutlineSettings( - FVector4(0, 0, 0, 0), FSlateColor(Outline), V.BorderWidth); - } - Background->SetBrush(Brush); -} - -void UUIButton_Base::NativeOnHovered() -{ - Super::NativeOnHovered(); - ApplyVisualState(EUIButtonVisual::Hovered); -} - -void UUIButton_Base::NativeOnUnhovered() -{ - Super::NativeOnUnhovered(); - ApplyVisualState(EUIButtonVisual::Normal); -} - -void UUIButton_Base::NativeOnPressed() -{ - Super::NativeOnPressed(); - ApplyVisualState(EUIButtonVisual::Pressed); -} - -void UUIButton_Base::NativeOnReleased() -{ - Super::NativeOnReleased(); - ApplyVisualState(IsHovered() ? EUIButtonVisual::Hovered : EUIButtonVisual::Normal); -} - -void UUIButton_Base::NativeOnEnabled() -{ - Super::NativeOnEnabled(); - ApplyVisualState(EUIButtonVisual::Normal); -} - -void UUIButton_Base::NativeOnDisabled() -{ - Super::NativeOnDisabled(); - ApplyVisualState(EUIButtonVisual::Disabled); + CurrentVisual = DefaultVisual; } void UUIButton_Base::NativePreConstruct() { Super::NativePreConstruct(); - InitializeButton(); - ApplyInputActionStyle(); + + EnsureMID(); + + // Padding/centralização do texto via slot do Overlay (design-time + runtime). + if (ButtonText) + { + if (UOverlaySlot* OS = Cast(ButtonText->Slot)) + { + OS->SetPadding(ContentPadding); + OS->SetHorizontalAlignment(HAlign_Center); + OS->SetVerticalAlignment(VAlign_Center); + } + } + + // GlowFx (drop shadow): vaza pra fora via padding negativo + deslocado pra + // baixo por GlowOffsetY (a sombra aparece mais embaixo, estilo box-shadow). + if (GlowFx) + { + if (UOverlaySlot* OS = Cast(GlowFx->Slot)) + { + OS->SetPadding(FMargin(-GlowExtent, -GlowExtent + GlowOffsetY, + -GlowExtent, -GlowExtent - GlowOffsetY)); + OS->SetHorizontalAlignment(HAlign_Fill); + OS->SetVerticalAlignment(VAlign_Fill); + } + } + + // Corners puxado pra dentro (CornerInset>0) ou pra fora/abraçando (<0). + if (Corners) + { + if (UOverlaySlot* OS = Cast(Corners->Slot)) + { + OS->SetPadding(FMargin(CornerInset)); + OS->SetHorizontalAlignment(HAlign_Fill); + OS->SetVerticalAlignment(VAlign_Fill); + } + } + + CurrentVisual = DefaultVisual; + bTransitioning = false; + ApplyVisual(CurrentVisual); } void UUIButton_Base::NativeConstruct() { Super::NativeConstruct(); - if (!bThemeBound) - { - if (const UGameInstance* GI = GetGameInstance()) - { - if (UZMMOThemeSubsystem* Theme = GI->GetSubsystem()) - { - Theme->OnThemeChanged.AddDynamic(this, &UUIButton_Base::HandleThemeChanged); - bThemeBound = true; - } - } - } + EnsureMID(); + bHoveredState = bPressedState = bSelectedState = false; + bDisabledState = !GetIsEnabled(); - InitializeButton(); - ApplyInputActionStyle(); - - if (bDeactivatedOnConstructWithTimeout && Timeout > 0.f) - { - StartDeactivateTimeout(); - } + // Estado inicial sem animar. + const FUIButtonStateVisual& Target = + bDisabledState ? DisabledVisual + : bSelectedState ? SelectedVisual + : DefaultVisual; + CurrentVisual = Target; + bTransitioning = false; + ApplyVisual(CurrentVisual); } -void UUIButton_Base::NativeDestruct() +void UUIButton_Base::NativeTick(const FGeometry& MyGeometry, float InDeltaTime) { - if (UWorld* World = GetWorld()) - { - World->GetTimerManager().ClearTimer(InactivityTimer); - World->GetTimerManager().ClearTimer(TimerTickHandle); - } + Super::NativeTick(MyGeometry, InDeltaTime); - if (bThemeBound) - { - if (const UGameInstance* GI = GetGameInstance()) - { - if (UZMMOThemeSubsystem* Theme = GI->GetSubsystem()) - { - Theme->OnThemeChanged.RemoveDynamic(this, &UUIButton_Base::HandleThemeChanged); - } - } - bThemeBound = false; - } - - Super::NativeDestruct(); -} - -// ---- Hyper "Deactivate Button On Timeout" ---- -void UUIButton_Base::StartDeactivateTimeout() -{ - UWorld* World = GetWorld(); - if (!World) + if (!bTransitioning) { return; } - SetIsEnabled(false); - TimeRemaining = Timeout; + TransitionElapsed += InDeltaTime; + const float Duration = FMath::Max(TransitionSeconds, MinTransition); + const float Alpha = FMath::Clamp(TransitionElapsed / Duration, 0.f, 1.f); + CurrentVisual = LerpVisual(FromVisual, ToVisual, EaseOutCubic(Alpha)); + ApplyVisual(CurrentVisual); - World->GetTimerManager().SetTimer( - InactivityTimer, this, &UUIButton_Base::HandleTimeoutFinished, Timeout, false); - - if (TimeUpdateFrequency > 0.f) + if (Alpha >= 1.f) { - World->GetTimerManager().SetTimer( - TimerTickHandle, this, &UUIButton_Base::HandleTimerTick, - TimeUpdateFrequency, true); + CurrentVisual = ToVisual; + ApplyVisual(CurrentVisual); + bTransitioning = false; } } -void UUIButton_Base::HandleTimeoutFinished() +// === Hooks de estado === + +void UUIButton_Base::NativeOnHovered() { - if (UWorld* World = GetWorld()) + Super::NativeOnHovered(); + bHoveredState = true; + RefreshState(); +} + +void UUIButton_Base::NativeOnUnhovered() +{ + Super::NativeOnUnhovered(); + bHoveredState = false; + RefreshState(); +} + +void UUIButton_Base::NativeOnPressed() +{ + Super::NativeOnPressed(); + bPressedState = true; + RefreshState(); +} + +void UUIButton_Base::NativeOnReleased() +{ + Super::NativeOnReleased(); + bPressedState = false; + RefreshState(); +} + +void UUIButton_Base::NativeOnEnabled() +{ + Super::NativeOnEnabled(); + bDisabledState = false; + RefreshState(); +} + +void UUIButton_Base::NativeOnDisabled() +{ + Super::NativeOnDisabled(); + bDisabledState = true; + RefreshState(); +} + +void UUIButton_Base::NativeOnSelected(bool bBroadcast) +{ + Super::NativeOnSelected(bBroadcast); + bSelectedState = true; + RefreshState(); +} + +void UUIButton_Base::NativeOnDeselected(bool bBroadcast) +{ + Super::NativeOnDeselected(bBroadcast); + bSelectedState = false; + RefreshState(); +} + +// === Internos === + +void UUIButton_Base::RefreshState() +{ + const FUIButtonStateVisual& Target = + bDisabledState ? DisabledVisual + : bPressedState ? PressedVisual + : bHoveredState ? HoveredVisual + : bSelectedState ? SelectedVisual + : DefaultVisual; + GoToState(Target); +} + +void UUIButton_Base::GoToState(const FUIButtonStateVisual& Target) +{ + if (TransitionSeconds <= MinTransition) { - World->GetTimerManager().ClearTimer(TimerTickHandle); + CurrentVisual = Target; + bTransitioning = false; + ApplyVisual(CurrentVisual); + return; } - TimeRemaining = 0.f; - SetIsEnabled(true); - OnButtonActivatedByTimeout.Broadcast(); + FromVisual = CurrentVisual; + ToVisual = Target; + TransitionElapsed = 0.f; + bTransitioning = true; } -void UUIButton_Base::HandleTimerTick() +void UUIButton_Base::ApplyVisual(const FUIButtonStateVisual& V) { - TimeRemaining = FMath::Max(0.f, TimeRemaining - TimeUpdateFrequency); - OnButtonTimerUpdated.Broadcast(TimeRemaining); + // Fundo: só gradient (o glow agora é a camada externa GlowFx). + if (BgMID) + { + BgMID->SetVectorParameterValue(P_TopColour, V.TopColour); + BgMID->SetVectorParameterValue(P_BottomColour, V.BottomColour); + } + // Halo externo (sai pra fora). + if (GlowMID) + { + GlowMID->SetVectorParameterValue(P_GlowColour, V.GlowColour); + GlowMID->SetScalarParameterValue(P_GlowIntensity, V.GlowIntensity); + } + // Cantos: cor acompanha a borda do estado (acende no hover); tamanho/peso + // vêm das UPROPERTYs (estáticos por instância). + if (CornerMID) + { + CornerMID->SetVectorParameterValue(P_CornerColour, V.BorderColour); + CornerMID->SetScalarParameterValue(P_CornerLength, CornerLength); + CornerMID->SetScalarParameterValue(P_CornerThickness, CornerThickness); + } + // Frame (material): mesma cor de borda do estado. + if (BorderMID) + { + BorderMID->SetVectorParameterValue(P_FrameColour, V.BorderColour); + } + + const bool bSimple = (BorderStyle == EUIButtonBorderStyle::Simple); + const bool bFrame = (BorderStyle == EUIButtonBorderStyle::Frame); + const bool bCorners = bShowCorners; // INDEPENDENTE da borda + + // Bg: canto arredondado sempre; a linha (outline) só no modo Simple. + if (Bg) + { + FSlateBrush B = Bg->GetBrush(); + B.DrawAs = ESlateBrushDrawType::RoundedBox; + FSlateBrushOutlineSettings Outline = B.OutlineSettings; + Outline.CornerRadii = FVector4(CornerRadius, CornerRadius, CornerRadius, CornerRadius); + Outline.RoundingType = ESlateBrushRoundingType::FixedRadius; + if (bSimple) + { + Outline.Color = FSlateColor(V.BorderColour); + Outline.Width = FMath::Max(V.BorderWidth, 0.f); + } + else + { + Outline.Color = FSlateColor(FLinearColor::Transparent); + Outline.Width = 0.f; + } + B.OutlineSettings = Outline; + Bg->SetBrush(B); + } + + // Frame (moldura material) visível só no modo Frame. + if (BorderFx) + { + BorderFx->SetVisibility(bFrame ? ESlateVisibility::HitTestInvisible : ESlateVisibility::Collapsed); + } + // Cantos visíveis só no modo Corners. + if (Corners) + { + Corners->SetVisibility(bCorners ? ESlateVisibility::HitTestInvisible : ESlateVisibility::Collapsed); + } + + if (ButtonText) + { + ButtonText->SetColorAndOpacity(FSlateColor(V.TextColour)); + } + + SetRenderScale(FVector2D(V.Scale, V.Scale)); + SetRenderOpacity(V.RenderOpacity); } -void UUIButton_Base::HandleThemeChanged(FName /*NewThemeId*/) +void UUIButton_Base::EnsureMID() { - RefreshUIStyle(); + // Prioriza o material do próprio brush (se o WBP definiu um); senão usa o + // material default da classe. Idempotente por imagem. + auto Ensure = [this](UImage* Img, UMaterialInterface* Fallback) -> UMaterialInstanceDynamic* + { + if (!Img) { return nullptr; } + UMaterialInterface* Mat = Cast(Img->GetBrush().GetResourceObject()); + if (!Mat) { Mat = Fallback; } + if (!Mat) { return nullptr; } + UMaterialInstanceDynamic* MID = UMaterialInstanceDynamic::Create(Mat, this); + Img->SetBrushFromMaterial(MID); + return MID; + }; + if (!BgMID) { BgMID = Ensure(Bg, BgMaterial); } + if (!GlowMID) { GlowMID = Ensure(GlowFx, GlowMaterial); } + if (!CornerMID) { CornerMID = Ensure(Corners, CornerMaterial); } + if (!BorderMID) { BorderMID = Ensure(BorderFx, FrameMaterial); } +} + +void UUIButton_Base::ApplyAspectToLayers() const +{ + // Cada camada usa o aspect da SUA geometry (Corners/BorderFx têm padding + // próprio, então tamanho ≠ do botão). Sem isso, os traços esticam. + auto SetAspect = [](UMaterialInstanceDynamic* MID, const UImage* Img) + { + if (!MID || !Img) { return; } + const FVector2D S = Img->GetCachedGeometry().GetLocalSize(); + if (S.Y > 1.f) + { + MID->SetScalarParameterValue(P_AspectRatio, static_cast(S.X / S.Y)); + } + }; + SetAspect(CornerMID, Corners); + SetAspect(BorderMID, BorderFx); + SetAspect(GlowMID, GlowFx); + // Glow (drop shadow): ancora o anel da sombra na borda do BOTÃO — fração = + // margem (GlowExtent) / meia-altura do GlowFx. Assim a sombra esmaece pros + // dois lados a partir da borda, sem aresta dura na borda externa do quad. + if (GlowMID && GlowFx) + { + const FVector2D S = GlowFx->GetCachedGeometry().GetLocalSize(); + if (S.Y > 1.f) + { + // Edge = fração (em inY=min(V,1-V)) onde está a borda do botão = margem/altura. + const float Edge = GlowExtent / static_cast(S.Y); + GlowMID->SetScalarParameterValue(P_GlowEdge, Edge); + // Largura do esmaecimento ~ a margem (escala com o tamanho do botão). + GlowMID->SetScalarParameterValue(P_GlowSoftness, Edge * 1.2f); + } + } +} + +int32 UUIButton_Base::NativePaint(const FPaintArgs& Args, const FGeometry& AllottedGeometry, + const FSlateRect& MyCullingRect, FSlateWindowElementList& OutDrawElements, + int32 LayerId, const FWidgetStyle& InWidgetStyle, bool bParentEnabled) const +{ + // NativePaint roda no Designer E em runtime (o Tick não roda no Designer) — + // é aqui que garantimos o aspect correto dos materiais em ambos. + ApplyAspectToLayers(); + return Super::NativePaint(Args, AllottedGeometry, MyCullingRect, OutDrawElements, + LayerId, InWidgetStyle, bParentEnabled); +} + +FUIButtonStateVisual UUIButton_Base::LerpVisual(const FUIButtonStateVisual& A, const FUIButtonStateVisual& B, float T) +{ + FUIButtonStateVisual R; + R.TopColour = FMath::Lerp(A.TopColour, B.TopColour, T); + R.BottomColour = FMath::Lerp(A.BottomColour, B.BottomColour, T); + R.GlowColour = FMath::Lerp(A.GlowColour, B.GlowColour, T); + R.GlowIntensity = FMath::Lerp(A.GlowIntensity, B.GlowIntensity, T); + R.BorderColour = FMath::Lerp(A.BorderColour, B.BorderColour, T); + R.BorderWidth = FMath::Lerp(A.BorderWidth, B.BorderWidth, T); + R.TextColour = FMath::Lerp(A.TextColour, B.TextColour, T); + R.Scale = FMath::Lerp(A.Scale, B.Scale, T); + R.RenderOpacity = FMath::Lerp(A.RenderOpacity, B.RenderOpacity, T); + return R; +} + +void UUIButton_Base::SetButtonText(const FText& InText) +{ + if (ButtonText) + { + ButtonText->SetText(InText); + } +} + +// === Acesso ao estilo ativo (DT_UI_Styles) — fase final === + +const FUIStyle* UUIButton_Base::GetActiveUIStyle(FName ThemeId) const +{ + const UDataTable* DT = LoadObject(nullptr, + TEXT("/Game/ZMMO/Data/UI/DT_UI_Styles.DT_UI_Styles")); + if (!DT) + { + return nullptr; + } + + if (const FUIStyleRow* Row = DT->FindRow( + ThemeId, TEXT("UUIButton_Base::GetActiveUIStyle"), false)) + { + return &Row->Style; + } + return nullptr; +} + +const FUIStyleButton* UUIButton_Base::GetActiveButtonStyle(FName ThemeId) const +{ + const FUIStyle* UIStyle = GetActiveUIStyle(ThemeId); + return UIStyle ? &UIStyle->Button : nullptr; } diff --git a/Source/ZMMO/Game/UI/Widgets/UIButton_Base.h b/Source/ZMMO/Game/UI/Widgets/UIButton_Base.h index cdc0415..520e9f3 100644 --- a/Source/ZMMO/Game/UI/Widgets/UIButton_Base.h +++ b/Source/ZMMO/Game/UI/Widgets/UIButton_Base.h @@ -2,35 +2,91 @@ #include "CoreMinimal.h" #include "CommonButtonBase.h" -#include "Layout/Margin.h" -#include "Types/SlateEnums.h" -#include "UI/UIStyleTypes.h" -#include "UI/UIStyleTokens.h" #include "UIButton_Base.generated.h" -class UBorder; -class UTextBlock; class UImage; -class USizeBox; -class UScaleBox; class UCommonTextBlock; -class UCommonActionWidget; +class UMaterialInterface; +class UMaterialInstanceDynamic; -DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnUIButtonActivatedByTimeout); -DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnUIButtonTimerUpdated, float, TimeRemaining); +struct FUIStyle; +struct FUIStyleButton; + +/** Tipo de borda do botão (fixo por instância; a COR acende por estado). */ +UENUM(BlueprintType) +enum class EUIButtonBorderStyle : uint8 +{ + None UMETA(DisplayName = "Sem borda"), + Simple UMETA(DisplayName = "Linha simples (Slate)"), + Frame UMETA(DisplayName = "Frame (material)") +}; /** - * Botão base do ZMMO — porte 1:1 do UI_Button_Master do projeto Hyper para C++ - * auto-configurável, trocando o Struct_Style_Button do Hyper pela camada de - * tema C++ (FUIStyle via UZMMOThemeSubsystem::GetActiveUIStyle()). + * Snapshot visual de UM estado do botão (Default / Hover / Pressed / Disabled). + * As cores de gradient/glow vão pro material (UI_M_Button via MID); a borda usa + * o RoundedBox nativo do Slate (OutlineSettings) e o scale/opacity são + * RenderTransform do widget. Transições entre estados interpolam esta struct + * inteira (ver UUIButton_Base::NativeTick). + */ +USTRUCT(BlueprintType) +struct ZMMO_API FUIButtonStateVisual +{ + GENERATED_BODY() + + /** Topo do gradient vertical do fundo (param TopColour do material). */ + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Button|State") + FLinearColor TopColour = FLinearColor(0.910f, 0.753f, 0.376f, 0.18f); + + /** Base do gradient vertical (param BottomColour). */ + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Button|State") + FLinearColor BottomColour = FLinearColor(0.659f, 0.471f, 0.188f, 0.10f); + + /** Cor do halo externo (camada GlowFx, material UI_M_Button_Glow). */ + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Button|State") + FLinearColor GlowColour = FLinearColor(0.910f, 0.753f, 0.376f, 1.f); + + /** Intensidade do halo externo (GlowFx). 0 = sem glow; hover acende (sai pra fora). */ + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Button|State", + meta = (ClampMin = "0", ClampMax = "1")) + float GlowIntensity = 0.f; + + /** Cor da borda/cantos (Slate outline no modo Simple, ou material no modo Corners). */ + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Button|State") + FLinearColor BorderColour = FLinearColor(0.831f, 0.659f, 0.314f, 0.28f); + + /** Espessura da borda em pixels (Slate OutlineSettings.Width). */ + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Button|State", + meta = (ClampMin = "0", ClampMax = "6")) + float BorderWidth = 1.f; + + /** Cor do texto (ButtonText->SetColorAndOpacity). */ + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Button|State") + FLinearColor TextColour = FLinearColor(1.f, 0.847f, 0.533f, 1.f); + + /** Scale do botão (CSS transform: scale). 1 = normal, 1.02 = hover, 0.98 = pressed. */ + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Button|State", + meta = (ClampMin = "0.5", ClampMax = "1.5")) + float Scale = 1.f; + + /** Opacidade geral do botão (CSS opacity — disabled usa 0.6). */ + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Button|State", + meta = (ClampMin = "0", ClampMax = "1")) + float RenderOpacity = 1.f; +}; + +/** + * Botão base do ZMMO (CommonUI). Mesmo padrão do UUIProgressBar_Base: um UImage + * (`Bg`) com material custom (UI_M_Button) controlado por MID + RoundedBox nativo + * do Slate pra borda/canto. Os estados do UCommonButtonBase (Hover/Pressed/ + * Disabled/Selected) trocam o FUIButtonStateVisual alvo; a transição interpola + * suave no Tick (estilo CSS `transition`). * - * Fundação: CommonUI (UCommonButtonBase) cuida de input/foco/click. - * Camada Abstract (padrão "_Abstract" do Hyper). WBP UI_Button_Base herda - * disto; UI_Button_Master herda do WBP. Os widgets visuais são opcionais - * (BindWidgetOptional) e nomeados como no Hyper. + * Modo atual = manual (os 4 FUIButtonStateVisual editáveis no Details, já com + * defaults do `btn-primary`). A leitura via DT_UI_Styles (bUseTheme + Variant) + * entra na fase final, reusando esta struct. * - * Auto-config: NativePreConstruct chama InitializeButton (text/number/icon/ - * sizebox/estilo) + ApplyInputActionStyle. Reage à troca de tema. + * WBP filho (UI_Button_Master_New): `Bg` (UImage, obrigatório, material no brush) + * + `ButtonText` (UCommonTextBlock, opcional). */ UCLASS(Abstract, Blueprintable) class ZMMO_API UUIButton_Base : public UCommonButtonBase @@ -38,184 +94,188 @@ class ZMMO_API UUIButton_Base : public UCommonButtonBase GENERATED_BODY() public: - // ---- Botão ---- - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Button") - EUIButtonShape ButtonTypeSelection = EUIButtonShape::Rectangle; + UUIButton_Base(const FObjectInitializer& ObjectInitializer); - /** - * Variante visual do botão (dropdown vem de FUIStyle.Button.Backgrounds/ - * Strokes/Fonts no DT_UI_Styles + as 4 clássicas como fallback). - * Data-driven via FName, sem enum fixo (padrão Hyper-style). - */ - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Button", - meta = (GetOptions = "GetVariantOptions")) - FName Variant = TEXT("Primary"); - - /** Opções do dropdown de Variant (data-driven do DT_UI_Styles). */ - UFUNCTION() - TArray GetVariantOptions() const; - - /** Hyper "Transform Policy = To Upper": força o texto em maiúsculas. */ - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Button") - bool bUppercaseText = true; - - // ---- Text Style ---- - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Text Style") - FText ButtonTextValue = FText::FromString(TEXT("Button Text")); - - /** Alinhamento do texto dentro do botão (esquerda/centro/direita). */ - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Text Style") - TEnumAsByte TextAlign = ETextJustify::Center; - - /** true = texto escala p/ caber no tamanho do botão (ScaleBox ScaleToFit); - * false = usa o tamanho fixo da fonte da variante. */ - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Text Style") - bool bAutoSizeText = true; - - /** Arredonda o Background (RoundedBox) e desenha a borda na cor da - * variante. Desligado = caixa reta. Raio/largura vêm de FUIStyleButton. */ - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Button") - bool bRoundedBackground = true; - - // ---- Number Text ---- - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Number Text") - bool bShowNumberText = false; - - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Number Text") - int32 NumberTextAmount = 0; - - // ---- Icon ---- - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Icon") - bool bUseIconInsteadOfText = false; - - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Icon") - TObjectPtr IconImage = nullptr; - - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Icon") - EUIIconType IconTypeSelection = EUIIconType::None; - - // ---- Size ---- - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Size") - float InHeightOverride = 50.f; - - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Size") - float InWidthOverride = 150.f; - - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Size") - bool bDoNotOverrideWithSizebox = false; - - // ---- Input Action Style ---- - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Input Action Style") - bool bShowInputActionWidget = false; - - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Input Action Style") - FMargin InputPadding = FMargin(0.f, 0.f, 0.f, -22.f); - - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Input Action Style") - TEnumAsByte InputHorzAlignment = HAlign_Center; - - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Input Action Style") - TEnumAsByte InputVertAlignment = VAlign_Bottom; - - // ---- Deactivate by Timer ---- - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Deactivate By Timer") - bool bDeactivatedOnConstructWithTimeout = false; - - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Deactivate By Timer") - float Timeout = 0.f; - - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Timer") - float TimeUpdateFrequency = 0.05f; - - UPROPERTY(BlueprintReadOnly, Category = "Timer") - float TimeRemaining = 0.f; - - UPROPERTY(BlueprintAssignable, Category = "Timer") - FOnUIButtonActivatedByTimeout OnButtonActivatedByTimeout; - - UPROPERTY(BlueprintAssignable, Category = "Timer") - FOnUIButtonTimerUpdated OnButtonTimerUpdated; - - // ---- API (porte 1:1 das funções do Hyper) ---- + /** Define o texto do botão (ButtonText). */ UFUNCTION(BlueprintCallable, Category = "UI Button") - void SetButtonText(FText InText); + void SetButtonText(const FText& InText); - UFUNCTION(BlueprintCallable, Category = "UI Button") - void SetNumberText(); + // === Visual por estado (modo manual; defaults = btn-primary) === - UFUNCTION(BlueprintCallable, Category = "UI Button") - void SetIcon(); + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "UI Button|Visual") + FUIButtonStateVisual DefaultVisual; - UFUNCTION(BlueprintCallable, Category = "UI Button") - void ApplySizeBox(); + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "UI Button|Visual") + FUIButtonStateVisual HoveredVisual; - UFUNCTION(BlueprintCallable, Category = "UI Button") - void ApplyInputActionStyle(); + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "UI Button|Visual") + FUIButtonStateVisual PressedVisual; - /** Orquestrador (Hyper "Initialize Button"): aplica tudo + estilo do tema. */ - UFUNCTION(BlueprintCallable, Category = "UI Button") - void InitializeButton(); + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "UI Button|Visual") + FUIButtonStateVisual DisabledVisual; - /** Re-resolve os tokens do tema ativo e reaplica (Hyper "Update Button Style"). */ - UFUNCTION(BlueprintCallable, Category = "UI Style") - void RefreshUIStyle(); + /** Estado "selecionado" (tabs/toggles). Default = igual ao Hovered. */ + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "UI Button|Visual") + FUIButtonStateVisual SelectedVisual; - /** Estado visual do botão (mapeia nos tokens BgNormal/Hover/Pressed/Disabled). */ - enum class EUIButtonVisual : uint8 { Normal, Hovered, Pressed, Disabled }; + /** Raio do canto em pixels (Slate RoundedBox). Comum a todos os estados. */ + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "UI Button|Visual", + meta = (ClampMin = "0", ClampMax = "32")) + float CornerRadius = 2.f; - /** Pinta o Background (fill + borda) conforme o estado, re-resolvendo o tema. */ - void ApplyVisualState(EUIButtonVisual State); + /** Tipo de borda (independente dos cantos): sem borda, linha Slate ou frame material. */ + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "UI Button|Visual") + EUIButtonBorderStyle BorderStyle = EUIButtonBorderStyle::Frame; + + /** Liga os cantos (corner brackets) — INDEPENDENTE da borda. Pode ter os dois, + * só um, ou nenhum. */ + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "UI Button|Visual") + bool bShowCorners = true; + + /** Material do fundo (UI_M_Button). Fallback quando o brush do Bg não traz + * material próprio. Defaults resolvidos no construtor. */ + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Button|Visual") + TObjectPtr BgMaterial; + + /** Material do halo externo (UI_M_Button_Glow), aplicado no GlowFx. */ + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Button|Visual") + TObjectPtr GlowMaterial; + + /** Material dos cantos (UI_M_CornerBrackets), aplicado no Corners. */ + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Button|Visual") + TObjectPtr CornerMaterial; + + /** Material da moldura (UI_M_Button_Frame), aplicado no BorderFx. */ + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Button|Visual") + TObjectPtr FrameMaterial; + + /** Padding do conteúdo (texto) dentro do botão. btn-primary = 28px horiz / + * 12px vert. Aplicado no slot do ButtonText em PreConstruct (o texto fica + * centralizado). */ + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "UI Button|Visual") + FMargin ContentPadding = FMargin(28.f, 12.f, 28.f, 12.f); + + /** Quanto o halo externo (GlowFx) vaza pra fora do botão, em pixels. Aplicado + * como padding negativo no slot do GlowFx em PreConstruct. */ + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "UI Button|Visual", + meta = (ClampMin = "0", ClampMax = "64")) + float GlowExtent = 16.f; + + /** Desloca os cantos. Negativo = pra FORA (abraçando o botão, estilo CSS); + * positivo = pra dentro. Aplicado no slot do Corners em PreConstruct. */ + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "UI Button|Visual", + meta = (ClampMin = "-32", ClampMax = "32")) + float CornerInset = -5.f; + + /** Comprimento de cada braço do "L" (fração da altura). Maior = L mais longo. */ + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "UI Button|Visual", + meta = (ClampMin = "0.05", ClampMax = "1.0")) + float CornerLength = 0.35f; + + /** Espessura/peso do traço dos cantos (fração da altura). Menor = mais fino. */ + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "UI Button|Visual", + meta = (ClampMin = "0.005", ClampMax = "0.3")) + float CornerThickness = 0.04f; + + /** Deslocamento da sombra (GlowFx) pra baixo, em px — dá o look de drop shadow. */ + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "UI Button|Visual", + meta = (ClampMin = "0", ClampMax = "32")) + float GlowOffsetY = 4.f; + + /** Duração da transição entre estados (s). 0 = troca instantânea. */ + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "UI Button|Animation", + meta = (ClampMin = "0", ClampMax = "1")) + float TransitionSeconds = 0.12f; protected: virtual void NativePreConstruct() override; virtual void NativeConstruct() override; - virtual void NativeDestruct() override; + virtual void NativeTick(const FGeometry& MyGeometry, float InDeltaTime) override; + virtual int32 NativePaint(const FPaintArgs& Args, const FGeometry& AllottedGeometry, + const FSlateRect& MyCullingRect, FSlateWindowElementList& OutDrawElements, + int32 LayerId, const FWidgetStyle& InWidgetStyle, bool bParentEnabled) const override; - // Estados do UCommonButtonBase → repinta o Background pela variante. + // === Hooks de estado do UCommonButtonBase === virtual void NativeOnHovered() override; virtual void NativeOnUnhovered() override; virtual void NativeOnPressed() override; virtual void NativeOnReleased() override; virtual void NativeOnEnabled() override; virtual void NativeOnDisabled() override; + virtual void NativeOnSelected(bool bBroadcast) override; + virtual void NativeOnDeselected(bool bBroadcast) override; - /** Hook opcional: o WBP pode estender/sobrescrever a aplicação visual. */ - UFUNCTION(BlueprintImplementableEvent, Category = "UI Style", - meta = (DisplayName = "Apply UI Style")) - void BP_ApplyUIStyle(const FUIStyleButton& Button, const FUIStyleButtonVariant& VariantStyle); + // === BindWidget (espelham a árvore do UI_Button_Master_New) === + // Todos BindWidgetOptional pra não quebrar WBPs que herdam UUIButton_Base sem + // essas peças — sem elas o visual degrada (não pinta), mas não quebra. - FUIStyleButtonVariant ResolveVariant(const FUIStyleButton& Button) const; + /** Halo externo (atrás do Bg, com padding negativo pra vazar pra fora). */ + UPROPERTY(BlueprintReadOnly, Category = "UI Button", meta = (BindWidgetOptional)) + TObjectPtr GlowFx; - // ---- Widgets visuais opcionais (nomes espelham o Hyper) ---- - UPROPERTY(meta = (BindWidgetOptional)) - TObjectPtr SizeBox_Master; + /** Fundo (gradient). */ + UPROPERTY(BlueprintReadOnly, Category = "UI Button", meta = (BindWidgetOptional)) + TObjectPtr Bg; - UPROPERTY(meta = (BindWidgetOptional)) + /** Moldura/frame (material), visível só no BorderStyle = Frame. */ + UPROPERTY(BlueprintReadOnly, Category = "UI Button", meta = (BindWidgetOptional)) + TObjectPtr BorderFx; + + /** Cantos (corner brackets), visível só no BorderStyle = Corners. */ + UPROPERTY(BlueprintReadOnly, Category = "UI Button", meta = (BindWidgetOptional)) + TObjectPtr Corners; + + UPROPERTY(BlueprintReadOnly, Category = "UI Button", meta = (BindWidgetOptional)) TObjectPtr ButtonText; - /** Envolve o ButtonText: escala o texto p/ caber no botão (bAutoSizeText). */ - UPROPERTY(meta = (BindWidgetOptional)) - TObjectPtr TextScaleBox; + // === Acesso ao estilo ativo (DT_UI_Styles) — usado na fase final === - UPROPERTY(meta = (BindWidgetOptional)) - TObjectPtr Number_Text; + /** Carrega DT_UI_Styles e devolve o FUIStyle da row pedida (default "Default"). */ + const FUIStyle* GetActiveUIStyle(FName ThemeId = TEXT("Default")) const; - UPROPERTY(meta = (BindWidgetOptional)) - TObjectPtr Image_Icon; - - /** Fundo temável (nossa adição sobre o Hyper, p/ FUIStyle). */ - UPROPERTY(meta = (BindWidgetOptional)) - TObjectPtr Background; + /** Atalho: devolve só o bloco Button do FUIStyle ativo. */ + const FUIStyleButton* GetActiveButtonStyle(FName ThemeId = TEXT("Default")) const; private: - void StartDeactivateTimeout(); - void HandleTimeoutFinished(); - void HandleTimerTick(); + /** Cria os MIDs a partir do material no brush de cada imagem (idempotente). */ + void EnsureMID(); - UFUNCTION() - void HandleThemeChanged(FName NewThemeId); + /** Passa a proporção W/H de CADA camada (cantos/frame/glow) pro respectivo + * material que compensa aspect. Chamado no NativePaint — roda no Designer E + * em runtime (o Tick NÃO roda no Designer, por isso o traço esticava lá). */ + void ApplyAspectToLayers() const; - bool bThemeBound = false; - FTimerHandle InactivityTimer; - FTimerHandle TimerTickHandle; + /** Recalcula o estado-alvo (disabled>pressed>hovered>selected>default) e transiciona. */ + void RefreshState(); + + /** Inicia transição pro visual alvo (ou aplica direto se TransitionSeconds≈0). */ + void GoToState(const FUIButtonStateVisual& Target); + + /** Empurra um visual pros widgets (MID + borda Slate + scale/opacity + texto). */ + void ApplyVisual(const FUIButtonStateVisual& V); + + static FUIButtonStateVisual LerpVisual(const FUIButtonStateVisual& A, const FUIButtonStateVisual& B, float T); + + UPROPERTY(Transient) + TObjectPtr BgMID; + + UPROPERTY(Transient) + TObjectPtr GlowMID; + + UPROPERTY(Transient) + TObjectPtr CornerMID; + + UPROPERTY(Transient) + TObjectPtr BorderMID; + + bool bHoveredState = false; + bool bPressedState = false; + bool bDisabledState = false; + bool bSelectedState = false; + + FUIButtonStateVisual FromVisual; + FUIButtonStateVisual ToVisual; + FUIButtonStateVisual CurrentVisual; + float TransitionElapsed = 0.f; + bool bTransitioning = false; }; diff --git a/Source/ZMMO/Game/UI/Widgets/UIPanel_Base.cpp b/Source/ZMMO/Game/UI/Widgets/UIPanel_Base.cpp index 7ec2f4f..80e9bd9 100644 --- a/Source/ZMMO/Game/UI/Widgets/UIPanel_Base.cpp +++ b/Source/ZMMO/Game/UI/Widgets/UIPanel_Base.cpp @@ -52,83 +52,43 @@ void UUIPanel_Base::RefreshUIStyle() const FUIStyle AS = ResolvePanelStyle(this, Fallback); const FUIStylePanel& P = AS.Panel; - // ---- Modo TEXTURA (padrão Hyper): brush por EUIPanelTexture ---- - // Fiel ao UI_Panel_Master do Hyper: só faz SetBrush nos dois Borders - // (Find no mapa por chave). Sem padding/visibility — a arte da textura - // já traz a moldura; o conteúdo é injetado pelo WBP filho. - // Seed dos mapas da instância a partir do tema quando vazios — assim o - // Details mostra Panel Background/Outline preenchidos e editáveis (igual - // ao Hyper, cujo `Panels` é alimentado pelo estilo). Edições do designer - // têm prioridade; o tema (P) é fallback por chave. - if (Panels.PanelBackground.Num() == 0) { Panels.PanelBackground = P.PanelBackground; } - if (Panels.PanelOutline.Num() == 0) { Panels.PanelOutline = P.PanelOutline; } + // bUseTheme = true -> brushes vêm da DT (lookup pelas 3 chaves). + // bUseTheme = false -> brushes vêm dos 3 FSlateBrush em Panels (direto). - const FSlateBrush* BgTex = nullptr; - if (PanelTexture != EUIPanelTexture::None) + static const FSlateBrush NoDrawBrush = [] { - BgTex = Panels.PanelBackground.Find(PanelTexture); - if (!BgTex) { BgTex = P.PanelBackground.Find(PanelTexture); } - } - if (BgTex) + FSlateBrush B; + B.DrawAs = ESlateBrushDrawType::NoDrawType; + return B; + }(); + + auto ResolveFromTheme = [](FName Key, + const TMap& Map) -> const FSlateBrush* { - Background->SetBrush(*BgTex); - if (Outline) - { - // Hyper: Find sempre seguido de SetBrush. No Hyper a entrada de - // outline ausente é um brush TRANSPARENTE (alpha 0) — não some - // widget, só não desenha. Um FSlateBrush default desenha um - // retângulo BRANCO; o equivalente correto é DrawAs=NoDrawType. - const FSlateBrush* OlTex = Panels.PanelOutline.Find(PanelTexture); - if (!OlTex) { OlTex = P.PanelOutline.Find(PanelTexture); } - static const FSlateBrush NoDrawBrush = [] - { - FSlateBrush B; - B.DrawAs = ESlateBrushDrawType::NoDrawType; - return B; - }(); - Outline->SetBrush(OlTex ? *OlTex : NoDrawBrush); - } - BP_ApplyPanelStyle(P); - return; - } + return Key.IsNone() ? nullptr : Map.Find(Key); + }; + + const FSlateBrush* BgTex = bUseTheme + ? ResolveFromTheme(BackgroundKey, P.PanelBackgroundByName) + : &Panels.PanelBackground; + Background->SetBrush(BgTex ? *BgTex : NoDrawBrush); - // ---- Modo COR (RoundedBox) ---- if (Outline) { - Outline->SetVisibility(ESlateVisibility::Collapsed); + const FSlateBrush* OlTex = bUseTheme + ? ResolveFromTheme(OutlineKey, P.PanelOutlineByName) + : &Panels.PanelOutline; + Outline->SetBrush(OlTex ? *OlTex : NoDrawBrush); } - FLinearColor Fill; - FLinearColor OutlineColor = P.BorderColor; - switch (PanelType) + if (OutlineEffect) { - case EUIPanelType::Secondary: Fill = P.BgRaised; break; - case EUIPanelType::Tertiary: Fill = P.BgSunken; break; - case EUIPanelType::Card: Fill = P.CardSelectedBg; OutlineColor = P.CardSelectedBorder; break; - case EUIPanelType::Primary: - case EUIPanelType::None: - default: Fill = P.Bg; break; + const FSlateBrush* OeTex = bUseTheme + ? ResolveFromTheme(OutlineEffectKey, P.PanelOutlineEffectByName) + : &Panels.PanelOutlineEffect; + OutlineEffect->SetBrush(OeTex ? *OeTex : NoDrawBrush); } - FSlateBrush Brush = Background->Background; - Brush.TintColor = FSlateColor(Fill); - if (bRoundedBackground) - { - const float R = bUseSmallPadding ? P.CornerRadiusSmall : P.CornerRadius; - Brush.DrawAs = ESlateBrushDrawType::RoundedBox; - Brush.OutlineSettings = FSlateBrushOutlineSettings( - FVector4(R, R, R, R), FSlateColor(OutlineColor), P.BorderWidth); - Brush.OutlineSettings.RoundingType = ESlateBrushRoundingType::FixedRadius; - } - else - { - Brush.DrawAs = ESlateBrushDrawType::Box; - Brush.OutlineSettings = FSlateBrushOutlineSettings( - FVector4(0, 0, 0, 0), FSlateColor(OutlineColor), P.BorderWidth); - } - Background->SetBrush(Brush); - Background->SetPadding(bUseSmallPadding ? P.PaddingSmall : P.Padding); - BP_ApplyPanelStyle(P); } @@ -176,3 +136,59 @@ void UUIPanel_Base::HandleThemeChanged(FName /*NewThemeId*/) { RefreshUIStyle(); } + +namespace +{ + enum class EPanelBrushSlot : uint8 { Background, Outline, OutlineEffect }; + + TArray CollectPanelOptions(EPanelBrushSlot Slot) + { + TArray Options; + Options.Add(FString()); // entry vazia = "sem brush" (NoDrawType) + + const UDataTable* DT = LoadObject(nullptr, + TEXT("/Game/ZMMO/Data/UI/DT_UI_Styles.DT_UI_Styles")); + if (!DT) + { + return Options; + } + const FUIStyleRow* Row = DT->FindRow( + FName(TEXT("Default")), TEXT("UIPanelGetOptions"), false); + if (!Row) + { + return Options; + } + + const TMap* Map = nullptr; + switch (Slot) + { + case EPanelBrushSlot::Background: Map = &Row->Style.Panel.PanelBackgroundByName; break; + case EPanelBrushSlot::Outline: Map = &Row->Style.Panel.PanelOutlineByName; break; + case EPanelBrushSlot::OutlineEffect: Map = &Row->Style.Panel.PanelOutlineEffectByName; break; + } + if (Map) + { + for (const TPair& Pair : *Map) + { + Options.Add(Pair.Key.ToString()); + } + Options.Sort(); + } + return Options; + } +} + +TArray UUIPanel_Base::GetPanelBackgroundOptions() const +{ + return CollectPanelOptions(EPanelBrushSlot::Background); +} + +TArray UUIPanel_Base::GetPanelOutlineOptions() const +{ + return CollectPanelOptions(EPanelBrushSlot::Outline); +} + +TArray UUIPanel_Base::GetPanelOutlineEffectOptions() const +{ + return CollectPanelOptions(EPanelBrushSlot::OutlineEffect); +} diff --git a/Source/ZMMO/Game/UI/Widgets/UIPanel_Base.h b/Source/ZMMO/Game/UI/Widgets/UIPanel_Base.h index 843cdca..9387f19 100644 --- a/Source/ZMMO/Game/UI/Widgets/UIPanel_Base.h +++ b/Source/ZMMO/Game/UI/Widgets/UIPanel_Base.h @@ -23,40 +23,60 @@ class ZMMO_API UUIPanel_Base : public UCommonUserWidget GENERATED_BODY() public: - /** Tipo do painel — escolhe Bg/BgRaised/BgSunken/Card de FUIStylePanel. */ - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Panel") - EUIPanelType PanelType = EUIPanelType::Primary; - - /** Arredonda o Background (RoundedBox) com a borda do tema. */ - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Panel") - bool bRoundedBackground = true; - - /** Usa o padding "pequeno" do tema (PaddingSmall) em vez de Padding. */ - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Panel") - bool bUseSmallPadding = false; - /** - * Conjunto de textura (modo Hyper). Se != None E houver brush no mapa - * FUIStylePanel.PanelBackground p/ esta chave, usa o painel TEXTURIZADO - * (brush bg + outline). Senão, cai no modo COR (RoundedBox acima). + * true = lê brushes da DT_UI_Styles (FUIStylePanel) — tema centralizado. + * false = ignora a DT e usa apenas `Panels` (brushes manuais no Details). */ UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Panel") - EUIPanelTexture PanelTexture = EUIPanelTexture::None; + bool bUseTheme = true; - /** - * Mapas de brush por instância — espelha o `Panels` do UI_Panel_Master - * do Hyper (Details mostra Panel Background / Panel Outline editáveis). - * Tem prioridade. Se não houver brush p/ a chave aqui, cai no tema - * (DT_UI_Styles). Em NativePreConstruct é semeado a partir do tema - * quando vazio, então por padrão o tema manda — como no Hyper. - */ - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Panel") + // ---- Modo TEMA: chave FName do TMap FUIStylePanel pra cada Border. ---- + // + // GetOptions popula dropdown lendo DT_UI_Styles row "Default" — adicione + // uma entry em FUIStylePanel.PanelBackgroundByName no DT e a categoria + // aparece aqui sem recompilar C++. Vazio = sem brush (NoDrawType). + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Panel", + meta = (EditCondition = "bUseTheme", EditConditionHides, + GetOptions = "GetPanelBackgroundOptions")) + FName BackgroundKey; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Panel", + meta = (EditCondition = "bUseTheme", EditConditionHides, + GetOptions = "GetPanelOutlineOptions")) + FName OutlineKey; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Panel", + meta = (EditCondition = "bUseTheme", EditConditionHides, + GetOptions = "GetPanelOutlineEffectOptions")) + FName OutlineEffectKey; + + // ---- Modo MANUAL: 3 brushes editáveis direto no Details. ---- + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Panel", + meta = (EditCondition = "!bUseTheme", EditConditionHides)) FUIPanelBrushSet Panels; /** Re-resolve os tokens do tema ativo e reaplica no Background. */ UFUNCTION(BlueprintCallable, Category = "UI Style") void RefreshUIStyle(); + /** + * Populadores do dropdown (meta=GetOptions) das chaves de textura. + * Lêem DT_UI_Styles row "Default" e retornam as FName de cada sub-mapa + * do FUIStylePanel. Editar o DT no editor reflete aqui sem recompilar. + * São métodos de instância porque o UE precisa de UFUNCTION reflexável; + * o conteúdo não usa estado da instância (poderia ser estático). + */ + UFUNCTION() + TArray GetPanelBackgroundOptions() const; + + UFUNCTION() + TArray GetPanelOutlineOptions() const; + + UFUNCTION() + TArray GetPanelOutlineEffectOptions() const; + protected: virtual void NativePreConstruct() override; virtual void NativeConstruct() override; @@ -75,6 +95,10 @@ protected: UPROPERTY(meta = (BindWidgetOptional)) TObjectPtr Outline; + /** Efeito sobre o contorno (glow/embossing/etc.) — pintado acima do Outline. */ + UPROPERTY(meta = (BindWidgetOptional)) + TObjectPtr OutlineEffect; + private: UFUNCTION() void HandleThemeChanged(FName NewThemeId); diff --git a/Source/ZMMO/Game/UI/Widgets/UIPlayerStatus_BonusRow.cpp b/Source/ZMMO/Game/UI/Widgets/UIPlayerStatus_BonusRow.cpp new file mode 100644 index 0000000..411b4c8 --- /dev/null +++ b/Source/ZMMO/Game/UI/Widgets/UIPlayerStatus_BonusRow.cpp @@ -0,0 +1,69 @@ +#include "UIPlayerStatus_BonusRow.h" + +#include "Components/TextBlock.h" +#include "Internationalization/Text.h" + +void UUIPlayerStatus_BonusRow::SetStatName(FText DisplayName) +{ + if (Text_StatName) Text_StatName->SetText(DisplayName); +} + +void UUIPlayerStatus_BonusRow::SetValues(int32 Base, int32 Bonus) +{ + if (Text_BasePart) Text_BasePart->SetText(FText::AsNumber(Base)); + + const bool bShowBonus = (Bonus != 0); + SetBonusVisibility(bShowBonus); + if (bShowBonus) + { + if (Text_SplitOp) Text_SplitOp->SetText(FText::FromString(Bonus > 0 ? TEXT("+") : TEXT("-"))); + if (Text_BonusPart) Text_BonusPart->SetText(FText::AsNumber(FMath::Abs(Bonus))); + } +} + +void UUIPlayerStatus_BonusRow::SetRangeValues(int32 MinBase, int32 MaxBase, int32 Bonus) +{ + if (Text_BasePart) + { + Text_BasePart->SetText(FText::FromString(FString::Printf(TEXT("%d~%d"), MinBase, MaxBase))); + } + + const bool bShowBonus = (Bonus != 0); + SetBonusVisibility(bShowBonus); + if (bShowBonus) + { + if (Text_SplitOp) Text_SplitOp->SetText(FText::FromString(Bonus > 0 ? TEXT("+") : TEXT("-"))); + if (Text_BonusPart) Text_BonusPart->SetText(FText::AsNumber(FMath::Abs(Bonus))); + } +} + +void UUIPlayerStatus_BonusRow::SetValuesX10(int32 BaseX10, int32 BonusX10) +{ + auto FormatX10 = [](int32 ValX10) -> FString + { + const int32 Sign = ValX10 < 0 ? -1 : 1; + const int32 Abs = FMath::Abs(ValX10); + return FString::Printf(TEXT("%d.%d"), Sign * (Abs / 10), Abs % 10); + }; + + if (Text_BasePart) Text_BasePart->SetText(FText::FromString(FormatX10(BaseX10))); + + const bool bShowBonus = (BonusX10 != 0); + SetBonusVisibility(bShowBonus); + if (bShowBonus) + { + if (Text_SplitOp) Text_SplitOp->SetText(FText::FromString(BonusX10 > 0 ? TEXT("+") : TEXT("-"))); + if (Text_BonusPart) + { + const int32 Abs = FMath::Abs(BonusX10); + Text_BonusPart->SetText(FText::FromString(FString::Printf(TEXT("%d.%d"), Abs / 10, Abs % 10))); + } + } +} + +void UUIPlayerStatus_BonusRow::SetBonusVisibility(bool bShow) +{ + const ESlateVisibility Vis = bShow ? ESlateVisibility::Visible : ESlateVisibility::Collapsed; + if (Text_SplitOp) Text_SplitOp->SetVisibility(Vis); + if (Text_BonusPart) Text_BonusPart->SetVisibility(Vis); +} diff --git a/Source/ZMMO/Game/UI/Widgets/UIPlayerStatus_BonusRow.h b/Source/ZMMO/Game/UI/Widgets/UIPlayerStatus_BonusRow.h new file mode 100644 index 0000000..95a0c77 --- /dev/null +++ b/Source/ZMMO/Game/UI/Widgets/UIPlayerStatus_BonusRow.h @@ -0,0 +1,46 @@ +#pragma once + +#include "CoreMinimal.h" +#include "CommonUserWidget.h" +#include "UIPlayerStatus_BonusRow.generated.h" + +class UTextBlock; + +/** + * Linha de um stat derivado (ATK/DEF/HIT/etc.) na janela de Status. + * Layout: nome + valor base + (separador + bônus). Sem botão. + * + * Três modos de valor: + * - SetValues(base, bonus) "10 + 2" + * - SetRangeValues(min, max, bonus) "10~15 + 2" (MATK pre-renewal) + * - SetValuesX10(baseX10, bonusX10) "1.5 + 0.0" (CRIT armazenado ×10) + * + * Pareada com o WBP UI_PlayerStatus_BonusRow. + */ +UCLASS(Abstract, Blueprintable) +class ZMMO_API UUIPlayerStatus_BonusRow : public UCommonUserWidget +{ + GENERATED_BODY() + +public: + UFUNCTION(BlueprintCallable, Category = "Zeus|Status") + void SetStatName(FText DisplayName); + + UFUNCTION(BlueprintCallable, Category = "Zeus|Status") + void SetValues(int32 Base, int32 Bonus); + + UFUNCTION(BlueprintCallable, Category = "Zeus|Status") + void SetRangeValues(int32 MinBase, int32 MaxBase, int32 Bonus); + + UFUNCTION(BlueprintCallable, Category = "Zeus|Status") + void SetValuesX10(int32 BaseX10, int32 BonusX10); + +protected: + UPROPERTY(meta = (BindWidgetOptional)) TObjectPtr Text_StatName; + UPROPERTY(meta = (BindWidgetOptional)) TObjectPtr Text_BasePart; + UPROPERTY(meta = (BindWidgetOptional)) TObjectPtr Text_SplitOp; + UPROPERTY(meta = (BindWidgetOptional)) TObjectPtr Text_BonusPart; + +private: + void SetBonusVisibility(bool bShow); +}; diff --git a/Source/ZMMO/Game/UI/Widgets/UIPlayerStatus_StatRow.cpp b/Source/ZMMO/Game/UI/Widgets/UIPlayerStatus_StatRow.cpp new file mode 100644 index 0000000..7accb9f --- /dev/null +++ b/Source/ZMMO/Game/UI/Widgets/UIPlayerStatus_StatRow.cpp @@ -0,0 +1,73 @@ +#include "UIPlayerStatus_StatRow.h" + +#include "Components/Button.h" +#include "Components/HorizontalBox.h" +#include "Components/TextBlock.h" +#include "Internationalization/Text.h" + +void UUIPlayerStatus_StatRow::SetupStat(int32 InStatId, FText IconLetter, FText DisplayName) +{ + StatId = InStatId; + if (Border_StatIconWrap_Label) Border_StatIconWrap_Label->SetText(IconLetter); + if (Text_StatName) Text_StatName->SetText(DisplayName); +} + +void UUIPlayerStatus_StatRow::SetValues(int32 Base, int32 Bonus) +{ + if (Text_StatBase) Text_StatBase->SetText(FText::AsNumber(Base)); + + const bool bShowBonus = (Bonus != 0); + const ESlateVisibility BonusVis = bShowBonus ? ESlateVisibility::Visible : ESlateVisibility::Collapsed; + + if (Layout_BonusGroup) + { + Layout_BonusGroup->SetVisibility(BonusVis); + } + else + { + if (Text_Sign) Text_Sign->SetVisibility(BonusVis); + if (Text_StatBonus) Text_StatBonus->SetVisibility(BonusVis); + } + + if (bShowBonus) + { + if (Text_Sign) Text_Sign->SetText(FText::FromString(Bonus > 0 ? TEXT("+") : TEXT("-"))); + if (Text_StatBonus) Text_StatBonus->SetText(FText::AsNumber(FMath::Abs(Bonus))); + } +} + +void UUIPlayerStatus_StatRow::SetPlusVisible(bool bVisible) +{ + if (Button_PlusBtn) + { + Button_PlusBtn->SetVisibility(bVisible ? ESlateVisibility::Visible : ESlateVisibility::Collapsed); + } +} + +void UUIPlayerStatus_StatRow::SetPlusEnabled(bool bEnabled) +{ + if (Button_PlusBtn) Button_PlusBtn->SetIsEnabled(bEnabled); +} + +void UUIPlayerStatus_StatRow::NativeOnInitialized() +{ + Super::NativeOnInitialized(); + if (Button_PlusBtn) + { + Button_PlusBtn->OnClicked.AddDynamic(this, &UUIPlayerStatus_StatRow::HandlePlusClicked); + } +} + +void UUIPlayerStatus_StatRow::NativeDestruct() +{ + if (Button_PlusBtn) + { + Button_PlusBtn->OnClicked.RemoveDynamic(this, &UUIPlayerStatus_StatRow::HandlePlusClicked); + } + Super::NativeDestruct(); +} + +void UUIPlayerStatus_StatRow::HandlePlusClicked() +{ + OnPlusClicked.Broadcast(StatId); +} diff --git a/Source/ZMMO/Game/UI/Widgets/UIPlayerStatus_StatRow.h b/Source/ZMMO/Game/UI/Widgets/UIPlayerStatus_StatRow.h new file mode 100644 index 0000000..ae9ce20 --- /dev/null +++ b/Source/ZMMO/Game/UI/Widgets/UIPlayerStatus_StatRow.h @@ -0,0 +1,68 @@ +#pragma once + +#include "CoreMinimal.h" +#include "CommonUserWidget.h" +#include "UIPlayerStatus_StatRow.generated.h" + +class UButton; +class UHorizontalBox; +class UTextBlock; + +DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FZMMOOnStatRowPlusClicked, int32, StatId); + +/** + * Linha de um stat primário (STR/AGI/VIT/INT/DEX/LUK) na janela de Status. + * Layout: ícone + nome + valor base + (sinal + bônus) + botão "+". + * + * Sem lógica de domínio — a janela (UUIPlayerStatus_Window) injeta o + * StatId e os valores via Setup/SetValues; o clique no "+" é re-emitido + * como OnPlusClicked(StatId). + * + * Pareada com o WBP UI_PlayerStatus_StatRow. + */ +UCLASS(Abstract, Blueprintable) +class ZMMO_API UUIPlayerStatus_StatRow : public UCommonUserWidget +{ + GENERATED_BODY() + +public: + /** Setup único chamado pela window quando inicializa a linha. */ + UFUNCTION(BlueprintCallable, Category = "Zeus|Status") + void SetupStat(int32 InStatId, FText IconLetter, FText DisplayName); + + /** Atualiza valores. Bonus=0 esconde a parte "+N". */ + UFUNCTION(BlueprintCallable, Category = "Zeus|Status") + void SetValues(int32 Base, int32 Bonus); + + /** Mostra/esconde o botão "+". */ + UFUNCTION(BlueprintCallable, Category = "Zeus|Status") + void SetPlusVisible(bool bVisible); + + /** Habilita/desabilita o botão "+" (independente da visibilidade). */ + UFUNCTION(BlueprintCallable, Category = "Zeus|Status") + void SetPlusEnabled(bool bEnabled); + + /** Disparado quando o usuário clica em "+". */ + UPROPERTY(BlueprintAssignable, Category = "Zeus|Status") + FZMMOOnStatRowPlusClicked OnPlusClicked; + +protected: + virtual void NativeOnInitialized() override; + virtual void NativeDestruct() override; + + UPROPERTY(meta = (BindWidgetOptional)) TObjectPtr Border_StatIconWrap_Label; + UPROPERTY(meta = (BindWidgetOptional)) TObjectPtr Text_StatName; + UPROPERTY(meta = (BindWidgetOptional)) TObjectPtr Text_StatBase; + UPROPERTY(meta = (BindWidgetOptional)) TObjectPtr Text_Sign; + UPROPERTY(meta = (BindWidgetOptional)) TObjectPtr Text_StatBonus; + UPROPERTY(meta = (BindWidgetOptional)) TObjectPtr Button_PlusBtn; + + /** HBox que envolve sinal+bonus; ocultado quando bonus = 0. Bind opcional — + * se o WBP não expuser, cai pra ocultar Text_Sign/Text_StatBonus individualmente. */ + UPROPERTY(meta = (BindWidgetOptional)) TObjectPtr Layout_BonusGroup; + +private: + UFUNCTION() void HandlePlusClicked(); + + int32 StatId = 0; +}; diff --git a/Source/ZMMO/Game/UI/Widgets/UIPlayerStatus_Window.cpp b/Source/ZMMO/Game/UI/Widgets/UIPlayerStatus_Window.cpp new file mode 100644 index 0000000..5a6bad1 --- /dev/null +++ b/Source/ZMMO/Game/UI/Widgets/UIPlayerStatus_Window.cpp @@ -0,0 +1,280 @@ +#include "UIPlayerStatus_Window.h" + +#include "CommonInputTypeEnum.h" +#include "Components/Button.h" +#include "Components/HorizontalBox.h" +#include "Components/TextBlock.h" +#include "InputCoreTypes.h" +#include "Engine/World.h" +#include "GameFramework/PlayerController.h" +#include "GameFramework/PlayerState.h" +#include "Internationalization/Text.h" +#include "ZMMO.h" +#include "ZMMOAttributeComponent.h" +#include "ZMMOJobDataAsset.h" +#include "ZMMOJobsSubsystem.h" +#include "ZMMOPlayerState.h" +#include "UI/InGame/UIInGameFlowSubsystem.h" +#include "UI/InGameTypes.h" +#include "UI/Widgets/UIPlayerStatus_BonusRow.h" +#include "UI/Widgets/UIPlayerStatus_StatRow.h" + +UUIPlayerStatus_Window::UUIPlayerStatus_Window(const FObjectInitializer& ObjectInitializer) + : Super(ObjectInitializer) +{ + // bIsBackHandler exige DefaultBackAction configurada no CommonUISettings; + // projeto ainda não tem DT de input actions, então registrar o handler + // poluía o log com "Cannot create action binding". Fechamento da janela + // vai por Alt+A (NativeOnKeyDown) e Button_Close. + bAutoActivate = true; + // Focusable + DesiredFocusTarget=self → recebe NativeOnKeyDown (Alt+A). + SetIsFocusable(true); +} + +UWidget* UUIPlayerStatus_Window::NativeGetDesiredFocusTarget() const +{ + return const_cast(this); +} + +TOptional UUIPlayerStatus_Window::GetDesiredInputConfig() const +{ + return FUIInputConfig(ECommonInputMode::Menu, EMouseCaptureMode::NoCapture); +} + +void UUIPlayerStatus_Window::NativeOnActivated() +{ + Super::NativeOnActivated(); + + BindRow(Row_Str, 0, TEXT("S"), TEXT("STR")); + BindRow(Row_Agi, 1, TEXT("A"), TEXT("AGI")); + BindRow(Row_Vit, 2, TEXT("V"), TEXT("VIT")); + BindRow(Row_Int, 3, TEXT("I"), TEXT("INT")); + BindRow(Row_Dex, 4, TEXT("D"), TEXT("DEX")); + BindRow(Row_Luk, 5, TEXT("L"), TEXT("LUK")); + + if (Row_Atk) Row_Atk->SetStatName(FText::FromString(TEXT("ATK"))); + if (Row_Matk) Row_Matk->SetStatName(FText::FromString(TEXT("MATK"))); + if (Row_Def) Row_Def->SetStatName(FText::FromString(TEXT("DEF"))); + if (Row_Mdef) Row_Mdef->SetStatName(FText::FromString(TEXT("MDEF"))); + if (Row_Hit) Row_Hit->SetStatName(FText::FromString(TEXT("HIT"))); + if (Row_Flee) Row_Flee->SetStatName(FText::FromString(TEXT("FLEE"))); + if (Row_Crit) Row_Crit->SetStatName(FText::FromString(TEXT("CRIT"))); + if (Row_Aspd) Row_Aspd->SetStatName(FText::FromString(TEXT("ASPD"))); + + if (TitleText) TitleText->SetText(FText::FromString(TEXT("Status"))); + + if (Button_Close) Button_Close->OnClicked.AddDynamic(this, &UUIPlayerStatus_Window::HandleCloseClicked); + + // Background_Panel default é SELF_HIT_TEST_INVISIBLE; precisa Visible + // pra capturar clicks em areas vazias do modal e bubbla pra Window. + if (Background_Panel) + { + Background_Panel->SetVisibility(ESlateVisibility::Visible); + } + + BindToLocalPlayer(); + + UE_LOG(LogZMMO, Log, TEXT("UIPlayerStatus_Window activated")); +} + +void UUIPlayerStatus_Window::NativeOnDeactivated() +{ + UnbindFromComponent(); + + auto UnbindRow = [this](UUIPlayerStatus_StatRow* Row) + { + if (Row) Row->OnPlusClicked.RemoveDynamic(this, &UUIPlayerStatus_Window::HandleRowPlusClicked); + }; + UnbindRow(Row_Str); UnbindRow(Row_Agi); UnbindRow(Row_Vit); + UnbindRow(Row_Int); UnbindRow(Row_Dex); UnbindRow(Row_Luk); + + if (Button_Close) Button_Close->OnClicked.RemoveDynamic(this, &UUIPlayerStatus_Window::HandleCloseClicked); + + Super::NativeOnDeactivated(); +} + +void UUIPlayerStatus_Window::BindRow(UUIPlayerStatus_StatRow* Row, int32 StatId, const TCHAR* IconLetter, const TCHAR* DisplayName) +{ + if (!Row) return; + Row->SetupStat(StatId, FText::FromString(IconLetter), FText::FromString(DisplayName)); + Row->OnPlusClicked.AddDynamic(this, &UUIPlayerStatus_Window::HandleRowPlusClicked); +} + +void UUIPlayerStatus_Window::BindToLocalPlayer() +{ + UWorld* World = GetWorld(); + if (!World) return; + APlayerController* PC = World->GetFirstPlayerController(); + if (!PC || !PC->PlayerState) return; + + // CharName vem do AZMMOPlayerState (setado pelo S_SPAWN_PLAYER no spawn + // local). Fallback pra APlayerState::GetPlayerName() se o cast falhar ou + // se CharName ainda nao chegou (race com handler que dispara o widget). + if (Text_CharName) + { + FString DisplayName = PC->PlayerState->GetPlayerName(); + if (const AZMMOPlayerState* ZMMOPs = Cast(PC->PlayerState)) + { + if (!ZMMOPs->CharName.IsEmpty()) + { + DisplayName = ZMMOPs->CharName; + } + } + Text_CharName->SetText(FText::FromString(DisplayName)); + } + + UZMMOAttributeComponent* Comp = PC->PlayerState->FindComponentByClass(); + if (!Comp || Comp == BoundComponent.Get()) return; + + UnbindFromComponent(); + BoundComponent = Comp; + Comp->OnAttributesChanged.AddDynamic(this, &UUIPlayerStatus_Window::HandleAttributesChanged); + Comp->OnStatAllocReply.AddDynamic(this, &UUIPlayerStatus_Window::HandleStatAllocReply); + RefreshFromSnapshot(Comp->GetSnapshot()); +} + +void UUIPlayerStatus_Window::UnbindFromComponent() +{ + if (UZMMOAttributeComponent* Old = BoundComponent.Get()) + { + Old->OnAttributesChanged.RemoveDynamic(this, &UUIPlayerStatus_Window::HandleAttributesChanged); + Old->OnStatAllocReply.RemoveDynamic(this, &UUIPlayerStatus_Window::HandleStatAllocReply); + } + BoundComponent.Reset(); +} + +void UUIPlayerStatus_Window::HandleAttributesChanged(const FZMMOAttributesSnapshot& Snapshot) +{ + bPlusButtonsLocked = false; + RefreshFromSnapshot(Snapshot); +} + +void UUIPlayerStatus_Window::HandleStatAllocReply(bool bAccepted, int32 Reason) +{ + UE_LOG(LogZMMO, Log, TEXT("UIPlayerStatus_Window: alloc reply accepted=%d reason=%d"), + bAccepted ? 1 : 0, Reason); + + bPlusButtonsLocked = false; + if (UZMMOAttributeComponent* Comp = BoundComponent.Get()) + { + const FZMMOAttributesSnapshot& S = Comp->GetSnapshot(); + SetAllPrimaryRowsPlusEnabled(S.StatusPoint > 0); + } +} + +void UUIPlayerStatus_Window::HandleRowPlusClicked(int32 StatId) +{ + if (bPlusButtonsLocked) return; + UZMMOAttributeComponent* Comp = BoundComponent.Get(); + if (!Comp) return; + + bPlusButtonsLocked = true; + SetAllPrimaryRowsPlusEnabled(false); + + Comp->RequestStatAlloc(StatId, /*Amount*/ 1); +} + +void UUIPlayerStatus_Window::SetAllPrimaryRowsPlusEnabled(bool bEnabled) +{ + auto Set = [bEnabled](UUIPlayerStatus_StatRow* Row) + { + if (Row) Row->SetPlusEnabled(bEnabled); + }; + Set(Row_Str); Set(Row_Agi); Set(Row_Vit); + Set(Row_Int); Set(Row_Dex); Set(Row_Luk); +} + +void UUIPlayerStatus_Window::RefreshFromSnapshot(const FZMMOAttributesSnapshot& S) +{ + // Primários: snapshot atual não splita base/bonus de primários (só derivados). + if (Row_Str) Row_Str->SetValues(S.Str, 0); + if (Row_Agi) Row_Agi->SetValues(S.Agi, 0); + if (Row_Vit) Row_Vit->SetValues(S.Vit, 0); + if (Row_Int) Row_Int->SetValues(S.Int, 0); + if (Row_Dex) Row_Dex->SetValues(S.Dex, 0); + if (Row_Luk) Row_Luk->SetValues(S.Luk, 0); + + // Header (level/job). + if (Text_BaseLevel_Value) Text_BaseLevel_Value->SetText(FText::AsNumber(S.BaseLevel)); + if (Text_JobLevel_Value) Text_JobLevel_Value->SetText(FText::AsNumber(S.JobLevel)); + if (Text_Job) + { + FText JobName; + if (UGameInstance* GI = GetGameInstance()) + { + if (const UZMMOJobsSubsystem* Jobs = GI->GetSubsystem()) + { + if (const UZMMOJobDataAsset* Data = Jobs->GetJobData(S.ClassId)) + { + JobName = Data->DisplayName; + } + } + } + if (JobName.IsEmpty()) + { + JobName = FText::FromString(FString::Printf(TEXT("Job %d"), S.ClassId)); + } + Text_Job->SetText(JobName); + } + + if (StatusPointText) + { + StatusPointText->SetText(FText::FromString( + FString::Printf(TEXT("Status Points: %d"), S.StatusPoint))); + } + + // Derivados (server-authoritative: base + equip). + if (Row_Atk) Row_Atk->SetValues(S.AtkBase, S.AtkEquipBonus); + if (Row_Matk) Row_Matk->SetRangeValues(S.MatkBaseMin, S.MatkBaseMax, S.MatkEquipBonus); + if (Row_Def) Row_Def->SetValues(S.DefBase, S.DefEquipBonus); + if (Row_Mdef) Row_Mdef->SetValues(S.MdefBase, S.MdefEquipBonus); + if (Row_Hit) Row_Hit->SetValues(S.HitBase, S.HitEquipBonus); + if (Row_Flee) Row_Flee->SetValues(S.FleeBase, S.FleeEquipBonus); + if (Row_Crit) Row_Crit->SetValuesX10(S.CritBaseX10, S.CritEquipBonusX10); + // ASPD não splita. + if (Row_Aspd) Row_Aspd->SetValues(S.Aspd, 0); + + const bool bCanAlloc = !bPlusButtonsLocked && S.StatusPoint > 0; + SetAllPrimaryRowsPlusEnabled(bCanAlloc); +} + +void UUIPlayerStatus_Window::HandleCloseClicked() +{ + if (UGameInstance* GI = GetGameInstance()) + { + if (UUIInGameFlowSubsystem* Flow = GI->GetSubsystem()) + { + Flow->SetState(EZMMOInGameUIState::Playing); + } + } +} + +FReply UUIPlayerStatus_Window::NativeOnKeyDown(const FGeometry& Geom, const FKeyEvent& KeyEvent) +{ + // Alt+A em modo Menu — PlayerController nao recebe (CommonUI absorve). + // Handle aqui pra completar o toggle. + if (KeyEvent.GetKey() == EKeys::A && KeyEvent.IsAltDown()) + { + if (UGameInstance* GI = GetGameInstance()) + { + if (UUIInGameFlowSubsystem* Flow = GI->GetSubsystem()) + { + Flow->SetState(EZMMOInGameUIState::Playing); + return FReply::Handled(); + } + } + } + return Super::NativeOnKeyDown(Geom, KeyEvent); +} + +bool UUIPlayerStatus_Window::IsDragHandleHit(const FPointerEvent& MouseEvent) const +{ + // Drag em qualquer parte do modal EXCETO Container_Stats (rows + botões +STR). + // Botão close consome o evento antes (UButton.OnClicked) e não bubbla aqui. + const FVector2D Pos = MouseEvent.GetScreenSpacePosition(); + if (Container_Stats && Container_Stats->GetCachedGeometry().IsUnderLocation(Pos)) + { + return false; + } + return true; +} diff --git a/Source/ZMMO/Game/UI/Widgets/UIPlayerStatus_Window.h b/Source/ZMMO/Game/UI/Widgets/UIPlayerStatus_Window.h new file mode 100644 index 0000000..b1541ff --- /dev/null +++ b/Source/ZMMO/Game/UI/Widgets/UIPlayerStatus_Window.h @@ -0,0 +1,112 @@ +#pragma once + +#include "CoreMinimal.h" +#include "UI/Common/UIDraggableWindow_Base.h" +#include "ZMMOAttributeTypes.h" +#include "UIPlayerStatus_Window.generated.h" + +class UButton; +class UTextBlock; +class UHorizontalBox; +class UUIPlayerStatus_BonusRow; +class UUIPlayerStatus_StatRow; +class UZMMOAttributeComponent; + +/** + * Janela de Status do player — composta por instâncias dos sub-widgets + * UUIPlayerStatus_StatRow (6 primários) e UUIPlayerStatus_BonusRow (8 derivados). + * + * Consome o UZMMOAttributeComponent do PlayerState e a pipeline de + * C_STAT_ALLOC (RequestStatAlloc → server valida → OnStatAllocReply). + * + * Pareada com o WBP UI_PlayerStatus_Window. Os 14 rows são BindWidgetOptional + * pra suportar variantes (ex.: HUD compacto sem CRIT/ASPD). + */ +UCLASS(Abstract, Blueprintable, BlueprintType) +class ZMMO_API UUIPlayerStatus_Window : public UUIDraggableWindow_Base +{ + GENERATED_BODY() + +public: + UUIPlayerStatus_Window(const FObjectInitializer& ObjectInitializer); + + /** Chave de persistencia no UZMMOUISaveGame. Estavel — nao renomear. */ + virtual FName GetWindowId() const override { return TEXT("PlayerStatus"); } + +protected: + virtual void NativeOnActivated() override; + virtual void NativeOnDeactivated() override; + virtual TOptional GetDesiredInputConfig() const override; + + /** Diz ao stack pra mover foco pra ca' (precisa pra NativeOnKeyDown ser chamado). */ + virtual UWidget* NativeGetDesiredFocusTarget() const override; + + /** Atalho Alt+A — fecha a janela em modo Menu (PC controller nao recebe). */ + virtual FReply NativeOnKeyDown(const FGeometry& Geom, const FKeyEvent& KeyEvent) override; + + /** Drag area = Container_Header (header inteiro). Header_DragHandle no WBP nao necessario. */ + virtual bool IsDragHandleHit(const FPointerEvent& MouseEvent) const override; + + /** Header do WBP (HorizontalBox raiz da identidade) — usado como drag area. */ + UPROPERTY(BlueprintReadOnly, meta = (BindWidgetOptional), Category = "Zeus|Status|Header") TObjectPtr Container_Header; + + /** + * Zona EXCLUÍDA do drag — é onde ficam os rows interativos (+STR etc.). + * Clicar aqui não deve iniciar drag mesmo em area "vazia" entre rows. + */ + UPROPERTY(BlueprintReadOnly, meta = (BindWidgetOptional), Category = "Zeus|Status") TObjectPtr Container_Stats; + + /** + * Panel de fundo (UIPanel_Base). Setado pra Visible em NativeOnActivated + * pra capturar clicks em areas vazias do modal — sem isso, drag só funciona + * onde algum filho é Visible (header/footer). + */ + UPROPERTY(BlueprintReadOnly, meta = (BindWidgetOptional), Category = "Zeus|Status") TObjectPtr Background_Panel; + + UPROPERTY(BlueprintReadOnly, meta = (BindWidgetOptional), Category = "Zeus|Status") TObjectPtr TitleText; + UPROPERTY(BlueprintReadOnly, meta = (BindWidgetOptional), Category = "Zeus|Status") TObjectPtr StatusPointText; + + // Header: identidade do char. + UPROPERTY(BlueprintReadOnly, meta = (BindWidgetOptional), Category = "Zeus|Status|Header") TObjectPtr Text_CharName; + UPROPERTY(BlueprintReadOnly, meta = (BindWidgetOptional), Category = "Zeus|Status|Header") TObjectPtr Text_Job; + UPROPERTY(BlueprintReadOnly, meta = (BindWidgetOptional), Category = "Zeus|Status|Header") TObjectPtr Text_BaseLevel_Value; + UPROPERTY(BlueprintReadOnly, meta = (BindWidgetOptional), Category = "Zeus|Status|Header") TObjectPtr Text_JobLevel_Value; + + UPROPERTY(BlueprintReadOnly, meta = (BindWidgetOptional), Category = "Zeus|Status") TObjectPtr Row_Str; + UPROPERTY(BlueprintReadOnly, meta = (BindWidgetOptional), Category = "Zeus|Status") TObjectPtr Row_Agi; + UPROPERTY(BlueprintReadOnly, meta = (BindWidgetOptional), Category = "Zeus|Status") TObjectPtr Row_Vit; + UPROPERTY(BlueprintReadOnly, meta = (BindWidgetOptional), Category = "Zeus|Status") TObjectPtr Row_Int; + UPROPERTY(BlueprintReadOnly, meta = (BindWidgetOptional), Category = "Zeus|Status") TObjectPtr Row_Dex; + UPROPERTY(BlueprintReadOnly, meta = (BindWidgetOptional), Category = "Zeus|Status") TObjectPtr Row_Luk; + + UPROPERTY(BlueprintReadOnly, meta = (BindWidgetOptional), Category = "Zeus|Status|Derived") TObjectPtr Row_Atk; + UPROPERTY(BlueprintReadOnly, meta = (BindWidgetOptional), Category = "Zeus|Status|Derived") TObjectPtr Row_Matk; + UPROPERTY(BlueprintReadOnly, meta = (BindWidgetOptional), Category = "Zeus|Status|Derived") TObjectPtr Row_Def; + UPROPERTY(BlueprintReadOnly, meta = (BindWidgetOptional), Category = "Zeus|Status|Derived") TObjectPtr Row_Mdef; + UPROPERTY(BlueprintReadOnly, meta = (BindWidgetOptional), Category = "Zeus|Status|Derived") TObjectPtr Row_Hit; + UPROPERTY(BlueprintReadOnly, meta = (BindWidgetOptional), Category = "Zeus|Status|Derived") TObjectPtr Row_Flee; + UPROPERTY(BlueprintReadOnly, meta = (BindWidgetOptional), Category = "Zeus|Status|Derived") TObjectPtr Row_Crit; + UPROPERTY(BlueprintReadOnly, meta = (BindWidgetOptional), Category = "Zeus|Status|Derived") TObjectPtr Row_Aspd; + + /** Botao de fechar na header — UButton simples (placeholder, estilizar depois). */ + UPROPERTY(BlueprintReadOnly, meta = (BindWidgetOptional), Category = "Zeus|Status") TObjectPtr Button_Close; + +private: + void BindToLocalPlayer(); + void UnbindFromComponent(); + void RefreshFromSnapshot(const FZMMOAttributesSnapshot& Snap); + void SetAllPrimaryRowsPlusEnabled(bool bEnabled); + void BindRow(UUIPlayerStatus_StatRow* Row, int32 StatId, const TCHAR* IconLetter, const TCHAR* DisplayName); + + UFUNCTION() void HandleAttributesChanged(const FZMMOAttributesSnapshot& Snapshot); + UFUNCTION() void HandleStatAllocReply(bool bAccepted, int32 Reason); + UFUNCTION() void HandleRowPlusClicked(int32 StatId); + UFUNCTION() void HandleCloseClicked(); + + UPROPERTY(Transient) + TWeakObjectPtr BoundComponent; + + /** Trava local pra evitar spam-click: vira true ao clicar, false ao + * receber StatAllocReply (ou snapshot novo, o que vier primeiro). */ + bool bPlusButtonsLocked = false; +}; diff --git a/Source/ZMMO/Game/UI/Widgets/UIProgressBar_Base.cpp b/Source/ZMMO/Game/UI/Widgets/UIProgressBar_Base.cpp new file mode 100644 index 0000000..b4e5045 --- /dev/null +++ b/Source/ZMMO/Game/UI/Widgets/UIProgressBar_Base.cpp @@ -0,0 +1,761 @@ +#include "UIProgressBar_Base.h" + +#include "Components/Image.h" +#include "Components/OverlaySlot.h" +#include "Engine/DataTable.h" +#include "Engine/GameInstance.h" +#include "Materials/MaterialInstanceDynamic.h" +#include "Materials/MaterialInterface.h" +#include "Styling/SlateBrush.h" +#include "Styling/SlateTypes.h" +#include "UI/UIStyleRow.h" +#include "UI/UIStyleTokens.h" +#include "ZMMOThemeSubsystem.h" + +namespace +{ + constexpr float MinAnimSeconds = 0.001f; + + // Fill material params + static const FName P_PrimaryLevel("PrimaryLevel"); + static const FName P_SecondaryLevel("SecondaryLevel"); + static const FName P_PrimaryColour("PrimaryColour"); + static const FName P_PrimaryColourBottom("PrimaryColourBottom"); + static const FName P_SecondaryColour("SecondaryColour"); + static const FName P_SecondaryColourBottom("SecondaryColourBottom"); + static const FName P_EmptyColour("EmptyColour"); + static const FName P_EnableGradient("EnableGradient"); + static const FName P_GradientSplit("GradientSplit"); + + // BG material params + static const FName P_BackgroundColour("BackgroundColour"); + + // TrackGrid material params + static const FName P_GridColour("GridColour"); + static const FName P_GridDivisions("GridDivisions"); + static const FName P_GridLineThickness("GridLineThickness"); + + // Shimmer material params + static const FName P_ShimmerColour("ShimmerColour"); + static const FName P_ShimmerWidth("ShimmerWidth"); + static const FName P_ShimmerSpeed("ShimmerSpeed"); + static const FName P_ShimmerSoftness("ShimmerSoftness"); + + // Edge material params + static const FName P_EdgeColour("EdgeColour"); + static const FName P_EdgeWidth("EdgeWidth"); + static const FName P_EdgeGlowWidth("EdgeGlowWidth"); + static const FName P_EdgeGlowIntensity("EdgeGlowIntensity"); + + FORCEINLINE float EaseOutCubic(float Alpha) + { + return 1.f - FMath::Pow(1.f - Alpha, 3.f); + } + + /** Resolve a FUIStyle ativa — runtime via ThemeSubsystem, design-time via DT direto. */ + FUIStyle ResolveProgressBarStyle(const UUserWidget* Widget) + { + if (Widget) + { + if (const UGameInstance* GI = Widget->GetGameInstance()) + { + if (const UZMMOThemeSubsystem* Theme = GI->GetSubsystem()) + { + return Theme->GetActiveUIStyle(); + } + } + } + // Design-time fallback: lê DT_UI_Styles row "Default" direto do disk. + if (const UDataTable* DT = LoadObject(nullptr, + TEXT("/Game/ZMMO/Data/UI/DT_UI_Styles.DT_UI_Styles"))) + { + if (const FUIStyleRow* Row = DT->FindRow( + FName(TEXT("Default")), TEXT("UIProgressBarDesign"), false)) + { + return Row->Style; + } + } + return FUIStyle(); + } + + TArray CollectVariantOptions() + { + TArray Options; + Options.Add(FString()); // entrada vazia = sem theme + const UDataTable* DT = LoadObject(nullptr, + TEXT("/Game/ZMMO/Data/UI/DT_UI_Styles.DT_UI_Styles")); + if (!DT) return Options; + const FUIStyleRow* Row = DT->FindRow( + FName(TEXT("Default")), TEXT("UIProgressBarGetOptions"), false); + if (!Row) return Options; + for (const TPair& Pair : Row->Style.ProgressBar.Variants) + { + Options.Add(Pair.Key.ToString()); + } + Options.Sort(); + return Options; + } +} + +void UUIProgressBar_Base::NativePreConstruct() +{ + Super::NativePreConstruct(); + + CurrentPrimaryLevel = InitialPrimaryLevel; + TargetPrimaryLevel = InitialPrimaryLevel; + PrimaryStart = InitialPrimaryLevel; + PrimaryElapsed = 0.f; + bAnimatingPrimary = false; + + CurrentSecondaryLevel = InitialSecondaryLevel; + TargetSecondaryLevel = InitialSecondaryLevel; + SecondaryStart = InitialSecondaryLevel; + SecondaryElapsed = 0.f; + bAnimatingSecondary = false; + + if (bUseTheme) + { + RefreshUIStyle(); + } + else + { + ApplyBrushStyle(); + EnsureMIDs(); + ApplyMaterialOverrides(); + } + ApplyPrimaryToMID(CurrentPrimaryLevel); + ApplySecondaryToMID(CurrentSecondaryLevel); +} + +void UUIProgressBar_Base::NativeConstruct() +{ + Super::NativeConstruct(); + + if (!bThemeBound) + { + if (const UGameInstance* GI = GetGameInstance()) + { + if (UZMMOThemeSubsystem* Theme = GI->GetSubsystem()) + { + Theme->OnThemeChanged.AddDynamic(this, &UUIProgressBar_Base::HandleThemeChanged); + bThemeBound = true; + } + } + } + + if (bUseTheme) + { + RefreshUIStyle(); + } + else + { + ApplyBrushStyle(); + EnsureMIDs(); + ApplyMaterialOverrides(); + } + ApplyPrimaryToMID(CurrentPrimaryLevel); + ApplySecondaryToMID(CurrentSecondaryLevel); +} + +void UUIProgressBar_Base::NativeDestruct() +{ + if (bThemeBound) + { + if (const UGameInstance* GI = GetGameInstance()) + { + if (UZMMOThemeSubsystem* Theme = GI->GetSubsystem()) + { + Theme->OnThemeChanged.RemoveDynamic(this, &UUIProgressBar_Base::HandleThemeChanged); + } + } + bThemeBound = false; + } + Super::NativeDestruct(); +} + +void UUIProgressBar_Base::HandleThemeChanged(FName /*NewThemeId*/) +{ + if (bUseTheme) + { + RefreshUIStyle(); + } +} + +void UUIProgressBar_Base::NativeTick(const FGeometry& MyGeometry, float InDeltaTime) +{ + Super::NativeTick(MyGeometry, InDeltaTime); + + if (!bAnimatingPrimary && !bAnimatingSecondary) + { + return; + } + + if (bAnimatingPrimary) + { + PrimaryElapsed += InDeltaTime; + const float Duration = FMath::Max(PrimaryAnimationSeconds, MinAnimSeconds); + const float Alpha = FMath::Clamp(PrimaryElapsed / Duration, 0.f, 1.f); + CurrentPrimaryLevel = FMath::Lerp(PrimaryStart, TargetPrimaryLevel, EaseOutCubic(Alpha)); + ApplyPrimaryToMID(CurrentPrimaryLevel); + if (Alpha >= 1.f) + { + CurrentPrimaryLevel = TargetPrimaryLevel; + ApplyPrimaryToMID(CurrentPrimaryLevel); + bAnimatingPrimary = false; + } + } + + if (bAnimatingSecondary) + { + SecondaryElapsed += InDeltaTime; + const float Duration = FMath::Max(SecondaryAnimationSeconds, MinAnimSeconds); + const float Alpha = FMath::Clamp(SecondaryElapsed / Duration, 0.f, 1.f); + CurrentSecondaryLevel = FMath::Lerp(SecondaryStart, TargetSecondaryLevel, EaseOutCubic(Alpha)); + ApplySecondaryToMID(CurrentSecondaryLevel); + if (Alpha >= 1.f) + { + CurrentSecondaryLevel = TargetSecondaryLevel; + ApplySecondaryToMID(CurrentSecondaryLevel); + bAnimatingSecondary = false; + } + } +} + +void UUIProgressBar_Base::SetTargetPrimaryLevel(float Primary01) +{ + const float Clamped = FMath::Clamp(Primary01, 0.f, 1.f); + if (PrimaryAnimationSeconds <= MinAnimSeconds) + { + SetPrimaryLevelImmediate(Clamped); + return; + } + if (FMath::IsNearlyEqual(Clamped, CurrentPrimaryLevel, KINDA_SMALL_NUMBER) && + FMath::IsNearlyEqual(Clamped, TargetPrimaryLevel, KINDA_SMALL_NUMBER)) + { + return; + } + TargetPrimaryLevel = Clamped; + PrimaryStart = CurrentPrimaryLevel; + PrimaryElapsed = 0.f; + bAnimatingPrimary = true; +} + +void UUIProgressBar_Base::SetPrimaryLevelImmediate(float Primary01) +{ + const float Clamped = FMath::Clamp(Primary01, 0.f, 1.f); + CurrentPrimaryLevel = Clamped; + TargetPrimaryLevel = Clamped; + PrimaryStart = Clamped; + PrimaryElapsed = 0.f; + bAnimatingPrimary = false; + ApplyPrimaryToMID(CurrentPrimaryLevel); +} + +void UUIProgressBar_Base::SetTargetSecondaryLevel(float Sec01) +{ + const float Clamped = FMath::Clamp(Sec01, 0.f, 1.f); + if (SecondaryAnimationSeconds <= MinAnimSeconds) + { + SetSecondaryLevelImmediate(Clamped); + return; + } + if (FMath::IsNearlyEqual(Clamped, CurrentSecondaryLevel, KINDA_SMALL_NUMBER) && + FMath::IsNearlyEqual(Clamped, TargetSecondaryLevel, KINDA_SMALL_NUMBER)) + { + return; + } + TargetSecondaryLevel = Clamped; + SecondaryStart = CurrentSecondaryLevel; + SecondaryElapsed = 0.f; + bAnimatingSecondary = true; +} + +void UUIProgressBar_Base::SetSecondaryLevelImmediate(float Sec01) +{ + const float Clamped = FMath::Clamp(Sec01, 0.f, 1.f); + CurrentSecondaryLevel = Clamped; + TargetSecondaryLevel = Clamped; + SecondaryStart = Clamped; + SecondaryElapsed = 0.f; + bAnimatingSecondary = false; + ApplySecondaryToMID(CurrentSecondaryLevel); +} + +void UUIProgressBar_Base::SetDrawStyle(EUIProgressBarStyle InStyle) +{ + DrawStyle = InStyle; + ApplyBrushStyle(); +} + +void UUIProgressBar_Base::SetCornerRadius(float RadiusPixels) +{ + CornerRadius = FMath::Clamp(RadiusPixels, 0.f, 100.f); + ApplyBrushStyle(); +} + +void UUIProgressBar_Base::SetBorderVisible(bool bVisible) +{ + bShowBorder = bVisible; + ApplyBrushStyle(); +} + +void UUIProgressBar_Base::SetBorderColour(FLinearColor Color) +{ + BorderColour = Color; + bShowBorder = true; + ApplyBrushStyle(); +} + +void UUIProgressBar_Base::SetBorderWidth(float WidthPixels) +{ + BorderWidth = FMath::Clamp(WidthPixels, 0.f, 10.f); + ApplyBrushStyle(); +} + +void UUIProgressBar_Base::SetPrimaryColour(FLinearColor Color) +{ + PrimaryColour = Color; + bOverridePrimaryColour = true; + if (FillMID) FillMID->SetVectorParameterValue(P_PrimaryColour, Color); +} + +void UUIProgressBar_Base::SetPrimaryColourBottom(FLinearColor Color) +{ + PrimaryColourBottom = Color; + bOverridePrimaryColourBottom = true; + if (FillMID) FillMID->SetVectorParameterValue(P_PrimaryColourBottom, Color); +} + +void UUIProgressBar_Base::SetSecondaryColour(FLinearColor Color) +{ + SecondaryColour = Color; + bOverrideSecondaryColour = true; + if (FillMID) FillMID->SetVectorParameterValue(P_SecondaryColour, Color); +} + +void UUIProgressBar_Base::SetSecondaryColourBottom(FLinearColor Color) +{ + SecondaryColourBottom = Color; + bOverrideSecondaryColourBottom = true; + if (FillMID) FillMID->SetVectorParameterValue(P_SecondaryColourBottom, Color); +} + +void UUIProgressBar_Base::SetEmptyColour(FLinearColor Color) +{ + EmptyColour = Color; + bOverrideEmptyColour = true; + if (FillMID) FillMID->SetVectorParameterValue(P_EmptyColour, Color); +} + +void UUIProgressBar_Base::SetBackgroundColour(FLinearColor Color) +{ + BackgroundColour = Color; + bOverrideBackgroundColour = true; + if (BackgroundMID) BackgroundMID->SetVectorParameterValue(P_BackgroundColour, Color); +} + +void UUIProgressBar_Base::SetTrackGridVisible(bool bVisible) +{ + bShowTrackGrid = bVisible; + if (TrackGridImage) + { + TrackGridImage->SetVisibility(bVisible ? ESlateVisibility::HitTestInvisible : ESlateVisibility::Collapsed); + } +} + +void UUIProgressBar_Base::SetGridColour(FLinearColor Color) +{ + GridColour = Color; + bOverrideGridColour = true; + if (TrackGridMID) TrackGridMID->SetVectorParameterValue(P_GridColour, Color); +} + +void UUIProgressBar_Base::SetGridDivisions(float Divisions) +{ + GridDivisions = FMath::Clamp(Divisions, 2.f, 50.f); + bOverrideGridDivisions = true; + if (TrackGridMID) TrackGridMID->SetScalarParameterValue(P_GridDivisions, GridDivisions); +} + +void UUIProgressBar_Base::SetGridLineThickness(float Thickness) +{ + GridLineThickness = FMath::Clamp(Thickness, 0.001f, 0.2f); + bOverrideGridLineThickness = true; + if (TrackGridMID) TrackGridMID->SetScalarParameterValue(P_GridLineThickness, GridLineThickness); +} + +void UUIProgressBar_Base::SetEdgeVisible(bool bVisible) +{ + bShowEdge = bVisible; + UpdateEdgeVisibility(); +} + +void UUIProgressBar_Base::SetEdgeColour(FLinearColor Color) +{ + EdgeColour = Color; + bOverrideEdgeColour = true; + if (EdgeMID) EdgeMID->SetVectorParameterValue(P_EdgeColour, Color); +} + +void UUIProgressBar_Base::SetEdgeWidth(float Width01) +{ + EdgeWidth = FMath::Clamp(Width01, 0.001f, 0.1f); + bOverrideEdgeWidth = true; + if (EdgeMID) EdgeMID->SetScalarParameterValue(P_EdgeWidth, EdgeWidth); +} + +void UUIProgressBar_Base::SetEdgeGlowWidth(float GlowWidth01) +{ + EdgeGlowWidth = FMath::Clamp(GlowWidth01, 0.f, 0.2f); + bOverrideEdgeGlowWidth = true; + if (EdgeMID) EdgeMID->SetScalarParameterValue(P_EdgeGlowWidth, EdgeGlowWidth); +} + +void UUIProgressBar_Base::SetEdgeGlowIntensity(float Intensity01) +{ + EdgeGlowIntensity = FMath::Clamp(Intensity01, 0.f, 1.f); + bOverrideEdgeGlowIntensity = true; + if (EdgeMID) EdgeMID->SetScalarParameterValue(P_EdgeGlowIntensity, EdgeGlowIntensity); +} + +void UUIProgressBar_Base::SetShimmerVisible(bool bVisible) +{ + bShowShimmer = bVisible; + if (ShimmerImage) + { + ShimmerImage->SetVisibility(bVisible ? ESlateVisibility::HitTestInvisible : ESlateVisibility::Collapsed); + } +} + +void UUIProgressBar_Base::SetShimmerColour(FLinearColor Color) +{ + ShimmerColour = Color; + bOverrideShimmerColour = true; + if (ShimmerMID) ShimmerMID->SetVectorParameterValue(P_ShimmerColour, Color); +} + +void UUIProgressBar_Base::SetShimmerWidth(float Width01) +{ + ShimmerWidth = FMath::Clamp(Width01, 0.05f, 1.f); + bOverrideShimmerWidth = true; + if (ShimmerMID) ShimmerMID->SetScalarParameterValue(P_ShimmerWidth, ShimmerWidth); +} + +void UUIProgressBar_Base::SetShimmerSpeed(float Speed) +{ + ShimmerSpeed = FMath::Clamp(Speed, 0.01f, 5.f); + bOverrideShimmerSpeed = true; + if (ShimmerMID) ShimmerMID->SetScalarParameterValue(P_ShimmerSpeed, ShimmerSpeed); +} + +void UUIProgressBar_Base::SetShimmerSoftness(float Softness) +{ + ShimmerSoftness = FMath::Clamp(Softness, 0.5f, 8.f); + bOverrideShimmerSoftness = true; + if (ShimmerMID) ShimmerMID->SetScalarParameterValue(P_ShimmerSoftness, ShimmerSoftness); +} + +void UUIProgressBar_Base::SetGradientEnabled(bool bEnabled) +{ + bEnableGradient = bEnabled; + bOverrideEnableGradient = true; + if (FillMID) FillMID->SetScalarParameterValue(P_EnableGradient, bEnabled ? 1.f : 0.f); +} + +void UUIProgressBar_Base::SetGradientSplit(float Split01) +{ + GradientSplit = FMath::Clamp(Split01, 0.f, 1.f); + bOverrideGradientSplit = true; + if (FillMID) FillMID->SetScalarParameterValue(P_GradientSplit, GradientSplit); +} + +void UUIProgressBar_Base::ShowHealPreview(float TargetPct, FLinearColor PreviewColor) +{ + SetSecondaryColour(PreviewColor); + SetTargetSecondaryLevel(TargetPct); +} + +void UUIProgressBar_Base::ShowDamageLag(float OldPct, float NewPct, FLinearColor LagColor) +{ + SetSecondaryColour(LagColor); + SetSecondaryLevelImmediate(OldPct); + SetPrimaryLevelImmediate(NewPct); + SetTargetSecondaryLevel(NewPct); +} + +void UUIProgressBar_Base::ShowBuffer(float BufferPct, FLinearColor BufferColor) +{ + SetSecondaryColour(BufferColor); + SetTargetSecondaryLevel(FMath::Clamp(CurrentPrimaryLevel + BufferPct, 0.f, 1.f)); +} + +void UUIProgressBar_Base::ClearSecondary() +{ + SetTargetSecondaryLevel(0.f); +} + +void UUIProgressBar_Base::EnsureMIDs() +{ + if (FillImage && !FillMID) + { + if (UMaterialInterface* Mat = Cast(FillImage->GetBrush().GetResourceObject())) + { + FillMID = UMaterialInstanceDynamic::Create(Mat, this); + FillImage->SetBrushFromMaterial(FillMID); + // SetBrushFromMaterial reseta DrawAs e OutlineSettings — re-aplica. + ApplyBrushStyle(); + } + } + if (BackgroundImage && !BackgroundMID) + { + if (UMaterialInterface* Mat = Cast(BackgroundImage->GetBrush().GetResourceObject())) + { + BackgroundMID = UMaterialInstanceDynamic::Create(Mat, this); + BackgroundImage->SetBrushFromMaterial(BackgroundMID); + ApplyBrushStyle(); + } + } + if (TrackGridImage && !TrackGridMID) + { + if (UMaterialInterface* Mat = Cast(TrackGridImage->GetBrush().GetResourceObject())) + { + TrackGridMID = UMaterialInstanceDynamic::Create(Mat, this); + TrackGridImage->SetBrushFromMaterial(TrackGridMID); + ApplyBrushStyle(); + } + } + if (ShimmerImage && !ShimmerMID) + { + if (UMaterialInterface* Mat = Cast(ShimmerImage->GetBrush().GetResourceObject())) + { + ShimmerMID = UMaterialInstanceDynamic::Create(Mat, this); + ShimmerImage->SetBrushFromMaterial(ShimmerMID); + ApplyBrushStyle(); + } + } + if (EdgeImage && !EdgeMID) + { + if (UMaterialInterface* Mat = Cast(EdgeImage->GetBrush().GetResourceObject())) + { + EdgeMID = UMaterialInstanceDynamic::Create(Mat, this); + EdgeImage->SetBrushFromMaterial(EdgeMID); + ApplyBrushStyle(); + } + } +} + +void UUIProgressBar_Base::ApplyMaterialOverrides() +{ + if (FillMID) + { + if (bOverridePrimaryColour) FillMID->SetVectorParameterValue(P_PrimaryColour, PrimaryColour); + if (bOverridePrimaryColourBottom) FillMID->SetVectorParameterValue(P_PrimaryColourBottom, PrimaryColourBottom); + if (bOverrideSecondaryColour) FillMID->SetVectorParameterValue(P_SecondaryColour, SecondaryColour); + if (bOverrideSecondaryColourBottom) FillMID->SetVectorParameterValue(P_SecondaryColourBottom, SecondaryColourBottom); + if (bOverrideEmptyColour) FillMID->SetVectorParameterValue(P_EmptyColour, EmptyColour); + if (bOverrideEnableGradient) FillMID->SetScalarParameterValue(P_EnableGradient, bEnableGradient ? 1.f : 0.f); + if (bOverrideGradientSplit) FillMID->SetScalarParameterValue(P_GradientSplit, GradientSplit); + } + if (BackgroundMID) + { + if (bOverrideBackgroundColour) BackgroundMID->SetVectorParameterValue(P_BackgroundColour, BackgroundColour); + } + if (TrackGridMID) + { + if (bOverrideGridColour) TrackGridMID->SetVectorParameterValue(P_GridColour, GridColour); + if (bOverrideGridDivisions) TrackGridMID->SetScalarParameterValue(P_GridDivisions, GridDivisions); + if (bOverrideGridLineThickness) TrackGridMID->SetScalarParameterValue(P_GridLineThickness, GridLineThickness); + } + + if (ShimmerMID) + { + if (bOverrideShimmerColour) ShimmerMID->SetVectorParameterValue(P_ShimmerColour, ShimmerColour); + if (bOverrideShimmerWidth) ShimmerMID->SetScalarParameterValue(P_ShimmerWidth, ShimmerWidth); + if (bOverrideShimmerSpeed) ShimmerMID->SetScalarParameterValue(P_ShimmerSpeed, ShimmerSpeed); + if (bOverrideShimmerSoftness) ShimmerMID->SetScalarParameterValue(P_ShimmerSoftness, ShimmerSoftness); + } + if (EdgeMID) + { + if (bOverrideEdgeColour) EdgeMID->SetVectorParameterValue(P_EdgeColour, EdgeColour); + if (bOverrideEdgeWidth) EdgeMID->SetScalarParameterValue(P_EdgeWidth, EdgeWidth); + if (bOverrideEdgeGlowWidth) EdgeMID->SetScalarParameterValue(P_EdgeGlowWidth, EdgeGlowWidth); + if (bOverrideEdgeGlowIntensity) EdgeMID->SetScalarParameterValue(P_EdgeGlowIntensity, EdgeGlowIntensity); + } + + // Visibilidade dos widgets opcionais + if (TrackGridImage) + { + TrackGridImage->SetVisibility(bShowTrackGrid ? ESlateVisibility::HitTestInvisible : ESlateVisibility::Collapsed); + } + UpdateEdgeVisibility(); + if (ShimmerImage) + { + ShimmerImage->SetVisibility(bShowShimmer ? ESlateVisibility::HitTestInvisible : ESlateVisibility::Collapsed); + } +} + +void UUIProgressBar_Base::ApplyBrushStyle() +{ + const float InsetForShimmer = bShowBorder ? FMath::Max(BorderWidth, 0.f) : 0.f; + + auto Apply = [this](UImage* Img, bool bAllowBorder, float RadiusInset) + { + if (!Img) + { + return; + } + FSlateBrush B = Img->GetBrush(); + B.DrawAs = (DrawStyle == EUIProgressBarStyle::Rounded) + ? ESlateBrushDrawType::RoundedBox + : ESlateBrushDrawType::Image; + + FSlateBrushOutlineSettings Outline = B.OutlineSettings; + const float R = FMath::Max(CornerRadius - RadiusInset, 0.f); + Outline.CornerRadii = FVector4(R, R, R, R); + Outline.RoundingType = ESlateBrushRoundingType::FixedRadius; + + if (bAllowBorder && bShowBorder) + { + Outline.Color = FSlateColor(BorderColour); + Outline.Width = FMath::Max(BorderWidth, 0.f); + } + else + { + Outline.Color = FSlateColor(FLinearColor::Transparent); + Outline.Width = 0.f; + } + B.OutlineSettings = Outline; + + Img->SetBrush(B); + }; + + // Borda só no BG. Shimmer ganha inset = BorderWidth (padding no slot + corner reduzido) + // pra não passar por cima do outline do BG. Demais widgets sem inset. + Apply(BackgroundImage, /*bAllowBorder=*/true, 0.f); + Apply(TrackGridImage, /*bAllowBorder=*/false, 0.f); + Apply(FillImage, /*bAllowBorder=*/false, 0.f); + Apply(EdgeImage, /*bAllowBorder=*/false, 0.f); + Apply(ShimmerImage, /*bAllowBorder=*/false, InsetForShimmer); + + if (ShimmerImage) + { + if (UOverlaySlot* OS = Cast(ShimmerImage->Slot)) + { + OS->SetPadding(FMargin(InsetForShimmer)); + } + } +} + +void UUIProgressBar_Base::ApplyPrimaryToMID(float Value) +{ + if (FillMID) + { + FillMID->SetScalarParameterValue(P_PrimaryLevel, Value); + } + // Edge acompanha o nível Primary (posição da linha = PrimaryLevel) + if (EdgeMID) + { + EdgeMID->SetScalarParameterValue(P_PrimaryLevel, Value); + } + UpdateEdgeVisibility(); +} + +void UUIProgressBar_Base::UpdateEdgeVisibility() +{ + if (!EdgeImage) + { + return; + } + if (!bShowEdge) + { + EdgeImage->SetVisibility(ESlateVisibility::Collapsed); + return; + } + // Auto-hide quando Primary está nos extremos (0 ou 1) — Edge encostaria na borda. + const bool bAtExtreme = (CurrentPrimaryLevel <= 0.f) || (CurrentPrimaryLevel >= 1.f); + EdgeImage->SetVisibility(bAtExtreme ? ESlateVisibility::Collapsed : ESlateVisibility::HitTestInvisible); +} + +void UUIProgressBar_Base::ApplySecondaryToMID(float Value) +{ + if (FillMID) + { + FillMID->SetScalarParameterValue(P_SecondaryLevel, Value); + } +} + +// ===================================================================== +// Theme integration (bUseTheme + Variant) +// ===================================================================== + +void UUIProgressBar_Base::RefreshUIStyle() +{ + if (!bUseTheme || Variant.IsNone()) + { + // Fallback: comportamento manual. + ApplyBrushStyle(); + EnsureMIDs(); + ApplyMaterialOverrides(); + return; + } + const FUIStyle ActiveStyle = ResolveProgressBarStyle(this); + const FUIStyleProgressBarVariant* V = ActiveStyle.ProgressBar.Variants.Find(Variant); + if (!V) + { + // Variante não existe no DT — fallback manual. + ApplyBrushStyle(); + EnsureMIDs(); + ApplyMaterialOverrides(); + return; + } + ApplyThemeVariant(*V); +} + +void UUIProgressBar_Base::ApplyThemeVariant(const FUIStyleProgressBarVariant& V) +{ + // Style — copia pros campos do widget e re-aplica brush. + DrawStyle = V.DrawStyle; + CornerRadius = V.CornerRadius; + bShowBorder = V.bShowBorder; + BorderColour = V.BorderColour; + BorderWidth = V.BorderWidth; + + // Toggles de efeito + bShowTrackGrid = V.bShowTrackGrid; + bShowEdge = V.bShowEdge; + bShowShimmer = V.bShowShimmer; + + // Troca materiais nos brushes quando definidos. Reseta MIDs cacheados pra + // que EnsureMIDs() pegue o material novo na próxima chamada. + auto AssignMaterial = [](UImage* Img, UMaterialInterface* Mat) + { + if (!Img || !Mat) return; + FSlateBrush B = Img->GetBrush(); + if (B.GetResourceObject() != Mat) + { + B.SetResourceObject(Mat); + B.DrawAs = ESlateBrushDrawType::Image; // reset; ApplyBrushStyle re-aplica + Img->SetBrush(B); + } + }; + if (V.FillMaterial) { AssignMaterial(FillImage, V.FillMaterial); FillMID = nullptr; } + if (V.BackgroundMaterial) { AssignMaterial(BackgroundImage, V.BackgroundMaterial); BackgroundMID = nullptr; } + if (V.TrackGridMaterial) { AssignMaterial(TrackGridImage, V.TrackGridMaterial); TrackGridMID = nullptr; } + if (V.EdgeMaterial) { AssignMaterial(EdgeImage, V.EdgeMaterial); EdgeMID = nullptr; } + if (V.ShimmerMaterial) { AssignMaterial(ShimmerImage, V.ShimmerMaterial); ShimmerMID = nullptr; } + + ApplyBrushStyle(); + EnsureMIDs(); + // Quando vem do tema, ignora os bOverride* individuais — variante já é a + // fonte da verdade. Cores são definidas pela MI atribuída ao brush. + + // Aplica visibility dos toggles + if (TrackGridImage) TrackGridImage->SetVisibility(bShowTrackGrid ? ESlateVisibility::HitTestInvisible : ESlateVisibility::Collapsed); + if (ShimmerImage) ShimmerImage->SetVisibility(bShowShimmer ? ESlateVisibility::HitTestInvisible : ESlateVisibility::Collapsed); + UpdateEdgeVisibility(); +} + +TArray UUIProgressBar_Base::GetProgressBarVariantOptions() const +{ + return CollectVariantOptions(); +} diff --git a/Source/ZMMO/Game/UI/Widgets/UIProgressBar_Base.h b/Source/ZMMO/Game/UI/Widgets/UIProgressBar_Base.h new file mode 100644 index 0000000..e04dde7 --- /dev/null +++ b/Source/ZMMO/Game/UI/Widgets/UIProgressBar_Base.h @@ -0,0 +1,466 @@ +#pragma once + +#include "CoreMinimal.h" +#include "CommonUserWidget.h" +#include "UI/UIStyleTypes.h" +#include "UIProgressBar_Base.generated.h" + +class UImage; +class UMaterialInstanceDynamic; +struct FUIStyleProgressBarVariant; + +/** + * ProgressBar do ZMMO. Wrappa um UImage (FillImage) + UImage de background + * (BackgroundImage) com material custom. O arredondamento dos cantos usa + * o RoundedBox nativo do Slate (Brush.DrawAs + OutlineSettings.CornerRadii) + * pra ficar pixel-perfect e independente do aspect ratio. + * + * Modelo assimetrico de niveis: SecondaryLevel so aparece visualmente se + * for MAIOR que PrimaryLevel — representa heal preview, damage trail, + * buffer empilhado em cima da barra principal. + */ +UCLASS(Abstract, Blueprintable) +class ZMMO_API UUIProgressBar_Base : public UCommonUserWidget +{ + GENERATED_BODY() + +public: + // === Theme (DT_UI_Styles integration) === + + /** + * true = le toda a config visual (materiais + style + toggles) do + * DT_UI_Styles (FUIStyleProgressBar.Variants[Variant]). Tema + * centralizado, ignora as UPROPERTYs override individuais abaixo. + * false = ignora a DT, usa apenas as UPROPERTYs/toggles do widget. + */ + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "ProgressBar|Theme") + bool bUseTheme = false; + + /** + * Chave da variante (HP/MP/XP/Cast/...). Dropdown popula via + * meta=GetOptions lendo DT_UI_Styles.Default.ProgressBar.Variants. Vazio + * = sem theme aplicado (igual bUseTheme=false). + */ + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "ProgressBar|Theme", + meta = (EditCondition = "bUseTheme", EditConditionHides, + GetOptions = "GetProgressBarVariantOptions")) + FName Variant; + + /** Re-resolve a variante atual e reaplica nos widgets. */ + UFUNCTION(BlueprintCallable, Category = "ProgressBar|Theme") + void RefreshUIStyle(); + + /** Populador do dropdown (meta=GetOptions) das variantes. */ + UFUNCTION() + TArray GetProgressBarVariantOptions() const; + + // === Levels === + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "ProgressBar", + meta = (ClampMin = "0", ClampMax = "1")) + float InitialPrimaryLevel = 1.f; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "ProgressBar", + meta = (ClampMin = "0", ClampMax = "1")) + float InitialSecondaryLevel = 0.f; + + // === Animation === + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "ProgressBar|Animation", + meta = (ClampMin = "0", ClampMax = "5")) + float PrimaryAnimationSeconds = 0.25f; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "ProgressBar|Animation", + meta = (ClampMin = "0", ClampMax = "5")) + float SecondaryAnimationSeconds = 0.8f; + + // === Draw Style === + + /** Box=DrawAs Image (sem corner), Rounded=DrawAs RoundedBox (cantos arredondados). */ + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "ProgressBar|Style") + EUIProgressBarStyle DrawStyle = EUIProgressBarStyle::Box; + + /** + * Raio dos cantos em PIXELS. Aplicado quando DrawStyle = Rounded. + * Se RoundingType = HalfHeightRadius (default do Slate), o Slate clampeia + * automaticamente em metade da altura — ou seja, valor maior que isso vira pill. + */ + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "ProgressBar|Style", + meta = (ClampMin = "0", ClampMax = "100")) + float CornerRadius = 8.f; + + /** Cor do outline em volta do Background (Slate OutlineSettings.Color). */ + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "ProgressBar|Style", + meta = (InlineEditConditionToggle)) + bool bShowBorder = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "ProgressBar|Style", + meta = (EditCondition = "bShowBorder")) + FLinearColor BorderColour = FLinearColor(0.f, 0.f, 0.f, 0.8f); + + /** Espessura do outline em pixels. */ + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "ProgressBar|Style", + meta = (EditCondition = "bShowBorder", ClampMin = "0", ClampMax = "10")) + float BorderWidth = 1.f; + + // === Color overrides (Fill material) === + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "ProgressBar|Colors", + meta = (InlineEditConditionToggle)) + bool bOverridePrimaryColour = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "ProgressBar|Colors", + meta = (EditCondition = "bOverridePrimaryColour")) + FLinearColor PrimaryColour = FLinearColor(0.957f, 0.769f, 0.353f, 1.f); + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "ProgressBar|Colors", + meta = (InlineEditConditionToggle)) + bool bOverridePrimaryColourBottom = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "ProgressBar|Colors", + meta = (EditCondition = "bOverridePrimaryColourBottom")) + FLinearColor PrimaryColourBottom = FLinearColor(0.478f, 0.282f, 0.094f, 1.f); + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "ProgressBar|Colors", + meta = (InlineEditConditionToggle)) + bool bOverrideSecondaryColour = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "ProgressBar|Colors", + meta = (EditCondition = "bOverrideSecondaryColour")) + FLinearColor SecondaryColour = FLinearColor(0.246201f, 0.002428f, 0.002125f, 1.f); + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "ProgressBar|Colors", + meta = (InlineEditConditionToggle)) + bool bOverrideSecondaryColourBottom = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "ProgressBar|Colors", + meta = (EditCondition = "bOverrideSecondaryColourBottom")) + FLinearColor SecondaryColourBottom = FLinearColor(0.123f, 0.001f, 0.001f, 1.f); + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "ProgressBar|Colors", + meta = (InlineEditConditionToggle)) + bool bOverrideEmptyColour = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "ProgressBar|Colors", + meta = (EditCondition = "bOverrideEmptyColour")) + FLinearColor EmptyColour = FLinearColor(0.f, 0.f, 0.f, 0.6f); + + // === Background override === + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "ProgressBar|Background", + meta = (InlineEditConditionToggle)) + bool bOverrideBackgroundColour = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "ProgressBar|Background", + meta = (EditCondition = "bOverrideBackgroundColour")) + FLinearColor BackgroundColour = FLinearColor(0.f, 0.f, 0.f, 0.6f); + + // === Track Grid (efeito de listras verticais sobre o BG) === + + /** Liga/desliga a renderização do TrackGridImage. */ + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "ProgressBar|TrackGrid") + bool bShowTrackGrid = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "ProgressBar|TrackGrid", + meta = (InlineEditConditionToggle)) + bool bOverrideGridColour = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "ProgressBar|TrackGrid", + meta = (EditCondition = "bOverrideGridColour")) + FLinearColor GridColour = FLinearColor(1.f, 1.f, 1.f, 0.05f); + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "ProgressBar|TrackGrid", + meta = (InlineEditConditionToggle)) + bool bOverrideGridDivisions = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "ProgressBar|TrackGrid", + meta = (EditCondition = "bOverrideGridDivisions", ClampMin = "2", ClampMax = "50")) + float GridDivisions = 10.f; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "ProgressBar|TrackGrid", + meta = (InlineEditConditionToggle)) + bool bOverrideGridLineThickness = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "ProgressBar|TrackGrid", + meta = (EditCondition = "bOverrideGridLineThickness", ClampMin = "0.001", ClampMax = "0.2")) + float GridLineThickness = 0.02f; + + // === Edge (linha vertical no fim do Primary) === + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "ProgressBar|Edge") + bool bShowEdge = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "ProgressBar|Edge", + meta = (InlineEditConditionToggle)) + bool bOverrideEdgeColour = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "ProgressBar|Edge", + meta = (EditCondition = "bOverrideEdgeColour")) + FLinearColor EdgeColour = FLinearColor(1.f, 0.94f, 0.78f, 0.85f); + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "ProgressBar|Edge", + meta = (InlineEditConditionToggle)) + bool bOverrideEdgeWidth = false; + + /** Largura da linha em UV (fração da barra). 0.005 ≈ 2px em barra de 400px. */ + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "ProgressBar|Edge", + meta = (EditCondition = "bOverrideEdgeWidth", ClampMin = "0.001", ClampMax = "0.1")) + float EdgeWidth = 0.005f; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "ProgressBar|Edge", + meta = (InlineEditConditionToggle)) + bool bOverrideEdgeGlowWidth = false; + + /** Largura do halo lateral em UV. 0 = sem glow. */ + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "ProgressBar|Edge", + meta = (EditCondition = "bOverrideEdgeGlowWidth", ClampMin = "0", ClampMax = "0.2")) + float EdgeGlowWidth = 0.02f; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "ProgressBar|Edge", + meta = (InlineEditConditionToggle)) + bool bOverrideEdgeGlowIntensity = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "ProgressBar|Edge", + meta = (EditCondition = "bOverrideEdgeGlowIntensity", ClampMin = "0", ClampMax = "1")) + float EdgeGlowIntensity = 0.5f; + + // === Shimmer (faixa de luz animada por cima do Fill) === + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "ProgressBar|Shimmer") + bool bShowShimmer = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "ProgressBar|Shimmer", + meta = (InlineEditConditionToggle)) + bool bOverrideShimmerColour = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "ProgressBar|Shimmer", + meta = (EditCondition = "bOverrideShimmerColour")) + FLinearColor ShimmerColour = FLinearColor(1.f, 0.94f, 0.78f, 0.35f); + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "ProgressBar|Shimmer", + meta = (InlineEditConditionToggle)) + bool bOverrideShimmerWidth = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "ProgressBar|Shimmer", + meta = (EditCondition = "bOverrideShimmerWidth", ClampMin = "0.05", ClampMax = "1.0")) + float ShimmerWidth = 0.2f; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "ProgressBar|Shimmer", + meta = (InlineEditConditionToggle)) + bool bOverrideShimmerSpeed = false; + + /** Ciclos por segundo. 0.4545 = ciclo de 2.2s (default CSS). */ + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "ProgressBar|Shimmer", + meta = (EditCondition = "bOverrideShimmerSpeed", ClampMin = "0.01", ClampMax = "5.0")) + float ShimmerSpeed = 0.4545f; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "ProgressBar|Shimmer", + meta = (InlineEditConditionToggle)) + bool bOverrideShimmerSoftness = false; + + /** Expoente da bell curve. Maior = pico mais fino. */ + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "ProgressBar|Shimmer", + meta = (EditCondition = "bOverrideShimmerSoftness", ClampMin = "0.5", ClampMax = "8.0")) + float ShimmerSoftness = 2.f; + + // === Gradient overrides === + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "ProgressBar|Gradient", + meta = (InlineEditConditionToggle)) + bool bOverrideEnableGradient = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "ProgressBar|Gradient", + meta = (EditCondition = "bOverrideEnableGradient")) + bool bEnableGradient = true; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "ProgressBar|Gradient", + meta = (InlineEditConditionToggle)) + bool bOverrideGradientSplit = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "ProgressBar|Gradient", + meta = (EditCondition = "bOverrideGradientSplit", ClampMin = "0", ClampMax = "1")) + float GradientSplit = 0.5f; + + // === Primary API === + + UFUNCTION(BlueprintCallable, Category = "ProgressBar") + void SetTargetPrimaryLevel(float Primary01); + + UFUNCTION(BlueprintCallable, Category = "ProgressBar") + void SetPrimaryLevelImmediate(float Primary01); + + UFUNCTION(BlueprintPure, Category = "ProgressBar") + float GetCurrentPrimaryLevel() const { return CurrentPrimaryLevel; } + + // === Secondary API === + + UFUNCTION(BlueprintCallable, Category = "ProgressBar") + void SetTargetSecondaryLevel(float Sec01); + + UFUNCTION(BlueprintCallable, Category = "ProgressBar") + void SetSecondaryLevelImmediate(float Sec01); + + UFUNCTION(BlueprintPure, Category = "ProgressBar") + float GetCurrentSecondaryLevel() const { return CurrentSecondaryLevel; } + + // === Style API === + + UFUNCTION(BlueprintCallable, Category = "ProgressBar|Style") + void SetDrawStyle(EUIProgressBarStyle InStyle); + + UFUNCTION(BlueprintCallable, Category = "ProgressBar|Style") + void SetCornerRadius(float RadiusPixels); + + UFUNCTION(BlueprintCallable, Category = "ProgressBar|Style") + void SetBorderVisible(bool bVisible); + + UFUNCTION(BlueprintCallable, Category = "ProgressBar|Style") + void SetBorderColour(FLinearColor Color); + + UFUNCTION(BlueprintCallable, Category = "ProgressBar|Style") + void SetBorderWidth(float WidthPixels); + + // === Color setters (Fill MID) === + + UFUNCTION(BlueprintCallable, Category = "ProgressBar|Colors") + void SetPrimaryColour(FLinearColor Color); + + UFUNCTION(BlueprintCallable, Category = "ProgressBar|Colors") + void SetPrimaryColourBottom(FLinearColor Color); + + UFUNCTION(BlueprintCallable, Category = "ProgressBar|Colors") + void SetSecondaryColour(FLinearColor Color); + + UFUNCTION(BlueprintCallable, Category = "ProgressBar|Colors") + void SetSecondaryColourBottom(FLinearColor Color); + + UFUNCTION(BlueprintCallable, Category = "ProgressBar|Colors") + void SetEmptyColour(FLinearColor Color); + + UFUNCTION(BlueprintCallable, Category = "ProgressBar|Background") + void SetBackgroundColour(FLinearColor Color); + + UFUNCTION(BlueprintCallable, Category = "ProgressBar|TrackGrid") + void SetTrackGridVisible(bool bVisible); + + UFUNCTION(BlueprintCallable, Category = "ProgressBar|TrackGrid") + void SetGridColour(FLinearColor Color); + + UFUNCTION(BlueprintCallable, Category = "ProgressBar|TrackGrid") + void SetGridDivisions(float Divisions); + + UFUNCTION(BlueprintCallable, Category = "ProgressBar|TrackGrid") + void SetGridLineThickness(float Thickness); + + UFUNCTION(BlueprintCallable, Category = "ProgressBar|Edge") + void SetEdgeVisible(bool bVisible); + + UFUNCTION(BlueprintCallable, Category = "ProgressBar|Edge") + void SetEdgeColour(FLinearColor Color); + + UFUNCTION(BlueprintCallable, Category = "ProgressBar|Edge") + void SetEdgeWidth(float Width01); + + UFUNCTION(BlueprintCallable, Category = "ProgressBar|Edge") + void SetEdgeGlowWidth(float GlowWidth01); + + UFUNCTION(BlueprintCallable, Category = "ProgressBar|Edge") + void SetEdgeGlowIntensity(float Intensity01); + + UFUNCTION(BlueprintCallable, Category = "ProgressBar|Shimmer") + void SetShimmerVisible(bool bVisible); + + UFUNCTION(BlueprintCallable, Category = "ProgressBar|Shimmer") + void SetShimmerColour(FLinearColor Color); + + UFUNCTION(BlueprintCallable, Category = "ProgressBar|Shimmer") + void SetShimmerWidth(float Width01); + + UFUNCTION(BlueprintCallable, Category = "ProgressBar|Shimmer") + void SetShimmerSpeed(float Speed); + + UFUNCTION(BlueprintCallable, Category = "ProgressBar|Shimmer") + void SetShimmerSoftness(float Softness); + + UFUNCTION(BlueprintCallable, Category = "ProgressBar|Gradient") + void SetGradientEnabled(bool bEnabled); + + UFUNCTION(BlueprintCallable, Category = "ProgressBar|Gradient") + void SetGradientSplit(float Split01); + + // === Wrappers semânticos === + + UFUNCTION(BlueprintCallable, Category = "ProgressBar|Effects") + void ShowHealPreview(float TargetPct, FLinearColor PreviewColor); + + UFUNCTION(BlueprintCallable, Category = "ProgressBar|Effects") + void ShowDamageLag(float OldPct, float NewPct, FLinearColor LagColor); + + UFUNCTION(BlueprintCallable, Category = "ProgressBar|Effects") + void ShowBuffer(float BufferPct, FLinearColor BufferColor); + + UFUNCTION(BlueprintCallable, Category = "ProgressBar|Effects") + void ClearSecondary(); + +protected: + virtual void NativePreConstruct() override; + virtual void NativeConstruct() override; + virtual void NativeDestruct() override; + virtual void NativeTick(const FGeometry& MyGeometry, float InDeltaTime) override; + + UPROPERTY(meta = (BindWidget)) + TObjectPtr FillImage; + + UPROPERTY(meta = (BindWidgetOptional)) + TObjectPtr BackgroundImage; + + UPROPERTY(meta = (BindWidgetOptional)) + TObjectPtr TrackGridImage; + + UPROPERTY(meta = (BindWidgetOptional)) + TObjectPtr EdgeImage; + + UPROPERTY(meta = (BindWidgetOptional)) + TObjectPtr ShimmerImage; + +private: + UPROPERTY(Transient) + TObjectPtr FillMID; + + UPROPERTY(Transient) + TObjectPtr BackgroundMID; + + UPROPERTY(Transient) + TObjectPtr TrackGridMID; + + UPROPERTY(Transient) + TObjectPtr EdgeMID; + + UPROPERTY(Transient) + TObjectPtr ShimmerMID; + + void EnsureMIDs(); + void ApplyPrimaryToMID(float Value); + void ApplySecondaryToMID(float Value); + void ApplyMaterialOverrides(); + void ApplyBrushStyle(); + void UpdateEdgeVisibility(); + void ApplyThemeVariant(const FUIStyleProgressBarVariant& Variant); + + UFUNCTION() + void HandleThemeChanged(FName NewThemeId); + + bool bThemeBound = false; + + float CurrentPrimaryLevel = 1.f; + float TargetPrimaryLevel = 1.f; + float PrimaryStart = 1.f; + float PrimaryElapsed = 0.f; + bool bAnimatingPrimary = false; + + float CurrentSecondaryLevel = 0.f; + float TargetSecondaryLevel = 0.f; + float SecondaryStart = 0.f; + float SecondaryElapsed = 0.f; + bool bAnimatingSecondary = false; +}; diff --git a/Source/ZMMOAttributes/Private/ZMMOAttributeNetworkHandler.cpp b/Source/ZMMOAttributes/Private/ZMMOAttributeNetworkHandler.cpp index b9c702b..e2c1e5e 100644 --- a/Source/ZMMOAttributes/Private/ZMMOAttributeNetworkHandler.cpp +++ b/Source/ZMMOAttributes/Private/ZMMOAttributeNetworkHandler.cpp @@ -19,9 +19,11 @@ namespace S.EntityId = P.EntityId; S.ClassId = P.ClassId; S.BaseLevel = P.BaseLevel; - S.BaseExp = P.BaseExp; + S.BaseExp = P.BaseExp; + S.BaseExpToNext = P.BaseExpToNext; S.JobLevel = P.JobLevel; - S.JobExp = P.JobExp; + S.JobExp = P.JobExp; + S.JobExpToNext = P.JobExpToNext; S.Str = P.Str; S.Agi = P.Agi; S.Vit = P.Vit; @@ -61,7 +63,7 @@ namespace // // Cada UZMMOAttributeComponent carrega seu proprio EntityId (seed em // AZMMOPlayerCharacter::HandleLocalSpawnReady ou no primeiro snapshot). - UZMMOAttributeComponent* FindComponentForEntity(UWorld* World, int32 EntityId) + UZMMOAttributeComponent* FindComponentForEntity(UWorld* World, int64 EntityId) // PR-HANDOFF-007 int64 { if (!World || EntityId == 0) { return nullptr; } const AGameStateBase* GS = World->GetGameState(); @@ -174,7 +176,7 @@ void UZMMOAttributeNetworkHandler::HandleAttributeSnapshotFull(const FZeusAttrib } } -void UZMMOAttributeNetworkHandler::HandleHpSpUpdate(int32 EntityId, int32 Hp, int32 Sp) +void UZMMOAttributeNetworkHandler::HandleHpSpUpdate(int64 EntityId, int32 Hp, int32 Sp) { UWorld* World = GetWorld(); if (!World) { return; } @@ -185,7 +187,7 @@ void UZMMOAttributeNetworkHandler::HandleHpSpUpdate(int32 EntityId, int32 Hp, in } } -void UZMMOAttributeNetworkHandler::HandleLevelUp(int32 EntityId, int32 NewBaseLevel, int32 StatusPointDelta) +void UZMMOAttributeNetworkHandler::HandleLevelUp(int64 EntityId, int32 NewBaseLevel, int32 StatusPointDelta) { UWorld* World = GetWorld(); if (!World) { return; } diff --git a/Source/ZMMOAttributes/Public/ZMMOAttributeComponent.h b/Source/ZMMOAttributes/Public/ZMMOAttributeComponent.h index 22522ed..6d11055 100644 --- a/Source/ZMMOAttributes/Public/ZMMOAttributeComponent.h +++ b/Source/ZMMOAttributes/Public/ZMMOAttributeComponent.h @@ -64,7 +64,7 @@ public: /// chegar, garantindo que o NetworkHandler consiga rotear via lookup /// por EntityId desde o inicio. UFUNCTION(BlueprintCallable, Category = "Zeus|Attributes") - void SeedEntityId(int32 InEntityId) { Current.EntityId = InEntityId; } + void SeedEntityId(int64 InEntityId) { Current.EntityId = InEntityId; } UFUNCTION(BlueprintPure, Category = "Zeus|Attributes") const FZMMOAttributesSnapshot& GetSnapshot() const { return Current; } diff --git a/Source/ZMMOAttributes/Public/ZMMOAttributeNetworkHandler.h b/Source/ZMMOAttributes/Public/ZMMOAttributeNetworkHandler.h index 0f47348..dbc81e3 100644 --- a/Source/ZMMOAttributes/Public/ZMMOAttributeNetworkHandler.h +++ b/Source/ZMMOAttributes/Public/ZMMOAttributeNetworkHandler.h @@ -34,8 +34,8 @@ public: private: void HandleAttributeSnapshotFull(const FZeusAttributesPayload& Payload); - void HandleHpSpUpdate(int32 EntityId, int32 Hp, int32 Sp); - void HandleLevelUp(int32 EntityId, int32 NewBaseLevel, int32 StatusPointDelta); + void HandleHpSpUpdate(int64 EntityId, int32 Hp, int32 Sp); + void HandleLevelUp(int64 EntityId, int32 NewBaseLevel, int32 StatusPointDelta); void HandleStatAllocReply(bool bAccepted, int32 Reason); UZeusNetworkSubsystem* GetZeusNetSubsystem() const; diff --git a/Source/ZMMOAttributes/Public/ZMMOAttributeTypes.h b/Source/ZMMOAttributes/Public/ZMMOAttributeTypes.h index aca138a..e19e322 100644 --- a/Source/ZMMOAttributes/Public/ZMMOAttributeTypes.h +++ b/Source/ZMMOAttributes/Public/ZMMOAttributeTypes.h @@ -20,14 +20,19 @@ struct ZMMOATTRIBUTES_API FZMMOAttributesSnapshot GENERATED_BODY() // === Identidade + classe === - UPROPERTY(BlueprintReadOnly, Category = "Zeus|Attributes") int32 EntityId = 0; + UPROPERTY(BlueprintReadOnly, Category = "Zeus|Attributes") int64 EntityId = 0; UPROPERTY(BlueprintReadOnly, Category = "Zeus|Attributes") int32 ClassId = 0; // === Progressao === UPROPERTY(BlueprintReadOnly, Category = "Zeus|Attributes") int32 BaseLevel = 1; UPROPERTY(BlueprintReadOnly, Category = "Zeus|Attributes") int64 BaseExp = 0; + /** EXP pra subir o proximo base level (server resolve via JobsDatabase). + * 0 = ja' em cap -> barra cheia. % = BaseExp / BaseExpToNext. */ + UPROPERTY(BlueprintReadOnly, Category = "Zeus|Attributes") int64 BaseExpToNext = 0; UPROPERTY(BlueprintReadOnly, Category = "Zeus|Attributes") int32 JobLevel = 1; UPROPERTY(BlueprintReadOnly, Category = "Zeus|Attributes") int64 JobExp = 0; + /** EXP pra subir o proximo job level. 0 = ja' em cap -> barra cheia. */ + UPROPERTY(BlueprintReadOnly, Category = "Zeus|Attributes") int64 JobExpToNext = 0; // === Stats primarios (Camada 1 base; efetivos vao em derivados) === UPROPERTY(BlueprintReadOnly, Category = "Zeus|Attributes|Stats") int32 Str = 1; diff --git a/ZMMO.uproject b/ZMMO.uproject index a06e717..4f68b16 100644 --- a/ZMMO.uproject +++ b/ZMMO.uproject @@ -52,6 +52,13 @@ "Editor" ] }, + { + "Name": "ZeusAdminTools", + "Enabled": true, + "TargetAllowList": [ + "Editor" + ] + }, { "Name": "ZeusUMGForge", "Enabled": true, @@ -70,6 +77,10 @@ { "Name": "CommonUI", "Enabled": true + }, + { + "Name": "NwiroIntegrationKit", + "Enabled": false } ], "AdditionalPluginDirectories": [