//========================================================================================================================================
// F4 Racing
//----------------------------------------------------------------------------------------------------------------------------------------
// This is an advancement in my game making skills, building off the back of my work on the "Collect the Balls" game.
//----------------------------------------------------------------------------------------------------------------------------------------
// created by Rune
// black box code by Sigonasr2 from the OLC (one lone coder) Discord server
//========================================================================================================================================
# define OLC_PGE_APPLICATION
# include "pixelGameEngine.h" // used for drawing the game
//========================================================================================================================================
// protected variables
//========================================================================================================================================
class Racer : public olc : : PixelGameEngine
{
public :
Racer ( )
{
sAppName = " F4 Racing " ;
}
private :
float car_pos = 0.0f ;
float distance = 0.0f ;
float speed = 0.0f ;
float curvature = 0.0f ;
float track_curve = 0.0f ;
float car_curve = 0.0f ;
float track_dist = 0.0f ;
float cur_lap_time = 0.0f ;
std : : vector < std : : pair < float , float > > vecTrack ;
std : : list < float > list_times ;
bool bothKeysPressed = false ; // checking for dual key press; could probably work with more than 2 keys
// custom control template
olc : : Key Move_Up = olc : : W ;
olc : : Key Move_Down = olc : : S ;
olc : : Key Move_Left = olc : : A ;
olc : : Key Move_Right = olc : : D ;
std : : array < std : : string , 4 > keyslist = { " UP " , " DOWN " , " LEFT " , " RIGHT " } ;
int configKeyIndex = - 1 ;
char pressedKey ;
// selection screen variables
int cup = - 1 ;
int track = - 1 ;
int player = 0 ;
//========================================================================================================================================
// Menu Setup
//========================================================================================================================================
public :
bool activeOption = true ; // highlighting current option
int highlighted ;
struct MenuItem
{
std : : string label ; // label
olc : : vi2d pos ; // label's position
std : : string label2 ; // another label to right of label
} ;
struct Object
{
olc : : vf2d pos { 0 , 0 } ; // the x position represents how far to the left/right it is from the center. -1 ~ 1: road's range; the y position is how far along the track it is
olc : : vf2d size { 1 , 1 } ;
olc : : Decal * decal = nullptr ;
olc : : vf2d offset { 0 , 0 } ; // how far off the image will be
bool rendered = false ;
olc : : vf2d drawPos ;
olc : : vf2d drawSize ;
olc : : vf2d drawOffsetPos ;
} ;
std : : vector < MenuItem > mainMenu ;
std : : vector < MenuItem > playGame ;
std : : vector < MenuItem > pickTrack ;
std : : vector < MenuItem > grandPrix ;
std : : vector < MenuItem > selectCar ;
std : : vector < MenuItem > controls ;
std : : vector < MenuItem > pause ;
std : : vector < std : : string > gameOver ;
std : : vector < Object > gameObjects ;
enum class state
{
MAIN_MENU , PLAY_GAME , CAR_SELECT , CONTROLS , RUN_GAME ,
// game
TRACK_SELECT , CUP_SELECT ,
// tracks
CIRCUIT , FIG_EIGHT , ARROW_HEAD , track_4 , OVERPASS , PEANUT , track_7 , TRIDENT , STAR ,
JX9 , // special track ;)
// cups
INDIE , STREET , PRO , GRAND_PRIX , PAUSED , RESUME
} ;
state gameState = state : : MAIN_MENU ;
//========================================================================================================================================
// Variables
//========================================================================================================================================
// lap times
// player
int score ; // score at end of track; based on final place
int lap ; // current lap player is on
int place ; // current place player is in
// initialize sprites
olc : : Decal * car ;
olc : : Decal * hill ;
olc : : Decal * grass ;
olc : : Decal * road ;
olc : : Decal * banner ;
olc : : Decal * title ;
olc : : Decal * cups ;
olc : : Decal * select ;
std : : string KeyToString ( olc : : Key key )
{
const std : : array < std : : string , 98 > keyStrings =
{
" NONE " , " A " , " B " , " C " , " D " , " E " , " F " , " G " , " H " , " I " , " J " , " K " , " L " , " M " , " N " , " O " , " P " , " Q " , " R " , " S " , " T " , " U " , " V " , " W " , " X " , " Y " , " Z " , " 0 " , " 1 " , " 2 " , " 3 " , " 4 " , " 5 " , " 6 " , " 7 " , " 8 " , " 9 " ,
" F1 " , " F2 " , " F3 " , " F4 " , " F5 " , " F6 " , " F7 " , " F8 " , " F9 " , " F10 " , " F11 " , " F12 " , " UP " , " DOWN " , " LEFT " , " RIGHT " , " SPACE " , " TAB " , " SHIFT " , " CTRL " , " INS " , " DEL " , " HOME " , " END " , " PGUP " , " PGDN " , " BACK " , " ESCAPE " , " ENTER " , " ENTER " ,
" PAUSE " , " SCROLL LK " , " Kp-0 " , " Kp-1 " , " Kp-2 " , " Kp-3 " , " Kp-4 " , " Kp-5 " , " Kp-6 " , " Kp-7 " , " Kp-8 " , " Kp-9 " , " Kp-* " , " Kp-/ " , " Kp-+ " , " Kp-- " , " Kp-. " , " . " , " = " , " , " , " - " , " OEM_1 " ,
" OEM_2 " , " OEM_3 " , " OEM_4 " , " OEM_5 " , " OEM_6 " , " OEM_7 " , " OEM_8 " , " CAPS LOCK " , " END "
} ;
return keyStrings [ key ] ;
}
// list of all track SECTIONS to be drawn per track
// std::pair is the sections of the track in question
std : : vector < std : : pair < float , float > > test_track =
{
{ 0.f , 10.f } ,
{ 0.f , 200.f } ,
{ 1.f , 200.f } ,
{ 0.f , 400.f } ,
{ - 1.f , 100.f } ,
{ 0.f , 200.f } ,
{ - 1.f , 200.f } ,
{ 1.f , 200.f } ,
{ 0.f , 200.f } ,
{ 0.2f , 500.f } ,
{ 0.f , 200.f }
} ;
std : : vector < std : : pair < float , float > > circuit_track =
{
{ 0.f , 10.f } ,
{ 0.f , 270.f } ,
{ - 1.f , 330.f } ,
{ 0.f , 550.f } ,
{ - 1.f , 330.f } ,
{ 0.f , 270.f }
} ;
std : : vector < std : : pair < float , float > > infinity_track =
{
{ 0.f , 10.f } ,
{ 0.f , 270.f } ,
{ 1.f , 330.f } ,
{ 0.f , 550.f } ,
{ 1.f , 330.f } ,
{ 0.f , 270.f }
} ;
// list of ALL tracks to pick from
std : : vector < std : : vector < std : : pair < float , float > > > trackList = {
test_track ,
circuit_track ,
infinity_track ,
} ;
//========================================================================================================================================
// Create Game Objects
//========================================================================================================================================
bool OnUserCreate ( ) override
{
mainMenu = // show menu options
{
{ " START GAME " , { ScreenWidth ( ) / 2 , ScreenHeight ( ) / 5 * 3 } } ,
{ " CONTROLS " , { ScreenWidth ( ) / 2 , ScreenHeight ( ) / 5 * 3 + 64 } } ,
{ " QUIT " , { ScreenWidth ( ) / 2 , ScreenHeight ( ) / 5 * 3 + 128 } }
} ;
playGame =
{
{ " TRACK SELECTION " , { ScreenWidth ( ) / 2 , ScreenHeight ( ) / 4 } } ,
{ " GRAND PRIX " , { ScreenWidth ( ) / 2 , ScreenHeight ( ) / 2 } } ,
{ " BACK " , { ScreenWidth ( ) / 2 , ScreenHeight ( ) / 6 * 5 } }
} ;
pickTrack = // show track options
{
{ " CIRCUIT " , { ScreenWidth ( ) / 4 , ScreenHeight ( ) / 4 } } ,
{ " INFINITY " , { ScreenWidth ( ) / 2 , ScreenHeight ( ) / 4 } } ,
{ " ARROWHEAD " , { ScreenWidth ( ) / 4 * 3 , ScreenHeight ( ) / 4 } } ,
{ " Track 4 " , { ScreenWidth ( ) / 4 , ScreenHeight ( ) / 2 } } ,
{ " OVERPASS " , { ScreenWidth ( ) / 2 , ScreenHeight ( ) / 2 } } ,
{ " PEANUT " , { ScreenWidth ( ) / 4 * 3 , ScreenHeight ( ) / 2 } } ,
{ " Track 7 " , { ScreenWidth ( ) / 4 , ScreenHeight ( ) / 4 * 3 } } ,
{ " TRIDENT " , { ScreenWidth ( ) / 2 , ScreenHeight ( ) / 4 * 3 } } ,
{ " STAR " , { ScreenWidth ( ) / 4 * 3 , ScreenHeight ( ) / 4 * 3 } } ,
{ " BACK " , { ScreenWidth ( ) / 2 , ScreenHeight ( ) / 6 * 5 } }
} ;
grandPrix = // show race cups
{
{ " INDIE CUP " , { ScreenWidth ( ) / 3 , ScreenHeight ( ) / 3 } } ,
{ " STREET CUP " , { ScreenWidth ( ) / 3 * 2 , ScreenHeight ( ) / 3 } } ,
{ " PRO CUP " , { ScreenWidth ( ) / 3 , ScreenHeight ( ) / 3 * 2 } } ,
{ " GRAND PRIX " , { ScreenWidth ( ) / 3 * 2 , ScreenHeight ( ) / 3 * 2 } } ,
{ " BACK " , { ScreenWidth ( ) / 2 , ScreenHeight ( ) / 6 * 5 } }
} ;
selectCar = // pick a color
{
{ " RED " , { ScreenWidth ( ) / 5 , ScreenHeight ( ) / 3 } } ,
{ " BLUE " , { ScreenWidth ( ) / 5 * 2 , ScreenHeight ( ) / 3 } } ,
{ " GREEN " , { ScreenWidth ( ) / 5 * 3 , ScreenHeight ( ) / 3 } } ,
{ " GOLD " , { ScreenWidth ( ) / 5 * 4 , ScreenHeight ( ) / 3 } } ,
{ " PURPLE " , { ScreenWidth ( ) / 5 , ScreenHeight ( ) / 3 * 2 } } ,
{ " WHITE " , { ScreenWidth ( ) / 5 * 2 , ScreenHeight ( ) / 3 * 2 } } ,
{ " ORANGE " , { ScreenWidth ( ) / 5 * 3 , ScreenHeight ( ) / 3 * 2 } } ,
{ " BLACK " , { ScreenWidth ( ) / 5 * 4 , ScreenHeight ( ) / 3 * 2 } } ,
{ " BACK " , { ScreenWidth ( ) / 2 , ScreenHeight ( ) / 6 * 5 } }
} ;
controls = // customize controls
{
{ " FORWARD " , { ScreenWidth ( ) / 3 , ScreenHeight ( ) / 9 * 2 } , KeyToString ( Move_Up ) } ,
{ " BACK " , { ScreenWidth ( ) / 3 , ScreenHeight ( ) / 9 * 3 } , KeyToString ( Move_Down ) } ,
{ " LEFT " , { ScreenWidth ( ) / 3 , ScreenHeight ( ) / 9 * 4 } , KeyToString ( Move_Left ) } ,
{ " RIGHT " , { ScreenWidth ( ) / 3 , ScreenHeight ( ) / 9 * 5 } , KeyToString ( Move_Right ) } ,
{ " DEFAULT " , { ScreenWidth ( ) / 2 , ScreenHeight ( ) / 9 * 6 } } ,
{ " BACK " , { ScreenWidth ( ) / 2 , ScreenHeight ( ) / 6 * 5 } }
} ;
pause =
{
{ " RESUME " , { ScreenWidth ( ) / 2 , ScreenHeight ( ) / 9 * 3 } } ,
{ " RESTART " , { ScreenWidth ( ) / 2 , ScreenHeight ( ) / 9 * 4 } } ,
{ " MENU " , { ScreenWidth ( ) / 2 , ScreenHeight ( ) / 9 * 5 } } ,
{ " QUIT " , { ScreenWidth ( ) / 2 , ScreenHeight ( ) / 9 * 6 } }
} ;
gameOver = { " Return to Main Menu " } ;
//========================================================================================================================================
// load sprites
car = new olc : : Decal ( new olc : : Sprite ( " art/car.png " ) ) ; // will contain 8 cars, with 3 angles per car
hill = new olc : : Decal ( new olc : : Sprite ( " art/hills.png " ) , false , false ) ;
grass = new olc : : Decal ( new olc : : Sprite ( " art/grass.png " ) , false , false ) ;
road = new olc : : Decal ( new olc : : Sprite ( " art/road.png " ) , false , false ) ; // holds both road and flag line
banner = new olc : : Decal ( new olc : : Sprite ( " art/start.png " ) , false , false ) ;
title = new olc : : Decal ( new olc : : Sprite ( " art/title.png " ) ) ;
cups = new olc : : Decal ( new olc : : Sprite ( " art/cups.png " ) ) ; // 64 px tiles; 128 px image
select = new olc : : Decal ( new olc : : Sprite ( " art/selection.png " ) ) ;
//========================================================================================================================================
//========================================================================================================================================
// may need to separate the track list into it's own function so as to declutter this one
// especially, given that there's a bunch of stuff going on with the tracks with much redundancy
//trackList= {track_1, track_2};
// JX9 track layout
// javidx9 =
//{
// // they'll likely be wonky for the game as it's not really modified to fit my units of
// // these happen to be measured in meters, as per Javid's original constructionmeasure
// vecTrack.push_back(std::make_pair(0.0f, 10.0f)); // flag (straight)
// vecTrack.push_back(std::make_pair(0.0f, 200.0f)); // straight
// vecTrack.push_back(std::make_pair(1.0f, 200.0f)); // sharp right
// vecTrack.push_back(std::make_pair(0.0f, 400.0f)); // long straight
// vecTrack.push_back(std::make_pair(-1.0f, 100.0f)); // soft left
// vecTrack.push_back(std::make_pair(0.0f, 200.0f)); // straight
// vecTrack.push_back(std::make_pair(-1.0f, 200.0f)); // sharp left
// vecTrack.push_back(std::make_pair(1.0f, 200.0f)); // sharp right
// vecTrack.push_back(std::make_pair(0.0f, 200.0f)); // straight
// vecTrack.push_back(std::make_pair(0.2f, 500.0f)); // gradual right
// vecTrack.push_back(std::make_pair(0.0f, 200.0f)); // straight
//}
// all 9 tracks below are running on yards for their distance units
// this means the end results are in miles; rather than kilometers
// enjoy the US measures of Burgers per Freedom Eagle
/*{
// Classic Oval
// Circuit =
{
// 1 mile loop; 1,760 yards
vecTrack . push_back ( std : : make_pair ( 0.0f , 10.0f ) ) ; // flag (straight)
vecTrack . push_back ( std : : make_pair ( 0.0f , 270.0f ) ) ; // straight
vecTrack . push_back ( std : : make_pair ( - 1.0f , 330.0f ) ) ; // left
vecTrack . push_back ( std : : make_pair ( 0.0f , 550.0f ) ) ; // straight
vecTrack . push_back ( std : : make_pair ( - 1.0f , 330.0f ) ) ; // left
vecTrack . push_back ( std : : make_pair ( 0.0f , 270.0f ) ) ; // straight
}
// Infinity
// FigureEight =
{
vecTrack . push_back ( std : : make_pair ( 0.0f , 10.0f ) ) ; // flag (straight); this is positioned somewhere after the intersection
vecTrack . push_back ( std : : make_pair ( 0.0f , 0.0f ) ) ; // straight
vecTrack . push_back ( std : : make_pair ( - 1.0f , 0.0f ) ) ; // left
vecTrack . push_back ( std : : make_pair ( 0.0f , 0.0f ) ) ; // straight; this crosses the first straight, to provide for chances of crashing
vecTrack . push_back ( std : : make_pair ( 1.0f , 0.0f ) ) ; // right
vecTrack . push_back ( std : : make_pair ( 0.0f , 0.0f ) ) ; // straight
}
// Arrow Head
// ArrowHead =
{
// miles
vecTrack . push_back ( std : : make_pair ( 0.0f , 10.0f ) ) ; // flag (straight)
vecTrack . push_back ( std : : make_pair ( 0.0f , 0.0f ) ) ; // straight
vecTrack . push_back ( std : : make_pair ( 1.0f , 0.0f ) ) ; // right
vecTrack . push_back ( std : : make_pair ( 0.0f , 0.0f ) ) ; // straight
vecTrack . push_back ( std : : make_pair ( 1.0f , 0.0f ) ) ; // right
vecTrack . push_back ( std : : make_pair ( - 1.0f , 0.0f ) ) ; // left
vecTrack . push_back ( std : : make_pair ( 1.0f , 0.0f ) ) ; // right
vecTrack . push_back ( std : : make_pair ( 0.0f , 0.0f ) ) ; // straight
}
// track 4
// ??? =
{
vecTrack . push_back ( std : : make_pair ( 0.0f , 10.0f ) ) ; // flag (straight)
}
// Over/Under
// OverPass =
{
vecTrack . push_back ( std : : make_pair ( 0.0f , 10.0f ) ) ; // flag (straight)
vecTrack . push_back ( std : : make_pair ( 0.0f , 0.0f ) ) ; // straight
vecTrack . push_back ( std : : make_pair ( 1.0f , 0.0f ) ) ; // right
vecTrack . push_back ( std : : make_pair ( 0.0f , 0.0f ) ) ; // straight
vecTrack . push_back ( std : : make_pair ( 1.0f , 0.0f ) ) ; // right
vecTrack . push_back ( std : : make_pair ( 1.0f , 0.0f ) ) ; // right
vecTrack . push_back ( std : : make_pair ( 0.0f , 0.0f ) ) ; // straight
vecTrack . push_back ( std : : make_pair ( 1.0f , 0.0f ) ) ; // right
vecTrack . push_back ( std : : make_pair ( 0.0f , 0.0f ) ) ; // straight
}
// Peanut
// Peanut =
{
vecTrack . push_back ( std : : make_pair ( 0.0f , 10.0f ) ) ; // flag (straight)
}
// track 7
// ??? =
{
vecTrack . push_back ( std : : make_pair ( 0.0f , 10.0f ) ) ; // flag (straight)
}
// Trident
// Trident =
{
vecTrack . push_back ( std : : make_pair ( 0.0f , 10.0f ) ) ; // flag (straight)
}
// Star
// ??? =
{
vecTrack . push_back ( std : : make_pair ( 0.0f , 10.0f ) ) ; // flag (straight)
}
} //*/
for ( auto t : vecTrack )
{
track_dist + = t . second ;
}
distance = track_dist - 100 ;
list_times = { 0 , 0 , 0 , 0 , 0 } ;
gameObjects . push_back ( { /*{-1.5,track_dist - 50}, {10,6}, {0,240}*/ } ) ;
return true ;
}
//========================================================================================================================================
// reset game
//========================================================================================================================================
void Reset ( )
{
car_pos = 0.0f ;
distance = 0.0f ;
speed = 0.0f ;
curvature = 0.0f ;
track_curve = 0.0f ;
car_curve = 0.0f ;
//track_dist = 0.0f; will need to be moved elsewhere; the game will calculate *ALL* track sizes at the start, and then simply use the appropriate numbers when loading each track on request
cur_lap_time = 0.0f ;
// Note: the following all need to be reset during the process, when they've been implemented
// -player position (on screen)
// -player position (on track)
// -track section
// -all lap times
// -score
//
// if called from other parts of the program, other than the 'F1' key, it should be resetting to the menu
// the 'F1' key is meant to reset the player on the current track they're on
//
}
//========================================================================================================================================
// main menu
//========================================================================================================================================
void DisplayMenu ( std : : vector < MenuItem > list )
{
// drawing and coloring menu selections
for ( int i = 0 ; i < list . size ( ) ; i + + )
{
olc : : vi2d textSize = GetTextSize ( list [ i ] . label ) / 2 * 4 ;
olc : : Pixel textCol = olc : : GREY ; // greys out unselected options
if ( highlighted = = i )
{
textCol = olc : : Pixel ( 255 , 201 , 14 ) ; // gold
}
DrawStringDecal ( list [ i ] . pos - textSize , list [ i ] . label , textCol , { 4.0f , 4.0f } ) ;
if ( list [ i ] . label2 . size ( ) > 0 )
{
olc : : vi2d offset = { ScreenWidth ( ) / 3 , - textSize . y / 2 } ;
DrawStringDecal ( list [ i ] . pos + offset , list [ i ] . label2 , textCol , { 4.0f , 4.0f } ) ;
}
}
// selecting options
highlighted = std : : clamp ( highlighted , 0 , ( int ) list . size ( ) - 1 ) ; // loop if out of range
int shortest = 9999999 ;
int largest = - 9999999 ;
int targetItem = highlighted ;
//========================================================================================================================================
if ( GetKey ( olc : : Key : : DOWN ) . bPressed )
{
for ( int i = 0 ; i < list . size ( ) ; i + + )
{
if ( highlighted ! = i )
{
if ( list [ highlighted ] . pos . x = = list [ i ] . pos . x )
{
if ( list [ highlighted ] . pos . y < list [ i ] . pos . y )
{
// it's below us
int dist = abs ( list [ highlighted ] . pos . y - list [ i ] . pos . y ) ;
if ( dist < shortest )
{
targetItem = i ;
shortest = dist ;
}
}
}
}
}
for ( int i = 0 ; i < list . size ( ) ; i + + )
{
if ( highlighted ! = i )
{
if ( list [ highlighted ] . pos . y < list [ i ] . pos . y )
{
// it's above us
int dist = abs ( list [ highlighted ] . pos . y - list [ i ] . pos . y ) ;
if ( dist < shortest )
{
targetItem = i ;
shortest = dist ;
}
}
}
}
// it failed, try another method
if ( highlighted = = targetItem )
{
targetItem = 0 ;
}
highlighted = targetItem ;
}
//========================================================================================================================================
if ( GetKey ( olc : : Key : : UP ) . bPressed )
{
for ( int i = 0 ; i < list . size ( ) ; i + + )
{
if ( highlighted ! = i )
{
if ( list [ highlighted ] . pos . x = = list [ i ] . pos . x )
{
if ( list [ highlighted ] . pos . y > list [ i ] . pos . y )
{
// it's above us
int dist = abs ( list [ highlighted ] . pos . y - list [ i ] . pos . y ) ;
if ( dist < shortest )
{
targetItem = i ;
shortest = dist ;
}
}
}
}
}
for ( int i = 0 ; i < list . size ( ) ; i + + )
{
if ( highlighted ! = i )
{
if ( list [ highlighted ] . pos . y > list [ i ] . pos . y )
{
// it's below us
int dist = abs ( list [ highlighted ] . pos . y - list [ i ] . pos . y ) ;
if ( dist < shortest )
{
targetItem = i ;
shortest = dist ;
}
}
}
}
// it failed, try another method
if ( highlighted = = targetItem )
{
targetItem = list . size ( ) - 1 ;
}
highlighted = targetItem ;
}
//========================================================================================================================================
if ( GetKey ( olc : : Key : : RIGHT ) . bPressed )
{
for ( int i = 0 ; i < list . size ( ) ; i + + )
{
if ( highlighted ! = i )
{
if ( list [ highlighted ] . pos . y = = list [ i ] . pos . y )
{
if ( list [ highlighted ] . pos . x < list [ i ] . pos . x )
{
// it's to the right
int dist = abs ( list [ highlighted ] . pos . x - list [ i ] . pos . x ) ;
if ( dist < shortest )
{
targetItem = i ;
shortest = dist ;
}
}
}
}
}
if ( highlighted = = targetItem )
{
for ( int i = 0 ; i < list . size ( ) ; i + + )
{
if ( highlighted ! = i )
{
if ( list [ highlighted ] . pos . y = = list [ i ] . pos . y )
{
if ( list [ highlighted ] . pos . x > list [ i ] . pos . x )
{
// it's to the left
int dist = abs ( list [ highlighted ] . pos . x - list [ i ] . pos . x ) ;
if ( dist > largest )
{
targetItem = i ;
largest = dist ;
}
}
}
}
}
}
highlighted = targetItem ;
}
//========================================================================================================================================
if ( GetKey ( olc : : Key : : LEFT ) . bPressed )
{
for ( int i = 0 ; i < list . size ( ) ; i + + )
{
if ( highlighted ! = i )
{
if ( list [ highlighted ] . pos . y = = list [ i ] . pos . y )
{
if ( list [ highlighted ] . pos . x > list [ i ] . pos . x )
{
// it's to the right
int dist = abs ( list [ highlighted ] . pos . x - list [ i ] . pos . x ) ;
if ( dist < shortest )
{
targetItem = i ;
shortest = dist ;
}
}
}
}
}
if ( highlighted = = targetItem )
{
for ( int i = 0 ; i < list . size ( ) ; i + + )
{
if ( highlighted ! = i )
{
if ( list [ highlighted ] . pos . y = = list [ i ] . pos . y )
{
if ( list [ highlighted ] . pos . x < list [ i ] . pos . x )
{
// it's to the left
int dist = abs ( list [ highlighted ] . pos . x - list [ i ] . pos . x ) ;
if ( dist > largest )
{
targetItem = i ;
largest = dist ;
}
}
}
}
}
}
highlighted = targetItem ;
}
//========================================
if ( highlighted > = ( int ) list . size ( ) )
{
highlighted = 0 ; // wrap around when above max
}
if ( highlighted < 0 )
{
highlighted = list . size ( ) - 1 ; // wrap around when below zero
}
}
//========================================================================================================================================
// controls
//========================================================================================================================================
void GetAnyKeyPress ( olc : : Key keypress ) override
{
if ( configKeyIndex ! = - 1 )
{
switch ( configKeyIndex )
{
case 0 :
{
Move_Up = keypress ;
} break ;
case 1 :
{
Move_Right = keypress ;
} break ;
case 2 :
{
Move_Left = keypress ;
} break ;
case 3 :
{
Move_Down = keypress ;
} break ;
}
controls [ configKeyIndex ] . label2 = KeyToString ( keypress ) ;
configKeyIndex = - 1 ;
}
}
//========================================================================================================================================
// Main Game Function
//========================================================================================================================================
bool OnUserUpdate ( float fElapsedTime ) override
{
if ( GetKey ( olc : : Key : : CTRL ) . bHeld & & GetKey ( olc : : Key : : F12 ) . bHeld & & ! bothKeysPressed )
{
return false ;
}
// draw skybox
GradientFillRectDecal ( { 0.0f , 0.0f } , { ScreenWidth ( ) + 0.0f , ScreenHeight ( ) / 2 + 0.0f } , olc : : DARK_BLUE , olc : : BLUE , olc : : BLUE , olc : : DARK_BLUE ) ;
// the hills have eyes; y'know, like in Super Mario Bros. What reference did you think was going to be here?
DrawPartialDecal ( { 0.0f , ScreenHeight ( ) / 2 - 100.0f } , hill , { 0.0f + track_curve * 200 , 0.0f } , { ScreenWidth ( ) + 0.0f , ( float ) hill - > sprite - > height } , { 1.5f , 1.0f } ) ;
//DrawPartialDecal({ 180.0f, ScreenHeight() / 2 - 100.0f }, hill, { 0.0f + track_curve * 200, 0.0f }, { ScreenWidth() + 0.0f, (float)hill->sprite->height });
DrawPartialWarpedDecal // draw grass
(
grass , { { 0.0f , ScreenHeight ( ) / 2 + 0.0f } ,
{ - ScreenWidth ( ) * 2 + 0.0f , ScreenHeight ( ) + 0.0f } ,
{ ScreenWidth ( ) * 2 + ScreenWidth ( ) + 0.0f , ScreenHeight ( ) + 0.0f } ,
{ ScreenWidth ( ) + 0.0f , ScreenHeight ( ) / 2 + 0.0f } } ,
{ 0.0f , - distance } , { 1 * 32 , 4 * 64 }
) ;
//========================================================================================================================================
if ( gameState = = state : : MAIN_MENU )
{
FillRectDecal ( { 0 , 0 } , { ScreenWidth ( ) + 0.f , ScreenHeight ( ) + 0.f } , olc : : Pixel ( 0 , 0 , 0 , 16 ) ) ;
// display title
DrawDecal ( { 95.5f , 95.5f } , title /*, {0.4f, 0.4f}*/ ) ;
DisplayMenu ( mainMenu ) ;
//DrawStringDecal({ ScreenWidth() / 2 + 0.0f, ScreenHeight() / 2 + 0.0f }, "START GAME", olc::WHITE, { 4.0f, 4.0f });
if ( GetKey ( olc : : Key : : ENTER ) . bPressed | | GetKey ( olc : : Key : : SPACE ) . bPressed )
{
// vertical menu
if ( highlighted = = 0 )
{
gameState = state : : PLAY_GAME ;
highlighted = 0 ;
}
else if ( highlighted = = 1 )
{
gameState = state : : CONTROLS ;
highlighted = 0 ;
}
else
{
return false ; // quit game
}
}
if ( GetKey ( olc : : Key : : ESCAPE ) . bPressed )
{
return false ; // exit via Escape key
}
}
//========================================================================================================================================
else if ( gameState = = state : : PLAY_GAME )
{
FillRectDecal ( { 0 , 0 } , { ScreenWidth ( ) + 0.f , ScreenHeight ( ) + 0.f } , olc : : Pixel ( 0 , 0 , 0 , 16 ) ) ;
DisplayMenu ( playGame ) ;
if ( GetKey ( olc : : Key : : ENTER ) . bPressed | | GetKey ( olc : : Key : : SPACE ) . bPressed )
{
// vertical menu
if ( highlighted = = 0 )
{
gameState = state : : TRACK_SELECT ;
highlighted = 0 ;
}
else if ( highlighted = = 1 )
{
gameState = state : : CUP_SELECT ;
highlighted = 0 ;
}
else
{
gameState = state : : MAIN_MENU ; // return to menu
highlighted = 0 ;
}
}
if ( GetKey ( olc : : Key : : ESCAPE ) . bPressed | | GetKey ( olc : : Key : : BACK ) . bPressed )
{
gameState = state : : MAIN_MENU ;
highlighted = 0 ;
}
}
//========================================================================================================================================
else if ( gameState = = state : : TRACK_SELECT )
{
FillRectDecal ( { 0 , 0 } , { ScreenWidth ( ) + 0.f , ScreenHeight ( ) + 0.f } , olc : : Pixel ( 0 , 0 , 0 , 16 ) ) ;
DisplayMenu ( pickTrack ) ;
if ( GetKey ( olc : : Key : : ENTER ) . bPressed | | GetKey ( olc : : Key : : SPACE ) . bPressed )
{
if ( highlighted > = 0 & & highlighted < = 8 )
{
track = highlighted + 1 ;
gameState = state : : CAR_SELECT ;
highlighted = 0 ;
}
else
{
gameState = state : : PLAY_GAME ;
highlighted = 0 ;
}
}
if ( GetKey ( olc : : Key : : CTRL ) . bHeld & & GetKey ( olc : : Key : : F9 ) . bHeld )
{
// okay, so, gotta work out how to do cheat codes it seems xD
track = 0 ;
gameState = state : : CAR_SELECT ;
}
if ( GetKey ( olc : : Key : : ESCAPE ) . bPressed | | GetKey ( olc : : Key : : BACK ) . bPressed )
{
gameState = state : : PLAY_GAME ;
highlighted = 0 ;
}
}
//========================================================================================================================================
else if ( gameState = = state : : CUP_SELECT )
{
FillRectDecal ( { 0 , 0 } , { ScreenWidth ( ) + 0.f , ScreenHeight ( ) + 0.f } , olc : : Pixel ( 0 , 0 , 0 , 16 ) ) ;
DrawPartialDecal ( { ScreenWidth ( ) / 3 - 96.f , ScreenHeight ( ) / 3 - 192.f } , cups , { 0 , 0 } , { 64 , 64 } , { 3.0f , 3.0f } , ( highlighted = = 0 ) ? olc : : WHITE : olc : : DARK_GREY ) ; // bronze
DrawPartialDecal ( { ScreenWidth ( ) / 3 * 2 - 96.f , ScreenHeight ( ) / 3 - 192.f } , cups , { 64 , 0 } , { 64 , 64 } , { 3.0f , 3.0f } , ( highlighted = = 1 ) ? olc : : WHITE : olc : : DARK_GREY ) ; // silver
DrawPartialDecal ( { ScreenWidth ( ) / 3 - 96.f , ScreenHeight ( ) / 3 * 2 - 192.f } , cups , { 0 , 64 } , { 64 , 64 } , { 3.0f , 3.0f } , ( highlighted = = 2 ) ? olc : : WHITE : olc : : DARK_GREY ) ; // gold
DrawPartialDecal ( { ScreenWidth ( ) / 3 * 2 - 96.f , ScreenHeight ( ) / 3 * 2 - 192.f } , cups , { 64 , 64 } , { 64 , 64 } , { 3.0f , 3.0f } , ( highlighted = = 3 ) ? olc : : WHITE : olc : : DARK_GREY ) ; // platinum
DisplayMenu ( grandPrix ) ;
if ( GetKey ( olc : : Key : : ENTER ) . bPressed | | GetKey ( olc : : Key : : SPACE ) . bPressed )
{
// 2 x 2 menu
if ( highlighted = = 0 )
{
gameState = state : : INDIE ;
cup = 0 ;
gameState = state : : CAR_SELECT ;
highlighted = 0 ;
}
else if ( highlighted = = 1 )
{
gameState = state : : STREET ;
cup = 1 ;
gameState = state : : CAR_SELECT ;
highlighted = 0 ;
}
else if ( highlighted = = 2 )
{
gameState = state : : PRO ;
cup = 2 ;
gameState = state : : CAR_SELECT ;
highlighted = 0 ;
}
else if ( highlighted = = 3 )
{
gameState = state : : GRAND_PRIX ;
cup = 3 ;
gameState = state : : CAR_SELECT ;
highlighted = 0 ;
}
else
{
gameState = state : : PLAY_GAME ; // return to menu
cup = - 1 ;
highlighted = 0 ;
}
}
if ( GetKey ( olc : : Key : : ESCAPE ) . bPressed | | GetKey ( olc : : Key : : BACK ) . bPressed )
{
gameState = state : : PLAY_GAME ;
highlighted = 0 ;
}
}
//========================================================================================================================================
else if ( gameState = = state : : CAR_SELECT )
{
FillRectDecal ( { 0 , 0 } , { ScreenWidth ( ) + 0.f , ScreenHeight ( ) + 0.f } , olc : : Pixel ( 0 , 0 , 0 , 16 ) ) ;
DisplayMenu ( selectCar ) ;
if ( GetKey ( olc : : Key : : ENTER ) . bPressed | | GetKey ( olc : : Key : : SPACE ) . bPressed )
{
if ( highlighted > = 0 & & highlighted < = 7 )
{
gameState = state : : RUN_GAME ;
vecTrack . clear ( ) ;
for ( int i = 0 ; i < trackList [ track ] . size ( ) ; i + + )
{
vecTrack . push_back ( trackList [ track ] [ i ] ) ;
track_dist + = vecTrack [ i ] . second ;
}
}
else
{
if ( cup ! = - 1 )
{
// return to cup select
gameState = state : : CUP_SELECT ;
}
if ( track ! = - 1 )
{
// return to track select
gameState = state : : TRACK_SELECT ;
}
highlighted = 0 ;
}
}
if ( GetKey ( olc : : Key : : ESCAPE ) . bPressed | | GetKey ( olc : : Key : : BACK ) . bPressed )
{
if ( cup ! = - 1 )
{
// return to cup select
gameState = state : : CUP_SELECT ;
}
if ( track ! = - 1 )
{
// return to track select
gameState = state : : TRACK_SELECT ;
}
highlighted = 0 ;
}
}
//========================================================================================================================================
else if ( gameState = = state : : CONTROLS )
{
FillRectDecal ( { 0 , 0 } , { ScreenWidth ( ) + 0.f , ScreenHeight ( ) + 0.f } , olc : : Pixel ( 0 , 0 , 0 , 16 ) ) ;
DisplayMenu ( controls ) ;
if ( GetKey ( olc : : Key : : ENTER ) . bPressed )
{
if ( highlighted = = 0 )
{
configKeyIndex = highlighted ;
}
else if ( highlighted = = 1 )
{
configKeyIndex = highlighted ;
}
else if ( highlighted = = 2 )
{
configKeyIndex = highlighted ;
}
else if ( highlighted = = 3 )
{
configKeyIndex = highlighted ;
}
else if ( highlighted = = 4 )
{
// reset default controls
DisplayMenu ( controls ) ;
Move_Up = olc : : W ;
Move_Down = olc : : S ;
Move_Left = olc : : A ;
Move_Right = olc : : D ;
}
else
{
gameState = state : : MAIN_MENU ;
highlighted = 0 ;
}
}
if ( GetKey ( olc : : Key : : ESCAPE ) . bPressed | | GetKey ( olc : : Key : : BACK ) . bPressed )
{
gameState = state : : MAIN_MENU ;
}
}
//========================================================================================================================================
else if ( gameState = = state : : PAUSED ) // pause menu
{
FillRectDecal ( { 0 , 0 } , { ScreenWidth ( ) + 0.f , ScreenHeight ( ) + 0.f } , olc : : Pixel ( 0 , 0 , 0 , 64 ) ) ;
DisplayMenu ( pause ) ;
if ( GetKey ( olc : : Key : : ENTER ) . bPressed )
{
if ( highlighted = = 0 )
{
gameState = state : : RUN_GAME ;
highlighted = 0 ;
}
if ( highlighted = = 1 )
{
// restart the track
// this will ONLY work on tracks after the track selection screen
// or on the first track in each cup (1, 4, 7, 1)
Reset ( ) ;
gameState = state : : RUN_GAME ;
highlighted = 0 ;
}
if ( highlighted = = 2 )
{
// main menu
Reset ( ) ;
gameState = state : : MAIN_MENU ;
highlighted = 0 ;
}
if ( highlighted = = 3 )
{
return false ;
}
}
}
//========================================================================================================================================
//========================================================================================================================================
else if ( gameState = = state : : RUN_GAME ) // run main game loop
{
Clear ( olc : : BLACK ) ; // this still needed? we've since replaced it with the gradient sky box
if ( GetKey ( olc : : Key : : ESCAPE ) . bPressed | | GetKey ( olc : : Key : : PAUSE ) . bPressed )
{
gameState = state : : PAUSED ;
}
// debug quick-reset
if ( GetKey ( olc : : Key : : F4 ) . bPressed )
{
Reset ( ) ;
}
// get a point on the track
float offset = 0 ;
int TrackSection = 0 ;
cur_lap_time + = fElapsedTime ;
// record lap time
if ( distance > = track_dist )
{
distance - = track_dist ;
list_times . push_front ( cur_lap_time ) ; // push time to front
list_times . pop_back ( ) ; // pop time to back
cur_lap_time = 0.0f ;
}
// find position on track (could optimize) << should probably optimize given the complexity of this game
while ( TrackSection < vecTrack . size ( ) & & offset < = distance )
{
offset + = vecTrack [ TrackSection ] . second ;
TrackSection + + ;
}
float TargetCurvature = vecTrack [ TrackSection - 1 ] . first ; // drawing curves to screen
float TrackCurveDiff = ( TargetCurvature - curvature ) * fElapsedTime * speed ; // correcting the drawing of curves to the screen
curvature + = TrackCurveDiff ; // update track curve difference
track_curve + = ( curvature ) * fElapsedTime * speed ;
//========================================================================================================================================
// | | draw racetrack canvas
//========================================================================================================================================
std : : vector < olc : : vf2d > pos ;
std : : vector < olc : : vf2d > uvs ;
for ( int y = 0 ; y < ScreenHeight ( ) / 2 ; y + + )
{
// racetrack canvas variables
float perspective = ( float ) y / ( ScreenHeight ( ) / 2.0f ) ;
float mid_point = 0.5f + curvature * powf ( ( 1.0f - perspective ) , 3 ) ;
float road_width = 0.1f + perspective * 0.8f ;
float curb_width = road_width * 0.15f ;
road_width * = 0.5f ;
int lf_curb = ( mid_point - road_width ) * ScreenWidth ( ) ;
int rt_curb = ( mid_point + road_width ) * ScreenWidth ( ) ;
int row = ScreenHeight ( ) / 2 + y ;
float horizon = powf ( 1.0f - perspective , 2 ) * 80 ;
// check for finish line
if ( distance + horizon > track_dist - 20 & & distance + horizon < track_dist )
{
// draw checkerboard pattern
uvs . push_back ( { 0 , ( float ) fmod ( ( ( distance + horizon ) - ( track_dist - 20 ) ) / 20.0f , 0.5f ) + 0.5f } ) ;
uvs . push_back ( { 1 , ( float ) fmod ( ( ( distance + horizon ) - ( track_dist - 20 ) ) / 20.0f , 0.5f ) + 0.5f } ) ;
}
else
{
// draw standard track piece
uvs . push_back ( { 0 , sinf ( 80.0f * powf ( 1.0f - perspective , 2 ) + distance ) / 4 + 0.25f } ) ;
uvs . push_back ( { 1 , sinf ( 80.0f * powf ( 1.0f - perspective , 2 ) + distance ) / 4 + 0.25f } ) ;
}
// drawing grass and curb
pos . push_back ( { lf_curb + 0.0f , row + 0.0f } ) ;
pos . push_back ( { rt_curb + 0.0f , row + 0.0f } ) ;
// black box rendering shenanigans; if I ever understand programming enough at a later date, I can figure out wtf is going on here
int i = 0 ;
for ( Object & obj : gameObjects )
{
float horizonRange = powf ( 1.0f - perspective , 2 ) * ( ( distance + horizon ) - obj . pos . y ) / horizon ;
if ( ! obj . rendered & & ( distance + horizon ) > obj . pos . y & & ( distance + horizon ) < obj . pos . y + 80 & & row > = ScreenHeight ( ) / 2 + ScreenHeight ( ) / 2 * horizonRange )
{
obj . drawPos = { ( mid_point + road_width * obj . pos . x ) * ScreenWidth ( ) , horizonRange * ( ScreenHeight ( ) / 2 ) + ScreenHeight ( ) / 2 } ;
obj . drawOffsetPos = { obj . offset . x , obj . size . y * road_width * - obj . offset . y } ;
obj . drawSize = { obj . size . x * road_width , obj . size . y * road_width } ;
obj . rendered = true ;
// yeah.... so, even after copying it manually, i still have no clue what this thing is doing, lol
}
}
}
// on the road again; drawing the track
SetDecalStructure ( olc : : DecalStructure : : STRIP ) ;
DrawPolygonDecal ( road , pos , uvs ) ;
SetDecalStructure ( olc : : DecalStructure : : FAN ) ;
// draw trackside props
for ( Object & obj : gameObjects )
{
if ( obj . rendered )
{
if ( obj . decal ! = nullptr )
{
DrawDecal ( obj . drawPos + obj . drawOffsetPos , obj . decal , obj . drawSize ) ;
}
else
{
FillRectDecal ( obj . drawPos , obj . drawSize , olc : : BLUE ) ;
}
}
}
//========================================================================================================================================
// | | car movement
//========================================================================================================================================
int car_dir = 0 ; // default car facing
if ( GetKey ( Move_Up ) . bHeld )
{
speed + = 2.0f * fElapsedTime ;
}
else // might remove this in a later stage of development
{
speed - = 1.0f * fElapsedTime ;
}
if ( GetKey ( Move_Down ) . bHeld )
{
// move back ?
distance - = 20.0f * fElapsedTime ;
}
if ( GetKey ( Move_Left ) . bHeld )
{
car_curve - = 0.7f * fElapsedTime ;
car_dir = - 1 ;
}
if ( GetKey ( Move_Right ) . bHeld )
{
car_curve + = 0.7f * fElapsedTime ;
car_dir = + 1 ;
}
// slow car if on grass
if ( fabs ( car_curve - track_curve ) > = 0.8f )
{
speed - = 5.0f * fElapsedTime ;
}
// clamp speed
if ( speed < 0.0f ) speed = 0.0f ;
if ( speed > 1.0f ) speed = 1.0f ;
// move car along track according to car speed
distance + = ( 70.0f * speed ) * fElapsedTime ;
// draw car
car_pos = car_curve - track_curve ;
DrawDecal ( { ScreenWidth ( ) / 2 - 128 + car_pos * ScreenWidth ( ) / 2 , ScreenHeight ( ) / 4 * 3.0f - 128 } , car ) ;
SetPixelMode ( olc : : Pixel : : ALPHA ) ;
//========================================================================================================================================
//========================================================================================================================================
// spedometer
std : : string mph = std : : to_string ( speed ) ;
DrawString ( 4 , 4 , mph ) ;
// show time
auto disp_time = [ ] ( float t )
{
int min = t / 60.0f ;
int sec = t - ( min * 60.0f ) ;
int milli = ( t - ( float ) sec ) * 1000.0f ;
// need to change this out to a 'DrawString()' function instead
return std : : to_string ( min ) + " . " + std : : to_string ( sec ) + " : " + std : : to_string ( milli ) ;
} ;
// correct for the first '0' in the seconds timer
//if ()
//{
// // draw min + '0' + sec + milli
// DrawString(4, 16, disp_time(cur_lap_time));
//}
//else
//{
DrawString ( 4 , 16 , disp_time ( cur_lap_time ) ) ;
//}
// display last 5 lap times
int j = 30 ;
for ( auto l : list_times )
{
DrawString ( 10 , j , disp_time ( l ) ) ;
j + = 10 ;
}
// debug code
//std::string numb1 = std::to_string(car_pos);
//DrawString(4, 26, numb1);
//std::string numb2 = std::to_string(track_dist);
//DrawString(4, 38, numb2);
}
return true ;
}
} ;
//========================================================================================================================================
int main ( )
{
Racer game ;
if ( game . Construct ( 1280 , 720 , 1 , 1 ) )
game . Start ( ) ;
return 0 ;
}
//========================================================================================================================================
// END OF FILE
//========================================================================================================================================