Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions lab1/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
cmake_minimum_required(VERSION 3.15)
project(gems)

set(CMAKE_CXX_STANDARD 17)

include(FetchContent)
FetchContent_Declare(
SDL3
GIT_REPOSITORY "https://github.com/libsdl-org/SDL.git"
GIT_TAG "main"
EXCLUDE_FROM_ALL
)
FetchContent_MakeAvailable(SDL3)

add_executable(gems
src/main.cpp
src/Game.cpp
src/Gem.cpp
src/Bonus.cpp
)

target_include_directories(gems PRIVATE include)
target_link_libraries(gems PRIVATE SDL3::SDL3)
20 changes: 19 additions & 1 deletion lab1/README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,20 @@
CPP laboratories
sheesh
sheesh

# Gems

## How to play:
Use your mouse to change neighbour gems, LMB to select, other buttons to remove selection

In total game has six colors:
1. Wheat
2. Maroon
3. Orange
4. Olive
5. Light pink
6. Slate gray

Bonuses can spawn in reward after gem elimination in radius of 3 gems
Bonuses are rendered as squares inside a gem:
1. Recolor (blue) - changes colors randomly of gem where the bonus is and his 2 random non-neighboring gems to color of gem with bonus.
2. Bomb (red) - eliminates 5 random gems in the field including source gem
23 changes: 23 additions & 0 deletions lab1/include/Bonus.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#pragma once
#include "GameObject.hpp"
#include <memory>

enum class BonusType { RECOLOR, BOMB };

class Bonus : public GameObject {
public:
virtual void Activate(class Game* game, size_t index) = 0;
virtual ~Bonus() = default;
};

class RecolorBonus : public Bonus {
public:
void Draw(SDL_Renderer* renderer, const SDL_FRect& rect) const override;
void Activate(class Game* game, size_t index) override;
};

class BombBonus : public Bonus {
public:
void Draw(SDL_Renderer* renderer, const SDL_FRect& rect) const override;
void Activate(class Game* game, size_t index) override;
};
12 changes: 12 additions & 0 deletions lab1/include/Colors.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#pragma once
#include "SDL3/SDL_pixels.h"

constexpr SDL_Color WHEAT{245, 222, 179, 255};
constexpr SDL_Color MAROON{128, 0, 0, 255};
constexpr SDL_Color ORANGE{255, 140, 0, 255};
constexpr SDL_Color OLIVE{128, 128, 0, 255};
constexpr SDL_Color LIGHT_PINK{255, 182, 193, 255};
constexpr SDL_Color SLATE_GRAY{112, 128, 144, 255};

constexpr SDL_Color PALETTE[] = {WHEAT, MAROON, ORANGE, OLIVE, LIGHT_PINK, SLATE_GRAY};
constexpr int PALETTE_SIZE = sizeof(PALETTE) / sizeof(SDL_Color);
45 changes: 45 additions & 0 deletions lab1/include/Game.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
#pragma once
#include "SDL3/SDL.h"
#include <vector>
#include <memory>
#include "Colors.hpp"
#include "GameObject.hpp"

constexpr int SCREEN_WIDTH = 800;
constexpr int SCREEN_HEIGHT = 600;
constexpr int GEM_WIDTH = 40;
constexpr int GEM_HEIGHT = 40;
constexpr int TOTAL_GEMS = SCREEN_WIDTH * SCREEN_HEIGHT / GEM_WIDTH / GEM_HEIGHT;
constexpr int LINE_LENGTH = SCREEN_WIDTH / GEM_WIDTH;
constexpr int ROWS = SCREEN_HEIGHT / GEM_HEIGHT;

class Game {
public:
Game();
bool Initialize();
void Run();
void ProcessInput();
void Update();
void Render() const;

void HandleClick(const SDL_MouseButtonEvent& e);
bool CheckTriplets();
void ActivateBonuses();
void SpawnBonus(size_t destroyed);

// Для доступа бонусов
std::vector<std::shared_ptr<GameObject>>& GetField() { return field; }
friend class BombBonus;
friend class RecolorBonus;

private:
bool shouldExit = false;
int selectedGem = -1;
SDL_Window* window = nullptr;
SDL_Renderer* renderer = nullptr;

std::vector<std::shared_ptr<GameObject>> field;

void InitField();
bool canPlace(size_t i, SDL_Color src) const;
};
10 changes: 10 additions & 0 deletions lab1/include/GameObject.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#pragma once
#include "SDL3/SDL_rect.h"
#include "SDL3/SDL_render.h"

class GameObject {
public:
virtual ~GameObject() = default;
virtual void Draw(SDL_Renderer* renderer, const SDL_FRect& rect) const = 0;
virtual void Update() {}
};
23 changes: 23 additions & 0 deletions lab1/include/Gem.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#pragma once
#include "GameObject.hpp"
#include "SDL3/SDL_pixels.h"
#include <memory>

class Bonus;

class Gem : public GameObject {
public:
explicit Gem(SDL_Color c);
Gem() = default;

void Draw(SDL_Renderer* renderer, const SDL_FRect& rect) const override;

void SetBonus(std::unique_ptr<Bonus> b);
bool HasBonus() const;
void ActivateBonus(class Game* game, size_t index);

SDL_Color color{255, 255, 255, 255};

private:
std::unique_ptr<Bonus> bonus;
};
91 changes: 91 additions & 0 deletions lab1/src/Bonus.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
#include "Bonus.hpp"
#include "Game.hpp"
#include "Gem.hpp"
#include "Colors.hpp"
#include <random>
#include <algorithm>

namespace {
int getRandomInt(int start, int end) {
static std::random_device rd;
static std::mt19937 gen(rd());
std::uniform_int_distribution<int> dist(start, end - 1);
return dist(gen);
}

bool isNeighbours(size_t a, size_t b) {
if (a == b) return false;
if (a > b) std::swap(a, b);
if (b - a == 1 && a % LINE_LENGTH != LINE_LENGTH-1) return true;
if (b - a == LINE_LENGTH) return true;
return false;
}
}

void RecolorBonus::Draw(SDL_Renderer* renderer, const SDL_FRect& rect) const {
SDL_SetRenderDrawColor(renderer, 0, 0, 255, 255);
SDL_FRect bonusRect = {
rect.x + rect.w * 3/8,
rect.y + rect.h * 3/8,
rect.w / 4,
rect.h / 4
};
SDL_RenderFillRect(renderer, &bonusRect);
}

void RecolorBonus::Activate(Game* game, size_t index) {
auto& field = game->GetField();
auto gem = std::dynamic_pointer_cast<Gem>(field[index]);
if (!gem) return;

auto src_color = gem->color;
gem->color = PALETTE[getRandomInt(0, PALETTE_SIZE)];

constexpr int RECOLOR_RADIUS = 2;
int x = index % LINE_LENGTH;
int y = index / LINE_LENGTH;

int x1 = getRandomInt(std::max(0, x - RECOLOR_RADIUS), std::min(x + RECOLOR_RADIUS, LINE_LENGTH));
int y1 = getRandomInt(std::max(0, y - RECOLOR_RADIUS), std::min(y + RECOLOR_RADIUS, ROWS));

size_t i1 = x1 + y1 * LINE_LENGTH;

while (isNeighbours(index, i1)) {
x1 = getRandomInt(std::max(0, x - RECOLOR_RADIUS), std::min(x + RECOLOR_RADIUS, LINE_LENGTH));
y1 = getRandomInt(std::max(0, y - RECOLOR_RADIUS), std::min(y + RECOLOR_RADIUS, ROWS));
i1 = x1 + y1 * LINE_LENGTH;
}

if (auto other = std::dynamic_pointer_cast<Gem>(field[i1])) {
other->color = src_color;
}
}

void BombBonus::Draw(SDL_Renderer* renderer, const SDL_FRect& rect) const {
SDL_SetRenderDrawColor(renderer, 255, 0, 0, 255);
SDL_FRect bonusRect = {
rect.x + rect.w * 3/8,
rect.y + rect.h * 3/8,
rect.w / 4,
rect.h / 4
};
SDL_RenderFillRect(renderer, &bonusRect);
}

void BombBonus::Activate(Game* game, size_t index) {
auto& field = game->GetField();
for (int i = 0; i < 4; i++) {
int x = getRandomInt(0, LINE_LENGTH);
int y = getRandomInt(0, ROWS);
size_t j = x + y * LINE_LENGTH;

if (j < field.size()) {
for (int row = y; row > 0; row--) {
size_t current = x + row * LINE_LENGTH;
size_t above = x + (row-1) * LINE_LENGTH;
field[current] = field[above];
}
field[x] = std::make_shared<Gem>(PALETTE[getRandomInt(0, PALETTE_SIZE)]);
}
}
}
Loading