commit 69fe3a43071f9eb6fab103784a8cad09840b9f9c Author: Sergiusz 'q3k' BazaƄski Date: Mon Oct 8 16:00:59 2012 +0200 First commit! diff --git a/0.png b/0.png new file mode 100644 index 0000000..f15830b Binary files /dev/null and b/0.png differ diff --git a/1.png b/1.png new file mode 100644 index 0000000..9935af9 Binary files /dev/null and b/1.png differ diff --git a/2.png b/2.png new file mode 100644 index 0000000..9e20889 Binary files /dev/null and b/2.png differ diff --git a/3.png b/3.png new file mode 100644 index 0000000..77e8c12 Binary files /dev/null and b/3.png differ diff --git a/4.png b/4.png new file mode 100644 index 0000000..ac4e5c8 Binary files /dev/null and b/4.png differ diff --git a/5.png b/5.png new file mode 100644 index 0000000..d864fcf Binary files /dev/null and b/5.png differ diff --git a/6.png b/6.png new file mode 100644 index 0000000..2022b71 Binary files /dev/null and b/6.png differ diff --git a/7.png b/7.png new file mode 100644 index 0000000..71eeb61 Binary files /dev/null and b/7.png differ diff --git a/8.png b/8.png new file mode 100644 index 0000000..63eef77 Binary files /dev/null and b/8.png differ diff --git a/GFXframework.cpp b/GFXframework.cpp new file mode 100644 index 0000000..a645599 --- /dev/null +++ b/GFXframework.cpp @@ -0,0 +1,70 @@ +#include "GFXframework.h" + +#include + +GFXframework* GFXframework::m_GFXframework= NULL; + +GFXframework::GFXframework() +{ + +} + +GFXframework::~GFXframework() +{ + m_Window->close(); + delete m_Window; +} + +GFXframework* GFXframework::GetInstance() +{ + if (m_GFXframework== NULL) + { + m_GFXframework = new GFXframework(); + } + return m_GFXframework; +} + +void GFXframework::ResetInstance() +{ + delete m_GFXframework; // REM : it works even if the pointer is NULL (does nothing then) + m_GFXframework = NULL; // so GetInstance will still work. +} + +bool GFXframework::InitWindow(int sizeX, int sizeY, char* name) +{ + m_Window = new sf::RenderWindow(sf::VideoMode(sizeX, sizeY), name); + return true; +} + +sf::RenderWindow* GFXframework::GetWindow() +{ + return m_Window; +} + +void GFXframework::LoadSprites() +{ + m_TileTexture[0].loadFromFile("0.png"); + m_TileTexture[1].loadFromFile("1.png"); + m_TileTexture[2].loadFromFile("2.png"); + m_TileTexture[3].loadFromFile("3.png"); + m_TileTexture[4].loadFromFile("4.png"); + m_TileTexture[5].loadFromFile("5.png"); + m_TileTexture[6].loadFromFile("6.png"); + m_TileTexture[7].loadFromFile("7.png"); + m_TileTexture[8].loadFromFile("8.png"); + m_TileTexture[9].loadFromFile("bomb.png"); + m_TileTexture[10].loadFromFile("happy.png"); + m_TileTexture[11].loadFromFile("ground.png"); + m_TileTexture[12].loadFromFile("flag.png"); + + for (int i = 0; i < 13; i++) + { + m_Sprite[i] =sf::Sprite(m_TileTexture[i]); + } +} + +void GFXframework::DisplaySpriteAt(int spriteType, int x, int y) +{ + m_Sprite[spriteType].setPosition((float)x, (float)y); + m_Window->draw(m_Sprite[spriteType]); +} \ No newline at end of file diff --git a/GFXframework.h b/GFXframework.h new file mode 100644 index 0000000..f08a68d --- /dev/null +++ b/GFXframework.h @@ -0,0 +1,34 @@ +// GFX FRAMEWORK SINGLETON + +#ifndef GFXFRAMEWORK_H +#define GFXFRAMEWORK_H + +#include + +class GFXframework +{ + // Singleton mechanics +protected: + GFXframework(); + static GFXframework* m_GFXframework; + +public: + static GFXframework* GetInstance(); + static void ResetInstance(); // we don't want any leaks! + + // GFX system methods and member variables +protected: + sf::RenderWindow* m_Window; + +public: + ~GFXframework(); + bool InitWindow(int sizeX, int sizeY, char* name); + sf::RenderWindow* GetWindow(); + + sf::Texture m_TileTexture[13]; + sf::Sprite m_Sprite[13]; + void LoadSprites(); + void DisplaySpriteAt(int spriteType, int x, int y); +}; + +#endif \ No newline at end of file diff --git a/README.txt b/README.txt new file mode 100644 index 0000000..5183940 --- /dev/null +++ b/README.txt @@ -0,0 +1,9 @@ +// A game of minesweeper, written in one day. +// +// CONTROLS: +// Left mouse click - test tile for mine +// Right mouse click - mark tile with a flag or remove a flag +// 'R' key - reset the game +// Esc - exit game + +// Depends on SFML (and VLC in debug) \ No newline at end of file diff --git a/bar.png b/bar.png new file mode 100644 index 0000000..f9880ce Binary files /dev/null and b/bar.png differ diff --git a/bomb.png b/bomb.png new file mode 100644 index 0000000..6030b72 Binary files /dev/null and b/bomb.png differ diff --git a/flag.png b/flag.png new file mode 100644 index 0000000..4868546 Binary files /dev/null and b/flag.png differ diff --git a/game.cpp b/game.cpp new file mode 100644 index 0000000..239ee99 --- /dev/null +++ b/game.cpp @@ -0,0 +1,172 @@ +#include "game.h" + +#include "general.h" +#include "tile.h" +#include "map.h" + +#include "GFXframework.h" +#include + +#include + +Game::Game() +{ + Init(); +} + +Game::~Game() +{ + delete m_LevelMap; + GFXframework::ResetInstance(); +} + +void Game::Init() +{ + m_Finished = false; + + m_numOfBombs = SMALL_MAP_BOMBS; + m_SizeX = SMALL_MAP_SIZE_X; + m_SizeY = SMALL_MAP_SIZE_Y; + + int window_width = m_SizeX * TILE_SIZE_X; + int window_height = m_SizeY * TILE_SIZE_Y + TILE_SIZE_Y/2; + + GFXframework::GetInstance()->InitWindow(window_width, window_height, "Don't Explode!"); + GFXframework::GetInstance()->LoadSprites(); + + // bottom bar, part of GUI + m_GUITexture.loadFromFile("bar.png"); + m_GUISprite =sf::Sprite(m_GUITexture); + + // create basic map + m_LevelMap = new Map(m_SizeX, m_SizeY, m_numOfBombs); + + m_FirstClick = true; +} + +void Game::Shutdown(int returnCode) +{ + (void)returnCode; +} + +void Game::MainLoop() +{ + while (GFXframework::GetInstance()->GetWindow()->isOpen()) + { + sf::Event event; + // main events processing + while (GFXframework::GetInstance()->GetWindow()->pollEvent(event)) + { + if (event.type == sf::Event::Closed) + GFXframework::GetInstance()->GetWindow()->close(); + } + + if (sf::Keyboard::isKeyPressed(sf::Keyboard::R)) + { + m_FirstClick = true; + m_LevelMap->Generate(); + m_Finished = false; + } + if (sf::Keyboard::isKeyPressed(sf::Keyboard::Escape )) + { + GFXframework::GetInstance()->GetWindow()->close(); + } + + // if the game isn't finished yet, allow all mechanics to work + if ( !m_Finished) + { + if (sf::Mouse::isButtonPressed(sf::Mouse::Left)) + { + if ( m_LeftMouseDown != true) + { + m_LeftMouseDown = true; + sf::Vector2i localPosition = sf::Mouse::getPosition(*(GFXframework::GetInstance()->GetWindow())); + + + if (m_FirstClick) + { + if (m_LevelMap->GenerateFromClick(localPosition.x, localPosition.y)) + { + m_FirstClick = false; + HandleLeftMouse(localPosition); + } + } + else + { + HandleLeftMouse(localPosition); + } + } + } + else + { + // prevent repeating signals of mousedown (SFML only has MouseDown, so we have to work around it) + m_LeftMouseDown = false; + } + + if (sf::Mouse::isButtonPressed(sf::Mouse::Right) && !m_FirstClick) + { + if ( m_RightMouseDown != true) + { + m_RightMouseDown = true; + sf::Vector2i localPosition = sf::Mouse::getPosition(*(GFXframework::GetInstance()->GetWindow())); + HandleRightMouse(localPosition); + } + } + else + { + // prevent repeating signals of mousedown (SFML only has MouseDown, so we have to work around it) + m_RightMouseDown = false; + } + + if (m_LevelMap->IsMapDone()) + { + m_Finished = true; + if (m_LevelMap->IsMapWon()) + { + m_LevelMap->WinMap(); + } + else + { + m_LevelMap->LoseMap(); + } + } + } + GFXframework::GetInstance()->GetWindow()->clear(); + Render(); + GFXframework::GetInstance()->GetWindow()->display(); + } + +} + +bool Game::HandleLeftMouse(sf::Vector2i localPosition) +{ + m_LevelMap->ClickAt(localPosition.x, localPosition.y); + return true; +} + +bool Game::HandleRightMouse(sf::Vector2i localPosition) +{ + m_LevelMap->RightClickAt(localPosition.x, localPosition.y); + return true; +} + +void Game::HandleGame() +{ +} + +void Game::DrawGui() +{ + // draw bottom bar, for MORE BLING! + for (int x = 0; x < m_SizeX; x++) + { + m_GUISprite.setPosition((float)(x * TILE_SIZE_X), (float)(m_SizeY * TILE_SIZE_Y)); + GFXframework::GetInstance()->GetWindow()->draw(m_GUISprite); + } +} + +void Game::Render() +{ + DrawGui(); + m_LevelMap->Render(); + +} diff --git a/game.h b/game.h new file mode 100644 index 0000000..e6d9d70 --- /dev/null +++ b/game.h @@ -0,0 +1,46 @@ +// MAIN GAME CLASS + +#ifndef GAME_H +#define GAME_H + +class Map; + +#include +#include + +class Game +{ +private: + bool m_Finished; + + int m_SizeX; + int m_SizeY; + int m_numOfBombs; + + bool m_FirstClick; + bool m_LeftMouseDown; + bool m_RightMouseDown; + + sf::Texture m_GUITexture; + sf::Sprite m_GUISprite; + + Map* m_LevelMap; + +public: + Game(); + ~Game(); + + + void Init(); + void Shutdown(int exitCode); // init and shutdown methods, woo + + void MainLoop(); // main loop, call that from the MAIN file and you have a game + bool HandleLeftMouse(sf::Vector2i localPosition); + bool HandleRightMouse(sf::Vector2i localPosition); + void HandleGame(); // some cool stuff may go here, eventually + + void DrawGui(); // draw gui... pretty stuff + void Render(); // render our game. we call this every frame +}; + +#endif \ No newline at end of file diff --git a/general.h b/general.h new file mode 100644 index 0000000..bd277f6 --- /dev/null +++ b/general.h @@ -0,0 +1,16 @@ +#ifndef GENERAL_H +#define GENERAL_H + +const int BOMB = 9; +const int HAPPY = 10; +const int GROUND = 11; +const int FLAG = 12; + +const int TILE_SIZE_X = 64; +const int TILE_SIZE_Y = 64; + +const int SMALL_MAP_SIZE_X = 9; +const int SMALL_MAP_SIZE_Y = 9; +const int SMALL_MAP_BOMBS = 10; + +#endif \ No newline at end of file diff --git a/ground.png b/ground.png new file mode 100644 index 0000000..665c5c8 Binary files /dev/null and b/ground.png differ diff --git a/happy.png b/happy.png new file mode 100644 index 0000000..4dbceb6 Binary files /dev/null and b/happy.png differ diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..0ff75f2 --- /dev/null +++ b/main.cpp @@ -0,0 +1,31 @@ +#include +/* +int main() +{ + sf::RenderWindow window(sf::VideoMode(800, 600), "SFML works!"); + + sf::Texture texture; + if (!texture.loadFromFile("happy.png")) + { + return -1; + } + sf::Sprite sprite(texture); + + sprite.setPosition(100,100); + + while (window.isOpen()) + { + sf::Event event; + while (window.pollEvent(event)) + { + if (event.type == sf::Event::Closed) + window.close(); + } + + window.clear(); + window.draw(sprite); + window.display(); + } + + return 0; +}*/ \ No newline at end of file diff --git a/map.cpp b/map.cpp new file mode 100644 index 0000000..c8d743d --- /dev/null +++ b/map.cpp @@ -0,0 +1,326 @@ +#include "map.h" + +#include "general.h" +#include "tile.h" + +#include +#include +#include + +Map::Map(int sizeX, int sizeY, int numBombs) +{ + srand((unsigned int)time(NULL)); + + m_SizeX = sizeX; + m_SizeY = sizeY; + m_numOfBombs = numBombs; + m_PlacesLeft = m_SizeX * m_SizeY - m_numOfBombs; + + m_MapDone = false; + + m_Layout = NULL; + + Generate(); +} + +Map::~Map() +{ + delete[] m_Layout; +} + +void Map::GenerateFrom(int fromX, int fromY) +{ + if (m_Layout != NULL) + { + delete[] m_Layout; + m_Layout = NULL; + } + + m_PlacesLeft = m_SizeX * m_SizeY - m_numOfBombs; + + m_Layout = new Tile[m_SizeX * m_SizeY]; + + for (int x = 0; x < m_SizeX; x++) + { + for (int y = 0; y < m_SizeY; y++) + { + m_Layout[x+y*m_SizeX].SetType(0); + } + } + int bombCount = m_numOfBombs; + while (bombCount != 0) + { + int posX = rand() % m_SizeX; + int posY = rand() % m_SizeY; + if ( posX > fromX + 1 || posX < fromX -1 || posY > fromY + 1 || posY < fromY - 1) + { + if (m_Layout[posX+posY*m_SizeX].GetType() == 0) + { + m_Layout[posX+posY*m_SizeX].SetType(BOMB); + bombCount--; + } + } + } + + for (int x = 0; x < m_SizeX; x++) + { + for (int y = 0; y < m_SizeY; y++) + { + if (m_Layout[x+y*m_SizeX].GetType() != 9) + { + int bombInNeighbours = 0; + + // CHECK LEFT THREE NEIGHBOURS + if (x > 0) + { + if (y > 0) + { + if (m_Layout[x-1+(y-1)*m_SizeX].GetType() == 9) + { + bombInNeighbours++; + } + } + if (y < m_SizeY - 1) + { + if (m_Layout[x-1+(y+1)*m_SizeX].GetType() == 9) + { + bombInNeighbours++; + } + } + if (m_Layout[x-1+y*m_SizeX].GetType() == 9) + { + bombInNeighbours++; + } + } + // CHECK RIGHT THREE NEIGHBOURS + if (x < m_SizeX - 1) + { + if (y > 0) + { + if (m_Layout[x+1+(y-1)*m_SizeX].GetType() == 9) + { + bombInNeighbours++; + } + } + if (y < m_SizeY - 1) + { + if (m_Layout[x+1+(y+1)*m_SizeX].GetType() == 9) + { + bombInNeighbours++; + } + } + if (m_Layout[x+1+y*m_SizeX].GetType() == 9) + { + bombInNeighbours++; + } + } + // CHECK MIDDLE TWO NEIGHBOURS + if (y > 0) + { + if (m_Layout[x+(y-1)*m_SizeX].GetType() == 9) + { + bombInNeighbours++; + } + } + if (y < m_SizeY - 1) + { + if (m_Layout[x+(y+1)*m_SizeX].GetType() == 9) + { + bombInNeighbours++; + } + } + + m_Layout[x+y*m_SizeX].SetType(bombInNeighbours); + + + } + //m_Layout[x+y*m_SizeX].SetSprite(); + m_Layout[x+y*m_SizeX].SetSpritePosition(x*TILE_SIZE_X, y*TILE_SIZE_Y); + } + } +} + +void Map::Generate() +{ + m_MapDone = false; + GenerateFrom(rand() % m_SizeX, rand() % m_SizeY); +} + +bool Map::GenerateFromClick(int mouseX, int mouseY) +{ + bool found = false; + int currentX = 0; + int currentY = 0; + m_MapDone = false; + for (int x = 0; x < m_SizeX; x++) + { + for (int y = 0; y < m_SizeY; y++) + { + bool revealed = m_Layout[x+y*m_SizeX].TestPosition(mouseX, mouseY); + if (revealed) + { + currentX = x; + currentY = y; + found = true; + DecrementFreePlaces(); + } + } + } + GenerateFrom(currentX, currentY); + return found; +} + +void Map::Render() +{ + for (int x = 0; x < m_SizeX; x++) + { + for (int y = 0; y < m_SizeY; y++) + { + m_Layout[x+y*m_SizeX].Render(); + } + } +} + +void Map::ClickAt(int mouseX, int mouseY) +{ + for (int x = 0; x < m_SizeX; x++) + { + for (int y = 0; y < m_SizeY; y++) + { + bool revealed = m_Layout[x+y*m_SizeX].TestClick(mouseX, mouseY); + if (revealed) + { + if (m_Layout[x+y*m_SizeX].GetType() == 0) + { + Reveal(x, y); + } + else + { + DecrementFreePlaces(); + if (m_Layout[x+y*m_SizeX].GetType() == 9) + { + m_MapDone = true; + } + } + } + } + } +} + +void Map::RightClickAt(int mouseX, int mouseY) +{ + for (int x = 0; x < m_SizeX; x++) + { + for (int y = 0; y < m_SizeY; y++) + { + bool revealed = m_Layout[x+y*m_SizeX].TestRightClick(mouseX, mouseY); + if (revealed) + { + + } + } + } +} + +void Map::Reveal(int indexX, int indexY) +{ + m_Layout[indexX+indexY*m_SizeX].Reveal(); + DecrementFreePlaces(); + if (m_Layout[indexX+indexY*m_SizeX].GetType() == 0) + { + if (indexX > 0) + { + if (indexY > 0) + { + if(m_Layout[indexX - 1 + (indexY - 1) * m_SizeX].IsRevealed() != true) + { + Reveal(indexX - 1, indexY - 1); + } + } + if (indexY < m_SizeY - 1) + { + if(m_Layout[indexX - 1 + (indexY + 1) * m_SizeX].IsRevealed() != true) + { + Reveal(indexX - 1, indexY + 1); + } + } + + if(m_Layout[indexX - 1 + indexY * m_SizeX].IsRevealed() != true) + { + Reveal(indexX - 1, indexY); + } + } + if (indexX < m_SizeX - 1) + { + if (indexY > 0) + { + if(m_Layout[indexX + 1 + (indexY - 1) * m_SizeX].IsRevealed() != true) + { + Reveal(indexX + 1, indexY - 1); + } + } + if (indexY < m_SizeY - 1) + { + if(m_Layout[indexX + 1 + (indexY + 1) * m_SizeX].IsRevealed() != true) + { + Reveal(indexX + 1, indexY + 1); + } + } + + if(m_Layout[indexX + 1 + indexY * m_SizeX].IsRevealed() != true) + { + Reveal(indexX + 1, indexY); + } + } + if (indexY > 0) + { + if(m_Layout[indexX + (indexY - 1) * m_SizeX].IsRevealed() != true) + { + Reveal(indexX, indexY - 1); + } + } + if (indexY < m_SizeY - 1) + { + if(m_Layout[indexX + (indexY + 1) * m_SizeX].IsRevealed() != true) + { + Reveal(indexX, indexY + 1); + } + } + } + +} + + +void Map::DecrementFreePlaces() +{ + m_PlacesLeft--; + if (m_PlacesLeft == 0) + { + m_MapDone = true; + } +} + +void Map::WinMap() +{ + for (int x = 0; x < m_SizeX; x++) + { + for (int y = 0; y < m_SizeY; y++) + { + m_Layout[x+y*m_SizeX].SetType(10); + m_Layout[x+y*m_SizeX].SetSpritePosition(x*TILE_SIZE_X, y*TILE_SIZE_Y); + m_Layout[x+y*m_SizeX].Finish(); + } + } +} + + +void Map::LoseMap() +{ + for (int x = 0; x < m_SizeX; x++) + { + for (int y = 0; y < m_SizeY; y++) + { + m_Layout[x+y*m_SizeX].Finish(); + } + } +} \ No newline at end of file diff --git a/map.h b/map.h new file mode 100644 index 0000000..eef0e97 --- /dev/null +++ b/map.h @@ -0,0 +1,44 @@ +// MAP CLASS, CONTAINS ALL LOGIC AND WINNING/LOSING TESTS + +#ifndef MAP_H +#define MAP_H + +class Tile; + +class Map +{ +private: + Tile* m_Layout; + + int m_SizeX; + int m_SizeY; + int m_numOfBombs; + + int m_PlacesLeft; + bool m_MapDone; + + void DecrementFreePlaces(); + +public: + Map(int sizeX, int sizeY, int numBombs); + ~Map(); + + void Render(); // render to window + + inline bool IsMapWon() { return m_PlacesLeft == 0; } + inline bool IsMapDone() { return m_MapDone; } + void WinMap(); + void LoseMap(); + + // generate map depending on amount of parameters + void Generate(); + void GenerateFrom(int x, int y); + bool GenerateFromClick(int mouseX, int mouseY); + + // process input + void ClickAt(int mouseX, int mouseY); + void RightClickAt(int mouseX, int mouseY); + void Reveal(int indexX, int indexY); +}; + +#endif diff --git a/minesweeper.cpp b/minesweeper.cpp new file mode 100644 index 0000000..cff76f5 --- /dev/null +++ b/minesweeper.cpp @@ -0,0 +1,21 @@ +// A game of minesweeper, written in one day. +// +// CONTROLS: +// Left mouse click - test tile for mine +// Right mouse click - mark tile with a flag or remove a flag +// 'R' key - reset the game +// Esc - exit game + +#include "game.h" + +#ifdef _DEBUG +#include // Visual Leak Detector +#endif + +int main() +{ + Game *game = new Game(); + game->MainLoop(); + delete game; + return 0; +} \ No newline at end of file diff --git a/tile.cpp b/tile.cpp new file mode 100644 index 0000000..3cfcb59 --- /dev/null +++ b/tile.cpp @@ -0,0 +1,79 @@ +#include "tile.h" + +#include "general.h" +#include "GFXframework.h" + +Tile::Tile() +{ + m_Revealed = false; + m_Flagged = false; +} + +Tile::~Tile() +{ +} + + +void Tile::SetSpritePosition(int x, int y) +{ + + m_PosX = x; + m_PosY = y; +} + +void Tile::Render() +{ + if (!m_Flagged) + { + if (m_Revealed) + { + GFXframework::GetInstance()->DisplaySpriteAt(m_Type, m_PosX, m_PosY); + } + else + { + GFXframework::GetInstance()->DisplaySpriteAt(GROUND, m_PosX, m_PosY); + } + } + else + { + GFXframework::GetInstance()->DisplaySpriteAt(FLAG, m_PosX, m_PosY); + } +} + +bool Tile::TestClick(int x, int y) +{ + if ( x > m_PosX && x <= m_PosX + TILE_SIZE_X && + y > m_PosY && y <= m_PosY + TILE_SIZE_Y) + { + if (!m_Revealed && !m_Flagged) + { + m_Revealed = true; + return true; + } + } + return false; +} + +bool Tile::TestRightClick(int x, int y) +{ + if ( x > m_PosX && x <= m_PosX + TILE_SIZE_X && + y > m_PosY && y <= m_PosY + TILE_SIZE_Y) + { + if (!m_Revealed) + { + m_Flagged = !m_Flagged; + return true; + } + } + return false; +} + +bool Tile::TestPosition(int x, int y) +{ + if ( x > m_PosX && x <= m_PosX + TILE_SIZE_X && + y > m_PosY && y <= m_PosY + TILE_SIZE_Y) + { + return true; + } + return false; +} \ No newline at end of file diff --git a/tile.h b/tile.h new file mode 100644 index 0000000..80ba3ab --- /dev/null +++ b/tile.h @@ -0,0 +1,39 @@ +// TILE CLASS, CONTAINS GRAPHICS AND COLLISION TESTS + +#ifndef TILE_H +#define TILE_H + +#include + +class Tile +{ +private: + int m_Type; + bool m_Revealed; + bool m_Flagged; + int m_PosX; + int m_PosY; +public: + Tile(); + ~Tile(); + + inline int GetType() { return m_Type; } + inline void SetType(int type) { m_Type = type; } + + void SetSpritePosition(int x, int y); + + bool IsRevealed() { return m_Revealed; } + inline void Reveal() { m_Revealed = true; } + + // used to fully reveal the tile once the game is done + inline void Finish() { m_Revealed = true; m_Flagged = false; } + + // tests for collision and mechanics for processing the input + bool TestPosition(int x, int y); + bool TestClick(int x, int y); + bool TestRightClick(int x, int y); + + void Render(); +}; + +#endif \ No newline at end of file