← index
JavaScript.Projects/source/apps/WebAssembly_Ports/pool/pool.c
Source: JavaScript.Projects/source/apps/WebAssembly_Ports/pool/pool.c
#include "pool.h"
#include "sdl.h"
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>

#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif

int shot_count = 0;
bool game_won = false;
bool showing_win_screen = false;
int MAX_BUMPERS = 4;
SDL_Texture *ball_tex;

void init_stick(struct Stick *stick) {
    memset(stick, 0, sizeof(struct Stick));
    stick->x = WINDOW_W / 2;
    stick->y = WINDOW_H / 2;
    stick->angle = 0.0f;
    stick->length = 80.0f;
    stick->charge = 0.0f;
    stick->charging = false;
    stick->visible = true;
}

void init_ball(struct Ball *ball) {
    memset(ball, 0, sizeof(struct Ball));
    ball->x = WINDOW_W / 2;
    ball->y = WINDOW_H / 2;
    ball->vx = 0.0f;
    ball->vy = 0.0f;
    ball->radius = 10.0f;
    ball->moving = false;
}

void init_target(struct Bumper *bumpers, struct Target *target) {
    memset(target, 0, sizeof(struct Target));
    target->width = 75;
    target->height = 75;
    target->ball_in_target = false;
    bool valid_position = false;
    int attempts = 0;
    while (!valid_position && attempts < 50) {
        target->x = 20 + rand() % (WINDOW_W - (int)target->width - 40);
        target->y = 20 + rand() % (WINDOW_H - (int)target->height - 40);
        valid_position = true;
        float center_x = WINDOW_W / 2;
        float center_y = WINDOW_H / 2;
        float target_center_x = target->x + target->width / 2;
        float target_center_y = target->y + target->height / 2;
        float dist_from_center = sqrtf((target_center_x - center_x) * (target_center_x - center_x) + 
                                      (target_center_y - center_y) * (target_center_y - center_y));
        
        if (dist_from_center < 120.0f) {
            valid_position = false;
            attempts++;
            continue;
        }
        for (int i = 0; i < MAX_BUMPERS; i++) {
            float bumper_dist_x = fabs(target_center_x - bumpers[i].x);
            float bumper_dist_y = fabs(target_center_y - bumpers[i].y);   
            if (bumper_dist_x < (target->width/2 + bumpers[i].radius + 20) &&
                bumper_dist_y < (target->height/2 + bumpers[i].radius + 20)) {
                valid_position = false;
                break;
            }
        }   
        attempts++;
    }  
    if (!valid_position) {
        int corner = rand() % 4;
        switch (corner) {
            case 0: 
                target->x = 20;
                target->y = 20;
                break;
            case 1: 
                target->x = WINDOW_W - target->width - 20;
                target->y = 20;
                break;
            case 2: 
                target->x = 20;
                target->y = WINDOW_H - target->height - 20;
                break;
            case 3: 
                target->x = WINDOW_W - target->width - 20;
                target->y = WINDOW_H - target->height - 20;
                break;
        }
    }
}

void init_bumpers(struct Bumper **bumpers) {
    if(*bumpers != NULL) {
        free(*bumpers);
        *bumpers = NULL;
        MAX_BUMPERS ++;
    }
    *bumpers = (struct Bumper *)malloc(sizeof(struct Bumper) * MAX_BUMPERS);
    for (int i = 0; i < MAX_BUMPERS; i++) {
        bool valid_position = false;
        int attempts = 0;
        while (!valid_position && attempts < 50) {
            (*bumpers)[i].x = 60 + rand() % (WINDOW_W - 120);
            (*bumpers)[i].y = 60 + rand() % (WINDOW_H - 120);
            (*bumpers)[i].radius = 15.0f;
            valid_position = true;
            float center_x = WINDOW_W / 2;
            float center_y = WINDOW_H / 2;
            float dist_from_center = sqrtf(((*bumpers)[i].x - center_x) * ((*bumpers)[i].x - center_x) + 
                                          ((*bumpers)[i].y - center_y) * ((*bumpers)[i].y - center_y));
            if (dist_from_center < 80.0f) {
                valid_position = false;
                attempts++;
                continue;
            }
            for (int j = 0; j < i; j++) {
                float dist = sqrtf(((*bumpers)[i].x - (*bumpers)[j].x) * ((*bumpers)[i].x - (*bumpers)[j].x) + 
                                  ((*bumpers)[i].y - (*bumpers)[j].y) * ((*bumpers)[i].y - (*bumpers)[j].y));
                if (dist < 60.0f) { 
                    valid_position = false;
                    break;
                }
            }   
            attempts++;
        }
        if (!valid_position) {
            (*bumpers)[i].x = 100 + (i * 150);
            (*bumpers)[i].y = 100 + ((i % 2) * 200);
            (*bumpers)[i].radius = 15.0f;
        }
    }
}
void ensure_bumpers_avoid_target(struct Bumper *bumpers, struct Target *target) {
    for (int i = 0; i < MAX_BUMPERS; i++) {
        bool needs_relocation = false;
        float buffer = 30.0f; 
        if (bumpers[i].x >= target->x - buffer && 
            bumpers[i].x <= target->x + target->width + buffer &&
            bumpers[i].y >= target->y - buffer && 
            bumpers[i].y <= target->y + target->height + buffer) {
            needs_relocation = true;
        }
        
        if (needs_relocation) {

            bool found_new_position = false;
            int attempts = 0;
            
            while (!found_new_position && attempts < 30) {
                float new_x = 60 + rand() % (WINDOW_W - 120);
                float new_y = 60 + rand() % (WINDOW_H - 120);
                float center_x = WINDOW_W / 2;
                float center_y = WINDOW_H / 2;
                float dist_from_center = sqrtf((new_x - center_x) * (new_x - center_x) + 
                                              (new_y - center_y) * (new_y - center_y));
                
                if (dist_from_center < 80.0f) {
                    attempts++;
                    continue;
                }
                if (new_x >= target->x - buffer && 
                    new_x <= target->x + target->width + buffer &&
                    new_y >= target->y - buffer && 
                    new_y <= target->y + target->height + buffer) {
                    attempts++;
                    continue;
                }
                bool too_close_to_other = false;
                for (int j = 0; j < MAX_BUMPERS; j++) {
                    if (i != j) {
                        float dist = sqrtf((new_x - bumpers[j].x) * (new_x - bumpers[j].x) + 
                                          (new_y - bumpers[j].y) * (new_y - bumpers[j].y));
                        if (dist < 60.0f) {
                            too_close_to_other = true;
                            break;
                        }
                    }
                }   
                if (!too_close_to_other) {
                    bumpers[i].x = new_x;
                    bumpers[i].y = new_y;
                    found_new_position = true;
                } else {
                    attempts++;
                }
            }
            if (!found_new_position) {
                float corners[4][2] = {
                    {100, 100},                          
                    {WINDOW_W - 100, 100},               
                    {100, WINDOW_H - 100},               
                    {WINDOW_W - 100, WINDOW_H - 100}     
                };
                float max_dist = 0;
                int best_corner = 0;
                for (int c = 0; c < 4; c++) {
                    float target_center_x = target->x + target->width / 2;
                    float target_center_y = target->y + target->height / 2;
                    float dist = sqrtf((corners[c][0] - target_center_x) * (corners[c][0] - target_center_x) +
                                      (corners[c][1] - target_center_y) * (corners[c][1] - target_center_y));
                    if (dist > max_dist) {
                        max_dist = dist;
                        best_corner = c;
                    }
                }
                bumpers[i].x = corners[best_corner][0] + (i * 20); 
                bumpers[i].y = corners[best_corner][1] + ((i % 2) * 20);
            }
        }
    }
}

void update_stick(struct Stick *stick) {
    const Uint8 *keys = SDL_GetKeyboardState(NULL);
    if (keys[SDL_SCANCODE_LEFT]) {
        stick->angle -= 0.05f;
    }
    if (keys[SDL_SCANCODE_RIGHT]) {
        stick->angle += 0.05f;
    }
    if (keys[SDL_SCANCODE_SPACE]) {
        if (!stick->charging) {
            stick->charging = true;
            stick->charge = 0.0f;
        }
        if (stick->charge < 100.0f) {
            stick->charge += 2.0f; 
        }
    }
}

void update_ball(struct Ball *ball) {
    if (!ball->moving) return;
    ball->just_stopped = false;
    ball->x += ball->vx;
    ball->y += ball->vy;
    if (ball->x - ball->radius <= 0 || ball->x + ball->radius >= WINDOW_W) {
        ball->vx = -ball->vx * 0.8f;
        ball->x = (ball->x - ball->radius <= 0) ? ball->radius : WINDOW_W - ball->radius;
    }
    
    if (ball->y - ball->radius <= 0 || ball->y + ball->radius >= WINDOW_H) {
        ball->vy = -ball->vy * 0.8f;
        ball->y = (ball->y - ball->radius <= 0) ? ball->radius : WINDOW_H - ball->radius;
    }
    ball->vx *= 0.995f;
    ball->vy *= 0.995f;
    if (fabs(ball->vx) < 0.1f && fabs(ball->vy) < 0.1f) {
        ball->vx = 0.0f;
        ball->vy = 0.0f;
        ball->moving = false;
        ball->just_stopped = true; 
    }
}

void check_ball_target(struct Ball *ball, struct Target *target) {
    target->ball_in_target = (ball->x >= target->x && 
                             ball->x <= target->x + target->width &&
                             ball->y >= target->y && 
                             ball->y <= target->y + target->height);
}

void hit_ball(struct Stick *stick, struct Ball *ball) {
    if (!stick->charging) return;
    float power = stick->charge / 100.0f * 15.0f; 
    ball->vx = cosf(stick->angle) * power;
    ball->vy = sinf(stick->angle) * power;
    ball->moving = true;
    shot_count++;    
    stick->charging = false;
    stick->charge = 0.0f;
    stick->visible = true;
}

void draw_stick(struct Stick *stick) {
    if (!stick->visible) return;

    float stick_offset = stick->charging ? (stick->charge / 100.0f * 30.0f) : 0.0f;
    float start_x = stick->x - cosf(stick->angle) * (stick->length + stick_offset);
    float start_y = stick->y - sinf(stick->angle) * (stick->length + stick_offset);
    float end_x = stick->x - cosf(stick->angle) * stick_offset;
    float end_y = stick->y - sinf(stick->angle) * stick_offset;
    
    
    if (stick->charging) {
        int red = 255;
        int green = 255 - (int)(stick->charge * 2.55f);
        int blue = 255 - (int)(stick->charge * 2.55f);
        SDL_SetRenderDrawColor(renderer, red, green, blue, 255);
    } else {
        SDL_SetRenderDrawColor(renderer, 139, 69, 19, 255); 
    }
    
    
    for (int i = -2; i <= 2; i++) {
        for (int j = -2; j <= 2; j++) {
            SDL_RenderDrawLine(renderer, 
                             (int)(start_x + i), (int)(start_y + j),
                             (int)(end_x + i), (int)(end_y + j));
        }
    }
    
    
    if (stick->charging) {
        SDL_SetRenderDrawColor(renderer, 255, 255, 0, 128);
        float aim_end_x = stick->x + cosf(stick->angle) * 100.0f;
        float aim_end_y = stick->y + sinf(stick->angle) * 100.0f;
        SDL_RenderDrawLine(renderer, (int)stick->x, (int)stick->y, 
                          (int)aim_end_x, (int)aim_end_y);
    }
}

void draw_ball(struct Ball *ball) {    
    int ball_size = (int)(ball->radius * 2);
    SDL_Rect dest_rect = {
        (int)(ball->x - ball->radius),  
        (int)(ball->y - ball->radius),  
        ball_size,                      
        ball_size                       
    };

    SDL_RenderCopy(renderer, ball_tex, NULL, &dest_rect);
}

void draw_target(struct Target *target) {

    SDL_Rect rect = {(int)target->x, (int)target->y, (int)target->width, (int)target->height};
    SDL_RenderCopy(renderer, target_bmp, NULL, &rect);
}

void draw_bumpers(struct Bumper *bumpers) {
    for (int i = 0; i < MAX_BUMPERS; i++) {
        int bumper_size = (int)(bumpers[i].radius * 2);
        SDL_Rect dest_rect = {
            (int)(bumpers[i].x - bumpers[i].radius),  
            (int)(bumpers[i].y - bumpers[i].radius),  
            bumper_size,                              
            bumper_size                               
        };
        SDL_RenderCopy(renderer, bumber, NULL, &dest_rect);
    }
}

void check_ball_bumper_collision(struct Ball *ball, struct Bumper *bumpers) {
    if (!ball->moving) return;
    
    for (int i = 0; i < MAX_BUMPERS; i++) {
        float dx = ball->x - bumpers[i].x;
        float dy = ball->y - bumpers[i].y;
        float distance = sqrtf(dx * dx + dy * dy);
        
        if (distance < ball->radius + bumpers[i].radius) {
            float nx = dx / distance;
            float ny = dy / distance;
            
            float overlap = ball->radius + bumpers[i].radius - distance;
            ball->x += nx * overlap;
            ball->y += ny * overlap;
            
            float dot_product = ball->vx * nx + ball->vy * ny;
            
            ball->vx = ball->vx - 2.0f * dot_product * nx;
            ball->vy = ball->vy - 2.0f * dot_product * ny;
            
            ball->vx *= 1.1f;
            ball->vy *= 1.1f;
            
            float speed = sqrtf(ball->vx * ball->vx + ball->vy * ball->vy);
            if (speed > 20.0f) {
                ball->vx = (ball->vx / speed) * 20.0f;
                ball->vy = (ball->vy / speed) * 20.0f;
            }
        }
    }
}

void draw_ui(struct Stick *stick, struct Target *target) {
    if (stick->charging) {
        SDL_Rect power_bg = {10, 10, 200, 20};
        SDL_Rect power_fill = {10, 10, (int)(stick->charge * 2), 20};
        
        SDL_SetRenderDrawColor(renderer, 50, 50, 50, 255);
        SDL_RenderFillRect(renderer, &power_bg);
        
        SDL_SetRenderDrawColor(renderer, 255, 255 - (int)(stick->charge * 2.55f), 0, 255);
        SDL_RenderFillRect(renderer, &power_fill);
        
        SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255);
        SDL_RenderDrawRect(renderer, &power_bg);
    }
    
    settextcolor(255, 255, 255, 255);
    printtext("Arrow Keys: Aim", 10, WINDOW_H - 60);
    printtext("Space: Hold to charge, Release to shoot", 10, WINDOW_H - 40);
    
    if (target->ball_in_target) {
        settextcolor(0, 255, 0, 255);
        printtext("TARGET HIT!", WINDOW_W/2 - 50, 50);
    } else {
        settextcolor(255, 255, 255, 255);
        printtext("Get ball in the target!", 10, WINDOW_H - 20);
    }
    settextcolor(255, 255, 255, 255);
    char sbuf[50];
    snprintf(sbuf, 49, "Tries: %d", shot_count);
    printtext(sbuf, 25, 35);
}

void realign_stick_with_ball(struct Stick *stick, struct Ball *ball) {
    stick->x = ball->x;
    stick->y = ball->y;
    stick->charge = 0.0f;
    stick->charging = false;
    stick->visible = true;
}

void check_win_condition(struct Ball *ball, struct Target *target) {
    if (target->ball_in_target && !ball->moving && !showing_win_screen) {
        game_won = true;
        showing_win_screen = true;
    }
}

void draw_win_screen(void) {
    
    SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
    SDL_RenderClear(renderer);
    
    settextcolor(0, 255, 0, 255);
    printtext("CONGRATULATIONS!", WINDOW_W/2 - 80, WINDOW_H/2 - 60);
    
    settextcolor(255, 255, 255, 255);
    printtext("You got the ball in the target!", WINDOW_W/2 - 120, WINDOW_H/2 - 20);
    
    char shot_text[50];
    snprintf(shot_text, 50, "Shots taken: %d", shot_count);
    settextcolor(255, 255, 0, 255);
    printtext(shot_text, WINDOW_W/2 - 60, WINDOW_H/2 + 20);
    
    settextcolor(200, 200, 200, 255);
    printtext("Press R to play again", WINDOW_W/2 - 80, WINDOW_H/2 + 60);
    printtext("Press ESC to quit", WINDOW_W/2 - 70, WINDOW_H/2 + 80);
}