#include<SDL2/SDL.h> #include<stdlib.h> #include<math.h> #include"sdl.h" #include"ship.h" #include"projectile.h" #include"alien.h" #ifdef __EMSCRIPTEN__ #include<emscripten.h> #endif int active = 1; SDL_Event e; struct Ship ship; struct pNode *projectiles = NULL; struct Alien *aliens = NULL; int alien_direction = 1; int alien_move_timer = 0; int alien_move_speed = 45; int explosion_active = 0; int explosion_x = 0; int explosion_y = 0; int explosion_timer = 0; int explosion_duration = 30; int score = 0; int lives = 3; int game_over = 0; int projectile_cooldown = 0; int projectile_cooldown_max = 12; int max_projectiles = 1; typedef struct { int active; int x, y; int timer; int duration; } AlienExplosion; AlienExplosion alien_explosions[10]; int max_alien_explosions = 10; void render(void); void loop(void); void keyscan(void); void update_aliens(void); void check_collisions(void); void check_ship_collision(void); void draw_explosion(int x, int y, int frame); int count_living_aliens(void); void reset_game(void); void draw_game_over(void); void reset_alien_positions(void); void check_alien_invasion(void); void draw_alien_explosions(void); void add_alien_explosion(int x, int y); int main(int argc, char **argv) { if(!initSDL("[space]", 640, 360, 1280, 720)) { fprintf(stderr, "Error: %s\n", SDL_GetError()); return EXIT_FAILURE; } ship_init(&ship); aliens = alien_create_grid(13, 6); #ifdef __EMSCRIPTEN__ emscripten_set_main_loop(loop, 0, 1); #else while(active == 1) { loop(); } #endif alien_free(aliens); pnode_free(projectiles); releaseSDL(); return 0; } void loop(void) { while(SDL_PollEvent(&e)) { switch(e.type) { case SDL_QUIT: active = 0; break; case SDL_KEYDOWN: if(e.key.keysym.sym == SDLK_ESCAPE) { active = 0; continue; } if(e.key.keysym.sym == SDLK_r) { reset_game(); continue; } if(e.key.keysym.sym == SDLK_RETURN && game_over) { reset_game(); continue; } break; } } if (game_over) { render(); SDL_Delay(16); return; } if (projectile_cooldown > 0) { projectile_cooldown--; } if (!game_over) { keyscan(); update_aliens(); check_collisions(); check_ship_collision(); check_alien_invasion(); } if (explosion_active) { if (explosion_timer >= explosion_duration) { explosion_active = 0; explosion_timer = 0; if(lives <= 0) { game_over = 1; } } } if (!explosion_active && count_living_aliens() == 0) { alien_free(aliens); aliens = alien_create_grid(13, 6); if (alien_move_speed > 10) { alien_move_speed -= 8; } alien_lowest_y = 0; } render(); SDL_Delay(16); } Uint64 timeout = 0; void update_aliens(void) { int living_aliens = count_living_aliens(); int base_speed = 45; int total_aliens = 78; if(timeout == 0) timeout = SDL_GetTicks64(); Uint64 wait = SDL_GetTicks64(); if(wait-timeout > 600) { projectiles = pnode_add(projectiles, rand()%630, alien_lowest_y + 30, 1); timeout = SDL_GetTicks64(); } alien_move_speed = base_speed - ((total_aliens - living_aliens) / 3); if (alien_move_speed < 8) alien_move_speed = 8; alien_move_timer++; if (alien_move_timer >= alien_move_speed) { alien_move_timer = 0; int hit_edge = 0; struct Alien *current = aliens; while (current != NULL) { if (current->alive) { if ((current->x <= 5 && alien_direction == -1) || (current->x >= 585 && alien_direction == 1)) { hit_edge = 1; break; } } current = current->next; } if (hit_edge) { alien_direction *= -1; aliens = alien_update_all(aliens, 0, 25); } else { aliens = alien_update_all(aliens, alien_direction * 12, 0); } } } int check_proj_col(int x, int y) { struct pNode *node = projectiles; while(node != NULL) { if(node->type == 0) { SDL_Point p = {x, y}; SDL_Rect rc = {node->x, node->y, 2, 2}; if(SDL_PointInRect(&p, &rc)) { return 1; } } node = node->next; } return 0; } void check_collisions(void) { struct pNode *current_projectile = projectiles; struct pNode *prev_projectile = NULL; while (current_projectile != NULL) { int hit = 0; struct Alien *current_alien = aliens; while (current_alien != NULL && !hit) { if (current_alien->alive) { if (current_projectile->x >= current_alien->x - 2 && current_projectile->x <= current_alien->x + 22 && current_projectile->y >= current_alien->y - 2 && current_projectile->y <= current_alien->y + 17) { current_alien->alive = 0; hit = 1; add_alien_explosion(current_alien->x + 10, current_alien->y + 8); switch(current_alien->type) { case 0: score += 30; break; case 1: score += 20; break; case 2: score += 10; break; case 3: score += 300; break; default: score += 10; break; } } } current_alien = current_alien->next; } if (current_projectile->type == 1) { struct pNode *player_proj = projectiles; struct pNode *prev_player_proj = NULL; while (player_proj != NULL) { if (player_proj != current_projectile && player_proj->type == 0) { int dx = player_proj->x - current_projectile->x; int dy = player_proj->y - current_projectile->y; int distance_squared = dx*dx + dy*dy; if (distance_squared < 25) { hit = 1; if (prev_player_proj == NULL) { projectiles = player_proj->next; free(player_proj); player_proj = projectiles; } else { prev_player_proj->next = player_proj->next; free(player_proj); player_proj = prev_player_proj->next; } break; } } if (player_proj != NULL) { prev_player_proj = player_proj; player_proj = player_proj->next; } } } if (hit) { if (prev_projectile == NULL) { projectiles = current_projectile->next; free(current_projectile); current_projectile = projectiles; } else { prev_projectile->next = current_projectile->next; free(current_projectile); current_projectile = prev_projectile->next; } } else { prev_projectile = current_projectile; current_projectile = current_projectile->next; } } } void check_ship_collision(void) { struct Alien *current = aliens; while (current != NULL) { if (current->alive) { if (current->x < ship.x + ship.w && current->x + 20 > ship.x && current->y < ship.y + ship.h && current->y + 15 > ship.y) { explosion_active = 1; explosion_x = ship.x + ship.w / 2; explosion_y = ship.y + ship.h / 2; explosion_timer = 0; current->alive = 0; lives--; reset_alien_positions(); break; } } current = current->next; } struct pNode *p_node = projectiles; while(p_node != NULL) { if(p_node->type == 1) { if(ship_inrect(&ship, p_node->x, p_node->y)) { explosion_active = 1; explosion_x = ship.x + ship.w / 2; explosion_y = ship.y + ship.h / 2; explosion_timer = 0; lives--; ship.x = 640/2 - ship.w/2; if (projectiles != NULL) { pnode_free(projectiles); projectiles = NULL; } reset_alien_positions(); break;; } } p_node = p_node->next; } } void reset_alien_positions(void) { struct Alien *current = aliens; int row = 0; int col = 0; int spacing_x = 45; int spacing_y = 35; int cols = 13; int grid_width = cols * spacing_x; int start_x = (640 - grid_width) / 2; int start_y = 40; while (current != NULL) { current->x = start_x + (col * spacing_x); current->y = start_y + (row * spacing_y); col++; if (col >= cols) { col = 0; row++; } current = current->next; } alien_direction = 1; alien_move_timer = 0; pnode_free(projectiles); projectiles = NULL; } void draw_explosion(int x, int y, int frame) { if (!explosion_active) return; explosion_timer++; if (explosion_timer >= explosion_duration) { explosion_active = 0; explosion_timer = 0; if(lives <= 0) { game_over = 1; } return; } int intensity = 255 - (explosion_timer * 255 / explosion_duration); SDL_SetRenderDrawColor(renderer, rand()%255, intensity, intensity / 2, 255); int num_lines = 12; int radius = explosion_timer * 2; for (int i = 0; i < num_lines; i++) { float angle = (i * 2.0f * 3.14159f) / num_lines; int end_x = x + (int)(radius * cos(angle)); int end_y = y + (int)(radius * sin(angle)); SDL_RenderDrawLine(renderer, x, y, end_x, end_y); if (explosion_timer % 3 == 0) { int spark_x = x + (rand() % 40) - 20; int spark_y = y + (rand() % 40) - 20; SDL_RenderDrawPoint(renderer, spark_x, spark_y); } } int circle_points = 16; for (int i = 0; i < circle_points; i++) { float angle1 = (i * 2.0f * 3.14159f) / circle_points; float angle2 = ((i + 1) * 2.0f * 3.14159f) / circle_points; int x1 = x + (int)(radius * cos(angle1)); int y1 = y + (int)(radius * sin(angle1)); int x2 = x + (int)(radius * cos(angle2)); int y2 = y + (int)(radius * sin(angle2)); SDL_RenderDrawLine(renderer, x1, y1, x2, y2); } } void draw_game_over(void) { SDL_SetRenderDrawColor(renderer, 0, 0, 0, 128); SDL_Rect overlay = {0, 0, 640, 360}; SDL_RenderFillRect(renderer, &overlay); settextcolor(255, 0, 0, 255); printtext("GAME OVER", 280, 150); settextcolor(255, 255, 255, 255); char final_score[64]; snprintf(final_score, 64, "Final Score: %d", score); printtext(final_score, 260, 180); settextcolor(255, 255, 0, 255); printtext("Press ENTER to continue", 200, 210); } int count_living_aliens(void) { int count = 0; struct Alien *current = aliens; while (current != NULL) { if (current->alive) { count++; } current = current->next; } return count; } int count_projectiles(void) { int count = 0; struct pNode *current = projectiles; while (current != NULL) { if(current->type == 0) count++; current = current->next; } return count; } void reset_game(void) { ship_init(&ship); if (projectiles != NULL) { pnode_free(projectiles); projectiles = NULL; } if (aliens != NULL) { alien_free(aliens); } aliens = alien_create_grid(13, 6); explosion_active = 0; explosion_timer = 0; alien_direction = 1; alien_move_timer = 0; alien_move_speed = 45; projectile_cooldown = 0; for (int i = 0; i < max_alien_explosions; i++) { alien_explosions[i].active = 0; } game_over = 0; score = 0; lives = 3; alien_lowest_y = 0; } void check_alien_invasion(void) { struct Alien *current = aliens; while (current != NULL) { if (current->alive && current->y >= ship.y - 30) { explosion_active = 1; explosion_x = ship.x + ship.w / 2; explosion_y = ship.y + ship.h / 2; explosion_timer = 0; lives--; reset_alien_positions(); if(lives <= 0) { game_over = 1; } break; } current = current->next; } } void render(void) { SDL_SetRenderTarget(renderer, texture); SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255); SDL_RenderClear(renderer); if (!explosion_active && !game_over) { ship_draw(&ship); } projectiles = pnode_display(projectiles); aliens = alien_display_all(aliens); draw_alien_explosions(); if (explosion_active) { draw_explosion(explosion_x, explosion_y, explosion_timer); } char score_buf[1024]; snprintf(score_buf, 1024, "Score: %d", score); settextcolor(255,255,255,255); printtext(score_buf, 15, 15); snprintf(score_buf,1024, "Lives: %d", lives); printtext(score_buf, 150, 15); if (game_over) { draw_game_over(); } SDL_SetRenderTarget(renderer, NULL); SDL_RenderCopy(renderer, texture, NULL, NULL); SDL_RenderPresent(renderer); } void keyscan(void) { const Uint8 *keys = SDL_GetKeyboardState(0); if (!explosion_active && !game_over) { if (keys[SDL_SCANCODE_LEFT]) { ship_left(&ship); } if(keys[SDL_SCANCODE_RIGHT]) { ship_right(&ship); } if(keys[SDL_SCANCODE_SPACE] && projectile_cooldown == 0 && count_projectiles() < max_projectiles) { projectiles = pnode_add(projectiles, ship.x+(ship.w/2), ship.y-ship.h, 0); projectile_cooldown = projectile_cooldown_max; } } } void add_alien_explosion(int x, int y) { for (int i = 0; i < max_alien_explosions; i++) { if (!alien_explosions[i].active) { alien_explosions[i].active = 1; alien_explosions[i].x = x; alien_explosions[i].y = y; alien_explosions[i].timer = 0; alien_explosions[i].duration = 15; break; } } } void draw_alien_explosions(void) { for (int i = 0; i < max_alien_explosions; i++) { if (alien_explosions[i].active) { alien_explosions[i].timer++; if (alien_explosions[i].timer >= alien_explosions[i].duration) { alien_explosions[i].active = 0; continue; } int x = alien_explosions[i].x; int y = alien_explosions[i].y; int timer = alien_explosions[i].timer; int intensity = 255 - (timer * 255 / alien_explosions[i].duration); SDL_SetRenderDrawColor(renderer, 255, intensity, 0, 255); int num_lines = 6; int radius = timer; for (int j = 0; j < num_lines; j++) { float angle = (j * 2.0f * 3.14159f) / num_lines; int end_x = x + (int)(radius * cos(angle)); int end_y = y + (int)(radius * sin(angle)); SDL_RenderDrawLine(renderer, x, y, end_x, end_y); } if (timer % 2 == 0) { for (int k = 0; k < 4; k++) { int spark_x = x + (rand() % 10) - 5; int spark_y = y + (rand() % 10) - 5; SDL_RenderDrawPoint(renderer, spark_x, spark_y); } } } } }