//#define FASEDOIS
//#define PULAHIST

/*
 *                      Arkanoid versus Ncurses: ARKURSES
 *
 *      Sinopse: Você esta dentro do personagem de uma barra. Uma barra feita de
 *  caracteres! O mundo esta sendo atacado por terriveis bloquinhos coloridos do
 *  mal! Direcione a bolinha aos blocos e derrote todos eles, salvando o mundo
 *  das garras da guangue dos "MenorIgualMaior".
 *
 *      Regras: Primeiramente, digite o numero de linhas de tropas de bloquinhos
 *  para batalhar (entre 1 e 8). Mova a barra com as setinhas. Tecla '+' ou '='
 *  aumentam a velocidade da bolinha, e '-' a diminui. A bolinha move-se
 *  em 45° ao trombar no meio da barra e em L ao colidir com as pontas '<'/'>'.
 *  O jogo pode ser pausado com a barra de espaço, e pode-se sair a qualquer hora
 *  apertando a tecla 'q'. Clique F2 para opção "novo jogo", e 's' para confirmar;
 *  'n' volta para o jogo e 'q' ainda sai do jogo.
 *
 *  Colisões e inversões de sentido:
 *      em 45°:
 *          p/ Esquerda com canto direito '>': inversão em ambas as direções;
 *          p/ Direita com canto esquerdo '<': inversão em ambas as direções;
 *          p/ Esquerda com resto '='/'<': inverte direção vertical;
 *          p/ Direita com resto '='/'>': inverte direção vertical;
 *      em L:
 *          na horizontal: inverte sentido horizontal;
 *          diagonal em qualquer sentido horizontal, com bloco em geral: inverte direção vertical;
 *
 *  Algumas considerações:
 *      -Compilando no gcc? Não esqueça de adicionar a flag "-lpanel -lncurses" [senão não funfa];
 *      -Sim, esse bagulho roda nos terminais virtuais [tty] [e ate melhor de jogar];
 *      -Esse jogo buga bastante;
 *      -Não existe tal coisa como fisica;
 *      -Não reclame da vida.
 *
 *  Hell, yeah
 *  Gil Barbosa Reis
 *
 */

#include <ncurses.h>
#include <panel.h>
#include <stdlib.h> // para o rand ()/srand ()
#include <time.h>   // time (), pra por no srand ()
#include <ctype.h>
#include <locale.h> // pra chars doidos [ê, ç]

#define BGhelp 10
#define FGshot 4
#define FGboss 3
#define HELP_WIDTH 36

#define LINES 24

#define SHOT_TIME 150   // boss moves for player to get a shot

#define CAMPO_ALTURA 22
#define CAMPO_LARGURA 48
#define CAMPO_y0 (LINES/2 - CAMPO_ALTURA/2)
#define CAMPO_x0 (COLS/2 - CAMPO_LARGURA/2)
#define BLOCO_y0 (CAMPO_y0 + 3)

#define BARRA_y0 (LINES/2 + CAMPO_ALTURA/2 - 2)
#define BARRA_x0 (COLS/2 - 1)

#define BOLA_y0 (BARRA_y0 - 1)
#define BOLA_x0 (COLS/2)

#define FALA_y0 (BARRA_y0 + 5)
#define FALA_x0 CAMPO_x0

void Help ();   // displays the help
void Restart ();    // ask for restarting the game, and does it if needed
void Pause ();  // pause the game
void AtualizaHud ();    // acho que você ja sabe o que faz, ne?

void CriaBlocos (); // inicializadores
void CriaBarra ();  // dos
void CriaBola ();   // graficos
void CriaCampo ();  // do jogo

void MoveBarraEsq ();   // mexe a
void MoveBarraDir ();   // barrinha

void MoveBolinha ();        // movimento
void AndaX (int y, int x);  // da
void AndaL (int y, int x);  // bolinha
void MoveUltimo (); // move o ultimo bloquinho
void MoveChefe ();  // move o chefe da segunda [e ultima] fase

char AlgoNoCaminhoBarra (int y, int x); // vai trombar na barra?
char AlgoNoCaminhoCampo (int y, int x); // ou talvez nos blocos?
void Quebra (char obst, int y, int x);  // se sim, que bom, então quebra la
char AlgoNoCaminhoBola (int y, int x);  // e o ultimo bloco, bate na bola?

void FogoArtificio (int x); // um fogo de artificio [que lindo ^^]
void Morreu ();     // vai que ele morre, ne?
void FaseDois ();   // ou passa do começo?
void Ganhou ();     // ou ate ganha o jogo!
int BateChefe (int y, int x);   // bateu no chefão; porrada, manow!
void AtualizaVidaChefe ();

void Shoot (char*); // todas as funcoes sobre o tiro
void ClickShoot ();

WINDOW *hud, *campo, *barra, *bola, *chefe, *HP_chefe;
int vidas;
int vida_chefe; // vida do chefe: numero de vezes que ainda falta bater nele
int numblocos;  // numero de blocos: 15 vezes o numero de linhas de blocos [dificuldade]

char movimento; // tipo do movimento: X para 45°, L para 31° (anda em L, como no xadrez)
char h_dir; // direção horizontal do movimento: E para esquerda e D para direita
char v_dir; // direção vertical do movimento: C para cima e B para baixo
char l_mov; // movimento em L: alterna entre 'H' para horizontal e 'D' para diagonal

int y_ultim, x_ultim;   // coordenadas do ultimo bloquinho
int y_chefe, x_chefe;   // coordenadas do chefe
char dificuldade;   // numero de linhas de bloquinhos

int s;  // escolhas do teclado
int periodo;    // tempim entre os frames: 10~20
unsigned char tiro; // contagem pra tiro: <SHOT_TIME, conta;        SHOT_TIME, disponivel;      >SHOT_TIME, atirou
int y_tiro, x_tiro; // coordenadas do tiro

int main () {
    int i;

    setlocale (LC_ALL, ""); // para aparecer os chars doidos
    srand (time (NULL));

// inicializações do curses
    initscr (); // inicia o modo curses
    start_color (); // cores ;]
    keypad (stdscr, TRUE); // permite uso de 'F's e setinhas
    init_pair (1, COLOR_WHITE, COLOR_GREEN);    // cor do HUD
    init_pair (2, COLOR_CYAN, COLOR_BLACK);     // outras
    init_pair (3, COLOR_RED, COLOR_BLACK);      // cores;
    init_pair (4, COLOR_YELLOW, COLOR_BLACK);   // para
    init_pair (5, COLOR_BLUE, COLOR_BLACK);     // os
    init_pair (6, COLOR_MAGENTA, COLOR_BLACK);  // bloquinhos
    init_pair (7, COLOR_GREEN, COLOR_BLACK);    //
    init_pair (8, COLOR_WHITE, COLOR_BLACK);    // e uma pra barrinha e pra bolinha [e pra bloco tambem, uai]
    init_pair (9, COLOR_BLACK, COLOR_BLACK);    // e mais uma para os bloquinhos [wow! que tanto!]
    init_pair (BGhelp, COLOR_WHITE, COLOR_BLUE);    // help color
    bkgd (COLOR_PAIR (8));

    hud = subwin (stdscr, 1, 0, 0, 0);          // cria o HUD
    wbkgd (hud, COLOR_PAIR (1));                // com o nome do
    wattron (hud, A_BOLD);                      // jogo e quantas
    mvwaddstr (hud, 0, COLS/2 - 4, "ARKURSES"); // vidas tem e quantos
    wrefresh (hud);

#ifndef FASEDOIS
    mvaddstr (6, 0, "Dificulty [1~8] >");
    do {
        mvscanw (6, 18, "%d", &dificuldade);
    } while (dificuldade < 1 || dificuldade > 8);
    move (6, 0);
    clrtoeol ();
#endif
#ifdef FASEDOIS
    dificuldade = 1;
#endif

    cbreak (); // não espera a tecla 'enter'
    noecho (); // não escreve as teclas apertadas, para interatividades
    curs_set (0); // esconde o cursor

    mvwaddstr (hud, 0, 0, "'?': Help");
    AtualizaHud ();

    int frame;  // frame em que esta: para mover a bolinha e o ultimo bloco com velocidades diferentes
    char fired; // diz se atirou ou nao
    while (s != 'q') {
        CriaCampo ();               // novo jogo:
        CriaBlocos ();
        CriaBarra ();               // cria as coisinhas
        CriaBola ();                // em seu devido lugar
#ifndef FASEDOIS
        vidas = 5;                  // e tambem seu
        numblocos = 15*dificuldade; // valor inicial
#endif
#ifdef FASEDOIS
        numblocos = 2;
        vidas = 15;
#endif
        periodo = 14;
        vida_chefe = -1;    // -1 for "not living yet" [0 is for dead]
        movimento = 'X';
        v_dir = 'C';
        h_dir = 'D';
        l_mov = 'H';

        AtualizaHud ();
        frame = 0;
        tiro = 0;
        fired = 0;
        s = 0;
        nodelay (stdscr, FALSE);    // começa jogo so se
        getch ();                   // clicar alguma coisa
        nodelay (stdscr, TRUE); // não espera o getch(), pra jogar mesmo

        while (s != KEY_F(2) && s != 'q') {
            if ((s = tolower (getch ())))
                flushinp ();

// ultimo bloquinho: falas e ele começa a mexer
            if (numblocos == 1 && frame % 5 == 0)
                MoveUltimo ();

// fase dois [depois de destruir todos os blocos]: chefe loko
            else if (vida_chefe > 0) {
                if (frame % 150 == 0)
                    MoveChefe ();
                if (frame % 5 == 0)
                    Shoot (&fired);
            }

// mexe a bolinha
            if (frame % 7 == 0)
                MoveBolinha ();

            switch (s) {
                case '?':
                    Help ();
                    break;

                case KEY_LEFT: case 'a':
                    MoveBarraEsq ();
                    break;
                case KEY_RIGHT: case 'd':
                    MoveBarraDir ();
                    break;

                // tiro, pro chefe
                case KEY_UP: case 'w':
                    if (tiro > SHOT_TIME && !fired) {
                        ClickShoot ();
                        fired = 1;
                    }
                    break;

// aumenta a velocidade ['=' para quem usa notebook sem teclado numerico e não quer segurar o shift, que nem eu]
                case '+': case '=':
                    if (periodo > 10 && periodo <= 30)
                        periodo -= 2;
                    break;
// diminui a velocidade
                case '-':
                    if (periodo >= 10 && periodo < 30)
                        periodo += 2;
                    break;

// jogador pausou → barra de espaço
                case ' ':
                    Pause ();
                    if (s != KEY_F(2))
                        break;

// em caso de novo jogo [F2] (ou perdeu o jogo, ou ganhou o jogo):
                case KEY_F(2):
                    Restart ();
                    break;
            }
            movimento == 'X' ? napms (periodo) : napms (periodo*0.89);
// proximo frame
            frame++;
        }
    }

    endwin ();
    return 0;
}

/* Displays the help (in a created window and panel, for going back to the normal field after) */
void Help () {
    WINDOW *help;
    PANEL *up;

    help = newwin (8, HELP_WIDTH, 1, 0);
    up = new_panel (help);
    update_panels ();
    doupdate ();

    box (help, 0, 0);
    wbkgd (help, COLOR_PAIR (BGhelp));
    wrefresh (help);
    mvwaddstr (help, 0, HELP_WIDTH/2 - 2, "HELP");
    wattron (help, A_BOLD);
    mvwaddstr (help, 1, 1, "Arrow Keys or A,D:");
    mvwaddstr (help, 2, 1, "'-':");
    mvwaddstr (help, 3, 1, "'+' or '=':");
    mvwaddstr (help, 4, 1, "Space:");
    mvwaddstr (help, 5, 1, "F2:");
    mvwaddstr (help, 6, 1, "Q:");

    wattrset (help, COLOR_PAIR (BGhelp));
    mvwaddstr (help, 1, 20, "move left/right");
    mvwaddstr (help, 2, 6, "slow down");
    mvwaddstr (help, 3, 13, "speed up");
    mvwaddstr (help, 4, 8, "pause");
    mvwaddstr (help, 5, 5, "reset game");
    mvwaddstr (help, 6, 4, "quit");

// writes the help window, wait for some key to be pressed and delete the help window
    wrefresh (help);
    nodelay (stdscr, FALSE);
    getch ();
    nodelay (stdscr, TRUE);

    wbkgd (help, COLOR_PAIR (0));
    werase (help);
    wrefresh (help);
    del_panel (up);
    delwin (help);
}

/* Pause the game, waiting for unpause/quit/reset game */
void Pause () {
    WINDOW *pause = newwin (1, COLS, FALA_y0, 0);
    PANEL *up = new_panel (pause);

    wattron (pause, A_BOLD);
    mvwaddstr (pause, 0, COLS/2 - 2, "PAUSE");

    update_panels ();
    doupdate ();

    nodelay (stdscr, FALSE);
    do {
        s = tolower (getch ());
    } while (s != ' ' && s != 'q' && s != KEY_F(2));
// jogador despausou, ou pediu novo jogo [tai embaixo], ou mandou sair
    nodelay (stdscr, TRUE);
    werase (pause);
    wrefresh (pause);
    del_panel (up);
    delwin (pause);
}

/* Restart the curses windows, for when restarting the game */
void Restart () {
    WINDOW *new = newwin (1, COLS, FALA_y0, 0);
    PANEL *up = new_panel (new);

    wattron (new, A_BOLD);
    mvwaddstr (new, 0, COLS/2 - 9, "NOVO JOGO? (y/n/q)"); // y: sim; n: não; q: sair (quit)

    update_panels ();
    doupdate ();

    nodelay (stdscr, FALSE);
// pega tecla ate uma das opções validas
    do {
        s = tolower (getch ());
    } while (s != 'y' && s != 'n' && s != 'q');
// em caso de fim de jogo (perdendo ou ganhando), escolher 'n' sai do jogo
    if ((vidas == 0 || vida_chefe == 0) && s == 'n')
        s = 'q';
// sim? então exorcisa a bola, barrinha e campo, e volta la refazer o jogo
    else if (s == 'y') {
        werase (bola);
        delwin (bola);
        werase (barra);
        delwin (barra);
        werase (campo);
        delwin (campo);
        werase (HP_chefe);
        delwin (HP_chefe);

        standend ();
        mvaddstr (FALA_y0, COLS/2 - 9, "                  ");
        s = KEY_F(2);
    }
// clear the new game window/panel
    nodelay (stdscr, TRUE);
    werase (new);
    wrefresh (new);
    del_panel (up);
    delwin (new);
}

/* reescreve quantas vidas tem e quantos blocos faltam */
void AtualizaHud () {
    mvwprintw (hud, 0, COLS - 21, "vidas: %d  blocos: %3.d", vidas, numblocos);
    wrefresh (hud);
}

/* Cria o campo, com sua caixinha bonitinha */
void CriaCampo () {
    campo = subwin (stdscr, CAMPO_ALTURA, CAMPO_LARGURA, CAMPO_y0, CAMPO_x0);
    wattron (stdscr, COLOR_PAIR (8));
    box (campo, 0, 0);
}

/* Desenha os blocos na tela, na posição certa, uma linha de cada cor */
void CriaBlocos () {
    int cor = 2, x, y;

    wattron (campo, A_BOLD);
    for (y = 0; y < dificuldade; y++) {
        wattron (campo, COLOR_PAIR (cor));
// desenha os 15 blocos de 3 chars, no formato especificado
        for (x = 0; x < 15; x++)
            mvwaddstr (campo, y + 3, (3*x) + 1, "<=>");
// muda a cor pra proxima linha
        cor++;
    }
    wrefresh (campo);
}

/* Cria a barra, na posição de inicio */
void CriaBarra () {
    barra = subwin (stdscr, 1, 6, BARRA_y0, BARRA_x0);
    mvwaddstr (barra, 0, 0, "<xxxx>");
    wrefresh (barra);
}

/* Cria a bolinha, na posição de inicio */
void CriaBola () {
    bola = subwin (stdscr, 1, 1, BOLA_y0, BOLA_x0);
    mvwaddch (bola, 0, 0, 'O');
    wrefresh (bola);
}

/* Move a barrinha uma casa pra esquerda */
void MoveBarraEsq () {
    int x, y; // coordenadas atuais da barra (contadas a partir do '<')
    getbegyx (barra, y, x);

    if (x > CAMPO_x0 + 1) {
        x--;
// não tromba na bolinha
        if (AlgoNoCaminhoBola (y - CAMPO_y0, x - CAMPO_x0) != 'O') {
            werase (barra);                 // apaga a barra
            wrefresh (barra);
            mvwin (barra, y, x);            // move-a
            mvwaddstr (barra, 0, 0, "<xxxx>");// e a reescreve
            wrefresh (barra);
        }
    }
}

/* Move a barrinha uma casa pra direita */
void MoveBarraDir () {
    int x, y; // coordenadas atuais da barra (contadas a partir do '<')
    getbegyx (barra, y, x);

    if (x < CAMPO_x0 + CAMPO_LARGURA - 7) {
        x++;
// não tromba na bolinha
        if (AlgoNoCaminhoBola (y - CAMPO_y0, x - CAMPO_x0 + 5) != 'O') {
            werase (barra);                 // apaga a barra
            wrefresh (barra);
            mvwin (barra, y, x);            // move-a
            mvwaddstr (barra, 0, 0, "<xxxx>");// e a reescreve
            wrefresh (barra);
        }
    }
}

/* Move a bolinha, levando em consideração o tipo do movimento */
void MoveBolinha () {
    int y, x;   // coordenadas da bolinha

    getbegyx (bola, y, x);  // onde esta a bola?

// ta na linha da barra, morreu
    if (y == BARRA_y0) {
        Morreu ();
// se acabaram todas as vidas, nem mexe a bolinha
        if (vidas == 0)
            return;
        v_dir = 'C';    // e volta a ir pra cima
    }
// inversão de sentido horizontal → lateral esquerda
    if (x == CAMPO_x0 + 1)
            h_dir = 'D';
// lateral direita
    if (x == CAMPO_x0 + CAMPO_LARGURA - 2)
        h_dir = 'E';
// inversão de sentido vertical → teto
    if (y == CAMPO_y0 + 1)
            v_dir = 'B';

    switch (movimento) {
        case 'X': AndaX (y, x); break;
        case 'L': AndaL (y, x); break;
    }
}

/* Anda em 45°: 1×1 */
void AndaX (int y, int x) {
    char obst;

    if (v_dir == 'C')
        switch (h_dir) {
            case 'D':
// proxima posição [se aplica a todos os movimentos]
                y--; x++;

// se estiver na 2ª fase, vê se bate com o chefão, dai não destroi bloquinho
                if (vida_chefe > 0 && BateChefe (y, x))
                    return;

// prevê colisão com bloquinhos [se aplica a todos os movimentos]
                obst = AlgoNoCaminhoCampo (y, x);
                if (obst == '=' || obst == '>') {
                    v_dir = 'B';
                    Quebra (obst, y, x);
                    return;
                }
                else if (obst == '<') {
                    if (AlgoNoCaminhoCampo (y + 1, x) != '<')
                        v_dir = 'B';
                    if (AlgoNoCaminhoCampo (y, x - 1) != '>')
                        h_dir = 'E';
// se bater no cantinho côncavo
                    if (AlgoNoCaminhoCampo (y, x - 1) == '>' && AlgoNoCaminhoCampo (y + 1, x) == '<') {
                        v_dir = 'B';
                        h_dir = 'E';
                        Quebra ('>', y, x - 1);
                        Quebra ('<', y + 1, x);
                        return;
                    }
                    Quebra (obst, y, x);
                    return;
                }

                werase (bola);
                wrefresh (bola);
                mvwin (bola, y, x);
                mvwaddch (bola, 0, 0, 'O');
                wrefresh (bola);
                break;
            case 'E':
                y--; x--;

                if (vida_chefe > 0 && BateChefe (y, x))
                    return;

                obst = AlgoNoCaminhoCampo (y, x);
                if (obst == '=' || obst == '<') {
                    v_dir = 'B';
                    Quebra (obst, y, x);
                    return;
                }
                else if (obst == '>') {
                    if (AlgoNoCaminhoCampo (y + 1, x) != '>')
                        v_dir = 'B';
                    if (AlgoNoCaminhoCampo (y, x + 1) != '<')
                        h_dir = 'D';
                    if (AlgoNoCaminhoCampo (y, x + 1) == '<' && AlgoNoCaminhoCampo (y + 1, x) == '>') {
                        v_dir = 'B';
                        h_dir = 'D';
                        Quebra ('<', y, x + 1);
                        Quebra ('>', y + 1, x);
                        return;
                    }
                    Quebra (obst, y, x);
                    return;
                }

                werase (bola);
                wrefresh (bola);
                mvwin (bola, y, x);
                mvwaddch (bola, 0, 0, 'O');
                wrefresh (bola);
                break;
        }
    else    // v_dir == 'B'
        switch (h_dir) {
            case 'D':
                y++; x++;
// bate ne barra?
                if (y == BARRA_y0) {
                    obst = AlgoNoCaminhoBarra (y, x);
                    if (obst == 'x') {
                        v_dir = 'C';
                        return;
                    }
                    else if (obst == '<') {
                        v_dir = 'C';
                        h_dir = 'E';
                        movimento = 'L';
                        return;
                    }
                    else if (obst == '>') {
                        v_dir = 'C';
                        movimento = 'L';
                        return;
                    }
                }

                if (vida_chefe > 0 && BateChefe (y, x))
                    return;

// bate em bloquinho?
                obst = AlgoNoCaminhoCampo (y, x);
                if (obst == '=' || obst == '>') {
                    v_dir = 'C';
                    Quebra (obst, y, x);
                    return;
                }
                else if (obst == '<') {
                    if (AlgoNoCaminhoCampo (y - 1, x) != '<')
                        v_dir = 'C';
                    if (AlgoNoCaminhoCampo (y, x - 1) != '>')
                        h_dir = 'E';
                    if (AlgoNoCaminhoCampo (y, x - 1) == '>' && AlgoNoCaminhoCampo (y - 1, x) == '<') {
                        v_dir = 'C';
                        h_dir = 'E';
                        Quebra ('>', y, x - 1);
                        Quebra ('<', y - 1, x);
                        return;
                    }
                    Quebra (obst, y, x);
                    return;
                }

                werase (bola);
                wrefresh (bola);
                mvwin (bola, y, x);
                mvwaddch (bola, 0, 0, 'O');
                wrefresh (bola);
                break;
            case 'E':
                y++; x--;
                if (y == BARRA_y0) {
                    obst = AlgoNoCaminhoBarra (y, x);
                    if (obst == 'x') {
                        v_dir = 'C';
                        return;
                    }
                    else if (obst == '<') {
                        v_dir = 'C';
                        movimento = 'L';
                        return;
                    }
                    else if (obst == '>') {
                        v_dir = 'C';
                        h_dir = 'D';
                        movimento = 'L';
                        return;
                    }
                }

                if (vida_chefe > 0 && BateChefe (y, x))
                    return;

                obst = AlgoNoCaminhoCampo (y, x);
                if (obst == '<' || obst == '=') {
                    v_dir = 'C';
                    Quebra (obst, y, x);
                    return;
                }
                else if (obst == '>') {
                    if (AlgoNoCaminhoCampo (y - 1, x) != '>')
                        v_dir = 'C';
                    if (AlgoNoCaminhoCampo (y, x + 1) != '<')
                        h_dir = 'D';
                    if (AlgoNoCaminhoCampo (y, x + 1) == '<' && AlgoNoCaminhoCampo (y - 1, x) == '>') {
                        v_dir = 'C';
                        h_dir = 'D';
                        Quebra ('<', y, x + 1);
                        Quebra ('>', y - 1, x);
                        return;
                    }
                    Quebra (obst, y, x);
                    return;
                }

                werase (bola);
                wrefresh (bola);
                mvwin (bola, y, x);
                mvwaddch (bola, 0, 0, 'O');
                wrefresh (bola);
                break;
        }
}

/* Anda em L: 1×2 → primeiro para o lado, e então diagonal */
void AndaL (int y, int x) {
    char obst;

// primeiro pro lado
    if (l_mov == 'H') {
        switch (h_dir) {
                case 'D':
                    x++;

                    if (vida_chefe > 0 && BateChefe (y, x))
                        return;

                    obst = AlgoNoCaminhoCampo (y, x);
                    if (obst == '<') {
                        h_dir = 'E';
                        Quebra (obst, y, x);
                        return;
                    }

                    werase (bola);
                    wrefresh (bola);
                    mvwin (bola, y, x);
                    mvwaddch (bola, 0, 0, 'O');
                    wrefresh (bola);
                    break;
                case 'E':
                    x--;

                    if (vida_chefe > 0 && BateChefe (y, x))
                        return;

                    obst = AlgoNoCaminhoCampo (y, x);
                    if (obst == '>') {
                        h_dir = 'D';
                        Quebra (obst, y, x);
                        return;
                    }

                    werase (bola);
                    wrefresh (bola);
                    mvwin (bola, y, x);
                    mvwaddch (bola, 0, 0, 'O');
                    wrefresh (bola);
                    break;
        }
        l_mov = 'D';
    }
// e então na diagonal
    else {
        if (v_dir == 'C')
            switch (h_dir) {
                case 'D':
                    y--; x++;

                    if (vida_chefe > 0 && BateChefe (y, x))
                        return;

                    obst = AlgoNoCaminhoCampo (y, x);
                    if (obst == '=' || obst == '>') {
                        v_dir = 'B';
                        Quebra (obst, y, x);
                        return;
                    }
                    else if (obst == '<') {
                        if (AlgoNoCaminhoCampo (y + 1, x) != '<')
                            v_dir = 'B';
                        else
                            h_dir = 'E';
                        if (AlgoNoCaminhoCampo (y, x - 1) == '>' && AlgoNoCaminhoCampo (y + 1, x) == '<') {
                            v_dir = 'B';
                            h_dir = 'E';
                            Quebra ('>', y, x - 1);
                            Quebra ('<', y + 1, x);
                            return;
                        }
                        Quebra (obst, y, x);
                        return;
                    }

                    werase (bola);
                    wrefresh (bola);
                    mvwin (bola, y, x);
                    mvwaddch (bola, 0, 0, 'O');
                    wrefresh (bola);
                    break;
                case 'E':
                    y--; x--;

                    if (vida_chefe > 0 && BateChefe (y, x))
                        return;

                    obst = AlgoNoCaminhoCampo (y, x);
                    if (obst == '<' || obst == '=') {
                        v_dir = 'B';
                        Quebra (obst, y, x);
                        return;
                    }
                    else if (obst == '>') {
                        if (AlgoNoCaminhoCampo (y + 1, x) != '>')
                            v_dir = 'B';
                        else
                            h_dir = 'D';
                        if (AlgoNoCaminhoCampo (y, x + 1) == '<' && AlgoNoCaminhoCampo (y + 1, x) == '>') {
                            v_dir = 'B';
                            h_dir = 'D';
                            Quebra ('<', y, x + 1);
                            Quebra ('>', y + 1, x);
                            return;
                        }
                        Quebra (obst, y, x);
                        return;
                    }

                    werase (bola);
                    wrefresh (bola);
                    mvwin (bola, y, x);
                    mvwaddch (bola, 0, 0, 'O');
                    wrefresh (bola);
                    break;
            }
        else    // v_dir == 'B'
            switch (h_dir) {
                case 'D':
                    y++; x++;

                    if (y == BARRA_y0) {
                        obst = AlgoNoCaminhoBarra (y, x);
                        if (obst == 'x') {
                            v_dir = 'C';
                            movimento = 'X';
                            return;
                        }
                        else if (obst == '<') {
                            v_dir = 'C';
                            h_dir = 'E';
                            return;
                        }
                        else if (obst == '>') {
                            v_dir = 'C';
                            return;
                        }
                    }

                    if (vida_chefe > 0 && BateChefe (y, x))
                        return;

                    obst = AlgoNoCaminhoCampo (y, x);
                    if (obst == '=' || obst == '>') {
                        v_dir = 'C';
                        Quebra (obst, y, x);
                        return;
                    }
                    else if (obst == '<') {
                        if (AlgoNoCaminhoCampo (y - 1, x) != '<')
                            v_dir = 'C';
                        else
                            h_dir = 'E';
                        if (AlgoNoCaminhoCampo (y, x - 1) == '>' && AlgoNoCaminhoCampo (y - 1, x) == '<') {
                            v_dir = 'C';
                            h_dir = 'E';
                            Quebra ('>', y, x - 1);
                            Quebra ('<', y - 1, x);
                            return;
                        }
                        Quebra (obst, y, x);
                        return;
                    }

                    werase (bola);
                    wrefresh (bola);
                    mvwin (bola, y, x);
                    mvwaddch (bola, 0, 0, 'O');
                    wrefresh (bola);
                    break;
                case 'E':
                    y++; x--;

                    if (y == BARRA_y0) {
                        obst = AlgoNoCaminhoBarra (y, x);
                        if (obst == 'x') {
                            v_dir = 'C';
                            movimento = 'X';
                            return;
                        }
                        else if (obst == '<') {
                            v_dir = 'C';
                            return;
                        }
                        else if (obst == '>') {
                            v_dir = 'C';
                            h_dir = 'D';
                            return;
                        }
                    }

                    if (vida_chefe > 0 && BateChefe (y, x))
                        return;

                    obst = AlgoNoCaminhoCampo (y, x);
                    if (obst == '<' || obst == '=') {
                        v_dir = 'C';
                        Quebra (obst, y, x);
                    return;
                    }
                    else if (obst == '>') {
                        if (AlgoNoCaminhoCampo (y - 1, x) != '>')
                            v_dir = 'C';
                        else
                            h_dir = 'D';
                        if (AlgoNoCaminhoCampo (y, x + 1) == '<' && AlgoNoCaminhoCampo (y - 1, x) == '>') {
                            v_dir = 'C';
                            h_dir = 'D';
                            Quebra ('<', y, x + 1);
                            Quebra ('>', y - 1, x);
                            return;
                        }
                        Quebra (obst, y, x);
                        return;
                    }

                    werase (bola);
                    wrefresh (bola);
                    mvwin (bola, y, x);
                    mvwaddch (bola, 0, 0, 'O');
                    wrefresh (bola);
                    break;
            }
        l_mov = 'H';
    }
}

/* Move o ultimo bloquinho, quando so faltar ele */
void MoveUltimo () {
    static char lado_ultim='D'; // direção horizontal do movimento do ultimo bloquinho: E para esquerda e D para direita

// começo do campo, agora vai pra direita
    if (x_ultim == 1)
        lado_ultim = 'D';
// fim do campo, agora vai pra esquerda
    if (x_ultim == CAMPO_LARGURA - 4)
        lado_ultim = 'E';

// indo pra direita
    switch (lado_ultim) {
        case 'D':
// se o bloco trombar na bola, ele quebra, ne
            if (AlgoNoCaminhoBola (y_ultim, x_ultim + 3) == 'O') {
                Quebra ('<', y_ultim + CAMPO_y0, x_ultim + CAMPO_x0);
                return;
            }
            mvwaddstr (campo, y_ultim, x_ultim, " <=>");
            wrefresh (campo);
            x_ultim++;
            break;
// indo pra esquerda
        case 'E':
            if (AlgoNoCaminhoBola (y_ultim, x_ultim - 1) == 'O') {
                Quebra ('<', y_ultim + CAMPO_y0, x_ultim + CAMPO_x0);
                return;
            }
            x_ultim--;
            mvwaddstr (campo, y_ultim, x_ultim, "<=> ");
            wrefresh (campo);
            break;
    }
}

/* Move o chefe da 2ª parte do jogo */
void MoveChefe () {
    char *cara[] = {
        "< > < >",
        "< 0 0 >",
        " <===> "
    };

// desdesenha-o
    int i;
    for (i = 0; i < 3; i++) {
        mvwaddstr (campo, y_chefe + i, x_chefe, "       ");
        wrefresh (campo);
    }

// onde esta a bolinha?
    int y_bola, x_bola;
    getbegyx (bola, y_bola, x_bola);

// se a bolinha esta numa linha do chefão, não move pra la
    if (y_bola <= (y_chefe + CAMPO_y0) + 2)
        do {
            x_chefe = (rand () % (CAMPO_LARGURA - 8)) + 1;
        } while (x_bola - (x_chefe + CAMPO_x0) < 7 && x_bola - (x_chefe + CAMPO_x0) >= 0);
// mas se a bola ta pros otros lado, pode ir pra qualquer lugar
    else
        x_chefe = (rand () % (CAMPO_LARGURA - 8)) + 1;

    wattron (campo, COLOR_PAIR (FGboss));
// e desenha-o novamente
    for (i = 0; i < 3; i++)
        mvwaddstr (campo, y_chefe + i, x_chefe, cara[i]);
    wrefresh (campo);
}

/* prevê colisão com a barra */
char AlgoNoCaminhoBarra (int y, int x) {
    int y_bar, x_bar;

    getbegyx (barra, y_bar, x_bar);
// coordenada tela → coordenada barra
    y -= y_bar;
    x -= x_bar;

    return (mvwinch (barra, y, x));
}

/* prevê colisão com a bolinha [a partir de coordenadas do campo] */
char AlgoNoCaminhoBola (int y, int x) {
    int y_bola, x_bola;

    getbegyx (bola, y_bola, x_bola);
// bola: coordenada tela → coordenada campo
    y_bola -= CAMPO_y0;
    x_bola -= CAMPO_x0;
// coordenada campo → coordenada bola
    y -= y_bola;
    x -= x_bola;

    return (mvwinch (bola, y, x));
}

/* prevê colisão com os bloquinhos */
char AlgoNoCaminhoCampo (int y, int x) {
// coordenada tela → coordenada campo
    y -= CAMPO_y0;
    x -= CAMPO_x0;

    return (mvwinch (campo, y, x));
}

/* destroi o bloquinho, quando acertado */
void Quebra (char obst, int y, int x) {
    if (obst == '<') {
        mvwaddstr (campo, y - CAMPO_y0, x - CAMPO_x0, "   ");
        wrefresh (campo);
    }
    else if (obst == '=') {
        mvwaddstr (campo, y - CAMPO_y0, x - CAMPO_x0 - 1, "   ");
        wrefresh (campo);
    }
    else if (obst == '>') {
        mvwaddstr (campo, y - CAMPO_y0, x - CAMPO_x0 - 2, "   ");
        wrefresh (campo);
    }

// quebrou um bloco!
    numblocos--;
    AtualizaHud ();

// ultimo bloquinho: falinhas [na main ele começa a mexer]
    if (numblocos == 1) {
        attron (A_BOLD);
        mvaddstr (BARRA_y0 + 2, COLS/2 - 5, "Finish him!");
        refresh ();
        napms (1000);
// onde esta o ultimo bloquinho?
        for (y_ultim = BLOCO_y0; y_ultim < BLOCO_y0 + dificuldade; y_ultim++) {
            for (x_ultim = CAMPO_x0 + 1; x_ultim < CAMPO_x0 + CAMPO_LARGURA - 3; x_ultim += 3)
                if (AlgoNoCaminhoCampo (y_ultim, x_ultim) == '<')
                    break;
            if (AlgoNoCaminhoCampo (y_ultim, x_ultim) == '<')
                break;
        }

        mvaddstr (y_ultim - 1, x_ultim, "NO!");
        refresh ();
        napms (1000);
        mvaddstr (y_ultim - 1, x_ultim, "   ");
        refresh ();

// ultimo: coordenadas tela → coordenadas campo
        y_ultim -= CAMPO_y0;
        x_ultim -= CAMPO_x0;

        wattron (campo, COLOR_PAIR (y_ultim - 1)); // continua com a cor dele mesmo
    }

// matou o ultimo: passa pra proxima fase [chefão, VWAHAHAHAHA!]
    else if (numblocos == 0)
        FaseDois ();
}

/* morreu, diminui uma vida; não tem mais, recomeça o jogo [se quiser, claro] */
void Morreu () {
    static char i = 0;

    i++;
// andando em L, morre so uma vez
    if (movimento == 'L' && i == 1) {
        return;
    }
    i = 0;

    vidas--;
    AtualizaHud ();

// da aquela piscadinha vermelha na bolinha, pra avisar que realmente morreu
    wbkgd (bola, COLOR_PAIR (3));
    wrefresh (bola);
    napms (300);
    wbkgd (bola, COLOR_PAIR (8));
    wrefresh (bola);

    if (vidas == 0) {
        attron (A_BOLD);
        for ( ; i < 4; i++) {
            mvaddstr (BARRA_y0 + 2, COLS/2 - 5, "FIM DE JOGO");
            refresh ();
            napms (600);
            mvaddstr (BARRA_y0 + 2, COLS/2 - 5, "           ");
            refresh ();
            napms (600);
        }
        attroff (A_BOLD);
        s = KEY_F(2);
    }
}

/* Solta um fogo de artificio, pra proxima funçãozinha ai =] */
void FogoArtificio (int x) {
    int y;

    for (y = BARRA_y0 - 1; y >= BLOCO_y0; y--) {
        mvaddch (y, x, '|');
        refresh ();
        napms (100);
        mvaddch (y, x, ' ');
        refresh ();
    }

    mvaddstr (y, x - 1,     "\\|/");
    mvaddstr (y + 1, x - 2, "--O--");
    mvaddstr (y + 2, x - 1,  "/|\\");
    refresh ();
    napms (400);
    mvaddstr (y, x - 1,      "   ");
    mvaddstr (y + 1, x - 2, "     ");
    mvaddstr (y + 2, x - 1,  "   ");
    refresh ();
}

/* Passou do começo [os bloco], historinha, e chefe "VWAHAHAHAHA" */
void FaseDois () {
    char *historia[] = {
        "Parabens! Você derrotou o ultimo bloquinho!",
        "E assim, a paz retornou ao mundo... Mas o quê?"
    }, *falas[] = {
        "Você achou que ganharia facil assim?",
        "Você vai ver, sua barrinha troxa!",

        "Sua mãe e tão gorda, mas tão gorda,",
        "que ela precisa de 2 bytes pra cada caractere!",

        "Ainda acha que pode me derrotar? VWAHAHAHA!",
        "E ai, vai ficar parado ou o quê?",

        "Alias, toma uma maldiçãozinha ai:",
        "quero ver você segurar essa bolinha agora!"
    };
    char *cara[] = {
        "< > < >",
        "< 0 0 >",
        " <===> "
    };

// apaga o "Finish Him!"
    mvaddstr (FALA_y0, COLS/2 - 5, "           ");

// tem uma chance em dificuldade de ganhar uma vida extra [ebaa! xD]
    if (rand() % dificuldade == 0) {
        attron (COLOR_PAIR (7));
        mvaddstr (1, COLS - 14, "+1");
        refresh ();
        napms (2000);
        vidas++;
        AtualizaHud ();
        mvaddstr (1, COLS - 14, "  ");
        refresh ();
        attroff (COLOR_PAIR (7));
    }

// apaga a bolinha, prela começar no lugar de começo normal [pro chefe não nascer com a bolinha dentro]
    werase (bola);
    wrefresh (bola);
    mvwin (bola, BOLA_y0, BOLA_x0);
    v_dir = 'C';
    h_dir = 'D';

    attroff (A_BOLD);
// fogos de artificio, primeiro no começo do campo
    int x = CAMPO_x0 + 4;
    int y;
#ifndef PULAHIST
    FogoArtificio (x);
// dai no cantinho direito
    x += 38;
    FogoArtificio (x);
// e la no meio do campo
    x -= 19;
    FogoArtificio (x);

// historinha, frase por frase
    for (y = 0; y < 2; y++) {
        mvaddstr (FALA_y0, FALA_x0, historia[y]);
        refresh ();
        napms (2500);
        move (FALA_y0, 0);
        clrtoeol ();
    }
#endif
// cria a janela do chefe e a desenha pausadamente
    y_chefe = 1;
    x_chefe = (x - CAMPO_x0) - 2;
    wattron (campo, A_BOLD);
    wattron (campo, COLOR_PAIR (FGboss));
    for (y = 0; y < 3; y++)
        for (x = 0; x < 7; x++) {
            mvwaddch (campo, y_chefe + y, x_chefe + x, cara[y][x]);
            wrefresh (campo);
            napms (300);
        }
#ifndef PULAHIST
// e mais linhas de fala do chefão
    for (y = 0; y < 8; y++) {
        mvaddstr (FALA_y0, FALA_x0, falas[y]);
        refresh ();
        napms (2500);
// o a maldição!
        if (y == 6) {
            int i;
            for (i = 0; i < 3; i++) {
                for (x = 2; x < 10; x++) {
                    wbkgd (barra, COLOR_PAIR (x));
                    wrefresh (barra);
                    napms (100);
                }
            }
            wbkgd (barra, COLOR_PAIR (8));
            wrefresh (barra);
            napms (300);
        }
        move (FALA_y0, 0);
        clrtoeol ();
    }
#endif

    attron (A_BOLD);
    mvaddstr (FALA_y0, COLS/2 - 4, "Kill him!");
    refresh ();

// bolinha no começo
    mvwaddch (bola, 0, 0, 'O');
    wrefresh (bola);

// cria a barra de vida do chefão e a desenha
    HP_chefe = subwin (stdscr, CAMPO_ALTURA, 2, CAMPO_y0, CAMPO_x0 + CAMPO_LARGURA + 1);
    wattron (HP_chefe, A_BOLD);
    wattron (HP_chefe, COLOR_PAIR (7));
    mvwaddstr (HP_chefe, 0, 0, "/\\");
    mvwaddstr (HP_chefe, 1, 0, "\\/");
    napms (500);
    wrefresh (HP_chefe);
    for (vida_chefe = 0; vida_chefe < CAMPO_ALTURA - 2; ) {
        vida_chefe++;
        mvwaddstr (HP_chefe, vida_chefe, 0, "||");
        mvwaddstr (HP_chefe, vida_chefe + 1, 0, "\\/");
        wrefresh (HP_chefe);
        napms (150);
    }
// limpa as escritinhas e volta pro jogo
    for (y = 0; y < 2; y++) {
        move (CAMPO_y0 + y - 3, 0);
        clrtoeol ();
    }
// e fica rapidão
    periodo = 9;
}

/* Bom jogo, jovem miriapode; tentaras outra vez? */
void Ganhou () {
    int i;

    attron (A_BOLD);
    for (i = 0; i < 4; i++) {
        mvaddstr (FALA_y0, COLS/2 - 5, "VOCÊ GANHOU!");
        refresh ();
        napms (600);
        mvaddstr (FALA_y0, COLS/2 - 5, "              ");
        refresh ();
        napms (600);
    }
    attroff (A_BOLD);
    s = KEY_F(2);
}

/* Vê se acertou o chefão, e se sim: PORRADA NELE! */
int BateChefe (int y, int x) {
// coordenadas gerais → coordenadas campo
    y -= CAMPO_y0;
    x -= CAMPO_x0;

// e possivel trombar com o chefão [ta na area dele]
    if (y <= y_chefe + 2 && x >= x_chefe && x <= x_chefe + 6) {
// coordenadas campo → coordenadas chefe [começando do 0]
        x -= x_chefe;
        y -= y_chefe;

        switch (y) {
// linha da boca " <===> "
            case 2:
// quininha do desenho, que e em branco: então nem bateu =P
                if (x == 0 || x == 6)
                    return 0;
                else {
                    if (!(movimento == 'L' && l_mov == 'D' && v_dir == 'C')) {
                        if (h_dir == 'D' && x == 1)
                            h_dir = 'E';
                        else if (h_dir == 'E' && x == 5)
                            h_dir = 'D';
                    }
                    v_dir = 'B';
                }
                break;

// linha do meio: "< 0 0 >"
            case 1:
// bate naquele espacinho enquinado, dai de qualquer modo inverte tudo
                if (x == 1) {
                    v_dir = 'B';
                    h_dir = 'E';
                    break;
                }
// e aqui no outro espacinho enquinado
                else if (x == 5) {
                    v_dir = 'B';
                    h_dir = 'D';
                    break;
                }
// de resto
                if (!(movimento == 'L' && l_mov == 'D' && v_dir == 'C')) {
                    if (h_dir == 'D')
                        h_dir = 'E';
                    else
                        h_dir = 'D';

                    if (l_mov == 'H')
                        break;
                }
                v_dir = 'B';
                break;

// linha de cima: "< > < >"
// inverte sentido horizontal, pois o vertical ja muda por bater no teto
            case 0:
                if (h_dir == 'D')
                    h_dir = 'E';
                else
                    h_dir = 'D';
                break;
        }

        AtualizaVidaChefe ();

        return 1;
    }
// se não ta nem no Y do chefão, nem tem como bater
    else
        return 0;
}

/* Tira um de vida do chefão */
void AtualizaVidaChefe () {
    mvwaddstr (HP_chefe, vida_chefe, 0, "\\/");
    mvwaddstr (HP_chefe, vida_chefe + 1, 0, "  ");
    wrefresh (HP_chefe);

    vida_chefe--;
    if (vida_chefe == 0)
        Ganhou ();
}

/* Atira contra o chefão, pra ficar mais divertido o negocio
 * cuida de ganhar o tiro, atirar e mover o tiro, assim como calcular a colisao com o chefe
 */
void Shoot (char *fired) {
    if (!*fired) {
        // ganha o tiro
        if (tiro == SHOT_TIME) {
            attron (COLOR_PAIR (FGshot));
            mvaddstr (CAMPO_ALTURA/2, CAMPO_x0 - 17, "You got a shot!");
            mvaddstr (CAMPO_ALTURA/2 + 1, CAMPO_x0 - 17, "Press Up key or W");
            attroff (COLOR_PAIR (FGshot));
            refresh ();
            tiro++;
        }
        else if (tiro < SHOT_TIME) {
            tiro++;
        }
    }
    // tiro correndo
    else {
        // ainda nao tem nem esperanca de acertar
        if (y_tiro >= CAMPO_y0 + 3) {
            wattron (campo, COLOR_PAIR (FGshot));

            mvwaddstr (campo, y_tiro, x_tiro, "||");
            wrefresh (campo);

            wattroff (campo, COLOR_PAIR (FGshot));
            y_tiro--;
        }
        // ta no y do chefe, sera que acertou?
        else {
            // apaga rastro do tiro
            for (y_tiro++; y_tiro < BARRA_y0 - CAMPO_y0; y_tiro++) {
                mvwaddstr (campo, y_tiro, x_tiro, "  ");
                wrefresh (campo);
            }
            // acertou! UHUL!
            if (x_tiro >= x_chefe && x_tiro <= x_chefe + 6) {
                AtualizaVidaChefe ();
            }
            tiro = 0;
            *fired = 0;
        }
    }
}

/* Clicou pra atirar */
void ClickShoot () {
    // descobre o 'x' da barra, pro tiro sair do meio dela
    getbegyx (barra, y_tiro, x_tiro);
    y_tiro -= CAMPO_y0 + 1;
    x_tiro -= CAMPO_x0 - 1;

    mvaddstr (CAMPO_ALTURA/2, CAMPO_x0 - 17, "               ");
    mvaddstr (CAMPO_ALTURA/2 + 1, CAMPO_x0 - 17, "                 ");
    refresh ();
    return;
}