xlib_playground

Xlib playground for experiments.
Log | Files | Refs

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 }