Created
December 16, 2014 14:55
-
-
Save yuyoyuppe/3edd605caea025085f52 to your computer and use it in GitHub Desktop.
clone of http://moh97.us/flow/. dev time ~1.5h
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <SFML/Graphics.hpp> | |
#include <chrono> | |
#include <iostream> | |
#include <vector> | |
/////////////////////////////////////////////////////////////////////////////////////////// | |
// КОНСТАНТЫ, СТРУКТУРЫ И ПРОЧИЕ НЯШНОСТИ | |
float screen_width = 500; | |
float screen_height = 500; | |
int field_size = 5; | |
struct Link | |
{ | |
int colorid; | |
sf::Vector2i position; | |
}; | |
struct Path | |
{ | |
int colorid; | |
std::vector<sf::Vector2i> links; | |
bool completed; | |
}; | |
/////////////////////////////////////////////////////////////////////////////////////////// | |
/////////////////////////////////////////////////////////////////////////////////////////// | |
// ИГРОВАЯ ЛОГИКА | |
std::vector<Link> fixed_points = {{0, {0, 0}}, {1, {1, 2}}, {1, {0, 1}}, {0, {2, 2}}}; // стратовые координаты | |
//std::vector<Path> paths; // пути | |
std::vector<Path> paths = {{0, {{0, 0}, {1, 0}, {2, 0}, {2, 1}}}}; // для удобства | |
typedef std::pair<bool, Link> OptionalLink; | |
OptionalLink pen_cell; // последняя позиция пера. первый параметр bool будет false, если не рисуем путь | |
sf::Vector2i cell_from_xy(sf::Vector2i xy) | |
{ | |
return{xy.x / screen_width * field_size, xy.y / screen_height * field_size}; | |
} | |
OptionalLink none_link() | |
{ | |
return std::make_pair(false, Link{-1, sf::Vector2i(-1, -1)}); | |
} | |
OptionalLink search_in_fixed(sf::Vector2i cell) | |
{ | |
auto res = std::find_if(fixed_points.begin(), fixed_points.end(), [&](Link l) {return cell == l.position;}); | |
if(fixed_points.end() != res) | |
return std::make_pair(true, *res); | |
else | |
return none_link(); | |
} | |
OptionalLink search_in_paths(sf::Vector2i cell) | |
{ | |
for(auto & p : paths) | |
{ | |
auto res = std::find_if(p.links.begin(), p.links.end(), [&](sf::Vector2i l) {return cell == l; }); | |
if(p.links.end() != res) | |
return std::make_pair(true, Link{p.colorid, *res}); | |
} | |
return none_link(); | |
} | |
void remove_path_by_colorid(int color_id) | |
{ | |
paths.erase(std::remove_if(paths.begin(), paths.end(), [&](Path & p) {return p.colorid == color_id;}), paths.end()); | |
} | |
Path * path_by_link(Link ol) | |
{ | |
for(auto & p : paths) | |
{ | |
auto res = std::find_if(p.links.begin(), p.links.end(), [&](sf::Vector2i l) {return ol.position == l;}); | |
if(p.links.end() != res) | |
return &p; | |
} | |
return nullptr; | |
} | |
void handle_pressed_down(sf::Vector2i cell) | |
{ | |
pen_cell = search_in_fixed(cell); | |
if(!pen_cell.first) | |
pen_cell = search_in_paths(cell); | |
std::cout << "pressed down at " << cell.x << " " << cell.y << (pen_cell.first? " and probably could start a path!" : "") << std::endl; | |
} | |
void handle_released(sf::Vector2i cell) | |
{ | |
std::cout << "released in " << cell.x << " " << cell.y << std::endl; | |
pen_cell.first = false; | |
} | |
bool validate(sf::Vector2i from, sf::Vector2i to) | |
{ | |
sf::Vector2f diff{float((from - to).x), float((from - to).y)}; | |
float length = std::sqrt(diff.x * diff.x + diff.y * diff.y); | |
return (length - 1.0f) < 0.0001f; | |
} | |
void handle_move_to_another_pressed(sf::Vector2i cell) | |
{ | |
if(!pen_cell.first) | |
{ | |
std::cout << "cannot draw a path from the invalid position!" << std::endl; | |
return; | |
} | |
Path * current_path = nullptr; | |
auto pen_search = search_in_fixed(pen_cell.second.position); | |
if(pen_search.first) // если нам нужно начать новый путь, т.к. мы рисуем из фиксированной точки | |
{ | |
remove_path_by_colorid(pen_search.second.colorid); // предварительно удаляем путь заданного цвета, если он был | |
paths.push_back(Path{pen_cell.second.colorid, {pen_cell.second.position}}); // создаём его | |
current_path = &paths.back(); // и направляем на него указатель - пусть он охренеет | |
} | |
else | |
current_path = path_by_link(pen_cell.second); // иначе находим существующий путь | |
auto movedto_search = search_in_fixed(cell); // теперь займёмся точкой, в которую мы переместились | |
bool path_completed = false; | |
if(movedto_search.first) // если мы попали в фиксированную точку | |
{ | |
if((movedto_search.second.colorid == pen_cell.second.colorid) && !search_in_paths(cell).first) // если цвета совпадают, а координаты - нет, то заканчиваем путь. | |
{ | |
std::cout << "you've completed the path!" << std::endl; | |
path_completed = true; // добавляем к условию победы | |
} | |
else // иначе ничего не делаем | |
{ | |
std::cout << "cannot do anything from here!" << std::endl; | |
return; | |
} | |
} | |
else // если мы не попали в фикс. точку, то нужно проверить, попали ли мы на какой-нибудь путь | |
{ | |
movedto_search = search_in_paths(cell); | |
if(movedto_search.first) // если мы попали в существующий путь | |
{ | |
if(movedto_search.second.colorid == pen_cell.second.colorid) // если мы попали в свой путь | |
{ | |
std::cout << "moved while pressed down to " << cell.x << " " << cell.y << " to the same path" << std::endl; | |
auto res = std::find_if(current_path->links.begin(), current_path->links.end(), | |
[&](sf::Vector2i l) {return l == movedto_search.second.position; }); // ищем в пути позицию текущей координаты | |
current_path->links.erase(res, current_path->links.end()); // удаляем всё, что было после неё, т.к. теперь мы путь рисуем от неё | |
} | |
else | |
{ | |
std::cout << "moved while pressed down to " << cell.x << " " << cell.y << " on the different path" << std::endl; | |
auto tmp = Link{current_path->colorid, current_path->links.front()}; // запоминаем первое звено текущего пути, т.к. придётся получать на него указатель заново(C++ за 300) | |
remove_path_by_colorid(movedto_search.second.colorid); // удаляем другой путь, т.к. мы рисуем на его месте | |
current_path = path_by_link(tmp); // восстанавливаем указатель, ведь он умер после удаления пути | |
} | |
} | |
else // попали в пустоту | |
{ | |
std::cout << "moved while pressed down to " << cell.x << " " << cell.y << " to the v01d" << std::endl; | |
auto res = std::find_if(current_path->links.begin(), current_path->links.end(), | |
[](sf::Vector2i l) {return l == pen_cell.second.position; }); // ищем в пути позицию предыдущей координаты, т.е. pen_cell | |
current_path->links.erase(++res, current_path->links.end()); // удаляем всё, что было после pen_cell, т.к. теперь мы путь рисуем от неё, а оставшийся путь нам больше не нужен | |
} | |
} | |
if(validate(pen_cell.second.position, cell)) // проверяем, что новая ячейка находится на расстоянии 1 от предыдущей, т.е. 1 шаг в горизонтали или вертикали онли | |
{ // предотвращает случайное перемещение по диагонали и проброс через фиксированную точку | |
current_path->links.push_back(cell); // добавляем в путь текущую координату | |
pen_cell = OptionalLink{true, {current_path->colorid, cell}}; | |
} | |
else // не будем ничего добавлять, если человек пытается протянуть как-то безумно:) | |
{ | |
std::cout << "Whoa! calm down, stop trying to polish the screen!" << std::endl; | |
pen_cell = none_link(); // прерываем рисование. заебал | |
} | |
current_path->completed = path_completed; | |
} | |
void handle_controls(sf::RenderWindow & w, float delta) | |
{ | |
static bool ButtonPressed = false; // запоминаем состоянии мышки! | |
static sf::Vector2i current_cell = {0, 0}; | |
sf::Event event; | |
while(w.pollEvent(event)) | |
{ | |
if(event.type == sf::Event::Closed) | |
w.close(); | |
if(event.type == sf::Event::MouseButtonPressed) // обрабатывааем событие нажатия кнопки мышки | |
{ | |
current_cell = cell_from_xy({event.mouseButton.x, event.mouseButton.y}); | |
handle_pressed_down(current_cell); | |
ButtonPressed = true; | |
} | |
if(event.type == sf::Event::MouseMoved && ButtonPressed) // обрабатывааем событие перемещения мышки с нажатой кнопкой | |
{ | |
auto possibly_new_cell = cell_from_xy({event.mouseMove.x, event.mouseMove.y}); // нам интересен только факт перемещения в другую ячейку | |
if(possibly_new_cell != current_cell) | |
{ | |
current_cell = possibly_new_cell; | |
handle_move_to_another_pressed(current_cell); | |
} | |
} | |
if(event.type == sf::Event::MouseButtonReleased) // обрабатываем событие отпускания кнопки мышки | |
{ | |
current_cell = cell_from_xy({event.mouseButton.x, event.mouseButton.y}); | |
handle_released(current_cell); | |
ButtonPressed = false; | |
} | |
} | |
} | |
/////////////////////////////////////////////////////////////////////////////////////////// | |
/////////////////////////////////////////////////////////////////////////////////////////// | |
// ГРАФОН | |
std::vector<sf::Color> color_values; // случайные цвета по айди, ня | |
void draw_line(sf::RenderWindow & w, sf::Vector2f start, sf::Vector2f end, sf::Color c) | |
{ | |
sf::Vertex line[] = | |
{ | |
sf::Vertex(start), | |
sf::Vertex(end) | |
}; | |
line[0].color = c; | |
line[1].color = c; | |
w.draw(line, 2, sf::Lines); | |
} | |
void draw_circle(sf::RenderWindow & w, sf::Vector2i cell, sf::Color c) | |
{ | |
sf::CircleShape s(screen_width / field_size / 5); | |
s.setFillColor(c); | |
s.setPosition(cell.x * screen_width / field_size + screen_width / field_size / 2 - screen_width / field_size / 5, | |
cell.y * screen_height / field_size + screen_height / field_size / 2 - screen_width / field_size / 5); | |
w.draw(s); | |
} | |
sf::Color color_from_id(int id) | |
{ | |
if(id >= color_values.size()) | |
color_values.push_back(sf::Color(rand() % 255, rand() % 255, rand() % 255)); | |
return color_values[id]; | |
} | |
void draw_elements(sf::RenderWindow & w) | |
{ | |
// рисуем решетку | |
for(int i = 1; i < field_size; i++) | |
{ | |
draw_line(w, {screen_width / field_size * i, 0.0f}, {screen_width / field_size * i, screen_height}, sf::Color::Black); | |
draw_line(w, {0.0f, screen_height / field_size * i}, {screen_width, screen_height / field_size * i}, sf::Color::Black); | |
} | |
// рисуем начальные звенья | |
for(auto & p : fixed_points) | |
draw_circle(w, p.position, color_from_id(p.colorid)); | |
// рисуем пути | |
for(auto & p : paths) | |
for(int i = 1; i < p.links.size(); i++) | |
draw_line(w, | |
{p.links[i - 1].x * screen_width / field_size + screen_width / field_size / 2, p.links[i - 1].y * screen_height / field_size + screen_height / field_size / 2}, | |
{p.links[i].x * screen_width / field_size + screen_width / field_size / 2, p.links[i].y * screen_height / field_size + screen_height / field_size / 2}, | |
color_from_id(p.colorid)); | |
} | |
/////////////////////////////////////////////////////////////////////////////////////////// | |
void game() | |
{ | |
srand(time(NULL)); | |
sf::RenderWindow window(sf::VideoMode(screen_width, screen_height), "Ebola"); | |
window.setFramerateLimit(250); | |
float delta_time = 0; | |
while(window.isOpen()) | |
{ | |
auto frame_start = std::chrono::high_resolution_clock::now(); | |
window.clear(sf::Color(153, 217, 234)); | |
handle_controls(window, delta_time); | |
draw_elements(window); | |
window.display(); | |
delta_time = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::high_resolution_clock::now() - frame_start).count(); | |
if(paths.size() == fixed_points.size() / 2) // проверяем условие победы | |
{ | |
if(std::all_of(paths.begin(), paths.end(), [](Path p) {return p.completed; })) | |
{ | |
std::cout << "you won^_^" << std::endl; | |
break; | |
} | |
} | |
} | |
} | |
void settings() | |
{ | |
char choice = '1'; | |
while(true) | |
{ | |
std::cout << "1. Resolution\n2.Field size\n3.Exit\nSelect an option: "; | |
std::cin >> choice; | |
switch(choice) | |
{ | |
case '1': | |
std::cin >> screen_height; | |
screen_width = screen_height; | |
break; | |
case '2': | |
std::cin >> field_size; | |
break; | |
default: | |
case '3': | |
return; | |
break; | |
} | |
std::cin.clear(); | |
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n'); | |
} | |
} | |
int main() | |
{ | |
char choice = '1'; | |
while(true) | |
{ | |
std::cout << "1. New game\n2.Settings\n3.Exit\nSelect an option: "; | |
std::cin >> choice; | |
switch(choice) | |
{ | |
case '1': | |
game(); | |
break; | |
case '2': | |
settings(); | |
break; | |
default: | |
case '3': | |
return 0; | |
break; | |
} | |
std::cin.clear(); | |
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n'); | |
} | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment