X-Git-Url: https://git.ozlabs.org/?a=blobdiff_plain;f=prpong.c;h=660fc8ae8a60402800f2664301aedb9a2df23cca;hb=4b884e9e3bff9f777f705d364627ce49ff4cedd6;hp=c62d0fcced8827bfc3881fe0e0ee2b03a17257ac;hpb=1774b5732984e261bf4fd68350051e7ff706c36c;p=ponghero.git diff --git a/prpong.c b/prpong.c index c62d0fc..660fc8a 100644 --- a/prpong.c +++ b/prpong.c @@ -4,100 +4,107 @@ #include #include #include +#include +#include +#include #include "stdrusty.h" +#include "list/list.h" +#include "talloc/talloc.h" +#include "container_of/container_of.h" #include +#include +#include +#include +#include -#define MAX_X 512 -#define MAX_Y 512 +#define EXPIRY_SECS 3 -/* Obtained from http://tog.acm.org/GraphicsGems/gemsiii/insectc.c */ -/* Faster Line Segment Intersection */ -/* Franklin Antonio */ +#define MAX_X CWIID_IR_X_MAX +#define MAX_Y CWIID_IR_Y_MAX -/* return values */ -#define DONT_INTERSECT 0 -#define DO_INTERSECT 1 -#define PARALLEL 2 +#define LEFT_PLAYER 1 +#define RIGHT_PLAYER 2 -/* The SAME_SIGNS macro assumes arithmetic where the exclusive-or */ -/* operation will work on sign bits. This works for twos-complement,*/ -/* and most other machine arithmetic. */ -#define SAME_SIGNS( a, b ) \ - (((long) ((unsigned long) a ^ (unsigned long) b)) >= 0 ) +static LIST_HEAD(objects); - -/* The use of some short working variables allows this code to run */ -/* faster on 16-bit computers, but is not essential. It should not */ -/* affect operation on 32-bit computers. The short working variables*/ -/* to not restrict the range of valid input values, as these were */ -/* constrained in any case, due to algorithm restrictions. */ -static int lines_intersect(long x1, long y1, long x2, long y2, long x3, long y3, - long x4,long y4, long *x, long *y) +struct coord { + double x; + double y; +}; - long Ax,Bx,Cx,Ay,By,Cy,d,e,f,num,offset; - short x1lo,x1hi,y1lo,y1hi; +/* Obtained from http://tog.acm.org/GraphicsGems/gemsiii/insectc.c */ +/* Faster Line Segment Intersection */ +/* Franklin Antonio */ +static bool lines_intersect(const struct coord *start1, + const struct coord *start2, + const struct coord *start3, + const struct coord *start4, + struct coord *intersect) +{ + double Ax,Bx,Cx,Ay,By,Cy,d,e,f,num; + struct coord lo, hi; - Ax = x2-x1; - Bx = x3-x4; + Ax = start2->x - start1->x; + Bx = start3->x - start4->x; - if(Ax<0) { /* X bound box test*/ - x1lo=(short)x2; x1hi=(short)x1; + if(Ax<0) { /* X bound box test*/ + lo.x = start2->x; hi.x = start1->x; } else { - x1hi=(short)x2; x1lo=(short)x1; + hi.x = start2->x; lo.x = start1->x; } if(Bx>0) { - if(x1hi < (short)x4 || (short)x3 < x1lo) return DONT_INTERSECT; + if(hi.x < start4->x || start3->x < lo.x) return false; } else { - if(x1hi < (short)x3 || (short)x4 < x1lo) return DONT_INTERSECT; + if(hi.x < start3->x || start4->x < lo.x) return false; } - Ay = y2-y1; - By = y3-y4; + Ay = start2->y - start1->y; + By = start3->y - start4->y; - if(Ay<0) { /* Y bound box test*/ - y1lo=(short)y2; y1hi=(short)y1; + if(Ay<0) { /* Y bound box test*/ + lo.y = start2->y; hi.y = start1->y; } else { - y1hi=(short)y2; y1lo=(short)y1; + hi.y = start2->y; lo.y = start1->y; } if(By>0) { - if(y1hi < (short)y4 || (short)y3 < y1lo) return DONT_INTERSECT; + if(hi.y < start4->y || start3->y < lo.y) return false; } else { - if(y1hi < (short)y3 || (short)y4 < y1lo) return DONT_INTERSECT; + if(hi.y < start3->y || start4->y < lo.y) return false; } - Cx = x1-x3; - Cy = y1-y3; - d = By*Cx - Bx*Cy; /* alpha numerator*/ - f = Ay*Bx - Ax*By; /* both denominator*/ - if(f>0) { /* alpha tests*/ - if(d<0 || d>f) return DONT_INTERSECT; + Cx = start1->x - start3->x; + Cy = start1->y - start3->y; + d = By*Cx - Bx*Cy; /* alpha numerator*/ + f = Ay*Bx - Ax*By; /* both denominator*/ + if (f>0) { /* alpha tests*/ + if (d < 0 || d > f) return false; } else { - if(d>0 || d 0 || d < f) return false; } - e = Ax*Cy - Ay*Cx; /* beta numerator*/ - if(f>0) { /* beta tests*/ - if(e<0 || e>f) return DONT_INTERSECT; + e = Ax*Cy - Ay*Cx; /* beta numerator*/ + if (f > 0) { /* beta tests*/ + if (e < 0 || e > f) return false; } else { - if(e>0 || e 0 || e < f) return false; } /*compute intersection coordinates*/ + /* Don't worry about tiny values of f (< 1/2 pixel across screen) */ + if (fabs(f) < 1.0 / (2.0 * MAX_X * MAX_Y)) + return false; - if(f==0) return PARALLEL; - num = d*Ax; /* numerator */ - offset = SAME_SIGNS(num,f) ? f/2 : -f/2; /* round direction*/ - *x = x1 + (num+offset) / f; /* intersection x */ + num = d*Ax; /* numerator */ + intersect->x = start1->x + num / f; /* intersection x */ num = d*Ay; - offset = SAME_SIGNS(num,f) ? f/2 : -f/2; - *y = y1 + (num+offset) / f; /* intersection y */ + intersect->y = start1->y + num / f; /* intersection y */ - return DO_INTERSECT; + return true; } - + // A set of very useful macros that you will find in most // code that I write whether I use them in a program or // not. @@ -478,28 +485,57 @@ static void line(SDL_Surface *s, } } -#if 1 -struct coord +/* Bounding box to be updated. */ +static bool needs_update; +static struct coord start_update, end_update; + +struct object { - long x; - long y; + /* 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; }; -static void thick_line(SDL_Surface *s, const struct line_segment *l) +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, + unsigned color) { struct line_segment adj; - int x, y; + int x, y, minx = MAX_X, miny = MAX_Y, maxx = 0, maxy = 0; + + SDL_LockSurface(s); /* Cheap hack */ for (x = -1; x < 2; x++) { @@ -509,24 +545,49 @@ static void thick_line(SDL_Surface *s, const struct line_segment *l) adj.start.y = l->start.y + y; adj.end.y = l->end.y + y; + if (adj.start.x < minx) + minx = adj.start.x; + if (adj.start.x > maxx) + maxx = adj.start.x; + if (adj.start.y < miny) + miny = adj.start.y; + if (adj.start.y > maxy) + maxy = adj.start.y; + if (adj.end.x < minx) + minx = adj.end.x; + if (adj.end.x > maxx) + maxx = adj.end.x; + if (adj.end.y < miny) + miny = adj.end.y; + if (adj.end.y > maxy) + maxy = adj.end.y; + line(s, adj.start.x, adj.start.y, adj.end.x, adj.end.y, - 0); + color); } } + + if (minx < 0) + minx = 0; + if (miny < 0) + miny = 0; + if (maxx >= MAX_X) + maxx = MAX_X-1; + if (maxy >= MAX_Y) + maxy = MAX_Y-1; + SDL_UpdateRect(s, minx, miny, maxx-minx, maxy-miny); + SDL_UnlockSurface(s); } static bool intersect(const struct coord *c1, const struct coord *c2, const struct line_segment *seg, struct coord *intersect) { - return lines_intersect(c1->x, c1->y, c2->x, c2->y, - seg->start.x, seg->start.y, - seg->end.x, seg->end.y, - &intersect->x, &intersect->y) == DO_INTERSECT; + return lines_intersect(c1, c2, &seg->start, &seg->end, intersect); } static double dist(const struct coord *c1, const struct coord *c2) { - long x = (c1->x - c2->x), y = (c1->y - c2->y); + double x = (c1->x - c2->x), y = (c1->y - c2->y); return sqrt(x * x + y * y); } @@ -536,23 +597,25 @@ static unsigned rad_to_deg(double rad) } static void bounce(struct ball *ball, const struct line_segment *line, - struct coord *remainder) + struct coord *move, double increase_speed) { double len, speed, lang, iang, oang; struct coord isect, new; - new.x = ball->pos.x + ball->move.x; - new.y = ball->pos.y + ball->move.y; + new.x = ball->pos.x + move->x; + new.y = ball->pos.y + move->y; /* How far were we supposed to move ball? */ - len = sqrt(remainder->x * remainder->x + remainder->y * remainder->y); + len = sqrt(move->x * move->x + move->y * move->y); /* How far is it to intersection? */ - intersect(&ball->pos, &new, line, &isect); + if (!intersect(&ball->pos, &new, line, &isect)) + errx(1, "No intersection any more?\n");; len -= dist(&ball->pos, &isect); /* Move ball to intersection. */ ball->pos = isect; + printf("ball is now at %f,%f\n", ball->pos.x, ball->pos.y); /* Outgoing angle = 2 * line angle - incident angle. */ lang = atan2(line->end.x - line->start.x, line->end.y - line->start.y); @@ -563,22 +626,23 @@ static void bounce(struct ball *ball, const struct line_segment *line, rad_to_deg(iang), rad_to_deg(oang)); - /* Set new direction for ball, at same speed */ + /* Set new direction for ball, at slightly increased speed */ speed = sqrt(ball->move.x * ball->move.x + ball->move.y * ball->move.y); - ball->move.x = round(sin(oang) * speed); - ball->move.y = round(cos(oang) * speed); - - /* Set remainder. */ - remainder->x = round(sin(oang) * len); - remainder->y = round(cos(oang) * len); - printf("len = %f, remainder = %li,%li\n", len, remainder->x, remainder->y); + speed += increase_speed; + ball->move.x = sin(oang) * speed; + ball->move.y = cos(oang) * speed; + + /* Set move. */ + move->x = sin(oang) * len; + move->y = cos(oang) * len; + printf("len = %f, move = %f,%f\n", len, move->x, move->y); } 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) @@ -617,81 +681,486 @@ static SDL_Surface *ball_surface(SDL_Surface *screen) return ball; } +static void clear_ignore(struct list_head *lines) +{ + struct line_segment *i; + printf("Unignoring...\n"); + list_for_each(lines, i, list) + i->ignore = false; +} + +static uint16_t *find_valid_point(struct cwiid_state *state) +{ + unsigned int i; + + for (i = 0; i < CWIID_IR_SRC_COUNT; i++) { + if (state->ir_src[i].valid) + return state->ir_src[i].pos; + } + return NULL; +} + +static void calibrate(SDL_Surface *screen, + const char *filename, + cwiid_wiimote_t *wiimote, + struct coord *calib, unsigned x, unsigned y) +{ + SDL_Surface *img = IMG_Load(filename); + uint16_t *pos; + struct cwiid_state state; + int last_x = MAX_X, last_y = MAX_Y, count = 0; + SDL_Rect rect = { .x = screen->w/2 - img->w/2, + .y = screen->h/2 - img->h/2 }; + + SDL_BlitSurface(img, NULL, screen, &rect); + SDL_UpdateRect(screen, 0, 0, 0, 0); + SDL_FreeSurface(img); + + SDL_Delay(500); + + /* Must see it for a full half second. */ + while (count < 20) { + SDL_Event event; + + if (cwiid_get_state(wiimote, &state)) + errx(1, "No wii state"); + + pos = find_valid_point(&state); + if (!pos) { + if (count) + count--; + } else { + /* Allow some jitter */ + if (abs(pos[0]-last_x) < 3 && abs(pos[1]-last_y) < 3) + count++; + last_x = pos[0]; + last_y = pos[1]; + } + + SDL_Delay(25); + if (SDL_PollEvent(&event) + && (event.type == SDL_QUIT + || (event.type == SDL_KEYDOWN + && event.key.keysym.sym == SDLK_ESCAPE))) { + SDL_Quit(); + exit(0); + } + } + + calib->x = last_x; + calib->y = last_y; + SDL_Delay(500); + printf("Calibration point: %u,%u\n", last_x, last_y); +} + +static bool map_coord(unsigned int x, unsigned int y, + const struct coord calib[], + struct coord *res) +{ + struct coord line_start, line_end; + struct coord pos = { x, y }, intersect; + double d1, d2; + + /* Calibration layout: + * + * 0 1 + * + * 3 2 + * + * We figure out the distance to each side, and then map using: + * + * x = d1 / (d1 + d2) * (rawend - rawstart) + rawstart + */ + line_start.x = 0; + line_end.x = MAX_X-1; + line_start.y = line_end.y = y; + + if (!lines_intersect(&calib[0], &calib[3], &line_start, &line_end, + &intersect)) + return false; + d1 = dist(&pos, &intersect); + if (!lines_intersect(&calib[1], &calib[2], &line_start, &line_end, + &intersect)) + return false; + d2 = dist(&pos, &intersect); + res->x = d1 / (d1 + d2) * MAX_X; + + line_start.y = 0; + line_end.y = MAX_Y-1; + line_start.x = line_end.x = x; + + if (!lines_intersect(&calib[0], &calib[1], &line_start, &line_end, + &intersect)) + return false; + d1 = dist(&pos, &intersect); + if (!lines_intersect(&calib[3], &calib[2], &line_start, &line_end, + &intersect)) + return false; + d2 = dist(&pos, &intersect); + res->y = d1 / (d1 + d2) * MAX_Y; + + return true; +} + +static void add_update(const struct object *obj) +{ + 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; + + if (!map_coord(startx, starty, calib, &l->start) + || !map_coord(endx, endy, calib, &l->end)) { + talloc_free(l); + return; + } + + 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; - struct line_segment lines[ARRAY_SIZE(border) + 1]; - static int isect_count = 0; - SDL_Surface *screen, *ballsur, *savesur = NULL; + SDL_Surface *screen; Uint8 video_bpp; Uint32 videoflags; - int i; - SDL_Event event; - struct line_segment *last_l = NULL; - SDL_Rect rect; - - if (argc != 9) - errx(1, "Usage: %s ballx bally ballangle ballspeed linestartx linestarty lineendx linendy\n" - " Where ballangle is 0 for north, 90 for east, etc\n", - argv[0]); + unsigned int i, time_since_last_ir = INT_MAX; + cwiid_wiimote_t *wiimote; + struct cwiid_state state; + struct coord calib[4]; + bdaddr_t addr = *BDADDR_ANY; + bool mouse = false, needs_calibration = true, drawing = false; + LIST_HEAD(lines); + struct cwiid_ir_src *ir, last_ir = { .valid = 0 }; + struct score left, right; - ball.pos.x = atol(argv[1]); - ball.pos.y = atol(argv[2]); - ball.move.x = roundf(sin(deg_to_rad(atof(argv[3]))) * atol(argv[4])); - ball.move.y = roundf(cos(deg_to_rad(atof(argv[3]))) * atol(argv[4])); - printf("ball move = %li,%li\n", ball.move.x, ball.move.y); + videoflags = SDL_SWSURFACE; - memcpy(lines, border, sizeof(border)); - lines[ARRAY_SIZE(border)].start.x = atof(argv[5]); - lines[ARRAY_SIZE(border)].start.y = atof(argv[6]); - lines[ARRAY_SIZE(border)].end.x = atof(argv[7]); - lines[ARRAY_SIZE(border)].end.y = atof(argv[8]); + if (argv[1] && streq(argv[1], "--fullscreen")) { + videoflags |= SDL_FULLSCREEN; + argc--; + argv++; + } + + if (argv[1] && strstarts(argv[1], "--calib=")) { + needs_calibration = false; + if (sscanf(argv[1], "--calib=%lf,%lf,%lf,%lf,%lf,%lf,%lf,%lf", + &calib[0].x, &calib[0].y, + &calib[1].x, &calib[1].y, + &calib[2].x, &calib[2].y, + &calib[3].x, &calib[3].y) != 8) + errx(1, "Not enough calibration points"); + argc--; + argv++; + } + + if (argv[1] && streq(argv[1], "--mouse")) { + mouse = true; + needs_calibration = false; + calib[0].x = calib[0].y = 0; + calib[1].x = MAX_X - 1; + calib[1].y = 0; + calib[2].x = MAX_X - 1; + calib[2].y = MAX_Y - 1; + calib[3].x = 0; + calib[3].y = MAX_Y - 1; + argc--; + argv++; + } + + if (argc != 2) + errx(1, "Usage: %s [--fullscreen] [--calib=x,y,x,y,x,y,x,y] [--mouse] speed\n", + argv[0]); /* Initialize SDL */ - if ( SDL_Init(SDL_INIT_VIDEO) < 0 ) { + if (SDL_Init(SDL_INIT_VIDEO) < 0) { fprintf(stderr, "Couldn't initialize SDL: %s\n",SDL_GetError()); return(1); } video_bpp = 0; - videoflags = SDL_SWSURFACE; - /* Set 640x480 video mode */ - if ( (screen=SDL_SetVideoMode(MAX_X,MAX_Y,video_bpp,videoflags)) == NULL ) { + if (!mouse) { + printf("Put Wiimote in discoverable mode (press 1+2)\n"); + wiimote = cwiid_open(&addr, 0); + if (!wiimote) + errx(1, "Can't find the Wiimote"); + + if (cwiid_set_rpt_mode(wiimote, CWIID_RPT_IR)) + errx(1, "Can't set IR repeat mode"); + } + + 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()); } /* White. */ memset(screen->pixels, 0xFF, screen->pitch * screen->h); - /* Draw lines. */ - for (i = 0; i < ARRAY_SIZE(lines); i++) - thick_line(screen, &lines[i]); - SDL_UnlockSurface(screen); SDL_UpdateRect(screen, 0, 0, 0, 0); - ballsur = ball_surface(screen); + if (needs_calibration) { + /* Calibration */ + calibrate(screen, "images/top-left.png", + wiimote, &calib[0], 0, 0); + calibrate(screen, "images/top-right.png", + wiimote, &calib[1], MAX_X - 1, 0); + calibrate(screen, "images/bottom-right.png", + wiimote, &calib[2], MAX_X - 1, MAX_Y - 1); + calibrate(screen, "images/bottom-left.png", + wiimote, &calib[3], 0, MAX_Y - 1); + } + + /* 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; + line_object_setup(&lines, &objects, &border[i]); + } - do { + /* 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; - struct line_segment *best_l; - unsigned i; + struct line_segment *best_l, *l, *next; double best_d; -#if 0 - SDL_Rect rect; + SDL_Event event; + struct timeval now; + bool ignored = false; - rect.x = ball.pos.x - ballsur->w/2; - rect.y = ball.pos.y - ballsur->h/2; - rect.w = ballsur->w; - rect.h = ballsur->h; -#endif + gettimeofday(&now, NULL); + + /* Expire lines */ + list_for_each_safe(&lines, l, next, list) { + if (timercmp(&l->expires, &now, <)) + talloc_free(l); + } again: best_d = MAX_X + MAX_Y; @@ -699,57 +1168,139 @@ int main(int argc, char *argv[]) new.x = ball.pos.x + move.x; new.y = ball.pos.y + move.y; - printf("Ball at %li,%li (%li,%li)\n", ball.pos.x, ball.pos.y, - move.x, move.y); - - for (i = 0; i < ARRAY_SIZE(lines); i++) { - if (last_l == &lines[i]) - continue; - if (intersect(&ball.pos, &new, &lines[i], &isect)) { + list_for_each(&lines, l, list) { + if (!l->ignore && intersect(&ball.pos, &new, l, &isect)) { double d; d = dist(&ball.pos, &isect); if (d < best_d) { - best_l = &lines[i]; + best_l = l; best_d = d; } } } + if (best_l) { - printf("Ball bouncing off (%lu,%lu %lu,%lu)!\n", + printf("Ball bouncing off (%f,%f %f,%f)!\n", best_l->start.x, best_l->start.y, best_l->end.x, best_l->end.y); - bounce(&ball, best_l, &move); + /* Only increase speed on *first* bounce, to avoid + * mega speed increases. */ + bounce(&ball, best_l, &move, ignored ? 0.0 : 0.1); + + /* If we moved, stop ignoring lines. */ + if (ignored && (move.x > 0.001 || move.y > 0.001)) + clear_ignore(&lines); + /* Don't hit the same line twice. */ - last_l = best_l; - isect_count++; + 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(5000); + exit(0); + } + } goto again; } - /* 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); + if (ignored && (move.x > 0.001 || move.y > 0.001)) { + clear_ignore(&lines); + printf("Moving by %f,%f to %f,%f\n", + move.x, move.y, new.x, new.y); } - /* 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; + /* We also need to redraw under old ball. */ + add_update(&ball.object); + + /* Move ball. */ ball.pos = new; - SDL_Delay(5); - } while (!SDL_PollEvent(&event) || event.type != SDL_QUIT); - SDL_Quit(); - return 0; + 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)) { + if (event.type == SDL_QUIT + || (event.type == SDL_KEYDOWN + && event.key.keysym.sym == SDLK_ESCAPE)) { + SDL_Quit(); + return 0; + } + if (mouse) { + switch (event.type) { + case SDL_MOUSEBUTTONDOWN: + drawing = true; + break; + case SDL_MOUSEBUTTONUP: + drawing = false; + break; + case SDL_MOUSEMOTION: + if (!drawing || + !same_side(&ball, event.motion.x)) + break; + + add_line(&lines, event.motion.x, + event.motion.y, + event.motion.x + + event.motion.xrel, + event.motion.y + + event.motion.yrel, + calib); + } + } + } + + if (mouse) + continue; + + if (cwiid_get_state(wiimote, &state)) + errx(1, "No wii state"); + + /* Find biggest state. */ + ir = &state.ir_src[0]; + 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[i]; + } + + if (ir->valid) { + /* Give it some slack for missing one or two... */ + 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; + } + time_since_last_ir++; + } } -#endif