mirror of
https://github.com/MaSzyna-EU07/maszyna.git
synced 2026-11-07 07:38:06 +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_file_actions([this]() { save(); }, [this]() { export_scenery(); });
|
||||||
ui()->set_new_scenery([this]() { m_newscenery_asked = true; });
|
ui()->set_new_scenery([this]() { m_newscenery_asked = true; });
|
||||||
ui()->set_open_scenery([this]() { m_openscenery_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_edit_menu([this]() { render_edit_menu(); });
|
||||||
ui()->set_tools_menu([this]() { render_track_presets_menu(); });
|
ui()->set_tools_menu([this]() { render_track_presets_menu(); });
|
||||||
ui()->set_menu_options([this]() {
|
ui()->set_menu_options([this]() {
|
||||||
|
|||||||
@@ -20,6 +20,8 @@ http://mozilla.org/MPL/2.0/.
|
|||||||
#include "world/Track.h"
|
#include "world/Track.h"
|
||||||
#include "world/Road.h"
|
#include "world/Road.h"
|
||||||
#include "world/RoadPoint.h"
|
#include "world/RoadPoint.h"
|
||||||
|
#include "world/MemCell.h"
|
||||||
|
#include "world/Event.h"
|
||||||
#include "simulation/simulation.h"
|
#include "simulation/simulation.h"
|
||||||
|
|
||||||
#include "imgui/imgui.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");
|
"It opens a few seconds after the last one is gone");
|
||||||
if (ImGui::Checkbox(STR_C("Paint the stop lines"), &State.stoplines))
|
if (ImGui::Checkbox(STR_C("Paint the stop lines"), &State.stoplines))
|
||||||
changed = true;
|
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;
|
break;
|
||||||
}
|
}
|
||||||
case roadpoint_node::kind_type::spawn:
|
case roadpoint_node::kind_type::spawn:
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ editor_ui::editor_ui()
|
|||||||
add_external_panel(&m_nodebankpanel);
|
add_external_panel(&m_nodebankpanel);
|
||||||
add_external_panel(&m_layerspanel);
|
add_external_panel(&m_layerspanel);
|
||||||
add_external_panel(&m_includespanel);
|
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
|
// the menu is a part of the layout of the editor, not a pop-up as in the driver mode
|
||||||
m_menu_always = true;
|
m_menu_always = true;
|
||||||
@@ -985,6 +986,14 @@ void editor_ui::render_menu_contents()
|
|||||||
|
|
||||||
ui_layer::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)
|
if (m_menuoptions)
|
||||||
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_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_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_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
|
// 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()
|
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
|
nodebank_panel m_nodebankpanel{"Node bank", true}; // templates to insert, paint or fill with
|
||||||
layers_panel m_layerspanel{"Layers", true};
|
layers_panel m_layerspanel{"Layers", true};
|
||||||
includes_panel m_includespanel{"Include database", false};
|
includes_panel m_includespanel{"Include database", false};
|
||||||
|
memcells_panel m_memcellspanel{"Memory cells", false};
|
||||||
std::function<void()> m_save;
|
std::function<void()> m_save;
|
||||||
std::function<void()> m_export;
|
std::function<void()> m_export;
|
||||||
std::function<void()> m_newscenery;
|
std::function<void()> m_newscenery;
|
||||||
|
|||||||
@@ -21,11 +21,13 @@ http://mozilla.org/MPL/2.0/.
|
|||||||
#include "application/editoruilayer.h"
|
#include "application/editoruilayer.h"
|
||||||
#include "rendering/renderer.h"
|
#include "rendering/renderer.h"
|
||||||
#include "simulation/simulation.h"
|
#include "simulation/simulation.h"
|
||||||
|
#include "utilities/Logs.h"
|
||||||
#include "editor/editorModelSets.hpp"
|
#include "editor/editorModelSets.hpp"
|
||||||
#include "editor/editorFormat.hpp"
|
#include "editor/editorFormat.hpp"
|
||||||
#include "editor/editorSettings.hpp"
|
#include "editor/editorSettings.hpp"
|
||||||
#include "imgui/imgui_internal.h"
|
#include "imgui/imgui_internal.h"
|
||||||
|
|
||||||
|
#include <cstring>
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include <set>
|
#include <set>
|
||||||
|
|
||||||
@@ -2453,3 +2455,197 @@ void includes_panel::render()
|
|||||||
}
|
}
|
||||||
ImGui::End();
|
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};
|
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
|
class functions_panel : public ui_panel
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|||||||
@@ -10290,3 +10290,88 @@ msgstr "Na jakiej długości od końca p1 szyny płynnie przechodzą w szyny tor
|
|||||||
|
|
||||||
msgid "How far from the end p2 the rails fade into those of the track joined there, where their textures differ; 0: the joint stays sharp, whatever the track joined there has"
|
msgid "How far from the end p2 the rails fade into those of the track joined there, where their textures differ; 0: the joint stays sharp, whatever the track joined there has"
|
||||||
msgstr "Na jakiej długości od końca p2 szyny płynnie przechodzą w szyny toru tam dołączonego, gdy ich tekstury są różne; 0: ostra granica na styku, niezależnie od ustawienia toru tam dołączonego"
|
msgstr "Na jakiej długości od końca p2 szyny płynnie przechodzą w szyny toru tam dołączonego, gdy ich tekstury są różne; 0: ostra granica na styku, niezależnie od ustawienia toru tam dołączonego"
|
||||||
|
|
||||||
|
# scenery editor: level crossings told by memory cells
|
||||||
|
|
||||||
|
msgid "Memory cell"
|
||||||
|
msgstr "Komórka pamięci"
|
||||||
|
|
||||||
|
msgid "(named after the crossing)"
|
||||||
|
msgstr "(nazwana od przejazdu)"
|
||||||
|
|
||||||
|
msgid "Events of the scenery close the entry onto the crossing with the first value of this memory cell:\nabove 0 the vehicles heading for the crossing are stopped, below 0 it's kept open whatever comes by the rails,\n0 leaves it to the trains. The vehicles past the stops, on the crossing or leaving it, drive on.\nThe crossing sets the second value: 1 while the entry is closed, 0 while it's open.\nLeft empty, the cell is the one named after the crossing with \":block\" added, made if the scenery doesn't have it"
|
||||||
|
msgstr "Eventy scenerii zamykają wjazd na przejazd pierwszą wartością tej komórki pamięci:\npowyżej 0 pojazdy jadące na przejazd są zatrzymywane, poniżej 0 jest on otwarty bez względu na to, co jedzie po torach,\n0 zostawia to pociągom. Pojazdy za miejscami zatrzymania, na przejeździe albo zjeżdżające z niego, jadą dalej.\nPrzejazd ustawia drugą wartość: 1, gdy wjazd jest zamknięty, 0, gdy jest otwarty.\nGdy pole jest puste, jest to komórka nazwana od przejazdu z dodanym \":block\", tworzona, jeśli sceneria jej nie ma"
|
||||||
|
|
||||||
|
msgid "The crossing has no name, so there's no memory cell to tell it by"
|
||||||
|
msgstr "Przejazd nie ma nazwy, więc nie ma komórki pamięci, którą można nim sterować"
|
||||||
|
|
||||||
|
msgid "The entry is closed"
|
||||||
|
msgstr "Wjazd zamknięty"
|
||||||
|
|
||||||
|
msgid "The entry is open"
|
||||||
|
msgstr "Wjazd otwarty"
|
||||||
|
|
||||||
|
msgid "By the trains"
|
||||||
|
msgstr "Według pociągów"
|
||||||
|
|
||||||
|
msgid "Closed"
|
||||||
|
msgstr "Zamknięty"
|
||||||
|
|
||||||
|
msgid "Kept open"
|
||||||
|
msgstr "Otwarty"
|
||||||
|
|
||||||
|
msgid "Sets the first value of the memory cell, the way an event of the scenery would. It isn't saved with the scenery"
|
||||||
|
msgstr "Ustawia pierwszą wartość komórki pamięci, tak jak zrobiłby to event scenerii. Nie jest zapisywane w scenerii"
|
||||||
|
|
||||||
|
# scenery editor: memory cells window
|
||||||
|
|
||||||
|
msgid "Debug"
|
||||||
|
msgstr "Debugowanie"
|
||||||
|
|
||||||
|
msgid "Memory cells"
|
||||||
|
msgstr "Komórki pamięci"
|
||||||
|
|
||||||
|
msgid "Looks up the memory cells of the scenery, shows what's in them and sets it by hand"
|
||||||
|
msgstr "Wyszukuje komórki pamięci scenerii, pokazuje ich zawartość i pozwala ją ustawić ręcznie"
|
||||||
|
|
||||||
|
msgid "In the texts too"
|
||||||
|
msgstr "Także w tekstach"
|
||||||
|
|
||||||
|
msgid "Log all"
|
||||||
|
msgstr "Wszystkie do logu"
|
||||||
|
|
||||||
|
msgid "Writes the content of all memory cells to the log"
|
||||||
|
msgstr "Zapisuje zawartość wszystkich komórek pamięci do logu"
|
||||||
|
|
||||||
|
msgid "Memory cells: %d, shown: %d"
|
||||||
|
msgstr "Komórek pamięci: %d, pokazanych: %d"
|
||||||
|
|
||||||
|
msgid "Text"
|
||||||
|
msgstr "Tekst"
|
||||||
|
|
||||||
|
msgid "Value 1"
|
||||||
|
msgstr "Wartość 1"
|
||||||
|
|
||||||
|
msgid "Value 2"
|
||||||
|
msgstr "Wartość 2"
|
||||||
|
|
||||||
|
msgid "Made by the simulator, it isn't saved with the scenery"
|
||||||
|
msgstr "Utworzona przez symulator, nie jest zapisywana w scenerii"
|
||||||
|
|
||||||
|
msgid "Pick a memory cell from the list to set what's in it. A double click shows where it is"
|
||||||
|
msgstr "Wybierz komórkę pamięci z listy, aby ustawić jej zawartość. Podwójne kliknięcie pokazuje, gdzie jest"
|
||||||
|
|
||||||
|
msgid "\"%s\": %s %g %g"
|
||||||
|
msgstr "\"%s\": %s %g %g"
|
||||||
|
|
||||||
|
msgid "At %.1f %.1f %.1f"
|
||||||
|
msgstr "Położenie %.1f %.1f %.1f"
|
||||||
|
|
||||||
|
msgid "Puts the ticked fields in the cell, the way an UpdateValues event does. The scenery files aren't changed"
|
||||||
|
msgstr "Wpisuje zaznaczone pola do komórki, tak jak event UpdateValues. Pliki scenerii nie są zmieniane"
|
||||||
|
|
||||||
|
msgid "Add to the values"
|
||||||
|
msgstr "Dodaj do wartości"
|
||||||
|
|
||||||
|
msgid "Read again"
|
||||||
|
msgstr "Odczytaj ponownie"
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ http://mozilla.org/MPL/2.0/.
|
|||||||
|
|
||||||
#include "world/Track.h"
|
#include "world/Track.h"
|
||||||
#include "world/Event.h"
|
#include "world/Event.h"
|
||||||
|
#include "world/MemCell.h"
|
||||||
#include "world/RoadOrder.h"
|
#include "world/RoadOrder.h"
|
||||||
#include "vehicle/DynObj.h"
|
#include "vehicle/DynObj.h"
|
||||||
#include "vehicle/Driver.h"
|
#include "vehicle/Driver.h"
|
||||||
@@ -356,6 +357,14 @@ void roadpoint_node::import(cParser &Input, glm::dvec3 const &Offset)
|
|||||||
{
|
{
|
||||||
m_state.stoplines = (Input.getToken<std::string>() != "no");
|
m_state.stoplines = (Input.getToken<std::string>() != "no");
|
||||||
}
|
}
|
||||||
|
else if (token == "memcell")
|
||||||
|
{
|
||||||
|
m_state.memcell = Input.getToken<std::string>();
|
||||||
|
if (m_state.memcell == "none")
|
||||||
|
{
|
||||||
|
m_state.memcell.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
else if (token == "interval")
|
else if (token == "interval")
|
||||||
{
|
{
|
||||||
Input.getTokens();
|
Input.getTokens();
|
||||||
@@ -488,6 +497,7 @@ void roadpoint_node::bind()
|
|||||||
{
|
{
|
||||||
case kind_type::crossing:
|
case kind_type::crossing:
|
||||||
{
|
{
|
||||||
|
bind_memcell();
|
||||||
bind_crossing();
|
bind_crossing();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -574,6 +584,42 @@ void roadpoint_node::bind()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// name of the memory cell the crossing is told by
|
||||||
|
std::string roadpoint_node::memcell_name() const
|
||||||
|
{
|
||||||
|
if (false == m_state.memcell.empty())
|
||||||
|
{
|
||||||
|
return m_state.memcell;
|
||||||
|
}
|
||||||
|
return m_name.empty() || m_name == "none" ? std::string{} : m_name + ":block";
|
||||||
|
}
|
||||||
|
|
||||||
|
// finds the memory cell the crossing is told by, or makes it
|
||||||
|
void roadpoint_node::bind_memcell()
|
||||||
|
{
|
||||||
|
auto const name{memcell_name()};
|
||||||
|
if (name.empty())
|
||||||
|
{
|
||||||
|
m_memcell = nullptr;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (m_memcell != nullptr && m_memcell->name() == name)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
m_memcell = simulation::Memory.find(name);
|
||||||
|
if (m_memcell == nullptr)
|
||||||
|
{
|
||||||
|
scene::node_data nodedata;
|
||||||
|
nodedata.name = name;
|
||||||
|
m_memcell = new TMemCell(nodedata);
|
||||||
|
// NOTE: it's made anew each time the scenery is loaded, so it isn't saved with it
|
||||||
|
m_memcell->is_exportable = false;
|
||||||
|
m_memcell->location(m_state.position);
|
||||||
|
simulation::Memory.insert(m_memcell);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// finds the rails of a crossing, and the places the vehicles of each lane are to stop at
|
// finds the rails of a crossing, and the places the vehicles of each lane are to stop at
|
||||||
void roadpoint_node::bind_crossing()
|
void roadpoint_node::bind_crossing()
|
||||||
{
|
{
|
||||||
@@ -896,9 +942,12 @@ void roadpoint_node::update_crossing(double const Deltatime)
|
|||||||
}
|
}
|
||||||
m_seen.swap(seen);
|
m_seen.swap(seen);
|
||||||
}
|
}
|
||||||
if ((m_hold > 0.0) != m_closed)
|
// the scenery can close the entry, or keep it open, whatever the trains do
|
||||||
|
auto const control{m_memcell != nullptr ? m_memcell->Value1() : 0.0};
|
||||||
|
auto const closed{control > 0.0 || (control == 0.0 && m_hold > 0.0)};
|
||||||
|
if (closed != m_closed)
|
||||||
{
|
{
|
||||||
close(m_hold > 0.0);
|
close(closed);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -906,10 +955,15 @@ void roadpoint_node::update_crossing(double const Deltatime)
|
|||||||
void roadpoint_node::close(bool const Closed)
|
void roadpoint_node::close(bool const Closed)
|
||||||
{
|
{
|
||||||
m_closed = Closed;
|
m_closed = Closed;
|
||||||
|
// only the vehicles heading for the crossing are stopped, at the stops ahead of it; the ones past them leave it
|
||||||
for (std::size_t idx = 0; idx < m_stops.size() && idx < m_events.size(); ++idx)
|
for (std::size_t idx = 0; idx < m_stops.size() && idx < m_events.size(); ++idx)
|
||||||
{
|
{
|
||||||
static_cast<stop_event *>(m_events[idx])->m_velocity = (Closed ? 0.0 : -1.0);
|
static_cast<stop_event *>(m_events[idx])->m_velocity = (Closed ? 0.0 : -1.0);
|
||||||
}
|
}
|
||||||
|
if (m_memcell != nullptr && m_memcell->Value2() != (Closed ? 1.0 : 0.0))
|
||||||
|
{
|
||||||
|
m_memcell->UpdateValues("", 0.0, Closed ? 1.0 : 0.0, basic_event::flags::value2);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// puts a waiting vehicle on the road when its time comes
|
// puts a waiting vehicle on the road when its time comes
|
||||||
@@ -1150,6 +1204,10 @@ void roadpoint_node::export_as_text_(std::ostream &Output) const
|
|||||||
case kind_type::crossing:
|
case kind_type::crossing:
|
||||||
{
|
{
|
||||||
Output << "clearance " << m_state.clearance << ' ' << "warning " << m_state.warning << ' ' << "stoplines " << (m_state.stoplines ? "yes" : "no") << ' ';
|
Output << "clearance " << m_state.clearance << ' ' << "warning " << m_state.warning << ' ' << "stoplines " << (m_state.stoplines ? "yes" : "no") << ' ';
|
||||||
|
if (false == m_state.memcell.empty())
|
||||||
|
{
|
||||||
|
Output << "memcell " << m_state.memcell << ' ';
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case kind_type::spawn:
|
case kind_type::spawn:
|
||||||
|
|||||||
@@ -25,19 +25,23 @@ class TDynamicObject;
|
|||||||
class basic_event;
|
class basic_event;
|
||||||
|
|
||||||
// a place on a road where something happens to the traffic, or is painted for it:
|
// a place on a road where something happens to the traffic, or is painted for it:
|
||||||
// - a level crossing: vehicles are stopped ahead of the rails for as long as a train is near,
|
// - a level crossing: vehicles are stopped ahead of the rails for as long as a train is near, or the scenery says so,
|
||||||
// - a point where vehicles appear, drawn from a set made ready while the scenery is loaded,
|
// - a point where vehicles appear, drawn from a set made ready while the scenery is loaded,
|
||||||
// - a point where vehicles are taken away. the ones which came from a point of the previous kind go back to its set,
|
// - a point where vehicles are taken away. the ones which came from a point of the previous kind go back to its set,
|
||||||
// - a pedestrian crossing: stripes painted across the road.
|
// - a pedestrian crossing: stripes painted across the road.
|
||||||
// scenery entries:
|
// scenery entries:
|
||||||
// node <max> <min> <name> crossing <position> [clearance <m>] [warning <m>] [stoplines <yes|no>] endcrossing
|
// node <max> <min> <name> crossing <position> [clearance <m>] [warning <m>] [stoplines <yes|no>] [memcell <name>] endcrossing
|
||||||
// node <max> <min> <name> spawn <position> [interval <s>] [variation <0..1>] [velocity <km/h>] [count <n>]
|
// node <max> <min> <name> spawn <position> [interval <s>] [variation <0..1>] [velocity <km/h>] [count <n>]
|
||||||
// [node <max> <min> <name> dynamic <folder> <skin> <type> <path> <offset> <driver> <velocity> <load> [<load type>] enddynamic]... endspawn
|
// [node <max> <min> <name> dynamic <folder> <skin> <type> <path> <offset> <driver> <velocity> <load> [<load type>] enddynamic]... endspawn
|
||||||
// node <max> <min> <name> despawn <position> [radius <m>] enddespawn
|
// node <max> <min> <name> despawn <position> [radius <m>] enddespawn
|
||||||
// node <max> <min> <name> crosswalk <position> [length <m>] endcrosswalk
|
// node <max> <min> <name> crosswalk <position> [length <m>] endcrosswalk
|
||||||
// the vehicles of a spawn point are given the way vehicles are put in a scenery; where they're put and how they're driven
|
// the vehicles of a spawn point are given the way vehicles are put in a scenery; where they're put and how they're driven
|
||||||
// is up to the point, so the path, the offset, the driver and the velocity of these entries don't matter. the listed vehicles
|
// is up to the point, so the path, the offset, the driver and the velocity of these entries don't matter. the listed vehicles
|
||||||
// are what the point draws from to have <count> vehicles of its own on the roads; one which gets stuck is taken off the road
|
// are what the point draws from to have <count> vehicles of its own on the roads; one which gets stuck is taken off the road.
|
||||||
|
// a level crossing is told by the scenery through a memory cell: the one named in its entry, or "<name of the crossing>:block",
|
||||||
|
// made if the scenery doesn't have it. its first value above zero closes the entry onto the crossing, below zero keeps it open
|
||||||
|
// whatever comes by the rails, and zero leaves it to the trains. the vehicles past the stops, on the crossing or leaving it, drive on.
|
||||||
|
// the second value is set by the crossing: 1 while its entry is closed, 0 while it's open
|
||||||
class roadpoint_node : public scene::basic_node
|
class roadpoint_node : public scene::basic_node
|
||||||
{
|
{
|
||||||
|
|
||||||
@@ -71,6 +75,7 @@ class roadpoint_node : public scene::basic_node
|
|||||||
float clearance{6.f}; // distance along the road from the point to the places the vehicles stop at
|
float clearance{6.f}; // distance along the road from the point to the places the vehicles stop at
|
||||||
float warning{800.f}; // how far along the rails a train coming closes the crossing
|
float warning{800.f}; // how far along the rails a train coming closes the crossing
|
||||||
bool stoplines{true}; // lines are painted where the vehicles stop
|
bool stoplines{true}; // lines are painted where the vehicles stop
|
||||||
|
std::string memcell; // name of the memory cell the scenery tells the crossing by; empty: the one named after the crossing
|
||||||
// point where vehicles appear
|
// point where vehicles appear
|
||||||
float interval{20.f}; // seconds from a vehicle to the next
|
float interval{20.f}; // seconds from a vehicle to the next
|
||||||
float variation{0.5f}; // part of the interval which is drawn at random
|
float variation{0.5f}; // part of the interval which is drawn at random
|
||||||
@@ -135,6 +140,13 @@ class roadpoint_node : public scene::basic_node
|
|||||||
{
|
{
|
||||||
return m_rails.size();
|
return m_rails.size();
|
||||||
}
|
}
|
||||||
|
// memory cell the level crossing is told by, nullptr if there's none
|
||||||
|
TMemCell *memcell() const
|
||||||
|
{
|
||||||
|
return m_memcell;
|
||||||
|
}
|
||||||
|
// name of that cell: the one the entry gives, or the one made of the name of the crossing; empty if neither is there
|
||||||
|
std::string memcell_name() const;
|
||||||
// vehicles made for a spawn point, and how many of them wait for their turn
|
// vehicles made for a spawn point, and how many of them wait for their turn
|
||||||
std::size_t vehicles() const
|
std::size_t vehicles() const
|
||||||
{
|
{
|
||||||
@@ -169,6 +181,7 @@ class roadpoint_node : public scene::basic_node
|
|||||||
void deserialize_(std::istream &Input) override;
|
void deserialize_(std::istream &Input) override;
|
||||||
void export_as_text_(std::ostream &Output) const override;
|
void export_as_text_(std::ostream &Output) const override;
|
||||||
void bind_crossing();
|
void bind_crossing();
|
||||||
|
void bind_memcell();
|
||||||
void update_crossing(double const Deltatime);
|
void update_crossing(double const Deltatime);
|
||||||
void update_spawn(double const Deltatime);
|
void update_spawn(double const Deltatime);
|
||||||
void update_despawn();
|
void update_despawn();
|
||||||
@@ -184,6 +197,7 @@ class roadpoint_node : public scene::basic_node
|
|||||||
std::map<TDynamicObject const *, double> m_seen; // rail vehicles near, with how far each was the last time
|
std::map<TDynamicObject const *, double> m_seen; // rail vehicles near, with how far each was the last time
|
||||||
bool m_closed{false};
|
bool m_closed{false};
|
||||||
double m_hold{0.0}; // time left for the crossing to stay closed after the last train was seen
|
double m_hold{0.0}; // time left for the crossing to stay closed after the last train was seen
|
||||||
|
TMemCell *m_memcell{nullptr}; // what the scenery tells the crossing by
|
||||||
double m_scan{0.0}; // time left to the next look at the rails
|
double m_scan{0.0}; // time left to the next look at the rails
|
||||||
// spawn and despawn
|
// spawn and despawn
|
||||||
std::vector<TTrack *> m_lanes; // spawn: the lane the vehicles are put on. despawn: lanes the vehicles are taken from
|
std::vector<TTrack *> m_lanes; // spawn: the lane the vehicles are put on. despawn: lanes the vehicles are taken from
|
||||||
|
|||||||
@@ -22,6 +22,31 @@ http://mozilla.org/MPL/2.0/.
|
|||||||
#include "vehicle/Driver.h"
|
#include "vehicle/Driver.h"
|
||||||
#include "utilities/Logs.h"
|
#include "utilities/Logs.h"
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
// how far the start of a way across a crossroads can be from the end of the path an axle leaves, to be where the axle gets on
|
||||||
|
constexpr double cross_reach { 1.0 };
|
||||||
|
|
||||||
|
// number of the ways across a crossroads; each can be taken either way
|
||||||
|
int cross_count( TTrack const &Cross ) {
|
||||||
|
|
||||||
|
return Cross.SwitchExtension->iRoads == 4 ? 6 : 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
// where the way of specified code starts, and the direction it leads in there. the code is the number of the way, 1..6,
|
||||||
|
// negative if the way is taken against its points
|
||||||
|
bool cross_start( TTrack const &Cross, int const Code, glm::dvec3 &Point, glm::dvec3 &Direction ) {
|
||||||
|
|
||||||
|
if( Code == 0 || std::abs( Code ) > cross_count( Cross ) ) { return false; }
|
||||||
|
auto const &segment { Cross.SwitchExtension->Segments[ std::abs( Code ) - 1 ] };
|
||||||
|
if( segment == nullptr ) { return false; }
|
||||||
|
Point = ( Code > 0 ? segment->FastGetPoint_0() : segment->FastGetPoint_1() );
|
||||||
|
Direction = ( Code > 0 ? segment->FastGetPoint( 0.05 ) : segment->FastGetPoint( 0.95 ) ) - Point;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
TTrackFollower::~TTrackFollower()
|
TTrackFollower::~TTrackFollower()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
@@ -30,6 +55,8 @@ bool TTrackFollower::Init(TTrack *pTrack, TDynamicObject *NewOwner, double fDir)
|
|||||||
{
|
{
|
||||||
fDirection = fDir;
|
fDirection = fDir;
|
||||||
Owner = NewOwner;
|
Owner = NewOwner;
|
||||||
|
// the axle is put on the track, it doesn't come from the one it was on before
|
||||||
|
pCurrentSegment = nullptr;
|
||||||
SetCurrentTrack(pTrack, 0);
|
SetCurrentTrack(pTrack, 0);
|
||||||
iEventFlag = 3; // na torze startowym również wykonać eventy 1/2
|
iEventFlag = 3; // na torze startowym również wykonać eventy 1/2
|
||||||
iEventallFlag = 3;
|
iEventallFlag = 3;
|
||||||
@@ -92,6 +119,9 @@ TTrack * TTrackFollower::SetCurrentTrack(TTrack *pTrack, int end)
|
|||||||
// kiedy powstaje!
|
// kiedy powstaje!
|
||||||
iSegment = 1; // doraźna poprawka
|
iSegment = 1; // doraźna poprawka
|
||||||
}
|
}
|
||||||
|
// the way has to start where the axle gets on. the one the driver picked may not: the driver may have looked
|
||||||
|
// again since the other axle got on, the vehicle may have no driver of its own, or it may be going backwards
|
||||||
|
iSegment = CrossEntry( *pTrack, iSegment, end );
|
||||||
if ((end ? iSegment : -iSegment) < 0)
|
if ((end ? iSegment : -iSegment) < 0)
|
||||||
fDirection = -fDirection; // wtórna zmiana
|
fDirection = -fDirection; // wtórna zmiana
|
||||||
pTrack->SwitchForced(abs(iSegment) - 1, nullptr); // wybór zapamiętanego segmentu
|
pTrack->SwitchForced(abs(iSegment) - 1, nullptr); // wybór zapamiętanego segmentu
|
||||||
@@ -123,6 +153,67 @@ TTrack * TTrackFollower::SetCurrentTrack(TTrack *pTrack, int end)
|
|||||||
return pCurrentTrack;
|
return pCurrentTrack;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// picks the way across the crossroads (Cross) for the axle which gets on it by specified end of the path it's on. it's the way
|
||||||
|
// taken by another axle of the vehicle, or of a vehicle coupled to it, which is on the crossroads already; or the one the driver
|
||||||
|
// picked (Wish); or, if neither starts where the axle gets on, the one leading the most straight on of the ways which start there
|
||||||
|
int TTrackFollower::CrossEntry( TTrack const &Cross, int const Wish, int const End ) const {
|
||||||
|
|
||||||
|
if( pCurrentSegment == nullptr || Cross.SwitchExtension == nullptr ) { return Wish; }
|
||||||
|
|
||||||
|
auto const joint { End ? pCurrentSegment->FastGetPoint_1() : pCurrentSegment->FastGetPoint_0() };
|
||||||
|
auto const starts_here = [&]( int const Code ) {
|
||||||
|
glm::dvec3 point, direction;
|
||||||
|
return cross_start( Cross, Code, point, direction ) && glm::distance( point, joint ) < cross_reach; };
|
||||||
|
|
||||||
|
if( Owner != nullptr ) {
|
||||||
|
for( auto const *vehicle : { Owner, Owner->Prev(), Owner->Next() } ) {
|
||||||
|
if( vehicle == nullptr ) { continue; }
|
||||||
|
for( auto const *axle : { &vehicle->Axle0, &vehicle->Axle1 } ) {
|
||||||
|
if( axle == this || axle->pCurrentTrack != &Cross || axle->iSegment == 0 ) { continue; }
|
||||||
|
if( starts_here( axle->iSegment ) ) { return axle->iSegment; }
|
||||||
|
// a vehicle going back over the crossroads gets on the way at its other end
|
||||||
|
if( starts_here( -axle->iSegment ) ) { return -axle->iSegment; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if( starts_here( Wish ) ) { return Wish; }
|
||||||
|
|
||||||
|
auto const incoming { joint - ( End ? pCurrentSegment->FastGetPoint( 0.95 ) : pCurrentSegment->FastGetPoint( 0.05 ) ) };
|
||||||
|
auto nearest { cross_reach };
|
||||||
|
for( int way = 1; way <= cross_count( Cross ); ++way ) {
|
||||||
|
for( auto const code : { way, -way } ) {
|
||||||
|
glm::dvec3 point, direction;
|
||||||
|
if( cross_start( Cross, code, point, direction ) ) {
|
||||||
|
nearest = std::min( nearest, glm::distance( point, joint ) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if( nearest >= cross_reach ) {
|
||||||
|
// no way starts there, the crossroads isn't joined with the path; it's left the way it was
|
||||||
|
return Wish;
|
||||||
|
}
|
||||||
|
auto result { Wish };
|
||||||
|
auto straightest { -2.0 };
|
||||||
|
for( int way = 1; way <= cross_count( Cross ); ++way ) {
|
||||||
|
for( auto const code : { way, -way } ) {
|
||||||
|
glm::dvec3 point, direction;
|
||||||
|
if( false == cross_start( Cross, code, point, direction )
|
||||||
|
|| glm::distance( point, joint ) > nearest + 0.05 ) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
auto const straight {
|
||||||
|
glm::length2( incoming ) > 0.0 && glm::length2( direction ) > 0.0 ?
|
||||||
|
glm::dot( glm::normalize( incoming ), glm::normalize( direction ) ) :
|
||||||
|
-1.0 };
|
||||||
|
if( straight > straightest ) {
|
||||||
|
straightest = straight;
|
||||||
|
result = code;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
bool TTrackFollower::Move(double fDistance, bool bPrimary)
|
bool TTrackFollower::Move(double fDistance, bool bPrimary)
|
||||||
{ // przesuwanie wózka po torach o odległość (fDistance), z wyzwoleniem eventów
|
{ // przesuwanie wózka po torach o odległość (fDistance), z wyzwoleniem eventów
|
||||||
// bPrimary=true - jest pierwszą osią w pojeździe, czyli generuje eventy i przepisuje pojazd
|
// bPrimary=true - jest pierwszą osią w pojeździe, czyli generuje eventy i przepisuje pojazd
|
||||||
|
|||||||
@@ -50,6 +50,7 @@ public:
|
|||||||
// methods
|
// methods
|
||||||
bool ComputatePosition(); // przeliczenie pozycji na torze
|
bool ComputatePosition(); // przeliczenie pozycji na torze
|
||||||
int JoinedEnd( int const End ) const;
|
int JoinedEnd( int const End ) const;
|
||||||
|
int CrossEntry( TTrack const &Cross, int const Wish, int const End ) const;
|
||||||
// members
|
// members
|
||||||
TTrack *pCurrentTrack = nullptr; // na którym torze siê znajduje
|
TTrack *pCurrentTrack = nullptr; // na którym torze siê znajduje
|
||||||
std::shared_ptr<TSegment> pCurrentSegment; // zwrotnice mog¹ mieæ dwa segmenty
|
std::shared_ptr<TSegment> pCurrentSegment; // zwrotnice mog¹ mieæ dwa segmenty
|
||||||
|
|||||||
Reference in New Issue
Block a user