Why my tree not respawning. Im getting the timer being set in Debug after tree disapears but nothing after?

#include "Actors/ClickableActors/Skilling/TreeBase/TreeBase.h"
#include "Components/StaticMeshComponent.h"
#include "BMM/BMMCharacter.h"
#include "Net/UnrealNetwork.h"
#include "Engine/Engine.h"
#include "Game/AbilitySystem/ResourceAttributSet/ResourceAttributeSet.h"
#include "Input/TimedInteractable.h"
#include "AbilitySystemInterface.h"
#include "AbilitySystemComponent.h"
#include "GameplayEffect.h"
#include "GameplayEffectTypes.h"

ATreeBase::ATreeBase() :
TreeHealth(100.0f),
MaxTreeHealth(100.0f),
MaxInteractionDistance(200.0f),
RespawnTime(5.0f),
InteractingPlayerCharacter(nullptr)  // Default values and initialize player reference
{
    PrimaryActorTick.bCanEverTick = true;
    TreeMesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("TreeMesh"));
    RootComponent = TreeMesh;
    bIsAxe = false;
    RequiredWoodcuttingLevel = 1;
    LogsGiven = 1;
    ExperienceGiven = 10;
    LogType = "DefaultLog";  // Default log type
    bIsChoppedDown = false;

    bReplicates = true;
    SetReplicateMovement(true);

    AbilitySystemComponent = CreateDefaultSubobject<UAbilitySystemComponent>(TEXT("AbilitySystemComponent"));
    ResourceAttributes = CreateDefaultSubobject<UResourceAttributeSet>(TEXT("ResourceAttributes"));
    ResourceAttributes->Health.SetCurrentValue(TreeHealth);
    ResourceAttributes->MaxHealth.SetCurrentValue(MaxTreeHealth);
}

void ATreeBase::BeginPlay()
{
    Super::BeginPlay();
    UE_LOG(LogTemp, Log, TEXT("Tree spawned: %s"), *GetName());
    ResourceAttributes->Health.SetCurrentValue(TreeHealth);
    ResourceAttributes->MaxHealth.SetCurrentValue(MaxTreeHealth);
}

UAbilitySystemComponent* ATreeBase::GetAbilitySystemComponent() const
{
    return nullptr;  // Return the appropriate AbilitySystemComponent instance if needed
}

void ATreeBase::StartRespawnTimer()
{
    if (HasAuthority())
    {
        GetWorldTimerManager().SetTimer(RespawnTimerHandle, this, &ATreeBase::RespawnTree, RespawnTime, false);
        UE_LOG(LogTemp, Log, TEXT("Respawn timer set for %f seconds"), RespawnTime);
    }
}

void ATreeBase::ChopDownTree(ABMMCharacter* PlayerCharacter)
{
    if (!bIsChoppedDown && HasAuthority() && PlayerCharacter && PlayerCharacter->SkillAttributeSet->WoodcuttingLevel.GetCurrentValue() >= RequiredWoodcuttingLevel)
    {
        StartRespawnTimer();
        bIsChoppedDown = true;
        UE_LOG(LogTemp, Log, TEXT("Tree being chopped down: %s by %s"), *GetName(), *PlayerCharacter->GetName());
        Interact(PlayerCharacter);
    }
    else if (bIsChoppedDown)
    {
        UE_LOG(LogTemp, Warning, TEXT("Tree is already chopped down: %s"), *GetName());
    }
    else
    {
        UE_LOG(LogTemp, Warning, TEXT("Player %s does not meet the required woodcutting level: %d"), *PlayerCharacter->GetName(), RequiredWoodcuttingLevel);
    }
}

void ATreeBase::HandleInteractTimerExpiry(ABMMCharacter* PlayerCharacter)
{
    UE_LOG(LogTemp, Log, TEXT("HandleInteractTimerExpiry called for: %s"), *GetName());

    if (HasAuthority() && PlayerCharacter)
    {
        UE_LOG(LogTemp, Log, TEXT("Destroying tree: %s"), *GetName());
        PlayerCharacter->UnfreezePlayer(); // Unfreeze the player when the tree is destroyed
        StopChopTreeAnimation(PlayerCharacter); // Stop the chopping animation
        StopDamageTimer(); // Stop the damage timer

        StartRespawnTimer();

        UE_LOG(LogTemp, Log, TEXT("Tree %s will respawn in %f seconds"), *GetName(), RespawnTime);

        TreeMesh->SetVisibility(false, true);
        TreeMesh->SetCollisionEnabled(ECollisionEnabled::NoCollision);
    }
    else
    {
        UE_LOG(LogTemp, Warning, TEXT("PlayerCharacter is null or not authoritative"));
    }
}

void ATreeBase::ApplyDamage()
{
    float DamageAmount = 10.0f;  // Default damage amount

    const float OldHealth = ResourceAttributes->Health.GetCurrentValue();
    const float NewHealth = OldHealth - DamageAmount;
    ResourceAttributes->Health.SetCurrentValue(NewHealth);

    GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Red, FString::Printf(TEXT("Hit Tree for %f Damage - Tree Health %f/%f"), DamageAmount, NewHealth, MaxTreeHealth));

    if (NewHealth <= 0.0f)
    {
        GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Green, TEXT("Tree has been destroyed!"));
        if (InteractingPlayerCharacter)
        {
            UE_LOG(LogTemp, Log, TEXT("Unfreezing Interacting Player: %s"), *InteractingPlayerCharacter->GetName());
            InteractingPlayerCharacter->UnfreezePlayer(); // Unfreeze the player when the tree is destroyed
        }

        StartRespawnTimer();

        UE_LOG(LogTemp, Log, TEXT("Tree %s will respawn in %f seconds"), *GetName(), RespawnTime);

        TreeMesh->SetVisibility(false, true);
        TreeMesh->SetCollisionEnabled(ECollisionEnabled::NoCollision);

        // Destroy the actor to handle proper cleanup
        Destroy();
    }
}

void ATreeBase::RespawnTree()
{
    UE_LOG(LogTemp, Log, TEXT("Respawning tree: %s"), *GetName());

    if (!HasAuthority())
    {
        UE_LOG(LogTemp, Error, TEXT("RespawnTree called on non-authoritative actor: %s"), *GetName());
        return;
    }

    // Use AActorSpawner to spawn the new tree actor
    FVector SpawnLocation = GetActorLocation();
    FRotator SpawnRotation = GetActorRotation();
    ATreeBase* NewTree = GetWorld()->SpawnActor<ATreeBase>(GetClass(), SpawnLocation, SpawnRotation);

    if (NewTree)
    {
        NewTree->TreeHealth = MaxTreeHealth;
        NewTree->ResourceAttributes->Health.SetCurrentValue(MaxTreeHealth);
        NewTree->bIsChoppedDown = false;

        NewTree->TreeMesh->SetVisibility(true);
        NewTree->TreeMesh->SetCollisionEnabled(ECollisionEnabled::QueryAndPhysics);
        NewTree->TreeMesh->SetCollisionResponseToAllChannels(ECR_Block);

        NewTree->bReplicates = true;
        NewTree->SetReplicateMovement(true);

        UE_LOG(LogTemp, Log, TEXT("Tree respawned and is now visible and interactable: %s"), *NewTree->GetName());
    }
    else
    {
        UE_LOG(LogTemp, Error, TEXT("Failed to respawn tree: %s"), *GetName());
    }
}

void ATreeBase::Server_RespawnTree_Implementation()
{
    RespawnTree();
}

bool ATreeBase::Server_RespawnTree_Validate()
{
    return true;
}

void ATreeBase::PlayChopTreeAnimation(ABMMCharacter* PlayerCharacter)
{
    if (PlayerCharacter)
    {
        if (UAnimInstance* AnimInstance = PlayerCharacter->GetMesh()->GetAnimInstance())
        {
            if (PlayerCharacter->ChopTreeAnimation)
            {
                AnimInstance->Montage_Play(PlayerCharacter->ChopTreeAnimation);
            }
        }
    }
}

void ATreeBase::StopChopTreeAnimation(ABMMCharacter* PlayerCharacter)
{
    if (PlayerCharacter)
    {
        if (UAnimInstance* AnimInstance = PlayerCharacter->GetMesh()->GetAnimInstance())
        {
            if (PlayerCharacter->ChopTreeAnimation)
            {
                AnimInstance->Montage_Stop(0.2f, PlayerCharacter->ChopTreeAnimation);
            }
        }
    }
}

void ATreeBase::StartDamageTimer()
{
    GetWorld()->GetTimerManager().SetTimer(DamageTimerHandle, this, &ATreeBase::ApplyDamage, 0.4f, true, 0.0f);  // 4 ticks
}

void ATreeBase::StopDamageTimer()
{
    GetWorld()->GetTimerManager().ClearTimer(DamageTimerHandle);
}

void ATreeBase::ServerApplyDamage_Implementation(float DamageAmount)
{
    ApplyDamage();
}

float ATreeBase::GetHealth() const
{
    return ResourceAttributes->Health.GetCurrentValue();
}

void ATreeBase::SetHealth(float NewHealth)
{
    TreeHealth = NewHealth;
    ResourceAttributes->Health.SetCurrentValue(NewHealth);
}

float ATreeBase::GetMaxHealth() const
{
    return ResourceAttributes->MaxHealth.GetCurrentValue();
}

void ATreeBase::SetMaxHealth(float NewMaxHealth)
{
    MaxTreeHealth = NewMaxHealth;
    ResourceAttributes->MaxHealth.SetCurrentValue(NewMaxHealth);
}

bool ATreeBase::ServerApplyDamage_Validate(float DamageAmount)
{
    return true;  // Optionally, add validation logic here
}

void ATreeBase::OnRep_Health()
{
    // Log the replicated health value
    UE_LOG(LogTemp, Log, TEXT("OnRep_Health called. New Health: %f"), TreeHealth);

    // Check if health is zero and handle destruction
    if (TreeHealth <= 0.0f)
    {
        HandleInteractTimerExpiry(nullptr);
    }
}

void ATreeBase::OnRep_IsChoppedDown()
{
    if (bIsChoppedDown)
    {
        StartRespawnTimer();
        UE_LOG(LogTemp, Log, TEXT("Tree %s will respawn in %f seconds"), *GetName(), RespawnTime);
        TreeMesh->SetVisibility(false, true);
        TreeMesh->SetCollisionEnabled(ECollisionEnabled::NoCollision);
    }
}

void ATreeBase::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
{
    Super::GetLifetimeReplicatedProps(OutLifetimeProps);
    DOREPLIFETIME(ATreeBase, ResourceAttributes);
    DOREPLIFETIME(ATreeBase, RequiredWoodcuttingLevel);
    DOREPLIFETIME(ATreeBase, LogsGiven);
    DOREPLIFETIME(ATreeBase, ExperienceGiven);
    DOREPLIFETIME(ATreeBase, LogType);
    DOREPLIFETIME(ATreeBase, TreeHealth);  // Replicate TreeHealth
    DOREPLIFETIME(ATreeBase, MaxTreeHealth);
    DOREPLIFETIME(ATreeBase, MaxInteractionDistance);  // Replicate MaxInteractionDistance
    DOREPLIFETIME(ATreeBase, RespawnTime);
    DOREPLIFETIME(ATreeBase, bIsChoppedDown); // Replicate bIsChoppedDown
}

void ATreeBase::AwardWoodcuttingExperience(ABMMCharacter* PlayerCharacter)
{
    if (PlayerCharacter)
    {
        UE_LOG(LogTemp, Log, TEXT("Starting to award experience"));
        if (PlayerCharacter->SkillAttributeSet)
        {
            PlayerCharacter->SkillAttributeSet->AddWoodcuttingExperience(ExperienceGiven);
            UE_LOG(LogTemp, Log, TEXT("Player %s awarded %d woodcutting experience"), *PlayerCharacter->GetName(), ExperienceGiven);

            GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Green, FString::Printf(TEXT("Awarded %d woodcutting experience!"), ExperienceGiven));

            if (PlayerCharacter->SkillAttributeSet->WoodcuttingLevel.GetCurrentValue() > 1)  // Adjust the condition as needed
            {
                GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Blue, TEXT("Level Up!"));
            }

            UE_LOG(LogTemp, Log, TEXT("Finished awarding experience"));
        }
        else
        {
            UE_LOG(LogTemp, Error, TEXT("PlayerCharacter's SkillAttributeSet is null"));
        }
    }
    else
    {
        UE_LOG(LogTemp, Warning, TEXT("PlayerCharacter is null"));
    }
}

void ATreeBase::Interact(ABMMCharacter* PlayerCharacter)
{
    UE_LOG(LogTemp, Log, TEXT("TreeBase: Interacting with: %s"), *GetName());

    if (PlayerCharacter)
    {
        FVector PlayerLocation = PlayerCharacter->GetActorLocation();
        FVector TreeLocation = GetActorLocation();
        float Distance = FVector::Dist(PlayerLocation, TreeLocation);

        if (Distance > MaxInteractionDistance)
        {
            UE_LOG(LogTemp, Warning, TEXT("Player %s is too far from tree %s to interact (Distance: %f, Max: %f)"), *PlayerCharacter->GetName(), *GetName(), Distance, MaxInteractionDistance);
            return;  
        }

        InteractingPlayerCharacter = PlayerCharacter;  // Store the reference to PlayerCharacter
        PlayerCharacter->FreezePlayer();  // Freeze the player when starting to chop the tree
        UE_LOG(LogTemp, Log, TEXT("Freezing Player: %s"), *PlayerCharacter->GetName());
        PlayChopTreeAnimation(PlayerCharacter);  // Play the chopping animation
        StartDamageTimer();  // Start the damage timer
    }

    Super::Interact(PlayerCharacter);  // Use the base class timer logic

    if (PlayerCharacter)
    {
        AwardWoodcuttingExperience(PlayerCharacter);
    }
}

Thanks!

Hey there @TonekNo! Welcome to the community! So it looks like you’re Destroy()ing the actor itself entirely in your ApplyDamage() function. This could be the cause, as if the actor holding the timer is destroyed (or prepared for garbage collection) the timer is invalidated as well.

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.