>>.... Mobile Spawn Algorithm.... Header File

-> NOTE: THIS IS RAW SOURCE

>>.... Mobile Spawn Algorithm... CPP File

//-----------------------------------------------------------------------------------------------------------------
// Simnply Spawns an object in the game world for a simple mobile game.						  -
// This was the verticle slice build to test some other systems for the game concept.		    	          -
// Nicholas Mallonee								                                  -
//-----------------------------------------------------------------------------------------------------------------
#pragma once

//-----------------------------------------------------------------------------------------------------------------
// Includes and Libraries																		  -
//-----------------------------------------------------------------------------------------------------------------
#include "GameFramework/Actor.h"
#include "Code_Base/Orbs/Orb.h"
#include "Runtime/Engine/Public/TimerManager.h"
#include "Spawner.generated.h"

//-----------------------------------------------------------------------------------------------------------------
// Spawner States	-- Enum																				  -
//-----------------------------------------------------------------------------------------------------------------
UENUM()
enum class ESpawnerState : uint8
{
	Random,
	Trail, 
	EveryOther
};

//-----------------------------------------------------------------------------------------------------------------
// Spawner Struct                                                                                                 -
//-----------------------------------------------------------------------------------------------------------------
USTRUCT(BlueprintType)
struct FSpawnerInformation
{
	GENERATED_USTRUCT_BODY()

	// -- Private -- Struct information 
private:
	UPROPERTY()
	ESpawnerState state;

	UPROPERTY()
	TArray lastUsedLocations;

	UPROPERTY()
	int32 spawnCount;

	// -- Public -- Accessors, Mutators, Other methods, and constuctor
public:
	/*
	 * Takes a state to change the spawner to
	 * @Param  The state that the spawner should be changed to
	 */
	void setState(ESpawnerState inState)
	{
		state = inState;
	}
		
	/* 
	 * Adds a Vector to the struct's array 
	 * @Param The Vector in which to add to the array	
	 */
	void addLocation(FVector loc)
	{
		if (lastUsedLocations.Num() > 10)
		{
			lastUsedLocations.RemoveAt(0);
			lastUsedLocations.Add(loc);
		}
		else
		{
			lastUsedLocations.Add(loc);
		}	
	}

	/*
	 * Changes the number of Spawn Count
	 * @Param Number to change the spawn count to 
	 */
	void setSpawnCount(int32 in)
	{
		spawnCount = in;
	}

	/* 
	 * Returns the state of the spawners behavior
	 * @Return the current state of the spawner
	 */
	ESpawnerState getState()
	{
		return state;
	}

	/*
	 * @Param Takes in a position to return 
	 * @Return The Vector at a given Position	
	 */
	FVector getLocation(int32 pos)
	{
		return lastUsedLocations[pos];	
	}
	
	/* 
	 * Returns the current count of how many times a spawn method has been called
	 * Since the last time it switched.
	 * @Return the int32 value of many times a metod was called 	
	 */
	int32 getCount()
	{
		return spawnCount;	
	}

	/* 
	 * Returns the array's logical size 
	 * @Returns the arrays size + 1
	 */
	int32 getArraySize()
	{
		return lastUsedLocations.Num();
	}

	/*
	 * Checks for an exact x in the array using a linear search 
	 * Note: This Method was never used and was later removed.  
	 * @Returns If an X was found
	 */
	bool isXInArray(float x)
	{
		bool flag = false;			

		return flag;
	}

	/* 
	 * Destroyes the objects to Gives the Memory back to the system	  
	 */
	void destroy()
	{
		lastUsedLocations.~TArray();
	}

	// -- Default Constructor
	FSpawnerInformation()
	{
		state = ESpawnerState::Random;
		spawnCount = 0;
		lastUsedLocations.SetNum(0);
	}
};

//-----------------------------------------------------------------------------------------------------------------
// Spawner Class Information																					  -
//-----------------------------------------------------------------------------------------------------------------
UCLASS()
class BERYL_API ASpawner : public AActor
{
	GENERATED_BODY()
	

	// -- Public Information -- Constructor and Engine Events -- // 
public:	
	ASpawner();

	virtual void BeginPlay() override;
	
	virtual void Tick( float DeltaSeconds ) override;

	virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override;

	// -- Private Information -- Spawning Methods -- // 
private:
	void determineSpawnPath();				

	void spawnInRandomSpot();

	void spawnUsingGeneratedX(float inX);

	// -- Private Information -- Number Generation and Check -- // 
private:
	bool canUseX(float inX);

	float generateNewRandomX();

	float generateTrailX();

	float generateEveryOther();

	// -- Private Information -- Orbs to Spawn -- // 
private:
	UPROPERTY(EditDefaultsOnly, Category = "Basic Orb")
	TSubclassOf basicOrb;

	// -- Private Information -- Spawner Information Struct -- //
private:
	FSpawnerInformation spawnerInformation; 	

	// -- Private Information -- Timers -- // 
private:
	FTimerManager spawnTimer; 

	FTimerHandle spawnTimerHandler; 
};

//-----------------------------------------------------------------------------------------------------------------
// Simnply Spawns an object in the game world for a simple mobile game.                                           -
// This was the verticle slice build to test some other systems for the game concept.                             -
// Nicholas Mallonee                                                                                              -
//-----------------------------------------------------------------------------------------------------------------


//-----------------------------------------------------------------------------------------------------------------
// Includes and Libraries                                                                                         -
//-----------------------------------------------------------------------------------------------------------------
#include "Beryl.h"
#include "Engine.h"
#include "Spawner.h"


//-----------------------------------------------------------------------------------------------------------------
// Constructors                                                                                                   -
//-----------------------------------------------------------------------------------------------------------------
ASpawner::ASpawner()
{
    // Allow the actor to tick
    PrimaryActorTick.bCanEverTick = true;
}


//-----------------------------------------------------------------------------------------------------------------
// Engine Events -
//-----------------------------------------------------------------------------------------------------------------
void ASpawner::BeginPlay()
{
    Super::BeginPlay();
   

    // Set the handler
    GetWorld()->GetTimerManager().SetTimer(spawnTimerHandler, this, &ASpawner::determineSpawnPath, .15f, true);
}


void ASpawner::Tick( float DeltaTime )
{
    Super::Tick( DeltaTime );
}


void ASpawner::EndPlay(const EEndPlayReason::Type EndPlayReason)
{
    // destroy the array in the struct
    spawnerInformation.destroy();

    // Clear Any Timers that are live -- Old way, removed after 4.7
    //GetWorldTimerManager().ClearTimer(this, &ASpawner::spawnInRandomSpot);

    // Clear Any Timers that are live -- new way
    GetWorld()->GetTimerManager().ClearTimer(spawnTimerHandler);

    // If we wanted to clear all the timers. -- safe way to remove all
    //UWorld* const world = GetWorld();
    //if (world)
    //{
        // GetWorld()->GetTimerManager().ClearAllTimersForObject(this);
    //}

    Super::EndPlay(EndPlayReason);
}
//-----------------------------------------------------------------------------------------------------------------
// Basic Spawn Methods                                                                                            -
//-----------------------------------------------------------------------------------------------------------------
void ASpawner::determineSpawnPath()
{
    // Check the current state
    if (spawnerInformation.getState() == ESpawnerState::Random)
    {
        // Check the last time it was changed, if it is above 50 switch the state
       if (spawnerInformation.getCount() >= 50)
       {
           int32 rand = FMath::RandRange(0, 10); // Random number to choose the weighted next state

           if (rand >= 0 && rand < 7) // 0 through 6
           {
               spawnerInformation.setState(ESpawnerState::Trail); // Change the state to trails
               spawnerInformation.setSpawnCount(0); // Set the spawn count back to 0

              // Ignore the check for spaces and quick generate a place to spawn
              spawnInRandomSpot();
           }
           else if (rand >= 7 && rand < 10)
           {
              spawnerInformation.setState(ESpawnerState::Random); // Change the state to random again
              spawnerInformation.setSpawnCount(0); // Set the spawn count back to 0

              // Ignore the check for spaces and quick generate a place to spawn
              spawnInRandomSpot();
           }
           else
           {
              spawnerInformation.setState(ESpawnerState::EveryOther); // Change the state to everyother
              spawnerInformation.setSpawnCount(0); // Set the spawn count back to 0

              // Ignore the check for spaces and quick generate a place to spawn so we can go back around
              spawnInRandomSpot();
           }
       }
       else // Else we know we can check and spawn an object
       {
           float newX = generateNewRandomX(); // Generate a new X to use
           spawnerInformation.setSpawnCount((spawnerInformation.getCount() + 1)); // Add to the count
           spawnUsingGeneratedX(newX); // Spawn the new object
       }
    }
    else if (spawnerInformation.getState() == ESpawnerState::Trail)
    {
        if (spawnerInformation.getCount() >= 5)
        {
            int32 rand = FMath::RandRange(0, 10); // Random number to choose the weighted next state
           

            if (rand >= 0 && rand < 7) // 0 through 6
            {
                spawnerInformation.setState(ESpawnerState::Random); // Change the state to trails
                spawnerInformation.setSpawnCount(0); // Set the spawn count back to 0

                // Ignore the check for spaces and quick generate a place to spawn
                spawnInRandomSpot();
            }
            else if (rand >= 7 && rand < 10)
            {
               spawnerInformation.setState(ESpawnerState::Trail); // Change the state to random again
               spawnerInformation.setSpawnCount(0); // Set the spawn count back to 0

               // Ignore the check for spaces and quick generate a place to spawn
               spawnInRandomSpot();
            }
            else
            {
               spawnerInformation.setState(ESpawnerState::EveryOther); // Change the state to everyother
               spawnerInformation.setSpawnCount(0); // Set the spawn count back to 0

               // Ignore the check for spaces and quick generate a place to spawn so we can go back around
               spawnInRandomSpot();
            }
        }
       else
       {
            float newX = generateTrailX(); // Generate a new X to use
           

            spawnerInformation.setSpawnCount((spawnerInformation.getCount() + 1)); // Add to the count
            spawnUsingGeneratedX(newX); // Spawn the new object
       }
   }
   else if (spawnerInformation.getState() == ESpawnerState::EveryOther)
   {
       if (spawnerInformation.getCount() >= 7)
       {
           int32 rand = FMath::RandRange(0, 10); // Random number to choose the weighted next state
          

           if (rand >= 0 && rand < 7) // 0 through 6
           {
               spawnerInformation.setState(ESpawnerState::Random); // Change the state to trails
               spawnerInformation.setSpawnCount(0); // Set the spawn count back to 0
 

               // Ignore the check for spaces and quick generate a place to spawn
               spawnInRandomSpot();
           }
           else if (rand >= 7 && rand < 10)
           {
               spawnerInformation.setState(ESpawnerState::Trail); // Change the state to random again
               spawnerInformation.setSpawnCount(0); // Set the spawn count back to 0
              

               // Ignore the check for spaces and quick generate a place to spawn
               spawnInRandomSpot();
           }
           else
           {
               spawnerInformation.setState(ESpawnerState::EveryOther); // Change the state to everyother
               spawnerInformation.setSpawnCount(0); // Set the spawn count back to 0
              

               // Ignore the check for spaces and quick generate a place to spawn so we can go back around
               spawnInRandomSpot();
           }
       }
       else
       {
           float newX = generateEveryOther(); // Generate a new X to use
           spawnerInformation.setSpawnCount((spawnerInformation.getCount() + 1)); // Add to the count
           spawnUsingGeneratedX(newX); // Spawn the new object
       }
   }
   else
   {
       spawnerInformation.setState(ESpawnerState::Random); // Change the state to trails
       spawnerInformation.setSpawnCount(0); // Set the spawn count back to 0
 

       // Ignore the check for spaces and quick generate a place to spawn
      spawnInRandomSpot();
   }
}

void ASpawner::spawnInRandomSpot()
{
    // Check for world and have it as a const
    UWorld* const world = GetWorld();

    // if the world exists try to spawn the object
    if (world)
    {
        // Generate an X to use
        float x = FMath::RandRange(-270, 270);

        // generate to the left of the player
        FVector loc = FVector(0.f, x, 500.f);

        // set the spawn params
        FActorSpawnParameters spawnParams;
        spawnParams.Owner = this;
        spawnParams.Instigator = Instigator;

        // Spawn the actor
        GetWorld()->SpawnActor<AOrb>(basicOrb, loc, FRotator::ZeroRotator, spawnParams);
    }
}

void ASpawner::spawnUsingGeneratedX(float inX)
{
    // Check for world and have it as a const
    UWorld* const world = GetWorld();

    // if the world exists try to spawn the object
    if (world)
    {
        // Package the X into a vector
        FVector loc = FVector(0.f, inX, 500.f);

        // Save the packaged Vector
        spawnerInformation.addLocation(loc);

        // set the spawn params
        FActorSpawnParameters spawnParams;
        spawnParams.Owner = this;
        spawnParams.Instigator = Instigator;

        // Spawn the actor
        GetWorld()->SpawnActor<AOrb>(basicOrb, loc, FRotator::ZeroRotator, spawnParams);
    }
}


//-----------------------------------------------------------------------------------------------------------------
// Basic Number Generation and checks                                                                             -
//-----------------------------------------------------------------------------------------------------------------
bool ASpawner::canUseX(float inX)
{
    if (spawnerInformation.getArraySize() > 5)
    {
        if (spawnerInformation.isXInArray(inX)) // if the exact X was found return
            return false;
        else
            return true;
    }
    else
        return true;
}


float ASpawner::generateNewRandomX()
{
    return FMath::RandRange(-270.f, 270.f);
}

float ASpawner::generateTrailX()
{
    // Get the last location and the X from that
    FVector last;

    float lastX = 0.f;
    if (spawnerInformation.getArraySize() >= 10)
    {
        last = spawnerInformation.getLocation((9));
        lastX = last.X;
    }
    else if (spawnerInformation.getArraySize() >= 1)
    {
        last = spawnerInformation.getLocation((spawnerInformation.getArraySize() - 1));
        lastX = last.X;
    }
    else
        lastX = 0.f;

    if (lastX <= -260.f || lastX >= 260.f)
    {
        if (lastX <= -260.f)
            lastX = lastX + 50.f;
        else if (lastX >= 260.f)
            lastX = lastX - 50.f;
    }
    else
    {
        int32 rand = FMath::RandRange(0, 1);
     

        if (rand == 0)
            lastX = lastX - 40.f;
        else
            lastX = lastX + 40.f;
    }

    return lastX;
}

float ASpawner::generateEveryOther()
{
    // Get the last location and the X from that
    FVector last;

    float lastX = 0.f;

    if (spawnerInformation.getArraySize() >= 10)
    {
        last = spawnerInformation.getLocation((9));
        lastX = last.X;
    }
    else if (spawnerInformation.getArraySize() >= 1)
    {
        last = spawnerInformation.getLocation((spawnerInformation.getArraySize() - 1));
        lastX = last.X;
    }
    else
        lastX = 150.f;

    if (lastX < 0.f && lastX != 0.f)
        lastX = lastX * (-1);
    else if (lastX > 0.f && lastX != 0.f)
        lastX = lastX * (-1);
   else
        lastX = 150.f;

    return lastX;

}