diff --git a/C++ProjectTemplate b/C++ProjectTemplate index 6425f6a..ad922a7 100755 Binary files a/C++ProjectTemplate and b/C++ProjectTemplate differ diff --git a/main.cpp b/main.cpp index 6ce8258..048720d 100644 --- a/main.cpp +++ b/main.cpp @@ -9,8 +9,6 @@ #define OLC_PGE_APPLICATION #include "pixelGameEngine.h" // used for drawing the game -using namespace olc; - //======================================================================================================================================== // protected variables //======================================================================================================================================== @@ -23,27 +21,27 @@ public: } private: - float car_pos = 0.0f; - float distance = 0.0f; - float speed = 0.0f; + float car_pos = 0.f; // track the horizontal location of player + float distance = 0.f; // track the "vertical" location of player; basically, they're somewhere on the track between the start and finish line + float speed = 0.f; - float curvature = 0.0f; - float track_curve = 0.0f; - float car_curve = 0.0f; - float track_dist = 0.0f; + float curvature = 0.f; // probably the player's horizontal position in relation to the track's curve + float track_curve = 0.f; + float car_curve = 0.f; // wtf.... + float track_dist = 0.f; // total distance of the circuit - float cur_lap_time = 0.0f; + float cur_lap_time = 0.f; std::vector> vecTrack; std::list 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; + // custom controls template + olc::Key Move_Up = olc::UP; + olc::Key Move_Down = olc::DOWN; + olc::Key Move_Left = olc::LEFT; + olc::Key Move_Right = olc::RIGHT; std::array keyslist = { "UP", "DOWN", "LEFT", "RIGHT" }; int configKeyIndex = -1; @@ -93,17 +91,9 @@ public: 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 + MAIN_MENU, PLAY_GAME, RUN_GAME, CONTROLS, PAUSED, RESUME, // main + TRACK_SELECT, CUP_SELECT, CAR_SELECT, // game + INDIE, STREET, PRO, GRAND_PRIX // cups }; state gameState = state::MAIN_MENU; @@ -115,23 +105,25 @@ public: // lap times // player - int score; // score at end of track; based on final place - int lap; // current lap player is on + int score = 0; // score at end of track; based on final place + int lap = 0; // current lap player is on int place; // current place player is in + int tracks_left; // initialize sprites olc::Decal* car; + olc::Decal* cars; olc::Decal* hill; olc::Decal* grass; olc::Decal* road; olc::Decal* banner; olc::Decal* title; olc::Decal* cups; - olc::Decal* select; + //olc::Decal* select; std::string KeyToString(olc::Key key) { - const std::array keyStrings = + const std::array 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", @@ -141,50 +133,104 @@ public: 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 > test_track = + // add laps to a new track struct + + struct 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 > curves; // curves + std::string name; // names + int laps; // laps }; - std::vector > circuit_track = + // special track ;) + Track javidx9 = { - {0.f, 10.f}, - {0.f, 270.f}, - {-1.f, 330.f}, - {0.f, 550.f}, - {-1.f, 330.f}, - {0.f, 270.f} + // 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 construction measure + {{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}}, "JAVIDX9", 3 }; - std::vector > infinity_track = + // 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 + //================================================================================================== + Track circuit = { - {0.f, 10.f}, - {0.f, 270.f}, - {1.f, 330.f}, - {0.f, 550.f}, - {1.f, 330.f}, - {0.f, 270.f} + // Classic Oval; 1 mile loop, 1,760 yards + { {0.f, 10.f}, {0.f, 270.f}, {-1.f, 330.f}, {0.f, 550.f}, {-1.f, 330.f}, {0.f, 270.f} }, "CIRCUIT", 3 }; - // list of ALL tracks to pick from - std::vector >> trackList = { - test_track, - circuit_track, - infinity_track, + Track infinity = + { + // figure eight + {{0.f, 10.f}, // flag (straight); this is positioned somewhere after the intersection + {0.f, 190.f }, {-1.f, 200.f}, + {0.f, 500.f }, // straight; this crosses the first straight, to provide for chances of crashing + {1.f, 200.f }, {0.f, 300.f }}, "INFINITY", 3 }; + Track arrow_head = + { + // fancy triangle + {{0.f, 10.f}, {0.f, 490.f}, {1.f, 130.f}, {0.f, 1000.f}, {1.f, 130.f}, + {0.f, 500.f}, {-1.f, 130.f}, {0.f, 500.f}, {1.f, 130.f}, {0.f, 500.f}}, "ARROW HEAD", 3 + }; + + Track track_4 = // ??? + { + {{0.f, 10.f},}, "Track 4", 3 + }; + + Track overpass = + { + // kind of a folded figure eight + {{0.f, 10.f}, + {0.f, 0.f}, + {1.f, 0.f}, + {0.f, 0.f}, + {1.f, 0.f}, + {1.f, 0.f}, + {0.f, 0.f}, + {1.f, 0.f}, + {0.f, 0.f}}, "OVERPASS", 3 + }; + + Track peanut= + { + // No more rhymes now. I mean it! + // Anybody want a peanut? + // GAH!!! + {{0.f, 10.f},}, "PEANUT", 4 + }; + + Track track_7 = // ??? + { + {{0.f, 10.f},}, "Track 7", 3 + }; + + Track trident = + { + // is your preferred Neptune or Poseidon? + {{0.f, 10.f},}, "TRIDENT", 3 + }; + + Track star = + { + // spangled banner; oh say can you see.... + // look, I suck at singing, and not gonna look up the lyrics, just, enjoy American awesomeness + {{0.f, 10.f},}, "STAR", 3 + }; + + std::vector trackList = + { + javidx9, + circuit, infinity, arrow_head, + track_4, overpass, peanut, + track_7, trident, star + }; + + //======================================================================================================================================== // Create Game Objects //======================================================================================================================================== @@ -206,56 +252,40 @@ public: 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} } + { "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 } }, + { "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 } }, + { "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} } + { "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} } + { "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" }; @@ -263,134 +293,17 @@ public: //======================================================================================================================================== // load sprites car = new olc::Decal(new olc::Sprite("art/car.png")); // will contain 8 cars, with 3 angles per car + cars = new olc::Decal(new olc::Sprite("art/cars.png")); // temp file; holds all 8 cars 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 + //select = new olc::Decal(new olc::Sprite("art/selection.png")); - } - - // 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 }; + // track stuff + list_times = { 0,0,0,0,0 }; // this needs modification, as most tracks will likely have 4 laps, some with 3, and some with 5 gameObjects.push_back({ /*{-1.5,track_dist - 50}, {10,6}, {0,240}*/ }); return true; @@ -409,9 +322,10 @@ public: 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 + track_dist = 0.0f; cur_lap_time = 0.0f; + lap = 0; // Note: the following all need to be reset during the process, when they've been implemented // -player position (on screen) @@ -425,6 +339,16 @@ public: // } + void DrawTrack() + { + vecTrack.clear(); + for (int i = 0; i < trackList[track].curves.size(); i++) + { + vecTrack.push_back(trackList[track].curves[i]); + track_dist += vecTrack[i].second; + } + } + //======================================================================================================================================== // main menu //======================================================================================================================================== @@ -469,7 +393,7 @@ public: // it's below us int dist = abs(list[highlighted].pos.y - list[i].pos.y); - if (distsprite->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; + track = -1; + } + } + if (GetKey(olc::Key::CTRL).bHeld && GetKey(olc::Key::F9).bHeld) + { + // cheat code for Javidx9's original track + 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 + std::array gameStates = { state::INDIE, state::STREET, state::PRO, state::GRAND_PRIX }; + if (highlighted >= 0 && highlighted <= 3) + { + gameState = gameStates[highlighted]; + cup = highlighted + 1; + gameState = state::CAR_SELECT; + tracks_left = 3; + track = 1 + highlighted * 3; + if (highlighted == 3) + { + tracks_left = 9; + } + highlighted = 0; + } + //if (highlighted == 0) + //{ + // gameState = state::INDIE; + // cup = 1; + // gameState = state::CAR_SELECT; + // highlighted = 0; + //} + //else if (highlighted == 1) + //{ + // gameState = state::STREET; + // cup = 2; + // gameState = state::CAR_SELECT; + // highlighted = 0; + //} + //else if (highlighted == 2) + //{ + // gameState = state::PRO; + // cup = 3; + // gameState = state::CAR_SELECT; + // highlighted = 0; + //} + //else if (highlighted == 3) + //{ + // gameState = state::GRAND_PRIX; + // cup = 4; + // gameState = state::CAR_SELECT; + // highlighted = 0; + //} + else + { + gameState = state::PLAY_GAME; // return to menu + cup = -1; + highlighted = 0; + } + } + if (GetKey(olc::Key::CTRL).bHeld && GetKey(olc::Key::F9).bHeld) + { + // cheat code for Javidx9 Grand Prix Cup + // add's Javid's track to the end of the standard Grand Prix + cup = 0; + gameState = state::CAR_SELECT; + tracks_left = 10; + } + if (GetKey(olc::Key::ESCAPE).bPressed || GetKey(olc::Key::BACK).bPressed) + { + gameState = state::PLAY_GAME; + cup = -1; + highlighted = 0; + } + } + +//======================================================================================================================================== + else if (gameState == state::CAR_SELECT) + { + FillRectDecal({ 0, 0 }, { ScreenWidth() + 0.f, ScreenHeight() + 0.f }, olc::Pixel(0, 0, 0, 16)); + DrawPartialDecal({ ScreenWidth() / 5 - 96.f, ScreenHeight() / 3 - 192.f }, cars, { 0, 0 }, { 256, 192}, { .75f, .75f }, (highlighted == 0) ? olc::WHITE : olc::DARK_GREY); // red + DrawPartialDecal({ ScreenWidth() / 5 * 2 - 96.f, ScreenHeight() / 3 - 192.f }, cars, { 256, 0 }, { 256, 192 }, { .75f, .75f }, (highlighted == 1) ? olc::WHITE : olc::DARK_GREY); // blue + DrawPartialDecal({ ScreenWidth() / 5 * 3 - 96.f, ScreenHeight() / 3 - 192.f }, cars, { 512, 0 }, { 256, 192 }, { .75f, .75f }, (highlighted == 2) ? olc::WHITE : olc::DARK_GREY); // green + DrawPartialDecal({ ScreenWidth() / 5 * 4 - 96.f, ScreenHeight() / 3 - 192.f }, cars, { 768, 0 }, { 256, 192 }, { .75f, .75f }, (highlighted == 3) ? olc::WHITE : olc::DARK_GREY); // gold + DrawPartialDecal({ ScreenWidth() / 5 - 96.f, ScreenHeight() / 3 * 2 - 192.f }, cars, { 0, 192 }, { 256, 192 }, { .75f, .75f }, (highlighted == 4) ? olc::WHITE : olc::DARK_GREY); // purple + DrawPartialDecal({ ScreenWidth() / 5 * 2 - 96.f, ScreenHeight() / 3 * 2 - 192.f }, cars, { 256, 192 }, { 256, 192 }, { .75f, .75f }, (highlighted == 5) ? olc::WHITE : olc::DARK_GREY); // white + DrawPartialDecal({ ScreenWidth() / 5 * 3 - 96.f, ScreenHeight() / 3 * 2 - 192.f }, cars, { 512, 192 }, { 256, 192 }, { .75f, .75f }, (highlighted == 6) ? olc::WHITE : olc::DARK_GREY); // orange + DrawPartialDecal({ ScreenWidth() / 5 * 4 - 96.f, ScreenHeight() / 3 * 2 - 192.f }, cars, { 768, 192 }, { 256, 192 }, { .75f, .75f }, (highlighted == 7) ? olc::WHITE : olc::DARK_GREY); // black + + DisplayMenu(selectCar); + if (GetKey(olc::Key::ENTER).bPressed || GetKey(olc::Key::SPACE).bPressed) + { + if (highlighted >= 0 && highlighted <= 7) + { + player = highlighted; + gameState = state::RUN_GAME; + + DrawTrack(); + //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; + } + } - DrawCenteredStringDecal({(float)ScreenWidth()/2,(float)ScreenHeight()/2},"Goblin",olc::GREEN,{2.5,2.5}); - //DrawStringDecal({0,0},"TEST",WHITE,{1,1}); +//======================================================================================================================================== + 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 || GetKey(olc::Key::SPACE).bPressed) + { + if (highlighted >= 0 && 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 || GetKey(olc::Key::SPACE).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(); + } + + // jump to end of lap + if (GetKey(olc::Key::F12).bPressed) + { + distance = track_dist - 50.f; + } + + // 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; + lap++; + } + + if (cup >= 0 && lap >= 1) + { + track++; + Reset(); + DrawTrack(); + } + + if (track != -1) + { + //olc::vi2d textSize = GetTextSize() / 2 * 4; + olc::vi2d textSize0 = GetTextSize("JAVIDX9") / 2 * 4; + olc::vi2d textSize1 = GetTextSize("CIRCUIT") / 2 * 4; + + switch (track) + { + case 0: + DrawStringDecal({ ScreenWidth() / 2 - 0.f, ScreenHeight() / 8 + 0.f }, "JAVIDX9", olc::WHITE, { 3.f, 3.f }); + break; + case 1: + DrawStringDecal({ ScreenWidth() / 2 - 0.f, ScreenHeight() / 8 + 0.f }, "CIRCUIT", olc::WHITE, { 3.f, 3.f }); + break; + case 2: + DrawStringDecal({ ScreenWidth() / 2 - 0.f, ScreenHeight() / 8 + 0.f }, "INFINITY", olc::WHITE, { 3.f, 3.f }); + break; + case 3: + DrawStringDecal({ ScreenWidth() / 2 - 0.f, ScreenHeight() / 8 + 0.f }, "ARROWHEAD", olc::WHITE, { 3.f, 3.f }); + break; + case 4: + DrawStringDecal({ ScreenWidth() / 2 - 0.f, ScreenHeight() / 8 + 0.f }, "TRACK 4", olc::WHITE, { 3.f, 3.f }); + break; + case 5: + DrawStringDecal({ ScreenWidth() / 2 - 0.f, ScreenHeight() / 8 + 0.f }, "OVERPASS", olc::WHITE, { 3.f, 3.f }); + break; + case 6: + DrawStringDecal({ ScreenWidth() / 2 - 0.f, ScreenHeight() / 8 + 0.f }, "PEANUT", olc::WHITE, { 3.f, 3.f }); + break; + case 7: + DrawStringDecal({ ScreenWidth() / 2 - 0.f, ScreenHeight() / 8 + 0.f }, "TRACK 7", olc::WHITE, { 3.f, 3.f }); + break; + case 8: + DrawStringDecal({ ScreenWidth() / 2 - 0.f, ScreenHeight() / 8 + 0.f }, "TRIDENT", olc::WHITE, { 3.f, 3.f }); + break; + case 9: + DrawStringDecal({ ScreenWidth() / 2 - 0.f, ScreenHeight() / 8 + 0.f }, "STAR", olc::WHITE, { 3.f, 3.f }); + break; + } + } + + // 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 pos; + std::vector 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; } @@ -717,8 +1223,15 @@ public: int main() { + std::vector test= {"Apple","Apple","Pear","Something","Tire","Orange","Orange","Orange"}; + std::vector::iterator it = std::unique(test.begin(),test.end()); + test.erase(it,test.end()); + for (int i=0;i