#include "editor.hpp" #include #include #include #include #include #include #include #include #include #include #include #include "imgui-SFML.h" #include "imgui.h" #include "level.hpp" #include "nfd.h" #include "nfd.hpp" #include "tileset.hpp" namespace silly::editor { void Editor::update(const sf::Event &event, sf::RenderWindow &window) { // tile rotation if (const auto *e = event.getIf()) { if (e->code == sf::Keyboard::Key::R) { this->rotation += 90.0f; if (this->rotation >= 360.0f) { this->rotation = 0.0f; } } } // world movement if (const auto *e = event.getIf()) { if (e->button == sf::Mouse::Button::Middle) { isDragging = true; lastMousePosition = window.mapPixelToCoords(sf::Mouse::getPosition(window)); } } if (const auto *e = event.getIf()) { if (e->button == sf::Mouse::Button::Middle) { isDragging = false; lastMousePosition = {}; } } // world zoom if (const auto *e = event.getIf()) { sf::FloatRect visibleArea({0.f, 0.f}, sf::Vector2f(window.getSize())); sf::View view(visibleArea); this->zoom -= e->delta; this->zoom = (this->zoom * 10.0f) / 10.0f; if (this->zoom >= 1.0f) { this->zoom = 1.0f; } else if (this->zoom <= 0.3f) { this->zoom = 0.3f; } view.zoom(this->zoom); view.setCenter(window.getView().getCenter()); window.setView(view); } } void Editor::update(sf::RenderWindow &window) { // tile placement if ((sf::Mouse::isButtonPressed(sf::Mouse::Button::Left) || sf::Mouse::isButtonPressed(sf::Mouse::Button::Right)) && !this->package.get_levels().empty() && !this->package.get_current_level().get_floors().empty()) { sf::Vector2f mousePosition = window.mapPixelToCoords(sf::Mouse::getPosition(window)); TileLevel &level = this->package.get_current_level(); TileFloor &floor = level.get_current_floor(); for (int x = 0; x < floor.get_width(); x++) { for (int y = 0; y < floor.get_height(); y++) { int rx = x * TILE_WIDTH, ry = y * TILE_HEIGHT; if ((rx < mousePosition.x && mousePosition.x < rx + TILE_WIDTH) && (ry < mousePosition.y && mousePosition.y < ry + TILE_HEIGHT) && // editor related mousePosition.x < window.getSize().x - 400.0f && !this->newTileState.has_value()) { sf::Vector2i pos(x, y); if (this->selectedTile.has_value()) { auto tile = this->selectedTile.value(); if (sf::Mouse::isButtonPressed(sf::Mouse::Button::Left)) { floor.place_tile(tile, pos, this->rotation); } else if (sf::Mouse::isButtonPressed(sf::Mouse::Button::Right)) { floor.remove_tile(tile->type, pos); } } } } } } // world movement if (isDragging) { sf::Vector2f mousePosition = window.mapPixelToCoords(sf::Mouse::getPosition(window)); sf::Vector2f deltaPosition = lastMousePosition - mousePosition; sf::View view = window.getView(); view.move(deltaPosition); window.setView(view); } } void Editor::createNewFloor(const sf::RenderWindow &window) { sf::Vector2u windowSize = window.getSize(); NewFloorState &state = this->newFloorState.value(); ImGui::SetNextWindowPos( ImVec2(windowSize.x / 2.0f - 100, windowSize.y / 2.0f - 50)); ImGui::SetNextWindowSize(ImVec2(200, 100)); ImGui::Begin("New floor", NULL, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize); ImGui::InputInt("Width", &state.width); ImGui::InputInt("Height", &state.height); state.width = std::min(std::max(state.width, 1), 100); state.height = std::min(std::max(state.height, 1), 100); if (ImGui::Button("Create")) { TileFloor floor(state.width, state.height); this->package.get_current_level().add_floor(floor); ImGui::SetWindowCollapsed(true); } if (ImGui::IsWindowCollapsed()) { ImGui::SetWindowCollapsed(false); this->newFloorState = std::nullopt; } ImGui::End(); } void Editor::createNewLevel(const sf::RenderWindow &window) { sf::Vector2u windowSize = window.getSize(); NewLevelState &state = this->newLevelState.value(); ImGui::SetNextWindowPos( ImVec2(windowSize.x / 2.0f - 100, windowSize.y / 2.0f - 40)); ImGui::SetNextWindowSize(ImVec2(200, 80)); ImGui::Begin("New level", NULL, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize); ImGui::InputText("Name", (char *)state.name.c_str(), sizeof(state.name)); if (ImGui::Button("Create")) { TileLevel level(state.name); this->package.add_level(level); ImGui::SetWindowCollapsed(true); } if (ImGui::IsWindowCollapsed()) { ImGui::SetWindowCollapsed(false); this->newLevelState = std::nullopt; } ImGui::End(); } void Editor::render(const sf::RenderWindow &window) { sf::Vector2u windowSize = window.getSize(); int width = 400; int height = windowSize.y; int x = windowSize.x - width; int y = 0; if (package.get_levels().empty()) { if (!this->newLevelState.has_value()) { this->newLevelState = std::make_optional((NewLevelState){}); } this->createNewLevel(window); return; } if (package.get_current_level().get_floors().empty()) { if (!this->newFloorState.has_value()) { this->newFloorState = std::make_optional((NewFloorState){}); } this->createNewFloor(window); return; } ImGui::SetNextWindowPos(ImVec2(x, y)); ImGui::SetNextWindowSize(ImVec2(width, height)); ImGui::Begin("Editor", NULL, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoTitleBar); // --- SELECTED TILE --- ImGui::BeginChild("SelectedTileRegion", ImVec2(200, 100)); if (this->selectedTile.has_value()) { TilesetTile *t = this->selectedTile->get(); if (t->type == TILE_FLOOR) { ImGui::Text("FLOOR"); } else if (t->type == TILE_WALL) { ImGui::Text("WALL"); } else { ImGui::Text("UNKNOWN"); } ImGui::Image(t->texture, sf::Vector2f(64, 64)); } else { ImGui::Text("NO TILE SELECTED!"); ImGui::Dummy(ImVec2(64, 64)); } ImGui::EndChild(); ImGui::SameLine(); // --- EDITOR SETTINGS --- ImGui::BeginChild("EditorSettingsRegion", ImVec2(170, 100)); ImGui::Text("Rotation is %.0f degrees", this->rotation); ImGui::EndChild(); // --- TILE SELECTION --- ImGui::BeginChild("TileSelectionRegion", ImVec2(0, 400), ImGuiChildFlags_Border, ImGuiWindowFlags_HorizontalScrollbar); float padding = 10.0f; float imageSize = TILE_WIDTH * 2.5f; int columns = ImGui::GetContentRegionAvail().x / (imageSize + padding); int count = 0; for (auto it = this->package.get_tileset().get_tiles().begin(); it != this->package.get_tileset().get_tiles().end(); ++it) { if (ImGui::ImageButton(std::to_string(it->get()->id).c_str(), it->get()->texture, sf::Vector2f(imageSize, imageSize))) { this->selectedTile = std::make_optional(*it); } count++; if (count % columns != 0) { ImGui::SameLine(); } } ImGui::EndChild(); // --- "CREATING A NEW TILE" WINDOW --- if (ImGui::Button("Add tile") && !this->newTileState.has_value()) { this->newTileState = std::make_optional((NewTileState){"", {}, TILE_FLOOR}); } if (this->newTileState.has_value()) { NewTileState &state = this->newTileState.value(); ImGui::SetNextWindowPos( ImVec2(windowSize.x / 2.0f - 200, windowSize.y / 2.0f - 250)); ImGui::SetNextWindowSize(ImVec2(400, 500)); ImGui::Begin("Creating a new tile", NULL, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize); // -- Tile type -- ImGui::Text("Tile type"); if (ImGui::RadioButton("Floor", state.type == TILE_FLOOR)) { state.type = TILE_FLOOR; } ImGui::SameLine(); if (ImGui::RadioButton("Wall", state.type == TILE_WALL)) { state.type = TILE_WALL; } // -- Texture loading -- ImGui::Text("2D"); if (!state.path.empty()) { ImGui::Image(state.texture); ImGui::SameLine(); } if (ImGui::Button("Load texture file")) { NFD::UniquePath outPath; nfdfilteritem_t filterItem[1] = {{"Images", "png"}}; nfdresult_t result = NFD::OpenDialog(outPath, filterItem, 1); if (result == NFD_OKAY) { state.path = outPath.get(); if (!state.texture.loadFromFile(state.path)) { state.path = ""; } } } // -- Tile preview -- if (!state.path.empty()) { for (int y = 0; y <= 5; y++) { for (int x = 0; x <= 10; x++) { ImGui::Image(state.texture); if (x < 10) { ImGui::SameLine(0, 0); } } } } // -- Tile creation -- if (ImGui::Button("Create a new tile")) { this->package.get_tileset().add_tile(state.path, state.type); ImGui::SetWindowCollapsed(true); } if (ImGui::IsWindowCollapsed()) { ImGui::SetWindowCollapsed(false); this->newTileState = std::nullopt; } ImGui::End(); } ImGui::End(); } const float Editor::get_zoom() const { return this->zoom; } }