Compare commits
17 Commits
xmake
...
8a4c4201fe
| Author | SHA1 | Date | |
|---|---|---|---|
| 8a4c4201fe | |||
| c08cfc2255 | |||
| 38008e00bc | |||
| 507bc9cc86 | |||
| e721a71894 | |||
| 1781b85332 | |||
| 92b58c4b98 | |||
| 8635d4b853 | |||
| 9780a36af4 | |||
| 6b16abda6a | |||
| 30dd323e22 | |||
| d87ddcdc22 | |||
| 8aaced68d0 | |||
| be6c8d9f77 | |||
| d9ccecfdd8 | |||
| 02bab6ed87 | |||
| 0e17996c35 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -11,6 +11,7 @@ build/
|
|||||||
# personnal documentation
|
# personnal documentation
|
||||||
doc/*.txt
|
doc/*.txt
|
||||||
doc/*.violet.html
|
doc/*.violet.html
|
||||||
|
doc/mockups/*
|
||||||
|
|
||||||
# pieces files
|
# pieces files
|
||||||
data/pieces/*.bin
|
data/pieces/*.bin
|
||||||
|
|||||||
2
.vscode/c_cpp_properties.json
vendored
2
.vscode/c_cpp_properties.json
vendored
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"configurations": [
|
"configurations": [
|
||||||
{
|
{
|
||||||
"name": "JMinos",
|
"name": "jminos",
|
||||||
"cppStandard": "c++20",
|
"cppStandard": "c++20",
|
||||||
"compileCommands": ".vscode/compile_commands.json"
|
"compileCommands": ".vscode/compile_commands.json"
|
||||||
}
|
}
|
||||||
|
|||||||
15
README.md
15
README.md
@@ -1,8 +1,11 @@
|
|||||||
# jminos
|
# jminos
|
||||||
|
|
||||||
|
Modern stacker game with every polyominos from size 1 to 15, made in C++ with [SFML 3](https://www.sfml-dev.org/)!
|
||||||
|
|
||||||
## Manual build and run
|
## Manual build and run
|
||||||
|
|
||||||
You need to install xmake and have a compiler with c++20 compatibility
|
This project uses xmake for compiling.
|
||||||
|
To be able to build it, you need to [install xmake](https://xmake.io) and have a compiler with c++20 compatibility, xmake will install SFML for you.
|
||||||
|
|
||||||
### Build the project
|
### Build the project
|
||||||
|
|
||||||
@@ -15,8 +18,12 @@ If you need to change the toolchain (for example using gcc):
|
|||||||
|
|
||||||
### Run the project
|
### Run the project
|
||||||
|
|
||||||
Graphical version:
|
``xmake run``
|
||||||
``xmake run graph``
|
Note that the program will generate the polyomino files for you. This can be quite long so it only does it up to size 10.
|
||||||
|
|
||||||
Command line version:
|
If for some reasons you wanna run the command line version:
|
||||||
``xmake run text``
|
``xmake run text``
|
||||||
|
|
||||||
|
## Credits
|
||||||
|
|
||||||
|
Font used: [Press Start](https://www.zone38.net/font/#pressstart).
|
||||||
|
|||||||
BIN
data/config/keybinds/layout0.bin
Normal file
BIN
data/config/keybinds/layout0.bin
Normal file
Binary file not shown.
BIN
data/config/keybinds/layout1.bin
Normal file
BIN
data/config/keybinds/layout1.bin
Normal file
Binary file not shown.
BIN
data/config/keybinds/layout2.bin
Normal file
BIN
data/config/keybinds/layout2.bin
Normal file
Binary file not shown.
BIN
data/config/keybinds/layout3.bin
Normal file
BIN
data/config/keybinds/layout3.bin
Normal file
Binary file not shown.
BIN
data/config/keybinds/layout4.bin
Normal file
BIN
data/config/keybinds/layout4.bin
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
17
data/fonts/pressstart/license.txt
Normal file
17
data/fonts/pressstart/license.txt
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
Thanks for downloading one of codeman38's retro video game fonts, as seen on Memepool, BoingBoing, and all around the blogosphere.
|
||||||
|
|
||||||
|
So, you're wondering what the license is for these fonts? Pretty simple; it's based upon that used for Bitstream's Vera font set <http://www.gnome.org/fonts/>.
|
||||||
|
|
||||||
|
Basically, here are the key points summarized, in as little legalese as possible; I hate reading license agreements as much as you probably do:
|
||||||
|
|
||||||
|
With one specific exception, you have full permission to bundle these fonts in your own free or commercial projects-- and by projects, I'm referring to not just software but also electronic documents and print publications.
|
||||||
|
|
||||||
|
So what's the exception? Simple: you can't re-sell these fonts in a commercial font collection. I've seen too many font CDs for sale in stores that are just a repackaging of thousands of freeware fonts found on the internet, and in my mind, that's quite a bit like highway robbery. Note that this *only* applies to products that are font collections in and of themselves; you may freely bundle these fonts with an operating system, application program, or the like.
|
||||||
|
|
||||||
|
Feel free to modify these fonts and even to release the modified versions, as long as you change the original font names (to ensure consistency among people with the font installed) and as long as you give credit somewhere in the font file to codeman38 or zone38.net. I may even incorporate these changes into a later version of my fonts if you wish to send me the modifed fonts via e-mail.
|
||||||
|
|
||||||
|
Also, feel free to mirror these fonts on your own site, as long as you make it reasonably clear that these fonts are not your own work. I'm not asking for much; linking to zone38.net or even just mentioning the nickname codeman38 should be enough.
|
||||||
|
|
||||||
|
Well, that pretty much sums it up... so without further ado, install and enjoy these fonts from the golden age of video games.
|
||||||
|
|
||||||
|
[ codeman38 | cody@zone38.net | http://www.zone38.net/ ]
|
||||||
BIN
data/fonts/pressstart/prstart.ttf
Normal file
BIN
data/fonts/pressstart/prstart.ttf
Normal file
Binary file not shown.
BIN
data/fonts/pressstart/prstartk.ttf
Normal file
BIN
data/fonts/pressstart/prstartk.ttf
Normal file
Binary file not shown.
@@ -38,7 +38,7 @@ _Repeat for every avaible actions._
|
|||||||
The settings file has the following format:
|
The settings file has the following format:
|
||||||
|
|
||||||
- The number of the chosen keybinds (from 0 to 4), stored with 1 byte
|
- The number of the chosen keybinds (from 0 to 4), stored with 1 byte
|
||||||
- The size multiplier of the window, stored with 1 byte
|
- The window size mode, stored with 1 byte
|
||||||
- The number of the last selected gamemode (converted from an Enum), stored with 1 byte
|
- The number of the last selected gamemode (converted from an Enum), stored with 1 byte
|
||||||
- The last selected width of the board, stored with 1 byte
|
- The last selected width of the board, stored with 1 byte
|
||||||
- The last selected height of the board, stored with 1 byte
|
- The last selected height of the board, stored with 1 byte
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ We will only talk about pieces and not polyominoes. In this project, pieces are
|
|||||||
|
|
||||||
Each frame, the UI will translate the user's input into a series of action to apply to the game. The list of action is the following:
|
Each frame, the UI will translate the user's input into a series of action to apply to the game. The list of action is the following:
|
||||||
|
|
||||||
- Quit the game
|
|
||||||
- Pause
|
- Pause
|
||||||
- Retry
|
- Retry
|
||||||
- Hold
|
- Hold
|
||||||
@@ -78,8 +77,9 @@ Since this game uses polyomino of high sizes which are very unplayable, we will
|
|||||||
5. Do the same as step 4 but now we move the piece one line up every time
|
5. Do the same as step 4 but now we move the piece one line up every time
|
||||||
6. Cancel the rotation
|
6. Cancel the rotation
|
||||||
|
|
||||||
Kicking is primarly designed for rotating, but it is also applied when a piece spawns into a wall.
|
_Note: at step 3, the direction which is checked first is actually the last movement done by the player._
|
||||||
0° rotations will first try to move the piece one position down, and if it can't try kicking the piece, only registering a kick if the piece couldn't move down.
|
Kicking is primarly designed for rotating, but there is also a 0° rotation applied when a piece spawns into a wall.
|
||||||
|
0° rotations will first try to move the piece one position down, and if it can't, try kicking the piece, only registering a kick if the piece couldn't move down.
|
||||||
|
|
||||||
## Detecting spins
|
## Detecting spins
|
||||||
|
|
||||||
|
|||||||
@@ -4,10 +4,9 @@
|
|||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The list of actions that can be taken by the player
|
* The list of in-game actions that can be taken by the player
|
||||||
*/
|
*/
|
||||||
enum Action {
|
enum Action {
|
||||||
QUIT,
|
|
||||||
PAUSE,
|
PAUSE,
|
||||||
RETRY,
|
RETRY,
|
||||||
HOLD,
|
HOLD,
|
||||||
@@ -22,8 +21,20 @@ enum Action {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
static const std::string ACTION_NAMES[] = { // name for each action
|
static const Action ACTION_LIST_IN_ORDER[] = { // the list of possible actions in a sorted order
|
||||||
"Quit",
|
MOVE_LEFT,
|
||||||
|
MOVE_RIGHT,
|
||||||
|
SOFT_DROP,
|
||||||
|
HARD_DROP,
|
||||||
|
ROTATE_CW,
|
||||||
|
ROTATE_CCW,
|
||||||
|
ROTATE_180,
|
||||||
|
ROTATE_0,
|
||||||
|
HOLD,
|
||||||
|
PAUSE,
|
||||||
|
RETRY
|
||||||
|
};
|
||||||
|
static const std::string ACTION_NAMES[] = { // name representation for each actions
|
||||||
"Pause",
|
"Pause",
|
||||||
"Retry",
|
"Retry",
|
||||||
"Hold",
|
"Hold",
|
||||||
|
|||||||
@@ -55,6 +55,8 @@ void Game::initialize() {
|
|||||||
void Game::nextFrame(const std::set<Action>& playerActions) {
|
void Game::nextFrame(const std::set<Action>& playerActions) {
|
||||||
if (this->lost || this->hasWon()) return;
|
if (this->lost || this->hasWon()) return;
|
||||||
|
|
||||||
|
bool pieceJustLocked = false;
|
||||||
|
|
||||||
if (this->started) {
|
if (this->started) {
|
||||||
bool AREJustEnded = (this->leftARETime == 1);
|
bool AREJustEnded = (this->leftARETime == 1);
|
||||||
if (this->leftARETime > 0) {
|
if (this->leftARETime > 0) {
|
||||||
@@ -84,13 +86,14 @@ void Game::nextFrame(const std::set<Action>& playerActions) {
|
|||||||
|
|
||||||
if (this->lost) {
|
if (this->lost) {
|
||||||
if (initialRotation == NONE) {
|
if (initialRotation == NONE) {
|
||||||
this->lost = (!this->board.rotate(initialRotation));
|
this->board.rotate(NONE);
|
||||||
|
this->lost = this->board.activePieceInWall();
|
||||||
|
if (this->lost) {
|
||||||
|
this->framesPassed++;
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (this->lost) {
|
|
||||||
this->framesPassed++;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* HOLD */
|
/* HOLD */
|
||||||
if (playerActions.contains(HOLD) && (!this->heldActions.contains(HOLD))) {
|
if (playerActions.contains(HOLD) && (!this->heldActions.contains(HOLD))) {
|
||||||
@@ -158,6 +161,7 @@ void Game::nextFrame(const std::set<Action>& playerActions) {
|
|||||||
this->score += HARD_DROP_SCORE;
|
this->score += HARD_DROP_SCORE;
|
||||||
}
|
}
|
||||||
this->lockPiece();
|
this->lockPiece();
|
||||||
|
pieceJustLocked = true;
|
||||||
}
|
}
|
||||||
// no need to apply gravity and lock delay if the piece was hard dropped
|
// no need to apply gravity and lock delay if the piece was hard dropped
|
||||||
else {
|
else {
|
||||||
@@ -187,6 +191,7 @@ void Game::nextFrame(const std::set<Action>& playerActions) {
|
|||||||
|
|
||||||
if ((this->totalLockDelay > this->parameters.getLockDelay()) || (this->totalForcedLockDelay > this->parameters.getForcedLockDelay())) {
|
if ((this->totalLockDelay > this->parameters.getLockDelay()) || (this->totalForcedLockDelay > this->parameters.getForcedLockDelay())) {
|
||||||
this->lockPiece();
|
this->lockPiece();
|
||||||
|
pieceJustLocked = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -201,11 +206,11 @@ void Game::nextFrame(const std::set<Action>& playerActions) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this->heldActions = playerActions;
|
|
||||||
|
|
||||||
if ((!this->started) || this->leftARETime > 0) {
|
if ((!this->started) || this->leftARETime > 0) {
|
||||||
for (Action action : playerActions) {
|
for (Action action : playerActions) {
|
||||||
this->initialActions.insert(action);
|
if ((!pieceJustLocked) && (!heldActions.contains(action))) {
|
||||||
|
this->initialActions.insert(action);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this->heldDAS >= 0) {
|
if (this->heldDAS >= 0) {
|
||||||
@@ -219,6 +224,8 @@ void Game::nextFrame(const std::set<Action>& playerActions) {
|
|||||||
else this->heldDAS = 0;
|
else this->heldDAS = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this->heldActions = playerActions;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Game::resetPiece(bool newPiece) {
|
void Game::resetPiece(bool newPiece) {
|
||||||
@@ -302,6 +309,11 @@ void Game::lockPiece() {
|
|||||||
if (this->leftARETime == 0) {
|
if (this->leftARETime == 0) {
|
||||||
this->lost = this->board.spawnNextPiece();
|
this->lost = this->board.spawnNextPiece();
|
||||||
this->resetPiece(true);
|
this->resetPiece(true);
|
||||||
|
|
||||||
|
if (this->lost) {
|
||||||
|
this->board.rotate(NONE);
|
||||||
|
this->lost = this->board.activePieceInWall();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -334,12 +346,16 @@ bool Game::isOnB2BChain() const {
|
|||||||
return this->B2BChain;
|
return this->B2BChain;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Game::areBlocksBones() const {
|
float Game::getLockDelayProgression() const {
|
||||||
return this->parameters.getBoneBlocks();
|
return (float) this->totalLockDelay / this->parameters.getLockDelay();
|
||||||
}
|
}
|
||||||
|
|
||||||
Position Game::ghostPiecePosition() const {
|
float Game::getForcedLockDelayProgression() const {
|
||||||
return this->board.lowestPosition();
|
return (float) this->totalForcedLockDelay / this->parameters.getForcedLockDelay();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Game::areBlocksBones() const {
|
||||||
|
return this->parameters.getBoneBlocks();
|
||||||
}
|
}
|
||||||
|
|
||||||
const Board& Game::getBoard() const {
|
const Board& Game::getBoard() const {
|
||||||
@@ -354,6 +370,10 @@ const Position& Game::getActivePiecePosition() const {
|
|||||||
return this->board.getActivePiecePosition();
|
return this->board.getActivePiecePosition();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Position Game::getGhostPiecePosition() const {
|
||||||
|
return this->board.lowestPosition();
|
||||||
|
}
|
||||||
|
|
||||||
const std::shared_ptr<Piece>& Game::getHeldPiece() const {
|
const std::shared_ptr<Piece>& Game::getHeldPiece() const {
|
||||||
return this->board.getHeldPiece();
|
return this->board.getHeldPiece();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -116,16 +116,21 @@ class Game {
|
|||||||
*/
|
*/
|
||||||
bool isOnB2BChain() const;
|
bool isOnB2BChain() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return How close the active piece's lock delay is to the maximum allowed, betwwen 0 and 1
|
||||||
|
*/
|
||||||
|
float getLockDelayProgression() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return How close the active piece's forced lock delay is to the maximum allowed, betwwen 0 and 1
|
||||||
|
*/
|
||||||
|
float getForcedLockDelayProgression() const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return If all blocks are currently bone blocks
|
* @return If all blocks are currently bone blocks
|
||||||
*/
|
*/
|
||||||
bool areBlocksBones() const;
|
bool areBlocksBones() const;
|
||||||
|
|
||||||
/**
|
|
||||||
* @return The position of the ghost piece
|
|
||||||
*/
|
|
||||||
Position ghostPiecePosition() const;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return The board
|
* @return The board
|
||||||
*/
|
*/
|
||||||
@@ -141,6 +146,11 @@ class Game {
|
|||||||
*/
|
*/
|
||||||
const Position& getActivePiecePosition() const;
|
const Position& getActivePiecePosition() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return The position of the ghost piece
|
||||||
|
*/
|
||||||
|
Position getGhostPiecePosition() const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return A pointer to the held piece, can be null
|
* @return A pointer to the held piece, can be null
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -36,9 +36,12 @@ void GameBoard::initialize() {
|
|||||||
this->activePiece = nullptr;
|
this->activePiece = nullptr;
|
||||||
this->heldPiece = nullptr;
|
this->heldPiece = nullptr;
|
||||||
this->isLastMoveKick = false;
|
this->isLastMoveKick = false;
|
||||||
|
this->movedLeftLast = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GameBoard::moveLeft() {
|
bool GameBoard::moveLeft() {
|
||||||
|
this->movedLeftLast = true;
|
||||||
|
|
||||||
if (this->activePieceInWall(Position{-1, 0})) {
|
if (this->activePieceInWall(Position{-1, 0})) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -50,6 +53,8 @@ bool GameBoard::moveLeft() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool GameBoard::moveRight() {
|
bool GameBoard::moveRight() {
|
||||||
|
this->movedLeftLast = false;
|
||||||
|
|
||||||
if (this->activePieceInWall(Position{1, 0})) {
|
if (this->activePieceInWall(Position{1, 0})) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -137,30 +142,21 @@ bool GameBoard::tryKicking(bool testingBottom, const std::set<Position>& safePos
|
|||||||
bool overlapsRight = true;
|
bool overlapsRight = true;
|
||||||
int i = (j == 0) ? 1 : 0;
|
int i = (j == 0) ? 1 : 0;
|
||||||
do {
|
do {
|
||||||
// check right before left arbitrarly, we don't decide this with rotations since it would still be arbitrary with 180° rotations
|
// we first check the side to which the player moved last
|
||||||
if (overlapsRight) {
|
if (movedLeftLast) {
|
||||||
Position shift{+i, j};
|
if (overlapsLeft) {
|
||||||
if (!this->activePieceOverlaps(safePositions, shift)) {
|
if (this->tryFittingKickedPiece(safePositions, Position({-i, j}), overlapsLeft)) return true;
|
||||||
overlapsLeft = false;
|
|
||||||
}
|
}
|
||||||
else {
|
if (overlapsRight) {
|
||||||
if (!this->activePieceInWall(shift)) {
|
if (this->tryFittingKickedPiece(safePositions, Position({+i, j}), overlapsRight)) return true;
|
||||||
this->activePiecePosition += shift;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
if (overlapsLeft) {
|
if (overlapsRight) {
|
||||||
Position shift{-i, j};
|
if (this->tryFittingKickedPiece(safePositions, Position({+i, j}), overlapsRight)) return true;
|
||||||
if (!this->activePieceOverlaps(safePositions, shift)) {
|
|
||||||
overlapsLeft = false;
|
|
||||||
}
|
}
|
||||||
else {
|
if (overlapsLeft) {
|
||||||
if (!this->activePieceInWall(shift)) {
|
if (this->tryFittingKickedPiece(safePositions, Position({-i, j}), overlapsLeft)) return true;
|
||||||
this->activePiecePosition += shift;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -177,6 +173,26 @@ bool GameBoard::tryKicking(bool testingBottom, const std::set<Position>& safePos
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool GameBoard::tryFittingKickedPiece(const std::set<Position>& safePositions, const Position& shift, bool& overlaps) {
|
||||||
|
if (!this->activePieceOverlaps(safePositions, shift)) {
|
||||||
|
overlaps = false;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (!this->activePieceInWall(shift)) {
|
||||||
|
this->activePiecePosition += shift;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GameBoard::activePieceOverlaps(const std::set<Position>& safePositions, const Position& shift) const {
|
||||||
|
for (Position position : this->activePiece->getPositions()) {
|
||||||
|
if (safePositions.contains(position + this->activePiecePosition + shift)) return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
bool GameBoard::hold(Rotation initialRotation) {
|
bool GameBoard::hold(Rotation initialRotation) {
|
||||||
std::swap(this->activePiece, this->heldPiece);
|
std::swap(this->activePiece, this->heldPiece);
|
||||||
|
|
||||||
@@ -236,6 +252,13 @@ bool GameBoard::spawnNextPiece() {
|
|||||||
return this->activePieceInWall();
|
return this->activePieceInWall();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool GameBoard::activePieceInWall(const Position& shift) const {
|
||||||
|
for (Position position : this->activePiece->getPositions()) {
|
||||||
|
if (this->board.getBlock(position + this->activePiecePosition + shift) != NOTHING) return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
bool GameBoard::touchesGround() const {
|
bool GameBoard::touchesGround() const {
|
||||||
return this->activePieceInWall(Position{0, -1});
|
return this->activePieceInWall(Position{0, -1});
|
||||||
}
|
}
|
||||||
@@ -257,6 +280,8 @@ LineClear GameBoard::lockPiece() {
|
|||||||
this->board.changeBlock(position + this->activePiecePosition, this->activePiece->getBlockType());
|
this->board.changeBlock(position + this->activePiecePosition, this->activePiece->getBlockType());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this->activePiece = nullptr;
|
||||||
|
|
||||||
return LineClear{this->board.clearRows(), isLockedInPlace, (!isLockedInPlace) && this->isLastMoveKick};
|
return LineClear{this->board.clearRows(), isLockedInPlace, (!isLockedInPlace) && this->isLastMoveKick};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -291,20 +316,6 @@ const std::vector<Piece>& GameBoard::getNextPieces() const {
|
|||||||
return this->nextQueue;
|
return this->nextQueue;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GameBoard::activePieceInWall(const Position& shift) const {
|
|
||||||
for (Position position : this->activePiece->getPositions()) {
|
|
||||||
if (this->board.getBlock(position + this->activePiecePosition + shift) != NOTHING) return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool GameBoard::activePieceOverlaps(const std::set<Position>& safePositions, const Position& shift) const {
|
|
||||||
for (Position position : this->activePiece->getPositions()) {
|
|
||||||
if (safePositions.contains(position + this->activePiecePosition + shift)) return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void GameBoard::goToSpawnPosition() {
|
void GameBoard::goToSpawnPosition() {
|
||||||
int lowestPosition = this->activePiece->getLength() - 1;
|
int lowestPosition = this->activePiece->getLength() - 1;
|
||||||
for (Position position : this->activePiece->getPositions()) {
|
for (Position position : this->activePiece->getPositions()) {
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ class GameBoard {
|
|||||||
int nextQueueLength; // the number of next pieces seeable at a time
|
int nextQueueLength; // the number of next pieces seeable at a time
|
||||||
std::vector<Piece> nextQueue; // the list of the next pieces to spawn in the board
|
std::vector<Piece> nextQueue; // the list of the next pieces to spawn in the board
|
||||||
bool isLastMoveKick; // wheter the last action the piece did was kicking
|
bool isLastMoveKick; // wheter the last action the piece did was kicking
|
||||||
|
bool movedLeftLast; // wheter the last sideway movement was a left one
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/**
|
/**
|
||||||
@@ -72,6 +73,18 @@ class GameBoard {
|
|||||||
*/
|
*/
|
||||||
bool tryKicking(bool testingBottom, const std::set<Position>& safePositions);
|
bool tryKicking(bool testingBottom, const std::set<Position>& safePositions);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tries fitting the kicked active piece at the specified position
|
||||||
|
* @return If it suceeded
|
||||||
|
*/
|
||||||
|
bool tryFittingKickedPiece(const std::set<Position>& safePositions, const Position& shift, bool& overlaps);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if one of the active piece's positions shifted by a specified position would overlap with a set of positions
|
||||||
|
* @return If the shifted active piece overlaps with one of the position
|
||||||
|
*/
|
||||||
|
bool activePieceOverlaps(const std::set<Position>& safePositions, const Position& shift = Position{0, 0}) const;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/**
|
/**
|
||||||
* Tries holding the active piece or swapping it if one was already stocked, while trying to apply an initial rotation to the newly spawned piece
|
* Tries holding the active piece or swapping it if one was already stocked, while trying to apply an initial rotation to the newly spawned piece
|
||||||
@@ -85,6 +98,12 @@ class GameBoard {
|
|||||||
*/
|
*/
|
||||||
bool spawnNextPiece();
|
bool spawnNextPiece();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if one of the active piece's positions touches a wall in the board
|
||||||
|
* @return If the active piece is in a wall
|
||||||
|
*/
|
||||||
|
bool activePieceInWall(const Position& shift = Position{0, 0}) const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks is the active piece as a wall directly below one of its position
|
* Checks is the active piece as a wall directly below one of its position
|
||||||
* @return If it touches a ground
|
* @return If it touches a ground
|
||||||
@@ -134,18 +153,6 @@ class GameBoard {
|
|||||||
const std::vector<Piece>& getNextPieces() const;
|
const std::vector<Piece>& getNextPieces() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/**
|
|
||||||
* Checks if one of the active piece's positions touches a wall in the board
|
|
||||||
* @return If the active piece spawned in a wall
|
|
||||||
*/
|
|
||||||
bool activePieceInWall(const Position& shift = Position{0, 0}) const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if one of the active piece's positions shifted by a specified position would overlap with a set of positions
|
|
||||||
* @return If the shifted active piece overlaps with one of the position
|
|
||||||
*/
|
|
||||||
bool activePieceOverlaps(const std::set<Position>& safePositions, const Position& shift = Position{0, 0}) const;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the active piece to its spawn position
|
* Sets the active piece to its spawn position
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ void GameParameters::clearLines(int lineNumber) {
|
|||||||
|
|
||||||
// level increments every 10 lines, stats only changes on level up
|
// level increments every 10 lines, stats only changes on level up
|
||||||
if (previousLines / 10 < this->clearedLines / 10) {
|
if (previousLines / 10 < this->clearedLines / 10) {
|
||||||
this->level = this->clearedLines / 10;
|
this->level += (this->clearedLines / 10 - previousLines / 10);
|
||||||
this->updateStats();
|
this->updateStats();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -6,6 +6,9 @@
|
|||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
|
static const int DEFAULT_BOARD_WIDTH = 10; // the default width of the board when starting the menu
|
||||||
|
static const int DEFAULT_BOARD_HEIGHT = 20; // the default height of the board when starting the menu
|
||||||
|
|
||||||
|
|
||||||
Menu::Menu() {
|
Menu::Menu() {
|
||||||
this->piecesList = std::make_shared<PiecesList>(PiecesList());
|
this->piecesList = std::make_shared<PiecesList>(PiecesList());
|
||||||
|
|||||||
@@ -4,9 +4,7 @@
|
|||||||
#include "Player.h"
|
#include "Player.h"
|
||||||
#include "Game.h"
|
#include "Game.h"
|
||||||
|
|
||||||
static const int FRAMES_PER_SECOND = 60; // the number of frames per second, all the values in the app were choosen with this number in mind
|
static const int FRAMES_PER_SECOND = 60; // the number of frames per second, all the values in the app were choosen with this number in mind
|
||||||
static const int DEFAULT_BOARD_WIDTH = 10; // the default width of the board when starting the menu
|
|
||||||
static const int DEFAULT_BOARD_HEIGHT = 20; // the default height of the board when starting the menu
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -1,20 +1,27 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "Settings.h"
|
#include "../Settings.h"
|
||||||
|
|
||||||
#include <stack>
|
#include <stack>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <SFML/Graphics.hpp>
|
#include <SFML/Graphics.hpp>
|
||||||
|
|
||||||
|
class AppMenu;
|
||||||
|
using MenuStack = std::stack<std::shared_ptr<AppMenu>>;
|
||||||
|
|
||||||
|
|
||||||
class AppMenu {
|
class AppMenu {
|
||||||
protected:
|
protected:
|
||||||
std::shared_ptr<std::stack<AppMenu>> menuStack;
|
std::shared_ptr<MenuStack> menuStack;
|
||||||
std::shared_ptr<Settings> settings;
|
std::shared_ptr<Settings> settings;
|
||||||
std::shared_ptr<sf::RenderWindow> renderWindow;
|
std::shared_ptr<sf::RenderWindow> renderWindow;
|
||||||
|
bool enterPressed = false;
|
||||||
|
bool enterReleased = false;
|
||||||
|
bool escPressed = false;
|
||||||
|
bool escReleased = false;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
AppMenu(std::shared_ptr<std::stack<AppMenu>> menuStack, std::shared_ptr<Settings> settings, std::shared_ptr<sf::RenderWindow> renderWindow) :
|
AppMenu(std::shared_ptr<MenuStack> menuStack, std::shared_ptr<Settings> settings, std::shared_ptr<sf::RenderWindow> renderWindow) :
|
||||||
menuStack(menuStack),
|
menuStack(menuStack),
|
||||||
settings(settings),
|
settings(settings),
|
||||||
renderWindow(renderWindow)
|
renderWindow(renderWindow)
|
||||||
@@ -22,6 +29,26 @@ class AppMenu {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void updateMetaBinds() {
|
||||||
|
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Key::Enter)) {
|
||||||
|
enterPressed = true;
|
||||||
|
enterReleased = false;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
enterReleased = enterPressed;
|
||||||
|
enterPressed = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Key::Escape)) {
|
||||||
|
escPressed = true;
|
||||||
|
escReleased = false;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
escReleased = escPressed;
|
||||||
|
escPressed = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
virtual void computeFrame() = 0;
|
virtual void computeFrame() = 0;
|
||||||
|
|
||||||
virtual void drawFrame() const = 0;
|
virtual void drawFrame() const = 0;
|
||||||
|
|||||||
188
src/GraphicalUI/AppMenus/GamePlayingAppMenu.cpp
Normal file
188
src/GraphicalUI/AppMenus/GamePlayingAppMenu.cpp
Normal file
@@ -0,0 +1,188 @@
|
|||||||
|
#include "GamePlayingAppMenu.h"
|
||||||
|
|
||||||
|
#include "AppMenu.h"
|
||||||
|
|
||||||
|
#include <stack>
|
||||||
|
#include <memory>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cmath>
|
||||||
|
#include <SFML/Graphics.hpp>
|
||||||
|
|
||||||
|
|
||||||
|
GamePlayingAppMenu::GamePlayingAppMenu(std::shared_ptr<MenuStack> menuStack, std::shared_ptr<Settings> settings, std::shared_ptr<sf::RenderWindow> renderWindow) :
|
||||||
|
AppMenu(menuStack, settings, renderWindow),
|
||||||
|
game(this->settings->getMenu().startGame(this->settings->getGamemode())) {
|
||||||
|
|
||||||
|
this->game.start();
|
||||||
|
this->paused = false;
|
||||||
|
this->pausePressed = false;
|
||||||
|
this->retryPressed = false;
|
||||||
|
|
||||||
|
int maxWidthMultiplier = (this->settings->getWindowSizeMultiplier() * 40) / (this->game.getBoard().getWidth());
|
||||||
|
int maxHeightMultiplier = (this->settings->getWindowSizeMultiplier() * 50) / (this->game.getBoard().getBaseHeight() + 10);
|
||||||
|
this->cellSizeZoom = std::min(maxWidthMultiplier, maxHeightMultiplier);
|
||||||
|
|
||||||
|
float boardWidth = this->game.getBoard().getWidth() * this->cellSizeZoom;
|
||||||
|
float boardHeight = (this->game.getBoard().getBaseHeight() + 10) * this->cellSizeZoom;
|
||||||
|
this->boardPosition = sf::Rect<float>(sf::Vector2f((this->settings->getWindowSizeMultiplier() * 40) - (boardWidth / 2),
|
||||||
|
(this->settings->getWindowSizeMultiplier() * 25) - (boardHeight / 2)),
|
||||||
|
sf::Vector2f(boardWidth, boardHeight));
|
||||||
|
}
|
||||||
|
|
||||||
|
void GamePlayingAppMenu::computeFrame() {
|
||||||
|
this->updateMetaBinds();
|
||||||
|
|
||||||
|
if (this->escReleased) {
|
||||||
|
this->menuStack->pop();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
std::set<Action> actions;
|
||||||
|
for (Action action : ACTION_LIST_IN_ORDER) {
|
||||||
|
for (sfKey key : this->settings->getKeybinds().getKeybinds(action)) {
|
||||||
|
if (sf::Keyboard::isKeyPressed(key)) {
|
||||||
|
actions.insert(action);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (actions.contains(RETRY)) {
|
||||||
|
this->retryPressed = true;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (this->retryPressed) {
|
||||||
|
this->game.reset();
|
||||||
|
this->game.start();
|
||||||
|
}
|
||||||
|
this->retryPressed = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (actions.contains(PAUSE)) {
|
||||||
|
this->pausePressed = true;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (this->pausePressed) {
|
||||||
|
this->paused = (!this->paused);
|
||||||
|
}
|
||||||
|
this->pausePressed = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!paused) {
|
||||||
|
this->game.nextFrame(actions);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GamePlayingAppMenu::drawFrame() const {
|
||||||
|
this->renderWindow->clear(sf::Color(200, 200, 200));
|
||||||
|
|
||||||
|
sf::Vector2f cellSize(this->cellSizeZoom, this->cellSizeZoom);
|
||||||
|
bool drawActivePiece = (this->game.getActivePiece() != nullptr) && (!this->game.hasLost());
|
||||||
|
|
||||||
|
// board
|
||||||
|
for (int y = this->game.getBoard().getBaseHeight() + 9; y >= 0; y--) {
|
||||||
|
for (int x = 0; x < this->game.getBoard().getWidth(); x++) {
|
||||||
|
Block block = this->game.getBoard().getBlock(Position{x, y});
|
||||||
|
|
||||||
|
sf::RectangleShape cell(cellSize);
|
||||||
|
cell.setFillColor(this->getColorOfBlock(block, (block == NOTHING) ? 0 : -30));
|
||||||
|
cell.setPosition(this->getBlockPosition(x, y));
|
||||||
|
this->renderWindow->draw(cell);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (drawActivePiece) {
|
||||||
|
// ghost piece
|
||||||
|
sf::Color ghostColor = this->getColorOfBlock(this->game.getActivePiece()->getBlockType(), 100);
|
||||||
|
for (const Position& position : this->game.getActivePiece()->getPositions()) {
|
||||||
|
Position cellPosition = (this->game.getGhostPiecePosition() + position);
|
||||||
|
|
||||||
|
sf::RectangleShape cell(cellSize);
|
||||||
|
cell.setFillColor(ghostColor);
|
||||||
|
cell.setPosition(this->getBlockPosition(cellPosition.x, cellPosition.y));
|
||||||
|
this->renderWindow->draw(cell);
|
||||||
|
}
|
||||||
|
|
||||||
|
// active piece outline
|
||||||
|
float pieceOutlineSize = std::roundf(this->cellSizeZoom / 4);
|
||||||
|
sf::Color pieceOultlineColor = sf::Color(255, 255 - (255 * this->game.getForcedLockDelayProgression()), 255 - (255 * this->game.getForcedLockDelayProgression()));
|
||||||
|
for (const Position& position : this->game.getActivePiece()->getPositions()) {
|
||||||
|
Position cellPosition = (this->game.getActivePiecePosition() + position);
|
||||||
|
|
||||||
|
sf::RectangleShape cell(cellSize);
|
||||||
|
cell.setOutlineThickness(pieceOutlineSize);
|
||||||
|
cell.setOutlineColor(pieceOultlineColor);
|
||||||
|
cell.setPosition(this->getBlockPosition(cellPosition.x, cellPosition.y));
|
||||||
|
this->renderWindow->draw(cell);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// top out line
|
||||||
|
sf::RectangleShape topOutLine(sf::Vector2f(this->cellSizeZoom * this->game.getBoard().getWidth(), std::roundf(this->cellSizeZoom / 4)));
|
||||||
|
topOutLine.setPosition(this->getBlockPosition(0, this->game.getBoard().getBaseHeight() - 1));
|
||||||
|
topOutLine.setFillColor(sf::Color(255, 0, 0));
|
||||||
|
this->renderWindow->draw(topOutLine);
|
||||||
|
|
||||||
|
if (drawActivePiece) {
|
||||||
|
// active piece
|
||||||
|
sf::Color pieceColor = this->getColorOfBlock(this->game.getActivePiece()->getBlockType(), -200 * (this->game.getLockDelayProgression()));
|
||||||
|
|
||||||
|
for (const Position& position : this->game.getActivePiece()->getPositions()) {
|
||||||
|
Position cellPosition = (this->game.getActivePiecePosition() + position);
|
||||||
|
|
||||||
|
sf::RectangleShape cell(cellSize);
|
||||||
|
cell.setFillColor(pieceColor);
|
||||||
|
cell.setPosition(this->getBlockPosition(cellPosition.x, cellPosition.y));
|
||||||
|
this->renderWindow->draw(cell);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
if (this->game.getNextPieces().size() > 0) {
|
||||||
|
for (int y = 10; y >= 0; y--) {
|
||||||
|
for (int x = 0; x <= 10; x++) {
|
||||||
|
Block block = this->game.getNextPieces().at(0).getBlockType();
|
||||||
|
sf::RectangleShape cell(sf::Vector2f(20.f, 20.f));
|
||||||
|
cell.setPosition(sf::Vector2f((x + 2 + this->game.getBoard().getWidth())*20, (this->game.getBoard().getBaseHeight() - y)*20));
|
||||||
|
if (this->game.getNextPieces().at(0).getPositions().contains(Position({x, y}))) {
|
||||||
|
cell.setFillColor(sf::Color(BLOCKS_COLOR[block].red, BLOCKS_COLOR[block].green, BLOCKS_COLOR[block].blue));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
cell.setFillColor(sf::Color(0, 0, 0));
|
||||||
|
}
|
||||||
|
this->renderWindow->draw(cell);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this->game.getHeldPiece() != nullptr) {
|
||||||
|
for (int y = 10; y >= 0; y--) {
|
||||||
|
for (int x = 0; x <= 10; x++) {
|
||||||
|
Block block = this->game.getHeldPiece()->getBlockType();
|
||||||
|
sf::RectangleShape cell(sf::Vector2f(20.f, 20.f));
|
||||||
|
cell.setPosition(sf::Vector2f((x + 12 + this->game.getBoard().getWidth())*20, (this->game.getBoard().getBaseHeight() - y)*20));
|
||||||
|
if (this->game.getHeldPiece()->getPositions().contains(Position({x, y}))) {
|
||||||
|
cell.setFillColor(sf::Color(BLOCKS_COLOR[block].red, BLOCKS_COLOR[block].green, BLOCKS_COLOR[block].blue));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
cell.setFillColor(sf::Color(0, 0, 0));
|
||||||
|
}
|
||||||
|
this->renderWindow->draw(cell);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
this->renderWindow->display();
|
||||||
|
}
|
||||||
|
|
||||||
|
sf::Color GamePlayingAppMenu::getColorOfBlock(Block block, int luminosityShift) const {
|
||||||
|
Color rgbColor = BLOCKS_COLOR[block];
|
||||||
|
return sf::Color(std::clamp(rgbColor.red + luminosityShift, 0, 255),
|
||||||
|
std::clamp(rgbColor.green + luminosityShift, 0, 255),
|
||||||
|
std::clamp(rgbColor.blue + luminosityShift, 0, 255));
|
||||||
|
}
|
||||||
|
|
||||||
|
sf::Vector2f GamePlayingAppMenu::getBlockPosition(int x, int y) const {
|
||||||
|
return sf::Vector2f(this->boardPosition.position.x + (x * this->cellSizeZoom),
|
||||||
|
this->boardPosition.position.y + ((this->game.getBoard().getBaseHeight() + 9 - y) * this->cellSizeZoom));
|
||||||
|
}
|
||||||
29
src/GraphicalUI/AppMenus/GamePlayingAppMenu.h
Normal file
29
src/GraphicalUI/AppMenus/GamePlayingAppMenu.h
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "AppMenu.h"
|
||||||
|
|
||||||
|
#include <stack>
|
||||||
|
#include <memory>
|
||||||
|
#include <SFML/Graphics.hpp>
|
||||||
|
|
||||||
|
|
||||||
|
class GamePlayingAppMenu : public AppMenu {
|
||||||
|
private:
|
||||||
|
Game game;
|
||||||
|
bool paused;
|
||||||
|
bool pausePressed;
|
||||||
|
bool retryPressed;
|
||||||
|
float cellSizeZoom;
|
||||||
|
sf::Rect<float> boardPosition;
|
||||||
|
|
||||||
|
public:
|
||||||
|
GamePlayingAppMenu(std::shared_ptr<MenuStack> menuStack, std::shared_ptr<Settings> settings, std::shared_ptr<sf::RenderWindow> renderWindow);
|
||||||
|
|
||||||
|
void computeFrame() override;
|
||||||
|
|
||||||
|
void drawFrame() const override;
|
||||||
|
|
||||||
|
sf::Color getColorOfBlock(Block block, int luminosityShift) const;
|
||||||
|
|
||||||
|
sf::Vector2f getBlockPosition(int x, int y) const;
|
||||||
|
};
|
||||||
91
src/GraphicalUI/AppMenus/GameSettingsAppMenu.cpp
Normal file
91
src/GraphicalUI/AppMenus/GameSettingsAppMenu.cpp
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
#include "GameSettingsAppMenu.h"
|
||||||
|
|
||||||
|
#include "AppMenu.h"
|
||||||
|
#include "GamePlayingAppMenu.h"
|
||||||
|
#include "PlayerCursor.h"
|
||||||
|
|
||||||
|
#include <stack>
|
||||||
|
#include <memory>
|
||||||
|
#include <vector>
|
||||||
|
#include <SFML/Graphics.hpp>
|
||||||
|
|
||||||
|
|
||||||
|
GameSettingsAppMenu::GameSettingsAppMenu(std::shared_ptr<MenuStack> menuStack, std::shared_ptr<Settings> settings, std::shared_ptr<sf::RenderWindow> renderWindow) :
|
||||||
|
AppMenu(menuStack, settings, renderWindow),
|
||||||
|
playerCursor(std::vector<unsigned int>({2, 3, 1})) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void GameSettingsAppMenu::computeFrame() {
|
||||||
|
this->updateMetaBinds();
|
||||||
|
this->playerCursor.updatePosition();
|
||||||
|
|
||||||
|
switch (this->playerCursor.getPosition().y) {
|
||||||
|
case 1 : {
|
||||||
|
switch (this->playerCursor.getPosition().x) {
|
||||||
|
case 0 : {this->settings->setGamemode(SPRINT); break;}
|
||||||
|
case 1 : {this->settings->setGamemode(MARATHON); break;}
|
||||||
|
case 2 : {this->settings->setGamemode(ULTRA); break;}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 2 : {
|
||||||
|
switch (this->playerCursor.getPosition().x) {
|
||||||
|
case 0 : {this->settings->setGamemode(MASTER); break;}
|
||||||
|
case 1 : break; //TODO
|
||||||
|
case 2 : break; //TODO
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this->enterReleased) {
|
||||||
|
if (this->playerCursor.getPosition().y == 0) {
|
||||||
|
//TODO
|
||||||
|
}
|
||||||
|
if (this->playerCursor.getPosition().y > 0) {
|
||||||
|
this->menuStack->push(std::make_shared<GamePlayingAppMenu>(this->menuStack, this->settings, this->renderWindow));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (this->escReleased) {
|
||||||
|
this->menuStack->pop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GameSettingsAppMenu::drawFrame() const {
|
||||||
|
this->renderWindow->clear(sf::Color(200, 200, 200));
|
||||||
|
|
||||||
|
|
||||||
|
sf::Font font("data/fonts/pressstart/prstartk.ttf");
|
||||||
|
sf::Text text(font, "", this->settings->getWindowSizeMultiplier() * 2);
|
||||||
|
text.setFillColor(sf::Color(0, 0, 0));
|
||||||
|
text.setOutlineColor(sf::Color(255, 255, 255));
|
||||||
|
|
||||||
|
text.setString("GAME SETTINGS");
|
||||||
|
text.setOrigin(text.getLocalBounds().getCenter());
|
||||||
|
text.setPosition(sf::Vector2f({(float) this->settings->getWindowSizeMultiplier() * 40, (float) this->settings->getWindowSizeMultiplier() * 5}));
|
||||||
|
this->renderWindow->draw(text);
|
||||||
|
|
||||||
|
this->placeText(text, "PIECES SELECT", 5.f, 15.f, 0, 0);
|
||||||
|
this->placeText(text, "BOARD SELECT", 40.f, 15.f, 1, 0);
|
||||||
|
|
||||||
|
this->placeText(text, "SPRINT", 5.f, 25.f, 0, 1);
|
||||||
|
this->placeText(text, "MARATHON", 25.f, 25.f, 1, 1);
|
||||||
|
this->placeText(text, "ULTRA", 50.f, 25.f, 2, 1);
|
||||||
|
this->placeText(text, "MASTER", 5.f, 35.f, 0, 2);
|
||||||
|
this->placeText(text, "TODO", 25.f, 35.f, 1, 2);
|
||||||
|
this->placeText(text, "TODO", 50.f, 35.f, 2, 2);
|
||||||
|
|
||||||
|
this->renderWindow->display();
|
||||||
|
}
|
||||||
|
|
||||||
|
void GameSettingsAppMenu::placeText(sf::Text& text, const sf::String& string, float xPos, float yPos, unsigned int xCursorOutline, unsigned int yCursorOutline) const {
|
||||||
|
float sizeMultiplier = this->settings->getWindowSizeMultiplier();
|
||||||
|
|
||||||
|
text.setString(string);
|
||||||
|
text.setOutlineThickness((this->playerCursor.getPosition().x == xCursorOutline
|
||||||
|
&& this->playerCursor.getPosition().y == yCursorOutline) ? (sizeMultiplier / 2) : 0);
|
||||||
|
text.setOrigin(sf::Vector2f({0, text.getLocalBounds().size.y / 2}));
|
||||||
|
text.setPosition(sf::Vector2f({sizeMultiplier * xPos, sizeMultiplier * yPos}));
|
||||||
|
this->renderWindow->draw(text);
|
||||||
|
}
|
||||||
23
src/GraphicalUI/AppMenus/GameSettingsAppMenu.h
Normal file
23
src/GraphicalUI/AppMenus/GameSettingsAppMenu.h
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "AppMenu.h"
|
||||||
|
#include "PlayerCursor.h"
|
||||||
|
|
||||||
|
#include <stack>
|
||||||
|
#include <memory>
|
||||||
|
#include <SFML/Graphics.hpp>
|
||||||
|
|
||||||
|
|
||||||
|
class GameSettingsAppMenu : public AppMenu {
|
||||||
|
private:
|
||||||
|
PlayerCursor playerCursor;
|
||||||
|
|
||||||
|
public:
|
||||||
|
GameSettingsAppMenu(std::shared_ptr<MenuStack> menuStack, std::shared_ptr<Settings> settings, std::shared_ptr<sf::RenderWindow> renderWindow);
|
||||||
|
|
||||||
|
void computeFrame() override;
|
||||||
|
|
||||||
|
void drawFrame() const override;
|
||||||
|
|
||||||
|
void placeText(sf::Text& text, const sf::String& string, float xPos, float yPos, unsigned int xCursorOutline, unsigned int yCursorOutline) const;
|
||||||
|
};
|
||||||
@@ -1,21 +1,73 @@
|
|||||||
#include "MainAppMenu.h"
|
#include "MainAppMenu.h"
|
||||||
|
|
||||||
#include "AppMenu.h"
|
#include "AppMenu.h"
|
||||||
|
#include "GameSettingsAppMenu.h"
|
||||||
|
#include "PlayerCursor.h"
|
||||||
|
|
||||||
#include <stack>
|
#include <stack>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <vector>
|
||||||
#include <SFML/Graphics.hpp>
|
#include <SFML/Graphics.hpp>
|
||||||
|
|
||||||
|
|
||||||
MainAppMenu::MainAppMenu(std::shared_ptr<std::stack<AppMenu>> menuStack, std::shared_ptr<Settings> settings, std::shared_ptr<sf::RenderWindow> renderWindow) :
|
MainAppMenu::MainAppMenu(std::shared_ptr<MenuStack> menuStack, std::shared_ptr<Settings> settings, std::shared_ptr<sf::RenderWindow> renderWindow) :
|
||||||
AppMenu(menuStack, settings, renderWindow) {
|
AppMenu(menuStack, settings, renderWindow),
|
||||||
|
playerCursor(std::vector<unsigned int>({1, 1, 1})) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainAppMenu::computeFrame() {
|
void MainAppMenu::computeFrame() {
|
||||||
|
this->updateMetaBinds();
|
||||||
|
this->playerCursor.updatePosition();
|
||||||
|
|
||||||
|
if (this->enterReleased) {
|
||||||
|
if (this->playerCursor.getPosition().y == 0) {
|
||||||
|
this->menuStack->push(std::make_shared<GameSettingsAppMenu>(this->menuStack, this->settings, this->renderWindow));
|
||||||
|
}
|
||||||
|
if (this->playerCursor.getPosition().y == 1) {
|
||||||
|
//TODO
|
||||||
|
}
|
||||||
|
if (this->playerCursor.getPosition().y == 2) {
|
||||||
|
//TODO
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (this->escReleased) {
|
||||||
|
this->menuStack->pop();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainAppMenu::drawFrame() const {
|
void MainAppMenu::drawFrame() const {
|
||||||
|
this->renderWindow->clear(sf::Color(200, 200, 200));
|
||||||
|
|
||||||
|
float sizeMultiplier = this->settings->getWindowSizeMultiplier();
|
||||||
|
|
||||||
|
sf::Font font("data/fonts/pressstart/prstartk.ttf");
|
||||||
|
sf::Text text(font, "", this->settings->getWindowSizeMultiplier() * 2);
|
||||||
|
text.setFillColor(sf::Color(0, 0, 0));
|
||||||
|
text.setOutlineColor(sf::Color(255, 255, 255));
|
||||||
|
|
||||||
|
text.setString("JMINOS");
|
||||||
|
text.setOrigin(text.getLocalBounds().getCenter());
|
||||||
|
text.setPosition(sf::Vector2f({sizeMultiplier * 40, sizeMultiplier * 10}));
|
||||||
|
this->renderWindow->draw(text);
|
||||||
|
|
||||||
|
text.setString("PLAY");
|
||||||
|
text.setOutlineThickness((this->playerCursor.getPosition().y == 0) ? (sizeMultiplier / 2) : 0);
|
||||||
|
text.setOrigin(text.getLocalBounds().getCenter());
|
||||||
|
text.setPosition(sf::Vector2f({sizeMultiplier * 40, sizeMultiplier * 20}));
|
||||||
|
this->renderWindow->draw(text);
|
||||||
|
|
||||||
|
text.setString("SETTINGS");
|
||||||
|
text.setOutlineThickness((this->playerCursor.getPosition().y == 1) ? (sizeMultiplier / 2) : 0);
|
||||||
|
text.setOrigin(text.getLocalBounds().getCenter());
|
||||||
|
text.setPosition(sf::Vector2f({sizeMultiplier * 40, sizeMultiplier * 30}));
|
||||||
|
this->renderWindow->draw(text);
|
||||||
|
|
||||||
|
text.setString("INFO");
|
||||||
|
text.setOutlineThickness((this->playerCursor.getPosition().y == 2) ? (sizeMultiplier / 2) : 0);
|
||||||
|
text.setOrigin(text.getLocalBounds().getCenter());
|
||||||
|
text.setPosition(sf::Vector2f({sizeMultiplier * 40, sizeMultiplier * 40}));
|
||||||
|
this->renderWindow->draw(text);
|
||||||
|
|
||||||
|
this->renderWindow->display();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "AppMenu.h"
|
#include "AppMenu.h"
|
||||||
|
#include "PlayerCursor.h"
|
||||||
|
|
||||||
#include <stack>
|
#include <stack>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
@@ -8,10 +9,13 @@
|
|||||||
|
|
||||||
|
|
||||||
class MainAppMenu : public AppMenu {
|
class MainAppMenu : public AppMenu {
|
||||||
|
private:
|
||||||
|
PlayerCursor playerCursor;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
MainAppMenu(std::shared_ptr<std::stack<AppMenu>> menuStack, std::shared_ptr<Settings> settings, std::shared_ptr<sf::RenderWindow> renderWindow);
|
MainAppMenu(std::shared_ptr<MenuStack> menuStack, std::shared_ptr<Settings> settings, std::shared_ptr<sf::RenderWindow> renderWindow);
|
||||||
|
|
||||||
void computeFrame();
|
void computeFrame() override;
|
||||||
|
|
||||||
void drawFrame() const;
|
void drawFrame() const override;
|
||||||
};
|
};
|
||||||
|
|||||||
105
src/GraphicalUI/AppMenus/PlayerCursor.cpp
Normal file
105
src/GraphicalUI/AppMenus/PlayerCursor.cpp
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
#include "PlayerCursor.h"
|
||||||
|
|
||||||
|
#include "../Keybinds.h"
|
||||||
|
#include "../Settings.h"
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <SFML/Graphics.hpp>
|
||||||
|
|
||||||
|
static const int MENU_DAS = FRAMES_PER_SECOND / 2;
|
||||||
|
|
||||||
|
|
||||||
|
PlayerCursor::PlayerCursor(std::vector<unsigned int> rows) :
|
||||||
|
rows(rows) {
|
||||||
|
|
||||||
|
this->position = sf::Vector2u({0, 0});
|
||||||
|
this->leftDAS = 0;
|
||||||
|
this->rightDAS = 0;
|
||||||
|
this->upDAS = 0;
|
||||||
|
this->downDAS = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PlayerCursor::updatePosition() {
|
||||||
|
(sf::Keyboard::isKeyPressed(sfKey::Left)) ? (this->leftDAS++) : (this->leftDAS = 0);
|
||||||
|
if (this->shouldMove(this->leftDAS)) {
|
||||||
|
this->moveLeft();
|
||||||
|
}
|
||||||
|
|
||||||
|
(sf::Keyboard::isKeyPressed(sfKey::Right)) ? (this->rightDAS++) : (this->rightDAS = 0);
|
||||||
|
if (this->shouldMove(this->rightDAS)) {
|
||||||
|
this->moveRight();
|
||||||
|
}
|
||||||
|
|
||||||
|
(sf::Keyboard::isKeyPressed(sfKey::Up)) ? (this->upDAS++) : (this->upDAS = 0);
|
||||||
|
if (this->shouldMove(this->upDAS)) {
|
||||||
|
this->moveUp();
|
||||||
|
}
|
||||||
|
|
||||||
|
(sf::Keyboard::isKeyPressed(sfKey::Down)) ? (this->downDAS++) : (this->downDAS = 0);
|
||||||
|
if (this->shouldMove(this->downDAS)) {
|
||||||
|
this->moveDown();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PlayerCursor::goToPosition(const sf::Vector2u& newPosition) {
|
||||||
|
if (this->rows.size() > newPosition.y) {
|
||||||
|
if (this->rows.at(newPosition.y) > newPosition.x) {
|
||||||
|
this->position = newPosition;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const sf::Vector2u& PlayerCursor::getPosition() const {
|
||||||
|
return this->position;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PlayerCursor::shouldMove(int DAS) const {
|
||||||
|
return (DAS == 1
|
||||||
|
|| (DAS > MENU_DAS && (DAS % 5) == 0)
|
||||||
|
|| (DAS > (FRAMES_PER_SECOND * 2)));
|
||||||
|
}
|
||||||
|
|
||||||
|
void PlayerCursor::moveLeft() {
|
||||||
|
if (this->position.x == 0) {
|
||||||
|
this->position.x = this->rows.at(this->position.y) - 1;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this->position.x--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PlayerCursor::moveRight() {
|
||||||
|
if (this->position.x == this->rows.at(this->position.y) - 1) {
|
||||||
|
this->position.x = 0;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this->position.x++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PlayerCursor::moveUp() {
|
||||||
|
if (this->position.y == 0) {
|
||||||
|
this->position.y = this->rows.size() - 1;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this->position.y--;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this->position.x >= this->rows.at(this->position.y)) {
|
||||||
|
this->position.x = this->rows.at(this->position.y) - 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PlayerCursor::moveDown() {
|
||||||
|
if (this->position.y == this->rows.size() - 1) {
|
||||||
|
this->position.y = 0;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this->position.y++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this->position.x >= this->rows.at(this->position.y)) {
|
||||||
|
this->position.x = this->rows.at(this->position.y) - 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
35
src/GraphicalUI/AppMenus/PlayerCursor.h
Normal file
35
src/GraphicalUI/AppMenus/PlayerCursor.h
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include <SFML/Graphics.hpp>
|
||||||
|
|
||||||
|
|
||||||
|
class PlayerCursor {
|
||||||
|
private:
|
||||||
|
std::vector<unsigned int> rows;
|
||||||
|
sf::Vector2u position;
|
||||||
|
int leftDAS;
|
||||||
|
int rightDAS;
|
||||||
|
int upDAS;
|
||||||
|
int downDAS;
|
||||||
|
|
||||||
|
public:
|
||||||
|
PlayerCursor(std::vector<unsigned int> rows);
|
||||||
|
|
||||||
|
void updatePosition();
|
||||||
|
|
||||||
|
void goToPosition(const sf::Vector2u& newPosition);
|
||||||
|
|
||||||
|
const sf::Vector2u& getPosition() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool shouldMove(int DAS) const;
|
||||||
|
|
||||||
|
void moveLeft();
|
||||||
|
|
||||||
|
void moveRight();
|
||||||
|
|
||||||
|
void moveUp();
|
||||||
|
|
||||||
|
void moveDown();
|
||||||
|
};
|
||||||
@@ -8,24 +8,24 @@
|
|||||||
#include <memory>
|
#include <memory>
|
||||||
#include <SFML/Graphics.hpp>
|
#include <SFML/Graphics.hpp>
|
||||||
|
|
||||||
static const double TIME_BETWEEN_FRAMES = (1000.f / 60.f);
|
static const double TIME_BETWEEN_FRAMES = (1000.f / FRAMES_PER_SECOND);
|
||||||
|
|
||||||
|
|
||||||
GraphApp::GraphApp() {
|
GraphApp::GraphApp() {
|
||||||
this->settings = std::make_shared<Settings>();
|
this->settings = std::make_shared<Settings>();
|
||||||
this->menuStack = std::make_shared<std::stack<AppMenu>>();
|
this->menuStack = std::make_shared<MenuStack>();
|
||||||
this->window = std::make_shared<sf::RenderWindow>();
|
this->renderWindow = std::make_shared<sf::RenderWindow>();
|
||||||
}
|
}
|
||||||
|
|
||||||
void GraphApp::startApp() {
|
void GraphApp::run() {
|
||||||
changeVideoMode(*this->window, this->settings->getVideoMode());
|
changeVideoMode(*this->renderWindow, this->settings->getVideoMode());
|
||||||
this->menuStack->push(MainAppMenu(this->menuStack, this->settings, this->window));
|
this->menuStack->push(std::make_shared<MainAppMenu>(this->menuStack, this->settings, this->renderWindow));
|
||||||
|
|
||||||
bool quit = false;
|
bool quit = false;
|
||||||
double timeAtNextFrame = 0;
|
double timeAtNextFrame = 0;
|
||||||
sf::Clock clock;
|
sf::Clock clock;
|
||||||
while (!quit) {
|
while (!quit) {
|
||||||
while (const std::optional event = this->window->pollEvent()) {
|
while (const std::optional event = this->renderWindow->pollEvent()) {
|
||||||
if (event->is<sf::Event::Closed>()) {
|
if (event->is<sf::Event::Closed>()) {
|
||||||
quit = true;
|
quit = true;
|
||||||
}
|
}
|
||||||
@@ -33,17 +33,20 @@ void GraphApp::startApp() {
|
|||||||
|
|
||||||
if (!quit) {
|
if (!quit) {
|
||||||
if (clock.getElapsedTime().asMilliseconds() > timeAtNextFrame) {
|
if (clock.getElapsedTime().asMilliseconds() > timeAtNextFrame) {
|
||||||
timeAtNextFrame += TIME_BETWEEN_FRAMES;
|
this->menuStack->top()->computeFrame();
|
||||||
this->menuStack->top().computeFrame();
|
|
||||||
|
|
||||||
if (this->menuStack->empty()) {
|
if (this->menuStack->empty()) {
|
||||||
quit = true;
|
quit = true;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
this->menuStack->top().drawFrame();
|
this->menuStack->top()->drawFrame();
|
||||||
|
}
|
||||||
|
|
||||||
|
while (clock.getElapsedTime().asMilliseconds() > timeAtNextFrame) {
|
||||||
|
timeAtNextFrame += TIME_BETWEEN_FRAMES;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
window->close();
|
renderWindow->close();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,11 +11,11 @@
|
|||||||
class GraphApp {
|
class GraphApp {
|
||||||
private:
|
private:
|
||||||
std::shared_ptr<Settings> settings;
|
std::shared_ptr<Settings> settings;
|
||||||
std::shared_ptr<std::stack<AppMenu>> menuStack;
|
std::shared_ptr<MenuStack> menuStack;
|
||||||
std::shared_ptr<sf::RenderWindow> window;
|
std::shared_ptr<sf::RenderWindow> renderWindow;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
GraphApp();
|
GraphApp();
|
||||||
|
|
||||||
void startApp();
|
void run();
|
||||||
};
|
};
|
||||||
|
|||||||
86
src/GraphicalUI/Keybinds.cpp
Normal file
86
src/GraphicalUI/Keybinds.cpp
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
#include "Keybinds.h"
|
||||||
|
|
||||||
|
#include "../Core/Action.h"
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
#include <set>
|
||||||
|
#include <fstream>
|
||||||
|
#include <SFML/Graphics.hpp>
|
||||||
|
|
||||||
|
|
||||||
|
Keybinds::Keybinds(int layoutNumber) :
|
||||||
|
layoutNumber(layoutNumber) {
|
||||||
|
|
||||||
|
for (Action action : ACTION_LIST_IN_ORDER) {
|
||||||
|
this->keybinds.insert({action, std::set<sfKey>()});
|
||||||
|
}
|
||||||
|
|
||||||
|
this->loadKeybindsFromFile();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Keybinds::loadKeybindsFromFile() {
|
||||||
|
std::ifstream layoutFile("data/config/keybinds/layout" + std::to_string(this->layoutNumber) + ".bin", std::ios::binary);
|
||||||
|
|
||||||
|
for (Action action : ACTION_LIST_IN_ORDER) {
|
||||||
|
this->keybinds.at(action).clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
char byte;
|
||||||
|
while (layoutFile.peek() != EOF) {
|
||||||
|
layoutFile.get(byte);
|
||||||
|
Action action = Action(byte);
|
||||||
|
|
||||||
|
bool separatorMet = false;
|
||||||
|
while (!separatorMet) {
|
||||||
|
layoutFile.get(byte);
|
||||||
|
if (byte == (char) 0xFF) {
|
||||||
|
separatorMet = true;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this->keybinds.at(action).insert(sfKey(byte));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Keybinds::saveKeybindsToFile() const {
|
||||||
|
std::ofstream layoutFile("data/config/keybinds/layout" + std::to_string(this->layoutNumber) + ".bin", std::ios::trunc | std::ios::binary);
|
||||||
|
|
||||||
|
char byte;
|
||||||
|
for (Action action : ACTION_LIST_IN_ORDER) {
|
||||||
|
byte = action;
|
||||||
|
layoutFile.write(&byte, 1);
|
||||||
|
|
||||||
|
for (sfKey key : this->keybinds.at(action)) {
|
||||||
|
byte = (int) key;
|
||||||
|
layoutFile.write(&byte, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
byte = 0xFF;
|
||||||
|
layoutFile.write(&byte, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Keybinds::addKey(Action action, sfKey key) {
|
||||||
|
this->keybinds.at(action).insert(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Keybinds::clearKeys(Action action) {
|
||||||
|
this->keybinds.at(action).clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::set<Action> Keybinds::getActions(sfKey key) const {
|
||||||
|
std::set<Action> actions;
|
||||||
|
|
||||||
|
for (const auto& [action, keys] : this->keybinds) {
|
||||||
|
if (keys.contains(key)) {
|
||||||
|
actions.insert(action);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return actions;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::set<sfKey>& Keybinds::getKeybinds(Action action) const {
|
||||||
|
return this->keybinds.at(action);
|
||||||
|
}
|
||||||
@@ -3,30 +3,32 @@
|
|||||||
#include "../Core/Action.h"
|
#include "../Core/Action.h"
|
||||||
|
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <vector>
|
#include <set>
|
||||||
#include <SFML/Graphics.hpp>
|
#include <SFML/Graphics.hpp>
|
||||||
|
|
||||||
using sfKey = sf::Keyboard::Key;
|
using sfKey = sf::Keyboard::Key;
|
||||||
|
|
||||||
|
static const int NUMBER_OF_KEYBINDS = 5;
|
||||||
|
static const int CUSTOMIZABLE_KEYBINDS = NUMBER_OF_KEYBINDS - 1;
|
||||||
|
|
||||||
|
|
||||||
class Keybinds {
|
class Keybinds {
|
||||||
private:
|
private:
|
||||||
std::map<Action, std::vector<sfKey>> keybinds;
|
std::map<Action, std::set<sfKey>> keybinds;
|
||||||
|
int layoutNumber;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Keybinds();
|
Keybinds(int layoutNumber);
|
||||||
|
|
||||||
void loadKeybindsFromFile();
|
void loadKeybindsFromFile();
|
||||||
|
|
||||||
void saveKeybindsToFile() const;
|
void saveKeybindsToFile() const;
|
||||||
|
|
||||||
void createDefaultKeybindsFile() const;
|
|
||||||
|
|
||||||
void addKey(Action action, sfKey key);
|
void addKey(Action action, sfKey key);
|
||||||
|
|
||||||
void clearKeys(Action action);
|
void clearKeys(Action action);
|
||||||
|
|
||||||
const std::vector<Action>& getActions(sfKey key) const;
|
const std::set<Action> getActions(sfKey key) const;
|
||||||
|
|
||||||
const std::vector<sfKey>& getKeybinds(Action action) const;
|
const std::set<sfKey>& getKeybinds(Action action) const;
|
||||||
};
|
};
|
||||||
|
|||||||
21
src/GraphicalUI/PiecesType.h
Normal file
21
src/GraphicalUI/PiecesType.h
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
|
||||||
|
enum PiecesType {
|
||||||
|
CONVEX_PIECES,
|
||||||
|
HOLELESS_PIECES,
|
||||||
|
OTHER_PIECES,
|
||||||
|
ALL_PIECES,
|
||||||
|
SINGLE_PIECE
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
inline int getSizeOfPieces(PiecesType type) {
|
||||||
|
if (type < SINGLE_PIECE) return 0;
|
||||||
|
|
||||||
|
else return (type - SINGLE_PIECE + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline PiecesType createSinglePieceType(int size) {
|
||||||
|
return PiecesType(SINGLE_PIECE + size - 1);
|
||||||
|
}
|
||||||
@@ -4,66 +4,174 @@
|
|||||||
#include "Keybinds.h"
|
#include "Keybinds.h"
|
||||||
|
|
||||||
#include <SFML/Graphics.hpp>
|
#include <SFML/Graphics.hpp>
|
||||||
|
#include <fstream>
|
||||||
|
|
||||||
static const int NUMBER_OF_KEYBINDS = 5;
|
|
||||||
static const int CUSTOMIZABLE_KEYBINDS = NUMBER_OF_KEYBINDS - 1;
|
|
||||||
static const sf::Vector2u BASE_WINDOW_SIZE = {80, 50};
|
static const sf::Vector2u BASE_WINDOW_SIZE = {80, 50};
|
||||||
static const int WINDOW_SIZE_MULTIPLIERS[] = {4, 6, 9, 14, 20};
|
static const int WINDOW_SIZE_MULTIPLIERS[] = {4, 6, 10, 14, 20};
|
||||||
static const int WINDOW_SIZE_LAST_MODE = (sizeof(WINDOW_SIZE_MULTIPLIERS) / sizeof(int)) - 1;
|
static const int WINDOW_SIZE_LAST_MODE = (sizeof(WINDOW_SIZE_MULTIPLIERS) / sizeof(int)) - 1;
|
||||||
|
|
||||||
|
|
||||||
Settings::Settings() {
|
Settings::Settings() {
|
||||||
for (int i = 1; i <= 15; i++) {
|
for (int i = 1; i <= MAXIMUM_PIECES_SIZE; i++) {
|
||||||
this->menu.getPiecesList().loadPieces(i);
|
this->menu.getPiecesList().loadPieces(i);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this->keybinds.reserve(NUMBER_OF_KEYBINDS);
|
||||||
|
for (int i = 0; i < NUMBER_OF_KEYBINDS; i++) {
|
||||||
|
this->keybinds.emplace_back(i);
|
||||||
|
}
|
||||||
|
|
||||||
this->loadSettingsFromFile();
|
this->loadSettingsFromFile();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Settings::loadSettingsFromFile() {
|
void Settings::loadSettingsFromFile() {
|
||||||
this->menu.getPiecesList().unselectAll();
|
std::ifstream settingsFile("data/config/settings.bin", std::ios::binary);
|
||||||
this->menu.getPiecesList().selectAllPieces(4);
|
char byte;
|
||||||
this->windowSizeMode = 2;
|
|
||||||
|
// keybind layout
|
||||||
|
settingsFile.get(byte);
|
||||||
|
this->chosenKeybinds = byte;
|
||||||
|
|
||||||
|
// window size mode
|
||||||
|
settingsFile.get(byte);
|
||||||
|
this->windowSizeMode = byte;
|
||||||
|
|
||||||
|
// gamemode
|
||||||
|
settingsFile.get(byte);
|
||||||
|
this->gamemode = Gamemode(byte);
|
||||||
|
|
||||||
|
// board width
|
||||||
|
settingsFile.get(byte);
|
||||||
|
this->menu.setBoardWidth(byte);
|
||||||
|
|
||||||
|
// board height
|
||||||
|
settingsFile.get(byte);
|
||||||
|
this->menu.setBoardHeight(byte);
|
||||||
|
|
||||||
|
// piece distribution
|
||||||
|
settingsFile.get(byte);
|
||||||
|
//TODO
|
||||||
|
|
||||||
|
// selected pieces
|
||||||
|
char pieceType;
|
||||||
|
char pieceValue;
|
||||||
|
this->selectedPieces.clear();
|
||||||
|
while (settingsFile.get(pieceType)) {
|
||||||
|
if (settingsFile.eof()) break;
|
||||||
|
|
||||||
|
settingsFile.get(pieceValue);
|
||||||
|
this->selectedPieces.push_back({PiecesType(pieceType), pieceValue});
|
||||||
|
}
|
||||||
|
this->confirmSelectedPieces();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Settings::saveSettingsToFile() const {
|
void Settings::saveSettingsToFile() const {
|
||||||
|
std::ofstream settingsFile("data/config/settings.bin", std::ios::trunc | std::ios::binary);
|
||||||
|
char byte;
|
||||||
|
|
||||||
}
|
// keybind layout
|
||||||
|
byte = this->chosenKeybinds;
|
||||||
|
settingsFile.write(&byte, 1);
|
||||||
|
|
||||||
void Settings::createDefaultSettingsFile() const {
|
// window size mode
|
||||||
|
byte = this->windowSizeMode;
|
||||||
|
settingsFile.write(&byte, 1);
|
||||||
|
|
||||||
|
// gamemode
|
||||||
|
byte = this->gamemode;
|
||||||
|
settingsFile.write(&byte, 1);
|
||||||
|
|
||||||
|
// board width
|
||||||
|
byte = this->menu.getBoardWidth();
|
||||||
|
settingsFile.write(&byte, 1);
|
||||||
|
|
||||||
|
// board height
|
||||||
|
byte = this->menu.getBoardHeight();
|
||||||
|
settingsFile.write(&byte, 1);
|
||||||
|
|
||||||
|
// piece distribution
|
||||||
|
//TODO
|
||||||
|
settingsFile.write(&byte, 1);
|
||||||
|
|
||||||
|
// selected pieces
|
||||||
|
for (const auto& [type, value] : this->selectedPieces) {
|
||||||
|
byte = type;
|
||||||
|
settingsFile.write(&byte, 1);
|
||||||
|
byte = value;
|
||||||
|
settingsFile.write(&byte, 1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Settings::selectNextKeybinds() {
|
bool Settings::selectNextKeybinds() {
|
||||||
if (this->chosenKeybinds < NUMBER_OF_KEYBINDS) {
|
if (this->chosenKeybinds < NUMBER_OF_KEYBINDS) {
|
||||||
this->chosenKeybinds++;
|
this->chosenKeybinds++;
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Settings::selectPreviousKeybinds() {
|
bool Settings::selectPreviousKeybinds() {
|
||||||
if (this->chosenKeybinds > 0) {
|
if (this->chosenKeybinds > 0) {
|
||||||
this->chosenKeybinds--;
|
this->chosenKeybinds--;
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Settings::canModifyCurrentKeybinds() const {
|
bool Settings::canModifyCurrentKeybinds() const {
|
||||||
return (this->chosenKeybinds == CUSTOMIZABLE_KEYBINDS);
|
return (this->chosenKeybinds == CUSTOMIZABLE_KEYBINDS);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Settings::setGamemode(Gamemode gamemode) {
|
||||||
|
this->gamemode = gamemode;
|
||||||
|
}
|
||||||
|
|
||||||
bool Settings::widenWindow() {
|
bool Settings::widenWindow() {
|
||||||
if (this->windowSizeMode < WINDOW_SIZE_LAST_MODE) {
|
if (this->windowSizeMode < WINDOW_SIZE_LAST_MODE) {
|
||||||
this->windowSizeMode++;
|
this->windowSizeMode++;
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Settings::shortenWindow() {
|
bool Settings::shortenWindow() {
|
||||||
if (this->windowSizeMode > 0) {
|
if (this->windowSizeMode > 0) {
|
||||||
this->windowSizeMode--;
|
this->windowSizeMode--;
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Settings::setGamemode(Gamemode gamemode) {
|
void Settings::selectPieces(PiecesType type, int value) {
|
||||||
this->gamemode = gamemode;
|
this->selectedPieces.emplace_back(type, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Settings::unselectPieces(int index) {
|
||||||
|
if (index >= this->selectedPieces.size()) return;
|
||||||
|
|
||||||
|
this->selectedPieces.erase(this->selectedPieces.begin() + index);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Settings::confirmSelectedPieces() {
|
||||||
|
this->menu.getPiecesList().unselectAll();
|
||||||
|
|
||||||
|
for (const auto& [type, value] : this->selectedPieces) {
|
||||||
|
int size = getSizeOfPieces(type);
|
||||||
|
|
||||||
|
if (size == 0) {
|
||||||
|
switch (type) {
|
||||||
|
case CONVEX_PIECES : {this->menu.getPiecesList().selectConvexPieces(value); break;}
|
||||||
|
case HOLELESS_PIECES : {this->menu.getPiecesList().selectHolelessPieces(value); break;}
|
||||||
|
case OTHER_PIECES : {this->menu.getPiecesList().selectOtherPieces(value); break;}
|
||||||
|
case ALL_PIECES : {this->menu.getPiecesList().selectAllPieces(value); break;}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (size > MAXIMUM_PIECES_SIZE) return;
|
||||||
|
|
||||||
|
this->menu.getPiecesList().selectPiece(size, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Menu& Settings::getMenu() {
|
Menu& Settings::getMenu() {
|
||||||
@@ -82,6 +190,10 @@ int Settings::getWindowSizeMultiplier() const {
|
|||||||
return WINDOW_SIZE_MULTIPLIERS[this->windowSizeMode];
|
return WINDOW_SIZE_MULTIPLIERS[this->windowSizeMode];
|
||||||
}
|
}
|
||||||
|
|
||||||
const sf::VideoMode& Settings::getVideoMode() const {
|
const sf::VideoMode Settings::getVideoMode() const {
|
||||||
return sf::VideoMode(BASE_WINDOW_SIZE * (unsigned int) WINDOW_SIZE_MULTIPLIERS[this->windowSizeMode]);
|
return sf::VideoMode(BASE_WINDOW_SIZE * (unsigned int) WINDOW_SIZE_MULTIPLIERS[this->windowSizeMode]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const std::vector<std::pair<PiecesType, int>>& Settings::getSelectedPieces() const {
|
||||||
|
return this->selectedPieces;
|
||||||
|
}
|
||||||
|
|||||||
@@ -2,10 +2,21 @@
|
|||||||
|
|
||||||
#include "../Core/Menu.h"
|
#include "../Core/Menu.h"
|
||||||
#include "Keybinds.h"
|
#include "Keybinds.h"
|
||||||
|
#include "PiecesType.h"
|
||||||
|
|
||||||
#include <SFML/Graphics.hpp>
|
#include <SFML/Graphics.hpp>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
static const int MAXIMUM_BOARD_WIDTH = 40;
|
||||||
|
static const int MAXIMUM_BOARD_HEIGHT = 40;
|
||||||
|
|
||||||
|
//#define __JMINOS_RELEASE__
|
||||||
|
#ifdef __JMINOS_RELEASE__
|
||||||
|
static const int MAXIMUM_PIECES_SIZE = 15;
|
||||||
|
#else
|
||||||
|
static const int MAXIMUM_PIECES_SIZE = 10;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
class Settings {
|
class Settings {
|
||||||
private:
|
private:
|
||||||
@@ -14,6 +25,7 @@ class Settings {
|
|||||||
int chosenKeybinds;
|
int chosenKeybinds;
|
||||||
Gamemode gamemode;
|
Gamemode gamemode;
|
||||||
int windowSizeMode;
|
int windowSizeMode;
|
||||||
|
std::vector<std::pair<PiecesType, int>> selectedPieces;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Settings();
|
Settings();
|
||||||
@@ -22,8 +34,6 @@ class Settings {
|
|||||||
|
|
||||||
void saveSettingsToFile() const;
|
void saveSettingsToFile() const;
|
||||||
|
|
||||||
void createDefaultSettingsFile() const;
|
|
||||||
|
|
||||||
bool selectNextKeybinds();
|
bool selectNextKeybinds();
|
||||||
|
|
||||||
bool selectPreviousKeybinds();
|
bool selectPreviousKeybinds();
|
||||||
@@ -36,6 +46,12 @@ class Settings {
|
|||||||
|
|
||||||
bool shortenWindow();
|
bool shortenWindow();
|
||||||
|
|
||||||
|
void selectPieces(PiecesType type, int value);
|
||||||
|
|
||||||
|
void unselectPieces(int index);
|
||||||
|
|
||||||
|
void confirmSelectedPieces();
|
||||||
|
|
||||||
Menu& getMenu();
|
Menu& getMenu();
|
||||||
|
|
||||||
Keybinds& getKeybinds();
|
Keybinds& getKeybinds();
|
||||||
@@ -44,5 +60,7 @@ class Settings {
|
|||||||
|
|
||||||
int getWindowSizeMultiplier() const;
|
int getWindowSizeMultiplier() const;
|
||||||
|
|
||||||
const sf::VideoMode& getVideoMode() const;
|
const sf::VideoMode getVideoMode() const;
|
||||||
|
|
||||||
|
const std::vector<std::pair<PiecesType, int>>& getSelectedPieces() const;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,138 +1,167 @@
|
|||||||
#include <SFML/Graphics.hpp>
|
#include "GraphApp.h"
|
||||||
#include "../Core/Menu.h"
|
|
||||||
#include "../Pieces/PiecesFiles.h"
|
#include "../Pieces/PiecesFiles.h"
|
||||||
#include <iostream>
|
|
||||||
|
|
||||||
void setToDefaultConfig();
|
#include <fstream>
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef __JMINOS_RELEASE__
|
||||||
|
|
||||||
int main() {
|
int main() {
|
||||||
std::srand(std::time(NULL));
|
std::srand(std::time(NULL));
|
||||||
|
|
||||||
sf::RenderWindow window(sf::VideoMode({800, 640}), "My window", sf::Style::Titlebar | sf::Style::Close);
|
GraphApp UI;
|
||||||
window.setPosition(sf::Vector2i(sf::VideoMode::getDesktopMode().size.x / 2 - 400, sf::VideoMode::getDesktopMode().size.y / 2 - 320));
|
UI.run();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
void resetConfigFiles();
|
||||||
|
void resetSettingsFile();
|
||||||
|
void resetKeybindFile(int layout);
|
||||||
|
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
std::srand(std::time(NULL));
|
||||||
|
|
||||||
PiecesFiles pf;
|
PiecesFiles pf;
|
||||||
for (int i = 1; i <= 10; i++) {
|
for (int i = 1; i <= MAXIMUM_PIECES_SIZE; i++) {
|
||||||
pf.savePieces(i);
|
if (!std::filesystem::exists("data/pieces/" + std::to_string(i) + "minos.bin")) {
|
||||||
|
std::cout << "pieces files for size " << i << " not found, generating..." << std::endl;
|
||||||
|
pf.savePieces(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!std::filesystem::exists("data/config/settings.bin")) {
|
||||||
|
resetSettingsFile();
|
||||||
|
}
|
||||||
|
for (int i = 0; i < 5; i++) {
|
||||||
|
if (!std::filesystem::exists("data/config/keybinds/layout" + std::to_string(i) + ".bin")) {
|
||||||
|
resetKeybindFile(i);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Menu m;
|
// do this before compiling release version
|
||||||
m.getPiecesList().loadPieces(10);
|
//resetConfigFiles();
|
||||||
m.getPiecesList().selectAllPieces(4);
|
|
||||||
m.setBoardWidth(10);
|
|
||||||
m.getPlayerControls().setDAS(6);
|
|
||||||
m.getPlayerControls().setARR(0);
|
|
||||||
m.getPlayerControls().setSDR(0);
|
|
||||||
Game game = m.startGame(SPRINT);
|
|
||||||
game.start();
|
|
||||||
|
|
||||||
sf::Clock clock;
|
GraphApp UI;
|
||||||
|
UI.run();
|
||||||
|
|
||||||
sf::Font font;
|
return 0;
|
||||||
if (!font.openFromFile("data/fonts/arial.ttf")) {
|
}
|
||||||
std::cout << "aaaaaaaaaaaaaa";
|
|
||||||
}
|
|
||||||
sf::Text text(font);
|
|
||||||
text.setCharacterSize(20);
|
|
||||||
text.setFillColor(sf::Color::White);
|
|
||||||
|
|
||||||
while (window.isOpen()) {
|
|
||||||
while (const std::optional event = window.pollEvent()) {
|
|
||||||
if (event->is<sf::Event::Closed>())
|
|
||||||
window.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (clock.getElapsedTime().asMilliseconds() > 16) {
|
void resetConfigFiles() {
|
||||||
clock.restart();
|
resetSettingsFile;
|
||||||
|
for (int i = 0; i < 5; i++) {
|
||||||
window.clear(sf::Color::Black);
|
resetKeybindFile(i);
|
||||||
|
|
||||||
std::set<Action> actions;
|
|
||||||
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Key::Left)) {
|
|
||||||
actions.insert(MOVE_LEFT);
|
|
||||||
}
|
|
||||||
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Key::Right)) {
|
|
||||||
actions.insert(MOVE_RIGHT);
|
|
||||||
}
|
|
||||||
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Key::Up)) {
|
|
||||||
actions.insert(HARD_DROP);
|
|
||||||
}
|
|
||||||
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Key::Down)) {
|
|
||||||
actions.insert(SOFT_DROP);
|
|
||||||
}
|
|
||||||
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Key::A)) {
|
|
||||||
actions.insert(ROTATE_CCW);
|
|
||||||
}
|
|
||||||
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Key::E)) {
|
|
||||||
actions.insert(ROTATE_CW);
|
|
||||||
}
|
|
||||||
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Key::Z)) {
|
|
||||||
actions.insert(HOLD);
|
|
||||||
}
|
|
||||||
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Key::Tab)) {
|
|
||||||
actions.insert(ROTATE_0);
|
|
||||||
}
|
|
||||||
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Key::Num2)) {
|
|
||||||
actions.insert(ROTATE_180);
|
|
||||||
}
|
|
||||||
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Key::R)) {
|
|
||||||
game.reset();
|
|
||||||
game.start();
|
|
||||||
}
|
|
||||||
game.nextFrame(actions);
|
|
||||||
|
|
||||||
for (int y = game.getBoard().getBaseHeight() + 5; y >= 0; y--) {
|
|
||||||
for (int x = 0; x < game.getBoard().getWidth(); x++) {
|
|
||||||
bool isActivePieceHere = (game.getActivePiece() != nullptr) && (game.getActivePiece()->getPositions().contains(Position{x, y} - game.getActivePiecePosition()));
|
|
||||||
bool isGhostPieceHere = (game.getActivePiece() != nullptr) && (game.getActivePiece()->getPositions().contains(Position{x, y} - game.ghostPiecePosition()));
|
|
||||||
Block block = (isActivePieceHere || isGhostPieceHere) ? game.getActivePiece()->getBlockType() : game.getBoard().getBlock(Position{x, y});
|
|
||||||
|
|
||||||
sf::RectangleShape cell(sf::Vector2f(20.f, 20.f));
|
|
||||||
cell.setFillColor(sf::Color(BLOCKS_COLOR[block].red, BLOCKS_COLOR[block].green, BLOCKS_COLOR[block].blue, (isGhostPieceHere && !isActivePieceHere) ? 150 : 255));
|
|
||||||
cell.setPosition(sf::Vector2f(x*20, (game.getBoard().getBaseHeight() + 10 - y)*20));
|
|
||||||
window.draw(cell);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (game.getNextPieces().size() > 0) {
|
|
||||||
for (int y = 10; y >= 0; y--) {
|
|
||||||
for (int x = 0; x <= 10; x++) {
|
|
||||||
Block block = game.getNextPieces().at(0).getBlockType();
|
|
||||||
sf::RectangleShape cell(sf::Vector2f(20.f, 20.f));
|
|
||||||
cell.setPosition(sf::Vector2f((x + 2 + game.getBoard().getWidth())*20, (game.getBoard().getBaseHeight() - y)*20));
|
|
||||||
if (game.getNextPieces().at(0).getPositions().contains(Position({x, y}))) {
|
|
||||||
cell.setFillColor(sf::Color(BLOCKS_COLOR[block].red, BLOCKS_COLOR[block].green, BLOCKS_COLOR[block].blue));
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
cell.setFillColor(sf::Color(0, 0, 0));
|
|
||||||
}
|
|
||||||
window.draw(cell);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (game.getHeldPiece() != nullptr) {
|
|
||||||
for (int y = 10; y >= 0; y--) {
|
|
||||||
for (int x = 0; x <= 10; x++) {
|
|
||||||
Block block = game.getHeldPiece()->getBlockType();
|
|
||||||
sf::RectangleShape cell(sf::Vector2f(20.f, 20.f));
|
|
||||||
cell.setPosition(sf::Vector2f((x + 12 + game.getBoard().getWidth())*20, (game.getBoard().getBaseHeight() - y)*20));
|
|
||||||
if (game.getHeldPiece()->getPositions().contains(Position({x, y}))) {
|
|
||||||
cell.setFillColor(sf::Color(BLOCKS_COLOR[block].red, BLOCKS_COLOR[block].green, BLOCKS_COLOR[block].blue));
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
cell.setFillColor(sf::Color(0, 0, 0));
|
|
||||||
}
|
|
||||||
window.draw(cell);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
text.setPosition(sf::Vector2f(12*20, (game.getBoard().getBaseHeight() - 5)*20));
|
|
||||||
text.setString(sf::String(std::to_string(game.getClearedLines())));
|
|
||||||
window.draw(text);
|
|
||||||
|
|
||||||
window.display();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void resetSettingsFile() {
|
||||||
|
std::ofstream settingsFile("data/config/settings.bin", std::ios::trunc | std::ios::binary);
|
||||||
|
char byte;
|
||||||
|
|
||||||
|
// keybind layout
|
||||||
|
byte = 0;
|
||||||
|
settingsFile.write(&byte, 1);
|
||||||
|
|
||||||
|
// window size mode
|
||||||
|
byte = 2;
|
||||||
|
settingsFile.write(&byte, 1);
|
||||||
|
|
||||||
|
// gamemode
|
||||||
|
byte = SPRINT;
|
||||||
|
settingsFile.write(&byte, 1);
|
||||||
|
|
||||||
|
// board width
|
||||||
|
byte = 10;
|
||||||
|
settingsFile.write(&byte, 1);
|
||||||
|
|
||||||
|
// board height
|
||||||
|
byte = 20;
|
||||||
|
settingsFile.write(&byte, 1);
|
||||||
|
|
||||||
|
// piece distribution
|
||||||
|
byte = 0;
|
||||||
|
settingsFile.write(&byte, 1);
|
||||||
|
|
||||||
|
// selected pieces
|
||||||
|
byte = ALL_PIECES;
|
||||||
|
settingsFile.write(&byte, 1);
|
||||||
|
byte = 4;
|
||||||
|
settingsFile.write(&byte, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void resetKeybindFile(int layout) {
|
||||||
|
if (layout < 0 || layout > 4) return;
|
||||||
|
|
||||||
|
std::ofstream layoutFile("data/config/keybinds/layout" + std::to_string(layout) + ".bin", std::ios::trunc | std::ios::binary);
|
||||||
|
std::map<Action, sfKey> keybinds;
|
||||||
|
|
||||||
|
if (layout != 4) {
|
||||||
|
keybinds.insert({PAUSE, sfKey::P});
|
||||||
|
keybinds.insert({RETRY, sfKey::R});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (layout == 0) {
|
||||||
|
keybinds.insert({MOVE_LEFT, sfKey::Left});
|
||||||
|
keybinds.insert({MOVE_RIGHT, sfKey::Right});
|
||||||
|
keybinds.insert({SOFT_DROP, sfKey::Down});
|
||||||
|
keybinds.insert({HARD_DROP, sfKey::Space});
|
||||||
|
keybinds.insert({ROTATE_CW, sfKey::Up});
|
||||||
|
keybinds.insert({ROTATE_CCW, sfKey::Z});
|
||||||
|
keybinds.insert({ROTATE_180, sfKey::X});
|
||||||
|
keybinds.insert({ROTATE_0, sfKey::LShift});
|
||||||
|
keybinds.insert({HOLD, sfKey::C});
|
||||||
|
}
|
||||||
|
if (layout == 1) {
|
||||||
|
keybinds.insert({MOVE_LEFT, sfKey::Z});
|
||||||
|
keybinds.insert({MOVE_RIGHT, sfKey::C});
|
||||||
|
keybinds.insert({SOFT_DROP, sfKey::X});
|
||||||
|
keybinds.insert({HARD_DROP, sfKey::S});
|
||||||
|
keybinds.insert({ROTATE_CW, sfKey::M});
|
||||||
|
keybinds.insert({ROTATE_CCW, sfKey::Comma});
|
||||||
|
keybinds.insert({ROTATE_180, sfKey::J});
|
||||||
|
keybinds.insert({ROTATE_0, sfKey::K});
|
||||||
|
keybinds.insert({HOLD, sfKey::LShift});
|
||||||
|
}
|
||||||
|
if (layout == 2) {
|
||||||
|
keybinds.insert({MOVE_LEFT, sfKey::A});
|
||||||
|
keybinds.insert({MOVE_RIGHT, sfKey::D});
|
||||||
|
keybinds.insert({SOFT_DROP, sfKey::W});
|
||||||
|
keybinds.insert({HARD_DROP, sfKey::S});
|
||||||
|
keybinds.insert({ROTATE_CW, sfKey::Left});
|
||||||
|
keybinds.insert({ROTATE_CCW, sfKey::Right});
|
||||||
|
keybinds.insert({ROTATE_180, sfKey::Up});
|
||||||
|
keybinds.insert({ROTATE_0, sfKey::Down});
|
||||||
|
keybinds.insert({HOLD, sfKey::RShift});
|
||||||
|
}
|
||||||
|
if (layout == 3) {
|
||||||
|
keybinds.insert({MOVE_LEFT, sfKey::Left});
|
||||||
|
keybinds.insert({MOVE_RIGHT, sfKey::Right});
|
||||||
|
keybinds.insert({SOFT_DROP, sfKey::Down});
|
||||||
|
keybinds.insert({HARD_DROP, sfKey::Up});
|
||||||
|
keybinds.insert({ROTATE_CW, sfKey::E});
|
||||||
|
keybinds.insert({ROTATE_CCW, sfKey::A});
|
||||||
|
keybinds.insert({ROTATE_180, sfKey::Num2});
|
||||||
|
keybinds.insert({ROTATE_0, sfKey::Tab});
|
||||||
|
keybinds.insert({HOLD, sfKey::Z});
|
||||||
|
}
|
||||||
|
|
||||||
|
char byte;
|
||||||
|
for (Action action : ACTION_LIST_IN_ORDER) {
|
||||||
|
byte = action;
|
||||||
|
layoutFile.write(&byte, 1);
|
||||||
|
|
||||||
|
if (keybinds.contains(action)) {
|
||||||
|
byte = (int) keybinds.at(action);
|
||||||
|
layoutFile.write(&byte, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
byte = 0xFF;
|
||||||
|
layoutFile.write(&byte, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ void TextApp::run() {
|
|||||||
default : std::cout << "Invalid answer!" << std::endl;
|
default : std::cout << "Invalid answer!" << std::endl;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
std::cout << "===| SEE YA NEXT TIME! |===";
|
std::cout << "===| SEE YA NEXT TIME! |===" << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
void TextApp::choosePieces() {
|
void TextApp::choosePieces() {
|
||||||
@@ -136,7 +136,6 @@ void TextApp::seeKeybinds() const {
|
|||||||
|
|
||||||
void TextApp::defaultKeybinds() {
|
void TextApp::defaultKeybinds() {
|
||||||
this->keybinds.clear();
|
this->keybinds.clear();
|
||||||
this->keybinds.insert({"quit", QUIT});
|
|
||||||
this->keybinds.insert({"pause", PAUSE});
|
this->keybinds.insert({"pause", PAUSE});
|
||||||
this->keybinds.insert({"retry", RETRY});
|
this->keybinds.insert({"retry", RETRY});
|
||||||
this->keybinds.insert({"h", HOLD});
|
this->keybinds.insert({"h", HOLD});
|
||||||
@@ -171,15 +170,21 @@ void TextApp::startGame() const {
|
|||||||
|
|
||||||
std::set<Action> playerActions;
|
std::set<Action> playerActions;
|
||||||
std::set<Action> lastFrameActions;
|
std::set<Action> lastFrameActions;
|
||||||
std::set<Action> metaActions;
|
bool retrying = false;
|
||||||
for (std::string action : actions) {
|
for (std::string action : actions) {
|
||||||
try {
|
if (action == "quit") {
|
||||||
Action playerAction = this->keybinds.at(action);
|
quit = true;
|
||||||
if (playerAction == PAUSE || playerAction == RETRY || playerAction == quit) {
|
}
|
||||||
metaActions.insert(playerAction);
|
else {
|
||||||
}
|
try {
|
||||||
else {
|
Action playerAction = this->keybinds.at(action);
|
||||||
if (playerAction == SOFT_DROP || playerAction == MOVE_LEFT || playerAction == MOVE_RIGHT) {
|
if (playerAction == RETRY) {
|
||||||
|
retrying = true;
|
||||||
|
}
|
||||||
|
else if (playerAction == PAUSE) {
|
||||||
|
paused = (!paused);
|
||||||
|
}
|
||||||
|
else if (playerAction == SOFT_DROP || playerAction == MOVE_LEFT || playerAction == MOVE_RIGHT) {
|
||||||
playerActions.insert(playerAction);
|
playerActions.insert(playerAction);
|
||||||
lastFrameActions.insert(playerAction);
|
lastFrameActions.insert(playerAction);
|
||||||
}
|
}
|
||||||
@@ -190,19 +195,12 @@ void TextApp::startGame() const {
|
|||||||
playerActions.insert(playerAction);
|
playerActions.insert(playerAction);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
catch (std::exception ignored) {}
|
||||||
}
|
}
|
||||||
catch (std::exception ignored) {}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (metaActions.contains(PAUSE)) {
|
if (!paused && !quit) {
|
||||||
paused = (!paused);
|
if (retrying) {
|
||||||
}
|
|
||||||
|
|
||||||
if (!paused) {
|
|
||||||
if (metaActions.contains(QUIT)) {
|
|
||||||
quit = true;
|
|
||||||
}
|
|
||||||
else if (metaActions.contains(RETRY)) {
|
|
||||||
game.reset();
|
game.reset();
|
||||||
game.start();
|
game.start();
|
||||||
}
|
}
|
||||||
@@ -253,7 +251,7 @@ void TextApp::printGame(const Game& game) const {
|
|||||||
for (int x = 0; x < game.getBoard().getWidth(); x++) {
|
for (int x = 0; x < game.getBoard().getWidth(); x++) {
|
||||||
/* BOARD PRINTING */
|
/* BOARD PRINTING */
|
||||||
bool isActivePieceHere = (game.getActivePiece() != nullptr) && (game.getActivePiece()->getPositions().contains(Position{x, y} - game.getActivePiecePosition()));
|
bool isActivePieceHere = (game.getActivePiece() != nullptr) && (game.getActivePiece()->getPositions().contains(Position{x, y} - game.getActivePiecePosition()));
|
||||||
bool isGhostPieceHere = (game.getActivePiece() != nullptr) && (game.getActivePiece()->getPositions().contains(Position{x, y} - game.ghostPiecePosition()));
|
bool isGhostPieceHere = (game.getActivePiece() != nullptr) && (game.getActivePiece()->getPositions().contains(Position{x, y} - game.getGhostPiecePosition()));
|
||||||
Block block = (isActivePieceHere || isGhostPieceHere) ? game.getActivePiece()->getBlockType() : game.getBoard().getBlock(Position{x, y});
|
Block block = (isActivePieceHere || isGhostPieceHere) ? game.getActivePiece()->getBlockType() : game.getBoard().getBlock(Position{x, y});
|
||||||
|
|
||||||
if (isActivePieceHere || isGhostPieceHere) {
|
if (isActivePieceHere || isGhostPieceHere) {
|
||||||
|
|||||||
Reference in New Issue
Block a user