diff --git a/data/sound/beep.ogg b/data/sound/beep.ogg new file mode 100644 index 0000000..71f5128 Binary files /dev/null and b/data/sound/beep.ogg differ diff --git a/data/sound/nom.ogg b/data/sound/nom.ogg new file mode 100644 index 0000000..3a55a7e Binary files /dev/null and b/data/sound/nom.ogg differ diff --git a/data/sprite/apple.png b/data/sprite/apple.png new file mode 100644 index 0000000..53405d8 Binary files /dev/null and b/data/sprite/apple.png differ diff --git a/data/sprite/breadball.png b/data/sprite/breadball.png new file mode 100644 index 0000000..da1cba7 Binary files /dev/null and b/data/sprite/breadball.png differ diff --git a/data/sprite/bricks.png b/data/sprite/bricks.png new file mode 100644 index 0000000..fe10e29 Binary files /dev/null and b/data/sprite/bricks.png differ diff --git a/data/sprite/snake/bend.png b/data/sprite/snake/bend.png new file mode 100644 index 0000000..6b67c61 Binary files /dev/null and b/data/sprite/snake/bend.png differ diff --git a/data/sprite/snake/body.png b/data/sprite/snake/body.png new file mode 100644 index 0000000..1b951c0 Binary files /dev/null and b/data/sprite/snake/body.png differ diff --git a/data/sprite/snake/head.png b/data/sprite/snake/head.png new file mode 100644 index 0000000..54ded56 Binary files /dev/null and b/data/sprite/snake/head.png differ diff --git a/data/sprite/snake/tail.png b/data/sprite/snake/tail.png new file mode 100644 index 0000000..65a83a1 Binary files /dev/null and b/data/sprite/snake/tail.png differ diff --git a/snake.c b/snake.c new file mode 100644 index 0000000..7df970d --- /dev/null +++ b/snake.c @@ -0,0 +1,362 @@ + +#include + +#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; + +char *tileWall = "data/sprite/bricks.png"; +char *tileFood = "data/sprite/apple.png"; +char *tileHead = "data/sprite/snake/head.png"; +char *tileBody = "data/sprite/snake/body.png"; +char *tileBend = "data/sprite/snake/bend.png"; +char *tileTail = "data/sprite/snake/tail.png"; + + +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]; + char *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() { + worldSetWidth(SIZE * WIDTH); + worldSetHeight(SIZE * HEIGHT); + worldSetFrameRate(FRAMERATE); + + storage = createGroup(); + char *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, "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(rgba(1, 1, 1, 0.25)); + rect(0, 0, worldGetWidth(), worldGetHeight()); + + 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(rgba(1, 1, 1, 0.25)); + rect(0, 0, worldGetWidth(), worldGetHeight()); + + 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) { + 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() { + worldSetInit(&init); + worldSetDraw(&draw); + worldRun(); + return 0; +}