From be2f37a0678c66278d1c491b8f1250a8a3be9feb Mon Sep 17 00:00:00 2001 From: ywang Date: Wed, 3 Nov 2021 18:55:31 +0100 Subject: Support different types of speed restrictions; add documentation --- .dir-locals.el | 5 ++ advtrains/doc/advtrains_speed_lessp.3advtrains.md | 15 ++++ .../doc/advtrains_speed_merge_aspect.3advtrains.md | 18 +++++ advtrains/init.lua | 4 +- advtrains/spec/speed_spec.lua | 70 +++++++++++++++++ advtrains/speed.lua | 88 ++++++++++++++++++++++ advtrains/trainlogic.lua | 5 +- advtrains_interlocking/approach.lua | 9 ++- .../doc/advtrains_signals_ks.7advtrains.md | 60 +++++++++++++++ advtrains_signals_ks/init.lua | 42 +++++++++++ 10 files changed, 309 insertions(+), 7 deletions(-) create mode 100644 .dir-locals.el create mode 100644 advtrains/doc/advtrains_speed_lessp.3advtrains.md create mode 100644 advtrains/doc/advtrains_speed_merge_aspect.3advtrains.md create mode 100644 advtrains/spec/speed_spec.lua create mode 100644 advtrains/speed.lua create mode 100644 advtrains_signals_ks/doc/advtrains_signals_ks.7advtrains.md diff --git a/.dir-locals.el b/.dir-locals.el new file mode 100644 index 0000000..57aae07 --- /dev/null +++ b/.dir-locals.el @@ -0,0 +1,5 @@ +((nil (tab-width . 8)) + (lua-mode (indent-tabs-mode . t) + (lua-indent-level . 8) + (lua-indent-close-paren-align . nil) + (lua-indent-nested-block-content-align . nil))) diff --git a/advtrains/doc/advtrains_speed_lessp.3advtrains.md b/advtrains/doc/advtrains_speed_lessp.3advtrains.md new file mode 100644 index 0000000..165d028 --- /dev/null +++ b/advtrains/doc/advtrains_speed_lessp.3advtrains.md @@ -0,0 +1,15 @@ +% advtrains.speed.lessp(3advtrains) | Advtrains Developer's Manual + +# NAME +`advtrains.speed.lessp`, `advtrains.speed.greaterp`, `advtrains.speed.not_lessp`, `advtrains.speed_not_greaterp`, `advtrains.speed.equalp`, `advtrains.speed.not_equalp`, `advtrains.speed.max`, `advtrains.speed.min` - speed restriction comparison functions + +# SYNOPSIS +Each function takes two arguments and returns a boolean or (for `advtrains.speed.max` and `advtrains.speed.min`) a valid speed limit + +# DESCRIPTION + +The functions above correspond to the arithmetic `<`, `>`, `>=`, `<=`, `==`, `~=` operators and the `math.max` and `math.min` functions, respectively. The constants `nil` and `false` are treated as -1. + +# NOTES + +These functions are trivial to implement and the implementation can be easily embedded into existing code. They are simply provided for convenience. diff --git a/advtrains/doc/advtrains_speed_merge_aspect.3advtrains.md b/advtrains/doc/advtrains_speed_merge_aspect.3advtrains.md new file mode 100644 index 0000000..02e83ea --- /dev/null +++ b/advtrains/doc/advtrains_speed_merge_aspect.3advtrains.md @@ -0,0 +1,18 @@ +% advtrains.speed.set_restriction(3advtrains) | Advtrains Developer's Manual + +# NAME +`advtrains.speed.set_restriction`, `advtrains.speed.merge_aspect` - modify speed restriction + +# SYNOPSIS +* `advtrains.speed.set_restriction(train, rtype, rval)` +* `advtrains.speed.merge_aspect(train, asp)` + +# DESCRIPTION + +The `advtrains.speed.set_restriction` function sets the speed restriction of type `rtype` of `train` to `rval` and updates the speed restriction value to the strictest speed restriction in the table, or `nil` if all speed restrictions are `nil` or `-1`. If the speed restriction table does not exist, it is created with the `"main"` speed restriction being the speed restriction value of `train`. + +The `advtrains.speed.merge_aspect` function merges the main aspect of `asp` into the speed restriction table with the same procedure described above. If the signal aspect table does not provide the type of speed restriction, the restriction type `"main"` is assumed. + +# SIDE EFFECTS + +Both functions modify `train.speed_restriction` and `train.speed_restrictions_t`. diff --git a/advtrains/init.lua b/advtrains/init.lua index 0882237..a7e5764 100644 --- a/advtrains/init.lua +++ b/advtrains/init.lua @@ -198,6 +198,8 @@ advtrains.meseconrules = advtrains.fpath=minetest.get_worldpath().."/advtrains" +advtrains.speed = dofile(advtrains.modpath.."/speed.lua") + dofile(advtrains.modpath.."/path.lua") dofile(advtrains.modpath.."/trainlogic.lua") dofile(advtrains.modpath.."/trainhud.lua") @@ -467,7 +469,7 @@ advtrains.avt_save = function(remove_players_from_wagons) "trainparts", "recently_collided_with_env", "atc_brake_target", "atc_wait_finish", "atc_command", "atc_delay", "door_open", "text_outside", "text_inside", "line", "routingcode", - "il_sections", "speed_restriction", "is_shunt", + "il_sections", "speed_restriction", "speed_restrictions_t", "is_shunt", "points_split", "autocouple", "atc_wait_autocouple", "ars_disable", }) --then save it diff --git a/advtrains/spec/speed_spec.lua b/advtrains/spec/speed_spec.lua new file mode 100644 index 0000000..0f0365f --- /dev/null +++ b/advtrains/spec/speed_spec.lua @@ -0,0 +1,70 @@ +package.path = "../?.lua;" .. package.path +advtrains = {} +_G.advtrains = advtrains +local speed = require("speed") + +describe("Arithmetic functions on speed restrictions", function() + it("should work", function() + local a = math.random() + local b = math.random(20) + -- This test is basically a "typo check" + assert.is_true (speed.lessp(a, b)) + assert.is_false(speed.greaterp(a, b)) + assert.is_false(speed.not_lessp(a, b)) + assert.is_true (speed.not_greaterp(a, b)) + assert.is_false(speed.lessp(a, a)) + assert.is_false(speed.greaterp(a, a)) + assert.is_true (speed.equalp(a, a)) + assert.is_false(speed.not_equalp(a, a)) + assert.equal(b, speed.max(a, b)) + assert.equal(a, speed.min(a, b)) + end) + it("should handle -1", function() + assert.is_false(speed.lessp(-1, math.random())) + end) + it("should handle nil", function() + assert.is_false(speed.greaterp(nil, math.random())) + end) + it("should handle mixed nil and -1", function() + assert.is_true(speed.equalp(nil, -1)) + end) +end) + +describe("The speed restriction setter", function() + it("should set the signal aspect", function() + local t = {speed_restrictions_t = {x = 5, y = 9}} + local u = {speed_restrictions_t = {x = 7, y = 9}, speed_restriction = 7} + speed.merge_aspect(t, {main = 7, type = "x"}) + assert.same(u, t) + end) + it("should work with existing signal aspect tables", function() + local t = {speed_restrictions_t = {main = 5, foo = 3}} + local u = {speed_restrictions_t = {main = 7, foo = 3}, speed_restriction = 3} + speed.merge_aspect(t, {main = 7}) + assert.same(u, t) + end) + it("should work with distant signals", function() + local t = {speed_restrictions_t = {main = 5}} + local u = {speed_restrictions_t = {main = 5}, speed_restriction = 5} + speed.merge_aspect(t, {}) + assert.same(u, t) + end) + it("should create the restriction table if necessary", function() + local t = {speed_restriction = 5} + local u = {speed_restriction = 3, speed_restrictions_t = {main = 5, foo = 3}} + speed.merge_aspect(t, {main = 3, type = "foo"}) + assert.same(u, t) + end) + it("should also create the restriction table for trains without any speed limit", function() + local t = {} + local u = {speed_restrictions_t = {}} + speed.merge_aspect(t, {}) + assert.same(u, t) + end) + it("should set the speed restriction to nil if that is the case", function() + local t = {speed_restriction = math.random(20)} + local u = {speed_restrictions_t = {main = -1}} + speed.merge_aspect(t, {main = -1}) + assert.same(u, t) + end) +end) diff --git a/advtrains/speed.lua b/advtrains/speed.lua new file mode 100644 index 0000000..ec4f928 --- /dev/null +++ b/advtrains/speed.lua @@ -0,0 +1,88 @@ +-- auxiliary functions for the reworked speed restriction system + +local function s_lessp(a, b) + if not a or a == -1 then + return false + elseif not b or b == -1 then + return true + else + return a < b + end +end + +local function s_greaterp(a, b) + return s_lessp(b, a) +end + +local function s_not_lessp(a, b) + return not s_lessp(a, b) +end + +local function s_not_greaterp(a, b) + return not s_greaterp(a, b) +end + +local function s_equalp(a, b) + return (a or -1) == (b or -1) +end + +local function s_not_equalp(a, b) + return (a or -1) ~= (b or -1) +end + +local function s_max(a, b) + if s_lessp(a, b) then + return b + else + return a + end +end + +local function s_min(a, b) + if s_lessp(a, b) then + return a + else + return b + end +end + +local function get_speed_restriction_from_table (tbl) + local strictest = -1 + for _, v in pairs(tbl) do + strictest = s_min(strictest, v) + end + if strictest == -1 then + return nil + end + return strictest +end + +local function set_speed_restriction (tbl, rtype, rval) + if rval then + tbl[rtype or "main"] = rval + end + return tbl +end + +local function set_speed_restriction_for_train (train, rtype, rval) + local t = train.speed_restrictions_t or {main = train.speed_restriction} + train.speed_restrictions_t = set_speed_restriction(t, rtype, rval) + train.speed_restriction = get_speed_restriction_from_table(t) +end + +local function merge_speed_restriction_from_aspect_to_train (train, asp) + return set_speed_restriction_for_train(train, asp.type, asp.main) +end + +return { + lessp = s_lessp, + greaterp = s_greaterp, + not_lessp = s_not_lessp, + not_greaterp = s_not_greaterp, + equalp = s_equalp, + not_equalp = s_not_equalp, + max = s_max, + min = s_min, + set_restriction = set_speed_restriction_for_train, + merge_aspect = merge_speed_restriction_from_aspect_to_train, +} diff --git a/advtrains/trainlogic.lua b/advtrains/trainlogic.lua index e7f2fd4..10af567 100644 --- a/advtrains/trainlogic.lua +++ b/advtrains/trainlogic.lua @@ -1168,6 +1168,7 @@ function advtrains.split_train_at_index(train, index) newtrain.line = train.line newtrain.routingcode = train.routingcode newtrain.speed_restriction = train.speed_restriction + newtrain.speed_restrictions_t = table.copy(train.speed_restrictions_t) newtrain.is_shunt = train.is_shunt newtrain.points_split = advtrains.merge_tables(train.points_split) newtrain.autocouple = train.autocouple @@ -1210,10 +1211,10 @@ function advtrains.invert_train(train_id) -- If interlocking present, check whether this train is in a section and then set as shunt move after reversion if advtrains.interlocking and train.il_sections and #train.il_sections > 0 then train.is_shunt = true - train.speed_restriction = advtrains.SHUNT_SPEED_MAX + advtrains.speed.set_restriction(train, "main", advtrains.SHUNT_SPEED_MAX) else train.is_shunt = false - train.speed_restriction = nil + advtrains.speed.set_restriction(train, "main", -1) end end diff --git a/advtrains_interlocking/approach.lua b/advtrains_interlocking/approach.lua index f60468a..eecf09a 100644 --- a/advtrains_interlocking/approach.lua +++ b/advtrains_interlocking/approach.lua @@ -14,19 +14,19 @@ local SHUNT_SPEED_MAX = advtrains.SHUNT_SPEED_MAX local il = advtrains.interlocking -local function get_over_function(speed, shunt) +local function get_over_function(speed, shunt, asptype) return function(pos, id, train, index, speed, lzbdata) if speed == 0 and minetest.settings:get_bool("at_il_force_lzb_halt") then atwarn(id,"overrun LZB 0 restriction (red signal) ",pos) -- Set train 1 index backward. Hope this does not lead to bugs... --train.index = index - 0.5 - train.speed_restriction = 0 + advtrains.speed.set_restriction(train, "main", 0) --TODO temporary --advtrains.drb_dump(id) --error("Debug: "..id.." triggered LZB-0") else - train.speed_restriction = speed + advtrains.speed.set_restriction(train, asptype, speed or -1) train.is_shunt = shunt end --atdebug("train drove over IP: speed=",speed,"shunt=",shunt) @@ -94,6 +94,7 @@ advtrains.tnc_register_on_approach(function(pos, id, train, index, has_entered, end -- nspd can now be: 1. !=0: new speed restriction, 2. =0: stop here or 3. nil: keep travspd if nspd then + travspd = nspd if nspd == -1 then travspd = nil else @@ -106,7 +107,7 @@ advtrains.tnc_register_on_approach(function(pos, id, train, index, has_entered, lspd = travspd local udata = {signal_pos = spos} - local callback = get_over_function(lspd, travsht) + local callback = get_over_function(lspd, travsht, asp.type) lzbdata.il_shunt = travsht lzbdata.il_speed = travspd --atdebug("new lzbdata",lzbdata) diff --git a/advtrains_signals_ks/doc/advtrains_signals_ks.7advtrains.md b/advtrains_signals_ks/doc/advtrains_signals_ks.7advtrains.md new file mode 100644 index 0000000..a043343 --- /dev/null +++ b/advtrains_signals_ks/doc/advtrains_signals_ks.7advtrains.md @@ -0,0 +1,60 @@ +% advtrains_signals_ks(7advtrains) | Advtrains User Guide + +# NAME +`advtrains_signals_ks` - Ks signals for advtrains + +# DESCRIPTION + +This mod includes a modified subset of German rail signals. This page documents the signals implemented by this mod and some differences between this mod and German signals used in real life. The TODO section is currently included as there are notable modifications to this mod by the current `new-ks` branch, but it will be removed when the branch is ready for review. + +# SIGNAL ASPECTS + +This section mainly describes the different signal aspects. Please note that the meaning of some signal aspects may differ from their RL counterparts, and that the differences documented in the following section are not comprehensive. + +Due to historical reasons, "ex-DB" and "ex-DR" are used to refer to the former Deutsche Bundesbahn (West Germany) and the former Deutsche Reichsbahn (East Germany), respectively. + +## Ks signals +The Ks signals are used like most other signals in advtrains. It has the following aspects: + +* Hp 0 (red light): Stop +* Ks 1 (green light): Proceed at maximum speed or with the speed limit shown on the Zs 3 indicator directly above the signal (if present) and expect to proceed the next main signal at maximum speed or, if the green light is flashing, with the speed limit shown on the Zs 3v indicator directly below the signal +* Ks 2 (yellow light): Proceed at maximum speed or with the speed limit shown on the Zs 3 indicator directly above the signal (if present) and expect to stop in front of the next main signal. + +In addition, Sh 1 (see below) may also appear with Hp 0, in which case the train continues in shunt mode. + +## Shunt signals +Shunt signals are labeled "Ks Shunting signal" in-game. It has the following aspects: + +* Sh 0 (two horizontally aligned red lights): Stop +* Sh 1/(ex-DR) Ra 12 (two white lights aligned on a slanted line): shunting allowed + +## Signal signs +There are a few signal signs provided by this mod: + +* Lf 7 (black number on a white background): Proceed with the permanent speed limit shown on the sign +* Lf 1/2 (black number on a yellow background): Proceed with the temporary speed limit shown on the sign +* Lf 3 (black letter "E" on a yellow background): The temporary speed limit previously set by Lf 1/2 is lifted +* "E" signal (**not** Lf 3) (black letter "E" on a white background): Proceed at maximum speed +* Ra 10 (the black text "Halt für Rangierfahrten" on a white semicircle): Do not proceed if in shunt mode +* Proceed as main ("PAM", in-game only) ("S" below a green arrow): Proceed without shunt mode + +# DIFFERENCES FROM REAL-LIFE SIGNALS + +[This document](https://www.bahnstatistik.de/Signale_pdf/SB-DBAG.pdf) is used for reference, + +* The speed is indicated in m/s instead of multiples of 10km/h. +* Due to the potentially large number of nodes, only certain hard-coded values are allowed. +* Certain visual effects, such as making signal signs reflective or lit at night, are not implemented. +* The "E" sign, unlike Lf 3, lifts the main speed restriction as if by Hp 1. +* The actual Lf 3 has an orange background. +* Signal signs operate independently from other signals. +* Distant signaling is not yet implemented. +* The location of most signals are not checked. The location of Zs 3 and Zs 3v are only checked relative to the location of the main (Ks) signal. +* The "shunt signals" in this mod are actually known as "Schutzsignale". The word "Rangiersignale" refers to a different set of signals (including acoustic signals) given by the person specifically responsible for train shunting. +* The ex-DB definition of Sh 1 ("Fahrverbot aufgehoben") is that the track section ahead is clear and does not imply that the driver is allowed to proceed. +* @orwell noted on the Minetest forum that the function of Lf 7 in the mod should normally be done with Zs 3. + +# TODO +* Implement warning speed for temporary slow zones ("vorübergehende Langsamfahrstellen") with Lf 1/Lf 2 and Lf 3. +* Use Zs 3 instead of Lf 7 for the main speed restriction; use Lf 7 for line speed restriction. +* Change the "E" sign to Zs 10. diff --git a/advtrains_signals_ks/init.lua b/advtrains_signals_ks/init.lua index 3a2f4d0..14d27b8 100755 --- a/advtrains_signals_ks/init.lua +++ b/advtrains_signals_ks/init.lua @@ -116,6 +116,7 @@ local suppasp_ra = { advtrains.trackplacer.register_tracktype("advtrains_signals_ks:hs") advtrains.trackplacer.register_tracktype("advtrains_signals_ks:ra") advtrains.trackplacer.register_tracktype("advtrains_signals_ks:sign") +advtrains.trackplacer.register_tracktype("advtrains_signals_ks:sign_lf") advtrains.trackplacer.register_tracktype("advtrains_signals_ks:zs3") advtrains.trackplacer.register_tracktype("advtrains_signals_ks:zs3v") advtrains.trackplacer.register_tracktype("advtrains_signals_ks:mast") @@ -296,6 +297,47 @@ for _, rtab in ipairs({ -- rotatable by trackworker advtrains.trackplacer.add_worked("advtrains_signals_ks:sign", typ, "_"..rot, prts.n) end + + for typ, prts in pairs { + [8] = {main = 8, n = "12", ici = true}, + [12] = {main = 12, n = "16"}, + [16] = {main = 16, n = "e"}, + ["e"] = {main = -1, n = "8"} + } do + minetest.register_node("advtrains_signals_ks:sign_lf_"..typ.."_"..rot, { + description = "Temporary local speed restriction sign", + drawtype = "mesh", + mesh = "advtrains_signals_ks_sign_smr"..rot..".obj", + tiles = {"advtrains_signals_ks_signpost.png", "advtrains_signals_ks_sign_"..typ..".png^[multiply:orange"}, + paramtype = "light", + sunlight_propagates = true, + light_source = 4, + paramtype2 = "facedir", + selection_box = { + type = "fixed", + fixed = {rtab.sbox, {-1/4, -1/2, -1/4, 1/4, -7/16, 1/4}} + }, + groups = { + cracky = 2, + advtrains_signal = 2, + not_blocking_trains = 1, + save_in_at_nodedb = 1, + not_in_creative_inventory = (rtab.ici and prts.ici) and 0 or 1, + }, + drop = "advtrains_signals_ks:sign_lf_8_0", + inventory_image = "advtrains_signals_ks_sign_8.png^[multiply:orange", + advtrains = { + -- This is a static signal! No set_aspect + get_aspect = function(pos, node) + return {main = prts.main, type = "temp"} + end, + }, + on_rightclick = advtrains.interlocking.signal_rc_handler, + can_dig = advtrains.interlocking.signal_can_dig, + after_dig_node = advtrains.interlocking.signal_after_dig + }) + advtrains.trackplacer.add_worked("advtrains_signals_ks:sign_lf", tostring(typ), "_"..rot, prts.n) + end -- Geschwindigkeits(vor)anzeiger für Ks-Signale for typ, prts in pairs({ -- cgit v1.2.3