Compare commits
18 Commits
optimizing
...
compress
| Author | SHA1 | Date | |
|---|---|---|---|
| 17459c4026 | |||
| 3e40ff7252 | |||
| dd8314f76c | |||
| d7f52239f0 | |||
| 72f4ea75ff | |||
| b1b7277666 | |||
| fd6bdc2b09 | |||
| 69b91d6497 | |||
| d50714ef8c | |||
| 6ed85869ae | |||
| 3d1feb6295 | |||
| 7a96136631 | |||
| fc6348cebc | |||
| 0bb25d4628 | |||
| 69c07abcec | |||
| 774aa1422f | |||
| 4dfeda0957 | |||
| 05bb79d4a9 |
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
|
# personnal documentation
|
||||||
doc/*.txt
|
doc/*.txt
|
||||||
doc/*.violet.html
|
doc/diagrams/*.violet.html
|
||||||
doc/mockups/*
|
doc/mockups/*
|
||||||
|
|
||||||
# data files
|
# data files
|
||||||
|
|||||||
63
README.md
@@ -1,28 +1,35 @@
|
|||||||
# jminos
|
# 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
|
## Download
|
||||||
|
|
||||||
// TODO when the game is finished //
|
// TODO when the game is finished //
|
||||||
|
|
||||||
This game has been tested on and built on Windows 11 and WSL2 Ubuntu only.
|
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).
|
If your OS isn't compactible with either of theses two, you can try [manually building the project](#manual-build).
|
||||||
|
|
||||||
## How to play
|
## How to play
|
||||||
|
|
||||||
|
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!
|
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 unchangeable keybinds.
|
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.
|
You will find more infos about the Rotation System, the scoring system, or the different pieces type in the **INFO** section of the main menu.
|
||||||
If you want to know more details about the generation of polyominoes, [check the documentation](/doc/)!
|
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
- Every polyominoes from size 1 to 15, selectable as you wish, with customizable propotionnality for each size!
|
- Every polyominoes up to pentedecaminoes!
|
||||||
- Customizable keybinds!
|
- 7bag with proportionnality for each polyomino size!
|
||||||
- 0° rotations!
|
|
||||||
- AutoRS as the Rotation System!
|
- AutoRS as the Rotation System!
|
||||||
|
- 0° rotations!
|
||||||
|
- All spin!
|
||||||
- IRS, IHS, infinite hold, and other leniency mechanics!
|
- IRS, IHS, infinite hold, and other leniency mechanics!
|
||||||
|
- Customizable board size!
|
||||||
|
- Customizable keybinds!
|
||||||
- Very bland interface!! (i'm not a designer)
|
- Very bland interface!! (i'm not a designer)
|
||||||
|
|
||||||
### Available gamemodes
|
### Available gamemodes
|
||||||
@@ -31,11 +38,22 @@ If you want to know more details about the generation of polyominoes, [check the
|
|||||||
- MARATHON : clear 200 lines with increasing gravity!
|
- MARATHON : clear 200 lines with increasing gravity!
|
||||||
- ULTRA : scores as much as possible in only 2 minutes!
|
- ULTRA : scores as much as possible in only 2 minutes!
|
||||||
- MASTER : clear 200 lines at levels higher than maximum gravity!
|
- MASTER : clear 200 lines at levels higher than maximum gravity!
|
||||||
|
- INVISIBLE : get 1000 grade while not being able to see the board!
|
||||||
- ZEN : practice indefinitely in this mode with no gravity!
|
- ZEN : practice indefinitely in this mode with no gravity!
|
||||||
|
|
||||||
### Screenshots
|
### Screenshots
|
||||||
|
|
||||||
// TODO when the game is finished //
|
Pentedecamino jumpscare
|
||||||
|

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

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

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

|
||||||
|
|
||||||
## Manual build
|
## Manual build
|
||||||
|
|
||||||
@@ -62,12 +80,43 @@ To switch between debug and release mode:
|
|||||||
``xmake f -m release``
|
``xmake f -m release``
|
||||||
|
|
||||||
If for some reasons you wanna run the command line version (not updated):
|
If for some reasons you wanna run the command line version (not updated):
|
||||||
|
``xmake build text``
|
||||||
``xmake run text``
|
``xmake run text``
|
||||||
|
|
||||||
### Package the project
|
### Package the project
|
||||||
|
|
||||||
// TODO when the game is finished //
|
// 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 type checking 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 f -m release``
|
||||||
|
``xmake build bmark``
|
||||||
|
``xmake run bmark``
|
||||||
|
|
||||||
## Credits
|
## Credits
|
||||||
|
|
||||||
Library used: [SFML 3](https://www.sfml-dev.org/).
|
Library used: [SFML 3](https://www.sfml-dev.org/).
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 164 KiB After Width: | Height: | Size: 164 KiB |
|
Before Width: | Height: | Size: 54 KiB After Width: | Height: | Size: 54 KiB |
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 |
68
src/Common/Compression.cpp
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
#include "Compression.h"
|
||||||
|
|
||||||
|
#include "VarInt.h"
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
|
#include <zlib.h>
|
||||||
|
|
||||||
|
#define COMPRESSION_THRESHOLD 64
|
||||||
|
|
||||||
|
static DataBuffer Inflate(const std::uint8_t* source, std::size_t size, std::size_t uncompressedSize) {
|
||||||
|
DataBuffer result;
|
||||||
|
result.Resize(uncompressedSize);
|
||||||
|
|
||||||
|
uncompress(reinterpret_cast<Bytef*>(result.data()), reinterpret_cast<uLongf*>(&uncompressedSize),
|
||||||
|
reinterpret_cast<const Bytef*>(source), static_cast<uLong>(size));
|
||||||
|
|
||||||
|
assert(result.GetSize() == uncompressedSize);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static DataBuffer Deflate(const std::uint8_t* source, std::size_t size) {
|
||||||
|
DataBuffer result;
|
||||||
|
uLongf compressedSize = size;
|
||||||
|
|
||||||
|
result.Resize(size); // Resize for the compressed data to fit into
|
||||||
|
compress(
|
||||||
|
reinterpret_cast<Bytef*>(result.data()), &compressedSize, reinterpret_cast<const Bytef*>(source), static_cast<uLong>(size));
|
||||||
|
result.Resize(compressedSize); // Resize to cut useless data
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
DataBuffer Compress(const DataBuffer& buffer) {
|
||||||
|
DataBuffer packet;
|
||||||
|
|
||||||
|
if (buffer.GetSize() < COMPRESSION_THRESHOLD) {
|
||||||
|
// Don't compress since it's a small packet
|
||||||
|
VarInt compressedDataLength = 0;
|
||||||
|
packet << compressedDataLength;
|
||||||
|
packet << buffer;
|
||||||
|
return packet;
|
||||||
|
}
|
||||||
|
|
||||||
|
DataBuffer compressedData = Deflate(buffer.data(), buffer.GetSize());
|
||||||
|
|
||||||
|
VarInt uncompressedDataLength = buffer.GetSize();
|
||||||
|
packet << uncompressedDataLength;
|
||||||
|
packet.WriteSome(compressedData.data(), compressedData.GetSize());
|
||||||
|
return packet;
|
||||||
|
}
|
||||||
|
|
||||||
|
DataBuffer Decompress(DataBuffer& buffer, std::uint64_t packetLength) {
|
||||||
|
VarInt uncompressedLength;
|
||||||
|
buffer >> uncompressedLength;
|
||||||
|
|
||||||
|
std::uint64_t compressedLength = packetLength - uncompressedLength.GetSerializedLength();
|
||||||
|
|
||||||
|
if (uncompressedLength.GetValue() == 0) {
|
||||||
|
// Data already uncompressed. Nothing to do
|
||||||
|
DataBuffer ret;
|
||||||
|
buffer.ReadSome(ret, compressedLength);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(buffer.GetReadOffset() + compressedLength <= buffer.GetSize());
|
||||||
|
|
||||||
|
return Inflate(buffer.data() + buffer.GetReadOffset(), compressedLength, uncompressedLength.GetValue());
|
||||||
|
}
|
||||||
23
src/Common/Compression.h
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \file Compression.h
|
||||||
|
* \brief File containing compress utilities
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "DataBuffer.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Compress some data
|
||||||
|
* \param buffer the data to compress
|
||||||
|
* \return the compressed data
|
||||||
|
*/
|
||||||
|
DataBuffer Compress(const DataBuffer& buffer);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Uncompress some data
|
||||||
|
* \param buffer the data to uncompress
|
||||||
|
* \param packetLength lenght of data
|
||||||
|
* \return the uncompressed data
|
||||||
|
*/
|
||||||
|
DataBuffer Decompress(DataBuffer& buffer, std::uint64_t packetLength);
|
||||||
98
src/Common/DataBuffer.cpp
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
#include "DataBuffer.h"
|
||||||
|
|
||||||
|
#include <fstream>
|
||||||
|
#include <iomanip>
|
||||||
|
#include <sstream>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
DataBuffer::DataBuffer(std::size_t initalCapacity) : m_ReadOffset(0) {
|
||||||
|
m_Buffer.reserve(initalCapacity);
|
||||||
|
}
|
||||||
|
|
||||||
|
DataBuffer::DataBuffer() : m_ReadOffset(0) {}
|
||||||
|
|
||||||
|
DataBuffer::DataBuffer(const DataBuffer& other) : m_Buffer(other.m_Buffer), m_ReadOffset(other.m_ReadOffset) {}
|
||||||
|
|
||||||
|
DataBuffer::DataBuffer(DataBuffer&& other) : m_Buffer(std::move(other.m_Buffer)), m_ReadOffset(std::move(other.m_ReadOffset)) {}
|
||||||
|
|
||||||
|
DataBuffer::DataBuffer(const std::string& str) : m_Buffer(str.begin(), str.end()), m_ReadOffset(0) {}
|
||||||
|
|
||||||
|
DataBuffer::DataBuffer(const DataBuffer& other, Data::difference_type offset) : m_ReadOffset(0) {
|
||||||
|
m_Buffer.reserve(other.GetSize() - static_cast<std::size_t>(offset));
|
||||||
|
std::copy(other.m_Buffer.begin() + offset, other.m_Buffer.end(), std::back_inserter(m_Buffer));
|
||||||
|
}
|
||||||
|
|
||||||
|
DataBuffer& DataBuffer::operator=(const DataBuffer& other) {
|
||||||
|
m_Buffer = other.m_Buffer;
|
||||||
|
m_ReadOffset = other.m_ReadOffset;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
DataBuffer& DataBuffer::operator=(DataBuffer&& other) {
|
||||||
|
m_Buffer = std::move(other.m_Buffer);
|
||||||
|
m_ReadOffset = std::move(other.m_ReadOffset);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DataBuffer::SetReadOffset(std::size_t pos) {
|
||||||
|
assert(pos <= GetSize());
|
||||||
|
m_ReadOffset = pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::size_t DataBuffer::GetSize() const {
|
||||||
|
return m_Buffer.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::size_t DataBuffer::GetRemaining() const {
|
||||||
|
return m_Buffer.size() - m_ReadOffset;
|
||||||
|
}
|
||||||
|
|
||||||
|
DataBuffer::iterator DataBuffer::begin() {
|
||||||
|
return m_Buffer.begin();
|
||||||
|
}
|
||||||
|
|
||||||
|
DataBuffer::iterator DataBuffer::end() {
|
||||||
|
return m_Buffer.end();
|
||||||
|
}
|
||||||
|
|
||||||
|
DataBuffer::const_iterator DataBuffer::begin() const {
|
||||||
|
return m_Buffer.begin();
|
||||||
|
}
|
||||||
|
|
||||||
|
DataBuffer::const_iterator DataBuffer::end() const {
|
||||||
|
return m_Buffer.end();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::ostream& operator<<(std::ostream& os, const DataBuffer& buffer) {
|
||||||
|
for (unsigned char u : buffer)
|
||||||
|
os << std::hex << std::setfill('0') << std::setw(2) << static_cast<int>(u) << " ";
|
||||||
|
os << std::dec;
|
||||||
|
return os;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DataBuffer::ReadFile(const std::string& fileName) {
|
||||||
|
try {
|
||||||
|
std::ifstream file(fileName, std::istream::binary);
|
||||||
|
std::ostringstream ss;
|
||||||
|
ss << file.rdbuf();
|
||||||
|
const std::string& s = ss.str();
|
||||||
|
m_Buffer = DataBuffer::Data(s.begin(), s.end());
|
||||||
|
m_ReadOffset = 0;
|
||||||
|
} catch (std::exception& e) {
|
||||||
|
std::cerr << "Failed to read file : " << e.what() << std::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return m_Buffer.size() > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DataBuffer::WriteFile(const std::string& fileName) const {
|
||||||
|
try {
|
||||||
|
std::ofstream file(fileName, std::ostream::binary);
|
||||||
|
file.write(reinterpret_cast<const char*>(m_Buffer.data()), static_cast<std::streamsize>(m_Buffer.size()));
|
||||||
|
file.flush();
|
||||||
|
} catch (std::exception& e) {
|
||||||
|
std::cerr << "Failed to write file : " << e.what() << std::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
284
src/Common/DataBuffer.h
Normal file
@@ -0,0 +1,284 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \file DataBuffer.h
|
||||||
|
* \brief File containing the blitz::DataBuffer class
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cassert>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <cstring>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \class DataBuffer
|
||||||
|
* \brief Class used to manipulate memory
|
||||||
|
*/
|
||||||
|
class DataBuffer {
|
||||||
|
private:
|
||||||
|
typedef std::vector<std::uint8_t> Data;
|
||||||
|
Data m_Buffer;
|
||||||
|
std::size_t m_ReadOffset;
|
||||||
|
|
||||||
|
public:
|
||||||
|
typedef Data::iterator iterator;
|
||||||
|
typedef Data::const_iterator const_iterator;
|
||||||
|
typedef Data::reference reference;
|
||||||
|
typedef Data::const_reference const_reference;
|
||||||
|
typedef Data::difference_type difference_type;
|
||||||
|
|
||||||
|
DataBuffer();
|
||||||
|
DataBuffer(std::size_t initalCapacity);
|
||||||
|
DataBuffer(const DataBuffer& other);
|
||||||
|
DataBuffer(const DataBuffer& other, difference_type offset);
|
||||||
|
DataBuffer(DataBuffer&& other);
|
||||||
|
DataBuffer(const std::string& str);
|
||||||
|
|
||||||
|
DataBuffer& operator=(const DataBuffer& other);
|
||||||
|
DataBuffer& operator=(DataBuffer&& other);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Append data to the buffer
|
||||||
|
*/
|
||||||
|
template <typename T>
|
||||||
|
void Append(const T& data) {
|
||||||
|
std::size_t size = sizeof(data);
|
||||||
|
std::size_t end_pos = m_Buffer.size();
|
||||||
|
m_Buffer.resize(m_Buffer.size() + size);
|
||||||
|
std::memcpy(&m_Buffer[end_pos], &data, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Append data to the buffer
|
||||||
|
*/
|
||||||
|
template <typename T>
|
||||||
|
DataBuffer& operator<<(const T& data) {
|
||||||
|
Append(data);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Append a string to the buffer
|
||||||
|
* \warning Don't use it for binary data !
|
||||||
|
* \param str The string to append
|
||||||
|
*/
|
||||||
|
DataBuffer& operator<<(const std::string& str) {
|
||||||
|
std::size_t strlen = str.length() + 1; // including null character
|
||||||
|
Resize(GetSize() + strlen);
|
||||||
|
std::memcpy(m_Buffer.data() + GetSize() - strlen, str.data(), strlen);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Append data to the buffer from another const buffer
|
||||||
|
* \param data The buffer to append
|
||||||
|
*/
|
||||||
|
DataBuffer& operator<<(const DataBuffer& data) {
|
||||||
|
m_Buffer.insert(m_Buffer.end(), data.begin(), data.end());
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Read some data from the buffer and assign to desired variable
|
||||||
|
*/
|
||||||
|
template <typename T>
|
||||||
|
DataBuffer& operator>>(T& data) {
|
||||||
|
assert(m_ReadOffset + sizeof(T) <= GetSize());
|
||||||
|
data = *(reinterpret_cast<T*>(&m_Buffer[m_ReadOffset]));
|
||||||
|
m_ReadOffset += sizeof(T);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Read some data from the buffer and assign to the new buffer
|
||||||
|
* \param data The buffer to assign
|
||||||
|
*/
|
||||||
|
DataBuffer& operator>>(DataBuffer& data) {
|
||||||
|
data.Resize(GetSize() - m_ReadOffset);
|
||||||
|
std::copy(m_Buffer.begin() + static_cast<difference_type>(m_ReadOffset), m_Buffer.end(), data.begin());
|
||||||
|
m_ReadOffset = m_Buffer.size();
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Read a string from the buffer
|
||||||
|
* \param str The string to assign in the new buffer
|
||||||
|
* \warning Don't use it for binary data !
|
||||||
|
*/
|
||||||
|
DataBuffer& operator>>(std::string& str) {
|
||||||
|
std::size_t stringSize =
|
||||||
|
strlen(reinterpret_cast<const char*>(m_Buffer.data()) + m_ReadOffset) + 1; // including null character
|
||||||
|
str.resize(stringSize);
|
||||||
|
std::copy(m_Buffer.begin() + static_cast<difference_type>(m_ReadOffset),
|
||||||
|
m_Buffer.begin() + static_cast<difference_type>(m_ReadOffset + stringSize), str.begin());
|
||||||
|
m_ReadOffset += stringSize;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Write some data to the buffer
|
||||||
|
* \param buffer The buffer to write
|
||||||
|
* \param amount The amount of data to write
|
||||||
|
*/
|
||||||
|
void WriteSome(const char* buffer, std::size_t amount) {
|
||||||
|
std::size_t end_pos = m_Buffer.size();
|
||||||
|
m_Buffer.resize(m_Buffer.size() + amount);
|
||||||
|
std::memcpy(m_Buffer.data() + end_pos, buffer, amount);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Write some data to the buffer
|
||||||
|
* \param buffer The buffer to write
|
||||||
|
* \param amount The amount of data to write
|
||||||
|
*/
|
||||||
|
void WriteSome(const std::uint8_t* buffer, std::size_t amount) {
|
||||||
|
std::size_t end_pos = m_Buffer.size();
|
||||||
|
m_Buffer.resize(m_Buffer.size() + amount);
|
||||||
|
std::memcpy(m_Buffer.data() + end_pos, buffer, amount);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Read some data from the buffer
|
||||||
|
* \param buffer The buffer to Read
|
||||||
|
* \param amount The amount of data from the buffer
|
||||||
|
*/
|
||||||
|
void ReadSome(char* buffer, std::size_t amount) {
|
||||||
|
assert(m_ReadOffset + amount <= GetSize());
|
||||||
|
std::copy_n(m_Buffer.begin() + static_cast<difference_type>(m_ReadOffset), amount, buffer);
|
||||||
|
m_ReadOffset += amount;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Read some data from the buffer
|
||||||
|
* \param buffer The buffer to Read
|
||||||
|
* \param amount The amount of data from the buffer
|
||||||
|
*/
|
||||||
|
void ReadSome(std::uint8_t* buffer, std::size_t amount) {
|
||||||
|
assert(m_ReadOffset + amount <= GetSize());
|
||||||
|
std::copy_n(m_Buffer.begin() + static_cast<difference_type>(m_ReadOffset), amount, buffer);
|
||||||
|
m_ReadOffset += amount;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Read some data from the buffer
|
||||||
|
* \param buffer The buffer to Read
|
||||||
|
* \param amount The amount of data from the buffer
|
||||||
|
*/
|
||||||
|
void ReadSome(DataBuffer& buffer, std::size_t amount) {
|
||||||
|
assert(m_ReadOffset + amount <= GetSize());
|
||||||
|
buffer.Resize(amount);
|
||||||
|
std::copy_n(m_Buffer.begin() + static_cast<difference_type>(m_ReadOffset), amount, buffer.begin());
|
||||||
|
m_ReadOffset += amount;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Resize the buffer
|
||||||
|
* \param size The new size of the buffer
|
||||||
|
*/
|
||||||
|
void Resize(std::size_t size) {
|
||||||
|
m_Buffer.resize(size);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Reserve some space in the buffer
|
||||||
|
* \param amount The amount of space to reserve
|
||||||
|
*/
|
||||||
|
void Reserve(std::size_t amount) {
|
||||||
|
m_Buffer.reserve(amount);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Clear the buffer
|
||||||
|
*/
|
||||||
|
void Clear() {
|
||||||
|
m_Buffer.clear();
|
||||||
|
m_ReadOffset = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief When the buffer has been read entirely
|
||||||
|
*/
|
||||||
|
bool IsFinished() const {
|
||||||
|
return m_ReadOffset >= m_Buffer.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Get the buffer data
|
||||||
|
*/
|
||||||
|
std::uint8_t* data() {
|
||||||
|
return m_Buffer.data();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Get the buffer data
|
||||||
|
*/
|
||||||
|
const std::uint8_t* data() const {
|
||||||
|
return m_Buffer.data();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Get the read offset
|
||||||
|
*/
|
||||||
|
std::size_t GetReadOffset() const {
|
||||||
|
return m_ReadOffset;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Set the read offset
|
||||||
|
* \param pos The new read offset
|
||||||
|
*/
|
||||||
|
void SetReadOffset(std::size_t pos);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Get the size of the buffer
|
||||||
|
*/
|
||||||
|
std::size_t GetSize() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Get the remaining size of the buffer
|
||||||
|
*/
|
||||||
|
std::size_t GetRemaining() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Read a file into the buffer
|
||||||
|
* \param fileName The name of the file to read
|
||||||
|
*/
|
||||||
|
bool ReadFile(const std::string& fileName);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Write a file into the buffer
|
||||||
|
* \param fileName The name of the file to write to
|
||||||
|
*/
|
||||||
|
bool WriteFile(const std::string& fileName) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Allocate the buffer on the heap
|
||||||
|
* \warning Don't forget to free the data !
|
||||||
|
*/
|
||||||
|
std::uint8_t* HeapAllocatedData() const {
|
||||||
|
std::uint8_t* newBuffer = new std::uint8_t[GetSize()];
|
||||||
|
std::memcpy(newBuffer, data(), GetSize());
|
||||||
|
return newBuffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Operator == to compare two DataBuffer
|
||||||
|
*/
|
||||||
|
bool operator==(const DataBuffer& other) const {
|
||||||
|
return m_Buffer == other.m_Buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
iterator begin();
|
||||||
|
iterator end();
|
||||||
|
const_iterator begin() const;
|
||||||
|
const_iterator end() const;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Operator << to write a DataBuffer to an ostream
|
||||||
|
*/
|
||||||
|
std::ostream& operator<<(std::ostream& os, const DataBuffer& buffer);
|
||||||
|
|
||||||
48
src/Common/VarInt.cpp
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
#include "VarInt.h"
|
||||||
|
|
||||||
|
#include "DataBuffer.h"
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
|
static constexpr int SEGMENT_BITS = 0x7F;
|
||||||
|
static constexpr int CONTINUE_BIT = 0x80;
|
||||||
|
|
||||||
|
std::size_t VarInt::GetSerializedLength() const {
|
||||||
|
DataBuffer buffer;
|
||||||
|
buffer << *this;
|
||||||
|
return buffer.GetSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
DataBuffer& operator<<(DataBuffer& out, const VarInt& var) {
|
||||||
|
std::uint64_t value = var.m_Value;
|
||||||
|
while (true) {
|
||||||
|
if ((value & ~static_cast<std::uint64_t>(SEGMENT_BITS)) == 0) {
|
||||||
|
out << static_cast<std::uint8_t>(value);
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
out << static_cast<std::uint8_t>((value & SEGMENT_BITS) | CONTINUE_BIT);
|
||||||
|
|
||||||
|
value >>= 7;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DataBuffer& operator>>(DataBuffer& in, VarInt& var) {
|
||||||
|
var.m_Value = 0;
|
||||||
|
unsigned int position = 0;
|
||||||
|
std::uint8_t currentByte;
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
in.ReadSome(¤tByte, 1);
|
||||||
|
var.m_Value |= static_cast<std::uint64_t>(currentByte & SEGMENT_BITS) << position;
|
||||||
|
|
||||||
|
if ((currentByte & CONTINUE_BIT) == 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
position += 7;
|
||||||
|
|
||||||
|
if (position >= 8 * sizeof(var.m_Value))
|
||||||
|
throw std::runtime_error("VarInt is too big");
|
||||||
|
}
|
||||||
|
|
||||||
|
return in;
|
||||||
|
}
|
||||||
54
src/Common/VarInt.h
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \file VarInt.h
|
||||||
|
* \brief File containing the blitz::VarInt class
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
class DataBuffer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \class VarInt
|
||||||
|
* \brief Variable-length format such that smaller numbers use fewer bytes.
|
||||||
|
*/
|
||||||
|
class VarInt {
|
||||||
|
private:
|
||||||
|
std::uint64_t m_Value;
|
||||||
|
|
||||||
|
public:
|
||||||
|
VarInt() : m_Value(0) {}
|
||||||
|
/**
|
||||||
|
* \brief Construct a variable integer from a value
|
||||||
|
* \param value The value of the variable integer
|
||||||
|
*/
|
||||||
|
VarInt(std::uint64_t value) : m_Value(value) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Get the value of the variable integer
|
||||||
|
*/
|
||||||
|
std::uint64_t GetValue() const {
|
||||||
|
return m_Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Get the length of the serialized variable integer
|
||||||
|
*/
|
||||||
|
std::size_t GetSerializedLength() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Serialize the variable integer
|
||||||
|
* \param out The buffer to write the serialized variable integer to
|
||||||
|
* \param var The variable integer to serialize
|
||||||
|
*/
|
||||||
|
friend DataBuffer& operator<<(DataBuffer& out, const VarInt& var);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Deserialize the variable integer
|
||||||
|
* \param in The buffer to read the serialized variable integer from
|
||||||
|
* \param var The variable integer to deserialize
|
||||||
|
*/
|
||||||
|
friend DataBuffer& operator>>(DataBuffer& in, VarInt& var);
|
||||||
|
};
|
||||||
@@ -366,6 +366,10 @@ bool Game::areBlocksBones() const {
|
|||||||
return this->parameters.getBoneBlocks();
|
return this->parameters.getBoneBlocks();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Game::isBoardInvisible() const {
|
||||||
|
return this->parameters.getInvisibleBoard();
|
||||||
|
}
|
||||||
|
|
||||||
const Board& Game::getBoard() const {
|
const Board& Game::getBoard() const {
|
||||||
return this->board.getBoard();
|
return this->board.getBoard();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -136,6 +136,11 @@ class Game {
|
|||||||
*/
|
*/
|
||||||
bool areBlocksBones() const;
|
bool areBlocksBones() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return If the board is currently invisible
|
||||||
|
*/
|
||||||
|
bool isBoardInvisible() const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return The board
|
* @return The board
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -24,6 +24,8 @@ void GameParameters::reset() {
|
|||||||
case MARATHON : {this->level = 1; break;}
|
case MARATHON : {this->level = 1; break;}
|
||||||
// goes from level 20 to 39
|
// goes from level 20 to 39
|
||||||
case MASTER : {this->level = 20; break;}
|
case MASTER : {this->level = 20; break;}
|
||||||
|
// goes from level 1 to 19
|
||||||
|
case INVISIBLE : {this->level = 1; break;}
|
||||||
// no gravity
|
// no gravity
|
||||||
case ZEN : {this->level = 0; break;}
|
case ZEN : {this->level = 0; break;}
|
||||||
default : this->level = 1;
|
default : this->level = 1;
|
||||||
@@ -34,7 +36,7 @@ void GameParameters::reset() {
|
|||||||
|
|
||||||
void GameParameters::lockedPiece(const LineClear& lineClear) {
|
void GameParameters::lockedPiece(const LineClear& lineClear) {
|
||||||
switch (this->gamemode) {
|
switch (this->gamemode) {
|
||||||
// modes where level increases
|
// modes where level increases with lines
|
||||||
case MARATHON :
|
case MARATHON :
|
||||||
case MASTER : {
|
case MASTER : {
|
||||||
int previousLines = this->clearedLines;
|
int previousLines = this->clearedLines;
|
||||||
@@ -51,9 +53,23 @@ void GameParameters::lockedPiece(const LineClear& lineClear) {
|
|||||||
default : this->clearedLines += lineClear.lines;
|
default : this->clearedLines += lineClear.lines;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int previousGrade = this->grade;
|
||||||
if (!((lineClear.lines == 0) && ((this->grade % 100) == 99))) {
|
if (!((lineClear.lines == 0) && ((this->grade % 100) == 99))) {
|
||||||
this->grade += (1 + lineClear.lines);
|
this->grade += (1 + lineClear.lines);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
switch (this->gamemode) {
|
||||||
|
// modes where level increases with grade
|
||||||
|
case INVISIBLE : {
|
||||||
|
if (previousGrade / 100 < this->grade / 100) {
|
||||||
|
this->level += 2;
|
||||||
|
this->updateStats();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// other modes
|
||||||
|
default : break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GameParameters::hasWon(int framesPassed) const {
|
bool GameParameters::hasWon(int framesPassed) const {
|
||||||
@@ -66,6 +82,8 @@ bool GameParameters::hasWon(int framesPassed) const {
|
|||||||
case MARATHON : return this->clearedLines >= 200;
|
case MARATHON : return this->clearedLines >= 200;
|
||||||
// win once 200 lines have been cleared
|
// win once 200 lines have been cleared
|
||||||
case MASTER : return this->clearedLines >= 200;
|
case MASTER : return this->clearedLines >= 200;
|
||||||
|
// win once 1000 grade has been passed
|
||||||
|
case INVISIBLE : return this->grade >= 1000;
|
||||||
// infinite mode
|
// infinite mode
|
||||||
case ZEN :
|
case ZEN :
|
||||||
default : return false;
|
default : return false;
|
||||||
@@ -84,7 +102,8 @@ void GameParameters::updateStats() {
|
|||||||
}
|
}
|
||||||
// 3 for slow-controls gamemodes
|
// 3 for slow-controls gamemodes
|
||||||
case MARATHON :
|
case MARATHON :
|
||||||
case MASTER : {
|
case MASTER :
|
||||||
|
case INVISIBLE : {
|
||||||
this->nextQueueLength = 3;
|
this->nextQueueLength = 3;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -94,10 +113,13 @@ void GameParameters::updateStats() {
|
|||||||
/* BONE BLOCKS */
|
/* BONE BLOCKS */
|
||||||
switch (this->gamemode) {
|
switch (this->gamemode) {
|
||||||
// blocks turns into bone blocks at level 30
|
// blocks turns into bone blocks at level 30
|
||||||
case MASTER : this->boneBlocks = (this->level >= 30);
|
case MASTER : {this->boneBlocks = (this->level >= 30); break;}
|
||||||
default : this->boneBlocks = false;
|
default : this->boneBlocks = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* INVISIBLE */
|
||||||
|
this->invisibleBoard = (this->gamemode == INVISIBLE);
|
||||||
|
|
||||||
/* GRAVITY */
|
/* GRAVITY */
|
||||||
// get gravity for an assumed 20-rows board
|
// get gravity for an assumed 20-rows board
|
||||||
static const int gravityPerLevel[] = {
|
static const int gravityPerLevel[] = {
|
||||||
@@ -152,6 +174,8 @@ void GameParameters::updateStats() {
|
|||||||
case MARATHON : {this->ARE = 24 - (this->level - 1); break;}
|
case MARATHON : {this->ARE = 24 - (this->level - 1); break;}
|
||||||
// starts at 400ms (24f) at lvl 20 and ends at 083ms (5f) at lvl 39
|
// starts at 400ms (24f) at lvl 20 and ends at 083ms (5f) at lvl 39
|
||||||
case MASTER : {this->ARE = 24 - (this->level - 20); break;}
|
case MASTER : {this->ARE = 24 - (this->level - 20); break;}
|
||||||
|
// fixed at 250ms (15f)
|
||||||
|
case INVISIBLE : {this->ARE = 15; break;}
|
||||||
// no ARE by default
|
// no ARE by default
|
||||||
default : this->ARE = 0;
|
default : this->ARE = 0;
|
||||||
}
|
}
|
||||||
@@ -228,6 +252,10 @@ bool GameParameters::getBoneBlocks() const {
|
|||||||
return this->boneBlocks;
|
return this->boneBlocks;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool GameParameters::getInvisibleBoard() const {
|
||||||
|
return this->invisibleBoard;
|
||||||
|
}
|
||||||
|
|
||||||
int GameParameters::getGravity() const {
|
int GameParameters::getGravity() const {
|
||||||
return this->gravity;
|
return this->gravity;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ class GameParameters {
|
|||||||
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
|
int nextQueueLength; // the number of pieces visibles in the next queue
|
||||||
bool boneBlocks; // wheter all blocks are bone blocks
|
bool boneBlocks; // wheter all blocks are bone blocks
|
||||||
|
bool invisibleBoard; // wheter the board is invisible
|
||||||
int gravity; // the gravity at which pieces drop
|
int gravity; // the gravity at which pieces drop
|
||||||
int lockDelay; // the time before the piece lock in place
|
int lockDelay; // the time before the piece lock in place
|
||||||
int forcedLockDelay; // the forced time before the piece lock in place
|
int forcedLockDelay; // the forced time before the piece lock in place
|
||||||
@@ -77,10 +78,15 @@ class GameParameters {
|
|||||||
int getNextQueueLength() const;
|
int getNextQueueLength() const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns wheter the blocks are currently bone blocks
|
* @return Wheter the blocks are currently bone blocks
|
||||||
*/
|
*/
|
||||||
bool getBoneBlocks() const;
|
bool getBoneBlocks() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Wheter the board is currently invisible
|
||||||
|
*/
|
||||||
|
bool getInvisibleBoard() const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return The current gravity for a 20-line high board
|
* @return The current gravity for a 20-line high board
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ enum Gamemode {
|
|||||||
MARATHON,
|
MARATHON,
|
||||||
ULTRA,
|
ULTRA,
|
||||||
MASTER,
|
MASTER,
|
||||||
|
INVISIBLE,
|
||||||
ZEN
|
ZEN
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -24,6 +25,7 @@ inline std::string getGamemodeName(Gamemode gamemode) {
|
|||||||
"MARATHON",
|
"MARATHON",
|
||||||
"ULTRA",
|
"ULTRA",
|
||||||
"MASTER",
|
"MASTER",
|
||||||
|
"INVISIBLE",
|
||||||
"ZEN"
|
"ZEN"
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -39,6 +41,7 @@ inline std::string getGamemodeGoal(Gamemode gamemode) {
|
|||||||
"200 lines",
|
"200 lines",
|
||||||
"2 minutes",
|
"2 minutes",
|
||||||
"200 lines",
|
"200 lines",
|
||||||
|
"1000 grade",
|
||||||
"Infinite"
|
"Infinite"
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
72
src/GraphicalUI/AppMenus/AppMenu.cpp
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
#include "AppMenu.h"
|
||||||
|
|
||||||
|
#include "../Settings.h"
|
||||||
|
#include "../PlayerCursor.h"
|
||||||
|
#include "../../Utils/AssetManager.h"
|
||||||
|
|
||||||
|
#include <stack>
|
||||||
|
#include <memory>
|
||||||
|
#include <optional>
|
||||||
|
#include <SFML/Graphics.hpp>
|
||||||
|
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AppMenu::updateMetaBinds() {
|
||||||
|
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Key::Enter)) {
|
||||||
|
this->enterPressed = true;
|
||||||
|
this->enterReleased = false;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this->enterReleased = this->enterPressed;
|
||||||
|
this->enterPressed = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Key::Escape)) {
|
||||||
|
this->escPressed = true;
|
||||||
|
this->escReleased = false;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this->escReleased = this->escPressed;
|
||||||
|
this->escPressed = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AppMenu::placeText(sf::Text& text, const std::optional<PlayerCursor>& playerCursor, const sf::String& string, float xPos, float yPos, const std::optional<sf::Vector2u>& cursorPos) const {
|
||||||
|
float sizeMultiplier = this->settings->getWindowSizeMultiplier();
|
||||||
|
|
||||||
|
text.setString(string);
|
||||||
|
if (playerCursor.has_value() && cursorPos.has_value()) {
|
||||||
|
text.setOutlineThickness((playerCursor.value().getPosition() == cursorPos.value()) ? (sizeMultiplier / 2) : 0);
|
||||||
|
}
|
||||||
|
text.setOrigin(sf::Vector2f({0, text.getLocalBounds().size.y / 2}));
|
||||||
|
text.setPosition(sf::Vector2f({sizeMultiplier * xPos, sizeMultiplier * yPos}));
|
||||||
|
this->renderWindow->draw(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AppMenu::placeTitle(sf::Text& text, const std::optional<PlayerCursor>& playerCursor, const sf::String& string, float yPos, const std::optional<sf::Vector2u>& cursorPos) const {
|
||||||
|
float sizeMultiplier = this->settings->getWindowSizeMultiplier();
|
||||||
|
|
||||||
|
text.setString(string);
|
||||||
|
if (playerCursor.has_value() && cursorPos.has_value()) {
|
||||||
|
text.setOutlineThickness((playerCursor.value().getPosition() == cursorPos.value()) ? (sizeMultiplier / 2) : 0);
|
||||||
|
}
|
||||||
|
text.setOrigin({text.getLocalBounds().getCenter().x, text.getLocalBounds().size.y / 2});
|
||||||
|
text.setPosition(sf::Vector2f({sizeMultiplier * 40.f, sizeMultiplier * yPos}));
|
||||||
|
this->renderWindow->draw(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
sf::Color AppMenu::getColorOfBlock(Block block, int luminosityShift) const {
|
||||||
|
Color rgbColor = BLOCKS_COLOR[block];
|
||||||
|
return sf::Color(std::clamp(rgbColor.red + luminosityShift, 0, 255),
|
||||||
|
std::clamp(rgbColor.green + luminosityShift, 0, 255),
|
||||||
|
std::clamp(rgbColor.blue + luminosityShift, 0, 255));
|
||||||
|
}
|
||||||
@@ -21,70 +21,21 @@ class AppMenu {
|
|||||||
bool enterReleased = false;
|
bool enterReleased = false;
|
||||||
bool escPressed = false;
|
bool escPressed = false;
|
||||||
bool escReleased = false;
|
bool escReleased = false;
|
||||||
sf::Font pressStartFont = sf::Font("data/fonts/pressstart/prstartk.ttf");
|
sf::Font pressStartFont;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
AppMenu(std::shared_ptr<MenuStack> menuStack, std::shared_ptr<Settings> settings, std::shared_ptr<sf::RenderWindow> renderWindow) :
|
AppMenu(std::shared_ptr<MenuStack> menuStack, std::shared_ptr<Settings> settings, std::shared_ptr<sf::RenderWindow> renderWindow);
|
||||||
menuStack(menuStack),
|
|
||||||
settings(settings),
|
|
||||||
renderWindow(renderWindow)
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void computeFrame() = 0;
|
virtual void computeFrame() = 0;
|
||||||
|
|
||||||
virtual void drawFrame() const = 0;
|
virtual void drawFrame() const = 0;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void updateMetaBinds() {
|
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)) {
|
void placeText(sf::Text& text, const std::optional<PlayerCursor>& playerCursor, const sf::String& string, float xPos, float yPos, const std::optional<sf::Vector2u>& cursorPos) const;
|
||||||
escPressed = true;
|
|
||||||
escReleased = false;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
escReleased = escPressed;
|
|
||||||
escPressed = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void placeText(sf::Text& text, const std::optional<PlayerCursor>& playerCursor, const sf::String& string, float xPos, float yPos, const std::optional<sf::Vector2u>& cursorPos) const {
|
void placeTitle(sf::Text& text, const std::optional<PlayerCursor>& playerCursor, const sf::String& string, float yPos, const std::optional<sf::Vector2u>& cursorPos) const;
|
||||||
float sizeMultiplier = this->settings->getWindowSizeMultiplier();
|
|
||||||
|
|
||||||
text.setString(string);
|
sf::Color getColorOfBlock(Block block, int luminosityShift) const;
|
||||||
if (playerCursor.has_value() && cursorPos.has_value()) {
|
|
||||||
text.setOutlineThickness((playerCursor.value().getPosition() == cursorPos.value()) ? (sizeMultiplier / 2) : 0);
|
|
||||||
}
|
|
||||||
text.setOrigin(sf::Vector2f({0, text.getLocalBounds().size.y / 2}));
|
|
||||||
text.setPosition(sf::Vector2f({sizeMultiplier * xPos, sizeMultiplier * yPos}));
|
|
||||||
this->renderWindow->draw(text);
|
|
||||||
}
|
|
||||||
|
|
||||||
void placeTitle(sf::Text& text, const std::optional<PlayerCursor>& playerCursor, const sf::String& string, float yPos, const std::optional<sf::Vector2u>& cursorPos) const {
|
|
||||||
float sizeMultiplier = this->settings->getWindowSizeMultiplier();
|
|
||||||
|
|
||||||
text.setString(string);
|
|
||||||
if (playerCursor.has_value() && cursorPos.has_value()) {
|
|
||||||
text.setOutlineThickness((playerCursor.value().getPosition() == cursorPos.value()) ? (sizeMultiplier / 2) : 0);
|
|
||||||
}
|
|
||||||
text.setOrigin({text.getLocalBounds().getCenter().x, text.getLocalBounds().size.y / 2});
|
|
||||||
text.setPosition(sf::Vector2f({sizeMultiplier * 40.f, sizeMultiplier * yPos}));
|
|
||||||
this->renderWindow->draw(text);
|
|
||||||
}
|
|
||||||
|
|
||||||
sf::Color getColorOfBlock(Block block, int luminosityShift) const {
|
|
||||||
Color rgbColor = BLOCKS_COLOR[block];
|
|
||||||
return sf::Color(std::clamp(rgbColor.red + luminosityShift, 0, 255),
|
|
||||||
std::clamp(rgbColor.green + luminosityShift, 0, 255),
|
|
||||||
std::clamp(rgbColor.blue + luminosityShift, 0, 255));
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -105,16 +105,25 @@ void GamePlayingAppMenu::computeFrame() {
|
|||||||
void GamePlayingAppMenu::drawFrame() const {
|
void GamePlayingAppMenu::drawFrame() const {
|
||||||
this->renderWindow->clear(sf::Color(200, 200, 200));
|
this->renderWindow->clear(sf::Color(200, 200, 200));
|
||||||
|
|
||||||
|
sf::Color bonesBlockColor(0, 0, 0);
|
||||||
|
sf::Color bonesBlockGhostColor(100, 100, 100);
|
||||||
|
bool areBlockBones = this->game.areBlocksBones();
|
||||||
|
bool isBoardInvisible = this->game.isBoardInvisible() && !(this->game.hasWon() || this->game.hasLost());
|
||||||
|
|
||||||
sf::Vector2f cellSize(this->cellSizeZoom, this->cellSizeZoom);
|
sf::Vector2f cellSize(this->cellSizeZoom, this->cellSizeZoom);
|
||||||
|
float cellOutlineThickness = this->cellSizeZoom / 4;
|
||||||
bool drawActivePiece = (this->game.getActivePiece() != nullptr) && (!this->game.hasLost());
|
bool drawActivePiece = (this->game.getActivePiece() != nullptr) && (!this->game.hasLost());
|
||||||
|
|
||||||
// board
|
// board
|
||||||
for (int y = this->game.getBoard().getBaseHeight() + 9; y >= 0; y--) {
|
for (int y = this->game.getBoard().getBaseHeight() + 9; y >= 0; y--) {
|
||||||
for (int x = 0; x < this->game.getBoard().getWidth(); x++) {
|
for (int x = 0; x < this->game.getBoard().getWidth(); x++) {
|
||||||
Block block = this->game.getBoard().getBlock(Position{x, y});
|
Block block = this->game.getBoard().getBlock(Position{x, y});
|
||||||
|
if (isBoardInvisible) block = NOTHING;
|
||||||
|
|
||||||
sf::RectangleShape cell(cellSize);
|
sf::RectangleShape cell(cellSize);
|
||||||
cell.setFillColor(this->getColorOfBlock(block, (block == NOTHING) ? 0 : -30));
|
cell.setFillColor((areBlockBones && block != NOTHING)
|
||||||
|
? bonesBlockColor
|
||||||
|
: this->getColorOfBlock(block, (block == NOTHING) ? 0 : -30));
|
||||||
cell.setPosition(this->getBoardBlockPosition(x, y));
|
cell.setPosition(this->getBoardBlockPosition(x, y));
|
||||||
this->renderWindow->draw(cell);
|
this->renderWindow->draw(cell);
|
||||||
}
|
}
|
||||||
@@ -122,7 +131,10 @@ void GamePlayingAppMenu::drawFrame() const {
|
|||||||
|
|
||||||
if (drawActivePiece) {
|
if (drawActivePiece) {
|
||||||
// ghost piece
|
// ghost piece
|
||||||
sf::Color ghostColor = this->getColorOfBlock(this->game.getActivePiece()->getBlockType(), 100);
|
sf::Color ghostColor = areBlockBones
|
||||||
|
? bonesBlockGhostColor
|
||||||
|
: this->getColorOfBlock(this->game.getActivePiece()->getBlockType(), 100);
|
||||||
|
|
||||||
for (const Position& position : this->game.getActivePiece()->getPositions()) {
|
for (const Position& position : this->game.getActivePiece()->getPositions()) {
|
||||||
Position cellPosition = (this->game.getGhostPiecePosition() + position);
|
Position cellPosition = (this->game.getGhostPiecePosition() + position);
|
||||||
|
|
||||||
@@ -133,13 +145,13 @@ void GamePlayingAppMenu::drawFrame() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// active piece outline
|
// active piece outline
|
||||||
float pieceOutlineSize = std::roundf(this->cellSizeZoom / 4);
|
|
||||||
sf::Color pieceOultlineColor = sf::Color(255, 255 - (255 * this->game.getForcedLockDelayProgression()), 255 - (255 * this->game.getForcedLockDelayProgression()));
|
sf::Color pieceOultlineColor = sf::Color(255, 255 - (255 * this->game.getForcedLockDelayProgression()), 255 - (255 * this->game.getForcedLockDelayProgression()));
|
||||||
|
|
||||||
for (const Position& position : this->game.getActivePiece()->getPositions()) {
|
for (const Position& position : this->game.getActivePiece()->getPositions()) {
|
||||||
Position cellPosition = (this->game.getActivePiecePosition() + position);
|
Position cellPosition = (this->game.getActivePiecePosition() + position);
|
||||||
|
|
||||||
sf::RectangleShape cell(cellSize);
|
sf::RectangleShape cell(cellSize);
|
||||||
cell.setOutlineThickness(pieceOutlineSize);
|
cell.setOutlineThickness(cellOutlineThickness);
|
||||||
cell.setOutlineColor(pieceOultlineColor);
|
cell.setOutlineColor(pieceOultlineColor);
|
||||||
cell.setPosition(this->getBoardBlockPosition(cellPosition.x, cellPosition.y));
|
cell.setPosition(this->getBoardBlockPosition(cellPosition.x, cellPosition.y));
|
||||||
this->renderWindow->draw(cell);
|
this->renderWindow->draw(cell);
|
||||||
@@ -154,7 +166,9 @@ void GamePlayingAppMenu::drawFrame() const {
|
|||||||
|
|
||||||
if (drawActivePiece) {
|
if (drawActivePiece) {
|
||||||
// active piece
|
// active piece
|
||||||
sf::Color pieceColor = this->getColorOfBlock(this->game.getActivePiece()->getBlockType(), -200 * (this->game.getLockDelayProgression()));
|
sf::Color pieceColor = areBlockBones
|
||||||
|
? bonesBlockColor
|
||||||
|
: this->getColorOfBlock(this->game.getActivePiece()->getBlockType(), -200 * (this->game.getLockDelayProgression()));
|
||||||
|
|
||||||
for (const Position& position : this->game.getActivePiece()->getPositions()) {
|
for (const Position& position : this->game.getActivePiece()->getPositions()) {
|
||||||
Position cellPosition = (this->game.getActivePiecePosition() + position);
|
Position cellPosition = (this->game.getActivePiecePosition() + position);
|
||||||
@@ -173,7 +187,9 @@ void GamePlayingAppMenu::drawFrame() const {
|
|||||||
nextBox.position.y -= upShift;
|
nextBox.position.y -= upShift;
|
||||||
|
|
||||||
sf::Vector2f nextCellSize(this->nextCellSizeZoom, this->nextCellSizeZoom);
|
sf::Vector2f nextCellSize(this->nextCellSizeZoom, this->nextCellSizeZoom);
|
||||||
sf::Color color = this->getColorOfBlock(this->game.getNextPieces().at(i).getBlockType(), 0);
|
sf::Color pieceColor = areBlockBones
|
||||||
|
? bonesBlockColor
|
||||||
|
: this->getColorOfBlock(this->game.getNextPieces().at(i).getBlockType(), 0);
|
||||||
sf::Color boxColor = sf::Color(180, 180, 180);
|
sf::Color boxColor = sf::Color(180, 180, 180);
|
||||||
|
|
||||||
int lowestRank = 0;
|
int lowestRank = 0;
|
||||||
@@ -181,7 +197,7 @@ void GamePlayingAppMenu::drawFrame() const {
|
|||||||
for (int x = 0; x < this->game.getNextPieces().at(i).getLength(); x++) {
|
for (int x = 0; x < this->game.getNextPieces().at(i).getLength(); x++) {
|
||||||
sf::RectangleShape cell(nextCellSize);
|
sf::RectangleShape cell(nextCellSize);
|
||||||
if (this->game.getNextPieces().at(i).getPositions().contains(Position{x, y})) {
|
if (this->game.getNextPieces().at(i).getPositions().contains(Position{x, y})) {
|
||||||
cell.setFillColor(color);
|
cell.setFillColor(pieceColor);
|
||||||
lowestRank = y;
|
lowestRank = y;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@@ -199,7 +215,9 @@ void GamePlayingAppMenu::drawFrame() const {
|
|||||||
// hold box
|
// hold box
|
||||||
if (this->game.getHeldPiece() != nullptr) {
|
if (this->game.getHeldPiece() != nullptr) {
|
||||||
sf::Vector2f holdCellSize(this->holdCellSizeZoom, this->holdCellSizeZoom);
|
sf::Vector2f holdCellSize(this->holdCellSizeZoom, this->holdCellSizeZoom);
|
||||||
sf::Color color = this->getColorOfBlock(this->game.getHeldPiece()->getBlockType(), 0);
|
sf::Color color = areBlockBones
|
||||||
|
? bonesBlockColor
|
||||||
|
: this->getColorOfBlock(this->game.getHeldPiece()->getBlockType(), 0);
|
||||||
sf::Color boxColor = sf::Color(180, 180, 180);
|
sf::Color boxColor = sf::Color(180, 180, 180);
|
||||||
|
|
||||||
for (int y = 0; y < this->game.getHeldPiece()->getLength(); y++) {
|
for (int y = 0; y < this->game.getHeldPiece()->getLength(); y++) {
|
||||||
|
|||||||
@@ -13,7 +13,7 @@
|
|||||||
|
|
||||||
GameSettingsAppMenu::GameSettingsAppMenu(std::shared_ptr<MenuStack> menuStack, std::shared_ptr<Settings> settings, std::shared_ptr<sf::RenderWindow> renderWindow) :
|
GameSettingsAppMenu::GameSettingsAppMenu(std::shared_ptr<MenuStack> menuStack, std::shared_ptr<Settings> settings, std::shared_ptr<sf::RenderWindow> renderWindow) :
|
||||||
AppMenu(menuStack, settings, renderWindow),
|
AppMenu(menuStack, settings, renderWindow),
|
||||||
playerCursor({2, 3, 2}) {
|
playerCursor({2, 3, 3}) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -33,7 +33,8 @@ void GameSettingsAppMenu::computeFrame() {
|
|||||||
case 2 : {
|
case 2 : {
|
||||||
switch (this->playerCursor.getPosition().x) {
|
switch (this->playerCursor.getPosition().x) {
|
||||||
case 0 : {this->settings->setGamemode(MASTER); break;}
|
case 0 : {this->settings->setGamemode(MASTER); break;}
|
||||||
case 1 : {this->settings->setGamemode(ZEN); break;}
|
case 1 : {this->settings->setGamemode(INVISIBLE); break;}
|
||||||
|
case 2 : {this->settings->setGamemode(ZEN); break;}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -76,7 +77,8 @@ void GameSettingsAppMenu::drawFrame() const {
|
|||||||
this->placeText(text, this->playerCursor, "MARATHON", 25.f, 35.f, sf::Vector2u{1, 1});
|
this->placeText(text, this->playerCursor, "MARATHON", 25.f, 35.f, sf::Vector2u{1, 1});
|
||||||
this->placeText(text, this->playerCursor, "ULTRA", 50.f, 35.f, sf::Vector2u{2, 1});
|
this->placeText(text, this->playerCursor, "ULTRA", 50.f, 35.f, sf::Vector2u{2, 1});
|
||||||
this->placeText(text, this->playerCursor, "MASTER", 5.f, 45.f, sf::Vector2u{0, 2});
|
this->placeText(text, this->playerCursor, "MASTER", 5.f, 45.f, sf::Vector2u{0, 2});
|
||||||
this->placeText(text, this->playerCursor, "ZEN", 25.f, 45.f, sf::Vector2u{1, 2});
|
this->placeText(text, this->playerCursor, "INVISIBLE", 25.f, 45.f, sf::Vector2u{1, 2});
|
||||||
|
this->placeText(text, this->playerCursor, "ZEN", 50.f, 45.f, sf::Vector2u{2, 2});
|
||||||
|
|
||||||
this->renderWindow->display();
|
this->renderWindow->display();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,14 +10,15 @@
|
|||||||
|
|
||||||
InfoAppMenu::InfoAppMenu(std::shared_ptr<MenuStack> menuStack, std::shared_ptr<Settings> settings, std::shared_ptr<sf::RenderWindow> renderWindow) :
|
InfoAppMenu::InfoAppMenu(std::shared_ptr<MenuStack> menuStack, std::shared_ptr<Settings> settings, std::shared_ptr<sf::RenderWindow> renderWindow) :
|
||||||
AppMenu(menuStack, settings, renderWindow),
|
AppMenu(menuStack, settings, renderWindow),
|
||||||
playerCursor({4}),
|
playerCursor({INFO_SECTIONS_COUNT}),
|
||||||
sectionsName(
|
sectionsName({
|
||||||
"< ABOUT >",
|
"< ABOUT >",
|
||||||
|
"< PIECES TYPES >",
|
||||||
|
"< 0 DEGREES ROTATIONS >",
|
||||||
"< ROTATION SYSTEM >",
|
"< ROTATION SYSTEM >",
|
||||||
"< SCORING >",
|
"< SCORING >"
|
||||||
"< 0 DEGREES ROTATIONS >"
|
}),
|
||||||
),
|
sectionsContent({
|
||||||
sectionsContent(
|
|
||||||
"This game is written in C++,\n"
|
"This game is written in C++,\n"
|
||||||
"using SFML 3 for the GUI.\n"
|
"using SFML 3 for the GUI.\n"
|
||||||
"It has been inspired by other\n"
|
"It has been inspired by other\n"
|
||||||
@@ -27,6 +28,25 @@ InfoAppMenu::InfoAppMenu(std::shared_ptr<MenuStack> menuStack, std::shared_ptr<S
|
|||||||
"to them in any ways.\n"
|
"to them in any ways.\n"
|
||||||
"Current version: beta.",
|
"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"
|
"This game uses its own\n"
|
||||||
"Rotation Sytem, called AutoRS.\n"
|
"Rotation Sytem, called AutoRS.\n"
|
||||||
"The rotation center is always the\n"
|
"The rotation center is always the\n"
|
||||||
@@ -46,18 +66,8 @@ InfoAppMenu::InfoAppMenu(std::shared_ptr<MenuStack> menuStack, std::shared_ptr<S
|
|||||||
"and doubles the score gained.\n"
|
"and doubles the score gained.\n"
|
||||||
"A spin is detected when the piece is\n"
|
"A spin is detected when the piece is\n"
|
||||||
"locked in place, a mini-spin simply\n"
|
"locked in place, a mini-spin simply\n"
|
||||||
"when the last move was a kick.",
|
"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."
|
|
||||||
) {
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,12 +7,13 @@
|
|||||||
#include <memory>
|
#include <memory>
|
||||||
#include <SFML/Graphics.hpp>
|
#include <SFML/Graphics.hpp>
|
||||||
|
|
||||||
|
static const int INFO_SECTIONS_COUNT = 5;
|
||||||
|
|
||||||
class InfoAppMenu : public AppMenu {
|
class InfoAppMenu : public AppMenu {
|
||||||
private:
|
private:
|
||||||
PlayerCursor playerCursor;
|
PlayerCursor playerCursor;
|
||||||
sf::String sectionsName[4];
|
std::array<std::string, INFO_SECTIONS_COUNT> sectionsName;
|
||||||
sf::String sectionsContent[4];
|
std::array<std::string, INFO_SECTIONS_COUNT> sectionsContent;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
InfoAppMenu(std::shared_ptr<MenuStack> menuStack, std::shared_ptr<Settings> settings, std::shared_ptr<sf::RenderWindow> renderWindow);
|
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 "AppMenu.h"
|
||||||
#include "../PlayerCursor.h"
|
#include "../PlayerCursor.h"
|
||||||
|
#include "../../Utils/AssetManager.h"
|
||||||
|
|
||||||
#include <stack>
|
#include <stack>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
@@ -22,8 +23,9 @@ SettingsKeybindsAppMenu::SettingsKeybindsAppMenu(std::shared_ptr<MenuStack> menu
|
|||||||
std::string textureName = ACTION_NAMES[action];
|
std::string textureName = ACTION_NAMES[action];
|
||||||
textureName = std::regex_replace(textureName, std::regex(" "), "");
|
textureName = std::regex_replace(textureName, std::regex(" "), "");
|
||||||
|
|
||||||
std::filesystem::path texturePath("data/images/keybinds/" + textureName + ".png");
|
const Asset& textureData = getResource("data/images/keybinds/" + textureName + ".png");
|
||||||
this->iconTextures[action] = sf::Texture(texturePath, false, {{0, 0}, {16, 16}});
|
|
||||||
|
this->iconTextures[action] = sf::Texture(textureData.data, textureData.size, false, {{0, 0}, {16, 16}});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ int main() {
|
|||||||
PiecesFiles pf;
|
PiecesFiles pf;
|
||||||
for (int i = 1; i <= MAXIMUM_PIECES_SIZE; i++) {
|
for (int i = 1; i <= MAXIMUM_PIECES_SIZE; i++) {
|
||||||
if (!std::filesystem::exists("data/pieces/" + std::to_string(i) + "minos.bin")) {
|
if (!std::filesystem::exists("data/pieces/" + std::to_string(i) + "minos.bin")) {
|
||||||
#ifdef NDEBUG
|
#ifndef DEBUG
|
||||||
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;
|
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
|
#endif
|
||||||
|
|
||||||
@@ -23,8 +23,8 @@ int main() {
|
|||||||
pf.savePieces(i);
|
pf.savePieces(i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#ifndef NDEBUG
|
#ifdef DEBUG
|
||||||
std::cout << "IMPORTANT: you are currently in debug mode, if you wish to use bigger pieces, type 'xmake f -m release'." << std::endl;
|
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;
|
bool everythingGenerated = true;
|
||||||
for (int i = DEBUG_PIECES_SIZE; i <= RELEASE_PIECES_SIZE; i++) {
|
for (int i = DEBUG_PIECES_SIZE; i <= RELEASE_PIECES_SIZE; i++) {
|
||||||
@@ -33,7 +33,7 @@ int main() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!everythingGenerated) {
|
if (!everythingGenerated) {
|
||||||
std::cout << "NOTE : you do not have all pieces generated, generating can take several minutes." << std::endl;
|
std::cout << "NOTE: You do not have all pieces generated, generating can take several minutes." << std::endl;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -70,6 +70,10 @@ int main() {
|
|||||||
|
|
||||||
|
|
||||||
void resetSettingsFile() {
|
void resetSettingsFile() {
|
||||||
|
if (!std::filesystem::exists("data/config")) {
|
||||||
|
std::filesystem::create_directories("data/config");
|
||||||
|
}
|
||||||
|
|
||||||
std::ofstream settingsFile("data/config/settings.bin", std::ios::trunc | std::ios::binary);
|
std::ofstream settingsFile("data/config/settings.bin", std::ios::trunc | std::ios::binary);
|
||||||
char byte;
|
char byte;
|
||||||
|
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ void Generator::generate(int polyominoSize, int lastAddedPositionNumber, int nex
|
|||||||
}
|
}
|
||||||
|
|
||||||
// generate the list of candidate positions
|
// generate the list of candidate positions
|
||||||
for (Position position : this->currentTestedShape) {
|
for (const Position position : this->currentTestedShape) {
|
||||||
this->tryToAddCandidatePosition(Position{position.x, position.y + 1}, nextAvaibleNumber, candidatePositions);
|
this->tryToAddCandidatePosition(Position{position.x, position.y + 1}, nextAvaibleNumber, candidatePositions);
|
||||||
this->tryToAddCandidatePosition(Position{position.x + 1, position.y}, nextAvaibleNumber, candidatePositions);
|
this->tryToAddCandidatePosition(Position{position.x + 1, position.y}, nextAvaibleNumber, candidatePositions);
|
||||||
this->tryToAddCandidatePosition(Position{position.x, position.y - 1}, nextAvaibleNumber, candidatePositions);
|
this->tryToAddCandidatePosition(Position{position.x, position.y - 1}, nextAvaibleNumber, candidatePositions);
|
||||||
@@ -58,7 +58,7 @@ void Generator::generate(int polyominoSize, int lastAddedPositionNumber, int nex
|
|||||||
}
|
}
|
||||||
|
|
||||||
// try adding a square only to positions with a higher number than the last one
|
// try adding a square only to positions with a higher number than the last one
|
||||||
for (auto [key, val] : candidatePositions) {
|
for (const auto [key, val] : candidatePositions) {
|
||||||
if (val > lastAddedPositionNumber) {
|
if (val > lastAddedPositionNumber) {
|
||||||
this->currentTestedShape.insert(key);
|
this->currentTestedShape.insert(key);
|
||||||
this->generate(polyominoSize, val, nextAvaibleNumber, (polyominoSize == this->currentTestedShape.size()) ? std::map<Position, int>() : candidatePositions);
|
this->generate(polyominoSize, val, nextAvaibleNumber, (polyominoSize == this->currentTestedShape.size()) ? std::map<Position, int>() : candidatePositions);
|
||||||
|
|||||||
@@ -10,6 +10,8 @@
|
|||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
||||||
|
#include "../Common/Compression.h"
|
||||||
|
|
||||||
|
|
||||||
PiecesFiles::PiecesFiles() {
|
PiecesFiles::PiecesFiles() {
|
||||||
}
|
}
|
||||||
@@ -19,34 +21,46 @@ bool PiecesFiles::savePieces(int polyominoSize) const {
|
|||||||
if (!this->getFilePath(polyominoSize, filePath)) {
|
if (!this->getFilePath(polyominoSize, filePath)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
std::ofstream piecesFile(filePath, std::ios::trunc | std::ios::binary);
|
|
||||||
if (!piecesFile.good()) {
|
Generator generator;
|
||||||
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
Generator generator;
|
constexpr std::size_t INITIAL_CAPACITY = 2048;
|
||||||
std::vector<Polyomino> nMinos = generator.generatePolyominoes(polyominoSize);
|
DataBuffer buffer(INITIAL_CAPACITY);
|
||||||
|
|
||||||
// sorting the polyominoes is done after setting spawn position to ensure the order is always the same
|
// 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();
|
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
|
// write the characteristics of the piece
|
||||||
char infoByte = (nMino.isConvex() << 7) + (nMino.hasHole() << 6) + nMino.getLength();
|
bool isConvex = polyomino.isConvex();
|
||||||
piecesFile.write(&infoByte, 1);
|
bool hasHole = (isConvex) ? false : polyomino.hasHole();
|
||||||
|
std::uint8_t infoByte = (isConvex << 7) + (hasHole << 6) + polyomino.getLength();
|
||||||
|
buffer << infoByte;
|
||||||
|
|
||||||
// write the positions of the piece
|
// write the positions of the piece
|
||||||
char positionByte;
|
std::uint8_t positionByte;
|
||||||
for (Position position : nMino.getPositions()) {
|
for (const Position position : polyomino.getPositions()) {
|
||||||
positionByte = (position.x << 4) + position.y;
|
positionByte = (position.x << 4) + position.y;
|
||||||
piecesFile.write(&positionByte, 1);
|
buffer << positionByte;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
DataBuffer compressed = Compress(buffer);
|
||||||
|
|
||||||
|
return compressed.WriteFile(filePath);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PiecesFiles::loadPieces(int polyominoSize, std::vector<Piece>& pieces, std::vector<int>& convexPieces, std::vector<int>& holelessPieces, std::vector<int>& otherPieces) const {
|
bool PiecesFiles::loadPieces(int polyominoSize, std::vector<Piece>& pieces, std::vector<int>& convexPieces, std::vector<int>& holelessPieces, std::vector<int>& otherPieces) const {
|
||||||
@@ -54,6 +68,13 @@ bool PiecesFiles::loadPieces(int polyominoSize, std::vector<Piece>& pieces, std:
|
|||||||
if (!this->getFilePath(polyominoSize, filePath)) {
|
if (!this->getFilePath(polyominoSize, filePath)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DataBuffer compressed;
|
||||||
|
if(!compressed.ReadFile(filePath))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
DataBuffer buffer = Decompress(compressed, compressed.GetSize());
|
||||||
|
|
||||||
std::ifstream piecesFile(filePath, std::ios::binary);
|
std::ifstream piecesFile(filePath, std::ios::binary);
|
||||||
if (!piecesFile.good()) {
|
if (!piecesFile.good()) {
|
||||||
return false;
|
return false;
|
||||||
@@ -76,10 +97,11 @@ bool PiecesFiles::loadPieces(int polyominoSize, std::vector<Piece>& pieces, std:
|
|||||||
char xMask = 0b1111'0000;
|
char xMask = 0b1111'0000;
|
||||||
char yMask = 0b0000'1111;
|
char yMask = 0b0000'1111;
|
||||||
|
|
||||||
char infoByte;
|
std::uint8_t infoByte;
|
||||||
int i = 0;
|
int i = 0;
|
||||||
while (piecesFile.get(infoByte)) {
|
while (!buffer.IsFinished()) {
|
||||||
if (piecesFile.eof()) break;
|
// if (piecesFile.eof()) break;
|
||||||
|
buffer >> infoByte;
|
||||||
|
|
||||||
// read piece infos
|
// read piece infos
|
||||||
bool isConvex = (infoByte & convexMask) >> 7;
|
bool isConvex = (infoByte & convexMask) >> 7;
|
||||||
@@ -88,9 +110,9 @@ bool PiecesFiles::loadPieces(int polyominoSize, std::vector<Piece>& pieces, std:
|
|||||||
|
|
||||||
// read positions
|
// read positions
|
||||||
std::set<Position> piecePositions;
|
std::set<Position> piecePositions;
|
||||||
char positionByte;
|
std::uint8_t positionByte;
|
||||||
for (int i = 0; i < polyominoSize; i++) {
|
for (int i = 0; i < polyominoSize; i++) {
|
||||||
piecesFile.get(positionByte);
|
buffer >> positionByte;
|
||||||
int x = ((unsigned char) positionByte & xMask) >> 4;
|
int x = ((unsigned char) positionByte & xMask) >> 4;
|
||||||
int y = positionByte & yMask;
|
int y = positionByte & yMask;
|
||||||
piecePositions.insert(Position{x, y});
|
piecePositions.insert(Position{x, y});
|
||||||
@@ -119,6 +141,11 @@ bool PiecesFiles::loadPieces(int polyominoSize, std::vector<Piece>& pieces, std:
|
|||||||
|
|
||||||
bool PiecesFiles::getFilePath(int polyominoSize, std::string& filePath) const {
|
bool PiecesFiles::getFilePath(int polyominoSize, std::string& filePath) const {
|
||||||
std::string dataFolderPath = "data/pieces/";
|
std::string dataFolderPath = "data/pieces/";
|
||||||
|
|
||||||
|
if (!std::filesystem::exists(dataFolderPath)) {
|
||||||
|
std::filesystem::create_directories(dataFolderPath);
|
||||||
|
}
|
||||||
|
|
||||||
if (!std::filesystem::is_directory(dataFolderPath)) {
|
if (!std::filesystem::is_directory(dataFolderPath)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,13 +22,18 @@ class PiecesFiles {
|
|||||||
*/
|
*/
|
||||||
bool savePieces(int polyominoSize) const;
|
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
|
* 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
|
* @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;
|
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
|
* 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
|
* @return If the data folder was found
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ Polyomino::Polyomino(const std::set<Position>& positions) {
|
|||||||
int maxX = INT_MIN;
|
int maxX = INT_MIN;
|
||||||
int minY = INT_MAX;
|
int minY = INT_MAX;
|
||||||
int maxY = INT_MIN;
|
int maxY = INT_MIN;
|
||||||
for (Position position : positions) {
|
for (const Position position : positions) {
|
||||||
if (position.x < minX) minX = position.x;
|
if (position.x < minX) minX = position.x;
|
||||||
if (position.x > maxX) maxX = position.x;
|
if (position.x > maxX) maxX = position.x;
|
||||||
if (position.y < minY) minY = position.y;
|
if (position.y < minY) minY = position.y;
|
||||||
@@ -40,13 +40,13 @@ Polyomino::Polyomino(const std::set<Position>& positions, int length) :
|
|||||||
void Polyomino::normalize() {
|
void Polyomino::normalize() {
|
||||||
int minX = INT_MAX;
|
int minX = INT_MAX;
|
||||||
int minY = INT_MAX;
|
int minY = INT_MAX;
|
||||||
for (Position position : this->positions) {
|
for (const Position position : this->positions) {
|
||||||
if (position.x < minX) minX = position.x;
|
if (position.x < minX) minX = position.x;
|
||||||
if (position.y < minY) minY = position.y;
|
if (position.y < minY) minY = position.y;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::set<Position> newPositions;
|
std::set<Position> newPositions;
|
||||||
for (Position position : this->positions) {
|
for (const Position position : this->positions) {
|
||||||
newPositions.insert(Position{position.x - minX, position.y - minY});
|
newPositions.insert(Position{position.x - minX, position.y - minY});
|
||||||
}
|
}
|
||||||
this->positions = std::move(newPositions);
|
this->positions = std::move(newPositions);
|
||||||
@@ -54,7 +54,7 @@ void Polyomino::normalize() {
|
|||||||
|
|
||||||
void Polyomino::rotateCW() {
|
void Polyomino::rotateCW() {
|
||||||
std::set<Position> newPositions;
|
std::set<Position> newPositions;
|
||||||
for (Position position : this->positions) {
|
for (const Position position : this->positions) {
|
||||||
newPositions.insert(Position{position.y, (length - 1) - (position.x)});
|
newPositions.insert(Position{position.y, (length - 1) - (position.x)});
|
||||||
}
|
}
|
||||||
this->positions = std::move(newPositions);
|
this->positions = std::move(newPositions);
|
||||||
@@ -62,7 +62,7 @@ void Polyomino::rotateCW() {
|
|||||||
|
|
||||||
void Polyomino::rotate180() {
|
void Polyomino::rotate180() {
|
||||||
std::set<Position> newPositions;
|
std::set<Position> newPositions;
|
||||||
for (Position position : this->positions) {
|
for (const Position position : this->positions) {
|
||||||
newPositions.insert(Position{(length - 1) - (position.x), (length - 1) - (position.y)});
|
newPositions.insert(Position{(length - 1) - (position.x), (length - 1) - (position.y)});
|
||||||
}
|
}
|
||||||
this->positions = std::move(newPositions);
|
this->positions = std::move(newPositions);
|
||||||
@@ -70,7 +70,7 @@ void Polyomino::rotate180() {
|
|||||||
|
|
||||||
void Polyomino::rotateCCW() {
|
void Polyomino::rotateCCW() {
|
||||||
std::set<Position> newPositions;
|
std::set<Position> newPositions;
|
||||||
for (Position position : this->positions) {
|
for (const Position position : this->positions) {
|
||||||
newPositions.insert(Position{(length - 1) - (position.y), position.x});
|
newPositions.insert(Position{(length - 1) - (position.y), position.x});
|
||||||
}
|
}
|
||||||
this->positions = std::move(newPositions);
|
this->positions = std::move(newPositions);
|
||||||
@@ -89,7 +89,7 @@ void Polyomino::goToSpawnPosition() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// calculates amount of squares per rows and columns
|
// calculates amount of squares per rows and columns
|
||||||
for (Position position : this->positions) {
|
for (const Position position : this->positions) {
|
||||||
linesCompleteness.at(0).at(position.y) += 1; // 0 = bottom to top = no rotation
|
linesCompleteness.at(0).at(position.y) += 1; // 0 = bottom to top = no rotation
|
||||||
linesCompleteness.at(1).at((length - 1) - position.x) += 1; // 1 = right to left = CW
|
linesCompleteness.at(1).at((length - 1) - position.x) += 1; // 1 = right to left = CW
|
||||||
linesCompleteness.at(2).at((length - 1) - position.y) += 1; // 2 = top to bottom = 180
|
linesCompleteness.at(2).at((length - 1) - position.y) += 1; // 2 = top to bottom = 180
|
||||||
@@ -158,14 +158,14 @@ void Polyomino::goToSpawnPosition() {
|
|||||||
|
|
||||||
int minX = INT_MAX;
|
int minX = INT_MAX;
|
||||||
int minY = INT_MAX;
|
int minY = INT_MAX;
|
||||||
for (Position position : this->positions) {
|
for (const Position position : this->positions) {
|
||||||
if (position.x < minX) minX = position.x;
|
if (position.x < minX) minX = position.x;
|
||||||
if (position.y < minY) minY = position.y;
|
if (position.y < minY) minY = position.y;
|
||||||
}
|
}
|
||||||
|
|
||||||
// center the piece with an up bias
|
// center the piece with an up bias
|
||||||
std::set<Position> newPositions;
|
std::set<Position> newPositions;
|
||||||
for (Position position : positions) {
|
for (const Position position : positions) {
|
||||||
newPositions.insert(Position{(position.x - minX) + (verticalEmptyLines / 2), (position.y - minY) + ((horizontalEmptyLines + 1) / 2)});
|
newPositions.insert(Position{(position.x - minX) + (verticalEmptyLines / 2), (position.y - minY) + ((horizontalEmptyLines + 1) / 2)});
|
||||||
}
|
}
|
||||||
this->positions = std::move(newPositions);
|
this->positions = std::move(newPositions);
|
||||||
|
|||||||
@@ -3,45 +3,61 @@
|
|||||||
#include "TextApp.h"
|
#include "TextApp.h"
|
||||||
|
|
||||||
#include <chrono>
|
#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 testGeneratorForOneSize(int size);
|
||||||
void testGeneratorByprintingAllNminos(int n);
|
void printPiecesByTypesForOneSize(int size);
|
||||||
void testStoringAndRetrievingPieces(int size);
|
void readStatsFromFilesForAllSizes(int max_size);
|
||||||
void generateFilesForAllSizes(int amount);
|
|
||||||
void generateFilesForOneSize(int size);
|
void benchmarking(int min_size, int max_size);
|
||||||
void loadFromFilesForOneSize(int size);
|
|
||||||
void readStatsFromFilesForAllSizes(int amount);
|
|
||||||
|
|
||||||
|
|
||||||
int main(int argc, char** argv) {
|
int main(int argc, char** argv) {
|
||||||
std::srand(std::time(NULL));
|
std::srand(std::time(NULL));
|
||||||
|
|
||||||
// dev: generate files if it hasn't been done before, UI will NOT generate the files
|
#ifdef BENCHMARK
|
||||||
//generateFilesForAllSizes(10);
|
#ifdef DEBUG
|
||||||
|
std::cout << "IMPORTANT: You are currently in debug mode, debug mode has lowest optimization settings and thus yields worse benchmarking results, to switch to release mode, type 'xmake f -m debug'." << std::endl;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
benchmarking(1, BENCHMARK_PIECES_SIZE);
|
||||||
|
#else
|
||||||
|
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;
|
TextApp UI;
|
||||||
UI.run();
|
UI.run();
|
||||||
|
#endif
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void testGeneratorForAllSizes(int amount) {
|
void testGeneratorForAllSizes(int max_size) {
|
||||||
using std::chrono::high_resolution_clock;
|
using std::chrono::high_resolution_clock;
|
||||||
using std::chrono::duration_cast;
|
using std::chrono::duration_cast;
|
||||||
using std::chrono::duration;
|
using std::chrono::duration;
|
||||||
using std::chrono::milliseconds;
|
using std::chrono::milliseconds;
|
||||||
Generator generator;
|
Generator generator;
|
||||||
|
|
||||||
for (int i = 1; i <= amount; i++) {
|
for (int i = 1; i <= max_size; i++) {
|
||||||
auto t1 = high_resolution_clock::now();
|
auto t1 = high_resolution_clock::now();
|
||||||
std::vector<Polyomino> n_minos = generator.generatePolyominoes(i);
|
std::vector<Polyomino> n_minos = generator.generatePolyominoes(i);
|
||||||
auto t2 = high_resolution_clock::now();
|
auto t2 = high_resolution_clock::now();
|
||||||
|
|
||||||
duration<double, std::milli> ms_double = t2 - t1;
|
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 +79,8 @@ void testGeneratorForOneSize(int size) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void testGeneratorByprintingAllNminos(int n) {
|
void printPiecesByTypesForOneSize(int size) {
|
||||||
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) {
|
|
||||||
PiecesFiles piecesFiles;
|
PiecesFiles piecesFiles;
|
||||||
piecesFiles.savePieces(size);
|
|
||||||
|
|
||||||
std::vector<Piece> pieces;
|
std::vector<Piece> pieces;
|
||||||
std::vector<int> convexPieces;
|
std::vector<int> convexPieces;
|
||||||
@@ -104,79 +104,9 @@ void testStoringAndRetrievingPieces(int size) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void generateFilesForAllSizes(int amount) {
|
void readStatsFromFilesForAllSizes(int max_size) {
|
||||||
using std::chrono::high_resolution_clock;
|
|
||||||
using std::chrono::duration_cast;
|
|
||||||
using std::chrono::duration;
|
|
||||||
using std::chrono::milliseconds;
|
|
||||||
PiecesFiles piecesFiles;
|
PiecesFiles piecesFiles;
|
||||||
|
for (int i = 1; i <= max_size; i++) {
|
||||||
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++) {
|
|
||||||
std::vector<Piece> pieces;
|
std::vector<Piece> pieces;
|
||||||
std::vector<int> convexPieces;
|
std::vector<int> convexPieces;
|
||||||
std::vector<int> holelessPieces;
|
std::vector<int> holelessPieces;
|
||||||
@@ -189,3 +119,66 @@ void readStatsFromFilesForAllSizes(int amount) {
|
|||||||
std::cout << "Others " << i << "-minos : " << otherPieces.size() << std::endl;
|
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_prstart_ttf[] = {
|
||||||
|
#include <data/fonts/pressstart/prstart.ttf.h>
|
||||||
|
};
|
||||||
|
|
||||||
|
static const unsigned char data_fonts_pressstart_prstartk_ttf[] = {
|
||||||
|
#include <data/fonts/pressstart/prstartk.ttf.h>
|
||||||
|
};
|
||||||
|
|
||||||
|
static const unsigned char data_images_keybinds_Rotate180_png[] = {
|
||||||
|
#include <data/images/keybinds/Rotate180.png.h>
|
||||||
|
};
|
||||||
|
|
||||||
|
static const unsigned char data_images_keybinds_Rotate0_png[] = {
|
||||||
|
#include <data/images/keybinds/Rotate0.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_Retry_png[] = {
|
||||||
|
#include <data/images/keybinds/Retry.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_Moveright_png[] = {
|
||||||
|
#include <data/images/keybinds/Moveright.png.h>
|
||||||
|
};
|
||||||
|
|
||||||
|
static const unsigned char data_images_keybinds_Harddrop_png[] = {
|
||||||
|
#include <data/images/keybinds/Harddrop.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_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_Pause_png[] = {
|
||||||
|
#include <data/images/keybinds/Pause.png.h>
|
||||||
|
};
|
||||||
|
|
||||||
|
static const Asset assets[] = {
|
||||||
|
{data_fonts_pressstart_prstart_ttf, sizeof(data_fonts_pressstart_prstart_ttf)},
|
||||||
|
{data_fonts_pressstart_prstartk_ttf, sizeof(data_fonts_pressstart_prstartk_ttf)},
|
||||||
|
{data_images_keybinds_Rotate180_png, sizeof(data_images_keybinds_Rotate180_png)},
|
||||||
|
{data_images_keybinds_Rotate0_png, sizeof(data_images_keybinds_Rotate0_png)},
|
||||||
|
{data_images_keybinds_RotateCCW_png, sizeof(data_images_keybinds_RotateCCW_png)},
|
||||||
|
{data_images_keybinds_Retry_png, sizeof(data_images_keybinds_Retry_png)},
|
||||||
|
{data_images_keybinds_RotateCW_png, sizeof(data_images_keybinds_RotateCW_png)},
|
||||||
|
{data_images_keybinds_Moveright_png, sizeof(data_images_keybinds_Moveright_png)},
|
||||||
|
{data_images_keybinds_Harddrop_png, sizeof(data_images_keybinds_Harddrop_png)},
|
||||||
|
{data_images_keybinds_Moveleft_png, sizeof(data_images_keybinds_Moveleft_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_Pause_png, sizeof(data_images_keybinds_Pause_png)},
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
static const std::map<std::string, AssetName> assetMap = {
|
||||||
|
{"data/fonts/pressstart/prstart.ttf", AssetName::data_fonts_pressstart_prstart_ttf},
|
||||||
|
{"data/fonts/pressstart/prstartk.ttf", AssetName::data_fonts_pressstart_prstartk_ttf},
|
||||||
|
{"data/images/keybinds/Rotate180.png", AssetName::data_images_keybinds_Rotate180_png},
|
||||||
|
{"data/images/keybinds/Rotate0.png", AssetName::data_images_keybinds_Rotate0_png},
|
||||||
|
{"data/images/keybinds/RotateCCW.png", AssetName::data_images_keybinds_RotateCCW_png},
|
||||||
|
{"data/images/keybinds/Retry.png", AssetName::data_images_keybinds_Retry_png},
|
||||||
|
{"data/images/keybinds/RotateCW.png", AssetName::data_images_keybinds_RotateCW_png},
|
||||||
|
{"data/images/keybinds/Moveright.png", AssetName::data_images_keybinds_Moveright_png},
|
||||||
|
{"data/images/keybinds/Harddrop.png", AssetName::data_images_keybinds_Harddrop_png},
|
||||||
|
{"data/images/keybinds/Moveleft.png", AssetName::data_images_keybinds_Moveleft_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/Pause.png", AssetName::data_images_keybinds_Pause_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_prstart_ttf,
|
||||||
|
data_fonts_pressstart_prstartk_ttf,
|
||||||
|
data_images_keybinds_Rotate180_png,
|
||||||
|
data_images_keybinds_Rotate0_png,
|
||||||
|
data_images_keybinds_RotateCCW_png,
|
||||||
|
data_images_keybinds_Retry_png,
|
||||||
|
data_images_keybinds_RotateCW_png,
|
||||||
|
data_images_keybinds_Moveright_png,
|
||||||
|
data_images_keybinds_Harddrop_png,
|
||||||
|
data_images_keybinds_Moveleft_png,
|
||||||
|
data_images_keybinds_Hold_png,
|
||||||
|
data_images_keybinds_Softdrop_png,
|
||||||
|
data_images_keybinds_Pause_png,
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
const Asset& getResource(AssetName fileName);
|
||||||
|
|
||||||
|
const Asset& getResource(const std::string& fileName);
|
||||||
33
xmake.lua
@@ -1,6 +1,8 @@
|
|||||||
add_rules("mode.debug", "mode.release")
|
add_rules("mode.debug", "mode.release")
|
||||||
|
|
||||||
add_requires("sfml 3.0.0")
|
includes("xmake/bin2c.lua")
|
||||||
|
|
||||||
|
add_requires("sfml 3.0.0", "zlib")
|
||||||
|
|
||||||
set_languages("c++20")
|
set_languages("c++20")
|
||||||
|
|
||||||
@@ -8,21 +10,44 @@ set_rundir(".")
|
|||||||
|
|
||||||
target("core")
|
target("core")
|
||||||
set_kind("$(kind)")
|
set_kind("$(kind)")
|
||||||
add_files("src/Pieces/*.cpp")
|
add_files("src/Pieces/*.cpp", "src/Core/*.cpp", "src/Common/*.cpp")
|
||||||
add_files("src/Core/*.cpp")
|
add_packages("zlib")
|
||||||
|
|
||||||
target("text")
|
target("text")
|
||||||
set_kind("binary")
|
|
||||||
set_default(false)
|
set_default(false)
|
||||||
|
set_kind("binary")
|
||||||
add_files("./src/TextUI/*.cpp")
|
add_files("./src/TextUI/*.cpp")
|
||||||
add_deps("core")
|
add_deps("core")
|
||||||
|
|
||||||
|
target("bmark")
|
||||||
|
set_default(false)
|
||||||
|
set_kind("binary")
|
||||||
|
add_files("./src/TextUI/*.cpp")
|
||||||
|
add_deps("core")
|
||||||
|
add_defines("BENCHMARK")
|
||||||
|
|
||||||
target("graph")
|
target("graph")
|
||||||
|
set_default(true)
|
||||||
|
add_rules("bin2c", {
|
||||||
|
extensions = {".png", ".ttf"},
|
||||||
|
outputSource = {"src/Utils/AssetManager.cpp"},
|
||||||
|
outputHeader = {"src/Utils/AssetManager.h"}
|
||||||
|
})
|
||||||
set_kind("binary")
|
set_kind("binary")
|
||||||
add_files("./src/GraphicalUI/**.cpp")
|
add_files("./src/GraphicalUI/**.cpp")
|
||||||
|
add_files("data/fonts/**.ttf", "data/images/**.png")
|
||||||
add_deps("core")
|
add_deps("core")
|
||||||
add_packages("sfml")
|
add_packages("sfml")
|
||||||
|
|
||||||
|
if is_mode("debug") then
|
||||||
|
add_defines("DEBUG")
|
||||||
|
end
|
||||||
|
|
||||||
|
if is_plat("mingw") then
|
||||||
|
add_ldflags("-static-libstdc++", "-static")
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
--
|
--
|
||||||
-- If you want to known more usage about xmake, please see https://xmake.io
|
-- 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)
|
||||||