Compare commits
36 Commits
acaa8eb83c
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| 63d845c910 | |||
| 6b19fe2d81 | |||
| 8a29b6eeb7 | |||
| baeb4098c9 | |||
| 57363d9e7f | |||
| c4f185736f | |||
| 7f3d9cd989 | |||
| ee12f1ad32 | |||
| 2ae207533c | |||
| 6ca0f21163 | |||
| 88d01f588b | |||
| 3499cf363c | |||
| cd7444f4d2 | |||
| 8df5fc29e9 | |||
| 662184001d | |||
| 163092858c | |||
| 4d2186c43c | |||
| d9faa65a27 | |||
| 21865dcc09 | |||
| 960280c9a7 | |||
| 4677d04b74 | |||
| e9d818e999 | |||
| 7a04253843 | |||
| a52a960347 | |||
| a2a2e16c93 | |||
| 2ed2e91381 | |||
| 08e998324c | |||
| 08182fcc15 | |||
| f6fda856bf | |||
| f8169a63e9 | |||
| 349d603775 | |||
| 408a764560 | |||
| ddb5f599f9 | |||
| d392c103ca | |||
| 281db9a375 | |||
| 794fcbb8a9 |
7
.gitignore
vendored
7
.gitignore
vendored
@@ -29,6 +29,13 @@ Content/*
|
|||||||
*.sublime-workspace
|
*.sublime-workspace
|
||||||
*.code-workspace
|
*.code-workspace
|
||||||
|
|
||||||
|
# Local MCP bridge plugin
|
||||||
|
Plugins/UEBridgeMCP-main/
|
||||||
|
Plugins/UE_MCP_Bridge/
|
||||||
|
.claude/
|
||||||
|
.ue-mcp.json
|
||||||
|
ue-mcp.yml
|
||||||
|
|
||||||
# OS files
|
# OS files
|
||||||
Thumbs.db
|
Thumbs.db
|
||||||
Desktop.ini
|
Desktop.ini
|
||||||
|
|||||||
15
AGENTS.md
15
AGENTS.md
@@ -8,7 +8,7 @@
|
|||||||
- Marketplace 插件源码只允许读取和适配,不直接修改 Engine 下的插件文件。
|
- Marketplace 插件源码只允许读取和适配,不直接修改 Engine 下的插件文件。
|
||||||
- Git 远程仓库固定为 `https://git.codable.cn/cit110/PHY.git`。
|
- Git 远程仓库固定为 `https://git.codable.cn/cit110/PHY.git`。
|
||||||
- `Content` 目录只管理 `Content/AGame` 及其子文件;`Content/Collections`、`Content/Developers` 等其他目录默认不纳入版本控制。
|
- `Content` 目录只管理 `Content/AGame` 及其子文件;`Content/Collections`、`Content/Developers` 等其他目录默认不纳入版本控制。
|
||||||
- 每次完成修改并通过对应验证后,必须提交并推送到 `origin`;如果远程认证或冲突阻塞推送,必须在最终汇报中说明。
|
- 每次完成修改并通过对应验证后,只提交到本地 Git;默认不推送到 `origin`,除非用户在当前任务中明确要求推送。
|
||||||
|
|
||||||
## 插件源码位置
|
## 插件源码位置
|
||||||
- Generic Combat System:`D:\ue\UE_5.7\Engine\Plugins\Marketplace\GenericCd51b9e2c8181V4`
|
- Generic Combat System:`D:\ue\UE_5.7\Engine\Plugins\Marketplace\GenericCd51b9e2c8181V4`
|
||||||
@@ -27,7 +27,7 @@
|
|||||||
- 相机专家向总架构汇报。主责 `AuroraDevs_UGC`、战斗锁定/瞄准/移动相机状态;不得主动引入 `GenericCameraSystem` 或 `SLSCameraModeSystem` 作为玩法层依赖。
|
- 相机专家向总架构汇报。主责 `AuroraDevs_UGC`、战斗锁定/瞄准/移动相机状态;不得主动引入 `GenericCameraSystem` 或 `SLSCameraModeSystem` 作为玩法层依赖。
|
||||||
- 战斗/GAS 专家向总架构汇报。主责 ASC、Ability、Attribute、GameplayTag、GameplayCue、伤害、复制、预测。
|
- 战斗/GAS 专家向总架构汇报。主责 ASC、Ability、Attribute、GameplayTag、GameplayCue、伤害、复制、预测。
|
||||||
- 输入专家向总架构汇报。主责 `GenericInputSystem`、`EnhancedInput`、输入到 GAS/UGC/运动/UI 的路由。
|
- 输入专家向总架构汇报。主责 `GenericInputSystem`、`EnhancedInput`、输入到 GAS/UGC/运动/UI 的路由。
|
||||||
- 运动专家向总架构汇报。主责 `SmoothLocomotionSystem`、`SLSCore`、`TraversalSystem`、`SLSIntegration`、`FootStepSystem`;运动状态只通过项目接口通知相机。
|
- 运动专家向总架构汇报。主责 `SmoothLocomotionSystem`、`SLSCore`、`TraversalSystem`、`SLSIntegration`、`FootStepSystem`;`USLSIntegrationComponent` 允许由运动专家集中接入,禁止其它系统散落依赖;运动状态只通过项目接口通知相机。
|
||||||
- UI 专家向总架构汇报。主责 `GenericUISystem`、`CommonUI`、UMG、HUD、输入模式切换。
|
- UI 专家向总架构汇报。主责 `GenericUISystem`、`CommonUI`、UMG、HUD、输入模式切换。
|
||||||
- 背包专家向总架构汇报。主责 `GenericInventorySystem`、物品、装备、拾取、战斗/UI 联动。
|
- 背包专家向总架构汇报。主责 `GenericInventorySystem`、物品、装备、拾取、战斗/UI 联动。
|
||||||
- 交互/效果专家向总架构汇报。主责 `GenericGameSystem`、`GenericEffectsSystem`、`SmartObjects`、`TargetingSystem`、Niagara。
|
- 交互/效果专家向总架构汇报。主责 `GenericGameSystem`、`GenericEffectsSystem`、`SmartObjects`、`TargetingSystem`、Niagara。
|
||||||
@@ -42,12 +42,21 @@
|
|||||||
- 专家交付必须包含:`改动范围`、`新增/修改接口`、`配置文件`、`蓝图/DataAsset 使用原因`、`验证结果`、`遗留风险`。
|
- 专家交付必须包含:`改动范围`、`新增/修改接口`、`配置文件`、`蓝图/DataAsset 使用原因`、`验证结果`、`遗留风险`。
|
||||||
- 验证 Agent 通过构建和必要测试后,Git/仓库 Agent 才能提交;总架构最终向用户汇报结果。
|
- 验证 Agent 通过构建和必要测试后,Git/仓库 Agent 才能提交;总架构最终向用户汇报结果。
|
||||||
- Git/仓库 Agent 初始化仓库时必须设置 `origin=https://git.codable.cn/cit110/PHY.git`,启用 Git LFS,并确认 `Content` 跟踪范围仅限 `Content/AGame/**`。
|
- Git/仓库 Agent 初始化仓库时必须设置 `origin=https://git.codable.cn/cit110/PHY.git`,启用 Git LFS,并确认 `Content` 跟踪范围仅限 `Content/AGame/**`。
|
||||||
- Git/仓库 Agent 在每次修改完成后负责提交并推送当前改动,不保留已完成但未提交的工作树。
|
- Git/仓库 Agent 在每次修改完成后负责提交当前改动,不保留已完成但未提交的工作树;默认不推送,除非用户在当前任务中明确要求。
|
||||||
- 相机/输入/运动/战斗之间的接口冲突、是否使用蓝图/DataAsset、是否引入插件模块依赖,统一由总架构裁决。
|
- 相机/输入/运动/战斗之间的接口冲突、是否使用蓝图/DataAsset、是否引入插件模块依赖,统一由总架构裁决。
|
||||||
|
|
||||||
|
## UE-MCP 使用约定
|
||||||
|
- 本项目已配置 Codex MCP server:`ue_mcp`。
|
||||||
|
- 需要操作 Unreal Editor 时,先调用 `project(action="get_status")`,确认 `editorConnected=true` 且项目为 `D:\ue\Projects\PHY\PHY.uproject`。
|
||||||
|
- UE Editor 侧桥接端口为 `ws://localhost:9877`,由项目插件 `UE_MCP_Bridge` 提供。
|
||||||
|
- 如果 MCP 工具未暴露,检查 `C:\Users\cit11\.codex\config.toml` 中 `[mcp_servers.ue_mcp]` 是否存在并启用,然后重启 Codex。
|
||||||
|
- 使用 MCP 修改资产、蓝图或关卡前必须先读取现状;C++ 和配置文件仍按项目优先级优先于蓝图/DataAsset。
|
||||||
|
- MCP 写操作完成后仍需按本文件的验证和提交规则收尾;默认不推送,除非用户在当前任务中明确要求。
|
||||||
|
|
||||||
## 相机规则
|
## 相机规则
|
||||||
- 项目相机主入口统一使用 Ultimate Gameplay Camera,即 `AuroraDevs_UGC`。
|
- 项目相机主入口统一使用 Ultimate Gameplay Camera,即 `AuroraDevs_UGC`。
|
||||||
- `GenericCameraSystem` 和 `SLSCameraModeSystem` 只允许作为插件内部依赖、示例资产参考或薄适配层依赖;项目玩法层尽可能不用。
|
- `GenericCameraSystem` 和 `SLSCameraModeSystem` 只允许作为插件内部依赖、示例资产参考或薄适配层依赖;项目玩法层尽可能不用。
|
||||||
|
- `USLSIntegrationComponent` 由运动层集中持有和暴露,不等于允许业务层直接依赖 `SLSCameraModeSystem`。
|
||||||
- 相机状态由项目级接口接收输入、运动和战斗状态,不允许业务代码到处直接耦合多个相机系统。
|
- 相机状态由项目级接口接收输入、运动和战斗状态,不允许业务代码到处直接耦合多个相机系统。
|
||||||
|
|
||||||
## C++ 和配置规则
|
## C++ 和配置规则
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -159,3 +159,15 @@ ManualIPAddress=
|
|||||||
+CollisionChannelRedirects=(OldName="VehicleMovement",NewName="Vehicle")
|
+CollisionChannelRedirects=(OldName="VehicleMovement",NewName="Vehicle")
|
||||||
+CollisionChannelRedirects=(OldName="PawnMovement",NewName="Pawn")
|
+CollisionChannelRedirects=(OldName="PawnMovement",NewName="Pawn")
|
||||||
|
|
||||||
|
[/Script/GameplayDebugger.GameplayDebuggerConfig]
|
||||||
|
CategorySlot0=F10
|
||||||
|
CategorySlot1=F1
|
||||||
|
CategorySlot2=F2
|
||||||
|
CategorySlot3=F3
|
||||||
|
CategorySlot4=F4
|
||||||
|
CategorySlot5=F5
|
||||||
|
CategorySlot6=F6
|
||||||
|
CategorySlot7=F7
|
||||||
|
CategorySlot8=F8
|
||||||
|
CategorySlot9=F9
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,14 @@
|
|||||||
[/Script/CommonUI.CommonUISettings]
|
[/Script/CommonUI.CommonUISettings]
|
||||||
CommonButtonAcceptKeyHandling=TriggerClick
|
CommonButtonAcceptKeyHandling=TriggerClick
|
||||||
|
|
||||||
|
[/Script/GenericUISystem.GUIS_GenericUISystemSettings]
|
||||||
|
GameUIPolicyClass="/Script/PHY.PHYGameUIPolicy"
|
||||||
|
|
||||||
[/Script/EngineSettings.GeneralProjectSettings]
|
[/Script/EngineSettings.GeneralProjectSettings]
|
||||||
ProjectID=E7C26E1F4D195F0DBE49C2A3E5017988
|
ProjectID=E7C26E1F4D195F0DBE49C2A3E5017988
|
||||||
CopyrightNotice=
|
CopyrightNotice=
|
||||||
|
|
||||||
|
[/Script/EngineSettings.GameMapsSettings]
|
||||||
|
GlobalDefaultGameMode=/Game/AGame/Gameplay/Framework/BP_PHYGameMode.BP_PHYGameMode_C
|
||||||
|
GlobalDefaultServerGameMode=/Game/AGame/Gameplay/Framework/BP_PHYGameMode.BP_PHYGameMode_C
|
||||||
|
|
||||||
|
|||||||
9
Config/DefaultPHYAnimation.ini
Normal file
9
Config/DefaultPHYAnimation.ini
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
[/Script/PHY.PHYAnimationSettings]
|
||||||
|
DefaultSourceLocomotionMesh="/Game/AGame/Character/SKM_Manny_Invis.SKM_Manny_Invis"
|
||||||
|
DefaultSourceAnimClass="/Game/AGame/Animation/Locomotion/ABP_UE5_Main.ABP_UE5_Main_C"
|
||||||
|
DefaultDisplayAnimClass="/Game/AGame/Animation/Retargeters/ABP_Retargeter.ABP_Retargeter_C"
|
||||||
|
DefaultIKRetargeter="/Game/AGame/Animation/Retargeters/RTG_Manny_To_Ida.RTG_Manny_To_Ida"
|
||||||
|
bHideSourceMeshInGame=False
|
||||||
|
bPreferLeaderPoseForSameSkeleton=True
|
||||||
|
bAllowRuntimeVisualMeshSwitch=True
|
||||||
|
bApplyPlayerClassMeshToAI=False
|
||||||
@@ -4,4 +4,12 @@ bEnableContextEffects=True
|
|||||||
DefaultMaxWalkSpeed=500.000000
|
DefaultMaxWalkSpeed=500.000000
|
||||||
DefaultSprintSpeed=650.000000
|
DefaultSprintSpeed=650.000000
|
||||||
DefaultMaxAcceleration=2048.000000
|
DefaultMaxAcceleration=2048.000000
|
||||||
|
DefaultMeshRelativeLocation=(X=0.000000,Y=0.000000,Z=-90.000000)
|
||||||
|
DefaultMeshRelativeRotation=(Pitch=0.000000,Yaw=-90.000000,Roll=0.000000)
|
||||||
DefaultInteractionOption=0
|
DefaultInteractionOption=0
|
||||||
|
DefaultPlayerCharacterClass="/Game/AGame/Character/Player/BP_PlayerBase.BP_PlayerBase_C"
|
||||||
|
DefaultAICharacterClass="/Game/AGame/Character/AI/BP_AIBase.BP_AIBase_C"
|
||||||
|
DefaultPlayerControllerClass="/Game/AGame/Gameplay/Player/BP_PHYPlayerController.BP_PHYPlayerController_C"
|
||||||
|
DefaultAIControllerClass="/Game/AGame/Gameplay/AI/BP_PHYAIController.BP_PHYAIController_C"
|
||||||
|
DefaultPlayerCameraManagerClass="/Game/AGame/Gameplay/Camera/BP_CameraManager_Default.BP_CameraManager_Default_C"
|
||||||
|
DefaultUGCCameraDataAsset="/Game/AGame/Gameplay/Camera/DA_Camera_Default.DA_Camera_Default"
|
||||||
|
|||||||
21
Config/DefaultPHYClass.ini
Normal file
21
Config/DefaultPHYClass.ini
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
[/Script/PHY.PHYClassSettings]
|
||||||
|
DefaultPlayerClassTag=(TagName="Class.Saber")
|
||||||
|
DefaultAIClassTag=(TagName="Class.Saber")
|
||||||
|
bApplyPlayerClassMeshToPlayers=True
|
||||||
|
bApplyPlayerClassMeshToAI=False
|
||||||
|
!PlayerClassMeshes=ClearArray
|
||||||
|
+PlayerClassMeshes=(ClassTag=(TagName="Class.Saber"),PlayerMesh="/Game/AGame/Character/Ida/SK_MMO_ADVANCED_F_01_AGame.SK_MMO_ADVANCED_F_01_AGame",PlayerDisplayAnimClass="/Game/AGame/Animation/Retargeters/ABP_Retargeter.ABP_Retargeter_C",PlayerRetargeter="/Game/AGame/Animation/Retargeters/RTG_Manny_To_Ida.RTG_Manny_To_Ida")
|
||||||
|
+PlayerClassMeshes=(ClassTag=(TagName="Class.Lancer"),PlayerMesh=None,PlayerRetargeter=)
|
||||||
|
+PlayerClassMeshes=(ClassTag=(TagName="Class.Archer"),PlayerMesh=None,PlayerRetargeter=)
|
||||||
|
+PlayerClassMeshes=(ClassTag=(TagName="Class.Rider"),PlayerMesh=None,PlayerRetargeter=)
|
||||||
|
+PlayerClassMeshes=(ClassTag=(TagName="Class.Caster"),PlayerMesh=None,PlayerRetargeter=)
|
||||||
|
+PlayerClassMeshes=(ClassTag=(TagName="Class.Assassin"),PlayerMesh=None,PlayerRetargeter=)
|
||||||
|
+PlayerClassMeshes=(ClassTag=(TagName="Class.Berserker"),PlayerMesh=None,PlayerRetargeter=)
|
||||||
|
!CoreAttributeAllocations=ClearArray
|
||||||
|
+CoreAttributeAllocations=(ClassTag=(TagName="Class.Saber"),Strength=12,Dexterity=10,Vitality=12,Intelligence=8,Spirit=8,Perception=10)
|
||||||
|
+CoreAttributeAllocations=(ClassTag=(TagName="Class.Lancer"),Strength=10,Dexterity=13,Vitality=10,Intelligence=7,Spirit=8,Perception=12)
|
||||||
|
+CoreAttributeAllocations=(ClassTag=(TagName="Class.Archer"),Strength=8,Dexterity=12,Vitality=8,Intelligence=8,Spirit=10,Perception=14)
|
||||||
|
+CoreAttributeAllocations=(ClassTag=(TagName="Class.Rider"),Strength=10,Dexterity=11,Vitality=11,Intelligence=8,Spirit=10,Perception=10)
|
||||||
|
+CoreAttributeAllocations=(ClassTag=(TagName="Class.Caster"),Strength=5,Dexterity=7,Vitality=8,Intelligence=16,Spirit=15,Perception=9)
|
||||||
|
+CoreAttributeAllocations=(ClassTag=(TagName="Class.Assassin"),Strength=7,Dexterity=15,Vitality=7,Intelligence=8,Spirit=8,Perception=15)
|
||||||
|
+CoreAttributeAllocations=(ClassTag=(TagName="Class.Berserker"),Strength=16,Dexterity=8,Vitality=15,Intelligence=4,Spirit=5,Perception=12)
|
||||||
@@ -2,3 +2,8 @@
|
|||||||
bRequireServerAuthority=True
|
bRequireServerAuthority=True
|
||||||
bEnableClientPrediction=True
|
bEnableClientPrediction=True
|
||||||
DefaultHitConfirmWindow=0.200000
|
DefaultHitConfirmWindow=0.200000
|
||||||
|
|
||||||
|
[/Script/PHY.PHYAttributeSettings]
|
||||||
|
DefaultCoreAttributes=(Strength=10.000000,Dexterity=10.000000,Vitality=10.000000,Intelligence=10.000000,Spirit=10.000000,Perception=10.000000)
|
||||||
|
CombatFormula=(MaxHealthBase=100.000000,MaxHealthVitality=18.000000,MaxHealthStrength=3.000000,MaxManaBase=60.000000,MaxManaIntelligence=12.000000,MaxManaSpirit=6.000000,MaxStaminaBase=80.000000,MaxStaminaVitality=5.000000,MaxStaminaDexterity=4.000000,PhysicalAttackPowerStrength=2.000000,PhysicalAttackPowerDexterity=0.500000,SpellPowerIntelligence=2.000000,SpellPowerSpirit=0.400000,ArmorVitality=1.200000,ArmorStrength=0.400000,MagicResistanceSpirit=1.100000,MagicResistanceIntelligence=0.300000,AccuracyBase=0.800000,AccuracyPerception=0.006000,AccuracyDexterity=0.002000,EvasionBase=0.030000,EvasionDexterity=0.003000,EvasionPerception=0.001000,CriticalChanceBase=0.050000,CriticalChanceDexterity=0.002000,CriticalChancePerception=0.001500,CriticalDamageBase=1.500000,CriticalDamageStrength=0.004000,CriticalDamagePerception=0.003000,AttackSpeedBase=1.000000,AttackSpeedDexterity=0.010000,CooldownReductionBase=0.000000,CooldownReductionIntelligence=0.001500,CooldownReductionSpirit=0.001000,BlockPowerBase=5.000000,BlockPowerStrength=1.200000,BlockPowerVitality=0.600000,GuardBreakPowerBase=5.000000,GuardBreakPowerStrength=1.100000,GuardBreakPowerPerception=0.400000,PoiseBase=20.000000,PoiseVitality=1.500000,PoiseStrength=0.500000,PoiseDamageBase=5.000000,PoiseDamageStrength=0.800000,PoiseDamageDexterity=0.200000)
|
||||||
|
ElementFormula=(DamageBonusBase=0.000000,DamageBonusIntelligence=0.001500,DamageBonusPerception=0.000500,ResistanceBase=0.020000,ResistanceSpirit=0.002000,ResistanceVitality=0.000500,PenetrationBase=0.000000,PenetrationPerception=0.001000,PenetrationIntelligence=0.000500)
|
||||||
|
|||||||
@@ -3,3 +3,13 @@ ConfigVersion=1
|
|||||||
bMultiplayerFirst=True
|
bMultiplayerFirst=True
|
||||||
bPreferCodeAndConfig=True
|
bPreferCodeAndConfig=True
|
||||||
TargetPlatformName=Win64
|
TargetPlatformName=Win64
|
||||||
|
|
||||||
|
[/Script/PHY.PHYGameFrameworkSettings]
|
||||||
|
DefaultPawnClass="/Game/AGame/Character/Player/BP_PlayerBase.BP_PlayerBase_C"
|
||||||
|
PlayerControllerClass="/Game/AGame/Gameplay/Player/BP_PHYPlayerController.BP_PHYPlayerController_C"
|
||||||
|
PlayerStateClass="/Game/AGame/Gameplay/Player/BP_PHYPlayerState.BP_PHYPlayerState_C"
|
||||||
|
GameStateClass="/Game/AGame/Gameplay/Framework/BP_PHYGameState.BP_PHYGameState_C"
|
||||||
|
HUDClass="/Game/AGame/Gameplay/Framework/BP_PHYHUD.BP_PHYHUD_C"
|
||||||
|
SpectatorClass="/Game/AGame/Gameplay/Framework/BP_PHYSpectatorPawn.BP_PHYSpectatorPawn_C"
|
||||||
|
bUseSeamlessTravel=True
|
||||||
|
bStartPlayersAsSpectators=False
|
||||||
|
|||||||
@@ -2,3 +2,6 @@
|
|||||||
DefaultMappingPriority=0
|
DefaultMappingPriority=0
|
||||||
bRouteInputThroughGenericInputSystem=True
|
bRouteInputThroughGenericInputSystem=True
|
||||||
bBlockGameplayInputWhenUIFocused=True
|
bBlockGameplayInputWhenUIFocused=True
|
||||||
|
DefaultMoveInputProcessorClass="/Script/PHY.PHYInputProcessor_Move"
|
||||||
|
DefaultLookInputProcessorClass="/Script/PHY.PHYInputProcessor_Look"
|
||||||
|
DefaultAttributeMenuInputProcessorClass="/Script/PHY.PHYInputProcessor_OpenAttributeMenu"
|
||||||
|
|||||||
@@ -1,4 +1,12 @@
|
|||||||
[/Script/PHY.PHYUISettings]
|
[/Script/PHY.PHYUISettings]
|
||||||
bUseCommonUI=True
|
bUseCommonUI=True
|
||||||
|
bUseGenericUISystem=True
|
||||||
bOpenMenusWithGameplayPause=False
|
bOpenMenusWithGameplayPause=False
|
||||||
|
RootLayoutClass="/Script/PHY.PHYGameUILayout"
|
||||||
|
DefaultHUDWidgetClass="/Script/PHY.PHYHUDStatusWidget"
|
||||||
|
AttributeMenuWidgetClass="/Script/PHY.PHYAttributeMenuWidget"
|
||||||
|
GameplayLayerTag=(TagName="UI.Layer.Game")
|
||||||
|
HUDLayerTag=(TagName="UI.Layer.HUD")
|
||||||
|
MenuLayerTag=(TagName="UI.Layer.Menu")
|
||||||
|
ModalLayerTag=(TagName="UI.Layer.Modal")
|
||||||
DefaultHUDLayerName=HUD
|
DefaultHUDLayerName=HUD
|
||||||
|
|||||||
BIN
Content/AGame/Animation/DA_Ida_IntegrationData.uasset
LFS
Normal file
BIN
Content/AGame/Animation/DA_Ida_IntegrationData.uasset
LFS
Normal file
Binary file not shown.
BIN
Content/AGame/Animation/Locomotion/ABP_UE5_Main.uasset
LFS
Normal file
BIN
Content/AGame/Animation/Locomotion/ABP_UE5_Main.uasset
LFS
Normal file
Binary file not shown.
BIN
Content/AGame/Animation/Retargeters/ABP_Retargeter.uasset
LFS
Normal file
BIN
Content/AGame/Animation/Retargeters/ABP_Retargeter.uasset
LFS
Normal file
Binary file not shown.
BIN
Content/AGame/Animation/Retargeters/IK_AutoGeneratedSource.uasset
LFS
Normal file
BIN
Content/AGame/Animation/Retargeters/IK_AutoGeneratedSource.uasset
LFS
Normal file
Binary file not shown.
BIN
Content/AGame/Animation/Retargeters/IK_AutoGeneratedTarget.uasset
LFS
Normal file
BIN
Content/AGame/Animation/Retargeters/IK_AutoGeneratedTarget.uasset
LFS
Normal file
Binary file not shown.
BIN
Content/AGame/Animation/Retargeters/RTG_Manny_To_Ida.uasset
LFS
Normal file
BIN
Content/AGame/Animation/Retargeters/RTG_Manny_To_Ida.uasset
LFS
Normal file
Binary file not shown.
BIN
Content/AGame/Character/AI/BP_AIBase.uasset
LFS
Normal file
BIN
Content/AGame/Character/AI/BP_AIBase.uasset
LFS
Normal file
Binary file not shown.
BIN
Content/AGame/Character/Ida/SKEL_UE5_F.uasset
LFS
Normal file
BIN
Content/AGame/Character/Ida/SKEL_UE5_F.uasset
LFS
Normal file
Binary file not shown.
BIN
Content/AGame/Character/Ida/SK_MMO_ADVANCED_F_01_AGame.uasset
LFS
Normal file
BIN
Content/AGame/Character/Ida/SK_MMO_ADVANCED_F_01_AGame.uasset
LFS
Normal file
Binary file not shown.
BIN
Content/AGame/Character/Ida/SK_MMO_ADVANCED_F_01_AGame_PhysicsAsset.uasset
LFS
Normal file
BIN
Content/AGame/Character/Ida/SK_MMO_ADVANCED_F_01_AGame_PhysicsAsset.uasset
LFS
Normal file
Binary file not shown.
BIN
Content/AGame/Character/Locomotion/SKM_Quinn_Simple_Source.uasset
LFS
Normal file
BIN
Content/AGame/Character/Locomotion/SKM_Quinn_Simple_Source.uasset
LFS
Normal file
Binary file not shown.
BIN
Content/AGame/Character/Locomotion/SKM_Quinn_Simple_Source_PhysicsAsset.uasset
LFS
Normal file
BIN
Content/AGame/Character/Locomotion/SKM_Quinn_Simple_Source_PhysicsAsset.uasset
LFS
Normal file
Binary file not shown.
BIN
Content/AGame/Character/Locomotion/SK_Mannequin.uasset
LFS
Normal file
BIN
Content/AGame/Character/Locomotion/SK_Mannequin.uasset
LFS
Normal file
Binary file not shown.
BIN
Content/AGame/Character/Player/BP_PlayerBase.uasset
LFS
Normal file
BIN
Content/AGame/Character/Player/BP_PlayerBase.uasset
LFS
Normal file
Binary file not shown.
BIN
Content/AGame/Character/SKM_Manny_Invis.uasset
LFS
Normal file
BIN
Content/AGame/Character/SKM_Manny_Invis.uasset
LFS
Normal file
Binary file not shown.
BIN
Content/AGame/Gameplay/AI/BP_PHYAIController.uasset
LFS
Normal file
BIN
Content/AGame/Gameplay/AI/BP_PHYAIController.uasset
LFS
Normal file
Binary file not shown.
BIN
Content/AGame/Gameplay/Camera/BP_CameraManager_Default.uasset
LFS
Normal file
BIN
Content/AGame/Gameplay/Camera/BP_CameraManager_Default.uasset
LFS
Normal file
Binary file not shown.
BIN
Content/AGame/Gameplay/Camera/DA_Camera_Default.uasset
LFS
Normal file
BIN
Content/AGame/Gameplay/Camera/DA_Camera_Default.uasset
LFS
Normal file
Binary file not shown.
BIN
Content/AGame/Gameplay/Framework/BP_PHYGameMode.uasset
LFS
Normal file
BIN
Content/AGame/Gameplay/Framework/BP_PHYGameMode.uasset
LFS
Normal file
Binary file not shown.
BIN
Content/AGame/Gameplay/Framework/BP_PHYGameState.uasset
LFS
Normal file
BIN
Content/AGame/Gameplay/Framework/BP_PHYGameState.uasset
LFS
Normal file
Binary file not shown.
BIN
Content/AGame/Gameplay/Framework/BP_PHYHUD.uasset
LFS
Normal file
BIN
Content/AGame/Gameplay/Framework/BP_PHYHUD.uasset
LFS
Normal file
Binary file not shown.
BIN
Content/AGame/Gameplay/Framework/BP_PHYSpectatorPawn.uasset
LFS
Normal file
BIN
Content/AGame/Gameplay/Framework/BP_PHYSpectatorPawn.uasset
LFS
Normal file
Binary file not shown.
BIN
Content/AGame/Gameplay/Player/BP_PHYPlayerCameraManager.uasset
LFS
Normal file
BIN
Content/AGame/Gameplay/Player/BP_PHYPlayerCameraManager.uasset
LFS
Normal file
Binary file not shown.
BIN
Content/AGame/Gameplay/Player/BP_PHYPlayerController.uasset
LFS
Normal file
BIN
Content/AGame/Gameplay/Player/BP_PHYPlayerController.uasset
LFS
Normal file
Binary file not shown.
BIN
Content/AGame/Gameplay/Player/BP_PHYPlayerState.uasset
LFS
Normal file
BIN
Content/AGame/Gameplay/Player/BP_PHYPlayerState.uasset
LFS
Normal file
Binary file not shown.
BIN
Content/AGame/Input/Action/Movement/IA_PHY_Look.uasset
LFS
Normal file
BIN
Content/AGame/Input/Action/Movement/IA_PHY_Look.uasset
LFS
Normal file
Binary file not shown.
BIN
Content/AGame/Input/Action/Movement/IA_PHY_Move.uasset
LFS
Normal file
BIN
Content/AGame/Input/Action/Movement/IA_PHY_Move.uasset
LFS
Normal file
Binary file not shown.
BIN
Content/AGame/Input/ICS_Default.uasset
LFS
Normal file
BIN
Content/AGame/Input/ICS_Default.uasset
LFS
Normal file
Binary file not shown.
BIN
Content/AGame/Input/IC_Default.uasset
LFS
Normal file
BIN
Content/AGame/Input/IC_Default.uasset
LFS
Normal file
Binary file not shown.
BIN
Content/AGame/Input/IMC_Default.uasset
LFS
Normal file
BIN
Content/AGame/Input/IMC_Default.uasset
LFS
Normal file
Binary file not shown.
BIN
Content/AGame/Map/Map_Test.umap
LFS
BIN
Content/AGame/Map/Map_Test.umap
LFS
Binary file not shown.
BIN
Content/AGame/UI/Attributes/Textures/T_PHY_UI_AttributeBackplate.uasset
LFS
Normal file
BIN
Content/AGame/UI/Attributes/Textures/T_PHY_UI_AttributeBackplate.uasset
LFS
Normal file
Binary file not shown.
BIN
Content/AGame/UI/Attributes/Textures/T_PHY_UI_CinnabarTab.uasset
LFS
Normal file
BIN
Content/AGame/UI/Attributes/Textures/T_PHY_UI_CinnabarTab.uasset
LFS
Normal file
Binary file not shown.
BIN
Content/AGame/UI/Attributes/Textures/T_PHY_UI_GoldRule.uasset
LFS
Normal file
BIN
Content/AGame/UI/Attributes/Textures/T_PHY_UI_GoldRule.uasset
LFS
Normal file
Binary file not shown.
BIN
Content/AGame/UI/Attributes/Textures/T_PHY_UI_JadePanel.uasset
LFS
Normal file
BIN
Content/AGame/UI/Attributes/Textures/T_PHY_UI_JadePanel.uasset
LFS
Normal file
Binary file not shown.
BIN
Content/AGame/UI/Attributes/Textures/T_PHY_UI_PaperPanel.uasset
LFS
Normal file
BIN
Content/AGame/UI/Attributes/Textures/T_PHY_UI_PaperPanel.uasset
LFS
Normal file
Binary file not shown.
BIN
Content/AGame/UI/Attributes/Textures/T_PHY_UI_PortraitFrame.uasset
LFS
Normal file
BIN
Content/AGame/UI/Attributes/Textures/T_PHY_UI_PortraitFrame.uasset
LFS
Normal file
Binary file not shown.
BIN
Content/AGame/UI/Attributes/Textures/T_PHY_UI_RowStrip.uasset
LFS
Normal file
BIN
Content/AGame/UI/Attributes/Textures/T_PHY_UI_RowStrip.uasset
LFS
Normal file
Binary file not shown.
28
PHY.uproject
28
PHY.uproject
@@ -11,6 +11,10 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"Plugins": [
|
"Plugins": [
|
||||||
|
{
|
||||||
|
"Name": "PythonScriptPlugin",
|
||||||
|
"Enabled": true
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"Name": "ModelingToolsEditorMode",
|
"Name": "ModelingToolsEditorMode",
|
||||||
"Enabled": true,
|
"Enabled": true,
|
||||||
@@ -26,6 +30,10 @@
|
|||||||
"Name": "ModularGameplay",
|
"Name": "ModularGameplay",
|
||||||
"Enabled": true
|
"Enabled": true
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"Name": "IKRig",
|
||||||
|
"Enabled": true
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"Name": "GenericCombatSystem",
|
"Name": "GenericCombatSystem",
|
||||||
"Enabled": true,
|
"Enabled": true,
|
||||||
@@ -53,6 +61,26 @@
|
|||||||
"SupportedTargetPlatforms": [
|
"SupportedTargetPlatforms": [
|
||||||
"Win64"
|
"Win64"
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "UE_MCP_Bridge",
|
||||||
|
"Enabled": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "Niagara",
|
||||||
|
"Enabled": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "PCG",
|
||||||
|
"Enabled": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "EnhancedInput",
|
||||||
|
"Enabled": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "CommonUI",
|
||||||
|
"Enabled": true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,12 +19,22 @@ public class PHY : ModuleRules
|
|||||||
"GameplayAbilities",
|
"GameplayAbilities",
|
||||||
"GameplayTasks",
|
"GameplayTasks",
|
||||||
"ModularGameplay",
|
"ModularGameplay",
|
||||||
|
"CommonUI",
|
||||||
|
"CommonInput",
|
||||||
|
"UMG",
|
||||||
|
"Slate",
|
||||||
|
"SlateCore",
|
||||||
"GenericGameplayAbilities",
|
"GenericGameplayAbilities",
|
||||||
"GenericCombatSystem",
|
"GenericCombatSystem",
|
||||||
"GenericInputSystem",
|
"GenericInputSystem",
|
||||||
"GenericGameSystem",
|
"GenericGameSystem",
|
||||||
|
"GenericUISystem",
|
||||||
"GenericEffectsSystem",
|
"GenericEffectsSystem",
|
||||||
|
"SLSCore",
|
||||||
|
"SmoothLocomotionSystem",
|
||||||
|
"SLSIntegration",
|
||||||
"AuroraDevs_UGC",
|
"AuroraDevs_UGC",
|
||||||
|
"IKRig",
|
||||||
"AIModule"
|
"AIModule"
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,74 @@
|
|||||||
|
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||||
|
|
||||||
|
#include "AbilitySystem/Attributes/PHYAttributeCalculationLibrary.h"
|
||||||
|
|
||||||
|
#include UE_INLINE_GENERATED_CPP_BY_NAME(PHYAttributeCalculationLibrary)
|
||||||
|
|
||||||
|
#include "AbilitySystem/Attributes/PHYCoreAttributeSet.h"
|
||||||
|
|
||||||
|
FPHYCoreAttributeSnapshot UPHYAttributeCalculationLibrary::MakeCoreSnapshotFromDefaults()
|
||||||
|
{
|
||||||
|
const FPHYCoreAttributeDefaults& Defaults = GetDefault<UPHYAttributeSettings>()->DefaultCoreAttributes;
|
||||||
|
|
||||||
|
FPHYCoreAttributeSnapshot Snapshot;
|
||||||
|
Snapshot.Strength = Defaults.Strength;
|
||||||
|
Snapshot.Dexterity = Defaults.Dexterity;
|
||||||
|
Snapshot.Vitality = Defaults.Vitality;
|
||||||
|
Snapshot.Intelligence = Defaults.Intelligence;
|
||||||
|
Snapshot.Spirit = Defaults.Spirit;
|
||||||
|
Snapshot.Perception = Defaults.Perception;
|
||||||
|
return Snapshot;
|
||||||
|
}
|
||||||
|
|
||||||
|
FPHYCoreAttributeSnapshot UPHYAttributeCalculationLibrary::MakeCoreSnapshotFromAttributeSet(const UPHYCoreAttributeSet* CoreAttributes)
|
||||||
|
{
|
||||||
|
if (!CoreAttributes)
|
||||||
|
{
|
||||||
|
return MakeCoreSnapshotFromDefaults();
|
||||||
|
}
|
||||||
|
|
||||||
|
FPHYCoreAttributeSnapshot Snapshot;
|
||||||
|
Snapshot.Strength = CoreAttributes->GetStrength();
|
||||||
|
Snapshot.Dexterity = CoreAttributes->GetDexterity();
|
||||||
|
Snapshot.Vitality = CoreAttributes->GetVitality();
|
||||||
|
Snapshot.Intelligence = CoreAttributes->GetIntelligence();
|
||||||
|
Snapshot.Spirit = CoreAttributes->GetSpirit();
|
||||||
|
Snapshot.Perception = CoreAttributes->GetPerception();
|
||||||
|
return Snapshot;
|
||||||
|
}
|
||||||
|
|
||||||
|
FPHYDerivedCombatAttributes UPHYAttributeCalculationLibrary::CalculateCombatAttributes(const FPHYCoreAttributeSnapshot& CoreAttributes)
|
||||||
|
{
|
||||||
|
const FPHYCombatAttributeFormula& Formula = GetDefault<UPHYAttributeSettings>()->CombatFormula;
|
||||||
|
|
||||||
|
FPHYDerivedCombatAttributes Result;
|
||||||
|
Result.MaxHealth = Formula.MaxHealthBase + CoreAttributes.Vitality * Formula.MaxHealthVitality + CoreAttributes.Strength * Formula.MaxHealthStrength;
|
||||||
|
Result.MaxMana = Formula.MaxManaBase + CoreAttributes.Intelligence * Formula.MaxManaIntelligence + CoreAttributes.Spirit * Formula.MaxManaSpirit;
|
||||||
|
Result.MaxStamina = Formula.MaxStaminaBase + CoreAttributes.Vitality * Formula.MaxStaminaVitality + CoreAttributes.Dexterity * Formula.MaxStaminaDexterity;
|
||||||
|
Result.PhysicalAttackPower = CoreAttributes.Strength * Formula.PhysicalAttackPowerStrength + CoreAttributes.Dexterity * Formula.PhysicalAttackPowerDexterity;
|
||||||
|
Result.SpellPower = CoreAttributes.Intelligence * Formula.SpellPowerIntelligence + CoreAttributes.Spirit * Formula.SpellPowerSpirit;
|
||||||
|
Result.Armor = CoreAttributes.Vitality * Formula.ArmorVitality + CoreAttributes.Strength * Formula.ArmorStrength;
|
||||||
|
Result.MagicResistance = CoreAttributes.Spirit * Formula.MagicResistanceSpirit + CoreAttributes.Intelligence * Formula.MagicResistanceIntelligence;
|
||||||
|
Result.Accuracy = FMath::Clamp(Formula.AccuracyBase + CoreAttributes.Perception * Formula.AccuracyPerception + CoreAttributes.Dexterity * Formula.AccuracyDexterity, 0.0f, 1.0f);
|
||||||
|
Result.Evasion = FMath::Clamp(Formula.EvasionBase + CoreAttributes.Dexterity * Formula.EvasionDexterity + CoreAttributes.Perception * Formula.EvasionPerception, 0.0f, 0.6f);
|
||||||
|
Result.CriticalChance = FMath::Clamp(Formula.CriticalChanceBase + CoreAttributes.Dexterity * Formula.CriticalChanceDexterity + CoreAttributes.Perception * Formula.CriticalChancePerception, 0.0f, 0.6f);
|
||||||
|
Result.CriticalDamage = FMath::Clamp(Formula.CriticalDamageBase + CoreAttributes.Strength * Formula.CriticalDamageStrength + CoreAttributes.Perception * Formula.CriticalDamagePerception, 1.0f, 3.0f);
|
||||||
|
Result.AttackSpeed = FMath::Clamp(Formula.AttackSpeedBase + CoreAttributes.Dexterity * Formula.AttackSpeedDexterity, 0.2f, 3.0f);
|
||||||
|
Result.CooldownReduction = FMath::Clamp(Formula.CooldownReductionBase + CoreAttributes.Intelligence * Formula.CooldownReductionIntelligence + CoreAttributes.Spirit * Formula.CooldownReductionSpirit, 0.0f, 0.6f);
|
||||||
|
Result.BlockPower = Formula.BlockPowerBase + CoreAttributes.Strength * Formula.BlockPowerStrength + CoreAttributes.Vitality * Formula.BlockPowerVitality;
|
||||||
|
Result.GuardBreakPower = Formula.GuardBreakPowerBase + CoreAttributes.Strength * Formula.GuardBreakPowerStrength + CoreAttributes.Perception * Formula.GuardBreakPowerPerception;
|
||||||
|
Result.Poise = Formula.PoiseBase + CoreAttributes.Vitality * Formula.PoiseVitality + CoreAttributes.Strength * Formula.PoiseStrength;
|
||||||
|
Result.PoiseDamage = Formula.PoiseDamageBase + CoreAttributes.Strength * Formula.PoiseDamageStrength + CoreAttributes.Dexterity * Formula.PoiseDamageDexterity;
|
||||||
|
return Result;
|
||||||
|
}
|
||||||
|
|
||||||
|
FPHYDerivedElementAttributes UPHYAttributeCalculationLibrary::CalculateElementAttributes(const FPHYCoreAttributeSnapshot& CoreAttributes)
|
||||||
|
{
|
||||||
|
const FPHYElementAttributeFormula& Formula = GetDefault<UPHYAttributeSettings>()->ElementFormula;
|
||||||
|
|
||||||
|
FPHYDerivedElementAttributes Result;
|
||||||
|
Result.DamageBonus = FMath::Clamp(Formula.DamageBonusBase + CoreAttributes.Intelligence * Formula.DamageBonusIntelligence + CoreAttributes.Perception * Formula.DamageBonusPerception, 0.0f, 2.0f);
|
||||||
|
Result.Resistance = FMath::Clamp(Formula.ResistanceBase + CoreAttributes.Spirit * Formula.ResistanceSpirit + CoreAttributes.Vitality * Formula.ResistanceVitality, 0.0f, 0.75f);
|
||||||
|
Result.Penetration = FMath::Clamp(Formula.PenetrationBase + CoreAttributes.Perception * Formula.PenetrationPerception + CoreAttributes.Intelligence * Formula.PenetrationIntelligence, 0.0f, 0.75f);
|
||||||
|
return Result;
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||||
|
|
||||||
|
#include "AbilitySystem/Attributes/PHYAttributeSettings.h"
|
||||||
|
|
||||||
|
#include UE_INLINE_GENERATED_CPP_BY_NAME(PHYAttributeSettings)
|
||||||
@@ -0,0 +1,202 @@
|
|||||||
|
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||||
|
|
||||||
|
#include "AbilitySystem/Attributes/PHYCombatAttributeSet.h"
|
||||||
|
|
||||||
|
#include UE_INLINE_GENERATED_CPP_BY_NAME(PHYCombatAttributeSet)
|
||||||
|
|
||||||
|
#include "AbilitySystem/Attributes/PHYAttributeCalculationLibrary.h"
|
||||||
|
#include "AbilitySystem/Attributes/PHYCoreAttributeSet.h"
|
||||||
|
#include "GameplayEffectExtension.h"
|
||||||
|
#include "Net/UnrealNetwork.h"
|
||||||
|
|
||||||
|
UPHYCombatAttributeSet::UPHYCombatAttributeSet()
|
||||||
|
{
|
||||||
|
InitializeFromConfiguredCoreDefaults();
|
||||||
|
}
|
||||||
|
|
||||||
|
void UPHYCombatAttributeSet::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
|
||||||
|
{
|
||||||
|
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
|
||||||
|
|
||||||
|
DOREPLIFETIME_CONDITION_NOTIFY(UPHYCombatAttributeSet, Health, COND_None, REPNOTIFY_Always);
|
||||||
|
DOREPLIFETIME_CONDITION_NOTIFY(UPHYCombatAttributeSet, MaxHealth, COND_None, REPNOTIFY_Always);
|
||||||
|
DOREPLIFETIME_CONDITION_NOTIFY(UPHYCombatAttributeSet, Mana, COND_None, REPNOTIFY_Always);
|
||||||
|
DOREPLIFETIME_CONDITION_NOTIFY(UPHYCombatAttributeSet, MaxMana, COND_None, REPNOTIFY_Always);
|
||||||
|
DOREPLIFETIME_CONDITION_NOTIFY(UPHYCombatAttributeSet, Stamina, COND_None, REPNOTIFY_Always);
|
||||||
|
DOREPLIFETIME_CONDITION_NOTIFY(UPHYCombatAttributeSet, MaxStamina, COND_None, REPNOTIFY_Always);
|
||||||
|
DOREPLIFETIME_CONDITION_NOTIFY(UPHYCombatAttributeSet, PhysicalAttackPower, COND_None, REPNOTIFY_Always);
|
||||||
|
DOREPLIFETIME_CONDITION_NOTIFY(UPHYCombatAttributeSet, SpellPower, COND_None, REPNOTIFY_Always);
|
||||||
|
DOREPLIFETIME_CONDITION_NOTIFY(UPHYCombatAttributeSet, Armor, COND_None, REPNOTIFY_Always);
|
||||||
|
DOREPLIFETIME_CONDITION_NOTIFY(UPHYCombatAttributeSet, MagicResistance, COND_None, REPNOTIFY_Always);
|
||||||
|
DOREPLIFETIME_CONDITION_NOTIFY(UPHYCombatAttributeSet, Accuracy, COND_None, REPNOTIFY_Always);
|
||||||
|
DOREPLIFETIME_CONDITION_NOTIFY(UPHYCombatAttributeSet, Evasion, COND_None, REPNOTIFY_Always);
|
||||||
|
DOREPLIFETIME_CONDITION_NOTIFY(UPHYCombatAttributeSet, CriticalChance, COND_None, REPNOTIFY_Always);
|
||||||
|
DOREPLIFETIME_CONDITION_NOTIFY(UPHYCombatAttributeSet, CriticalDamage, COND_None, REPNOTIFY_Always);
|
||||||
|
DOREPLIFETIME_CONDITION_NOTIFY(UPHYCombatAttributeSet, AttackSpeed, COND_None, REPNOTIFY_Always);
|
||||||
|
DOREPLIFETIME_CONDITION_NOTIFY(UPHYCombatAttributeSet, CooldownReduction, COND_None, REPNOTIFY_Always);
|
||||||
|
DOREPLIFETIME_CONDITION_NOTIFY(UPHYCombatAttributeSet, BlockPower, COND_None, REPNOTIFY_Always);
|
||||||
|
DOREPLIFETIME_CONDITION_NOTIFY(UPHYCombatAttributeSet, GuardBreakPower, COND_None, REPNOTIFY_Always);
|
||||||
|
DOREPLIFETIME_CONDITION_NOTIFY(UPHYCombatAttributeSet, Poise, COND_None, REPNOTIFY_Always);
|
||||||
|
DOREPLIFETIME_CONDITION_NOTIFY(UPHYCombatAttributeSet, PoiseDamage, COND_None, REPNOTIFY_Always);
|
||||||
|
}
|
||||||
|
|
||||||
|
void UPHYCombatAttributeSet::PreAttributeChange(const FGameplayAttribute& Attribute, float& NewValue)
|
||||||
|
{
|
||||||
|
Super::PreAttributeChange(Attribute, NewValue);
|
||||||
|
|
||||||
|
if (Attribute == GetHealthAttribute())
|
||||||
|
{
|
||||||
|
NewValue = FMath::Clamp(NewValue, 0.0f, GetMaxHealth());
|
||||||
|
}
|
||||||
|
else if (Attribute == GetManaAttribute())
|
||||||
|
{
|
||||||
|
NewValue = FMath::Clamp(NewValue, 0.0f, GetMaxMana());
|
||||||
|
}
|
||||||
|
else if (Attribute == GetStaminaAttribute())
|
||||||
|
{
|
||||||
|
NewValue = FMath::Clamp(NewValue, 0.0f, GetMaxStamina());
|
||||||
|
}
|
||||||
|
else if (Attribute == GetMaxHealthAttribute() || Attribute == GetMaxManaAttribute() || Attribute == GetMaxStaminaAttribute())
|
||||||
|
{
|
||||||
|
NewValue = FMath::Max(NewValue, 1.0f);
|
||||||
|
}
|
||||||
|
else if (Attribute == GetAccuracyAttribute())
|
||||||
|
{
|
||||||
|
NewValue = FMath::Clamp(NewValue, 0.0f, 1.0f);
|
||||||
|
}
|
||||||
|
else if (Attribute == GetEvasionAttribute() || Attribute == GetCriticalChanceAttribute() || Attribute == GetCooldownReductionAttribute())
|
||||||
|
{
|
||||||
|
NewValue = FMath::Clamp(NewValue, 0.0f, 0.6f);
|
||||||
|
}
|
||||||
|
else if (Attribute == GetCriticalDamageAttribute())
|
||||||
|
{
|
||||||
|
NewValue = FMath::Clamp(NewValue, 1.0f, 5.0f);
|
||||||
|
}
|
||||||
|
else if (Attribute == GetAttackSpeedAttribute())
|
||||||
|
{
|
||||||
|
NewValue = FMath::Clamp(NewValue, 0.1f, 5.0f);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
NewValue = FMath::Max(NewValue, 0.0f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void UPHYCombatAttributeSet::PostGameplayEffectExecute(const FGameplayEffectModCallbackData& Data)
|
||||||
|
{
|
||||||
|
Super::PostGameplayEffectExecute(Data);
|
||||||
|
|
||||||
|
if (Data.EvaluatedData.Attribute == GetHealthAttribute())
|
||||||
|
{
|
||||||
|
SetHealth(FMath::Clamp(GetHealth(), 0.0f, GetMaxHealth()));
|
||||||
|
}
|
||||||
|
else if (Data.EvaluatedData.Attribute == GetManaAttribute())
|
||||||
|
{
|
||||||
|
SetMana(FMath::Clamp(GetMana(), 0.0f, GetMaxMana()));
|
||||||
|
}
|
||||||
|
else if (Data.EvaluatedData.Attribute == GetStaminaAttribute())
|
||||||
|
{
|
||||||
|
SetStamina(FMath::Clamp(GetStamina(), 0.0f, GetMaxStamina()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void UPHYCombatAttributeSet::PostAttributeChange(const FGameplayAttribute& Attribute, float OldValue, float NewValue)
|
||||||
|
{
|
||||||
|
Super::PostAttributeChange(Attribute, OldValue, NewValue);
|
||||||
|
|
||||||
|
if (Attribute == GetMaxHealthAttribute() && GetHealth() > NewValue)
|
||||||
|
{
|
||||||
|
SetHealth(NewValue);
|
||||||
|
}
|
||||||
|
else if (Attribute == GetMaxManaAttribute() && GetMana() > NewValue)
|
||||||
|
{
|
||||||
|
SetMana(NewValue);
|
||||||
|
}
|
||||||
|
else if (Attribute == GetMaxStaminaAttribute() && GetStamina() > NewValue)
|
||||||
|
{
|
||||||
|
SetStamina(NewValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void UPHYCombatAttributeSet::InitializeFromConfiguredCoreDefaults()
|
||||||
|
{
|
||||||
|
const FPHYCoreAttributeSnapshot Snapshot = UPHYAttributeCalculationLibrary::MakeCoreSnapshotFromDefaults();
|
||||||
|
const FPHYDerivedCombatAttributes Derived = UPHYAttributeCalculationLibrary::CalculateCombatAttributes(Snapshot);
|
||||||
|
|
||||||
|
InitMaxHealth(Derived.MaxHealth);
|
||||||
|
InitHealth(Derived.MaxHealth);
|
||||||
|
InitMaxMana(Derived.MaxMana);
|
||||||
|
InitMana(Derived.MaxMana);
|
||||||
|
InitMaxStamina(Derived.MaxStamina);
|
||||||
|
InitStamina(Derived.MaxStamina);
|
||||||
|
InitPhysicalAttackPower(Derived.PhysicalAttackPower);
|
||||||
|
InitSpellPower(Derived.SpellPower);
|
||||||
|
InitArmor(Derived.Armor);
|
||||||
|
InitMagicResistance(Derived.MagicResistance);
|
||||||
|
InitAccuracy(Derived.Accuracy);
|
||||||
|
InitEvasion(Derived.Evasion);
|
||||||
|
InitCriticalChance(Derived.CriticalChance);
|
||||||
|
InitCriticalDamage(Derived.CriticalDamage);
|
||||||
|
InitAttackSpeed(Derived.AttackSpeed);
|
||||||
|
InitCooldownReduction(Derived.CooldownReduction);
|
||||||
|
InitBlockPower(Derived.BlockPower);
|
||||||
|
InitGuardBreakPower(Derived.GuardBreakPower);
|
||||||
|
InitPoise(Derived.Poise);
|
||||||
|
InitPoiseDamage(Derived.PoiseDamage);
|
||||||
|
}
|
||||||
|
|
||||||
|
void UPHYCombatAttributeSet::InitializeFromCoreAttributes(const UPHYCoreAttributeSet* CoreAttributes)
|
||||||
|
{
|
||||||
|
const FPHYCoreAttributeSnapshot Snapshot = UPHYAttributeCalculationLibrary::MakeCoreSnapshotFromAttributeSet(CoreAttributes);
|
||||||
|
const FPHYDerivedCombatAttributes Derived = UPHYAttributeCalculationLibrary::CalculateCombatAttributes(Snapshot);
|
||||||
|
|
||||||
|
SetMaxHealth(Derived.MaxHealth);
|
||||||
|
SetHealth(FMath::Clamp(GetHealth(), 0.0f, Derived.MaxHealth));
|
||||||
|
SetMaxMana(Derived.MaxMana);
|
||||||
|
SetMana(FMath::Clamp(GetMana(), 0.0f, Derived.MaxMana));
|
||||||
|
SetMaxStamina(Derived.MaxStamina);
|
||||||
|
SetStamina(FMath::Clamp(GetStamina(), 0.0f, Derived.MaxStamina));
|
||||||
|
SetPhysicalAttackPower(Derived.PhysicalAttackPower);
|
||||||
|
SetSpellPower(Derived.SpellPower);
|
||||||
|
SetArmor(Derived.Armor);
|
||||||
|
SetMagicResistance(Derived.MagicResistance);
|
||||||
|
SetAccuracy(Derived.Accuracy);
|
||||||
|
SetEvasion(Derived.Evasion);
|
||||||
|
SetCriticalChance(Derived.CriticalChance);
|
||||||
|
SetCriticalDamage(Derived.CriticalDamage);
|
||||||
|
SetAttackSpeed(Derived.AttackSpeed);
|
||||||
|
SetCooldownReduction(Derived.CooldownReduction);
|
||||||
|
SetBlockPower(Derived.BlockPower);
|
||||||
|
SetGuardBreakPower(Derived.GuardBreakPower);
|
||||||
|
SetPoise(Derived.Poise);
|
||||||
|
SetPoiseDamage(Derived.PoiseDamage);
|
||||||
|
}
|
||||||
|
|
||||||
|
#define PHY_COMBAT_REPNOTIFY(PropertyName) \
|
||||||
|
void UPHYCombatAttributeSet::OnRep_##PropertyName(const FGameplayAttributeData& OldValue) \
|
||||||
|
{ \
|
||||||
|
GAMEPLAYATTRIBUTE_REPNOTIFY(UPHYCombatAttributeSet, PropertyName, OldValue); \
|
||||||
|
}
|
||||||
|
|
||||||
|
PHY_COMBAT_REPNOTIFY(Health)
|
||||||
|
PHY_COMBAT_REPNOTIFY(MaxHealth)
|
||||||
|
PHY_COMBAT_REPNOTIFY(Mana)
|
||||||
|
PHY_COMBAT_REPNOTIFY(MaxMana)
|
||||||
|
PHY_COMBAT_REPNOTIFY(Stamina)
|
||||||
|
PHY_COMBAT_REPNOTIFY(MaxStamina)
|
||||||
|
PHY_COMBAT_REPNOTIFY(PhysicalAttackPower)
|
||||||
|
PHY_COMBAT_REPNOTIFY(SpellPower)
|
||||||
|
PHY_COMBAT_REPNOTIFY(Armor)
|
||||||
|
PHY_COMBAT_REPNOTIFY(MagicResistance)
|
||||||
|
PHY_COMBAT_REPNOTIFY(Accuracy)
|
||||||
|
PHY_COMBAT_REPNOTIFY(Evasion)
|
||||||
|
PHY_COMBAT_REPNOTIFY(CriticalChance)
|
||||||
|
PHY_COMBAT_REPNOTIFY(CriticalDamage)
|
||||||
|
PHY_COMBAT_REPNOTIFY(AttackSpeed)
|
||||||
|
PHY_COMBAT_REPNOTIFY(CooldownReduction)
|
||||||
|
PHY_COMBAT_REPNOTIFY(BlockPower)
|
||||||
|
PHY_COMBAT_REPNOTIFY(GuardBreakPower)
|
||||||
|
PHY_COMBAT_REPNOTIFY(Poise)
|
||||||
|
PHY_COMBAT_REPNOTIFY(PoiseDamage)
|
||||||
|
|
||||||
|
#undef PHY_COMBAT_REPNOTIFY
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||||
|
|
||||||
|
#include "AbilitySystem/Attributes/PHYCoreAttributeSet.h"
|
||||||
|
|
||||||
|
#include UE_INLINE_GENERATED_CPP_BY_NAME(PHYCoreAttributeSet)
|
||||||
|
|
||||||
|
#include "AbilitySystem/Attributes/PHYAttributeSettings.h"
|
||||||
|
#include "Net/UnrealNetwork.h"
|
||||||
|
|
||||||
|
UPHYCoreAttributeSet::UPHYCoreAttributeSet()
|
||||||
|
{
|
||||||
|
const FPHYCoreAttributeDefaults& Defaults = GetDefault<UPHYAttributeSettings>()->DefaultCoreAttributes;
|
||||||
|
|
||||||
|
InitStrength(Defaults.Strength);
|
||||||
|
InitDexterity(Defaults.Dexterity);
|
||||||
|
InitVitality(Defaults.Vitality);
|
||||||
|
InitIntelligence(Defaults.Intelligence);
|
||||||
|
InitSpirit(Defaults.Spirit);
|
||||||
|
InitPerception(Defaults.Perception);
|
||||||
|
}
|
||||||
|
|
||||||
|
void UPHYCoreAttributeSet::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
|
||||||
|
{
|
||||||
|
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
|
||||||
|
|
||||||
|
DOREPLIFETIME_CONDITION_NOTIFY(UPHYCoreAttributeSet, Strength, COND_None, REPNOTIFY_Always);
|
||||||
|
DOREPLIFETIME_CONDITION_NOTIFY(UPHYCoreAttributeSet, Dexterity, COND_None, REPNOTIFY_Always);
|
||||||
|
DOREPLIFETIME_CONDITION_NOTIFY(UPHYCoreAttributeSet, Vitality, COND_None, REPNOTIFY_Always);
|
||||||
|
DOREPLIFETIME_CONDITION_NOTIFY(UPHYCoreAttributeSet, Intelligence, COND_None, REPNOTIFY_Always);
|
||||||
|
DOREPLIFETIME_CONDITION_NOTIFY(UPHYCoreAttributeSet, Spirit, COND_None, REPNOTIFY_Always);
|
||||||
|
DOREPLIFETIME_CONDITION_NOTIFY(UPHYCoreAttributeSet, Perception, COND_None, REPNOTIFY_Always);
|
||||||
|
}
|
||||||
|
|
||||||
|
void UPHYCoreAttributeSet::OnRep_Strength(const FGameplayAttributeData& OldValue)
|
||||||
|
{
|
||||||
|
GAMEPLAYATTRIBUTE_REPNOTIFY(UPHYCoreAttributeSet, Strength, OldValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
void UPHYCoreAttributeSet::OnRep_Dexterity(const FGameplayAttributeData& OldValue)
|
||||||
|
{
|
||||||
|
GAMEPLAYATTRIBUTE_REPNOTIFY(UPHYCoreAttributeSet, Dexterity, OldValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
void UPHYCoreAttributeSet::OnRep_Vitality(const FGameplayAttributeData& OldValue)
|
||||||
|
{
|
||||||
|
GAMEPLAYATTRIBUTE_REPNOTIFY(UPHYCoreAttributeSet, Vitality, OldValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
void UPHYCoreAttributeSet::OnRep_Intelligence(const FGameplayAttributeData& OldValue)
|
||||||
|
{
|
||||||
|
GAMEPLAYATTRIBUTE_REPNOTIFY(UPHYCoreAttributeSet, Intelligence, OldValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
void UPHYCoreAttributeSet::OnRep_Spirit(const FGameplayAttributeData& OldValue)
|
||||||
|
{
|
||||||
|
GAMEPLAYATTRIBUTE_REPNOTIFY(UPHYCoreAttributeSet, Spirit, OldValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
void UPHYCoreAttributeSet::OnRep_Perception(const FGameplayAttributeData& OldValue)
|
||||||
|
{
|
||||||
|
GAMEPLAYATTRIBUTE_REPNOTIFY(UPHYCoreAttributeSet, Perception, OldValue);
|
||||||
|
}
|
||||||
@@ -0,0 +1,128 @@
|
|||||||
|
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||||
|
|
||||||
|
#include "AbilitySystem/Attributes/PHYElementAttributeSet.h"
|
||||||
|
|
||||||
|
#include UE_INLINE_GENERATED_CPP_BY_NAME(PHYElementAttributeSet)
|
||||||
|
|
||||||
|
#include "AbilitySystem/Attributes/PHYAttributeCalculationLibrary.h"
|
||||||
|
#include "AbilitySystem/Attributes/PHYCoreAttributeSet.h"
|
||||||
|
#include "Net/UnrealNetwork.h"
|
||||||
|
|
||||||
|
UPHYElementAttributeSet::UPHYElementAttributeSet()
|
||||||
|
{
|
||||||
|
InitializeFromConfiguredCoreDefaults();
|
||||||
|
}
|
||||||
|
|
||||||
|
void UPHYElementAttributeSet::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
|
||||||
|
{
|
||||||
|
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
|
||||||
|
|
||||||
|
DOREPLIFETIME_CONDITION_NOTIFY(UPHYElementAttributeSet, FireDamageBonus, COND_None, REPNOTIFY_Always);
|
||||||
|
DOREPLIFETIME_CONDITION_NOTIFY(UPHYElementAttributeSet, WaterDamageBonus, COND_None, REPNOTIFY_Always);
|
||||||
|
DOREPLIFETIME_CONDITION_NOTIFY(UPHYElementAttributeSet, IceDamageBonus, COND_None, REPNOTIFY_Always);
|
||||||
|
DOREPLIFETIME_CONDITION_NOTIFY(UPHYElementAttributeSet, LightningDamageBonus, COND_None, REPNOTIFY_Always);
|
||||||
|
DOREPLIFETIME_CONDITION_NOTIFY(UPHYElementAttributeSet, EarthDamageBonus, COND_None, REPNOTIFY_Always);
|
||||||
|
DOREPLIFETIME_CONDITION_NOTIFY(UPHYElementAttributeSet, WindDamageBonus, COND_None, REPNOTIFY_Always);
|
||||||
|
DOREPLIFETIME_CONDITION_NOTIFY(UPHYElementAttributeSet, LightDamageBonus, COND_None, REPNOTIFY_Always);
|
||||||
|
DOREPLIFETIME_CONDITION_NOTIFY(UPHYElementAttributeSet, DarkDamageBonus, COND_None, REPNOTIFY_Always);
|
||||||
|
DOREPLIFETIME_CONDITION_NOTIFY(UPHYElementAttributeSet, FireResistance, COND_None, REPNOTIFY_Always);
|
||||||
|
DOREPLIFETIME_CONDITION_NOTIFY(UPHYElementAttributeSet, WaterResistance, COND_None, REPNOTIFY_Always);
|
||||||
|
DOREPLIFETIME_CONDITION_NOTIFY(UPHYElementAttributeSet, IceResistance, COND_None, REPNOTIFY_Always);
|
||||||
|
DOREPLIFETIME_CONDITION_NOTIFY(UPHYElementAttributeSet, LightningResistance, COND_None, REPNOTIFY_Always);
|
||||||
|
DOREPLIFETIME_CONDITION_NOTIFY(UPHYElementAttributeSet, EarthResistance, COND_None, REPNOTIFY_Always);
|
||||||
|
DOREPLIFETIME_CONDITION_NOTIFY(UPHYElementAttributeSet, WindResistance, COND_None, REPNOTIFY_Always);
|
||||||
|
DOREPLIFETIME_CONDITION_NOTIFY(UPHYElementAttributeSet, LightResistance, COND_None, REPNOTIFY_Always);
|
||||||
|
DOREPLIFETIME_CONDITION_NOTIFY(UPHYElementAttributeSet, DarkResistance, COND_None, REPNOTIFY_Always);
|
||||||
|
DOREPLIFETIME_CONDITION_NOTIFY(UPHYElementAttributeSet, ElementPenetration, COND_None, REPNOTIFY_Always);
|
||||||
|
}
|
||||||
|
|
||||||
|
void UPHYElementAttributeSet::PreAttributeChange(const FGameplayAttribute& Attribute, float& NewValue)
|
||||||
|
{
|
||||||
|
Super::PreAttributeChange(Attribute, NewValue);
|
||||||
|
|
||||||
|
if (Attribute == GetFireResistanceAttribute() || Attribute == GetWaterResistanceAttribute() ||
|
||||||
|
Attribute == GetIceResistanceAttribute() || Attribute == GetLightningResistanceAttribute() ||
|
||||||
|
Attribute == GetEarthResistanceAttribute() || Attribute == GetWindResistanceAttribute() ||
|
||||||
|
Attribute == GetLightResistanceAttribute() || Attribute == GetDarkResistanceAttribute() ||
|
||||||
|
Attribute == GetElementPenetrationAttribute())
|
||||||
|
{
|
||||||
|
NewValue = FMath::Clamp(NewValue, 0.0f, 0.75f);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
NewValue = FMath::Clamp(NewValue, 0.0f, 2.0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
void UPHYElementAttributeSet::InitializeFromConfiguredCoreDefaults()
|
||||||
|
{
|
||||||
|
const FPHYCoreAttributeSnapshot Snapshot = UPHYAttributeCalculationLibrary::MakeCoreSnapshotFromDefaults();
|
||||||
|
const FPHYDerivedElementAttributes Derived = UPHYAttributeCalculationLibrary::CalculateElementAttributes(Snapshot);
|
||||||
|
|
||||||
|
InitFireDamageBonus(Derived.DamageBonus);
|
||||||
|
InitWaterDamageBonus(Derived.DamageBonus);
|
||||||
|
InitIceDamageBonus(Derived.DamageBonus);
|
||||||
|
InitLightningDamageBonus(Derived.DamageBonus);
|
||||||
|
InitEarthDamageBonus(Derived.DamageBonus);
|
||||||
|
InitWindDamageBonus(Derived.DamageBonus);
|
||||||
|
InitLightDamageBonus(Derived.DamageBonus);
|
||||||
|
InitDarkDamageBonus(Derived.DamageBonus);
|
||||||
|
InitFireResistance(Derived.Resistance);
|
||||||
|
InitWaterResistance(Derived.Resistance);
|
||||||
|
InitIceResistance(Derived.Resistance);
|
||||||
|
InitLightningResistance(Derived.Resistance);
|
||||||
|
InitEarthResistance(Derived.Resistance);
|
||||||
|
InitWindResistance(Derived.Resistance);
|
||||||
|
InitLightResistance(Derived.Resistance);
|
||||||
|
InitDarkResistance(Derived.Resistance);
|
||||||
|
InitElementPenetration(Derived.Penetration);
|
||||||
|
}
|
||||||
|
|
||||||
|
void UPHYElementAttributeSet::InitializeFromCoreAttributes(const UPHYCoreAttributeSet* CoreAttributes)
|
||||||
|
{
|
||||||
|
const FPHYCoreAttributeSnapshot Snapshot = UPHYAttributeCalculationLibrary::MakeCoreSnapshotFromAttributeSet(CoreAttributes);
|
||||||
|
const FPHYDerivedElementAttributes Derived = UPHYAttributeCalculationLibrary::CalculateElementAttributes(Snapshot);
|
||||||
|
|
||||||
|
SetFireDamageBonus(Derived.DamageBonus);
|
||||||
|
SetWaterDamageBonus(Derived.DamageBonus);
|
||||||
|
SetIceDamageBonus(Derived.DamageBonus);
|
||||||
|
SetLightningDamageBonus(Derived.DamageBonus);
|
||||||
|
SetEarthDamageBonus(Derived.DamageBonus);
|
||||||
|
SetWindDamageBonus(Derived.DamageBonus);
|
||||||
|
SetLightDamageBonus(Derived.DamageBonus);
|
||||||
|
SetDarkDamageBonus(Derived.DamageBonus);
|
||||||
|
SetFireResistance(Derived.Resistance);
|
||||||
|
SetWaterResistance(Derived.Resistance);
|
||||||
|
SetIceResistance(Derived.Resistance);
|
||||||
|
SetLightningResistance(Derived.Resistance);
|
||||||
|
SetEarthResistance(Derived.Resistance);
|
||||||
|
SetWindResistance(Derived.Resistance);
|
||||||
|
SetLightResistance(Derived.Resistance);
|
||||||
|
SetDarkResistance(Derived.Resistance);
|
||||||
|
SetElementPenetration(Derived.Penetration);
|
||||||
|
}
|
||||||
|
|
||||||
|
#define PHY_ELEMENT_REPNOTIFY(PropertyName) \
|
||||||
|
void UPHYElementAttributeSet::OnRep_##PropertyName(const FGameplayAttributeData& OldValue) \
|
||||||
|
{ \
|
||||||
|
GAMEPLAYATTRIBUTE_REPNOTIFY(UPHYElementAttributeSet, PropertyName, OldValue); \
|
||||||
|
}
|
||||||
|
|
||||||
|
PHY_ELEMENT_REPNOTIFY(FireDamageBonus)
|
||||||
|
PHY_ELEMENT_REPNOTIFY(WaterDamageBonus)
|
||||||
|
PHY_ELEMENT_REPNOTIFY(IceDamageBonus)
|
||||||
|
PHY_ELEMENT_REPNOTIFY(LightningDamageBonus)
|
||||||
|
PHY_ELEMENT_REPNOTIFY(EarthDamageBonus)
|
||||||
|
PHY_ELEMENT_REPNOTIFY(WindDamageBonus)
|
||||||
|
PHY_ELEMENT_REPNOTIFY(LightDamageBonus)
|
||||||
|
PHY_ELEMENT_REPNOTIFY(DarkDamageBonus)
|
||||||
|
PHY_ELEMENT_REPNOTIFY(FireResistance)
|
||||||
|
PHY_ELEMENT_REPNOTIFY(WaterResistance)
|
||||||
|
PHY_ELEMENT_REPNOTIFY(IceResistance)
|
||||||
|
PHY_ELEMENT_REPNOTIFY(LightningResistance)
|
||||||
|
PHY_ELEMENT_REPNOTIFY(EarthResistance)
|
||||||
|
PHY_ELEMENT_REPNOTIFY(WindResistance)
|
||||||
|
PHY_ELEMENT_REPNOTIFY(LightResistance)
|
||||||
|
PHY_ELEMENT_REPNOTIFY(DarkResistance)
|
||||||
|
PHY_ELEMENT_REPNOTIFY(ElementPenetration)
|
||||||
|
|
||||||
|
#undef PHY_ELEMENT_REPNOTIFY
|
||||||
7
Source/PHY/Private/Animation/PHYAnimationSettings.cpp
Normal file
7
Source/PHY/Private/Animation/PHYAnimationSettings.cpp
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||||
|
|
||||||
|
#include "Animation/PHYAnimationSettings.h"
|
||||||
|
|
||||||
|
#include UE_INLINE_GENERATED_CPP_BY_NAME(PHYAnimationSettings)
|
||||||
|
|
||||||
|
// 动画配置仅承载默认值,实际应用由 Mesh Bridge 在角色初始化时读取。
|
||||||
271
Source/PHY/Private/Animation/PHYCharacterMeshBridgeComponent.cpp
Normal file
271
Source/PHY/Private/Animation/PHYCharacterMeshBridgeComponent.cpp
Normal file
@@ -0,0 +1,271 @@
|
|||||||
|
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||||
|
|
||||||
|
#include "Animation/PHYCharacterMeshBridgeComponent.h"
|
||||||
|
|
||||||
|
#include UE_INLINE_GENERATED_CPP_BY_NAME(PHYCharacterMeshBridgeComponent)
|
||||||
|
|
||||||
|
#include "Animation/AnimInstance.h"
|
||||||
|
#include "Animation/PHYAnimationSettings.h"
|
||||||
|
#include "Class/PHYClassComponent.h"
|
||||||
|
#include "Animation/PHYRetargetPoseAnimInstance.h"
|
||||||
|
#include "Components/SkeletalMeshComponent.h"
|
||||||
|
#include "Engine/SkeletalMesh.h"
|
||||||
|
#include "GameFramework/Character.h"
|
||||||
|
|
||||||
|
UPHYCharacterMeshBridgeComponent::UPHYCharacterMeshBridgeComponent(const FObjectInitializer& ObjectInitializer)
|
||||||
|
: Super(ObjectInitializer)
|
||||||
|
{
|
||||||
|
PrimaryComponentTick.bCanEverTick = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void UPHYCharacterMeshBridgeComponent::BeginPlay()
|
||||||
|
{
|
||||||
|
Super::BeginPlay();
|
||||||
|
|
||||||
|
// 构造期或 PlayerState 职业同步可能早于显示 Mesh 注册,运行时再确认一次。
|
||||||
|
RefreshFollowPoseMode();
|
||||||
|
}
|
||||||
|
|
||||||
|
void UPHYCharacterMeshBridgeComponent::InitializeMeshBridge(ACharacter* Character, USkeletalMeshComponent* InDisplayMeshComponent)
|
||||||
|
{
|
||||||
|
SourceMeshComponent = Character ? Character->GetMesh() : nullptr;
|
||||||
|
DisplayMeshComponent = InDisplayMeshComponent;
|
||||||
|
|
||||||
|
if (!SourceMeshComponent || !DisplayMeshComponent)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
DisplayMeshComponent->SetCollisionEnabled(ECollisionEnabled::NoCollision);
|
||||||
|
DisplayMeshComponent->SetGenerateOverlapEvents(false);
|
||||||
|
DisplayMeshComponent->bReceivesDecals = SourceMeshComponent->bReceivesDecals;
|
||||||
|
DisplayMeshComponent->VisibilityBasedAnimTickOption = SourceMeshComponent->VisibilityBasedAnimTickOption;
|
||||||
|
|
||||||
|
ApplySourceDefaults();
|
||||||
|
|
||||||
|
const UPHYAnimationSettings* Settings = GetDefault<UPHYAnimationSettings>();
|
||||||
|
if (!CurrentRetargeter.IsValid())
|
||||||
|
{
|
||||||
|
CurrentRetargeter = Settings ? Settings->DefaultIKRetargeter : FSoftObjectPath();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Settings)
|
||||||
|
{
|
||||||
|
SourceMeshComponent->SetHiddenInGame(Settings->bHideSourceMeshInGame);
|
||||||
|
}
|
||||||
|
|
||||||
|
RefreshFollowPoseMode();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool UPHYCharacterMeshBridgeComponent::ApplyDisplayMesh(USkeletalMesh* NewDisplayMesh, TSubclassOf<UAnimInstance> NewDisplayAnimClass, FSoftObjectPath NewRetargeter)
|
||||||
|
{
|
||||||
|
if (!DisplayMeshComponent || !NewDisplayMesh)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const UPHYAnimationSettings* Settings = GetDefault<UPHYAnimationSettings>();
|
||||||
|
if (DisplayMeshComponent->GetSkeletalMeshAsset() && Settings && !Settings->bAllowRuntimeVisualMeshSwitch)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
DisplayMeshComponent->SetSkeletalMesh(NewDisplayMesh);
|
||||||
|
|
||||||
|
if (NewDisplayAnimClass)
|
||||||
|
{
|
||||||
|
DisplayMeshComponent->SetAnimationMode(EAnimationMode::AnimationBlueprint);
|
||||||
|
DisplayMeshComponent->SetAnimInstanceClass(NewDisplayAnimClass);
|
||||||
|
}
|
||||||
|
else if (Settings && !Settings->DefaultDisplayAnimClass.IsNull())
|
||||||
|
{
|
||||||
|
if (UClass* DefaultDisplayAnimClass = Settings->DefaultDisplayAnimClass.LoadSynchronous())
|
||||||
|
{
|
||||||
|
DisplayMeshComponent->SetAnimationMode(EAnimationMode::AnimationBlueprint);
|
||||||
|
DisplayMeshComponent->SetAnimInstanceClass(DefaultDisplayAnimClass);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CurrentRetargeter = NewRetargeter.IsValid() ? NewRetargeter : (Settings ? Settings->DefaultIKRetargeter : FSoftObjectPath());
|
||||||
|
RefreshFollowPoseMode();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool UPHYCharacterMeshBridgeComponent::ApplyPlayerClassVisualFromClassComponent(const UPHYClassComponent* ClassComponent)
|
||||||
|
{
|
||||||
|
if (!ClassComponent)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ClassComponent->IsPlayerClassMeshAllowed())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
USkeletalMesh* PlayerMesh = ClassComponent->GetConfiguredPlayerMesh();
|
||||||
|
const FSoftObjectPath PlayerRetargeter = ClassComponent->GetConfiguredPlayerRetargeter();
|
||||||
|
return PlayerMesh ? ApplyDisplayMesh(PlayerMesh, nullptr, PlayerRetargeter) : false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void UPHYCharacterMeshBridgeComponent::RefreshFollowPoseMode()
|
||||||
|
{
|
||||||
|
if (!SourceMeshComponent || !DisplayMeshComponent)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (CanUseLeaderPose())
|
||||||
|
{
|
||||||
|
DisplayMeshComponent->SetLeaderPoseComponent(SourceMeshComponent);
|
||||||
|
DisplayMeshComponent->SetAnimationMode(EAnimationMode::AnimationBlueprint);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
DisplayMeshComponent->SetLeaderPoseComponent(nullptr);
|
||||||
|
|
||||||
|
ConfigureRetargetFollowPose();
|
||||||
|
}
|
||||||
|
|
||||||
|
USkeletalMeshComponent* UPHYCharacterMeshBridgeComponent::GetVisualMeshComponentForSocket(const FName SocketName) const
|
||||||
|
{
|
||||||
|
if (!SocketName.IsNone() && DisplayMeshComponent && DisplayMeshComponent->DoesSocketExist(SocketName))
|
||||||
|
{
|
||||||
|
return DisplayMeshComponent;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!SocketName.IsNone() && SourceMeshComponent && SourceMeshComponent->DoesSocketExist(SocketName))
|
||||||
|
{
|
||||||
|
return SourceMeshComponent;
|
||||||
|
}
|
||||||
|
|
||||||
|
return DisplayMeshComponent ? DisplayMeshComponent : SourceMeshComponent;
|
||||||
|
}
|
||||||
|
|
||||||
|
void UPHYCharacterMeshBridgeComponent::ApplySourceDefaults()
|
||||||
|
{
|
||||||
|
if (!SourceMeshComponent)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const UPHYAnimationSettings* Settings = GetDefault<UPHYAnimationSettings>();
|
||||||
|
if (!Settings)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!SourceMeshComponent->GetSkeletalMeshAsset() && !Settings->DefaultSourceLocomotionMesh.IsNull())
|
||||||
|
{
|
||||||
|
if (USkeletalMesh* DefaultSourceMesh = Settings->DefaultSourceLocomotionMesh.LoadSynchronous())
|
||||||
|
{
|
||||||
|
SourceMeshComponent->SetSkeletalMesh(DefaultSourceMesh);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Settings->DefaultSourceAnimClass.IsNull())
|
||||||
|
{
|
||||||
|
if (UClass* DefaultSourceAnimClass = Settings->DefaultSourceAnimClass.LoadSynchronous())
|
||||||
|
{
|
||||||
|
SourceMeshComponent->SetAnimationMode(EAnimationMode::AnimationBlueprint);
|
||||||
|
SourceMeshComponent->SetAnimInstanceClass(DefaultSourceAnimClass);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool UPHYCharacterMeshBridgeComponent::CanUseLeaderPose() const
|
||||||
|
{
|
||||||
|
const UPHYAnimationSettings* Settings = GetDefault<UPHYAnimationSettings>();
|
||||||
|
if (Settings && !Settings->bPreferLeaderPoseForSameSkeleton)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const USkeletalMesh* SourceMesh = SourceMeshComponent ? SourceMeshComponent->GetSkeletalMeshAsset() : nullptr;
|
||||||
|
const USkeletalMesh* DisplayMesh = DisplayMeshComponent ? DisplayMeshComponent->GetSkeletalMeshAsset() : nullptr;
|
||||||
|
return SourceMesh && DisplayMesh && SourceMesh->GetSkeleton() && SourceMesh->GetSkeleton() == DisplayMesh->GetSkeleton();
|
||||||
|
}
|
||||||
|
|
||||||
|
void UPHYCharacterMeshBridgeComponent::ConfigureRetargetFollowPose()
|
||||||
|
{
|
||||||
|
if (!SourceMeshComponent || !DisplayMeshComponent)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!DisplayMeshComponent->GetSkeletalMeshAsset())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!SourceMeshComponent->IsRegistered() || !DisplayMeshComponent->IsRegistered())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
TSubclassOf<UAnimInstance> RetargetAnimClass = ResolveRetargetDisplayAnimClass();
|
||||||
|
if (!RetargetAnimClass)
|
||||||
|
{
|
||||||
|
UE_LOG(LogTemp, Warning, TEXT("PHYCharacterMeshBridgeComponent: DisplayMesh %s has no retarget AnimClass."), *GetNameSafe(DisplayMeshComponent));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!RetargetAnimClass->IsChildOf(UPHYRetargetPoseAnimInstance::StaticClass()))
|
||||||
|
{
|
||||||
|
UE_LOG(LogTemp, Warning, TEXT("PHYCharacterMeshBridgeComponent: Display AnimClass %s is not based on UPHYRetargetPoseAnimInstance; falling back to native retarget instance."), *GetNameSafe(RetargetAnimClass.Get()));
|
||||||
|
RetargetAnimClass = UPHYRetargetPoseAnimInstance::StaticClass();
|
||||||
|
}
|
||||||
|
|
||||||
|
DisplayMeshComponent->SetAnimationMode(EAnimationMode::AnimationBlueprint);
|
||||||
|
if (DisplayMeshComponent->GetAnimClass() != RetargetAnimClass.Get())
|
||||||
|
{
|
||||||
|
DisplayMeshComponent->SetAnimInstanceClass(RetargetAnimClass);
|
||||||
|
}
|
||||||
|
|
||||||
|
DisplayMeshComponent->InitAnim(true);
|
||||||
|
|
||||||
|
if (!CurrentRetargeter.IsValid())
|
||||||
|
{
|
||||||
|
UE_LOG(LogTemp, Warning, TEXT("PHYCharacterMeshBridgeComponent: DisplayMesh %s needs an IK Retargeter for cross-skeleton follow pose."), *GetNameSafe(DisplayMeshComponent));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
UPHYRetargetPoseAnimInstance* RetargetAnimInstance = Cast<UPHYRetargetPoseAnimInstance>(DisplayMeshComponent->GetAnimInstance());
|
||||||
|
if (!RetargetAnimInstance)
|
||||||
|
{
|
||||||
|
UE_LOG(LogTemp, Warning, TEXT("PHYCharacterMeshBridgeComponent: DisplayMesh %s did not create a UPHYRetargetPoseAnimInstance. AnimClass=%s AnimInstance=%s"),
|
||||||
|
*GetNameSafe(DisplayMeshComponent),
|
||||||
|
*GetNameSafe(DisplayMeshComponent->GetAnimClass()),
|
||||||
|
*GetNameSafe(DisplayMeshComponent->GetAnimInstance()));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
RetargetAnimInstance->ConfigureRetargetPoseFromMesh(SourceMeshComponent, CurrentRetargeter);
|
||||||
|
}
|
||||||
|
|
||||||
|
TSubclassOf<UAnimInstance> UPHYCharacterMeshBridgeComponent::ResolveRetargetDisplayAnimClass() const
|
||||||
|
{
|
||||||
|
if (!DisplayMeshComponent)
|
||||||
|
{
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (UClass* CurrentAnimClass = DisplayMeshComponent->GetAnimClass())
|
||||||
|
{
|
||||||
|
if (CurrentAnimClass != UPHYRetargetPoseAnimInstance::StaticClass())
|
||||||
|
{
|
||||||
|
return CurrentAnimClass;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const UPHYAnimationSettings* Settings = GetDefault<UPHYAnimationSettings>();
|
||||||
|
if (Settings && !Settings->DefaultDisplayAnimClass.IsNull())
|
||||||
|
{
|
||||||
|
if (UClass* DefaultDisplayAnimClass = Settings->DefaultDisplayAnimClass.LoadSynchronous())
|
||||||
|
{
|
||||||
|
return DefaultDisplayAnimClass;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return UPHYRetargetPoseAnimInstance::StaticClass();
|
||||||
|
}
|
||||||
38
Source/PHY/Private/Animation/PHYRetargetPoseAnimInstance.cpp
Normal file
38
Source/PHY/Private/Animation/PHYRetargetPoseAnimInstance.cpp
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||||
|
|
||||||
|
#include "Animation/PHYRetargetPoseAnimInstance.h"
|
||||||
|
|
||||||
|
#include UE_INLINE_GENERATED_CPP_BY_NAME(PHYRetargetPoseAnimInstance)
|
||||||
|
|
||||||
|
#include "Components/SkeletalMeshComponent.h"
|
||||||
|
#include "Retargeter/IKRetargeter.h"
|
||||||
|
|
||||||
|
bool UPHYRetargetPoseAnimInstance::ConfigureRetargetPoseFromMesh(USkeletalMeshComponent* InSourceMeshComponent, const FSoftObjectPath InRetargeterPath)
|
||||||
|
{
|
||||||
|
RetargetSourceMeshComponent = InSourceMeshComponent;
|
||||||
|
RetargeterPath = InRetargeterPath;
|
||||||
|
LoadedRetargeter = nullptr;
|
||||||
|
|
||||||
|
if (!RetargetSourceMeshComponent)
|
||||||
|
{
|
||||||
|
UE_LOG(LogTemp, Warning, TEXT("PHYRetargetPoseAnimInstance: SourceMeshComponent is missing."));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!RetargeterPath.IsValid())
|
||||||
|
{
|
||||||
|
UE_LOG(LogTemp, Warning, TEXT("PHYRetargetPoseAnimInstance: IK Retargeter path is missing for %s."), *GetNameSafe(GetOwningComponent()));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
UObject* LoadedObject = RetargeterPath.TryLoad();
|
||||||
|
LoadedRetargeter = Cast<UIKRetargeter>(LoadedObject);
|
||||||
|
if (!LoadedRetargeter)
|
||||||
|
{
|
||||||
|
UE_LOG(LogTemp, Warning, TEXT("PHYRetargetPoseAnimInstance: %s is not a valid UIKRetargeter asset."), *RetargeterPath.ToString());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// AnimGraph 中的 Retarget Pose From Mesh 节点通过属性绑定读取 LoadedRetargeter。
|
||||||
|
return true;
|
||||||
|
}
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
||||||
|
|
||||||
#include "Camera/PHYUGCSpringArmComponent.h"
|
|
||||||
|
|
||||||
#include UE_INLINE_GENERATED_CPP_BY_NAME(PHYUGCSpringArmComponent)
|
|
||||||
|
|
||||||
// 当前包装层只解除抽象限制,后续相机专家可在这里集中扩展 UGC 项目逻辑。
|
|
||||||
@@ -4,14 +4,30 @@
|
|||||||
|
|
||||||
#include UE_INLINE_GENERATED_CPP_BY_NAME(PHYAICharacter)
|
#include UE_INLINE_GENERATED_CPP_BY_NAME(PHYAICharacter)
|
||||||
|
|
||||||
|
#include "AbilitySystem/Attributes/PHYCombatAttributeSet.h"
|
||||||
|
#include "AbilitySystem/Attributes/PHYCoreAttributeSet.h"
|
||||||
|
#include "AbilitySystem/Attributes/PHYElementAttributeSet.h"
|
||||||
|
#include "Class/PHYClassComponent.h"
|
||||||
|
#include "Class/PHYClassSettings.h"
|
||||||
#include "GGA_AbilitySystemComponent.h"
|
#include "GGA_AbilitySystemComponent.h"
|
||||||
|
#include "AI/PHYAIController.h"
|
||||||
|
|
||||||
APHYAICharacter::APHYAICharacter(const FObjectInitializer& ObjectInitializer)
|
APHYAICharacter::APHYAICharacter(const FObjectInitializer& ObjectInitializer)
|
||||||
: Super(ObjectInitializer)
|
: Super(ObjectInitializer)
|
||||||
{
|
{
|
||||||
|
AIControllerClass = APHYAIController::StaticClass();
|
||||||
|
AutoPossessAI = EAutoPossessAI::PlacedInWorldOrSpawned;
|
||||||
|
|
||||||
AbilitySystemComponent = CreateDefaultSubobject<UGGA_AbilitySystemComponent>(TEXT("AbilitySystemComponent"));
|
AbilitySystemComponent = CreateDefaultSubobject<UGGA_AbilitySystemComponent>(TEXT("AbilitySystemComponent"));
|
||||||
AbilitySystemComponent->SetIsReplicated(true);
|
AbilitySystemComponent->SetIsReplicated(true);
|
||||||
AbilitySystemComponent->SetReplicationMode(EGameplayEffectReplicationMode::Mixed);
|
AbilitySystemComponent->SetReplicationMode(EGameplayEffectReplicationMode::Mixed);
|
||||||
|
|
||||||
|
CoreAttributeSet = CreateDefaultSubobject<UPHYCoreAttributeSet>(TEXT("CoreAttributeSet"));
|
||||||
|
CombatAttributeSet = CreateDefaultSubobject<UPHYCombatAttributeSet>(TEXT("CombatAttributeSet"));
|
||||||
|
ElementAttributeSet = CreateDefaultSubobject<UPHYElementAttributeSet>(TEXT("ElementAttributeSet"));
|
||||||
|
|
||||||
|
ClassComponent = CreateDefaultSubobject<UPHYClassComponent>(TEXT("ClassComponent"));
|
||||||
|
ClassComponent->SetClassTag(GetDefault<UPHYClassSettings>()->DefaultAIClassTag);
|
||||||
}
|
}
|
||||||
|
|
||||||
void APHYAICharacter::BeginPlay()
|
void APHYAICharacter::BeginPlay()
|
||||||
@@ -33,5 +49,11 @@ void APHYAICharacter::InitializeAIAbilitySystem()
|
|||||||
if (AbilitySystemComponent)
|
if (AbilitySystemComponent)
|
||||||
{
|
{
|
||||||
InitializeAbilitySystem(AbilitySystemComponent, this);
|
InitializeAbilitySystem(AbilitySystemComponent, this);
|
||||||
|
|
||||||
|
if (ClassComponent)
|
||||||
|
{
|
||||||
|
ClassComponent->ApplyClassAttributesToAbilitySystem(AbilitySystemComponent);
|
||||||
|
ClassComponent->ApplyConfiguredPlayerMeshIfAllowed(this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
#include UE_INLINE_GENERATED_CPP_BY_NAME(PHYCharacterBase)
|
#include UE_INLINE_GENERATED_CPP_BY_NAME(PHYCharacterBase)
|
||||||
|
|
||||||
#include "AbilitySystemComponent.h"
|
#include "AbilitySystemComponent.h"
|
||||||
|
#include "Animation/PHYCharacterMeshBridgeComponent.h"
|
||||||
#include "Camera/CameraComponent.h"
|
#include "Camera/CameraComponent.h"
|
||||||
#include "Characters/PHYCharacterSettings.h"
|
#include "Characters/PHYCharacterSettings.h"
|
||||||
#include "Characters/PHYCharacterStateComponent.h"
|
#include "Characters/PHYCharacterStateComponent.h"
|
||||||
@@ -17,16 +18,45 @@
|
|||||||
#include "GameFramework/CharacterMovementComponent.h"
|
#include "GameFramework/CharacterMovementComponent.h"
|
||||||
#include "Interaction/GGS_InteractionSystemComponent.h"
|
#include "Interaction/GGS_InteractionSystemComponent.h"
|
||||||
#include "Net/UnrealNetwork.h"
|
#include "Net/UnrealNetwork.h"
|
||||||
|
#include "PHYConfigSettings.h"
|
||||||
#include "PHYGameplayTags.h"
|
#include "PHYGameplayTags.h"
|
||||||
#include "Ragdoll/GGS_RagdollComponent.h"
|
#include "Ragdoll/GGS_RagdollComponent.h"
|
||||||
|
#include "Components/SLSCharacterMovementComponent.h"
|
||||||
|
#include "SLSIntegrationComponent.h"
|
||||||
|
#include "SLSIntegrationDataAsset.h"
|
||||||
#include "Targeting/GCS_TargetingSystemComponent.h"
|
#include "Targeting/GCS_TargetingSystemComponent.h"
|
||||||
#include "Team/GCS_CombatTeamAgentComponent.h"
|
#include "Team/GCS_CombatTeamAgentComponent.h"
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
ESLSRotationMode GetSLSRotationMode(const FGameplayTag NewRotationMode)
|
||||||
|
{
|
||||||
|
if (NewRotationMode == PHYGameplayTags::State_Rotation_Looking)
|
||||||
|
{
|
||||||
|
return ESLSRotationMode::LookingDirection;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (NewRotationMode == PHYGameplayTags::State_Rotation_Aiming)
|
||||||
|
{
|
||||||
|
return ESLSRotationMode::AimingDirection;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ESLSRotationMode::VelocityDirection;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
APHYCharacterBase::APHYCharacterBase(const FObjectInitializer& ObjectInitializer)
|
APHYCharacterBase::APHYCharacterBase(const FObjectInitializer& ObjectInitializer)
|
||||||
: Super(ObjectInitializer)
|
: Super(ObjectInitializer.SetDefaultSubobjectClass<USLSCharacterMovementComponent>(ACharacter::CharacterMovementComponentName))
|
||||||
{
|
{
|
||||||
bReplicates = true;
|
bReplicates = true;
|
||||||
|
|
||||||
|
const UPHYCharacterSettings* CharacterSettings = GetDefault<UPHYCharacterSettings>();
|
||||||
|
if (USkeletalMeshComponent* SourceMeshComponent = GetMesh())
|
||||||
|
{
|
||||||
|
SourceMeshComponent->SetRelativeLocation(CharacterSettings ? CharacterSettings->DefaultMeshRelativeLocation : FVector(0.0f, 0.0f, -90.0f));
|
||||||
|
SourceMeshComponent->SetRelativeRotation(CharacterSettings ? CharacterSettings->DefaultMeshRelativeRotation : FRotator(0.0f, -90.0f, 0.0f));
|
||||||
|
}
|
||||||
|
|
||||||
CharacterStateComponent = CreateDefaultSubobject<UPHYCharacterStateComponent>(TEXT("CharacterStateComponent"));
|
CharacterStateComponent = CreateDefaultSubobject<UPHYCharacterStateComponent>(TEXT("CharacterStateComponent"));
|
||||||
CombatSystemComponent = CreateDefaultSubobject<UGCS_CombatSystemComponent>(TEXT("CombatSystemComponent"));
|
CombatSystemComponent = CreateDefaultSubobject<UGCS_CombatSystemComponent>(TEXT("CombatSystemComponent"));
|
||||||
TargetingSystemComponent = CreateDefaultSubobject<UGCS_TargetingSystemComponent>(TEXT("TargetingSystemComponent"));
|
TargetingSystemComponent = CreateDefaultSubobject<UGCS_TargetingSystemComponent>(TEXT("TargetingSystemComponent"));
|
||||||
@@ -34,6 +64,50 @@ APHYCharacterBase::APHYCharacterBase(const FObjectInitializer& ObjectInitializer
|
|||||||
InteractionSystemComponent = CreateDefaultSubobject<UGGS_InteractionSystemComponent>(TEXT("InteractionSystemComponent"));
|
InteractionSystemComponent = CreateDefaultSubobject<UGGS_InteractionSystemComponent>(TEXT("InteractionSystemComponent"));
|
||||||
RagdollComponent = CreateDefaultSubobject<UGGS_RagdollComponent>(TEXT("RagdollComponent"));
|
RagdollComponent = CreateDefaultSubobject<UGGS_RagdollComponent>(TEXT("RagdollComponent"));
|
||||||
ContextEffectComponent = CreateDefaultSubobject<UGES_ContextEffectComponent>(TEXT("ContextEffectComponent"));
|
ContextEffectComponent = CreateDefaultSubobject<UGES_ContextEffectComponent>(TEXT("ContextEffectComponent"));
|
||||||
|
MeshBridgeComponent = CreateDefaultSubobject<UPHYCharacterMeshBridgeComponent>(TEXT("MeshBridgeComponent"));
|
||||||
|
SLSIntegrationComponent = CreateDefaultSubobject<USLSIntegrationComponent>(TEXT("SLSIntegrationComponent"));
|
||||||
|
|
||||||
|
USLSIntegrationDataAsset* SLSIntegrationRuntimeDataAsset = CreateDefaultSubobject<USLSIntegrationDataAsset>(TEXT("SLSIntegrationRuntimeDataAsset"));
|
||||||
|
if (SLSIntegrationComponent && SLSIntegrationRuntimeDataAsset)
|
||||||
|
{
|
||||||
|
FSLSBaseCharacterMovementSettingsOp* MovementSettingsOp = SLSIntegrationRuntimeDataAsset->GetFirstSLSDataOpOfType<FSLSBaseCharacterMovementSettingsOp>();
|
||||||
|
if (!MovementSettingsOp)
|
||||||
|
{
|
||||||
|
MovementSettingsOp = SLSIntegrationRuntimeDataAsset->AddAndGetSLSDataOp<FSLSBaseCharacterMovementSettingsOp>();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (MovementSettingsOp)
|
||||||
|
{
|
||||||
|
if (FSLSBaseCharacterMovementSettings* MovementSettings = static_cast<FSLSBaseCharacterMovementSettings*>(MovementSettingsOp->GetSettings()))
|
||||||
|
{
|
||||||
|
MovementSettings->bAddSkeletalBoneSyncComponent = false;
|
||||||
|
MovementSettings->Config.InitComponentType = ESLSComponentInitType::ManualInit;
|
||||||
|
MovementSettings->Config.MovementType = ESLSMovementType::Jog;
|
||||||
|
MovementSettings->Config.RotationMode = ESLSRotationMode::VelocityDirection;
|
||||||
|
MovementSettings->Config.bEnableSprintInLookingDirectionRotationMode = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SLSIntegrationRuntimeDataAsset->InitializeData();
|
||||||
|
SLSIntegrationComponent->SLSIntegrationDataAsset = SLSIntegrationRuntimeDataAsset;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (UClass* GenericIntegrationClass = FindObject<UClass>(nullptr, TEXT("/Script/GenericGameSystem.GenericIntegrationComponent")))
|
||||||
|
{
|
||||||
|
GenericIntegrationComponent = Cast<UActorComponent>(CreateDefaultSubobject(
|
||||||
|
TEXT("GenericIntegrationComponent"),
|
||||||
|
UActorComponent::StaticClass(),
|
||||||
|
GenericIntegrationClass,
|
||||||
|
false,
|
||||||
|
false));
|
||||||
|
}
|
||||||
|
|
||||||
|
DisplayMeshComponent = CreateDefaultSubobject<USkeletalMeshComponent>(TEXT("DisplayMeshComponent"));
|
||||||
|
DisplayMeshComponent->SetupAttachment(GetMesh());
|
||||||
|
DisplayMeshComponent->SetRelativeLocation(FVector::ZeroVector);
|
||||||
|
DisplayMeshComponent->SetRelativeRotation(FRotator::ZeroRotator);
|
||||||
|
DisplayMeshComponent->SetCollisionEnabled(ECollisionEnabled::NoCollision);
|
||||||
|
DisplayMeshComponent->SetGenerateOverlapEvents(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void APHYCharacterBase::PreInitializeComponents()
|
void APHYCharacterBase::PreInitializeComponents()
|
||||||
@@ -43,6 +117,16 @@ void APHYCharacterBase::PreInitializeComponents()
|
|||||||
UGameFrameworkComponentManager::AddGameFrameworkComponentReceiver(this);
|
UGameFrameworkComponentManager::AddGameFrameworkComponentReceiver(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void APHYCharacterBase::PostInitializeComponents()
|
||||||
|
{
|
||||||
|
Super::PostInitializeComponents();
|
||||||
|
|
||||||
|
if (MeshBridgeComponent)
|
||||||
|
{
|
||||||
|
MeshBridgeComponent->InitializeMeshBridge(this, DisplayMeshComponent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void APHYCharacterBase::BeginPlay()
|
void APHYCharacterBase::BeginPlay()
|
||||||
{
|
{
|
||||||
Super::BeginPlay();
|
Super::BeginPlay();
|
||||||
@@ -59,6 +143,22 @@ void APHYCharacterBase::BeginPlay()
|
|||||||
ContextEffectComponent->SetGameplayTagsProvider(this);
|
ContextEffectComponent->SetGameplayTagsProvider(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (const UPHYLocomotionSettings* LocomotionSettings = GetDefault<UPHYLocomotionSettings>(); LocomotionSettings && LocomotionSettings->bUseSmoothLocomotionSystem)
|
||||||
|
{
|
||||||
|
if (USLSCharacterMovementComponent* SLSMovementComponent = GetSLSCharacterMovementComponent())
|
||||||
|
{
|
||||||
|
const UPHYCharacterSettings* CharacterSettings = GetDefault<UPHYCharacterSettings>();
|
||||||
|
SLSMovementComponent->ApplyBaseSLSCharacterMovementSettings();
|
||||||
|
SLSMovementComponent->MaxWalkSpeed = CharacterSettings ? CharacterSettings->DefaultMaxWalkSpeed : SLSMovementComponent->MaxWalkSpeed;
|
||||||
|
SLSMovementComponent->MaxAcceleration = CharacterSettings ? CharacterSettings->DefaultMaxAcceleration : SLSMovementComponent->MaxAcceleration;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (CharacterStateComponent)
|
||||||
|
{
|
||||||
|
SetRotationMode_Implementation(CharacterStateComponent->GetRotationMode());
|
||||||
|
}
|
||||||
|
|
||||||
UGameFrameworkComponentManager::SendGameFrameworkComponentExtensionEvent(this, UGameFrameworkComponentManager::NAME_GameActorReady);
|
UGameFrameworkComponentManager::SendGameFrameworkComponentExtensionEvent(this, UGameFrameworkComponentManager::NAME_GameActorReady);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -170,6 +270,11 @@ bool APHYCharacterBase::HandleInputTag(const FGameplayTag InputTag, const ETrigg
|
|||||||
|
|
||||||
if (InputTag == PHYGameplayTags::Input_Sprint)
|
if (InputTag == PHYGameplayTags::Input_Sprint)
|
||||||
{
|
{
|
||||||
|
if (USLSCharacterMovementComponent* SLSMovementComponent = GetSLSCharacterMovementComponent())
|
||||||
|
{
|
||||||
|
SLSMovementComponent->UpdateMovementType(bPressed ? ESLSMovementType::Sprint : ESLSMovementType::Jog);
|
||||||
|
}
|
||||||
|
|
||||||
if (CharacterStateComponent)
|
if (CharacterStateComponent)
|
||||||
{
|
{
|
||||||
CharacterStateComponent->SetMovementState(bPressed ? PHYGameplayTags::State_Movement_Sprint : PHYGameplayTags::State_Movement_Run);
|
CharacterStateComponent->SetMovementState(bPressed ? PHYGameplayTags::State_Movement_Sprint : PHYGameplayTags::State_Movement_Run);
|
||||||
@@ -186,10 +291,10 @@ bool APHYCharacterBase::HandleInputTag(const FGameplayTag InputTag, const ETrigg
|
|||||||
|
|
||||||
if (InputTag == PHYGameplayTags::Input_Aim)
|
if (InputTag == PHYGameplayTags::Input_Aim)
|
||||||
{
|
{
|
||||||
|
SetRotationMode_Implementation(bPressed ? PHYGameplayTags::State_Rotation_Aiming : PHYGameplayTags::State_Rotation_OrientToMovement);
|
||||||
if (CharacterStateComponent)
|
if (CharacterStateComponent)
|
||||||
{
|
{
|
||||||
CharacterStateComponent->SetMovementSet(bPressed ? PHYGameplayTags::State_MovementSet_Aiming : PHYGameplayTags::State_MovementSet_Default);
|
CharacterStateComponent->SetMovementSet(bPressed ? PHYGameplayTags::State_MovementSet_Aiming : PHYGameplayTags::State_MovementSet_Default);
|
||||||
CharacterStateComponent->SetRotationMode(bPressed ? PHYGameplayTags::State_Rotation_Strafe : PHYGameplayTags::State_Rotation_OrientToMovement);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@@ -230,6 +335,11 @@ bool APHYCharacterBase::HandleInputTag(const FGameplayTag InputTag, const ETrigg
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
USLSCharacterMovementComponent* APHYCharacterBase::GetSLSCharacterMovementComponent() const
|
||||||
|
{
|
||||||
|
return Cast<USLSCharacterMovementComponent>(GetCharacterMovement());
|
||||||
|
}
|
||||||
|
|
||||||
void APHYCharacterBase::SetMovementIntent(const FVector NewMovementIntent)
|
void APHYCharacterBase::SetMovementIntent(const FVector NewMovementIntent)
|
||||||
{
|
{
|
||||||
const FVector ClampedIntent = NewMovementIntent.GetClampedToMaxSize(1.0f);
|
const FVector ClampedIntent = NewMovementIntent.GetClampedToMaxSize(1.0f);
|
||||||
@@ -397,6 +507,11 @@ void APHYCharacterBase::SetRotationMode_Implementation(const FGameplayTag NewRot
|
|||||||
{
|
{
|
||||||
CharacterStateComponent->SetRotationMode(NewRotationMode);
|
CharacterStateComponent->SetRotationMode(NewRotationMode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (USLSCharacterMovementComponent* SLSMovementComponent = GetSLSCharacterMovementComponent())
|
||||||
|
{
|
||||||
|
SLSMovementComponent->UpdateRotationMode(GetSLSRotationMode(NewRotationMode));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
FGameplayTag APHYCharacterBase::GetRotationMode_Implementation() const
|
FGameplayTag APHYCharacterBase::GetRotationMode_Implementation() const
|
||||||
@@ -540,6 +655,12 @@ void APHYCharacterBase::ApplyCharacterSettings()
|
|||||||
MoveComponent->MaxAcceleration = Settings->DefaultMaxAcceleration;
|
MoveComponent->MaxAcceleration = Settings->DefaultMaxAcceleration;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (USkeletalMeshComponent* SourceMeshComponent = GetMesh())
|
||||||
|
{
|
||||||
|
SourceMeshComponent->SetRelativeLocation(Settings->DefaultMeshRelativeLocation);
|
||||||
|
SourceMeshComponent->SetRelativeRotation(Settings->DefaultMeshRelativeRotation);
|
||||||
|
}
|
||||||
|
|
||||||
if (CharacterStateComponent && GetCharacterMovement() && GetCharacterMovement()->IsFalling())
|
if (CharacterStateComponent && GetCharacterMovement() && GetCharacterMovement()->IsFalling())
|
||||||
{
|
{
|
||||||
CharacterStateComponent->SetMovementState(PHYGameplayTags::State_Movement_Falling);
|
CharacterStateComponent->SetMovementState(PHYGameplayTags::State_Movement_Falling);
|
||||||
|
|||||||
@@ -5,14 +5,17 @@
|
|||||||
#include UE_INLINE_GENERATED_CPP_BY_NAME(PHYPlayerCharacter)
|
#include UE_INLINE_GENERATED_CPP_BY_NAME(PHYPlayerCharacter)
|
||||||
|
|
||||||
#include "Camera/CameraComponent.h"
|
#include "Camera/CameraComponent.h"
|
||||||
#include "Camera/PHYUGCSpringArmComponent.h"
|
#include "Class/PHYClassComponent.h"
|
||||||
#include "GameFramework/SpringArmComponent.h"
|
#include "GameFramework/SpringArmComponent.h"
|
||||||
|
#include "GIPS_InputSystemComponent.h"
|
||||||
#include "Player/PHYPlayerState.h"
|
#include "Player/PHYPlayerState.h"
|
||||||
|
|
||||||
APHYPlayerCharacter::APHYPlayerCharacter(const FObjectInitializer& ObjectInitializer)
|
APHYPlayerCharacter::APHYPlayerCharacter(const FObjectInitializer& ObjectInitializer)
|
||||||
: Super(ObjectInitializer)
|
: Super(ObjectInitializer)
|
||||||
{
|
{
|
||||||
CameraBoom = CreateDefaultSubobject<UPHYUGCSpringArmComponent>(TEXT("CameraBoom"));
|
InputSystemComponent = CreateDefaultSubobject<UGIPS_InputSystemComponent>(TEXT("InputSystemComponent"));
|
||||||
|
|
||||||
|
CameraBoom = CreateDefaultSubobject<USpringArmComponent>(TEXT("CameraBoom"));
|
||||||
CameraBoom->SetupAttachment(GetRootComponent());
|
CameraBoom->SetupAttachment(GetRootComponent());
|
||||||
CameraBoom->TargetArmLength = 320.0f;
|
CameraBoom->TargetArmLength = 320.0f;
|
||||||
CameraBoom->bUsePawnControlRotation = true;
|
CameraBoom->bUsePawnControlRotation = true;
|
||||||
@@ -47,5 +50,11 @@ void APHYPlayerCharacter::InitializeAbilitySystemFromPlayerState()
|
|||||||
if (UAbilitySystemComponent* PlayerASC = PHYPlayerState->GetAbilitySystemComponent())
|
if (UAbilitySystemComponent* PlayerASC = PHYPlayerState->GetAbilitySystemComponent())
|
||||||
{
|
{
|
||||||
InitializeAbilitySystem(PlayerASC, PHYPlayerState);
|
InitializeAbilitySystem(PlayerASC, PHYPlayerState);
|
||||||
|
|
||||||
|
if (UPHYClassComponent* ClassComponent = PHYPlayerState->GetClassComponent())
|
||||||
|
{
|
||||||
|
ClassComponent->ApplyClassAttributesToAbilitySystem(PlayerASC);
|
||||||
|
ClassComponent->ApplyConfiguredPlayerMeshIfAllowed(this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
264
Source/PHY/Private/Class/PHYClassComponent.cpp
Normal file
264
Source/PHY/Private/Class/PHYClassComponent.cpp
Normal file
@@ -0,0 +1,264 @@
|
|||||||
|
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||||
|
|
||||||
|
#include "Class/PHYClassComponent.h"
|
||||||
|
|
||||||
|
#include UE_INLINE_GENERATED_CPP_BY_NAME(PHYClassComponent)
|
||||||
|
|
||||||
|
#include "AbilitySystemComponent.h"
|
||||||
|
#include "Animation/AnimInstance.h"
|
||||||
|
#include "AbilitySystem/Attributes/PHYAttributeCalculationLibrary.h"
|
||||||
|
#include "AbilitySystem/Attributes/PHYCombatAttributeSet.h"
|
||||||
|
#include "AbilitySystem/Attributes/PHYCoreAttributeSet.h"
|
||||||
|
#include "AbilitySystem/Attributes/PHYElementAttributeSet.h"
|
||||||
|
#include "Animation/PHYCharacterMeshBridgeComponent.h"
|
||||||
|
#include "Characters/PHYAICharacter.h"
|
||||||
|
#include "Characters/PHYCharacterBase.h"
|
||||||
|
#include "Characters/PHYPlayerCharacter.h"
|
||||||
|
#include "Class/PHYClassSettings.h"
|
||||||
|
#include "Net/UnrealNetwork.h"
|
||||||
|
#include "PHYGameplayTags.h"
|
||||||
|
#include "Player/PHYPlayerState.h"
|
||||||
|
|
||||||
|
UPHYClassComponent::UPHYClassComponent(const FObjectInitializer& ObjectInitializer)
|
||||||
|
: Super(ObjectInitializer)
|
||||||
|
{
|
||||||
|
SetIsReplicatedByDefault(true);
|
||||||
|
|
||||||
|
CurrentClassTag = PHYGameplayTags::Class_Saber;
|
||||||
|
}
|
||||||
|
|
||||||
|
void UPHYClassComponent::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
|
||||||
|
{
|
||||||
|
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
|
||||||
|
|
||||||
|
DOREPLIFETIME(ThisClass, CurrentClassTag);
|
||||||
|
}
|
||||||
|
|
||||||
|
void UPHYClassComponent::SetClassTag(const FGameplayTag NewClassTag)
|
||||||
|
{
|
||||||
|
if (!NewClassTag.IsValid())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (AActor* OwnerActor = GetOwner())
|
||||||
|
{
|
||||||
|
if (!OwnerActor->HasAuthority())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CurrentClassTag = NewClassTag;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool UPHYClassComponent::IsPlayerClassMeshAllowed() const
|
||||||
|
{
|
||||||
|
const UPHYClassSettings* ClassSettings = GetDefault<UPHYClassSettings>();
|
||||||
|
if (!ClassSettings)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const AActor* OwnerActor = GetOwner();
|
||||||
|
if (OwnerActor && OwnerActor->IsA<APHYAICharacter>())
|
||||||
|
{
|
||||||
|
return ClassSettings->bApplyPlayerClassMeshToAI;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (OwnerActor && (OwnerActor->IsA<APHYPlayerState>() || OwnerActor->IsA<APHYPlayerCharacter>()))
|
||||||
|
{
|
||||||
|
return ClassSettings->bApplyPlayerClassMeshToPlayers;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ClassSettings->bApplyPlayerClassMeshToPlayers;
|
||||||
|
}
|
||||||
|
|
||||||
|
USkeletalMesh* UPHYClassComponent::GetConfiguredPlayerMesh() const
|
||||||
|
{
|
||||||
|
const UPHYClassSettings* ClassSettings = GetDefault<UPHYClassSettings>();
|
||||||
|
if (!ClassSettings || !CurrentClassTag.IsValid())
|
||||||
|
{
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const FPHYPlayerClassMeshConfig& MeshConfig : ClassSettings->PlayerClassMeshes)
|
||||||
|
{
|
||||||
|
if (MeshConfig.ClassTag == CurrentClassTag)
|
||||||
|
{
|
||||||
|
return MeshConfig.PlayerMesh.LoadSynchronous();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
TSubclassOf<UAnimInstance> UPHYClassComponent::GetConfiguredPlayerDisplayAnimClass() const
|
||||||
|
{
|
||||||
|
const UPHYClassSettings* ClassSettings = GetDefault<UPHYClassSettings>();
|
||||||
|
if (!ClassSettings || !CurrentClassTag.IsValid())
|
||||||
|
{
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const FPHYPlayerClassMeshConfig& MeshConfig : ClassSettings->PlayerClassMeshes)
|
||||||
|
{
|
||||||
|
if (MeshConfig.ClassTag == CurrentClassTag)
|
||||||
|
{
|
||||||
|
return MeshConfig.PlayerDisplayAnimClass.LoadSynchronous();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
FSoftObjectPath UPHYClassComponent::GetConfiguredPlayerRetargeter() const
|
||||||
|
{
|
||||||
|
const UPHYClassSettings* ClassSettings = GetDefault<UPHYClassSettings>();
|
||||||
|
if (!ClassSettings || !CurrentClassTag.IsValid())
|
||||||
|
{
|
||||||
|
return FSoftObjectPath();
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const FPHYPlayerClassMeshConfig& MeshConfig : ClassSettings->PlayerClassMeshes)
|
||||||
|
{
|
||||||
|
if (MeshConfig.ClassTag == CurrentClassTag)
|
||||||
|
{
|
||||||
|
return MeshConfig.PlayerRetargeter;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return FSoftObjectPath();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool UPHYClassComponent::ApplyConfiguredPlayerMeshIfAllowed(AActor* AvatarActor) const
|
||||||
|
{
|
||||||
|
if (!IsPlayerClassMeshAllowed())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
AActor* MeshOwner = AvatarActor ? AvatarActor : GetOwner();
|
||||||
|
APHYCharacterBase* Character = Cast<APHYCharacterBase>(MeshOwner);
|
||||||
|
if (!Character)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
USkeletalMesh* PlayerMesh = GetConfiguredPlayerMesh();
|
||||||
|
if (!PlayerMesh)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const FSoftObjectPath PlayerRetargeter = GetConfiguredPlayerRetargeter();
|
||||||
|
UPHYCharacterMeshBridgeComponent* MeshBridge = Character->GetMeshBridgeComponent();
|
||||||
|
if (!MeshBridge)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
MeshBridge->InitializeMeshBridge(Character, Character->GetDisplayMeshComponent());
|
||||||
|
return MeshBridge->ApplyDisplayMesh(PlayerMesh, GetConfiguredPlayerDisplayAnimClass(), PlayerRetargeter);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool UPHYClassComponent::ApplyClassAttributesToAbilitySystem(UAbilitySystemComponent* AbilitySystemComponent) const
|
||||||
|
{
|
||||||
|
if (!AbilitySystemComponent)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const AActor* AuthorityActor = GetOwner() ? GetOwner() : AbilitySystemComponent->GetOwnerActor();
|
||||||
|
if (!AuthorityActor || !AuthorityActor->HasAuthority())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const UPHYClassSettings* ClassSettings = GetDefault<UPHYClassSettings>();
|
||||||
|
if (!ClassSettings)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr int32 RequiredTotalPoints = 60;
|
||||||
|
const FPHYClassCoreAttributeAllocation* Allocation = ClassSettings->FindCoreAttributeAllocation(CurrentClassTag);
|
||||||
|
if (!Allocation || !Allocation->IsValidTotal(RequiredTotalPoints))
|
||||||
|
{
|
||||||
|
UE_LOG(LogTemp, Warning, TEXT("PHYClassComponent: Class %s has no valid %d-point core attribute allocation. Falling back to DefaultPlayerClassTag."),
|
||||||
|
*CurrentClassTag.ToString(), RequiredTotalPoints);
|
||||||
|
Allocation = ClassSettings->FindCoreAttributeAllocation(ClassSettings->DefaultPlayerClassTag);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Allocation || !Allocation->IsValidTotal(RequiredTotalPoints))
|
||||||
|
{
|
||||||
|
UE_LOG(LogTemp, Warning, TEXT("PHYClassComponent: DefaultPlayerClassTag allocation is missing or invalid. Falling back to Class.Saber."));
|
||||||
|
Allocation = ClassSettings->FindCoreAttributeAllocation(PHYGameplayTags::Class_Saber);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Allocation || !Allocation->IsValidTotal(RequiredTotalPoints))
|
||||||
|
{
|
||||||
|
UE_LOG(LogTemp, Warning, TEXT("PHYClassComponent: Unable to apply class attributes because no valid fallback allocation exists."));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
FPHYCoreAttributeSnapshot Snapshot;
|
||||||
|
Snapshot.Strength = Allocation->Strength;
|
||||||
|
Snapshot.Dexterity = Allocation->Dexterity;
|
||||||
|
Snapshot.Vitality = Allocation->Vitality;
|
||||||
|
Snapshot.Intelligence = Allocation->Intelligence;
|
||||||
|
Snapshot.Spirit = Allocation->Spirit;
|
||||||
|
Snapshot.Perception = Allocation->Perception;
|
||||||
|
|
||||||
|
const FPHYDerivedCombatAttributes CombatAttributes = UPHYAttributeCalculationLibrary::CalculateCombatAttributes(Snapshot);
|
||||||
|
const FPHYDerivedElementAttributes ElementAttributes = UPHYAttributeCalculationLibrary::CalculateElementAttributes(Snapshot);
|
||||||
|
|
||||||
|
// 直接设置 ASC 属性基值,重复调用只会覆盖当前职业结果,不会叠加。
|
||||||
|
AbilitySystemComponent->SetNumericAttributeBase(UPHYCoreAttributeSet::GetStrengthAttribute(), Snapshot.Strength);
|
||||||
|
AbilitySystemComponent->SetNumericAttributeBase(UPHYCoreAttributeSet::GetDexterityAttribute(), Snapshot.Dexterity);
|
||||||
|
AbilitySystemComponent->SetNumericAttributeBase(UPHYCoreAttributeSet::GetVitalityAttribute(), Snapshot.Vitality);
|
||||||
|
AbilitySystemComponent->SetNumericAttributeBase(UPHYCoreAttributeSet::GetIntelligenceAttribute(), Snapshot.Intelligence);
|
||||||
|
AbilitySystemComponent->SetNumericAttributeBase(UPHYCoreAttributeSet::GetSpiritAttribute(), Snapshot.Spirit);
|
||||||
|
AbilitySystemComponent->SetNumericAttributeBase(UPHYCoreAttributeSet::GetPerceptionAttribute(), Snapshot.Perception);
|
||||||
|
|
||||||
|
AbilitySystemComponent->SetNumericAttributeBase(UPHYCombatAttributeSet::GetMaxHealthAttribute(), CombatAttributes.MaxHealth);
|
||||||
|
AbilitySystemComponent->SetNumericAttributeBase(UPHYCombatAttributeSet::GetHealthAttribute(), CombatAttributes.MaxHealth);
|
||||||
|
AbilitySystemComponent->SetNumericAttributeBase(UPHYCombatAttributeSet::GetMaxManaAttribute(), CombatAttributes.MaxMana);
|
||||||
|
AbilitySystemComponent->SetNumericAttributeBase(UPHYCombatAttributeSet::GetManaAttribute(), CombatAttributes.MaxMana);
|
||||||
|
AbilitySystemComponent->SetNumericAttributeBase(UPHYCombatAttributeSet::GetMaxStaminaAttribute(), CombatAttributes.MaxStamina);
|
||||||
|
AbilitySystemComponent->SetNumericAttributeBase(UPHYCombatAttributeSet::GetStaminaAttribute(), CombatAttributes.MaxStamina);
|
||||||
|
AbilitySystemComponent->SetNumericAttributeBase(UPHYCombatAttributeSet::GetPhysicalAttackPowerAttribute(), CombatAttributes.PhysicalAttackPower);
|
||||||
|
AbilitySystemComponent->SetNumericAttributeBase(UPHYCombatAttributeSet::GetSpellPowerAttribute(), CombatAttributes.SpellPower);
|
||||||
|
AbilitySystemComponent->SetNumericAttributeBase(UPHYCombatAttributeSet::GetArmorAttribute(), CombatAttributes.Armor);
|
||||||
|
AbilitySystemComponent->SetNumericAttributeBase(UPHYCombatAttributeSet::GetMagicResistanceAttribute(), CombatAttributes.MagicResistance);
|
||||||
|
AbilitySystemComponent->SetNumericAttributeBase(UPHYCombatAttributeSet::GetAccuracyAttribute(), CombatAttributes.Accuracy);
|
||||||
|
AbilitySystemComponent->SetNumericAttributeBase(UPHYCombatAttributeSet::GetEvasionAttribute(), CombatAttributes.Evasion);
|
||||||
|
AbilitySystemComponent->SetNumericAttributeBase(UPHYCombatAttributeSet::GetCriticalChanceAttribute(), CombatAttributes.CriticalChance);
|
||||||
|
AbilitySystemComponent->SetNumericAttributeBase(UPHYCombatAttributeSet::GetCriticalDamageAttribute(), CombatAttributes.CriticalDamage);
|
||||||
|
AbilitySystemComponent->SetNumericAttributeBase(UPHYCombatAttributeSet::GetAttackSpeedAttribute(), CombatAttributes.AttackSpeed);
|
||||||
|
AbilitySystemComponent->SetNumericAttributeBase(UPHYCombatAttributeSet::GetCooldownReductionAttribute(), CombatAttributes.CooldownReduction);
|
||||||
|
AbilitySystemComponent->SetNumericAttributeBase(UPHYCombatAttributeSet::GetBlockPowerAttribute(), CombatAttributes.BlockPower);
|
||||||
|
AbilitySystemComponent->SetNumericAttributeBase(UPHYCombatAttributeSet::GetGuardBreakPowerAttribute(), CombatAttributes.GuardBreakPower);
|
||||||
|
AbilitySystemComponent->SetNumericAttributeBase(UPHYCombatAttributeSet::GetPoiseAttribute(), CombatAttributes.Poise);
|
||||||
|
AbilitySystemComponent->SetNumericAttributeBase(UPHYCombatAttributeSet::GetPoiseDamageAttribute(), CombatAttributes.PoiseDamage);
|
||||||
|
|
||||||
|
AbilitySystemComponent->SetNumericAttributeBase(UPHYElementAttributeSet::GetFireDamageBonusAttribute(), ElementAttributes.DamageBonus);
|
||||||
|
AbilitySystemComponent->SetNumericAttributeBase(UPHYElementAttributeSet::GetWaterDamageBonusAttribute(), ElementAttributes.DamageBonus);
|
||||||
|
AbilitySystemComponent->SetNumericAttributeBase(UPHYElementAttributeSet::GetIceDamageBonusAttribute(), ElementAttributes.DamageBonus);
|
||||||
|
AbilitySystemComponent->SetNumericAttributeBase(UPHYElementAttributeSet::GetLightningDamageBonusAttribute(), ElementAttributes.DamageBonus);
|
||||||
|
AbilitySystemComponent->SetNumericAttributeBase(UPHYElementAttributeSet::GetEarthDamageBonusAttribute(), ElementAttributes.DamageBonus);
|
||||||
|
AbilitySystemComponent->SetNumericAttributeBase(UPHYElementAttributeSet::GetWindDamageBonusAttribute(), ElementAttributes.DamageBonus);
|
||||||
|
AbilitySystemComponent->SetNumericAttributeBase(UPHYElementAttributeSet::GetLightDamageBonusAttribute(), ElementAttributes.DamageBonus);
|
||||||
|
AbilitySystemComponent->SetNumericAttributeBase(UPHYElementAttributeSet::GetDarkDamageBonusAttribute(), ElementAttributes.DamageBonus);
|
||||||
|
AbilitySystemComponent->SetNumericAttributeBase(UPHYElementAttributeSet::GetFireResistanceAttribute(), ElementAttributes.Resistance);
|
||||||
|
AbilitySystemComponent->SetNumericAttributeBase(UPHYElementAttributeSet::GetWaterResistanceAttribute(), ElementAttributes.Resistance);
|
||||||
|
AbilitySystemComponent->SetNumericAttributeBase(UPHYElementAttributeSet::GetIceResistanceAttribute(), ElementAttributes.Resistance);
|
||||||
|
AbilitySystemComponent->SetNumericAttributeBase(UPHYElementAttributeSet::GetLightningResistanceAttribute(), ElementAttributes.Resistance);
|
||||||
|
AbilitySystemComponent->SetNumericAttributeBase(UPHYElementAttributeSet::GetEarthResistanceAttribute(), ElementAttributes.Resistance);
|
||||||
|
AbilitySystemComponent->SetNumericAttributeBase(UPHYElementAttributeSet::GetWindResistanceAttribute(), ElementAttributes.Resistance);
|
||||||
|
AbilitySystemComponent->SetNumericAttributeBase(UPHYElementAttributeSet::GetLightResistanceAttribute(), ElementAttributes.Resistance);
|
||||||
|
AbilitySystemComponent->SetNumericAttributeBase(UPHYElementAttributeSet::GetDarkResistanceAttribute(), ElementAttributes.Resistance);
|
||||||
|
AbilitySystemComponent->SetNumericAttributeBase(UPHYElementAttributeSet::GetElementPenetrationAttribute(), ElementAttributes.Penetration);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
63
Source/PHY/Private/Class/PHYClassSettings.cpp
Normal file
63
Source/PHY/Private/Class/PHYClassSettings.cpp
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||||
|
|
||||||
|
#include "Class/PHYClassSettings.h"
|
||||||
|
|
||||||
|
#include UE_INLINE_GENERATED_CPP_BY_NAME(PHYClassSettings)
|
||||||
|
|
||||||
|
#include "PHYGameplayTags.h"
|
||||||
|
|
||||||
|
int32 FPHYClassCoreAttributeAllocation::GetTotalPoints() const
|
||||||
|
{
|
||||||
|
return Strength + Dexterity + Vitality + Intelligence + Spirit + Perception;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FPHYClassCoreAttributeAllocation::IsValidTotal(const int32 RequiredTotal) const
|
||||||
|
{
|
||||||
|
return GetTotalPoints() == RequiredTotal;
|
||||||
|
}
|
||||||
|
|
||||||
|
UPHYClassSettings::UPHYClassSettings()
|
||||||
|
{
|
||||||
|
DefaultPlayerClassTag = PHYGameplayTags::Class_Saber;
|
||||||
|
DefaultAIClassTag = PHYGameplayTags::Class_Saber;
|
||||||
|
|
||||||
|
PlayerClassMeshes =
|
||||||
|
{
|
||||||
|
{ PHYGameplayTags::Class_Saber, nullptr },
|
||||||
|
{ PHYGameplayTags::Class_Lancer, nullptr },
|
||||||
|
{ PHYGameplayTags::Class_Archer, nullptr },
|
||||||
|
{ PHYGameplayTags::Class_Rider, nullptr },
|
||||||
|
{ PHYGameplayTags::Class_Caster, nullptr },
|
||||||
|
{ PHYGameplayTags::Class_Assassin, nullptr },
|
||||||
|
{ PHYGameplayTags::Class_Berserker, nullptr }
|
||||||
|
};
|
||||||
|
|
||||||
|
CoreAttributeAllocations =
|
||||||
|
{
|
||||||
|
{ PHYGameplayTags::Class_Saber, 12, 10, 12, 8, 8, 10 },
|
||||||
|
{ PHYGameplayTags::Class_Lancer, 10, 13, 10, 7, 8, 12 },
|
||||||
|
{ PHYGameplayTags::Class_Archer, 8, 12, 8, 8, 10, 14 },
|
||||||
|
{ PHYGameplayTags::Class_Rider, 10, 11, 11, 8, 10, 10 },
|
||||||
|
{ PHYGameplayTags::Class_Caster, 5, 7, 8, 16, 15, 9 },
|
||||||
|
{ PHYGameplayTags::Class_Assassin, 7, 15, 7, 8, 8, 15 },
|
||||||
|
{ PHYGameplayTags::Class_Berserker, 16, 8, 15, 4, 5, 12 }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const FPHYClassCoreAttributeAllocation* UPHYClassSettings::FindCoreAttributeAllocation(const FGameplayTag ClassTag) const
|
||||||
|
{
|
||||||
|
if (!ClassTag.IsValid())
|
||||||
|
{
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const FPHYClassCoreAttributeAllocation& Allocation : CoreAttributeAllocations)
|
||||||
|
{
|
||||||
|
if (Allocation.ClassTag == ClassTag)
|
||||||
|
{
|
||||||
|
return &Allocation;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
22
Source/PHY/Private/Game/PHYGameFrameworkSettings.cpp
Normal file
22
Source/PHY/Private/Game/PHYGameFrameworkSettings.cpp
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||||
|
|
||||||
|
#include "Game/PHYGameFrameworkSettings.h"
|
||||||
|
|
||||||
|
#include UE_INLINE_GENERATED_CPP_BY_NAME(PHYGameFrameworkSettings)
|
||||||
|
|
||||||
|
#include "Characters/PHYPlayerCharacter.h"
|
||||||
|
#include "Game/PHYGameState.h"
|
||||||
|
#include "Game/PHYHUD.h"
|
||||||
|
#include "GameFramework/SpectatorPawn.h"
|
||||||
|
#include "Player/PHYPlayerController.h"
|
||||||
|
#include "Player/PHYPlayerState.h"
|
||||||
|
|
||||||
|
UPHYGameFrameworkSettings::UPHYGameFrameworkSettings()
|
||||||
|
{
|
||||||
|
DefaultPawnClass = APHYPlayerCharacter::StaticClass();
|
||||||
|
PlayerControllerClass = APHYPlayerController::StaticClass();
|
||||||
|
PlayerStateClass = APHYPlayerState::StaticClass();
|
||||||
|
GameStateClass = APHYGameState::StaticClass();
|
||||||
|
HUDClass = APHYHUD::StaticClass();
|
||||||
|
SpectatorClass = ASpectatorPawn::StaticClass();
|
||||||
|
}
|
||||||
116
Source/PHY/Private/Game/PHYGameModeBase.cpp
Normal file
116
Source/PHY/Private/Game/PHYGameModeBase.cpp
Normal file
@@ -0,0 +1,116 @@
|
|||||||
|
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||||
|
|
||||||
|
#include "Game/PHYGameModeBase.h"
|
||||||
|
|
||||||
|
#include UE_INLINE_GENERATED_CPP_BY_NAME(PHYGameModeBase)
|
||||||
|
|
||||||
|
#include "Characters/PHYPlayerCharacter.h"
|
||||||
|
#include "Game/PHYGameFrameworkSettings.h"
|
||||||
|
#include "Game/PHYGameState.h"
|
||||||
|
#include "Game/PHYHUD.h"
|
||||||
|
#include "GameFramework/SpectatorPawn.h"
|
||||||
|
#include "Player/PHYPlayerController.h"
|
||||||
|
#include "Player/PHYPlayerState.h"
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
template <typename TBase>
|
||||||
|
void ApplyConfiguredClass(const TSoftClassPtr<TBase>& ConfiguredClass, TSubclassOf<TBase>& TargetClass)
|
||||||
|
{
|
||||||
|
if (ConfiguredClass.IsNull())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (UClass* LoadedClass = ConfiguredClass.LoadSynchronous())
|
||||||
|
{
|
||||||
|
TargetClass = LoadedClass;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
APHYGameModeBase::APHYGameModeBase(const FObjectInitializer& ObjectInitializer)
|
||||||
|
: Super(ObjectInitializer)
|
||||||
|
{
|
||||||
|
DefaultPawnClass = APHYPlayerCharacter::StaticClass();
|
||||||
|
PlayerControllerClass = APHYPlayerController::StaticClass();
|
||||||
|
PlayerStateClass = APHYPlayerState::StaticClass();
|
||||||
|
GameStateClass = APHYGameState::StaticClass();
|
||||||
|
HUDClass = APHYHUD::StaticClass();
|
||||||
|
SpectatorClass = ASpectatorPawn::StaticClass();
|
||||||
|
|
||||||
|
bUseSeamlessTravel = true;
|
||||||
|
bStartPlayersAsSpectators = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void APHYGameModeBase::InitGame(const FString& MapName, const FString& Options, FString& ErrorMessage)
|
||||||
|
{
|
||||||
|
ApplyGameFrameworkSettings();
|
||||||
|
|
||||||
|
Super::InitGame(MapName, Options, ErrorMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
void APHYGameModeBase::InitGameState()
|
||||||
|
{
|
||||||
|
Super::InitGameState();
|
||||||
|
|
||||||
|
SetGameFrameworkReady(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void APHYGameModeBase::StartPlay()
|
||||||
|
{
|
||||||
|
Super::StartPlay();
|
||||||
|
|
||||||
|
SetGameFrameworkReady(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void APHYGameModeBase::PostLogin(APlayerController* NewPlayer)
|
||||||
|
{
|
||||||
|
Super::PostLogin(NewPlayer);
|
||||||
|
|
||||||
|
// 玩家登录后的队伍、职业、存档恢复由后续对应系统在这里挂接。
|
||||||
|
}
|
||||||
|
|
||||||
|
void APHYGameModeBase::Logout(AController* Exiting)
|
||||||
|
{
|
||||||
|
// 玩家退出前的持久化和队伍清理由后续对应系统在这里挂接。
|
||||||
|
Super::Logout(Exiting);
|
||||||
|
}
|
||||||
|
|
||||||
|
UClass* APHYGameModeBase::GetDefaultPawnClassForController_Implementation(AController* InController)
|
||||||
|
{
|
||||||
|
(void)InController;
|
||||||
|
return DefaultPawnClass ? DefaultPawnClass.Get() : Super::GetDefaultPawnClassForController_Implementation(InController);
|
||||||
|
}
|
||||||
|
|
||||||
|
APHYGameState* APHYGameModeBase::GetPHYGameState() const
|
||||||
|
{
|
||||||
|
return GetGameState<APHYGameState>();
|
||||||
|
}
|
||||||
|
|
||||||
|
void APHYGameModeBase::ApplyGameFrameworkSettings()
|
||||||
|
{
|
||||||
|
const UPHYGameFrameworkSettings* Settings = GetDefault<UPHYGameFrameworkSettings>();
|
||||||
|
if (!Settings)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ApplyConfiguredClass(Settings->DefaultPawnClass, DefaultPawnClass);
|
||||||
|
ApplyConfiguredClass(Settings->PlayerControllerClass, PlayerControllerClass);
|
||||||
|
ApplyConfiguredClass(Settings->PlayerStateClass, PlayerStateClass);
|
||||||
|
ApplyConfiguredClass(Settings->GameStateClass, GameStateClass);
|
||||||
|
ApplyConfiguredClass(Settings->HUDClass, HUDClass);
|
||||||
|
ApplyConfiguredClass(Settings->SpectatorClass, SpectatorClass);
|
||||||
|
|
||||||
|
bUseSeamlessTravel = Settings->bUseSeamlessTravel;
|
||||||
|
bStartPlayersAsSpectators = Settings->bStartPlayersAsSpectators;
|
||||||
|
}
|
||||||
|
|
||||||
|
void APHYGameModeBase::SetGameFrameworkReady(const bool bNewReady)
|
||||||
|
{
|
||||||
|
if (APHYGameState* PHYGameState = GetPHYGameState())
|
||||||
|
{
|
||||||
|
PHYGameState->SetGameFrameworkReady(bNewReady);
|
||||||
|
}
|
||||||
|
}
|
||||||
36
Source/PHY/Private/Game/PHYGameState.cpp
Normal file
36
Source/PHY/Private/Game/PHYGameState.cpp
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||||
|
|
||||||
|
#include "Game/PHYGameState.h"
|
||||||
|
|
||||||
|
#include UE_INLINE_GENERATED_CPP_BY_NAME(PHYGameState)
|
||||||
|
|
||||||
|
#include "Net/UnrealNetwork.h"
|
||||||
|
|
||||||
|
APHYGameState::APHYGameState(const FObjectInitializer& ObjectInitializer)
|
||||||
|
: Super(ObjectInitializer)
|
||||||
|
{
|
||||||
|
bReplicates = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void APHYGameState::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
|
||||||
|
{
|
||||||
|
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
|
||||||
|
|
||||||
|
DOREPLIFETIME(ThisClass, bGameFrameworkReady);
|
||||||
|
}
|
||||||
|
|
||||||
|
void APHYGameState::SetGameFrameworkReady(const bool bNewReady)
|
||||||
|
{
|
||||||
|
if (!HasAuthority())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
bGameFrameworkReady = bNewReady;
|
||||||
|
OnRep_GameFrameworkReady();
|
||||||
|
}
|
||||||
|
|
||||||
|
void APHYGameState::OnRep_GameFrameworkReady()
|
||||||
|
{
|
||||||
|
// 预留给 HUD、UI 或战局阶段系统监听框架就绪状态。
|
||||||
|
}
|
||||||
177
Source/PHY/Private/Game/PHYHUD.cpp
Normal file
177
Source/PHY/Private/Game/PHYHUD.cpp
Normal file
@@ -0,0 +1,177 @@
|
|||||||
|
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||||
|
|
||||||
|
#include "Game/PHYHUD.h"
|
||||||
|
|
||||||
|
#include UE_INLINE_GENERATED_CPP_BY_NAME(PHYHUD)
|
||||||
|
|
||||||
|
#include "CommonActivatableWidget.h"
|
||||||
|
#include "Engine/GameInstance.h"
|
||||||
|
#include "GameplayTags/PHYGameplayTags_UI.h"
|
||||||
|
#include "PHYConfigSettings.h"
|
||||||
|
#include "UI/GUIS_GameUILayout.h"
|
||||||
|
#include "UI/GUIS_GameUIPolicy.h"
|
||||||
|
#include "UI/GUIS_GameUISubsystem.h"
|
||||||
|
#include "UI/PHYAttributeMenuWidget.h"
|
||||||
|
#include "UI/PHYHUDStatusWidget.h"
|
||||||
|
#include "UI/PHYPlayerUIContext.h"
|
||||||
|
|
||||||
|
APHYHUD::APHYHUD(const FObjectInitializer& ObjectInitializer)
|
||||||
|
: Super(ObjectInitializer)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void APHYHUD::BeginPlay()
|
||||||
|
{
|
||||||
|
Super::BeginPlay();
|
||||||
|
|
||||||
|
InitializeHUDForPlayer(GetOwningPlayerController());
|
||||||
|
}
|
||||||
|
|
||||||
|
void APHYHUD::InitializeHUDForPlayer(APlayerController* OwningPlayerController)
|
||||||
|
{
|
||||||
|
if (!OwningPlayerController || bHUDInitialized)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (InitializeGenericUIForPlayer(OwningPlayerController))
|
||||||
|
{
|
||||||
|
DefaultHUDWidget = PushDefaultHUDWidget(OwningPlayerController);
|
||||||
|
bHUDInitialized = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
UCommonActivatableWidget* APHYHUD::OpenAttributeMenu(APlayerController* OwningPlayerController)
|
||||||
|
{
|
||||||
|
APlayerController* TargetPlayerController = OwningPlayerController ? OwningPlayerController : GetOwningPlayerController();
|
||||||
|
if (!TargetPlayerController)
|
||||||
|
{
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!bHUDInitialized)
|
||||||
|
{
|
||||||
|
InitializeHUDForPlayer(TargetPlayerController);
|
||||||
|
}
|
||||||
|
|
||||||
|
const UPHYUISettings* UISettings = GetDefault<UPHYUISettings>();
|
||||||
|
TSubclassOf<UCommonActivatableWidget> AttributeMenuClass = UPHYAttributeMenuWidget::StaticClass();
|
||||||
|
if (UISettings && !UISettings->AttributeMenuWidgetClass.IsNull())
|
||||||
|
{
|
||||||
|
if (UClass* LoadedAttributeMenuClass = UISettings->AttributeMenuWidgetClass.LoadSynchronous())
|
||||||
|
{
|
||||||
|
AttributeMenuClass = LoadedAttributeMenuClass;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
UCommonActivatableWidget* PushedWidget = PushMenuWidget(TargetPlayerController, AttributeMenuClass);
|
||||||
|
if (UPHYAttributeMenuWidget* AttributeMenuWidget = Cast<UPHYAttributeMenuWidget>(PushedWidget))
|
||||||
|
{
|
||||||
|
AttributeMenuWidget->InitializeFromPlayer(TargetPlayerController);
|
||||||
|
}
|
||||||
|
|
||||||
|
return PushedWidget;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool APHYHUD::InitializeGenericUIForPlayer(APlayerController* OwningPlayerController)
|
||||||
|
{
|
||||||
|
if (!OwningPlayerController || !OwningPlayerController->IsLocalController())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const UPHYUISettings* UISettings = GetDefault<UPHYUISettings>();
|
||||||
|
if (!UISettings || !UISettings->bUseCommonUI || !UISettings->bUseGenericUISystem)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ULocalPlayer* LocalPlayer = OwningPlayerController->GetLocalPlayer();
|
||||||
|
if (!LocalPlayer)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
UGameInstance* GameInstance = OwningPlayerController->GetGameInstance();
|
||||||
|
UGUIS_GameUISubsystem* GameUISubsystem = GameInstance ? GameInstance->GetSubsystem<UGUIS_GameUISubsystem>() : nullptr;
|
||||||
|
if (!GameUISubsystem)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
GameUISubsystem->AddPlayer(LocalPlayer);
|
||||||
|
|
||||||
|
if (!PlayerUIContext)
|
||||||
|
{
|
||||||
|
PlayerUIContext = NewObject<UPHYPlayerUIContext>(this);
|
||||||
|
}
|
||||||
|
PlayerUIContext->InitializeForPlayer(OwningPlayerController);
|
||||||
|
|
||||||
|
GameUISubsystem->RegisterUIContextForPlayer(LocalPlayer, PlayerUIContext, PlayerUIContextBindingHandle);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
UCommonActivatableWidget* APHYHUD::PushDefaultHUDWidget(APlayerController* OwningPlayerController)
|
||||||
|
{
|
||||||
|
if (!OwningPlayerController || DefaultHUDWidget)
|
||||||
|
{
|
||||||
|
return DefaultHUDWidget;
|
||||||
|
}
|
||||||
|
|
||||||
|
const UPHYUISettings* UISettings = GetDefault<UPHYUISettings>();
|
||||||
|
TSubclassOf<UCommonActivatableWidget> HUDWidgetClass = UPHYHUDStatusWidget::StaticClass();
|
||||||
|
if (UISettings && !UISettings->DefaultHUDWidgetClass.IsNull())
|
||||||
|
{
|
||||||
|
if (UClass* LoadedHUDWidgetClass = UISettings->DefaultHUDWidgetClass.LoadSynchronous())
|
||||||
|
{
|
||||||
|
HUDWidgetClass = LoadedHUDWidgetClass;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ULocalPlayer* LocalPlayer = OwningPlayerController->GetLocalPlayer();
|
||||||
|
if (!LocalPlayer)
|
||||||
|
{
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
UGUIS_GameUIPolicy* UIPolicy = UGUIS_GameUIPolicy::GetGameUIPolicy(OwningPlayerController);
|
||||||
|
UGUIS_GameUILayout* RootLayout = UIPolicy ? UIPolicy->GetRootLayout(LocalPlayer) : nullptr;
|
||||||
|
if (!RootLayout)
|
||||||
|
{
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
const FGameplayTag HUDLayerTag = (UISettings && UISettings->HUDLayerTag.IsValid()) ? UISettings->HUDLayerTag : PHYGameplayTags::UI_Layer_HUD;
|
||||||
|
UCommonActivatableWidget* PushedWidget = RootLayout->PushWidgetToLayerStack<UCommonActivatableWidget>(HUDLayerTag, HUDWidgetClass);
|
||||||
|
if (UPHYHUDStatusWidget* StatusWidget = Cast<UPHYHUDStatusWidget>(PushedWidget))
|
||||||
|
{
|
||||||
|
StatusWidget->InitializeFromPlayer(OwningPlayerController);
|
||||||
|
}
|
||||||
|
|
||||||
|
return PushedWidget;
|
||||||
|
}
|
||||||
|
|
||||||
|
UCommonActivatableWidget* APHYHUD::PushMenuWidget(APlayerController* OwningPlayerController, TSubclassOf<UCommonActivatableWidget> WidgetClass)
|
||||||
|
{
|
||||||
|
if (!OwningPlayerController || !WidgetClass)
|
||||||
|
{
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
const UPHYUISettings* UISettings = GetDefault<UPHYUISettings>();
|
||||||
|
ULocalPlayer* LocalPlayer = OwningPlayerController->GetLocalPlayer();
|
||||||
|
if (!LocalPlayer)
|
||||||
|
{
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
UGUIS_GameUIPolicy* UIPolicy = UGUIS_GameUIPolicy::GetGameUIPolicy(OwningPlayerController);
|
||||||
|
UGUIS_GameUILayout* RootLayout = UIPolicy ? UIPolicy->GetRootLayout(LocalPlayer) : nullptr;
|
||||||
|
if (!RootLayout)
|
||||||
|
{
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
const FGameplayTag MenuLayerTag = (UISettings && UISettings->MenuLayerTag.IsValid()) ? UISettings->MenuLayerTag : PHYGameplayTags::UI_Layer_Menu;
|
||||||
|
return RootLayout->PushWidgetToLayerStack<UCommonActivatableWidget>(MenuLayerTag, WidgetClass);
|
||||||
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||||
|
|
||||||
|
#include "GameplayTags/PHYGameplayTags_Attribute.h"
|
||||||
|
|
||||||
|
// 属性 Tag 只表达领域和分类,具体属性读写仍通过 GAS AttributeSet 完成。
|
||||||
|
namespace PHYGameplayTags
|
||||||
|
{
|
||||||
|
UE_DEFINE_GAMEPLAY_TAG(Attribute_Core, "Attribute.Core");
|
||||||
|
UE_DEFINE_GAMEPLAY_TAG(Attribute_Core_Strength, "Attribute.Core.Strength");
|
||||||
|
UE_DEFINE_GAMEPLAY_TAG(Attribute_Core_Dexterity, "Attribute.Core.Dexterity");
|
||||||
|
UE_DEFINE_GAMEPLAY_TAG(Attribute_Core_Vitality, "Attribute.Core.Vitality");
|
||||||
|
UE_DEFINE_GAMEPLAY_TAG(Attribute_Core_Intelligence, "Attribute.Core.Intelligence");
|
||||||
|
UE_DEFINE_GAMEPLAY_TAG(Attribute_Core_Spirit, "Attribute.Core.Spirit");
|
||||||
|
UE_DEFINE_GAMEPLAY_TAG(Attribute_Core_Perception, "Attribute.Core.Perception");
|
||||||
|
|
||||||
|
UE_DEFINE_GAMEPLAY_TAG(Attribute_Combat, "Attribute.Combat");
|
||||||
|
UE_DEFINE_GAMEPLAY_TAG(Attribute_Combat_Health, "Attribute.Combat.Health");
|
||||||
|
UE_DEFINE_GAMEPLAY_TAG(Attribute_Combat_Mana, "Attribute.Combat.Mana");
|
||||||
|
UE_DEFINE_GAMEPLAY_TAG(Attribute_Combat_Stamina, "Attribute.Combat.Stamina");
|
||||||
|
UE_DEFINE_GAMEPLAY_TAG(Attribute_Combat_Offense, "Attribute.Combat.Offense");
|
||||||
|
UE_DEFINE_GAMEPLAY_TAG(Attribute_Combat_Defense, "Attribute.Combat.Defense");
|
||||||
|
|
||||||
|
UE_DEFINE_GAMEPLAY_TAG(Attribute_Element, "Attribute.Element");
|
||||||
|
UE_DEFINE_GAMEPLAY_TAG(Attribute_Element_Fire, "Attribute.Element.Fire");
|
||||||
|
UE_DEFINE_GAMEPLAY_TAG(Attribute_Element_Water, "Attribute.Element.Water");
|
||||||
|
UE_DEFINE_GAMEPLAY_TAG(Attribute_Element_Ice, "Attribute.Element.Ice");
|
||||||
|
UE_DEFINE_GAMEPLAY_TAG(Attribute_Element_Lightning, "Attribute.Element.Lightning");
|
||||||
|
UE_DEFINE_GAMEPLAY_TAG(Attribute_Element_Earth, "Attribute.Element.Earth");
|
||||||
|
UE_DEFINE_GAMEPLAY_TAG(Attribute_Element_Wind, "Attribute.Element.Wind");
|
||||||
|
UE_DEFINE_GAMEPLAY_TAG(Attribute_Element_Light, "Attribute.Element.Light");
|
||||||
|
UE_DEFINE_GAMEPLAY_TAG(Attribute_Element_Dark, "Attribute.Element.Dark");
|
||||||
|
UE_DEFINE_GAMEPLAY_TAG(Attribute_Element_DamageBonus, "Attribute.Element.DamageBonus");
|
||||||
|
UE_DEFINE_GAMEPLAY_TAG(Attribute_Element_Resistance, "Attribute.Element.Resistance");
|
||||||
|
UE_DEFINE_GAMEPLAY_TAG(Attribute_Element_Penetration, "Attribute.Element.Penetration");
|
||||||
|
}
|
||||||
15
Source/PHY/Private/GameplayTags/PHYGameplayTags_Class.cpp
Normal file
15
Source/PHY/Private/GameplayTags/PHYGameplayTags_Class.cpp
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||||
|
|
||||||
|
#include "GameplayTags/PHYGameplayTags_Class.h"
|
||||||
|
|
||||||
|
// 职业 Tag 由职业系统维护,玩家和 AI 共享分类语义,但 Mesh 应用策略由职业配置控制。
|
||||||
|
namespace PHYGameplayTags
|
||||||
|
{
|
||||||
|
UE_DEFINE_GAMEPLAY_TAG(Class_Saber, "Class.Saber");
|
||||||
|
UE_DEFINE_GAMEPLAY_TAG(Class_Lancer, "Class.Lancer");
|
||||||
|
UE_DEFINE_GAMEPLAY_TAG(Class_Archer, "Class.Archer");
|
||||||
|
UE_DEFINE_GAMEPLAY_TAG(Class_Rider, "Class.Rider");
|
||||||
|
UE_DEFINE_GAMEPLAY_TAG(Class_Caster, "Class.Caster");
|
||||||
|
UE_DEFINE_GAMEPLAY_TAG(Class_Assassin, "Class.Assassin");
|
||||||
|
UE_DEFINE_GAMEPLAY_TAG(Class_Berserker, "Class.Berserker");
|
||||||
|
}
|
||||||
@@ -15,4 +15,5 @@ namespace PHYGameplayTags
|
|||||||
UE_DEFINE_GAMEPLAY_TAG(Input_Aim, "Input.Aim");
|
UE_DEFINE_GAMEPLAY_TAG(Input_Aim, "Input.Aim");
|
||||||
UE_DEFINE_GAMEPLAY_TAG(Input_LockOn, "Input.LockOn");
|
UE_DEFINE_GAMEPLAY_TAG(Input_LockOn, "Input.LockOn");
|
||||||
UE_DEFINE_GAMEPLAY_TAG(Input_Inventory_Toggle, "Input.Inventory.Toggle");
|
UE_DEFINE_GAMEPLAY_TAG(Input_Inventory_Toggle, "Input.Inventory.Toggle");
|
||||||
|
UE_DEFINE_GAMEPLAY_TAG(Input_Menu_Attribute, "Input.Menu.Attribute");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,5 +18,6 @@ namespace PHYGameplayTags
|
|||||||
UE_DEFINE_GAMEPLAY_TAG(State_MovementSet_Default, "State.MovementSet.Default");
|
UE_DEFINE_GAMEPLAY_TAG(State_MovementSet_Default, "State.MovementSet.Default");
|
||||||
UE_DEFINE_GAMEPLAY_TAG(State_MovementSet_Aiming, "State.MovementSet.Aiming");
|
UE_DEFINE_GAMEPLAY_TAG(State_MovementSet_Aiming, "State.MovementSet.Aiming");
|
||||||
UE_DEFINE_GAMEPLAY_TAG(State_Rotation_OrientToMovement, "State.Rotation.OrientToMovement");
|
UE_DEFINE_GAMEPLAY_TAG(State_Rotation_OrientToMovement, "State.Rotation.OrientToMovement");
|
||||||
UE_DEFINE_GAMEPLAY_TAG(State_Rotation_Strafe, "State.Rotation.Strafe");
|
UE_DEFINE_GAMEPLAY_TAG(State_Rotation_Looking, "State.Rotation.Looking");
|
||||||
|
UE_DEFINE_GAMEPLAY_TAG(State_Rotation_Aiming, "State.Rotation.Aiming");
|
||||||
}
|
}
|
||||||
|
|||||||
12
Source/PHY/Private/GameplayTags/PHYGameplayTags_UI.cpp
Normal file
12
Source/PHY/Private/GameplayTags/PHYGameplayTags_UI.cpp
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||||
|
|
||||||
|
#include "GameplayTags/PHYGameplayTags_UI.h"
|
||||||
|
|
||||||
|
// UI 层级 Tag 是 GenericUISystem 注册和推送界面的稳定路由,不使用裸字符串。
|
||||||
|
namespace PHYGameplayTags
|
||||||
|
{
|
||||||
|
UE_DEFINE_GAMEPLAY_TAG(UI_Layer_Game, "UI.Layer.Game");
|
||||||
|
UE_DEFINE_GAMEPLAY_TAG(UI_Layer_HUD, "UI.Layer.HUD");
|
||||||
|
UE_DEFINE_GAMEPLAY_TAG(UI_Layer_Menu, "UI.Layer.Menu");
|
||||||
|
UE_DEFINE_GAMEPLAY_TAG(UI_Layer_Modal, "UI.Layer.Modal");
|
||||||
|
}
|
||||||
83
Source/PHY/Private/Input/PHYInputProcessor_Look.cpp
Normal file
83
Source/PHY/Private/Input/PHYInputProcessor_Look.cpp
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||||
|
|
||||||
|
#include "Input/PHYInputProcessor_Look.h"
|
||||||
|
|
||||||
|
#include UE_INLINE_GENERATED_CPP_BY_NAME(PHYInputProcessor_Look)
|
||||||
|
|
||||||
|
#include "Characters/PHYPlayerCharacter.h"
|
||||||
|
#include "GIPS_InputSystemComponent.h"
|
||||||
|
#include "PHYGameplayTags.h"
|
||||||
|
#include "Player/PHYPlayerController.h"
|
||||||
|
|
||||||
|
UPHYInputProcessor_Look::UPHYInputProcessor_Look()
|
||||||
|
{
|
||||||
|
InputTags.AddTag(PHYGameplayTags::Input_Look);
|
||||||
|
TriggerEvents = {
|
||||||
|
ETriggerEvent::Started,
|
||||||
|
ETriggerEvent::Triggered,
|
||||||
|
ETriggerEvent::Ongoing,
|
||||||
|
ETriggerEvent::Completed,
|
||||||
|
ETriggerEvent::Canceled
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
bool UPHYInputProcessor_Look::CheckCanHandleInput_Implementation(UGIPS_InputSystemComponent* IC, const FInputActionInstance& ActionData, const FGameplayTag InputTag, const ETriggerEvent TriggerEvent) const
|
||||||
|
{
|
||||||
|
(void)ActionData;
|
||||||
|
(void)TriggerEvent;
|
||||||
|
|
||||||
|
if (!IC || InputTag != PHYGameplayTags::Input_Look)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const APHYPlayerCharacter* PlayerCharacter = Cast<APHYPlayerCharacter>(IC->GetControlledPawn());
|
||||||
|
return PlayerCharacter && PlayerCharacter->GetController<APHYPlayerController>();
|
||||||
|
}
|
||||||
|
|
||||||
|
void UPHYInputProcessor_Look::HandleInputTriggered_Implementation(UGIPS_InputSystemComponent* IC, const FInputActionInstance& ActionData, const FGameplayTag InputTag) const
|
||||||
|
{
|
||||||
|
(void)InputTag;
|
||||||
|
RouteLookInput(IC, ActionData, ETriggerEvent::Triggered);
|
||||||
|
}
|
||||||
|
|
||||||
|
void UPHYInputProcessor_Look::HandleInputStarted_Implementation(UGIPS_InputSystemComponent* IC, const FInputActionInstance& ActionData, const FGameplayTag InputTag) const
|
||||||
|
{
|
||||||
|
(void)InputTag;
|
||||||
|
RouteLookInput(IC, ActionData, ETriggerEvent::Started);
|
||||||
|
}
|
||||||
|
|
||||||
|
void UPHYInputProcessor_Look::HandleInputOngoing_Implementation(UGIPS_InputSystemComponent* IC, const FInputActionInstance& ActionData, const FGameplayTag InputTag) const
|
||||||
|
{
|
||||||
|
(void)InputTag;
|
||||||
|
RouteLookInput(IC, ActionData, ETriggerEvent::Ongoing);
|
||||||
|
}
|
||||||
|
|
||||||
|
void UPHYInputProcessor_Look::HandleInputCanceled_Implementation(UGIPS_InputSystemComponent* IC, const FInputActionInstance& ActionData, const FGameplayTag InputTag) const
|
||||||
|
{
|
||||||
|
(void)InputTag;
|
||||||
|
RouteLookInput(IC, ActionData, ETriggerEvent::Canceled);
|
||||||
|
}
|
||||||
|
|
||||||
|
void UPHYInputProcessor_Look::HandleInputCompleted_Implementation(UGIPS_InputSystemComponent* IC, const FInputActionInstance& ActionData, const FGameplayTag InputTag) const
|
||||||
|
{
|
||||||
|
(void)InputTag;
|
||||||
|
RouteLookInput(IC, ActionData, ETriggerEvent::Completed);
|
||||||
|
}
|
||||||
|
|
||||||
|
FString UPHYInputProcessor_Look::GetEditorFriendlyName_Implementation() const
|
||||||
|
{
|
||||||
|
return TEXT("PHY Look");
|
||||||
|
}
|
||||||
|
|
||||||
|
bool UPHYInputProcessor_Look::RouteLookInput(UGIPS_InputSystemComponent* IC, const FInputActionInstance& ActionData, const ETriggerEvent TriggerEvent) const
|
||||||
|
{
|
||||||
|
if (!IC)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
APHYPlayerCharacter* PlayerCharacter = Cast<APHYPlayerCharacter>(IC->GetControlledPawn());
|
||||||
|
APHYPlayerController* PlayerController = PlayerCharacter ? PlayerCharacter->GetController<APHYPlayerController>() : nullptr;
|
||||||
|
return PlayerController ? PlayerController->RouteLookInputFromProcessor(ActionData, TriggerEvent) : false;
|
||||||
|
}
|
||||||
83
Source/PHY/Private/Input/PHYInputProcessor_Move.cpp
Normal file
83
Source/PHY/Private/Input/PHYInputProcessor_Move.cpp
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||||
|
|
||||||
|
#include "Input/PHYInputProcessor_Move.h"
|
||||||
|
|
||||||
|
#include UE_INLINE_GENERATED_CPP_BY_NAME(PHYInputProcessor_Move)
|
||||||
|
|
||||||
|
#include "Characters/PHYPlayerCharacter.h"
|
||||||
|
#include "GIPS_InputSystemComponent.h"
|
||||||
|
#include "PHYGameplayTags.h"
|
||||||
|
#include "Player/PHYPlayerController.h"
|
||||||
|
|
||||||
|
UPHYInputProcessor_Move::UPHYInputProcessor_Move()
|
||||||
|
{
|
||||||
|
InputTags.AddTag(PHYGameplayTags::Input_Move);
|
||||||
|
TriggerEvents = {
|
||||||
|
ETriggerEvent::Started,
|
||||||
|
ETriggerEvent::Triggered,
|
||||||
|
ETriggerEvent::Ongoing,
|
||||||
|
ETriggerEvent::Completed,
|
||||||
|
ETriggerEvent::Canceled
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
bool UPHYInputProcessor_Move::CheckCanHandleInput_Implementation(UGIPS_InputSystemComponent* IC, const FInputActionInstance& ActionData, const FGameplayTag InputTag, const ETriggerEvent TriggerEvent) const
|
||||||
|
{
|
||||||
|
(void)ActionData;
|
||||||
|
(void)TriggerEvent;
|
||||||
|
|
||||||
|
if (!IC || InputTag != PHYGameplayTags::Input_Move)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const APHYPlayerCharacter* PlayerCharacter = Cast<APHYPlayerCharacter>(IC->GetControlledPawn());
|
||||||
|
return PlayerCharacter && PlayerCharacter->GetController<APHYPlayerController>();
|
||||||
|
}
|
||||||
|
|
||||||
|
void UPHYInputProcessor_Move::HandleInputTriggered_Implementation(UGIPS_InputSystemComponent* IC, const FInputActionInstance& ActionData, const FGameplayTag InputTag) const
|
||||||
|
{
|
||||||
|
(void)InputTag;
|
||||||
|
RouteMoveInput(IC, ActionData, ETriggerEvent::Triggered);
|
||||||
|
}
|
||||||
|
|
||||||
|
void UPHYInputProcessor_Move::HandleInputStarted_Implementation(UGIPS_InputSystemComponent* IC, const FInputActionInstance& ActionData, const FGameplayTag InputTag) const
|
||||||
|
{
|
||||||
|
(void)InputTag;
|
||||||
|
RouteMoveInput(IC, ActionData, ETriggerEvent::Started);
|
||||||
|
}
|
||||||
|
|
||||||
|
void UPHYInputProcessor_Move::HandleInputOngoing_Implementation(UGIPS_InputSystemComponent* IC, const FInputActionInstance& ActionData, const FGameplayTag InputTag) const
|
||||||
|
{
|
||||||
|
(void)InputTag;
|
||||||
|
RouteMoveInput(IC, ActionData, ETriggerEvent::Ongoing);
|
||||||
|
}
|
||||||
|
|
||||||
|
void UPHYInputProcessor_Move::HandleInputCanceled_Implementation(UGIPS_InputSystemComponent* IC, const FInputActionInstance& ActionData, const FGameplayTag InputTag) const
|
||||||
|
{
|
||||||
|
(void)InputTag;
|
||||||
|
RouteMoveInput(IC, ActionData, ETriggerEvent::Canceled);
|
||||||
|
}
|
||||||
|
|
||||||
|
void UPHYInputProcessor_Move::HandleInputCompleted_Implementation(UGIPS_InputSystemComponent* IC, const FInputActionInstance& ActionData, const FGameplayTag InputTag) const
|
||||||
|
{
|
||||||
|
(void)InputTag;
|
||||||
|
RouteMoveInput(IC, ActionData, ETriggerEvent::Completed);
|
||||||
|
}
|
||||||
|
|
||||||
|
FString UPHYInputProcessor_Move::GetEditorFriendlyName_Implementation() const
|
||||||
|
{
|
||||||
|
return TEXT("PHY Move");
|
||||||
|
}
|
||||||
|
|
||||||
|
bool UPHYInputProcessor_Move::RouteMoveInput(UGIPS_InputSystemComponent* IC, const FInputActionInstance& ActionData, const ETriggerEvent TriggerEvent) const
|
||||||
|
{
|
||||||
|
if (!IC)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
APHYPlayerCharacter* PlayerCharacter = Cast<APHYPlayerCharacter>(IC->GetControlledPawn());
|
||||||
|
APHYPlayerController* PlayerController = PlayerCharacter ? PlayerCharacter->GetController<APHYPlayerController>() : nullptr;
|
||||||
|
return PlayerController ? PlayerController->RouteMoveInputFromProcessor(ActionData, TriggerEvent) : false;
|
||||||
|
}
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||||
|
|
||||||
|
#include "Input/PHYInputProcessor_OpenAttributeMenu.h"
|
||||||
|
|
||||||
|
#include UE_INLINE_GENERATED_CPP_BY_NAME(PHYInputProcessor_OpenAttributeMenu)
|
||||||
|
|
||||||
|
#include "Characters/PHYPlayerCharacter.h"
|
||||||
|
#include "GIPS_InputSystemComponent.h"
|
||||||
|
#include "PHYGameplayTags.h"
|
||||||
|
#include "Player/PHYPlayerController.h"
|
||||||
|
|
||||||
|
UPHYInputProcessor_OpenAttributeMenu::UPHYInputProcessor_OpenAttributeMenu()
|
||||||
|
{
|
||||||
|
InputTags.AddTag(PHYGameplayTags::Input_Menu_Attribute);
|
||||||
|
TriggerEvents = {
|
||||||
|
ETriggerEvent::Started
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
bool UPHYInputProcessor_OpenAttributeMenu::CheckCanHandleInput_Implementation(UGIPS_InputSystemComponent* IC, const FInputActionInstance& ActionData, const FGameplayTag InputTag, const ETriggerEvent TriggerEvent) const
|
||||||
|
{
|
||||||
|
(void)ActionData;
|
||||||
|
|
||||||
|
if (!IC || InputTag != PHYGameplayTags::Input_Menu_Attribute || TriggerEvent != ETriggerEvent::Started)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const APHYPlayerCharacter* PlayerCharacter = Cast<APHYPlayerCharacter>(IC->GetControlledPawn());
|
||||||
|
const APHYPlayerController* PlayerController = PlayerCharacter ? PlayerCharacter->GetController<APHYPlayerController>() : nullptr;
|
||||||
|
return PlayerController && PlayerController->IsLocalController();
|
||||||
|
}
|
||||||
|
|
||||||
|
void UPHYInputProcessor_OpenAttributeMenu::HandleInputStarted_Implementation(UGIPS_InputSystemComponent* IC, const FInputActionInstance& ActionData, const FGameplayTag InputTag) const
|
||||||
|
{
|
||||||
|
(void)InputTag;
|
||||||
|
RouteOpenAttributeMenuInput(IC, ActionData, ETriggerEvent::Started);
|
||||||
|
}
|
||||||
|
|
||||||
|
FString UPHYInputProcessor_OpenAttributeMenu::GetEditorFriendlyName_Implementation() const
|
||||||
|
{
|
||||||
|
return TEXT("PHY Open Attribute Menu");
|
||||||
|
}
|
||||||
|
|
||||||
|
bool UPHYInputProcessor_OpenAttributeMenu::RouteOpenAttributeMenuInput(UGIPS_InputSystemComponent* IC, const FInputActionInstance& ActionData, const ETriggerEvent TriggerEvent) const
|
||||||
|
{
|
||||||
|
if (!IC)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
APHYPlayerCharacter* PlayerCharacter = Cast<APHYPlayerCharacter>(IC->GetControlledPawn());
|
||||||
|
APHYPlayerController* PlayerController = PlayerCharacter ? PlayerCharacter->GetController<APHYPlayerController>() : nullptr;
|
||||||
|
return PlayerController ? PlayerController->RouteOpenAttributeMenuInputFromProcessor(ActionData, TriggerEvent) : false;
|
||||||
|
}
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
||||||
|
|
||||||
#include "Player/PHYPlayerCameraManager.h"
|
|
||||||
|
|
||||||
#include UE_INLINE_GENERATED_CPP_BY_NAME(PHYPlayerCameraManager)
|
|
||||||
|
|
||||||
#include "Player/PHYPlayerController.h"
|
|
||||||
|
|
||||||
FRotator APHYPlayerCameraManager::GetRotationInput_Implementation() const
|
|
||||||
{
|
|
||||||
const APHYPlayerController* PHYController = Cast<APHYPlayerController>(PCOwner);
|
|
||||||
return PHYController ? PHYController->GetCachedRotationInput() : FRotator::ZeroRotator;
|
|
||||||
}
|
|
||||||
|
|
||||||
FVector APHYPlayerCameraManager::GetMovementControlInput_Implementation() const
|
|
||||||
{
|
|
||||||
const APHYPlayerController* PHYController = Cast<APHYPlayerController>(PCOwner);
|
|
||||||
return PHYController ? PHYController->GetCachedMovementControlInput() : FVector::ZeroVector;
|
|
||||||
}
|
|
||||||
@@ -5,9 +5,11 @@
|
|||||||
#include UE_INLINE_GENERATED_CPP_BY_NAME(PHYPlayerController)
|
#include UE_INLINE_GENERATED_CPP_BY_NAME(PHYPlayerController)
|
||||||
|
|
||||||
#include "Characters/PHYPlayerCharacter.h"
|
#include "Characters/PHYPlayerCharacter.h"
|
||||||
|
#include "Characters/PHYCharacterSettings.h"
|
||||||
|
#include "Components/SLSCharacterMovementComponent.h"
|
||||||
|
#include "Game/PHYHUD.h"
|
||||||
#include "GIPS_InputSystemComponent.h"
|
#include "GIPS_InputSystemComponent.h"
|
||||||
#include "InputActionValue.h"
|
#include "InputActionValue.h"
|
||||||
#include "Player/PHYPlayerCameraManager.h"
|
|
||||||
#include "PHYGameplayTags.h"
|
#include "PHYGameplayTags.h"
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
@@ -40,9 +42,14 @@ namespace
|
|||||||
APHYPlayerController::APHYPlayerController(const FObjectInitializer& ObjectInitializer)
|
APHYPlayerController::APHYPlayerController(const FObjectInitializer& ObjectInitializer)
|
||||||
: Super(ObjectInitializer)
|
: Super(ObjectInitializer)
|
||||||
{
|
{
|
||||||
PlayerCameraManagerClass = APHYPlayerCameraManager::StaticClass();
|
const UPHYCharacterSettings* CharacterSettings = GetDefault<UPHYCharacterSettings>();
|
||||||
|
if (CharacterSettings && !CharacterSettings->DefaultPlayerCameraManagerClass.IsNull())
|
||||||
InputSystemComponent = CreateDefaultSubobject<UGIPS_InputSystemComponent>(TEXT("InputSystemComponent"));
|
{
|
||||||
|
if (UClass* CameraManagerClass = CharacterSettings->DefaultPlayerCameraManagerClass.LoadSynchronous())
|
||||||
|
{
|
||||||
|
PlayerCameraManagerClass = CameraManagerClass;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void APHYPlayerController::BeginPlay()
|
void APHYPlayerController::BeginPlay()
|
||||||
@@ -64,28 +71,57 @@ APHYPlayerCharacter* APHYPlayerController::GetPHYPlayerCharacter() const
|
|||||||
return Cast<APHYPlayerCharacter>(GetPawn());
|
return Cast<APHYPlayerCharacter>(GetPawn());
|
||||||
}
|
}
|
||||||
|
|
||||||
void APHYPlayerController::BindInputEvents()
|
UGIPS_InputSystemComponent* APHYPlayerController::GetInputSystemComponent() const
|
||||||
{
|
{
|
||||||
if (!InputSystemComponent)
|
if (const APHYPlayerCharacter* PHYCharacter = GetPHYPlayerCharacter())
|
||||||
{
|
{
|
||||||
return;
|
return PHYCharacter->GetInputSystemComponent();
|
||||||
}
|
}
|
||||||
|
|
||||||
InputSystemComponent->OnReceivedInput.RemoveDynamic(this, &ThisClass::OnReceivedInput);
|
return nullptr;
|
||||||
InputSystemComponent->OnReceivedInput.AddDynamic(this, &ThisClass::OnReceivedInput);
|
}
|
||||||
|
|
||||||
|
void APHYPlayerController::BindInputEvents()
|
||||||
|
{
|
||||||
|
if (BoundInputSystemComponent)
|
||||||
|
{
|
||||||
|
BoundInputSystemComponent->OnReceivedInput.RemoveDynamic(this, &ThisClass::OnReceivedInput);
|
||||||
|
}
|
||||||
|
|
||||||
|
BoundInputSystemComponent = GetInputSystemComponent();
|
||||||
|
if (BoundInputSystemComponent)
|
||||||
|
{
|
||||||
|
BoundInputSystemComponent->OnReceivedInput.RemoveDynamic(this, &ThisClass::OnReceivedInput);
|
||||||
|
BoundInputSystemComponent->OnReceivedInput.AddDynamic(this, &ThisClass::OnReceivedInput);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void APHYPlayerController::OnReceivedInput(const FInputActionInstance& ActionData, const FGameplayTag& InputTag, const ETriggerEvent TriggerEvent)
|
void APHYPlayerController::OnReceivedInput(const FInputActionInstance& ActionData, const FGameplayTag& InputTag, const ETriggerEvent TriggerEvent)
|
||||||
{
|
{
|
||||||
if (InputTag == PHYGameplayTags::Input_Move)
|
if (InputTag == PHYGameplayTags::Input_Move)
|
||||||
{
|
{
|
||||||
HandleMoveInput(ActionData, TriggerEvent);
|
if (!ConsumeMoveProcessorGuard(TriggerEvent))
|
||||||
|
{
|
||||||
|
RouteMoveInput(ActionData, TriggerEvent);
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (InputTag == PHYGameplayTags::Input_Look)
|
if (InputTag == PHYGameplayTags::Input_Look)
|
||||||
{
|
{
|
||||||
HandleLookInput(ActionData, TriggerEvent);
|
if (!ConsumeLookProcessorGuard(TriggerEvent))
|
||||||
|
{
|
||||||
|
RouteLookInput(ActionData, TriggerEvent);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (InputTag == PHYGameplayTags::Input_Menu_Attribute)
|
||||||
|
{
|
||||||
|
if (!ConsumeAttributeMenuProcessorGuard(TriggerEvent))
|
||||||
|
{
|
||||||
|
RouteOpenAttributeMenuInput(ActionData, TriggerEvent);
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -95,6 +131,42 @@ void APHYPlayerController::OnReceivedInput(const FInputActionInstance& ActionDat
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool APHYPlayerController::RouteMoveInput(const FInputActionInstance& ActionData, const ETriggerEvent TriggerEvent)
|
||||||
|
{
|
||||||
|
return HandleMoveInput(ActionData, TriggerEvent);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool APHYPlayerController::RouteMoveInputFromProcessor(const FInputActionInstance& ActionData, const ETriggerEvent TriggerEvent)
|
||||||
|
{
|
||||||
|
bMoveInputHandledByProcessor = true;
|
||||||
|
LastMoveInputProcessorTriggerEvent = TriggerEvent;
|
||||||
|
return RouteMoveInput(ActionData, TriggerEvent);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool APHYPlayerController::RouteLookInput(const FInputActionInstance& ActionData, const ETriggerEvent TriggerEvent)
|
||||||
|
{
|
||||||
|
return HandleLookInput(ActionData, TriggerEvent);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool APHYPlayerController::RouteLookInputFromProcessor(const FInputActionInstance& ActionData, const ETriggerEvent TriggerEvent)
|
||||||
|
{
|
||||||
|
bLookInputHandledByProcessor = true;
|
||||||
|
LastLookInputProcessorTriggerEvent = TriggerEvent;
|
||||||
|
return RouteLookInput(ActionData, TriggerEvent);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool APHYPlayerController::RouteOpenAttributeMenuInput(const FInputActionInstance& ActionData, const ETriggerEvent TriggerEvent)
|
||||||
|
{
|
||||||
|
return HandleOpenAttributeMenuInput(ActionData, TriggerEvent);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool APHYPlayerController::RouteOpenAttributeMenuInputFromProcessor(const FInputActionInstance& ActionData, const ETriggerEvent TriggerEvent)
|
||||||
|
{
|
||||||
|
bAttributeMenuInputHandledByProcessor = true;
|
||||||
|
LastAttributeMenuInputProcessorTriggerEvent = TriggerEvent;
|
||||||
|
return RouteOpenAttributeMenuInput(ActionData, TriggerEvent);
|
||||||
|
}
|
||||||
|
|
||||||
bool APHYPlayerController::HandleMoveInput(const FInputActionInstance& ActionData, const ETriggerEvent TriggerEvent)
|
bool APHYPlayerController::HandleMoveInput(const FInputActionInstance& ActionData, const ETriggerEvent TriggerEvent)
|
||||||
{
|
{
|
||||||
APHYPlayerCharacter* PHYCharacter = GetPHYPlayerCharacter();
|
APHYPlayerCharacter* PHYCharacter = GetPHYPlayerCharacter();
|
||||||
@@ -106,33 +178,92 @@ bool APHYPlayerController::HandleMoveInput(const FInputActionInstance& ActionDat
|
|||||||
|
|
||||||
CachedMovementInput = IsInputReleased(TriggerEvent) ? FVector2D::ZeroVector : ExtractAxis2D(ActionData.GetValue());
|
CachedMovementInput = IsInputReleased(TriggerEvent) ? FVector2D::ZeroVector : ExtractAxis2D(ActionData.GetValue());
|
||||||
|
|
||||||
const FRotator CurrentControlRotation = GetControlRotation();
|
const FRotator YawRotation(0.0f, PHYCharacter->GetControlRotation().Yaw, 0.0f);
|
||||||
const FRotator YawRotation(0.0f, CurrentControlRotation.Yaw, 0.0f);
|
|
||||||
const FVector ForwardDirection = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::X);
|
const FVector ForwardDirection = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::X);
|
||||||
const FVector RightDirection = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::Y);
|
const FVector RightDirection = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::Y);
|
||||||
|
|
||||||
const FVector WorldIntent = (ForwardDirection * CachedMovementInput.Y) + (RightDirection * CachedMovementInput.X);
|
const FVector WorldIntent = (ForwardDirection * CachedMovementInput.Y) + (RightDirection * CachedMovementInput.X);
|
||||||
PHYCharacter->SetMovementIntent(WorldIntent);
|
PHYCharacter->SetMovementIntent(WorldIntent);
|
||||||
|
|
||||||
if (!CachedMovementInput.IsNearlyZero())
|
if (USLSCharacterMovementComponent* SLSMovementComponent = PHYCharacter->GetSLSCharacterMovementComponent())
|
||||||
{
|
{
|
||||||
PHYCharacter->AddMovementInput(ForwardDirection, CachedMovementInput.Y);
|
SLSMovementComponent->Input_OnMove(CachedMovementInput);
|
||||||
PHYCharacter->AddMovementInput(RightDirection, CachedMovementInput.X);
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool APHYPlayerController::ConsumeMoveProcessorGuard(const ETriggerEvent TriggerEvent)
|
||||||
|
{
|
||||||
|
if (bMoveInputHandledByProcessor && LastMoveInputProcessorTriggerEvent == TriggerEvent)
|
||||||
|
{
|
||||||
|
bMoveInputHandledByProcessor = false;
|
||||||
|
LastMoveInputProcessorTriggerEvent = ETriggerEvent::None;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bMoveInputHandledByProcessor = false;
|
||||||
|
LastMoveInputProcessorTriggerEvent = ETriggerEvent::None;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool APHYPlayerController::ConsumeLookProcessorGuard(const ETriggerEvent TriggerEvent)
|
||||||
|
{
|
||||||
|
if (bLookInputHandledByProcessor && LastLookInputProcessorTriggerEvent == TriggerEvent)
|
||||||
|
{
|
||||||
|
bLookInputHandledByProcessor = false;
|
||||||
|
LastLookInputProcessorTriggerEvent = ETriggerEvent::None;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bLookInputHandledByProcessor = false;
|
||||||
|
LastLookInputProcessorTriggerEvent = ETriggerEvent::None;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool APHYPlayerController::ConsumeAttributeMenuProcessorGuard(const ETriggerEvent TriggerEvent)
|
||||||
|
{
|
||||||
|
if (bAttributeMenuInputHandledByProcessor && LastAttributeMenuInputProcessorTriggerEvent == TriggerEvent)
|
||||||
|
{
|
||||||
|
bAttributeMenuInputHandledByProcessor = false;
|
||||||
|
LastAttributeMenuInputProcessorTriggerEvent = ETriggerEvent::None;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bAttributeMenuInputHandledByProcessor = false;
|
||||||
|
LastAttributeMenuInputProcessorTriggerEvent = ETriggerEvent::None;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool APHYPlayerController::HandleOpenAttributeMenuInput(const FInputActionInstance& ActionData, const ETriggerEvent TriggerEvent)
|
||||||
|
{
|
||||||
|
(void)ActionData;
|
||||||
|
|
||||||
|
if (TriggerEvent != ETriggerEvent::Started || !IsLocalController())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
APHYHUD* PHYHUD = GetHUD<APHYHUD>();
|
||||||
|
return PHYHUD && PHYHUD->OpenAttributeMenu(this);
|
||||||
|
}
|
||||||
|
|
||||||
bool APHYPlayerController::HandleLookInput(const FInputActionInstance& ActionData, const ETriggerEvent TriggerEvent)
|
bool APHYPlayerController::HandleLookInput(const FInputActionInstance& ActionData, const ETriggerEvent TriggerEvent)
|
||||||
{
|
{
|
||||||
CachedLookInput = IsInputReleased(TriggerEvent) ? FVector2D::ZeroVector : ExtractAxis2D(ActionData.GetValue());
|
CachedLookInput = IsInputReleased(TriggerEvent) ? FVector2D::ZeroVector : ExtractAxis2D(ActionData.GetValue());
|
||||||
CachedRotationInput = FRotator(CachedLookInput.Y, CachedLookInput.X, 0.0f);
|
CachedRotationInput = FRotator(CachedLookInput.Y, CachedLookInput.X, 0.0f);
|
||||||
|
|
||||||
if (!CachedLookInput.IsNearlyZero())
|
APHYPlayerCharacter* PHYCharacter = GetPHYPlayerCharacter();
|
||||||
|
if (!PHYCharacter)
|
||||||
{
|
{
|
||||||
AddYawInput(CachedLookInput.X);
|
return false;
|
||||||
AddPitchInput(CachedLookInput.Y);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (USLSCharacterMovementComponent* SLSMovementComponent = PHYCharacter->GetSLSCharacterMovementComponent())
|
||||||
|
{
|
||||||
|
SLSMovementComponent->Input_OnLook(CachedLookInput);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|||||||
@@ -4,7 +4,13 @@
|
|||||||
|
|
||||||
#include UE_INLINE_GENERATED_CPP_BY_NAME(PHYPlayerState)
|
#include UE_INLINE_GENERATED_CPP_BY_NAME(PHYPlayerState)
|
||||||
|
|
||||||
|
#include "AbilitySystem/Attributes/PHYCombatAttributeSet.h"
|
||||||
|
#include "AbilitySystem/Attributes/PHYCoreAttributeSet.h"
|
||||||
|
#include "AbilitySystem/Attributes/PHYElementAttributeSet.h"
|
||||||
|
#include "Class/PHYClassComponent.h"
|
||||||
|
#include "Class/PHYClassSettings.h"
|
||||||
#include "GGA_AbilitySystemComponent.h"
|
#include "GGA_AbilitySystemComponent.h"
|
||||||
|
#include "Net/UnrealNetwork.h"
|
||||||
|
|
||||||
APHYPlayerState::APHYPlayerState(const FObjectInitializer& ObjectInitializer)
|
APHYPlayerState::APHYPlayerState(const FObjectInitializer& ObjectInitializer)
|
||||||
: Super(ObjectInitializer)
|
: Super(ObjectInitializer)
|
||||||
@@ -14,9 +20,78 @@ APHYPlayerState::APHYPlayerState(const FObjectInitializer& ObjectInitializer)
|
|||||||
AbilitySystemComponent = CreateDefaultSubobject<UGGA_AbilitySystemComponent>(TEXT("AbilitySystemComponent"));
|
AbilitySystemComponent = CreateDefaultSubobject<UGGA_AbilitySystemComponent>(TEXT("AbilitySystemComponent"));
|
||||||
AbilitySystemComponent->SetIsReplicated(true);
|
AbilitySystemComponent->SetIsReplicated(true);
|
||||||
AbilitySystemComponent->SetReplicationMode(EGameplayEffectReplicationMode::Mixed);
|
AbilitySystemComponent->SetReplicationMode(EGameplayEffectReplicationMode::Mixed);
|
||||||
|
|
||||||
|
CoreAttributeSet = CreateDefaultSubobject<UPHYCoreAttributeSet>(TEXT("CoreAttributeSet"));
|
||||||
|
CombatAttributeSet = CreateDefaultSubobject<UPHYCombatAttributeSet>(TEXT("CombatAttributeSet"));
|
||||||
|
ElementAttributeSet = CreateDefaultSubobject<UPHYElementAttributeSet>(TEXT("ElementAttributeSet"));
|
||||||
|
|
||||||
|
ClassComponent = CreateDefaultSubobject<UPHYClassComponent>(TEXT("ClassComponent"));
|
||||||
|
ClassComponent->SetClassTag(GetDefault<UPHYClassSettings>()->DefaultPlayerClassTag);
|
||||||
|
}
|
||||||
|
|
||||||
|
void APHYPlayerState::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
|
||||||
|
{
|
||||||
|
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
|
||||||
|
|
||||||
|
DOREPLIFETIME(ThisClass, PlayerLevel);
|
||||||
|
DOREPLIFETIME(ThisClass, Experience);
|
||||||
|
DOREPLIFETIME(ThisClass, ExperienceForNextLevel);
|
||||||
}
|
}
|
||||||
|
|
||||||
UAbilitySystemComponent* APHYPlayerState::GetAbilitySystemComponent() const
|
UAbilitySystemComponent* APHYPlayerState::GetAbilitySystemComponent() const
|
||||||
{
|
{
|
||||||
return AbilitySystemComponent;
|
return AbilitySystemComponent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void APHYPlayerState::SetPlayerLevel(const int32 NewLevel)
|
||||||
|
{
|
||||||
|
SetProgression(NewLevel, Experience, ExperienceForNextLevel);
|
||||||
|
}
|
||||||
|
|
||||||
|
void APHYPlayerState::SetExperience(const int32 NewExperience)
|
||||||
|
{
|
||||||
|
SetProgression(PlayerLevel, NewExperience, ExperienceForNextLevel);
|
||||||
|
}
|
||||||
|
|
||||||
|
void APHYPlayerState::AddExperience(const int32 DeltaExperience)
|
||||||
|
{
|
||||||
|
SetExperience(Experience + DeltaExperience);
|
||||||
|
}
|
||||||
|
|
||||||
|
void APHYPlayerState::SetExperienceForNextLevel(const int32 NewExperienceForNextLevel)
|
||||||
|
{
|
||||||
|
SetProgression(PlayerLevel, Experience, NewExperienceForNextLevel);
|
||||||
|
}
|
||||||
|
|
||||||
|
void APHYPlayerState::SetProgression(const int32 NewLevel, const int32 NewExperience, const int32 NewExperienceForNextLevel)
|
||||||
|
{
|
||||||
|
if (!HasAuthority())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const int32 ClampedLevel = FMath::Max(1, NewLevel);
|
||||||
|
const int32 ClampedExperience = FMath::Max(0, NewExperience);
|
||||||
|
const int32 ClampedExperienceForNextLevel = FMath::Max(1, NewExperienceForNextLevel);
|
||||||
|
|
||||||
|
if (PlayerLevel == ClampedLevel && Experience == ClampedExperience && ExperienceForNextLevel == ClampedExperienceForNextLevel)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
PlayerLevel = ClampedLevel;
|
||||||
|
Experience = ClampedExperience;
|
||||||
|
ExperienceForNextLevel = ClampedExperienceForNextLevel;
|
||||||
|
|
||||||
|
BroadcastProgressionChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
void APHYPlayerState::OnRep_Progression()
|
||||||
|
{
|
||||||
|
BroadcastProgressionChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
void APHYPlayerState::BroadcastProgressionChanged()
|
||||||
|
{
|
||||||
|
OnProgressionChanged.Broadcast(PlayerLevel, Experience, ExperienceForNextLevel);
|
||||||
|
}
|
||||||
|
|||||||
135
Source/PHY/Private/UI/PHYAttributeGroupWidget.cpp
Normal file
135
Source/PHY/Private/UI/PHYAttributeGroupWidget.cpp
Normal file
@@ -0,0 +1,135 @@
|
|||||||
|
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||||
|
|
||||||
|
#include "UI/PHYAttributeGroupWidget.h"
|
||||||
|
|
||||||
|
#include UE_INLINE_GENERATED_CPP_BY_NAME(PHYAttributeGroupWidget)
|
||||||
|
|
||||||
|
#include "Blueprint/WidgetTree.h"
|
||||||
|
#include "Components/Border.h"
|
||||||
|
#include "Components/TextBlock.h"
|
||||||
|
#include "Components/VerticalBox.h"
|
||||||
|
#include "Components/VerticalBoxSlot.h"
|
||||||
|
#include "Engine/Texture2D.h"
|
||||||
|
#include "Fonts/SlateFontInfo.h"
|
||||||
|
#include "Styling/CoreStyle.h"
|
||||||
|
#include "Styling/SlateBrush.h"
|
||||||
|
#include "UI/PHYAttributeRowWidget.h"
|
||||||
|
#include "Widgets/SWidget.h"
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
const TCHAR* const AttributeGroupPaperTexturePath = TEXT("/Game/AGame/UI/Attributes/Textures/T_PHY_UI_PaperPanel.T_PHY_UI_PaperPanel");
|
||||||
|
const TCHAR* const AttributeGroupGoldRuleTexturePath = TEXT("/Game/AGame/UI/Attributes/Textures/T_PHY_UI_GoldRule.T_PHY_UI_GoldRule");
|
||||||
|
|
||||||
|
FSlateBrush MakeAttributeGroupBrush(const TCHAR* TexturePath, const FVector2D ImageSize, const FLinearColor Tint, const ESlateBrushDrawType::Type DrawAs = ESlateBrushDrawType::Box, const FMargin Margin = FMargin(0.08f))
|
||||||
|
{
|
||||||
|
FSlateBrush Brush;
|
||||||
|
Brush.DrawAs = DrawAs;
|
||||||
|
Brush.Margin = Margin;
|
||||||
|
Brush.ImageSize = ImageSize;
|
||||||
|
Brush.TintColor = FSlateColor(Tint);
|
||||||
|
if (UTexture2D* Texture = LoadObject<UTexture2D>(nullptr, TexturePath))
|
||||||
|
{
|
||||||
|
Brush.SetResourceObject(Texture);
|
||||||
|
}
|
||||||
|
return Brush;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
UPHYAttributeGroupWidget::UPHYAttributeGroupWidget(const FObjectInitializer& ObjectInitializer)
|
||||||
|
: Super(ObjectInitializer)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
TSharedRef<SWidget> UPHYAttributeGroupWidget::RebuildWidget()
|
||||||
|
{
|
||||||
|
BuildNativeWidgetTree();
|
||||||
|
return Super::RebuildWidget();
|
||||||
|
}
|
||||||
|
|
||||||
|
void UPHYAttributeGroupWidget::SynchronizeProperties()
|
||||||
|
{
|
||||||
|
Super::SynchronizeProperties();
|
||||||
|
UpdateGroupWidgets();
|
||||||
|
}
|
||||||
|
|
||||||
|
void UPHYAttributeGroupWidget::SetGroupTitle(FText InGroupTitle)
|
||||||
|
{
|
||||||
|
GroupTitle = MoveTemp(InGroupTitle);
|
||||||
|
UpdateGroupWidgets();
|
||||||
|
}
|
||||||
|
|
||||||
|
void UPHYAttributeGroupWidget::ClearRows()
|
||||||
|
{
|
||||||
|
if (RowsBox)
|
||||||
|
{
|
||||||
|
RowsBox->ClearChildren();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
UPHYAttributeRowWidget* UPHYAttributeGroupWidget::AddRow(FText Label, FText Value, const bool bCanUpgrade)
|
||||||
|
{
|
||||||
|
BuildNativeWidgetTree();
|
||||||
|
|
||||||
|
if (!RowsBox || !WidgetTree)
|
||||||
|
{
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
UPHYAttributeRowWidget* RowWidget = WidgetTree->ConstructWidget<UPHYAttributeRowWidget>(UPHYAttributeRowWidget::StaticClass());
|
||||||
|
RowWidget->SetRow(MoveTemp(Label), MoveTemp(Value), bCanUpgrade);
|
||||||
|
if (UVerticalBoxSlot* RowSlot = RowsBox->AddChildToVerticalBox(RowWidget))
|
||||||
|
{
|
||||||
|
RowSlot->SetPadding(FMargin(0.0f, 2.0f));
|
||||||
|
}
|
||||||
|
return RowWidget;
|
||||||
|
}
|
||||||
|
|
||||||
|
void UPHYAttributeGroupWidget::BuildNativeWidgetTree()
|
||||||
|
{
|
||||||
|
if (!WidgetTree)
|
||||||
|
{
|
||||||
|
WidgetTree = NewObject<UWidgetTree>(this, TEXT("WidgetTree"), RF_Transient);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (WidgetTree->RootWidget && TitleText && RowsBox)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
UBorder* RootBorder = WidgetTree->ConstructWidget<UBorder>(UBorder::StaticClass(), TEXT("RootBorder"));
|
||||||
|
RootBorder->SetBrushColor(FLinearColor::Transparent);
|
||||||
|
RootBorder->SetPadding(FMargin(14.0f, 8.0f));
|
||||||
|
WidgetTree->RootWidget = RootBorder;
|
||||||
|
|
||||||
|
UVerticalBox* RootBox = WidgetTree->ConstructWidget<UVerticalBox>(UVerticalBox::StaticClass(), TEXT("RootBox"));
|
||||||
|
RootBorder->SetContent(RootBox);
|
||||||
|
|
||||||
|
TitleText = WidgetTree->ConstructWidget<UTextBlock>(UTextBlock::StaticClass(), TEXT("TitleText"));
|
||||||
|
TitleText->SetFont(FSlateFontInfo(FCoreStyle::GetDefaultFont(), 19));
|
||||||
|
TitleText->SetColorAndOpacity(FSlateColor(FLinearColor(0.48f, 0.27f, 0.11f, 1.0f)));
|
||||||
|
if (UVerticalBoxSlot* TitleSlot = RootBox->AddChildToVerticalBox(TitleText))
|
||||||
|
{
|
||||||
|
TitleSlot->SetPadding(FMargin(2.0f, 0.0f, 0.0f, 2.0f));
|
||||||
|
}
|
||||||
|
|
||||||
|
UBorder* HeaderRule = WidgetTree->ConstructWidget<UBorder>(UBorder::StaticClass(), TEXT("HeaderRule"));
|
||||||
|
HeaderRule->SetBrush(MakeAttributeGroupBrush(AttributeGroupGoldRuleTexturePath, FVector2D(512.0f, 24.0f), FLinearColor(0.87f, 0.69f, 0.34f, 0.72f), ESlateBrushDrawType::Image, FMargin(0.0f)));
|
||||||
|
if (UVerticalBoxSlot* RuleSlot = RootBox->AddChildToVerticalBox(HeaderRule))
|
||||||
|
{
|
||||||
|
RuleSlot->SetPadding(FMargin(0.0f, 0.0f, 0.0f, 8.0f));
|
||||||
|
}
|
||||||
|
|
||||||
|
RowsBox = WidgetTree->ConstructWidget<UVerticalBox>(UVerticalBox::StaticClass(), TEXT("RowsBox"));
|
||||||
|
RootBox->AddChildToVerticalBox(RowsBox);
|
||||||
|
|
||||||
|
UpdateGroupWidgets();
|
||||||
|
}
|
||||||
|
|
||||||
|
void UPHYAttributeGroupWidget::UpdateGroupWidgets()
|
||||||
|
{
|
||||||
|
if (TitleText)
|
||||||
|
{
|
||||||
|
TitleText->SetText(GroupTitle);
|
||||||
|
}
|
||||||
|
}
|
||||||
625
Source/PHY/Private/UI/PHYAttributeMenuWidget.cpp
Normal file
625
Source/PHY/Private/UI/PHYAttributeMenuWidget.cpp
Normal file
@@ -0,0 +1,625 @@
|
|||||||
|
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||||
|
|
||||||
|
#include "UI/PHYAttributeMenuWidget.h"
|
||||||
|
|
||||||
|
#include UE_INLINE_GENERATED_CPP_BY_NAME(PHYAttributeMenuWidget)
|
||||||
|
|
||||||
|
#include "AbilitySystemComponent.h"
|
||||||
|
#include "AbilitySystem/Attributes/PHYCombatAttributeSet.h"
|
||||||
|
#include "AbilitySystem/Attributes/PHYCoreAttributeSet.h"
|
||||||
|
#include "AbilitySystem/Attributes/PHYElementAttributeSet.h"
|
||||||
|
#include "Blueprint/WidgetTree.h"
|
||||||
|
#include "Components/Border.h"
|
||||||
|
#include "Components/Button.h"
|
||||||
|
#include "Components/CanvasPanel.h"
|
||||||
|
#include "Components/CanvasPanelSlot.h"
|
||||||
|
#include "Components/HorizontalBox.h"
|
||||||
|
#include "Components/HorizontalBoxSlot.h"
|
||||||
|
#include "Components/SafeZone.h"
|
||||||
|
#include "Components/ScaleBox.h"
|
||||||
|
#include "Components/SizeBox.h"
|
||||||
|
#include "Components/Spacer.h"
|
||||||
|
#include "Components/TextBlock.h"
|
||||||
|
#include "Components/VerticalBox.h"
|
||||||
|
#include "Components/VerticalBoxSlot.h"
|
||||||
|
#include "Components/WidgetSwitcher.h"
|
||||||
|
#include "Engine/Texture2D.h"
|
||||||
|
#include "Fonts/SlateFontInfo.h"
|
||||||
|
#include "GameFramework/PlayerController.h"
|
||||||
|
#include "Player/PHYPlayerState.h"
|
||||||
|
#include "Styling/CoreStyle.h"
|
||||||
|
#include "Styling/SlateBrush.h"
|
||||||
|
#include "Styling/SlateTypes.h"
|
||||||
|
#include "UI/PHYAttributeResourceBarWidget.h"
|
||||||
|
#include "UI/PHYAttributeOverviewPageWidget.h"
|
||||||
|
#include "UI/PHYAttributeSummaryWidget.h"
|
||||||
|
#include "UI/PHYCombatDerivedAttributePageWidget.h"
|
||||||
|
#include "UI/PHYElementDerivedAttributePageWidget.h"
|
||||||
|
#include "Widgets/SWidget.h"
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
constexpr float AttributeDesignWidth = 1693.0f;
|
||||||
|
constexpr float AttributeDesignHeight = 942.0f;
|
||||||
|
const TCHAR* const AttributeBackplateTexturePath = TEXT("/Game/AGame/UI/Attributes/Textures/T_PHY_UI_AttributeBackplate.T_PHY_UI_AttributeBackplate");
|
||||||
|
const TCHAR* const AttributePaperTexturePath = TEXT("/Game/AGame/UI/Attributes/Textures/T_PHY_UI_PaperPanel.T_PHY_UI_PaperPanel");
|
||||||
|
const TCHAR* const AttributeJadeTexturePath = TEXT("/Game/AGame/UI/Attributes/Textures/T_PHY_UI_JadePanel.T_PHY_UI_JadePanel");
|
||||||
|
const TCHAR* const AttributeCinnabarTexturePath = TEXT("/Game/AGame/UI/Attributes/Textures/T_PHY_UI_CinnabarTab.T_PHY_UI_CinnabarTab");
|
||||||
|
const TCHAR* const AttributeGoldRuleTexturePath = TEXT("/Game/AGame/UI/Attributes/Textures/T_PHY_UI_GoldRule.T_PHY_UI_GoldRule");
|
||||||
|
|
||||||
|
FSlateBrush MakeAttributeMenuBrush(const TCHAR* TexturePath, const FVector2D ImageSize, const FLinearColor Tint, const ESlateBrushDrawType::Type DrawAs = ESlateBrushDrawType::Box, const FMargin Margin = FMargin(0.08f))
|
||||||
|
{
|
||||||
|
FSlateBrush Brush;
|
||||||
|
Brush.DrawAs = DrawAs;
|
||||||
|
Brush.Margin = Margin;
|
||||||
|
Brush.ImageSize = ImageSize;
|
||||||
|
Brush.TintColor = FSlateColor(Tint);
|
||||||
|
if (UTexture2D* Texture = LoadObject<UTexture2D>(nullptr, TexturePath))
|
||||||
|
{
|
||||||
|
Brush.SetResourceObject(Texture);
|
||||||
|
}
|
||||||
|
return Brush;
|
||||||
|
}
|
||||||
|
|
||||||
|
FSlateBrush MakeAttributeTransparentBrush()
|
||||||
|
{
|
||||||
|
FSlateBrush Brush;
|
||||||
|
Brush.DrawAs = ESlateBrushDrawType::NoDrawType;
|
||||||
|
Brush.TintColor = FSlateColor(FLinearColor::Transparent);
|
||||||
|
return Brush;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AddAttributeMenuCanvasChild(UCanvasPanel* CanvasPanel, UWidget* ChildWidget, const FVector2D Position, const FVector2D Size, const int32 ZOrder = 0)
|
||||||
|
{
|
||||||
|
if (!CanvasPanel || !ChildWidget)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (UCanvasPanelSlot* CanvasSlot = CanvasPanel->AddChildToCanvas(ChildWidget))
|
||||||
|
{
|
||||||
|
CanvasSlot->SetAutoSize(false);
|
||||||
|
CanvasSlot->SetPosition(Position);
|
||||||
|
CanvasSlot->SetSize(Size);
|
||||||
|
CanvasSlot->SetZOrder(ZOrder);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
UTextBlock* CreateAttributeMenuText(UWidgetTree* WidgetTree, const FName WidgetName, const FText Text, const int32 FontSize, const FLinearColor Color)
|
||||||
|
{
|
||||||
|
UTextBlock* TextBlock = WidgetTree->ConstructWidget<UTextBlock>(UTextBlock::StaticClass(), WidgetName);
|
||||||
|
TextBlock->SetText(Text);
|
||||||
|
TextBlock->SetFont(FSlateFontInfo(FCoreStyle::GetDefaultFont(), FontSize));
|
||||||
|
TextBlock->SetColorAndOpacity(FSlateColor(Color));
|
||||||
|
return TextBlock;
|
||||||
|
}
|
||||||
|
|
||||||
|
UButton* CreateAttributeNavButton(UWidgetTree* WidgetTree, const FName WidgetName, const FText Label, const bool bActive)
|
||||||
|
{
|
||||||
|
UButton* Button = WidgetTree->ConstructWidget<UButton>(UButton::StaticClass(), WidgetName);
|
||||||
|
|
||||||
|
FButtonStyle ButtonStyle;
|
||||||
|
const FSlateBrush TransparentBrush = MakeAttributeTransparentBrush();
|
||||||
|
ButtonStyle.SetNormal(TransparentBrush);
|
||||||
|
ButtonStyle.SetHovered(TransparentBrush);
|
||||||
|
ButtonStyle.SetPressed(TransparentBrush);
|
||||||
|
Button->SetStyle(ButtonStyle);
|
||||||
|
|
||||||
|
UTextBlock* ButtonText = CreateAttributeMenuText(WidgetTree, NAME_None, Label, 25, bActive ? FLinearColor(0.04f, 0.72f, 0.66f, 1.0f) : FLinearColor(0.34f, 0.32f, 0.26f, 0.62f));
|
||||||
|
Button->SetContent(ButtonText);
|
||||||
|
return Button;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ApplyAttributeSubTabStyle(UButton* Button, const bool bActive)
|
||||||
|
{
|
||||||
|
if (!Button)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
FButtonStyle ButtonStyle;
|
||||||
|
const FLinearColor NormalTint = bActive ? FLinearColor(0.16f, 0.46f, 0.39f, 0.92f) : FLinearColor(0.90f, 0.83f, 0.66f, 0.76f);
|
||||||
|
const FLinearColor HoverTint = bActive ? FLinearColor(0.19f, 0.54f, 0.46f, 0.95f) : FLinearColor(0.96f, 0.89f, 0.72f, 0.90f);
|
||||||
|
ButtonStyle.SetNormal(MakeAttributeMenuBrush(bActive ? AttributeJadeTexturePath : AttributePaperTexturePath, FVector2D(210.0f, 54.0f), NormalTint));
|
||||||
|
ButtonStyle.SetHovered(MakeAttributeMenuBrush(bActive ? AttributeJadeTexturePath : AttributePaperTexturePath, FVector2D(210.0f, 54.0f), HoverTint));
|
||||||
|
ButtonStyle.SetPressed(MakeAttributeMenuBrush(AttributeCinnabarTexturePath, FVector2D(210.0f, 54.0f), FLinearColor(0.72f, 0.24f, 0.14f, 0.92f)));
|
||||||
|
Button->SetStyle(ButtonStyle);
|
||||||
|
Button->SetBackgroundColor(FLinearColor::White);
|
||||||
|
}
|
||||||
|
|
||||||
|
UBorder* CreateAttributeMenuLine(UWidgetTree* WidgetTree, const FName WidgetName)
|
||||||
|
{
|
||||||
|
UBorder* Line = WidgetTree->ConstructWidget<UBorder>(UBorder::StaticClass(), WidgetName);
|
||||||
|
Line->SetBrush(MakeAttributeMenuBrush(AttributeGoldRuleTexturePath, FVector2D(512.0f, 24.0f), FLinearColor(1.0f, 0.86f, 0.44f, 0.80f), ESlateBrushDrawType::Image, FMargin(0.0f)));
|
||||||
|
return Line;
|
||||||
|
}
|
||||||
|
|
||||||
|
TArray<FGameplayAttribute> MakeTrackedAttributes()
|
||||||
|
{
|
||||||
|
return {
|
||||||
|
UPHYCoreAttributeSet::GetStrengthAttribute(),
|
||||||
|
UPHYCoreAttributeSet::GetDexterityAttribute(),
|
||||||
|
UPHYCoreAttributeSet::GetVitalityAttribute(),
|
||||||
|
UPHYCoreAttributeSet::GetIntelligenceAttribute(),
|
||||||
|
UPHYCoreAttributeSet::GetSpiritAttribute(),
|
||||||
|
UPHYCoreAttributeSet::GetPerceptionAttribute(),
|
||||||
|
UPHYCombatAttributeSet::GetHealthAttribute(),
|
||||||
|
UPHYCombatAttributeSet::GetMaxHealthAttribute(),
|
||||||
|
UPHYCombatAttributeSet::GetManaAttribute(),
|
||||||
|
UPHYCombatAttributeSet::GetMaxManaAttribute(),
|
||||||
|
UPHYCombatAttributeSet::GetStaminaAttribute(),
|
||||||
|
UPHYCombatAttributeSet::GetMaxStaminaAttribute(),
|
||||||
|
UPHYCombatAttributeSet::GetPhysicalAttackPowerAttribute(),
|
||||||
|
UPHYCombatAttributeSet::GetSpellPowerAttribute(),
|
||||||
|
UPHYCombatAttributeSet::GetArmorAttribute(),
|
||||||
|
UPHYCombatAttributeSet::GetMagicResistanceAttribute(),
|
||||||
|
UPHYCombatAttributeSet::GetAccuracyAttribute(),
|
||||||
|
UPHYCombatAttributeSet::GetEvasionAttribute(),
|
||||||
|
UPHYCombatAttributeSet::GetCriticalChanceAttribute(),
|
||||||
|
UPHYCombatAttributeSet::GetCriticalDamageAttribute(),
|
||||||
|
UPHYCombatAttributeSet::GetAttackSpeedAttribute(),
|
||||||
|
UPHYCombatAttributeSet::GetCooldownReductionAttribute(),
|
||||||
|
UPHYCombatAttributeSet::GetBlockPowerAttribute(),
|
||||||
|
UPHYCombatAttributeSet::GetGuardBreakPowerAttribute(),
|
||||||
|
UPHYCombatAttributeSet::GetPoiseAttribute(),
|
||||||
|
UPHYCombatAttributeSet::GetPoiseDamageAttribute(),
|
||||||
|
UPHYElementAttributeSet::GetFireDamageBonusAttribute(),
|
||||||
|
UPHYElementAttributeSet::GetWaterDamageBonusAttribute(),
|
||||||
|
UPHYElementAttributeSet::GetIceDamageBonusAttribute(),
|
||||||
|
UPHYElementAttributeSet::GetLightningDamageBonusAttribute(),
|
||||||
|
UPHYElementAttributeSet::GetEarthDamageBonusAttribute(),
|
||||||
|
UPHYElementAttributeSet::GetWindDamageBonusAttribute(),
|
||||||
|
UPHYElementAttributeSet::GetLightDamageBonusAttribute(),
|
||||||
|
UPHYElementAttributeSet::GetDarkDamageBonusAttribute(),
|
||||||
|
UPHYElementAttributeSet::GetFireResistanceAttribute(),
|
||||||
|
UPHYElementAttributeSet::GetWaterResistanceAttribute(),
|
||||||
|
UPHYElementAttributeSet::GetIceResistanceAttribute(),
|
||||||
|
UPHYElementAttributeSet::GetLightningResistanceAttribute(),
|
||||||
|
UPHYElementAttributeSet::GetEarthResistanceAttribute(),
|
||||||
|
UPHYElementAttributeSet::GetWindResistanceAttribute(),
|
||||||
|
UPHYElementAttributeSet::GetLightResistanceAttribute(),
|
||||||
|
UPHYElementAttributeSet::GetDarkResistanceAttribute(),
|
||||||
|
UPHYElementAttributeSet::GetElementPenetrationAttribute()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
UPHYAttributeMenuWidget::UPHYAttributeMenuWidget(const FObjectInitializer& ObjectInitializer)
|
||||||
|
: Super(ObjectInitializer)
|
||||||
|
{
|
||||||
|
InputConfig = EGUIS_ActivatableWidgetInputMode::Menu;
|
||||||
|
SetIsBackHandler(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
TSharedRef<SWidget> UPHYAttributeMenuWidget::RebuildWidget()
|
||||||
|
{
|
||||||
|
BuildNativeWidgetTree();
|
||||||
|
return Super::RebuildWidget();
|
||||||
|
}
|
||||||
|
|
||||||
|
void UPHYAttributeMenuWidget::SynchronizeProperties()
|
||||||
|
{
|
||||||
|
Super::SynchronizeProperties();
|
||||||
|
RefreshAttributePages();
|
||||||
|
UpdateTabVisuals();
|
||||||
|
}
|
||||||
|
|
||||||
|
void UPHYAttributeMenuWidget::InitializeFromPlayer(APlayerController* InPlayerController)
|
||||||
|
{
|
||||||
|
if (!InPlayerController)
|
||||||
|
{
|
||||||
|
BoundPlayerController.Reset();
|
||||||
|
UnbindFromPlayerState();
|
||||||
|
UnbindFromAbilitySystem();
|
||||||
|
RefreshAttributePages();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
BoundPlayerController = InPlayerController;
|
||||||
|
RefreshFromBoundSources();
|
||||||
|
}
|
||||||
|
|
||||||
|
void UPHYAttributeMenuWidget::RefreshFromBoundSources()
|
||||||
|
{
|
||||||
|
APlayerController* PlayerController = BoundPlayerController.Get();
|
||||||
|
if (!PlayerController)
|
||||||
|
{
|
||||||
|
UnbindFromPlayerState();
|
||||||
|
UnbindFromAbilitySystem();
|
||||||
|
RefreshAttributePages();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
APHYPlayerState* PHYPlayerState = PlayerController->GetPlayerState<APHYPlayerState>();
|
||||||
|
BindToPlayerState(PHYPlayerState);
|
||||||
|
BindToAbilitySystem(PHYPlayerState ? PHYPlayerState->GetAbilitySystemComponent() : nullptr);
|
||||||
|
RefreshAttributePages();
|
||||||
|
}
|
||||||
|
|
||||||
|
void UPHYAttributeMenuWidget::NativeConstruct()
|
||||||
|
{
|
||||||
|
Super::NativeConstruct();
|
||||||
|
|
||||||
|
if (!BoundPlayerController.IsValid())
|
||||||
|
{
|
||||||
|
InitializeFromPlayer(GetOwningPlayer());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
RefreshFromBoundSources();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void UPHYAttributeMenuWidget::NativeDestruct()
|
||||||
|
{
|
||||||
|
UnbindFromPlayerState();
|
||||||
|
UnbindFromAbilitySystem();
|
||||||
|
BoundPlayerController.Reset();
|
||||||
|
|
||||||
|
Super::NativeDestruct();
|
||||||
|
}
|
||||||
|
|
||||||
|
void UPHYAttributeMenuWidget::NativeTick(const FGeometry& MyGeometry, const float InDeltaTime)
|
||||||
|
{
|
||||||
|
Super::NativeTick(MyGeometry, InDeltaTime);
|
||||||
|
|
||||||
|
APlayerController* PlayerController = BoundPlayerController.Get();
|
||||||
|
if (!PlayerController)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
APHYPlayerState* CurrentPlayerState = PlayerController->GetPlayerState<APHYPlayerState>();
|
||||||
|
if (CurrentPlayerState != BoundPlayerState.Get() || !BoundAbilitySystemComponent.IsValid())
|
||||||
|
{
|
||||||
|
RefreshFromBoundSources();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void UPHYAttributeMenuWidget::BuildNativeWidgetTree()
|
||||||
|
{
|
||||||
|
if (!WidgetTree)
|
||||||
|
{
|
||||||
|
WidgetTree = NewObject<UWidgetTree>(this, TEXT("WidgetTree"), RF_Transient);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (WidgetTree->RootWidget && SummaryWidget && PageSwitcher && OverviewPage && CombatPage && ElementPage && HeaderHealthBar && HeaderManaBar && HeaderStaminaBar)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
USafeZone* RootSafeZone = WidgetTree->ConstructWidget<USafeZone>(USafeZone::StaticClass(), TEXT("RootSafeZone"));
|
||||||
|
WidgetTree->RootWidget = RootSafeZone;
|
||||||
|
|
||||||
|
UBorder* RootWash = WidgetTree->ConstructWidget<UBorder>(UBorder::StaticClass(), TEXT("RootWash"));
|
||||||
|
RootWash->SetBrushColor(FLinearColor(0.08f, 0.10f, 0.09f, 0.96f));
|
||||||
|
RootSafeZone->SetContent(RootWash);
|
||||||
|
|
||||||
|
UScaleBox* DesignScaleBox = WidgetTree->ConstructWidget<UScaleBox>(UScaleBox::StaticClass(), TEXT("DesignScaleBox"));
|
||||||
|
DesignScaleBox->SetStretch(EStretch::ScaleToFit);
|
||||||
|
DesignScaleBox->SetStretchDirection(EStretchDirection::Both);
|
||||||
|
RootWash->SetContent(DesignScaleBox);
|
||||||
|
|
||||||
|
USizeBox* DesignSizeBox = WidgetTree->ConstructWidget<USizeBox>(USizeBox::StaticClass(), TEXT("DesignSizeBox"));
|
||||||
|
DesignSizeBox->SetWidthOverride(AttributeDesignWidth);
|
||||||
|
DesignSizeBox->SetHeightOverride(AttributeDesignHeight);
|
||||||
|
DesignScaleBox->AddChild(DesignSizeBox);
|
||||||
|
|
||||||
|
UCanvasPanel* DesignCanvas = WidgetTree->ConstructWidget<UCanvasPanel>(UCanvasPanel::StaticClass(), TEXT("DesignCanvas"));
|
||||||
|
DesignSizeBox->AddChild(DesignCanvas);
|
||||||
|
|
||||||
|
UBorder* BackgroundPanel = WidgetTree->ConstructWidget<UBorder>(UBorder::StaticClass(), TEXT("BackgroundPanel"));
|
||||||
|
BackgroundPanel->SetBrush(MakeAttributeMenuBrush(AttributeBackplateTexturePath, FVector2D(AttributeDesignWidth, AttributeDesignHeight), FLinearColor::White, ESlateBrushDrawType::Image, FMargin(0.0f)));
|
||||||
|
AddAttributeMenuCanvasChild(DesignCanvas, BackgroundPanel, FVector2D(0.0f, 0.0f), FVector2D(AttributeDesignWidth, AttributeDesignHeight), 0);
|
||||||
|
|
||||||
|
UBorder* TitlePlaque = WidgetTree->ConstructWidget<UBorder>(UBorder::StaticClass(), TEXT("TitlePlaque"));
|
||||||
|
TitlePlaque->SetBrushColor(FLinearColor::Transparent);
|
||||||
|
TitlePlaque->SetPadding(FMargin(78.0f, 0.0f, 0.0f, 4.0f));
|
||||||
|
AddAttributeMenuCanvasChild(DesignCanvas, TitlePlaque, FVector2D(0.0f, 7.0f), FVector2D(402.0f, 88.0f), 3);
|
||||||
|
|
||||||
|
UTextBlock* TitleText = CreateAttributeMenuText(WidgetTree, TEXT("TitleText"), FText::FromString(TEXT("角色属性")), 31, FLinearColor(0.99f, 0.88f, 0.61f, 1.0f));
|
||||||
|
TitlePlaque->SetContent(TitleText);
|
||||||
|
|
||||||
|
UButton* AttributeNavButton = CreateAttributeNavButton(WidgetTree, TEXT("AttributeNavButton"), FText::FromString(TEXT("属性")), true);
|
||||||
|
AddAttributeMenuCanvasChild(DesignCanvas, AttributeNavButton, FVector2D(552.0f, 9.0f), FVector2D(170.0f, 74.0f), 3);
|
||||||
|
UButton* EquipNavButton = CreateAttributeNavButton(WidgetTree, TEXT("EquipNavButton"), FText::FromString(TEXT("装备")), false);
|
||||||
|
AddAttributeMenuCanvasChild(DesignCanvas, EquipNavButton, FVector2D(764.0f, 11.0f), FVector2D(155.0f, 70.0f), 3);
|
||||||
|
UButton* SkillNavButton = CreateAttributeNavButton(WidgetTree, TEXT("SkillNavButton"), FText::FromString(TEXT("技能")), false);
|
||||||
|
AddAttributeMenuCanvasChild(DesignCanvas, SkillNavButton, FVector2D(978.0f, 11.0f), FVector2D(155.0f, 70.0f), 3);
|
||||||
|
UButton* BagNavButton = CreateAttributeNavButton(WidgetTree, TEXT("BagNavButton"), FText::FromString(TEXT("背包")), false);
|
||||||
|
AddAttributeMenuCanvasChild(DesignCanvas, BagNavButton, FVector2D(1188.0f, 11.0f), FVector2D(155.0f, 70.0f), 3);
|
||||||
|
|
||||||
|
UBorder* HeaderRule = CreateAttributeMenuLine(WidgetTree, TEXT("HeaderRule"));
|
||||||
|
HeaderRule->SetBrushColor(FLinearColor::Transparent);
|
||||||
|
AddAttributeMenuCanvasChild(DesignCanvas, HeaderRule, FVector2D(392.0f, 85.0f), FVector2D(1245.0f, 18.0f), 1);
|
||||||
|
|
||||||
|
SummaryWidget = WidgetTree->ConstructWidget<UPHYAttributeSummaryWidget>(UPHYAttributeSummaryWidget::StaticClass(), TEXT("SummaryWidget"));
|
||||||
|
UBorder* SummaryPanel = WidgetTree->ConstructWidget<UBorder>(UBorder::StaticClass(), TEXT("SummaryPanel"));
|
||||||
|
SummaryPanel->SetBrushColor(FLinearColor::Transparent);
|
||||||
|
SummaryPanel->SetPadding(FMargin(27.0f, 24.0f, 27.0f, 22.0f));
|
||||||
|
SummaryPanel->SetContent(SummaryWidget);
|
||||||
|
AddAttributeMenuCanvasChild(DesignCanvas, SummaryPanel, FVector2D(35.0f, 103.0f), FVector2D(462.0f, 710.0f), 2);
|
||||||
|
|
||||||
|
UBorder* PageBorder = WidgetTree->ConstructWidget<UBorder>(UBorder::StaticClass(), TEXT("PageBorder"));
|
||||||
|
PageBorder->SetBrushColor(FLinearColor::Transparent);
|
||||||
|
PageBorder->SetPadding(FMargin(28.0f, 22.0f, 28.0f, 24.0f));
|
||||||
|
AddAttributeMenuCanvasChild(DesignCanvas, PageBorder, FVector2D(515.0f, 103.0f), FVector2D(1120.0f, 735.0f), 2);
|
||||||
|
|
||||||
|
UVerticalBox* PageBox = WidgetTree->ConstructWidget<UVerticalBox>(UVerticalBox::StaticClass(), TEXT("PageBox"));
|
||||||
|
PageBorder->SetContent(PageBox);
|
||||||
|
|
||||||
|
UTextBlock* ResourceTitleText = CreateAttributeMenuText(WidgetTree, TEXT("ResourceTitleText"), FText::FromString(TEXT("战斗资源")), 22, FLinearColor(0.39f, 0.22f, 0.10f, 1.0f));
|
||||||
|
PageBox->AddChildToVerticalBox(ResourceTitleText);
|
||||||
|
|
||||||
|
UBorder* ResourceRule = CreateAttributeMenuLine(WidgetTree, TEXT("ResourceRule"));
|
||||||
|
if (UVerticalBoxSlot* ResourceRuleSlot = PageBox->AddChildToVerticalBox(ResourceRule))
|
||||||
|
{
|
||||||
|
ResourceRuleSlot->SetPadding(FMargin(0.0f, 4.0f, 0.0f, 8.0f));
|
||||||
|
}
|
||||||
|
|
||||||
|
HeaderHealthBar = WidgetTree->ConstructWidget<UPHYAttributeResourceBarWidget>(UPHYAttributeResourceBarWidget::StaticClass(), TEXT("HeaderHealthBar"));
|
||||||
|
HeaderManaBar = WidgetTree->ConstructWidget<UPHYAttributeResourceBarWidget>(UPHYAttributeResourceBarWidget::StaticClass(), TEXT("HeaderManaBar"));
|
||||||
|
HeaderStaminaBar = WidgetTree->ConstructWidget<UPHYAttributeResourceBarWidget>(UPHYAttributeResourceBarWidget::StaticClass(), TEXT("HeaderStaminaBar"));
|
||||||
|
|
||||||
|
if (UVerticalBoxSlot* HealthSlot = PageBox->AddChildToVerticalBox(HeaderHealthBar))
|
||||||
|
{
|
||||||
|
HealthSlot->SetPadding(FMargin(4.0f, 0.0f, 0.0f, 2.0f));
|
||||||
|
}
|
||||||
|
if (UVerticalBoxSlot* ManaSlot = PageBox->AddChildToVerticalBox(HeaderManaBar))
|
||||||
|
{
|
||||||
|
ManaSlot->SetPadding(FMargin(4.0f, 0.0f, 0.0f, 2.0f));
|
||||||
|
}
|
||||||
|
if (UVerticalBoxSlot* StaminaSlot = PageBox->AddChildToVerticalBox(HeaderStaminaBar))
|
||||||
|
{
|
||||||
|
StaminaSlot->SetPadding(FMargin(4.0f, 0.0f, 0.0f, 12.0f));
|
||||||
|
}
|
||||||
|
|
||||||
|
UHorizontalBox* TabBox = WidgetTree->ConstructWidget<UHorizontalBox>(UHorizontalBox::StaticClass(), TEXT("TabBox"));
|
||||||
|
if (UVerticalBoxSlot* TabSlot = PageBox->AddChildToVerticalBox(TabBox))
|
||||||
|
{
|
||||||
|
TabSlot->SetPadding(FMargin(0.0f, 2.0f, 0.0f, 14.0f));
|
||||||
|
}
|
||||||
|
|
||||||
|
OverviewTabButton = CreateTabButton(TEXT("OverviewTabButton"), FText::FromString(TEXT("核心属性")));
|
||||||
|
CombatTabButton = CreateTabButton(TEXT("CombatTabButton"), FText::FromString(TEXT("战斗衍生")));
|
||||||
|
ElementTabButton = CreateTabButton(TEXT("ElementTabButton"), FText::FromString(TEXT("元素衍生")));
|
||||||
|
|
||||||
|
if (UHorizontalBoxSlot* OverviewTabSlot = TabBox->AddChildToHorizontalBox(OverviewTabButton))
|
||||||
|
{
|
||||||
|
OverviewTabSlot->SetPadding(FMargin(0.0f, 0.0f, 10.0f, 0.0f));
|
||||||
|
}
|
||||||
|
if (UHorizontalBoxSlot* CombatTabSlot = TabBox->AddChildToHorizontalBox(CombatTabButton))
|
||||||
|
{
|
||||||
|
CombatTabSlot->SetPadding(FMargin(0.0f, 0.0f, 10.0f, 0.0f));
|
||||||
|
}
|
||||||
|
TabBox->AddChildToHorizontalBox(ElementTabButton);
|
||||||
|
|
||||||
|
PageSwitcher = WidgetTree->ConstructWidget<UWidgetSwitcher>(UWidgetSwitcher::StaticClass(), TEXT("PageSwitcher"));
|
||||||
|
if (UVerticalBoxSlot* SwitcherSlot = PageBox->AddChildToVerticalBox(PageSwitcher))
|
||||||
|
{
|
||||||
|
SwitcherSlot->SetSize(FSlateChildSize(ESlateSizeRule::Fill));
|
||||||
|
}
|
||||||
|
|
||||||
|
OverviewPage = WidgetTree->ConstructWidget<UPHYAttributeOverviewPageWidget>(UPHYAttributeOverviewPageWidget::StaticClass(), TEXT("OverviewPage"));
|
||||||
|
CombatPage = WidgetTree->ConstructWidget<UPHYCombatDerivedAttributePageWidget>(UPHYCombatDerivedAttributePageWidget::StaticClass(), TEXT("CombatPage"));
|
||||||
|
ElementPage = WidgetTree->ConstructWidget<UPHYElementDerivedAttributePageWidget>(UPHYElementDerivedAttributePageWidget::StaticClass(), TEXT("ElementPage"));
|
||||||
|
PageSwitcher->AddChild(OverviewPage);
|
||||||
|
PageSwitcher->AddChild(CombatPage);
|
||||||
|
PageSwitcher->AddChild(ElementPage);
|
||||||
|
|
||||||
|
OverviewTabButton->OnClicked.AddUniqueDynamic(this, &UPHYAttributeMenuWidget::HandleOverviewTabClicked);
|
||||||
|
CombatTabButton->OnClicked.AddUniqueDynamic(this, &UPHYAttributeMenuWidget::HandleCombatTabClicked);
|
||||||
|
ElementTabButton->OnClicked.AddUniqueDynamic(this, &UPHYAttributeMenuWidget::HandleElementTabClicked);
|
||||||
|
|
||||||
|
UButton* ConfirmButton = CreateAttributeNavButton(WidgetTree, TEXT("ConfirmButton"), FText::FromString(TEXT("确认")), true);
|
||||||
|
AddAttributeMenuCanvasChild(DesignCanvas, ConfirmButton, FVector2D(484.0f, 865.0f), FVector2D(255.0f, 58.0f), 3);
|
||||||
|
UButton* BackButton = CreateAttributeNavButton(WidgetTree, TEXT("BackButton"), FText::FromString(TEXT("返回")), false);
|
||||||
|
AddAttributeMenuCanvasChild(DesignCanvas, BackButton, FVector2D(774.0f, 865.0f), FVector2D(210.0f, 58.0f), 3);
|
||||||
|
UButton* ResetButton = CreateAttributeNavButton(WidgetTree, TEXT("ResetButton"), FText::FromString(TEXT("重置加点")), false);
|
||||||
|
AddAttributeMenuCanvasChild(DesignCanvas, ResetButton, FVector2D(1014.0f, 865.0f), FVector2D(260.0f, 58.0f), 3);
|
||||||
|
|
||||||
|
SetActivePage(ActivePageIndex);
|
||||||
|
RefreshAttributePages();
|
||||||
|
}
|
||||||
|
|
||||||
|
UButton* UPHYAttributeMenuWidget::CreateTabButton(const FName ButtonName, const FText Label)
|
||||||
|
{
|
||||||
|
UButton* TabButton = WidgetTree->ConstructWidget<UButton>(UButton::StaticClass(), ButtonName);
|
||||||
|
ApplyAttributeSubTabStyle(TabButton, false);
|
||||||
|
|
||||||
|
UTextBlock* TabText = WidgetTree->ConstructWidget<UTextBlock>(UTextBlock::StaticClass());
|
||||||
|
TabText->SetText(Label);
|
||||||
|
TabText->SetFont(FSlateFontInfo(FCoreStyle::GetDefaultFont(), 18));
|
||||||
|
TabText->SetColorAndOpacity(FSlateColor(FLinearColor(0.30f, 0.23f, 0.15f, 1.0f)));
|
||||||
|
TabButton->SetContent(TabText);
|
||||||
|
return TabButton;
|
||||||
|
}
|
||||||
|
|
||||||
|
void UPHYAttributeMenuWidget::SetActivePage(const int32 NewPageIndex)
|
||||||
|
{
|
||||||
|
ActivePageIndex = FMath::Clamp(NewPageIndex, 0, 2);
|
||||||
|
if (PageSwitcher)
|
||||||
|
{
|
||||||
|
PageSwitcher->SetActiveWidgetIndex(ActivePageIndex);
|
||||||
|
}
|
||||||
|
UpdateTabVisuals();
|
||||||
|
}
|
||||||
|
|
||||||
|
void UPHYAttributeMenuWidget::UpdateTabVisuals()
|
||||||
|
{
|
||||||
|
ApplyAttributeSubTabStyle(OverviewTabButton, ActivePageIndex == 0);
|
||||||
|
ApplyAttributeSubTabStyle(CombatTabButton, ActivePageIndex == 1);
|
||||||
|
ApplyAttributeSubTabStyle(ElementTabButton, ActivePageIndex == 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
void UPHYAttributeMenuWidget::BindToPlayerState(APHYPlayerState* NewPlayerState)
|
||||||
|
{
|
||||||
|
if (BoundPlayerState.Get() == NewPlayerState)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
UnbindFromPlayerState();
|
||||||
|
BoundPlayerState = NewPlayerState;
|
||||||
|
|
||||||
|
if (NewPlayerState)
|
||||||
|
{
|
||||||
|
NewPlayerState->OnProgressionChanged.AddUniqueDynamic(this, &UPHYAttributeMenuWidget::HandleProgressionChanged);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void UPHYAttributeMenuWidget::UnbindFromPlayerState()
|
||||||
|
{
|
||||||
|
if (APHYPlayerState* PlayerState = BoundPlayerState.Get())
|
||||||
|
{
|
||||||
|
PlayerState->OnProgressionChanged.RemoveDynamic(this, &UPHYAttributeMenuWidget::HandleProgressionChanged);
|
||||||
|
}
|
||||||
|
BoundPlayerState.Reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
void UPHYAttributeMenuWidget::BindToAbilitySystem(UAbilitySystemComponent* NewAbilitySystemComponent)
|
||||||
|
{
|
||||||
|
if (BoundAbilitySystemComponent.Get() == NewAbilitySystemComponent && AttributeChangedHandles.Num() > 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
UnbindFromAbilitySystem();
|
||||||
|
BoundAbilitySystemComponent = NewAbilitySystemComponent;
|
||||||
|
|
||||||
|
if (!NewAbilitySystemComponent)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
BoundAttributes = MakeTrackedAttributes();
|
||||||
|
AttributeChangedHandles.Reserve(BoundAttributes.Num());
|
||||||
|
for (const FGameplayAttribute& Attribute : BoundAttributes)
|
||||||
|
{
|
||||||
|
AttributeChangedHandles.Add(NewAbilitySystemComponent->GetGameplayAttributeValueChangeDelegate(Attribute).AddUObject(this, &UPHYAttributeMenuWidget::HandleAttributeChanged));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void UPHYAttributeMenuWidget::UnbindFromAbilitySystem()
|
||||||
|
{
|
||||||
|
if (UAbilitySystemComponent* AbilitySystemComponent = BoundAbilitySystemComponent.Get())
|
||||||
|
{
|
||||||
|
for (int32 Index = 0; Index < BoundAttributes.Num() && Index < AttributeChangedHandles.Num(); ++Index)
|
||||||
|
{
|
||||||
|
if (AttributeChangedHandles[Index].IsValid())
|
||||||
|
{
|
||||||
|
AbilitySystemComponent->GetGameplayAttributeValueChangeDelegate(BoundAttributes[Index]).Remove(AttributeChangedHandles[Index]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
AttributeChangedHandles.Reset();
|
||||||
|
BoundAttributes.Reset();
|
||||||
|
BoundAbilitySystemComponent.Reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
void UPHYAttributeMenuWidget::RefreshAttributePages()
|
||||||
|
{
|
||||||
|
if (SummaryWidget)
|
||||||
|
{
|
||||||
|
const APHYPlayerState* PlayerState = BoundPlayerState.Get();
|
||||||
|
const FString PlayerName = PlayerState ? PlayerState->GetPlayerName() : TEXT("修行者");
|
||||||
|
const int32 Level = PlayerState ? PlayerState->GetPlayerLevel() : 1;
|
||||||
|
const int32 Experience = PlayerState ? PlayerState->GetExperience() : 0;
|
||||||
|
const int32 ExperienceForNextLevel = PlayerState ? PlayerState->GetExperienceForNextLevel() : 100;
|
||||||
|
SummaryWidget->SetSummary(FText::FromString(PlayerName), Level, Experience, ExperienceForNextLevel, CalculateDisplayCombatPower());
|
||||||
|
SummaryWidget->SetResources(
|
||||||
|
GetAttributeValue(UPHYCombatAttributeSet::GetHealthAttribute()),
|
||||||
|
GetAttributeValue(UPHYCombatAttributeSet::GetMaxHealthAttribute()),
|
||||||
|
GetAttributeValue(UPHYCombatAttributeSet::GetManaAttribute()),
|
||||||
|
GetAttributeValue(UPHYCombatAttributeSet::GetMaxManaAttribute()),
|
||||||
|
GetAttributeValue(UPHYCombatAttributeSet::GetStaminaAttribute()),
|
||||||
|
GetAttributeValue(UPHYCombatAttributeSet::GetMaxStaminaAttribute()));
|
||||||
|
}
|
||||||
|
|
||||||
|
const float Health = GetAttributeValue(UPHYCombatAttributeSet::GetHealthAttribute());
|
||||||
|
const float MaxHealth = GetAttributeValue(UPHYCombatAttributeSet::GetMaxHealthAttribute());
|
||||||
|
const float Mana = GetAttributeValue(UPHYCombatAttributeSet::GetManaAttribute());
|
||||||
|
const float MaxMana = GetAttributeValue(UPHYCombatAttributeSet::GetMaxManaAttribute());
|
||||||
|
const float Stamina = GetAttributeValue(UPHYCombatAttributeSet::GetStaminaAttribute());
|
||||||
|
const float MaxStamina = GetAttributeValue(UPHYCombatAttributeSet::GetMaxStaminaAttribute());
|
||||||
|
if (HeaderHealthBar)
|
||||||
|
{
|
||||||
|
HeaderHealthBar->SetResource(FText::FromString(TEXT("生命值 (HP)")), Health, MaxHealth, FLinearColor(0.78f, 0.18f, 0.14f, 1.0f));
|
||||||
|
}
|
||||||
|
if (HeaderManaBar)
|
||||||
|
{
|
||||||
|
HeaderManaBar->SetResource(FText::FromString(TEXT("法力值 (MP)")), Mana, MaxMana, FLinearColor(0.12f, 0.45f, 0.88f, 1.0f));
|
||||||
|
}
|
||||||
|
if (HeaderStaminaBar)
|
||||||
|
{
|
||||||
|
HeaderStaminaBar->SetResource(FText::FromString(TEXT("耐力值 (ST)")), Stamina, MaxStamina, FLinearColor(0.12f, 0.67f, 0.58f, 1.0f));
|
||||||
|
}
|
||||||
|
|
||||||
|
UAbilitySystemComponent* AbilitySystemComponent = BoundAbilitySystemComponent.Get();
|
||||||
|
if (OverviewPage)
|
||||||
|
{
|
||||||
|
OverviewPage->RefreshAttributes(AbilitySystemComponent);
|
||||||
|
}
|
||||||
|
if (CombatPage)
|
||||||
|
{
|
||||||
|
CombatPage->RefreshAttributes(AbilitySystemComponent);
|
||||||
|
}
|
||||||
|
if (ElementPage)
|
||||||
|
{
|
||||||
|
ElementPage->RefreshAttributes(AbilitySystemComponent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void UPHYAttributeMenuWidget::HandleAttributeChanged(const FOnAttributeChangeData& /*ChangeData*/)
|
||||||
|
{
|
||||||
|
RefreshAttributePages();
|
||||||
|
}
|
||||||
|
|
||||||
|
void UPHYAttributeMenuWidget::HandleProgressionChanged(const int32 /*NewLevel*/, const int32 /*NewExperience*/, const int32 /*NewExperienceForNextLevel*/)
|
||||||
|
{
|
||||||
|
RefreshAttributePages();
|
||||||
|
}
|
||||||
|
|
||||||
|
void UPHYAttributeMenuWidget::HandleOverviewTabClicked()
|
||||||
|
{
|
||||||
|
SetActivePage(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void UPHYAttributeMenuWidget::HandleCombatTabClicked()
|
||||||
|
{
|
||||||
|
SetActivePage(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void UPHYAttributeMenuWidget::HandleElementTabClicked()
|
||||||
|
{
|
||||||
|
SetActivePage(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
float UPHYAttributeMenuWidget::GetAttributeValue(const FGameplayAttribute& Attribute) const
|
||||||
|
{
|
||||||
|
UAbilitySystemComponent* AbilitySystemComponent = BoundAbilitySystemComponent.Get();
|
||||||
|
return AbilitySystemComponent ? AbilitySystemComponent->GetNumericAttribute(Attribute) : 0.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
int32 UPHYAttributeMenuWidget::CalculateDisplayCombatPower() const
|
||||||
|
{
|
||||||
|
const float OffensivePower =
|
||||||
|
GetAttributeValue(UPHYCombatAttributeSet::GetPhysicalAttackPowerAttribute()) +
|
||||||
|
GetAttributeValue(UPHYCombatAttributeSet::GetSpellPowerAttribute()) +
|
||||||
|
GetAttributeValue(UPHYCombatAttributeSet::GetGuardBreakPowerAttribute()) +
|
||||||
|
GetAttributeValue(UPHYCombatAttributeSet::GetPoiseDamageAttribute());
|
||||||
|
|
||||||
|
const float DefensivePower =
|
||||||
|
GetAttributeValue(UPHYCombatAttributeSet::GetArmorAttribute()) +
|
||||||
|
GetAttributeValue(UPHYCombatAttributeSet::GetMagicResistanceAttribute()) +
|
||||||
|
GetAttributeValue(UPHYCombatAttributeSet::GetBlockPowerAttribute()) +
|
||||||
|
GetAttributeValue(UPHYCombatAttributeSet::GetPoiseAttribute());
|
||||||
|
|
||||||
|
const float ResourcePower =
|
||||||
|
GetAttributeValue(UPHYCombatAttributeSet::GetMaxHealthAttribute()) * 0.20f +
|
||||||
|
GetAttributeValue(UPHYCombatAttributeSet::GetMaxManaAttribute()) * 0.10f +
|
||||||
|
GetAttributeValue(UPHYCombatAttributeSet::GetMaxStaminaAttribute()) * 0.10f;
|
||||||
|
|
||||||
|
return FMath::RoundToInt(OffensivePower + DefensivePower + ResourcePower);
|
||||||
|
}
|
||||||
50
Source/PHY/Private/UI/PHYAttributeOverviewPageWidget.cpp
Normal file
50
Source/PHY/Private/UI/PHYAttributeOverviewPageWidget.cpp
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||||
|
|
||||||
|
#include "UI/PHYAttributeOverviewPageWidget.h"
|
||||||
|
|
||||||
|
#include UE_INLINE_GENERATED_CPP_BY_NAME(PHYAttributeOverviewPageWidget)
|
||||||
|
|
||||||
|
#include "AbilitySystemComponent.h"
|
||||||
|
#include "AbilitySystem/Attributes/PHYCombatAttributeSet.h"
|
||||||
|
#include "AbilitySystem/Attributes/PHYCoreAttributeSet.h"
|
||||||
|
#include "UI/PHYAttributeGroupWidget.h"
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
float GetOverviewAttributeValue(const UAbilitySystemComponent* AbilitySystemComponent, const FGameplayAttribute& Attribute)
|
||||||
|
{
|
||||||
|
return AbilitySystemComponent ? AbilitySystemComponent->GetNumericAttribute(Attribute) : 0.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
FText MakeOverviewNumberText(const float Value)
|
||||||
|
{
|
||||||
|
return FText::FromString(FString::Printf(TEXT("%.0f"), Value));
|
||||||
|
}
|
||||||
|
|
||||||
|
FText MakeOverviewPairText(const float CurrentValue, const float MaxValue)
|
||||||
|
{
|
||||||
|
return FText::FromString(FString::Printf(TEXT("%.0f / %.0f"), FMath::Max(0.0f, CurrentValue), FMath::Max(0.0f, MaxValue)));
|
||||||
|
}
|
||||||
|
|
||||||
|
void AddOverviewAttributeRow(UPHYAttributeGroupWidget* GroupWidget, const TCHAR* Label, const FText& Value, const bool bCanUpgrade = false)
|
||||||
|
{
|
||||||
|
if (GroupWidget)
|
||||||
|
{
|
||||||
|
GroupWidget->AddRow(FText::FromString(Label), Value, bCanUpgrade);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void UPHYAttributeOverviewPageWidget::RefreshAttributes(UAbilitySystemComponent* AbilitySystemComponent)
|
||||||
|
{
|
||||||
|
SetPageTitle(FText::FromString(TEXT("核心属性")));
|
||||||
|
ClearGroups();
|
||||||
|
|
||||||
|
UPHYAttributeGroupWidget* CoreGroup = AddGroup(FText::FromString(TEXT("核心属性")));
|
||||||
|
AddOverviewAttributeRow(CoreGroup, TEXT("力量"), MakeOverviewNumberText(GetOverviewAttributeValue(AbilitySystemComponent, UPHYCoreAttributeSet::GetStrengthAttribute())), true);
|
||||||
|
AddOverviewAttributeRow(CoreGroup, TEXT("敏捷"), MakeOverviewNumberText(GetOverviewAttributeValue(AbilitySystemComponent, UPHYCoreAttributeSet::GetDexterityAttribute())), true);
|
||||||
|
AddOverviewAttributeRow(CoreGroup, TEXT("体质"), MakeOverviewNumberText(GetOverviewAttributeValue(AbilitySystemComponent, UPHYCoreAttributeSet::GetVitalityAttribute())), true);
|
||||||
|
AddOverviewAttributeRow(CoreGroup, TEXT("智力"), MakeOverviewNumberText(GetOverviewAttributeValue(AbilitySystemComponent, UPHYCoreAttributeSet::GetIntelligenceAttribute())), true);
|
||||||
|
AddOverviewAttributeRow(CoreGroup, TEXT("精神"), MakeOverviewNumberText(GetOverviewAttributeValue(AbilitySystemComponent, UPHYCoreAttributeSet::GetSpiritAttribute())), true);
|
||||||
|
AddOverviewAttributeRow(CoreGroup, TEXT("感知"), MakeOverviewNumberText(GetOverviewAttributeValue(AbilitySystemComponent, UPHYCoreAttributeSet::GetPerceptionAttribute())), true);
|
||||||
|
}
|
||||||
122
Source/PHY/Private/UI/PHYAttributePageWidget.cpp
Normal file
122
Source/PHY/Private/UI/PHYAttributePageWidget.cpp
Normal file
@@ -0,0 +1,122 @@
|
|||||||
|
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||||
|
|
||||||
|
#include "UI/PHYAttributePageWidget.h"
|
||||||
|
|
||||||
|
#include UE_INLINE_GENERATED_CPP_BY_NAME(PHYAttributePageWidget)
|
||||||
|
|
||||||
|
#include "Blueprint/WidgetTree.h"
|
||||||
|
#include "Components/Border.h"
|
||||||
|
#include "Components/ScrollBox.h"
|
||||||
|
#include "Components/ScrollBoxSlot.h"
|
||||||
|
#include "Components/TextBlock.h"
|
||||||
|
#include "Components/UniformGridPanel.h"
|
||||||
|
#include "Components/UniformGridSlot.h"
|
||||||
|
#include "Components/VerticalBox.h"
|
||||||
|
#include "Components/VerticalBoxSlot.h"
|
||||||
|
#include "Fonts/SlateFontInfo.h"
|
||||||
|
#include "Styling/CoreStyle.h"
|
||||||
|
#include "UI/PHYAttributeGroupWidget.h"
|
||||||
|
#include "Widgets/SWidget.h"
|
||||||
|
|
||||||
|
UPHYAttributePageWidget::UPHYAttributePageWidget(const FObjectInitializer& ObjectInitializer)
|
||||||
|
: Super(ObjectInitializer)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
TSharedRef<SWidget> UPHYAttributePageWidget::RebuildWidget()
|
||||||
|
{
|
||||||
|
BuildNativeWidgetTree();
|
||||||
|
return Super::RebuildWidget();
|
||||||
|
}
|
||||||
|
|
||||||
|
void UPHYAttributePageWidget::SynchronizeProperties()
|
||||||
|
{
|
||||||
|
Super::SynchronizeProperties();
|
||||||
|
UpdatePageWidgets();
|
||||||
|
}
|
||||||
|
|
||||||
|
void UPHYAttributePageWidget::SetPageTitle(FText InPageTitle)
|
||||||
|
{
|
||||||
|
PageTitle = MoveTemp(InPageTitle);
|
||||||
|
UpdatePageWidgets();
|
||||||
|
}
|
||||||
|
|
||||||
|
void UPHYAttributePageWidget::ClearGroups()
|
||||||
|
{
|
||||||
|
GroupCount = 0;
|
||||||
|
if (GroupsGrid)
|
||||||
|
{
|
||||||
|
GroupsGrid->ClearChildren();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
UPHYAttributeGroupWidget* UPHYAttributePageWidget::AddGroup(FText GroupTitle)
|
||||||
|
{
|
||||||
|
BuildNativeWidgetTree();
|
||||||
|
|
||||||
|
if (!GroupsGrid || !WidgetTree)
|
||||||
|
{
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
UPHYAttributeGroupWidget* GroupWidget = WidgetTree->ConstructWidget<UPHYAttributeGroupWidget>(UPHYAttributeGroupWidget::StaticClass());
|
||||||
|
GroupWidget->SetGroupTitle(MoveTemp(GroupTitle));
|
||||||
|
UBorder* GroupPadding = WidgetTree->ConstructWidget<UBorder>(UBorder::StaticClass());
|
||||||
|
GroupPadding->SetBrushColor(FLinearColor(1.0f, 1.0f, 1.0f, 0.0f));
|
||||||
|
GroupPadding->SetPadding(FMargin(0.0f, 0.0f, 14.0f, 14.0f));
|
||||||
|
GroupPadding->SetContent(GroupWidget);
|
||||||
|
const int32 GroupIndex = GroupCount++;
|
||||||
|
if (UUniformGridSlot* GroupSlot = GroupsGrid->AddChildToUniformGrid(GroupPadding, GroupIndex / 2, GroupIndex % 2))
|
||||||
|
{
|
||||||
|
GroupSlot->SetHorizontalAlignment(HAlign_Fill);
|
||||||
|
GroupSlot->SetVerticalAlignment(VAlign_Top);
|
||||||
|
}
|
||||||
|
return GroupWidget;
|
||||||
|
}
|
||||||
|
|
||||||
|
void UPHYAttributePageWidget::BuildNativeWidgetTree()
|
||||||
|
{
|
||||||
|
if (!WidgetTree)
|
||||||
|
{
|
||||||
|
WidgetTree = NewObject<UWidgetTree>(this, TEXT("WidgetTree"), RF_Transient);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (WidgetTree->RootWidget && TitleText && GroupsScrollBox && GroupsGrid)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
UVerticalBox* RootBox = WidgetTree->ConstructWidget<UVerticalBox>(UVerticalBox::StaticClass(), TEXT("RootBox"));
|
||||||
|
WidgetTree->RootWidget = RootBox;
|
||||||
|
|
||||||
|
TitleText = WidgetTree->ConstructWidget<UTextBlock>(UTextBlock::StaticClass(), TEXT("TitleText"));
|
||||||
|
TitleText->SetFont(FSlateFontInfo(FCoreStyle::GetDefaultFont(), 12));
|
||||||
|
TitleText->SetColorAndOpacity(FSlateColor(FLinearColor(0.42f, 0.18f, 0.08f, 0.0f)));
|
||||||
|
TitleText->SetVisibility(ESlateVisibility::Collapsed);
|
||||||
|
if (UVerticalBoxSlot* TitleSlot = RootBox->AddChildToVerticalBox(TitleText))
|
||||||
|
{
|
||||||
|
TitleSlot->SetPadding(FMargin(0.0f));
|
||||||
|
}
|
||||||
|
|
||||||
|
GroupsScrollBox = WidgetTree->ConstructWidget<UScrollBox>(UScrollBox::StaticClass(), TEXT("GroupsScrollBox"));
|
||||||
|
if (UVerticalBoxSlot* ScrollSlot = RootBox->AddChildToVerticalBox(GroupsScrollBox))
|
||||||
|
{
|
||||||
|
ScrollSlot->SetSize(FSlateChildSize(ESlateSizeRule::Fill));
|
||||||
|
}
|
||||||
|
|
||||||
|
GroupsGrid = WidgetTree->ConstructWidget<UUniformGridPanel>(UUniformGridPanel::StaticClass(), TEXT("GroupsGrid"));
|
||||||
|
if (UScrollBoxSlot* GroupsSlot = Cast<UScrollBoxSlot>(GroupsScrollBox->AddChild(GroupsGrid)))
|
||||||
|
{
|
||||||
|
GroupsSlot->SetPadding(FMargin(0.0f));
|
||||||
|
}
|
||||||
|
|
||||||
|
UpdatePageWidgets();
|
||||||
|
}
|
||||||
|
|
||||||
|
void UPHYAttributePageWidget::UpdatePageWidgets()
|
||||||
|
{
|
||||||
|
if (TitleText)
|
||||||
|
{
|
||||||
|
TitleText->SetText(PageTitle);
|
||||||
|
}
|
||||||
|
}
|
||||||
141
Source/PHY/Private/UI/PHYAttributeResourceBarWidget.cpp
Normal file
141
Source/PHY/Private/UI/PHYAttributeResourceBarWidget.cpp
Normal file
@@ -0,0 +1,141 @@
|
|||||||
|
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||||
|
|
||||||
|
#include "UI/PHYAttributeResourceBarWidget.h"
|
||||||
|
|
||||||
|
#include UE_INLINE_GENERATED_CPP_BY_NAME(PHYAttributeResourceBarWidget)
|
||||||
|
|
||||||
|
#include "Blueprint/WidgetTree.h"
|
||||||
|
#include "Components/Border.h"
|
||||||
|
#include "Components/HorizontalBox.h"
|
||||||
|
#include "Components/HorizontalBoxSlot.h"
|
||||||
|
#include "Components/ProgressBar.h"
|
||||||
|
#include "Components/SizeBox.h"
|
||||||
|
#include "Components/TextBlock.h"
|
||||||
|
#include "Components/VerticalBox.h"
|
||||||
|
#include "Components/VerticalBoxSlot.h"
|
||||||
|
#include "Engine/Texture2D.h"
|
||||||
|
#include "Fonts/SlateFontInfo.h"
|
||||||
|
#include "Styling/CoreStyle.h"
|
||||||
|
#include "Styling/SlateBrush.h"
|
||||||
|
#include "Widgets/SWidget.h"
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
const TCHAR* const AttributeResourceRowTexturePath = TEXT("/Game/AGame/UI/Attributes/Textures/T_PHY_UI_RowStrip.T_PHY_UI_RowStrip");
|
||||||
|
|
||||||
|
float MakePercent(const float CurrentValue, const float MaxValue)
|
||||||
|
{
|
||||||
|
return MaxValue > UE_SMALL_NUMBER ? FMath::Clamp(CurrentValue / MaxValue, 0.0f, 1.0f) : 0.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
FSlateBrush MakeAttributeResourceBrush()
|
||||||
|
{
|
||||||
|
FSlateBrush Brush;
|
||||||
|
Brush.DrawAs = ESlateBrushDrawType::Box;
|
||||||
|
Brush.Margin = FMargin(0.08f);
|
||||||
|
Brush.ImageSize = FVector2D(512.0f, 64.0f);
|
||||||
|
Brush.TintColor = FSlateColor(FLinearColor(1.0f, 0.98f, 0.90f, 0.24f));
|
||||||
|
if (UTexture2D* Texture = LoadObject<UTexture2D>(nullptr, AttributeResourceRowTexturePath))
|
||||||
|
{
|
||||||
|
Brush.SetResourceObject(Texture);
|
||||||
|
}
|
||||||
|
return Brush;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
UPHYAttributeResourceBarWidget::UPHYAttributeResourceBarWidget(const FObjectInitializer& ObjectInitializer)
|
||||||
|
: Super(ObjectInitializer)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
TSharedRef<SWidget> UPHYAttributeResourceBarWidget::RebuildWidget()
|
||||||
|
{
|
||||||
|
BuildNativeWidgetTree();
|
||||||
|
return Super::RebuildWidget();
|
||||||
|
}
|
||||||
|
|
||||||
|
void UPHYAttributeResourceBarWidget::SynchronizeProperties()
|
||||||
|
{
|
||||||
|
Super::SynchronizeProperties();
|
||||||
|
UpdateResourceWidgets();
|
||||||
|
}
|
||||||
|
|
||||||
|
void UPHYAttributeResourceBarWidget::SetResource(FText InLabel, const float InCurrentValue, const float InMaxValue, const FLinearColor InBarTint)
|
||||||
|
{
|
||||||
|
Label = MoveTemp(InLabel);
|
||||||
|
CurrentValue = InCurrentValue;
|
||||||
|
MaxValue = FMath::Max(1.0f, InMaxValue);
|
||||||
|
BarTint = InBarTint;
|
||||||
|
UpdateResourceWidgets();
|
||||||
|
}
|
||||||
|
|
||||||
|
void UPHYAttributeResourceBarWidget::BuildNativeWidgetTree()
|
||||||
|
{
|
||||||
|
if (!WidgetTree)
|
||||||
|
{
|
||||||
|
WidgetTree = NewObject<UWidgetTree>(this, TEXT("WidgetTree"), RF_Transient);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (WidgetTree->RootWidget && LabelText && ValueText && ResourceBar)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
UBorder* RootBorder = WidgetTree->ConstructWidget<UBorder>(UBorder::StaticClass(), TEXT("RootBorder"));
|
||||||
|
RootBorder->SetBrush(MakeAttributeResourceBrush());
|
||||||
|
RootBorder->SetPadding(FMargin(10.0f, 5.0f));
|
||||||
|
WidgetTree->RootWidget = RootBorder;
|
||||||
|
|
||||||
|
UHorizontalBox* HeaderBox = WidgetTree->ConstructWidget<UHorizontalBox>(UHorizontalBox::StaticClass(), TEXT("HeaderBox"));
|
||||||
|
RootBorder->SetContent(HeaderBox);
|
||||||
|
|
||||||
|
LabelText = WidgetTree->ConstructWidget<UTextBlock>(UTextBlock::StaticClass(), TEXT("LabelText"));
|
||||||
|
LabelText->SetFont(FSlateFontInfo(FCoreStyle::GetDefaultFont(), 16));
|
||||||
|
LabelText->SetColorAndOpacity(FSlateColor(FLinearColor(0.34f, 0.27f, 0.17f, 1.0f)));
|
||||||
|
if (UHorizontalBoxSlot* LabelSlot = HeaderBox->AddChildToHorizontalBox(LabelText))
|
||||||
|
{
|
||||||
|
LabelSlot->SetSize(FSlateChildSize(ESlateSizeRule::Automatic));
|
||||||
|
LabelSlot->SetPadding(FMargin(0.0f, 0.0f, 18.0f, 0.0f));
|
||||||
|
LabelSlot->SetVerticalAlignment(VAlign_Center);
|
||||||
|
}
|
||||||
|
|
||||||
|
USizeBox* BarSizeBox = WidgetTree->ConstructWidget<USizeBox>(USizeBox::StaticClass(), TEXT("BarSizeBox"));
|
||||||
|
BarSizeBox->SetHeightOverride(13.0f);
|
||||||
|
if (UHorizontalBoxSlot* BarOuterSlot = HeaderBox->AddChildToHorizontalBox(BarSizeBox))
|
||||||
|
{
|
||||||
|
BarOuterSlot->SetSize(FSlateChildSize(ESlateSizeRule::Fill));
|
||||||
|
BarOuterSlot->SetPadding(FMargin(0.0f, 2.0f, 18.0f, 0.0f));
|
||||||
|
BarOuterSlot->SetVerticalAlignment(VAlign_Center);
|
||||||
|
}
|
||||||
|
|
||||||
|
ResourceBar = WidgetTree->ConstructWidget<UProgressBar>(UProgressBar::StaticClass(), TEXT("ResourceBar"));
|
||||||
|
BarSizeBox->AddChild(ResourceBar);
|
||||||
|
|
||||||
|
ValueText = WidgetTree->ConstructWidget<UTextBlock>(UTextBlock::StaticClass(), TEXT("ValueText"));
|
||||||
|
ValueText->SetFont(FSlateFontInfo(FCoreStyle::GetDefaultFont(), 14));
|
||||||
|
ValueText->SetColorAndOpacity(FSlateColor(FLinearColor(0.23f, 0.20f, 0.16f, 1.0f)));
|
||||||
|
if (UHorizontalBoxSlot* ValueSlot = HeaderBox->AddChildToHorizontalBox(ValueText))
|
||||||
|
{
|
||||||
|
ValueSlot->SetSize(FSlateChildSize(ESlateSizeRule::Automatic));
|
||||||
|
ValueSlot->SetVerticalAlignment(VAlign_Center);
|
||||||
|
}
|
||||||
|
|
||||||
|
UpdateResourceWidgets();
|
||||||
|
}
|
||||||
|
|
||||||
|
void UPHYAttributeResourceBarWidget::UpdateResourceWidgets()
|
||||||
|
{
|
||||||
|
if (LabelText)
|
||||||
|
{
|
||||||
|
LabelText->SetText(Label);
|
||||||
|
}
|
||||||
|
if (ValueText)
|
||||||
|
{
|
||||||
|
ValueText->SetText(FText::FromString(FString::Printf(TEXT("%.0f / %.0f"), FMath::Max(0.0f, CurrentValue), FMath::Max(1.0f, MaxValue))));
|
||||||
|
}
|
||||||
|
if (ResourceBar)
|
||||||
|
{
|
||||||
|
ResourceBar->SetPercent(MakePercent(CurrentValue, MaxValue));
|
||||||
|
ResourceBar->SetFillColorAndOpacity(BarTint);
|
||||||
|
}
|
||||||
|
}
|
||||||
140
Source/PHY/Private/UI/PHYAttributeRowWidget.cpp
Normal file
140
Source/PHY/Private/UI/PHYAttributeRowWidget.cpp
Normal file
@@ -0,0 +1,140 @@
|
|||||||
|
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||||
|
|
||||||
|
#include "UI/PHYAttributeRowWidget.h"
|
||||||
|
|
||||||
|
#include UE_INLINE_GENERATED_CPP_BY_NAME(PHYAttributeRowWidget)
|
||||||
|
|
||||||
|
#include "Blueprint/WidgetTree.h"
|
||||||
|
#include "Components/Border.h"
|
||||||
|
#include "Components/Button.h"
|
||||||
|
#include "Components/HorizontalBox.h"
|
||||||
|
#include "Components/HorizontalBoxSlot.h"
|
||||||
|
#include "Components/TextBlock.h"
|
||||||
|
#include "Engine/Texture2D.h"
|
||||||
|
#include "Fonts/SlateFontInfo.h"
|
||||||
|
#include "Styling/CoreStyle.h"
|
||||||
|
#include "Styling/SlateBrush.h"
|
||||||
|
#include "Styling/SlateTypes.h"
|
||||||
|
#include "Widgets/SWidget.h"
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
const TCHAR* const AttributeRowTexturePath = TEXT("/Game/AGame/UI/Attributes/Textures/T_PHY_UI_RowStrip.T_PHY_UI_RowStrip");
|
||||||
|
const TCHAR* const AttributeRowCinnabarTexturePath = TEXT("/Game/AGame/UI/Attributes/Textures/T_PHY_UI_CinnabarTab.T_PHY_UI_CinnabarTab");
|
||||||
|
|
||||||
|
FSlateBrush MakeAttributeRowBrush(const TCHAR* TexturePath, const FVector2D ImageSize, const FLinearColor Tint, const ESlateBrushDrawType::Type DrawAs = ESlateBrushDrawType::Box, const FMargin Margin = FMargin(0.08f))
|
||||||
|
{
|
||||||
|
FSlateBrush Brush;
|
||||||
|
Brush.DrawAs = DrawAs;
|
||||||
|
Brush.Margin = Margin;
|
||||||
|
Brush.ImageSize = ImageSize;
|
||||||
|
Brush.TintColor = FSlateColor(Tint);
|
||||||
|
if (UTexture2D* Texture = LoadObject<UTexture2D>(nullptr, TexturePath))
|
||||||
|
{
|
||||||
|
Brush.SetResourceObject(Texture);
|
||||||
|
}
|
||||||
|
return Brush;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
UPHYAttributeRowWidget::UPHYAttributeRowWidget(const FObjectInitializer& ObjectInitializer)
|
||||||
|
: Super(ObjectInitializer)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
TSharedRef<SWidget> UPHYAttributeRowWidget::RebuildWidget()
|
||||||
|
{
|
||||||
|
BuildNativeWidgetTree();
|
||||||
|
return Super::RebuildWidget();
|
||||||
|
}
|
||||||
|
|
||||||
|
void UPHYAttributeRowWidget::SynchronizeProperties()
|
||||||
|
{
|
||||||
|
Super::SynchronizeProperties();
|
||||||
|
UpdateRowWidgets();
|
||||||
|
}
|
||||||
|
|
||||||
|
void UPHYAttributeRowWidget::SetRow(FText InLabel, FText InValue, const bool bInCanUpgrade)
|
||||||
|
{
|
||||||
|
Label = MoveTemp(InLabel);
|
||||||
|
Value = MoveTemp(InValue);
|
||||||
|
bCanUpgrade = bInCanUpgrade;
|
||||||
|
UpdateRowWidgets();
|
||||||
|
}
|
||||||
|
|
||||||
|
void UPHYAttributeRowWidget::BuildNativeWidgetTree()
|
||||||
|
{
|
||||||
|
if (!WidgetTree)
|
||||||
|
{
|
||||||
|
WidgetTree = NewObject<UWidgetTree>(this, TEXT("WidgetTree"), RF_Transient);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (WidgetTree->RootWidget && RowBorder && RowBox && LabelText && ValueText && UpgradeButton && UpgradeText)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
RowBorder = WidgetTree->ConstructWidget<UBorder>(UBorder::StaticClass(), TEXT("RowBorder"));
|
||||||
|
RowBorder->SetBrush(MakeAttributeRowBrush(AttributeRowTexturePath, FVector2D(512.0f, 64.0f), FLinearColor(1.0f, 0.98f, 0.90f, 0.42f)));
|
||||||
|
RowBorder->SetPadding(FMargin(9.0f, 3.0f));
|
||||||
|
WidgetTree->RootWidget = RowBorder;
|
||||||
|
|
||||||
|
RowBox = WidgetTree->ConstructWidget<UHorizontalBox>(UHorizontalBox::StaticClass(), TEXT("RowBox"));
|
||||||
|
RowBorder->SetContent(RowBox);
|
||||||
|
|
||||||
|
LabelText = WidgetTree->ConstructWidget<UTextBlock>(UTextBlock::StaticClass(), TEXT("LabelText"));
|
||||||
|
LabelText->SetFont(FSlateFontInfo(FCoreStyle::GetDefaultFont(), 16));
|
||||||
|
LabelText->SetColorAndOpacity(FSlateColor(FLinearColor(0.31f, 0.25f, 0.16f, 1.0f)));
|
||||||
|
if (UHorizontalBoxSlot* LabelSlot = RowBox->AddChildToHorizontalBox(LabelText))
|
||||||
|
{
|
||||||
|
LabelSlot->SetSize(FSlateChildSize(ESlateSizeRule::Fill));
|
||||||
|
LabelSlot->SetPadding(FMargin(0.0f, 3.0f, 10.0f, 3.0f));
|
||||||
|
LabelSlot->SetVerticalAlignment(VAlign_Center);
|
||||||
|
}
|
||||||
|
|
||||||
|
ValueText = WidgetTree->ConstructWidget<UTextBlock>(UTextBlock::StaticClass(), TEXT("ValueText"));
|
||||||
|
ValueText->SetFont(FSlateFontInfo(FCoreStyle::GetDefaultFont(), 16));
|
||||||
|
ValueText->SetColorAndOpacity(FSlateColor(FLinearColor(0.08f, 0.36f, 0.28f, 1.0f)));
|
||||||
|
if (UHorizontalBoxSlot* ValueSlot = RowBox->AddChildToHorizontalBox(ValueText))
|
||||||
|
{
|
||||||
|
ValueSlot->SetSize(FSlateChildSize(ESlateSizeRule::Automatic));
|
||||||
|
ValueSlot->SetPadding(FMargin(0.0f, 3.0f, 12.0f, 3.0f));
|
||||||
|
ValueSlot->SetVerticalAlignment(VAlign_Center);
|
||||||
|
}
|
||||||
|
|
||||||
|
UpgradeButton = WidgetTree->ConstructWidget<UButton>(UButton::StaticClass(), TEXT("UpgradeButton"));
|
||||||
|
FButtonStyle UpgradeButtonStyle;
|
||||||
|
UpgradeButtonStyle.SetNormal(MakeAttributeRowBrush(AttributeRowCinnabarTexturePath, FVector2D(34.0f, 34.0f), FLinearColor(0.20f, 0.35f, 0.31f, 0.96f)));
|
||||||
|
UpgradeButtonStyle.SetHovered(MakeAttributeRowBrush(AttributeRowCinnabarTexturePath, FVector2D(34.0f, 34.0f), FLinearColor(0.27f, 0.48f, 0.42f, 1.0f)));
|
||||||
|
UpgradeButtonStyle.SetPressed(MakeAttributeRowBrush(AttributeRowCinnabarTexturePath, FVector2D(34.0f, 34.0f), FLinearColor(0.74f, 0.24f, 0.13f, 1.0f)));
|
||||||
|
UpgradeButton->SetStyle(UpgradeButtonStyle);
|
||||||
|
UpgradeText = WidgetTree->ConstructWidget<UTextBlock>(UTextBlock::StaticClass(), TEXT("UpgradeText"));
|
||||||
|
UpgradeText->SetText(FText::FromString(TEXT("+")));
|
||||||
|
UpgradeText->SetFont(FSlateFontInfo(FCoreStyle::GetDefaultFont(), 20));
|
||||||
|
UpgradeText->SetColorAndOpacity(FSlateColor(FLinearColor(0.98f, 0.92f, 0.78f, 1.0f)));
|
||||||
|
UpgradeButton->SetContent(UpgradeText);
|
||||||
|
if (UHorizontalBoxSlot* UpgradeSlot = RowBox->AddChildToHorizontalBox(UpgradeButton))
|
||||||
|
{
|
||||||
|
UpgradeSlot->SetSize(FSlateChildSize(ESlateSizeRule::Automatic));
|
||||||
|
UpgradeSlot->SetPadding(FMargin(0.0f, 3.0f, 0.0f, 3.0f));
|
||||||
|
UpgradeSlot->SetVerticalAlignment(VAlign_Center);
|
||||||
|
}
|
||||||
|
|
||||||
|
UpdateRowWidgets();
|
||||||
|
}
|
||||||
|
|
||||||
|
void UPHYAttributeRowWidget::UpdateRowWidgets()
|
||||||
|
{
|
||||||
|
if (LabelText)
|
||||||
|
{
|
||||||
|
LabelText->SetText(Label);
|
||||||
|
}
|
||||||
|
if (ValueText)
|
||||||
|
{
|
||||||
|
ValueText->SetText(Value);
|
||||||
|
}
|
||||||
|
if (UpgradeButton)
|
||||||
|
{
|
||||||
|
UpgradeButton->SetVisibility(bCanUpgrade ? ESlateVisibility::Visible : ESlateVisibility::Collapsed);
|
||||||
|
}
|
||||||
|
}
|
||||||
207
Source/PHY/Private/UI/PHYAttributeSummaryWidget.cpp
Normal file
207
Source/PHY/Private/UI/PHYAttributeSummaryWidget.cpp
Normal file
@@ -0,0 +1,207 @@
|
|||||||
|
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||||
|
|
||||||
|
#include "UI/PHYAttributeSummaryWidget.h"
|
||||||
|
|
||||||
|
#include UE_INLINE_GENERATED_CPP_BY_NAME(PHYAttributeSummaryWidget)
|
||||||
|
|
||||||
|
#include "Blueprint/WidgetTree.h"
|
||||||
|
#include "Components/Border.h"
|
||||||
|
#include "Components/HorizontalBox.h"
|
||||||
|
#include "Components/HorizontalBoxSlot.h"
|
||||||
|
#include "Components/SizeBox.h"
|
||||||
|
#include "Components/Spacer.h"
|
||||||
|
#include "Components/TextBlock.h"
|
||||||
|
#include "Components/VerticalBox.h"
|
||||||
|
#include "Components/VerticalBoxSlot.h"
|
||||||
|
#include "Engine/Texture2D.h"
|
||||||
|
#include "Fonts/SlateFontInfo.h"
|
||||||
|
#include "Styling/CoreStyle.h"
|
||||||
|
#include "Styling/SlateBrush.h"
|
||||||
|
#include "UI/PHYAttributeResourceBarWidget.h"
|
||||||
|
#include "Widgets/SWidget.h"
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
const TCHAR* const AttributeSummaryJadeTexturePath = TEXT("/Game/AGame/UI/Attributes/Textures/T_PHY_UI_JadePanel.T_PHY_UI_JadePanel");
|
||||||
|
const TCHAR* const AttributeSummaryPortraitTexturePath = TEXT("/Game/AGame/UI/Attributes/Textures/T_PHY_UI_PortraitFrame.T_PHY_UI_PortraitFrame");
|
||||||
|
|
||||||
|
FSlateBrush MakeAttributeSummaryBrush(const TCHAR* TexturePath, const FVector2D ImageSize, const FLinearColor Tint, const ESlateBrushDrawType::Type DrawAs = ESlateBrushDrawType::Box, const FMargin Margin = FMargin(0.08f))
|
||||||
|
{
|
||||||
|
FSlateBrush Brush;
|
||||||
|
Brush.DrawAs = DrawAs;
|
||||||
|
Brush.Margin = Margin;
|
||||||
|
Brush.ImageSize = ImageSize;
|
||||||
|
Brush.TintColor = FSlateColor(Tint);
|
||||||
|
if (UTexture2D* Texture = LoadObject<UTexture2D>(nullptr, TexturePath))
|
||||||
|
{
|
||||||
|
Brush.SetResourceObject(Texture);
|
||||||
|
}
|
||||||
|
return Brush;
|
||||||
|
}
|
||||||
|
|
||||||
|
FText MakeAttributeSummaryDisplayName(const FText& SourceName)
|
||||||
|
{
|
||||||
|
FString NameString = SourceName.IsEmpty() ? FString(TEXT("修行者")) : SourceName.ToString();
|
||||||
|
if (NameString.Len() > 10)
|
||||||
|
{
|
||||||
|
NameString = NameString.Left(10) + TEXT("...");
|
||||||
|
}
|
||||||
|
return FText::FromString(NameString);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
UPHYAttributeSummaryWidget::UPHYAttributeSummaryWidget(const FObjectInitializer& ObjectInitializer)
|
||||||
|
: Super(ObjectInitializer)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
TSharedRef<SWidget> UPHYAttributeSummaryWidget::RebuildWidget()
|
||||||
|
{
|
||||||
|
BuildNativeWidgetTree();
|
||||||
|
return Super::RebuildWidget();
|
||||||
|
}
|
||||||
|
|
||||||
|
void UPHYAttributeSummaryWidget::SynchronizeProperties()
|
||||||
|
{
|
||||||
|
Super::SynchronizeProperties();
|
||||||
|
UpdateSummaryWidgets();
|
||||||
|
}
|
||||||
|
|
||||||
|
void UPHYAttributeSummaryWidget::SetSummary(FText InPlayerName, const int32 InLevel, const int32 InExperience, const int32 InExperienceForNextLevel, const int32 InCombatPower)
|
||||||
|
{
|
||||||
|
PlayerName = MoveTemp(InPlayerName);
|
||||||
|
Level = FMath::Max(1, InLevel);
|
||||||
|
Experience = FMath::Max(0, InExperience);
|
||||||
|
ExperienceForNextLevel = FMath::Max(1, InExperienceForNextLevel);
|
||||||
|
CombatPower = FMath::Max(0, InCombatPower);
|
||||||
|
UpdateSummaryWidgets();
|
||||||
|
}
|
||||||
|
|
||||||
|
void UPHYAttributeSummaryWidget::SetResources(const float InHealth, const float InMaxHealth, const float InMana, const float InMaxMana, const float InStamina, const float InMaxStamina)
|
||||||
|
{
|
||||||
|
Health = InHealth;
|
||||||
|
MaxHealth = FMath::Max(1.0f, InMaxHealth);
|
||||||
|
Mana = InMana;
|
||||||
|
MaxMana = FMath::Max(1.0f, InMaxMana);
|
||||||
|
Stamina = InStamina;
|
||||||
|
MaxStamina = FMath::Max(1.0f, InMaxStamina);
|
||||||
|
UpdateSummaryWidgets();
|
||||||
|
}
|
||||||
|
|
||||||
|
void UPHYAttributeSummaryWidget::BuildNativeWidgetTree()
|
||||||
|
{
|
||||||
|
if (!WidgetTree)
|
||||||
|
{
|
||||||
|
WidgetTree = NewObject<UWidgetTree>(this, TEXT("WidgetTree"), RF_Transient);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (WidgetTree->RootWidget && PlayerNameText && LevelText && CombatPowerText && ExperienceBar && ResourceBox)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
UBorder* RootBorder = WidgetTree->ConstructWidget<UBorder>(UBorder::StaticClass(), TEXT("RootBorder"));
|
||||||
|
RootBorder->SetBrushColor(FLinearColor(1.0f, 1.0f, 1.0f, 0.0f));
|
||||||
|
RootBorder->SetPadding(FMargin(0.0f));
|
||||||
|
WidgetTree->RootWidget = RootBorder;
|
||||||
|
|
||||||
|
UVerticalBox* RootBox = WidgetTree->ConstructWidget<UVerticalBox>(UVerticalBox::StaticClass(), TEXT("RootBox"));
|
||||||
|
RootBorder->SetContent(RootBox);
|
||||||
|
|
||||||
|
UHorizontalBox* HeaderBox = WidgetTree->ConstructWidget<UHorizontalBox>(UHorizontalBox::StaticClass(), TEXT("HeaderBox"));
|
||||||
|
if (UVerticalBoxSlot* HeaderSlot = RootBox->AddChildToVerticalBox(HeaderBox))
|
||||||
|
{
|
||||||
|
HeaderSlot->SetPadding(FMargin(0.0f, 0.0f, 0.0f, 14.0f));
|
||||||
|
}
|
||||||
|
|
||||||
|
UBorder* LevelSeal = WidgetTree->ConstructWidget<UBorder>(UBorder::StaticClass(), TEXT("LevelSeal"));
|
||||||
|
LevelSeal->SetBrush(MakeAttributeSummaryBrush(AttributeSummaryJadeTexturePath, FVector2D(92.0f, 92.0f), FLinearColor(0.18f, 0.33f, 0.30f, 0.96f)));
|
||||||
|
LevelSeal->SetPadding(FMargin(10.0f));
|
||||||
|
if (UHorizontalBoxSlot* SealSlot = HeaderBox->AddChildToHorizontalBox(LevelSeal))
|
||||||
|
{
|
||||||
|
SealSlot->SetSize(FSlateChildSize(ESlateSizeRule::Automatic));
|
||||||
|
SealSlot->SetPadding(FMargin(0.0f, 0.0f, 18.0f, 0.0f));
|
||||||
|
SealSlot->SetVerticalAlignment(VAlign_Center);
|
||||||
|
}
|
||||||
|
|
||||||
|
LevelText = WidgetTree->ConstructWidget<UTextBlock>(UTextBlock::StaticClass(), TEXT("LevelText"));
|
||||||
|
LevelText->SetFont(FSlateFontInfo(FCoreStyle::GetDefaultFont(), 31));
|
||||||
|
LevelText->SetColorAndOpacity(FSlateColor(FLinearColor(1.0f, 0.86f, 0.55f, 1.0f)));
|
||||||
|
LevelSeal->SetContent(LevelText);
|
||||||
|
|
||||||
|
UVerticalBox* HeaderTextBox = WidgetTree->ConstructWidget<UVerticalBox>(UVerticalBox::StaticClass(), TEXT("HeaderTextBox"));
|
||||||
|
if (UHorizontalBoxSlot* HeaderTextSlot = HeaderBox->AddChildToHorizontalBox(HeaderTextBox))
|
||||||
|
{
|
||||||
|
HeaderTextSlot->SetSize(FSlateChildSize(ESlateSizeRule::Fill));
|
||||||
|
HeaderTextSlot->SetVerticalAlignment(VAlign_Center);
|
||||||
|
}
|
||||||
|
|
||||||
|
PlayerNameText = WidgetTree->ConstructWidget<UTextBlock>(UTextBlock::StaticClass(), TEXT("PlayerNameText"));
|
||||||
|
PlayerNameText->SetFont(FSlateFontInfo(FCoreStyle::GetDefaultFont(), 24));
|
||||||
|
PlayerNameText->SetColorAndOpacity(FSlateColor(FLinearColor(0.26f, 0.20f, 0.12f, 1.0f)));
|
||||||
|
HeaderTextBox->AddChildToVerticalBox(PlayerNameText);
|
||||||
|
|
||||||
|
CombatPowerText = WidgetTree->ConstructWidget<UTextBlock>(UTextBlock::StaticClass(), TEXT("CombatPowerText"));
|
||||||
|
CombatPowerText->SetFont(FSlateFontInfo(FCoreStyle::GetDefaultFont(), 17));
|
||||||
|
CombatPowerText->SetColorAndOpacity(FSlateColor(FLinearColor(0.60f, 0.36f, 0.11f, 1.0f)));
|
||||||
|
if (UVerticalBoxSlot* PowerSlot = HeaderTextBox->AddChildToVerticalBox(CombatPowerText))
|
||||||
|
{
|
||||||
|
PowerSlot->SetPadding(FMargin(0.0f, 8.0f, 0.0f, 0.0f));
|
||||||
|
}
|
||||||
|
|
||||||
|
USizeBox* PortraitSizeBox = WidgetTree->ConstructWidget<USizeBox>(USizeBox::StaticClass(), TEXT("PortraitSizeBox"));
|
||||||
|
PortraitSizeBox->SetHeightOverride(438.0f);
|
||||||
|
if (UVerticalBoxSlot* PortraitSlot = RootBox->AddChildToVerticalBox(PortraitSizeBox))
|
||||||
|
{
|
||||||
|
PortraitSlot->SetPadding(FMargin(0.0f, 2.0f, 0.0f, 18.0f));
|
||||||
|
}
|
||||||
|
|
||||||
|
UBorder* PortraitBorder = WidgetTree->ConstructWidget<UBorder>(UBorder::StaticClass(), TEXT("PortraitBorder"));
|
||||||
|
PortraitBorder->SetBrush(MakeAttributeSummaryBrush(AttributeSummaryPortraitTexturePath, FVector2D(380.0f, 438.0f), FLinearColor(1.0f, 1.0f, 1.0f, 0.98f), ESlateBrushDrawType::Image, FMargin(0.0f)));
|
||||||
|
PortraitBorder->SetPadding(FMargin(0.0f));
|
||||||
|
PortraitSizeBox->AddChild(PortraitBorder);
|
||||||
|
|
||||||
|
ResourceBox = WidgetTree->ConstructWidget<UVerticalBox>(UVerticalBox::StaticClass(), TEXT("ResourceBox"));
|
||||||
|
RootBox->AddChildToVerticalBox(ResourceBox);
|
||||||
|
|
||||||
|
ExperienceBar = WidgetTree->ConstructWidget<UPHYAttributeResourceBarWidget>(UPHYAttributeResourceBarWidget::StaticClass(), TEXT("ExperienceBar"));
|
||||||
|
HealthBar = WidgetTree->ConstructWidget<UPHYAttributeResourceBarWidget>(UPHYAttributeResourceBarWidget::StaticClass(), TEXT("HealthBar"));
|
||||||
|
ManaBar = WidgetTree->ConstructWidget<UPHYAttributeResourceBarWidget>(UPHYAttributeResourceBarWidget::StaticClass(), TEXT("ManaBar"));
|
||||||
|
StaminaBar = WidgetTree->ConstructWidget<UPHYAttributeResourceBarWidget>(UPHYAttributeResourceBarWidget::StaticClass(), TEXT("StaminaBar"));
|
||||||
|
|
||||||
|
ResourceBox->AddChildToVerticalBox(ExperienceBar);
|
||||||
|
|
||||||
|
UpdateSummaryWidgets();
|
||||||
|
}
|
||||||
|
|
||||||
|
void UPHYAttributeSummaryWidget::UpdateSummaryWidgets()
|
||||||
|
{
|
||||||
|
if (PlayerNameText)
|
||||||
|
{
|
||||||
|
PlayerNameText->SetText(MakeAttributeSummaryDisplayName(PlayerName));
|
||||||
|
}
|
||||||
|
if (LevelText)
|
||||||
|
{
|
||||||
|
LevelText->SetText(FText::FromString(FString::Printf(TEXT("%d"), Level)));
|
||||||
|
}
|
||||||
|
if (CombatPowerText)
|
||||||
|
{
|
||||||
|
CombatPowerText->SetText(FText::FromString(FString::Printf(TEXT("战斗评分\n%d"), CombatPower)));
|
||||||
|
}
|
||||||
|
if (ExperienceBar)
|
||||||
|
{
|
||||||
|
ExperienceBar->SetResource(FText::FromString(TEXT("经验")), static_cast<float>(Experience), static_cast<float>(ExperienceForNextLevel), FLinearColor(0.80f, 0.55f, 0.18f, 1.0f));
|
||||||
|
}
|
||||||
|
if (HealthBar)
|
||||||
|
{
|
||||||
|
HealthBar->SetResource(FText::FromString(TEXT("生命")), Health, MaxHealth, FLinearColor(0.72f, 0.16f, 0.12f, 1.0f));
|
||||||
|
}
|
||||||
|
if (ManaBar)
|
||||||
|
{
|
||||||
|
ManaBar->SetResource(FText::FromString(TEXT("法力")), Mana, MaxMana, FLinearColor(0.22f, 0.38f, 0.86f, 1.0f));
|
||||||
|
}
|
||||||
|
if (StaminaBar)
|
||||||
|
{
|
||||||
|
StaminaBar->SetResource(FText::FromString(TEXT("耐力")), Stamina, MaxStamina, FLinearColor(0.18f, 0.60f, 0.54f, 1.0f));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,72 @@
|
|||||||
|
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||||
|
|
||||||
|
#include "UI/PHYCombatDerivedAttributePageWidget.h"
|
||||||
|
|
||||||
|
#include UE_INLINE_GENERATED_CPP_BY_NAME(PHYCombatDerivedAttributePageWidget)
|
||||||
|
|
||||||
|
#include "AbilitySystemComponent.h"
|
||||||
|
#include "AbilitySystem/Attributes/PHYCombatAttributeSet.h"
|
||||||
|
#include "UI/PHYAttributeGroupWidget.h"
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
float GetCombatDerivedAttributeValue(const UAbilitySystemComponent* AbilitySystemComponent, const FGameplayAttribute& Attribute)
|
||||||
|
{
|
||||||
|
return AbilitySystemComponent ? AbilitySystemComponent->GetNumericAttribute(Attribute) : 0.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
FText MakeCombatNumberText(const float Value)
|
||||||
|
{
|
||||||
|
return FText::FromString(FString::Printf(TEXT("%.0f"), Value));
|
||||||
|
}
|
||||||
|
|
||||||
|
FText MakeCombatPercentText(const float Value)
|
||||||
|
{
|
||||||
|
return FText::FromString(FString::Printf(TEXT("%.1f%%"), Value * 100.0f));
|
||||||
|
}
|
||||||
|
|
||||||
|
FText MakeCombatMultiplierText(const float Value)
|
||||||
|
{
|
||||||
|
return FText::FromString(FString::Printf(TEXT("x%.2f"), Value));
|
||||||
|
}
|
||||||
|
|
||||||
|
void AddCombatAttributeRow(UPHYAttributeGroupWidget* GroupWidget, const TCHAR* Label, const FText& Value)
|
||||||
|
{
|
||||||
|
if (GroupWidget)
|
||||||
|
{
|
||||||
|
GroupWidget->AddRow(FText::FromString(Label), Value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void UPHYCombatDerivedAttributePageWidget::RefreshAttributes(UAbilitySystemComponent* AbilitySystemComponent)
|
||||||
|
{
|
||||||
|
SetPageTitle(FText::FromString(TEXT("战斗衍生属性")));
|
||||||
|
ClearGroups();
|
||||||
|
|
||||||
|
UPHYAttributeGroupWidget* ResourceGroup = AddGroup(FText::FromString(TEXT("资源上限")));
|
||||||
|
AddCombatAttributeRow(ResourceGroup, TEXT("当前生命"), MakeCombatNumberText(GetCombatDerivedAttributeValue(AbilitySystemComponent, UPHYCombatAttributeSet::GetHealthAttribute())));
|
||||||
|
AddCombatAttributeRow(ResourceGroup, TEXT("生命上限"), MakeCombatNumberText(GetCombatDerivedAttributeValue(AbilitySystemComponent, UPHYCombatAttributeSet::GetMaxHealthAttribute())));
|
||||||
|
AddCombatAttributeRow(ResourceGroup, TEXT("当前法力"), MakeCombatNumberText(GetCombatDerivedAttributeValue(AbilitySystemComponent, UPHYCombatAttributeSet::GetManaAttribute())));
|
||||||
|
AddCombatAttributeRow(ResourceGroup, TEXT("法力上限"), MakeCombatNumberText(GetCombatDerivedAttributeValue(AbilitySystemComponent, UPHYCombatAttributeSet::GetMaxManaAttribute())));
|
||||||
|
AddCombatAttributeRow(ResourceGroup, TEXT("当前耐力"), MakeCombatNumberText(GetCombatDerivedAttributeValue(AbilitySystemComponent, UPHYCombatAttributeSet::GetStaminaAttribute())));
|
||||||
|
AddCombatAttributeRow(ResourceGroup, TEXT("耐力上限"), MakeCombatNumberText(GetCombatDerivedAttributeValue(AbilitySystemComponent, UPHYCombatAttributeSet::GetMaxStaminaAttribute())));
|
||||||
|
|
||||||
|
UPHYAttributeGroupWidget* AttackGroup = AddGroup(FText::FromString(TEXT("攻击能力")));
|
||||||
|
AddCombatAttributeRow(AttackGroup, TEXT("物理攻击"), MakeCombatNumberText(GetCombatDerivedAttributeValue(AbilitySystemComponent, UPHYCombatAttributeSet::GetPhysicalAttackPowerAttribute())));
|
||||||
|
AddCombatAttributeRow(AttackGroup, TEXT("法术强度"), MakeCombatNumberText(GetCombatDerivedAttributeValue(AbilitySystemComponent, UPHYCombatAttributeSet::GetSpellPowerAttribute())));
|
||||||
|
AddCombatAttributeRow(AttackGroup, TEXT("命中率"), MakeCombatPercentText(GetCombatDerivedAttributeValue(AbilitySystemComponent, UPHYCombatAttributeSet::GetAccuracyAttribute())));
|
||||||
|
AddCombatAttributeRow(AttackGroup, TEXT("暴击率"), MakeCombatPercentText(GetCombatDerivedAttributeValue(AbilitySystemComponent, UPHYCombatAttributeSet::GetCriticalChanceAttribute())));
|
||||||
|
AddCombatAttributeRow(AttackGroup, TEXT("暴击伤害"), MakeCombatMultiplierText(GetCombatDerivedAttributeValue(AbilitySystemComponent, UPHYCombatAttributeSet::GetCriticalDamageAttribute())));
|
||||||
|
AddCombatAttributeRow(AttackGroup, TEXT("攻击速度"), MakeCombatMultiplierText(GetCombatDerivedAttributeValue(AbilitySystemComponent, UPHYCombatAttributeSet::GetAttackSpeedAttribute())));
|
||||||
|
AddCombatAttributeRow(AttackGroup, TEXT("冷却缩减"), MakeCombatPercentText(GetCombatDerivedAttributeValue(AbilitySystemComponent, UPHYCombatAttributeSet::GetCooldownReductionAttribute())));
|
||||||
|
AddCombatAttributeRow(AttackGroup, TEXT("破防强度"), MakeCombatNumberText(GetCombatDerivedAttributeValue(AbilitySystemComponent, UPHYCombatAttributeSet::GetGuardBreakPowerAttribute())));
|
||||||
|
AddCombatAttributeRow(AttackGroup, TEXT("韧性伤害"), MakeCombatNumberText(GetCombatDerivedAttributeValue(AbilitySystemComponent, UPHYCombatAttributeSet::GetPoiseDamageAttribute())));
|
||||||
|
|
||||||
|
UPHYAttributeGroupWidget* DefenseGroup = AddGroup(FText::FromString(TEXT("防御能力")));
|
||||||
|
AddCombatAttributeRow(DefenseGroup, TEXT("护甲"), MakeCombatNumberText(GetCombatDerivedAttributeValue(AbilitySystemComponent, UPHYCombatAttributeSet::GetArmorAttribute())));
|
||||||
|
AddCombatAttributeRow(DefenseGroup, TEXT("魔法抗性"), MakeCombatNumberText(GetCombatDerivedAttributeValue(AbilitySystemComponent, UPHYCombatAttributeSet::GetMagicResistanceAttribute())));
|
||||||
|
AddCombatAttributeRow(DefenseGroup, TEXT("闪避率"), MakeCombatPercentText(GetCombatDerivedAttributeValue(AbilitySystemComponent, UPHYCombatAttributeSet::GetEvasionAttribute())));
|
||||||
|
AddCombatAttributeRow(DefenseGroup, TEXT("格挡强度"), MakeCombatNumberText(GetCombatDerivedAttributeValue(AbilitySystemComponent, UPHYCombatAttributeSet::GetBlockPowerAttribute())));
|
||||||
|
AddCombatAttributeRow(DefenseGroup, TEXT("韧性"), MakeCombatNumberText(GetCombatDerivedAttributeValue(AbilitySystemComponent, UPHYCombatAttributeSet::GetPoiseAttribute())));
|
||||||
|
}
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||||
|
|
||||||
|
#include "UI/PHYElementDerivedAttributePageWidget.h"
|
||||||
|
|
||||||
|
#include UE_INLINE_GENERATED_CPP_BY_NAME(PHYElementDerivedAttributePageWidget)
|
||||||
|
|
||||||
|
#include "AbilitySystemComponent.h"
|
||||||
|
#include "AbilitySystem/Attributes/PHYElementAttributeSet.h"
|
||||||
|
#include "UI/PHYAttributeGroupWidget.h"
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
float GetElementDerivedAttributeValue(const UAbilitySystemComponent* AbilitySystemComponent, const FGameplayAttribute& Attribute)
|
||||||
|
{
|
||||||
|
return AbilitySystemComponent ? AbilitySystemComponent->GetNumericAttribute(Attribute) : 0.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
FText MakeElementPercentText(const float Value)
|
||||||
|
{
|
||||||
|
return FText::FromString(FString::Printf(TEXT("%.1f%%"), Value * 100.0f));
|
||||||
|
}
|
||||||
|
|
||||||
|
void AddElementAttributeRow(UPHYAttributeGroupWidget* GroupWidget, const TCHAR* Label, const FText& Value)
|
||||||
|
{
|
||||||
|
if (GroupWidget)
|
||||||
|
{
|
||||||
|
GroupWidget->AddRow(FText::FromString(Label), Value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void UPHYElementDerivedAttributePageWidget::RefreshAttributes(UAbilitySystemComponent* AbilitySystemComponent)
|
||||||
|
{
|
||||||
|
SetPageTitle(FText::FromString(TEXT("元素衍生属性")));
|
||||||
|
ClearGroups();
|
||||||
|
|
||||||
|
UPHYAttributeGroupWidget* BonusGroup = AddGroup(FText::FromString(TEXT("元素伤害加成")));
|
||||||
|
AddElementAttributeRow(BonusGroup, TEXT("火伤加成"), MakeElementPercentText(GetElementDerivedAttributeValue(AbilitySystemComponent, UPHYElementAttributeSet::GetFireDamageBonusAttribute())));
|
||||||
|
AddElementAttributeRow(BonusGroup, TEXT("水伤加成"), MakeElementPercentText(GetElementDerivedAttributeValue(AbilitySystemComponent, UPHYElementAttributeSet::GetWaterDamageBonusAttribute())));
|
||||||
|
AddElementAttributeRow(BonusGroup, TEXT("冰伤加成"), MakeElementPercentText(GetElementDerivedAttributeValue(AbilitySystemComponent, UPHYElementAttributeSet::GetIceDamageBonusAttribute())));
|
||||||
|
AddElementAttributeRow(BonusGroup, TEXT("雷伤加成"), MakeElementPercentText(GetElementDerivedAttributeValue(AbilitySystemComponent, UPHYElementAttributeSet::GetLightningDamageBonusAttribute())));
|
||||||
|
AddElementAttributeRow(BonusGroup, TEXT("地伤加成"), MakeElementPercentText(GetElementDerivedAttributeValue(AbilitySystemComponent, UPHYElementAttributeSet::GetEarthDamageBonusAttribute())));
|
||||||
|
AddElementAttributeRow(BonusGroup, TEXT("风伤加成"), MakeElementPercentText(GetElementDerivedAttributeValue(AbilitySystemComponent, UPHYElementAttributeSet::GetWindDamageBonusAttribute())));
|
||||||
|
AddElementAttributeRow(BonusGroup, TEXT("光伤加成"), MakeElementPercentText(GetElementDerivedAttributeValue(AbilitySystemComponent, UPHYElementAttributeSet::GetLightDamageBonusAttribute())));
|
||||||
|
AddElementAttributeRow(BonusGroup, TEXT("暗伤加成"), MakeElementPercentText(GetElementDerivedAttributeValue(AbilitySystemComponent, UPHYElementAttributeSet::GetDarkDamageBonusAttribute())));
|
||||||
|
AddElementAttributeRow(BonusGroup, TEXT("元素穿透"), MakeElementPercentText(GetElementDerivedAttributeValue(AbilitySystemComponent, UPHYElementAttributeSet::GetElementPenetrationAttribute())));
|
||||||
|
|
||||||
|
UPHYAttributeGroupWidget* ResistanceGroup = AddGroup(FText::FromString(TEXT("元素抗性")));
|
||||||
|
AddElementAttributeRow(ResistanceGroup, TEXT("火抗"), MakeElementPercentText(GetElementDerivedAttributeValue(AbilitySystemComponent, UPHYElementAttributeSet::GetFireResistanceAttribute())));
|
||||||
|
AddElementAttributeRow(ResistanceGroup, TEXT("水抗"), MakeElementPercentText(GetElementDerivedAttributeValue(AbilitySystemComponent, UPHYElementAttributeSet::GetWaterResistanceAttribute())));
|
||||||
|
AddElementAttributeRow(ResistanceGroup, TEXT("冰抗"), MakeElementPercentText(GetElementDerivedAttributeValue(AbilitySystemComponent, UPHYElementAttributeSet::GetIceResistanceAttribute())));
|
||||||
|
AddElementAttributeRow(ResistanceGroup, TEXT("雷抗"), MakeElementPercentText(GetElementDerivedAttributeValue(AbilitySystemComponent, UPHYElementAttributeSet::GetLightningResistanceAttribute())));
|
||||||
|
AddElementAttributeRow(ResistanceGroup, TEXT("地抗"), MakeElementPercentText(GetElementDerivedAttributeValue(AbilitySystemComponent, UPHYElementAttributeSet::GetEarthResistanceAttribute())));
|
||||||
|
AddElementAttributeRow(ResistanceGroup, TEXT("风抗"), MakeElementPercentText(GetElementDerivedAttributeValue(AbilitySystemComponent, UPHYElementAttributeSet::GetWindResistanceAttribute())));
|
||||||
|
AddElementAttributeRow(ResistanceGroup, TEXT("光抗"), MakeElementPercentText(GetElementDerivedAttributeValue(AbilitySystemComponent, UPHYElementAttributeSet::GetLightResistanceAttribute())));
|
||||||
|
AddElementAttributeRow(ResistanceGroup, TEXT("暗抗"), MakeElementPercentText(GetElementDerivedAttributeValue(AbilitySystemComponent, UPHYElementAttributeSet::GetDarkResistanceAttribute())));
|
||||||
|
}
|
||||||
93
Source/PHY/Private/UI/PHYGameUILayout.cpp
Normal file
93
Source/PHY/Private/UI/PHYGameUILayout.cpp
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||||
|
|
||||||
|
#include "UI/PHYGameUILayout.h"
|
||||||
|
|
||||||
|
#include UE_INLINE_GENERATED_CPP_BY_NAME(PHYGameUILayout)
|
||||||
|
|
||||||
|
#include "Blueprint/WidgetTree.h"
|
||||||
|
#include "Components/Overlay.h"
|
||||||
|
#include "Components/OverlaySlot.h"
|
||||||
|
#include "GameplayTags/PHYGameplayTags_UI.h"
|
||||||
|
#include "Widgets/CommonActivatableWidgetContainer.h"
|
||||||
|
#include "Widgets/SWidget.h"
|
||||||
|
|
||||||
|
UPHYGameUILayout::UPHYGameUILayout(const FObjectInitializer& ObjectInitializer)
|
||||||
|
: Super(ObjectInitializer)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
TSharedRef<SWidget> UPHYGameUILayout::RebuildWidget()
|
||||||
|
{
|
||||||
|
BuildNativeLayoutTree();
|
||||||
|
RegisterNativeLayers();
|
||||||
|
|
||||||
|
return Super::RebuildWidget();
|
||||||
|
}
|
||||||
|
|
||||||
|
void UPHYGameUILayout::BuildNativeLayoutTree()
|
||||||
|
{
|
||||||
|
if (!WidgetTree)
|
||||||
|
{
|
||||||
|
WidgetTree = NewObject<UWidgetTree>(this, TEXT("WidgetTree"), RF_Transient);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (WidgetTree->RootWidget && GameLayerStack && HUDLayerStack && MenuLayerStack && ModalLayerStack)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
UOverlay* RootOverlay = WidgetTree->ConstructWidget<UOverlay>(UOverlay::StaticClass(), TEXT("RootOverlay"));
|
||||||
|
WidgetTree->RootWidget = RootOverlay;
|
||||||
|
|
||||||
|
GameLayerStack = AddLayerStack(RootOverlay, TEXT("GameLayerStack"));
|
||||||
|
HUDLayerStack = AddLayerStack(RootOverlay, TEXT("HUDLayerStack"));
|
||||||
|
MenuLayerStack = AddLayerStack(RootOverlay, TEXT("MenuLayerStack"));
|
||||||
|
ModalLayerStack = AddLayerStack(RootOverlay, TEXT("ModalLayerStack"));
|
||||||
|
|
||||||
|
bNativeLayersRegistered = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
UCommonActivatableWidgetStack* UPHYGameUILayout::AddLayerStack(UOverlay* RootOverlay, const FName StackName) const
|
||||||
|
{
|
||||||
|
if (!RootOverlay || !WidgetTree)
|
||||||
|
{
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
UCommonActivatableWidgetStack* LayerStack = WidgetTree->ConstructWidget<UCommonActivatableWidgetStack>(UCommonActivatableWidgetStack::StaticClass(), StackName);
|
||||||
|
if (UOverlaySlot* LayerSlot = RootOverlay->AddChildToOverlay(LayerStack))
|
||||||
|
{
|
||||||
|
LayerSlot->SetHorizontalAlignment(HAlign_Fill);
|
||||||
|
LayerSlot->SetVerticalAlignment(VAlign_Fill);
|
||||||
|
LayerSlot->SetPadding(FMargin(0.0f));
|
||||||
|
}
|
||||||
|
|
||||||
|
return LayerStack;
|
||||||
|
}
|
||||||
|
|
||||||
|
void UPHYGameUILayout::RegisterNativeLayers()
|
||||||
|
{
|
||||||
|
if (bNativeLayersRegistered)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (GameLayerStack)
|
||||||
|
{
|
||||||
|
RegisterLayer(PHYGameplayTags::UI_Layer_Game, GameLayerStack);
|
||||||
|
}
|
||||||
|
if (HUDLayerStack)
|
||||||
|
{
|
||||||
|
RegisterLayer(PHYGameplayTags::UI_Layer_HUD, HUDLayerStack);
|
||||||
|
}
|
||||||
|
if (MenuLayerStack)
|
||||||
|
{
|
||||||
|
RegisterLayer(PHYGameplayTags::UI_Layer_Menu, MenuLayerStack);
|
||||||
|
}
|
||||||
|
if (ModalLayerStack)
|
||||||
|
{
|
||||||
|
RegisterLayer(PHYGameplayTags::UI_Layer_Modal, ModalLayerStack);
|
||||||
|
}
|
||||||
|
|
||||||
|
bNativeLayersRegistered = true;
|
||||||
|
}
|
||||||
37
Source/PHY/Private/UI/PHYGameUIPolicy.cpp
Normal file
37
Source/PHY/Private/UI/PHYGameUIPolicy.cpp
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||||
|
|
||||||
|
#include "UI/PHYGameUIPolicy.h"
|
||||||
|
|
||||||
|
#include UE_INLINE_GENERATED_CPP_BY_NAME(PHYGameUIPolicy)
|
||||||
|
|
||||||
|
#include "PHYConfigSettings.h"
|
||||||
|
#include "UI/PHYGameUILayout.h"
|
||||||
|
#include "UObject/UnrealType.h"
|
||||||
|
|
||||||
|
UPHYGameUIPolicy::UPHYGameUIPolicy(const FObjectInitializer& ObjectInitializer)
|
||||||
|
: Super(ObjectInitializer)
|
||||||
|
{
|
||||||
|
ApplyConfiguredLayoutClass();
|
||||||
|
}
|
||||||
|
|
||||||
|
void UPHYGameUIPolicy::ApplyConfiguredLayoutClass()
|
||||||
|
{
|
||||||
|
TSubclassOf<UGUIS_GameUILayout> SelectedLayoutClass = UPHYGameUILayout::StaticClass();
|
||||||
|
|
||||||
|
if (const UPHYUISettings* UISettings = GetDefault<UPHYUISettings>())
|
||||||
|
{
|
||||||
|
if (!UISettings->RootLayoutClass.IsNull())
|
||||||
|
{
|
||||||
|
if (UClass* LoadedLayoutClass = UISettings->RootLayoutClass.LoadSynchronous())
|
||||||
|
{
|
||||||
|
SelectedLayoutClass = LoadedLayoutClass;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GenericUISystem 将 LayoutClass 设为私有 EditAnywhere 属性;这里通过反射写入 C++ 默认布局,避免创建仅用于赋默认值的蓝图资产。
|
||||||
|
if (const FSoftClassProperty* LayoutClassProperty = FindFProperty<FSoftClassProperty>(UGUIS_GameUIPolicy::StaticClass(), TEXT("LayoutClass")))
|
||||||
|
{
|
||||||
|
LayoutClassProperty->SetPropertyValue_InContainer(this, FSoftObjectPtr(SelectedLayoutClass.Get()));
|
||||||
|
}
|
||||||
|
}
|
||||||
420
Source/PHY/Private/UI/PHYHUDStatusWidget.cpp
Normal file
420
Source/PHY/Private/UI/PHYHUDStatusWidget.cpp
Normal file
@@ -0,0 +1,420 @@
|
|||||||
|
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||||
|
|
||||||
|
#include "UI/PHYHUDStatusWidget.h"
|
||||||
|
|
||||||
|
#include UE_INLINE_GENERATED_CPP_BY_NAME(PHYHUDStatusWidget)
|
||||||
|
|
||||||
|
#include "AbilitySystemComponent.h"
|
||||||
|
#include "AbilitySystem/Attributes/PHYCombatAttributeSet.h"
|
||||||
|
#include "Blueprint/WidgetTree.h"
|
||||||
|
#include "Components/Overlay.h"
|
||||||
|
#include "Components/OverlaySlot.h"
|
||||||
|
#include "Components/ProgressBar.h"
|
||||||
|
#include "Components/SafeZone.h"
|
||||||
|
#include "Components/TextBlock.h"
|
||||||
|
#include "Components/VerticalBox.h"
|
||||||
|
#include "Components/VerticalBoxSlot.h"
|
||||||
|
#include "Fonts/SlateFontInfo.h"
|
||||||
|
#include "GameFramework/PlayerController.h"
|
||||||
|
#include "GameplayEffectTypes.h"
|
||||||
|
#include "Player/PHYPlayerState.h"
|
||||||
|
#include "Styling/CoreStyle.h"
|
||||||
|
#include "Widgets/SWidget.h"
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
FText MakeResourceText(const TCHAR* Label, const float Value, const float MaxValue)
|
||||||
|
{
|
||||||
|
return FText::FromString(FString::Printf(TEXT("%s %.0f / %.0f"), Label, FMath::Max(0.0f, Value), FMath::Max(0.0f, MaxValue)));
|
||||||
|
}
|
||||||
|
|
||||||
|
FText MakeProgressionText(const int32 CurrentExperience, const int32 RequiredExperience)
|
||||||
|
{
|
||||||
|
return FText::FromString(FString::Printf(TEXT("XP %d / %d"), FMath::Max(0, CurrentExperience), FMath::Max(1, RequiredExperience)));
|
||||||
|
}
|
||||||
|
|
||||||
|
float MakeResourcePercent(const float Value, const float MaxValue)
|
||||||
|
{
|
||||||
|
return MaxValue > UE_SMALL_NUMBER ? FMath::Clamp(Value / MaxValue, 0.0f, 1.0f) : 0.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
float MakeProgressionPercent(const int32 CurrentExperience, const int32 RequiredExperience)
|
||||||
|
{
|
||||||
|
return RequiredExperience > 0 ? FMath::Clamp(static_cast<float>(CurrentExperience) / static_cast<float>(RequiredExperience), 0.0f, 1.0f) : 0.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RemoveAttributeChangedDelegate(UAbilitySystemComponent* AbilitySystemComponent, const FGameplayAttribute& Attribute, FDelegateHandle& DelegateHandle)
|
||||||
|
{
|
||||||
|
if (AbilitySystemComponent && DelegateHandle.IsValid())
|
||||||
|
{
|
||||||
|
AbilitySystemComponent->GetGameplayAttributeValueChangeDelegate(Attribute).Remove(DelegateHandle);
|
||||||
|
DelegateHandle.Reset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
UPHYHUDStatusWidget::UPHYHUDStatusWidget(const FObjectInitializer& ObjectInitializer)
|
||||||
|
: Super(ObjectInitializer)
|
||||||
|
{
|
||||||
|
InputConfig = EGUIS_ActivatableWidgetInputMode::Default;
|
||||||
|
}
|
||||||
|
|
||||||
|
TSharedRef<SWidget> UPHYHUDStatusWidget::RebuildWidget()
|
||||||
|
{
|
||||||
|
BuildNativeWidgetTree();
|
||||||
|
return Super::RebuildWidget();
|
||||||
|
}
|
||||||
|
|
||||||
|
void UPHYHUDStatusWidget::SynchronizeProperties()
|
||||||
|
{
|
||||||
|
Super::SynchronizeProperties();
|
||||||
|
UpdateResourceWidgets();
|
||||||
|
}
|
||||||
|
|
||||||
|
void UPHYHUDStatusWidget::InitializeFromPlayer(APlayerController* InPlayerController)
|
||||||
|
{
|
||||||
|
if (!InPlayerController)
|
||||||
|
{
|
||||||
|
BoundPlayerController.Reset();
|
||||||
|
UnbindFromPlayerState();
|
||||||
|
UnbindFromAbilitySystem();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
BoundPlayerController = InPlayerController;
|
||||||
|
RefreshFromBoundSources();
|
||||||
|
}
|
||||||
|
|
||||||
|
void UPHYHUDStatusWidget::RefreshFromBoundSources()
|
||||||
|
{
|
||||||
|
APlayerController* PlayerController = BoundPlayerController.Get();
|
||||||
|
if (!PlayerController)
|
||||||
|
{
|
||||||
|
UnbindFromPlayerState();
|
||||||
|
UnbindFromAbilitySystem();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
APHYPlayerState* PHYPlayerState = PlayerController->GetPlayerState<APHYPlayerState>();
|
||||||
|
BindToPlayerState(PHYPlayerState);
|
||||||
|
BindToAbilitySystem(PHYPlayerState ? PHYPlayerState->GetAbilitySystemComponent() : nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void UPHYHUDStatusWidget::SetHealth(const float NewHealth, const float NewMaxHealth)
|
||||||
|
{
|
||||||
|
Health = NewHealth;
|
||||||
|
MaxHealth = NewMaxHealth;
|
||||||
|
UpdateResourceWidgets();
|
||||||
|
}
|
||||||
|
|
||||||
|
void UPHYHUDStatusWidget::SetMana(const float NewMana, const float NewMaxMana)
|
||||||
|
{
|
||||||
|
Mana = NewMana;
|
||||||
|
MaxMana = NewMaxMana;
|
||||||
|
UpdateResourceWidgets();
|
||||||
|
}
|
||||||
|
|
||||||
|
void UPHYHUDStatusWidget::SetStamina(const float NewStamina, const float NewMaxStamina)
|
||||||
|
{
|
||||||
|
Stamina = NewStamina;
|
||||||
|
MaxStamina = NewMaxStamina;
|
||||||
|
UpdateResourceWidgets();
|
||||||
|
}
|
||||||
|
|
||||||
|
void UPHYHUDStatusWidget::SetProgression(const int32 NewLevel, const int32 NewExperience, const int32 NewExperienceForNextLevel)
|
||||||
|
{
|
||||||
|
Level = FMath::Max(1, NewLevel);
|
||||||
|
Experience = FMath::Max(0, NewExperience);
|
||||||
|
ExperienceForNextLevel = FMath::Max(1, NewExperienceForNextLevel);
|
||||||
|
UpdateResourceWidgets();
|
||||||
|
}
|
||||||
|
|
||||||
|
void UPHYHUDStatusWidget::NativeConstruct()
|
||||||
|
{
|
||||||
|
Super::NativeConstruct();
|
||||||
|
|
||||||
|
if (!BoundPlayerController.IsValid())
|
||||||
|
{
|
||||||
|
InitializeFromPlayer(GetOwningPlayer());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
RefreshFromBoundSources();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void UPHYHUDStatusWidget::NativeDestruct()
|
||||||
|
{
|
||||||
|
UnbindFromPlayerState();
|
||||||
|
UnbindFromAbilitySystem();
|
||||||
|
BoundPlayerController.Reset();
|
||||||
|
|
||||||
|
Super::NativeDestruct();
|
||||||
|
}
|
||||||
|
|
||||||
|
void UPHYHUDStatusWidget::NativeTick(const FGeometry& MyGeometry, const float InDeltaTime)
|
||||||
|
{
|
||||||
|
Super::NativeTick(MyGeometry, InDeltaTime);
|
||||||
|
|
||||||
|
APlayerController* PlayerController = BoundPlayerController.Get();
|
||||||
|
if (!PlayerController)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
APHYPlayerState* CurrentPlayerState = PlayerController->GetPlayerState<APHYPlayerState>();
|
||||||
|
if (CurrentPlayerState != BoundPlayerState.Get() || !BoundAbilitySystemComponent.IsValid())
|
||||||
|
{
|
||||||
|
RefreshFromBoundSources();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void UPHYHUDStatusWidget::BuildNativeWidgetTree()
|
||||||
|
{
|
||||||
|
if (!WidgetTree)
|
||||||
|
{
|
||||||
|
WidgetTree = NewObject<UWidgetTree>(this, TEXT("WidgetTree"), RF_Transient);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (WidgetTree->RootWidget && TitleText && LevelText && HealthText && HealthBar && ManaText && ManaBar && StaminaText && StaminaBar && ExperienceText && ExperienceBar)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
USafeZone* RootSafeZone = WidgetTree->ConstructWidget<USafeZone>(USafeZone::StaticClass(), TEXT("RootSafeZone"));
|
||||||
|
UOverlay* RootOverlay = WidgetTree->ConstructWidget<UOverlay>(UOverlay::StaticClass(), TEXT("RootOverlay"));
|
||||||
|
UVerticalBox* StatusBox = WidgetTree->ConstructWidget<UVerticalBox>(UVerticalBox::StaticClass(), TEXT("StatusBox"));
|
||||||
|
|
||||||
|
WidgetTree->RootWidget = RootSafeZone;
|
||||||
|
RootSafeZone->SetContent(RootOverlay);
|
||||||
|
|
||||||
|
if (UOverlaySlot* StatusSlot = RootOverlay->AddChildToOverlay(StatusBox))
|
||||||
|
{
|
||||||
|
StatusSlot->SetHorizontalAlignment(HAlign_Left);
|
||||||
|
StatusSlot->SetVerticalAlignment(VAlign_Top);
|
||||||
|
StatusSlot->SetPadding(FMargin(32.0f, 24.0f, 0.0f, 0.0f));
|
||||||
|
}
|
||||||
|
|
||||||
|
TitleText = WidgetTree->ConstructWidget<UTextBlock>(UTextBlock::StaticClass(), TEXT("TitleText"));
|
||||||
|
TitleText->SetText(FText::FromString(TEXT("PHY")));
|
||||||
|
TitleText->SetFont(FSlateFontInfo(FCoreStyle::GetDefaultFont(), 22));
|
||||||
|
TitleText->SetColorAndOpacity(FSlateColor(FLinearColor(0.85f, 0.95f, 1.0f, 1.0f)));
|
||||||
|
if (UVerticalBoxSlot* TitleSlot = StatusBox->AddChildToVerticalBox(TitleText))
|
||||||
|
{
|
||||||
|
TitleSlot->SetPadding(FMargin(0.0f, 0.0f, 0.0f, 4.0f));
|
||||||
|
}
|
||||||
|
|
||||||
|
LevelText = WidgetTree->ConstructWidget<UTextBlock>(UTextBlock::StaticClass(), TEXT("LevelText"));
|
||||||
|
LevelText->SetFont(FSlateFontInfo(FCoreStyle::GetDefaultFont(), 16));
|
||||||
|
LevelText->SetColorAndOpacity(FSlateColor(FLinearColor(0.95f, 0.95f, 0.95f, 1.0f)));
|
||||||
|
if (UVerticalBoxSlot* LevelSlot = StatusBox->AddChildToVerticalBox(LevelText))
|
||||||
|
{
|
||||||
|
LevelSlot->SetPadding(FMargin(0.0f, 0.0f, 0.0f, 8.0f));
|
||||||
|
}
|
||||||
|
|
||||||
|
HealthText = WidgetTree->ConstructWidget<UTextBlock>(UTextBlock::StaticClass(), TEXT("HealthText"));
|
||||||
|
HealthText->SetFont(FSlateFontInfo(FCoreStyle::GetDefaultFont(), 16));
|
||||||
|
HealthText->SetColorAndOpacity(FSlateColor(FLinearColor(0.95f, 0.95f, 0.95f, 1.0f)));
|
||||||
|
StatusBox->AddChildToVerticalBox(HealthText);
|
||||||
|
|
||||||
|
HealthBar = WidgetTree->ConstructWidget<UProgressBar>(UProgressBar::StaticClass(), TEXT("HealthBar"));
|
||||||
|
HealthBar->SetFillColorAndOpacity(FLinearColor(0.76f, 0.12f, 0.12f, 1.0f));
|
||||||
|
if (UVerticalBoxSlot* HealthBarSlot = StatusBox->AddChildToVerticalBox(HealthBar))
|
||||||
|
{
|
||||||
|
HealthBarSlot->SetPadding(FMargin(0.0f, 2.0f, 0.0f, 8.0f));
|
||||||
|
}
|
||||||
|
|
||||||
|
ManaText = WidgetTree->ConstructWidget<UTextBlock>(UTextBlock::StaticClass(), TEXT("ManaText"));
|
||||||
|
ManaText->SetFont(FSlateFontInfo(FCoreStyle::GetDefaultFont(), 16));
|
||||||
|
ManaText->SetColorAndOpacity(FSlateColor(FLinearColor(0.95f, 0.95f, 0.95f, 1.0f)));
|
||||||
|
StatusBox->AddChildToVerticalBox(ManaText);
|
||||||
|
|
||||||
|
ManaBar = WidgetTree->ConstructWidget<UProgressBar>(UProgressBar::StaticClass(), TEXT("ManaBar"));
|
||||||
|
ManaBar->SetFillColorAndOpacity(FLinearColor(0.26f, 0.34f, 0.86f, 1.0f));
|
||||||
|
if (UVerticalBoxSlot* ManaBarSlot = StatusBox->AddChildToVerticalBox(ManaBar))
|
||||||
|
{
|
||||||
|
ManaBarSlot->SetPadding(FMargin(0.0f, 2.0f, 0.0f, 8.0f));
|
||||||
|
}
|
||||||
|
|
||||||
|
StaminaText = WidgetTree->ConstructWidget<UTextBlock>(UTextBlock::StaticClass(), TEXT("StaminaText"));
|
||||||
|
StaminaText->SetFont(FSlateFontInfo(FCoreStyle::GetDefaultFont(), 16));
|
||||||
|
StaminaText->SetColorAndOpacity(FSlateColor(FLinearColor(0.95f, 0.95f, 0.95f, 1.0f)));
|
||||||
|
StatusBox->AddChildToVerticalBox(StaminaText);
|
||||||
|
|
||||||
|
StaminaBar = WidgetTree->ConstructWidget<UProgressBar>(UProgressBar::StaticClass(), TEXT("StaminaBar"));
|
||||||
|
StaminaBar->SetFillColorAndOpacity(FLinearColor(0.12f, 0.62f, 0.86f, 1.0f));
|
||||||
|
if (UVerticalBoxSlot* StaminaBarSlot = StatusBox->AddChildToVerticalBox(StaminaBar))
|
||||||
|
{
|
||||||
|
StaminaBarSlot->SetPadding(FMargin(0.0f, 2.0f, 0.0f, 8.0f));
|
||||||
|
}
|
||||||
|
|
||||||
|
ExperienceText = WidgetTree->ConstructWidget<UTextBlock>(UTextBlock::StaticClass(), TEXT("ExperienceText"));
|
||||||
|
ExperienceText->SetFont(FSlateFontInfo(FCoreStyle::GetDefaultFont(), 16));
|
||||||
|
ExperienceText->SetColorAndOpacity(FSlateColor(FLinearColor(0.95f, 0.95f, 0.95f, 1.0f)));
|
||||||
|
StatusBox->AddChildToVerticalBox(ExperienceText);
|
||||||
|
|
||||||
|
ExperienceBar = WidgetTree->ConstructWidget<UProgressBar>(UProgressBar::StaticClass(), TEXT("ExperienceBar"));
|
||||||
|
ExperienceBar->SetFillColorAndOpacity(FLinearColor(0.93f, 0.62f, 0.18f, 1.0f));
|
||||||
|
if (UVerticalBoxSlot* ExperienceBarSlot = StatusBox->AddChildToVerticalBox(ExperienceBar))
|
||||||
|
{
|
||||||
|
ExperienceBarSlot->SetPadding(FMargin(0.0f, 2.0f, 0.0f, 0.0f));
|
||||||
|
}
|
||||||
|
|
||||||
|
UpdateResourceWidgets();
|
||||||
|
}
|
||||||
|
|
||||||
|
void UPHYHUDStatusWidget::BindToPlayerState(APHYPlayerState* NewPlayerState)
|
||||||
|
{
|
||||||
|
if (BoundPlayerState.Get() == NewPlayerState)
|
||||||
|
{
|
||||||
|
RefreshProgressionFromPlayerState();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
UnbindFromPlayerState();
|
||||||
|
BoundPlayerState = NewPlayerState;
|
||||||
|
|
||||||
|
if (NewPlayerState)
|
||||||
|
{
|
||||||
|
NewPlayerState->OnProgressionChanged.AddUniqueDynamic(this, &UPHYHUDStatusWidget::HandleProgressionChanged);
|
||||||
|
RefreshProgressionFromPlayerState();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void UPHYHUDStatusWidget::UnbindFromPlayerState()
|
||||||
|
{
|
||||||
|
if (APHYPlayerState* PlayerState = BoundPlayerState.Get())
|
||||||
|
{
|
||||||
|
PlayerState->OnProgressionChanged.RemoveDynamic(this, &UPHYHUDStatusWidget::HandleProgressionChanged);
|
||||||
|
}
|
||||||
|
BoundPlayerState.Reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
void UPHYHUDStatusWidget::BindToAbilitySystem(UAbilitySystemComponent* NewAbilitySystemComponent)
|
||||||
|
{
|
||||||
|
if (BoundAbilitySystemComponent.Get() == NewAbilitySystemComponent && HealthChangedHandle.IsValid())
|
||||||
|
{
|
||||||
|
RefreshResourcesFromAbilitySystem();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
UnbindFromAbilitySystem();
|
||||||
|
BoundAbilitySystemComponent = NewAbilitySystemComponent;
|
||||||
|
|
||||||
|
if (!NewAbilitySystemComponent)
|
||||||
|
{
|
||||||
|
UpdateResourceWidgets();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
HealthChangedHandle = NewAbilitySystemComponent->GetGameplayAttributeValueChangeDelegate(UPHYCombatAttributeSet::GetHealthAttribute()).AddUObject(this, &UPHYHUDStatusWidget::HandleAttributeChanged);
|
||||||
|
MaxHealthChangedHandle = NewAbilitySystemComponent->GetGameplayAttributeValueChangeDelegate(UPHYCombatAttributeSet::GetMaxHealthAttribute()).AddUObject(this, &UPHYHUDStatusWidget::HandleAttributeChanged);
|
||||||
|
ManaChangedHandle = NewAbilitySystemComponent->GetGameplayAttributeValueChangeDelegate(UPHYCombatAttributeSet::GetManaAttribute()).AddUObject(this, &UPHYHUDStatusWidget::HandleAttributeChanged);
|
||||||
|
MaxManaChangedHandle = NewAbilitySystemComponent->GetGameplayAttributeValueChangeDelegate(UPHYCombatAttributeSet::GetMaxManaAttribute()).AddUObject(this, &UPHYHUDStatusWidget::HandleAttributeChanged);
|
||||||
|
StaminaChangedHandle = NewAbilitySystemComponent->GetGameplayAttributeValueChangeDelegate(UPHYCombatAttributeSet::GetStaminaAttribute()).AddUObject(this, &UPHYHUDStatusWidget::HandleAttributeChanged);
|
||||||
|
MaxStaminaChangedHandle = NewAbilitySystemComponent->GetGameplayAttributeValueChangeDelegate(UPHYCombatAttributeSet::GetMaxStaminaAttribute()).AddUObject(this, &UPHYHUDStatusWidget::HandleAttributeChanged);
|
||||||
|
|
||||||
|
RefreshResourcesFromAbilitySystem();
|
||||||
|
}
|
||||||
|
|
||||||
|
void UPHYHUDStatusWidget::UnbindFromAbilitySystem()
|
||||||
|
{
|
||||||
|
if (UAbilitySystemComponent* AbilitySystemComponent = BoundAbilitySystemComponent.Get())
|
||||||
|
{
|
||||||
|
RemoveAttributeChangedDelegate(AbilitySystemComponent, UPHYCombatAttributeSet::GetHealthAttribute(), HealthChangedHandle);
|
||||||
|
RemoveAttributeChangedDelegate(AbilitySystemComponent, UPHYCombatAttributeSet::GetMaxHealthAttribute(), MaxHealthChangedHandle);
|
||||||
|
RemoveAttributeChangedDelegate(AbilitySystemComponent, UPHYCombatAttributeSet::GetManaAttribute(), ManaChangedHandle);
|
||||||
|
RemoveAttributeChangedDelegate(AbilitySystemComponent, UPHYCombatAttributeSet::GetMaxManaAttribute(), MaxManaChangedHandle);
|
||||||
|
RemoveAttributeChangedDelegate(AbilitySystemComponent, UPHYCombatAttributeSet::GetStaminaAttribute(), StaminaChangedHandle);
|
||||||
|
RemoveAttributeChangedDelegate(AbilitySystemComponent, UPHYCombatAttributeSet::GetMaxStaminaAttribute(), MaxStaminaChangedHandle);
|
||||||
|
}
|
||||||
|
|
||||||
|
HealthChangedHandle.Reset();
|
||||||
|
MaxHealthChangedHandle.Reset();
|
||||||
|
ManaChangedHandle.Reset();
|
||||||
|
MaxManaChangedHandle.Reset();
|
||||||
|
StaminaChangedHandle.Reset();
|
||||||
|
MaxStaminaChangedHandle.Reset();
|
||||||
|
BoundAbilitySystemComponent.Reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
void UPHYHUDStatusWidget::RefreshResourcesFromAbilitySystem()
|
||||||
|
{
|
||||||
|
UAbilitySystemComponent* AbilitySystemComponent = BoundAbilitySystemComponent.Get();
|
||||||
|
if (!AbilitySystemComponent)
|
||||||
|
{
|
||||||
|
UpdateResourceWidgets();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Health = AbilitySystemComponent->GetNumericAttribute(UPHYCombatAttributeSet::GetHealthAttribute());
|
||||||
|
MaxHealth = AbilitySystemComponent->GetNumericAttribute(UPHYCombatAttributeSet::GetMaxHealthAttribute());
|
||||||
|
Mana = AbilitySystemComponent->GetNumericAttribute(UPHYCombatAttributeSet::GetManaAttribute());
|
||||||
|
MaxMana = AbilitySystemComponent->GetNumericAttribute(UPHYCombatAttributeSet::GetMaxManaAttribute());
|
||||||
|
Stamina = AbilitySystemComponent->GetNumericAttribute(UPHYCombatAttributeSet::GetStaminaAttribute());
|
||||||
|
MaxStamina = AbilitySystemComponent->GetNumericAttribute(UPHYCombatAttributeSet::GetMaxStaminaAttribute());
|
||||||
|
|
||||||
|
UpdateResourceWidgets();
|
||||||
|
}
|
||||||
|
|
||||||
|
void UPHYHUDStatusWidget::RefreshProgressionFromPlayerState()
|
||||||
|
{
|
||||||
|
APHYPlayerState* PlayerState = BoundPlayerState.Get();
|
||||||
|
if (!PlayerState)
|
||||||
|
{
|
||||||
|
UpdateResourceWidgets();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
SetProgression(PlayerState->GetPlayerLevel(), PlayerState->GetExperience(), PlayerState->GetExperienceForNextLevel());
|
||||||
|
}
|
||||||
|
|
||||||
|
void UPHYHUDStatusWidget::HandleAttributeChanged(const FOnAttributeChangeData& /*ChangeData*/)
|
||||||
|
{
|
||||||
|
RefreshResourcesFromAbilitySystem();
|
||||||
|
}
|
||||||
|
|
||||||
|
void UPHYHUDStatusWidget::HandleProgressionChanged(const int32 NewLevel, const int32 NewExperience, const int32 NewExperienceForNextLevel)
|
||||||
|
{
|
||||||
|
SetProgression(NewLevel, NewExperience, NewExperienceForNextLevel);
|
||||||
|
}
|
||||||
|
|
||||||
|
void UPHYHUDStatusWidget::UpdateResourceWidgets()
|
||||||
|
{
|
||||||
|
if (LevelText)
|
||||||
|
{
|
||||||
|
LevelText->SetText(FText::FromString(FString::Printf(TEXT("Lv %d"), FMath::Max(1, Level))));
|
||||||
|
}
|
||||||
|
if (HealthText)
|
||||||
|
{
|
||||||
|
HealthText->SetText(MakeResourceText(TEXT("HP"), Health, MaxHealth));
|
||||||
|
}
|
||||||
|
if (HealthBar)
|
||||||
|
{
|
||||||
|
HealthBar->SetPercent(MakeResourcePercent(Health, MaxHealth));
|
||||||
|
}
|
||||||
|
if (ManaText)
|
||||||
|
{
|
||||||
|
ManaText->SetText(MakeResourceText(TEXT("MP"), Mana, MaxMana));
|
||||||
|
}
|
||||||
|
if (ManaBar)
|
||||||
|
{
|
||||||
|
ManaBar->SetPercent(MakeResourcePercent(Mana, MaxMana));
|
||||||
|
}
|
||||||
|
if (StaminaText)
|
||||||
|
{
|
||||||
|
StaminaText->SetText(MakeResourceText(TEXT("ST"), Stamina, MaxStamina));
|
||||||
|
}
|
||||||
|
if (StaminaBar)
|
||||||
|
{
|
||||||
|
StaminaBar->SetPercent(MakeResourcePercent(Stamina, MaxStamina));
|
||||||
|
}
|
||||||
|
if (ExperienceText)
|
||||||
|
{
|
||||||
|
ExperienceText->SetText(MakeProgressionText(Experience, ExperienceForNextLevel));
|
||||||
|
}
|
||||||
|
if (ExperienceBar)
|
||||||
|
{
|
||||||
|
ExperienceBar->SetPercent(MakeProgressionPercent(Experience, ExperienceForNextLevel));
|
||||||
|
}
|
||||||
|
}
|
||||||
18
Source/PHY/Private/UI/PHYPlayerUIContext.cpp
Normal file
18
Source/PHY/Private/UI/PHYPlayerUIContext.cpp
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||||
|
|
||||||
|
#include "UI/PHYPlayerUIContext.h"
|
||||||
|
|
||||||
|
#include UE_INLINE_GENERATED_CPP_BY_NAME(PHYPlayerUIContext)
|
||||||
|
|
||||||
|
#include "GameFramework/PlayerController.h"
|
||||||
|
|
||||||
|
void UPHYPlayerUIContext::InitializeForPlayer(APlayerController* InPlayerController)
|
||||||
|
{
|
||||||
|
PlayerController = InPlayerController;
|
||||||
|
RefreshPawn();
|
||||||
|
}
|
||||||
|
|
||||||
|
void UPHYPlayerUIContext::RefreshPawn()
|
||||||
|
{
|
||||||
|
Pawn = PlayerController.IsValid() ? PlayerController->GetPawn() : nullptr;
|
||||||
|
}
|
||||||
@@ -0,0 +1,167 @@
|
|||||||
|
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "CoreMinimal.h"
|
||||||
|
#include "Kismet/BlueprintFunctionLibrary.h"
|
||||||
|
#include "AbilitySystem/Attributes/PHYAttributeSettings.h"
|
||||||
|
#include "PHYAttributeCalculationLibrary.generated.h"
|
||||||
|
|
||||||
|
class UPHYCoreAttributeSet;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 基础属性输入快照,用于 C++、GE 和后续 MMC 共享公式。
|
||||||
|
*/
|
||||||
|
USTRUCT(BlueprintType)
|
||||||
|
struct FPHYCoreAttributeSnapshot
|
||||||
|
{
|
||||||
|
GENERATED_BODY()
|
||||||
|
|
||||||
|
/** @brief 力量。 */
|
||||||
|
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="PHY|Attributes|Core")
|
||||||
|
float Strength = 0.0f;
|
||||||
|
|
||||||
|
/** @brief 灵巧。 */
|
||||||
|
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="PHY|Attributes|Core")
|
||||||
|
float Dexterity = 0.0f;
|
||||||
|
|
||||||
|
/** @brief 体魄。 */
|
||||||
|
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="PHY|Attributes|Core")
|
||||||
|
float Vitality = 0.0f;
|
||||||
|
|
||||||
|
/** @brief 智力。 */
|
||||||
|
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="PHY|Attributes|Core")
|
||||||
|
float Intelligence = 0.0f;
|
||||||
|
|
||||||
|
/** @brief 精神。 */
|
||||||
|
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="PHY|Attributes|Core")
|
||||||
|
float Spirit = 0.0f;
|
||||||
|
|
||||||
|
/** @brief 感知。 */
|
||||||
|
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="PHY|Attributes|Core")
|
||||||
|
float Perception = 0.0f;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 次级战斗属性计算结果。
|
||||||
|
*/
|
||||||
|
USTRUCT(BlueprintType)
|
||||||
|
struct FPHYDerivedCombatAttributes
|
||||||
|
{
|
||||||
|
GENERATED_BODY()
|
||||||
|
|
||||||
|
/** @brief 推导后的生命上限。 */
|
||||||
|
UPROPERTY(BlueprintReadOnly, Category="PHY|Attributes|Combat")
|
||||||
|
float MaxHealth = 0.0f;
|
||||||
|
|
||||||
|
/** @brief 推导后的法力上限。 */
|
||||||
|
UPROPERTY(BlueprintReadOnly, Category="PHY|Attributes|Combat")
|
||||||
|
float MaxMana = 0.0f;
|
||||||
|
|
||||||
|
/** @brief 推导后的耐力上限。 */
|
||||||
|
UPROPERTY(BlueprintReadOnly, Category="PHY|Attributes|Combat")
|
||||||
|
float MaxStamina = 0.0f;
|
||||||
|
|
||||||
|
/** @brief 推导后的物理攻击强度。 */
|
||||||
|
UPROPERTY(BlueprintReadOnly, Category="PHY|Attributes|Combat")
|
||||||
|
float PhysicalAttackPower = 0.0f;
|
||||||
|
|
||||||
|
/** @brief 推导后的法术强度。 */
|
||||||
|
UPROPERTY(BlueprintReadOnly, Category="PHY|Attributes|Combat")
|
||||||
|
float SpellPower = 0.0f;
|
||||||
|
|
||||||
|
/** @brief 推导后的护甲。 */
|
||||||
|
UPROPERTY(BlueprintReadOnly, Category="PHY|Attributes|Combat")
|
||||||
|
float Armor = 0.0f;
|
||||||
|
|
||||||
|
/** @brief 推导后的魔法抗性。 */
|
||||||
|
UPROPERTY(BlueprintReadOnly, Category="PHY|Attributes|Combat")
|
||||||
|
float MagicResistance = 0.0f;
|
||||||
|
|
||||||
|
/** @brief 推导后的命中率。 */
|
||||||
|
UPROPERTY(BlueprintReadOnly, Category="PHY|Attributes|Combat")
|
||||||
|
float Accuracy = 0.0f;
|
||||||
|
|
||||||
|
/** @brief 推导后的闪避率。 */
|
||||||
|
UPROPERTY(BlueprintReadOnly, Category="PHY|Attributes|Combat")
|
||||||
|
float Evasion = 0.0f;
|
||||||
|
|
||||||
|
/** @brief 推导后的暴击率。 */
|
||||||
|
UPROPERTY(BlueprintReadOnly, Category="PHY|Attributes|Combat")
|
||||||
|
float CriticalChance = 0.0f;
|
||||||
|
|
||||||
|
/** @brief 推导后的暴击伤害倍率。 */
|
||||||
|
UPROPERTY(BlueprintReadOnly, Category="PHY|Attributes|Combat")
|
||||||
|
float CriticalDamage = 0.0f;
|
||||||
|
|
||||||
|
/** @brief 推导后的攻击速度倍率。 */
|
||||||
|
UPROPERTY(BlueprintReadOnly, Category="PHY|Attributes|Combat")
|
||||||
|
float AttackSpeed = 0.0f;
|
||||||
|
|
||||||
|
/** @brief 推导后的冷却缩减比例。 */
|
||||||
|
UPROPERTY(BlueprintReadOnly, Category="PHY|Attributes|Combat")
|
||||||
|
float CooldownReduction = 0.0f;
|
||||||
|
|
||||||
|
/** @brief 推导后的格挡强度。 */
|
||||||
|
UPROPERTY(BlueprintReadOnly, Category="PHY|Attributes|Combat")
|
||||||
|
float BlockPower = 0.0f;
|
||||||
|
|
||||||
|
/** @brief 推导后的破防强度。 */
|
||||||
|
UPROPERTY(BlueprintReadOnly, Category="PHY|Attributes|Combat")
|
||||||
|
float GuardBreakPower = 0.0f;
|
||||||
|
|
||||||
|
/** @brief 推导后的韧性。 */
|
||||||
|
UPROPERTY(BlueprintReadOnly, Category="PHY|Attributes|Combat")
|
||||||
|
float Poise = 0.0f;
|
||||||
|
|
||||||
|
/** @brief 推导后的韧性伤害。 */
|
||||||
|
UPROPERTY(BlueprintReadOnly, Category="PHY|Attributes|Combat")
|
||||||
|
float PoiseDamage = 0.0f;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 元素属性计算结果。
|
||||||
|
*/
|
||||||
|
USTRUCT(BlueprintType)
|
||||||
|
struct FPHYDerivedElementAttributes
|
||||||
|
{
|
||||||
|
GENERATED_BODY()
|
||||||
|
|
||||||
|
/** @brief 推导后的通用元素伤害加成。 */
|
||||||
|
UPROPERTY(BlueprintReadOnly, Category="PHY|Attributes|Element")
|
||||||
|
float DamageBonus = 0.0f;
|
||||||
|
|
||||||
|
/** @brief 推导后的通用元素抗性。 */
|
||||||
|
UPROPERTY(BlueprintReadOnly, Category="PHY|Attributes|Element")
|
||||||
|
float Resistance = 0.0f;
|
||||||
|
|
||||||
|
/** @brief 推导后的通用元素穿透。 */
|
||||||
|
UPROPERTY(BlueprintReadOnly, Category="PHY|Attributes|Element")
|
||||||
|
float Penetration = 0.0f;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 属性公式函数库,避免首期依赖复杂 MMC 资产。
|
||||||
|
*/
|
||||||
|
UCLASS()
|
||||||
|
class PHY_API UPHYAttributeCalculationLibrary : public UBlueprintFunctionLibrary
|
||||||
|
{
|
||||||
|
GENERATED_BODY()
|
||||||
|
|
||||||
|
public:
|
||||||
|
/** @brief 从配置默认值创建基础属性快照。 */
|
||||||
|
UFUNCTION(BlueprintCallable, BlueprintPure, Category="PHY|Attributes")
|
||||||
|
static FPHYCoreAttributeSnapshot MakeCoreSnapshotFromDefaults();
|
||||||
|
|
||||||
|
/** @brief 从基础属性集创建基础属性快照。 */
|
||||||
|
UFUNCTION(BlueprintCallable, BlueprintPure, Category="PHY|Attributes")
|
||||||
|
static FPHYCoreAttributeSnapshot MakeCoreSnapshotFromAttributeSet(const UPHYCoreAttributeSet* CoreAttributes);
|
||||||
|
|
||||||
|
/** @brief 根据基础属性和配置公式计算次级战斗属性。 */
|
||||||
|
UFUNCTION(BlueprintCallable, BlueprintPure, Category="PHY|Attributes")
|
||||||
|
static FPHYDerivedCombatAttributes CalculateCombatAttributes(const FPHYCoreAttributeSnapshot& CoreAttributes);
|
||||||
|
|
||||||
|
/** @brief 根据基础属性和配置公式计算通用元素属性。 */
|
||||||
|
UFUNCTION(BlueprintCallable, BlueprintPure, Category="PHY|Attributes")
|
||||||
|
static FPHYDerivedElementAttributes CalculateElementAttributes(const FPHYCoreAttributeSnapshot& CoreAttributes);
|
||||||
|
};
|
||||||
12
Source/PHY/Public/AbilitySystem/Attributes/PHYAttributeSet.h
Normal file
12
Source/PHY/Public/AbilitySystem/Attributes/PHYAttributeSet.h
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "AbilitySystemComponent.h"
|
||||||
|
#include "AttributeSet.h"
|
||||||
|
|
||||||
|
#define PHY_ATTRIBUTE_ACCESSORS(ClassName, PropertyName) \
|
||||||
|
GAMEPLAYATTRIBUTE_PROPERTY_GETTER(ClassName, PropertyName) \
|
||||||
|
GAMEPLAYATTRIBUTE_VALUE_GETTER(PropertyName) \
|
||||||
|
GAMEPLAYATTRIBUTE_VALUE_SETTER(PropertyName) \
|
||||||
|
GAMEPLAYATTRIBUTE_VALUE_INITTER(PropertyName)
|
||||||
@@ -0,0 +1,301 @@
|
|||||||
|
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "CoreMinimal.h"
|
||||||
|
#include "UObject/Object.h"
|
||||||
|
#include "PHYAttributeSettings.generated.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 基础属性初始值。
|
||||||
|
*/
|
||||||
|
USTRUCT(BlueprintType)
|
||||||
|
struct FPHYCoreAttributeDefaults
|
||||||
|
{
|
||||||
|
GENERATED_BODY()
|
||||||
|
|
||||||
|
/** @brief 力量初始值。 */
|
||||||
|
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category="PHY|Attributes|Core")
|
||||||
|
float Strength = 10.0f;
|
||||||
|
|
||||||
|
/** @brief 灵巧初始值。 */
|
||||||
|
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category="PHY|Attributes|Core")
|
||||||
|
float Dexterity = 10.0f;
|
||||||
|
|
||||||
|
/** @brief 体魄初始值。 */
|
||||||
|
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category="PHY|Attributes|Core")
|
||||||
|
float Vitality = 10.0f;
|
||||||
|
|
||||||
|
/** @brief 智力初始值。 */
|
||||||
|
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category="PHY|Attributes|Core")
|
||||||
|
float Intelligence = 10.0f;
|
||||||
|
|
||||||
|
/** @brief 精神初始值。 */
|
||||||
|
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category="PHY|Attributes|Core")
|
||||||
|
float Spirit = 10.0f;
|
||||||
|
|
||||||
|
/** @brief 感知初始值。 */
|
||||||
|
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category="PHY|Attributes|Core")
|
||||||
|
float Perception = 10.0f;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 次级战斗属性公式系数。
|
||||||
|
*/
|
||||||
|
USTRUCT(BlueprintType)
|
||||||
|
struct FPHYCombatAttributeFormula
|
||||||
|
{
|
||||||
|
GENERATED_BODY()
|
||||||
|
|
||||||
|
/** @brief 生命上限基础值。 */
|
||||||
|
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category="PHY|Attributes|Formula")
|
||||||
|
float MaxHealthBase = 100.0f;
|
||||||
|
|
||||||
|
/** @brief 每点体魄提供的生命上限。 */
|
||||||
|
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category="PHY|Attributes|Formula")
|
||||||
|
float MaxHealthVitality = 18.0f;
|
||||||
|
|
||||||
|
/** @brief 每点力量提供的生命上限。 */
|
||||||
|
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category="PHY|Attributes|Formula")
|
||||||
|
float MaxHealthStrength = 3.0f;
|
||||||
|
|
||||||
|
/** @brief 法力上限基础值。 */
|
||||||
|
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category="PHY|Attributes|Formula")
|
||||||
|
float MaxManaBase = 60.0f;
|
||||||
|
|
||||||
|
/** @brief 每点智力提供的法力上限。 */
|
||||||
|
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category="PHY|Attributes|Formula")
|
||||||
|
float MaxManaIntelligence = 12.0f;
|
||||||
|
|
||||||
|
/** @brief 每点精神提供的法力上限。 */
|
||||||
|
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category="PHY|Attributes|Formula")
|
||||||
|
float MaxManaSpirit = 6.0f;
|
||||||
|
|
||||||
|
/** @brief 耐力上限基础值。 */
|
||||||
|
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category="PHY|Attributes|Formula")
|
||||||
|
float MaxStaminaBase = 80.0f;
|
||||||
|
|
||||||
|
/** @brief 每点体魄提供的耐力上限。 */
|
||||||
|
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category="PHY|Attributes|Formula")
|
||||||
|
float MaxStaminaVitality = 5.0f;
|
||||||
|
|
||||||
|
/** @brief 每点灵巧提供的耐力上限。 */
|
||||||
|
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category="PHY|Attributes|Formula")
|
||||||
|
float MaxStaminaDexterity = 4.0f;
|
||||||
|
|
||||||
|
/** @brief 力量对物理攻击的系数。 */
|
||||||
|
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category="PHY|Attributes|Formula")
|
||||||
|
float PhysicalAttackPowerStrength = 2.0f;
|
||||||
|
|
||||||
|
/** @brief 灵巧对物理攻击的系数。 */
|
||||||
|
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category="PHY|Attributes|Formula")
|
||||||
|
float PhysicalAttackPowerDexterity = 0.5f;
|
||||||
|
|
||||||
|
/** @brief 智力对法术强度的系数。 */
|
||||||
|
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category="PHY|Attributes|Formula")
|
||||||
|
float SpellPowerIntelligence = 2.0f;
|
||||||
|
|
||||||
|
/** @brief 精神对法术强度的系数。 */
|
||||||
|
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category="PHY|Attributes|Formula")
|
||||||
|
float SpellPowerSpirit = 0.4f;
|
||||||
|
|
||||||
|
/** @brief 体魄对护甲的系数。 */
|
||||||
|
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category="PHY|Attributes|Formula")
|
||||||
|
float ArmorVitality = 1.2f;
|
||||||
|
|
||||||
|
/** @brief 力量对护甲的系数。 */
|
||||||
|
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category="PHY|Attributes|Formula")
|
||||||
|
float ArmorStrength = 0.4f;
|
||||||
|
|
||||||
|
/** @brief 精神对魔法抗性的系数。 */
|
||||||
|
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category="PHY|Attributes|Formula")
|
||||||
|
float MagicResistanceSpirit = 1.1f;
|
||||||
|
|
||||||
|
/** @brief 智力对魔法抗性的系数。 */
|
||||||
|
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category="PHY|Attributes|Formula")
|
||||||
|
float MagicResistanceIntelligence = 0.3f;
|
||||||
|
|
||||||
|
/** @brief 命中基础值。 */
|
||||||
|
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category="PHY|Attributes|Formula")
|
||||||
|
float AccuracyBase = 0.8f;
|
||||||
|
|
||||||
|
/** @brief 感知对命中的系数。 */
|
||||||
|
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category="PHY|Attributes|Formula")
|
||||||
|
float AccuracyPerception = 0.006f;
|
||||||
|
|
||||||
|
/** @brief 灵巧对命中的系数。 */
|
||||||
|
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category="PHY|Attributes|Formula")
|
||||||
|
float AccuracyDexterity = 0.002f;
|
||||||
|
|
||||||
|
/** @brief 闪避基础值。 */
|
||||||
|
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category="PHY|Attributes|Formula")
|
||||||
|
float EvasionBase = 0.03f;
|
||||||
|
|
||||||
|
/** @brief 灵巧对闪避的系数。 */
|
||||||
|
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category="PHY|Attributes|Formula")
|
||||||
|
float EvasionDexterity = 0.003f;
|
||||||
|
|
||||||
|
/** @brief 感知对闪避的系数。 */
|
||||||
|
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category="PHY|Attributes|Formula")
|
||||||
|
float EvasionPerception = 0.001f;
|
||||||
|
|
||||||
|
/** @brief 暴击率基础值。 */
|
||||||
|
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category="PHY|Attributes|Formula")
|
||||||
|
float CriticalChanceBase = 0.05f;
|
||||||
|
|
||||||
|
/** @brief 灵巧对暴击率的系数。 */
|
||||||
|
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category="PHY|Attributes|Formula")
|
||||||
|
float CriticalChanceDexterity = 0.002f;
|
||||||
|
|
||||||
|
/** @brief 感知对暴击率的系数。 */
|
||||||
|
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category="PHY|Attributes|Formula")
|
||||||
|
float CriticalChancePerception = 0.0015f;
|
||||||
|
|
||||||
|
/** @brief 暴击伤害基础倍率。 */
|
||||||
|
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category="PHY|Attributes|Formula")
|
||||||
|
float CriticalDamageBase = 1.5f;
|
||||||
|
|
||||||
|
/** @brief 力量对暴击伤害的系数。 */
|
||||||
|
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category="PHY|Attributes|Formula")
|
||||||
|
float CriticalDamageStrength = 0.004f;
|
||||||
|
|
||||||
|
/** @brief 感知对暴击伤害的系数。 */
|
||||||
|
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category="PHY|Attributes|Formula")
|
||||||
|
float CriticalDamagePerception = 0.003f;
|
||||||
|
|
||||||
|
/** @brief 攻击速度基础倍率。 */
|
||||||
|
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category="PHY|Attributes|Formula")
|
||||||
|
float AttackSpeedBase = 1.0f;
|
||||||
|
|
||||||
|
/** @brief 灵巧对攻击速度的系数。 */
|
||||||
|
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category="PHY|Attributes|Formula")
|
||||||
|
float AttackSpeedDexterity = 0.01f;
|
||||||
|
|
||||||
|
/** @brief 冷却缩减基础值。 */
|
||||||
|
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category="PHY|Attributes|Formula")
|
||||||
|
float CooldownReductionBase = 0.0f;
|
||||||
|
|
||||||
|
/** @brief 智力对冷却缩减的系数。 */
|
||||||
|
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category="PHY|Attributes|Formula")
|
||||||
|
float CooldownReductionIntelligence = 0.0015f;
|
||||||
|
|
||||||
|
/** @brief 精神对冷却缩减的系数。 */
|
||||||
|
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category="PHY|Attributes|Formula")
|
||||||
|
float CooldownReductionSpirit = 0.001f;
|
||||||
|
|
||||||
|
/** @brief 格挡强度基础值。 */
|
||||||
|
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category="PHY|Attributes|Formula")
|
||||||
|
float BlockPowerBase = 5.0f;
|
||||||
|
|
||||||
|
/** @brief 力量对格挡强度的系数。 */
|
||||||
|
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category="PHY|Attributes|Formula")
|
||||||
|
float BlockPowerStrength = 1.2f;
|
||||||
|
|
||||||
|
/** @brief 体魄对格挡强度的系数。 */
|
||||||
|
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category="PHY|Attributes|Formula")
|
||||||
|
float BlockPowerVitality = 0.6f;
|
||||||
|
|
||||||
|
/** @brief 破防强度基础值。 */
|
||||||
|
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category="PHY|Attributes|Formula")
|
||||||
|
float GuardBreakPowerBase = 5.0f;
|
||||||
|
|
||||||
|
/** @brief 力量对破防强度的系数。 */
|
||||||
|
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category="PHY|Attributes|Formula")
|
||||||
|
float GuardBreakPowerStrength = 1.1f;
|
||||||
|
|
||||||
|
/** @brief 感知对破防强度的系数。 */
|
||||||
|
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category="PHY|Attributes|Formula")
|
||||||
|
float GuardBreakPowerPerception = 0.4f;
|
||||||
|
|
||||||
|
/** @brief 韧性基础值。 */
|
||||||
|
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category="PHY|Attributes|Formula")
|
||||||
|
float PoiseBase = 20.0f;
|
||||||
|
|
||||||
|
/** @brief 体魄对韧性的系数。 */
|
||||||
|
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category="PHY|Attributes|Formula")
|
||||||
|
float PoiseVitality = 1.5f;
|
||||||
|
|
||||||
|
/** @brief 力量对韧性的系数。 */
|
||||||
|
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category="PHY|Attributes|Formula")
|
||||||
|
float PoiseStrength = 0.5f;
|
||||||
|
|
||||||
|
/** @brief 韧性伤害基础值。 */
|
||||||
|
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category="PHY|Attributes|Formula")
|
||||||
|
float PoiseDamageBase = 5.0f;
|
||||||
|
|
||||||
|
/** @brief 力量对韧性伤害的系数。 */
|
||||||
|
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category="PHY|Attributes|Formula")
|
||||||
|
float PoiseDamageStrength = 0.8f;
|
||||||
|
|
||||||
|
/** @brief 灵巧对韧性伤害的系数。 */
|
||||||
|
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category="PHY|Attributes|Formula")
|
||||||
|
float PoiseDamageDexterity = 0.2f;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 元素属性公式系数。
|
||||||
|
*/
|
||||||
|
USTRUCT(BlueprintType)
|
||||||
|
struct FPHYElementAttributeFormula
|
||||||
|
{
|
||||||
|
GENERATED_BODY()
|
||||||
|
|
||||||
|
/** @brief 元素伤害加成基础值。 */
|
||||||
|
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category="PHY|Attributes|Element")
|
||||||
|
float DamageBonusBase = 0.0f;
|
||||||
|
|
||||||
|
/** @brief 智力对元素伤害加成的系数。 */
|
||||||
|
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category="PHY|Attributes|Element")
|
||||||
|
float DamageBonusIntelligence = 0.0015f;
|
||||||
|
|
||||||
|
/** @brief 感知对元素伤害加成的系数。 */
|
||||||
|
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category="PHY|Attributes|Element")
|
||||||
|
float DamageBonusPerception = 0.0005f;
|
||||||
|
|
||||||
|
/** @brief 元素抗性基础值。 */
|
||||||
|
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category="PHY|Attributes|Element")
|
||||||
|
float ResistanceBase = 0.02f;
|
||||||
|
|
||||||
|
/** @brief 精神对元素抗性的系数。 */
|
||||||
|
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category="PHY|Attributes|Element")
|
||||||
|
float ResistanceSpirit = 0.002f;
|
||||||
|
|
||||||
|
/** @brief 体魄对元素抗性的系数。 */
|
||||||
|
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category="PHY|Attributes|Element")
|
||||||
|
float ResistanceVitality = 0.0005f;
|
||||||
|
|
||||||
|
/** @brief 通用元素穿透基础值。 */
|
||||||
|
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category="PHY|Attributes|Element")
|
||||||
|
float PenetrationBase = 0.0f;
|
||||||
|
|
||||||
|
/** @brief 感知对通用元素穿透的系数。 */
|
||||||
|
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category="PHY|Attributes|Element")
|
||||||
|
float PenetrationPerception = 0.001f;
|
||||||
|
|
||||||
|
/** @brief 智力对通用元素穿透的系数。 */
|
||||||
|
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category="PHY|Attributes|Element")
|
||||||
|
float PenetrationIntelligence = 0.0005f;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 属性体系配置,首期放入 PHYCombat 以便和战斗公式一起维护。
|
||||||
|
*/
|
||||||
|
UCLASS(Config=PHYCombat, DefaultConfig)
|
||||||
|
class PHY_API UPHYAttributeSettings : public UObject
|
||||||
|
{
|
||||||
|
GENERATED_BODY()
|
||||||
|
|
||||||
|
public:
|
||||||
|
/** @brief 基础属性默认初始值。 */
|
||||||
|
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category="PHY|Attributes")
|
||||||
|
FPHYCoreAttributeDefaults DefaultCoreAttributes;
|
||||||
|
|
||||||
|
/** @brief 次级战斗属性公式。 */
|
||||||
|
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category="PHY|Attributes")
|
||||||
|
FPHYCombatAttributeFormula CombatFormula;
|
||||||
|
|
||||||
|
/** @brief 元素属性公式。 */
|
||||||
|
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category="PHY|Attributes")
|
||||||
|
FPHYElementAttributeFormula ElementFormula;
|
||||||
|
};
|
||||||
|
|
||||||
@@ -0,0 +1,183 @@
|
|||||||
|
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "CoreMinimal.h"
|
||||||
|
#include "AbilitySystem/Attributes/PHYAttributeSet.h"
|
||||||
|
#include "PHYCombatAttributeSet.generated.h"
|
||||||
|
|
||||||
|
class UPHYCoreAttributeSet;
|
||||||
|
struct FGameplayEffectModCallbackData;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief PHY 次级战斗属性集,由基础属性和装备、效果共同驱动。
|
||||||
|
*/
|
||||||
|
UCLASS(BlueprintType)
|
||||||
|
class PHY_API UPHYCombatAttributeSet : public UAttributeSet
|
||||||
|
{
|
||||||
|
GENERATED_BODY()
|
||||||
|
|
||||||
|
public:
|
||||||
|
/** @brief 构造战斗属性并按默认基础属性推导首期数值。 */
|
||||||
|
UPHYCombatAttributeSet();
|
||||||
|
|
||||||
|
/** @brief 注册属性复制。 */
|
||||||
|
virtual void GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const override;
|
||||||
|
|
||||||
|
/** @brief 属性变更前限制百分比和资源范围。 */
|
||||||
|
virtual void PreAttributeChange(const FGameplayAttribute& Attribute, float& NewValue) override;
|
||||||
|
|
||||||
|
/** @brief GameplayEffect 执行后限制资源不超过上限。 */
|
||||||
|
virtual void PostGameplayEffectExecute(const FGameplayEffectModCallbackData& Data) override;
|
||||||
|
|
||||||
|
/** @brief 属性变更后同步当前资源与上限。 */
|
||||||
|
virtual void PostAttributeChange(const FGameplayAttribute& Attribute, float OldValue, float NewValue) override;
|
||||||
|
|
||||||
|
/** @brief 当前生命。 */
|
||||||
|
UPROPERTY(BlueprintReadOnly, ReplicatedUsing=OnRep_Health, Category="PHY|Attributes|Combat")
|
||||||
|
FGameplayAttributeData Health;
|
||||||
|
PHY_ATTRIBUTE_ACCESSORS(UPHYCombatAttributeSet, Health);
|
||||||
|
|
||||||
|
/** @brief 生命上限。 */
|
||||||
|
UPROPERTY(BlueprintReadOnly, ReplicatedUsing=OnRep_MaxHealth, Category="PHY|Attributes|Combat")
|
||||||
|
FGameplayAttributeData MaxHealth;
|
||||||
|
PHY_ATTRIBUTE_ACCESSORS(UPHYCombatAttributeSet, MaxHealth);
|
||||||
|
|
||||||
|
/** @brief 当前法力。 */
|
||||||
|
UPROPERTY(BlueprintReadOnly, ReplicatedUsing=OnRep_Mana, Category="PHY|Attributes|Combat")
|
||||||
|
FGameplayAttributeData Mana;
|
||||||
|
PHY_ATTRIBUTE_ACCESSORS(UPHYCombatAttributeSet, Mana);
|
||||||
|
|
||||||
|
/** @brief 法力上限。 */
|
||||||
|
UPROPERTY(BlueprintReadOnly, ReplicatedUsing=OnRep_MaxMana, Category="PHY|Attributes|Combat")
|
||||||
|
FGameplayAttributeData MaxMana;
|
||||||
|
PHY_ATTRIBUTE_ACCESSORS(UPHYCombatAttributeSet, MaxMana);
|
||||||
|
|
||||||
|
/** @brief 当前耐力。 */
|
||||||
|
UPROPERTY(BlueprintReadOnly, ReplicatedUsing=OnRep_Stamina, Category="PHY|Attributes|Combat")
|
||||||
|
FGameplayAttributeData Stamina;
|
||||||
|
PHY_ATTRIBUTE_ACCESSORS(UPHYCombatAttributeSet, Stamina);
|
||||||
|
|
||||||
|
/** @brief 耐力上限。 */
|
||||||
|
UPROPERTY(BlueprintReadOnly, ReplicatedUsing=OnRep_MaxStamina, Category="PHY|Attributes|Combat")
|
||||||
|
FGameplayAttributeData MaxStamina;
|
||||||
|
PHY_ATTRIBUTE_ACCESSORS(UPHYCombatAttributeSet, MaxStamina);
|
||||||
|
|
||||||
|
/** @brief 物理攻击强度。 */
|
||||||
|
UPROPERTY(BlueprintReadOnly, ReplicatedUsing=OnRep_PhysicalAttackPower, Category="PHY|Attributes|Combat")
|
||||||
|
FGameplayAttributeData PhysicalAttackPower;
|
||||||
|
PHY_ATTRIBUTE_ACCESSORS(UPHYCombatAttributeSet, PhysicalAttackPower);
|
||||||
|
|
||||||
|
/** @brief 法术强度。 */
|
||||||
|
UPROPERTY(BlueprintReadOnly, ReplicatedUsing=OnRep_SpellPower, Category="PHY|Attributes|Combat")
|
||||||
|
FGameplayAttributeData SpellPower;
|
||||||
|
PHY_ATTRIBUTE_ACCESSORS(UPHYCombatAttributeSet, SpellPower);
|
||||||
|
|
||||||
|
/** @brief 护甲。 */
|
||||||
|
UPROPERTY(BlueprintReadOnly, ReplicatedUsing=OnRep_Armor, Category="PHY|Attributes|Combat")
|
||||||
|
FGameplayAttributeData Armor;
|
||||||
|
PHY_ATTRIBUTE_ACCESSORS(UPHYCombatAttributeSet, Armor);
|
||||||
|
|
||||||
|
/** @brief 魔法抗性。 */
|
||||||
|
UPROPERTY(BlueprintReadOnly, ReplicatedUsing=OnRep_MagicResistance, Category="PHY|Attributes|Combat")
|
||||||
|
FGameplayAttributeData MagicResistance;
|
||||||
|
PHY_ATTRIBUTE_ACCESSORS(UPHYCombatAttributeSet, MagicResistance);
|
||||||
|
|
||||||
|
/** @brief 命中率,通常作为 0 到 1 的比例使用。 */
|
||||||
|
UPROPERTY(BlueprintReadOnly, ReplicatedUsing=OnRep_Accuracy, Category="PHY|Attributes|Combat")
|
||||||
|
FGameplayAttributeData Accuracy;
|
||||||
|
PHY_ATTRIBUTE_ACCESSORS(UPHYCombatAttributeSet, Accuracy);
|
||||||
|
|
||||||
|
/** @brief 闪避率,通常作为 0 到 1 的比例使用。 */
|
||||||
|
UPROPERTY(BlueprintReadOnly, ReplicatedUsing=OnRep_Evasion, Category="PHY|Attributes|Combat")
|
||||||
|
FGameplayAttributeData Evasion;
|
||||||
|
PHY_ATTRIBUTE_ACCESSORS(UPHYCombatAttributeSet, Evasion);
|
||||||
|
|
||||||
|
/** @brief 暴击率,通常作为 0 到 1 的比例使用。 */
|
||||||
|
UPROPERTY(BlueprintReadOnly, ReplicatedUsing=OnRep_CriticalChance, Category="PHY|Attributes|Combat")
|
||||||
|
FGameplayAttributeData CriticalChance;
|
||||||
|
PHY_ATTRIBUTE_ACCESSORS(UPHYCombatAttributeSet, CriticalChance);
|
||||||
|
|
||||||
|
/** @brief 暴击伤害倍率,1.5 表示 150% 伤害。 */
|
||||||
|
UPROPERTY(BlueprintReadOnly, ReplicatedUsing=OnRep_CriticalDamage, Category="PHY|Attributes|Combat")
|
||||||
|
FGameplayAttributeData CriticalDamage;
|
||||||
|
PHY_ATTRIBUTE_ACCESSORS(UPHYCombatAttributeSet, CriticalDamage);
|
||||||
|
|
||||||
|
/** @brief 攻击速度倍率。 */
|
||||||
|
UPROPERTY(BlueprintReadOnly, ReplicatedUsing=OnRep_AttackSpeed, Category="PHY|Attributes|Combat")
|
||||||
|
FGameplayAttributeData AttackSpeed;
|
||||||
|
PHY_ATTRIBUTE_ACCESSORS(UPHYCombatAttributeSet, AttackSpeed);
|
||||||
|
|
||||||
|
/** @brief 冷却缩减比例。 */
|
||||||
|
UPROPERTY(BlueprintReadOnly, ReplicatedUsing=OnRep_CooldownReduction, Category="PHY|Attributes|Combat")
|
||||||
|
FGameplayAttributeData CooldownReduction;
|
||||||
|
PHY_ATTRIBUTE_ACCESSORS(UPHYCombatAttributeSet, CooldownReduction);
|
||||||
|
|
||||||
|
/** @brief 格挡强度。 */
|
||||||
|
UPROPERTY(BlueprintReadOnly, ReplicatedUsing=OnRep_BlockPower, Category="PHY|Attributes|Combat")
|
||||||
|
FGameplayAttributeData BlockPower;
|
||||||
|
PHY_ATTRIBUTE_ACCESSORS(UPHYCombatAttributeSet, BlockPower);
|
||||||
|
|
||||||
|
/** @brief 破防强度。 */
|
||||||
|
UPROPERTY(BlueprintReadOnly, ReplicatedUsing=OnRep_GuardBreakPower, Category="PHY|Attributes|Combat")
|
||||||
|
FGameplayAttributeData GuardBreakPower;
|
||||||
|
PHY_ATTRIBUTE_ACCESSORS(UPHYCombatAttributeSet, GuardBreakPower);
|
||||||
|
|
||||||
|
/** @brief 韧性。 */
|
||||||
|
UPROPERTY(BlueprintReadOnly, ReplicatedUsing=OnRep_Poise, Category="PHY|Attributes|Combat")
|
||||||
|
FGameplayAttributeData Poise;
|
||||||
|
PHY_ATTRIBUTE_ACCESSORS(UPHYCombatAttributeSet, Poise);
|
||||||
|
|
||||||
|
/** @brief 韧性伤害。 */
|
||||||
|
UPROPERTY(BlueprintReadOnly, ReplicatedUsing=OnRep_PoiseDamage, Category="PHY|Attributes|Combat")
|
||||||
|
FGameplayAttributeData PoiseDamage;
|
||||||
|
PHY_ATTRIBUTE_ACCESSORS(UPHYCombatAttributeSet, PoiseDamage);
|
||||||
|
|
||||||
|
/** @brief 按默认配置中的基础属性计算并写入次级属性。 */
|
||||||
|
void InitializeFromConfiguredCoreDefaults();
|
||||||
|
|
||||||
|
/** @brief 按指定基础属性集计算并写入次级属性。 */
|
||||||
|
void InitializeFromCoreAttributes(const UPHYCoreAttributeSet* CoreAttributes);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
/** @brief 复制当前生命变化。 */
|
||||||
|
UFUNCTION() void OnRep_Health(const FGameplayAttributeData& OldValue);
|
||||||
|
/** @brief 复制生命上限变化。 */
|
||||||
|
UFUNCTION() void OnRep_MaxHealth(const FGameplayAttributeData& OldValue);
|
||||||
|
/** @brief 复制当前法力变化。 */
|
||||||
|
UFUNCTION() void OnRep_Mana(const FGameplayAttributeData& OldValue);
|
||||||
|
/** @brief 复制法力上限变化。 */
|
||||||
|
UFUNCTION() void OnRep_MaxMana(const FGameplayAttributeData& OldValue);
|
||||||
|
/** @brief 复制当前耐力变化。 */
|
||||||
|
UFUNCTION() void OnRep_Stamina(const FGameplayAttributeData& OldValue);
|
||||||
|
/** @brief 复制耐力上限变化。 */
|
||||||
|
UFUNCTION() void OnRep_MaxStamina(const FGameplayAttributeData& OldValue);
|
||||||
|
/** @brief 复制物理攻击强度变化。 */
|
||||||
|
UFUNCTION() void OnRep_PhysicalAttackPower(const FGameplayAttributeData& OldValue);
|
||||||
|
/** @brief 复制法术强度变化。 */
|
||||||
|
UFUNCTION() void OnRep_SpellPower(const FGameplayAttributeData& OldValue);
|
||||||
|
/** @brief 复制护甲变化。 */
|
||||||
|
UFUNCTION() void OnRep_Armor(const FGameplayAttributeData& OldValue);
|
||||||
|
/** @brief 复制魔法抗性变化。 */
|
||||||
|
UFUNCTION() void OnRep_MagicResistance(const FGameplayAttributeData& OldValue);
|
||||||
|
/** @brief 复制命中率变化。 */
|
||||||
|
UFUNCTION() void OnRep_Accuracy(const FGameplayAttributeData& OldValue);
|
||||||
|
/** @brief 复制闪避率变化。 */
|
||||||
|
UFUNCTION() void OnRep_Evasion(const FGameplayAttributeData& OldValue);
|
||||||
|
/** @brief 复制暴击率变化。 */
|
||||||
|
UFUNCTION() void OnRep_CriticalChance(const FGameplayAttributeData& OldValue);
|
||||||
|
/** @brief 复制暴击伤害倍率变化。 */
|
||||||
|
UFUNCTION() void OnRep_CriticalDamage(const FGameplayAttributeData& OldValue);
|
||||||
|
/** @brief 复制攻击速度倍率变化。 */
|
||||||
|
UFUNCTION() void OnRep_AttackSpeed(const FGameplayAttributeData& OldValue);
|
||||||
|
/** @brief 复制冷却缩减比例变化。 */
|
||||||
|
UFUNCTION() void OnRep_CooldownReduction(const FGameplayAttributeData& OldValue);
|
||||||
|
/** @brief 复制格挡强度变化。 */
|
||||||
|
UFUNCTION() void OnRep_BlockPower(const FGameplayAttributeData& OldValue);
|
||||||
|
/** @brief 复制破防强度变化。 */
|
||||||
|
UFUNCTION() void OnRep_GuardBreakPower(const FGameplayAttributeData& OldValue);
|
||||||
|
/** @brief 复制韧性变化。 */
|
||||||
|
UFUNCTION() void OnRep_Poise(const FGameplayAttributeData& OldValue);
|
||||||
|
/** @brief 复制韧性伤害变化。 */
|
||||||
|
UFUNCTION() void OnRep_PoiseDamage(const FGameplayAttributeData& OldValue);
|
||||||
|
};
|
||||||
@@ -0,0 +1,78 @@
|
|||||||
|
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "CoreMinimal.h"
|
||||||
|
#include "AbilitySystem/Attributes/PHYAttributeSet.h"
|
||||||
|
#include "PHYCoreAttributeSet.generated.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief PHY 基础属性集,作为所有玩家和 AI 的通用根属性。
|
||||||
|
*/
|
||||||
|
UCLASS(BlueprintType)
|
||||||
|
class PHY_API UPHYCoreAttributeSet : public UAttributeSet
|
||||||
|
{
|
||||||
|
GENERATED_BODY()
|
||||||
|
|
||||||
|
public:
|
||||||
|
/** @brief 构造基础属性并读取默认配置。 */
|
||||||
|
UPHYCoreAttributeSet();
|
||||||
|
|
||||||
|
/** @brief 注册属性复制。 */
|
||||||
|
virtual void GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const override;
|
||||||
|
|
||||||
|
/** @brief 力量,影响物理攻击、格挡、破防和负重倾向。 */
|
||||||
|
UPROPERTY(BlueprintReadOnly, ReplicatedUsing=OnRep_Strength, Category="PHY|Attributes|Core")
|
||||||
|
FGameplayAttributeData Strength;
|
||||||
|
PHY_ATTRIBUTE_ACCESSORS(UPHYCoreAttributeSet, Strength);
|
||||||
|
|
||||||
|
/** @brief 灵巧,影响暴击、攻速、命中和闪避倾向。 */
|
||||||
|
UPROPERTY(BlueprintReadOnly, ReplicatedUsing=OnRep_Dexterity, Category="PHY|Attributes|Core")
|
||||||
|
FGameplayAttributeData Dexterity;
|
||||||
|
PHY_ATTRIBUTE_ACCESSORS(UPHYCoreAttributeSet, Dexterity);
|
||||||
|
|
||||||
|
/** @brief 体魄,影响生命、护甲、耐力和韧性倾向。 */
|
||||||
|
UPROPERTY(BlueprintReadOnly, ReplicatedUsing=OnRep_Vitality, Category="PHY|Attributes|Core")
|
||||||
|
FGameplayAttributeData Vitality;
|
||||||
|
PHY_ATTRIBUTE_ACCESSORS(UPHYCoreAttributeSet, Vitality);
|
||||||
|
|
||||||
|
/** @brief 智力,影响法术强度、法力和冷却缩减倾向。 */
|
||||||
|
UPROPERTY(BlueprintReadOnly, ReplicatedUsing=OnRep_Intelligence, Category="PHY|Attributes|Core")
|
||||||
|
FGameplayAttributeData Intelligence;
|
||||||
|
PHY_ATTRIBUTE_ACCESSORS(UPHYCoreAttributeSet, Intelligence);
|
||||||
|
|
||||||
|
/** @brief 精神,影响法力、魔法抗性和元素抗性倾向。 */
|
||||||
|
UPROPERTY(BlueprintReadOnly, ReplicatedUsing=OnRep_Spirit, Category="PHY|Attributes|Core")
|
||||||
|
FGameplayAttributeData Spirit;
|
||||||
|
PHY_ATTRIBUTE_ACCESSORS(UPHYCoreAttributeSet, Spirit);
|
||||||
|
|
||||||
|
/** @brief 感知,影响命中、暴击、弱点和穿透倾向。 */
|
||||||
|
UPROPERTY(BlueprintReadOnly, ReplicatedUsing=OnRep_Perception, Category="PHY|Attributes|Core")
|
||||||
|
FGameplayAttributeData Perception;
|
||||||
|
PHY_ATTRIBUTE_ACCESSORS(UPHYCoreAttributeSet, Perception);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
/** @brief 复制力量。 */
|
||||||
|
UFUNCTION()
|
||||||
|
void OnRep_Strength(const FGameplayAttributeData& OldValue);
|
||||||
|
|
||||||
|
/** @brief 复制灵巧。 */
|
||||||
|
UFUNCTION()
|
||||||
|
void OnRep_Dexterity(const FGameplayAttributeData& OldValue);
|
||||||
|
|
||||||
|
/** @brief 复制体魄。 */
|
||||||
|
UFUNCTION()
|
||||||
|
void OnRep_Vitality(const FGameplayAttributeData& OldValue);
|
||||||
|
|
||||||
|
/** @brief 复制智力。 */
|
||||||
|
UFUNCTION()
|
||||||
|
void OnRep_Intelligence(const FGameplayAttributeData& OldValue);
|
||||||
|
|
||||||
|
/** @brief 复制精神。 */
|
||||||
|
UFUNCTION()
|
||||||
|
void OnRep_Spirit(const FGameplayAttributeData& OldValue);
|
||||||
|
|
||||||
|
/** @brief 复制感知。 */
|
||||||
|
UFUNCTION()
|
||||||
|
void OnRep_Perception(const FGameplayAttributeData& OldValue);
|
||||||
|
};
|
||||||
@@ -0,0 +1,155 @@
|
|||||||
|
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "CoreMinimal.h"
|
||||||
|
#include "AbilitySystem/Attributes/PHYAttributeSet.h"
|
||||||
|
#include "PHYElementAttributeSet.generated.h"
|
||||||
|
|
||||||
|
class UPHYCoreAttributeSet;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief PHY 元素进攻与防御属性集。
|
||||||
|
*/
|
||||||
|
UCLASS(BlueprintType)
|
||||||
|
class PHY_API UPHYElementAttributeSet : public UAttributeSet
|
||||||
|
{
|
||||||
|
GENERATED_BODY()
|
||||||
|
|
||||||
|
public:
|
||||||
|
/** @brief 构造元素属性并按默认基础属性推导首期数值。 */
|
||||||
|
UPHYElementAttributeSet();
|
||||||
|
|
||||||
|
/** @brief 注册属性复制。 */
|
||||||
|
virtual void GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const override;
|
||||||
|
|
||||||
|
/** @brief 限制元素比例属性范围。 */
|
||||||
|
virtual void PreAttributeChange(const FGameplayAttribute& Attribute, float& NewValue) override;
|
||||||
|
|
||||||
|
/** @brief 按默认配置中的基础属性计算并写入元素属性。 */
|
||||||
|
void InitializeFromConfiguredCoreDefaults();
|
||||||
|
|
||||||
|
/** @brief 按指定基础属性集计算并写入元素属性。 */
|
||||||
|
void InitializeFromCoreAttributes(const UPHYCoreAttributeSet* CoreAttributes);
|
||||||
|
|
||||||
|
/** @brief 火元素伤害加成。 */
|
||||||
|
UPROPERTY(BlueprintReadOnly, ReplicatedUsing=OnRep_FireDamageBonus, Category="PHY|Attributes|Element")
|
||||||
|
FGameplayAttributeData FireDamageBonus;
|
||||||
|
PHY_ATTRIBUTE_ACCESSORS(UPHYElementAttributeSet, FireDamageBonus);
|
||||||
|
|
||||||
|
/** @brief 水元素伤害加成。 */
|
||||||
|
UPROPERTY(BlueprintReadOnly, ReplicatedUsing=OnRep_WaterDamageBonus, Category="PHY|Attributes|Element")
|
||||||
|
FGameplayAttributeData WaterDamageBonus;
|
||||||
|
PHY_ATTRIBUTE_ACCESSORS(UPHYElementAttributeSet, WaterDamageBonus);
|
||||||
|
|
||||||
|
/** @brief 冰元素伤害加成。 */
|
||||||
|
UPROPERTY(BlueprintReadOnly, ReplicatedUsing=OnRep_IceDamageBonus, Category="PHY|Attributes|Element")
|
||||||
|
FGameplayAttributeData IceDamageBonus;
|
||||||
|
PHY_ATTRIBUTE_ACCESSORS(UPHYElementAttributeSet, IceDamageBonus);
|
||||||
|
|
||||||
|
/** @brief 雷元素伤害加成。 */
|
||||||
|
UPROPERTY(BlueprintReadOnly, ReplicatedUsing=OnRep_LightningDamageBonus, Category="PHY|Attributes|Element")
|
||||||
|
FGameplayAttributeData LightningDamageBonus;
|
||||||
|
PHY_ATTRIBUTE_ACCESSORS(UPHYElementAttributeSet, LightningDamageBonus);
|
||||||
|
|
||||||
|
/** @brief 地元素伤害加成。 */
|
||||||
|
UPROPERTY(BlueprintReadOnly, ReplicatedUsing=OnRep_EarthDamageBonus, Category="PHY|Attributes|Element")
|
||||||
|
FGameplayAttributeData EarthDamageBonus;
|
||||||
|
PHY_ATTRIBUTE_ACCESSORS(UPHYElementAttributeSet, EarthDamageBonus);
|
||||||
|
|
||||||
|
/** @brief 风元素伤害加成。 */
|
||||||
|
UPROPERTY(BlueprintReadOnly, ReplicatedUsing=OnRep_WindDamageBonus, Category="PHY|Attributes|Element")
|
||||||
|
FGameplayAttributeData WindDamageBonus;
|
||||||
|
PHY_ATTRIBUTE_ACCESSORS(UPHYElementAttributeSet, WindDamageBonus);
|
||||||
|
|
||||||
|
/** @brief 光元素伤害加成。 */
|
||||||
|
UPROPERTY(BlueprintReadOnly, ReplicatedUsing=OnRep_LightDamageBonus, Category="PHY|Attributes|Element")
|
||||||
|
FGameplayAttributeData LightDamageBonus;
|
||||||
|
PHY_ATTRIBUTE_ACCESSORS(UPHYElementAttributeSet, LightDamageBonus);
|
||||||
|
|
||||||
|
/** @brief 暗元素伤害加成。 */
|
||||||
|
UPROPERTY(BlueprintReadOnly, ReplicatedUsing=OnRep_DarkDamageBonus, Category="PHY|Attributes|Element")
|
||||||
|
FGameplayAttributeData DarkDamageBonus;
|
||||||
|
PHY_ATTRIBUTE_ACCESSORS(UPHYElementAttributeSet, DarkDamageBonus);
|
||||||
|
|
||||||
|
/** @brief 火元素抗性。 */
|
||||||
|
UPROPERTY(BlueprintReadOnly, ReplicatedUsing=OnRep_FireResistance, Category="PHY|Attributes|Element")
|
||||||
|
FGameplayAttributeData FireResistance;
|
||||||
|
PHY_ATTRIBUTE_ACCESSORS(UPHYElementAttributeSet, FireResistance);
|
||||||
|
|
||||||
|
/** @brief 水元素抗性。 */
|
||||||
|
UPROPERTY(BlueprintReadOnly, ReplicatedUsing=OnRep_WaterResistance, Category="PHY|Attributes|Element")
|
||||||
|
FGameplayAttributeData WaterResistance;
|
||||||
|
PHY_ATTRIBUTE_ACCESSORS(UPHYElementAttributeSet, WaterResistance);
|
||||||
|
|
||||||
|
/** @brief 冰元素抗性。 */
|
||||||
|
UPROPERTY(BlueprintReadOnly, ReplicatedUsing=OnRep_IceResistance, Category="PHY|Attributes|Element")
|
||||||
|
FGameplayAttributeData IceResistance;
|
||||||
|
PHY_ATTRIBUTE_ACCESSORS(UPHYElementAttributeSet, IceResistance);
|
||||||
|
|
||||||
|
/** @brief 雷元素抗性。 */
|
||||||
|
UPROPERTY(BlueprintReadOnly, ReplicatedUsing=OnRep_LightningResistance, Category="PHY|Attributes|Element")
|
||||||
|
FGameplayAttributeData LightningResistance;
|
||||||
|
PHY_ATTRIBUTE_ACCESSORS(UPHYElementAttributeSet, LightningResistance);
|
||||||
|
|
||||||
|
/** @brief 地元素抗性。 */
|
||||||
|
UPROPERTY(BlueprintReadOnly, ReplicatedUsing=OnRep_EarthResistance, Category="PHY|Attributes|Element")
|
||||||
|
FGameplayAttributeData EarthResistance;
|
||||||
|
PHY_ATTRIBUTE_ACCESSORS(UPHYElementAttributeSet, EarthResistance);
|
||||||
|
|
||||||
|
/** @brief 风元素抗性。 */
|
||||||
|
UPROPERTY(BlueprintReadOnly, ReplicatedUsing=OnRep_WindResistance, Category="PHY|Attributes|Element")
|
||||||
|
FGameplayAttributeData WindResistance;
|
||||||
|
PHY_ATTRIBUTE_ACCESSORS(UPHYElementAttributeSet, WindResistance);
|
||||||
|
|
||||||
|
/** @brief 光元素抗性。 */
|
||||||
|
UPROPERTY(BlueprintReadOnly, ReplicatedUsing=OnRep_LightResistance, Category="PHY|Attributes|Element")
|
||||||
|
FGameplayAttributeData LightResistance;
|
||||||
|
PHY_ATTRIBUTE_ACCESSORS(UPHYElementAttributeSet, LightResistance);
|
||||||
|
|
||||||
|
/** @brief 暗元素抗性。 */
|
||||||
|
UPROPERTY(BlueprintReadOnly, ReplicatedUsing=OnRep_DarkResistance, Category="PHY|Attributes|Element")
|
||||||
|
FGameplayAttributeData DarkResistance;
|
||||||
|
PHY_ATTRIBUTE_ACCESSORS(UPHYElementAttributeSet, DarkResistance);
|
||||||
|
|
||||||
|
/** @brief 通用元素穿透,首期不按元素拆分。 */
|
||||||
|
UPROPERTY(BlueprintReadOnly, ReplicatedUsing=OnRep_ElementPenetration, Category="PHY|Attributes|Element")
|
||||||
|
FGameplayAttributeData ElementPenetration;
|
||||||
|
PHY_ATTRIBUTE_ACCESSORS(UPHYElementAttributeSet, ElementPenetration);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
/** @brief 复制火元素伤害加成变化。 */
|
||||||
|
UFUNCTION() void OnRep_FireDamageBonus(const FGameplayAttributeData& OldValue);
|
||||||
|
/** @brief 复制水元素伤害加成变化。 */
|
||||||
|
UFUNCTION() void OnRep_WaterDamageBonus(const FGameplayAttributeData& OldValue);
|
||||||
|
/** @brief 复制冰元素伤害加成变化。 */
|
||||||
|
UFUNCTION() void OnRep_IceDamageBonus(const FGameplayAttributeData& OldValue);
|
||||||
|
/** @brief 复制雷元素伤害加成变化。 */
|
||||||
|
UFUNCTION() void OnRep_LightningDamageBonus(const FGameplayAttributeData& OldValue);
|
||||||
|
/** @brief 复制地元素伤害加成变化。 */
|
||||||
|
UFUNCTION() void OnRep_EarthDamageBonus(const FGameplayAttributeData& OldValue);
|
||||||
|
/** @brief 复制风元素伤害加成变化。 */
|
||||||
|
UFUNCTION() void OnRep_WindDamageBonus(const FGameplayAttributeData& OldValue);
|
||||||
|
/** @brief 复制光元素伤害加成变化。 */
|
||||||
|
UFUNCTION() void OnRep_LightDamageBonus(const FGameplayAttributeData& OldValue);
|
||||||
|
/** @brief 复制暗元素伤害加成变化。 */
|
||||||
|
UFUNCTION() void OnRep_DarkDamageBonus(const FGameplayAttributeData& OldValue);
|
||||||
|
/** @brief 复制火元素抗性变化。 */
|
||||||
|
UFUNCTION() void OnRep_FireResistance(const FGameplayAttributeData& OldValue);
|
||||||
|
/** @brief 复制水元素抗性变化。 */
|
||||||
|
UFUNCTION() void OnRep_WaterResistance(const FGameplayAttributeData& OldValue);
|
||||||
|
/** @brief 复制冰元素抗性变化。 */
|
||||||
|
UFUNCTION() void OnRep_IceResistance(const FGameplayAttributeData& OldValue);
|
||||||
|
/** @brief 复制雷元素抗性变化。 */
|
||||||
|
UFUNCTION() void OnRep_LightningResistance(const FGameplayAttributeData& OldValue);
|
||||||
|
/** @brief 复制地元素抗性变化。 */
|
||||||
|
UFUNCTION() void OnRep_EarthResistance(const FGameplayAttributeData& OldValue);
|
||||||
|
/** @brief 复制风元素抗性变化。 */
|
||||||
|
UFUNCTION() void OnRep_WindResistance(const FGameplayAttributeData& OldValue);
|
||||||
|
/** @brief 复制光元素抗性变化。 */
|
||||||
|
UFUNCTION() void OnRep_LightResistance(const FGameplayAttributeData& OldValue);
|
||||||
|
/** @brief 复制暗元素抗性变化。 */
|
||||||
|
UFUNCTION() void OnRep_DarkResistance(const FGameplayAttributeData& OldValue);
|
||||||
|
/** @brief 复制通用元素穿透变化。 */
|
||||||
|
UFUNCTION() void OnRep_ElementPenetration(const FGameplayAttributeData& OldValue);
|
||||||
|
};
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user