From: Rusty Russell Date: Thu, 24 Jan 2008 12:54:19 +0000 (+1100) Subject: Proper redraw and scoring. X-Git-Url: https://git.ozlabs.org/?p=ponghero.git;a=commitdiff_plain;h=aad127ce5b932106bc2ddde0309688a0613f1587 Proper redraw and scoring. --- diff --git a/Makefile b/Makefile index 11d33b3..f3e0240 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ #! /usr/bin/make CFLAGS:=-Wall -Wmissing-prototypes -Wmissing-declarations -g -I../ccan #-g #-O2 -g -LDFLAGS:=-lSDL -lcwiid -prpong: prpong.c stdrusty.h +LDFLAGS:=-lSDL -lSDL_image -lcwiid +prpong: prpong.c stdrusty.h ../ccan/talloc/talloc.o wiimote: wiimote.c stdrusty.h diff --git a/images/0.png b/images/0.png new file mode 100644 index 0000000..b29a2fe Binary files /dev/null and b/images/0.png differ diff --git a/images/1.png b/images/1.png new file mode 100644 index 0000000..7ce4af0 Binary files /dev/null and b/images/1.png differ diff --git a/images/2.png b/images/2.png new file mode 100644 index 0000000..7b79963 Binary files /dev/null and b/images/2.png differ diff --git a/images/3.png b/images/3.png new file mode 100644 index 0000000..9e5ca84 Binary files /dev/null and b/images/3.png differ diff --git a/images/4.png b/images/4.png new file mode 100644 index 0000000..292790a Binary files /dev/null and b/images/4.png differ diff --git a/images/5.png b/images/5.png new file mode 100644 index 0000000..5c7f217 Binary files /dev/null and b/images/5.png differ diff --git a/images/6.png b/images/6.png new file mode 100644 index 0000000..a965c68 Binary files /dev/null and b/images/6.png differ diff --git a/images/7.png b/images/7.png new file mode 100644 index 0000000..1b5674a Binary files /dev/null and b/images/7.png differ diff --git a/images/left-won.png b/images/left-won.png new file mode 100644 index 0000000..d60dcad Binary files /dev/null and b/images/left-won.png differ diff --git a/images/right-won.png b/images/right-won.png new file mode 100644 index 0000000..49dbf26 Binary files /dev/null and b/images/right-won.png differ diff --git a/prpong.c b/prpong.c index 7799f63..ddf4afb 100644 --- a/prpong.c +++ b/prpong.c @@ -9,14 +9,24 @@ #include #include "stdrusty.h" #include "list/list.h" +#include "talloc/talloc.h" +#include "container_of/container_of.h" #include +#include #include #include #include +#define EXPIRY_SECS 3 + #define MAX_X CWIID_IR_X_MAX #define MAX_Y CWIID_IR_Y_MAX +#define LEFT_PLAYER 1 +#define RIGHT_PLAYER 2 + +static LIST_HEAD(objects); + struct coord { double x; @@ -475,18 +485,48 @@ static void line(SDL_Surface *s, } } +/* Bounding box to be updated. */ +static bool needs_update; +static struct coord start_update, end_update; + +struct object +{ + /* List of all objects. */ + struct list_node list; + /* Bounding box. */ + struct coord start, end; + + void (*redraw)(SDL_Surface *s, struct object *me); +}; + struct ball { + struct object object; + struct coord pos; struct coord move; + SDL_Surface *image; }; struct line_segment { + struct object object; + struct coord start, end; struct list_node list; bool ignore; struct timeval expires; + /* Does someone score if they hit this line ? */ + struct score *score; +}; + +struct score +{ + struct object object; + + SDL_Surface *image; + unsigned int value; + SDL_Surface *won; }; static void thick_line(SDL_Surface *s, const struct line_segment *l, @@ -599,10 +639,10 @@ static void bounce(struct ball *ball, const struct line_segment *line, } static struct line_segment border[] = { - { { 0, 0, }, { MAX_X-1, 0 } }, - { { MAX_X-1, 0, }, { MAX_X-1, MAX_Y-1 } }, - { { MAX_X-1, MAX_Y-1, }, { 0, MAX_Y-1 } }, - { { 0, MAX_Y-1, }, { 0, 0 } }, + { .start = { 0, 0, }, .end = { MAX_X-1, 0 } }, + { .start = { MAX_X-1, 0, }, .end = { MAX_X-1, MAX_Y-1 } }, + { .start = { MAX_X-1, MAX_Y-1, }, .end = { 0, MAX_Y-1 } }, + { .start = { 0, MAX_Y-1, }, .end = { 0, 0 } }, }; static inline float deg_to_rad(float degrees) @@ -749,31 +789,218 @@ static bool map_coord(unsigned int x, unsigned int y, return true; } -static struct line_segment *new_line(unsigned int startx, unsigned int starty, - unsigned int endx, unsigned int endy, - const struct coord calib[]) +static void add_update(const struct object *obj) { - struct line_segment *l = malloc(sizeof(*l)); + if (!needs_update) { + start_update = obj->start; + end_update = obj->end; + needs_update = true; + return; + } + if (obj->start.x < start_update.x) + start_update.x = obj->start.x; + if (obj->start.y < start_update.y) + start_update.y = obj->start.y; + if (obj->end.x > end_update.x) + end_update.x = obj->end.x; + if (obj->end.y > end_update.y) + end_update.y = obj->end.y; +} + +static void destroy_object(struct object *object) +{ + list_del(&object->list); + add_update(object); +} + +static void add_object(struct list_head *list, + struct object *obj, + unsigned int startx, unsigned int starty, + unsigned int width, unsigned int height, + void (*redraw)(SDL_Surface *s, struct object *me)) +{ + list_add_tail(list, &obj->list); + obj->start.x = startx; + obj->start.y = starty; + obj->end.x = startx + width; + obj->end.y = starty + height; + obj->redraw = redraw; + add_update(obj); +} + +static void redraw_line(SDL_Surface *s, struct object *me) +{ + struct line_segment *l = container_of(me, struct line_segment, object); + + thick_line(s, l, 0); +} + +static int destroy_line(struct line_segment *l) +{ + list_del(&l->list); + destroy_object(&l->object); + return 0; +} + +static void line_object_setup(struct list_head *lines, + struct list_head *objects, + struct line_segment *l) +{ + unsigned int tmp, startx, endx, starty, endy; + + list_add_tail(lines, &l->list); + + startx = l->start.x; + starty = l->start.y; + endx = l->end.x; + endy = l->end.y; + + /* Find bounding box */ + if (startx > endx) { + tmp = endx; + endx = startx; + startx = tmp; + } + if (starty > endy) { + tmp = endy; + endy = starty; + starty = tmp; + } + /* Padding for thick lines (beware underflow) */ + if (startx > 0) + startx--; + if (starty > 0) + starty--; + endx++; + endy++; + + add_object(objects, &l->object, startx, starty, + endx - startx, endy - starty, redraw_line); +} + +static void add_line(struct list_head *lines, + unsigned int startx, unsigned int starty, + unsigned int endx, unsigned int endy, + const struct coord calib[]) +{ + struct line_segment *l = talloc(NULL, struct line_segment); + struct timeval now; - l->ignore = false; if (!map_coord(startx, starty, calib, &l->start) || !map_coord(endx, endy, calib, &l->end)) { - free(l); - return NULL; + talloc_free(l); + return; } - return l; + + l->ignore = false; + l->score = NULL; + gettimeofday(&now, NULL); + l->expires = now; + l->expires.tv_sec += EXPIRY_SECS; + + line_object_setup(lines, &objects, l); + talloc_set_destructor(l, destroy_line); +} + +static void redraw_ball(SDL_Surface *s, struct object *me) +{ + struct ball *b = container_of(me, struct ball, object); + SDL_Rect rect = { .x = b->pos.x - b->image->w/2, + .y = b->pos.y - b->image->h/2 }; + + SDL_BlitSurface(b->image, NULL, s, &rect); +} + +static void redraw_score(SDL_Surface *s, struct object *me) +{ + struct score *score = container_of(me, struct score, object); + SDL_Rect rect = { .x = score->object.start.x, + .y = score->object.start.y }; + + SDL_BlitSurface(score->image, NULL, s, &rect); +} + +static SDL_Surface *score(struct score *s) +{ + char filename[40]; + + s->value++; + SDL_FreeSurface(s->image); + sprintf(filename, "images/%u.png", s->value); + s->image = IMG_Load(filename); + /* No more images? You won! */ + if (!s->image) + return s->won; + + add_update(&s->object); + return NULL; +} + +/* Due to rounding, we exaggerate bounding box by 1 here. */ +static void do_updates(struct list_head *objects, SDL_Surface *screen) +{ + struct object *i; + unsigned int y, startx, starty, endx, endy; + + /* Clip */ + if (start_update.x <= 0) + startx = 0; + else + startx = start_update.x - 1; + if (start_update.y <= 0) + starty = 0; + else + starty = start_update.y - 1; + + endx = end_update.x+1; + endy = end_update.y+1; + + if (endx > screen->w) + endx = screen->w; + if (endy > screen->h) + endy = screen->h; + + SDL_LockSurface(screen); + /* First we clear the area to white. */ + for (y = starty; y < endy; y++) { + memset(screen->pixels + y * screen->pitch + + screen->format->BytesPerPixel * startx, + 0xFF, + (endx - startx) * screen->format->BytesPerPixel); + } + SDL_UnlockSurface(screen); + + /* Now redraw everything which overlaps it */ + list_for_each(objects, i, list) { + if (i->end.x < startx) + continue; + if (i->end.y < starty) + continue; + if (i->start.x > endx) + continue; + if (i->start.y > endy) + continue; + i->redraw(screen, i); + } + + SDL_UpdateRect(screen, startx, starty, endx - startx, endy - starty); + + /* Reset bounding box */ + needs_update = false; } +static bool same_side(const struct ball *ball, unsigned int x) +{ + return (ball->pos.x >= MAX_X/2) == (x >= MAX_X/2); +} int main(int argc, char *argv[]) { struct ball ball; - SDL_Surface *screen, *ballsur, *savesur = NULL; + SDL_Surface *screen; Uint8 video_bpp; Uint32 videoflags; unsigned int i, time_since_last_ir = INT_MAX; - SDL_Rect rect; - struct timeval line_life = { 3, 0 }; cwiid_wiimote_t *wiimote; struct cwiid_state state; struct coord calib[4]; @@ -781,7 +1008,7 @@ int main(int argc, char *argv[]) bool mouse = false, needs_calibration = true, drawing = false; LIST_HEAD(lines); struct cwiid_ir_src *ir, last_ir = { .valid = 0 }; - struct line_segment *n; + struct score left, right; videoflags = SDL_SWSURFACE; @@ -817,17 +1044,10 @@ int main(int argc, char *argv[]) argv++; } - if (argc != 5) - errx(1, "Usage: %s [--fullscreen] [--calib=x,y,x,y,x,y,x,y] [--mouse] ballx bally ballangle ballspeed\n" - " Where ballangle is 0 for north, 90 for east, etc\n", + if (argc != 2) + errx(1, "Usage: %s [--fullscreen] [--calib=x,y,x,y,x,y,x,y] [--mouse] speed\n", argv[0]); - ball.pos.x = atol(argv[1]); - ball.pos.y = atol(argv[2]); - ball.move.x = sin(deg_to_rad(atof(argv[3]))) * atol(argv[4]); - ball.move.y = cos(deg_to_rad(atof(argv[3]))) * atol(argv[4]); - printf("ball move = %f,%f\n", ball.move.x, ball.move.y); - /* Initialize SDL */ if (SDL_Init(SDL_INIT_VIDEO) < 0) { fprintf(stderr, "Couldn't initialize SDL: %s\n",SDL_GetError()); @@ -846,13 +1066,13 @@ int main(int argc, char *argv[]) errx(1, "Can't set IR repeat mode"); } - if ( (screen=SDL_SetVideoMode(MAX_X,MAX_Y,video_bpp,videoflags)) == NULL ) { + if ((screen=SDL_SetVideoMode(MAX_X,MAX_Y,video_bpp,videoflags)) == NULL) { errx(1, "Couldn't set %dx%dx%d video mode: %s", MAX_X, MAX_Y,video_bpp, SDL_GetError()); } /* Set the surface pixels and refresh! */ - if ( SDL_LockSurface(screen) < 0 ) { + if (SDL_LockSurface(screen) < 0) { errx(1, "Couldn't lock the display surface: %s", SDL_GetError()); } @@ -870,16 +1090,44 @@ int main(int argc, char *argv[]) calibrate(screen, wiimote, &calib[3], 0, MAX_Y - 1); } - /* Draw borders, put them in list. */ + /* Create borders, put them in list. */ for (i = 0; i < ARRAY_SIZE(border); i++) { border[i].ignore = false; border[i].expires.tv_sec = LONG_MAX; border[i].expires.tv_usec = 0; - list_add(&lines, &border[i].list); - thick_line(screen, &border[i], 0); + line_object_setup(&lines, &objects, &border[i]); } - ballsur = ball_surface(screen); + /* Set up scores. */ + left.value = right.value = 0; + left.image = IMG_Load("images/0.png"); + left.won = IMG_Load("images/left-won.png"); + right.image = IMG_Load("images/0.png"); + right.won = IMG_Load("images/right-won.png"); + if (!left.image || !right.image || !left.won || !right.won) + err(1, "Opening score images"); + + add_object(&objects, &left.object, + MAX_X/6 - left.image->w/2, 10, + left.image->w, left.image->h, redraw_score); + add_object(&objects, &right.object, + MAX_X - MAX_X/6 - left.image->w/2, 10, + left.image->w, left.image->h, redraw_score); + + /* Hit right border = score to left, and vice versa. */ + border[1].score = &left; + border[3].score = &right; + + srandom(time(NULL)); + ball.pos.x = MAX_X/2; + ball.pos.y = MAX_Y/2; + ball.move.x = (random() % 2 ? -1 : 1) * atoi(argv[1]); + ball.move.y = 0; + ball.image = ball_surface(screen); + add_object(&objects, &ball.object, + ball.pos.x - ball.image->w/2, + ball.pos.y - ball.image->h/2, + ball.image->w, ball.image->h, redraw_ball); for (;;) { struct coord new, isect, move = ball.move; @@ -893,13 +1141,8 @@ int main(int argc, char *argv[]) /* Expire lines */ list_for_each_safe(&lines, l, next, list) { - if (timercmp(&l->expires, &now, <)) { - printf("Deleting line %p\n", l); - list_del(&l->list); - /* FIXME: Undraw properly. */ - thick_line(screen, l, 0xFFFFFFFF); - free(l); - } + if (timercmp(&l->expires, &now, <)) + talloc_free(l); } again: @@ -934,6 +1177,24 @@ int main(int argc, char *argv[]) /* Don't hit the same line twice. */ printf("Ignoring that line\n"); best_l->ignore = ignored = true; + if (best_l->score) { + SDL_Surface *won = score(best_l->score); + if (won) { + SDL_Rect rect; + + rect.x = MAX_X/2 - won->w/2; + rect.y = MAX_Y/2 - won->h/2; + rect.w = won->w; + rect.h = won->h; + SDL_BlitSurface(won, NULL, screen, + &rect); + SDL_UpdateRect(screen, + rect.x, rect.y, + rect.w, rect.h); + SDL_Delay(3000); + exit(0); + } + } goto again; } @@ -943,28 +1204,22 @@ int main(int argc, char *argv[]) move.x, move.y, new.x, new.y); } - /* Restore what was there before ball. */ - if (savesur) { - SDL_BlitSurface(savesur, NULL, screen, &rect); - SDL_UpdateRect(screen, rect.x, rect.y, rect.w, rect.h); - } else { - savesur = sub_surface(screen, ballsur->w, ballsur->h); - } + /* We also need to redraw under old ball. */ + add_update(&ball.object); - /* Save away image under new ball. */ - rect.w = ballsur->w; - rect.h = ballsur->h; - rect.x = new.x - ballsur->w/2; - rect.y = new.y - ballsur->h/2; - SDL_BlitSurface(screen, &rect, savesur, NULL); - - /* Draw new ball */ - SDL_BlitSurface(ballsur, NULL, screen, &rect); - SDL_UpdateRect(screen, rect.x, rect.y, rect.w, rect.h); - /* That SDL_BlitSurface can crop rect. */ - rect.x = new.x - ballsur->w/2; - rect.y = new.y - ballsur->h/2; + /* Move ball. */ ball.pos = new; + ball.object.start.x = ball.pos.x - ball.image->w/2; + ball.object.start.y = ball.pos.y - ball.image->h/2; + ball.object.end.x = ball.pos.x + ball.image->w/2; + ball.object.end.y = ball.pos.y + ball.image->h/2; + + /* Need to draw under new ball. */ + add_update(&ball.object); + + /* Clears, redraws and resets the updates */ + do_updates(&objects, screen); + SDL_Delay(25); while (SDL_PollEvent(&event)) { @@ -983,23 +1238,17 @@ int main(int argc, char *argv[]) drawing = false; break; case SDL_MOUSEMOTION: - if (!drawing) + if (!drawing || + !same_side(&ball, event.motion.x)) break; - n = new_line(event.motion.x, - event.motion.y, - event.motion.x - + event.motion.xrel, - event.motion.y - + event.motion.yrel, - calib); - - if (n) { - timeradd(&now, &line_life, - &n->expires); - list_add_tail(&lines, &n->list); - thick_line(screen, n, 0); - } + add_line(&lines, event.motion.x, + event.motion.y, + event.motion.x + + event.motion.xrel, + event.motion.y + + event.motion.yrel, + calib); } } } @@ -1015,23 +1264,22 @@ int main(int argc, char *argv[]) for (i = 0; i < CWIID_IR_SRC_COUNT; i++) { if (!state.ir_src[i].valid) continue; + /* Only look at dots on same side as ball */ + if (!same_side(&ball, state.ir_src[i].pos[0])) + continue; if (!ir->valid || state.ir_src[i].size > ir->size) ir = &state.ir_src[0]; } if (ir->valid) { /* Give it some slack for missing one or two... */ - if (time_since_last_ir <= 5) { - n = new_line(last_ir.pos[0], - last_ir.pos[1], - ir->pos[0], - ir->pos[1], - calib); - if (n) { - timeradd(&now, &line_life, &n->expires); - list_add_tail(&lines, &n->list); - thick_line(screen, n, 0); - } + if (time_since_last_ir <= 5 + && same_side(&ball, last_ir.pos[0])) { + add_line(&lines, last_ir.pos[0], + last_ir.pos[1], + ir->pos[0], + ir->pos[1], + calib); } time_since_last_ir = 0; last_ir = *ir;