Advertisement
alien_fx_fiend

2D-Racing-App, /w Advanced Collision Detection!

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