Error c2039: 'combatInstance': is not a member of 'UGameCharacter'

So, to better learn about C++ development in Unreal Engine, I decided to take the eBook tutorials by Packt (“Building an RPG”). I had many irritating hours of error fixing, old code replacement with new, writing and trying to understand the code better, and all of those situations I managed to compile projects (even though VS IDE kept spitting out errors that did not even exist there – somehow Visual Studio has really odd flaws and syntax gliches whenever you include new headers or edit code).

Anyway, I finally stumbled upon this:

error c2039: 'combatInstance': is not a member of 'UGameCharacter'

This error does seem to make sense, because I can’t seem to find any declaration of this member/variable in any of the header files in the whole project, even after specifically searching in the solution explorer. It’s not mentioned anywhere in the book till page 63, in which they just show you a fragment of the code and tells you that CombatInstance pointer of all characters should point to itself, which will be done in the constructor. This one’s not that bad of a bad situations, because sometimes they don’t even document specific things they do and instead tells you to replace the whole code with “this-and-that” … and later on you realise that there are about 5 missing includes and a whole code block with 60% typos… this isn’t the case, but it still irritates a bit.

#GameCharacter.h

// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

//#include "CoreMinimal.h"
//#include "UObject/NoExportTypes.h"

#include "FCharacterInfo.h"
#include "FCharacterClassInfo.h"

#include "FEnemyInfo.h"
#include "GameCharacter.generated.h"


class CombatEngine;


UCLASS(BlueprintType)
class UNREALRPG_API UGameCharacter : public UObject
{
	GENERATED_BODY()

		UGameCharacter(const class FObjectInitializer& objectInitializer);
	
public:

	FCharacterClassInfo* ClassInfo;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "CharacterInfo")
		FString CharacterName;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "CharacterInfo")
		int32 MHP;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "CharacterInfo")
		int32 MMP;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "CharacterInfo")
		int32 HP;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "CharacterInfo")
		int32 MP;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "CharacterInfo")
		int32 ATK;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "CharacterInfo")
		int32 DEF;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "CharacterInfo")
		int32 LUCK;

	
	
	static UGameCharacter* CreateGameCharacter(FCharacterInfo* characterInfo, UObject* outer);
	static UGameCharacter* CreateGameCharacter(FEnemyInfo* enemyInfo, UObject* outer);

	void BeginDestroy() override;

	// page 60

protected:
	float testDelayTimer;

public:
	void BeginMakeDecision();
	bool MakeDecision(float DeltaSeconds);

	void BeginExecuteAction();
	bool ExecuteAction(float DeltaSeconds);

};

#CombatEngine.cpp

// Fill out your copyright notice in the Description page of Project Settings.

#include "CombatEngine.h"

CombatEngine::CombatEngine(TArray<UGameCharacter*> playerParty, TArray<UGameCharacter*> enemyParty)
{
	this->playerParty = playerParty;
	this->enemyParty = enemyParty;

	// first add all players to combat order
	for (int i = 0; i < playerParty.Num(); i++)
	{
		this->combatantOrder.Add(playerParty[i]);
	}

	// next add all enemies to combat order
	for (int i = 0; i < enemyParty.Num(); i++)
	{
		this->combatantOrder.Add(enemyParty[i]);
	}

	// add to combat instance
	for (int i = 0; i < this->combatantOrder.Num(); i++)
	{
		this->combatantOrder[i]->combatInstance = this; // 'not a member' error
	}

	this->tickTargetIndex = 0;
	this->SetPhase(CombatPhase::CPHASE_Decision);

}

CombatEngine::~CombatEngine()
{
	// free enemies
	for (int i = 0; i < this->enemyParty.Num(); i++)
	{
		this->enemyParty[i] = nullptr;
	}

	for (int i = 0; i < this->combatantOrder.Num(); i++)
	{
		this->combatantOrder[i]->combatInstance = nullptr; // 'not a member' error
	}

}

bool CombatEngine::Tick(float DeltaSeconds)
{
	switch (phase)
	{
	case CombatPhase::CPHASE_Decision:
	{
		if (!this->waitingForCharacter)
		{
			this->currentTickTarget->BeginMakeDecision();
			this->waitingForCharacter = true;
		}

		bool decisionMade = this->currentTickTarget->MakeDecision(DeltaSeconds);

		if (decisionMade)
		{
			SelectNextCharacter();

			// no text character, switch to action phase
			if (this->tickTargetIndex == -1)
			{
				this->SetPhase(CombatPhase::CPHASE_Action);
			}
		}
	}
	break;
	case CombatPhase::CPHASE_Action:
	{
		if (!this->waitingForCharacter)
		{
			this->currentTickTarget->BeginExecuteAction();
			this->waitingForCharacter = true;
		}

		bool actionFinished = this->currentTickTarget->ExecuteAction(DeltaSeconds);

		if (actionFinished)
		{
			SelectNextCharacter();

			// no text character, switch to action phase
			if (this->tickTargetIndex == -1)
			{
				this->SetPhase(CombatPhase::CPHASE_Decision);
			}
		}
	}
	break;
	// in case of victory or combat, return true (combat is finished)
	case CombatPhase::CPHASE_GameOver:
	case CombatPhase::CPHASE_Victory:
		return true;
	break;

	}

	// check for game over
	int deadCount = 0;
	for (int i = 0; i < this->playerParty.Num(); i++)
	{
		if (this->playerParty[i]->HP <= 0) deadCount++;
	}

	// all players have died, switch to game over phase
	if (deadCount == this->playerParty.Num())
	{
		this->SetPhase(CombatPhase::CPHASE_GameOver);
		return false;
	}

	// check for victory
	deadCount = 0;
	for (int i = 0; i < this->enemyParty.Num(); i++)
	{
		if (this->enemyParty[i]->HP <= 0) deadCount++;
	}

	// all enemies have died, switch to victory phase
	if (deadCount == this->enemyParty.Num())
	{
		this->SetPhase(CombatPhase::CPHASE_Victory);
		return false;
	}

	// if execution reaches here, combat has not finished
	return false;

}

void CombatEngine::SetPhase(CombatPhase phase)
{
	this->phase = phase;

	switch (phase)
	{
	case CombatPhase::CPHASE_Action:
	case CombatPhase::CPHASE_Decision:
		// set the active target to the first character in the combat order
		this->tickTargetIndex = 0;
		this->SelectNextCharacter();
	break;
	case CombatPhase::CPHASE_Victory:
		// todo: handle victory
	break;
	case CombatPhase::CPHASE_GameOver:
		// todo: handle game over
	break;

	}

}

void CombatEngine::SelectNextCharacter()
{
	this->waitingForCharacter = false;
	
	for (int i = this->tickTargetIndex; i < this->combatantOrder.Num(); i++)
	{
		UGameCharacter* character = this->combatantOrder[i];

		if (character->HP > 0)
		{
			this->tickTargetIndex = i + 1;
			this->currentTickTarget = character;
			return;
		}

		this->tickTargetIndex = -1;
		this->currentTickTarget = nullptr;

	}
}

I guess I found the fix myself.

As far as constant like “this” was a member of “CombatEngine class”, all I did was declare this same “CombatInstance”
as a part of it in GameCharacter header:

#GameCharacter.h
{
// …
public:
// …

	CombatEngine* combatInstance;

};