#include #include #include "manager.hpp" #include "resource_manager.hpp" #include "states/game.hpp" #include "states/editor.hpp" /** * Définition des variables et fonctions globales internes * (accessibles uniquement dans ce fichier) */ namespace { const sf::Color SELECT_RECT_COLOR = sf::Color(33, 33, 33, 40); const sf::Color SELECT_RECT_BORDER_COLOR = sf::Color(33, 33, 33, 127); const sf::Color ZONE_POINT_COLOR = sf::Color(140, 15, 15, 255); const sf::Color ZONE_BORDER_COLOR = sf::Color(200, 15, 15, 255); const float WHEEL_SCROLL_SPEED = -7.f; const float POINTER_SCROLL_SPEED = 5.f; const int POINTER_SCROLL_PADDING = 10; } /** * Arrondit le vecteur donné à une position * sur la grille */ inline sf::Vector2f roundVectorToGrid(sf::Vector2f input) { input /= Manager::GRID; input.x = round(input.x); input.y = round(input.y); input *= Manager::GRID; return input; } Editor::Editor(Manager& manager) : Level(manager), drag_control_point(nullptr), drag_mode(Editor::DragMode::NONE) { // ajout des boutons d'action de la barre d'action action_toolbar.addButton("gear", Utility::Direction::WEST, 0); action_toolbar.addButton( "save", Utility::Direction::WEST, 1, std::bind(&Editor::save, this) ); action_toolbar.addButton( "test", Utility::Direction::WEST, 2, std::bind(&Editor::test, this) ); // affichage de l'interface d'édition du temps action_toolbar.setTimeEditable( [this]() { setTotalTime(getTotalTime() + 5); }, [this]() { setTotalTime(getTotalTime() - 5); } ); // ajout de la barre d'objets à l'écran getManager().addWidget(object_toolbar.getWindow()); } Editor::~Editor() { getManager().removeWidget(object_toolbar.getWindow()); } void Editor::enable() { Level::enable(); // attributs de la fenêtre getManager().setTitle(sf::String(L"Édition de ") + getName()); getManager().getWindow().setFramerateLimit(Manager::FPS); // on positionne la caméra au centre des joueurs sf::View camera = getCamera(); camera.setCenter(getPlayerCenter()); setCamera(camera); // joue la musique de l'éditeur ResourceManager::get().playMusic("editor.ogg"); // on affiche la barre d'objets object_toolbar.getWindow()->Show(true); } void Editor::processEvent(const sf::Event& event) { Level::processEvent(event); // lorsque l'on clique dans l'éditeur if (event.type == sf::Event::MouseButtonPressed) { sf::Vector2i mouse_position(event.mouseButton.x, event.mouseButton.y); sf::Vector2f position = pixelToCoords(mouse_position); Object::Ptr pointed_object = getObject(position); sf::Vector2f* control_point = getControlPoint(position); if (event.mouseButton.button == sf::Mouse::Left) { // clic sur un point de contrôle : déplacement du point if (control_point != nullptr) { drag_control_point = control_point; drag_mode = Editor::DragMode::CONTROL_POINT; return; } // clic + shift : sélection par rectangle de sélection if (getManager().isKeyPressed(Manager::Modifier::SHIFT)) { drag_start = mouse_position; drag_end = mouse_position; drag_mode = Editor::DragMode::SELECT_RECT; return; } // clic sur un objet : démarrage de la sélection libre if (pointed_object != nullptr) { if (getManager().isKeyPressed(Manager::Modifier::CONTROL)) { drag_start = mouse_position; drag_end = mouse_position; drag_mode = Editor::DragMode::SELECT_BULK; select(pointed_object, Editor::SelectionMode::ADD); } else { select(pointed_object, Editor::SelectionMode::FLIP); } return; } // clic gauche dans le vide : démarrage du placement en drag&drop drag_start = mouse_position; drag_end = mouse_position; drag_mode = Editor::DragMode::PLACE; select(addObject(position), Editor::SelectionMode::REPLACE); } if (event.mouseButton.button == sf::Mouse::Right) { // clic droit sur un point de contrôle : suppression de ce point if (control_point != nullptr) { removeControlPoint(control_point); return; } // clic droit sur un objet : démarrage de la suppression en drag&drop if (pointed_object != nullptr) { drag_start = mouse_position; drag_end = mouse_position; drag_mode = Editor::DragMode::REMOVE; removeObject(pointed_object); return; } } } // lorsqu'on déplace la souris if (event.type == sf::Event::MouseMoved) { sf::Vector2i mouse_position(event.mouseMove.x, event.mouseMove.y); sf::Vector2f position = pixelToCoords(mouse_position); Object::Ptr pointed_object = getObject(position); drag_end = mouse_position; // mode déplacement de point de contrôle if (drag_mode == Editor::DragMode::CONTROL_POINT) { *drag_control_point = roundVectorToGrid(position); } // mode placement d'objets if (drag_mode == Editor::DragMode::PLACE && pointed_object == nullptr) { select(addObject(position), Editor::SelectionMode::ADD); } // mode suppression d'objets if (drag_mode == Editor::DragMode::REMOVE && pointed_object != nullptr) { removeObject(pointed_object); } // mode sélection libre : on ajoute l'objet à la sélection if (drag_mode == Editor::DragMode::SELECT_BULK) { select(position, Editor::SelectionMode::ADD); } return; } // lorsqu'on relâche un clic dans l'éditeur if (event.type == sf::Event::MouseButtonReleased) { // mode sélection rectangulaire : on applique la sélection if (drag_mode == Editor::DragMode::SELECT_RECT) { select(pixelToCoords(drag_start), pixelToCoords(drag_end)); } drag_mode = Editor::DragMode::NONE; } // lorsqu'on scrolle on déplace la vue ou change la polarité if (event.type == sf::Event::MouseWheelScrolled) { // si on a Ctrl appuyé, on change la polarité if (getManager().isKeyPressed(Manager::Modifier::CONTROL)) { sf::Vector2i mouse_position(event.mouseWheelScroll.x, event.mouseWheelScroll.y); sf::Vector2f position = pixelToCoords(mouse_position); Object::Ptr pointed_object = getObject(position); if (pointed_object != nullptr) { float new_charge = pointed_object->getCharge() + event.mouseWheelScroll.delta; if (new_charge != 0) { new_charge /= std::abs(new_charge); } pointed_object->setCharge(new_charge); } } // sinon, on déplace la vue else { sf::Vector2f cur_center = getCenterGoal(); // la molette est horizontale ssi. elle l'est vraiment ou // si on utilise la molette verticale et shift bool horizontal = ( event.mouseWheelScroll.wheel == sf::Mouse::HorizontalWheel || (event.mouseWheelScroll.wheel == sf::Mouse::VerticalWheel && getManager().isKeyPressed(Manager::Modifier::SHIFT)) ); if (!horizontal) { cur_center.y += event.mouseWheelScroll.delta * WHEEL_SCROLL_SPEED; } else { cur_center.x += event.mouseWheelScroll.delta * WHEEL_SCROLL_SPEED; } setCenterGoal(cur_center); } } // gestion des touches if (event.type == sf::Event::KeyPressed) { // appui sur suppr : suppression des blocs sélectionnés if (event.key.code == sf::Keyboard::Delete) { for (auto it = selection.begin(); it != selection.end();) { removeObject(*it); } clearSelection(); } // appui sur Ctrl + S : sauvegarde du niveau if (event.key.code == sf::Keyboard::S && event.key.control) { save(); } // appui sur Ctrl + A : sélection de tous les objets if (event.key.code == sf::Keyboard::A && event.key.control) { selectAll(); } // appui sur espace : test du niveau en cours d'édition if (event.key.code == sf::Keyboard::Space) { test(); } // touche retour ou échap : on quitte l'éditeur if (event.key.code == sf::Keyboard::BackSpace || event.key.code == sf::Keyboard::Escape) { getManager().popState(); } } } void Editor::frame() { sf::RenderWindow& window = getManager().getWindow(); sf::Vector2i window_size = (sf::Vector2i) window.getSize(); Level::frame(); // dessin de la frame draw(); // màj du temps du timer action_toolbar.setTime(getTotalTime()); // scroll de la caméra lorsque la souris se situe sur les bords if (window.hasFocus()) { sf::Vector2f cur_center = getCenterGoal(); sf::Vector2i mouse = sf::Mouse::getPosition(window); // détection du dépassement sur un des 4 bords if (mouse.x >= -POINTER_SCROLL_PADDING && mouse.x < window_size.x + POINTER_SCROLL_PADDING && mouse.y >= -POINTER_SCROLL_PADDING && mouse.y < window_size.y + POINTER_SCROLL_PADDING) { if (mouse.x < POINTER_SCROLL_PADDING) { cur_center.x -= POINTER_SCROLL_SPEED; } if (mouse.x >= window_size.x - POINTER_SCROLL_PADDING) { cur_center.x += POINTER_SCROLL_SPEED; } if (mouse.y < POINTER_SCROLL_PADDING) { cur_center.y -= POINTER_SCROLL_SPEED; } if (mouse.y >= window_size.y - POINTER_SCROLL_PADDING) { cur_center.y += POINTER_SCROLL_SPEED; } setCenterGoal(cur_center); } } // màj du titre de la fenêtre getManager().setTitle(sf::String(L"Édition de ") + getName()); // positionnement de la barre d'objets object_toolbar.getWindow()->SetAllocation(sf::FloatRect( window_size.x - object_toolbar.getWidth(), action_toolbar.getHeight(), object_toolbar.getWidth(), window_size.y - action_toolbar.getHeight() )); } void Editor::draw() { sf::RenderWindow& window = getManager().getWindow(); // dessin des objets du niveau Level::draw(); // dessin de la zone de jeu const std::vector& zone = getZone(); sf::VertexArray zone_polygon(sf::LinesStrip); control_points_circles.clear(); for (auto it = zone.begin(); it != zone.end(); it++) { sf::CircleShape circle(5); sf::Vector2f position = *it; circle.setOrigin(sf::Vector2f(5, 5)); circle.setFillColor(ZONE_POINT_COLOR); circle.setPosition(position); zone_polygon.append(sf::Vertex(position, ZONE_BORDER_COLOR)); control_points_circles.push_back(circle); } zone_polygon.append(sf::Vertex(zone[0], ZONE_BORDER_COLOR)); window.draw(zone_polygon); for (auto it = control_points_circles.begin(); it != control_points_circles.end(); it++) { window.draw(*it); } // dessin du rectangle de sélection if (drag_mode == Editor::DragMode::SELECT_RECT) { sf::Vector2f size = pixelToCoords(drag_end) - pixelToCoords(drag_start); sf::Vector2f pos = pixelToCoords(drag_start); sf::RectangleShape selection_rect(size); selection_rect.setPosition(pos); selection_rect.setFillColor(SELECT_RECT_COLOR); selection_rect.setOutlineThickness(2.f); selection_rect.setOutlineColor(SELECT_RECT_BORDER_COLOR); window.draw(selection_rect); } } Object::Ptr Editor::getObject(sf::Vector2f position) { const std::vector& objects = getObjects(); for (auto it = objects.begin(); it != objects.end(); it++) { if ((*it)->getAABB().contains(position)) { return *it; } } return nullptr; } sf::Vector2f* Editor::getControlPoint(sf::Vector2f position) { std::vector& zone = getZone(); for (unsigned i = 0; i < zone.size(); i++) { if (control_points_circles[i].getGlobalBounds().contains(position)) { return &zone[i]; } } return nullptr; } Object::Ptr Editor::addObject(sf::Vector2f position) { // on arrondit à l'unité de grille la plus proche position = roundVectorToGrid(position); Object::Ptr created_object = object_toolbar.createObject(); if (created_object == nullptr) { return nullptr; } created_object->setPosition(position); // avant d'ajouter l'objet, on vérifie qu'il ne soit // pas superposé à un autre. Si c'est le cas, on annule // la procédure for (auto const &object : getObjects()) { if (object->getAABB().intersects(created_object->getAABB())) { return nullptr; } } // sinon, on ajoute l'objet return Level::addObject(created_object); } void Editor::removeObject(Object::Ptr object) { if (object == nullptr) { return; } // on supprime l'objet de la sélection selection.erase(std::remove( selection.begin(), selection.end(), object ), selection.end()); Level::removeObject(object); // si c'était un joueur, il faut renuméroter // les autres pour plus de convenance if (object->getTypeId() == Player::TYPE_ID) { // on réattribue les numéros de joueurs for (unsigned int i = 0; i < getPlayers().size(); i++) { getPlayers()[i]->setPlayerNumber(i); } } } void Editor::removeControlPoint(sf::Vector2f* control_point) { if (control_point == nullptr) { return; } std::vector& zone = getZone(); // on supprime le point de la liste zone.erase(std::remove( zone.begin(), zone.end(), *control_point ), zone.end()); } void Editor::removeObject(sf::Vector2f position) { removeObject(getObject(position)); } void Editor::select(Object::Ptr object, Editor::SelectionMode mode) { if (object == nullptr) { return; } bool already_selected = std::count(selection.begin(), selection.end(), object) > 0; // dans les modes REPLACE et FLIP, on remplace l'ancienne sélection // pour REPLACE, on sélectionne forcément l'objet // pour FLIP, on le sélectionne s'il ne l'est pas, on le désélectionne sinon if (mode == Editor::SelectionMode::REPLACE || mode == Editor::SelectionMode::FLIP) { clearSelection(); // on resélectionne l'objet ssi. on force la sélection // ou s'il n'était pas déjà sélectionné if (!already_selected || mode == Editor::SelectionMode::REPLACE) { object->setSelected(true); selection.push_back(object); } } // dans le mode ADD, on rajoute juste l'objet à la sélection if (mode == Editor::SelectionMode::ADD && !already_selected) { object->setSelected(true); selection.push_back(object); } } void Editor::select(sf::Vector2f position, Editor::SelectionMode mode) { select(getObject(position), mode); } void Editor::select(sf::Vector2f top_left, sf::Vector2f bottom_right) { const std::vector& objects = getObjects(); sf::FloatRect selection_rect( std::min(top_left.x, bottom_right.x), std::min(top_left.y, bottom_right.y), std::abs(bottom_right.x - top_left.x), std::abs(bottom_right.y - top_left.y) ); clearSelection(); // sélection des éléments intersectant le rectangle for (auto it = objects.begin(); it != objects.end(); it++) { if ((*it)->getAABB().intersects(selection_rect)) { select(*it, Editor::SelectionMode::ADD); } } } void Editor::clearSelection() { for (auto it = selection.begin(); it != selection.end(); it++) { (*it)->setSelected(false); } selection.clear(); } void Editor::selectAll() { const std::vector& objects = getObjects(); for (auto it = objects.begin(); it != objects.end(); it++) { (*it)->setSelected(true); selection.push_back(*it); } } void Editor::test() { // création d'une partie en mode test auto test_game = std::unique_ptr(new Game(getManager(), true)); clearSelection(); // copie des propriétés test_game->setName(getName()); test_game->setTotalTime(getTotalTime()); test_game->setBackground(getBackground()); test_game->setMusic(getMusic()); test_game->getObjects().clear(); test_game->getZone().clear(); // copie des objets du niveau vers le jeu for (auto const &object : getObjects()) { test_game->addObject(object->clone()); } // copie de la zone de jeu for (auto const &point : getZone()) { test_game->getZone().push_back(point); } // repositionnement de la caméra test_game->setCamera(getCamera()); getManager().pushState(std::move(test_game)); }