maj00r
970c6b52c5
feat(editor): clips of the tutorial lessons (looking around, laying track, curves, joining, switches, fixing), recorded in the editor with a badge of the mouse button or key in use
2026-10-17 23:30:37 +02:00
maj00r
a890087b8b
fix(editor): the tooltip of the Terrain work area is translated
2026-10-17 23:30:37 +02:00
maj00r
dfeb1b9e23
fix(editor): the hints of laying and dragging track (start, starts at the end of, joins, no track fits, moved) are translated
2026-10-17 23:30:37 +02:00
maj00r
2a3d7f2e29
feat(editor): scripted mouse and keys of a self-test are delivered between frames like real input events, so a press seen by ImGui starts a drag as it does by hand; while a script drives the input the real mouse and keyboard are ignored
2026-10-17 23:30:37 +02:00
maj00r
92aa39ce6e
fix(editor): dragging a point of a selected track no longer hands the drag to the gizmo, which jumped under the cursor and lifted the point in the plane of the screen; the gizmo is switched off while a point is dragged
2026-10-17 23:30:37 +02:00
maj00r
d0ebb01c2f
fix(editor): a blank standard orthophoto tile that the geoportal sent while it was busy is removed from the disk cache and fetched once more, instead of staying a black hole in the map for good
2026-10-17 23:30:37 +02:00
maj00r
fc3f391e19
feat(editor): Help > Tutorial instead of First steps: seven lessons (looking around, laying track, curves, joining, switches, selecting and fixing, saving and driving), each with a looping clip from lang/editor_tutorial/<lesson>.gif played from the start on a click, the mouse buttons and keys drawn as pictures beside what they do, ticks that fill in when the user does it, a button that turns the right tool on, Back / Next and a dot per lesson that turns green when it is done
2026-10-17 23:30:37 +02:00
maj00r
6d0423b58a
feat(editor): friendlier track editing: the point labels of a path are translated, a selected switch says that the turnout tool sets its geometry and offers Edit the switch (T), the geometry of its paths starts collapsed, the gizmo options show only on the tabs that use the gizmo, cancelling a laying clears its status and the status of a laying counts pieces and metres
2026-10-17 23:30:37 +02:00
maj00r
1dc61eaf31
feat(editor): scripted mouse and keys for the self-tests and the launcher: mouse, mouseat, glide, button, wheel, press, release, tap, type and softcursor drive the window as the user does, EU07_LAUNCHER_SELFTEST runs a script in the launcher and the scenery wizard and hands an editor script on with editorscript; selectat selects the path nearest a point, switchlink no longer takes over a crossover left in the tool, joints without points logs every issue with free ends
2026-10-17 23:30:37 +02:00
maj00r
58a0b0c864
Merge branch 'feat/track-join-ux' into feat/switch-blades
2026-10-17 23:30:37 +02:00
maj00r
20c4b6542e
feat(track): the blades of a switch whose frog is cut from the rails are made from its geometry: the tip is where the gauge lines of both paths are 3 mm apart, the head reaches its full width where they are a head plus 0.5 mm apart, and the heel is the shortest blade whose open flangeway stays at least 58 mm with a throw of 160 mm bent like a cantilever from the heel, or straight when that does not fit as in slips; the blade is the whole rail profile lofted from the tip to the heel, cut along the head of the stock rail, with the head lowered by the drop of the blade half of the profile file towards the tip and back to the top at a 50 mm head, and thrown with knots every 0.5 m so the vertex count does not change between the positions; the stock rails get stations every 0.5 m along the blades so their curve follows the blade, the fixed rail behind the heel starts at the heel, the old blades take their segment count per path, and the self-test command blade logs the tip, heel, drop, flangeway and the gauge lines against x^2/2R and faults on a short flangeway, a heel past the throat or a change of the vertex count
2026-10-17 23:30:36 +02:00
maj00r
df2bbae449
Merge branch 'feat/rail-crossings' into feat/switch-blades
...
The frogs, crossings and check rails of feat/rail-crossings next to the track editor and the switch paths made of pieces of feat/track-join-ux. The self-test commands of the crossings (frog, geomhash, frogkey, guards, railtime, crossings and switches 0|1, now called throw 0|1 because the editor already has switches) go into their own group selftest_rails.
2026-10-17 23:30:36 +02:00
maj00r
5544fbc43b
revert(track): no filler block at obtuse crossings, the flangeway between the point rail and the knuckle rail stays open as before
2026-10-17 23:28:41 +02:00
maj00r
c47147d277
fix(track): the filler block of an obtuse crossing ends where the head of the point rail reaches its full width, so it only closes the flangeway and no longer shows as a plate in the widening vee between the point and the knuckle rail
2026-10-17 23:28:41 +02:00
maj00r
7786076bda
feat(track): at a crossing at a wide angle each guard rail is glued to its running rail by a filler block between their webs along its whole length, so the flangeways and the gaps cut for the wheels of the other track have a floor instead of showing the ballast; the filler of the track not going through lies a millimetre lower where both overlap
2026-10-17 23:28:41 +02:00
maj00r
618a3004dd
feat(track): at an obtuse crossing the flangeway between the point rail and the knuckle rail is closed below by a filler block at the height of the webs, from the meeting point to where the point reaches its full profile, so the ballast no longer shows through the gap; the rail profile gives the middle of its web and the height where its head starts above it
2026-10-17 23:28:41 +02:00
maj00r
a30918a95e
fix(track): at an obtuse crossing the rail ending at the meeting point and the rail going on from it are joined by a mitre on the bisector of the bend, like one bent knuckle rail, instead of two square ends overlapping in a wedge, and the check rails of the two tracks meet there the same way instead of overlapping each other by 3 cm; a loft span can turn its first and last cross-section about the gauge line for such joints
2026-10-17 23:28:41 +02:00
maj00r
156c118308
fix(track): the legs of an acute nose are cut along the line halfway between the gauge lines of the two rails, followed every 10 cm from the tip to where the full profiles part, instead of a straight bisector, so the nose of a switch frog with a curved diverging path has no seam opening along its middle
2026-10-17 23:28:41 +02:00
maj00r
649dde731c
fix(track): a loft cut along its length keeps its surface where the cut crosses a point of the profile: each span between stations sorts the points as clamped or open by its middle, so the ring at a station where a point lies right on the edge does not drop the next point to its own height and leave a dark groove along the top of the rail; clamped points that are not on the edge are kept between the heights of the edge on both sides of the run, so they never stand above the surface
2026-10-17 23:28:41 +02:00
maj00r
275f732c66
fix(track): the pieces of the top of a rail head are laid only where they have width on the head between its cut edges, split where the edges of the wheel treads cross those cut edges, and the shared spans reach 2 cm further for the fade, so no inverted piece is drawn inside the neighbouring rail and no fin stands out of the head; EU07_EDITOR_TREADS paints the pieces in plain colours: blue shared, green and red the fades, yellow the edge of the rolled band, magenta the matte part out of reach of the wheels
2026-10-17 23:28:41 +02:00
maj00r
a107b04fcf
fix(track): the treads of the wheels reach from the gauge line as far as a wheel 140 mm wide with the back faces 1360 mm apart does, 820 mm from the axis of a standard gauge track, instead of a flat 100 mm
2026-10-17 23:28:41 +02:00
maj00r
674efade70
feat(track): frogs, crossings and shared running surfaces only for the rail profiles the sceneries of the package use: tracks without a profile, default, s60, uic60 and s39; rails of other profiles are laid whole again
2026-10-17 23:28:41 +02:00
maj00r
7100cec12f
feat(track): the texture of a shared running surface blends into the texture of the rail it lies on: 2 cm on each side of the boundary both are drawn in tinted variants of their materials that fade per vertex into the mean colour of the two treads, sampled from the texture files, so the edge of the wheels of the other track leaves a soft band instead of a line
2026-10-17 23:28:41 +02:00
maj00r
93d512c0f2
fix(track): obtuse crossings share the running surface too: the top of a rail crossed by the treads of the wheels of the other track takes the brighter texture between the gauge line of the other rail and the edge of its treads, so the boundary runs aslant on both sides
2026-10-17 23:28:41 +02:00
maj00r
2c8e8d00c4
fix(track): the shared running surface ends along the edge of the treads of the wheels of the other track, not across the rail: on a nose leg the edge runs aslant over the head, on a stock rail it follows the curve of the other path; only the top of the head takes the other texture, the rest of the rail keeps its own
2026-10-17 23:28:41 +02:00
maj00r
45ef9c470f
fix(track): the two legs of a V nose are cut along the bisector of their gauge lines and meet in the middle as one nose, from a tip 12 mm wide to where their full profiles part, instead of the splice rail giving way under the head of the point rail
2026-10-17 23:28:41 +02:00
maj00r
59446f9a98
fix(track): flat rail frogs have no plates in the V; the shared running surface of a nose leg goes from its tip to where the wheels of the other track stop reaching its head, on the leg only and not on the wing beside it; the stock rail of a switch is shared up to a quarter of the blade length, where the blade head is half its width and takes the wheel
2026-10-17 23:28:41 +02:00
maj00r
9448bd8c70
feat(track): where two rails share a running surface it takes the texture whose running surface is brighter; the brightness is read from the texture file over the u range of the rail head top, the shared parts are the block of crossing grooved rails, the nose of an acute frog from its tip to the end of the V, the through rail across a wide or grooved crossing, the frog of a switch, and the stock rails of a switch with frogs up to half the blade length where the blade grows to a full head
2026-10-17 23:28:41 +02:00
maj00r
2f8aeb24c0
fix(track): a crossing of grooved rails is one flat block where their lofts overlap; the rail cut by the other covers the roundings of the rail going through, the tops in the block are flat in one texture, the plates of the V reach up to the rail top, the walls of the cuts are lit and textured as walls, and the nose between two flangeways starts blunt at the width of a nose tip instead of a thin sliver standing in the groove
2026-10-17 23:28:41 +02:00
maj00r
9343ebcb13
feat(track): frogs and crossings for every rail profile; grooved rails share the floor of their grooves where they cross and need no check rails, the V beside the rail going through is one block where the lofts of the rails overlap; the nose of an acute frog of flat rails is filled between its legs as far as their lofts overlap, like a cast insert
2026-10-17 23:28:41 +02:00
maj00r
59f1a5a144
fix(track): the lowest of two height lists also takes the points where they cross, so the top of an obtuse point rail is not cut narrower than its sides; the end of a short neighbour track joined at a crossing is the nearer one, not any within half a metre
2026-10-17 23:28:41 +02:00
maj00r
215f16e871
fix(editor): self-test frog takes the throat, nose, wing end and check rails from the sizes the rails are laid with, so the wing end is logged where it is laid; self-test crossings tells crossings at middle angles apart and counts them
2026-10-17 23:28:41 +02:00
maj00r
1a3a38e96d
refactor(track): one plan projection, cross product and meeting of two lines in plan for the crossings of rails, the frog of a switch and the check rails, in place of copies in each
2026-10-17 23:28:41 +02:00
maj00r
405081e81c
refactor(track): the rails of the two paths of a switch are laid by one loop for left and right switches alike, the side of the blade of a path and the blade itself come from one place, used when a switch is made, thrown and its frog found
2026-10-17 23:28:41 +02:00
maj00r
809b48c2ab
refactor(track): the blades of a thrown switch are drawn again with their cut faces by one helper instead of four copies; self-test command switches sets every switch to a path and moves its blades there at once
2026-10-17 23:28:41 +02:00
maj00r
e324eb10f9
perf(track): pieces of gauge lines whose bounding boxes do not meet are skipped before they are intersected; self-test railtime logs how much of the time goes to finding the crossings
2026-10-17 23:28:41 +02:00
maj00r
36fb68d8f9
perf(track): the gauge lines, crossings and check rails of a track are kept until a track is made, rebuilt or removed, so laying the rails of a track no longer recomputes its neighbours' crossings for each wing rail; self-test command railtime times planning the rails of all tracks
2026-10-17 23:28:41 +02:00
maj00r
929294af24
feat(editor): self-test command guards logs each guard rail and what each of its ends and breaks is cut along: the rail of the other track, how far off its gauge line and how skewed; a fault when a cut is not along a rail, or a glued end of the frame of a crossing at a middle angle is not on the face of a guard rail of the other track
2026-10-17 23:28:41 +02:00
maj00r
7088618d90
feat(editor): every switch laid in the editor gets the key frogs, a box in the Switch panel sets or clears it as one undo step; the editor rebuilds the tracks crossing a changed or removed one; self-test commands frog, crossings, frogkey and geomhash; Polish translation
2026-10-17 23:28:41 +02:00
maj00r
2159d682f2
feat(track): rails are cut where they cross other rails: a switch with the key frogs draws its frog, wing rails and check rails from its rails, tracks crossing other tracks get acute frogs with wing rails and check rails, obtuse points, guard rails along the quadrilateral of a crossing between 20 and 45 degrees and broken rails with guard rails at wider angles, the track going through chosen by name; sizes come from the rail profile, only for the profiles default, s60, uic60 and s39, others are laid whole; rail ends left open are closed with their cross-section; switches of older sceneries, with check rails in their includes and no key, are drawn as before
2026-10-17 23:28:41 +02:00
maj00r
7e1b2c553c
feat(renderer): a matte variant of a material for the parts of rails no wheel rolls on: it lights the same maps as weathered steel, with the weathering scaled per vertex by userdata, so a polished running surface fades into unused steel without a bright stripe; the cut faces of rails are drawn in it in an extra pass
2026-10-17 23:28:41 +02:00
jerrrrycho
c7f9796387
fix(ai): release horn when human driver takes over from AI ( #210 )
...
* fix(ai): release what the AI was holding when the player takes over
* fix(ai): keep the light selector lookup within its table, Sonar findings of the handover
- the light selector size comes from the vehicle definition (LightsList Size)
and isn't limited, while the table holds 18 positions; a bigger selector
read past the table, and so did a table index other than the front or
rear one. Only the positions the table holds are looked at now, and the
selector stays where it is for a bad table index (cpp:S2259),
- the lambdas of the handover capture what they use explicitly (cpp:S3608),
- the chained assignments resetting the headlight state on the AI taking
over are split (cpp:S1121).
2026-10-16 14:44:30 +02:00
jerrrrycho
b400255dae
Test/physics unit tests ( #213 )
...
* build: doctest 2.4.11 in ref/
Single header test framework (MIT), vendored like the other libraries in
ref/ so the tests need nothing extra installed on the CI runners.
* refactor(physics): read vehicle definitions from text as well as files
TMoverParameters::LoadFIZ(path) opens the file and hands the parser over
to LoadFIZ_Parse(), which holds what LoadFIZ() did with it before. The
new LoadFIZ_Text() reads the same format from a string, so a definition
can be made up in memory, e.g. by a unit test. No change in behaviour for
files.
* build: the simulation core as a library the game links
The vehicle physics (McZapkie), the vehicle definition parser and the
helpers they use build as eu07_core, a static library the game links,
so the unit tests to come can link the very same objects instead of
compiling their own copy with their own settings.
The game's build settings (include dirs, definitions, compile options,
libraries) move from the executable to eu07_settings, an interface
target the library links publicly, so the game, the library and later
the tests all build with the same ones. What only the executable needs
stays with it: /MAP, /LARGEADDRESSAWARE, output dirs, the precompiled
header, runtime DLL copying, the crash reporter and the experimental
renderer. No change in what the game builds or links.
* test: unit tests of the simulation core, run as part of the build
eu07_unit_tests: C++ tests (doctest) of the simulation, which set up the
conditions, run the code and check the outcome. No game data, no window.
- the code under test is the core library (eu07_core), the very objects
the game links, built with the game's settings; engine_stubs.cpp stands
in for the logs, settings and scenery layers the library reaches for.
python gets linked with the settings but isn't needed to run the tests
- test vehicles are JSON definitions in tests/unit/data, with the sections
and keys of the game's vehicle definitions (tables as "rows", _keys are
comments), changed by the tests where needed and loaded from memory by
the game's own loader; an unknown section is an error rather than
silently skipped
- test::vehicle creates the mover the way TDynamicObject::Init() does and
steps it the way the simulation loop does, on a straight track of given
friction and gradient, optionally holding the brake pipe pressure the
way a coupled locomotive would
- the random numbers of the simulation are reseeded before every test
case, so a test gives the same result alone (IDE) or among the others
- building the game runs the tests at the end, once the game is linked,
and prints a one line summary ("Unit tests: 11/11 passed (80 checks)").
A failure prints the full report and fails the build, in CI and locally
alike, and removes the game binary so the next build reruns them. A
change of the tests alone relinks the game so they run (Ninja and
Makefile generators). -DWITH_TESTS=OFF leaves them out
- first tests: definitions reach the vehicle, KE service application and
release, deeper reduction brakes harder, every implemented distributor
valve charges/applies/releases, service braking stops a moving wagon
* test: stub basic_node::radius_() for GCC and Clang
The Linux build failed to link the unit tests: undefined reference to
scene::basic_node::radius_() from basic_node's vtable in engine_stubs.o.
basic_node has no key function for the Itanium C++ ABI (tooltip() is
declared plain but defined inline in the header), so GCC and Clang emit
its vtable in every unit using scenenode.h, and the vtable needs
radius_(), defined in scenenode.cpp, which the tests don't link. MSVC
emits vtables differently, so the Windows build linked.
* test: test vehicle definitions load with GCC and Clang
All the tests reading a definition failed on Linux with "unknown vehicle
definition section 0": the parsed definition was brace-initialised
(auto const json{parse(...)}), which GCC and Clang take for the
initializer list constructor of nlohmann::json, making an array holding
the definition, while MSVC copies it. Initialise it with =, and require
the definition to be a JSON object.
* test: SonarCloud issues of the unit tests
- test::definition_error for unusable test vehicle definitions instead of
std::runtime_error (S112)
- the rows of a table section written by a function of their own, less
nesting in definition::text() (S134)
- std::less<> for the section table (S6045)
- comments in the empty methods of the reseeding listener and stub (S1186)
- NOSONAR where the engine's headers fix the stubs: the extern globals
and the member function signatures (S5421, S5817, S1238)
- NOSONAR on LoadFIZ_Parse(): the cognitive complexity is the former body
of LoadFIZ(), moved as it was (S3776)
- a comment Sonar took for code reworded (S125)
2026-10-15 17:56:41 +02:00
maj00r
fb73a3e011
test(editor): self-test steps that keep a laying or connection ghost on screen under a set cursor, open the switch template editor, release a held placement and leave the top view, for screenshots of the tools at work
2026-10-14 23:55:04 +02:00
maj00r
aa20a88ed0
feat(editor): connections of tracks laid whole from the switch tool: a crossover with a straight insert or an aligning curve between switches of different angles or tracks that aren't parallel, a double crossover meeting on either track, a scissors crossover with the crossing of its inserts, and a switch ladder onto the parallel tracks of a fan or onto new ones at given spacings; press on a straight and drag onto the other track, Shift+wheel the kind, Ctrl+wheel the R of the aligning curve, Alt+wheel where a double crossover meets; the ghost shows the whole set with the spacing, the inserts and the distance of the mathematical points; switches get their drives and a //$r connection record; the crossover drawn from a diverging end uses the same solver; self-test connection
2026-10-14 23:38:31 +02:00
maj00r
52a289b15b
feat(editor): switch templates by their geometry and a template editor: the diverging track is a chain of straights, arcs and transition curves from the start of the switch, with the pre-blade stretch and the angle of the blade at its tip; own templates are kept in eu07_switch_templates.txt and listed in the switch tool; double switches Rpd and Rpj are laid as two switches joined by the main track and handled as one set by sliding, replacing, flipping, the gizmo and deleting, with drives for both switches; self-test switchtemplates
2026-10-14 23:17:30 +02:00
maj00r
ea341af27a
feat(editor): a switch path can be a chain of pieces: the attributes pieces1 and pieces2 keep the parts of each path after the classic bezier, which stays as the approximation older versions read and log the attributes as unknown; the simulation drives, draws and maps a path along its pieces by distance, pieces that don't join the ends of the path are reported and the bezier used; the catalogue Rz switches get the diverging path as the arc of their radius and the straight after it, moving, rotating and mirroring a switch carry the pieces, reshaping a path by hand drops them; self-test commands switchpieces and switchbreak
build-feat-track-join
2026-10-12 18:32:23 +02:00
maj00r
46d41549c9
feat(editor): the profile takes the switch zones from the //$r records: the parts of a double slip and the paths of a crossing make one zone from its first part to its last, a crossing made of plain paths is a zone too, the zone is named by the number and the type of the switch; a switch with no record stays a zone named by its path; self-test vprofile logs the zones
2026-10-12 18:11:17 +02:00
maj00r
9b75e64c9b
feat(editor): digitizing takes the good parts of a line: a run whose design can't be read whole is split at the track the reading fails at and each part is digitized on its own, only single tracks that make no line are left out and named in the log; Digitize all reports the lines digitized and the paths left out, opening a line digitized in parts says so with the reason for the paths left out; a stored radius agrees with the one its design makes within 0.1 %
2026-10-12 18:00:26 +02:00
maj00r
95c3842748
feat(editor): digitizing takes a line whose tracks lie up to 0.5 m off the curves read from them: the record keeps how far as fit in its source line and counts the tracks within it as lying on it, so they aren't shown as departed; a record whose elements differ from the ones its design makes only by elements of no length is no longer reported as differing
2026-10-12 17:42:40 +02:00