Example #1
0
/* Reset the game state, called prior to starting a new game. */
void reset_game(nbstate *state)
{
	/* Start on the title screen, level 0: */
	state->state = STATE_TITLESCREEN;
	state->level = 0;
	state->scores.s = 0; /* Start with a score of 0. */
	state->numballs = state->startballs; /* Set the initial balls. */
	park_ball(state); /* Park the current ball. */
	/* Reset all the power-up and power-down timers: */
	state->powertimes.widebat = 0;
	state->powertimes.slowmotion = 0;
	state->powertimes.stickybat = 0;
	state->powertimes.powerball = 0;
	state->powertimes.narrowbat = 0;
	state->powertimes.fastmotion = 0;
	/* Reset to the normal bat size: */
	state->bat = NORMALBAT;
	/* Reset to the normal ball velocity: */
	state->ball.v = state->ball.nv;
	/* Draw the title screen: */
	set_level_active(state);
}
Example #2
0
/* Increment to the next level. */
void increment_level(nbstate *state)
{
	/* If we are already on the last level: */
	if(state->level == state->numlevels) {
		/* Go the the "game won" state. */
		state->state = STATE_GAMEWON;
		/* Update the high score and save it if appropriate: */
		save_hiscore(state);
	} else { /* Not on the last level. */
		/* If we're on the title screen, go to the running state. */
		if(state->state == STATE_TITLESCREEN) {
			state->state = STATE_RUNNING;
		/* Otherwise give the player some more balls for completing a
		 * level: */
		} else state->numballs += state->newlevelballs;
		state->level++; /* Increment the level counter. */
	}
	/* We always start a new level with the ball parked: */
	park_ball(state);
	/* Draw the new level and set up various other things for it: */
	set_level_active(state);
}
Example #3
0
/* Called whenever a ball is lost, either by it falling off the bottom of the
 * screen, or the player pressing the "suicide key". */
void lost_ball(nbstate *state)
{
	/* Decrement the balls count and if there are none left: */
	if(!state->numballs--) {
		/* Go to the "game lost" state and update the high score if
		 * appropriate: */
		state->state = STATE_GAMELOST;
		/* Update the high score and save it if necessary: */
		save_hiscore(state);
		/* This could probably be done better by just drawing the
		 * splash- set_level_active() redraws the entire game area: */
		set_level_active(state);
		return;
	}

	/* Erase the balls line at the top of the screen: */
	draw_background(state, 0, state->scores.h, state->canvaswidth,
			state->ball.s->h + (BALLS_BORDER * 2));
	/* Draw the balls again, but with one less than there was before: */
	draw_balls(state);
	/* Copy the balls row to the output window: */
	draw_canvas(state, 0, state->scores.h, state->canvaswidth,
				state->ball.s->h + (BALLS_BORDER * 2));
	/* Park the new ball and erase the old one: */
	park_ball(state);
	move_ball(state);
	/* Redraw the bat. This is a bit of a hack because sometimes when the
	 * ball falls below the top of the bat on its way to the bottom of the
	 * screen it can clip the bat and erase a bit of it. This redraws the
	 * bat to hide that: */
	draw_bat(state);
	/* Copy the redrawn bat to the output window: */
	draw_canvas(state, state->batx - (state->batwidths[state->bat] / 2),
			state->canvasheight - state->batheight,
			state->batwidths[state->bat], state->batheight);
}
Example #4
0
/* Check if the ball will collide with something if it is moved to the
 * specified coordinates. If so, the direction is changed and 1 is returned
 * to indicate that the caller should recalculate the new coordinates and
 * try again. If there was no collision it returns 0. If something exceptional
 * happens (eg. the last brick is destroyed or the last ball is lost) it
 * returns 2 to indicate that the caller should give up trying to move the
 * ball. */
int check_ball_collision(nbstate *state, coords *c)
{
	int i, bc;
	grid *g = state->grid;

	/* Check for a collision with the top of the game area: */
	if(c->y < state->ball.s->h + (2 * BALLS_BORDER) + state->scores.h) {
		/* Bounce the ball back down and ask the caller to try again: */
		state->ball.d = normalise_angle(M_PI - state->ball.d);
		return 1;
	}

	/* Check for a collision with the bottom of the game area: */
	if(c->y > state->canvasheight - state->ball.s->h) {
		/* If the solidfloor cheat is active, bounce the ball back up
		 * and ask the caller to try again: */
		if(state->flags.sf) {
			state->ball.d = normalise_angle(M_PI - state->ball.d);
			return 1;
		} else {
			/* Otherwise destroy the ball, move the new ball to
			 * the parked position (park_ball() is called by
			 * lost_ball()) and ask the caller to give up trying
			 * to move the ball: */
			lost_ball(state);
			move_ball(state);
			return 2;
		}
	}

	/* Check for a collision with the left hand side of the game area: */
	if(c->x < 0) {
		/* Bounce the ball back and ask the caller to try again: */
		state->ball.d = normalise_angle((2 * M_PI) - state->ball.d);
		return 1;
	}

	/* Check for a collision with the right hand side of the game area: */
	if(c->x > state->canvaswidth - state->ball.s->w) {
		/* Bounce the ball back and ask the caller to try again: */
		state->ball.d = normalise_angle((2 * M_PI) - state->ball.d);
		return 1;
	}

	/* Check for a collision with the bat: */
	if(c->y > state->canvasheight - state->batheight - state->ball.s->h &&
			c->x > state->batx -
			(state->batwidths[state->bat] / 2) - state->ball.s->w &&
			c->x < state->batx +
			(state->batwidths[state->bat] / 2)) {

		/* If the collision happened with the side of the bat instead
		 * of the top, we don't care so just tell the caller there
		 * was no collision: */
		if(state->ball.y > state->canvasheight - state->batheight -
				state->ball.s->h)
			return 0;

		/* If the StickyBat power-up is active, park the ball: */
		if(state->powertimes.stickybat) {
			park_ball(state);
			move_ball(state);
			return 2;
		} else {
			/* Otherwise bounce it back up and ask the caller to
			 * try again: */
			state->ball.d = normalise_angle(((c->x +
						(state->ball.s->w / 2)
						- state->batx) /
						state->batwidths[state->bat] /
						2) * M_PI);
			return 1;
		}
	}

	/* Check for collisions with the bricks: */
	bc = 0; /* No collisions have happened yet. */
	/* For each brick in the grid: */
	for(i = 0; i < state->width * state->height; i++) {
		/* If there is a brick in this grid position and the ball
		 * intersects it:  */
		if(g->b && c->y + state->ball.s->h > g->y && c->y < g->y +
				state->brickheight && c->x + state->ball.s->w
				> g->x && c->x < g->x + state->brickwidth) {

			/* Perform the brick collision actions, and if
			 * something exceptional happens (ie. we destroy the
			 * last brick), return straight away asking the caller
			 * to give up trying to move the ball: */
			if(brick_collision(state, g)) return 2;

			/* Unless the NoBounce cheat is active, bounce the
			 * ball off the brick. Only do this on the first brick
			 * collision we find. */
			if(!state->flags.nb && !bc) {
				bc = 1;
				/* Bounce off the left face: */
				if(state->ball.x + state->ball.s->w < g->x) {
					state->ball.d = normalise_angle((2 *
							M_PI) - state->ball.d);
				/* Bounce off the right face: */
				} else if(state->ball.x >= g->x +
						state->brickwidth) {
					state->ball.d = normalise_angle((2 *
							M_PI) - state->ball.d);
				/* Bounce off the upper face: */
				} else if(state->ball.y + state->ball.s->h
								< g->y) {
					state->ball.d = normalise_angle(M_PI -
								state->ball.d);
				/* Bounce off the lower face: */
				} else if(state->ball.y >= g->y +
							state->brickheight) {
					state->ball.d = normalise_angle(M_PI -
								state->ball.d);
				} else {
					/* This shouldn't happen, but I don't
					 * trust the above algorithm 100%. */
					debug_printf ("Internal error: "
						"couldn't figure out brick "
						"collision face\n");
				}
			}
		}
		g++; /* Increment to the next grid position. */
	}

	/* If a brick collision occured, ask the caller to try again: */
	if(bc) return 1;
	return 0; /* Otherwise tell the caller that no collision occured. */
}