192 lines
		
	
	
		
			5.5 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			192 lines
		
	
	
		
			5.5 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| #include "CharacterStatsComponent.h"
 | |
| #include "SkillTreeComponent.h"
 | |
| #include "GameFramework/Character.h"
 | |
| #include "GameFramework/CharacterMovementComponent.h"
 | |
| 
 | |
| UCharacterStatsComponent::UCharacterStatsComponent()
 | |
| {
 | |
|     PrimaryComponentTick.bCanEverTick = false;
 | |
| 
 | |
|     // Default base stats
 | |
|     BaseMaxHealth = 100.0f;
 | |
|     BaseDamage = 10.0f;
 | |
|     BaseSpeed = 600.0f;
 | |
| 
 | |
|     // Initialize current values
 | |
|     CurrentHealth = BaseMaxHealth;
 | |
|     MaxHealth = BaseMaxHealth;
 | |
|     CurrentDamage = BaseDamage;
 | |
|     CurrentSpeed = BaseSpeed;
 | |
| 
 | |
|     // Initialize bonuses to 0
 | |
|     SkillHealthBonus = 0.0f;
 | |
|     SkillDamageBonus = 0.0f;
 | |
|     SkillSpeedBonus = 0.0f;
 | |
|     SkillHealthBonusPercent = 0.0f;
 | |
|     SkillDamageBonusPercent = 0.0f;
 | |
|     SkillSpeedBonusPercent = 0.0f;
 | |
| }
 | |
| 
 | |
| void UCharacterStatsComponent::BeginPlay()
 | |
| {
 | |
|     Super::BeginPlay();
 | |
| 
 | |
|     // Find the skill tree component on the same actor
 | |
|     AActor* Owner = GetOwner();
 | |
|     if (Owner)
 | |
|     {
 | |
|         OwnerSkillTree = Owner->FindComponentByClass<USkillTreeComponent>();
 | |
| 
 | |
|         // Bind to skill tree events
 | |
|         if (OwnerSkillTree)
 | |
|         {
 | |
|             OwnerSkillTree->OnSkillPurchased.AddDynamic(this, &UCharacterStatsComponent::OnSkillPurchased);
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     InitializeStats();
 | |
| }
 | |
| 
 | |
| void UCharacterStatsComponent::InitializeStats()
 | |
| {
 | |
|     MaxHealth = BaseMaxHealth;
 | |
|     CurrentHealth = MaxHealth;
 | |
|     CurrentDamage = BaseDamage;
 | |
|     CurrentSpeed = BaseSpeed;
 | |
| 
 | |
|     RecalculateStats();
 | |
| }
 | |
| 
 | |
| void UCharacterStatsComponent::OnSkillPurchased(FName SkillID)
 | |
| {
 | |
|     // Called when a skill is purchased, update all stats
 | |
|     UpdateStatsFromSkillTree();
 | |
| }
 | |
| 
 | |
| void UCharacterStatsComponent::UpdateStatsFromSkillTree()
 | |
| {
 | |
|     if (!OwnerSkillTree)
 | |
|     {
 | |
|         return;
 | |
|     }
 | |
| 
 | |
|     // Reset skill bonuses
 | |
|     SkillHealthBonus = 0.0f;
 | |
|     SkillDamageBonus = 0.0f;
 | |
|     SkillSpeedBonus = 0.0f;
 | |
|     SkillHealthBonusPercent = 0.0f;
 | |
|     SkillDamageBonusPercent = 0.0f;
 | |
|     SkillSpeedBonusPercent = 0.0f;
 | |
| 
 | |
|     // Calculate total bonuses from purchased skills
 | |
|     TArray<FSkillNodeRuntime> AllSkills = OwnerSkillTree->GetAllSkillNodes();
 | |
|     for (const FSkillNodeRuntime& Skill : AllSkills)
 | |
|     {
 | |
|         if (Skill.CurrentState == ESkillNodeState::Purchased)
 | |
|         {
 | |
|             // Add flat bonuses
 | |
|             SkillHealthBonus += Skill.NodeData.HealthBonus;
 | |
|             SkillDamageBonus += Skill.NodeData.DamageBonus;
 | |
|             SkillSpeedBonus += Skill.NodeData.SpeedBonus;
 | |
| 
 | |
|             // Add percentage bonuses
 | |
|             SkillHealthBonusPercent += Skill.NodeData.HealthBonusPercent;
 | |
|             SkillDamageBonusPercent += Skill.NodeData.DamageBonusPercent;
 | |
|             SkillSpeedBonusPercent += Skill.NodeData.SpeedBonusPercent;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     RecalculateStats();
 | |
| }
 | |
| 
 | |
| void UCharacterStatsComponent::RecalculateStats()
 | |
| {
 | |
|     // Calculate final stats: Base + Flat Bonus + (Base * Percentage Bonus)
 | |
|     float OldMaxHealth = MaxHealth;
 | |
| 
 | |
|     MaxHealth = BaseMaxHealth + SkillHealthBonus + (BaseMaxHealth * SkillHealthBonusPercent / 100.0f);
 | |
|     CurrentDamage = BaseDamage + SkillDamageBonus + (BaseDamage * SkillDamageBonusPercent / 100.0f);
 | |
|     CurrentSpeed = BaseSpeed + SkillSpeedBonus + (BaseSpeed * SkillSpeedBonusPercent / 100.0f);
 | |
| 
 | |
|     // Scale current health proportionally if max health changed
 | |
|     if (OldMaxHealth > 0 && MaxHealth != OldMaxHealth)
 | |
|     {
 | |
|         float HealthPercent = CurrentHealth / OldMaxHealth;
 | |
|         CurrentHealth = MaxHealth * HealthPercent;
 | |
|     }
 | |
| 
 | |
|     // Apply speed to character movement
 | |
|     ApplySpeedToMovement();
 | |
| 
 | |
|     // Broadcast events
 | |
|     OnHealthChanged.Broadcast(CurrentHealth, MaxHealth);
 | |
|     OnDamageChanged.Broadcast(CurrentDamage);
 | |
|     OnSpeedChanged.Broadcast(CurrentSpeed);
 | |
|     OnStatsUpdated.Broadcast();
 | |
| 
 | |
|     UE_LOG(LogTemp, Log, TEXT("Stats Updated - Health: %.0f/%.0f, Damage: %.0f, Speed: %.0f"),
 | |
|         CurrentHealth, MaxHealth, CurrentDamage, CurrentSpeed);
 | |
| }
 | |
| 
 | |
| void UCharacterStatsComponent::TakeDamage(float DamageAmount)
 | |
| {
 | |
|     if (DamageAmount <= 0.0f)
 | |
|     {
 | |
|         return;
 | |
|     }
 | |
| 
 | |
|     CurrentHealth = FMath::Clamp(CurrentHealth - DamageAmount, 0.0f, MaxHealth);
 | |
|     OnHealthChanged.Broadcast(CurrentHealth, MaxHealth);
 | |
| 
 | |
|     if (CurrentHealth <= 0.0f)
 | |
|     {
 | |
|         UE_LOG(LogTemp, Warning, TEXT("Character has died!"));
 | |
|     }
 | |
| }
 | |
| 
 | |
| void UCharacterStatsComponent::Heal(float HealAmount)
 | |
| {
 | |
|     if (HealAmount <= 0.0f)
 | |
|     {
 | |
|         return;
 | |
|     }
 | |
| 
 | |
|     CurrentHealth = FMath::Clamp(CurrentHealth + HealAmount, 0.0f, MaxHealth);
 | |
|     OnHealthChanged.Broadcast(CurrentHealth, MaxHealth);
 | |
| }
 | |
| 
 | |
| void UCharacterStatsComponent::SetHealth(float NewHealth)
 | |
| {
 | |
|     CurrentHealth = FMath::Clamp(NewHealth, 0.0f, MaxHealth);
 | |
|     OnHealthChanged.Broadcast(CurrentHealth, MaxHealth);
 | |
| }
 | |
| 
 | |
| FText UCharacterStatsComponent::GetHealthText() const
 | |
| {
 | |
|     return FText::Format(FText::FromString(TEXT("{0} / {1}")),
 | |
|         FText::AsNumber(FMath::RoundToInt(CurrentHealth)),
 | |
|         FText::AsNumber(FMath::RoundToInt(MaxHealth)));
 | |
| }
 | |
| 
 | |
| FText UCharacterStatsComponent::GetDamageText() const
 | |
| {
 | |
|     return FText::Format(FText::FromString(TEXT("{0}")),
 | |
|         FText::AsNumber(FMath::RoundToInt(CurrentDamage)));
 | |
| }
 | |
| 
 | |
| FText UCharacterStatsComponent::GetSpeedText() const
 | |
| {
 | |
|     return FText::Format(FText::FromString(TEXT("{0}")),
 | |
|         FText::AsNumber(FMath::RoundToInt(CurrentSpeed)));
 | |
| }
 | |
| 
 | |
| void UCharacterStatsComponent::ApplySpeedToMovement()
 | |
| {
 | |
|     // Apply speed to character movement component
 | |
|     ACharacter* CharacterOwner = Cast<ACharacter>(GetOwner());
 | |
|     if (CharacterOwner && CharacterOwner->GetCharacterMovement())
 | |
|     {
 | |
|         CharacterOwner->GetCharacterMovement()->MaxWalkSpeed = CurrentSpeed;
 | |
|         UE_LOG(LogTemp, Log, TEXT("Applied movement speed: %.0f"), CurrentSpeed);
 | |
|     }
 | |
| } |