Accessing a variable in another script is crashing the engine.

I am trying to set up my SCharacter script as an Ammo repository for testing purposes (want to get held ammo off of the weapon itself so it can eventually exist in a player inventory). The specific section of code I have come up with compiles in VS2015 but crashes unreal if it isn’t commented out. The bolded and underline code causes the crash(myPawn is a reference to the SCharacter script where I am holding the HeldRifleAmmo, and MaxRifleAmmo variables). This is from the survival game example that I am modifying for learning purposes.

SWeapon.cpp



#include "SurvivalGame.h"
#include "SWeapon.h"
#include "SCharacter.h"
#include "STypes.h"
#include "SWeaponPickup.h"
#include "SPlayerController.h"

ASWeapon::ASWeapon(const class FObjectInitializer& PCIP)
: Super(PCIP)
{
	Mesh = PCIP.CreateDefaultSubobject<USkeletalMeshComponent>(this, TEXT("WeaponMesh3P"));
	Mesh->MeshComponentUpdateFlag = EMeshComponentUpdateFlag::OnlyTickPoseWhenRendered;
	Mesh->bReceivesDecals = true;
	Mesh->CastShadow = true;
	Mesh->SetCollisionObjectType(ECC_WorldDynamic);
	Mesh->SetCollisionEnabled(ECollisionEnabled::NoCollision);
	Mesh->SetCollisionResponseToAllChannels(ECR_Ignore);
	Mesh->SetCollisionResponseToChannel(ECC_Visibility, ECR_Block);
	RootComponent = Mesh;

	bIsEquipped = false;
	CurrentState = EWeaponState::Idle;
	AmmoType = EAmmoType::Rifle;
	PrimaryActorTick.bCanEverTick = true;
	PrimaryActorTick.TickGroup = TG_PrePhysics;

	SetReplicates(true);
	bNetUseOwnerRelevancy = true;

	MuzzleAttachPoint = TEXT("MuzzleFlashSocket");
	StorageSlot = EInventorySlot::Primary;
	
	
	ShotsPerMinute = 700;
	
	
	
	switch (AmmoType)
	{
	case EAmmoType::Rifle:
		**StartAmmo = MyPawn->GetRifleAmmo();
		MaxAmmo = MyPawn->GetMaxRifleAmmo();**
		break;
	case EAmmoType::Shotgun:
		StartAmmo = 0;
		MaxAmmo = 0;
		break;
	case EAmmoType::Pistol:
		StartAmmo = 0;
		MaxAmmo = 0;
		break;
	case EAmmoType::SniperRifle:
		StartAmmo = 0;
		MaxAmmo = 0;
		break;
	default:
		StartAmmo = 0;
		MaxAmmo = 0;
		break;
	}
	
	MaxAmmoPerClip = 30;
	NoAnimReloadDuration = 1.5f;
	NoEquipAnimDuration = 0.5f;
	SemiAuto = false;
	SemiFire = false;
}





This is the crash log:



Generating report for minidump

Application version 4.10.2
 ... built from changelist 2818068

OS version 10.0.0.10586
Running 8 x64 processors
Exception was "Access violation - code c0000005 (first/second chance not available)"

Source context from "survivalgame/source/survivalgame/private/items/sweapon.cpp"

<SOURCE START>
<SOURCE END>

<CALLSTACK START>
UE4Editor_SurvivalGame!ASWeapon::ASWeapon() [c:\users\brad\documents\unreal projects\survivalgame\source\survivalgame\private\items\sweapon.cpp:44]
UE4Editor_CoreUObject!UClass::CreateDefaultObject() [d:\buildfarm\buildmachine_++depot+ue4-releases+4.10\engine\source\runtime\coreuobject\private\uobject\class.cpp:2657]
UE4Editor_CoreUObject!UObjectLoadAllCompiledInDefaultProperties() [d:\buildfarm\buildmachine_++depot+ue4-releases+4.10\engine\source\runtime\coreuobject\private\uobject\uobjectbase.cpp:658]
UE4Editor_CoreUObject!ProcessNewlyLoadedUObjects() [d:\buildfarm\buildmachine_++depot+ue4-releases+4.10\engine\source\runtime\coreuobject\private\uobject\uobjectbase.cpp:752]
UE4Editor_CoreUObject!TBaseStaticDelegateInstance<void __cdecl(void)>::ExecuteIfSafe() [d:\buildfarm\buildmachine_++depot+ue4-releases+4.10\engine\source\runtime\core\public\delegates\delegateinstancesimpl_variadics.inl:921]
UE4Editor_Core!TBaseMulticastDelegate<void>::Broadcast() [d:\buildfarm\buildmachine_++depot+ue4-releases+4.10\engine\source\runtime\core\public\delegates\delegatesignatureimpl_variadics.inl:809]
UE4Editor_Core!FModuleManager::LoadModuleWithFailureReason() [d:\buildfarm\buildmachine_++depot+ue4-releases+4.10\engine\source\runtime\core\private\modules\modulemanager.cpp:426]
UE4Editor_Projects!FModuleDescriptor::LoadModulesForPhase() [d:\buildfarm\buildmachine_++depot+ue4-releases+4.10\engine\source\runtime\projects\private\moduledescriptor.cpp:370]
UE4Editor_Projects!FProjectManager::LoadModulesForProject() [d:\buildfarm\buildmachine_++depot+ue4-releases+4.10\engine\source\runtime\projects\private\projectmanager.cpp:53]
UE4Editor!FEngineLoop::LoadStartupModules() [d:\buildfarm\buildmachine_++depot+ue4-releases+4.10\engine\source\runtime\launch\private\launchengineloop.cpp:1989]
UE4Editor!FEngineLoop::PreInit() [d:\buildfarm\buildmachine_++depot+ue4-releases+4.10\engine\source\runtime\launch\private\launchengineloop.cpp:1495]
UE4Editor!GuardedMain() [d:\buildfarm\buildmachine_++depot+ue4-releases+4.10\engine\source\runtime\launch\private\launch.cpp:110]
UE4Editor!GuardedMainWrapper() [d:\buildfarm\buildmachine_++depot+ue4-releases+4.10\engine\source\runtime\launch\private\windows\launchwindows.cpp:126]
UE4Editor!WinMain() [d:\buildfarm\buildmachine_++depot+ue4-releases+4.10\engine\source\runtime\launch\private\windows\launchwindows.cpp:200]
UE4Editor!__scrt_common_main_seh() [f:\dd\vctools\crt\vcstartup\src\startup\exe_common.inl:264]
kernel32
ntdll
<CALLSTACK END>


The error “Access violation - code c000005” means in most of the cases that you are trying to access something that doesnt exist. So i guess MyPawn->GetBlah… is crashing this because MyPawn is nullptr.

if im write then the following wont crash the engine:

I assume that MyPawn is a classmember of your WeaponClass…




case EAmmoType::Rifle:

if(MyPawn)
{
		StartAmmo = MyPawn->GetRifleAmmo();
		MaxAmmo = MyPawn->GetMaxRifleAmmo();
}
else
{
StartAmmo = 0;
MaxAmmo = 0;
}
		break;



However, this is not solving your problem. You are inside a constructor and of what i can tell is that you never set your Pawn pointer before you try to access it.
It is ok if you initialize your values with some default and not trying to access things that may not exists. Better to do it a bit later when you are 100% sure that all the objects are initialized.

So here when you spawn your Weapon consider to let the pawn handle this ammo stuff when it gets a new weapon in its inventory, just a thought.

kind regards

So this is happening because myPawn isnt initialized yet?

Could i trigger a function at the end of initialization(PostInitialization ()) that laccesses mypawn after it has been created?

yes it is happening because of that.

I would advise not to call those initialization functions on your own, because they are called automagically by the engine.

Can you tell me where do you actually try to set the MyPawn variable for a weapon?

Edit: did i really wrote “write” instead of “right” in my previous post? Im sorry for that :wink:

This is a script for the Survival Game Template so I guess it is ok to post here. I think I am misunderstanding something fundamental about C++ here, my conversion from C# has been pretty rocky. In C# I would set a scripts variables in the Awake() function and then access other need variables from other scripts in the Start() function. That is what I am trying, and failing, to do here. MyPawn is a reference to the SCharacter script where the Ammo Variables are held.

SWeapon.h (abbreviated)



/** pawn owner */
	UPROPERTY(Transient, ReplicatedUsing = OnRep_MyPawn)
	class ASCharacter* MyPawn;


SWeapon.cpp (Full thing)





#include "SurvivalGame.h"
#include "SWeapon.h"
#include "SCharacter.h"  
#include "STypes.h"
#include "SWeaponPickup.h"
#include "SPlayerController.h"


ASWeapon::ASWeapon(const class FObjectInitializer& PCIP)
: Super(PCIP)
{
	Mesh = PCIP.CreateDefaultSubobject<USkeletalMeshComponent>(this, TEXT("WeaponMesh3P"));
	Mesh->MeshComponentUpdateFlag = EMeshComponentUpdateFlag::OnlyTickPoseWhenRendered;
	Mesh->bReceivesDecals = true;
	Mesh->CastShadow = true;
	Mesh->SetCollisionObjectType(ECC_WorldDynamic);
	Mesh->SetCollisionEnabled(ECollisionEnabled::NoCollision);
	Mesh->SetCollisionResponseToAllChannels(ECR_Ignore);
	Mesh->SetCollisionResponseToChannel(ECC_Visibility, ECR_Block);
	RootComponent = Mesh;
	bIsEquipped = false;
	CurrentState = EWeaponState::Idle;
	AmmoType = EAmmoType::Rifle;
	PrimaryActorTick.bCanEverTick = true;
	PrimaryActorTick.TickGroup = TG_PrePhysics;

	SetReplicates(true);
	bNetUseOwnerRelevancy = true;

	MuzzleAttachPoint = TEXT("MuzzleFlashSocket");
	StorageSlot = EInventorySlot::Primary;
	
	
	ShotsPerMinute = 700;

	
	MaxAmmoPerClip = 30;
	NoAnimReloadDuration = 1.5f;
	NoEquipAnimDuration = 0.5f;
	SemiAuto = false;
	SemiFire = false;
	
}


void ASWeapon::PostInitializeComponents()
{
	Super::PostInitializeComponents();

	/* Setup configuration */
	InitializeAmmo();
	
}

void ASWeapon::InitializeAmmo()
{
	
	if(MyPawn) //this is never returning true
        {
		switch (AmmoType)
		{
		case EAmmoType::Rifle:
			StartAmmo =MyPawn->GetRifleAmmo();
			MaxAmmo = MyPawn->GetMaxRifleAmmo();
			break;
		case EAmmoType::Shotgun:
			StartAmmo = 0;
			MaxAmmo = 0;
			break;
		case EAmmoType::Pistol:
			StartAmmo = 0;
			MaxAmmo = 0;
			break;
		case EAmmoType::SniperRifle:
			StartAmmo = 0;
			MaxAmmo = 0;
			break;
		default:
			StartAmmo = 0;
			MaxAmmo = 0;
			break;
		}
        }
	CurrentAmmo = FMath::Min(StartAmmo, MaxAmmo);
	CurrentAmmoInClip = FMath::Min(MaxAmmoPerClip, StartAmmo);
	TimeBetweenShots = 60.0f / ShotsPerMinute;
}

void ASWeapon::EndPlay(const EEndPlayReason::Type EndPlayReason)
{
	Super::EndPlay(EndPlayReason);

	DetachMeshFromPawn();
	StopSimulatingWeaponFire();
}


/*
	Return Mesh of Weapon
*/
USkeletalMeshComponent* ASWeapon::GetWeaponMesh() const
{
	return Mesh;
}


class ASCharacter* ASWeapon::GetPawnOwner() const
{
	return MyPawn;
}


void ASWeapon::SetOwningPawn(ASCharacter* NewOwner)
{
	if (MyPawn != NewOwner)
	{
		Instigator = NewOwner;
		MyPawn = NewOwner;
		// Net owner for RPC calls.
		SetOwner(NewOwner);
	}
}


void ASWeapon::OnRep_MyPawn()
{
	if (MyPawn)
	{
		OnEnterInventory(MyPawn);
	}
	else
	{
		OnLeaveInventory();

	}
}


void ASWeapon::AttachMeshToPawn(EInventorySlot Slot)
{
	if (MyPawn)
	{
		// Remove and hide
		DetachMeshFromPawn();

		USkeletalMeshComponent* PawnMesh = MyPawn->GetMesh();
		FName AttachPoint = MyPawn->GetInventoryAttachPoint(Slot);
		Mesh->SetHiddenInGame(false);
		Mesh->AttachTo(PawnMesh, AttachPoint, EAttachLocation::SnapToTarget);
	}
}


void ASWeapon::DetachMeshFromPawn()
{
	Mesh->DetachFromParent();
	Mesh->SetHiddenInGame(true);	
}


void ASWeapon::OnEquip(bool bPlayAnimation)
{
	bPendingEquip = true;
	DetermineWeaponState();

	if (bPlayAnimation)
	{
		float Duration = PlayWeaponAnimation(EquipAnim);
		if (Duration <= 0.0f)
		{
			// Failsafe in case animation is missing
			Duration = NoEquipAnimDuration;
		}
		EquipStartedTime = GetWorld()->TimeSeconds;
		EquipDuration = Duration;

		GetWorldTimerManager().SetTimer(EquipFinishedTimerHandle, this, &ASWeapon::OnEquipFinished, Duration, false);
	}
	else
	{
		/* Immediately finish equipping */
		OnEquipFinished();
	}

	if (MyPawn && MyPawn->IsLocallyControlled())
	{
		PlayWeaponSound(EquipSound);
	}
}


void ASWeapon::OnUnEquip()
{
	bIsEquipped = false;
	StopFire();

	if (bPendingEquip)
	{
		StopWeaponAnimation(EquipAnim);
		bPendingEquip = false;

		GetWorldTimerManager().ClearTimer(EquipFinishedTimerHandle);
	}
	if (bPendingReload)
	{
		StopWeaponAnimation(ReloadAnim);
		bPendingReload = false;

		GetWorldTimerManager().ClearTimer(TimerHandle_ReloadWeapon);
	}

	DetermineWeaponState();
}


void ASWeapon::OnEnterInventory(ASCharacter* NewOwner)
{
	SetOwningPawn(NewOwner);
	AttachMeshToPawn(StorageSlot);
}


void ASWeapon::OnLeaveInventory()
{
	if (Role == ROLE_Authority)
	{
		SetOwningPawn(nullptr);
	}

	if (IsAttachedToPawn())
	{
		OnUnEquip();
	}

	DetachMeshFromPawn();
}


bool ASWeapon::IsEquipped() const
{
	return bIsEquipped;
}


bool ASWeapon::IsAttachedToPawn() const // TODO: Review name to more accurately specify meaning.
{
	return bIsEquipped || bPendingEquip;
}


void ASWeapon::StartFire()
{
	
	if (Role < ROLE_Authority)
	{
		ServerStartFire();
	}

	if (!bWantsToFire)
	{
		
		bWantsToFire = true;
		DetermineWeaponState();
	}
}


void ASWeapon::StopFire()
{
	if (Role < ROLE_Authority)
	{
		ServerStopFire();
	}

	if (bWantsToFire)
	{
		bWantsToFire = false;
		DetermineWeaponState();
	}
}


bool ASWeapon::ServerStartFire_Validate()
{
	return true;
}


void ASWeapon::ServerStartFire_Implementation()
{
	StartFire();
}


bool ASWeapon::ServerStopFire_Validate()
{
	return true;
}


void ASWeapon::ServerStopFire_Implementation()
{
	StopFire();
}


bool ASWeapon::CanFire() const
{
	bool bPawnCanFire = MyPawn && MyPawn->CanFire();
	bool bStateOK = CurrentState == EWeaponState::Idle || CurrentState == EWeaponState::Firing;
	bool bSFire;
	if (SemiAuto)
	{
		bSFire = SemiFire;
	}
	else
	{
		bSFire = false;
	}
	return bPawnCanFire && bStateOK && !bPendingReload && !bSFire;
}


FVector ASWeapon::GetAdjustedAim() const
{
	ASPlayerController* const PC = Instigator ? Cast<ASPlayerController>(Instigator->Controller) : nullptr;
	FVector FinalAim = FVector::ZeroVector;

	if (PC)
	{
		FVector CamLoc;
		FRotator CamRot;
		PC->GetPlayerViewPoint(CamLoc, CamRot);

		FinalAim = CamRot.Vector();
	}
	else if (Instigator)
	{
		FinalAim = Instigator->GetBaseAimRotation().Vector();
	}

	return FinalAim;
}


FVector ASWeapon::GetCameraDamageStartLocation(const FVector& AimDir) const
{
	ASPlayerController* PC = MyPawn ? Cast<ASPlayerController>(MyPawn->Controller) : nullptr;
	FVector OutStartTrace = FVector::ZeroVector;

	if (PC)
	{
		FRotator DummyRot;
		PC->GetPlayerViewPoint(OutStartTrace, DummyRot);

		// Adjust trace so there is nothing blocking the ray between the camera and the pawn, and calculate distance from adjusted start
		OutStartTrace = OutStartTrace + AimDir * (FVector::DotProduct((Instigator->GetActorLocation() - OutStartTrace), AimDir));
	}

	return OutStartTrace;
}


FHitResult ASWeapon::WeaponTrace(const FVector& TraceFrom, const FVector& TraceTo) const
{
	FCollisionQueryParams TraceParams(TEXT("WeaponTrace"), true, Instigator);
	TraceParams.bTraceAsyncScene = true;
	TraceParams.bReturnPhysicalMaterial = true;

	FHitResult Hit(ForceInit);
	GetWorld()->LineTraceSingleByChannel(Hit, TraceFrom, TraceTo, COLLISION_WEAPON, TraceParams);

	return Hit;
}



void ASWeapon::HandleFiring()
{
	if (CurrentAmmoInClip > 0)
	{
		if (CanFire())
		{
			

			if (GetNetMode() != NM_DedicatedServer)
			{
				
					SimulateWeaponFire();
					
					
				
			}

			if (MyPawn && MyPawn->IsLocallyControlled())
			{
				FireWeapon();
				
				UseAmmo();

				// Update firing FX on remote clients if this is called on server
				BurstCounter++;
			}
		}
	}
	else if (CanReload())
	{
		StartReload();
	}
	else if (MyPawn && MyPawn->IsLocallyControlled())
	{
		if (GetCurrentAmmo() == 0 && !bRefiring)
		{
			PlayWeaponSound(OutOfAmmoSound);
		}

		/* Reload after firing last round */
		if (CurrentAmmoInClip <= 0 && CanReload())
		{
			StartReload();
		}

		/* Stop weapon fire FX, but stay in firing state */
		if (BurstCounter > 0)
		{
			OnBurstFinished();
		}
	}

	if (MyPawn && MyPawn->IsLocallyControlled())
	{
		if (Role < ROLE_Authority)
		{
			ServerHandleFiring();
		}

		/* Retrigger HandleFiring on a delay for automatic weapons */
		bRefiring = (CurrentState == EWeaponState::Firing && TimeBetweenShots > 0.0f);
		if (bRefiring)
		{
			GetWorldTimerManager().SetTimer(TimerHandle_HandleFiring, this, &ASWeapon::HandleFiring, TimeBetweenShots, false);
		}
	}

	/* Make Noise on every shot. The data is managed by the PawnNoiseEmitterComponent created in SBaseCharacter and used by PawnSensingComponent in SZombieCharacter */
	if (MyPawn)
	{
		MyPawn->MakePawnNoise(1.0f);
	}

	LastFireTime = GetWorld()->GetTimeSeconds();
}


void ASWeapon::SimulateWeaponFire()
{
	if (MuzzleFX)
	{
		MuzzlePSC = UGameplayStatics::SpawnEmitterAttached(MuzzleFX, Mesh, MuzzleAttachPoint);	
	}

	if (!bPlayingFireAnim)
	{
		
		PlayWeaponAnimation(FireAnim);
		bPlayingFireAnim = true;

	}

	PlayWeaponSound(FireSound);
	if (SemiAuto)
	{
		SemiFire = true;
	}
}


void ASWeapon::StopSimulatingWeaponFire()
{
	if (bPlayingFireAnim)
	{
		StopWeaponAnimation(FireAnim);
		bPlayingFireAnim = false;
	}
}



void ASWeapon::OnRep_BurstCounter()
{
	if (BurstCounter > 0)
	{
		
		SimulateWeaponFire();
			
	}
	else
	{
		StopSimulatingWeaponFire();
		
	}
}



bool ASWeapon::ServerHandleFiring_Validate()
{
	return true;
}


void ASWeapon::ServerHandleFiring_Implementation()
{
	const bool bShouldUpdateAmmo = (CurrentAmmoInClip > 0 && CanFire());

	HandleFiring();

	if (bShouldUpdateAmmo)
	{
		UseAmmo();

		// Update firing FX on remote clients
		BurstCounter++;
	}
}


void ASWeapon::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
{
	Super::GetLifetimeReplicatedProps(OutLifetimeProps);

	DOREPLIFETIME(ASWeapon, MyPawn);

	DOREPLIFETIME_CONDITION(ASWeapon, CurrentAmmo, COND_OwnerOnly);
	DOREPLIFETIME_CONDITION(ASWeapon, CurrentAmmoInClip, COND_OwnerOnly);
	DOREPLIFETIME_CONDITION(ASWeapon, BurstCounter, COND_SkipOwner);
	DOREPLIFETIME_CONDITION(ASWeapon, bPendingReload, COND_SkipOwner);
}


FVector ASWeapon::GetMuzzleLocation() const
{
	return Mesh->GetSocketLocation(MuzzleAttachPoint);
}


FVector ASWeapon::GetMuzzleDirection() const
{
	return Mesh->GetSocketRotation(MuzzleAttachPoint).Vector();
}


UAudioComponent* ASWeapon::PlayWeaponSound(USoundCue* SoundToPlay)
{
	UAudioComponent* AC = nullptr;
	if (SoundToPlay && MyPawn)
	{
		AC = UGameplayStatics::SpawnSoundAttached(SoundToPlay, MyPawn->GetRootComponent());
	}

	return AC;
}


EWeaponState ASWeapon::GetCurrentState() const
{
	return CurrentState;
}



void ASWeapon::SetWeaponState(EWeaponState NewState)
{
	const EWeaponState PrevState = CurrentState;

	if (PrevState == EWeaponState::Firing && NewState == EWeaponState::Firing && !CanFire())
	{
		OnBurstFinished();
	}
	
	if (PrevState == EWeaponState::Firing && NewState != EWeaponState::Firing)
	{
		OnBurstFinished();
	}

	CurrentState = NewState;

	if (PrevState != EWeaponState::Firing && NewState == EWeaponState::Firing && CanFire())
	{
		OnBurstStarted();
	}
}


void ASWeapon::OnBurstStarted()
{
	// Start firing, can be delayed to satisfy TimeBetweenShots
	const float GameTime = GetWorld()->GetTimeSeconds();
	if (LastFireTime > 0 && TimeBetweenShots > 0.0f &&
		LastFireTime + TimeBetweenShots > GameTime)
	{
		GetWorldTimerManager().SetTimer(TimerHandle_HandleFiring, this, &ASWeapon::HandleFiring, LastFireTime + TimeBetweenShots - GameTime, false);
	}
	else
	{
		HandleFiring();
	}
}


void ASWeapon::OnBurstFinished()
{
	BurstCounter = 0;

	if (GetNetMode() != NM_DedicatedServer)
	{
		StopSimulatingWeaponFire();
	}

	GetWorldTimerManager().ClearTimer(TimerHandle_HandleFiring);
	bRefiring = false;
	SemiFire = false;
}


void ASWeapon::DetermineWeaponState()
{
	EWeaponState NewState = EWeaponState::Idle;

	if (bIsEquipped)
	{
		if (bPendingReload)
		{
			if (CanReload())
			{
				NewState = EWeaponState::Reloading;
			}
			else
			{
				NewState = CurrentState;
			}
		}
		else if (!bPendingReload && bWantsToFire)
		{
			
				NewState = EWeaponState::Firing;
				
		}
	
	}
	else if (bPendingEquip)
	{
		NewState = EWeaponState::Equipping;
	}

	SetWeaponState(NewState);
}


float ASWeapon::GetEquipStartedTime() const
{
	return EquipStartedTime;
}


float ASWeapon::GetEquipDuration() const
{
	return EquipDuration;
}


float ASWeapon::PlayWeaponAnimation(UAnimMontage* Animation, float InPlayRate, FName StartSectionName)
{
	float Duration = 0.0f;
	if (MyPawn)
	{
		if (Animation)
		{
			Duration = MyPawn->PlayAnimMontage(Animation, InPlayRate, StartSectionName);
		}
	}

	return Duration;
}


void ASWeapon::StopWeaponAnimation(UAnimMontage* Animation)
{
	if (MyPawn)
	{
		if (Animation)
		{
			MyPawn->StopAnimMontage(Animation);
		}
	}
}


void ASWeapon::OnEquipFinished()
{
	AttachMeshToPawn();

	bIsEquipped = true;
	bPendingEquip = false;

	DetermineWeaponState();

	if (MyPawn)
	{
		// Try to reload empty clip
		if (MyPawn->IsLocallyControlled() &&
			CurrentAmmoInClip <= 0 &&
			CanReload())
		{
			StartReload();
		}
	}
}



void ASWeapon::UseAmmo()
{
	CurrentAmmoInClip--;
	CurrentAmmo--;
	if (MyPawn)
	{
		switch (AmmoType)
		{
			case EAmmoType::Rifle:
				MyPawn->SubtractRifleAmmo(1);

			break;
			case EAmmoType::Shotgun:
			break;
			case EAmmoType::Pistol:
			break;
			case EAmmoType::SniperRifle:
			break;
			default:
			break;
		}
	}
	
}


int32 ASWeapon::GiveAmmo(int32 AddAmount)
{
	const int32 MissingAmmo = FMath::Max(0, MaxAmmo - CurrentAmmo);
	AddAmount = FMath::Min(AddAmount, MissingAmmo);
	CurrentAmmo += AddAmount;

	/* Push reload request to client */
	if (GetCurrentAmmoInClip() <= 0 && CanReload() &&
		MyPawn->GetCurrentWeapon() == this)
	{
		ClientStartReload();
	}

	/* Return the unused ammo when weapon is filled up */
	return FMath::Max(0, AddAmount - MissingAmmo);
}


void ASWeapon::SetAmmoCount(int32 NewTotalAmount)
{
	CurrentAmmo = FMath::Min(MaxAmmo, NewTotalAmount);
	CurrentAmmoInClip = FMath::Min(MaxAmmoPerClip, CurrentAmmo);
}


int32 ASWeapon::GetCurrentAmmo() const
{
	return CurrentAmmo;
}


int32 ASWeapon::GetCurrentAmmoInClip() const
{
	return CurrentAmmoInClip;
}


int32 ASWeapon::GetMaxAmmoPerClip() const
{
	return MaxAmmoPerClip;
}


int32 ASWeapon::GetMaxAmmo() const
{
	return MaxAmmo;
}


void ASWeapon::StartReload(bool bFromReplication)
{
	/* Push the request to server */
	if (!bFromReplication && Role < ROLE_Authority)
	{
		ServerStartReload();
	}

	/* If local execute requested or we are running on the server */
	if (bFromReplication || CanReload())
	{
		bPendingReload = true;
		DetermineWeaponState();

		float AnimDuration = PlayWeaponAnimation(ReloadAnim);
		if (AnimDuration <= 0.0f)
		{
			AnimDuration = NoAnimReloadDuration;
		}

		GetWorldTimerManager().SetTimer(TimerHandle_StopReload, this, &ASWeapon::StopSimulateReload, AnimDuration, false);
		if (Role == ROLE_Authority)
		{
			GetWorldTimerManager().SetTimer(TimerHandle_ReloadWeapon, this, &ASWeapon::ReloadWeapon, FMath::Max(0.1f, AnimDuration - 0.1f), false);
		}

		if (MyPawn && MyPawn->IsLocallyControlled())
		{
			PlayWeaponSound(ReloadSound);
		}
	}
}


void ASWeapon::StopSimulateReload()
{
	if (CurrentState == EWeaponState::Reloading)
	{
		bPendingReload = false;
		DetermineWeaponState();
		StopWeaponAnimation(ReloadAnim);
	}
}


void ASWeapon::ReloadWeapon()
{
	int32 ClipDelta = FMath::Min(MaxAmmoPerClip - CurrentAmmoInClip, CurrentAmmo - CurrentAmmoInClip);

	if (ClipDelta > 0)
	{
		CurrentAmmoInClip += ClipDelta;
	}
}


bool ASWeapon::CanReload()
{
	bool bCanReload = (!MyPawn || MyPawn->CanReload());
	bool bGotAmmo = (CurrentAmmoInClip < MaxAmmoPerClip) && ((CurrentAmmo - CurrentAmmoInClip) > 0);
	bool bStateOKToReload = ((CurrentState == EWeaponState::Idle) || (CurrentState == EWeaponState::Firing));
	return (bCanReload && bGotAmmo && bStateOKToReload);
}


void ASWeapon::OnRep_Reload()
{
	if (bPendingReload)
	{
		/* By passing true we do not push back to server and execute it locally */
		StartReload(true);
	}
	else
	{
		StopSimulateReload();
	}
}


void ASWeapon::ServerStartReload_Implementation()
{
	StartReload();
}


bool ASWeapon::ServerStartReload_Validate()
{
	return true;
}


void ASWeapon::ServerStopReload_Implementation()
{
	StopSimulateReload();
}


bool ASWeapon::ServerStopReload_Validate()
{
	return true;
}


void ASWeapon::ClientStartReload_Implementation()
{
	StartReload();
}


Hey,

ok this is how i understand this class:

When a weapon is spawned it does not automatically belongs to a pawn, since its a survival game it reminds of of DayZ and it makes sense because its lying on the ground somewhere.

The only cases where a Pawn object actually is avaialable (Not Null) is when the Function



void ASWeapon::OnEnterInventory(ASCharacter* NewOwner)
{
	SetOwningPawn(NewOwner);
	AttachMeshToPawn(StorageSlot);
}


got called. I assume this happen inside the ASCharacter class if its trying to pickup an item on the ground by calling


theWeapon->OnEnterInventory(this);

and when the weapon is dropped from a pawn OnLeaveInventory is called which cleans the MyPawn variable again and makes it nullptr.
In the end it is not intended to access MyPawn during construction or initialization.

So to get your initialization to work you would need to move your code after OnEnterInventory.

Thank you so much, this worked perfectly.

glad to hear that :wink: