示例#1
0
/* Increment the score by the specifed amount and redraw the score bar: */
void increment_score(nbstate *state, int points)
{
    state->scores.s += points; /* Increment the current score. */
    draw_scores(state); /* Redraw the score bar onto the canvas. */
    /* Copy the bar onto the output window. */
    draw_canvas(state, 0, 0, state->canvaswidth, state->scores.h);
}
示例#2
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);
}
示例#3
0
/* Redraw the ball in its current position, including copying it to the output
 * window. */
void redraw_ball(nbstate *state)
{
	/* Erase the ball area to the background: */
	draw_background(state, (int)state->ball.x, (int)state->ball.y,
			state->ball.s->w, state->ball.s->h);

	/* Redraw any powers we may have erased: */
	redraw_powers(state, (int)state->ball.x, (int)state->ball.y,
			state->ball.s->w, state->ball.s->h);

	/* Redraw the ball: */
	draw_ball(state);

	/* Copy it to the output window: */
	draw_canvas(state, state->ball.lx, state->ball.ly, state->ball.s->w,
			state->ball.s->h);
}
示例#4
0
/* Destroy the specified brick, clear the relevant piece of the game area,
 * increment the score by the appropriate amount, decrement the current
 * number of bricks, and if we run out of bricks, increment to the next level.
 * Returns 0 if the level wasn't incremented and 1 if it was. */
static int destroy_brick(nbstate *state, grid *g)
{
	int immutable;

	/* Wipe the canvas where the brick is back to the background: */
	draw_background(state, g->x, g->y, state->brickwidth,
			state->brickheight);
	/* Redraw any powers we may just have accidentally erased: */
	redraw_powers(state, g->x, g->y, state->brickwidth, state->brickheight);
	/* Copy the affected area to the output window: */
	draw_canvas(state, g->x, g->y, state->brickwidth,
			state->brickheight);

	/* Increment the score by the appropriate amount: */
	if(g->b->flags & BRICK_FLAG_HUGE_BONUS)
		increment_score(state, state->hugebonuspoints);
	else if(g->b->flags & BRICK_FLAG_LARGE_BONUS)
		increment_score(state, state->largebonuspoints);
	else if(g->b->flags & BRICK_FLAG_MEDIUM_BONUS)
		increment_score(state, state->mediumbonuspoints);
	else if(g->b->flags & BRICK_FLAG_SMALL_BONUS)
		increment_score(state, state->smallbonuspoints);
	else increment_score(state, state->normalpoints);

	/* Remember whether this was an immutable brick (which does not factor
	 * when counting the number of bricks remaining because it cannot
	 * normally be destroyed- only the PowerBall power-up and the
	 * NoBounce cheat can cause immutable bricks to be destroyed. */
	immutable = g->b->flags & BRICK_FLAG_IMMUTABLE;
	g->b = 0; /* Wipe the brick from this grid location. */

	/* If this wasn't an immutable brick, decrement the current count of
	 * bricks and if we run out, increment to the next level and return. */
	if(!immutable && !--state->numbricks) {
		increment_level(state);
		return 1; /* Level was incremented. */
	}

	/* Create the power-up or power-down (if any): */
	if(g->power != NOPOWER)
		new_power(state, g->power, g->x + (state->brickwidth / 2),
									g->y);

	return 0; /* Level wasn't incremented. */
}
示例#5
0
/* Move the ball to from the position specified by state->ball.lx and
 * state->ball.ly to state->ball.x and state->ball.y, and update the relevant
 * area of the output window. */
void move_ball(nbstate *state)
{
	int x, w, y, h;

	/* Check that the ball really has moved: */
	if(state->ball.lx == state->ball.x && state->ball.ly == state->ball.y)
		return;

	/* Calculate the position and dimensions of the rectangle which
	 * encloses both the old ball and the new ball so we know what area
	 * to copy to the output window later. FIXME: this is quite inefficient
	 * when doing stuff like parking the ball, where the old ball position
	 * could be at the other side of the screen to the place we want to
	 * move it to. It is however OK in the common case of only moving the
	 * ball a few pixels at a time. */
	if(state->ball.x < state->ball.lx) {
		x = (int)state->ball.x;
		w = state->ball.lx - x + state->ball.s->w;
	} else {
		x = state->ball.lx;
		w = (int)state->ball.x - x + state->ball.s->w;
	}
	if(state->ball.y < state->ball.ly) {
		y = (int)state->ball.y;
		h = state->ball.ly - y + state->ball.s->h;
	} else {
		y = state->ball.ly;
		h = (int)state->ball.y - y + state->ball.s->h;
	}

	/* Draw the background where the old ball image is to erase it: */
	draw_background(state, state->ball.lx, state->ball.ly,
			state->ball.s->w, state->ball.s->h);

	/* Redraw any powers that are under that position: */
	redraw_powers(state, state->ball.lx, state->ball.ly, state->ball.s->w,
							state->ball.s->h);

	/* Draw the ball in the new position and update ball.lx and ball.ly: */
	draw_ball(state);

	/* Draw the modified area of the canvas to the output window: */
	draw_canvas(state, x, y, w, h);
}
示例#6
0
/* Called from destroy_brick() to create a new power box of the specified type
 * centred on the specified location. */
void new_power(nbstate *state, int type, int x, int y)
{
    power *p, *pn;
    sprite *s = state->powersprites[type];

    /* Allocate the new power structure: */
    if(!(p = malloc(sizeof(power)))) {
        oom();
        return;
    }

    /* Fill in the parameters: */
    p->type = type;
    p->y = y;

    /* Calculate the left hand coordinate of the power box (the passed in
     * X coordinate specifies the centre, not the actual position): */
    p->x = x - (s->w / 2);

    /* Shift the box left or right if needed to make sure it is fully on
     * the screen: */
    if(p->x < 0) p->x = 0;
    if(p->x + s->w > state->canvaswidth)
        p->x = state->canvaswidth - s->w;

    p->next = NULL; /* This is the last in the list so there is no next. */

    /* If the list is empty, make this power the head of the list: */
    if(!state->powers) state->powers = p;
    else {
        /* Otherwise loop through until the end of the list: */
        for(pn = state->powers; pn->next; pn = pn->next);
        pn->next = p; /* Tag this power onto the end of the list. */
    }

    /* Draw the new power: */
    draw_power(state, p, p->x, p->y, s->w, s->h);

    /* Draw the modified area of the canvas to the output window: */
    draw_canvas(state, p->x, p->y, s->w, s->h);
}
示例#7
0
/* Called whenever the ball collides with a brick, and handles such things as
 * destroying the brick if appropriate, redrawing it with more transparency
 * if appropriate, incrementing to the next level when we destroy the last brick
 *  (actually, that's done by destroy_brick() which is called from
 * brick_collision()), etc. Returns 0 if the level was not incremented and 1
 * if it was. */
int brick_collision(nbstate *state, grid *g)
{
	/* If either the PowerBall power-up or the NoBounce cheat is active,
	 * simply destroy the brick in one hit even if it is immutable. */
	if(state->powertimes.powerball || state->flags.nb)
		return destroy_brick(state, g);

	/* If this is an immutable brick and PowerBall and NoBounce are not
	 * active, it can't be destroyed so return normally: */
	if(g->b->flags & BRICK_FLAG_IMMUTABLE) return 0;

	/* Increment the number of hits on this brick. This is used to keep
	 * track of when to destroy a "2 hits or "3 hits" brick, and also how
	 * transparent to draw those bricks as: */
	g->hits++;

	/* If this is a 2-hit brick and it has only been hit once or it is a
	 * 3 hit brick that has been hit either once or twice: */
	if(((g->b->flags & BRICK_FLAG_2_HITS) && g->hits < 2) ||
			((g->b->flags & BRICK_FLAG_3_HITS) && g->hits < 3)) {
		/* Clear the area where the brick is: */
		draw_background(state, g->x, g->y, state->brickwidth,
				state->brickheight);
		/* Redraw the brick (with additional transparency to indicate
		 * that it has been hit): */
		draw_brick(state, g, 0, 0, state->brickwidth,
				state->brickheight);
		/* Redraw any powers we may just have accidentally erased: */
		redraw_powers(state, g->x, g->y, state->brickwidth,
				state->brickheight);
		/* Copy the changed area to the output window: */
		draw_canvas(state, g->x, g->y, state->brickwidth,
				state->brickheight);
	/* Otherwise, destroy the brick: */
	} else return destroy_brick(state, g);

	return 0; /* Level was not incremented. */
}
示例#8
0
/* Because the canvas is a full double buffer, all we need to do when we get
 * an exposure event is to copy the specified area of the canvas to the
 * output window. */
void handle_exposure_event(nbstate *state, GR_EVENT_EXPOSURE *ev)
{
	draw_canvas(state, ev->x, ev->y, ev->width, ev->height);
}
示例#9
0
/* Called every animation frame to move all of the power boxes down and perform
 * collision detection, etc. Doesn't draw the results to the canvas unless the
 * power collides with something and is destroyed. */
void animate_powers(nbstate *state)
{
    sprite *s;
    power *p, *pnext;

    /* For each power in the list: */
    for(p = state->powers; p; p = pnext) {
        /* Remember the next item first, because we may destroy this
         * power, and then it would be wrong to use the pointer to
         * find the pointer to the next one in the list: */
        pnext = p->next;

        /* Find the sprite for this power type: */
        s = state->powersprites[p->type];

        /* Clear the old power away: */
        draw_background(state, p->x, p->y, s->w, s->h);

        /* Redraw any bricks we may have accidentally erased: */
        draw_bricks(state, p->x, p->y, s->w, s->h);

        /* Move it down: */
        p->y += state->powerv;

        /* If it has reached the bottom of the screen: */
        if(p->y + s->h > state->canvasheight) {

            /* Copy the erased region to the screen: */
            draw_canvas(state, p->x, p->y - state->powerv,
                        s->w, s->h);

            /* Destroy the power structure: */
            destroy_power(state, p);

            /* Check if it will collide with the bat: */
        } else if(p->y + s->h > state->canvasheight - state->batheight
                  && p->x + s->w > state->batx -
                  (state->batwidths[state->bat] / 2) &&
                  p->x < state->batx +
                  (state->batwidths[state->bat] / 2)) {

            /* Copy the erased region to the screen: */
            draw_canvas(state, p->x, p->y - state->powerv,
                        s->w, s->h);

            /* Activate the relevant power: */
            power_collision(state, p);

            /* Destroy the power structure: */
            destroy_power(state, p);

        } else {
            /* Redraw it in its new location: */
            draw_power(state, p, p->x, p->y, s->w, s->h);
        }
    }

    /* Redraw the ball in case we accidentally drew over it: */
    redraw_ball(state);

    /* Copy all the modified areas to the output window: */
    for(p = state->powers; p; p = p->next)
        draw_canvas(state, p->x, p->y - state->powerv, s->w,
                    s->h + state->powerv);

}
示例#10
0
/* Make the current level active. This mainly consists of destroying the old
 * background image and loading the new one, destroying the splash image,
 * possibly loading a new splash image depending on the state, copying over the
 * new level data into the current level state, then redrawing the entire game
 * area. */
void set_level_active(nbstate *state)
{
	int i;
	level *lev;
	grid *g, *gg;
	int bgchanged;
	sprite *s = NULL;
	power *p, *pnext;
	GR_PIXMAP_ID ctmp;
	char *backgroundfile;

	/* Destroy the old splash image sprite: */
	destroy_sprite(state, state->splash);

	/* If we're on the title screen: */
	if(state->state == STATE_TITLESCREEN) {
		/* Set the background file to the title background file: */
		backgroundfile = state->titlebackground;
		/* Set the tiled state appropriately: */
		state->backgroundtiled = state->titlebackgroundtiled;
		/* Try to load the title screen splash graphic (if it doesn't
		 * work nothing bad will happen- load_sprite() will print an
		 * error message and draw_splash() will not draw anything.) */
		state->splash = load_sprite(state, state->titlesplash, -1, -1);
	} else { /* Not on the title screen. */
		/* Find the level structure for the current level number: */
		for(lev = state->levels, i = 1; i < state->level;
				lev = lev->next, i++);
		/* Set the current number of bricks and background info: */
		state->numbricks = lev->numbricks;
		backgroundfile = lev->backgroundname;
		state->backgroundtiled = lev->backgroundtiled;
		/* If we're in the "game won" state, try to load the appropriate
		 * splash image: */
		if(state->state == STATE_GAMEWON) {
			state->splash = load_sprite(state, state->gamewonsplash,
									-1, -1);
		/* If we're in the "game lost" state, try to load the
		 * appropriate splash image: */
		} else if(state->state == STATE_GAMELOST) {
			state->splash = load_sprite(state,
						state->gamelostsplash, -1, -1);
		} else { /* We must be in the STATE_RUNNING state. */
			/* No splash image: */
			state->splash = NULL;
			/* Copy this levels game grid into the current game
			 * grid: */
			g = state->grid;
			gg = lev->grid;
			for(i = 0; i < state->width * state->height; i++)
				*g++ = *gg++;
		}
	}

	/* If there was a background filename specified: */
	if(backgroundfile) {
		/* If there is a current background sprite with a filename
		 * and the filename is the same as the new background
		 * filename, the background file has not changed. Otherwise,
		 * assume that it has. */
		if(state->background && state->background->fname &&
				!strcmp(backgroundfile,
					state->background->fname))
			bgchanged = 0;
		else bgchanged = 1;
	/* No background filename was specified, so assume it has changed (to
	 * a blank black frame): */
	} else bgchanged = 1;

	/* If the background image has changed, try to load the new one: */
	if(bgchanged && !(s = load_sprite(state, backgroundfile, -1, -1))) {
		/* If it fails, try to make a new empty sprite and colour it
		 * in black (the 16*16 pixels is purely arbitrary- make it too
		 * large and it uses a lot of memory, make it too small and
		 * we spend ages painting hundreds of tiny tiles onto the
		 * background. */
		if(!(s = make_empty_sprite(state, backgroundfile, 16, 16))) {
			/* If that fails too (shouldn't happen under normal
			 * circumstances), issue a warning and keep the old
			 * background image sprite: */
			s = state->background;
		} else {
			/* Fill in the new dummy background black: */
			GrSetGCForeground(state->gc, GR_COLOR_BLACK);
			GrFillRect(s->p, state->gc, 0, 0, 16, 16);
			/* Make it tiled. FIXME: it would make more sense to
			 * have a "no background image" option which simply
			 * coloured in the background black: */
			state->backgroundtiled = 1;
		}
	}

	/* If we have made a new background image sprite: */
	if(bgchanged && s != state->background) {
		/* Destroy the old one: */
		destroy_sprite(state, state->background);
		/* Set the background to the new sprite: */
		state->background = s;
	}

	/* Empty the list of power boxes: */
	for(p = state->powers; p; p = pnext) {
		pnext = p->next;
		free(p);
	}
	state->powers = NULL;

	/* If fading has been requested, we want to fade the new level in, so
	 * swap the canvasses around so the current screen is the old canvas,
	 * then draw the new screen and make the new canvas, then start the
	 * process of fading it in: */
	if(state->faderate) {
		/* Swap the canvasses around: */
		ctmp = state->oldcanvas;
		state->oldcanvas = state->canvas;
		state->canvas = ctmp;

		/* Remember the state we're fading into: */
		state->nextstate = state->state;

		/* Go into the fading state: */
		state->state= STATE_FADING;

		/* Initialise the fade level as completely opaque: */
		state->fadelevel = 256;
	}

	/* Clear the whole game area to the background image: */
	draw_background(state, 0, 0, state->canvaswidth, state->canvasheight);

	/* If we're not on the title screen or fading to the title screen: */
	if(state->state != STATE_TITLESCREEN && !(state->state == STATE_FADING
				&& state->nextstate == STATE_TITLESCREEN)) {
		/* Draw the bricks: */
		draw_bricks(state, 0, 0, state->canvaswidth,
						state->canvasheight);
		draw_scores(state); /* Draw the scores bar. */
		draw_balls(state); /* Draw the row of balls. */
		draw_bat(state); /* Draw the bat. */
		/* Draw the current ball unless the game is over: */
		if(state->state != STATE_GAMELOST &&
				state->state != STATE_GAMEWON)
			draw_ball(state);
	}

	draw_splash(state); /* Draw the splash graphic (if there is one). */

	/* If we're fading, remember the new canvas and generate the first
	 * frame of the fade: */
	if(state->state == STATE_FADING) {

		/* Swap the canvasses around: */
		ctmp = state->newcanvas;
		state->newcanvas = state->canvas;
		state->canvas = ctmp;

		/* Reduce the opacity: */
		state->fadelevel -= state->faderate;

		/* Generate the first frame: */
		GrCopyArea(state->canvas, state->gc, 0, 0, state->canvaswidth,
				state->canvasheight, state->newcanvas, 0, 0, 0);
		GrCopyArea(state->canvas, state->gc, 0, 0, state->canvaswidth,
				state->canvasheight, state->oldcanvas, 0, 0,
				GR_CONST_BLEND | state->fadelevel);
	}

	/* Copy the entire redrawn canvas to the output window: */
	draw_canvas(state, 0, 0, state->canvaswidth, state->canvasheight);
}