diff --git a/application/editormode.cpp b/application/editormode.cpp index 2096bb72..56dabcb8 100644 --- a/application/editormode.cpp +++ b/application/editormode.cpp @@ -931,6 +931,7 @@ void editor_mode::restore_terrain(EditorSnapshot &Snapshot) void editor_mode::undo_last() { + first_step_done(first_step::undo); // a change of the terrain made a moment ago is a step of its own before it's taken back m_history_busy = true; terrain_history_step(); @@ -1478,6 +1479,7 @@ bool editor_mode::update() render_new_scenery_popup(); render_open_scenery_popup(); render_shortcuts_window(); + render_first_steps(); render_orthophoto_window(); render_environment_window(); draw_selection_overlay(); @@ -2906,6 +2908,7 @@ bool editor_mode::save() ui()->set_status(STR("The scenery wasn't opened for editing. Start the simulator with: -edit "), true); return false; } + first_step_done(first_step::save); // the heightmap terrain is kept in files of its own, the scenery only needs the directive which loads it std::vector rootstatements; @@ -4582,9 +4585,66 @@ void editor_mode::render_help_menu() ImGui::MenuItem(STR_C("Keyboard shortcuts"), "F1", &m_shortcuts_open); if (ImGui::IsItemHovered()) ImGui::SetTooltip("%s", STR_C("The keys of the editor, for each field of work")); + ImGui::MenuItem(STR_C("First steps"), nullptr, &m_first_steps_open); + if (ImGui::IsItemHovered()) + ImGui::SetTooltip("%s", STR_C("Laying the first track, step by step")); ImGui::EndMenu(); } +void editor_mode::first_step_done(first_step const Step) +{ + m_first_steps_done[static_cast(Step)] = true; +} + +void editor_mode::render_first_steps() +{ + if (false == m_first_steps_open) + return; + if (ui()->mode() == nodebank_panel::TRACK) + first_step_done(first_step::tracks); + if (m_lay.active) + first_step_done(first_step::lay); + static char const *const steps[]{ + "F3: the Tracks field of work", + "L: lay track", + "Click on the ground, then once more: a straight. Each next click lays a piece and goes on from its end, Esc ends", + "While laying on: Ctrl+wheel changes the radius, Shift+wheel the shape (arc then straight, straight then arc, arc, straight). The plain wheel zooms", + "Joining: press on a free end (magenta ring) and drag onto another free end", + "T: switch. Click a straight, then press on it and drag: along sets the direction, sideways the side", + "Ctrl+Z takes a step back, Backspace while laying takes back the last piece", + "Ctrl+S saves the scenery"}; + ImGui::SetNextWindowSize(ImVec2(440.0f * Global.ui_scale, 0.0f), ImGuiCond_FirstUseEver); + ImGui::SetNextWindowPos(ImVec2(8.0f, ImGui::GetIO().DisplaySize.y - 8.0f), ImGuiCond_FirstUseEver, ImVec2(0.0f, 1.0f)); + ImGui::SetNextWindowBgAlpha(1.0f); + if (ImGui::Begin((std::string(STR_C("First steps")) + "###firststeps").c_str(), &m_first_steps_open, ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_AlwaysAutoResize)) + { + ImGui::PushTextWrapPos(420.0f * Global.ui_scale); + ImGui::TextDisabled("%s", STR_C("The ticks fill in by themselves, the window doesn't stop anything")); + for (std::size_t i = 0; i < m_first_steps_done.size(); ++i) + { + bool const done{m_first_steps_done[i]}; + ImGui::TextColored(done ? ImVec4(0.4f, 0.9f, 0.4f, 1.0f) : ImGui::GetStyleColorVec4(ImGuiCol_Text), "%s", done ? "[x]" : "[ ]"); + ImGui::SameLine(); + if (done) + ImGui::TextDisabled("%s", STR_C(steps[i])); + else + ImGui::TextWrapped("%s", STR_C(steps[i])); + } + ImGui::Separator(); + ImGui::TextDisabled("%s", STR_C("RMB held + W S A D: fly the camera, F: fly to the selected, F1: all the keys")); + if (ui()->mode() == nodebank_panel::TRACK && false == GfxRenderer->Mouse_Hit()) + ImGui::TextColored(ImVec4(1.0f, 0.7f, 0.2f, 1.0f), "%s", STR_C("No ground under the cursor: track goes onto the level 0 plane, drawn as a grid. Terrain: F5")); + ImGui::PopTextWrapPos(); + bool show{EditorSettings.first_steps()}; + if (ImGui::Checkbox(STR_C("Show when the editor opens"), &show)) + { + EditorSettings.first_steps(show); + EditorSettings.save(); + } + } + ImGui::End(); +} + void editor_mode::render_shortcuts_window() { if (false == m_shortcuts_open) @@ -4640,7 +4700,12 @@ void editor_mode::render_shortcuts_window() {"Shift+LMB", "Add a track to the selection, drag: a box"}, {"RMB on a track", "What can be done with it"}, {"K", "Split the selected track at the cursor"}, - {"Enter / Backspace", "Laying track: finish / take the last point back"}, + {"L, LMB, LMB...", "Lay track: each click lays a piece and goes on from its end"}, + {"Drag from a free end", "Lead a track out of it; onto another free end: join the two"}, + {"Ctrl+wheel", "Laying: radius"}, + {"Shift+wheel", "Laying: arc then straight, straight then arc, arc, straight; at a free end: another way to join"}, + {"0-9 while laying", "Type the length (or the radius) of the next piece, Enter lays it"}, + {"Enter / Backspace", "Laying track: lay up to the cursor / take the last piece back"}, {"Del", "Delete the selected tracks"}}}, {"Roads", {{"Esc", "Finish the road being built"}, @@ -5150,6 +5215,19 @@ void editor_mode::enter() WriteLog("Editor: camera at the first track, " + track->name()); break; } + if (false == m_saved_camera.has_value() && simulation::Paths.sequence().empty()) + { + std::vector found; + auto const ground{ground_heights({glm::dvec3{0.0, 5000.0, 0.0}}, true, &found).front()}; + Camera.Pos = glm::dvec3{0.0, (found.front() != 0 ? ground : 0.0) + 60.0, 60.0}; + Camera.Angle = glm::vec3(glm::radians(-45.0f), 0.0f, 0.0f); + Camera.m_owner = nullptr; + FreeFlyModeFlag = true; + Global.pCamera = Camera; + choose_work_area(work_area::tracks); + WriteLog("Editor: camera over the origin of the empty scenery"); + } + m_first_steps_open = EditorSettings.first_steps(); } Global.ControlPicking = true; @@ -5458,11 +5536,7 @@ void editor_mode::on_key(int const Key, int const Scancode, int const Action, in if (is_press(Action) && ui()->mode() == nodebank_panel::FILL && !m_fill_points.empty()) m_fill_points.pop_back(); if (is_press(Action) && ui()->mode() == nodebank_panel::TRACK && m_lay.active && !m_lay.points.empty()) - { - m_lay.points.pop_back(); - if (m_lay.points.empty()) - m_lay.start = {}; - } + lay_back(); break; case GLFW_KEY_ENTER: diff --git a/application/editormode.h b/application/editormode.h index c5676b4c..d33480e6 100644 --- a/application/editormode.h +++ b/application/editormode.h @@ -252,6 +252,11 @@ class editor_mode : public application_mode, private editor_track::observer void render_help_menu(); void render_shortcuts_window(); bool m_shortcuts_open{false}; + enum class first_step { tracks, lay, laid, wheel, join, turnout, undo, save, count }; + void render_first_steps(); + void first_step_done(first_step const Step); + bool m_first_steps_open{false}; + std::array(first_step::count)> m_first_steps_done{}; // deletes what's selected: a road piece or point, a signal, an include of a scenery template, a model, tracks (Del) void delete_selected(); void delete_model(TAnimModel *Model, bool const Joined); @@ -1092,8 +1097,17 @@ class editor_mode : public application_mode, private editor_track::observer bool copy_selected{false}; editor_track::path_style style; std::string status; + double status_time{-100.0}; + struct step + { + editor_track::snap_target start; + glm::dvec3 point{0.0}; + }; + std::vector chain; }; lay_tool m_lay; + void lay_after_history(bool const Undo); + void lay_back(); void render_lay_ui(); void render_lay_status(); void lay_click(); diff --git a/application/editormodeselftest.cpp b/application/editormodeselftest.cpp index 999d74b8..6dbb1750 100644 --- a/application/editormodeselftest.cpp +++ b/application/editormodeselftest.cpp @@ -1711,6 +1711,43 @@ editor_mode::selftest_result editor_mode::selftest_join(std::string const &comma WriteLog(format("SELFTEST %s end: %.3f m from the target", command.c_str(), geometry::plan_distance(previous, b))); return selftest_result::handled; } + if (command == "laychain") + { + int layout{0}; + words >> layout >> m_lay.radius; + m_track_layout = static_cast(std::clamp(layout, 0, 3)); + m_lay.active = true; + lay_cancel(); + auto const report = [&](std::string const &Step) { + auto const *start{m_lay.start.track}; + WriteLog(format("SELFTEST laychain %s: chain %zu, points %zu, start %s%s at %.2f %.2f, history %zu, status %s", Step.c_str(), m_lay.chain.size(), m_lay.points.size(), start != nullptr ? start->name().c_str() : "-", start != nullptr && start->m_editorremoved ? " (removed)" : "", + m_lay.points.empty() ? 0.0 : m_lay.points.front().x, m_lay.points.empty() ? 0.0 : m_lay.points.front().z, m_history.size(), m_lay.status.c_str())); + }; + for (std::string word; words >> word;) + { + if (word == "b") + lay_back(); + else if (word == "u") + undo_last(); + else if (word == "r") + redo_last(); + else + { + glm::dvec3 point{std::strtod(word.c_str(), nullptr), 5000.0, 0.0}; + words >> point.z; + std::vector found; + auto const ground{ground_heights({point}, true, &found)}; + point.y = found[0] != 0 ? ground[0] : m_lay.points.empty() ? 0.0 : m_lay.points.back().y; + m_lay.mouse_snap = editor_track::find_free_end(nullptr, 1, point, 3.0, {}); + m_lay.mouse = m_lay.mouse_snap.track != nullptr ? point : lay_bed(point); + lay_click(); + word += format(" %.1f", point.z); + } + report(word); + } + lay_cancel(); + return selftest_result::handled; + } if (command == "layclicks") { int layout{0}; diff --git a/application/editormodetrack.cpp b/application/editormodetrack.cpp index 9d1ac2b7..16e4956d 100644 --- a/application/editormodetrack.cpp +++ b/application/editormodetrack.cpp @@ -77,6 +77,27 @@ double piece_plan_radius(segment_data const &Path) return curve.plan_length() / turn; } +std::string lines_of(std::vector const &Texts) +{ + std::string text; + for (auto const &line : Texts) + text += (text.empty() ? "" : "\n") + line; + return text; +} + +std::string narrowed(std::vector const &Pieces, editor_track::layout const Layout, double const Radius) +{ + if (Layout != editor_track::layout::arc_straight && Layout != editor_track::layout::straight_arc) + return {}; + double sharpest{0.0}; + for (auto const &piece : Pieces) + if (auto const radius{piece_plan_radius(piece)}; radius >= 0.5 && (sharpest == 0.0 || radius < sharpest)) + sharpest = radius; + if (sharpest == 0.0 || sharpest >= Radius * 0.99) + return {}; + return format(STR_C("R %.0f m doesn't reach the cursor, the arc goes through it with R %.0f m"), Radius, sharpest); +} + std::string describe(editor_track::straight const &Line) { return format("L %.2f m az %.4f deg i %.2f per mille %zu paths", Line.length, Line.azimuth, Line.grade * 1000.0, Line.tracks.size()); @@ -489,6 +510,7 @@ void editor_mode::restore_track_snapshot(EditorSnapshot const &Snapshot, std::ve } } + lay_after_history(Undo); m_track_snap = {}; m_track_dirty = false; m_track_point = {}; @@ -531,6 +553,7 @@ void editor_mode::show_track_tab(track_tab const Tab) m_straights.tool = 0; m_lay.points.clear(); m_lay.start = {}; + m_lay.chain.clear(); m_lay.preview.clear(); m_lay.status.clear(); m_lay.active = Tab == track_tab::lay; @@ -614,7 +637,7 @@ std::string editor_mode::track_mode_name() const return STR_C("Select: LMB on a path"); switch (m_track_tab) { - case track_tab::lay: return m_extend.active ? STR_C("Laying track: new path from the free end") : STR_C("Laying track: LMB adds the points"); + case track_tab::lay: return m_extend.active ? STR_C("Laying track: new path from the free end") : STR_C("Laying track: each click lays a piece, Esc ends"); case track_tab::turnout: if (m_switch.placing) return STR_C("Switch: drag to set its direction and side"); @@ -3636,7 +3659,7 @@ std::vector editor_mode::track_key_hints(bool const All) hints = {{"LMB", chained ? "lay the shape up to here and go on from its end" : "lay the straight up to here and go on from its end"}, {"LMB on a free end", "join and lay"}}; if (chained || m_lay.mouse_snap.track != nullptr) hints.insert(hints.end(), {{"Ctrl+wheel", "radius"}, {"Shift+wheel", "arc / straight, at a free end: another shape"}}); - hints.insert(hints.end(), {{"Enter", "lay up to the cursor"}, {"Backspace", "take back the point"}, {"Esc", "cancel"}}); + hints.insert(hints.end(), {{"Enter", "lay up to the cursor"}, {"Backspace", m_lay.chain.empty() ? "take back the point" : "take back the last piece"}, {"Esc", "cancel"}}); } } else if (false == m_straights.detour.empty()) @@ -3821,6 +3844,8 @@ std::string editor_mode::track_readout() const text += "\n" + lay.preview_error; if (lay.points.size() == 1 && (lay.start.track != nullptr || lay.mouse_snap.track != nullptr)) text += "\n" + track_shaping(lay.mouse_snap.track != nullptr && lay.start.track != nullptr); + if (false == lay.status.empty() && ImGui::GetTime() - lay.status_time < 4.0) + text += "\n" + lay.status; return text; } auto const &state{m_straights}; @@ -4449,7 +4474,13 @@ std::vector editor_mode::join_pieces(TTrack const *From, editor_tr result = std::move(*eased); } Length = result.length; - Error = result.kind == alignment::join_kind::bezier ? std::string{STR_C("No curve fits between the ends, they are joined with a free curve")} : std::string{}; + std::vector notes; + for (auto const &warning : result.warnings) + if (m_join_radius > 0.0 || warning.find("R reduced") == std::string::npos) + notes.push_back(warning); + if (result.kind == alignment::join_kind::bezier) + notes.insert(notes.begin(), STR_C("No curve fits between the ends, they are joined with a free curve")); + Error = lines_of(notes); m_join_last = result; return result.pieces; } @@ -4514,6 +4545,9 @@ void editor_mode::finish_extend() editor_track::commit(created); digitize_drawn(created); push_track_snapshot({}, created); + first_step_done(first_step::laid); + if (target.track != nullptr) + first_step_done(first_step::join); m_node = created.back(); ui()->set_node(m_node); straight_refresh(); @@ -4621,6 +4655,7 @@ bool editor_mode::track_wheel(double const Offset) return *radius; return radii[0]; }; + first_step_done(first_step::wheel); if (io.KeyCtrl) { if (false == joined) @@ -4662,6 +4697,7 @@ std::vector editor_mode::lay_pieces(std::vector const { auto const grade{-grade_of(tool.start.direction)}; pieces = editor_track::layout_pieces(m_track_layout, Points[0], glm::normalize(leaving), grade, Points[1], tool.radius); + Error = narrowed(pieces, m_track_layout, tool.radius); follow_ground(pieces, Points[0].y, grade, Points[1].y, std::numeric_limits::quiet_NaN(), tool.start.track); } else if (End.track == nullptr) @@ -4675,6 +4711,7 @@ std::vector editor_mode::lay_pieces(std::vector const auto const back{editor_track::layout_pieces(m_track_layout, End.position, glm::normalize(-plan_of(End.direction)), -grade_of(End.direction), Points[0], tool.radius)}; for (auto piece{back.rbegin()}; piece != back.rend(); ++piece) pieces.push_back(reversed(*piece)); + Error = narrowed(pieces, m_track_layout, tool.radius); follow_ground(pieces, Points[0].y, std::numeric_limits::quiet_NaN(), End.position.y, grade_of(End.direction), End.track); } for (auto const &piece : pieces) @@ -4697,7 +4734,7 @@ std::vector editor_mode::lay_pieces(std::vector const Error = shape.errors.empty() ? std::string{STR_C("The track can't be laid through these points")} : shape.errors.front(); return {}; } - Error = shape.warnings.empty() ? std::string{} : shape.warnings.front(); + Error = lines_of(shape.warnings); auto const count = [&](alignment::result const &Shape, alignment::design const &Design) { return std::max(alignment::minimum_pieces(Shape, Design), static_cast(std::ceil(Shape.length / std::max(5.0, tool.piece_length)))); }; @@ -4734,7 +4771,7 @@ std::vector editor_mode::lay_pieces(std::vector const if (easedshape.valid) { Length = editor_track::eased_length(in, easedshape.length, out); - Error = easedshape.warnings.empty() ? std::string{} : easedshape.warnings.front(); + Error = lines_of(easedshape.warnings); return editor_track::eased_pieces(in, alignment::pieces(easedshape, easeddesign, count(easedshape, easeddesign)), out); } Error = STR("The curvature changes abruptly where the track joins, there's no room for a transition curve") + (easedshape.errors.empty() ? std::string{} : ": " + easedshape.errors.front()); @@ -4803,6 +4840,7 @@ void editor_mode::lay_click() } if (tool.points.empty()) { + tool.chain.clear(); tool.start = snap; tool.points.push_back(snap.track != nullptr ? snap.position : tool.mouse); return; @@ -4813,6 +4851,7 @@ void editor_mode::lay_click() m_typed.clear(); if (tool.points.size() == 1) { + lay_tool::step const previous{tool.start, tool.points.front()}; tool.points.push_back(tool.mouse); lay_finish({}); if (false == tool.points.empty()) @@ -4830,6 +4869,7 @@ void editor_mode::lay_click() next.position = path.points[segment_data::point::end]; auto const &control{path.points[segment_data::point::control2]}; next.direction = glm::normalize(control != glm::dvec3{} ? control : path.points[segment_data::point::start] - next.position); + tool.chain.push_back(previous); tool.start = next; tool.points = {next.position}; return; @@ -4843,6 +4883,7 @@ void editor_mode::lay_finish(editor_track::snap_target const &End) double length; std::string error; auto const pieces{lay_pieces(tool.points, End, length, error)}; + tool.status_time = ImGui::GetTime(); if (pieces.empty()) { tool.status = error.empty() ? std::string{STR_C("Click at least two points")} : error; @@ -4859,6 +4900,9 @@ void editor_mode::lay_finish(editor_track::snap_target const &End) editor_track::commit(created); digitize_drawn(created); push_track_snapshot({}, created); + first_step_done(first_step::laid); + if (End.track != nullptr) + first_step_done(first_step::join); tool.status = format(STR_C("Laid %zu paths, %.1f m"), created.size(), length); if (false == error.empty()) tool.status += "\n" + error; @@ -4921,9 +4965,49 @@ void editor_mode::lay_cancel() auto &tool{m_lay}; tool.points.clear(); tool.start = {}; + tool.chain.clear(); tool.preview.clear(); } +void editor_mode::lay_after_history(bool const Undo) +{ + auto &tool{m_lay}; + if (tool.points.empty()) + return; + if (false == Undo) + { + lay_cancel(); + return; + } + while (tool.start.track != nullptr && tool.start.track->m_editorremoved) + { + if (tool.chain.empty()) + { + lay_cancel(); + return; + } + tool.start = tool.chain.back().start; + tool.points = {tool.chain.back().point}; + tool.chain.pop_back(); + } +} + +void editor_mode::lay_back() +{ + auto &tool{m_lay}; + if (false == tool.chain.empty()) + { + undo_last(); + tool.status = STR_C("The last piece was taken back"); + tool.status_time = ImGui::GetTime(); + return; + } + if (false == tool.points.empty()) + tool.points.pop_back(); + if (tool.points.empty()) + tool.start = {}; +} + void editor_mode::render_lay_ui() { auto &tool{m_lay}; @@ -5136,8 +5220,8 @@ void editor_mode::switch_placed(std::vector const &Created) if (found == Created.end()) return; auto const slip{std::count_if(Created.begin(), Created.end(), [](TTrack const *Track) { return Track != nullptr && Track->eType == tt_Switch; }) > 1}; - m_switch.armed = -1; m_switch.placing = false; + first_step_done(first_step::turnout); m_track_point = {}; m_node = *found; ui()->set_node(m_node); @@ -5319,6 +5403,27 @@ void editor_mode::draw_build_overlay() const previous = next; } }; + if (false == GfxRenderer->Mouse_Hit() && false == ImGui::GetIO().WantCaptureMouse) + { + auto const centre{m_lay.active ? m_lay.mouse : cursor_ground()}; + double const cell{10.0}; + int const reach{10}; + glm::dvec3 const origin{std::round(centre.x / cell) * cell, centre.y, std::round(centre.z / cell) * cell}; + for (int i = -reach; i <= reach; ++i) + { + auto const major{std::fmod(std::abs(origin.x / cell + i), 10.0) < 0.5}; + auto const colour{major ? IM_COL32(255, 255, 255, 70) : IM_COL32(255, 255, 255, 35)}; + for (int j = -reach; j < reach; ++j) + projection.line(drawlist, origin + glm::dvec3{i * cell, 0.0, j * cell}, origin + glm::dvec3{i * cell, 0.0, (j + 1) * cell}, colour, 1.0f); + } + for (int j = -reach; j <= reach; ++j) + { + auto const major{std::fmod(std::abs(origin.z / cell + j), 10.0) < 0.5}; + auto const colour{major ? IM_COL32(255, 255, 255, 70) : IM_COL32(255, 255, 255, 35)}; + for (int i = -reach; i < reach; ++i) + projection.line(drawlist, origin + glm::dvec3{i * cell, 0.0, j * cell}, origin + glm::dvec3{(i + 1) * cell, 0.0, j * cell}, colour, 1.0f); + } + } auto const &display{ImGui::GetIO().DisplaySize}; for (auto const *track : simulation::Paths.sequence()) { @@ -5377,7 +5482,7 @@ void editor_mode::draw_build_overlay() const auto const &lay{m_lay}; auto const piece_colour = [&](segment_data const &Path) { if (false == lay.preview_error.empty()) - return overlay_color::invalid; + return overlay_color::warning; return piece_plan_radius(Path) < 0.5 ? IM_COL32(255, 255, 255, 230) : overlay_color::marked; }; for (auto const &piece : lay.preview) diff --git a/application/editormodetrackux.cpp b/application/editormodetrackux.cpp index ecde52e7..11ae4ac3 100644 --- a/application/editormodetrackux.cpp +++ b/application/editormodetrackux.cpp @@ -216,7 +216,7 @@ editor_mode::track_intent editor_mode::track_intent_at(int const Mods) return make(kind::lay_end, lay.points.empty() ? "Click: start from this free end, drag: lead a track out of it" : "Click: join this end and lay"); } intent.position = lay.mouse; - return make(kind::lay, lay.points.empty() ? "Click: start of the new track" : "Click: next point"); + return make(kind::lay, lay.points.empty() ? "Click: start of the new track" : "Click: lay the track to here"); } if (m_track_tab == track_tab::turntable) return turntable_intent(ground, shift); @@ -369,7 +369,7 @@ editor_mode::track_intent editor_mode::track_intent_at(int const Mods) { case track_tab::straights: return make(kind::select, "Click: the whole straight through this track"); case track_tab::route: return make(kind::select, "Click: the curve of this track with its straights"); - case track_tab::turnout: return make(kind::select, m_hover.track->eType == tt_Switch ? "Click: edit this switch" : m_switch.armed < 0 ? "Click: choose this track, then the type of the switch" : "Click: put a switch into this track"); + case track_tab::turnout: return make(kind::select, m_hover.track->eType == tt_Switch ? "Click: edit this switch" : m_switch.armed < 0 ? "Click: choose this track, then the type of the switch" : "Click: choose this track, then press and drag on it for the switch"); case track_tab::profile: return make(kind::select, "Click: the grade line through this track"); case track_tab::lineside: return make(kind::select, "Click: the line the objects go along"); default: return make(kind::select, "Click: select"); diff --git a/application/editorprojection.h b/application/editorprojection.h index ba3accb8..3a5e29e5 100644 --- a/application/editorprojection.h +++ b/application/editorprojection.h @@ -23,6 +23,7 @@ ImU32 constexpr marked{IM_COL32(255, 210, 60, 255)}; ImU32 constexpr grip{IM_COL32(60, 230, 90, 255)}; ImU32 constexpr selected{IM_COL32(40, 220, 255, 255)}; ImU32 constexpr invalid{IM_COL32(255, 60, 60, 255)}; +ImU32 constexpr warning{IM_COL32(255, 140, 40, 255)}; ImU32 constexpr highlight{IM_COL32(255, 255, 255, 255)}; } // namespace overlay_color diff --git a/editor/editorSettings.cpp b/editor/editorSettings.cpp index 4b242132..0623e454 100644 --- a/editor/editorSettings.cpp +++ b/editor/editorSettings.cpp @@ -102,6 +102,8 @@ bool editorSettings::load() parse(value, m_nodebank_card); else if (key == "colours") parse(value, m_palette); + else if (key == "first_steps") + m_first_steps = (value == "1"); else if (key.rfind(origin_prefix, 0) == 0) { // northing;easting @@ -145,6 +147,7 @@ bool editorSettings::save() stream << "nodebank_previews " << (m_nodebank_previews ? 1 : 0) << "\n"; stream << "nodebank_card " << m_nodebank_card << "\n"; stream << "colours " << m_palette << "\n"; + stream << "first_steps " << (m_first_steps ? 1 : 0) << "\n"; stream << std::fixed << std::setprecision(3); for (auto const &origin : m_orthophoto_origins) stream << origin.first << " " << origin.second.first << ";" << origin.second.second << "\n"; diff --git a/editor/editorSettings.hpp b/editor/editorSettings.hpp index 7bfb1d36..6b87d11b 100644 --- a/editor/editorSettings.hpp +++ b/editor/editorSettings.hpp @@ -54,6 +54,9 @@ public: int palette() const { return m_palette; } void palette(int Palette) { m_palette = Palette; } + bool first_steps() const { return m_first_steps; } + void first_steps(bool Show) { m_first_steps = Show; } + private: movement_scheme m_movement{movement_scheme::wsad}; orthophoto_settings m_orthophoto; @@ -63,6 +66,7 @@ private: bool m_nodebank_previews{true}; int m_nodebank_card{96}; int m_palette{0}; + bool m_first_steps{true}; }; // global editor settings instance diff --git a/lang/editor_pl.po b/lang/editor_pl.po index 7f9d77f9..b9689cce 100644 --- a/lang/editor_pl.po +++ b/lang/editor_pl.po @@ -29,8 +29,8 @@ msgstr "Pokaż inspektor" msgid "The curve can't be read from this path, edit it as a single path" msgstr "Z tego odcinka nie da się odczytać łuku, edytuj go jako pojedynczy odcinek" -msgid "Laying track: LMB adds the points" -msgstr "Układanie toru: LPM dodaje punkty" +msgid "Laying track: each click lays a piece, Esc ends" +msgstr "Układanie toru: każdy klik kładzie odcinek, Esc kończy" msgid "Switch: drag to set its direction and side" msgstr "Rozjazd: przeciągnij, aby ustawić kierunek i stronę" @@ -2835,8 +2835,8 @@ msgstr "Klik: dołącz do tego końca i ułóż" msgid "Click: start of the new track" msgstr "Klik: początek nowego toru" -msgid "Click: next point" -msgstr "Klik: następny punkt" +msgid "Click: lay the track to here" +msgstr "Klik: połóż tor do tego miejsca" msgid "Press: where the model starts, drag to its end" msgstr "Wciśnij: początek modelu, przeciągnij do jego końca" @@ -2901,8 +2901,8 @@ msgstr "Klik: edytuj ten rozjazd" msgid "Click: choose this track, then the type of the switch" msgstr "Klik: wybierz ten tor, potem typ rozjazdu" -msgid "Click: put a switch into this track" -msgstr "Klik: wstaw rozjazd w ten tor" +msgid "Click: choose this track, then press and drag on it for the switch" +msgstr "Klik: wybierz ten tor, potem wciśnij i przeciągnij na nim, by wstawić rozjazd" msgid "Click: the grade line through this track" msgstr "Klik: niweleta przez ten tor" @@ -4948,8 +4948,8 @@ msgstr "Skróty klawiszowe" msgid "Lay track / switch / turntable" msgstr "Układanie toru / rozjazd / obrotnica" -msgid "Laying track: finish / take the last point back" -msgstr "Układanie toru: zakończ / cofnij ostatni punkt" +msgid "Laying track: lay up to the cursor / take the last piece back" +msgstr "Układanie toru: połóż do kursora / cofnij ostatni odcinek" msgid "Leave the editor" msgstr "Wyjście z edytora" @@ -5487,3 +5487,75 @@ msgstr ", ostrzej niż rozjazd (R %.0f m)" msgid ", the switch moved %.2f m" msgstr ", rozjazd przesunięty o %.2f m" + +msgid "take back the last piece" +msgstr "cofnij ostatni odcinek" + +msgid "The last piece was taken back" +msgstr "Ostatni odcinek cofnięty" + +msgid "R %.0f m doesn't reach the cursor, the arc goes through it with R %.0f m" +msgstr "R %.0f m nie sięga kursora, łuk przechodzi przez niego z R %.0f m" + +msgid "First steps" +msgstr "Pierwsze kroki" + +msgid "Laying the first track, step by step" +msgstr "Pierwszy tor krok po kroku" + +msgid "The ticks fill in by themselves, the window doesn't stop anything" +msgstr "Kroki odhaczają się same, okno niczego nie blokuje" + +msgid "F3: the Tracks field of work" +msgstr "F3: dział Tory" + +msgid "L: lay track" +msgstr "L: układanie toru" + +msgid "Click on the ground, then once more: a straight. Each next click lays a piece and goes on from its end, Esc ends" +msgstr "Kliknij na terenie, potem jeszcze raz: prosta. Każdy kolejny klik kładzie odcinek i ciągnie tor dalej z jego końca, Esc kończy" + +msgid "While laying on: Ctrl+wheel changes the radius, Shift+wheel the shape (arc then straight, straight then arc, arc, straight). The plain wheel zooms" +msgstr "W trakcie układania: Ctrl+kółko zmienia promień, Shift+kółko kształt (łuk i prosta, prosta i łuk, łuk, prosta). Samo kółko przybliża widok" + +msgid "Joining: press on a free end (magenta ring) and drag onto another free end" +msgstr "Łączenie: wciśnij na wolnym końcu (różowy pierścień) i przeciągnij na inny wolny koniec" + +msgid "T: switch. Click a straight, then press on it and drag: along sets the direction, sideways the side" +msgstr "T: rozjazd. Kliknij prostą, potem wciśnij na niej i przeciągnij: wzdłuż ustala kierunek, w bok stronę" + +msgid "Ctrl+Z takes a step back, Backspace while laying takes back the last piece" +msgstr "Ctrl+Z cofa krok, Backspace w trakcie układania cofa ostatni odcinek" + +msgid "Ctrl+S saves the scenery" +msgstr "Ctrl+S zapisuje scenerię" + +msgid "RMB held + W S A D: fly the camera, F: fly to the selected, F1: all the keys" +msgstr "Wciśnięty PPM + W S A D: lot kamerą, F: leć do zaznaczonego, F1: wszystkie klawisze" + +msgid "No ground under the cursor: track goes onto the level 0 plane, drawn as a grid. Terrain: F5" +msgstr "Pod kursorem nie ma terenu: tor trafia na płaszczyznę na wysokości 0, widoczną jako siatka. Teren: F5" + +msgid "Show when the editor opens" +msgstr "Pokazuj po otwarciu edytora" + +msgid "L, LMB, LMB..." +msgstr "L, LPM, LPM..." + +msgid "Lay track: each click lays a piece and goes on from its end" +msgstr "Układanie toru: każdy klik kładzie odcinek i ciągnie tor dalej z jego końca" + +msgid "Lead a track out of it; onto another free end: join the two" +msgstr "Wyprowadza z niego tor; na inny wolny koniec: łączy oba" + +msgid "Laying: radius" +msgstr "Układanie: promień" + +msgid "Laying: arc then straight, straight then arc, arc, straight; at a free end: another way to join" +msgstr "Układanie: łuk i prosta, prosta i łuk, łuk, prosta; na wolnym końcu: inny sposób połączenia" + +msgid "0-9 while laying" +msgstr "0-9 w trakcie układania" + +msgid "Type the length (or the radius) of the next piece, Enter lays it" +msgstr "Wpisz długość (lub promień) następnego odcinka, Enter go kładzie"