#include <helianthus.h>
#define SIZE 32
#define WIDTH 16
#define HEIGHT 16
#define MAXLEN (WIDTH*HEIGHT+1)
#define MAXFOOD 5
#define FRAMERATE 24
enum TileType {
NONE,
WALL,
FOOD,
SNAKE
};
enum GameMode {
MENU,
START,
PLAY,
GAMEOVER
};
int grid[WIDTH][HEIGHT];
Sprite sprites[WIDTH][HEIGHT];
Group gridGroup, storage;
Sound nom, cut;
int snake[MAXLEN][2];
int len;
int foodCount;
int dx, dy;
int framesRemain;
int optStop;
int optCut;
double optWalls;
int optStepFrames;
int difficulty;
int mode;
Animation tileWall;
Animation tileFood;
Animation tileHead;
Animation tileBody;
Animation tileBend;
Animation tileTail;
Font font;
void putFood() {
for(int i = 0; i < 100 && foodCount < MAXFOOD; ++i) {
int x = randomNumber(0, WIDTH-1);
int y = randomNumber(0, HEIGHT-1);
if (grid[x][y] == NONE) {
grid[x][y] = FOOD;
spriteSetAnimation(sprites[x][y], tileFood);
++foodCount;
}
}
}
void setSnakeTile(int i) {
int x = snake[i][0];
int y = snake[i][1];
Sprite s = sprites[x][y];
Animation tile = tileBody;
double rotation = 0;
int left = FALSE;
int right = FALSE;
int top = FALSE;
int bottom = FALSE;
if (i > 0) {
left = left || snake[i-1][0] < x;
right = right || snake[i-1][0] > x;
top = top || snake[i-1][1] < y;
bottom = bottom || snake[i-1][1] > y;
}
if (i < len-1) {
left = left || snake[i+1][0] < x;
right = right || snake[i+1][0] > x;
top = top || snake[i+1][1] < y;
bottom = bottom || snake[i+1][1] > y;
}
if (i == 0) {
tile = tileHead;
if (top) rotation = 90;
if (bottom) rotation = -90;
if (right) rotation = 180;
} else
if (i == len-1) {
tile = tileTail;
if (top) rotation = -90;
if (bottom) rotation = 90;
if (left) rotation = 180;
} else
if (left && top) {
tile = tileBend;
rotation = 180;
} else
if (left && bottom) {
tile = tileBend;
rotation = 90;
} else
if (right && top) {
tile = tileBend;
rotation = -90;
} else
if (right && bottom) {
tile = tileBend;
rotation = 0;
} else
if (top) {
rotation = 90;
}
spriteSetAnimation(s, tile);
spriteSetRotation(s, rotation);
}
void resetTile(int x, int y) {
grid[x][y] = NONE;
Sprite s = sprites[x][y];
spriteSetNoAnimation(s);
spriteSetRotation(s, 0);
}
void moveSnake() {
int x = snake[0][0] + dx;
int y = snake[0][1] + dy;
int cell = grid[x][y];
if (cell == WALL) {
if (!optStop) mode = GAMEOVER;
dx = snake[0][0] - snake[1][0];
dy = snake[0][1] - snake[1][1];
return;
}
if (cell == SNAKE) {
if (!optCut) { mode = GAMEOVER; return; }
int prevLen = len;
for(int i = 0; i < len; ++i)
if (snake[i][0] == x && snake[i][1] == y)
{ len=i; setSnakeTile(len-1); break; }
for(int i = len; i < prevLen; ++i)
resetTile(snake[i][0], snake[i][1]);
soundPlay(cut, FALSE);
}
if (cell != NONE && cell != FOOD && cell != SNAKE)
{ mode = GAMEOVER; return; }
for(int i = len; i > 0; --i) {
snake[i][0] = snake[i-1][0];
snake[i][1] = snake[i-1][1];
}
snake[0][0] = x;
snake[0][1] = y;
if (cell == FOOD || cell == SNAKE) {
grid[x][y] = SNAKE;
++len;
setSnakeTile(0);
setSnakeTile(1);
} else {
grid[x][y] = SNAKE;
setSnakeTile(0);
setSnakeTile(1);
setSnakeTile(len-1);
resetTile(snake[len][0], snake[len][1]);
}
if (cell == FOOD) {
--foodCount;
soundPlay(nom, FALSE);
putFood();
}
}
void restart() {
optStepFrames = 12;
optWalls = 0;
optCut = TRUE;
optStop = TRUE;
switch(difficulty) {
case 2:
optStepFrames = 12;
optWalls = 0.01;
break;
case 3:
optStepFrames = 10;
optWalls = 0.02;
optCut = FALSE;
break;
case 4:
optStepFrames = 8;
optWalls = 0.04;
optCut = FALSE;
optStop = FALSE;
break;
case 5:
optStepFrames = 6;
optWalls = 0.06;
optCut = FALSE;
optStop = FALSE;
break;
};
for(int x = 0; x < WIDTH; ++x) {
for(int y = 0; y < HEIGHT; ++y) {
grid[x][y] = NONE;
Sprite s = sprites[x][y];
spriteSetRotation(s, 0);
spriteSetNoAnimation(s);
if ( x == 0 || y == 0
|| x == WIDTH-1 || y == HEIGHT-1
|| randomFloat() < optWalls )
{
grid[x][y] = WALL;
spriteSetAnimation(s, tileWall);
}
}
}
len = 2;
snake[0][0] = WIDTH/2;
snake[0][1] = HEIGHT/2;
snake[1][0] = snake[0][0] - 1;
snake[1][1] = snake[0][1];
for(int i = 0; i < len; ++i)
grid[ snake[i][0] ][ snake[i][1] ] = SNAKE;
for(int i = 0; i < len; ++i)
setSnakeTile(i);
dx = 1;
dy = 0;
foodCount = 0;
putFood();
framesRemain = optStepFrames;
mode = START;
}
void init() {
tileWall = createAnimation("data/sprite/bricks.png");
tileFood = createAnimation("data/sprite/apple.png");
tileHead = createAnimation("data/sprite/snake/head.png");
tileBody = createAnimation("data/sprite/snake/body.png");
tileBend = createAnimation("data/sprite/snake/bend.png");
tileTail = createAnimation("data/sprite/snake/tail.png");
font = createFont("data/fonts/blackcry.ttf");
textFont(font);
storage = createGroup();
Animation tiles[6] = { tileWall, tileFood, tileHead, tileBody, tileBend, tileTail };
for(int i = 0; i < 6; ++i) {
Sprite s = createSprite(0, 0);
spriteSetAnimation(s, tiles[i]);
groupAdd(storage, s);
}
groupSetVisibleEach(storage, FALSE);
gridGroup = createGroup();
for(int x = 0; x < WIDTH; ++x) {
for(int y = 0; y < HEIGHT; ++y) {
Sprite s = createSpriteEx((x + 0.5)*SIZE, (y + 0.5)*SIZE, SIZE, SIZE);
spriteSetShapeColor(s, colorByName("transparent"));
groupAdd(gridGroup, s);
sprites[x][y] = s;
}
}
nom = createSound("data/sound/beep.ogg");
cut = createSound("data/sound/nom.ogg");
mode = MENU;
}
void draw() {
double cx = worldGetWidth()/2.0;
double cy = worldGetHeight()/2.0;
if (mode == PLAY) {
int prevdx = snake[0][0] - snake[1][0];
int prevdy = snake[0][1] - snake[1][1];
if (keyWentDown("left") && (prevdx != 1 || prevdy != 0)) { dx = -1; dy = 0; framesRemain = 0; }
else if (keyWentDown("right") && (prevdx != -1 || prevdy != 0)) { dx = 1; dy = 0; framesRemain = 0; }
else if (keyWentDown("up") && (prevdx != 0 || prevdy != 1)) { dx = 0; dy = -1; framesRemain = 0; }
else if (keyWentDown("down") && (prevdx != 0 || prevdy != -1)) { dx = 0; dy = 1; framesRemain = 0; }
if (--framesRemain <= 0) {
moveSnake();
framesRemain = optStepFrames;
}
drawSprites();
if (keyWentDown("escape")) mode = MENU;
} else
if (mode == GAMEOVER) {
drawSprites();
fill(colorByRGBA(1, 1, 1, 0.25));
rect(0, 0, worldGetWidth(), worldGetHeight());
noFill();
textAlign(HALIGN_CENTER, HALIGN_CENTER);
textSize(24);
text("Game Over", cx, cy);
textSize(12);
text("press enter", cx, cy + 24);
if (keyWentDown("return")) mode = MENU;
if (keyWentDown("escape")) mode = MENU;
} else
if (mode == START) {
drawSprites();
fill(colorByRGBA(1, 1, 1, 0.25));
rect(0, 0, worldGetWidth(), worldGetHeight());
noFill();
textAlign(HALIGN_CENTER, VALIGN_CENTER);
textSize(24);
text("Press enter to start", cx, cy);
if (keyWentDown("return")) mode = PLAY;
if (keyWentDown("escape")) mode = MENU;
} else
if (mode == MENU) {
noFill();
textAlign(HALIGN_LEFT, VALIGN_CENTER);
textSize(24);
int x = cx - 100;
int y = cy - 24*3;
text("Select difficulty level:", x, y); y += 24;
text("1 - very easy", x, y); y += 24;
text("2 - easy", x, y); y += 24;
text("3 - medium", x, y); y += 24;
text("4 - hard", x, y); y += 24;
text("5 - very hard", x, y); y += 24;
difficulty = 0;
if (keyWentDown("1")) difficulty = 1;
if (keyWentDown("2")) difficulty = 2;
if (keyWentDown("3")) difficulty = 3;
if (keyWentDown("4")) difficulty = 4;
if (keyWentDown("5")) difficulty = 5;
if (difficulty) restart();
if (keyWentDown("escape")) worldStop();
}
}
int main() {
worldSetWidth(SIZE * WIDTH);
worldSetHeight(SIZE * HEIGHT);
worldSetFrameRate(FRAMERATE);
worldSetInit(&init);
worldSetDraw(&draw);
worldRun();
return 0;
}