2022-01-11 11:07:17 +01:00
// Fill out your copyright notice in the Description page of Project Settings.
# include "AdventureMap.h"
# include "HexTile.h"
# include "AdventureCameraPawn.h"
# include "AdventureCharacter.h"
# include "Kismet/GameplayStatics.h"
2022-01-16 18:33:43 +01:00
# include "Algo/Reverse.h"
2022-01-17 17:53:54 +01:00
# include <unordered_map>
2022-01-11 11:07:17 +01:00
// Sets default values
AAdventureMap : : AAdventureMap ( )
{
}
// Called when the game starts or when spawned
void AAdventureMap : : BeginPlay ( )
{
Super : : BeginPlay ( ) ;
2022-01-17 17:53:54 +01:00
World = GetWorld ( ) ;
2022-01-11 11:07:17 +01:00
if ( IsValid ( BaseTileClass ) )
{
MakeGrid ( ) ;
}
}
// Called once on Begin Play
void AAdventureMap : : MakeGrid ( )
{
FVector NextHexAt = FVector ( ) ;
float HexWidth = sqrt ( 3 ) * TileSize ;
int QOffset = 0 ;
for ( int r = 1 ; r < = GridSize ; r + + )
{
float XOffset = 0.f ;
2022-01-16 18:33:43 +01:00
if ( r % 2 ! = 0 )
2022-01-11 11:07:17 +01:00
{
2022-01-16 18:33:43 +01:00
if ( r > 1 )
{
QOffset - - ;
}
2022-01-11 11:07:17 +01:00
}
2022-01-16 18:33:43 +01:00
else { XOffset = HexWidth / 2 ; }
2022-01-11 11:07:17 +01:00
for ( int q = 1 ; q < = GridSize ; q + + )
{
NextHexAt . X = XOffset + ( HexWidth * q ) ;
NextHexAt . Y = TileSize * 1.5f * r ;
NextHexAt . Z = 0.f ;
FTransform SpawnTransform = FTransform ( NextHexAt ) ;
AHexTile * Tile = World - > SpawnActor < AHexTile > ( BaseTileClass , SpawnTransform ) ;
Tile - > Q = q - 1 + QOffset ;
Tile - > R = r - 1 ;
Grid . Add ( Tile ) ;
}
}
for ( auto & tile : Grid )
{
tile - > Index = GridIndex ( tile - > Q , tile - > R ) ;
}
2022-01-11 18:16:26 +01:00
bHexGridReady = true ;
2022-01-11 11:07:17 +01:00
}
2022-01-12 18:22:16 +01:00
// Every Hex Tile's index within the Grid Array can be derived from its Q and R coordinates
int32 AAdventureMap : : GridIndex ( int32 qAxial , int32 rAxial )
{
/*
* The Q axis is ( i . e . columns are ) oriented diagonally .
* The Hex Grid has a rough square shape , hence the Q coordinates must be offset by - 1 every other row .
*/
int32 column = qAxial + FMath : : FloorToInt ( rAxial / 2 ) ;
return ( rAxial * GridSize ) + column ;
}
2022-01-11 11:07:17 +01:00
AHexTile * AAdventureMap : : RandomHex ( )
{
2022-01-13 17:07:28 +01:00
int32 RandHex = GridIndex ( FMath : : RandRange ( 0 , GridSize - 1 ) , FMath : : RandRange ( 0 , GridSize - 1 ) ) ;
2022-01-11 18:16:26 +01:00
return Grid [ RandHex ] ;
2022-01-12 18:22:16 +01:00
}
2022-01-13 16:34:06 +01:00
TArray < AHexTile * > AAdventureMap : : Neighbors ( AHexTile * OfHex )
{
TArray < AHexTile * > Neighbors ;
2022-01-16 18:33:43 +01:00
int32 I ;
2022-01-13 16:34:06 +01:00
2022-01-16 18:33:43 +01:00
I = GridIndex ( OfHex - > Q + 1 , OfHex - > R + 0 ) ;
if ( Grid . IsValidIndex ( I ) & & OfHex - > Distance ( Grid [ I ] ) = = 1 ) { Neighbors . Add ( Grid [ I ] ) ; }
2022-01-13 16:34:06 +01:00
2022-01-16 18:33:43 +01:00
I = GridIndex ( OfHex - > Q + 1 , OfHex - > R - 1 ) ;
if ( Grid . IsValidIndex ( I ) & & OfHex - > Distance ( Grid [ I ] ) = = 1 ) { Neighbors . Add ( Grid [ I ] ) ; }
2022-01-13 16:34:06 +01:00
2022-01-16 18:33:43 +01:00
I = GridIndex ( OfHex - > Q + 0 , OfHex - > R - 1 ) ;
if ( Grid . IsValidIndex ( I ) & & OfHex - > Distance ( Grid [ I ] ) = = 1 ) { Neighbors . Add ( Grid [ I ] ) ; }
2022-01-13 16:34:06 +01:00
2022-01-16 18:33:43 +01:00
I = GridIndex ( OfHex - > Q - 1 , OfHex - > R + 0 ) ;
if ( Grid . IsValidIndex ( I ) & & OfHex - > Distance ( Grid [ I ] ) = = 1 ) { Neighbors . Add ( Grid [ I ] ) ; }
2022-01-13 16:34:06 +01:00
2022-01-16 18:33:43 +01:00
I = GridIndex ( OfHex - > Q - 1 , OfHex - > R + 1 ) ;
if ( Grid . IsValidIndex ( I ) & & OfHex - > Distance ( Grid [ I ] ) = = 1 ) { Neighbors . Add ( Grid [ I ] ) ; }
2022-01-13 16:34:06 +01:00
2022-01-16 18:33:43 +01:00
I = GridIndex ( OfHex - > Q + 0 , OfHex - > R + 1 ) ;
if ( Grid . IsValidIndex ( I ) & & OfHex - > Distance ( Grid [ I ] ) = = 1 ) { Neighbors . Add ( Grid [ I ] ) ; }
2022-01-13 16:34:06 +01:00
2022-01-13 16:54:37 +01:00
return Neighbors ;
2022-01-13 16:34:06 +01:00
}
2022-01-16 18:33:43 +01:00
2022-01-17 17:53:54 +01:00
// Massive memory leak when Goal is more than 2 distance away from Start (leading to the Editor freezing)
// Also freezes when the Goal is on the Edge of the map (i.e. has only 3 Neighbors)
2022-01-16 18:33:43 +01:00
TArray < AHexTile * > AAdventureMap : : FindPathAStar ( AHexTile * Start , AHexTile * Goal )
{
2022-01-17 17:53:54 +01:00
TArray < AHexTile * > PQ ; // Makeshift Priority Queue. (High value = Low priority)
PQ . Init ( Start , 1 ) ;
// The goal of this loop is to edit the Hex->CameFrom pointers, So as to chain Hexes from Goal to Start
while ( ! PQ . IsEmpty ( ) )
{
AHexTile * Current = PQ [ 0 ] ;
PQ . RemoveAt ( 0 ) ;
UE_LOG ( LogTemp , Warning , TEXT ( " Popping top priority Hex Q:%d|R:%d " ) , Current - > Q , Current - > R ) ;
if ( Current = = Goal ) // early exit
2022-01-16 21:19:34 +01:00
{
2022-01-17 17:53:54 +01:00
UE_LOG ( LogTemp , Warning , TEXT ( " Goal found!!! " ) ) ; // debug
2022-01-16 21:19:34 +01:00
break ;
}
2022-01-16 18:33:43 +01:00
2022-01-17 17:53:54 +01:00
UE_LOG ( LogTemp , Warning , TEXT ( " Expanding the frontier... " ) ) ; // debug
2022-01-16 18:33:43 +01:00
for ( AHexTile * Next : Neighbors ( Current ) )
{
2022-01-16 21:19:34 +01:00
int32 NewCost = Current - > CostSoFar + Next - > MoveCost ;
2022-01-17 17:53:54 +01:00
if ( NewCost < Next - > CostSoFar | | ! PQ . Contains ( Next ) )
{
Next - > CostSoFar = NewCost ;
Next - > CameFrom = Current ;
PQ . Remove ( Next ) ;
int32 NewPrio = NewCost + Next - > Distance ( Goal ) ; // Higher value = Lower priority
UE_LOG ( LogTemp , Warning , TEXT ( " Hex Q:%d|R:%d pathing updated. Readjusting priorities... " ) , Next - > Q , Next - > R ) ; // debug
if ( ! PQ . IsEmpty ( ) )
{
int32 OldPrio ;
int32 OldIndex = PQ . Num ( ) - 1 ; // To be inserted as lowest priority by default
for ( AHexTile * Hex : PQ )
{
OldPrio = Hex - > CostSoFar + Hex - > Distance ( Goal ) ;
if ( NewPrio < = OldPrio ) // looking for 1. Hex in "PQ" with a lower priority than that of "Next"
{
PQ . Find ( Hex , OldIndex ) ; // redefining of "OldIndex"
break ;
}
}
UE_LOG ( LogTemp , Warning , TEXT ( " Inserting Hex at priority %d " ) , OldIndex ) ; // debug
PQ . Insert ( Next , OldIndex ) ;
}
else
{
PQ . Add ( Next ) ;
UE_LOG ( LogTemp , Warning , TEXT ( " Adding one last Candidate. " ) ) ; // debug
}
}
}
}
TArray < AHexTile * > Path ;
AHexTile * Hex = Goal ;
while ( Hex ! = Start )
{
Path . Emplace ( Hex ) ;
Hex = Hex - > CameFrom ;
}
Algo : : Reverse ( Path ) ;
return Path ;
}
/*
// alternative implementation using faster built in priority queue data structure
TArray < AHexTile * > AAdventureMap : : FindPathAStarPQ ( AHexTile * Start , AHexTile * Goal )
{
// TArray<AHexTile*> PQ;
std : : priority_queue < AHexTile * , std : : vector < AHexTile * > > PQ ; // currently missing a custom comparator as third template arg (designating effective priority of pq elements)
std : : unordered_map < AHexTile * , AHexTile * > CameFrom ; // if in doubt: switch to templates instead of predefined hex types
// PQ.Init(Start, 1);
PQ . push ( Start ) ;
CameFrom [ Start ] = Start ;
// while (!PQ.IsEmpty())
while ( ! PQ . empty ( ) )
{
AHexTile * Current = PQ . top ( ) ;
// PQ.RemoveAt(0);
PQ . pop ( ) ;
if ( Current = = Goal )
{
UE_LOG ( LogTemp , Warning , TEXT ( " Goal found! " ) ) ; // debug
break ;
}
// Expanding the Frontier
for ( AHexTile * Next : Neighbors ( Current ) )
{
int32 NewCost = Current - > CostSoFar + Next - > MoveCost ;
2022-01-16 21:19:34 +01:00
// UE_LOG(LogTemp, Warning, TEXT("Cost calculated.")); // debug
2022-01-17 17:53:54 +01:00
if ( ! PQ . Contains ( Next ) | | NewCost < Next - > CostSoFar )
2022-01-16 18:33:43 +01:00
{
2022-01-16 21:19:34 +01:00
// UE_LOG(LogTemp, Warning, TEXT("New candidate found.")); // debug
2022-01-16 18:33:43 +01:00
Next - > CostSoFar = NewCost ;
int32 NewPrio = NewCost + Next - > Distance ( Goal ) ;
// Adjust the Priority Queue
2022-01-17 17:53:54 +01:00
if ( PQ . Contains ( Next ) ) { PQ . Remove ( Next ) ; }
for ( AHexTile * Hex : PQ ) // at this point PQ is empty, need to make sure it's not.
2022-01-16 18:33:43 +01:00
{
int32 OldPrio = Hex - > CostSoFar + Hex - > Distance ( Goal ) ;
int32 Index ;
2022-01-17 17:53:54 +01:00
PQ . Find ( Hex , Index ) ;
2022-01-16 21:19:34 +01:00
// UE_LOG(LogTemp, Warning, TEXT("Comparing priorities...")); // debug
2022-01-16 18:33:43 +01:00
if ( OldPrio > NewPrio )
2022-01-16 21:19:34 +01:00
{
2022-01-17 17:53:54 +01:00
PQ . Insert ( Next , Index ) ;
2022-01-16 18:33:43 +01:00
Next - > CameFrom = Current ;
2022-01-16 21:19:34 +01:00
// UE_LOG(LogTemp, Warning, TEXT("Looks promising!")); // debug
2022-01-16 18:33:43 +01:00
break ;
}
2022-01-17 17:53:54 +01:00
if ( Index = = PQ . Num ( ) - 1 & & OldPrio < = NewPrio )
2022-01-16 18:33:43 +01:00
{
2022-01-17 17:53:54 +01:00
PQ . Emplace ( Next ) ;
2022-01-16 18:33:43 +01:00
Next - > CameFrom = Current ;
2022-01-16 21:19:34 +01:00
// UE_LOG(LogTemp, Warning, TEXT("Low prio added")); // debug
break ;
2022-01-16 18:33:43 +01:00
}
}
2022-01-17 17:53:54 +01:00
if ( PQ . IsEmpty ( ) ) { PQ . Emplace ( Next ) ; }
2022-01-16 18:33:43 +01:00
}
}
}
TArray < AHexTile * > Path ;
AHexTile * Hex = Goal ;
2022-01-17 17:53:54 +01:00
while ( Hex ! = Start )
2022-01-16 18:33:43 +01:00
{
Path . Emplace ( Hex ) ;
Hex = Hex - > CameFrom ;
}
Algo : : Reverse ( Path ) ;
2022-01-16 21:19:34 +01:00
return Path ;
2022-01-16 18:33:43 +01:00
}
2022-01-17 17:53:54 +01:00
*/