
#include "Projectile.h"


#define BULLET_PROJECTILE_WIDTH_ 2
#define BULLET_PROJECTILE_HEIGHT_ 4

void initProjectile(Projectile* projectile, u8 x, u8 y, u8 direction)
{
    projectile->health = 1;
    projectile->object.position.x = x;
    projectile->object.position.y = y;
    projectile->object.previousPosition.x = x;
    projectile->object.previousPosition.y = y;
    projectile->object.width = BULLET_PROJECTILE_WIDTH_;
    projectile->object.height = BULLET_PROJECTILE_HEIGHT_;
    projectile->redraw = TRUE;
    projectile->object.direction = direction;

    if (direction == DIRECTION_RIGHT)
    {
        projectile->object.velocity.x = 1;

        // Adding the player width + 1 so the player cannot move on top of the projectile
        // causing graphics issues on the screen.
        projectile->object.position.x += TILE_WIDTH_BYTES + 1;
    }
    else
    {
        projectile->object.velocity.x = -1;

        // Subtracting the projectile width + 1 so the player cannot move on top of the projectile
        // causing graphics issues on the screen.
        projectile->object.position.x -= (BULLET_PROJECTILE_WIDTH_ + 1);
    }

    // Copy the content of the background where the projectile will be drawn to save for later.
    copyBackground(projectile);
}

void drawProjectile(Projectile* projectile)
{
    // Do not draw the projectile if health is zero (it has been destroyed).
    if (projectile->health && projectile->redraw)
    {
        // Draw over the position the projectile previously was with the saved background sprite.
        u8* memory = cpct_getScreenPtr(CPCT_VMEM_START, projectile->object.previousPosition.x, projectile->object.previousPosition.y);
        cpct_drawSprite(projectile->backgroundSprite, memory, projectile->object.width, projectile->object.height);

        // Copy the content of the background where the projectile sprite currently is (before it is drawn) for later redraw when the projectile moves.
        copyBackground(projectile);

        // Draw a square to represent the projectile at the current position.
        memory = cpct_getScreenPtr(CPCT_VMEM_START, projectile->object.position.x, projectile->object.position.y);
        cpct_drawSolidBox(memory, cpct_px2byteM0(3, 3), projectile->object.width, projectile->object.height);

        projectile->redraw = FALSE;
    }
}

void logicProjectile(Projectile* projectile)
{
    // Do not update the projectile if health is zero (it has been destroyed).
    if (projectile->health)
    {
        u8 cellX;
        u8 cellY;
        u8 i = 0;
        Enemy* enemy = mapEnemies;
        u8 cell;

        // Update the previous position before updating the current position with the velocity.
        projectile->object.previousPosition.x = projectile->object.position.x;
        projectile->object.previousPosition.y = projectile->object.position.y;

        // Calculate new projectile position from velocity.
        projectile->object.position.y += projectile->object.velocity.y;
        projectile->object.position.x += projectile->object.velocity.x;

        // The cells that the projectile is occupying.
        cellX = projectile->object.position.x / TILE_WIDTH_BYTES;
        cellY = projectile->object.position.y / TILE_HEIGHT;
        cell = MAPA(cellX, cellY);

        projectile->redraw = TRUE;

        if (cell & 96)
        {
            // Destroy the projectile if it runs into an item or interactable.
            destroyProjectile(projectile);
        }
        else if (cell & 31 && !(cell & CELL_WATER_FLAG))
        {
            // Destroy the projectile if it runs into a tile but not water.
            destroyProjectile(projectile);
        }
        else if (cellX >= SCREEN_TILES_X)
        {
            // Destroy the tile if it goes off screen.
            destroyProjectile(projectile);
        }

        for (i = 0; i < mapEnemyCount; ++i)
        {
            // Test if the enemy (make sure its in the same room) is colliding with the projectile (based on current position).
            if (enemy->object.position.room == currentRoom && testObjectIntersectionBVelocity(&projectile->object, &enemy->object))
            {
                // Destroy the projectile when the projectile collides with an enemy.
                destroyProjectile(projectile);

                // Damage the enemy which will destroy the enemy if is 0 (destroying the enemy will redraw the nearby cells).
                damageEnemy(enemy);

                // Redraw hud after updating score.
                hudRedraw = TRUE;

                break;
            }

            ++enemy;
        }
    }
}

void destroyProjectile(Projectile* projectile)
{
    u8* memory = cpct_getScreenPtr(CPCT_VMEM_START, projectile->object.previousPosition.x, projectile->object.previousPosition.y);

    projectile->health = 0;
    projectile->redraw = FALSE;

    // Draw the projectile where the projectile was previously before it was destroyed.
    cpct_drawSprite(projectile->backgroundSprite, memory, projectile->object.width, projectile->object.height);

    // Start the timer so there is a little delay between a projectile being destroyed and the next one being shot.
    projectileDestroyedTimerCount = 1;
}

void copyBackground(Projectile* projectile)
{
    u8 i = 0;

    // Copy the content of the background where the actor is currently positioned to save for later redrawing (when it moves).
    for (i = 0; i < projectile->object.height; ++i)
    {
        // For some reason I cannot add i*width directly and have to put it in a variable otherwise I get an sdcc error (FATAL Compiler Internal Error).
        u8 temp = i * projectile->object.width;

        cpct_memcpy(projectile->backgroundSprite + temp, cpct_getScreenPtr(CPCT_VMEM_START, projectile->object.position.x, projectile->object.position.y + i), projectile->object.width);
    }
}