main.c (8357B)
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <time.h> 4 #include <unistd.h> 5 #include <string.h> 6 7 #include "main.h" 8 #include "world_map.h" 9 10 /* macros */ 11 #define FPS (60) 12 #define SUB_TICK (4) 13 #define NUM_BLOCK (200) 14 #define GRAVITY (1000) 15 16 /* 17 #define COUNT_FPS 18 */ 19 20 enum keys { 21 KEY_D, 22 KEY_S, 23 KEY_A, 24 KEY_W, 25 KEY_Q, 26 KEY_SPACE, 27 NUM_KEY, //number of keys in this enum 28 }; 29 enum key_state { 30 KEY_UP, 31 KEY_DOWN, 32 }; 33 enum next_menu { 34 START_MENU, 35 GAME_PLAY, 36 GAME_OVER, 37 QUIT, 38 }; 39 40 /* variables */ 41 struct Object *player; 42 int player_is_falling; 43 enum next_menu next_menu = START_MENU; 44 45 46 /* function prototypes */ 47 /* menus */ 48 void start_menu(void); 49 void game_play(void); 50 void game_over(void); 51 /* events */ 52 void receive_events(enum key_state[]); 53 void handle_inputs(enum key_state[]); 54 /* sort */ 55 void sort_ll(struct OL *ol, struct Object *player); 56 57 void 58 start_menu(void) 59 { 60 char *menu_char_q = "press q to quit."; 61 char *menu_char_s = "press <space> to start."; 62 63 x_clear_area(); 64 x_draw_string(0x00FFFF, 65 WORLD_WIDTH * BLOCK_SIZE/2 - 10 * strlen(menu_char_q)/2, 66 WORLD_HEIGHT * BLOCK_SIZE/2, 67 menu_char_q, strlen(menu_char_q)); 68 x_draw_string(0x00FFFF, 69 WORLD_WIDTH * BLOCK_SIZE/2 - 10 * strlen(menu_char_s)/2, 70 WORLD_HEIGHT * BLOCK_SIZE/2 + 20, 71 menu_char_s, strlen(menu_char_s)); 72 73 while (next_menu == START_MENU) { 74 char c; 75 int event = x_next_event(&c); 76 77 switch (event) { 78 case XEXPOSE: 79 x_draw_string(0x00FFFF, 80 WORLD_WIDTH * BLOCK_SIZE/2 - 10 * strlen(menu_char_q)/2, 81 WORLD_HEIGHT * BLOCK_SIZE/2, 82 menu_char_q, strlen(menu_char_q)); 83 x_draw_string(0x00FFFF, 84 WORLD_WIDTH * BLOCK_SIZE/2 - 10 * strlen(menu_char_s)/2, 85 WORLD_HEIGHT * BLOCK_SIZE/2 + 20, 86 menu_char_s, strlen(menu_char_s)); 87 88 break; 89 case XWINDEL: 90 next_menu = QUIT; 91 break; 92 case XKEYPRESS: 93 switch (c) { 94 case 'q': 95 next_menu = QUIT; 96 break; 97 case ' ': 98 next_menu = GAME_PLAY; 99 break; 100 default: 101 break; 102 } 103 break; 104 default: 105 break; 106 } 107 } 108 } 109 110 void 111 game_play(void) 112 { 113 enum key_state key_state[NUM_KEY]; 114 long t0, t1, dt; 115 #ifdef COUNT_FPS 116 int fps_count = 0; 117 #endif 118 struct timespec ts; 119 struct OH *blh[WORLD_WIDTH]; // block list header 120 struct OL *blc; 121 122 for (int xi = 0; xi < WORLD_WIDTH; xi++) 123 blh[xi] = create_ol(); 124 125 for (int i = 0; i < WORLD_WIDTH * WORLD_HEIGHT; i++) { 126 if (world_map[i] == 'b') { 127 int xi = i % WORLD_WIDTH; 128 // TODO: don't forget to free these things. 129 append_ol(blh[xi], create_object(i % WORLD_WIDTH * BLOCK_SIZE, 130 i / WORLD_WIDTH * BLOCK_SIZE, 131 0, 0, 0, 0, 132 BLOCK_SIZE, BLOCK_SIZE, 133 BLOCK_SIZE * BLOCK_SIZE)); 134 } else if (world_map[i] == 'p') { 135 player = create_object(i % WORLD_WIDTH * BLOCK_SIZE, 136 i / WORLD_WIDTH * BLOCK_SIZE, 137 0, 0, 0, GRAVITY, 138 BLOCK_SIZE, BLOCK_SIZE, 139 BLOCK_SIZE * BLOCK_SIZE); 140 } 141 } 142 143 while (next_menu == GAME_PLAY){ 144 receive_events(key_state); 145 handle_inputs(key_state); 146 147 clock_gettime(CLOCK_MONOTONIC, &ts); 148 t0 = ts.tv_nsec; 149 150 for (int k = 0; k < SUB_TICK; k++) { 151 next_tick(player, 1e9 / FPS / SUB_TICK); 152 153 // game over when fall out of the world 154 if (player->p.y > WORLD_HEIGHT * BLOCK_SIZE) { 155 next_menu = GAME_OVER; 156 break; 157 } 158 // bind within the world 159 if (player->p.x < 0) { 160 player->p.x = 0; 161 } 162 if (WORLD_WIDTH * BLOCK_SIZE < player->p.x + 163 player->body.rectangle.w) { 164 player->p.x = WORLD_WIDTH * BLOCK_SIZE - 165 player->body.rectangle.w; 166 } 167 168 struct OH *collidings; 169 collidings = create_ol(); 170 171 player_is_falling = 1; 172 for (int xi = (int) player->p.x / BLOCK_SIZE; 173 xi <= (int) player->p.x / BLOCK_SIZE + 1 && xi < WORLD_WIDTH; 174 xi++) { 175 blc = blh[xi]->first; 176 while (blc != NULL) { 177 if (is_on_floor_before(player, blc->o)) { 178 player_is_falling = 0; 179 } 180 if (test_collision(player, blc->o)) 181 append_ol(collidings, blc->o); 182 blc = blc->next; 183 } 184 } 185 if (collidings->first != NULL) { 186 sort_ol(collidings, player); 187 blc = collidings->first; 188 while (blc != NULL) { 189 handle_collision_mf(player, blc->o); 190 blc = blc->next; 191 } 192 } 193 free_ol(collidings); 194 } 195 196 // fix fps 197 // TODO: This method create some strange stripe when 198 // rendered in 60fps on a 60fps monitor. 199 dt = 0; 200 while (dt < 1.0 * 1e9 / FPS){ 201 clock_gettime(CLOCK_MONOTONIC, &ts); 202 t1 = ts.tv_nsec; 203 dt = t1 > t0 ? t1 - t0 : t1 - t0 + 1e9; 204 } 205 #ifdef COUNT_FPS 206 // count fps. 207 fps_count++; 208 if (t1 < t0){ 209 printf("fps: %u\n", fps_count); 210 fps_count = 0; 211 } 212 #endif 213 214 x_clear_area(); 215 for (int xi = 0; xi < WORLD_WIDTH; xi++) { 216 blc = blh[xi]->first; 217 while (blc != NULL) { 218 x_draw_rectangle(0x00FF00, 219 blc->o->p.x, blc->o->p.y, // position 220 blc->o->body.rectangle.w, 221 blc->o->body.rectangle.h); 222 blc = blc->next; 223 } 224 } 225 x_draw_rectangle(0x009FFF, 226 player->p.x, player->p.y, // position 227 player->body.rectangle.w, player->body.rectangle.h); 228 char status_string[128]; 229 snprintf(status_string, 128, "falling: %d", player_is_falling); 230 x_draw_string(0x00FFFF, 231 0, 20, 232 status_string, 233 strlen(status_string)); 234 } 235 for (int xi = 0; xi < WORLD_WIDTH; xi++) 236 free_obj_and_ol(blh[xi]); 237 } 238 239 void 240 game_over(void) 241 { 242 char *menu_char = "GAME OVER"; 243 244 x_clear_area(); 245 x_draw_string(0x00FFFF, 246 WORLD_WIDTH * BLOCK_SIZE/2 - 10 * strlen(menu_char)/2, 247 WORLD_HEIGHT * BLOCK_SIZE/2, 248 menu_char, strlen(menu_char)); 249 x_flush(); 250 251 sleep(1); 252 while(x_pending() > 0) { 253 x_next_event(NULL); 254 } 255 next_menu = START_MENU; 256 } 257 258 void 259 receive_events(enum key_state key_state[]) 260 { 261 while (x_pending() > 0) { 262 char c; 263 int event = x_next_event(&c); 264 265 switch (event) { 266 case XWINDEL: 267 next_menu = QUIT; 268 break; 269 case XKEYPRESS: 270 switch (c) { 271 case 'q': 272 key_state[KEY_Q] = KEY_DOWN; 273 break; 274 case 'd': 275 key_state[KEY_D] = KEY_DOWN; 276 break; 277 case 'a': 278 key_state[KEY_A] = KEY_DOWN; 279 break; 280 case 'w': 281 key_state[KEY_W] = KEY_DOWN; 282 break; 283 case 's': 284 key_state[KEY_S] = KEY_DOWN; 285 break; 286 case ' ': 287 key_state[KEY_SPACE] = KEY_DOWN; 288 break; 289 default: 290 break; 291 } 292 break; 293 case XKEYRELEASE: 294 switch (c) { 295 case 'q': 296 key_state[KEY_Q] = KEY_UP; 297 break; 298 case 'd': 299 key_state[KEY_D] = KEY_UP; 300 break; 301 case 'a': 302 key_state[KEY_A] = KEY_UP; 303 break; 304 case 'w': 305 key_state[KEY_W] = KEY_UP; 306 break; 307 case 's': 308 key_state[KEY_S] = KEY_UP; 309 break; 310 case ' ': 311 key_state[KEY_SPACE] = KEY_UP; 312 break; 313 default: 314 break; 315 } 316 break; 317 } 318 } 319 } 320 321 void 322 handle_inputs(enum key_state key_state[]) 323 { 324 if (key_state[KEY_Q] == KEY_DOWN){ 325 next_menu = GAME_OVER; 326 return; 327 } 328 if (key_state[KEY_D] == KEY_DOWN && key_state[KEY_A] != KEY_DOWN) { 329 if (player->v.x > 0) { 330 player->a.x = 500; 331 } else { 332 player->a.x = 1000; 333 } 334 } else if (key_state[KEY_A] == KEY_DOWN && key_state[KEY_D] != KEY_DOWN) { 335 if (player->v.x > 0) { 336 player->a.x = -1000; 337 } else { 338 player->a.x = -500; 339 } 340 } else { 341 if (player_is_falling) 342 player->a.x = -player->v.x; 343 else 344 player->a.x = -3 * player->v.x; 345 } 346 347 if (player->v.x < -200) player->v.x = -200; 348 if (player->v.x > 200) player->v.x = 200; 349 /* 350 if (key_state[KEY_S] == KEY_DOWN) 351 player->v.y += 300; 352 if (key_state[KEY_W] == KEY_DOWN) 353 player->v.y += -300; 354 */ 355 if (!player_is_falling && key_state[KEY_SPACE] == KEY_DOWN) { 356 player->v.y = -450; 357 } 358 } 359 360 void 361 swap_ll(struct OL *oi, struct OL *oj) 362 { 363 struct Object *tmp; 364 tmp = oi->o; 365 oi->o = oj->o; 366 oj->o = tmp; 367 } 368 369 void 370 sort_ll(struct OL *ol, struct Object *player) 371 { 372 if (ol == NULL) 373 return; 374 struct OL *oi, *oj; 375 for (oi = ol; oi->next != NULL; oi = oi->next) { 376 for (oj = oi->next; oj != NULL; oj = oj->next) { 377 if (object_dist(oj->o, player) < object_dist(oi->o, player)) 378 swap_ll(oi, oj); 379 } 380 } 381 } 382 383 int 384 main(void) 385 { 386 x_setup_window(0, 0, 387 WORLD_WIDTH * BLOCK_SIZE, WORLD_HEIGHT * BLOCK_SIZE, 388 0x000000, "UNKO"); 389 while (next_menu != QUIT){ 390 switch (next_menu){ 391 case START_MENU: 392 start_menu(); 393 break; 394 case GAME_PLAY: 395 game_play(); 396 break; 397 case GAME_OVER: 398 game_over(); 399 break; 400 default: 401 break; 402 } 403 } 404 405 x_clean_up(); 406 return 0; 407 }