Skip to content
Snippets Groups Projects
render.c 28.94 KiB
#include "render.h"

float timer = 0;

SDL_Window *window;
SDL_Renderer *renderer;

TTF_Font *RobotoFont;
SDL_DisplayMode screenDimension;

SDL_Rect destRect;
SDL_Rect rect;
SDL_Rect sky;
SDL_Rect ground;
SDL_Rect racket;

int showHub = 1;

SDL_Texture *netTexture;
SDL_Texture *netEdgeLeftTexture;
SDL_Texture *netEdgeRightTexture;
SDL_Texture *crowdTexture;
SDL_Texture *playerTexture;
SDL_Texture *ballTexture;
SDL_Texture *skyTexture;
SDL_Texture *groundTexture;
SDL_Texture *racketTexture;

int **rays;
int raysListLength = 0;

int *ray1;
int *ray2;

rayInfo_t raysListHead;

float fps;

SDL_Texture *loadTexture(char *path)
{
    SDL_Surface *surface = IMG_Load(path);
    SDL_Texture *texture = SDL_CreateTextureFromSurface(renderer, surface);
    SDL_FreeSurface(surface);
    return texture;
}

void addRayInfoToList(rayInfo_t *rayInfoHead, rayInfo_t *rayInfo)
{
    rayInfo->next = rayInfoHead->next;
    rayInfoHead->next = rayInfo;
}

void freeRayInfoList(rayInfo_t *rayInfoHead)
{
    rayInfo_t *rayInfo = rayInfoHead->next;
    while (rayInfo != NULL)
    {
        rayInfo_t *next = rayInfo->next;
        free(rayInfo);
        rayInfo = next;
    }
}

rayInfo_t *allocRayInfo(float ra, float distT, int r, int isTransparent, int direction, float htexture, int rx, int ry)
{
    rayInfo_t *rayInfo = malloc(sizeof(rayInfo_t));
    if (rayInfo == NULL)
    {
        printf("Error: malloc failed\n");
        exit(1);
    }
    rayInfo->ra = ra;
    rayInfo->distT = distT;
    rayInfo->r = r;
    rayInfo->isTransparent = isTransparent;
    rayInfo->direction = direction;
    rayInfo->htexture = htexture;
    rayInfo->rx = rx;
    rayInfo->ry = ry;
    rayInfo->next = NULL;
    return rayInfo;
}

void initRays()
{
    int i;
    rays = malloc(sizeof(int *) * 2 * NB_RAYS);
    for (i = 0; i < NB_RAYS * 2; i++)
    {
        rays[i] = malloc(sizeof(int) * 2);
    }
}

int isRaysListEmpty()
{
    return raysListLength == 0;
}

void addRayToList(int x, int y)
{
    if (raysListLength < 2 * NB_RAYS)
    {
        *rays[raysListLength] = x;
        *(rays[raysListLength] + 1) = y;
        raysListLength++;
    }
}

void resetRayList()
{
    int i;
    for (i = 0; i < 2 * NB_RAYS; i++)
    {
        *rays[i] = 0;
        *(rays[i] + 1) = 0;
    }
    raysListLength = 0;
}

// end ray casting variables

void createWindow()
{

    if (SDL_Init(SDL_INIT_VIDEO) != 0)
    {
        printf("Couldn't create window.");
        exit(EXIT_FAILURE);
    }

    SDL_GetCurrentDisplayMode(0, &screenDimension);

    window = SDL_CreateWindow("Mat Le Tennisman", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, screenDimension.w, screenDimension.h, SDL_WINDOW_INPUT_GRABBED | SDL_WINDOW_SHOWN | SDL_WINDOW_FULLSCREEN_DESKTOP);

    if (window == NULL)
    {
        printf("Couldn't create window");
        exit(EXIT_FAILURE);
    }
    renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_TARGETTEXTURE | SDL_RENDERER_PRESENTVSYNC);

    if (renderer == NULL)
    {
        printf("Couldn't create renderer.");
        exit(EXIT_FAILURE);
    }

    if (TTF_Init() == -1)
    {
        exit(EXIT_FAILURE);
    }

    RobotoFont = TTF_OpenFont("Res/Roboto-Black.ttf", 50);
}

void endSDL()
{
    SDL_DestroyTexture(netTexture);
    SDL_DestroyTexture(netEdgeLeftTexture);
    SDL_DestroyTexture(netEdgeRightTexture);
    SDL_DestroyTexture(crowdTexture);
    SDL_DestroyTexture(playerTexture);
    SDL_DestroyTexture(ballTexture);
    SDL_DestroyTexture(skyTexture);
    SDL_DestroyTexture(groundTexture);
    SDL_DestroyTexture(racketTexture);

    SDL_DestroyWindow(window);
    SDL_DestroyRenderer(renderer);
    TTF_CloseFont(RobotoFont);
    TTF_Quit();
    SDL_Quit();
}

void drawRayColumn(rayInfo_t *rayInfo)
{
    float ca = player.angle - rayInfo->ra;
    if (ca < 0)
        ca += 2 * pi;
    if (ca > 2 * pi)
        ca -= 2 * pi;
    rayInfo->distT = rayInfo->distT * cos(ca);

    float lineH = (screenDimension.h / 2) / rayInfo->distT;
    rect.x = rayInfo->r;
    rect.y = (screenDimension.h / 2 + player.viewAngle) - lineH;
    rect.w = 1;
    rect.h = (2 * screenDimension.w * lineH / 20);

    destRect.x = rayInfo->htexture;
    destRect.y = 0;
    destRect.w = 1;
    destRect.h = 64;

    if (rayInfo->isTransparent)
    {
        rect.h *= 1.2;
        if (map[rayInfo->ry / BLOCK_SIZE][rayInfo->rx / BLOCK_SIZE] == 3)
        {
            SDL_RenderCopy(renderer, netEdgeLeftTexture, &destRect, &rect);
        }

        if (map[rayInfo->ry / BLOCK_SIZE][rayInfo->rx / BLOCK_SIZE] == 4)
        {
            SDL_RenderCopy(renderer, netEdgeRightTexture, &destRect, &rect);
        }

        else
        {
            SDL_RenderCopy(renderer, netTexture, &destRect, &rect);
        }
    }
    else
    {
        destRect.x += +64 * (SDL_GetTicks() / 200 % 4);
        if (rayInfo->direction)
        {
            SDL_RenderCopy(renderer, crowdTexture, &destRect, &rect);
        }
        else
        {
            SDL_RenderCopy(renderer, crowdTexture, &destRect, &rect);
        }
    }
}

void drawVerticalWalls()
{
    rayInfo_t *current = raysListHead.next;
    while (current != NULL)
    {
        if (current->direction && !current->isTransparent)
        {
            drawRayColumn(current);
        }
        current = current->next;
    }
}

void drawVerticalNet()
{
    rayInfo_t *current = raysListHead.next;
    while (current != NULL)
    {
        if (current->direction && current->isTransparent)
        {
            drawRayColumn(current);
        }
        current = current->next;
    }
}

void drawHorizentalWalls()
{
    rayInfo_t *current = raysListHead.next;
    while (current != NULL)
    {
        if (!current->direction)
        {
            drawRayColumn(current);
        }
        current = current->next;
    }
}

void castSingleRay(float angle, float *distanceWall, float *distanceNet, int *returnXWall, int *returnYWall, int *returnXNet, int *returnYNet)
{
    // ray casting variables
    int mx, my, dof;
    double rx, ry, rx2, ry2, xo, yo, distT2;
    double ra;
    mx = 0;
    my = 0;
    raysListHead.next = NULL;
    ra = angle;
    if (ra < 0)
        ra -= 2 * pi;
    if (ra > 2 * pi)
        ra -= 2 * pi;
    // check horizontal rays
    int foundSolidWallH = 0;
    dof = 0;
    float disH, hx = player.x, hy = player.y, hx2 = player.x, hy2 = player.y;
    float aTan = -1 / tan(ra);
    if (ra > pi)
    { // looking up
        ry = (((int)player.y >> 6) << 6) - 0.0001;
        rx = (player.y - ry) * aTan + player.x;
        yo = -BLOCK_SIZE;
        xo = -yo * aTan;
    }
    if (ra < pi)
    { // looking down
        ry = (((int)player.y >> 6) << 6) + BLOCK_SIZE;
        rx = (player.y - ry) * aTan + player.x;
        yo = BLOCK_SIZE;
        xo = -yo * aTan;
    }
    if (ra == pi)
    {
        ry = player.y;
        rx = player.x;
        dof = DOF;
    }
    while (dof < DOF)
    {
        mx = (int)rx >> 6;
        my = (int)ry >> 6;
        if (mx >= 0 && mx < MAP_WIDTH && my >= 0 && my < MAP_HEIGHT)
        {
            if (map[my][mx] == 1)
            {
                hx = rx;
                hy = ry;
                disH = sqrt((rx - player.x) * (rx - player.x) + (ry - player.y) * (ry - player.y));
                dof = DOF;
                foundSolidWallH = 1;
            }
            else
            {
                hx2 = rx;
                hy2 = ry;
                dof++;
                rx += xo;
                ry += yo;
            }
        }
        else
        {
            rx += xo;
            ry += yo;
            dof++;
        }
    }

    // check vertical rays
    dof = 0;
    float disV = 100000, disV2 = 100000, vx = player.x, vy = player.y, vx2, vy2;
    float nTan = -tan(ra);
    if (ra > pi / 2 && ra < 3 * pi / 2)
    { // looking left
        rx = (((int)player.x >> 6) << 6) - 0.0001;
        ry = player.y + (player.x - rx) * nTan;
        xo = -BLOCK_SIZE;
        yo = -xo * nTan;
    }
    if (ra < pi / 2 || ra > 3 * pi / 2)
    { // looking right
        rx = (((int)player.x >> 6) << 6) + BLOCK_SIZE;
        ry = player.y + (player.x - rx) * nTan;
        xo = BLOCK_SIZE;
        yo = -xo * nTan;
    }
    if (ra == pi || ra == 0)
    {
        ry = player.y;
        rx = player.x;
        dof = DOF;
    }
    int foundSolidWallV = 0;
    int foundTransparentWallV = 0;
    while (dof < DOF)
    {
        mx = (int)rx >> 6;
        my = (int)ry >> 6;
        if (mx >= 0 && mx < MAP_WIDTH && my >= 0 && my < MAP_HEIGHT && map[my][mx])
        {
            if (map[my][mx] == 1)
            {
                vx = rx;
                vy = ry;
                disV = sqrt((rx - player.x) * (rx - player.x) + (ry - player.y) * (ry - player.y));
                foundSolidWallV = 1;
                dof = DOF;
            }
            else
            {
                vx2 = rx;
                vy2 = ry;
                disV2 = sqrt((rx - player.x) * (rx - player.x) + (ry - player.y) * (ry - player.y));
                foundTransparentWallV = 1;
                dof++;
                rx += xo;
                ry += yo;
            }
        }
        else
        {
            rx += xo;
            ry += yo;
            dof++;
        }
    }

    if (foundTransparentWallV)
    {
        if (disH < disV2)
        {
            rx = hx2;
            ry = hy2;
            distT2 = disV2;
        }
        else
        {
            rx = vx2;
            ry = vy2;
        }
        if (foundSolidWallV)
        {
            if (disH < disV)
            {
                rx2 = hx;
                ry2 = hy;
                distT2 = disH;
            }
            else
            {
                rx2 = vx;
                ry2 = vy;
                distT2 = disV;
            }
        }
        if (foundSolidWallH)
        {
            if (disH < disV)
            {
                rx2 = hx;
                ry2 = hy;
                distT2 = disH;
            }
            else
            {
                rx2 = vx;
                ry2 = vy;
                distT2 = disV;
            }
        }
    }
    else
    {
        if (disH < disV)
        {
            rx = hx;
            ry = hy;
        }
        else
        {
            rx = vx;
            ry = vy;
        }
    }

    *returnXWall = (int)rx2;
    *returnYWall = (int)ry2;
    *distanceWall = distT2;

    *returnXNet = (int)rx;
    *returnYNet = (int)ry2;
    *distanceNet = (int)distT2;
}

void castRays(int map[][MAP_WIDTH])
{
    // ray casting variables
    float htexture, htexture2;
    int r, mx, my, dof;
    double rx, ry, rx2, ry2, xo, yo, distT, distT2;
    double ra;
    mx = 0;
    my = 0;
    resetRayList();
    freeRayInfoList(&raysListHead);
    raysListHead.next = NULL;
    ra = player.angle - DR * FOV_ANGLE / 4;
    if (ra < 0)
        ra -= 2 * pi;
    if (ra > 2 * pi)
        ra -= 2 * pi;
    for (r = 0; r < NB_RAYS; r++)
    {
        // check horizontal rays
        int foundSolidWallH = 0;
        dof = 0;
        float disH = 100000, disH2 = 100000, hx = player.x, hy = player.y, hx2 = player.x, hy2 = player.y;
        float aTan = -1 / tan(ra);
        if (ra > pi)
        { // looking up
            ry = (((int)player.y >> 6) << 6) - 0.0001;
            rx = (player.y - ry) * aTan + player.x;
            yo = -BLOCK_SIZE;
            xo = -yo * aTan;
        }
        if (ra < pi)
        { // looking down
            ry = (((int)player.y >> 6) << 6) + BLOCK_SIZE;
            rx = (player.y - ry) * aTan + player.x;
            yo = BLOCK_SIZE;
            xo = -yo * aTan;
        }
        if (ra == pi)
        {
            ry = player.y;
            rx = player.x;
            dof = DOF;
        }
        while (dof < DOF)
        {
            mx = (int)rx >> 6;
            my = (int)ry >> 6;
            if (mx >= 0 && mx < MAP_WIDTH && my >= 0 && my < MAP_HEIGHT)
            {
                if (map[my][mx] == 1)
                {
                    hx = rx;
                    hy = ry;
                    disH = sqrt((rx - player.x) * (rx - player.x) + (ry - player.y) * (ry - player.y));
                    dof = DOF;
                    foundSolidWallH = 1;
                }
                else
                {
                    hx2 = rx;
                    hy2 = ry;
                    disH2 = sqrt((rx - player.x) * (rx - player.x) + (ry - player.y) * (ry - player.y));
                    dof++;
                    rx += xo;
                    ry += yo;
                }
            }
            else
            {
                rx += xo;
                ry += yo;
                dof++;
            }
        }

        // check vertical rays
        dof = 0;
        float disV = 100000, disV2 = 100000, vx = player.x, vy = player.y, vx2, vy2;
        float nTan = -tan(ra);
        if (ra > pi / 2 && ra < 3 * pi / 2)
        { // looking left
            rx = (((int)player.x >> 6) << 6) - 0.0001;
            ry = player.y + (player.x - rx) * nTan;
            xo = -BLOCK_SIZE;
            yo = -xo * nTan;
        }
        if (ra < pi / 2 || ra > 3 * pi / 2)
        { // looking right
            rx = (((int)player.x >> 6) << 6) + BLOCK_SIZE;
            ry = player.y + (player.x - rx) * nTan;
            xo = BLOCK_SIZE;
            yo = -xo * nTan;
        }
        if (ra == pi || ra == 0)
        {
            ry = player.y;
            rx = player.x;
            dof = DOF;
        }
        int foundSolidWallV = 0;
        int foundTransparentWallV = 0;
        while (dof < DOF)
        {
            mx = (int)rx >> 6;
            my = (int)ry >> 6;
            if (mx >= 0 && mx < MAP_WIDTH && my >= 0 && my < MAP_HEIGHT && map[my][mx])
            {
                if (map[my][mx] == 1)
                {
                    vx = rx;
                    vy = ry;
                    disV = sqrt((rx - player.x) * (rx - player.x) + (ry - player.y) * (ry - player.y));
                    foundSolidWallV = 1;
                    dof = DOF;
                }
                else
                {
                    vx2 = rx;
                    vy2 = ry;
                    disV2 = sqrt((rx - player.x) * (rx - player.x) + (ry - player.y) * (ry - player.y));
                    foundTransparentWallV = 1;
                    dof++;
                    rx += xo;
                    ry += yo;
                }
            }
            else
            {
                rx += xo;
                ry += yo;
                dof++;
            }
        }

        int direction, direction2;

        if (foundTransparentWallV)
        {
            if (disH < disV2)
            {
                rx = hx2;
                ry = hy2;
                distT = disH2;
                direction = 0;
                htexture = (int)(rx) % BLOCK_SIZE;
            }
            else
            {
                rx = vx2;
                ry = vy2;
                distT = disV2;
                direction = 1;
                htexture = (int)(ry) % BLOCK_SIZE;
            }
            if (foundSolidWallV)
            {
                if (disH < disV)
                {
                    rx2 = hx;
                    ry2 = hy;
                    distT2 = disH;
                    direction2 = 0;
                    htexture2 = (int)(rx2) % BLOCK_SIZE;
                }
                else
                {
                    rx2 = vx;
                    ry2 = vy;
                    distT2 = disV;
                    direction2 = 1;
                    htexture2 = (int)(ry2) % BLOCK_SIZE;
                }
            }
            if (foundSolidWallH)
            {
                if (disH < disV)
                {
                    rx2 = hx;
                    ry2 = hy;
                    distT2 = disH;
                    direction2 = 0;
                    htexture2 = (int)(rx2) % BLOCK_SIZE;
                }
                else
                {
                    rx2 = vx;
                    ry2 = vy;
                    distT2 = disV;
                    direction2 = 1;
                    htexture2 = (int)(ry2) % BLOCK_SIZE;
                }
            }
        }
        else
        {
            if (disH < disV)
            {
                rx = hx;
                ry = hy;
                distT = disH;
                direction = 0;
                htexture = (int)(rx) % BLOCK_SIZE;
            }
            else
            {
                rx = vx;
                ry = vy;
                distT = disV;
                direction = 1;
                htexture = (int)(ry) % BLOCK_SIZE;
            }
        }

        ra = ra + ANGLE_INC / 2;
        if (ra > 2 * pi)
            ra -= 2 * pi;
        if (ra < 0)
            ra += 2 * pi;

        // draw ray
        rayInfo_t *column = allocRayInfo(ra, distT, r, foundTransparentWallV, direction, htexture, rx, ry);
        addRayInfoToList(&raysListHead, column);
        if (foundTransparentWallV)
        {
            if (foundSolidWallV)
            {
                rayInfo_t *column = allocRayInfo(ra, distT2, r, 0, direction2, htexture2, rx2, ry2);
                addRayInfoToList(&raysListHead, column);
            }
            else
            {
                rayInfo_t *column = allocRayInfo(ra, distT2, r, 0, direction, htexture2, rx2, ry2);
                addRayInfoToList(&raysListHead, column);
            }
        }

        addRayToList(rx, ry);
        addRayToList(rx2, ry2);
    }
}

void drawEnnemy()
{
    float ennemyAngle = atan2((ennemy.y + ennemy.w / 2) - (player.y + player.w / 2), (ennemy.x + ennemy.w / 2) - (player.x + player.w / 2));
    float ennemyDistance = sqrt((ennemy.x - player.x) * (ennemy.x - player.x) + (ennemy.y - player.y) * (ennemy.y - player.y)) * BLOCK_SIZE;
    int ennemyWidth = 50;
    int ennemyHeight = 200;
    float angleMin = player.angle - (FOV_ANGLE * DR) / 2;
    float angleMax = player.angle + (FOV_ANGLE * DR) / 2;
    if (angleMin < 0)
    {
        angleMin += 2 * pi;
        angleMax += 2 * pi;
    }
    if (angleMax > 2 * pi)
    {
        angleMax -= 2 * pi;
        angleMin -= 2 * pi;
    }
    if (angleMax > 0 && angleMin > 0 && ennemyAngle < 0)
    {
        ennemyAngle += 2 * pi;
    }

    if (ennemyAngle >= angleMin && ennemyAngle <= angleMax)
    {
        rect.x = screenDimension.w / 2 + (screenDimension.w * tan(ennemyAngle - player.angle)) * sqrt(3) * 0.5;
        rect.w = (ennemyWidth * screenDimension.w) / (ennemyDistance / BLOCK_SIZE);
        rect.h = (ennemyHeight * screenDimension.h) / (ennemyDistance / BLOCK_SIZE);
        rect.y = (screenDimension.h / 2 + player.viewAngle) - rect.h / 5;

        destRect.x = 0;
        destRect.y = 0;
        destRect.w = 64;
        destRect.h = 64;
        float angleSum = ennemyAngle + player.angle;
        if (angleSum > 2 * pi)
            angleSum -= 2 * pi;
        if (angleSum < 0)
            angleSum += 2 * pi;

        if (angleSum > 5 * pi / 3 && angleSum <= pi / 3)
        {
            destRect.x = 2 * destRect.w;
        }
        else if (angleSum > pi / 3 && angleSum <= 2 * pi / 3)
        {
            destRect.x = 3 * destRect.w;
        }
        else if (angleSum > 2 * pi / 3 && angleSum <= 4 * pi / 3)
        {
            destRect.x = 0 * destRect.w;
        }
        else if (angleSum > 4 * pi / 3 && angleSum <= 5 * pi / 3)
        {
            destRect.x = 1 * destRect.w;
        }
        SDL_RenderCopy(renderer, playerTexture, &destRect, &rect);
    }
}

int isAngleInRange(float angle, float min, float max)
{
    return ((angle >= min && angle <= max)) || ((angle >= max && angle <= min));
}

void drawBall()
{
    float ballAngle = atan2(ball.y - player.y, ball.x - player.x);
    float ballDistance = sqrt((ball.x - player.x) * (ball.x - player.x) + (ball.y - player.y) * (ball.y - player.y)) * BLOCK_SIZE;
    float ballDistanceX = ballDistance * cos(ballAngle - player.angle);
    float ballViewAngle = atan2(ball.z * BLOCK_SIZE, ballDistanceX);
    int ballWidth = 25;
    int ballHeight = 25;
    float angleMin = player.angle - (FOV_ANGLE * DR) / 2;
    float angleMax = player.angle + (FOV_ANGLE * DR) / 2;
    if (angleMin < 0)
    {
        angleMin += 2 * pi;
        angleMax += 2 * pi;
    }
    if (angleMax > 2 * pi)
    {
        angleMax -= 2 * pi;
        angleMin -= 2 * pi;
    }
    if (angleMax > 0 && angleMin > 0 && ballAngle < 0)
    {
        ballAngle += 2 * pi;
    }

    if (ballAngle >= angleMin && ballAngle <= angleMax)
    {
        rect.x = screenDimension.w / 2 + (screenDimension.w * tan(ballAngle - player.angle)) * sqrt(3) * 0.5;
        rect.w = (ballWidth * screenDimension.w) / (2 * ballDistance / BLOCK_SIZE);
        rect.h = (ballHeight * screenDimension.h) / (2 * ballDistance / BLOCK_SIZE);
        rect.y = (3 * screenDimension.h / 4 + player.viewAngle) - 2 * tan(ballViewAngle) * ballDistance;

        destRect.x = 32 * (SDL_GetTicks() / 150 % 4);
        destRect.y = 0;
        destRect.w = 32;
        destRect.h = 32;

        SDL_RenderCopy(renderer, ballTexture, &destRect, &rect);
    }
}

void drawSkyAndGround()
{
    destRect.x = ((int)((player.angle + pi) * RD + player.x / BLOCK_SIZE));
    destRect.y = 0;
    destRect.w = 100;
    destRect.h = 128 / 2;

    rect.x = 0;
    rect.y = screenDimension.h / 2 + player.viewAngle;
    rect.h = screenDimension.h - rect.y;
    rect.w = screenDimension.w;
    SDL_RenderCopy(renderer, groundTexture, &destRect, &rect);

    sky.x = 0;
    sky.y = 0;
    sky.w = screenDimension.w;
    sky.h = screenDimension.h / 2 + player.viewAngle;

    SDL_RenderCopy(renderer, skyTexture, &destRect, &sky);

    SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
}

void drawRacket()
{
    // todo
}

void drawMap2D(int map[][MAP_WIDTH])
{
    int i, j;
    rect.w = CELL_SIZE;
    rect.h = CELL_SIZE;
    rect.x = 0;
    rect.y = 0;
    // draw ray
    SDL_SetRenderDrawColor(renderer, 255, 255, 0, 255);
    for (i = 0; i < raysListLength; i++)
    {
        SDL_RenderDrawLine(renderer, player.x * CELL_SIZE / BLOCK_SIZE, player.y * CELL_SIZE / BLOCK_SIZE, rays[i][0] * CELL_SIZE / BLOCK_SIZE, rays[i][1] * CELL_SIZE / BLOCK_SIZE);
    }
    // draw map
    for (i = 0; i < MAP_HEIGHT; i++)
    {
        for (j = 0; j < MAP_WIDTH; j++)
        {
            switch (map[i][j])
            {
            // bords du terrain
            case 1:
                SDL_SetRenderDrawColor(renderer, 5, 255, 255, 255);
                SDL_RenderFillRect(renderer, &rect);
                break;
            // filet du milieu
            case 2:
                SDL_SetRenderDrawColor(renderer, 255, 255, 255, 100);
                SDL_RenderFillRect(renderer, &rect);
                break;

            // extremites du filet gauche et droit
            case 3:
                SDL_SetRenderDrawColor(renderer, 255, 255, 255, 100);
                SDL_RenderFillRect(renderer, &rect);
                break;
            case 4:
                SDL_SetRenderDrawColor(renderer, 255, 255, 255, 100);
                SDL_RenderFillRect(renderer, &rect);
                break;
            }
            rect.x += CELL_SIZE;
        }
        rect.y += CELL_SIZE;
        rect.x = 0;
    }

    // draw player
    SDL_SetRenderDrawColor(renderer, 0, 0, 255, 255);
    rect.x = (player.x * CELL_SIZE) / BLOCK_SIZE - CELL_SIZE / 2;
    rect.y = (player.y * CELL_SIZE) / BLOCK_SIZE - CELL_SIZE / 2;
    SDL_RenderFillRect(renderer, &rect);

    // draw ennemi
    SDL_SetRenderDrawColor(renderer, 255, 0, 0, 255);
    rect.x = (ennemy.x * CELL_SIZE) / BLOCK_SIZE - CELL_SIZE / 2;
    rect.y = (ennemy.y * CELL_SIZE) / BLOCK_SIZE - CELL_SIZE / 2;
    SDL_RenderFillRect(renderer, &rect);

    // draw landing point
    if (landingPointPlayerIsFind == 1)
    {
        SDL_SetRenderDrawColor(renderer, 255, 0, 0, 255);
        rect.x = landingPointPlayerX * CELL_SIZE;
        rect.y = CELL_SIZE;
        rect.h = (MAP_HEIGHT - 2) * CELL_SIZE;
        rect.w = 3;
        SDL_RenderFillRect(renderer, &rect);
        // reset taille cellule
        rect.h = CELL_SIZE;
        rect.w = CELL_SIZE;
    }
    if (landingPointEnnemyIsFind == 1)
    {
        SDL_SetRenderDrawColor(renderer, 255, 0, 0, 255);
        rect.x = landingPointEnnemyX * CELL_SIZE;
        rect.y = CELL_SIZE;
        rect.h = (MAP_HEIGHT - 2) * CELL_SIZE;
        rect.w = 3;
        SDL_RenderFillRect(renderer, &rect);
        // reset taille cellule
        rect.h = CELL_SIZE;
        rect.w = CELL_SIZE;
    }

    // draw ball
    SDL_SetRenderDrawColor(renderer, 0, 255, 0, 255);
    rect.x = (ball.x * CELL_SIZE) / BLOCK_SIZE - CELL_SIZE / 2;
    rect.y = (ball.y * CELL_SIZE) / BLOCK_SIZE - CELL_SIZE / 2;
    SDL_RenderFillRect(renderer, &rect);

    SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
}

void drawString(char *str, int x, int y, int w, int h, int r, int g, int b, int a)
{
    SDL_Color color = {r, g, b, a};
    SDL_Surface *text = TTF_RenderText_Solid(RobotoFont, str, color);
    SDL_Texture *texture = SDL_CreateTextureFromSurface(renderer, text);
    rect.x = x;
    rect.y = y;
    rect.w = w;
    rect.h = h;
    SDL_RenderCopy(renderer, texture, NULL, &rect);
    SDL_FreeSurface(text);
    SDL_DestroyTexture(texture);
}

void drawHitIntensity(){
    SDL_SetRenderDrawColor(renderer, 255, 0, 0, 255);
    rect.w = screenDimension.w/20;
    rect.x = screenDimension.w - rect.w;
    //printf("%f\n", player.hitIntensityTimer);
    rect.h = 2 * screenDimension.h/2 * ( player.hitIntensityTimer/1000);
    rect.y = 3 * screenDimension.h/4 - rect.h;
    SDL_RenderFillRect(renderer, &rect);
}

void drawFPS()
{
    char str[10];
    sprintf(str, "%d", (int)fps);
    drawString(str, screenDimension.w - 50, 0, 50, 50, 255, 255, 255, 255);
}

void drawInfosPlayer()
{
    char str_x_player[10];
    char str_y_player[10];
    char str_z_player[10];
    char str_playerX[20] = "playerX:";
    char str_playerY[20] = "playerY:";
    char str_playerZ[20] = "playerZ:";

    sprintf(str_x_player, "%d", (int)player.x);
    strcat(str_playerX, str_x_player);
    sprintf(str_y_player, "%d", (int)player.y);
    strcat(str_playerY, str_y_player);
    sprintf(str_z_player, "%d", (int)player.z);
    strcat(str_playerZ, str_z_player);

    drawString(str_playerX, screenDimension.w - 120, 50, 100, 50, 255, 255, 255, 255);
    drawString(str_playerY, screenDimension.w - 120, 100, 100, 50, 255, 255, 255, 255);
    drawString(str_playerZ, screenDimension.w - 120, 150, 100, 50, 255, 255, 255, 255);
}

void drawInfosBall()
{
    char str_x_ball[10];
    char str_y_ball[10];
    char str_z_ball[10];
    char str_ballX[20] = "ballX/BS:";
    char str_ballY[20] = "ballY:";
    char str_ballZ[20] = "ballZ:";

    sprintf(str_x_ball, "%d", (int)ball.x / BLOCK_SIZE);
    strcat(str_ballX, str_x_ball);
    sprintf(str_y_ball, "%d", (int)ball.y);
    strcat(str_ballY, str_y_ball);
    sprintf(str_z_ball, "%d", (int)ball.z);
    strcat(str_ballZ, str_z_ball);

    drawString(str_ballX, screenDimension.w - 120, 200, 100, 50, 255, 255, 255, 255);
    drawString(str_ballY, screenDimension.w - 120, 250, 100, 50, 255, 255, 255, 255);
    drawString(str_ballZ, screenDimension.w - 120, 300, 100, 50, 255, 255, 255, 255);
}

void drawHub()
{
    drawInfosPlayer();
    drawInfosBall();
}

void drawGame()
{
    SDL_RenderClear(renderer);
    drawSkyAndGround();
    castRays(map);
    if (ball.x < MAP_WIDTH * BLOCK_SIZE / 2)
    {
        drawVerticalWalls();
        drawHorizentalWalls();
        drawEnnemy();
        drawVerticalNet();
        drawBall();
    }
    else
    {
        drawVerticalWalls();
        drawHorizentalWalls();
        drawEnnemy();
        // todo bonus : draw point de chute de la balle
        drawBall();
        drawVerticalNet();
    }
    drawMap2D(map);
    drawRacket();
    if (player.isHoldingClick){

        drawHitIntensity();
    }
    drawFPS();
    // affiche le hub
    if (showHub)
    {
        drawHub();
    }

    SDL_RenderPresent(renderer);
}

void mainLoop()
{
    createWindow();
    initRays();

    netTexture = loadTexture("Res/net.png");
    crowdTexture = loadTexture("Res/crowd.png");
    playerTexture = loadTexture("Res/player_sprite.png");
    ballTexture = loadTexture("Res/ball_sprite.png");
    netEdgeLeftTexture = loadTexture("Res/netLeft.png");
    netEdgeRightTexture = loadTexture("Res/netRight.png");
    skyTexture = loadTexture("Res/sky.png");
    groundTexture = loadTexture("Res/ground.png");
    racketTexture = loadTexture("Res/racket.png");

    ray1 = malloc(sizeof(int) * 2);
    ray2 = malloc(sizeof(int) * 2);

    unsigned int a = SDL_GetTicks();
    unsigned int b = SDL_GetTicks();
    double delta = 0;

    pthread_t eventThread;
    if (pthread_create(&eventThread, NULL, EventLoop, NULL) != 0)
    {
        printf("Couldn't create thread.");
        exit(EXIT_FAILURE);
    }

    while (running)
    {
        a = SDL_GetTicks();
        delta = (a - b);
        if (delta > 1000 / FPS_TO_GET)
        {
            fps = 1000 / delta;
            timer += delta;
            b = a;
            switch (game_state)
            {
            case MENU:
                // Menu();
                break;
            case GAME:
                drawGame();
                managePlayer();
                manageEnnemy();
                updateBall();
                break;
            }
        }
        else
        {
            // fait dormir le thread pour garder des ressources
            usleep(1000 * (1000 / FPS_TO_GET - delta));
        }
    }
    endSDL();
}