# include "Unit.h"
# include "olcUTIL_Geometry2D.h"
# include "TileManager.h"
# include "util.h"
# include "DebuffIcon.h"
# include "olcPGEX_QuickGUI.h"
# include "Textbox.h"
Unit : : ~ Unit ( ) { } ;
void Unit : : RandomHit ( std : : vector < std : : unique_ptr < Audio > > & SOUNDS ) {
switch ( rand ( ) % 3 ) {
case 0 : { SOUNDS [ Sound : : HIT1 ] - > Play ( GetPos ( ) , 1 , 0.6 ) ; } break ;
case 1 : { SOUNDS [ Sound : : HIT2 ] - > Play ( GetPos ( ) , 1 , 0.6 ) ; } break ;
case 2 : { SOUNDS [ Sound : : HIT3 ] - > Play ( GetPos ( ) , 1 , 0.6 ) ; } break ;
}
}
std : : string LeftShifter : : unitName = " Left Shifter " ;
std : : string LeftShifter : : unitDescription = " Shifts target memory 1 bit to the left. " ;
std : : vector < Memory > LeftShifter : : resourceCost = { { RANGE , 2 } , { ATKSPD , 2 } , { MOVESPD , 6 } , { PROCEDURE , 1 } , { HEALTH , 4 } } ;
LeftShifter : : LeftShifter ( PixelGameEngine * pge , vf2d pos , std : : vector < std : : unique_ptr < Renderable > > & IMAGES , bool friendly , bool moveable )
: Unit ( pge , LeftShifter : : resourceCost , pos , 12 , * IMAGES [ LEFT_SHIFTER ] , CONSTANT : : ATTACKER_TARGET_COL , CONSTANT : : ATTACKER_ATTACK_COL , friendly , moveable ) { }
void LeftShifter : : Attack ( Unit & victim , std : : vector < std : : shared_ptr < Unit > > & otherUnits , std : : vector < std : : unique_ptr < Audio > > & SOUNDS ) {
victim < < = 1 ;
RandomHit ( SOUNDS ) ;
}
std : : string RightShifter : : unitName = " Right Shifter " ;
std : : string RightShifter : : unitDescription = " Shifts target memory 1 bit to the right. " ;
std : : vector < Memory > RightShifter : : resourceCost = { { HEALTH , 4 } , { RANGE , 2 } , { ATKSPD , 2 } , { MOVESPD , 6 } , { PROCEDURE , 1 } } ;
RightShifter : : RightShifter ( PixelGameEngine * pge , vf2d pos , std : : vector < std : : unique_ptr < Renderable > > & IMAGES , bool friendly , bool moveable )
: Unit ( pge , RightShifter : : resourceCost , pos , 12 , * IMAGES [ RIGHT_SHIFTER ] , CONSTANT : : ATTACKER_TARGET_COL , CONSTANT : : ATTACKER_ATTACK_COL , friendly , moveable ) { }
void RightShifter : : Attack ( Unit & victim , std : : vector < std : : shared_ptr < Unit > > & otherUnits , std : : vector < std : : unique_ptr < Audio > > & SOUNDS ) {
victim > > = 1 ;
RandomHit ( SOUNDS ) ;
}
std : : string BitRestorer : : unitName = " Bit Restorer " ;
std : : string BitRestorer : : unitDescription = " Randomly restores 1 missing bit to a target. " ;
std : : vector < Memory > BitRestorer : : resourceCost = { { PROCEDURE , 6 } , { RANGE , 1 } , { ATKSPD , 1 } , { MOVESPD , 2 } , { HEALTH , 2 } } ;
BitRestorer : : BitRestorer ( PixelGameEngine * pge , vf2d pos , std : : vector < std : : unique_ptr < Renderable > > & IMAGES , bool friendly , bool moveable )
: Unit ( pge , BitRestorer : : resourceCost , pos , 12 , * IMAGES [ BIT_RESTORER ] , CONSTANT : : HEALER_TARGET_COL , CONSTANT : : HEALER_ATTACK_COL , friendly , moveable , true , false ) { }
void BitRestorer : : Attack ( Unit & victim , std : : vector < std : : shared_ptr < Unit > > & otherUnits , std : : vector < std : : unique_ptr < Audio > > & SOUNDS ) {
std : : vector < int > emptyMemoryPositions ;
for ( int i = 0 ; i < victim . GetMemorySize ( ) ; i + + ) {
if ( ! victim . memory [ i ] ) {
emptyMemoryPositions . emplace_back ( i ) ;
}
}
if ( emptyMemoryPositions . size ( ) = = 0 ) {
//First see if we can find another damaged target, if we can, then we try healing them. Otherwise we exit.
appliedTarget . reset ( ) ;
AttemptToHealOtherAllies ( otherUnits , SOUNDS ) ;
return ;
}
int randomBit = emptyMemoryPositions [ rand ( ) % emptyMemoryPositions . size ( ) ] ;
victim . memory [ randomBit ] = true ;
SOUNDS [ Sound : : HEAL ] - > Play ( GetPos ( ) , 1 , 0.6 ) ;
}
void BitRestorer : : AttemptToHealOtherAllies ( std : : vector < std : : shared_ptr < Unit > > & otherUnits , std : : vector < std : : unique_ptr < Audio > > & SOUNDS ) {
std : : vector < int > emptyMemoryPositions ;
for ( auto & u : otherUnits ) {
if ( u . get ( ) ! = this & & u - > IsFriendly ( ) & & InRange ( u ) ) {
for ( int i = 0 ; i < u - > GetMemorySize ( ) ; i + + ) {
if ( ! u - > memory [ i ] ) {
emptyMemoryPositions . emplace_back ( i ) ;
}
}
if ( emptyMemoryPositions . size ( ) ! = 0 ) {
int randomBit = emptyMemoryPositions [ rand ( ) % emptyMemoryPositions . size ( ) ] ;
u - > memory [ randomBit ] = true ;
appliedTarget = u ;
SOUNDS [ Sound : : HEAL ] - > Play ( GetPos ( ) , 1 , 0.6 ) ;
return ;
}
}
}
}
std : : string MemorySwapper : : unitName = " Memory Swapper " ;
std : : string MemorySwapper : : unitDescription = " Flips the orientation of all bits of a target around. " ;
std : : vector < Memory > MemorySwapper : : resourceCost = { { RANGE , 3 } , { ATKSPD , 1 } , { HEALTH , 3 } , { PROCEDURE , 3 } , { MOVESPD , 4 } } ;
MemorySwapper : : MemorySwapper ( PixelGameEngine * pge , vf2d pos , std : : vector < std : : unique_ptr < Renderable > > & IMAGES , bool friendly , bool moveable )
: Unit ( pge , MemorySwapper : : resourceCost , pos , 12 , * IMAGES [ MEMORY_SWAPPER ] , CONSTANT : : ATTACKER_TARGET_COL , CONSTANT : : ATTACKER_ATTACK_COL , friendly , moveable , true ) {
autoAcquireFriendlyTarget = false ;
}
void MemorySwapper : : Attack ( Unit & victim , std : : vector < std : : shared_ptr < Unit > > & otherUnits , std : : vector < std : : unique_ptr < Audio > > & SOUNDS ) {
std : : vector < bool > oldMemory = victim . memory ;
for ( int i = 0 ; i < oldMemory . size ( ) ; i + + ) {
victim . memory [ i ] = oldMemory [ oldMemory . size ( ) - i - 1 ] ;
}
std : : vector < Marker * > order ;
for ( int i = 0 ; i < 5 ; i + + ) {
int lowestInd = 999999 ;
Marker * lowest = nullptr ;
if ( victim . health . index < lowestInd ) { lowest = & victim . health ; lowestInd = lowest - > index ; }
if ( victim . range . index < lowestInd ) { lowest = & victim . range ; lowestInd = lowest - > index ; }
if ( victim . atkSpd . index < lowestInd ) { lowest = & victim . atkSpd ; lowestInd = lowest - > index ; }
if ( victim . moveSpd . index < lowestInd ) { lowest = & victim . moveSpd ; lowestInd = lowest - > index ; }
if ( victim . procedure . index < lowestInd ) { lowest = & victim . procedure ; lowestInd = lowest - > index ; }
if ( lowest ! = nullptr ) {
order . push_back ( lowest ) ;
lowest - > index = 9999999 ;
}
}
std : : reverse ( order . begin ( ) , order . end ( ) ) ;
int i = 0 ;
int marker = 0 ;
while ( marker < oldMemory . size ( ) ) {
order [ i ] - > index = marker ;
marker + = order [ i ] - > size ;
i + + ;
}
//Clean up after ourselves for any 0-sized stats.
if ( victim . health . index = = 9999999 ) victim . health . index = 0 ;
if ( victim . range . index = = 9999999 ) victim . range . index = 0 ;
if ( victim . atkSpd . index = = 9999999 ) victim . atkSpd . index = 0 ;
if ( victim . moveSpd . index = = 9999999 ) victim . moveSpd . index = 0 ;
if ( victim . procedure . index = = 9999999 ) victim . procedure . index = 0 ;
RandomHit ( SOUNDS ) ;
}
std : : string Corrupter : : unitName = " Corrupter " ;
std : : string Corrupter : : unitDescription = " Chooses a random bit and negates it on a target. " ;
std : : vector < Memory > Corrupter : : resourceCost = { { ATKSPD , 3 } , { RANGE , 1 } , { PROCEDURE , 8 } , { MOVESPD , 8 } , { HEALTH , 4 } } ;
Corrupter : : Corrupter ( PixelGameEngine * pge , vf2d pos , std : : vector < std : : unique_ptr < Renderable > > & IMAGES , bool friendly , bool moveable )
: Unit ( pge , Corrupter : : resourceCost , pos , 12 , * IMAGES [ CORRUPTER ] , CONSTANT : : ATTACKER_TARGET_COL , CONSTANT : : ATTACKER_ATTACK_COL , friendly , moveable ) { }
void Corrupter : : Attack ( Unit & victim , std : : vector < std : : shared_ptr < Unit > > & otherUnits , std : : vector < std : : unique_ptr < Audio > > & SOUNDS ) {
//Chooses a bit at random and corrupts it.
int randomBit = rand ( ) % victim . memory . size ( ) ;
if ( victim . memory [ randomBit ] ) {
RandomHit ( SOUNDS ) ;
}
victim . memory [ randomBit ] = false ;
}
std : : string MemoryAllocator : : unitName = " Memory Allocator " ;
std : : string MemoryAllocator : : unitDescription = " A unit that builds other units. " ;
std : : vector < Memory > MemoryAllocator : : resourceCost = { { RANGE , 1 } , { ATKSPD , 1 } , { MOVESPD , 2 } , { PROCEDURE , 1 } , { HEALTH , 1 } } ;
MemoryAllocator : : MemoryAllocator ( PixelGameEngine * pge , vf2d pos , std : : vector < std : : unique_ptr < Renderable > > & IMAGES , bool friendly , bool moveable )
: Unit ( pge , MemoryAllocator : : resourceCost , pos , 12 , * IMAGES [ UNIT_ALLOCATOR ] , CONSTANT : : ATTACKER_TARGET_COL , CONSTANT : : ATTACKER_ATTACK_COL , friendly , true , false ) {
isAllocator = true ;
}
void MemoryAllocator : : Attack ( Unit & victim , std : : vector < std : : shared_ptr < Unit > > & otherUnits , std : : vector < std : : unique_ptr < Audio > > & SOUNDS ) {
}
void MemoryAllocator : : Update ( PixelGameEngine * pge , std : : vector < std : : unique_ptr < Audio > > & SOUNDS , std : : vector < std : : shared_ptr < Unit > > & queuedUnits ) {
if ( IsBuilding ( ) ) {
SetTargetLocation ( CONSTANT : : UNSELECTED ) ;
target . reset ( ) ;
buildTime - = pge - > GetElapsedTime ( ) ;
if ( buildTime < = 0 ) {
for ( int i = 0 ; i < GetMemorySize ( ) ; i + + ) {
memory [ i ] = false ; //Kill the unit.
}
SOUNDS [ Sound : : SPAWN ] - > Play ( GetPos ( ) ) ;
queuedUnits . push_back ( std : : move ( buildTransformUnit ) ) ;
}
}
}
void MemoryAllocator : : Draw ( TileTransformedView & game , std : : vector < std : : unique_ptr < Renderable > > & IMAGES ) {
if ( IsBuilding ( ) & & ! InFogOfWar ( ) ) {
game . GetPGE ( ) - > SetDrawTarget ( img . Sprite ( ) ) ;
game . GetPGE ( ) - > Clear ( BLANK ) ;
Renderable & targetImg = buildTransformUnit - > GetImage ( ) ;
game . GetPGE ( ) - > SetDrawTarget ( nullptr ) ;
game . DrawPartialRotatedDecal ( GetGhostPos ( ) , img . Decal ( ) , 0 , img . Sprite ( ) - > Size ( ) / 2 , { 0 , 0 } , { float ( img . Sprite ( ) - > width ) , float ( ( buildTime / CONSTANT : : UNIT_BUILD_TIME ) * img . Sprite ( ) - > height ) } , { 1 , 1 } , GetUnitColor ( ) / 3 ) ;
game . DrawPartialRotatedDecal ( GetGhostPos ( ) + vf2d { 0.f , ( buildTime / CONSTANT : : UNIT_BUILD_TIME ) * buildTransformUnit - > GetImage ( ) . Sprite ( ) - > height } , buildTransformUnit - > GetImage ( ) . Decal ( ) , 0 , buildTransformUnit - > GetImage ( ) . Sprite ( ) - > Size ( ) / 2 , { 0.f , ( buildTime / CONSTANT : : UNIT_BUILD_TIME ) * buildTransformUnit - > GetImage ( ) . Sprite ( ) - > height } , { float ( buildTransformUnit - > GetImage ( ) . Sprite ( ) - > width ) , float ( buildTransformUnit - > GetImage ( ) . Sprite ( ) - > height - ( buildTime / CONSTANT : : UNIT_BUILD_TIME ) * buildTransformUnit - > GetImage ( ) . Sprite ( ) - > height ) } , { 1 , 1 } , GetUnitColor ( ) / 1.5f ) ;
if ( fmod ( buildTime , 1 ) > 0.5 ) {
game . DrawRotatedDecal ( GetGhostPos ( ) , IMAGES [ SELECTION_CIRCLE ] - > Decal ( ) , 0 , IMAGES [ SELECTION_CIRCLE ] - > Sprite ( ) - > Size ( ) / 2 , vf2d ( img . Sprite ( ) - > Size ( ) ) / IMAGES [ SELECTION_CIRCLE ] - > Sprite ( ) - > Size ( ) , { 0 , 202 , 217 } ) ;
}
} else {
game . DrawRotatedDecal ( GetGhostPos ( ) , img . Decal ( ) , 0 , img . Sprite ( ) - > Size ( ) / 2 , { 1 , 1 } , GetUnitColor ( ) ) ;
if ( IsSelected ( ) ) {
game . DrawRotatedDecal ( GetGhostPos ( ) , IMAGES [ SELECTION_CIRCLE ] - > Decal ( ) , 0 , IMAGES [ SELECTION_CIRCLE ] - > Sprite ( ) - > Size ( ) / 2 , vf2d ( img . Sprite ( ) - > Size ( ) ) / IMAGES [ SELECTION_CIRCLE ] - > Sprite ( ) - > Size ( ) , WHITE ) ;
}
if ( IsAttached ( ) ) {
game . DrawRotatedDecal ( GetGhostPos ( ) , IMAGES [ SELECTION_CIRCLE ] - > Decal ( ) , 0 , IMAGES [ SELECTION_CIRCLE ] - > Sprite ( ) - > Size ( ) / 2 , vf2d ( img . Sprite ( ) - > Size ( ) ) / IMAGES [ SELECTION_CIRCLE ] - > Sprite ( ) - > Size ( ) , IsFriendly ( ) ? GREEN : RED ) ;
}
}
}
std : : string RAMBank : : unitName = " RAM Bank " ;
std : : string RAMBank : : unitDescription = " Allows for the construction of Memory Allocators. " ;
std : : vector < Memory > RAMBank : : resourceCost = { { PROCEDURE , 25 } , { HEALTH , 16 } } ;
RAMBank : : RAMBank ( PixelGameEngine * pge , vf2d pos , std : : vector < std : : unique_ptr < Renderable > > & IMAGES , bool friendly )
: Unit ( pge , RAMBank : : resourceCost , pos , 41 , * IMAGES [ RAM_BANK ] , WHITE , WHITE , friendly , false
, false , false
) , randomOffset ( { util : : random ( 128 ) , util : : random ( 128 ) } ) , matrixImg ( * IMAGES [ MATRIX ] ) ,
originalImg ( * IMAGES [ RAM_BANK ] ) {
img . Create ( IMAGES [ RAM_BANK ] - > Sprite ( ) - > width , IMAGES [ RAM_BANK ] - > Sprite ( ) - > height ) ;
pge - > SetDrawTarget ( img . Sprite ( ) ) ;
pge - > Clear ( BLANK ) ;
pge - > DrawSprite ( { 0 , 0 } , IMAGES [ RAM_BANK ] - > Sprite ( ) ) ;
pge - > SetDrawTarget ( nullptr ) ;
allocatorManager . colNormal = olc : : VERY_DARK_GREEN ;
allocatorManager . colHover = olc : : GREEN ;
allocatorManager . colClick = olc : : YELLOW ;
allocatorManager . colDisable = olc : : DARK_GREY ;
allocatorManager . colBorder = olc : : YELLOW ;
allocatorManager . colText = olc : : WHITE ;
allocatorButton = new QuickGUI : : ImageButton ( allocatorManager , * IMAGES [ UNIT_ALLOCATOR ] , { 0.5f , 0.5f } , pos - vf2d { 8 , 48 } , { 20 , 20 } ) ;
isRAMBank = true ;
}
void RAMBank : : Attack ( Unit & victim , std : : vector < std : : shared_ptr < Unit > > & otherUnits , std : : vector < std : : unique_ptr < Audio > > & SOUNDS ) {
}
void RAMBank : : Update ( PixelGameEngine * pge , std : : vector < std : : unique_ptr < Audio > > & SOUNDS , std : : vector < std : : shared_ptr < Unit > > & queuedUnits ) {
pge - > SetDrawTarget ( img . Sprite ( ) ) ;
for ( int y = 0 ; y < img . Sprite ( ) - > height ; y + + ) {
for ( int x = 0 ; x < img . Sprite ( ) - > width ; x + + ) {
Pixel col = originalImg . Sprite ( ) - > GetPixel ( x , y ) ;
if ( col = = WHITE ) {
pge - > Draw ( x , y , matrixImg . Sprite ( ) - > GetPixel ( int ( x + randomOffset . x ) , int ( y + randomOffset . y ) ) ) ;
}
}
}
if ( ! soundStarted ) {
soundStarted = true ;
soundHandle = SOUNDS [ int ( Sound : : HUM ) ] - > Play ( GetPos ( ) , 0.4 , 0.4 , true ) ;
}
img . Decal ( ) - > Update ( ) ;
pge - > SetDrawTarget ( nullptr ) ;
}
void RAMBank : : DrawHud ( TileTransformedView & game , std : : vector < std : : unique_ptr < Renderable > > & IMAGES ) {
if ( IsSelected ( ) ) {
allocatorManager . DrawDecal ( game ) ;
}
}
void RAMBank : : Draw ( TileTransformedView & game , std : : vector < std : : unique_ptr < Renderable > > & IMAGES ) {
game . DrawRotatedDecal ( GetGhostPos ( ) , img . Decal ( ) , 0 , img . Sprite ( ) - > Size ( ) / 2 , { 1 , 1 } , GetUnitColor ( ) ) ;
if ( IsSelected ( ) ) {
game . DrawRotatedDecal ( GetGhostPos ( ) , IMAGES [ SELECTION_CIRCLE ] - > Decal ( ) , 0 , IMAGES [ SELECTION_CIRCLE ] - > Sprite ( ) - > Size ( ) / 2 , vf2d ( img . Sprite ( ) - > Size ( ) ) / IMAGES [ SELECTION_CIRCLE ] - > Sprite ( ) - > Size ( ) , WHITE ) ;
}
if ( IsAttached ( ) ) {
game . DrawRotatedDecal ( GetGhostPos ( ) , IMAGES [ SELECTION_CIRCLE ] - > Decal ( ) , 0 , IMAGES [ SELECTION_CIRCLE ] - > Sprite ( ) - > Size ( ) / 2 , vf2d ( img . Sprite ( ) - > Size ( ) ) / IMAGES [ SELECTION_CIRCLE ] - > Sprite ( ) - > Size ( ) , IsFriendly ( ) ? GREEN : RED ) ;
}
}
void RAMBank : : OnDeath ( std : : vector < std : : unique_ptr < Audio > > & SOUNDS ) {
SOUNDS [ int ( Sound : : HUM ) ] - > Stop ( soundHandle ) ;
}
void RAMBank : : UpdateGUIState ( TileTransformedView & game , Resources & player_resources , Textbox & displayBox , bool & hovered , int totalUsedMemory , int availableMemory ) {
allocatorManager . Update ( game ) ;
auto TotalResources = [ & ] ( ) {
return player_resources . atkSpd + player_resources . health + player_resources . moveSpd + player_resources . procedure + player_resources . range ;
} ;
bool buttonEnabled = IsSelected ( ) & & TotalResources ( ) > = 5 & & GetProcedure ( ) = = procedure . size & & 5 + totalUsedMemory < = availableMemory ;
if ( buttonEnabled & & allocatorButton - > bHover ) {
displayBox . Initialize ( CONSTANT : : MEMORY_ALLOCATOR_BOX_DISPLAY_STRING , { } , CONSTANT : : MEMORY_ALLOCATOR_BOX_HEADER_STRING ) ;
//displayBox.SetVisible(true);
hovered = true ;
}
allocatorButton - > Enable ( buttonEnabled ) ;
}
bool RAMBank : : ClickHandled ( TileTransformedView & game , Resources & player_resources , std : : vector < std : : shared_ptr < Unit > > & units , std : : vector < std : : unique_ptr < Renderable > > & IMAGES ) {
if ( allocatorButton - > bPressed ) {
//First try to take one of each.
if ( player_resources . atkSpd > 0 & & player_resources . health > 0 & & player_resources . moveSpd > 0 & & player_resources . procedure > 0 & & player_resources . range > 0 ) {
player_resources . atkSpd - - ;
player_resources . health - - ;
player_resources . moveSpd - - ;
player_resources . procedure - - ;
player_resources . range - - ;
} else {
//Remove from the highest resource amount until 5 are removed.
for ( int i = 0 ; i < 5 ; i + + ) {
int * maxAmt = & player_resources . atkSpd ;
if ( player_resources . health > * maxAmt ) {
maxAmt = & player_resources . health ;
}
if ( player_resources . moveSpd > * maxAmt ) {
maxAmt = & player_resources . moveSpd ;
}
if ( player_resources . procedure > * maxAmt ) {
maxAmt = & player_resources . procedure ;
}
if ( player_resources . range > * maxAmt ) {
maxAmt = & player_resources . range ;
}
( * maxAmt ) - - ;
}
}
units . push_back ( std : : make_shared < MemoryAllocator > ( game . GetPGE ( ) , GetPos ( ) + vf2d { 0 , 24 } , IMAGES , friendly ) ) ;
return true ;
}
return false ;
}
std : : string _Platform : : unitName = " Platform " ;
std : : string _Platform : : unitDescription = " Anchored to the ground, this unit is an intermediate step for larger units. " ;
std : : vector < Memory > _Platform : : resourceCost = { { HEALTH , 6 } } ;
_Platform : : _Platform ( PixelGameEngine * pge , vf2d pos , std : : vector < std : : unique_ptr < Renderable > > & IMAGES , bool friendly , bool moveable )
: Unit ( pge , _Platform : : resourceCost , pos , 24 , * IMAGES [ PLATFORM ] , CONSTANT : : ATTACKER_TARGET_COL , CONSTANT : : ATTACKER_ATTACK_COL , friendly , false ) {
isPlatform = true ;
}
void _Platform : : Attack ( Unit & victim , std : : vector < std : : shared_ptr < Unit > > & otherUnits , std : : vector < std : : unique_ptr < Audio > > & SOUNDS ) { } ;
void _Platform : : Update ( PixelGameEngine * pge , std : : vector < std : : unique_ptr < Audio > > & SOUNDS , std : : vector < std : : shared_ptr < Unit > > & queuedUnits ) {
if ( IsBuilding ( ) ) {
SetTargetLocation ( CONSTANT : : UNSELECTED ) ;
target . reset ( ) ;
buildTime - = pge - > GetElapsedTime ( ) ;
if ( buildTime < = 0 ) {
for ( int i = 0 ; i < GetMemorySize ( ) ; i + + ) {
memory [ i ] = false ; //Kill the unit.
}
SOUNDS [ Sound : : SPAWN ] - > Play ( GetPos ( ) ) ;
queuedUnits . push_back ( std : : move ( buildTransformUnit ) ) ;
}
}
}
void _Platform : : Draw ( TileTransformedView & game , std : : vector < std : : unique_ptr < Renderable > > & IMAGES ) {
if ( IsBuilding ( ) ) {
game . GetPGE ( ) - > SetDrawTarget ( img . Sprite ( ) ) ;
game . GetPGE ( ) - > Clear ( BLANK ) ;
Renderable & targetImg = buildTransformUnit - > GetImage ( ) ;
game . GetPGE ( ) - > SetDrawTarget ( nullptr ) ;
game . DrawPartialRotatedDecal ( GetGhostPos ( ) , img . Decal ( ) , 0 , img . Sprite ( ) - > Size ( ) / 2 , { 0 , 0 } , { float ( img . Sprite ( ) - > width ) , float ( ( buildTime / CONSTANT : : UNIT_BUILD_TIME ) * img . Sprite ( ) - > height ) } , { 1 , 1 } , GetUnitColor ( ) / 3 ) ;
game . DrawPartialRotatedDecal ( GetGhostPos ( ) + vf2d { 0.f , ( buildTime / CONSTANT : : UNIT_BUILD_TIME ) * buildTransformUnit - > GetImage ( ) . Sprite ( ) - > height } , buildTransformUnit - > GetImage ( ) . Decal ( ) , 0 , buildTransformUnit - > GetImage ( ) . Sprite ( ) - > Size ( ) / 2 , { 0.f , ( buildTime / CONSTANT : : UNIT_BUILD_TIME ) * buildTransformUnit - > GetImage ( ) . Sprite ( ) - > height } , { float ( buildTransformUnit - > GetImage ( ) . Sprite ( ) - > width ) , float ( buildTransformUnit - > GetImage ( ) . Sprite ( ) - > height - ( buildTime / CONSTANT : : UNIT_BUILD_TIME ) * buildTransformUnit - > GetImage ( ) . Sprite ( ) - > height ) } , { 1 , 1 } , GetUnitColor ( ) / 1.5f ) ;
if ( fmod ( buildTime , 1 ) > 0.5 ) {
game . DrawRotatedDecal ( GetGhostPos ( ) , IMAGES [ SELECTION_CIRCLE ] - > Decal ( ) , 0 , IMAGES [ SELECTION_CIRCLE ] - > Sprite ( ) - > Size ( ) / 2 , vf2d ( img . Sprite ( ) - > Size ( ) ) / IMAGES [ SELECTION_CIRCLE ] - > Sprite ( ) - > Size ( ) , { 0 , 202 , 217 } ) ;
}
} else {
game . DrawRotatedDecal ( GetGhostPos ( ) , img . Decal ( ) , 0 , img . Sprite ( ) - > Size ( ) / 2 , { 1 , 1 } , GetUnitColor ( ) ) ;
if ( IsSelected ( ) ) {
game . DrawRotatedDecal ( GetGhostPos ( ) , IMAGES [ SELECTION_CIRCLE ] - > Decal ( ) , 0 , IMAGES [ SELECTION_CIRCLE ] - > Sprite ( ) - > Size ( ) / 2 , vf2d ( img . Sprite ( ) - > Size ( ) ) / IMAGES [ SELECTION_CIRCLE ] - > Sprite ( ) - > Size ( ) , WHITE ) ;
}
if ( IsAttached ( ) ) {
game . DrawRotatedDecal ( GetGhostPos ( ) , IMAGES [ SELECTION_CIRCLE ] - > Decal ( ) , 0 , IMAGES [ SELECTION_CIRCLE ] - > Sprite ( ) - > Size ( ) / 2 , vf2d ( img . Sprite ( ) - > Size ( ) ) / IMAGES [ SELECTION_CIRCLE ] - > Sprite ( ) - > Size ( ) , IsFriendly ( ) ? GREEN : RED ) ;
}
}
}
std : : string Refresher : : unitName = " Refresher " ;
std : : string Refresher : : unitDescription = " Repairs missing bits to surrounding units. " ;
std : : vector < Memory > Refresher : : resourceCost = { { ATKSPD , 3 } , { RANGE , 1 } , { PROCEDURE , 8 } , { HEALTH , 4 } } ;
Refresher : : Refresher ( PixelGameEngine * pge , vf2d pos , std : : vector < std : : unique_ptr < Renderable > > & IMAGES , bool friendly , bool moveable )
: Unit ( pge , Refresher : : resourceCost , pos , 24 , * IMAGES [ REFRESHER ] , CONSTANT : : ATTACKER_TARGET_COL , CONSTANT : : ATTACKER_ATTACK_COL , friendly , false
, true , false ) { }
void Refresher : : Attack ( Unit & victim , std : : vector < std : : shared_ptr < Unit > > & otherUnits , std : : vector < std : : unique_ptr < Audio > > & SOUNDS ) {
target . reset ( ) ; //Doesn't acquire a target.
for ( auto & u : otherUnits ) {
if ( IsFriendly ( ) = = u - > IsFriendly ( ) & & InRange ( u . get ( ) ) ) {
int maxAttempts = 1000 ;
int attempts = 0 ;
while ( attempts < maxAttempts ) {
int randomBit = rand ( ) % u - > GetMemorySize ( ) ;
if ( ! u - > memory [ randomBit ] ) {
u - > memory [ randomBit ] = true ;
return ;
}
attempts + + ;
}
}
}
}
void Refresher : : OnDeath ( std : : vector < std : : unique_ptr < Audio > > & SOUNDS ) {
SOUNDS [ Sound : : REFRESHER ] - > Stop ( soundHandle ) ;
} ;
void Refresher : : Update ( PixelGameEngine * pge , std : : vector < std : : unique_ptr < Audio > > & SOUNDS , std : : vector < std : : shared_ptr < Unit > > & queuedUnits ) {
if ( ! soundStarted ) {
soundStarted = true ;
soundHandle = SOUNDS [ Sound : : REFRESHER ] - > Play ( GetPos ( ) , 1 , 1 , true ) ;
}
} ;
std : : string Turret : : unitName = " Turret " ;
std : : string Turret : : unitDescription = " Automatically targets attack and movement speed memory ranges before others. " ;
std : : vector < Memory > Turret : : resourceCost = { { ATKSPD , 4 } , { RANGE , 5 } , { HEALTH , 6 } , { PROCEDURE , 16 } } ;
Turret : : Turret ( PixelGameEngine * pge , vf2d pos , std : : vector < std : : unique_ptr < Renderable > > & IMAGES , bool friendly , bool moveable )
: Unit ( pge , Turret : : resourceCost , pos , 24 , * IMAGES [ TURRET ] , CONSTANT : : ATTACKER_TARGET_COL , CONSTANT : : ATTACKER_ATTACK_COL , friendly , false ) { }
void Turret : : Attack ( Unit & victim , std : : vector < std : : shared_ptr < Unit > > & otherUnits , std : : vector < std : : unique_ptr < Audio > > & SOUNDS ) {
if ( victim . GetMoveSpd ( ) > 0 ) {
for ( int i = 0 ; i < victim . moveSpd . size ; i + + ) {
if ( victim . memory [ victim . moveSpd . index + i ] ) {
victim . memory [ victim . moveSpd . index + i ] = false ;
return ;
}
}
}
if ( victim . GetAtkSpd ( ) > 0 ) {
for ( int i = 0 ; i < victim . atkSpd . size ; i + + ) {
if ( victim . memory [ victim . atkSpd . index + i ] ) {
victim . memory [ victim . atkSpd . index + i ] = false ;
return ;
}
}
}
int maxAttempts = 1000 ;
int attempts = 0 ;
while ( attempts < maxAttempts ) {
int randomBit = rand ( ) % victim . GetMemorySize ( ) ;
if ( victim . memory [ randomBit ] ) {
victim . memory [ randomBit ] = false ;
return ;
}
attempts + + ;
}
}
void Turret : : OnDeath ( std : : vector < std : : unique_ptr < Audio > > & SOUNDS ) {
SOUNDS [ Sound : : TURRET ] - > Stop ( soundHandle ) ;
} ;
void Turret : : Update ( PixelGameEngine * pge , std : : vector < std : : unique_ptr < Audio > > & SOUNDS , std : : vector < std : : shared_ptr < Unit > > & queuedUnits ) {
if ( ! soundStarted ) {
soundStarted = true ;
soundHandle = SOUNDS [ Sound : : TURRET ] - > Play ( GetPos ( ) , 1 , 1 , true ) ;
}
} ;
std : : string MemoryGuard : : unitName = " Memory Guard " ;
std : : string MemoryGuard : : unitDescription = " Reduces the chance of bit modification for all surrounding units by 30% " ;
std : : vector < Memory > MemoryGuard : : resourceCost = { { HEALTH , 10 } , { ATKSPD , 4 } , { RANGE , 4 } , { PROCEDURE , 12 } } ;
MemoryGuard : : MemoryGuard ( PixelGameEngine * pge , vf2d pos , std : : vector < std : : unique_ptr < Renderable > > & IMAGES , bool friendly , bool moveable )
: Unit ( pge , MemoryGuard : : resourceCost , pos , 24 , * IMAGES [ MEMORY_GUARD ] , CONSTANT : : ATTACKER_TARGET_COL , CONSTANT : : ATTACKER_ATTACK_COL , friendly , false
, true , false ) { }
void MemoryGuard : : Attack ( Unit & victim , std : : vector < std : : shared_ptr < Unit > > & otherUnits , std : : vector < std : : unique_ptr < Audio > > & SOUNDS ) {
target . reset ( ) ; //Doesn't acquire a target.
for ( auto & u : otherUnits ) {
if ( IsFriendly ( ) = = u - > IsFriendly ( ) & & InRange ( u . get ( ) ) ) {
u - > SetGuardTime ( 3 ) ;
}
}
}
void MemoryGuard : : OnDeath ( std : : vector < std : : unique_ptr < Audio > > & SOUNDS ) {
SOUNDS [ Sound : : MEMORY_GUARD ] - > Stop ( soundHandle ) ;
} ;
void MemoryGuard : : Update ( PixelGameEngine * pge , std : : vector < std : : unique_ptr < Audio > > & SOUNDS , std : : vector < std : : shared_ptr < Unit > > & queuedUnits ) {
if ( ! soundStarted ) {
soundStarted = true ;
soundHandle = SOUNDS [ Sound : : MEMORY_GUARD ] - > Play ( GetPos ( ) , 1 , 1 , true ) ;
}
} ;
Unit : : Unit ( PixelGameEngine * pge , std : : vector < Memory > memory , vf2d pos , float radius , Renderable & img , Pixel targetLineColor , Pixel attackingLineColor , bool friendly , bool moveable , bool friendlyInteractable , bool enemyInteractable )
: pos ( pos ) , radius ( radius ) , ghostPos ( { - 999999 , - 999999 } ) , img ( img ) , targetLineCol ( targetLineColor ) , attackingLineCol ( attackingLineColor ) , friendly ( friendly ) , moveable ( moveable ) , friendlyInteractable ( friendlyInteractable ) , enemyInteractable ( enemyInteractable ) {
int marker = 0 ;
for ( Memory & mem : memory ) {
for ( int i = 0 ; i < mem . size ; i + + ) {
this - > memory . push_back ( true ) ;
this - > ghostMemory . push_back ( true ) ;
}
switch ( mem . type ) {
case HEALTH : {
health . index = marker ;
health . size = mem . size ;
} break ;
case RANGE : {
range . index = marker ;
range . size = mem . size ;
} break ;
case ATKSPD : {
atkSpd . index = marker ;
atkSpd . size = mem . size ;
} break ;
case MOVESPD : {
moveSpd . index = marker ;
moveSpd . size = mem . size ;
} break ;
case PROCEDURE : {
procedure . index = marker ;
procedure . size = mem . size ;
} break ;
}
marker + = mem . size ;
}
attackingLine . Create ( 25 , 24 , false , false ) ;
targetingLine . Create ( 25 , 24 , false , false ) ;
}
void Unit : : DrawRangeIndicator ( PixelGameEngine * pge , TileTransformedView & game , std : : vector < std : : unique_ptr < Renderable > > & IMAGES ) {
if ( ! CanInteractWithAllies ( ) & & ! CanInteractWithEnemies ( ) ) return ;
float dist = geom2d : : line < float > ( game . ScreenToWorld ( pge - > GetMousePos ( ) ) , GetGhostPos ( ) ) . length ( ) ;
float range = 12 * ( GetRange ( ) + 1 ) ;
float totalRange = GetUnitSize ( ) . x / 2 + range ;
if ( IsSelected ( ) ) {
Pixel col ;
if ( CanInteractWithAllies ( ) & & ! CanInteractWithEnemies ( ) ) {
col = { 40 , 127 , 173 } ;
} else {
col = { 0 , 196 , 0 } ;
}
game . DrawRotatedDecal ( GetGhostPos ( ) , IMAGES [ RANGE_INDICATOR ] - > Decal ( ) , 0 , IMAGES [ RANGE_INDICATOR ] - > Sprite ( ) - > Size ( ) / 2 , { totalRange / 12 , totalRange / 12 } , col ) ;
} else
if ( dist < range * 2 ) {
Pixel col ;
if ( IsFriendly ( ) ) {
if ( CanInteractWithAllies ( ) & & ! CanInteractWithEnemies ( ) ) {
col = { 40 , 127 , 173 } ;
} else {
col = { 0 , 196 , 0 } ;
}
} else {
if ( CanInteractWithAllies ( ) & & ! CanInteractWithEnemies ( ) ) {
col = { 194 , 37 , 168 } ;
} else {
col = { 196 , 130 , 0 } ;
}
}
uint8_t transparency = uint8_t ( ( 1.f - ( dist / ( range * 2 ) ) ) * 255 ) ;
game . DrawRotatedDecal ( GetGhostPos ( ) , IMAGES [ RANGE_INDICATOR ] - > Decal ( ) , 0 , IMAGES [ RANGE_INDICATOR ] - > Sprite ( ) - > Size ( ) / 2 , { totalRange / 12 , totalRange / 12 } , { col . r , col . g , col . b , transparency } ) ;
}
}
void Unit : : Draw ( TileTransformedView & game , std : : vector < std : : unique_ptr < Renderable > > & IMAGES ) {
game . DrawRotatedDecal ( GetGhostPos ( ) , img . Decal ( ) , 0 , img . Sprite ( ) - > Size ( ) / 2 , { 1 , 1 } , GetUnitColor ( ) ) ;
if ( IsSelected ( ) ) {
game . DrawRotatedDecal ( GetGhostPos ( ) , IMAGES [ SELECTION_CIRCLE ] - > Decal ( ) , 0 , IMAGES [ SELECTION_CIRCLE ] - > Sprite ( ) - > Size ( ) / 2 , vf2d ( img . Sprite ( ) - > Size ( ) ) / IMAGES [ SELECTION_CIRCLE ] - > Sprite ( ) - > Size ( ) , WHITE ) ;
}
if ( IsAttached ( ) ) {
game . DrawRotatedDecal ( GetGhostPos ( ) , IMAGES [ SELECTION_CIRCLE ] - > Decal ( ) , 0 , IMAGES [ SELECTION_CIRCLE ] - > Sprite ( ) - > Size ( ) / 2 , vf2d ( img . Sprite ( ) - > Size ( ) ) / IMAGES [ SELECTION_CIRCLE ] - > Sprite ( ) - > Size ( ) , IsFriendly ( ) ? GREEN : RED ) ;
}
}
void Unit : : DrawHud ( TileTransformedView & game , std : : vector < std : : unique_ptr < Renderable > > & IMAGES ) { }
void Unit : : _DrawHud ( TileTransformedView & game , std : : vector < std : : unique_ptr < Renderable > > & IMAGES , bool unitMetersGreyedOut ) {
DrawHud ( game , IMAGES ) ;
int initialBarX = ghostPos . x - GetMemorySize ( ) / 2 * CONSTANT : : BAR_SQUARE_SIZE . x - CONSTANT : : BAR_SQUARE_SIZE . x / 2 ;
int initialBarY = ghostPos . y - CONSTANT : : BAR_SQUARE_SIZE . y - img . Sprite ( ) - > height / 2 - 2 ;
Pixel col = 0 ;
auto CheckColor = [ & ] ( int i , Pixel & col ) {
if ( health . index = = i & & health . size > 0 ) {
col = CONSTANT : : HEALTH_COLOR ;
}
if ( range . index = = i & & range . size > 0 ) {
col = unitMetersGreyedOut ? VERY_DARK_GREY : CONSTANT : : RANGE_COLOR ;
}
if ( atkSpd . index = = i & & atkSpd . size > 0 ) {
col = unitMetersGreyedOut ? VERY_DARK_GREY : CONSTANT : : ATKSPD_COLOR ;
}
if ( moveSpd . index = = i & & moveSpd . size > 0 ) {
col = unitMetersGreyedOut ? VERY_DARK_GREY : CONSTANT : : MOVESPD_COLOR ;
}
if ( procedure . index = = i & & procedure . size > 0 ) {
col = unitMetersGreyedOut ? VERY_DARK_GREY : CONSTANT : : PROCEDURE_COLOR ;
}
} ;
if ( GetHealth ( ) > 0 ) {
game . FillRectDecal ( vf2d { float ( initialBarX ) + health . index * CONSTANT : : BAR_SQUARE_SIZE . x ,
float ( initialBarY ) } - vf2d { 0 , 1 } , CONSTANT : : BAR_SQUARE_SIZE + vf2d { CONSTANT : : BAR_SQUARE_SIZE . x * health . size - 1 , 2 } , CONSTANT : : HEALTH_COLOR ) ;
}
if ( GetAtkSpd ( ) > 0 ) {
game . FillRectDecal ( vf2d { float ( initialBarX ) + atkSpd . index * CONSTANT : : BAR_SQUARE_SIZE . x ,
float ( initialBarY ) } - vf2d { 0 , 1 } , CONSTANT : : BAR_SQUARE_SIZE + vf2d { CONSTANT : : BAR_SQUARE_SIZE . x * atkSpd . size - 1 , 2 } , unitMetersGreyedOut ? VERY_DARK_GREY : CONSTANT : : ATKSPD_COLOR ) ;
}
if ( GetMoveSpd ( ) > 0 ) {
game . FillRectDecal ( vf2d { float ( initialBarX ) + moveSpd . index * CONSTANT : : BAR_SQUARE_SIZE . x ,
float ( initialBarY ) } - vf2d { 0 , 1 } , CONSTANT : : BAR_SQUARE_SIZE + vf2d { CONSTANT : : BAR_SQUARE_SIZE . x * moveSpd . size - 1 , 2 } , unitMetersGreyedOut ? VERY_DARK_GREY : CONSTANT : : MOVESPD_COLOR ) ;
}
if ( GetProcedure ( ) > 0 ) {
game . FillRectDecal ( vf2d { float ( initialBarX ) + procedure . index * CONSTANT : : BAR_SQUARE_SIZE . x ,
float ( initialBarY ) } - vf2d { 0 , 1 } , CONSTANT : : BAR_SQUARE_SIZE + vf2d { CONSTANT : : BAR_SQUARE_SIZE . x * procedure . size - 1 , 2 } , unitMetersGreyedOut ? VERY_DARK_GREY : CONSTANT : : PROCEDURE_COLOR ) ;
}
if ( GetRange ( ) > 0 ) {
game . FillRectDecal ( vf2d { float ( initialBarX ) + range . index * CONSTANT : : BAR_SQUARE_SIZE . x ,
float ( initialBarY ) } - vf2d { 0 , 1 } , CONSTANT : : BAR_SQUARE_SIZE + vf2d { CONSTANT : : BAR_SQUARE_SIZE . x * range . size - 1 , 2 } , unitMetersGreyedOut ? VERY_DARK_GREY : CONSTANT : : RANGE_COLOR ) ;
}
for ( int i = 0 ; i < GetMemorySize ( ) ; i + + ) {
CheckColor ( i , col ) ;
game . FillRectDecal ( { float ( initialBarX ) + i * CONSTANT : : BAR_SQUARE_SIZE . x ,
float ( initialBarY ) } , CONSTANT : : BAR_SQUARE_SIZE , ghostMemory [ i ] ? col : col / 4 ) ;
game . DrawRectDecal ( { float ( initialBarX ) + i * CONSTANT : : BAR_SQUARE_SIZE . x ,
float ( initialBarY ) } , CONSTANT : : BAR_SQUARE_SIZE , BLACK ) ;
}
}
void Unit : : DrawUnitDamageStats ( PixelGameEngine * pge , TileTransformedView & game , std : : vector < std : : unique_ptr < Renderable > > & IMAGES ) {
if ( ! InFogOfWar ( ) ) {
if ( ! target . expired ( ) ) {
geom2d : : line < float > lineToTarget ( pos , target . lock ( ) - > pos ) ;
lineToTarget . start = lineToTarget . rpoint ( GetUnitSize ( ) . x / 2 ) ;
lineToTarget . end = lineToTarget . rpoint ( lineToTarget . length ( ) - GetUnitSize ( ) . x / 4 ) ;
util : : ApplyMatrixEffect ( game . GetPGE ( ) , targetingLine , * IMAGES [ TARGETING_LINE ] , IMAGES [ MATRIX ] ) ;
game . DrawPartialRotatedDecal ( lineToTarget . upoint ( 0.5 ) , targetingLine . Decal ( ) , lineToTarget . vector ( ) . polar ( ) . y , { lineToTarget . length ( ) / 2 , 12 } , { lineShift * 10 , 0 } , { lineToTarget . length ( ) , 24 } , { 1 , 1 } , targetLineCol ) ;
} else
if ( targetLoc ! = CONSTANT : : UNSELECTED ) {
geom2d : : line < float > lineToTarget ( pos , targetLoc ) ;
lineToTarget . start = lineToTarget . rpoint ( GetUnitSize ( ) . x / 2 ) ;
lineToTarget . end = lineToTarget . rpoint ( lineToTarget . length ( ) - GetUnitSize ( ) . x / 4 ) ;
util : : ApplyMatrixEffect ( game . GetPGE ( ) , targetingLine , * IMAGES [ TARGETING_LINE ] , IMAGES [ MATRIX ] ) ;
game . DrawPartialRotatedDecal ( lineToTarget . upoint ( 0.5 ) , targetingLine . Decal ( ) , lineToTarget . vector ( ) . polar ( ) . y , { lineToTarget . length ( ) / 2 , 12 } , { lineShift * 10 , 0 } , { lineToTarget . length ( ) , 24 } , { 1 , 0.6 } , CONSTANT : : MOVE_LINE_COL ) ;
}
if ( ! appliedTarget . expired ( ) ) {
geom2d : : line < float > lineToTarget ( pos , appliedTarget . lock ( ) - > pos ) ;
lineToTarget . start = lineToTarget . rpoint ( GetUnitSize ( ) . x / 2 ) ;
lineToTarget . end = lineToTarget . rpoint ( lineToTarget . length ( ) - GetUnitSize ( ) . x / 4 ) ;
if ( reloadTimer > 0 ) {
util : : ApplyMatrixEffect ( game . GetPGE ( ) , attackingLine , * IMAGES [ ATTACKING_LINE ] , IMAGES [ MATRIX ] ) ;
float reloadSpd = 1.f / ( GetAtkSpd ( ) / 2.f ) ;
game . DrawPartialRotatedDecal ( lineToTarget . upoint ( 0.5 ) , attackingLine . Decal ( ) , lineToTarget . vector ( ) . polar ( ) . y , { lineToTarget . length ( ) / 2 , 12 } , { lineShift * 30 , 0 } , { lineToTarget . length ( ) , 24 } , { 1 , 1 + ( attackFailed ? - 0.3f : ( reloadTimer / reloadSpd ) * 0.25f ) } , attackFailed ? Pixel { 192 , 192 , 192 , 130 } : Pixel { attackingLineCol . r , attackingLineCol . g , attackingLineCol . b , uint8_t ( IsFriendly ( ) ? 200 : 160 ) } ) ;
}
}
}
float dist = geom2d : : line < float > ( game . ScreenToWorld ( pge - > GetMousePos ( ) ) , GetGhostPos ( ) ) . length ( ) ;
float range = 12 * ( GetRange ( ) + 1 ) ;
auto DrawStatDown = [ & ] ( Marker & maxStat , int currentStat , vf2d pos , vf2d downDisplayPos , Image img , uint8_t transparency = 255 ) {
if ( maxStat . size > 0 ) {
if ( currentStat ! = maxStat . size ) {
game . DrawDecal ( this - > pos + pos , IMAGES [ img ] - > Decal ( ) , { 1 , 1 } , currentStat = = 0 ? Pixel { 192 , 64 , 64 , transparency } : Pixel { 255 , 255 , 255 , transparency } ) ;
if ( currentStat > 0 ) {
game . DrawDecal ( this - > pos + downDisplayPos , IMAGES [ DOWN_ARROW ] - > Decal ( ) , { 1 , 1 } , { 255 , 255 , 255 , transparency } ) ;
} else {
game . DrawDecal ( this - > pos + downDisplayPos , IMAGES [ RED_X ] - > Decal ( ) , { 1 , 1 } , { 255 , 255 , 255 , transparency } ) ;
}
}
}
} ;
if ( IsSelected ( ) ) {
DrawStatDown ( atkSpd , GetAtkSpd ( ) , { - 48 - 8 , - 24 } , { - 48 + 8 , - 18 } , RLD ) ;
DrawStatDown ( moveSpd , GetMoveSpd ( ) , { - 48 - 8 , 8 } , { - 36 + 8 , 2 } , SPD ) ;
DrawStatDown ( this - > range , GetRange ( ) , { 8 , - 24 } , { 20 , - 18 } , RNG ) ;
DrawStatDown ( procedure , GetProcedure ( ) , { 8 , 8 } , { 26 , 2 } , PRC ) ;
} else
if ( dist < range * 2 ) {
uint8_t transparency = uint8_t ( ( 1.f - ( dist / ( range * 2 ) ) ) * 255 ) ;
DrawStatDown ( atkSpd , GetAtkSpd ( ) , { - 48 - 8 , - 24 } , { - 48 + 8 , - 18 } , RLD , transparency ) ;
DrawStatDown ( moveSpd , GetMoveSpd ( ) , { - 48 - 8 , 8 } , { - 36 + 8 , 2 } , SPD , transparency ) ;
DrawStatDown ( this - > range , GetRange ( ) , { 8 , - 24 } , { 20 , - 18 } , RNG , transparency ) ;
DrawStatDown ( procedure , GetProcedure ( ) , { 8 , 8 } , { 26 , 2 } , PRC , transparency ) ;
}
}
int Unit : : GetBits ( Marker & m ) {
int activeBits = 0 ;
for ( int i = 0 ; i < m . size ; i + + ) {
if ( memory [ i + m . index ] ) {
activeBits + + ;
}
}
return activeBits ;
}
void Unit : : _OnDeath ( std : : vector < std : : unique_ptr < Audio > > & SOUNDS ) {
switch ( rand ( ) % 3 ) {
case 0 : {
SOUNDS [ Sound : : DEAD1 ] - > Play ( GetPos ( ) ) ;
} break ;
case 1 : {
SOUNDS [ Sound : : DEAD2 ] - > Play ( GetPos ( ) ) ;
} break ;
case 2 : {
SOUNDS [ Sound : : DEAD3 ] - > Play ( GetPos ( ) ) ;
} break ;
}
OnDeath ( SOUNDS ) ;
}
void Unit : : OnDeath ( std : : vector < std : : unique_ptr < Audio > > & SOUNDS ) { }
int Unit : : GetHealth ( ) {
return GetBits ( health ) ;
}
int Unit : : GetRange ( ) {
return GetBits ( range ) ;
}
int Unit : : GetAtkSpd ( ) {
return GetBits ( atkSpd ) ;
}
int Unit : : GetMoveSpd ( ) {
return GetBits ( moveSpd ) ;
}
int Unit : : GetProcedure ( ) {
return GetBits ( procedure ) ;
}
int Unit : : GetMemorySize ( ) {
return memory . size ( ) ;
}
void Unit : : RunAI ( PixelGameEngine * pge ) { }
void Unit : : _RunAI ( PixelGameEngine * pge ) {
RunAI ( pge ) ;
}
void Unit : : _Update ( PixelGameEngine * pge , std : : vector < std : : unique_ptr < Audio > > & SOUNDS , Resources & player_resources , Resources & enemy_resources , std : : vector < std : : shared_ptr < Unit > > & queuedUnits , std : : array < float , 5 > & resourceGainTimer , std : : vector < ResourceGainIcon > & resourceGainIcons , std : : vector < std : : unique_ptr < Renderable > > & IMAGES , GameFlags & flags ) {
if ( ! target . expired ( ) ) {
auto ptrTarget = target . lock ( ) ;
if ( ! InRange ( ptrTarget ) & & CanMove ( ) ) {
SetPos ( GetPos ( ) + ( ptrTarget - > GetPos ( ) - pos ) . norm ( ) * GetMoveSpd ( ) * 12 * pge - > GetElapsedTime ( ) ) ;
}
} else
if ( targetLoc ! = CONSTANT : : UNSELECTED ) {
float dist = geom2d : : line < float > ( pos , targetLoc ) . length ( ) ;
if ( dist > 24 ) {
if ( CanMove ( ) ) {
SetPos ( GetPos ( ) + ( targetLoc - pos ) . norm ( ) * GetMoveSpd ( ) * 12 * pge - > GetElapsedTime ( ) ) ;
}
} else {
if ( willAttachWhenReachingDestination & & ! attachTarget . expired ( ) & & attachTarget . lock ( ) - > attachedUnit . expired ( ) ) {
attachedPoint = attachTarget ;
attachedPoint . lock ( ) - > attachedUnit = self_ptr ;
}
willAttachWhenReachingDestination = false ;
targetLoc = CONSTANT : : UNSELECTED ;
}
}
if ( ! attachedPoint . expired ( ) ) {
SetPos ( attachedPoint . lock ( ) - > pos + vf2d { cos ( float ( attachedPoint . lock ( ) - > rot + PI / 2 ) ) * 16 , sin ( float ( attachedPoint . lock ( ) - > rot + PI / 2 ) ) * 16 } ) ;
collectionTime - = pge - > GetElapsedTime ( ) ;
if ( collectionTime < = 0 ) {
collectionTime = CONSTANT : : COLLECTION_WAIT_TIME ;
Resources & targetResource = IsFriendly ( ) ? player_resources : enemy_resources ;
switch ( attachedPoint . lock ( ) - > type ) {
case HEALTH : {
if ( ! IsFriendly ( ) & & flags . difficulty = = 2 ) {
targetResource . health + = 3 ;
}
if ( ! IsFriendly ( ) & & flags . difficulty = = 1 ) {
targetResource . health + + ;
}
if ( ! IsFriendly ( ) & & flags . difficulty = = 0 ) {
targetResource . health - - ;
}
targetResource . health + + ;
} break ;
case RANGE : {
if ( ! IsFriendly ( ) & & flags . difficulty = = 2 ) {
targetResource . range + = 3 ;
}
if ( ! IsFriendly ( ) & & flags . difficulty = = 1 ) {
targetResource . range + + ;
}
if ( ! IsFriendly ( ) & & flags . difficulty = = 0 ) {
targetResource . range - - ;
}
targetResource . range + + ;
} break ;
case ATKSPD : {
if ( ! IsFriendly ( ) & & flags . difficulty = = 2 ) {
targetResource . atkSpd + = 3 ;
}
if ( ! IsFriendly ( ) & & flags . difficulty = = 1 ) {
targetResource . atkSpd + + ;
}
if ( ! IsFriendly ( ) & & flags . difficulty = = 0 ) {
targetResource . atkSpd - - ;
}
targetResource . atkSpd + + ;
} break ;
case MOVESPD : {
if ( ! IsFriendly ( ) & & flags . difficulty = = 2 ) {
targetResource . moveSpd + = 3 ;
}
if ( ! IsFriendly ( ) & & flags . difficulty = = 1 ) {
targetResource . moveSpd + + ;
}
if ( ! IsFriendly ( ) & & flags . difficulty = = 0 ) {
targetResource . moveSpd - - ;
}
targetResource . moveSpd + + ;
} break ;
case PROCEDURE : {
if ( ! IsFriendly ( ) & & flags . difficulty = = 2 ) {
targetResource . procedure + = 3 ;
}
if ( ! IsFriendly ( ) & & flags . difficulty = = 1 ) {
targetResource . procedure + + ;
}
if ( ! IsFriendly ( ) & & flags . difficulty = = 0 ) {
targetResource . procedure - - ;
}
targetResource . procedure + + ;
} break ;
}
if ( ! InFogOfWar ( ) ) {
resourceGainIcons . push_back ( { IMAGES [ RESOURCE ] . get ( ) , attachedPoint . lock ( ) - > type , GetPos ( ) } ) ;
}
}
}
if ( ! IsFriendly ( ) ) {
_RunAI ( pge ) ;
}
if ( ! GhostInFogOfWar ( ) & & InFogOfWar ( ) ) {
HideGhost ( ) ;
}
if ( ! InFogOfWar ( ) ) {
ghostPos = pos ;
ghostMemory = memory ;
}
reloadTimer = std : : max ( 0.f , reloadTimer - pge - > GetElapsedTime ( ) ) ;
guardTime = std : : max ( 0.f , guardTime - pge - > GetElapsedTime ( ) ) ;
lineShift - = pge - > GetElapsedTime ( ) ;
if ( lineShift < - 25 ) lineShift + = 25 ;
Update ( pge , SOUNDS , queuedUnits ) ;
}
std : : vector < bool > operator < < ( Unit & u , const int n ) {
std : : vector < bool > tempMem = u . memory ;
for ( int i = 0 ; i < u . GetMemorySize ( ) - 1 ; i + + ) {
tempMem [ i ] = tempMem [ i + 1 ] ;
}
tempMem [ u . GetMemorySize ( ) - 1 ] = 0 ;
return tempMem ;
}
std : : vector < bool > operator > > ( Unit & u , const int n ) {
std : : vector < bool > tempMem = u . memory ;
for ( int i = 1 ; i < u . GetMemorySize ( ) ; i + + ) {
tempMem [ i ] = tempMem [ i - 1 ] ;
}
tempMem [ 0 ] = 0 ;
return tempMem ;
}
bool Unit : : IsFriendly ( ) {
return friendly ;
}
bool Unit : : IsSelected ( ) {
return selected ;
}
void Unit : : Select ( ) {
selected = true ;
}
void Unit : : Deselect ( ) {
selected = false ;
}
vf2d Unit : : GetPos ( ) {
return pos ;
}
bool Unit : : IsDead ( ) {
return dead ;
}
vf2d Unit : : GetUnitSize ( ) {
return vf2d { radius , radius } * 2 ;
}
void Unit : : SetTargetUnit ( std : : weak_ptr < Unit > target ) {
this - > target = target ;
this - > targetLoc = CONSTANT : : UNSELECTED ;
willAttachWhenReachingDestination = false ;
}
void Unit : : SetTargetLocation ( vf2d targetLoc ) {
this - > target . reset ( ) ;
this - > targetLoc = targetLoc ;
willAttachWhenReachingDestination = false ;
}
bool Unit : : InRange ( std : : shared_ptr < Unit > target ) {
return InRange ( target . get ( ) ) ;
}
bool Unit : : InRange ( Unit * target ) {
float range = 12 * ( GetRange ( ) + 1 ) ;
float totalRange = GetUnitSize ( ) . x / 2 + range ;
return geom2d : : overlaps ( geom2d : : circle < float > { GetPos ( ) , totalRange } , geom2d : : circle < float > { target - > GetPos ( ) , target - > GetUnitSize ( ) . x / 2 } ) ;
}
bool Unit : : InRange ( vf2d pos ) {
float range = 12 * ( GetRange ( ) + 1 ) ;
float totalRange = GetUnitSize ( ) . x / 2 + range ;
return geom2d : : overlaps ( geom2d : : circle < float > { GetPos ( ) , totalRange } , geom2d : : circle < float > { pos , 0.1 } ) ;
}
void Unit : : SetPos ( vf2d newPos ) {
pos = newPos ;
}
void Unit : : AttemptAttack ( std : : weak_ptr < Unit > attacker , std : : weak_ptr < Unit > unit , std : : vector < std : : shared_ptr < Unit > > & otherUnits , std : : vector < DebuffIcon > & debuffIcons , std : : vector < std : : unique_ptr < Renderable > > & IMAGES , std : : vector < std : : unique_ptr < Audio > > & SOUNDS ) {
if ( reloadTimer > 0 ) return ;
std : : weak_ptr < Unit > finalTarget ;
if ( ! unit . expired ( ) ) {
finalTarget = unit ;
if ( ! target . expired ( ) ) {
auto ptrTarget = target . lock ( ) ;
if ( InRange ( ptrTarget ) ) {
finalTarget = ptrTarget ;
}
}
}
if ( ! finalTarget . expired ( ) ) {
if ( InRange ( finalTarget . lock ( ) ) ) {
appliedTarget = finalTarget ;
bool hadAtkSpd = finalTarget . lock ( ) - > GetAtkSpd ( ) > 0 ;
bool hadMoveSpd = finalTarget . lock ( ) - > GetMoveSpd ( ) > 0 ;
bool hadRange = finalTarget . lock ( ) - > GetRange ( ) > 0 ;
bool hadProcedure = finalTarget . lock ( ) - > GetProcedure ( ) > 0 ;
_Attack ( attacker , finalTarget , otherUnits , SOUNDS ) ; //Call the parent function first, followed by the child.
if ( hadAtkSpd & & finalTarget . lock ( ) - > GetAtkSpd ( ) = = 0 ) {
debuffIcons . emplace_back ( IMAGES [ RLD_ICON ] . get ( ) , IMAGES [ RED_X ] . get ( ) , finalTarget . lock ( ) - > GetPos ( ) - vf2d { util : : random ( 12 ) - 6 , 4 } ) ;
}
if ( hadMoveSpd & & finalTarget . lock ( ) - > GetMoveSpd ( ) = = 0 ) {
debuffIcons . emplace_back ( IMAGES [ SPD_ICON ] . get ( ) , IMAGES [ RED_X ] . get ( ) , finalTarget . lock ( ) - > GetPos ( ) - vf2d { util : : random ( 12 ) - 6 , 4 } ) ;
}
if ( hadRange & & finalTarget . lock ( ) - > GetRange ( ) = = 0 ) {
debuffIcons . emplace_back ( IMAGES [ RNG_ICON ] . get ( ) , IMAGES [ RED_X ] . get ( ) , finalTarget . lock ( ) - > GetPos ( ) - vf2d { util : : random ( 12 ) - 6 , 4 } ) ;
}
if ( hadProcedure & & finalTarget . lock ( ) - > GetProcedure ( ) = = 0 ) {
debuffIcons . emplace_back ( IMAGES [ PRC_ICON ] . get ( ) , IMAGES [ RED_X ] . get ( ) , finalTarget . lock ( ) - > GetPos ( ) - vf2d { util : : random ( 12 ) - 6 , 4 } ) ;
}
}
}
}
void Unit : : Update ( PixelGameEngine * pge , std : : vector < std : : unique_ptr < Audio > > & SOUNDS , std : : vector < std : : shared_ptr < Unit > > & queuedUnits ) { }
void Unit : : Attacked ( std : : weak_ptr < Unit > attacker ) { }
void Unit : : _Attacked ( std : : weak_ptr < Unit > attacker , std : : vector < std : : shared_ptr < Unit > > & otherUnits ) {
Attacked ( attacker ) ;
if ( attacker . lock ( ) - > IsFriendly ( ) ! = IsFriendly ( ) & & CanInteractWithEnemies ( ) ) {
SetTargetUnit ( attacker ) ;
}
if ( ! IsFriendly ( ) & & ! attacker . lock ( ) - > IsFriendly ( ) & & ! attacker . lock ( ) - > CanInteractWithAllies ( ) ) {
attacker . lock ( ) - > SetTargetLocation ( attacker . lock ( ) - > GetPos ( ) ) ; //This effectively negates enemies targeting each other for attacking if they're not supposed to. Somehow this occurs sometimes.
}
if ( ! IsFriendly ( ) & & attacker . lock ( ) - > IsFriendly ( ) ! = IsFriendly ( ) ) {
for ( auto & u : otherUnits ) {
if ( this ! = u . get ( ) & & ! u - > IsFriendly ( ) & & u - > GetCurrentTarget ( ) . expired ( ) & & u - > CanMove ( ) & & ! u - > IsAllocator ( ) ) {
geom2d : : line < float > distLine = { GetPos ( ) , u - > GetPos ( ) } ;
if ( distLine . length ( ) < 320 ) {
if ( u - > CanInteractWithEnemies ( ) ) {
u - > SetTargetUnit ( attacker ) ;
} else
if ( u - > CanInteractWithAllies ( ) ) {
u - > SetTargetLocation ( GetPos ( ) ) ;
}
}
}
}
}
}
void Unit : : _Attack ( std : : weak_ptr < Unit > attacker , std : : weak_ptr < Unit > finalTarget , std : : vector < std : : shared_ptr < Unit > > & otherUnits , std : : vector < std : : unique_ptr < Audio > > & SOUNDS ) {
if ( GetProcedure ( ) > 0 & & GetAtkSpd ( ) > 0 ) {
attackFailed = false ;
float procChance = float ( GetProcedure ( ) ) / procedure . size ;
if ( util : : random ( 1 ) > = 1 - procChance ) {
Attack ( * finalTarget . lock ( ) , otherUnits , SOUNDS ) ;
finalTarget . lock ( ) - > _Attacked ( attacker , otherUnits ) ;
reloadTimer = 1.f / ( GetAtkSpd ( ) / 2.f ) ;
if ( GetCurrentTarget ( ) . expired ( ) & & ! IsFriendly ( ) ) {
if ( finalTarget . lock ( ) - > IsFriendly ( ) ! = IsFriendly ( ) & & CanInteractWithEnemies ( ) ) {
SetTargetUnit ( finalTarget ) ;
}
}
} else {
reloadTimer = 1.f / ( GetAtkSpd ( ) / 2.f ) ;
attackFailed = true ;
}
}
}
bool Unit : : InFogOfWar ( ) {
return TileManager : : visibleTiles . count ( GetPos ( ) / 96 ) = = 0 ;
}
bool Unit : : GhostInFogOfWar ( ) {
return TileManager : : visibleTiles . count ( ghostPos / 96 ) = = 0 ;
}
void Unit : : HideGhost ( ) {
ghostPos = { 99999 , - 99999 } ;
}
vf2d Unit : : GetGhostPos ( ) {
return ghostPos ;
}
bool Unit : : IsMoveable ( ) {
return moveable & & attachedPoint . expired ( ) ;
}
bool Unit : : CanInteractWithAllies ( ) {
return friendlyInteractable & & attachedPoint . expired ( ) & & buildTime < = 0 ;
}
bool Unit : : CanInteractWithEnemies ( ) {
return enemyInteractable & & attachedPoint . expired ( ) & & buildTime < = 0 ;
}
Renderable & Unit : : GetImage ( ) {
return img ;
}
std : : weak_ptr < Unit > Unit : : GetCurrentTarget ( ) {
return target ;
}
bool Unit : : AutoAcquiresFriendlyTargets ( ) {
return autoAcquireFriendlyTarget ;
}
bool Unit : : CanMove ( ) {
return moveSpd . size > 0 & & attachedPoint . expired ( ) & & buildTime < = 0 ;
}
void Unit : : SetTargetCollectionPoint ( std : : weak_ptr < CollectionPoint > targetCP , std : : weak_ptr < Unit > self_ptr ) {
SetTargetLocation ( targetCP . lock ( ) - > pos ) ;
attachTarget = targetCP ;
willAttachWhenReachingDestination = true ;
this - > self_ptr = self_ptr ;
}
Pixel Unit : : GetUnitColor ( ) {
Pixel col ;
if ( ! attachedPoint . expired ( ) ) {
switch ( attachedPoint . lock ( ) - > type ) {
case HEALTH : {
return CONSTANT : : HEALTH_COLOR / 2 ;
} break ;
case RANGE : {
return CONSTANT : : RANGE_COLOR / 2 ;
} break ;
case ATKSPD : {
return CONSTANT : : ATKSPD_COLOR / 2 ;
} break ;
case MOVESPD : {
return CONSTANT : : MOVESPD_COLOR / 2 ;
} break ;
case PROCEDURE : {
return CONSTANT : : PROCEDURE_COLOR / 2 ;
} break ;
}
} else {
return friendly ? Pixel { 192 , 192 , 255 } : Pixel { 255 , 192 , 192 } ;
}
}
bool Unit : : ClickHandled ( TileTransformedView & game , Resources & player_resources , std : : vector < std : : shared_ptr < Unit > > & units , std : : vector < std : : unique_ptr < Renderable > > & IMAGES ) {
return false ;
} ;
void Unit : : UpdateGUIState ( TileTransformedView & game , Resources & player_resources , Textbox & displayBox , bool & hovered , int totalUsedMemory , int availableMemory ) { } ;
bool Unit : : IsAllocator ( ) {
return isAllocator & & attachedPoint . expired ( ) & & buildTime < = 0 ;
}
void Unit : : SetBuildUnit ( float buildTime , std : : shared_ptr < Unit > finalUnit , std : : vector < std : : unique_ptr < Audio > > & SOUNDS ) {
if ( this - > IsAllocator ( ) ) {
SOUNDS [ Sound : : SMALLBUILD ] - > Play ( GetPos ( ) ) ;
} else
if ( this - > IsPlatform ( ) ) {
SOUNDS [ Sound : : BIGBUILD ] - > Play ( GetPos ( ) ) ;
}
//Once the units start building they aren't considered allocators/platforms anymore, must do it before.
this - > buildTime = buildTime ;
this - > buildTransformUnit = std : : move ( finalUnit ) ;
}
bool Unit : : IsBuilding ( ) {
return buildTime > 0 ;
}
void Unit : : SetGuardTime ( float time ) {
guardTime = time ;
}
bool Unit : : IsGuarded ( ) {
return guardTime > 0 ;
}
void Unit : : SaveMemory ( ) {
savedMemory = memory ;
}
bool Unit : : IsPlatform ( ) {
return isPlatform & & attachedPoint . expired ( ) & & buildTime < = 0 ;
}
bool Unit : : IsAttached ( ) {
return ! attachedPoint . expired ( ) ;
}
Unit * Unit : : GetBuildUnit ( ) {
return buildTransformUnit . get ( ) ;
}
bool Unit : : IsRAMBank ( ) {
return isRAMBank ;
}