diff --git a/F0-nolib/blink/blink.bin b/F0-nolib/blink/blink.bin old mode 100755 new mode 100644 index af5480b..40e69e2 Binary files a/F0-nolib/blink/blink.bin and b/F0-nolib/blink/blink.bin differ diff --git a/F1-nolib/Tetris/Readme b/F1-nolib/Tetris/Readme index dda72a0..7351a80 100644 --- a/F1-nolib/Tetris/Readme +++ b/F1-nolib/Tetris/Readme @@ -1,2 +1,11 @@ -TETRIS @ HUB75E-based RGB LED panel +Some games @ HUB75E-based RGB LED panel + +Snake: +Classical snake with additional food: +cyan - increase snake size +yellow - additional score +green - decrease snake size + +Tetris: +Tetris with a little bigger cup than in classical game \ No newline at end of file diff --git a/F1-nolib/Tetris/TETRIS.bin b/F1-nolib/Tetris/TETRIS.bin index 2b2cb54..4a75d18 100755 Binary files a/F1-nolib/Tetris/TETRIS.bin and b/F1-nolib/Tetris/TETRIS.bin differ diff --git a/F1-nolib/Tetris/arkanoid.c b/F1-nolib/Tetris/arkanoid.c new file mode 100644 index 0000000..98cb9f0 --- /dev/null +++ b/F1-nolib/Tetris/arkanoid.c @@ -0,0 +1,631 @@ +/* + * This file is part of the TETRIS project. + * Copyright 2021 Edward V. Emelianov . + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "adcrandom.h" +#include "arkanoid.h" +#include "buttons.h" +#include "debug.h" +#include "fonts.h" +#include "proto.h" +#include "screen.h" + +// fraction of 1 for fixed point +#define FIXPOINT (1000) + +// time (in ms) for keeping racket old X speed +#define KEEPXSPEEDTIME (300) + +// max amount of balls +#define NBALLSMAX (8) +// fixed point macros: +// int to fixed +#define i2fixed(x) ((x)*FIXPOINT) +// fixed to int +#define fixed2i(x) ((x)/FIXPOINT) + +// field parameters (should be multiple of BRICK_WIDTH) +#define FIELD_WIDTH (39) +// bottom height +#define FIELD_BOTTOM (12) +// X coordinate of field start +#define X0 (1) +// active zone (with bricks) +#define FIELD_ACTIVE (SCREEN_HEIGHT - FIELD_BOTTOM) +// Y-coordinate of racket +#define RACKET_Y (SCREEN_HEIGHT - 2) +// floor (should be seen when bonus activates) +#define FLOOR_Y (SCREEN_HEIGHT - 1) + +// starting Y speed of the ball +#define BALL_NORMAL_SPEED (0.007 * FIXPOINT) +#define BALL_MIN_Y_SPEED (0.005 * FIXPOINT) +// maximal speed of the ball +#define BALL_MAX_X_SPEED (0.015 * FIXPOINT) +#define BALL_MAX_Y_SPEED (0.010 * FIXPOINT) +// racket speed delta per millisecond +#define D_RACKET_SPEED (0.002 * FIXPOINT) + +// part of racket's speed to add to ball's speed (1/frac) +#define XSPDFRAC (200) +#define YSPDFRAC (1000) + +#define BRICK_WIDTH (3) + +// colors +#define BACKGROUND_COLOR (COLOR_BLACK) +#define FOREGROUND_COLOR (COLOR_LYELLOW) +#define CUP_COLOR (COLOR_LRED) +#define RACKET_COLOR (COLOR_LGREEN) +#define BALL_COLOR (COLOR_GREEN) + +// racket widths +#define RACKET_MEDIUM (5) +#define RACKET_NARROW (3) +#define RACKET_WIDE (9) + +// starting amount of lives +#define LIVES_AT_START (3) + +// probability of bonus appears (percent) +#define BONUS_PROBABILITY (35) + +// there's a bonus inside this brick +#define BRICK_IS_BONUS (1<<7) +// and there's another free flag @ 1<<6 + reserved @ 1<<5 +// mask to get color from brick type +#define COLOR_MASK (0x3f) +typedef enum{ // brick types + BRICK_NONE = 0, // no brick + BRICK_WALL, // non-breakable brick + BRICK_GRAY, // two bricks without bonuses + BRICK_WHITE, + BRICK_FLOOR, // add floor under the racket + BRICK_ENLARGE, // enlarge the rocket (don't forget to move it's center when positioned at the edge) + BRICK_CATCH, // sticky balls + BRICK_SLOW, // slow ball + BRICK_DISRUPTION // each ball divides to three +} bricktype; + +static const uint8_t brickcolors[] = { + [BRICK_NONE] = 0, + [BRICK_WALL] = CUP_COLOR, + [BRICK_GRAY] = 0b00100101, + [BRICK_WHITE] = COLOR_WHITE, + [BRICK_FLOOR] = COLOR_LBLUE, + [BRICK_ENLARGE] = COLOR_LGREEN, + [BRICK_CATCH] = COLOR_LCYAN, + [BRICK_SLOW] = COLOR_LYELLOW, + [BRICK_DISRUPTION] = COLOR_LPURPLE, +}; + +// empty cell +#define ____ (0) +// simple bricks with no bonuses +#define _VV_ (BRICK_GRAY) +#define _WW_ (BRICK_WHITE) +// bricks with bonuses +#define _FL_ (BRICK_FLOOR) +#define _EN_ (BRICK_ENLARGE) +#define _CA_ (BRICK_CATCH) +#define _SL_ (BRICK_SLOW) +#define _DI_ (BRICK_DISRUPTION) +// wall +#define XXXX (BRICK_WALL) + +#define FIELD_SZ (FIELD_ACTIVE * FIELD_WIDTH / BRICK_WIDTH) + +#define LEVELS_AMOUNT (3) + +// empty level +#if 0 +static uint8_t levelX[FIELD_SZ] = { +/*00*/ ____,____,____,____,____,____,____,____,____,____,____,____,____, +/*01*/ ____,____,____,____,____,____,____,____,____,____,____,____,____, +/*02*/ ____,____,____,____,____,____,____,____,____,____,____,____,____, +/*03*/ ____,____,____,____,____,____,____,____,____,____,____,____,____, +/*04*/ ____,____,____,____,____,____,____,____,____,____,____,____,____, +/*05*/ ____,____,____,____,____,____,____,____,____,____,____,____,____, +/*06*/ ____,____,____,____,____,____,____,____,____,____,____,____,____, +/*07*/ ____,____,____,____,____,____,____,____,____,____,____,____,____, +/*08*/ ____,____,____,____,____,____,____,____,____,____,____,____,____, +/*09*/ ____,____,____,____,____,____,____,____,____,____,____,____,____, +/*10*/ ____,____,____,____,____,____,____,____,____,____,____,____,____, +/*11*/ ____,____,____,____,____,____,____,____,____,____,____,____,____, +/*12*/ ____,____,____,____,____,____,____,____,____,____,____,____,____, +/*13*/ ____,____,____,____,____,____,____,____,____,____,____,____,____, +/*14*/ ____,____,____,____,____,____,____,____,____,____,____,____,____, +/*15*/ ____,____,____,____,____,____,____,____,____,____,____,____,____, +/*16*/ ____,____,____,____,____,____,____,____,____,____,____,____,____, +/*17*/ ____,____,____,____,____,____,____,____,____,____,____,____,____, +/*18*/ ____,____,____,____,____,____,____,____,____,____,____,____,____, +/*19*/ ____,____,____,____,____,____,____,____,____,____,____,____,____, +}; +#endif + +static uint8_t level1[FIELD_SZ] = { +/*00*/ _VV_,____,____,____,____,_WW_,____,_WW_,____,____,____,____,_VV_, +/*01*/ ____,_VV_,____,____,____,____,_WW_,____,____,____,____,_VV_,____, +/*02*/ ____,____,_VV_,____,____,____,____,____,____,____,_VV_,____,____, +/*03*/ ____,_SL_,____,_VV_,____,____,____,____,____,_VV_,____,_SL_,____, +/*04*/ ____,____,____,____,_VV_,_DI_,_DI_,_DI_,_VV_,____,____,____,____, +/*05*/ ____,____,____,____,____,_VV_,_DI_,_VV_,____,____,____,____,____, +/*06*/ ____,____,____,____,_EN_,____,_VV_,____,_EN_,____,____,____,____, +/*07*/ ____,____,____,____,____,_CA_,_FL_,_CA_,____,____,____,____,____, +/*08*/ ____,____,____,____,____,____,_CA_,____,____,____,____,____,____, +/*09*/ ____,____,____,____,____,____,____,____,____,____,____,____,____, +/*10*/ ____,____,____,____,____,____,____,____,____,____,____,____,____, +/*11*/ ____,____,____,____,____,____,____,____,____,____,____,____,____, +/*12*/ ____,____,____,____,____,____,____,____,____,____,____,____,____, +/*13*/ ____,____,____,____,____,____,____,____,____,____,____,____,____, +/*14*/ ____,____,____,____,____,____,____,____,____,____,____,____,____, +/*15*/ ____,____,____,____,____,____,____,____,____,____,____,____,____, +/*16*/ ____,____,____,____,____,____,____,____,____,____,____,____,____, +/*17*/ ____,____,____,____,____,____,____,____,____,____,____,____,____, +/*18*/ ____,____,____,____,____,____,____,____,____,____,____,____,____, +/*19*/ ____,____,____,____,____,____,____,____,____,____,____,____,____, +}; +static uint8_t level2[FIELD_SZ] = { +/*00*/ ____,____,____,_WW_,____,_WW_,____,_WW_,____,_WW_,____,____,____, +/*01*/ ____,____,_WW_,_VV_,____,____,_WW_,____,____,_VV_,_WW_,____,____, +/*02*/ ____,_WW_,_WW_,____,_VV_,____,____,____,_VV_,____,_WW_,_WW_,____, +/*03*/ _WW_,____,____,_WW_,____,_VV_,____,_VV_,____,_WW_,____,____,_WW_, +/*04*/ ____,____,_VV_,____,_WW_,____,_VV_,____,_WW_,____,_VV_,____,____, +/*05*/ ____,____,____,____,____,_WW_,____,_WW_,____,____,____,____,____, +/*06*/ ____,____,____,____,____,____,_WW_,____,____,____,____,____,____, +/*07*/ ____,____,____,____,____,____,____,____,____,____,____,____,____, +/*08*/ ____,____,____,____,____,____,____,____,____,____,____,____,____, +/*09*/ ____,____,____,____,____,____,____,____,____,____,____,____,____, +/*10*/ ____,____,____,____,____,____,____,____,____,____,____,____,____, +/*11*/ ____,____,____,____,____,____,____,____,____,____,____,____,____, +/*12*/ ____,____,____,____,____,____,____,____,____,____,____,____,____, +/*13*/ ____,____,____,____,____,____,____,____,____,____,____,____,____, +/*14*/ ____,____,____,____,____,____,____,____,____,____,____,____,____, +/*15*/ ____,____,____,____,____,____,____,____,____,____,____,____,____, +/*16*/ ____,____,____,____,____,____,____,____,____,____,____,____,____, +/*17*/ ____,____,____,____,____,____,____,____,____,____,____,____,____, +/*18*/ ____,____,____,____,____,____,____,____,____,____,____,____,____, +/*19*/ ____,____,____,____,____,____,____,____,____,____,____,____,____, +}; +static uint8_t level3[FIELD_SZ] = { // w=13, h=20 +/*00*/ _WW_,_VV_,_WW_,____,____,_VV_,____,____,____,____,_VV_,____,____, +/*01*/ _VV_,_WW_,_VV_,____,_VV_,____,_VV_,____,____,____,_VV_,____,____, +/*02*/ ____,____,_WW_,_VV_,____,____,____,_VV_,____,____,_VV_,____,____, +/*03*/ ____,____,____,_WW_,_VV_,____,____,____,_VV_,____,_VV_,____,_VV_, +/*04*/ ____,____,____,____,_WW_,_VV_,____,____,____,_VV_,_VV_,_VV_,____, +/*05*/ ____,____,_WW_,____,____,_WW_,_VV_,____,____,____,_VV_,_WW_,_WW_, +/*06*/ ____,____,____,____,____,____,_WW_,_VV_,____,____,_VV_,____,____, +/*07*/ ____,____,____,_WW_,_WW_,_WW_,_WW_,_WW_,_VV_,____,_VV_,____,_VV_, +/*08*/ ____,____,____,____,____,____,____,____,_WW_,_VV_,_VV_,_VV_,_WW_, +/*09*/ ____,____,_VV_,____,____,____,____,____,____,_WW_,_VV_,_WW_,____, +/*10*/ ____,____,_VV_,_VV_,_VV_,_VV_,_VV_,_VV_,_VV_,_VV_,_VV_,_VV_,_VV_, +/*11*/ ____,____,____,____,____,____,____,____,____,____,____,____,____, +/*12*/ ____,____,____,____,____,____,____,____,____,____,____,____,____, +/*13*/ ____,____,____,____,____,____,____,____,____,____,____,____,____, +/*14*/ ____,____,____,____,____,____,____,____,____,____,____,____,____, +/*15*/ ____,____,____,____,____,____,____,____,____,____,____,____,____, +/*16*/ ____,____,____,____,____,____,____,____,____,____,____,____,____, +/*17*/ ____,____,____,____,____,____,____,____,____,____,____,____,____, +/*18*/ ____,____,____,____,____,____,____,____,____,____,____,____,____, +/*19*/ ____,____,____,____,____,____,____,____,____,____,____,____,____, +}; +static uint8_t *levels[LEVELS_AMOUNT] = {level1, level2, level3}; +static uint8_t currentLevel[FIELD_SZ]; // data of current level's bricks +static uint8_t paused = 0; + +// flags for theball.flags +#define ACTIVE_BALL (1<<0) +#define STICKY_BALL (1<<1) + +// !!! BALL AND RACKET COORDINATES ARE IN FIELD SYSTEM !!! +typedef struct{ // all coordinates and speeds are fixed point + int x; int y; // old/actual center coordinates + int xspeed; int yspeed; // speed + uint8_t flags; +} theball; + +extern uint32_t volatile Tms; + +// current balls amount (level ends when last ball falls) +static uint8_t Nballs; +static theball balls[NBALLSMAX]; +static uint8_t racketW; // current racket width +// all coordinates and speeds are fixed point +static int racketX; +static int racketspeed, oldracketspeed; +// level number +static uint8_t curLevNo; +// lifes amount +static uint16_t Nlives; +// total bricks amount +static uint16_t Nbricks; + +// bonuses +static uint8_t floor_activated; // ==1 if floor is active + +static void showText(){ // show score/level etc + int y = curfont->height - curfont->baseline; + PutStringAt(X0+FIELD_WIDTH+2, y, " "); + PutStringAt(X0+FIELD_WIDTH+2, y, u2str(score)); + y += curfont->height; + int w = PutStringAt(X0+FIELD_WIDTH+2, y, u2str(Nlives)); + PutStringAt(X0+FIELD_WIDTH+2+w, y, "L "); + y += curfont->height; + if(paused){ + PutStringAt(X0+FIELD_WIDTH+2, y, "PAUSE"); + }else + PutStringAt(X0+FIELD_WIDTH+2, y, " "); +} + +#ifdef EBUG +static void showcur(){ + uint8_t *targ = currentLevel; + const char *brk; + for(int y = 0; y < FIELD_ACTIVE; ++y){ + for(int x = 0; x < (FIELD_WIDTH / BRICK_WIDTH); ++x){ + uint8_t b = *targ++; + switch(b & COLOR_MASK){ + case 0: + brk = "___"; + break; + case 1: + brk = "XXX"; + break; + case 2: + brk = "_VV"; + break; + case 3: + brk = "_WW"; + break; + case 4: + brk = "_FL"; + break; + case 5: + brk = "_EN"; + break; + case 6: + brk = "_CA"; + break; + case 7: + brk = "_SL"; + break; + case 8: + brk = "_DI"; + break; + default: + brk = "????"; + break; + } + USB_send(brk); + if(b & BRICK_IS_BONUS) USB_send("+"); + else USB_send("_"); + USB_send(" "); + } + USB_send("\n"); + } +} +#else +#define showcur() +#endif + +static void draw_level(){ + if(curLevNo > LEVELS_AMOUNT) curLevNo = LEVELS_AMOUNT; + uint8_t *brickz = levels[curLevNo], *targ = currentLevel; + for(int y = 0; y < FIELD_ACTIVE; ++y){ + for(int x = 0; x < (FIELD_WIDTH / BRICK_WIDTH); ++x){ + uint8_t cur = *brickz++, curcol = cur & COLOR_MASK; + if((curcol > BRICK_WHITE) && getRand() % 100 < BONUS_PROBABILITY) cur |= BRICK_IS_BONUS; + *targ++ = cur; // copy brick data into level + if(!cur) continue; + uint8_t curclolr = brickcolors[curcol]; + int X = X0 + x*BRICK_WIDTH; + DrawPix(X+0, y, curclolr); + DrawPix(X+1, y, curclolr); + DrawPix(X+2, y, curclolr); + DBG("Brick "); DBGU(cur); DBG(" @ "); DBGU(x); DBG(" , "); DBGU(y); NL(); + if(cur != BRICK_WALL) ++Nbricks; + } + } + showcur(); + for(int y = 0; y < SCREEN_HEIGHT; ++y){ + DrawPix(X0-1, y, CUP_COLOR); + DrawPix(X0+FIELD_WIDTH, y, CUP_COLOR); + } + showText(); +} + +// x is pixel coordinate in field system +static void draw_racket(int x, uint8_t color){ + int xl = X0 + x - racketW / 2; + for(int i = 0; i < racketW; ++i){ + DrawPix(xl + i, RACKET_Y, color); + } + //DBG("Racket @"); DBGU(x); DBG(" colr "); DBGU(color); DBG(" spd "); DBGU(racketspeed); NL(); +} + +static void process_racket(int dt){ + if(!racketspeed) return; + oldracketspeed = racketspeed; + int xnew = racketX + racketspeed * dt; + int X = fixed2i(xnew), Xo = fixed2i(racketX); + if(X == Xo){ + racketX = xnew; + return; + } + int xmax = FIELD_WIDTH - 1 - racketW/2; + if(X < racketW/2){ + X = racketW/2; + xnew = i2fixed(X); + racketspeed = 0; + //DBG("Left border\n"); + }else if(X > xmax){ + X = xmax; + xnew = i2fixed(X); + racketspeed = 0; + //DBG("Right border\n"); + }else{ + draw_racket(Xo, BACKGROUND_COLOR); // clear racket + draw_racket(X, RACKET_COLOR); + } + racketX = xnew; +} + +static void startlevl(){ + racketW = RACKET_MEDIUM; + DBG("wid: "); DBGU(racketW); NL(); + racketX = i2fixed(FIELD_WIDTH / 2); + racketspeed = 0; + oldracketspeed = 0; + floor_activated = 0; + Nballs = 1; + for(int i = 1; i < NBALLSMAX; ++i) balls[i].flags = 0; + int rx = fixed2i(racketX); + balls[0] = (theball){.x = racketX, .y = i2fixed(RACKET_Y-1), .flags = STICKY_BALL|ACTIVE_BALL}; + draw_racket(rx, RACKET_COLOR); + DrawPix(X0+rx, RACKET_Y-1, BALL_COLOR); + Tlast = Tms; + showText(); // refresh Nlives text + clear_events(); +} + +// The ball gets the active field cell? +// @return 0 if ball falled out +static int process_ball(theball *ball, int dt){ // dt is time since last check, ms + if(ball->flags & STICKY_BALL){ // ball follows the racket + int Xold = fixed2i(ball->x), Xnew = fixed2i(racketX); + ball->y = i2fixed(RACKET_Y - 1); + ball->x = racketX; + if(Xold != Xnew){ // redraw ball + DrawPix(X0 + Xold, RACKET_Y - 1, BACKGROUND_COLOR); + DrawPix(X0 + Xnew, RACKET_Y - 1, BALL_COLOR); + } + return 1; + } + int DX = ball->xspeed * dt, DY = ball->yspeed * dt; + int xnew = ball->x + DX; + int ynew = ball->y + DY; + // get integer coordinates (in field's system!) + int X = fixed2i(xnew), Y = fixed2i(ynew); + int Xo = fixed2i(ball->x), Yo = fixed2i(ball->y); + if(Xo == X && Yo == Y){ // still the old cell + ball->x = xnew; + ball->y = ynew; + return 1; + } + // ball is in new cell; delete old + DrawPix(X0 + Xo, Yo, BACKGROUND_COLOR); + //DBG("Del ball @ "); DBGU(Xo); DBG(", "); DBGU(Yo); NL(); + if(X < 0){ // left border + DBG("Ball: left border\n"); + xnew = DX; X = 0; + ball->xspeed = -ball->xspeed; + }else if(X > FIELD_WIDTH - 1){ // right border + DBG("Ball: right border\n"); + X = FIELD_WIDTH-1; + xnew = i2fixed(FIELD_WIDTH) - DX; + ball->xspeed = -ball->xspeed; + }else if(Y < 0){ // upper border + DBG("Ball: upper border\n"); + ynew = DY; Y = 0; + ball->yspeed = -ball->yspeed; + }else if(Y > RACKET_Y){ // fall into the depth + if(!floor_activated){ + DBG("Ball: drop down\n"); + return 0; + } + ynew = i2fixed(RACKET_Y) - DY; Y = RACKET_Y; + ball->yspeed = -ball->yspeed; + DBG("Ball: floor\n"); + }else if(Y == RACKET_Y){ // check the racket + DBG("check the racket\n"); + int w2 = i2fixed(racketW) / 2; + if(xnew >= racketX - w2 && xnew <= racketX + w2){ // got the racket + int absrs = (oldracketspeed > 0) ? oldracketspeed : -oldracketspeed; + ynew = i2fixed(RACKET_Y) - DY; Y = RACKET_Y - 1; + ball->xspeed += oldracketspeed / XSPDFRAC; + if(ball->xspeed > BALL_MAX_X_SPEED) ball->xspeed = BALL_MAX_X_SPEED; + else if(ball->xspeed < -BALL_MAX_X_SPEED) ball->xspeed = -BALL_MAX_X_SPEED; + if(absrs){ + ball->yspeed += absrs/YSPDFRAC; + if(ball->yspeed > BALL_MAX_Y_SPEED) ball->yspeed = BALL_MAX_Y_SPEED; + }/* else{ + --ball->yspeed; + if(ball->yspeed < BALL_MIN_Y_SPEED) ball->yspeed = BALL_MIN_Y_SPEED; + }*/ + DBG("Ball: got racket, racketspeed="); DBGU(absrs); + DBG(" ballspeed: "); DBGU(ball->xspeed); DBG(", "); DBGU(ball->yspeed); NL(); + ball->yspeed = -ball->yspeed; // change ball direction + } + }else if(Y < FIELD_ACTIVE){ // ball is in brick zone, check hits + int idx = (FIELD_WIDTH * Y + X) / BRICK_WIDTH; + uint8_t cur = currentLevel[idx]; + if(cur){ + DBG("brick y="); DBGU(Y); DBG(", x="); DBGU(X); DBG(", idx="); DBGU(idx); DBG(", cur="); DBGU(cur); NL(); + /* + * Ball hits the brick, process this + * TODO: DONT FORGET ABOUT BONUSES!!! + */ + if(cur != BRICK_WALL){ // brick can be broken + // delete brick + DBG("Now delete ["); DBGU(idx); DBG("]="); DBGU(currentLevel[idx]); NL(); + currentLevel[idx] = 0; + //showcur(); + int sX = X/BRICK_WIDTH; sX *= BRICK_WIDTH; sX += X0; + DBG("sx="); DBGU(sX); NL(); + DrawPix(sX+0, Y, BACKGROUND_COLOR); + DrawPix(sX+1, Y, BACKGROUND_COLOR); + DrawPix(sX+2, Y, BACKGROUND_COLOR); + /* process bonuses here */ + score += (cur & COLOR_MASK) + 1; + if(--Nbricks == 0){ + if(++curLevNo >= LEVELS_AMOUNT){ + DBG("No more levels, you win!\n"); + return 0; // no more levels + } + // init next level + ClearScreen(); + draw_level(); + startlevl(); + return 1; + } + showText(); + } + // check from which side the ball came and change direction + if(Xo != X && Yo == Y) ball->xspeed = -ball->xspeed; + else if(Yo != Y && Xo == X) ball->yspeed = -ball->yspeed; + else{ball->xspeed = -ball->xspeed; ball->yspeed = -ball->yspeed; } + X = Xo; Y = Yo; xnew = ball->x; ynew = ball->y; + } + } + ball->x = xnew; ball->y = ynew; + DrawPix(X0 + X, Y, BALL_COLOR); + //DBG("Draw ball @ "); DBGU(X); DBG(", "); DBGU(Y); NL(); + return 1; +} + +static void fire_a_balls(){ // run sticked balls + for(int i = 0; i < NBALLSMAX; ++i){ + if(balls[i].flags & STICKY_BALL){ // this ball is alive + DBG("run a ball, racket speed = "); DBGU(oldracketspeed); NL(); + balls[i].flags &= ~STICKY_BALL; + balls[i].xspeed = oldracketspeed / XSPDFRAC; + int absrs = (oldracketspeed > 0) ? oldracketspeed : -oldracketspeed; + balls[i].yspeed = -BALL_NORMAL_SPEED - absrs/YSPDFRAC; + if(balls[i].yspeed < -BALL_MAX_Y_SPEED) balls[i].yspeed = -BALL_MAX_Y_SPEED; + if(balls[i].xspeed > BALL_MAX_X_SPEED) balls[i].xspeed = BALL_MAX_X_SPEED; + else if(balls[i].xspeed < -BALL_MAX_X_SPEED) balls[i].xspeed = -BALL_MAX_X_SPEED; + } + } +} + +void arkanoid_init(){ + setBGcolor(BACKGROUND_COLOR); + setFGcolor(FOREGROUND_COLOR); + choose_font(FONTN8); + ClearScreen(); + ScreenON(); + score = 0; + Nlives = LIVES_AT_START; + curLevNo = 0; + draw_level(); + startlevl(); +} + +/* + * BUTTONS: + * left/right - move racket + * set - fire a ball + * up - pause/resume + * menu - go to menu + */ + +int arkanoid_process(){ + static uint32_t Tracketspeed = 0; + if(Tms == Tlast) return 1; + int dt = (int)(Tms - Tlast); + Tlast = Tms; + // check buttons + keyevent evt; +#define PRESS(x) (keystate(x, &evt) && evt == EVT_PRESS) +#define HOLD(x) (keyevt(x) == EVT_HOLD) +#define RELEASE(x) (keyevt(x) == EVT_RELEASE) + if(PRESS(KEY_U)){ // UP - pause + if(paused){ + DBG("----> Arkanoid resume\n"); + paused = 0; + showText(); + }else{ + DBG("----> Arkanoid paused\n"); + paused = 1; + showcur(); + showText(); + } + } + if(paused){ + clear_events(); + return 1; + } + uint8_t released = 0; + if(PRESS(KEY_L)) racketspeed = -FIXPOINT; // single move @1pt + else if(HOLD(KEY_L)) racketspeed += -D_RACKET_SPEED; + else released = 1; + if(PRESS(KEY_R)) racketspeed = FIXPOINT; // single move @1pt + else if(HOLD(KEY_R)) racketspeed += D_RACKET_SPEED; + else ++released; + if(2 == released){ + if(racketspeed){ + Tracketspeed = Tms; + racketspeed = 0; + DBG("Racket stops @"); DBGU(oldracketspeed); NL(); + }else{ + if(Tms - Tracketspeed > KEEPXSPEEDTIME){ +#ifdef EBUG + if(oldracketspeed) USB_send("oldracspd=0\n"); +#endif + oldracketspeed = 0; + } + } + } + if(PRESS(KEY_S)){ + fire_a_balls(); + } + if(keystate(KEY_M, &evt) && evt == EVT_HOLD){ // break the game + return 0; + } +#undef PRESS +#undef HOLD + process_racket(dt); + for(int i = 0; i < NBALLSMAX; ++i){ + if(balls[i].flags & ACTIVE_BALL){ // this ball is alive + if(!process_ball(&balls[i], dt)){ // ball falls out + balls[i].flags = 0; + --Nballs; + DBG("--balls: "); DBGU(Nballs); NL(); + } + } + } + if(Nballs == 0){ // --lives + DBG("Ball falled\n"); + if(--Nlives == 0){ + DBG("no more lives\n"); + return 0; // game over + } + draw_racket(fixed2i(racketX), BACKGROUND_COLOR); // clear racket + startlevl(); + } + return 1; +} diff --git a/F1-nolib/Tetris/arkanoid.h b/F1-nolib/Tetris/arkanoid.h new file mode 100644 index 0000000..da9e4a8 --- /dev/null +++ b/F1-nolib/Tetris/arkanoid.h @@ -0,0 +1,25 @@ +/* + * This file is part of the TETRIS project. + * Copyright 2021 Edward V. Emelianov . + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#ifndef ARKANOID_H__ +#define ARKANOID_H__ + +void arkanoid_init(); +int arkanoid_process(); + +#endif // ARKANOID_H__ diff --git a/F1-nolib/Tetris/buttons.c b/F1-nolib/Tetris/buttons.c index 20e267b..7d38a55 100644 --- a/F1-nolib/Tetris/buttons.c +++ b/F1-nolib/Tetris/buttons.c @@ -26,7 +26,7 @@ uint32_t lastUnsleep = 0; // threshold in ms for press/hold #define PRESSTHRESHOLD (9) -#define HOLDTHRESHOLD (249) +#define HOLDTHRESHOLD (199) typedef struct{ keycode code; // key code diff --git a/F1-nolib/Tetris/fontNumb8.h b/F1-nolib/Tetris/fontNumb8.h index df748d2..88d0373 100644 --- a/F1-nolib/Tetris/fontNumb8.h +++ b/F1-nolib/Tetris/fontNumb8.h @@ -19,8 +19,8 @@ // this file should be included JUST ONCE! // only in fonts.c -#define FONTNUMB8BYTES 11 -#define FONTNUMB8HEIGHT 11 +#define FONTNUMB8BYTES 10 +#define FONTNUMB8HEIGHT 10 #define FONTNUMB8BASELINE 2 @@ -92,7 +92,6 @@ const uint8_t fontNumb8_table[] = { ________, ________, ________, - ________, // 1 0x20 - ' ' 4, ________, @@ -105,10 +104,8 @@ const uint8_t fontNumb8_table[] = { ________, ________, ________, - ________, // 2 0x30 - '0' 6, - ________, _XXX____, XX_XX___, X___X___, @@ -121,7 +118,6 @@ const uint8_t fontNumb8_table[] = { ________, // 3 0x31 - '1' 6, - ________, _XX_____, X_X_____, __X_____, @@ -134,7 +130,6 @@ const uint8_t fontNumb8_table[] = { ________, // 4 0x32 - '2' 6, - ________, _XXX____, X___X___, ____X___, @@ -147,7 +142,6 @@ const uint8_t fontNumb8_table[] = { ________, // 5 0x33 - '3' 6, - ________, XXXX____, ____X___, ____X___, @@ -160,7 +154,6 @@ const uint8_t fontNumb8_table[] = { ________, // 6 0x34 - '4' 7, - ________, ___XX___, ___XX___, __X_X___, @@ -173,7 +166,6 @@ const uint8_t fontNumb8_table[] = { ________, // 7 0x35 - '5' 5, - ________, XXXX____, X_______, X_______, @@ -186,7 +178,6 @@ const uint8_t fontNumb8_table[] = { ________, // 8 0x36 - '6' 6, - ________, _XXX____, X___X___, X_______, @@ -199,7 +190,6 @@ const uint8_t fontNumb8_table[] = { ________, // 9 0x37 - '7' 5, - ________, XXXX____, ___X____, __X_____, @@ -212,7 +202,6 @@ const uint8_t fontNumb8_table[] = { ________, // 10 0x38 - '8' 6, - ________, _XXX____, X___X___, X___X___, @@ -225,7 +214,6 @@ const uint8_t fontNumb8_table[] = { ________, // 11 0x39 - '9' 6, - ________, _XXX____, X___X___, X___X___, @@ -238,7 +226,6 @@ const uint8_t fontNumb8_table[] = { ________, // 12 0x41 - 'A' 6, - ________, __X_____, _X_X____, _X_X____, @@ -251,7 +238,6 @@ const uint8_t fontNumb8_table[] = { ________, // 13 0x4f - 'O' 7, - ________, __XX____, _X__X___, X____X__, @@ -264,7 +250,6 @@ const uint8_t fontNumb8_table[] = { ________, // 14 0x21 - '!' 2, - ________, X_______, X_______, X_______, @@ -278,7 +263,6 @@ const uint8_t fontNumb8_table[] = { // 15 0x22 - '"' 4, ________, - ________, X_X_____, X_X_____, X_X_____, @@ -291,7 +275,6 @@ const uint8_t fontNumb8_table[] = { // 16 0x23 - '#' 7, ________, - ________, ____X_X_, ___X_X__, XXXXXXX_, @@ -303,7 +286,6 @@ const uint8_t fontNumb8_table[] = { ________, // 17 0x24 - '$' 6, - ________, __X_____, _XXX____, X_X_X___, @@ -317,7 +299,6 @@ const uint8_t fontNumb8_table[] = { // 18 0x25 - '%' 8, ________, - ________, _XX___X_, X__X_X__, _XX_X___, @@ -330,7 +311,6 @@ const uint8_t fontNumb8_table[] = { // 19 0x26 - '&' 7, ________, - ________, _X______, X_X_____, X_X_____, @@ -343,7 +323,6 @@ const uint8_t fontNumb8_table[] = { // 20 0x27 - ''' 2, ________, - ________, X_______, X_______, X_______, @@ -355,7 +334,6 @@ const uint8_t fontNumb8_table[] = { ________, // 21 0x28 - '(' 4, - ________, __XX____, _X______, X_______, @@ -368,7 +346,6 @@ const uint8_t fontNumb8_table[] = { ________, // 22 0x29 - ')' 4, - ________, XX______, __X_____, ___X____, @@ -383,7 +360,6 @@ const uint8_t fontNumb8_table[] = { 6, ________, ________, - ________, __X_____, XXXXX___, _XXX____, @@ -396,7 +372,6 @@ const uint8_t fontNumb8_table[] = { 6, ________, ________, - ________, __X_____, __X_____, XXXXX___, @@ -413,7 +388,6 @@ const uint8_t fontNumb8_table[] = { ________, ________, ________, - ________, XX______, _X______, X_______, @@ -424,7 +398,6 @@ const uint8_t fontNumb8_table[] = { ________, ________, ________, - ________, XXXXX___, ________, ________, @@ -439,14 +412,12 @@ const uint8_t fontNumb8_table[] = { ________, ________, ________, - ________, XX______, XX______, ________, ________, // 28 0x2F - '/' 6, - ________, ____X___, ___X____, ___X____, @@ -461,7 +432,6 @@ const uint8_t fontNumb8_table[] = { 3, ________, ________, - ________, XX______, XX______, ________, @@ -475,7 +445,6 @@ const uint8_t fontNumb8_table[] = { ________, ________, ________, - ________, XX______, XX______, ________, @@ -487,7 +456,6 @@ const uint8_t fontNumb8_table[] = { 4, ________, ________, - ________, __X_____, _X______, X_______, @@ -501,7 +469,6 @@ const uint8_t fontNumb8_table[] = { ________, ________, ________, - ________, XXXX____, ________, XXXX____, @@ -513,7 +480,6 @@ const uint8_t fontNumb8_table[] = { 4, ________, ________, - ________, X_______, _X______, __X_____, @@ -524,7 +490,6 @@ const uint8_t fontNumb8_table[] = { ________, // 34 0x3F - '?' 5, - ________, _XX_____, X__X____, ___X____, @@ -537,7 +502,6 @@ const uint8_t fontNumb8_table[] = { ________, // 35 0x40 - '@' 8, - ________, ___XXX__, __X__X__, _X_XXXX_, @@ -550,7 +514,6 @@ const uint8_t fontNumb8_table[] = { ________, // 36 0x42 - 'B' 6, - ________, XXX_____, X__X____, X_X_____, @@ -563,7 +526,6 @@ const uint8_t fontNumb8_table[] = { ________, // 37 0x43 - 'C' 7, - ________, __XXX___, _X___X__, X_______, @@ -576,7 +538,6 @@ const uint8_t fontNumb8_table[] = { ________, // 38 0x44 - 'D' 6, - ________, XXX_____, X__X____, X___X___, @@ -589,7 +550,6 @@ const uint8_t fontNumb8_table[] = { ________, // 39 0x45 - 'E' 6, - ________, XXXXX___, X_______, X_______, @@ -602,7 +562,6 @@ const uint8_t fontNumb8_table[] = { ________, // 40 0x46 - 'F' 6, - ________, XXXXX___, X___X___, X_______, @@ -615,7 +574,6 @@ const uint8_t fontNumb8_table[] = { ________, // 41 0x47 - 'G' 7, - ________, __XXX___, _X___X__, X_______, @@ -628,7 +586,6 @@ const uint8_t fontNumb8_table[] = { ________, // 42 0x48 - 'H' 6, - ________, X___X___, X___X___, X___X___, @@ -641,7 +598,6 @@ const uint8_t fontNumb8_table[] = { ________, // 43 0x49 - 'I' 4, - ________, XXX_____, _X______, _X______, @@ -654,7 +610,6 @@ const uint8_t fontNumb8_table[] = { ________, // 44 0x4A - 'J' 5, - ________, XXXX____, X__X____, ___X____, @@ -667,7 +622,6 @@ const uint8_t fontNumb8_table[] = { ________, // 45 0x4B - 'K' 6, - ________, X___X___, X__X____, X_X_____, @@ -680,7 +634,6 @@ const uint8_t fontNumb8_table[] = { ________, // 46 0x4C - 'L' 6, - ________, X_______, X_______, X_______, @@ -693,7 +646,6 @@ const uint8_t fontNumb8_table[] = { ________, // 47 0x4D - 'M' 8, - ________, X_____X_, XX___XX_, X_X_X_X_, @@ -706,7 +658,6 @@ const uint8_t fontNumb8_table[] = { ________, // 48 0x4E - 'N' 6, - ________, X___X___, XX__X___, XX__X___, @@ -719,7 +670,6 @@ const uint8_t fontNumb8_table[] = { ________, // 49 0x50 - 'P' 6, - ________, XXXX____, X__XX___, X___X___, @@ -732,7 +682,6 @@ const uint8_t fontNumb8_table[] = { ________, // 50 0x51 - 'Q' 7, - ________, __XX____, _X__X___, X____X__, @@ -745,7 +694,6 @@ const uint8_t fontNumb8_table[] = { ________, // 51 0x52 - 'R' 6, - ________, XXXX____, X__XX___, X___X___, @@ -758,7 +706,6 @@ const uint8_t fontNumb8_table[] = { ________, // 52 0x53 - 'S' 6, - ________, _XX_____, X__X____, X_______, @@ -771,7 +718,6 @@ const uint8_t fontNumb8_table[] = { ________, // 53 0x54 - 'T' 6, - ________, XXXXX___, __X_____, __X_____, @@ -784,7 +730,6 @@ const uint8_t fontNumb8_table[] = { ________, // 54 0x55 - 'U' 7, - ________, X____X__, X____X__, X____X__, @@ -797,7 +742,6 @@ const uint8_t fontNumb8_table[] = { ________, // 55 0x56 - 'V' 6, - ________, X___X___, X___X___, X___X___, @@ -810,7 +754,6 @@ const uint8_t fontNumb8_table[] = { ________, // 56 0x57 - 'W' 8, - ________, X_____X_, X_____X_, X_____X_, @@ -823,7 +766,6 @@ const uint8_t fontNumb8_table[] = { ________, // 57 0x58 - 'X' 6, - ________, X___X___, X___X___, _X_X____, @@ -836,7 +778,6 @@ const uint8_t fontNumb8_table[] = { ________, // 58 0x59 - 'Y' 6, - ________, X___X___, X___X___, X___X___, @@ -849,7 +790,6 @@ const uint8_t fontNumb8_table[] = { ________, // 59 0x5A - 'Z' 6, - ________, XXXXX___, ____X___, ___X____, diff --git a/F1-nolib/Tetris/main.c b/F1-nolib/Tetris/main.c index 1a9a559..0513d46 100644 --- a/F1-nolib/Tetris/main.c +++ b/F1-nolib/Tetris/main.c @@ -17,6 +17,7 @@ */ #include "adcrandom.h" +#include "arkanoid.h" #include "balls.h" #include "buttons.h" #include "fonts.h" @@ -39,6 +40,7 @@ enum{ STATE_MENU, STATE_SNAKE, STATE_TETRIS, + STATE_ARKANOID, STATE_SLEEP, STATE_GAMEOVER } curstate = STATE_SLEEP; @@ -97,6 +99,10 @@ static void process_menu(){ tetris_init(); curstate = STATE_TETRIS; break; + case MENU_ARKANOID: + USB_send("Select 'Arkanoid'\n"); + arkanoid_init(); + curstate = STATE_ARKANOID; default: break; } @@ -158,6 +164,12 @@ int main(void){ curstate = STATE_GAMEOVER; } break; + case STATE_ARKANOID: + if(!arkanoid_process()){ + show_gameover(); + curstate = STATE_GAMEOVER; + } + break; case STATE_GAMEOVER: // show gameover screen if(keystate(KEY_M, &evt) && evt == EVT_RELEASE){ gotomenu(); diff --git a/F1-nolib/Tetris/menu.c b/F1-nolib/Tetris/menu.c index ee77e42..9fa1b91 100644 --- a/F1-nolib/Tetris/menu.c +++ b/F1-nolib/Tetris/menu.c @@ -33,10 +33,11 @@ #define midY (SCREEN_HH - 1) static const char* items[] = { - [MENU_SLEEP] = " SLEEP ", - [MENU_BALLS] = " BALLS ", - [MENU_SNAKE] = " SNAKE ", - [MENU_TETRIS] = " TETRIS " + [MENU_SLEEP] = " SLEEP ", + [MENU_BALLS] = " BALLS ", + [MENU_SNAKE] = " SNAKE ", + [MENU_TETRIS] = " TETRIS ", + [MENU_ARKANOID] = " ARKANOID ", }; static menuitem curitem = MENU_SLEEP; diff --git a/F1-nolib/Tetris/menu.h b/F1-nolib/Tetris/menu.h index 8ac68a9..1ed39f0 100644 --- a/F1-nolib/Tetris/menu.h +++ b/F1-nolib/Tetris/menu.h @@ -26,6 +26,7 @@ typedef enum{ MENU_BALLS, MENU_SNAKE, MENU_TETRIS, + MENU_ARKANOID, MENU_ITEMS, MENU_NONE } menuitem; diff --git a/F1-nolib/Tetris/screen.c b/F1-nolib/Tetris/screen.c index d770ceb..6ff022e 100644 --- a/F1-nolib/Tetris/screen.c +++ b/F1-nolib/Tetris/screen.c @@ -85,7 +85,7 @@ uint8_t DrawCharAt(int16_t X, int16_t Y, uint8_t Char){ const uint8_t *curchar = font_char(Char); if(!curchar) return 0; // now change Y coordinate to left upper corner of font - Y += 1 - curfont->height + curfont->baseline; + Y += curfont->baseline - curfont->height + 1; // height and width of letter in pixels uint8_t h = curfont->height, w = *curchar++; // now curchar is pointer to bits array uint8_t lw = curfont->bytes / h; // width of letter in bytes diff --git a/F1-nolib/Tetris/screen.h b/F1-nolib/Tetris/screen.h index 87edeea..b3a3cf5 100644 --- a/F1-nolib/Tetris/screen.h +++ b/F1-nolib/Tetris/screen.h @@ -49,6 +49,8 @@ typedef enum{ // screen states // colors #define COLOR_BLACK (0b00000000) #define COLOR_WHITE (0b11111111) +#define COLOR_GRAY (0b01101101) +#define COLOR_LGRAY (0b10110110) #define COLOR_BLUE (0b00000011) #define COLOR_LBLUE (0b00000001) #define COLOR_GREEN (0b00011100) @@ -58,6 +60,7 @@ typedef enum{ // screen states #define COLOR_CYAN (0b00011111) #define COLOR_LCYAN (0b00000101) #define COLOR_PURPLE (0b11100011) +#define COLOR_LPURPLE (0b01100001) #define COLOR_YELLOW (0b11111100) #define COLOR_LYELLOW (0b00100100)