Apply class attribute allocations
This commit is contained in:
@@ -45,10 +45,11 @@ void APHYAICharacter::InitializeAIAbilitySystem()
|
||||
if (AbilitySystemComponent)
|
||||
{
|
||||
InitializeAbilitySystem(AbilitySystemComponent, this);
|
||||
}
|
||||
|
||||
if (ClassComponent)
|
||||
{
|
||||
ClassComponent->ApplyConfiguredPlayerMeshIfAllowed(this);
|
||||
if (ClassComponent)
|
||||
{
|
||||
ClassComponent->ApplyClassAttributesToAbilitySystem(AbilitySystemComponent);
|
||||
ClassComponent->ApplyConfiguredPlayerMeshIfAllowed(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,10 +51,11 @@ void APHYPlayerCharacter::InitializeAbilitySystemFromPlayerState()
|
||||
if (UAbilitySystemComponent* PlayerASC = PHYPlayerState->GetAbilitySystemComponent())
|
||||
{
|
||||
InitializeAbilitySystem(PlayerASC, PHYPlayerState);
|
||||
}
|
||||
|
||||
if (UPHYClassComponent* ClassComponent = PHYPlayerState->GetClassComponent())
|
||||
{
|
||||
ClassComponent->ApplyConfiguredPlayerMeshIfAllowed(this);
|
||||
if (UPHYClassComponent* ClassComponent = PHYPlayerState->GetClassComponent())
|
||||
{
|
||||
ClassComponent->ApplyClassAttributesToAbilitySystem(PlayerASC);
|
||||
ClassComponent->ApplyConfiguredPlayerMeshIfAllowed(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,11 @@
|
||||
|
||||
#include UE_INLINE_GENERATED_CPP_BY_NAME(PHYClassComponent)
|
||||
|
||||
#include "AbilitySystemComponent.h"
|
||||
#include "AbilitySystem/Attributes/PHYAttributeCalculationLibrary.h"
|
||||
#include "AbilitySystem/Attributes/PHYCombatAttributeSet.h"
|
||||
#include "AbilitySystem/Attributes/PHYCoreAttributeSet.h"
|
||||
#include "AbilitySystem/Attributes/PHYElementAttributeSet.h"
|
||||
#include "Characters/PHYAICharacter.h"
|
||||
#include "Characters/PHYPlayerCharacter.h"
|
||||
#include "Class/PHYClassSettings.h"
|
||||
@@ -110,3 +115,104 @@ bool UPHYClassComponent::ApplyConfiguredPlayerMeshIfAllowed(AActor* AvatarActor)
|
||||
Character->GetMesh()->SetSkeletalMesh(PlayerMesh);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool UPHYClassComponent::ApplyClassAttributesToAbilitySystem(UAbilitySystemComponent* AbilitySystemComponent) const
|
||||
{
|
||||
if (!AbilitySystemComponent)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const AActor* AuthorityActor = GetOwner() ? GetOwner() : AbilitySystemComponent->GetOwnerActor();
|
||||
if (!AuthorityActor || !AuthorityActor->HasAuthority())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const UPHYClassSettings* ClassSettings = GetDefault<UPHYClassSettings>();
|
||||
if (!ClassSettings)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
constexpr int32 RequiredTotalPoints = 60;
|
||||
const FPHYClassCoreAttributeAllocation* Allocation = ClassSettings->FindCoreAttributeAllocation(CurrentClassTag);
|
||||
if (!Allocation || !Allocation->IsValidTotal(RequiredTotalPoints))
|
||||
{
|
||||
UE_LOG(LogTemp, Warning, TEXT("PHYClassComponent: Class %s has no valid %d-point core attribute allocation. Falling back to DefaultPlayerClassTag."),
|
||||
*CurrentClassTag.ToString(), RequiredTotalPoints);
|
||||
Allocation = ClassSettings->FindCoreAttributeAllocation(ClassSettings->DefaultPlayerClassTag);
|
||||
}
|
||||
|
||||
if (!Allocation || !Allocation->IsValidTotal(RequiredTotalPoints))
|
||||
{
|
||||
UE_LOG(LogTemp, Warning, TEXT("PHYClassComponent: DefaultPlayerClassTag allocation is missing or invalid. Falling back to Class.Saber."));
|
||||
Allocation = ClassSettings->FindCoreAttributeAllocation(PHYGameplayTags::Class_Saber);
|
||||
}
|
||||
|
||||
if (!Allocation || !Allocation->IsValidTotal(RequiredTotalPoints))
|
||||
{
|
||||
UE_LOG(LogTemp, Warning, TEXT("PHYClassComponent: Unable to apply class attributes because no valid fallback allocation exists."));
|
||||
return false;
|
||||
}
|
||||
|
||||
FPHYCoreAttributeSnapshot Snapshot;
|
||||
Snapshot.Strength = Allocation->Strength;
|
||||
Snapshot.Dexterity = Allocation->Dexterity;
|
||||
Snapshot.Vitality = Allocation->Vitality;
|
||||
Snapshot.Intelligence = Allocation->Intelligence;
|
||||
Snapshot.Spirit = Allocation->Spirit;
|
||||
Snapshot.Perception = Allocation->Perception;
|
||||
|
||||
const FPHYDerivedCombatAttributes CombatAttributes = UPHYAttributeCalculationLibrary::CalculateCombatAttributes(Snapshot);
|
||||
const FPHYDerivedElementAttributes ElementAttributes = UPHYAttributeCalculationLibrary::CalculateElementAttributes(Snapshot);
|
||||
|
||||
// 直接设置 ASC 属性基值,重复调用只会覆盖当前职业结果,不会叠加。
|
||||
AbilitySystemComponent->SetNumericAttributeBase(UPHYCoreAttributeSet::GetStrengthAttribute(), Snapshot.Strength);
|
||||
AbilitySystemComponent->SetNumericAttributeBase(UPHYCoreAttributeSet::GetDexterityAttribute(), Snapshot.Dexterity);
|
||||
AbilitySystemComponent->SetNumericAttributeBase(UPHYCoreAttributeSet::GetVitalityAttribute(), Snapshot.Vitality);
|
||||
AbilitySystemComponent->SetNumericAttributeBase(UPHYCoreAttributeSet::GetIntelligenceAttribute(), Snapshot.Intelligence);
|
||||
AbilitySystemComponent->SetNumericAttributeBase(UPHYCoreAttributeSet::GetSpiritAttribute(), Snapshot.Spirit);
|
||||
AbilitySystemComponent->SetNumericAttributeBase(UPHYCoreAttributeSet::GetPerceptionAttribute(), Snapshot.Perception);
|
||||
|
||||
AbilitySystemComponent->SetNumericAttributeBase(UPHYCombatAttributeSet::GetMaxHealthAttribute(), CombatAttributes.MaxHealth);
|
||||
AbilitySystemComponent->SetNumericAttributeBase(UPHYCombatAttributeSet::GetHealthAttribute(), CombatAttributes.MaxHealth);
|
||||
AbilitySystemComponent->SetNumericAttributeBase(UPHYCombatAttributeSet::GetMaxManaAttribute(), CombatAttributes.MaxMana);
|
||||
AbilitySystemComponent->SetNumericAttributeBase(UPHYCombatAttributeSet::GetManaAttribute(), CombatAttributes.MaxMana);
|
||||
AbilitySystemComponent->SetNumericAttributeBase(UPHYCombatAttributeSet::GetMaxStaminaAttribute(), CombatAttributes.MaxStamina);
|
||||
AbilitySystemComponent->SetNumericAttributeBase(UPHYCombatAttributeSet::GetStaminaAttribute(), CombatAttributes.MaxStamina);
|
||||
AbilitySystemComponent->SetNumericAttributeBase(UPHYCombatAttributeSet::GetPhysicalAttackPowerAttribute(), CombatAttributes.PhysicalAttackPower);
|
||||
AbilitySystemComponent->SetNumericAttributeBase(UPHYCombatAttributeSet::GetSpellPowerAttribute(), CombatAttributes.SpellPower);
|
||||
AbilitySystemComponent->SetNumericAttributeBase(UPHYCombatAttributeSet::GetArmorAttribute(), CombatAttributes.Armor);
|
||||
AbilitySystemComponent->SetNumericAttributeBase(UPHYCombatAttributeSet::GetMagicResistanceAttribute(), CombatAttributes.MagicResistance);
|
||||
AbilitySystemComponent->SetNumericAttributeBase(UPHYCombatAttributeSet::GetAccuracyAttribute(), CombatAttributes.Accuracy);
|
||||
AbilitySystemComponent->SetNumericAttributeBase(UPHYCombatAttributeSet::GetEvasionAttribute(), CombatAttributes.Evasion);
|
||||
AbilitySystemComponent->SetNumericAttributeBase(UPHYCombatAttributeSet::GetCriticalChanceAttribute(), CombatAttributes.CriticalChance);
|
||||
AbilitySystemComponent->SetNumericAttributeBase(UPHYCombatAttributeSet::GetCriticalDamageAttribute(), CombatAttributes.CriticalDamage);
|
||||
AbilitySystemComponent->SetNumericAttributeBase(UPHYCombatAttributeSet::GetAttackSpeedAttribute(), CombatAttributes.AttackSpeed);
|
||||
AbilitySystemComponent->SetNumericAttributeBase(UPHYCombatAttributeSet::GetCooldownReductionAttribute(), CombatAttributes.CooldownReduction);
|
||||
AbilitySystemComponent->SetNumericAttributeBase(UPHYCombatAttributeSet::GetBlockPowerAttribute(), CombatAttributes.BlockPower);
|
||||
AbilitySystemComponent->SetNumericAttributeBase(UPHYCombatAttributeSet::GetGuardBreakPowerAttribute(), CombatAttributes.GuardBreakPower);
|
||||
AbilitySystemComponent->SetNumericAttributeBase(UPHYCombatAttributeSet::GetPoiseAttribute(), CombatAttributes.Poise);
|
||||
AbilitySystemComponent->SetNumericAttributeBase(UPHYCombatAttributeSet::GetPoiseDamageAttribute(), CombatAttributes.PoiseDamage);
|
||||
|
||||
AbilitySystemComponent->SetNumericAttributeBase(UPHYElementAttributeSet::GetFireDamageBonusAttribute(), ElementAttributes.DamageBonus);
|
||||
AbilitySystemComponent->SetNumericAttributeBase(UPHYElementAttributeSet::GetWaterDamageBonusAttribute(), ElementAttributes.DamageBonus);
|
||||
AbilitySystemComponent->SetNumericAttributeBase(UPHYElementAttributeSet::GetIceDamageBonusAttribute(), ElementAttributes.DamageBonus);
|
||||
AbilitySystemComponent->SetNumericAttributeBase(UPHYElementAttributeSet::GetLightningDamageBonusAttribute(), ElementAttributes.DamageBonus);
|
||||
AbilitySystemComponent->SetNumericAttributeBase(UPHYElementAttributeSet::GetEarthDamageBonusAttribute(), ElementAttributes.DamageBonus);
|
||||
AbilitySystemComponent->SetNumericAttributeBase(UPHYElementAttributeSet::GetWindDamageBonusAttribute(), ElementAttributes.DamageBonus);
|
||||
AbilitySystemComponent->SetNumericAttributeBase(UPHYElementAttributeSet::GetLightDamageBonusAttribute(), ElementAttributes.DamageBonus);
|
||||
AbilitySystemComponent->SetNumericAttributeBase(UPHYElementAttributeSet::GetDarkDamageBonusAttribute(), ElementAttributes.DamageBonus);
|
||||
AbilitySystemComponent->SetNumericAttributeBase(UPHYElementAttributeSet::GetFireResistanceAttribute(), ElementAttributes.Resistance);
|
||||
AbilitySystemComponent->SetNumericAttributeBase(UPHYElementAttributeSet::GetWaterResistanceAttribute(), ElementAttributes.Resistance);
|
||||
AbilitySystemComponent->SetNumericAttributeBase(UPHYElementAttributeSet::GetIceResistanceAttribute(), ElementAttributes.Resistance);
|
||||
AbilitySystemComponent->SetNumericAttributeBase(UPHYElementAttributeSet::GetLightningResistanceAttribute(), ElementAttributes.Resistance);
|
||||
AbilitySystemComponent->SetNumericAttributeBase(UPHYElementAttributeSet::GetEarthResistanceAttribute(), ElementAttributes.Resistance);
|
||||
AbilitySystemComponent->SetNumericAttributeBase(UPHYElementAttributeSet::GetWindResistanceAttribute(), ElementAttributes.Resistance);
|
||||
AbilitySystemComponent->SetNumericAttributeBase(UPHYElementAttributeSet::GetLightResistanceAttribute(), ElementAttributes.Resistance);
|
||||
AbilitySystemComponent->SetNumericAttributeBase(UPHYElementAttributeSet::GetDarkResistanceAttribute(), ElementAttributes.Resistance);
|
||||
AbilitySystemComponent->SetNumericAttributeBase(UPHYElementAttributeSet::GetElementPenetrationAttribute(), ElementAttributes.Penetration);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -6,6 +6,16 @@
|
||||
|
||||
#include "PHYGameplayTags.h"
|
||||
|
||||
int32 FPHYClassCoreAttributeAllocation::GetTotalPoints() const
|
||||
{
|
||||
return Strength + Dexterity + Vitality + Intelligence + Spirit + Perception;
|
||||
}
|
||||
|
||||
bool FPHYClassCoreAttributeAllocation::IsValidTotal(const int32 RequiredTotal) const
|
||||
{
|
||||
return GetTotalPoints() == RequiredTotal;
|
||||
}
|
||||
|
||||
UPHYClassSettings::UPHYClassSettings()
|
||||
{
|
||||
DefaultPlayerClassTag = PHYGameplayTags::Class_Saber;
|
||||
@@ -21,4 +31,33 @@ UPHYClassSettings::UPHYClassSettings()
|
||||
{ PHYGameplayTags::Class_Assassin, nullptr },
|
||||
{ PHYGameplayTags::Class_Berserker, nullptr }
|
||||
};
|
||||
|
||||
CoreAttributeAllocations =
|
||||
{
|
||||
{ PHYGameplayTags::Class_Saber, 12, 10, 12, 8, 8, 10 },
|
||||
{ PHYGameplayTags::Class_Lancer, 10, 13, 10, 7, 8, 12 },
|
||||
{ PHYGameplayTags::Class_Archer, 8, 12, 8, 8, 10, 14 },
|
||||
{ PHYGameplayTags::Class_Rider, 10, 11, 11, 8, 10, 10 },
|
||||
{ PHYGameplayTags::Class_Caster, 5, 7, 8, 16, 15, 9 },
|
||||
{ PHYGameplayTags::Class_Assassin, 7, 15, 7, 8, 8, 15 },
|
||||
{ PHYGameplayTags::Class_Berserker, 16, 8, 15, 4, 5, 12 }
|
||||
};
|
||||
}
|
||||
|
||||
const FPHYClassCoreAttributeAllocation* UPHYClassSettings::FindCoreAttributeAllocation(const FGameplayTag ClassTag) const
|
||||
{
|
||||
if (!ClassTag.IsValid())
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
for (const FPHYClassCoreAttributeAllocation& Allocation : CoreAttributeAllocations)
|
||||
{
|
||||
if (Allocation.ClassTag == ClassTag)
|
||||
{
|
||||
return &Allocation;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include "PHYClassComponent.generated.h"
|
||||
|
||||
class USkeletalMesh;
|
||||
class UAbilitySystemComponent;
|
||||
|
||||
/**
|
||||
* @brief PHY 职业组件。
|
||||
@@ -46,6 +47,10 @@ public:
|
||||
UFUNCTION(BlueprintCallable, Category="PHY|Class")
|
||||
bool ApplyConfiguredPlayerMeshIfAllowed(AActor* AvatarActor = nullptr) const;
|
||||
|
||||
/** @brief 在服务器上按当前职业把基础属性和派生属性写入 ASC。 */
|
||||
UFUNCTION(BlueprintCallable, Category="PHY|Class|Attributes")
|
||||
bool ApplyClassAttributesToAbilitySystem(UAbilitySystemComponent* AbilitySystemComponent) const;
|
||||
|
||||
protected:
|
||||
/** @brief 当前职业 Tag,由服务器复制给客户端。 */
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Replicated, Category="PHY|Class")
|
||||
|
||||
@@ -10,6 +10,52 @@
|
||||
|
||||
class USkeletalMesh;
|
||||
|
||||
/**
|
||||
* @brief 职业基础六维属性分配。
|
||||
*
|
||||
* 每个职业首期固定总点数为 60,用于初始化 CoreAttributeSet,并继续推导战斗/元素属性。
|
||||
*/
|
||||
USTRUCT(BlueprintType)
|
||||
struct PHY_API FPHYClassCoreAttributeAllocation
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
/** @brief 职业 Tag,必须来自 Class.* 原生 Gameplay Tag。 */
|
||||
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category="PHY|Class|Attributes")
|
||||
FGameplayTag ClassTag;
|
||||
|
||||
/** @brief 力量点数。 */
|
||||
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category="PHY|Class|Attributes")
|
||||
int32 Strength = 0;
|
||||
|
||||
/** @brief 灵巧点数。 */
|
||||
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category="PHY|Class|Attributes")
|
||||
int32 Dexterity = 0;
|
||||
|
||||
/** @brief 体魄点数。 */
|
||||
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category="PHY|Class|Attributes")
|
||||
int32 Vitality = 0;
|
||||
|
||||
/** @brief 智力点数。 */
|
||||
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category="PHY|Class|Attributes")
|
||||
int32 Intelligence = 0;
|
||||
|
||||
/** @brief 精神点数。 */
|
||||
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category="PHY|Class|Attributes")
|
||||
int32 Spirit = 0;
|
||||
|
||||
/** @brief 感知点数。 */
|
||||
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category="PHY|Class|Attributes")
|
||||
int32 Perception = 0;
|
||||
|
||||
/** @brief 返回六维属性总点数。 */
|
||||
int32 GetTotalPoints() const;
|
||||
|
||||
/** @brief 检查六维属性总点数是否等于要求值。 */
|
||||
bool IsValidTotal(int32 RequiredTotal = 60) const;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 玩家职业 Mesh 配置项。
|
||||
*/
|
||||
@@ -54,6 +100,10 @@ public:
|
||||
UPROPERTY(Config, EditDefaultsOnly, Category="PHY|Class")
|
||||
TArray<FPHYPlayerClassMeshConfig> PlayerClassMeshes;
|
||||
|
||||
/** @brief 职业基础属性分配表,首期每个职业总点数必须为 60。 */
|
||||
UPROPERTY(Config, EditDefaultsOnly, Category="PHY|Class|Attributes")
|
||||
TArray<FPHYClassCoreAttributeAllocation> CoreAttributeAllocations;
|
||||
|
||||
/** @brief 是否允许玩家 Avatar 应用玩家职业 Mesh。 */
|
||||
UPROPERTY(Config, EditDefaultsOnly, Category="PHY|Class")
|
||||
bool bApplyPlayerClassMeshToPlayers = true;
|
||||
@@ -61,4 +111,7 @@ public:
|
||||
/** @brief 是否允许 AI 应用玩家职业 Mesh,默认关闭以保持 AI 外观独立。 */
|
||||
UPROPERTY(Config, EditDefaultsOnly, Category="PHY|Class")
|
||||
bool bApplyPlayerClassMeshToAI = false;
|
||||
|
||||
/** @brief 按职业 Tag 查找基础属性分配,未配置时返回 nullptr。 */
|
||||
const FPHYClassCoreAttributeAllocation* FindCoreAttributeAllocation(FGameplayTag ClassTag) const;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user