Compare commits
13 Commits
321271b748
...
v0.2
| Author | SHA1 | Date | |
|---|---|---|---|
| e0de2b5f90 | |||
| df7c9bd211 | |||
| f883bd5bab | |||
| 0f026635f6 | |||
| be071bd606 | |||
| 3320545465 | |||
| d1646d0fb5 | |||
| 3d74ef7cd5 | |||
| 88cb44c5fe | |||
| 009ed8edc3 | |||
| b2567844fc | |||
| de8a5e6e34 | |||
| c168cd68d7 |
2
.gitignore
vendored
@@ -13,7 +13,7 @@ doc/*.txt
|
||||
doc/*.violet.html
|
||||
doc/mockups/*
|
||||
|
||||
# pieces files
|
||||
# data files
|
||||
data/pieces/*.bin
|
||||
data/config/*.bin
|
||||
data/config/keybinds/*.bin
|
||||
|
||||
41
README.md
@@ -2,10 +2,35 @@
|
||||
|
||||
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
|
||||
## Download
|
||||
|
||||
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.
|
||||
// to bo included when release version is done //
|
||||
|
||||
This game has been tested on and provides executables for Windows 11 and Linux under Ubuntu only.
|
||||
If your OS isn't compactible with either of theses two, you can try [manually building the project](#manual-build-and-run).
|
||||
|
||||
## How to play
|
||||
|
||||
### General
|
||||
|
||||
You can see and change in-game keybinds in the **SETTINGS** section of the main menu!
|
||||
All of in-menu navigation is done with the arrow keys, the Enter key and the Escape key. Theses are unmutable keybinds.
|
||||
You will find some more infos about the Rotation System and the scoring in the **INFO** section of the main menu.
|
||||
If you want to know more details about the generation and classification of polyominoes, [check the documentation](/doc/)!
|
||||
|
||||
### Available gamemodes
|
||||
|
||||
- SPRINT : clear 40 lines as fast as possible!
|
||||
- MARATHON : clear 200 lines with increasing gravity!
|
||||
- ULTRA : scores as much as possible in only 2 minutes!
|
||||
- MASTER : clear 200 lines at levels higher than maximum gravity!
|
||||
- ZEN : practice indefinitely in this mode with no gravity!
|
||||
- ??? : still to do
|
||||
|
||||
## Manual build
|
||||
|
||||
This project uses xmake for compiling, xmake is cross-platform and works in most OS, xmake also automatically install supported librairies.
|
||||
To be able to build this project, you need to [have xmake installed](https://xmake.io) and have a compiler with C++20 compatibility.
|
||||
|
||||
### Build the project
|
||||
|
||||
@@ -19,11 +44,17 @@ If you need to change the toolchain (for example using gcc):
|
||||
### Run the project
|
||||
|
||||
``xmake run``
|
||||
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.
|
||||
Note that the program will generate the polyomino files for you the first time. This lasts several minutes so it only does it up to size 10. If you want to use the full range up to size 15, you will need to uncomment the ``#define`` at line 13 of file ``src/GraphicalUI/Settings.h``.
|
||||
|
||||
If for some reasons you wanna run the command line version:
|
||||
``xmake run text``
|
||||
|
||||
### Create a release version (xmake packaging ???)
|
||||
|
||||
## Credits
|
||||
|
||||
Font used: [Press Start](https://www.zone38.net/font/#pressstart).
|
||||
Library used: [SFML 3](https://www.sfml-dev.org/).
|
||||
Font used: [Press Start](https://www.zone38.net/font/#pressstart).
|
||||
|
||||
Inspired by other modern stacker games such as Techmino, jstris, tetr.io, etc.
|
||||
This game isn't affiliated with any of them.
|
||||
|
||||
BIN
data/images/keybinds/Harddrop.png
Normal file
|
After Width: | Height: | Size: 619 B |
BIN
data/images/keybinds/Hold.png
Normal file
|
After Width: | Height: | Size: 618 B |
BIN
data/images/keybinds/Moveleft.png
Normal file
|
After Width: | Height: | Size: 604 B |
BIN
data/images/keybinds/Moveright.png
Normal file
|
After Width: | Height: | Size: 603 B |
BIN
data/images/keybinds/Pause.png
Normal file
|
After Width: | Height: | Size: 591 B |
BIN
data/images/keybinds/Retry.png
Normal file
|
After Width: | Height: | Size: 600 B |
BIN
data/images/keybinds/Rotate0.png
Normal file
|
After Width: | Height: | Size: 624 B |
BIN
data/images/keybinds/Rotate180.png
Normal file
|
After Width: | Height: | Size: 627 B |
BIN
data/images/keybinds/RotateCCW.png
Normal file
|
After Width: | Height: | Size: 622 B |
BIN
data/images/keybinds/RotateCW.png
Normal file
|
After Width: | Height: | Size: 623 B |
BIN
data/images/keybinds/Softdrop.png
Normal file
|
After Width: | Height: | Size: 606 B |
@@ -38,6 +38,9 @@ _Repeat for every avaible actions._
|
||||
The settings file has the following format:
|
||||
|
||||
- The number of the chosen keybinds (from 0 to 4), stored with 1 byte
|
||||
- The DAS of the player, stored with 1 byte
|
||||
- The ARR of the player, stored with 1 byte
|
||||
- The SDR of the player, stored with 1 byte
|
||||
- The window size mode, stored with 1 byte
|
||||
- The master volume, stored with 1 byte
|
||||
- The number of the last selected gamemode (converted from an Enum), stored with 1 byte
|
||||
|
||||
@@ -92,8 +92,17 @@ Since we work with a great deal of different size and shapes, the rules for spin
|
||||
|
||||
## Score calculation
|
||||
|
||||
- For every position soft dropped, add 1 to the score
|
||||
- For every position hard dropped, add 2 to the score
|
||||
- When locking a piece, add 1 to the score if it is due to lock delay, or 10 if it was hard dropped.
|
||||
- When clearing one line, add 100 to the score, 200 for 2 lines, 400 for 3 lines, 800 for 4 lines, 1600 for 5 lines, etc.
|
||||
- If the line clear is a spin, count the score like a normal clear of 2x more line (200 for 1-line spin, 800 for 2, 3200 for 3, etc.)
|
||||
- When performing a spin, a mini spin, or clearing 4 or more lines, B2B is activated, every subsequent line clear that is a spin, a mini spin, or clear 4 or more lines, scores twice as much
|
||||
|
||||
## Grade calculation
|
||||
|
||||
Grade is an alternate system to line clears.
|
||||
|
||||
- Each time a piece is dropped, 1 point is added to the total.
|
||||
- For each cleared line, 1 point is added to the total.
|
||||
|
||||
The only exception occurs when the total ends in '99', a line must be cleared to progress.
|
||||
When this line is cleared, the point of the piece is also counted towards the total.
|
||||
|
||||
@@ -42,9 +42,9 @@ static const std::string ACTION_NAMES[] = { // name representation for each a
|
||||
"Hard drop",
|
||||
"Move left",
|
||||
"Move right",
|
||||
"Rotate 0°",
|
||||
"Rotate 0",
|
||||
"Rotate CW",
|
||||
"Rotate 180°",
|
||||
"Rotate 180",
|
||||
"Rotate CCW"
|
||||
};
|
||||
|
||||
|
||||
@@ -9,8 +9,8 @@
|
||||
#include <memory>
|
||||
|
||||
static const int SUBPX_PER_ROW = 60; // the number of position the active piece can take "between" two rows
|
||||
static const int SOFT_DROP_SCORE = 1; // the score gained by line soft dropped
|
||||
static const int HARD_DROP_SCORE = 2; // the score gained by line hard dropped
|
||||
static const int LOCK_DELAY_SCORE = 1; // the score gained when the piece locks due to lock delay
|
||||
static const int HARD_DROP_SCORE = 10; // the score gained when the piece locks due to an hard drop
|
||||
static const int LINE_CLEAR_BASE_SCORE = 100; // the score value of clearing a single line
|
||||
static const int B2B_SCORE_MULTIPLIER = 2; // by how much havaing B2B on multiplies the score of the line clear
|
||||
static const int B2B_MIN_LINE_NUMBER = 4; // the minimum number of lines needed to be cleared at once to gain B2B (without a spin)
|
||||
@@ -140,16 +140,14 @@ void Game::nextFrame(const std::set<Action>& playerActions) {
|
||||
|
||||
// SDR=0 -> instant drop
|
||||
if (appliedSDR == 0) {
|
||||
while (this->board.moveDown()) {
|
||||
this->score += SOFT_DROP_SCORE;
|
||||
}
|
||||
while (this->board.moveDown());
|
||||
}
|
||||
// SDR>1 -> move down by specified amount
|
||||
else {
|
||||
this->subVerticalPosition += (SUBPX_PER_ROW / appliedSDR);
|
||||
while (this->subVerticalPosition >= SUBPX_PER_ROW) {
|
||||
this->board.moveDown();
|
||||
this->subVerticalPosition -= SUBPX_PER_ROW;
|
||||
this->score += (this->board.moveDown() * SOFT_DROP_SCORE);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -157,11 +155,10 @@ void Game::nextFrame(const std::set<Action>& playerActions) {
|
||||
/* HARD DROP */
|
||||
// needs to be done last because we can enter ARE period afterwards
|
||||
if (this->initialActions.contains(HARD_DROP) || (playerActions.contains(HARD_DROP) && (!this->heldActions.contains(HARD_DROP)))) {
|
||||
while (this->board.moveDown()) {
|
||||
this->score += HARD_DROP_SCORE;
|
||||
}
|
||||
while (this->board.moveDown());
|
||||
this->lockPiece();
|
||||
pieceJustLocked = true;
|
||||
this->score += HARD_DROP_SCORE;
|
||||
}
|
||||
// no need to apply gravity and lock delay if the piece was hard dropped
|
||||
else {
|
||||
@@ -192,6 +189,7 @@ void Game::nextFrame(const std::set<Action>& playerActions) {
|
||||
if ((this->totalLockDelay > this->parameters.getLockDelay()) || (this->totalForcedLockDelay > this->parameters.getForcedLockDelay())) {
|
||||
this->lockPiece();
|
||||
pieceJustLocked = true;
|
||||
this->score += LOCK_DELAY_SCORE;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -290,19 +288,23 @@ void Game::rotatePiece(Rotation rotation) {
|
||||
|
||||
void Game::lockPiece() {
|
||||
LineClear clear = this->board.lockPiece();
|
||||
this->parameters.clearLines(clear.lines);
|
||||
this->parameters.lockedPiece(clear);
|
||||
|
||||
bool B2BConditionsAreMet = ((clear.lines > B2B_MIN_LINE_NUMBER) || clear.isSpin || clear.isMiniSpin);
|
||||
if (clear.lines > 0) {
|
||||
bool B2BConditionsAreMet = ((clear.lines >= B2B_MIN_LINE_NUMBER) || clear.isSpin || clear.isMiniSpin);
|
||||
|
||||
/* clearing one more line is worth 2x more
|
||||
clearing with a spin is worth as much as clearing 2x more lines */
|
||||
clearing with a spin is worth as much as clearing 2x more lines */
|
||||
long int clearScore = LINE_CLEAR_BASE_SCORE;
|
||||
clearScore = clearScore << (clear.lines << (clear.isSpin));
|
||||
|
||||
if (this->B2BChain && B2BConditionsAreMet) clearScore *= B2B_SCORE_MULTIPLIER;
|
||||
if (this->B2BChain && B2BConditionsAreMet) {
|
||||
clearScore *= B2B_SCORE_MULTIPLIER;
|
||||
}
|
||||
this->score += clearScore;
|
||||
|
||||
this->B2BChain = B2BConditionsAreMet;
|
||||
}
|
||||
this->B2BChain = B2BConditionsAreMet;
|
||||
|
||||
if (!this->hasWon()) {
|
||||
this->leftARETime = this->parameters.getARE();
|
||||
@@ -330,6 +332,10 @@ int Game::getClearedLines() const {
|
||||
return this->parameters.getClearedLines();
|
||||
}
|
||||
|
||||
int Game::getGrade() const {
|
||||
return this->parameters.getGrade();
|
||||
}
|
||||
|
||||
int Game::getLevel() const {
|
||||
return this->parameters.getLevel();
|
||||
}
|
||||
|
||||
@@ -101,6 +101,11 @@ class Game {
|
||||
*/
|
||||
int getClearedLines() const;
|
||||
|
||||
/**
|
||||
* @return The current grade
|
||||
*/
|
||||
int getGrade() const;
|
||||
|
||||
/**
|
||||
* @return The number of frames passed since the start of the game
|
||||
*/
|
||||
|
||||
@@ -13,6 +13,8 @@ GameParameters::GameParameters(Gamemode gamemode, const Player& controls) :
|
||||
|
||||
void GameParameters::reset() {
|
||||
this->clearedLines = 0;
|
||||
this->grade = 0;
|
||||
|
||||
switch (this->gamemode) {
|
||||
// lowest gravity
|
||||
case SPRINT : {this->level = 1; break;}
|
||||
@@ -22,19 +24,21 @@ void GameParameters::reset() {
|
||||
case MARATHON : {this->level = 1; break;}
|
||||
// goes from level 20 to 39
|
||||
case MASTER : {this->level = 20; break;}
|
||||
// no gravity
|
||||
case ZEN : {this->level = 0; break;}
|
||||
default : this->level = 1;
|
||||
}
|
||||
|
||||
this->updateStats();
|
||||
}
|
||||
|
||||
void GameParameters::clearLines(int lineNumber) {
|
||||
void GameParameters::lockedPiece(const LineClear& lineClear) {
|
||||
switch (this->gamemode) {
|
||||
// modes where level increases
|
||||
case MARATHON :
|
||||
case MASTER : {
|
||||
int previousLines = this->clearedLines;
|
||||
this->clearedLines += lineNumber;
|
||||
this->clearedLines += lineClear.lines;
|
||||
|
||||
// level increments every 10 lines, stats only changes on level up
|
||||
if (previousLines / 10 < this->clearedLines / 10) {
|
||||
@@ -44,7 +48,11 @@ void GameParameters::clearLines(int lineNumber) {
|
||||
break;
|
||||
}
|
||||
// other modes
|
||||
default : this->clearedLines += lineNumber;
|
||||
default : this->clearedLines += lineClear.lines;
|
||||
}
|
||||
|
||||
if (!((lineClear.lines == 0) && ((this->grade % 100) == 99))) {
|
||||
this->grade += (1 + lineClear.lines);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,6 +66,8 @@ bool GameParameters::hasWon(int framesPassed) const {
|
||||
case MARATHON : return this->clearedLines >= 200;
|
||||
// win once 200 lines have been cleared
|
||||
case MASTER : return this->clearedLines >= 200;
|
||||
// infinite mode
|
||||
case ZEN :
|
||||
default : return false;
|
||||
}
|
||||
}
|
||||
@@ -65,13 +75,14 @@ bool GameParameters::hasWon(int framesPassed) const {
|
||||
void GameParameters::updateStats() {
|
||||
/* NEXT QUEUE */
|
||||
switch (this->gamemode) {
|
||||
// 5 for rapidity gamemodes
|
||||
// 5 for fast-controls gamemodes
|
||||
case SPRINT :
|
||||
case ULTRA : {
|
||||
case ULTRA :
|
||||
case ZEN : {
|
||||
this->nextQueueLength = 5;
|
||||
break;
|
||||
}
|
||||
// 3 for endurance gamemodes
|
||||
// 3 for slow-controls gamemodes
|
||||
case MARATHON :
|
||||
case MASTER : {
|
||||
this->nextQueueLength = 3;
|
||||
@@ -126,6 +137,8 @@ void GameParameters::updateStats() {
|
||||
switch (this->gamemode) {
|
||||
// starts at 500ms (30f) at lvl 20 and ends at 183ms (11f) at lvl 39
|
||||
case MASTER : {this->lockDelay = 30 - (this->level - 20); break;}
|
||||
// 10s
|
||||
case ZEN : {this->lockDelay = 60 * 10; break;}
|
||||
// 1s by default
|
||||
default : this->lockDelay = 60;
|
||||
}
|
||||
@@ -203,6 +216,10 @@ int GameParameters::getLevel() const {
|
||||
return this->level;
|
||||
}
|
||||
|
||||
int GameParameters::getGrade() const {
|
||||
return this->grade;
|
||||
}
|
||||
|
||||
int GameParameters::getNextQueueLength() const {
|
||||
return this->nextQueueLength;
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
#include "Gamemode.h"
|
||||
#include "Player.h"
|
||||
#include "LineClear.h"
|
||||
|
||||
|
||||
/**
|
||||
@@ -14,6 +15,7 @@ class GameParameters {
|
||||
Player controls; // the player's controls
|
||||
int clearedLines; // the number of cleared lines
|
||||
int level; // the current level
|
||||
int grade; // the current amount of points
|
||||
int nextQueueLength; // the number of pieces visibles in the next queue
|
||||
bool boneBlocks; // wheter all blocks are bone blocks
|
||||
int gravity; // the gravity at which pieces drop
|
||||
@@ -39,7 +41,7 @@ class GameParameters {
|
||||
/**
|
||||
* Counts the newly cleared lines and update level and stats if needed
|
||||
*/
|
||||
void clearLines(int lineNumber);
|
||||
void lockedPiece(const LineClear& lineClear);
|
||||
|
||||
/**
|
||||
* Checks if the game ended based on the current states and time passed, accorind to the gamemode
|
||||
@@ -63,6 +65,11 @@ class GameParameters {
|
||||
* @return The current level
|
||||
*/
|
||||
int getLevel() const;
|
||||
|
||||
/**
|
||||
* @return The current grade
|
||||
*/
|
||||
int getGrade() const;
|
||||
|
||||
/**
|
||||
* @return The length of the next queue
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
|
||||
/**
|
||||
* Every gamemode supported by the game
|
||||
@@ -8,5 +10,37 @@ enum Gamemode {
|
||||
SPRINT,
|
||||
MARATHON,
|
||||
ULTRA,
|
||||
MASTER
|
||||
MASTER,
|
||||
ZEN
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @return A string containing the name of the gamemode
|
||||
*/
|
||||
inline std::string getGamemodeName(Gamemode gamemode) {
|
||||
static const std::string GAMEMODE_NAMES[] = {
|
||||
"SPRINT",
|
||||
"MARATHON",
|
||||
"ULTRA",
|
||||
"MASTER",
|
||||
"ZEN"
|
||||
};
|
||||
|
||||
return GAMEMODE_NAMES[gamemode];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return A tiny string containing the goal of the gamemode
|
||||
*/
|
||||
inline std::string getGamemodeGoal(Gamemode gamemode) {
|
||||
static const std::string GAMEMODE_DESCRIPTIONS[] = {
|
||||
"40 lines",
|
||||
"200 lines",
|
||||
"2 minutes",
|
||||
"200 lines",
|
||||
"Chill"
|
||||
};
|
||||
|
||||
return GAMEMODE_DESCRIPTIONS[gamemode];
|
||||
}
|
||||
|
||||
@@ -47,6 +47,14 @@ Player& Menu::getPlayerControls() {
|
||||
return this->playerControls;
|
||||
}
|
||||
|
||||
const Player& Menu::readPlayerControls() const {
|
||||
return this->playerControls;
|
||||
}
|
||||
|
||||
PiecesList& Menu::getPiecesList() {
|
||||
return *this->piecesList;
|
||||
}
|
||||
|
||||
const PiecesList& Menu::readPiecesList() const {
|
||||
return *this->piecesList;
|
||||
}
|
||||
|
||||
@@ -50,14 +50,24 @@ class Menu {
|
||||
* @return The height of the board
|
||||
*/
|
||||
int getBoardHeight() const;
|
||||
|
||||
|
||||
/**
|
||||
* @return A reference to the player's controls
|
||||
*/
|
||||
Player& getPlayerControls();
|
||||
|
||||
/**
|
||||
* @return A reference to the player's controls
|
||||
*/
|
||||
const Player& readPlayerControls() const;
|
||||
|
||||
/**
|
||||
* @return A reference to the pieces list
|
||||
*/
|
||||
PiecesList& getPiecesList();
|
||||
|
||||
/**
|
||||
* @return A reference to the pieces list
|
||||
*/
|
||||
const PiecesList& readPiecesList() const;
|
||||
};
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
#include "../Settings.h"
|
||||
#include "../PlayerCursor.h"
|
||||
|
||||
#include <stack>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <SFML/Graphics.hpp>
|
||||
|
||||
class AppMenu;
|
||||
@@ -19,6 +21,7 @@ class AppMenu {
|
||||
bool enterReleased = false;
|
||||
bool escPressed = false;
|
||||
bool escReleased = false;
|
||||
sf::Font pressStartFont = sf::Font("data/fonts/pressstart/prstartk.ttf");
|
||||
|
||||
public:
|
||||
AppMenu(std::shared_ptr<MenuStack> menuStack, std::shared_ptr<Settings> settings, std::shared_ptr<sf::RenderWindow> renderWindow) :
|
||||
@@ -29,6 +32,11 @@ class AppMenu {
|
||||
|
||||
}
|
||||
|
||||
virtual void computeFrame() = 0;
|
||||
|
||||
virtual void drawFrame() const = 0;
|
||||
|
||||
protected:
|
||||
void updateMetaBinds() {
|
||||
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Key::Enter)) {
|
||||
enterPressed = true;
|
||||
@@ -49,15 +57,27 @@ class AppMenu {
|
||||
}
|
||||
}
|
||||
|
||||
virtual void computeFrame() = 0;
|
||||
void placeText(sf::Text& text, const std::optional<PlayerCursor>& playerCursor, const sf::String& string, float xPos, float yPos, const std::optional<sf::Vector2u>& cursorPos) const {
|
||||
float sizeMultiplier = this->settings->getWindowSizeMultiplier();
|
||||
|
||||
virtual void drawFrame() const = 0;
|
||||
text.setString(string);
|
||||
if (playerCursor.has_value() && cursorPos.has_value()) {
|
||||
text.setOutlineThickness((playerCursor.value().getPosition() == cursorPos.value()) ? (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);
|
||||
}
|
||||
|
||||
void placeTitle(sf::Text& text, const std::optional<PlayerCursor>& playerCursor, const sf::String& string, float yPos, const std::optional<sf::Vector2u>& cursorPos) const {
|
||||
float sizeMultiplier = this->settings->getWindowSizeMultiplier();
|
||||
|
||||
text.setString(string);
|
||||
if (playerCursor.has_value() && cursorPos.has_value()) {
|
||||
text.setOutlineThickness((playerCursor.value().getPosition() == cursorPos.value()) ? (sizeMultiplier / 2) : 0);
|
||||
}
|
||||
text.setOrigin({text.getLocalBounds().getCenter().x, text.getLocalBounds().size.y / 2});
|
||||
text.setPosition(sf::Vector2f({sizeMultiplier * 40.f, sizeMultiplier * yPos}));
|
||||
this->renderWindow->draw(text);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
inline void changeVideoMode(sf::RenderWindow& window, const sf::VideoMode& videoMode) {
|
||||
window.create(videoMode, "jminos", sf::Style::Close | sf::Style::Titlebar);
|
||||
sf::Vector2u desktopSize = sf::VideoMode::getDesktopMode().size;
|
||||
sf::Vector2u windowSize = window.getSize();
|
||||
window.setPosition(sf::Vector2i((desktopSize.x / 2) - (windowSize.x / 2), (desktopSize.y / 2) - (windowSize.y / 2)));
|
||||
}
|
||||
|
||||
67
src/GraphicalUI/AppMenus/GameBoardAppMenu.cpp
Normal file
@@ -0,0 +1,67 @@
|
||||
#include "GameBoardAppMenu.h"
|
||||
|
||||
#include "AppMenu.h"
|
||||
#include "../PlayerCursor.h"
|
||||
|
||||
#include <stack>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <SFML/Graphics.hpp>
|
||||
|
||||
|
||||
GameBoardAppMenu::GameBoardAppMenu(std::shared_ptr<MenuStack> menuStack, std::shared_ptr<Settings> settings, std::shared_ptr<sf::RenderWindow> renderWindow) :
|
||||
AppMenu(menuStack, settings, renderWindow),
|
||||
playerCursor({1, 1}) {
|
||||
|
||||
}
|
||||
|
||||
void GameBoardAppMenu::computeFrame() {
|
||||
this->updateMetaBinds();
|
||||
this->playerCursor.updatePosition();
|
||||
|
||||
Menu& menu = this->settings->getMenu();
|
||||
|
||||
switch (this->playerCursor.getPosition().y) {
|
||||
case 0 : {
|
||||
if (this->playerCursor.movedLeft()) {
|
||||
menu.setBoardWidth(std::max(1, menu.getBoardWidth() - 1));
|
||||
}
|
||||
if (this->playerCursor.movedRight()) {
|
||||
menu.setBoardWidth(std::min(MAXIMUM_BOARD_WIDTH, menu.getBoardWidth() + 1));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 1 : {
|
||||
if (this->playerCursor.movedLeft()) {
|
||||
menu.setBoardHeight(std::max(1, menu.getBoardHeight() - 1));
|
||||
}
|
||||
if (this->playerCursor.movedRight()) {
|
||||
menu.setBoardHeight(std::min(MAXIMUM_BOARD_HEIGHT, menu.getBoardHeight() + 1));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (this->escReleased) {
|
||||
this->menuStack->pop();
|
||||
}
|
||||
}
|
||||
|
||||
void GameBoardAppMenu::drawFrame() const {
|
||||
this->renderWindow->clear(sf::Color(200, 200, 200));
|
||||
|
||||
const Menu& menu = this->settings->getMenu();
|
||||
|
||||
sf::Text text(this->pressStartFont, "", this->settings->getWindowSizeMultiplier() * 2);
|
||||
text.setFillColor(sf::Color(0, 0, 0));
|
||||
text.setOutlineColor(sf::Color(255, 255, 255));
|
||||
|
||||
this->placeTitle(text, {}, "BOARD SETTINGS", 5.f, {});
|
||||
|
||||
sf::Vector2u windowSize = this->renderWindow->getSize();
|
||||
|
||||
this->placeText(text, this->playerCursor, "< BOARD WIDTH: " + std::to_string(menu.getBoardWidth()) + " >", 5.f, 15.f, sf::Vector2u{0, 0});
|
||||
this->placeText(text, this->playerCursor, "< BOARD HEIGHT: " + std::to_string(menu.getBoardHeight()) + " >", 5.f, 25.f, sf::Vector2u{0, 1});
|
||||
|
||||
this->renderWindow->display();
|
||||
}
|
||||
21
src/GraphicalUI/AppMenus/GameBoardAppMenu.h
Normal file
@@ -0,0 +1,21 @@
|
||||
#pragma once
|
||||
|
||||
#include "AppMenu.h"
|
||||
#include "../PlayerCursor.h"
|
||||
|
||||
#include <stack>
|
||||
#include <memory>
|
||||
#include <SFML/Graphics.hpp>
|
||||
|
||||
|
||||
class GameBoardAppMenu : public AppMenu {
|
||||
private:
|
||||
PlayerCursor playerCursor;
|
||||
|
||||
public:
|
||||
GameBoardAppMenu(std::shared_ptr<MenuStack> menuStack, std::shared_ptr<Settings> settings, std::shared_ptr<sf::RenderWindow> renderWindow);
|
||||
|
||||
void computeFrame() override;
|
||||
|
||||
void drawFrame() const override;
|
||||
};
|
||||
@@ -6,14 +6,17 @@
|
||||
#include <memory>
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <string>
|
||||
#include <SFML/Graphics.hpp>
|
||||
|
||||
static const int TIME_BEFORE_STARTING = 60;
|
||||
|
||||
|
||||
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->startTimer = TIME_BEFORE_STARTING;
|
||||
this->paused = false;
|
||||
this->pausePressed = false;
|
||||
this->retryPressed = false;
|
||||
@@ -24,14 +27,37 @@ GamePlayingAppMenu::GamePlayingAppMenu(std::shared_ptr<MenuStack> menuStack, std
|
||||
|
||||
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));
|
||||
this->boardPosition = sf::FloatRect(sf::Vector2f((this->settings->getWindowSizeMultiplier() * 40) - (boardWidth / 2),
|
||||
(this->settings->getWindowSizeMultiplier() * 25) - (boardHeight / 2)),
|
||||
sf::Vector2f(boardWidth, boardHeight));
|
||||
|
||||
|
||||
for (int i = 0; i < 5; i++) {
|
||||
this->nextQueuePosition[i] = sf::FloatRect(sf::Vector2f(this->boardPosition.position.x + boardWidth + (5.f * this->settings->getWindowSizeMultiplier()), (10.f + (10.f * i)) * this->settings->getWindowSizeMultiplier()),
|
||||
sf::Vector2f(8.f * this->settings->getWindowSizeMultiplier(), 8.f * this->settings->getWindowSizeMultiplier()));
|
||||
}
|
||||
|
||||
this->nextCellSizeZoom = this->nextQueuePosition[0].size.y;
|
||||
for (const auto& piece : this->settings->getMenu().getPiecesList().getSelectedPieces()) {
|
||||
float nextPieceCellSizeZoom = ((int) this->nextQueuePosition[0].size.y) / this->settings->getMenu().getPiecesList().getPiece(piece).getLength();
|
||||
this->nextCellSizeZoom = std::min(this->nextCellSizeZoom, nextPieceCellSizeZoom);
|
||||
}
|
||||
|
||||
this->holdBoxPosition = sf::FloatRect(sf::Vector2f(this->boardPosition.position.x - ((8.f + 5.f) * this->settings->getWindowSizeMultiplier()), (10.f) * this->settings->getWindowSizeMultiplier()),
|
||||
sf::Vector2f(8.f * this->settings->getWindowSizeMultiplier(), 8.f * this->settings->getWindowSizeMultiplier()));
|
||||
this->holdCellSizeZoom = this->nextCellSizeZoom;
|
||||
}
|
||||
|
||||
void GamePlayingAppMenu::computeFrame() {
|
||||
this->updateMetaBinds();
|
||||
|
||||
if (this->startTimer > 0) {
|
||||
this->startTimer--;
|
||||
if (this->startTimer == 0) {
|
||||
this->game.start();
|
||||
}
|
||||
}
|
||||
|
||||
if (this->escReleased) {
|
||||
this->menuStack->pop();
|
||||
}
|
||||
@@ -51,7 +77,7 @@ void GamePlayingAppMenu::computeFrame() {
|
||||
else {
|
||||
if (this->retryPressed) {
|
||||
this->game.reset();
|
||||
this->game.start();
|
||||
this->startTimer = TIME_BEFORE_STARTING;
|
||||
}
|
||||
this->retryPressed = false;
|
||||
}
|
||||
@@ -85,7 +111,7 @@ void GamePlayingAppMenu::drawFrame() const {
|
||||
|
||||
sf::RectangleShape cell(cellSize);
|
||||
cell.setFillColor(this->getColorOfBlock(block, (block == NOTHING) ? 0 : -30));
|
||||
cell.setPosition(this->getBlockPosition(x, y));
|
||||
cell.setPosition(this->getBoardBlockPosition(x, y));
|
||||
this->renderWindow->draw(cell);
|
||||
}
|
||||
}
|
||||
@@ -98,7 +124,7 @@ void GamePlayingAppMenu::drawFrame() const {
|
||||
|
||||
sf::RectangleShape cell(cellSize);
|
||||
cell.setFillColor(ghostColor);
|
||||
cell.setPosition(this->getBlockPosition(cellPosition.x, cellPosition.y));
|
||||
cell.setPosition(this->getBoardBlockPosition(cellPosition.x, cellPosition.y));
|
||||
this->renderWindow->draw(cell);
|
||||
}
|
||||
|
||||
@@ -111,14 +137,14 @@ void GamePlayingAppMenu::drawFrame() const {
|
||||
sf::RectangleShape cell(cellSize);
|
||||
cell.setOutlineThickness(pieceOutlineSize);
|
||||
cell.setOutlineColor(pieceOultlineColor);
|
||||
cell.setPosition(this->getBlockPosition(cellPosition.x, cellPosition.y));
|
||||
cell.setPosition(this->getBoardBlockPosition(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.setPosition(this->getBoardBlockPosition(0, this->game.getBoard().getBaseHeight() - 1));
|
||||
topOutLine.setFillColor(sf::Color(255, 0, 0));
|
||||
this->renderWindow->draw(topOutLine);
|
||||
|
||||
@@ -131,11 +157,111 @@ void GamePlayingAppMenu::drawFrame() const {
|
||||
|
||||
sf::RectangleShape cell(cellSize);
|
||||
cell.setFillColor(pieceColor);
|
||||
cell.setPosition(this->getBlockPosition(cellPosition.x, cellPosition.y));
|
||||
cell.setPosition(this->getBoardBlockPosition(cellPosition.x, cellPosition.y));
|
||||
this->renderWindow->draw(cell);
|
||||
}
|
||||
}
|
||||
|
||||
// next queue
|
||||
int upShift = 0;
|
||||
for (int i = 0; i < std::min((int) this->game.getNextPieces().size(), 5); i++) {
|
||||
sf::FloatRect nextBox = this->nextQueuePosition[i];
|
||||
nextBox.position.y -= upShift;
|
||||
|
||||
sf::Vector2f nextCellSize(this->nextCellSizeZoom, this->nextCellSizeZoom);
|
||||
sf::Color color = this->getColorOfBlock(this->game.getNextPieces().at(i).getBlockType(), 0);
|
||||
sf::Color boxColor = sf::Color(180, 180, 180);
|
||||
|
||||
int lowestRank = 0;
|
||||
for (int y = 0; y < this->game.getNextPieces().at(i).getLength(); y++) {
|
||||
for (int x = 0; x < this->game.getNextPieces().at(i).getLength(); x++) {
|
||||
sf::RectangleShape cell(nextCellSize);
|
||||
if (this->game.getNextPieces().at(i).getPositions().contains(Position{x, y})) {
|
||||
cell.setFillColor(color);
|
||||
lowestRank = y;
|
||||
}
|
||||
else {
|
||||
cell.setFillColor(boxColor);
|
||||
}
|
||||
cell.setPosition(sf::Vector2f(nextBox.position.x + (x * this->nextCellSizeZoom),
|
||||
nextBox.position.y + ((this->game.getNextPieces().at(i).getLength() - y - 1) * this->nextCellSizeZoom)));
|
||||
this->renderWindow->draw(cell);
|
||||
}
|
||||
}
|
||||
|
||||
upShift += nextBox.size.y - (this->game.getNextPieces().at(i).getLength() * this->nextCellSizeZoom);
|
||||
}
|
||||
|
||||
// hold box
|
||||
if (this->game.getHeldPiece() != nullptr) {
|
||||
sf::Vector2f holdCellSize(this->holdCellSizeZoom, this->holdCellSizeZoom);
|
||||
sf::Color color = this->getColorOfBlock(this->game.getHeldPiece()->getBlockType(), 0);
|
||||
sf::Color boxColor = sf::Color(180, 180, 180);
|
||||
|
||||
for (int y = 0; y < this->game.getHeldPiece()->getLength(); y++) {
|
||||
for (int x = 0; x < this->game.getHeldPiece()->getLength(); x++) {
|
||||
sf::RectangleShape cell(holdCellSize);
|
||||
if (this->game.getHeldPiece()->getPositions().contains(Position{x, y})) {
|
||||
cell.setFillColor(color);
|
||||
}
|
||||
else {
|
||||
cell.setFillColor(boxColor);
|
||||
}
|
||||
cell.setPosition(sf::Vector2f(this->holdBoxPosition.position.x + (x * this->nextCellSizeZoom),
|
||||
this->holdBoxPosition.position.y + ((this->game.getHeldPiece()->getLength() - y - 1) * this->holdCellSizeZoom)));
|
||||
this->renderWindow->draw(cell);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// stats
|
||||
int windowSizeMultiplier = this->settings->getWindowSizeMultiplier();
|
||||
int fontSize = (this->boardPosition.size.x > (windowSizeMultiplier * 30.f)) ? (windowSizeMultiplier) : (windowSizeMultiplier * 2);
|
||||
sf::Text text(this->pressStartFont, "", fontSize);
|
||||
text.setFillColor(sf::Color(0, 0, 0));
|
||||
|
||||
int millisecondes = this->game.getFramesPassed() * (1000.f / FRAMES_PER_SECOND);
|
||||
std::string showedMillisecondes = std::to_string(millisecondes % 1000);
|
||||
while (showedMillisecondes.size() < 3) {
|
||||
showedMillisecondes = "0" + showedMillisecondes;
|
||||
}
|
||||
std::string showedSecondes = std::to_string((millisecondes / 1000) % 60);
|
||||
while (showedSecondes.size() < 2) {
|
||||
showedSecondes = "0" + showedSecondes;
|
||||
}
|
||||
std::string showedMinutes = std::to_string((millisecondes / (60 * 1000)));
|
||||
std::string showedTime = showedMinutes + ":" + showedSecondes + "." + showedMillisecondes;
|
||||
|
||||
this->placeText(text, {}, getGamemodeName(this->settings->getGamemode()), 1.f, 3.f, {});
|
||||
this->placeText(text, {}, getGamemodeGoal(this->settings->getGamemode()), 1.f, 6.f, {});
|
||||
|
||||
if (this->game.isOnB2BChain()) {
|
||||
this->placeText(text, {}, "B2B", 1.f, 22.f, {});
|
||||
}
|
||||
this->placeText(text, {}, "LINES:" + std::to_string(this->game.getClearedLines()), 1.f, 27.f, {});
|
||||
this->placeText(text, {}, "LEVEL:" + std::to_string(this->game.getLevel()), 1.f, 32.f, {});
|
||||
this->placeText(text, {}, "SCORE:" + std::to_string(this->game.getScore()), 1.f, 37.f, {});
|
||||
this->placeText(text, {}, "GRADE:" + std::to_string(this->game.getGrade()), 1.f, 42.f, {});
|
||||
this->placeText(text, {}, showedTime, 1.f, 47.f, {});
|
||||
|
||||
// game state
|
||||
text.setOutlineColor(sf::Color(255, 255, 255));
|
||||
text.setOutlineThickness(windowSizeMultiplier / 2.f);
|
||||
|
||||
if (this->game.hasWon()) {
|
||||
this->placeTitle(text, {}, "WIN", 25.f, {});
|
||||
}
|
||||
else if (this->game.hasLost()) {
|
||||
this->placeTitle(text, {}, "LOSE", 25.f, {});
|
||||
}
|
||||
else if (this->paused) {
|
||||
this->placeTitle(text, {}, "PAUSE", 25.f, {});
|
||||
}
|
||||
else if (this->startTimer > 0) {
|
||||
text.setCharacterSize(windowSizeMultiplier * 4);
|
||||
this->placeTitle(text, {}, std::to_string(((this->startTimer - 1) / (TIME_BEFORE_STARTING / 4))), 25.f, {});
|
||||
}
|
||||
|
||||
this->renderWindow->display();
|
||||
}
|
||||
|
||||
@@ -146,7 +272,7 @@ sf::Color GamePlayingAppMenu::getColorOfBlock(Block block, int luminosityShift)
|
||||
std::clamp(rgbColor.blue + luminosityShift, 0, 255));
|
||||
}
|
||||
|
||||
sf::Vector2f GamePlayingAppMenu::getBlockPosition(int x, int y) const {
|
||||
sf::Vector2f GamePlayingAppMenu::getBoardBlockPosition(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));
|
||||
}
|
||||
|
||||
@@ -10,11 +10,16 @@
|
||||
class GamePlayingAppMenu : public AppMenu {
|
||||
private:
|
||||
Game game;
|
||||
int startTimer;
|
||||
bool paused;
|
||||
bool pausePressed;
|
||||
bool retryPressed;
|
||||
sf::FloatRect boardPosition;
|
||||
float cellSizeZoom;
|
||||
sf::Rect<float> boardPosition;
|
||||
sf::FloatRect holdBoxPosition;
|
||||
float holdCellSizeZoom;
|
||||
sf::FloatRect nextQueuePosition[5];
|
||||
float nextCellSizeZoom;
|
||||
|
||||
public:
|
||||
GamePlayingAppMenu(std::shared_ptr<MenuStack> menuStack, std::shared_ptr<Settings> settings, std::shared_ptr<sf::RenderWindow> renderWindow);
|
||||
@@ -25,5 +30,5 @@ class GamePlayingAppMenu : public AppMenu {
|
||||
|
||||
sf::Color getColorOfBlock(Block block, int luminosityShift) const;
|
||||
|
||||
sf::Vector2f getBlockPosition(int x, int y) const;
|
||||
sf::Vector2f getBoardBlockPosition(int x, int y) const;
|
||||
};
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
#include "GameSettingsAppMenu.h"
|
||||
|
||||
#include "AppMenu.h"
|
||||
#include "GameBoardAppMenu.h"
|
||||
#include "GamePlayingAppMenu.h"
|
||||
#include "PlayerCursor.h"
|
||||
#include "../PlayerCursor.h"
|
||||
|
||||
#include <stack>
|
||||
#include <memory>
|
||||
@@ -12,7 +13,7 @@
|
||||
|
||||
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})) {
|
||||
playerCursor({2, 3, 3}) {
|
||||
|
||||
}
|
||||
|
||||
@@ -32,7 +33,7 @@ void GameSettingsAppMenu::computeFrame() {
|
||||
case 2 : {
|
||||
switch (this->playerCursor.getPosition().x) {
|
||||
case 0 : {this->settings->setGamemode(MASTER); break;}
|
||||
case 1 : break; //TODO
|
||||
case 1 : {this->settings->setGamemode(ZEN); break;}
|
||||
case 2 : break; //TODO
|
||||
}
|
||||
break;
|
||||
@@ -41,7 +42,12 @@ void GameSettingsAppMenu::computeFrame() {
|
||||
|
||||
if (this->enterReleased) {
|
||||
if (this->playerCursor.getPosition().y == 0) {
|
||||
//TODO
|
||||
if (this->playerCursor.getPosition().x == 0) {
|
||||
//TODO
|
||||
}
|
||||
if (this->playerCursor.getPosition().x == 1) {
|
||||
this->menuStack->push(std::make_shared<GameBoardAppMenu>(this->menuStack, this->settings, this->renderWindow));
|
||||
}
|
||||
}
|
||||
if (this->playerCursor.getPosition().y > 0) {
|
||||
this->menuStack->push(std::make_shared<GamePlayingAppMenu>(this->menuStack, this->settings, this->renderWindow));
|
||||
@@ -55,37 +61,21 @@ void GameSettingsAppMenu::computeFrame() {
|
||||
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);
|
||||
sf::Text text(this->pressStartFont, "", 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->placeTitle(text, {}, "GAME SETTINGS", 5.f, {});
|
||||
|
||||
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, this->playerCursor, "PIECES SELECT (TODO)", 5.f, 15.f, sf::Vector2u{0, 0});
|
||||
this->placeText(text, this->playerCursor, "BOARD SELECT", 40.f, 15.f, sf::Vector2u{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->placeText(text, this->playerCursor, "SPRINT", 5.f, 25.f, sf::Vector2u{0, 1});
|
||||
this->placeText(text, this->playerCursor, "MARATHON", 25.f, 25.f, sf::Vector2u{1, 1});
|
||||
this->placeText(text, this->playerCursor, "ULTRA", 50.f, 25.f, sf::Vector2u{2, 1});
|
||||
this->placeText(text, this->playerCursor, "MASTER", 5.f, 35.f, sf::Vector2u{0, 2});
|
||||
this->placeText(text, this->playerCursor, "ZEN", 25.f, 35.f, sf::Vector2u{1, 2});
|
||||
this->placeText(text, this->playerCursor, "??? (TODO)", 50.f, 35.f, sf::Vector2u{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);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "AppMenu.h"
|
||||
#include "PlayerCursor.h"
|
||||
#include "../PlayerCursor.h"
|
||||
|
||||
#include <stack>
|
||||
#include <memory>
|
||||
@@ -18,6 +18,4 @@ class GameSettingsAppMenu : public AppMenu {
|
||||
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;
|
||||
};
|
||||
|
||||
88
src/GraphicalUI/AppMenus/InfoAppMenu.cpp
Normal file
@@ -0,0 +1,88 @@
|
||||
#include "InfoAppMenu.h"
|
||||
|
||||
#include "AppMenu.h"
|
||||
#include "../PlayerCursor.h"
|
||||
|
||||
#include <stack>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <SFML/Graphics.hpp>
|
||||
|
||||
|
||||
InfoAppMenu::InfoAppMenu(std::shared_ptr<MenuStack> menuStack, std::shared_ptr<Settings> settings, std::shared_ptr<sf::RenderWindow> renderWindow) :
|
||||
AppMenu(menuStack, settings, renderWindow),
|
||||
playerCursor({4}),
|
||||
sectionsName(
|
||||
"< ABOUT >",
|
||||
"< ROTATION SYSTEM >",
|
||||
"< SCORING >",
|
||||
"< 0 DEGREES ROTATIONS >"
|
||||
),
|
||||
sectionsContent(
|
||||
"This game is written in C++,\n"
|
||||
"using SFML 3 for the GUI.\n"
|
||||
"It has been inspired by other\n"
|
||||
"stacker games, such as\n"
|
||||
"Techmino, jstris, tetr.io, etc.\n"
|
||||
"This project isn't affiliated\n"
|
||||
"to them in any ways.\n"
|
||||
"Current version: beta.",
|
||||
|
||||
"This game uses its own\n"
|
||||
"Rotation Sytem, called AutoRS.\n"
|
||||
"The rotation center is always the\n"
|
||||
"center of the piece by default.\n"
|
||||
"When kicking the piece, it will\n"
|
||||
"compute and try (most) position that\n"
|
||||
"touches the original piece,\n"
|
||||
"prioritizing sides over depth and\n"
|
||||
"firstly going down before going up.",
|
||||
|
||||
"The score gained from a line clear\n"
|
||||
"doubles when clearing one more line.\n"
|
||||
"Clearing with a spin scores as much\n"
|
||||
"as clearing 2x more lines normally.\n"
|
||||
"B2B is granted by clearing at least\n"
|
||||
"4 lines or doing a spin or mini-spin,\n"
|
||||
"and doubles the score gained.\n"
|
||||
"A spin is detected when the piece is\n"
|
||||
"locked in place, a mini-spin simply\n"
|
||||
"when the last move was a kick.",
|
||||
|
||||
"This games introduces 0 degrees\n"
|
||||
"rotations, which work by simpling\n"
|
||||
"moving the piece down and kicking\n"
|
||||
"it as is, allowing for new kinds\n"
|
||||
"of kicks.\n"
|
||||
"As a leniency mechanic, when a\n"
|
||||
"piece spawns it will automatically\n"
|
||||
"try a 0 degrees rotations if it\n"
|
||||
"spawned inside a wall."
|
||||
) {
|
||||
|
||||
}
|
||||
|
||||
void InfoAppMenu::computeFrame() {
|
||||
this->updateMetaBinds();
|
||||
this->playerCursor.updatePosition();
|
||||
|
||||
if (this->escReleased) {
|
||||
this->menuStack->pop();
|
||||
}
|
||||
}
|
||||
|
||||
void InfoAppMenu::drawFrame() const {
|
||||
this->renderWindow->clear(sf::Color(200, 200, 200));
|
||||
|
||||
sf::Text text(this->pressStartFont, "", this->settings->getWindowSizeMultiplier() * 2);
|
||||
text.setFillColor(sf::Color(0, 0, 0));
|
||||
text.setOutlineColor(sf::Color(255, 255, 255));
|
||||
|
||||
this->placeTitle(text, this->playerCursor, this->sectionsName[this->playerCursor.getPosition().x], 10.f, this->playerCursor.getPosition());
|
||||
|
||||
text.setLineSpacing((float) this->settings->getWindowSizeMultiplier() / 8);
|
||||
text.setOutlineThickness(0);
|
||||
this->placeText(text, {}, this->sectionsContent[this->playerCursor.getPosition().x], 5.f, 30.f, {});
|
||||
|
||||
this->renderWindow->display();
|
||||
}
|
||||
23
src/GraphicalUI/AppMenus/InfoAppMenu.h
Normal file
@@ -0,0 +1,23 @@
|
||||
#pragma once
|
||||
|
||||
#include "AppMenu.h"
|
||||
#include "../PlayerCursor.h"
|
||||
|
||||
#include <stack>
|
||||
#include <memory>
|
||||
#include <SFML/Graphics.hpp>
|
||||
|
||||
|
||||
class InfoAppMenu : public AppMenu {
|
||||
private:
|
||||
PlayerCursor playerCursor;
|
||||
sf::String sectionsName[4];
|
||||
sf::String sectionsContent[4];
|
||||
|
||||
public:
|
||||
InfoAppMenu(std::shared_ptr<MenuStack> menuStack, std::shared_ptr<Settings> settings, std::shared_ptr<sf::RenderWindow> renderWindow);
|
||||
|
||||
void computeFrame() override;
|
||||
|
||||
void drawFrame() const override;
|
||||
};
|
||||
@@ -3,7 +3,8 @@
|
||||
#include "AppMenu.h"
|
||||
#include "GameSettingsAppMenu.h"
|
||||
#include "SettingsMainAppMenu.h"
|
||||
#include "PlayerCursor.h"
|
||||
#include "InfoAppMenu.h"
|
||||
#include "../PlayerCursor.h"
|
||||
|
||||
#include <stack>
|
||||
#include <memory>
|
||||
@@ -13,7 +14,7 @@
|
||||
|
||||
MainAppMenu::MainAppMenu(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>({1, 1, 1})) {
|
||||
playerCursor({1, 1, 1}) {
|
||||
|
||||
}
|
||||
|
||||
@@ -29,7 +30,7 @@ void MainAppMenu::computeFrame() {
|
||||
this->menuStack->push(std::make_shared<SettingsMainAppMenu>(this->menuStack, this->settings, this->renderWindow));
|
||||
}
|
||||
if (this->playerCursor.getPosition().y == 2) {
|
||||
//TODO
|
||||
this->menuStack->push(std::make_shared<InfoAppMenu>(this->menuStack, this->settings, this->renderWindow));
|
||||
}
|
||||
}
|
||||
if (this->escReleased) {
|
||||
@@ -40,35 +41,15 @@ void MainAppMenu::computeFrame() {
|
||||
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);
|
||||
sf::Text text(this->pressStartFont, "", 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);
|
||||
this->placeTitle(text, {}, "JMINOS", 10.f, {});
|
||||
|
||||
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->placeTitle(text, this->playerCursor, "PLAY", 20.f, sf::Vector2u{0, 0});
|
||||
this->placeTitle(text, this->playerCursor, "SETTINGS", 30.f, sf::Vector2u{0, 1});
|
||||
this->placeTitle(text, this->playerCursor, "INFO", 40.f, sf::Vector2u{0, 2});
|
||||
|
||||
this->renderWindow->display();
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "AppMenu.h"
|
||||
#include "PlayerCursor.h"
|
||||
#include "../PlayerCursor.h"
|
||||
|
||||
#include <stack>
|
||||
#include <memory>
|
||||
|
||||
77
src/GraphicalUI/AppMenus/SettingsControlsAppMenu.cpp
Normal file
@@ -0,0 +1,77 @@
|
||||
#include "SettingsControlsAppMenu.h"
|
||||
|
||||
#include "AppMenu.h"
|
||||
#include "../PlayerCursor.h"
|
||||
|
||||
#include <stack>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <SFML/Graphics.hpp>
|
||||
|
||||
|
||||
SettingsControlsAppMenu::SettingsControlsAppMenu(std::shared_ptr<MenuStack> menuStack, std::shared_ptr<Settings> settings, std::shared_ptr<sf::RenderWindow> renderWindow) :
|
||||
AppMenu(menuStack, settings, renderWindow),
|
||||
playerCursor({1, 1, 1}) {
|
||||
|
||||
}
|
||||
|
||||
void SettingsControlsAppMenu::computeFrame() {
|
||||
this->updateMetaBinds();
|
||||
this->playerCursor.updatePosition();
|
||||
|
||||
Player& playerControls = this->settings->getMenu().getPlayerControls();
|
||||
|
||||
switch (this->playerCursor.getPosition().y) {
|
||||
case 0 : {
|
||||
if (this->playerCursor.movedLeft()) {
|
||||
playerControls.setDAS(playerControls.getDAS() - 1);
|
||||
}
|
||||
if (this->playerCursor.movedRight()) {
|
||||
playerControls.setDAS(playerControls.getDAS() + 1);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 1 : {
|
||||
if (this->playerCursor.movedLeft()) {
|
||||
playerControls.setARR(playerControls.getARR() - 1);
|
||||
}
|
||||
if (this->playerCursor.movedRight()) {
|
||||
playerControls.setARR(playerControls.getARR() + 1);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 2 : {
|
||||
if (this->playerCursor.movedLeft()) {
|
||||
playerControls.setSDR(playerControls.getSDR() - 1);
|
||||
}
|
||||
if (this->playerCursor.movedRight()) {
|
||||
playerControls.setSDR(playerControls.getSDR() + 1);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (this->escReleased) {
|
||||
this->menuStack->pop();
|
||||
}
|
||||
}
|
||||
|
||||
void SettingsControlsAppMenu::drawFrame() const {
|
||||
this->renderWindow->clear(sf::Color(200, 200, 200));
|
||||
|
||||
const Player& playerControls = this->settings->getMenu().readPlayerControls();
|
||||
|
||||
sf::Text text(this->pressStartFont, "", this->settings->getWindowSizeMultiplier() * 2);
|
||||
text.setFillColor(sf::Color(0, 0, 0));
|
||||
text.setOutlineColor(sf::Color(255, 255, 255));
|
||||
|
||||
this->placeTitle(text, {}, "CONTROLS SETTINGS", 5.f, {});
|
||||
|
||||
sf::Vector2u windowSize = this->renderWindow->getSize();
|
||||
|
||||
this->placeText(text, this->playerCursor, "< DAS: " + std::to_string(playerControls.getDAS()) + " >", 5.f, 15.f, sf::Vector2u{0, 0});
|
||||
this->placeText(text, this->playerCursor, "< ARR: " + std::to_string(playerControls.getARR()) + " >", 5.f, 25.f, sf::Vector2u{0, 1});
|
||||
this->placeText(text, this->playerCursor, "< SDR: " + std::to_string(playerControls.getSDR()) + " >", 5.f, 35.f, sf::Vector2u{0, 2});
|
||||
|
||||
this->renderWindow->display();
|
||||
}
|
||||
21
src/GraphicalUI/AppMenus/SettingsControlsAppMenu.h
Normal file
@@ -0,0 +1,21 @@
|
||||
#pragma once
|
||||
|
||||
#include "AppMenu.h"
|
||||
#include "../PlayerCursor.h"
|
||||
|
||||
#include <stack>
|
||||
#include <memory>
|
||||
#include <SFML/Graphics.hpp>
|
||||
|
||||
|
||||
class SettingsControlsAppMenu : public AppMenu {
|
||||
private:
|
||||
PlayerCursor playerCursor;
|
||||
|
||||
public:
|
||||
SettingsControlsAppMenu(std::shared_ptr<MenuStack> menuStack, std::shared_ptr<Settings> settings, std::shared_ptr<sf::RenderWindow> renderWindow);
|
||||
|
||||
void computeFrame() override;
|
||||
|
||||
void drawFrame() const override;
|
||||
};
|
||||
128
src/GraphicalUI/AppMenus/SettingsKeybindsAppMenu.cpp
Normal file
@@ -0,0 +1,128 @@
|
||||
#include "SettingsKeybindsAppMenu.h"
|
||||
|
||||
#include "AppMenu.h"
|
||||
#include "../PlayerCursor.h"
|
||||
|
||||
#include <stack>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <regex>
|
||||
#include <filesystem>
|
||||
#include <algorithm>
|
||||
#include <SFML/Graphics.hpp>
|
||||
|
||||
|
||||
SettingsKeybindsAppMenu::SettingsKeybindsAppMenu(std::shared_ptr<MenuStack> menuStack, std::shared_ptr<Settings> settings, std::shared_ptr<sf::RenderWindow> renderWindow) :
|
||||
AppMenu(menuStack, settings, renderWindow),
|
||||
playerCursor({12, 1}) {
|
||||
|
||||
this->selectedAnAction = false;
|
||||
|
||||
for (Action action : ACTION_LIST_IN_ORDER) {
|
||||
std::string textureName = ACTION_NAMES[action];
|
||||
textureName = std::regex_replace(textureName, std::regex(" "), "");
|
||||
|
||||
std::filesystem::path texturePath("data/images/keybinds/" + textureName + ".png");
|
||||
this->iconTextures[action] = sf::Texture(texturePath, false, {{0, 0}, {16, 16}});
|
||||
}
|
||||
}
|
||||
|
||||
void SettingsKeybindsAppMenu::computeFrame() {
|
||||
this->updateMetaBinds();
|
||||
|
||||
if (!this->selectedAnAction) {
|
||||
this->playerCursor.updatePosition();
|
||||
|
||||
if (this->playerCursor.movedLeft()) {
|
||||
this->settings->selectPreviousKeybinds();
|
||||
}
|
||||
if (this->playerCursor.movedRight()) {
|
||||
this->settings->selectNextKeybinds();
|
||||
}
|
||||
}
|
||||
else {
|
||||
bool addedKeybind = false;
|
||||
for (const auto& [key, string] : KEYS_TO_STRING) {
|
||||
if (sf::Keyboard::isKeyPressed(key) && (key != sfKey::Enter) && (key != sfKey::Escape)) {
|
||||
this->settings->getKeybinds().addKey(this->actionSelected, key);
|
||||
addedKeybind = true;
|
||||
}
|
||||
|
||||
if (addedKeybind) {
|
||||
this->selectedAnAction = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (this->enterReleased && this->settings->getKeybinds().isModifiable()) {
|
||||
this->selectedAnAction = !selectedAnAction;
|
||||
this->actionSelected = ACTION_LIST_IN_ORDER[this->playerCursor.getPosition().y - 1];
|
||||
}
|
||||
if (this->escReleased) {
|
||||
if (this->selectedAnAction) {
|
||||
this->settings->getKeybinds().clearKeys(this->actionSelected);
|
||||
this->selectedAnAction = false;
|
||||
}
|
||||
else {
|
||||
this->menuStack->pop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SettingsKeybindsAppMenu::drawFrame() const {
|
||||
this->renderWindow->clear(sf::Color(200, 200, 200));
|
||||
|
||||
sf::Text text(this->pressStartFont, "", this->settings->getWindowSizeMultiplier() * 2);
|
||||
text.setFillColor(sf::Color(0, 0, 0));
|
||||
text.setOutlineColor(sf::Color(255, 255, 255));
|
||||
|
||||
this->placeTitle(text, {}, "KEYBINDS SETTINGS", 5.f, {});
|
||||
|
||||
if (this->settings->getKeybindsLayout() == CUSTOMIZABLE_KEYBINDS) {
|
||||
this->placeText(text, this->playerCursor, "< CUSTOM >", 5.f, 15.f, sf::Vector2u{0, 0});
|
||||
}
|
||||
else {
|
||||
this->placeText(text, this->playerCursor, "< DEFAULT " + std::to_string(this->settings->getKeybindsLayout() + 1) + " >", 5.f, 15.f, sf::Vector2u{0, 0});
|
||||
}
|
||||
|
||||
if (this->selectedAnAction) {
|
||||
text.setOutlineColor(sf::Color(255, 0, 0));
|
||||
}
|
||||
|
||||
int i = 0;
|
||||
int firstElem = std::clamp(((int) this->playerCursor.getPosition().y) - 2, 0, 8);
|
||||
|
||||
for (Action action : ACTION_LIST_IN_ORDER) {
|
||||
if (i >= firstElem && i < (firstElem + 3)) {
|
||||
sf::String string;
|
||||
bool firstKey = true;
|
||||
for (sfKey key : this->settings->getKeybinds().getKeybinds(action)) {
|
||||
if (KEYS_TO_STRING.contains(key)) {
|
||||
std::string keyString = KEYS_TO_STRING.at(key);
|
||||
if (firstKey) {
|
||||
string += keyString;
|
||||
firstKey = false;
|
||||
}
|
||||
else {
|
||||
string += ", " + keyString;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this->placeText(text, this->playerCursor, setStringToUpperCase(ACTION_NAMES[action]), 15.f, ((i - firstElem) * 10) + 25.f, sf::Vector2u{0, (unsigned int) i + 1});
|
||||
text.setOutlineThickness(0);
|
||||
this->placeText(text, {}, string, 40.f, ((i - firstElem) * 10) + 25.f, {});
|
||||
|
||||
sf::Sprite sprite(this->iconTextures[action]);
|
||||
sprite.setOrigin(sprite.getLocalBounds().getCenter());
|
||||
sprite.setPosition(sf::Vector2f(8.f, ((i - firstElem) * 10) + 25.f) * (float) this->settings->getWindowSizeMultiplier());
|
||||
sprite.setScale(sprite.getScale() * ((float) this->settings->getWindowSizeMultiplier() / 2));
|
||||
this->renderWindow->draw(sprite);
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
this->renderWindow->display();
|
||||
}
|
||||
24
src/GraphicalUI/AppMenus/SettingsKeybindsAppMenu.h
Normal file
@@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
|
||||
#include "AppMenu.h"
|
||||
#include "../PlayerCursor.h"
|
||||
|
||||
#include <stack>
|
||||
#include <memory>
|
||||
#include <SFML/Graphics.hpp>
|
||||
|
||||
|
||||
class SettingsKeybindsAppMenu : public AppMenu {
|
||||
private:
|
||||
PlayerCursor playerCursor;
|
||||
sf::Texture iconTextures[11];
|
||||
bool selectedAnAction;
|
||||
Action actionSelected;
|
||||
|
||||
public:
|
||||
SettingsKeybindsAppMenu(std::shared_ptr<MenuStack> menuStack, std::shared_ptr<Settings> settings, std::shared_ptr<sf::RenderWindow> renderWindow);
|
||||
|
||||
void computeFrame() override;
|
||||
|
||||
void drawFrame() const override;
|
||||
};
|
||||
@@ -1,7 +1,9 @@
|
||||
#include "SettingsMainAppMenu.h"
|
||||
|
||||
#include "AppMenu.h"
|
||||
#include "PlayerCursor.h"
|
||||
#include "SettingsKeybindsAppMenu.h"
|
||||
#include "SettingsControlsAppMenu.h"
|
||||
#include "../PlayerCursor.h"
|
||||
|
||||
#include <stack>
|
||||
#include <memory>
|
||||
@@ -11,7 +13,7 @@
|
||||
|
||||
SettingsMainAppMenu::SettingsMainAppMenu(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>({1, 1, 1, 1})) {
|
||||
playerCursor({1, 1, 1, 1}) {
|
||||
|
||||
}
|
||||
|
||||
@@ -23,12 +25,12 @@ void SettingsMainAppMenu::computeFrame() {
|
||||
case 2 : {
|
||||
if (this->playerCursor.movedLeft()) {
|
||||
if (this->settings->shortenWindow()) {
|
||||
changeVideoMode(*this->renderWindow, this->settings->getVideoMode());
|
||||
this->settings->changeVideoMode(*this->renderWindow);
|
||||
}
|
||||
}
|
||||
if (this->playerCursor.movedRight()) {
|
||||
if (this->settings->widenWindow()) {
|
||||
changeVideoMode(*this->renderWindow, this->settings->getVideoMode());
|
||||
this->settings->changeVideoMode(*this->renderWindow);
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -46,10 +48,10 @@ void SettingsMainAppMenu::computeFrame() {
|
||||
|
||||
if (this->enterReleased) {
|
||||
if (this->playerCursor.getPosition().y == 0) {
|
||||
//TODO
|
||||
this->menuStack->push(std::make_shared<SettingsKeybindsAppMenu>(this->menuStack, this->settings, this->renderWindow));
|
||||
}
|
||||
if (this->playerCursor.getPosition().y == 1) {
|
||||
//TODO
|
||||
this->menuStack->push(std::make_shared<SettingsControlsAppMenu>(this->menuStack, this->settings, this->renderWindow));
|
||||
}
|
||||
}
|
||||
if (this->escReleased) {
|
||||
@@ -60,33 +62,18 @@ void SettingsMainAppMenu::computeFrame() {
|
||||
void SettingsMainAppMenu::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);
|
||||
sf::Text text(this->pressStartFont, "", this->settings->getWindowSizeMultiplier() * 2);
|
||||
text.setFillColor(sf::Color(0, 0, 0));
|
||||
text.setOutlineColor(sf::Color(255, 255, 255));
|
||||
|
||||
text.setString("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->placeTitle(text, {}, "SETTINGS", 5.f, {});
|
||||
|
||||
sf::Vector2u windowSize = this->renderWindow->getSize();
|
||||
|
||||
this->placeText(text, "CHANGE KEYBINDS", 5.f, 15.f, 0, 0);
|
||||
this->placeText(text, "CHANGE CONTROLS", 5.f, 25.f, 0, 1);
|
||||
this->placeText(text, "WINDOW SIZE: " + std::to_string(windowSize.x) + "x" + std::to_string(windowSize.y), 5.f, 35.f, 0, 2);
|
||||
this->placeText(text, "VOLUME: " + std::to_string(this->settings->getMasterVolume()) + "%", 5.f, 45.f, 0, 3);
|
||||
this->placeText(text, this->playerCursor, "CHANGE KEYBINDS", 5.f, 15.f, sf::Vector2u{0, 0});
|
||||
this->placeText(text, this->playerCursor, "CHANGE CONTROLS", 5.f, 25.f, sf::Vector2u{0, 1});
|
||||
this->placeText(text, this->playerCursor, "< WINDOW SIZE: " + std::to_string(windowSize.x) + "x" + std::to_string(windowSize.y) + " >", 5.f, 35.f, sf::Vector2u{0, 2});
|
||||
this->placeText(text, this->playerCursor, "< VOLUME: " + std::to_string(this->settings->getMasterVolume()) + "% >", 5.f, 45.f, sf::Vector2u{0, 3});
|
||||
|
||||
this->renderWindow->display();
|
||||
}
|
||||
|
||||
void SettingsMainAppMenu::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);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "AppMenu.h"
|
||||
#include "PlayerCursor.h"
|
||||
#include "../PlayerCursor.h"
|
||||
|
||||
#include <stack>
|
||||
#include <memory>
|
||||
@@ -18,6 +18,4 @@ class SettingsMainAppMenu : public AppMenu {
|
||||
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;
|
||||
};
|
||||
|
||||
@@ -18,7 +18,7 @@ GraphApp::GraphApp() {
|
||||
}
|
||||
|
||||
void GraphApp::run() {
|
||||
changeVideoMode(*this->renderWindow, this->settings->getVideoMode());
|
||||
this->settings->changeVideoMode(*this->renderWindow);
|
||||
this->menuStack->push(std::make_shared<MainAppMenu>(this->menuStack, this->settings, this->renderWindow));
|
||||
|
||||
bool quit = false;
|
||||
|
||||
@@ -66,6 +66,7 @@ void Keybinds::saveKeybindsToFile() const {
|
||||
|
||||
void Keybinds::addKey(Action action, sfKey key) {
|
||||
if (!this->modifiable) return;
|
||||
if (!KEYS_TO_STRING.contains(key)) return;
|
||||
|
||||
this->keybinds.at(action).insert(key);
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <optional>
|
||||
#include <SFML/Graphics.hpp>
|
||||
|
||||
using sfKey = sf::Keyboard::Key;
|
||||
@@ -17,7 +18,7 @@ class Keybinds {
|
||||
std::map<Action, std::set<sfKey>> keybinds;
|
||||
int layoutNumber;
|
||||
bool modifiable;
|
||||
|
||||
|
||||
public:
|
||||
Keybinds(int layoutNumber);
|
||||
|
||||
@@ -35,3 +36,121 @@ class Keybinds {
|
||||
|
||||
const std::set<sfKey>& getKeybinds(Action action) const;
|
||||
};
|
||||
|
||||
|
||||
inline std::string setStringToUpperCase(std::string&& str) {
|
||||
std::transform(str.begin(), str.end(), str.begin(), ::toupper);
|
||||
return str;
|
||||
}
|
||||
|
||||
inline std::string setStringToUpperCase(const std::string& str) {
|
||||
std::string result = str;
|
||||
std::transform(result.begin(), result.end(), result.begin(), ::toupper);
|
||||
return result;
|
||||
}
|
||||
|
||||
#define INSERT_MAPPING(identifier) {sfKey::identifier, setStringToUpperCase(#identifier)}
|
||||
static const std::map<sfKey, sf::String> KEYS_TO_STRING = {
|
||||
INSERT_MAPPING(A),
|
||||
INSERT_MAPPING(B),
|
||||
INSERT_MAPPING(C),
|
||||
INSERT_MAPPING(D),
|
||||
INSERT_MAPPING(E),
|
||||
INSERT_MAPPING(F),
|
||||
INSERT_MAPPING(G),
|
||||
INSERT_MAPPING(H),
|
||||
INSERT_MAPPING(I),
|
||||
INSERT_MAPPING(J),
|
||||
INSERT_MAPPING(K),
|
||||
INSERT_MAPPING(L),
|
||||
INSERT_MAPPING(M),
|
||||
INSERT_MAPPING(N),
|
||||
INSERT_MAPPING(O),
|
||||
INSERT_MAPPING(P),
|
||||
INSERT_MAPPING(Q),
|
||||
INSERT_MAPPING(R),
|
||||
INSERT_MAPPING(S),
|
||||
INSERT_MAPPING(T),
|
||||
INSERT_MAPPING(U),
|
||||
INSERT_MAPPING(V),
|
||||
INSERT_MAPPING(W),
|
||||
INSERT_MAPPING(X),
|
||||
INSERT_MAPPING(Y),
|
||||
INSERT_MAPPING(Z),
|
||||
INSERT_MAPPING(Num0),
|
||||
INSERT_MAPPING(Num1),
|
||||
INSERT_MAPPING(Num2),
|
||||
INSERT_MAPPING(Num3),
|
||||
INSERT_MAPPING(Num4),
|
||||
INSERT_MAPPING(Num5),
|
||||
INSERT_MAPPING(Num6),
|
||||
INSERT_MAPPING(Num7),
|
||||
INSERT_MAPPING(Num8),
|
||||
INSERT_MAPPING(Num9),
|
||||
INSERT_MAPPING(Escape),
|
||||
INSERT_MAPPING(LControl),
|
||||
INSERT_MAPPING(LShift),
|
||||
INSERT_MAPPING(LAlt),
|
||||
INSERT_MAPPING(LSystem),
|
||||
INSERT_MAPPING(RControl),
|
||||
INSERT_MAPPING(RShift),
|
||||
INSERT_MAPPING(RAlt),
|
||||
INSERT_MAPPING(RSystem),
|
||||
INSERT_MAPPING(Menu),
|
||||
INSERT_MAPPING(LBracket),
|
||||
INSERT_MAPPING(RBracket),
|
||||
INSERT_MAPPING(Semicolon),
|
||||
INSERT_MAPPING(Comma),
|
||||
INSERT_MAPPING(Period),
|
||||
INSERT_MAPPING(Apostrophe),
|
||||
INSERT_MAPPING(Slash),
|
||||
INSERT_MAPPING(Backslash),
|
||||
INSERT_MAPPING(Grave),
|
||||
INSERT_MAPPING(Equal),
|
||||
INSERT_MAPPING(Hyphen),
|
||||
INSERT_MAPPING(Space),
|
||||
INSERT_MAPPING(Enter),
|
||||
INSERT_MAPPING(Backspace),
|
||||
INSERT_MAPPING(Tab),
|
||||
INSERT_MAPPING(PageUp),
|
||||
INSERT_MAPPING(PageDown),
|
||||
INSERT_MAPPING(End),
|
||||
INSERT_MAPPING(Home),
|
||||
INSERT_MAPPING(Insert),
|
||||
INSERT_MAPPING(Delete),
|
||||
INSERT_MAPPING(Add),
|
||||
INSERT_MAPPING(Subtract),
|
||||
INSERT_MAPPING(Multiply),
|
||||
INSERT_MAPPING(Divide),
|
||||
INSERT_MAPPING(Left),
|
||||
INSERT_MAPPING(Right),
|
||||
INSERT_MAPPING(Up),
|
||||
INSERT_MAPPING(Down),
|
||||
INSERT_MAPPING(Numpad0),
|
||||
INSERT_MAPPING(Numpad1),
|
||||
INSERT_MAPPING(Numpad2),
|
||||
INSERT_MAPPING(Numpad3),
|
||||
INSERT_MAPPING(Numpad4),
|
||||
INSERT_MAPPING(Numpad5),
|
||||
INSERT_MAPPING(Numpad6),
|
||||
INSERT_MAPPING(Numpad7),
|
||||
INSERT_MAPPING(Numpad8),
|
||||
INSERT_MAPPING(Numpad9),
|
||||
INSERT_MAPPING(F1),
|
||||
INSERT_MAPPING(F2),
|
||||
INSERT_MAPPING(F3),
|
||||
INSERT_MAPPING(F4),
|
||||
INSERT_MAPPING(F5),
|
||||
INSERT_MAPPING(F6),
|
||||
INSERT_MAPPING(F7),
|
||||
INSERT_MAPPING(F8),
|
||||
INSERT_MAPPING(F9),
|
||||
INSERT_MAPPING(F10),
|
||||
INSERT_MAPPING(F11),
|
||||
INSERT_MAPPING(F12),
|
||||
INSERT_MAPPING(F13),
|
||||
INSERT_MAPPING(F14),
|
||||
INSERT_MAPPING(F15),
|
||||
INSERT_MAPPING(Pause)
|
||||
};
|
||||
#undef INSERT_MAPPING
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#include "PlayerCursor.h"
|
||||
|
||||
#include "../Keybinds.h"
|
||||
#include "../Settings.h"
|
||||
#include "Keybinds.h"
|
||||
#include "Settings.h"
|
||||
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
@@ -33,6 +33,18 @@ void Settings::loadSettingsFromFile() {
|
||||
settingsFile.get(byte);
|
||||
this->chosenKeybinds = byte;
|
||||
|
||||
// DAS tuning
|
||||
settingsFile.get(byte);
|
||||
this->menu.getPlayerControls().setDAS(byte);
|
||||
|
||||
// ARR tuning
|
||||
settingsFile.get(byte);
|
||||
this->menu.getPlayerControls().setARR(byte);
|
||||
|
||||
// SDR tuning
|
||||
settingsFile.get(byte);
|
||||
this->menu.getPlayerControls().setSDR(byte);
|
||||
|
||||
// window size mode
|
||||
settingsFile.get(byte);
|
||||
this->windowSizeMode = byte;
|
||||
@@ -56,6 +68,12 @@ void Settings::loadSettingsFromFile() {
|
||||
// piece distribution
|
||||
settingsFile.get(byte);
|
||||
//TODO
|
||||
if (byte == 2) {
|
||||
for (int i = 1; i <= 15; i++) {
|
||||
settingsFile.get(byte);
|
||||
//TODO
|
||||
}
|
||||
}
|
||||
|
||||
// selected pieces
|
||||
char pieceType;
|
||||
@@ -71,6 +89,8 @@ void Settings::loadSettingsFromFile() {
|
||||
}
|
||||
|
||||
void Settings::saveSettingsToFile() const {
|
||||
this->keybinds.at(CUSTOMIZABLE_KEYBINDS).saveKeybindsToFile();
|
||||
|
||||
std::ofstream settingsFile("data/config/settings.bin", std::ios::trunc | std::ios::binary);
|
||||
char byte;
|
||||
|
||||
@@ -78,6 +98,18 @@ void Settings::saveSettingsToFile() const {
|
||||
byte = this->chosenKeybinds;
|
||||
settingsFile.write(&byte, 1);
|
||||
|
||||
// DAS tuning
|
||||
byte = this->menu.readPlayerControls().getDAS();
|
||||
settingsFile.write(&byte, 1);
|
||||
|
||||
// ARR tuning
|
||||
byte = this->menu.readPlayerControls().getARR();
|
||||
settingsFile.write(&byte, 1);
|
||||
|
||||
// SDR tuning
|
||||
byte = this->menu.readPlayerControls().getSDR();
|
||||
settingsFile.write(&byte, 1);
|
||||
|
||||
// window size mode
|
||||
byte = this->windowSizeMode;
|
||||
settingsFile.write(&byte, 1);
|
||||
@@ -112,7 +144,7 @@ void Settings::saveSettingsToFile() const {
|
||||
}
|
||||
|
||||
bool Settings::selectNextKeybinds() {
|
||||
if (this->chosenKeybinds < NUMBER_OF_KEYBINDS) {
|
||||
if (this->chosenKeybinds < (NUMBER_OF_KEYBINDS - 1)) {
|
||||
this->chosenKeybinds++;
|
||||
return true;
|
||||
}
|
||||
@@ -131,10 +163,6 @@ bool Settings::canModifyCurrentKeybinds() const {
|
||||
return (this->chosenKeybinds == CUSTOMIZABLE_KEYBINDS);
|
||||
}
|
||||
|
||||
void Settings::setGamemode(Gamemode gamemode) {
|
||||
this->gamemode = gamemode;
|
||||
}
|
||||
|
||||
bool Settings::widenWindow() {
|
||||
if (this->windowSizeMode < WINDOW_SIZE_LAST_MODE) {
|
||||
this->windowSizeMode++;
|
||||
@@ -151,6 +179,15 @@ bool Settings::shortenWindow() {
|
||||
return false;
|
||||
}
|
||||
|
||||
void Settings::changeVideoMode(sf::RenderWindow& window) const {
|
||||
sf::VideoMode videoMode(BASE_WINDOW_SIZE * (unsigned int) WINDOW_SIZE_MULTIPLIERS[this->windowSizeMode]);
|
||||
window.create(videoMode, "jminos", sf::Style::Close | sf::Style::Titlebar);
|
||||
|
||||
sf::Vector2u desktopSize = sf::VideoMode::getDesktopMode().size;
|
||||
sf::Vector2u windowSize = window.getSize();
|
||||
window.setPosition(sf::Vector2i((desktopSize.x / 2) - (windowSize.x / 2), (desktopSize.y / 2) - (windowSize.y / 2)));
|
||||
}
|
||||
|
||||
bool Settings::raiseMasterVolume() {
|
||||
if (this->masterVolume < 100) {
|
||||
this->masterVolume = std::min(this->masterVolume + 5, 100);
|
||||
@@ -167,6 +204,10 @@ bool Settings::lowerMasterVolume() {
|
||||
return false;
|
||||
}
|
||||
|
||||
void Settings::setGamemode(Gamemode gamemode) {
|
||||
this->gamemode = gamemode;
|
||||
}
|
||||
|
||||
void Settings::selectPieces(PiecesType type, int value) {
|
||||
this->selectedPieces.emplace_back(type, value);
|
||||
}
|
||||
@@ -207,6 +248,10 @@ Keybinds& Settings::getKeybinds() {
|
||||
return this->keybinds.at(this->chosenKeybinds);
|
||||
}
|
||||
|
||||
int Settings::getKeybindsLayout() const {
|
||||
return this->chosenKeybinds;
|
||||
}
|
||||
|
||||
Gamemode Settings::getGamemode() const {
|
||||
return this->gamemode;
|
||||
}
|
||||
@@ -219,10 +264,6 @@ int Settings::getMasterVolume() const {
|
||||
return this->masterVolume;
|
||||
}
|
||||
|
||||
const sf::VideoMode Settings::getVideoMode() const {
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -23,9 +23,9 @@ class Settings {
|
||||
Menu menu;
|
||||
std::vector<Keybinds> keybinds;
|
||||
int chosenKeybinds;
|
||||
Gamemode gamemode;
|
||||
int windowSizeMode;
|
||||
int masterVolume;
|
||||
Gamemode gamemode;
|
||||
std::vector<std::pair<PiecesType, int>> selectedPieces;
|
||||
|
||||
public:
|
||||
@@ -41,16 +41,18 @@ class Settings {
|
||||
|
||||
bool canModifyCurrentKeybinds() const;
|
||||
|
||||
void setGamemode(Gamemode gamemode);
|
||||
|
||||
bool widenWindow();
|
||||
|
||||
bool shortenWindow();
|
||||
|
||||
void changeVideoMode(sf::RenderWindow& window) const;
|
||||
|
||||
bool raiseMasterVolume();
|
||||
|
||||
bool lowerMasterVolume();
|
||||
|
||||
void setGamemode(Gamemode gamemode);
|
||||
|
||||
void selectPieces(PiecesType type, int value);
|
||||
|
||||
void unselectPieces(int index);
|
||||
@@ -61,13 +63,13 @@ class Settings {
|
||||
|
||||
Keybinds& getKeybinds();
|
||||
|
||||
int getKeybindsLayout() const;
|
||||
|
||||
Gamemode getGamemode() const;
|
||||
|
||||
int getWindowSizeMultiplier() const;
|
||||
|
||||
int getMasterVolume() const;
|
||||
|
||||
const sf::VideoMode getVideoMode() const;
|
||||
|
||||
const std::vector<std::pair<PiecesType, int>>& getSelectedPieces() const;
|
||||
};
|
||||
|
||||
@@ -1,23 +1,10 @@
|
||||
#include "GraphApp.h"
|
||||
#include "../Pieces/PiecesFiles.h"
|
||||
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
|
||||
|
||||
#ifdef __JMINOS_RELEASE__
|
||||
|
||||
int main() {
|
||||
std::srand(std::time(NULL));
|
||||
|
||||
GraphApp UI;
|
||||
UI.run();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
void resetConfigFiles();
|
||||
void resetSettingsFile();
|
||||
void resetKeybindFile(int layout);
|
||||
|
||||
@@ -33,17 +20,16 @@ int main() {
|
||||
}
|
||||
}
|
||||
if (!std::filesystem::exists("data/config/settings.bin")) {
|
||||
std::cout << "settings file not found, generating..." << std::endl;
|
||||
resetSettingsFile();
|
||||
}
|
||||
for (int i = 0; i < 5; i++) {
|
||||
for (int i = 0; i < NUMBER_OF_KEYBINDS; i++) {
|
||||
if (!std::filesystem::exists("data/config/keybinds/layout" + std::to_string(i) + ".bin")) {
|
||||
std::cout << "keybind file n°" << (i + 1) << "/" << NUMBER_OF_KEYBINDS << " not found, generating..." << std::endl;
|
||||
resetKeybindFile(i);
|
||||
}
|
||||
}
|
||||
|
||||
// do this before compiling release version
|
||||
//resetConfigFiles();
|
||||
|
||||
GraphApp UI;
|
||||
UI.run();
|
||||
|
||||
@@ -51,21 +37,28 @@ int main() {
|
||||
}
|
||||
|
||||
|
||||
void resetConfigFiles() {
|
||||
resetSettingsFile;
|
||||
for (int i = 0; i < 5; i++) {
|
||||
resetKeybindFile(i);
|
||||
}
|
||||
}
|
||||
|
||||
void resetSettingsFile() {
|
||||
std::ofstream settingsFile("data/config/settings.bin", std::ios::trunc | std::ios::binary);
|
||||
char byte;
|
||||
|
||||
Menu menu;
|
||||
|
||||
// keybind layout
|
||||
byte = 0;
|
||||
settingsFile.write(&byte, 1);
|
||||
|
||||
// DAS tuning
|
||||
byte = menu.getPlayerControls().getDAS();
|
||||
settingsFile.write(&byte, 1);
|
||||
|
||||
// ARR tuning
|
||||
byte = menu.getPlayerControls().getARR();
|
||||
settingsFile.write(&byte, 1);
|
||||
|
||||
// SDR tuning
|
||||
byte = menu.getPlayerControls().getSDR();
|
||||
settingsFile.write(&byte, 1);
|
||||
|
||||
// window size mode
|
||||
byte = 2;
|
||||
settingsFile.write(&byte, 1);
|
||||
@@ -75,15 +68,15 @@ void resetSettingsFile() {
|
||||
settingsFile.write(&byte, 1);
|
||||
|
||||
// gamemode
|
||||
byte = SPRINT;
|
||||
byte = Gamemode(0);
|
||||
settingsFile.write(&byte, 1);
|
||||
|
||||
// board width
|
||||
byte = 10;
|
||||
byte = menu.getBoardWidth();
|
||||
settingsFile.write(&byte, 1);
|
||||
|
||||
// board height
|
||||
byte = 20;
|
||||
byte = menu.getBoardHeight();
|
||||
settingsFile.write(&byte, 1);
|
||||
|
||||
// piece distribution
|
||||
@@ -167,5 +160,3 @@ void resetKeybindFile(int layout) {
|
||||
layoutFile.write(&byte, 1);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||