2004-06-16 21:54:53 +00:00
|
|
|
/***************************************************************************
|
|
|
|
* __________ __ ___.
|
|
|
|
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
|
|
|
|
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
|
|
|
|
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
|
|
|
|
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
|
|
|
|
* \/ \/ \/ \/ \/
|
|
|
|
* $Id$
|
|
|
|
*
|
|
|
|
* Copyright (C) 2004 Daniel Stenberg
|
|
|
|
*
|
|
|
|
* All files in this archive are subject to the GNU General Public License.
|
|
|
|
* See the file COPYING in the source tree root for full license agreement.
|
|
|
|
*
|
|
|
|
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
|
|
|
* KIND, either express or implied.
|
|
|
|
*
|
|
|
|
****************************************************************************/
|
|
|
|
#include "plugin.h"
|
|
|
|
|
2004-06-16 21:56:46 +00:00
|
|
|
#ifdef HAVE_LCD_BITMAP
|
|
|
|
|
2004-06-16 21:54:53 +00:00
|
|
|
#define PAD_HEIGHT 10
|
|
|
|
#define PAD_WIDTH 2
|
|
|
|
|
|
|
|
#define BALL_HEIGTH 2
|
|
|
|
#define BALL_WIDTH 2
|
|
|
|
|
|
|
|
#define SPEEDX 150
|
|
|
|
#define SPEEDY 120
|
|
|
|
|
|
|
|
#define RES 100
|
|
|
|
|
|
|
|
static struct plugin_api* rb;
|
|
|
|
|
|
|
|
struct pong {
|
|
|
|
int ballx; /* current X*RES position of the ball */
|
|
|
|
int bally; /* current Y*RES position of the ball */
|
|
|
|
int w_pad[2]; /* wanted current Y positions of pads */
|
|
|
|
int e_pad[2]; /* existing current Y positions of pads */
|
|
|
|
int ballspeedx; /* */
|
|
|
|
int ballspeedy; /* */
|
|
|
|
|
|
|
|
int score[2];
|
|
|
|
};
|
|
|
|
|
|
|
|
void singlepad(int x, int y, int set)
|
|
|
|
{
|
|
|
|
if(set)
|
|
|
|
rb->lcd_fillrect(x, y, PAD_WIDTH, PAD_HEIGHT);
|
|
|
|
else
|
|
|
|
rb->lcd_clearrect(x, y, PAD_WIDTH, PAD_HEIGHT);
|
|
|
|
}
|
|
|
|
|
|
|
|
void pad(struct pong *p, int pad)
|
|
|
|
{
|
|
|
|
static int xpos[2]={0, 112-PAD_WIDTH};
|
|
|
|
|
|
|
|
/* clear existing pad */
|
|
|
|
singlepad(xpos[pad], p->e_pad[pad], 0);
|
|
|
|
|
|
|
|
/* draw wanted pad */
|
|
|
|
singlepad(xpos[pad], p->w_pad[pad], 1);
|
|
|
|
|
|
|
|
/* existing is now the wanted */
|
|
|
|
p->e_pad[pad] = p->w_pad[pad];
|
|
|
|
}
|
|
|
|
|
|
|
|
bool wallcollide(struct pong *p, int pad)
|
|
|
|
{
|
|
|
|
/* we have already checked for pad-collision, just check if this hits
|
|
|
|
the wall */
|
|
|
|
if(pad) {
|
|
|
|
/* right-side */
|
|
|
|
if(p->ballx > 112*RES)
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
if(p->ballx < 0)
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* returns true if the ball has hit a pad, and then the info variable
|
|
|
|
will have extra angle info */
|
|
|
|
|
|
|
|
bool padcollide(struct pong *p, int pad, int *info)
|
|
|
|
{
|
|
|
|
int x = p->ballx/RES;
|
|
|
|
int y = p->bally/RES;
|
|
|
|
|
|
|
|
if((y < (p->e_pad[pad]+PAD_HEIGHT)) &&
|
|
|
|
(y + BALL_HEIGTH > p->e_pad[pad])) {
|
|
|
|
/* Y seems likely right */
|
|
|
|
|
|
|
|
/* store the delta between ball-middle MINUS pad-middle, so
|
|
|
|
it returns:
|
|
|
|
0 when the ball hits exactly the middle of the pad
|
|
|
|
positive numbers when the ball is below the middle of the pad
|
|
|
|
negative numbers when the ball is above the middle of the pad
|
|
|
|
|
|
|
|
max number is +- PAD_HEIGHT/2
|
|
|
|
*/
|
|
|
|
|
|
|
|
*info = (y+BALL_HEIGTH/2) - (p->e_pad[pad] + PAD_HEIGHT/2);
|
|
|
|
|
|
|
|
if(pad) {
|
|
|
|
/* right-side */
|
|
|
|
if((x + BALL_WIDTH) > (112 - PAD_WIDTH))
|
|
|
|
return true; /* phump */
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
if(x <= 0)
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false; /* nah */
|
|
|
|
}
|
|
|
|
|
|
|
|
void bounce(struct pong *p, int pad, int info)
|
|
|
|
{
|
|
|
|
(void)pad; /* not used right now */
|
|
|
|
p->ballspeedx = -p->ballspeedx;
|
|
|
|
|
|
|
|
/* info is the hit-angle into the pad */
|
|
|
|
if(p->ballspeedy > 0) {
|
|
|
|
/* downwards */
|
|
|
|
if(info > 0) {
|
|
|
|
/* below the middle of the pad */
|
|
|
|
p->ballspeedy += info * RES/3;
|
|
|
|
}
|
|
|
|
else if(info < 0) {
|
|
|
|
/* above the middle */
|
|
|
|
p->ballspeedy = info * RES/2;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
/* upwards */
|
|
|
|
if(info > 0) {
|
|
|
|
/* below the middle of the pad */
|
|
|
|
p->ballspeedy = info * RES/2;
|
|
|
|
}
|
|
|
|
else if(info < 0) {
|
|
|
|
/* above the middle */
|
|
|
|
p->ballspeedy -= info * RES/3;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
p->ballspeedy += rb->rand()%21-10;
|
|
|
|
|
|
|
|
#if 0
|
|
|
|
fprintf(stderr, "INFO: %d YSPEED: %d\n", info, p->ballspeedy);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
void score(struct pong *p, int pad)
|
|
|
|
{
|
|
|
|
rb->splash(HZ/4, true, "%s scores!", pad?"right":"left");
|
|
|
|
rb->lcd_clear_display();
|
|
|
|
p->score[pad]++;
|
|
|
|
|
|
|
|
/* then move the X-speed of the ball and give it a random Y position */
|
|
|
|
p->ballspeedx = -p->ballspeedx;
|
|
|
|
p->bally = rb->rand()%(64-BALL_HEIGTH);
|
|
|
|
|
|
|
|
/* restore Y-speed to default */
|
|
|
|
p->ballspeedy = (p->ballspeedy > 0) ? SPEEDY : -SPEEDY;
|
|
|
|
|
|
|
|
/* set the existing pad positions to something weird to force pad
|
|
|
|
updates */
|
|
|
|
p->e_pad[0] = -1;
|
|
|
|
p->e_pad[1] = -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
void ball(struct pong *p)
|
|
|
|
{
|
|
|
|
int x = p->ballx/RES;
|
|
|
|
int y = p->bally/RES;
|
|
|
|
|
|
|
|
int newx;
|
|
|
|
int newy;
|
|
|
|
|
|
|
|
int info;
|
|
|
|
|
|
|
|
/* movement */
|
|
|
|
p->ballx += p->ballspeedx;
|
|
|
|
p->bally += p->ballspeedy;
|
|
|
|
|
|
|
|
newx = p->ballx/RES;
|
|
|
|
newy = p->bally/RES;
|
|
|
|
|
|
|
|
/* detect if ball hits a wall */
|
|
|
|
if(newy + BALL_HEIGTH > 64) {
|
|
|
|
/* hit floor, bounce */
|
|
|
|
p->ballspeedy = -p->ballspeedy;
|
|
|
|
newy = 64 - BALL_HEIGTH;
|
|
|
|
p->bally = newy * RES;
|
|
|
|
}
|
|
|
|
else if(newy < 0) {
|
|
|
|
/* hit ceiling, bounce */
|
|
|
|
p->ballspeedy = -p->ballspeedy;
|
|
|
|
p->bally = 0;
|
|
|
|
newy = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* detect if ball hit pads */
|
|
|
|
if(padcollide(p, 0, &info))
|
|
|
|
bounce(p, 0, info);
|
|
|
|
else if(padcollide(p, 1, &info))
|
|
|
|
bounce(p, 1, info);
|
|
|
|
else if(wallcollide(p, 0))
|
|
|
|
score(p, 1);
|
|
|
|
else if(wallcollide(p, 1))
|
|
|
|
score(p, 0);
|
|
|
|
|
|
|
|
/* clear old position */
|
|
|
|
rb->lcd_clearrect(x, y, BALL_WIDTH, BALL_HEIGTH);
|
|
|
|
|
|
|
|
/* draw the new ball position */
|
|
|
|
rb->lcd_fillrect(newx, newy, BALL_WIDTH, BALL_HEIGTH);
|
|
|
|
}
|
|
|
|
|
|
|
|
void padmove(int *pos, int dir)
|
|
|
|
{
|
|
|
|
*pos += dir;
|
|
|
|
if(*pos > (64-PAD_HEIGHT))
|
|
|
|
*pos = (64-PAD_HEIGHT);
|
|
|
|
else if(*pos < 0)
|
|
|
|
*pos = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool keys(struct pong *p)
|
|
|
|
{
|
|
|
|
int key;
|
|
|
|
|
|
|
|
int time = 4; /* number of ticks this function will loop reading keys */
|
|
|
|
int start = *rb->current_tick;
|
|
|
|
int end = start + time;
|
|
|
|
|
|
|
|
while(end > *rb->current_tick) {
|
|
|
|
key = rb->button_get_w_tmo(end - *rb->current_tick);
|
|
|
|
|
|
|
|
if(key & BUTTON_OFF)
|
|
|
|
return false; /* exit game NOW */
|
|
|
|
|
|
|
|
if(key & BUTTON_LEFT) /* player left goes down */
|
|
|
|
padmove(&p->w_pad[0], 1);
|
|
|
|
|
|
|
|
if(key & BUTTON_F1) /* player left goes up */
|
|
|
|
padmove(&p->w_pad[0], -1);
|
|
|
|
|
|
|
|
if(key & BUTTON_RIGHT) /* player right goes down */
|
|
|
|
padmove(&p->w_pad[1], 1);
|
|
|
|
|
|
|
|
if(key & BUTTON_F3) /* player right goes up */
|
|
|
|
padmove(&p->w_pad[1], -1);
|
|
|
|
}
|
|
|
|
return true; /* return false to exit game */
|
|
|
|
}
|
|
|
|
|
|
|
|
void showscore(struct pong *p)
|
|
|
|
{
|
|
|
|
static char buffer[20];
|
|
|
|
|
2004-06-16 22:16:50 +00:00
|
|
|
rb->snprintf(buffer, 20, "%d - %d", p->score[0], p->score[1]);
|
2004-06-16 21:54:53 +00:00
|
|
|
rb->lcd_puts(4, 0, buffer);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* this is the plugin entry point */
|
|
|
|
enum plugin_status plugin_start(struct plugin_api* api, void* parameter)
|
|
|
|
{
|
|
|
|
struct pong pong;
|
|
|
|
bool game = true;
|
|
|
|
|
|
|
|
/* init the struct with some silly values to start with */
|
|
|
|
|
|
|
|
pong.ballx = 20*RES;
|
|
|
|
pong.bally = 20*RES;
|
|
|
|
|
|
|
|
pong.e_pad[0] = 0;
|
|
|
|
pong.w_pad[0] = 7;
|
|
|
|
pong.e_pad[1] = 0;
|
|
|
|
pong.w_pad[1] = 40;
|
|
|
|
|
|
|
|
pong.ballspeedx = SPEEDX;
|
|
|
|
pong.ballspeedy = SPEEDY;
|
|
|
|
|
|
|
|
pong.score[0] = pong.score[1] = 0; /* lets start at 0 - 0 ;-) */
|
|
|
|
|
|
|
|
/* if you don't use the parameter, you can do like
|
|
|
|
this to avoid the compiler warning about it */
|
|
|
|
(void)parameter;
|
|
|
|
|
2004-06-16 22:16:01 +00:00
|
|
|
TEST_PLUGIN_API(api);
|
|
|
|
|
2004-06-16 21:54:53 +00:00
|
|
|
rb = api; /* use the "standard" rb pointer */
|
|
|
|
|
|
|
|
/* Clear screen */
|
|
|
|
rb->lcd_clear_display();
|
|
|
|
|
|
|
|
/* go go go */
|
|
|
|
while(game) {
|
|
|
|
showscore(&pong);
|
|
|
|
pad(&pong, 0); /* draw left pad */
|
|
|
|
pad(&pong, 1); /* draw right pad */
|
|
|
|
ball(&pong); /* move and draw ball */
|
|
|
|
|
|
|
|
rb->lcd_update();
|
|
|
|
|
|
|
|
game = keys(&pong); /* deal with keys */
|
|
|
|
}
|
|
|
|
|
|
|
|
return PLUGIN_OK;
|
|
|
|
}
|
2004-06-16 21:56:46 +00:00
|
|
|
|
|
|
|
#endif /* HAVE_LCD_BITMAP */
|