· 5 months ago · Apr 19, 2025, 11:45 AM
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
16const int WIDTH = 1366;
17const int HEIGHT = 768;
18const int ROAD_WIDTH = 200;
19const int CAR_WIDTH = 50;
20const int CAR_HEIGHT = 100;
21const int TYRE_SIZE = 10;
22const int FPS = 60;
23const int TIMER = 4;
24const int TURN_RADIUS = 5;
25//const double PI = 3.14159265358979323846;
26//const double M_PI = 3.14159265358979323846;
27
28int playerX = 100;
29int playerY = HEIGHT - CAR_HEIGHT - 50;
30//int playerSpeedX = 0;
31//int playerSpeedY = 0;
32int aiX = playerX + CAR_WIDTH + 20;
33int aiY = playerY;
34float aiAngle = -M_PI / 2; // Add this line
35//int aiSpeedX = 0;
36//int aiSpeedY = 0;
37int speed = 5;
38int aiSpeed = 5;
39int timer = TIMER;
40int playerTyre1X = playerX + 10;
41int playerTyre1Y = playerY + CAR_HEIGHT - TYRE_SIZE;
42int playerTyre2X = playerX + CAR_WIDTH - TYRE_SIZE - 10;
43int playerTyre2Y = playerY + CAR_HEIGHT - TYRE_SIZE;
44int aiTyre1X = aiX + 10;
45int aiTyre1Y = aiY + CAR_HEIGHT - TYRE_SIZE;
46int aiTyre2X = aiX + CAR_WIDTH - TYRE_SIZE - 10;
47int aiTyre2Y = aiY + CAR_HEIGHT - TYRE_SIZE;
48float playerAngle = -M_PI / 2; // Initialize to face North by default
49//float playerAngle = 0.0f;
50
51bool gameStarted = false;
52bool gameOver = false;
53bool playerWon = false;
54bool godMode = true;
55//int timer = 30 * 10; // 30 seconds * 10 (timer resolution)
56
57// --- Forward Declarations (If needed) ---
58LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
59
60int 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
97LRESULT 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```