From 96be688d1374d33234869625394bbc039d78364b Mon Sep 17 00:00:00 2001 From: pwarszawski Date: Wed, 30 Sep 2026 14:51:36 +0200 Subject: [PATCH 1/7] Send the reverser position, not the absolute direction, when activating a cab CabActivisation() passed DirAbsolute (DirActive * CabActive) as the value of the "Direction" command, which every receiving vehicle stores as its own DirActive. Every other sender passes DirActive. With the cab at the rear end (CabActive = -1) the two have opposite signs, so the other units got their reverser flipped against the controlling one. It shows whenever a rear cab is activated after the reverser has been set: - the AI takes over an occupied cab (TakeControl() calls CabActivisation(true)), e.g. an EN57 inserted with reardriver and a starting velocity: the motor car drives the train backwards; - a vehicle without automatic cab activation (SA134) driven by the AI from the start: the cab is activated by a driver hint after the reverser is set, and the two units pull against each other. Fixes #169. --- McZapkie/Mover.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/McZapkie/Mover.cpp b/McZapkie/Mover.cpp index 83c918e2..68b88918 100644 --- a/McZapkie/Mover.cpp +++ b/McZapkie/Mover.cpp @@ -2901,7 +2901,7 @@ bool TMoverParameters::CabActivisation(bool const Enforce) CabMaster = true; SecuritySystem.set_enabled(true); // activate the alerter TODO: make it part of control based cab selection SendCtrlToNext("CabActivisation", 1, CabActive); - SendCtrlToNext("Direction", DirAbsolute, CabActive); + SendCtrlToNext("Direction", DirActive, CabActive); if (InactiveCabFlag & activation::springbrakeoff) { SpringBrakeActivate(false); From f9deee5cfdd63db40ace5c555fdec13cf61cb24e Mon Sep 17 00:00:00 2001 From: Hirek193 Date: Thu, 1 Oct 2026 02:03:49 +0200 Subject: [PATCH 2/7] fix(editor): stuck brush after clicks on UI; add polygon area fill (#191) --- application/editormode.cpp | 619 +++++++++++++++++++++++++++++++-- application/editormode.h | 26 +- application/editoruilayer.cpp | 10 + application/editoruilayer.h | 2 + application/editoruipanels.cpp | 43 ++- application/editoruipanels.h | 7 +- 6 files changed, 674 insertions(+), 33 deletions(-) diff --git a/application/editormode.cpp b/application/editormode.cpp index 46a8edad..94b1424d 100644 --- a/application/editormode.cpp +++ b/application/editormode.cpp @@ -24,6 +24,8 @@ http://mozilla.org/MPL/2.0/. #include "model/Model3d.h" #include "utilities/Float3d.h" #include "scene/scene.h" +#include "utilities/parser.h" +#include "utilities/utilities.h" #include "imgui/imgui.h" @@ -58,6 +60,14 @@ namespace return state == GLFW_PRESS; } + // true when the most recent left button press landed in the viewport rather than on an ImGui window. + // valid once ImGui has processed the press (i.e. from mode update onwards), so it's also used to + // discard deferred pick callbacks of a press that leaked past the application's UI filter + inline bool viewport_click() + { + return false == ImGui::GetIO().MouseDownOwned[0]; + } + // tests whether the vertical line through (Px,Pz) passes over triangle abc; if so returns the // surface height at that point through OutY. used by the "snap to ground" (END) feature. inline bool triangle_height_at(glm::dvec3 const &a, glm::dvec3 const &b, glm::dvec3 const &c, @@ -117,6 +127,158 @@ namespace } } + // world transform of a model instance, matching the renderer: translate * rotateY * rotateX * rotateZ * scale + glm::dmat4 instance_matrix(TAnimModel const &Model) + { + glm::dmat4 rootm(1.0); + rootm = glm::translate(rootm, Model.location()); + glm::vec3 const angles = Model.Angles(); + if (angles.y != 0.0f) rootm = glm::rotate(rootm, glm::radians(static_cast(angles.y)), glm::dvec3(0.0, 1.0, 0.0)); + if (angles.x != 0.0f) rootm = glm::rotate(rootm, glm::radians(static_cast(angles.x)), glm::dvec3(1.0, 0.0, 0.0)); + if (angles.z != 0.0f) rootm = glm::rotate(rootm, glm::radians(static_cast(angles.z)), glm::dvec3(0.0, 0.0, 1.0)); + return glm::scale(rootm, glm::dvec3(Model.Scale())); + } + + // even-odd test of a point against a polygon outline projected onto the XZ plane + bool point_in_polygon_xz(std::vector const &Polygon, double const X, double const Z) + { + bool inside = false; + for (std::size_t i = 0, j = Polygon.size() - 1; i < Polygon.size(); j = i++) + { + auto const &a = Polygon[i]; + auto const &b = Polygon[j]; + if ((a.z > Z) != (b.z > Z) && X < (b.x - a.x) * (Z - a.z) / (b.z - a.z) + a.x) + inside = !inside; + } + return inside; + } + + // area of a polygon outline projected onto the XZ plane (shoelace formula) + double polygon_area_xz(std::vector const &Polygon) + { + if (Polygon.size() < 3) + return 0.0; + double sum = 0.0; + for (std::size_t i = 0, j = Polygon.size() - 1; i < Polygon.size(); j = i++) + sum += Polygon[j].x * Polygon[i].z - Polygon[i].x * Polygon[j].z; + return std::abs(sum) * 0.5; + } + + // short readable name of a node template (model file and texture), like the node bank shows it + std::string template_label(std::string const &Template) + { + cParser tokenizer(Template); + tokenizer.getTokens(9, false); // node, ranges, name, type, position, rotation + auto model{tokenizer.getToken(false)}; + auto texture{tokenizer.getToken(false)}; + replace_slashes(model); + erase_extension(model); + replace_slashes(texture); + return texture == "none" || texture.empty() ? model : model + " (" + texture + ")"; + } + + // world triangles bucketed on a uniform XZ grid, for many ground height queries over one area + class triangle_grid + { + public: + triangle_grid(std::vector Triangles, glm::dvec2 const &Min, glm::dvec2 const &Max) : m_triangles(std::move(Triangles)), m_min(Min) + { + glm::dvec2 const extent = glm::max(Max - Min, glm::dvec2(1.0)); + // ~8 m cells, coarser for big areas so large terrain triangles don't flood the grid + m_cellsize = std::max(8.0, std::max(extent.x, extent.y) / 128.0); + m_columns = static_cast(std::ceil(extent.x / m_cellsize)) + 1; + m_rows = static_cast(std::ceil(extent.y / m_cellsize)) + 1; + m_cells.resize(static_cast(m_columns) * m_rows); + + for (std::uint32_t idx = 0; idx < m_triangles.size(); ++idx) + { + auto const &t = m_triangles[idx]; + int const c0 = column(std::min({t[0].x, t[1].x, t[2].x})); + int const c1 = column(std::max({t[0].x, t[1].x, t[2].x})); + int const r0 = row(std::min({t[0].z, t[1].z, t[2].z})); + int const r1 = row(std::max({t[0].z, t[1].z, t[2].z})); + for (int r = r0; r <= r1; ++r) + for (int c = c0; c <= c1; ++c) + m_cells[static_cast(r) * m_columns + c].push_back(idx); + } + } + + // highest surface at (X,Z) that isn't above Ceiling. returns: true if there is one + bool height_at(double const X, double const Z, double const Ceiling, double &OutY) const + { + bool found = false; + for (auto const idx : m_cells[static_cast(row(Z)) * m_columns + column(X)]) + { + auto const &t = m_triangles[idx]; + double y; + if (triangle_height_at(t[0], t[1], t[2], X, Z, y) && y <= Ceiling && (!found || y > OutY)) + { + OutY = y; + found = true; + } + } + return found; + } + + private: + int column(double const X) const + { + return std::clamp(static_cast(std::floor((X - m_min.x) / m_cellsize)), 0, m_columns - 1); + } + int row(double const Z) const + { + return std::clamp(static_cast(std::floor((Z - m_min.y) / m_cellsize)), 0, m_rows - 1); + } + + std::vector m_triangles; + std::vector> m_cells; + glm::dvec2 m_min; + double m_cellsize{8.0}; + int m_columns{1}; + int m_rows{1}; + }; + + // appends triangles of a shape node which overlap specified XZ rectangle + void gather_shape_triangles(scene::shape_node const &Shape, glm::dvec2 const &Min, glm::dvec2 const &Max, std::vector &Out) + { + auto const &data = Shape.data(); + if (data.area.radius >= 0.0f) + { + double const r = data.area.radius; + if (data.area.center.x + r < Min.x || data.area.center.x - r > Max.x || data.area.center.z + r < Min.y || data.area.center.z - r > Max.y) + return; + } + + auto const add = [&](glm::dvec3 const &a, glm::dvec3 const &b, glm::dvec3 const &c) { + if (std::max({a.x, b.x, c.x}) < Min.x || std::min({a.x, b.x, c.x}) > Max.x || std::max({a.z, b.z, c.z}) < Min.y || std::min({a.z, b.z, c.z}) > Max.y) + return; + Out.push_back({a, b, c}); + }; + + if (false == data.vertices.empty()) + { + for (std::size_t i = 0; i + 2 < data.vertices.size(); i += 3) + add(data.vertices[i].position, data.vertices[i + 1].position, data.vertices[i + 2].position); + return; + } + // once the shape is uploaded its world-space source is released; the renderer keeps an origin-relative copy + if (data.geometry.bank == 0 && data.geometry.chunk == 0) + return; + auto const &verts = GfxRenderer->Vertices(data.geometry); + auto const &indices = GfxRenderer->Indices(data.geometry); + auto const to_world = [&](gfx::basic_vertex const &v) { return data.origin + glm::dvec3(v.position); }; + if (false == indices.empty()) + { + for (std::size_t i = 0; i + 2 < indices.size(); i += 3) + add(to_world(verts[indices[i]]), to_world(verts[indices[i + 1]]), to_world(verts[indices[i + 2]])); + } + else + { + for (std::size_t i = 0; i + 2 < verts.size(); i += 3) + add(to_world(verts[i]), to_world(verts[i + 1]), to_world(verts[i + 2])); + } + } + } bool editor_mode::editormode_input::init() @@ -306,32 +468,38 @@ void editor_mode::handle_brush_mouse_hold(int Action, int Button) GfxRenderer->Pick_Node_Callback( [this, mode, rotation_mode, fixed_rotation_value, Action, Button](scene::basic_node * /*node*/) { + // picks are resolved a few frames later; drop the ones queued before the button went up, + // or for a press that turned out to belong to the UI + if (!mouseHold || !viewport_click()) + return; + const std::string *src = ui()->get_active_node_template(); if (!src) return; std::string name = "editor_"; - glm::dvec3 newPos = clamp_mouse_offset_to_max(GfxRenderer->Mouse_Position()); - double distance = glm::distance(newPos, oldPos); - if (distance < ui()->getSpacing()) + // spacing is measured in world space: the mouse offset is camera-relative and the camera may move mid-stroke + glm::dvec3 const newPos = Camera.Pos + clamp_mouse_offset_to_max(GfxRenderer->Mouse_Position()); + if (m_brush_has_last && glm::distance(newPos, oldPos) < ui()->getSpacing()) return; - TAnimModel *cloned = simulation::State.create_model(*src, name, Camera.Pos + newPos); + TAnimModel *cloned = simulation::State.create_model(*src, name, newPos); oldPos = newPos; + m_brush_has_last = true; if (!cloned) return; std::string new_name = "editor_" + cloned->uuid.to_string(); cloned->m_name = new_name; - + apply_rotation_for_new_node(cloned, rotation_mode, fixed_rotation_value); + std::string as_text; cloned->export_as_text(as_text); push_snapshot(cloned, EditorSnapshot::Action::Add, as_text); m_node = cloned; - apply_rotation_for_new_node(m_node, rotation_mode, fixed_rotation_value); ui()->set_node(m_node); }); } @@ -439,6 +607,9 @@ void editor_mode::nullify_history_pointers(scene::basic_node *node) if (s.node_ptr == node) s.node_ptr = nullptr; } + + // deleted nodes also drop out of the "undo last fill" set + m_fill_last.erase(std::remove(m_fill_last.begin(), m_fill_last.end(), node), m_fill_last.end()); } void editor_mode::undo_last() @@ -626,6 +797,19 @@ bool editor_mode::update() Application.set_cursor(GLFW_CURSOR_NORMAL); } + // same for the left button (brush / sculpt / click placement). the UI filter in the application + // uses io.WantCaptureMouse from the previous frame, so a press on a panel the cursor has just + // moved onto can leak into the viewport, and its release is then swallowed by the UI; the brush + // would keep painting with the button up. by now ImGui has processed this frame's input, so + // MouseDown is the real button state and MouseDownOwned tells whether the press hit the UI. + // (pick callbacks queued by such a press are discarded separately, see viewport_click()) + if (mouseHold && (!ImGui::GetIO().MouseDown[0] || !viewport_click())) + { + mouseHold = false; + m_dragging = false; + m_input.mouse.button(GLFW_MOUSE_BUTTON_LEFT, GLFW_RELEASE); + } + // fixed step render time routines (50 Hz) fTime50Hz += deltarealtime; // accumulate even when paused to keep frame reads stable while (fTime50Hz >= 1.0 / 50.0) @@ -708,6 +892,13 @@ bool editor_mode::update() // --- ImGuizmo: in-viewport transform gizmo for the selected node --- render_gizmo(); + // --- area fill: outline overlay and settings, while the mode is active --- + if (ui()->mode() == nodebank_panel::FILL) + { + draw_area_fill_outline(); + render_area_fill(); + } + // --- ImGui: Editor Settings & History windows --- if(m_settings_open) render_settings(); @@ -1097,18 +1288,8 @@ void editor_mode::capture_terrain() return; } - // instance world transform, matching the renderer: translate * rotateY * rotateX * rotateZ * scale - glm::dmat4 rootm(1.0); - rootm = glm::translate(rootm, model->location()); - glm::vec3 const angles = model->Angles(); - if (angles.y != 0.0f) rootm = glm::rotate(rootm, glm::radians(static_cast(angles.y)), glm::dvec3(0.0, 1.0, 0.0)); - if (angles.x != 0.0f) rootm = glm::rotate(rootm, glm::radians(static_cast(angles.x)), glm::dvec3(1.0, 0.0, 0.0)); - if (angles.z != 0.0f) rootm = glm::rotate(rootm, glm::radians(static_cast(angles.z)), glm::dvec3(0.0, 0.0, 1.0)); - glm::vec3 const scale = model->Scale(); - rootm = glm::scale(rootm, glm::dvec3(scale)); - std::vector tris; - gather_submodel_triangles(model->pModel->Root, rootm, tris); + gather_submodel_triangles(model->pModel->Root, instance_matrix(*model), tris); if (tris.empty()) { WriteLog("Editor: selected model has no readable geometry to capture", logtype::generic); @@ -1174,6 +1355,366 @@ void editor_mode::capture_terrain() simulation::State.delete_model(model); } +void editor_mode::add_area_fill_point() +{ + // like the other placement tools, the point comes from the depth buffer under the cursor + // (ground geometry only), relative to the camera + glm::dvec3 const offset = GfxRenderer->Mouse_Position(); + if (glm::length(offset) < 1e-3) + return; // no ground under the cursor (yet) + + m_fill_points.push_back(Camera.Pos + offset); +} + +void editor_mode::undo_last_area_fill() +{ + // nullify_history_pointers() removes deleted nodes from m_fill_last, so work on a copy + auto const created = std::move(m_fill_last); + m_fill_last.clear(); + + for (TAnimModel *model : created) + { + if (model == nullptr) + continue; + if (m_node == model) + { + m_node = nullptr; + ui()->set_node(nullptr); + } + nullify_history_pointers(model); + remove_from_hierarchy(model); + simulation::State.delete_model(model); + } + m_fill_status = "Removed " + std::to_string(created.size()) + " objects of the last fill"; +} + +void editor_mode::run_area_fill() +{ + // limits keeping a single click from freezing the editor for too long + std::size_t constexpr max_objects = 20000; + // large model instances (e.g. terrain tiles) are treated as ground, smaller ones (houses, trees) are not + float constexpr terrain_model_radius = 50.0f; + // ground is searched up to this far above the height interpolated from the outline points, + // so hills inside the outline are found but bridges or roofs high above it are not + double constexpr height_tolerance = 50.0; + + if (m_fill_points.size() < 3) + { + m_fill_status = "Outline needs at least 3 points"; + return; + } + + // model package + std::vector package; + if (m_fill_package == 0) + package = m_fill_custom; + else + package = ui()->nodebank_group_templates(static_cast(m_fill_package - 1)); + package.erase(std::remove_if(package.begin(), package.end(), [](std::string const &Template) { return Template.empty(); }), package.end()); + if (package.empty()) + { + m_fill_status = "Selected model package is empty"; + return; + } + + // area bounds + glm::dvec2 bmin(std::numeric_limits::max()); + glm::dvec2 bmax(-std::numeric_limits::max()); + for (auto const &p : m_fill_points) + { + bmin = glm::min(bmin, glm::dvec2(p.x, p.z)); + bmax = glm::max(bmax, glm::dvec2(p.x, p.z)); + } + double const area = polygon_area_xz(m_fill_points); + auto const target = static_cast(std::min(std::round(area / 10000.0 * std::max(0.0f, m_fill_density)), max_objects)); + if (target == 0) + { + m_fill_status = "Density too low for this area, nothing to place"; + return; + } + + // scatter points: random samples inside the outline, rejecting the ones closer than the minimal spacing + // to an already accepted point (checked on a hashed grid with cell size == spacing) + double const spacing = std::max(0.0f, m_fill_min_spacing); + std::unordered_map> occupancy; + auto const cell_key = [](std::int64_t const X, std::int64_t const Z) { return (X << 32) ^ (Z & 0xffffffff); }; + std::vector points; + points.reserve(target); + for (std::size_t attempt = 0; attempt < target * 30 && points.size() < target; ++attempt) + { + glm::dvec2 const p(LocalRandom(bmin.x, bmax.x), LocalRandom(bmin.y, bmax.y)); + if (false == point_in_polygon_xz(m_fill_points, p.x, p.y)) + continue; + if (spacing > 0.0) + { + auto const cx = static_cast(std::floor(p.x / spacing)); + auto const cz = static_cast(std::floor(p.y / spacing)); + bool free = true; + for (std::int64_t dz = -1; dz <= 1 && free; ++dz) + for (std::int64_t dx = -1; dx <= 1 && free; ++dx) + { + auto const lookup = occupancy.find(cell_key(cx + dx, cz + dz)); + if (lookup == occupancy.end()) + continue; + for (auto const idx : lookup->second) + if (glm::distance(points[idx], p) < spacing) + { + free = false; + break; + } + } + if (!free) + continue; + occupancy[cell_key(cx, cz)].push_back(points.size()); + } + points.push_back(p); + } + + // ground geometry within the area. shapes hold the (legacy) terrain, large instances the terrain tiles + std::vector triangles; + glm::dvec2 const center = (bmin + bmax) * 0.5; + float const radius = static_cast(glm::length(bmax - bmin) * 0.5); + auto const sections = simulation::Region->sections(glm::dvec3(center.x, 0.0, center.y), radius); // copy, the result is a scratchpad + for (auto *section : sections) + { + for (auto const &shape : section->m_shapes) + gather_shape_triangles(shape, bmin, bmax, triangles); + for (auto &cell : section->m_cells) + { + double const r = cell.m_area.radius; + if (cell.m_area.center.x + r < bmin.x || cell.m_area.center.x - r > bmax.x || cell.m_area.center.z + r < bmin.y || cell.m_area.center.z - r > bmax.y) + continue; + for (auto const &shape : cell.m_shapesopaque) + gather_shape_triangles(shape, bmin, bmax, triangles); + if (false == m_fill_models_as_ground) + continue; + for (auto *instance : cell.m_instancesopaque) + { + if (instance == nullptr || instance->Model() == nullptr || instance->radius() < terrain_model_radius) + continue; + std::vector modeltriangles; + gather_submodel_triangles(instance->Model()->Root, instance_matrix(*instance), modeltriangles); + for (auto const &t : modeltriangles) + if (std::max({t[0].x, t[1].x, t[2].x}) >= bmin.x && std::min({t[0].x, t[1].x, t[2].x}) <= bmax.x && std::max({t[0].z, t[1].z, t[2].z}) >= bmin.y && + std::min({t[0].z, t[1].z, t[2].z}) <= bmax.y) + triangles.push_back(t); + } + } + } + triangle_grid const ground(std::move(triangles), bmin, bmax); + auto const terrains = active_terrains(); + + auto const rotation_mode = m_fill_random_rotation ? functions_panel::RANDOM : ui()->rot_mode(); + auto const fixed_rotation_value = ui()->rot_val(); + float const scalemin = std::max(0.01f, std::min(m_fill_scale_min, m_fill_scale_max)); + float const scalemax = std::max(0.01f, std::max(m_fill_scale_min, m_fill_scale_max)); + + std::vector created; + created.reserve(points.size()); + std::size_t unsupported = 0; + for (auto const &p : points) + { + // reference height: the outline points, weighted by inverse squared distance + double weights = 0.0, reference = 0.0; + for (auto const &v : m_fill_points) + { + double const w = 1.0 / std::max(1e-6, glm::length2(glm::dvec2(v.x, v.z) - p)); + weights += w; + reference += w * v.y; + } + reference /= weights; + double const ceiling = reference + height_tolerance; + + double y = -std::numeric_limits::max(); + bool found = ground.height_at(p.x, p.y, ceiling, y); + for (editor_terrain *terrain : terrains) + { + if (!terrain->contains(p.x, p.y)) + continue; + double const h = terrain->height_at(p.x, p.y); + if (h <= ceiling && (!found || h > y)) + { + y = h; + found = true; + } + } + if (!found) + { + y = reference; + ++unsupported; + } + + glm::dvec3 const location(p.x, y, p.y); + if (!simulation::Region->point_inside(location)) + continue; + + auto const &src = package[package.size() > 1 ? std::min(package.size() - 1, static_cast(LocalRandom(0.0, static_cast(package.size())))) : 0]; + TAnimModel *cloned = simulation::State.create_model(src, "editor_", location); + if (!cloned) + continue; + cloned->m_name = "editor_" + cloned->uuid.to_string(); + apply_rotation_for_new_node(cloned, rotation_mode, fixed_rotation_value); + if (scalemin != 1.0f || scalemax != 1.0f) + cloned->Scale(static_cast(LocalRandom(scalemin, scalemax))); + created.push_back(cloned); + } + + // a new fill replaces the "last fill" undo set; the previous objects stay in the scene + m_fill_last = std::move(created); + + m_fill_status = "Placed " + std::to_string(m_fill_last.size()) + " of " + std::to_string(target) + " objects"; + if (points.size() < target) + m_fill_status += " (limited by min. spacing)"; + if (unsupported > 0) + m_fill_status += ", " + std::to_string(unsupported) + " without ground found (height interpolated)"; + WriteLog("Editor: area fill - " + m_fill_status, logtype::generic); +} + +void editor_mode::draw_area_fill_outline() const +{ + if (m_fill_points.empty()) + return; + + // same camera-relative view and clean perspective the transform gizmo uses + ImGuiIO const &io = ImGui::GetIO(); + glm::mat4 const view = GfxRenderer->Camera_View_Matrix(); + glm::dvec3 const camerapos = GfxRenderer->Camera_Position(); + float const fovy = glm::radians(Global.FieldOfView / Global.ZoomFactor); + float const aspect = io.DisplaySize.y > 0.0f ? io.DisplaySize.x / io.DisplaySize.y : 1.0f; + glm::mat4 const viewprojection = glm::perspective(fovy, aspect, 0.1f, 10000.0f) * view; + + auto const to_clip = [&](glm::dvec3 const &Point) { return viewprojection * glm::vec4(glm::vec3(Point - camerapos), 1.0f); }; + auto const to_screen = [&](glm::vec4 const &Clip) { + return ImVec2((Clip.x / Clip.w * 0.5f + 0.5f) * io.DisplaySize.x, (0.5f - Clip.y / Clip.w * 0.5f) * io.DisplaySize.y); + }; + float constexpr nearw = 0.1f; + + ImDrawList *drawlist = ImGui::GetBackgroundDrawList(); + ImU32 const edgecolor = IM_COL32(255, 200, 40, 230); + ImU32 const closingcolor = IM_COL32(255, 200, 40, 110); + + std::size_t const count = m_fill_points.size(); + for (std::size_t i = 0; i < count; ++i) + { + bool const closing = (i + 1 == count); + if (closing && count < 3) + break; + glm::vec4 a = to_clip(m_fill_points[i]); + glm::vec4 b = to_clip(m_fill_points[(i + 1) % count]); + // clip the edge against the near plane, so points behind the camera don't flip across the screen + if (a.w < nearw && b.w < nearw) + continue; + if (a.w < nearw) + a = glm::mix(a, b, (nearw - a.w) / (b.w - a.w)); + else if (b.w < nearw) + b = glm::mix(b, a, (nearw - b.w) / (a.w - b.w)); + drawlist->AddLine(to_screen(a), to_screen(b), closing ? closingcolor : edgecolor, 2.0f); + } + for (std::size_t i = 0; i < count; ++i) + { + glm::vec4 const c = to_clip(m_fill_points[i]); + if (c.w < nearw) + continue; + drawlist->AddCircleFilled(to_screen(c), i == 0 ? 6.0f : 4.0f, i == 0 ? IM_COL32(255, 90, 40, 255) : edgecolor); + } +} + +void editor_mode::render_area_fill() +{ + ImGui::Begin("Area fill", nullptr, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoFocusOnAppearing); + + ImGui::TextUnformatted("LMB: add outline point, Backspace: remove last point"); + double const area = polygon_area_xz(m_fill_points); + ImGui::Text("Points: %zu Area: %.0f m2 (%.2f ha)", m_fill_points.size(), area, area / 10000.0); + if (ImGui::Button("Remove last point") && !m_fill_points.empty()) + m_fill_points.pop_back(); + ImGui::SameLine(); + if (ImGui::Button("Clear outline")) + m_fill_points.clear(); + + ImGui::Separator(); + + // model package: a node bank group, or a custom set assembled from node bank templates + auto const groups = ui()->nodebank_groups(); + m_fill_package = std::clamp(m_fill_package, 0, static_cast(groups.size())); + std::string const current = m_fill_package == 0 ? std::string("Custom set") : groups[m_fill_package - 1]; + ImGui::SetNextItemWidth(250.0f); + if (ImGui::BeginCombo("Model package", current.c_str())) + { + if (ImGui::Selectable("Custom set", m_fill_package == 0)) + m_fill_package = 0; + for (int i = 0; i < static_cast(groups.size()); ++i) + { + auto const label = groups[i] + "##fillgroup" + std::to_string(i); + if (ImGui::Selectable(label.c_str(), m_fill_package == i + 1)) + m_fill_package = i + 1; + } + ImGui::EndCombo(); + } + + if (m_fill_package == 0) + { + ImGui::BeginChild("fill_custom_set", ImVec2(350.0f, 110.0f), true); + for (int i = 0; i < static_cast(m_fill_custom.size()); ++i) + { + auto const label = template_label(m_fill_custom[i]) + "##fillcustom" + std::to_string(i); + if (ImGui::Selectable(label.c_str(), m_fill_custom_idx == i)) + m_fill_custom_idx = i; + } + ImGui::EndChild(); + if (ImGui::Button("Add selected template")) + { + // the node bank selection, not a random pick from the brush set + auto const *src = ui()->get_active_node_template(true); + if (src && !src->empty()) + m_fill_custom.push_back(*src); + } + ImGui::SameLine(); + if (ImGui::Button("Remove") && m_fill_custom_idx >= 0 && m_fill_custom_idx < static_cast(m_fill_custom.size())) + { + m_fill_custom.erase(m_fill_custom.begin() + m_fill_custom_idx); + m_fill_custom_idx = -1; + } + ImGui::SameLine(); + if (ImGui::Button("Clear set")) + { + m_fill_custom.clear(); + m_fill_custom_idx = -1; + } + } + else + { + ImGui::Text("Templates in group: %zu", ui()->nodebank_group_templates(static_cast(m_fill_package - 1)).size()); + } + + ImGui::Separator(); + + ImGui::SetNextItemWidth(120.0f); + ImGui::InputFloat("Density (objects/ha)", &m_fill_density, 10.0f, 100.0f, "%.1f"); + m_fill_density = std::max(0.0f, m_fill_density); + ImGui::SetNextItemWidth(120.0f); + ImGui::InputFloat("Min. spacing (m)", &m_fill_min_spacing, 0.5f, 2.0f, "%.1f"); + m_fill_min_spacing = std::max(0.0f, m_fill_min_spacing); + ImGui::SetNextItemWidth(200.0f); + ImGui::DragFloatRange2("Scale", &m_fill_scale_min, &m_fill_scale_max, 0.01f, 0.1f, 5.0f, "min %.2f", "max %.2f"); + ImGui::Checkbox("Random rotation (off: Functions panel setting)", &m_fill_random_rotation); + ImGui::Checkbox("Large models count as ground (terrain tiles)", &m_fill_models_as_ground); + + ImGui::Text("Estimated objects: %.0f", std::round(area / 10000.0 * m_fill_density)); + + if (ImGui::Button("Fill area")) + run_area_fill(); + ImGui::SameLine(); + std::string const undolabel = "Undo last fill (" + std::to_string(m_fill_last.size()) + ")"; + if (ImGui::Button(undolabel.c_str()) && !m_fill_last.empty()) + undo_last_area_fill(); + + if (!m_fill_status.empty()) + ImGui::TextUnformatted(m_fill_status.c_str()); + + ImGui::End(); +} + void editor_mode::render_gizmo() { // the transform gizmo is suppressed while editing terrain, so the brush/chunk tool owns the mouse @@ -1381,6 +1922,7 @@ void editor_mode::exit() g_redo.clear(); m_history.clear(); + m_fill_last.clear(); // drop selection so a stale/dangling node pointer isn't used on the next editor session m_node = nullptr; @@ -1496,6 +2038,12 @@ void editor_mode::on_key(int const Key, int const Scancode, int const Action, in } break; + case GLFW_KEY_BACKSPACE: + // area fill: remove the last outline point + if (is_press(Action) && ui()->mode() == nodebank_panel::FILL && !m_fill_points.empty()) + m_fill_points.pop_back(); + break; + case GLFW_KEY_F: if (is_press(Action)) { @@ -1565,11 +2113,30 @@ void editor_mode::on_mouse_button(int const Button, int const Action, int const if (is_press(Action)) { mouseHold = true; - m_node = nullptr; + m_brush_has_last = false; // every brush stroke starts with a placement - // delegate node picking behaviour depending on current panel mode + // in area fill mode the left button only adds outline points, it doesn't touch the selection + if (mode == nodebank_panel::FILL) + { + GfxRenderer->Pick_Node_Callback([this](scene::basic_node * /*node*/) { + if (viewport_click()) + add_area_fill_point(); + }); + m_input.mouse.button(Button, Action); + return; + } + + // delegate node picking behaviour depending on current panel mode. + // NOTE: the pick is resolved a few frames later, often after the button was already released, + // so the callback mustn't depend on the button still being held GfxRenderer->Pick_Node_Callback( [this, mode, rotation_mode, fixed_rotation_value](scene::basic_node *node) { + // the press turned out to be meant for the UI + if (!viewport_click()) + return; + + m_node = nullptr; + // ignore picks that are beyond allowed placement distance if (node) { double const dist = glm::distance(node->location(), glm::dvec3{Global.pCamera.Pos}); @@ -1578,9 +2145,6 @@ void editor_mode::on_mouse_button(int const Button, int const Action, int const } if (mode == nodebank_panel::MODIFY) { - if (!m_dragging) - return; - m_node = node; ui()->set_node(m_node); } @@ -1607,19 +2171,16 @@ void editor_mode::on_mouse_button(int const Button, int const Action, int const if (!cloned) return; - // record addition for undo - std::string as_text; std::string new_name = "editor_" + cloned->uuid.to_string(); - cloned->m_name = new_name; + apply_rotation_for_new_node(cloned, rotation_mode, fixed_rotation_value); + + // record addition for undo (after rotation, so a redo recreates the same thing) + std::string as_text; cloned->export_as_text(as_text); push_snapshot(cloned, EditorSnapshot::Action::Add, as_text); - if (!m_dragging) - return; - m_node = cloned; - apply_rotation_for_new_node(m_node, rotation_mode, fixed_rotation_value); ui()->set_node(m_node); } }); diff --git a/application/editormode.h b/application/editormode.h index 29b1ad69..413b4225 100644 --- a/application/editormode.h +++ b/application/editormode.h @@ -19,6 +19,10 @@ http://mozilla.org/MPL/2.0/. #include "editor/editorTerrainStreamer.hpp" #include +#include +#include + +class TAnimModel; class editor_mode : public application_mode { @@ -122,7 +126,8 @@ class editor_mode : public application_mode scene::basic_node *m_node{nullptr}; // currently selected scene node bool m_takesnapshot{true}; // helper, hints whether snapshot of selected node(s) should be taken before modification bool m_dragging = false; - glm::dvec3 oldPos; + glm::dvec3 oldPos; // world position of the last brush placement + bool m_brush_has_last{false}; // false at the start of a brush stroke, so the first placement ignores spacing bool mouseHold{false}; float kMaxPlacementDistance = 200.0f; static bool m_change_history; @@ -196,6 +201,25 @@ class editor_mode : public application_mode void render_change_history(); void render_settings(); + // area fill: scatters models from a package over a polygon outlined in the viewport + void render_area_fill(); + void draw_area_fill_outline() const; + void add_area_fill_point(); + void run_area_fill(); + void undo_last_area_fill(); + std::vector m_fill_points; // outline vertices (world space) in click order + std::vector m_fill_last; // instances created by the most recent fill, for "Undo last fill" + std::vector m_fill_custom; // user-assembled package of node templates + int m_fill_custom_idx{-1}; + int m_fill_package{0}; // 0 = custom set, N = nodebank group N-1 + float m_fill_density{200.0f}; // objects per hectare + float m_fill_min_spacing{2.0f}; // minimal distance between placed objects (m) + bool m_fill_random_rotation{true}; // random yaw; otherwise the Functions panel rotation settings apply + float m_fill_scale_min{1.0f}; + float m_fill_scale_max{1.0f}; + bool m_fill_models_as_ground{true}; // large model instances (terrain tiles) count as ground + std::string m_fill_status; // result of the last fill, shown in the panel + // ImGuizmo-based transform gizmo for the selected node enum class gizmo_operation { translate, rotate, scale }; void render_gizmo(); diff --git a/application/editoruilayer.cpp b/application/editoruilayer.cpp index d87dd8fa..b00b1e51 100644 --- a/application/editoruilayer.cpp +++ b/application/editoruilayer.cpp @@ -55,6 +55,16 @@ void editor_ui::toggleBrushSettings(bool isVisible) m_brushobjects.is_open = isVisible; } +std::vector editor_ui::nodebank_groups() const +{ + return m_nodebankpanel.group_names(); +} + +std::vector editor_ui::nodebank_group_templates(std::size_t const Group) const +{ + return m_nodebankpanel.group_templates(Group); +} + void editor_ui::set_node(scene::basic_node *Node) { m_node = Node; diff --git a/application/editoruilayer.h b/application/editoruilayer.h index ad3d5447..57d75466 100644 --- a/application/editoruilayer.h +++ b/application/editoruilayer.h @@ -37,6 +37,8 @@ class editor_ui : public ui_layer nodebank_panel::edit_mode mode(); float getSpacing(); void toggleBrushSettings(bool isVisible); + std::vector nodebank_groups() const; + std::vector nodebank_group_templates(std::size_t const Group) const; private: // members diff --git a/application/editoruipanels.cpp b/application/editoruipanels.cpp index cbfe0ae7..80eec0ba 100644 --- a/application/editoruipanels.cpp +++ b/application/editoruipanels.cpp @@ -434,13 +434,13 @@ void brush_object_list::render() { Objects.erase(Objects.begin() + idx); } - if (ImGui::Button("Remove all") && idx >= 0 && idx < Objects.size()) + if (ImGui::Button("Remove all")) { Objects.clear(); } } - ImGui::End(); } + ImGui::End(); } nodebank_panel::nodebank_panel(std::string const &Name, bool const Isopen) : ui_panel(Name, Isopen) @@ -535,6 +535,8 @@ void nodebank_panel::render() ImGui::SameLine(); ImGui::RadioButton("Copy to bank", (int *)&mode, COPY); ImGui::SameLine(); + ImGui::RadioButton("Area fill", (int *)&mode, FILL); + ImGui::SameLine(); if (ImGui::Button("Reload Nodebank")) { nodebank_reload(); @@ -598,6 +600,43 @@ const std::string *nodebank_panel::get_active_template() return m_selectedtemplate.get(); } +std::vector nodebank_panel::group_names() const +{ + std::vector names; + for (auto const &entry : m_nodebank) + { + if (entry.second->empty()) + names.emplace_back(entry.first); + else if (names.empty()) + names.emplace_back("(ungrouped)"); // templates listed before the first header + } + return names; +} + +std::vector nodebank_panel::group_templates(std::size_t const Group) const +{ + std::vector templates; + std::size_t group{0}; + bool started{false}; + for (auto const &entry : m_nodebank) + { + if (entry.second->empty()) + { + // header: opens the next group, unless it's the very first entry + if (started) + ++group; + started = true; + continue; + } + started = true; + if (group == Group) + templates.emplace_back(*entry.second); + else if (group > Group) + break; + } + return templates; +} + std::string nodebank_panel::generate_node_label(std::string Input) const { diff --git a/application/editoruipanels.h b/application/editoruipanels.h index 41da7173..50e8bf99 100644 --- a/application/editoruipanels.h +++ b/application/editoruipanels.h @@ -81,7 +81,8 @@ class nodebank_panel : public ui_panel MODIFY, COPY, ADD, - BRUSH + BRUSH, + FILL }; edit_mode mode = MODIFY; @@ -90,6 +91,10 @@ class nodebank_panel : public ui_panel void render() override; void add_template(const std::string &desc); const std::string *get_active_template(); + // nodebank groups (collapsing headers), in file order; entries listed before the first header form an unnamed group + std::vector group_names() const; + // node templates of the group with specified index (as returned by group_names()) + std::vector group_templates(std::size_t const Group) const; private: // methods: From 99c5a0090b63d931db62fe8b801493e447f0a681 Mon Sep 17 00:00:00 2001 From: jakubg1 <24206305+jakubg1@users.noreply.github.com> Date: Thu, 1 Oct 2026 02:10:21 +0200 Subject: [PATCH 3/7] Remove brake position check from `TController::PrepareEngine()` The function would flag the consist as not ready (`iEngineActive` set to `false`) when the brake handle was at a cutoff position. Since this is what the brake lever is exactly starting at in the EW58 train, the train when asked to move the brake lever to operating position would not listen since the `control_tractive_and_braking_force()` function which is supposed to do that is gated behind the readiness flag in the `TController::Update()` function. This makes sure EW58 trains can be driven by AI once again instead of being stuck. I don't really know why the brake position check was there, since it would fail only if: - There is a brake handle - The brake handle is in the cutoff position - The brake handle is NOT in the operating position The third check seems redundant but curiously there is one case where this matters and it's... MHZ_EN57. Its operating and cutoff positions are both defined as `0`, which might be a hack, since the actual cutoff position in the MHZ_EN57 handle is meant to be `6`. --- vehicle/Driver.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/vehicle/Driver.cpp b/vehicle/Driver.cpp index 9dfd6731..6f3c4100 100644 --- a/vehicle/Driver.cpp +++ b/vehicle/Driver.cpp @@ -2857,10 +2857,7 @@ bool TController::PrepareEngine() && false == IsAnyLineBreakerOpen && ( false == IsAnyConverterPresent || true == IsAnyConverterEnabled ) && ( false == IsAnyCompressorPresent || true == IsAnyCompressorEnabled ) - && ( mvControlling->ScndPipePress > 4.5 || mvControlling->VeselVolume == 0.0 ) - && ( static_cast(mvOccupied->fBrakeCtrlPos) == static_cast(mvOccupied->Handle->GetPos(bh_RP)) - || static_cast(mvOccupied->fBrakeCtrlPos) != static_cast(mvOccupied->Handle->GetPos(bh_NP)) - || mvOccupied->BrakeHandle == TBrakeHandle::NoHandle ); + && ( mvControlling->ScndPipePress > 4.5 || mvControlling->VeselVolume == 0.0 ); } if( true == isready ) { From 403a6d1f9f6ed080a01da23218a21fcd8cca1894 Mon Sep 17 00:00:00 2001 From: jerrrrycho Date: Thu, 1 Oct 2026 07:58:28 +0200 Subject: [PATCH 4/7] fix: yet other sonar sast fixes --- McZapkie/Mover.cpp | 19 ++++++++----------- rendering/opengl33renderer.cpp | 8 ++++---- rendering/openglrenderer.cpp | 8 ++++---- simulation/simulationenvironment.cpp | 6 +++--- 4 files changed, 19 insertions(+), 22 deletions(-) diff --git a/McZapkie/Mover.cpp b/McZapkie/Mover.cpp index 68b88918..61bdc17a 100644 --- a/McZapkie/Mover.cpp +++ b/McZapkie/Mover.cpp @@ -2408,7 +2408,7 @@ bool TMoverParameters::IncMainCtrl(int CtrlSpeed) { ++MainCtrlPos; OK = true; - if (EIMCtrlType == 0 && SpeedCtrlAutoTurnOffFlag & 1 == 1 && MainCtrlActualPos != MainCtrlPos) + if (EIMCtrlType == 0 && ( SpeedCtrlAutoTurnOffFlag & 1 ) == 1 && MainCtrlActualPos != MainCtrlPos) { DecScndCtrl(2); SpeedCtrlUnit.IsActive = false; @@ -2591,7 +2591,7 @@ bool TMoverParameters::DecMainCtrl(int CtrlSpeed) { MainCtrlPos--; OK = true; - if (EIMCtrlType == 0 && SpeedCtrlAutoTurnOffFlag & 1 == 1 && MainCtrlActualPos != MainCtrlPos) + if (EIMCtrlType == 0 && (SpeedCtrlAutoTurnOffFlag & 1) == 1 && MainCtrlActualPos != MainCtrlPos) { DecScndCtrl(2); SpeedCtrlUnit.IsActive = false; @@ -2780,7 +2780,7 @@ bool TMoverParameters::IncScndCtrl(int CtrlSpeed) if (OK && EngineType == TEngineType::ElectricInductionMotor && ScndCtrlPosNo == 1 && MainCtrlPos > 0) { SpeedCtrlValue = Vel; - if (EIMCtrlType == 0 && SpeedCtrlAutoTurnOffFlag & 1 == 1) + if (EIMCtrlType == 0 && (SpeedCtrlAutoTurnOffFlag & 1) == 1) { MainCtrlActualPos = MainCtrlPos; } @@ -4586,14 +4586,11 @@ void TMoverParameters::UpdatePipePressure(double dt) auto const lowvoltagepower{Power24vIsAvailable || Power110vIsAvailable}; - if (true == RadioStopFlag || true == AlarmChainFlag || (true == EIMCtrlEmergency && LocalBrakePosA >= 1.0) || SecuritySystem.is_braking() || - (SpringBrakeDriveEmergencyVel >= 0 && Vel > SpringBrakeDriveEmergencyVel && SpringBrake.IsActive) - /* - // NOTE: disabled because 32 is 'load destroyed' flag, what does this have to do with emergency brake? - // (if it's supposed to be broken coupler, such event sets alarmchainflag instead when appropriate) - || ( true == TestFlag( EngDmgFlag, 32 ) ) - */ - || (0 == CabActive && InactiveCabFlag & activation::emergencybrake) || (SpringBrakeDriveEmergencyVel >= 0 && Vel > SpringBrakeDriveEmergencyVel && SpringBrake.IsActive)) + // EngDmgFlag 32 (load destroyed) used to trigger this too; dropped, it has nothing to do with + // the emergency brake (a broken coupler sets AlarmChainFlag instead) + if (RadioStopFlag || AlarmChainFlag || (EIMCtrlEmergency && LocalBrakePosA >= 1.0) || SecuritySystem.is_braking() || + (SpringBrakeDriveEmergencyVel >= 0 && Vel > SpringBrakeDriveEmergencyVel && SpringBrake.IsActive) || + (0 == CabActive && (InactiveCabFlag & activation::emergencybrake) != 0)) { EmergencyValveFlow = PF(0, PipePress, 0.15) * dt; } diff --git a/rendering/opengl33renderer.cpp b/rendering/opengl33renderer.cpp index b9937816..06fad0cd 100644 --- a/rendering/opengl33renderer.cpp +++ b/rendering/opengl33renderer.cpp @@ -81,9 +81,9 @@ bool opengl33_renderer::Init(GLFWwindow *Window) } // rgb value for 5780 kelvin - Global.DayLight.diffuse[0] = 255.0f / 255.0f; - Global.DayLight.diffuse[1] = 242.0f / 255.0f; - Global.DayLight.diffuse[2] = 231.0f / 255.0f; + Global.DayLight.diffuse[0] = 1.0f; // 255 / 255 + Global.DayLight.diffuse[1] = 0.9490196f; // 242 / 255 + Global.DayLight.diffuse[2] = 0.9058824f; // 231 / 255 Global.DayLight.is_directional = true; m_sunlight.id = opengl33_renderer::sunlight; @@ -1956,7 +1956,7 @@ bool opengl33_renderer::Render(world_environment *Environment) // moon { Bind_Texture(0, m_moontexture); - glm::vec3 mooncolor(255.0f / 255.0f, 242.0f / 255.0f, 231.0f / 255.0f); + glm::vec3 mooncolor(1.0f, 0.9490196f, 0.9058824f); glm::vec4 color(mooncolor.r, mooncolor.g, mooncolor.b, // fade the moon if it's near the sun in the sky, especially during the day std::max(0.f, 1.0 - 0.5 * Global.fLuminance - 0.65 * std::max(0.f, glm::dot(Environment->m_sun.getDirection(), Environment->m_moon.getDirection()))) * fogfactor); diff --git a/rendering/openglrenderer.cpp b/rendering/openglrenderer.cpp index 6f06e9b3..dcbd1df2 100644 --- a/rendering/openglrenderer.cpp +++ b/rendering/openglrenderer.cpp @@ -88,9 +88,9 @@ opengl_renderer::Init( GLFWwindow *Window ) { ::glEnable( opengl_renderer::sunlight ); // rgb value for 5780 kelvin - Global.DayLight.diffuse[ 0 ] = 255.0f / 255.0f; - Global.DayLight.diffuse[ 1 ] = 242.0f / 255.0f; - Global.DayLight.diffuse[ 2 ] = 231.0f / 255.0f; + Global.DayLight.diffuse[0] = 1.0f; // 255 / 255 + Global.DayLight.diffuse[1] = 0.9490196f; // 242 / 255 + Global.DayLight.diffuse[2] = 0.9058824f; // 231 / 255 Global.DayLight.is_directional = true; m_sunlight.id = opengl_renderer::sunlight; // ::glLightf( opengl_renderer::sunlight, GL_SPOT_CUTOFF, 90.0f ); @@ -1603,7 +1603,7 @@ opengl_renderer::Render( world_environment *Environment ) { if (!m_isATI) { Bind_Texture( m_moontexture ); - glm::vec3 mooncolor( 255.0f / 255.0f, 242.0f / 255.0f, 231.0f / 255.0f ); + glm::vec3 mooncolor(1.0f, 0.9490196f, 0.9058824f); ::glColor4f( mooncolor.r, mooncolor.g, mooncolor.b, // fade the moon if it's near the sun in the sky, especially during the day diff --git a/simulation/simulationenvironment.cpp b/simulation/simulationenvironment.cpp index 9837e0e8..b96cea20 100644 --- a/simulation/simulationenvironment.cpp +++ b/simulation/simulationenvironment.cpp @@ -140,7 +140,7 @@ world_environment::update() { m_lightintensity = moonlightlevel; // if the moon is up, it overrides the twilight twilightfactor = 0.0f; - keylightcolor = glm::vec3( 255.0f / 255.0f, 242.0f / 255.0f, 202.0f / 255.0f ); + keylightcolor = glm::vec3( 1.0f, 0.9490196f, 0.7921569f ); } else { // regular situation with sun as the key light @@ -152,8 +152,8 @@ world_environment::update() { // include 'golden hour' effect in twilight lighting float const duskfactor = 1.25f - std::clamp( Global.SunAngle, 0.0f, 18.0f ) / 18.0f; keylightcolor = glm::mix( - glm::vec3( 255.0f / 255.0f, 242.0f / 255.0f, 231.0f / 255.0f ), - glm::vec3( 235.0f / 255.0f, 120.0f / 255.0f, 36.0f / 255.0f ), + glm::vec3( 1.0f, 0.9490196f, 0.9058824f ), // 255, 242, 231 / 255 + glm::vec3( 0.9215686f, 0.4705882f, 0.1411765f ), // 235, 120, 36 / 255 duskfactor ); } // ...retrieve current sky colour and brightness... From f05b58b0e9995991e841c6562f36f8585d761af7 Mon Sep 17 00:00:00 2001 From: jerrrrycho Date: Thu, 1 Oct 2026 08:01:23 +0200 Subject: [PATCH 5/7] Revert "fix: yet other sonar sast fixes" This reverts commit 403a6d1f9f6ed080a01da23218a21fcd8cca1894. --- McZapkie/Mover.cpp | 19 +++++++++++-------- rendering/opengl33renderer.cpp | 8 ++++---- rendering/openglrenderer.cpp | 8 ++++---- simulation/simulationenvironment.cpp | 6 +++--- 4 files changed, 22 insertions(+), 19 deletions(-) diff --git a/McZapkie/Mover.cpp b/McZapkie/Mover.cpp index 61bdc17a..68b88918 100644 --- a/McZapkie/Mover.cpp +++ b/McZapkie/Mover.cpp @@ -2408,7 +2408,7 @@ bool TMoverParameters::IncMainCtrl(int CtrlSpeed) { ++MainCtrlPos; OK = true; - if (EIMCtrlType == 0 && ( SpeedCtrlAutoTurnOffFlag & 1 ) == 1 && MainCtrlActualPos != MainCtrlPos) + if (EIMCtrlType == 0 && SpeedCtrlAutoTurnOffFlag & 1 == 1 && MainCtrlActualPos != MainCtrlPos) { DecScndCtrl(2); SpeedCtrlUnit.IsActive = false; @@ -2591,7 +2591,7 @@ bool TMoverParameters::DecMainCtrl(int CtrlSpeed) { MainCtrlPos--; OK = true; - if (EIMCtrlType == 0 && (SpeedCtrlAutoTurnOffFlag & 1) == 1 && MainCtrlActualPos != MainCtrlPos) + if (EIMCtrlType == 0 && SpeedCtrlAutoTurnOffFlag & 1 == 1 && MainCtrlActualPos != MainCtrlPos) { DecScndCtrl(2); SpeedCtrlUnit.IsActive = false; @@ -2780,7 +2780,7 @@ bool TMoverParameters::IncScndCtrl(int CtrlSpeed) if (OK && EngineType == TEngineType::ElectricInductionMotor && ScndCtrlPosNo == 1 && MainCtrlPos > 0) { SpeedCtrlValue = Vel; - if (EIMCtrlType == 0 && (SpeedCtrlAutoTurnOffFlag & 1) == 1) + if (EIMCtrlType == 0 && SpeedCtrlAutoTurnOffFlag & 1 == 1) { MainCtrlActualPos = MainCtrlPos; } @@ -4586,11 +4586,14 @@ void TMoverParameters::UpdatePipePressure(double dt) auto const lowvoltagepower{Power24vIsAvailable || Power110vIsAvailable}; - // EngDmgFlag 32 (load destroyed) used to trigger this too; dropped, it has nothing to do with - // the emergency brake (a broken coupler sets AlarmChainFlag instead) - if (RadioStopFlag || AlarmChainFlag || (EIMCtrlEmergency && LocalBrakePosA >= 1.0) || SecuritySystem.is_braking() || - (SpringBrakeDriveEmergencyVel >= 0 && Vel > SpringBrakeDriveEmergencyVel && SpringBrake.IsActive) || - (0 == CabActive && (InactiveCabFlag & activation::emergencybrake) != 0)) + if (true == RadioStopFlag || true == AlarmChainFlag || (true == EIMCtrlEmergency && LocalBrakePosA >= 1.0) || SecuritySystem.is_braking() || + (SpringBrakeDriveEmergencyVel >= 0 && Vel > SpringBrakeDriveEmergencyVel && SpringBrake.IsActive) + /* + // NOTE: disabled because 32 is 'load destroyed' flag, what does this have to do with emergency brake? + // (if it's supposed to be broken coupler, such event sets alarmchainflag instead when appropriate) + || ( true == TestFlag( EngDmgFlag, 32 ) ) + */ + || (0 == CabActive && InactiveCabFlag & activation::emergencybrake) || (SpringBrakeDriveEmergencyVel >= 0 && Vel > SpringBrakeDriveEmergencyVel && SpringBrake.IsActive)) { EmergencyValveFlow = PF(0, PipePress, 0.15) * dt; } diff --git a/rendering/opengl33renderer.cpp b/rendering/opengl33renderer.cpp index 06fad0cd..b9937816 100644 --- a/rendering/opengl33renderer.cpp +++ b/rendering/opengl33renderer.cpp @@ -81,9 +81,9 @@ bool opengl33_renderer::Init(GLFWwindow *Window) } // rgb value for 5780 kelvin - Global.DayLight.diffuse[0] = 1.0f; // 255 / 255 - Global.DayLight.diffuse[1] = 0.9490196f; // 242 / 255 - Global.DayLight.diffuse[2] = 0.9058824f; // 231 / 255 + Global.DayLight.diffuse[0] = 255.0f / 255.0f; + Global.DayLight.diffuse[1] = 242.0f / 255.0f; + Global.DayLight.diffuse[2] = 231.0f / 255.0f; Global.DayLight.is_directional = true; m_sunlight.id = opengl33_renderer::sunlight; @@ -1956,7 +1956,7 @@ bool opengl33_renderer::Render(world_environment *Environment) // moon { Bind_Texture(0, m_moontexture); - glm::vec3 mooncolor(1.0f, 0.9490196f, 0.9058824f); + glm::vec3 mooncolor(255.0f / 255.0f, 242.0f / 255.0f, 231.0f / 255.0f); glm::vec4 color(mooncolor.r, mooncolor.g, mooncolor.b, // fade the moon if it's near the sun in the sky, especially during the day std::max(0.f, 1.0 - 0.5 * Global.fLuminance - 0.65 * std::max(0.f, glm::dot(Environment->m_sun.getDirection(), Environment->m_moon.getDirection()))) * fogfactor); diff --git a/rendering/openglrenderer.cpp b/rendering/openglrenderer.cpp index dcbd1df2..6f06e9b3 100644 --- a/rendering/openglrenderer.cpp +++ b/rendering/openglrenderer.cpp @@ -88,9 +88,9 @@ opengl_renderer::Init( GLFWwindow *Window ) { ::glEnable( opengl_renderer::sunlight ); // rgb value for 5780 kelvin - Global.DayLight.diffuse[0] = 1.0f; // 255 / 255 - Global.DayLight.diffuse[1] = 0.9490196f; // 242 / 255 - Global.DayLight.diffuse[2] = 0.9058824f; // 231 / 255 + Global.DayLight.diffuse[ 0 ] = 255.0f / 255.0f; + Global.DayLight.diffuse[ 1 ] = 242.0f / 255.0f; + Global.DayLight.diffuse[ 2 ] = 231.0f / 255.0f; Global.DayLight.is_directional = true; m_sunlight.id = opengl_renderer::sunlight; // ::glLightf( opengl_renderer::sunlight, GL_SPOT_CUTOFF, 90.0f ); @@ -1603,7 +1603,7 @@ opengl_renderer::Render( world_environment *Environment ) { if (!m_isATI) { Bind_Texture( m_moontexture ); - glm::vec3 mooncolor(1.0f, 0.9490196f, 0.9058824f); + glm::vec3 mooncolor( 255.0f / 255.0f, 242.0f / 255.0f, 231.0f / 255.0f ); ::glColor4f( mooncolor.r, mooncolor.g, mooncolor.b, // fade the moon if it's near the sun in the sky, especially during the day diff --git a/simulation/simulationenvironment.cpp b/simulation/simulationenvironment.cpp index b96cea20..9837e0e8 100644 --- a/simulation/simulationenvironment.cpp +++ b/simulation/simulationenvironment.cpp @@ -140,7 +140,7 @@ world_environment::update() { m_lightintensity = moonlightlevel; // if the moon is up, it overrides the twilight twilightfactor = 0.0f; - keylightcolor = glm::vec3( 1.0f, 0.9490196f, 0.7921569f ); + keylightcolor = glm::vec3( 255.0f / 255.0f, 242.0f / 255.0f, 202.0f / 255.0f ); } else { // regular situation with sun as the key light @@ -152,8 +152,8 @@ world_environment::update() { // include 'golden hour' effect in twilight lighting float const duskfactor = 1.25f - std::clamp( Global.SunAngle, 0.0f, 18.0f ) / 18.0f; keylightcolor = glm::mix( - glm::vec3( 1.0f, 0.9490196f, 0.9058824f ), // 255, 242, 231 / 255 - glm::vec3( 0.9215686f, 0.4705882f, 0.1411765f ), // 235, 120, 36 / 255 + glm::vec3( 255.0f / 255.0f, 242.0f / 255.0f, 231.0f / 255.0f ), + glm::vec3( 235.0f / 255.0f, 120.0f / 255.0f, 36.0f / 255.0f ), duskfactor ); } // ...retrieve current sky colour and brightness... From 06078ddab928bbc2bb0c5eaf43e541dbb84b436e Mon Sep 17 00:00:00 2001 From: jerrrrycho Date: Thu, 1 Oct 2026 13:30:25 +0200 Subject: [PATCH 6/7] fix: other sonar sast fixes (#193) --- McZapkie/Mover.cpp | 19 ++++++++----------- rendering/opengl33renderer.cpp | 8 ++++---- rendering/openglrenderer.cpp | 8 ++++---- simulation/simulationenvironment.cpp | 6 +++--- 4 files changed, 19 insertions(+), 22 deletions(-) diff --git a/McZapkie/Mover.cpp b/McZapkie/Mover.cpp index 68b88918..61bdc17a 100644 --- a/McZapkie/Mover.cpp +++ b/McZapkie/Mover.cpp @@ -2408,7 +2408,7 @@ bool TMoverParameters::IncMainCtrl(int CtrlSpeed) { ++MainCtrlPos; OK = true; - if (EIMCtrlType == 0 && SpeedCtrlAutoTurnOffFlag & 1 == 1 && MainCtrlActualPos != MainCtrlPos) + if (EIMCtrlType == 0 && ( SpeedCtrlAutoTurnOffFlag & 1 ) == 1 && MainCtrlActualPos != MainCtrlPos) { DecScndCtrl(2); SpeedCtrlUnit.IsActive = false; @@ -2591,7 +2591,7 @@ bool TMoverParameters::DecMainCtrl(int CtrlSpeed) { MainCtrlPos--; OK = true; - if (EIMCtrlType == 0 && SpeedCtrlAutoTurnOffFlag & 1 == 1 && MainCtrlActualPos != MainCtrlPos) + if (EIMCtrlType == 0 && (SpeedCtrlAutoTurnOffFlag & 1) == 1 && MainCtrlActualPos != MainCtrlPos) { DecScndCtrl(2); SpeedCtrlUnit.IsActive = false; @@ -2780,7 +2780,7 @@ bool TMoverParameters::IncScndCtrl(int CtrlSpeed) if (OK && EngineType == TEngineType::ElectricInductionMotor && ScndCtrlPosNo == 1 && MainCtrlPos > 0) { SpeedCtrlValue = Vel; - if (EIMCtrlType == 0 && SpeedCtrlAutoTurnOffFlag & 1 == 1) + if (EIMCtrlType == 0 && (SpeedCtrlAutoTurnOffFlag & 1) == 1) { MainCtrlActualPos = MainCtrlPos; } @@ -4586,14 +4586,11 @@ void TMoverParameters::UpdatePipePressure(double dt) auto const lowvoltagepower{Power24vIsAvailable || Power110vIsAvailable}; - if (true == RadioStopFlag || true == AlarmChainFlag || (true == EIMCtrlEmergency && LocalBrakePosA >= 1.0) || SecuritySystem.is_braking() || - (SpringBrakeDriveEmergencyVel >= 0 && Vel > SpringBrakeDriveEmergencyVel && SpringBrake.IsActive) - /* - // NOTE: disabled because 32 is 'load destroyed' flag, what does this have to do with emergency brake? - // (if it's supposed to be broken coupler, such event sets alarmchainflag instead when appropriate) - || ( true == TestFlag( EngDmgFlag, 32 ) ) - */ - || (0 == CabActive && InactiveCabFlag & activation::emergencybrake) || (SpringBrakeDriveEmergencyVel >= 0 && Vel > SpringBrakeDriveEmergencyVel && SpringBrake.IsActive)) + // EngDmgFlag 32 (load destroyed) used to trigger this too; dropped, it has nothing to do with + // the emergency brake (a broken coupler sets AlarmChainFlag instead) + if (RadioStopFlag || AlarmChainFlag || (EIMCtrlEmergency && LocalBrakePosA >= 1.0) || SecuritySystem.is_braking() || + (SpringBrakeDriveEmergencyVel >= 0 && Vel > SpringBrakeDriveEmergencyVel && SpringBrake.IsActive) || + (0 == CabActive && (InactiveCabFlag & activation::emergencybrake) != 0)) { EmergencyValveFlow = PF(0, PipePress, 0.15) * dt; } diff --git a/rendering/opengl33renderer.cpp b/rendering/opengl33renderer.cpp index b9937816..06fad0cd 100644 --- a/rendering/opengl33renderer.cpp +++ b/rendering/opengl33renderer.cpp @@ -81,9 +81,9 @@ bool opengl33_renderer::Init(GLFWwindow *Window) } // rgb value for 5780 kelvin - Global.DayLight.diffuse[0] = 255.0f / 255.0f; - Global.DayLight.diffuse[1] = 242.0f / 255.0f; - Global.DayLight.diffuse[2] = 231.0f / 255.0f; + Global.DayLight.diffuse[0] = 1.0f; // 255 / 255 + Global.DayLight.diffuse[1] = 0.9490196f; // 242 / 255 + Global.DayLight.diffuse[2] = 0.9058824f; // 231 / 255 Global.DayLight.is_directional = true; m_sunlight.id = opengl33_renderer::sunlight; @@ -1956,7 +1956,7 @@ bool opengl33_renderer::Render(world_environment *Environment) // moon { Bind_Texture(0, m_moontexture); - glm::vec3 mooncolor(255.0f / 255.0f, 242.0f / 255.0f, 231.0f / 255.0f); + glm::vec3 mooncolor(1.0f, 0.9490196f, 0.9058824f); glm::vec4 color(mooncolor.r, mooncolor.g, mooncolor.b, // fade the moon if it's near the sun in the sky, especially during the day std::max(0.f, 1.0 - 0.5 * Global.fLuminance - 0.65 * std::max(0.f, glm::dot(Environment->m_sun.getDirection(), Environment->m_moon.getDirection()))) * fogfactor); diff --git a/rendering/openglrenderer.cpp b/rendering/openglrenderer.cpp index 6f06e9b3..dcbd1df2 100644 --- a/rendering/openglrenderer.cpp +++ b/rendering/openglrenderer.cpp @@ -88,9 +88,9 @@ opengl_renderer::Init( GLFWwindow *Window ) { ::glEnable( opengl_renderer::sunlight ); // rgb value for 5780 kelvin - Global.DayLight.diffuse[ 0 ] = 255.0f / 255.0f; - Global.DayLight.diffuse[ 1 ] = 242.0f / 255.0f; - Global.DayLight.diffuse[ 2 ] = 231.0f / 255.0f; + Global.DayLight.diffuse[0] = 1.0f; // 255 / 255 + Global.DayLight.diffuse[1] = 0.9490196f; // 242 / 255 + Global.DayLight.diffuse[2] = 0.9058824f; // 231 / 255 Global.DayLight.is_directional = true; m_sunlight.id = opengl_renderer::sunlight; // ::glLightf( opengl_renderer::sunlight, GL_SPOT_CUTOFF, 90.0f ); @@ -1603,7 +1603,7 @@ opengl_renderer::Render( world_environment *Environment ) { if (!m_isATI) { Bind_Texture( m_moontexture ); - glm::vec3 mooncolor( 255.0f / 255.0f, 242.0f / 255.0f, 231.0f / 255.0f ); + glm::vec3 mooncolor(1.0f, 0.9490196f, 0.9058824f); ::glColor4f( mooncolor.r, mooncolor.g, mooncolor.b, // fade the moon if it's near the sun in the sky, especially during the day diff --git a/simulation/simulationenvironment.cpp b/simulation/simulationenvironment.cpp index 9837e0e8..b96cea20 100644 --- a/simulation/simulationenvironment.cpp +++ b/simulation/simulationenvironment.cpp @@ -140,7 +140,7 @@ world_environment::update() { m_lightintensity = moonlightlevel; // if the moon is up, it overrides the twilight twilightfactor = 0.0f; - keylightcolor = glm::vec3( 255.0f / 255.0f, 242.0f / 255.0f, 202.0f / 255.0f ); + keylightcolor = glm::vec3( 1.0f, 0.9490196f, 0.7921569f ); } else { // regular situation with sun as the key light @@ -152,8 +152,8 @@ world_environment::update() { // include 'golden hour' effect in twilight lighting float const duskfactor = 1.25f - std::clamp( Global.SunAngle, 0.0f, 18.0f ) / 18.0f; keylightcolor = glm::mix( - glm::vec3( 255.0f / 255.0f, 242.0f / 255.0f, 231.0f / 255.0f ), - glm::vec3( 235.0f / 255.0f, 120.0f / 255.0f, 36.0f / 255.0f ), + glm::vec3( 1.0f, 0.9490196f, 0.9058824f ), // 255, 242, 231 / 255 + glm::vec3( 0.9215686f, 0.4705882f, 0.1411765f ), // 235, 120, 36 / 255 duskfactor ); } // ...retrieve current sky colour and brightness... From 1474d2b773ce1510d0c2e73505c464d2616b0045 Mon Sep 17 00:00:00 2001 From: jerrrrycho Date: Thu, 1 Oct 2026 13:30:36 +0200 Subject: [PATCH 7/7] fix: merge nested ifs in Driver.cpp cpp:s1066 (#194) --- vehicle/Driver.cpp | 1723 ++++++++++++++++++++------------------------ 1 file changed, 785 insertions(+), 938 deletions(-) diff --git a/vehicle/Driver.cpp b/vehicle/Driver.cpp index 6f3c4100..9f1b2586 100644 --- a/vehicle/Driver.cpp +++ b/vehicle/Driver.cpp @@ -219,13 +219,13 @@ void TSpeedPos::CommandCheck() fVelNext = value1; iFlags &= ~(spShuntSemaphor | spPassengerStopPoint | spStopOnSBL); iFlags |= spSemaphor;// nie manewrowa, nie przystanek, nie zatrzymać na SBL, ale semafor - if (value1 == 0.0) // jeśli pierwsza zerowa - if (value2 != 0.0) // a druga nie - { // S1 na SBL, można przejechać po zatrzymaniu (tu nie mamy prędkości ani odległości) - fVelNext = value2; // normalnie będzie zezwolenie na jazdę, aby się usunął z tabelki - iFlags |= spStopOnSBL; // flaga, że ma zatrzymać; na pewno nie zezwoli na manewry - } - break; + if (value1 == 0.0 && value2 != 0.0) // jeśli pierwsza zerowa + // a druga nie + { // S1 na SBL, można przejechać po zatrzymaniu (tu nie mamy prędkości ani odległości) + fVelNext = value2; // normalnie będzie zezwolenie na jazdę, aby się usunął z tabelki + iFlags |= spStopOnSBL; // flaga, że ma zatrzymać; na pewno nie zezwoli na manewry + } + break; case TCommandType::cm_SectionVelocity: // odcinek z ograniczeniem prędkości fVelNext = value1; @@ -301,46 +301,39 @@ bool TSpeedPos::Update() if( trTrack->iCategoryFlag & 1 ) { // railways - if( iFlags & spSwitch ) { + if( iFlags & spSwitch && (trTrack->GetSwitchState() & 1) != 0 != ((iFlags & spSwitchStatus) != 0) ) { // jeśli odcinek zmienny - if( (trTrack->GetSwitchState() & 1) != 0 != - ( ( iFlags & spSwitchStatus ) != 0 ) ) { - // czy stan się zmienił? - // Ra: zakładam, że są tylko 2 możliwe stany - iFlags ^= spSwitchStatus; - if( ( iFlags & spElapsed ) == 0 ) { - // jeszcze trzeba skanowanie wykonać od tego toru - // problem jest chyba, jeśli zwrotnica się przełoży zaraz po zjechaniu z niej - // na Mydelniczce potrafi skanować na wprost mimo pojechania na bok - return true; - } - } - } + // czy stan się zmienił? + // Ra: zakładam, że są tylko 2 możliwe stany + iFlags ^= spSwitchStatus; + if ((iFlags & spElapsed) == 0) + { + // jeszcze trzeba skanowanie wykonać od tego toru + // problem jest chyba, jeśli zwrotnica się przełoży zaraz po zjechaniu z niej + // na Mydelniczce potrafi skanować na wprost mimo pojechania na bok + return true; + } + } } else { // roads and others - if( iFlags & 0xF0000000 ) { + if( iFlags & 0xF0000000 && (iFlags & 0xF0000000) > 0x10000000 ) { // jeśli skrzyżowanie, ograniczyć prędkość przy skręcaniu - if( ( iFlags & 0xF0000000 ) > 0x10000000 ) { - // ±1 to jazda na wprost, ±2 nieby też, ale z przecięciem głównej drogi - chyba że jest równorzędne... - // TODO: uzależnić prędkość od promienia; - // albo niech będzie ograniczona w skrzyżowaniu (velocity z ujemną wartością) - fVelNext = 30.0; - } - } + // ±1 to jazda na wprost, ±2 nieby też, ale z przecięciem głównej drogi - chyba że jest równorzędne... + // TODO: uzależnić prędkość od promienia; + // albo niech będzie ograniczona w skrzyżowaniu (velocity z ujemną wartością) + fVelNext = 30.0; + } } } } - else if (iFlags & spEvent) { + else if (iFlags & spEvent && ((iFlags & spElapsed) == 0 || fVelNext == 0.0)) { // jeśli event - if( (iFlags & spElapsed) == 0 - || fVelNext == 0.0 ) { - // ignore already passed signals, but keep an eye on overrun stops - // odczyt komórki pamięci najlepiej by było zrobić jako notyfikację, - // czyli zmiana komórki wywoła jakąś podaną funkcję - CommandCheck(); // sprawdzenie typu komendy w evencie i określenie prędkości - } - } + // ignore already passed signals, but keep an eye on overrun stops + // odczyt komórki pamięci najlepiej by było zrobić jako notyfikację, + // czyli zmiana komórki wywoła jakąś podaną funkcję + CommandCheck(); // sprawdzenie typu komendy w evencie i określenie prędkości + } return false; }; @@ -377,11 +370,10 @@ bool TSpeedPos::IsProperSemaphor(TOrders order) else if (iFlags & spOutsideStation) return true; } - else if (order & Obey_train) + else if (order & Obey_train && iFlags & spSemaphor) { - if (iFlags & spSemaphor) - return true; - } + return true; + } return false; // true gdy zatrzymanie, wtedy nie ma po co skanować dalej } @@ -408,11 +400,10 @@ bool TSpeedPos::Set(basic_event *event, double dist, double length, TOrders orde else if (iFlags & spOutsideStation) return true; } - else if (order & Obey_train) + else if (order & Obey_train && iFlags & spSemaphor && fVelNext == 0.0) { - if (iFlags & spSemaphor && fVelNext == 0.0) - return true; - } + return true; + } return false; // true gdy zatrzymanie, wtedy nie ma po co skanować dalej }; @@ -423,10 +414,9 @@ void TSpeedPos::Set(TTrack *track, double dist, int flag) if (trTrack) { iFlags = flag | (trTrack->eType == tt_Normal ? spTrack : spTrack | spSwitch ); // zapamiętanie kierunku wraz z typem - if (iFlags & spSwitch) - if (trTrack->GetSwitchState() & 1) - iFlags |= spSwitchStatus; - fVelNext = trTrack->VelocityGet(); + if (iFlags & spSwitch && trTrack->GetSwitchState() & 1) + iFlags |= spSwitchStatus; + fVelNext = trTrack->VelocityGet(); if (trTrack->iDamageFlag & 128) fVelNext = 0.0; // jeśli uszkodzony, to też stój if (iFlags & spEnd) @@ -590,52 +580,48 @@ void TController::TableTraceRoute(double fDistance, TDynamicObject *pVehicle) auto const events { CheckTrackEvent( pTrack, fLastDir ) }; for( auto *pEvent : events ) { - if( pEvent != nullptr ) // jeśli jest semafor na tym torze + if( pEvent != nullptr && TableNotFound(pEvent, fCurrentDistance) ) // jeśli jest semafor na tym torze { // trzeba sprawdzić tabelkę, bo dodawanie drugi raz tego samego przystanku nie jest korzystne - if (TableNotFound(pEvent, fCurrentDistance)) // jeśli nie ma - { - TableAddNew(); // zawsze jest true + // jeśli nie ma + TableAddNew(); // zawsze jest true - if (Global.iWriteLogEnabled & 8) { - WriteLog("Speed table for " + OwnerName() + " found new event, " + pEvent->m_name); - } - auto &newspeedpoint = sSpeedTable[iLast]; -/* - if( newspeedpoint.Set( - pEvent, - fCurrentDistance + ProjectEventOnTrack( pEvent, pTrack, fLastDir ), - OrderCurrentGet() ) ) { -*/ - if( newspeedpoint.Set( - pEvent, - GetDistanceToEvent( pTrack, pEvent, fLastDir, fCurrentDistance ), - fLength, - OrderCurrentGet() ) ) { + if (Global.iWriteLogEnabled & 8) + { + WriteLog("Speed table for " + OwnerName() + " found new event, " + pEvent->m_name); + } + auto &newspeedpoint = sSpeedTable[iLast]; + /* + if( newspeedpoint.Set( + pEvent, + fCurrentDistance + ProjectEventOnTrack( pEvent, pTrack, fLastDir ), + OrderCurrentGet() ) ) { + */ + if (newspeedpoint.Set(pEvent, GetDistanceToEvent(pTrack, pEvent, fLastDir, fCurrentDistance), fLength, OrderCurrentGet())) + { - fDistance = newspeedpoint.fDist; // jeśli sygnał stop, to nie ma potrzeby dalej skanować - SemNextStopIndex = iLast; - if (SemNextIndex == npos) { - SemNextIndex = iLast; - } - if (Global.iWriteLogEnabled & 8) { - WriteLog("(stop signal from " - + (SemNextStopIndex != npos ? sSpeedTable[SemNextStopIndex].GetName() : "unknown semaphor") - + ")"); - } - } - else { - if( true == newspeedpoint.IsProperSemaphor(OrderCurrentGet()) - && SemNextIndex == npos ) { - SemNextIndex = iLast; // sprawdzamy czy pierwszy na drodze - } - if (Global.iWriteLogEnabled & 8) { - WriteLog("(forward signal for " - + (SemNextIndex != npos ? sSpeedTable[SemNextIndex].GetName() : "unknown semaphor") - + ")"); - } - } - } - } + fDistance = newspeedpoint.fDist; // jeśli sygnał stop, to nie ma potrzeby dalej skanować + SemNextStopIndex = iLast; + if (SemNextIndex == npos) + { + SemNextIndex = iLast; + } + if (Global.iWriteLogEnabled & 8) + { + WriteLog("(stop signal from " + (SemNextStopIndex != npos ? sSpeedTable[SemNextStopIndex].GetName() : "unknown semaphor") + ")"); + } + } + else + { + if (true == newspeedpoint.IsProperSemaphor(OrderCurrentGet()) && SemNextIndex == npos) + { + SemNextIndex = iLast; // sprawdzamy czy pierwszy na drodze + } + if (Global.iWriteLogEnabled & 8) + { + WriteLog("(forward signal for " + (SemNextIndex != npos ? sSpeedTable[SemNextIndex].GetName() : "unknown semaphor") + ")"); + } + } + } } // event dodajemy najpierw, żeby móc sprawdzić, czy tor został dodany po odczytaniu prędkości następnego if( pTrack->VelocityGet() == 0.0 // zatrzymanie @@ -686,17 +672,13 @@ void TController::TableTraceRoute(double fDistance, TDynamicObject *pVehicle) */ } } - else if ( pTrack->fRadius != 0.0 // odległość na łuku lepiej aproksymować cięciwami - || ( tLast != nullptr - && tLast->fRadius != 0.0 )) // koniec łuku też jest istotny + else if ( (pTrack->fRadius != 0.0 // odległość na łuku lepiej aproksymować cięciwami + || tLast != nullptr && tLast->fRadius != 0.0) && + TableAddNew()) // koniec łuku też jest istotny { // albo dla liczenia odległości przy pomocy cięciw - te usuwać po przejechaniu - if (TableAddNew()) { - // dodanie odcinka do tabelki - sSpeedTable[iLast].Set( - pTrack, fCurrentDistance, - fLastDir < 0 ? spEnabled | spCurve | spReverse : spEnabled | spCurve ); - } - } + // dodanie odcinka do tabelki + sSpeedTable[iLast].Set(pTrack, fCurrentDistance, fLastDir < 0 ? spEnabled | spCurve | spReverse : spEnabled | spCurve); + } } fCurrentDistance += fTrackLength; // doliczenie kolejnego odcinka do przeskanowanej długości @@ -708,31 +690,22 @@ void TController::TableTraceRoute(double fDistance, TDynamicObject *pVehicle) if (pTrack != nullptr ) { // jeśli kolejny istnieje - if( tLast != nullptr ) { + if( tLast != nullptr && tLast->VelocityGet() > 0 && (tLast->VelocityGet() < pTrack->VelocityGet() || pTrack->VelocityGet() < 0) ) { - if( tLast->VelocityGet() > 0 - && ( tLast->VelocityGet() < pTrack->VelocityGet() - || pTrack->VelocityGet() < 0 ) ) { - - if( iLast != -1 - && sSpeedTable[iLast].trTrack == tLast ) { - // if the track is already in the table we only need to mark it as relevant - sSpeedTable[ iLast ].iFlags |= spEnabled; - } - else { - // otherwise add it - TableAddNew(); - sSpeedTable[ iLast ].Set( - tLast, - fCurrentDistance - fTrackLength, // by now the current distance points to beginning of next track - ( fLastDir > 0 ? - pTrack->iPrevDirection : - pTrack->iNextDirection ) ? - spEnabled : - spEnabled | spReverse ); - } - } - } + if (iLast != -1 && sSpeedTable[iLast].trTrack == tLast) + { + // if the track is already in the table we only need to mark it as relevant + sSpeedTable[iLast].iFlags |= spEnabled; + } + else + { + // otherwise add it + TableAddNew(); + sSpeedTable[iLast].Set(tLast, + fCurrentDistance - fTrackLength, // by now the current distance points to beginning of next track + (fLastDir > 0 ? pTrack->iPrevDirection : pTrack->iNextDirection) ? spEnabled : spEnabled | spReverse); + } + } // zwiększenie skanowanej odległości tylko jeśli istnieje dalszy tor fTrackLength = pTrack->Length(); } @@ -749,14 +722,12 @@ void TController::TableTraceRoute(double fDistance, TDynamicObject *pVehicle) fLastDir < 0 ? spEnabled | spEnd | spReverse : spEnabled | spEnd); } } - else if( sSpeedTable[ iLast ].trTrack == tLast ) { + else if( sSpeedTable[iLast].trTrack == tLast && false == TestFlag(sSpeedTable[iLast].trTrack->iCategoryFlag, 0x100) ) { // otherwise just mark the last added track as the final one // TODO: investigate exactly how we can wind up not marking the last existing track as actual end - if( false == TestFlag( sSpeedTable[ iLast ].trTrack->iCategoryFlag, 0x100 ) ) { - // don't mark portals, as these aren't exactly track ends, but teleport devices - sSpeedTable[ iLast ].iFlags |= spEnabled | spEnd; - } - } + // don't mark portals, as these aren't exactly track ends, but teleport devices + sSpeedTable[iLast].iFlags |= spEnabled | spEnd; + } // to ostatnia pozycja, bo NULL nic nie da, a może się podpiąć obrotnica, czy jakieś transportery return; } @@ -820,27 +791,21 @@ void TController::TableCheck(double fDistance) { // degradacja pozycji sSpeedTable[i].iFlags &= ~spEnabled; // nie liczy się } - else if( ( sSpeedTable[ i ].iFlags & 0xF0000028 ) == spElapsed ) { + else if( (sSpeedTable[i].iFlags & 0xF0000028) == spElapsed && sSpeedTable[i].fVelNext < 0 ) { // jest z tyłu (najechany) i nie jest zwrotnicą ani skrzyżowaniem - if( sSpeedTable[ i ].fVelNext < 0 ) // a nie ma ograniczenia prędkości - { - sSpeedTable[ i ].iFlags = 0; // to nie ma go po co trzymać (odtykacz usunie ze środka) - } - } + // a nie ma ograniczenia prędkości + sSpeedTable[i].iFlags = 0; // to nie ma go po co trzymać (odtykacz usunie ze środka) + } } - else if (sSpeedTable[i].iFlags & spEvent) // jeśli event + else if (sSpeedTable[i].iFlags & spEvent && sSpeedTable[i].fDist < (typeid(*sSpeedTable[i].evEvent) == typeid(putvalues_event) ? -fLength : 0) && mvOccupied->CategoryFlag == 2 && + sSpeedTable[i].fDist < -0.75) // jeśli event { - if (sSpeedTable[i].fDist < ( - typeid( *sSpeedTable[i].evEvent ) == typeid( putvalues_event ) ? - -fLength : - 0)) // jeśli jest z tyłu - if (mvOccupied->CategoryFlag == 2 && sSpeedTable[i].fDist < -0.75) - { // pociąg staje zawsze, a samochód tylko jeśli nie przejedzie całą długością (może być zaskoczony zmianą) - // WriteLog("TableCheck: Event is behind. Delete from table: " + sSpeedTable[i].evEvent->asName); - sSpeedTable[i].iFlags &= ~spEnabled; // degradacja pozycji dla samochodu; - // semafory usuwane tylko przy sprawdzaniu, bo wysyłają komendy - } - } + // jeśli jest z tyłu + // pociąg staje zawsze, a samochód tylko jeśli nie przejedzie całą długością (może być zaskoczony zmianą) + // WriteLog("TableCheck: Event is behind. Delete from table: " + sSpeedTable[i].evEvent->asName); + sSpeedTable[i].iFlags &= ~spEnabled; // degradacja pozycji dla samochodu; + // semafory usuwane tylko przy sprawdzaniu, bo wysyłają komendy + } } } sSpeedTable[iLast].Update(); // aktualizacja ostatniego @@ -890,10 +855,10 @@ TCommandType TController::TableUpdate(double &fVelDes, double &fDist, double &fN iDrivigFlags |= moveSwitchFound; // rozjazd z przodu/pod ogranicza np. sens skanowania wstecz SwitchClearDist = point.fDist + point.trTrack->Length() + fLength; } - else if ( TestFlag( point.iFlags, spEvent ) ) // W4 może się deaktywować + else if ( TestFlag(point.iFlags, spEvent) && TableUpdateEvent(v, go, point, d_to_next_sem, idx) ) // W4 może się deaktywować { // jeżeli event, może być potrzeba wysłania komendy, aby ruszył - if( TableUpdateEvent( v, go, point, d_to_next_sem, idx ) ) { continue; } - } + continue; + } auto const railwaytrackend { true == TestFlag(point.iFlags, spEnd) && is_train() }; if( v >= 0.0 // pozycje z prędkością -1 można spokojnie pomijać @@ -938,14 +903,10 @@ TCommandType TController::TableUpdate(double &fVelDes, double &fDist, double &fN // points located behind can affect our current speed, but little else else if ( point.iFlags & spTrack) // jeśli tor { // tor ogranicza prędkość, dopóki cały skład nie przejedzie, - if( v >= 1.0 ) { // EU06 się zawieszało po dojechaniu na koniec toru postojowego - if( d + point.trTrack->Length() < -fLength ) { - if( false == railwaytrackend ) { - // zapętlenie, jeśli już wyjechał za ten odcinek - continue; - } - } - } + if( v >= 1.0 && d + point.trTrack->Length() < -fLength && false == railwaytrackend ) { // EU06 się zawieszało po dojechaniu na koniec toru postojowego + // zapętlenie, jeśli już wyjechał za ten odcinek + continue; + } if( v < fVelDes ) { // ograniczenie aktualnej prędkości aż do wyjechania za ograniczenie fVelDes = v; @@ -953,11 +914,9 @@ TCommandType TController::TableUpdate(double &fVelDes, double &fDist, double &fN if( v < VelLimitLastDist.first ) { VelLimitLastDist.second = d + point.trTrack->Length() + fLength; } - else if( VelLimitLastDist.second > 0 ) { // the speed limit can potentially start afterwards, so don't mark it as broken too soon - if( ( point.iFlags & ( spSwitch | spPassengerStopPoint ) ) == 0 ) { - speedlimitiscontinuous = false; - } - } + else if( VelLimitLastDist.second > 0 && (point.iFlags & (spSwitch | spPassengerStopPoint)) == 0 ) { // the speed limit can potentially start afterwards, so don't mark it as broken too soon + speedlimitiscontinuous = false; + } if( false == railwaytrackend ) { continue; // i tyle wystarczy } @@ -1029,19 +988,16 @@ TCommandType TController::TableUpdate(double &fVelDes, double &fDist, double &fN } // if (v>=0.0) // finding a point which doesn't impose speed limit means any potential active speed limit can't extend through entire scan table // NOTE: we test actual speed value for the given point, as events may receive (v = -1) as a way to disable them in irrelevant modes - if( point.fVelNext < 0 ) { + if( point.fVelNext < 0 && (point.iFlags & (spSwitch | spPassengerStopPoint)) == 0 ) { // NOTE: we exclude switches from this rule, as speed limits generally extend through these - if( ( point.iFlags & ( spSwitch | spPassengerStopPoint ) ) == 0 ) { - speedlimitiscontinuous = false; - } - } + speedlimitiscontinuous = false; + } if (fNext >= 0.0) { // jeśli ograniczenie - if( ( point.iFlags & ( spEnabled | spEvent ) ) == ( spEnabled | spEvent ) ) { // tylko sygnał przypisujemy - if( eSignNext == nullptr ) { // jeśli jeszcze nic nie zapisane tam - eSignNext = point.evEvent; // dla informacji - } - } + if( (point.iFlags & (spEnabled | spEvent)) == (spEnabled | spEvent) && eSignNext == nullptr ) { // tylko sygnał przypisujemy + // jeśli jeszcze nic nie zapisane tam + eSignNext = point.evEvent; // dla informacji + } if( fNext == 0.0 ) { break; // nie ma sensu analizować tabelki dalej } @@ -1408,20 +1364,13 @@ TController::TableUpdateEvent( double &Velocity, TCommandType &Command, TSpeedPo auto const magnetlocation { pVehicles[ end::front ]->MoverParameters->SecuritySystem.MagnetLocation }; auto const magnetrange { 1.0 }; auto const ismagnetpassed { Point.fDist < -( magnetlocation + magnetrange ) }; - if( Point.fDist < -magnetlocation ) { + if( Point.fDist < -magnetlocation && (mvOccupied->Vel > EU07_AI_NOMOVEMENT || false == ismagnetpassed) ) { // NOTE: normally we'd activate the magnet once the leading vehicle passes it // but on a fresh scan after direction change it would be detected as long as it's under consist, and meet the (simple) activation condition // thus we're doing a more precise check in such situation (we presume direction change takes place only if the vehicle is standing still) - if( mvOccupied->Vel > EU07_AI_NOMOVEMENT - || false == ismagnetpassed ) { - mvOccupied->SecuritySystem.set_cabsignal_lock(!AIControllFlag); // don't make life difficult for the ai, but a human driver is a fair game - PutCommand( - Point.evEvent->input_text(), - Point.evEvent->input_value( 1 ), - Point.evEvent->input_value( 2 ), - nullptr ); - } - } + mvOccupied->SecuritySystem.set_cabsignal_lock(!AIControllFlag); // don't make life difficult for the ai, but a human driver is a fair game + PutCommand(Point.evEvent->input_text(), Point.evEvent->input_value(1), Point.evEvent->input_value(2), nullptr); + } if( ismagnetpassed ) { Point.Clear(); mvOccupied->SecuritySystem.set_cabsignal_lock(false); @@ -1507,21 +1456,18 @@ TController::TableUpdateEvent( double &Velocity, TCommandType &Command, TSpeedPo } else if ( Point.iFlags & spStopOnSBL) { // jeśli S1 na SBL - if( mvOccupied->Vel < 2.0 ) { + if( mvOccupied->Vel < 2.0 && Point.fDist < fMaxProximityDist && Obstacle.distance > 1000 ) { // stanąć nie musi, ale zwolnić przynajmniej // jest w maksymalnym zasięgu to można go pominąć (wziąć drugą prędkosć) // as long as there isn't any obstacle in arbitrary view range - if( Point.fDist < fMaxProximityDist - && Obstacle.distance > 1000 ) { - eSignSkip = Point.evEvent; - // jazda na widoczność - skanować możliwość kolizji i nie podjeżdżać zbyt blisko - // usunąć flagę po podjechaniu blisko semafora zezwalającego na jazdę - // ostrożnie interpretować sygnały - semafor może zezwalać na jazdę pociągu z przodu! - iDrivigFlags |= moveVisibility; - // store the ordered restricted speed and don't exceed it until the flag is cleared - VelRestricted = Point.evEvent->input_value( 2 ); - } - } + eSignSkip = Point.evEvent; + // jazda na widoczność - skanować możliwość kolizji i nie podjeżdżać zbyt blisko + // usunąć flagę po podjechaniu blisko semafora zezwalającego na jazdę + // ostrożnie interpretować sygnały - semafor może zezwalać na jazdę pociągu z przodu! + iDrivigFlags |= moveVisibility; + // store the ordered restricted speed and don't exceed it until the flag is cleared + VelRestricted = Point.evEvent->input_value(2); + } if( eSignSkip != Point.evEvent ) { // jeśli ten SBL nie jest do pominięcia to ma 0 odczytywać Velocity = Point.evEvent->input_value( 1 ); @@ -1570,54 +1516,59 @@ TController::TableUpdateEvent( double &Velocity, TCommandType &Command, TSpeedPo if ( Point.fDist < 0) VelRoad = Point.fVelNext; } - else if ( Point.iFlags & spSectionVel) + else if ( Point.iFlags & spSectionVel && Point.fDist < 0) { // to W27 - if ( Point.fDist < 0) // teraz trzeba sprawdzić inne warunki - { - if ( Point.fSectionVelocityDist == 0.0) { - if( Global.iWriteLogEnabled & 8 ) { - WriteLog( "TableUpdate: Event is behind. SVD = 0: " + Point.evEvent->m_name ); - } - Point.Clear(); // jeśli punktowy to kasujemy i nie dajemy ograniczenia na stałe - } - else if ( Point.fSectionVelocityDist < 0.0) { - // ograniczenie obowiązujące do następnego - if ( Point.fVelNext == min_speed(Point.fVelNext, VelLimitLast) - && Point.fVelNext != VelLimitLast) { - // jeśli ograniczenie jest mniejsze niż obecne to obowiązuje od zaraz - VelLimitLast = Point.fVelNext; - } - else if ( Point.fDist < -fLength) { - // jeśli większe to musi wyjechać za poprzednie - VelLimitLast = Point.fVelNext; - if( Global.iWriteLogEnabled & 8 ) { - WriteLog( "TableUpdate: Event is behind. SVD < 0: " + Point.evEvent->m_name ); - } - Point.Clear(); // wyjechaliśmy poza poprzednie, można skasować - } - } - else - { // jeśli większe to ograniczenie ma swoją długość - if ( Point.fVelNext == min_speed(Point.fVelNext, VelLimitLast) - && Point.fVelNext != VelLimitLast) { - // jeśli ograniczenie jest mniejsze niż obecne to obowiązuje od zaraz - VelLimitLast = Point.fVelNext; - } - else if ( Point.fDist < -fLength - && Point.fVelNext != VelLimitLast) { - // jeśli większe to musi wyjechać za poprzednie - VelLimitLast = Point.fVelNext; - } - else if (Point.fDist < -fLength - Point.fSectionVelocityDist) { - VelLimitLast = -1.0; - if( Global.iWriteLogEnabled & 8 ) { - WriteLog( "TableUpdate: Event is behind. SVD > 0: " + Point.evEvent->m_name ); - } - Point.Clear(); // wyjechaliśmy poza poprzednie, można skasować - } - } - } - } + // teraz trzeba sprawdzić inne warunki + if (Point.fSectionVelocityDist == 0.0) + { + if (Global.iWriteLogEnabled & 8) + { + WriteLog("TableUpdate: Event is behind. SVD = 0: " + Point.evEvent->m_name); + } + Point.Clear(); // jeśli punktowy to kasujemy i nie dajemy ograniczenia na stałe + } + else if (Point.fSectionVelocityDist < 0.0) + { + // ograniczenie obowiązujące do następnego + if (Point.fVelNext == min_speed(Point.fVelNext, VelLimitLast) && Point.fVelNext != VelLimitLast) + { + // jeśli ograniczenie jest mniejsze niż obecne to obowiązuje od zaraz + VelLimitLast = Point.fVelNext; + } + else if (Point.fDist < -fLength) + { + // jeśli większe to musi wyjechać za poprzednie + VelLimitLast = Point.fVelNext; + if (Global.iWriteLogEnabled & 8) + { + WriteLog("TableUpdate: Event is behind. SVD < 0: " + Point.evEvent->m_name); + } + Point.Clear(); // wyjechaliśmy poza poprzednie, można skasować + } + } + else + { // jeśli większe to ograniczenie ma swoją długość + if (Point.fVelNext == min_speed(Point.fVelNext, VelLimitLast) && Point.fVelNext != VelLimitLast) + { + // jeśli ograniczenie jest mniejsze niż obecne to obowiązuje od zaraz + VelLimitLast = Point.fVelNext; + } + else if (Point.fDist < -fLength && Point.fVelNext != VelLimitLast) + { + // jeśli większe to musi wyjechać za poprzednie + VelLimitLast = Point.fVelNext; + } + else if (Point.fDist < -fLength - Point.fSectionVelocityDist) + { + VelLimitLast = -1.0; + if (Global.iWriteLogEnabled & 8) + { + WriteLog("TableUpdate: Event is behind. SVD > 0: " + Point.evEvent->m_name); + } + Point.Clear(); // wyjechaliśmy poza poprzednie, można skasować + } + } + } //sprawdzenie eventów pasywnych przed nami { // zawalidrogi nie ma (albo pojazd jest samochodem), sprawdzić sygnał @@ -1632,81 +1583,89 @@ TController::TableUpdateEvent( double &Velocity, TCommandType &Command, TSpeedPo Point.Clear(); } } - else if( Command == TCommandType::cm_Unknown ) { + else if( Command == TCommandType::cm_Unknown && Velocity != 0.0 ) { // jeśli jeszcze nie ma komendy // komenda jest tylko gdy ma jechać, bo stoi na podstawie tabelki - if( Velocity != 0.0 ) { - // jeśli nie było komendy wcześniej - pierwsza się liczy - ustawianie VelSignal - Command = TCommandType::cm_ShuntVelocity; // w trybie pociągowym tylko jeśli włącza tryb manewrowy (v!=0.0) - // Ra 2014-06: (VelSignal) nie może być tu ustawiane, bo Tm może być daleko - if( VelSignal == 0.0 ) { - // aby stojący ruszył - VelSignal = Velocity; - } - if( Point.fDist < 0.0 ) { - // jeśli przejechany - //!!! ustawienie, gdy przejechany jest lepsze niż wcale, ale to jeszcze nie to - VelSignal = Velocity; - // to można usunąć (nie mogą być usuwane w skanowaniu) - Point.Clear(); - } - } - } + // jeśli nie było komendy wcześniej - pierwsza się liczy - ustawianie VelSignal + Command = TCommandType::cm_ShuntVelocity; // w trybie pociągowym tylko jeśli włącza tryb manewrowy (v!=0.0) + // Ra 2014-06: (VelSignal) nie może być tu ustawiane, bo Tm może być daleko + if (VelSignal == 0.0) + { + // aby stojący ruszył + VelSignal = Velocity; + } + if (Point.fDist < 0.0) + { + // jeśli przejechany + //!!! ustawienie, gdy przejechany jest lepsze niż wcale, ale to jeszcze nie to + VelSignal = Velocity; + // to można usunąć (nie mogą być usuwane w skanowaniu) + Point.Clear(); + } + } } else if( ( Point.iFlags & spSectionVel ) == 0 ) { //jeśli jakiś event pasywny ale nie ograniczenie - if( Command == TCommandType::cm_Unknown ) { - // jeśli nie było komendy wcześniej - pierwsza się liczy - ustawianie VelSignal - if( Velocity < 0.0 - || Velocity >= 1.0 ) { - // bo wartość 0.1 służy do hamowania tylko - Command = TCommandType::cm_SetVelocity; // może odjechać - // Ra 2014-06: (VelSignal) nie może być tu ustawiane, bo semafor może być daleko - // VelSignal=v; //nie do końca tak, to jest druga prędkość; -1 nie wpisywać... - if( VelSignal == 0.0 ) { - // aby stojący ruszył - VelSignal = -1.0; - } - if( Point.fDist < 0.0 ) { - // jeśli przejechany - VelSignal = Velocity == 0.0 ? 0.0 : -1.0; - // ustawienie, gdy przejechany jest lepsze niż wcale, ale to jeszcze nie to - if( Point.iFlags & spEvent ) { - // jeśli event - if( Point.evEvent != eSignSkip - || Point.fVelNext != VelRestricted ) { - // ale inny niż ten, na którym minięto S1, chyba że się już zmieniło - // sygnał zezwalający na jazdę wyłącza jazdę na widoczność (po S1 na SBL) - iDrivigFlags &= ~moveVisibility; - // remove restricted speed - VelRestricted = -1.0; - } - } - // jeśli nie jest ograniczeniem prędkości to można usunąć - // (nie mogą być usuwane w skanowaniu) - Point.Clear(); - } - } - else if( Point.evEvent->is_command() ) { - // jeśli prędkość jest zerowa, a komórka zawiera komendę - eSignNext = Point.evEvent; // dla informacji - // make sure the command isn't for someone else - // TBD, TODO: revise this check for bank/loose_shunt; we might want to ignore obstacles with no owner in these modes - if( Point.fDist < Obstacle.distance ) { - if( true == TestFlag( iDrivigFlags, moveStopHere ) ) { - // jeśli ma stać, dostaje komendę od razu - Command = TCommandType::cm_Command; // komenda z komórki, do wykonania po zatrzymaniu - } - // NOTE: human drivers are likely to stop wherever and expect things to still work, so we give them more leeway - else if( Point.fDist <= ( AIControllFlag ? fMaxProximityDist : std::max( 250.0, fMaxProximityDist ) ) ) { - // jeśli ma dociągnąć, to niech dociąga - // (moveStopCloser dotyczy dociągania do W4, nie semafora) - Command = TCommandType::cm_Command; // komenda z komórki, do wykonania po zatrzymaniu - } - } - } - } - } + if( Command != TCommandType::cm_Unknown ) + { + } + else + { + // jeśli nie było komendy wcześniej - pierwsza się liczy - ustawianie VelSignal + if (Velocity < 0.0 || Velocity >= 1.0) + { + // bo wartość 0.1 służy do hamowania tylko + Command = TCommandType::cm_SetVelocity; // może odjechać + // Ra 2014-06: (VelSignal) nie może być tu ustawiane, bo semafor może być daleko + // VelSignal=v; //nie do końca tak, to jest druga prędkość; -1 nie wpisywać... + if (VelSignal == 0.0) + { + // aby stojący ruszył + VelSignal = -1.0; + } + if (Point.fDist < 0.0) + { + // jeśli przejechany + VelSignal = Velocity == 0.0 ? 0.0 : -1.0; + // ustawienie, gdy przejechany jest lepsze niż wcale, ale to jeszcze nie to + if (Point.iFlags & spEvent && (Point.evEvent != eSignSkip || Point.fVelNext != VelRestricted)) + { + // jeśli event + // ale inny niż ten, na którym minięto S1, chyba że się już zmieniło + // sygnał zezwalający na jazdę wyłącza jazdę na widoczność (po S1 na SBL) + iDrivigFlags &= ~moveVisibility; + // remove restricted speed + VelRestricted = -1.0; + } + // jeśli nie jest ograniczeniem prędkości to można usunąć + // (nie mogą być usuwane w skanowaniu) + Point.Clear(); + } + } + else if (Point.evEvent->is_command()) + { + // jeśli prędkość jest zerowa, a komórka zawiera komendę + eSignNext = Point.evEvent; // dla informacji + // make sure the command isn't for someone else + // TBD, TODO: revise this check for bank/loose_shunt; we might want to ignore obstacles with no owner in these modes + if (Point.fDist < Obstacle.distance) + { + if (true == TestFlag(iDrivigFlags, moveStopHere)) + { + // jeśli ma stać, dostaje komendę od razu + Command = TCommandType::cm_Command; // komenda z komórki, do wykonania po zatrzymaniu + } + // NOTE: human drivers are likely to stop wherever and expect things to still work, so we give them more leeway + else if (Point.fDist <= (AIControllFlag ? fMaxProximityDist : std::max(250.0, fMaxProximityDist))) + { + // jeśli ma dociągnąć, to niech dociąga + // (moveStopCloser dotyczy dociągania do W4, nie semafora) + Command = TCommandType::cm_Command; // komenda z komórki, do wykonania po zatrzymaniu + } + } + } + } + } } // jeśli nie ma zawalidrogi return false; @@ -1964,12 +1923,10 @@ std::string TController::OrderCurrent() const { // pobranie aktualnego rozkazu celem wyświetlenia auto const order { OrderCurrentGet() }; // generally prioritize direction change... - if( order & Change_direction ) { + if( order & Change_direction && false == TestFlag(iDrivigFlags, moveConnect) ) { // ...but not in the middle of coupling operation - if( false == TestFlag( iDrivigFlags, moveConnect ) ) { - return STR("Change direction"); - } - } + return STR("Change direction"); + } switch( static_cast( order & ~Change_direction ) ) { case Wait_for_orders: { return STR("Wait for orders"); } @@ -2085,14 +2042,10 @@ void TController::Activation() } BrakeLevelSet( mvOccupied->BrakeCtrlPos ); } - if( mvControlling->EngineType == TEngineType::DieselEngine ) { + if( mvControlling->EngineType == TEngineType::DieselEngine && mvControlling->ShuntModeAllow ) { // dla 2Ls150 - przed ustawieniem kierunku - można zmienić tryb pracy - if( mvControlling->ShuntModeAllow ) { - mvControlling->CurrentSwitch( - (OrderCurrentGet() & (Shunt | Loose_shunt)) != 0 - || fMass > 224000.0 ); // do tego na wzniesieniu może nie dać rady na liniowym - } - } + mvControlling->CurrentSwitch((OrderCurrentGet() & (Shunt | Loose_shunt)) != 0 || fMass > 224000.0); // do tego na wzniesieniu może nie dać rady na liniowym + } // Ra: to przełączanie poniżej jest tu bez sensu mvOccupied->CabOccupied = iDirection; // aktywacja kabiny w prowadzonym pojeżdzie (silnikowy może być odwrotnie?) mvOccupied->CabActivisation(); // uruchomienie kabin w członach @@ -2379,24 +2332,22 @@ bool TController::CheckVehicles(TOrders user) while (p) { // sprawdzanie, czy jest głównym sterującym, żeby nie było konfliktu if( p->Mechanik // jeśli ma obsadę - && p->Mechanik != this ) { // ale chodzi o inny pojazd, niż aktualnie sprawdzający - if( p->Mechanik->iDrivigFlags & movePrimary ) { - // a tamten ma priorytet - // TODO: take into account drivers' operating modes, one or more of them might be on banking duty - if( iDrivigFlags & movePrimary - && mvOccupied->DirAbsolute - && mvOccupied->BrakeCtrlPos >= -1 ) { - // jeśli rządzi i ma kierunek - p->Mechanik->primary( false ); // dezaktywuje tamtego - p->Mechanik->ZeroLocalBrake(); - p->MoverParameters->BrakeLevelSet( p->MoverParameters->Handle->GetPos( bh_NP ) ); // odcięcie na zaworze maszynisty - p->Mechanik->BrakeLevelSet( p->MoverParameters->BrakeCtrlPos ); //ustawienie zmiennej GBH - } - else { - main = false; // nici z rządzenia - } - } - } + && p->Mechanik != this && p->Mechanik->iDrivigFlags & movePrimary ) { // ale chodzi o inny pojazd, niż aktualnie sprawdzający + // a tamten ma priorytet + // TODO: take into account drivers' operating modes, one or more of them might be on banking duty + if (iDrivigFlags & movePrimary && mvOccupied->DirAbsolute && mvOccupied->BrakeCtrlPos >= -1) + { + // jeśli rządzi i ma kierunek + p->Mechanik->primary(false); // dezaktywuje tamtego + p->Mechanik->ZeroLocalBrake(); + p->MoverParameters->BrakeLevelSet(p->MoverParameters->Handle->GetPos(bh_NP)); // odcięcie na zaworze maszynisty + p->Mechanik->BrakeLevelSet(p->MoverParameters->BrakeCtrlPos); // ustawienie zmiennej GBH + } + else + { + main = false; // nici z rządzenia + } + } ++iVehicles; // jest jeden pojazd więcej pVehicles[end::rear] = p; // zapamiętanie ostatniego fLength += p->MoverParameters->Dim.L; // dodanie długości pojazdu @@ -2523,19 +2474,17 @@ bool TController::CheckVehicles(TOrders user) // potentially adjust light state control_lights(); // custom ai action for disconnect mode: switch off lights on disconnected vehicle(s) - if( is_train() ) { - if( true == TestFlag( OrderCurrentGet(), Disconnect ) ) { - if( AIControllFlag ) { - // światła manewrowe (Tb1) tylko z przodu, aby nie pozostawić odczepionego ze światłem - if( mvOccupied->DirActive >= 0 ) { // jak ma kierunek do przodu - pVehicles[ end::rear ]->RaLightsSet( -1, 0 ); - } - else { // jak dociska - pVehicles[ end::front ]->RaLightsSet( 0, -1 ); - } - } - } - } + if( is_train() && true == TestFlag(OrderCurrentGet(), Disconnect) && AIControllFlag ) { + // światła manewrowe (Tb1) tylko z przodu, aby nie pozostawić odczepionego ze światłem + if (mvOccupied->DirActive >= 0) + { // jak ma kierunek do przodu + pVehicles[end::rear]->RaLightsSet(-1, 0); + } + else + { // jak dociska + pVehicles[end::front]->RaLightsSet(0, -1); + } + } // enable door locks cue_action( driver_hint::consistdoorlockson ); // potentially enable train heating @@ -2642,10 +2591,10 @@ int TController::OrderDirectionChange(int newdir, TMoverParameters *Vehicle) IncBrake(); // niech hamuje if (Vehicle->DirActive == testd * Vehicle->CabActive) VelforDriver = -1; // można jechać, bo kierunek jest zgodny z żądanym - if (is_emu()) - if (Vehicle->DirActive > 0) - // if () //tylko jeśli jazda pociągowa (tego nie wiemy w momencie odpalania silnika) - Vehicle->DirectionForward(); // Ra: z przekazaniem do silnikowego + if (is_emu() && Vehicle->DirActive > 0) + // if () //tylko jeśli jazda pociągowa (tego nie wiemy w momencie odpalania silnika) + Vehicle->DirectionForward(); + // Ra: z przekazaniem do silnikowego return (int)VelforDriver; // zwraca prędkość mechanika } @@ -3115,11 +3064,10 @@ bool TController::IncBrake() OK = false; } } - if( /*GBH mvOccupied->BrakeCtrlPos*/BrakeCtrlPosition > 0 ) { - if( mvOccupied->Hamulec->Releaser() ) { - mvOccupied->BrakeReleaser( 0 ); - } - } + if( /*GBH mvOccupied->BrakeCtrlPos*/ + /*GBH mvOccupied->BrakeCtrlPos*/ BrakeCtrlPosition > 0 && mvOccupied->Hamulec->Releaser() ) { + mvOccupied->BrakeReleaser(0); + } break; } case TBrakeSystem::ElectroPneumatic: { @@ -3230,15 +3178,13 @@ bool TController::DecBrake() { auto const pos_diff { ( is_dmu() ? 0.25 : 1.0 ) }; deltaAcc = -AccDesired * BrakeAccFactor() - ( fBrake_a0[ 0 ] + 4 * (/*GBH mvOccupied->BrakeCtrlPosR*/BrakeCtrlPosition - pos_diff )*fBrake_a1[ 0 ] ); } - if (deltaAcc < 0) { - if(/*GBH mvOccupied->BrakeCtrlPosR*/BrakeCtrlPosition > 0 ) { - OK = /*mvOccupied->*/BrakeLevelAdd( -0.25 ); - //if ((deltaAcc < 5 * fBrake_a1[0]) && (mvOccupied->BrakeCtrlPosR >= 1.2)) - // mvOccupied->BrakeLevelAdd(-1.0); - /* if (mvOccupied->BrakeCtrlPosR < 0.74) GBH */ - if( BrakeCtrlPosition < 0.74 ) - /*mvOccupied->*/BrakeLevelSet( gbh_RP ); - } + if (deltaAcc < 0 && /*GBH mvOccupied->BrakeCtrlPosR*/ BrakeCtrlPosition > 0) { + OK = /*mvOccupied->*/ BrakeLevelAdd(-0.25); + // if ((deltaAcc < 5 * fBrake_a1[0]) && (mvOccupied->BrakeCtrlPosR >= 1.2)) + // mvOccupied->BrakeLevelAdd(-1.0); + /* if (mvOccupied->BrakeCtrlPosR < 0.74) GBH */ + if (BrakeCtrlPosition < 0.74) + /*mvOccupied->*/ BrakeLevelSet(gbh_RP); } if( !OK ) { OK = mvOccupied->DecLocalBrakeLevel(2); @@ -3380,183 +3326,173 @@ bool TController::IncSpeed() if( mvControlling->EnginePowerSource.SourceType == TPowerSource::Accumulator ) { fVoltage = mvControlling->BatteryVoltage; } - if (!IsAnyMotorOverloadRelayOpen - && !mvControlling->ControlPressureSwitch) { - if (mvControlling->IsMainCtrlNoPowerPos() - || mvControlling->StLinFlag) { // youBy polecił dodać 2012-09-08 v367 - // na pozycji 0 przejdzie, a na pozostałych będzie czekać, aż się załączą liniowe (zgaśnie DelayCtrlFlag) - if (Ready || iDrivigFlags & movePress) { - auto const usehighoverloadrelaythreshold { - mvControlling->TrainType != dt_ET42 // ET42 uses these variables for different purpose. TODO: fix this - && mvControlling->ImaxHi > mvControlling->ImaxLo - && mvOccupied->Vel < (mvControlling->IsMotorOverloadRelayHighThresholdOn() ? 30.0 : 20.0) - && iVehicles - ControlledEnginesCount > 0 -// && ( std::fabs( mvControlling->Im ) > 0.85 * mvControlling->Imax ) - && mvControlling->Imax * mvControlling->EngineVoltage * ControlledEnginesCount / - (fMass * (fAccGravity == 0.025 ? -0.01 : // prevent div/0 - fAccGravity - 0.025)) < - -2.8 }; - // use series mode: - // if high threshold is set for motor overload relay, - // if the power station is heavily burdened, - // if it generates enough traction force - // to build up speed to 30/40 km/h for passenger/cargo train (10 km/h less if going uphill) - auto const sufficienttractionforce { std::abs( mvControlling->Ft ) * ControlledEnginesCount > ( IsHeavyCargoTrain ? 75 : 50 ) * 1000.0 }; - auto const sufficientacceleration { AbsAccS >= ( IsHeavyCargoTrain ? 0.03 : IsCargoTrain ? 0.06 : 0.09 ) }; - auto const seriesmodefieldshunting { mvControlling->ScndCtrlPos > 0 && mvControlling->RList[mvControlling->MainCtrlPos].Bn == 1 }; - auto const parallelmodefieldshunting { mvControlling->ScndCtrlPos > 0 && mvControlling->RList[mvControlling->MainCtrlPos].Bn > 1 }; - auto const minvoltage { ( mvPantographUnit->EnginePowerSource.SourceType == TPowerSource::CurrentCollector ? mvPantographUnit->EnginePowerSource.CollectorParameters.MinV : 0.0 ) }; - auto const maxvoltage { ( mvPantographUnit->EnginePowerSource.SourceType == TPowerSource::CurrentCollector ? mvPantographUnit->EnginePowerSource.CollectorParameters.MaxV : 0.0 ) }; - auto const seriesmodevoltage { - std::lerp( - minvoltage, - maxvoltage, - IsHeavyCargoTrain ? 0.35 : 0.40 ) }; - auto const useseriesmode = mvControlling->Imax > mvControlling->ImaxLo || true == usehighoverloadrelaythreshold || fVoltage < seriesmodevoltage || - (true == sufficientacceleration && true == sufficienttractionforce && - mvOccupied->Vel <= (IsCargoTrain ? 40 : 30) + (seriesmodefieldshunting ? 5 : 0) - (fAccGravity < -0.025 ? 10 : 0)); - // when not in series mode use the first available parallel mode configuration until 50/60 km/h for passenger/cargo train - // (if there's only one parallel mode configuration it'll be used regardless of current speed) - auto const usefieldshunting = mvControlling->StLinFlag && mvControlling->RList[mvControlling->MainCtrlPos].R < 0.01 && - (useseriesmode ? mvControlling->RList[mvControlling->MainCtrlPos].Bn == 1 : - true == sufficientacceleration && true == sufficienttractionforce && mvOccupied->Vel <= (IsCargoTrain ? 60 : 50) + (parallelmodefieldshunting ? 5 : 0) ? - mvControlling->RList[mvControlling->MainCtrlPos].Bn > 1 : - mvControlling->MainCtrlPos == mvControlling->MainCtrlPosNo); + if (!IsAnyMotorOverloadRelayOpen && !mvControlling->ControlPressureSwitch && (mvControlling->IsMainCtrlNoPowerPos() || mvControlling->StLinFlag) && (Ready || iDrivigFlags & movePress)) { + // youBy polecił dodać 2012-09-08 v367 + // na pozycji 0 przejdzie, a na pozostałych będzie czekać, aż się załączą liniowe (zgaśnie DelayCtrlFlag) + auto const usehighoverloadrelaythreshold{mvControlling->TrainType != dt_ET42 // ET42 uses these variables for different purpose. TODO: fix this + && mvControlling->ImaxHi > mvControlling->ImaxLo && mvOccupied->Vel < (mvControlling->IsMotorOverloadRelayHighThresholdOn() ? 30.0 : 20.0) && + iVehicles - ControlledEnginesCount > 0 + // && ( std::fabs( mvControlling->Im ) > 0.85 * mvControlling->Imax ) + && mvControlling->Imax * mvControlling->EngineVoltage * ControlledEnginesCount / + (fMass * (fAccGravity == 0.025 ? -0.01 : // prevent div/0 + fAccGravity - 0.025)) < + -2.8}; + // use series mode: + // if high threshold is set for motor overload relay, + // if the power station is heavily burdened, + // if it generates enough traction force + // to build up speed to 30/40 km/h for passenger/cargo train (10 km/h less if going uphill) + auto const sufficienttractionforce{std::abs(mvControlling->Ft) * ControlledEnginesCount > (IsHeavyCargoTrain ? 75 : 50) * 1000.0}; + auto const sufficientacceleration{AbsAccS >= (IsHeavyCargoTrain ? 0.03 : IsCargoTrain ? 0.06 : 0.09)}; + auto const seriesmodefieldshunting{mvControlling->ScndCtrlPos > 0 && mvControlling->RList[mvControlling->MainCtrlPos].Bn == 1}; + auto const parallelmodefieldshunting{mvControlling->ScndCtrlPos > 0 && mvControlling->RList[mvControlling->MainCtrlPos].Bn > 1}; + auto const minvoltage{(mvPantographUnit->EnginePowerSource.SourceType == TPowerSource::CurrentCollector ? mvPantographUnit->EnginePowerSource.CollectorParameters.MinV : 0.0)}; + auto const maxvoltage{(mvPantographUnit->EnginePowerSource.SourceType == TPowerSource::CurrentCollector ? mvPantographUnit->EnginePowerSource.CollectorParameters.MaxV : 0.0)}; + auto const seriesmodevoltage{std::lerp(minvoltage, maxvoltage, IsHeavyCargoTrain ? 0.35 : 0.40)}; + auto const useseriesmode = mvControlling->Imax > mvControlling->ImaxLo || true == usehighoverloadrelaythreshold || fVoltage < seriesmodevoltage || + (true == sufficientacceleration && true == sufficienttractionforce && + mvOccupied->Vel <= (IsCargoTrain ? 40 : 30) + (seriesmodefieldshunting ? 5 : 0) - (fAccGravity < -0.025 ? 10 : 0)); + // when not in series mode use the first available parallel mode configuration until 50/60 km/h for passenger/cargo train + // (if there's only one parallel mode configuration it'll be used regardless of current speed) + auto const usefieldshunting = mvControlling->StLinFlag && mvControlling->RList[mvControlling->MainCtrlPos].R < 0.01 && + (useseriesmode ? mvControlling->RList[mvControlling->MainCtrlPos].Bn == 1 : + true == sufficientacceleration && true == sufficienttractionforce && mvOccupied->Vel <= (IsCargoTrain ? 60 : 50) + (parallelmodefieldshunting ? 5 : 0) ? + mvControlling->RList[mvControlling->MainCtrlPos].Bn > 1 : + mvControlling->MainCtrlPos == mvControlling->MainCtrlPosNo); - // if needed enable high threshold for overload relay... - if( mvControlling->TrainType != dt_ET42 ) { // ET42 uses these variables for different purpose. TODO: fix this - if( usehighoverloadrelaythreshold ) { - if( mvControlling->Imax < mvControlling->ImaxHi ) { - // to enable this setting we'll typically need the main controller in series mode (which is not guaranteed) - if( mvControlling->RList[ mvControlling->MainCtrlPos ].Bn > 1 ) { - if( false == mvControlling->IsScndCtrlNoPowerPos() ) { - mvControlling->DecScndCtrl( 2 ); - } - while( false == mvControlling->IsMainCtrlNoPowerPos() - && mvControlling->RList[mvControlling->MainCtrlPos].Bn > 1 ) { - mvControlling->DecMainCtrl( 1 ); // kręcimy nastawnik jazdy o 1 wstecz - } - } - mvControlling->CurrentSwitch( true ); // rozruch wysoki (za to może się ślizgać) - } - } - // ...or disable high threshold for overload relay if no longer needed - else { - if( mvControlling->IsMotorOverloadRelayHighThresholdOn() - && std::fabs(mvControlling->Im) < mvControlling->ImaxLo ) { - mvControlling->CurrentSwitch( false ); // rozruch wysoki wyłącz - } - } - } - - double Vs = 99999; - if( usefieldshunting ? - mvControlling->ScndCtrlPos < mvControlling->ScndCtrlPosNo : - mvControlling->MainCtrlPos < mvControlling->MainCtrlPosNo ) { - Vs = ESMVelocity( !usefieldshunting ); - } - - if( std::abs(mvControlling->Im) < (fReady < 0.4 ? mvControlling->Imin : mvControlling->IminLo) - || mvControlling->Vel > Vs ) { - // Ra: wywalał nadmiarowy, bo Im może być ujemne; jak nie odhamowany, to nie przesadzać z prądem - if( usefieldshunting ) { - // to dać bocznik - // engage the shuntfield only if there's sufficient power margin to draw from - auto const sufficientpowermargin { fVoltage - seriesmodevoltage > ( IsHeavyCargoTrain ? 100.0 : 75.0 ) * ControlledEnginesCount }; - - OK = sufficientpowermargin ? mvControlling->IncScndCtrl(1) : true; - } - else { - // jeśli ustawiony bocznik to bocznik na zero po chamsku - if( mvControlling->ScndCtrlPos ) { - mvControlling->DecScndCtrl( 2 ); - } - // kręcimy nastawnik jazdy - // don't draw too much power; - // keep from dropping into series mode when entering/using parallel mode, and from shutting down in the series mode - auto const sufficientpowermargin { - fVoltage - ( - mvControlling->RList[ std::min( mvControlling->MainCtrlPos + 1, mvControlling->MainCtrlPosNo ) ].Bn == 1 ? - minvoltage : - seriesmodevoltage ) - > ( IsHeavyCargoTrain ? 100.0 : 75.0 ) * ControlledEnginesCount }; - - OK = sufficientpowermargin && false == mvControlling->DelayCtrlFlag ? mvControlling->IncMainCtrl(1) : true; - // czekaj na 1 pozycji, zanim się nie włączą liniowe - if( true == mvControlling->StLinFlag ) { - iDrivigFlags |= moveIncSpeed; - } - else { - iDrivigFlags &= ~moveIncSpeed; - } - - if( mvControlling->Im == 0 - && mvControlling->MainCtrlPowerPos() > 1 ) { - // brak prądu na dalszych pozycjach - // nie załączona lokomotywa albo wywalił nadmiarowy - Need_TryAgain = true; - } - } + // if needed enable high threshold for overload relay... + if (mvControlling->TrainType != dt_ET42) + { // ET42 uses these variables for different purpose. TODO: fix this + if (usehighoverloadrelaythreshold) + { + if (mvControlling->Imax < mvControlling->ImaxHi) + { + // to enable this setting we'll typically need the main controller in series mode (which is not guaranteed) + if (mvControlling->RList[mvControlling->MainCtrlPos].Bn > 1) + { + if (false == mvControlling->IsScndCtrlNoPowerPos()) + { + mvControlling->DecScndCtrl(2); + } + while (false == mvControlling->IsMainCtrlNoPowerPos() && mvControlling->RList[mvControlling->MainCtrlPos].Bn > 1) + { + mvControlling->DecMainCtrl(1); // kręcimy nastawnik jazdy o 1 wstecz + } + } + mvControlling->CurrentSwitch(true); // rozruch wysoki (za to może się ślizgać) } - else { - OK = false; - } } - } - } + // ...or disable high threshold for overload relay if no longer needed + else + { + if (mvControlling->IsMotorOverloadRelayHighThresholdOn() && std::fabs(mvControlling->Im) < mvControlling->ImaxLo) + { + mvControlling->CurrentSwitch(false); // rozruch wysoki wyłącz + } + } + } + + double Vs = 99999; + if (usefieldshunting ? mvControlling->ScndCtrlPos < mvControlling->ScndCtrlPosNo : mvControlling->MainCtrlPos < mvControlling->MainCtrlPosNo) + { + Vs = ESMVelocity(!usefieldshunting); + } + + if (std::abs(mvControlling->Im) < (fReady < 0.4 ? mvControlling->Imin : mvControlling->IminLo) || mvControlling->Vel > Vs) + { + // Ra: wywalał nadmiarowy, bo Im może być ujemne; jak nie odhamowany, to nie przesadzać z prądem + if (usefieldshunting) + { + // to dać bocznik + // engage the shuntfield only if there's sufficient power margin to draw from + auto const sufficientpowermargin{fVoltage - seriesmodevoltage > (IsHeavyCargoTrain ? 100.0 : 75.0) * ControlledEnginesCount}; + + OK = sufficientpowermargin ? mvControlling->IncScndCtrl(1) : true; + } + else + { + // jeśli ustawiony bocznik to bocznik na zero po chamsku + if (mvControlling->ScndCtrlPos) + { + mvControlling->DecScndCtrl(2); + } + // kręcimy nastawnik jazdy + // don't draw too much power; + // keep from dropping into series mode when entering/using parallel mode, and from shutting down in the series mode + auto const sufficientpowermargin{fVoltage - + (mvControlling->RList[std::min(mvControlling->MainCtrlPos + 1, mvControlling->MainCtrlPosNo)].Bn == 1 ? minvoltage : seriesmodevoltage) > + (IsHeavyCargoTrain ? 100.0 : 75.0) * ControlledEnginesCount}; + + OK = sufficientpowermargin && false == mvControlling->DelayCtrlFlag ? mvControlling->IncMainCtrl(1) : true; + // czekaj na 1 pozycji, zanim się nie włączą liniowe + if (true == mvControlling->StLinFlag) + { + iDrivigFlags |= moveIncSpeed; + } + else + { + iDrivigFlags &= ~moveIncSpeed; + } + + if (mvControlling->Im == 0 && mvControlling->MainCtrlPowerPos() > 1) + { + // brak prądu na dalszych pozycjach + // nie załączona lokomotywa albo wywalił nadmiarowy + Need_TryAgain = true; + } + } + } + else + { + OK = false; + } + } // mvControlling->AutoRelayCheck(); // sprawdzenie logiki sterowania break; case TEngineType::Dumb: - if (!IsAnyMotorOverloadRelayOpen) - if (Ready || iDrivigFlags & movePress) //{(BrakePress<=0.01*MaxBrakePress)} - { - OK = IncSpeedEIM(); - if( !OK ) - OK = mvControlling->IncMainCtrl(1); - if (!OK) - OK = mvControlling->IncScndCtrl(1); - } - break; + if (!IsAnyMotorOverloadRelayOpen && (Ready || iDrivigFlags & movePress)) + //{(BrakePress<=0.01*MaxBrakePress)} + { + OK = IncSpeedEIM(); + if (!OK) + OK = mvControlling->IncMainCtrl(1); + if (!OK) + OK = mvControlling->IncScndCtrl(1); + } + break; case TEngineType::DieselElectric: - if (!IsAnyMotorOverloadRelayOpen - && !mvControlling->ControlPressureSwitch) { - if (mvControlling->IsMainCtrlNoPowerPos() || - mvControlling->StLinFlag) { // youBy polecił dodać 2012-09-08 v367 - // na pozycji 0 przejdzie, a na pozostałych będzie czekać, aż się załączą liniowe (zgaśnie DelayCtrlFlag) - if (Ready || iDrivigFlags & movePress) //{(BrakePress<=0.01*MaxBrakePress)} - { - OK = IncSpeedEIM(); - if( !OK ) - OK = mvControlling->IncMainCtrl(1); - if (!OK) - OK = mvControlling->IncScndCtrl(1); - } - } - } + if (!IsAnyMotorOverloadRelayOpen && !mvControlling->ControlPressureSwitch && (mvControlling->IsMainCtrlNoPowerPos() || mvControlling->StLinFlag) && (Ready || iDrivigFlags & movePress)) { + // youBy polecił dodać 2012-09-08 v367 + // na pozycji 0 przejdzie, a na pozostałych będzie czekać, aż się załączą liniowe (zgaśnie DelayCtrlFlag) + //{(BrakePress<=0.01*MaxBrakePress)} + OK = IncSpeedEIM(); + if (!OK) + OK = mvControlling->IncMainCtrl(1); + if (!OK) + OK = mvControlling->IncScndCtrl(1); + } break; case TEngineType::ElectricInductionMotor: - if( !IsAnyMotorOverloadRelayOpen ) { - if( Ready || iDrivigFlags & movePress || mvOccupied->ShuntMode ) //{(BrakePress<=0.01*MaxBrakePress)} - { - OK = IncSpeedEIM(); - if( mvControlling->SpeedCtrl && mvControlling->Mains ) { - // cruise control - auto const couplinginprogress{ true == TestFlag(iDrivigFlags, moveConnect) && true == TestFlag(OrderCurrentGet(), Connect) }; - auto const SpeedCntrlVel{ ( - ActualProximityDist > std::max(50.0, fMaxProximityDist) || couplinginprogress ? - VelDesired : - min_speed( VelDesired, VelNext ) ) }; - if( SpeedCntrlVel >= mvControlling->SpeedCtrlUnit.MinVelocity - && SpeedCntrlVel - mvControlling->SpeedCtrlUnit.MinVelocity == quantize(SpeedCntrlVel - mvControlling->SpeedCtrlUnit.MinVelocity, mvControlling->SpeedCtrlUnit.VelocityStep) ) { - SpeedCntrl( SpeedCntrlVel ); - } - else if( SpeedCntrlVel > 0.1 ) { - SpeedCntrl( 0.0 ); - mvControlling->DecScndCtrl( 2 ); - } - } - } - } + if( !IsAnyMotorOverloadRelayOpen && (Ready || iDrivigFlags & movePress || mvOccupied->ShuntMode) ) { + //{(BrakePress<=0.01*MaxBrakePress)} + OK = IncSpeedEIM(); + if (mvControlling->SpeedCtrl && mvControlling->Mains) + { + // cruise control + auto const couplinginprogress{true == TestFlag(iDrivigFlags, moveConnect) && true == TestFlag(OrderCurrentGet(), Connect)}; + auto const SpeedCntrlVel{(ActualProximityDist > std::max(50.0, fMaxProximityDist) || couplinginprogress ? VelDesired : min_speed(VelDesired, VelNext))}; + if (SpeedCntrlVel >= mvControlling->SpeedCtrlUnit.MinVelocity && + SpeedCntrlVel - mvControlling->SpeedCtrlUnit.MinVelocity == quantize(SpeedCntrlVel - mvControlling->SpeedCtrlUnit.MinVelocity, mvControlling->SpeedCtrlUnit.VelocityStep)) + { + SpeedCntrl(SpeedCntrlVel); + } + else if (SpeedCntrlVel > 0.1) + { + SpeedCntrl(0.0); + mvControlling->DecScndCtrl(2); + } + } + } break; case TEngineType::WheelsDriven: if (!mvControlling->CabActive) @@ -3633,10 +3569,10 @@ bool TController::DecSpeed(bool force) { case TEngineType::None: // McZapkie-041003: wagon sterowniczy iDrivigFlags &= ~moveIncSpeed; // usunięcie flagi jazdy - if (force) // przy aktywacji kabiny jest potrzeba natychmiastowego wyzerowania - if (mvControlling->MainCtrlPosNo > 0) // McZapkie-041003: wagon sterowniczy, np. EZT - mvControlling->DecMainCtrl( std::min( mvControlling->MainCtrlPowerPos(), 2 ) ); -// mvControlling->AutoRelayCheck(); // sprawdzenie logiki sterowania + if (force && mvControlling->MainCtrlPosNo > 0) // przy aktywacji kabiny jest potrzeba natychmiastowego wyzerowania + // McZapkie-041003: wagon sterowniczy, np. EZT + mvControlling->DecMainCtrl(std::min(mvControlling->MainCtrlPowerPos(), 2)); + // mvControlling->AutoRelayCheck(); // sprawdzenie logiki sterowania return false; case TEngineType::ElectricSeriesMotor: OK = mvControlling->DecScndCtrl(2); // najpierw bocznik na zero @@ -3927,17 +3863,15 @@ void TController::SpeedSet() { } } // jak prędkość mniejsza niż minimalna na danym biegu, wrzucić niższy - else if( velocity < motorparams.fi ) { + else if( velocity < motorparams.fi && mvControlling->ScndCtrlPos > 1 ) { // ... but ensure we don't switch all way down to 0 - if( mvControlling->ScndCtrlPos > 1 ) { - mvControlling->DecMainCtrl( 2 ); - while( mvControlling->ScndCtrlPos > 1 // repeat the entry check as we're working in a loop - && mvControlling->DecScndCtrl(1) - && mvControlling->MotorParam[mvControlling->ScndCtrlPos].mIsat == 0.0 ) { // jeśli bieg jałowy to kolejny - ; - } - } - } + mvControlling->DecMainCtrl(2); + while (mvControlling->ScndCtrlPos > 1 // repeat the entry check as we're working in a loop + && mvControlling->DecScndCtrl(1) && mvControlling->MotorParam[mvControlling->ScndCtrlPos].mIsat == 0.0) + { // jeśli bieg jałowy to kolejny + ; + } + } } break; } @@ -4149,37 +4083,40 @@ void TController::SetTimeControllers() } } // 5.5 universal control for diesel electric vehicles - if (mvControlling->EngineType == TEngineType::DieselElectric && mvControlling->EIMCtrlType == 3) + if (mvControlling->EngineType == TEngineType::DieselElectric && mvControlling->EIMCtrlType == 3 && AccDesired >= 0.0 && mvControlling->StLinFlag) { // NOTE: partial implementation for speedinc/dec // TBD, TODO: implement fully? - if( AccDesired >= 0.0 - && mvControlling->StLinFlag ) { + auto const PosInc{mvControlling->MainCtrlPosNo}; + auto PosKeep{0}; + auto PosDec{0}; + for (int i = PosInc; i >= 0; --i) + { + if (mvControlling->UniCtrlList[i].SetCtrlVal <= 0) + { + if (mvControlling->UniCtrlList[i].SpeedDown == 0.0) + { + PosKeep = i; + } + if (mvControlling->UniCtrlList[i].SpeedDown > 0.01) + { + PosDec = i; + break; + } + } + } - auto const PosInc { mvControlling->MainCtrlPosNo }; - auto PosKeep { 0 }; - auto PosDec{ 0 }; - for( int i = PosInc; i >= 0; --i ) { - if( mvControlling->UniCtrlList[ i ].SetCtrlVal <= 0 ) { - if( mvControlling->UniCtrlList[ i ].SpeedDown == 0.0 ) { - PosKeep = i; - } - if( mvControlling->UniCtrlList[ i ].SpeedDown > 0.01 ) { - PosDec = i; - break; - } - } - } + auto const DesiredPos{(AccDesired > AbsAccS + 0.05 ? PosInc : AccDesired < AbsAccS - 0.05 ? PosDec : PosKeep)}; - auto const DesiredPos { ( - AccDesired > AbsAccS + 0.05 ? PosInc : - AccDesired < AbsAccS - 0.05 ? PosDec : - PosKeep ) }; - - while( mvControlling->MainCtrlPos > DesiredPos && mvControlling->DecMainCtrl( 1 ) ) { ; } - while( mvControlling->MainCtrlPos < DesiredPos && mvControlling->IncMainCtrl( 1 ) ) { ; } - } - } + while (mvControlling->MainCtrlPos > DesiredPos && mvControlling->DecMainCtrl(1)) + { + ; + } + while (mvControlling->MainCtrlPos < DesiredPos && mvControlling->IncMainCtrl(1)) + { + ; + } + } //6. UniversalBrakeButtons //6.1. Checking flags for Over pressure if (std::abs(BrakeCtrlPosition - gbh_FS)<0.5) { @@ -4228,12 +4165,9 @@ void TController::CheckTimeControllers() } } //4. Check Speed Control System - if (mvOccupied->EngineType == TEngineType::ElectricInductionMotor && mvOccupied->ScndCtrlPosNo>1 && mvOccupied->SpeedCtrlTypeTime) + if (mvOccupied->EngineType == TEngineType::ElectricInductionMotor && mvOccupied->ScndCtrlPosNo > 1 && mvOccupied->SpeedCtrlTypeTime && mvOccupied->ScndCtrlPosNo == 4) { - if (mvOccupied->ScndCtrlPosNo == 4) - { - mvOccupied->ScndCtrlPos = 2; - } + mvOccupied->ScndCtrlPos = 2; } //5. Check Main Controller in Dizels @@ -4315,15 +4249,12 @@ void TController::Doors( bool const Open, int const Side ) { return; } - if( true == doors_open() ) { + if( true == doors_open() && (pVehicle->MoverParameters->Doors.close_control == control_t::conductor || pVehicle->MoverParameters->Doors.close_control == control_t::driver || + pVehicle->MoverParameters->Doors.close_control == control_t::mixed) ) { // consist-wide remote signals to close doors - if( pVehicle->MoverParameters->Doors.close_control == control_t::conductor - || pVehicle->MoverParameters->Doors.close_control == control_t::driver - || pVehicle->MoverParameters->Doors.close_control == control_t::mixed ) { - cue_action( driver_hint::doorrightclose ); - cue_action( driver_hint::doorleftclose ); - } - } + cue_action(driver_hint::doorrightclose); + cue_action(driver_hint::doorleftclose); + } if( true == doors_permit_active() ) { cue_action( driver_hint::doorrightpermitoff ); cue_action( driver_hint::doorleftpermitoff ); @@ -5058,15 +4989,13 @@ void TController::JumpToNextOrder( bool const Ignoremergedchangedirection ) if (OrderList[OrderPos] != Wait_for_orders) { if( (OrderList[OrderPos] & Change_direction) != 0 // jeśli zmiana kierunku - && OrderList[OrderPos] != Change_direction ) { // ale nałożona na coś + && OrderList[OrderPos] != Change_direction && false == Ignoremergedchangedirection ) { // ale nałożona na coś - if( false == Ignoremergedchangedirection ) { - // usunięcie zmiany kierunku z innej komendy - OrderList[ OrderPos ] = TOrders( OrderList[ OrderPos ] & ~Change_direction ); - OrderCheck(); - return; - } - } + // usunięcie zmiany kierunku z innej komendy + OrderList[OrderPos] = TOrders(OrderList[OrderPos] & ~Change_direction); + OrderCheck(); + return; + } if (OrderPos < maxorders - 1) ++OrderPos; else @@ -5142,9 +5071,10 @@ void TController::OrderNext(TOrders NewOrder) void TController::OrderPush(TOrders NewOrder) { // zapisanie na stosie kolejnego rozkazu do wykonania - if (OrderPos == OrderTop) // jeśli miałby być zapis na aktalnej pozycji - if (OrderList[OrderPos] < Shunt) // ale nie jedzie - ++OrderTop; // niektóre operacje muszą zostać najpierw dokończone => zapis na kolejnej + if (OrderPos == OrderTop && OrderList[OrderPos] < Shunt) // jeśli miałby być zapis na aktalnej pozycji + // ale nie jedzie + ++OrderTop; + // niektóre operacje muszą zostać najpierw dokończone => zapis na kolejnej if (OrderList[OrderTop] != NewOrder) // jeśli jest to samo, to nie dodajemy OrderList[OrderTop++] = NewOrder; // dodanie rozkazu na stos // if (OrderTop" : " " ) + (std::to_string(b) + ": " + Order2Str(OrderList[b]))); - if (b) // z wyjątkiem pierwszej pozycji - if (OrderList[b] == Wait_for_orders) // jeśli końcowa komenda - break; // dalej nie trzeba + if (b && OrderList[b] == Wait_for_orders) // z wyjątkiem pierwszej pozycji + // jeśli końcowa komenda + break; + // dalej nie trzeba } }; @@ -5523,66 +5454,64 @@ TCommandType TController::BackwardScan( double const Range ) } } // reakcja AI w trybie manewrowym dodatkowo na sygnały manewrowe - if (OrderCurrentGet() ? OrderCurrentGet() & (Shunt | Loose_shunt | Connect) : - true) // w Wait_for_orders też widzi tarcze + if ((OrderCurrentGet() ? OrderCurrentGet() & (Shunt | Loose_shunt | Connect) : true) && (move || e->input_command() == TCommandType::cm_ShuntVelocity)) // w Wait_for_orders też widzi tarcze { - if (move || e->input_command() == TCommandType::cm_ShuntVelocity) - { // jeśli powyżej było SetVelocity 0 0, to dociągamy pod S1 -/* - if ((scandist > fMinProximityDist) && - ((mvOccupied->Vel > EU07_AI_NOMOVEMENT) || (scanvel == 0.0))) - { - // jeśli tarcza jest daleko, to: - //- jesli pojazd jedzie, to informujemy o zmianie prędkości - //- jeśli stoi, to z własnej inicjatywy może podjechać pod zamkniętą tarczę - if (mvOccupied->Vel > EU07_AI_NOMOVEMENT) // tylko jeśli jedzie - { // Mechanik->PutCommand("SetProximityVelocity",scandist,scanvel,sl); -#if LOGBACKSCAN - // WriteLog(edir+"SetProximityVelocity "+AnsiString(scandist)+" - // "+AnsiString(scanvel)); - WriteLog(edir); -#endif - // SetProximityVelocity(scandist,scanvel,&sl); - // jeśli jedzie na W5 albo koniec toru, to można zmienić kierunek - return (iDrivigFlags & moveTrackEnd) ? TCommandType::cm_ChangeDirection : - TCommandType::cm_Unknown; - // TBD: request direction change also if VelNext in current direction is 0, - // while signal behind requests movement? - } - } - else - { - // ustawiamy prędkość tylko wtedy, gdy ma ruszyć, albo stanąć albo ma stać pod - // tarczą stop trzeba powtarzać, bo inaczej zatrąbi i pojedzie sam if - // ((MoverParameters->Vel==0.0)||(scanvel==0.0)) //jeśli jedzie lub ma stanąć/stać - { // nie dostanie komendy jeśli jedzie i ma jechać - // PutCommand("ShuntVelocity",scanvel,e->Params[9].asMemCell->Value2(),&sl,stopSem); -#if LOGBACKSCAN - WriteLog(edir + " - [ShuntVelocity] [" + to_string(scanvel, 2) + "] [" + - to_string(e->input_value(2), 2) + "]"); -#endif - return (scanvel > 0 ? TCommandType::cm_ShuntVelocity : - TCommandType::cm_Unknown); - } - } - // NOTE: dead code due to returns in branching above - if ((scanvel != 0.0) && (scandist < 100.0)) - { - // jeśli Tm w odległości do 100m podaje zezwolenie na jazdę, to od razu ją - // ignorujemy, aby móc szukać kolejnej eSignSkip=e; //wtedy uznajemy ignorowaną przy - // poszukiwaniu nowej -#if LOGBACKSCAN - WriteLog(edir + " - will be ignored due to Ms2"); -#endif - return (scanvel > 0 ? TCommandType::cm_ShuntVelocity : TCommandType::cm_Unknown); - } -*/ - return VelNext > 0 ? + // jeśli powyżej było SetVelocity 0 0, to dociągamy pod S1 + /* + if ((scandist > fMinProximityDist) && + ((mvOccupied->Vel > EU07_AI_NOMOVEMENT) || (scanvel == 0.0))) + { + // jeśli tarcza jest daleko, to: + //- jesli pojazd jedzie, to informujemy o zmianie prędkości + //- jeśli stoi, to z własnej inicjatywy może podjechać pod zamkniętą tarczę + if (mvOccupied->Vel > EU07_AI_NOMOVEMENT) // tylko jeśli jedzie + { // Mechanik->PutCommand("SetProximityVelocity",scandist,scanvel,sl); + #if LOGBACKSCAN + // WriteLog(edir+"SetProximityVelocity "+AnsiString(scandist)+" + // "+AnsiString(scanvel)); + WriteLog(edir); + #endif + // SetProximityVelocity(scandist,scanvel,&sl); + // jeśli jedzie na W5 albo koniec toru, to można zmienić kierunek + return (iDrivigFlags & moveTrackEnd) ? TCommandType::cm_ChangeDirection : + TCommandType::cm_Unknown; + // TBD: request direction change also if VelNext in current direction is 0, + // while signal behind requests movement? + } + } + else + { + // ustawiamy prędkość tylko wtedy, gdy ma ruszyć, albo stanąć albo ma stać pod + // tarczą stop trzeba powtarzać, bo inaczej zatrąbi i pojedzie sam if + // ((MoverParameters->Vel==0.0)||(scanvel==0.0)) //jeśli jedzie lub ma stanąć/stać + { // nie dostanie komendy jeśli jedzie i ma jechać + // PutCommand("ShuntVelocity",scanvel,e->Params[9].asMemCell->Value2(),&sl,stopSem); + #if LOGBACKSCAN + WriteLog(edir + " - [ShuntVelocity] [" + to_string(scanvel, 2) + "] [" + + to_string(e->input_value(2), 2) + "]"); + #endif + return (scanvel > 0 ? TCommandType::cm_ShuntVelocity : + TCommandType::cm_Unknown); + } + } + // NOTE: dead code due to returns in branching above + if ((scanvel != 0.0) && (scandist < 100.0)) + { + // jeśli Tm w odległości do 100m podaje zezwolenie na jazdę, to od razu ją + // ignorujemy, aby móc szukać kolejnej eSignSkip=e; //wtedy uznajemy ignorowaną przy + // poszukiwaniu nowej + #if LOGBACKSCAN + WriteLog(edir + " - will be ignored due to Ms2"); + #endif + return (scanvel > 0 ? TCommandType::cm_ShuntVelocity : TCommandType::cm_Unknown); + } + */ + return VelNext > 0 ? TCommandType::cm_Unknown : // no need to bother if we can continue in current direction scanvel == 0 ? TCommandType::cm_Unknown : // no need to bother with a stop signal TCommandType::cm_ShuntVelocity; // otherwise report a relevant shunt signal behind - } // if (move?... + // if (move?... } // if (OrderCurrentGet()==Shunt) return e->m_passive && e->is_command() ? TCommandType::cm_Command : TCommandType::cm_Unknown; @@ -5978,25 +5907,19 @@ TController::determine_consist_state() { Ready = false; } // Ra: odluźnianie przeładowanych lokomotyw, ciągniętych na zimno - prowizorka... - if( bp >= 0.4 ) { // wg UIC określone sztywno na 0.04 - if( AIControllFlag || (Global.AITrainman && mvOccupied->Vel < EU07_AI_NOMOVEMENT && !is_emu() && !is_dmu())) { - if( BrakeCtrlPosition == gbh_RP // jest pozycja jazdy - && false == TestFlag(vehicle->Hamulec->GetBrakeStatus(), b_dmg) // brake isn't broken - && vehicle->PipePress - mvOccupied->Handle->GetRP() > -0.1 // jeśli ciśnienie jak dla jazdy - && vehicle->Hamulec->GetCRP() > vehicle->PipePress + 0.12 ) { // za dużo w zbiorniku - // indywidualne luzowanko - vehicle->BrakeReleaser( 1 ); - } - } - } - if (bp < 0.1) { - if ( AIControllFlag || Global.AITrainman ) { - if (false == TestFlag(vehicle->Hamulec->GetBrakeStatus(), b_dmg) // brake isn't broken - && vehicle->Hamulec->GetCRP() < vehicle->PipePress - 0.1 ) { // już nie jest za dużo w zbiorniku - // koniec indywidualnego luzowanka - vehicle->BrakeReleaser( 0 ); - } - } + if( bp >= 0.4 && (AIControllFlag || Global.AITrainman && mvOccupied->Vel < EU07_AI_NOMOVEMENT && !is_emu() && !is_dmu()) && BrakeCtrlPosition == gbh_RP // jest pozycja jazdy + && false == TestFlag(vehicle->Hamulec->GetBrakeStatus(), b_dmg) // brake isn't broken + && vehicle->PipePress - mvOccupied->Handle->GetRP() > -0.1 // jeśli ciśnienie jak dla jazdy + && vehicle->Hamulec->GetCRP() > vehicle->PipePress + 0.12 ) { // wg UIC określone sztywno na 0.04 + // za dużo w zbiorniku + // indywidualne luzowanko + vehicle->BrakeReleaser(1); + } + if (bp < 0.1 && (AIControllFlag || Global.AITrainman) && false == TestFlag(vehicle->Hamulec->GetBrakeStatus(), b_dmg) // brake isn't broken + && vehicle->Hamulec->GetCRP() < vehicle->PipePress - 0.1) { + // już nie jest za dużo w zbiorniku + // koniec indywidualnego luzowanka + vehicle->BrakeReleaser(0); } } fReady = std::max( bp, fReady ); // szukanie najbardziej zahamowanego @@ -6148,13 +6071,11 @@ TController::control_pantographs() { // uśrednione napięcie sieci: przy spadku poniżej wartości minimalnej opóźnić rozruch o losowy czas fVoltage = 0.5 * (fVoltage + std::max( mvControlling->GetTrainsetHighVoltage(), mvControlling->PantographVoltage ) ); - if( fVoltage < mvControlling->EnginePowerSource.CollectorParameters.MinV ) { + if( fVoltage < mvControlling->EnginePowerSource.CollectorParameters.MinV && fActionTime >= PrepareTime ) { // gdy rozłączenie WS z powodu niskiego napięcia - if( fActionTime >= PrepareTime ) { - // jeśli czas oczekiwania nie został ustawiony, losowy czas oczekiwania przed ponownym załączeniem jazdy - fActionTime = -2.0 - Random( 10 ); - } - } + // jeśli czas oczekiwania nie został ustawiony, losowy czas oczekiwania przed ponownym załączeniem jazdy + fActionTime = -2.0 - Random(10); + } // raise/lower pantographs as needed auto const useregularpantographlayout { @@ -6203,24 +6124,23 @@ TController::control_pantographs() { } } - if( mvOccupied->Vel > 5 ) { + if( mvOccupied->Vel > 5 && mvPantographUnit->EnginePowerSource.CollectorParameters.CollectorsNo > 1 ) { // opuszczenie przedniego po rozpędzeniu się o ile jest więcej niż jeden - if( mvPantographUnit->EnginePowerSource.CollectorParameters.CollectorsNo > 1 ) { - if( iDirection >= 0 && useregularpantographlayout ) // jak jedzie w kierunku sprzęgu 0 - { // poczekać na podniesienie tylnego - if( mvPantographUnit->PantFrontVolt != 0.0 - && mvPantographUnit->PantRearVolt != 0.0 ) { // czy jest napięcie zasilające na tylnym? - cue_action( driver_hint::frontpantographvalveoff ); // opuszcza od sprzęgu 0 - } - } - else { // poczekać na podniesienie przedniego - if( mvPantographUnit->PantRearVolt != 0.0 - && mvPantographUnit->PantFrontVolt != 0.0 ) { // czy jest napięcie zasilające na przednim? - cue_action( driver_hint::rearpantographvalveoff ); // opuszcza od sprzęgu 1 - } - } - } - } + if (iDirection >= 0 && useregularpantographlayout) // jak jedzie w kierunku sprzęgu 0 + { // poczekać na podniesienie tylnego + if (mvPantographUnit->PantFrontVolt != 0.0 && mvPantographUnit->PantRearVolt != 0.0) + { // czy jest napięcie zasilające na tylnym? + cue_action(driver_hint::frontpantographvalveoff); // opuszcza od sprzęgu 0 + } + } + else + { // poczekać na podniesienie przedniego + if (mvPantographUnit->PantRearVolt != 0.0 && mvPantographUnit->PantFrontVolt != 0.0) + { // czy jest napięcie zasilające na przednim? + cue_action(driver_hint::rearpantographvalveoff); // opuszcza od sprzęgu 1 + } + } + } } else { @@ -6237,26 +6157,29 @@ TController::control_pantographs() { } } else { - if( mvOccupied->AIHintPantUpIfIdle - && IdleTime > 45.0 - // NOTE: abs(stoptime) covers either at least 15 sec remaining for a scheduled stop, or 15+ secs spent at a basic stop - && std::abs(fStopTime) > 15.0 ) { - // spending a longer at a stop, raise also front pantograph - if( mvPantographUnit->EnginePowerSource.CollectorParameters.CollectorsNo > 1 ) { - if( iDirection >= 0 && useregularpantographlayout ) { - // jak jedzie w kierunku sprzęgu 0 - if( mvPantographUnit->PantFrontVolt == 0.0 ) { - cue_action( driver_hint::frontpantographvalveon, 5 ); // discard the hint if speed exceeds 5 km/h - } - } - else { - if( mvPantographUnit->PantRearVolt == 0.0 ) { - cue_action( driver_hint::rearpantographvalveon, 5 ); // discard the hint if speed exceeds 5 km/h - } - } - } - } - } + if( !mvOccupied->AIHintPantUpIfIdle || IdleTime <= 45.0 || std::abs(fStopTime) <= 15.0 || mvPantographUnit->EnginePowerSource.CollectorParameters.CollectorsNo <= 1 ) + { + } + else + { + // spending a longer at a stop, raise also front pantograph + if (iDirection >= 0 && useregularpantographlayout) + { + // jak jedzie w kierunku sprzęgu 0 + if (mvPantographUnit->PantFrontVolt == 0.0) + { + cue_action(driver_hint::frontpantographvalveon, 5); // discard the hint if speed exceeds 5 km/h + } + } + else + { + if (mvPantographUnit->PantRearVolt == 0.0) + { + cue_action(driver_hint::rearpantographvalveon, 5); // discard the hint if speed exceeds 5 km/h + } + } + } + } } void @@ -6465,25 +6388,21 @@ TController::control_doors() { // HACK: crude way to deal with automatic door opening on W4 preventing further ride // for human-controlled vehicles with no door control and dynamic brake auto-activating with door open // TODO: check if this situation still happens and the hack is still needed - if( false == AIControllFlag ) { + if( false == AIControllFlag && (mvControlling->EngineType == TEngineType::DieselEngine ? mvControlling->RList[mvControlling->MainCtrlPos].Mn > 0 : mvControlling->MainCtrlPowerPos() > 0) ) { // for diesel engines react when engine is put past idle revolutions // for others straightforward master controller check - if( mvControlling->EngineType == TEngineType::DieselEngine ? mvControlling->RList[mvControlling->MainCtrlPos].Mn > 0 : mvControlling->MainCtrlPowerPos() > 0 ) { - Doors( false ); - return; - } - } + Doors(false); + return; + } } void TController::UpdateCommand() { - if( false == mvOccupied->CommandIn.Command.empty() ) { - if( false == mvOccupied->RunInternalCommand() ) { - // rozpoznaj komende bo lokomotywa jej nie rozpoznaje - RecognizeCommand(); // samo czyta komendę wstawioną do pojazdu? - } - } + if( false == mvOccupied->CommandIn.Command.empty() && false == mvOccupied->RunInternalCommand() ) { + // rozpoznaj komende bo lokomotywa jej nie rozpoznaje + RecognizeCommand(); // samo czyta komendę wstawioną do pojazdu? + } } void @@ -6751,12 +6670,10 @@ TController::check_departure() { } } } - else if( VelSignal == 0.0 && VelNext > 0.0 && mvOccupied->Vel < 1.0 ) { - if( iCoupler > 0 || (iDrivigFlags & moveStopHere) == 0 ) { - // Ra: tu jest coś nie tak, bo bez tego warunku ruszało w manewrowym !!!! - SetVelocity( VelNext, VelNext, stopSem ); // omijanie SBL - } - } + else if( VelSignal == 0.0 && VelNext > 0.0 && mvOccupied->Vel < 1.0 && (iCoupler > 0 || (iDrivigFlags & moveStopHere) == 0) ) { + // Ra: tu jest coś nie tak, bo bez tego warunku ruszało w manewrowym !!!! + SetVelocity(VelNext, VelNext, stopSem); // omijanie SBL + } } // verify progress of load exchange @@ -6802,12 +6719,11 @@ TController::UpdateChangeDirection() { // TODO: rework into driver mode independent routine if( false == TestFlag( OrderCurrentGet(), Change_direction ) ) { return; } - if( true == AIControllFlag ) { + if( true == AIControllFlag && mvOccupied->Vel < EU07_AI_NOMOVEMENT ) { // sprobuj zmienic kierunek (może być zmieszane z jeszcze jakąś komendą) - if( mvOccupied->Vel < EU07_AI_NOMOVEMENT ) { - // jeśli się zatrzymał, to zmieniamy kierunek jazdy, a nawet kabinę/człon - Activation(); // ustawienie zadanego wcześniej kierunku i ewentualne przemieszczenie AI - } // Change_direction (tylko dla AI) + // jeśli się zatrzymał, to zmieniamy kierunek jazdy, a nawet kabinę/człon + Activation(); // ustawienie zadanego wcześniej kierunku i ewentualne przemieszczenie AI + // Change_direction (tylko dla AI) } // shared part of the routine, implement if direction matches what was requested if( mvOccupied->Vel < EU07_AI_NOMOVEMENT @@ -6818,15 +6734,13 @@ TController::UpdateChangeDirection() { VelSignalNext = -1; PrepareEngine(); JumpToNextOrder(); // następnie robimy, co jest do zrobienia (Shunt albo Obey_train) - if( OrderCurrentGet() & ( Shunt | Loose_shunt | Connect ) ) { + if( OrderCurrentGet() & (Shunt | Loose_shunt | Connect) && false == TestFlag(iDrivigFlags, moveStopHere) ) { // jeśli dalej mamy manewry - if( false == TestFlag( iDrivigFlags, moveStopHere ) ) { - // o ile nie ma stać w miejscu, - // jechać od razu w przeciwną stronę i nie trąbić z tego tytułu: - iDrivigFlags &= ~moveStartHorn; - SetVelocity( fShuntVelocity, fShuntVelocity ); - } - } + // o ile nie ma stać w miejscu, + // jechać od razu w przeciwną stronę i nie trąbić z tego tytułu: + iDrivigFlags &= ~moveStartHorn; + SetVelocity(fShuntVelocity, fShuntVelocity); + } } } @@ -6906,29 +6820,25 @@ TController::UpdateConnect() { auto *vehicleparameters { vehicle->MoverParameters }; if( vehicleparameters->Couplers[ couplingend ].CouplingFlag != iCoupler ) { auto const &neighbour { vehicleparameters->Neighbours[ couplingend ] }; - if( neighbour.vehicle != nullptr ) { - if( neighbour.distance < 10 ) { - // check whether we need to attach coupler adapter - auto coupleriscompatible { true }; - auto const &coupler { vehicleparameters->Couplers[ couplingend ] }; - if( coupler.type() != TCouplerType::Automatic ) { - auto const &othercoupler = neighbour.vehicle->MoverParameters->Couplers[ ( neighbour.vehicle_end != 2 ? neighbour.vehicle_end : coupler.ConnectedNr ) ]; - if( othercoupler.type() == TCouplerType::Automatic ) { - coupleriscompatible = false; - cue_action( driver_hint::couplingadapterattach, couplingend ); - } - } - // próba podczepienia - if( AIControllFlag || Global.AITrainman ) { - if( coupleriscompatible && neighbour.distance < 2 ) { - vehicleparameters->Attach( - couplingend, neighbour.vehicle_end, - neighbour.vehicle->MoverParameters, - iCoupler ); - } - } - } - } + if( neighbour.vehicle != nullptr && neighbour.distance < 10 ) { + // check whether we need to attach coupler adapter + auto coupleriscompatible{true}; + auto const &coupler{vehicleparameters->Couplers[couplingend]}; + if (coupler.type() != TCouplerType::Automatic) + { + auto const &othercoupler = neighbour.vehicle->MoverParameters->Couplers[(neighbour.vehicle_end != 2 ? neighbour.vehicle_end : coupler.ConnectedNr)]; + if (othercoupler.type() == TCouplerType::Automatic) + { + coupleriscompatible = false; + cue_action(driver_hint::couplingadapterattach, couplingend); + } + } + // próba podczepienia + if ((AIControllFlag || Global.AITrainman) && coupleriscompatible && neighbour.distance < 2) + { + vehicleparameters->Attach(couplingend, neighbour.vehicle_end, neighbour.vehicle->MoverParameters, iCoupler); + } + } } // NOTE: no else as the preceeding block can potentially establish expected connection if( vehicleparameters->Couplers[ couplingend ].CouplingFlag == iCoupler ) { @@ -6943,18 +6853,12 @@ TController::UpdateConnect() { } // moveConnect else { // początek podczepiania, z wyłączeniem sprawdzania fTrackBlock - if( Obstacle.distance <= 20.0 ) { - if( !iCouplingVehicle ) { - // write down which vehicle should be coupled with the target consist, - // so we don't lose track of it if the user does something unexpected - iCouplingVehicle = { - pVehicles[ end::front ], - ( pVehicles[ end::front ]->DirectionGet() > 0 ? - end::front : - end::rear ) }; - iDrivigFlags |= moveConnect; - } - } + if( Obstacle.distance <= 20.0 && !iCouplingVehicle ) { + // write down which vehicle should be coupled with the target consist, + // so we don't lose track of it if the user does something unexpected + iCouplingVehicle = {pVehicles[end::front], (pVehicles[end::front]->DirectionGet() > 0 ? end::front : end::rear)}; + iDrivigFlags |= moveConnect; + } } } @@ -7009,13 +6913,11 @@ TController::UpdateDisconnect() { if( iVehicleCount >= 0 ) { // early test for human drivers, who might disregard the proper procedure, or perform it before they receive the order to - if( false == AIControllFlag ) { + if( false == AIControllFlag && unit_count(iVehicleCount + 2) <= iVehicleCount + 1 ) { // iVehicleCount = 0 means the consist should be reduced to (1) leading unit, thus we increase our test values by 1 - if( unit_count( iVehicleCount + 2 ) <= iVehicleCount + 1 ) { - iVehicleCount = -2; // odczepiono, co było do odczepienia - return; // we'll wrap up the procedure on the next update beat - } - } + iVehicleCount = -2; // odczepiono, co było do odczepienia + return; // we'll wrap up the procedure on the next update beat + } // regular uncoupling procedure, performed by ai and naively expected from the human drivers // 3rd stage: change direction, compress buffers and uncouple if( iDirection != iDirectionOrder ) { @@ -7135,11 +7037,10 @@ TController::handle_engine() { OrderNext( Prepare_engine ); } // basic engine preparation - if( OrderCurrentGet() == Prepare_engine ) { - if( PrepareEngine() ) { // gotowy do drogi? - JumpToNextOrder(); - } - } + if( OrderCurrentGet() == Prepare_engine && PrepareEngine() ) { + // gotowy do drogi? + JumpToNextOrder(); + } // engine state can potentially deteriorate in one of usual driving modes if( OrderCurrentGet() & (Change_direction | Connect | Disconnect | Shunt | Loose_shunt | Obey_train | Bank) && false == iEngineActive ) { @@ -7454,16 +7355,12 @@ TController::adjust_desired_speed_for_limits() { return; // speed limit can't get any lower } - if( ( OrderCurrentGet() & ( Shunt | Loose_shunt | Obey_train | Bank ) ) != 0 ) { + if( (OrderCurrentGet() & (Shunt | Loose_shunt | Obey_train | Bank)) != 0 && (iDrivigFlags & moveStopHere) != 0 && mvOccupied->Vel < 0.01 && VelSignalNext == 0.0 ) { // w Connect nie, bo moveStopHere odnosi się do stanu po połączeniu - if( (iDrivigFlags & moveStopHere) != 0 - && mvOccupied->Vel < 0.01 - && VelSignalNext == 0.0 ) { - // jeśli ma czekać na wolną drogę, stoi a wyjazdu nie ma, to ma stać - VelDesired = 0.0; - return; // speed limit can't get any lower - } - } + // jeśli ma czekać na wolną drogę, stoi a wyjazdu nie ma, to ma stać + VelDesired = 0.0; + return; // speed limit can't get any lower + } if( OrderCurrentGet() == Wait_for_orders ) { // wait means sit and wait @@ -7719,18 +7616,11 @@ TController::adjust_desired_speed_for_current_speed() { } // NOTE: as a stop-gap measure the routine is limited to trains only while car calculations seem off - if( is_train() ) { - if( vel < VelDesired ) { - // don't adjust acceleration when going above current target speed - if( -AccDesired * BrakeAccFactor() < ( - false == Ready || VelNext > vel - 40.0 ? - fBrake_a0[ 0 ] * 0.8 : - -fAccThreshold ) - / ( 1.2 * braking_distance_multiplier( VelNext ) ) ) { - AccDesired = std::max( EU07_AI_NOACCELERATION, AccDesired ); - } - } - } + if( is_train() && vel < VelDesired && + -AccDesired * BrakeAccFactor() < (false == Ready || VelNext > vel - 40.0 ? fBrake_a0[0] * 0.8 : -fAccThreshold) / (1.2 * braking_distance_multiplier(VelNext)) ) { + // don't adjust acceleration when going above current target speed + AccDesired = std::max(EU07_AI_NOACCELERATION, AccDesired); + } } // hamowanie kontrolne @@ -7876,15 +7766,11 @@ void TController::control_relays() { void TController::control_motor_connectors() { // ensure motor connectors are active - if( mvControlling->EngineType == TEngineType::ElectricSeriesMotor - || mvControlling->EngineType == TEngineType::DieselElectric - || is_emu() ) { - if( Need_TryAgain ) { - // true, jeśli druga pozycja w elektryku nie załapała - cue_action( driver_hint::mastercontrollersetzerospeed ); - Need_TryAgain = false; - } - } + if( (mvControlling->EngineType == TEngineType::ElectricSeriesMotor || mvControlling->EngineType == TEngineType::DieselElectric || is_emu()) && Need_TryAgain ) { + // true, jeśli druga pozycja w elektryku nie załapała + cue_action(driver_hint::mastercontrollersetzerospeed); + Need_TryAgain = false; + } } void TController::control_tractive_force() { @@ -7892,23 +7778,20 @@ void TController::control_tractive_force() { auto const velocity { DirectionalVel() }; // jeśli przyspieszenie pojazdu jest mniejsze niż żądane oraz... if( AccDesired > EU07_AI_NOACCELERATION // don't add power if not asked for actual speed-up - && (AbsAccS < AccDesired /*- std::min( 0.05, 0.01 * iDriverFailCount )*/ || (mvOccupied->SpeedCtrlUnit.IsActive && velocity < mvOccupied->SpeedCtrlUnit.FullPowerVelocity)) - && false == TestFlag(iDrivigFlags, movePress) ) { + && (AbsAccS < AccDesired /*- std::min( 0.05, 0.01 * iDriverFailCount )*/ || mvOccupied->SpeedCtrlUnit.IsActive && velocity < mvOccupied->SpeedCtrlUnit.FullPowerVelocity) && + false == TestFlag(iDrivigFlags, movePress) && + velocity < (VelDesired == 1.0 ? // work around for trains getting stuck on tracks with speed limit = 1 + VelDesired : + VelDesired - fVelMinus) && + (ActualProximityDist > (is_car() ? fMinProximityDist : // cars are allowed to move within min proximity distance + fMaxProximityDist) ? // other vehicle types keep wider margin + true : + velocity + 1.0 < VelNext) ) { // ...jeśli prędkość w kierunku czoła jest mniejsza od dozwolonej o margines... - if( velocity < ( - VelDesired == 1.0 ? // work around for trains getting stuck on tracks with speed limit = 1 - VelDesired : - VelDesired - fVelMinus ) ) { - // within proximity margin don't exceed next scheduled speed, outside of the margin accelerate as you like - if( ActualProximityDist > (is_car() ? fMinProximityDist : // cars are allowed to move within min proximity distance - fMaxProximityDist) ? // other vehicle types keep wider margin - true : - velocity + 1.0 < VelNext ) { - // ...to można przyspieszyć - increase_tractive_force(); - } - } - } + // within proximity margin don't exceed next scheduled speed, outside of the margin accelerate as you like + // ...to można przyspieszyć + increase_tractive_force(); + } // zmniejszanie predkosci // margines dla prędkości jest doliczany tylko jeśli oczekiwana prędkość jest większa od 5km/h if( false == TestFlag( iDrivigFlags, movePress ) ) { @@ -7962,12 +7845,10 @@ void TController::control_braking_force() { auto const velocity { DirectionalVel() }; // jeśli przyspieszamy, to nie hamujemy - if( AccDesired > 0.0 ) { - if( OrderCurrentGet() != Disconnect // przy odłączaniu nie zwalniamy tu hamulca - && false == TestFlag(iDrivigFlags, movePress) ) { - cue_action( driver_hint::brakingforcesetzero ); - } - } + if( AccDesired > 0.0 && OrderCurrentGet() != Disconnect // przy odłączaniu nie zwalniamy tu hamulca + && false == TestFlag(iDrivigFlags, movePress) ) { + cue_action(driver_hint::brakingforcesetzero); + } if( is_emu() && !ForcePNBrake ) { // właściwie, to warunek powinien być na działający EP @@ -7989,11 +7870,10 @@ void TController::control_braking_force() { if( AbsAccS < AccDesired - 0.05 ) { // jeśli opóźnienie większe od wymaganego (z histerezą) luzowanie, gdy za dużo // TBD: check if the condition isn't redundant with the DecBrake() code - if( BrakeCtrlPosition >= 0 ) { - if( VelDesired > 0.0 ) { // sanity check to prevent unintended brake release on sharp slopes - cue_action( driver_hint::brakingforcedecrease ); - } - } + if( BrakeCtrlPosition >= 0 && VelDesired > 0.0 ) { + // sanity check to prevent unintended brake release on sharp slopes + cue_action(driver_hint::brakingforcedecrease); + } } else { cue_action( driver_hint::brakingforcelap ); @@ -8002,50 +7882,30 @@ void TController::control_braking_force() { } // type & dt_ezt else { // a stara wersja w miarę dobrze działa na składy wagonowe - if( ( AccDesired < fAccGravity - 0.1 && AbsAccS > AccDesired + fBrake_a1[0] ) // regular braking - || ( fAccGravity < -0.05 && velocity < -0.1 ) ) { // also brake if uphill and slipping back + if( (AccDesired < fAccGravity - 0.1 && AbsAccS > AccDesired + fBrake_a1[0] // regular braking + || fAccGravity < -0.05 && velocity < -0.1) && + (fBrakeTime < 0.0 || AccDesired < fAccGravity - 0.5 || BrakeCtrlPosition <= 0) ) { // also brake if uphill and slipping back // u góry ustawia się hamowanie na fAccThreshold - if( fBrakeTime < 0.0 - || AccDesired < fAccGravity - 0.5 - || BrakeCtrlPosition <= 0 ) { - // jeśli upłynął czas reakcji hamulca, chyba że nagłe albo luzował - // TODO: check whether brake delay variable still has any purpose - cue_action( - driver_hint::brakingforceincrease, - // Ra: ten czas należy zmniejszyć, jeśli czas dojazdu do zatrzymania jest mniejszy - ( 3.0 - + 0.5 * ( ( - mvOccupied->BrakeDelayFlag > bdelay_G ? - mvOccupied->BrakeDelay[ 1 ] : - mvOccupied->BrakeDelay[ 3 ] ) - - 3.0 ) ) - * 0.5 ); // Ra: tymczasowo, bo przeżyna S1 - } - } - if ( AccDesired < fAccGravity - 0.05 - && AccDesired - fBrake_a1[0] * 0.51 - AbsAccS > 0.05 ) { + // jeśli upłynął czas reakcji hamulca, chyba że nagłe albo luzował + // TODO: check whether brake delay variable still has any purpose + cue_action(driver_hint::brakingforceincrease, + // Ra: ten czas należy zmniejszyć, jeśli czas dojazdu do zatrzymania jest mniejszy + (3.0 + 0.5 * ((mvOccupied->BrakeDelayFlag > bdelay_G ? mvOccupied->BrakeDelay[1] : mvOccupied->BrakeDelay[3]) - 3.0)) * 0.5); // Ra: tymczasowo, bo przeżyna S1 + } + if ( AccDesired < fAccGravity - 0.05 && AccDesired - fBrake_a1[0] * 0.51 - AbsAccS > 0.05 && OrderCurrentGet() != Disconnect && VelDesired > 0.0 ) { // jak hamuje, to nie tykaj kranu za często // yB: luzuje hamulec dopiero przy różnicy opóźnień rzędu 0.2 - if( OrderCurrentGet() != Disconnect ) { // przy odłączaniu nie zwalniamy tu hamulca - if( VelDesired > 0.0 ) { // sanity check to prevent unintended brake release on sharp slopes - // TODO: check whether brake delay variable still has any purpose - cue_action( - driver_hint::brakingforcedecrease, - (mvOccupied->BrakeDelayFlag > bdelay_G ? mvOccupied->BrakeDelay[0] : mvOccupied->BrakeDelay[2]) / 3.0 - * 0.5 ); // Ra: tymczasowo, bo przeżyna S1 - } - } - } + // przy odłączaniu nie zwalniamy tu hamulca + // sanity check to prevent unintended brake release on sharp slopes + // TODO: check whether brake delay variable still has any purpose + cue_action(driver_hint::brakingforcedecrease, + (mvOccupied->BrakeDelayFlag > bdelay_G ? mvOccupied->BrakeDelay[0] : mvOccupied->BrakeDelay[2]) / 3.0 * 0.5); // Ra: tymczasowo, bo przeżyna S1 + } // stop-gap measure to ensure cars actually brake to stop even when above calculactions go awry // instead of releasing the brakes and creeping into obstacle at 1-2 km/h - if( is_car() ) { - if( VelDesired == 0.0 - && velocity > VelDesired - && ActualProximityDist <= fMinProximityDist - && mvOccupied->LocalBrakePosA < 0.01 ) { - cue_action( driver_hint::brakingforceincrease ); - } - } + if( is_car() && VelDesired == 0.0 && velocity > VelDesired && ActualProximityDist <= fMinProximityDist && mvOccupied->LocalBrakePosA < 0.01 ) { + cue_action(driver_hint::brakingforceincrease); + } } // odhamowywanie składu po zatrzymaniu i zabezpieczanie lokomotywy @@ -8158,27 +8018,17 @@ void TController::control_main_pipe() { || mvOccupied->BrakeHandle == TBrakeHandle::MHZ_K8P || mvOccupied->BrakeHandle == TBrakeHandle::M394 ) { - if( iDrivigFlags & moveOerlikons - || true == IsCargoTrain ) { + if( (iDrivigFlags & moveOerlikons || true == IsCargoTrain) && BrakeCtrlPosition == gbh_RP && AbsAccS < 0.03 && AccDesired > -0.03 && VelDesired - mvOccupied->Vel > 2.0 && fReady > 0.35 && + mvOccupied->EqvtPipePress < 4.5 // don't charge with a risk of overcharging the main pipe + && mvOccupied->Compressor >= 7.5 // don't charge without sufficient pressure in the tank + && BrakeChargingCooldown >= 0.0 // don't charge while cooldown is active + && (ActualProximityDist > 100.0 // don't charge if we're about to be braking soon + || min_speed(mvOccupied->Vel, VelNext) >= mvOccupied->Vel) ) { - if( BrakeCtrlPosition == gbh_RP - && AbsAccS < 0.03 - && AccDesired > -0.03 - && VelDesired - mvOccupied->Vel > 2.0 ) { - - if( fReady > 0.35 - && mvOccupied->EqvtPipePress < 4.5 // don't charge with a risk of overcharging the main pipe - && mvOccupied->Compressor >= 7.5 // don't charge without sufficient pressure in the tank - && BrakeChargingCooldown >= 0.0 // don't charge while cooldown is active - && ( ActualProximityDist > 100.0 // don't charge if we're about to be braking soon - || min_speed(mvOccupied->Vel, VelNext) >= mvOccupied->Vel ) ) { - - BrakeLevelSet( gbh_FS ); - // don't charge the brakes too often, or we risk overcharging - BrakeChargingCooldown = -1 * std::clamp( iVehicles * 3, 30, 90 ); - } - } - } + BrakeLevelSet(gbh_FS); + // don't charge the brakes too often, or we risk overcharging + BrakeChargingCooldown = -1 * std::clamp(iVehicles * 3, 30, 90); + } if( mvOccupied->Compressor < 5.0 || ( BrakeCtrlPosition < gbh_RP @@ -8208,11 +8058,10 @@ TController::check_route_ahead( double const Range ) { break; } case TCommandType::cm_SetVelocity: { // od wersji 357 semafor nie budzi wyłączonej lokomotywy - if( ( OrderCurrentGet() & ~( Shunt | Loose_shunt | Obey_train | Bank ) ) == 0 ) { // jedzie w dowolnym trybie albo Wait_for_orders - if( std::fabs( VelSignal ) >= 1.0 ) { // 0.1 nie wysyła się do samochodow, bo potem nie ruszą - PutCommand( "SetVelocity", VelSignal, VelNext, nullptr ); // komenda robi dodatkowe operacje - } - } + if( (OrderCurrentGet() & ~(Shunt | Loose_shunt | Obey_train | Bank)) == 0 && std::fabs(VelSignal) >= 1.0 ) { // jedzie w dowolnym trybie albo Wait_for_orders + // 0.1 nie wysyła się do samochodow, bo potem nie ruszą + PutCommand("SetVelocity", VelSignal, VelNext, nullptr); // komenda robi dodatkowe operacje + } break; } case TCommandType::cm_ShuntVelocity: { // od wersji 357 Tm nie budzi wyłączonej lokomotywy @@ -8225,17 +8074,15 @@ TController::check_route_ahead( double const Range ) { break; } case TCommandType::cm_Command: { // komenda z komórki - if( ( OrderCurrentGet() & ~( Shunt | Loose_shunt | Obey_train | Bank ) ) == 0 ) { + if( (OrderCurrentGet() & ~(Shunt | Loose_shunt | Obey_train | Bank)) == 0 && mvOccupied->Vel < 0.1 ) { // jedzie w dowolnym trybie albo Wait_for_orders - if( mvOccupied->Vel < 0.1 ) { - // dopiero jak stanie -/* - PutCommand( eSignNext->text(), eSignNext->value( 1 ), eSignNext->value( 2 ), nullptr ); - eSignNext->StopCommandSent(); // się wykonało już -*/ // replacement of the above - eSignNext->send_command( *this ); - } - } + // dopiero jak stanie + /* + PutCommand( eSignNext->text(), eSignNext->value( 1 ), eSignNext->value( 2 ), nullptr ); + eSignNext->StopCommandSent(); // się wykonało już + */ // replacement of the above + eSignNext->send_command(*this); + } break; } default: {