第一次提交
This commit is contained in:
21
Source/PHY.Target.cs
Normal file
21
Source/PHY.Target.cs
Normal file
@@ -0,0 +1,21 @@
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
using UnrealBuildTool;
|
||||
using System.Collections.Generic;
|
||||
|
||||
public class PHYTarget : TargetRules
|
||||
{
|
||||
public PHYTarget(TargetInfo Target) : base(Target)
|
||||
{
|
||||
Type = TargetType.Game;
|
||||
DefaultBuildSettings = BuildSettingsVersion.V6;
|
||||
IncludeOrderVersion = EngineIncludeOrderVersion.Unreal5_7;
|
||||
ExtraModuleNames.Add("PHY");
|
||||
RegisterModulesCreatedByRider();
|
||||
}
|
||||
|
||||
private void RegisterModulesCreatedByRider()
|
||||
{
|
||||
ExtraModuleNames.AddRange(new string[] { "PHYInventory" });
|
||||
}
|
||||
}
|
||||
51
Source/PHY/PHY.Build.cs
Normal file
51
Source/PHY/PHY.Build.cs
Normal file
@@ -0,0 +1,51 @@
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
using UnrealBuildTool;
|
||||
|
||||
public class PHY : ModuleRules
|
||||
{
|
||||
public PHY(ReadOnlyTargetRules Target) : base(Target)
|
||||
{
|
||||
PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs;
|
||||
CppStandard = CppStandardVersion.Cpp20;
|
||||
|
||||
PrivateIncludePaths.AddRange(new string[]
|
||||
{
|
||||
System.IO.Path.Combine(ModuleDirectory, "Private"),
|
||||
});
|
||||
|
||||
PublicDependencyModuleNames.AddRange(new string[] {
|
||||
"Core",
|
||||
"CoreUObject",
|
||||
"Engine",
|
||||
"InputCore",
|
||||
"EnhancedInput"
|
||||
});
|
||||
|
||||
PrivateDependencyModuleNames.AddRange(new string[]
|
||||
{
|
||||
"GameplayAbilities",
|
||||
"GameplayTags",
|
||||
"GameplayTasks",
|
||||
"GenericInputSystem",
|
||||
"GenericUISystem",
|
||||
"GenericInventorySystem",
|
||||
"GenericMovementSystem",
|
||||
"Slate",
|
||||
"SlateCore",
|
||||
"AuroraDevs_UGC",
|
||||
"IKRig",
|
||||
"NetCore",
|
||||
"UMG",
|
||||
"CommonUI"
|
||||
});
|
||||
|
||||
// Uncomment if you are using Slate UI
|
||||
// PrivateDependencyModuleNames.AddRange(new string[] { "Slate", "SlateCore" });
|
||||
|
||||
// Uncomment if you are using online features
|
||||
// PrivateDependencyModuleNames.Add("OnlineSubsystem");
|
||||
|
||||
// To include OnlineSubsystemSteam, add it to the plugins section in your uproject file with the Enabled attribute set to true
|
||||
}
|
||||
}
|
||||
6
Source/PHY/PHY.cpp
Normal file
6
Source/PHY/PHY.cpp
Normal file
@@ -0,0 +1,6 @@
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
#include "PHY.h"
|
||||
#include "Modules/ModuleManager.h"
|
||||
|
||||
IMPLEMENT_PRIMARY_GAME_MODULE( FDefaultGameModuleImpl, PHY, "PHY" );
|
||||
6
Source/PHY/PHY.h
Normal file
6
Source/PHY/PHY.h
Normal file
@@ -0,0 +1,6 @@
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
|
||||
246
Source/PHY/Private/AbilitySystem/Attributes/PHYAttributeSet.cpp
Normal file
246
Source/PHY/Private/AbilitySystem/Attributes/PHYAttributeSet.cpp
Normal file
@@ -0,0 +1,246 @@
|
||||
// Copyright 2025 PHY. All Rights Reserved.
|
||||
|
||||
#include "AbilitySystem/Attributes/PHYAttributeSet.h"
|
||||
// (If your include paths don't pick up the module Public folder, use the short include below.)
|
||||
// #include "PHYAttributeSet.h"
|
||||
|
||||
#include "GameplayEffectExtension.h"
|
||||
#include "Net/UnrealNetwork.h"
|
||||
|
||||
UPHYAttributeSet::UPHYAttributeSet()
|
||||
{
|
||||
}
|
||||
|
||||
void UPHYAttributeSet::PreAttributeChange(const FGameplayAttribute& Attribute, float& NewValue)
|
||||
{
|
||||
Super::PreAttributeChange(Attribute, NewValue);
|
||||
|
||||
// Basic clamps
|
||||
if (Attribute == GetHealthAttribute())
|
||||
{
|
||||
NewValue = ClampNonNegative(NewValue);
|
||||
NewValue = FMath::Min(NewValue, GetMaxHealth());
|
||||
}
|
||||
else if (Attribute == GetMaxHealthAttribute())
|
||||
{
|
||||
NewValue = ClampNonNegative(NewValue);
|
||||
}
|
||||
else if (Attribute == GetInnerPowerAttribute())
|
||||
{
|
||||
NewValue = ClampNonNegative(NewValue);
|
||||
NewValue = FMath::Min(NewValue, GetMaxInnerPower());
|
||||
}
|
||||
else if (Attribute == GetMaxInnerPowerAttribute())
|
||||
{
|
||||
NewValue = ClampNonNegative(NewValue);
|
||||
}
|
||||
else if (Attribute == GetMoveSpeedAttribute())
|
||||
{
|
||||
// Allow designers to decide exact caps later; keep reasonable defaults.
|
||||
NewValue = FMath::Clamp(NewValue, 0.f, 2000.f);
|
||||
}
|
||||
else if (Attribute == GetCritChanceAttribute() || Attribute == GetDodgeChanceAttribute() || Attribute == GetHitChanceAttribute()
|
||||
|| Attribute == GetParryChanceAttribute() || Attribute == GetCounterChanceAttribute() || Attribute == GetArmorPenetrationAttribute()
|
||||
|| Attribute == GetDamageReductionAttribute() || Attribute == GetLifeStealAttribute())
|
||||
{
|
||||
NewValue = FMath::Clamp(NewValue, 0.f, 1.f);
|
||||
}
|
||||
else if (Attribute == GetCritDamageAttribute())
|
||||
{
|
||||
// Crit damage multiplier should be >= 1.0
|
||||
NewValue = FMath::Max(1.f, NewValue);
|
||||
}
|
||||
}
|
||||
|
||||
void UPHYAttributeSet::PostGameplayEffectExecute(const FGameplayEffectModCallbackData& Data)
|
||||
{
|
||||
Super::PostGameplayEffectExecute(Data);
|
||||
|
||||
const FGameplayAttribute& Attribute = Data.EvaluatedData.Attribute;
|
||||
|
||||
if (Attribute == GetHealthAttribute())
|
||||
{
|
||||
SetHealth(FMath::Clamp(GetHealth(), 0.f, GetMaxHealth()));
|
||||
}
|
||||
else if (Attribute == GetMaxHealthAttribute())
|
||||
{
|
||||
// When MaxHealth is reduced, clamp Health to new MaxHealth.
|
||||
SetMaxHealth(ClampNonNegative(GetMaxHealth()));
|
||||
SetHealth(FMath::Clamp(GetHealth(), 0.f, GetMaxHealth()));
|
||||
}
|
||||
else if (Attribute == GetInnerPowerAttribute())
|
||||
{
|
||||
SetInnerPower(FMath::Clamp(GetInnerPower(), 0.f, GetMaxInnerPower()));
|
||||
}
|
||||
else if (Attribute == GetMaxInnerPowerAttribute())
|
||||
{
|
||||
SetMaxInnerPower(ClampNonNegative(GetMaxInnerPower()));
|
||||
SetInnerPower(FMath::Clamp(GetInnerPower(), 0.f, GetMaxInnerPower()));
|
||||
}
|
||||
else if (Attribute == GetStrengthAttribute()) { SetStrength(ClampNonNegative(GetStrength())); }
|
||||
else if (Attribute == GetConstitutionAttribute()) { SetConstitution(ClampNonNegative(GetConstitution())); }
|
||||
else if (Attribute == GetInnerBreathAttribute()) { SetInnerBreath(ClampNonNegative(GetInnerBreath())); }
|
||||
else if (Attribute == GetAgilityAttribute()) { SetAgility(ClampNonNegative(GetAgility())); }
|
||||
else if (Attribute == GetPhysicalAttackAttribute()){ SetPhysicalAttack(ClampNonNegative(GetPhysicalAttack())); }
|
||||
else if (Attribute == GetPhysicalDefenseAttribute()){ SetPhysicalDefense(ClampNonNegative(GetPhysicalDefense())); }
|
||||
else if (Attribute == GetMoveSpeedAttribute()) { SetMoveSpeed(FMath::Clamp(GetMoveSpeed(), 0.f, 2000.f)); }
|
||||
else if (Attribute == GetTenacityAttribute()) { SetTenacity(ClampNonNegative(GetTenacity())); }
|
||||
else if (Attribute == GetCritChanceAttribute()) { SetCritChance(FMath::Clamp(GetCritChance(), 0.f, 1.f)); }
|
||||
else if (Attribute == GetCritDamageAttribute()) { SetCritDamage(FMath::Max(1.f, GetCritDamage())); }
|
||||
else if (Attribute == GetDodgeChanceAttribute()) { SetDodgeChance(FMath::Clamp(GetDodgeChance(), 0.f, 1.f)); }
|
||||
else if (Attribute == GetHitChanceAttribute()) { SetHitChance(FMath::Clamp(GetHitChance(), 0.f, 1.f)); }
|
||||
else if (Attribute == GetParryChanceAttribute()) { SetParryChance(FMath::Clamp(GetParryChance(), 0.f, 1.f)); }
|
||||
else if (Attribute == GetCounterChanceAttribute()){ SetCounterChance(FMath::Clamp(GetCounterChance(), 0.f, 1.f)); }
|
||||
else if (Attribute == GetArmorPenetrationAttribute()) { SetArmorPenetration(FMath::Clamp(GetArmorPenetration(), 0.f, 1.f)); }
|
||||
else if (Attribute == GetDamageReductionAttribute()) { SetDamageReduction(FMath::Clamp(GetDamageReduction(), 0.f, 1.f)); }
|
||||
else if (Attribute == GetLifeStealAttribute()) { SetLifeSteal(FMath::Clamp(GetLifeSteal(), 0.f, 1.f)); }
|
||||
else if (Attribute == GetHealthRegenRateAttribute()) { SetHealthRegenRate(ClampNonNegative(GetHealthRegenRate())); }
|
||||
else if (Attribute == GetInnerPowerRegenRateAttribute()) { SetInnerPowerRegenRate(ClampNonNegative(GetInnerPowerRegenRate())); }
|
||||
}
|
||||
|
||||
void UPHYAttributeSet::OnRep_Strength(const FGameplayAttributeData& OldValue)
|
||||
{
|
||||
GAMEPLAYATTRIBUTE_REPNOTIFY(UPHYAttributeSet, Strength, OldValue);
|
||||
}
|
||||
|
||||
void UPHYAttributeSet::OnRep_Constitution(const FGameplayAttributeData& OldValue)
|
||||
{
|
||||
GAMEPLAYATTRIBUTE_REPNOTIFY(UPHYAttributeSet, Constitution, OldValue);
|
||||
}
|
||||
|
||||
void UPHYAttributeSet::OnRep_InnerBreath(const FGameplayAttributeData& OldValue)
|
||||
{
|
||||
GAMEPLAYATTRIBUTE_REPNOTIFY(UPHYAttributeSet, InnerBreath, OldValue);
|
||||
}
|
||||
|
||||
void UPHYAttributeSet::OnRep_Agility(const FGameplayAttributeData& OldValue)
|
||||
{
|
||||
GAMEPLAYATTRIBUTE_REPNOTIFY(UPHYAttributeSet, Agility, OldValue);
|
||||
}
|
||||
|
||||
void UPHYAttributeSet::OnRep_Health(const FGameplayAttributeData& OldValue)
|
||||
{
|
||||
GAMEPLAYATTRIBUTE_REPNOTIFY(UPHYAttributeSet, Health, OldValue);
|
||||
}
|
||||
|
||||
void UPHYAttributeSet::OnRep_MaxHealth(const FGameplayAttributeData& OldValue)
|
||||
{
|
||||
GAMEPLAYATTRIBUTE_REPNOTIFY(UPHYAttributeSet, MaxHealth, OldValue);
|
||||
}
|
||||
|
||||
void UPHYAttributeSet::OnRep_MoveSpeed(const FGameplayAttributeData& OldValue)
|
||||
{
|
||||
GAMEPLAYATTRIBUTE_REPNOTIFY(UPHYAttributeSet, MoveSpeed, OldValue);
|
||||
}
|
||||
|
||||
void UPHYAttributeSet::OnRep_PhysicalAttack(const FGameplayAttributeData& OldValue)
|
||||
{
|
||||
GAMEPLAYATTRIBUTE_REPNOTIFY(UPHYAttributeSet, PhysicalAttack, OldValue);
|
||||
}
|
||||
|
||||
void UPHYAttributeSet::OnRep_PhysicalDefense(const FGameplayAttributeData& OldValue)
|
||||
{
|
||||
GAMEPLAYATTRIBUTE_REPNOTIFY(UPHYAttributeSet, PhysicalDefense, OldValue);
|
||||
}
|
||||
|
||||
void UPHYAttributeSet::OnRep_Tenacity(const FGameplayAttributeData& OldValue)
|
||||
{
|
||||
GAMEPLAYATTRIBUTE_REPNOTIFY(UPHYAttributeSet, Tenacity, OldValue);
|
||||
}
|
||||
|
||||
void UPHYAttributeSet::OnRep_CritChance(const FGameplayAttributeData& OldValue)
|
||||
{
|
||||
GAMEPLAYATTRIBUTE_REPNOTIFY(UPHYAttributeSet, CritChance, OldValue);
|
||||
}
|
||||
|
||||
void UPHYAttributeSet::OnRep_CritDamage(const FGameplayAttributeData& OldValue)
|
||||
{
|
||||
GAMEPLAYATTRIBUTE_REPNOTIFY(UPHYAttributeSet, CritDamage, OldValue);
|
||||
}
|
||||
|
||||
void UPHYAttributeSet::OnRep_DodgeChance(const FGameplayAttributeData& OldValue)
|
||||
{
|
||||
GAMEPLAYATTRIBUTE_REPNOTIFY(UPHYAttributeSet, DodgeChance, OldValue);
|
||||
}
|
||||
|
||||
void UPHYAttributeSet::OnRep_HitChance(const FGameplayAttributeData& OldValue)
|
||||
{
|
||||
GAMEPLAYATTRIBUTE_REPNOTIFY(UPHYAttributeSet, HitChance, OldValue);
|
||||
}
|
||||
|
||||
void UPHYAttributeSet::OnRep_ParryChance(const FGameplayAttributeData& OldValue)
|
||||
{
|
||||
GAMEPLAYATTRIBUTE_REPNOTIFY(UPHYAttributeSet, ParryChance, OldValue);
|
||||
}
|
||||
|
||||
void UPHYAttributeSet::OnRep_CounterChance(const FGameplayAttributeData& OldValue)
|
||||
{
|
||||
GAMEPLAYATTRIBUTE_REPNOTIFY(UPHYAttributeSet, CounterChance, OldValue);
|
||||
}
|
||||
|
||||
void UPHYAttributeSet::OnRep_ArmorPenetration(const FGameplayAttributeData& OldValue)
|
||||
{
|
||||
GAMEPLAYATTRIBUTE_REPNOTIFY(UPHYAttributeSet, ArmorPenetration, OldValue);
|
||||
}
|
||||
|
||||
void UPHYAttributeSet::OnRep_DamageReduction(const FGameplayAttributeData& OldValue)
|
||||
{
|
||||
GAMEPLAYATTRIBUTE_REPNOTIFY(UPHYAttributeSet, DamageReduction, OldValue);
|
||||
}
|
||||
|
||||
void UPHYAttributeSet::OnRep_LifeSteal(const FGameplayAttributeData& OldValue)
|
||||
{
|
||||
GAMEPLAYATTRIBUTE_REPNOTIFY(UPHYAttributeSet, LifeSteal, OldValue);
|
||||
}
|
||||
|
||||
void UPHYAttributeSet::OnRep_HealthRegenRate(const FGameplayAttributeData& OldValue)
|
||||
{
|
||||
GAMEPLAYATTRIBUTE_REPNOTIFY(UPHYAttributeSet, HealthRegenRate, OldValue);
|
||||
}
|
||||
|
||||
void UPHYAttributeSet::OnRep_InnerPower(const FGameplayAttributeData& OldValue)
|
||||
{
|
||||
GAMEPLAYATTRIBUTE_REPNOTIFY(UPHYAttributeSet, InnerPower, OldValue);
|
||||
}
|
||||
|
||||
void UPHYAttributeSet::OnRep_MaxInnerPower(const FGameplayAttributeData& OldValue)
|
||||
{
|
||||
GAMEPLAYATTRIBUTE_REPNOTIFY(UPHYAttributeSet, MaxInnerPower, OldValue);
|
||||
}
|
||||
|
||||
void UPHYAttributeSet::OnRep_InnerPowerRegenRate(const FGameplayAttributeData& OldValue)
|
||||
{
|
||||
GAMEPLAYATTRIBUTE_REPNOTIFY(UPHYAttributeSet, InnerPowerRegenRate, OldValue);
|
||||
}
|
||||
|
||||
void UPHYAttributeSet::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
|
||||
{
|
||||
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
|
||||
|
||||
DOREPLIFETIME_CONDITION_NOTIFY(UPHYAttributeSet, Strength, COND_None, REPNOTIFY_Always);
|
||||
DOREPLIFETIME_CONDITION_NOTIFY(UPHYAttributeSet, Constitution, COND_None, REPNOTIFY_Always);
|
||||
DOREPLIFETIME_CONDITION_NOTIFY(UPHYAttributeSet, InnerBreath, COND_None, REPNOTIFY_Always);
|
||||
DOREPLIFETIME_CONDITION_NOTIFY(UPHYAttributeSet, Agility, COND_None, REPNOTIFY_Always);
|
||||
|
||||
DOREPLIFETIME_CONDITION_NOTIFY(UPHYAttributeSet, Health, COND_None, REPNOTIFY_Always);
|
||||
DOREPLIFETIME_CONDITION_NOTIFY(UPHYAttributeSet, MaxHealth, COND_None, REPNOTIFY_Always);
|
||||
DOREPLIFETIME_CONDITION_NOTIFY(UPHYAttributeSet, InnerPower, COND_None, REPNOTIFY_Always);
|
||||
DOREPLIFETIME_CONDITION_NOTIFY(UPHYAttributeSet, MaxInnerPower, COND_None, REPNOTIFY_Always);
|
||||
|
||||
DOREPLIFETIME_CONDITION_NOTIFY(UPHYAttributeSet, MoveSpeed, COND_None, REPNOTIFY_Always);
|
||||
DOREPLIFETIME_CONDITION_NOTIFY(UPHYAttributeSet, PhysicalAttack, COND_None, REPNOTIFY_Always);
|
||||
DOREPLIFETIME_CONDITION_NOTIFY(UPHYAttributeSet, PhysicalDefense, COND_None, REPNOTIFY_Always);
|
||||
|
||||
DOREPLIFETIME_CONDITION_NOTIFY(UPHYAttributeSet, Tenacity, COND_None, REPNOTIFY_Always);
|
||||
DOREPLIFETIME_CONDITION_NOTIFY(UPHYAttributeSet, CritChance, COND_None, REPNOTIFY_Always);
|
||||
DOREPLIFETIME_CONDITION_NOTIFY(UPHYAttributeSet, CritDamage, COND_None, REPNOTIFY_Always);
|
||||
DOREPLIFETIME_CONDITION_NOTIFY(UPHYAttributeSet, DodgeChance, COND_None, REPNOTIFY_Always);
|
||||
DOREPLIFETIME_CONDITION_NOTIFY(UPHYAttributeSet, HitChance, COND_None, REPNOTIFY_Always);
|
||||
DOREPLIFETIME_CONDITION_NOTIFY(UPHYAttributeSet, ParryChance, COND_None, REPNOTIFY_Always);
|
||||
DOREPLIFETIME_CONDITION_NOTIFY(UPHYAttributeSet, CounterChance, COND_None, REPNOTIFY_Always);
|
||||
DOREPLIFETIME_CONDITION_NOTIFY(UPHYAttributeSet, ArmorPenetration, COND_None, REPNOTIFY_Always);
|
||||
DOREPLIFETIME_CONDITION_NOTIFY(UPHYAttributeSet, DamageReduction, COND_None, REPNOTIFY_Always);
|
||||
DOREPLIFETIME_CONDITION_NOTIFY(UPHYAttributeSet, LifeSteal, COND_None, REPNOTIFY_Always);
|
||||
DOREPLIFETIME_CONDITION_NOTIFY(UPHYAttributeSet, HealthRegenRate, COND_None, REPNOTIFY_Always);
|
||||
DOREPLIFETIME_CONDITION_NOTIFY(UPHYAttributeSet, InnerPowerRegenRate, COND_None, REPNOTIFY_Always);
|
||||
}
|
||||
163
Source/PHY/Private/AbilitySystem/Attributes/PHYAttributeSet.h
Normal file
163
Source/PHY/Private/AbilitySystem/Attributes/PHYAttributeSet.h
Normal file
@@ -0,0 +1,163 @@
|
||||
// Copyright 2025 PHY. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "AttributeSet.h"
|
||||
#include "AbilitySystemComponent.h"
|
||||
#include "PHYAttributeSet.generated.h"
|
||||
|
||||
#define ATTRIBUTE_ACCESSORS(ClassName, PropertyName) \
|
||||
GAMEPLAYATTRIBUTE_PROPERTY_GETTER(ClassName, PropertyName) \
|
||||
GAMEPLAYATTRIBUTE_VALUE_GETTER(PropertyName) \
|
||||
GAMEPLAYATTRIBUTE_VALUE_SETTER(PropertyName) \
|
||||
GAMEPLAYATTRIBUTE_VALUE_INITTER(PropertyName)
|
||||
|
||||
UCLASS()
|
||||
class PHY_API UPHYAttributeSet : public UAttributeSet
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UPHYAttributeSet();
|
||||
|
||||
virtual void PreAttributeChange(const FGameplayAttribute& Attribute, float& NewValue) override;
|
||||
virtual void PostGameplayEffectExecute(const FGameplayEffectModCallbackData& Data) override;
|
||||
virtual void GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const override;
|
||||
|
||||
UPROPERTY(BlueprintReadOnly, Category = "Attributes|Primary", ReplicatedUsing = OnRep_Strength)
|
||||
FGameplayAttributeData Strength;
|
||||
ATTRIBUTE_ACCESSORS(UPHYAttributeSet, Strength)
|
||||
|
||||
UPROPERTY(BlueprintReadOnly, Category = "Attributes|Primary", ReplicatedUsing = OnRep_Constitution)
|
||||
FGameplayAttributeData Constitution;
|
||||
ATTRIBUTE_ACCESSORS(UPHYAttributeSet, Constitution)
|
||||
|
||||
UPROPERTY(BlueprintReadOnly, Category = "Attributes|Primary", ReplicatedUsing = OnRep_InnerBreath)
|
||||
FGameplayAttributeData InnerBreath;
|
||||
ATTRIBUTE_ACCESSORS(UPHYAttributeSet, InnerBreath)
|
||||
|
||||
UPROPERTY(BlueprintReadOnly, Category = "Attributes|Primary", ReplicatedUsing = OnRep_Agility)
|
||||
FGameplayAttributeData Agility;
|
||||
ATTRIBUTE_ACCESSORS(UPHYAttributeSet, Agility)
|
||||
|
||||
UPROPERTY(BlueprintReadOnly, Category = "Attributes|Vitals", ReplicatedUsing = OnRep_Health)
|
||||
FGameplayAttributeData Health;
|
||||
ATTRIBUTE_ACCESSORS(UPHYAttributeSet, Health)
|
||||
|
||||
UPROPERTY(BlueprintReadOnly, Category = "Attributes|Vitals", ReplicatedUsing = OnRep_MaxHealth)
|
||||
FGameplayAttributeData MaxHealth;
|
||||
ATTRIBUTE_ACCESSORS(UPHYAttributeSet, MaxHealth)
|
||||
|
||||
UPROPERTY(BlueprintReadOnly, Category = "Attributes|Derived", ReplicatedUsing = OnRep_MoveSpeed)
|
||||
FGameplayAttributeData MoveSpeed;
|
||||
ATTRIBUTE_ACCESSORS(UPHYAttributeSet, MoveSpeed)
|
||||
|
||||
UPROPERTY(BlueprintReadOnly, Category = "Attributes|Derived", ReplicatedUsing = OnRep_PhysicalAttack)
|
||||
FGameplayAttributeData PhysicalAttack;
|
||||
ATTRIBUTE_ACCESSORS(UPHYAttributeSet, PhysicalAttack)
|
||||
|
||||
UPROPERTY(BlueprintReadOnly, Category = "Attributes|Derived", ReplicatedUsing = OnRep_PhysicalDefense)
|
||||
FGameplayAttributeData PhysicalDefense;
|
||||
ATTRIBUTE_ACCESSORS(UPHYAttributeSet, PhysicalDefense)
|
||||
|
||||
/** Secondary - 韧性(负面效果持续缩短/抵抗) */
|
||||
UPROPERTY(BlueprintReadOnly, Category = "Attributes|Secondary", ReplicatedUsing = OnRep_Tenacity)
|
||||
FGameplayAttributeData Tenacity;
|
||||
ATTRIBUTE_ACCESSORS(UPHYAttributeSet, Tenacity)
|
||||
|
||||
/** Secondary - 暴击率(0-1) */
|
||||
UPROPERTY(BlueprintReadOnly, Category = "Attributes|Secondary", ReplicatedUsing = OnRep_CritChance)
|
||||
FGameplayAttributeData CritChance;
|
||||
ATTRIBUTE_ACCESSORS(UPHYAttributeSet, CritChance)
|
||||
|
||||
/** Secondary - 暴击伤害倍率(例如 1.5 表示 +50%) */
|
||||
UPROPERTY(BlueprintReadOnly, Category = "Attributes|Secondary", ReplicatedUsing = OnRep_CritDamage)
|
||||
FGameplayAttributeData CritDamage;
|
||||
ATTRIBUTE_ACCESSORS(UPHYAttributeSet, CritDamage)
|
||||
|
||||
/** Secondary - 闪避率(0-1) */
|
||||
UPROPERTY(BlueprintReadOnly, Category = "Attributes|Secondary", ReplicatedUsing = OnRep_DodgeChance)
|
||||
FGameplayAttributeData DodgeChance;
|
||||
ATTRIBUTE_ACCESSORS(UPHYAttributeSet, DodgeChance)
|
||||
|
||||
/** Secondary - 命中(可做命中率或命中值) */
|
||||
UPROPERTY(BlueprintReadOnly, Category = "Attributes|Secondary", ReplicatedUsing = OnRep_HitChance)
|
||||
FGameplayAttributeData HitChance;
|
||||
ATTRIBUTE_ACCESSORS(UPHYAttributeSet, HitChance)
|
||||
|
||||
/** Secondary - 招架率(0-1) */
|
||||
UPROPERTY(BlueprintReadOnly, Category = "Attributes|Secondary", ReplicatedUsing = OnRep_ParryChance)
|
||||
FGameplayAttributeData ParryChance;
|
||||
ATTRIBUTE_ACCESSORS(UPHYAttributeSet, ParryChance)
|
||||
|
||||
/** Secondary - 反击几率(0-1),一般在成功闪避/招架后判定 */
|
||||
UPROPERTY(BlueprintReadOnly, Category = "Attributes|Secondary", ReplicatedUsing = OnRep_CounterChance)
|
||||
FGameplayAttributeData CounterChance;
|
||||
ATTRIBUTE_ACCESSORS(UPHYAttributeSet, CounterChance)
|
||||
|
||||
/** Secondary - 穿甲(百分比 0-1 或值,先按 0-1 做) */
|
||||
UPROPERTY(BlueprintReadOnly, Category = "Attributes|Secondary", ReplicatedUsing = OnRep_ArmorPenetration)
|
||||
FGameplayAttributeData ArmorPenetration;
|
||||
ATTRIBUTE_ACCESSORS(UPHYAttributeSet, ArmorPenetration)
|
||||
|
||||
/** Secondary - 免伤(0-1) */
|
||||
UPROPERTY(BlueprintReadOnly, Category = "Attributes|Secondary", ReplicatedUsing = OnRep_DamageReduction)
|
||||
FGameplayAttributeData DamageReduction;
|
||||
ATTRIBUTE_ACCESSORS(UPHYAttributeSet, DamageReduction)
|
||||
|
||||
/** Secondary - 吸血(0-1) */
|
||||
UPROPERTY(BlueprintReadOnly, Category = "Attributes|Secondary", ReplicatedUsing = OnRep_LifeSteal)
|
||||
FGameplayAttributeData LifeSteal;
|
||||
ATTRIBUTE_ACCESSORS(UPHYAttributeSet, LifeSteal)
|
||||
|
||||
/** Secondary - 生命回复/秒 */
|
||||
UPROPERTY(BlueprintReadOnly, Category = "Attributes|Secondary", ReplicatedUsing = OnRep_HealthRegenRate)
|
||||
FGameplayAttributeData HealthRegenRate;
|
||||
ATTRIBUTE_ACCESSORS(UPHYAttributeSet, HealthRegenRate)
|
||||
|
||||
/** Resource - 内力(当前) */
|
||||
UPROPERTY(BlueprintReadOnly, Category = "Attributes|Resource", ReplicatedUsing = OnRep_InnerPower)
|
||||
FGameplayAttributeData InnerPower;
|
||||
ATTRIBUTE_ACCESSORS(UPHYAttributeSet, InnerPower)
|
||||
|
||||
/** Resource - 内力上限 */
|
||||
UPROPERTY(BlueprintReadOnly, Category = "Attributes|Resource", ReplicatedUsing = OnRep_MaxInnerPower)
|
||||
FGameplayAttributeData MaxInnerPower;
|
||||
ATTRIBUTE_ACCESSORS(UPHYAttributeSet, MaxInnerPower)
|
||||
|
||||
/** Secondary - 内力回复/秒 */
|
||||
UPROPERTY(BlueprintReadOnly, Category = "Attributes|Secondary", ReplicatedUsing = OnRep_InnerPowerRegenRate)
|
||||
FGameplayAttributeData InnerPowerRegenRate;
|
||||
ATTRIBUTE_ACCESSORS(UPHYAttributeSet, InnerPowerRegenRate)
|
||||
|
||||
protected:
|
||||
UFUNCTION() void OnRep_Strength(const FGameplayAttributeData& OldValue);
|
||||
UFUNCTION() void OnRep_Constitution(const FGameplayAttributeData& OldValue);
|
||||
UFUNCTION() void OnRep_InnerBreath(const FGameplayAttributeData& OldValue);
|
||||
UFUNCTION() void OnRep_Agility(const FGameplayAttributeData& OldValue);
|
||||
UFUNCTION() void OnRep_Health(const FGameplayAttributeData& OldValue);
|
||||
UFUNCTION() void OnRep_MaxHealth(const FGameplayAttributeData& OldValue);
|
||||
UFUNCTION() void OnRep_MoveSpeed(const FGameplayAttributeData& OldValue);
|
||||
UFUNCTION() void OnRep_PhysicalAttack(const FGameplayAttributeData& OldValue);
|
||||
UFUNCTION() void OnRep_PhysicalDefense(const FGameplayAttributeData& OldValue);
|
||||
UFUNCTION() void OnRep_Tenacity(const FGameplayAttributeData& OldValue);
|
||||
UFUNCTION() void OnRep_CritChance(const FGameplayAttributeData& OldValue);
|
||||
UFUNCTION() void OnRep_CritDamage(const FGameplayAttributeData& OldValue);
|
||||
UFUNCTION() void OnRep_DodgeChance(const FGameplayAttributeData& OldValue);
|
||||
UFUNCTION() void OnRep_HitChance(const FGameplayAttributeData& OldValue);
|
||||
UFUNCTION() void OnRep_ParryChance(const FGameplayAttributeData& OldValue);
|
||||
UFUNCTION() void OnRep_CounterChance(const FGameplayAttributeData& OldValue);
|
||||
UFUNCTION() void OnRep_ArmorPenetration(const FGameplayAttributeData& OldValue);
|
||||
UFUNCTION() void OnRep_DamageReduction(const FGameplayAttributeData& OldValue);
|
||||
UFUNCTION() void OnRep_LifeSteal(const FGameplayAttributeData& OldValue);
|
||||
UFUNCTION() void OnRep_HealthRegenRate(const FGameplayAttributeData& OldValue);
|
||||
UFUNCTION() void OnRep_InnerPower(const FGameplayAttributeData& OldValue);
|
||||
UFUNCTION() void OnRep_MaxInnerPower(const FGameplayAttributeData& OldValue);
|
||||
UFUNCTION() void OnRep_InnerPowerRegenRate(const FGameplayAttributeData& OldValue);
|
||||
|
||||
static float ClampNonNegative(float Value) { return FMath::Max(0.f, Value); }
|
||||
};
|
||||
|
||||
#undef ATTRIBUTE_ACCESSORS
|
||||
|
||||
@@ -0,0 +1,191 @@
|
||||
// Copyright 2025 PHY. All Rights Reserved.
|
||||
|
||||
#include "AbilitySystem/Effects/PHYGE_DerivedAttributes.h"
|
||||
|
||||
#include "AbilitySystem/Attributes/PHYAttributeSet.h"
|
||||
#include "AbilitySystem/MMC/PHY_MMC_MaxHealth.h"
|
||||
#include "AbilitySystem/MMC/PHY_MMC_MoveSpeed.h"
|
||||
#include "AbilitySystem/MMC/PHY_MMC_PhysicalAttack.h"
|
||||
#include "AbilitySystem/MMC/PHY_MMC_PhysicalDefense.h"
|
||||
#include "AbilitySystem/MMC/PHY_MMC_MaxInnerPower.h"
|
||||
#include "AbilitySystem/MMC/PHY_MMC_Tenacity.h"
|
||||
#include "AbilitySystem/MMC/PHY_MMC_CritChance.h"
|
||||
#include "AbilitySystem/MMC/PHY_MMC_CritDamage.h"
|
||||
#include "AbilitySystem/MMC/PHY_MMC_DodgeChance.h"
|
||||
#include "AbilitySystem/MMC/PHY_MMC_HitChance.h"
|
||||
#include "AbilitySystem/MMC/PHY_MMC_ParryChance.h"
|
||||
#include "AbilitySystem/MMC/PHY_MMC_CounterChance.h"
|
||||
#include "AbilitySystem/MMC/PHY_MMC_DamageReduction.h"
|
||||
#include "AbilitySystem/MMC/PHY_MMC_HealthRegenRate.h"
|
||||
#include "AbilitySystem/MMC/PHY_MMC_InnerPowerRegenRate.h"
|
||||
|
||||
UPHYGE_DerivedAttributes::UPHYGE_DerivedAttributes()
|
||||
{
|
||||
DurationPolicy = EGameplayEffectDurationType::Infinite;
|
||||
|
||||
// MaxHealth
|
||||
{
|
||||
FGameplayModifierInfo Info;
|
||||
Info.Attribute = UPHYAttributeSet::GetMaxHealthAttribute();
|
||||
Info.ModifierOp = EGameplayModOp::Override;
|
||||
FCustomCalculationBasedFloat Calc;
|
||||
Calc.CalculationClassMagnitude = UPHY_MMC_MaxHealth::StaticClass();
|
||||
Info.ModifierMagnitude = FGameplayEffectModifierMagnitude(Calc);
|
||||
Modifiers.Add(Info);
|
||||
}
|
||||
|
||||
// MoveSpeed
|
||||
{
|
||||
FGameplayModifierInfo Info;
|
||||
Info.Attribute = UPHYAttributeSet::GetMoveSpeedAttribute();
|
||||
Info.ModifierOp = EGameplayModOp::Override;
|
||||
FCustomCalculationBasedFloat Calc;
|
||||
Calc.CalculationClassMagnitude = UPHY_MMC_MoveSpeed::StaticClass();
|
||||
Info.ModifierMagnitude = FGameplayEffectModifierMagnitude(Calc);
|
||||
Modifiers.Add(Info);
|
||||
}
|
||||
|
||||
// PhysicalAttack
|
||||
{
|
||||
FGameplayModifierInfo Info;
|
||||
Info.Attribute = UPHYAttributeSet::GetPhysicalAttackAttribute();
|
||||
Info.ModifierOp = EGameplayModOp::Override;
|
||||
FCustomCalculationBasedFloat Calc;
|
||||
Calc.CalculationClassMagnitude = UPHY_MMC_PhysicalAttack::StaticClass();
|
||||
Info.ModifierMagnitude = FGameplayEffectModifierMagnitude(Calc);
|
||||
Modifiers.Add(Info);
|
||||
}
|
||||
|
||||
// PhysicalDefense
|
||||
{
|
||||
FGameplayModifierInfo Info;
|
||||
Info.Attribute = UPHYAttributeSet::GetPhysicalDefenseAttribute();
|
||||
Info.ModifierOp = EGameplayModOp::Override;
|
||||
FCustomCalculationBasedFloat Calc;
|
||||
Calc.CalculationClassMagnitude = UPHY_MMC_PhysicalDefense::StaticClass();
|
||||
Info.ModifierMagnitude = FGameplayEffectModifierMagnitude(Calc);
|
||||
Modifiers.Add(Info);
|
||||
}
|
||||
|
||||
// MaxInnerPower
|
||||
{
|
||||
FGameplayModifierInfo Info;
|
||||
Info.Attribute = UPHYAttributeSet::GetMaxInnerPowerAttribute();
|
||||
Info.ModifierOp = EGameplayModOp::Override;
|
||||
FCustomCalculationBasedFloat Calc;
|
||||
Calc.CalculationClassMagnitude = UPHY_MMC_MaxInnerPower::StaticClass();
|
||||
Info.ModifierMagnitude = FGameplayEffectModifierMagnitude(Calc);
|
||||
Modifiers.Add(Info);
|
||||
}
|
||||
|
||||
// Tenacity
|
||||
{
|
||||
FGameplayModifierInfo Info;
|
||||
Info.Attribute = UPHYAttributeSet::GetTenacityAttribute();
|
||||
Info.ModifierOp = EGameplayModOp::Override;
|
||||
FCustomCalculationBasedFloat Calc;
|
||||
Calc.CalculationClassMagnitude = UPHY_MMC_Tenacity::StaticClass();
|
||||
Info.ModifierMagnitude = FGameplayEffectModifierMagnitude(Calc);
|
||||
Modifiers.Add(Info);
|
||||
}
|
||||
|
||||
// CritChance
|
||||
{
|
||||
FGameplayModifierInfo Info;
|
||||
Info.Attribute = UPHYAttributeSet::GetCritChanceAttribute();
|
||||
Info.ModifierOp = EGameplayModOp::Override;
|
||||
FCustomCalculationBasedFloat Calc;
|
||||
Calc.CalculationClassMagnitude = UPHY_MMC_CritChance::StaticClass();
|
||||
Info.ModifierMagnitude = FGameplayEffectModifierMagnitude(Calc);
|
||||
Modifiers.Add(Info);
|
||||
}
|
||||
|
||||
// CritDamage
|
||||
{
|
||||
FGameplayModifierInfo Info;
|
||||
Info.Attribute = UPHYAttributeSet::GetCritDamageAttribute();
|
||||
Info.ModifierOp = EGameplayModOp::Override;
|
||||
FCustomCalculationBasedFloat Calc;
|
||||
Calc.CalculationClassMagnitude = UPHY_MMC_CritDamage::StaticClass();
|
||||
Info.ModifierMagnitude = FGameplayEffectModifierMagnitude(Calc);
|
||||
Modifiers.Add(Info);
|
||||
}
|
||||
|
||||
// DodgeChance
|
||||
{
|
||||
FGameplayModifierInfo Info;
|
||||
Info.Attribute = UPHYAttributeSet::GetDodgeChanceAttribute();
|
||||
Info.ModifierOp = EGameplayModOp::Override;
|
||||
FCustomCalculationBasedFloat Calc;
|
||||
Calc.CalculationClassMagnitude = UPHY_MMC_DodgeChance::StaticClass();
|
||||
Info.ModifierMagnitude = FGameplayEffectModifierMagnitude(Calc);
|
||||
Modifiers.Add(Info);
|
||||
}
|
||||
|
||||
// HitChance
|
||||
{
|
||||
FGameplayModifierInfo Info;
|
||||
Info.Attribute = UPHYAttributeSet::GetHitChanceAttribute();
|
||||
Info.ModifierOp = EGameplayModOp::Override;
|
||||
FCustomCalculationBasedFloat Calc;
|
||||
Calc.CalculationClassMagnitude = UPHY_MMC_HitChance::StaticClass();
|
||||
Info.ModifierMagnitude = FGameplayEffectModifierMagnitude(Calc);
|
||||
Modifiers.Add(Info);
|
||||
}
|
||||
|
||||
// ParryChance
|
||||
{
|
||||
FGameplayModifierInfo Info;
|
||||
Info.Attribute = UPHYAttributeSet::GetParryChanceAttribute();
|
||||
Info.ModifierOp = EGameplayModOp::Override;
|
||||
FCustomCalculationBasedFloat Calc;
|
||||
Calc.CalculationClassMagnitude = UPHY_MMC_ParryChance::StaticClass();
|
||||
Info.ModifierMagnitude = FGameplayEffectModifierMagnitude(Calc);
|
||||
Modifiers.Add(Info);
|
||||
}
|
||||
|
||||
// CounterChance
|
||||
{
|
||||
FGameplayModifierInfo Info;
|
||||
Info.Attribute = UPHYAttributeSet::GetCounterChanceAttribute();
|
||||
Info.ModifierOp = EGameplayModOp::Override;
|
||||
FCustomCalculationBasedFloat Calc;
|
||||
Calc.CalculationClassMagnitude = UPHY_MMC_CounterChance::StaticClass();
|
||||
Info.ModifierMagnitude = FGameplayEffectModifierMagnitude(Calc);
|
||||
Modifiers.Add(Info);
|
||||
}
|
||||
|
||||
// DamageReduction
|
||||
{
|
||||
FGameplayModifierInfo Info;
|
||||
Info.Attribute = UPHYAttributeSet::GetDamageReductionAttribute();
|
||||
Info.ModifierOp = EGameplayModOp::Override;
|
||||
FCustomCalculationBasedFloat Calc;
|
||||
Calc.CalculationClassMagnitude = UPHY_MMC_DamageReduction::StaticClass();
|
||||
Info.ModifierMagnitude = FGameplayEffectModifierMagnitude(Calc);
|
||||
Modifiers.Add(Info);
|
||||
}
|
||||
|
||||
// HealthRegenRate
|
||||
{
|
||||
FGameplayModifierInfo Info;
|
||||
Info.Attribute = UPHYAttributeSet::GetHealthRegenRateAttribute();
|
||||
Info.ModifierOp = EGameplayModOp::Override;
|
||||
FCustomCalculationBasedFloat Calc;
|
||||
Calc.CalculationClassMagnitude = UPHY_MMC_HealthRegenRate::StaticClass();
|
||||
Info.ModifierMagnitude = FGameplayEffectModifierMagnitude(Calc);
|
||||
Modifiers.Add(Info);
|
||||
}
|
||||
|
||||
// InnerPowerRegenRate
|
||||
{
|
||||
FGameplayModifierInfo Info;
|
||||
Info.Attribute = UPHYAttributeSet::GetInnerPowerRegenRateAttribute();
|
||||
Info.ModifierOp = EGameplayModOp::Override;
|
||||
FCustomCalculationBasedFloat Calc;
|
||||
Calc.CalculationClassMagnitude = UPHY_MMC_InnerPowerRegenRate::StaticClass();
|
||||
Info.ModifierMagnitude = FGameplayEffectModifierMagnitude(Calc);
|
||||
Modifiers.Add(Info);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
// Copyright 2025 PHY. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "GameplayEffect.h"
|
||||
#include "PHYGE_DerivedAttributes.generated.h"
|
||||
|
||||
/**
|
||||
* Infinite GE that drives derived attributes via MMCs.
|
||||
*
|
||||
* Applied once on spawn/login; re-apply if you need to refresh snapshot-based MMCs.
|
||||
*/
|
||||
UCLASS()
|
||||
class PHY_API UPHYGE_DerivedAttributes : public UGameplayEffect
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UPHYGE_DerivedAttributes();
|
||||
};
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
// Copyright 2025 PHY. All Rights Reserved.
|
||||
|
||||
#include "AbilitySystem/Effects/PHYGE_InitPrimary.h"
|
||||
|
||||
#include "AbilitySystem/Attributes/PHYAttributeSet.h"
|
||||
#include "GameplayTags/InitAttributeTags.h"
|
||||
|
||||
UPHYGE_InitPrimary::UPHYGE_InitPrimary()
|
||||
{
|
||||
DurationPolicy = EGameplayEffectDurationType::Instant;
|
||||
|
||||
// Use SetByCaller so one GE works for every class.
|
||||
auto AddSetByCaller = [this](FGameplayAttribute Attr, FGameplayTag DataTag)
|
||||
{
|
||||
FGameplayModifierInfo Info;
|
||||
Info.Attribute = Attr;
|
||||
Info.ModifierOp = EGameplayModOp::Override;
|
||||
FSetByCallerFloat SetByCaller;
|
||||
SetByCaller.DataTag = DataTag;
|
||||
Info.ModifierMagnitude = FGameplayEffectModifierMagnitude(SetByCaller);
|
||||
Modifiers.Add(Info);
|
||||
};
|
||||
|
||||
AddSetByCaller(UPHYAttributeSet::GetStrengthAttribute(), InitAttributeTags::Tag__Data_Init_Primary_Strength);
|
||||
AddSetByCaller(UPHYAttributeSet::GetConstitutionAttribute(), InitAttributeTags::Tag__Data_Init_Primary_Constitution);
|
||||
AddSetByCaller(UPHYAttributeSet::GetInnerBreathAttribute(), InitAttributeTags::Tag__Data_Init_Primary_InnerBreath);
|
||||
AddSetByCaller(UPHYAttributeSet::GetAgilityAttribute(), InitAttributeTags::Tag__Data_Init_Primary_Agility);
|
||||
|
||||
// Also initialize current Health to MaxHealth after derived GE runs. We'll set Health in code post-apply.
|
||||
}
|
||||
18
Source/PHY/Private/AbilitySystem/Effects/PHYGE_InitPrimary.h
Normal file
18
Source/PHY/Private/AbilitySystem/Effects/PHYGE_InitPrimary.h
Normal file
@@ -0,0 +1,18 @@
|
||||
// Copyright 2025 PHY. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "GameplayEffect.h"
|
||||
#include "PHYGE_InitPrimary.generated.h"
|
||||
|
||||
/** Instant GE that sets initial primary stats (四维). */
|
||||
UCLASS()
|
||||
class PHY_API UPHYGE_InitPrimary : public UGameplayEffect
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UPHYGE_InitPrimary();
|
||||
};
|
||||
|
||||
33
Source/PHY/Private/AbilitySystem/Effects/PHYGE_RegenTick.cpp
Normal file
33
Source/PHY/Private/AbilitySystem/Effects/PHYGE_RegenTick.cpp
Normal file
@@ -0,0 +1,33 @@
|
||||
// Copyright 2025 PHY. All Rights Reserved.
|
||||
|
||||
#include "AbilitySystem/Effects/PHYGE_RegenTick.h"
|
||||
|
||||
#include "AbilitySystem/Attributes/PHYAttributeSet.h"
|
||||
#include "GameplayTags/RegenTags.h"
|
||||
|
||||
UPHYGE_RegenTick::UPHYGE_RegenTick()
|
||||
{
|
||||
DurationPolicy = EGameplayEffectDurationType::Instant;
|
||||
|
||||
// Add Health each tick
|
||||
{
|
||||
FGameplayModifierInfo Info;
|
||||
Info.Attribute = UPHYAttributeSet::GetHealthAttribute();
|
||||
Info.ModifierOp = EGameplayModOp::Additive;
|
||||
FSetByCallerFloat SBC;
|
||||
SBC.DataTag = RegenTags::Tag__Data_Regen_Health;
|
||||
Info.ModifierMagnitude = FGameplayEffectModifierMagnitude(SBC);
|
||||
Modifiers.Add(Info);
|
||||
}
|
||||
|
||||
// Add InnerPower each tick
|
||||
{
|
||||
FGameplayModifierInfo Info;
|
||||
Info.Attribute = UPHYAttributeSet::GetInnerPowerAttribute();
|
||||
Info.ModifierOp = EGameplayModOp::Additive;
|
||||
FSetByCallerFloat SBC;
|
||||
SBC.DataTag = RegenTags::Tag__Data_Regen_InnerPower;
|
||||
Info.ModifierMagnitude = FGameplayEffectModifierMagnitude(SBC);
|
||||
Modifiers.Add(Info);
|
||||
}
|
||||
}
|
||||
18
Source/PHY/Private/AbilitySystem/Effects/PHYGE_RegenTick.h
Normal file
18
Source/PHY/Private/AbilitySystem/Effects/PHYGE_RegenTick.h
Normal file
@@ -0,0 +1,18 @@
|
||||
// Copyright 2025 PHY. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "GameplayEffect.h"
|
||||
#include "PHYGE_RegenTick.generated.h"
|
||||
|
||||
/** Instant GE applied periodically (code-driven) to regen Health and InnerPower. */
|
||||
UCLASS()
|
||||
class PHY_API UPHYGE_RegenTick : public UGameplayEffect
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UPHYGE_RegenTick();
|
||||
};
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
// Copyright 2025 PHY. All Rights Reserved.
|
||||
|
||||
#include "AbilitySystem/MMC/PHY_MMC_CounterChance.h"
|
||||
|
||||
#include "AbilitySystem/Attributes/PHYAttributeSet.h"
|
||||
|
||||
UPHY_MMC_CounterChance::UPHY_MMC_CounterChance()
|
||||
{
|
||||
AgilityDef = FGameplayEffectAttributeCaptureDefinition(
|
||||
UPHYAttributeSet::GetAgilityAttribute(),
|
||||
EGameplayEffectAttributeCaptureSource::Target,
|
||||
true);
|
||||
|
||||
RelevantAttributesToCapture.Add(AgilityDef);
|
||||
}
|
||||
|
||||
float UPHY_MMC_CounterChance::CalculateBaseMagnitude_Implementation(const FGameplayEffectSpec& Spec) const
|
||||
{
|
||||
FAggregatorEvaluateParameters Params;
|
||||
Params.SourceTags = Spec.CapturedSourceTags.GetAggregatedTags();
|
||||
Params.TargetTags = Spec.CapturedTargetTags.GetAggregatedTags();
|
||||
|
||||
float Agility = 0.f;
|
||||
GetCapturedAttributeMagnitude(AgilityDef, Spec, Params, Agility);
|
||||
Agility = FMath::Max(0.f, Agility);
|
||||
|
||||
constexpr float Base = 0.1f;
|
||||
constexpr float K = 0.001f;
|
||||
return FMath::Clamp(Base + Agility * K, 0.f, 1.f);
|
||||
}
|
||||
|
||||
22
Source/PHY/Private/AbilitySystem/MMC/PHY_MMC_CounterChance.h
Normal file
22
Source/PHY/Private/AbilitySystem/MMC/PHY_MMC_CounterChance.h
Normal file
@@ -0,0 +1,22 @@
|
||||
// Copyright 2025 PHY. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "GameplayModMagnitudeCalculation.h"
|
||||
#include "PHY_MMC_CounterChance.generated.h"
|
||||
|
||||
/** CounterChance = Base(0.1) + Agility * 0.001 */
|
||||
UCLASS()
|
||||
class PHY_API UPHY_MMC_CounterChance : public UGameplayModMagnitudeCalculation
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UPHY_MMC_CounterChance();
|
||||
virtual float CalculateBaseMagnitude_Implementation(const FGameplayEffectSpec& Spec) const override;
|
||||
|
||||
protected:
|
||||
FGameplayEffectAttributeCaptureDefinition AgilityDef;
|
||||
};
|
||||
|
||||
31
Source/PHY/Private/AbilitySystem/MMC/PHY_MMC_CritChance.cpp
Normal file
31
Source/PHY/Private/AbilitySystem/MMC/PHY_MMC_CritChance.cpp
Normal file
@@ -0,0 +1,31 @@
|
||||
// Copyright 2025 PHY. All Rights Reserved.
|
||||
|
||||
#include "AbilitySystem/MMC/PHY_MMC_CritChance.h"
|
||||
|
||||
#include "AbilitySystem/Attributes/PHYAttributeSet.h"
|
||||
|
||||
UPHY_MMC_CritChance::UPHY_MMC_CritChance()
|
||||
{
|
||||
AgilityDef = FGameplayEffectAttributeCaptureDefinition(
|
||||
UPHYAttributeSet::GetAgilityAttribute(),
|
||||
EGameplayEffectAttributeCaptureSource::Target,
|
||||
true);
|
||||
|
||||
RelevantAttributesToCapture.Add(AgilityDef);
|
||||
}
|
||||
|
||||
float UPHY_MMC_CritChance::CalculateBaseMagnitude_Implementation(const FGameplayEffectSpec& Spec) const
|
||||
{
|
||||
FAggregatorEvaluateParameters Params;
|
||||
Params.SourceTags = Spec.CapturedSourceTags.GetAggregatedTags();
|
||||
Params.TargetTags = Spec.CapturedTargetTags.GetAggregatedTags();
|
||||
|
||||
float Agility = 0.f;
|
||||
GetCapturedAttributeMagnitude(AgilityDef, Spec, Params, Agility);
|
||||
Agility = FMath::Max(0.f, Agility);
|
||||
|
||||
constexpr float Base = 0.05f;
|
||||
constexpr float K = 0.002f;
|
||||
return FMath::Clamp(Base + Agility * K, 0.f, 1.f);
|
||||
}
|
||||
|
||||
22
Source/PHY/Private/AbilitySystem/MMC/PHY_MMC_CritChance.h
Normal file
22
Source/PHY/Private/AbilitySystem/MMC/PHY_MMC_CritChance.h
Normal file
@@ -0,0 +1,22 @@
|
||||
// Copyright 2025 PHY. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "GameplayModMagnitudeCalculation.h"
|
||||
#include "PHY_MMC_CritChance.generated.h"
|
||||
|
||||
/** CritChance = Base(0.05) + Agility * 0.002 */
|
||||
UCLASS()
|
||||
class PHY_API UPHY_MMC_CritChance : public UGameplayModMagnitudeCalculation
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UPHY_MMC_CritChance();
|
||||
virtual float CalculateBaseMagnitude_Implementation(const FGameplayEffectSpec& Spec) const override;
|
||||
|
||||
protected:
|
||||
FGameplayEffectAttributeCaptureDefinition AgilityDef;
|
||||
};
|
||||
|
||||
31
Source/PHY/Private/AbilitySystem/MMC/PHY_MMC_CritDamage.cpp
Normal file
31
Source/PHY/Private/AbilitySystem/MMC/PHY_MMC_CritDamage.cpp
Normal file
@@ -0,0 +1,31 @@
|
||||
// Copyright 2025 PHY. All Rights Reserved.
|
||||
|
||||
#include "AbilitySystem/MMC/PHY_MMC_CritDamage.h"
|
||||
|
||||
#include "AbilitySystem/Attributes/PHYAttributeSet.h"
|
||||
|
||||
UPHY_MMC_CritDamage::UPHY_MMC_CritDamage()
|
||||
{
|
||||
StrengthDef = FGameplayEffectAttributeCaptureDefinition(
|
||||
UPHYAttributeSet::GetStrengthAttribute(),
|
||||
EGameplayEffectAttributeCaptureSource::Target,
|
||||
true);
|
||||
|
||||
RelevantAttributesToCapture.Add(StrengthDef);
|
||||
}
|
||||
|
||||
float UPHY_MMC_CritDamage::CalculateBaseMagnitude_Implementation(const FGameplayEffectSpec& Spec) const
|
||||
{
|
||||
FAggregatorEvaluateParameters Params;
|
||||
Params.SourceTags = Spec.CapturedSourceTags.GetAggregatedTags();
|
||||
Params.TargetTags = Spec.CapturedTargetTags.GetAggregatedTags();
|
||||
|
||||
float Strength = 0.f;
|
||||
GetCapturedAttributeMagnitude(StrengthDef, Spec, Params, Strength);
|
||||
Strength = FMath::Max(0.f, Strength);
|
||||
|
||||
constexpr float Base = 1.5f;
|
||||
constexpr float K = 0.005f;
|
||||
return FMath::Max(1.f, Base + Strength * K);
|
||||
}
|
||||
|
||||
22
Source/PHY/Private/AbilitySystem/MMC/PHY_MMC_CritDamage.h
Normal file
22
Source/PHY/Private/AbilitySystem/MMC/PHY_MMC_CritDamage.h
Normal file
@@ -0,0 +1,22 @@
|
||||
// Copyright 2025 PHY. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "GameplayModMagnitudeCalculation.h"
|
||||
#include "PHY_MMC_CritDamage.generated.h"
|
||||
|
||||
/** CritDamage = Base(1.5) + Strength * 0.005 */
|
||||
UCLASS()
|
||||
class PHY_API UPHY_MMC_CritDamage : public UGameplayModMagnitudeCalculation
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UPHY_MMC_CritDamage();
|
||||
virtual float CalculateBaseMagnitude_Implementation(const FGameplayEffectSpec& Spec) const override;
|
||||
|
||||
protected:
|
||||
FGameplayEffectAttributeCaptureDefinition StrengthDef;
|
||||
};
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
// Copyright 2025 PHY. All Rights Reserved.
|
||||
|
||||
#include "AbilitySystem/MMC/PHY_MMC_DamageReduction.h"
|
||||
|
||||
#include "AbilitySystem/Attributes/PHYAttributeSet.h"
|
||||
|
||||
UPHY_MMC_DamageReduction::UPHY_MMC_DamageReduction()
|
||||
{
|
||||
ConstitutionDef = FGameplayEffectAttributeCaptureDefinition(
|
||||
UPHYAttributeSet::GetConstitutionAttribute(),
|
||||
EGameplayEffectAttributeCaptureSource::Target,
|
||||
true);
|
||||
|
||||
RelevantAttributesToCapture.Add(ConstitutionDef);
|
||||
}
|
||||
|
||||
float UPHY_MMC_DamageReduction::CalculateBaseMagnitude_Implementation(const FGameplayEffectSpec& Spec) const
|
||||
{
|
||||
FAggregatorEvaluateParameters Params;
|
||||
Params.SourceTags = Spec.CapturedSourceTags.GetAggregatedTags();
|
||||
Params.TargetTags = Spec.CapturedTargetTags.GetAggregatedTags();
|
||||
|
||||
float Constitution = 0.f;
|
||||
GetCapturedAttributeMagnitude(ConstitutionDef, Spec, Params, Constitution);
|
||||
Constitution = FMath::Max(0.f, Constitution);
|
||||
|
||||
constexpr float Base = 0.f;
|
||||
constexpr float K = 0.001f;
|
||||
return FMath::Clamp(Base + Constitution * K, 0.f, 1.f);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
// Copyright 2025 PHY. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "GameplayModMagnitudeCalculation.h"
|
||||
#include "PHY_MMC_DamageReduction.generated.h"
|
||||
|
||||
/** DamageReduction = Base(0) + Constitution * 0.001 */
|
||||
UCLASS()
|
||||
class PHY_API UPHY_MMC_DamageReduction : public UGameplayModMagnitudeCalculation
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UPHY_MMC_DamageReduction();
|
||||
virtual float CalculateBaseMagnitude_Implementation(const FGameplayEffectSpec& Spec) const override;
|
||||
|
||||
protected:
|
||||
FGameplayEffectAttributeCaptureDefinition ConstitutionDef;
|
||||
};
|
||||
|
||||
31
Source/PHY/Private/AbilitySystem/MMC/PHY_MMC_DodgeChance.cpp
Normal file
31
Source/PHY/Private/AbilitySystem/MMC/PHY_MMC_DodgeChance.cpp
Normal file
@@ -0,0 +1,31 @@
|
||||
// Copyright 2025 PHY. All Rights Reserved.
|
||||
|
||||
#include "AbilitySystem/MMC/PHY_MMC_DodgeChance.h"
|
||||
|
||||
#include "AbilitySystem/Attributes/PHYAttributeSet.h"
|
||||
|
||||
UPHY_MMC_DodgeChance::UPHY_MMC_DodgeChance()
|
||||
{
|
||||
AgilityDef = FGameplayEffectAttributeCaptureDefinition(
|
||||
UPHYAttributeSet::GetAgilityAttribute(),
|
||||
EGameplayEffectAttributeCaptureSource::Target,
|
||||
true);
|
||||
|
||||
RelevantAttributesToCapture.Add(AgilityDef);
|
||||
}
|
||||
|
||||
float UPHY_MMC_DodgeChance::CalculateBaseMagnitude_Implementation(const FGameplayEffectSpec& Spec) const
|
||||
{
|
||||
FAggregatorEvaluateParameters Params;
|
||||
Params.SourceTags = Spec.CapturedSourceTags.GetAggregatedTags();
|
||||
Params.TargetTags = Spec.CapturedTargetTags.GetAggregatedTags();
|
||||
|
||||
float Agility = 0.f;
|
||||
GetCapturedAttributeMagnitude(AgilityDef, Spec, Params, Agility);
|
||||
Agility = FMath::Max(0.f, Agility);
|
||||
|
||||
constexpr float Base = 0.02f;
|
||||
constexpr float K = 0.002f;
|
||||
return FMath::Clamp(Base + Agility * K, 0.f, 1.f);
|
||||
}
|
||||
|
||||
22
Source/PHY/Private/AbilitySystem/MMC/PHY_MMC_DodgeChance.h
Normal file
22
Source/PHY/Private/AbilitySystem/MMC/PHY_MMC_DodgeChance.h
Normal file
@@ -0,0 +1,22 @@
|
||||
// Copyright 2025 PHY. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "GameplayModMagnitudeCalculation.h"
|
||||
#include "PHY_MMC_DodgeChance.generated.h"
|
||||
|
||||
/** DodgeChance = Base(0.02) + Agility * 0.002 */
|
||||
UCLASS()
|
||||
class PHY_API UPHY_MMC_DodgeChance : public UGameplayModMagnitudeCalculation
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UPHY_MMC_DodgeChance();
|
||||
virtual float CalculateBaseMagnitude_Implementation(const FGameplayEffectSpec& Spec) const override;
|
||||
|
||||
protected:
|
||||
FGameplayEffectAttributeCaptureDefinition AgilityDef;
|
||||
};
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
// Copyright 2025 PHY. All Rights Reserved.
|
||||
|
||||
#include "AbilitySystem/MMC/PHY_MMC_HealthRegenRate.h"
|
||||
|
||||
#include "AbilitySystem/Attributes/PHYAttributeSet.h"
|
||||
|
||||
UPHY_MMC_HealthRegenRate::UPHY_MMC_HealthRegenRate()
|
||||
{
|
||||
ConstitutionDef = FGameplayEffectAttributeCaptureDefinition(
|
||||
UPHYAttributeSet::GetConstitutionAttribute(),
|
||||
EGameplayEffectAttributeCaptureSource::Target,
|
||||
true);
|
||||
|
||||
RelevantAttributesToCapture.Add(ConstitutionDef);
|
||||
}
|
||||
|
||||
float UPHY_MMC_HealthRegenRate::CalculateBaseMagnitude_Implementation(const FGameplayEffectSpec& Spec) const
|
||||
{
|
||||
FAggregatorEvaluateParameters Params;
|
||||
Params.SourceTags = Spec.CapturedSourceTags.GetAggregatedTags();
|
||||
Params.TargetTags = Spec.CapturedTargetTags.GetAggregatedTags();
|
||||
|
||||
float Constitution = 0.f;
|
||||
GetCapturedAttributeMagnitude(ConstitutionDef, Spec, Params, Constitution);
|
||||
Constitution = FMath::Max(0.f, Constitution);
|
||||
|
||||
constexpr float K = 0.1f;
|
||||
return Constitution * K;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
// Copyright 2025 PHY. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "GameplayModMagnitudeCalculation.h"
|
||||
#include "PHY_MMC_HealthRegenRate.generated.h"
|
||||
|
||||
/** HealthRegenRate = Constitution * 0.1 */
|
||||
UCLASS()
|
||||
class PHY_API UPHY_MMC_HealthRegenRate : public UGameplayModMagnitudeCalculation
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UPHY_MMC_HealthRegenRate();
|
||||
virtual float CalculateBaseMagnitude_Implementation(const FGameplayEffectSpec& Spec) const override;
|
||||
|
||||
protected:
|
||||
FGameplayEffectAttributeCaptureDefinition ConstitutionDef;
|
||||
};
|
||||
|
||||
31
Source/PHY/Private/AbilitySystem/MMC/PHY_MMC_HitChance.cpp
Normal file
31
Source/PHY/Private/AbilitySystem/MMC/PHY_MMC_HitChance.cpp
Normal file
@@ -0,0 +1,31 @@
|
||||
// Copyright 2025 PHY. All Rights Reserved.
|
||||
|
||||
#include "AbilitySystem/MMC/PHY_MMC_HitChance.h"
|
||||
|
||||
#include "AbilitySystem/Attributes/PHYAttributeSet.h"
|
||||
|
||||
UPHY_MMC_HitChance::UPHY_MMC_HitChance()
|
||||
{
|
||||
AgilityDef = FGameplayEffectAttributeCaptureDefinition(
|
||||
UPHYAttributeSet::GetAgilityAttribute(),
|
||||
EGameplayEffectAttributeCaptureSource::Target,
|
||||
true);
|
||||
|
||||
RelevantAttributesToCapture.Add(AgilityDef);
|
||||
}
|
||||
|
||||
float UPHY_MMC_HitChance::CalculateBaseMagnitude_Implementation(const FGameplayEffectSpec& Spec) const
|
||||
{
|
||||
FAggregatorEvaluateParameters Params;
|
||||
Params.SourceTags = Spec.CapturedSourceTags.GetAggregatedTags();
|
||||
Params.TargetTags = Spec.CapturedTargetTags.GetAggregatedTags();
|
||||
|
||||
float Agility = 0.f;
|
||||
GetCapturedAttributeMagnitude(AgilityDef, Spec, Params, Agility);
|
||||
Agility = FMath::Max(0.f, Agility);
|
||||
|
||||
constexpr float Base = 0.9f;
|
||||
constexpr float K = 0.001f;
|
||||
return FMath::Clamp(Base + Agility * K, 0.f, 1.f);
|
||||
}
|
||||
|
||||
22
Source/PHY/Private/AbilitySystem/MMC/PHY_MMC_HitChance.h
Normal file
22
Source/PHY/Private/AbilitySystem/MMC/PHY_MMC_HitChance.h
Normal file
@@ -0,0 +1,22 @@
|
||||
// Copyright 2025 PHY. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "GameplayModMagnitudeCalculation.h"
|
||||
#include "PHY_MMC_HitChance.generated.h"
|
||||
|
||||
/** HitChance = Base(0.9) + Agility * 0.001 */
|
||||
UCLASS()
|
||||
class PHY_API UPHY_MMC_HitChance : public UGameplayModMagnitudeCalculation
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UPHY_MMC_HitChance();
|
||||
virtual float CalculateBaseMagnitude_Implementation(const FGameplayEffectSpec& Spec) const override;
|
||||
|
||||
protected:
|
||||
FGameplayEffectAttributeCaptureDefinition AgilityDef;
|
||||
};
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
// Copyright 2025 PHY. All Rights Reserved.
|
||||
|
||||
#include "AbilitySystem/MMC/PHY_MMC_InnerPowerRegenRate.h"
|
||||
|
||||
#include "AbilitySystem/Attributes/PHYAttributeSet.h"
|
||||
|
||||
UPHY_MMC_InnerPowerRegenRate::UPHY_MMC_InnerPowerRegenRate()
|
||||
{
|
||||
InnerBreathDef = FGameplayEffectAttributeCaptureDefinition(
|
||||
UPHYAttributeSet::GetInnerBreathAttribute(),
|
||||
EGameplayEffectAttributeCaptureSource::Target,
|
||||
true);
|
||||
|
||||
RelevantAttributesToCapture.Add(InnerBreathDef);
|
||||
}
|
||||
|
||||
float UPHY_MMC_InnerPowerRegenRate::CalculateBaseMagnitude_Implementation(const FGameplayEffectSpec& Spec) const
|
||||
{
|
||||
FAggregatorEvaluateParameters Params;
|
||||
Params.SourceTags = Spec.CapturedSourceTags.GetAggregatedTags();
|
||||
Params.TargetTags = Spec.CapturedTargetTags.GetAggregatedTags();
|
||||
|
||||
float InnerBreath = 0.f;
|
||||
GetCapturedAttributeMagnitude(InnerBreathDef, Spec, Params, InnerBreath);
|
||||
InnerBreath = FMath::Max(0.f, InnerBreath);
|
||||
|
||||
constexpr float K = 0.15f;
|
||||
return InnerBreath * K;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
// Copyright 2025 PHY. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "GameplayModMagnitudeCalculation.h"
|
||||
#include "PHY_MMC_InnerPowerRegenRate.generated.h"
|
||||
|
||||
/** InnerPowerRegenRate = InnerBreath * 0.15 */
|
||||
UCLASS()
|
||||
class PHY_API UPHY_MMC_InnerPowerRegenRate : public UGameplayModMagnitudeCalculation
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UPHY_MMC_InnerPowerRegenRate();
|
||||
virtual float CalculateBaseMagnitude_Implementation(const FGameplayEffectSpec& Spec) const override;
|
||||
|
||||
protected:
|
||||
FGameplayEffectAttributeCaptureDefinition InnerBreathDef;
|
||||
};
|
||||
|
||||
31
Source/PHY/Private/AbilitySystem/MMC/PHY_MMC_MaxHealth.cpp
Normal file
31
Source/PHY/Private/AbilitySystem/MMC/PHY_MMC_MaxHealth.cpp
Normal file
@@ -0,0 +1,31 @@
|
||||
// Copyright 2025 PHY. All Rights Reserved.
|
||||
|
||||
#include "AbilitySystem/MMC/PHY_MMC_MaxHealth.h"
|
||||
|
||||
#include "AbilitySystem/Attributes/PHYAttributeSet.h"
|
||||
|
||||
UPHY_MMC_MaxHealth::UPHY_MMC_MaxHealth()
|
||||
{
|
||||
ConstitutionDef = FGameplayEffectAttributeCaptureDefinition(
|
||||
UPHYAttributeSet::GetConstitutionAttribute(),
|
||||
EGameplayEffectAttributeCaptureSource::Target,
|
||||
true /* snapshot */);
|
||||
|
||||
RelevantAttributesToCapture.Add(ConstitutionDef);
|
||||
}
|
||||
|
||||
float UPHY_MMC_MaxHealth::CalculateBaseMagnitude_Implementation(const FGameplayEffectSpec& Spec) const
|
||||
{
|
||||
FAggregatorEvaluateParameters EvalParams;
|
||||
EvalParams.SourceTags = Spec.CapturedSourceTags.GetAggregatedTags();
|
||||
EvalParams.TargetTags = Spec.CapturedTargetTags.GetAggregatedTags();
|
||||
|
||||
float Constitution = 0.f;
|
||||
GetCapturedAttributeMagnitude(ConstitutionDef, Spec, EvalParams, Constitution);
|
||||
Constitution = FMath::Max(0.f, Constitution);
|
||||
|
||||
constexpr float BaseMaxHealth = 100.f;
|
||||
constexpr float ConstitutionToHealth = 25.f;
|
||||
return BaseMaxHealth + Constitution * ConstitutionToHealth;
|
||||
}
|
||||
|
||||
22
Source/PHY/Private/AbilitySystem/MMC/PHY_MMC_MaxHealth.h
Normal file
22
Source/PHY/Private/AbilitySystem/MMC/PHY_MMC_MaxHealth.h
Normal file
@@ -0,0 +1,22 @@
|
||||
// Copyright 2025 PHY. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "GameplayModMagnitudeCalculation.h"
|
||||
#include "PHY_MMC_MaxHealth.generated.h"
|
||||
|
||||
/** MaxHealth = Base(100) + Constitution * 25 */
|
||||
UCLASS()
|
||||
class PHY_API UPHY_MMC_MaxHealth : public UGameplayModMagnitudeCalculation
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UPHY_MMC_MaxHealth();
|
||||
virtual float CalculateBaseMagnitude_Implementation(const FGameplayEffectSpec& Spec) const override;
|
||||
|
||||
protected:
|
||||
FGameplayEffectAttributeCaptureDefinition ConstitutionDef;
|
||||
};
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
// Copyright 2025 PHY. All Rights Reserved.
|
||||
|
||||
#include "AbilitySystem/MMC/PHY_MMC_MaxInnerPower.h"
|
||||
|
||||
#include "AbilitySystem/Attributes/PHYAttributeSet.h"
|
||||
|
||||
UPHY_MMC_MaxInnerPower::UPHY_MMC_MaxInnerPower()
|
||||
{
|
||||
InnerBreathDef = FGameplayEffectAttributeCaptureDefinition(
|
||||
UPHYAttributeSet::GetInnerBreathAttribute(),
|
||||
EGameplayEffectAttributeCaptureSource::Target,
|
||||
true);
|
||||
|
||||
RelevantAttributesToCapture.Add(InnerBreathDef);
|
||||
}
|
||||
|
||||
float UPHY_MMC_MaxInnerPower::CalculateBaseMagnitude_Implementation(const FGameplayEffectSpec& Spec) const
|
||||
{
|
||||
FAggregatorEvaluateParameters Params;
|
||||
Params.SourceTags = Spec.CapturedSourceTags.GetAggregatedTags();
|
||||
Params.TargetTags = Spec.CapturedTargetTags.GetAggregatedTags();
|
||||
|
||||
float InnerBreath = 0.f;
|
||||
GetCapturedAttributeMagnitude(InnerBreathDef, Spec, Params, InnerBreath);
|
||||
InnerBreath = FMath::Max(0.f, InnerBreath);
|
||||
|
||||
constexpr float Base = 100.f;
|
||||
constexpr float K = 20.f;
|
||||
return Base + InnerBreath * K;
|
||||
}
|
||||
|
||||
22
Source/PHY/Private/AbilitySystem/MMC/PHY_MMC_MaxInnerPower.h
Normal file
22
Source/PHY/Private/AbilitySystem/MMC/PHY_MMC_MaxInnerPower.h
Normal file
@@ -0,0 +1,22 @@
|
||||
// Copyright 2025 PHY. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "GameplayModMagnitudeCalculation.h"
|
||||
#include "PHY_MMC_MaxInnerPower.generated.h"
|
||||
|
||||
/** MaxInnerPower = Base(100) + InnerBreath * 20 */
|
||||
UCLASS()
|
||||
class PHY_API UPHY_MMC_MaxInnerPower : public UGameplayModMagnitudeCalculation
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UPHY_MMC_MaxInnerPower();
|
||||
virtual float CalculateBaseMagnitude_Implementation(const FGameplayEffectSpec& Spec) const override;
|
||||
|
||||
protected:
|
||||
FGameplayEffectAttributeCaptureDefinition InnerBreathDef;
|
||||
};
|
||||
|
||||
31
Source/PHY/Private/AbilitySystem/MMC/PHY_MMC_MoveSpeed.cpp
Normal file
31
Source/PHY/Private/AbilitySystem/MMC/PHY_MMC_MoveSpeed.cpp
Normal file
@@ -0,0 +1,31 @@
|
||||
// Copyright 2025 PHY. All Rights Reserved.
|
||||
|
||||
#include "AbilitySystem/MMC/PHY_MMC_MoveSpeed.h"
|
||||
|
||||
#include "AbilitySystem/Attributes/PHYAttributeSet.h"
|
||||
|
||||
UPHY_MMC_MoveSpeed::UPHY_MMC_MoveSpeed()
|
||||
{
|
||||
AgilityDef = FGameplayEffectAttributeCaptureDefinition(
|
||||
UPHYAttributeSet::GetAgilityAttribute(),
|
||||
EGameplayEffectAttributeCaptureSource::Target,
|
||||
true /* snapshot */);
|
||||
|
||||
RelevantAttributesToCapture.Add(AgilityDef);
|
||||
}
|
||||
|
||||
float UPHY_MMC_MoveSpeed::CalculateBaseMagnitude_Implementation(const FGameplayEffectSpec& Spec) const
|
||||
{
|
||||
FAggregatorEvaluateParameters EvalParams;
|
||||
EvalParams.SourceTags = Spec.CapturedSourceTags.GetAggregatedTags();
|
||||
EvalParams.TargetTags = Spec.CapturedTargetTags.GetAggregatedTags();
|
||||
|
||||
float Agility = 0.f;
|
||||
GetCapturedAttributeMagnitude(AgilityDef, Spec, EvalParams, Agility);
|
||||
Agility = FMath::Max(0.f, Agility);
|
||||
|
||||
constexpr float BaseMoveSpeed = 600.f;
|
||||
constexpr float AgilityToMoveSpeed = 2.f;
|
||||
return BaseMoveSpeed + Agility * AgilityToMoveSpeed;
|
||||
}
|
||||
|
||||
22
Source/PHY/Private/AbilitySystem/MMC/PHY_MMC_MoveSpeed.h
Normal file
22
Source/PHY/Private/AbilitySystem/MMC/PHY_MMC_MoveSpeed.h
Normal file
@@ -0,0 +1,22 @@
|
||||
// Copyright 2025 PHY. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "GameplayModMagnitudeCalculation.h"
|
||||
#include "PHY_MMC_MoveSpeed.generated.h"
|
||||
|
||||
/** MoveSpeed = Base(600) + Agility * 2 */
|
||||
UCLASS()
|
||||
class PHY_API UPHY_MMC_MoveSpeed : public UGameplayModMagnitudeCalculation
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UPHY_MMC_MoveSpeed();
|
||||
virtual float CalculateBaseMagnitude_Implementation(const FGameplayEffectSpec& Spec) const override;
|
||||
|
||||
protected:
|
||||
FGameplayEffectAttributeCaptureDefinition AgilityDef;
|
||||
};
|
||||
|
||||
31
Source/PHY/Private/AbilitySystem/MMC/PHY_MMC_ParryChance.cpp
Normal file
31
Source/PHY/Private/AbilitySystem/MMC/PHY_MMC_ParryChance.cpp
Normal file
@@ -0,0 +1,31 @@
|
||||
// Copyright 2025 PHY. All Rights Reserved.
|
||||
|
||||
#include "AbilitySystem/MMC/PHY_MMC_ParryChance.h"
|
||||
|
||||
#include "AbilitySystem/Attributes/PHYAttributeSet.h"
|
||||
|
||||
UPHY_MMC_ParryChance::UPHY_MMC_ParryChance()
|
||||
{
|
||||
StrengthDef = FGameplayEffectAttributeCaptureDefinition(
|
||||
UPHYAttributeSet::GetStrengthAttribute(),
|
||||
EGameplayEffectAttributeCaptureSource::Target,
|
||||
true);
|
||||
|
||||
RelevantAttributesToCapture.Add(StrengthDef);
|
||||
}
|
||||
|
||||
float UPHY_MMC_ParryChance::CalculateBaseMagnitude_Implementation(const FGameplayEffectSpec& Spec) const
|
||||
{
|
||||
FAggregatorEvaluateParameters Params;
|
||||
Params.SourceTags = Spec.CapturedSourceTags.GetAggregatedTags();
|
||||
Params.TargetTags = Spec.CapturedTargetTags.GetAggregatedTags();
|
||||
|
||||
float Strength = 0.f;
|
||||
GetCapturedAttributeMagnitude(StrengthDef, Spec, Params, Strength);
|
||||
Strength = FMath::Max(0.f, Strength);
|
||||
|
||||
constexpr float Base = 0.03f;
|
||||
constexpr float K = 0.001f;
|
||||
return FMath::Clamp(Base + Strength * K, 0.f, 1.f);
|
||||
}
|
||||
|
||||
22
Source/PHY/Private/AbilitySystem/MMC/PHY_MMC_ParryChance.h
Normal file
22
Source/PHY/Private/AbilitySystem/MMC/PHY_MMC_ParryChance.h
Normal file
@@ -0,0 +1,22 @@
|
||||
// Copyright 2025 PHY. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "GameplayModMagnitudeCalculation.h"
|
||||
#include "PHY_MMC_ParryChance.generated.h"
|
||||
|
||||
/** ParryChance = Base(0.03) + Strength * 0.001 */
|
||||
UCLASS()
|
||||
class PHY_API UPHY_MMC_ParryChance : public UGameplayModMagnitudeCalculation
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UPHY_MMC_ParryChance();
|
||||
virtual float CalculateBaseMagnitude_Implementation(const FGameplayEffectSpec& Spec) const override;
|
||||
|
||||
protected:
|
||||
FGameplayEffectAttributeCaptureDefinition StrengthDef;
|
||||
};
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
// Copyright 2025 PHY. All Rights Reserved.
|
||||
|
||||
#include "AbilitySystem/MMC/PHY_MMC_PhysicalAttack.h"
|
||||
|
||||
#include "AbilitySystem/Attributes/PHYAttributeSet.h"
|
||||
|
||||
UPHY_MMC_PhysicalAttack::UPHY_MMC_PhysicalAttack()
|
||||
{
|
||||
StrengthDef = FGameplayEffectAttributeCaptureDefinition(
|
||||
UPHYAttributeSet::GetStrengthAttribute(),
|
||||
EGameplayEffectAttributeCaptureSource::Target,
|
||||
true /* snapshot */);
|
||||
|
||||
RelevantAttributesToCapture.Add(StrengthDef);
|
||||
}
|
||||
|
||||
float UPHY_MMC_PhysicalAttack::CalculateBaseMagnitude_Implementation(const FGameplayEffectSpec& Spec) const
|
||||
{
|
||||
FAggregatorEvaluateParameters EvalParams;
|
||||
EvalParams.SourceTags = Spec.CapturedSourceTags.GetAggregatedTags();
|
||||
EvalParams.TargetTags = Spec.CapturedTargetTags.GetAggregatedTags();
|
||||
|
||||
float Strength = 0.f;
|
||||
GetCapturedAttributeMagnitude(StrengthDef, Spec, EvalParams, Strength);
|
||||
Strength = FMath::Max(0.f, Strength);
|
||||
|
||||
constexpr float BaseAttack = 10.f;
|
||||
constexpr float StrengthToAttack = 2.f;
|
||||
return BaseAttack + Strength * StrengthToAttack;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
// Copyright 2025 PHY. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "GameplayModMagnitudeCalculation.h"
|
||||
#include "PHY_MMC_PhysicalAttack.generated.h"
|
||||
|
||||
/** PhysicalAttack = Base(10) + Strength * 2 */
|
||||
UCLASS()
|
||||
class PHY_API UPHY_MMC_PhysicalAttack : public UGameplayModMagnitudeCalculation
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UPHY_MMC_PhysicalAttack();
|
||||
virtual float CalculateBaseMagnitude_Implementation(const FGameplayEffectSpec& Spec) const override;
|
||||
|
||||
protected:
|
||||
FGameplayEffectAttributeCaptureDefinition StrengthDef;
|
||||
};
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
// Copyright 2025 PHY. All Rights Reserved.
|
||||
|
||||
#include "AbilitySystem/MMC/PHY_MMC_PhysicalDefense.h"
|
||||
|
||||
#include "AbilitySystem/Attributes/PHYAttributeSet.h"
|
||||
|
||||
UPHY_MMC_PhysicalDefense::UPHY_MMC_PhysicalDefense()
|
||||
{
|
||||
ConstitutionDef = FGameplayEffectAttributeCaptureDefinition(
|
||||
UPHYAttributeSet::GetConstitutionAttribute(),
|
||||
EGameplayEffectAttributeCaptureSource::Target,
|
||||
true /* snapshot */);
|
||||
|
||||
RelevantAttributesToCapture.Add(ConstitutionDef);
|
||||
}
|
||||
|
||||
float UPHY_MMC_PhysicalDefense::CalculateBaseMagnitude_Implementation(const FGameplayEffectSpec& Spec) const
|
||||
{
|
||||
FAggregatorEvaluateParameters EvalParams;
|
||||
EvalParams.SourceTags = Spec.CapturedSourceTags.GetAggregatedTags();
|
||||
EvalParams.TargetTags = Spec.CapturedTargetTags.GetAggregatedTags();
|
||||
|
||||
float Constitution = 0.f;
|
||||
GetCapturedAttributeMagnitude(ConstitutionDef, Spec, EvalParams, Constitution);
|
||||
Constitution = FMath::Max(0.f, Constitution);
|
||||
|
||||
constexpr float BaseDefense = 5.f;
|
||||
constexpr float ConstitutionToDefense = 1.5f;
|
||||
return BaseDefense + Constitution * ConstitutionToDefense;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
// Copyright 2025 PHY. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "GameplayModMagnitudeCalculation.h"
|
||||
#include "PHY_MMC_PhysicalDefense.generated.h"
|
||||
|
||||
/** PhysicalDefense = Base(5) + Constitution * 1.5 */
|
||||
UCLASS()
|
||||
class PHY_API UPHY_MMC_PhysicalDefense : public UGameplayModMagnitudeCalculation
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UPHY_MMC_PhysicalDefense();
|
||||
virtual float CalculateBaseMagnitude_Implementation(const FGameplayEffectSpec& Spec) const override;
|
||||
|
||||
protected:
|
||||
FGameplayEffectAttributeCaptureDefinition ConstitutionDef;
|
||||
};
|
||||
|
||||
31
Source/PHY/Private/AbilitySystem/MMC/PHY_MMC_Tenacity.cpp
Normal file
31
Source/PHY/Private/AbilitySystem/MMC/PHY_MMC_Tenacity.cpp
Normal file
@@ -0,0 +1,31 @@
|
||||
// Copyright 2025 PHY. All Rights Reserved.
|
||||
|
||||
#include "AbilitySystem/MMC/PHY_MMC_Tenacity.h"
|
||||
|
||||
#include "AbilitySystem/Attributes/PHYAttributeSet.h"
|
||||
|
||||
UPHY_MMC_Tenacity::UPHY_MMC_Tenacity()
|
||||
{
|
||||
InnerBreathDef = FGameplayEffectAttributeCaptureDefinition(
|
||||
UPHYAttributeSet::GetInnerBreathAttribute(),
|
||||
EGameplayEffectAttributeCaptureSource::Target,
|
||||
true);
|
||||
|
||||
RelevantAttributesToCapture.Add(InnerBreathDef);
|
||||
}
|
||||
|
||||
float UPHY_MMC_Tenacity::CalculateBaseMagnitude_Implementation(const FGameplayEffectSpec& Spec) const
|
||||
{
|
||||
FAggregatorEvaluateParameters Params;
|
||||
Params.SourceTags = Spec.CapturedSourceTags.GetAggregatedTags();
|
||||
Params.TargetTags = Spec.CapturedTargetTags.GetAggregatedTags();
|
||||
|
||||
float InnerBreath = 0.f;
|
||||
GetCapturedAttributeMagnitude(InnerBreathDef, Spec, Params, InnerBreath);
|
||||
InnerBreath = FMath::Max(0.f, InnerBreath);
|
||||
|
||||
constexpr float Base = 0.f;
|
||||
constexpr float K = 1.f;
|
||||
return Base + InnerBreath * K;
|
||||
}
|
||||
|
||||
22
Source/PHY/Private/AbilitySystem/MMC/PHY_MMC_Tenacity.h
Normal file
22
Source/PHY/Private/AbilitySystem/MMC/PHY_MMC_Tenacity.h
Normal file
@@ -0,0 +1,22 @@
|
||||
// Copyright 2025 PHY. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "GameplayModMagnitudeCalculation.h"
|
||||
#include "PHY_MMC_Tenacity.generated.h"
|
||||
|
||||
/** Tenacity = Base(0) + InnerBreath * 1 */
|
||||
UCLASS()
|
||||
class PHY_API UPHY_MMC_Tenacity : public UGameplayModMagnitudeCalculation
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UPHY_MMC_Tenacity();
|
||||
virtual float CalculateBaseMagnitude_Implementation(const FGameplayEffectSpec& Spec) const override;
|
||||
|
||||
protected:
|
||||
FGameplayEffectAttributeCaptureDefinition InnerBreathDef;
|
||||
};
|
||||
|
||||
17
Source/PHY/Private/AbilitySystem/PHYCharacterClass.h
Normal file
17
Source/PHY/Private/AbilitySystem/PHYCharacterClass.h
Normal file
@@ -0,0 +1,17 @@
|
||||
// Copyright 2025 PHY. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "PHYCharacterClass.generated.h"
|
||||
|
||||
/** 可根据项目需要扩展更多职业/门派/流派 */
|
||||
UENUM(BlueprintType)
|
||||
enum class EPHYCharacterClass : uint8
|
||||
{
|
||||
None UMETA(DisplayName = "无"),
|
||||
Warrior UMETA(DisplayName = "武者"),
|
||||
Tank UMETA(DisplayName = "铁卫"),
|
||||
Healer UMETA(DisplayName = "医者"),
|
||||
Assassin UMETA(DisplayName = "刺客"),
|
||||
};
|
||||
|
||||
67
Source/PHY/Private/AbilitySystem/PHYClassDefaults.h
Normal file
67
Source/PHY/Private/AbilitySystem/PHYClassDefaults.h
Normal file
@@ -0,0 +1,67 @@
|
||||
// Copyright 2025 PHY. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Engine/DataAsset.h"
|
||||
#include "AbilitySystem/PHYCharacterClass.h"
|
||||
#include "PHYClassDefaults.generated.h"
|
||||
|
||||
USTRUCT(BlueprintType)
|
||||
struct FPHYPrimaryAttributes
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly)
|
||||
float Strength = 10.f;
|
||||
|
||||
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly)
|
||||
float Constitution = 10.f;
|
||||
|
||||
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly)
|
||||
float InnerBreath = 10.f;
|
||||
|
||||
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly)
|
||||
float Agility = 10.f;
|
||||
};
|
||||
|
||||
USTRUCT(BlueprintType)
|
||||
struct FPHYClassDefaultsRow
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly)
|
||||
EPHYCharacterClass Class = EPHYCharacterClass::None;
|
||||
|
||||
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly)
|
||||
FPHYPrimaryAttributes Primary;
|
||||
};
|
||||
|
||||
/** DataAsset holding per-class initial attributes. */
|
||||
UCLASS(BlueprintType)
|
||||
class PHY_API UPHYClassDefaults : public UDataAsset
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
/** Defaults used when Class not found */
|
||||
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly)
|
||||
FPHYPrimaryAttributes FallbackPrimary;
|
||||
|
||||
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly)
|
||||
TArray<FPHYClassDefaultsRow> Classes;
|
||||
|
||||
UFUNCTION(BlueprintCallable)
|
||||
FPHYPrimaryAttributes GetPrimaryForClass(EPHYCharacterClass InClass) const
|
||||
{
|
||||
for (const FPHYClassDefaultsRow& Row : Classes)
|
||||
{
|
||||
if (Row.Class == InClass)
|
||||
{
|
||||
return Row.Primary;
|
||||
}
|
||||
}
|
||||
return FallbackPrimary;
|
||||
}
|
||||
};
|
||||
|
||||
50
Source/PHY/Private/Anim/RetargeterAnim.cpp
Normal file
50
Source/PHY/Private/Anim/RetargeterAnim.cpp
Normal file
@@ -0,0 +1,50 @@
|
||||
//
|
||||
|
||||
|
||||
#include "RetargeterAnim.h"
|
||||
|
||||
#include "Components/RetargeterComponent.h"
|
||||
#include "Retargeter/IKRetargeter.h"
|
||||
|
||||
void URetargeterAnim::NativeInitializeAnimation()
|
||||
{
|
||||
Super::NativeInitializeAnimation();
|
||||
|
||||
// 尝试从拥有者获取RetargeterComponent
|
||||
if (const AActor* Owner = GetOwningActor())
|
||||
{
|
||||
RetargeterComponent = Owner->FindComponentByClass<URetargeterComponent>();
|
||||
|
||||
if (RetargeterComponent)
|
||||
{
|
||||
// 绑定到retarget改变事件
|
||||
RetargeterComponent->OnRetargetChanged.AddDynamic(this,&URetargeterAnim::OnRetargetChanged);
|
||||
|
||||
// 获取当前的retarget信息
|
||||
CurrentRetargetInfo = RetargeterComponent->GetRetargetInfo_Implementation();
|
||||
OnRetargetChanged(CurrentRetargetInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void URetargeterAnim::BeginDestroy()
|
||||
{
|
||||
// 解绑代理以防止内存泄漏
|
||||
if (RetargeterComponent)
|
||||
{
|
||||
RetargeterComponent->OnRetargetChanged.RemoveDynamic(this, &URetargeterAnim::OnRetargetChanged);
|
||||
RetargeterComponent = nullptr;
|
||||
}
|
||||
|
||||
Super::BeginDestroy();
|
||||
}
|
||||
|
||||
void URetargeterAnim::OnRetargetChanged(const FRetargetInfo& NewRetargetInfo)
|
||||
{
|
||||
CurrentRetargetInfo = NewRetargetInfo;
|
||||
if (!CurrentRetargetInfo.Retargeter.IsNull())
|
||||
{
|
||||
IkRetargeter = CurrentRetargetInfo.Retargeter.LoadSynchronous();
|
||||
}
|
||||
}
|
||||
|
||||
43
Source/PHY/Private/Anim/RetargeterAnim.h
Normal file
43
Source/PHY/Private/Anim/RetargeterAnim.h
Normal file
@@ -0,0 +1,43 @@
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Animation/AnimInstance.h"
|
||||
#include "Gameplay/PHYGameInstance.h"
|
||||
#include "RetargeterAnim.generated.h"
|
||||
|
||||
struct FRetargetProfile;
|
||||
struct FRetargetInfo;
|
||||
|
||||
class URetargeterComponent;
|
||||
|
||||
/**
|
||||
* Retargeter动画实例,用于处理角色网格的IK retarget逻辑
|
||||
*/
|
||||
UCLASS()
|
||||
class PHY_API URetargeterAnim : public UAnimInstance
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
protected:
|
||||
virtual void NativeInitializeAnimation() override;
|
||||
virtual void BeginDestroy() override;
|
||||
|
||||
private:
|
||||
/** 当前的retarget信息 */
|
||||
FRetargetInfo CurrentRetargetInfo;
|
||||
|
||||
/** IK Retargeter实例 */
|
||||
UPROPERTY(BlueprintReadOnly, Category = "PHY|Retargeter", meta = (AllowPrivateAccess = "true"))
|
||||
UIKRetargeter* IkRetargeter;
|
||||
|
||||
/** Retargeter组件的引用 */
|
||||
UPROPERTY()
|
||||
TObjectPtr<URetargeterComponent> RetargeterComponent;
|
||||
|
||||
/** 当retarget信息改变时调用 */
|
||||
UFUNCTION()
|
||||
void OnRetargetChanged(const FRetargetInfo& NewRetargetInfo);
|
||||
|
||||
};
|
||||
32
Source/PHY/Private/Character/AICharacter.cpp
Normal file
32
Source/PHY/Private/Character/AICharacter.cpp
Normal file
@@ -0,0 +1,32 @@
|
||||
//
|
||||
|
||||
|
||||
#include "AICharacter.h"
|
||||
|
||||
|
||||
// Sets default values
|
||||
AAICharacter::AAICharacter()
|
||||
{
|
||||
// Set this character to call Tick() every frame. You can turn this off to improve performance if you don't need it.
|
||||
PrimaryActorTick.bCanEverTick = true;
|
||||
}
|
||||
|
||||
// Called when the game starts or when spawned
|
||||
void AAICharacter::BeginPlay()
|
||||
{
|
||||
Super::BeginPlay();
|
||||
|
||||
}
|
||||
|
||||
// Called every frame
|
||||
void AAICharacter::Tick(float DeltaTime)
|
||||
{
|
||||
Super::Tick(DeltaTime);
|
||||
}
|
||||
|
||||
// Called to bind functionality to input
|
||||
void AAICharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
|
||||
{
|
||||
Super::SetupPlayerInputComponent(PlayerInputComponent);
|
||||
}
|
||||
|
||||
28
Source/PHY/Private/Character/AICharacter.h
Normal file
28
Source/PHY/Private/Character/AICharacter.h
Normal file
@@ -0,0 +1,28 @@
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "PHYCharacter.h"
|
||||
#include "AICharacter.generated.h"
|
||||
|
||||
UCLASS()
|
||||
class PHY_API AAICharacter : public APHYCharacter
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
// Sets default values for this character's properties
|
||||
AAICharacter();
|
||||
|
||||
protected:
|
||||
// Called when the game starts or when spawned
|
||||
virtual void BeginPlay() override;
|
||||
|
||||
public:
|
||||
// Called every frame
|
||||
virtual void Tick(float DeltaTime) override;
|
||||
|
||||
// Called to bind functionality to input
|
||||
virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override;
|
||||
};
|
||||
32
Source/PHY/Private/Character/PHYCharacter.cpp
Normal file
32
Source/PHY/Private/Character/PHYCharacter.cpp
Normal file
@@ -0,0 +1,32 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
|
||||
#include "Character/PHYCharacter.h"
|
||||
|
||||
#include "GIS_InventorySystemComponent.h"
|
||||
#include "GMS_CharacterMovementSystemComponent.h"
|
||||
#include "Components/RetargeterComponent.h"
|
||||
|
||||
// Sets default values
|
||||
APHYCharacter::APHYCharacter()
|
||||
{
|
||||
// Set this character to call Tick() every frame. You can turn this off to improve performance if you don't need it.
|
||||
PrimaryActorTick.bCanEverTick = true;
|
||||
|
||||
InventorySystemComponent = CreateDefaultSubobject<UGIS_InventorySystemComponent>(TEXT("InventorySystem"));
|
||||
MovementSystemComponent = CreateDefaultSubobject<UGMS_CharacterMovementSystemComponent>(TEXT("MovementSystem"));
|
||||
RetargeterComponent = CreateDefaultSubobject<URetargeterComponent>(TEXT("Retargeter"));
|
||||
}
|
||||
|
||||
// Called when the game starts or when spawned
|
||||
void APHYCharacter::BeginPlay()
|
||||
{
|
||||
Super::BeginPlay();
|
||||
|
||||
// 库存初始化应由服务器执行(组件接口也标记了 BlueprintAuthorityOnly)。
|
||||
if (HasAuthority() && InventorySystemComponent)
|
||||
{
|
||||
InventorySystemComponent->InitializeInventorySystem();
|
||||
}
|
||||
}
|
||||
|
||||
55
Source/PHY/Private/Character/PHYCharacter.h
Normal file
55
Source/PHY/Private/Character/PHYCharacter.h
Normal file
@@ -0,0 +1,55 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "GameFramework/Character.h"
|
||||
#include "PHYCharacter.generated.h"
|
||||
|
||||
class URetargeterComponent;
|
||||
|
||||
class UGIS_InventorySystemComponent;
|
||||
class UGMS_CharacterMovementSystemComponent;
|
||||
|
||||
|
||||
UCLASS()
|
||||
class APHYCharacter : public ACharacter
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
// Sets default values for this character's properties
|
||||
APHYCharacter();
|
||||
|
||||
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "PHY|Inventory")
|
||||
UGIS_InventorySystemComponent* GetInventorySystemComponent() const { return InventorySystemComponent; }
|
||||
|
||||
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "PHY|Movement")
|
||||
UGMS_CharacterMovementSystemComponent* GetMovementSystemComponent() const { return MovementSystemComponent; }
|
||||
|
||||
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "PHY|Retargeter")
|
||||
URetargeterComponent* GetRetargeterComponent() const { return RetargeterComponent; }
|
||||
|
||||
protected:
|
||||
// Called when the game starts or when spawned
|
||||
virtual void BeginPlay() override;
|
||||
|
||||
/**
|
||||
* 角色的库存系统组件(来自 GenericInventorySystem 插件 GIS)。
|
||||
* 由服务器初始化,客户端通过复制拿到数据。
|
||||
*/
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "PHY|Inventory", meta = (AllowPrivateAccess = "true"))
|
||||
TObjectPtr<UGIS_InventorySystemComponent> InventorySystemComponent;
|
||||
|
||||
/**
|
||||
* 角色的移动系统组件(来自 GenericMovementSystem 插件 GMS)。
|
||||
*/
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "PHY|Movement", meta = (AllowPrivateAccess = "true"))
|
||||
TObjectPtr<UGMS_CharacterMovementSystemComponent> MovementSystemComponent;
|
||||
|
||||
/**
|
||||
* 角色的Retargeter组件,用于处理角色网格的retarget逻辑。
|
||||
*/
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "PHY|Retargeter", meta = (AllowPrivateAccess = "true"))
|
||||
TObjectPtr<URetargeterComponent> RetargeterComponent;
|
||||
};
|
||||
230
Source/PHY/Private/Character/PHYPlayerCharacter.cpp
Normal file
230
Source/PHY/Private/Character/PHYPlayerCharacter.cpp
Normal file
@@ -0,0 +1,230 @@
|
||||
//
|
||||
|
||||
|
||||
#include "PHYPlayerCharacter.h"
|
||||
|
||||
#include "GIPS_InputSystemComponent.h"
|
||||
#include "InputActionValue.h"
|
||||
#include "Camera/CameraComponent.h"
|
||||
#include "GameFramework/SpringArmComponent.h"
|
||||
#include "Gameplay/Player/PHYPlayerState.h"
|
||||
#include "Gameplay/PHYGameInstance.h"
|
||||
#include "GameplayTags/InputTags.h"
|
||||
#include "Components/RetargeterComponent.h"
|
||||
#include "AbilitySystemComponent.h"
|
||||
#include "AbilitySystem/Attributes/PHYAttributeSet.h"
|
||||
#include "AbilitySystem/Effects/PHYGE_DerivedAttributes.h"
|
||||
#include "AbilitySystem/Effects/PHYGE_InitPrimary.h"
|
||||
#include "AbilitySystem/Effects/PHYGE_RegenTick.h"
|
||||
#include "AbilitySystem/PHYClassDefaults.h"
|
||||
#include "GameplayTags/InitAttributeTags.h"
|
||||
#include "GameplayTags/RegenTags.h"
|
||||
#include "GameFramework/CharacterMovementComponent.h"
|
||||
#include "UI/HUD/PHYGameHUD.h"
|
||||
|
||||
|
||||
APHYPlayerCharacter::APHYPlayerCharacter()
|
||||
{
|
||||
PrimaryActorTick.bCanEverTick = true;
|
||||
CameraBoom = CreateDefaultSubobject<USpringArmComponent>(TEXT("CameraBoom"));
|
||||
CameraBoom->SetupAttachment(RootComponent);
|
||||
CameraBoom->TargetArmLength = 300.f;
|
||||
CameraBoom->bUsePawnControlRotation = true;
|
||||
Camera = CreateDefaultSubobject<UCameraComponent>(TEXT("Camera"));
|
||||
Camera->SetupAttachment(CameraBoom, USpringArmComponent::SocketName);
|
||||
Camera->bUsePawnControlRotation = false;
|
||||
InputSystemComponent = CreateDefaultSubobject<UGIPS_InputSystemComponent>(TEXT("InputSystemComponent"));
|
||||
RetargetMeshComponent = CreateDefaultSubobject<USkeletalMeshComponent>(TEXT("RetargetMeshComponent"));
|
||||
RetargetMeshComponent->SetupAttachment(GetMesh());
|
||||
}
|
||||
|
||||
FRotator APHYPlayerCharacter::GetRotationInput_Implementation() const
|
||||
{
|
||||
if (InputSystemComponent)
|
||||
{
|
||||
const FInputActionValue LookInputValue = InputSystemComponent->GetInputActionValueOfInputTag(::InputTags::Tag__Input_Look);
|
||||
const FVector2D LookInputVector = LookInputValue.Get<FVector2D>();
|
||||
return FRotator(LookInputVector.Y, LookInputVector.X, 0.f);
|
||||
}
|
||||
return FRotator::ZeroRotator;
|
||||
}
|
||||
|
||||
FVector APHYPlayerCharacter::GetMovementInput_Implementation() const
|
||||
{
|
||||
if (InputSystemComponent)
|
||||
{
|
||||
const FInputActionValue MoveInputValue = InputSystemComponent->GetInputActionValueOfInputTag(::InputTags::Tag__Input_Move);
|
||||
const FVector2D MoveInputVector = MoveInputValue.Get<FVector2D>();
|
||||
return FVector(MoveInputVector.X, MoveInputVector.Y, 0.f);
|
||||
}
|
||||
return FVector::ZeroVector;
|
||||
}
|
||||
|
||||
|
||||
void APHYPlayerCharacter::PostInitializeComponents()
|
||||
{
|
||||
Super::PostInitializeComponents();
|
||||
|
||||
// 将自定义的RetargetMeshComponent设置到RetargeterComponent
|
||||
if (RetargeterComponent && RetargetMeshComponent)
|
||||
{
|
||||
RetargeterComponent->CustomRetargetMeshComponent = RetargetMeshComponent;
|
||||
}
|
||||
}
|
||||
|
||||
void APHYPlayerCharacter::PossessedBy(AController* NewController)
|
||||
{
|
||||
Super::PossessedBy(NewController);
|
||||
|
||||
InitializeGAS();
|
||||
// 初始化hud
|
||||
InitializeHUD();
|
||||
if (APHYPlayerState* PS = GetPlayerState<APHYPlayerState>())
|
||||
{
|
||||
if (RetargeterComponent && RetargeterComponent->GetDefaultRetargeterTag().IsValid() && !PS->GetReTargeterTag().IsValid())
|
||||
{
|
||||
PS->ServerSetReTargeterTag(RetargeterComponent->GetDefaultRetargeterTag());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void APHYPlayerCharacter::InitializeHUD()
|
||||
{
|
||||
if (const APlayerController* PC = Cast<APlayerController>(GetController()))
|
||||
{
|
||||
if (APHYGameHUD* GameHUD = Cast<APHYGameHUD>(PC->GetHUD()))
|
||||
{
|
||||
GameHUD->InitializeHUD();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void APHYPlayerCharacter::OnRep_PlayerState()
|
||||
{
|
||||
Super::OnRep_PlayerState();
|
||||
InitializeGAS();
|
||||
// 初始化hud
|
||||
InitializeHUD();
|
||||
}
|
||||
|
||||
void APHYPlayerCharacter::InitializeGAS()
|
||||
{
|
||||
APHYPlayerState* PS = GetPlayerState<APHYPlayerState>();
|
||||
if (!PS) return;
|
||||
|
||||
UAbilitySystemComponent* ASC = PS->GetAbilitySystemComponent();
|
||||
if (!ASC) return;
|
||||
|
||||
ASC->InitAbilityActorInfo(PS, this);
|
||||
|
||||
// Server applies init effects.
|
||||
if (HasAuthority())
|
||||
{
|
||||
// Init primary values
|
||||
{
|
||||
FGameplayEffectContextHandle Ctx = ASC->MakeEffectContext();
|
||||
Ctx.AddSourceObject(this);
|
||||
FGameplayEffectSpecHandle Spec = ASC->MakeOutgoingSpec(UPHYGE_InitPrimary::StaticClass(), 1.f, Ctx);
|
||||
if (Spec.IsValid())
|
||||
{
|
||||
// Per-class init values (fallback to defaults if no asset is set)
|
||||
FPHYPrimaryAttributes Primary;
|
||||
if (const UPHYGameInstance* GI = GetGameInstance<UPHYGameInstance>())
|
||||
{
|
||||
if (const UPHYClassDefaults* Defaults = GI->GetClassDefaults())
|
||||
{
|
||||
Primary = Defaults->GetPrimaryForClass(CharacterClass);
|
||||
}
|
||||
else
|
||||
{
|
||||
Primary = FPHYPrimaryAttributes{};
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Primary = FPHYPrimaryAttributes{};
|
||||
}
|
||||
|
||||
// Use SetByCaller so the same GE can be reused for every class.
|
||||
Spec.Data->SetSetByCallerMagnitude(InitAttributeTags::Tag__Data_Init_Primary_Strength, Primary.Strength);
|
||||
Spec.Data->SetSetByCallerMagnitude(InitAttributeTags::Tag__Data_Init_Primary_Constitution, Primary.Constitution);
|
||||
Spec.Data->SetSetByCallerMagnitude(InitAttributeTags::Tag__Data_Init_Primary_InnerBreath, Primary.InnerBreath);
|
||||
Spec.Data->SetSetByCallerMagnitude(InitAttributeTags::Tag__Data_Init_Primary_Agility, Primary.Agility);
|
||||
|
||||
ASC->ApplyGameplayEffectSpecToSelf(*Spec.Data.Get());
|
||||
}
|
||||
}
|
||||
|
||||
// Apply derived attributes (MMC driven)
|
||||
{
|
||||
FGameplayEffectContextHandle Ctx = ASC->MakeEffectContext();
|
||||
Ctx.AddSourceObject(this);
|
||||
FGameplayEffectSpecHandle Spec = ASC->MakeOutgoingSpec(UPHYGE_DerivedAttributes::StaticClass(), 1.f, Ctx);
|
||||
if (Spec.IsValid())
|
||||
{
|
||||
ASC->ApplyGameplayEffectSpecToSelf(*Spec.Data.Get());
|
||||
}
|
||||
}
|
||||
|
||||
// Set current Health to MaxHealth once derived is applied.
|
||||
if (const UPHYAttributeSet* AS = PS->GetAttributeSet())
|
||||
{
|
||||
ASC->SetNumericAttributeBase(UPHYAttributeSet::GetHealthAttribute(), AS->GetMaxHealth());
|
||||
ASC->SetNumericAttributeBase(UPHYAttributeSet::GetInnerPowerAttribute(), AS->GetMaxInnerPower());
|
||||
}
|
||||
|
||||
StopRegen();
|
||||
if (RegenInterval > 0.f)
|
||||
{
|
||||
GetWorldTimerManager().SetTimer(RegenTimerHandle, this, &APHYPlayerCharacter::RegenTick, RegenInterval, true);
|
||||
}
|
||||
}
|
||||
|
||||
// Push MoveSpeed to CharacterMovement (both sides, will converge via replication)
|
||||
if (UCharacterMovementComponent* MoveComp = GetCharacterMovement())
|
||||
{
|
||||
if (const UPHYAttributeSet* AS = PS->GetAttributeSet())
|
||||
{
|
||||
const float DesiredSpeed = AS->GetMoveSpeed();
|
||||
if (DesiredSpeed > 0.f)
|
||||
{
|
||||
MoveComp->MaxWalkSpeed = DesiredSpeed;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void APHYPlayerCharacter::StopRegen()
|
||||
{
|
||||
if (!HasAuthority()) return;
|
||||
GetWorldTimerManager().ClearTimer(RegenTimerHandle);
|
||||
}
|
||||
|
||||
void APHYPlayerCharacter::RegenTick()
|
||||
{
|
||||
if (!HasAuthority()) return;
|
||||
|
||||
APHYPlayerState* PS = GetPlayerState<APHYPlayerState>();
|
||||
if (!PS) return;
|
||||
|
||||
UAbilitySystemComponent* ASC = PS->GetAbilitySystemComponent();
|
||||
if (!ASC) return;
|
||||
|
||||
const UPHYAttributeSet* AS = PS->GetAttributeSet();
|
||||
if (!AS) return;
|
||||
|
||||
// Calculate per-tick amounts (rate is per-second)
|
||||
const float HealthDelta = AS->GetHealthRegenRate() * RegenInterval;
|
||||
const float InnerPowerDelta = AS->GetInnerPowerRegenRate() * RegenInterval;
|
||||
|
||||
if (HealthDelta <= 0.f && InnerPowerDelta <= 0.f) return;
|
||||
|
||||
FGameplayEffectContextHandle Ctx = ASC->MakeEffectContext();
|
||||
Ctx.AddSourceObject(this);
|
||||
FGameplayEffectSpecHandle Spec = ASC->MakeOutgoingSpec(UPHYGE_RegenTick::StaticClass(), 1.f, Ctx);
|
||||
if (!Spec.IsValid()) return;
|
||||
|
||||
Spec.Data->SetSetByCallerMagnitude(RegenTags::Tag__Data_Regen_Health, FMath::Max(0.f, HealthDelta));
|
||||
Spec.Data->SetSetByCallerMagnitude(RegenTags::Tag__Data_Regen_InnerPower, FMath::Max(0.f, InnerPowerDelta));
|
||||
ASC->ApplyGameplayEffectSpecToSelf(*Spec.Data.Get());
|
||||
}
|
||||
73
Source/PHY/Private/Character/PHYPlayerCharacter.h
Normal file
73
Source/PHY/Private/Character/PHYPlayerCharacter.h
Normal file
@@ -0,0 +1,73 @@
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "PHYCharacter.h"
|
||||
#include "AbilitySystem/PHYCharacterClass.h"
|
||||
#include "Pawn/UGC_PawnInterface.h"
|
||||
#include "PHYPlayerCharacter.generated.h"
|
||||
|
||||
class UGIPS_InputSystemComponent;
|
||||
class USpringArmComponent;
|
||||
class UCameraComponent;
|
||||
class URetargeterComponent;
|
||||
|
||||
UCLASS()
|
||||
class PHY_API APHYPlayerCharacter : public APHYCharacter,
|
||||
public IUGC_PawnInterface
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
|
||||
APHYPlayerCharacter();
|
||||
|
||||
protected:
|
||||
/** 职业/门派/流派:决定初始四维 */
|
||||
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category="PHY|Class")
|
||||
EPHYCharacterClass CharacterClass = EPHYCharacterClass::Warrior;
|
||||
|
||||
/**
|
||||
* 角色的相机组件
|
||||
*/
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "PHY|Camera")
|
||||
TObjectPtr<UCameraComponent> Camera;
|
||||
/**
|
||||
* 角色的相机臂组件
|
||||
*/
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "PHY|Camera")
|
||||
TObjectPtr<USpringArmComponent> CameraBoom;
|
||||
/**
|
||||
* 角色的输入组件
|
||||
*/
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "PHY|Input")
|
||||
TObjectPtr<UGIPS_InputSystemComponent> InputSystemComponent;
|
||||
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "PHY|Retarget")
|
||||
TObjectPtr<USkeletalMeshComponent> RetargetMeshComponent;
|
||||
|
||||
//~ Begin IUGC_PawnInterface
|
||||
virtual FRotator GetRotationInput_Implementation() const override;
|
||||
virtual FVector GetMovementInput_Implementation() const override;
|
||||
//~ End IUGC_PawnInterface
|
||||
|
||||
protected:
|
||||
virtual void PossessedBy(AController* NewController) override;
|
||||
void InitializeHUD();
|
||||
virtual void OnRep_PlayerState() override;
|
||||
|
||||
/** Server: apply init effects. Both sides: init actor info. */
|
||||
void InitializeGAS();
|
||||
|
||||
protected:
|
||||
virtual void PostInitializeComponents() override;
|
||||
|
||||
/** Regen tick interval (seconds). Server-only. */
|
||||
UPROPERTY(EditDefaultsOnly, Category="PHY|Regen")
|
||||
float RegenInterval = 1.0f;
|
||||
|
||||
FTimerHandle RegenTimerHandle;
|
||||
void RegenTick();
|
||||
void StopRegen();
|
||||
};
|
||||
128
Source/PHY/Private/Components/RetargeterComponent.cpp
Normal file
128
Source/PHY/Private/Components/RetargeterComponent.cpp
Normal file
@@ -0,0 +1,128 @@
|
||||
//
|
||||
|
||||
#include "RetargeterComponent.h"
|
||||
|
||||
#include "Gameplay/PHYGameInstance.h"
|
||||
#include "Components/SkeletalMeshComponent.h"
|
||||
#include "GameFramework/Character.h"
|
||||
|
||||
|
||||
|
||||
URetargeterComponent::URetargeterComponent()
|
||||
{
|
||||
PrimaryComponentTick.bCanEverTick = false;
|
||||
}
|
||||
|
||||
void URetargeterComponent::BeginPlay()
|
||||
{
|
||||
Super::BeginPlay();
|
||||
|
||||
// 延迟一帧初始化,确保所有组件都已完全初始化
|
||||
if (bAutoInitializeRetargeter)
|
||||
{
|
||||
GetWorld()->GetTimerManager().SetTimerForNextTick([this]()
|
||||
{
|
||||
if (IsValid(this))
|
||||
{
|
||||
InitializeRetargeter();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void URetargeterComponent::InitializeRetargeter()
|
||||
{
|
||||
// 先刷新网格设置
|
||||
RefreshMeshSettings();
|
||||
|
||||
// 然后设置retargeter
|
||||
SetupRetargeter();
|
||||
}
|
||||
|
||||
void URetargeterComponent::SetupRetargeter()
|
||||
{
|
||||
// 获取retarget mesh
|
||||
USkeletalMeshComponent* RetargetMeshComponent = GetRetargetMeshComponent_Implementation();
|
||||
|
||||
// 获取动画实例
|
||||
TSubclassOf<UAnimInstance> RetargeterAnim = GetRetargeterAnimInstance_Implementation();
|
||||
|
||||
// 设置retarget mesh的skeletal mesh和动画实例
|
||||
if (RetargetMeshComponent && RetargeterAnim)
|
||||
{
|
||||
RetargetMeshComponent->SetAnimInstanceClass(RetargeterAnim);
|
||||
RetargetMeshComponent->SetCollisionProfileName(TEXT("CharacterMesh"));
|
||||
RetargetMeshComponent->SetCollisionResponseToChannel(ECC_GameTraceChannel1, ECollisionResponse::ECR_Block);
|
||||
}
|
||||
}
|
||||
|
||||
void URetargeterComponent::RefreshMeshSettings()
|
||||
{
|
||||
UE_LOG(LogTemp, Log, TEXT("URetargeterComponent::RefreshMeshSettings()"));
|
||||
|
||||
// 获取retarget mesh组件
|
||||
USkeletalMeshComponent* RetargetMeshComponent = GetRetargetMeshComponent_Implementation();
|
||||
|
||||
// 获取retarget信息
|
||||
FRetargetInfo RetargetInfo = GetRetargetInfo_Implementation();
|
||||
|
||||
if (RetargetMeshComponent && !RetargetInfo.Mesh.IsNull())
|
||||
{
|
||||
USkeletalMesh* RetargetMesh = RetargetInfo.Mesh.LoadSynchronous();
|
||||
|
||||
RetargetMeshComponent->SetSkeletalMesh(RetargetMesh);
|
||||
// 广播retarget改变事件
|
||||
if (OnRetargetChanged.IsBound())
|
||||
{
|
||||
OnRetargetChanged.Broadcast(RetargetInfo);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void URetargeterComponent::ChangeRetargetTag(const FGameplayTag& NewRetargeterTag)
|
||||
{
|
||||
DefaultRetargeterTag = NewRetargeterTag;
|
||||
RefreshMeshSettings();
|
||||
}
|
||||
|
||||
FRetargetInfo URetargeterComponent::GetRetargetInfo_Implementation()
|
||||
{
|
||||
FRetargetInfo RetargetInfo;
|
||||
|
||||
if (UPHYGameInstance* GameInstance = Cast<UPHYGameInstance>(GetWorld()->GetGameInstance()))
|
||||
{
|
||||
if (TOptional<FRetargetInfo> Info = GameInstance->GetRetargetInfo(DefaultRetargeterTag); Info.IsSet())
|
||||
{
|
||||
RetargetInfo = Info.GetValue();
|
||||
}
|
||||
}
|
||||
|
||||
return RetargetInfo;
|
||||
}
|
||||
|
||||
USkeletalMeshComponent* URetargeterComponent::GetMainMeshComponent_Implementation()
|
||||
{
|
||||
if (const ACharacter* CharacterOwner = Cast<ACharacter>(GetOwner()))
|
||||
{
|
||||
return CharacterOwner->GetMesh();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
USkeletalMeshComponent* URetargeterComponent::GetRetargetMeshComponent_Implementation()
|
||||
{
|
||||
// 如果设置了自定义的Retarget Mesh组件,则使用它
|
||||
if (CustomRetargetMeshComponent)
|
||||
{
|
||||
return CustomRetargetMeshComponent;
|
||||
}
|
||||
|
||||
// 否则使用主mesh
|
||||
return GetMainMeshComponent_Implementation();
|
||||
}
|
||||
|
||||
TSubclassOf<UAnimInstance> URetargeterComponent::GetRetargeterAnimInstance_Implementation()
|
||||
{
|
||||
return RetargeterAnimInstance;
|
||||
}
|
||||
72
Source/PHY/Private/Components/RetargeterComponent.h
Normal file
72
Source/PHY/Private/Components/RetargeterComponent.h
Normal file
@@ -0,0 +1,72 @@
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Components/ActorComponent.h"
|
||||
#include "Interface/Retargetable.h"
|
||||
#include "Gameplay/PHYGameInstance.h"
|
||||
#include "RetargeterComponent.generated.h"
|
||||
|
||||
// 声明Retarget信息改变的代理
|
||||
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnRetargetChanged, const FRetargetInfo&, NewRetargetInfo);
|
||||
|
||||
|
||||
UCLASS(ClassGroup=(Custom), meta=(BlueprintSpawnableComponent))
|
||||
class PHY_API URetargeterComponent : public UActorComponent, public IRetargetable
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
URetargeterComponent();
|
||||
|
||||
/** 设置retargeter,包括动画实例和碰撞设置 */
|
||||
UFUNCTION(BlueprintCallable, Category = "PHY|Retargeter")
|
||||
void SetupRetargeter();
|
||||
|
||||
/** 刷新网格设置,应用新的骨骼网格 */
|
||||
UFUNCTION(BlueprintCallable, Category = "PHY|Retargeter")
|
||||
void RefreshMeshSettings();
|
||||
|
||||
/** 初始化retargeter(通常在BeginPlay中调用) */
|
||||
UFUNCTION(BlueprintCallable, Category = "PHY|Retargeter")
|
||||
void InitializeRetargeter();
|
||||
|
||||
/** 更改Retargeter Tag并刷新retarget设置 */
|
||||
UFUNCTION(BlueprintCallable, Category = "PHY|Retargeter")
|
||||
void ChangeRetargetTag(const FGameplayTag& NewRetargeterTag);
|
||||
|
||||
/** 获取默认的Retargeter Tag */
|
||||
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "PHY|Retargeter")
|
||||
FGameplayTag GetDefaultRetargeterTag() const { return DefaultRetargeterTag; }
|
||||
|
||||
protected:
|
||||
virtual void BeginPlay() override;
|
||||
|
||||
public:
|
||||
// IRetargetable interface implementation
|
||||
virtual FRetargetInfo GetRetargetInfo_Implementation() override;
|
||||
virtual USkeletalMeshComponent* GetMainMeshComponent_Implementation() override;
|
||||
virtual USkeletalMeshComponent* GetRetargetMeshComponent_Implementation() override;
|
||||
virtual TSubclassOf<UAnimInstance> GetRetargeterAnimInstance_Implementation() override;
|
||||
|
||||
/** Retarget信息改变时触发的代理 */
|
||||
UPROPERTY(BlueprintAssignable, Category = "PHY|Retargeter")
|
||||
FOnRetargetChanged OnRetargetChanged;
|
||||
|
||||
/** 默认的Retargeter Tag,用于从GameInstance获取Retargeter信息 */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "PHY|Retargeter", meta = (Categories = "Retargeter"))
|
||||
FGameplayTag DefaultRetargeterTag;
|
||||
|
||||
/** Retargeter使用的动画实例类 */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "PHY|Retargeter")
|
||||
TSubclassOf<UAnimInstance> RetargeterAnimInstance;
|
||||
|
||||
/** 是否在BeginPlay时自动初始化Retargeter */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "PHY|Retargeter")
|
||||
bool bAutoInitializeRetargeter = true;
|
||||
|
||||
/** 自定义的Retarget Mesh组件(如果为空,则使用角色的主Mesh) */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "PHY|Retargeter")
|
||||
TObjectPtr<USkeletalMeshComponent> CustomRetargetMeshComponent;
|
||||
};
|
||||
11
Source/PHY/Private/Gameplay/PHYGameInstance.cpp
Normal file
11
Source/PHY/Private/Gameplay/PHYGameInstance.cpp
Normal file
@@ -0,0 +1,11 @@
|
||||
//
|
||||
|
||||
|
||||
#include "PHYGameInstance.h"
|
||||
|
||||
TOptional<FRetargetInfo> UPHYGameInstance::GetRetargetInfo(const FGameplayTag& RetargetInfoTag) const
|
||||
{
|
||||
const FRetargetInfo* Info = Retargeters.Find(RetargetInfoTag);
|
||||
if (!Info) return TOptional<FRetargetInfo>();
|
||||
return TOptional<FRetargetInfo>(*Info);
|
||||
}
|
||||
42
Source/PHY/Private/Gameplay/PHYGameInstance.h
Normal file
42
Source/PHY/Private/Gameplay/PHYGameInstance.h
Normal file
@@ -0,0 +1,42 @@
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "GameplayTagContainer.h"
|
||||
#include "Engine/GameInstance.h"
|
||||
#include "PHYGameInstance.generated.h"
|
||||
|
||||
class UIKRetargeter;
|
||||
class UPHYClassDefaults;
|
||||
/**
|
||||
*
|
||||
*/
|
||||
USTRUCT(BlueprintType)
|
||||
struct FRetargetInfo
|
||||
{
|
||||
GENERATED_BODY()
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "PHY|Retargeter")
|
||||
TSoftObjectPtr<UIKRetargeter> Retargeter;
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "PHY|Retargeter")
|
||||
TSoftObjectPtr<USkeletalMesh> Mesh;
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "PHY|Retargeter")
|
||||
bool bMale = false;
|
||||
};
|
||||
|
||||
UCLASS()
|
||||
class PHY_API UPHYGameInstance : public UGameInstance
|
||||
{
|
||||
GENERATED_BODY()
|
||||
UPROPERTY(EditAnywhere, Category = "Config|Character",meta=(Categories = "Targeter"))
|
||||
TMap<FGameplayTag,FRetargetInfo> Retargeters;
|
||||
|
||||
/** 全局职业/门派默认四维配置 */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Config|Attributes", meta=(AllowPrivateAccess=true))
|
||||
TObjectPtr<UPHYClassDefaults> ClassDefaults;
|
||||
|
||||
public:
|
||||
TOptional<FRetargetInfo> GetRetargetInfo(const FGameplayTag& RetargetInfoTag) const;
|
||||
const UPHYClassDefaults* GetClassDefaults() const { return ClassDefaults; }
|
||||
|
||||
};
|
||||
4
Source/PHY/Private/Gameplay/PHYGameMode.cpp
Normal file
4
Source/PHY/Private/Gameplay/PHYGameMode.cpp
Normal file
@@ -0,0 +1,4 @@
|
||||
//
|
||||
|
||||
|
||||
#include "PHYGameMode.h"
|
||||
16
Source/PHY/Private/Gameplay/PHYGameMode.h
Normal file
16
Source/PHY/Private/Gameplay/PHYGameMode.h
Normal file
@@ -0,0 +1,16 @@
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "GameFramework/GameModeBase.h"
|
||||
#include "PHYGameMode.generated.h"
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
UCLASS()
|
||||
class PHY_API APHYGameMode : public AGameModeBase
|
||||
{
|
||||
GENERATED_BODY()
|
||||
};
|
||||
4
Source/PHY/Private/Gameplay/PHYGameState.cpp
Normal file
4
Source/PHY/Private/Gameplay/PHYGameState.cpp
Normal file
@@ -0,0 +1,4 @@
|
||||
//
|
||||
|
||||
|
||||
#include "PHYGameState.h"
|
||||
16
Source/PHY/Private/Gameplay/PHYGameState.h
Normal file
16
Source/PHY/Private/Gameplay/PHYGameState.h
Normal file
@@ -0,0 +1,16 @@
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "GameFramework/GameStateBase.h"
|
||||
#include "PHYGameState.generated.h"
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
UCLASS()
|
||||
class PHY_API APHYGameState : public AGameStateBase
|
||||
{
|
||||
GENERATED_BODY()
|
||||
};
|
||||
@@ -0,0 +1,5 @@
|
||||
|
||||
|
||||
|
||||
#include "Gameplay/Player/PHYPlayerController.h"
|
||||
|
||||
17
Source/PHY/Private/Gameplay/Player/PHYPlayerController.h
Normal file
17
Source/PHY/Private/Gameplay/Player/PHYPlayerController.h
Normal file
@@ -0,0 +1,17 @@
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "GameFramework/PlayerController.h"
|
||||
#include "PHYPlayerController.generated.h"
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
UCLASS()
|
||||
class APHYPlayerController : public APlayerController
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
};
|
||||
83
Source/PHY/Private/Gameplay/Player/PHYPlayerState.cpp
Normal file
83
Source/PHY/Private/Gameplay/Player/PHYPlayerState.cpp
Normal file
@@ -0,0 +1,83 @@
|
||||
// Copyright 2025 PHY. All Rights Reserved.
|
||||
|
||||
#include "PHYPlayerState.h"
|
||||
|
||||
#include "AbilitySystemComponent.h"
|
||||
#include "AbilitySystem/Attributes/PHYAttributeSet.h"
|
||||
#include "Character/PHYPlayerCharacter.h"
|
||||
#include "Components/RetargeterComponent.h"
|
||||
#include "Gameplay/PHYGameInstance.h"
|
||||
#include "Net/UnrealNetwork.h"
|
||||
#include "Net/Core/PushModel/PushModel.h"
|
||||
|
||||
APHYPlayerState::APHYPlayerState()
|
||||
{
|
||||
// 创建AbilitySystemComponent
|
||||
AbilitySystemComponent = CreateDefaultSubobject<UAbilitySystemComponent>(TEXT("AbilitySystemComponent"));
|
||||
AttributeSet = CreateDefaultSubobject<UPHYAttributeSet>(TEXT("AttributeSet"));
|
||||
|
||||
// 设置AbilitySystemComponent的网络复制
|
||||
AbilitySystemComponent->SetIsReplicated(true);
|
||||
|
||||
// 使用Minimal复制模式(推荐用于PlayerState)
|
||||
AbilitySystemComponent->SetReplicationMode(EGameplayEffectReplicationMode::Minimal);
|
||||
|
||||
}
|
||||
|
||||
UAbilitySystemComponent* APHYPlayerState::GetAbilitySystemComponent() const
|
||||
{
|
||||
return AbilitySystemComponent;
|
||||
}
|
||||
|
||||
void APHYPlayerState::OnRep_ReTargeterTagChanged(FGameplayTag OldTag)
|
||||
{
|
||||
//获取character
|
||||
if (APHYPlayerCharacter* Character = Cast<APHYPlayerCharacter>(GetPawn()))
|
||||
{
|
||||
//获取retargeter组件
|
||||
if (URetargeterComponent* RetargeterComponent = Character->GetRetargeterComponent())
|
||||
{
|
||||
//从GameInstance获取新的RetargetInfo
|
||||
if (const UPHYGameInstance* GI = GetGameInstance<UPHYGameInstance>())
|
||||
{
|
||||
if (const TOptional<FRetargetInfo> NewRetargetInfo = GI->GetRetargetInfo(ReTargeterTag); NewRetargetInfo.IsSet())
|
||||
{
|
||||
//应用新的RetargetInfo到组件
|
||||
RetargeterComponent->ChangeRetargetTag(ReTargeterTag);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void APHYPlayerState::GetLifetimeReplicatedProps(TArray<class FLifetimeProperty>& OutLifetimeProps) const
|
||||
{
|
||||
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
|
||||
FDoRepLifetimeParams SharedParams;
|
||||
SharedParams.bIsPushBased = true;
|
||||
|
||||
DOREPLIFETIME_WITH_PARAMS_FAST(APHYPlayerState, ReTargeterTag, SharedParams);
|
||||
}
|
||||
|
||||
void APHYPlayerState::SetReTargeterTag(const FGameplayTag& NewReTargeterTag)
|
||||
{
|
||||
MARK_PROPERTY_DIRTY_FROM_NAME(APHYPlayerState, ReTargeterTag, this);
|
||||
if (const ENetMode NetMode = GetNetMode(); NetMode == NM_Standalone || NetMode == NM_ListenServer)
|
||||
{
|
||||
OnRep_ReTargeterTagChanged(ReTargeterTag);
|
||||
}
|
||||
ReTargeterTag = NewReTargeterTag;
|
||||
ForceNetUpdate();
|
||||
}
|
||||
|
||||
void APHYPlayerState::ServerSetReTargeterTag_Implementation(const FGameplayTag& NewReTargeterTag)
|
||||
{
|
||||
if (!NewReTargeterTag.IsValid() || NewReTargeterTag == ReTargeterTag) return;
|
||||
if (const UPHYGameInstance* GI = GetGameInstance<UPHYGameInstance>())
|
||||
{
|
||||
if (GI->GetRetargetInfo(NewReTargeterTag).IsSet())
|
||||
{
|
||||
SetReTargeterTag(NewReTargeterTag);
|
||||
}
|
||||
}
|
||||
}
|
||||
62
Source/PHY/Private/Gameplay/Player/PHYPlayerState.h
Normal file
62
Source/PHY/Private/Gameplay/Player/PHYPlayerState.h
Normal file
@@ -0,0 +1,62 @@
|
||||
// Copyright 2025 PHY. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "AbilitySystemInterface.h"
|
||||
#include "GameplayTagContainer.h"
|
||||
#include "GameFramework/PlayerState.h"
|
||||
#include "PHYPlayerState.generated.h"
|
||||
|
||||
class UAbilitySystemComponent;
|
||||
class UPHYAttributeSet;
|
||||
|
||||
/**
|
||||
* 玩家状态类,包含GAS支持
|
||||
* 用于管理玩家属性、技能等游戏玩法系统
|
||||
*/
|
||||
UCLASS()
|
||||
class PHY_API APHYPlayerState : public APlayerState, public IAbilitySystemInterface
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
APHYPlayerState();
|
||||
|
||||
// IAbilitySystemInterface implementation
|
||||
virtual UAbilitySystemComponent* GetAbilitySystemComponent() const override;
|
||||
|
||||
protected:
|
||||
/** GAS Ability System Component */
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Abilities", Meta = (AllowPrivateAccess = true))
|
||||
TObjectPtr<UAbilitySystemComponent> AbilitySystemComponent;
|
||||
|
||||
/** GAS AttributeSet (Primary/Vitals/Derived). Lives on PlayerState for replication. */
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Abilities", Meta = (AllowPrivateAccess = true))
|
||||
TObjectPtr<UPHYAttributeSet> AttributeSet;
|
||||
|
||||
public:
|
||||
|
||||
/** 获取Ability System Component */
|
||||
UFUNCTION(BlueprintCallable, Category = "Abilities")
|
||||
UAbilitySystemComponent* GetPHYAbilitySystemComponent() const { return AbilitySystemComponent; }
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "Abilities")
|
||||
const UPHYAttributeSet* GetAttributeSet() const { return AttributeSet; }
|
||||
|
||||
// 原有的ReTargeterTag相关代码
|
||||
UPROPERTY(VisibleAnywhere,Category = "Config|Character",ReplicatedUsing=OnRep_ReTargeterTagChanged)
|
||||
FGameplayTag ReTargeterTag;
|
||||
|
||||
UFUNCTION()
|
||||
void OnRep_ReTargeterTagChanged(FGameplayTag OldTag);
|
||||
|
||||
virtual void GetLifetimeReplicatedProps(TArray<class FLifetimeProperty>& OutLifetimeProps) const override;
|
||||
|
||||
void SetReTargeterTag(const FGameplayTag& NewReTargeterTag);
|
||||
|
||||
public:
|
||||
UFUNCTION(Server, Reliable,BlueprintCallable)
|
||||
void ServerSetReTargeterTag(const FGameplayTag& NewReTargeterTag);
|
||||
FGameplayTag GetReTargeterTag() const { return ReTargeterTag; }
|
||||
};
|
||||
12
Source/PHY/Private/GameplayTags/InitAttributeTags.cpp
Normal file
12
Source/PHY/Private/GameplayTags/InitAttributeTags.cpp
Normal file
@@ -0,0 +1,12 @@
|
||||
// Copyright 2025 PHY. All Rights Reserved.
|
||||
|
||||
#include "GameplayTags/InitAttributeTags.h"
|
||||
|
||||
namespace InitAttributeTags
|
||||
{
|
||||
UE_DEFINE_GAMEPLAY_TAG_COMMENT(Tag__Data_Init_Primary_Strength, "Data.Init.Primary.Strength", "Init Primary Strength (SetByCaller)");
|
||||
UE_DEFINE_GAMEPLAY_TAG_COMMENT(Tag__Data_Init_Primary_Constitution, "Data.Init.Primary.Constitution", "Init Primary Constitution (SetByCaller)");
|
||||
UE_DEFINE_GAMEPLAY_TAG_COMMENT(Tag__Data_Init_Primary_InnerBreath, "Data.Init.Primary.InnerBreath", "Init Primary InnerBreath (SetByCaller)");
|
||||
UE_DEFINE_GAMEPLAY_TAG_COMMENT(Tag__Data_Init_Primary_Agility, "Data.Init.Primary.Agility", "Init Primary Agility (SetByCaller)");
|
||||
}
|
||||
|
||||
16
Source/PHY/Private/GameplayTags/InitAttributeTags.h
Normal file
16
Source/PHY/Private/GameplayTags/InitAttributeTags.h
Normal file
@@ -0,0 +1,16 @@
|
||||
// Copyright 2025 PHY. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "NativeGameplayTags.h"
|
||||
|
||||
/** GameplayTags used for initializing attributes (SetByCaller). */
|
||||
namespace InitAttributeTags
|
||||
{
|
||||
UE_DECLARE_GAMEPLAY_TAG_EXTERN(Tag__Data_Init_Primary_Strength);
|
||||
UE_DECLARE_GAMEPLAY_TAG_EXTERN(Tag__Data_Init_Primary_Constitution);
|
||||
UE_DECLARE_GAMEPLAY_TAG_EXTERN(Tag__Data_Init_Primary_InnerBreath);
|
||||
UE_DECLARE_GAMEPLAY_TAG_EXTERN(Tag__Data_Init_Primary_Agility);
|
||||
}
|
||||
|
||||
12
Source/PHY/Private/GameplayTags/InputTags.cpp
Normal file
12
Source/PHY/Private/GameplayTags/InputTags.cpp
Normal file
@@ -0,0 +1,12 @@
|
||||
//
|
||||
|
||||
|
||||
#include "InputTags.h"
|
||||
|
||||
|
||||
namespace InputTags
|
||||
{
|
||||
UE_DEFINE_GAMEPLAY_TAG_COMMENT(Tag__Input_Move,"Input.Move","Tag for move input");
|
||||
UE_DEFINE_GAMEPLAY_TAG_COMMENT(Tag__Input_Look,"Input.Look","Tag for look input");
|
||||
UE_DEFINE_GAMEPLAY_TAG_COMMENT(Tag__Input_Jump,"Input.Jump","Tag for jump input");
|
||||
}
|
||||
16
Source/PHY/Private/GameplayTags/InputTags.h
Normal file
16
Source/PHY/Private/GameplayTags/InputTags.h
Normal file
@@ -0,0 +1,16 @@
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "NativeGameplayTags.h"
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
namespace InputTags
|
||||
{
|
||||
UE_DECLARE_GAMEPLAY_TAG_EXTERN(Tag__Input_Move);
|
||||
UE_DECLARE_GAMEPLAY_TAG_EXTERN(Tag__Input_Look);
|
||||
UE_DECLARE_GAMEPLAY_TAG_EXTERN(Tag__Input_Jump);
|
||||
};
|
||||
13
Source/PHY/Private/GameplayTags/MovementTags.cpp
Normal file
13
Source/PHY/Private/GameplayTags/MovementTags.cpp
Normal file
@@ -0,0 +1,13 @@
|
||||
//
|
||||
|
||||
|
||||
#include "MovementTags.h"
|
||||
|
||||
|
||||
namespace MovementTags
|
||||
{
|
||||
UE_DEFINE_GAMEPLAY_TAG_COMMENT(Tag__MovementSet_Default,"GMS.MovementSet.Default","Tag for GMS MovementSet Default");
|
||||
UE_DEFINE_GAMEPLAY_TAG_COMMENT(Tag__MovementSet_ADS,"GMS.MovementSet.ADS","Tag for GMS MovementSet ADS");
|
||||
UE_DEFINE_GAMEPLAY_TAG_COMMENT(Tag__MovementSet_ADS_Crouched,"GMS.MovementSet.ADS.Crouched","Tag for GMS MovementSet ADS Crouched");
|
||||
UE_DEFINE_GAMEPLAY_TAG_COMMENT(Tag__MovementSet_Crouched,"GMS.MovementSet.Crouched","Tag for GMS MovementSet Crouched");
|
||||
}
|
||||
17
Source/PHY/Private/GameplayTags/MovementTags.h
Normal file
17
Source/PHY/Private/GameplayTags/MovementTags.h
Normal file
@@ -0,0 +1,17 @@
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "NativeGameplayTags.h"
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
namespace MovementTags
|
||||
{
|
||||
UE_DECLARE_GAMEPLAY_TAG_EXTERN(Tag__MovementSet_Default);
|
||||
UE_DECLARE_GAMEPLAY_TAG_EXTERN(Tag__MovementSet_ADS);
|
||||
UE_DECLARE_GAMEPLAY_TAG_EXTERN(Tag__MovementSet_ADS_Crouched);
|
||||
UE_DECLARE_GAMEPLAY_TAG_EXTERN(Tag__MovementSet_Crouched);
|
||||
};
|
||||
15
Source/PHY/Private/GameplayTags/ReTargeterTags.cpp
Normal file
15
Source/PHY/Private/GameplayTags/ReTargeterTags.cpp
Normal file
@@ -0,0 +1,15 @@
|
||||
//
|
||||
|
||||
|
||||
#include "ReTargeterTags.h"
|
||||
|
||||
|
||||
namespace ReTargeterTags
|
||||
{
|
||||
UE_DEFINE_GAMEPLAY_TAG_COMMENT(Tag__Retargeter_F_Warrior,"Retargeter.F.Warrior","Tag for retargeting to the F_Warrior retargeter");
|
||||
UE_DEFINE_GAMEPLAY_TAG_COMMENT(Tag__Retargeter_F_Warrior_Newbie,"Retargeter.F.Warrior.Newbie","Tag for retargeting to the F_Warrior_Newbee retargeter");
|
||||
UE_DEFINE_GAMEPLAY_TAG_COMMENT(Tag__Retargeter_F_Warrior_Addvanced,"Retargeter.F.Warrior.Addvanced","Tag for retargeting to the F_Warrior_Addvanced retargeter");
|
||||
UE_DEFINE_GAMEPLAY_TAG_COMMENT(Tag__Retargeter_M_Warrior,"Retargeter.M.Warrior","Tag for retargeting to the M_Warrior retargeter");
|
||||
UE_DEFINE_GAMEPLAY_TAG_COMMENT(Tag__Retargeter_M_Warrior_Newbie,"Retargeter.M.Warrior.Newbie","Tag for retargeting to the M_Warrior_Newbee retargeter");
|
||||
UE_DEFINE_GAMEPLAY_TAG_COMMENT(Tag__Retargeter_M_Warrior_Addvanced,"Retargeter.M.Warrior.Addvanced","Tag for retargeting to the M_Warrior_Addvanced retargeter");
|
||||
}
|
||||
19
Source/PHY/Private/GameplayTags/ReTargeterTags.h
Normal file
19
Source/PHY/Private/GameplayTags/ReTargeterTags.h
Normal file
@@ -0,0 +1,19 @@
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "NativeGameplayTags.h"
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
namespace ReTargeterTags
|
||||
{
|
||||
UE_DECLARE_GAMEPLAY_TAG_EXTERN(Tag__Retargeter_F_Warrior);
|
||||
UE_DECLARE_GAMEPLAY_TAG_EXTERN(Tag__Retargeter_F_Warrior_Newbie);
|
||||
UE_DECLARE_GAMEPLAY_TAG_EXTERN(Tag__Retargeter_F_Warrior_Addvanced);
|
||||
UE_DECLARE_GAMEPLAY_TAG_EXTERN(Tag__Retargeter_M_Warrior);
|
||||
UE_DECLARE_GAMEPLAY_TAG_EXTERN(Tag__Retargeter_M_Warrior_Newbie);
|
||||
UE_DECLARE_GAMEPLAY_TAG_EXTERN(Tag__Retargeter_M_Warrior_Addvanced);
|
||||
};
|
||||
10
Source/PHY/Private/GameplayTags/RegenTags.cpp
Normal file
10
Source/PHY/Private/GameplayTags/RegenTags.cpp
Normal file
@@ -0,0 +1,10 @@
|
||||
// Copyright 2025 PHY. All Rights Reserved.
|
||||
|
||||
#include "GameplayTags/RegenTags.h"
|
||||
|
||||
namespace RegenTags
|
||||
{
|
||||
UE_DEFINE_GAMEPLAY_TAG_COMMENT(Tag__Data_Regen_Health, "Data.Regen.Health", "SetByCaller: Health regen per tick");
|
||||
UE_DEFINE_GAMEPLAY_TAG_COMMENT(Tag__Data_Regen_InnerPower, "Data.Regen.InnerPower", "SetByCaller: InnerPower regen per tick");
|
||||
}
|
||||
|
||||
13
Source/PHY/Private/GameplayTags/RegenTags.h
Normal file
13
Source/PHY/Private/GameplayTags/RegenTags.h
Normal file
@@ -0,0 +1,13 @@
|
||||
// Copyright 2025 PHY. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "NativeGameplayTags.h"
|
||||
|
||||
namespace RegenTags
|
||||
{
|
||||
UE_DECLARE_GAMEPLAY_TAG_EXTERN(Tag__Data_Regen_Health);
|
||||
UE_DECLARE_GAMEPLAY_TAG_EXTERN(Tag__Data_Regen_InnerPower);
|
||||
}
|
||||
|
||||
13
Source/PHY/Private/GameplayTags/UITags.cpp
Normal file
13
Source/PHY/Private/GameplayTags/UITags.cpp
Normal file
@@ -0,0 +1,13 @@
|
||||
//
|
||||
|
||||
|
||||
#include "UITags.h"
|
||||
|
||||
|
||||
namespace UITags
|
||||
{
|
||||
UE_DEFINE_GAMEPLAY_TAG(Tag__UI_Layer_Game, "UI.Layer.Game");
|
||||
UE_DEFINE_GAMEPLAY_TAG(Tag__UI_Layer_GameMenu, "UI.Layer.GameMenu");
|
||||
UE_DEFINE_GAMEPLAY_TAG(Tag__UI_Layer_Menu, "UI.Layer.Menu");
|
||||
UE_DEFINE_GAMEPLAY_TAG(Tag__UI_Layer_Modal, "UI.Layer.Modal");
|
||||
}
|
||||
17
Source/PHY/Private/GameplayTags/UITags.h
Normal file
17
Source/PHY/Private/GameplayTags/UITags.h
Normal file
@@ -0,0 +1,17 @@
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "NativeGameplayTags.h"
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
namespace UITags
|
||||
{
|
||||
UE_DECLARE_GAMEPLAY_TAG_EXTERN(Tag__UI_Layer_Game);
|
||||
UE_DECLARE_GAMEPLAY_TAG_EXTERN(Tag__UI_Layer_GameMenu);
|
||||
UE_DECLARE_GAMEPLAY_TAG_EXTERN(Tag__UI_Layer_Menu);
|
||||
UE_DECLARE_GAMEPLAY_TAG_EXTERN(Tag__UI_Layer_Modal);
|
||||
};
|
||||
17
Source/PHY/Private/Input/InputHelper.h
Normal file
17
Source/PHY/Private/Input/InputHelper.h
Normal file
@@ -0,0 +1,17 @@
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "GIPS_InputSystemComponent.h"
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
namespace InputHelper
|
||||
{
|
||||
static APawn* GetOwnerPawn(const UGIPS_InputSystemComponent* InputSystemComponent)
|
||||
{
|
||||
return Cast<APawn>(InputSystemComponent->GetOwner());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
//
|
||||
|
||||
|
||||
#include "InputProcessor_Look.h"
|
||||
|
||||
#include "GameplayTags/InputTags.h"
|
||||
#include "Input/InputHelper.h"
|
||||
|
||||
UInputProcessor_Look::UInputProcessor_Look()
|
||||
{
|
||||
InputTags.AddTag(::InputTags::Tag__Input_Look);
|
||||
TriggerEvents.Empty();
|
||||
TriggerEvents.AddUnique(ETriggerEvent::Triggered);
|
||||
}
|
||||
|
||||
void UInputProcessor_Look::HandleInputTriggered_Implementation(UGIPS_InputSystemComponent* IC,
|
||||
const FInputActionInstance& ActionData, FGameplayTag InputTag) const
|
||||
{
|
||||
if (InputTag != InputTags::Tag__Input_Look) return;
|
||||
APawn* OwnerPawn = InputHelper::GetOwnerPawn(IC);
|
||||
if (!OwnerPawn) return;
|
||||
const FInputActionValue LookInputValue = IC->GetInputActionValueOfInputTag(InputTag);
|
||||
const FVector2D LookInputVector = LookInputValue.Get<FVector2D>();
|
||||
OwnerPawn->AddControllerYawInput(LookInputVector.X);
|
||||
OwnerPawn->AddControllerPitchInput(LookInputVector.Y);
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "GIPS_InputProcessor.h"
|
||||
#include "InputProcessor_Look.generated.h"
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
UCLASS()
|
||||
class PHY_API UInputProcessor_Look : public UGIPS_InputProcessor
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UInputProcessor_Look();
|
||||
virtual void HandleInputTriggered_Implementation(UGIPS_InputSystemComponent* IC, const FInputActionInstance& ActionData, FGameplayTag InputTag) const override;
|
||||
};
|
||||
@@ -0,0 +1,32 @@
|
||||
//
|
||||
|
||||
|
||||
#include "InputProcessor_Move.h"
|
||||
|
||||
#include "GameplayTags/InputTags.h"
|
||||
#include "Input/InputHelper.h"
|
||||
|
||||
UInputProcessor_Move::UInputProcessor_Move()
|
||||
{
|
||||
InputTags.AddTag(::InputTags::Tag__Input_Move);
|
||||
TriggerEvents.Empty();
|
||||
TriggerEvents.AddUnique(ETriggerEvent::Triggered);
|
||||
}
|
||||
|
||||
void UInputProcessor_Move::HandleInputTriggered_Implementation(UGIPS_InputSystemComponent* IC,
|
||||
const FInputActionInstance& ActionData, FGameplayTag InputTag) const
|
||||
{
|
||||
if (InputTag != InputTags::Tag__Input_Move) return;
|
||||
APawn* OwnerPawn = InputHelper::GetOwnerPawn(IC);
|
||||
if (!OwnerPawn) return;
|
||||
const FInputActionValue MoveInputValue = IC->GetInputActionValueOfInputTag(InputTag);
|
||||
const FVector2D MoveInputVector = MoveInputValue.Get<FVector2D>();
|
||||
// 计算向右
|
||||
const FRotator Rotation = OwnerPawn->GetControlRotation();
|
||||
const FRotator YawRotation(0.f, Rotation.Yaw, 0.f);
|
||||
const FVector ForwardDirection = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::X);
|
||||
const FVector RightDirection = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::Y);
|
||||
// 添加输入
|
||||
OwnerPawn->AddMovementInput(ForwardDirection, MoveInputVector.Y);
|
||||
OwnerPawn->AddMovementInput(RightDirection, MoveInputVector.X);
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "GIPS_InputProcessor.h"
|
||||
#include "InputProcessor_Move.generated.h"
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
UCLASS()
|
||||
class PHY_API UInputProcessor_Move : public UGIPS_InputProcessor
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UInputProcessor_Move();
|
||||
|
||||
virtual void HandleInputTriggered_Implementation(UGIPS_InputSystemComponent* IC, const FInputActionInstance& ActionData, FGameplayTag InputTag) const override;
|
||||
};
|
||||
7
Source/PHY/Private/Interface/Retargetable.cpp
Normal file
7
Source/PHY/Private/Interface/Retargetable.cpp
Normal file
@@ -0,0 +1,7 @@
|
||||
//
|
||||
|
||||
|
||||
#include "Retargetable.h"
|
||||
|
||||
|
||||
// Add default functionality here for any IRetargeterable functions that are not pure virtual.
|
||||
37
Source/PHY/Private/Interface/Retargetable.h
Normal file
37
Source/PHY/Private/Interface/Retargetable.h
Normal file
@@ -0,0 +1,37 @@
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Gameplay/PHYGameInstance.h"
|
||||
#include "UObject/Interface.h"
|
||||
#include "Retargetable.generated.h"
|
||||
|
||||
|
||||
// This class does not need to be modified.
|
||||
UINTERFACE()
|
||||
class URetargetable : public UInterface
|
||||
{
|
||||
GENERATED_BODY()
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
class PHY_API IRetargetable
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
// Add interface functions to this class. This is the class that will be inherited to implement this interface.
|
||||
public:
|
||||
UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category = "PHY|Retargeter")
|
||||
FRetargetInfo GetRetargetInfo();
|
||||
|
||||
UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category = "PHY|Retargeter")
|
||||
USkeletalMeshComponent* GetMainMeshComponent();
|
||||
|
||||
UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category = "PHY|Retargeter")
|
||||
USkeletalMeshComponent* GetRetargetMeshComponent();
|
||||
UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category = "PHY|Retargeter")
|
||||
TSubclassOf<UAnimInstance> GetRetargeterAnimInstance();
|
||||
};
|
||||
63
Source/PHY/Private/UI/HUD/PHYGameHUD.cpp
Normal file
63
Source/PHY/Private/UI/HUD/PHYGameHUD.cpp
Normal file
@@ -0,0 +1,63 @@
|
||||
//
|
||||
|
||||
|
||||
#include "PHYGameHUD.h"
|
||||
|
||||
#include "GameplayTags/UITags.h"
|
||||
#include "UI/Actions/GUIS_AsyncAction_PushContentToUILayer.h"
|
||||
|
||||
UGUIS_GameUISubsystem* APHYGameHUD::GetUISubsystem() const
|
||||
{
|
||||
if (const UGameInstance* GameInstance = GetGameInstance())
|
||||
{
|
||||
return GameInstance->GetSubsystem<UGUIS_GameUISubsystem>();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void APHYGameHUD::OnBeforePushOverlapWidget(UCommonActivatableWidget* UserWidget)
|
||||
{
|
||||
}
|
||||
|
||||
void APHYGameHUD::OnAfterPushOverlapWidget(UCommonActivatableWidget* UserWidget)
|
||||
{
|
||||
}
|
||||
|
||||
void APHYGameHUD::InitializeHUD()
|
||||
{
|
||||
// 获取当前的player controller
|
||||
APlayerController* PC = GetOwningPlayerController();
|
||||
if (!PC) return;
|
||||
if (ULocalPlayer* LocalPlayer = PC->GetLocalPlayer())
|
||||
{
|
||||
if (UGUIS_GameUISubsystem* UISubsystem = GetUISubsystem())
|
||||
{
|
||||
// 创建并注册HUD的UI到本地玩家
|
||||
UISubsystem->AddPlayer(LocalPlayer);
|
||||
}
|
||||
}
|
||||
if (OverlapWidgetClass.IsNull()) return;
|
||||
// 将重叠提示控件推送到游戏UI层
|
||||
if (UGUIS_AsyncAction_PushContentToUILayer * PushAction = UGUIS_AsyncAction_PushContentToUILayer::PushContentToUILayerForPlayer(PC, OverlapWidgetClass, UITags::Tag__UI_Layer_Game))
|
||||
{
|
||||
PushAction->BeforePush.AddDynamic(this, &APHYGameHUD::OnBeforePushOverlapWidget);
|
||||
PushAction->AfterPush.AddDynamic(this, &APHYGameHUD::OnAfterPushOverlapWidget);
|
||||
PushAction->Activate();
|
||||
}
|
||||
}
|
||||
|
||||
void APHYGameHUD::EndPlay(const EEndPlayReason::Type EndPlayReason)
|
||||
{
|
||||
if (const APlayerController* PC = GetOwningPlayerController())
|
||||
{
|
||||
if (ULocalPlayer* LocalPlayer = PC->GetLocalPlayer())
|
||||
{
|
||||
if (UGUIS_GameUISubsystem* UISubsystem = GetUISubsystem())
|
||||
{
|
||||
// 创建并注册HUD的UI到本地玩家
|
||||
UISubsystem->RemovePlayer(LocalPlayer);
|
||||
}
|
||||
}
|
||||
}
|
||||
Super::EndPlay(EndPlayReason);
|
||||
}
|
||||
38
Source/PHY/Private/UI/HUD/PHYGameHUD.h
Normal file
38
Source/PHY/Private/UI/HUD/PHYGameHUD.h
Normal file
@@ -0,0 +1,38 @@
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "GameFramework/HUD.h"
|
||||
#include "UI/GUIS_GameUISubsystem.h"
|
||||
#include "PHYGameHUD.generated.h"
|
||||
|
||||
|
||||
class UCommonActivatableWidget;
|
||||
/**
|
||||
*
|
||||
*/
|
||||
UCLASS()
|
||||
class PHY_API APHYGameHUD : public AHUD
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
// 获取GUIS_GameSubsystem
|
||||
UGUIS_GameUISubsystem* GetUISubsystem() const;
|
||||
|
||||
public:
|
||||
|
||||
void InitializeHUD();
|
||||
|
||||
protected:
|
||||
virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override;
|
||||
|
||||
UPROPERTY(EditDefaultsOnly, Category="PHY|UI")
|
||||
TSoftClassPtr<UCommonActivatableWidget> OverlapWidgetClass;
|
||||
|
||||
private:
|
||||
UFUNCTION()
|
||||
void OnBeforePushOverlapWidget(UCommonActivatableWidget* UserWidget);
|
||||
UFUNCTION()
|
||||
void OnAfterPushOverlapWidget(UCommonActivatableWidget* UserWidget);
|
||||
};
|
||||
4
Source/PHY/Private/UI/Menu/Menu_Overlap.cpp
Normal file
4
Source/PHY/Private/UI/Menu/Menu_Overlap.cpp
Normal file
@@ -0,0 +1,4 @@
|
||||
//
|
||||
|
||||
|
||||
#include "Menu_Overlap.h"
|
||||
16
Source/PHY/Private/UI/Menu/Menu_Overlap.h
Normal file
16
Source/PHY/Private/UI/Menu/Menu_Overlap.h
Normal file
@@ -0,0 +1,16 @@
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "UI/GUIS_ActivatableWidget.h"
|
||||
#include "Menu_Overlap.generated.h"
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
UCLASS()
|
||||
class PHY_API UMenu_Overlap : public UGUIS_ActivatableWidget
|
||||
{
|
||||
GENERATED_BODY()
|
||||
};
|
||||
37
Source/PHY/Private/UI/PHYGameUILayout.cpp
Normal file
37
Source/PHY/Private/UI/PHYGameUILayout.cpp
Normal file
@@ -0,0 +1,37 @@
|
||||
// Copyright 2026 PHY. All Rights Reserved.
|
||||
|
||||
#include "UI/PHYGameUILayout.h"
|
||||
|
||||
#include "GameplayTags/UITags.h"
|
||||
#include "Widgets/CommonActivatableWidgetContainer.h"
|
||||
|
||||
#include UE_INLINE_GENERATED_CPP_BY_NAME(PHYGameUILayout)
|
||||
|
||||
UPHYGameUILayout::UPHYGameUILayout(const FObjectInitializer& ObjectInitializer)
|
||||
: Super(ObjectInitializer)
|
||||
{
|
||||
}
|
||||
|
||||
void UPHYGameUILayout::NativeOnInitialized()
|
||||
{
|
||||
Super::NativeOnInitialized();
|
||||
|
||||
// Register available layers. Widget Blueprint can bind any subset.
|
||||
if (Layer_Game)
|
||||
{
|
||||
RegisterLayer(UITags::Tag__UI_Layer_Game, Layer_Game);
|
||||
}
|
||||
if (Layer_GameMenu)
|
||||
{
|
||||
RegisterLayer(UITags::Tag__UI_Layer_GameMenu, Layer_GameMenu);
|
||||
}
|
||||
if (Layer_Menu)
|
||||
{
|
||||
RegisterLayer(UITags::Tag__UI_Layer_Menu, Layer_Menu);
|
||||
}
|
||||
if (Layer_Modal)
|
||||
{
|
||||
RegisterLayer(UITags::Tag__UI_Layer_Modal, Layer_Modal);
|
||||
}
|
||||
}
|
||||
|
||||
47
Source/PHY/Private/UI/PHYGameUILayout.h
Normal file
47
Source/PHY/Private/UI/PHYGameUILayout.h
Normal file
@@ -0,0 +1,47 @@
|
||||
// Copyright 2026 PHY. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "UI/GUIS_GameUILayout.h"
|
||||
#include "PHYGameUILayout.generated.h"
|
||||
|
||||
class UCommonActivatableWidgetStack;
|
||||
class UCommonActivatableWidgetContainerBase;
|
||||
|
||||
/**
|
||||
* Root UI Layout for a single local player.
|
||||
*
|
||||
* In BP (Widget Blueprint derived from this), you should:
|
||||
* - Create several UCommonActivatableWidgetStack (or other ContainerBase) widgets
|
||||
* - Bind them to the properties below
|
||||
* - In PreConstruct/Construct, call RegisterLayer for each stack with tags from UITags
|
||||
*/
|
||||
UCLASS(Abstract, BlueprintType)
|
||||
class PHY_API UPHYGameUILayout : public UGUIS_GameUILayout
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UPHYGameUILayout(const FObjectInitializer& ObjectInitializer);
|
||||
|
||||
protected:
|
||||
virtual void NativeOnInitialized() override;
|
||||
|
||||
/** Game HUD layer stack. Tag: UI.Layer.Game */
|
||||
UPROPERTY(meta=(BindWidgetOptional), BlueprintReadOnly)
|
||||
TObjectPtr<UCommonActivatableWidgetContainerBase> Layer_Game = nullptr;
|
||||
|
||||
/** In-game menu layer stack. Tag: UI.Layer.GameMenu */
|
||||
UPROPERTY(meta=(BindWidgetOptional), BlueprintReadOnly)
|
||||
TObjectPtr<UCommonActivatableWidgetContainerBase> Layer_GameMenu = nullptr;
|
||||
|
||||
/** Main menu layer stack. Tag: UI.Layer.Menu */
|
||||
UPROPERTY(meta=(BindWidgetOptional), BlueprintReadOnly)
|
||||
TObjectPtr<UCommonActivatableWidgetContainerBase> Layer_Menu = nullptr;
|
||||
|
||||
/** Modal layer stack. Tag: UI.Layer.Modal */
|
||||
UPROPERTY(meta=(BindWidgetOptional), BlueprintReadOnly)
|
||||
TObjectPtr<UCommonActivatableWidgetContainerBase> Layer_Modal = nullptr;
|
||||
};
|
||||
|
||||
20
Source/PHY/Private/UI/PHYGameUIPolicy.h
Normal file
20
Source/PHY/Private/UI/PHYGameUIPolicy.h
Normal file
@@ -0,0 +1,20 @@
|
||||
// Copyright 2026 PHY. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "UI/GUIS_GameUIPolicy.h"
|
||||
#include "PHYGameUIPolicy.generated.h"
|
||||
|
||||
/**
|
||||
* Game-specific UI policy.
|
||||
*
|
||||
* This class mainly exists so we can set up a default LayoutClass in a Blueprint child,
|
||||
* and keep all UI wiring inside the game module.
|
||||
*/
|
||||
UCLASS(BlueprintType)
|
||||
class PHY_API UPHYGameUIPolicy : public UGUIS_GameUIPolicy
|
||||
{
|
||||
GENERATED_BODY()
|
||||
};
|
||||
|
||||
40
Source/PHY/Private/UI/Synty/Synty_IconFrame.cpp
Normal file
40
Source/PHY/Private/UI/Synty/Synty_IconFrame.cpp
Normal file
@@ -0,0 +1,40 @@
|
||||
//
|
||||
|
||||
|
||||
#include "Synty_IconFrame.h"
|
||||
|
||||
#include "Components/Image.h"
|
||||
#include "Kismet/KismetMaterialLibrary.h"
|
||||
|
||||
void USynty_IconFrame::UpdateIconImage(TSoftObjectPtr<UTexture2D> NewTexture)
|
||||
{
|
||||
IconTexture = NewTexture;
|
||||
if (IconImage && !IconTexture.IsNull())
|
||||
{
|
||||
if (IconMaterialInstance)
|
||||
{
|
||||
IconMaterialInstance->SetTextureParameterValue(FName("InputTexture"), IconTexture.LoadSynchronous());
|
||||
FSlateBrush Brush;
|
||||
Brush.SetResourceObject(IconMaterialInstance);
|
||||
Brush.DrawAs = ESlateBrushDrawType::Box;
|
||||
Brush.SetImageSize(IconSize);
|
||||
Brush.Margin = IconPadding;
|
||||
IconImage->SetBrush(Brush);
|
||||
}
|
||||
else
|
||||
{
|
||||
IconImage->SetBrushFromTexture(IconTexture.LoadSynchronous());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void USynty_IconFrame::NativePreConstruct()
|
||||
{
|
||||
Super::NativePreConstruct();
|
||||
if (IconMaterialInterface)
|
||||
{
|
||||
IconMaterialInstance = UKismetMaterialLibrary::CreateDynamicMaterialInstance(this, IconMaterialInterface);
|
||||
}
|
||||
UpdateIconImage(IconTexture);
|
||||
}
|
||||
34
Source/PHY/Private/UI/Synty/Synty_IconFrame.h
Normal file
34
Source/PHY/Private/UI/Synty/Synty_IconFrame.h
Normal file
@@ -0,0 +1,34 @@
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Blueprint/UserWidget.h"
|
||||
#include "Synty_IconFrame.generated.h"
|
||||
|
||||
/**
|
||||
* Synty风格的图标框架Widget
|
||||
*/
|
||||
UCLASS()
|
||||
class PHY_API USynty_IconFrame : public UUserWidget
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
UPROPERTY(meta=(BindWidget))
|
||||
class UImage* IconImage;
|
||||
|
||||
UPROPERTY(EditAnywhere,Category="Synty|Config")
|
||||
TSoftObjectPtr<UTexture2D> IconTexture;
|
||||
UPROPERTY(EditAnywhere,Category="Synty|Config")
|
||||
FVector2D IconSize;
|
||||
UPROPERTY(EditAnywhere,Category="Synty|Config")
|
||||
FMargin IconPadding;
|
||||
UPROPERTY(EditAnywhere,Category="Synty|Config")
|
||||
UMaterialInterface* IconMaterialInterface;
|
||||
UPROPERTY()
|
||||
UMaterialInstanceDynamic* IconMaterialInstance;
|
||||
public:
|
||||
void UpdateIconImage(TSoftObjectPtr<UTexture2D> NewTexture);
|
||||
protected:
|
||||
virtual void NativePreConstruct() override;
|
||||
};
|
||||
21
Source/PHYEditor.Target.cs
Normal file
21
Source/PHYEditor.Target.cs
Normal file
@@ -0,0 +1,21 @@
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
using UnrealBuildTool;
|
||||
using System.Collections.Generic;
|
||||
|
||||
public class PHYEditorTarget : TargetRules
|
||||
{
|
||||
public PHYEditorTarget( TargetInfo Target) : base(Target)
|
||||
{
|
||||
Type = TargetType.Editor;
|
||||
DefaultBuildSettings = BuildSettingsVersion.V6;
|
||||
IncludeOrderVersion = EngineIncludeOrderVersion.Unreal5_7;
|
||||
ExtraModuleNames.Add("PHY");
|
||||
RegisterModulesCreatedByRider();
|
||||
}
|
||||
|
||||
private void RegisterModulesCreatedByRider()
|
||||
{
|
||||
ExtraModuleNames.AddRange(new string[] { "PHYInventory" });
|
||||
}
|
||||
}
|
||||
34
Source/PHYInventory/PHYInventory.Build.cs
Normal file
34
Source/PHYInventory/PHYInventory.Build.cs
Normal file
@@ -0,0 +1,34 @@
|
||||
using UnrealBuildTool;
|
||||
|
||||
public class PHYInventory : ModuleRules
|
||||
{
|
||||
public PHYInventory(ReadOnlyTargetRules Target) : base(Target)
|
||||
{
|
||||
PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs;
|
||||
|
||||
PublicDependencyModuleNames.AddRange(
|
||||
new string[]
|
||||
{
|
||||
"Core",
|
||||
"GenericUISystem",
|
||||
"GenericInputSystem",
|
||||
"CommonUI",
|
||||
"UMG",
|
||||
"Slate",
|
||||
"SlateCore",
|
||||
"GameplayTags",
|
||||
"GenericInventorySystem"
|
||||
}
|
||||
);
|
||||
|
||||
PrivateDependencyModuleNames.AddRange(
|
||||
new string[]
|
||||
{
|
||||
"CoreUObject",
|
||||
"Engine",
|
||||
"Slate",
|
||||
"SlateCore"
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
7
Source/PHYInventory/Private/ItemFilterInterface.cpp
Normal file
7
Source/PHYInventory/Private/ItemFilterInterface.cpp
Normal file
@@ -0,0 +1,7 @@
|
||||
//
|
||||
|
||||
|
||||
#include "ItemFilterInterface.h"
|
||||
|
||||
|
||||
// Add default functionality here for any IItemFilterInterface functions that are not pure virtual.
|
||||
17
Source/PHYInventory/Private/PHYInventory.cpp
Normal file
17
Source/PHYInventory/Private/PHYInventory.cpp
Normal file
@@ -0,0 +1,17 @@
|
||||
#include "PHYInventory.h"
|
||||
|
||||
#define LOCTEXT_NAMESPACE "FPHYInventoryModule"
|
||||
|
||||
void FPHYInventoryModule::StartupModule()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void FPHYInventoryModule::ShutdownModule()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
#undef LOCTEXT_NAMESPACE
|
||||
|
||||
IMPLEMENT_MODULE(FPHYInventoryModule, PHYInventory)
|
||||
35
Source/PHYInventory/Private/UI/ItemStacks/ItemData.cpp
Normal file
35
Source/PHYInventory/Private/UI/ItemStacks/ItemData.cpp
Normal file
@@ -0,0 +1,35 @@
|
||||
//
|
||||
|
||||
|
||||
#include "UI/ItemStacks/ItemData.h"
|
||||
|
||||
#include "UI/ItemStacks/ItemStackContainer.h"
|
||||
|
||||
UItemStackContainer* UItemData::GetContainer() const
|
||||
{
|
||||
return OwningItemStackContainer.Get();
|
||||
}
|
||||
|
||||
void UItemData::SetContainer(UItemStackContainer* InContainer)
|
||||
{
|
||||
OwningItemStackContainer = InContainer;
|
||||
}
|
||||
|
||||
void UItemData::Reset()
|
||||
{
|
||||
ItemInfo = FGIS_ItemInfo();
|
||||
}
|
||||
|
||||
bool UItemData::IsValidItem() const
|
||||
{
|
||||
return ItemInfo.IsValid();
|
||||
}
|
||||
|
||||
int32 UItemData::GetItemSlotIndex() const
|
||||
{
|
||||
if (OwningItemStackContainer.IsValid())
|
||||
{
|
||||
return OwningItemStackContainer.Get()->FindItemSlotIndex(this);
|
||||
}
|
||||
return INDEX_NONE;
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
//
|
||||
|
||||
|
||||
#include "UI/ItemStacks/ItemDataDragDropOperation.h"
|
||||
#include "UI/ItemStacks/ItemData.h"
|
||||
#include "UI/ItemStacks/ItemStackContainer.h"
|
||||
|
||||
UItemStackContainer* UItemDataDragDropOperation::GetTargetItemStackView() const
|
||||
{
|
||||
return TargetItemStackView.Get();
|
||||
}
|
||||
|
||||
void UItemDataDragDropOperation::SetTargetItemStackView(UItemStackContainer* InItemStackContainer)
|
||||
{
|
||||
TargetItemStackView = TWeakObjectPtr<UItemStackContainer>(InItemStackContainer);
|
||||
}
|
||||
|
||||
UItemData* UItemDataDragDropOperation::GetTargetItemData() const
|
||||
{
|
||||
return TargetItemData.Get();
|
||||
}
|
||||
|
||||
void UItemDataDragDropOperation::SetTargetItemData(UItemData* InItemData)
|
||||
{
|
||||
TargetItemData = TWeakObjectPtr<UItemData>(InItemData);
|
||||
}
|
||||
|
||||
int32 UItemDataDragDropOperation::GetTargetItemIndex() const
|
||||
{
|
||||
return TargetItemIndex;
|
||||
}
|
||||
|
||||
void UItemDataDragDropOperation::SetTargetItemIndex(int32 InTargetItemIndex)
|
||||
{
|
||||
TargetItemIndex = InTargetItemIndex;
|
||||
}
|
||||
|
||||
UItemStackContainer* UItemDataDragDropOperation::GetSourceItemStackView() const
|
||||
{
|
||||
return SourceItemStackView.Get();
|
||||
}
|
||||
|
||||
void UItemDataDragDropOperation::SetSourceItemStackView(UItemStackContainer* InItemStackContainer)
|
||||
{
|
||||
SourceItemStackView = TWeakObjectPtr<UItemStackContainer>(InItemStackContainer);
|
||||
}
|
||||
|
||||
UItemData* UItemDataDragDropOperation::GetSourceItemData() const
|
||||
{
|
||||
return SourceItemData.Get();
|
||||
}
|
||||
|
||||
void UItemDataDragDropOperation::SetSourceItemData(UItemData* InItemData)
|
||||
{
|
||||
SourceItemData = TWeakObjectPtr<UItemData>(InItemData);
|
||||
}
|
||||
|
||||
int32 UItemDataDragDropOperation::GwtSourceItemIndex() const
|
||||
{
|
||||
return SourceItemIndex;
|
||||
}
|
||||
|
||||
void UItemDataDragDropOperation::SetSourceItemIndex(int32 InSourceItemIndex)
|
||||
{
|
||||
SourceItemIndex = InSourceItemIndex;
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
//
|
||||
|
||||
|
||||
#include "UI/ItemStacks/ItemDataDraggingWidget.h"
|
||||
|
||||
#include "CommonTextBlock.h"
|
||||
#include "Components/Image.h"
|
||||
#include "Components/SizeBox.h"
|
||||
|
||||
void UItemDataDraggingWidget::NativePreConstruct()
|
||||
{
|
||||
Super::NativePreConstruct();
|
||||
if (MainSizeBox)
|
||||
{
|
||||
MainSizeBox->SetHeightOverride(Height);
|
||||
MainSizeBox->SetWidthOverride(Width);
|
||||
}
|
||||
SetAmount(ItemAmount);
|
||||
SetIcon(IconTexture);
|
||||
}
|
||||
|
||||
void UItemDataDraggingWidget::SetAmount(const int32 InAmount)
|
||||
{
|
||||
ItemAmount = InAmount;
|
||||
if (AmountText)
|
||||
{
|
||||
AmountText->SetText(FText::AsNumber(ItemAmount));
|
||||
}
|
||||
}
|
||||
|
||||
void UItemDataDraggingWidget::SetIcon(UTexture2D* InIconTexture)
|
||||
{
|
||||
IconTexture = InIconTexture;
|
||||
if (IconImage)
|
||||
{
|
||||
IconImage->SetBrushFromTexture(IconTexture);
|
||||
}
|
||||
}
|
||||
493
Source/PHYInventory/Private/UI/ItemStacks/ItemStackContainer.cpp
Normal file
493
Source/PHYInventory/Private/UI/ItemStacks/ItemStackContainer.cpp
Normal file
@@ -0,0 +1,493 @@
|
||||
//
|
||||
|
||||
|
||||
#include "UI/ItemStacks/ItemStackContainer.h"
|
||||
|
||||
#include "CommonTabListWidgetBase.h"
|
||||
#include "GIS_InventoryFunctionLibrary.h"
|
||||
#include "GIS_InventorySystemComponent.h"
|
||||
#include "GIS_ItemCollection.h"
|
||||
#include "GIS_ItemSlotCollection.h"
|
||||
#include "ItemFilterInterface.h"
|
||||
#include "Async/GIS_AsyncAction_WaitInventorySystem.h"
|
||||
#include "Components/ListView.h"
|
||||
|
||||
void UItemStackContainer::SetOwningActor_Implementation(AActor* NewOwningActor)
|
||||
{
|
||||
OwningActor = NewOwningActor;
|
||||
}
|
||||
|
||||
void UItemStackContainer::OnDeactivated_Implementation()
|
||||
{
|
||||
IGUIS_UserWidgetInterface::OnDeactivated_Implementation();
|
||||
}
|
||||
|
||||
void UItemStackContainer::HandleTabSelected(const FName TabId)
|
||||
{
|
||||
ApplyFilter(TabId);
|
||||
}
|
||||
|
||||
void UItemStackContainer::HandleInventorySystemInitialized()
|
||||
{
|
||||
WaitInventorySystem = nullptr;
|
||||
if (OwningActor.IsValid())
|
||||
{
|
||||
InventorySystemComponent = UGIS_InventorySystemComponent::GetInventorySystemComponent(OwningActor.Get());
|
||||
if (InventorySystemComponent.IsValid())
|
||||
{
|
||||
// 订阅库存系统的消息
|
||||
InventorySystemComponent.Get()->OnInventoryStackUpdate.AddDynamic(this,&UItemStackContainer::HandleInventoryStackChanged);
|
||||
}
|
||||
if (FilterTabsReference)
|
||||
{
|
||||
FilterTabsReference.Get()->OnTabSelected.AddDynamic(this,&UItemStackContainer::HandleTabSelected);
|
||||
}
|
||||
// 创建默认的item data槽位
|
||||
CreateDefaultItemDataSlots();
|
||||
// 同步数据
|
||||
SyncAll();
|
||||
}
|
||||
}
|
||||
|
||||
void UItemStackContainer::CreateDefaultItemDataSlots()
|
||||
{
|
||||
if (!InventorySystemComponent.IsValid()) return;
|
||||
UGIS_ItemCollection* OutCollection;
|
||||
// 从库存系统中获取集合,并添加到item data中
|
||||
if (InventorySystemComponent->FindTypedCollectionByTag(CollectionToTrackTag,UGIS_ItemSlotCollection::StaticClass(),OutCollection))
|
||||
{
|
||||
if (UGIS_ItemSlotCollection* SlotCollection = Cast<UGIS_ItemSlotCollection>(OutCollection))
|
||||
{
|
||||
const TArray<FGIS_ItemSlotDefinition>& Definitions = SlotCollection->GetMyDefinition()->GetSlotDefinitions();
|
||||
for (int i = 0; i < Definitions.Num(); ++i)
|
||||
{
|
||||
SetOrAddItemInfoToItemDataArray(FGIS_ItemInfo(),i);
|
||||
}
|
||||
for (int i = 0; i < ItemDataArray.Num(); ++i)
|
||||
{
|
||||
ItemDataArray[i]->SetItemSlotDefinition(Definitions[i]);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
// 如果没有找到slot collection,则创建默认数量的item data槽位
|
||||
if (bCreateDefaultItemDataSlots)
|
||||
{
|
||||
for (int i = 0; i < DefaultItemDataSlotCount; ++i)
|
||||
{
|
||||
SetOrAddItemInfoToItemDataArray(FGIS_ItemInfo(),i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UItemStackContainer::OnActivated_Implementation()
|
||||
{
|
||||
// 注册list view events
|
||||
RegisterListViewEvents();
|
||||
OwningActor = Execute_GetOwningActor(this);
|
||||
if (OwningActor.IsValid())
|
||||
{
|
||||
WaitInventorySystem = UGIS_AsyncAction_WaitInventorySystemInitialized::WaitInventorySystemInitialized(this,OwningActor.Get());
|
||||
if (WaitInventorySystem)
|
||||
{
|
||||
WaitInventorySystem->OnCompleted.AddDynamic(this,&UItemStackContainer::HandleInventorySystemInitialized);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
AActor* UItemStackContainer::GetOwningActor_Implementation()
|
||||
{
|
||||
if (!OwningActor.IsValid())
|
||||
{
|
||||
if (GetOwningPlayerPawn())
|
||||
{
|
||||
OwningActor = GetOwningPlayerPawn();
|
||||
}
|
||||
else
|
||||
{
|
||||
OwningActor = GetOwningPlayer();
|
||||
}
|
||||
}
|
||||
return OwningActor.Get();
|
||||
}
|
||||
|
||||
void UItemStackContainer::AddFilterObject(UObject* InFilterObject)
|
||||
{
|
||||
if (InFilterObject && InFilterObject->Implements<UItemFilterInterface>())
|
||||
{
|
||||
ItemFilterObjects.AddUnique(InFilterObject);
|
||||
}
|
||||
}
|
||||
|
||||
void UItemStackContainer::RemoveFilterObject(UObject* InFilterObject)
|
||||
{
|
||||
if (InFilterObject && InFilterObject->Implements<UItemFilterInterface>())
|
||||
{
|
||||
ItemFilterObjects.Remove(InFilterObject);
|
||||
}
|
||||
}
|
||||
|
||||
void UItemStackContainer::SetSelectedIndexForNewItem(const int32 NewSelectedIndex)
|
||||
{
|
||||
SelectedIndexForNewItem = NewSelectedIndex;
|
||||
}
|
||||
|
||||
bool UItemStackContainer::SwapItemDataSlots(const int32 IndexA, const int32 IndexB)
|
||||
{
|
||||
if (!CanMoveItem(IndexA,this,IndexB)) return false;
|
||||
if (ItemDataArray.IsValidIndex(IndexA) && ItemDataArray.IsValidIndex(IndexB))
|
||||
{
|
||||
// 缓存A的数据
|
||||
const UItemData* TempItemData = ItemDataArray[IndexA];
|
||||
const UItemData* TargetItemData = ItemDataArray[IndexB];
|
||||
// 缓存其中的数据
|
||||
TOptional<FGIS_ItemInfo> TempItemInfo = TempItemData ? TOptional<FGIS_ItemInfo>(TempItemData->GetItemInfo()) : TOptional<FGIS_ItemInfo>();
|
||||
// 交换数据
|
||||
if (TOptional<FGIS_ItemInfo> TempTargetItemInfo = TargetItemData ? TOptional<FGIS_ItemInfo>(TargetItemData->GetItemInfo()) : TOptional<FGIS_ItemInfo>(); TempTargetItemInfo.IsSet())
|
||||
{
|
||||
AssignItemInfoToItemData(TempTargetItemInfo.GetValue(), IndexA);
|
||||
}
|
||||
if (TempItemInfo.IsSet())
|
||||
{
|
||||
AssignItemInfoToItemData(TempItemInfo.GetValue(), IndexB);
|
||||
}
|
||||
LastDropIndex = IndexB;
|
||||
// 同步list view
|
||||
SyncItemDataToListView();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool UItemStackContainer::SwapItemDataToOtherContainer(const int32 SourceIndex, UItemStackContainer* TargetContainer,
|
||||
const int32 TargetIndex)
|
||||
{
|
||||
if (!TargetContainer) return false;
|
||||
if (!CanMoveItem(SourceIndex,TargetContainer,TargetIndex)) return false;
|
||||
if (ItemDataArray.IsValidIndex(SourceIndex) && TargetContainer->GetItemDataArray().IsValidIndex(TargetIndex))
|
||||
{
|
||||
// 缓存A的数据
|
||||
const UItemData* TempItemData = ItemDataArray[SourceIndex];
|
||||
const UItemData* TargetItemData = TargetContainer->GetItemDataArray()[TargetIndex];
|
||||
// 缓存其中的数据
|
||||
TOptional<FGIS_ItemInfo> TempItemInfo = TempItemData ? TOptional<FGIS_ItemInfo>(TempItemData->GetItemInfo()) : TOptional<FGIS_ItemInfo>();
|
||||
if (TOptional<FGIS_ItemInfo> TempTargetItemInfo = TargetItemData ? TOptional<FGIS_ItemInfo>(TargetItemData->GetItemInfo()) : TOptional<FGIS_ItemInfo>(); TempTargetItemInfo.IsSet())
|
||||
{
|
||||
AssignItemInfoToItemData(TempTargetItemInfo.GetValue(), SourceIndex);
|
||||
}
|
||||
if (TempItemInfo.IsSet())
|
||||
{
|
||||
TargetContainer->AssignItemInfoToItemData(TempItemInfo.GetValue(), TargetIndex);
|
||||
}
|
||||
TargetContainer->LastDropIndex = TargetIndex;
|
||||
// 同步list view
|
||||
SyncItemDataToListView();
|
||||
TargetContainer->SyncItemDataToListView();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool UItemStackContainer::CanMoveItem(const int32 IndexA,UItemStackContainer* TargetContainer,const int32 IndexB)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
int32 UItemStackContainer::FindItemSlotIndex(const UItemData* InItemData) const
|
||||
{
|
||||
if (!InItemData) return INDEX_NONE;
|
||||
return ItemDataArray.IndexOfByKey(InItemData);
|
||||
}
|
||||
|
||||
void UItemStackContainer::AssignItemInfoToItemData(const FGIS_ItemInfo& InInfo, const int32 SlotIndex)
|
||||
{
|
||||
if (ItemDataArray.IsValidIndex(SlotIndex))
|
||||
{
|
||||
if (UItemData* ItemData = ItemDataArray[SlotIndex])
|
||||
{
|
||||
ItemData->SetItemInfo(InInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UItemStackContainer::UnassignItemInfoToItemData(const FGIS_ItemInfo& InInfo)
|
||||
{
|
||||
if (const int32 Index = FindItemIndexFromDataByStackID(InInfo.StackId); Index != INDEX_NONE)
|
||||
{
|
||||
const FGIS_ItemInfo EmptyInfo;
|
||||
AssignItemInfoToItemData(EmptyInfo, Index);
|
||||
}
|
||||
}
|
||||
|
||||
int32 UItemStackContainer::FindItemIndexFromDataByStackID(const FGuid& StackID) const
|
||||
{
|
||||
for (int32 Index = 0; Index < ItemDataArray.Num(); ++Index)
|
||||
{
|
||||
if (const UItemData* ItemData = ItemDataArray[Index])
|
||||
{
|
||||
if (ItemData->GetItemInfo().StackId == StackID)
|
||||
{
|
||||
return Index;
|
||||
}
|
||||
}
|
||||
}
|
||||
return INDEX_NONE;
|
||||
}
|
||||
|
||||
TArray<FGIS_ItemInfo> UItemStackContainer::GetItemInfoArrayFromInventorySystem()
|
||||
{
|
||||
TArray<FGIS_ItemInfo> LocalItemInfos;
|
||||
if (InventorySystemComponent.IsValid())
|
||||
{
|
||||
if (CollectionToTrackTag.IsValid())
|
||||
{
|
||||
if (const UGIS_ItemCollection* ItemCollection = InventorySystemComponent.Get()->GetCollectionByTag(CollectionToTrackTag))
|
||||
{
|
||||
LocalItemInfos = ItemCollection->GetAllItemInfos();
|
||||
}
|
||||
}
|
||||
}
|
||||
if (LocalItemInfos.IsEmpty()) return LocalItemInfos;
|
||||
// 通过配置的name,和query 过滤
|
||||
if (ItemFilterMap.Num() > 0 && ItemFilterName.IsValid())
|
||||
{
|
||||
if (const FGameplayTagQuery* Query = ItemFilterMap.Find(ItemFilterName))
|
||||
{
|
||||
LocalItemInfos = UGIS_InventoryFunctionLibrary::FilterItemInfosByTagQuery(LocalItemInfos,*Query);
|
||||
}
|
||||
}
|
||||
// 通过Object过滤
|
||||
if (ItemFilterObjects.Num() > 0)
|
||||
{
|
||||
for (UObject* FilterObject : ItemFilterObjects)
|
||||
{
|
||||
if (FilterObject && FilterObject->Implements<UItemFilterInterface>())
|
||||
{
|
||||
LocalItemInfos = IItemFilterInterface::Execute_FilterItemInfo(FilterObject,LocalItemInfos);
|
||||
}
|
||||
}
|
||||
}
|
||||
return LocalItemInfos;
|
||||
}
|
||||
|
||||
void UItemStackContainer::ResetItemDataArray()
|
||||
{
|
||||
for (UItemData* ItemData : ItemDataArray)
|
||||
{
|
||||
ItemData->Reset();
|
||||
}
|
||||
}
|
||||
|
||||
void UItemStackContainer::SetOrAddItemInfoToItemDataArray(const FGIS_ItemInfo& InInfo, const int32 SlotIndex)
|
||||
{
|
||||
if (SlotIndex < 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
// 确保数组大小足够
|
||||
if (ItemDataArray.Num() <= SlotIndex)
|
||||
{
|
||||
ItemDataArray.SetNum(SlotIndex + 1);
|
||||
}
|
||||
|
||||
// 确保该槽位有对象
|
||||
UItemData* ItemDataRef = ItemDataArray[SlotIndex];
|
||||
if (!ItemDataRef)
|
||||
{
|
||||
ItemDataRef = NewObject<UItemData>(this);
|
||||
}
|
||||
ItemDataRef->SetItemInfo(InInfo);
|
||||
}
|
||||
|
||||
void UItemStackContainer::SyncItemInfoToItemDataArray(const TArray<FGIS_ItemInfo>& InItemInfos)
|
||||
{
|
||||
if (InventorySystemComponent.IsValid() && CollectionToTrackTag.IsValid())
|
||||
{
|
||||
// 获取集合,如果是slotted collection则根据slot来赋值
|
||||
if (const UGIS_ItemSlotCollection* SlotCollection = Cast<UGIS_ItemSlotCollection>(InventorySystemComponent->GetCollectionByTag(CollectionToTrackTag)))
|
||||
{
|
||||
for (int i = 0; i < InItemInfos.Num(); ++i)
|
||||
{
|
||||
const FGIS_ItemInfo& Info = InItemInfos[i];
|
||||
const int32 SlotIndex = SlotCollection->GetItemSlotIndex(Info.Item);
|
||||
SetOrAddItemInfoToItemDataArray(Info, SlotIndex == INDEX_NONE ? i : SlotIndex);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// 如果是普通的集合,则直接按顺序赋值
|
||||
for (int i = 0; i < InItemInfos.Num(); ++i)
|
||||
{
|
||||
SetOrAddItemInfoToItemDataArray(InItemInfos[i], i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UItemStackContainer::SyncItemDataToListView()
|
||||
{
|
||||
if (ItemListView == nullptr) return;
|
||||
ItemListView->ClearSelection();
|
||||
ItemListView->ClearListItems();
|
||||
ItemListView->SetListItems(ItemDataArray);
|
||||
ItemListView->RegenerateAllEntries();
|
||||
// 恢复上次拖拽的选中状态
|
||||
if (ItemDataArray.IsValidIndex(LastDropIndex) && LastDropIndex != INDEX_NONE)
|
||||
{
|
||||
ItemListView->SetSelectedItem(ItemDataArray[LastDropIndex]);
|
||||
LastDropIndex = INDEX_NONE;
|
||||
} else
|
||||
{
|
||||
// 默认选中第一个
|
||||
if (ItemDataArray.IsValidIndex(0) && ItemDataArray[0]->IsValidItem())
|
||||
{
|
||||
ItemListView->SetSelectedItem(ItemDataArray[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int32 UItemStackContainer::FindSuitableItemDataSlotForNewItem()
|
||||
{
|
||||
if (SelectedIndexForNewItem == INDEX_NONE)
|
||||
{
|
||||
for (int i = 0; i < ItemDataArray.Num(); ++i)
|
||||
{
|
||||
if (const UItemData* ItemData = ItemDataArray[i])
|
||||
{
|
||||
if (!ItemData->IsValidItem())
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
}
|
||||
return ItemDataArray.Num();
|
||||
}
|
||||
const int32 ReturnIndex = SelectedIndexForNewItem;
|
||||
SelectedIndexForNewItem = INDEX_NONE;
|
||||
return ReturnIndex;
|
||||
}
|
||||
|
||||
void UItemStackContainer::SyncAll()
|
||||
{
|
||||
ResetItemDataArray();
|
||||
SyncItemInfoToItemDataArray(GetItemInfoArrayFromInventorySystem());
|
||||
SyncItemDataToListView();
|
||||
}
|
||||
|
||||
void UItemStackContainer::ApplyFilter(const FName& InFilterName)
|
||||
{
|
||||
if (InFilterName.IsNone()) return;
|
||||
if (ItemFilterMap.Contains(InFilterName))
|
||||
{
|
||||
ItemFilterName = InFilterName;
|
||||
SyncAll();
|
||||
}
|
||||
}
|
||||
|
||||
void UItemStackContainer::HandleItemSelectionChanged(UObject* Object) const
|
||||
{
|
||||
if (UItemData* SelectedItemData = Cast<UItemData>(Object))
|
||||
{
|
||||
OnItemSelectionChanged.Broadcast(SelectedItemData,true);
|
||||
return;
|
||||
}
|
||||
OnItemSelectionChanged.Broadcast(nullptr,false);
|
||||
}
|
||||
|
||||
void UItemStackContainer::HandleItemHoveredChanged(UObject* Object, bool bIsHovered) const
|
||||
{
|
||||
if (UItemData* HoveredItemData = Cast<UItemData>(Object))
|
||||
{
|
||||
OnItemHoveredChanged.Broadcast(HoveredItemData,bIsHovered);
|
||||
}
|
||||
}
|
||||
|
||||
void UItemStackContainer::HandleItemClicked(UObject* Object) const
|
||||
{
|
||||
if (UItemData* ClickedItemData = Cast<UItemData>(Object))
|
||||
{
|
||||
OnItemClicked.Broadcast(ClickedItemData);
|
||||
}
|
||||
}
|
||||
|
||||
void UItemStackContainer::HandleListViewEntryWidgetGenerated(UUserWidget& UserWidget) const
|
||||
{
|
||||
OnItemEntryGenerated.Broadcast(&UserWidget);
|
||||
}
|
||||
|
||||
void UItemStackContainer::HandleItemDoubleClicked(UObject* Object) const
|
||||
{
|
||||
if (UItemData* ClickedItemData = Cast<UItemData>(Object))
|
||||
{
|
||||
OnItemDoubleClicked.Broadcast(ClickedItemData);
|
||||
}
|
||||
}
|
||||
|
||||
void UItemStackContainer::RegisterListViewEvents()
|
||||
{
|
||||
if (ItemListView)
|
||||
{
|
||||
// 注册事件
|
||||
ItemSelectionChangedHandle = ItemListView->OnItemSelectionChanged().AddUObject(this, &UItemStackContainer::HandleItemSelectionChanged);
|
||||
ItemHoveredChangedHandle = ItemListView->OnItemIsHoveredChanged().AddUObject(this, &UItemStackContainer::HandleItemHoveredChanged);
|
||||
ItemClickedHandle = ItemListView->OnItemClicked().AddUObject(this, &UItemStackContainer::HandleItemClicked);
|
||||
ListViewEntryWidgetGeneratedHandle = ItemListView->OnEntryWidgetGenerated().AddUObject(this, &UItemStackContainer::HandleListViewEntryWidgetGenerated);
|
||||
ItemDoubleClickedHandle = ItemListView->OnItemDoubleClicked().AddUObject(this, &UItemStackContainer::HandleItemDoubleClicked);
|
||||
}
|
||||
}
|
||||
|
||||
void UItemStackContainer::UnregisterListViewEvents() const
|
||||
{
|
||||
if (ItemListView)
|
||||
{
|
||||
// 注销事件
|
||||
ItemListView->OnItemSelectionChanged().Remove(ItemSelectionChangedHandle);
|
||||
ItemListView->OnItemIsHoveredChanged().Remove(ItemHoveredChangedHandle);
|
||||
ItemListView->OnItemClicked().Remove(ItemClickedHandle);
|
||||
ItemListView->OnEntryWidgetGenerated().Remove(ListViewEntryWidgetGeneratedHandle);
|
||||
ItemListView->OnItemDoubleClicked().Remove(ItemDoubleClickedHandle);
|
||||
}
|
||||
}
|
||||
|
||||
void UItemStackContainer::HandleInventoryStackChanged(const FGIS_InventoryStackUpdateMessage& Message)
|
||||
{
|
||||
if (const UGIS_InventorySystemComponent* Inventory = Message.Inventory)
|
||||
{
|
||||
// 仅处理关联的库存系统组件的消息
|
||||
if (!InventorySystemComponent.IsValid() && Inventory != InventorySystemComponent.Get()) return;
|
||||
// 获取集合
|
||||
if (UGIS_ItemCollection* ItemCollection = Inventory->GetCollectionById(Message.CollectionId))
|
||||
{
|
||||
// 仅处理配置的集合tag的消息
|
||||
if (ItemCollection->GetCollectionTag() != CollectionToTrackTag) return;
|
||||
FGIS_ItemInfo ChangedItemInfo = FGIS_ItemInfo(Message.Instance,Message.NewCount,ItemCollection,Message.StackId);
|
||||
switch (Message.ChangeType) {
|
||||
case EGIS_ItemStackChangeType::WasAdded:
|
||||
// 添加
|
||||
{
|
||||
const int32 SlotIndex = FindSuitableItemDataSlotForNewItem();
|
||||
SetOrAddItemInfoToItemDataArray(ChangedItemInfo, SlotIndex);
|
||||
}
|
||||
break;
|
||||
case EGIS_ItemStackChangeType::WasRemoved:
|
||||
// 移除
|
||||
{
|
||||
ChangedItemInfo.Amount = Message.NewCount - Message.Delta;
|
||||
UnassignItemInfoToItemData(ChangedItemInfo);
|
||||
}
|
||||
break;
|
||||
case EGIS_ItemStackChangeType::Changed:
|
||||
// 修改
|
||||
{
|
||||
if (const int32 ChangedIndex = FindItemIndexFromDataByStackID(Message.StackId); ChangedIndex != INDEX_NONE)
|
||||
{
|
||||
SetOrAddItemInfoToItemDataArray(ChangedItemInfo, ChangedIndex);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
SyncItemDataToListView();
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user