xlib_playground

Xlib playground for experiments.
Log | Files | Refs

main.c (12571B)


      1 #include <stdio.h>
      2 #include <stdlib.h>
      3 #include <stdint.h>
      4 #include <time.h>
      5 #include <unistd.h>
      6 #include <string.h>
      7 
      8 #include <sys/resource.h> // for debuging of memory leak
      9 
     10 #include "list.h"
     11 #include "object.h"
     12 #include "draw.h"
     13 #include "world_map.h"
     14 
     15 /*
     16 #define COUNT_FPS
     17 */
     18 
     19 /* constants */
     20 enum {
     21 	FPS = 60,
     22 	SUB_FRAME = 4,
     23 	GRAVITY = 1000,
     24 	WIN_WIDTH = 800,
     25 	WIN_HEIGHT = 600,
     26 };
     27 
     28 enum keys {
     29 	KEY_Q,
     30 	KEY_I,
     31 	KEY_D,
     32 	KEY_S,
     33 	KEY_A,
     34 	KEY_W,
     35 	KEY_SPACE,
     36 	NUM_KEY, //number of keys in this enum
     37 };
     38 enum key_state {
     39 	KEY_UP,
     40 	KEY_DOWN,
     41 };
     42 enum next_menu {
     43 	START_MENU,
     44 	GAME_PLAY,
     45 	GAME_OVER,
     46 	GAME_CLEAR,
     47 	QUIT,
     48 };
     49 enum debug_msg {
     50 	DFPS,
     51 	DONFLOOR,
     52 	DPOS,
     53 	DVEL,
     54 	DMEM,
     55 	NDMSG,
     56 	DMAXLEN = 128,
     57 };
     58 
     59 /* variables */
     60 enum next_menu next_menu = START_MENU;
     61 void (* col_func[NUM_OBJ_TYPE][NUM_OBJ_TYPE])(Object *, Object *) = {0};
     62 
     63 /* function prototypes */
     64 /* menus */
     65 void start_menu(void);
     66 void game_play(void);
     67 void game_over(void);
     68 void game_clear(void);
     69 /* events */
     70 void receive_events(enum key_state[]);
     71 void handle_inputs(enum key_state[], Object *, int *);
     72 /* collision functions */
     73 void col_pf(Object *, Object *); // player-flag
     74 void col_pb(Object *, Object *); // player-block
     75 void col_pe(Object *, Object *); // player-enemy
     76 void col_eb(Object *, Object *); // enemy-block
     77 void col_ee(Object *, Object *); // enemy-enemy
     78 
     79 /* misc: these functions use interfaces from multiple header files */
     80 /* TODO: I want to rewrite these more cleanly */
     81 void draw_object(const Object *);
     82 void draw_object_scroll(const Object *, int);
     83 
     84 void
     85 start_menu(void)
     86 {
     87 	char *menu_char_q = "press q to quit.";
     88 	char *menu_char_s = "press <space> to start.";
     89 
     90 	x_clear_area();
     91 	x_draw_string(0x00FFFF,
     92 				  WIN_WIDTH / 2 - 10 * strlen(menu_char_q)/2,
     93 				  WIN_HEIGHT / 2,
     94 				  menu_char_q, strlen(menu_char_q));
     95 	x_draw_string(0x00FFFF,
     96 				  WIN_WIDTH / 2 - 10 * strlen(menu_char_s)/2,
     97 				  WIN_HEIGHT / 2 + 20,
     98 				  menu_char_s, strlen(menu_char_s));
     99 
    100 	while (next_menu == START_MENU) {
    101 		char c;
    102 		int  event = x_next_event(&c);
    103 
    104 		switch (event) {
    105 		case XEXPOSE:
    106 			x_draw_string(0x00FFFF,
    107 						  WIN_WIDTH / 2 - 10 * strlen(menu_char_q)/2,
    108 						  WIN_HEIGHT / 2,
    109 						  menu_char_q, strlen(menu_char_q));
    110 			x_draw_string(0x00FFFF,
    111 						  WIN_WIDTH / 2 - 10 * strlen(menu_char_s)/2,
    112 						  WIN_HEIGHT / 2 + 20,
    113 						  menu_char_s, strlen(menu_char_s));
    114 
    115 			break;
    116 		case XWINDEL:
    117 			next_menu = QUIT;
    118 			break;
    119 		case XKEYPRESS:
    120 			switch (c) {
    121 			case 'q':
    122 				next_menu = QUIT;
    123 				break;
    124 			case ' ':
    125 				next_menu = GAME_PLAY;
    126 				break;
    127 			default:
    128 				break;
    129 			}
    130 			break;
    131 		default:
    132 			break;
    133 		}
    134 	}
    135 }
    136 
    137 void
    138 game_play(void)
    139 {
    140 	enum key_state key_state[NUM_KEY];
    141 	long t0, t1, dt;
    142 	struct timespec ts;
    143 	List *ol = NULL; // block list
    144 	Object *player = NULL;
    145 	List *olc = NULL; // cursor used to scan ol
    146 	int scroll_dst = 0;
    147 	int scroll_margin = WIN_WIDTH * 2 / 5;
    148 
    149 	int debug = 0;
    150 	int frame_count = 0;
    151 	int cur_fps = 0;
    152 	char debug_msg[NDMSG][DMAXLEN] = {0};
    153 
    154 	struct rusage rusage; // for debuging of memory leak
    155 
    156 	/* setup collision handler */
    157 	col_func[TPLAYER][TFLAG] = col_func[TFLAG][TPLAYER] = &col_pf;
    158 	col_func[TPLAYER][TBLOCK] = col_func[TBLOCK][TPLAYER] = &col_pb;
    159 	col_func[TPLAYER][TENEMY] = col_func[TENEMY][TPLAYER] = &col_pe;
    160 	col_func[TENEMY][TBLOCK] = col_func[TBLOCK][TENEMY] = &col_eb;
    161 	col_func[TENEMY][TENEMY] = col_func[TENEMY][TENEMY] = &col_ee;
    162 
    163 	/* load world into ol */
    164     for (int i = 0; i < WORLD_WIDTH * WORLD_HEIGHT; i++) {
    165         if (world_map[i] == 'b') {
    166             olc = laddfront(ol, create_object(TBLOCK, 0x00FF00,
    167 				i % WORLD_WIDTH * BLOCK_SIZE,
    168 				i / WORLD_WIDTH * BLOCK_SIZE,
    169 				0, 0, 0, 0,
    170 				BLOCK_SIZE, BLOCK_SIZE,
    171 				BLOCK_SIZE * BLOCK_SIZE,
    172 				NULL));
    173 			if (olc == NULL) {
    174 				fprintf(stderr, "main: error. adding item to list ol\n");
    175 			} else {
    176 				ol = olc;
    177 			}
    178         } else if (world_map[i] == 'p') {
    179             player = create_object(TPLAYER, 0x0000FF,
    180 				i % WORLD_WIDTH * BLOCK_SIZE,
    181 				i / WORLD_WIDTH * BLOCK_SIZE,
    182 				0, 0, 0, GRAVITY,
    183 				BLOCK_SIZE, BLOCK_SIZE,
    184 				BLOCK_SIZE * BLOCK_SIZE,
    185 				NULL);
    186 			olc = laddfront(ol, player);
    187 			if (player == NULL || olc == NULL) {
    188 				fprintf(stderr, "main: error. creating player\n");
    189 				exit(1);
    190 			} else {
    191 				ol = olc;
    192 			}
    193 		} else if (world_map[i] == 'e') {
    194 			olc = laddfront(ol, create_object(TENEMY, 0xFF0000,
    195 				i % WORLD_WIDTH * BLOCK_SIZE,
    196 				i / WORLD_WIDTH * BLOCK_SIZE,
    197 				-10, 0, 0, GRAVITY,
    198 				BLOCK_SIZE, BLOCK_SIZE,
    199 				BLOCK_SIZE * BLOCK_SIZE,
    200 				NULL));
    201 			if (olc == NULL) {
    202 				fprintf(stderr, "main: error. adding item to list ol\n");
    203 			} else {
    204 				ol = olc;
    205 			}
    206 		} else if (world_map[i] == 'f') {
    207 			olc = laddfront(ol, create_object(TFLAG, 0xFFFF00,
    208 				i % WORLD_WIDTH * BLOCK_SIZE,
    209 				i / WORLD_WIDTH * BLOCK_SIZE,
    210 				0, 0, 0, 0,
    211 				BLOCK_SIZE, BLOCK_SIZE,
    212 				BLOCK_SIZE * BLOCK_SIZE,
    213 				NULL));
    214 			if (olc == NULL) {
    215 				fprintf(stderr, "main: error. adding item to list ol\n");
    216 			} else {
    217 				ol = olc;
    218 			}
    219 		}
    220 	}
    221 
    222 	while (next_menu == GAME_PLAY){
    223 		receive_events(key_state);
    224 		handle_inputs(key_state, player, &debug);
    225 		if (debug) {
    226 			snprintf(debug_msg[DONFLOOR], DMAXLEN,
    227 				"on_floor: %d", player->on_floor);
    228 		}
    229 
    230 		clock_gettime(CLOCK_MONOTONIC, &ts);
    231 		t0 = ts.tv_nsec;
    232 
    233 		for (int k = 0; k < SUB_FRAME; k++) {
    234 			for (olc = ol; olc != NULL; olc = olc->next) {
    235 				((Object *)olc->item)->next_tick((Object *)olc->item,
    236 					1e9 / FPS / SUB_FRAME);
    237 			}
    238 
    239 			// game over when fall out of the world
    240 			if (player->p.y > WORLD_HEIGHT * BLOCK_SIZE) {
    241 				next_menu = GAME_OVER;
    242 				break;
    243 			}
    244 			// bind within the world
    245 			if (player->p.x < 0) {
    246 				player->p.x = 0;
    247 				if (player->v.x < 0)
    248 					player->v.x = 0;
    249 			}
    250 			if (WORLD_WIDTH * BLOCK_SIZE < player->p.x +
    251 			    player->body.rectangle.w) {
    252 				player->p.x = WORLD_WIDTH * BLOCK_SIZE -
    253 				             player->body.rectangle.w;
    254 				if (player->v.x > 0)
    255 					player->v.x = 0;
    256 			}
    257 
    258 			// scrolling
    259 			if (player->p.x - scroll_dst > WIN_WIDTH - scroll_margin) {
    260 				scroll_dst = player->p.x - WIN_WIDTH + scroll_margin;
    261 				if (scroll_dst > WORLD_WIDTH * BLOCK_SIZE - WIN_WIDTH)
    262 					scroll_dst = WORLD_WIDTH * BLOCK_SIZE - WIN_WIDTH;
    263 			}
    264 			if (player->p.x - scroll_dst < scroll_margin) {
    265 				scroll_dst = player->p.x - scroll_margin;
    266 				if (scroll_dst < 0)
    267 					scroll_dst = 0;
    268 			}
    269 
    270 			player->on_floor = 0;
    271 			if (debug) {
    272 				snprintf(debug_msg[DVEL], DMAXLEN,
    273 					"vel: (%3.0f, %3.0f)", player->v.x, player->v.y);
    274 			}
    275 
    276 
    277 			lhandle_collision(ol);
    278 		}
    279 
    280 		// fix fps
    281 		// TODO: This method create some strange stripe when
    282 		// rendered in 60fps on a 60fps monitor.
    283 		dt = 0;
    284 		while (dt < 1.0 * 1e9 / FPS){
    285 			clock_gettime(CLOCK_MONOTONIC, &ts);
    286 			t1 = ts.tv_nsec;
    287 			dt = t1 > t0 ? t1 - t0 : t1 - t0 + 1e9;
    288 		}
    289 
    290 		// count fps.
    291 		if (debug) {
    292 			frame_count++;
    293 			if (t1 < t0){
    294 				cur_fps = frame_count;
    295 				snprintf(debug_msg[DFPS], DMAXLEN, "fps: %d", cur_fps);
    296 				frame_count = 0;
    297 			}
    298 
    299 			snprintf(debug_msg[DPOS], DMAXLEN,
    300 				"pos: (%3.2f, %3.2f)", player->p.x, player->p.y);
    301 
    302 			getrusage(RUSAGE_SELF, &rusage);
    303 			snprintf(debug_msg[DMEM], DMAXLEN, "mem: %ld", rusage.ru_maxrss);
    304 		}
    305 
    306 		x_clear_area();
    307 		for (olc = ol; olc != NULL; olc = olc->next) {
    308 			draw_object_scroll((Object *)olc->item, scroll_dst);
    309 		}
    310 
    311 		if (debug) {
    312 			for (int i = 0; i < NDMSG; i++) {
    313 				x_draw_string(0xFFFFFF,
    314 					10, (i + 1) * 12,
    315 					debug_msg[i], strlen(debug_msg[i]));
    316 			}
    317 		}
    318 	}
    319 	lfreei(ol, (void (*)(void *))&free_object);
    320 }
    321 
    322 void
    323 game_over(void)
    324 {
    325 	char *menu_char = "GAME OVER";
    326 
    327 	x_draw_string(0x00FFFF,
    328 				WIN_WIDTH / 2 - 10 * strlen(menu_char)/2,
    329 				WIN_HEIGHT / 2,
    330 				menu_char, strlen(menu_char));
    331 	x_flush();
    332 
    333 	sleep(1);
    334 	while(x_pending() > 0) {
    335 		x_next_event(NULL);
    336 	}
    337 	next_menu = START_MENU;
    338 }
    339 
    340 void
    341 game_clear(void)
    342 {
    343 	char *menu_char = "GAME CLEAR";
    344 
    345 	x_draw_string(0x00FFFF,
    346 				WIN_WIDTH / 2 - 10 * strlen(menu_char)/2,
    347 				WIN_HEIGHT / 2,
    348 				menu_char, strlen(menu_char));
    349 	x_flush();
    350 
    351 	sleep(1);
    352 	while(x_pending() > 0) {
    353 		x_next_event(NULL);
    354 	}
    355 	next_menu = START_MENU;
    356 }
    357 
    358 void
    359 receive_events(enum key_state key_state[])
    360 {
    361 	while (x_pending() > 0) {
    362 		char c;
    363 		int event = x_next_event(&c);
    364 
    365 		switch (event) {
    366 		case XWINDEL:
    367 			next_menu = QUIT;
    368 			break;
    369 		case XKEYPRESS:
    370 			switch (c) {
    371 			case 'q':
    372 				key_state[KEY_Q] = KEY_DOWN;
    373 				break;
    374 			case 'i':
    375 				key_state[KEY_I] = KEY_DOWN;
    376 				break;
    377 			case 'd':
    378 				key_state[KEY_D] = KEY_DOWN;
    379 				break;
    380 			case 'a':
    381 				key_state[KEY_A] = KEY_DOWN;
    382 				break;
    383 			case 'w':
    384 				key_state[KEY_W] = KEY_DOWN;
    385 				break;
    386 			case 's':
    387 				key_state[KEY_S] = KEY_DOWN;
    388 				break;
    389 			case ' ':
    390 				key_state[KEY_SPACE] = KEY_DOWN;
    391 				break;
    392 			default:
    393 				break;
    394 			}
    395 			break;
    396 		case XKEYRELEASE:
    397 			switch (c) {
    398 			case 'q':
    399 				key_state[KEY_Q] = KEY_UP;
    400 				break;
    401 			case 'i':
    402 				key_state[KEY_I] = KEY_UP;
    403 				break;
    404 			case 'd':
    405 				key_state[KEY_D] = KEY_UP;
    406 				break;
    407 			case 'a':
    408 				key_state[KEY_A] = KEY_UP;
    409 				break;
    410 			case 'w':
    411 				key_state[KEY_W] = KEY_UP;
    412 				break;
    413 			case 's':
    414 				key_state[KEY_S] = KEY_UP;
    415 				break;
    416 			case ' ':
    417 				key_state[KEY_SPACE] = KEY_UP;
    418 				break;
    419 			default:
    420 				break;
    421 			}
    422 			break;
    423 		}
    424 	}
    425 }
    426 
    427 void
    428 handle_inputs(enum key_state key_state[], Object *player, int *debug)
    429 {
    430 	static int key_i_before = KEY_UP;
    431 
    432 	if (key_state[KEY_Q] == KEY_DOWN) {
    433 		next_menu = GAME_OVER;
    434 		return;
    435 	}
    436 	if (key_state[KEY_I] == KEY_DOWN && key_i_before == KEY_UP) {
    437 		*debug = !*debug;
    438 		key_i_before = KEY_DOWN;
    439 	}
    440 	if (key_state[KEY_I] == KEY_UP && key_i_before == KEY_DOWN) {
    441 		key_i_before = KEY_UP;
    442 	}
    443 
    444 	if (key_state[KEY_D] == KEY_DOWN && key_state[KEY_A] != KEY_DOWN) {
    445 		if (player->v.x > 0) {
    446 			player->a.x = 500;
    447 		} else {
    448 			player->a.x = 1000;
    449 		}
    450 	} else if (key_state[KEY_A] == KEY_DOWN && key_state[KEY_D] != KEY_DOWN) {
    451 		if (player->v.x > 0) {
    452 			player->a.x = -1000;
    453 		} else {
    454 			player->a.x = -500;
    455 		}
    456 	} else {
    457 		if (player->on_floor)
    458 			player->a.x = -3 * player->v.x;
    459 		else
    460 			player->a.x = -player->v.x;
    461 	}
    462 
    463 	if (player->v.x < -200) player->v.x = -200;
    464 	if (player->v.x > 200) player->v.x = 200;
    465 	if (player->on_floor && key_state[KEY_SPACE] == KEY_DOWN) {
    466 		player->v.y = -450;
    467 	}
    468 }
    469 
    470 void
    471 col_pf(Object *o0, Object *o1)
    472 {
    473 	if (!test_collision(o0, o1))
    474 		return;
    475 	if (o0->type == TFLAG && o1->type == TPLAYER)
    476 		handle_collision_mf(o1, o0);
    477 	else if (o0->type == TPLAYER && o1->type == TFLAG)
    478 		handle_collision_mf(o0, o1);
    479 	else
    480 		fprintf(stderr, "col_pf: Error. invalid object types\n");
    481 	next_menu = GAME_CLEAR;
    482 }
    483 
    484 void
    485 col_pb(Object *op, Object *ob)
    486 {
    487 	if (op->type == TBLOCK && ob->type == TPLAYER) {
    488 		handle_collision_mf(ob, op);
    489 		if(is_on_floor(ob, op))
    490 			ob->on_floor = 1;
    491 	} else if (op->type == TPLAYER && ob->type == TBLOCK) {
    492 		handle_collision_mf(op, ob);
    493 		if(is_on_floor(op, ob))
    494 			op->on_floor = 1;
    495 	} else {
    496 		fprintf(stderr, "col_pb: invalid object type: [%d, %d]\n",
    497 			op->type, ob->type);
    498 	}
    499 }
    500 
    501 void
    502 col_eb(Object *oe, Object *ob)
    503 {
    504 	float pvx;
    505 	if (oe->type == TBLOCK && ob->type == TENEMY) {
    506 		pvx = ob->v.x;
    507 		handle_collision_mf(ob, oe);
    508 		if (ob->v.x == 0)
    509 			ob->v.x = -pvx;
    510 	} else if (oe->type == TENEMY && ob->type == TBLOCK) {
    511 		pvx = oe->v.x;
    512 		handle_collision_mf(oe, ob);
    513 		if (oe->v.x == 0)
    514 			oe->v.x = -pvx;
    515 	} else {
    516 		fprintf(stderr, "col_pb: invalid object type: [%d, %d]\n",
    517 			oe->type, ob->type);
    518 	}
    519 }
    520 
    521 void
    522 col_ee(Object *o0, Object *o1)
    523 {
    524 	o0->v.x *= -1;
    525 	o1->v.x *= -1;
    526 }
    527 
    528 void
    529 col_pe(Object *op, Object *oe)
    530 {
    531 	next_menu = GAME_OVER;
    532 }
    533 
    534 
    535 void
    536 draw_object(const Object *o)
    537 {
    538 	if (o->shape == SRECTANGLE) {
    539 		x_draw_rectangle(o->color,
    540 						 o->p.x, o->p.y,
    541 						 o->body.rectangle.w,
    542 						 o->body.rectangle.h);
    543 	} else {
    544 		fprintf(stderr, "draw_object: Drawing object other than rectangle is"
    545 						 "not implemented yet.\n");
    546 	}
    547 }
    548 
    549 void
    550 draw_object_scroll(const Object *o, int sd)
    551 {
    552 	if (o->shape == SRECTANGLE) {
    553 		if (sd - o->body.rectangle.w <= o->p.x &&
    554 			o->p.x < WIN_WIDTH + sd) {
    555 			x_draw_rectangle(o->color,
    556 				o->p.x - sd, o->p.y,
    557 				o->body.rectangle.w,
    558 				o->body.rectangle.h);
    559 		}
    560 	} else {
    561 		fprintf(stderr, "draw_object_scroll: Drawing object other than"
    562 						"rectangle is not implemented yet.\n");
    563 	}
    564 }
    565 
    566 int
    567 main(void)
    568 {
    569 	x_setup_window(0, 0,
    570 				   WIN_WIDTH, WIN_HEIGHT,
    571 				   0x000000, "UNKO");
    572 	while (next_menu != QUIT){
    573 		switch (next_menu){
    574 		case START_MENU:
    575 			start_menu();
    576 			break;
    577 		case GAME_PLAY:
    578 			game_play();
    579 			break;
    580 		case GAME_OVER:
    581 			game_over();
    582 			break;
    583 		case GAME_CLEAR:
    584 			game_clear();
    585 			break;
    586 		default:
    587 			break;
    588 		}
    589 	}
    590 
    591 	x_clean_up();
    592 	return 0;
    593 }