#include "asteroid_window.hpp" #include <cmath> #include <cstdlib> #include <ctime> namespace mx { AsteroidsWindow::AsteroidsWindow(mxApp &app) : mx::Window(app), ship{320, 240, 0, 0} { screen = SDL_CreateTexture(app.ren, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, 640, 480); if(!screen) { mx::system_err << "MasterX System: Error creating Asteroid texture..\n"; mx::system_err.flush(); exit(EXIT_FAILURE); } std::srand(static_cast<unsigned>(std::time(0))); for (int i = 0; i < 5; ++i) { spawnAsteroid(); } } AsteroidsWindow::~AsteroidsWindow() { if(screen != nullptr) { SDL_DestroyTexture(screen); } } void AsteroidsWindow::screenResize(int w, int h) { const int baseWidth = 1280; const int baseHeight = 720; int screenWidth = w; int screenHeight = h; float scaleX = static_cast<float>(screenWidth) / baseWidth; float scaleY = static_cast<float>(screenHeight) / baseHeight; int windowWidth = static_cast<int>(640 * scaleX); int windowHeight = static_cast<int>(480 * scaleY); int windowPosX = (screenWidth - windowWidth) / 2; int windowPosY = (screenHeight - windowHeight) / 2; SDL_Rect rc={windowPosX, windowPosY, windowWidth, windowHeight}; setRect(rc); } void AsteroidsWindow::draw(mxApp &app) { if(!Window::isVisible()) return; if(!Window::isDraw()) return; Window::draw(app); Window::drawMenubar(app); SDL_Renderer *renderer = app.ren; SDL_SetRenderTarget(renderer, screen); SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255); SDL_RenderClear(renderer); drawShip(renderer, ship.x, ship.y, ship.angle); drawAsteroids(renderer); drawBullets(renderer); app.printText(25, 25, "Lives: " + std::to_string(lives),{255,255,255,255}); app.printText(25, 50, "Score: " + std::to_string(score), {255,0,0,255}); SDL_SetRenderTarget(renderer, app.tex); SDL_Rect rc; Window::getDrawRect(rc); SDL_RenderCopy(renderer, screen, nullptr, &rc); static Uint32 lastUpdate = SDL_GetTicks(); Uint32 current = SDL_GetTicks(); Uint32 delta = current - lastUpdate; if (delta >= 16) { lastUpdate = current; updateShip(); updateAsteroids(); updateBullets(); checkShipCollision(); const Uint8 *state = SDL_GetKeyboardState(NULL); if (state[SDL_SCANCODE_LEFT]) { ship.angle -= 5; } if (state[SDL_SCANCODE_RIGHT]) { ship.angle += 5; } if (state[SDL_SCANCODE_UP]) { ship.speed += 0.1; } static Uint32 lastFireTime = 0; static const Uint32 fireCooldown = 300; Uint32 currentTime = SDL_GetTicks(); if (state[SDL_SCANCODE_SPACE] && currentTime - lastFireTime >= fireCooldown) { fireBullet(); lastFireTime = currentTime; // Update the last fire time } } } void AsteroidsWindow::newGame() { score = 0; lives = 3; resetShip(); } bool AsteroidsWindow::event(mxApp &app, SDL_Event &e) { return Window::event(app, e); } void AsteroidsWindow::spawnAsteroid() { Asteroid asteroid; asteroid.x = static_cast<float>(std::rand() % 640); asteroid.y = static_cast<float>(std::rand() % 480); asteroid.dx = (std::rand() % 100 - 50) / 100.0f; asteroid.dy = (std::rand() % 100 - 50) / 100.0f; asteroid.radius = 20 + std::rand() % 30; asteroids.push_back(asteroid); } void AsteroidsWindow::updateAsteroids() { for (auto &asteroid : asteroids) { asteroid.x += asteroid.dx; asteroid.y += asteroid.dy; if (asteroid.x < 0) asteroid.x += 640; if (asteroid.x > 640) asteroid.x -= 640; if (asteroid.y < 0) asteroid.y += 480; if (asteroid.y > 480) asteroid.y -= 480; } if (asteroids.size() < 5) { while (asteroids.size() < 5) { spawnAsteroid(); } } } void AsteroidsWindow::updateBullets() { std::vector<Asteroid> newAsteroids; for (auto bulletIt = bullets.begin(); bulletIt != bullets.end(); ) { bulletIt->x += bulletIt->dx; bulletIt->y += bulletIt->dy; if (bulletIt->x < 0 || bulletIt->x > 640 || bulletIt->y < 0 || bulletIt->y > 480) { bulletIt = bullets.erase(bulletIt); continue; } bool bulletErased = false; for (auto asteroidIt = asteroids.begin(); asteroidIt != asteroids.end(); ) { float dist = std::sqrt(std::pow(bulletIt->x - asteroidIt->x, 2) + std::pow(bulletIt->y - asteroidIt->y, 2)); if (dist <= asteroidIt->radius) { bulletIt = bullets.erase(bulletIt); bulletErased = true; score += 100; if (asteroidIt->radius > 10) { for (int i = 0; i < 2; ++i) { Asteroid smallAsteroid; smallAsteroid.x = asteroidIt->x; smallAsteroid.y = asteroidIt->y; smallAsteroid.radius = asteroidIt->radius / 2; smallAsteroid.dx = (std::rand() % 100 - 50) / 100.0f; smallAsteroid.dy = (std::rand() % 100 - 50) / 100.0f; newAsteroids.push_back(smallAsteroid); } } asteroidIt = asteroids.erase(asteroidIt); break; } else { ++asteroidIt; } } if (!bulletErased) { ++bulletIt; } } asteroids.insert(asteroids.end(), newAsteroids.begin(), newAsteroids.end()); } void AsteroidsWindow::resetShip() { ship.x = 320; ship.y = 240; ship.angle = 0; ship.speed = 0; } void AsteroidsWindow::checkShipCollision() { for (const auto &asteroid : asteroids) { float dist = std::sqrt(std::pow(ship.x - asteroid.x, 2) + std::pow(ship.y - asteroid.y, 2)); if (dist <= asteroid.radius) { lives--; if (lives > 0) { resetShip(); } else { lives = 3; score = 0; resetShip(); } break; } } } void AsteroidsWindow::updateShip() { ship.x += ship.speed * std::cos(ship.angle * M_PI / 180.0); ship.y += ship.speed * std::sin(ship.angle * M_PI / 180.0); if (ship.x < 0) ship.x += 640; if (ship.x > 640) ship.x -= 640; if (ship.y < 0) ship.y += 480; if (ship.y > 480) ship.y -= 480; ship.speed *= 0.99; } template<typename T> T minx(const T &a, const T &b) { return (a < b) ? a : b; } template<typename T> T maxx(const T &a, const T&b) { return (a > b) ? a : b; } void AsteroidsWindow::drawShip(SDL_Renderer *renderer, int ship_x,int ship_y, float ship_angle) { SDL_Color color_inner = {200, 200, 200, 255}; SDL_Color color_outer = {100, 100, 100, 255}; SDL_Point points[3]; points[0] = { static_cast<int>(ship_x + 15 * std::cos(ship_angle * M_PI / 180.0)), static_cast<int>(ship_y + 15 * std::sin(ship_angle * M_PI / 180.0)) }; points[1] = { static_cast<int>(ship_x + 15 * std::cos((ship_angle + 140) * M_PI / 180.0)), static_cast<int>(ship_y + 15 * std::sin((ship_angle + 140) * M_PI / 180.0)) }; points[2] = { static_cast<int>(ship_x + 15 * std::cos((ship_angle + 220) * M_PI / 180.0)), static_cast<int>(ship_y + 15 * std::sin((ship_angle + 220) * M_PI / 180.0)) }; int minY = minx(points[0].y, minx(points[1].y, points[2].y)); int maxY = maxx(points[0].y, maxx(points[1].y, points[2].y)); for (int y = minY; y <= maxY; y++) { int startX = 640, endX = 0; for (int i = 0; i < 3; i++) { int next = (i + 1) % 3; if ((points[i].y <= y && points[next].y > y) || (points[next].y <= y && points[i].y > y)) { float t = (float)(y - points[i].y) / (points[next].y - points[i].y); int x = points[i].x + t * (points[next].x - points[i].x); if (x < startX) { startX = x; } if (x > endX) { endX = x; } } } for (int x = startX; x <= endX; x++) { float t = (float)(x - startX) / (endX - startX); Uint8 r = color_outer.r * (1 - t) + color_inner.r * t; Uint8 g = color_outer.g * (1 - t) + color_inner.g * t; Uint8 b = color_outer.b * (1 - t) + color_inner.b * t; SDL_SetRenderDrawColor(renderer, r, g, b, 255); SDL_RenderDrawPoint(renderer, x, y); } } } void AsteroidsWindow::drawAsteroids(SDL_Renderer *renderer) { for (const auto &asteroid : asteroids) { int rounded_x = static_cast<int>(std::round(asteroid.x)); int rounded_y = static_cast<int>(std::round(asteroid.y)); int radius = static_cast<int>(std::round(asteroid.radius)); draw_circle(renderer, rounded_x, rounded_y, radius); } } void AsteroidsWindow::draw_circle(SDL_Renderer *renderer, int center_x, int center_y, int radius) { for (int y = -radius; y <= radius; y++) { for (int x = -radius; x <= radius; x++) { int dist_sq = x * x + y * y; if (dist_sq <= radius * radius) { float dist = std::sqrt(dist_sq); float shade = 255 * (1 - (dist / radius)); if (shade < 0) shade = 0; if (shade > 255) shade = 255; SDL_SetRenderDrawColor(renderer, (Uint8)shade, (Uint8)shade, (Uint8)shade, 255); SDL_RenderDrawPoint(renderer, center_x + x, center_y + y); } } } } void AsteroidsWindow::drawBullets(SDL_Renderer *renderer) { SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255); for (const auto &bullet : bullets) { SDL_Rect rect = { static_cast<int>(bullet.x - 2), static_cast<int>(bullet.y - 2), 4, 4 }; SDL_RenderFillRect(renderer, &rect); } } void AsteroidsWindow::fireBullet() { Bullet bullet; bullet.x = ship.x; bullet.y = ship.y; bullet.dx = 5 * std::cos(ship.angle * M_PI / 180.0); bullet.dy = 5 * std::sin(ship.angle * M_PI / 180.0); bullets.push_back(bullet); } }