#include <Adafruit_GFX.h>
#include <Adafruit_ILI9341.h>
#include <pgmspace.h>
#define TFT_CS D0
#define TFT_RST D2
#define TFT_DC D1
Adafruit_ILI9341 tft = Adafruit_ILI9341(TFT_CS, TFT_DC, TFT_RST);
#define pin_up A5
#define pin_down A3
uint8_t y = 128;
/* object types */
struct _st_ot_struct {
uint8_t missle_mask;
uint8_t hit_mask;
uint8_t points;
uint8_t draw_fn;
uint8_t move_fn;
uint8_t destroy_fn;
uint8_t is_hit_fn;
uint8_t fire_fn;
};
typedef struct _st_ot_struct st_ot;
struct _st_obj_struct {
uint8_t ot;
int8_t tmp;
int16_t x, y;
int8_t x0, y0, x1, y1;
};
typedef struct _st_obj_struct st_obj;
#define ST_FP 4
#define ST_DRAW_NONE 0
#define ST_DRAW_BBOX 1
#define ST_DRAW_TRASH1 2
#define ST_DRAW_PLAYER1 3
#define ST_DRAW_TRASH2 4
#define ST_DRAW_PLAYER2 5
#define ST_DRAW_PLAYER3 6
#define ST_DRAW_GADGET 7
#define ST_DRAW_BACKSLASH 8
#define ST_DRAW_SLASH 9
#define ST_DRAW_BIG_TRASH 10
#define ST_MOVE_NONE 0
#define ST_MOVE_X_SLOW 1
#define ST_MOVE_PX_NORMAL 2
#define ST_MOVE_PX_FAST 3
#define ST_MOVE_PLAYER 4
#define ST_MOVE_PY 5
#define ST_MOVE_NY 6
#define ST_MOVE_IMPLODE 7
#define ST_MOVE_X_FAST 8
#define ST_MOVE_WALL 9
#define ST_MOVE_NXPY 10
#define ST_MOVE_NXNY 11
#define ST_IS_HIT_NONE 0
#define ST_IS_HIT_BBOX 1
#define ST_IS_HIT_WALL 2
#define ST_DESTROY_NONE 0
#define ST_DESTROY_DISAPPEAR 1
#define ST_DESTROY_TO_DUST 2
#define ST_DESTROY_GADGET 3
#define ST_DESTROY_PLAYER 4
#define ST_DESTROY_PLAYER_GADGETS 5
#define ST_DESTROY_BIG_TRASH 6
#define ST_FIRE_NONE 0
#define ST_FIRE_PLAYER1 1
#define ST_FIRE_PLAYER2 2
#define ST_FIRE_PLAYER3 3
#define ST_OT_WALL_SOLID 1
#define ST_OT_BIG_TRASH 2
#define ST_OT_MISSLE 3
#define ST_OT_TRASH1 4
#define ST_OT_PLAYER 5
#define ST_OT_DUST_PY 6
#define ST_OT_DUST_NY 7
#define ST_OT_TRASH_IMPLODE 8
#define ST_OT_TRASH2 9
#define ST_OT_PLAYER2 10
#define ST_OT_PLAYER3 11
#define ST_OT_GADGET 12
#define ST_OT_GADGET_IMPLODE 13
#define ST_OT_DUST_NXPY 14
#define ST_OT_DUST_NXNY 15
st_obj st_objects[60];
uint8_t st_player_pos;
#define ST_POINTS_PER_LEVEL 25
uint16_t st_player_points;
uint16_t st_player_points_delayed;
uint16_t st_highscore = 0;
#define ST_STATE_PREPARE 0
#define ST_STATE_IPREPARE 1
#define ST_STATE_GAME 2
#define ST_STATE_END 3
#define ST_STATE_IEND 4
uint8_t st_state = ST_STATE_PREPARE;
uint8_t st_difficulty = 1;
#define ST_DIFF_VIS_LEN 30
#define ST_DIFF_FP 5
uint16_t st_to_diff_cnt = 0;
const st_ot st_object_types[] PROGMEM = {
{ 0, 0, 0, ST_DRAW_NONE, ST_MOVE_NONE, ST_DESTROY_DISAPPEAR, ST_IS_HIT_NONE, ST_FIRE_NONE },
{ 2, 1, 30, ST_DRAW_BBOX, ST_MOVE_WALL, ST_DESTROY_DISAPPEAR, ST_IS_HIT_WALL, ST_FIRE_NONE },
{ 2, 1, 0, ST_DRAW_BIG_TRASH, ST_MOVE_X_SLOW, ST_DESTROY_BIG_TRASH, ST_IS_HIT_BBOX, ST_FIRE_NONE },
{ 1, 0, 0, ST_DRAW_BBOX, ST_MOVE_PX_FAST, ST_DESTROY_DISAPPEAR, ST_IS_HIT_NONE, ST_FIRE_NONE },
{ 2, 1, 0, ST_DRAW_TRASH1, ST_MOVE_X_SLOW, ST_DESTROY_TO_DUST, ST_IS_HIT_BBOX, ST_FIRE_NONE },
{ 0, 2, 0, ST_DRAW_PLAYER1, ST_MOVE_PLAYER, ST_DESTROY_PLAYER, ST_IS_HIT_BBOX, ST_FIRE_PLAYER1 },
{ 0, 0, 0, ST_DRAW_BBOX, ST_MOVE_PY, ST_DESTROY_NONE, ST_IS_HIT_NONE, ST_FIRE_NONE },
{ 0, 0, 0, ST_DRAW_BBOX, ST_MOVE_NY, ST_DESTROY_NONE, ST_IS_HIT_NONE, ST_FIRE_NONE },
{ 0, 0, 5, ST_DRAW_TRASH1, ST_MOVE_IMPLODE, ST_DESTROY_NONE, ST_IS_HIT_NONE, ST_FIRE_NONE },
{ 2, 1, 0, ST_DRAW_TRASH2, ST_MOVE_X_SLOW, ST_DESTROY_TO_DUST, ST_IS_HIT_BBOX, ST_FIRE_NONE },
{ 0, 2, 0, ST_DRAW_PLAYER2, ST_MOVE_PLAYER, ST_DESTROY_PLAYER_GADGETS, ST_IS_HIT_BBOX, ST_FIRE_PLAYER2 },
{ 0, 2, 0, ST_DRAW_PLAYER3, ST_MOVE_PLAYER, ST_DESTROY_PLAYER_GADGETS, ST_IS_HIT_BBOX, ST_FIRE_PLAYER3 },
{ 0, 1, 0, ST_DRAW_GADGET, ST_MOVE_X_FAST, ST_DESTROY_GADGET, ST_IS_HIT_BBOX, ST_FIRE_NONE },
{ 0, 0, 20, ST_DRAW_GADGET, ST_MOVE_IMPLODE, ST_DESTROY_NONE, ST_IS_HIT_NONE, ST_FIRE_NONE },
{ 0, 0, 0, ST_DRAW_BACKSLASH, ST_MOVE_NXPY, ST_DESTROY_NONE, ST_IS_HIT_NONE, ST_FIRE_NONE },
{ 0, 0, 0, ST_DRAW_SLASH, ST_MOVE_NXNY, ST_DESTROY_NONE, ST_IS_HIT_NONE, ST_FIRE_NONE },
};
const uint8_t st_bitmap_player1[] PROGMEM = {
0x060,
0x0f8,
0x07e,
0x0f8,
0x060
};
const uint8_t st_bitmap_player2[] PROGMEM = {
0x060,
0x078,
0x060,
0x0e0,
0x0f8,
0x07e,
0x0f8,
0x060
};
const uint8_t st_bitmap_player3[] PROGMEM = {
0x060,
0x078,
0x060,
0x0e0,
0x0f8,
0x07e,
0x0f8,
0x0e0,
0x060,
0x078,
0x060
};
const uint8_t st_bitmap_trash_5x5_1[] PROGMEM = {
0x070,
0x0f0,
0x0f8,
0x078,
0x030,
};
const uint8_t st_bitmap_trash_5x5_2[] PROGMEM = {
0x030,
0x0f8,
0x0f8,
0x0f0,
0x070,
};
const uint8_t st_bitmap_trash_7x7[] PROGMEM = {
0x038,
0x07c,
0x0fc,
0x0fe,
0x0fe,
0x07e,
0x078,
};
const uint8_t st_bitmap_gadget[] PROGMEM = {
0x070,
0x0d8,
0x088,
0x0d8,
0x070,
};
char st_itoa_buf[12];
char *st_itoa(unsigned long v) {
volatile unsigned char i = 11;
st_itoa_buf[11] = '\0';
while( i > 0) {
i--;
st_itoa_buf[i] = (v % 10)+'0';
v /= 10;
if ( v == 0 )
break;
}
return st_itoa_buf+i;
}
uint8_t st_rnd(void) {
return rand();
}
static st_obj *st_GetObj(uint8_t objnr) {
return st_objects+objnr;
}
uint8_t st_GetMissleMask(uint8_t objnr) {
st_obj *o = st_GetObj(objnr);
return pgm_read_byte(&(st_object_types[o->ot].missle_mask));
}
uint8_t st_GetHitMask(uint8_t objnr) {
st_obj *o = st_GetObj(objnr);
return pgm_read_byte(&(st_object_types[o->ot].hit_mask));
}
int8_t st_FindObj(uint8_t ot) {
int8_t i;
for( i = 0; i < 60; i++ ) {
if ( st_objects[i].ot == ot )
return i;
}
return -1;
}
void st_ClrObjs(void) {
int8_t i;
for( i = 0; i < 60; i++ )
st_objects[i].ot = 0;
}
int8_t st_NewObj(void) {
int8_t i;
for( i = 0; i < 60; i++ ) {
if ( st_objects[i].ot == 0 )
return i;
}
return -1;
}
uint8_t st_CntObj(uint8_t ot) {
uint8_t i;
uint8_t cnt = 0;
for( i = 0; i < 60; i++ ) {
if ( st_objects[i].ot == ot )
cnt++;
}
return cnt;
}
uint8_t st_px_x, st_px_y;
uint8_t st_CalcXY(st_obj *o) {
st_px_y = o->y>>ST_FP;
st_px_x = o->x>>ST_FP;
return st_px_x;
}
void st_SetXY(st_obj *o, uint8_t x, uint8_t y) {
o->x = ((int16_t)x) << ST_FP;
o->y = ((int16_t)y) << ST_FP;
}
int16_t st_bbox_x0, st_bbox_y0, st_bbox_x1, st_bbox_y1;
void st_CalcBBOX(uint8_t objnr) {
st_obj *o = st_GetObj(objnr);
st_bbox_x0 = (uint16_t)(o->x>>ST_FP);
st_bbox_x1 = st_bbox_x0;
st_bbox_x0 += o->x0;
st_bbox_x1 += o->x1;
st_bbox_y0 = (uint16_t)(o->y>>ST_FP);
st_bbox_y1 = st_bbox_y0;
st_bbox_y0 += o->y0;
st_bbox_y1 += o->y1;
}
uint8_t st_cbbox_x0, st_cbbox_y0, st_cbbox_x1, st_cbbox_y1;
uint8_t st_ClipBBOX(void) {
if ( st_bbox_x0 >= 240 )
return 0;
if ( st_bbox_x0 >= 0 )
st_cbbox_x0 = (uint16_t)st_bbox_x0;
else
st_cbbox_x0 = 0;
if ( st_bbox_x1 < 0 )
return 0;
if ( st_bbox_x1 < 240 )
st_cbbox_x1 = (uint16_t)st_bbox_x1;
else
st_cbbox_x1 = 240-1;
if ( st_bbox_y0 >= 320 )
return 0;
if ( st_bbox_y0 >= 0 )
st_cbbox_y0 = (uint16_t)st_bbox_y0;
else
st_cbbox_y0 = 0;
if ( st_bbox_y1 < 0 )
return 0;
if ( st_bbox_y1 < 320 )
st_cbbox_y1 = (uint16_t)st_bbox_y1;
else
st_cbbox_y1 = 320-1;
return 1;
}
uint8_t st_IsOut(uint8_t objnr) {
st_CalcBBOX(objnr);
if ( st_bbox_x0 >= 240 )
return 1;
if ( st_bbox_x1 < 0 )
return 1;
if ( st_bbox_y0 >= 320 )
return 1;
if ( st_bbox_y1 < 0 )
return 1;
return 0;
}
void st_Disappear(uint8_t objnr) {
st_obj *o = st_GetObj(objnr);
st_player_points += pgm_read_byte(&(st_object_types[o->ot].points));
o->ot = 0;
}
void st_Move(uint8_t objnr) {
st_obj *o = st_GetObj(objnr);
switch(pgm_read_byte(&(st_object_types[o->ot].move_fn))) {
case ST_MOVE_NONE:
break;
case ST_MOVE_X_SLOW:
o->x -= (1<<ST_FP)/8;
o->x -= st_difficulty;
o->y += (int16_t)o->tmp;
if ( o->y >= ((320-1) << ST_FP) || o->y <= 0 )
o->tmp = - o->tmp;
break;
case ST_MOVE_X_FAST:
o->x -= (1<<ST_FP)/2;
o->y += (int16_t)o->tmp;
if ( o->y >= ((320-1) << ST_FP) || o->y <= 0 )
o->tmp = - o->tmp;
break;
case ST_MOVE_PX_NORMAL:
o->x += (1<<ST_FP)/4;
break;
case ST_MOVE_PX_FAST:
o->x += (1<<ST_FP);
break;
case ST_MOVE_PLAYER:
o->y = st_player_pos<<ST_FP;
break;
case ST_MOVE_PY:
o->y += 3*ST_FP;
break;
case ST_MOVE_NY:
o->y -= 3*ST_FP;
break;
case ST_MOVE_NXPY:
o->y += 3*ST_FP;
o->x -= 3*ST_FP;
break;
case ST_MOVE_NXNY:
o->y -= 3*ST_FP;
o->x -= 3*ST_FP;
break;
case ST_MOVE_IMPLODE:
o->tmp++;
if ( (o->tmp & 0x03) == 0 ) {
if ( o->x0 != o->x1 )
o->x0++;
else
st_Disappear(objnr);
}
break;
case ST_MOVE_WALL:
o->x -= 1;
o->x -= (st_difficulty>>1);
break;
}
}
void st_DrawBBOX(uint8_t objnr) {
st_CalcBBOX(objnr);
if ( st_ClipBBOX() == 0 )
return;
tft.drawRect(st_cbbox_x0, st_cbbox_y0, st_cbbox_x1-st_cbbox_x0+1, st_cbbox_y1-st_cbbox_y0+1, ILI9341_WHITE);
}
void st_DrawBitmap(uint8_t objnr, const uint8_t * bm, uint8_t w, uint8_t h) {
st_CalcBBOX(objnr);
tft.drawBitmap(st_bbox_x0, st_bbox_y1, bm, w, h, ILI9341_WHITE);
}
void st_DrawObj(uint8_t objnr) {
st_obj *o = st_GetObj(objnr);
switch(pgm_read_byte(&(st_object_types[o->ot].draw_fn))) {
case ST_DRAW_NONE:
break;
case ST_DRAW_BBOX:
st_DrawBBOX(objnr);
break;
case ST_DRAW_TRASH1:
st_DrawBitmap(objnr, st_bitmap_trash_5x5_1,o->x1-o->x0+1, 5);
break;
case ST_DRAW_TRASH2:
st_DrawBitmap(objnr, st_bitmap_trash_5x5_2,o->x1-o->x0+1, 5);
break;
case ST_DRAW_BIG_TRASH:
st_DrawBitmap(objnr, st_bitmap_trash_7x7,o->x1-o->x0+1, 7);
break;
case ST_DRAW_PLAYER1:
st_DrawBitmap(objnr, st_bitmap_player1,7,5);
break;
case ST_DRAW_PLAYER2:
st_DrawBitmap(objnr, st_bitmap_player2,7,8);
break;
case ST_DRAW_PLAYER3:
st_DrawBitmap(objnr, st_bitmap_player3,7,11);
break;
case ST_DRAW_GADGET:
st_DrawBitmap(objnr, st_bitmap_gadget,5,5);
break;
case ST_DRAW_BACKSLASH:
{
uint8_t x;
uint8_t y;
x = st_CalcXY(o);
y = st_px_y;
tft.drawPixel(x, y, ILI9341_WHITE);
x++; y--;
tft.drawPixel(x, y, ILI9341_WHITE);
x++; y--;
tft.drawPixel(x, y, ILI9341_WHITE);
}
break;
case ST_DRAW_SLASH:
{
uint8_t x;
uint8_t y;
x = st_CalcXY(o);
y = st_px_y;
tft.drawPixel(x, y, ILI9341_WHITE);
x++; y++;
tft.drawPixel(x, y, ILI9341_WHITE);
x++; y++;
tft.drawPixel(x, y, ILI9341_WHITE);
}
break;
}
}
uint8_t st_IsHitBBOX(uint8_t objnr, uint8_t x, uint8_t y) {
st_CalcBBOX(objnr);
if ( st_ClipBBOX() == 0 )
return 0;
if ( x < st_cbbox_x0 )
return 0;
if ( x > st_cbbox_x1 )
return 0;
if ( y < st_cbbox_y0 )
return 0;
if ( y > st_cbbox_y1 )
return 0;
return 1;
}
void st_Destroy(uint8_t objnr) {
int8_t nr;
st_obj *o = st_GetObj(objnr);
switch(pgm_read_byte(&(st_object_types[o->ot].destroy_fn))) {
case ST_DESTROY_NONE:
break;
case ST_DESTROY_DISAPPEAR:
st_Disappear(objnr);
break;
case ST_DESTROY_GADGET:
nr = st_FindObj(ST_OT_PLAYER2);
if ( nr >= 0 )
st_SetupPlayer(nr, ST_OT_PLAYER3);
else {
nr = st_FindObj(ST_OT_PLAYER);
if ( nr >= 0 )
st_SetupPlayer(nr, ST_OT_PLAYER2);
}
st_NewTrashDustAreaArgs(o->x, o->y, ST_OT_DUST_PY);
st_NewTrashDustAreaArgs(o->x, o->y, ST_OT_DUST_NY);
o->ot = ST_OT_GADGET_IMPLODE;
o->tmp = 0;
break;
case ST_DESTROY_TO_DUST:
st_NewTrashDustAreaArgs(o->x, o->y, ST_OT_DUST_PY);
st_NewTrashDustAreaArgs(o->x, o->y, ST_OT_DUST_NY);
o->ot = ST_OT_TRASH_IMPLODE;
o->tmp = 0;
break;
case ST_DESTROY_BIG_TRASH:
st_NewTrashDustAreaArgs(o->x, o->y, ST_OT_DUST_PY);
st_NewTrashDustAreaArgs(o->x, o->y, ST_OT_DUST_NY);
st_InitTrash((o->x>>ST_FP)-1, (o->y>>ST_FP)+3, 2+(st_rnd()&3));
st_InitTrash((o->x>>ST_FP)-2, (o->y>>ST_FP)-3, -2-(st_rnd()&3));
st_Disappear(objnr);
break;
case ST_DESTROY_PLAYER:
st_Disappear(objnr);
st_state = ST_STATE_END;
o->tmp = 0;
break;
case ST_DESTROY_PLAYER_GADGETS:
st_SetupPlayer(objnr, ST_OT_PLAYER);
break;
}
}
uint8_t st_IsHit(uint8_t objnr, uint8_t x, uint8_t y, uint8_t missle_mask) {
uint8_t hit_mask = st_GetHitMask(objnr);
st_obj *o;
if ( (hit_mask & missle_mask) == 0 )
return 0;
o = st_GetObj(objnr);
switch(pgm_read_byte(&(st_object_types[o->ot].is_hit_fn))) {
case ST_IS_HIT_NONE:
break;
case ST_IS_HIT_BBOX:
if ( st_IsHitBBOX(objnr, x, y) != 0 ) {
st_Destroy(objnr);
return 1;
}
break;
case ST_IS_HIT_WALL:
if ( st_IsHitBBOX(objnr, x, y) != 0 ) {
o->x0++;
if ( o->x0 < o->x1 ) {
st_NewTrashDust(x, y, ST_OT_DUST_NXPY);
st_NewTrashDust(x, y, ST_OT_DUST_NXNY);
}
else {
st_Destroy(objnr);
st_NewTrashDust(x, y, ST_OT_DUST_NXPY);
st_NewTrashDust(x, y, ST_OT_DUST_NXNY);
st_NewTrashDust(x, y, ST_OT_DUST_NY);
st_NewTrashDust(x, y, ST_OT_DUST_PY);
}
return 1;
}
break;
}
return 0;
}
uint8_t st_fire_player = 0;
uint8_t st_fire_period = 51;
uint8_t st_manual_fire_delay = 20;
uint8_t st_is_fire_last_value = 0;
void st_FireStep(uint8_t is_auto_fire, uint8_t is_fire) {
if ( is_auto_fire != 0 ) {
st_fire_player++;
if ( st_fire_player >= st_fire_period )
st_fire_player = 0;
}
else {
if ( st_fire_player < st_manual_fire_delay ) {
st_fire_player++;
}
else {
if ( st_is_fire_last_value == 0 )
if ( is_fire != 0 )
st_fire_player = 0;
}
st_is_fire_last_value = is_fire;
}
}
void st_Fire(uint8_t objnr) {
st_obj *o = st_GetObj(objnr);
uint8_t x;
uint8_t y;
switch(pgm_read_byte(&(st_object_types[o->ot].fire_fn))) {
case ST_FIRE_NONE:
break;
case ST_FIRE_PLAYER1:
if ( st_fire_player == 0 ) {
x = st_CalcXY(o);
y = st_px_y;
st_NewPlayerMissle(x , y );
}
break;
case ST_FIRE_PLAYER2:
if ( st_fire_player == 0 ) {
x = st_CalcXY(o);
y = st_px_y;
st_NewPlayerMissle(x , y );
st_NewPlayerMissle(x , y+4 );
}
break;
case ST_FIRE_PLAYER3:
if ( st_fire_player == 0 ) {
x = st_CalcXY(o);
y = st_px_y;
st_NewPlayerMissle(x , y );
st_NewPlayerMissle(x , y+4 );
st_NewPlayerMissle(x , y-4 );
}
break;
}
}
void st_NewGadget(uint8_t x, uint8_t y) {
st_obj *o;
int8_t objnr = st_NewObj();
if ( objnr < 0 )
return;
o = st_GetObj(objnr);
st_SetXY(o, x, y);
o->ot = ST_OT_GADGET;
o->tmp = 8;
o->x0 = -3;
o->x1 = 1;
o->y0 = -2;
o->y1 = 2;
}
void st_InitTrash(uint8_t x, uint8_t y, int8_t dir) {
st_obj *o;
int8_t objnr = st_NewObj();
if ( objnr < 0 )
return;
o = st_GetObj(objnr);
if ( (st_rnd() & 1) == 0 )
o->ot = ST_OT_TRASH1;
else
o->ot = ST_OT_TRASH2;
if ( dir == 0 ) {
o->tmp = 0;
if ( st_rnd() & 1 ) {
if ( st_rnd() & 1 )
o->tmp++;
else
o->tmp--;
}
}
else {
o->tmp = dir;
}
st_SetXY(o, x, y);
o->x0 = -3;
o->x1 = 1;
o->y0 = -2;
o->y1 = 2;
if ( st_difficulty >= 5 ) {
if ( (st_rnd() & 3) == 0 ) {
o->ot = ST_OT_BIG_TRASH;
o->y0--;
o->y1++;
o->x0--;
o->x1++;
}
}
}
void st_NewTrashDust(uint8_t x, uint8_t y, int ot) {
st_obj *o;
int8_t objnr = st_NewObj();
if ( objnr < 0 )
return;
o = st_GetObj(objnr);
o->ot = ot;
st_SetXY(o, x, y);
o->x0 = 0;
o->x1 = 0;
o->y0 = -2;
o->y1 = 2;
}
void st_NewTrashDustAreaArgs(int16_t x, int16_t y, int ot) {
st_NewTrashDust(x>>ST_FP, y>>ST_FP, ot);
}
void st_NewWall(void) {
st_obj *o;
int8_t objnr = st_NewObj();
int8_t h;
if ( objnr < 0 )
return;
o = st_GetObj(objnr);
o->ot = ST_OT_WALL_SOLID;
h = st_rnd();
h &= 63;
h = (int8_t)(((int16_t)h*(int16_t)(320/4))>>6);
h += 320/6;
o->x0 = 0;
o->x1 = 5;
o->x = (240-1)<<ST_FP;
if ( (st_rnd() & 1) == 0 ) {
o->y = (320-1)<<ST_FP;
o->y0 = -h;
o->y1 = 0;
}
else {
o->y = (0)<<ST_FP;
o->y0 = 0;
o->y1 = h;
}
}
void st_NewPlayerMissle(uint8_t x, uint8_t y) {
st_obj *o;
int8_t objnr = st_NewObj();
if ( objnr < 0 )
return;
o = st_GetObj(objnr);
o->ot = ST_OT_MISSLE;
st_SetXY(o, x, y);
o->x0 = -4;
o->x1 = 1;
o->y0 = 0;
o->y1 = 0;
}
void st_SetupPlayer(uint8_t objnr, uint8_t ot) {
st_obj *o = st_GetObj(objnr);
switch(ot) {
case ST_OT_PLAYER:
o->ot = ot;
o->y0 = -2;
o->y1 = 2;
break;
case ST_OT_PLAYER2:
o->ot = ot;
o->y0 = -2;
o->y1 = 5;
break;
case ST_OT_PLAYER3:
o->ot = ot;
o->y0 = -5;
o->y1 = 5;
break;
}
}
void st_NewPlayer(void) {
st_obj *o;
int8_t objnr = st_NewObj();
if ( objnr < 0 )
return;
o = st_GetObj(objnr);
o->x = 6<<ST_FP;
o->y = (320/2)<<ST_FP;
o->x0 = -6;
o->x1 = 0;
st_SetupPlayer(objnr, ST_OT_PLAYER);
}
void st_InitDeltaWall(void) {
uint8_t i;
uint8_t cnt = 0;
uint8_t max_x = 0;
uint8_t max_l;
uint8_t min_dist_for_new = 40;
uint8_t my_difficulty = st_difficulty;
if ( st_difficulty >= 2 ) {
max_l = 240;
max_l -= min_dist_for_new;
if ( my_difficulty > 30 )
my_difficulty = 30;
min_dist_for_new -= my_difficulty;
for( i = 0; i < 60; i++ ) {
if ( st_objects[i].ot == ST_OT_WALL_SOLID ) {
cnt++;
if ( max_x < (st_objects[i].x>>ST_FP) )
max_x = (st_objects[i].x>>ST_FP);
}
}
if ( max_x < max_l ) {
st_NewWall();
}
}
}
void st_InitDeltaTrash(void) {
uint8_t i;
uint8_t cnt = 0;
uint8_t max_x = 0;
uint8_t max_l;
uint8_t upper_trash_limit = 60-7;
uint8_t min_dist_for_new = 20;
uint8_t my_difficulty = st_difficulty;
if ( my_difficulty > 14 )
my_difficulty = 14;
min_dist_for_new -= my_difficulty;
for( i = 0; i < 60; i++ ) {
if ( st_objects[i].ot == ST_OT_TRASH1 || st_objects[i].ot == ST_OT_TRASH2 || st_objects[i].ot == ST_OT_GADGET || st_objects[i].ot == ST_OT_BIG_TRASH ) {
cnt++;
if ( max_x < (st_objects[i].x>>ST_FP) )
max_x = (st_objects[i].x>>ST_FP);
}
}
max_l = 240;
max_l -= min_dist_for_new;
if ( cnt < upper_trash_limit )
if ( max_x < max_l ) {
if ( (st_difficulty >= 3) && ((st_rnd() & 7) == 0) )
st_NewGadget(240-1, rand() & (320-1));
else
st_InitTrash(240-1, rand() & (320-1),0 );
}
}
void st_InitDelta(void) {
st_InitDeltaTrash();
st_InitDeltaWall();
}
void st_DrawInGame(uint8_t fps) {
uint8_t i;
for( i = 0; i < 60; i++ )
st_DrawObj(i);
tft.fillRect(0, 0, 240, 8, ILI9341_BLACK);
tft.drawFastHLine(0, 8, 240, ILI9341_WHITE);
tft.setCursor(0, 0);
tft.setTextColor(ILI9341_WHITE);
tft.setTextSize(1);
tft.print(st_itoa(st_difficulty));
tft.drawFastHLine(10, 10, (st_to_diff_cnt>>ST_DIFF_FP)+1, ILI9341_WHITE);
tft.drawFastVLine(10, 7, 3, ILI9341_WHITE);
tft.drawFastVLine(10+ST_DIFF_VIS_LEN, 7, 3, ILI9341_WHITE);
tft.setCursor(180, 0);
tft.print(st_itoa(st_player_points_delayed));
if ( fps > 0 ) {
tft.setCursor(90, 0);
tft.print("FPS:");
tft.print(st_itoa(fps));
}
}
void st_Draw(uint8_t fps) {
switch(st_state) {
case ST_STATE_PREPARE:
case ST_STATE_IPREPARE:
tft.setCursor(31, 160);
tft.setTextColor(ILI9341_RED);
tft.setTextSize(2);
tft.print("GAMESHINE");
tft.setTextColor(ILI9341_WHITE);
tft.setTextSize(2);
tft.setCursor(18, 145);
tft.print("SPACE TRASH!!!");
tft.drawFastHLine(240-st_to_diff_cnt-10, 160, 11, ILI9341_WHITE);
break;
case ST_STATE_GAME:
st_DrawInGame(fps);
break;
case ST_STATE_END:
case ST_STATE_IEND:
tft.setCursor(0, 160);
tft.setTextColor(ILI9341_WHITE);
tft.setTextSize(1);
tft.print("Game Over");
tft.setCursor(50, 160);
tft.print(st_itoa(st_player_points));
tft.setCursor(75, 160);
tft.print(st_itoa(st_highscore));
tft.drawFastHLine(st_to_diff_cnt, 160, 11, ILI9341_WHITE);
break;
}
}
void st_SetupInGame(void) {
st_player_points = 0;
st_player_points_delayed = 0;
st_difficulty = 1;
st_to_diff_cnt = 0;
st_ClrObjs();
st_NewPlayer();
}
void st_Setup(void) {
tft.begin();
tft.setRotation(1);
tft.fillScreen(ILI9341_BLACK);
}
void st_StepInGame(uint8_t player_pos, uint8_t is_auto_fire, uint8_t is_fire) {
uint8_t i, j;
uint8_t missle_mask;
if ( player_pos < 64 )
st_player_pos = 0;
else if ( player_pos >= 192 )
st_player_pos = 320-2-1;
else
st_player_pos = ((uint16_t)((player_pos-64)) * (uint16_t)(320-2))/128;
st_player_pos+=1;
for( i = 0; i < 60; i++ )
st_Move(i);
for( i = 0; i < 60; i++ )
if ( st_objects[i].ot != 0 )
if ( st_IsOut(i) != 0 )
st_Disappear(i);
for( i = 0; i < 60; i++ ) {
missle_mask = st_GetMissleMask(i);
if ( missle_mask != 0 )
if ( st_CalcXY(st_objects+i) != 0 )
for( j = 0; j < 60; j++ )
if ( i != j )
if ( st_IsHit(j, st_px_x, st_px_y, missle_mask) != 0 ) {
st_Destroy(i);
}
}
st_FireStep(is_auto_fire, is_fire);
for( i = 0; i < 60; i++ )
st_Fire(i);
st_InitDelta();
st_to_diff_cnt++;
if ( st_to_diff_cnt == (ST_DIFF_VIS_LEN<<ST_DIFF_FP) ) {
st_to_diff_cnt = 0;
st_difficulty++;
st_player_points += ST_POINTS_PER_LEVEL;
}
if ( st_player_points_delayed < st_player_points )
st_player_points_delayed++;
}
void st_Step(uint8_t player_pos, uint8_t is_auto_fire, uint8_t is_fire) {
switch(st_state) {
case ST_STATE_PREPARE:
st_to_diff_cnt = 240-10;
st_state = ST_STATE_IPREPARE;
break;
case ST_STATE_IPREPARE:
st_to_diff_cnt--;
if ( st_to_diff_cnt == 0 ) {
st_state = ST_STATE_GAME;
st_SetupInGame();
}
break;
case ST_STATE_GAME:
st_StepInGame(player_pos, is_auto_fire, is_fire);
break;
case ST_STATE_END:
st_to_diff_cnt = 240-10;
if ( st_highscore < st_player_points)
st_highscore = st_player_points;
st_state = ST_STATE_IEND;
break;
case ST_STATE_IEND:
st_to_diff_cnt--;
if ( st_to_diff_cnt == 0 )
st_state = ST_STATE_PREPARE;
break;
}
}
void drawHexagon(int x, int y, int size, uint16_t color) {
for (int i = 0; i < 6; i++) {
int x0 = x + size * cos(i * PI / 3);
int y0 = y + size * sin(i * PI / 3);
int x1 = x + size * cos((i + 1) * PI / 3);
int y1 = y + size * sin((i + 1) * PI / 3);
tft.drawLine(x0, y0, x1, y1, color);
}
}
void drawStaticBackground() {
tft.fillScreen(ILI9341_BLACK);
// Draw some static shapes
for (int i = 0; i < 20; i++) {
int x = rand() % 240;
int y = rand() % 320;
int r = rand() % 20 + 5;
tft.drawCircle(x, y, r, ILI9341_BLUE);
}
for (int i = 0; i < 20; i++) {
int x = rand() % 240;
int y = rand() % 320;
int w = rand() % 40 + 10;
int h = rand() % 40 + 10;
tft.drawRect(x, y, w, h, ILI9341_GREEN);
}
for (int i = 0; i < 20; i++) {
int x0 = rand() % 240;
int y0 = rand() % 320;
int x1 = rand() % 240;
int y1 = rand() % 320;
tft.drawLine(x0, y0, x1, y1, ILI9341_RED);
}
}
void splashScreen() {
tft.fillScreen(ILI9341_BLACK);
// Draw "GAMESHINE" logo with pulsing animation
static float textSize = 3.0;
static bool increasing = true;
const float textSizeMin = 2.5;
const float textSizeMax = 3.5;
const float textSizeStep = 0.05;
if (increasing) {
textSize += textSizeStep;
if (textSize > textSizeMax) increasing = false;
} else {
textSize -= textSizeStep;
if (textSize < textSizeMin) increasing = true;
}
const char *title = "GAMESHINE";
const char *subtitle = "A Fab Academy Project";
// Center text calculations
int16_t x1, y1;
uint16_t w, h;
// Calculate center for title
tft.setTextSize(textSize);
tft.getTextBounds(title, 0, 0, &x1, &y1, &w, &h);
int16_t x = (310 - w) / 2 + 10;
int16_t y = (320 - h) / 2 - 60 ;
tft.setCursor(x, y);
tft.setTextColor(ILI9341_RED);
tft.println(title);
// Calculate center for subtitle
tft.setTextSize(2);
tft.getTextBounds(subtitle, 0, 0, &x1, &y1, &w, &h);
x = (310 - w) / 2 + 10;
y = (320 - h) / 2 - 35;
tft.setCursor(x, y);
tft.setTextColor(ILI9341_WHITE);
tft.println(subtitle);
}
void menu() {
tft.fillScreen(ILI9341_BLACK);
// Draw hexagons
for (int i = 0; i < 50; i++) {
int x = rand() % 240;
int y = rand() % 320;
int size = rand() % 20 + 10;
uint16_t color = rand() % 65536;
drawHexagon(x, y, size, color);
}
// Animate the "GAMESHINE" logo
static uint8_t colorIndex = 0;
uint16_t colors[] = { ILI9341_RED, ILI9341_GREEN, ILI9341_BLUE, ILI9341_YELLOW, ILI9341_CYAN };
tft.setCursor(30, 50);
tft.setTextColor(colors[colorIndex]);
tft.setTextSize(3);
tft.println("GAMESHINE");
tft.setCursor(50, 150);
tft.setTextColor(ILI9341_GREEN);
tft.setTextSize(2);
tft.println("1. Space Trash");
tft.setCursor(50, 200);
tft.setTextColor(ILI9341_BLUE);
tft.println("2. GalaFab");
// Update color index
colorIndex = (colorIndex + 1) % 5;
}
void setup() {
tft.begin();
tft.setRotation(1);
tft.fillScreen(ILI9341_BLACK);
}
unsigned long startTime;
bool splashScreenDone = false;
uint8_t gameSelection = 0;
void loop() {
if (!splashScreenDone) {
unsigned long currentTime = millis();
if (currentTime - startTime > 15000) { // Show splash screen for 15 seconds
splashScreenDone = true;
} else {
splashScreen();
}
} else {
static unsigned long lastUpdate = 0;
if (millis() - lastUpdate > 500) { // Update every 500 milliseconds
menu();
lastUpdate = millis();
}
if (gameSelection == 0) {
if (digitalRead(pin_up)) {
gameSelection = 1;
delay(200);
} else if (digitalRead(pin_down)) {
gameSelection = 2;
delay(200);
}
}
if (gameSelection == 1) {
st_Setup();
for (;;) {
static uint8_t direction = 0; // 0 for down, 1 for up
st_Step(y, 1, 1); // Automatic fire enabled
if (direction == 0) {
y++;
if (y >= 255) {
direction = 1;
}
} else {
y--;
if (y <= 1) {
direction = 0;
}
}
tft.fillScreen(ILI9341_BLACK);
st_Draw(0);
}
} else if (gameSelection == 2) {
// To be implemented...
}
}
}