Support different types of speed restrictions; add documentation

master
ywang 2021-11-03 18:55:31 +01:00
parent 2299470523
commit be2f37a067
10 changed files with 309 additions and 7 deletions

5
.dir-locals.el Normal file
View File

@ -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)))

View File

@ -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.

View File

@ -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`.

View File

@ -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

View File

@ -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)

88
advtrains/speed.lua Normal file
View File

@ -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,
}

View File

@ -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

View File

@ -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)

View File

@ -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.

View File

@ -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({