on commence l'interface là ouais

This commit is contained in:
2025-03-21 22:52:29 +01:00
parent 021620acef
commit c25abec6ba
20 changed files with 521 additions and 7 deletions

29
README.md Normal file
View File

@@ -0,0 +1,29 @@
# jminos
## Manual build and run
You need to install xmake and have a compiler with c++20 compatibility
### Install the necessary dependencies
``xrepo install sfml``
Package location (on Linux) for IntelliSense:
``home/<user>/.xmake/packages/**``
### Build the project
``cd jminos``
``xmake``
If you need to change the toolchain (for example using gcc):
``xmake f --toolchain=gcc``
### Run the project
Graphical version:
``xmake run graph``
Command line version:
``xmake run text``

View File

View File

View File

View File

View File

0
data/config/settings.bin Normal file
View File

BIN
data/fonts/arial.ttf Normal file

Binary file not shown.

View File

@@ -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 size multiplier of the window, 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

View File

@@ -26,6 +26,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.

View File

@@ -0,0 +1,36 @@
#pragma once
#include "Settings.h"
#include <stack>
#include <memory>
#include <SFML/Graphics.hpp>
class AppMenu {
protected:
std::shared_ptr<std::stack<AppMenu>> menuStack;
std::shared_ptr<Settings> settings;
std::shared_ptr<sf::RenderWindow> renderWindow;
public:
AppMenu(std::shared_ptr<std::stack<AppMenu>> menuStack, std::shared_ptr<Settings> settings, std::shared_ptr<sf::RenderWindow> renderWindow) :
menuStack(menuStack),
settings(settings),
renderWindow(renderWindow)
{
}
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)));
}

View File

@@ -0,0 +1,21 @@
#include "MainAppMenu.h"
#include "AppMenu.h"
#include <stack>
#include <memory>
#include <SFML/Graphics.hpp>
MainAppMenu::MainAppMenu(std::shared_ptr<std::stack<AppMenu>> menuStack, std::shared_ptr<Settings> settings, std::shared_ptr<sf::RenderWindow> renderWindow) :
AppMenu(menuStack, settings, renderWindow) {
}
void MainAppMenu::computeFrame() {
}
void MainAppMenu::drawFrame() const {
}

View File

@@ -0,0 +1,17 @@
#pragma once
#include "AppMenu.h"
#include <stack>
#include <memory>
#include <SFML/Graphics.hpp>
class MainAppMenu : public AppMenu {
public:
MainAppMenu(std::shared_ptr<std::stack<AppMenu>> menuStack, std::shared_ptr<Settings> settings, std::shared_ptr<sf::RenderWindow> renderWindow);
void computeFrame();
void drawFrame() const;
};

View File

@@ -0,0 +1,49 @@
#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 / 60.f);
GraphApp::GraphApp() {
this->settings = std::make_shared<Settings>();
this->menuStack = std::make_shared<std::stack<AppMenu>>();
this->window = std::make_shared<sf::RenderWindow>();
}
void GraphApp::startApp() {
changeVideoMode(*this->window, this->settings->getVideoMode());
this->menuStack->push(MainAppMenu(this->menuStack, this->settings, this->window));
bool quit = false;
double timeAtNextFrame = 0;
sf::Clock clock;
while (!quit) {
while (const std::optional event = this->window->pollEvent()) {
if (event->is<sf::Event::Closed>()) {
quit = true;
}
}
if (!quit) {
if (clock.getElapsedTime().asMilliseconds() > timeAtNextFrame) {
timeAtNextFrame += TIME_BETWEEN_FRAMES;
this->menuStack->top().computeFrame();
if (this->menuStack->empty()) {
quit = true;
}
else {
this->menuStack->top().drawFrame();
}
}
}
}
window->close();
}

View 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<std::stack<AppMenu>> menuStack;
std::shared_ptr<sf::RenderWindow> window;
public:
GraphApp();
void startApp();
};

View File

@@ -0,0 +1,32 @@
#pragma once
#include "../Core/Action.h"
#include <map>
#include <vector>
#include <SFML/Graphics.hpp>
using sfKey = sf::Keyboard::Key;
class Keybinds {
private:
std::map<Action, std::vector<sfKey>> keybinds;
public:
Keybinds();
void loadKeybindsFromFile();
void saveKeybindsToFile() const;
void createDefaultKeybindsFile() const;
void addKey(Action action, sfKey key);
void clearKeys(Action action);
const std::vector<Action>& getActions(sfKey key) const;
const std::vector<sfKey>& getKeybinds(Action action) const;
};

View File

@@ -0,0 +1,87 @@
#include "Settings.h"
#include "../Core/Menu.h"
#include "Keybinds.h"
#include <SFML/Graphics.hpp>
static const int NUMBER_OF_KEYBINDS = 5;
static const int CUSTOMIZABLE_KEYBINDS = NUMBER_OF_KEYBINDS - 1;
static const sf::Vector2u BASE_WINDOW_SIZE = {80, 50};
static const int WINDOW_SIZE_MULTIPLIERS[] = {4, 6, 9, 14, 20};
static const int WINDOW_SIZE_LAST_MODE = (sizeof(WINDOW_SIZE_MULTIPLIERS) / sizeof(int)) - 1;
Settings::Settings() {
for (int i = 1; i <= 15; i++) {
this->menu.getPiecesList().loadPieces(i);
}
this->loadSettingsFromFile();
}
void Settings::loadSettingsFromFile() {
this->menu.getPiecesList().unselectAll();
this->menu.getPiecesList().selectAllPieces(4);
this->windowSizeMode = 2;
}
void Settings::saveSettingsToFile() const {
}
void Settings::createDefaultSettingsFile() const {
}
bool Settings::selectNextKeybinds() {
if (this->chosenKeybinds < NUMBER_OF_KEYBINDS) {
this->chosenKeybinds++;
}
}
bool Settings::selectPreviousKeybinds() {
if (this->chosenKeybinds > 0) {
this->chosenKeybinds--;
}
}
bool Settings::canModifyCurrentKeybinds() const {
return (this->chosenKeybinds == CUSTOMIZABLE_KEYBINDS);
}
bool Settings::widenWindow() {
if (this->windowSizeMode < WINDOW_SIZE_LAST_MODE) {
this->windowSizeMode++;
}
}
bool Settings::shortenWindow() {
if (this->windowSizeMode > 0) {
this->windowSizeMode--;
}
}
void Settings::setGamemode(Gamemode gamemode) {
this->gamemode = gamemode;
}
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]);
}

View File

@@ -0,0 +1,48 @@
#pragma once
#include "../Core/Menu.h"
#include "Keybinds.h"
#include <SFML/Graphics.hpp>
#include <vector>
class Settings {
private:
Menu menu;
std::vector<Keybinds> keybinds;
int chosenKeybinds;
Gamemode gamemode;
int windowSizeMode;
public:
Settings();
void loadSettingsFromFile();
void saveSettingsToFile() const;
void createDefaultSettingsFile() const;
bool selectNextKeybinds();
bool selectPreviousKeybinds();
bool canModifyCurrentKeybinds() const;
void setGamemode(Gamemode gamemode);
bool widenWindow();
bool shortenWindow();
Menu& getMenu();
Keybinds& getKeybinds();
Gamemode getGamemode() const;
int getWindowSizeMultiplier() const;
const sf::VideoMode& getVideoMode() const;
};

138
src/GraphicalUI/main.cpp Normal file
View File

@@ -0,0 +1,138 @@
#include <SFML/Graphics.hpp>
#include "../Core/Menu.h"
#include "../Pieces/PiecesFiles.h"
#include <iostream>
void setToDefaultConfig();
int main() {
std::srand(std::time(NULL));
sf::RenderWindow window(sf::VideoMode({800, 640}), "My window", sf::Style::Titlebar | sf::Style::Close);
window.setPosition(sf::Vector2i(sf::VideoMode::getDesktopMode().size.x / 2 - 400, sf::VideoMode::getDesktopMode().size.y / 2 - 320));
PiecesFiles pf;
for (int i = 1; i <= 10; i++) {
pf.savePieces(i);
}
Menu m;
m.getPiecesList().loadPieces(10);
m.getPiecesList().selectAllPieces(4);
m.setBoardWidth(10);
m.getPlayerControls().setDAS(6);
m.getPlayerControls().setARR(0);
m.getPlayerControls().setSDR(0);
Game game = m.startGame(SPRINT);
game.start();
sf::Clock clock;
sf::Font font;
if (!font.openFromFile("data/fonts/arial.ttf")) {
std::cout << "aaaaaaaaaaaaaa";
}
sf::Text text(font);
text.setCharacterSize(20);
text.setFillColor(sf::Color::White);
while (window.isOpen()) {
while (const std::optional event = window.pollEvent()) {
if (event->is<sf::Event::Closed>())
window.close();
}
if (clock.getElapsedTime().asMilliseconds() > 16) {
clock.restart();
window.clear(sf::Color::Black);
std::set<Action> actions;
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Key::Left)) {
actions.insert(MOVE_LEFT);
}
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Key::Right)) {
actions.insert(MOVE_RIGHT);
}
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Key::Up)) {
actions.insert(HARD_DROP);
}
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Key::Down)) {
actions.insert(SOFT_DROP);
}
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Key::A)) {
actions.insert(ROTATE_CCW);
}
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Key::E)) {
actions.insert(ROTATE_CW);
}
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Key::Z)) {
actions.insert(HOLD);
}
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Key::Tab)) {
actions.insert(ROTATE_0);
}
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Key::Num2)) {
actions.insert(ROTATE_180);
}
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Key::R)) {
game.reset();
game.start();
}
game.nextFrame(actions);
for (int y = game.getBoard().getBaseHeight() + 5; y >= 0; y--) {
for (int x = 0; x < game.getBoard().getWidth(); x++) {
bool isActivePieceHere = (game.getActivePiece() != nullptr) && (game.getActivePiece()->getPositions().contains(Position{x, y} - game.getActivePiecePosition()));
bool isGhostPieceHere = (game.getActivePiece() != nullptr) && (game.getActivePiece()->getPositions().contains(Position{x, y} - game.ghostPiecePosition()));
Block block = (isActivePieceHere || isGhostPieceHere) ? game.getActivePiece()->getBlockType() : game.getBoard().getBlock(Position{x, y});
sf::RectangleShape cell(sf::Vector2f(20.f, 20.f));
cell.setFillColor(sf::Color(BLOCKS_COLOR[block].red, BLOCKS_COLOR[block].green, BLOCKS_COLOR[block].blue, (isGhostPieceHere && !isActivePieceHere) ? 150 : 255));
cell.setPosition(sf::Vector2f(x*20, (game.getBoard().getBaseHeight() + 10 - y)*20));
window.draw(cell);
}
}
if (game.getNextPieces().size() > 0) {
for (int y = 10; y >= 0; y--) {
for (int x = 0; x <= 10; x++) {
Block block = game.getNextPieces().at(0).getBlockType();
sf::RectangleShape cell(sf::Vector2f(20.f, 20.f));
cell.setPosition(sf::Vector2f((x + 2 + game.getBoard().getWidth())*20, (game.getBoard().getBaseHeight() - y)*20));
if (game.getNextPieces().at(0).getPositions().contains(Position({x, y}))) {
cell.setFillColor(sf::Color(BLOCKS_COLOR[block].red, BLOCKS_COLOR[block].green, BLOCKS_COLOR[block].blue));
}
else {
cell.setFillColor(sf::Color(0, 0, 0));
}
window.draw(cell);
}
}
}
if (game.getHeldPiece() != nullptr) {
for (int y = 10; y >= 0; y--) {
for (int x = 0; x <= 10; x++) {
Block block = game.getHeldPiece()->getBlockType();
sf::RectangleShape cell(sf::Vector2f(20.f, 20.f));
cell.setPosition(sf::Vector2f((x + 12 + game.getBoard().getWidth())*20, (game.getBoard().getBaseHeight() - y)*20));
if (game.getHeldPiece()->getPositions().contains(Position({x, y}))) {
cell.setFillColor(sf::Color(BLOCKS_COLOR[block].red, BLOCKS_COLOR[block].green, BLOCKS_COLOR[block].blue));
}
else {
cell.setFillColor(sf::Color(0, 0, 0));
}
window.draw(cell);
}
}
}
text.setPosition(sf::Vector2f(12*20, (game.getBoard().getBaseHeight() - 5)*20));
text.setString(sf::String(std::to_string(game.getClearedLines())));
window.draw(text);
window.display();
}
}
}

View File

@@ -1,13 +1,20 @@
set_languages("c++20")
target("main")
set_kind("binary")
set_optimize("fastest")
set_rundir(".")
add_requires("sfml")
target("text")
set_kind("binary")
add_files("./src/Pieces/*.cpp")
add_files("./src/Core/*.cpp")
add_files("./src/TextUI/*.cpp")
set_optimize("fastest")
target("graph")
set_kind("binary")
add_files("./src/Pieces/*.cpp")
add_files("./src/Core/*.cpp")
add_files("./src/GraphicalUI/*.cpp")
add_packages("sfml")
--
-- If you want to known more usage about xmake, please see https://xmake.io