diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..7626617 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,25 @@ +cmake_minimum_required(VERSION 3.21) +project(CandyCrush LANGUAGES CXX) + +set(CMAKE_CXX_STANDARD 20) + +set(BUILD_SHARED_LIBS OFF) +set(SFML_USE_STATIC_STD_LIBS ON) +set(SFML_BUILD_AUDIO ON) +set(SFML_BUILD_GRAPHICS ON) +set(SFML_BUILD_WINDOW OFF) +set(SFML_BUILD_NETWORK OFF) +set(SFML_USE_SYSTEM_DEPS OFF) +set(SFML_BUILD_EXAMPLES OFF) + +include(FetchContent) +FetchContent_Declare(SFML + GIT_REPOSITORY https://github.com/SFML/SFML.git + GIT_TAG 3.0.2 +) +FetchContent_MakeAvailable(SFML) + +add_executable(CandyCrush src/main.cpp) +target_link_libraries(CandyCrush PRIVATE sfml-graphics sfml-audio) + + diff --git a/fonts/Inter-Black.ttf b/fonts/Inter-Black.ttf new file mode 100644 index 0000000..5aecf7d Binary files /dev/null and b/fonts/Inter-Black.ttf differ diff --git a/fonts/Inter-Regular.ttf b/fonts/Inter-Regular.ttf new file mode 100644 index 0000000..8d4eebf Binary files /dev/null and b/fonts/Inter-Regular.ttf differ diff --git a/images/background.png b/images/background.png new file mode 100644 index 0000000..91178a0 Binary files /dev/null and b/images/background.png differ diff --git a/images/button_bg.png b/images/button_bg.png new file mode 100644 index 0000000..e7e1032 Binary files /dev/null and b/images/button_bg.png differ diff --git a/images/candys.png b/images/candys.png new file mode 100644 index 0000000..a7c87c9 Binary files /dev/null and b/images/candys.png differ diff --git a/music/background.ogg b/music/background.ogg new file mode 100644 index 0000000..f64fa01 Binary files /dev/null and b/music/background.ogg differ diff --git a/src/checkCombinations.hpp b/src/checkCombinations.hpp new file mode 100644 index 0000000..66be5ff --- /dev/null +++ b/src/checkCombinations.hpp @@ -0,0 +1,54 @@ +#pragma once +#include "structCandy.hpp" +#include "move.hpp" + +void check(candy map[][10]) +{ + for(int i = 1;i <= 8;i++) + for(int j = 1;j <= 8;j++) + { + if (map[i][j].kind == map[i+1][j].kind || map[i+1][j].kind > 4 || map[i][j].kind > 4) + if (map[i][j].kind == map[i-1][j].kind || map[i-1][j].kind > 4 || map[i][j].kind > 4) + if (map[i-1][j].kind == map[i+1][j].kind || map[i-1][j].kind > 4 || map[i+1][j].kind > 4) + for(int k = -1; k <= 1; k++) + { + if (map[i + k][j].kind == 6) for(int n = 0; n <= 8; n++) map[i + k][n].combination++; + if (map[i + k][j].kind == 5) for(int n = -1; n <= 1; n++) for(int m = -1; m <= 1; m++) if(i + k + n >= 0 && i + k + n <= 8 && j + m >= 0 && j + m <= 8) map[i + k + n][j + m].combination++; + map[i + k][j].combination++; + } + + if (map[i][j].kind == map[i][j+1].kind || map[i][j+1].kind > 4 || map[i][j].kind > 4) + if (map[i][j].kind == map[i][j-1].kind || map[i][j-1].kind > 4 || map[i][j].kind > 4) + if (map[i][j-1].kind == map[i][j+1].kind || map[i][j-1].kind > 4 || map[i][j+1].kind > 4) + for(int k = -1; k <= 1; k++) + { + if (map[i][j + k].kind == 6) for(int n = 0; n <= 8; n++) map[i][n].combination++; + if (map[i][j + k].kind == 5) for(int n = -1; n <= 1; n++) for(int m = -1; m <= 1; m++) if(i + n >= 0 && i + n <= 8 && j + k + m >= 0 && j + k + m <= 8) map[i + n][j + k + m].combination++; + map[i][j + k].combination++; + } + } +} + +void update(bool &isMoving, candy map[][10], int &points) +{ + if (!isMoving) + { + for(int i = 8;i > 0; i--) + for(int j = 1;j <= 8;j++) + if (map[i][j].combination) + for(int n = i;n > 0; n--) + if (!map[n][j].combination) {swap(map[n][j], map[i][j], map); break;}; + + for(int j = 1; j <= 8;j++) + for(int i = 8,n = 0; i > 0; i--) + if (map[i][j].combination) + { + points++; + map[i][j].kind = rand() % 5; + if (rand() % 100 < 5) map[i][j].kind = 5 + rand() % 2; + map[i][j].y = -tile_size * n++; + map[i][j].combination=0; + map[i][j].alpha = 255; + } + } +} \ No newline at end of file diff --git a/src/generationCandys.hpp b/src/generationCandys.hpp new file mode 100644 index 0000000..f5994f7 --- /dev/null +++ b/src/generationCandys.hpp @@ -0,0 +1,14 @@ +#pragma once +#include "structCandy.hpp" +void generation(candy map[][10]) +{ + for (int i = 1;i <= 8; i++) + for (int j = 1; j <= 8; j++) + { + map[i][j].kind = rand() % 5; + map[i][j].column = j; + map[i][j].row = i; + map[i][j].x = j * tile_size; + map[i][j].y = i * tile_size; + } +} diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..e078ab4 --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,198 @@ +#include +#include +#include +#include +#include +#include +#include +#include "mouseClickHandler.hpp" +#include "structCandy.hpp" +#include "generationCandys.hpp" +#include "checkCombinations.hpp" +#include "move.hpp" +#include + +candy map[10][10]; + +void playMusic() +{ + static sf::Music background_music; + if (!background_music.openFromFile("music/background.ogg")) + { + std::cerr << "Ошибка загрузки музыки" << std::endl; + } + background_music.setLooping(true); + background_music.setVolume(100); + background_music.play(); +} + +int main() +{ + using namespace sf; + + int seed; + srand(seed); + + + sf::RenderWindow window(sf::VideoMode({1280u, 720u}), "Candy Crush"); + window.setFramerateLimit(120); + playMusic(); + + Texture bg_texture, candys_texture, button_texture; + if (!bg_texture.loadFromFile("images/background.png")) return -1; + if (!candys_texture.loadFromFile("images/candys.png")) return -1; + if (!button_texture.loadFromFile("images/button_bg.png")) return -1; + + candys_texture.setSmooth(false); + + Sprite background(bg_texture), candys(candys_texture), button_bg(button_texture); + button_bg.setPosition({490, 150}); + + Font font_regular, font_black; + if (!font_regular.openFromFile("fonts/Inter-Regular.ttf")) return -1; + if (!font_black.openFromFile("fonts/Inter-Black.ttf")) return -1; + + Text points_text(font_regular, "0", 20); + points_text.setPosition({10, 5}); + Text rating_text(font_regular, "", 28); + rating_text.setPosition({500, 260}); + Text newgame_button_text(font_black, "NEW GAME", 48); + newgame_button_text.setPosition({500, 160}); + Text endgame_button_text(font_black, "END GAME", 24); + endgame_button_text.setPosition({1110, 655}); + + generation(map); + + int x0, y0, x, y; + int click = 0; + Vector2i position; + bool isSwap = false; + bool isMoving = false; + int points = 0; + int scene = 0; + + if (std::filesystem::exists("last_game.data")) + { + std::ifstream points_file("last_game.data"); + points_file >> points; + points_file.close(); + } + + std::vector rating; + for(int i = 0; i < 5; i++) rating.push_back(0); + if (std::filesystem::exists("rating.data")) + { + std::ifstream rating_file("rating.data"); + rating_file >> rating[0] >> rating[1] >> rating[2] >> rating[3] >> rating[4]; + rating_file.close(); + } + + String rating_str; + for(int i = 0; i < 5; i++) rating_str += std::to_string(i + 1) + " - " + std::to_string(rating[i]) + " points\n"; + rating_text.setString(rating_str); + + if (points > 0) { + newgame_button_text.setString("CONTINUE"); + newgame_button_text.setPosition({510, 160}); + } + + while (window.isOpen()) + { + while (const std::optional event = window.pollEvent()) + { + if (event->is()) + { + std::ofstream points_file("last_game.data"); + points_file << points; + points_file.close(); + + std::ofstream rating_file("rating.data"); + rating_file << rating[0] << " " << rating[1] << " " << rating[2] << " " << rating[3] << " " << rating[4]; + rating_file.close(); + + window.close(); + } + + if (const auto* mousePressed = event->getIf()) + { + if (mousePressed->button == Mouse::Button::Left) + { + if (scene == 0) + { + position = Mouse::getPosition(window); + if (position.x >= 490 && position.x <= 790 && position.y >= 150 && position.y <= 230) + { + scene = 1; + button_bg.setScale({0.5f, 0.5f}); + button_bg.setPosition({1100, 650}); + } + } + else + { + position = Mouse::getPosition(window); + if (position.x >= 1100 && position.x <= 1250 && position.y >= 650 && position.y <= 690) + { + if (points > rating[4]) rating[4] = points; + std::sort(rating.begin(), rating.end(), std::greater()); + + rating_str = ""; + for(int i = 0; i < 5; i++) rating_str += std::to_string(i + 1) + " - " + std::to_string(rating[i]) + " points\n"; + rating_text.setString(rating_str); + + scene = 0; + points = 0; + + newgame_button_text.setString("NEW GAME"); + newgame_button_text.setPosition({500, 160}); + + button_bg.setScale({1, 1}); + button_bg.setPosition({490, 150}); + } + if (!isSwap && !isMoving) click++; + position -= offset; + } + } + } + } + + if (scene == 0) + { + window.draw(background); + window.draw(button_bg); + window.draw(newgame_button_text); + window.draw(rating_text); + } + else if (scene == 1) + { + mouse_click(click, x0, y0, tile_size, position, isSwap, x, y, map); + check(map); + move(isMoving, map); + deleting(isMoving, map); + antiswap(isMoving, isSwap, map, y0, x0, y, x); + update(isMoving, map, points); + + window.draw(background); + + window.draw(button_bg); + window.draw(endgame_button_text); + + points_text.setString("Points: " + std::to_string(points)); + window.draw(points_text); + + for (int i = 1; i <= 8; i++) + for (int j = 1; j <= 8; j++) + { + candy element = map[i][j]; + candys.setTextureRect(IntRect({element.kind * 98*2, 0}, {98*2, 98*2})); + candys.setScale({70 / candys.getLocalBounds().size.y, 70 / candys.getLocalBounds().size.x}); + candys.setColor(Color(255, 255, 255, element.alpha)); + candys.setPosition({static_cast(element.x), static_cast(element.y)}); + candys.move({static_cast(offset.x - tile_size), static_cast(offset.y - tile_size)}); + window.draw(candys); + } + } + + window.display(); + } + return 0; +} diff --git a/src/mouseClickHandler.hpp b/src/mouseClickHandler.hpp new file mode 100644 index 0000000..3e20c91 --- /dev/null +++ b/src/mouseClickHandler.hpp @@ -0,0 +1,23 @@ +#pragma once +#include +#include +#include "structCandy.hpp" +#include "move.hpp" + +void mouse_click(int &click, int &x0, int &y0, int tile_size, sf::Vector2i pos, bool &isSwap, int &x, int &y, candy map[][10]){ + if (click == 1) + { + x0 = pos.x / tile_size + 1; + y0 = pos.y / tile_size + 1; + } + if (click == 2) + { + x = pos.x / tile_size + 1; + y = pos.y / tile_size + 1; + if (abs(x - x0)+ abs(y - y0) == 1) + { + swap(map[y0][x0], map[y][x], map); isSwap = 1; click = 0; + } + else click = 1; + } +} \ No newline at end of file diff --git a/src/move.hpp b/src/move.hpp new file mode 100644 index 0000000..300ba9f --- /dev/null +++ b/src/move.hpp @@ -0,0 +1,48 @@ +#pragma once +#include +#include "structCandy.hpp" + +void swap(candy first, candy second, candy map[][10]) +{ + std::swap(first.column,second.column); + std::swap(first.row,second.row); + + map[first.row][first.column] = first; + map[second.row][second.column] = second; +} + +void move(bool &isMoving, candy map[][10]) +{ + isMoving=false; + for (int i = 1;i <= 8;i++) + for (int j = 1;j <= 8;j++) + { + candy &element = map[i][j]; + int dx, dy; + for(int n = 0; n < 4; n++) + { + dx = element.x - element.column * tile_size; + dy = element.y - element.row * tile_size; + if (dx) element.x -= dx/abs(dx); + if (dy) element.y -= dy/abs(dy); + } + if (dx||dy) isMoving = 1; + } +} + +void deleting(bool &isMoving, candy map[][10]) +{ + if (!isMoving) + for (int i = 1; i <= 8; i++) + for (int j = 1;j <= 8; j++) + if (map[i][j].combination) if (map[i][j].alpha>10) {map[i][j].alpha -= 10; isMoving=true;} +} + +void antiswap(bool &isMoving, bool &isSwap, candy map[][10], int y0, int x0, int y, int x) +{ + int score = 0; + for (int i = 1;i <= 8;i++) + for (int j = 1;j <= 8;j++) + score += map[i][j].combination; + if (isSwap && !isMoving) {if (!score) swap(map[y0][x0], map[y][x], map); isSwap=0;} +} \ No newline at end of file diff --git a/src/structCandy.hpp b/src/structCandy.hpp new file mode 100644 index 0000000..de43f9c --- /dev/null +++ b/src/structCandy.hpp @@ -0,0 +1,19 @@ +#pragma once +#include + +constexpr int tile_size = 80; +sf::Vector2i offset(320, 40); + +struct candy +{ + int x, y; + int column, row; + int kind; + int combination; + int alpha; + candy() + { + combination = 0; + alpha = 255; + } +}; \ No newline at end of file