//#define FASEDOIS
//#define PULAHIST
/*
* Arkanoid versus Ncurses: ARKURSES
*
* Sinopse: Você está dentro do personagem de uma barra. Uma barra feita de
* caracteres! O mundo está sendo atacado por terríveis 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 número 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] [é até melhor de jogar];
* -Esse jogo buga bastante;
* -Não existe tal coisa como física;
* -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ê já sabe o que faz, né?
void CriaBlocos (); // inicializadores
void CriaBarra (); // dos
void CriaBola (); // gráficos
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 último bloquinho
void MoveChefe (); // move o chefe da segunda [e última] 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 lá
char AlgoNoCaminhoBola (int y, int x); // e o último bloco, bate na bola?
void FogoArtificio (int x); // um fogo de artifício [que lindo ^^]
void Morreu (); // vai que ele morre, né?
void FaseDois (); // ou passa do começo?
void Ganhou (); // ou até 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: número de vezes que ainda falta bater nele
int numblocos; // número de blocos: 15 vezes o número 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 último bloquinho
int y_chefe, x_chefe; // coordenadas do chefe
char dificuldade; // número 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 também, 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 está: para mover a bolinha e o último 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 também 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 só 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 ();
// último 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 numérico 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);
// próximo 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 [taí 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 até uma das opções válidas
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 lá 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 próxima linha
cor++;
}
wrefresh (campo);
}
/* Cria a barra, na posição de início */
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 início */
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 está a bola?
// tá 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':
// próxima posição [se aplica a todos os movimentos]
y--; x++;
// se estiver na 2ª fase, vê se bate com o chefão, daí 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 último bloquinho, quando só faltar ele */
void MoveUltimo () {
static char lado_ultim='D'; // direção horizontal do movimento do último 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, né
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 está a bolinha?
int y_bola, x_bola;
getbegyx (bola, y_bola, x_bola);
// se a bolinha está numa linha do chefão, não move pra lá
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 tá 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));
}
/* destrói 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 ();
// último 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 está o último 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 ();
// último: 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 último: passa pra próxima 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 só uma vez
if (movimento == 'L' && i == 1) {
return;
}
i = 0;
vidas--;
AtualizaHud ();
// dá 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 artifício, pra próxima funçãozinha aí =] */
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[] = {
"Parabéns! Você derrotou o último bloquinho!",
"E assim, a paz retornou ao mundo... Mas o quê?"
}, *falas[] = {
"Você achou que ganharia fácil assim?",
"Você vai ver, sua barrinha troxa!",
"Sua mã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 aí, vai ficar parado ou o quê?",
"Aliás, toma uma maldiçãozinha aí:",
"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 artifício, primeiro no começo do campo
int x = CAMPO_x0 + 4;
int y;
#ifndef PULAHIST
FogoArtificio (x);
// daí no cantinho direito
x += 38;
FogoArtificio (x);
// e lá 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);
// ó 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 miriápode; tentarás 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;
// é possível trombar com o chefão [tá na área 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 é 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, daí 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 já 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 tá 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 negócio
* 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;
}