Compare commits
17 Commits
v0.1
...
507bc9cc86
| Author | SHA1 | Date | |
|---|---|---|---|
| 507bc9cc86 | |||
| e721a71894 | |||
| 1781b85332 | |||
| 92b58c4b98 | |||
| 8635d4b853 | |||
| 9780a36af4 | |||
| 6b16abda6a | |||
| 30dd323e22 | |||
| d87ddcdc22 | |||
| 8aaced68d0 | |||
| be6c8d9f77 | |||
| d9ccecfdd8 | |||
| 02bab6ed87 | |||
| 0e17996c35 | |||
| ea8478275b | |||
| 29177bc94d | |||
| c25abec6ba |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -11,6 +11,7 @@ build/
|
||||
# personnal documentation
|
||||
doc/*.txt
|
||||
doc/*.violet.html
|
||||
doc/mockups/*
|
||||
|
||||
# pieces files
|
||||
data/pieces/*.bin
|
||||
|
||||
10
.vscode/c_cpp_properties.json
vendored
Normal file
10
.vscode/c_cpp_properties.json
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"configurations": [
|
||||
{
|
||||
"name": "jminos",
|
||||
"cppStandard": "c++20",
|
||||
"compileCommands": ".vscode/compile_commands.json"
|
||||
}
|
||||
],
|
||||
"version": 4
|
||||
}
|
||||
29
README.md
Normal file
29
README.md
Normal file
@@ -0,0 +1,29 @@
|
||||
# jminos
|
||||
|
||||
Modern stacker game with every polyominos from size 1 to 15, made in C++ with [SFML 3](https://www.sfml-dev.org/)!
|
||||
|
||||
## Manual build and run
|
||||
|
||||
This project uses xmake for compiling.
|
||||
To be able to build it, you need to [install xmake](https://xmake.io) and have a compiler with c++20 compatibility, xmake will install SFML for you.
|
||||
|
||||
### Build the project
|
||||
|
||||
``cd jminos``
|
||||
|
||||
``xmake``
|
||||
|
||||
If you need to change the toolchain (for example using gcc):
|
||||
``xmake f --toolchain=gcc``
|
||||
|
||||
### Run the project
|
||||
|
||||
``xmake run``
|
||||
Note that the program will genereate the polyomino files for you. This can be quite long so it only does it up to size 10.
|
||||
|
||||
If for some reasons you wanna run the command line version:
|
||||
``xmake run text``
|
||||
|
||||
## Credits
|
||||
|
||||
Font used: [Press Start](https://www.zone38.net/font/#pressstart).
|
||||
BIN
data/config/keybinds/layout0.bin
Normal file
BIN
data/config/keybinds/layout0.bin
Normal file
Binary file not shown.
BIN
data/config/keybinds/layout1.bin
Normal file
BIN
data/config/keybinds/layout1.bin
Normal file
Binary file not shown.
BIN
data/config/keybinds/layout2.bin
Normal file
BIN
data/config/keybinds/layout2.bin
Normal file
Binary file not shown.
BIN
data/config/keybinds/layout3.bin
Normal file
BIN
data/config/keybinds/layout3.bin
Normal file
Binary file not shown.
BIN
data/config/keybinds/layout4.bin
Normal file
BIN
data/config/keybinds/layout4.bin
Normal file
Binary file not shown.
BIN
data/config/settings.bin
Normal file
BIN
data/config/settings.bin
Normal file
Binary file not shown.
17
data/fonts/pressstart/license.txt
Normal file
17
data/fonts/pressstart/license.txt
Normal file
@@ -0,0 +1,17 @@
|
||||
Thanks for downloading one of codeman38's retro video game fonts, as seen on Memepool, BoingBoing, and all around the blogosphere.
|
||||
|
||||
So, you're wondering what the license is for these fonts? Pretty simple; it's based upon that used for Bitstream's Vera font set <http://www.gnome.org/fonts/>.
|
||||
|
||||
Basically, here are the key points summarized, in as little legalese as possible; I hate reading license agreements as much as you probably do:
|
||||
|
||||
With one specific exception, you have full permission to bundle these fonts in your own free or commercial projects-- and by projects, I'm referring to not just software but also electronic documents and print publications.
|
||||
|
||||
So what's the exception? Simple: you can't re-sell these fonts in a commercial font collection. I've seen too many font CDs for sale in stores that are just a repackaging of thousands of freeware fonts found on the internet, and in my mind, that's quite a bit like highway robbery. Note that this *only* applies to products that are font collections in and of themselves; you may freely bundle these fonts with an operating system, application program, or the like.
|
||||
|
||||
Feel free to modify these fonts and even to release the modified versions, as long as you change the original font names (to ensure consistency among people with the font installed) and as long as you give credit somewhere in the font file to codeman38 or zone38.net. I may even incorporate these changes into a later version of my fonts if you wish to send me the modifed fonts via e-mail.
|
||||
|
||||
Also, feel free to mirror these fonts on your own site, as long as you make it reasonably clear that these fonts are not your own work. I'm not asking for much; linking to zone38.net or even just mentioning the nickname codeman38 should be enough.
|
||||
|
||||
Well, that pretty much sums it up... so without further ado, install and enjoy these fonts from the golden age of video games.
|
||||
|
||||
[ codeman38 | cody@zone38.net | http://www.zone38.net/ ]
|
||||
BIN
data/fonts/pressstart/prstart.ttf
Normal file
BIN
data/fonts/pressstart/prstart.ttf
Normal file
Binary file not shown.
BIN
data/fonts/pressstart/prstartk.ttf
Normal file
BIN
data/fonts/pressstart/prstartk.ttf
Normal file
Binary file not shown.
@@ -1,6 +1,6 @@
|
||||
# Pieces storage
|
||||
# Files format
|
||||
|
||||
## What is stored
|
||||
## Pieces
|
||||
|
||||
If you don't know what a polyomino is, check [this other file](Pieces_representation.md#what-are-polyominoes).
|
||||
|
||||
@@ -8,8 +8,6 @@ Generating polyominoes of size n is exponential in regard to n. Because of this,
|
||||
|
||||
We want the pieces to be always sorted in the same order, always attributed the same block type, and always set at the same spawn position, no matter how they were generated. We also want them to be separated in 3 categories : convex, not convex but without a hole, and with a hole. Theses problematics are already resolved internally, but will be calculated before storage as to not need extra calculcations upon load (except for the block type which is trivially computed).
|
||||
|
||||
## How is it stored
|
||||
|
||||
Pieces are stored in binary files. Each file simply contains every polyomino of one size, one after the other. Since each file contains all polyominoes of the same size, we know how much stuff to read and don't need delimiters. We know we've read all pieces simply when we reach the end of file character.
|
||||
|
||||
Each piece is stored as follows:
|
||||
@@ -18,3 +16,32 @@ Each piece is stored as follows:
|
||||
|
||||
The current implementation only allows to generate polyominoes up to size 16, but can be upgraded by storing coordinates on 8 bits instead of 4.
|
||||
It has been currently choosen to use pieces only up to size 15 for this game.
|
||||
|
||||
## Config
|
||||
|
||||
When compiling a release version, default files will be used, theses will then be impacted by the user and used for their next sessions.
|
||||
|
||||
### Keybinds
|
||||
|
||||
The games has 4 keyboard configs by default that never changes, and a modifiable 5th one, but theses are all stored the same.
|
||||
|
||||
Each keybinds files has the the following format:
|
||||
|
||||
- The number of the action (converted from an Enum), stored with 1 byte
|
||||
- The number of each binded keys (also converted from an Enum), stored with 1 byte
|
||||
- A separator characters which is 0xFF
|
||||
|
||||
_Repeat for every avaible actions._
|
||||
|
||||
### Settings
|
||||
|
||||
The settings file has the following format:
|
||||
|
||||
- The number of the chosen keybinds (from 0 to 4), stored with 1 byte
|
||||
- The window size mode, stored with 1 byte
|
||||
- The number of the last selected gamemode (converted from an Enum), stored with 1 byte
|
||||
- The 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
|
||||
@@ -7,7 +7,6 @@ We will only talk about pieces and not polyominoes. In this project, pieces are
|
||||
|
||||
Each frame, the UI will translate the user's input into a series of action to apply to the game. The list of action is the following:
|
||||
|
||||
- Quit the game
|
||||
- Pause
|
||||
- Retry
|
||||
- Hold
|
||||
@@ -26,6 +25,8 @@ Then the rest of actions will be tested in the list's order.
|
||||
Finally, gravity and lock delay are applied last.
|
||||
Moving and soft dropping can be held but hard dropping, holding and rotating needs to be released and pressed again to happen.
|
||||
|
||||
Menu navigation is managed by the UI and uses static keybinds to prevent the user from softlocking themselves.
|
||||
|
||||
## ARR and DAS
|
||||
|
||||
The sidewise movement of the piece is defined by two parameters: DAS and ARR.
|
||||
|
||||
@@ -4,10 +4,9 @@
|
||||
|
||||
|
||||
/**
|
||||
* The list of actions that can be taken by the player
|
||||
* The list of in-game actions that can be taken by the player
|
||||
*/
|
||||
enum Action {
|
||||
QUIT,
|
||||
PAUSE,
|
||||
RETRY,
|
||||
HOLD,
|
||||
@@ -22,8 +21,20 @@ enum Action {
|
||||
};
|
||||
|
||||
|
||||
static const std::string ACTION_NAMES[] = { // name for each action
|
||||
"Quit",
|
||||
static const Action ACTION_LIST_IN_ORDER[] = { // the list of possible actions in a sorted order
|
||||
MOVE_LEFT,
|
||||
MOVE_RIGHT,
|
||||
SOFT_DROP,
|
||||
HARD_DROP,
|
||||
ROTATE_CW,
|
||||
ROTATE_CCW,
|
||||
ROTATE_180,
|
||||
ROTATE_0,
|
||||
HOLD,
|
||||
PAUSE,
|
||||
RETRY
|
||||
};
|
||||
static const std::string ACTION_NAMES[] = { // name representation for each actions
|
||||
"Pause",
|
||||
"Retry",
|
||||
"Hold",
|
||||
|
||||
@@ -6,6 +6,9 @@
|
||||
|
||||
#include <memory>
|
||||
|
||||
static const int DEFAULT_BOARD_WIDTH = 10; // the default width of the board when starting the menu
|
||||
static const int DEFAULT_BOARD_HEIGHT = 20; // the default height of the board when starting the menu
|
||||
|
||||
|
||||
Menu::Menu() {
|
||||
this->piecesList = std::make_shared<PiecesList>(PiecesList());
|
||||
|
||||
@@ -4,9 +4,7 @@
|
||||
#include "Player.h"
|
||||
#include "Game.h"
|
||||
|
||||
static const int FRAMES_PER_SECOND = 60; // the number of frames per second, all the values in the app were choosen with this number in mind
|
||||
static const int DEFAULT_BOARD_WIDTH = 10; // the default width of the board when starting the menu
|
||||
static const int DEFAULT_BOARD_HEIGHT = 20; // the default height of the board when starting the menu
|
||||
static const int FRAMES_PER_SECOND = 60; // the number of frames per second, all the values in the app were choosen with this number in mind
|
||||
|
||||
|
||||
/**
|
||||
|
||||
63
src/GraphicalUI/AppMenus/AppMenu.h
Normal file
63
src/GraphicalUI/AppMenus/AppMenu.h
Normal file
@@ -0,0 +1,63 @@
|
||||
#pragma once
|
||||
|
||||
#include "../Settings.h"
|
||||
|
||||
#include <stack>
|
||||
#include <memory>
|
||||
#include <SFML/Graphics.hpp>
|
||||
|
||||
class AppMenu;
|
||||
using MenuStack = std::stack<std::shared_ptr<AppMenu>>;
|
||||
|
||||
|
||||
class AppMenu {
|
||||
protected:
|
||||
std::shared_ptr<MenuStack> menuStack;
|
||||
std::shared_ptr<Settings> settings;
|
||||
std::shared_ptr<sf::RenderWindow> renderWindow;
|
||||
bool enterPressed = false;
|
||||
bool enterReleased = false;
|
||||
bool escPressed = false;
|
||||
bool escReleased = false;
|
||||
|
||||
public:
|
||||
AppMenu(std::shared_ptr<MenuStack> menuStack, std::shared_ptr<Settings> settings, std::shared_ptr<sf::RenderWindow> renderWindow) :
|
||||
menuStack(menuStack),
|
||||
settings(settings),
|
||||
renderWindow(renderWindow)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void updateMetaBinds() {
|
||||
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Key::Enter)) {
|
||||
enterPressed = true;
|
||||
enterReleased = false;
|
||||
}
|
||||
else {
|
||||
enterReleased = enterPressed;
|
||||
enterPressed = false;
|
||||
}
|
||||
|
||||
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Key::Escape)) {
|
||||
escPressed = true;
|
||||
escReleased = false;
|
||||
}
|
||||
else {
|
||||
escReleased = escPressed;
|
||||
escPressed = false;
|
||||
}
|
||||
}
|
||||
|
||||
virtual void computeFrame() = 0;
|
||||
|
||||
virtual void drawFrame() const = 0;
|
||||
};
|
||||
|
||||
|
||||
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)));
|
||||
}
|
||||
103
src/GraphicalUI/AppMenus/GamePlayingAppMenu.cpp
Normal file
103
src/GraphicalUI/AppMenus/GamePlayingAppMenu.cpp
Normal file
@@ -0,0 +1,103 @@
|
||||
#include "GamePlayingAppMenu.h"
|
||||
|
||||
#include "AppMenu.h"
|
||||
|
||||
#include <stack>
|
||||
#include <memory>
|
||||
#include <SFML/Graphics.hpp>
|
||||
|
||||
|
||||
GamePlayingAppMenu::GamePlayingAppMenu(std::shared_ptr<MenuStack> menuStack, std::shared_ptr<Settings> settings, std::shared_ptr<sf::RenderWindow> renderWindow) :
|
||||
AppMenu(menuStack, settings, renderWindow),
|
||||
game(this->settings->getMenu().startGame(this->settings->getGamemode()))
|
||||
{
|
||||
|
||||
this->game.start();
|
||||
this->paused = false;
|
||||
}
|
||||
|
||||
void GamePlayingAppMenu::computeFrame() {
|
||||
this->updateMetaBinds();
|
||||
|
||||
if (this->escReleased) {
|
||||
this->menuStack->pop();
|
||||
}
|
||||
else {
|
||||
std::set<Action> actions;
|
||||
for (Action action : ACTION_LIST_IN_ORDER) {
|
||||
for (sfKey key : this->settings->getKeybinds().getKeybinds(action)) {
|
||||
if (sf::Keyboard::isKeyPressed(key)) {
|
||||
actions.insert(action);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (actions.contains(RETRY)) {
|
||||
this->game.reset();
|
||||
this->game.start();
|
||||
}
|
||||
if (actions.contains(PAUSE)) {
|
||||
this->paused = (!this->paused);
|
||||
}
|
||||
|
||||
if (!paused) {
|
||||
this->game.nextFrame(actions);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GamePlayingAppMenu::drawFrame() const {
|
||||
this->renderWindow->clear(sf::Color::Black);
|
||||
|
||||
int sizeMultiplier = this->settings->getWindowSizeMultiplier();
|
||||
sf::Vector2f cellSize((float) sizeMultiplier, (float) sizeMultiplier);
|
||||
|
||||
for (int y = this->game.getBoard().getBaseHeight() + 10; y >= 0; y--) {
|
||||
for (int x = 0; x < this->game.getBoard().getWidth(); x++) {
|
||||
bool isActivePieceHere = (this->game.getActivePiece() != nullptr) && (this->game.getActivePiece()->getPositions().contains(Position{x, y} - this->game.getActivePiecePosition()));
|
||||
bool isGhostPieceHere = (this->game.getActivePiece() != nullptr) && (this->game.getActivePiece()->getPositions().contains(Position{x, y} - this->game.ghostPiecePosition()));
|
||||
Block block = (isActivePieceHere || isGhostPieceHere) ? this->game.getActivePiece()->getBlockType() : this->game.getBoard().getBlock(Position{x, y});
|
||||
|
||||
sf::RectangleShape cell(cellSize);
|
||||
cell.setFillColor(sf::Color(BLOCKS_COLOR[block].red, BLOCKS_COLOR[block].green, BLOCKS_COLOR[block].blue, (isGhostPieceHere && !isActivePieceHere) ? 150 : 255));
|
||||
cell.setPosition(sf::Vector2f(x * sizeMultiplier, (this->game.getBoard().getBaseHeight() + 10 - y) * sizeMultiplier));
|
||||
this->renderWindow->draw(cell);
|
||||
}
|
||||
}
|
||||
|
||||
if (this->game.getNextPieces().size() > 0) {
|
||||
for (int y = 10; y >= 0; y--) {
|
||||
for (int x = 0; x <= 10; x++) {
|
||||
Block block = this->game.getNextPieces().at(0).getBlockType();
|
||||
sf::RectangleShape cell(sf::Vector2f(20.f, 20.f));
|
||||
cell.setPosition(sf::Vector2f((x + 2 + this->game.getBoard().getWidth())*20, (this->game.getBoard().getBaseHeight() - y)*20));
|
||||
if (this->game.getNextPieces().at(0).getPositions().contains(Position({x, y}))) {
|
||||
cell.setFillColor(sf::Color(BLOCKS_COLOR[block].red, BLOCKS_COLOR[block].green, BLOCKS_COLOR[block].blue));
|
||||
}
|
||||
else {
|
||||
cell.setFillColor(sf::Color(0, 0, 0));
|
||||
}
|
||||
this->renderWindow->draw(cell);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (this->game.getHeldPiece() != nullptr) {
|
||||
for (int y = 10; y >= 0; y--) {
|
||||
for (int x = 0; x <= 10; x++) {
|
||||
Block block = this->game.getHeldPiece()->getBlockType();
|
||||
sf::RectangleShape cell(sf::Vector2f(20.f, 20.f));
|
||||
cell.setPosition(sf::Vector2f((x + 12 + this->game.getBoard().getWidth())*20, (this->game.getBoard().getBaseHeight() - y)*20));
|
||||
if (this->game.getHeldPiece()->getPositions().contains(Position({x, y}))) {
|
||||
cell.setFillColor(sf::Color(BLOCKS_COLOR[block].red, BLOCKS_COLOR[block].green, BLOCKS_COLOR[block].blue));
|
||||
}
|
||||
else {
|
||||
cell.setFillColor(sf::Color(0, 0, 0));
|
||||
}
|
||||
this->renderWindow->draw(cell);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this->renderWindow->display();
|
||||
}
|
||||
21
src/GraphicalUI/AppMenus/GamePlayingAppMenu.h
Normal file
21
src/GraphicalUI/AppMenus/GamePlayingAppMenu.h
Normal file
@@ -0,0 +1,21 @@
|
||||
#pragma once
|
||||
|
||||
#include "AppMenu.h"
|
||||
|
||||
#include <stack>
|
||||
#include <memory>
|
||||
#include <SFML/Graphics.hpp>
|
||||
|
||||
|
||||
class GamePlayingAppMenu : public AppMenu {
|
||||
private:
|
||||
Game game;
|
||||
bool paused;
|
||||
|
||||
public:
|
||||
GamePlayingAppMenu(std::shared_ptr<MenuStack> menuStack, std::shared_ptr<Settings> settings, std::shared_ptr<sf::RenderWindow> renderWindow);
|
||||
|
||||
void computeFrame();
|
||||
|
||||
void drawFrame() const;
|
||||
};
|
||||
91
src/GraphicalUI/AppMenus/GameSettingsAppMenu.cpp
Normal file
91
src/GraphicalUI/AppMenus/GameSettingsAppMenu.cpp
Normal file
@@ -0,0 +1,91 @@
|
||||
#include "GameSettingsAppMenu.h"
|
||||
|
||||
#include "AppMenu.h"
|
||||
#include "GamePlayingAppMenu.h"
|
||||
#include "PlayerCursor.h"
|
||||
|
||||
#include <stack>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <SFML/Graphics.hpp>
|
||||
|
||||
|
||||
GameSettingsAppMenu::GameSettingsAppMenu(std::shared_ptr<MenuStack> menuStack, std::shared_ptr<Settings> settings, std::shared_ptr<sf::RenderWindow> renderWindow) :
|
||||
AppMenu(menuStack, settings, renderWindow),
|
||||
playerCursor(std::vector<unsigned int>({2, 3, 1})) {
|
||||
|
||||
}
|
||||
|
||||
void GameSettingsAppMenu::computeFrame() {
|
||||
this->updateMetaBinds();
|
||||
this->playerCursor.updatePosition();
|
||||
|
||||
switch (this->playerCursor.getPosition().y) {
|
||||
case 1 : {
|
||||
switch (this->playerCursor.getPosition().x) {
|
||||
case 0 : {this->settings->setGamemode(SPRINT); break;}
|
||||
case 1 : {this->settings->setGamemode(MARATHON); break;}
|
||||
case 2 : {this->settings->setGamemode(ULTRA); break;}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 2 : {
|
||||
switch (this->playerCursor.getPosition().x) {
|
||||
case 0 : {this->settings->setGamemode(MASTER); break;}
|
||||
case 1 : break; //TODO
|
||||
case 2 : break; //TODO
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (this->enterReleased) {
|
||||
if (this->playerCursor.getPosition().y == 0) {
|
||||
//TODO
|
||||
}
|
||||
if (this->playerCursor.getPosition().y > 0) {
|
||||
this->menuStack->push(std::make_shared<GamePlayingAppMenu>(this->menuStack, this->settings, this->renderWindow));
|
||||
}
|
||||
}
|
||||
if (this->escReleased) {
|
||||
this->menuStack->pop();
|
||||
}
|
||||
}
|
||||
|
||||
void GameSettingsAppMenu::drawFrame() const {
|
||||
this->renderWindow->clear(sf::Color(200, 200, 200));
|
||||
|
||||
|
||||
sf::Font font("data/fonts/pressstart/prstartk.ttf");
|
||||
sf::Text text(font, "", this->settings->getWindowSizeMultiplier() * 2);
|
||||
text.setFillColor(sf::Color(0, 0, 0));
|
||||
text.setOutlineColor(sf::Color(255, 255, 255));
|
||||
|
||||
text.setString("GAME SETTINGS");
|
||||
text.setOrigin(text.getLocalBounds().getCenter());
|
||||
text.setPosition(sf::Vector2f({(float) this->settings->getWindowSizeMultiplier() * 40, (float) this->settings->getWindowSizeMultiplier() * 5}));
|
||||
this->renderWindow->draw(text);
|
||||
|
||||
this->placeText(text, "PIECES SELECT", 5.f, 15.f, 0, 0);
|
||||
this->placeText(text, "BOARD SELECT", 40.f, 15.f, 1, 0);
|
||||
|
||||
this->placeText(text, "SPRINT", 5.f, 25.f, 0, 1);
|
||||
this->placeText(text, "MARATHON", 25.f, 25.f, 1, 1);
|
||||
this->placeText(text, "ULTRA", 50.f, 25.f, 2, 1);
|
||||
this->placeText(text, "MASTER", 5.f, 35.f, 0, 2);
|
||||
this->placeText(text, "TODO", 25.f, 35.f, 1, 2);
|
||||
this->placeText(text, "TOOD", 50.f, 35.f, 2, 2);
|
||||
|
||||
this->renderWindow->display();
|
||||
}
|
||||
|
||||
void GameSettingsAppMenu::placeText(sf::Text& text, const sf::String& string, float xPos, float yPos, unsigned int xCursorOutline, unsigned int yCursorOutline) const {
|
||||
float sizeMultiplier = this->settings->getWindowSizeMultiplier();
|
||||
|
||||
text.setString(string);
|
||||
text.setOutlineThickness((this->playerCursor.getPosition().x == xCursorOutline
|
||||
&& this->playerCursor.getPosition().y == yCursorOutline) ? (sizeMultiplier / 2) : 0);
|
||||
text.setOrigin(sf::Vector2f({0, text.getLocalBounds().size.y / 2}));
|
||||
text.setPosition(sf::Vector2f({sizeMultiplier * xPos, sizeMultiplier * yPos}));
|
||||
this->renderWindow->draw(text);
|
||||
}
|
||||
23
src/GraphicalUI/AppMenus/GameSettingsAppMenu.h
Normal file
23
src/GraphicalUI/AppMenus/GameSettingsAppMenu.h
Normal file
@@ -0,0 +1,23 @@
|
||||
#pragma once
|
||||
|
||||
#include "AppMenu.h"
|
||||
#include "PlayerCursor.h"
|
||||
|
||||
#include <stack>
|
||||
#include <memory>
|
||||
#include <SFML/Graphics.hpp>
|
||||
|
||||
|
||||
class GameSettingsAppMenu : public AppMenu {
|
||||
private:
|
||||
PlayerCursor playerCursor;
|
||||
|
||||
public:
|
||||
GameSettingsAppMenu(std::shared_ptr<MenuStack> menuStack, std::shared_ptr<Settings> settings, std::shared_ptr<sf::RenderWindow> renderWindow);
|
||||
|
||||
void computeFrame();
|
||||
|
||||
void drawFrame() const;
|
||||
|
||||
void placeText(sf::Text& text, const sf::String& string, float xPos, float yPos, unsigned int xCursorOutline, unsigned int yCursorOutline) const ;
|
||||
};
|
||||
73
src/GraphicalUI/AppMenus/MainAppMenu.cpp
Normal file
73
src/GraphicalUI/AppMenus/MainAppMenu.cpp
Normal file
@@ -0,0 +1,73 @@
|
||||
#include "MainAppMenu.h"
|
||||
|
||||
#include "AppMenu.h"
|
||||
#include "GameSettingsAppMenu.h"
|
||||
#include "PlayerCursor.h"
|
||||
|
||||
#include <stack>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <SFML/Graphics.hpp>
|
||||
|
||||
|
||||
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})) {
|
||||
|
||||
}
|
||||
|
||||
void MainAppMenu::computeFrame() {
|
||||
this->updateMetaBinds();
|
||||
this->playerCursor.updatePosition();
|
||||
|
||||
if (this->enterReleased) {
|
||||
if (this->playerCursor.getPosition().y == 0) {
|
||||
this->menuStack->push(std::make_shared<GameSettingsAppMenu>(this->menuStack, this->settings, this->renderWindow));
|
||||
}
|
||||
if (this->playerCursor.getPosition().y == 1) {
|
||||
//TODO
|
||||
}
|
||||
if (this->playerCursor.getPosition().y == 2) {
|
||||
//TODO
|
||||
}
|
||||
}
|
||||
if (this->escReleased) {
|
||||
this->menuStack->pop();
|
||||
}
|
||||
}
|
||||
|
||||
void MainAppMenu::drawFrame() const {
|
||||
this->renderWindow->clear(sf::Color(200, 200, 200));
|
||||
|
||||
float sizeMultiplier = this->settings->getWindowSizeMultiplier();
|
||||
|
||||
sf::Font font("data/fonts/pressstart/prstartk.ttf");
|
||||
sf::Text text(font, "", this->settings->getWindowSizeMultiplier() * 2);
|
||||
text.setFillColor(sf::Color(0, 0, 0));
|
||||
text.setOutlineColor(sf::Color(255, 255, 255));
|
||||
|
||||
text.setString("JMINOS");
|
||||
text.setOrigin(text.getLocalBounds().getCenter());
|
||||
text.setPosition(sf::Vector2f({sizeMultiplier * 40, sizeMultiplier * 10}));
|
||||
this->renderWindow->draw(text);
|
||||
|
||||
text.setString("PLAY");
|
||||
text.setOutlineThickness((this->playerCursor.getPosition().y == 0) ? (sizeMultiplier / 2) : 0);
|
||||
text.setOrigin(text.getLocalBounds().getCenter());
|
||||
text.setPosition(sf::Vector2f({sizeMultiplier * 40, sizeMultiplier * 20}));
|
||||
this->renderWindow->draw(text);
|
||||
|
||||
text.setString("SETTINGS");
|
||||
text.setOutlineThickness((this->playerCursor.getPosition().y == 1) ? (sizeMultiplier / 2) : 0);
|
||||
text.setOrigin(text.getLocalBounds().getCenter());
|
||||
text.setPosition(sf::Vector2f({sizeMultiplier * 40, sizeMultiplier * 30}));
|
||||
this->renderWindow->draw(text);
|
||||
|
||||
text.setString("INFO");
|
||||
text.setOutlineThickness((this->playerCursor.getPosition().y == 2) ? (sizeMultiplier / 2) : 0);
|
||||
text.setOrigin(text.getLocalBounds().getCenter());
|
||||
text.setPosition(sf::Vector2f({sizeMultiplier * 40, sizeMultiplier * 40}));
|
||||
this->renderWindow->draw(text);
|
||||
|
||||
this->renderWindow->display();
|
||||
}
|
||||
21
src/GraphicalUI/AppMenus/MainAppMenu.h
Normal file
21
src/GraphicalUI/AppMenus/MainAppMenu.h
Normal file
@@ -0,0 +1,21 @@
|
||||
#pragma once
|
||||
|
||||
#include "AppMenu.h"
|
||||
#include "PlayerCursor.h"
|
||||
|
||||
#include <stack>
|
||||
#include <memory>
|
||||
#include <SFML/Graphics.hpp>
|
||||
|
||||
|
||||
class MainAppMenu : public AppMenu {
|
||||
private:
|
||||
PlayerCursor playerCursor;
|
||||
|
||||
public:
|
||||
MainAppMenu(std::shared_ptr<MenuStack> menuStack, std::shared_ptr<Settings> settings, std::shared_ptr<sf::RenderWindow> renderWindow);
|
||||
|
||||
void computeFrame();
|
||||
|
||||
void drawFrame() const;
|
||||
};
|
||||
105
src/GraphicalUI/AppMenus/PlayerCursor.cpp
Normal file
105
src/GraphicalUI/AppMenus/PlayerCursor.cpp
Normal file
@@ -0,0 +1,105 @@
|
||||
#include "PlayerCursor.h"
|
||||
|
||||
#include "../Keybinds.h"
|
||||
#include "../Settings.h"
|
||||
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
#include <SFML/Graphics.hpp>
|
||||
|
||||
static const int MENU_DAS = FRAMES_PER_SECOND / 2;
|
||||
|
||||
|
||||
PlayerCursor::PlayerCursor(std::vector<unsigned int> rows) :
|
||||
rows(rows) {
|
||||
|
||||
this->position = sf::Vector2u({0, 0});
|
||||
this->leftDAS = 0;
|
||||
this->rightDAS = 0;
|
||||
this->upDAS = 0;
|
||||
this->downDAS = 0;
|
||||
}
|
||||
|
||||
void PlayerCursor::updatePosition() {
|
||||
(sf::Keyboard::isKeyPressed(sfKey::Left)) ? (this->leftDAS++) : (this->leftDAS = 0);
|
||||
if (this->shouldMove(this->leftDAS)) {
|
||||
this->moveLeft();
|
||||
}
|
||||
|
||||
(sf::Keyboard::isKeyPressed(sfKey::Right)) ? (this->rightDAS++) : (this->rightDAS = 0);
|
||||
if (this->shouldMove(this->rightDAS)) {
|
||||
this->moveRight();
|
||||
}
|
||||
|
||||
(sf::Keyboard::isKeyPressed(sfKey::Up)) ? (this->upDAS++) : (this->upDAS = 0);
|
||||
if (this->shouldMove(this->upDAS)) {
|
||||
this->moveUp();
|
||||
}
|
||||
|
||||
(sf::Keyboard::isKeyPressed(sfKey::Down)) ? (this->downDAS++) : (this->downDAS = 0);
|
||||
if (this->shouldMove(this->downDAS)) {
|
||||
this->moveDown();
|
||||
}
|
||||
}
|
||||
|
||||
void PlayerCursor::goToPosition(const sf::Vector2u& newPosition) {
|
||||
if (this->rows.size() > newPosition.y) {
|
||||
if (this->rows.at(newPosition.y) > newPosition.x) {
|
||||
this->position = newPosition;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const sf::Vector2u& PlayerCursor::getPosition() const {
|
||||
return this->position;
|
||||
}
|
||||
|
||||
bool PlayerCursor::shouldMove(int DAS) const {
|
||||
return (DAS == 1
|
||||
|| (DAS > MENU_DAS && (DAS % 5) == 0)
|
||||
|| (DAS > (FRAMES_PER_SECOND * 2)));
|
||||
}
|
||||
|
||||
void PlayerCursor::moveLeft() {
|
||||
if (this->position.x == 0) {
|
||||
this->position.x = this->rows.at(this->position.y) - 1;
|
||||
}
|
||||
else {
|
||||
this->position.x--;
|
||||
}
|
||||
}
|
||||
|
||||
void PlayerCursor::moveRight() {
|
||||
if (this->position.x == this->rows.at(this->position.y) - 1) {
|
||||
this->position.x = 0;
|
||||
}
|
||||
else {
|
||||
this->position.x++;
|
||||
}
|
||||
}
|
||||
|
||||
void PlayerCursor::moveUp() {
|
||||
if (this->position.y == 0) {
|
||||
this->position.y = this->rows.size() - 1;
|
||||
}
|
||||
else {
|
||||
this->position.y--;
|
||||
}
|
||||
|
||||
if (this->position.x >= this->rows.at(this->position.y)) {
|
||||
this->position.x = this->rows.at(this->position.y) - 1;
|
||||
}
|
||||
}
|
||||
|
||||
void PlayerCursor::moveDown() {
|
||||
if (this->position.y == this->rows.size() - 1) {
|
||||
this->position.y = 0;
|
||||
}
|
||||
else {
|
||||
this->position.y++;
|
||||
}
|
||||
|
||||
if (this->position.x >= this->rows.at(this->position.y)) {
|
||||
this->position.x = this->rows.at(this->position.y) - 1;
|
||||
}
|
||||
}
|
||||
35
src/GraphicalUI/AppMenus/PlayerCursor.h
Normal file
35
src/GraphicalUI/AppMenus/PlayerCursor.h
Normal file
@@ -0,0 +1,35 @@
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include <SFML/Graphics.hpp>
|
||||
|
||||
|
||||
class PlayerCursor {
|
||||
private:
|
||||
std::vector<unsigned int> rows;
|
||||
sf::Vector2u position;
|
||||
int leftDAS;
|
||||
int rightDAS;
|
||||
int upDAS;
|
||||
int downDAS;
|
||||
|
||||
public:
|
||||
PlayerCursor(std::vector<unsigned int> rows);
|
||||
|
||||
void updatePosition();
|
||||
|
||||
void goToPosition(const sf::Vector2u& newPosition);
|
||||
|
||||
const sf::Vector2u& getPosition() const;
|
||||
|
||||
private:
|
||||
bool shouldMove(int DAS) const;
|
||||
|
||||
void moveLeft();
|
||||
|
||||
void moveRight();
|
||||
|
||||
void moveUp();
|
||||
|
||||
void moveDown();
|
||||
};
|
||||
52
src/GraphicalUI/GraphApp.cpp
Normal file
52
src/GraphicalUI/GraphApp.cpp
Normal file
@@ -0,0 +1,52 @@
|
||||
#include "GraphApp.h"
|
||||
|
||||
#include "AppMenus/AppMenu.h"
|
||||
#include "AppMenus/MainAppMenu.h"
|
||||
#include "Settings.h"
|
||||
|
||||
#include <stack>
|
||||
#include <memory>
|
||||
#include <SFML/Graphics.hpp>
|
||||
|
||||
static const double TIME_BETWEEN_FRAMES = (1000.f / FRAMES_PER_SECOND);
|
||||
|
||||
|
||||
GraphApp::GraphApp() {
|
||||
this->settings = std::make_shared<Settings>();
|
||||
this->menuStack = std::make_shared<MenuStack>();
|
||||
this->renderWindow = std::make_shared<sf::RenderWindow>();
|
||||
}
|
||||
|
||||
void GraphApp::run() {
|
||||
changeVideoMode(*this->renderWindow, this->settings->getVideoMode());
|
||||
this->menuStack->push(std::make_shared<MainAppMenu>(this->menuStack, this->settings, this->renderWindow));
|
||||
|
||||
bool quit = false;
|
||||
double timeAtNextFrame = 0;
|
||||
sf::Clock clock;
|
||||
while (!quit) {
|
||||
while (const std::optional event = this->renderWindow->pollEvent()) {
|
||||
if (event->is<sf::Event::Closed>()) {
|
||||
quit = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!quit) {
|
||||
if (clock.getElapsedTime().asMilliseconds() > timeAtNextFrame) {
|
||||
this->menuStack->top()->computeFrame();
|
||||
|
||||
if (this->menuStack->empty()) {
|
||||
quit = true;
|
||||
}
|
||||
else {
|
||||
this->menuStack->top()->drawFrame();
|
||||
}
|
||||
|
||||
while (clock.getElapsedTime().asMilliseconds() > timeAtNextFrame) {
|
||||
timeAtNextFrame += TIME_BETWEEN_FRAMES;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
renderWindow->close();
|
||||
}
|
||||
21
src/GraphicalUI/GraphApp.h
Normal file
21
src/GraphicalUI/GraphApp.h
Normal file
@@ -0,0 +1,21 @@
|
||||
#pragma once
|
||||
|
||||
#include "AppMenus/AppMenu.h"
|
||||
#include "Settings.h"
|
||||
|
||||
#include <stack>
|
||||
#include <memory>
|
||||
#include <SFML/Graphics.hpp>
|
||||
|
||||
|
||||
class GraphApp {
|
||||
private:
|
||||
std::shared_ptr<Settings> settings;
|
||||
std::shared_ptr<MenuStack> menuStack;
|
||||
std::shared_ptr<sf::RenderWindow> renderWindow;
|
||||
|
||||
public:
|
||||
GraphApp();
|
||||
|
||||
void run();
|
||||
};
|
||||
86
src/GraphicalUI/Keybinds.cpp
Normal file
86
src/GraphicalUI/Keybinds.cpp
Normal file
@@ -0,0 +1,86 @@
|
||||
#include "Keybinds.h"
|
||||
|
||||
#include "../Core/Action.h"
|
||||
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <fstream>
|
||||
#include <SFML/Graphics.hpp>
|
||||
|
||||
|
||||
Keybinds::Keybinds(int layoutNumber) :
|
||||
layoutNumber(layoutNumber) {
|
||||
|
||||
for (Action action : ACTION_LIST_IN_ORDER) {
|
||||
this->keybinds.insert({action, std::set<sfKey>()});
|
||||
}
|
||||
|
||||
this->loadKeybindsFromFile();
|
||||
}
|
||||
|
||||
void Keybinds::loadKeybindsFromFile() {
|
||||
std::ifstream layoutFile("data/config/keybinds/layout" + std::to_string(this->layoutNumber) + ".bin", std::ios::binary);
|
||||
|
||||
for (Action action : ACTION_LIST_IN_ORDER) {
|
||||
this->keybinds.at(action).clear();
|
||||
}
|
||||
|
||||
char byte;
|
||||
while (layoutFile.peek() != EOF) {
|
||||
layoutFile.get(byte);
|
||||
Action action = Action(byte);
|
||||
|
||||
bool separatorMet = false;
|
||||
while (!separatorMet) {
|
||||
layoutFile.get(byte);
|
||||
if (byte == (char) 0xFF) {
|
||||
separatorMet = true;
|
||||
}
|
||||
else {
|
||||
this->keybinds.at(action).insert(sfKey(byte));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Keybinds::saveKeybindsToFile() const {
|
||||
std::ofstream layoutFile("data/config/keybinds/layout" + std::to_string(this->layoutNumber) + ".bin", std::ios::trunc | std::ios::binary);
|
||||
|
||||
char byte;
|
||||
for (Action action : ACTION_LIST_IN_ORDER) {
|
||||
byte = action;
|
||||
layoutFile.write(&byte, 1);
|
||||
|
||||
for (sfKey key : this->keybinds.at(action)) {
|
||||
byte = (int) key;
|
||||
layoutFile.write(&byte, 1);
|
||||
}
|
||||
|
||||
byte = 0xFF;
|
||||
layoutFile.write(&byte, 1);
|
||||
}
|
||||
}
|
||||
|
||||
void Keybinds::addKey(Action action, sfKey key) {
|
||||
this->keybinds.at(action).insert(key);
|
||||
}
|
||||
|
||||
void Keybinds::clearKeys(Action action) {
|
||||
this->keybinds.at(action).clear();
|
||||
}
|
||||
|
||||
const std::set<Action> Keybinds::getActions(sfKey key) const {
|
||||
std::set<Action> actions;
|
||||
|
||||
for (const auto& [action, keys] : this->keybinds) {
|
||||
if (keys.contains(key)) {
|
||||
actions.insert(action);
|
||||
}
|
||||
}
|
||||
|
||||
return actions;
|
||||
}
|
||||
|
||||
const std::set<sfKey>& Keybinds::getKeybinds(Action action) const {
|
||||
return this->keybinds.at(action);
|
||||
}
|
||||
34
src/GraphicalUI/Keybinds.h
Normal file
34
src/GraphicalUI/Keybinds.h
Normal file
@@ -0,0 +1,34 @@
|
||||
#pragma once
|
||||
|
||||
#include "../Core/Action.h"
|
||||
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <SFML/Graphics.hpp>
|
||||
|
||||
using sfKey = sf::Keyboard::Key;
|
||||
|
||||
static const int NUMBER_OF_KEYBINDS = 5;
|
||||
static const int CUSTOMIZABLE_KEYBINDS = NUMBER_OF_KEYBINDS - 1;
|
||||
|
||||
|
||||
class Keybinds {
|
||||
private:
|
||||
std::map<Action, std::set<sfKey>> keybinds;
|
||||
int layoutNumber;
|
||||
|
||||
public:
|
||||
Keybinds(int layoutNumber);
|
||||
|
||||
void loadKeybindsFromFile();
|
||||
|
||||
void saveKeybindsToFile() const;
|
||||
|
||||
void addKey(Action action, sfKey key);
|
||||
|
||||
void clearKeys(Action action);
|
||||
|
||||
const std::set<Action> getActions(sfKey key) const;
|
||||
|
||||
const std::set<sfKey>& getKeybinds(Action action) const;
|
||||
};
|
||||
21
src/GraphicalUI/PiecesType.h
Normal file
21
src/GraphicalUI/PiecesType.h
Normal file
@@ -0,0 +1,21 @@
|
||||
#pragma once
|
||||
|
||||
|
||||
enum PiecesType {
|
||||
CONVEX_PIECES,
|
||||
HOLELESS_PIECES,
|
||||
OTHER_PIECES,
|
||||
ALL_PIECES,
|
||||
SINGLE_PIECE
|
||||
};
|
||||
|
||||
|
||||
inline int getSizeOfPieces(PiecesType type) {
|
||||
if (type < SINGLE_PIECE) return 0;
|
||||
|
||||
else return (type - SINGLE_PIECE + 1);
|
||||
}
|
||||
|
||||
inline PiecesType createSinglePieceType(int size) {
|
||||
return PiecesType(SINGLE_PIECE + size - 1);
|
||||
}
|
||||
199
src/GraphicalUI/Settings.cpp
Normal file
199
src/GraphicalUI/Settings.cpp
Normal file
@@ -0,0 +1,199 @@
|
||||
#include "Settings.h"
|
||||
|
||||
#include "../Core/Menu.h"
|
||||
#include "Keybinds.h"
|
||||
|
||||
#include <SFML/Graphics.hpp>
|
||||
#include <fstream>
|
||||
|
||||
static const sf::Vector2u BASE_WINDOW_SIZE = {80, 50};
|
||||
static const int WINDOW_SIZE_MULTIPLIERS[] = {4, 6, 10, 14, 20};
|
||||
static const int WINDOW_SIZE_LAST_MODE = (sizeof(WINDOW_SIZE_MULTIPLIERS) / sizeof(int)) - 1;
|
||||
|
||||
|
||||
Settings::Settings() {
|
||||
for (int i = 1; i <= MAXIMUM_PIECES_SIZE; i++) {
|
||||
this->menu.getPiecesList().loadPieces(i);
|
||||
}
|
||||
|
||||
this->keybinds.reserve(NUMBER_OF_KEYBINDS);
|
||||
for (int i = 0; i < NUMBER_OF_KEYBINDS; i++) {
|
||||
this->keybinds.emplace_back(i);
|
||||
}
|
||||
|
||||
this->loadSettingsFromFile();
|
||||
}
|
||||
|
||||
void Settings::loadSettingsFromFile() {
|
||||
std::ifstream settingsFile("data/config/settings.bin", std::ios::binary);
|
||||
char byte;
|
||||
|
||||
// keybind layout
|
||||
settingsFile.get(byte);
|
||||
this->chosenKeybinds = byte;
|
||||
|
||||
// window size mode
|
||||
settingsFile.get(byte);
|
||||
this->windowSizeMode = byte;
|
||||
|
||||
// gamemode
|
||||
settingsFile.get(byte);
|
||||
this->gamemode = Gamemode(byte);
|
||||
|
||||
// board width
|
||||
settingsFile.get(byte);
|
||||
this->menu.setBoardWidth(byte);
|
||||
|
||||
// board height
|
||||
settingsFile.get(byte);
|
||||
this->menu.setBoardHeight(byte);
|
||||
|
||||
// piece distribution
|
||||
settingsFile.get(byte);
|
||||
//TODO
|
||||
|
||||
// selected pieces
|
||||
char pieceType;
|
||||
char pieceValue;
|
||||
this->selectedPieces.clear();
|
||||
while (settingsFile.get(pieceType)) {
|
||||
if (settingsFile.eof()) break;
|
||||
|
||||
settingsFile.get(pieceValue);
|
||||
this->selectedPieces.push_back({PiecesType(pieceType), pieceValue});
|
||||
}
|
||||
this->confirmSelectedPieces();
|
||||
}
|
||||
|
||||
void Settings::saveSettingsToFile() const {
|
||||
std::ofstream settingsFile("data/config/settings.bin", std::ios::trunc | std::ios::binary);
|
||||
char byte;
|
||||
|
||||
// keybind layout
|
||||
byte = this->chosenKeybinds;
|
||||
settingsFile.write(&byte, 1);
|
||||
|
||||
// window size mode
|
||||
byte = this->windowSizeMode;
|
||||
settingsFile.write(&byte, 1);
|
||||
|
||||
// gamemode
|
||||
byte = this->gamemode;
|
||||
settingsFile.write(&byte, 1);
|
||||
|
||||
// board width
|
||||
byte = this->menu.getBoardWidth();
|
||||
settingsFile.write(&byte, 1);
|
||||
|
||||
// board height
|
||||
byte = this->menu.getBoardHeight();
|
||||
settingsFile.write(&byte, 1);
|
||||
|
||||
// piece distribution
|
||||
//TODO
|
||||
settingsFile.write(&byte, 1);
|
||||
|
||||
// selected pieces
|
||||
for (const auto& [type, value] : this->selectedPieces) {
|
||||
byte = type;
|
||||
settingsFile.write(&byte, 1);
|
||||
byte = value;
|
||||
settingsFile.write(&byte, 1);
|
||||
}
|
||||
}
|
||||
|
||||
bool Settings::selectNextKeybinds() {
|
||||
if (this->chosenKeybinds < NUMBER_OF_KEYBINDS) {
|
||||
this->chosenKeybinds++;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Settings::selectPreviousKeybinds() {
|
||||
if (this->chosenKeybinds > 0) {
|
||||
this->chosenKeybinds--;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
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++;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Settings::shortenWindow() {
|
||||
if (this->windowSizeMode > 0) {
|
||||
this->windowSizeMode--;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void Settings::selectPieces(PiecesType type, int value) {
|
||||
this->selectedPieces.emplace_back(type, value);
|
||||
}
|
||||
|
||||
void Settings::unselectPieces(int index) {
|
||||
if (index >= this->selectedPieces.size()) return;
|
||||
|
||||
this->selectedPieces.erase(this->selectedPieces.begin() + index);
|
||||
}
|
||||
|
||||
void Settings::confirmSelectedPieces() {
|
||||
this->menu.getPiecesList().unselectAll();
|
||||
|
||||
for (const auto& [type, value] : this->selectedPieces) {
|
||||
int size = getSizeOfPieces(type);
|
||||
|
||||
if (size == 0) {
|
||||
switch (type) {
|
||||
case CONVEX_PIECES : {this->menu.getPiecesList().selectConvexPieces(value); break;}
|
||||
case HOLELESS_PIECES : {this->menu.getPiecesList().selectHolelessPieces(value); break;}
|
||||
case OTHER_PIECES : {this->menu.getPiecesList().selectOtherPieces(value); break;}
|
||||
case ALL_PIECES : {this->menu.getPiecesList().selectAllPieces(value); break;}
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (size > MAXIMUM_PIECES_SIZE) return;
|
||||
|
||||
this->menu.getPiecesList().selectPiece(size, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Menu& Settings::getMenu() {
|
||||
return this->menu;
|
||||
}
|
||||
|
||||
Keybinds& Settings::getKeybinds() {
|
||||
return this->keybinds.at(this->chosenKeybinds);
|
||||
}
|
||||
|
||||
Gamemode Settings::getGamemode() const {
|
||||
return this->gamemode;
|
||||
}
|
||||
|
||||
int Settings::getWindowSizeMultiplier() const {
|
||||
return WINDOW_SIZE_MULTIPLIERS[this->windowSizeMode];
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
66
src/GraphicalUI/Settings.h
Normal file
66
src/GraphicalUI/Settings.h
Normal file
@@ -0,0 +1,66 @@
|
||||
#pragma once
|
||||
|
||||
#include "../Core/Menu.h"
|
||||
#include "Keybinds.h"
|
||||
#include "PiecesType.h"
|
||||
|
||||
#include <SFML/Graphics.hpp>
|
||||
#include <vector>
|
||||
|
||||
static const int MAXIMUM_BOARD_WIDTH = 40;
|
||||
static const int MAXIMUM_BOARD_HEIGHT = 40;
|
||||
|
||||
//#define __JMINOS_RELEASE__
|
||||
#ifdef __JMINOS_RELEASE__
|
||||
static const int MAXIMUM_PIECES_SIZE = 15;
|
||||
#else
|
||||
static const int MAXIMUM_PIECES_SIZE = 10;
|
||||
#endif
|
||||
|
||||
|
||||
class Settings {
|
||||
private:
|
||||
Menu menu;
|
||||
std::vector<Keybinds> keybinds;
|
||||
int chosenKeybinds;
|
||||
Gamemode gamemode;
|
||||
int windowSizeMode;
|
||||
std::vector<std::pair<PiecesType, int>> selectedPieces;
|
||||
|
||||
public:
|
||||
Settings();
|
||||
|
||||
void loadSettingsFromFile();
|
||||
|
||||
void saveSettingsToFile() const;
|
||||
|
||||
bool selectNextKeybinds();
|
||||
|
||||
bool selectPreviousKeybinds();
|
||||
|
||||
bool canModifyCurrentKeybinds() const;
|
||||
|
||||
void setGamemode(Gamemode gamemode);
|
||||
|
||||
bool widenWindow();
|
||||
|
||||
bool shortenWindow();
|
||||
|
||||
void selectPieces(PiecesType type, int value);
|
||||
|
||||
void unselectPieces(int index);
|
||||
|
||||
void confirmSelectedPieces();
|
||||
|
||||
Menu& getMenu();
|
||||
|
||||
Keybinds& getKeybinds();
|
||||
|
||||
Gamemode getGamemode() const;
|
||||
|
||||
int getWindowSizeMultiplier() const;
|
||||
|
||||
const sf::VideoMode getVideoMode() const;
|
||||
|
||||
const std::vector<std::pair<PiecesType, int>>& getSelectedPieces() const;
|
||||
};
|
||||
156
src/GraphicalUI/main.cpp
Normal file
156
src/GraphicalUI/main.cpp
Normal file
@@ -0,0 +1,156 @@
|
||||
#include "GraphApp.h"
|
||||
#include "../Pieces/PiecesFiles.h"
|
||||
|
||||
#include <fstream>
|
||||
|
||||
|
||||
void resetConfigFiles();
|
||||
void resetSettingsFile();
|
||||
void resetKeybindFile(int layout);
|
||||
|
||||
|
||||
int main() {
|
||||
std::srand(std::time(NULL));
|
||||
|
||||
#ifndef __JMINOS_RELEASE__
|
||||
|
||||
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;
|
||||
pf.savePieces(i);
|
||||
}
|
||||
}
|
||||
if (!std::filesystem::exists("data/config/settings.bin")) {
|
||||
resetSettingsFile();
|
||||
}
|
||||
for (int i = 0; i < 5; i++) {
|
||||
if (!std::filesystem::exists("data/config/keybinds/layout" + std::to_string(i) + ".bin")) {
|
||||
resetKeybindFile(i);
|
||||
}
|
||||
}
|
||||
|
||||
// uncomment before compiling release version
|
||||
//resetConfigFiles();
|
||||
|
||||
#endif
|
||||
|
||||
GraphApp UI;
|
||||
UI.run();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
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;
|
||||
|
||||
// keybind layout
|
||||
byte = 0;
|
||||
settingsFile.write(&byte, 1);
|
||||
|
||||
// window size mode
|
||||
byte = 2;
|
||||
settingsFile.write(&byte, 1);
|
||||
|
||||
// gamemode
|
||||
byte = SPRINT;
|
||||
settingsFile.write(&byte, 1);
|
||||
|
||||
// board width
|
||||
byte = 10;
|
||||
settingsFile.write(&byte, 1);
|
||||
|
||||
// board height
|
||||
byte = 20;
|
||||
settingsFile.write(&byte, 1);
|
||||
|
||||
// piece distribution
|
||||
byte = 0;
|
||||
settingsFile.write(&byte, 1);
|
||||
|
||||
// selected pieces
|
||||
byte = ALL_PIECES;
|
||||
settingsFile.write(&byte, 1);
|
||||
byte = 4;
|
||||
settingsFile.write(&byte, 1);
|
||||
}
|
||||
|
||||
void resetKeybindFile(int layout) {
|
||||
if (layout < 0 || layout > 4) return;
|
||||
|
||||
std::ofstream layoutFile("data/config/keybinds/layout" + std::to_string(layout) + ".bin", std::ios::trunc | std::ios::binary);
|
||||
std::map<Action, sfKey> keybinds;
|
||||
|
||||
if (layout != 4) {
|
||||
keybinds.insert({PAUSE, sfKey::P});
|
||||
keybinds.insert({RETRY, sfKey::R});
|
||||
}
|
||||
|
||||
if (layout == 0) {
|
||||
keybinds.insert({MOVE_LEFT, sfKey::Left});
|
||||
keybinds.insert({MOVE_RIGHT, sfKey::Right});
|
||||
keybinds.insert({SOFT_DROP, sfKey::Down});
|
||||
keybinds.insert({HARD_DROP, sfKey::Space});
|
||||
keybinds.insert({ROTATE_CW, sfKey::Up});
|
||||
keybinds.insert({ROTATE_CCW, sfKey::Z});
|
||||
keybinds.insert({ROTATE_180, sfKey::X});
|
||||
keybinds.insert({ROTATE_0, sfKey::LShift});
|
||||
keybinds.insert({HOLD, sfKey::C});
|
||||
}
|
||||
if (layout == 1) {
|
||||
keybinds.insert({MOVE_LEFT, sfKey::Z});
|
||||
keybinds.insert({MOVE_RIGHT, sfKey::C});
|
||||
keybinds.insert({SOFT_DROP, sfKey::X});
|
||||
keybinds.insert({HARD_DROP, sfKey::S});
|
||||
keybinds.insert({ROTATE_CW, sfKey::M});
|
||||
keybinds.insert({ROTATE_CCW, sfKey::Comma});
|
||||
keybinds.insert({ROTATE_180, sfKey::J});
|
||||
keybinds.insert({ROTATE_0, sfKey::K});
|
||||
keybinds.insert({HOLD, sfKey::LShift});
|
||||
}
|
||||
if (layout == 2) {
|
||||
keybinds.insert({MOVE_LEFT, sfKey::A});
|
||||
keybinds.insert({MOVE_RIGHT, sfKey::D});
|
||||
keybinds.insert({SOFT_DROP, sfKey::W});
|
||||
keybinds.insert({HARD_DROP, sfKey::S});
|
||||
keybinds.insert({ROTATE_CW, sfKey::Left});
|
||||
keybinds.insert({ROTATE_CCW, sfKey::Right});
|
||||
keybinds.insert({ROTATE_180, sfKey::Up});
|
||||
keybinds.insert({ROTATE_0, sfKey::Down});
|
||||
keybinds.insert({HOLD, sfKey::RShift});
|
||||
}
|
||||
if (layout == 3) {
|
||||
keybinds.insert({MOVE_LEFT, sfKey::Left});
|
||||
keybinds.insert({MOVE_RIGHT, sfKey::Right});
|
||||
keybinds.insert({SOFT_DROP, sfKey::Down});
|
||||
keybinds.insert({HARD_DROP, sfKey::Up});
|
||||
keybinds.insert({ROTATE_CW, sfKey::E});
|
||||
keybinds.insert({ROTATE_CCW, sfKey::A});
|
||||
keybinds.insert({ROTATE_180, sfKey::Num2});
|
||||
keybinds.insert({ROTATE_0, sfKey::Tab});
|
||||
keybinds.insert({HOLD, sfKey::Z});
|
||||
}
|
||||
|
||||
char byte;
|
||||
for (Action action : ACTION_LIST_IN_ORDER) {
|
||||
byte = action;
|
||||
layoutFile.write(&byte, 1);
|
||||
|
||||
if (keybinds.contains(action)) {
|
||||
byte = (int) keybinds.at(action);
|
||||
layoutFile.write(&byte, 1);
|
||||
}
|
||||
|
||||
byte = 0xFF;
|
||||
layoutFile.write(&byte, 1);
|
||||
}
|
||||
}
|
||||
@@ -58,7 +58,7 @@ void TextApp::run() {
|
||||
default : std::cout << "Invalid answer!" << std::endl;
|
||||
}
|
||||
}
|
||||
std::cout << "===| SEE YA NEXT TIME! |===";
|
||||
std::cout << "===| SEE YA NEXT TIME! |===" << std::endl;
|
||||
}
|
||||
|
||||
void TextApp::choosePieces() {
|
||||
@@ -136,7 +136,6 @@ void TextApp::seeKeybinds() const {
|
||||
|
||||
void TextApp::defaultKeybinds() {
|
||||
this->keybinds.clear();
|
||||
this->keybinds.insert({"quit", QUIT});
|
||||
this->keybinds.insert({"pause", PAUSE});
|
||||
this->keybinds.insert({"retry", RETRY});
|
||||
this->keybinds.insert({"h", HOLD});
|
||||
@@ -171,15 +170,21 @@ void TextApp::startGame() const {
|
||||
|
||||
std::set<Action> playerActions;
|
||||
std::set<Action> lastFrameActions;
|
||||
std::set<Action> metaActions;
|
||||
bool retrying = false;
|
||||
for (std::string action : actions) {
|
||||
try {
|
||||
Action playerAction = this->keybinds.at(action);
|
||||
if (playerAction == PAUSE || playerAction == RETRY || playerAction == quit) {
|
||||
metaActions.insert(playerAction);
|
||||
}
|
||||
else {
|
||||
if (playerAction == SOFT_DROP || playerAction == MOVE_LEFT || playerAction == MOVE_RIGHT) {
|
||||
if (action == "quit") {
|
||||
quit = true;
|
||||
}
|
||||
else {
|
||||
try {
|
||||
Action playerAction = this->keybinds.at(action);
|
||||
if (playerAction == RETRY) {
|
||||
retrying = true;
|
||||
}
|
||||
else if (playerAction == PAUSE) {
|
||||
paused = (!paused);
|
||||
}
|
||||
else if (playerAction == SOFT_DROP || playerAction == MOVE_LEFT || playerAction == MOVE_RIGHT) {
|
||||
playerActions.insert(playerAction);
|
||||
lastFrameActions.insert(playerAction);
|
||||
}
|
||||
@@ -190,19 +195,12 @@ void TextApp::startGame() const {
|
||||
playerActions.insert(playerAction);
|
||||
}
|
||||
}
|
||||
catch (std::exception ignored) {}
|
||||
}
|
||||
catch (std::exception ignored) {}
|
||||
}
|
||||
|
||||
if (metaActions.contains(PAUSE)) {
|
||||
paused = (!paused);
|
||||
}
|
||||
|
||||
if (!paused) {
|
||||
if (metaActions.contains(QUIT)) {
|
||||
quit = true;
|
||||
}
|
||||
else if (metaActions.contains(RETRY)) {
|
||||
if (!paused && !quit) {
|
||||
if (retrying) {
|
||||
game.reset();
|
||||
game.start();
|
||||
}
|
||||
|
||||
26
xmake.lua
26
xmake.lua
@@ -1,13 +1,29 @@
|
||||
add_rules("mode.debug", "mode.release")
|
||||
|
||||
add_requires("sfml 3.0.0")
|
||||
|
||||
set_languages("c++20")
|
||||
|
||||
target("main")
|
||||
set_rundir(".")
|
||||
|
||||
set_optimize("fastest")
|
||||
|
||||
target("core")
|
||||
set_kind("$(kind)")
|
||||
add_files("src/Pieces/*.cpp")
|
||||
add_files("src/Core/*.cpp")
|
||||
|
||||
target("text")
|
||||
set_kind("binary")
|
||||
set_rundir(".")
|
||||
add_files("./src/Pieces/*.cpp")
|
||||
add_files("./src/Core/*.cpp")
|
||||
set_default(false)
|
||||
add_files("./src/TextUI/*.cpp")
|
||||
set_optimize("fastest")
|
||||
add_deps("core")
|
||||
|
||||
target("graph")
|
||||
set_kind("binary")
|
||||
add_files("./src/GraphicalUI/**.cpp")
|
||||
add_deps("core")
|
||||
add_packages("sfml")
|
||||
|
||||
--
|
||||
-- If you want to known more usage about xmake, please see https://xmake.io
|
||||
|
||||
Reference in New Issue
Block a user