#include "SynEngine.hpp" #include enum CellState { EmptyCell, PointCell, SnakeCell }; enum PlayerSprite { Empty, Point, Straight, Curved, Back, Head, PointGot, GameOver }; struct SnakePart { sf::Vector2i cellPosition; unsigned direction; }; class Snake : public IBehaviour { private: std::vector snake; unsigned int lenght; SnakePart *backSecond; SnakePart *back; SnakePart *headSecond; SnakePart *head; Timer *timer; Grid *grid; sf::Vector2i gridSize; float timeCountdown; float timeResetValue; unsigned TickPerSec; GameConfiguration *config; sf::Vector2i pointPosition; void Move(); void Grow(); void ResetPointPosition(); int GetRandom(int, int); unsigned GetDirection(sf::Vector2i, sf::Vector2i); unsigned GetBackDirection(); unsigned GetMoveDirection(); sf::Vector2i GetDirectionVector(unsigned); PlayerSprite GetNewSprite(unsigned &); CellState NextCellState(); public: Snake(); void Update(); ~Snake(); }; Snake::Snake() { pointPosition = { 0, 0 }; // TEST lenght = 2; snake.reserve(lenght); for (unsigned i = 0; i < lenght; i++) snake.push_back({ sf::Vector2i(1 - i, 0), 0}); timer = &GameManager::timer; head = backSecond = &snake[0]; back = headSecond = &snake[1]; config = GameManager::GetConfig(); grid = &GameManager::GetGrid(); gridSize = config -> GetGridSize(); TickPerSec = config -> GetTickPerSec(); timeResetValue = 1.0 / (float)TickPerSec; timeCountdown = timeResetValue; grid -> UpdateCell(snake[0].cellPosition.x, snake[0].cellPosition.y, PlayerSprite::Head, 0); grid -> UpdateCell(snake[1].cellPosition.x, snake[1].cellPosition.y, PlayerSprite::Back, 0); ResetPointPosition(); } int Snake::GetRandom(int min, int max) { int result; static float value = 0.0; value += GameManager::timer.GetTimePassed(); srand(value); result = rand() % (max - min); result += min; return result; } void Snake::ResetPointPosition() { bool isInside = false; int i; do { isInside = false; pointPosition = sf::Vector2i(GetRandom(0, gridSize.x), GetRandom(0, gridSize.y)); for (i = 0; i < lenght; i++) if(snake[i].cellPosition == pointPosition) { isInside = true; break; } } while (isInside); grid -> UpdateCell(pointPosition.x, pointPosition.y, PlayerSprite::Point, 0); } void Snake::Grow() { sf::Vector2i secondPosition; sf::Vector2i headPosition = head -> cellPosition; unsigned previousDirection; unsigned headDirection; PlayerSprite sprite; headDirection = GetMoveDirection(); headPosition += GetDirectionVector(headDirection); grid -> ApplyGridLimitsToVector2i(headPosition); snake.insert(snake.begin(), { headPosition, headDirection }); lenght = snake.size(); previousDirection = snake[1].direction; sprite = GetNewSprite(previousDirection); head = &snake[0]; headSecond = &snake[1]; back = &snake[lenght - 1]; backSecond = &snake[lenght - 2]; grid -> UpdateCell(headPosition.x, headPosition.y, PlayerSprite::Head, headDirection); grid -> UpdateCell(headSecond -> cellPosition.x, headSecond -> cellPosition.y, sprite, (int)previousDirection); } void Snake::Move() { grid -> UpdateCell(back -> cellPosition.x, back -> cellPosition.y, PlayerSprite::Empty); snake.pop_back(); Grow(); grid -> UpdateCell(back -> cellPosition.x, back -> cellPosition.y, PlayerSprite::Back, GetBackDirection()); } void Snake::Update() { if(timeCountdown > 0.0) { timeCountdown -= timer -> GetDeltaTime(); return; } switch (NextCellState()) { case CellState::EmptyCell: Move(); break; case CellState::PointCell: Grow(); ResetPointPosition(); break; // case CellState::SnakeCell: GameOver(); break; } timeCountdown = timeResetValue; } unsigned Snake::GetMoveDirection() { // Check if the new direction is opposite to the head's direction unsigned direction = head -> direction; unsigned inputDirection = GameManager::GetInputDirection(); if((inputDirection + 2) % 4 != direction) return inputDirection; return direction; } sf::Vector2i Snake::GetDirectionVector(unsigned direction) { switch (direction) { case 0: return sf::Vector2i(1, 0); case 1: return sf::Vector2i(0, -1); case 2: return sf::Vector2i(-1, 0); case 3: return sf::Vector2i(0, 1); default: return sf::Vector2i(1, 0); } } CellState Snake::NextCellState() { sf::Vector2i cellPosition = head -> cellPosition + GetDirectionVector(GetMoveDirection()); grid -> ApplyGridLimitsToVector2i(cellPosition); for (int i = 0; i < lenght; i++) if(snake[i].cellPosition == cellPosition) return CellState::SnakeCell; if(cellPosition == pointPosition) return CellState::PointCell; return CellState::EmptyCell; } PlayerSprite Snake::GetNewSprite(unsigned &direction) { SnakePart *third = &snake[2]; SnakePart *second = headSecond; sf::Vector2i vector = third -> cellPosition - head -> cellPosition; unsigned newDirection = GameManager::GetInputDirection(); // I need to update this. It look so bad if(vector.x != 0 && vector.y != 0) { direction += 2; direction %= 4; if(direction > newDirection) { unsigned temp; temp = direction; direction = newDirection; newDirection = temp; } // 0 - 1 -> 0 // 1 - 2 -> 1 // 2 - 3 -> 2 // 0 - 3 -> 3 if(direction == 0 && newDirection == 3) direction = 3; return PlayerSprite::Curved; } direction = GetDirection(head -> cellPosition, second -> cellPosition); return PlayerSprite::Straight; } unsigned Snake::GetDirection(sf::Vector2i first, sf::Vector2i second) { first -= second; if(first.x > 0) return 0; if(first.y < 0) return 1; if(first.x < 0) return 2; if(first.y > 0) return 3; return 0; } unsigned Snake::GetBackDirection() { return GetDirection(snake[lenght - 2].cellPosition, snake[lenght - 1].cellPosition); } Snake::~Snake() { }