aboutsummaryrefslogtreecommitdiff
path: root/advtrains_line_automation
diff options
context:
space:
mode:
Diffstat (limited to 'advtrains_line_automation')
-rw-r--r--advtrains_line_automation/init.lua2
-rw-r--r--advtrains_line_automation/line_editor.lua676
-rw-r--r--advtrains_line_automation/line_functions.lua747
-rw-r--r--advtrains_line_automation/stoprail.lua92
-rw-r--r--advtrains_line_automation/structs.md96
5 files changed, 1526 insertions, 87 deletions
diff --git a/advtrains_line_automation/init.lua b/advtrains_line_automation/init.lua
index 5cd890c..7e65174 100644
--- a/advtrains_line_automation/init.lua
+++ b/advtrains_line_automation/init.lua
@@ -19,6 +19,8 @@ advtrains.lines = {
local modpath = minetest.get_modpath(minetest.get_current_modname()) .. DIR_DELIM
dofile(modpath.."railwaytime.lua")
+dofile(modpath.."line_functions.lua")
+dofile(modpath.."line_editor.lua")
dofile(modpath.."scheduler.lua")
dofile(modpath.."station_editor.lua")
dofile(modpath.."stoprail.lua")
diff --git a/advtrains_line_automation/line_editor.lua b/advtrains_line_automation/line_editor.lua
new file mode 100644
index 0000000..c24e2e6
--- /dev/null
+++ b/advtrains_line_automation/line_editor.lua
@@ -0,0 +1,676 @@
+local def
+local F = minetest.formspec_escape
+local ifthenelse = ch_core.ifthenelse
+
+local max_stations = 60
+
+local MODE_NORMAL = 0 -- normální zastávka (výchozí nebo mezilehlá)
+local MODE_REQUEST_STOP = 1 -- zastávka na znamení (mezilehlá)
+local MODE_HIDDEN = 2 -- skrytá zastávka (výchozí nebo mezilehlá)
+local MODE_DISABLED = 3 -- vypnutá zastávka (mezilehlá nebo koncová)
+local MODE_FINAL = 4 -- koncová zastávka (linka zde jízdu končí)
+local MODE_FINAL_HIDDEN = 5 -- koncová zastávka skrytá
+local MODE_FINAL_CONTINUE = 6 -- koncová zastávka (vlak pokračuje jako jiná linka)
+
+local color_red = core.get_color_escape_sequence("#ff0000")
+local color_green = core.get_color_escape_sequence("#00ff00")
+
+local cancel_linevar = assert(advtrains.lines.cancel_linevar)
+local get_line_description = assert(advtrains.lines.get_line_description)
+local linevar_decompose = assert(advtrains.lines.linevar_decompose)
+local try_get_linevar_def = assert(advtrains.lines.try_get_linevar_def)
+
+local function check_rights(pinfo, owner)
+ if pinfo.role == "new" or pinfo.role == "none" then
+ return false
+ end
+ if owner == nil or pinfo.role == "admin" or pinfo.player_name == owner then
+ return true
+ end
+ return false
+end
+
+local function get_stn_from_linevar(linevar)
+ local line, stn = linevar_decompose(linevar)
+ return ifthenelse(line ~= nil, stn, nil)
+end
+
+local function add_linevar(stn, linevar_def)
+ local station = advtrains.lines.stations[stn]
+ if station == nil then
+ return false, "Chybná stanice!"
+ end
+ local linevars = station.linevars
+ if linevars == nil then
+ linevars = {}
+ station.linevars = linevars
+ end
+ if linevars[linevar_def.name] ~= nil then
+ return false, "Nemohu přidat, varianta linky '"..linevar_def.name.."' již existuje."
+ end
+ linevars[linevar_def.name] = linevar_def
+ return true, nil
+end
+
+local function replace_linevar(stn, linevar_def)
+ local station = advtrains.lines.stations[stn]
+ if station == nil or station.linevars == nil then
+ return false, "Chybná stanice!"
+ end
+ local linevar = assert(linevar_def.name)
+ local linevars = station.linevars
+ if linevars[linevar] == nil then
+ return false, "Nemohu nahradit, varianta linky '"..linevar.."' dosud neexistuje!"
+ end
+ linevars[linevar] = linevar_def
+ for train_id, train in pairs(advtrains.trains) do
+ local ls = train.line_status
+ if ls ~= nil and ls.linevar == linevar then
+ core.log("action", "Train "..train_id.." restarted from index "..ls.linevar_index.." due to replacement of linevar '"..linevar.."'.")
+ ls.linevar_index = 1
+ end
+ end
+ return true, nil
+end
+
+local function delete_linevar(stn, linevar)
+ local station = advtrains.lines.stations[stn]
+ if station == nil or station.linevars == nil then
+ return false, "Chybná stanice!"
+ end
+ local linevars = station.linevars
+ if linevars[assert(linevar)] == nil then
+ return false, "Nemohu odstranit, varianta linky '"..linevar.."' nebyla nalezena!"
+ end
+ linevars[linevar] = nil
+ for train_id, train in pairs(advtrains.trains) do
+ local ls = train.line_status
+ if ls ~= nil and ls.linevar == linevar then
+ core.log("action", "Train "..train_id.." removed from deleted linevar '"..linevar.."'.")
+ cancel_linevar(train)
+ end
+ end
+ return true, nil
+end
+
+local function get_formspec(custom_state)
+ local pinfo = ch_core.normalize_player(assert(custom_state.player_name))
+ if pinfo.player == nil then
+ minetest.log("error", "Expected player not in game!")
+ return ""
+ end
+
+ local selection_index = custom_state.selection_index
+ local formspec = {
+ ch_core.formspec_header({formspec_version = 6, size = {20, 16}, auto_background = true}),
+ "label[0.5,0.6;Editor variant linek]"..
+ "style[s01_pos",
+ }
+ for i = 2, max_stations do
+ table.insert(formspec, string.format(",s%02d_pos", i))
+ end
+ table.insert(formspec, ";font=mono;font_size=-4]"..
+ "style[rc;font=mono]"..
+ "tablecolumns[color;text,align=right;text;text,align=center;color;text,width=7;color;text]"..
+ "table[0.5,1.25;19,5;linevar;#ffffff,LINKA,TRASA,SM. KÓD,#ffffff,SPRAVUJE,#ffffff,STAV")
+
+ for _, linevar_def in ipairs(custom_state.linevars) do
+ local lv_line, lv_stn, lv_rc = linevar_decompose(linevar_def.name)
+ local color = ifthenelse(linevar_def.disabled, "#cccccc", "#ffffff")
+ table.insert(formspec,
+ ","..color..","..F(lv_line)..","..F(get_line_description(linevar_def, {first_stop = true, last_stop = true}))..
+ ","..F(lv_rc)..","..ifthenelse(linevar_def.owner == pinfo.player_name, "#00ff00", color)..",")
+ table.insert(formspec, F(ch_core.prihlasovaci_na_zobrazovaci(linevar_def.owner)))
+ table.insert(formspec, ","..color..",")
+ if linevar_def.disabled then
+ table.insert(formspec, "vypnutá")
+ end
+ end
+ if selection_index ~= nil then
+ table.insert(formspec, ";"..selection_index.."]")
+ else
+ table.insert(formspec, ";]")
+ end
+ if pinfo.role ~= "new" then
+ table.insert(formspec, "button[14.5,0.3;3.5,0.75;create;nová varianta...]")
+ end
+ local has_rights_to_open_variant =
+ pinfo.role == "admin" or (selection_index or 1) == 1 or
+ pinfo.player_name == custom_state.linevars[selection_index - 1].owner
+
+ if has_rights_to_open_variant then
+ table.insert(formspec, "button[10.5,0.3;3.5,0.75;delete;smazat variantu]")
+ end
+ table.insert(formspec, "button_exit[18.75,0.3;0.75,0.75;close;X]"..
+ "field[0.5,7;1.25,0.75;line;linka:;"..F(custom_state.line).."]"..
+ "field[2,7;1.5,0.75;rc;sm.kód:;"..F(custom_state.rc).."]"..
+ "field[3.75,7;3,0.75;train_name;jméno vlaku:;"..F(custom_state.train_name).."]")
+ if pinfo.role ~= "admin" then
+ table.insert(formspec, "label[7,6.75;spravuje:\n")
+ else
+ table.insert(formspec, "field[7,7;4,0.75;owner;spravuje:;")
+ end
+ table.insert(formspec, F(custom_state.owner).."]"..
+ "checkbox[11.25,7.25;disable_linevar;vypnout;"..custom_state.disable_linevar.."]")
+
+ if pinfo.role ~= "new" then
+ if has_rights_to_open_variant then
+ table.insert(formspec, "button[10,15;4.5,0.75;save;"..
+ ifthenelse(custom_state.compiled_linevar == nil, "ověřit změny\npřed uložením]", "uložit změny]"))
+ end
+ table.insert(formspec, "button[15,15.25;4,0.5;reset;vrátit změny]")
+ end
+
+ table.insert(formspec, "tooltip[line;"..
+ "Označení linky. Musí být neprázdné. Varianta linky bude použita pouze na vlaky s tímto označením linky.]"..
+ "tooltip[rc;Směrový kód. Může být prázdný. Varianta linky bude použita pouze na vlaky\\,\n"..
+ "jejichž směrový kód se přesně shoduje se zadaným. Obvykle se toto pole nechává prázdné.]"..
+ "tooltip[train_name;Volitelný údaj. Je-li zadán\\, jízdní řády budou uvádět u spojů této\n"..
+ "varianty zadané jméno.]"..
+ "tooltip[disable_linevar;Zaškrtnutím variantu linky vypnete. Vypnutá varianta linky není používána\n"..
+ "na žádné další vlaky\\, stávající vlaky však mohou dojet do svých koncových zastávek.]")
+
+ table.insert(formspec, "container[0,8.75]"..
+ "label[0.5,0.25;odjezd]"..
+ "label[2,0.25;kód dop.]"..
+ "label[4.75,0.25;režim zastávky]"..
+ "label[9.5,0.25;kolej]"..
+ "label[11,0.25;omezení pozice]"..
+ "scrollbaroptions[min=0;max=550;arrows=show]"..
+ "scrollbar[19,0.5;0.5,5.5;vertical;evl_scroll;"..custom_state.evl_scroll.."]"..
+ "scroll_container[0.5,0.5;18.5,5.5;evl_scroll;vertical]"..
+ "box[0,0;20,70;#00808040]") -- box[] = pozadí
+
+ -- výchozí zastávka:
+ table.insert(formspec,
+ "label[0.1,0.4;0]"..
+ "field[1.5,0;2.5,0.75;s01_stn;;"..F(custom_state.stops[1].stn).."]"..
+ "dropdown[4.25,0;4.5,0.75;s01_mode;výchozí,skrytá (výchozí);"..custom_state.stops[1].mode..";true]"..
+ "field[9,0;1.25,0.75;s01_track;;"..F(custom_state.stops[1].track).."]"..
+ "field[10.5,0;3,0.75;s01_pos;;"..F(custom_state.stops[1].pos).."]"..
+ "label[13.75,0.4;"..F(custom_state.stops[1].label).."]")
+
+ -- ostatní zastávky:
+ local y_base, y_scale = 0, 1
+ for i = 2, max_stations do
+ local stop = custom_state.stops[i]
+ local n
+ if i < 10 then
+ n = "0"..i
+ else
+ n = tostring(i)
+ end
+ local y = string.format("%f", y_base + (i - 1) * y_scale)
+ local y2 = string.format("%f", y_base + (i - 1) * y_scale + 0.4) -- for a label
+ table.insert(formspec,
+ "field[0,"..y..";1.25,0.75;s"..n.."_dep;;"..F(stop.dep).."]"..
+ "field[1.5,"..y..";2.5,0.75;s"..n.."_stn;;"..F(stop.stn).."]"..
+ "dropdown[4.25,"..y..";4.5,0.75;s"..n.."_mode;normální,na znamení,skrytá (mezilehlá),vypnutá,koncová,koncová skrytá,"..
+ "koncová (pokračuje);"..stop.mode..";true]"..
+ "field[9,"..y..";1.25,0.75;s"..n.."_track;;"..F(stop.track).."]"..
+ "field[10.5,"..y..";3,0.75;s"..n.."_pos;;"..F(stop.pos).."]"..
+ "label[13.75,"..y2..";"..F(stop.label).."]")
+ end
+
+ table.insert(formspec,
+ "scroll_container_end[]"..
+ "tooltip[0,0;2,1;Odjezd: očekávaná jízdní doba v sekundách od odjezdu z výchozí zastávky\n"..
+ "do odjezdu z dané zastávky. Podle ní se počítá zpoždění. Hodnota musí být jedinečná\n"..
+ "pro každou zastávku na lince a podle ní se zastávky seřadí.\n"..
+ "Pro úplné smazání dopravny z linky nechte pole prázdné.]"..
+ "tooltip[2,0;2.75,1;Kód dopravny: kód dopravny\\, kde má vlak zastavit. Vlak bude ignorovat\n"..
+ "ARS pravidla a zastaví na první zastávkové koleji v dopravně pro odpovídající počet vagonů.\n"..
+ "Kód dopravny se na lince může opakovat.]"..
+ "tooltip[4.75,0;4.75,1;Režim zastávky: výchozí/normální - vždy zastaví\\;\n"..
+ "na znamení: zastaví na znamení (zatím neimplementováno)\\;\n"..
+ "skrytá – vždy zastaví\\, ale nezobrazí se v jízdních řádech\\;\n"..
+ "vypnutá – nezastaví (použijte při výlukách nebo při zrušení zastávky)\\;\n"..
+ "koncová – vždy zastaví a tím ukončí spoj\\, vlak se stane nelinkovým\\;\n"..
+ "koncová (pokračuje) – jako koncová\\, ale vlak se může na odjezdu opět stát linkovým.]"..
+ "tooltip[9.5,0;1.5,1;Kolej: nepovinný\\, orientační údaj do jízdních řádů – na které koleji\n"..
+ "vlaky obvykle zastavují. Nepovinný údaj.]"..
+ "tooltip[10.5,0;3.5,1;Omezení pozice: Zadávejte jen v případě potřeby.\n"..
+ "Je-li zadáno\\, vlak v dané dopravně nezastaví na žádné jiné zastávkové koleji\n"..
+ "než na té\\, která leží přesně na zadané pozici. Příklad platné hodnoty:\n123,7,-13]"..
+ "container_end[]")
+
+ -- if pinfo.role ~= "new" then
+ return table.concat(formspec)
+end
+
+local mode_from_formspec_map = {MODE_NORMAL, MODE_REQUEST_STOP, MODE_HIDDEN, MODE_DISABLED, MODE_FINAL, MODE_FINAL_HIDDEN, MODE_FINAL_CONTINUE}
+local mode_to_formspec_map = table.key_value_swap(mode_from_formspec_map)
+
+local function mode_to_formspec(i, raw_mode)
+ if i == 1 then
+ return ifthenelse(raw_mode ~= nil and raw_mode == MODE_HIDDEN, 2, 1)
+ elseif raw_mode == nil then
+ return 1
+ else
+ return mode_to_formspec_map[raw_mode] or 1
+ end
+end
+
+local function mode_from_formspec(i, fs_mode)
+ if i == 1 then
+ return ifthenelse(fs_mode == 2, MODE_HIDDEN, nil)
+ else
+ local result = mode_from_formspec_map[fs_mode]
+ return ifthenelse(result ~= nil and result ~= MODE_NORMAL, result, nil)
+ end
+end
+
+local function custom_state_set_selection_index(custom_state, new_selection_index)
+ -- this will also refresh stops and resets the changes
+ assert(custom_state.player_name)
+ assert(custom_state.linevars)
+ assert(new_selection_index)
+ local current_linevar = custom_state.linevars[new_selection_index - 1]
+ custom_state.selection_index = new_selection_index or 1
+ local stops = custom_state.stops
+ if stops == nil then
+ stops = {}
+ custom_state.stops = stops
+ end
+ local linevar_stops
+ if current_linevar ~= nil then
+ linevar_stops = current_linevar.stops
+ else
+ linevar_stops = {}
+ end
+
+ for i = 1, max_stations do
+ local stop = linevar_stops[i]
+ if stop ~= nil then
+ stops[i] = {
+ dep = tostring(assert(stop.dep)),
+ stn = assert(stop.stn),
+ mode = mode_to_formspec(i, stop.mode),
+ track = stop.track or "",
+ pos = stop.pos or "",
+ label = "",
+ }
+ else
+ stops[i] = {
+ dep = ifthenelse(i == 1, "0", ""),
+ stn = "",
+ mode = 1,
+ track = "",
+ pos = "",
+ label = "",
+ }
+ end
+ end
+ if current_linevar ~= nil then
+ local lv_line, lv_stn, lv_rc = linevar_decompose(current_linevar.name)
+ custom_state.line = lv_line or ""
+ custom_state.rc = lv_rc or ""
+ custom_state.train_name = current_linevar.train_name or ""
+ custom_state.owner = assert(current_linevar.owner)
+ custom_state.disable_linevar = ifthenelse(current_linevar.disabled, "true", "false")
+ else
+ custom_state.line = ""
+ custom_state.rc = ""
+ custom_state.train_name = ""
+ custom_state.owner = custom_state.player_name
+ custom_state.disable_linevar = "false"
+ end
+ custom_state.compiled_linevar = nil
+ custom_state.evl_scroll = 0
+end
+
+local function num_transform(s)
+ local prefix = s:match("^([0-9]+)/")
+ if prefix == nil then
+ return s
+ end
+ return string.format(" %020d%s", tonumber(prefix) or 0, s:sub(#prefix, -1))
+end
+
+local function linevars_sorter(a, b)
+ return num_transform(a.name) < num_transform(b.name)
+end
+
+local function custom_state_refresh_linevars(custom_state, linevar_to_select)
+ assert(custom_state.player_name)
+ local linevars = {}
+ for _, stdata in pairs(advtrains.lines.stations) do
+ if stdata.linevars ~= nil then
+ for _, linevar_def in pairs(stdata.linevars) do
+ table.insert(linevars, linevar_def)
+ end
+ end
+ end
+ table.sort(linevars, linevars_sorter)
+ custom_state.selection_index = nil
+ custom_state.linevars = linevars
+ custom_state.compiled_linevar = nil
+ if linevar_to_select ~= nil then
+ for i, linevar_def in ipairs(linevars) do
+ if linevar_def.name == linevar_to_select then
+ custom_state_set_selection_index(custom_state, i + 1)
+ return true
+ end
+ end
+ return false
+ end
+end
+
+local function custom_state_compile_linevar(custom_state)
+ local stations = advtrains.lines.stations
+ local line = assert(custom_state.line)
+ local stn = assert(custom_state.stops[1].stn)
+ local rc = assert(custom_state.rc)
+ local train_name = assert(custom_state.train_name)
+ local owner = assert(custom_state.owner)
+ local stops = {}
+ if line == "" then
+ return false, "Označení linky nesmí být prázdné!"
+ elseif line:find("[/|\\]") then
+ return false, "Označení linky nesmí obsahovat znaky '/', '|' a '\\'!"
+ elseif line:len() > 256 then
+ return false, "Označení linky je příliš dlouhé!"
+ elseif stn == "" then
+ return false, "Výchozí zastávka musí být vyplněná!"
+ elseif rc:find("[/|\\]") then
+ return false, "Směrový kód nesmí obsahovat znaky '/', '|' a '\\'!"
+ elseif owner == "" then
+ return false, "Správa linky musí být vyplněná!"
+ elseif train_name:len() > 256 then
+ return false, "Jméno vlaku je příliš dlouhé!"
+ end
+ -- Zkontrolovat zastávky:
+ local errcount = 0
+ local finalcount = 0
+ local dep_to_index = {}
+ for i, stop in ipairs(assert(custom_state.stops)) do
+ local good_label
+ stop.label = ""
+ if stop.dep == "" then
+ -- přeskočit
+ elseif not stop.dep:match("^[0-9][0-9]*$") then
+ errcount = errcount + 1
+ stop.label = color_red.."Chybný formát času odjezdu!"
+ elseif tonumber(stop.dep) < 0 or tonumber(stop.dep) > 3600 then
+ errcount = errcount + 1
+ stop.label = color_red.."Čas odjezdu musí být v rozsahu 0 až 3600!"
+ elseif dep_to_index[tonumber(stop.dep)] ~= nil then
+ errcount = errcount + 1
+ stop.label = color_red.."Duplicitní čas odjezdu!"
+ else
+ dep_to_index[tonumber(stop.dep)] = i
+ if stop.stn == "" or stations[stop.stn] == nil or stations[stop.stn].name == nil then
+ errcount = errcount + 1
+ stop.label = color_red.."Neznámý kód dopravny!"
+ elseif stop.stn:find("[/|\\]") then
+ errcount = errcount + 1
+ stop.label = color_red.."Kód dopravny nesmí obsahovat znaky '/', '|' a '\\'!"
+ elseif stop.track:len() > 16 then
+ errcount = errcount + 1
+ stop.label = color_red.."Označení koleje je příliš dlouhé!"
+ elseif stop.pos ~= "" and not stop.pos:match("^[-0-9][0-9]*,[-0-9][0-9]*,[-0-9][0-9]*$") then
+ errcount = errcount + 1
+ stop.label = color_red.."Neplatný formát pozice!"
+ elseif stop.pos:len() > 22 then
+ errcount = errcount + 1
+ stop.label = color_red.."Specifikace pozice je příliš dlouhá!"
+ else
+ -- v pořádku:
+ local new_stop = {
+ stn = stop.stn,
+ dep = tonumber(stop.dep),
+ }
+ local new_mode = mode_from_formspec(i, stop.mode)
+ if new_mode ~= nil then
+ new_stop.mode = new_mode
+ if i > 1 and (new_mode == MODE_FINAL or new_mode == MODE_FINAL_CONTINUE or new_mode == MODE_FINAL_HIDDEN) then
+ finalcount = finalcount + 1
+ end
+ end
+ if stop.pos ~= "" then
+ new_stop.pos = stop.pos
+ end
+ if stop.track ~= "" then
+ new_stop.track = stop.track
+ end
+ table.insert(stops, new_stop)
+ if stop.stn ~= "" then
+ stop.label = color_green.."= "..assert(stations[stop.stn].name)
+ end
+ end
+ end
+ end
+ if errcount > 0 then
+ return false, errcount.." chyb v seznamu zastávek!"
+ end
+ if finalcount == 0 then
+ return false, "Varianta linky musí obsahovat alespoň jednu koncovou zastávku!"
+ end
+ table.sort(stops, function(a, b) return a.dep < b.dep end)
+ custom_state.compiled_linevar = {
+ name = line.."/"..stops[1].stn.."/"..rc,
+ line = line,
+ owner = owner,
+ stops = stops,
+ }
+ if train_name ~= "" then
+ custom_state.compiled_linevar.train_name = train_name
+ end
+ if custom_state.disable_linevar == "true" then
+ custom_state.compiled_linevar.disabled = true
+ end
+ return true, nil
+end
+
+local function formspec_callback(custom_state, player, formname, fields)
+ local reload_stations, update_formspec = false, false
+ -- print("DEBUG: "..dump2({custom_state = custom_state, formname = formname, fields = fields}))
+
+ if fields.quit then
+ return
+ end
+ -- scrollbar:
+ if fields.evl_scroll then
+ local event = core.explode_scrollbar_event(fields.evl_scroll)
+ if event.type == "CHG" then
+ custom_state.evl_scroll = event.value
+ end
+ end
+ -- checkbox:
+ if fields.disable_linevar then
+ custom_state.disable_linevar = fields.disable_linevar
+ end
+ -- dropdowns:
+ for i = 1, max_stations do
+ local id = string.format("s%02d_mode", i)
+ local n = tonumber(fields[id])
+ if n ~= nil then
+ custom_state.stops[i].mode = n
+ end
+ end
+ -- fields:
+ for _, key in ipairs({"line", "rc", "train_name", "owner"}) do
+ if fields[key] then
+ custom_state[key] = fields[key]
+ end
+ end
+ for i, stop in ipairs(custom_state.stops) do
+ local prefix = string.format("s%02d_", i)
+ for _, key in ipairs({"dep", "stn", "track", "pos"}) do
+ local value = fields[prefix..key]
+ if value then
+ stop[key] = value
+ end
+ end
+ end
+ -- selection:
+ if fields.linevar then
+ local event = core.explode_table_event(fields.linevar)
+ if event.type == "CHG" or event.type == "DCL" then
+ custom_state_set_selection_index(custom_state, assert(tonumber(event.row)))
+ update_formspec = true
+ end
+ end
+
+ -- buttons:
+ if fields.create then
+ custom_state_set_selection_index(custom_state, 1)
+ update_formspec = true
+ elseif fields.reset then
+ custom_state_set_selection_index(custom_state, custom_state.selection_index or 1)
+ update_formspec = true
+ elseif fields.save then
+ local pinfo = ch_core.normalize_player(player)
+ if pinfo.role == "new" or pinfo.role == "none" then
+ core.log("error", "Access violation in line editor caused by '"..pinfo.player_name.."'!")
+ return -- access violation!
+ end
+ if custom_state.compiled_linevar == nil then
+ -- zkontrolovat a skompilovat
+ local success, errmsg = custom_state_compile_linevar(custom_state)
+ if success then
+ -- TODO: zkontrolovat práva a možnost přepsání i zde!
+ ch_core.systemovy_kanal(pinfo.player_name, "Úspěšně ověřeno. Varianta linky může být uložena.")
+ else
+ ch_core.systemovy_kanal(pinfo.player_name, "Ověření selhalo: "..(errmsg or "Neznámý důvod"))
+ end
+ update_formspec = true
+ else
+ -- pokusit se uložit...
+ local selection_index = custom_state.selection_index or 1
+ local selected_linevar, selected_linevar_def, selected_linevar_station
+ local to_linevar, to_linevar_def, to_linevar_station
+ local new_linevar, new_linevar_def, new_linevar_station
+
+ -- NEW:
+ new_linevar_def = custom_state.compiled_linevar
+ new_linevar = new_linevar_def.name
+ new_linevar_station = get_stn_from_linevar(new_linevar)
+
+ -- SELECTED:
+ if custom_state.selection_index > 1 and custom_state.linevars[selection_index - 1] ~= nil then
+ selected_linevar_def, selected_linevar_station = try_get_linevar_def(custom_state.linevars[selection_index - 1].name)
+ if selected_linevar_def ~= nil then
+ selected_linevar = selected_linevar_def.name
+ end
+ end
+
+ -- TO OVERWRITE:
+ to_linevar_def, to_linevar_station = try_get_linevar_def(new_linevar)
+ if to_linevar_def ~= nil then
+ to_linevar = to_linevar_def.name
+ end
+
+ local success, errmsg
+ if selected_linevar == nil then
+ if to_linevar == nil then
+ -- zcela nová varianta
+ success, errmsg = add_linevar(new_linevar_station, new_linevar_def)
+ else
+ -- replace
+ success = check_rights(pinfo, to_linevar_def.owner)
+ if success then
+ success, errmsg = replace_linevar(new_linevar_station, new_linevar_def)
+ else
+ errmsg = "Nedostatečná práva k variantě linky '"..to_linevar.."'."
+ end
+ end
+ elseif to_linevar == nil then
+ -- delete and add
+ success = check_rights(pinfo, selected_linevar_def.owner)
+ if success then
+ success, errmsg = delete_linevar(selected_linevar_station, selected_linevar)
+ if success then
+ success, errmsg = add_linevar(new_linevar_station, new_linevar_def)
+ end
+ else
+ errmsg = "Nedostatečná práva k variantě linky '"..selected_linevar.."'."
+ end
+ elseif selected_linevar ~= to_linevar then
+ -- delete and replace
+ success = check_rights(pinfo, to_linevar_def.owner)
+ if success then
+ success = check_rights(pinfo, selected_linevar_def.owner)
+ if success then
+ success, errmsg = delete_linevar(selected_linevar_station, selected_linevar)
+ if success then
+ success, errmsg = replace_linevar(new_linevar_station, new_linevar_def)
+ end
+ else
+ errmsg = "Nedostatečná práva k variantě linky '"..selected_linevar.."'."
+ end
+ else
+ errmsg = "Nedostatečná práva k variantě linky '"..to_linevar.."'."
+ end
+ else
+ -- replace
+ success = check_rights(pinfo, to_linevar_def.owner)
+ if success then
+ success, errmsg = replace_linevar(new_linevar_station, new_linevar_def)
+ else
+ errmsg = "Nedostatečná práva k variantě linky '"..to_linevar.."'."
+ end
+ end
+
+ if success then
+ ch_core.systemovy_kanal(pinfo.player_name, "Varianta linky '"..new_linevar.."' úspěšně uložena.")
+ custom_state_refresh_linevars(custom_state, new_linevar)
+ update_formspec = true
+ else
+ ch_core.systemovy_kanal(pinfo.player_name, "Ukládání selhalo: "..(errmsg or "Neznámá chyba."))
+ end
+ end
+
+ elseif fields.delete then
+ local pinfo = ch_core.normalize_player(player)
+ if pinfo.role == "new" or pinfo.role == "none" then
+ core.log("error", "Access violation in line editor caused by '"..pinfo.player_name.."'!")
+ return -- access violation!
+ end
+ local selection_index = custom_state.selection_index or 1
+ local selected_linevar, selected_linevar_def, selected_linevar_station
+ if custom_state.selection_index > 1 and custom_state.linevars[selection_index - 1] ~= nil then
+ selected_linevar_def, selected_linevar_station = try_get_linevar_def(custom_state.linevars[selection_index - 1].name)
+ if selected_linevar_def ~= nil then
+ selected_linevar = selected_linevar_def.name
+ end
+ local success, errmsg
+ success = check_rights(pinfo, selected_linevar_def.owner)
+ if success then
+ success, errmsg = delete_linevar(selected_linevar_station, selected_linevar)
+ else
+ errmsg = "Nedostatečná práva k variantě linky '"..selected_linevar.."'."
+ end
+ if success then
+ ch_core.systemovy_kanal(pinfo.player_name, "Varianta linky '"..selected_linevar.."' úspěšně smazána.")
+ custom_state_refresh_linevars(custom_state)
+ update_formspec = true
+ else
+ ch_core.systemovy_kanal(pinfo.player_name, "Mazání selhalo: "..(errmsg or "Neznámá chyba."))
+ end
+ end
+ end
+
+ if update_formspec then
+ return get_formspec(custom_state)
+ end
+end
+
+local function show_formspec(player)
+ if player == nil then return false end
+ local custom_state = {
+ player_name = assert(player:get_player_name()),
+ evl_scroll = 0,
+ }
+ custom_state_refresh_linevars(custom_state)
+ custom_state_set_selection_index(custom_state, 1)
+ ch_core.show_formspec(player, "advtrains_line_automation:editor_linek", get_formspec(custom_state), formspec_callback, custom_state, {})
+end
+
+def = {
+ -- params = "",
+ description = "Otevře editor variant linek",
+ privs = {ch_registered_player = true},
+ func = function(player_name, param) show_formspec(minetest.get_player_by_name(player_name)) end,
+}
+core.register_chatcommand("linky", def)
diff --git a/advtrains_line_automation/line_functions.lua b/advtrains_line_automation/line_functions.lua
new file mode 100644
index 0000000..7e050be
--- /dev/null
+++ b/advtrains_line_automation/line_functions.lua
@@ -0,0 +1,747 @@
+local al = advtrains.lines
+local rwt = assert(advtrains.lines.rwt)
+
+local MODE_NORMAL = 0 -- normální zastávka (výchozí nebo mezilehlá)
+local MODE_REQUEST_STOP = 1 -- zastávka na znamení (mezilehlá)
+local MODE_HIDDEN = 2 -- skrytá zastávka (výchozí nebo mezilehlá)
+local MODE_DISABLED = 3 -- vypnutá zastávka (mezilehlá nebo koncová)
+local MODE_FINAL = 4 -- koncová zastávka (linka zde jízdu končí)
+local MODE_FINAL_HIDDEN = 5 -- koncová zastávka skrytá
+local MODE_FINAL_CONTINUE = 6 -- koncová zastávka (vlak pokračuje jako jiná linka)
+
+local simple_modes = {
+ [MODE_NORMAL] = MODE_NORMAL,
+ [MODE_REQUEST_STOP] = MODE_NORMAL,
+ [MODE_HIDDEN] = MODE_NORMAL,
+ [MODE_DISABLED] = MODE_DISABLED,
+ [MODE_FINAL] = MODE_FINAL,
+ [MODE_FINAL_HIDDEN] = MODE_FINAL,
+ [MODE_FINAL_CONTINUE] = MODE_FINAL,
+}
+
+local debug_print_i = 0
+
+-- LOCAL funkce:
+-- =========================================================================
+local function debug_print(s)
+ debug_print_i = debug_print_i + 1
+ core.chat_send_all("["..debug_print_i.."] "..tostring(s))
+ return s
+end
+
+-- Je-li stn kód stanice, vrací její jméno, jinak vrací "???". stn může být i nil.
+local function get_station_name(stn)
+ local station = advtrains.lines.stations[stn or ""]
+ if station ~= nil and station.name ~= nil then
+ return station.name
+ else
+ return "???"
+ end
+end
+
+--[[
+ -- Vrací index následujícího výskytu 'stn' v seznamu zastávek podle linevar_def.
+ -- Vrací i skryté zastávky, ale ne vypnuté.
+ -- Je-li linevar_def == nil nebo stn == nil nebo stn == "", vrací nil.
+ - line_status = table
+ - linevar_def = table or nil
+ - stn = string
+ return: int or nil
+]]
+local function find_next_index(line_status, linevar_def, stn)
+ assert(line_status)
+ if linevar_def ~= nil and stn ~= nil and stn ~= "" then
+ local stops = linevar_def.stops
+ if line_status.linevar_index < #stops then
+ for i = line_status.linevar_index + 1, #stops do
+ local stop = stops[i]
+ if stop.stn == stn and (stop.mode or MODE_NORMAL) ~= MODE_DISABLED then
+ return i
+ end
+ end
+ end
+ end
+end
+
+--[[
+ Používá se na *nelinkový* vlak stojící na *neanonymní* zastávce.
+ Zahájí jízdu na lince nalezené podle kombinace line/stn/rc, nebo vrátí false.
+]]
+local function line_start(train, stn, departure_rwtime)
+ assert(train)
+ assert(stn and stn ~= "")
+ assert(departure_rwtime)
+ local ls = al.get_line_status(train)
+ if ls.linevar ~= nil then
+ error("line_start() used on a train that is already on a line: "..dump2({train = train}))
+ end
+ local linevar, linevar_def = al.try_get_linevar(train.line, stn, train.routingcode)
+ if linevar == nil or linevar_def.disabled then
+ -- print("DEBUG: line_start() failed for "..(train.line or "").."/"..stn.."/"..(train.routingcode or ""))
+ return false
+ end
+ ls.linevar = linevar
+ ls.linevar_station = stn
+ ls.linevar_index = 1
+ ls.linevar_dep = departure_rwtime
+ ls.linevar_last_dep = departure_rwtime
+ ls.linevar_last_stn = stn
+ train.text_outside = al.get_line_description(linevar_def, {line_number = true, last_stop = true, last_stop_prefix = "", train_name = true})
+ -- print("DEBUG: line_start(): "..dump2({train_id = train.id, line_status = ls}))
+ return true
+end
+
+local function should_stop(pos, stdata, train)
+ if stdata == nil or stdata.stn == nil then
+ -- print("DEBUG: should_stop() == false, because stdata is invalid!")
+ return false -- neplatná data
+ end
+ local n_trainparts = #assert(train.trainparts)
+ -- vyhovuje počet vagonů?
+ if not ((stdata.minparts or 0) <= n_trainparts and n_trainparts <= (stdata.maxparts or 128)) then
+ -- print("DEBUG: should_stop("..stdata.stn..") == false, because n_trainparts is not in interval")
+ return false
+ end
+ local stn = assert(stdata.stn) -- zastávka stále může být anonymní
+ local ls, linevar_def = al.get_line_status(train)
+ local next_index
+ if stn ~= "" then
+ next_index = find_next_index(ls, linevar_def, stn)
+ end
+ -- jde o linkový vlak?
+ if linevar_def ~= nil then
+ if next_index == nil then
+ -- print("DEBUG: should_stop("..stn..") == false, because linevar=='"..ls.linevar.."' and next_index == nil")
+ return false
+ end
+ local stop = assert(linevar_def.stops[next_index])
+ if stop.pos ~= nil and stop.pos ~= string.format("%d,%d,%d", pos.x, pos.y, pos.z) then
+ -- print("DEBUG: should_stop("..stn..") == false, because the stop is limited to position "..stop.pos)
+ return false
+ end
+ if stop.mode ~= nil and stop.mode == MODE_REQUEST_STOP then
+ -- TODO...
+ ---debug_print("Vlak "..train.id.." by měl zastavit na zastávce na znamení.")
+ end
+ -- print("DEBUG: should_stop("..stn..") == true for "..linevar_def.name)
+ return true
+ -- local result = next_index ~= nil -- zastávka má index => zastavit
+ --[[
+ if result then
+ print("DEBUG: should_stop() == true, because linevar=='"..ls.linevar.."' and get_line_status() retuned next_index == "..next_index)
+ else
+ print("DEBUG: should_stop() == false, because linevar=='"..ls.linevar.."' and get_line_status() retuned next_index == nil")
+ end
+ ]]
+ -- return result
+ -- TODO: zastávky na znamení
+ else
+ local ars = stdata.ars
+ -- vyhovuje vlak ARS pravidlům?
+ local result = ars and (ars.default or advtrains.interlocking.ars_check_rule_match(ars, train))
+ --[[
+ if result then
+ print("DEBUG: should_stop("..stn..") == true, because linevar==nil and ARS rules match")
+ else
+ print("DEBUG: should_stop("..stn..") == false, because linevar==nil and ARS rules don't match")
+ end ]]
+ return result
+ end
+end
+
+local function update_value(train, key, setting)
+ if setting == "-" then
+ train[key] = nil
+ elseif setting ~= "" then
+ train[key] = setting
+ end
+ return train[key]
+end
+
+--[[
+ Pokud vlak jede na lince, odebere ho z této linky a vrátí true; jinak vrátí false.
+]]
+function al.cancel_linevar(train)
+ local ls = train.line_status
+ if ls == nil or ls.linevar == nil then return false end
+ -- print("DEBUG: line_end(train_id = "..train.id..", linevar was: "..ls.linevar.."): "..dump2({line_status_old = ls}))
+ ls.linevar = nil
+ ls.linevar_station = nil
+ ls.linevar_index = nil
+ ls.linevar_dep = nil
+ ls.linevar_last_dep = nil
+ ls.linevar_last_stn = nil
+ train.text_outside = ""
+ return true
+end
+
+-- Vrací seznam názvů neskrytých zastávek, počínaje od uvedeného indexu.
+-- Pokud by vrátila prázdný seznam, vrátí místo něj {"???"}.
+function al.get_stop_names(linevar_def, start_index)
+ local stations = advtrains.lines.stations
+ local result = {}
+ if linevar_def ~= nil then
+ local stops = linevar_def.stops
+ if start_index == nil then
+ start_index = 1
+ end
+ for i = start_index, #stops do
+ local stop = stops[i]
+ if stop.mode == nil or (stop.mode ~= MODE_HIDDEN and stop.mode ~= MODE_FINAL_HIDDEN and stop.mode ~= MODE_DISABLED) then
+ local station = stations[stop.stn]
+ if station ~= nil and station.name ~= nil and station.name ~= "" then
+ table.insert(result, station.name)
+ else
+ table.insert(result, "???")
+ end
+ end
+ end
+ end
+ if result[1] == nil then
+ result[1] = "???"
+ end
+ return result
+end
+
+--[[
+ Vrací:
+ a) index, stop_data -- pokud byla vyhovující další zastávka nalezena
+ b) nil, nil -- pokud nalezena nebyla
+]]
+function al.get_next_stop(linevar_def, current_index, allow_hidden_stops)
+ local stops = assert(linevar_def.stops)
+ if current_index < #stops then
+ for i = current_index + 1, #stops do
+ local mode = stops[i].mode
+ if mode == nil or (mode ~= MODE_DISABLED and ((mode ~= MODE_HIDDEN and mode ~= MODE_FINAL_HIDDEN) or allow_hidden_stops)) then
+ return i, stops[i]
+ end
+ end
+ end
+ return nil, nil
+end
+
+--[[
+ Vrací:
+ a) index, stop_data -- pokud byla vyhovující koncová zastávka nalezena
+ b) nil, nil -- pokud nalezena nebyla
+]]
+function al.get_terminus(linevar_def, current_index, allow_hidden_stops)
+ local stops = assert(linevar_def.stops)
+ if current_index < #stops then
+ for i = current_index + 1, #stops do
+ local mode = stops[i].mode
+ if mode ~= nil and (mode == MODE_FINAL or mode == MODE_FINAL_CONTINUE or (mode == MODE_FINAL_HIDDEN and allow_hidden_stops)) then
+ return i, stops[i]
+ end
+ end
+ end
+ return nil, nil
+end
+
+--[[
+ options = {
+ line_number = bool or nil, -- zahrnout do popisu číslo linky? nil => false
+ first_stop = bool or nil, -- zahrnout do popisu název výchozí zastávky? nil => false
+ last_stop = bool or nil, -- zahrnout do popisu název cílové zastávky? nil => true
+ last_stop_prefix = string or nil, -- text před název cílové zastávky; nil => "⇒ "
+ train_name = bool or nil, -- zahrnout do popisu jméno vlaku, je-li k dispozici; nil => false
+ train_name_prefix = string or nil, -- text před jméno vlaku; nil => "\n"
+ }
+]]
+function al.get_line_description(linevar_def, options)
+ local line, stn = al.linevar_decompose(linevar_def.name)
+ if line == nil then
+ return "???"
+ end
+ if options == nil then options = {} end
+ local s1, s2, s3, s4
+ if options.line_number then
+ s1 = "["..line.."] "
+ else
+ s1 = ""
+ end
+ if options.first_stop then
+ s2 = get_station_name(stn).." "
+ else
+ s2 = ""
+ end
+ if options.last_stop == nil or options.last_stop then
+ s3 = "???"
+ local terminus_index, terminus_data = al.get_terminus(linevar_def, 1, false)
+ if terminus_index ~= nil then
+ s3 = get_station_name(terminus_data.stn)
+ end
+ s3 = (options.last_stop_prefix or "⇒ ")..s3
+ else
+ s3 = ""
+ end
+ if options.train_name and linevar_def.train_name ~= nil then
+ s4 = (options.train_name_prefix or "\n")..linevar_def.train_name
+ else
+ s4 = ""
+ end
+ return s1..s2..s3..s4
+end
+
+function al.get_stop_description(stop_data, next_stop_data)
+ local s1, s2 = "", ""
+ if stop_data ~= nil then
+ local mode = stop_data.mode or MODE_NORMAL
+ if mode ~= MODE_DISABLED and mode ~= MODE_HIDDEN and mode ~= MODE_FINAL_HIDDEN then
+ s1 = get_station_name(stop_data.stn)
+ if mode == MODE_FINAL or mode == MODE_FINAL_CONTINUE then
+ s2 = "Koncová zastávka"
+ end
+ end
+ end
+ if next_stop_data ~= nil then
+ local mode = next_stop_data.mode or MODE_NORMAL
+ if mode ~= MODE_DISABLED and mode ~= MODE_HIDDEN and mode ~= MODE_FINAL_HIDDEN then
+ s2 = "Příští zastávka/stanice: "..get_station_name(next_stop_data.stn)
+ if mode == MODE_REQUEST_STOP then
+ s2 = s2.." (na znamení)"
+ end
+ end
+ end
+ if s1 ~= "" and s2 ~= "" then
+ return s1.."\n"..s2
+ else
+ return s1..s2
+ end
+end
+
+--[[
+ Není-li zpoždění k dispozici, vrací {text = ""}, jinak vrací:
+ {
+ has_delay = bool,
+ delay = int,
+ text = string,
+ }
+]]
+function al.get_delay_description(line_status, linevar_def)
+ assert(line_status)
+ if linevar_def == nil then
+ return {text = ""}
+ end
+ local expected_departure = line_status.linevar_dep + assert(linevar_def.stops[line_status.linevar_index]).dep
+ local real_departure = line_status.linevar_last_dep
+ local delay = real_departure - expected_departure
+ if delay < -1 or delay > 10 then
+ return {
+ has_delay = true,
+ delay = delay,
+ text = "zpoždění "..delay.." sekund",
+ }
+ else
+ return {
+ has_delay = false,
+ delay = delay,
+ text = "bez zpoždění",
+ }
+ end
+end
+
+-- Pokud zadaná varianta linky existuje, vrátí:
+-- linevar_def, linevar_station
+-- Jinak vrací:
+-- nil, nil
+-- Je-li linevar_station == nil, doplní se z linevar. Je-li linevar == nil, vrátí nil, nil.
+function al.try_get_linevar_def(linevar, linevar_station)
+ if linevar == nil then
+ -- print("DEBUG: try_get_linevar_def() => nil, because linevar == nil")
+ return nil, nil
+ end
+ if linevar_station == nil then
+ local line
+ line, linevar_station = al.linevar_decompose(linevar)
+ if line == nil then
+ return nil, nil
+ end
+ end
+ local t = advtrains.lines.stations[linevar_station]
+ if t ~= nil then
+ t = t.linevars
+ if t ~= nil then
+ return t[linevar], linevar_station
+ -- else
+ -- print("DEBUG: no linevars for linevar_station '"..linevar_station.."'")
+ end
+ -- else
+ -- print("DEBUG: no data for linevar_station '"..linevar_station.."'")
+ end
+ return nil, nil
+end
+
+--[[
+ Vrací:
+ a) nil, nil, pokud daná kombinace line/stn/rc nemá definovanou variantu linky
+ b) linevar, linevar_def, pokud má
+]]
+function al.try_get_linevar(line, stn, rc)
+ if line ~= nil and line ~= "" and stn ~= nil and stn ~= "" then
+ local linevar = line.."/"..stn.."/"..(rc or "")
+ local result = al.try_get_linevar_def(linevar, stn)
+ if result ~= nil then
+ -- print("DEBUG: linevar combination found: "..dump2({linevar = linevar, line = line, stn = stn, rc = rc or "", linevar_def = result}))
+ return linevar, result
+ end
+ end
+ -- print("DEBUG: linevar combination not found for "..(line or "").."/"..(stn or "").."/"..(rc or ""))
+ return nil, nil
+end
+
+--[[
+ Vrací 2 hodnoty:
+ - line_status = table -- tabulka train.line_status (může být prázdná)
+ - linevar_def = table or nil -- jde-li o linkový vlak, vrací definici linevar, jinak nil
+ Parametry:
+ - train = table (train)
+]]
+function al.get_line_status(train)
+ assert(train)
+ if train.line_status == nil then
+ train.line_status = {}
+ -- print("DEBUG: new empty line_status created for train "..train.id)
+ return train.line_status, nil
+ end
+ local ls = train.line_status
+ if ls.linevar == nil then
+ -- nelinkový vlak
+ return ls, nil
+ end
+ local rwtime = rwt.to_secs(rwt.get_time())
+ if rwtime - ls.linevar_dep >= 86400 then
+ core.log("warning", "Train "..train.id.." put out of linevar '"..ls.linevar.."', because it was riding more then 24 hours.")
+ al.cancel_linevar(train)
+ return ls, nil
+ end
+ local linevar_def = al.try_get_linevar_def(ls.linevar, ls.linevar_station)
+ if linevar_def == nil then
+ core.log("warning", "Train "..train.id.." was riding a non-existent (undefined) line '"..tostring(ls.linevar).."'!")
+ al.cancel_linevar(train)
+ elseif linevar_def.stops[ls.linevar_index] == nil then
+ core.log("warning", "Train "..train.id.." put out of linevar '"..ls.linevar.."', because its index "..ls.linevar_index.." became invalid.")
+ al.cancel_linevar(train)
+ linevar_def = nil
+ else
+ local train_line_prefix = (train.line or "").."/"
+ if train_line_prefix ~= ls.linevar:sub(1, train_line_prefix:len()) then
+ core.log("warning", "Train "..train.id.." put out of linevar '"..ls.linevar.."', because its line changed to '"..tostring(train.line).."'.")
+ al.cancel_linevar(train)
+ linevar_def = nil
+ else
+ -- [DEBUG:] TEMPORARY:
+ if linevar_def.line == nil then
+ linevar_def.line = assert(train.line)
+ end
+ end
+ end
+ return ls, linevar_def
+end
+
+function al.on_train_approach(pos, train_id, train, index, has_entered)
+ if has_entered then return end -- do not stop again!
+ if train.path_cn[index] ~= 1 then return end -- špatný směr
+ local pe = advtrains.encode_pos(pos)
+ local stdata = advtrains.lines.stops[pe]
+ if should_stop(pos, stdata, train) then
+ -- print("DEBUG: on_train_approach(): will stop at station '"..stdata.stn.."'")
+ advtrains.lzb_add_checkpoint(train, index, 2, nil)
+ if train.line_status.linevar == nil then
+ -- nelinkový vlak:
+ local stn = advtrains.lines.stations[stdata.stn]
+ local stnname = stn and stn.name or attrans("Unknown Station")
+ train.text_inside = attrans("Next Stop:") .. "\n"..stnname
+ end
+ advtrains.interlocking.ars_set_disable(train, true)
+ end
+end
+
+function al.on_train_enter(pos, train_id, train, index)
+ if train.path_cn[index] ~= 1 then return end -- špatný směr
+ local pe = advtrains.encode_pos(pos)
+ local stdata = advtrains.lines.stops[pe]
+ local stn = stdata.stn or ""
+ local rwtime = assert(rwt.to_secs(rwt.get_time()))
+ local ls, linevar_def = al.get_line_status(train)
+ local next_index
+ if stn ~= "" then
+ if linevar_def ~= nil then
+ next_index = find_next_index(ls, linevar_def, stn)
+ end
+ ls.last_enter = {stn = stn, encpos = pe, rwtime = rwtime}
+ debug_print("Vlak "..train_id.." zaznamenán: "..stn.." "..core.pos_to_string(pos).." @ "..rwtime)
+ end
+ if not should_stop(pos, stdata, train) then
+ -- průjezd
+ debug_print("Vlak "..train_id.." projel zastávkou "..stn)
+ return
+ end
+
+ -- naplánovat čas odjezdu
+ local wait = tonumber(stdata.wait) or 0
+ local interval = stdata.interval
+ local last_dep = stdata.last_dep -- posl. odjezd z této zastávkové koleje
+
+ if interval ~= nil and last_dep ~= nil then
+ if last_dep > rwtime then
+ last_dep = rwtime
+ end
+ local ioffset = stdata.ioffset or 0
+ local normal_dep = rwtime + wait
+ local next_dep = last_dep + (interval - (last_dep + (interval - ioffset)) % interval)
+ if normal_dep < next_dep then
+ core.log("action", "[INFO] The train "..train_id.." will wait for "..(next_dep - normal_dep).." additional seconds due to interval at "..core.pos_to_string(pos)..".")
+ wait = wait + (next_dep - normal_dep)
+ -- else -- will wait normal time
+ end
+ end
+ local planned_departure = rwtime + wait
+ debug_print("Vlak "..train_id.." zastavil na "..stn.." a odjede za "..wait.." sekund ("..planned_departure..").")
+ -- print("DEBUG: planned departure: "..planned_departure.." = "..rwtime.." + "..wait)
+ stdata.last_dep = planned_departure -- naplánovaný čas odjezdu
+ ls.standing_at = pe
+ ls.stop_request = nil
+ -- print("DEBUG: standing ls = "..dump2(ls))
+
+ local can_start_line
+ local had_linevar = linevar_def ~= nil
+ if linevar_def == nil then
+ -- nelinkový vlak
+ can_start_line = true
+ -- print("DEBUG: train "..train_id.." is non-line train, can start a new line")
+ elseif next_index ~= nil then
+ -- linkový vlak zastavil na své řádné zastávce
+ assert(stn ~= "") -- dopravna musí mít kód
+ local stop_def = assert(linevar_def.stops[next_index])
+ debug_print("Vlak "..train_id.." je linkový vlak ("..ls.linevar..") a zastavil na své pravidelné zastávce "..stn.." (index = "..next_index..")")
+ -- print("DEBUG: train "..train_id.." stopped at regular stop '"..stn.."': "..dump2({stop_def = stop_def}))
+
+ -- zaznamenat přeskočené zastávky:
+ if next_index ~= ls.linevar_index + 1 then
+ local skipped_stops = {}
+ for i = ls.linevar_index + 1, next_index - 1 do
+ local mode = linevar_def.stops[i].mode or MODE_NORMAL
+ if mode ~= MODE_DISABLED then
+ table.insert(skipped_stops, linevar_def.stops[i].stn)
+ end
+ end
+ if #skipped_stops > 0 then
+ core.log("warning", "Train "..train_id.." of line '"..linevar_def.name.."' skipped "..#skipped_stops.." stops: "..table.concat(skipped_stops, ", "))
+ end
+ end
+
+ local stop_smode = simple_modes[stop_def.mode or 0]
+ if stop_smode == MODE_NORMAL then
+ -- mezilehlá zastávka
+ can_start_line = false
+ ls.linevar_index = next_index
+ ls.linevar_last_dep = planned_departure
+ ls.linevar_last_stn = stn
+ -- print("DEBUG: "..dump2({line_status = ls}))
+ debug_print("Jde o mezilehlou zastávku.")
+ local next_stop_index, next_stop_data = al.get_next_stop(linevar_def, next_index)
+ train.text_inside = al.get_stop_description(linevar_def.stops[next_index], linevar_def.stops[next_stop_index or 0])
+ else
+ -- koncová zastávka
+ can_start_line = stop_def.mode == MODE_FINAL_CONTINUE
+ debug_print("Vlak "..train_id.." skončil jízdu na lince "..ls.linevar..", může pokračovat na jinou linku: "..(can_start_line and "ihned" or "na příští zastávce"))
+ train.text_inside = al.get_stop_description(linevar_def.stops[next_index])
+ al.cancel_linevar(train)
+ end
+ else
+ -- linkový vlak zastavil na neznámé zastávce (nemělo by nastat)
+ core.log("warning", "Train "..train_id.." of linevar '"..ls.linevar.."' stopped at unknown station '"..stn.."' at "..core.pos_to_string(pos).."!")
+ debug_print("Vlak "..train_id.." je linkový vlak ("..ls.linevar.."), ale zastavil na sobě neznámé zastávce "..stn..", což by se nemělo stát.")
+ can_start_line = false
+ end
+ -- print("DEBUG: "..dump2({can_start_line = can_start_line}))
+
+ -- ATC příkaz
+ local atc_command = "B0 W O"..stdata.doors..(stdata.kick and "K" or "").." D"..wait..
+ (stdata.keepopen and " " or " OC ")..(stdata.reverse and "R" or "").." D"..(stdata.ddelay or 1)..
+ " A1 S" ..(stdata.speed or "M")
+ advtrains.atc.train_set_command(train, atc_command, true)
+
+ -- provést změny vlaku
+ local new_line = stdata.line or ""
+ local new_routingcode = stdata.routingcode or ""
+ update_value(train, "line", new_line)
+ update_value(train, "routingcode", new_routingcode)
+
+ -- začít novou linku?
+ if can_start_line and line_start(train, stn, planned_departure) then
+ debug_print("Vlak "..train_id.." zahájil jízdu na nové lince ("..ls.linevar..") ze stanice "..stn..".")
+ core.log("action", "Train "..train_id.." started a route with linevar '"..ls.linevar.."' at station '"..stn.."'.")
+ train.text_inside = get_station_name(stn)
+ assert(ls.linevar)
+ linevar_def = assert(al.try_get_linevar_def(ls.linevar))
+ local next_stop_index, next_stop_data = al.get_next_stop(linevar_def, 1)
+ if next_stop_index ~= nil then
+ train.text_inside = train.text_inside.."\nPříští zastávka/stanice: "..get_station_name(next_stop_data.stn)
+ if next_stop_data.mode ~= nil and next_stop_data.mode == MODE_REQUEST_STOP then
+ train.text_inside = train.text_inside.." (na znamení)"
+ end
+ end
+ elseif not had_linevar and ls.linevar == nil then
+ -- vlak, který nebyl a stále není linkový:
+ train.text_inside = get_station_name(stn)
+ -- core.after(wait, function() train.text_inside = "" end)
+ end
+ -- print("DEBUG: the train will wait for "..wait.." seconds")
+end
+
+function al.on_train_leave(pos, train_id, train, index)
+ local pe = advtrains.encode_pos(pos)
+ local stdata = advtrains.lines.stops[pe]
+ if stdata == nil then
+ return -- neplatná zastávka
+ end
+ local stn = stdata.stn or ""
+ local ls, linevar_def = al.get_line_status(train)
+ local rwtime = assert(rwt.to_secs(rwt.get_time()))
+
+ if stn ~= "" then
+ ls.last_leave = {stn = stn, encpos = pe, rwtime = rwtime}
+ debug_print("Vlak "..train_id.." zaznamenán na odjezdu: "..stn.." "..core.pos_to_string(pos).." @ "..rwtime)
+ end
+
+ -- print("DEBUG: *on_train_leave("..train_id..") from "..(stdata and stdata.stn or "nil"))
+ if ls.standing_at == pe then
+ -- vlak stál v této dopravně
+ ls.standing_at = nil
+ ls.stop_request = nil
+ if stn ~= "" then
+ -- print("DEBUG: on_train_leave from non-anonymous stop")
+ debug_print("Vlak "..train_id.." odjel ze zastávky "..stn)
+ if ls.linevar_last_dep ~= nil and ls.linevar_last_dep > rwtime then
+ debug_print("Vlak "..train_id.." předčasně odjel z dopravny "..stn.." (zbývalo "..(ls.linevar_last_dep - rwtime).." sekund)")
+ ls.linevar_last_dep = rwtime -- předčasný odjezd
+ if ls.linevar_index == 1 then
+ ls.linevar_dep = rwtime
+ end
+ end
+ else
+ -- print("DEBUG: on_train_leave from anonymous stop")
+ debug_print("Vlak "..train_id.." odjel z anonymní zastávky "..core.pos_to_string(pos)..".")
+ end
+ train.text_inside = ""
+ if linevar_def ~= nil then
+ -- linkový vlak:
+ local next_stop_index, next_stop_data = al.get_next_stop(linevar_def, ls.linevar_index)
+ if next_stop_index ~= nil then
+ train.text_inside = al.get_stop_description(nil, next_stop_data)
+ end
+ end
+ -- else
+ --[[ průjezd?
+ local stn = (stdata and stdata.stn) or ""
+ if stn ~= "" then
+ debug_print("DEBUG: Vlak "..train_id.." projel (odjezd) zastávkou "..stn..".")
+ else
+ debug_print("DEBUG: Vlak "..train_id.." projel (odjezd) anonymní zastávkou "..core.pos_to_string(pos)..".")
+ end
+ ]]
+ end
+end
+
+function al.linevar_decompose(linevar)
+ if linevar == nil then
+ return nil, nil, nil
+ end
+ local parts = string.split(linevar, "/", true, 3)
+ return parts[1], parts[2] or "", parts[3] or ""
+end
+
+--[[ DEBUG:
+local debug_print = {}
+function debug_print.print()
+ print(".")
+ core.after(1, debug_print.print)
+end
+debug_print.print()
+]]
+
+--[[
+function al.rwt_to_cas()
+ return
+end
+]]
+
+local function get_last_pos(line_status)
+ assert(line_status)
+ local last_enter, last_leave, standing_at = line_status.last_enter, line_status.last_leave, line_status.standing_at
+ local last_pos, last_pos_type
+ if last_enter ~= nil then
+ last_pos, last_pos_type = last_enter, "enter"
+ if standing_at ~= nil and standing_at == last_enter.encpos then
+ last_pos_type = "standing"
+ elseif last_leave ~= nil and last_leave.rwtime > last_enter.rwtime then
+ last_pos, last_pos_type = last_leave, "leave"
+ end
+ elseif last_leave ~= nil then
+ last_pos, last_pos_type = last_leave, "leave"
+ else
+ result.type = "none"
+ return {type = "none"}
+ end
+ local result = {type = last_pos_type, last_pos = last_pos}
+ if last_enter ~= nil then
+ result.last_enter = last_enter
+ end
+ if last_leave ~= nil then
+ result.last_leave = last_leave
+ end
+ return result
+end
+
+local function get_train_position(line_status, linevar_def, rwtime)
+ if line_status ~= nil then
+ local last_pos_info = get_last_pos(line_status)
+ print("DEBUG: last_pos_info = "..dump2({last_pos_info}))
+ local last_pos = last_pos_info.last_pos
+ if last_pos ~= nil then
+ local result = "„"..get_station_name(last_pos.stn).."“"
+ local delay_info = al.get_delay_description(line_status, linevar_def)
+ if last_pos_info.type ~= "standing" then
+ result = result.." (před "..(rwtime - last_pos.rwtime).." sekundami)"
+ end
+ if delay_info.has_delay ~= nil then
+ result = result.." ("..delay_info.text..")"
+ end
+ return result
+ end
+ end
+ return "???"
+end
+
+-- příkaz /vlaky
+local def = {
+ params = "[linka]",
+ description = "Vypíše všechny linkové vlaky na zadané lince (resp. na všech linkách)",
+ privs = {},
+ func = function(player_name, param)
+ local result = {}
+ if not param:match("/") then
+ local rwtime = rwt.to_secs(rwt.get_time())
+ for train_id, train in pairs(advtrains.trains) do
+ local ls, linevar_def = al.get_line_status(train)
+ if linevar_def ~= nil and (param == "" or ls.linevar:sub(1, #param + 1) == param.."/") then
+ local direction_index, direction_stop = al.get_terminus(linevar_def, ls.linevar_index, false)
+ local direction = "???"
+ if direction_index ~= nil then
+ direction = get_station_name(direction_stop.stn)
+ end
+ local s = "("..train_id..") ["..linevar_def.line.."] směr „"..direction.."“, poloha: "..get_train_position(ls, linevar_def, rwtime)
+ table.insert(result, s)
+ end
+ end
+ end
+ if #result == 0 then
+ return false, "Nenalezen žádný odpovídající vlak."
+ end
+ return true, "Nalezeno "..#result.." vlaků:\n- "..table.concat(result, "\n- ")
+ end,
+}
+core.register_chatcommand("vlaky", def)
diff --git a/advtrains_line_automation/stoprail.lua b/advtrains_line_automation/stoprail.lua
index abc3488..c63e5a9 100644
--- a/advtrains_line_automation/stoprail.lua
+++ b/advtrains_line_automation/stoprail.lua
@@ -1,6 +1,8 @@
-- stoprail.lua
-- adds "stop rail". Recognized by lzb. (part of behavior is implemented there)
+local rwt = assert(advtrains.lines.rwt)
+
local function to_int(n)
--- Disallow floating-point numbers
local k = tonumber(n)
@@ -171,7 +173,6 @@ minetest.register_on_player_receive_fields(function(player, formname, fields)
if fields.ioffsetnow and fields.interval ~= "" and fields.interval ~= "0" then
local interval = to_int(fields.interval)
if 0 < interval and interval <= 3600 then
- local rwt = assert(advtrains.lines.rwt)
local rwtime = rwt.to_secs(rwt.get_time())
set_offset = rwtime % interval
end
@@ -283,13 +284,6 @@ minetest.register_on_player_receive_fields(function(player, formname, fields)
end)
-local function should_stop(stdata, train)
- local n_trainparts = #assert(train.trainparts)
- local ars = stdata.ars
- return ars and (stdata.minparts or 0) <= n_trainparts and n_trainparts <= (stdata.maxparts or 128) and
- (ars.default or advtrains.interlocking.ars_check_rule_match(ars, train))
-end
-
local adefunc = function(def, preset, suffix, rotation)
return {
after_place_node=function(pos)
@@ -313,85 +307,9 @@ local adefunc = function(def, preset, suffix, rotation)
show_stoprailform(pos, player)
end,
advtrains = {
- on_train_approach = function(pos,train_id, train, index, has_entered)
- if has_entered then return end -- do not stop again!
- if train.path_cn[index] == 1 then
- local pe = advtrains.encode_pos(pos)
- local stdata = advtrains.lines.stops[pe]
- if stdata and stdata.stn then
- --[[TODO REMOVE AFTER SOME TIME (only migration)
- if not stdata.ars then
- stdata.ars = {default=true}
- end
- ]]
- if should_stop(stdata, train) then
- advtrains.lzb_add_checkpoint(train, index, 2, nil)
- local stn = advtrains.lines.stations[stdata.stn]
- local stnname = stn and stn.name or attrans("Unknown Station")
- train.text_inside = attrans("Next Stop:") .. "\n"..stnname
- advtrains.interlocking.ars_set_disable(train, true)
- end
- end
- end
- end,
- on_train_enter = function(pos, train_id, train, index)
- if train.path_cn[index] == 1 then
- local pe = advtrains.encode_pos(pos)
- local stdata = advtrains.lines.stops[pe]
- if not stdata then
- return
- end
-
- if should_stop(stdata, train) then
- local stn = advtrains.lines.stations[stdata.stn]
- local stnname = stn and stn.name or attrans("Unknown Station")
- local line = stdata.line or ""
- local routingcode = stdata.routingcode or ""
- local wait = tonumber(stdata.wait) or 0
- local interval = stdata.interval
- local ioffset = stdata.ioffset or 0
- local lastdep = stdata.lastdep
- local rwt = advtrains.lines.rwt
- local rwtime = assert(tonumber(rwt.to_secs(rwt.get_time())))
-
- -- Interval
- if lastdep ~= nil and interval ~= nil then
- if lastdep > rwtime then
- lastdep = rwtime
- end
- local normaldep = rwtime + wait
- local nextdep = lastdep + (interval - (lastdep + (interval - ioffset)) % interval)
- if normaldep < nextdep then
- minetest.log("action", "[INFO] The train "..train_id.." will wait for "..(nextdep - normaldep).." additional seconds due to interval at "..core.pos_to_string(pos)..".")
- wait = wait + (nextdep - normaldep)
- -- else -- will wait normal time
- end
- end
- stdata.lastdep = rwtime + wait
-
- -- Send ATC command and set text
- local command = "B0 W O"..stdata.doors..(stdata.kick and "K" or "").." D"..wait..
- (stdata.keepopen and " " or " OC ")..(stdata.reverse and "R" or "").." D"..(stdata.ddelay or 1)..
- " A1 S" ..(stdata.speed or "M")
- -- print("DEBUG: setting command for train "..train_id.." at rwtime "..rwtime..": "..command)
- advtrains.atc.train_set_command(train, command, true)
- train.text_inside = stnname
- if line == "-" then
- train.line = nil
- elseif line ~= "" then
- train.line = line
- end
- if routingcode == "-" then
- train.routingcode = nil
- elseif routingcode ~= "" then
- train.routingcode = routingcode
- end
- if tonumber(wait) then
- minetest.after(tonumber(wait), function() train.text_inside = "" end)
- end
- end
- end
- end
+ on_train_approach = advtrains.lines.on_train_approach,
+ on_train_enter = advtrains.lines.on_train_enter,
+ on_train_leave = advtrains.lines.on_train_leave,
},
}
end
diff --git a/advtrains_line_automation/structs.md b/advtrains_line_automation/structs.md
new file mode 100644
index 0000000..a18d8cf
--- /dev/null
+++ b/advtrains_line_automation/structs.md
@@ -0,0 +1,96 @@
+Datové struktury:
+
+train = {
+ line_status = {
+ -- záznam o posledním zastavení/průjezdu vlaku neanonymní zastávkovou kolejí
+ -- (používá se jako údaj o poloze vlaku)
+ last_enter = {
+ stn = string, -- kód dopravny
+ encpos = string, -- zakódovaná pozice koleje, kde došlo ke kontaktu
+ rwtime = int, -- železniční čas
+ } or nil,
+
+ -- záznam o posledním odjezdu/průjezdu vlaku neanonymní zastávkovou kolejí
+ -- (používá se jako údaj o poloze vlaku)
+ last_leave = {
+ stn = string, -- kód dopravny
+ encpos = string, -- zakódovaná pozice koleje, kde došlo ke kontaktu
+ rwtime = int, -- železniční čas
+ } or nil,
+
+ -- pokud vlak právě stojí na zastávkové koleji, obsahuje její zakódovanou pozici;
+ -- při odjezdu se vynuluje
+ standing_at = string or nil,
+
+ -- nastaví se na 1 v případě, že "bylo dáno znamení", aby vlak zastavil
+ stop_request = 1 or nil,
+
+ -- Následující pole jsou vyplněna jen u linkových vlaků:
+ -- ===========================
+ -- varianta linky LINE/STCODE/RC
+ linevar = string,
+
+ -- prostřední díl z 'linevar' (kód stanice, kde jsou uložena data varianty linky)
+ linevar_station = string,
+
+ -- skutečný železniční čas odjezdu z *výchozí* zastávky spoje
+ linevar_dep = int,
+
+ -- index zastávky spoje (do pole 'stops'), kde vlak naposledy zastavil
+ linevar_index = int,
+
+ -- skutečný železniční čas odjezdu z poslední zastávky spoje, kde vlak zastavil
+ linevar_last_dep = int,
+
+ -- kód zastávky spoje, kde vlak naposledy zastavil
+ linevar_last_stn = string,
+ }
+}
+
+station = {
+ linevars = {
+ [linevar] = {
+ -- linevar (LINE/STCODE/RC)
+ name = string,
+
+ -- LINE (první část názvu)
+ line = string,
+
+ -- přihlašovací jméno postavy, která linku spravuje
+ owner = string,
+
+ -- jméno vlaku pro zobrazení (volitelné)
+ train_name = string or nil,
+
+ -- je-li true, nové vlaky nemohou dostat tuto variantu přidělenu
+ disabled = bool or nil,
+
+ -- seznam zastávek na lince, seřazený podle 'dep':
+ stops = {
+ {
+ -- kód dopravny, kde má vlak zastavit
+ stn = string,
+
+ -- plánovaný čas odjezdu, relativně vůči odjezdu z výchozí zastávky (v sekundách)
+ dep = int,
+
+ -- režim zastávky (podle konstant ve zdrojovém kódu)
+ -- nil odpovídá 0 (normální zastavení)
+ mode = int or nil,
+
+ -- je-li vyplněna, vlak zastaví jen na koleji na uvedené pozici
+ pos = "X,Y,Z" or nil,
+
+ -- orientační údaj pro cestující, na které koleji má vlak zastavit
+ track = string or nil,
+ }...
+ }
+ }
+ }
+}
+
+stop = {
+ -- žel. čas posledního odjezdu jakéhokoliv zastavivšího vlaku z této zastávkové koleje;
+ -- používá se v kombinaci s intervalem
+ last_dep = int or nil,
+}