Blob Blame Raw

#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;
}