Compare commits
20 Commits
v0.2
...
optimizing
| Author | SHA1 | Date | |
|---|---|---|---|
| 6da3cb66fa | |||
| e0ab6a4828 | |||
| a62b3c018d | |||
| 46ebb88ef2 | |||
| de14978b01 | |||
| d5ac79559e | |||
| 5ea47ddd25 | |||
| 1a95765877 | |||
| 6bff555cbc | |||
| f4b58fb67e | |||
| 0271b56542 | |||
| 5d73e751d7 | |||
| 314b7a8488 | |||
| 7151be0b1a | |||
| d124205a71 | |||
| 87920548e5 | |||
| 57620c70a2 | |||
| 3dac18c821 | |||
| 3538403f40 | |||
| ec40495328 |
41
README.md
41
README.md
@@ -4,19 +4,26 @@ Modern stacker game with every polyominos from size 1 to 15, made in C++ with [S
|
||||
|
||||
## Download
|
||||
|
||||
// to bo included when release version is done //
|
||||
// TODO when the game is finished //
|
||||
|
||||
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).
|
||||
This game has been tested on and built on Windows 11 and WSL2 Ubuntu only.
|
||||
If your OS isn't compactible with either of theses two, you can try [manually building the project](#manual-build).
|
||||
|
||||
## 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/)!
|
||||
All of in-menu navigation is done with the **arrow keys**, the **Enter key** and the **Escape key**. Theses are unchangeable keybinds.
|
||||
You will find more infos about the Rotation System, the scoring system, or the different pieces type in the **INFO** section of the main menu.
|
||||
If you want to know more details about the generation of polyominoes, [check the documentation](/doc/)!
|
||||
|
||||
## Features
|
||||
|
||||
- Every polyominoes from size 1 to 15, selectable as you wish, with customizable propotionnality for each size!
|
||||
- Customizable keybinds!
|
||||
- 0° rotations!
|
||||
- AutoRS as the Rotation System!
|
||||
- IRS, IHS, infinite hold, and other leniency mechanics!
|
||||
- Very bland interface!! (i'm not a designer)
|
||||
|
||||
### Available gamemodes
|
||||
|
||||
@@ -25,7 +32,10 @@ If you want to know more details about the generation and classification of poly
|
||||
- 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
|
||||
|
||||
### Screenshots
|
||||
|
||||
// TODO when the game is finished //
|
||||
|
||||
## Manual build
|
||||
|
||||
@@ -44,12 +54,19 @@ 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 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:
|
||||
The program will generate the polyomino files for you if you don't have them.
|
||||
As this is a lengthy process, debug mode limits pieces size to 10.
|
||||
To switch between debug and release mode:
|
||||
``xmake f -m debug``
|
||||
``xmake f -m release``
|
||||
|
||||
If for some reasons you wanna run the command line version (not updated):
|
||||
``xmake run text``
|
||||
|
||||
### Create a release version (xmake packaging ???)
|
||||
### Package the project
|
||||
|
||||
// TODO when the game is finished //
|
||||
|
||||
## Credits
|
||||
|
||||
|
||||
@@ -37,6 +37,8 @@ _Repeat for every avaible actions._
|
||||
|
||||
The settings file has the following format:
|
||||
|
||||
- The versions of the file format, stored with 1 byte
|
||||
- The previous maximum pieces size selected, stored with 1 byte
|
||||
- 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
|
||||
@@ -47,5 +49,11 @@ The settings file has the following format:
|
||||
- The last selected width of the board, stored with 1 byte
|
||||
- The last selected height of the board, stored with 1 byte
|
||||
- The uniformity mode (0 for default distribution, 1 for uniformous distribution, 2 for custom distribution), stored with 1 byte
|
||||
- If custom distribution is set, store the proportion of each size (15x1 byte total)
|
||||
- Every selected pieces, using 1 byte for the type of selection (once again converted from an Enum) and 1 byte for the actual value
|
||||
- For each size, store the custom proportion (from 0 to 20) (15x1 byte total)
|
||||
- Every selected pieces
|
||||
- For every groupe of piece (ALL, CONVEX, HOLELESS, OTHER), use 1 byte for the type (once again converted from an Enum) and 1 byte for the size
|
||||
- For every single piece, use 1 byte for (the size + encoding that it is a single piece),
|
||||
and 3 bytes to store the number of the piece (allows number up to 16M, size 15 has 6M pieces).
|
||||
|
||||
The current file format version is 11.
|
||||
If the file starts with a number lower than that, it will be regenerated upon launching the game.
|
||||
|
||||
@@ -106,3 +106,17 @@ Grade is an alternate system to line clears.
|
||||
|
||||
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.
|
||||
|
||||
## Pieces distribution
|
||||
|
||||
A bag is an object which contains a set of pieces.
|
||||
The principle of a bag generator is to put every available pieces into a bag and take them out one by one until the bag is empty, to then start with a new full bag.
|
||||
It's a way to have equal distribution of pieces while still allowing for a random order of pieces.
|
||||
|
||||
The game has 3 modes of pieces distribution:
|
||||
|
||||
1. Default. The simplest of them, all selected pieces are put in a single bag.
|
||||
2. Uniformous. The pieces are now separated by size and put in different bags. Each size will now appear as often as any other.
|
||||
3. Custom. The pieces are once again separated by size, but now the player specifies how likely each size is to appear.
|
||||
|
||||
Both system 2 and 3 uses a system analogous to a bag to determine which size of piece to take next.
|
||||
|
||||
@@ -7,26 +7,54 @@
|
||||
#include <utility>
|
||||
#include <cstdlib>
|
||||
|
||||
static const double SMALLEST_CONSIDERED_PROPORTION = 0.01; // the smallest a proportion can get before it is considered equal to 0
|
||||
|
||||
|
||||
Bag::Bag(const std::shared_ptr<PiecesList>& piecesList) :
|
||||
piecesList(piecesList) {
|
||||
|
||||
this->currentBag = this->piecesList->getSelectedPieces();
|
||||
this->nextBag.clear();
|
||||
|
||||
this->highestSize = this->piecesList->getHighestLoadedSize();
|
||||
this->selectedPieces = this->piecesList->getSelectedPieces();
|
||||
this->distributionMode = this->piecesList->getDistributionMode();
|
||||
this->propotionsPerSize = this->piecesList->getProportionsPerSize();
|
||||
|
||||
this->currentBags.clear();
|
||||
this->nextBags.clear();
|
||||
this->sizesBag.clear();
|
||||
this->sizesProgression.clear();
|
||||
|
||||
if (this->distributionMode == DEFAULT) {
|
||||
this->currentBags.push_back(this->selectedPieces);
|
||||
this->nextBags.push_back({});
|
||||
}
|
||||
else {
|
||||
for (int i = 0; i <= this->highestSize; i++) {
|
||||
this->currentBags.push_back(PieceBag());
|
||||
this->nextBags.push_back(PieceBag());
|
||||
this->sizesProgression.push_back(0);
|
||||
}
|
||||
|
||||
for (const auto& piece : this->selectedPieces) {
|
||||
int pieceSize = this->piecesList->lookAtPiece(piece).getPositions().size();
|
||||
this->currentBags.at(pieceSize).push_back(piece);
|
||||
}
|
||||
}
|
||||
|
||||
this->prepareNext();
|
||||
}
|
||||
|
||||
void Bag::jumpToNextBag() {
|
||||
if (this->currentBag.size() < this->nextBag.size()) {
|
||||
std::swap(this->currentBag, this->nextBag);
|
||||
for (int i = 0; i < this->currentBags.size(); i++) {
|
||||
if (this->currentBags.at(i).size() < this->nextBags.at(i).size()) {
|
||||
std::swap(this->currentBags.at(i), this->nextBags.at(i));
|
||||
}
|
||||
|
||||
for (const auto& piece : this->nextBags.at(i)) {
|
||||
this->currentBags.at(i).push_back(piece);
|
||||
}
|
||||
this->nextBags.at(i).clear();
|
||||
}
|
||||
|
||||
for (const std::pair<int, int>& pieceIndex : this->nextBag) {
|
||||
this->currentBag.push_back(pieceIndex);
|
||||
}
|
||||
this->nextBag.clear();
|
||||
|
||||
this->prepareNext();
|
||||
}
|
||||
|
||||
@@ -43,13 +71,40 @@ Piece Bag::getNext() {
|
||||
}
|
||||
|
||||
void Bag::prepareNext() {
|
||||
if (this->currentBag.empty()) {
|
||||
std::swap(this->currentBag, this->nextBag);
|
||||
if (this->distributionMode == DEFAULT) {
|
||||
this->getNextPieceFromBag(0);
|
||||
}
|
||||
else {
|
||||
if (this->sizesBag.empty()) {
|
||||
for (int i = 0; i <= this->highestSize; i++) {
|
||||
if (this->propotionsPerSize.at(i) >= SMALLEST_CONSIDERED_PROPORTION
|
||||
&& !(this->currentBags.at(i).empty() && this->nextBags.at(i).empty())) {
|
||||
while (this->sizesProgression.at(i) < 1) {
|
||||
this->sizesBag.push_back(i);
|
||||
this->sizesProgression.at(i) += this->propotionsPerSize.at(i);
|
||||
}
|
||||
|
||||
this->sizesProgression.at(i) -= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int nextSizeIndex = std::rand() % this->sizesBag.size();
|
||||
int nextSize = this->sizesBag.at(nextSizeIndex);
|
||||
this->sizesBag.erase(this->sizesBag.begin() + nextSizeIndex);
|
||||
|
||||
this->getNextPieceFromBag(nextSize);
|
||||
}
|
||||
}
|
||||
|
||||
void Bag::getNextPieceFromBag(int bagIndex) {
|
||||
if (this->currentBags.at(bagIndex).empty()) {
|
||||
std::swap(this->currentBags.at(bagIndex), this->nextBags.at(bagIndex));
|
||||
}
|
||||
|
||||
int indexIndex = std::rand() % this->currentBag.size();
|
||||
this->next = this->currentBag.at(indexIndex);
|
||||
int indexIndex = std::rand() % this->currentBags.at(bagIndex).size();
|
||||
this->next = this->currentBags.at(bagIndex).at(indexIndex);
|
||||
|
||||
this->nextBag.push_back(this->next);
|
||||
this->currentBag.erase(this->currentBag.begin() + indexIndex);
|
||||
this->nextBags.at(bagIndex).push_back(this->next);
|
||||
this->currentBags.at(bagIndex).erase(this->currentBags.at(bagIndex).begin() + indexIndex);
|
||||
}
|
||||
|
||||
@@ -2,11 +2,14 @@
|
||||
|
||||
#include "../Pieces/Piece.h"
|
||||
#include "PiecesList.h"
|
||||
#include "DistributionMode.h"
|
||||
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
|
||||
using PieceBag = std::vector<std::pair<int, int>>;
|
||||
|
||||
|
||||
/**
|
||||
* A litteral bag of pieces, in which you take each of its piece randomly one by one then start again with a new bag
|
||||
@@ -14,10 +17,15 @@
|
||||
class Bag {
|
||||
private:
|
||||
std::shared_ptr<PiecesList> piecesList; // the list of loaded pieces
|
||||
int highestSize; // the highest size of piece in the bag
|
||||
std::vector<std::pair<int, int>> selectedPieces; // the list of pieces that can be given to the player
|
||||
DistributionMode distributionMode; // the distribution mode
|
||||
std::vector<double> propotionsPerSize; // the proportion of pieces for each size
|
||||
std::pair<int, int> next; // the next piece to give
|
||||
std::vector<std::pair<int, int>> currentBag; // the list of pieces that are still to be taken out before starting a new bag
|
||||
std::vector<std::pair<int, int>> nextBag; // the list of pieces that have been taken out of the current bag and have been placed in the next
|
||||
std::vector<PieceBag> currentBags; // for each size, the list of pieces that are still to be taken out before starting a new bag
|
||||
std::vector<PieceBag> nextBags; // for each size, the list of pieces that have been taken out of the current bag and have been placed in the next
|
||||
std::vector<int> sizesBag; // the list each of bags that are still to have a piece taken out of them
|
||||
std::vector<double> sizesProgression; // how close each size is to meet its quota of pieces
|
||||
|
||||
public:
|
||||
/**
|
||||
@@ -26,7 +34,7 @@ class Bag {
|
||||
Bag(const std::shared_ptr<PiecesList>& piecesList);
|
||||
|
||||
/**
|
||||
* Ignores the remaining pieces in the current bag and startd fresh from a new bag
|
||||
* Ignores the remaining pieces in the current bag and start fresh from a new bag
|
||||
*/
|
||||
void jumpToNextBag();
|
||||
|
||||
@@ -44,7 +52,12 @@ class Bag {
|
||||
|
||||
private:
|
||||
/**
|
||||
* Prepare the next picked piece in advance
|
||||
* Prepares the next picked piece in advance
|
||||
*/
|
||||
void prepareNext();
|
||||
|
||||
/**
|
||||
* Gets the next picked piece from the specified bag
|
||||
*/
|
||||
void getNextPieceFromBag(int bagIndex);
|
||||
};
|
||||
|
||||
27
src/Core/DistributionMode.h
Normal file
27
src/Core/DistributionMode.h
Normal file
@@ -0,0 +1,27 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
|
||||
/**
|
||||
* The modes of pieces distribution managed by the app
|
||||
*/
|
||||
enum DistributionMode {
|
||||
DEFAULT,
|
||||
UNIFORM,
|
||||
CUSTOM
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @return A string containing the name of the given distribution mode
|
||||
*/
|
||||
inline std::string getPiecesDistributionName(DistributionMode distributionMode) {
|
||||
static const std::string DISTRIBUTION_NAMES[] = {
|
||||
"DEFAULT",
|
||||
"UNIFORM",
|
||||
"CUSTOM"
|
||||
};
|
||||
|
||||
return DISTRIBUTION_NAMES[distributionMode];
|
||||
}
|
||||
@@ -67,27 +67,29 @@ void Game::nextFrame(const std::set<Action>& playerActions) {
|
||||
if (AREJustEnded) {
|
||||
this->lost = this->board.spawnNextPiece();
|
||||
this->resetPiece(true);
|
||||
}
|
||||
|
||||
/* IRS and IHS */
|
||||
Rotation initialRotation = NONE
|
||||
+ ((this->initialActions.contains(ROTATE_CW)) ? CLOCKWISE : NONE)
|
||||
+ ((this->initialActions.contains(ROTATE_180)) ? DOUBLE : NONE)
|
||||
+ ((this->initialActions.contains(ROTATE_CCW)) ? COUNTERCLOCKWISE : NONE);
|
||||
/* IRS and IHS */
|
||||
bool initialRotated = (this->initialActions.contains(ROTATE_0) || this->initialActions.contains(ROTATE_CW)
|
||||
|| this->initialActions.contains(ROTATE_180) || this->initialActions.contains(ROTATE_CCW));
|
||||
Rotation initialRotation = NONE
|
||||
+ ((this->initialActions.contains(ROTATE_CW)) ? CLOCKWISE : NONE)
|
||||
+ ((this->initialActions.contains(ROTATE_180)) ? DOUBLE : NONE)
|
||||
+ ((this->initialActions.contains(ROTATE_CCW)) ? COUNTERCLOCKWISE : NONE);
|
||||
|
||||
if (this->initialActions.contains(HOLD)) {
|
||||
this->lost = (!this->board.hold(initialRotation));
|
||||
}
|
||||
else {
|
||||
if ((initialRotation != NONE) || this->initialActions.contains(ROTATE_0)) {
|
||||
this->lost = (!this->board.rotate(initialRotation));
|
||||
if (this->initialActions.contains(HOLD)) {
|
||||
this->board.hold(initialRotation);
|
||||
}
|
||||
else {
|
||||
if (initialRotated) {
|
||||
this->board.rotate(initialRotation);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (this->lost) {
|
||||
if (initialRotation == NONE) {
|
||||
this->lost = this->board.activePieceInWall();
|
||||
if (this->lost) {
|
||||
this->board.rotate(NONE);
|
||||
this->lost = this->board.activePieceInWall();
|
||||
|
||||
if (this->lost) {
|
||||
this->framesPassed++;
|
||||
return;
|
||||
@@ -97,7 +99,7 @@ void Game::nextFrame(const std::set<Action>& playerActions) {
|
||||
|
||||
/* HOLD */
|
||||
if (playerActions.contains(HOLD) && (!this->heldActions.contains(HOLD))) {
|
||||
if (this->board.hold()) {
|
||||
if (this->board.hold({})) {
|
||||
this->resetPiece(false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,8 @@
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
#include <cstdlib>
|
||||
#include <iostream>
|
||||
#include <optional>
|
||||
|
||||
|
||||
GameBoard::GameBoard(int boardWidth, int boardHeight, const std::shared_ptr<PiecesList>& piecesList, int nextQueueLength) :
|
||||
@@ -193,12 +195,12 @@ bool GameBoard::activePieceOverlaps(const std::set<Position>& safePositions, con
|
||||
return false;
|
||||
}
|
||||
|
||||
bool GameBoard::hold(Rotation initialRotation) {
|
||||
bool GameBoard::hold(std::optional<Rotation> initialRotation) {
|
||||
Position storedPosition = this->activePiecePosition;
|
||||
std::swap(this->activePiece, this->heldPiece);
|
||||
|
||||
bool isFirstTimeHolding = (this->activePiece == nullptr);
|
||||
if (isFirstTimeHolding) {
|
||||
// try with the next piece in queue since there is no piece in the hold box yet
|
||||
if (this->nextQueueLength == 0) {
|
||||
this->activePiece = std::make_shared<Piece>(this->generator.lookNext());
|
||||
}
|
||||
@@ -207,21 +209,25 @@ bool GameBoard::hold(Rotation initialRotation) {
|
||||
}
|
||||
}
|
||||
|
||||
Piece stored = *this->activePiece;
|
||||
Position storedPosition = this->activePiecePosition;
|
||||
// try initial rotation
|
||||
this->goToSpawnPosition();
|
||||
this->rotate(initialRotation);
|
||||
if (initialRotation.has_value()) {
|
||||
std::shared_ptr<Piece> storedPiece(this->activePiece);
|
||||
this->rotate(initialRotation.value());
|
||||
|
||||
// if the piece can't spawn, abort initial rotation
|
||||
if (this->activePieceInWall()) {
|
||||
this->activePiece = storedPiece;
|
||||
}
|
||||
}
|
||||
|
||||
// if the piece can't spawn, try 0° rotation
|
||||
if (this->activePieceInWall()) {
|
||||
this->activePiece = std::make_shared<Piece>(stored);
|
||||
this->goToSpawnPosition();
|
||||
std::shared_ptr<Piece> storedPiece(this->activePiece);
|
||||
this->rotate(NONE);
|
||||
|
||||
// if the piece still can't spawn, abort holding
|
||||
if (this->activePieceInWall()) {
|
||||
if (isFirstTimeHolding) {
|
||||
this->activePiece = nullptr;
|
||||
}
|
||||
this->activePiece = (isFirstTimeHolding) ? nullptr : storedPiece;
|
||||
std::swap(this->activePiece, this->heldPiece);
|
||||
this->activePiecePosition = storedPosition;
|
||||
return false;
|
||||
@@ -229,7 +235,6 @@ bool GameBoard::hold(Rotation initialRotation) {
|
||||
}
|
||||
|
||||
if (isFirstTimeHolding) {
|
||||
// confirm we keep the piece we tried with
|
||||
this->nextQueue.push_back(this->generator.getNext());
|
||||
this->nextQueue.erase(this->nextQueue.begin());
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
|
||||
|
||||
/**
|
||||
@@ -90,7 +91,7 @@ class GameBoard {
|
||||
* 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
|
||||
* @return If it suceeded
|
||||
*/
|
||||
bool hold(Rotation initialRotation = NONE);
|
||||
bool hold(std::optional<Rotation> initialRotation);
|
||||
|
||||
/**
|
||||
* Spawns the next piece from the queue
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
GameParameters::GameParameters(Gamemode gamemode, const Player& controls) :
|
||||
gamemode(gamemode),
|
||||
controls(controls) {
|
||||
|
||||
|
||||
this->reset();
|
||||
}
|
||||
|
||||
|
||||
@@ -15,7 +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 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 +39,7 @@ inline std::string getGamemodeGoal(Gamemode gamemode) {
|
||||
"200 lines",
|
||||
"2 minutes",
|
||||
"200 lines",
|
||||
"Chill"
|
||||
"Infinite"
|
||||
};
|
||||
|
||||
return GAMEMODE_DESCRIPTIONS[gamemode];
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
#include "../Pieces/Piece.h"
|
||||
#include "../Pieces/PiecesFiles.h"
|
||||
#include "DistributionMode.h"
|
||||
|
||||
#include <vector>
|
||||
#include <utility>
|
||||
@@ -11,6 +12,10 @@ PiecesList::PiecesList() {
|
||||
this->highestLoadedSize = 0;
|
||||
this->selectedPieces.clear();
|
||||
|
||||
this->distributionMode = DEFAULT;
|
||||
this->proportionsPerSize.clear();
|
||||
this->customProportionsPerSize.clear();
|
||||
|
||||
// we need to have something at index 0 even if there is no pieces of size 0
|
||||
this->loadedPieces.clear();
|
||||
this->convexPieces.clear();
|
||||
@@ -24,6 +29,7 @@ PiecesList::PiecesList() {
|
||||
|
||||
bool PiecesList::loadPieces(int size) {
|
||||
if (size < 1) return false;
|
||||
if (size <= this->highestLoadedSize) return true;
|
||||
|
||||
PiecesFiles piecesFiles;
|
||||
for (int i = this->highestLoadedSize + 1; i <= size; i++) {
|
||||
@@ -87,6 +93,14 @@ void PiecesList::unselectAll() {
|
||||
this->selectedPieces.clear();
|
||||
}
|
||||
|
||||
bool PiecesList::setDistributionMode(DistributionMode distributionMode) {
|
||||
if (distributionMode == DEFAULT || distributionMode == UNIFORM || distributionMode == CUSTOM) {
|
||||
this->distributionMode = distributionMode;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
int PiecesList::getHighestLoadedSize() const {
|
||||
return this->highestLoadedSize;
|
||||
}
|
||||
@@ -101,13 +115,42 @@ std::vector<std::pair<int, int>> PiecesList::getSelectedPieces() const {
|
||||
return this->selectedPieces;
|
||||
}
|
||||
|
||||
DistributionMode PiecesList::getDistributionMode() const {
|
||||
return this->distributionMode;
|
||||
}
|
||||
|
||||
bool PiecesList::changeCustomDistribution(int size, double distribution) {
|
||||
if (size < 1 || size > this->highestLoadedSize || distribution > 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
this->customProportionsPerSize.at(size) = distribution;
|
||||
return true;
|
||||
}
|
||||
|
||||
std::vector<double> PiecesList::getProportionsPerSize() const {
|
||||
if (this->distributionMode == CUSTOM) {
|
||||
return this->customProportionsPerSize;
|
||||
}
|
||||
else {
|
||||
return this->proportionsPerSize;
|
||||
}
|
||||
}
|
||||
|
||||
Piece PiecesList::getPiece(const std::pair<int, int>& pieceIndex) const {
|
||||
return this->loadedPieces.at(pieceIndex.first).at(pieceIndex.second);
|
||||
}
|
||||
|
||||
const Piece& PiecesList::lookAtPiece(const std::pair<int, int>& pieceIndex) const {
|
||||
return this->loadedPieces.at(pieceIndex.first).at(pieceIndex.second);
|
||||
}
|
||||
|
||||
void PiecesList::pushBackEmptyVectors() {
|
||||
this->loadedPieces.push_back(std::vector<Piece>());
|
||||
this->convexPieces.push_back(std::vector<int>());
|
||||
this->holelessPieces.push_back(std::vector<int>());
|
||||
this->otherPieces.push_back(std::vector<int>());
|
||||
|
||||
this->proportionsPerSize.push_back(1);
|
||||
this->customProportionsPerSize.push_back(1);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "../Pieces/Piece.h"
|
||||
#include "DistributionMode.h"
|
||||
|
||||
#include <vector>
|
||||
#include <utility>
|
||||
@@ -18,6 +19,9 @@ class PiecesList {
|
||||
std::vector<std::vector<int>> holelessPieces; // the list of holeless loaded pieces by size
|
||||
std::vector<std::vector<int>> otherPieces; // the list of other loaded pieces by size
|
||||
std::vector<std::pair<int, int>> selectedPieces; // the list of all currently selected pieces
|
||||
DistributionMode distributionMode; // the current pieces distribution mode
|
||||
std::vector<double> proportionsPerSize; // the proportion of piece for each sizes
|
||||
std::vector<double> customProportionsPerSize; // the proportion of piece for each sizes when the distribution mode is set to custom
|
||||
|
||||
public:
|
||||
/**
|
||||
@@ -66,6 +70,19 @@ class PiecesList {
|
||||
*/
|
||||
void unselectAll();
|
||||
|
||||
/**
|
||||
* Changes the current pieces distribution mode
|
||||
* @return If the mode is supported
|
||||
*/
|
||||
bool setDistributionMode(DistributionMode distributionMode);
|
||||
|
||||
/**
|
||||
* Changes the distribution of the specified size for when the distribution mode is set to custom,
|
||||
* the specified distribution must lower or equal to 1
|
||||
* @return If the new distribution was applied
|
||||
*/
|
||||
bool changeCustomDistribution(int size, double distribution);
|
||||
|
||||
/**
|
||||
* @return The highest loaded size of pieces
|
||||
*/
|
||||
@@ -81,10 +98,25 @@ class PiecesList {
|
||||
*/
|
||||
std::vector<std::pair<int, int>> getSelectedPieces() const;
|
||||
|
||||
/**
|
||||
* @return The current distribution mode
|
||||
*/
|
||||
DistributionMode getDistributionMode() const;
|
||||
|
||||
/**
|
||||
* @return The proportion of pieces for each loaded size
|
||||
*/
|
||||
std::vector<double> getProportionsPerSize() const;
|
||||
|
||||
/**
|
||||
* @return A copy of the piece corresponding to the specified index
|
||||
*/
|
||||
Piece getPiece(const std::pair<int, int>& pieceIndex) const;
|
||||
|
||||
/**
|
||||
* @return The piece corresponding to the specified index
|
||||
*/
|
||||
const Piece& lookAtPiece(const std::pair<int, int>& pieceIndex) const;
|
||||
|
||||
private:
|
||||
/**
|
||||
|
||||
@@ -57,6 +57,33 @@ class AppMenu {
|
||||
}
|
||||
}
|
||||
|
||||
sf::Text createText(int fontSize = 2) const {
|
||||
sf::Text newText(this->pressStartFont, "", this->settings->getWindowSizeMultiplier() * fontSize);
|
||||
newText.setFillColor(sf::Color::Black);
|
||||
newText.setOutlineColor(sf::Color::White);
|
||||
newText.setOutlineThickness(0);
|
||||
|
||||
return newText;
|
||||
}
|
||||
|
||||
void setTextPosition(sf::Text& text, float xPos, float yPos) const {
|
||||
float sizeMultiplier = this->settings->getWindowSizeMultiplier();
|
||||
|
||||
text.setOrigin(sf::Vector2f({0, text.getLocalBounds().size.y / 2}));
|
||||
text.setPosition(sf::Vector2f({sizeMultiplier * xPos, sizeMultiplier * yPos}));
|
||||
}
|
||||
|
||||
void setTitlePosition(sf::Text& text, float yPos) const {
|
||||
float sizeMultiplier = this->settings->getWindowSizeMultiplier();
|
||||
|
||||
text.setOrigin({text.getLocalBounds().getCenter().x, text.getLocalBounds().size.y / 2});
|
||||
text.setPosition(sf::Vector2f({sizeMultiplier * 40.f, sizeMultiplier * yPos}));
|
||||
}
|
||||
|
||||
void setTextOutline(sf::Text& text, bool hasOutline) const {
|
||||
text.setOutlineThickness(hasOutline * (this->settings->getWindowSizeMultiplier() / 2));
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
@@ -80,4 +107,11 @@ class AppMenu {
|
||||
text.setPosition(sf::Vector2f({sizeMultiplier * 40.f, sizeMultiplier * yPos}));
|
||||
this->renderWindow->draw(text);
|
||||
}
|
||||
|
||||
sf::Color 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));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
|
||||
#include <stack>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <SFML/Graphics.hpp>
|
||||
|
||||
|
||||
@@ -58,8 +57,6 @@ void GameBoardAppMenu::drawFrame() const {
|
||||
|
||||
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});
|
||||
|
||||
|
||||
85
src/GraphicalUI/AppMenus/GameDistributionAppMenu.cpp
Normal file
85
src/GraphicalUI/AppMenus/GameDistributionAppMenu.cpp
Normal file
@@ -0,0 +1,85 @@
|
||||
#include "GameDistributionAppMenu.h"
|
||||
|
||||
#include "AppMenu.h"
|
||||
#include "../PlayerCursor.h"
|
||||
|
||||
#include <stack>
|
||||
#include <memory>
|
||||
#include <SFML/Graphics.hpp>
|
||||
|
||||
|
||||
GameDistributionAppMenu::GameDistributionAppMenu(std::shared_ptr<MenuStack> menuStack, std::shared_ptr<Settings> settings, std::shared_ptr<sf::RenderWindow> renderWindow) :
|
||||
AppMenu(menuStack, settings, renderWindow),
|
||||
playerCursor({1}) {
|
||||
|
||||
for (int i = 1; i <= this->settings->getMaximumPiecesSize(); i++) {
|
||||
this->playerCursor.addRow(i, 1);
|
||||
}
|
||||
}
|
||||
|
||||
void GameDistributionAppMenu::computeFrame() {
|
||||
this->updateMetaBinds();
|
||||
this->playerCursor.updatePosition();
|
||||
|
||||
PiecesList& piecesList = this->settings->getMenu().getPiecesList();
|
||||
|
||||
if (this->playerCursor.getPosition().y == 0) {
|
||||
if (this->playerCursor.movedLeft()) {
|
||||
piecesList.setDistributionMode(DistributionMode((int) piecesList.getDistributionMode() - 1));
|
||||
}
|
||||
if (this->playerCursor.movedRight()) {
|
||||
piecesList.setDistributionMode(DistributionMode((int) piecesList.getDistributionMode() + 1));
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (piecesList.getDistributionMode() != CUSTOM) {
|
||||
this->playerCursor.goToPosition({0, 0});
|
||||
}
|
||||
else {
|
||||
if (this->playerCursor.movedLeft()) {
|
||||
this->settings->decreaseDistribution(this->playerCursor.getPosition().y);
|
||||
}
|
||||
if (this->playerCursor.movedRight()) {
|
||||
this->settings->increaseDistribution(this->playerCursor.getPosition().y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (this->escReleased) {
|
||||
this->settings->confirmDistribution();
|
||||
this->menuStack->pop();
|
||||
}
|
||||
}
|
||||
|
||||
void GameDistributionAppMenu::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, {}, "DISTRIBUTION SETTINGS", 5.f, {});
|
||||
|
||||
const DistributionMode distributionMode = this->settings->getMenu().readPiecesList().getDistributionMode();
|
||||
const std::vector<int>& distributions = this->settings->getDistributions();
|
||||
|
||||
int firstElem = std::clamp(((int) this->playerCursor.getPosition().y) - 1, 0, this->settings->getMaximumPiecesSize() - 3);
|
||||
if (firstElem == 0) {
|
||||
this->placeText(text, this->playerCursor, "< DISTRIBUTION MODE: " + getPiecesDistributionName(distributionMode) + " >", 5.f, 15.f, sf::Vector2u{0, 0});
|
||||
}
|
||||
else {
|
||||
this->placeText(text, this->playerCursor, "< SIZE " + std::to_string(firstElem) + " PROBABILITY: " + std::to_string(distributions.at(firstElem)) + " >", 5.f, 15.f, sf::Vector2u{(unsigned int) firstElem, 0});
|
||||
}
|
||||
|
||||
if (distributionMode != CUSTOM) {
|
||||
text.setFillColor(sf::Color(100, 100, 100));
|
||||
}
|
||||
|
||||
this->placeText(text, this->playerCursor, "< SIZE " + std::to_string(firstElem + 1) + " PROBABILITY: " + std::to_string(distributions.at(firstElem + 1)) + " >", 5.f, 25.f, sf::Vector2u{0, (unsigned int) firstElem + 1});
|
||||
this->placeText(text, this->playerCursor, "< SIZE " + std::to_string(firstElem + 2) + " PROBABILITY: " + std::to_string(distributions.at(firstElem + 2)) + " >", 5.f, 35.f, sf::Vector2u{0, (unsigned int) firstElem + 2});
|
||||
this->placeText(text, this->playerCursor, "< SIZE " + std::to_string(firstElem + 3) + " PROBABILITY: " + std::to_string(distributions.at(firstElem + 3)) + " >", 5.f, 45.f, sf::Vector2u{0, (unsigned int) firstElem + 3});
|
||||
|
||||
this->renderWindow->display();
|
||||
}
|
||||
21
src/GraphicalUI/AppMenus/GameDistributionAppMenu.h
Normal file
21
src/GraphicalUI/AppMenus/GameDistributionAppMenu.h
Normal file
@@ -0,0 +1,21 @@
|
||||
#pragma once
|
||||
|
||||
#include "AppMenu.h"
|
||||
#include "../PlayerCursor.h"
|
||||
|
||||
#include <stack>
|
||||
#include <memory>
|
||||
#include <SFML/Graphics.hpp>
|
||||
|
||||
|
||||
class GameDistributionAppMenu : public AppMenu {
|
||||
private:
|
||||
PlayerCursor playerCursor;
|
||||
|
||||
public:
|
||||
GameDistributionAppMenu(std::shared_ptr<MenuStack> menuStack, std::shared_ptr<Settings> settings, std::shared_ptr<sf::RenderWindow> renderWindow);
|
||||
|
||||
void computeFrame() override;
|
||||
|
||||
void drawFrame() const override;
|
||||
};
|
||||
234
src/GraphicalUI/AppMenus/GamePiecesAppMenu.cpp
Normal file
234
src/GraphicalUI/AppMenus/GamePiecesAppMenu.cpp
Normal file
@@ -0,0 +1,234 @@
|
||||
#include "GamePiecesAppMenu.h"
|
||||
|
||||
#include "AppMenu.h"
|
||||
#include "GameDistributionAppMenu.h"
|
||||
#include "../PlayerCursor.h"
|
||||
|
||||
#include <stack>
|
||||
#include <memory>
|
||||
#include <SFML/Graphics.hpp>
|
||||
|
||||
|
||||
GamePiecesAppMenu::GamePiecesAppMenu(std::shared_ptr<MenuStack> menuStack, std::shared_ptr<Settings> settings, std::shared_ptr<sf::RenderWindow> renderWindow) :
|
||||
AppMenu(menuStack, settings, renderWindow),
|
||||
playerCursor({1, (unsigned int) this->settings->getSelectedPieces().size() + 1u}) {
|
||||
|
||||
for (int i = 1; i <= this->settings->getMaximumPiecesSize(); i++) {
|
||||
this->playerCursor.addRow(i + 1, this->settings->getMenu().readPiecesList().getNumberOfPieces(i) + 4);
|
||||
}
|
||||
|
||||
if (this->settings->getMaximumPiecesSize() < MAXIMUM_PIECES_SIZE) {
|
||||
this->playerCursor.addRow(this->settings->getMaximumPiecesSize() + 2, 1);
|
||||
}
|
||||
}
|
||||
|
||||
void GamePiecesAppMenu::computeFrame() {
|
||||
this->updateMetaBinds();
|
||||
this->playerCursor.updatePosition();
|
||||
|
||||
if (this->playerCursor.movedDown() && this->playerCursor.getPosition().y == 2) {
|
||||
this->playerCursor.goToPosition({0, 2});
|
||||
}
|
||||
|
||||
if (this->enterReleased) {
|
||||
if (this->playerCursor.getPosition().y == 0) {
|
||||
this->menuStack->push(std::make_shared<GameDistributionAppMenu>(this->menuStack, this->settings, this->renderWindow));
|
||||
}
|
||||
|
||||
if (this->playerCursor.getPosition().y == (this->settings->getMaximumPiecesSize() + 2)) {
|
||||
int newMaxSize = this->settings->getMaximumPiecesSize() + 1;
|
||||
this->settings->loadPieces(newMaxSize);
|
||||
|
||||
this->playerCursor.removeRow(newMaxSize + 1);
|
||||
this->playerCursor.addRow(newMaxSize + 1, this->settings->getMenu().readPiecesList().getNumberOfPieces(newMaxSize) + 4);
|
||||
this->playerCursor.goToPosition({0u, newMaxSize + 1u});
|
||||
|
||||
if (newMaxSize < MAXIMUM_PIECES_SIZE) {
|
||||
this->playerCursor.addRow(newMaxSize + 2, 1);
|
||||
}
|
||||
}
|
||||
else if (this->playerCursor.getPosition().y > 1) {
|
||||
if (this->playerCursor.getPosition().x >= 4) {
|
||||
this->settings->selectPieces(createSinglePieceType(this->playerCursor.getPosition().y - 1), this->playerCursor.getPosition().x - 4);
|
||||
}
|
||||
else {
|
||||
switch (this->playerCursor.getPosition().x) {
|
||||
case 0 : {this->settings->selectPieces(ALL_PIECES, this->playerCursor.getPosition().y - 1); break;}
|
||||
case 1 : {this->settings->selectPieces(CONVEX_PIECES, this->playerCursor.getPosition().y - 1); break;}
|
||||
case 2 : {this->settings->selectPieces(HOLELESS_PIECES, this->playerCursor.getPosition().y - 1); break;}
|
||||
case 3 : {this->settings->selectPieces(OTHER_PIECES, this->playerCursor.getPosition().y - 1); break;}
|
||||
}
|
||||
}
|
||||
this->playerCursor.addPosition(0, 1);
|
||||
}
|
||||
}
|
||||
if (this->escReleased) {
|
||||
if (this->playerCursor.getPosition().y == 1) {
|
||||
if (this->playerCursor.getPosition().x > 0) {
|
||||
this->settings->unselectPieces(this->playerCursor.getPosition().x - 1);
|
||||
this->playerCursor.removePosition(this->playerCursor.getPosition().x - 1, 1);
|
||||
}
|
||||
}
|
||||
else {
|
||||
this->settings->confirmSelectedPieces();
|
||||
this->menuStack->pop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GamePiecesAppMenu::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, {}, "PIECES SELECT", 5.f, {});
|
||||
|
||||
if (this->playerCursor.getPosition().y == 0) {
|
||||
this->placeText(text, this->playerCursor, "PIECES DISTRIBUTION", 5.f, 15.f, sf::Vector2u{0, 0});
|
||||
this->drawSelectedPiecesRow(25.f);
|
||||
this->drawRow(1, 35.f, true);
|
||||
this->drawRow(2, 45.f, true);
|
||||
}
|
||||
else {
|
||||
this->drawSelectedPiecesRow(15.f);
|
||||
|
||||
bool drawFromFirstElem = (this->playerCursor.getPosition().y == 1);
|
||||
bool addExtraLine = (this->settings->getMaximumPiecesSize() < MAXIMUM_PIECES_SIZE);
|
||||
int firstElem = std::clamp(((int) this->playerCursor.getPosition().y) - 2, 1, this->settings->getMaximumPiecesSize() - 2 + (addExtraLine ? 1 : 0));
|
||||
this->drawRow(firstElem, 25.f, drawFromFirstElem);
|
||||
this->drawRow(firstElem + 1, 35.f, drawFromFirstElem);
|
||||
this->drawRow(firstElem + 2, 45.f, drawFromFirstElem);
|
||||
}
|
||||
|
||||
this->renderWindow->display();
|
||||
}
|
||||
|
||||
void GamePiecesAppMenu::drawSelectedPiecesRow(float yPos) const {
|
||||
sf::RectangleShape rect({(float) this->renderWindow->getSize().x, 8.f * this->settings->getWindowSizeMultiplier()});
|
||||
rect.setPosition({0.f, (yPos - 4.f) * this->settings->getWindowSizeMultiplier()});
|
||||
rect.setFillColor({240, 240, 240});
|
||||
this->renderWindow->draw(rect);
|
||||
|
||||
sf::Text text(this->pressStartFont, "", this->settings->getWindowSizeMultiplier());
|
||||
text.setFillColor({0, 0, 0});
|
||||
|
||||
int elem = (this->playerCursor.getPosition().y == 1) ? std::max(((int) this->playerCursor.getPosition().x) - 4, 0) : 0;
|
||||
float xProgress = 1.f;
|
||||
|
||||
bool first = true;
|
||||
while (true) {
|
||||
if ((this->playerCursor.getPosition().y == 1) && (elem == this->playerCursor.getPosition().x)) {
|
||||
this->placeText(text, {}, "|", xProgress, yPos, {});
|
||||
xProgress += (1.f + (text.getGlobalBounds().size.x / this->settings->getWindowSizeMultiplier()));
|
||||
}
|
||||
|
||||
if (elem >= this->settings->getSelectedPieces().size()) return;
|
||||
|
||||
const auto& [pieceType, value] = this->settings->getSelectedPieces().at(elem);
|
||||
|
||||
int pieceSize = getSizeOfPieces(pieceType);
|
||||
if (pieceSize > 0) {
|
||||
if (!(pieceSize > this->settings->getMaximumPiecesSize())) {
|
||||
const Piece& piece = this->settings->getMenu().readPiecesList().lookAtPiece({pieceSize, value});
|
||||
int cellSize = (8 * this->settings->getWindowSizeMultiplier()) / (piece.getLength());
|
||||
sf::FloatRect piecePosition(sf::Vector2f(xProgress, yPos - 4.f) * (float) this->settings->getWindowSizeMultiplier(), sf::Vector2f(8 , 8) * (float) this->settings->getWindowSizeMultiplier());
|
||||
this->drawPiece(piece, cellSize, piecePosition, false);
|
||||
xProgress += (1.f + 8.f);
|
||||
}
|
||||
else {
|
||||
this->placeText(text, {}, "UNLOADED_" + std::to_string(pieceSize), xProgress, yPos, {});
|
||||
xProgress += (1.f + (text.getGlobalBounds().size.x / this->settings->getWindowSizeMultiplier()));
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (!(value > this->settings->getMaximumPiecesSize())) {
|
||||
this->placeText(text, {}, ((first) ? "" : " ") + getPiecesTypeName(pieceType) + "_" + std::to_string(value), xProgress, yPos, {});
|
||||
xProgress += (1.f + (text.getGlobalBounds().size.x / this->settings->getWindowSizeMultiplier()));
|
||||
}
|
||||
else {
|
||||
this->placeText(text, {}, "UNLOADED_" + std::to_string(pieceSize), xProgress, yPos, {});
|
||||
xProgress += (1.f + (text.getGlobalBounds().size.x / this->settings->getWindowSizeMultiplier()));
|
||||
}
|
||||
}
|
||||
|
||||
elem++;
|
||||
}
|
||||
}
|
||||
|
||||
void GamePiecesAppMenu::drawRow(int piecesSize, float yPos, bool drawFromFirstElem) const {
|
||||
if (piecesSize > this->settings->getMaximumPiecesSize()) {
|
||||
sf::Text text(this->pressStartFont, "", this->settings->getWindowSizeMultiplier() * 2);
|
||||
text.setOutlineThickness(this->settings->getWindowSizeMultiplier() / 2);
|
||||
if (this->playerCursor.getPosition().y == (piecesSize + 1)) {
|
||||
text.setOutlineColor({255, 255, 255});
|
||||
}
|
||||
else {
|
||||
text.setOutlineColor({0, 0, 0});
|
||||
}
|
||||
|
||||
std::string sizeString = "LOAD SIZE " + std::to_string(piecesSize) + "? ";
|
||||
if (piecesSize <= 10) {
|
||||
text.setFillColor({0, 255, 0});
|
||||
this->placeText(text, {}, sizeString + "(LOW LOAD TIME)", 1.f, yPos, {});
|
||||
}
|
||||
else if (piecesSize <= 13) {
|
||||
text.setFillColor({255, 255, 0});
|
||||
this->placeText(text, {}, sizeString + "(MEDIUM LOAD TIME)", 1.f, yPos, {});
|
||||
}
|
||||
else {
|
||||
text.setFillColor({255, 0, 0});
|
||||
this->placeText(text, {}, sizeString + "(LONG LOAD TIME)", 1.f, yPos, {});
|
||||
}
|
||||
}
|
||||
else {
|
||||
int numberOfPieces = this->settings->getMenu().readPiecesList().getNumberOfPieces(piecesSize);
|
||||
int firstElem = (drawFromFirstElem) ? -4 : std::max(((int) this->playerCursor.getPosition().x) - 7, -4);
|
||||
|
||||
sf::Text text(this->pressStartFont, "", this->settings->getWindowSizeMultiplier());
|
||||
text.setFillColor({0, 0, 0});
|
||||
text.setOutlineColor({255, 255, 255});
|
||||
|
||||
this->placeText(text, {}, "SIZE " + std::to_string(piecesSize), 1.f, yPos, {});
|
||||
|
||||
for (int i = 0; i < 7; i++) {
|
||||
if (i + firstElem >= numberOfPieces) return;
|
||||
|
||||
if ((i + firstElem) < 0) {
|
||||
switch (i + firstElem) {
|
||||
case -4 : {this->placeText(text, this->playerCursor, "ALL", 10.f + (i * 10.f), yPos, sf::Vector2u{0, piecesSize + 1u}); break;}
|
||||
case -3 : {this->placeText(text, this->playerCursor, "CONVEX", 10.f + (i * 10.f), yPos, sf::Vector2u{1, piecesSize + 1u}); break;}
|
||||
case -2 : {this->placeText(text, this->playerCursor, "HOLELESS", 10.f + (i * 10.f), yPos, sf::Vector2u{2, piecesSize + 1u}); break;}
|
||||
case -1 : {this->placeText(text, this->playerCursor, "OTHER", 10.f + (i * 10.f), yPos, sf::Vector2u{3, piecesSize + 1u}); break;}
|
||||
}
|
||||
}
|
||||
else {
|
||||
const Piece& piece = this->settings->getMenu().readPiecesList().lookAtPiece({piecesSize, firstElem + i});
|
||||
int cellSize = (8 * this->settings->getWindowSizeMultiplier()) / (piece.getLength());
|
||||
sf::FloatRect piecePosition(sf::Vector2f(10.f + (i * 10.f), yPos - 4.f) * (float) this->settings->getWindowSizeMultiplier(), sf::Vector2f(8 , 8) * (float) this->settings->getWindowSizeMultiplier());
|
||||
this->drawPiece(piece, cellSize, piecePosition, this->playerCursor.getPosition() == sf::Vector2u{i + firstElem + 4u, piecesSize + 1u});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GamePiecesAppMenu::drawPiece(const Piece& piece, int cellSize, const sf::FloatRect& piecePosition, bool selected) const {
|
||||
sf::RectangleShape rect(piecePosition.size);
|
||||
rect.setPosition(piecePosition.position);
|
||||
rect.setFillColor({180, 180, 180});
|
||||
if (selected) {
|
||||
rect.setOutlineColor({0, 0, 0});
|
||||
rect.setOutlineThickness(this->settings->getWindowSizeMultiplier() / 2);
|
||||
}
|
||||
this->renderWindow->draw(rect);
|
||||
|
||||
sf::RectangleShape cell(sf::Vector2f(cellSize, cellSize));
|
||||
cell.setFillColor(this->getColorOfBlock(piece.getBlockType(), 0));
|
||||
|
||||
for (const Position& cellPosition : piece.getPositions()) {
|
||||
cell.setPosition(sf::Vector2f(piecePosition.position.x + (cellPosition.x * cellSize),
|
||||
piecePosition.position.y + ((piece.getLength() - cellPosition.y - 1) * cellSize) ));
|
||||
this->renderWindow->draw(cell);
|
||||
}
|
||||
}
|
||||
29
src/GraphicalUI/AppMenus/GamePiecesAppMenu.h
Normal file
29
src/GraphicalUI/AppMenus/GamePiecesAppMenu.h
Normal file
@@ -0,0 +1,29 @@
|
||||
#pragma once
|
||||
|
||||
#include "AppMenu.h"
|
||||
#include "../PlayerCursor.h"
|
||||
|
||||
#include <stack>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <SFML/Graphics.hpp>
|
||||
|
||||
|
||||
class GamePiecesAppMenu : public AppMenu {
|
||||
private:
|
||||
PlayerCursor playerCursor;
|
||||
|
||||
public:
|
||||
GamePiecesAppMenu(std::shared_ptr<MenuStack> menuStack, std::shared_ptr<Settings> settings, std::shared_ptr<sf::RenderWindow> renderWindow);
|
||||
|
||||
void computeFrame() override;
|
||||
|
||||
void drawFrame() const override;
|
||||
|
||||
private:
|
||||
void drawSelectedPiecesRow(float yPos) const;
|
||||
|
||||
void drawRow(int piecesSize, float yPos, bool drawFromFirstElem) const;
|
||||
|
||||
void drawPiece(const Piece& piece, int cellSize, const sf::FloatRect& pos, bool selected) const;
|
||||
};
|
||||
@@ -9,14 +9,15 @@
|
||||
#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->startTimer = TIME_BEFORE_STARTING;
|
||||
this->startTimer = this->settings->getStartTimerLength() * FRAMES_PER_SECOND;
|
||||
if (this->startTimer == 0) {
|
||||
this->game.start();
|
||||
}
|
||||
this->paused = false;
|
||||
this->pausePressed = false;
|
||||
this->retryPressed = false;
|
||||
@@ -39,7 +40,7 @@ GamePlayingAppMenu::GamePlayingAppMenu(std::shared_ptr<MenuStack> menuStack, std
|
||||
|
||||
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();
|
||||
float nextPieceCellSizeZoom = ((int) this->nextQueuePosition[0].size.y) / this->settings->getMenu().getPiecesList().lookAtPiece(piece).getLength();
|
||||
this->nextCellSizeZoom = std::min(this->nextCellSizeZoom, nextPieceCellSizeZoom);
|
||||
}
|
||||
|
||||
@@ -77,7 +78,10 @@ void GamePlayingAppMenu::computeFrame() {
|
||||
else {
|
||||
if (this->retryPressed) {
|
||||
this->game.reset();
|
||||
this->startTimer = TIME_BEFORE_STARTING;
|
||||
this->startTimer = this->settings->getStartTimerLength() * FRAMES_PER_SECOND;
|
||||
if (this->startTimer == 0) {
|
||||
this->game.start();
|
||||
}
|
||||
}
|
||||
this->retryPressed = false;
|
||||
}
|
||||
@@ -247,6 +251,7 @@ void GamePlayingAppMenu::drawFrame() const {
|
||||
// game state
|
||||
text.setOutlineColor(sf::Color(255, 255, 255));
|
||||
text.setOutlineThickness(windowSizeMultiplier / 2.f);
|
||||
text.setCharacterSize(windowSizeMultiplier * 4);
|
||||
|
||||
if (this->game.hasWon()) {
|
||||
this->placeTitle(text, {}, "WIN", 25.f, {});
|
||||
@@ -258,20 +263,12 @@ void GamePlayingAppMenu::drawFrame() const {
|
||||
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->placeTitle(text, {}, std::to_string(((this->startTimer - 1) / ((this->settings->getStartTimerLength() * FRAMES_PER_SECOND) / 4))), 25.f, {});
|
||||
}
|
||||
|
||||
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::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));
|
||||
|
||||
@@ -28,7 +28,5 @@ class GamePlayingAppMenu : public AppMenu {
|
||||
|
||||
void drawFrame() const override;
|
||||
|
||||
sf::Color getColorOfBlock(Block block, int luminosityShift) const;
|
||||
|
||||
sf::Vector2f getBoardBlockPosition(int x, int y) const;
|
||||
};
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
#include "GameSettingsAppMenu.h"
|
||||
|
||||
#include "AppMenu.h"
|
||||
#include "GamePiecesAppMenu.h"
|
||||
#include "GameBoardAppMenu.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({2, 3, 3}) {
|
||||
playerCursor({2, 3, 2}) {
|
||||
|
||||
}
|
||||
|
||||
@@ -34,7 +34,6 @@ void GameSettingsAppMenu::computeFrame() {
|
||||
switch (this->playerCursor.getPosition().x) {
|
||||
case 0 : {this->settings->setGamemode(MASTER); break;}
|
||||
case 1 : {this->settings->setGamemode(ZEN); break;}
|
||||
case 2 : break; //TODO
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -43,7 +42,7 @@ void GameSettingsAppMenu::computeFrame() {
|
||||
if (this->enterReleased) {
|
||||
if (this->playerCursor.getPosition().y == 0) {
|
||||
if (this->playerCursor.getPosition().x == 0) {
|
||||
//TODO
|
||||
this->menuStack->push(std::make_shared<GamePiecesAppMenu>(this->menuStack, this->settings, this->renderWindow));
|
||||
}
|
||||
if (this->playerCursor.getPosition().x == 1) {
|
||||
this->menuStack->push(std::make_shared<GameBoardAppMenu>(this->menuStack, this->settings, this->renderWindow));
|
||||
@@ -67,15 +66,17 @@ void GameSettingsAppMenu::drawFrame() const {
|
||||
|
||||
this->placeTitle(text, {}, "GAME SETTINGS", 5.f, {});
|
||||
|
||||
this->placeText(text, this->playerCursor, "PIECES SELECT (TODO)", 5.f, 15.f, sf::Vector2u{0, 0});
|
||||
this->placeText(text, this->playerCursor, "PIECES SELECT", 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, 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});
|
||||
text.setOutlineThickness(0);
|
||||
this->placeTitle(text, {}, "GAMEMODE SELECT", 25.f, {});
|
||||
|
||||
this->placeText(text, this->playerCursor, "SPRINT", 5.f, 35.f, sf::Vector2u{0, 1});
|
||||
this->placeText(text, this->playerCursor, "MARATHON", 25.f, 35.f, sf::Vector2u{1, 1});
|
||||
this->placeText(text, this->playerCursor, "ULTRA", 50.f, 35.f, sf::Vector2u{2, 1});
|
||||
this->placeText(text, this->playerCursor, "MASTER", 5.f, 45.f, sf::Vector2u{0, 2});
|
||||
this->placeText(text, this->playerCursor, "ZEN", 25.f, 45.f, sf::Vector2u{1, 2});
|
||||
|
||||
this->renderWindow->display();
|
||||
}
|
||||
|
||||
@@ -5,18 +5,18 @@
|
||||
|
||||
#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}),
|
||||
playerCursor({INFO_SECTIONS_COUNT}),
|
||||
sectionsName(
|
||||
"< ABOUT >",
|
||||
"< PIECES TYPES >",
|
||||
"< 0 DEGREES ROTATIONS >",
|
||||
"< ROTATION SYSTEM >",
|
||||
"< SCORING >",
|
||||
"< 0 DEGREES ROTATIONS >"
|
||||
"< SCORING >"
|
||||
),
|
||||
sectionsContent(
|
||||
"This game is written in C++,\n"
|
||||
@@ -28,6 +28,25 @@ InfoAppMenu::InfoAppMenu(std::shared_ptr<MenuStack> menuStack, std::shared_ptr<S
|
||||
"to them in any ways.\n"
|
||||
"Current version: beta.",
|
||||
|
||||
"There is multiple pieces type in\n"
|
||||
"the selection screen. Use theses\n"
|
||||
"categories for size of at least 7.\n"
|
||||
"Convex, Holeless and Others are\n"
|
||||
"all mutually exclusive.\n"
|
||||
"Others have holes inside them, and\n"
|
||||
"Convex are presumably easier to\n"
|
||||
"play with than Holeless.",
|
||||
|
||||
"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.",
|
||||
|
||||
"This game uses its own\n"
|
||||
"Rotation Sytem, called AutoRS.\n"
|
||||
"The rotation center is always the\n"
|
||||
@@ -47,25 +66,48 @@ InfoAppMenu::InfoAppMenu(std::shared_ptr<MenuStack> menuStack, std::shared_ptr<S
|
||||
"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.",
|
||||
"when the last move was a kick."
|
||||
),
|
||||
sectionNameText(this->createText()),
|
||||
sectionContentText(this->createText()),
|
||||
renderTexture(this->renderWindow->getSize()),
|
||||
sprite(this->renderTexture.getTexture()) {
|
||||
|
||||
"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."
|
||||
) {
|
||||
this->setTextOutline(this->sectionNameText, true);
|
||||
|
||||
this->sectionContentText.setLineSpacing((float) this->settings->getWindowSizeMultiplier() / 8);
|
||||
|
||||
this->sectionNameText.setString(this->sectionsName[this->playerCursor.getPosition().x]);
|
||||
this->setTitlePosition(this->sectionNameText, 10.f);
|
||||
|
||||
this->sectionContentText.setString(this->sectionsContent[this->playerCursor.getPosition().x]);
|
||||
this->setTextPosition(this->sectionContentText, 5.f, 30.f);
|
||||
|
||||
this->renderTexture.clear(sf::Color(200, 200, 200));
|
||||
this->renderTexture.draw(this->sectionNameText);
|
||||
this->renderTexture.draw(this->sectionContentText);
|
||||
this->renderTexture.display();
|
||||
this->sprite.setTexture(this->renderTexture.getTexture());
|
||||
}
|
||||
|
||||
void InfoAppMenu::computeFrame() {
|
||||
this->updateMetaBinds();
|
||||
this->playerCursor.updatePosition();
|
||||
|
||||
if (this->playerCursor.movedLeft() || this->playerCursor.movedRight()) {
|
||||
this->sectionNameText.setString(this->sectionsName[this->playerCursor.getPosition().x]);
|
||||
this->setTitlePosition(this->sectionNameText, 10.f);
|
||||
|
||||
this->sectionContentText.setString(this->sectionsContent[this->playerCursor.getPosition().x]);
|
||||
this->setTextPosition(this->sectionContentText, 5.f, 30.f);
|
||||
|
||||
this->renderTexture.clear(sf::Color(200, 200, 200));
|
||||
this->renderTexture.draw(this->sectionNameText);
|
||||
this->renderTexture.draw(this->sectionContentText);
|
||||
this->renderTexture.display();
|
||||
this->sprite.setTexture(this->renderTexture.getTexture());
|
||||
}
|
||||
|
||||
if (this->escReleased) {
|
||||
this->menuStack->pop();
|
||||
}
|
||||
@@ -74,15 +116,7 @@ void InfoAppMenu::computeFrame() {
|
||||
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->renderWindow->draw(sprite);
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
@@ -7,12 +7,18 @@
|
||||
#include <memory>
|
||||
#include <SFML/Graphics.hpp>
|
||||
|
||||
static const int INFO_SECTIONS_COUNT = 5;
|
||||
|
||||
|
||||
class InfoAppMenu : public AppMenu {
|
||||
private:
|
||||
PlayerCursor playerCursor;
|
||||
sf::String sectionsName[4];
|
||||
sf::String sectionsContent[4];
|
||||
sf::String sectionsName[INFO_SECTIONS_COUNT];
|
||||
sf::String sectionsContent[INFO_SECTIONS_COUNT];
|
||||
sf::Text sectionNameText;
|
||||
sf::Text sectionContentText;
|
||||
sf::RenderTexture renderTexture;
|
||||
sf::Sprite sprite;
|
||||
|
||||
public:
|
||||
InfoAppMenu(std::shared_ptr<MenuStack> menuStack, std::shared_ptr<Settings> settings, std::shared_ptr<sf::RenderWindow> renderWindow);
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
|
||||
#include <stack>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <SFML/Graphics.hpp>
|
||||
|
||||
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
|
||||
#include <stack>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <SFML/Graphics.hpp>
|
||||
|
||||
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
|
||||
#include <stack>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <regex>
|
||||
#include <filesystem>
|
||||
@@ -15,7 +14,7 @@
|
||||
|
||||
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}) {
|
||||
playerCursor({1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}) {
|
||||
|
||||
this->selectedAnAction = false;
|
||||
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
|
||||
#include <stack>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <SFML/Graphics.hpp>
|
||||
|
||||
|
||||
@@ -37,10 +36,10 @@ void SettingsMainAppMenu::computeFrame() {
|
||||
}
|
||||
case 3 : {
|
||||
if (this->playerCursor.movedLeft()) {
|
||||
this->settings->lowerMasterVolume();
|
||||
this->settings->shortenStartTimer();
|
||||
}
|
||||
if (this->playerCursor.movedRight()) {
|
||||
this->settings->raiseMasterVolume();
|
||||
this->settings->lengthenStartTimer();
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -73,7 +72,7 @@ void SettingsMainAppMenu::drawFrame() const {
|
||||
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->placeText(text, this->playerCursor, "< START TIMER: " + std::to_string(this->settings->getStartTimerLength()) + "s >", 5.f, 45.f, sf::Vector2u{0, 3});
|
||||
|
||||
this->renderWindow->display();
|
||||
}
|
||||
|
||||
75
src/GraphicalUI/AppMenus/StartUpAppMenu.cpp
Normal file
75
src/GraphicalUI/AppMenus/StartUpAppMenu.cpp
Normal file
@@ -0,0 +1,75 @@
|
||||
#include "StartUpAppMenu.h"
|
||||
|
||||
#include "AppMenu.h"
|
||||
#include "MainAppMenu.h"
|
||||
#include "../PlayerCursor.h"
|
||||
|
||||
#include <stack>
|
||||
#include <memory>
|
||||
#include <algorithm>
|
||||
#include <SFML/Graphics.hpp>
|
||||
|
||||
|
||||
StartUpAppMenu::StartUpAppMenu(std::shared_ptr<MenuStack> menuStack, std::shared_ptr<Settings> settings, std::shared_ptr<sf::RenderWindow> renderWindow) :
|
||||
AppMenu(menuStack, settings, renderWindow),
|
||||
playerCursor({MAXIMUM_PIECES_SIZE + 1}) {
|
||||
|
||||
this->playerCursor.goToPosition({(unsigned int) std::clamp(this->settings->getMaximumPiecesSize(), MINIMUM_PIECES_SIZE, MAXIMUM_PIECES_SIZE), 0u});
|
||||
}
|
||||
|
||||
void StartUpAppMenu::computeFrame() {
|
||||
this->updateMetaBinds();
|
||||
this->playerCursor.updatePosition();
|
||||
|
||||
if (this->playerCursor.getPosition().x < MINIMUM_PIECES_SIZE) {
|
||||
if (this->playerCursor.movedLeft()) {
|
||||
this->playerCursor.goToPosition({MAXIMUM_PIECES_SIZE, 0});
|
||||
}
|
||||
else {
|
||||
this->playerCursor.goToPosition({MINIMUM_PIECES_SIZE, 0});
|
||||
}
|
||||
}
|
||||
|
||||
if (this->enterReleased) {
|
||||
this->settings->loadSettingsFromFile(true, {this->playerCursor.getPosition().x});
|
||||
this->menuStack->pop();
|
||||
|
||||
if (this->settings->hasLoadedPieces()) {
|
||||
this->menuStack->push(std::make_shared<MainAppMenu>(this->menuStack, this->settings, this->renderWindow));
|
||||
}
|
||||
else {
|
||||
std::cout << "ERROR: COULD NOT LOAD PIECES" << std::endl;
|
||||
std::cout << "ARGUMENT WAS: " << this->playerCursor.getPosition().x << std::endl;
|
||||
}
|
||||
}
|
||||
else if (this->escReleased) {
|
||||
this->menuStack->pop();
|
||||
}
|
||||
}
|
||||
|
||||
void StartUpAppMenu::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, {}, "SELECT THE LOADED PIECES MAXIMUM SIZE", 10.f, {});
|
||||
this->placeTitle(text, this->playerCursor, "< " + std::to_string(this->playerCursor.getPosition().x) + " >", 25.f, this->playerCursor.getPosition());
|
||||
|
||||
text.setOutlineColor({0, 0, 0});
|
||||
if (this->playerCursor.getPosition().x <= 10) {
|
||||
text.setFillColor({0, 255, 0});
|
||||
this->placeTitle(text, {}, "LOW LOAD TIME", 40.f, {});
|
||||
}
|
||||
else if (this->playerCursor.getPosition().x <= 13) {
|
||||
text.setFillColor({255, 255, 0});
|
||||
this->placeTitle(text, {}, "MEDIUM LOAD TIME", 40.f, {});
|
||||
}
|
||||
else {
|
||||
text.setFillColor({255, 0, 0});
|
||||
this->placeTitle(text, {}, "LONG LOAD TIME", 40.f, {});
|
||||
}
|
||||
|
||||
this->renderWindow->display();
|
||||
}
|
||||
21
src/GraphicalUI/AppMenus/StartUpAppMenu.h
Normal file
21
src/GraphicalUI/AppMenus/StartUpAppMenu.h
Normal file
@@ -0,0 +1,21 @@
|
||||
#pragma once
|
||||
|
||||
#include "AppMenu.h"
|
||||
#include "../PlayerCursor.h"
|
||||
|
||||
#include <stack>
|
||||
#include <memory>
|
||||
#include <SFML/Graphics.hpp>
|
||||
|
||||
|
||||
class StartUpAppMenu : public AppMenu {
|
||||
private:
|
||||
PlayerCursor playerCursor;
|
||||
|
||||
public:
|
||||
StartUpAppMenu(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,7 @@
|
||||
#include "GraphApp.h"
|
||||
|
||||
#include "AppMenus/AppMenu.h"
|
||||
#include "AppMenus/MainAppMenu.h"
|
||||
#include "AppMenus/StartUpAppMenu.h"
|
||||
#include "Settings.h"
|
||||
|
||||
#include <stack>
|
||||
@@ -12,14 +12,14 @@ static const double TIME_BETWEEN_FRAMES = (1000.f / FRAMES_PER_SECOND);
|
||||
|
||||
|
||||
GraphApp::GraphApp() {
|
||||
this->settings = std::make_shared<Settings>();
|
||||
this->settings = std::make_shared<Settings>(false);
|
||||
this->menuStack = std::make_shared<MenuStack>();
|
||||
this->renderWindow = std::make_shared<sf::RenderWindow>();
|
||||
}
|
||||
|
||||
void GraphApp::run() {
|
||||
this->settings->changeVideoMode(*this->renderWindow);
|
||||
this->menuStack->push(std::make_shared<MainAppMenu>(this->menuStack, this->settings, this->renderWindow));
|
||||
this->menuStack->push(std::make_shared<StartUpAppMenu>(this->menuStack, this->settings, this->renderWindow));
|
||||
|
||||
bool quit = false;
|
||||
double timeAtNextFrame = 0;
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <optional>
|
||||
#include <algorithm>
|
||||
#include <SFML/Graphics.hpp>
|
||||
|
||||
using sfKey = sf::Keyboard::Key;
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
|
||||
enum PiecesType {
|
||||
CONVEX_PIECES,
|
||||
@@ -19,3 +21,15 @@ inline int getSizeOfPieces(PiecesType type) {
|
||||
inline PiecesType createSinglePieceType(int size) {
|
||||
return PiecesType(SINGLE_PIECE + size - 1);
|
||||
}
|
||||
|
||||
inline std::string getPiecesTypeName(PiecesType piecesType) {
|
||||
static const std::string PIECES_TYPE_NAME[] = {
|
||||
"CONVEX",
|
||||
"HOLELESS",
|
||||
"OTHER",
|
||||
"ALL",
|
||||
"SINGLE"
|
||||
};
|
||||
|
||||
return PIECES_TYPE_NAME[piecesType];
|
||||
}
|
||||
|
||||
@@ -42,6 +42,13 @@ void PlayerCursor::updatePosition() {
|
||||
}
|
||||
}
|
||||
|
||||
bool PlayerCursor::moved() const {
|
||||
return (this->movedLeft()
|
||||
|| this->movedRight()
|
||||
|| this->movedUp()
|
||||
|| this->movedDown());
|
||||
}
|
||||
|
||||
bool PlayerCursor::movedLeft() const {
|
||||
return this->shouldMove(this->leftDAS);
|
||||
}
|
||||
@@ -66,6 +73,48 @@ void PlayerCursor::goToPosition(const sf::Vector2u& newPosition) {
|
||||
}
|
||||
}
|
||||
|
||||
bool PlayerCursor::addPosition(unsigned int x, unsigned int y) {
|
||||
if (y >= this->rows.size()) return false;
|
||||
if (x > this->rows.at(y)) return false;
|
||||
|
||||
this->rows.at(y)++;
|
||||
if ((y == this->position.y) && (x <= this->position.x)) {
|
||||
this->position.x++;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PlayerCursor::removePosition(unsigned int x, unsigned int y) {
|
||||
if (y >= this->rows.size()) return false;
|
||||
if (x >= this->rows.at(y)) return false;
|
||||
|
||||
this->rows.at(y)--;
|
||||
if ((y == this->position.y) && (x < this->position.x)) {
|
||||
this->position.x--;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PlayerCursor::addRow(unsigned int position, unsigned int width) {
|
||||
if (position > this->rows.size()) return false;
|
||||
|
||||
this->rows.insert(this->rows.begin() + position, width);
|
||||
if (position <= this->position.y) {
|
||||
this->position.y++;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PlayerCursor::removeRow(unsigned int position) {
|
||||
if (position >= this->rows.size()) return false;
|
||||
|
||||
this->rows.erase(this->rows.begin() + position);
|
||||
if (position < this->position.y) {
|
||||
this->position.y--;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
const sf::Vector2u& PlayerCursor::getPosition() const {
|
||||
return this->position;
|
||||
}
|
||||
@@ -73,7 +122,7 @@ const sf::Vector2u& PlayerCursor::getPosition() const {
|
||||
bool PlayerCursor::shouldMove(int DAS) const {
|
||||
return (DAS == 1
|
||||
|| (DAS > MENU_DAS && (DAS % 5) == 0)
|
||||
|| (DAS > (FRAMES_PER_SECOND * 2)));
|
||||
|| (DAS > (MENU_DAS * 4)));
|
||||
}
|
||||
|
||||
void PlayerCursor::moveLeft() {
|
||||
|
||||
@@ -18,6 +18,8 @@ class PlayerCursor {
|
||||
|
||||
void updatePosition();
|
||||
|
||||
bool moved() const;
|
||||
|
||||
bool movedLeft() const;
|
||||
|
||||
bool movedRight() const;
|
||||
@@ -28,6 +30,14 @@ class PlayerCursor {
|
||||
|
||||
void goToPosition(const sf::Vector2u& newPosition);
|
||||
|
||||
bool addPosition(unsigned int x, unsigned int y);
|
||||
|
||||
bool removePosition(unsigned int x, unsigned int y);
|
||||
|
||||
bool addRow(unsigned int position, unsigned int width);
|
||||
|
||||
bool removeRow(unsigned int position);
|
||||
|
||||
const sf::Vector2u& getPosition() const;
|
||||
|
||||
private:
|
||||
|
||||
@@ -2,7 +2,10 @@
|
||||
|
||||
#include "../Core/Menu.h"
|
||||
#include "Keybinds.h"
|
||||
#include "PiecesType.h"
|
||||
|
||||
#include <vector>
|
||||
#include <optional>
|
||||
#include <fstream>
|
||||
#include <algorithm>
|
||||
#include <SFML/Graphics.hpp>
|
||||
@@ -10,25 +13,64 @@
|
||||
static const sf::Vector2u BASE_WINDOW_SIZE = {80, 50};
|
||||
static const int WINDOW_SIZE_MULTIPLIERS[] = {4, 6, 10, 14, 20, 30, 40};
|
||||
static const int WINDOW_SIZE_LAST_MODE = (sizeof(WINDOW_SIZE_MULTIPLIERS) / sizeof(int)) - 1;
|
||||
static const int START_TIMER_MAX = 4;
|
||||
static const int DISTRIBUTION_MAX = 20;
|
||||
|
||||
|
||||
Settings::Settings() {
|
||||
for (int i = 1; i <= MAXIMUM_PIECES_SIZE; i++) {
|
||||
this->menu.getPiecesList().loadPieces(i);
|
||||
}
|
||||
|
||||
Settings::Settings(bool loadPieces) {
|
||||
this->keybinds.clear();
|
||||
this->keybinds.reserve(NUMBER_OF_KEYBINDS);
|
||||
for (int i = 0; i < NUMBER_OF_KEYBINDS; i++) {
|
||||
this->keybinds.emplace_back(i);
|
||||
}
|
||||
|
||||
this->loadSettingsFromFile();
|
||||
this->loadSettingsFromFile(loadPieces, {});
|
||||
}
|
||||
|
||||
void Settings::loadSettingsFromFile() {
|
||||
void Settings::loadPieces(int maximumPiecesSizeRequest) {
|
||||
if (maximumPiecesSizeRequest < MINIMUM_PIECES_SIZE) {
|
||||
maximumPiecesSizeRequest = MINIMUM_PIECES_SIZE;
|
||||
}
|
||||
else if (maximumPiecesSizeRequest > MAXIMUM_PIECES_SIZE) {
|
||||
maximumPiecesSizeRequest = MAXIMUM_PIECES_SIZE;
|
||||
}
|
||||
|
||||
bool succeeded = true;
|
||||
int i = 1;
|
||||
while (succeeded && (i <= maximumPiecesSizeRequest)) {
|
||||
succeeded = this->menu.getPiecesList().loadPieces(i);
|
||||
i++;
|
||||
}
|
||||
|
||||
if (succeeded) {
|
||||
this->maximumPiecesSize = maximumPiecesSizeRequest;
|
||||
}
|
||||
this->loadedPieces = succeeded;
|
||||
}
|
||||
|
||||
void Settings::loadSettingsFromFile(bool loadPieces, std::optional<int> maximumPiecesSizeRequest) {
|
||||
std::ifstream settingsFile("data/config/settings.bin", std::ios::binary);
|
||||
char byte;
|
||||
|
||||
// file format version
|
||||
settingsFile.get(byte);
|
||||
|
||||
// maximum pieces size
|
||||
settingsFile.get(byte);
|
||||
this->maximumPiecesSize = byte;
|
||||
|
||||
if (loadPieces) {
|
||||
if (maximumPiecesSizeRequest.has_value()) {
|
||||
this->loadPieces(maximumPiecesSizeRequest.value());
|
||||
}
|
||||
else {
|
||||
this->loadPieces(byte);
|
||||
}
|
||||
}
|
||||
else {
|
||||
this->loadedPieces = false;
|
||||
}
|
||||
|
||||
// keybind layout
|
||||
settingsFile.get(byte);
|
||||
this->chosenKeybinds = byte;
|
||||
@@ -49,9 +91,9 @@ void Settings::loadSettingsFromFile() {
|
||||
settingsFile.get(byte);
|
||||
this->windowSizeMode = byte;
|
||||
|
||||
// master volume
|
||||
// start timer length
|
||||
settingsFile.get(byte);
|
||||
this->masterVolume = byte;
|
||||
this->startTimerLength = byte;
|
||||
|
||||
// gamemode
|
||||
settingsFile.get(byte);
|
||||
@@ -65,35 +107,68 @@ void Settings::loadSettingsFromFile() {
|
||||
settingsFile.get(byte);
|
||||
this->menu.setBoardHeight(byte);
|
||||
|
||||
// piece distribution
|
||||
settingsFile.get(byte);
|
||||
//TODO
|
||||
if (byte == 2) {
|
||||
if (this->loadedPieces) {
|
||||
// piece distribution
|
||||
settingsFile.get(byte);
|
||||
this->menu.getPiecesList().setDistributionMode(DistributionMode(byte));
|
||||
|
||||
this->distributions.clear();
|
||||
this->distributions.push_back(0);
|
||||
for (int i = 1; i <= 15; i++) {
|
||||
settingsFile.get(byte);
|
||||
//TODO
|
||||
this->distributions.push_back(byte);
|
||||
}
|
||||
}
|
||||
this->confirmDistribution();
|
||||
|
||||
// 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});
|
||||
// selected pieces
|
||||
char pieceType;
|
||||
char pieceSize;
|
||||
char lowByte;
|
||||
char midByte;
|
||||
char highByte;
|
||||
|
||||
this->selectedPieces.clear();
|
||||
while (settingsFile.get(pieceType)) {
|
||||
if (settingsFile.eof()) break;
|
||||
|
||||
if (getSizeOfPieces(PiecesType(pieceType)) == 0) {
|
||||
settingsFile.get(pieceSize);
|
||||
|
||||
this->selectedPieces.emplace_back(PiecesType(pieceType), pieceSize);
|
||||
}
|
||||
else {
|
||||
settingsFile.get(lowByte);
|
||||
settingsFile.get(midByte);
|
||||
settingsFile.get(highByte);
|
||||
|
||||
int pieceNumber = ((unsigned char) lowByte) + ((unsigned char) midByte << 8) + ((unsigned char) highByte << 16);
|
||||
this->selectedPieces.emplace_back(PiecesType(pieceType), pieceNumber);
|
||||
}
|
||||
}
|
||||
this->confirmSelectedPieces();
|
||||
}
|
||||
else {
|
||||
this->distributions.clear();
|
||||
this->selectedPieces.clear();
|
||||
}
|
||||
this->confirmSelectedPieces();
|
||||
}
|
||||
|
||||
void Settings::saveSettingsToFile() const {
|
||||
if (!this->loadedPieces) return;
|
||||
|
||||
this->keybinds.at(CUSTOMIZABLE_KEYBINDS).saveKeybindsToFile();
|
||||
|
||||
std::ofstream settingsFile("data/config/settings.bin", std::ios::trunc | std::ios::binary);
|
||||
char byte;
|
||||
|
||||
// file format version
|
||||
byte = CURRENT_FILE_FORMAT_VERSION;
|
||||
settingsFile.write(&byte, 1);
|
||||
|
||||
// maximum pieces size
|
||||
byte = this->maximumPiecesSize;
|
||||
settingsFile.write(&byte, 1);
|
||||
|
||||
// keybind layout
|
||||
byte = this->chosenKeybinds;
|
||||
settingsFile.write(&byte, 1);
|
||||
@@ -114,8 +189,8 @@ void Settings::saveSettingsToFile() const {
|
||||
byte = this->windowSizeMode;
|
||||
settingsFile.write(&byte, 1);
|
||||
|
||||
// master volume
|
||||
byte = this->masterVolume;
|
||||
// start timer length
|
||||
byte = this->startTimerLength;
|
||||
settingsFile.write(&byte, 1);
|
||||
|
||||
// gamemode
|
||||
@@ -131,15 +206,30 @@ void Settings::saveSettingsToFile() const {
|
||||
settingsFile.write(&byte, 1);
|
||||
|
||||
// piece distribution
|
||||
//TODO
|
||||
byte = this->menu.readPiecesList().getDistributionMode();
|
||||
settingsFile.write(&byte, 1);
|
||||
|
||||
for (int i = 1; i <= 15; i++) {
|
||||
byte = this->distributions.at(i);
|
||||
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);
|
||||
if (getSizeOfPieces(type) == 0) {
|
||||
byte = value;
|
||||
settingsFile.write(&byte, 1);
|
||||
}
|
||||
else {
|
||||
int number = value;
|
||||
for (int i = 0; i < 3; i++) {
|
||||
byte = (number % 256);
|
||||
settingsFile.write(&byte, 1);
|
||||
number = (number >> 8);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -188,17 +278,17 @@ void Settings::changeVideoMode(sf::RenderWindow& window) const {
|
||||
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);
|
||||
bool Settings::lengthenStartTimer() {
|
||||
if (this->startTimerLength < START_TIMER_MAX) {
|
||||
this->startTimerLength++;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Settings::lowerMasterVolume() {
|
||||
if (this->masterVolume > 0) {
|
||||
this->masterVolume = std::max(this->masterVolume - 5, 0);
|
||||
bool Settings::shortenStartTimer() {
|
||||
if (this->startTimerLength > 0) {
|
||||
this->startTimerLength--;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -209,35 +299,80 @@ void Settings::setGamemode(Gamemode gamemode) {
|
||||
}
|
||||
|
||||
void Settings::selectPieces(PiecesType type, int value) {
|
||||
if (!this->loadedPieces) return;
|
||||
|
||||
this->selectedPieces.emplace_back(type, value);
|
||||
}
|
||||
|
||||
void Settings::unselectPieces(int index) {
|
||||
if (!this->loadedPieces) return;
|
||||
if (index >= this->selectedPieces.size()) return;
|
||||
|
||||
this->selectedPieces.erase(this->selectedPieces.begin() + index);
|
||||
}
|
||||
|
||||
void Settings::confirmSelectedPieces() {
|
||||
if (!this->loadedPieces) return;
|
||||
|
||||
this->menu.getPiecesList().unselectAll();
|
||||
|
||||
bool selectedNone = true;
|
||||
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;}
|
||||
if (!(value > this->maximumPiecesSize)) {
|
||||
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;}
|
||||
}
|
||||
selectedNone = false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (size > MAXIMUM_PIECES_SIZE) return;
|
||||
|
||||
this->menu.getPiecesList().selectPiece(size, value);
|
||||
if (!(getSizeOfPieces(type) > this->maximumPiecesSize)) {
|
||||
this->menu.getPiecesList().selectPiece(size, value);
|
||||
selectedNone = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (selectedNone) {
|
||||
this->selectedPieces.push_back(DEFAULT_SELECTION);
|
||||
this->confirmSelectedPieces();
|
||||
}
|
||||
}
|
||||
|
||||
bool Settings::increaseDistribution(int size) {
|
||||
if (!this->loadedPieces) return false;
|
||||
if (size < 1 || size > this->maximumPiecesSize) return false;
|
||||
|
||||
if (this->distributions.at(size) < DISTRIBUTION_MAX) {
|
||||
this->distributions.at(size)++;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Settings::decreaseDistribution(int size) {
|
||||
if (!this->loadedPieces) return false;
|
||||
if (size < 1 || size > this->maximumPiecesSize) return false;
|
||||
|
||||
if (this->distributions.at(size) > 0) {
|
||||
this->distributions.at(size)--;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void Settings::confirmDistribution() {
|
||||
if (!this->loadedPieces) return;
|
||||
|
||||
for (int i = 1; i <= 15; i++) {
|
||||
this->menu.getPiecesList().changeCustomDistribution(i, (double) 1 / (this->distributions.at(i) + 0.001));
|
||||
}
|
||||
}
|
||||
|
||||
Menu& Settings::getMenu() {
|
||||
@@ -248,6 +383,14 @@ Keybinds& Settings::getKeybinds() {
|
||||
return this->keybinds.at(this->chosenKeybinds);
|
||||
}
|
||||
|
||||
int Settings::getMaximumPiecesSize() const {
|
||||
return this->maximumPiecesSize;
|
||||
}
|
||||
|
||||
bool Settings::hasLoadedPieces() const {
|
||||
return this->loadedPieces;
|
||||
}
|
||||
|
||||
int Settings::getKeybindsLayout() const {
|
||||
return this->chosenKeybinds;
|
||||
}
|
||||
@@ -260,10 +403,14 @@ int Settings::getWindowSizeMultiplier() const {
|
||||
return WINDOW_SIZE_MULTIPLIERS[this->windowSizeMode];
|
||||
}
|
||||
|
||||
int Settings::getMasterVolume() const {
|
||||
return this->masterVolume;
|
||||
int Settings::getStartTimerLength() const {
|
||||
return this->startTimerLength;
|
||||
}
|
||||
|
||||
const std::vector<std::pair<PiecesType, int>>& Settings::getSelectedPieces() const {
|
||||
return this->selectedPieces;
|
||||
}
|
||||
|
||||
const std::vector<int>& Settings::getDistributions() const {
|
||||
return this->distributions;
|
||||
}
|
||||
|
||||
@@ -4,34 +4,47 @@
|
||||
#include "Keybinds.h"
|
||||
#include "PiecesType.h"
|
||||
|
||||
#include <SFML/Graphics.hpp>
|
||||
#include <vector>
|
||||
#include <optional>
|
||||
#include <SFML/Graphics.hpp>
|
||||
|
||||
static const int CURRENT_FILE_FORMAT_VERSION = 11;
|
||||
|
||||
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;
|
||||
static const int RELEASE_PIECES_SIZE = 15;
|
||||
static const int DEBUG_PIECES_SIZE = 10;
|
||||
|
||||
static const int MINIMUM_PIECES_SIZE = 4;
|
||||
#ifdef NDEBUG
|
||||
static const int MAXIMUM_PIECES_SIZE = RELEASE_PIECES_SIZE;
|
||||
#else
|
||||
static const int MAXIMUM_PIECES_SIZE = 10;
|
||||
static const int MAXIMUM_PIECES_SIZE = DEBUG_PIECES_SIZE;
|
||||
#endif
|
||||
|
||||
static const std::pair<PiecesType, int> DEFAULT_SELECTION = {ALL_PIECES, MINIMUM_PIECES_SIZE};
|
||||
|
||||
|
||||
class Settings {
|
||||
private:
|
||||
Menu menu;
|
||||
int maximumPiecesSize;
|
||||
bool loadedPieces;
|
||||
std::vector<Keybinds> keybinds;
|
||||
int chosenKeybinds;
|
||||
int windowSizeMode;
|
||||
int masterVolume;
|
||||
int startTimerLength;
|
||||
Gamemode gamemode;
|
||||
std::vector<std::pair<PiecesType, int>> selectedPieces;
|
||||
std::vector<int> distributions;
|
||||
|
||||
public:
|
||||
Settings();
|
||||
Settings(bool loadPieces);
|
||||
|
||||
void loadSettingsFromFile();
|
||||
void loadPieces(int maximumPiecesSizeRequest);
|
||||
|
||||
void loadSettingsFromFile(bool loadPieces, std::optional<int> maximumPiecesSizeRequest);
|
||||
|
||||
void saveSettingsToFile() const;
|
||||
|
||||
@@ -47,9 +60,9 @@ class Settings {
|
||||
|
||||
void changeVideoMode(sf::RenderWindow& window) const;
|
||||
|
||||
bool raiseMasterVolume();
|
||||
bool lengthenStartTimer();
|
||||
|
||||
bool lowerMasterVolume();
|
||||
bool shortenStartTimer();
|
||||
|
||||
void setGamemode(Gamemode gamemode);
|
||||
|
||||
@@ -59,17 +72,29 @@ class Settings {
|
||||
|
||||
void confirmSelectedPieces();
|
||||
|
||||
bool increaseDistribution(int size);
|
||||
|
||||
bool decreaseDistribution(int size);
|
||||
|
||||
void confirmDistribution();
|
||||
|
||||
Menu& getMenu();
|
||||
|
||||
Keybinds& getKeybinds();
|
||||
|
||||
int getMaximumPiecesSize() const;
|
||||
|
||||
bool hasLoadedPieces() const;
|
||||
|
||||
int getKeybindsLayout() const;
|
||||
|
||||
Gamemode getGamemode() const;
|
||||
|
||||
int getWindowSizeMultiplier() const;
|
||||
|
||||
int getMasterVolume() const;
|
||||
int getStartTimerLength() const;
|
||||
|
||||
const std::vector<std::pair<PiecesType, int>>& getSelectedPieces() const;
|
||||
|
||||
const std::vector<int>& getDistributions() const;
|
||||
};
|
||||
|
||||
@@ -15,17 +15,49 @@ int main() {
|
||||
PiecesFiles pf;
|
||||
for (int i = 1; i <= MAXIMUM_PIECES_SIZE; 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;
|
||||
#ifdef NDEBUG
|
||||
std::cout << "IMPORTANT: You are currently in release mode, if you do not wish to generate big pieces (can take several minutes), type 'xmake f -m debug'." << std::endl;
|
||||
#endif
|
||||
|
||||
std::cout << "INFO: Pieces files for size " << i << " not found, generating..." << std::endl;
|
||||
pf.savePieces(i);
|
||||
}
|
||||
}
|
||||
#ifndef NDEBUG
|
||||
std::cout << "IMPORTANT: you are currently in debug mode, if you wish to use bigger pieces, type 'xmake f -m release'." << std::endl;
|
||||
|
||||
bool everythingGenerated = true;
|
||||
for (int i = DEBUG_PIECES_SIZE; i <= RELEASE_PIECES_SIZE; i++) {
|
||||
if (!std::filesystem::exists("data/pieces/" + std::to_string(i) + "minos.bin")) {
|
||||
everythingGenerated = false;
|
||||
}
|
||||
}
|
||||
if (!everythingGenerated) {
|
||||
std::cout << "NOTE : you do not have all pieces generated, generating can take several minutes." << std::endl;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!std::filesystem::exists("data/config/settings.bin")) {
|
||||
std::cout << "settings file not found, generating..." << std::endl;
|
||||
std::cout << "INFO: Settings file not found, generating..." << std::endl;
|
||||
resetSettingsFile();
|
||||
}
|
||||
else {
|
||||
std::ifstream settingsFile("data/config/settings.bin", std::ios::binary);
|
||||
char byte;
|
||||
|
||||
settingsFile.get(byte);
|
||||
if ((unsigned char) byte < CURRENT_FILE_FORMAT_VERSION) {
|
||||
std::cout << "INFO: Files format changed, regenerating..." << std::endl;
|
||||
resetSettingsFile();
|
||||
for (int i = 0; i < NUMBER_OF_KEYBINDS; i++) {
|
||||
resetKeybindFile(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;
|
||||
std::cout << "INFO: Keybind file n°" << (i + 1) << "/" << NUMBER_OF_KEYBINDS << " not found, generating..." << std::endl;
|
||||
resetKeybindFile(i);
|
||||
}
|
||||
}
|
||||
@@ -43,6 +75,14 @@ void resetSettingsFile() {
|
||||
|
||||
Menu menu;
|
||||
|
||||
// file format version
|
||||
byte = CURRENT_FILE_FORMAT_VERSION;
|
||||
settingsFile.write(&byte, 1);
|
||||
|
||||
// maximum pieces size
|
||||
byte = MINIMUM_PIECES_SIZE;
|
||||
settingsFile.write(&byte, 1);
|
||||
|
||||
// keybind layout
|
||||
byte = 0;
|
||||
settingsFile.write(&byte, 1);
|
||||
@@ -63,8 +103,8 @@ void resetSettingsFile() {
|
||||
byte = 2;
|
||||
settingsFile.write(&byte, 1);
|
||||
|
||||
// master volume
|
||||
byte = 50;
|
||||
// start timer length
|
||||
byte = 2;
|
||||
settingsFile.write(&byte, 1);
|
||||
|
||||
// gamemode
|
||||
@@ -80,8 +120,12 @@ void resetSettingsFile() {
|
||||
settingsFile.write(&byte, 1);
|
||||
|
||||
// piece distribution
|
||||
byte = 0;
|
||||
byte = DEFAULT;
|
||||
settingsFile.write(&byte, 1);
|
||||
for (int i = 1; i <= 15; i++) {
|
||||
byte = 1;
|
||||
settingsFile.write(&byte, 1);
|
||||
}
|
||||
|
||||
// selected pieces
|
||||
byte = ALL_PIECES;
|
||||
|
||||
@@ -91,7 +91,7 @@ bool PiecesFiles::loadPieces(int polyominoSize, std::vector<Piece>& pieces, std:
|
||||
char positionByte;
|
||||
for (int i = 0; i < polyominoSize; i++) {
|
||||
piecesFile.get(positionByte);
|
||||
int x = (positionByte & xMask) >> 4;
|
||||
int x = ((unsigned char) positionByte & xMask) >> 4;
|
||||
int y = positionByte & yMask;
|
||||
piecePositions.insert(Position{x, y});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user