← index
JavaScript.Projects/source/apps/WebAssembly_Ports/invaders/main.c
Source: JavaScript.Projects/source/apps/WebAssembly_Ports/invaders/main.c
#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);
                }
            }
        }
    }
}