Пример #1
0
/* 
 * game_loop
 *   DESCRIPTION: Main event loop for the adventure game.
 *   INPUTS: none
 *   OUTPUTS: none
 *   RETURN VALUE: GAME_QUIT if the player quits, or GAME_WON if they have won
 *   SIDE EFFECTS: drives the display, etc.
 */
static game_condition_t
game_loop ()
{
    /* 
     * Variables used to carry information between event loop ticks; see
     * initialization below for explanations of purpose.
     */
    struct timeval start_time, tick_time;

    struct timeval cur_time; /* current time (during tick)      */
    cmd_t cmd;               /* command issued by input control */
    int32_t enter_room;      /* player has changed rooms        */

    /* Record the starting time--assume success. */
    (void)gettimeofday (&start_time, NULL);

    /* Calculate the time at which the first event loop tick should occur. */
    tick_time = start_time;
    if ((tick_time.tv_usec += TICK_USEC) > 1000000) {
	tick_time.tv_sec++;
	tick_time.tv_usec -= 1000000;
    }

    /* The player has just entered the first room. */
    enter_room = 1;

    /* The main event loop. */
    while (1) {
	/* 
	 * Update the screen, preparing the VGA palette and photo-drawing
	 * routines and drawing a new room photo first if the player has
	 * entered a new room, then showing the screen (and status bar,
	 * once you have it working).
	 */
	if (enter_room) {
	    /* Reset the view window to (0,0). */
	    game_info.map_x = game_info.map_y = 0;
	    set_view_window (game_info.map_x, game_info.map_y);

	    /* Discard any partially-typed command. */
	    reset_typed_command ();
	    
	    /* Adjust colors and photo drawing for the current room photo. */
	    prep_room (game_info.where);

	    /* Draw the room (calls show. */
	    redraw_room ();

	    /* Only draw once on entry. */
	    enter_room = 0;
	}

	show_screen ();
	//lock status_msg to prevent changes
	(void)pthread_mutex_lock (&msg_lock);
	room_t* curr_room = game_info.where; // This is the current room pointer
	// Now call fill_status_bar with all the possible strings as params
	fill_status_bar(room_name(curr_room), get_typed_command(), status_msg);
	(void)pthread_mutex_unlock (&msg_lock); //unlock
	// Calculate game time
	display_time_on_tux (cur_time.tv_sec - start_time.tv_sec);
	/*
	 * Wait for tick.  The tick defines the basic timing of our
	 * event loop, and is the minimum amount of time between events.
	 */
	do {
	    if (gettimeofday (&cur_time, NULL) != 0) {
		/* Panic!  (should never happen) */
		clear_mode_X ();
		shutdown_input ();
		perror ("gettimeofday");
		exit (3);
	    }
	} while (!time_is_after (&cur_time, &tick_time));

	/*
	 * Advance the tick time.  If we missed one or more ticks completely, 
	 * i.e., if the current time is already after the time for the next 
	 * tick, just skip the extra ticks and advance the clock to the one
	 * that we haven't missed.
	 */
	do {
	    if ((tick_time.tv_usec += TICK_USEC) > 1000000) {
		tick_time.tv_sec++;
		tick_time.tv_usec -= 1000000;
	    }
	} while (time_is_after (&cur_time, &tick_time));

	/*
	 * Handle asynchronous events.  These events use real time rather
	 * than tick counts for timing, although the real time is rounded
	 * off to the nearest tick by definition.
	 */
	/* (none right now...) */

	/* 
	 * Handle synchronous events--in this case, only player commands. 
	 * Note that typed commands that move objects may cause the room
	 * to be redrawn.
	 */
	
	cmd = get_command ();
	switch (cmd) {
	    case CMD_UP:    move_photo_down ();  break;
	    case CMD_RIGHT: move_photo_left ();  break;
	    case CMD_DOWN:  move_photo_up ();    break;
	    case CMD_LEFT:  move_photo_right (); break;
	    case CMD_MOVE_LEFT:   
		enter_room = (TC_CHANGE_ROOM == 
			      try_to_move_left (&game_info.where));
		break;
	    case CMD_ENTER:
		enter_room = (TC_CHANGE_ROOM ==
			      try_to_enter (&game_info.where));
		break;
	    case CMD_MOVE_RIGHT:
		enter_room = (TC_CHANGE_ROOM == 
			      try_to_move_right (&game_info.where));
		break;
	    case CMD_TYPED:
		if (handle_typing ()) {
		    enter_room = 1;
		}
		break;
	    case CMD_QUIT: return GAME_QUIT;
	    default: break;
	}
	// Repeat the same thing for the tux
	cmd = get_tux_command();
	switch (cmd) {
	    case CMD_UP:    move_photo_down ();  break;
	    case CMD_RIGHT: move_photo_left ();  break;
	    case CMD_DOWN:  move_photo_up ();    break;
	    case CMD_LEFT:  move_photo_right (); break;
	    case CMD_MOVE_LEFT:   
		enter_room = (TC_CHANGE_ROOM == 
			      try_to_move_left (&game_info.where));
		break;
	    case CMD_ENTER:
		enter_room = (TC_CHANGE_ROOM ==
			      try_to_enter (&game_info.where));
		break;
	    case CMD_MOVE_RIGHT:
		enter_room = (TC_CHANGE_ROOM == 
			      try_to_move_right (&game_info.where));
		break;
	    case CMD_QUIT: return GAME_QUIT;
	    default: break;
	}
	/* If player wins the game, their room becomes NULL. */
	if (NULL == game_info.where) {
	    return GAME_WON;
	}
    } /* end of the main event loop */
}
Пример #2
0
/* 
 * handle_typing
 *   DESCRIPTION: Parse and execute a typed command.
 *   INPUTS: none (reads typed command)
 *   OUTPUTS: none
 *   RETURN VALUE: 1 if the player's room changes, 0 otherwise
 *   SIDE EFFECTS: may move the player, move objects, and/or redraw the screen
 */
static int32_t
handle_typing ()
{
    const char*      cmd;     /* command verb typed                */
    int32_t          cmd_len; /* length of command verb            */
    const char*      arg;     /* argument given to command verb    */
    int32_t          idx;     /* loop index over command list      */
    tc_action_t      result;  /* result of typed command execution */

    /* Read the command and strip leading spaces.  If it's empty, return. */
    cmd = get_typed_command ();
    while (' ' == *cmd) { cmd++; }
    if ('\0' == *cmd) { return 0; }

    /* 
     * Walk over the command verb, calculating its length as we go.  Space
     * or NUL marks the end of the verb, after which the argument begins.
     * Leading spaces are first stripped from the argument, but we make no
     * attempt to deal with trailing spaces (argument names must match
     * exactly).
     */
    for (cmd_len = 0; ' ' != cmd[cmd_len] && '\0' != cmd[cmd_len]; cmd_len++);
    arg = &cmd[cmd_len];
    while (' ' == *arg) { arg++; }

    /* Compare the typed verb with each command in our list. */
    for (idx = 0; NULL != cmd_list[idx].name; idx++) {

        /* If the typed verb is not long enough, it can't match. */
	if (cmd_list[idx].min_len > cmd_len) { continue; }

	/* Compare the prefix of the command with the typed verb. */
        if (0 != strncasecmp (cmd_list[idx].name, cmd, cmd_len)) { continue; }

	/* Execute the command found. */
	switch (cmd_list[idx].cmd) {
	    case TC_BUY:
	        result = typed_cmd_buy (&game_info.where, arg);
		break;
	    case TC_CHARGE:
	        result = typed_cmd_charge (&game_info.where, arg);
		break;
	    case TC_DO:
	        result = typed_cmd_do (&game_info.where, arg);
		break;
	    case TC_DRINK:
	        result = typed_cmd_drink (&game_info.where, arg);
		break;
	    case TC_DROP:
	        result = typed_cmd_drop (&game_info.where, arg);
		if (!player_has_board ()) {
		    game_info.x_speed = MOTION_SPEED;
		}
		if (!player_has_jetpack ()) {
		    game_info.y_speed = MOTION_SPEED;
		}
		break;
	    case TC_FIX:
	        result = typed_cmd_fix (&game_info.where, arg);
		break;
	    case TC_FLASH:
	        result = typed_cmd_flash (&game_info.where, arg);
		break;
	    case TC_GET:
	        result = typed_cmd_get (&game_info.where, arg);
		if (player_has_board ()) {
		    game_info.x_speed = MOTION_SPEED * 3;
		}
		if (player_has_jetpack ()) {
		    game_info.y_speed = MOTION_SPEED * 3;
		}
		break;
	    case TC_GO:
	        result = typed_cmd_go (&game_info.where, arg);
		break;
	    case TC_INSTALL:
	        result = typed_cmd_install (&game_info.where, arg);
		break;
	    case TC_INVENTORY:
	        result = typed_cmd_inventory (&game_info.where, arg);
		break;
	    case TC_SIGH:
	        result = typed_cmd_sigh (&game_info.where, arg);
		break;
	    case TC_USE:
	        result = typed_cmd_use (&game_info.where, arg);
		break;
	    case TC_WEAR:
	        result = typed_cmd_wear (&game_info.where, arg);
		break;
	    default:
		show_status ("Bug...!");
		result = TC_ALLOW_EDIT;
	        break;
	}

	/* Handle command result and return. */
	if (TC_CHANGE_ROOM == result) {
	    return 1;
	}
	if (TC_ALLOW_EDIT != result) {
	    reset_typed_command ();
	    if (TC_REDRAW_ROOM == result) {
	        redraw_room ();
	    }
	}
	return 0;
    }
    
    /* The command was not recognized. */
    show_status ("What are you babbling about?");
    return 0;
}
Пример #3
0
/*
 * status_thread
 *   DESCRIPTION: Function executed by status message helper thread.
 *                Waits for a message to be displayed, then shows the
 *                message for 1.5 seconds before deleting it.  If a
 *                new message has appeared in the meantime, restarts the
 *                clock and tries again.
 *   INPUTS: none (ignored)
 *   OUTPUTS: none
 *   RETURN VALUE: NULL
 *   SIDE EFFECTS: Changes the status message to an empty string.
 */
static void*
status_thread (void* ignore)
{
	struct timespec ts; /* absolute wake-up time */

	while (1) {

		/*
		 * Wait for a message to appear.  Note that we must check the
		 * condition after acquiring the lock, and that pthread_cond_wait
		 * yields the lock, then reacquires the lock before returning.
		 */
		(void)pthread_mutex_lock (&msg_lock);
		while ('\0' == status_msg[0]) {
			print_status_text(room_name(game_info.where), STATUS_ROOM_COLOR,
			                  STATUS_BG_COLOR, ALIGN_LEFT, 1);
			print_status_text(get_typed_command(), STATUS_COMMAND_COLOR,
			                  STATUS_BG_COLOR, ALIGN_RIGHT, 0);
			pthread_cond_wait (&msg_cv, &msg_lock);
		}

		/*
		 * A message is present: if we stop before the timeout
		 * passes, assume that a new one has been posted; if the
		 * timeout passes, clear the message and wait for a new one.
		 */
		do {
			/* Get the current time. */
			clock_gettime (CLOCK_REALTIME, &ts);
			const char *command = get_typed_command();
			int alignment = ALIGN_CENTER;
			if (command[0] != '\0') {
				alignment = ALIGN_LEFT;
			}
			print_status_text(status_msg, STATUS_FG_COLOR, STATUS_BG_COLOR,
			                  alignment, 1);
			print_status_text(command, STATUS_COMMAND_COLOR, STATUS_BG_COLOR,
			                  ALIGN_RIGHT, 0);

			/* Add 1.5 seconds to it. */
			if (500000000 <= ts.tv_nsec) {
				ts.tv_sec += 2;
				ts.tv_nsec -= 500000000;
			} else {
				ts.tv_sec += 1;
				ts.tv_nsec += 500000000;
			}

			/*
			 * And go to sleep.  If we wake up due to anything but a
			 * timeout, we assume (possibly incorrectly) that a new
			 * message has appeared and try to wait 1.5 seconds again.
			 */
		} while (ETIMEDOUT !=
		         pthread_cond_timedwait (&msg_cv, &msg_lock, &ts));

		/*
		 * Clear the message, then release the lock (remember that
		 * pthread_cond_timedwait reacquires the lock before returning).
		 */
		status_msg[0] = '\0';
		(void)pthread_mutex_unlock (&msg_lock);
	}

	/* This code never executes--the thread should always be cancelled. */
	return NULL;
}