diff --git a/application/editormode.cpp b/application/editormode.cpp index db791846..3eb0a454 100644 --- a/application/editormode.cpp +++ b/application/editormode.cpp @@ -5301,7 +5301,12 @@ void editor_mode::render_include_gizmo(glm::mat4 const &View, glm::mat4 const &P glm::vec3 const snapvalue(translating ? m_gizmo_snap : 5.0f); float const *snap = Global.ctrlState && snapvalue.x > 0.0f ? glm::value_ptr(snapvalue) : nullptr; + // in the signal tool a press on the mast drags the signal along its track: the gizmo, whose up axis runs along the + // mast, gives way over a signal and while one is dragged + auto const yield{ui()->mode() == nodebank_panel::TRACK && m_track_tab == track_tab::signals && false == ImGuizmo::IsUsing() && (m_signal.moving || m_signal.placing || signal_hit() >= 0)}; + ImGuizmo::Enable(false == yield); ImGuizmo::Manipulate(glm::value_ptr(View), glm::value_ptr(Projection), static_cast(operation), mode, glm::value_ptr(matrix), nullptr, snap); + ImGuizmo::Enable(true); if (false == ImGuizmo::IsUsing()) return; diff --git a/application/editormodesignal.cpp b/application/editormodesignal.cpp index 53840611..ab7b211a 100644 --- a/application/editormodesignal.cpp +++ b/application/editormodesignal.cpp @@ -473,7 +473,23 @@ void editor_mode::signal_move_update() if (false == dragged) return; auto const found{std::find_if(tool.standing.begin(), tool.standing.end(), [&](signal_standing const &Standing) { return Standing.include.instance == tool.selected; })}; - if (found == tool.standing.end() || m_hover.track == nullptr) + if (found == tool.standing.end()) + { + tool.moved_valid = false; + return; + } + // the track under the cursor; off every track, as it is when the signal is dragged beside its track, the place of the + // track the signal stands by nearest the cursor + auto *track{m_hover.track}; + auto path{m_hover.path}; + auto point{m_hover.point}; + if (track == nullptr && found->track != nullptr && false == found->track->m_editorremoved && false == found->track->m_paths.empty()) + { + track = found->track; + path = std::clamp(found->spot.path, 0, static_cast(track->m_paths.size()) - 1); + point = bezier{track->m_paths[path]}.point(nearest_on(*track, path, cursor_level(found->spot.axis.y))); + } + if (track == nullptr) { tool.moved_valid = false; return; @@ -481,13 +497,14 @@ void editor_mode::signal_move_update() auto const *item{signal_template_of(found->include.file)}; if (item == nullptr) return; + path = std::clamp(path, 0, static_cast(track->m_paths.size()) - 1); auto const yaw{glm::radians(found->include.yaw + 180.0)}; glm::dvec2 const travel{std::sin(yaw), std::cos(yaw)}; - auto const parameter{nearest_on(*m_hover.track, std::clamp(m_hover.path, 0, static_cast(m_hover.track->m_paths.size()) - 1), m_hover.point)}; - auto const forward{forward_at(*m_hover.track, std::clamp(m_hover.path, 0, static_cast(m_hover.track->m_paths.size()) - 1), parameter)}; + auto const parameter{nearest_on(*track, path, point)}; + auto const forward{forward_at(*track, path, parameter)}; auto const facing{glm::dot(forward, travel) >= 0.0 ? 1 : -1}; - auto const sideoftravel{glm::dot(plan_of(found->include.location) - plan_of(m_hover.point), right_of(travel)) >= 0.0 ? 1 : -1}; - tool.moved_valid = signal_spot_at(*m_hover.track, m_hover.path, m_hover.point, m_hover.point, facing, sideoftravel * facing, *item, tool.moved); + auto const sideoftravel{glm::dot(plan_of(found->include.location) - plan_of(point), right_of(travel)) >= 0.0 ? 1 : -1}; + tool.moved_valid = signal_spot_at(*track, path, point, point, facing, sideoftravel * facing, *item, tool.moved); } void editor_mode::signal_release() @@ -504,12 +521,22 @@ void editor_mode::signal_release() return; tool.moving = false; auto const &mouse{ImGui::GetIO().MousePos}; - if (false == tool.moved_valid || glm::length(glm::vec2{mouse.x, mouse.y} - tool.pressed) <= kDragStart) + if (glm::length(glm::vec2{mouse.x, mouse.y} - tool.pressed) <= kDragStart) return; + if (false == tool.moved_valid) + { + tool.status = STR_C("Drop the signal by a track"); + return; + } tool.moved_valid = false; auto const found{std::find_if(tool.standing.begin(), tool.standing.end(), [&](signal_standing const &Standing) { return Standing.include.instance == tool.selected; })}; - if (found == tool.standing.end() || false == scene::Layers.tracked(found->include.instance) || false == scene::Layers.removable(found->include.instance)) + if (found == tool.standing.end()) return; + if (false == scene::Layers.tracked(found->include.instance) || false == scene::Layers.removable(found->include.instance)) + { + tool.status = STR_C("This signal is in a file the editor doesn't change, it can't be moved"); + return; + } auto const &spot{tool.moved}; auto const &included{scene::Layers.instance(found->include.instance)}; auto values{found->include.values}; diff --git a/editor/editorIncludeInfo.cpp b/editor/editorIncludeInfo.cpp index f613323c..62ec5a63 100644 --- a/editor/editorIncludeInfo.cpp +++ b/editor/editorIncludeInfo.cpp @@ -129,6 +129,14 @@ bool read_template(std::string const &File, template_text &Text, std::string &Er } linebegin = next; } + // a template without a description of its own may have one beside the game data, in editor/templates, where the + // scripts of tools/editor write it with --sidecar: a check of the game files doesn't take it away + if (Text.yaml.empty()) + { + std::ifstream beside{"editor/templates/" + File, std::ios_base::binary}; + if (beside.is_open()) + Text.yaml.assign(std::istreambuf_iterator(beside), std::istreambuf_iterator()); + } if (false == is_utf8(Text.yaml, extended)) { Text.yaml = win1250_to_utf8(Text.yaml); @@ -752,7 +760,7 @@ include_entry examine(std::string const &File) { return entry; } - entry.described = false == text.lines.empty(); + entry.described = false == text.yaml.empty(); if (false == entry.described) { entry.issue = "the template has no description"; diff --git a/lang/editor_pl.po b/lang/editor_pl.po index 13de5644..7dca57ac 100644 --- a/lang/editor_pl.po +++ b/lang/editor_pl.po @@ -7052,3 +7052,9 @@ msgstr "Klik: przesuń ten W17 na miejsce" msgid "Click: put the W17 here" msgstr "Klik: postaw tu W17" + +msgid "Drop the signal by a track" +msgstr "Upuść sygnalizator przy torze" + +msgid "This signal is in a file the editor doesn't change, it can't be moved" +msgstr "Ten sygnalizator jest w pliku, którego edytor nie zmienia, nie da się go przesunąć" diff --git a/tools/editor/drive_headers.py b/tools/editor/drive_headers.py index 16af94fb..ba6206ce 100644 --- a/tools/editor/drive_headers.py +++ b/tools/editor/drive_headers.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 """Writes //$e descriptions into the switch drive templates (scenery/tory/napedy), so the editor places them with a switch. -usage: drive_headers.py [--write] [--zip ] +usage: drive_headers.py [--write] [--zip ] [--sidecar] without --write it only reports what it would write; --zip packs the written templates with their scenery/ paths """ @@ -9,7 +9,7 @@ import os import re import sys -from editor_headers import body_lines, decode, load, options, pack, write +from editor_headers import body_lines, decode, load, options, pack, sidecar, write FOLDER = 'scenery/tory/napedy' PATTERN = re.compile(r'^(ele1?|mech[12]?)-rz([lp])-([lp])((?:_[a-z]+)*)\.inc$') @@ -78,6 +78,8 @@ def main(): print('%d described, %d skipped' % (len(done), len(skipped))) if writing: write(data, FOLDER, 'drives', done) + if '--sidecar' in sys.argv: + sidecar(data, FOLDER, done) if target: pack(target, FOLDER, done) return 0 diff --git a/tools/editor/editor_headers.py b/tools/editor/editor_headers.py index 54b6e720..d50da789 100644 --- a/tools/editor/editor_headers.py +++ b/tools/editor/editor_headers.py @@ -50,6 +50,23 @@ def options(argv, usage): return argv[1], '--write' in argv, pack +def sidecar(data, folder, entries): + """the descriptions next to the game data instead of in the templates: /editor/templates/, the lines without the marks, which the editor reads for a template without its own; + the templates stay as they are, so a check of the game files by the store doesn't take the descriptions away""" + root = os.path.join(data, 'editor', 'templates', os.path.relpath(folder, 'scenery')) + written = 0 + for file, info in entries: + if info.get('header') is None: + continue + target = os.path.normpath(os.path.join(root, file)) + os.makedirs(os.path.dirname(target), exist_ok=True) # NOSONAR + with open(target, 'w', encoding='utf-8', newline='\n') as out: # NOSONAR + out.write('\n'.join(info['header']) + '\n') + written += 1 + print('%d descriptions written beside the data in %s' % (written, root)) + + def write(data, folder, name, entries): backup = os.path.join(data, 'editor_backup') os.makedirs(backup, exist_ok=True) # NOSONAR diff --git a/tools/editor/signal_headers.py b/tools/editor/signal_headers.py index 63aacf75..fdb3a9e2 100644 --- a/tools/editor/signal_headers.py +++ b/tools/editor/signal_headers.py @@ -1,15 +1,16 @@ #!/usr/bin/env python3 """Writes //$e descriptions into the signal templates of the scenery directory, so the editor can place them. -usage: signal_headers.py [--write] -without --write it only reports what it would write +usage: signal_headers.py [--write] [--sidecar] +without --write it only reports what it would write; --sidecar writes them beside the data, in editor/templates, the +templates left as they are """ import os import re import sys -from editor_headers import body_lines, decode, load, write +from editor_headers import body_lines, decode, load, sidecar, write FAMILIES = [ (re.compile(r'^ss[dow]?\d'), 'main'), @@ -200,6 +201,8 @@ def main(): if writing: write(sys.argv[1], 'scenery', 'signals', done) + if '--sidecar' in sys.argv: + sidecar(sys.argv[1], 'scenery', done) return 0 diff --git a/tools/editor/turntable_headers.py b/tools/editor/turntable_headers.py index 891ad432..c14cc2e4 100755 --- a/tools/editor/turntable_headers.py +++ b/tools/editor/turntable_headers.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 """Writes //$e descriptions into the turntable templates of the scenery, so the editor places them as turntables. -usage: turntable_headers.py [--write] [--zip ] +usage: turntable_headers.py [--write] [--zip ] [--sidecar] without --write it only reports what it would write; --zip packs every described turntable template with its scenery/ path """ @@ -9,7 +9,7 @@ import os import re import sys -from editor_headers import LINEMARK, body_lines, decode, load, options, pack, write +from editor_headers import LINEMARK, body_lines, decode, load, options, pack, sidecar, write FOLDER = 'scenery' ROTATION = re.compile(r'^event\s+\S*tor(\d+)rot\s+animation\s+\S+\s+\S+\s+rotate\s+\S+\s+\S+\s+\S+\s+(\S+)', re.I | re.M) @@ -95,6 +95,8 @@ def main(): write(data, FOLDER, 'turntables', fresh) if target: pack(target, FOLDER, done) + if '--sidecar' in sys.argv: + sidecar(data, FOLDER, done) return 0