mirror of
https://github.com/MaSzyna-EU07/maszyna.git
synced 2026-10-31 22:49:05 +01:00
fix(roads): an axle getting on a crossroads takes the way the other axle took, one starting where it gets on
This commit is contained in:
@@ -534,6 +534,7 @@ editor_mode::editor_mode() {
|
||||
ui()->set_file_actions([this]() { save(); }, [this]() { export_scenery(); });
|
||||
ui()->set_new_scenery([this]() { m_newscenery_asked = true; });
|
||||
ui()->set_open_scenery([this]() { m_openscenery_asked = true; });
|
||||
ui()->set_memcell_focus([this](scene::basic_node *Node) { start_focus(Node); });
|
||||
ui()->set_edit_menu([this]() { render_edit_menu(); });
|
||||
ui()->set_tools_menu([this]() { render_track_presets_menu(); });
|
||||
ui()->set_menu_options([this]() {
|
||||
|
||||
@@ -20,6 +20,8 @@ http://mozilla.org/MPL/2.0/.
|
||||
#include "world/Track.h"
|
||||
#include "world/Road.h"
|
||||
#include "world/RoadPoint.h"
|
||||
#include "world/MemCell.h"
|
||||
#include "world/Event.h"
|
||||
#include "simulation/simulation.h"
|
||||
|
||||
#include "imgui/imgui.h"
|
||||
@@ -537,6 +539,51 @@ bool editor_mode::render_roadpoint_layout(roadpoint_node::state &State, roadpoin
|
||||
"It opens a few seconds after the last one is gone");
|
||||
if (ImGui::Checkbox(STR_C("Paint the stop lines"), &State.stoplines))
|
||||
changed = true;
|
||||
// what the scenery closes the entry onto the crossing with
|
||||
char memcell[128];
|
||||
copy_text(memcell, sizeof(memcell), State.memcell);
|
||||
ImGui::SetNextItemWidth(200.0f);
|
||||
if (ImGui::InputTextWithHint(STR_C("Memory cell"), STR_C("(named after the crossing)"), memcell, sizeof(memcell), ImGuiInputTextFlags_EnterReturnsTrue))
|
||||
{
|
||||
auto name{lower_case(memcell)};
|
||||
if (name == "none")
|
||||
name.clear();
|
||||
if (name.find(' ') == std::string::npos && name != State.memcell)
|
||||
{
|
||||
State.memcell = name;
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
item_tooltip("Events of the scenery close the entry onto the crossing with the first value of this memory cell:\n"
|
||||
"above 0 the vehicles heading for the crossing are stopped, below 0 it's kept open whatever comes by the rails,\n"
|
||||
"0 leaves it to the trains. The vehicles past the stops, on the crossing or leaving it, drive on.\n"
|
||||
"The crossing sets the second value: 1 while the entry is closed, 0 while it's open.\n"
|
||||
"Left empty, the cell is the one named after the crossing with \":block\" added, made if the scenery doesn't have it");
|
||||
if (Point != nullptr)
|
||||
{
|
||||
auto *cell{Point->memcell()};
|
||||
if (cell == nullptr)
|
||||
{
|
||||
ImGui::TextColored(warning, "%s", STR_C("The crossing has no name, so there's no memory cell to tell it by"));
|
||||
}
|
||||
else
|
||||
{
|
||||
ImGui::Text("\"%s\": %g %g", cell->name().c_str(), cell->Value1(), cell->Value2());
|
||||
ImGui::SameLine();
|
||||
ImGui::TextUnformatted(Point->closed() ? STR_C("The entry is closed") : STR_C("The entry is open"));
|
||||
// the value can be tried out right here; the scenery isn't changed by it
|
||||
int control{cell->Value1() > 0.0 ? 1 : cell->Value1() < 0.0 ? 2 : 0};
|
||||
auto const before{control};
|
||||
ImGui::RadioButton(STR_C("By the trains"), &control, 0);
|
||||
ImGui::SameLine();
|
||||
ImGui::RadioButton(STR_C("Closed"), &control, 1);
|
||||
ImGui::SameLine();
|
||||
ImGui::RadioButton(STR_C("Kept open"), &control, 2);
|
||||
item_tooltip("Sets the first value of the memory cell, the way an event of the scenery would. It isn't saved with the scenery");
|
||||
if (control != before)
|
||||
cell->UpdateValues("", control == 1 ? 1.0 : control == 2 ? -1.0 : 0.0, 0.0, basic_event::flags::value1);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case roadpoint_node::kind_type::spawn:
|
||||
|
||||
@@ -36,6 +36,7 @@ editor_ui::editor_ui()
|
||||
add_external_panel(&m_nodebankpanel);
|
||||
add_external_panel(&m_layerspanel);
|
||||
add_external_panel(&m_includespanel);
|
||||
add_external_panel(&m_memcellspanel);
|
||||
|
||||
// the menu is a part of the layout of the editor, not a pop-up as in the driver mode
|
||||
m_menu_always = true;
|
||||
@@ -985,6 +986,14 @@ void editor_ui::render_menu_contents()
|
||||
|
||||
ui_layer::render_menu_contents();
|
||||
|
||||
if (ImGui::BeginMenu(STR_C("Debug")))
|
||||
{
|
||||
ImGui::MenuItem(STR_C("Memory cells"), nullptr, &m_memcellspanel.is_open);
|
||||
if (ImGui::IsItemHovered())
|
||||
ImGui::SetTooltip("%s", STR_C("Looks up the memory cells of the scenery, shows what's in them and sets it by hand"));
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
|
||||
if (m_menuoptions)
|
||||
m_menuoptions();
|
||||
}
|
||||
|
||||
@@ -108,6 +108,7 @@ class editor_ui : public ui_layer
|
||||
void set_file_actions(std::function<void()> Save, std::function<void()> Export);
|
||||
void set_new_scenery(std::function<void()> New) { m_newscenery = std::move(New); }
|
||||
void set_open_scenery(std::function<void()> Open) { m_openscenery = std::move(Open); }
|
||||
void set_memcell_focus(std::function<void(scene::basic_node *)> Focus) { m_memcellspanel.focus = std::move(Focus); }
|
||||
// part of the main window between the docked windows, where the 3d view is seen; for the overlays placed in it
|
||||
static glm::vec2 view_min()
|
||||
{
|
||||
@@ -197,6 +198,7 @@ class editor_ui : public ui_layer
|
||||
nodebank_panel m_nodebankpanel{"Node bank", true}; // templates to insert, paint or fill with
|
||||
layers_panel m_layerspanel{"Layers", true};
|
||||
includes_panel m_includespanel{"Include database", false};
|
||||
memcells_panel m_memcellspanel{"Memory cells", false};
|
||||
std::function<void()> m_save;
|
||||
std::function<void()> m_export;
|
||||
std::function<void()> m_newscenery;
|
||||
|
||||
@@ -21,11 +21,13 @@ http://mozilla.org/MPL/2.0/.
|
||||
#include "application/editoruilayer.h"
|
||||
#include "rendering/renderer.h"
|
||||
#include "simulation/simulation.h"
|
||||
#include "utilities/Logs.h"
|
||||
#include "editor/editorModelSets.hpp"
|
||||
#include "editor/editorFormat.hpp"
|
||||
#include "editor/editorSettings.hpp"
|
||||
#include "imgui/imgui_internal.h"
|
||||
|
||||
#include <cstring>
|
||||
#include <filesystem>
|
||||
#include <set>
|
||||
|
||||
@@ -2453,3 +2455,197 @@ void includes_panel::render()
|
||||
}
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
// memory cells of the scenery
|
||||
void memcells_panel::render()
|
||||
{
|
||||
if (false == is_open)
|
||||
{
|
||||
return;
|
||||
}
|
||||
ImGui::SetNextWindowPos(ImVec2S(560, 110), ImGuiCond_FirstUseEver);
|
||||
ImGui::SetNextWindowSize(ImVec2S(620, 560), ImGuiCond_FirstUseEver);
|
||||
auto const panelname{(title.empty() ? std::string{STR_C(m_name.c_str())} : title) + "###" + m_name};
|
||||
if (ImGui::Begin(panelname.c_str(), &is_open, ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoCollapse))
|
||||
{
|
||||
render_list();
|
||||
render_cell();
|
||||
}
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
void memcells_panel::render_list()
|
||||
{
|
||||
auto const &cells{simulation::Memory.sequence()};
|
||||
// the cells are never taken away, only added, so a change of the count is a change of the set
|
||||
if (m_sorted.size() != static_cast<std::size_t>(std::count_if(cells.begin(), cells.end(), [](TMemCell const *Cell) { return Cell != nullptr; })))
|
||||
{
|
||||
m_sorted.clear();
|
||||
for (auto *cell : cells)
|
||||
{
|
||||
if (cell != nullptr)
|
||||
{
|
||||
m_sorted.emplace_back(cell);
|
||||
}
|
||||
}
|
||||
std::stable_sort(m_sorted.begin(), m_sorted.end(), [](TMemCell const *Left, TMemCell const *Right) { return Left->name() < Right->name(); });
|
||||
m_listedtime = -1.0;
|
||||
}
|
||||
ImGui::SetNextItemWidth(260.0f);
|
||||
ImGui::InputTextWithHint("##filter", STR_C("Search"), m_filter, sizeof(m_filter));
|
||||
ImGui::SameLine();
|
||||
ImGui::Checkbox(STR_C("In the texts too"), &m_searchtext);
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button(STR_C("Log all")))
|
||||
{
|
||||
simulation::Memory.log_all();
|
||||
}
|
||||
if (ImGui::IsItemHovered())
|
||||
{
|
||||
ImGui::SetTooltip("%s", STR_C("Writes the content of all memory cells to the log"));
|
||||
}
|
||||
|
||||
std::string filter{m_filter};
|
||||
std::transform(filter.begin(), filter.end(), filter.begin(), [](unsigned char Character) { return static_cast<char>(std::tolower(Character)); });
|
||||
auto const now{ImGui::GetTime()};
|
||||
auto const searchtext{m_searchtext && false == filter.empty()};
|
||||
if (filter != m_listedfilter || m_listedtime < 0.0 || (searchtext && now - m_listedtime > 0.5))
|
||||
{
|
||||
m_listedfilter = filter;
|
||||
m_listedtime = now;
|
||||
m_listed.clear();
|
||||
auto const matches = [&filter](std::string const &Text) {
|
||||
if (filter.empty())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
std::string text{Text};
|
||||
std::transform(text.begin(), text.end(), text.begin(), [](unsigned char Character) { return static_cast<char>(std::tolower(Character)); });
|
||||
return text.find(filter) != std::string::npos;
|
||||
};
|
||||
for (auto *cell : m_sorted)
|
||||
{
|
||||
if (matches(cell->name()) || (searchtext && matches(cell->Text())))
|
||||
{
|
||||
m_listed.emplace_back(cell);
|
||||
}
|
||||
}
|
||||
}
|
||||
ImGui::TextDisabled(STR_C("Memory cells: %d, shown: %d"), static_cast<int>(m_sorted.size()), static_cast<int>(m_listed.size()));
|
||||
|
||||
auto const listheight{std::max(120.0f, ImGui::GetContentRegionAvail().y - ImGui::GetFrameHeightWithSpacing() * 7.0f)};
|
||||
if (ImGui::BeginTable("##memcells", 5, ImGuiTableFlags_Resizable | ImGuiTableFlags_BordersInnerV | ImGuiTableFlags_RowBg | ImGuiTableFlags_ScrollY, ImVec2(-1.0f, listheight)))
|
||||
{
|
||||
ImGui::TableSetupScrollFreeze(0, 1);
|
||||
ImGui::TableSetupColumn(STR_C("Name"), ImGuiTableColumnFlags_WidthStretch, 2.0f);
|
||||
ImGui::TableSetupColumn(STR_C("Text"), ImGuiTableColumnFlags_WidthStretch, 1.5f);
|
||||
ImGui::TableSetupColumn(STR_C("Value 1"), ImGuiTableColumnFlags_WidthStretch, 0.7f);
|
||||
ImGui::TableSetupColumn(STR_C("Value 2"), ImGuiTableColumnFlags_WidthStretch, 0.7f);
|
||||
ImGui::TableSetupColumn(STR_C("Track"), ImGuiTableColumnFlags_WidthStretch, 1.0f);
|
||||
ImGui::TableHeadersRow();
|
||||
ImGuiListClipper clipper;
|
||||
clipper.Begin(static_cast<int>(m_listed.size()));
|
||||
while (clipper.Step())
|
||||
{
|
||||
for (int row = clipper.DisplayStart; row < clipper.DisplayEnd; ++row)
|
||||
{
|
||||
auto *cell{m_listed[row]};
|
||||
ImGui::PushID(cell);
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
auto const name{cell->name().empty() ? std::string{STR_C("(no name)")} : cell->name()};
|
||||
if (ImGui::Selectable(name.c_str(), cell == m_selected, ImGuiSelectableFlags_SpanAllColumns | ImGuiSelectableFlags_AllowDoubleClick))
|
||||
{
|
||||
select(cell);
|
||||
if (ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left) && focus && cell->location() != glm::dvec3{0.0})
|
||||
{
|
||||
focus(cell);
|
||||
}
|
||||
}
|
||||
if (false == cell->is_exportable && ImGui::IsItemHovered())
|
||||
{
|
||||
ImGui::SetTooltip("%s", STR_C("Made by the simulator, it isn't saved with the scenery"));
|
||||
}
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextUnformatted(cell->Text().c_str());
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%g", cell->Value1());
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%g", cell->Value2());
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextUnformatted(cell->Track != nullptr ? cell->Track->name().c_str() : cell->asTrackName.c_str());
|
||||
ImGui::PopID();
|
||||
}
|
||||
}
|
||||
ImGui::EndTable();
|
||||
}
|
||||
}
|
||||
|
||||
void memcells_panel::select(TMemCell *Cell)
|
||||
{
|
||||
m_selected = Cell;
|
||||
if (Cell == nullptr)
|
||||
{
|
||||
return;
|
||||
}
|
||||
auto const &text{Cell->Text()};
|
||||
auto const length{std::min(text.size(), sizeof(m_text) - 1)};
|
||||
std::memcpy(m_text, text.data(), length);
|
||||
m_text[length] = 0;
|
||||
m_value1 = Cell->Value1();
|
||||
m_value2 = Cell->Value2();
|
||||
}
|
||||
|
||||
void memcells_panel::render_cell()
|
||||
{
|
||||
ImGui::Separator();
|
||||
if (m_selected == nullptr)
|
||||
{
|
||||
ImGui::TextDisabled("%s", STR_C("Pick a memory cell from the list to set what's in it. A double click shows where it is"));
|
||||
return;
|
||||
}
|
||||
auto *cell{m_selected};
|
||||
auto const location{cell->location()};
|
||||
ImGui::Text(STR_C("\"%s\": %s %g %g"), cell->name().c_str(), cell->Text().c_str(), cell->Value1(), cell->Value2());
|
||||
ImGui::SameLine();
|
||||
ImGui::BeginDisabled(false == static_cast<bool>(focus) || location == glm::dvec3{0.0});
|
||||
if (ImGui::SmallButton(STR_C("Show")))
|
||||
{
|
||||
focus(cell);
|
||||
}
|
||||
ImGui::EndDisabled();
|
||||
ImGui::TextDisabled(STR_C("At %.1f %.1f %.1f"), location.x, location.y, location.z);
|
||||
|
||||
ImGui::Checkbox("##settext", &m_settext);
|
||||
ImGui::SameLine();
|
||||
ImGui::SetNextItemWidth(260.0f);
|
||||
ImGui::InputText(STR_C("Text"), m_text, sizeof(m_text));
|
||||
ImGui::Checkbox("##setvalue1", &m_setvalue1);
|
||||
ImGui::SameLine();
|
||||
ImGui::SetNextItemWidth(160.0f);
|
||||
ImGui::InputDouble(STR_C("Value 1"), &m_value1, 1.0, 10.0, "%g");
|
||||
ImGui::Checkbox("##setvalue2", &m_setvalue2);
|
||||
ImGui::SameLine();
|
||||
ImGui::SetNextItemWidth(160.0f);
|
||||
ImGui::InputDouble(STR_C("Value 2"), &m_value2, 1.0, 10.0, "%g");
|
||||
ImGui::BeginDisabled(false == (m_settext || m_setvalue1 || m_setvalue2));
|
||||
if (ImGui::Button(STR_C("Set")))
|
||||
{
|
||||
auto const mask{(m_settext ? basic_event::flags::text : 0) | (m_setvalue1 ? basic_event::flags::value1 : 0) | (m_setvalue2 ? basic_event::flags::value2 : 0) |
|
||||
(m_add ? basic_event::flags::mode_add : 0)};
|
||||
cell->UpdateValues(m_text, m_value1, m_value2, mask);
|
||||
WriteLog("Memory cell \"" + cell->name() + "\" set by hand to: " + cell->Text() + " " + std::to_string(cell->Value1()) + " " + std::to_string(cell->Value2()));
|
||||
}
|
||||
ImGui::EndDisabled();
|
||||
if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled))
|
||||
{
|
||||
ImGui::SetTooltip("%s", STR_C("Puts the ticked fields in the cell, the way an UpdateValues event does. The scenery files aren't changed"));
|
||||
}
|
||||
ImGui::SameLine();
|
||||
ImGui::Checkbox(STR_C("Add to the values"), &m_add);
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button(STR_C("Read again")))
|
||||
{
|
||||
select(cell);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -275,6 +275,40 @@ class includes_panel : public ui_panel
|
||||
bool m_statuserror{false};
|
||||
};
|
||||
|
||||
// memory cells of the scenery: looked up by name or text, with what's in them, which can be set by hand while the scenery runs
|
||||
class memcells_panel : public ui_panel
|
||||
{
|
||||
|
||||
public:
|
||||
memcells_panel(std::string const &Name, bool const Isopen) : ui_panel(Name, Isopen) {}
|
||||
|
||||
void render() override;
|
||||
|
||||
// moves the camera to specified node; provided by the editor mode
|
||||
std::function<void(scene::basic_node *)> focus;
|
||||
|
||||
private:
|
||||
// methods
|
||||
void render_list();
|
||||
void render_cell();
|
||||
void select(TMemCell *Cell);
|
||||
// members
|
||||
char m_filter[128]{};
|
||||
bool m_searchtext{true}; // the filter is looked for in the texts of the cells as well as in their names
|
||||
std::vector<TMemCell *> m_sorted; // all cells, in the order of their names
|
||||
std::vector<TMemCell *> m_listed; // the ones matching the filter
|
||||
std::string m_listedfilter; // filter the list was made for
|
||||
double m_listedtime{-1.0}; // when it was made; the texts change, so it's made again now and then
|
||||
TMemCell *m_selected{nullptr};
|
||||
char m_text[256]{}; // what's going to be put in the selected cell
|
||||
double m_value1{0.0};
|
||||
double m_value2{0.0};
|
||||
bool m_settext{true};
|
||||
bool m_setvalue1{true};
|
||||
bool m_setvalue2{true};
|
||||
bool m_add{false}; // the values are added to what the cell holds, the way an event with the add mode does
|
||||
};
|
||||
|
||||
class functions_panel : public ui_panel
|
||||
{
|
||||
|
||||
|
||||
Reference in New Issue
Block a user