Proper redraw and scoring.
authorRusty Russell <rusty@rustcorp.com.au>
Thu, 24 Jan 2008 12:54:19 +0000 (23:54 +1100)
committerRusty Russell <rusty@rustcorp.com.au>
Thu, 24 Jan 2008 12:54:19 +0000 (23:54 +1100)
12 files changed:
Makefile
images/0.png [new file with mode: 0644]
images/1.png [new file with mode: 0644]
images/2.png [new file with mode: 0644]
images/3.png [new file with mode: 0644]
images/4.png [new file with mode: 0644]
images/5.png [new file with mode: 0644]
images/6.png [new file with mode: 0644]
images/7.png [new file with mode: 0644]
images/left-won.png [new file with mode: 0644]
images/right-won.png [new file with mode: 0644]
prpong.c

index 11d33b33b0d3e1c3ce81ff494a8003e9a4e04e05..f3e02405f80e6a71441566843fb595f1ffc1d966 100644 (file)
--- 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 (file)
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 (file)
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 (file)
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 (file)
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 (file)
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 (file)
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 (file)
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 (file)
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 (file)
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 (file)
index 0000000..49dbf26
Binary files /dev/null and b/images/right-won.png differ
index 7799f63fdccf90d068ab2f90d34f858163cfdc26..ddf4afbde1e61f97e44083d4b86d74f75fa2ae63 100644 (file)
--- a/prpong.c
+++ b/prpong.c
@@ -9,14 +9,24 @@
 #include <limits.h>
 #include "stdrusty.h"
 #include "list/list.h"
+#include "talloc/talloc.h"
+#include "container_of/container_of.h"
 #include <SDL/SDL.h>
+#include <SDL/SDL_image.h>
 #include <cwiid.h>
 #include <assert.h>
 #include <unistd.h>
 
+#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;