Initial Commit
This commit is contained in:
49
CMakeLists.txt
Normal file
49
CMakeLists.txt
Normal file
@@ -0,0 +1,49 @@
|
||||
cmake_minimum_required(VERSION 3.18..3.30)
|
||||
project(Retris
|
||||
VERSION 1.0
|
||||
LANGUAGES CXX)
|
||||
|
||||
if (PROJECT_SOURCE_DIR STREQUAL PROJECT_BINARY_DIR)
|
||||
message(FATAL_ERROR, "In-source builds are not allowed")
|
||||
endif()
|
||||
|
||||
set(CMAKE_CXX_STANDARD 20)
|
||||
|
||||
include(cmake/CPM.cmake)
|
||||
|
||||
CPMAddPackage(
|
||||
NAME mcolor
|
||||
URL https://git.redacted.cc/maxine/mcolor/archive/Prerelease-6.3.zip
|
||||
)
|
||||
|
||||
CPMAddPackage(
|
||||
NAME J3ML
|
||||
URL https://git.redacted.cc/josh/j3ml/archive/3.4.5.zip
|
||||
)
|
||||
|
||||
CPMAddPackage(
|
||||
NAME ReWindow
|
||||
URL https://git.redacted.cc/Redacted/ReWindow/archive/Prerelease-32.zip
|
||||
)
|
||||
|
||||
CPMAddPackage(
|
||||
NAME JGL
|
||||
URL https://git.redacted.cc/josh/JGL/archive/Prerelease-52.zip
|
||||
)
|
||||
|
||||
CPMAddPackage(
|
||||
NAME JUI
|
||||
URL https://git.redacted.cc/josh/ReJUI/archive/Prerelease-5.14.zip
|
||||
)
|
||||
|
||||
|
||||
|
||||
add_executable(Retris main.cpp)
|
||||
|
||||
target_include_directories(Retris PUBLIC
|
||||
${J3ML_SOURCE_DIR}/include
|
||||
${ReWindow_SOURCE_DIR}/include
|
||||
${JGL_SOURCE_DIR}/include
|
||||
${JUI_SOURCE_DIR}/include)
|
||||
|
||||
target_link_libraries(Retris PUBLIC J3ML ReWindow JGL JUI)
|
24
cmake/CPM.cmake
Normal file
24
cmake/CPM.cmake
Normal file
@@ -0,0 +1,24 @@
|
||||
# SPDX-License-Identifier: MIT
|
||||
#
|
||||
# SPDX-FileCopyrightText: Copyright (c) 2019-2023 Lars Melchior and contributors
|
||||
|
||||
set(CPM_DOWNLOAD_VERSION 0.38.7)
|
||||
set(CPM_HASH_SUM "83e5eb71b2bbb8b1f2ad38f1950287a057624e385c238f6087f94cdfc44af9c5")
|
||||
|
||||
if(CPM_SOURCE_CACHE)
|
||||
set(CPM_DOWNLOAD_LOCATION "${CPM_SOURCE_CACHE}/cpm/CPM_${CPM_DOWNLOAD_VERSION}.cmake")
|
||||
elseif(DEFINED ENV{CPM_SOURCE_CACHE})
|
||||
set(CPM_DOWNLOAD_LOCATION "$ENV{CPM_SOURCE_CACHE}/cpm/CPM_${CPM_DOWNLOAD_VERSION}.cmake")
|
||||
else()
|
||||
set(CPM_DOWNLOAD_LOCATION "${CMAKE_BINARY_DIR}/cmake/CPM_${CPM_DOWNLOAD_VERSION}.cmake")
|
||||
endif()
|
||||
|
||||
# Expand relative path. This is important if the provided path contains a tilde (~)
|
||||
get_filename_component(CPM_DOWNLOAD_LOCATION ${CPM_DOWNLOAD_LOCATION} ABSOLUTE)
|
||||
|
||||
file(DOWNLOAD
|
||||
https://github.com/cpm-cmake/CPM.cmake/releases/download/v${CPM_DOWNLOAD_VERSION}/CPM.cmake
|
||||
${CPM_DOWNLOAD_LOCATION} EXPECTED_HASH SHA256=${CPM_HASH_SUM}
|
||||
)
|
||||
|
||||
include(${CPM_DOWNLOAD_LOCATION})
|
421
main.cpp
Normal file
421
main.cpp
Normal file
@@ -0,0 +1,421 @@
|
||||
#include <iostream>
|
||||
#include <ReWindow/types/Window.h>
|
||||
#include "jlog/Logger.hpp"
|
||||
#include "ReWindow/Logger.h"
|
||||
#include "JGL/JGL.h"
|
||||
|
||||
using namespace ReWindow;
|
||||
|
||||
#define GL_MAJOR 2
|
||||
#define GL_MINOR 1
|
||||
|
||||
int fieldWidth = 24;
|
||||
int fieldHeight = 27;
|
||||
float scale = 32;
|
||||
|
||||
int window_width = fieldWidth * scale;
|
||||
int window_height = (fieldHeight * scale)+20;
|
||||
|
||||
Color4 tetcolors[10] = {
|
||||
Colors::Red,
|
||||
Colors::Purples::Magenta,
|
||||
Colors::Yellow,
|
||||
Colors::Green,
|
||||
Colors::Cyans::Cyan,
|
||||
Colors::Blue,
|
||||
Colors::Oranges::Gold,
|
||||
Colors::Reds::DarkRed,
|
||||
Colors::Gray,
|
||||
Colors::White,
|
||||
};
|
||||
|
||||
std::wstring tetramino[7];
|
||||
|
||||
void define_tetraminos()
|
||||
{
|
||||
tetramino[0].append(L" X ");
|
||||
tetramino[0].append(L" X ");
|
||||
tetramino[0].append(L" X ");
|
||||
tetramino[0].append(L" X ");
|
||||
|
||||
tetramino[1].append(L" X ");
|
||||
tetramino[1].append(L" XX ");
|
||||
tetramino[1].append(L" X ");
|
||||
tetramino[1].append(L" ");
|
||||
|
||||
tetramino[2].append(L" X ");
|
||||
tetramino[2].append(L" XX ");
|
||||
tetramino[2].append(L" X ");
|
||||
tetramino[2].append(L" ");
|
||||
|
||||
tetramino[3].append(L" ");
|
||||
tetramino[3].append(L" XX ");
|
||||
tetramino[3].append(L" XX ");
|
||||
tetramino[3].append(L" ");
|
||||
|
||||
tetramino[4].append(L" X ");
|
||||
tetramino[4].append(L" XX ");
|
||||
tetramino[4].append(L" X ");
|
||||
tetramino[4].append(L" ");
|
||||
|
||||
tetramino[5].append(L" ");
|
||||
tetramino[5].append(L" XX ");
|
||||
tetramino[5].append(L" X ");
|
||||
tetramino[5].append(L" X ");
|
||||
|
||||
tetramino[6].append(L" ");
|
||||
tetramino[6].append(L" XX ");
|
||||
tetramino[6].append(L" X ");
|
||||
tetramino[6].append(L" X ");
|
||||
}
|
||||
|
||||
|
||||
int rotate(int px, int py, int r)
|
||||
{
|
||||
switch(r % 4)
|
||||
{
|
||||
case 0 : return py * 4 + px; // 0 degrees
|
||||
case 1 : return 12 + py - (px*4); // 90 degrees
|
||||
case 2 : return 15 - (py * 4) - px; // 180 degrees
|
||||
case 3 : return 3 - py + (px * 4); // 270 degrees;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
unsigned char *pField = nullptr;
|
||||
|
||||
void setup_playing_field()
|
||||
{
|
||||
pField = new unsigned char [fieldWidth* fieldHeight];
|
||||
for (int x = 0; x < fieldWidth; x++) {
|
||||
for (int y = 0; y < fieldHeight; y++) {
|
||||
pField[y*fieldWidth + x] = (x == 0 || x == fieldWidth - 1 || y == fieldHeight -1) ? 9 : 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool does_piece_fit(int nTetramino, int rotation, int posX, int posY)
|
||||
{
|
||||
for (int px = 0; px < 4; px++) {
|
||||
for (int py = 0; py < 4; py++) {
|
||||
int pi = rotate(px, py, rotation);
|
||||
int fi = (posY + py) * fieldWidth + (posX + px);
|
||||
|
||||
if (posX + px >= 0 && posX + px < fieldWidth) {
|
||||
if (posY + py >= 0 && posY + py < fieldHeight) {
|
||||
if (tetramino[nTetramino][pi] == L'X' && pField[fi] != 0)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
class RetrisWindow : public OpenGLWindow {
|
||||
public:
|
||||
int score = 0;
|
||||
float gameTime = 0;
|
||||
int currentPiece = 0;
|
||||
int currentRotation = 0;
|
||||
int currentX = fieldWidth / 2;
|
||||
int currentY = 0;
|
||||
|
||||
float gamespeed = 1; // Increment to speed up gameticks.
|
||||
|
||||
bool gameOver = false;
|
||||
|
||||
std::vector<int> vLines;
|
||||
|
||||
|
||||
void initGL() {
|
||||
|
||||
if (!JGL::Init({ GetSize().x, GetSize().y}, 75, 100))
|
||||
Logger::Fatal("Initialization failed.");
|
||||
|
||||
|
||||
glClearColor(0.f, 0.f, 0.f, 0.f);
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
glDepthFunc(GL_LESS);
|
||||
glDepthMask(GL_TRUE);
|
||||
}
|
||||
|
||||
RetrisWindow() : OpenGLWindow("Retris - A Tetris Clone by redacted.cc", window_width, window_height, GL_MAJOR, GL_MINOR)
|
||||
{
|
||||
score = 0;
|
||||
ResetGameState();
|
||||
}
|
||||
|
||||
void ResetGameState()
|
||||
{
|
||||
score = 0;
|
||||
gameTime = 0;
|
||||
currentPiece = 0;
|
||||
currentRotation = 0;
|
||||
currentX = fieldWidth / 2;
|
||||
currentY = 0;
|
||||
gamespeed = 1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void DrawBoard()
|
||||
{
|
||||
for (int x = 0; x < fieldWidth; x++)
|
||||
{
|
||||
for (int y = 0; y < fieldHeight; y++)
|
||||
{
|
||||
auto cell = pField[y * fieldWidth + x];
|
||||
if (cell > 0)
|
||||
{
|
||||
Color4 color = tetcolors[cell];
|
||||
Color4 outline = color.Lerp(Colors::White, 0.5f);
|
||||
|
||||
JGL::J2D::FillChamferRect(outline, Vector2{x*scale, y*scale}+Vector2{8, 8}, {scale-16, scale-16}, 10);
|
||||
JGL::J2D::FillChamferRect(color, {x*scale, y*scale}, {scale, scale}, 5);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DrawTetramino(int pieceID, int curX, int curY, int rot)
|
||||
{
|
||||
for (int px = 0; px < 4; px++) {
|
||||
for (int py = 0; py < 4; py++) {
|
||||
if (tetramino[pieceID][rotate(px, py, rot)]==L'X') {
|
||||
Color4 color = tetcolors[pieceID+1];
|
||||
Color4 outline = color.Lerp(Colors::White, 0.5f);
|
||||
JGL::J2D::FillChamferRect(outline, Vector2((curX+px)*scale , (curY+py)*scale) + Vector2{8, 8}, {scale-16, scale-16}, 10);
|
||||
JGL::J2D::FillChamferRect(color, {(curX+px)*scale, (curY+py)*scale}, {scale, scale}, 5);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DrawCurrentPiece() {
|
||||
DrawTetramino(currentPiece, currentX, currentY, currentRotation);
|
||||
}
|
||||
|
||||
void DrawInfo() {
|
||||
if (gameOver)
|
||||
{
|
||||
J2D::DrawString(Colors::White, "You lose, fuckface!!", 0, 0, 1, 12);
|
||||
} else {
|
||||
J2D::DrawString(Colors::White, "Score:"+std::to_string(score), 0, 0, 1, 12);
|
||||
J2D::DrawString(Colors::White, "Next:", 0, 14, 1, 12);
|
||||
}
|
||||
}
|
||||
|
||||
void Input()
|
||||
{
|
||||
/*
|
||||
if (IsKeyDown(Keys::LeftArrow) &&
|
||||
does_piece_fit(currentPiece, currentRotation, currentX-1, currentY))
|
||||
currentX--;
|
||||
|
||||
if (IsKeyDown(Keys::RightArrow) &&
|
||||
does_piece_fit(currentPiece, currentRotation, currentX+1, currentY))
|
||||
currentX++;
|
||||
|
||||
if (IsKeyDown(Keys::DownArrow) &&
|
||||
does_piece_fit(currentPiece, currentRotation, currentX+1, currentY))
|
||||
currentX++;
|
||||
|
||||
if (IsKeyDown(Keys::RightControl) &&
|
||||
does_piece_fit(currentPiece, currentRotation, currentX+1, currentY))
|
||||
currentRotation++;
|
||||
|
||||
if (IsKeyDown(Keys::RightShift) &&
|
||||
does_piece_fit(currentPiece, currentRotation, currentX+1, currentY))
|
||||
currentRotation--;
|
||||
*/
|
||||
}
|
||||
|
||||
|
||||
void OnKeyDown(const ReWindow::KeyDownEvent &ev) override
|
||||
{
|
||||
auto key = ev.key;
|
||||
|
||||
if (key == Keys::LeftArrow &&
|
||||
does_piece_fit(currentPiece, currentRotation, currentX-1, currentY))
|
||||
currentX--;
|
||||
|
||||
if (key == Keys::RightArrow &&
|
||||
does_piece_fit(currentPiece, currentRotation, currentX+1, currentY))
|
||||
currentX++;
|
||||
|
||||
if (key == Keys::DownArrow &&
|
||||
does_piece_fit(currentPiece, currentRotation, currentX, currentY+1))
|
||||
currentY++;
|
||||
|
||||
if (key == Keys::UpArrow &&
|
||||
does_piece_fit(currentPiece, currentRotation+1, currentX, currentY))
|
||||
currentRotation++;
|
||||
|
||||
/*if (key == Keys::RightShift &&
|
||||
does_piece_fit(currentPiece, currentRotation-1, currentX, currentY))
|
||||
currentRotation--;
|
||||
*/
|
||||
|
||||
if (key == Keys::Escape)
|
||||
ResetGameState();
|
||||
}
|
||||
|
||||
|
||||
void Render()
|
||||
{
|
||||
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
glLoadIdentity();
|
||||
|
||||
|
||||
JGL::J2D::Begin();
|
||||
DrawBoard();
|
||||
DrawCurrentPiece();
|
||||
DrawInfo();
|
||||
JGL::J2D::End();
|
||||
}
|
||||
|
||||
// Checks for instances of "Tetris" (a full line) on the board
|
||||
void CheckForTetris()
|
||||
{
|
||||
// Check for horiz lines
|
||||
for (int py = 0; py < 4; py++)
|
||||
if (currentY + py < fieldHeight - 1)
|
||||
{
|
||||
bool bLine = true;
|
||||
for (int px = 1; px < fieldWidth-1; px++)
|
||||
{
|
||||
bLine &= (pField[(currentY+py)*fieldWidth+px]) != 0;
|
||||
}
|
||||
|
||||
if (bLine)
|
||||
{
|
||||
for (int px = 1; px < fieldWidth-1; px++)
|
||||
{
|
||||
pField[(currentY+py) * fieldWidth + px] = 8;
|
||||
|
||||
}
|
||||
|
||||
vLines.push_back(currentY+py);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ClearLines()
|
||||
{
|
||||
int prescore = 0;
|
||||
for (auto &v: vLines)
|
||||
{
|
||||
prescore++;
|
||||
for (int px = 1; px < fieldWidth-1; px++)
|
||||
{
|
||||
for (int py = v; py > 0; py--)
|
||||
{
|
||||
pField[py*fieldWidth+px] = pField[(py-1)*fieldWidth+px];
|
||||
|
||||
}
|
||||
pField[px] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
score += 50*prescore;
|
||||
vLines.clear();
|
||||
|
||||
}
|
||||
|
||||
void NextPiece()
|
||||
{
|
||||
// Choose Next Piece
|
||||
currentX = fieldWidth/2;
|
||||
currentY = 0;
|
||||
currentRotation = 0;
|
||||
currentPiece = rand() % 7;
|
||||
}
|
||||
void DropCurrentPiece() {
|
||||
// lock current piece into the field
|
||||
for (int px = 0; px < 4; px++)
|
||||
{
|
||||
for (int py = 0; py < 4; py++)
|
||||
{
|
||||
if (tetramino[currentPiece][rotate(px, py, currentRotation)]==L'X')
|
||||
pField[(currentY+py)*fieldWidth + (currentX+px)] = currentPiece+1;
|
||||
}
|
||||
}
|
||||
}
|
||||
void GameTick()
|
||||
{
|
||||
|
||||
// if piece does not fit
|
||||
gameOver = !does_piece_fit(currentPiece, currentRotation, currentX, currentY);
|
||||
if (gameOver)
|
||||
{
|
||||
//ResetGameState();
|
||||
return;
|
||||
}
|
||||
|
||||
// Can the piece move?
|
||||
if (does_piece_fit(currentPiece, currentRotation, currentX, currentY+1)) {
|
||||
currentY++;
|
||||
} else {
|
||||
DropCurrentPiece();
|
||||
CheckForTetris();
|
||||
NextPiece();
|
||||
gamespeed++;
|
||||
|
||||
if (!vLines.empty())
|
||||
{
|
||||
//DrawBoard(); // ASS HACK
|
||||
//using namespace std::chrono_literals;
|
||||
//std::this_thread::sleep_for(400ms);
|
||||
ClearLines();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void OnRefresh(float elapsed) override
|
||||
{
|
||||
OpenGLWindow::SwapBuffers();
|
||||
|
||||
gameTime += elapsed;
|
||||
|
||||
if (gameTime > 1.f / gamespeed) {
|
||||
gameTime = 0;
|
||||
GameTick();
|
||||
}
|
||||
|
||||
Input();
|
||||
Render();
|
||||
OpenGLWindow::OnRefresh(elapsed);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
int main() {
|
||||
/// Suppress logging from RWindow library.
|
||||
ReWindow::Logger::Debug.EnableConsole(false);
|
||||
|
||||
|
||||
define_tetraminos();
|
||||
setup_playing_field();
|
||||
|
||||
auto* window = new RetrisWindow();
|
||||
|
||||
if (!window->Open())
|
||||
std::cerr << "Unable to open Retris Window" << std::endl;
|
||||
|
||||
window->initGL();
|
||||
|
||||
while (!window->IsClosing())
|
||||
window->ManagedRefresh();
|
||||
|
||||
delete window;
|
||||
return 0;
|
||||
}
|
Reference in New Issue
Block a user