renamed project because it is to cool for stupid name
This commit is contained in:
commit
fe14b29d15
106 changed files with 16442 additions and 0 deletions
115
games/invader_draw.c
Normal file
115
games/invader_draw.c
Normal file
|
|
@ -0,0 +1,115 @@
|
|||
#include "invaders2.h"
|
||||
/*----------------------getter/setter----------------------------*/
|
||||
|
||||
unsigned char getInvaderPixel(Invaders * iv, unsigned char x, unsigned char y)
|
||||
{
|
||||
if (((x - iv->pos.x) < MAX_INVADER_WIDTH) && ((x - iv->pos.x) >= 0) && ((y
|
||||
- iv->pos.y) < MAX_INVADER_HEIGHT) && ((y - iv->pos.y) >= 0))
|
||||
{
|
||||
return iv->map[x - iv->pos.x][y - iv->pos.y];
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void setInvaderPixel(Invaders * iv, unsigned char x, unsigned char y,
|
||||
unsigned char val)
|
||||
{
|
||||
if (((x - iv->pos.x) < MAX_INVADER_WIDTH) && ((x - iv->pos.x) >= 0) && ((y
|
||||
- iv->pos.y) < MAX_INVADER_HEIGHT) && ((y - iv->pos.y) >= 0))
|
||||
{
|
||||
iv->map[x - iv->pos.x][y - iv->pos.y] = val;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned char getGuardPixel(unsigned char guards[BORG_WIDTH], unsigned char x,
|
||||
unsigned char y)
|
||||
{
|
||||
if (x < BORG_WIDTH && y == GUARD_LINE)
|
||||
return guards[x];
|
||||
return 0;
|
||||
}
|
||||
|
||||
void setGuardPixel(unsigned char guards[BORG_WIDTH], unsigned char x,
|
||||
unsigned char y, unsigned char val)
|
||||
{
|
||||
if (x < BORG_WIDTH && y == GUARD_LINE && val <= 3)
|
||||
guards[x] = val;
|
||||
}
|
||||
|
||||
/*----------------------drawing Method---------------------------*/
|
||||
|
||||
void draw(Invaders * iv, Spaceship * sc, Player * pl, Cannon * cn,
|
||||
unsigned char guards[BORG_WIDTH], uPixel st[MAX_SHOTS], uPixel * shot)
|
||||
{
|
||||
clearScreen ();
|
||||
|
||||
int x, y;
|
||||
|
||||
/*---SPACESHIP---*/
|
||||
if (sc->pos < RIGHT_BORDER)
|
||||
{
|
||||
setPixel (sc->pos, SPACESHIP_LINE, sc->lives);
|
||||
}
|
||||
if (sc->pos - 1 < RIGHT_BORDER)
|
||||
{
|
||||
setPixel (sc->pos + 1, SPACESHIP_LINE, sc->lives);
|
||||
}
|
||||
|
||||
/*---INVADERS--*/
|
||||
for (y = 0; y < MAX_INVADER_HEIGHT; y++)
|
||||
{
|
||||
for (x = 0; x < MAX_INVADER_WIDTH; x++)
|
||||
{
|
||||
//mal in oder statement umwandeln ;-)
|
||||
if (iv->map[x][y] == 0)
|
||||
continue;
|
||||
if (x + iv->pos.x > RIGHT_BORDER)
|
||||
continue;
|
||||
if (x + iv->pos.x < 0)
|
||||
continue;
|
||||
|
||||
setPixel (x + iv->pos.x, y + iv->pos.y, iv->map[x][y]);
|
||||
}
|
||||
}
|
||||
|
||||
/*---GUARDS---*/
|
||||
for (x = 0; x < BORG_WIDTH; ++x)
|
||||
{
|
||||
if (guards[x] != 0)
|
||||
{
|
||||
setPixel (x, GUARD_LINE, guards[x]);
|
||||
}
|
||||
}
|
||||
|
||||
/*---SHOTS--*/
|
||||
int i;
|
||||
for (i = 0; i < MAX_SHOTS; ++i)
|
||||
{
|
||||
if (st[i].x < BORG_WIDTH && st[i].y < BORG_HEIGHT)
|
||||
{
|
||||
setPixel (st[i].x, st[i].y, 3);
|
||||
}
|
||||
}
|
||||
|
||||
/*draw player shot */
|
||||
if (!(cn->ready))
|
||||
{
|
||||
setPixel (shot->x, shot->y, 3);
|
||||
}
|
||||
|
||||
/*-- CANNON --*/
|
||||
if (cn->pos >= LEFT_BORDER + 1)
|
||||
{
|
||||
setPixel (cn->pos - 1, 15, pl->lives);
|
||||
}
|
||||
if (cn->pos < BORG_WIDTH)
|
||||
{
|
||||
setPixel (cn->pos, 15, pl->lives);
|
||||
setPixel (cn->pos, 14, pl->lives);
|
||||
}
|
||||
if (cn->pos < RIGHT_BORDER)
|
||||
{
|
||||
setPixel (cn->pos + 1, 15, pl->lives);
|
||||
}
|
||||
|
||||
}
|
||||
153
games/invader_init.c
Normal file
153
games/invader_init.c
Normal file
|
|
@ -0,0 +1,153 @@
|
|||
#include "invaders2.h"
|
||||
|
||||
unsigned char peter[8][11] =
|
||||
{
|
||||
{ 0, 0, P, 0, 0, 0, 0, 0, P, 0, 0 },
|
||||
{ 0, 0, 0, P, 0, 0, 0, P, 0, 0, 0 },
|
||||
{ 0, 0, P, P, P, P, P, P, P, 0, 0 },
|
||||
{ 0, P, P, 0, P, P, P, 0, P, P, 0 },
|
||||
{ P, P, P, P, P, P, P, P, P, P, P },
|
||||
{ P, 0, P, P, P, P, P, P, P, 0, P },
|
||||
{ P, 0, P, 0, 0, 0, 0, 0, P, 0, P },
|
||||
{ 0, 0, 0, P, P, 0, P, P, 0, 0, 0 } };
|
||||
|
||||
unsigned char hans[8][11] =
|
||||
{
|
||||
{ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
|
||||
{ 1, 2, 1, 1, 2, 2, 2, 1, 2, 2, 1 },
|
||||
{ 1, 2, 1, 1, 2, 1, 2, 1, 2, 1, 2 },
|
||||
{ 1, 2, 1, 1, 2, 2, 2, 1, 2, 2, 1 },
|
||||
{ 1, 2, 1, 1, 2, 1, 2, 1, 2, 1, 2 },
|
||||
{ 1, 2, 2, 1, 2, 1, 2, 1, 2, 2, 2 },
|
||||
{ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
|
||||
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } };
|
||||
|
||||
void initGuards(unsigned char guards[BORG_WIDTH])
|
||||
{
|
||||
int x;
|
||||
for (x = 0; x < BORG_WIDTH; ++x)
|
||||
{
|
||||
guards[x] = 0;
|
||||
}
|
||||
|
||||
guards[3] = 3;
|
||||
guards[6] = 3;
|
||||
guards[10] = 3;
|
||||
guards[13] = 3;
|
||||
|
||||
}
|
||||
|
||||
void initInvaders(Invaders * iv, unsigned char lv)
|
||||
{
|
||||
unsigned char x, y;
|
||||
|
||||
// first zero out map!
|
||||
for (x = 0; x < MAX_INVADER_WIDTH; ++x)
|
||||
{
|
||||
for (y = 0; y < MAX_INVADER_HEIGHT; ++y)
|
||||
{
|
||||
iv->map[x][y] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
iv->speedinc = 0;
|
||||
iv->isEdged = 0;
|
||||
|
||||
switch (lv)
|
||||
{
|
||||
case 0:
|
||||
for (x = 0; x < 8; ++x)
|
||||
{
|
||||
iv->map[x][0] = 2;
|
||||
iv->map[x][1] = 2;
|
||||
iv->map[x][2] = 2;
|
||||
iv->map[x][3] = 1;
|
||||
// iv->map[x][4] = 1;
|
||||
}
|
||||
|
||||
iv->pos.x = 4;
|
||||
iv->pos.y = SPACESHIP_LINE + 1;
|
||||
iv->speed = MIN_SPEED - 3;
|
||||
iv->direction = 1;
|
||||
break;
|
||||
|
||||
case 1:
|
||||
for (x = 0; x < 8; ++x)
|
||||
{
|
||||
//for(y = 0; y < MAX_INVADER_HEIGHT; ++y) {
|
||||
iv->map[x][0] = 3;
|
||||
iv->map[x][1] = 3;
|
||||
iv->map[x][2] = 2;
|
||||
iv->map[x][3] = 2;
|
||||
// iv->map[x][4] = 1;
|
||||
//}
|
||||
}
|
||||
|
||||
iv->pos.x = 4;
|
||||
iv->pos.y = SPACESHIP_LINE + 1;
|
||||
iv->speed = MIN_SPEED - 2;
|
||||
|
||||
iv->direction = 1;
|
||||
break;
|
||||
|
||||
case 2:
|
||||
for (x = 0; x < 8; ++x)
|
||||
{
|
||||
//for(y = 0; y < MAX_INVADER_HEIGHT; ++y) {
|
||||
iv->map[x][0] = 3;
|
||||
iv->map[x][1] = 3;
|
||||
iv->map[x][2] = 2;
|
||||
iv->map[x][3] = 2;
|
||||
iv->map[x][4] = 1;
|
||||
//}
|
||||
}
|
||||
|
||||
iv->pos.x = 4;
|
||||
iv->pos.y = SPACESHIP_LINE + 1;
|
||||
iv->speed = MIN_SPEED - 1;
|
||||
|
||||
iv->direction = 1;
|
||||
break;
|
||||
|
||||
case 3:
|
||||
for (x = 0; x < 11; ++x)
|
||||
{
|
||||
for (y = 0; y < 8; ++y)
|
||||
{
|
||||
if (hans[y][x] != 0)
|
||||
{
|
||||
iv->map[x][y] = 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
iv->pos.x = 3;
|
||||
iv->pos.y = SPACESHIP_LINE + 1;
|
||||
|
||||
iv->speed = MIN_SPEED + 2;
|
||||
iv->direction = 1;
|
||||
|
||||
break;
|
||||
|
||||
case 4:
|
||||
for (x = 0; x < 11; ++x)
|
||||
{
|
||||
for (y = 0; y < 8; ++y)
|
||||
{
|
||||
if (peter[y][x] != 0)
|
||||
{
|
||||
iv->map[x][y] = 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
iv->pos.x = 3;
|
||||
iv->pos.y = SPACESHIP_LINE + 1;
|
||||
|
||||
iv->speed = MIN_SPEED + 3;
|
||||
iv->direction = 1;
|
||||
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
304
games/invader_proc.c
Normal file
304
games/invader_proc.c
Normal file
|
|
@ -0,0 +1,304 @@
|
|||
#include <stdlib.h>
|
||||
#include "prng.h"
|
||||
#include "invaders2.h"
|
||||
|
||||
void procCannon(Cannon * cn, uPixel * shot)
|
||||
{
|
||||
static unsigned char mv = 0;
|
||||
if (mv >= CANNON_SPEED)
|
||||
{
|
||||
mv = 0;
|
||||
if (JOYISLEFT)
|
||||
{
|
||||
if (cn->pos != RIGHT_BORDER)
|
||||
{
|
||||
cn->pos++;
|
||||
}
|
||||
}
|
||||
else if (JOYISRIGHT)
|
||||
{
|
||||
if (cn->pos != LEFT_BORDER)
|
||||
{
|
||||
cn->pos--;
|
||||
}
|
||||
}
|
||||
else if (JOYISFIRE)
|
||||
{
|
||||
|
||||
if (cn->ready)
|
||||
{
|
||||
shot->x = cn->pos;
|
||||
shot->y = 14;
|
||||
cn->ready = 0;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
mv++;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
unsigned char areAtBorder(Invaders * iv)
|
||||
{
|
||||
int y;
|
||||
for (y = SPACESHIP_LINE + 1; y <= GUARD_LINE; ++y)
|
||||
{
|
||||
if (getInvaderPixel(iv, LEFT_BORDER, y) || getInvaderPixel(iv,
|
||||
RIGHT_BORDER, y))
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
void procInvaders(Invaders * iv, uPixel st[MAX_SHOTS])
|
||||
{
|
||||
static unsigned char mv = 0;
|
||||
|
||||
if (mv >= iv->speed)
|
||||
{
|
||||
mv = 0;
|
||||
if (areAtBorder(iv) && !(iv->isEdged))
|
||||
{
|
||||
iv->pos.y++;
|
||||
iv->direction = -iv->direction;
|
||||
iv->isEdged = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
iv->pos.x += iv->direction;
|
||||
iv->isEdged = 0;
|
||||
}
|
||||
|
||||
}
|
||||
mv++;
|
||||
|
||||
unsigned char i, y;
|
||||
unsigned char spos = random8() % 16;
|
||||
if (spos >= BORG_WIDTH)
|
||||
return;
|
||||
|
||||
unsigned char shoot = random8();
|
||||
|
||||
if (shoot < SHOOTING_RATE)
|
||||
{
|
||||
for (i = 0; i < MAX_SHOTS; ++i)
|
||||
{
|
||||
if (st[i].x > BORG_WIDTH || st[i].y > BORG_HEIGHT)
|
||||
{
|
||||
|
||||
for (y = GUARD_LINE; y > SPACESHIP_LINE; --y)
|
||||
{
|
||||
if (getInvaderPixel(iv, spos, y) != 0)
|
||||
{
|
||||
|
||||
st[i].x = spos;
|
||||
st[i].y = y + 1;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
} //for SHOTS
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void procShots(Invaders * iv, Player * pl, Cannon * cn, Spaceship * sc,
|
||||
unsigned char guards[BORG_WIDTH], uPixel st[MAX_SHOTS], uPixel * shot)
|
||||
{
|
||||
|
||||
int i;
|
||||
static unsigned char cmv = 0, imv = 0;
|
||||
|
||||
// shuß mit einen struct mit dem shuß!!
|
||||
|
||||
if (cmv >= CANNON_SHOOTING_SPEED)
|
||||
{
|
||||
cmv = 0;
|
||||
if (!(cn->ready))
|
||||
{
|
||||
shot->y--;
|
||||
}
|
||||
if (shot->y > BORG_HEIGHT)
|
||||
{
|
||||
cn->ready = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (imv >= INVADER_SHOOTING_SPEED)
|
||||
{
|
||||
imv = 0;
|
||||
|
||||
for (i = 0; i < MAX_SHOTS; ++i)
|
||||
{
|
||||
if ( /*st[i].x < BORG_WIDTH && */st[i].y < BORG_HEIGHT)
|
||||
{
|
||||
st[i].y++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cmv++;
|
||||
imv++;
|
||||
|
||||
/****************************************************************/
|
||||
/* TESTE OB GETROFFEN */
|
||||
/****************************************************************/
|
||||
|
||||
// USER CANNON
|
||||
|
||||
unsigned char tmp;
|
||||
if (!(cn->ready))
|
||||
{
|
||||
for (i = 0; i < MAX_SHOTS; ++i)
|
||||
{
|
||||
if (shot->x == st[i].x && shot->y == st[i].y)
|
||||
{
|
||||
st[i].x = 255;
|
||||
st[i].y = 255;
|
||||
cn->ready = 1;
|
||||
}
|
||||
}
|
||||
|
||||
//GUARDS
|
||||
if ((tmp = getGuardPixel(guards, shot->x, shot->y)))
|
||||
{
|
||||
--tmp;
|
||||
setGuardPixel(guards, shot->x, shot->y, tmp);
|
||||
cn->ready = 1;
|
||||
goto invader_shots;
|
||||
}
|
||||
|
||||
//INVADER
|
||||
if ((tmp = getInvaderPixel(iv, shot->x, shot->y)))
|
||||
{
|
||||
--tmp;
|
||||
setInvaderPixel(iv, shot->x, shot->y, tmp);
|
||||
|
||||
if (tmp == 0)
|
||||
{
|
||||
iv->speedinc++;
|
||||
if (iv->speedinc == SPEED_INC_RATE)
|
||||
{
|
||||
iv->speedinc = 0;
|
||||
iv->speed -= SPEED_INC_VALUE;
|
||||
}
|
||||
pl->points += POINTS_FOR_KILL;
|
||||
}
|
||||
else
|
||||
{
|
||||
pl->points += POINTS_FOR_HIT;
|
||||
}
|
||||
cn->ready = 1;
|
||||
goto invader_shots;
|
||||
}
|
||||
|
||||
//SPACESHIP
|
||||
|
||||
if (shot->y == SPACESHIP_LINE)
|
||||
{
|
||||
if (shot->x == sc->pos || shot->x == sc->pos + 1)
|
||||
{
|
||||
sc->pos = 255;
|
||||
pl->points += POINTS_FOR_SPACESHIP;
|
||||
cn->ready = 1;
|
||||
goto invader_shots;
|
||||
}
|
||||
}
|
||||
} // !(cn->ready)
|
||||
|
||||
|
||||
invader_shots: for (i = 0; i < MAX_SHOTS; ++i)
|
||||
{
|
||||
if ((tmp = getGuardPixel(guards, st[i].x, st[i].y)))
|
||||
{
|
||||
--tmp;
|
||||
setGuardPixel(guards, st[i].x, st[i].y, tmp);
|
||||
st[i].x = 255;
|
||||
st[i].y = 255;
|
||||
}
|
||||
|
||||
if (st[i].y == BORG_HEIGHT - 1)
|
||||
{
|
||||
if (st[i].x == cn->pos)
|
||||
{
|
||||
|
||||
pl->lives--;
|
||||
st[i].x = 255;
|
||||
st[i].y = 255;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void procSpaceship(Spaceship * sc)
|
||||
{
|
||||
unsigned char rnd1 = random8();
|
||||
unsigned char rnd2 = random8();
|
||||
|
||||
static unsigned char sct = 0;
|
||||
|
||||
if (sc->pos > RIGHT_BORDER)
|
||||
{
|
||||
|
||||
if (rnd1 == 73)
|
||||
{
|
||||
if (rnd2 >= 200)
|
||||
{
|
||||
sc->pos = RIGHT_BORDER;
|
||||
sct = 0;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
if (sct == SPACESHIP_SPEED)
|
||||
{
|
||||
sct = 0;
|
||||
if (sc->pos == 0)
|
||||
{
|
||||
sc->pos = 255;
|
||||
}
|
||||
else
|
||||
{
|
||||
sc->pos--;
|
||||
}
|
||||
}
|
||||
}
|
||||
sct++;
|
||||
}
|
||||
|
||||
unsigned char getStatus(Invaders * iv)
|
||||
{
|
||||
|
||||
//count Invader!
|
||||
unsigned char x, y, inv = 0;
|
||||
for (x = 0; x < MAX_INVADER_WIDTH; ++x)
|
||||
{
|
||||
for (y = 0; y < MAX_INVADER_HEIGHT; ++y)
|
||||
{
|
||||
if (iv->map[x][y] != 0)
|
||||
inv++;
|
||||
}
|
||||
}
|
||||
|
||||
//LEVEL BEREINIGT
|
||||
if (inv == 0)
|
||||
return 1;
|
||||
|
||||
//INVADERS REACHED EARTH
|
||||
for (x = 0; x < BORG_WIDTH; ++x)
|
||||
{
|
||||
if (getInvaderPixel(iv, x, GUARD_LINE + 1))
|
||||
return 2;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
134
games/invaders2.c
Normal file
134
games/invaders2.c
Normal file
|
|
@ -0,0 +1,134 @@
|
|||
#include <stdio.h>
|
||||
#include "util.h"
|
||||
#include "invaders2.h"
|
||||
|
||||
//#include <stdio.h>
|
||||
|
||||
#ifndef __AVR__
|
||||
#define wait(_X) myWait(_X)
|
||||
#endif
|
||||
|
||||
void borg_invaders()
|
||||
{
|
||||
// waitForFire = 0;
|
||||
/****************************************************************/
|
||||
/* INITIALIZE */
|
||||
/****************************************************************/
|
||||
|
||||
Invaders iv;
|
||||
Cannon cn;
|
||||
Player pl;
|
||||
Spaceship sc;
|
||||
|
||||
unsigned char guards[BORG_WIDTH];
|
||||
|
||||
unsigned char level = 0;
|
||||
unsigned char ivStatus = 0;
|
||||
|
||||
uPixel st[MAX_SHOTS] =
|
||||
{
|
||||
{ 255, 255},
|
||||
{ 255, 255},
|
||||
{ 255, 255},
|
||||
{ 255, 255},
|
||||
{ 255, 255}
|
||||
};
|
||||
|
||||
uPixel shot;
|
||||
// = {0,0};
|
||||
|
||||
pl.points = 0;
|
||||
pl.lives = 3;
|
||||
|
||||
//--------Init Cannon-------//
|
||||
//cn.pos = 4;
|
||||
//cn.ready = 1;
|
||||
|
||||
/****************************************************************/
|
||||
/* INTRO */
|
||||
/****************************************************************/
|
||||
//drawIntroScreen(2000);
|
||||
|
||||
|
||||
/****************************************************************/
|
||||
/* GAME LOOP */
|
||||
/****************************************************************/
|
||||
do
|
||||
{
|
||||
//----- INITIALIZE LEVEL-----//
|
||||
initGuards(guards);
|
||||
initInvaders(&iv, level);
|
||||
|
||||
//Spaceship
|
||||
sc.lives = 1;
|
||||
sc.pos = 255;
|
||||
|
||||
//Cannon
|
||||
cn.pos = 7;
|
||||
cn.ready = 1;
|
||||
|
||||
draw(&iv, &sc, &pl, &cn, guards, st, &shot);
|
||||
|
||||
while (1)
|
||||
{
|
||||
procInvaders(&iv, st);
|
||||
procSpaceship(&sc);
|
||||
|
||||
procShots(&iv, &pl, &cn, &sc, guards, st, &shot);
|
||||
procCannon(&cn, &shot);
|
||||
|
||||
draw(&iv, &sc, &pl, &cn, guards, st, &shot);
|
||||
|
||||
ivStatus = getStatus(&iv);
|
||||
|
||||
if (ivStatus == 2) //LOST
|
||||
{
|
||||
//pl.lives--;
|
||||
pl.lives = 0;
|
||||
break;
|
||||
|
||||
}
|
||||
else if (ivStatus == 1) //WON
|
||||
{
|
||||
unsigned int bonus= POINTS_FOR_LEVEL * (level + 1) * (12 - iv.pos.y);
|
||||
pl.points += bonus;
|
||||
//printf("cleared l: %d , y: %d bonus: %d \n",
|
||||
// level, iv.pos.y, bonus);
|
||||
if (level == MAX_LEVEL - 1)
|
||||
{
|
||||
level = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
level = (level + 1);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (pl.lives <= 0)
|
||||
{
|
||||
//scrolltext("GAME OVER",0,80);
|
||||
break;
|
||||
}
|
||||
|
||||
wait (WAIT_MS);
|
||||
} //IN LEVEL LOOP
|
||||
|
||||
} while (pl.lives > 0); //GAME LOOP
|
||||
|
||||
clearScreen ();
|
||||
//wait(5000);
|
||||
char text[64];
|
||||
snprintf(text, 64, "</#points: %u", pl.points);
|
||||
scrolltext(text);
|
||||
//printf("scores: %d\n", pl.points);
|
||||
|
||||
|
||||
/****************************************************************/
|
||||
/* PLAYER STAT */
|
||||
/* HIGH SCORES */
|
||||
/****************************************************************/
|
||||
|
||||
// waitForFire = 1;
|
||||
|
||||
}
|
||||
182
games/invaders2.h
Normal file
182
games/invaders2.h
Normal file
|
|
@ -0,0 +1,182 @@
|
|||
/* Space INVADERS V0.2
|
||||
*
|
||||
* by: Fabian Bremerich
|
||||
* Thanx to: Peter F.
|
||||
*
|
||||
* date: Mi, 08.03.2006
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef INVADERS2_H
|
||||
#define INVADERS2_H
|
||||
|
||||
#define USE_ORIGINAL_PIXEL_API
|
||||
|
||||
/*CONNECTION TO SIMULATOR*/
|
||||
//extern char fkey;
|
||||
|
||||
/* TEST PARTS NEW API */
|
||||
|
||||
typedef struct
|
||||
{
|
||||
signed char x;
|
||||
signed char y;
|
||||
} sPixel;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
unsigned char x;
|
||||
unsigned char y;
|
||||
} uPixel;
|
||||
|
||||
//for compatibility to pisel.h api!
|
||||
#ifdef USE_ORIGINAL_PIXEL_API
|
||||
#include "pixel.h"
|
||||
#include "scrolltext.h"
|
||||
#include "joystick.h"
|
||||
|
||||
//typedef uPixel pixel;
|
||||
#define uPixel pixel
|
||||
//#define getPixel(_X, _Y) get_pixel( (pixel){_X, _Y})
|
||||
#define clearScreen() clear_screen(0)
|
||||
//#define
|
||||
|
||||
//#ifdef SIMULATOR
|
||||
#define setPixel(_X, _Y, _V) setpixel( (pixel){_X, _Y}, _V)
|
||||
//#else //if defined (AVR)
|
||||
//#define setPixel(_X, _Y, _V) reverseSetPixel( (pixel){_X, _Y}, _V)
|
||||
//#endif
|
||||
|
||||
#endif
|
||||
|
||||
/****************************************************************/
|
||||
/* GLOBALE VAR */
|
||||
/****************************************************************/
|
||||
#define P 3
|
||||
extern unsigned char peter[8][11];
|
||||
extern unsigned char hans[8][11];
|
||||
|
||||
/****************************************************************/
|
||||
/* DEFINES */
|
||||
/****************************************************************/
|
||||
#define START_LIVES 3
|
||||
|
||||
#define SPACESHIP_LINE 1
|
||||
//#define SPACESHIP_TRIGGER_POINTS 250
|
||||
//#define SPACESHIP_TRIGGER_RATE 333
|
||||
|
||||
|
||||
#define GUARD_LINE 13
|
||||
|
||||
#define BORG_WIDTH 16
|
||||
#define BORG_HEIGHT 16
|
||||
|
||||
#ifdef SWITCHED_SIDE
|
||||
#define RIGHT_BORDER 0
|
||||
#define LEFT_BORDER (BORG_WIDTH -1 )
|
||||
#else
|
||||
#define RIGHT_BORDER (BORG_WIDTH -1 )
|
||||
#define LEFT_BORDER 0
|
||||
#endif
|
||||
|
||||
#define MAX_INVADER_HEIGHT 8
|
||||
#define MAX_INVADER_WIDTH 12
|
||||
#define MAX_INVADER_LIVES 3
|
||||
|
||||
#define POINTS_FOR_HIT 5
|
||||
#define POINTS_FOR_KILL 25
|
||||
#define POINTS_FOR_SPACESHIP 75
|
||||
#define POINTS_FOR_LEVEL 100
|
||||
|
||||
#define MAX_SHOTS 7
|
||||
#define MIN_SPEED 70
|
||||
#define SPEED_INC_RATE 2
|
||||
#define SPEED_INC_VALUE 3
|
||||
#define MAX_LEVEL 5
|
||||
|
||||
#define SHOOTING_RATE 6
|
||||
#define INVADER_SHOOTING_SPEED 10
|
||||
#define CANNON_SHOOTING_SPEED 4
|
||||
#define SPACESHIP_SPEED 30
|
||||
|
||||
#define CANNON_SPEED 2
|
||||
|
||||
#define WAIT_MS 15
|
||||
//#define WAIT_MS 20
|
||||
|
||||
|
||||
typedef struct
|
||||
{
|
||||
unsigned char map[MAX_INVADER_WIDTH][MAX_INVADER_HEIGHT];
|
||||
sPixel pos;
|
||||
|
||||
unsigned char speed;
|
||||
unsigned char speedinc;
|
||||
signed char direction;
|
||||
unsigned char isEdged;
|
||||
} Invaders;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
unsigned char pos;
|
||||
unsigned char lives;
|
||||
} Spaceship;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
unsigned char pos;
|
||||
unsigned char ready;
|
||||
} Cannon;
|
||||
|
||||
//typedef struct {
|
||||
// unsigned char guards[numGards];
|
||||
//}
|
||||
|
||||
typedef struct
|
||||
{
|
||||
signed char lives;
|
||||
unsigned int points;
|
||||
} Player;
|
||||
|
||||
/****************************************************************/
|
||||
/* FUNCTIONS */
|
||||
/****************************************************************/
|
||||
|
||||
void borg_invaders();
|
||||
/*----------------------main_level_funcs-------------------------*/
|
||||
|
||||
void procSpaceship(Spaceship * sp);
|
||||
void procCannon(Cannon * cn, uPixel * shot);
|
||||
|
||||
void procInvaders(Invaders * iv, uPixel st[MAX_SHOTS]);
|
||||
void procShots(Invaders * iv, Player * pl, Cannon * cn, Spaceship * sc,
|
||||
unsigned char guards[BORG_WIDTH], uPixel st[MAX_SHOTS], uPixel * shot);
|
||||
|
||||
unsigned char getStatus(Invaders * iv);
|
||||
|
||||
/*----------------------Initialization---------------------------*/
|
||||
void initGuards(unsigned char guards[BORG_WIDTH]);
|
||||
void initInvaders(Invaders * iv, unsigned char lv);
|
||||
//void initSpaceship(Spaceship* sc);
|
||||
//void initPlayer(Player* pl);
|
||||
|
||||
/*----------------------getter/setter----------------------------*/
|
||||
|
||||
unsigned char getInvaderPixel(Invaders * iv, unsigned char x, unsigned char y);
|
||||
|
||||
void setInvaderPixel(Invaders * iv, unsigned char x, unsigned char y,
|
||||
unsigned char val);
|
||||
|
||||
unsigned char getGuardPixel(unsigned char guards[BORG_WIDTH], unsigned char x,
|
||||
unsigned char y);
|
||||
|
||||
void setGuardPixel(unsigned char guards[BORG_WIDTH], unsigned char x,
|
||||
unsigned char y, unsigned char val);
|
||||
|
||||
/*----------------------drawing Method---------------------------*/
|
||||
|
||||
void draw(Invaders * iv, Spaceship * sc, Player * pl, Cannon * cn,
|
||||
unsigned char guards[BORG_WIDTH], uPixel ishots[MAX_SHOTS],
|
||||
uPixel * shot);
|
||||
|
||||
#endif
|
||||
19
games/tetris/COPYING
Normal file
19
games/tetris/COPYING
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
Copyright (c) 2007 Christian Kroll
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to
|
||||
deal in the Software without restriction, including without limitation the
|
||||
rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
sell copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
IN THE SOFTWARE.
|
||||
19
games/tetris/LICENSE
Normal file
19
games/tetris/LICENSE
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
Copyright (c) 2007 Christian Kroll
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to
|
||||
deal in the Software without restriction, including without limitation the
|
||||
rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
sell copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
IN THE SOFTWARE.
|
||||
25
games/tetris/Makefile
Normal file
25
games/tetris/Makefile
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
LD = avr-ld
|
||||
|
||||
all: tetris.o
|
||||
|
||||
tetris.o: piece.o playfield.o view.o logic.o input.o
|
||||
$(LD) -r piece.o playfield.o view.o logic.o input.o -o tetris.o
|
||||
|
||||
piece.o: piece.c piece.h
|
||||
$(MCU_CC) $(CFLAGS) -c piece.c -o piece.o
|
||||
|
||||
playfield.o: playfield.c playfield.h piece.h
|
||||
$(MCU_CC) $(CFLAGS) -c playfield.c -o playfield.o
|
||||
|
||||
view.o: view.c view.h logic.h piece.h playfield.h ../config.h ../pixel.h \
|
||||
../util.h ../scrolltext.h
|
||||
$(MCU_CC) $(CFLAGS) -c view.c -o view.o
|
||||
|
||||
logic.o: logic.c logic.h piece.h playfield.h input.h view.h
|
||||
$(MCU_CC) $(CFLAGS) -c logic.c -o logic.o
|
||||
|
||||
input.o: input.c input.h ../joystick.h ../util.h
|
||||
$(MCU_CC) $(CFLAGS) -c input.c -o input.o
|
||||
|
||||
clean:
|
||||
rm -rf *.o *.d
|
||||
400
games/tetris/input.c
Normal file
400
games/tetris/input.c
Normal file
|
|
@ -0,0 +1,400 @@
|
|||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <inttypes.h>
|
||||
#include <assert.h>
|
||||
#include "../joystick.h"
|
||||
#include "../util.h"
|
||||
#include "input.h"
|
||||
|
||||
/* - the API simulator and the real API have different named wait functions
|
||||
* - the macro PM helps in reading values from PROGMEM on the AVR arch
|
||||
*/
|
||||
#ifdef __AVR__
|
||||
#include <avr/pgmspace.h>
|
||||
#define WAIT(ms) wait(ms)
|
||||
#define PM(value) pgm_read_word(&value)
|
||||
#else
|
||||
#define PROGMEM
|
||||
#define WAIT(ms) myWait(ms)
|
||||
#define PM(value) (value)
|
||||
#endif
|
||||
|
||||
|
||||
/***********
|
||||
* defines *
|
||||
***********/
|
||||
|
||||
// amount of milliseconds that each loop cycle waits
|
||||
#define TETRIS_INPUT_TICKS 5
|
||||
|
||||
// amount of milliseconds the input is ignored after the pause combo has been
|
||||
// pressed, since it is difficult to release all buttons simultaneously
|
||||
#define TETRIS_INPUT_PAUSE_TICKS 100
|
||||
|
||||
// amount of allowed loop cycles while in pause mode so that the game
|
||||
// automatically continues after five minutes
|
||||
#define TETRIS_INPUT_PAUSE_CYCLES 60000
|
||||
|
||||
// minimum of cycles in gliding mode
|
||||
#define TETRIS_INPUT_GLIDE_CYCLES 75
|
||||
|
||||
// here you can adjust the delays (in loop cycles) for key repeat
|
||||
#define TETRIS_INPUT_REPEAT_INITIALDELAY 40
|
||||
#define TETRIS_INPUT_REPEAT_DELAY 10
|
||||
|
||||
// Here you can adjust the amount of loop cycles a command is ignored after
|
||||
// its button has been released (to reduce joystick chatter)
|
||||
#define TETRIS_INPUT_CHATTER_TICKS_ROT_CW 24
|
||||
#define TETRIS_INPUT_CHATTER_TICKS_ROT_CCW 24
|
||||
#define TETRIS_INPUT_CHATTER_TICKS_LEFT 12
|
||||
#define TETRIS_INPUT_CHATTER_TICKS_RIGHT 12
|
||||
#define TETRIS_INPUT_CHATTER_TICKS_DOWN 12
|
||||
#define TETRIS_INPUT_CHATTER_TICKS_DROP 24
|
||||
|
||||
|
||||
/***************************
|
||||
* non-interface functions *
|
||||
***************************/
|
||||
|
||||
/* Function: tetris_input_chatterProtect;
|
||||
* Description: sets an ignore counter to a command specific value if it is 0
|
||||
* Argument pIn: pointer to an input object
|
||||
* Argument cmd: the command whose counter should be set
|
||||
* Return value: void
|
||||
*/
|
||||
void tetris_input_chatterProtect (tetris_input_t *pIn,
|
||||
tetris_input_command_t cmd)
|
||||
{
|
||||
// never exceed the index
|
||||
assert(cmd < TETRIS_INCMD_NONE);
|
||||
|
||||
// amount of loop cycles a command is ignored after its button has been
|
||||
// released (every command has its own counter)
|
||||
const static uint8_t nInitialIgnoreValue[TETRIS_INCMD_NONE] PROGMEM =
|
||||
{
|
||||
TETRIS_INPUT_CHATTER_TICKS_ROT_CW,
|
||||
TETRIS_INPUT_CHATTER_TICKS_ROT_CCW,
|
||||
TETRIS_INPUT_CHATTER_TICKS_LEFT,
|
||||
TETRIS_INPUT_CHATTER_TICKS_RIGHT,
|
||||
TETRIS_INPUT_CHATTER_TICKS_DOWN,
|
||||
TETRIS_INPUT_CHATTER_TICKS_DROP,
|
||||
0, // TETRIS_INCMD_GRAVITY (irrelevant because it doesn't have a button)
|
||||
0 // TETRIS_INCMD_PAUSE (is a combination of ROT_CW and DOWN)
|
||||
};
|
||||
|
||||
// setting ignore counter according to the predefined array
|
||||
if (pIn->nIgnoreCmdCounter[cmd] == 0)
|
||||
{
|
||||
// if the command isn't TETRIS_INCMD_PAUSE, setting the ignore counter
|
||||
// is straight forward
|
||||
if (cmd != TETRIS_INCMD_PAUSE)
|
||||
{
|
||||
pIn->nIgnoreCmdCounter[cmd] = PM(nInitialIgnoreValue[cmd]);
|
||||
}
|
||||
// TETRIS_INCMD_PAUSE is issued via a combination of the buttons for
|
||||
// TETRIS_INCMD_ROT_CW and TETRIS_INCMD_DOWN, so we must set their
|
||||
// ignore counters
|
||||
else
|
||||
{
|
||||
pIn->nIgnoreCmdCounter[TETRIS_INCMD_ROT_CW] =
|
||||
TETRIS_INPUT_CHATTER_TICKS_ROT_CW;
|
||||
pIn->nIgnoreCmdCounter[TETRIS_INCMD_DOWN] =
|
||||
TETRIS_INPUT_CHATTER_TICKS_DOWN;
|
||||
}
|
||||
}
|
||||
|
||||
// The ignore counter of TETRIS_INCMD_PAUSE is either set to the counter
|
||||
// value of TETRIS_INCMD_ROT_CW or TETRIS_INCMD_DOWN (whichever is higher).
|
||||
if ((cmd == TETRIS_INCMD_ROT_CW) || (cmd == TETRIS_INCMD_DOWN))
|
||||
{
|
||||
// helper variables (which the compiler hopefully optimizes away)
|
||||
uint8_t nRotCw = pIn->nIgnoreCmdCounter[TETRIS_INCMD_ROT_CW];
|
||||
uint8_t nDown = pIn->nIgnoreCmdCounter[TETRIS_INCMD_DOWN];
|
||||
|
||||
pIn->nIgnoreCmdCounter[TETRIS_INCMD_PAUSE] =
|
||||
(nRotCw > nDown ? nRotCw : nDown);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Function: tetris_input_queryJoystick
|
||||
* Description: translates joystick movements into tetris_input_command_t
|
||||
* Argument pIn: pointer to an input object
|
||||
* Return value: see definition of tetris_input_command_t
|
||||
*/
|
||||
tetris_input_command_t tetris_input_queryJoystick()
|
||||
{
|
||||
tetris_input_command_t cmdReturn;
|
||||
|
||||
if (JOYISFIRE)
|
||||
{
|
||||
cmdReturn = TETRIS_INCMD_DROP;
|
||||
}
|
||||
else if (JOYISLEFT)
|
||||
{
|
||||
cmdReturn = TETRIS_INCMD_LEFT;
|
||||
}
|
||||
else if (JOYISRIGHT)
|
||||
{
|
||||
cmdReturn = TETRIS_INCMD_RIGHT;
|
||||
}
|
||||
else if (JOYISUP && JOYISDOWN)
|
||||
{
|
||||
cmdReturn = TETRIS_INCMD_PAUSE;
|
||||
WAIT(TETRIS_INPUT_PAUSE_TICKS);
|
||||
}
|
||||
else if (JOYISDOWN)
|
||||
{
|
||||
cmdReturn = TETRIS_INCMD_DOWN;
|
||||
}
|
||||
else if (JOYISUP)
|
||||
{
|
||||
cmdReturn = TETRIS_INCMD_ROT_CW;
|
||||
}
|
||||
else
|
||||
{
|
||||
cmdReturn = TETRIS_INCMD_NONE;
|
||||
}
|
||||
|
||||
return cmdReturn;
|
||||
}
|
||||
|
||||
|
||||
/*****************************
|
||||
* construction/destruction *
|
||||
*****************************/
|
||||
|
||||
/* Function: tetris_input_construct
|
||||
* Description: constructs an input object for André's borg
|
||||
* Return value: pointer to a newly created input object
|
||||
*/
|
||||
tetris_input_t *tetris_input_construct()
|
||||
{
|
||||
tetris_input_t *pIn = (tetris_input_t *)malloc(sizeof(tetris_input_t));
|
||||
assert(pIn != NULL);
|
||||
|
||||
pIn->cmdLast = TETRIS_INCMD_NONE;
|
||||
pIn->nLevel = 0xFF;
|
||||
tetris_input_setLevel(pIn, 0);
|
||||
pIn->nLoopCycles = 0;
|
||||
pIn->nRepeatCount = -TETRIS_INPUT_REPEAT_INITIALDELAY;
|
||||
pIn->nPauseCount = 0;
|
||||
memset(pIn->nIgnoreCmdCounter, 0, TETRIS_INCMD_NONE);
|
||||
|
||||
return pIn;
|
||||
}
|
||||
|
||||
|
||||
/* Function: tetris_input_destruct
|
||||
* Description: destructs an input structure
|
||||
* Argument pIn: pointer to the input object which should to be destructed
|
||||
* Return value: void
|
||||
*/
|
||||
void tetris_input_destruct(tetris_input_t *pIn)
|
||||
{
|
||||
assert(pIn != NULL);
|
||||
free(pIn);
|
||||
}
|
||||
|
||||
|
||||
/***************************
|
||||
* input related functions *
|
||||
***************************/
|
||||
|
||||
/* Function: retris_input_getCommand
|
||||
* Description: retrieves commands from joystick or loop interval
|
||||
* Argument pIn: pointer to an input object
|
||||
* Argument nPace: falling pace (see definition of tetris_input_pace_t)
|
||||
* Return value: see definition of tetris_input_command_t
|
||||
*/
|
||||
tetris_input_command_t tetris_input_getCommand(tetris_input_t *pIn,
|
||||
tetris_input_pace_t nPace)
|
||||
{
|
||||
assert (pIn != NULL);
|
||||
|
||||
// holds the translated command value of the joystick
|
||||
tetris_input_command_t cmdJoystick = TETRIS_INCMD_NONE;
|
||||
|
||||
// this variable both serves as the return value and as a flag for not
|
||||
// leaving the function as long as its value is TETRIS_INCMD_NONE
|
||||
tetris_input_command_t cmdReturn = TETRIS_INCMD_NONE;
|
||||
|
||||
uint8_t nMaxCycles;
|
||||
|
||||
// if the piece is gliding we grant the player a reasonable amount of time
|
||||
// to make the game more controllable at higher falling speeds
|
||||
if ((nPace == TETRIS_INPACE_GLIDING) &&
|
||||
(pIn->nMaxCycles < TETRIS_INPUT_GLIDE_CYCLES))
|
||||
{
|
||||
nMaxCycles = TETRIS_INPUT_GLIDE_CYCLES;
|
||||
}
|
||||
else
|
||||
{
|
||||
nMaxCycles = pIn->nMaxCycles;
|
||||
}
|
||||
|
||||
while (pIn->nLoopCycles < nMaxCycles)
|
||||
{
|
||||
cmdJoystick = tetris_input_queryJoystick();
|
||||
|
||||
// only obey current command if it is not considered as chattering
|
||||
if (((cmdJoystick < TETRIS_INCMD_NONE) ?
|
||||
pIn->nIgnoreCmdCounter[cmdJoystick] : 0) == 0)
|
||||
{
|
||||
switch (cmdJoystick)
|
||||
{
|
||||
case TETRIS_INCMD_LEFT:
|
||||
case TETRIS_INCMD_RIGHT:
|
||||
case TETRIS_INCMD_DOWN:
|
||||
// only react if either the current command differs from the
|
||||
// last one or enough loop cycles have been run on the same
|
||||
// command (for key repeat)
|
||||
if ((pIn->cmdLast != cmdJoystick)
|
||||
|| ((pIn->cmdLast == cmdJoystick)
|
||||
&& (pIn->nRepeatCount >= TETRIS_INPUT_REPEAT_DELAY)))
|
||||
{
|
||||
// reset repeat counter
|
||||
if (pIn->cmdLast != cmdJoystick)
|
||||
{
|
||||
// different command: we set an extra initial delay
|
||||
pIn->nRepeatCount = -TETRIS_INPUT_REPEAT_INITIALDELAY;
|
||||
}
|
||||
else
|
||||
{
|
||||
// same command: there's no extra initial delay
|
||||
pIn->nRepeatCount = 0;
|
||||
}
|
||||
|
||||
// update cmdLast and return value
|
||||
pIn->cmdLast = cmdReturn = cmdJoystick;
|
||||
}
|
||||
else
|
||||
{
|
||||
// if not enough loop cycles have been run we increment the
|
||||
// repeat counter, ensure that we continue the loop and
|
||||
// keep the key repeat functioning
|
||||
++pIn->nRepeatCount;
|
||||
cmdReturn = TETRIS_INCMD_NONE;
|
||||
}
|
||||
break;
|
||||
|
||||
case TETRIS_INCMD_DROP:
|
||||
case TETRIS_INCMD_ROT_CW:
|
||||
case TETRIS_INCMD_ROT_CCW:
|
||||
// no key repeat here
|
||||
if (pIn->cmdLast != cmdJoystick)
|
||||
{
|
||||
pIn->cmdLast = cmdReturn = cmdJoystick;
|
||||
}
|
||||
else
|
||||
{
|
||||
// if we reach here the command is ignored
|
||||
cmdReturn = TETRIS_INCMD_NONE;
|
||||
}
|
||||
break;
|
||||
|
||||
case TETRIS_INCMD_PAUSE:
|
||||
// if this is an initial pause command, make sure that the logic
|
||||
// module is informed about it
|
||||
if (pIn->cmdLast != TETRIS_INCMD_PAUSE)
|
||||
{
|
||||
pIn->cmdLast = cmdReturn = cmdJoystick;
|
||||
pIn->nPauseCount = 0;
|
||||
}
|
||||
// consecutive pause commands should not cause the loop to leave
|
||||
else
|
||||
{
|
||||
cmdReturn = TETRIS_INCMD_NONE;
|
||||
}
|
||||
break;
|
||||
|
||||
case TETRIS_INCMD_NONE:
|
||||
// chatter protection
|
||||
if (pIn->cmdLast != TETRIS_INCMD_NONE)
|
||||
{
|
||||
tetris_input_chatterProtect(pIn, pIn->cmdLast);
|
||||
}
|
||||
|
||||
// If the game is paused (last command was TETRIS_INCMD_PAUSE)
|
||||
// we ensure that the variable which holds that last command
|
||||
// isn't touched. We use this as a flag so that the loop cycle
|
||||
// counter doesn't get incremented.
|
||||
// We count the number of pause cycles, though. If enough pause
|
||||
// cycles have been run, we enforce the continuation of the game.
|
||||
if ((pIn->cmdLast != TETRIS_INCMD_PAUSE) ||
|
||||
(++pIn->nPauseCount > TETRIS_INPUT_PAUSE_CYCLES))
|
||||
{
|
||||
pIn->cmdLast = TETRIS_INCMD_NONE;
|
||||
}
|
||||
|
||||
// reset repeat counter
|
||||
pIn->nRepeatCount = -TETRIS_INPUT_REPEAT_INITIALDELAY;
|
||||
|
||||
// using cmdReturn as a flag for not leaving the loop
|
||||
cmdReturn = TETRIS_INCMD_NONE;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
// current command is considered as chattering
|
||||
else
|
||||
{
|
||||
pIn->cmdLast = cmdReturn = TETRIS_INCMD_NONE;
|
||||
}
|
||||
|
||||
// decrement all ignore counters
|
||||
for (int nIgnoreIndex = 0; nIgnoreIndex < TETRIS_INCMD_NONE; ++nIgnoreIndex)
|
||||
{
|
||||
if (pIn->nIgnoreCmdCounter[nIgnoreIndex] != 0)
|
||||
{
|
||||
--pIn->nIgnoreCmdCounter[nIgnoreIndex];
|
||||
}
|
||||
}
|
||||
|
||||
// reset automatic falling if the player has dropped a piece
|
||||
if ((cmdReturn == TETRIS_INCMD_DOWN)
|
||||
|| (cmdReturn == TETRIS_INCMD_DROP))
|
||||
{
|
||||
pIn->nLoopCycles = 0;
|
||||
}
|
||||
// otherwise ensure automatic falling (unless the game is running)
|
||||
else if ((cmdReturn != TETRIS_INCMD_PAUSE) &&
|
||||
!((cmdReturn == TETRIS_INCMD_NONE) &&
|
||||
(pIn->cmdLast == TETRIS_INCMD_PAUSE)))
|
||||
{
|
||||
++pIn->nLoopCycles;
|
||||
}
|
||||
|
||||
WAIT(TETRIS_INPUT_TICKS);
|
||||
if (cmdReturn != TETRIS_INCMD_NONE)
|
||||
{
|
||||
return cmdReturn;
|
||||
}
|
||||
}
|
||||
|
||||
// since we have left the loop we reset the cycle counter
|
||||
pIn->nLoopCycles = 0;
|
||||
|
||||
return TETRIS_INCMD_GRAVITY;
|
||||
}
|
||||
|
||||
|
||||
/* Function: tetris_input_setLevel
|
||||
* Description: modifies time interval of input events
|
||||
* Argument pIn: pointer to an input object
|
||||
* Argument nLvl: desired level (0 <= nLvl <= TETRIS_INPUT_LEVELS - 1)
|
||||
* Return value: void
|
||||
*/
|
||||
void tetris_input_setLevel(tetris_input_t *pIn,
|
||||
uint8_t nLvl)
|
||||
{
|
||||
assert(pIn != NULL);
|
||||
assert(nLvl <= TETRIS_INPUT_LEVELS - 1);
|
||||
if (pIn->nLevel != nLvl)
|
||||
{
|
||||
pIn->nLevel = nLvl;
|
||||
pIn->nMaxCycles = 400 / (nLvl + 2);
|
||||
}
|
||||
}
|
||||
129
games/tetris/input.h
Normal file
129
games/tetris/input.h
Normal file
|
|
@ -0,0 +1,129 @@
|
|||
#ifndef INPUT_H_
|
||||
#define INPUT_H_
|
||||
|
||||
#include <inttypes.h>
|
||||
|
||||
|
||||
/***********
|
||||
* defines *
|
||||
***********/
|
||||
|
||||
// number of levels
|
||||
#define TETRIS_INPUT_LEVELS 20
|
||||
|
||||
|
||||
/*********
|
||||
* types *
|
||||
*********/
|
||||
|
||||
typedef enum tetris_input_command_t
|
||||
{
|
||||
TETRIS_INCMD_ROT_CW, // rotate clockwise
|
||||
TETRIS_INCMD_ROT_CCW, // rotate counter clockwise
|
||||
TETRIS_INCMD_LEFT, // move piece left
|
||||
TETRIS_INCMD_RIGHT, // move piece right
|
||||
TETRIS_INCMD_DOWN, // lower piece by one row
|
||||
TETRIS_INCMD_DROP, // move piece to the ground immediately
|
||||
TETRIS_INCMD_GRAVITY, // piece gets pulled by gravity
|
||||
TETRIS_INCMD_PAUSE, // pause the game
|
||||
TETRIS_INCMD_NONE // idle (must alway be the last one)
|
||||
}
|
||||
tetris_input_command_t;
|
||||
|
||||
|
||||
typedef enum tetris_input_pace_t
|
||||
{
|
||||
TETRIS_INPACE_HOVERING, // normal falling pace
|
||||
TETRIS_INPACE_GLIDING /* guarantees a minimum docking time to avoid that
|
||||
pieces are docked immediately if they hit something
|
||||
in higher levels */
|
||||
}
|
||||
tetris_input_pace_t;
|
||||
|
||||
|
||||
typedef struct tetris_input_t
|
||||
{
|
||||
// current level (determines falling speed)
|
||||
uint8_t nLevel;
|
||||
|
||||
// Amount of loop cycles between forced piece movements. This value gets
|
||||
// set via the tetris_input_setLevel() function.
|
||||
uint8_t nMaxCycles;
|
||||
|
||||
// This counter keeps track of the number of loop cycles whoch have been
|
||||
// done since the last forced piece movement. It gets reset if it either
|
||||
// reaches a well defined value (causing a gravity command to be issued)
|
||||
// or the player has moved down the piece herself/himself.
|
||||
uint8_t nLoopCycles;
|
||||
|
||||
// Amount of loop cycles in which the same command has been issued
|
||||
// consecutively. It gets reset if either the current command differs from
|
||||
// the last one or a well-defined value has been reached (thereby
|
||||
// regulating the pace of the key repeat as commands are only processed
|
||||
// if that value is reached).
|
||||
int8_t nRepeatCount;
|
||||
|
||||
// Keeps track of the number loop cycles which have been run while in
|
||||
// pause mode. As soon as a well defined value is reached, the game
|
||||
// continues (in case someone paused the game and forgot to resume it).
|
||||
uint16_t nPauseCount;
|
||||
|
||||
|
||||
// last command (important for key repeat)
|
||||
tetris_input_command_t cmdLast;
|
||||
|
||||
// Every command has its own counter. A command is ignored as long as its
|
||||
// counter is unequal to 0. A counter gets set to a specific value (or 0)
|
||||
// if the button of the corresponding command has been released by the
|
||||
// player. All counters get decremented by one every loop cycle until they
|
||||
// are zero. This is used to work against joystick chatter. Look at the
|
||||
// TETRIS_INPUT_CHATTER_TICKS_... constants in input.c for the initial
|
||||
// values of these counters.
|
||||
uint8_t nIgnoreCmdCounter[TETRIS_INCMD_NONE];
|
||||
}
|
||||
tetris_input_t;
|
||||
|
||||
/****************************
|
||||
* construction/destruction *
|
||||
****************************/
|
||||
|
||||
/* Function: tetris_input_construct
|
||||
* Description: constructs an input object for André's borg
|
||||
* Return value: pointer to a newly created input object
|
||||
*/
|
||||
tetris_input_t *tetris_input_construct();
|
||||
|
||||
|
||||
/* Function: tetris_input_destruct
|
||||
* Description: destructs an input object
|
||||
* Argument pIn: pointer to the input object which should be destructed
|
||||
* Return value: void
|
||||
*/
|
||||
void tetris_input_destruct(tetris_input_t *pIn);
|
||||
|
||||
|
||||
/***************************
|
||||
* input related functions *
|
||||
***************************/
|
||||
|
||||
/* Function: retris_input_getCommand
|
||||
* Description: retrieves commands from joystick or loop interval
|
||||
* Argument pIn: pointer to an input object
|
||||
* Argument nPace: falling pace (see definition of tetris_input_pace_t)
|
||||
* Return value: see definition of tetris_input_command_t
|
||||
*/
|
||||
tetris_input_command_t tetris_input_getCommand(tetris_input_t *pIn,
|
||||
tetris_input_pace_t nPace);
|
||||
|
||||
|
||||
/* Function: tetris_input_setLevel
|
||||
* Description: modifies time interval of input events
|
||||
* Argument pIn: pointer to an input object
|
||||
* Argument nLvl: desired level (0 <= nLvl <= TETRIS_INPUT_LEVELS - 1)
|
||||
* Return value: void
|
||||
*/
|
||||
void tetris_input_setLevel(tetris_input_t *pIn,
|
||||
uint8_t nLvl);
|
||||
|
||||
|
||||
#endif /*INPUT_H_*/
|
||||
467
games/tetris/logic.c
Normal file
467
games/tetris/logic.c
Normal file
|
|
@ -0,0 +1,467 @@
|
|||
/* Borgtris
|
||||
* by: Christian Kroll
|
||||
* date: Tuesday, 2007/09/16
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
#ifdef __AVR__
|
||||
#include <avr/eeprom.h>
|
||||
#include <avr/interrupt.h>
|
||||
#endif
|
||||
|
||||
#include "logic.h"
|
||||
#include "piece.h"
|
||||
#include "playfield.h"
|
||||
#include "view.h"
|
||||
#include "input.h"
|
||||
#include "../prng.h"
|
||||
|
||||
|
||||
#ifdef EEMEM
|
||||
/***********************
|
||||
* Highscore in EEPROM *
|
||||
***********************/
|
||||
|
||||
uint16_t tetris_logic_nHighscore EEMEM;
|
||||
#endif
|
||||
|
||||
/***************************
|
||||
* non-interface functions *
|
||||
***************************/
|
||||
|
||||
/* Function: tetris_logic_calculateLines
|
||||
* Description: calculates number of lines for the given row mask
|
||||
* Argument nRowMask: row mask from which the no. of lines will be calculated
|
||||
* Return value: number of lines of the row mask
|
||||
*/
|
||||
uint8_t tetris_logic_calculateLines(uint8_t nRowMask)
|
||||
{
|
||||
uint8_t nMask = 0x0001;
|
||||
uint8_t nLines = 0;
|
||||
for (uint8_t i = 0; i < 4; ++i)
|
||||
{
|
||||
if ((nMask & nRowMask) != 0)
|
||||
{
|
||||
++nLines;
|
||||
}
|
||||
nMask <<= 1;
|
||||
}
|
||||
|
||||
return nLines;
|
||||
}
|
||||
|
||||
uint16_t tetris_logic_retrieveHighscore(void)
|
||||
{
|
||||
#ifdef EEMEM
|
||||
uint16_t nHighscore = 0;
|
||||
nHighscore = eeprom_read_word(&tetris_logic_nHighscore);
|
||||
|
||||
// a score of 65535 is most likely caused by uninitialized EEPROM addresses
|
||||
if (nHighscore == 65535)
|
||||
{
|
||||
nHighscore = 0;
|
||||
}
|
||||
|
||||
return nHighscore;
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
void tetris_logic_saveHighscore(uint16_t nHighscore)
|
||||
{
|
||||
#ifdef EEMEM
|
||||
if (nHighscore > tetris_logic_retrieveHighscore())
|
||||
{
|
||||
eeprom_write_word(&tetris_logic_nHighscore, nHighscore);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/****************************
|
||||
* construction/destruction *
|
||||
****************************/
|
||||
|
||||
/* Function: tetris_logic_construct
|
||||
* Description: constructs a logic object
|
||||
* Return value: pointer to a newly created logic object
|
||||
*/
|
||||
tetris_logic_t *tetris_logic_construct()
|
||||
{
|
||||
tetris_logic_t *pLogic = (tetris_logic_t *)malloc(sizeof(tetris_logic_t));
|
||||
assert(pLogic != NULL);
|
||||
memset(pLogic, 0, sizeof(tetris_logic_t));
|
||||
return pLogic;
|
||||
}
|
||||
|
||||
/* Function: tetris_logic_destruct
|
||||
* Description: destructs a logic object
|
||||
* Argument pIn: pointer to the logic object to be destructed
|
||||
* Return value: void
|
||||
*/
|
||||
void tetris_logic_destruct(tetris_logic_t *pLogic)
|
||||
{
|
||||
assert(pLogic != 0);
|
||||
free(pLogic);
|
||||
}
|
||||
|
||||
|
||||
/***************************
|
||||
* logic related functions *
|
||||
***************************/
|
||||
|
||||
/* Function: tetris
|
||||
* Description: runs the tetris game
|
||||
* Return value: void
|
||||
*/
|
||||
void tetris ()
|
||||
{
|
||||
// get view dependent dimensions of the playfield
|
||||
int8_t nWidth;
|
||||
int8_t nHeight;
|
||||
tetris_view_getDimensions(&nWidth, &nHeight);
|
||||
|
||||
// holds the current user command which should be processed
|
||||
tetris_input_command_t inCmd;
|
||||
|
||||
// prepare data structures that drive the game...
|
||||
tetris_logic_t *pLogic = tetris_logic_construct();
|
||||
tetris_playfield_t *pPl = tetris_playfield_construct(nWidth, nHeight);
|
||||
tetris_input_t *pIn = tetris_input_construct();
|
||||
tetris_view_t *pView = tetris_view_construct(pLogic, pPl);
|
||||
|
||||
// runtime variable
|
||||
int8_t nPieceRow;
|
||||
|
||||
// retrieve highscore
|
||||
static uint16_t nHighscore = 0;
|
||||
if (nHighscore == 0)
|
||||
{
|
||||
nHighscore = tetris_logic_retrieveHighscore();
|
||||
}
|
||||
|
||||
// initialize current and next piece
|
||||
tetris_piece_t *pPiece = NULL;
|
||||
tetris_piece_t *pNextPiece = pPiece =
|
||||
tetris_piece_construct(random8() % 7, TETRIS_PC_ANGLE_0);
|
||||
|
||||
// the view only monitors the logic and the playfield object for the game
|
||||
// status so we must put information like the next piece or the current
|
||||
// highscore to a place where the view can find it
|
||||
tetris_logic_setHighscore(pLogic, nHighscore);
|
||||
tetris_logic_setPreviewPiece(pLogic, pNextPiece);
|
||||
|
||||
// pace flag
|
||||
tetris_input_pace_t inPace;
|
||||
|
||||
// game loop, runs as long as the game is not over
|
||||
while (tetris_playfield_getStatus(pPl) != TETRIS_PFS_GAMEOVER)
|
||||
{
|
||||
// what we do strongly depends on the status of the playfield
|
||||
switch (tetris_playfield_getStatus(pPl))
|
||||
{
|
||||
// the playfield awaits a new piece
|
||||
case TETRIS_PFS_READY:
|
||||
// make preview piece the current piece and create new preview piece
|
||||
pPiece = pNextPiece;
|
||||
pNextPiece =
|
||||
tetris_piece_construct(random8() % 7, TETRIS_PC_ANGLE_0);
|
||||
tetris_logic_setPreviewPiece(pLogic, pNextPiece);
|
||||
|
||||
// insert new piece into playfield
|
||||
tetris_piece_t *pOldPiece;
|
||||
tetris_playfield_insertPiece(pPl, pPiece, &pOldPiece);
|
||||
|
||||
// destruct old piece (if it exists) since we don't need it anymore
|
||||
if (pOldPiece != NULL)
|
||||
{
|
||||
tetris_piece_destruct(pOldPiece);
|
||||
pOldPiece = NULL;
|
||||
}
|
||||
break;
|
||||
|
||||
// a piece is hovering and can be controlled by the player
|
||||
case TETRIS_PFS_HOVERING:
|
||||
case TETRIS_PFS_GLIDING:
|
||||
// if the piece is gliding the input module has to grant us
|
||||
// a minimum amount of time to move it
|
||||
if (tetris_playfield_getStatus(pPl) == TETRIS_PFS_GLIDING)
|
||||
{
|
||||
inPace = TETRIS_INPACE_GLIDING;
|
||||
}
|
||||
else
|
||||
{
|
||||
inPace = TETRIS_INPACE_HOVERING;
|
||||
}
|
||||
|
||||
// ensure correct view mode if the game isn't paused
|
||||
if ((inCmd = tetris_input_getCommand(pIn, inPace))
|
||||
!= TETRIS_INCMD_PAUSE)
|
||||
{
|
||||
tetris_view_setViewMode(pView, TETRIS_VIMO_RUNNING);
|
||||
}
|
||||
|
||||
// what we do depends on what the input module tells us
|
||||
switch (inCmd)
|
||||
{
|
||||
// game paused?
|
||||
case TETRIS_INCMD_PAUSE:
|
||||
// tell the view it should display the pause screen
|
||||
tetris_view_setViewMode(pView, TETRIS_VIMO_PAUSED);
|
||||
break;
|
||||
|
||||
// the piece was pulled down by the almighty gravity
|
||||
case TETRIS_INCMD_GRAVITY:
|
||||
tetris_playfield_advancePiece(pPl);
|
||||
break;
|
||||
|
||||
// the player has pulled down the piece herself/himself
|
||||
case TETRIS_INCMD_DOWN:
|
||||
tetris_playfield_advancePiece(pPl);
|
||||
// if the game still runs, reward the player with extra points
|
||||
if (tetris_playfield_getStatus(pPl) != TETRIS_PFS_GAMEOVER)
|
||||
{
|
||||
tetris_logic_singleDrop(pLogic, 1);
|
||||
}
|
||||
break;
|
||||
|
||||
// player shifted the piece to the left
|
||||
case TETRIS_INCMD_LEFT:
|
||||
tetris_playfield_movePiece(pPl, TETRIS_PFD_LEFT);
|
||||
break;
|
||||
|
||||
// player shifted the piece to the right
|
||||
case TETRIS_INCMD_RIGHT:
|
||||
tetris_playfield_movePiece(pPl, TETRIS_PFD_RIGHT);
|
||||
break;
|
||||
|
||||
// player rotated the piece clockwise
|
||||
case TETRIS_INCMD_ROT_CW:
|
||||
tetris_playfield_rotatePiece(pPl, TETRIS_PC_ROT_CW);
|
||||
break;
|
||||
|
||||
// player rotated the piece counter clockwise
|
||||
case TETRIS_INCMD_ROT_CCW:
|
||||
tetris_playfield_rotatePiece(pPl, TETRIS_PC_ROT_CCW);
|
||||
break;
|
||||
|
||||
// the player decided to make an immediate drop
|
||||
case TETRIS_INCMD_DROP:
|
||||
nPieceRow = tetris_playfield_getRow(pPl);
|
||||
// emulate immediate drop
|
||||
while((tetris_playfield_getStatus(pPl) == TETRIS_PFS_GLIDING) ||
|
||||
(tetris_playfield_getStatus(pPl) == TETRIS_PFS_HOVERING))
|
||||
{
|
||||
tetris_playfield_advancePiece(pPl);
|
||||
}
|
||||
// if the game still runs, reward the player with extra points
|
||||
if (tetris_playfield_getStatus(pPl) != TETRIS_PFS_GAMEOVER)
|
||||
{
|
||||
tetris_logic_completeDrop(pLogic,
|
||||
tetris_playfield_getRow(pPl) - nPieceRow);
|
||||
}
|
||||
break;
|
||||
|
||||
// avoid compiler warnings
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
// the piece has irrevocably hit the ground
|
||||
case TETRIS_PFS_DOCKED:
|
||||
// remove complete lines (if any)
|
||||
tetris_playfield_removeCompleteLines(pPl);
|
||||
|
||||
// let the logic object decide how many points the player gets
|
||||
// and whether the level gets changed
|
||||
tetris_logic_removedLines(pLogic, tetris_playfield_getRowMask(pPl));
|
||||
tetris_input_setLevel(pIn, tetris_logic_getLevel(pLogic));
|
||||
break;
|
||||
|
||||
// avoid compiler warnings
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// the view updates it state every loop cycle to make changes visible
|
||||
tetris_view_update(pView);
|
||||
}
|
||||
|
||||
// game is over and we provide the player with her/his results
|
||||
tetris_view_showResults(pView);
|
||||
|
||||
// update highscore if it has been beaten
|
||||
uint16_t nScore = tetris_logic_getScore(pLogic);
|
||||
if (nScore > nHighscore)
|
||||
{
|
||||
nHighscore = nScore;
|
||||
tetris_logic_saveHighscore(nHighscore);
|
||||
}
|
||||
|
||||
// clean up
|
||||
tetris_view_destruct(pView);
|
||||
tetris_input_destruct(pIn);
|
||||
tetris_playfield_destruct(pPl);
|
||||
tetris_logic_destruct(pLogic);
|
||||
tetris_piece_destruct(pPiece);
|
||||
tetris_piece_destruct(pNextPiece);
|
||||
}
|
||||
|
||||
|
||||
/* Function: tetris_logic_singleDrop
|
||||
* Description: add points which result from single step dropping
|
||||
* Argument pLogic: the logic object we want to modify
|
||||
* Argument nLines: the number of rows involved
|
||||
* Return value: void
|
||||
*/
|
||||
void tetris_logic_singleDrop(tetris_logic_t *pLogic,
|
||||
uint8_t nLines)
|
||||
{
|
||||
assert(pLogic != 0);
|
||||
pLogic->nScore += nLines;
|
||||
}
|
||||
|
||||
|
||||
/* Function: tetris_logic_completeDrop
|
||||
* Description: add points which result from a complete drop
|
||||
* Argument pLogic: the logic object we want to modify
|
||||
* Argument nLines: the number of rows involved
|
||||
* Return value: void
|
||||
*/
|
||||
void tetris_logic_completeDrop(tetris_logic_t *pLogic,
|
||||
uint8_t nLines)
|
||||
{
|
||||
assert(pLogic != 0);
|
||||
pLogic->nScore += nLines * 2;
|
||||
}
|
||||
|
||||
|
||||
/* Function: tetris_logic_removedLines
|
||||
* Description: add points which result from removed rows
|
||||
* Argument pLogic: the logic object we want to modify
|
||||
* Argument nRowMask: see tetris_playfield_completeLines
|
||||
* Return value: void
|
||||
*/
|
||||
void tetris_logic_removedLines(tetris_logic_t *pLogic,
|
||||
uint8_t nRowMask)
|
||||
{
|
||||
assert(pLogic != 0);
|
||||
uint8_t nLines = tetris_logic_calculateLines(nRowMask);
|
||||
pLogic->nLines += nLines;
|
||||
pLogic->nLevel = ((pLogic->nLines / 10) < TETRIS_INPUT_LEVELS) ?
|
||||
(pLogic->nLines / 10) : (TETRIS_INPUT_LEVELS - 1);
|
||||
|
||||
switch (nLines)
|
||||
{
|
||||
case 1:
|
||||
pLogic->nScore += 50;
|
||||
break;
|
||||
case 2:
|
||||
pLogic->nScore += 150;
|
||||
break;
|
||||
case 3:
|
||||
pLogic->nScore += 250;
|
||||
break;
|
||||
case 4:
|
||||
pLogic->nScore += 400;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*****************
|
||||
* get functions *
|
||||
*****************/
|
||||
|
||||
/* Function: tetris_logic_getScore
|
||||
* Description: returns the current score
|
||||
* Argument pLogic: the logic object we want information from
|
||||
* Return value: the score as uint16_t
|
||||
*/
|
||||
uint16_t tetris_logic_getScore(tetris_logic_t *pLogic)
|
||||
{
|
||||
assert(pLogic != NULL);
|
||||
return pLogic->nScore;
|
||||
}
|
||||
|
||||
|
||||
/* Function: tetris_logic_getHighscore
|
||||
* Description: returns the current highscore
|
||||
* Argument pLogic: the logic object we want information from
|
||||
* Return value: the highscore as uint16_t
|
||||
*/
|
||||
|
||||
uint16_t tetris_logic_getHighscore(tetris_logic_t *pLogic)
|
||||
{
|
||||
assert(pLogic != NULL);
|
||||
return pLogic->nHighscore;
|
||||
}
|
||||
|
||||
|
||||
/* Function: tetris_logic_setHighscore
|
||||
* Description: set highscore
|
||||
* Argument pLogic: the logic object we want to modify
|
||||
* Argmument nHighscore: highscore
|
||||
*/
|
||||
void tetris_logic_setHighscore(tetris_logic_t *pLogic,
|
||||
uint16_t nHighscore)
|
||||
{
|
||||
assert(pLogic != NULL);
|
||||
pLogic->nHighscore = nHighscore;
|
||||
}
|
||||
|
||||
|
||||
/* Function: tetris_logic_getLevel
|
||||
* Description: returns the current level
|
||||
* Argument pLogic: the logic object we want information from
|
||||
* Return value: the level as uint8_t
|
||||
*/
|
||||
uint8_t tetris_logic_getLevel(tetris_logic_t *pLogic)
|
||||
{
|
||||
assert(pLogic != NULL);
|
||||
return pLogic->nLevel;
|
||||
}
|
||||
|
||||
|
||||
/* Function: tetris_logic_getLines
|
||||
* Description: returns the number of completed lines
|
||||
* Argument pLogic: the logic object we want information from
|
||||
* Return value: number of completed lines as uint16_t
|
||||
*/
|
||||
uint16_t tetris_logic_getLines(tetris_logic_t *pLogic)
|
||||
{
|
||||
assert(pLogic != NULL);
|
||||
return pLogic->nLines;
|
||||
}
|
||||
|
||||
|
||||
/* Function: tetris_logic_setPreviewPiece
|
||||
* Description: help for the view to determine the preview piece
|
||||
* Argument pLogic: the logic object we want to modify
|
||||
* Argument pPiece: pointer to piece intended to be the next one (may be NULL)
|
||||
* Return value: void
|
||||
*/
|
||||
void tetris_logic_setPreviewPiece(tetris_logic_t *pLogic,
|
||||
tetris_piece_t *pPiece)
|
||||
{
|
||||
assert(pLogic != NULL);
|
||||
pLogic->pPreviewPiece = pPiece;
|
||||
}
|
||||
|
||||
|
||||
/* Function: tetris_logic_getPreviewPiece
|
||||
* Description: returns piece which was set via tetris_logic_setPreviewPiece
|
||||
* Argument pLogic: the logic object we want information from
|
||||
* Return value: the piece intended to be the next one (may be NULL)
|
||||
*/
|
||||
tetris_piece_t* tetris_logic_getPreviewPiece(tetris_logic_t *pLogic)
|
||||
{
|
||||
assert(pLogic != NULL);
|
||||
return pLogic->pPreviewPiece;
|
||||
}
|
||||
|
||||
143
games/tetris/logic.h
Normal file
143
games/tetris/logic.h
Normal file
|
|
@ -0,0 +1,143 @@
|
|||
#ifndef TETRIS_LOGIC_H_
|
||||
#define TETRIS_LOGIC_H_
|
||||
|
||||
#include <inttypes.h>
|
||||
#include "piece.h"
|
||||
|
||||
/*********
|
||||
* types *
|
||||
*********/
|
||||
|
||||
typedef struct tetris_logic_t
|
||||
{
|
||||
uint16_t nScore; // score of the player
|
||||
uint16_t nHighscore; // highscore
|
||||
uint8_t nLevel; // current level
|
||||
uint16_t nLines; // number of completed lines
|
||||
tetris_piece_t *pPreviewPiece; // the piece intended to be the next one
|
||||
}
|
||||
tetris_logic_t;
|
||||
|
||||
/****************************
|
||||
* construction/destruction *
|
||||
****************************/
|
||||
|
||||
/* Function: tetris_logic_construct
|
||||
* Description: constructs a logic object
|
||||
* Return value: pointer to a newly created logic object
|
||||
*/
|
||||
tetris_logic_t *tetris_logic_construct();
|
||||
|
||||
/* Function: tetris_logic_destruct
|
||||
* Description: destructs a logic object
|
||||
* Argument pIn: pointer to the logic object to be destructed
|
||||
* Return value: void
|
||||
*/
|
||||
void tetris_logic_destruct(tetris_logic_t *pLogic);
|
||||
|
||||
/***************************
|
||||
* logic related functions *
|
||||
***************************/
|
||||
|
||||
/* Function: tetris
|
||||
* Description: runs the tetris game
|
||||
* Return value: void
|
||||
*/
|
||||
void tetris();
|
||||
|
||||
|
||||
/* Function: tetris_logic_singleDrop
|
||||
* Description: add points which result from single step dropping
|
||||
* Argument pLogic: the logic object we want to modify
|
||||
* Argument nLines: the number of rows involved
|
||||
* Return value: void
|
||||
*/
|
||||
void tetris_logic_singleDrop(tetris_logic_t *pLogic,
|
||||
uint8_t nLines);
|
||||
|
||||
|
||||
/* Function: tetris_logic_completeDrop
|
||||
* Description: add points which result from a complete drop
|
||||
* Argument pLogic: the logic object we want to modify
|
||||
* Argument nLines: the number of rows involved
|
||||
* Return value: void
|
||||
*/
|
||||
void tetris_logic_completeDrop(tetris_logic_t *pLogic,
|
||||
uint8_t nLines);
|
||||
|
||||
|
||||
/* Function: tetris_logic_removedLines
|
||||
* Description: add points which result from removed rows
|
||||
* Argument pLogic: the logic object we want to modify
|
||||
* Argument nRowMask: see tetris_playfield_completeLines
|
||||
* Return value: void
|
||||
*/
|
||||
void tetris_logic_removedLines(tetris_logic_t *pLogic,
|
||||
uint8_t nRowMask);
|
||||
|
||||
|
||||
/*********************
|
||||
* get/set functions *
|
||||
*********************/
|
||||
|
||||
/* Function: tetris_logic_getScore
|
||||
* Description: returns the current score
|
||||
* Argument pLogic: the logic object we want information from
|
||||
* Return value: the score as uint16_t
|
||||
*/
|
||||
uint16_t tetris_logic_getScore(tetris_logic_t *pLogic);
|
||||
|
||||
|
||||
/* Function: tetris_logic_getHighscore
|
||||
* Description: returns the current highscore
|
||||
* Argument pLogic: the logic object we want information from
|
||||
* Return value: the highscore as uint16_t
|
||||
*/
|
||||
uint16_t tetris_logic_getHighscore(tetris_logic_t *pLogic);
|
||||
|
||||
|
||||
/* Function: tetris_logic_setHighscore
|
||||
* Description: set highscore
|
||||
* Argument pLogic: the logic object we want to modify
|
||||
* Argmument nHighscore: highscore
|
||||
*/
|
||||
void tetris_logic_setHighscore(tetris_logic_t *pLogic,
|
||||
uint16_t nHighscore);
|
||||
|
||||
|
||||
/* Function: tetris_logic_getLevel
|
||||
* Description: returns the current level
|
||||
* Argument pLogic: the logic object we want information from
|
||||
* Return value: the level as uint8_t
|
||||
*/
|
||||
uint8_t tetris_logic_getLevel(tetris_logic_t *pLogic);
|
||||
|
||||
|
||||
/* Function: tetris_logic_getLines
|
||||
* Description: returns the number of completed lines
|
||||
* Argument pLogic: the logic object we want information from
|
||||
* Return value: number of completed lines as uint16_t
|
||||
*/
|
||||
uint16_t tetris_logic_getLines(tetris_logic_t *pLogic);
|
||||
|
||||
|
||||
/* Function: tetris_logic_setPreviewPiece
|
||||
* Description: help for the view to determine the preview piece
|
||||
* Argument pLogic: the logic object we want to modify
|
||||
* Argument pPiece: pointer to piece intended to be the next one
|
||||
* Return value: void
|
||||
*/
|
||||
void tetris_logic_setPreviewPiece(tetris_logic_t *pLogic,
|
||||
tetris_piece_t *pPiece);
|
||||
|
||||
|
||||
/* Function: tetris_logic_getPreviewPiece
|
||||
* Description: returns piece which was set via tetris_logic_setPreviewPiece
|
||||
* Argument pLogic: the logic object we want information from
|
||||
* Return value: the piece intended to be the next one
|
||||
*/
|
||||
tetris_piece_t* tetris_logic_getPreviewPiece(tetris_logic_t *pLogic);
|
||||
|
||||
|
||||
#endif /*TETRIS_LOGIC_H_*/
|
||||
|
||||
119
games/tetris/piece.c
Normal file
119
games/tetris/piece.c
Normal file
|
|
@ -0,0 +1,119 @@
|
|||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
#include <inttypes.h>
|
||||
#include "piece.h"
|
||||
|
||||
#ifdef __AVR__
|
||||
#include <avr/pgmspace.h>
|
||||
#else
|
||||
#define PROGMEM
|
||||
#endif
|
||||
|
||||
|
||||
/*****************************
|
||||
* construction/destruction *
|
||||
*****************************/
|
||||
|
||||
/* Function: tetris_piece_construct
|
||||
* Description: constructs a piece with the given attributes
|
||||
* Argument s: shape of the piece (see tetris_piece_shape_t)
|
||||
* Argument a: its angle (see tetris_piece_angel_t)
|
||||
* Return value: pointer to a newly created piece
|
||||
*/
|
||||
tetris_piece_t *tetris_piece_construct(tetris_piece_shape_t s,
|
||||
tetris_piece_angle_t a)
|
||||
{
|
||||
tetris_piece_t *p_piece = (tetris_piece_t*) malloc (sizeof(tetris_piece_t));
|
||||
assert(p_piece != NULL);
|
||||
|
||||
p_piece->shape = s;
|
||||
p_piece->angle = a;
|
||||
|
||||
return p_piece;
|
||||
}
|
||||
|
||||
/* Function: tetris_piece_destruct
|
||||
* Description: destructs a piece
|
||||
* Argument pPc: pointer to the piece to be destructed
|
||||
* Return value: void
|
||||
*/
|
||||
void tetris_piece_destruct(tetris_piece_t *pPc)
|
||||
{
|
||||
assert(pPc != NULL);
|
||||
free(pPc);
|
||||
}
|
||||
|
||||
|
||||
/****************************
|
||||
* piece related functions *
|
||||
****************************/
|
||||
|
||||
/* Function: tetris_piece_getBitmap
|
||||
* Description: returns bitfield representation of the piece
|
||||
* Argument pPc: piece from which the bitfield shuld be retrieved
|
||||
* Return value: bitfield representation of the piece
|
||||
* - nth nibble is nth row of the piece (from upper left)
|
||||
* - the LSB of a nibble represents the left side of a row
|
||||
*/
|
||||
uint16_t tetris_piece_getBitmap(tetris_piece_t *pPc)
|
||||
{
|
||||
assert(pPc != NULL);
|
||||
|
||||
// Lookup table:
|
||||
// A value in an array represents a piece in a specific angle (rotating
|
||||
// clockwise from index 0).
|
||||
const static uint16_t piece[][4] PROGMEM =
|
||||
{{0x0F00, 0x2222, 0x0F00, 0x2222}, // LINE
|
||||
{0x4E00, 0x4640, 0x0E40, 0x4C40}, // T
|
||||
{0x0660, 0x0660, 0x0660, 0x0660}, // SQUARE
|
||||
{0x2E00, 0x88C0, 0x0E80, 0xC440}, // L
|
||||
{0x8E00, 0x6440, 0x0E20, 0x44C0}, // LBACK
|
||||
{0x6C00, 0x4620, 0x6C00, 0x4620}, // S
|
||||
{0xC600, 0x4C80, 0xC600, 0x4C80}}; // Z
|
||||
|
||||
#ifdef __AVR__
|
||||
return pgm_read_word(&piece[pPc->shape][pPc->angle]);
|
||||
#else
|
||||
return piece[pPc->shape][pPc->angle];
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
/* Function: tetris_piece_rotate
|
||||
* Description: rotates a piece
|
||||
* Argument pPc: piece to rotate
|
||||
* Argument r: type of rotation (see tetris_piece_rotation_t)
|
||||
* Return value: void
|
||||
*/
|
||||
void tetris_piece_rotate(tetris_piece_t *pPc,
|
||||
tetris_piece_rotation_t r)
|
||||
{
|
||||
assert(pPc != NULL);
|
||||
|
||||
// we just rotate through the available angles in the given direction and
|
||||
// make wrap arounds where appropriate
|
||||
switch (r)
|
||||
{
|
||||
case TETRIS_PC_ROT_CW:
|
||||
if (pPc->angle == TETRIS_PC_ANGLE_270)
|
||||
{
|
||||
pPc->angle = TETRIS_PC_ANGLE_0;
|
||||
}
|
||||
else
|
||||
{
|
||||
++pPc->angle;
|
||||
}
|
||||
break;
|
||||
case TETRIS_PC_ROT_CCW:
|
||||
if (pPc->angle == TETRIS_PC_ANGLE_0)
|
||||
{
|
||||
pPc->angle = TETRIS_PC_ANGLE_270;
|
||||
}
|
||||
else
|
||||
{
|
||||
--pPc->angle;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
97
games/tetris/piece.h
Normal file
97
games/tetris/piece.h
Normal file
|
|
@ -0,0 +1,97 @@
|
|||
#ifndef TETRIS_PIECE_H_
|
||||
#define TETRIS_PIECE_H_
|
||||
|
||||
#include <inttypes.h>
|
||||
|
||||
|
||||
/*********
|
||||
* types *
|
||||
*********/
|
||||
|
||||
typedef enum tetris_piece_shape_t
|
||||
{
|
||||
TETRIS_PC_LINE,
|
||||
TETRIS_PC_T,
|
||||
TETRIS_PC_SQUARE,
|
||||
TETRIS_PC_L,
|
||||
TETRIS_PC_LBACK,
|
||||
TETRIS_PC_S,
|
||||
TETRIS_PC_Z
|
||||
}
|
||||
tetris_piece_shape_t;
|
||||
|
||||
|
||||
typedef enum tetris_piece_angle_t
|
||||
{
|
||||
TETRIS_PC_ANGLE_0,
|
||||
TETRIS_PC_ANGLE_90,
|
||||
TETRIS_PC_ANGLE_180,
|
||||
TETRIS_PC_ANGLE_270
|
||||
}
|
||||
tetris_piece_angle_t;
|
||||
|
||||
|
||||
typedef enum tetris_piece_rotation_t
|
||||
{
|
||||
TETRIS_PC_ROT_CW, // clockwise rotation
|
||||
TETRIS_PC_ROT_CCW // counter clockwise rotation
|
||||
}
|
||||
tetris_piece_rotation_t;
|
||||
|
||||
|
||||
typedef struct tetris_piece_t
|
||||
{
|
||||
tetris_piece_shape_t shape; // specifies the shape of the piece
|
||||
tetris_piece_angle_t angle; // specifies one of 4 angels
|
||||
}
|
||||
tetris_piece_t;
|
||||
|
||||
|
||||
/*****************************
|
||||
* construction/destruction *
|
||||
*****************************/
|
||||
|
||||
/* Function: tetris_piece_construct
|
||||
* Description: constructs a piece with the given attributes
|
||||
* Argument s: shape of the piece (see tetris_piece_shape_t)
|
||||
* Argument a: its angle (see tetris_piece_angel_t)
|
||||
* Return value: pointer to a newly created piece
|
||||
*/
|
||||
tetris_piece_t *tetris_piece_construct(tetris_piece_shape_t s,
|
||||
tetris_piece_angle_t a);
|
||||
|
||||
|
||||
/* Function: tetris_piece_destruct
|
||||
* Description: destructs a piece
|
||||
* Argument pPc: pointer to the piece to be destructed
|
||||
* Return value: void
|
||||
*/
|
||||
void tetris_piece_destruct(tetris_piece_t *pPc);
|
||||
|
||||
|
||||
/****************************
|
||||
* piece related functions *
|
||||
****************************/
|
||||
|
||||
/* Function: tetris_piece_getBitmap
|
||||
* Description: returns bitfield representation of the piece
|
||||
* Argument pPc: piece from which the bitfield shuld be retrieved
|
||||
* Return value: bitfield representation of the piece
|
||||
* - nth nibble is nth row of the piece (from upper left)
|
||||
* - the LSB of a nibble represents the left side of a row
|
||||
*/
|
||||
uint16_t tetris_piece_getBitmap(tetris_piece_t *pPc);
|
||||
|
||||
|
||||
/* Function: tetris_piece_rotate
|
||||
* Description: rotates a piece
|
||||
* Argument pPc: piece to rotate
|
||||
* Argument r: type of rotation (see tetris_piece_rotation_t)
|
||||
* Return value: void
|
||||
*/
|
||||
void tetris_piece_rotate(tetris_piece_t *pPc,
|
||||
tetris_piece_rotation_t r);
|
||||
|
||||
|
||||
#endif /*TETRIS_PIECE_H_*/
|
||||
|
||||
595
games/tetris/playfield.c
Normal file
595
games/tetris/playfield.c
Normal file
|
|
@ -0,0 +1,595 @@
|
|||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <inttypes.h>
|
||||
#include "playfield.h"
|
||||
#include "piece.h"
|
||||
|
||||
|
||||
/***************************
|
||||
* non-interface functions *
|
||||
***************************/
|
||||
|
||||
/* Function: tetris_playfield_hoverStatus;
|
||||
* Description: determines if piece is either hovering or gliding
|
||||
* Argument pPl: playfield perform action on
|
||||
* Return value: TETRIS_PFS_HOVERING or TETRIS_PFS_GLIDING
|
||||
*/
|
||||
tetris_playfield_status_t tetris_playfield_hoverStatus(tetris_playfield_t* pPl)
|
||||
{
|
||||
// if the piece touches the dump we ensure that the status is "gliding"
|
||||
if (tetris_playfield_collision(pPl, pPl->nColumn, pPl->nRow + 1))
|
||||
{
|
||||
return TETRIS_PFS_GLIDING;
|
||||
}
|
||||
// otherwise the status must be "hovering"
|
||||
else
|
||||
{
|
||||
return TETRIS_PFS_HOVERING;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/****************************
|
||||
* construction/destruction *
|
||||
****************************/
|
||||
|
||||
/* Function: tetris_playfield_construct
|
||||
* Description: constructs a playfield with the given diemensions
|
||||
* Argument nWidth: width of playfield (4 <= n <= 16)
|
||||
* Argument nHeight: height of playfield (4 <= n <= 124)
|
||||
* Return value: pointer to a newly created playfield
|
||||
*/
|
||||
tetris_playfield_t *tetris_playfield_construct(int8_t nWidth,
|
||||
int8_t nHeight)
|
||||
{
|
||||
assert((nWidth >= 4) && (nWidth <= 16));
|
||||
assert((nHeight >= 4) && (nHeight <= 124));
|
||||
|
||||
tetris_playfield_t *pPlayfield =
|
||||
(tetris_playfield_t*) malloc(sizeof(tetris_playfield_t));
|
||||
|
||||
if (pPlayfield != NULL)
|
||||
{
|
||||
// allocating mem for dump array
|
||||
pPlayfield->dump = (uint16_t*) calloc(nHeight, sizeof(uint16_t));
|
||||
|
||||
if (pPlayfield->dump != NULL)
|
||||
{
|
||||
// setting desired attributes
|
||||
pPlayfield->nWidth = nWidth;
|
||||
pPlayfield->nHeight = nHeight;
|
||||
tetris_playfield_reset(pPlayfield);
|
||||
|
||||
return pPlayfield;
|
||||
}
|
||||
else
|
||||
{
|
||||
free(pPlayfield);
|
||||
pPlayfield = NULL;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/* Function: tetris_playfield_destruct
|
||||
* Description: destructs a playfield
|
||||
* Argument pPl: pointer to the playfield to be destructed
|
||||
* Return value: void
|
||||
*/
|
||||
void tetris_playfield_destruct(tetris_playfield_t *pPl)
|
||||
{
|
||||
assert(pPl != NULL);
|
||||
|
||||
// if memory for the dump array has been allocated, free it
|
||||
if (pPl->dump != NULL)
|
||||
{
|
||||
free(pPl->dump);
|
||||
}
|
||||
free(pPl);
|
||||
}
|
||||
|
||||
|
||||
/*******************************
|
||||
* playfield related functions *
|
||||
*******************************/
|
||||
|
||||
/* Function: tetris_playfield_reset
|
||||
* Description: resets playfield to begin a new game
|
||||
* Argument pPl: playfield to perform action on
|
||||
* Return value: void
|
||||
*/
|
||||
void tetris_playfield_reset(tetris_playfield_t *pPl)
|
||||
{
|
||||
assert(pPl != NULL);
|
||||
|
||||
pPl->pPiece = NULL;
|
||||
pPl->nColumn = 0;
|
||||
pPl->nRow = 0;
|
||||
pPl->nRowMask = 0;
|
||||
|
||||
// clear dump if it has been allocated in memory
|
||||
if (pPl->dump != NULL)
|
||||
{
|
||||
memset(pPl->dump, 0, pPl->nHeight);
|
||||
}
|
||||
|
||||
pPl->status = TETRIS_PFS_READY;
|
||||
}
|
||||
|
||||
|
||||
/* Function: tetris_playfield_insertPiece
|
||||
* Description: inserts a new piece
|
||||
* Argument pPl: playfield to perform action on
|
||||
* Argument pPiece: piece to be inserted
|
||||
* Argument ppOldPiece: [out] indirect pointer to former piece for deallocation
|
||||
* Return value: void
|
||||
*/
|
||||
void tetris_playfield_insertPiece(tetris_playfield_t *pPl,
|
||||
tetris_piece_t *pPiece,
|
||||
tetris_piece_t** ppOldPiece)
|
||||
{
|
||||
assert((pPl != NULL) && (pPiece != NULL) && (ppOldPiece != NULL));
|
||||
|
||||
// a piece can only be inserted in state TETRIS_PFS_READY
|
||||
assert(pPl->status == TETRIS_PFS_READY);
|
||||
|
||||
// row mask is now meaningless
|
||||
pPl->nRowMask = 0;
|
||||
|
||||
// replace old piece
|
||||
*ppOldPiece = pPl->pPiece;
|
||||
pPl->pPiece = pPiece;
|
||||
|
||||
// set horizontal start position (in the middle of the top line)
|
||||
pPl->nColumn = (pPl->nWidth - 2) / 2;
|
||||
|
||||
// set vertical start position (first piece row with matter at pos. 1)
|
||||
uint16_t nPieceMap = tetris_piece_getBitmap(pPl->pPiece);
|
||||
uint16_t nElementMask = 0xF000;
|
||||
pPl->nRow = -3;
|
||||
while ((nPieceMap & nElementMask) == 0)
|
||||
{
|
||||
++pPl->nRow;
|
||||
nElementMask >>= 4;
|
||||
}
|
||||
if (pPl->nRow < 0)
|
||||
{
|
||||
++pPl->nRow;
|
||||
}
|
||||
|
||||
// did we already collide with something?
|
||||
if (tetris_playfield_collision(pPl, pPl->nColumn, pPl->nRow) == 1)
|
||||
{
|
||||
// game over man, game over!!
|
||||
pPl->status = TETRIS_PFS_GAMEOVER;
|
||||
}
|
||||
else
|
||||
{
|
||||
// bring it on!
|
||||
pPl->status = tetris_playfield_hoverStatus(pPl);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Function: tetris_playfield_collision
|
||||
* Description: detects if piece collides with s.th. at a given position
|
||||
* Argument pPl: playfield to perform action on
|
||||
* Argument nColumn: column where the piece should be moved
|
||||
* Argument nRow: row where the piece should be moved
|
||||
* Return value: 1 for collision, 0 otherwise
|
||||
*/
|
||||
uint8_t tetris_playfield_collision(tetris_playfield_t *pPl,
|
||||
int8_t nColumn,
|
||||
int8_t nRow)
|
||||
{
|
||||
assert(pPl != NULL);
|
||||
|
||||
// only allow coordinates which are within sane ranges
|
||||
assert((nColumn >= -4) && (nColumn < pPl->nWidth));
|
||||
assert((nRow >= -4) && (nRow < pPl->nHeight));
|
||||
|
||||
// The rows of a piece get compared with the background one by one
|
||||
// until either a collision occures or all rows are compared. Both the
|
||||
// piece row and the part of the playfield it covers are represented in
|
||||
// 4 bits which were singled out from their corresponding uint16_t
|
||||
// values and are aligned to LSB. In case where a piece overlaps with
|
||||
// either the left or the right border we "enhance" the playfield part
|
||||
// via bit shifting and set all bits representing the border to 1.
|
||||
//
|
||||
// NOTE: LSB represents the left most position.
|
||||
uint16_t nPieceMap = tetris_piece_getBitmap(pPl->pPiece);
|
||||
uint16_t nPlayfieldPart;
|
||||
uint16_t nPieceRowMap;
|
||||
|
||||
// negative nRow values indicate that the piece hasn't fully entered the
|
||||
// playfield yet which requires special treatment if the piece overlaps
|
||||
// with either the left or the right border
|
||||
if (nRow < 0)
|
||||
{
|
||||
uint16_t nBorderMask = 0x0000;
|
||||
// piece overlaps with left border
|
||||
if (nColumn < 0)
|
||||
{
|
||||
nBorderMask = 0x1111 << (-nColumn - 1);
|
||||
}
|
||||
// piece overlaps with right border
|
||||
else if ((nColumn + 3) >= pPl->nWidth)
|
||||
{
|
||||
nBorderMask = 0x8888 >> ((nColumn + 3) - pPl->nWidth);
|
||||
}
|
||||
// return if piece collides with border
|
||||
if ((nPieceMap & nBorderMask) != 0)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
// here we check the part which has already entered the playfield
|
||||
for (int8_t y = (nRow < 0) ? -nRow : 0; y < 4; ++y)
|
||||
{
|
||||
// current piece row overlaps with lower border
|
||||
if ((y + nRow) >= pPl->nHeight)
|
||||
{
|
||||
// all 4 bits represent the lower border
|
||||
nPlayfieldPart = 0x000F;
|
||||
}
|
||||
// piece overlaps with left border
|
||||
else if (nColumn < 0)
|
||||
{
|
||||
// clear all bits we are not interested in
|
||||
nPlayfieldPart = (pPl->dump[y + nRow] & (0x000F >> -nColumn));
|
||||
// add zeros to the left (the bits "behind" the left border)
|
||||
nPlayfieldPart <<= -nColumn;
|
||||
// set bits beyond left border to 1
|
||||
nPlayfieldPart |= 0x000F >> (4 + nColumn);
|
||||
}
|
||||
// piece overlaps with right border
|
||||
else if ((nColumn + 3) >= pPl->nWidth)
|
||||
{
|
||||
// align the bits we are interested in to LSB
|
||||
// (thereby clearing the rest)
|
||||
nPlayfieldPart = pPl->dump[y + nRow] >> nColumn;
|
||||
// set bits beyond right border to 1
|
||||
nPlayfieldPart |= 0xFFF8 >> (nColumn + 3 - pPl->nWidth);
|
||||
}
|
||||
// current row neither overlaps with left, right nor lower border
|
||||
else
|
||||
{
|
||||
// clear all bits we are not interested in and align the
|
||||
// remaing row to LSB
|
||||
nPlayfieldPart =
|
||||
(pPl->dump[y + nRow] & (0x000F << nColumn)) >> nColumn;
|
||||
}
|
||||
|
||||
// clear all bits of the piece we are not interested in and
|
||||
// align the remaing row to LSB
|
||||
nPieceRowMap = (nPieceMap & (0x000F << (y << 2))) >> (y << 2);
|
||||
|
||||
// finally check for a collision
|
||||
if ((nPlayfieldPart & nPieceRowMap) != 0)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
// if we reach here, no collision was detected
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* Function: tetris_playfield_advancePiece
|
||||
* Description: lowers piece by one row or finally docks it
|
||||
* Argument pPl: playfield to perform action on
|
||||
* Return value: void
|
||||
*/
|
||||
void tetris_playfield_advancePiece(tetris_playfield_t *pPl)
|
||||
{
|
||||
assert(pPl != NULL);
|
||||
|
||||
// a piece can only be lowered if it is hovering or gliding
|
||||
assert ((pPl->status == TETRIS_PFS_HOVERING) ||
|
||||
(pPl->status == TETRIS_PFS_GLIDING));
|
||||
|
||||
if (tetris_playfield_collision(pPl, pPl->nColumn, pPl->nRow + 1))
|
||||
{
|
||||
uint16_t nPiece = tetris_piece_getBitmap(pPl->pPiece);
|
||||
|
||||
// Is the playfield filled up?
|
||||
if ((pPl->nRow < 0) && (nPiece & (0x0FFF >> ((3 + pPl->nRow) << 2))) != 0)
|
||||
{
|
||||
pPl->status = TETRIS_PFS_GAMEOVER;
|
||||
}
|
||||
else
|
||||
{
|
||||
// determine valid start point for dump index
|
||||
int8_t nStartRow = ((pPl->nRow + 3) < pPl->nHeight) ?
|
||||
(pPl->nRow + 3) : pPl->nHeight - 1;
|
||||
for (int8_t i = nStartRow; i >= pPl->nRow; --i)
|
||||
{
|
||||
int8_t y = i - pPl->nRow;
|
||||
|
||||
// clear all bits of the piece we are not interested in and
|
||||
// align the rest to LSB
|
||||
uint16_t nPieceMap = (nPiece & (0x000F << (y << 2))) >> (y << 2);
|
||||
// shift the remaining content to the current column
|
||||
if (pPl->nColumn >= 0)
|
||||
{
|
||||
nPieceMap <<= pPl->nColumn;
|
||||
}
|
||||
else
|
||||
{
|
||||
nPieceMap >>= -pPl->nColumn;
|
||||
}
|
||||
// embed piece in playfield
|
||||
pPl->dump[i] |= nPieceMap;
|
||||
}
|
||||
|
||||
// the piece has finally been docked
|
||||
pPl->status = TETRIS_PFS_DOCKED;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// since there is no collision the piece may continue its travel
|
||||
// to the ground...
|
||||
pPl->nRow++;
|
||||
|
||||
// are we gliding?
|
||||
pPl->status = tetris_playfield_hoverStatus(pPl);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Function: tetris_playfield_movePiece
|
||||
* Description: moves piece to the given direction
|
||||
* Argument pPl: playfield to perform action on
|
||||
* Argument direction: direction (see tetris_playfield_direction_t)
|
||||
* Return value: 1 if piece could be moved, 0 otherwise
|
||||
*/
|
||||
uint8_t tetris_playfield_movePiece(tetris_playfield_t *pPl,
|
||||
tetris_playfield_direction_t direction)
|
||||
{
|
||||
assert(pPl != NULL);
|
||||
|
||||
// a piece can only be moved if it is still hovering or gliding
|
||||
assert((pPl->status == TETRIS_PFS_HOVERING) ||
|
||||
(pPl->status == TETRIS_PFS_GLIDING));
|
||||
|
||||
int8_t nOffset = (direction == TETRIS_PFD_LEFT) ? -1 : 1;
|
||||
if (tetris_playfield_collision(pPl, pPl->nColumn + nOffset, pPl->nRow) == 0)
|
||||
{
|
||||
pPl->nColumn += nOffset;
|
||||
|
||||
// are we gliding?
|
||||
pPl->status = tetris_playfield_hoverStatus(pPl);
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* Function: tetris_playfield_rotatePiece
|
||||
* Description: rotates piece to the given direction
|
||||
* Argument pPl: playfield to perform action on
|
||||
* Argument r: type of rotation (see tetris_piece_rotation_t)
|
||||
* Return value: 1 if piece could be rotated, 0 otherwise
|
||||
*/
|
||||
uint8_t tetris_playfield_rotatePiece(tetris_playfield_t *pPl,
|
||||
tetris_piece_rotation_t rotation)
|
||||
{
|
||||
assert(pPl != NULL);
|
||||
|
||||
// a piece can only be rotation if it is still hovering or gliding
|
||||
assert((pPl->status == TETRIS_PFS_HOVERING) ||
|
||||
(pPl->status == TETRIS_PFS_GLIDING));
|
||||
|
||||
tetris_piece_rotate(pPl->pPiece, rotation);
|
||||
|
||||
// does the rotated piece cause a collision?
|
||||
if (tetris_playfield_collision(pPl, pPl->nColumn, pPl->nRow) != 0)
|
||||
{
|
||||
// in that case we revert the rotation
|
||||
if (rotation == TETRIS_PC_ROT_CW)
|
||||
{
|
||||
tetris_piece_rotate(pPl->pPiece, TETRIS_PC_ROT_CCW);
|
||||
}
|
||||
else
|
||||
{
|
||||
tetris_piece_rotate(pPl->pPiece, TETRIS_PC_ROT_CW);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// are we gliding?
|
||||
pPl->status = tetris_playfield_hoverStatus(pPl);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
/* Function: tetris_playfield_removeCompletedLines
|
||||
* Description: removes completed lines (if any) and lowers the dump
|
||||
* Argument pPl: playfield to perform action on
|
||||
* Return value: void
|
||||
*/
|
||||
void tetris_playfield_removeCompleteLines(tetris_playfield_t *pPl)
|
||||
{
|
||||
assert(pPl != NULL);
|
||||
|
||||
// rows can only be removed if we are in state TETRIS_PFS_DOCKED
|
||||
assert(pPl->status == TETRIS_PFS_DOCKED);
|
||||
|
||||
// bit mask of a full row
|
||||
uint16_t nFullRow = 0xFFFF >> (16 - pPl->nWidth);
|
||||
|
||||
// bit mask (only 4 bits) that tells us if the n-th row after the
|
||||
// current nRow is complete (n-th bit set to 1, LSB represents nRow itself)
|
||||
uint8_t nRowMask = 0;
|
||||
|
||||
// determine sane start and stop values for the dump' index
|
||||
int8_t nStartRow =
|
||||
((pPl->nRow + 3) >= pPl->nHeight) ? pPl->nHeight - 1 : pPl->nRow + 3;
|
||||
int8_t nStopRow = (pPl->nRow < 0) ? 0 : pPl->nRow;
|
||||
|
||||
// dump index variables
|
||||
// for incomplete rows, both variables will be decremented
|
||||
// for complete rows, only i gets decremented
|
||||
int8_t nLowestRow = nStartRow;
|
||||
|
||||
// this loop only considers rows which are affected by the piece
|
||||
for (int8_t i = nStartRow; i >= nStopRow; --i)
|
||||
{
|
||||
// is current row a full row?
|
||||
if ((nFullRow & pPl->dump[i]) == nFullRow)
|
||||
{
|
||||
// set corresponding bit for the row mask
|
||||
// nRowMask |= 0x08 >> (nStartRow - i);
|
||||
nRowMask |= 0x01 << (i - pPl->nRow);
|
||||
}
|
||||
else
|
||||
{
|
||||
// if nLowestRow and i differ, the dump has to be shifted
|
||||
if (i < nLowestRow)
|
||||
{
|
||||
pPl->dump[nLowestRow] = pPl->dump[i];
|
||||
}
|
||||
--nLowestRow;
|
||||
}
|
||||
}
|
||||
|
||||
// if rows have been removed, this loop shifts the rest of the dump
|
||||
uint8_t nComplete = nLowestRow - nStopRow + 1;
|
||||
if (nComplete > 0)
|
||||
{
|
||||
for (int8_t i = nStopRow - 1; nLowestRow >= 0; --i)
|
||||
{
|
||||
// is the row we are copying from below the upper border?
|
||||
if (i >= 0)
|
||||
{
|
||||
// just copy from that row
|
||||
pPl->dump[nLowestRow] = pPl->dump[i];
|
||||
}
|
||||
else
|
||||
{
|
||||
// rows above the upper border are always empty
|
||||
pPl->dump[nLowestRow] = 0;
|
||||
}
|
||||
--nLowestRow;
|
||||
}
|
||||
}
|
||||
|
||||
// ready to get the next piece
|
||||
pPl->status = TETRIS_PFS_READY;
|
||||
|
||||
pPl->nRowMask = nRowMask;
|
||||
}
|
||||
|
||||
|
||||
/*****************
|
||||
* get functions *
|
||||
*****************/
|
||||
|
||||
/* Function: tetris_playfield_getWidth
|
||||
* Description: returns the width of the playfield
|
||||
* Argument pPl: the playfield we want information from
|
||||
* Return value: width of the playfield
|
||||
*/
|
||||
int8_t tetris_playfield_getWidth(tetris_playfield_t *pPl)
|
||||
{
|
||||
assert(pPl != NULL);
|
||||
return pPl->nWidth;
|
||||
}
|
||||
|
||||
|
||||
/* Function: tetris_playfield_getHeight
|
||||
* Description: returns the height of the playfield
|
||||
* Argument pPl: the playfield we want information from
|
||||
* Return value: height of the playfield
|
||||
*/
|
||||
int8_t tetris_playfield_getHeight(tetris_playfield_t *pPl)
|
||||
{
|
||||
assert(pPl != NULL);
|
||||
return pPl->nHeight;
|
||||
}
|
||||
|
||||
|
||||
/* Function: tetris_playfield_getPiece
|
||||
* Description: returns the currently falling piece
|
||||
* Argument pPl: the playfield we want information from
|
||||
* Return value: pointer to the currently falling piece
|
||||
*/
|
||||
tetris_piece_t *tetris_playfield_getPiece(tetris_playfield_t *pPl)
|
||||
{
|
||||
assert(pPl != NULL);
|
||||
return pPl->pPiece;
|
||||
}
|
||||
|
||||
|
||||
/* Function: tetris_playfield_getColumn
|
||||
* Description: returns the column of the currently falling piece
|
||||
* Argument pPl: the playfield we want information from
|
||||
* Return value: column of the currently falling piece
|
||||
*/
|
||||
int8_t tetris_playfield_getColumn(tetris_playfield_t *pPl)
|
||||
{
|
||||
assert(pPl != NULL);
|
||||
return pPl->nColumn;
|
||||
}
|
||||
|
||||
|
||||
/* Function: tetris_playfield_getRow
|
||||
* Description: returns the row of the currently falling piece
|
||||
* Argument pPl: the playfield we want information from
|
||||
* Return value: row of the currently falling piece
|
||||
*/
|
||||
int8_t tetris_playfield_getRow(tetris_playfield_t *pPl)
|
||||
{
|
||||
assert(pPl != NULL);
|
||||
return pPl->nRow;
|
||||
}
|
||||
|
||||
|
||||
/* Function: tetris_playfield_getRowMask
|
||||
* Description: returns the row mask relative to nRow
|
||||
* Argument pPl: the playfield we want information from
|
||||
* Return value: the first 4 bits indicate which lines (relative to nRow)
|
||||
* have been removed if we are in status TETRIS_PFS_READY
|
||||
* LSB is the highest line
|
||||
*/
|
||||
uint8_t tetris_playfield_getRowMask(tetris_playfield_t *pPl)
|
||||
{
|
||||
assert(pPl != NULL);
|
||||
return pPl->nRowMask;
|
||||
}
|
||||
|
||||
|
||||
/* Function: tetris_playfield_getStatus
|
||||
* Description: returns the status of the playfield
|
||||
* Argument pPl: the playfield we want information from
|
||||
* Return value: status of the playfield (see tetris_playfield_status_t)
|
||||
*/
|
||||
tetris_playfield_status_t tetris_playfield_getStatus(tetris_playfield_t *pPl)
|
||||
{
|
||||
assert(pPl != NULL);
|
||||
return pPl->status;
|
||||
}
|
||||
|
||||
|
||||
/* Function: tetris_playfield_getDumpRow
|
||||
* Description: returns the given row of the dump (as bitmap)
|
||||
* Argument pPl: the playfield we want information from
|
||||
* Argument nRow: the number of the row (0 <= nRow < height of playfield)
|
||||
* Return value: bitmap of the requested row (LSB is leftmost column)
|
||||
*/
|
||||
uint16_t tetris_playfield_getDumpRow(tetris_playfield_t *pPl,
|
||||
int8_t nRow)
|
||||
{
|
||||
assert(pPl != NULL);
|
||||
assert((0 <= nRow) && (nRow < pPl->nHeight));
|
||||
return pPl->dump[nRow];
|
||||
}
|
||||
|
||||
213
games/tetris/playfield.h
Normal file
213
games/tetris/playfield.h
Normal file
|
|
@ -0,0 +1,213 @@
|
|||
#ifndef TETRIS_PLAYFIELD_H_
|
||||
#define TETRIS_PLAYFIELD_H_
|
||||
|
||||
#include <inttypes.h>
|
||||
#include "piece.h"
|
||||
|
||||
|
||||
/*********
|
||||
* types *
|
||||
*********/
|
||||
|
||||
// directions to which a piece can be moved
|
||||
typedef enum tetris_playfield_direction_t
|
||||
{
|
||||
TETRIS_PFD_LEFT,
|
||||
TETRIS_PFD_RIGHT
|
||||
}
|
||||
tetris_playfield_direction_t;
|
||||
|
||||
|
||||
// status of the playfield
|
||||
typedef enum tetris_playfield_status_t
|
||||
{
|
||||
TETRIS_PFS_READY, // ready to get next piece
|
||||
TETRIS_PFS_HOVERING, // piece is still hovering
|
||||
TETRIS_PFS_GLIDING, // piece is gliding on the dump
|
||||
TETRIS_PFS_DOCKED, // piece has been docked
|
||||
TETRIS_PFS_GAMEOVER // playfield is filled up
|
||||
}
|
||||
tetris_playfield_status_t;
|
||||
|
||||
|
||||
// tetris_playfield_t
|
||||
typedef struct tetris_playfield_t
|
||||
{
|
||||
int8_t nWidth; // width of playfield
|
||||
int8_t nHeight; // height of playfield
|
||||
tetris_piece_t *pPiece; // currently falling piece
|
||||
int8_t nColumn; // horz. piece pos. (0 is left)
|
||||
int8_t nRow; // vert. piece pos. (0 is top)
|
||||
uint8_t nRowMask; // removed lines relative to nRow (bitmap)
|
||||
tetris_playfield_status_t status; // status
|
||||
uint16_t *dump; // playfield itself
|
||||
}
|
||||
tetris_playfield_t;
|
||||
|
||||
|
||||
/****************************
|
||||
* construction/destruction *
|
||||
****************************/
|
||||
|
||||
/* Function: tetris_playfield_construct
|
||||
* Description: constructs a playfield with the given diemensions
|
||||
* Argument nWidth: width of playfield (4 <= n <= 16)
|
||||
* Argument nHeight: height of playfield (4 <= n <= 124)
|
||||
* Return value: pointer to a newly created playfield
|
||||
*/
|
||||
tetris_playfield_t *tetris_playfield_construct(int8_t nWidth, int8_t nHeight);
|
||||
|
||||
|
||||
/* Function: tetris_playfield_destruct
|
||||
* Description: destructs a playfield
|
||||
* Argument pPl: pointer to the playfield to be destructed
|
||||
* Return value: void
|
||||
*/
|
||||
void tetris_playfield_destruct(tetris_playfield_t *pPl);
|
||||
|
||||
|
||||
/*******************************
|
||||
* playfield related functions *
|
||||
*******************************/
|
||||
|
||||
/* Function: tetris_playfield_reset
|
||||
* Description: resets playfield to begin a new game
|
||||
* Argument pPl: playfield to perform action on
|
||||
* Return value: void
|
||||
*/
|
||||
void tetris_playfield_reset(tetris_playfield_t *pPl);
|
||||
|
||||
|
||||
/* Function: tetris_playfield_insertPiece
|
||||
* Description: inserts a new piece
|
||||
* Argument pPl: playfield to perform action on
|
||||
* Argument pPiece: piece to be inserted
|
||||
* Argument ppOldPiece: [out] indirect pointer to former piece for deallocation
|
||||
* Return value: void
|
||||
*/
|
||||
void tetris_playfield_insertPiece(tetris_playfield_t *pPl,
|
||||
tetris_piece_t *pPiece,
|
||||
tetris_piece_t** ppOldPiece);
|
||||
|
||||
|
||||
/* Function: tetris_playfield_collision
|
||||
* Description: detects if piece collides with s.th. at a given position
|
||||
* Argument pPl: playfield to perform action on
|
||||
* Argument nColumn: column where the piece should be moved
|
||||
* Argument nRow: row where the piece should be moved
|
||||
* Return value: 1 for collision, 0 otherwise
|
||||
*/
|
||||
uint8_t tetris_playfield_collision(tetris_playfield_t *pPl,
|
||||
int8_t nColumn,
|
||||
int8_t nRow);
|
||||
|
||||
|
||||
/* Function: tetris_playfield_advancePiece
|
||||
* Description: lowers piece by one row or finally docks it
|
||||
* Argument pPl: playfield to perform action on
|
||||
* Return value: void
|
||||
*/
|
||||
void tetris_playfield_advancePiece(tetris_playfield_t *pPl);
|
||||
|
||||
|
||||
/* Function: tetris_playfield_movePiece
|
||||
* Description: moves piece to the given direction
|
||||
* Argument pPl: playfield to perform action on
|
||||
* Argument direction: direction (see tetris_playfield_direction_t)
|
||||
* Return value: 1 if piece could be moved, 0 otherwise
|
||||
*/
|
||||
uint8_t tetris_playfield_movePiece(tetris_playfield_t *pPl,
|
||||
tetris_playfield_direction_t direction);
|
||||
|
||||
|
||||
/* Function: tetris_playfield_rotatePiece
|
||||
* Description: rotates piece to the given direction
|
||||
* Argument pPl: playfield to perform action on
|
||||
* Argument r: type of rotation (see tetris_piece_rotation_t)
|
||||
* Return value: 1 if piece could be rotated, 0 otherwise
|
||||
*/
|
||||
uint8_t tetris_playfield_rotatePiece(tetris_playfield_t *pPl,
|
||||
tetris_piece_rotation_t rotation);
|
||||
|
||||
|
||||
/* Function: tetris_playfield_removeCompletedLines
|
||||
* Description: removes completed lines (if any) and lowers the dump
|
||||
* Argument pPl: playfield to perform action on
|
||||
* Return value: void
|
||||
*/
|
||||
void tetris_playfield_removeCompleteLines(tetris_playfield_t *pPl);
|
||||
|
||||
|
||||
/*****************
|
||||
* get functions *
|
||||
*****************/
|
||||
|
||||
/* Function: tetris_playfield_getWidth
|
||||
* Description: returns the width of the playfield
|
||||
* Argument pPl: the playfield we want information from
|
||||
* Return value: width of the playfield
|
||||
*/
|
||||
int8_t tetris_playfield_getWidth(tetris_playfield_t *pPl);
|
||||
|
||||
|
||||
/* Function: tetris_playfield_getHeight
|
||||
* Description: returns the height of the playfield
|
||||
* Argument pPl: the playfield we want information from
|
||||
* Return value: height of the playfield
|
||||
*/
|
||||
int8_t tetris_playfield_getHeight(tetris_playfield_t *pPl);
|
||||
|
||||
|
||||
/* Function: tetris_playfield_getPiece
|
||||
* Description: returns the currently falling piece
|
||||
* Argument pPl: the playfield we want information from
|
||||
* Return value: pointer to the currently falling piece
|
||||
*/
|
||||
tetris_piece_t *tetris_playfield_getPiece(tetris_playfield_t *pPl);
|
||||
|
||||
|
||||
/* Function: tetris_playfield_getColumn
|
||||
* Description: returns the column of the currently falling piece
|
||||
* Argument pPl: the playfield we want information from
|
||||
* Return value: column of the currently falling piece
|
||||
*/
|
||||
int8_t tetris_playfield_getColumn(tetris_playfield_t *pPl);
|
||||
|
||||
|
||||
/* Function: tetris_playfield_getRow
|
||||
* Description: returns the row of the currently falling piece
|
||||
* Argument pPl: the playfield we want information from
|
||||
* Return value: row of the currently falling piece
|
||||
*/
|
||||
int8_t tetris_playfield_getRow(tetris_playfield_t *pPl);
|
||||
|
||||
|
||||
/* Function: tetris_playfield_getRowMask
|
||||
* Description: returns the row mask relative to nRow
|
||||
* Argument pPl: the playfield we want information from
|
||||
* Return value: the first 4 bits indicate which lines (relative to nRow)
|
||||
* have been removed if we are in status TETRIS_PFS_READY
|
||||
*/
|
||||
uint8_t tetris_playfield_getRowMask(tetris_playfield_t *pPl);
|
||||
|
||||
|
||||
/* Function: tetris_playfield_getStatus
|
||||
* Description: returns the status of the playfield
|
||||
* Argument pPl: the playfield we want information from
|
||||
* Return value: status of the playfield (see tetris_playfield_status_t)
|
||||
*/
|
||||
tetris_playfield_status_t tetris_playfield_getStatus(tetris_playfield_t *pPl);
|
||||
|
||||
|
||||
/* Function: tetris_playfield_getDumpRow
|
||||
* Description: returns the given row of the dump (as bitmap)
|
||||
* Argument pPl: the playfield we want information from
|
||||
* Argument nRow: the number of the row (0 <= nRow <= 124)
|
||||
* Return value: bitmap of the requested row (LSB is leftmost column)
|
||||
*/
|
||||
uint16_t tetris_playfield_getDumpRow(tetris_playfield_t *pPl,
|
||||
int8_t nRow);
|
||||
|
||||
|
||||
#endif /*TETRIS_PLAYFIELD_H_*/
|
||||
|
||||
421
games/tetris/view.c
Normal file
421
games/tetris/view.c
Normal file
|
|
@ -0,0 +1,421 @@
|
|||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <assert.h>
|
||||
#include <inttypes.h>
|
||||
#include "../config.h"
|
||||
#include "../pixel.h"
|
||||
#include "../util.h"
|
||||
#include "../scrolltext.h"
|
||||
#include "logic.h"
|
||||
#include "piece.h"
|
||||
#include "playfield.h"
|
||||
#include "view.h"
|
||||
|
||||
/* the API simulator and the real API have different named wait functions */
|
||||
#ifdef __AVR__
|
||||
#define WAIT(ms) wait(ms)
|
||||
#else
|
||||
#define WAIT(ms) myWait(ms)
|
||||
#endif
|
||||
|
||||
|
||||
/***********
|
||||
* defines *
|
||||
***********/
|
||||
|
||||
// how often should the border blink (to indicate level up)
|
||||
#define TETRIS_VIEW_BORDER_BLINK_COUNT 2
|
||||
// amount of time (in ms) between border color changes
|
||||
#define TETRIS_VIEW_BORDER_BLINK_DELAY 100
|
||||
|
||||
// how often should the lines blink when they get removed
|
||||
#define TETRIS_VIEW_LINE_BLINK_COUNT 3
|
||||
// amount of time (in ms) between line color changes
|
||||
#define TETRIS_VIEW_LINE_BLINK_DELAY 75
|
||||
|
||||
// colors of game elements
|
||||
#define TETRIS_VIEW_COLORSPACE 0
|
||||
#define TETRIS_VIEW_COLORBORDER 1
|
||||
#define TETRIS_VIEW_COLORFADE 2
|
||||
#define TETRIS_VIEW_COLORPIECE 3
|
||||
|
||||
|
||||
/***************************
|
||||
* non-interface functions *
|
||||
***************************/
|
||||
|
||||
/* Function: tetris_view_getPieceColor
|
||||
* Description: helper function to dim the piece color if game is paused
|
||||
* Argument pV: pointer to the view whose pause status is of interest
|
||||
* Return value: void
|
||||
*/
|
||||
uint8_t tetris_view_getPieceColor (tetris_view_t *pV)
|
||||
{
|
||||
if (pV->modeCurrent == TETRIS_VIMO_RUNNING)
|
||||
{
|
||||
return TETRIS_VIEW_COLORPIECE;
|
||||
}
|
||||
else
|
||||
{
|
||||
return TETRIS_VIEW_COLORBORDER;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Function: tetris_view_drawDump
|
||||
* Description: redraws the dump and the falling piece (if necessary)
|
||||
* Argument pV: pointer to the view on which the dump should be drawn
|
||||
* Return value: void
|
||||
*/
|
||||
void tetris_view_drawDump(tetris_view_t *pV)
|
||||
{
|
||||
assert(pV->pPl != NULL);
|
||||
if (tetris_playfield_getRow(pV->pPl) <= -4)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int8_t nPieceRow = tetris_playfield_getRow(pV->pPl);
|
||||
|
||||
// only redraw dump completely if the view mode has been changed
|
||||
int8_t nStartRow;
|
||||
if (pV->modeCurrent == pV->modeOld)
|
||||
{
|
||||
nStartRow = ((nPieceRow + 3) < 16) ? (nPieceRow + 3) : 15;
|
||||
}
|
||||
else
|
||||
{
|
||||
nStartRow = 15;
|
||||
}
|
||||
|
||||
uint16_t nRowMap;
|
||||
uint16_t nElementMask;
|
||||
|
||||
tetris_playfield_status_t status = tetris_playfield_getStatus(pV->pPl);
|
||||
for (int8_t nRow = nStartRow; nRow >= 0; --nRow)
|
||||
{
|
||||
nRowMap = tetris_playfield_getDumpRow(pV->pPl, nRow);
|
||||
|
||||
// if a piece is hovering or gliding it needs to be drawn
|
||||
if ((status == TETRIS_PFS_HOVERING) || (status == TETRIS_PFS_GLIDING) ||
|
||||
(status == TETRIS_PFS_GAMEOVER))
|
||||
{
|
||||
if ((nRow >= nPieceRow) && (nRow <= nPieceRow + 3))
|
||||
{
|
||||
int8_t y = nRow - nPieceRow;
|
||||
int8_t nColumn = tetris_playfield_getColumn(pV->pPl);
|
||||
uint16_t nPieceMap =
|
||||
tetris_piece_getBitmap(tetris_playfield_getPiece(pV->pPl));
|
||||
// clear all bits of the piece we are not interested in and
|
||||
// align the remaining row to LSB
|
||||
nPieceMap = (nPieceMap & (0x000F << (y << 2))) >> (y << 2);
|
||||
// shift remaining part to current column
|
||||
if (nColumn >= 0)
|
||||
{
|
||||
nPieceMap <<= nColumn;
|
||||
}
|
||||
else
|
||||
{
|
||||
nPieceMap >>= -nColumn;
|
||||
}
|
||||
// cut off unwanted stuff
|
||||
nPieceMap &= 0x03ff;
|
||||
// finally embed piece into the view
|
||||
nRowMap |= nPieceMap;
|
||||
}
|
||||
}
|
||||
|
||||
nElementMask = 0x0001;
|
||||
|
||||
for (int8_t x = 0; x < 10; ++x)
|
||||
{
|
||||
unsigned char nColor;
|
||||
if ((nRowMap & nElementMask) != 0)
|
||||
{
|
||||
nColor = tetris_view_getPieceColor(pV);
|
||||
}
|
||||
else
|
||||
{
|
||||
nColor = TETRIS_VIEW_COLORSPACE;
|
||||
}
|
||||
setpixel((pixel){14-x,nRow}, nColor);
|
||||
nElementMask <<= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Function: tetris_view_drawPreviewPiece
|
||||
* Description: redraws the preview window
|
||||
* Argument pV: pointer to the view on which the piece should be drawn
|
||||
* Argmument pPc: pointer to the piece for the preview window (may be NULL)
|
||||
* Return value: void
|
||||
*/
|
||||
void tetris_view_drawPreviewPiece(tetris_view_t *pV, tetris_piece_t *pPc)
|
||||
{
|
||||
if (pPc != NULL)
|
||||
{
|
||||
uint8_t nColor;
|
||||
uint16_t nElementMask = 0x0001;
|
||||
uint16_t nPieceMap;
|
||||
if (pV->modeCurrent == TETRIS_VIMO_RUNNING)
|
||||
{
|
||||
nPieceMap = tetris_piece_getBitmap(pPc);
|
||||
}
|
||||
else
|
||||
{
|
||||
nPieceMap = 0x26a6;
|
||||
}
|
||||
|
||||
for (uint8_t y = 0; y < 4; ++y)
|
||||
{
|
||||
for (uint8_t x = 0; x < 4; ++x)
|
||||
{
|
||||
if ((nPieceMap & nElementMask) != 0)
|
||||
{
|
||||
nColor = TETRIS_VIEW_COLORPIECE;
|
||||
}
|
||||
else
|
||||
{
|
||||
nColor = TETRIS_VIEW_COLORSPACE;
|
||||
}
|
||||
setpixel((pixel) {3 - x, y + 6}, nColor);
|
||||
nElementMask <<= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (uint8_t y = 0; y < 4; ++y)
|
||||
{
|
||||
for (uint8_t x = 0; x < 4; ++x)
|
||||
{
|
||||
setpixel((pixel) {3 - x, y + 6}, TETRIS_VIEW_COLORSPACE);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Function: tetris_view_drawBorders
|
||||
* Description: draws borders in the given color
|
||||
* Argument nColor: the color for the border
|
||||
* Return value: void
|
||||
*/
|
||||
void tetris_view_drawBorders(uint8_t nColor)
|
||||
{
|
||||
// drawing playfield
|
||||
uint8_t x, y;
|
||||
for (y = 0; y < 16; ++y)
|
||||
{
|
||||
setpixel((pixel){4, y}, nColor);
|
||||
setpixel((pixel){15, y}, nColor);
|
||||
}
|
||||
for (y = 0; y < 5; ++y)
|
||||
{
|
||||
for (x = 0; x <= 3; ++x){
|
||||
setpixel((pixel){x, y}, nColor);
|
||||
setpixel((pixel){x, y + 11}, nColor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Function: tetris_view_blinkBorders
|
||||
* Description: lets the borders blink to notify player of a level change
|
||||
* Return value: void
|
||||
*/
|
||||
void tetris_view_blinkBorders()
|
||||
{
|
||||
for (uint8_t i = 0; i < TETRIS_VIEW_BORDER_BLINK_COUNT; ++i)
|
||||
{
|
||||
tetris_view_drawBorders(TETRIS_VIEW_COLORPIECE);
|
||||
WAIT(TETRIS_VIEW_BORDER_BLINK_DELAY);
|
||||
tetris_view_drawBorders(TETRIS_VIEW_COLORBORDER);
|
||||
WAIT(TETRIS_VIEW_BORDER_BLINK_DELAY);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Function: tetris_view_blinkLines
|
||||
* Description: lets complete lines blink to emphasize their removal
|
||||
* Argmument pPl: pointer to the playfield whose complete lines should blink
|
||||
* Return value: void
|
||||
*/
|
||||
void tetris_view_blinkLines(tetris_playfield_t *pPl)
|
||||
{
|
||||
// reduce necessity of pointer arithmetic
|
||||
int8_t nRow = tetris_playfield_getRow(pPl);
|
||||
uint8_t nRowMask = tetris_playfield_getRowMask(pPl);
|
||||
|
||||
// don't try to draw below the border
|
||||
int8_t nDeepestRowOffset = ((nRow + 3) < tetris_playfield_getHeight(pPl) ?
|
||||
3 : tetris_playfield_getHeight(pPl) - (nRow + 1));
|
||||
|
||||
// this loop controls how often the lines should blink
|
||||
for (uint8_t i = 0; i < TETRIS_VIEW_LINE_BLINK_COUNT; ++i)
|
||||
{
|
||||
// this loop determines the color of the line to be drawn
|
||||
for (uint8_t nColIdx = 0; nColIdx < 2; ++nColIdx)
|
||||
{
|
||||
// iterate through the possibly complete lines
|
||||
for (uint8_t j = 0; j <= nDeepestRowOffset; ++j)
|
||||
{
|
||||
// is current line a complete line?
|
||||
if ((nRowMask & (0x01 << j)) != 0)
|
||||
{
|
||||
// draw line in current color
|
||||
uint8_t y = nRow + j;
|
||||
for (uint8_t x = 0; x < 10; ++x)
|
||||
{
|
||||
|
||||
uint8_t nColor = (nColIdx == 0 ? TETRIS_VIEW_COLORFADE
|
||||
: TETRIS_VIEW_COLORPIECE);
|
||||
setpixel((pixel){14 - x, y}, nColor);
|
||||
}
|
||||
}
|
||||
}
|
||||
// wait a few ms to make the blink effect visible
|
||||
WAIT(TETRIS_VIEW_LINE_BLINK_DELAY);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/****************************
|
||||
* construction/destruction *
|
||||
****************************/
|
||||
|
||||
/* Function: tetris_view_construct
|
||||
* Description: constructs a view for André's borg
|
||||
* Argument pPl: pointer to logic object which should be observed
|
||||
* Argument pPl: pointer to playfield which should be observed
|
||||
* Return value: pointer to a newly created view
|
||||
*/
|
||||
tetris_view_t *tetris_view_construct(tetris_logic_t *pLogic,
|
||||
tetris_playfield_t *pPl)
|
||||
{
|
||||
// memory allocation
|
||||
assert((pLogic != NULL) && (pPl != NULL));
|
||||
tetris_view_t *pView =
|
||||
(tetris_view_t *) malloc(sizeof(tetris_view_t));
|
||||
assert(pView != NULL);
|
||||
|
||||
// init
|
||||
memset(pView, 0, sizeof(tetris_view_t));
|
||||
pView->pLogic = pLogic;
|
||||
pView->pPl = pPl;
|
||||
pView->modeCurrent = TETRIS_VIMO_RUNNING;
|
||||
pView->modeOld = TETRIS_VIMO_RUNNING;
|
||||
|
||||
// drawing some first stuff
|
||||
clear_screen(0);
|
||||
tetris_view_drawBorders(TETRIS_VIEW_COLORBORDER);
|
||||
|
||||
return pView;
|
||||
}
|
||||
|
||||
|
||||
/* Function: tetris_view_destruct
|
||||
* Description: destructs a view
|
||||
* Argument pView: pointer to the view which should be destructed
|
||||
* Return value: void
|
||||
*/
|
||||
void tetris_view_destruct(tetris_view_t *pView)
|
||||
{
|
||||
assert(pView != NULL);
|
||||
free(pView);
|
||||
}
|
||||
|
||||
|
||||
/***************************
|
||||
* view related functions *
|
||||
***************************/
|
||||
|
||||
/* Function: tetris_view_getDimensions
|
||||
* Description: destructs a view
|
||||
* Argument w: [out] pointer to an int8_t to store the playfield width
|
||||
* Argument h: [out] pointer to an int8_t to store the playfield height
|
||||
* Return value: void
|
||||
*/
|
||||
void tetris_view_getDimensions(int8_t *w,
|
||||
int8_t *h)
|
||||
{
|
||||
assert((w != NULL) && (h != NULL));
|
||||
*w = 10;
|
||||
*h = 16;
|
||||
}
|
||||
|
||||
|
||||
/* Function: tetris_view_setViewMode
|
||||
* Description: sets the view mode (pause or running)
|
||||
* Argument pV: pointer to the view whose mode should be set
|
||||
* Argument vm: see definition of tetris_view_mode_t
|
||||
* Return value: void
|
||||
*/
|
||||
void tetris_view_setViewMode(tetris_view_t *pV, tetris_view_mode_t vm)
|
||||
{
|
||||
pV->modeOld = pV->modeCurrent;
|
||||
pV->modeCurrent = vm;
|
||||
}
|
||||
|
||||
|
||||
/* Function: tetris_view_update
|
||||
* Description: informs a view about changes in the game
|
||||
* Argument pV: pointer to the view which should be updated
|
||||
* Return value: void
|
||||
*/
|
||||
void tetris_view_update(tetris_view_t *pV)
|
||||
{
|
||||
assert(pV != NULL);
|
||||
|
||||
// let complete lines blink (if there are any)
|
||||
if (tetris_playfield_getRowMask(pV->pPl) != 0)
|
||||
{
|
||||
tetris_view_blinkLines(pV->pPl);
|
||||
}
|
||||
|
||||
// draw preview piece
|
||||
tetris_view_drawPreviewPiece(pV, tetris_logic_getPreviewPiece(pV->pLogic));
|
||||
|
||||
// draw dump
|
||||
tetris_view_drawDump(pV);
|
||||
|
||||
// visual feedback to inform about a level change
|
||||
uint8_t nLevel = tetris_logic_getLevel(pV->pLogic);
|
||||
if (nLevel != pV->nOldLevel)
|
||||
{
|
||||
tetris_view_blinkBorders();
|
||||
pV->nOldLevel = nLevel;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Function: tetris_view_showResults
|
||||
* Description: shows results after game
|
||||
* Argument pV: pointer to the view which should show the reults
|
||||
* Return value: void
|
||||
*/
|
||||
void tetris_view_showResults(tetris_view_t *pV)
|
||||
{
|
||||
char pszResults[48];
|
||||
uint16_t nScore = tetris_logic_getScore(pV->pLogic);
|
||||
uint16_t nHighscore = tetris_logic_getHighscore(pV->pLogic);
|
||||
uint16_t nLines = tetris_logic_getLines(pV->pLogic);
|
||||
|
||||
if (nScore <= nHighscore)
|
||||
{
|
||||
snprintf(pszResults, 48 * sizeof(char),
|
||||
"</#Lines %u Score %u Highscore %u",
|
||||
nLines, nScore, nHighscore);
|
||||
}
|
||||
else
|
||||
{
|
||||
snprintf(pszResults, 48 * sizeof(char),
|
||||
"</#Lines %u New Highscore %u", nLines, nScore);
|
||||
}
|
||||
|
||||
scrolltext(pszResults);
|
||||
}
|
||||
|
||||
95
games/tetris/view.h
Normal file
95
games/tetris/view.h
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
#ifndef TETRIS_VIEW_H_
|
||||
#define TETRIS_VIEW_H_
|
||||
|
||||
#include <inttypes.h>
|
||||
#include "logic.h"
|
||||
#include "piece.h"
|
||||
#include "playfield.h"
|
||||
|
||||
|
||||
/*********
|
||||
* types *
|
||||
*********/
|
||||
|
||||
// presentation modes
|
||||
typedef enum tetris_view_mode_t
|
||||
{
|
||||
TETRIS_VIMO_PAUSED,
|
||||
TETRIS_VIMO_RUNNING
|
||||
}
|
||||
tetris_view_mode_t;
|
||||
|
||||
typedef struct tetris_view_t
|
||||
{
|
||||
tetris_logic_t *pLogic; // associated logic object
|
||||
tetris_playfield_t *pPl; // associated playfield
|
||||
tetris_view_mode_t modeCurrent; // current presentation mode
|
||||
tetris_view_mode_t modeOld; // old presentation mode
|
||||
uint8_t nOldLevel; // helper variable to recognize level changes
|
||||
}
|
||||
tetris_view_t;
|
||||
|
||||
|
||||
/*****************************
|
||||
* construction/destruction *
|
||||
*****************************/
|
||||
|
||||
/* Function: tetris_view_construct
|
||||
* Description: constructs a view for André's borg
|
||||
* Argument pPl: pointer to logic object which should be observed
|
||||
* Argument pPl: pointer to playfield which should be observed
|
||||
* Return value: pointer to a newly created view
|
||||
*/
|
||||
tetris_view_t *tetris_view_construct(tetris_logic_t *pLogic,
|
||||
tetris_playfield_t *pPl);
|
||||
|
||||
|
||||
/* Function: tetris_view_destruct
|
||||
* Description: destructs a view
|
||||
* Argument pView: pointer to the view to be destructed
|
||||
* Return value: void
|
||||
*/
|
||||
void tetris_view_destruct(tetris_view_t *pView);
|
||||
|
||||
|
||||
/***************************
|
||||
* view related functions *
|
||||
***************************/
|
||||
|
||||
/* Function: tetris_view_getDimensions
|
||||
* Description: destructs a view
|
||||
* Argument w: [out] pointer to an int8_t to store the playfield width
|
||||
* Argument h: [out] pointer to an int8_t to store the playfield height
|
||||
* Return value: void
|
||||
*/
|
||||
void tetris_view_getDimensions(int8_t *w,
|
||||
int8_t *h);
|
||||
|
||||
|
||||
/* Function: tetris_view_setViewMode
|
||||
* Description: sets the view mode (pause or running)
|
||||
* Argument pV: pointer to the view whose mode should be set
|
||||
* Argument vm: see definition of tetris_view_mode_t
|
||||
* Return value: void
|
||||
*/
|
||||
void tetris_view_setViewMode(tetris_view_t *pV, tetris_view_mode_t vm);
|
||||
|
||||
|
||||
/* Function: tetris_view_update
|
||||
* Description: informs a view about changes in the game
|
||||
* Argument pV: pointer to the view which should be updated
|
||||
* Return value: void
|
||||
*/
|
||||
void tetris_view_update(tetris_view_t *pV);
|
||||
|
||||
|
||||
/* Function: tetris_view_showResults
|
||||
* Description: shows results after game
|
||||
* Argument pV: pointer to the view which should show the reults
|
||||
* Return value: void
|
||||
*/
|
||||
void tetris_view_showResults(tetris_view_t *pV);
|
||||
|
||||
|
||||
#endif /*TETRIS_VIEW_H_*/
|
||||
|
||||
Loading…
Add table
Add a link
Reference in a new issue