Created
March 17, 2019 15:00
-
-
Save baeharam/d42bd762357433c83ac7aa7fb4725a8e to your computer and use it in GitHub Desktop.
My Dirty Tetris
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <stdio.h> | |
#include <windows.h> | |
#include <stdlib.h> | |
#include <conio.h> // 비표준 함수 kbhit()와 getch()를 위한 헤더파일 | |
#include <time.h> // Sleep(시간)함수를 위한 헤더파일 | |
// 참 거짓 | |
#define TRUE 1 | |
#define FALSE 0 | |
// 키 입력 제어 | |
#define KEY 224 | |
#define LEFT 75 | |
#define RIGHT 77 | |
#define UP 72 | |
#define DOWN 80 | |
#define SPACE 32 | |
#define p 112 | |
#define P 80 | |
#define ENTER 13 | |
#define ESC 27 | |
#define r 114 | |
#define R 82 | |
#define q 113 | |
#define Q 81 | |
// 화면에 표시되는 모양들의 상태 | |
#define EMPTY 0 | |
#define INACTIVE 2 | |
#define ACTIVE -2 | |
#define WALL_WIDTH 1 | |
#define WALL_HEIGHT 11 | |
#define WALL_CORNER1 111 | |
#define WALL_CORNER2 112 | |
#define WALL_CORNER3 113 | |
#define WALL_CORNER4 114 | |
#define SHADOW -100 | |
#define EMPTY_Q 10 | |
#define LEVELUP 800 | |
// 점수, 경험치 보드에 표시되는 모양들의 상태 | |
// 그리고 보드의 너비와 높이 | |
#define SCORE_INACTIVE1 45 | |
#define SCORE_INACTIVE2 46 | |
#define SCORE_EMPTY 50 | |
#define SCORE_WIDTH 15 | |
#define SCORE_HEIGHT 5 | |
#define LEVEL_INACTIVE1 45 | |
#define LEVEL_INACTIVE2 46 | |
#define LEVEL_EMPTY 50 | |
#define LEVEL_WIDTH 9 | |
#define LEVEL_HEIGHT 5 | |
// screen_org가 처음 초기화 될 경우 쓰임 | |
#define FIRST -1 | |
// 블록의 종류 개수 | |
#define KIND 7 | |
// 너비와 높이 | |
#define WIDTH 15 | |
#define HEIGHT 25 | |
// 다음 블록을 보여주는 테두리 좌표 | |
#define SHOWX 1 | |
#define SHOWY 32 | |
#define SHOWXX 1 | |
#define SHOWYY 49 | |
// 점수를 보여주는 테두리 좌표 | |
#define SCOREX 29 | |
#define SCOREY 3 | |
// 조작법을 보여주는 좌표 | |
#define JOYX 10 | |
#define JOYY 33 | |
// 게임 종료 상태를 보여주는 좌표 | |
#define PAUSEX 12 | |
#define PAUSEY 23 | |
// 다음 레벨까지 퍼센트 보여주는 테두리 좌표 | |
#define LEVELX 29 | |
#define LEVELY 38 | |
// 퍼센트 좌표 | |
#define LEVELXX 29 | |
#define LEVELYY 36 | |
// 궁극기 on/off 표시해주는 테두리 좌표 | |
#define SKILLX 1 | |
#define SKILLY 66 | |
// 패치내역 표시해주는 좌표 | |
#define PATCHX 3 | |
#define PATCHY 2 | |
// 레벨업 표시해주는 좌표 | |
#define LEVELUPX 8 | |
#define LEVELUPY 8 | |
// [전역변수] | |
// 종류에 대한 변수 | |
int kind; // 현재 블록의 종류 | |
int next_kind; // 다음에 올 블록의 종류 | |
int next2_kind; // 다다음에 올 블록의 종류 | |
// 좌표로 이용되는 index | |
int bx, by; // 가로 index와 세로 index | |
int sx, sy; // shadow에 사용되는 가로 index와 세로 index | |
// 여러가지 함수에 이용되는 변수 | |
int rotation; // 회전의 종류 | |
int shadow_rotation; // shadow block을 위한 이전 회전 값 | |
int key; // 키값 | |
int line_num; // 완성된 줄 개수 | |
int check_line; // 레벨을 위한 줄 개수 확인 | |
// 게임정보에 대한 변수 | |
int percent; //경험치 | |
int score; // 점수 | |
int speed; // 게임속도 | |
int level; // 현재 게임 레벨 | |
// 판단에 대한 변수 | |
int color_flag; // 깜박거릴 경우 색깔을 바꿀지에 대한 판단 | |
int stop_flag; // 멈출지 여부 판단 | |
int space_flag; // 스페이스 키를 눌렀는지에 대한 판단 | |
int reset_flag; // 다시 시작하는지에 대한 판단 | |
int line_flag; // 한 줄이 꽉 찼나 판단 | |
int gameover; // 게임이 끝났는지를 판단 | |
int q_flag; // 궁극기 on/off에 대한 판단 | |
int condition; // 시작화면 메뉴선택에 대한 판단 | |
// update에 따른 2가지 화면 배열 | |
int screen_org[HEIGHT][WIDTH]; // 이전 화면 | |
int screen_up[HEIGHT][WIDTH]; // 업데이트 화면 | |
// 점수, 경험치 보드를 위한 2차원 배열 | |
int scoreboard[SCORE_HEIGHT][SCORE_WIDTH]; | |
int levelboard[LEVEL_HEIGHT][LEVEL_WIDTH]; | |
// 시간 제어 상수와 변수 | |
const int FRAME = 20; // 키 입력을 받을 횟수의 상수값 | |
int framestay = 0; // 위 상수값을 이용하기 위해 취급하는 변수값 | |
// 블록 4차원 배열 | |
int block[7][4][4][4] = | |
{ | |
{ | |
{ { 0,0,0,0 },{ 1,1,1,1 },{ 0,0,0,0 },{ 0,0,0,0 } },{ { 0,1,0,0 },{ 0,1,0,0 },{ 0,1,0,0 },{ 0,1,0,0 } }, | |
{ { 0,0,0,0 },{ 1,1,1,1 },{ 0,0,0,0 },{ 0,0,0,0 } },{ { 0,1,0,0 },{ 0,1,0,0 },{ 0,1,0,0 },{ 0,1,0,0 } } | |
}, | |
{ | |
{ { 0,1,0,0 },{ 1,1,1,0 },{ 0,0,0,0 },{ 0,0,0,0 } },{ { 1,0,0,0 },{ 1,1,0,0 },{ 1,0,0,0 },{ 0,0,0,0 } }, | |
{ { 1,1,1,0 },{ 0,1,0,0 },{ 0,0,0,0 },{ 0,0,0,0 } },{ { 0,1,0,0 },{ 1,1,0,0 },{ 0,1,0,0 },{ 0,0,0,0 } } | |
}, | |
{ | |
{ { 0,1,1,0 },{ 1,1,0,0 },{ 0,0,0,0 },{ 0,0,0,0 } },{ { 1,0,0,0 },{ 1,1,0,0 },{ 0,1,0,0 },{ 0,0,0,0 } }, | |
{ { 0,1,1,0 },{ 1,1,0,0 },{ 0,0,0,0 },{ 0,0,0,0 } },{ { 1,0,0,0 },{ 1,1,0,0 },{ 0,1,0,0 },{ 0,0,0,0 } } | |
}, | |
{ | |
{ { 1,1,0,0 },{ 0,1,1,0 },{ 0,0,0,0 },{ 0,0,0,0 } },{ { 0,1,0,0 },{ 1,1,0,0 },{ 1,0,0,0 },{ 0,0,0,0 } }, | |
{ { 1,1,0,0 },{ 0,1,1,0 },{ 0,0,0,0 },{ 0,0,0,0 } },{ { 0,1,0,0 },{ 1,1,0,0 },{ 1,0,0,0 },{ 0,0,0,0 } } | |
}, | |
{ | |
{ { 1,1,1,0 },{ 0,0,1,0 },{ 0,0,0,0 },{ 0,0,0,0 } },{ { 0,1,0,0 },{ 0,1,0,0 },{ 1,1,0,0 },{ 0,0,0,0 } }, | |
{ { 1,0,0,0 },{ 1,1,1,0 },{ 0,0,0,0 },{ 0,0,0,0 } },{ { 1,1,0,0 },{ 1,0,0,0 },{ 1,0,0,0 },{ 0,0,0,0 } } | |
}, | |
{ | |
{ { 1,1,1,0 },{ 1,0,0,0 },{ 0,0,0,0 },{ 0,0,0,0 } },{ { 1,1,0,0 },{ 0,1,0,0 },{ 0,1,0,0 },{ 0,0,0,0 } }, | |
{ { 0,0,1,0 },{ 1,1,1,0 },{ 0,0,0,0 },{ 0,0,0,0 } },{ { 1,0,0,0 },{ 1,0,0,0 },{ 1,1,0,0 },{ 0,0,0,0 } } | |
}, | |
{ | |
{ { 1,1,0,0 },{ 1,1,0,0 },{ 0,0,0,0 },{ 0,0,0,0 } },{ { 1,1,0,0 },{ 1,1,0,0 },{ 0,0,0,0 },{ 0,0,0,0 } }, | |
{ { 1,1,0,0 },{ 1,1,0,0 },{ 0,0,0,0 },{ 0,0,0,0 } },{ { 1,1,0,0 },{ 1,1,0,0 },{ 0,0,0,0 },{ 0,0,0,0 } } | |
} | |
}; | |
// [함수 선언] | |
// 보조함수 | |
void removecursor(void); // 커서 깜박거림 지우기 | |
void gotoxy(int x, int y); // (x,y)로 좌표 이동 | |
void check_levelup(); // 레벨업에 따른 메시지 함수 | |
void print_level(void); // 현재 레벨 출력 함수 | |
void next_level(void); // 다음 레벨까지 퍼센트 표시 함수 | |
void next2_block(void); // 다다음 블록을 보여주는 함수 | |
void over_message(void); // 게임이 종료됬을 때 나타나는 메시지 함수 | |
void startscreen(void); // 게임 시작전 화면을 출력하는 함수 | |
void next_block(void); // 다음 블록을 보여주는 함수 | |
void score_board(void); // 현재 점수를 보여주는 함수 | |
void joystick(void); // 조작법 표시해주는 함수 | |
void allclear(); // 궁극기 ON/OFF 표시 함수 | |
void levelup_format(); // 레벨업 표시 함수 | |
void reset_message(); // 게임을 다시시작할 때 나타나는 함수 | |
// 메인 기능 함수 | |
void draw_screen(void); // 화면 그리는 함수 (이전과 업데이트 비교해서 다른 부분만) | |
void reset_screen(void); // screen_org와 screen_up의 요소 초기화 | |
void reset(void); // 맨 처음 위에서 블록이 생길 때의 조건 값으로 초기화 | |
void make_block(void); // 블록을 만드는 함수 (블록 부분을 ACTIVE로 초기화) | |
void drop_block(void); // 블록을 한 칸 떨어뜨리는 함수 | |
void move_block(int dir); // 블록을 키보드 방향에 따라 움직이는 함수 | |
void shadow_block(void); // 블록이 내려올 자리를 미리 그림자 형식으로 보여주는 함수 | |
void move_shadow_block(int dir); // 쉐도우 블록을 움직이는 함수 | |
void check_level(void); // 레벨 체크하는 함수 | |
void check_key(void); // 키보드 입력 제어 함수 | |
void line_check(void); // 줄이 꽉 찼나 체크하고 꽉 찬 줄은 없앤 후 나머지 내리는 함수. | |
int check_crash(int rotation, int x, int y); // 블록의 충돌 여부를 확인하는 함수 | |
void start_function(void); // 블록이 내려올 때마다 호출되어야 하는 함수를 호출하는 함수 | |
void info_reset(void); // 게임정보를 리셋하는 함수 | |
void win_game(void); // 게임을 깻을 경우 출력하는 함수 | |
void clear_skill(void); // 궁극기 함수 | |
int cover_where(void); // 밑에서부터 제일 처음으로 비어있는 줄의 index 반환 함수 | |
void patchnote(void); // 게임 패치 내역 설명 | |
// 점수판을 위한 점수 출력 포맷 함수 | |
void reset_scoreboard(void); // 처음으로 초기화 하는 함수 | |
void make_scoreboard(int num, int pos); // 숫자에 따라 배열 업데이트 하는 함수 | |
void show_scoreboard(void); // 완성된 배열을 보여주는 함수 | |
void complete_scoreboard(int num); // 숫자를 한 자리씩 쪼개 완전히 나타내는 함수 | |
// 경험치판 전용 | |
void reset_levelboard(void); // 처음으로 초기화 하는 함수 | |
void make_levelboard(int num, int pos); // 숫자에 따라 배열 업데이트 하는 함수 | |
void show_levelboard(void); // 완성된 배열을 보여주는 함수 | |
void complete_levelboard(int num); // 숫자를 한 자리씩 쪼개 완전히 나타내는 함수 | |
// 메인 함수 | |
int main(void) | |
{ | |
system("mode con:cols=80 lines=30"); // 콘솔창 크기 80x40으로 설정 | |
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 15); // 폰트 색깔 진하게 | |
removecursor(); // 커서의 깜박거림 지움 | |
startscreen(); // 게임 시작 전 화면 출력. | |
system("mode con:cols=80 lines=37"); | |
// 게임화면 호출하기 위한 화면 클리어 | |
system("cls"); | |
srand((unsigned)time(NULL)); // 랜덤값의 시드 초기화 | |
reset_screen(); // screen_org와 screen_up의 초기화 | |
reset(); // 처음 블록의 조건값 초기화 | |
info_reset(); // 게임 정보 초기화. | |
next_kind = rand() % KIND; // 종류를 랜덤으로 설정 | |
next2_kind = rand() % KIND; // 다다음 종류를 생성 | |
sx = 0, sy = 0; // shadow block의 좌표 초기화 | |
reset_scoreboard(); // 점수판 초기화 | |
reset_levelboard(); // 경험치판 초기화 | |
while (TRUE) { | |
/* 원래 방식 : for문을 통해 키 입력을 5번 받은 후 drop_block() 호출. | |
하지만 Sleep(100)이 5번 호출되서 전체적인 지연시간이 많아짐. | |
개선 방식 : 프레임으로 쪼개서 키 입력과 시간을 지연시키는 방법. | |
총 500이라는 지연 시간은 동일하지만 프레임을 20으로 쪼개서 입력받을 수 있는 | |
개수는 더 많아지고 부드러워짐 */ | |
// 상수값을 계속해서 변하는 변수값에 대입 | |
framestay = FRAME; | |
// 무한루프 안에 무한루프 | |
while (TRUE) { | |
check_key(); | |
draw_screen(); | |
// 스페이스 바를 눌렀을 경우 더 이상 입력 받을 필요 없음. | |
if (space_flag == TRUE) { | |
drop_block(); | |
break; | |
} | |
// 바닥에 닿았다면 잠깐의 이동시간 주기 | |
if (check_crash(rotation, bx + 1, by)) Sleep(50); | |
// 입력 시간이 다 된 경우도 블록을 내리고 빠져나온다. | |
// 단, 먼저 체크해야 할 것은 플레이어가 P를 누른 여부이다. | |
if (stop_flag == FALSE) { | |
if (framestay-- == 0) { | |
drop_block(); | |
break; | |
} | |
} | |
Sleep(speed / 20); | |
} | |
// 꽉찬 줄이 있으면 없앤다. | |
line_check(); | |
// 게임이 끝났다면 메시지를 띄우고 다시 진행한다. | |
if (gameover == TRUE) { | |
over_message(); | |
continue; | |
} | |
// 레벨 업에 대해 체크한다. | |
check_level(); | |
} | |
getch(); // 바로 안 끝나기 위한 입력 함수 | |
return 0; | |
} | |
// 점수와 경험치 보드에 대한 함수들 | |
void reset_scoreboard() | |
{ | |
for (int i = 0; i<SCORE_HEIGHT; i++) { | |
for (int j = 0; j<SCORE_WIDTH; j++) { | |
if (scoreboard[i][j] == SCORE_INACTIVE1 || | |
scoreboard[i][j] == SCORE_INACTIVE2) | |
scoreboard[i][j] = SCORE_EMPTY; | |
} | |
} | |
for (int i = 0; i<SCORE_HEIGHT; i++) { | |
for (int j = 0; j<SCORE_WIDTH; j++) { | |
if (j == 12 || j == 14) | |
scoreboard[i][j] = SCORE_INACTIVE2; | |
else if (j == 13) { | |
if (i == 0 || i == SCORE_HEIGHT - 1) | |
scoreboard[i][j] = SCORE_INACTIVE2; | |
} | |
} | |
} | |
} | |
void make_scoreboard(int num, int pos) | |
{ | |
int score = 0; | |
if (pos == 3 || pos == 9) | |
score = SCORE_INACTIVE1; | |
else | |
score = SCORE_INACTIVE2; | |
switch (num) | |
{ | |
case 0: | |
for (int i = 0; i<SCORE_HEIGHT; i++) { | |
for (int j = pos; j <= pos + 2; j++) { | |
if (scoreboard[i][j] == SCORE_INACTIVE1 || | |
scoreboard[i][j] == SCORE_INACTIVE2) | |
scoreboard[i][j] = SCORE_EMPTY; | |
} | |
} | |
for (int i = 0; i<SCORE_HEIGHT; i++) { | |
for (int j = pos; j <= pos + 2; j++) { | |
if (j == pos || j == pos + 2) | |
scoreboard[i][j] = score; | |
else if (j == pos + 1) { | |
if (i == 0 || i == SCORE_HEIGHT - 1) | |
scoreboard[i][j] = score; | |
} | |
} | |
} | |
break; | |
case 1: | |
for (int i = 0; i<SCORE_HEIGHT; i++) { | |
for (int j = pos; j <= pos + 2; j++) { | |
if (scoreboard[i][j] == SCORE_INACTIVE1 || | |
scoreboard[i][j] == SCORE_INACTIVE2) | |
scoreboard[i][j] = SCORE_EMPTY; | |
} | |
} | |
for (int i = 0; i<SCORE_HEIGHT; i++) | |
scoreboard[i][pos + 2] = score; | |
break; | |
case 2: | |
for (int i = 0; i<SCORE_HEIGHT; i++) { | |
for (int j = pos; j <= pos + 2; j++) { | |
if (scoreboard[i][j] == SCORE_INACTIVE1 || | |
scoreboard[i][j] == SCORE_INACTIVE2) | |
scoreboard[i][j] = SCORE_EMPTY; | |
} | |
} | |
for (int i = 0; i<SCORE_HEIGHT; i++) { | |
for (int j = pos; j <= pos + 2; j++) { | |
if (i == 0 || i == 2) | |
scoreboard[i][j] = score; | |
else if (i == SCORE_HEIGHT - 1) | |
scoreboard[i][j] = score; | |
else if (i == 1 && j == pos + 2) scoreboard[i][j] = score; | |
else if (i == 3 && j == pos) scoreboard[i][j] = score; | |
} | |
} | |
break; | |
case 3: | |
for (int i = 0; i<SCORE_HEIGHT; i++) { | |
for (int j = pos; j <= pos + 2; j++) { | |
if (scoreboard[i][j] == SCORE_INACTIVE1 || | |
scoreboard[i][j] == SCORE_INACTIVE2) | |
scoreboard[i][j] = SCORE_EMPTY; | |
} | |
} | |
for (int i = 0; i<SCORE_HEIGHT; i++) { | |
for (int j = pos; j <= pos + 2; j++) { | |
if (j == pos + 2) | |
scoreboard[i][j] = score; | |
else { | |
if (i != 1 && i != 3) | |
scoreboard[i][j] = score; | |
} | |
} | |
} | |
break; | |
case 4: | |
for (int i = 0; i<SCORE_HEIGHT; i++) { | |
for (int j = pos; j <= pos + 2; j++) { | |
if (scoreboard[i][j] == SCORE_INACTIVE1 || | |
scoreboard[i][j] == SCORE_INACTIVE2) | |
scoreboard[i][j] = SCORE_EMPTY; | |
} | |
} | |
for (int i = 0; i<SCORE_HEIGHT; i++) { | |
for (int j = pos; j <= pos + 2; j++) { | |
if (j == pos + 2) | |
scoreboard[i][j] = score; | |
else { | |
if (j == pos + 1) { | |
if (i == 2) | |
scoreboard[i][j] = score; | |
} | |
else { | |
if (i != 3 && i != SCORE_HEIGHT - 1) | |
scoreboard[i][j] = score; | |
} | |
} | |
} | |
} | |
break; | |
case 5: | |
for (int i = 0; i<SCORE_HEIGHT; i++) { | |
for (int j = pos; j <= pos + 2; j++) { | |
if (scoreboard[i][j] == SCORE_INACTIVE1 || | |
scoreboard[i][j] == SCORE_INACTIVE2) | |
scoreboard[i][j] = SCORE_EMPTY; | |
} | |
} | |
for (int i = 0; i<SCORE_HEIGHT; i++) { | |
for (int j = pos; j <= pos + 2; j++) { | |
if (i == 0 || i == 2) | |
scoreboard[i][j] = score; | |
else if (i == SCORE_HEIGHT - 1) | |
scoreboard[i][j] = score; | |
else { | |
if (i == 1 && j == pos) scoreboard[i][j] = score; | |
else if (i == 3 && j == pos + 2) scoreboard[i][j] = score; | |
} | |
} | |
} | |
break; | |
case 6: | |
for (int i = 0; i<SCORE_HEIGHT; i++) { | |
for (int j = pos; j <= pos + 2; j++) { | |
if (scoreboard[i][j] == SCORE_INACTIVE1 || | |
scoreboard[i][j] == SCORE_INACTIVE2) | |
scoreboard[i][j] = SCORE_EMPTY; | |
} | |
} | |
for (int i = 0; i<SCORE_HEIGHT; i++) { | |
for (int j = pos; j <= pos + 2; j++) { | |
if (i == 0 || i == 2) | |
scoreboard[i][j] = score; | |
else if (i == SCORE_HEIGHT - 1) | |
scoreboard[i][j] = score; | |
if (i == 1 && j == pos) scoreboard[i][j] = score; | |
else if (i == 3) { | |
if (j == pos) scoreboard[i][j] = score; | |
else if (j == pos + 2) scoreboard[i][j] = score; | |
} | |
} | |
} | |
break; | |
case 7: | |
for (int i = 0; i<SCORE_HEIGHT; i++) { | |
for (int j = pos; j <= pos + 2; j++) { | |
if (scoreboard[i][j] == SCORE_INACTIVE1 || | |
scoreboard[i][j] == SCORE_INACTIVE2) | |
scoreboard[i][j] = SCORE_EMPTY; | |
} | |
} | |
for (int i = 0; i<SCORE_HEIGHT; i++) { | |
for (int j = pos; j <= pos + 2; j++) { | |
if (j == pos + 2) scoreboard[i][j] = score; | |
else if (j == pos) { | |
if (i != 3 && i != SCORE_HEIGHT - 1) | |
scoreboard[i][j] = score; | |
} | |
else if (j == pos + 1) { | |
if (i == 0) | |
scoreboard[i][j] = score; | |
} | |
} | |
} | |
break; | |
case 8: | |
for (int i = 0; i<SCORE_HEIGHT; i++) { | |
for (int j = pos; j <= pos + 2; j++) { | |
if (scoreboard[i][j] == SCORE_INACTIVE1 || | |
scoreboard[i][j] == SCORE_INACTIVE2) | |
scoreboard[i][j] = SCORE_EMPTY; | |
} | |
} | |
for (int i = 0; i<SCORE_HEIGHT; i++) { | |
for (int j = pos; j <= pos + 2; j++) { | |
if (i == 0 || i == 2) | |
scoreboard[i][j] = score; | |
else if (i == SCORE_HEIGHT - 1) | |
scoreboard[i][j] = score; | |
else if (i == 1 || i == 3) { | |
if (j == pos || j == pos + 2) | |
scoreboard[i][j] = score; | |
} | |
} | |
} | |
break; | |
case 9: | |
for (int i = 0; i<SCORE_HEIGHT; i++) { | |
for (int j = pos; j <= pos + 2; j++) { | |
if (scoreboard[i][j] == SCORE_INACTIVE1 || | |
scoreboard[i][j] == SCORE_INACTIVE2) | |
scoreboard[i][j] = SCORE_EMPTY; | |
} | |
} | |
for (int i = 0; i<SCORE_HEIGHT; i++) { | |
for (int j = pos; j <= pos + 2; j++) { | |
if (j == pos + 2) | |
scoreboard[i][j] = score; | |
else if (j == pos) { | |
if (i != 3 && i != SCORE_HEIGHT - 1) | |
scoreboard[i][j] = score; | |
} | |
else if (j == pos + 1) { | |
if (i == 0 || i == 2) | |
scoreboard[i][j] = score; | |
} | |
} | |
} | |
break; | |
} | |
} | |
void show_scoreboard() | |
{ | |
for (int i = 0; i<SCORE_HEIGHT; i++) { | |
for (int j = 0; j<SCORE_WIDTH; j++) { | |
gotoxy(i + SCOREX + 1, 2 * j + 5); | |
if (scoreboard[i][j] == SCORE_INACTIVE1) { | |
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 27); | |
printf("■"); | |
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 15); | |
} | |
else if (scoreboard[i][j] == SCORE_INACTIVE2) { | |
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 31); | |
printf("■"); | |
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 15); | |
} | |
else if (scoreboard[i][j] == SCORE_EMPTY) | |
printf(" "); | |
} | |
} | |
} | |
void complete_scoreboard(int num) | |
{ | |
int score_show = 0; // 계속해서 계산될 값 | |
int zero_flag = FALSE; // 0 출력 가능 여부 확인 | |
// one~five는 만의 자리부터 일의 자리를 상징 | |
int one = num / 10000; score_show = num % 10000; | |
if (one>0) { | |
make_scoreboard(one, 0); | |
zero_flag = TRUE; // 다음에 0출력 가능 | |
} | |
int two = score_show / 1000; score_show = score_show % 1000; | |
if (zero_flag == TRUE) | |
make_scoreboard(two, 3); | |
else { | |
if (two>0) { | |
make_scoreboard(two, 3); | |
zero_flag = TRUE; | |
} | |
} | |
int three = score_show / 100; score_show = score_show % 100; | |
if (zero_flag == TRUE) | |
make_scoreboard(three, 6); | |
else { | |
if (three>0) { | |
make_scoreboard(three, 6); | |
zero_flag = TRUE; | |
} | |
} | |
int four = score_show / 10; | |
if (zero_flag == TRUE) | |
make_scoreboard(four, 9); | |
else { | |
if (four>0) { | |
make_scoreboard(four, 9); | |
zero_flag = TRUE; | |
} | |
} | |
int five = score_show = score_show % 10; | |
if (zero_flag == TRUE) | |
make_scoreboard(five, 12); | |
else { | |
if (five>0) { | |
make_scoreboard(five, 12); | |
zero_flag = TRUE; | |
} | |
} | |
show_scoreboard(); | |
} | |
void reset_levelboard() | |
{ | |
for (int i = 0; i<LEVEL_HEIGHT; i++) { | |
for (int j = 0; j <= LEVEL_WIDTH - 1; j++) { | |
if (levelboard[i][j] == LEVEL_INACTIVE1 || | |
levelboard[i][j] == LEVEL_INACTIVE2) | |
levelboard[i][j] = LEVEL_EMPTY; | |
} | |
} | |
for (int i = 0; i<LEVEL_HEIGHT; i++) { | |
for (int j = 0; j<LEVEL_WIDTH; j++) { | |
if (j == 6 || j == 8) | |
levelboard[i][j] = LEVEL_INACTIVE2; | |
else if (j == 7) { | |
if (i == 0 || i == LEVEL_HEIGHT - 1) | |
levelboard[i][j] = LEVEL_INACTIVE2; | |
} | |
} | |
} | |
} | |
void make_levelboard(int num, int pos) | |
{ | |
int level = 0; | |
if (pos == 0 || pos == 6) | |
level = LEVEL_INACTIVE1; | |
else | |
level = LEVEL_INACTIVE2; | |
switch (num) | |
{ | |
case 0: | |
for (int i = 0; i<LEVEL_HEIGHT; i++) { | |
for (int j = pos; j <= pos + 2; j++) { | |
if (levelboard[i][j] == LEVEL_INACTIVE1 || | |
levelboard[i][j] == LEVEL_INACTIVE2) | |
levelboard[i][j] = LEVEL_EMPTY; | |
} | |
} | |
for (int i = 0; i<LEVEL_HEIGHT; i++) { | |
for (int j = pos; j <= pos + 2; j++) { | |
if (j == pos || j == pos + 2) | |
levelboard[i][j] = level; | |
else if (j == pos + 1) { | |
if (i == 0 || i == LEVEL_HEIGHT - 1) | |
levelboard[i][j] = level; | |
} | |
} | |
} | |
break; | |
case 1: | |
for (int i = 0; i<LEVEL_HEIGHT; i++) { | |
for (int j = pos; j <= pos + 2; j++) { | |
if (levelboard[i][j] == LEVEL_INACTIVE1 || | |
levelboard[i][j] == LEVEL_INACTIVE2) | |
levelboard[i][j] = LEVEL_EMPTY; | |
} | |
} | |
for (int i = 0; i<LEVEL_HEIGHT; i++) | |
levelboard[i][pos + 2] = level; | |
break; | |
case 2: | |
for (int i = 0; i<LEVEL_HEIGHT; i++) { | |
for (int j = pos; j <= pos + 2; j++) { | |
if (levelboard[i][j] == LEVEL_INACTIVE1 || | |
levelboard[i][j] == LEVEL_INACTIVE2) | |
levelboard[i][j] = LEVEL_EMPTY; | |
} | |
} | |
for (int i = 0; i<LEVEL_HEIGHT; i++) { | |
for (int j = pos; j <= pos + 2; j++) { | |
if (i == 0 || i == 2) | |
levelboard[i][j] = level; | |
else if (i == LEVEL_HEIGHT - 1) | |
levelboard[i][j] = level; | |
else if (i == 1 && j == pos + 2) levelboard[i][j] = level; | |
else if (i == 3 && j == pos) levelboard[i][j] = level; | |
} | |
} | |
break; | |
case 3: | |
for (int i = 0; i<LEVEL_HEIGHT; i++) { | |
for (int j = pos; j <= pos + 2; j++) { | |
if (levelboard[i][j] == LEVEL_INACTIVE1 || | |
levelboard[i][j] == LEVEL_INACTIVE2) | |
levelboard[i][j] = LEVEL_EMPTY; | |
} | |
} | |
for (int i = 0; i<LEVEL_HEIGHT; i++) { | |
for (int j = pos; j <= pos + 2; j++) { | |
if (j == pos + 2) | |
levelboard[i][j] = level; | |
else { | |
if (i != 1 && i != 3) | |
levelboard[i][j] = level; | |
} | |
} | |
} | |
break; | |
case 4: | |
for (int i = 0; i<LEVEL_HEIGHT; i++) { | |
for (int j = pos; j <= pos + 2; j++) { | |
if (levelboard[i][j] == LEVEL_INACTIVE1 || | |
levelboard[i][j] == LEVEL_INACTIVE2) | |
levelboard[i][j] = LEVEL_EMPTY; | |
} | |
} | |
for (int i = 0; i<LEVEL_HEIGHT; i++) { | |
for (int j = pos; j <= pos + 2; j++) { | |
if (j == pos + 2) | |
levelboard[i][j] = level; | |
else { | |
if (j == pos + 1) { | |
if (i == 2) | |
levelboard[i][j] = level; | |
} | |
else { | |
if (i != 3 && i != LEVEL_HEIGHT - 1) | |
levelboard[i][j] = level; | |
} | |
} | |
} | |
} | |
break; | |
case 5: | |
for (int i = 0; i<LEVEL_HEIGHT; i++) { | |
for (int j = pos; j <= pos + 2; j++) { | |
if (levelboard[i][j] == LEVEL_INACTIVE1 || | |
levelboard[i][j] == LEVEL_INACTIVE2) | |
levelboard[i][j] = LEVEL_EMPTY; | |
} | |
} | |
for (int i = 0; i<LEVEL_HEIGHT; i++) { | |
for (int j = pos; j <= pos + 2; j++) { | |
if (i == 0 || i == 2) | |
levelboard[i][j] = level; | |
else if (i == LEVEL_HEIGHT - 1) | |
levelboard[i][j] = level; | |
else { | |
if (i == 1 && j == pos) levelboard[i][j] = level; | |
else if (i == 3 && j == pos + 2) levelboard[i][j] = level; | |
} | |
} | |
} | |
break; | |
case 6: | |
for (int i = 0; i<LEVEL_HEIGHT; i++) { | |
for (int j = pos; j <= pos + 2; j++) { | |
if (levelboard[i][j] == LEVEL_INACTIVE1 || | |
levelboard[i][j] == LEVEL_INACTIVE2) | |
levelboard[i][j] = LEVEL_EMPTY; | |
} | |
} | |
for (int i = 0; i<LEVEL_HEIGHT; i++) { | |
for (int j = pos; j <= pos + 2; j++) { | |
if (i == 0 || i == 2) | |
levelboard[i][j] = level; | |
else if (i == LEVEL_HEIGHT - 1) | |
levelboard[i][j] = level; | |
if (i == 1 && j == pos) levelboard[i][j] = level; | |
else if (i == 3) { | |
if (j == pos) levelboard[i][j] = level; | |
else if (j == pos + 2) levelboard[i][j] = level; | |
} | |
} | |
} | |
break; | |
case 7: | |
for (int i = 0; i<LEVEL_HEIGHT; i++) { | |
for (int j = pos; j <= pos + 2; j++) { | |
if (levelboard[i][j] == LEVEL_INACTIVE1 || | |
levelboard[i][j] == LEVEL_INACTIVE2) | |
levelboard[i][j] = LEVEL_EMPTY; | |
} | |
} | |
for (int i = 0; i<LEVEL_HEIGHT; i++) { | |
for (int j = pos; j <= pos + 2; j++) { | |
if (j == pos + 2) levelboard[i][j] = level; | |
else if (j == pos) { | |
if (i != 3 && i != LEVEL_HEIGHT - 1) | |
levelboard[i][j] = level; | |
} | |
else if (j == pos + 1) { | |
if (i == 0) | |
levelboard[i][j] = level; | |
} | |
} | |
} | |
break; | |
case 8: | |
for (int i = 0; i<LEVEL_HEIGHT; i++) { | |
for (int j = pos; j <= pos + 2; j++) { | |
if (levelboard[i][j] == LEVEL_INACTIVE1 || | |
levelboard[i][j] == LEVEL_INACTIVE2) | |
levelboard[i][j] = LEVEL_EMPTY; | |
} | |
} | |
for (int i = 0; i<LEVEL_HEIGHT; i++) { | |
for (int j = pos; j <= pos + 2; j++) { | |
if (i == 0 || i == 2) | |
levelboard[i][j] = level; | |
else if (i == LEVEL_HEIGHT - 1) | |
levelboard[i][j] = level; | |
else if (i == 1 || i == 3) { | |
if (j == pos || j == pos + 2) | |
levelboard[i][j] = level; | |
} | |
} | |
} | |
break; | |
case 9: | |
for (int i = 0; i<LEVEL_HEIGHT; i++) { | |
for (int j = pos; j <= pos + 2; j++) { | |
if (levelboard[i][j] == LEVEL_INACTIVE1 || | |
levelboard[i][j] == LEVEL_INACTIVE2) | |
levelboard[i][j] = LEVEL_EMPTY; | |
} | |
} | |
for (int i = 0; i<LEVEL_HEIGHT; i++) { | |
for (int j = pos; j <= pos + 2; j++) { | |
if (j == pos + 2) | |
levelboard[i][j] = level; | |
else if (j == pos) { | |
if (i != 3 && i != LEVEL_HEIGHT - 1) | |
levelboard[i][j] = level; | |
} | |
else if (j == pos + 1) { | |
if (i == 0 || i == 2) | |
levelboard[i][j] = level; | |
} | |
} | |
} | |
break; | |
} | |
} | |
void show_levelboard() | |
{ | |
for (int i = 0; i<LEVEL_HEIGHT; i++) { | |
for (int j = 0; j<LEVEL_WIDTH; j++) { | |
gotoxy(i + LEVELXX + 1, 2 * j + LEVELYY + 7); | |
if (levelboard[i][j] == LEVEL_INACTIVE1) { | |
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 27); | |
printf("■"); | |
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 15); | |
} | |
else if (levelboard[i][j] == LEVEL_INACTIVE2) { | |
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 31); | |
printf("■"); | |
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 15); | |
} | |
else if (levelboard[i][j] == LEVEL_EMPTY) | |
printf(" "); | |
} | |
} | |
} | |
void complete_levelboard(int num) | |
{ | |
int level_show = 0; // 계속해서 계산될 값 | |
int zero_flag = FALSE; // 0 출력 가능 여부 확인 | |
// one~five는 만의 자리부터 일의 자리를 상징 | |
int one = num / 100; level_show = num % 100; | |
if (one>0) { | |
make_levelboard(one, 0); | |
zero_flag = TRUE; // 다음에 0출력 가능 | |
} | |
int two = level_show / 10; | |
if (zero_flag == TRUE) | |
make_levelboard(two, 3); | |
else { | |
if (two>0) { | |
make_levelboard(two, 3); | |
zero_flag = TRUE; | |
} | |
} | |
int three = level_show % 10; | |
if (zero_flag == TRUE) | |
make_levelboard(three, 6); | |
else { | |
if (three>0) { | |
make_levelboard(three, 6); | |
zero_flag = TRUE; | |
} | |
} | |
show_levelboard(); | |
} | |
// [메인 기능 함수들] | |
void start_function() // 블록이 내려올 때마다 호출되어야 하는 함수를 호출하는 함수 | |
{ | |
// 첫 블록을 만드는 함수, 기본 블록 + 쉐도우 블록을 만든다. | |
make_block(); | |
shadow_block(); | |
// 처음 게임화면 말고 출력되어야 할 게임 정보들 출력 | |
next_block(); | |
next2_block(); | |
score_board(); | |
joystick(); | |
next_level(); | |
print_level(); | |
allclear(); | |
} | |
// 화면을 그리는 함수 (이전 화면과 비교한 뒤 바뀐 부분만 그려주므로 깜박거림 x) | |
void draw_screen() | |
{ | |
// 처음 블록이 내려와야 하는 경우 | |
if (reset_flag == TRUE) | |
start_function(); | |
// 게임 화면을 출력한다. (비교 후 업데이트 방식) | |
for (int i = 0; i<HEIGHT; i++) { | |
for (int j = 0; j<WIDTH; j++) { | |
// 업데이트 된 화면과 이전 화면이 다른 경우가 발견되면 | |
if (screen_org[i][j] != screen_up[i][j]) | |
{ | |
// index가 가리키는 좌표로 간뒤 | |
gotoxy(i + 1, 2 * j + 1); | |
// 요소의 값에 따라 출력해준다. | |
switch (screen_up[i][j]) { | |
// 0일 경우 공백 2개로 출력 | |
case EMPTY: | |
case EMPTY_Q: // 궁극기의 경우 깜박거림을 위해서 | |
printf(" "); | |
break; | |
// 1일 경우 가로 벽 모양 출력 | |
case WALL_WIDTH: | |
printf("─"); | |
break; | |
// 11일 경우 세로 벽 모양 출력 | |
case WALL_HEIGHT: | |
printf("│"); | |
break; | |
// 111일 경우 왼쪽상단 벽 코너 모양 출력 | |
case WALL_CORNER1: | |
printf("┌"); | |
break; | |
// 112일 경우 우측상단 벽 코너 모양 출력 | |
case WALL_CORNER2: | |
printf("┐"); | |
break; | |
// 113일 경우 왼쪽하단 벽 코너 모양 출력 | |
case WALL_CORNER3: | |
printf("└"); | |
break; | |
// 114일 경우 우측하단 벽 코너 모양 출력 | |
case WALL_CORNER4: | |
printf("┘"); | |
break; | |
// 레벨업 출력 경우 | |
case LEVELUP: | |
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 26); | |
printf("■"); | |
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 15); | |
break; | |
// 2일 경우 블록이므로 블록 모양 출력 | |
case ACTIVE: | |
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 14); | |
printf("■"); | |
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 15); | |
break; | |
// -2일 경우 다 내려온 블록이므로 그 모양 출력 | |
case INACTIVE: | |
// color_flag에 따라 색깔이 달라지는데 이 경우는 | |
// 깜박거리는 기능 때 빨간색으로 바꿔주기 위함이다. | |
// 따라서 color_flag에 따라 기본 색깔과 빨간색이 변경된다. | |
if (color_flag == TRUE) | |
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 12); | |
else | |
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 11); | |
printf("▦"); | |
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 15); | |
break; | |
case SHADOW: | |
printf("□"); | |
break; | |
} | |
} | |
} | |
} | |
/*최신 화면을 screen_org에 업데이트 시킨다. | |
출력할 때 move_block이나 drop_block과 같은 다른 함수로 인해 screen_up이 계속 | |
바뀌기 때문에 나중에 screen_org와 비교하기 위해서이다.*/ | |
for (int i = 0; i<HEIGHT; i++) | |
for (int j = 0; j<WIDTH; j++) | |
screen_org[i][j] = screen_up[i][j]; | |
} | |
// screen_org와 screen_up을 처음에 초기화하는 함수 (그냥 입력하면 더러워서 이렇게) | |
void reset_screen() | |
{ | |
for (int i = 0; i<HEIGHT; i++) { | |
for (int j = 0; j<WIDTH; j++) { | |
screen_org[i][j] = FIRST; // 일단 screen_org는 FIRST(-1)로 전부 초기화 | |
// 그러는 이유는 화면을 그릴 때 screen_up과 비교를 하는데 screen_up에 나타나지 | |
// 않는 값이 FIRST(-1)이므로 이렇게 한다. 물론 -5,-7... 등이 될 수도 있음 | |
// 그 다음 screen_up 초기화 과정 | |
// 첫 번째 행이나 마지막 행은 전부 테두리이므로 | |
// 코너와 가로, 세로 모양 나눠서 초기화 한뒤 나머지 공백으로 초기화 | |
if (i == 0) { | |
if (j == 0) | |
screen_up[i][j] = WALL_CORNER1; | |
else if (j == WIDTH - 1) | |
screen_up[i][j] = WALL_CORNER2; | |
else | |
screen_up[i][j] = WALL_WIDTH; | |
} | |
// 그 행이 아닐 경우 | |
else if (i == HEIGHT - 1) { | |
if (j == 0) | |
screen_up[i][j] = WALL_CORNER3; | |
else if (j == WIDTH - 1) | |
screen_up[i][j] = WALL_CORNER4; | |
else | |
screen_up[i][j] = WALL_WIDTH; | |
} | |
else { | |
if (j == 0) | |
screen_up[i][j] = WALL_HEIGHT; | |
else if (j == WIDTH - 1) | |
screen_up[i][j] = WALL_HEIGHT; | |
else | |
screen_up[i][j] = EMPTY; | |
} | |
} | |
} | |
} | |
// 게임정보에 대한 리셋 함수 | |
void info_reset() | |
{ | |
// 처음부터 시작이므로 그에 대한 flag 활성화, 비활성화 | |
reset_flag = TRUE; | |
stop_flag = FALSE; | |
gameover = FALSE; | |
q_flag = FALSE; | |
// 점수, 레벨, 경험치, 속도, 없앤 줄 초기화 | |
score = 0; | |
level = 1; | |
percent = 0; | |
speed = 500; | |
check_line = 0; | |
} | |
// 줄이 꽉 찼나 체크하고 꽉 찬 줄은 없앤 후 나머지 내리는 함수. | |
void line_check() | |
{ | |
line_num = 0; // 줄 개수에 대한 변수 초기화 | |
line_flag = 0; // 한 줄이 모두 꽉차있는지에 대한 변수 초기화 | |
int start[29] = { 0 }; // 완성되는 줄이 시작하는 행 배열 변수 초기화 | |
int cover[29] = { 0 }; // 메궈야 하는 줄에 대한 행 배열 변수 초기화 | |
color_flag = FALSE; // 일단 콤보가 있는지 모르니 FALSE로 초기화 | |
int line_term = 0; // 줄 사이의 간격 변수 초기화 | |
// 맨 밑에서부터 맨 위까지 검사를 한다. | |
for (int i = HEIGHT - 2; i>0; i--) | |
{ | |
for (int j = 1; j<WIDTH - 1; j++) | |
{ | |
// 행 기준으로 열을 모두 검사하여 하나라도 INACTIVE가 아닌 경우가 | |
// 있으면 line_flag가 FALSE가 되어 줄 개수는 증가하지 않는다. | |
if (screen_up[i][j] == INACTIVE) | |
line_flag = TRUE; | |
else { | |
cover[i] = i; | |
line_flag = FALSE; | |
break; | |
} | |
} | |
// 그러나 모두 INACTIVE가 되어 line_flag가 TRUE가 된다면 | |
// 처음 시작하는 행의 index를 start에 저장한 후 | |
// 줄 개수에 대한 2가지 변수 line_num과 check_line을 증가시킨다. | |
if (line_flag == TRUE) { | |
start[i] = i; // 꽉찬 행을 계속 기록 | |
// line_num : 블록이 내려올 때까지에 대해 꽉찬 줄 개수 조사 | |
// check_line : 한 레벨의 게임이 끝날 때까지 없어진 줄 개수 조사 | |
line_num++; | |
check_line++; | |
} | |
} | |
// 꽉찬 줄이 1개 이상이라면 그에 대한 처리를 해줘야 한다. | |
if (line_num>0) { | |
/*깜박거리는 부분 | |
EMPTY와 INACTIVE로 번갈아 초기화하면서 화면을 그리는데 딜레이를 주면 | |
깜박거리는 효과를 구현할 수 있다. | |
대신 SHADOW 부분은 건너뛰고 나타낸다. (겹치면 사라질 수 있기 때문에)*/ | |
for (int i = HEIGHT - 2; i >= 1; i--) { | |
if (start[i] != 0) { | |
for (int j = 1; j<WIDTH - 1; j++) { | |
if (screen_up[start[i]][j] != SHADOW) | |
screen_up[start[i]][j] = EMPTY; | |
} | |
} | |
} | |
draw_screen(); | |
Sleep(150); | |
// 깜박거릴 때 색깔이 바뀌어야 하기 때문에 flag 활성화 | |
// 맨 처음에 활성화하지 않는 이유는 EMPTY라서 쓸모가 없기 때문에 | |
color_flag = TRUE; | |
for (int i = HEIGHT - 2; i >= 1; i--) { | |
if (start[i] != 0) { | |
for (int j = 1; j<WIDTH - 1; j++) { | |
if (screen_up[start[i]][j] != SHADOW) | |
screen_up[start[i]][j] = INACTIVE; | |
} | |
} | |
} | |
draw_screen(); | |
Sleep(150); | |
for (int i = HEIGHT - 2; i >= 1; i--) { | |
if (start[i] != 0) { | |
for (int j = 1; j<WIDTH - 1; j++) { | |
if (screen_up[start[i]][j] != SHADOW) | |
screen_up[start[i]][j] = EMPTY; | |
} | |
} | |
} | |
draw_screen(); | |
Sleep(150); | |
for (int i = HEIGHT - 2; i >= 1; i--) { | |
if (start[i] != 0) { | |
for (int j = 1; j<WIDTH - 1; j++) { | |
if (screen_up[start[i]][j] != SHADOW) | |
screen_up[start[i]][j] = INACTIVE; | |
} | |
} | |
} | |
draw_screen(); | |
Sleep(150); | |
for (int i = HEIGHT - 2; i >= 1; i--) { | |
if (start[i] != 0) { | |
for (int j = 1; j<WIDTH - 1; j++) { | |
if (screen_up[start[i]][j] != SHADOW) | |
screen_up[start[i]][j] = EMPTY; | |
} | |
} | |
} | |
draw_screen(); | |
Sleep(150); | |
// 깜박거리는 효과 끝났으므로 다시 flag 비활성화 | |
color_flag = FALSE; | |
// 꽉찬 줄을 지운다음 | |
// 그 바로 위부터 처음 행까지 INACTIVE를 조사해서 지운 줄 개수만큼 행에 더해 | |
// 내린 후에 원래의 INACTIVE 자리를 EMPTY로 만든다. | |
for (int i = HEIGHT - 2; i >= 1; i--) { | |
if (start[i] != 0) { | |
for (int j = 1; j<WIDTH - 1; j++) { | |
if (screen_up[start[i]][j] != SHADOW) | |
screen_up[start[i]][j] = EMPTY; | |
} | |
} | |
} | |
for (int i = HEIGHT - 2; i >= 1; i--) { | |
if (cover[i] != 0 && (cover_where()>cover[i])) { | |
line_term = cover_where() - cover[i]; | |
for (int j = 1; j<WIDTH - 1; j++) { | |
if (screen_up[cover[i]][j] == INACTIVE) { | |
screen_up[cover[i]][j] = EMPTY; | |
screen_up[cover[i] + line_term][j] = INACTIVE; | |
} | |
} | |
} | |
} | |
// 한 줄 없애면 받는 기본 점수 | |
score += line_num * 100; | |
// 콤보라면 보너스 점수 | |
if (line_num>1) score += line_num * 30; | |
// 레벨에 맞는 경험치 계산 | |
// 레벨 10의 경우는 계산방식 다르기 때문에 예외처리 | |
if (level != 10) { | |
if ((check_line * 100) / (level * 5 + 5)<100) | |
percent = (check_line * 100) / (level * 5 + 5); | |
else | |
percent = 100; | |
} | |
else { | |
if ((check_line * 100) / 100<100) | |
percent = check_line * 100 / 100; | |
else | |
percent = 100; | |
} | |
// 바로 업데이트! | |
score_board(); | |
next_level(); | |
} | |
} | |
// 블록이 차 있는 줄의 바로 위 행 index를 리턴하는 함수 | |
int cover_where() | |
{ | |
int empty_flag = 0; // 한 줄이 완전히 비어있는지 확인한는 flag | |
int empty_start = 0; // 제일 처음으로 완전히 비어있는 줄 | |
for (int i = HEIGHT - 2; i >= 1; i--) { | |
for (int j = 1; j<WIDTH - 1; j++) { | |
// 제일 처음으로 완전히 비어있는 줄 찾기 | |
// SHADOW를 포함하는 이유는 포함하지 않는다면 비어있는 줄로 안보기 때문이다. | |
if (screen_up[i][j] == EMPTY || screen_up[i][j] == SHADOW) | |
empty_flag = TRUE; | |
else { | |
// 한번이라도 FALSE라면 바로 빠져나간다. | |
empty_flag = FALSE; | |
break; | |
} | |
} | |
// 한 줄이 완전히 비어있다면 empty_start에 그 index 저장후 빠져나간다. | |
if (empty_flag == TRUE) { | |
empty_start = i; | |
break; | |
} | |
} | |
return empty_start; | |
} | |
// 현재 레벨 체크하는 함수 | |
void check_level() | |
{ | |
/*각 레벨에 따라서 경험치, 레벨, 속도를 조절한다. | |
각 레벨마다 레벨 업 하기 위해서 없애야 할 줄 개수가 다르다. | |
경험치는 그 조건과는 관계없이 계속 오른다. | |
만약 레벨 업의 조건을 충족시켰다면 레벨, 속도가 오르고 | |
check_levelup() 함수를 호출하여 레벨 업 메시지를 출력한 뒤 | |
다음 단계의 게임으로 이동시킨다*/ | |
switch (level) | |
{ | |
// 레벨 10의 경우는 계산방식 다르므로 예외처리 | |
if (level != 10) { | |
if ((check_line * 100) / (level * 5 + 5)<100) | |
percent = (check_line * 100) / (level * 5 + 5); | |
else | |
percent = 100; | |
} | |
case 1: | |
if (check_line >= 10) { | |
level++; | |
speed -= 20; | |
check_levelup(); | |
} | |
break; | |
case 2: | |
if (check_line >= 15) { | |
level++; | |
speed -= 20; | |
check_levelup(); | |
} | |
break; | |
case 3: | |
if (check_line >= 20) { | |
level++; | |
speed -= 20; | |
check_levelup(); | |
} | |
break; | |
case 4: | |
if (check_line >= 25) { | |
level++; | |
speed -= 20; | |
check_levelup(); | |
} | |
break; | |
case 5: | |
if (check_line >= 30) { | |
level++; | |
speed -= 20; | |
check_levelup(); | |
} | |
break; | |
case 6: | |
if (check_line >= 35) { | |
level++; | |
speed -= 20; | |
check_levelup(); | |
} | |
break; | |
case 7: | |
if (check_line >= 40) { | |
level++; | |
speed -= 20; | |
check_levelup(); | |
} | |
break; | |
case 8: | |
if (check_line >= 45) { | |
level++; | |
speed -= 20; | |
check_levelup(); | |
} | |
break; | |
case 9: | |
if (check_line >= 50) { | |
level++; | |
speed -= 20; | |
check_levelup(); | |
} | |
break; | |
case 10: | |
if ((check_line * 100) / 100<100) | |
percent = (check_line * 100) / 100; | |
else | |
percent = 100; | |
if (check_line >= 100) { | |
level++; | |
speed -= 20; | |
check_levelup(); | |
} | |
break; | |
} | |
} | |
// 키 입력에 따른 기능 함수 | |
void check_key() | |
{ | |
key = 0; // 키값 초기화 | |
if (kbhit()) // 키 버퍼에 입력값이 있으면 | |
{ | |
// 그 값을 조사한다. | |
key = getch(); | |
// 그 값이 KEY(224)이고 stop_flag가 FALSE, 즉 P를 누르지 않아ㅆ다면 | |
if (key == KEY && stop_flag == FALSE) | |
{ | |
// 입력한 값을 한번 더 조사한다. | |
// 왜냐하면 KEY(224)이므로 값이 하나 더 있기 때문이다. | |
key = getch(); | |
switch (key) | |
{ | |
// LEFT가 입력됬다면 한칸 왼쪽에 대해 충돌 여부 확인한 뒤 | |
// 일반 블록과 쉐도우 블록을 왼쪽으로 옮긴다. | |
case LEFT: | |
if (!check_crash(rotation, bx, by - 1)) { | |
move_block(LEFT); | |
move_shadow_block(LEFT); | |
} | |
break; | |
// RIGHT가 입력됬다면 한칸 오른쪽에 대해 충돌 여부 확인한 뒤 | |
// 일반 블록과 쉐도우 블록을 오른쪽으로 옮긴다. | |
case RIGHT: | |
if (!check_crash(rotation, bx, by + 1)) { | |
move_block(RIGHT); | |
move_shadow_block(RIGHT); | |
} | |
break; | |
// DOWN이 입력됬다면 한칸 아래쪽에 대해 충돌 여부 확인한 뒤 | |
// 일반 블록만 아래쪽으로 옮긴다. (쉐도우 블록은 항상 최하단) | |
case DOWN: | |
if (!check_crash(rotation, bx + 1, by)) | |
move_block(DOWN); | |
break; | |
// UP이 입력됬다면 다음 회전에 대해 충돌 여부 확인한 뒤 | |
// 일반 블록과 쉐도우 블록을 회전시킨다. | |
case UP: | |
if (!check_crash((rotation + 1) % 4, bx, by)) { | |
move_block(UP); | |
move_shadow_block(UP); | |
} | |
break; | |
} | |
} | |
// 입력한 키값이 방향키가 아니라 스페이스바이고 P를 누르지 않았다면 | |
else if (key == SPACE && stop_flag == FALSE) | |
{ | |
// 충돌할 때까지 계속 블록을 내린다. | |
while (!check_crash(rotation, bx + 1, by)) | |
move_block(DOWN); | |
space_flag = TRUE; // 스페이스를 눌렀으므로 space_flag 활성화 | |
if (bx != 1) | |
score += 5; // 보너스 점수 | |
allclear(); | |
} | |
// 입력한 키값이 P이면 PAUSE 이므로 멈춘다. | |
// stop_flag가 TRUE라면 (멈춘 경우) FALSE로 바꾼다. | |
// stop_flag가 FALSE라면 (멈추지 않은 경우) TRUE로 바꾼다. | |
else if (key == P || key == p) { | |
if (stop_flag == FALSE) | |
stop_flag = TRUE; | |
else | |
stop_flag = FALSE; | |
} | |
// 입력한 키값이 R이라면 RESET 이므로 리셋 시킨다. | |
else if ((key == R || key == r) && stop_flag == FALSE) { | |
// 점수판, 경험치판 리셋 | |
reset_message(); | |
//reset_scoreboard(); | |
//reset_levelboard(); | |
//reset_screen(); // 화면 전체를 리셋 | |
//info_reset(); // 게임 정보를 리셋 | |
} | |
// 입력한 키값이 Q라면 궁극기 실행 | |
else if ((key == Q || key == q) && stop_flag == FALSE) { | |
if (q_flag == TRUE) clear_skill(); | |
} | |
// 입력한 키값이 ESC라면 게임종료. | |
else if (key == ESC && stop_flag == FALSE) | |
exit(0); | |
} | |
while (kbhit())getch(); // 키 버퍼비우기 (사용자가 키를 여러번 칠 수 있기 때문에) | |
} | |
// 궁극기 함수 | |
void clear_skill() | |
{ | |
// 테두리 빼고 화면을 전부 조사해서 기본 블록이나 쉐도우 블록이 아니라면 | |
// 즉, INACTIVE인 모든 블록을 깜박거리는 효과 적용해서 없앤다. | |
// EMPTY_Q를 사용하는 이유는 EMPTY를 사용하면 이전 블록이 있었던 위치를 기억 못하기 때문 | |
for (int i = 1; i<HEIGHT - 1; i++) { | |
for (int j = 1; j<WIDTH - 1; j++) { | |
if (screen_up[i][j] == INACTIVE) | |
screen_up[i][j] = EMPTY_Q; | |
} | |
} | |
draw_screen(); | |
Sleep(150); | |
// 깜박거릴 때 색깔 바꾸기 위해서 | |
color_flag = TRUE; | |
for (int i = 1; i<HEIGHT - 1; i++) { | |
for (int j = 1; j<WIDTH - 1; j++) { | |
if (screen_up[i][j] == EMPTY_Q) | |
screen_up[i][j] = INACTIVE; | |
} | |
} | |
draw_screen(); | |
Sleep(150); | |
for (int i = 1; i<HEIGHT - 1; i++) { | |
for (int j = 1; j<WIDTH - 1; j++) { | |
if (screen_up[i][j] == INACTIVE) | |
screen_up[i][j] = EMPTY_Q; | |
} | |
} | |
draw_screen(); | |
Sleep(150); | |
for (int i = 1; i<HEIGHT - 1; i++) { | |
for (int j = 1; j<WIDTH - 1; j++) { | |
if (screen_up[i][j] == EMPTY_Q) | |
screen_up[i][j] = INACTIVE; | |
} | |
} | |
draw_screen(); | |
Sleep(150); | |
// 마지막엔 그냥 EMPTY를 써서 원래대로 만든다. | |
for (int i = 1; i<HEIGHT - 1; i++) { | |
for (int j = 1; j<WIDTH - 1; j++) { | |
if (screen_up[i][j] == INACTIVE) | |
screen_up[i][j] = EMPTY; | |
} | |
} | |
// 깜박거리는 효과 끝났으니 색깔 원래대로. | |
color_flag = FALSE; | |
// 궁극기 썼으니 3000점 소모. | |
score -= 3000; | |
reset_scoreboard(); | |
// 바로 업데이트 | |
score_board(); | |
allclear(); | |
} | |
// 블록이 내려올 자리를 미리 그림자 형식으로 보여주는 함수 | |
void shadow_block() | |
{ | |
// move_block()으로 업데이트 된 index를 sx,sy에 업데이트. | |
// sx와 sy를 따로 쓰는 이유는 가독성과 그 값을 직접 변경해야 하는 경우가 | |
// 있기 때문이다. | |
sx = bx; | |
sy = by; | |
// sx를 바닥이나 블록에 충돌할 때까지 보내기 (스페이스 누른 경우와 동일) | |
while (TRUE) | |
{ | |
if (!check_crash(rotation, sx + 1, sy)) sx++; | |
else break; | |
} | |
// 그렇게 sx를 끝까지 내린다음 그곳에 shadow 블록을 만든다. | |
// 이렇게 shadow block을 만들 수 있다. | |
for (int i = 0; i<4; i++) { | |
for (int j = 0; j<4; j++) { | |
if (block[kind][rotation][i][j] == 1) | |
screen_up[sx + i][sy + j] = SHADOW; | |
} | |
} | |
} | |
// 쉐도우 블록을 움직이는 함수 | |
void move_shadow_block(int dir) | |
{ | |
switch (dir) | |
{ | |
// 이미 move_block()으로 bx, by가 바뀐 상태이므로 | |
// 회전이 아닌 방향키에 대한 경우는 처리방식이 모두 동일하다. | |
// 원래 자리를 EMPTY로 처리한 후 shadow_block()함수 호출. | |
case LEFT: | |
case RIGHT: | |
case DOWN: | |
for (int i = 0; i<4; i++) { | |
for (int j = 0; j<4; j++) { | |
if (block[kind][rotation][i][j] == 1) | |
screen_up[sx + i][sy + j] = EMPTY; | |
} | |
} | |
shadow_block(); | |
break; | |
// 단, 회전하는 경우라면 move_block()에서 rotation의 값이 변경되기 전, | |
// shadow_rotation에 이전 값을 저장하였기 때문에 원래 자리를 | |
// EMPTY로 처리할 수 있다. 이후 마찬가지로 shadow_block() 함수 호출. | |
case UP: | |
for (int i = 0; i<4; i++) { | |
for (int j = 0; j<4; j++) { | |
if (block[kind][shadow_rotation][i][j] == 1) | |
screen_up[sx + i][sy + j] = EMPTY; | |
} | |
} | |
shadow_block(); | |
break; | |
} | |
} | |
// 처음 블록이 내려올 때의 그 기본 블록을 만드는 함수 (ACTIVE로 초기화) | |
void make_block() | |
{ | |
// 블록이 다시 내려오는 경우이므로 kind,rotation,bx,by를 다시 초기화한다. | |
reset(); | |
// 만약 이 블록이 충돌하지 않을 경우 | |
if (!check_crash(rotation, bx, by)) { | |
for (int i = 0; i<4; i++) { | |
for (int j = 0; j<4; j++) { | |
if (block[kind][rotation][i][j] == 1) { // 블록의 요소 값이 1인 경우만 | |
// 미리 설정해놓은 가로 index와 세로 index를 이용해서 screen_up을 | |
// ACTIVE로 초기화 | |
// 간단하게 말해서 블록의 종류에 따라 화면에 표시하는 것임 | |
screen_up[bx + i][by + j] = ACTIVE; | |
} | |
} | |
} | |
} | |
// 내려와야 할 블록을 만들었으니 이젠 처음이 아니므로 다시 FALSE로 초기화 | |
reset_flag = FALSE; | |
} | |
// 블록을 키보드 방향(dir)에 따라 움직이는 함수 | |
void move_block(int dir) | |
{ | |
switch (dir) | |
{ | |
// 왼쪽 방향키를 누른 경우 | |
// 원래 자리를 다 비우고 왼쪽으로 한칸 옮겨서 다시 ACTIVE시킨다. | |
case LEFT: | |
for (int i = 0; i<4; i++) { | |
for (int j = 0; j<4; j++) { | |
if (block[kind][rotation][i][j] == 1) | |
screen_up[bx + i][by + j] = EMPTY; | |
} | |
} | |
for (int i = 0; i<4; i++) { | |
for (int j = 0; j<4; j++) { | |
if (block[kind][rotation][i][j] == 1) | |
screen_up[bx + i][by + j - 1] = ACTIVE; | |
} | |
} | |
by--; // 그 후 왼족 index 증가 | |
break; | |
// 오른족 방향키를 누른 경우 | |
// 원래 자리를 다 비우고 오른쪽으로 한칸 옮겨서 다시 ACTIVE 시킨다 | |
case RIGHT: | |
for (int i = 0; i<4; i++) { | |
for (int j = 0; j<4; j++) { | |
if (block[kind][rotation][i][j] == 1) | |
screen_up[bx + i][by + j] = EMPTY; | |
} | |
} | |
for (int i = 0; i<4; i++) { | |
for (int j = 0; j<4; j++) { | |
if (block[kind][rotation][i][j] == 1) | |
screen_up[bx + i][by + j + 1] = ACTIVE; | |
} | |
} | |
by++; // 그 후 오른쪽 index 증가 | |
break; | |
// 아래쪽 방향키를 누른 경우 | |
// 원래 자리를 다 비우고 아래쪽으로 한칸 옮겨서 다시 ACTIVE 시킨다. | |
case DOWN: | |
for (int i = 0; i<4; i++) { | |
for (int j = 0; j<4; j++) { | |
if (block[kind][rotation][i][j] == 1) | |
screen_up[bx + i][by + j] = EMPTY; | |
} | |
} | |
for (int i = 0; i<4; i++) { | |
for (int j = 0; j<4; j++) { | |
if (block[kind][rotation][i][j] == 1) | |
screen_up[bx + i + 1][by + j] = ACTIVE; | |
} | |
} | |
bx++; // 그 후 아래쪽 index 증가 | |
break; | |
// 위쪽 방향키를 누른 경우 | |
// 원래 자리를 다 비우고 다음 회전 모양으로 전환하여 ACTIVE 시킨다. | |
case UP: | |
for (int i = 0; i<4; i++) { | |
for (int j = 0; j<4; j++) { | |
if (block[kind][rotation][i][j] == 1) | |
screen_up[bx + i][by + j] = EMPTY; | |
} | |
} | |
// move_shadow_block() 함수에서 사용해야 하므로 이전 회전값 저장. | |
shadow_rotation = rotation; | |
// 회전값 다음으로 초기화 | |
rotation = (rotation + 1) % 4; | |
// 4로 나눈 나머지를 사용하는 이유는 0~3까지만 rotation의 index가 허용되기 때문. | |
for (int i = 0; i<4; i++) { | |
for (int j = 0; j<4; j++) { | |
if (block[kind][rotation][i][j] == 1) | |
screen_up[bx + i][by + j] = ACTIVE; | |
} | |
} | |
break; | |
} | |
} | |
// 블록이 한 칸 내려오는 함수 | |
void drop_block() | |
{ | |
// 충돌이 발생하지 않으면 한 칸 내리기 | |
if (!check_crash(rotation, bx + 1, by)) | |
move_block(DOWN); | |
// 츙돌이 발생하면 블록을 그자리에 굳혀서 INACTIVE로 만들기 | |
else { | |
for (int i = 0; i<4; i++) { | |
for (int j = 0; j<4; j++) { | |
if (block[kind][rotation][i][j] == 1) | |
screen_up[bx + i][by + j] = INACTIVE; | |
} | |
} | |
reset_flag = TRUE; // 블록이 다 내려왔으니 새로운 블록이 필요하다. | |
// 따라서 reset_flag를 킨다. | |
// 블록을 굳혇는데 행의 index가 첫 행이라면 게임이 끝난것이므로 | |
// 게임오버 flag인 gameover 변수를 TRUE 값으로 변경한다. | |
if (bx == 1) gameover = TRUE; | |
} | |
} | |
// 블록이 움직일 때 충돌 여부를 확인하는 함수 | |
// 이 함수를 이용해서 모든 충돌여부를 계산하므로 모체가 되는 함수. | |
int check_crash(int rotation, int x, int y) | |
{ | |
for (int i = 0; i<4; i++) | |
{ | |
for (int j = 0; j<4; j++) { | |
// 다음에 올 블록이 화면의 빈 공간이 아닌 부분과 겹치게 되면 | |
// 단 ACTIVE와 겹칠 경우는 제외한다. (왜냐하면 바로 이전 블록이므로) | |
// 참을 리턴한다. | |
if (block[kind][rotation][i][j] == 1 && screen_up[x + i][y + j]>EMPTY) | |
return TRUE; | |
} | |
} | |
return FALSE; | |
} | |
// 처음 조건 값으로 초기화 시키는 함수 | |
// 따라서 블록을 처음 만드는 make_block()에서 호출한다. | |
void reset() | |
{ | |
rotation = 0; // 회전종류는 제일 첫 번째 (기본, 회전 안한 상태) | |
bx = 1; // 가로 index=1 | |
by = WIDTH / 2 - 1; // 세로 index=WIDTH/2-1 | |
kind = next_kind; // 종류를 랜덤으로 설정 | |
next_kind = next2_kind; // 다음 종류를 생성 | |
next2_kind = rand() % KIND; // 다다음 종류를 생성 | |
space_flag = FALSE; // 스페이스 flag 초기화 (처음이므로 space 쓴 후) | |
} | |
// [어떤 것을 표시해주는 출력 함수들] | |
// 게임을 깬 것에 대한 메시지 출력 함수 | |
void win_game() | |
{ | |
system("cls"); | |
// over_message() 함수, 즉 게임이 끝난 경우와 똑같은 메시지 출력. | |
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 14); | |
gotoxy(PAUSEX, PAUSEY); | |
printf("▣▣▣▣▣▣▣▣▣▣▣▣▣▣▣▣"); | |
gotoxy(PAUSEX + 1, PAUSEY); | |
printf("▣ ▣"); | |
gotoxy(PAUSEX + 2, PAUSEY); | |
printf("▣ CONGRATULATION!! ▣ "); | |
gotoxy(PAUSEX + 3, PAUSEY); | |
printf("▣ ▣"); | |
gotoxy(PAUSEX + 4, PAUSEY); | |
printf("▣ 게임에서 승리하였습니다! ▣", score); | |
gotoxy(PAUSEX + 5, PAUSEY); | |
printf("▣ ▣"); | |
gotoxy(PAUSEX + 6, PAUSEY); | |
printf("▣ 게임을 끝내시려면 ▣"); | |
gotoxy(PAUSEX + 7, PAUSEY); | |
printf("▣ ▣"); | |
gotoxy(PAUSEX + 8, PAUSEY); | |
printf("▣ 아무키나 눌러주세요! ▣"); | |
gotoxy(PAUSEX + 9, PAUSEY); | |
printf("▣ ▣"); | |
gotoxy(PAUSEX + 10, PAUSEY); | |
printf("▣▣▣▣▣▣▣▣▣▣▣▣▣▣▣▣"); | |
// 아무키나 입력 받는다. | |
getch(); | |
// 입력받으면 게임 종료. | |
exit(0); | |
} | |
// 레벨 업에 따른 메시지 처리 함수 | |
void check_levelup() | |
{ | |
// 모든 레벨을 깻으면 게임을 깬것이기 때문에 그에 대한 함수 호출. | |
if (level>10) win_game(); | |
// 메시지를 출력하기 위해 화면을 리셋시킨 후. | |
// 처음 시작이 아니므로 reset_flag를 FALSE로 초기화 한 다음 | |
// draw_screen()으로 화면을 출력한다. | |
reset_screen(); | |
reset_flag = FALSE; | |
draw_screen(); | |
// draw_screen()에는 처음 시작하는 경우에만 점수판과 퍼센트를 표시하기 | |
// 때문에 직접적으로 호출을 해줘서 출력해야 한다. | |
next_level(); | |
score_board(); | |
reset_levelboard(); // 경험치판 초기화 | |
levelup_format(); | |
draw_screen(); | |
Sleep(1000); // 1초 정도의 딜레이를 준뒤 | |
// 다시 화면을 리셋시킨 후 이젠 처음부터 시작해야 하므로 | |
// reset_flag를 TRUE로 바꾼 뒤 레벨 업에 따라 초기화 해야 하는 | |
// check_line(제거 된 줄 개수)과 percent(경험치)를 초기화 시킨다. | |
reset_screen(); | |
reset_flag = TRUE; | |
check_line = 0; | |
percent = 0; | |
while (kbhit()) getch(); // 키 버퍼 비우기 (다음 게임 화면에 영향 안주기 위해서) | |
} | |
// 현재 레벨 출력함수 | |
void print_level() | |
{ | |
gotoxy(27, 10); | |
printf("현재 레벨 : [%d]", level); | |
} | |
// 조작법 표시해주는 함수 | |
void joystick() | |
{ | |
gotoxy(JOYX, JOYY); | |
printf(" △ 회전"); | |
gotoxy(JOYX + 2, JOYY); | |
printf("◁ ▷ 왼쪽/오른쪽"); | |
gotoxy(JOYX + 4, JOYY); | |
printf(" ▽ 아래쪽"); | |
gotoxy(JOYX + 6, JOYY); | |
printf(" SPACE BAR 한번에 내리기[추가점수]"); | |
gotoxy(JOYX + 8, JOYY); | |
printf(" P 일시정지"); | |
gotoxy(JOYX + 10, JOYY); | |
printf(" ESC 게임종료"); | |
gotoxy(JOYX + 12, JOYY); | |
printf(" R 다시시작[레벨/점수 리셋]"); | |
gotoxy(JOYX + 14, JOYY); | |
printf(" Q 궁극기[3000점 소모]"); | |
} | |
// 점수를 표시해주는 함수 | |
void score_board() | |
{ | |
gotoxy(SCOREX, SCOREY); | |
printf("┌─현재점수──────────┐"); | |
gotoxy(SCOREX + 1, SCOREY); | |
printf("│ │"); | |
gotoxy(SCOREX + 2, SCOREY); | |
printf("│ │"); | |
gotoxy(SCOREX + 3, SCOREY); | |
printf("│ │"); | |
gotoxy(SCOREX + 4, SCOREY); | |
printf("│ │"); | |
gotoxy(SCOREX + 5, SCOREY); | |
printf("│ │"); | |
gotoxy(SCOREX + 6, SCOREY); | |
printf("└───────────────┘"); | |
complete_scoreboard(score); | |
} | |
// 다음레벨 까지 퍼센트 표시하는 함수 | |
void next_level() | |
{ | |
gotoxy(LEVELX, LEVELY); | |
printf("┌─경험치───────────┐"); | |
gotoxy(LEVELX + 1, LEVELY); | |
printf("│ │"); | |
gotoxy(LEVELX + 2, LEVELY); | |
printf("│ │"); | |
gotoxy(LEVELX + 3, LEVELY); | |
printf("│ │"); | |
gotoxy(LEVELX + 4, LEVELY); | |
printf("│ │"); | |
gotoxy(LEVELX + 5, LEVELY); | |
printf("│ │"); | |
gotoxy(LEVELX + 6, LEVELY); | |
printf("└───────────────┘"); | |
gotoxy(LEVELX + 1, LEVELY + 23); | |
printf(" ■"); | |
gotoxy(LEVELX + 2, LEVELY + 23); | |
printf(" □ ■"); | |
gotoxy(LEVELX + 3, LEVELY + 23); | |
printf(" ■ "); | |
gotoxy(LEVELX + 4, LEVELY + 23); | |
printf(" ■ □ "); | |
gotoxy(LEVELX + 5, LEVELY + 23); | |
printf(" ■ "); | |
complete_levelboard(percent); | |
} | |
// 다음에 올 블럭이 뭔지 표시해주는 함수 | |
void next_block() | |
{ | |
gotoxy(SHOWX, SHOWY); | |
printf("┌──────┐\n"); | |
gotoxy(SHOWX + 1, SHOWY); | |
printf("│ │"); | |
gotoxy(SHOWX + 2, SHOWY); | |
printf("│ │"); | |
gotoxy(SHOWX + 3, SHOWY); | |
printf("│ │"); | |
gotoxy(SHOWX + 4, SHOWY); | |
printf("│ │"); | |
gotoxy(SHOWX + 5, SHOWY); | |
printf("│ │"); | |
gotoxy(SHOWX + 6, SHOWY); | |
printf("│ │"); | |
gotoxy(SHOWX + 7, SHOWY); | |
printf("└──────┘"); | |
gotoxy(SHOWX + 2, SHOWY + 4); | |
printf("다음 블록"); | |
for (int i = 0; i<4; i++) | |
{ | |
for (int j = 0; j<4; j++) | |
{ | |
if (block[next_kind][0][i][j] == 1) { | |
gotoxy(SHOWX + 4 + i, SHOWY + 5 + 2 * j); | |
printf("■"); | |
} | |
} | |
printf("\n"); | |
} | |
} | |
// 다다음에 올 블럭이 뭔지 표시해주는 함수 | |
void next2_block() | |
{ | |
gotoxy(SHOWX, SHOWYY); | |
printf("┌──────┐\n"); | |
gotoxy(SHOWX + 1, SHOWYY); | |
printf("│ │"); | |
gotoxy(SHOWX + 2, SHOWYY); | |
printf("│ │"); | |
gotoxy(SHOWX + 3, SHOWYY); | |
printf("│ │"); | |
gotoxy(SHOWX + 4, SHOWYY); | |
printf("│ │"); | |
gotoxy(SHOWX + 5, SHOWYY); | |
printf("│ │"); | |
gotoxy(SHOWX + 6, SHOWYY); | |
printf("│ │"); | |
gotoxy(SHOWX + 7, SHOWYY); | |
printf("└──────┘"); | |
gotoxy(SHOWX + 2, SHOWYY + 3); | |
printf("다다음 블록"); | |
for (int i = 0; i<4; i++) | |
{ | |
for (int j = 0; j<4; j++) | |
{ | |
if (block[next2_kind][0][i][j] == 1) { | |
gotoxy(SHOWX + 4 + i, SHOWYY + 5 + 2 * j); | |
printf("■"); | |
} | |
} | |
printf("\n"); | |
} | |
} | |
// 궁극기 on/off 표시해주는 함수 | |
void allclear() | |
{ | |
gotoxy(SKILLX, SKILLY); | |
printf("┌─────┐\n"); | |
gotoxy(SKILLX + 1, SKILLY); | |
printf("│ │"); | |
gotoxy(SKILLX + 2, SKILLY); | |
printf("│ │"); | |
gotoxy(SKILLX + 3, SKILLY); | |
printf("│ │"); | |
gotoxy(SKILLX + 4, SKILLY); | |
printf("│ │"); | |
gotoxy(SKILLX + 5, SKILLY); | |
printf("│ │"); | |
gotoxy(SKILLX + 6, SKILLY); | |
printf("│ │"); | |
gotoxy(SKILLX + 7, SKILLY); | |
printf("└─────┘"); | |
if (score >= 3000) { | |
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 14); | |
gotoxy(SKILLX + 2, SKILLY + 3); | |
printf("궁극기(Q)"); | |
gotoxy(SKILLX + 4, SKILLY + 4); | |
q_flag = TRUE; | |
printf("★ON★"); | |
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 15); | |
} | |
else { | |
gotoxy(SKILLX + 2, SKILLY + 3); | |
printf("궁극기(Q)"); | |
gotoxy(SKILLX + 4, SKILLY + 5); | |
q_flag = FALSE; | |
printf("OFF"); | |
} | |
} | |
// 게임의 패치내역을 말해주는 함수 | |
void patchnote() | |
{ | |
system("mode con:cols=80 lines=37"); | |
system("cls"); | |
gotoxy(1, 10); | |
printf("※ 패치내역 ※"); | |
// 대형 패치 : 옥색, 소형 패치 : 노란색 | |
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 11); | |
gotoxy(PATCHX, PATCHY); | |
printf("ver 0.1.1 : 기본 테트리스 기능과 간단한 UI"); | |
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 14); | |
gotoxy(PATCHX + 2, PATCHY); | |
printf("ver 0.1.2 : 상태에 따른 블록의 모양 개선"); | |
gotoxy(PATCHX + 4, PATCHY); | |
printf("ver 0.1.3 : for문을 이용한 키 입력 처리 방식 개선"); | |
gotoxy(PATCHX + 6, PATCHY); | |
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 11); | |
printf("ver 0.2.1 : 쉐도우 블록 구현을 통한 플레이 환경 개선"); | |
gotoxy(PATCHX + 8, PATCHY); | |
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 14); | |
printf("ver 0.2.2 : 점수판, 경험치, 레벨 시스템 추가"); | |
gotoxy(PATCHX + 10, PATCHY); | |
printf("ver 0.2.3 : 블록 상태에 따른 색깔 및 다음 블록 출력"); | |
gotoxy(PATCHX + 12, PATCHY); | |
printf("ver 0.2.4 : 블록의 한 줄이 사라질 때 깜박거림 기능 추가"); | |
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 11); | |
gotoxy(PATCHX + 14, PATCHY); | |
printf("ver 0.3.1 : 궁극기 기능 추가"); | |
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 14); | |
gotoxy(PATCHX + 16, PATCHY); | |
printf("ver 0.3.2 : 사라진 줄에 대해 복구하는 기능에 대한 오류 수정"); | |
gotoxy(PATCHX + 18, PATCHY); | |
printf("ver 0.3.3 : 시작화면 개선 및 패치노트 추가"); | |
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 11); | |
gotoxy(PATCHX + 20, PATCHY); | |
printf("ver 0.4.1 : 점수판 및 경험치판 전광판 형식으로 개선"); | |
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 14); | |
gotoxy(PATCHX + 22, PATCHY); | |
printf("ver 0.4.2 : 점수와 경험치판 개선에 따른 리셋 오류 수정"); | |
gotoxy(PATCHX + 24, PATCHY); | |
printf("ver 0.4.3 : 경험치 계속 바뀌는 오류 및 100%% 초과 오류 수정"); | |
gotoxy(PATCHX + 26, PATCHY); | |
printf("ver 0.4.4 : 경험치 100%% 안차는 오류 및 점수판 선 넘는 오류 수정"); | |
gotoxy(PATCHX + 28, PATCHY); | |
printf("ver 0.4.5 : 전광판 색깔 수정 및 P 누른 상태에서 키 눌러지는 오류 수정"); | |
gotoxy(PATCHX + 30, PATCHY); | |
printf("ver 0.4.6 : 레벨업 메시지 포맷 수정 및 게임 오버 메시지 좌표, 색깔 수정"); | |
gotoxy(PATCHX + 32, PATCHY); | |
printf("ver 0.4.7 : 리셋확인 메시지 추가 및 게임오버 메시지 출력방식 수정"); | |
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 15); | |
gotoxy(PATCHX + 34, PATCHY); | |
printf("아무키나 누르시면 다시 시작화면으로 이동합니다!"); | |
getch(); // 아무 입력이나 받고 | |
system("mode con:cols=80 lines=30"); | |
system("cls"); // 화면 클리어 한 후에 | |
startscreen(); // 시작화면 다시 호출 | |
} | |
// 게임 시작전 화면을 출력하는 함수 | |
void startscreen() | |
{ | |
// 키 입력을 받을 변수 | |
int choice = 0; | |
// 지금 어떤 메뉴를 선택하고 있는지 알려주는 변수 | |
// TRUE : 게임 시작 선택, FALSE : 패치 노트 선택 | |
condition = TRUE; | |
gotoxy(4, 17); | |
printf("●●● ●●● ●●● ●●● ●●● ●●● "); | |
gotoxy(5, 17); | |
printf(" ● ● ● ● ● ● ● "); | |
gotoxy(6, 17); | |
printf(" ● ●●● ● ●● ● ● "); | |
gotoxy(7, 17); | |
printf(" ● ● ● ●● ● ● "); | |
gotoxy(8, 17); | |
printf(" ● ●●● ● ● ● ●●● ●●● "); | |
gotoxy(11, 26); | |
printf(" Ver 0.4.7 "); | |
gotoxy(15, 26); | |
printf(" 2017.04.03 ~ 2017.05.16 "); | |
gotoxy(17, 26); | |
printf(" 배하람 "); | |
gotoxy(22, 26); | |
printf(" ▶ 게임 시작 "); | |
gotoxy(25, 26); | |
printf(" ▷ 패치 노트 "); | |
while (TRUE) { | |
choice = getch(); | |
if (condition == TRUE) { | |
if (choice == KEY) { | |
choice = getch(); | |
if (choice == DOWN) { | |
gotoxy(22, 32); printf("▷"); | |
gotoxy(25, 32); printf("▶"); | |
condition = FALSE; | |
} | |
} | |
else if (choice == ENTER) | |
break; | |
} | |
else { | |
if (choice == KEY) { | |
choice = getch(); | |
if (choice == UP) { | |
gotoxy(22, 32); printf("▶"); | |
gotoxy(25, 32); printf("▷"); | |
condition = TRUE; | |
} | |
} | |
else if (choice == ENTER) | |
patchnote(); | |
} | |
if (choice == ENTER) break; | |
} | |
} | |
// 게임이 종료됬을 때 나타나는 메시지 창 함수 | |
void over_message() | |
{ | |
// 폰트 색깔 바꾸는 함수 | |
// 먼저 노란색으로 바꾼 뒤 | |
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 14); | |
gotoxy(PAUSEX, PAUSEY); | |
printf("▣▣▣▣▣▣▣▣▣▣▣▣▣▣▣▣"); | |
gotoxy(PAUSEX + 1, PAUSEY); | |
printf("▣ ▣"); | |
gotoxy(PAUSEX + 2, PAUSEY); | |
printf("▣ GAME OVER! ▣ "); | |
gotoxy(PAUSEX + 3, PAUSEY); | |
printf("▣ ▣"); | |
gotoxy(PAUSEX + 4, PAUSEY); | |
printf("▣ 획득한 점수 :%4d ▣", score); | |
gotoxy(PAUSEX + 5, PAUSEY); | |
printf("▣ ▣"); | |
gotoxy(PAUSEX + 6, PAUSEY); | |
printf("▣ 다시 시작하시려면 ▣"); | |
gotoxy(PAUSEX + 7, PAUSEY); | |
printf("▣ ▣"); | |
gotoxy(PAUSEX + 8, PAUSEY); | |
printf("▣ 아무키나 눌러주세요! ▣"); | |
gotoxy(PAUSEX + 9, PAUSEY); | |
printf("▣ ▣"); | |
gotoxy(PAUSEX + 10, PAUSEY); | |
printf("▣▣▣▣▣▣▣▣▣▣▣▣▣▣▣▣"); | |
// 다시 하얀색으로 바꾸준다. | |
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 15); | |
getch(); | |
// 입력을 받으면 화면 초기화+리셋 시킨후 gameover를 초기화 | |
for (int i = PAUSEX; i <= PAUSEX + 10; i++) { | |
gotoxy(i, PAUSEY); | |
printf(" "); | |
} | |
gameover = FALSE; | |
info_reset(); | |
reset_screen(); | |
reset_scoreboard(); | |
reset_levelboard(); | |
} | |
// 게임을 다시 시작할 때 나타나는 메시지 창 함수 | |
void reset_message() | |
{ | |
int yesno = 0; // 키입력 | |
int condition = TRUE; // yes인 상태 | |
// 폰트 색깔 바꾸는 함수 | |
// 먼저 노란색으로 바꾼 뒤 | |
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 14); | |
gotoxy(PAUSEX, PAUSEY); | |
printf("▣▣▣▣▣▣▣▣▣▣▣▣▣▣▣▣"); | |
gotoxy(PAUSEX + 1, PAUSEY); | |
printf("▣ ▣"); | |
gotoxy(PAUSEX + 2, PAUSEY); | |
printf("▣ 전부 초기화됩니다! ▣ "); | |
gotoxy(PAUSEX + 3, PAUSEY); | |
printf("▣ ▣"); | |
gotoxy(PAUSEX + 4, PAUSEY); | |
printf("▣ 다시 시작하시겠습니까? ▣"); | |
gotoxy(PAUSEX + 5, PAUSEY); | |
printf("▣ ▣"); | |
gotoxy(PAUSEX + 6, PAUSEY); | |
printf("▣ ▶ 예 ▣"); | |
gotoxy(PAUSEX + 7, PAUSEY); | |
printf("▣ ▣"); | |
gotoxy(PAUSEX + 8, PAUSEY); | |
printf("▣ ▷ 아니오 ▣"); | |
gotoxy(PAUSEX + 9, PAUSEY); | |
printf("▣ ▣"); | |
gotoxy(PAUSEX + 10, PAUSEY); | |
printf("▣▣▣▣▣▣▣▣▣▣▣▣▣▣▣▣"); | |
// 다시 하얀색으로 바꾸준다. | |
while (TRUE) { | |
yesno = getch(); | |
if (condition == TRUE) { | |
if (yesno == ENTER) { | |
for (int i = PAUSEX; i <= PAUSEX + 10; i++) { | |
gotoxy(i, PAUSEY); | |
printf(" "); | |
} | |
info_reset(); | |
reset_screen(); | |
reset_scoreboard(); | |
reset_levelboard(); | |
break; | |
} | |
else if (yesno == KEY) { | |
yesno = getch(); | |
if (yesno == DOWN) { | |
gotoxy(PAUSEX + 6, PAUSEY + 9); printf("▷"); | |
gotoxy(PAUSEX + 8, PAUSEY + 9); printf("▶"); | |
condition = FALSE; // no인 상태로 변경 | |
} | |
} | |
} | |
else if (condition == FALSE) { | |
if (yesno == ENTER) { | |
for (int i = PAUSEX; i <= PAUSEX + 10; i++) { | |
gotoxy(i, PAUSEY); | |
printf(" "); | |
} | |
// 이전 화면 저장하기 위한 screen_org 초기화 과정 | |
for (int i = 0; i<HEIGHT; i++) { | |
for (int j = 0; j<WIDTH; j++) | |
screen_org[i][j] = FIRST; | |
} | |
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 15); | |
joystick(); | |
break; | |
} | |
else if (yesno == KEY) { | |
yesno = getch(); | |
if (yesno == UP) { | |
gotoxy(PAUSEX + 6, PAUSEY + 9); printf("▶"); | |
gotoxy(PAUSEX + 8, PAUSEY + 9); printf("▷"); | |
condition = TRUE; // yes인 상태로 변경 | |
} | |
} | |
} | |
while (kbhit()) getch(); | |
} | |
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 15); | |
} | |
// 레벨 업 표시 해주는 함수 (기존 배열 초기화) | |
void levelup_format() | |
{ | |
// 화면 초기화 한후 | |
reset_screen(); | |
for (int i = 0; i<HEIGHT; i++) { | |
for (int j = 0; j<WIDTH; j++) { | |
if (i >= 2 && i <= 6) { | |
if (j == 2 || j == 8) | |
screen_up[i][j] = LEVELUP; | |
else if (j == 12) | |
screen_up[i][j] = LEVELUP; | |
} | |
else if (i == 7) | |
{ | |
if (j == 2 || j == 9) | |
screen_up[i][j] = LEVELUP; | |
else if (j == 11) | |
screen_up[i][j] = LEVELUP; | |
} | |
else if (i == 8) { | |
if (j >= 2 && j <= 6) | |
screen_up[i][j] = LEVELUP; | |
else if (j == 10) | |
screen_up[i][j] = LEVELUP; | |
} | |
else if (i == 14) { | |
if (j == 2 || j == 6) | |
screen_up[i][j] = LEVELUP; | |
else if (j >= 8 && j <= 12) | |
screen_up[i][j] = LEVELUP; | |
} | |
else if (i == 15 || i == 16) { | |
if (j == 2 || j == 6) | |
screen_up[i][j] = LEVELUP; | |
else if (j == 8 || j == 12) | |
screen_up[i][j] = LEVELUP; | |
} | |
else if (i == 17) { | |
if (j == 2 || j == 6) | |
screen_up[i][j] = LEVELUP; | |
else if (j >= 8 && j <= 12) | |
screen_up[i][j] = LEVELUP; | |
} | |
else if (i == 18 || i == 19) { | |
if (j == 2 || j == 6) | |
screen_up[i][j] = LEVELUP; | |
else if (j == 8) | |
screen_up[i][j] = LEVELUP; | |
} | |
else if (i == 20) { | |
if (j >= 2 && j <= 6) | |
screen_up[i][j] = LEVELUP; | |
else if (j == 8) | |
screen_up[i][j] = LEVELUP; | |
} | |
} | |
} | |
} | |
// 커서의 깜박거림을 지우는 함수 | |
void removecursor() | |
{ | |
CONSOLE_CURSOR_INFO curInfo; | |
GetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE), &curInfo); | |
curInfo.bVisible = 0; | |
SetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE), &curInfo); | |
} | |
// 커서를 원하는 좌표로 옮기는 함수 (x,y)에서 x는 행이고, y는 열 | |
void gotoxy(int x, int y) | |
{ | |
COORD Pos = { y - 1, x - 1 }; | |
SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), Pos); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment