Want to contribute? Fork me on Codeberg.org!
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
septadrop/src/Main.cpp

473 lines
12 KiB

#include <SFML/Graphics.hpp>
#include <SFML/Graphics/Color.hpp>
3 years ago
#include <SFML/Graphics/Font.hpp>
3 years ago
#include <SFML/Graphics/Rect.hpp>
#include <SFML/Graphics/RectangleShape.hpp>
#include <SFML/Graphics/RenderWindow.hpp>
#include <SFML/Graphics/Sprite.hpp>
3 years ago
#include <SFML/Graphics/Texture.hpp>
#include <SFML/System/Clock.hpp>
#include <SFML/System/Vector2.hpp>
#include <SFML/Window/Keyboard.hpp>
#include <SFML/Window/Window.hpp>
#include <SFML/Window/WindowStyle.hpp>
#include <initializer_list>
#include <iostream>
#include <iterator>
3 years ago
#include <string>
3 years ago
#define TILE_SIZE 20
#define GRID_WIDTH 14
#define GRID_HEIGHT 20
#define WINDOW_WIDTH 500
#define WINDOW_HEIGHT 440
#define PLAYFIELD_X 20
#define PLAYFIELD_Y 20
class NumberRenderer {
public:
sf::Texture texture;
sf::IntRect comma_rect;
sf::IntRect numeral_rects[10];
NumberRenderer(
sf::Texture _texture,
sf::IntRect _comma_rect,
std::initializer_list<sf::IntRect> _numeral_rects
) {
texture = _texture;
comma_rect = _comma_rect;
sprite = sf::Sprite(texture);
int i = 0;
for (auto numeral_rect = _numeral_rects.begin(); numeral_rect != _numeral_rects.end(); ++numeral_rect) {
numeral_rects[i] = *numeral_rect;
i++;
}
}
void render(sf::RenderWindow* window, uint number, int x, int y) {
auto number_string = std::to_string(number);
std::string numeral_string;
numeral_string.push_back(number_string.back());
auto numeral_rect = numeral_rects[std::stoi(numeral_string)];
int x_offset = -numeral_rect.width;
uint digits = number_string.length();
for (int i = digits - 1; i >= 0; i--) {
char numeral_string[] = {number_string[i]};
auto numeral_rect = numeral_rects[std::stoi(numeral_string)];
if ((digits - i) % 3 == 1 && i != digits - 1) {
sprite.setTextureRect(comma_rect);
sprite.setPosition(x + x_offset, y);
window->draw(sprite);
x_offset -= numeral_rect.width;
}
sprite.setTextureRect(numeral_rect);
sprite.setPosition(x + x_offset, y);
window->draw(sprite);
if (i == 0) {
break;
}
if ((digits - i) % 3 == 0) {
x_offset -= comma_rect.width;
continue;
}
numeral_string[0] = number_string[i - 1];
numeral_rect = numeral_rects[std::stoi(numeral_string)];
x_offset -= numeral_rect.width;
}
}
private:
sf::Sprite sprite;
};
3 years ago
class TileType {
public:
3 years ago
sf::IntRect texture_rect;
sf::IntRect ghost_texture_rect;
TileType(sf::IntRect _texture_rect, sf::IntRect _ghost_texture_rect) {
texture_rect = _texture_rect;
ghost_texture_rect = _ghost_texture_rect;
}
};
3 years ago
TileType tile_type_0(
sf::IntRect(0, 0, TILE_SIZE, TILE_SIZE),
sf::IntRect(0, TILE_SIZE, TILE_SIZE, TILE_SIZE)
);
TileType tile_type_1(
sf::IntRect(TILE_SIZE, 0, TILE_SIZE, TILE_SIZE),
sf::IntRect(TILE_SIZE, TILE_SIZE, TILE_SIZE, TILE_SIZE)
);
TileType tile_type_2(
sf::IntRect(TILE_SIZE * 2, 0, TILE_SIZE, TILE_SIZE),
sf::IntRect(TILE_SIZE * 2, TILE_SIZE, TILE_SIZE, TILE_SIZE)
);
TileType tile_type_3(
sf::IntRect(TILE_SIZE * 3, 0, TILE_SIZE, TILE_SIZE),
sf::IntRect(TILE_SIZE * 3, TILE_SIZE, TILE_SIZE, TILE_SIZE)
);
TileType tile_type_4(
sf::IntRect(TILE_SIZE * 4, 0, TILE_SIZE, TILE_SIZE),
sf::IntRect(TILE_SIZE * 4, TILE_SIZE, TILE_SIZE, TILE_SIZE)
);
TileType tile_type_5(
sf::IntRect(TILE_SIZE * 5, 0, TILE_SIZE, TILE_SIZE),
sf::IntRect(TILE_SIZE * 5, TILE_SIZE, TILE_SIZE, TILE_SIZE)
);
TileType tile_type_6(
sf::IntRect(TILE_SIZE * 6, 0, TILE_SIZE, TILE_SIZE),
sf::IntRect(TILE_SIZE * 6, TILE_SIZE, TILE_SIZE, TILE_SIZE)
);
class BlockType {
public:
static BlockType i, j, l, o, s, t, z;
static BlockType* list[];
static BlockType* random() {
return list[rand() % 7];
}
TileType* tile_type;
std::vector<std::vector<bool>> grid;
bool rotate;
BlockType(TileType* _tile_type, const std::vector<std::vector<bool>> _grid, bool _rotate = true) {
tile_type = _tile_type;
grid = _grid;
rotate = _rotate;
}
};
3 years ago
// https://gamedev.stackexchange.com/a/17978
3 years ago
BlockType BlockType::i(&tile_type_0, {
{0, 0, 0, 0},
{1, 1, 1, 1},
{0, 0, 0, 0},
{0, 0, 0, 0}
});
3 years ago
BlockType BlockType::j(&tile_type_1, {
{1, 0, 0},
{1, 1, 1},
{0, 0, 0}
});
3 years ago
BlockType BlockType::l(&tile_type_2, {
{0, 0, 1},
{1, 1, 1},
{0, 0, 0}
});
3 years ago
BlockType BlockType::o(&tile_type_3, {
{1, 1},
{1, 1}
}, false);
3 years ago
BlockType BlockType::s(&tile_type_4, {
{0, 1, 1},
{1, 1, 0},
{0, 0, 0}
});
3 years ago
BlockType BlockType::t(&tile_type_5, {
{0, 1, 0},
{1, 1, 1},
{0, 0, 0}
});
3 years ago
BlockType BlockType::z(&tile_type_6, {
{1, 1, 0},
{0, 1, 1},
{0, 0, 0}
});
BlockType* BlockType::list[] = {&i, &j, &l, &o, &s, &t, &z};
class Block {
public:
BlockType* type;
sf::Vector2i position;
int rotation_state;
Block() {
type = BlockType::random();
position = sf::Vector2i(GRID_WIDTH / 2 - type->grid[0].size() / 2, 0);
rotation_state = 0;
}
std::vector<sf::Vector2i> get_tiles() {
std::vector<sf::Vector2i>tiles = {};
for (int y = 0; y < type->grid.size(); y++) {
for (int x = 0; x < type->grid[y].size(); x++) {
if (!type->grid[y][x]) {
continue;
}
int rotated_x = x;
int rotated_y = y;
if (type->rotate) {
int center_x = type->grid[0].size() / 2;
int center_y = type->grid.size() / 2;
int offset_x = x - center_x;
int offset_y = y - center_y;
switch (rotation_state) {
case 0:
rotated_x = x;
rotated_y = y;
break;
case 1:
rotated_x = center_x + offset_y;
rotated_y = center_y - offset_x;
break;
case 2:
rotated_x = center_x - offset_x;
rotated_y = center_y - offset_y;
break;
case 3:
rotated_x = center_x - offset_y;
rotated_y = center_y + offset_x;
break;
default:
rotation_state %= 4;
}
}
int global_x = rotated_x + position.x;
int global_y = rotated_y + position.y;
tiles.push_back(sf::Vector2i(global_x, global_y));
}
}
return tiles;
}
};
3 years ago
int main()
{
srand(time(NULL));
sf::RenderWindow window(sf::VideoMode(WINDOW_WIDTH, WINDOW_HEIGHT), "elnutris", sf::Style::Close);
3 years ago
window.setFramerateLimit(60);
Block block;
TileType* grid[GRID_HEIGHT][GRID_WIDTH] = { nullptr };
3 years ago
sf::Texture texture;
texture.loadFromFile("../res/texture.png");
sf::Sprite sprite;
sprite.setTexture(texture);
3 years ago
sf::Texture background_texture;
background_texture.loadFromFile("../res/background.png");
sf::Sprite background;
background.setTexture(background_texture);
sf::Texture numeral_texture;
numeral_texture.loadFromFile("../res/numerals.png");
NumberRenderer number_renderer(numeral_texture, sf::IntRect(134, 0, 10, 16), {
sf::IntRect(0, 0, 14, 16),
sf::IntRect(14, 0, 8, 16),
sf::IntRect(22, 0, 14, 16),
sf::IntRect(36, 0, 14, 16),
sf::IntRect(50, 0, 14, 16),
sf::IntRect(64, 0, 14, 16),
sf::IntRect(78, 0, 14, 16),
sf::IntRect(92, 0, 14, 16),
sf::IntRect(106, 0, 14, 16),
sf::IntRect(120, 0, 14, 16)
});
bool rotate = true;
bool move_left = true;
bool move_right = true;
bool snap = false;
sf::Clock update_clock;
sf::Clock move_clock;
int lines = 0;
int blocks = 0;
int tiles = 0;
3 years ago
int update_interval = 250;
3 years ago
auto clear_color = sf::Color(73, 52, 61);
3 years ago
while (window.isOpen())
{
sf::Event event;
while (window.pollEvent(event))
{
switch (event.type) {
case sf::Event::Closed:
window.close();
break;
case sf::Event::KeyPressed:
switch (event.key.code) {
case sf::Keyboard::Space:
snap = true;
break;
case sf::Keyboard::Up:
rotate = true;
break;
case sf::Keyboard::Left:
move_left = true;
break;
case sf::Keyboard::Right:
move_right = true;
break;
default:
break;
}
break;
default:
break;
}
3 years ago
}
bool is_update_frame = update_clock.getElapsedTime().asMilliseconds() > (sf::Keyboard::isKeyPressed(sf::Keyboard::Down) ? update_interval / 2 : update_interval);
if (is_update_frame) {
update_clock.restart();
}
bool is_move_frame = move_clock.getElapsedTime().asMilliseconds() > update_interval / 2;
if (is_move_frame) {
move_clock.restart();
}
// Rotation
if (rotate && is_move_frame) {
block.rotation_state++;
// Check to see if new rotation state is overlapping any tiles
for (auto tile : block.get_tiles()) {
if (tile.x <= 0 || tile.x >= GRID_WIDTH || grid[tile.y][tile.x]) {
block.rotation_state--;
break;
}
}
rotate = false;
}
// Horizontal movement
if (is_move_frame) {
int movement = 0;
if (move_left || sf::Keyboard::isKeyPressed(sf::Keyboard::Key::Left)) {
movement--;
move_left = false;
}
if (move_right ||sf::Keyboard::isKeyPressed(sf::Keyboard::Key::Right)) {
movement++;
move_right = false;
}
if (movement != 0) {
for (auto tile : block.get_tiles()) {
if (tile.x + movement < 0 || tile.x + movement >= GRID_WIDTH || grid[tile.y][tile.x + movement]) {
goto after_movement_loop;
}
}
block.position.x += movement;
}
}
after_movement_loop:
3 years ago
// Snapping
int snap_offset = 0;
while (true) {
for (auto tile : block.get_tiles()) {
int y = tile.y + snap_offset;
if (y == GRID_HEIGHT - 1 || grid[y + 1][tile.x] != nullptr) {
goto after_snap_loop;
3 years ago
}
}
snap_offset++;
3 years ago
}
after_snap_loop:
bool landed = snap;
if (snap) {
block.position.y += snap_offset;
snap = false;
}
3 years ago
// Land checking
if (!snap && is_update_frame) {
for (auto tile : block.get_tiles()) {
if (tile.y == GRID_HEIGHT - 1 || grid[tile.y + 1][tile.x] != nullptr) {
landed = true;
break;
}
}
}
// Clear window
// Normally, one would run window.clear(),
// but the background image covers the entire window.
window.draw(background);
// Draw block
if (!landed) {
for (auto tile : block.get_tiles()) {
int snap_y = tile.y + snap_offset;
3 years ago
sprite.setTextureRect(block.type->tile_type->texture_rect);
sprite.setPosition(PLAYFIELD_X + tile.x * TILE_SIZE, PLAYFIELD_Y + tile.y * TILE_SIZE);
3 years ago
window.draw(sprite);
sprite.setTextureRect(block.type->tile_type->ghost_texture_rect);
sprite.setPosition(PLAYFIELD_X + tile.x * TILE_SIZE, PLAYFIELD_Y + snap_y * TILE_SIZE);
3 years ago
window.draw(sprite);
}
}
// Landing (transfering block to grid and reinitializing)
if (landed) {
if (block.position.y == 0) {
update_interval += lines * 10;
lines = 0;
blocks = 0;
tiles = 0;
for (int y = 0; y < GRID_HEIGHT; y++) {
for (int x = 0; x < GRID_WIDTH; x++) {
grid[y][x] = nullptr;
}
}
} else {
tiles += block.get_tiles().size();
blocks++;
for (auto tile : block.get_tiles()) {
grid[tile.y][tile.x] = block.type->tile_type;
}
// Check for completed rows
for (int y = 0; y < GRID_HEIGHT; y++) {
bool completed = true;
for (int x = 0; x < GRID_WIDTH; x++) {
if (!grid[y][x]) {
completed = false;
break;
}
}
if (!completed) {
continue;
}
for (int z = y - 1; z >= 0; z--) {
for (int x = 0; x < GRID_WIDTH; x++) {
grid[z + 1][x] = grid[z][x];
}
}
lines++;
update_interval -= 10;
}
}
block = Block();
} else if(is_update_frame) {
block.position.y++;
}
// Drawing grid
for (int y = 0; y < GRID_HEIGHT; y++) {
for (int x = 0; x < GRID_WIDTH; x++) {
auto tile_type = grid[y][x];
if (tile_type == nullptr) {
// If tile_type is a nullptr (no block), continue
continue;
}
3 years ago
sprite.setTextureRect(tile_type->texture_rect);
sprite.setPosition(PLAYFIELD_X + x * TILE_SIZE, PLAYFIELD_Y + y * TILE_SIZE);
3 years ago
window.draw(sprite);
}
}
number_renderer.render(&window, lines, 477, 242);
number_renderer.render(&window, blocks, 477, 322);
number_renderer.render(&window, tiles, 477, 362);
3 years ago
window.display();
}
return 0;
}