Skip to content

Instantly share code, notes, and snippets.

@baeharam
Created March 17, 2019 15:00
Show Gist options
  • Save baeharam/d42bd762357433c83ac7aa7fb4725a8e to your computer and use it in GitHub Desktop.
Save baeharam/d42bd762357433c83ac7aa7fb4725a8e to your computer and use it in GitHub Desktop.
My Dirty Tetris
#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