TArray Storing Ammo Data Help

Hello! I am having struggles with understanding how I would be able to store my Weapon data properly in my Arrays.

  1. I’m having issues having the Array and CurrentWeapon pointer figure out how to add my ammo properly
  2. Issues with when spawning from the array to show the correct CurrentAmmo and CurrentClip

Any Idea what’s happening or what I am doing wrong?

character.cpp

void ATesterCharacter::BeginPlay()
{
	GiveDefaultWeapons();
}

void ATesterCharacter::GiveDefaultWeapons()
{
	WeapInventory[0] = Knife_D;
	AWeapon *Spawner = GetWorld()->SpawnActor<AWeapon>(WeapInventory[0]);
	if (Spawner)
	{
		GEngine->AddOnScreenDebugMessage(-1, 2.f, FColor::Black, "Spawned");
		CurrentWeapon = Spawner;
		Spawner->AttachRootComponentTo(GetMesh1P(), "WeapSocket", EAttachLocation::SnapToTarget);
	}

}

//////////////////////////////////////////////////////////////////////////
// Input

void ATesterCharacter::SetupPlayerInputComponent(class UInputComponent* InputComponent)
{
	// set up gameplay key bindings
	check(InputComponent);

	InputComponent->BindAction("Jump", IE_Pressed, this, &ACharacter::Jump);
	InputComponent->BindAction("Jump", IE_Released, this, &ACharacter::StopJumping);
	InputComponent->BindAction("PreviousWeapon", IE_Pressed, this, &ATesterCharacter::PrevWeapon);
	InputComponent->BindAction("NextWeapon", IE_Pressed, this, &ATesterCharacter::NextWeapon);
	
	InputComponent->BindAction("Fire", IE_Pressed, this, &ATesterCharacter::OnFire);
	InputComponent->BindTouch(EInputEvent::IE_Pressed, this, &ATesterCharacter::TouchStarted);

	InputComponent->BindAxis("MoveForward", this, &ATesterCharacter::MoveForward);
	InputComponent->BindAxis("MoveRight", this, &ATesterCharacter::MoveRight);
	
	// We have 2 versions of the rotation bindings to handle different kinds of devices differently
	// "turn" handles devices that provide an absolute delta, such as a mouse.
	// "turnrate" is for devices that we choose to treat as a rate of change, such as an analog joystick
	InputComponent->BindAxis("Turn", this, &APawn::AddControllerYawInput);
	InputComponent->BindAxis("TurnRate", this, &ATesterCharacter::TurnAtRate);
	InputComponent->BindAxis("LookUp", this, &APawn::AddControllerPitchInput);
	InputComponent->BindAxis("LookUpRate", this, &ATesterCharacter::LookUpAtRate);
}

void ATesterCharacter::OnFire()
{
	if (CurrentWeapon != NULL)
	{
		CurrentWeapon->Fire();
	}
}

void ATesterCharacter::OnCollision(AActor *OtherActor, UPrimitiveComponent *OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult &SweepResult)
{
	AWeapon *Weapon = Cast<AWeapon>(OtherActor);
	if (Weapon)
	{
		ProcessWeaponPickup(Weapon);
	}
}

void ATesterCharacter::ProcessWeaponPickup(AWeapon *Weapon)
{
	if (Weapon != NULL)
	{
		//Check to see if Weapon is not currently in the priority slot in Array (basically checks to see if weapon is in the array)
		if (WeapInventory[Weapon->WeapConfig.Priority] != Weapon->GetClass())
		{
			//Insert Weapon in correct slot by priority
			WeapInventory.Insert(Weapon->GetClass(), Weapon->WeapConfig.Priority);
			GEngine->AddOnScreenDebugMessage(-1, 2.f, FColor::Black, "You Just picked up a " + WeapInventory[Weapon->WeapConfig.Priority]->GetDefaultObject<AWeapon>()->WeapConfig.Name);

			//Checks to see if the weapon is a higher priority number
			if (Weapon->WeapConfig.Priority > WeapInventory[CurrentWeapon->WeapConfig.Priority]->GetDefaultObject<AWeapon>()->WeapConfig.Priority)
			{
				//Equip weapon with the higher priority
				EquipWeapon(WeapInventory[Weapon->WeapConfig.Priority]);
			}
			//Destroy the picked up weapon on the scene
			Weapon->Destroy();
		}
		
		else
		{
			if (!WeapInventory[Weapon->WeapConfig.Priority]->GetDefaultObject<AWeapon>()->WeapConfig.bIsEquipped)
			{
				if (WeapInventory[Weapon->WeapConfig.Priority]->GetDefaultObject<AWeapon>()->CurrentAmmo >= 0 && Weapon->CurrentAmmo <= (WeapInventory[Weapon->WeapConfig.Priority]->GetDefaultObject<AWeapon>()->WeapConfig.MaxAmmo - WeapInventory[Weapon->WeapConfig.Priority]->GetDefaultObject<AWeapon>()->CurrentAmmo))
				{
					WeapInventory[Weapon->WeapConfig.Priority]->GetDefaultObject<AWeapon>()->CurrentAmmo += Weapon->CurrentAmmo;
					GEngine->AddOnScreenDebugMessage(-1, 2.f, FColor::Black, "Added " + Weapon->CurrentAmmo);
					Weapon->Destroy();
				}
				else
				{
					GEngine->AddOnScreenDebugMessage(-1, 2.f, FColor::Black, "Full Ammo on " + WeapInventory[Weapon->WeapConfig.Priority]->GetDefaultObject<AWeapon>()->WeapConfig.Name);
				}
			}

			else
			{
				if (CurrentWeapon->CurrentAmmo >= 0 && Weapon->CurrentAmmo <= (CurrentWeapon->WeapConfig.MaxAmmo - CurrentWeapon->CurrentAmmo))
				{
					CurrentWeapon->CurrentAmmo += Weapon->CurrentAmmo;
					GEngine->AddOnScreenDebugMessage(-1, 2.f, FColor::Black, "Added " + Weapon->CurrentAmmo);
				}
				if (CurrentWeapon->CurrentAmmo == CurrentWeapon->WeapConfig.MaxAmmo)
				{
					GEngine->AddOnScreenDebugMessage(-1, 2.f, FColor::Black, "Full");
				}
				else
				{
					CurrentWeapon->CurrentAmmo = CurrentWeapon->WeapConfig.MaxAmmo;
				}
			}
			
		}
	}
}

void ATesterCharacter::ProcessItemPickup(AItem *Item)
{

}

void ATesterCharacter::NextWeapon()
{
	if (GetNextWeapon() != NULL && GetNextWeapon()->GetDefaultObject<AWeapon>() != CurrentWeapon)
	{
		EquipWeapon(GetNextWeapon());
		WeapInventory[CurrentWeapon->WeapConfig.Priority] = CurrentWeapon->GetClass();
	}
}

void ATesterCharacter::PrevWeapon()
{
	if (GetPreviousWeapon() != NULL && CurrentWeapon != GetPreviousWeapon()->GetDefaultObject<AWeapon>())
	{
		EquipWeapon(GetPreviousWeapon());
		WeapInventory[CurrentWeapon->WeapConfig.Priority] = CurrentWeapon->GetClass();
	}
}

TSubclassOf<AWeapon> ATesterCharacter::GetPreviousWeapon()
{
	if (WeapInventory[CurrentWeapon->WeapConfig.Priority - 1] == NULL)
	{
		return WeapInventory[CurrentWeapon->WeapConfig.Priority];
	}
	else
	{
		return WeapInventory[CurrentWeapon->WeapConfig.Priority - 1];
	}
}

TSubclassOf<AWeapon> ATesterCharacter::GetNextWeapon()
{
	if (WeapInventory[CurrentWeapon->WeapConfig.Priority + 1] == NULL)
	{
		return WeapInventory[CurrentWeapon->WeapConfig.Priority];
	}
	else
	{
		return WeapInventory[CurrentWeapon->WeapConfig.Priority + 1];
	}
}

void ATesterCharacter::EquipWeapon(TSubclassOf<AWeapon> Weapon)
{
	if (CurrentWeapon != NULL)
	{
		CurrentWeapon->Destroy();
		Weapon->GetDefaultObject<AWeapon>()->WeapConfig.bIsEquipped = true;
		AWeapon *Spawner = GetWorld()->SpawnActor<AWeapon>(Weapon);
		if (Spawner)
		{
			Spawner->CollisionComp->SetCollisionEnabled(ECollisionEnabled::NoCollision);
			GEngine->AddOnScreenDebugMessage(-1, 2.f, FColor::Black, "Spawned");
			CurrentWeapon = Spawner;
			Spawner->AttachRootComponentTo(GetMesh1P(), "WeapSocket", EAttachLocation::SnapToTarget);
		}
	}
}

character.h

// Copyright 1998-2014 Epic Games, Inc. All Rights Reserved.
#pragma once
#include "GameFramework/Character.h"
#include "Weapon.h"
#include "Item.h"
#include "Knife.h"
#include "TesterCharacter.generated.h"

UCLASS(config=Game)
class ATesterCharacter : public ACharacter
{
	GENERATED_BODY()

	/** Pawn mesh: 1st person view (arms; seen only by self) */
	UPROPERTY(VisibleDefaultsOnly, Category=Mesh)
	class USkeletalMeshComponent* Mesh1P;

	UPROPERTY(VisibleDefaultsOnly, Category = Collision)
	class UBoxComponent *CollisionComp;

	/** First person camera */
	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Camera, meta = (AllowPrivateAccess = "true"))
	class UCameraComponent* FirstPersonCameraComponent;
public:
	ATesterCharacter(const FObjectInitializer& ObjectInitializer);

	/** Base turn rate, in deg/sec. Other scaling may affect final turn rate. */
	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category=Camera)
	float BaseTurnRate;

	/** Base look up/down rate, in deg/sec. Other scaling may affect final rate. */
	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category=Camera)
	float BaseLookUpRate;

	/** Gun muzzle's offset from the characters location */
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category=Gameplay)
	FVector GunOffset;

	/** Projectile class to spawn */
	UPROPERTY(EditDefaultsOnly, Category=Projectile)
	TSubclassOf<class ATesterProjectile> ProjectileClass;

	/** Sound to play each time we fire */
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category=Gameplay)
	class USoundBase* FireSound;

	/** AnimMontage to play each time we fire */
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Gameplay)
	class UAnimMontage* FireAnimation;

	//Stores Currently Equiped Weapon Information(to access functions, variables, etc)
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Weapon)
	AWeapon *CurrentWeapon;

	//Inventory for the Weapons
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Inventory)
	TArray<TSubclassOf<AWeapon>> WeapInventory;

	//Inventory for Key Items
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Inventory)
	TArray<TSubclassOf<AItem>> ItemInventory;

	TSubclassOf<AKnife> Knife_D;

	void GiveDefaultWeapons();

	//Tells Inventory if that weapon is already in inventory. If it is, the function will then add ammo. If ammo is full, it will then not process the pickup
	void ProcessWeaponPickup(AWeapon *Weapon);

	TSubclassOf<AWeapon> GetPreviousWeapon();

	TSubclassOf<AWeapon> GetNextWeapon();

	void NextWeapon();

	void PrevWeapon();

	void ProcessItemPickup(AItem *Item);

	void EquipWeapon(TSubclassOf<AWeapon> Weapon);

	virtual void BeginPlay() override;

protected:

	/** Handler for a touch input beginning. */
	void TouchStarted(const ETouchIndex::Type FingerIndex, const FVector Location);

	/** Fires a projectile. */
	void OnFire();

	/** Handles moving forward/backward */
	void MoveForward(float Val);

	/** Handles stafing movement, left and right */
	void MoveRight(float Val);

	/**
	 * Called via input to turn at a given rate.
	 * @param Rate	This is a normalized rate, i.e. 1.0 means 100% of desired turn rate
	 */
	void TurnAtRate(float Rate);

	/**
	 * Called via input to turn look up/down at a given rate.
	 * @param Rate	This is a normalized rate, i.e. 1.0 means 100% of desired turn rate
	 */
	void LookUpAtRate(float Rate);

protected:
	// APawn interface
	virtual void SetupPlayerInputComponent(class UInputComponent* InputComponent) override;
	// End of APawn interface

	UFUNCTION()
	void OnCollision(AActor *OtherActor, UPrimitiveComponent *OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult &SweepResult);

public:
	/** Returns Mesh1P subobject **/
	FORCEINLINE class USkeletalMeshComponent* GetMesh1P() const { return Mesh1P; }
	/** Returns FirstPersonCameraComponent subobject **/
	FORCEINLINE class UCameraComponent* GetFirstPersonCameraComponent() const { return FirstPersonCameraComponent; }
};

weapon.h

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

#pragma once

#include "GameFramework/Actor.h"
#include "Weapon.generated.h"

#define TRACE_WEAPON ECC_GameTraceChannel1

UENUM(BlueprintType)
namespace EProjectile
{
	enum Type
	{
		E_Bullet			UMETA(DisplayName = "Bullet"),
		E_Projectile		UMETA(DisplayName = "Projectile"),
		E_Melee				UMETA(DisplayName = "Melee"),
	};
}

USTRUCT(BlueprintType)
struct FWeaponConfig
{
	GENERATED_USTRUCT_BODY()

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Config)
	int32 MaxAmmo;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Config)
	int32 MaxClip;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Config)
	float WeaponRange;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Config)
	float TimeBetweenShots;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Ammo)
	int32 ShotCost;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Config)
	float WeaponSpread;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Config)
	FString Name;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Config)
	UTexture2D* SplashArt;

	//Use of Priority Makes the picking up of weapon and equiping them automatically sort them in TesterCharacter
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Config)
	int32 Priority;

	//Checks to see if weapon is equipped
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Config)
	bool bIsEquipped;
};

/**
 * 
 */
UCLASS()
class Tester_API AWeapon : public AActor
{
	GENERATED_UCLASS_BODY()

	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Mesh)
	USkeletalMeshComponent *Mesh;

	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Collision)
	UBoxComponent *CollisionComp;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Config)
	FWeaponConfig WeapConfig;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Config)
	TEnumAsByte<EProjectile::Type> ProjType;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Config)
	int32 CurrentAmmo;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Config)
	int32 CurrentClip;

	void Fire();
	void ReloadAmmo();

	virtual void Instant_Fire();
	virtual void ProjectileFire();
	virtual void MeleeFire();

protected:
	FHitResult WeaponTrace(const FVector &TraceFrom, const FVector &TraceTo) const;

	void ProcessInstantHit(const FHitResult &Impact, const FVector &Origin, const FVector &ShootDir, int32 RandomSeed, float ReticleSpread);
	
};

weapon.cpp

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

#include "Tester.h"
#include "Weapon.h"


AWeapon::AWeapon(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
	Mesh = ObjectInitializer.CreateDefaultSubobject<USkeletalMeshComponent>(this, "Mesh");
	RootComponent = Mesh;

	CollisionComp = ObjectInitializer.CreateDefaultSubobject<UBoxComponent>(this, "CollisionComp");
	CollisionComp->AttachParent = Mesh;
}

void AWeapon::Fire()
{
	if (CurrentClip > 0)
	{
		CurrentClip -= WeapConfig.ShotCost;
		switch (ProjType)
		{
		case EProjectile::E_Bullet:
			Instant_Fire();
			break;
		case EProjectile::E_Projectile:
			ProjectileFire();
			break;
		case EProjectile::E_Melee:
			MeleeFire();
		default:
			break;
		}
	}
	else
	{
		ReloadAmmo();
	}
}

void AWeapon::ReloadAmmo()
{
	if (CurrentAmmo > 0)
	{
		if (CurrentAmmo < WeapConfig.MaxClip)
		{
			CurrentClip = CurrentAmmo;
		}
		else
		{
			CurrentAmmo -= WeapConfig.MaxClip;
			CurrentClip += WeapConfig.MaxClip;
		}
	}
	else
	{
		GEngine->AddOnScreenDebugMessage(-1, 2.f, FColor::Blue, "No Ammo Left");
	}
}

FHitResult AWeapon::WeaponTrace(const FVector &TraceFrom, const FVector &TraceTo) const
{
	static FName WeaponFireTag = FName(TEXT("WeaponTrace"));

	FCollisionQueryParams TraceParams(WeaponFireTag, true, Instigator);
	TraceParams.bTraceAsyncScene = true;
	TraceParams.bReturnPhysicalMaterial = true;
	TraceParams.AddIgnoredActor(this);

	FHitResult Hit(ForceInit);

	GetWorld()->LineTraceSingle(Hit, TraceFrom, TraceTo, TRACE_WEAPON, TraceParams);

	return Hit;
}

void AWeapon::ProcessInstantHit(const FHitResult &Impact, const FVector &Origin, const FVector &ShootDir, int32 RandomSeed, float ReticleSpread)
{
	const FVector EndTrace = Origin + ShootDir * WeapConfig.WeaponRange;
	const FVector EndPoint = Impact.GetActor() ? Impact.ImpactPoint : EndTrace;
	DrawDebugLine(this->GetWorld(), Origin, Impact.TraceEnd, FColor::Black, true, 10000, 10.f);
}

void AWeapon::Instant_Fire()
{
	// Get the camera transform
	FVector CameraLoc;
	FRotator CameraRot;
	GetActorEyesViewPoint(CameraLoc, CameraRot);
	const int32 RandomSeed = FMath::Rand();
	FRandomStream WeaponRandomStream(RandomSeed);
	const float CurrentSpread = WeapConfig.WeaponSpread;
	const float SpreadCone = FMath::DegreesToRadians(WeapConfig.WeaponSpread * 0.5);
	const FVector AimDir = Mesh->GetSocketRotation("MF").Vector();
	const FVector StartTrace = Mesh->GetSocketLocation("MF");
	const FVector ShootDir = WeaponRandomStream.VRandCone(AimDir, SpreadCone, SpreadCone);
	const FVector EndTrace = StartTrace + ShootDir * WeapConfig.WeaponRange;
	const FHitResult Impact = WeaponTrace(StartTrace, EndTrace);

	ProcessInstantHit(Impact, StartTrace, ShootDir, RandomSeed, CurrentSpread);
}

void AWeapon::ProjectileFire()
{

}

void AWeapon::MeleeFire()
{

}

In the constructor of the Pistol, I just make the WeapConfig.Priority = 1.and the knife is 0.

Another thing to keep in mind, On the level, there are a few pistols ready to pick up with preset currentclip and currentammo as well AND I’m using 4.6(but that portion shouldnt really matter except the different pointer stuff)

22922-issuepart5.jpg

Anybody? :frowning:

I’m curious if I should just run a parallel array with the weapons. one holding the ammo and another holding the weapon? but I’m unsure if this is a good way of doing this?

I got the answer at this thread Here Thank you to all who helped me!!!