xlib_playground

Xlib playground for experiments.
Log | Files | Refs

main.c (8293B)


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