Advertisement
alien_fx_fiend

Asteroids Game C++ GDI-Based Stable Only Reduce Flicker Initially

Nov 1st, 2024
59
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 15.32 KB | Source Code | 0 0
  1. #include <windows.h>
  2. #include <vector>
  3. #include <cmath>
  4. #include <ctime>
  5. #include <algorithm>  // Include this header for std::min and std::max
  6. #include "resource.h"
  7.  
  8. #define WIN_WIDTH 800
  9. #define WIN_HEIGHT 600
  10. // Add with other global variables
  11. bool keyStates[256] = { false };  // Track key states
  12. bool godMode = false; // Track God Mode state
  13. bool paused = false; // Track paused state
  14.  
  15. LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
  16. void DrawTriangle(HDC hdc, POINT pts[], int x, int y, double angle);
  17. void DrawAsteroid(HDC hdc, POINT pts[], int x, int y, double angle);
  18. bool CheckCollision(POINT player[], POINT asteroid[], int px, int py, int ax, int ay);
  19. void MoveAsteroids();
  20. void ShootBullet();
  21. void DrawBullet(HDC hdc, int x, int y);
  22. bool CheckBulletCollision(int bx, int by);
  23. bool DoLinesIntersect(int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4);
  24. int Direction(int x1, int y1, int x2, int y2, int x3, int y3);
  25. bool OnSegment(int x1, int y1, int x2, int y2, int x3, int y3);
  26. bool PointInPolygon(int px, int py, POINT polygon[], int ox, int oy);
  27. void GenerateAsteroids();
  28. void SpawnNewAsteroid();
  29. void UpdatePlayerMovement();
  30.  
  31. struct Asteroid {
  32.     int x, y;
  33.     double angle;
  34.     POINT shape[8];
  35. };
  36.  
  37. struct Bullet {
  38.     int x, y;
  39.     double angle;
  40. };
  41.  
  42. std::vector<Asteroid> asteroids;
  43. std::vector<Bullet> bullets;
  44. POINT player[3] = { {0, -30}, {20, 20}, {-20, 20} };
  45. int playerX = WIN_WIDTH / 2, playerY = WIN_HEIGHT / 2;
  46. double playerAngle = 0;
  47. int score = 0;
  48. int lives = 3;
  49.  
  50.  
  51. int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
  52.     WNDCLASSEX wcex;
  53.     HWND hwnd;
  54.     MSG msg;
  55.  
  56.     wcex.cbSize = sizeof(WNDCLASSEX);
  57.     wcex.style = CS_HREDRAW | CS_VREDRAW;
  58.     wcex.lpfnWndProc = WndProc;
  59.     wcex.cbClsExtra = 0;
  60.     wcex.cbWndExtra = 0;
  61.     wcex.hInstance = hInstance;
  62.     wcex.hIcon = LoadIcon(NULL, IDI_APPLICATION);
  63.     wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
  64.     wcex.hbrBackground = NULL; // Set background brush to NULL
  65.     wcex.lpszMenuName = NULL;
  66.     wcex.lpszClassName = TEXT("Asteroids");
  67.     wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_ICON1));
  68.     //wcex.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
  69.  
  70.     RegisterClassEx(&wcex);
  71.  
  72.     // Get the screen dimensions
  73.     int screenWidth = GetSystemMetrics(SM_CXSCREEN);
  74.     int screenHeight = GetSystemMetrics(SM_CYSCREEN);
  75.  
  76.     // Calculate the window position to center it
  77.     int windowWidth = WIN_WIDTH + 16;
  78.     int windowHeight = WIN_HEIGHT + 39;
  79.     int posX = (screenWidth - windowWidth) / 2;
  80.     int posY = (screenHeight - windowHeight) / 2;
  81.  
  82.     hwnd = CreateWindow(TEXT("Asteroids"), TEXT("Asteroids ArrowKeys=Controls Space=Shoot F1=GodMode P=Pause F2=About Escape=Quit"), WS_OVERLAPPEDWINDOW,
  83.         posX, posY, windowWidth + 16, windowHeight + 39, NULL, NULL, hInstance, NULL);
  84.  
  85.     ShowWindow(hwnd, nCmdShow);
  86.     UpdateWindow(hwnd);
  87.  
  88.     srand(static_cast<unsigned int>(time(0)));
  89.  
  90.     GenerateAsteroids();
  91.  
  92.         // Main game loop
  93.     while (true) {
  94.         if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
  95.             if (msg.message == WM_QUIT) break;
  96.             TranslateMessage(&msg);
  97.             DispatchMessage(&msg);
  98.         }
  99.         else {
  100.             // Only update game state if not paused
  101.             if (!paused) {
  102.                 UpdatePlayerMovement();
  103.                 for (auto& ast : asteroids) {
  104.                     if (!godMode && CheckCollision(player, ast.shape, playerX, playerY, ast.x, ast.y)) {
  105.                         lives--;
  106.                         if (lives <= 0) {
  107.                             MessageBox(hwnd, TEXT("Game Over!"), TEXT("Asteroids"), MB_OK);
  108.                             PostQuitMessage(0);
  109.                         }
  110.                         playerX = WIN_WIDTH / 2;
  111.                         playerY = WIN_HEIGHT / 2;
  112.                         playerAngle = 0;
  113.                         memset(keyStates, 0, sizeof(keyStates));
  114.                     }
  115.                 }
  116.                 if (rand() % 100 < 2) {
  117.                     SpawnNewAsteroid();
  118.                 }
  119.  
  120.                 MoveAsteroids();
  121.                 for (auto it = bullets.begin(); it != bullets.end();) {
  122.                     it->x += 10 * cos(it->angle * 3.14159 / 180);
  123.                     it->y += 10 * sin(it->angle * 3.14159 / 180);
  124.  
  125.                     if (CheckBulletCollision(it->x, it->y)) {
  126.                         it = bullets.erase(it);
  127.                     }
  128.                     else {
  129.                         ++it;
  130.                     }
  131.                 }
  132.             }
  133.             InvalidateRect(hwnd, NULL, FALSE); // Use FALSE to avoid erasing background
  134.             Sleep(16); // ~60 FPS
  135.         }
  136.     }
  137.  
  138.     return (int)msg.wParam;
  139. }
  140.  
  141.  
  142. LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
  143.     static HDC hdcMem;
  144.     static HBITMAP hbmMem;
  145.     static HBITMAP hbmOld;
  146.     static HBRUSH hbrBkGnd;
  147.     static RECT rect;
  148.     PAINTSTRUCT ps;
  149.     static TCHAR scoreText[50];
  150.  
  151.     switch (msg) {
  152.     case WM_CREATE:
  153.         GetClientRect(hwnd, &rect);
  154.         hdcMem = CreateCompatibleDC(NULL);
  155.         hbmMem = CreateCompatibleBitmap(GetDC(hwnd), rect.right, rect.bottom);
  156.         hbmOld = (HBITMAP)SelectObject(hdcMem, hbmMem);
  157.         hbrBkGnd = CreateSolidBrush(RGB(0, 0, 0));
  158.         break;
  159.  
  160.     case WM_PAINT: {
  161.         HDC hdc = BeginPaint(hwnd, &ps);
  162.  
  163.         FillRect(hdcMem, &rect, hbrBkGnd);
  164.  
  165.         DrawTriangle(hdcMem, player, playerX, playerY, playerAngle);
  166.  
  167.         for (auto& ast : asteroids) {
  168.             DrawAsteroid(hdcMem, ast.shape, ast.x, ast.y, ast.angle);
  169.         }
  170.  
  171.         for (auto& bullet : bullets) {
  172.             DrawBullet(hdcMem, bullet.x, bullet.y);
  173.         }
  174.  
  175.         SetTextColor(hdcMem, RGB(255, 255, 255));
  176.         SetBkMode(hdcMem, TRANSPARENT);
  177.         wsprintf(scoreText, TEXT("Score: %d  Lives: %d"), score, lives);
  178.         TextOut(hdcMem, 10, 10, scoreText, lstrlen(scoreText));
  179.  
  180.         if (godMode) {
  181.             TextOut(hdcMem, WIN_WIDTH / 2 - 50, 10, TEXT("God Mode"), 8);
  182.         }
  183.  
  184.         if (paused) {
  185.             TextOut(hdcMem, WIN_WIDTH / 2 - 50, WIN_HEIGHT / 2 - 10, TEXT("Game Paused"), 10);
  186.         }
  187.  
  188.         BitBlt(hdc, 0, 0, rect.right, rect.bottom, hdcMem, 0, 0, SRCCOPY);
  189.  
  190.         EndPaint(hwnd, &ps);
  191.         break;
  192.     }
  193.  
  194.     case WM_KEYDOWN:
  195.         keyStates[wParam] = true;
  196.         if (wParam == VK_F1) {
  197.             godMode = !godMode; // Toggle God Mode
  198.         }
  199.         else if (wParam == 'P') {
  200.             paused = !paused; // Toggle Pause
  201.         }
  202.         if (wParam == VK_ESCAPE) {
  203.             PostQuitMessage(0);
  204.             return 0;
  205.         }
  206.         // Add this new code block
  207.         else if (wParam == VK_F2) {
  208.             MessageBox(hwnd, TEXT("Asteroids v10.8 Basic GDI-Based C++ Win32 Game (384 lines of code) by Entisoft Software (c) Evans Thorpemorton"), TEXT("About"), MB_OK | MB_ICONINFORMATION);
  209.         }
  210.         break;
  211.  
  212.     case WM_KEYUP:
  213.         keyStates[wParam] = false;
  214.         break;
  215.  
  216.     case WM_DESTROY:
  217.         SelectObject(hdcMem, hbmOld);
  218.         DeleteObject(hbmMem);
  219.         DeleteDC(hdcMem);
  220.         DeleteObject(hbrBkGnd);
  221.         PostQuitMessage(0);
  222.         break;
  223.  
  224.     default:
  225.         return DefWindowProc(hwnd, msg, wParam, lParam);
  226.     }
  227.     return 0;
  228. }
  229.  
  230. void DrawTriangle(HDC hdc, POINT pts[], int x, int y, double angle) {
  231.     POINT rotated[3];
  232.     for (int i = 0; i < 3; ++i) {
  233.         rotated[i].x = pts[i].x * cos(angle * 3.14159 / 180) - pts[i].y * sin(angle * 3.14159 / 180);
  234.         rotated[i].y = pts[i].x * sin(angle * 3.14159 / 180) + pts[i].y * cos(angle * 3.14159 / 180);
  235.         rotated[i].x += x;
  236.         rotated[i].y += y;
  237.     }
  238.     // In DrawTriangle function, before Polygon call
  239.     HPEN hotPinkPen = CreatePen(PS_SOLID, 1, RGB(255, 20, 147));
  240.     HBRUSH hotPinkBrush = CreateSolidBrush(RGB(255, 20, 147));
  241.     SelectObject(hdc, hotPinkPen);
  242.     SelectObject(hdc, hotPinkBrush);
  243.     Polygon(hdc, rotated, 3);
  244.     DeleteObject(hotPinkPen);
  245.     DeleteObject(hotPinkBrush);
  246. }
  247.  
  248. void DrawAsteroid(HDC hdc, POINT pts[], int x, int y, double angle) {
  249.     POINT rotated[8];
  250.     for (int i = 0; i < 8; ++i) {
  251.         rotated[i].x = pts[i].x * cos(angle * 3.14159 / 180) - pts[i].y * sin(angle * 3.14159 / 180);
  252.         rotated[i].y = pts[i].x * sin(angle * 3.14159 / 180) + pts[i].y * cos(angle * 3.14159 / 180);
  253.         rotated[i].x += x;
  254.         rotated[i].y += y;
  255.     }
  256.  
  257.     // In DrawAsteroid function, before Polygon call
  258.     HPEN goldPen = CreatePen(PS_SOLID, 1, RGB(255, 215, 0));
  259.     HBRUSH goldBrush = CreateSolidBrush(RGB(255, 215, 0));
  260.     SelectObject(hdc, goldPen);
  261.     SelectObject(hdc, goldBrush);
  262.     Polygon(hdc, rotated, 8);
  263.     DeleteObject(goldPen);
  264.     DeleteObject(goldBrush);
  265. }
  266.  
  267. void UpdatePlayerMovement() {
  268.     if (keyStates[VK_LEFT]) {
  269.         playerAngle -= 5;
  270.     }
  271.     if (keyStates[VK_RIGHT]) {
  272.         playerAngle += 5;
  273.     }
  274.     if (keyStates[VK_UP]) {
  275.         playerX += 5 * cos((playerAngle - 90) * 3.14159 / 180);
  276.         playerY += 5 * sin((playerAngle - 90) * 3.14159 / 180);
  277.     }
  278.     // Add to UpdatePlayerMovement() function
  279.     if (keyStates[VK_DOWN]) {
  280.         playerX -= 5 * cos((playerAngle - 90) * 3.14159 / 180);
  281.         playerY -= 5 * sin((playerAngle - 90) * 3.14159 / 180);
  282.     }
  283.     // Add to UpdatePlayerMovement() function
  284.     static int shootCooldown = 0;
  285.     if (keyStates[VK_SPACE] && shootCooldown == 0) {
  286.         ShootBullet();
  287.         shootCooldown = 10; // Adjust this value to control firing rate
  288.     }
  289.     if (shootCooldown > 0) {
  290.         shootCooldown--;
  291.     }
  292.  
  293.     // Wrap around screen
  294.     if (playerX < 0) playerX = WIN_WIDTH;
  295.     if (playerX > WIN_WIDTH) playerX = 0;
  296.     if (playerY < 0) playerY = WIN_HEIGHT;
  297.     if (playerY > WIN_HEIGHT) playerY = 0;
  298. }
  299.  
  300. bool CheckCollision(POINT player[], POINT asteroid[], int px, int py, int ax, int ay) {
  301.     for (int i = 0; i < 3; ++i) {
  302.         int px1 = player[i].x + px;
  303.         int py1 = player[i].y + py;
  304.         int px2 = player[(i + 1) % 3].x + px;
  305.         int py2 = player[(i + 1) % 3].y + py;
  306.  
  307.         for (int j = 0; j < 8; ++j) {
  308.             int ax1 = asteroid[j].x + ax;
  309.             int ay1 = asteroid[j].y + ay;
  310.             int ax2 = asteroid[(j + 1) % 8].x + ax;
  311.             int ay2 = asteroid[(j + 1) % 8].y + ay;
  312.  
  313.             // Check if the line segments intersect
  314.             if (DoLinesIntersect(px1, py1, px2, py2, ax1, ay1, ax2, ay2)) {
  315.                 return true;
  316.             }
  317.         }
  318.     }
  319.     return false;
  320. }
  321.  
  322. bool DoLinesIntersect(int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4) {
  323.     // Calculate the direction of the lines
  324.     int d1 = Direction(x3, y3, x4, y4, x1, y1);
  325.     int d2 = Direction(x3, y3, x4, y4, x2, y2);
  326.     int d3 = Direction(x1, y1, x2, y2, x3, y3);
  327.     int d4 = Direction(x1, y1, x2, y2, x4, y4);
  328.  
  329.     // Check if the lines intersect
  330.     if (((d1 > 0 && d2 < 0) || (d1 < 0 && d2 > 0)) && ((d3 > 0 && d4 < 0) || (d3 < 0 && d4 > 0))) {
  331.         return true;
  332.     }
  333.  
  334.     // Check for collinear points
  335.     if (d1 == 0 && OnSegment(x3, y3, x4, y4, x1, y1)) return true;
  336.     if (d2 == 0 && OnSegment(x3, y3, x4, y4, x2, y2)) return true;
  337.     if (d3 == 0 && OnSegment(x1, y1, x2, y2, x3, y3)) return true;
  338.     if (d4 == 0 && OnSegment(x1, y1, x2, y2, x4, y4)) return true;
  339.  
  340.     return false;
  341. }
  342.  
  343. int Direction(int x1, int y1, int x2, int y2, int x3, int y3) {
  344.     return (x3 - x1) * (y2 - y1) - (y3 - y1) * (x2 - x1);
  345. }
  346.  
  347. bool OnSegment(int x1, int y1, int x2, int y2, int x3, int y3) {
  348.     int minX = x1 < x2 ? x1 : x2;
  349.     int maxX = x1 > x2 ? x1 : x2;
  350.     int minY = y1 < y2 ? y1 : y2;
  351.     int maxY = y1 > y2 ? y1 : y2;
  352.  
  353.     if (minX <= x3 && x3 <= maxX && minY <= y3 && y3 <= maxY) {
  354.         return true;
  355.     }
  356.     return false;
  357. }
  358.  
  359. void MoveAsteroids() {
  360.     for (auto& ast : asteroids) {
  361.         ast.x += 2 * cos(ast.angle * 3.14159 / 180);
  362.         ast.y += 2 * sin(ast.angle * 3.14159 / 180);
  363.  
  364.         // Wrap around screen
  365.         if (ast.x < 0) ast.x = WIN_WIDTH;
  366.         if (ast.x > WIN_WIDTH) ast.x = 0;
  367.         if (ast.y < 0) ast.y = WIN_HEIGHT;
  368.         if (ast.y > WIN_HEIGHT) ast.y = 0;
  369.     }
  370. }
  371.  
  372. void ShootBullet() {
  373.     Bullet bullet;
  374.     // Calculate the tip of the triangle (point[0] is the top vertex)
  375.     bullet.x = playerX + player[0].x * cos(playerAngle * 3.14159 / 180)
  376.         - player[0].y * sin(playerAngle * 3.14159 / 180);
  377.     bullet.y = playerY + player[0].x * sin(playerAngle * 3.14159 / 180)
  378.         + player[0].y * cos(playerAngle * 3.14159 / 180);
  379.     // Adjust the bullet angle to match the ship's orientation
  380.     bullet.angle = playerAngle - 90;  // Subtract 90 degrees to align with the ship's nose
  381.     bullets.push_back(bullet);
  382. }
  383.  
  384. // Modified DrawBullet function for green bullets
  385. void DrawBullet(HDC hdc, int x, int y) {
  386.     HPEN greenPen = CreatePen(PS_SOLID, 1, RGB(0, 255, 0));    // Bright green
  387.     HBRUSH greenBrush = CreateSolidBrush(RGB(0, 255, 0));      // Bright green
  388.     SelectObject(hdc, greenPen);
  389.     SelectObject(hdc, greenBrush);
  390.     Ellipse(hdc, x - 4, y - 4, x + 4, y + 4);
  391.     DeleteObject(greenPen);
  392.     DeleteObject(greenBrush);
  393. }
  394.  
  395. bool CheckBulletCollision(int bx, int by) {
  396.     for (auto it = asteroids.begin(); it != asteroids.end();) {
  397.         if (PointInPolygon(bx, by, it->shape, it->x, it->y)) {
  398.             it = asteroids.erase(it);
  399.             score += 10;
  400.             return true;
  401.         }
  402.         else {
  403.             ++it;
  404.         }
  405.     }
  406.     return false;
  407. }
  408.  
  409. bool PointInPolygon(int px, int py, POINT polygon[], int ox, int oy) {
  410.     int i, j, nvert = 8;
  411.     bool c = false;
  412.  
  413.     for (i = 0, j = nvert - 1; i < nvert; j = i++) {
  414.         int polyX1 = polygon[i].x + ox;
  415.         int polyY1 = polygon[i].y + oy;
  416.         int polyX2 = polygon[j].x + ox;
  417.         int polyY2 = polygon[j].y + oy;
  418.  
  419.         if (((polyY1 > py) != (polyY2 > py)) &&
  420.             (px < (polyX2 - polyX1) * (py - polyY1) / (polyY2 - polyY1) + polyX1)) {
  421.             c = !c;
  422.         }
  423.     }
  424.     return c;
  425. }
  426.  
  427. void GenerateAsteroids() {
  428.     for (int i = 0; i < 5; ++i) {
  429.         Asteroid ast;
  430.         ast.x = rand() % WIN_WIDTH;
  431.         ast.y = rand() % WIN_HEIGHT;
  432.         ast.angle = rand() % 360;
  433.         for (int j = 0; j < 8; ++j) {
  434.             double angle = j * 45 * 3.14159 / 180;
  435.             ast.shape[j].x = (rand() % 20 + 10) * cos(angle);
  436.             ast.shape[j].y = (rand() % 20 + 10) * sin(angle);
  437.         }
  438.         asteroids.push_back(ast);
  439.     }
  440. }
  441.  
  442. void SpawnNewAsteroid() {
  443.     Asteroid ast;
  444.     // Randomly choose a side of the screen to spawn from
  445.     int side = rand() % 4;
  446.     switch (side) {
  447.     case 0: // Top
  448.         ast.x = rand() % WIN_WIDTH;
  449.         ast.y = -30;
  450.         break;
  451.     case 1: // Right
  452.         ast.x = WIN_WIDTH + 30;
  453.         ast.y = rand() % WIN_HEIGHT;
  454.         break;
  455.     case 2: // Bottom
  456.         ast.x = rand() % WIN_WIDTH;
  457.         ast.y = WIN_HEIGHT + 30;
  458.         break;
  459.     case 3: // Left
  460.         ast.x = -30;
  461.         ast.y = rand() % WIN_HEIGHT;
  462.         break;
  463.     }
  464.  
  465.     ast.angle = rand() % 360;
  466.     for (int j = 0; j < 8; ++j) {
  467.         double angle = j * 45 * 3.14159 / 180;
  468.         ast.shape[j].x = (rand() % 20 + 10) * cos(angle);
  469.         ast.shape[j].y = (rand() % 20 + 10) * sin(angle);
  470.     }
  471.     asteroids.push_back(ast);
  472. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement