USTRUCT - Fail to assign value to a variable in struct

UENUM()
enum class FPlantType : uint8 {
    FPlantType_None = 0,
    FPlantType_CoffeeTree,
    FPlantType_CocoaTree
};

UENUM()
enum class FPlantState : uint8 {
    FPlantState_Available = 0,
    FPlantState_PlantedSmall,
    FPlantState_PlantedBig,
    FPlantState_Collectable,
    FPlantState_RequestToBeCollected,
    FPlantState_RequestToBeDeleted
};

USTRUCT()
struct FCPlantDefinition {

    GENERATED_BODY()

    FVector OriginLocation;

    FPlantType PlantType;

    FPlantState PlantState;

    float GrowTimeAccumulated;

    float GrowTotalTime;

    int TotalHP;

    int HP;

    int Order;
};

FCPlantDefinition definition;
Sometimes I assign ‘definition.PlantState = FPlantState::FPlantState_PlantedSmall’, its value will go back to FPlantState::FPlantState_Available. I am sure there is only one place to change PlantState.
Anyone has same issue before, thanks ~

We’ll need to see more code (like where does ‘definition’ come from?) related to how you’re assigning to the member to provide any help. If it’s a “sometimes”, have you identified the assignments that aren’t working and could share that code.

Sure
Desktop.zip (9.9 KB)
The relative files: CFFarmLevelMng.h .cpp / CFFarmPlantActor.h .cpp / CFPlantInfo.h / CFFarmActionStateMoveAndPlant.h .cpp

Where to chage state:

void UCFFarmActionStateMoveAndPlant::OnCharacter_DidCharacterReleaseSkill_Implementation(const FString& animationDisplayName,
    FVector aoeDecalLocation) {

    if (SkillStore->IsAnimationForPlantTree(animationDisplayName)) {
        AnimationEndingNotified = true;

        uint32 TId = FPlatformTLS::GetCurrentThreadId();
        UCFGameInstance* GameInstance = (UCFGameInstance*)(Player->GetGameInstance());
        bool mainT = GameInstance->IsMainThread(TId);
        ICFCollectableInterface::Execute_Plant(ObjectForPlant, FPlantType::FPlantType_CocoaTree);
    }
}

CFFarmPlantActor implements an interface, ICFCollectableInterface.

bool ACFFarmPlantActor::Plant_Implementation(FPlantType type) {

    if (TPlantDefinition.PlantType != FPlantType::FPlantType_None) {
        return false;
    }
    if (TPlantDefinition.PlantState != FPlantState::FPlantState_Available) {
        return false;
    }
    SetPlantType(type);
    SetState(FPlantState::FPlantState_PlantedSmall);
    return true;
}

=======================
Where to check state:
CFFarmLevelMng holds an array of CFFarmPlantActor, it will call the method ‘ManualTick’ defined in CFFarmPlantActor.

void ACFFarmLevelMng::Tick(float DeltaTime) {
    Super::Tick(DeltaTime);
    for (auto It = PlantMap.CreateConstIterator(); It; ++It) {
        ACFFarmPlantActor* Actor = It.Value();
        Actor->ManualTick(DeltaTime);
    }
}

Where to find sometime wrong:

void ACFFarmPlantActor::ManualTick(float DeltaTime) {

    if (TPlantDefinition.PlantState == FPlantState::FPlantState_Available) {
        UE_LOG(LogTemp,
            Warning,
            TEXT("Return FPlantState_Available"));
        return;
    }
   .
   .

After SetState(FPlantState::FPlantState_PlantedSmall) is called in ‘Plant_Implementation’,
TPlantDefinition.PlantState is expected to be ‘FPlantState::FPlantState_PlantedSmall’ and its value is still ‘FPlantState::FPlantState_Available’.

What does the SetState(…) code look like?

void ACFFarmPlantActor::SetState(FPlantState state) {

    if (state == FPlantState::FPlantState_PlantedSmall) {
        UE_LOG(LogTemp, Warning, TEXT("Set Small with Order:%d"), PlantDefinition.Order);
    } 
    if (state == FPlantState::FPlantState_Available) {
        UE_LOG(LogTemp, Warning, TEXT("Set Available with Order:%d"), PlantDefinition.Order);
    }

    UE_LOG(LogTemp, Warning, TEXT("SetState: %d"), state);

    PlantDefinition.PlantState = state;
.
.
}

For debugging, I add one integer called ‘TestState’ for ACFFarmPlantActor.
So In Plant_Implementation, there is one more line

bool ACFFarmPlantActor::Plant_Implementation(FPlantType type) {
    if (PlantDefinition.PlantType != FPlantType::FPlantType_None) {
        return false;
    }
    if (PlantState != FPlantState::FPlantState_Available) {
        return false;
    }
    SetPlantType(type);
    SetState(FPlantState::FPlantState_PlantedSmall);
    TestState = (int)(FPlantState::FPlantState_PlantedSmall);
    return true;
}

Its interesting that value of TestState is also 0, when I print its value in method ‘ManualTick’.

   UE_LOG(LogTemp,
        Warning,
        TEXT("PlantDefinition.PlantState:%d TestState:%d"),
        PlantDefinition.PlantState, TestState);
UENUM()
enum class FPlantState : uint8 {
    FPlantState_Available = 0,
    FPlantState_PlantedSmall,
    FPlantState_PlantedBig,
    FPlantState_Collectable,
    FPlantState_RequestToBeCollected,
    FPlantState_RequestToBeDeleted
};

That would seem to indicate that your Plant function probably isn’t being called. Or SetState is being called multiple times a frame, first to something non-zero and then to zero. You’ve got log output in SetState, are you seeing that in your logs?
Have you tried setting any breakpoints?

None of your logic or other setup looks obviously wrong (though I’ve only looked at what you posted, not your linked zip file). So you’ve likely got a logic error somewhere with the way your skill and/or FarmLevelMng is working. But it’s very difficult for someone to help you with that remotely.

The root cause is I create duplicate object with same tags, and both are put in TMap.
[Solved]