diff --git a/firmware/l0dable/bricks.c b/firmware/l0dable/bricks.c new file mode 100644 index 0000000..d3a90cd --- /dev/null +++ b/firmware/l0dable/bricks.c @@ -0,0 +1,258 @@ +/* bricks.c - provided by briks */ + +#include "basic/basic.h" +#include "usetable.h" + +#define SCREEN_WIDTH 96 +#define SCREEN_HEIGHT 67 +#define FIELD_WIDTH 8 +#define FIELD_HEIGHT 7 +#define BRICK_WIDTH 11 +#define BRICK_HEIGHT 4 +#define BRICK_SPACING 1 + +#define PADDLE_WIDTH 20 +#define PADDLE_Y 66 +#define PADDLE_SPEED 3 + +#define PAUSE_INITIAL 30 + +#define LIVES_INITIAL 5 + +#define LEVELS 3 + +int levels[LEVELS][FIELD_HEIGHT][FIELD_WIDTH] = { + { + {0,0,0,0,0,0,0,0}, + {0,1,1,0,0,1,1,0}, + {0,1,1,0,0,1,1,0}, + {0,0,0,0,0,0,0,0}, + {0,0,0,0,0,0,0,0}, + {0,1,0,0,0,0,1,0}, + {0,0,1,1,1,1,0,0} + }, + { + {0,1,0,1,0,1,0,1}, + {1,0,1,0,1,0,1,0}, + {0,1,0,1,0,1,0,1}, + {1,0,1,0,1,0,1,0}, + {0,1,0,1,0,1,0,1}, + {1,0,1,0,1,0,1,0}, + {0,0,0,0,0,0,0,0} + }, + { + {1,1,1,1,1,1,1,1}, + {1,1,1,1,1,1,1,1}, + {1,1,1,1,1,1,1,1}, + {1,1,1,1,1,1,1,1}, + {1,1,1,1,1,1,1,1}, + {1,1,1,1,1,1,1,1}, + {0,0,0,0,0,0,0,0} + } +}; + +int playLevel(int levelNo, int pause); +void drawBricks(int bricks[FIELD_HEIGHT][FIELD_WIDTH]); +void drawPxChk(int x, int y, int color); +void drawBall(int ball[2], int color); +void drawPaddle(int paddleX, int color); +int fieldIsCleared(int bricks[FIELD_HEIGHT][FIELD_WIDTH]); +int abs(int x); + +void ram(void) { + + lcdClear(); + lcdPrintln(""); + lcdPrintln(""); + lcdPrintln(" BRICKS"); + lcdPrintln(""); + lcdPrintln(""); + lcdPrintln(""); + lcdPrintln(" by briks"); + lcdRefresh(); + delayms(1000); + + int pause = PAUSE_INITIAL; + for (int i = 1; true; i++) { + lcdClear(); + lcdPrintln(""); + lcdPrintln(""); + lcdPrintln(""); + lcdPrintln(" Level"); + lcdPrintln(""); + lcdPrint(" "); + lcdPrintln(IntToStr(i, 2, 0)); + lcdRefresh(); + delayms(1000); + if (playLevel(i % LEVELS, pause) == 0) { + return; + } + pause = pause - (pause / 4); // shorten pause (increases speed) + } +} + +int playLevel(int levelNo, int pause) { + + lcdClear(); + + // load level + int bricks[FIELD_HEIGHT][FIELD_WIDTH]; + memcpy(bricks, levels[levelNo], sizeof(bricks)); + + // initialisation + int ball[2] = {SCREEN_WIDTH / 2, FIELD_HEIGHT * (BRICK_HEIGHT + BRICK_SPACING) + 1}; + int direction[2] = {1,1}; + int paddleX = SCREEN_WIDTH / 2 - PADDLE_WIDTH / 2; + int lives = LIVES_INITIAL; + + drawBricks(bricks); + + while ( 1 ) { + + // ball + drawBall(ball, 0); + ball[0] += direction[0]; + ball[1] += direction[1]; + + // paddle / user input + drawPaddle(paddleX, 0); + int key = getInputRaw(); + switch (key) { + case BTN_ENTER: + // exit + return 0; + case BTN_LEFT: + paddleX -= PADDLE_SPEED; + if (paddleX < 0) + paddleX = 0; + break; + case BTN_RIGHT: + paddleX += PADDLE_SPEED; + if (paddleX + PADDLE_WIDTH > SCREEN_WIDTH) + paddleX = SCREEN_WIDTH - PADDLE_WIDTH; + break; + } + drawPaddle(paddleX, 1); + + // collisions + + // bricks + int x = ball[0] / ((BRICK_WIDTH + BRICK_SPACING)); + int y = ball[1] / ((BRICK_HEIGHT + BRICK_SPACING)); + if (0 <= x && x < FIELD_WIDTH && 0 <= y && y < FIELD_HEIGHT) { + if (bricks[y][x] == 1) { + // collision with brick + int xRel = ball[0] - x * (BRICK_WIDTH + BRICK_SPACING); + int yRel = ball[1] - y * (BRICK_HEIGHT + BRICK_SPACING); + if (xRel == 0 || xRel == BRICK_WIDTH) + direction[0] *= -1; // hit top or bottom + if (yRel == 0 || yRel == BRICK_HEIGHT) + direction[1] *= -1; // hit left or right + bricks[y][x] = 0; + if (fieldIsCleared(bricks)) + return 1; // next level + } + } + + // paddle / bottom + if (direction[1] > 0) { + // moving to the bottom + if (ball[1] >= PADDLE_Y) { + if (paddleX <= ball[0] && ball[0] <= paddleX + PADDLE_WIDTH) { + // collision with paddle + direction[1] = - abs(direction[1]); + if (key == BTN_LEFT) + direction[0] = -2; + else if (key == BTN_RIGHT) + direction[0] = 2; + else + direction[0] = (direction[0] > 0) ? 1 : -1; + } + else { + // ball lost + lives--; + if (lives == 0) { + lcdClear(); + lcdPrintln(""); + lcdPrintln(""); + lcdPrintln(""); + lcdPrintln(""); + lcdPrintln(" GAME OVER"); + lcdRefresh(); + delayms(2000); + return 0; + } + ball[0] = SCREEN_WIDTH / 2; + ball[1] = FIELD_HEIGHT * (BRICK_HEIGHT + BRICK_SPACING) + 1; + direction[0] = (paddleX + PADDLE_WIDTH / 2 < ball[0]) ? -1 : 1; + direction[1] = 1; + } + } + } + + // walls + + if (ball[1] <= 0) + direction[1] = abs(direction[1]); + if (ball[0] <= 0) + direction[0] = abs(direction[0]); + else if (ball[0] >= SCREEN_WIDTH - 1) + direction[0] = - abs(direction[0]); + + drawBricks(bricks); + drawBall(ball, 1); + + lcdRefresh(); + delayms(pause); + } + return 0; +} + +void drawBricks(int bricks[FIELD_HEIGHT][FIELD_WIDTH]) { + for (int x = 0; x < FIELD_WIDTH; x++) + for (int y = 0; y < FIELD_HEIGHT; y++) + for (int i = 0; i < BRICK_WIDTH; i++) + for (int j = 0; j < BRICK_HEIGHT; j++) + lcdSetPixel(x * (BRICK_WIDTH + BRICK_SPACING) + i, y * (BRICK_HEIGHT + BRICK_SPACING) + j, bricks[y][x]); +} + +void drawPxChk(int x, int y, int color) { + if (x < 0 || x >= SCREEN_WIDTH || y < 0 || y >= SCREEN_HEIGHT) + return; + lcdSetPixel(x, y, color); +} + +void drawBall(int ball[2], int color) { + drawPxChk(ball[0] - 1, ball[1] - 1, color); + drawPxChk(ball[0], ball[1] - 1, color); + drawPxChk(ball[0] + 1, ball[1] - 1, color); + drawPxChk(ball[0] - 1, ball[1], color); + drawPxChk(ball[0], ball[1], color); + drawPxChk(ball[0] + 1, ball[1], color); + drawPxChk(ball[0] - 1, ball[1] + 1, color); + drawPxChk(ball[0], ball[1] + 1, color); + drawPxChk(ball[0] + 1, ball[1] + 1, color); +} + +void drawPaddle(int paddleX, int color) { + for (int x = 0; x < PADDLE_WIDTH; x++) { + lcdSetPixel(paddleX + x, PADDLE_Y, color); + lcdSetPixel(paddleX + x, PADDLE_Y + 1, color); + } +} + +int fieldIsCleared(int bricks[FIELD_HEIGHT][FIELD_WIDTH]) { + for (int x = 0; x < FIELD_WIDTH; x++) + for (int y = 0; y < FIELD_HEIGHT; y++) + if (bricks[y][x] == 1) + return 0; + return true; +} + +int abs(int x) { + if (x < 0) + return x * -1; + return x; +} + +