Compare commits
13 Commits
d5ac79559e
...
assets
| Author | SHA1 | Date | |
|---|---|---|---|
| d50714ef8c | |||
| 3d1feb6295 | |||
| 7a96136631 | |||
| fc6348cebc | |||
| 0bb25d4628 | |||
| 69c07abcec | |||
| 774aa1422f | |||
| 4dfeda0957 | |||
| 05bb79d4a9 | |||
| e0ab6a4828 | |||
| a62b3c018d | |||
| 46ebb88ef2 | |||
| de14978b01 |
36
.github/workflows/ubuntu.yml
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
name: Linux arm64
|
||||
run-name: Build And Test
|
||||
|
||||
on: [push]
|
||||
|
||||
|
||||
env:
|
||||
XMAKE_ROOT: y
|
||||
|
||||
jobs:
|
||||
Build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check out repository code
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Prepare XMake
|
||||
uses: xmake-io/github-action-setup-xmake@v1
|
||||
with:
|
||||
xmake-version: latest
|
||||
actions-cache-folder: '.xmake-cache'
|
||||
actions-cache-key: 'ubuntu'
|
||||
|
||||
- name: Install SFML
|
||||
run: |
|
||||
apt update
|
||||
apt install -y libsfml-dev
|
||||
|
||||
- name: XMake config
|
||||
run: xmake f -p linux -y
|
||||
|
||||
- name: Build
|
||||
run: xmake
|
||||
|
||||
- name: Test
|
||||
run: xmake test
|
||||
2
.gitignore
vendored
@@ -10,7 +10,7 @@ build/
|
||||
|
||||
# personnal documentation
|
||||
doc/*.txt
|
||||
doc/*.violet.html
|
||||
doc/diagrams/*.violet.html
|
||||
doc/mockups/*
|
||||
|
||||
# data files
|
||||
|
||||
86
README.md
@@ -1,22 +1,35 @@
|
||||
# jminos
|
||||
|
||||
Modern stacker game with every polyominos from size 1 to 15, made in C++ with [SFML 3](https://www.sfml-dev.org/)!
|
||||
Modern stacker game with every polyominoes from size 1 to 15, made in C++ with [SFML 3](https://www.sfml-dev.org/)!
|
||||
|
||||
## 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 for Windows 11 and WSL2 Ubuntu.
|
||||
If your OS isn't compactible with either of theses two, you can try [manually building the project](#manual-build).
|
||||
|
||||
## How to play
|
||||
|
||||
### General
|
||||
Choose which pieces you want to play with and stack them up until you either win or top out!
|
||||
Make full lines to make them dissapear.
|
||||
Use the different spins to kick the pieces in spot you couldn't imagine were attaignable!
|
||||
Each gamemode has its own objective, but you can play as you wish.
|
||||
|
||||
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.
|
||||
|
||||
## Features
|
||||
|
||||
- Every polyominoes up to pentedecaminoes!
|
||||
- 7bag with proportionnality for each polyomino size!
|
||||
- Customizable board 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
|
||||
|
||||
@@ -26,6 +39,20 @@ If you want to know more details about the generation and classification of poly
|
||||
- MASTER : clear 200 lines at levels higher than maximum gravity!
|
||||
- ZEN : practice indefinitely in this mode with no gravity!
|
||||
|
||||
### Screenshots
|
||||
|
||||
Pentedecamino jumpscare
|
||||

|
||||
|
||||
Pieces select screen
|
||||

|
||||
|
||||
AutoRS demonstration
|
||||

|
||||
|
||||
0° spins demonstration
|
||||

|
||||
|
||||
## Manual build
|
||||
|
||||
This project uses xmake for compiling, xmake is cross-platform and works in most OS, xmake also automatically install supported librairies.
|
||||
@@ -43,12 +70,49 @@ 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 build text``
|
||||
``xmake run text``
|
||||
|
||||
### Create a release version (xmake packaging ???)
|
||||
### Package the project
|
||||
|
||||
// TODO when the game is finished //
|
||||
|
||||
## Benchmarks
|
||||
|
||||
### One-sided n-polyominoes
|
||||
|
||||
| n | Number | Generation | File storing | File retrieving | File size |
|
||||
| - | - | - | - | - | - |
|
||||
| 1 | 1 | 0s 0.005622ms | 0s 0.750955ms | 0s 0.014016ms | 2 bytes |
|
||||
| 2 | 1 | 0s 0.005758ms | 0s 0.181323ms | 0s 0.012256ms | 3 bytes |
|
||||
| 3 | 2 | 0s 0.017525ms | 0s 0.054497ms | 0s 0.006431ms | 8 bytes |
|
||||
| 4 | 7 | 0s 0.050554ms | 0s 0.067617ms | 0s 0.010984ms | 35 bytes |
|
||||
| 5 | 18 | 0s 0.191971ms | 0s 0.109905ms | 0s 0.021234ms | 108 bytes |
|
||||
| 6 | 60 | 0s 0.651757ms | 0s 0.327465ms | 0s 0.04558ms | 420 bytes |
|
||||
| 7 | 196 | 0s 3.38847ms | 0s 1.94434ms | 0s 0.258777ms | 1568 bytes |
|
||||
| 8 | 704 | 0s 22.7411ms | 0s 10.0103ms | 0s 1.34813ms | 6336 bytes |
|
||||
| 9 | 2500 | 0s 66.2949ms | 0s 20.6137ms | 0s 2.56374ms | 25000 bytes |
|
||||
| 10 | 9189 | 0s 194.764ms | 0s 84.5884ms | 0s 9.64467ms | 101079 bytes |
|
||||
| 11 | 33896 | 0s 759.182ms | 0s 378.494ms | 0s 44.1424ms | 406752 bytes |
|
||||
| 12 | 126759 | 2s 709.277ms | 1s 530.34ms | 0s 155ms | 1647867 bytes |
|
||||
| 13 | 476270 | 10s 668.308ms | 7s 395.512ms | 0s 765.601ms | 6667780 bytes |
|
||||
| 14 | 1802312 | 45s 606.597ms | 32s 28.7977ms | 2s 919.653ms | 27034680 bytes |
|
||||
| 15 | ~6M | ~5mn | ~5mn | ~10s | ~100 MB |
|
||||
|
||||
_File storing includes normalizing and sorting all polyominoes before writing them to the file._
|
||||
If you want to know more details about the generation and storage of polyominoes, [check the documentation](/doc/)!
|
||||
|
||||
Run it yourself by typing:
|
||||
``xmake build benchmark``
|
||||
``xmake run benchmark``
|
||||
|
||||
## Credits
|
||||
|
||||
|
||||
|
Before Width: | Height: | Size: 164 KiB After Width: | Height: | Size: 164 KiB |
|
Before Width: | Height: | Size: 54 KiB After Width: | Height: | Size: 54 KiB |
@@ -55,5 +55,5 @@ The settings file has the following format:
|
||||
- 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 10.
|
||||
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.
|
||||
|
||||
BIN
doc/readme/big_piece.png
Normal file
|
After Width: | Height: | Size: 30 KiB |
BIN
doc/readme/pieces_selection.png
Normal file
|
After Width: | Height: | Size: 42 KiB |
BIN
doc/readme/rotation_0.gif
Normal file
|
After Width: | Height: | Size: 614 KiB |
BIN
doc/readme/rotations.gif
Normal file
|
After Width: | Height: | Size: 779 KiB |
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include <utility>
|
||||
#include <cstdlib>
|
||||
#include <iostream>
|
||||
#include <optional>
|
||||
|
||||
|
||||
GameBoard::GameBoard(int boardWidth, int boardHeight, const std::shared_ptr<PiecesList>& piecesList, int nextQueueLength) :
|
||||
@@ -194,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());
|
||||
}
|
||||
@@ -208,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;
|
||||
@@ -230,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
|
||||
|
||||
@@ -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];
|
||||
|
||||
@@ -29,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++) {
|
||||
|
||||
12
src/GraphicalUI/AppMenus/AppMenu.cpp
Normal file
@@ -0,0 +1,12 @@
|
||||
#include "AppMenu.h"
|
||||
|
||||
#include "../../Utils/AssetManager.h"
|
||||
|
||||
AppMenu::AppMenu(std::shared_ptr<MenuStack> menuStack, std::shared_ptr<Settings> settings, std::shared_ptr<sf::RenderWindow> renderWindow) : menuStack(menuStack),
|
||||
settings(settings),
|
||||
renderWindow(renderWindow)
|
||||
{
|
||||
const Asset& file = getResource(AssetName::data_fonts_pressstart_prstartk_ttf);
|
||||
|
||||
this->pressStartFont = sf::Font(file.data, file.size);
|
||||
}
|
||||
@@ -21,16 +21,10 @@ class AppMenu {
|
||||
bool enterReleased = false;
|
||||
bool escPressed = false;
|
||||
bool escReleased = false;
|
||||
sf::Font pressStartFont = sf::Font("data/fonts/pressstart/prstartk.ttf");
|
||||
sf::Font pressStartFont;
|
||||
|
||||
public:
|
||||
AppMenu(std::shared_ptr<MenuStack> menuStack, std::shared_ptr<Settings> settings, std::shared_ptr<sf::RenderWindow> renderWindow) :
|
||||
menuStack(menuStack),
|
||||
settings(settings),
|
||||
renderWindow(renderWindow)
|
||||
{
|
||||
|
||||
}
|
||||
AppMenu(std::shared_ptr<MenuStack> menuStack, std::shared_ptr<Settings> settings, std::shared_ptr<sf::RenderWindow> renderWindow);
|
||||
|
||||
virtual void computeFrame() = 0;
|
||||
|
||||
|
||||
@@ -16,6 +16,10 @@ GamePiecesAppMenu::GamePiecesAppMenu(std::shared_ptr<MenuStack> menuStack, std::
|
||||
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() {
|
||||
@@ -30,7 +34,20 @@ void GamePiecesAppMenu::computeFrame() {
|
||||
if (this->playerCursor.getPosition().y == 0) {
|
||||
this->menuStack->push(std::make_shared<GameDistributionAppMenu>(this->menuStack, this->settings, this->renderWindow));
|
||||
}
|
||||
if (this->playerCursor.getPosition().y > 1) {
|
||||
|
||||
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);
|
||||
}
|
||||
@@ -78,7 +95,8 @@ void GamePiecesAppMenu::drawFrame() const {
|
||||
this->drawSelectedPiecesRow(15.f);
|
||||
|
||||
bool drawFromFirstElem = (this->playerCursor.getPosition().y == 1);
|
||||
int firstElem = std::clamp(((int) this->playerCursor.getPosition().y) - 2, 1, this->settings->getMaximumPiecesSize() - 2);
|
||||
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);
|
||||
@@ -112,15 +130,27 @@ void GamePiecesAppMenu::drawSelectedPiecesRow(float yPos) const {
|
||||
|
||||
int pieceSize = getSizeOfPieces(pieceType);
|
||||
if (pieceSize > 0) {
|
||||
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);
|
||||
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 {
|
||||
this->placeText(text, {}, ((first) ? "" : " ") + getPiecesTypeName(pieceType) + "_" + std::to_string(value), xProgress, yPos, {});
|
||||
xProgress += (1.f + (text.getGlobalBounds().size.x / this->settings->getWindowSizeMultiplier()));
|
||||
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++;
|
||||
@@ -128,31 +158,57 @@ void GamePiecesAppMenu::drawSelectedPiecesRow(float yPos) const {
|
||||
}
|
||||
|
||||
void GamePiecesAppMenu::drawRow(int piecesSize, float yPos, bool drawFromFirstElem) const {
|
||||
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;}
|
||||
}
|
||||
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 {
|
||||
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});
|
||||
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});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,12 +10,13 @@
|
||||
|
||||
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"
|
||||
@@ -27,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"
|
||||
@@ -46,17 +66,7 @@ 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.",
|
||||
|
||||
"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."
|
||||
"when the last move was a kick."
|
||||
) {
|
||||
|
||||
}
|
||||
|
||||
@@ -7,12 +7,14 @@
|
||||
#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];
|
||||
|
||||
public:
|
||||
InfoAppMenu(std::shared_ptr<MenuStack> menuStack, std::shared_ptr<Settings> settings, std::shared_ptr<sf::RenderWindow> renderWindow);
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
#include "AppMenu.h"
|
||||
#include "../PlayerCursor.h"
|
||||
#include "../../Utils/AssetManager.h"
|
||||
|
||||
#include <stack>
|
||||
#include <memory>
|
||||
@@ -22,8 +23,9 @@ SettingsKeybindsAppMenu::SettingsKeybindsAppMenu(std::shared_ptr<MenuStack> menu
|
||||
std::string textureName = ACTION_NAMES[action];
|
||||
textureName = std::regex_replace(textureName, std::regex(" "), "");
|
||||
|
||||
std::filesystem::path texturePath("data/images/keybinds/" + textureName + ".png");
|
||||
this->iconTextures[action] = sf::Texture(texturePath, false, {{0, 0}, {16, 16}});
|
||||
const Asset& textureData = getResource("data/images/keybinds/" + textureName + ".png");
|
||||
|
||||
this->iconTextures[action] = sf::Texture(textureData.data, textureData.size, false, {{0, 0}, {16, 16}});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,14 +6,15 @@
|
||||
|
||||
#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({LOADED_PIECES_SIZE + 1}) {
|
||||
playerCursor({MAXIMUM_PIECES_SIZE + 1}) {
|
||||
|
||||
this->playerCursor.goToPosition({MINIMUM_PIECES_SIZE, 0});
|
||||
this->playerCursor.goToPosition({(unsigned int) std::clamp(this->settings->getMaximumPiecesSize(), MINIMUM_PIECES_SIZE, MAXIMUM_PIECES_SIZE), 0u});
|
||||
}
|
||||
|
||||
void StartUpAppMenu::computeFrame() {
|
||||
@@ -22,7 +23,7 @@ void StartUpAppMenu::computeFrame() {
|
||||
|
||||
if (this->playerCursor.getPosition().x < MINIMUM_PIECES_SIZE) {
|
||||
if (this->playerCursor.movedLeft()) {
|
||||
this->playerCursor.goToPosition({LOADED_PIECES_SIZE, 0});
|
||||
this->playerCursor.goToPosition({MAXIMUM_PIECES_SIZE, 0});
|
||||
}
|
||||
else {
|
||||
this->playerCursor.goToPosition({MINIMUM_PIECES_SIZE, 0});
|
||||
@@ -30,7 +31,7 @@ void StartUpAppMenu::computeFrame() {
|
||||
}
|
||||
|
||||
if (this->enterReleased) {
|
||||
*this->settings = Settings(this->playerCursor.getPosition().x);
|
||||
this->settings->loadSettingsFromFile(true, {this->playerCursor.getPosition().x});
|
||||
this->menuStack->pop();
|
||||
|
||||
if (this->settings->hasLoadedPieces()) {
|
||||
|
||||
@@ -12,7 +12,7 @@ static const double TIME_BETWEEN_FRAMES = (1000.f / FRAMES_PER_SECOND);
|
||||
|
||||
|
||||
GraphApp::GraphApp() {
|
||||
this->settings = std::make_shared<Settings>(0);
|
||||
this->settings = std::make_shared<Settings>(false);
|
||||
this->menuStack = std::make_shared<MenuStack>();
|
||||
this->renderWindow = std::make_shared<sf::RenderWindow>();
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "PiecesType.h"
|
||||
|
||||
#include <vector>
|
||||
#include <optional>
|
||||
#include <fstream>
|
||||
#include <algorithm>
|
||||
#include <SFML/Graphics.hpp>
|
||||
@@ -17,20 +18,21 @@ static const int DISTRIBUTION_MAX = 20;
|
||||
|
||||
|
||||
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(loadPieces);
|
||||
this->loadSettingsFromFile(loadPieces, {});
|
||||
}
|
||||
|
||||
bool Settings::loadPieces(int maximumPiecesSizeRequest) {
|
||||
void Settings::loadPieces(int maximumPiecesSizeRequest) {
|
||||
if (maximumPiecesSizeRequest < MINIMUM_PIECES_SIZE) {
|
||||
maximumPiecesSizeRequest = MINIMUM_PIECES_SIZE;
|
||||
}
|
||||
else if (maximumPiecesSizeRequest > LOADED_PIECES_SIZE || maximumPiecesSizeRequest > MAXIMUM_PIECES_SIZE) {
|
||||
maximumPiecesSizeRequest = LOADED_PIECES_SIZE;
|
||||
else if (maximumPiecesSizeRequest > MAXIMUM_PIECES_SIZE) {
|
||||
maximumPiecesSizeRequest = MAXIMUM_PIECES_SIZE;
|
||||
}
|
||||
|
||||
bool succeeded = true;
|
||||
@@ -40,12 +42,13 @@ bool Settings::loadPieces(int maximumPiecesSizeRequest) {
|
||||
i++;
|
||||
}
|
||||
|
||||
this->maximumPiecesSize = (succeeded) ? maximumPiecesSizeRequest : 0;
|
||||
return succeeded;
|
||||
|
||||
if (succeeded) {
|
||||
this->maximumPiecesSize = maximumPiecesSizeRequest;
|
||||
}
|
||||
this->loadedPieces = succeeded;
|
||||
}
|
||||
|
||||
void Settings::loadSettingsFromFile(bool loadPieces) {
|
||||
void Settings::loadSettingsFromFile(bool loadPieces, std::optional<int> maximumPiecesSizeRequest) {
|
||||
std::ifstream settingsFile("data/config/settings.bin", std::ios::binary);
|
||||
char byte;
|
||||
|
||||
@@ -54,8 +57,15 @@ void Settings::loadSettingsFromFile(bool loadPieces) {
|
||||
|
||||
// maximum pieces size
|
||||
settingsFile.get(byte);
|
||||
this->maximumPiecesSize = byte;
|
||||
|
||||
if (loadPieces) {
|
||||
this->loadedPieces = this->loadPieces(byte);
|
||||
if (maximumPiecesSizeRequest.has_value()) {
|
||||
this->loadPieces(maximumPiecesSizeRequest.value());
|
||||
}
|
||||
else {
|
||||
this->loadPieces(byte);
|
||||
}
|
||||
}
|
||||
else {
|
||||
this->loadedPieces = false;
|
||||
@@ -124,18 +134,15 @@ void Settings::loadSettingsFromFile(bool loadPieces) {
|
||||
if (getSizeOfPieces(PiecesType(pieceType)) == 0) {
|
||||
settingsFile.get(pieceSize);
|
||||
|
||||
if (!(pieceSize > this->maximumPiecesSize)) {
|
||||
this->selectedPieces.emplace_back(PiecesType(pieceType), pieceSize);
|
||||
}
|
||||
this->selectedPieces.emplace_back(PiecesType(pieceType), pieceSize);
|
||||
}
|
||||
else {
|
||||
if (!(getSizeOfPieces(PiecesType(pieceType)) > this->maximumPiecesSize)) {
|
||||
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);
|
||||
}
|
||||
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();
|
||||
@@ -309,25 +316,33 @@ void Settings::confirmSelectedPieces() {
|
||||
|
||||
this->menu.getPiecesList().unselectAll();
|
||||
|
||||
if (this->getSelectedPieces().size() == 0) {
|
||||
this->selectedPieces.push_back(DEFAULT_SELECTION);
|
||||
}
|
||||
|
||||
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 {
|
||||
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) {
|
||||
|
||||
@@ -5,21 +5,22 @@
|
||||
#include "PiecesType.h"
|
||||
|
||||
#include <vector>
|
||||
#include <optional>
|
||||
#include <SFML/Graphics.hpp>
|
||||
|
||||
static const int CURRENT_FILE_FORMAT_VERSION = 10;
|
||||
static const int CURRENT_FILE_FORMAT_VERSION = 11;
|
||||
|
||||
static const int MAXIMUM_BOARD_WIDTH = 40;
|
||||
static const int MAXIMUM_BOARD_HEIGHT = 40;
|
||||
|
||||
static const int MINIMUM_PIECES_SIZE = 4;
|
||||
static const int MAXIMUM_PIECES_SIZE = 15;
|
||||
static const int RELEASE_PIECES_SIZE = 15;
|
||||
static const int DEBUG_PIECES_SIZE = 10;
|
||||
|
||||
//#define __JMINOS_RELEASE__
|
||||
#ifdef __JMINOS_RELEASE__
|
||||
static const int LOADED_PIECES_SIZE = 15;
|
||||
static const int MINIMUM_PIECES_SIZE = 4;
|
||||
#ifdef NDEBUG
|
||||
static const int MAXIMUM_PIECES_SIZE = RELEASE_PIECES_SIZE;
|
||||
#else
|
||||
static const int LOADED_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};
|
||||
@@ -41,11 +42,9 @@ class Settings {
|
||||
public:
|
||||
Settings(bool loadPieces);
|
||||
|
||||
private:
|
||||
bool loadPieces(int maximumPiecesSizeRequest);
|
||||
void loadPieces(int maximumPiecesSizeRequest);
|
||||
|
||||
public:
|
||||
void loadSettingsFromFile(bool loadPieces = true);
|
||||
void loadSettingsFromFile(bool loadPieces, std::optional<int> maximumPiecesSizeRequest);
|
||||
|
||||
void saveSettingsToFile() const;
|
||||
|
||||
|
||||
@@ -13,15 +13,32 @@ int main() {
|
||||
std::srand(std::time(NULL));
|
||||
|
||||
PiecesFiles pf;
|
||||
for (int i = 1; i <= LOADED_PIECES_SIZE; i++) {
|
||||
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 {
|
||||
@@ -30,13 +47,17 @@ int main() {
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -49,6 +70,7 @@ int main() {
|
||||
|
||||
|
||||
void resetSettingsFile() {
|
||||
std::filesystem::create_directories("data/config");
|
||||
std::ofstream settingsFile("data/config/settings.bin", std::ios::trunc | std::ios::binary);
|
||||
char byte;
|
||||
|
||||
@@ -61,7 +83,7 @@ void resetSettingsFile() {
|
||||
// maximum pieces size
|
||||
byte = MINIMUM_PIECES_SIZE;
|
||||
settingsFile.write(&byte, 1);
|
||||
|
||||
|
||||
// keybind layout
|
||||
byte = 0;
|
||||
settingsFile.write(&byte, 1);
|
||||
@@ -116,6 +138,7 @@ void resetSettingsFile() {
|
||||
void resetKeybindFile(int layout) {
|
||||
if (layout < 0 || layout > 4) return;
|
||||
|
||||
std::filesystem::create_directories("data/config/keybinds/layout");
|
||||
std::ofstream layoutFile("data/config/keybinds/layout" + std::to_string(layout) + ".bin", std::ios::trunc | std::ios::binary);
|
||||
std::map<Action, sfKey> keybinds;
|
||||
|
||||
|
||||
@@ -10,6 +10,8 @@
|
||||
#include <filesystem>
|
||||
#include <algorithm>
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
|
||||
PiecesFiles::PiecesFiles() {
|
||||
}
|
||||
@@ -25,22 +27,35 @@ bool PiecesFiles::savePieces(int polyominoSize) const {
|
||||
}
|
||||
|
||||
Generator generator;
|
||||
std::vector<Polyomino> nMinos = generator.generatePolyominoes(polyominoSize);
|
||||
std::vector<Polyomino> polyominoes = generator.generatePolyominoes(polyominoSize);
|
||||
|
||||
return this->savePieces(polyominoSize, polyominoes);
|
||||
}
|
||||
|
||||
bool PiecesFiles::savePieces(int polyominoSize, std::vector<Polyomino>& polyominoes) const {
|
||||
std::string filePath;
|
||||
if (!this->getFilePath(polyominoSize, filePath)) {
|
||||
return false;
|
||||
}
|
||||
std::ofstream piecesFile(filePath, std::ios::trunc | std::ios::binary);
|
||||
if (!piecesFile.good()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// sorting the polyominoes is done after setting spawn position to ensure the order is always the same
|
||||
for (Polyomino& nMino : nMinos) {
|
||||
for (Polyomino& nMino : polyominoes) {
|
||||
nMino.goToSpawnPosition();
|
||||
}
|
||||
std::sort(nMinos.begin(), nMinos.end());
|
||||
std::sort(polyominoes.begin(), polyominoes.end());
|
||||
|
||||
for (const Polyomino& nMino : nMinos) {
|
||||
for (const Polyomino& polyomino : polyominoes) {
|
||||
// write the characteristics of the piece
|
||||
char infoByte = (nMino.isConvex() << 7) + (nMino.hasHole() << 6) + nMino.getLength();
|
||||
char infoByte = (polyomino.isConvex() << 7) + (polyomino.hasHole() << 6) + polyomino.getLength();
|
||||
piecesFile.write(&infoByte, 1);
|
||||
|
||||
// write the positions of the piece
|
||||
char positionByte;
|
||||
for (Position position : nMino.getPositions()) {
|
||||
for (const Position position : polyomino.getPositions()) {
|
||||
positionByte = (position.x << 4) + position.y;
|
||||
piecesFile.write(&positionByte, 1);
|
||||
}
|
||||
@@ -119,7 +134,12 @@ bool PiecesFiles::loadPieces(int polyominoSize, std::vector<Piece>& pieces, std:
|
||||
|
||||
bool PiecesFiles::getFilePath(int polyominoSize, std::string& filePath) const {
|
||||
std::string dataFolderPath = "data/pieces/";
|
||||
if (!std::filesystem::is_directory(dataFolderPath)) {
|
||||
|
||||
if (!fs::exists(dataFolderPath)) {
|
||||
fs::create_directories(dataFolderPath);
|
||||
}
|
||||
|
||||
if (!fs::is_directory(dataFolderPath)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -22,13 +22,18 @@ class PiecesFiles {
|
||||
*/
|
||||
bool savePieces(int polyominoSize) const;
|
||||
|
||||
/**
|
||||
* Generate a file containing all the pieces of the specified size, assuming they have been correctly generated
|
||||
* @return If the file could be created
|
||||
*/
|
||||
bool savePieces(int polyominoSize, std::vector<Polyomino>& polyominoes) const;
|
||||
|
||||
/**
|
||||
* Replace the content of the vectors by the pieces of the specified size, if the file wasn't found the vectors stays untouched
|
||||
* @return If the file was found
|
||||
*/
|
||||
bool loadPieces(int polyominoSize, std::vector<Piece>& pieces, std::vector<int>& convexPieces, std::vector<int>& holelessPieces, std::vector<int>& otherPieces) const;
|
||||
|
||||
private:
|
||||
/**
|
||||
* Puts the path to the piece file of the specified size in order, if the data folder wasn't found the string stays untouched
|
||||
* @return If the data folder was found
|
||||
|
||||
@@ -3,45 +3,60 @@
|
||||
#include "TextApp.h"
|
||||
|
||||
#include <chrono>
|
||||
#include <filesystem>
|
||||
#include <cmath>
|
||||
|
||||
static const int MAXIMUM_PIECES_SIZE = 10;
|
||||
static const int BENCHMARK_PIECES_SIZE = 15;
|
||||
|
||||
|
||||
void testGeneratorForAllSizes(int amount);
|
||||
void testGeneratorForAllSizes(int max_size);
|
||||
void testGeneratorForOneSize(int size);
|
||||
void testGeneratorByprintingAllNminos(int n);
|
||||
void testStoringAndRetrievingPieces(int size);
|
||||
void generateFilesForAllSizes(int amount);
|
||||
void generateFilesForOneSize(int size);
|
||||
void loadFromFilesForOneSize(int size);
|
||||
void readStatsFromFilesForAllSizes(int amount);
|
||||
void printPiecesByTypesForOneSize(int size);
|
||||
void readStatsFromFilesForAllSizes(int max_size);
|
||||
|
||||
void benchmarking(int min_size, int max_size);
|
||||
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
std::srand(std::time(NULL));
|
||||
|
||||
// dev: generate files if it hasn't been done before, UI will NOT generate the files
|
||||
//generateFilesForAllSizes(10);
|
||||
#ifdef BENCHMARK
|
||||
benchmarking(1, BENCHMARK_PIECES_SIZE);
|
||||
#else
|
||||
// dev: generate files if it hasn't been done before, UI will NOT generate the files
|
||||
//generateFilesForAllSizes(10);
|
||||
|
||||
TextApp UI;
|
||||
UI.run();
|
||||
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 << "INFO: Pieces files for size " << i << " not found, generating..." << std::endl;
|
||||
pf.savePieces(i);
|
||||
}
|
||||
}
|
||||
|
||||
TextApp UI;
|
||||
UI.run();
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void testGeneratorForAllSizes(int amount) {
|
||||
void testGeneratorForAllSizes(int max_size) {
|
||||
using std::chrono::high_resolution_clock;
|
||||
using std::chrono::duration_cast;
|
||||
using std::chrono::duration;
|
||||
using std::chrono::milliseconds;
|
||||
Generator generator;
|
||||
|
||||
for (int i = 1; i <= amount; i++) {
|
||||
for (int i = 1; i <= max_size; i++) {
|
||||
auto t1 = high_resolution_clock::now();
|
||||
std::vector<Polyomino> n_minos = generator.generatePolyominoes(i);
|
||||
auto t2 = high_resolution_clock::now();
|
||||
|
||||
duration<double, std::milli> ms_double = t2 - t1;
|
||||
std::cout << "generated " << n_minos.size() << " polyominoes of size " << i << " in " << ms_double.count() << "ms" << std::endl;
|
||||
std::cout << "Generated " << n_minos.size() << " polyominoes of size " << i << " in " << ms_double.count() << "ms" << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,24 +78,8 @@ void testGeneratorForOneSize(int size) {
|
||||
}
|
||||
}
|
||||
|
||||
void testGeneratorByprintingAllNminos(int n) {
|
||||
Generator generator;
|
||||
std::vector<Polyomino> n_minos = generator.generatePolyominoes(n);
|
||||
|
||||
for (Polyomino& n_mino : n_minos) {
|
||||
n_mino.goToSpawnPosition();
|
||||
}
|
||||
std::sort(n_minos.begin(), n_minos.end());
|
||||
|
||||
for (Polyomino& n_mino : n_minos) {
|
||||
|
||||
std::cout << n_mino << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
void testStoringAndRetrievingPieces(int size) {
|
||||
void printPiecesByTypesForOneSize(int size) {
|
||||
PiecesFiles piecesFiles;
|
||||
piecesFiles.savePieces(size);
|
||||
|
||||
std::vector<Piece> pieces;
|
||||
std::vector<int> convexPieces;
|
||||
@@ -104,79 +103,9 @@ void testStoringAndRetrievingPieces(int size) {
|
||||
}
|
||||
}
|
||||
|
||||
void generateFilesForAllSizes(int amount) {
|
||||
using std::chrono::high_resolution_clock;
|
||||
using std::chrono::duration_cast;
|
||||
using std::chrono::duration;
|
||||
using std::chrono::milliseconds;
|
||||
void readStatsFromFilesForAllSizes(int max_size) {
|
||||
PiecesFiles piecesFiles;
|
||||
|
||||
for (int i = 1; i <= amount; i++) {
|
||||
auto t1 = high_resolution_clock::now();
|
||||
piecesFiles.savePieces(i);
|
||||
auto t2 = high_resolution_clock::now();
|
||||
|
||||
duration<double, std::milli> ms_double = t2 - t1;
|
||||
std::cout << "Generated pieces files for size " << i << " in " << ms_double.count() << "ms" << std::endl;
|
||||
}
|
||||
|
||||
std::vector<Piece> pieces;
|
||||
std::vector<int> convexPieces;
|
||||
std::vector<int> holelessPieces;
|
||||
std::vector<int> otherPieces;
|
||||
for (int i = 1; i <= amount; i++) {
|
||||
auto t1 = high_resolution_clock::now();
|
||||
piecesFiles.loadPieces(i, pieces, convexPieces, holelessPieces, otherPieces);
|
||||
auto t2 = high_resolution_clock::now();
|
||||
|
||||
duration<double, std::milli> ms_double = t2 - t1;
|
||||
std::cout << "Read pieces from files for size " << i << " in " << ms_double.count() << "ms" << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
void generateFilesForOneSize(int size) {
|
||||
using std::chrono::high_resolution_clock;
|
||||
using std::chrono::duration_cast;
|
||||
using std::chrono::duration;
|
||||
using std::chrono::milliseconds;
|
||||
PiecesFiles piecesFiles;
|
||||
|
||||
std::cout << "Generating " << size << "-minos files" << std::endl;
|
||||
for (int i = 0; i < 10; i++) {
|
||||
auto t1 = high_resolution_clock::now();
|
||||
piecesFiles.savePieces(size);
|
||||
auto t2 = high_resolution_clock::now();
|
||||
|
||||
duration<double, std::milli> ms_double = t2 - t1;
|
||||
std::cout << ms_double.count() << "ms" << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
void loadFromFilesForOneSize(int size) {
|
||||
using std::chrono::high_resolution_clock;
|
||||
using std::chrono::duration_cast;
|
||||
using std::chrono::duration;
|
||||
using std::chrono::milliseconds;
|
||||
PiecesFiles piecesFiles;
|
||||
|
||||
std::vector<Piece> pieces;
|
||||
std::vector<int> convexPieces;
|
||||
std::vector<int> holelessPieces;
|
||||
std::vector<int> otherPieces;
|
||||
std::cout << "Loading " << size << "-minos from files" << std::endl;
|
||||
for (int i = 0; i < 10; i++) {
|
||||
auto t1 = high_resolution_clock::now();
|
||||
piecesFiles.loadPieces(size, pieces, convexPieces, holelessPieces, otherPieces);
|
||||
auto t2 = high_resolution_clock::now();
|
||||
|
||||
duration<double, std::milli> ms_double = t2 - t1;
|
||||
std::cout << ms_double.count() << "ms" << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
void readStatsFromFilesForAllSizes(int amount) {
|
||||
PiecesFiles piecesFiles;
|
||||
for (int i = 1; i <= amount; i++) {
|
||||
for (int i = 1; i <= max_size; i++) {
|
||||
std::vector<Piece> pieces;
|
||||
std::vector<int> convexPieces;
|
||||
std::vector<int> holelessPieces;
|
||||
@@ -189,3 +118,66 @@ void readStatsFromFilesForAllSizes(int amount) {
|
||||
std::cout << "Others " << i << "-minos : " << otherPieces.size() << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
void benchmarking(int min_size, int max_size) {
|
||||
using std::chrono::high_resolution_clock;
|
||||
using std::chrono::duration_cast;
|
||||
using std::chrono::duration;
|
||||
using std::chrono::milliseconds;
|
||||
|
||||
std::cout << "| n | Number | Generation | File storing | File retrieving | File size |" << std::endl;
|
||||
std::cout << "| - | - | - | - | - | - |" << std::endl;
|
||||
|
||||
Generator gen;
|
||||
PiecesFiles pf;
|
||||
|
||||
for (int i = min_size; i <= max_size; i++) {
|
||||
std::cout << "| " << i;
|
||||
|
||||
auto t1 = high_resolution_clock::now();
|
||||
std::vector<Polyomino> polyominoes = gen.generatePolyominoes(i);
|
||||
auto t2 = high_resolution_clock::now();
|
||||
duration<double, std::milli> ms_double = t2 - t1;
|
||||
std::cout << " | " << polyominoes.size();
|
||||
std::flush(std::cout);
|
||||
std::cout << " | " << (int) ms_double.count() / 1000 << "s " << std::fmod(ms_double.count(), 1000) << "ms ";
|
||||
std::flush(std::cout);
|
||||
|
||||
t1 = high_resolution_clock::now();
|
||||
pf.savePieces(i, polyominoes);
|
||||
t2 = high_resolution_clock::now();
|
||||
ms_double = t2 - t1;
|
||||
std::cout << " | " << (int) ms_double.count() / 1000 << "s " << std::fmod(ms_double.count(), 1000) << "ms ";
|
||||
std::flush(std::cout);
|
||||
|
||||
polyominoes.clear();
|
||||
polyominoes.shrink_to_fit();
|
||||
|
||||
std::vector<Piece> pieces;
|
||||
std::vector<int> convexPieces;
|
||||
std::vector<int> holelessPieces;
|
||||
std::vector<int> otherPieces;
|
||||
|
||||
t1 = high_resolution_clock::now();
|
||||
pf.loadPieces(i, pieces, convexPieces, holelessPieces, otherPieces);
|
||||
t2 = high_resolution_clock::now();
|
||||
ms_double = t2 - t1;
|
||||
std::cout << " | " << (int) ms_double.count() / 1000 << "s " << std::fmod(ms_double.count(), 1000) << "ms ";
|
||||
std::flush(std::cout);
|
||||
|
||||
pieces.clear();
|
||||
pieces.shrink_to_fit();
|
||||
convexPieces.clear();
|
||||
convexPieces.shrink_to_fit();
|
||||
holelessPieces.clear();
|
||||
holelessPieces.shrink_to_fit();
|
||||
otherPieces.clear();
|
||||
otherPieces.shrink_to_fit();
|
||||
|
||||
std::string filePath;
|
||||
pf.getFilePath(i, filePath);
|
||||
int fileSize = std::filesystem::file_size(filePath);
|
||||
std::cout << " | " << fileSize << " bytes |" << std::endl;
|
||||
std::flush(std::cout);
|
||||
}
|
||||
}
|
||||
|
||||
97
src/Utils/AssetManager.cpp
Normal file
@@ -0,0 +1,97 @@
|
||||
#include "./AssetManager.h"
|
||||
|
||||
#include <map>
|
||||
|
||||
static const unsigned char data_fonts_pressstart_prstartk_ttf[] = {
|
||||
#include <data/fonts/pressstart/prstartk.ttf.h>
|
||||
};
|
||||
|
||||
static const unsigned char data_fonts_pressstart_prstart_ttf[] = {
|
||||
#include <data/fonts/pressstart/prstart.ttf.h>
|
||||
};
|
||||
|
||||
static const unsigned char data_images_keybinds_Rotate0_png[] = {
|
||||
#include <data/images/keybinds/Rotate0.png.h>
|
||||
};
|
||||
|
||||
static const unsigned char data_images_keybinds_Moveright_png[] = {
|
||||
#include <data/images/keybinds/Moveright.png.h>
|
||||
};
|
||||
|
||||
static const unsigned char data_images_keybinds_RotateCW_png[] = {
|
||||
#include <data/images/keybinds/RotateCW.png.h>
|
||||
};
|
||||
|
||||
static const unsigned char data_images_keybinds_Pause_png[] = {
|
||||
#include <data/images/keybinds/Pause.png.h>
|
||||
};
|
||||
|
||||
static const unsigned char data_images_keybinds_Hold_png[] = {
|
||||
#include <data/images/keybinds/Hold.png.h>
|
||||
};
|
||||
|
||||
static const unsigned char data_images_keybinds_Softdrop_png[] = {
|
||||
#include <data/images/keybinds/Softdrop.png.h>
|
||||
};
|
||||
|
||||
static const unsigned char data_images_keybinds_RotateCCW_png[] = {
|
||||
#include <data/images/keybinds/RotateCCW.png.h>
|
||||
};
|
||||
|
||||
static const unsigned char data_images_keybinds_Moveleft_png[] = {
|
||||
#include <data/images/keybinds/Moveleft.png.h>
|
||||
};
|
||||
|
||||
static const unsigned char data_images_keybinds_Rotate180_png[] = {
|
||||
#include <data/images/keybinds/Rotate180.png.h>
|
||||
};
|
||||
|
||||
static const unsigned char data_images_keybinds_Retry_png[] = {
|
||||
#include <data/images/keybinds/Retry.png.h>
|
||||
};
|
||||
|
||||
static const unsigned char data_images_keybinds_Harddrop_png[] = {
|
||||
#include <data/images/keybinds/Harddrop.png.h>
|
||||
};
|
||||
|
||||
static const Asset assets[] = {
|
||||
{data_fonts_pressstart_prstartk_ttf, sizeof(data_fonts_pressstart_prstartk_ttf)},
|
||||
{data_fonts_pressstart_prstart_ttf, sizeof(data_fonts_pressstart_prstart_ttf)},
|
||||
{data_images_keybinds_Rotate0_png, sizeof(data_images_keybinds_Rotate0_png)},
|
||||
{data_images_keybinds_Moveright_png, sizeof(data_images_keybinds_Moveright_png)},
|
||||
{data_images_keybinds_RotateCW_png, sizeof(data_images_keybinds_RotateCW_png)},
|
||||
{data_images_keybinds_Pause_png, sizeof(data_images_keybinds_Pause_png)},
|
||||
{data_images_keybinds_Hold_png, sizeof(data_images_keybinds_Hold_png)},
|
||||
{data_images_keybinds_Softdrop_png, sizeof(data_images_keybinds_Softdrop_png)},
|
||||
{data_images_keybinds_RotateCCW_png, sizeof(data_images_keybinds_RotateCCW_png)},
|
||||
{data_images_keybinds_Moveleft_png, sizeof(data_images_keybinds_Moveleft_png)},
|
||||
{data_images_keybinds_Rotate180_png, sizeof(data_images_keybinds_Rotate180_png)},
|
||||
{data_images_keybinds_Retry_png, sizeof(data_images_keybinds_Retry_png)},
|
||||
{data_images_keybinds_Harddrop_png, sizeof(data_images_keybinds_Harddrop_png)},
|
||||
|
||||
};
|
||||
|
||||
static const std::map<std::string, AssetName> assetMap = {
|
||||
{"data/fonts/pressstart/prstartk.ttf", AssetName::data_fonts_pressstart_prstartk_ttf},
|
||||
{"data/fonts/pressstart/prstart.ttf", AssetName::data_fonts_pressstart_prstart_ttf},
|
||||
{"data/images/keybinds/Rotate0.png", AssetName::data_images_keybinds_Rotate0_png},
|
||||
{"data/images/keybinds/Moveright.png", AssetName::data_images_keybinds_Moveright_png},
|
||||
{"data/images/keybinds/RotateCW.png", AssetName::data_images_keybinds_RotateCW_png},
|
||||
{"data/images/keybinds/Pause.png", AssetName::data_images_keybinds_Pause_png},
|
||||
{"data/images/keybinds/Hold.png", AssetName::data_images_keybinds_Hold_png},
|
||||
{"data/images/keybinds/Softdrop.png", AssetName::data_images_keybinds_Softdrop_png},
|
||||
{"data/images/keybinds/RotateCCW.png", AssetName::data_images_keybinds_RotateCCW_png},
|
||||
{"data/images/keybinds/Moveleft.png", AssetName::data_images_keybinds_Moveleft_png},
|
||||
{"data/images/keybinds/Rotate180.png", AssetName::data_images_keybinds_Rotate180_png},
|
||||
{"data/images/keybinds/Retry.png", AssetName::data_images_keybinds_Retry_png},
|
||||
{"data/images/keybinds/Harddrop.png", AssetName::data_images_keybinds_Harddrop_png},
|
||||
|
||||
};
|
||||
|
||||
const Asset& getResource(AssetName fileName) {
|
||||
return assets[static_cast<std::size_t>(fileName)];
|
||||
}
|
||||
|
||||
const Asset& getResource(const std::string& fileName) {
|
||||
return getResource(assetMap.at(fileName));
|
||||
}
|
||||
30
src/Utils/AssetManager.h
Normal file
@@ -0,0 +1,30 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
struct Asset {
|
||||
const unsigned char* data;
|
||||
std::size_t size;
|
||||
};
|
||||
|
||||
enum class AssetName {
|
||||
data_fonts_pressstart_prstartk_ttf,
|
||||
data_fonts_pressstart_prstart_ttf,
|
||||
data_images_keybinds_Rotate0_png,
|
||||
data_images_keybinds_Moveright_png,
|
||||
data_images_keybinds_RotateCW_png,
|
||||
data_images_keybinds_Pause_png,
|
||||
data_images_keybinds_Hold_png,
|
||||
data_images_keybinds_Softdrop_png,
|
||||
data_images_keybinds_RotateCCW_png,
|
||||
data_images_keybinds_Moveleft_png,
|
||||
data_images_keybinds_Rotate180_png,
|
||||
data_images_keybinds_Retry_png,
|
||||
data_images_keybinds_Harddrop_png,
|
||||
|
||||
};
|
||||
|
||||
const Asset& getResource(AssetName fileName);
|
||||
|
||||
const Asset& getResource(const std::string& fileName);
|
||||
29
xmake.lua
@@ -1,5 +1,7 @@
|
||||
add_rules("mode.debug", "mode.release")
|
||||
|
||||
includes("xmake/bin2c.lua")
|
||||
|
||||
add_requires("sfml 3.0.0")
|
||||
|
||||
set_languages("c++20")
|
||||
@@ -11,18 +13,33 @@ target("core")
|
||||
add_files("src/Pieces/*.cpp")
|
||||
add_files("src/Core/*.cpp")
|
||||
|
||||
target("text")
|
||||
set_kind("binary")
|
||||
set_default(false)
|
||||
add_files("./src/TextUI/*.cpp")
|
||||
add_deps("core")
|
||||
|
||||
target("graph")
|
||||
set_default(true)
|
||||
add_rules("bin2c", {
|
||||
extensions = {".png", ".ttf"},
|
||||
outputSource = {"src/Utils/AssetManager.cpp"},
|
||||
outputHeader = {"src/Utils/AssetManager.h"}
|
||||
})
|
||||
set_kind("binary")
|
||||
add_files("./src/GraphicalUI/**.cpp")
|
||||
add_files("data/fonts/**.ttf", "data/images/**.png")
|
||||
add_deps("core")
|
||||
add_packages("sfml")
|
||||
|
||||
target("text")
|
||||
set_default(false)
|
||||
set_kind("binary")
|
||||
add_files("./src/TextUI/*.cpp")
|
||||
add_deps("core")
|
||||
|
||||
target("benchmark")
|
||||
set_default(false)
|
||||
set_kind("binary")
|
||||
add_files("./src/TextUI/*.cpp")
|
||||
add_deps("core")
|
||||
add_defines("BENCHMARK")
|
||||
|
||||
|
||||
--
|
||||
-- If you want to known more usage about xmake, please see https://xmake.io
|
||||
--
|
||||
|
||||
124
xmake/bin2c.lua
Normal file
@@ -0,0 +1,124 @@
|
||||
rule("bin2c")
|
||||
set_extensions(".bin")
|
||||
|
||||
on_load(function (target)
|
||||
local headerdir = path.join(target:autogendir(), "rules", "bin2c")
|
||||
|
||||
local outputSource = table.unpack(target:extraconf("rules", "bin2c", "outputSource"))
|
||||
|
||||
if not os.isdir(headerdir) then
|
||||
os.mkdir(headerdir)
|
||||
end
|
||||
|
||||
target:add("includedirs", headerdir)
|
||||
|
||||
target:add("files", outputSource)
|
||||
end)
|
||||
|
||||
before_buildcmd_files(function (target, batchcmds, sourcebatch, opt)
|
||||
local outputHeader = table.unpack(target:extraconf("rules", "bin2c", "outputHeader"))
|
||||
|
||||
local outputHeaderEnumContent = ""
|
||||
|
||||
for _, filePath in ipairs(sourcebatch.sourcefiles) do
|
||||
local escapedName = string.gsub(filePath, "[/|.]", "_")
|
||||
outputHeaderEnumContent = outputHeaderEnumContent .. "\t" .. escapedName .. ",\n"
|
||||
end
|
||||
|
||||
local outputHeaderContent = string.format([[
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
struct Asset {
|
||||
const unsigned char* data;
|
||||
std::size_t size;
|
||||
};
|
||||
|
||||
enum class AssetName {
|
||||
%s
|
||||
};
|
||||
|
||||
const Asset& getResource(AssetName fileName);
|
||||
|
||||
const Asset& getResource(const std::string& fileName);
|
||||
]], outputHeaderEnumContent)
|
||||
|
||||
local outputSource = table.unpack(target:extraconf("rules", "bin2c", "outputSource"))
|
||||
|
||||
local relativePath = path.join(path.relative(path.directory(outputHeader), path.directory(outputSource)), path.filename(outputHeader))
|
||||
|
||||
local outputSourceContent = string.format([[
|
||||
#include "%s"
|
||||
|
||||
#include <map>
|
||||
|
||||
]], relativePath)
|
||||
|
||||
|
||||
local outputSourceArrayVars = ""
|
||||
local outputSourceMapVars = ""
|
||||
|
||||
for _, filePath in ipairs(sourcebatch.sourcefiles) do
|
||||
local escapedName = string.gsub(filePath, "[/|.]", "_")
|
||||
local varDecl = string.format("static const unsigned char %s[] = {\n\t#include <%s>\n};\n\n", escapedName, filePath .. ".h")
|
||||
outputSourceContent = outputSourceContent .. varDecl
|
||||
outputSourceArrayVars = outputSourceArrayVars .. string.format("\t{%s, sizeof(%s)},\n", escapedName, escapedName)
|
||||
outputSourceMapVars = outputSourceMapVars .. string.format("\t{\"%s\", AssetName::%s},\n", filePath, escapedName)
|
||||
end
|
||||
|
||||
outputSourceContent = outputSourceContent .. string.format([[
|
||||
static const Asset assets[] = {
|
||||
%s
|
||||
};
|
||||
|
||||
static const std::map<std::string, AssetName> assetMap = {
|
||||
%s
|
||||
};
|
||||
|
||||
]], outputSourceArrayVars, outputSourceMapVars)
|
||||
|
||||
outputSourceContent = outputSourceContent .. [[
|
||||
const Asset& getResource(AssetName fileName) {
|
||||
return assets[static_cast<std::size_t>(fileName)];
|
||||
}
|
||||
|
||||
const Asset& getResource(const std::string& fileName) {
|
||||
return getResource(assetMap.at(fileName));
|
||||
}
|
||||
]]
|
||||
|
||||
for _, sourcefile_bin in ipairs(sourcebatch.sourcefiles) do
|
||||
-- get header file
|
||||
local headerdir = path.join(target:autogendir(), "rules", "bin2c")
|
||||
local headerfile = path.join(headerdir, sourcefile_bin .. ".h")
|
||||
target:add("includedirs", headerdir)
|
||||
|
||||
-- add commands
|
||||
batchcmds:show_progress(opt.progress, "${color.build.object}generating.bin2c %s", sourcefile_bin)
|
||||
batchcmds:mkdir(headerdir)
|
||||
local argv = {"lua", "private.utils.bin2c", "-i", path(sourcefile_bin), "-o", path(headerfile)}
|
||||
local linewidth = target:extraconf("rules", "bin2c", "linewidth")
|
||||
if linewidth then
|
||||
table.insert(argv, "-w")
|
||||
table.insert(argv, tostring(linewidth))
|
||||
end
|
||||
local nozeroend = target:extraconf("rules", "bin2c", "nozeroend")
|
||||
if nozeroend then
|
||||
table.insert(argv, "--nozeroend")
|
||||
end
|
||||
batchcmds:vrunv(os.programfile(), argv, {envs = {XMAKE_SKIP_HISTORY = "y"}})
|
||||
|
||||
-- add deps
|
||||
batchcmds:add_depfiles(sourcefile_bin)
|
||||
batchcmds:set_depmtime(os.mtime(headerfile))
|
||||
batchcmds:set_depcache(target:dependfile(headerfile))
|
||||
|
||||
end
|
||||
|
||||
batchcmds:show_progress(opt.progress, "${color.build.object}generating.bin2c %s", outputHeader)
|
||||
io.writefile(outputHeader, outputHeaderContent)
|
||||
batchcmds:show_progress(opt.progress, "${color.build.object}generating.bin2c %s", outputSource)
|
||||
io.writefile(outputSource, outputSourceContent)
|
||||
end)
|
||||