#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(); // 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 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:.0f} / {1:.0f}")), FText::AsNumber(CurrentHealth), FText::AsNumber(MaxHealth)); } FText UCharacterStatsComponent::GetDamageText() const { return FText::Format(FText::FromString(TEXT("Damage: {0:.0f}")), FText::AsNumber(CurrentDamage)); } FText UCharacterStatsComponent::GetSpeedText() const { return FText::Format(FText::FromString(TEXT("Speed: {0:.0f}")), FText::AsNumber(CurrentSpeed)); } void UCharacterStatsComponent::ApplySpeedToMovement() { // Apply speed to character movement component ACharacter* CharacterOwner = Cast(GetOwner()); if (CharacterOwner && CharacterOwner->GetCharacterMovement()) { CharacterOwner->GetCharacterMovement()->MaxWalkSpeed = CurrentSpeed; UE_LOG(LogTemp, Log, TEXT("Applied movement speed: %.0f"), CurrentSpeed); } }