Advertisement
alien_fx_fiend

2D-Racing: Vast Improvements Visually + AI Circuit Loop Logic Rewrite Gemini2.5Pro

Apr 19th, 2025
320
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 24.21 KB | Source Code | 0 0
  1. ==++ Here's the full source code for (file 1/1) "RaceCar.cpp"::: ++==
  2. ```RaceCar.cpp
  3. #include <Windows.h>
  4. #include <ctime>
  5. #include <cstdlib>
  6. #include <math.h>
  7. #include <stdio.h>
  8. #include <string>
  9. #include "resource.h"  // Add this with your other includes
  10.  
  11. // --- Constants --- (Consider adding const double M_PI if not defined in cmath)
  12. #ifndef M_PI
  13. #define M_PI 3.14159265358979323846
  14. #endif
  15. // Global Variables
  16. const int WIDTH = 1366;
  17. const int HEIGHT = 768;
  18. const int ROAD_WIDTH = 200;
  19. const int CAR_WIDTH = 50;
  20. const int CAR_HEIGHT = 100;
  21. const int TYRE_SIZE = 10;
  22. const int FPS = 60;
  23. const int TIMER = 4;
  24. const int TURN_RADIUS = 5;
  25. //const double PI = 3.14159265358979323846;
  26. //const double M_PI = 3.14159265358979323846;
  27.  
  28. int playerX = 100;
  29. int playerY = HEIGHT - CAR_HEIGHT - 50;
  30. //int playerSpeedX = 0;
  31. //int playerSpeedY = 0;
  32. int aiX = playerX + CAR_WIDTH + 20;
  33. int aiY = playerY;
  34. float aiAngle = -M_PI / 2;  // Add this line
  35. //int aiSpeedX = 0;
  36. //int aiSpeedY = 0;
  37. int speed = 5;
  38. int aiSpeed = 5;
  39. int timer = TIMER;
  40. int playerTyre1X = playerX + 10;
  41. int playerTyre1Y = playerY + CAR_HEIGHT - TYRE_SIZE;
  42. int playerTyre2X = playerX + CAR_WIDTH - TYRE_SIZE - 10;
  43. int playerTyre2Y = playerY + CAR_HEIGHT - TYRE_SIZE;
  44. int aiTyre1X = aiX + 10;
  45. int aiTyre1Y = aiY + CAR_HEIGHT - TYRE_SIZE;
  46. int aiTyre2X = aiX + CAR_WIDTH - TYRE_SIZE - 10;
  47. int aiTyre2Y = aiY + CAR_HEIGHT - TYRE_SIZE;
  48. float playerAngle = -M_PI / 2;  // Initialize to face North by default
  49. //float playerAngle = 0.0f;
  50.  
  51. bool gameStarted = false;
  52. bool gameOver = false;
  53. bool playerWon = false;
  54. bool godMode = true;
  55. //int timer = 30 * 10; // 30 seconds * 10 (timer resolution)
  56.  
  57. // --- Forward Declarations (If needed) ---
  58. LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
  59.  
  60. int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
  61. {
  62.    WNDCLASSEX wc = { 0 };
  63.    wc.cbSize = sizeof(WNDCLASSEX);
  64.    wc.style = CS_HREDRAW | CS_VREDRAW;
  65.    wc.lpfnWndProc = WndProc;
  66.    wc.hInstance = hInstance;
  67.    wc.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_ICON1));
  68.    wc.hCursor = LoadCursor(NULL, IDC_ARROW);
  69.    wc.hbrBackground = NULL; // Set to NULL for custom background drawing
  70.    wc.lpszMenuName = NULL;
  71.    wc.lpszClassName = L"RacingGame";
  72.    wc.hIconSm = (HICON)LoadImage(hInstance, MAKEINTRESOURCE(IDI_ICON1), IMAGE_ICON, 16, 16, LR_DEFAULTCOLOR);
  73.    RegisterClassEx(&wc);
  74.  
  75.    int screenWidth = GetSystemMetrics(SM_CXSCREEN);
  76.    int screenHeight = GetSystemMetrics(SM_CYSCREEN);
  77.    int windowX = (screenWidth - WIDTH) / 2;
  78.    int windowY = (screenHeight - HEIGHT) / 2;
  79.  
  80.    HWND hWnd = CreateWindowEx(0, L"RacingGame", L"Racing Game (ArrowKeys=Move G=GodMode)",
  81.        WS_OVERLAPPEDWINDOW | WS_VISIBLE, // Added WS_VISIBLE
  82.        windowX, windowY, WIDTH, HEIGHT,
  83.        NULL, NULL, hInstance, NULL);
  84.  
  85.    // Removed ShowWindow, WS_VISIBLE in CreateWindowEx handles it
  86.  
  87.    MSG msg = { 0 };
  88.    while (GetMessage(&msg, NULL, 0, 0))
  89.    {
  90.        TranslateMessage(&msg);
  91.        DispatchMessage(&msg);
  92.    }
  93.    return (int)msg.wParam; // Return final message code
  94. }
  95.  
  96. // Window Procedure
  97. LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
  98. {
  99.    switch (message)
  100.    {
  101.    case WM_CREATE:
  102.        SetTimer(hWnd, 1, 1000 / FPS, NULL); // Timer fires based on FPS
  103.        // Initial car positions before game starts (if needed)
  104.        playerX = WIDTH / 4; // Example start
  105.        playerY = HEIGHT - CAR_HEIGHT - 50;
  106.        playerAngle = 0; // Pointing up
  107.        aiX = WIDTH * 3 / 4; // Example start
  108.        aiY = HEIGHT - CAR_HEIGHT - 50;
  109.        aiAngle = 0; // Pointing up
  110.        break;
  111.    case WM_TIMER:
  112.        if (timer > 0 && !gameStarted)
  113.        {
  114.            timer--;
  115.            InvalidateRect(hWnd, NULL, FALSE); // Request redraw for countdown
  116.        }
  117.        else if (timer <= 0 && !gameStarted)
  118.        {
  119.            gameStarted = true;
  120.            srand((unsigned int)time(0));
  121.            aiSpeed = rand() % 3 + 4; // Adjust AI speed range if needed (4-6)
  122.  
  123.            // Set initial positions for the race start
  124.            playerX = ROAD_WIDTH / 2 - CAR_WIDTH / 2; // Start on left lane
  125.            playerY = HEIGHT - CAR_HEIGHT - 20;       // Near bottom
  126.            playerAngle = 0;                          // Facing up
  127.  
  128.            aiX = ROAD_WIDTH / 2 - CAR_WIDTH / 2;    // Start on left lane
  129.            aiY = HEIGHT - CAR_HEIGHT - 150;         // Ahead of player
  130.            aiAngle = 0;                             // Facing up
  131.  
  132.            InvalidateRect(hWnd, NULL, FALSE); // Redraw for game start
  133.        }
  134.        else if (gameStarted) // Game logic runs when gameStarted is true
  135.        {
  136.            static bool gKeyPressed = false;
  137.            if (GetAsyncKeyState('G') & 0x8000) // Use 0x8000 for currently pressed state
  138.            {
  139.                // Basic toggle needs a flag to prevent rapid switching                
  140.                if (!gKeyPressed) {
  141.                    godMode = !godMode;
  142.                    gKeyPressed = true; // Mark as pressed
  143.                }
  144.            }
  145.            else {
  146.                gKeyPressed = false; // Reset flag when key is released
  147.            }
  148.  
  149.  
  150.            // --- Player Movement & Rotation ---
  151.            float prevPlayerX = (float)playerX; // Store previous state
  152.            float prevPlayerY = (float)playerY;
  153.            float prevPlayerAngle = playerAngle;
  154.            float playerRadAngle = playerAngle * (float)M_PI / 180.0f; // Convert degrees to radians if using degrees
  155.  
  156.            float angularVelocity = 3.0f; // Degrees per frame for turning
  157.            float currentSpeed = 0;
  158.  
  159.            if (GetAsyncKeyState(VK_UP) & 0x8000) {
  160.                currentSpeed = (float)speed;
  161.            }
  162.            if (GetAsyncKeyState(VK_DOWN) & 0x8000) {
  163.                currentSpeed = -(float)speed / 2; // Slower reverse
  164.            }
  165.  
  166.            if (currentSpeed != 0) { // Only allow turning when moving
  167.                if (GetAsyncKeyState(VK_LEFT) & 0x8000) {
  168.                    playerAngle -= angularVelocity;
  169.                }
  170.                if (GetAsyncKeyState(VK_RIGHT) & 0x8000) {
  171.                    playerAngle += angularVelocity;
  172.                }
  173.            }
  174.  
  175.            // Normalize angle (optional but good practice)
  176.            if (playerAngle >= 360.0f) playerAngle -= 360.0f;
  177.            if (playerAngle < 0.0f) playerAngle += 360.0f;
  178.  
  179.            // Update position based on angle (using radians)
  180.            playerRadAngle = playerAngle * (float)M_PI / 180.0f;
  181.            playerX += (int)(sin(playerRadAngle) * currentSpeed);
  182.            playerY -= (int)(cos(playerRadAngle) * currentSpeed); // Y decreases upwards
  183.  
  184.            // --- AI Movement Logic ---
  185.            // Simple AI: Follows road path, adjusts angle at corners
  186.            // (This is a basic example, can be made more complex)
  187.  
  188.            // Define path points or regions
  189.            // --- AI Movement Logic ---
  190.            int corner1Y = ROAD_WIDTH * 2;
  191.            int corner2X = WIDTH - ROAD_WIDTH;
  192.  
  193.            if (aiY > corner1Y && aiX < corner2X) {
  194.                aiAngle = 0;
  195.                aiY -= aiSpeed;
  196.            }
  197.            else if (aiY <= corner1Y && aiX < corner2X) {
  198.                aiAngle = 90;
  199.                aiX += aiSpeed;
  200.                if (abs(aiY - ROAD_WIDTH) > aiSpeed)
  201.                    aiY += (ROAD_WIDTH - aiY > 0) ? 1 : -1;
  202.            }
  203.            else if (aiX >= corner2X && aiY < HEIGHT - CAR_HEIGHT) {
  204.                aiAngle = 180;
  205.                aiY += aiSpeed;
  206.            }
  207.            else if (aiY >= HEIGHT - CAR_HEIGHT && aiX >= corner2X) {
  208.                aiX = ROAD_WIDTH / 2 - CAR_WIDTH / 2;
  209.                aiY = HEIGHT - CAR_HEIGHT - 20;
  210.                aiAngle = 0;
  211.            }
  212.            // Basic wrap around or finish line logic could go here
  213.  
  214.            // --- Collision Detection & Handling (Simplified) ---
  215.            // Basic AABB check (doesn't account for rotation)
  216.             RECT playerRect = { playerX, playerY, playerX + CAR_WIDTH, playerY + CAR_HEIGHT };
  217.             RECT aiRect = { aiX, aiY, aiX + CAR_WIDTH, aiY + CAR_HEIGHT };
  218.             RECT intersection;
  219.  
  220.             if (!godMode && IntersectRect(&intersection, &playerRect, &aiRect))
  221.             {
  222.                 // Collision occurred - crude response: move player back slightly
  223.                 playerX = (int)prevPlayerX;
  224.                 playerY = (int)prevPlayerY;
  225.                 playerAngle = prevPlayerAngle;
  226.                 // Could add bounce effect or game over here
  227.             }
  228.  
  229.             // --- Boundary Checks (Simple) ---
  230.             // Prevent player from going completely off-screen (adjust as needed)
  231.             if (playerX < -CAR_WIDTH) playerX = -CAR_WIDTH;
  232.             if (playerX > WIDTH) playerX = WIDTH;
  233.             if (playerY < -CAR_HEIGHT) playerY = -CAR_HEIGHT;
  234.             if (playerY > HEIGHT) playerY = HEIGHT;
  235.             // More sophisticated road boundary checks needed for proper gameplay
  236.  
  237.            // Request redraw
  238.             InvalidateRect(hWnd, NULL, FALSE);
  239.         }
  240.         break; // End WM_TIMER
  241.         case WM_PAINT:
  242.         {
  243.             PAINTSTRUCT ps;
  244.             HDC hdc = BeginPaint(hWnd, &ps);
  245.  
  246.             // --- Double Buffering Setup ---
  247.             RECT clientRect;
  248.             GetClientRect(hWnd, &clientRect);
  249.             HDC memDC = CreateCompatibleDC(hdc);
  250.             HBITMAP memBitmap = CreateCompatibleBitmap(hdc, clientRect.right, clientRect.bottom);
  251.             HBITMAP oldBitmap = (HBITMAP)SelectObject(memDC, memBitmap);
  252.  
  253.             // --- Drawing Starts Here (Draw onto memDC) ---
  254.  
  255.             // 1. Draw Background (Light Green)
  256.             HBRUSH lightGreenBrush = CreateSolidBrush(RGB(144, 238, 144));
  257.             FillRect(memDC, &clientRect, lightGreenBrush);
  258.             DeleteObject(lightGreenBrush);
  259.  
  260.             // 2. Draw Roads (Black)
  261.             HBRUSH blackBrush = CreateSolidBrush(RGB(0, 0, 0));
  262.             RECT verticalRoad = { 0, 0, ROAD_WIDTH, HEIGHT };
  263.             FillRect(memDC, &verticalRoad, blackBrush);
  264.             RECT horizontalRoad = { 0, 0, WIDTH, ROAD_WIDTH * 2 };
  265.             FillRect(memDC, &horizontalRoad, blackBrush);
  266.             // Add second vertical road on the right if needed for a circuit
  267.             RECT verticalRoad2 = { WIDTH - ROAD_WIDTH, 0, WIDTH, HEIGHT };
  268.             FillRect(memDC, &verticalRoad2, blackBrush);
  269.             DeleteObject(blackBrush);
  270.  
  271.             // 3. Draw Road Markings (Yellow Dashed Lines)
  272.             HBRUSH yellowBrush = CreateSolidBrush(RGB(255, 255, 0));
  273.             HGDIOBJ oldYellowBrush = SelectObject(memDC, yellowBrush);
  274.             // Vertical dashes (Left Road)
  275.             for (int y = 0; y < HEIGHT; y += 80) {
  276.                 Rectangle(memDC, ROAD_WIDTH / 2 - 5, y, ROAD_WIDTH / 2 + 5, y + 40);
  277.             }
  278.             // Vertical dashes (Right Road - if exists)
  279.             for (int y = 0; y < HEIGHT; y += 80) {
  280.                 Rectangle(memDC, WIDTH - ROAD_WIDTH / 2 - 5, y, WIDTH - ROAD_WIDTH / 2 + 5, y + 40);
  281.             }
  282.             // Horizontal dashes
  283.             for (int x = 0; x < WIDTH; x += 80) {
  284.                 Rectangle(memDC, x, ROAD_WIDTH - 5, x + 40, ROAD_WIDTH + 5);
  285.             }
  286.             SelectObject(memDC, oldYellowBrush);
  287.             DeleteObject(yellowBrush);
  288.  
  289.             // --- Draw Player Car (Red) ---
  290.             // Uses playerAngle in degrees, convert to radians for transformation
  291.             float playerRadAngleDraw = playerAngle * (float)M_PI / 180.0f;
  292.             HBRUSH redBrush = CreateSolidBrush(RGB(255, 0, 0));
  293.             HGDIOBJ oldRedBrush = SelectObject(memDC, redBrush);
  294.             int savedDCPlayer = SaveDC(memDC);
  295.             SetGraphicsMode(memDC, GM_ADVANCED);
  296.             XFORM xformPlayer;
  297.             xformPlayer.eM11 = (FLOAT)cos(playerRadAngleDraw);
  298.             xformPlayer.eM12 = (FLOAT)sin(playerRadAngleDraw);
  299.             xformPlayer.eM21 = (FLOAT)-sin(playerRadAngleDraw); // Negative sin for standard rotation
  300.             xformPlayer.eM22 = (FLOAT)cos(playerRadAngleDraw);
  301.             xformPlayer.eDx = (FLOAT)playerX + CAR_WIDTH / 2;
  302.             xformPlayer.eDy = (FLOAT)playerY + CAR_HEIGHT / 2;
  303.             SetWorldTransform(memDC, &xformPlayer);
  304.  
  305.             // Draw Player Body (relative coords)
  306.             Rectangle(memDC, -CAR_WIDTH / 2, -CAR_HEIGHT / 2, CAR_WIDTH / 2, CAR_HEIGHT / 2);
  307.  
  308.             // HEADLIGHTS FIRST
  309.             SelectObject(memDC, CreateSolidBrush(RGB(255, 255, 0)));
  310.             Rectangle(memDC, -CAR_WIDTH / 2 + 2, -CAR_HEIGHT / 2 + 2, -CAR_WIDTH / 4, -CAR_HEIGHT / 2 + 6);
  311.             Rectangle(memDC, CAR_WIDTH / 4, -CAR_HEIGHT / 2 + 2, CAR_WIDTH / 2 - 2, -CAR_HEIGHT / 2 + 6);
  312.  
  313.             // Draw Player Headlights (relative coords)
  314.             HBRUSH pHeadlightBrush = CreateSolidBrush(RGB(255, 255, 220)); // Light Yellow
  315.             HGDIOBJ oldPHeadlightBrush = SelectObject(memDC, pHeadlightBrush);
  316.             int pHeadlightSize = 8;
  317.             int pHeadlightXOffset = CAR_WIDTH / 4;
  318.             int pHeadlightYOffset = -CAR_HEIGHT / 2 + 10 - 8;
  319.             Ellipse(memDC, -pHeadlightXOffset - pHeadlightSize / 2, pHeadlightYOffset, -pHeadlightXOffset + pHeadlightSize / 2, pHeadlightYOffset + pHeadlightSize);
  320.             Ellipse(memDC, pHeadlightXOffset - pHeadlightSize / 2, pHeadlightYOffset, pHeadlightXOffset + pHeadlightSize / 2, pHeadlightYOffset + pHeadlightSize);
  321.             SelectObject(memDC, oldPHeadlightBrush);
  322.             DeleteObject(pHeadlightBrush);
  323.  
  324.             // Draw Player Windows (relative coords)
  325.             HBRUSH pWinBrush = CreateSolidBrush(RGB(60, 60, 60)); // Dark Gray
  326.             HGDIOBJ oldPWinBrush = SelectObject(memDC, pWinBrush);
  327.             int pWsWidth = CAR_WIDTH * 3 / 4;
  328.             int pWsHeight = CAR_HEIGHT / 5;
  329.             int pWsY = -CAR_HEIGHT / 2 + 25 + 10;
  330.             Rectangle(memDC, -pWsWidth / 2, pWsY, pWsWidth / 2, pWsY + pWsHeight); // Windscreen
  331.             int pSideWWidth = CAR_WIDTH / 8;
  332.             int pSideWHeight = CAR_HEIGHT / 4;
  333.             int pSideWY = pWsY + pWsHeight / 2 - pSideWHeight / 2 + 10;
  334.             int pSideWXOffset = CAR_WIDTH / 2 - 5 - pSideWWidth / 2;
  335.             Rectangle(memDC, -pSideWXOffset - pSideWWidth / 2, pSideWY, -pSideWXOffset + pSideWWidth / 2, pSideWY + pSideWHeight); // Left Side
  336.             Rectangle(memDC, pSideWXOffset - pSideWWidth / 2, pSideWY, pSideWXOffset + pSideWWidth / 2, pSideWY + pSideWHeight); // Right Side
  337.             SelectObject(memDC, oldPWinBrush);
  338.             DeleteObject(pWinBrush);
  339.  
  340.             // Draw Player Tyres (relative coords)
  341.             HBRUSH pTyreBrush = CreateSolidBrush(RGB(0, 0, 0));
  342.             HGDIOBJ oldPTyreBrush = SelectObject(memDC, pTyreBrush);
  343.             int pTyreWidth = 10;
  344.             int pTyreHeight = 15;
  345.  
  346.             // Keeping original vertical positions:
  347.             int pFrontTyreY = -CAR_HEIGHT / 2 + 15; // Original front Y position
  348.             int pRearTyreY = CAR_HEIGHT / 2 - pTyreHeight - 5; // Original rear Y position
  349.  
  350.             // Adjust horizontal positions to stick to the edges of the car
  351.             int pLeftTyreX = -CAR_WIDTH / 2; // Left edge of the car
  352.             int pRightTyreX = CAR_WIDTH / 2;  // Right edge of the car
  353.  
  354.             // Draw tyres at the left and right positions
  355.             // Front Left Tyre
  356.             Rectangle(memDC, pLeftTyreX - pTyreWidth / 2, pFrontTyreY, pLeftTyreX + pTyreWidth / 2, pFrontTyreY + pTyreHeight); // FL
  357.             // Front Right Tyre
  358.             Rectangle(memDC, pRightTyreX - pTyreWidth / 2, pFrontTyreY, pRightTyreX + pTyreWidth / 2, pFrontTyreY + pTyreHeight); // FR
  359.             // Rear Left Tyre
  360.             Rectangle(memDC, pLeftTyreX - pTyreWidth / 2, pRearTyreY, pLeftTyreX + pTyreWidth / 2, pRearTyreY + pTyreHeight); // RL
  361.             // Rear Right Tyre
  362.             Rectangle(memDC, pRightTyreX - pTyreWidth / 2, pRearTyreY, pRightTyreX + pTyreWidth / 2, pRearTyreY + pTyreHeight); // RR
  363.  
  364.             SelectObject(memDC, oldPTyreBrush);
  365.             DeleteObject(pTyreBrush);
  366.  
  367.             // Restore player DC
  368.             RestoreDC(memDC, savedDCPlayer);
  369.             SelectObject(memDC, oldRedBrush);
  370.             DeleteObject(redBrush);
  371.  
  372.             // --- Draw AI Car (Blue) ---
  373.             // Uses aiAngle in degrees, convert to radians for transformation
  374.             float aiRadAngleDraw = aiAngle * (float)M_PI / 180.0f;
  375.             HBRUSH blueBrush = CreateSolidBrush(RGB(0, 0, 255));
  376.             HGDIOBJ oldBlueBrush = SelectObject(memDC, blueBrush);
  377.             int savedDCAi = SaveDC(memDC);
  378.             SetGraphicsMode(memDC, GM_ADVANCED);
  379.             XFORM xformAi; // Use a different name
  380.             xformAi.eM11 = (FLOAT)cos(aiRadAngleDraw);
  381.             xformAi.eM12 = (FLOAT)sin(aiRadAngleDraw);
  382.             xformAi.eM21 = (FLOAT)-sin(aiRadAngleDraw); // Negative sin for standard rotation
  383.             xformAi.eM22 = (FLOAT)cos(aiRadAngleDraw);
  384.             xformAi.eDx = (FLOAT)aiX + CAR_WIDTH / 2;
  385.             xformAi.eDy = (FLOAT)aiY + CAR_HEIGHT / 2;
  386.             SetWorldTransform(memDC, &xformAi); // Apply the transformation
  387.  
  388.             // Draw AI Body (relative coords)
  389.             Rectangle(memDC, -CAR_WIDTH / 2, -CAR_HEIGHT / 2, CAR_WIDTH / 2, CAR_HEIGHT / 2);
  390.  
  391.             // HEADLIGHTS FIRST
  392.             SelectObject(memDC, CreateSolidBrush(RGB(255, 255, 0)));
  393.             Rectangle(memDC, -CAR_WIDTH / 2 + 2, -CAR_HEIGHT / 2 + 2, -CAR_WIDTH / 4, -CAR_HEIGHT / 2 + 6);
  394.             Rectangle(memDC, CAR_WIDTH / 4, -CAR_HEIGHT / 2 + 2, CAR_WIDTH / 2 - 2, -CAR_HEIGHT / 2 + 6);
  395.  
  396.             // Draw AI Headlights (relative coords)
  397.             HBRUSH aiHeadlightBrush = CreateSolidBrush(RGB(255, 255, 220)); // Light Yellow
  398.             HGDIOBJ oldAiHeadlightBrush = SelectObject(memDC, aiHeadlightBrush);
  399.             int aiHeadlightSize = 8;
  400.             int aiHeadlightXOffset = CAR_WIDTH / 4;
  401.             int aiHeadlightYOffset = -CAR_HEIGHT / 2 + 10 - 8; // Near "top" edge in local coords
  402.             Ellipse(memDC, -aiHeadlightXOffset - aiHeadlightSize / 2, aiHeadlightYOffset, -aiHeadlightXOffset + aiHeadlightSize / 2, aiHeadlightYOffset + aiHeadlightSize);
  403.             Ellipse(memDC, aiHeadlightXOffset - aiHeadlightSize / 2, aiHeadlightYOffset, aiHeadlightXOffset + aiHeadlightSize / 2, aiHeadlightYOffset + aiHeadlightSize);
  404.             SelectObject(memDC, oldAiHeadlightBrush);
  405.             DeleteObject(aiHeadlightBrush);
  406.  
  407.             // Draw AI Windows (relative coords)
  408.             HBRUSH aiWinBrush = CreateSolidBrush(RGB(60, 60, 60)); // Dark Gray
  409.             HGDIOBJ oldAiWinBrush = SelectObject(memDC, aiWinBrush);
  410.             int aiWsWidth = CAR_WIDTH * 3 / 4;
  411.             int aiWsHeight = CAR_HEIGHT / 5;
  412.             int aiWsY = -CAR_HEIGHT / 2 + 25 + 10; // Near "top" edge
  413.             Rectangle(memDC, -aiWsWidth / 2, aiWsY, aiWsWidth / 2, aiWsY + aiWsHeight); // Windscreen
  414.             int aiSideWWidth = CAR_WIDTH / 8;
  415.             int aiSideWHeight = CAR_HEIGHT / 4;
  416.             int aiSideWY = aiWsY + aiWsHeight / 2 - aiSideWHeight / 2 + 10; // Centered vertically
  417.             int aiSideWXOffset = CAR_WIDTH / 2 - 5 - aiSideWWidth / 2; // Offset from center
  418.             Rectangle(memDC, -aiSideWXOffset - aiSideWWidth / 2, aiSideWY, -aiSideWXOffset + aiSideWWidth / 2, aiSideWY + aiSideWHeight); // Left
  419.             Rectangle(memDC, aiSideWXOffset - aiSideWWidth / 2, aiSideWY, aiSideWXOffset + aiSideWWidth / 2, aiSideWY + aiSideWHeight); // Right
  420.             SelectObject(memDC, oldAiWinBrush);
  421.             DeleteObject(aiWinBrush);
  422.  
  423.             // Draw AI Tyres (relative coords)
  424.             HBRUSH aiTyreBrush = CreateSolidBrush(RGB(0, 0, 0));
  425.             HGDIOBJ oldAiTyreBrush = SelectObject(memDC, aiTyreBrush);
  426.             int aiTyreWidth = 10;
  427.             int aiTyreHeight = 15;
  428.  
  429.             // Keeping original vertical positions:
  430.             int aiFrontTyreY = -CAR_HEIGHT / 2 + 15; // Original front Y position
  431.             int aiRearTyreY = CAR_HEIGHT / 2 - aiTyreHeight - 5; // Original rear Y position
  432.  
  433.             // Adjust horizontal positions to stick to the edges of the car
  434.             int aiLeftTyreX = -CAR_WIDTH / 2; // Left edge of the car
  435.             int aiRightTyreX = CAR_WIDTH / 2;  // Right edge of the car
  436.  
  437.             // Draw tyres at the left and right positions
  438.             // Front Left Tyre
  439.             Rectangle(memDC, aiLeftTyreX - aiTyreWidth / 2, aiFrontTyreY, aiLeftTyreX + aiTyreWidth / 2, aiFrontTyreY + aiTyreHeight); // FL
  440.             // Front Right Tyre
  441.             Rectangle(memDC, aiRightTyreX - aiTyreWidth / 2, aiFrontTyreY, aiRightTyreX + aiTyreWidth / 2, aiFrontTyreY + aiTyreHeight); // FR
  442.             // Rear Left Tyre
  443.             Rectangle(memDC, aiLeftTyreX - aiTyreWidth / 2, aiRearTyreY, aiLeftTyreX + aiTyreWidth / 2, aiRearTyreY + aiTyreHeight); // RL
  444.             // Rear Right Tyre
  445.             Rectangle(memDC, aiRightTyreX - aiTyreWidth / 2, aiRearTyreY, aiRightTyreX + aiTyreWidth / 2, aiRearTyreY + aiTyreHeight); // RR
  446.  
  447.             SelectObject(memDC, oldAiTyreBrush);
  448.             DeleteObject(aiTyreBrush);
  449.  
  450.             // Restore AI DC state
  451.             RestoreDC(memDC, savedDCAi);
  452.             SelectObject(memDC, oldBlueBrush);
  453.             DeleteObject(blueBrush);
  454.  
  455.  
  456.             // --- Draw Overlay Text ---
  457.             SetBkMode(memDC, TRANSPARENT); // Make text background transparent
  458.  
  459.             // Timer display (only before game starts)
  460.             if (!gameStarted && timer > 0)
  461.             {
  462.                 char timerText[10];
  463.                 // Display seconds correctly (integer division), ensure >= 1
  464.                 int secondsLeft = max(1, (timer + FPS - 1) / FPS);
  465.                 sprintf_s(timerText, "%d", secondsLeft);
  466.                 SetTextColor(memDC, RGB(255, 255, 0)); // Yellow countdown
  467.                 HFONT hFont = CreateFont(48, 0, 0, 0, FW_BOLD, FALSE, FALSE, FALSE, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | FF_SWISS, L"Arial");
  468.                 HFONT oldFont = (HFONT)SelectObject(memDC, hFont);
  469.                 SetTextAlign(memDC, TA_CENTER | TA_BASELINE); // Center align text
  470.                 TextOutA(memDC, WIDTH / 2, HEIGHT / 2, timerText, strlen(timerText));
  471.                 SetTextAlign(memDC, TA_LEFT | TA_TOP); // Reset alignment
  472.                 SelectObject(memDC, oldFont); // Restore old font
  473.                 DeleteObject(hFont); // Delete created font
  474.             }
  475.             else if (!gameStarted && timer <= 0) {
  476.                 // Optionally display "GO!" briefly
  477.             }
  478.  
  479.  
  480.             // God Mode Status Display
  481.             if (godMode)
  482.             {
  483.                 SetTextColor(memDC, RGB(0, 255, 0)); // Green text for God Mode
  484.                 HFONT hFont = CreateFont(20, 0, 0, 0, FW_BOLD, FALSE, FALSE, FALSE, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | FF_SWISS, L"Arial");
  485.                 HFONT oldFont = (HFONT)SelectObject(memDC, hFont);
  486.                 TextOutA(memDC, 10, 10, "God Mode ON (G)", 15);
  487.                 SelectObject(memDC, oldFont);
  488.                 DeleteObject(hFont);
  489.             }
  490.  
  491.             // --- Double Buffering End ---
  492.             BitBlt(hdc, 0, 0, clientRect.right, clientRect.bottom, memDC, 0, 0, SRCCOPY);
  493.  
  494.             // --- Cleanup ---
  495.             SelectObject(memDC, oldBitmap);
  496.             DeleteObject(memBitmap);
  497.             DeleteDC(memDC);
  498.  
  499.             EndPaint(hWnd, &ps);
  500.         }
  501.         break; // End WM_PAINT
  502.     case WM_DESTROY:
  503.         KillTimer(hWnd, 1);
  504.         PostQuitMessage(0);
  505.         break;
  506.     case WM_KEYDOWN:
  507.         if (wParam == VK_F1)
  508.         {
  509.             MessageBoxW(hWnd, L"2D Racing Game 3.0 Programmed in C++ Win32 API (491 lines of code) by Entisoft Software (c) Evans Thorpemorton", L"About", MB_OK | MB_ICONINFORMATION); // orig 395 lines
  510.         }
  511.         //break;
  512.         if (wParam == VK_ESCAPE)
  513.         {
  514.             PostQuitMessage(0);
  515.         }
  516.         break;
  517.  
  518.         // Add WM_ERASEBKGND to prevent default background flicker
  519.     case WM_ERASEBKGND:
  520.         return 1; // Indicate that we handled background erasing (by not doing it)
  521.  
  522.     default:
  523.         return DefWindowProc(hWnd, message, wParam, lParam);
  524.     }
  525.     return 0;
  526. }
  527. ```
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement