mirror of
https://github.com/MaSzyna-EU07/maszyna.git
synced 2026-10-26 23:59:48 +01:00
fix(editor): a signal goes to the other side of the track and turns around
Dragging a standing signal over the track takes it to the other side; the side used to come from where the signal stood, so it never crossed. The selected signal gets "To the other side" and "Turn around": the first mirrors it over the axis facing the same trains, the second turns it to the trains of the other direction and moves its event to the other event list of the path. Either one swaps a head leaning to the track for its counterpart, and an include keeps the template its directive names, so undo brings the old file back too.
This commit is contained in:
@@ -3058,7 +3058,12 @@ void editor_mode::select_include(scene::instance_handle const Instance)
|
||||
|
||||
void editor_mode::set_include_directive(scene::instance_handle const Instance, std::string const &Directive)
|
||||
{
|
||||
if (false == scene::Layers.modify(Instance, Directive))
|
||||
std::string file;
|
||||
std::vector<std::string> values;
|
||||
if (scene::Layers.tracked(Instance) && editor_includes::parse_directive(Directive, file, values) && scene::Layers.instance(Instance).file != nullptr &&
|
||||
editor_includes::normalized(file) == editor_includes::normalized(*scene::Layers.instance(Instance).file))
|
||||
file.clear();
|
||||
if (false == scene::Layers.modify(Instance, Directive, file))
|
||||
return;
|
||||
std::vector<TAnimModel *> retired;
|
||||
simulation::State.rebuild_include(Instance, retired);
|
||||
|
||||
@@ -1796,8 +1796,6 @@ class editor_mode : public application_mode, private editor_track::observer, pri
|
||||
std::string read;
|
||||
TTrack *track{nullptr};
|
||||
int list{0};
|
||||
signal_spot spot;
|
||||
bool on_track{false};
|
||||
};
|
||||
struct signal_tool
|
||||
{
|
||||
@@ -1821,6 +1819,9 @@ class editor_mode : public application_mode, private editor_track::observer, pri
|
||||
int hover{-1};
|
||||
scene::instance_handle selected{0};
|
||||
bool moving{false};
|
||||
glm::dvec2 grab{0.0};
|
||||
signal_spot home;
|
||||
bool home_valid{false};
|
||||
signal_spot moved;
|
||||
bool moved_valid{false};
|
||||
std::string status;
|
||||
@@ -1836,7 +1837,12 @@ class editor_mode : public application_mode, private editor_track::observer, pri
|
||||
void signal_release();
|
||||
bool signal_place(signal_spot const &Spot, signal_template const &Template);
|
||||
void signal_move_update();
|
||||
void signal_move_finish();
|
||||
bool signal_standing_spot(signal_standing const &Standing, signal_spot &Spot) const;
|
||||
bool signal_drop_at(signal_standing const &Standing, TTrack &Track, int const Path, glm::dvec3 const &Point, int const Side, signal_spot &Spot) const;
|
||||
bool signal_move_to(signal_standing const &Standing, signal_spot const &Spot, char const *Read, char const *Unread);
|
||||
bool signal_flip(scene::instance_handle const Instance);
|
||||
bool signal_turn(scene::instance_handle const Instance);
|
||||
signal_standing const *signal_standing_of(scene::instance_handle const Instance) const;
|
||||
bool signal_delete(scene::instance_handle const Instance);
|
||||
void signal_detach_events(std::string const &Read, std::vector<std::pair<TTrack *, editor_track::state>> &States);
|
||||
void signal_renamed(std::string const &Before, std::string const &After, EditorSnapshot &Snapshot);
|
||||
|
||||
@@ -50,7 +50,7 @@ http://mozilla.org/MPL/2.0/.
|
||||
|
||||
editor_mode::selftest_result editor_mode::selftest_signal(std::string const &command, std::istringstream &words)
|
||||
{
|
||||
if (command == "signal" || command == "signalmove" || command == "signaldelete" || command == "signalrename" || command == "signalprobe")
|
||||
if (command == "signal" || command == "signalmove" || command == "signaldelete" || command == "signalrename" || command == "signalprobe" || command == "signalflip" || command == "signalturn")
|
||||
{
|
||||
auto values{selftest_values(words)};
|
||||
show_track_tab(track_tab::signals);
|
||||
@@ -107,29 +107,40 @@ editor_mode::selftest_result editor_mode::selftest_signal(std::string const &com
|
||||
else if (command == "signalmove")
|
||||
{
|
||||
auto const *standing{standing_named(values["name"])};
|
||||
auto const *item{standing != nullptr ? signal_template_of(standing->include.file) : nullptr};
|
||||
if (item == nullptr)
|
||||
auto *track{simulation::Paths.find(values["track"])};
|
||||
if (standing == nullptr || track == nullptr || track->m_paths.empty())
|
||||
{
|
||||
WriteLog("SELFTEST signalmove: no signal " + values["name"]);
|
||||
WriteLog("SELFTEST signalmove: no signal " + values["name"] + " or no track " + values["track"]);
|
||||
return selftest_result::handled;
|
||||
}
|
||||
auto const yaw{glm::radians(standing->include.yaw + 180.0)};
|
||||
glm::dvec2 const travel{std::sin(yaw), std::cos(yaw)};
|
||||
signal_spot probe;
|
||||
if (false == spot_on(*item, 1, 1, probe))
|
||||
auto const path{std::clamp(values.count("path") ? std::stoi(values["path"]) : 0, 0, static_cast<int>(track->m_paths.size()) - 1)};
|
||||
auto const at{values.count("at") ? std::stod(values["at"]) : 0.5};
|
||||
geometry::bezier const curve{track->m_paths[path]};
|
||||
auto point{curve.point(at)};
|
||||
auto const sided{values.count("side") != 0};
|
||||
auto side{values["side"] == "left" ? -1 : 1};
|
||||
if (values.count("off"))
|
||||
{
|
||||
WriteLog("SELFTEST signalmove: no such track " + values["track"]);
|
||||
return selftest_result::handled;
|
||||
auto const tangent{glm::normalize(glm::dvec2{curve.first(at).x, curve.first(at).z})};
|
||||
auto const off{std::stod(values["off"])};
|
||||
point += glm::dvec3{-tangent.y * off, 0.0, tangent.x * off};
|
||||
side = 0;
|
||||
}
|
||||
auto const facing{glm::dot(probe.travel, travel) >= 0.0 ? 1 : -1};
|
||||
auto const sideoftravel{glm::dot(geometry::plan_of(standing->include.location) - geometry::plan_of(standing->spot.axis), glm::dvec2{-travel.y, travel.x}) >= 0.0 ? 1 : -1};
|
||||
else if (signal_spot home; false == sided && signal_standing_spot(*standing, home))
|
||||
side = home.side * (home.list == 2 ? 1 : -1);
|
||||
signal_select(standing->include.instance);
|
||||
tool.moving = true;
|
||||
tool.moved_valid = spot_on(*item, facing, sideoftravel * facing, tool.moved);
|
||||
tool.moved_valid = signal_drop_at(*standing, *track, path, point, side, tool.moved);
|
||||
tool.pressed = {-1000.f, -1000.f};
|
||||
signal_release();
|
||||
WriteLog("SELFTEST signalmove: " + tool.status);
|
||||
}
|
||||
else if (command == "signalflip" || command == "signalturn")
|
||||
{
|
||||
auto const *standing{standing_named(values["name"])};
|
||||
auto const done{standing != nullptr && (command == "signalflip" ? signal_flip(standing->include.instance) : signal_turn(standing->include.instance))};
|
||||
WriteLog("SELFTEST " + command + " " + values["name"] + (done ? ": " : ": failed, ") + tool.status);
|
||||
}
|
||||
else if (command == "signaldelete")
|
||||
{
|
||||
auto const *standing{standing_named(values["name"])};
|
||||
|
||||
@@ -46,6 +46,7 @@ float const kSignalPick{12.0f};
|
||||
float const kDragStart{6.0f};
|
||||
double const kDragTravel{1.0};
|
||||
double const kRefreshPeriod{2.0};
|
||||
double const kSignalReach{10.0};
|
||||
|
||||
char const *const kKinds[] = {"main", "block", "warning", "shunt", "stop", "substitute", "repeater"};
|
||||
char const *const kKindLabels[] = {"Semaphore", "Automatic block", "Warning signal", "Shunting signal", "Stop signal", "Substitute signal", "Repeater"};
|
||||
@@ -88,6 +89,17 @@ glm::dvec2 right_of(glm::dvec2 const &Direction)
|
||||
return {-Direction.y, Direction.x};
|
||||
}
|
||||
|
||||
glm::dvec2 travel_of(double const Yaw)
|
||||
{
|
||||
auto const yaw{glm::radians(Yaw + 180.0)};
|
||||
return {std::sin(yaw), std::cos(yaw)};
|
||||
}
|
||||
|
||||
int travel_side(int const Side, int const List)
|
||||
{
|
||||
return Side * (List == 2 ? 1 : -1);
|
||||
}
|
||||
|
||||
int lean_wanted(int const Side)
|
||||
{
|
||||
return Side > 0 ? 1 : 2;
|
||||
@@ -430,9 +442,12 @@ void editor_mode::signal_press(track_intent const &Intent)
|
||||
{
|
||||
if (Intent.index < 0 || Intent.index >= static_cast<int>(tool.standing.size()))
|
||||
return;
|
||||
signal_select(tool.standing[Intent.index].include.instance);
|
||||
auto const &standing{tool.standing[Intent.index]};
|
||||
tool.grab = plan_of(standing.include.location) - plan_of(cursor_level(standing.include.location.y));
|
||||
tool.home_valid = signal_standing_spot(standing, tool.home);
|
||||
tool.moving = true;
|
||||
tool.moved_valid = false;
|
||||
signal_select(standing.include.instance);
|
||||
return;
|
||||
}
|
||||
auto const *armed{signal_armed()};
|
||||
@@ -473,39 +488,84 @@ void editor_mode::signal_move_update()
|
||||
return;
|
||||
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())
|
||||
auto const *found{signal_standing_of(tool.selected)};
|
||||
if (found == nullptr)
|
||||
{
|
||||
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 const &location{found->include.location};
|
||||
auto const cursor{cursor_level(location.y)};
|
||||
glm::dvec3 const held{cursor.x + tool.grab.x, location.y, cursor.z + tool.grab.y};
|
||||
// the track under the cursor; off every track, as it is when the signal is dragged beside its track, the track the
|
||||
// signal stands by
|
||||
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())
|
||||
if (track == nullptr && tool.home_valid)
|
||||
{
|
||||
track = found->track;
|
||||
path = std::clamp(found->spot.path, 0, static_cast<int>(track->m_paths.size()) - 1);
|
||||
point = bezier{track->m_paths[path]}.point(nearest_on(*track, path, cursor_level(found->spot.axis.y)));
|
||||
track = tool.home.track;
|
||||
path = tool.home.path;
|
||||
}
|
||||
tool.moved_valid = track != nullptr && signal_drop_at(*found, *track, path, held, 0, tool.moved);
|
||||
}
|
||||
|
||||
editor_mode::signal_standing const *editor_mode::signal_standing_of(scene::instance_handle const Instance) const
|
||||
{
|
||||
auto const &standing{m_signal.standing};
|
||||
auto const found{std::find_if(standing.begin(), standing.end(), [&](signal_standing const &Standing) { return Standing.include.instance == Instance; })};
|
||||
return found != standing.end() ? &*found : nullptr;
|
||||
}
|
||||
|
||||
bool editor_mode::signal_drop_at(signal_standing const &Standing, TTrack &Track, int const Path, glm::dvec3 const &Point, int const Side, signal_spot &Spot) const
|
||||
{
|
||||
auto const *item{signal_template_of(Standing.include.file)};
|
||||
if (item == nullptr || Track.m_paths.empty() || Track.m_editorremoved)
|
||||
return false;
|
||||
auto const path{std::clamp(Path, 0, static_cast<int>(Track.m_paths.size()) - 1)};
|
||||
auto const forward{forward_at(Track, path, nearest_on(Track, path, Point))};
|
||||
auto const facing{glm::dot(forward, travel_of(Standing.include.yaw)) >= 0.0 ? 1 : -1};
|
||||
return signal_spot_at(Track, path, Point, Point, facing, Side * facing, *item, Spot);
|
||||
}
|
||||
|
||||
bool editor_mode::signal_standing_spot(signal_standing const &Standing, signal_spot &Spot) const
|
||||
{
|
||||
auto const &location{Standing.include.location};
|
||||
TTrack *track{nullptr};
|
||||
int path{0};
|
||||
auto best{kSignalReach};
|
||||
auto const consider = [&](TTrack *Track) {
|
||||
if (Track == nullptr || Track->m_editorremoved)
|
||||
return;
|
||||
for (int p = 0; p < static_cast<int>(Track->m_paths.size()); ++p)
|
||||
{
|
||||
bezier const curve{Track->m_paths[p]};
|
||||
auto const low{glm::min(glm::min(plan_of(curve.p0), plan_of(curve.p1)), glm::min(plan_of(curve.p2), plan_of(curve.p3)))};
|
||||
auto const high{glm::max(glm::max(plan_of(curve.p0), plan_of(curve.p1)), glm::max(plan_of(curve.p2), plan_of(curve.p3)))};
|
||||
if (glm::length(glm::max(glm::max(low - plan_of(location), plan_of(location) - high), glm::dvec2{0.0})) >= best)
|
||||
continue;
|
||||
auto const distance{geometry::plan_distance(curve.point(nearest_on(*Track, p, location)), location)};
|
||||
if (distance < best)
|
||||
{
|
||||
best = distance;
|
||||
track = Track;
|
||||
path = p;
|
||||
}
|
||||
}
|
||||
};
|
||||
consider(Standing.track);
|
||||
if (track == nullptr)
|
||||
{
|
||||
tool.moved_valid = false;
|
||||
return;
|
||||
}
|
||||
auto const *item{signal_template_of(found->include.file)};
|
||||
if (item == nullptr)
|
||||
return;
|
||||
path = std::clamp(path, 0, static_cast<int>(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(*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(point), right_of(travel)) >= 0.0 ? 1 : -1};
|
||||
tool.moved_valid = signal_spot_at(*track, path, point, point, facing, sideoftravel * facing, *item, tool.moved);
|
||||
for (auto *candidate : simulation::Paths.sequence())
|
||||
consider(candidate);
|
||||
if (track == nullptr)
|
||||
return false;
|
||||
auto const axis{bezier{track->m_paths[path]}.point(nearest_on(*track, path, location))};
|
||||
auto const side{glm::dot(plan_of(location) - plan_of(axis), right_of(travel_of(Standing.include.yaw))) >= 0.0 ? 1 : -1};
|
||||
if (false == signal_drop_at(Standing, *track, path, location, side, Spot))
|
||||
return false;
|
||||
Spot.offset = geometry::plan_distance(location, axis);
|
||||
Spot.position = location;
|
||||
Spot.yaw = Standing.include.yaw;
|
||||
return true;
|
||||
}
|
||||
|
||||
void editor_mode::signal_release()
|
||||
@@ -530,40 +590,53 @@ void editor_mode::signal_release()
|
||||
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())
|
||||
return;
|
||||
if (false == scene::Layers.tracked(found->include.instance) || false == scene::Layers.removable(found->include.instance))
|
||||
if (auto const *found{signal_standing_of(tool.selected)})
|
||||
signal_move_to(*found, tool.moved, STR_C("Signal %s moved, read by %s %s of %s, Ctrl+Z takes it back"), STR_C("Signal %s moved, Ctrl+Z takes it back"));
|
||||
}
|
||||
|
||||
bool editor_mode::signal_move_to(signal_standing const &Standing, signal_spot const &Spot, char const *Read, char const *Unread)
|
||||
{
|
||||
auto &tool{m_signal};
|
||||
auto const instance{Standing.include.instance};
|
||||
if (false == scene::Layers.tracked(instance) || false == scene::Layers.removable(instance))
|
||||
{
|
||||
tool.status = STR_C("This signal is in a file the editor doesn't change, it can't be moved");
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
auto const &spot{tool.moved};
|
||||
auto const &included{scene::Layers.instance(found->include.instance)};
|
||||
auto values{found->include.values};
|
||||
auto const local{spot.position - included.context.offset};
|
||||
auto const &included{scene::Layers.instance(instance)};
|
||||
auto file{*included.file};
|
||||
signal_spot before;
|
||||
auto const *item{signal_template_of(Standing.include.file)};
|
||||
if (item != nullptr && signal_standing_spot(Standing, before) && travel_side(before.side, before.list) != travel_side(Spot.side, Spot.list))
|
||||
file = signal_variant(*item, Spot);
|
||||
auto values{Standing.include.values};
|
||||
auto const local{Spot.position - included.context.offset};
|
||||
values[1] = editor_includes::number(local.x);
|
||||
values[2] = editor_includes::number(local.y);
|
||||
values[3] = editor_includes::number(local.z);
|
||||
values[4] = editor_includes::number(spot.yaw);
|
||||
auto before{scene::Layers.directive(found->include.instance)};
|
||||
values[4] = editor_includes::number(Spot.yaw);
|
||||
auto const *next{signal_template_of(file)};
|
||||
auto const read{next != nullptr && false == next->read.empty() ? editor_includes::substitute(next->read, values) : Standing.read};
|
||||
auto directive{scene::Layers.directive(instance)};
|
||||
std::vector<std::pair<TTrack *, editor_track::state>> states;
|
||||
if (false == found->read.empty() && spot.track->Dynamics.empty())
|
||||
if (false == Standing.read.empty() && Spot.track->Dynamics.empty())
|
||||
{
|
||||
signal_detach_events(found->read, states);
|
||||
if (std::none_of(states.begin(), states.end(), [&](auto const &State) { return State.first == spot.track; }))
|
||||
states.emplace_back(spot.track, editor_track::capture(*spot.track));
|
||||
editor_track::events(*spot.track, spot.list).emplace_back(ToLower(found->read), nullptr);
|
||||
signal_detach_events(Standing.read, states);
|
||||
if (std::none_of(states.begin(), states.end(), [&](auto const &State) { return State.first == Spot.track; }))
|
||||
states.emplace_back(Spot.track, editor_track::capture(*Spot.track));
|
||||
editor_track::events(*Spot.track, Spot.list).emplace_back(ToLower(read), nullptr);
|
||||
std::string missing;
|
||||
editor_track::bind_events(*spot.track, missing);
|
||||
spot.track->mark_dirty();
|
||||
editor_track::bind_events(*Spot.track, missing);
|
||||
Spot.track->mark_dirty();
|
||||
}
|
||||
set_include_directive(found->include.instance, editor_includes::compose_directive(*included.file, values));
|
||||
auto const name{Standing.name};
|
||||
auto const node{Standing.include.file};
|
||||
set_include_directive(instance, editor_includes::compose_directive(file, values));
|
||||
if (states.empty())
|
||||
{
|
||||
EditorSnapshot snap;
|
||||
snap.directives.emplace_back(found->include.instance, std::move(before));
|
||||
snap.node_name = found->include.file;
|
||||
snap.directives.emplace_back(instance, std::move(directive));
|
||||
snap.node_name = node;
|
||||
trim_history();
|
||||
m_history.push_back(std::move(snap));
|
||||
g_redo.clear();
|
||||
@@ -571,13 +644,47 @@ void editor_mode::signal_release()
|
||||
else
|
||||
{
|
||||
push_track_snapshot(std::move(states));
|
||||
m_history.back().directives.emplace_back(found->include.instance, std::move(before));
|
||||
m_history.back().directives.emplace_back(instance, std::move(directive));
|
||||
}
|
||||
tool.status = found->read.empty() ? format(STR_C("Signal %s moved, Ctrl+Z takes it back"), found->name.c_str())
|
||||
: format(STR_C("Signal %s moved, read by %s %s of %s, Ctrl+Z takes it back"), found->name.c_str(), editor_track::event_keyword(spot.list), found->read.c_str(), name_of(*spot.track).c_str());
|
||||
auto const instance{found->include.instance};
|
||||
signal_refresh(true);
|
||||
signal_select(instance);
|
||||
auto const *moved{signal_standing_of(instance)};
|
||||
tool.status = moved != nullptr && moved->track != nullptr ? format(Read, name.c_str(), editor_track::event_keyword(moved->list), moved->read.c_str(), name_of(*moved->track).c_str()) : format(Unread, name.c_str());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool editor_mode::signal_flip(scene::instance_handle const Instance)
|
||||
{
|
||||
auto const *standing{signal_standing_of(Instance)};
|
||||
signal_spot spot;
|
||||
if (standing == nullptr || false == signal_standing_spot(*standing, spot))
|
||||
{
|
||||
m_signal.status = STR_C("No track by the signal to take it across");
|
||||
return false;
|
||||
}
|
||||
auto const across{plan_of(spot.position) - plan_of(spot.axis)};
|
||||
auto const mirrored{plan_of(spot.axis) + spot.travel * glm::dot(across, spot.travel) - right_of(spot.travel) * glm::dot(across, right_of(spot.travel))};
|
||||
spot.side = -spot.side;
|
||||
spot.position = {mirrored.x, spot.position.y, mirrored.y};
|
||||
return signal_move_to(*standing, spot, STR_C("Signal %s moved to the other side of the track, read by %s %s of %s, Ctrl+Z takes it back"), STR_C("Signal %s moved to the other side of the track, Ctrl+Z takes it back"));
|
||||
}
|
||||
|
||||
bool editor_mode::signal_turn(scene::instance_handle const Instance)
|
||||
{
|
||||
auto const *standing{signal_standing_of(Instance)};
|
||||
signal_spot spot;
|
||||
if (standing == nullptr || false == signal_standing_spot(*standing, spot))
|
||||
{
|
||||
m_signal.status = STR_C("No track by the signal to turn it to");
|
||||
return false;
|
||||
}
|
||||
auto const *item{signal_template_of(standing->include.file)};
|
||||
auto const position{spot.position};
|
||||
if (item == nullptr || false == signal_spot_at(*spot.track, spot.path, spot.axis, spot.axis, spot.list == 2 ? -1 : 1, spot.side, *item, spot))
|
||||
return false;
|
||||
spot.position = position;
|
||||
spot.yaw = std::fmod(standing->include.yaw + 180.0, 360.0);
|
||||
return signal_move_to(*standing, spot, STR_C("Signal %s turned to the trains of the other direction, read by %s %s of %s, Ctrl+Z takes it back"), STR_C("Signal %s turned to the trains of the other direction, Ctrl+Z takes it back"));
|
||||
}
|
||||
|
||||
std::string editor_mode::signal_variant(signal_template const &Template, signal_spot const &Spot) const
|
||||
@@ -876,9 +983,8 @@ void editor_mode::render_signal_palette()
|
||||
|
||||
void editor_mode::render_signal_selected()
|
||||
{
|
||||
auto &tool{m_signal};
|
||||
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())
|
||||
auto const *found{signal_standing_of(m_signal.selected)};
|
||||
if (found == nullptr)
|
||||
return;
|
||||
ImGui::Separator();
|
||||
ImGui::TextUnformatted(format(STR_C("Selected: %s (%s)"), found->name.c_str(), found->include.file.c_str()).c_str());
|
||||
@@ -889,6 +995,15 @@ void editor_mode::render_signal_selected()
|
||||
ImGui::TextDisabled(STR_C("Read by %s %s of %s"), editor_track::event_keyword(found->list), found->read.c_str(), name_of(*found->track).c_str());
|
||||
else
|
||||
ImGui::TextColored(ImVec4(1.0f, 0.5f, 0.3f, 1.0f), STR_C("No path has %s among its events: the train doesn't see it"), found->read.c_str());
|
||||
auto const instance{found->include.instance};
|
||||
auto const width{(ImGui::GetContentRegionAvail().x - ImGui::GetStyle().ItemSpacing.x) * 0.5f};
|
||||
if (ImGui::Button(STR_C("To the other side"), ImVec2(width, 0.0f)))
|
||||
signal_flip(instance);
|
||||
item_tooltip("Takes the signal across the track, it keeps facing the same trains. Dragging it over the track does the same");
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button(STR_C("Turn around"), ImVec2(width, 0.0f)))
|
||||
signal_turn(instance);
|
||||
item_tooltip("Turns the signal to the trains of the other direction, they read it by the other event of the path");
|
||||
}
|
||||
|
||||
void editor_mode::render_signal_ui()
|
||||
@@ -1038,10 +1153,7 @@ void editor_mode::draw_signal_overlay() const
|
||||
drawlist->AddText(ImVec2(head.x + 6.0f, head.y - 6.0f), colour, label.c_str());
|
||||
}
|
||||
if (selected || hovered)
|
||||
{
|
||||
auto const yaw{glm::radians(standing.include.yaw + 180.0)};
|
||||
arrow(location, glm::dvec2{std::sin(yaw), std::cos(yaw)}, colour);
|
||||
}
|
||||
arrow(location, travel_of(standing.include.yaw), colour);
|
||||
}
|
||||
auto const preview = [&](signal_spot const &Spot, ImU32 const Colour) {
|
||||
projection.line(drawlist, Spot.axis + glm::dvec3{0.0, kRailTop, 0.0}, Spot.position, Colour, 2.0f);
|
||||
|
||||
@@ -1641,8 +1641,10 @@ std::vector<editor_mode::key_hint> editor_mode::track_key_hints(bool const All)
|
||||
hints = {{"LMB on a track", "the line the objects go along"}};
|
||||
else if (m_track_window_open && m_track_tab == track_tab::parallel)
|
||||
hints = {{"LMB on a track", "the new track goes beside it"}, {"Click on a side in the panel", "the side of the new track"}};
|
||||
else if (m_track_window_open && m_track_tab == track_tab::signals && (m_signal.placing || m_signal.moving))
|
||||
hints = {{"Drag along", m_signal.moving ? "moves the signal along the track" : "the trains it faces run that way"}, {"Release", "done"}, {"Esc", "cancel"}};
|
||||
else if (m_track_window_open && m_track_tab == track_tab::signals && m_signal.moving)
|
||||
hints = {{"Drag along", "moves the signal along the track"}, {"Drag over the track", "to the other side"}, {"Release", "done"}, {"Esc", "cancel"}};
|
||||
else if (m_track_window_open && m_track_tab == track_tab::signals && m_signal.placing)
|
||||
hints = {{"Drag along", "the trains it faces run that way"}, {"Release", "done"}, {"Esc", "cancel"}};
|
||||
else if (m_track_window_open && m_track_tab == track_tab::signals && signal_armed() == nullptr)
|
||||
hints = {{"List in the panel", "the type of the signal"}, {"Drag a signal", "moves it along the track"}, {"LMB on a signal", "selects it"}};
|
||||
else if (m_track_window_open && m_track_tab == track_tab::signals)
|
||||
|
||||
@@ -9955,3 +9955,36 @@ msgstr "Gdy tor wychodzi poza teren, NMT dobudowuje się wokół kursora na bie
|
||||
|
||||
msgid "The ends have to be numbers, from 0.1 m to 100 km apart"
|
||||
msgstr "Końce muszą być liczbami, od 0,1 m do 100 km od siebie"
|
||||
|
||||
msgid "To the other side"
|
||||
msgstr "Na drugą stronę"
|
||||
|
||||
msgid "Takes the signal across the track, it keeps facing the same trains. Dragging it over the track does the same"
|
||||
msgstr "Przenosi sygnalizator na drugą stronę toru, dalej stoi przodem do tych samych pociągów. To samo robi przeciągnięcie go przez tor"
|
||||
|
||||
msgid "Turns the signal to the trains of the other direction, they read it by the other event of the path"
|
||||
msgstr "Obraca sygnalizator do pociągów z przeciwnego kierunku, odczytują go przez drugi event toru"
|
||||
|
||||
msgid "No track by the signal to take it across"
|
||||
msgstr "Przy sygnalizatorze nie ma toru, na którego drugą stronę można go przenieść"
|
||||
|
||||
msgid "No track by the signal to turn it to"
|
||||
msgstr "Przy sygnalizatorze nie ma toru, do którego można go obrócić"
|
||||
|
||||
msgid "Signal %s moved to the other side of the track, read by %s %s of %s, Ctrl+Z takes it back"
|
||||
msgstr "Sygnalizator %s przeniesiony na drugą stronę toru, pociąg odczytuje go przez %s %s toru %s, Ctrl+Z cofa"
|
||||
|
||||
msgid "Signal %s moved to the other side of the track, Ctrl+Z takes it back"
|
||||
msgstr "Sygnalizator %s przeniesiony na drugą stronę toru, Ctrl+Z cofa"
|
||||
|
||||
msgid "Signal %s turned to the trains of the other direction, read by %s %s of %s, Ctrl+Z takes it back"
|
||||
msgstr "Sygnalizator %s obrócony do pociągów z przeciwnego kierunku, pociąg odczytuje go przez %s %s toru %s, Ctrl+Z cofa"
|
||||
|
||||
msgid "Signal %s turned to the trains of the other direction, Ctrl+Z takes it back"
|
||||
msgstr "Sygnalizator %s obrócony do pociągów z przeciwnego kierunku, Ctrl+Z cofa"
|
||||
|
||||
msgid "Drag over the track"
|
||||
msgstr "Przeciągnij przez tor"
|
||||
|
||||
msgid "to the other side"
|
||||
msgstr "na drugą stronę"
|
||||
|
||||
@@ -1090,13 +1090,17 @@ std::string node_layers::directive(instance_handle const Instance) const
|
||||
}
|
||||
|
||||
// replaces the directive of specified include, the scenery file receives it on save
|
||||
bool node_layers::modify(instance_handle const Instance, std::string const &Directive)
|
||||
bool node_layers::modify(instance_handle const Instance, std::string const &Directive, std::string const &File)
|
||||
{
|
||||
if (false == tracked(Instance) || m_instances[Instance - 1].dead || Directive.empty())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
m_instances[Instance - 1].directive = Directive;
|
||||
if (false == File.empty())
|
||||
{
|
||||
m_instances[Instance - 1].file = &(*m_templates.emplace(File).first);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -335,7 +335,7 @@ class node_layers
|
||||
// returns: empty text if the directive can't be read
|
||||
std::string directive(instance_handle Instance) const;
|
||||
// replaces the directive of specified include, the scenery file receives it on save. NOTE: the nodes aren't touched
|
||||
bool modify(instance_handle Instance, std::string const &Directive);
|
||||
bool modify(instance_handle Instance, std::string const &Directive, std::string const &File = {});
|
||||
// takes the models specified include was loaded with out of the scene, once the editor shows its own in their place
|
||||
void rebuilt(instance_handle Instance);
|
||||
// true if specified node was replaced by the editor with a node showing the current state of its include
|
||||
|
||||
Reference in New Issue
Block a user