pssp/src/main.cpp

568 lines
15 KiB
C++

#include<iostream>
#include<vector>
#include<filesystem>
#include<algorithm>
#include<random>
#include<chrono>
#include<SDL.h>
#include"PSX_Texture.h"
#include"PSX_Button.h"
void init();
void close();
const int SCREEN_WIDTH = 640;
const int SCREEN_HEIGHT = 480;
SDL_Window* gWindow = NULL;
SDL_Renderer* gRenderer = NULL;
bool quit;
struct pos2d{
int x;
int y;
};
pos2d mousePos;
void mainMenu();
bool game(PSX_Texture* puzzle, int numPieces, bool directMode, bool fCompleted);
bool isHovering(SDL_Rect* rect);
void init(){
SDL_Init(SDL_INIT_VIDEO);
gWindow = SDL_CreateWindow("pssp",SDL_WINDOWPOS_CENTERED,SDL_WINDOWPOS_CENTERED,SCREEN_WIDTH,SCREEN_HEIGHT,SDL_WINDOW_SHOWN);
gRenderer = SDL_CreateRenderer(gWindow,-1,SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);
SDL_SetRenderDrawColor(gRenderer,0xFF,0xFF,0xFF,0xFF);
quit = false;
}
void close(){
SDL_DestroyRenderer(gRenderer);
SDL_DestroyWindow(gWindow);
gWindow = NULL;
gRenderer = NULL;
SDL_Quit();
}
namespace fs = std::filesystem;
//int main(int argc, char* argv[]){
int main(){
init();
// Defaults
int numPieces = 3;
bool directMode = false;
/* Loading screen */
PSX_Texture loading(gRenderer);
loading.loadText("loading...", SDL_Color{0xFF,0xFF,0xFF,0xFF},"assets/font.otf",40);
SDL_Rect rLoading = {
SCREEN_WIDTH/2 - loading.getW()/2,
SCREEN_HEIGHT/2 - loading.getH()/2,
loading.getW(),
loading.getH()
};
SDL_SetRenderDrawColor(gRenderer,0,0,0,0xFF);
SDL_RenderClear(gRenderer);
loading.render(&rLoading);
SDL_RenderPresent(gRenderer);
/* Title */
PSX_Texture title(gRenderer);
title.loadText(
"Posweg's Simple SDL2 Puzzle",
SDL_Color{0xFF,0xFF,0xFF,0xFF},
"assets/font.otf",
25
);
SDL_Rect rTitle = {SCREEN_WIDTH/2 - title.getW()/2, 15, title.getW(), title.getH()};
/* Side panel */
int sSidePanelW = 200;
int sidePanelMargin = 10;
SDL_Rect rSidePanel = {
SCREEN_WIDTH-sSidePanelW,
rTitle.y*2+rTitle.h,
sSidePanelW - sidePanelMargin,
SCREEN_HEIGHT- (rTitle.y*2+rTitle.h) - sidePanelMargin,
};
// Number of pieces
PSX_Texture numText(gRenderer);
numText.loadText("Number of pieces:",SDL_Color{0xFF,0xFF,0xFF,0xFF},"assets/font.otf",20);
SDL_Rect rNumText = {rSidePanel.x,rSidePanel.y,numText.getW(),numText.getH()};
SDL_Rect rNum1 = {rNumText.x,rNumText.y + rNumText.h + sidePanelMargin, 30,30};
PSX_Button num1(gRenderer, &rNum1, "2");
SDL_Rect rNum2 = {rNumText.x + rNum1.w + 10, rNumText.y + rNumText.h + sidePanelMargin, 30,30};
PSX_Button num2(gRenderer, &rNum2, "3");
SDL_Rect rNum3 = {rNumText.x + rNum2.w*2 + 10*2, rNumText.y + rNumText.h + sidePanelMargin, 30,30};
PSX_Button num3(gRenderer, &rNum3, "4");
// Mode of play
PSX_Texture modeText(gRenderer);
modeText.loadText("Mode:",SDL_Color{0xFF,0xFF,0xFF,0xFF},"assets/font.otf",20);
SDL_Rect rModeText = {
rSidePanel.x,
rNum1.y + rNum1.h + sidePanelMargin,
modeText.getW(),
modeText.getH()
};
// Quit button
SDL_Rect rQuitButton = {rSidePanel.x,rSidePanel.y + rSidePanel.h - 50, rSidePanel.w, 50};
PSX_Button quitButton(gRenderer, &rQuitButton, "Quit");
/* Thumbs */
// Thumb layout composition
int thumbW = 50;
int thumbH = thumbW;
int margin = 10;
int outerMargin = 10;
int topMargin = rTitle.y*2 + rTitle.h;
int containerW = SCREEN_WIDTH - 200;
int containerH = SCREEN_HEIGHT - topMargin - topMargin;
int actualPage = 0;
int thumbAmountW = 1 + (containerW-outerMargin*2 - thumbW) / (thumbW + margin);
int thumbAmountH = 1 + (containerH - thumbH) / (thumbH + margin);
int leftMargin = (containerW-thumbAmountW*(thumbW + margin) + margin)/2;
// Individual thumb creation
std::vector<PSX_Texture> thumbs;
thumbs.reserve(1000);
// Without reservation the textures
// glitch out very ugly
PSX_Texture thumbName(gRenderer);
std::string finalMessage;
SDL_Rect rThumbsName;
// SDL_Texture creation of every JPG image in the folder specified
int k = 0;
for(const auto & entry : fs::directory_iterator("images")){
if(entry.path().extension() == ".jpg"){
SDL_SetRenderDrawColor(gRenderer,0,0,0,0xFF);
SDL_RenderClear(gRenderer);
loading.render(&rLoading);
thumbs.push_back(PSX_Texture(gRenderer));
thumbs[k].loadTexture(entry.path().string());
finalMessage = "";
finalMessage.append("Importing texture: ");
finalMessage.append(entry.path());
thumbName.loadText(finalMessage,SDL_Color{127,127,127,0xFF},"assets/font.otf",15);
rThumbsName = {
SCREEN_WIDTH/2 - thumbName.getW()/2,
rLoading.y + rLoading.h + 10,
thumbName.getW(),
thumbName.getH()
};
thumbName.render(&rThumbsName);
SDL_RenderPresent(gRenderer);
k++;
}
}
// Page changer and indicator
std::vector<PSX_Texture> pageIndicator;
pageIndicator.reserve(static_cast<int>(thumbs.size())/thumbAmountW/thumbAmountH + 1);
std::vector<SDL_Rect> rPageIndicator;
rPageIndicator.resize(static_cast<int>(thumbs.size())/thumbAmountW/thumbAmountH + 1);
for(int i = 0; i < static_cast<int>(thumbs.size())/thumbAmountW/thumbAmountH + 1; i++){
pageIndicator.push_back(PSX_Texture(gRenderer));
pageIndicator[i].loadText(
std::to_string(i+1)
+ " / " +
std::to_string(static_cast<int>(thumbs.size())/thumbAmountW/thumbAmountH + 1),
SDL_Color{0xFF,0xFF,0xFF,0xFF},
"assets/font.otf",20
);
rPageIndicator[i].x = containerW/2 - pageIndicator[i].getW()/2;
rPageIndicator[i].y = SCREEN_HEIGHT - pageIndicator[i].getH()/2 - topMargin/2;
rPageIndicator[i].w = pageIndicator[i].getW();
rPageIndicator[i].h = pageIndicator[i].getH();
}
SDL_Rect rPreviousButton = {
rPageIndicator[0].x - 150 - 20,
rPageIndicator[0].y - 5,
150,
rPageIndicator[0].h + 10
};
PSX_Button previousButton(gRenderer, &rPreviousButton, "Previous page");
SDL_Rect rNextButton = {
rPageIndicator[0].x + rPageIndicator[0].w + 20,
rPreviousButton.y,
rPreviousButton.w,
rPreviousButton.h
};
PSX_Button nextButton(gRenderer, &rNextButton, "Next page");
// To track completed images
std::vector<bool> fCompletedThumbs;
fCompletedThumbs.resize(thumbs.size());
for(int i = 0; i < static_cast<int>(thumbs.size()); i++){
fCompletedThumbs[i] = false;
}
/* Rendering */
SDL_Event e;
while(!quit){
bool click = false;
while(SDL_PollEvent(&e)!=0){
if(e.type == SDL_QUIT){
quit = true;
}
if(e.type == SDL_MOUSEBUTTONDOWN){
click = true;
}
}
SDL_SetRenderDrawBlendMode(gRenderer, SDL_BLENDMODE_BLEND);
// The white rect won't let the image
// behind be seen without setting the blendmode
SDL_SetRenderDrawColor(gRenderer,0,0,0,0xFF);
SDL_RenderClear(gRenderer);
SDL_GetMouseState(&mousePos.x, &mousePos.y);
title.render(&rTitle);
// Side panel
numText.render(&rNumText);
num1.render(isHovering(&rNum1));
num2.render(isHovering(&rNum2));
num3.render(isHovering(&rNum3));
if(isHovering(&rQuitButton)){
quitButton.render(true);
if(click){
quit = true;
}
} else quitButton.render(false);
if(numPieces == 2) num1.render(3);
else if(isHovering(&rNum1)){
num1.render(true);
if(click) numPieces = 2;
} else num1.render(false);
if(numPieces == 3) num2.render(3);
else if(isHovering(&rNum2) or numPieces == 3){
num2.render(true);
if(click) numPieces = 3;
} else num2.render(false);
if(numPieces == 4) num3.render(3);
else if(isHovering(&rNum3) or numPieces == 4){
num3.render(true);
if(click) numPieces = 4;
} else num3.render(false);
//modeText.render(&rModeText);
if(actualPage <= 0){
previousButton.render(3);
}
else if(isHovering(&rPreviousButton)){
previousButton.render(true);
if(click) actualPage--;
} else previousButton.render(false);
if(actualPage >= static_cast<int>(thumbs.size())/thumbAmountW/thumbAmountH){
nextButton.render(3);
} else
if(isHovering(&rNextButton)){
nextButton.render(true);
if(click)
actualPage++;
} else nextButton.render(false);
pageIndicator[actualPage].render(&rPageIndicator[actualPage]);
// Thumbs loop
for(int i = 0; i < k; i++){
if(i/thumbAmountW/thumbAmountH == actualPage){
// Position every thumb
SDL_Rect tempRect = {
leftMargin + (thumbW + margin) * (i - (i/thumbAmountW)*thumbAmountW),
topMargin + (thumbH + margin) * (
i/thumbAmountW - (i/thumbAmountW/thumbAmountH)*thumbAmountH),
thumbW,
thumbH
};
thumbs[i].render(&tempRect);
if(fCompletedThumbs[i] == false){
SDL_SetRenderDrawColor(gRenderer,0,0,0,200);
SDL_RenderFillRect(gRenderer, &tempRect);
}
if(isHovering(&tempRect)){
SDL_SetRenderDrawColor(gRenderer, 0xFF,0xFF,0xFF,127);
SDL_RenderFillRect(gRenderer, &tempRect);
if(click){
click = false;
fCompletedThumbs[i] = game(
&thumbs[i],
numPieces,
directMode,
fCompletedThumbs[i]
);
}
}
}
}
SDL_RenderPresent(gRenderer);
}
close();
return 0;
}
bool game(PSX_Texture* puzzle, int numPieces, bool directMode, bool fCompleted){
int puzzleDivX = numPieces; // Number of horizontal blocks
int puzzleDivY = puzzleDivX; // Number of vertical block
int space = 0; // Space between blocks
SDL_Rect puzzleRect;
puzzleRect.w = 360;
puzzleRect.h = puzzleRect.w;
puzzleRect.y = SCREEN_HEIGHT/2 - puzzleRect.h/2;
puzzleRect.x = SCREEN_WIDTH/2 - puzzleRect.w/2;
SDL_Rect puzzleSubRect = {0,0,0,0};
SDL_Rect puzzleSubFrame = {0,0,0,0};
pos2d puzzleGridBubble = {0,0};
pos2d puzzleGridHole = {0,0};
std::vector<std::vector<pos2d>> puzzleGrid;
// Random number list generation
std::vector<int> gridList;
gridList.resize(puzzleDivX*puzzleDivY);
for(int i = 0; i < puzzleDivX*puzzleDivY; i++){
gridList[i] = i;
}
std::random_device rd;
std::mt19937 g(rd());
std::shuffle(gridList.begin(), gridList.end(), g);
// The list is generated in one dimension to
// assure there won't be repeatings and make
// the alorith easier. Later it's going to be
// transformed back again to a 2D Matrix.
// Assing the random number list to the grid
puzzleGrid.resize(puzzleDivX);
for(int i = 0; i < puzzleDivX; i++){
puzzleGrid[i].resize(puzzleDivY);
for(int j = 0; j < puzzleDivY; j++){
// Convert from list of integers to 2D grid and assign
puzzleGrid[i][j].x = gridList[i*puzzleDivX+j]/puzzleDivX;
puzzleGrid[i][j].y = gridList[i*puzzleDivX+j] - puzzleGrid[i][j].x * puzzleDivX;
// The [i*PuzzleDivX+j] is to keep track of the list
// index number, since we're in nested for loops
// and not a plain one. 2D matrix -> 1D list
// Inverse order mode
/*puzzleGrid[i][j].x = puzzleDivX - (i + 1);
puzzleGrid[i][j].y = puzzleDivY - (j + 1);*/
// Detect where the hole is in the random grid
if(
puzzleGrid[i][j].x == puzzleDivY-1 and
puzzleGrid[i][j].y == puzzleDivX-1
){
puzzleGridHole = {i,j};
}
}
}
/* Bottom buttons */
// Return button
SDL_Rect rBackButton;
rBackButton.w = 200;
rBackButton.h = 50;
rBackButton.y = SCREEN_HEIGHT - rBackButton.h - (SCREEN_HEIGHT - puzzleRect.h - rBackButton.h) / 3;
rBackButton.x =
SCREEN_WIDTH - rBackButton.w - (SCREEN_WIDTH/2 - (
rBackButton.w*2 + (SCREEN_HEIGHT-(rBackButton.y+rBackButton.h))) /2
);
PSX_Button backButton(gRenderer, &rBackButton, "Back");
// Shuffle again button
SDL_Rect rShuffleButton = {
SCREEN_WIDTH - rBackButton.x - rBackButton.w,
rBackButton.y,
rBackButton.w,
rBackButton.h
};
PSX_Button shuffleButton(gRenderer, &rShuffleButton, "Delete");
SDL_Event e;
while(!quit){
bool click = false;
while(SDL_PollEvent(&e)!=0){
if(e.type == SDL_QUIT){
quit = true;
}
if(e.type == SDL_MOUSEBUTTONDOWN){
click = true;
}
}
SDL_SetRenderDrawBlendMode(gRenderer, SDL_BLENDMODE_BLEND);
SDL_SetRenderDrawColor(gRenderer,0,0,0,0xFF);
SDL_RenderClear(gRenderer);
SDL_GetMouseState(&mousePos.x, &mousePos.y);
if(!fCompleted){
fCompleted = true;
for(int i = 0; i < puzzleDivX; i++){
for(int j = 0; j < puzzleDivY; j++){
// Position the pieces
puzzleSubRect = {
puzzleRect.x + i * puzzleRect.w / puzzleDivX + space * i,
puzzleRect.y + j * puzzleRect.h / puzzleDivY + space * j,
puzzleRect.w / puzzleDivX,
puzzleRect.h / puzzleDivY,
};
// Set the section of the image to show in the actual piece
puzzleSubFrame = {
(puzzleGrid[i][j].x * puzzle->getW()) / puzzleDivX,
(puzzleGrid[i][j].y * puzzle->getH()) / puzzleDivY,
puzzle->getW() / puzzleDivX,
puzzle->getH() / puzzleDivY,
};
// Piece interaction, ignore the hole piece
if(puzzleGrid[i][j].x != puzzleDivY-1 or puzzleGrid[i][j].y != puzzleDivX-1){
puzzle->render(&puzzleSubRect, &puzzleSubFrame);
if(isHovering(&puzzleSubRect)){
// Hover effect
SDL_SetRenderDrawColor(gRenderer, 0xFF,0xFF,0xFF,127);
SDL_RenderFillRect(gRenderer, &puzzleSubRect);
if(
// Direct motion mode
click and(
!directMode or
(puzzleGridHole.x+1 == i and
puzzleGridHole.y == j)
or
(puzzleGridHole.x-1 == i and
puzzleGridHole.y == j)
or
(puzzleGridHole.x == i and
puzzleGridHole.y-1 == j)
or
(puzzleGridHole.x == i and
puzzleGridHole.y+1 == j)
)
){
// Bubble swap the selected piece with the hole
puzzleGridBubble = puzzleGrid
[puzzleGridHole.x]
[puzzleGridHole.y];
puzzleGrid
[puzzleGridHole.x]
[puzzleGridHole.y] = puzzleGrid[i][j];
puzzleGrid[i][j] = puzzleGridBubble;
// Update where the hole is
puzzleGridHole = {i,j};
}
}
}
// Check if piece is in the correct spot. If not, set
// the completed flag to false
if(i != puzzleGrid[i][j].x or j != puzzleGrid[i][j].y) fCompleted = false;
}
}
}
if(fCompleted){
puzzleRect.y = SCREEN_HEIGHT - (rBackButton.y + rBackButton.h);
if(isHovering(&rBackButton)){
backButton.render(true);
if(click){
click = false;
return true;
}
} else backButton.render(false);
if(isHovering(&rShuffleButton)){
shuffleButton.render(true);
if(click){
click = false;
return false;
}
} else shuffleButton.render(false);
puzzle->render(&puzzleRect);
}
SDL_RenderPresent(gRenderer);
}
return false;
}
bool isHovering(SDL_Rect* rect){
if(
mousePos.x > rect->x and
mousePos.x < rect->x + rect->w and
mousePos.y > rect->y and
mousePos.y < rect->y + rect->h
){
return true;
}
else{
return false;
}
}