Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #include <windows.h>
- #include <vector>
- #include <cmath>
- #include <ctime>
- #define ID_TIMER 1
- #define BALL_RADIUS 10
- #define TABLE_WIDTH 800
- #define TABLE_HEIGHT 400
- HINSTANCE hInst;
- HWND hwnd;
- HDC hdcMem, hdcBall;
- HBITMAP hbmMem, hbmOld;
- HPEN hPen;
- HBRUSH hBrush;
- RECT table = { 50, 50, 850, 450 };
- POINT cueStart, cueEnd;
- bool isDragging = false;
- float power = 0.0f;
- struct Ball {
- float x, y;
- float vx, vy;
- COLORREF color;
- bool isPocketed;
- };
- std::vector<Ball> balls;
- Ball cueBall;
- bool playerTurn = true;
- bool gameOver = false;
- LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
- void InitBalls();
- void DrawTable(HDC);
- void DrawBalls(HDC);
- void DrawCue(HDC);
- void DrawPowerMeter(HDC);
- void UpdateBalls();
- void CheckCollisions();
- void CheckPockets();
- void AIMove();
- void ApplyEnglish(Ball&, float, float);
- bool CheckGameOver();
- int APIENTRY wWinMain(HINSTANCE hInstance, HINSTANCE, LPWSTR, int nCmdShow) {
- hInst = hInstance;
- WNDCLASS wc = {};
- wc.lpfnWndProc = WndProc;
- wc.hInstance = hInstance;
- wc.lpszClassName = L"8BallPool";
- RegisterClass(&wc);
- hwnd = CreateWindowEx(0, L"8BallPool", L"8-Ball Pool", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 900, 600, nullptr, nullptr, hInstance, nullptr);
- ShowWindow(hwnd, nCmdShow);
- MSG msg = {};
- while (GetMessage(&msg, nullptr, 0, 0)) {
- TranslateMessage(&msg);
- DispatchMessage(&msg);
- }
- return (int)msg.wParam;
- }
- LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) {
- switch (message) {
- case WM_CREATE:
- srand((unsigned int)time(NULL));
- InitBalls();
- SetTimer(hwnd, ID_TIMER, 16, NULL);
- break;
- case WM_PAINT: {
- PAINTSTRUCT ps;
- HDC hdc = BeginPaint(hwnd, &ps);
- if (!hdcMem) {
- hdcMem = CreateCompatibleDC(hdc);
- hbmMem = CreateCompatibleBitmap(hdc, 900, 600);
- hbmOld = (HBITMAP)SelectObject(hdcMem, hbmMem);
- }
- FillRect(hdcMem, &ps.rcPaint, (HBRUSH)(COLOR_WINDOW + 1));
- DrawTable(hdcMem);
- DrawBalls(hdcMem);
- if (!gameOver && playerTurn) {
- DrawCue(hdcMem);
- DrawPowerMeter(hdcMem);
- }
- BitBlt(hdc, 0, 0, 900, 600, hdcMem, 0, 0, SRCCOPY);
- EndPaint(hwnd, &ps);
- break;
- }
- case WM_LBUTTONDOWN:
- if (gameOver || !playerTurn) break;
- cueStart.x = LOWORD(lParam);
- cueStart.y = HIWORD(lParam);
- isDragging = true;
- break;
- case WM_MOUSEMOVE:
- if (isDragging) {
- cueEnd.x = LOWORD(lParam);
- cueEnd.y = HIWORD(lParam);
- power = min(sqrt(pow(cueEnd.x - cueStart.x, 2) + pow(cueEnd.y - cueStart.y, 2)) / 100.0f, 1.0f);
- InvalidateRect(hwnd, NULL, FALSE);
- }
- break;
- case WM_LBUTTONUP:
- if (isDragging) {
- isDragging = false;
- float dx = cueEnd.x - cueStart.x;
- float dy = cueEnd.y - cueStart.y;
- cueBall.vx = dx * power * 0.5f;
- cueBall.vy = dy * power * 0.5f;
- ApplyEnglish(cueBall, dx * power * 0.1f, dy * power * 0.1f);
- playerTurn = false;
- InvalidateRect(hwnd, NULL, FALSE);
- }
- break;
- case WM_TIMER:
- if (!gameOver) {
- UpdateBalls();
- CheckCollisions();
- CheckPockets();
- if (!playerTurn) AIMove();
- if (CheckGameOver()) {
- gameOver = true;
- MessageBox(hwnd, L"Game Over! All balls are pocketed.", L"Game Over", MB_OK);
- }
- InvalidateRect(hwnd, NULL, FALSE);
- }
- break;
- case WM_DESTROY:
- KillTimer(hwnd, ID_TIMER);
- SelectObject(hdcMem, hbmOld);
- DeleteObject(hbmMem);
- DeleteDC(hdcMem);
- PostQuitMessage(0);
- break;
- default:
- return DefWindowProc(hwnd, message, wParam, lParam);
- }
- return 0;
- }
- void InitBalls() {
- balls.clear();
- cueBall = { 450.0f, 250.0f, 0.0f, 0.0f, RGB(255, 255, 255), false };
- balls.push_back(cueBall);
- for (int i = 0; i < 7; ++i) {
- float angle = (float)i / 7.0f * 3.14159f * 2.0f;
- balls.push_back({ 600.0f + cos(angle) * 30, 250.0f + sin(angle) * 30, 0.0f, 0.0f, RGB(255, 255, 0), false });
- balls.push_back({ 600.0f + cos(angle) * 60, 250.0f + sin(angle) * 60, 0.0f, 0.0f, RGB(255, 0, 0), false });
- }
- }
- void DrawTable(HDC hdc) {
- HBRUSH hBrush = CreateSolidBrush(RGB(0, 128, 0));
- FillRect(hdc, &table, hBrush);
- DeleteObject(hBrush);
- for (int i = 0; i < 2; ++i) {
- for (int j = 0; j < 3; ++j) {
- Ellipse(hdc, table.left + j * TABLE_WIDTH / 2 - BALL_RADIUS, table.top + i * TABLE_HEIGHT - BALL_RADIUS, table.left + j * TABLE_WIDTH / 2 + BALL_RADIUS, table.top + i * TABLE_HEIGHT + BALL_RADIUS);
- }
- }
- }
- void DrawBalls(HDC hdc) {
- for (const auto& ball : balls) {
- if (!ball.isPocketed) {
- hBrush = CreateSolidBrush(ball.color);
- SelectObject(hdc, hBrush);
- Ellipse(hdc, (int)(ball.x - BALL_RADIUS), (int)(ball.y - BALL_RADIUS), (int)(ball.x + BALL_RADIUS), (int)(ball.y + BALL_RADIUS));
- DeleteObject(hBrush);
- }
- }
- }
- void DrawCue(HDC hdc) {
- MoveToEx(hdc, (int)cueBall.x, (int)cueBall.y, NULL);
- LineTo(hdc, cueEnd.x, cueEnd.y);
- }
- void DrawPowerMeter(HDC hdc) {
- HBRUSH hBrush = CreateSolidBrush(RGB(0, 255, 0));
- RECT rect = { 10, 50, 30, 450 };
- FillRect(hdc, &rect, hBrush);
- DeleteObject(hBrush);
- hBrush = CreateSolidBrush(RGB(255, 255, 0));
- rect.bottom = 450 - (int)(400 * power);
- FillRect(hdc, &rect, hBrush);
- DeleteObject(hBrush);
- hBrush = CreateSolidBrush(RGB(255, 0, 0));
- rect.bottom = 450 - (int)(400 * power / 2);
- FillRect(hdc, &rect, hBrush);
- DeleteObject(hBrush);
- }
- void UpdateBalls() {
- for (auto& ball : balls) {
- if (!ball.isPocketed) {
- ball.x += ball.vx;
- ball.y += ball.vy;
- ball.vx *= 0.99f;
- ball.vy *= 0.99f;
- if (fabs(ball.vx) < 0.01f) ball.vx = 0.0f;
- if (fabs(ball.vy) < 0.01f) ball.vy = 0.0f;
- }
- }
- }
- void CheckCollisions() {
- for (size_t i = 0; i < balls.size(); ++i) {
- for (size_t j = i + 1; j < balls.size(); ++j) {
- Ball& b1 = balls[i];
- Ball& b2 = balls[j];
- if (!b1.isPocketed && !b2.isPocketed) {
- float dx = b2.x - b1.x;
- float dy = b2.y - b1.y;
- float dist = sqrt(dx * dx + dy * dy);
- if (dist < BALL_RADIUS * 2) {
- float angle = atan2(dy, dx);
- float sinA = sin(angle);
- float cosA = cos(angle);
- // Rotate ball positions
- float x1 = 0;
- float y1 = 0;
- float x2 = dx * cosA + dy * sinA;
- float y2 = dy * cosA - dx * sinA;
- // Rotate velocities
- float vx1 = b1.vx * cosA + b1.vy * sinA;
- float vy1 = b1.vy * cosA - b1.vx * sinA;
- float vx2 = b2.vx * cosA + b2.vy * sinA;
- float vy2 = b2.vy * cosA - b2.vx * sinA;
- // Collision reaction
- float vx1Final = ((BALL_RADIUS - BALL_RADIUS) * vx1 + (2 * BALL_RADIUS) * vx2) / (BALL_RADIUS + BALL_RADIUS);
- float vx2Final = ((BALL_RADIUS - BALL_RADIUS) * vx2 + (2 * BALL_RADIUS) * vx1) / (BALL_RADIUS + BALL_RADIUS);
- // Update positions to avoid overlap
- x1 += vx1Final;
- x2 += vx2Final;
- // Rotate positions and velocities back
- b1.x = b1.x + (x1 * cosA - y1 * sinA);
- b1.y = b1.y + (y1 * cosA + x1 * sinA);
- b2.x = b1.x + (x2 * cosA - y2 * sinA);
- b2.y = b1.y + (y2 * cosA + x2 * sinA);
- b1.vx = vx1Final * cosA - vy1 * sinA;
- b1.vy = vy1 * cosA + vx1Final * sinA;
- b2.vx = vx2Final * cosA - vy2 * sinA;
- b2.vy = vy2 * cosA + vx2Final * sinA;
- }
- }
- }
- // Table boundary collision
- if (!balls[i].isPocketed) {
- if (balls[i].x - BALL_RADIUS < table.left) {
- balls[i].x = table.left + BALL_RADIUS;
- balls[i].vx = -balls[i].vx;
- }
- if (balls[i].x + BALL_RADIUS > table.right) {
- balls[i].x = table.right - BALL_RADIUS;
- balls[i].vx = -balls[i].vx;
- }
- if (balls[i].y - BALL_RADIUS < table.top) {
- balls[i].y = table.top + BALL_RADIUS;
- balls[i].vy = -balls[i].vy;
- }
- if (balls[i].y + BALL_RADIUS > table.bottom) {
- balls[i].y = table.bottom - BALL_RADIUS;
- balls[i].vy = -balls[i].vy;
- }
- }
- }
- }
- void CheckPockets() {
- for (auto& ball : balls) {
- if (!ball.isPocketed) {
- for (int i = 0; i < 2; ++i) {
- for (int j = 0; j < 3; ++j) {
- POINT pocket = { table.left + j * TABLE_WIDTH / 2, table.top + i * TABLE_HEIGHT };
- float dx = ball.x - pocket.x;
- float dy = ball.y - pocket.y;
- if (sqrt(dx * dx + dy * dy) < BALL_RADIUS) {
- ball.isPocketed = true;
- ball.vx = ball.vy = 0;
- if (ball.color == RGB(255, 255, 255)) {
- cueBall.x = 450.0f;
- cueBall.y = 250.0f;
- cueBall.vx = cueBall.vy = 0.0f;
- cueBall.isPocketed = false;
- }
- }
- }
- }
- }
- }
- }
- void AIMove() {
- if (playerTurn || gameOver) return;
- // Simple AI: hit the nearest ball
- Ball* targetBall = nullptr;
- float minDist = FLT_MAX;
- for (auto& ball : balls) {
- if (&ball != &cueBall && !ball.isPocketed) {
- float dx = ball.x - cueBall.x;
- float dy = ball.y - cueBall.y;
- float dist = sqrt(dx * dx + dy * dy);
- if (dist < minDist) {
- minDist = dist;
- targetBall = &ball;
- }
- }
- }
- if (targetBall) {
- float angle = atan2(targetBall->y - cueBall.y, targetBall->x - cueBall.x);
- cueBall.vx = cos(angle) * 5;
- cueBall.vy = sin(angle) * 5;
- }
- playerTurn = true;
- }
- void ApplyEnglish(Ball& ball, float englishX, float englishY) {
- ball.vx += englishX * 0.05f;
- ball.vy += englishY * 0.05f;
- // Apply friction effect to English
- ball.vx *= 0.98f;
- ball.vy *= 0.98f;
- }
- bool CheckGameOver() {
- bool allPocketed = true;
- for (const auto& ball : balls) {
- if (&ball != &cueBall && !ball.isPocketed) {
- allPocketed = false;
- break;
- }
- }
- return allPocketed;
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement