なんかC言語の授業で作ったプログラム
この記事を書いた動機 ただ単にせっかく書いたコードを、無下にしてしまうのは勿体ないなと思い、非効率でバグだらけのスパゲッティコードではあるが、まあおいておこうかなと思っただけです。(かつコメントの英語もガバだらけ。。。) ちなみに、授業の制約上、一つのファイルにまとめざるを得ず、プログラムを分割できなかったのでえげつなく肥大化しています。。。 使っているライブラリ C標準ライブラリ curses プレイ動画 各画面の説明 スタート画面 終わりの画面 風のすてーじ 雨のすてーじ 晴れの日すてーじ コンパイル makefile build: gcc main.c -lcurses -lm -o main bash 上の操作 make build ./main # 実行 コード本体 main.c #include <curses.h> #include <stdlib.h> #include <math.h> #include <time.h> #include <unistd.h> #include <string.h> #define START_BUTTON 0 #define END_BUTTON 1 #define RETURN_BUTTON 2 #define CURSOR "====================" #define CORSOR_REMOVE " " // define const values for each stage #define RAIN_STAGE 0 #define WIND_STAGE 1 #define SUNNY_STAGE 2 // rain stage setting #define RAIN_SIZE_X 2 // set size of a rain #define RAIN_SIZE_Y 2 // set size of a rain #define RAIN_Y_SPACE 10 // avoid rain creation if there are created rain near the new rain #define RAIN_X_SPACE 3 // avoid rain creation if there are created rain near the new rain #define NUMEBR_OF_RAIN 5 // number of newly created rain in 1 sec #define SPEED_OF_RAIN 30 // in a sec #define DAMAGE_OF_RAIN 10 // amount of damage when player hit a rain #define NUMBER_OF_RETRY 20 // define how many times to recreate random nums #define RAIN_STAGE_POSSIBLE 30 // possibily of rain stage to be selected #define RAIN_STAGE_TIME_LIMIT 20 #define AMOUNT_OF_RAIN 28 // limit amount of rain // wind stage setting #define DAMAGE_OF_OBJ 20 // amount of damage when player hit a obj in this stage #define NUMEBR_OF_BLOWN_AWAY_OBJ 5 // number of obj to be created in a sec #define SPEED_OF_WIND_X 1 // numberOfFrame % this const value -> 100f in a sec #define MOVEMENT_OF_OBJ_X 5 // amount of movement in one position update #define SPEED_OF_WIND_Y 20 // amount of movement in a sec #define AMOUNT_OF_OBJ 28 // limit amount of obj #define WIND_STAGE_POSSIBLE 30 // possibily of wind stage to be selected #define WIND_STAGE_TIME_LIMIT 20 // sunny stage setting #define AMOUNT_OF_RACOVER 10 // amount of hp recover when player hit point obj #define NUMEBR_OF_POINT 20 // amount of point obj to be cteated in a sec #define AMOUNT_OF_POINT 10 // limit amount of point obj #define SPEED_OF_POINT_X 40 // amount of movement in a sec #define SPEED_OF_POINT_Y 40 // amount of movement in a sec #define SUNNY_STAGE_POSSOIBLE 40 // possibily of sunny stage to be selected #define SUNNY_STAGE_TIME_LIMIT 10 // main game setting #define NUMBER_OF_FRAME 100 // amount of frame in a sec #define DEFAULT_PLAYER_HP 100 #define DEFAULT_GAME_TIME 20 // sec #define PLAYER_SPEED 2 // movement of player in a sec #define PLAYER_JUMP_MAX 20 // height of player jump when there are space enough #define PLAYER_JUMP_TIME 1 // define time length of player jump #define PLAYER_SCORE 40 // one time, If player avoid obj #define DAMAGE_INTARVAL 1 // define damage interval when player hit a obj // player #define IN_DAMAGE_INTARVAL 1 #define OUT_DAMAGE_INTARVAL 0 #define NUMBER_OF_GAME 3 //game system value #define ALLOCATION_OK 0 // ram allocation alart #define ALLOCATION_NOT_OK 1 // ram allocation alart #define RESIZE_NOT_HAPPEND 0 // window resize alart #define RESIZE_HAPPEND 1 // window resize alart // show debug log when DEBUG is defined // #define DEBUG typedef struct VECTOR { int x,y; }Vector; // float version of vector typedef struct VECTOR_F { float x,y; }VectorF; typedef struct GRAPHIC { // hold position of top and left corner Vector position,PreviousPosition; Vector size; // [y][x] char **data; }Graphic; typedef struct PLAYER { int hp,score,currentStageScore; // enter defined value int damageIntarval; // enter number of frame since start interval int datageIntarvalFrame; Vector CurrentPosition,PreviousPosition; VectorF speed; // count number of played stage int counterOfGame; Graphic body; }Player; typedef struct STAGE { // hold possiblity of stage to be selected int possible; // hold time limit of each stages int timeLimit; char name[20]; // hold how long player play the stage double timer; // hold obj will be shown in stage int numberOfPreparedObj; Graphic *PreparedObj; int numberOfObj; Graphic *obj; // stage function. This function will not contain game loop. // This function will call random stage chooser or end screen // void (*stageFunc)(Player *player,Stage *stages); void (*stageFunc)(Player *player,struct STAGE*,int numberOfFrame); }Stage; // This value will be used globaly to run this game int width,height; char logs[256]; void setStageParam(Stage *stages); void showPlayer(Player player); void initPlayer(Player *player); int jumpPlayer(Player *player,int numberOfFrame); Vector clacBodyPosition(Vector position, Vector bodySize); int resizeDetect(){ int w,h; getmaxyx(stdscr,h,w); int status = RESIZE_NOT_HAPPEND; if(h != height || w != width){ status = RESIZE_HAPPEND; width = w; height = h; clear(); } return status; } int adjustTimer(double timer){ double length = ((double)1 / NUMBER_OF_FRAME); if(timer < length){ return (int)((length - timer) * 1000000); }else{ return 0; } } double clacTimeFromClock_t(clock_t time){ return (double) time / CLOCKS_PER_SEC; } void debugLog(int y,int x,char *text){ #ifdef DEBUG char newlogs[240]; sprintf(newlogs,"debug log: %s",text); mvaddstr(y,x,newlogs); refresh(); #endif } // This function can be used any Graphic obj int allocateGraphicRam(Graphic *target){ target -> data = (char **)malloc(sizeof(char *) * target -> size.y); if(target -> data == NULL) return ALLOCATION_NOT_OK; for(int y = 0; y < target -> size.y; y++){ target -> data[y] = (char *)malloc(sizeof(char) * target -> size.x); if(target -> data[y] == NULL) return ALLOCATION_NOT_OK; } return ALLOCATION_OK; } // This function can be used any Graphic obj void freeGraphicRam(Graphic *target){ for(int y = 0; y < target -> size.y; y++){ free(target -> data[y]); } free(target -> data); } void freeObjGraphicRam(Graphic *target,int numberOfObj){ for(int i = 0; i < numberOfObj; i++){ freeGraphicRam(&(target[i])); } } // This function should be executed once when start this game void initWholeGame(){ srand((unsigned)time(NULL)); initscr(); curs_set(0); noecho(); getmaxyx(stdscr,height,width); } // This function should be executed onec when end this game void endWholeGame(Player *player,Stage *stages){ endwin(); // free pointers here // 3 stages may contain pointers which are need to be freed. for(int i = 0; i < 3;i++){ // freeObjGraphicRam(stages[i].obj,stages[i].numberOfObj); freeObjGraphicRam(stages[i].PreparedObj,stages[i].numberOfPreparedObj); // free(stages[i].obj); free(stages[i].PreparedObj); } // player body may contain pointers which need to be freed. free(player -> body.data); } void movePlayerRandomly(Player *player,int numberOfFrame){ // getmaxyx(stdscr,height,width); resizeDetect(); player -> PreviousPosition = player->CurrentPosition; if(player -> CurrentPosition.y > 0 && player->speed.y != 0) player -> CurrentPosition.y = jumpPlayer(player,numberOfFrame); sprintf(logs,"player speed.y: %lf",player->speed.y); debugLog(6,3,logs); sprintf(logs,"player current position.y: %d",player -> CurrentPosition.y); debugLog(7,3,logs); if(numberOfFrame % (100 / PLAYER_SPEED) == 0){ // make random position // 0 -> negative // 1 -> 0 // 2 -> positive Vector direction = {.x=rand() % 3,.y=rand() % 3}; sprintf(logs,"direction x: %d y: %d",direction.x,direction.y); debugLog(4,3,logs); refresh(); // update position if(direction.x == 0 && player -> CurrentPosition.x > 0) player -> CurrentPosition.x -= 1; if(direction.y == 0 && player -> CurrentPosition.y > 0 && !(player->speed.y != 0)) player -> CurrentPosition.y = jumpPlayer(player,numberOfFrame); if(direction.x == 2 && player -> CurrentPosition.x < width) player -> CurrentPosition.x += 1; // if(direction.y == 2 && player -> CurrentPosition.y < height) player -> CurrentPosition.y += 1; // debug sprintf(logs,"x: %d y: %d",player -> CurrentPosition.x,player -> CurrentPosition.y); debugLog(5,3,logs); } // update player graphic position player -> body.position = clacBodyPosition(player->CurrentPosition,player->body.size); } // Start UI ------------------------------------------------------- int startUI(){ int keys = 0; // init these vars -> OK Vector titlePosition,startButtonPosition,endButtonPosition; Player player; initPlayer(&player); // set defalt option int selectedButton = START_BUTTON; clear(); getmaxyx(stdscr,height,width); // set position retative to screen size titlePosition.y = (height / 2) - 5; startButtonPosition.y = (height / 2) + 1; endButtonPosition.y = (height / 2 ) + 4; titlePosition.x = (width / 2) - 5; startButtonPosition.x = (width / 2) - 5; endButtonPosition.x = (width / 2 ) - 5; // show cursor with first defualt option if(selectedButton == START_BUTTON){ mvaddstr(startButtonPosition.y + 1,startButtonPosition.x,CURSOR); }else{ mvaddstr(endButtonPosition.y + 1,endButtonPosition.x,CURSOR); } // show title and start,end button mvaddstr(titlePosition.y,titlePosition.x,"Climate survivor"); mvaddstr(startButtonPosition.y,startButtonPosition.x,"start game"); mvaddstr(endButtonPosition.y,endButtonPosition.x,"end game"); refresh(); int numberOfFrame = 0; clock_t start = 0, delta = 0; double deltaf = 0; while(1){ start = clock(); // recaculation of position when resize window if(resizeDetect() == RESIZE_HAPPEND){ titlePosition.y = (height / 2) - 5; startButtonPosition.y = (height / 2) + 1; endButtonPosition.y = (height / 2 ) + 4; titlePosition.x = (width / 2) - 5; startButtonPosition.x = (width / 2) - 5; endButtonPosition.x = (width / 2 ) - 5; } timeout(0); keys = getch(); movePlayerRandomly(&player,numberOfFrame); showPlayer(player); sprintf(logs,"conuter is %d",numberOfFrame); debugLog(3,3,logs); numberOfFrame++; // when w or s key is pressed if(keys == 'w' || keys == 's'){ if(selectedButton == START_BUTTON){ selectedButton = END_BUTTON; }else{ selectedButton = START_BUTTON; } }else if(keys == '\n'){ break; } // show title and start,end button mvaddstr(titlePosition.y,titlePosition.x,"Climate survivor"); mvaddstr(startButtonPosition.y,startButtonPosition.x,"start game"); mvaddstr(endButtonPosition.y,endButtonPosition.x,"end game"); // update cursor if(selectedButton == START_BUTTON){ mvaddstr(endButtonPosition.y + 1,endButtonPosition.x,CORSOR_REMOVE); mvaddstr(startButtonPosition.y + 1,startButtonPosition.x,CURSOR); }else{ mvaddstr(startButtonPosition.y + 1,startButtonPosition.x,CORSOR_REMOVE); mvaddstr(endButtonPosition.y + 1,endButtonPosition.x,CURSOR); } refresh(); delta = clock() - start; deltaf = clacTimeFromClock_t(delta); usleep(adjustTimer(deltaf)); } // free ram which allocated in initPlayer function free(player.body.data); // for next return selectedButton; } // Start UI ------------------------------------------------------- // end UI ----------------------------------------------------------- int endUI(Player *player){ int keys; // init position -> ok Vector scorePosition,endButtonPosition,returnButtonPosition; Player backgoundPlayer; initPlayer(&backgoundPlayer); // set defualt option int selectedButton = END_BUTTON; clear(); getmaxyx(stdscr,height,width); // init position scorePosition.y = (height / 2) - 5; returnButtonPosition.y = (height / 2) + 1; endButtonPosition.y = (height / 2) + 4; scorePosition.x = (width / 2) + 10; endButtonPosition.x = (width / 2) + 10; returnButtonPosition.x = (width / 2) + 10; // show end or return button // show score char scoreText[30]; sprintf(scoreText,"Score is: %d",player -> score); mvaddstr(scorePosition.y,scorePosition.x,scoreText); // TODO: fix worngly inverted button mvaddstr(returnButtonPosition.y,returnButtonPosition.x,"end game"); mvaddstr(endButtonPosition.y,endButtonPosition.x,"return start screen"); // show cursor if(selectedButton == END_BUTTON){ mvaddstr(endButtonPosition.y + 1,endButtonPosition.x,CURSOR); }else if(selectedButton == RETURN_BUTTON){ mvaddstr(returnButtonPosition.y + 1,returnButtonPosition.x,CURSOR); } refresh(); int count = 0; clock_t start = 0, delta = 0; double deltaf = 0; while(1){ start = clock(); // recaculation of position when resize window is happened if(resizeDetect() == RESIZE_HAPPEND){ scorePosition.y = (height / 2) - 5; returnButtonPosition.y = (height / 2) + 1; endButtonPosition.y = (height / 2) + 4; scorePosition.x = (width / 2) + 10; endButtonPosition.x = (width / 2) + 10; returnButtonPosition.x = (width / 2) + 10; } timeout(0); keys = getch(); count++; movePlayerRandomly(&backgoundPlayer,count); showPlayer(backgoundPlayer); // when w or s key is pressed if(keys == 'w' || keys == 's'){ if(selectedButton == END_BUTTON){ selectedButton = RETURN_BUTTON; }else{ selectedButton = END_BUTTON; } }else if(keys == '\n'){ break; } // update cursor if(selectedButton == END_BUTTON){ mvaddstr(endButtonPosition.y + 1,endButtonPosition.x,CORSOR_REMOVE); mvaddstr(returnButtonPosition.y + 1,returnButtonPosition.x,CURSOR); }else if(selectedButton == RETURN_BUTTON){ mvaddstr(returnButtonPosition.y + 1,returnButtonPosition.x,CORSOR_REMOVE); mvaddstr(endButtonPosition.y + 1,endButtonPosition.x,CURSOR); } // update text when overwrited by background player mvaddstr(scorePosition.y,scorePosition.x,scoreText); // TODO: fix worngly inverted button mvaddstr(returnButtonPosition.y,returnButtonPosition.x,"end game"); mvaddstr(endButtonPosition.y,endButtonPosition.x,"return start screen"); refresh(); delta = clock() - start; deltaf = clacTimeFromClock_t(delta); usleep(adjustTimer(deltaf)); } free(backgoundPlayer.body.data); return selectedButton; } // end UI ----------------------------------------------------------- // This function makes possible convert player position to body position for graphic Vector clacBodyPosition(Vector position, Vector bodySize){ Vector result; if (bodySize.x % 2 == 0) result.x = position.x - (bodySize.x / 2); if (bodySize.x % 2 != 0) result.x = position.x - (bodySize.x / 2); result.y = position.y - bodySize.y; return result; } void initPlayer(Player *player){ player -> hp = DEFAULT_PLAYER_HP; player -> score = 0; player -> currentStageScore = 0; player -> counterOfGame = 0; player -> damageIntarval = OUT_DAMAGE_INTARVAL; player -> datageIntarvalFrame = 0; // set first player position getmaxyx(stdscr,player -> CurrentPosition.y,player -> CurrentPosition.x); player -> CurrentPosition.x = player -> CurrentPosition.x / 2; player -> PreviousPosition = player -> CurrentPosition; player -> speed.x = 0; player -> speed.y = 0; // set player body player -> body.size.x = 5; player -> body.size.y = 4; player -> body.position = clacBodyPosition(player->CurrentPosition,player->body.size); // allocate ram // player.body.data need to be freed when end game allocateGraphicRam(&(player -> body)); // create and set player graphic char tempBody[4][5] = { {' ','#','#','#',' '}, {'#','#','#','#','#'}, {' ','#','#','#',' '}, {' ','#',' ','#',' '} }; for(int y = 0; y < player -> body.size.y ; y++){ for(int x = 0; x < player -> body.size.x ; x++){ player -> body.data[y][x] = tempBody[y][x]; } } } void showBackgroud(Graphic background){ for(int y = 0; y < background.size.y ; y++){ for(int x = 0; x < background.size.x ; x++){ move(y + background.position.y ,x + background.position.x); addch(background.data[y][x]); } } } void initStage(Stage stage){ clear(); } int stageSelector(Stage *stages){ int random = rand() % 100; sprintf(logs,"stage selector rundom num is: %d",random); debugLog(14,3,logs); if(random <= stages[RAIN_STAGE].possible) return RAIN_STAGE; if(stages[RAIN_STAGE].possible < random && random <= stages[RAIN_STAGE].possible + stages[WIND_STAGE].possible) return WIND_STAGE; if(stages[RAIN_STAGE].possible + stages[WIND_STAGE].possible < random && random <= stages[RAIN_STAGE].possible + stages[WIND_STAGE].possible + stages[SUNNY_STAGE].possible) return SUNNY_STAGE; // if any if sentence is not working debugLog(5,3,"Warning! stageSelector function is not working correctly!"); return RAIN_STAGE; } void addPoint(Vector position,char draw){ if( position.x >= 0 && position.y >= 0 && position.x <= width && position.y <= height ){ mvaddch(position.y,position.x,draw); } } // generic version of show graphic // player graphic is not rendered here void showGraphic(Graphic target){ // getmaxyx(stdscr,height,width); resizeDetect(); // clear old graphic for(int y = 0; y < target.size.y; y++){ for(int x = 0; x < target.size.x; x++){ if( y + target.PreviousPosition.y >= 0 && x + target.PreviousPosition.x >= 0 && y + target.PreviousPosition.y <= height && x + target.PreviousPosition.x <= width ){ // move(y + target.PreviousPosition.y, x + target.PreviousPosition.x); // addch(' '); Vector pp = target.PreviousPosition; pp.y += y; pp.x += x; addPoint(pp,' '); } } } // render new graphic for(int y = 0; y < target.size.y; y++){ for(int x = 0; x < target.size.x; x++){ if( y + target.PreviousPosition.y >= 0 && x + target.PreviousPosition.x >= 0 && y + target.PreviousPosition.y <= height && x + target.PreviousPosition.x <= width ){ // move(y + target.position.y, x + target.position.x); // addch(target.data[y][x]); Vector p = target.position; p.y += y; p.x += x; // TODO fix segmantation fault addPoint(p,target.data[y][x]); } } } } // This function will update player hp // This function will delete obj which hit the player int detectHit(Player *player,Stage *stages,int selectedStage){ int detect = FALSE; // getmaxyx(stdscr,height,width); resizeDetect(); // For loop for all obj in stage for(int objs = 0; objs < stages[selectedStage].numberOfObj; objs++){ // for(int objs = 0; objs < 0; objs){ Graphic target = stages[selectedStage].obj[objs]; // refrence obj and player graphics and compare both chars // if both char info is not space like ' ', that mean detect hit. // return bool value // check target posiotion and check box in range or not to player Vector targetPosition = target.position; Vector targetSize = target.size; if( targetPosition.x + targetSize.x <= player -> body.position.x && targetPosition.y + targetSize.y <= player -> body.position.y && targetPosition.x >= player -> body.position.x + player -> body.size.x && targetPosition.y >= player -> body.position.y + player -> body.size.y ){ // skip detect hit when trarget is not in player graphic continue; } // For loop to refrence all player body char for(int py = 0; py < player -> body.size.y; py++){ for(int px = 0; px < player -> body.size.x; px++){ // For loop to refrence all obj body char for(int oy = 0; oy < target.size.y ; oy++){ for(int ox = 0; ox < target.size.x; ox++){ char Aplayer = ' '; char Aobj = ' '; sprintf(logs,"debug log: obj %d",objs); debugLog(4,3,logs); if( player -> body.position.x + px == target.position.x + ox && player -> body.position.y + py == target.position.y + oy ){ Aplayer = player -> body.data[py][px]; Aobj = target.data[oy][ox]; sprintf(logs,"debug log: detect hit at x: %d y: %d ",player -> body.position.x + px,player -> body.position.y + py); debugLog(5,3,logs); sprintf(logs,"debug log: Player char: %c obj char: %c",Aplayer,Aobj); debugLog(6,3,logs); } // comapre values if( Aplayer != ' ' && Aobj != ' ' ) detect = TRUE; } } } } } sprintf(logs,"detection var: %d",detect); debugLog(9,3,logs); if(detect == TRUE){ debugLog(8,3,"collision detected"); } return detect; } // do special task for each stage --------------------------------- void rainStage(Player *player,Stage *stages,int numberOfFrame){ // If there are objs which are diesappered, add score. // If collion is detected, reduce player hp // getmaxyx(stdscr,height,width); resizeDetect(); // move rain // SPEED_OF_RAIN -> amount of movement in a sec // numberOfFrame % (100 / SPEED_OF_RAIN) -> number of update in a sec if(numberOfFrame % (int)((double)100 / SPEED_OF_RAIN) == 0){ // Rondomly move rain without limitation -> fall movement for(int obj_index = 0; obj_index < stages[RAIN_STAGE].numberOfObj; obj_index++){ stages[RAIN_STAGE].obj[obj_index].PreviousPosition = stages[RAIN_STAGE].obj[obj_index].position; stages[RAIN_STAGE].obj[obj_index].position.y += 1; } } // make rain obj // add rain with random shapes // control number of new rain obj by if sentence blow if(numberOfFrame % (int)((double)100 / NUMEBR_OF_RAIN) == 0 && stages[RAIN_STAGE].numberOfObj < AMOUNT_OF_RAIN){ // make rain array // init the all array element with space char rain[RAIN_SIZE_Y][RAIN_SIZE_Y] = {' '}; for(int y = 0 ; y < RAIN_SIZE_Y; y++){ for(int x = 0; x < RAIN_SIZE_X; x++){ // rain[y][x] = '&'; int random = rand() % 2; if(x % 2 == 0){ if(random % 2 == 0){ rain[y][x] = '&'; rain[y][x + 1] = ' '; } if(random % 2 != 0){ rain[y][x] = ' '; rain[y][x + 1] = '&'; } } } } // create random position // make not make same random num int randomPosition = 0; int count = 0; while (count < NUMBER_OF_RETRY) { count++; int moreBreak = FALSE; randomPosition = rand() % width; // avoid segmantation fault if(stages[RAIN_STAGE].numberOfObj == 0) { moreBreak = TRUE; break; } for(int obj_index = 0; obj_index < stages[RAIN_STAGE].numberOfObj; obj_index++){ if( // check obj in same x position stages[RAIN_STAGE].obj[obj_index].position.x == randomPosition && // check how far from new obj in y aixs stages[RAIN_STAGE].obj[obj_index].position.y < RAIN_Y_SPACE ){ // If there are no space enough, recreate random num sprintf(logs,"debug log: false random num is %d",randomPosition); debugLog(3,3,logs); }else if(stages[RAIN_STAGE].obj[obj_index].position.x < RAIN_X_SPACE){ // If there are no space enough, recreate random num sprintf(logs,"debug log: false random num is %d",randomPosition); debugLog(3,3,logs); }else{ sprintf(logs,"debug log: true random num is %d",randomPosition); debugLog(3,3,logs); moreBreak = TRUE; break; } } if(moreBreak == TRUE) break; } // allocate ram for new rain obj // set position. x position is random but y position is always being at 0. Vector size = {.x=RAIN_SIZE_X,.y=RAIN_SIZE_Y}; Vector position = {.x=randomPosition,.y=0}; int status = ALLOCATION_OK; if(stages[RAIN_STAGE].obj == NULL){ while(stages[RAIN_STAGE].obj == NULL){ stages[RAIN_STAGE].obj = (Graphic *)malloc(sizeof(Graphic) * (AMOUNT_OF_RAIN + 2)); } stages[RAIN_STAGE].obj[stages[RAIN_STAGE].numberOfObj].size = size; stages[RAIN_STAGE].obj[stages[RAIN_STAGE].numberOfObj].position = position; status = allocateGraphicRam(&(stages[RAIN_STAGE].obj[stages[RAIN_STAGE].numberOfObj])); if(status == ALLOCATION_OK){ // add rain obj to stage obj for(int y = 0 ; y < RAIN_SIZE_Y; y++){ for(int x = 0; x < RAIN_SIZE_X; x++){ stages[RAIN_STAGE].obj[stages[RAIN_STAGE].numberOfObj].data[y][x] = rain[y][x]; } } } }else{ stages[RAIN_STAGE].numberOfObj += 1; stages[RAIN_STAGE].obj[stages[RAIN_STAGE].numberOfObj].size = size; stages[RAIN_STAGE].obj[stages[RAIN_STAGE].numberOfObj].position = position; status = allocateGraphicRam(&(stages[RAIN_STAGE].obj[stages[RAIN_STAGE].numberOfObj])); if(status == ALLOCATION_OK){ // add rain obj to stage obj for(int y = 0 ; y < RAIN_SIZE_Y; y++){ for(int x = 0; x < RAIN_SIZE_X; x++){ stages[RAIN_STAGE].obj[stages[RAIN_STAGE].numberOfObj].data[y][x] = rain[y][x]; } } }else{ stages[RAIN_STAGE].numberOfObj -= 1; } } } // show and update all rain graphics // separate code for in range version and out of range version // check in range or not for(int obj_index = 0; obj_index < stages[RAIN_STAGE].numberOfObj; obj_index++){ // make and use show graphic function showGraphic(stages[RAIN_STAGE].obj[obj_index]); } // add score as player is alive int score = 0; score += (stages[RAIN_STAGE].timer * PLAYER_SCORE); player -> currentStageScore = score; } void windStage(Player *player,Stage *stages,int numberOfFrame){ // getmaxyx(stdscr,height,width); resizeDetect(); // If there are objs which are diesappered, add score. // If collion is detected, reduce player hp // move obj by wind // update position if(numberOfFrame % SPEED_OF_WIND_X == 0 || numberOfFrame % (int)((double)100 / SPEED_OF_WIND_Y) == 0){ for(int obj_index = 0; obj_index < stages[WIND_STAGE].numberOfObj; obj_index++){ stages[WIND_STAGE].obj[obj_index].PreviousPosition = stages[WIND_STAGE].obj[obj_index].position; } } // SPEED_OF_WIND_X -> amount of movement in a sec if(numberOfFrame % SPEED_OF_WIND_X == 0){ for(int obj_index = 0; obj_index < stages[WIND_STAGE].numberOfObj; obj_index++){ stages[WIND_STAGE].obj[obj_index].position.x += MOVEMENT_OF_OBJ_X; } } // SPEED_OF_WIND_Y -> amount of movement in a sec if(numberOfFrame % (int)((double)100 / SPEED_OF_WIND_Y) == 0){ for(int obj_index = 0; obj_index < stages[WIND_STAGE].numberOfObj; obj_index++){ if(rand() % 2){ stages[WIND_STAGE].obj[obj_index].position.y -= 1; }else{ stages[WIND_STAGE].obj[obj_index].position.y += 1; } } } // create new obj if(numberOfFrame % (int)((double)100 / NUMEBR_OF_BLOWN_AWAY_OBJ) == 0 && stages[WIND_STAGE].numberOfObj < AMOUNT_OF_OBJ){ // create random position Vector position = {.x=0,.y=0}; position.y = rand() % height; // create obj int choosenObj = rand() % stages[WIND_STAGE].numberOfPreparedObj; char **fig; fig = stages[WIND_STAGE].PreparedObj[choosenObj].data; int status = ALLOCATION_OK; Vector size = stages[WIND_STAGE].PreparedObj[choosenObj].size; if(stages[WIND_STAGE].obj == NULL) { while(stages[WIND_STAGE].obj == NULL){ stages[WIND_STAGE].obj = (Graphic *)malloc(sizeof(Graphic) * (AMOUNT_OF_OBJ + 2)); } stages[WIND_STAGE].obj[stages[WIND_STAGE].numberOfObj].size = size; stages[WIND_STAGE].obj[stages[WIND_STAGE].numberOfObj].position = position; // if size value is not set, cause segmantation fault status = allocateGraphicRam(&(stages[WIND_STAGE].obj[stages[WIND_STAGE].numberOfObj])); // add rain obj to stage obj if(status == ALLOCATION_OK){ for(int y = 0 ; y < stages[WIND_STAGE].obj[stages[WIND_STAGE].numberOfObj].size.y ; y++){ for(int x = 0; x < stages[WIND_STAGE].obj[stages[WIND_STAGE].numberOfObj].size.x ; x++){ stages[WIND_STAGE].obj[stages[WIND_STAGE].numberOfObj].data[y][x] = fig[y][x]; } } } }else{ stages[WIND_STAGE].numberOfObj += 1; stages[WIND_STAGE].obj[stages[WIND_STAGE].numberOfObj].size = size; stages[WIND_STAGE].obj[stages[WIND_STAGE].numberOfObj].position = position; status = allocateGraphicRam(&(stages[WIND_STAGE].obj[stages[WIND_STAGE].numberOfObj])); // add rain obj to stage obj if(status == ALLOCATION_OK){ for(int y = 0 ; y < stages[WIND_STAGE].obj[stages[WIND_STAGE].numberOfObj].size.y ; y++){ for(int x = 0; x < stages[WIND_STAGE].obj[stages[WIND_STAGE].numberOfObj].size.x ; x++){ stages[WIND_STAGE].obj[stages[WIND_STAGE].numberOfObj].data[y][x] = fig[y][x]; } } }else{ stages[WIND_STAGE].numberOfObj -= 1; } } } // update graphics for(int obj_index = 0; obj_index < stages[WIND_STAGE].numberOfObj; obj_index++){ // make and use show graphic function showGraphic(stages[WIND_STAGE].obj[obj_index]); } // add score as player is alive int score = 0; score += (stages[WIND_STAGE].timer * PLAYER_SCORE); player -> currentStageScore = score; // move obj randomly -> in x and y aixs // show random obj // count obj, disappered obj, appered obj // use count obj result to caculate score and then update player score } void sunnyStage(Player *player,Stage *stages,int numberOfFrame){ // getmaxyx(stdscr,height,width); resizeDetect(); // move obj randomly // update position if(numberOfFrame % (int)((double)100 / SPEED_OF_POINT_X) == 0|| numberOfFrame % (int)((double)100 / SPEED_OF_POINT_Y) == 0){ for(int obj_index = 0; obj_index < stages[SUNNY_STAGE].numberOfObj; obj_index++){ stages[SUNNY_STAGE].obj[obj_index].PreviousPosition = stages[SUNNY_STAGE].obj[obj_index].position; } } // SPEED_OF_POINT_X -> amount of movement in a sec if(numberOfFrame % (int)((double)100 / SPEED_OF_POINT_X) == 0){ for(int obj_index = 0; obj_index < stages[SUNNY_STAGE].numberOfObj; obj_index++){ if(rand() % 2){ stages[SUNNY_STAGE].obj[obj_index].position.x -= 1; }else{ stages[SUNNY_STAGE].obj[obj_index].position.x += 1; } } } // SPEED_OF_POINT_Y -> amount of movement in a sec if(numberOfFrame % (int)((double)100 / SPEED_OF_POINT_Y) == 0){ for(int obj_index = 0; obj_index < stages[SUNNY_STAGE].numberOfObj; obj_index++){ if(rand() % 2){ stages[SUNNY_STAGE].obj[obj_index].position.y -= 1; }else{ stages[SUNNY_STAGE].obj[obj_index].position.y += 1; } } } // create new obj if(numberOfFrame % (int)((double)100 / NUMEBR_OF_POINT) == 0 && stages[SUNNY_STAGE].numberOfObj < AMOUNT_OF_POINT){ Vector position = {.x=rand()%width,.y=rand()%height}; // create obj int choosenObj = 0; char **fig; fig = stages[SUNNY_STAGE].PreparedObj[choosenObj].data; Vector size = stages[SUNNY_STAGE].PreparedObj[choosenObj].size; int status = ALLOCATION_OK; if(stages[SUNNY_STAGE].obj == NULL) { while(stages[SUNNY_STAGE].obj == NULL){ stages[SUNNY_STAGE].obj = (Graphic *)malloc(sizeof(Graphic) * (AMOUNT_OF_POINT + 2)); } stages[SUNNY_STAGE].obj[stages[SUNNY_STAGE].numberOfObj].size = size; stages[SUNNY_STAGE].obj[stages[SUNNY_STAGE].numberOfObj].position = position; status = allocateGraphicRam(&(stages[SUNNY_STAGE].obj[stages[SUNNY_STAGE].numberOfObj])); // add point obj to stage obj if(status == ALLOCATION_OK){ for(int y = 0 ; y < stages[SUNNY_STAGE].obj[stages[SUNNY_STAGE].numberOfObj].size.y ; y++){ for(int x = 0; x < stages[SUNNY_STAGE].obj[stages[SUNNY_STAGE].numberOfObj].size.x ; x++){ stages[SUNNY_STAGE].obj[stages[SUNNY_STAGE].numberOfObj].data[y][x] = fig[y][x]; } } } }else{ stages[SUNNY_STAGE].numberOfObj += 1; stages[SUNNY_STAGE].obj[stages[SUNNY_STAGE].numberOfObj].size = size; stages[SUNNY_STAGE].obj[stages[SUNNY_STAGE].numberOfObj].position = position; status = allocateGraphicRam(&(stages[SUNNY_STAGE].obj[stages[SUNNY_STAGE].numberOfObj])); // add point obj to stage obj if(status == ALLOCATION_OK){ for(int y = 0 ; y < stages[SUNNY_STAGE].obj[stages[SUNNY_STAGE].numberOfObj].size.y ; y++){ for(int x = 0; x < stages[SUNNY_STAGE].obj[stages[SUNNY_STAGE].numberOfObj].size.x ; x++){ stages[SUNNY_STAGE].obj[stages[SUNNY_STAGE].numberOfObj].data[y][x] = fig[y][x]; } } }else{ stages[SUNNY_STAGE].numberOfObj -= 1; } } } // update graphics for(int obj_index = 0; obj_index < stages[SUNNY_STAGE].numberOfObj; obj_index++){ // make and use show graphic function showGraphic(stages[SUNNY_STAGE].obj[obj_index]); } } // do special task for each stage --------------------------------- // show ui top left and right corner void ShowStageUI(Stage stage,Player player){ Vector timerPosition,hpPosition,scorePosition,stageNamePosition; // getmaxyx(stdscr,height,width); resizeDetect(); timerPosition.y = 1; timerPosition.x = width - 20; hpPosition.y = timerPosition.y + 1; hpPosition.x = width - 20; scorePosition.y = hpPosition.y + 1; scorePosition.x = width - 20; stageNamePosition.y = 1; stageNamePosition.x = 1; char timerText[20]; char hpText[20]; char scoreText[20]; sprintf(timerText,"Time: %-3d/%-3d(sec)",(int)stage.timer,stage.timeLimit); sprintf(hpText,"HP: %-3d",player.hp); sprintf(scoreText,"Score: %-3d",player.currentStageScore + player.score); mvaddstr(timerPosition.y,timerPosition.x,timerText); mvaddstr(hpPosition.y,hpPosition.x,hpText); mvaddstr(scorePosition.y,scorePosition.x,scoreText); mvaddstr(stageNamePosition.y,stageNamePosition.x,stage.name); refresh(); } int jumpPlayer(Player *player,int numberOfFrame){ // pi value should be between 0 and PLAYER_JUMP_MAX int result = 0; double temp = 0; float pi = 0; int playerJumpHeight = PLAYER_JUMP_MAX; // getmaxyx(stdscr,height,width); resizeDetect(); if(playerJumpHeight > height){ playerJumpHeight = height - 4; } double lengthInOneFrame = (double)(playerJumpHeight * 2) / 100; lengthInOneFrame /= PLAYER_JUMP_TIME; if(player -> speed.y < (playerJumpHeight * 2)) player->speed.y += lengthInOneFrame; else player -> speed.y = 0; pi = sin(player -> speed.y * (3.14f / (playerJumpHeight * 2))); temp = (double)height - (pi * (double)playerJumpHeight); return (int)temp; } void controlPlayer(Player *player,int numberOfFrame){ // getmaxyx(stdscr,height,width); resizeDetect(); int keys; // player can be controled by wasd keys timeout(0); keys = getch(); player->PreviousPosition = player->CurrentPosition; // jump if(((keys == 'w' || keys == ' ') && player->CurrentPosition.y > 0) || player->speed.y != 0) player->CurrentPosition.y = jumpPlayer(player,numberOfFrame); // move left and right in constant speed if(keys == 'a' && player->CurrentPosition.x > 0 + (player->body.size.x / 2) + 1) player->CurrentPosition.x -= PLAYER_SPEED; if(keys == 'd' && player->CurrentPosition.x < width - player->body.size.x) player->CurrentPosition.x += PLAYER_SPEED; // update graphic position player -> body.position = clacBodyPosition(player->CurrentPosition,player->body.size); } void detectHitPlayer(Player *player,Stage *stages,int selectedStage,int numberOfFrame){ int collosion = detectHit(player,stages,selectedStage); if(collosion == TRUE && player -> damageIntarval == OUT_DAMAGE_INTARVAL){ switch (selectedStage) { case RAIN_STAGE: flash(); player -> hp -= DAMAGE_OF_RAIN; break; case SUNNY_STAGE: flash(); player -> hp += AMOUNT_OF_RACOVER; player -> currentStageScore += PLAYER_SCORE; break; case WIND_STAGE: flash(); player -> hp -= DAMAGE_OF_OBJ; break; default: debugLog(3,10,"Worng stage is selected. Check code."); break; } player -> damageIntarval = IN_DAMAGE_INTARVAL; } if(player -> damageIntarval == IN_DAMAGE_INTARVAL) player -> datageIntarvalFrame += 1; if(player -> datageIntarvalFrame == (100 * DAMAGE_INTARVAL)){ player -> damageIntarval = OUT_DAMAGE_INTARVAL; player -> datageIntarvalFrame = 0; } sprintf(logs,"damage intarval: %d",player -> datageIntarvalFrame); debugLog(11,3,logs); } void showPlayer(Player player){ // clear old player graphic Vector oldBodyPosition = clacBodyPosition(player.PreviousPosition,player.body.size); for(int y = 0; y < player.body.size.y; y++){ for(int x = 0; x < player.body.size.x; x++){ move(y + oldBodyPosition.y,x + oldBodyPosition.x); addch(' '); } } // show player for(int y = 0; y < player.body.size.y; y++){ for(int x = 0; x < player.body.size.x; x++){ move(y + player.body.position.y,x + player.body.position.x); addch(player.body.data[y][x]); } } refresh(); } // TODO: fix segmatation fualt after wind stage // call once when start button selected // make start_UI call this function when start button is selected. -> OK void StageLoop(Player *player,Stage *stages){ int selectedStage = WIND_STAGE; for(int times = 0; times < NUMBER_OF_GAME; times++){ selectedStage = stageSelector(stages); initStage(stages[selectedStage]); clock_t start = 0, delta = 0; time_t gameStartTime = time(NULL); double deltaf = 0; int numberOfFrame = 0; int resizeHappn = RESIZE_NOT_HAPPEND; while(1){ start = clock(); resizeHappn = resizeDetect(); // move -> update graphic -> collion detection controlPlayer(player,numberOfFrame); // show time hp score stage name -> OK ShowStageUI(stages[selectedStage],*player); // update order // stage graphic -> player graphic if(numberOfFrame % 30 == 0) stages[selectedStage].stageFunc(player,stages,numberOfFrame); showPlayer(*player); // collsion system detectHitPlayer(player,stages,selectedStage,numberOfFrame); refresh(); numberOfFrame++; delta = clock() - start; deltaf = clacTimeFromClock_t(delta); stages[selectedStage].timer = difftime(time(NULL),gameStartTime); if(resizeHappn) resizeHappn = RESIZE_NOT_HAPPEND; usleep(adjustTimer(deltaf)); if(stages[selectedStage].timer > stages[selectedStage].timeLimit || player -> hp <= 0) break; } // all stage reqire addtional memory management freeObjGraphicRam(stages[selectedStage].obj,stages[selectedStage].numberOfObj); free(stages[selectedStage].obj); stages[selectedStage].numberOfObj = 0; // stages[selectedStage].timer = 0; setStageParam(stages); player -> score += player -> currentStageScore; player -> currentStageScore = 0; if(player -> hp <= 0) break; } } // pass stage array whose size is 3 void setStageParam(Stage *stages){ // getmaxyx(stdscr,height,width); resizeDetect(); // rain stage -------------------------------------------------------------------------------- stages[RAIN_STAGE].possible = RAIN_STAGE_POSSIBLE; stages[RAIN_STAGE].timer = 0; stages[RAIN_STAGE].timeLimit = RAIN_STAGE_TIME_LIMIT; sprintf(stages[RAIN_STAGE].name,"rain stage"); // rain obj // obj will be used to manage rain obj in rain stage // rain will be made by function and caculation // So in this function, I will not allocate stages[RAIN_STAGE].numberOfObj = 0; stages[RAIN_STAGE].obj = NULL; stages[RAIN_STAGE].numberOfPreparedObj = 0; stages[RAIN_STAGE].PreparedObj = NULL; // stage function pointer stages[RAIN_STAGE].stageFunc = rainStage; // rain stage -------------------------------------------------------------------------------- // wind stage -------------------------------------------------------------------------------- stages[WIND_STAGE].possible = WIND_STAGE_POSSIBLE; stages[WIND_STAGE].timer = 0; stages[WIND_STAGE].timeLimit = WIND_STAGE_TIME_LIMIT; sprintf(stages[WIND_STAGE].name,"wind stage"); // blown away obj // rectangle obj 0 int obj_index = 0; stages[WIND_STAGE].numberOfPreparedObj = 1; stages[WIND_STAGE].PreparedObj = malloc(sizeof(Graphic) * 3); stages[WIND_STAGE].PreparedObj[obj_index].position.x = 0; stages[WIND_STAGE].PreparedObj[obj_index].position.y = 0; stages[WIND_STAGE].PreparedObj[obj_index].size.x = 3; stages[WIND_STAGE].PreparedObj[obj_index].size.y = 3; allocateGraphicRam(&(stages[WIND_STAGE].PreparedObj[obj_index])); char rectangle[3][3] = { {'A','A','A'}, {'A',' ','A'}, {'B','B','B'}}; for(int y = 0; y < stages[WIND_STAGE].PreparedObj[obj_index].size.y; y++){ for(int x = 0; x < stages[WIND_STAGE].PreparedObj[obj_index].size.x; x++){ stages[WIND_STAGE].PreparedObj[obj_index].data[y][x] = rectangle[y][x]; } } obj_index++; stages[WIND_STAGE].numberOfPreparedObj += 1; // triangle obj 1 stages[WIND_STAGE].PreparedObj[obj_index].position.x = 0; stages[WIND_STAGE].PreparedObj[obj_index].position.y = 0; stages[WIND_STAGE].PreparedObj[obj_index].size.x = 5; stages[WIND_STAGE].PreparedObj[obj_index].size.y = 3; allocateGraphicRam(&(stages[WIND_STAGE].PreparedObj[obj_index])); char triangle[3][5] = { {' ',' ','a',' ',' '}, {' ','A',' ','A',' '}, {'b','B','B','B','b'}}; for(int y = 0; y < stages[WIND_STAGE].PreparedObj[obj_index].size.y; y++){ for(int x = 0; x < stages[WIND_STAGE].PreparedObj[obj_index].size.x; x++){ stages[WIND_STAGE].PreparedObj[obj_index].data[y][x] = triangle[y][x]; } } obj_index++; stages[WIND_STAGE].numberOfPreparedObj += 1; // hexagon? obj 2 stages[WIND_STAGE].PreparedObj[obj_index].position.x = 0; stages[WIND_STAGE].PreparedObj[obj_index].position.y = 0; stages[WIND_STAGE].PreparedObj[obj_index].size.x = 5; stages[WIND_STAGE].PreparedObj[obj_index].size.y = 6; allocateGraphicRam(&(stages[WIND_STAGE].PreparedObj[obj_index])); char hexagon[6][5] = { {' ',' ','*',' ',' '}, {' ','J',' ','L',' '}, {'A',' ',' ',' ','A'}, {'A',' ',' ',' ','A'}, {' ','L',' ','J',' '}, {' ',' ','*',' ',' '}}; for(int y = 0; y < stages[WIND_STAGE].PreparedObj[obj_index].size.y; y++){ for(int x = 0; x < stages[WIND_STAGE].PreparedObj[obj_index].size.x; x++){ stages[WIND_STAGE].PreparedObj[obj_index].data[y][x] = hexagon[y][x]; } } stages[WIND_STAGE].numberOfObj = 0; stages[WIND_STAGE].obj = NULL; // stage function pointer stages[WIND_STAGE].stageFunc = windStage; // wind stage -------------------------------------------------------------------------------- // sunny stage -------------------------------------------------------------------------------- stages[SUNNY_STAGE].possible = SUNNY_STAGE_POSSOIBLE; stages[SUNNY_STAGE].timer = 0; stages[SUNNY_STAGE].timeLimit = SUNNY_STAGE_TIME_LIMIT; sprintf(stages[SUNNY_STAGE].name,"sunny stage"); // point obj 0 // This obj give player score and point // If player miss this obj, these obj will not be score obj_index = 0; stages[SUNNY_STAGE].numberOfPreparedObj = 1; stages[SUNNY_STAGE].PreparedObj = malloc(sizeof(Graphic) * 1); stages[SUNNY_STAGE].PreparedObj[obj_index].position.x = 0; stages[SUNNY_STAGE].PreparedObj[obj_index].position.y = 0; stages[SUNNY_STAGE].PreparedObj[obj_index].size.x = 3; stages[SUNNY_STAGE].PreparedObj[obj_index].size.y = 5; allocateGraphicRam(&(stages[SUNNY_STAGE].PreparedObj[obj_index])); char point[5][3] = { {'P','P','P'}, {'P',' ','P'}, {'P','P','P'}, {'P',' ',' '}, {'P',' ',' '}}; for(int y = 0; y < stages[SUNNY_STAGE].PreparedObj[obj_index].size.y; y++){ for(int x = 0; x < stages[SUNNY_STAGE].PreparedObj[obj_index].size.x; x++){ stages[SUNNY_STAGE].PreparedObj[obj_index].data[y][x] = point[y][x]; } } stages[SUNNY_STAGE].numberOfObj = 0; stages[SUNNY_STAGE].obj = NULL; // stage function pointer stages[SUNNY_STAGE].stageFunc = sunnyStage; // synny stage -------------------------------------------------------------------------------- } int main(int argc,char ** args){ int selectedMode = START_BUTTON; Player player; Stage *stages; stages = malloc(sizeof(Stage) * 3); setStageParam(stages); initWholeGame(); // show start UI selectedMode = startUI(); initPlayer(&player); // OK while(1){ if(selectedMode == START_BUTTON){ initPlayer(&player); // OK StageLoop(&player,stages); // show end UI selectedMode = endUI(&player); }else if(selectedMode == RETURN_BUTTON){ selectedMode = startUI(); }else if(selectedMode == END_BUTTON){ break; } } // end curses and free ram endWholeGame(&player,stages); free(stages); return 0; }