aboutsummaryrefslogtreecommitdiff
path: root/advtrains_line_automation
diff options
context:
space:
mode:
Diffstat (limited to 'advtrains_line_automation')
-rw-r--r--advtrains_line_automation/init.lua7
-rw-r--r--advtrains_line_automation/line_editor.lua856
-rw-r--r--advtrains_line_automation/line_functions.lua1377
-rw-r--r--advtrains_line_automation/mod.conf4
-rw-r--r--advtrains_line_automation/models/advtrains_tuber.obj150
-rw-r--r--advtrains_line_automation/models/license.txt5
-rw-r--r--advtrains_line_automation/railwaytime.lua15
-rw-r--r--advtrains_line_automation/station_announcement.lua1579
-rw-r--r--advtrains_line_automation/station_editor.lua1054
-rw-r--r--advtrains_line_automation/stoprail.lua299
-rw-r--r--advtrains_line_automation/structs.md190
-rw-r--r--advtrains_line_automation/textures/advtrains_dtrack_shared_stop.pngbin3306 -> 7559 bytes
-rw-r--r--advtrains_line_automation/textures/advtrains_line_automation_jrad.pngbin0 -> 2516 bytes
-rw-r--r--advtrains_line_automation/time_table.lua93
14 files changed, 5539 insertions, 90 deletions
diff --git a/advtrains_line_automation/init.lua b/advtrains_line_automation/init.lua
index e7d0ea6..814dc27 100644
--- a/advtrains_line_automation/init.lua
+++ b/advtrains_line_automation/init.lua
@@ -19,10 +19,15 @@ 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.."stoprail.lua")
+dofile(modpath.."station_editor.lua")
+dofile(modpath.."stoprail.lua")
+dofile(modpath.."station_announcement.lua")
+dofile(modpath.."time_table.lua")
function advtrains.lines.load(data)
if data then
diff --git a/advtrains_line_automation/line_editor.lua b/advtrains_line_automation/line_editor.lua
new file mode 100644
index 0000000..33686d1
--- /dev/null
+++ b/advtrains_line_automation/line_editor.lua
@@ -0,0 +1,856 @@
+local def
+local F = minetest.formspec_escape
+local ifthenelse = function(cond, a, b) if cond then return a else return b end end
+
+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_last_passages = assert(advtrains.lines.get_last_passages)
+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 show_last_passages_formspec -- forward declaration
+
+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 same_stops(stops1, stops2)
+ assert(stops1)
+ assert(stops2)
+ if #stops1 ~= #stops2 then
+ return false
+ end
+ for i, stop in ipairs(stops1) do
+ if stop.stn ~= stops2[i].stn then
+ return false
+ end
+ end
+ return true
+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
+ local old_linevar_def = linevars[linevar]
+ if old_linevar_def == nil then
+ return false, "Nemohu nahradit, varianta linky '"..linevar.."' dosud neexistuje!"
+ end
+ linevars[linevar] = linevar_def
+ local restart_count = 0
+ if not same_stops(old_linevar_def.stops, linevar_def.stops) then
+ -- změnily se zastávky, musíme restartovat vlaky:
+ 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
+ restart_count = restart_count + 1
+ end
+ end
+ end
+ if restart_count > 0 then
+ return true, restart_count.." vlak/ů restartován/o kvůli změnám"
+ else
+ return true, nil
+ end
+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_raw = custom_state.selection_index
+ local selection_index = selection_index_raw or 1
+ 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_raw ~= 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 == 1 or
+ pinfo.player_name == custom_state.linevars[selection_index - 1].owner
+
+ if selection_index > 1 and 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.."]"..
+ "field[13.5,7;3,0.75;continues;pokračování:;"..F(custom_state.continues).."]")
+
+ if custom_state.message ~= "" then
+ table.insert(formspec, "label[0.5,8.25;"..F(custom_state.message).."]")
+ end
+ if selection_index > 1 then
+ table.insert(formspec, "button[5,15;4.5,0.75;last_passages;poslední jízdy...]"..
+ "tooltip[last_passages;Zobrazí přehled časů několika posledních jízd na dané variantě linky.]")
+ end
+ 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]")
+ 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;stání]"..
+ "label[3.5,0.25;kód dop.]"..
+ "label[6.25,0.25;režim zastávky]"..
+ "label[11,0.25;kolej]"..
+ "label[12.5,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;1.25,0.75;s01_wait;;"..F(custom_state.stops[1].wait).."]"..
+ "field[3,0;2.5,0.75;s01_stn;;"..F(custom_state.stops[1].stn).."]"..
+ "dropdown[5.75,0;4.5,0.75;s01_mode;výchozí,skrytá (výchozí);"..custom_state.stops[1].mode..";true]"..
+ "field[10.5,0;1.25,0.75;s01_track;;"..F(custom_state.stops[1].track).."]"..
+ "field[12,0;3,0.75;s01_pos;;"..F(custom_state.stops[1].pos).."]"..
+ "label[15.25,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..";1.25,0.75;s"..n.."_wait;;"..F(stop.wait).."]"..
+ "field[3,"..y..";2.5,0.75;s"..n.."_stn;;"..F(stop.stn).."]"..
+ "dropdown[5.75,"..y..";4.5,0.75;s"..n..
+ "_mode;normální,na znamení (experimentální),skrytá (mezilehlá),vypnutá,koncová,koncová skrytá,"..
+ "koncová (pokračuje);"..stop.mode..";true]"..
+ "field[10.5,"..y..";1.25,0.75;s"..n.."_track;;"..F(stop.track).."]"..
+ "field[12,"..y..";3,0.75;s"..n.."_pos;;"..F(stop.pos).."]"..
+ "label[15.25,"..y2..";"..F(stop.label).."]")
+ end
+
+ table.insert(formspec,
+ "scroll_container_end[]"..
+ "tooltip[0,0;1.5,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[1.5,0;1.5,1;Stání: očekáváná doba stání před odjezdem. Pro koncové zastávky očekávaná doba stání po příjezdu.]"..
+ "tooltip[3.5,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[6.25,0;4.75,1;Režim zastávky: výchozí/normální - vždy zastaví\\;\n"..
+ "na znamení: zastaví na znamení (zatím experimentální)\\;\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[10.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[12.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)),
+ wait = tostring(stop.wait or 10),
+ 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", ""),
+ wait = "10",
+ 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")
+ custom_state.continues = current_linevar.continue_line or ""
+ if custom_state.continues ~= "" then
+ custom_state.continues = custom_state.continues.."/"..(current_linevar.continue_rc or "")
+ end
+ else
+ custom_state.line = ""
+ custom_state.rc = ""
+ custom_state.train_name = ""
+ custom_state.owner = custom_state.player_name
+ custom_state.disable_linevar = "false"
+ custom_state.continues = ""
+ end
+ custom_state.owner = ch_core.prihlasovaci_na_zobrazovaci(custom_state.owner)
+ custom_state.compiled_linevar = nil
+ custom_state.evl_scroll = 0
+ custom_state.message = ""
+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é!"
+ elseif custom_state.continues:len() - custom_state.continues:gsub("/", ""):len() > 1 then
+ return false, "Pole 'pokračování' smí obsahovat nejvýše jedno lomítko!"
+ 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
+ local new_wait = tonumber(stop.wait)
+ if new_wait ~= nil and new_wait == math.floor(new_wait) and new_wait >= 0 and new_wait <= 3600 then
+ new_stop.wait = new_wait
+ 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)
+
+ local index_vychozi, index_cil
+ for i, stop in ipairs(stops) do
+ local mode = stop.mode or MODE_NORMAL
+ if mode ~= MODE_DISABLED and mode ~= MODE_HIDDEN and mode ~= MODE_FINAL_HIDDEN then
+ if index_vychozi == nil then
+ index_vychozi = i
+ end
+ index_cil = i
+ end
+ if mode == MODE_FINAL or mode == MODE_FINAL_CONTINUE or mode == MODE_FINAL_HIDDEN then
+ break
+ end
+ end
+
+ custom_state.compiled_linevar = {
+ name = line.."/"..stops[1].stn.."/"..rc,
+ owner = ch_core.jmeno_na_prihlasovaci(owner),
+ stops = stops,
+ continue_line = "",
+ continue_rc = "",
+ index_vychozi = index_vychozi,
+ index_cil = index_cil,
+ }
+ 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
+ local continues_split = custom_state.continues:find("/")
+ if continues_split == nil then
+ custom_state.compiled_linevar.continue_line = custom_state.continues
+ else
+ custom_state.compiled_linevar.continue_line = custom_state.continues:sub(1, continues_split - 1)
+ custom_state.compiled_linevar.continue_rc = custom_state.continues:sub(continues_split + 1, -1)
+ 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", "continues"}) 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", "wait", "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!
+ custom_state.message = color_green.."Úspěšně ověřeno. Varianta linky může být uložena."
+ else
+ custom_state.message = color_red.."Ověření selhalo: "..(errmsg or "Neznámý důvod")
+ end
+ update_formspec = true
+ else
+ -- pokusit se uložit...
+ custom_state.message = ""
+
+ 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
+ core.log("action", "Will add a new linevar '"..new_linevar.."'")
+ success, errmsg = add_linevar(new_linevar_station, new_linevar_def)
+ else
+ -- replace
+ core.log("action", "Will replace an existing linevar '"..new_linevar.."'")
+ 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
+ core.log("action", "Will delete selected linevar '"..selected_linevar.."' and add new linevar '"..new_linevar.."'")
+ 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
+ core.log("action", "Will add delete selected linevar '"..selected_linevar.."' and replace existing linevar '"..new_linevar.."'")
+ 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
+ core.log("action", "Will replace existing linevar '"..new_linevar.."'")
+ 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
+ custom_state.message = color_green.."Varianta linky '"..new_linevar.."' úspěšně uložena."
+ custom_state_refresh_linevars(custom_state, new_linevar)
+ else
+ custom_state.message = color_red.."Ukládání selhalo: "..(errmsg or "Neznámá chyba.")
+ end
+ update_formspec = true
+ 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 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
+ custom_state.message = "Varianta linky '"..selected_linevar.."' úspěšně smazána."
+ custom_state_refresh_linevars(custom_state)
+ custom_state_set_selection_index(custom_state, 1)
+ else
+ custom_state.message = "Mazání selhalo: "..(errmsg or "Neznámá chyba.")
+ end
+ update_formspec = true
+ end
+ elseif fields.last_passages then
+ local selected_linevar_def = try_get_linevar_def(custom_state.linevars[(custom_state.selection_index or 1) - 1].name)
+ if selected_linevar_def ~= nil then
+ assert(selected_linevar_def.name)
+ show_last_passages_formspec(player, selected_linevar_def, assert(selected_linevar_def.name))
+ return
+ end
+ end
+
+ if update_formspec then
+ return get_formspec(custom_state)
+ end
+end
+
+local function show_editor_formspec(player, linevar_to_select)
+ if player == nil then return false end
+ local custom_state = {
+ player_name = assert(player:get_player_name()),
+ evl_scroll = 0,
+ message = "",
+ continues = "",
+ }
+ if not custom_state_refresh_linevars(custom_state, linevar_to_select) then
+ custom_state_set_selection_index(custom_state, 1)
+ end
+ ch_core.show_formspec(player, "advtrains_line_automation:editor_linek", get_formspec(custom_state), formspec_callback, custom_state, {})
+end
+
+local function lp_formspec_callback(custom_state, player, formname, fields)
+ if fields.back then
+ show_editor_formspec(player, custom_state.selected_linevar)
+ end
+end
+
+show_last_passages_formspec = function(player, linevar_def, selected_linevar)
+ local formspec = {
+ "formspec_version[6]"..
+ "size[20,10]"..
+ "label[0.5,0.6;Poslední jízdy na variantě linky ",
+ F(assert(linevar_def.name)),
+ "]"..
+ "tablecolumns[text;text;text,width=5;text,width=5;text,width=5;text,width=5;text,width=5;text,width=5;text,width=5;text,width=5;text,width=5;text,width=5]",
+ "table[0.5,1.25;19,8;jizdy;KÓD,DOPRAVNA,1.j.,2.j.,3.j.,4.j.,5.j.,6.j.,7.j.,8.j.,9.j.,10.j."
+ }
+ local passages, stops = get_last_passages(linevar_def)
+ local max_time = {}
+ if passages ~= nil then
+ for j = 1, 10 do
+ max_time[j] = 0
+ if passages[j] == nil then
+ passages[j] = {}
+ end
+ end
+ -- stání na výchozí zastávce:
+ table.insert(formspec, ",,STÁNÍ NA V.Z.:")
+ for j = 1, 10 do
+ local wait = passages[j].wait
+ if wait ~= nil then
+ table.insert(formspec, ","..wait.." s")
+ else
+ table.insert(formspec, ",-")
+ end
+ end
+ -- odjezd z výchozí zastávky:
+ table.insert(formspec, ","..F(stops[1][1])..","..F(stops[1][2]).." (odj.)")
+ for j = 1, 10 do
+ local time = passages[j][1]
+ if time ~= nil then
+ table.insert(formspec, ",("..time..")")
+ if max_time[j] < time then
+ max_time[j] = time
+ end
+ else
+ table.insert(formspec, ",-")
+ end
+ end
+ -- odjezdy z ostatních zasŧávek:
+ for i = 2, #stops do -- i = index zastávky
+ table.insert(formspec, ","..F(stops[i][1])..","..F(stops[i][2]))
+ for j = 1, 10 do -- j = index jízdy
+ local dep_vych = passages[j][1]
+ local time = passages[j][i]
+ if time ~= nil and dep_vych ~= nil then
+ table.insert(formspec, ","..(time - dep_vych))
+ if max_time[j] < time then
+ max_time[j] = time
+ end
+ else
+ table.insert(formspec, ",-")
+ end
+ end
+ end
+ table.insert(formspec, ",,DOBA JÍZDY:")
+ for i = 1, 10 do
+ if max_time[i] ~= 0 then
+ table.insert(formspec, ",_"..(max_time[i] - passages[i][1]).."_")
+ else
+ table.insert(formspec, ",-")
+ end
+ end
+ end
+ table.insert(formspec, ";]"..
+ "button[17.75,0.3;1.75,0.75;back;zpět]"..
+ "tooltip[jizdy;Časové údaje jsou v sekundách železničního času.]")
+ formspec = table.concat(formspec)
+ local custom_state = {
+ player_name = player:get_player_name(),
+ selected_linevar = selected_linevar,
+ }
+ ch_core.show_formspec(player, "advtrains_line_automation:posledni_jizdy", formspec, lp_formspec_callback, custom_state, {})
+end
+
+def = {
+ -- params = "",
+ description = "Otevře editor variant linek",
+ privs = {railway_operator = true},
+ func = function(player_name, param) show_editor_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..ee8f238
--- /dev/null
+++ b/advtrains_line_automation/line_functions.lua
@@ -0,0 +1,1377 @@
+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 current_passages = {--[[
+ [train_id] = {
+ [1] = rwtime,
+ ...,
+ [n] = rwtime (časy *odjezdu*, kromě koncových zastávek, kde jde o čas příjezdu)
+ wait = int or nil (původně naplánovaná doba čekání na výchozí zastávce)
+ }
+]]}
+
+local last_passages = {--[[
+ [linevar] = {
+ [1..10] = {[1] = rwtime, ..., wait} -- jízdy seřazeny od nejstarší (1) po nejnovější (až 10) podle odjezdu z výchozí zastávky
+ }
+]]}
+
+local diakritika_na_velka = {
+ ["á"] = "Á", ["ä"] = "Ä", ["č"] = "Č", ["ď"] = "Ď", ["é"] = "É", ["ě"] = "Ě", ["í"] = "Í", ["ĺ"] = "Ĺ", ["ľ"] = "Ľ",
+ ["ň"] = "Ň", ["ó"] = "Ó", ["ô"] = "Ô", ["ŕ"] = "Ŕ", ["ř"] = "Ř", ["š"] = "Š", ["ť"] = "Ť", ["ú"] = "Ú", ["ů"] = "Ů",
+ ["ý"] = "Ý", ["ž"] = "Ž",
+}
+
+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
+al.get_station_name = get_station_name
+
+local function na_velka_pismena(s)
+ local l = #s
+ local i = 1
+ local res = ""
+ local c
+ while i <= l do
+ c = diakritika_na_velka[s:sub(i, i + 1)]
+ if c then
+ res = res .. c
+ i = i + 2
+ else
+ res = res .. s:sub(i, i)
+ i = i + 1
+ end
+ end
+ return string.upper(res)
+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
+
+-- Vrací kódy výchozí a cílové stanice k zobrazení cestujícím: stn_first (string), stn_terminus (string)
+-- Předané linevar_def musí obsahovat platný seznam 'stops', ale nemusí obsahovat nic jiného (nemusí to být platná definice).
+-- V případě chyby vrací nil, nil.
+local function get_first_last_stations(linevar_def)
+ local a, b = linevar_def.index_vychozi, linevar_def.index_cil
+ if a ~= nil and b ~= nil then
+ local stops = linevar_def.stops
+ return stops[a].stn, stops[b].stn
+ else
+ return nil, nil
+ 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
+ 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
+ ls.linevar_past = nil
+ train.text_outside = al.get_line_description(linevar_def, {
+ line_number = true,
+ last_stop = true,
+ last_stop_prefix = "",
+ last_stop_uppercase = true,
+ train_name = true,
+ })
+ return true
+end
+
+--[[
+ Vrací:
+ - "true", pokud má vlak zastavit
+ - nil, pokud zastavit nemá
+ - "on_request", pokud má zastavit na znamení, ale znamení zatím nebylo dáno
+]]
+local function should_stop(pos, stdata, train)
+ if stdata == nil or stdata.stn == nil then
+ return nil -- 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
+ return nil
+ 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
+ return nil
+ 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
+ return nil
+ end
+ if stop.mode ~= nil and stop.mode == MODE_REQUEST_STOP then
+ if ls.stop_request ~= nil then
+ debug_print("Vlak "..train.id.." zastaví na zastávce na znamení.")
+ return "true"
+ else
+ debug_print("Vlak "..train.id.." možná zastaví na zastávce na znamení.")
+ return "on_request"
+ end
+ end
+ return "true"
+ -- 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
+ return "true"
+ else
+ return nil
+ end
+ 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
+ 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í předchozí zastávka nalezena
+ b) nil, nil -- pokud nalezena nebyla
+]]
+function al.get_first_stop(linevar_def, allow_hidden_stops)
+ if allow_hidden_stops then
+ return 1, linevar_def.stops[1]
+ else
+ local result = linevar_def.index_vychozi
+ if result ~= nil then
+ return result, linevar_def.stops[result]
+ else
+ return nil, nil
+ end
+ end
+end
+
+--[[
+ Vrací:
+ a) index, stop_data -- pokud byla vyhovující předchozí zastávka nalezena
+ b) nil, nil -- pokud nalezena nebyla
+]]
+function al.get_last_stop(linevar_def, allow_hidden_stops)
+ if allow_hidden_stops then
+ local stops = linevar_def.stops[result]
+ for i = linevar_def.index_cil, #stops do
+ local stop = stops[i]
+ local mode = stop.mode
+ if mode == MODE_FINAL or mode == MODE_FINAL_CONTINUE or mode == MODE_FINAL_HIDDEN then
+ return i, stop
+ end
+ end
+ else
+ local result = linevar_def.index_cil
+ if result ~= nil then
+ return result, linevar_def.stops[result]
+ end
+ end
+ return nil, nil
+end
+
+--[[
+ Vrací:
+ a) index, stop_data -- pokud byla vyhovující předchozí zastávka nalezena
+ b) nil, nil -- pokud nalezena nebyla
+]]
+function al.get_prev_stop(linevar_def, current_index, allow_hidden_stops)
+ local stops = assert(linevar_def.stops)
+ assert(current_index)
+ if current_index > 1 then
+ for i = current_index - 1, 1, -1 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í 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)
+ assert(current_index)
+ 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)
+ if linevar_def.index_cil ~= nil then
+ return linevar_def.index_cil, linevar_def.stops[linevar_def.index_cil]
+ end
+ local stops = assert(linevar_def.stops)
+ local r_i, r_stop
+ if current_index < #stops then
+ for i = current_index + 1, #stops do
+ local mode = stops[i].mode or MODE_NORMAL
+ if mode ~= MODE_DISABLED and ((mode ~= MODE_HIDDEN and mode ~= MODE_FINAL_HIDDEN) or allow_hidden_stops) then
+ r_i, r_stop = i, stops[i]
+ end
+ if mode == MODE_FINAL or mode == MODE_FINAL_CONTINUE or mode == MODE_FINAL_HIDDEN then
+ break
+ end
+ end
+ end
+ return r_i, r_stop
+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 => "⇒ "
+ last_stop_uppercase = bool or nil, -- je-li true, název cílové zastávky se před uvedením převede na velká písmena
+ 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)
+ local first_stn, last_stn = get_first_last_stations(linevar_def)
+ if line == nil or first_stn == nil or last_stn == 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 first_stn == last_stn and options.first_stop and (options.last_stop == nil or options.last_stop) then
+ s2 = get_station_name(last_stn)
+ if options.last_stop_uppercase then
+ s2 = na_velka_pismena(s2)
+ end
+ s3 = " (okružní)"
+ else
+ if options.first_stop then
+ s2 = get_station_name(first_stn).." "
+ else
+ s2 = ""
+ end
+ if options.last_stop == nil or options.last_stop then
+ s3 = get_station_name(last_stn)
+ if options.last_stop_uppercase then
+ s3 = na_velka_pismena(s3)
+ end
+ s3 = (options.last_stop_prefix or "⇒ ")..s3
+ else
+ s3 = ""
+ end
+ 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
+ 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 or
+ (next_stop_data ~= nil and next_stop_data.mode == MODE_FINAL_HIDDEN)
+ then
+ s2 = "Koncová zastávka"
+ end
+ ]]
+ end
+ end
+ if next_stop_data ~= nil then
+ local mode = next_stop_data.mode
+ 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
+ else
+ s2 = "Koncová zastávka"
+ 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, rwtime)
+ assert(line_status)
+ if linevar_def == nil then
+ return {text = ""}
+ end
+ local stops = linevar_def.stops
+ local expected_departure = line_status.linevar_dep + assert(stops[line_status.linevar_index]).dep
+ local real_departure = line_status.linevar_last_dep
+ local delay = real_departure - expected_departure
+
+ if rwtime ~= nil then
+ local expected_departure_next
+ for i = line_status.linevar_index + 1, #stops do
+ if stops[i] == nil then
+ break
+ else
+ local mode = stops[i].mode
+ if mode == nil or mode ~= MODE_DISABLED then
+ expected_departure_next = line_status.linevar_dep + stops[i].dep
+ break
+ end
+ end
+ end
+ if expected_departure_next ~= nil then
+ local delay2 = rwtime - expected_departure_next
+ if delay2 > delay then
+ delay = delay2
+ end
+ end
+ end
+
+ 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
+
+-- Test na módy MODE_FINAL*
+function al.is_final_mode(stop_mode)
+ return stop_mode ~= nil and simple_modes[stop_mode] == MODE_FINAL
+end
+local is_final_mode = al.is_final_mode
+
+-- Test na skrytou zastávku. Vrací true, pokud zadaný mód neodpovídá skryté zastávce.
+function al.is_visible_mode(stop_mode)
+ return stop_mode == nil or (stop_mode ~= MODE_HIDDEN and stop_mode ~= MODE_FINAL_HIDDEN)
+end
+local is_visible_mode = al.is_visible_mode
+
+-- 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
+ 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
+ end
+ 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
+ return linevar, result
+ end
+ end
+ 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 = {}
+ return train.line_status, nil
+ end
+ local ls = train.line_status
+ if ls.linevar == nil then
+ -- nelinkový vlak
+ local linevar_past = ls.linevar_past
+ if linevar_past ~= nil then
+ local rwtime = rwt.to_secs(rwt.get_time())
+ if train.line ~= linevar_past.line or rwtime - linevar_past.arrival >= 86400 then
+ -- smazat linevar_past, pokud se změnilo označení linky nebo uplynulo 24 hodin
+ ls.linevar_past = nil
+ end
+ end
+ 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
+ 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) ~= nil then
+ 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
+
+local function record_skipped_stops(train_id, linevar_def, linevar_index, next_index)
+ assert(linevar_def)
+ if next_index > linevar_index + 1 then
+ local skipped_stops = {}
+ for i = 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
+ return #skipped_stops
+ else
+ return 0
+ 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
+ local should_stop_result = should_stop(pos, stdata, train)
+ if should_stop_result == nil then
+ debug_print("Vlak "..train_id.." projel zastávkou "..stn)
+ return
+ elseif should_stop_result == "on_request" then
+ -- projetí zastávky na znamení
+ debug_print("Vlak "..train_id.." projel zastávkou na znamení "..stn)
+ if linevar_def ~= nil and next_index ~= nil then
+ local stop_def = assert(linevar_def.stops[next_index])
+ if stop_def.mode == nil or stop_def.mode ~= MODE_REQUEST_STOP then
+ error("Internal error: mode "..MODE_REQUEST_STOP.." expected, but the real mode is "..tostring(stop_def.mode).."!")
+ end
+ assert(stn ~= "")
+ record_skipped_stops(train_id, linevar_def, ls.linevar_index, next_index)
+ ls.linevar_index = next_index
+ ls.linevar_last_dep = rwtime -- u zastávky na znamení se průjezd počítá jako odjezd
+ ls.linevar_last_stn = stn
+ local next_stop_index, next_stop_data = al.get_next_stop(linevar_def, next_index)
+ train.text_inside = al.get_stop_description(nil, linevar_def.stops[next_stop_index or 0])
+ -- ATC command:
+ local atc_command = "A1 S" ..(stdata.speed or "M")
+ advtrains.atc.train_set_command(train, atc_command, true)
+ end
+ 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..").")
+ stdata.last_dep = planned_departure -- naplánovaný čas odjezdu
+ stdata.last_wait = wait -- naplánovaná doba čekání
+ ls.standing_at = pe
+ if linevar_def == nil or next_index == nil or (linevar_def.stops[next_index].mode or MODE_NORMAL) ~= MODE_HIDDEN then
+ -- zrušit stop_request, pokud jsme nezastavili na skryté zastávce:
+ ls.stop_request = nil
+ end
+
+ local can_start_line
+ local had_linevar = linevar_def ~= nil
+ if linevar_def == nil then
+ -- nelinkový vlak
+ can_start_line = true
+ 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..")")
+ record_skipped_stops(train_id, linevar_def, ls.linevar_index, next_index)
+ 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
+ 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
+ core.log("action", "Train "..train_id.." arrived at the final station "..stop_def.stn.." of linevar "..ls.linevar.." after "..(rwtime - ls.linevar_dep).." seconds.")
+ 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])
+ local current_passage = current_passages[train_id]
+ if current_passage ~= nil then
+ current_passage[next_index] = rwtime
+ current_passages[train_id] = nil
+ end
+ al.cancel_linevar(train)
+ -- vyplnit linevar_past:
+ if train.line ~= nil and train.line ~= "" then
+ local past_linevar = assert(linevar_def.name)
+ local past_line = al.linevar_decompose(past_linevar)
+ ls.linevar_past = {
+ line = assert(past_line),
+ linevar = past_linevar,
+ station = stn,
+ arrival = rwtime,
+ }
+ end
+ 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
+
+ -- 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 stn ~= nil and stn ~= "" 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
+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
+
+ if ls.standing_at == pe then
+ -- vlak stál v této dopravně
+ ls.standing_at = nil
+ if
+ linevar_def == nil or ls.linevar_index == nil or
+ linevar_def.stops[ls.linevar_index] == nil or
+ (linevar_def.stops[ls.linevar_index].mode or MODE_NORMAL) ~= MODE_HIDDEN
+ then
+ ls.stop_request = nil -- zrušit stop_request při odjezdu ze zastávky, pokud není nekoncová skrytá
+ end
+ if stn ~= "" then
+ 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
+ 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:
+ debug_print("Linkový vlak "..train_id.." odjel ze zastávky s indexem "..ls.linevar_index)
+ if ls.linevar_index == 1 then
+ -- odjezd z výchozí zastávky:
+ local new_passage = {rwtime}
+ if stdata.last_wait ~= nil then
+ new_passage.wait = stdata.last_wait
+ end
+ current_passages[train_id] = new_passage
+ local passages_by_linevar = last_passages[ls.linevar]
+ if passages_by_linevar == nil then
+ passages_by_linevar = {new_passage}
+ last_passages[ls.linevar] = passages_by_linevar
+ else
+ while #passages_by_linevar >= 10 do
+ table.remove(passages_by_linevar, 1)
+ end
+ table.insert(passages_by_linevar, new_passage)
+ end
+ else
+ -- odjezd z nácestné zastávky
+ local current_passage = current_passages[train_id]
+ if current_passage ~= nil then
+ current_passage[ls.linevar_index] = rwtime
+ end
+ end
+ 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("Vlak "..train_id.." projel (odjezd) zastávkou "..stn..".")
+ else
+ debug_print("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
+
+--[[
+ Vrací:
+ a) pokud linevar existuje a má průjezdy:
+ passages, stops:
+ {{[1] = rwtime, ..., wait = int or nil}...}, {"kód", "název"}...}
+ b) jinak:
+ nil, nil
+]]
+function al.get_last_passages(linevar_def)
+ local lp = last_passages[linevar_def.name]
+ if linevar_def ~= nil and lp ~= nil and lp[1] ~= nil then
+ local passages, stops = {}, {}
+ for i, stop in ipairs(linevar_def.stops) do
+ stops[i] = {stop.stn, get_station_name(stop.stn)}
+ end
+ for i = 1, #lp do
+ passages[i] = table.copy(lp[i])
+ end
+ return passages, stops
+ end
+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
+
+function al.get_last_pos_station_name(line_status)
+ local result = get_last_pos(line_status)
+ if result.type ~= "none" then
+ return get_station_name(result.last_pos.stn)
+ else
+ return nil
+ end
+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)
+ 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, rwtime)
+ 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
+
+--[[
+local function prepare_prediction_for_dump(result)
+ local x = {}
+ for i, record in ipairs(result) do
+ local y = table.copy(record)
+ y.stdata = record.stdata.name
+ if y.arr_linevar_def ~= nil then
+ y.arr_linevar_def = y.arr_linevar_def.name
+ end
+ if y.dep_linevar_def ~= nil then
+ y.dep_linevar_def = y.dep_linevar_def.name
+ end
+ x[i] = y
+ end
+ return x
+end
+]]
+
+local function predict_train_continue(line, stn, rc, departure, result)
+ if line == nil or line == "" then
+ return
+ end
+ local linevar_def = al.try_get_linevar_def(line.."/"..stn.."/"..rc, stn)
+ if linevar_def == nil then
+ return
+ end
+ if #result == 0 then
+ return
+ end
+ local stops = assert(linevar_def.stops)
+ local last_record = result[#result]
+ last_record.dep = departure
+ last_record.dep_linevar_def = linevar_def
+ last_record.dep_index = 1
+ local index = 2
+ while stops[index] ~= nil do
+ local stop = stops[index]
+ local stdata = advtrains.lines.stations[stop.stn]
+ if stop.mode == MODE_FINAL or stop.mode == MODE_FINAL_CONTINUE or stop.mode == MODE_FINAL_HIDDEN then
+ -- koncová zastávka
+ local arr = departure + stop.dep
+ local record = {
+ stn = assert(stop.stn),
+ track = stop.track or "",
+ stdata = stdata,
+ arr = arr,
+ arr_linevar_def = linevar_def,
+ arr_index = index,
+ delay = 0,
+ hidden = stop.mode == MODE_FINAL_HIDDEN,
+ }
+ table.insert(result, record)
+ return
+ elseif stop.mode ~= MODE_DISABLED then
+ -- mezilehlá zastávka
+ local dep = departure + stop.dep
+ table.insert(result, {
+ stn = assert(stop.stn),
+ track = stop.track or "",
+ stdata = stdata,
+ arr = dep - (stop.wait or 10),
+ arr_linevar_def = linevar_def,
+ arr_index = index,
+ dep = dep,
+ dep_linevar_def = linevar_def,
+ dep_index = index,
+ delay = 0,
+ hidden = stop.mode == MODE_HIDDEN,
+ })
+ end
+ index = index + 1
+ end
+end
+
+--[[
+ Zadaný vlak musí být linkový.
+ Parametry:
+ line_status = table,
+ linevar_def = table,
+ rwtime = int,
+ allow_continue = bool,
+ Vrací *pole* následujících záznamů:
+ {
+ stn = string,
+ track = string,
+ delay = int,
+ stdata = table or nil,
+ dep = int or nil, dep_linevar_def = table or nil, dep_index = int or nil,
+ arr = int or nil, arr_linevar_def = table or nil, arr_index = int or nil,
+ }
+]]
+function al.predict_train(line_status, linevar_def, rwtime, allow_continue)
+ assert(line_status)
+ assert(linevar_def)
+ local stops = linevar_def.stops
+ local result = {}
+ local index = assert(line_status.linevar_index)
+ if rwtime == nil then
+ rwtime = rwt.to_secs(rwt.get_time())
+ end
+ local delay_desc = al.get_delay_description(line_status, linevar_def, rwtime)
+ local delay
+ if delay_desc.has_delay then
+ delay = delay_desc.delay
+ else
+ delay = 0
+ end
+ local departure = line_status.linevar_dep
+ if line_status.standing_at ~= nil then
+ -- vlak stojí na zastávce
+ local stop = assert(stops[index])
+ local stdata = advtrains.lines.stations[stop.stn]
+ table.insert(result, {
+ stn = line_status.linevar_last_stn,
+ track = stop.track or "",
+ stdata = stdata,
+ dep = assert(line_status.linevar_last_dep),
+ dep_linevar_def = assert(linevar_def),
+ dep_index = index,
+ -- arr = nil, arr_index = nil, arr_linevar_def = nil,
+ delay = delay,
+ hidden = stop.mode == MODE_HIDDEN
+ })
+ end
+ index = index + 1
+ while stops[index] ~= nil do
+ local stop = stops[index]
+ local stdata = advtrains.lines.stations[stop.stn]
+ if stop.mode == MODE_FINAL or stop.mode == MODE_FINAL_CONTINUE or stop.mode == MODE_FINAL_HIDDEN then
+ -- koncová zastávka
+ local arr = departure + stop.dep + delay
+ local record = {
+ stn = assert(stop.stn),
+ track = stop.track or "",
+ stdata = stdata,
+ arr = arr,
+ arr_linevar_def = linevar_def,
+ arr_index = index,
+ delay = delay,
+ hidden = stop.mode == MODE_FINAL_HIDDEN,
+ final = true,
+ }
+ table.insert(result, record)
+ if allow_continue and (linevar_def.continue_line or "") ~= "" and stop.mode == MODE_FINAL_CONTINUE then
+ predict_train_continue(linevar_def.continue_line, stop.stn, linevar_def.continue_rc, arr + (stop.wait or 10), result)
+ end
+ break
+ elseif stop.mode ~= MODE_DISABLED then
+ -- mezilehlá zastávka
+ local dep = departure + stop.dep + delay
+ table.insert(result, {
+ stn = assert(stop.stn),
+ track = stop.track or "",
+ stdata = stdata,
+ arr = dep - (stop.wait or 10),
+ arr_linevar_def = linevar_def,
+ arr_index = index,
+ dep = dep,
+ dep_linevar_def = assert(linevar_def),
+ dep_index = index,
+ delay = delay,
+ hidden = stop.mode == MODE_HIDDEN,
+ })
+ end
+ index = index + 1
+ end
+ -- print("DEBUG: "..dump2({prediction = prepare_prediction_for_dump(result)}))
+ return result
+end
+
+--[[
+ Parametry:
+ linevar_def = table, -- definice linevar, z něhož se má analýza provádět
+ linevar_index = int,
+ rwtime = int or nil, -- (aktuální žel. čas; nepovinné)
+ trains = {train_table...} or nil, -- je-li zadáno, bude zkoumat pouze vlaky v tomto seznamu
+ Vrací *pole* záznamů (stejných jako al.predict_train) vztahujících se k odjezdu
+ z požadované zastávky, seřazené od nejbližšího odjezdu po nejvzdálenější.
+]]
+function al.predict_station_departures(linevar_def, linevar_index, rwtime, trains)
+ assert(linevar_def)
+ assert(linevar_index)
+ local linevar = linevar_def.name
+ local stop = assert(linevar_def.stops[linevar_index])
+ if trains == nil then
+ trains = al.get_trains_by_linevar()[linevar] or {}
+ end
+ if rwtime == nil then
+ rwtime = rwt.to_secs(rwt.get_time())
+ end
+ local result = {}
+ for _, train in ipairs(trains) do
+ local ls, lvdef = al.get_line_status(train)
+ if ls.linevar == linevar and ls.linevar_index <= linevar_index then
+ local prediction = al.predict_train(ls, linevar_def, rwtime, true)
+ for _, pr in ipairs(prediction) do
+ if
+ pr.dep ~= nil and pr.dep_linevar_def ~= nil and pr.dep_index ~= nil and
+ pr.dep_linevar_def.name == linevar and
+ pr.dep_index == linevar_index and
+ pr.dep > rwtime
+ then
+ table.insert(result, pr)
+ break
+ end
+ end
+ end
+ end
+ table.sort(result, function(a, b) return a.dep < b.dep end)
+ return result
+end
+
+--[[
+ => {{
+ linevar = string,
+ indices = {int,...},
+ linevar_def = linevar_def,
+ }...}
+]]
+function al.get_linevars_by_station(stn, track, options)
+ if options == nil then
+ options = {}
+ end
+ local include_hidden_stops = options.include_hidden_stops
+ local ignore_first_stop = options.ignore_first_stop
+ local ignore_last_stop = options.ignore_last_stop
+ local result = {}
+ assert(stn)
+ for lvstn, stdata in pairs(advtrains.lines.stations) do
+ if stdata.linevars ~= nil then
+ for linevar, linevar_def in pairs(stdata.linevars) do
+ local first_stop_index = al.get_first_stop(linevar_def, include_hidden_stops)
+ local last_stop_index = al.get_last_stop(linevar_def, include_hidden_stops)
+ if not (ignore_first_stop or ignore_last_stop) or (first_stop_index ~= nil and last_stop_index ~= nil) then
+ local ld
+ for i, stop in ipairs(linevar_def.stops) do
+ if
+ stop.stn == stn and
+ (track == nil or tostring(stop.track) == track) and
+ (include_hidden_stops or is_visible_mode(stop.mode)) and
+ ((not ignore_first_stop) or i ~= 1) and
+ ((not ignore_last_stop) or (not is_final_mode(stop.mode)))
+ then
+ if ld == nil then
+ ld = {linevar = linevar, linevar_def = linevar_def, indices = {i}}
+ table.insert(result, ld)
+ else
+ table.insert(ld.indices, i)
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+ if #result > 1 then
+ table.sort(result, function(a, b) return a.linevar < b.linevar end)
+ end
+ return result
+end
+
+--[[
+ => {
+ [linevar] = {train...}, -- generuje jen neprázdné seznamy
+ }
+]]
+function al.get_trains_by_linevar()
+ local result = {}
+ for train_id, train in pairs(advtrains.trains) do
+ local ls, linevar_def = al.get_line_status(train)
+ if linevar_def ~= nil then
+ local linevar = linevar_def.name
+ local list = result[linevar]
+ if list ~= nil then
+ table.insert(list, train)
+ else
+ list = {train}
+ result[linevar] = list
+ end
+ end
+ end
+ for linevar, list in pairs(result) do
+ if list[2] ~= nil then
+ table.sort(list, function(a, b) return a.id < b.id end)
+ end
+ end
+ return result
+end
+
+local function vlaky(param, past_trains_too)
+ local result = {}
+ if param:match("/") then
+ return result -- parametr nesmí obsahovat '/'
+ end
+ local train_line_prefix
+ if param ~= "" then
+ train_line_prefix = param.."/"
+ end
+ local rwtime = rwt.to_secs(rwt.get_time())
+ local players_per_train = {}
+ local results = {}
+ for player_name, train_id in pairs(advtrains.player_to_train_mapping) do
+ results[train_id] = (results[train_id] or 0) + 1
+ end
+ for train_id, train in pairs(advtrains.trains) do
+ local ls, linevar_def = al.get_line_status(train)
+ if linevar_def ~= nil then
+ if train_line_prefix == nil or train_line_prefix == ls.linevar:sub(1, #train_line_prefix) 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 line = al.linevar_decompose(linevar_def.name)
+ local s = "("..train_id..") ["..line.."] směr „"..direction.."“, poloha: "..
+ get_train_position(ls, linevar_def, rwtime)
+ if results[train_id] ~= nil then
+ s = s.." ["..results[train_id].." cestující/ch]"
+ end
+ table.insert(results, {key = linevar_def.name.."/"..ls.linevar_index, value = s})
+ end
+ elseif past_trains_too and ls.linevar_past ~= nil and (train_line_prefix == nil or ls.linevar_past.line == param) then
+ local age = rwtime - ls.linevar_past.arrival
+ local s = "("..train_id..") ["..ls.linevar_past.line.."] služební, poloha: "..
+ get_station_name(ls.linevar_past.station).." (před "..age.." sekundami)"
+ if results[train_id] ~= nil then
+ s = s.." ["..results[train_id].." cestující/ch]"
+ end
+ table.insert(results, {
+ key = string.format("%s/~/%s/%05d", ls.linevar_past.line, ls.linevar_past.station, age),
+ value = s,
+ })
+ end
+ end
+ table.sort(results, function(a, b) return a.key < b.key end)
+ for i, v in ipairs(results) do
+ result[i] = v.value
+ end
+ return result
+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 = vlaky(param, false)
+ 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)
+def = {
+ params = "[linka]",
+ description = "Vypíše všechny linkové vlaky na zadané lince (resp. na všech linkách) a ty, které nedávno jízdu na lince ukončily",
+ privs = {},
+ func = function(player_name, param)
+ local result = vlaky(param, true)
+ 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)
+
+def = {
+ -- params = "",
+ description = "(pro ladění)",
+ privs = {server = true},
+ func = function(player_name, param)
+ local train = advtrains.trains[param]
+ if train == nil then
+ return false, "Vlak "..param.." nenalezen!"
+ end
+ local line_status, linevar_def = al.get_line_status(train)
+ if linevar_def == nil then
+ return false, "Vlak "..param.." není linkový!"
+ end
+ -- function al.predict_train(line_status, linevar_def, rwtime, allow_continue)
+ local rwtime = rwt.to_secs(rwt.get_time())
+ local predictions = al.predict_train(line_status, linevar_def, rwtime, true)
+ local result = {"----"}
+ local s
+ for i, record in ipairs(predictions) do
+ local arr, dep
+ if record.arr ~= nil then
+ arr = "("..(record.arr - rwtime)..", lv="..record.arr_linevar_def.name..", i="..record.arr_index..")"
+ else
+ arr = "nil"
+ end
+ if record.dep ~= nil then
+ dep = "("..(record.dep - rwtime)..", lv="..record.dep_linevar_def.name..", i="..record.dep_index..")"
+ else
+ dep = "nil"
+ end
+ result[i + 1] = "- "..record.stn.." ["..record.track.."] arr="..arr.." dep="..dep.." delay="..record.delay
+ end
+ table.insert(result, "----")
+ s = table.concat(result, "\n")
+ print(s)
+ core.chat_send_player(player_name, s)
+ return true
+ end,
+}
+core.register_chatcommand("jřád", def)
+core.register_chatcommand("jrad", def)
diff --git a/advtrains_line_automation/mod.conf b/advtrains_line_automation/mod.conf
index e9851c8..eeae5a2 100644
--- a/advtrains_line_automation/mod.conf
+++ b/advtrains_line_automation/mod.conf
@@ -3,5 +3,5 @@ title=Advanced Trains Line Automation
description=Tools for automatic train lines
author=orwell96
-depends=advtrains_interlocking
-optional_depends=advtrains_train_track
+depends=advtrains_interlocking,default
+optional_depends=advtrains_train_track,ch_time,signs_api,unifieddyes
diff --git a/advtrains_line_automation/models/advtrains_tuber.obj b/advtrains_line_automation/models/advtrains_tuber.obj
new file mode 100644
index 0000000..bd17341
--- /dev/null
+++ b/advtrains_line_automation/models/advtrains_tuber.obj
@@ -0,0 +1,150 @@
+# Blender v2.76 (sub 0) OBJ File: 'dark_tuber.blend'
+# www.blender.org
+g tuber
+v 0.068165 0.171006 0.601066
+v 0.068165 0.328994 0.601066
+v -0.068165 0.328994 0.601066
+v -0.068165 0.171006 0.601066
+v 0.184430 0.065570 0.081152
+v 0.184430 0.434431 0.016861
+v -0.190760 0.430332 0.072541
+v -0.184430 0.065570 0.221249
+v 0.111639 0.137188 0.264378
+v 0.108474 0.360762 0.259011
+v -0.131011 0.350268 0.238021
+v -0.114803 0.135139 0.328379
+v 0.050415 0.195602 0.451854
+v 0.050415 0.304398 0.466035
+v -0.058854 0.298933 0.440372
+v -0.050415 0.195602 0.469734
+v 0.132013 0.116200 0.277490
+v 0.132013 0.383800 0.248250
+v -0.132013 0.383800 0.277490
+v -0.132013 0.116200 0.355945
+v 0.000000 0.171006 0.601066
+v 0.000000 0.019494 0.166419
+v 0.000000 0.118180 0.299225
+v 0.000000 0.184188 0.460794
+v 0.000000 0.091796 0.320542
+v -0.000000 0.328994 0.601066
+v -0.003165 0.478457 0.029481
+v -0.012851 0.373499 0.245669
+v -0.004220 0.313080 0.453203
+v -0.000000 0.408204 0.259046
+v -0.068165 0.250000 0.601066
+v -0.233671 0.247951 0.162114
+v -0.143497 0.241679 0.281741
+v -0.064484 0.247267 0.455053
+v -0.156417 0.250000 0.320542
+v 0.068165 0.250000 0.601066
+v 0.230506 0.250000 0.033787
+v 0.130646 0.250000 0.263154
+v 0.060264 0.250000 0.458944
+v 0.156417 0.250000 0.259046
+vt 1.000000 1.000000
+vt 0.937500 1.000000
+vt 0.875000 1.000000
+vt 0.875000 0.937500
+vt 0.875000 0.875000
+vt 0.937500 0.875000
+vt 1.000000 0.875000
+vt 1.000000 0.937500
+vt 0.750000 1.000000
+vt 0.750000 0.500000
+vt 0.875000 0.500000
+vt 0.625000 0.500000
+vt 0.625000 1.000000
+vt 0.500000 1.000000
+vt 0.500000 0.500000
+vt 0.375000 0.500000
+vt 0.375000 1.000000
+vt 0.250000 1.000000
+vt 0.250000 0.500000
+vt 0.125000 0.500000
+vt 0.125000 1.000000
+vt -0.000000 1.000000
+vt 0.000000 0.500000
+vt 0.875000 -0.000000
+vt 1.000000 0.000000
+vt 1.000000 0.500000
+vt 1.000000 0.750000
+vt 1.000000 0.812500
+vt 0.875000 0.812500
+vt 0.875000 0.750000
+vt 0.937500 0.750000
+vt 0.125000 -0.000000
+vt -0.000000 -0.000000
+vt 0.375000 -0.000000
+vt 0.250000 -0.000000
+vt 0.625000 0.000000
+vt 0.500000 -0.000000
+vt 0.750000 0.000000
+vn 0.000000 -1.000000 -0.000000
+vn 0.764800 0.518100 -0.383000
+vn 0.930800 -0.283500 0.230800
+vn -0.161500 -0.277500 0.947000
+vn -0.905200 -0.416000 -0.086500
+vn -0.072500 -0.254700 -0.964300
+vn 0.010500 0.576200 0.817300
+vn -0.165900 0.384900 -0.907900
+vn -0.905900 0.402100 0.132600
+vn 0.035600 0.997300 -0.063700
+vn -0.087800 -0.403600 -0.910700
+vn -0.956900 -0.283400 -0.062600
+vn -0.099700 -0.205600 0.973500
+vn 0.967500 -0.221400 0.122200
+vn -0.075100 0.278700 -0.957400
+vn -0.923500 0.343200 0.171400
+vn 0.889600 0.387400 -0.241800
+vn 0.065900 0.396300 0.915700
+vn -0.266100 0.364700 0.892300
+vn 0.338400 -0.356100 -0.871000
+vn -0.375700 0.505200 0.776900
+vn 0.166400 -0.230300 -0.958800
+vn 0.235500 0.298600 -0.924900
+vn 0.129200 -0.199200 0.971400
+vn 0.153800 0.447300 -0.881100
+vn 0.220500 -0.269400 0.937500
+vn 0.900900 0.426900 0.078000
+vn -0.953500 -0.254700 0.160900
+vn -0.867100 -0.365900 0.338100
+vn 0.816200 0.577700 0.008700
+vn -0.930700 0.321200 -0.174700
+vn 0.969100 -0.229000 -0.091300
+vn -0.898400 0.371600 -0.234200
+vn 0.942500 -0.300400 -0.146600
+s off
+f 1/1/1 36/2/1 2/3/1 26/4/1 3/5/1 31/6/1 4/7/1 21/8/1
+f 32/3/2 7/9/2 11/10/2 33/11/2
+f 40/12/3 37/13/3 6/14/3 18/15/3
+f 30/16/4 27/17/4 7/18/4 19/19/4
+f 35/20/5 32/21/5 8/22/5 20/23/5
+f 25/11/6 21/24/6 4/25/6 20/26/6
+f 22/21/7 8/22/7 12/23/7 23/20/7
+f 27/13/8 6/14/8 10/15/8 28/12/8
+f 37/17/9 5/18/9 9/19/9 38/16/9
+f 13/27/10 24/28/10 16/7/10 34/6/10 15/5/10 29/29/10 14/30/10 39/31/10
+f 22/3/11 25/11/11 20/26/11 8/1/11
+f 31/32/12 35/20/12 20/23/12 4/33/12
+f 26/34/13 30/16/13 19/19/13 3/35/13
+f 36/36/14 40/12/14 18/15/14 2/37/14
+f 28/12/15 10/15/15 14/37/15 29/36/15
+f 38/16/16 9/19/16 13/35/16 39/34/16
+f 33/11/17 11/10/17 15/38/17 34/24/17
+f 23/20/18 12/23/18 16/33/18 24/32/18
+f 9/19/19 23/20/19 24/32/19 13/35/19
+f 5/9/20 17/10/20 25/11/20 22/3/20
+f 5/18/21 22/21/21 23/20/21 9/19/21
+f 17/10/22 1/38/22 21/24/22 25/11/22
+f 11/10/23 28/12/23 29/36/23 15/38/23
+f 2/37/24 18/15/24 30/16/24 26/34/24
+f 7/9/25 27/13/25 28/12/25 11/10/25
+f 18/15/26 6/14/26 27/17/26 30/16/26
+f 12/26/27 33/11/27 34/24/27 16/25/27
+f 3/35/28 19/19/28 35/20/28 31/32/28
+f 19/19/29 7/18/29 32/21/29 35/20/29
+f 8/1/30 32/3/30 33/11/30 12/26/30
+f 10/15/31 38/16/31 39/34/31 14/37/31
+f 1/38/32 17/10/32 40/12/32 36/36/32
+f 6/14/33 37/17/33 38/16/33 10/15/33
+f 17/10/34 5/9/34 37/13/34 40/12/34
diff --git a/advtrains_line_automation/models/license.txt b/advtrains_line_automation/models/license.txt
new file mode 100644
index 0000000..c76bd96
--- /dev/null
+++ b/advtrains_line_automation/models/license.txt
@@ -0,0 +1,5 @@
+advtrains_tuber.obj
+ Author: Hume2
+ Source: https://gitlab.com/h2mm/underch underch_dark_tuber.obj (Underground Challenge)
+ Modified (rotated, scaled)
+ License: CC0
diff --git a/advtrains_line_automation/railwaytime.lua b/advtrains_line_automation/railwaytime.lua
index 258009e..29ef110 100644
--- a/advtrains_line_automation/railwaytime.lua
+++ b/advtrains_line_automation/railwaytime.lua
@@ -304,5 +304,20 @@ function rwt.last_rpt(rwtime, rpt_interval, rpt_offset)
return rwt.to_table(res_s)
end
+function rwt.to_os_time(rwtime)
+ local rw_now = rwt.to_secs(rwt.get_time())
+ return rwt.to_secs(rwtime) - rw_now + os.time()
+end
advtrains.lines.rwt = rwt
+
+if core.get_modpath("ch_time") then
+ ch_time.set_rwtime_callback(function()
+ local rwtime = rwt.get_time()
+ return {
+ secs = rwt.to_secs(rwtime),
+ string = rwt.to_string(rwtime, true),
+ string_extended = rwt.to_string(rwtime),
+ }
+ end)
+end
diff --git a/advtrains_line_automation/station_announcement.lua b/advtrains_line_automation/station_announcement.lua
new file mode 100644
index 0000000..3f9956c
--- /dev/null
+++ b/advtrains_line_automation/station_announcement.lua
@@ -0,0 +1,1579 @@
+local al = advtrains.lines
+local F = core.formspec_escape
+local ifthenelse = function(cond, a, b) if cond then return a else return b end end
+local rwt = assert(advtrains.lines.rwt)
+local def
+local function CF(s)
+ if s ~= nil then return F(s) else return "" end
+end
+local has_ch_time = core.get_modpath("ch_time")
+local has_signs_api = core.get_modpath("signs_api")
+local has_unifieddyes = core.get_modpath("unifieddyes")
+local rozhlas_node_name = "advtrains_line_automation:stanicni_rozhlas"
+
+local RMODE_DEP = 1
+local RMODE_ARR = 2
+local RMODE_BOTH = 3
+
+local PAGE_SETUP_1 = 1
+local PAGE_SETUP_2 = 2
+local PAGE_OWNERSHIP = 3
+
+local hl_texty = {
+ {
+ id = "vcpp",
+ sample = "Vážení cestující, prosíme pozor!",
+ default = "",
+ }, {
+ id = "tvl",
+ sample = "{TYPVLAKU}",
+ }, {
+ id = "vlk",
+ sample = "Vlak",
+ }, {
+ id = "lnky",
+ sample = "linky {LINKA}",
+ }, {
+ id = "jmvlku",
+ sample = "{JMVLAKU}",
+ }, {
+ id = "zesm",
+ sample = "ze směru {VYCHOZI}",
+ }, {
+ id = "prijnk",
+ sample = "přijíždí na kolej {KOLEJ}.",
+ }, {
+ id = "prisnk",
+ sample = "bude přistaven na kolej {KOLEJ}.",
+ }, {
+ id = "zpoz",
+ sample = "Vlak má {ZPOZDENI} sekund zpoždění.",
+ default = "",
+ }, {
+ id = "zpozz",
+ sample = "Vlak má -{ZPOZDENI} sekund zpoždění.",
+ default = "",
+ }, {
+ id = "vkonc",
+ sample = "Vlak zde jízdu končí.",
+ }, {
+ id = "pokrnc",
+ sample = "Vlak dále pokračuje směr {NASL} a {CIL}",
+ }, {
+ id = "pokrc",
+ sample = "Vlak pokračuje směr {CIL}",
+ }, {
+ id = "ozasek",
+ sample = ", odjezd za {ODJZA} sekund.",
+ }, {
+ id = "dek",
+ sample = "Děkujeme, že používáte naše služby.",
+ default = "",
+ }
+}
+local hl_texty_id_to_idx = function()
+ local result = {}
+ for i, def in ipairs(hl_texty) do
+ local id = assert(def.id)
+ if result[id] ~= nil then
+ error("Duplicity: id = "..id.."!")
+ end
+ def.fs_sample = F(def.sample)
+ def.fs_default = F(def.default or def.sample)
+ result[id] = i
+ end
+ return result
+end
+hl_texty_id_to_idx = hl_texty_id_to_idx()
+
+local punch_context = {--[[
+ [player_name] = {
+ stn = string,
+ epos = string,
+ i = int,
+ }
+]]}
+
+local function dist2(a, b)
+ local x, y, z = a.x - b.x, a.y - b.y, a.z - b.z
+ return x * x + y * y + z * z
+end
+
+local function goa(t, k) -- goa = get or add
+ local result = t[k]
+ if result == nil then
+ result = {}
+ t[k] = result
+ end
+ return result
+end
+
+--[[
+ Vrací:
+ - success = bool
+ - min = int or nil (pro success == false vždy nil)
+ - max = int or nil (pro success == false vždy nil)
+ - align = "left" or "right" or "center"
+]]
+local function lengths_from_string(s)
+ if s == "" or s == "-" then
+ return true, nil, nil
+ end
+ local l = #s
+ s = s:gsub("^ +", "")
+ local left = #s < l
+ l = #s
+ s = s:gsub(" +$", "")
+ local right = #s < l
+ local align
+ if not left then
+ align = "left" -- mezery vpravo, nebo žádné mezery
+ elseif right then
+ align = "center" -- mezery na obou stranách
+ else
+ align = "right" -- mezery jen vlevo
+ end
+ local n = s:match("^%d+$")
+ if n ~= nil then
+ n = assert(tonumber(n))
+ if n > 256 then
+ n = 256
+ end
+ return true, n, n, align
+ end
+ local min, max = s:match("^(%d*)-(%d*)$")
+ if min == nil then
+ return false, nil, nil, nil -- bad format
+ end
+ min, max = tonumber(min), tonumber(max)
+ if min ~= nil and min > 256 then min = 256 end
+ if max ~= nil and max > 256 then max = 256 end
+ if min ~= nil and max ~= nil and min > max then
+ min = max
+ end
+ return true, min, max, align
+end
+
+--[[ (obsolete)
+local function lengths_to_string(min, max)
+ if min == nil then
+ if max ~= nil then
+ return "-"..max
+ else
+ return ""
+ end
+ elseif max == nil then
+ return min.."-"
+ elseif min == max then
+ return tostring(min)
+ else
+ return min.."-"..max
+ end
+end
+]]
+
+local alphanum_chars_set = {
+ ["a"] = true, ["A"] = true, ["á"] = true, ["Á"] = true, ["ä"] = true, ["Ä"] = true, ["b"] = true, ["B"] = true, ["č"] = true,
+ ["Č"] = true, ["d"] = true, ["D"] = true, ["ď"] = true, ["Ď"] = true, ["e"] = true, ["E"] = true, ["é"] = true, ["É"] = true,
+ ["ě"] = true, ["Ě"] = true, ["f"] = true, ["F"] = true, ["g"] = true, ["G"] = true, ["h"] = true, ["H"] = true, ["i"] = true,
+ ["I"] = true, ["í"] = true, ["Í"] = true, ["j"] = true, ["J"] = true, ["k"] = true, ["K"] = true, ["l"] = true, ["L"] = true,
+ ["ĺ"] = true, ["Ĺ"] = true, ["ľ"] = true, ["Ľ"] = true, ["m"] = true, ["M"] = true, ["n"] = true, ["N"] = true, ["ň"] = true,
+ ["Ň"] = true, ["o"] = true, ["O"] = true, ["ó"] = true, ["Ó"] = true, ["ô"] = true, ["Ô"] = true, ["p"] = true, ["P"] = true,
+ ["q"] = true, ["Q"] = true, ["r"] = true, ["R"] = true, ["ŕ"] = true, ["Ŕ"] = true, ["ř"] = true, ["Ř"] = true, ["s"] = true,
+ ["S"] = true, ["š"] = true, ["Š"] = true, ["t"] = true, ["T"] = true, ["ť"] = true, ["Ť"] = true, ["u"] = true, ["U"] = true,
+ ["ú"] = true, ["Ú"] = true, ["ů"] = true, ["Ů"] = true, ["v"] = true, ["V"] = true, ["w"] = true, ["W"] = true, ["x"] = true,
+ ["X"] = true, ["y"] = true, ["Y"] = true, ["ý"] = true, ["Ý"] = true, ["z"] = true, ["Z"] = true, ["ž"] = true, ["Ž"] = true,
+ ["0"] = true, ["1"] = true, ["2"] = true, ["3"] = true, ["4"] = true, ["5"] = true, ["6"] = true, ["7"] = true, ["8"] = true,
+ ["9"] = true,
+}
+
+local function dosadit(format, data, defaults)
+ assert(type(format) == "string")
+ assert(type(data) == "table")
+ if defaults == nil then
+ defaults = {}
+ end
+ local result = {}
+ local i = 1
+ local e = 0
+ local b = format:find("{", e + 1, true)
+ while b ~= nil do
+ if e < b - 1 then
+ table.insert(result, format:sub(e + 1, b - 1))
+ end
+ e = format:find("}", b + 1, true)
+ if e == nil then
+ core.log("warning", "[advtrains_line_automation] dosadit(): invalid format: <"..format..">")
+ table.insert(result, format:sub(b, -1))
+ break
+ end
+ local tag = format:sub(b + 1, e - 1)
+ local tag_name = tag
+ local tagfmt, tagalt = ""
+ local b2 = tag:find("[:|]")
+ if b2 == nil then
+ tag_name, tagfmt = tag, ""
+ else
+ tag_name = tag:sub(1, b2 - 1)
+ repeat
+ local e2 = tag:find("[:|]", b2 + 1) or (#tag + 1)
+ local c = tag:sub(b2, b2)
+ if c == ":" then
+ tagfmt = tag:sub(b2 + 1, e2 - 1)
+ elseif c == "|" then
+ tagalt = tag:sub(b2 + 1, e2 - 1)
+ end
+ b2 = e2
+ until b2 >= #tag
+ end
+ local min, max, align
+ if tagfmt ~= "" then
+ local success
+ success, min, max, align = lengths_from_string(tagfmt)
+ end
+ tag = tag_name
+ if tag:len() < 4 and ch_core.utf8_length(tag) == 1 and alphanum_chars_set[tag] == nil then
+ -- speciální případ: zopakovat znak alespoň min-krát
+ if min ~= nil then
+ table.insert(result, string.rep(tag, min))
+ else
+ table.insert(result, tag)
+ end
+ else
+ local value = data[tag] or tagalt or defaults[tag] or "ERR"
+ local len = ch_core.utf8_length(value)
+ if min ~= nil and len < min then
+ -- řetězec je kratší než minimum => prodloužit
+ local missing = min - len
+ if align == "left" then
+ table.insert(result, value)
+ table.insert(result, string.rep(" ", missing))
+ elseif align == "right" then
+ table.insert(result, string.rep(" ", missing))
+ table.insert(result, value)
+ else
+ table.insert(result, string.rep(" ", math.floor(missing / 2)))
+ table.insert(result, value)
+ table.insert(result, string.rep(" ", missing - math.floor(missing / 2)))
+ end
+ elseif max ~= nil and len > max then
+ -- řetězec je delší než maximum => oříznout
+ if align == "left" then
+ local pos = assert(ch_core.utf8_seek(value, 1, max))
+ table.insert(result, value:sub(1, pos - 1))
+ elseif align == "right" then
+ local pos = assert(ch_core.utf8_seek(value, 1, len - max))
+ table.insert(result, value:sub(pos, -1))
+ else
+ local overflow = len - max
+ local half = math.floor(overflow / 2)
+ local pos1 = assert(ch_core.utf8_seek(value, 1, half))
+ local pos2 = (ch_core.utf8_seek(value, pos1, len - overflow) or 0) - 1
+ table.insert(result, value:sub(pos1, pos2))
+ end
+ else
+ -- řetězec odpovídá
+ table.insert(result, value)
+ end
+ end
+ b = format:find("{", e + 1, true)
+ end
+ table.insert(result, format:sub(e + 1, -1))
+ return table.concat(result)
+end
+
+-- Vrátí neuspořádaný seznam čísel řádků, které musejí být zformátovány pro daný formátovací řetězec.
+local function ktere_radky(s, empty_as_string)
+ local set = {}
+ local list = {}
+ for match in s:gmatch("{[1-9][:|}]") do
+ local n = tonumber(match:sub(2,2))
+ if n ~= nil and not set[n] then
+ set[n] = true
+ table.insert(list, n)
+ end
+ end
+ if list[1] == nil and empty_as_string then
+ return ""
+ else
+ return list
+ end
+end
+
+local function sestavit_hlaseni(settings, settings_override, data, defaults)
+ local parts = {}
+ local function a(s)
+ table.insert(parts, s)
+ end
+ local function t(id)
+ if id == nil then
+ return ""
+ end
+ local key = "tx_"..id
+ local result = settings_override[key] or settings[key]
+ if result == nil then
+ local def = assert(hl_texty[hl_texty_id_to_idx[id]])
+ result = assert(def.default or def.sample)
+ end
+ return result
+ end
+ assert(settings)
+ if settings_override == nil then
+ settings_override = {}
+ end
+ assert(data)
+ a(t("vcpp"))
+ a("{SEP}")
+ a(t(ifthenelse(data.TYPVLAKU ~= nil, "tvl", "vlk")))
+ a("{SEP}")
+ if data.LINKA ~= nil then
+ a(t("lnky"))
+ a("{SEP}")
+ end
+ if data.VYCHOZI ~= nil then
+ a(t("zesm"))
+ a("{SEP}")
+ a(t("prijnk"))
+ else
+ a(t("prisnk"))
+ end
+ a("{SEP}")
+ if data.typ_zpozdeni == "+" then
+ a(t("zpoz"))
+ a("{SEP}")
+ elseif data.typ_zpozdeni == "-" then
+ a(t("zpozz"))
+ a("{SEP}")
+ end
+ if not data.CIL then
+ a(t("vkonc"))
+ elseif data.NASL then
+ a(t("pokrnc"))
+ else
+ a(t("pokrc"))
+ end
+ if data.ODJZA then
+ a(t("ozasek"))
+ else
+ a(".")
+ end
+ a("{SEP}")
+ a(t("dek"))
+ data = table.copy(data)
+ data.SEP = "{SEP}"
+ local s = table.concat(parts)
+ s = assert(dosadit(s, data, defaults))
+ s = s:gsub(" *{SEP} *", "{SEP}")
+ while true do
+ t = s:gsub("{SEP}{SEP}", "{SEP}")
+ if t:len() == s:len() then
+ break
+ end
+ s = t
+ end
+ s = s:gsub("^{SEP}", "")
+ s = s:gsub("{SEP}$", "")
+ s = s:gsub("{SEP}", " ")
+ return s
+end
+
+local function get_or_add_anns(stn)
+ local station = advtrains.lines.stations[stn]
+ if station == nil then
+ return nil -- dopravna neexistuje!
+ end
+ local anns = station.anns
+ if anns == nil then
+ anns = {}
+ station.anns = anns
+ end
+ return anns
+end
+
+local cedule_default_settings = {
+ empty = "{-:5}",
+ fs = "",
+ pos = vector.zero(),
+ row = "{LINKA: 3} za {ODJZA: 2-3} s",
+ text = "{ZDE} - odjezdy\n{1}\n{2}",
+ text_rtf = {1, 2},
+}
+
+local function init_ann_data(stn, epos)
+ local anns = get_or_add_anns(stn)
+ if anns == nil then
+ return
+ end
+ local result = {
+ cedule = {
+ table.copy(cedule_default_settings), table.copy(cedule_default_settings),
+ table.copy(cedule_default_settings), table.copy(cedule_default_settings)},
+ chat_dosah = 50,
+ fmt_delay = "{}",
+ fmt_negdelay = "-{}",
+ fmt_nodelay = "",
+ fn_firstupper = false,
+ fs_koleje = "",
+ koleje = "",
+ owner = "",
+ version = 3,
+ }
+ anns[epos] = result
+ return result
+end
+
+local function get_ann_data(stn, epos, make_copy)
+ local anns = get_or_add_anns(stn)
+ if anns == nil then
+ core.log("error", "get_ann_data() called on non-existent stn '"..tostring(stn).."'!")
+ return nil
+ end
+ local ann = assert(anns[epos] or init_ann_data(stn, epos))
+ if make_copy then
+ ann = table.copy(ann)
+ local cedule = {}
+ for i, v in ipairs(ann.cedule) do
+ cedule[i] = table.copy(v)
+ end
+ ann.cedule = cedule
+ end
+ return ann
+end
+
+local function set_ann_data(stn, epos, data)
+ local anns = get_or_add_anns(stn)
+ if anns == nil then
+ return false
+ end
+ local ann = anns[epos]
+ if ann ~= nil then
+ for k, v in pairs(data) do
+ ann[k] = v
+ end
+ else
+ anns[epos] = table.copy(data)
+ end
+ return true
+end
+
+local function attach_sign(stn, epos, i, sign_pos)
+ -- zkontrolovat rozhlas:
+ local rozhl_pos = advtrains.decode_pos(epos)
+ core.load_area(rozhl_pos)
+ local rozhl_node = core.get_node(rozhl_pos)
+ if rozhl_node.name ~= rozhlas_node_name then
+ return false, "Staniční rozhlas se nenachází na očekávané pozici."
+ end
+ local rozhl_meta = core.get_meta(rozhl_pos)
+ if rozhl_meta:get_string("stn") ~= stn then
+ return false, "Staniční rozhlas přísluší k jiné dopravně." -- vnitřní chyba!
+ end
+ local data = get_ann_data(stn, epos, false)
+ if data == nil then
+ return false, "Data staničního rozhlasu nebyla nalezena."
+ end
+
+ -- zkontrolovat ceduli:
+ core.load_area(sign_pos)
+ local sign_node = core.get_node_or_nil(sign_pos)
+ if sign_node == nil or core.get_item_group(sign_node.name, "display_api") == 0 then
+ return false, "Toto není podporovaná cedule."
+ end
+
+ -- zkontrolovat vzdálenost:
+ if vector.distance(rozhl_pos, sign_pos) > 1024 then
+ return false, "Cedule je příliš daleko. Vzdálenost může být max. 1024 metrů."
+ end
+
+ -- zkontrolovat zadání:
+ local cedule = data.cedule[i]
+ if cedule == nil then
+ return false, "Chybný index."
+ end
+
+ -- připojit:
+ local s = string.format("%d,%d,%d", sign_pos.x, sign_pos.y, sign_pos.z)
+ local fs = F(s)
+ cedule.fs = fs
+ cedule.pos = sign_pos
+ return true, "Cedule úspěšně připojena ke staničnímu rozhlasu."
+end
+
+local function detach_sign(stn, epos, i)
+ -- zkontrolovat rozhlas:
+ local rozhl_pos = advtrains.decode_pos(epos)
+ core.load_area(rozhl_pos)
+ local rozhl_node = core.get_node(rozhl_pos)
+ if rozhl_node.name ~= rozhlas_node_name then
+ return false, "Staniční rozhlas se nenachází na očekávané pozici."
+ end
+ local rozhl_meta = core.get_meta(rozhl_pos)
+ if rozhl_meta:get_string("stn") ~= stn then
+ return false, "Staniční rozhlas přísluší k jiné dopravně." -- vnitřní chyba!
+ end
+ local data = get_ann_data(stn, epos, false)
+ if data == nil then
+ return false, "Data staničního rozhlasu nebyla nalezena."
+ end
+
+ -- zkontrolovat zadání:
+ local cedule = data.cedule[i]
+ if cedule == nil then
+ return false, "Chybný index."
+ end
+
+ if cedule.fs == "" then
+ return false, "Cedule není připojena."
+ end
+
+ -- odpojit:
+ cedule.fs = ""
+ cedule.pos = vector.zero()
+ return true, "Cedule úspěšně odpojena."
+end
+
+
+local function init_formspec_callback(custom_state, player, formname, fields)
+ local player_name = player:get_player_name()
+ local pos = custom_state.pos
+ local node = core.get_node(pos)
+ if node.name ~= rozhlas_node_name then
+ core.chat_send_player(player_name, "CHYBA: staniční rozhlas nenalezen!")
+ return
+ end
+ if not core.check_player_privs(player, "railway_operator") then
+ core.chat_send_player(player_name, "*** K instalaci staničního rozhlasu je nutné právo railway_operator!")
+ return
+ end
+ if fields.dopravna then
+ local event = core.explode_textlist_event(fields.dopravna)
+ local new_index = tonumber(event.index)
+ if new_index ~= nil and custom_state.list[new_index] ~= nil then
+ custom_state.selection_index = new_index
+ end
+ if event.type == "DCL" then
+ fields.zvolit_dopravnu = "true"
+ end
+ end
+ if fields.zvolit_dopravnu and custom_state.selection_index ~= nil then
+ local stn = assert(custom_state.list[custom_state.selection_index].stn)
+ local stdata = advtrains.lines.stations[stn]
+ if stdata ~= nil then
+ local meta = core.get_meta(pos)
+ meta:set_string("infotext", "staniční rozhlas ("..(stdata.name or "???")..")")
+ meta:set_string("owner", player_name)
+ meta:set_string("stn", stn)
+ init_ann_data(stn, custom_state.epos)
+ core.chat_send_player(player_name, "*** Úspěšně nastaveno.")
+ end
+ end
+end
+
+local function get_setup_formspec(custom_state)
+ local player_name = assert(custom_state.player_name)
+ local page = assert(custom_state.page)
+ local stations = assert(custom_state.stations)
+ local stn = custom_state.stn
+ local data = custom_state.data
+
+ local formspec = {
+ "formspec_version[6]"..
+ "size[15,16.5]"..
+ -- "style_type[textarea;font=mono]"..
+ "tabheader[0,0;0.75;tab;Nastavení 1,Nastavení 2",
+ ifthenelse(custom_state.is_admin, ",Vlastnictví;", ";"),
+ custom_state.page..";false;true]"..
+ "button_exit[14,0.25;0.75,0.75;close;X]"..
+ "item_image[0.5,0.5;1,1;"..rozhlas_node_name.."]"..
+ "label[1.75,1;staniční rozhlas]"..
+ "label[9,0.75;vlastník/ice:\n",
+ ch_core.prihlasovaci_na_zobrazovaci(custom_state.owner),
+ "]",
+ }
+ local function a(x)
+ if type(x) == "table" then
+ for _, s in ipairs(x) do
+ table.insert(formspec, s)
+ end
+ else
+ table.insert(formspec, x)
+ end
+ end
+ if page == PAGE_SETUP_1 then
+ a( "container[0.5,1.5]"..
+ "label[0,0.5;dopravna:]"..
+ "dropdown[1.75,0.2;5,0.6;dopravna;")
+ for i, station in ipairs(stations) do
+ a(ifthenelse(i ~= 1, ",", "")..F(station.stn).." | "..F(station.name))
+ end
+ a{";"..custom_state.station_index..";true]"..
+ "label[7,0.5;omezit jen na koleje:]"..
+ "field[10,0.2;4,0.6;koleje;;",
+ data.fs_koleje or "",
+ "]label[0,1.25;režim:]"..
+ "dropdown[1.75,1;3,0.6;rmode;odjezdy,příjezdy,odjezdy i příjezdy;",
+ tostring(data.rmode),
+ ";true]container_end[]",
+ -- ----
+ "container[0.5,3.25]"..
+ "checkbox[0,0.25;fn_firstupper;první písmeno řádky odjezdu vždy velké;",
+ ifthenelse(ifthenelse(custom_state.fn_firstupper ~= nil, custom_state.fn_firstupper, data.fn_firstupper), "true", "false"),
+ "]field[0,1;3.25,0.75;fmt_nodelay;bez zpoždění;", F(data.fmt_nodelay or ""), "]"..
+ "field[4,1;3.25,0.75;fmt_delay;zpoždění ({} = číslo);", F(data.fmt_delay or "{}"), "]"..
+ "field[8,1;3.25,0.75;fmt_negdelay;záp.zpoždění ({} = číslo);", F(data.fmt_negdelay or "-{}"), "]"..
+ "tooltip[fmt_nodelay;Text pro značku {ZPOZDENI} v případě\\, že vlak jede bez zpoždění.]"..
+ "tooltip[fmt_delay;Formát pro značku {ZPOZDENI} v případě\\, že vlak má (kladné) zpoždění.\n"..
+ "Značka {} se nahradí počtem sekund zpoždění.]"..
+ "tooltip[fmt_negdelay;Formát pro značku {ZPOZDENI} v případě\\, že vlak má záporné zpoždění.\n"..
+ "Značka {} se nahradí absolutní hodnotou počtu sekund záporného zpoždění.]"..
+ "container_end[]"..
+ "container[0.5,10.5]"..
+ "box[0,0.15;14,0.05;#000000FF]"}
+ --[[
+ -- hlášení do četu (zatím neimplementováno):
+ a{ "label[7,0.5;dosah hlášení v četu \\[m\\] (0=vyp):]"..
+ "field[11.5,0.25;2,0.5;chat_dosah;;",
+ tostring(data.chat_dosah or "50"),
+ "]"..
+ "tooltip[chat_dosah;Dosah je současně limitován doslechem jednotlivých hráčských postav.]"..
+ "label[0,0.5;texty pro hlášení příjezdu/přistavení vlaku v četu:]"..
+ "tablecolumns[text;text]"..
+ "table[0,0.75;14,2;texty;"}
+ for _, def in ipairs(hl_texty) do
+ local id = "tx_"..def.id
+ a(def.fs_sample)
+ a(",")
+ a(F(data[id] or def.default or def.sample))
+ a(",")
+ end
+ formspec[#formspec] = ";"..(custom_state.tx_index or "").."]"
+ a("label[0.1,3.125;text:]".."field[0.75,2.75;10,0.75;preklad;;")
+ if custom_state.tx_index ~= nil then
+ local def = assert(hl_texty[custom_state.tx_index])
+ local id = "tx_"..def.id
+ local s = data[id]
+ if s ~= nil then
+ a(F(s))
+ else
+ a(def.fs_default)
+ end
+ end
+ a( "]"..
+ "button[11,2.75;3,0.75;preklad_set;nastavit]"..
+ "tooltip[preklad;V levém sloupci tabulky jsou vzorové texty\\, v pravém sloupci jsou texty\\,\n"..
+ "které budou na jejich místě použity pro tento staniční rozhlas (ty můžete měnit).]"..
+ "textarea[0,3.5;14,1;;;příklad: ")
+ local hlaseni = sestavit_hlaseni(data, custom_state, {
+ LINKA = "S1",
+ VYCHOZI = "Praha hl. n.",
+ typ_zpozdeni = "+",
+ CIL = "Bratislava hl. st.",
+ NASL = "Pardubice hl. n.",
+ ODJZA = "60",
+ ZPOZDENI = "13",
+ JMVLAKU = "Testovací expres",
+ KOLEJ = "A",
+ })
+ a(F(hlaseni))
+ a("]")
+ ]]
+ a("container_end[]"--[[..
+ -- ----
+ "container[0.5,9.25]"..
+ "label[0,0.25;Formáty řádků ({1}, {2} atd.):]"..
+ "container_end[]" ]]
+ )
+ a("button[0.5,15;14,1;btn_save;Uložit]")
+ elseif page == PAGE_SETUP_2 then
+ a( "textarea[0.5,2;14,2;;;"..F("Pro nápovědu a použitou syntaxi viz wiki Českého hvozdu (článek „Staniční rozhlas“).").."]"..
+ "container[0.5,4]")
+ for i = 1, 4 do
+ local cedule = data.cedule[i]
+ local s = tostring(i)
+ a{ "container[0,", string.format("%f", 2.5 * (i - 1)),
+ "]label[0,0.3;cedule ", s, ":]"..
+ "field[0,0.9;9.75,0.75;fmt_cedule", s, "_row;formát řádky / prázdný řádek:;",
+ F(cedule.row),
+ "]field[0,1.75;9.75,0.75;fmt_cedule", s, "_empty;;", F(cedule.empty),
+ "]textarea[10,0.5;4,2;fmt_cedule", s, ";;"..F(cedule.text),
+ "]field[2,0;4.5,0.5;pos_cedule", s, ";;", cedule.fs,
+ "]button_exit[10,0;3,0.5;zam_cedule", s,
+ ";zaměřit...]button[6.75,0;3,0.5;",
+ ifthenelse(cedule.fs ~= "", "odp_cedule"..s..";odpojit", "pri_cedule"..s..";připojit"),
+ "]container_end[]",
+ }
+ end
+ a{ "container_end[]"..
+ "label[0.5,14.5;", CF(custom_state.message), "]"..
+ "button[0.5,15;14,1;btn_save;Uložit]",
+ }
+ elseif custom_state.is_admin and page == PAGE_OWNERSHIP then
+ a{
+ "field[0.5,2.5;5,0.75;owner;vlastník/ice:;",
+ ch_core.prihlasovaci_na_zobrazovaci(custom_state.owner),
+ "]button[5.75,2.5;3,0.75;set_owner;nastavit]"..
+ "label[0.5,13;",
+ CF(custom_state.message),
+ "]button_exit[0.5,15;14,1;close2;zavřít]",
+ }
+ end
+ return table.concat(formspec)
+end
+
+local function setup_formspec_callback(custom_state, player, formname, fields)
+ assert(player:get_player_name() == custom_state.player_name)
+ local node = core.get_node(custom_state.pos)
+ if node.name ~= rozhlas_node_name then
+ return
+ end
+ local is_admin = custom_state.is_admin
+ local page = custom_state.page
+ local stn = custom_state.stn
+ local data = custom_state.data
+
+ if page == PAGE_SETUP_1 then
+ if
+ fields.dopravna and
+ fields.dopravna ~= tostring(custom_state.station_index) and
+ tonumber(fields.dopravna) ~= nil and
+ custom_state.stations[tonumber(fields.dopravna)] ~= nil
+ then
+ -- přepnout dopravnu
+ custom_state.station_index = tonumber(fields.dopravna)
+ end
+ if fields.rmode then
+ local new_rmode = tonumber(fields.rmode)
+ if new_rmode ~= nil and new_rmode ~= data.rmode then
+ data.rmode = new_rmode
+ end
+ end
+
+ -- zaškrtávací pole:
+ if fields.fn_firstupper then
+ data.fn_firstupper = ifthenelse(fields.fn_firstupper == "true", true, false)
+ end
+
+ -- koleje:
+ if fields.koleje and F(fields.koleje) ~= data.fs_koleje then
+ local parts = string.split(fields.koleje, ",", false)
+ local list, set = {}, {}
+ for _, part in ipairs(parts) do
+ if not set[part] then
+ set[part] = true
+ table.insert(list, part)
+ end
+ end
+ if #list == 0 then
+ -- všechny koleje
+ data.fs_koleje = ""
+ data.koleje = ""
+ elseif #list == 1 then
+ data.fs_koleje = F(list[1])
+ data.koleje = list[1]
+ else
+ table.sort(list, function(a, b) return a < b end)
+ data.fs_koleje = F(table.concat(list, ","))
+ data.koleje = set
+ end
+ end
+ if fields.fmt_delay then
+ data.fmt_delay = fields.fmt_delay
+ end
+ if fields.fmt_nodelay then
+ data.fmt_nodelay = fields.fmt_nodelay
+ end
+ if fields.fmt_negdelay then
+ data.fmt_negdelay = fields.fmt_negdelay
+ end
+
+ -- texty
+ if fields.texty then
+ local event = core.explode_table_event(fields.texty)
+ if (event.type == "CHG" or event.type == "DCL") and event.row ~= custom_state.tx_index and hl_texty[event.row] ~= nil then
+ custom_state.tx_index = event.row
+ end
+ end
+
+ if fields.preklad_set and custom_state.tx_index then
+ local def = assert(hl_texty[custom_state.tx_index])
+ custom_state.data["tx_"..def.id] = assert(fields.preklad)
+ return get_setup_formspec(custom_state)
+ end
+ if fields.chat_dosah and tonumber(fields.chat_dosah) ~= nil then
+ local new_dosah = tonumber(fields.chat_dosah)
+ if new_dosah ~= nil and new_dosah == math.floor(new_dosah) and new_dosah >= 0 and new_dosah < 1024 then
+ custom_state.data.chat_dosah = new_dosah
+ end
+ end
+
+ -- uložit?
+ if fields.btn_save then
+ local new_stn = custom_state.stations[custom_state.station_index].stn
+ if new_stn ~= stn then
+ -- přesunout rozhlas na jinou dopravnu
+ local meta = assert(core.get_meta(custom_state.pos))
+ local old_anns = get_or_add_anns(stn)
+ local new_anns = get_or_add_anns(new_stn)
+ if old_anns ~= nil and new_anns ~= nil then
+ local epos = custom_state.epos
+ new_anns[epos] = old_anns[epos]
+ old_anns[epos] = nil
+ meta:set_string("stn", new_stn)
+ stn = new_stn
+ custom_state.stn = new_stn
+ end
+ end
+ set_ann_data(stn, custom_state.epos, data)
+ custom_state.data = get_ann_data(stn, custom_state.epos, true)
+ -- update infotext:
+ local meta = core.get_meta(custom_state.pos)
+ local station_name = al.get_station_name(meta:get_string("stn"))
+ local koleje = custom_state.data.fs_koleje
+ if koleje ~= "" then
+ koleje = " ["..koleje:gsub("\\", "").."]"
+ end
+ meta:set_string("infotext", "staniční rozhlas\n"..station_name..koleje)
+ end
+ elseif page == PAGE_SETUP_2 then
+ -- update fields:
+ for i = 1, 4 do
+ local cedule = data.cedule[i]
+ local s = fields["fmt_cedule"..i]
+ if s ~= nil then
+ cedule.text = s
+ cedule.text_rtf = ktere_radky(s, true)
+ end
+ s = fields["fmt_cedule"..i.."_row"]
+ if s ~= nil then
+ cedule.row = s
+ end
+ s = fields["fmt_cedule"..i.."_empty"]
+ if s ~= nil then
+ cedule.empty = s
+ end
+ end
+ if fields.btn_save then
+ local new_cedule = {}
+ for i = 1, 4 do
+ new_cedule[i] = table.copy(data.cedule[i])
+ end
+ get_ann_data(stn, custom_state.epos, false).cedule = new_cedule
+ return get_setup_formspec(custom_state)
+ else
+ for i = 1, 4 do
+ if fields["pri_cedule"..i] then
+ -- připojit ceduli:
+ local pos_cedule_str = fields["pos_cedule"..i]
+ local x, y, z = pos_cedule_str:match("^(-?%d+),(-?%d+),(-?%d+)$")
+ if x == nil or y == nil or z == nil then
+ custom_state.message = "Chybný formát pozice: "..pos_cedule_str
+ return get_setup_formspec(custom_state)
+ end
+ local pos_cedule = vector.new(tonumber(x), tonumber(y), tonumber(z))
+ local success, message = attach_sign(stn, custom_state.epos, i, pos_cedule)
+ if success then
+ local current_data = get_ann_data(stn, custom_state.epos, true)
+ if current_data ~= nil then
+ custom_state.data = current_data
+ end
+ custom_state.message = "Cedule úspěšně připojena."
+ else
+ custom_state.message = "CHYBA: "..message
+ end
+ return get_setup_formspec(custom_state)
+ end
+ if fields["odp_cedule"..i] then
+ local success, message = detach_sign(stn, custom_state.epos, i)
+ if success then
+ local current_data = get_ann_data(stn, custom_state.epos, true)
+ if current_data ~= nil then
+ custom_state.data = current_data
+ end
+ custom_state.message = "Cedule úspěšně odpojena."
+ else
+ custom_state.message = "CHYBA: "..message
+ end
+ return get_setup_formspec(custom_state)
+ end
+ if fields["zam_cedule"..i] then
+ local player_name = player:get_player_name()
+ local stack = player:get_wielded_item()
+ if stack:is_empty() then
+ player:set_wielded_item("advtrains_line_automation:sign_selector")
+ core.chat_send_player(player_name, "*** Levým klikem vyberte ceduli k připojení...")
+ else
+ player:get_inventory():add_item("main", "advtrains_line_automation:sign_selector")
+ core.chat_send_player(player_name, "*** Levým klikem výběrovým nástrojem vyberte ceduli k připojení...")
+ end
+ punch_context[player_name] = {stn = assert(custom_state.stn), epos = custom_state.epos, i = i}
+ return
+ end
+ end
+ end
+ elseif page == PAGE_OWNERSHIP then
+ if is_admin and fields.set_owner then
+ local new_owner = ch_core.jmeno_na_existujici_prihlasovaci(fields.owner or "")
+ if new_owner == nil then
+ custom_state.message = "Postava '"..(fields.owner or "").."' neexistuje!"
+ else
+ local ann_data = assert(get_ann_data(stn, custom_state.epos, false))
+ ann_data.owner = new_owner
+ core.load_area(custom_state.pos)
+ local meta = core.get_meta(custom_state.pos)
+ meta:set_string("owner", new_owner)
+ custom_state.owner = new_owner
+ end
+ return get_setup_formspec(custom_state)
+ end
+ end
+ if fields.tab then
+ -- přepnout stránku
+ local new_tab = tonumber(fields.tab)
+ if new_tab ~= nil and new_tab ~= page and (
+ new_tab == PAGE_SETUP_1 or new_tab == PAGE_SETUP_2 or (is_admin and new_tab == PAGE_OWNERSHIP)
+ ) then
+ custom_state.page = new_tab
+ custom_state.message = "" -- smazat zprávu
+ end
+ return get_setup_formspec(custom_state)
+ end
+ if not fields.quit then
+ return get_setup_formspec(custom_state)
+ end
+end
+
+local function fill(line, prefix, rwtime_now, rwtime_value)
+ if rwtime_value == nil then
+ return
+ end
+ local secs = math.ceil((rwtime_value - rwtime_now) / 5) * 5
+ if secs < 0 then
+ secs = -secs
+ line[prefix.."_Z"] = "-"
+ end
+ local n_s = secs % 60
+ local n_m = (secs - n_s) / 60
+ local s = tostring(n_s)
+ local m = tostring(n_m)
+ line[prefix] = tostring(secs)
+ line[prefix.."_M"] = m
+ line[prefix.."_S"] = s
+ if n_m < 10 then
+ line[prefix.."_MM"] = "0"..m
+ else
+ line[prefix.."_MM"] = m
+ end
+ if n_s < 10 then
+ line[prefix.."_SS"] = "0"..s
+ else
+ line[prefix.."_SS"] = s
+ end
+end
+
+--[[
+ stn -- kód dopravny, jejíž příjezdy/odjezdy se budou zapisovat na cedule
+ epos -- kódovaná pozice st. rozhlasu (pro načtení dat)
+ signs -- (volitelný) seznam až 4 pozic cedulí, které se budou aktualizovat
+ records -- viz níže
+ rwtime -- aktuální žel. čas (číselná forma)
+ ---
+ records je tabulka záznamů z al.predict_train(), doplněných o následující pole:
+ start = string or nil, -- název výchozí zastávky, pokud vlak odněkud přijíždí, jinak ""
+ destination = string, -- název cílové zastávky, pokud vlak někam pokračuje, jinak ""
+ prev_stop = string, -- název předchozí zastávky, pokud vlak odněkud přijíždí a pokud jde o jinou zastávku než 'start', jinak ""
+ next_stop = string, -- název násl. zast., pokud vlak pokračuje a pokud jde o jinou zastávku než 'destination', jinak ""
+ last_pos = string, -- název poslední známé polohy vlaku, nebo ""
+ --
+ stn = string, track = string, delay = int,
+ stdata = table or nil,
+ dep = int or nil, dep_linevar_def = table or nil, dep_index = int or nil,
+ arr = int or nil, arr_linevar_def = table or nil, arr_index = int or nil,
+]]
+
+local function update_ann(stn, epos, signs, records, rwtime)
+ local ann = get_ann_data(stn, epos)
+ if ann == nil then
+ core.log("error", "update_ann() called for "..stn.."/"..epos..", but ann is nil!")
+ return
+ end
+ local tracks
+ local any_line = {
+ ZDE = al.get_station_name(stn),
+ }
+ if ann.fs_koleje ~= "" then
+ any_line.KOLEJE = ann.fs_koleje:gsub("\\", "")
+ tracks = ann.koleje
+ if tracks ~= nil and type(tracks) ~= "table" then
+ if tracks == "" then
+ tracks = nil
+ else
+ tracks = {[tracks] = true}
+ end
+ end
+ end
+ if has_ch_time then
+ local cas = ch_time.aktualni_cas()
+ any_line.HH = string.format("%02d", cas.hodina)
+ any_line.MM = string.format("%02d", cas.minuta)
+ any_line.SS = string.format("%02d", cas.sekunda)
+ end
+ local lines = {}
+ for _, record in ipairs(records) do
+ assert(record.start ~= nil)
+ assert(record.destination ~= nil)
+ assert(record.prev_stop ~= nil)
+ assert(record.next_stop ~= nil)
+ assert(record.last_pos ~= nil)
+ if
+ (tracks == nil or tracks[record.track]) and (
+ (ann.rmode == RMODE_ARR and record.arr ~= nil) or
+ (ann.rmode == RMODE_DEP and record.dep ~= nil) or
+ (ann.rmode == RMODE_BOTH and (record.arr ~= nil or record.dep ~= nil))
+ )
+ then
+ local linevar_def, index
+ if record.dep ~= nil then
+ linevar_def, index = record.dep_linevar_def, record.dep_index
+ else
+ linevar_def, index = record.arr_linevar_def, record.arr_index
+ end
+ local stops = linevar_def.stops
+ local line = setmetatable({}, {__index = any_line})
+ line.LINKA = advtrains.lines.linevar_decompose(linevar_def.name)
+ assert(line.LINKA)
+ if record.start ~= "" then
+ line.VYCHOZI = record.start
+ end
+ if record.destination ~= "" then
+ line.CIL = record.destination
+ end
+ if record.track ~= "" then
+ line.KOLEJ = record.track
+ end
+ fill(line, "PRIJZA", rwtime, record.arr)
+ fill(line, "ODJZA", rwtime, record.dep)
+ local abs_delay = math.abs(record.delay)
+ if abs_delay < 5 then
+ line.ZPOZDENI = ann.fmt_nodelay or ""
+ else
+ line.ZPOZDENI = dosadit(
+ ifthenelse(record.delay > 0, ann.fmt_delay or "{}", ann.fmt_negdelay or "-{}"),
+ {[""] = tostring(5 * math.ceil(abs_delay / 5))}
+ )
+ end
+ -- PREDCH
+ if record.prev_stop ~= "" then
+ line.PREDCH = record.prev_stop
+ end
+ -- NASL
+ if record.next_stop ~= "" then
+ line.NASL = record.next_stop
+ end
+ -- POLOHA
+ if record.last_pos ~= "" then
+ line.POLOHA = record.last_pos
+ end
+ -- JMVLAKU
+ if linevar_def.train_name ~= nil then
+ line.JMVLAKU = linevar_def.train_name
+ end
+ -- TYP
+ line.TYP = "Os"
+ -- TYPVLAKU
+ line.TYPVLAKU = "osobní vlak"
+ table.insert(lines, line)
+ end
+ end
+ if signs ~= nil then
+ local empty_table = {}
+ for i = 1, 4 do
+ local cedule = ann.cedule[i]
+ local sign_pos = signs[i]
+ if sign_pos ~= nil and core.compare_block_status(sign_pos, "active") then
+ local formatted_lines = setmetatable({}, {__index = any_line})
+ if type(cedule.text_rtf) == "table" then
+ for _, i_row in ipairs(cedule.text_rtf) do
+ if lines[i_row] == nil then
+ -- prázdný řádek
+ formatted_lines[tostring(i_row)] = dosadit(cedule.empty, empty_table)
+ else
+ -- řádek odjezdu
+ local s = dosadit(cedule.row, lines[i_row])
+ if s ~= "" and ann.fn_firstupper then
+ local l = ch_core.utf8_seek(s, 1, 1)
+ if l == nil then
+ s = ch_core.na_velka_pismena(s)
+ else
+ local c = s:sub(1, l - 1)
+ local uc = ch_core.na_velka_pismena(c)
+ if uc ~= c then
+ s = uc..s:sub(l, -1)
+ end
+ end
+ end
+ formatted_lines[tostring(i_row)] = s
+ end
+ end
+ end
+ local s = dosadit(cedule.text, formatted_lines)
+ if has_signs_api then
+ signs_api.set_display_text(sign_pos, s)
+ end
+ end
+ end
+ end
+end
+
+local globalstep_time = -5
+local first_run = true
+
+local function first_globalstep()
+ for stn, stdata in pairs(advtrains.lines.stations) do
+ local anns = stdata.anns
+ if stdata.anns ~= nil then
+ for rozh_epos, ann in pairs(stdata.anns) do
+ if ann.version < 2 then
+ -- upgrade version 1 to version 2:
+ ann.rmode = RMODE_DEP
+ ann.version = 2
+ end
+ if ann.version < 3 then
+ local pos = advtrains.decode_pos(rozh_epos)
+ core.load_area(pos)
+ local meta = core.get_meta(pos)
+ ann.owner = meta:get_string("owner")
+ core.log("action", "strozhlas at position "..core.pos_to_string(pos).." upgraded to metadata version 3 (owner "..ann.owner..")")
+ ann.version = 3
+ end
+ end
+ end
+ end
+end
+
+local function get_start_by_linevar_def(cache, linevar_def)
+ local result = cache[linevar_def.name]
+ if result == nil then
+ result = al.get_line_description(linevar_def, {first_stop = true, last_stop = false, last_stop_prefix = ""})
+ if result == "???" then
+ result = ""
+ end
+ cache[linevar_def.name] = result
+ end
+ return result
+end
+
+local function get_destination_by_linevar_def(cache, linevar_def)
+ local result = cache[linevar_def.name]
+ if result == nil then
+ result = al.get_line_description(linevar_def, {first_stop = false, last_stop = true, last_stop_prefix = ""})
+ if result == "???" then
+ result = ""
+ end
+ cache[linevar_def.name] = result
+ end
+ return result
+end
+
+local function get_name_by_stn(cache, stn, alt)
+ local result = cache[stn]
+ if result == nil then
+ result = al.get_station_name(stn)
+ if result == "???" then
+ result = ""
+ end
+ cache[stn] = result
+ end
+ if result == "" then
+ return alt
+ else
+ return result
+ end
+end
+
+local function globalstep(dtime)
+ globalstep_time = globalstep_time + dtime
+ if globalstep_time < 0 then
+ return
+ end
+ globalstep_time = globalstep_time - 5
+
+ if first_run then
+ first_run = false
+ return first_globalstep()
+ end
+
+ local rwtime = rwt.to_secs(rwt.get_time())
+ local rwtime_limit = rwtime + 3600
+ -- Shromáždit rozhlasy:
+ local subscriptions = {--[[
+ [stn] = {{
+ rozh_pos = vector,
+ rozh_epos = string,
+ rozh_def = table,
+ signs = {[1..4] = vector or nil} or nil,
+ }...}
+ ]]}
+ local signs = {}
+ for stn, stdata in pairs(advtrains.lines.stations) do
+ local anns = stdata.anns
+ if stdata.anns ~= nil then
+ for rozh_epos, ann in pairs(stdata.anns) do
+ local rozh_pos = advtrains.decode_pos(rozh_epos)
+ local is_admin = core.check_player_privs(assert(ann.owner), "protection_bypass")
+ local signs_count = 0
+ for i = 1, 4 do
+ local cedule = assert(ann.cedule[i])
+ local s = cedule.fs
+ if s ~= nil and s ~= "" then
+ local sign_pos = assert(cedule.pos)
+ if core.compare_block_status(sign_pos, "active") then
+ signs[i] = sign_pos
+ signs_count = signs_count + 1
+ if not is_admin then
+ local sign_meta = core.get_meta(sign_pos)
+ local sign_owner = sign_meta:get_string("owner")
+ if sign_meta:get_int("access_level") ~= 1 and sign_owner ~= "" and ann.owner ~= sign_owner then
+ -- unauthorized access to the sign
+ core.log("warning", "Station announcement "..core.pos_to_string(rozh_pos).." owned by '"..
+ ann.owner.."' cannot write to sign at "..core.pos_to_string(sign_pos).." owned by '"..
+ sign_owner.."'! Access denied.")
+ signs[i] = nil
+ signs_count = signs_count - 1
+ end
+ end
+ end
+ end
+ end
+ if signs_count > 0 then
+ -- nějaké cedule jsou aktivní
+ table.insert(goa(subscriptions, stn), {rozh_pos = rozh_pos, rozh_epos = rozh_epos, rozh_def = ann, signs = signs})
+ signs = {}
+ elseif core.compare_block_status(rozh_pos, "active") then
+ -- cedule nejsou aktivní, ale rozhlas ano
+ table.insert(goa(subscriptions, stn), {rozh_pos = rozh_pos, rozh_epos = rozh_epos, rozh_def = ann})
+ end
+ end
+ end
+ end
+
+ -- Shromáždit vlaky:
+ local by_stn = {}
+ for stn, _ in pairs(subscriptions) do
+ by_stn[stn] = {}
+ end
+ local start_by_linevar = {}
+ local destination_by_linevar = {}
+ local name_by_stn = {}
+
+ for _, train in pairs(advtrains.trains) do
+ local ls, linevar_def = al.get_line_status(train)
+ if linevar_def ~= nil then
+ local prediction = al.predict_train(ls, linevar_def, rwtime, true)
+ local last_pos = al.get_last_pos_station_name(ls) or ""
+ for i, record in ipairs(prediction) do
+ local records = by_stn[record.stn]
+ if
+ records ~= nil and
+ (record.dep ~= nil or record.arr ~= nil) and
+ (record.dep == nil or record.dep < rwtime_limit) and
+ (record.arr == nil or record.arr < rwtime_limit)
+ then
+ -- nutno doplnit: start, destination, prev_stop, next_stop, last_pos
+ record.last_pos = last_pos -- název poslední dopravny, kde byl vlak spatřen
+ if record.final and record.arr ~= nil and record.dep ~= nil and record.arr_linevar_def.name ~= record.dep_linevar_def.name then
+ -- změna linky => rozdělit na příjezd a odjezd
+ local record2 = table.copy(record)
+ record2.dep = nil
+ record2.dep_linevar_def = nil
+ record2.dep_index = nil
+ record2.start = get_start_by_linevar_def(start_by_linevar, record.arr_linevar_def)
+ local other_index, other_data = al.get_prev_stop(record.arr_linevar_def, record.arr_index, false)
+ if other_index ~= nil then
+ record2.prev_stop = get_name_by_stn(name_by_stn, other_data.stn, "")
+ end
+ record2.next_stop = ""
+ record2.destination = get_destination_by_linevar_def(destination_by_linevar, record.arr_linevar_def)
+ table.insert(records, record2)
+ record.arr = nil
+ record.arr_linevar_def = nil
+ record.arr_index = nil
+ record.start = get_start_by_linevar_def(start_by_linevar, record.dep_linevar_def)
+ record.prev_stop = ""
+ other_index, other_data = al.get_next_stop(record.dep_linevar_def, record.dep_index, false)
+ if other_index ~= nil then
+ record.next_stop = get_name_by_stn(name_by_stn, other_data.stn, "")
+ end
+ record.destination = get_destination_by_linevar_def(destination_by_linevar, record.dep_linevar_def)
+ table.insert(records, record)
+ elseif record.dep ~= nil then
+ -- odjezd nebo příjezd/odjezd (ale na stejné lince!)
+ local linevar_def = record.dep_linevar_def
+ local index = record.dep_index
+ record.start = get_start_by_linevar_def(start_by_linevar, linevar_def)
+ record.destination = get_destination_by_linevar_def(destination_by_linevar, linevar_def)
+ record.prev_stop = ""
+ record.next_stop = ""
+ local other_index, other_data
+ if record.arr ~= nil then
+ other_index, other_data = al.get_prev_stop(linevar_def, index, false)
+ if other_index ~= nil then
+ record.prev_stop = get_name_by_stn(name_by_stn, other_data.stn, "")
+ end
+ end
+ other_index, other_data = al.get_next_stop(linevar_def, index, false)
+ if other_index ~= nil then
+ record.next_stop = get_name_by_stn(name_by_stn, other_data.stn, "")
+ end
+ record.destination = get_destination_by_linevar_def(destination_by_linevar, linevar_def)
+ table.insert(records, record)
+ elseif record.arr ~= nil then
+ -- jen příjezd
+ local linevar_def = record.arr_linevar_def
+ local index = record.arr_index
+ record.start = get_start_by_linevar_def(start_by_linevar, linevar_def)
+ local other_index, other_data = al.get_prev_stop(linevar_def, index, false)
+ if other_index ~= nil then
+ record.prev_stop = get_name_by_stn(name_by_stn, other_data.stn, "")
+ else
+ record.prev_stop = ""
+ end
+ record.next_stop = ""
+ record.destination = get_destination_by_linevar_def(destination_by_linevar, linevar_def)
+ table.insert(records, record)
+ end
+ end
+ end
+ end
+ end
+
+ -- Aktualizovat rozhlasy:
+ for stn, records in pairs(by_stn) do
+ local deps = records
+ local arrs = table.copy(records)
+ table.sort(deps, function(a, b)
+ return assert(a.dep or a.arr) < assert(b.dep or b.arr)
+ end)
+ table.sort(arrs, function(a, b)
+ return (a.arr or 1.0e+100) < (b.arr or 1.0e+100)
+ end)
+ for i = #arrs, 1, -1 do
+ if arrs[i].arr == nil then
+ arrs[i] = nil -- ponechat jen příjezdy v rámci nejbližší hodiny
+ end
+ end
+ for _, ann in ipairs(subscriptions[stn]) do
+ if ann.rmode ~= RMODE_ARR then
+ update_ann(assert(stn), assert(ann.rozh_epos), ann.signs, deps, rwtime)
+ else
+ update_ann(assert(stn), assert(ann.rozh_epos), ann.signs, arrs, rwtime)
+ end
+ end
+ end
+end
+
+core.register_globalstep(globalstep)
+
+local function can_dig(pos, player)
+ if player ~= nil and core.is_player(player) then
+ local owner = core.get_meta(pos):get_string("owner")
+ local player_name = player:get_player_name()
+ return owner == "" or owner == player_name or core.check_player_privs(player_name, "protection_bypass")
+ end
+ return false
+end
+
+local function on_construct(pos)
+ local meta = core.get_meta(pos)
+ meta:set_string("infotext", "staniční rozhlas")
+end
+
+local function on_destruct(pos)
+ local meta = core.get_meta(pos)
+ local stn = meta:get_string("stn")
+ if stn ~= "" then
+ local epos = advtrains.encode_pos(pos)
+ local stdata = advtrains.lines.stations[stn]
+ if stdata ~= nil then
+ (stdata.anns or {})[epos] = nil -- odstranit data staničního rozhlasu
+ end
+ end
+end
+
+local function on_dig(pos, node, digger)
+ -- TODO?
+ -- return core.node_dig(pos, node, digger)
+ if not can_dig(pos, digger) then
+ return false
+ end
+ if has_unifieddyes then
+ return unifieddyes.on_dig(pos, node, digger)
+ else
+ return core.node_dig(pos, node, digger)
+ end
+end
+
+local function on_rightclick(pos, node, clicker, itemstack, pointed_thing)
+ if clicker == nil or not clicker:is_player() then
+ return
+ end
+ local player_name = assert(clicker:get_player_name())
+ local meta = core.get_meta(pos)
+ local owner = meta:get_string("owner")
+ local stn = meta:get_string("stn")
+ local epos = advtrains.encode_pos(pos)
+ local stdata = advtrains.lines.stations[stn]
+ if owner == "" or stn == "" or stdata == nil or stdata.anns == nil or stdata.anns[epos] == nil then
+ if not core.check_player_privs(clicker, "railway_operator") then
+ core.chat_send_player(player_name, "*** K instalaci staničního rozhlasu je nutné právo railway_operator!")
+ return
+ end
+ local limit = 256 * 256
+ local stations = advtrains.lines.load_stations_for_formspec()
+ local list = {}
+ for i, station_data in ipairs(stations) do
+ local d2 = limit
+ for _, stop in ipairs(station_data.tracks) do
+ local d2new = dist2(pos, stop.pos)
+ if d2new < d2 then
+ d2 = d2new
+ end
+ end
+ if d2 < limit then
+ table.insert(list, {
+ stn = assert(station_data.stn),
+ name = assert(station_data.name),
+ distance = math.floor(math.sqrt(d2)),
+ })
+ end
+ end
+ table.sort(list, function(a, b) return a.distance < b.distance end)
+ local custom_state = {
+ pos = pos,
+ epos = advtrains.encode_pos(pos),
+ list = list,
+ }
+ local formspec = {
+ "formspec_version[6]"..
+ "size[15,8]"..
+ "button_exit[14,0.25;0.75,0.75;close;X]"..
+ "item_image[0.5,0.5;1,1;"..rozhlas_node_name.."]"..
+ "label[1.75,1;staniční rozhlas]"..
+ "label[0.5,2.25;vyberte dopravnu:]"..
+ "button_exit[0.5,6.75;14,1;zvolit_dopravnu;Zvolit]"..
+ "textlist[0.5,2.5;14,4;dopravna;",
+ }
+ if #list > 0 then
+ for i, candidate in ipairs(list) do
+ if i ~= 1 then
+ table.insert(formspec, ",")
+ end
+ table.insert(formspec, F(candidate.stn.." | "..candidate.name.." ("..candidate.distance.." m)"))
+ end
+ custom_state.selection_index = 1
+ table.insert(formspec, ";1]")
+ else
+ table.insert(formspec, ";]")
+ end
+ ch_core.show_formspec(clicker, "advtrains_line_automation:init_rozhlas", table.concat(formspec), init_formspec_callback, custom_state, {})
+ return
+ end
+ if player_name ~= owner and not core.check_player_privs(player_name, "protection_bypass") then -- train_admin?
+ core.chat_send_player(player_name, "Nemáte oprávnění nastavovat tento staniční rozhlas!")
+ return
+ end
+ local stations = advtrains.lines.load_stations_for_formspec()
+ local selection_index
+ for i, station in ipairs(stations) do
+ if station.stn == stn then
+ selection_index = i
+ break
+ end
+ end
+ if selection_index == nil then
+ core.log("error", "Cannot determine selection index for the station '"..stn.."' in the list of stations!")
+ core.chat_send_player(player_name, "CHYBA: Vnitřní chyba: dopravna nenalezena ve výpisu!")
+ return
+ end
+ local custom_state = {
+ player_name = player_name,
+ is_admin = ch_core.get_player_role(player_name) == "admin",
+ pos = pos,
+ epos = epos,
+ owner = owner,
+ stn = assert(stn), -- kam byl staniční rozhlas dosud přiřazen
+ stations = stations, -- pro dropdown[]
+ station_index = selection_index, -- pro dropdown[]
+ data = assert(get_ann_data(stn, epos, true)),
+ page = PAGE_SETUP_1,
+ }
+ ch_core.show_formspec(clicker, "advtrains_line_automation:rozhlas", get_setup_formspec(custom_state),
+ setup_formspec_callback, custom_state, {})
+end
+
+local function selector_on_use(itemstack, user, pointed_thing)
+ if pointed_thing.type ~= "node" then
+ return
+ end
+ if user ~= nil then
+ local player_name = user:get_player_name()
+ local pc = punch_context[player_name]
+ if pc ~= nil then
+ local success, message = attach_sign(assert(pc.stn), assert(pc.epos), assert(pc.i), assert(pointed_thing.under))
+ if not success then
+ message = message.." Zaměřování bude zrušeno."
+ end
+ core.chat_send_player(player_name, message)
+ end
+ end
+ return ItemStack()
+end
+
+local box = {
+ type = "fixed",
+ fixed = {
+ {-0.125, 0.125, 0.25, 0.125, 0.5, 0.5},
+ {-0.25, 0.05, 0, 0.25, 0.45, 0.25},
+ },
+}
+
+def = {
+ description = "staniční rozhlas",
+ tiles = {{name = "default_steel_block.png", backface_culling = true}},
+ drawtype = "mesh",
+ mesh = "advtrains_tuber.obj",
+ paramtype = "light",
+ paramtype2 = "4dir",
+ selection_box = box,
+ collision_box = box,
+ groups = {oddly_breakable_by_hand = 3, ud_param2_colorable = 1},
+ sounds = default.node_sound_metal_defaults(),
+ can_dig = can_dig,
+ on_dig = on_dig,
+ on_construct = on_construct,
+ on_destruct = on_destruct,
+ on_rightclick = on_rightclick,
+}
+if has_unifieddyes then
+ def.paramtype2 = "color4dir"
+ def.palette = "unifieddyes_palette_color4dir.png"
+end
+core.register_node(rozhlas_node_name, def)
+
+def = {
+ description = "levým klikem zvolte ceduli pro připojení",
+ groups = {not_in_creative_inventory = 1, tool = 1},
+ inventory_image = "[combine:16x16:7,0=blank.png\\^[noalpha\\^[resize\\:2x16:0,7=blank.png\\^[noalpha\\^[resize\\:16x2",
+ wield_image = "blank.png",
+ on_use = selector_on_use,
+}
+
+core.register_tool("advtrains_line_automation:sign_selector", def)
+
+core.register_craft({
+ output = rozhlas_node_name,
+ recipe = {
+ {"default:steel_ingot", "default:steel_ingot", "default:steel_ingot"},
+ {"default:steel_ingot", "mesecons_noteblock:noteblock", "default:steel_ingot"},
+ {"default:steel_ingot", "default:steel_ingot", "default:steel_ingot"},
+ },
+})
diff --git a/advtrains_line_automation/station_editor.lua b/advtrains_line_automation/station_editor.lua
new file mode 100644
index 0000000..ceeeffe
--- /dev/null
+++ b/advtrains_line_automation/station_editor.lua
@@ -0,0 +1,1054 @@
+local def
+local F = minetest.formspec_escape
+local ifthenelse = function(cond, a, b) if cond then return a else return b end end
+local rwt = assert(advtrains.lines.rwt)
+
+local function load_stations()
+ local result = {}
+ local by_stn = {}
+ local all_lines_set = {["*"] = true}
+ local lines_set = {}
+ for stn, data in pairs(advtrains.lines.stations) do
+ local new_station = {
+ stn = stn,
+ name = data.name or "",
+ owner = data.owner or "",
+ tracks = {},
+ }
+ by_stn[stn] = new_station
+ lines_set[stn] = {}
+ table.insert(result, new_station)
+ end
+ for epos, data in pairs(advtrains.lines.stops) do
+ if data.stn ~= nil and data.stn ~= "" and by_stn[data.stn] ~= nil then
+ local st = by_stn[data.stn]
+ local new_track = {
+ epos = epos,
+ pos = assert(advtrains.decode_pos(epos)),
+ track = data.track or "",
+ }
+ table.insert(st.tracks, new_track)
+ local ars = data.ars
+ if ars ~= nil then
+ if ars.default then
+ lines_set[data.stn] = all_lines_set
+ else
+ local set = lines_set[data.stn]
+ for _, rule in ipairs(ars) do
+ if not rule.n and rule.ln ~= nil then
+ set[rule.ln] = true
+ end
+ end
+ end
+ end
+ end
+ end
+ for _, st in ipairs(result) do
+ local set = lines_set[st.stn]
+ if set["*"] then
+ st.lines = {"*"}
+ else
+ local lines = {}
+ for line, _ in pairs(set) do
+ table.insert(lines, line)
+ end
+ table.sort(lines)
+ st.lines = lines
+ end
+ end
+ table.sort(result, function(a, b) return a.stn < b.stn end)
+ return result
+end
+
+advtrains.lines.load_stations_for_formspec = load_stations
+
+local function station_distance(player_pos, st)
+ if st.tracks[1] == nil then
+ return -- no tracks
+ end
+ local distance = vector.distance(player_pos, st.tracks[1].pos)
+ for i = 2, #st.tracks do
+ local d2 = vector.distance(player_pos, st.tracks[i].pos)
+ if d2 < distance then
+ distance = d2
+ end
+ end
+ return distance
+end
+
+local function station_distance_s(player_pos, st)
+ local result = station_distance(player_pos, st)
+ if result ~= nil then
+ return string.format("%d m", result)
+ else
+ return ""
+ end
+end
+
+
+local function filter_all()
+ return true
+end
+
+local function filter_mine(a, player_info)
+ return a.owner == player_info.name
+end
+
+local function sort_by_distance(a, b, player_info)
+ return (station_distance(player_info.pos, a) or 1.0e+20) < (station_distance(player_info.pos, b) or 1.0e+20)
+end
+
+local function sort_by_stn(a, b)
+ return tostring(a.stn) < tostring(b.stn)
+end
+
+local function sort_by_name(a, b)
+ local a_key, b_key = ch_core.utf8_radici_klic(a.name, false), ch_core.utf8_radici_klic(b.name, false)
+ if a_key ~= b_key then
+ return a_key < b_key
+ else
+ return sort_by_stn(a, b)
+ end
+end
+
+local filters = {
+ {
+ description = "od nejbližší",
+ -- filter = filter_all,
+ sorter = sort_by_distance,
+ },
+ {
+ description = "podle kódu A-Z",
+ -- filter = filter_all,
+ sorter = sort_by_stn,
+ },
+ {
+ description = "podle názvu A-Z",
+ -- filter = filter_all,
+ sorter = sort_by_name,
+ },
+ {
+ description = "moje (podle kódu A-Z)",
+ filter = filter_mine,
+ sorter = sort_by_stn,
+ },
+ {
+ description = "moje (podle názvu A-Z)",
+ filter = filter_mine,
+ sorter = sort_by_name,
+ },
+ {
+ description = "moje (od nejbližší)",
+ filter = filter_mine,
+ sorter = sort_by_distance,
+ },
+}
+
+
+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 player_info = {
+ name = assert(custom_state.player_name),
+ pos = assert(custom_state.player_pos),
+ }
+
+ local stations = custom_state.stations
+ if stations == nil then
+ local filter = filters[custom_state.active_filter]
+ local filter_func = filter.filter or filter_all
+ local sorter_func = filter.sorter
+ stations = {}
+ for _, st in ipairs(load_stations()) do
+ if filter_func(st, player_info) then
+ table.insert(stations, st)
+ end
+ end
+ table.sort(stations, function(a, b) return sorter_func(a, b, player_info) end)
+ custom_state.stations = stations
+ if custom_state.selection_index ~= nil and custom_state.selection_index > #stations + 1 then
+ custom_state.selection_index = nil
+ end
+ end
+
+ local selection_index = custom_state.selection_index
+ local formspec = {
+ ch_core.formspec_header({formspec_version = 6, size = {20, 10}, auto_background = true}),
+ "label[0.5,0.6;Editor dopraven]",
+ }
+
+ table.insert(formspec, "tablecolumns[image,"..
+ "0=ch_core_empty.png,"..
+ "1=basic_materials_padlock.png\\^[resize:16x16"..
+ ";text;text,width=25;color,span=1;text,width=7;text;text]"..
+ "table[0.5,1.25;19,5;dopravna;0,KÓD,NÁZEV,#ffffff,SPRAVUJE,VZDÁLENOST,INFO")
+ for _, st in ipairs(stations) do
+ local n_tracks = #st.tracks
+ table.insert(formspec, ",0,"..F(st.stn)..","..F(st.name)..",#ffffff,"..F(ch_core.prihlasovaci_na_zobrazovaci(st.owner))..","..
+ F(station_distance_s(custom_state.player_pos, st))..","..n_tracks.." kolej")
+ if n_tracks < 1 or n_tracks > 4 then
+ table.insert(formspec, "í")
+ elseif n_tracks ~= 1 then
+ table.insert(formspec, "e")
+ end
+ if n_tracks > 0 then
+ table.insert(formspec, "\\, linky " ..F(table.concat(st.lines, ",")))
+ end
+ end
+ table.insert(formspec, ";"..(selection_index or "").."]")
+
+ -- rozbalovací pole s volbou filtru a řazení
+ table.insert(formspec, "dropdown[8,0.3;7,0.6;filter;"..F(assert(filters[1].description)))
+ for i = 2, #filters do
+ table.insert(formspec, ","..F(filters[i].description))
+ end
+ table.insert(formspec, ";"..custom_state.active_filter..";true]")
+
+ table.insert(formspec, "button_exit[18.75,0.3;0.75,0.75;close;X]")
+
+ local st = selection_index ~= nil and stations[selection_index - 1]
+ local stn, nazev, spravuje
+ if st then
+ stn = F(st.stn)
+ nazev = F(st.name)
+ spravuje = F(ch_core.prihlasovaci_na_zobrazovaci(st.owner))
+ else
+ stn, nazev, spravuje = "", "", F(pinfo.viewname)
+ end
+ table.insert(formspec,
+ "field[0.5,7;2.5,0.75;stn;kód:;"..stn.."]"..
+ "field[3.25,7;7,0.75;name;název:;"..nazev.."]"..
+ ifthenelse(pinfo.role == "admin", "field[10.5,7;4,0.75;owner;spravuje:;", "label[10.5,6.75;spravuje:\n")..
+ spravuje.."]")
+ if pinfo.role ~= "new" then
+ table.insert(formspec, "button[0.5,8;4.5,0.75;vytvorit;vytvořit novou]"..
+ "button[10,8;4.5,0.75;jrad;jízdní řády...]")
+ if st and (pinfo.role == "admin" or st.owner == pinfo.player_name) then
+ table.insert(formspec, "button[5.25,8;4.5,0.75;ulozit;uložit změny]")
+ if st.tracks[1] == nil then
+ table.insert(formspec, "button[15.25,8;3,0.75;smazat;smazat]")
+ end
+ if custom_state.linevars[1] ~= nil then
+ table.insert(formspec, "label[0.5,9.4;přiřadit kolej]"..
+ "field[2.75,9.1;1,0.6;kolej;;]"..
+ "label[3.9,9.4;lince]"..
+ "dropdown[5,9.1;5,0.6;klinevar;")
+ for i, lvar in ipairs(custom_state.linevars) do
+ if i ~= 1 then
+ table.insert(formspec, ",")
+ end
+ table.insert(formspec, F(lvar.linevar.." | "..lvar.dep.." "..stn.." ["..lvar.track.."]"))
+ end
+ table.insert(formspec, ";"..custom_state.current_linevar..";true]"..
+ "button[10.25,9;4.25,0.75;priradit_kolej;přiřadit]"..
+ "tooltip[klinevar;")
+ table.insert(formspec, F("Vysvětlení formátu:\n<linka>/<kód vých.dop.>/<sm.kód> | <odjezd> <kód dop.> [<stávající kolej>]"))
+ table.insert(formspec, "]")
+ end
+ end
+ end
+ if st and st.tracks[1] ~= nil then
+ table.insert(formspec, "textarea[14.75,7;4.75,2.5;;pozice kolejí:;"..F(minetest.pos_to_string(st.tracks[1].pos)))
+ for i = 2, #st.tracks do
+ table.insert(formspec, "\n"..F(minetest.pos_to_string(st.tracks[i].pos)))
+ end
+ table.insert(formspec, "]")
+ end
+ return table.concat(formspec)
+end
+
+local function formspec_callback(custom_state, player, formname, fields)
+ local update_formspec = false
+ if fields.vytvorit then
+ local new_stn, new_name, new_owner = fields.stn, fields.name or "", fields.owner
+ local pinfo = ch_core.normalize_player(player)
+ if pinfo.role ~= "admin" or new_owner == nil or new_owner == "" then
+ new_owner = pinfo.player_name
+ else
+ new_owner = ch_core.jmeno_na_prihlasovaci(new_owner)
+ end
+ if new_stn == nil or new_stn == "" then
+ ch_core.systemovy_kanal(custom_state.player_name, "CHYBA: kód nesmí být prázdný!")
+ return
+ end
+ local als = advtrains.lines.stations
+ if als[new_stn] ~= nil then
+ ch_core.systemovy_kanal(custom_state.player_name, "CHYBA: zastávka s kódem "..new_stn.." již existuje!")
+ return
+ end
+ als[new_stn] = {name = assert(new_name), owner = assert(new_owner)}
+ custom_state.stations = nil
+ update_formspec = true
+ ch_core.systemovy_kanal(custom_state.player_name, "Dopravna úspěšně vytvořena.")
+ elseif fields.ulozit then
+ local new_stn, new_name, new_owner = fields.stn, fields.name or "", fields.owner
+ local pinfo = ch_core.normalize_player(player)
+ local st = custom_state.stations[(custom_state.selection_index or 0) - 1]
+ if st == nil then
+ ch_core.systemovy_kanal(custom_state.player_name, "CHYBA: není vybrána žádná zastávka!")
+ return
+ end
+ local change_stn, change_name = st.stn ~= new_stn, st.name ~= new_name
+ local change_owner = pinfo.role == "admin" and fields.owner ~= nil and fields.owner ~= "" and
+ ch_core.jmeno_na_prihlasovaci(fields.owner) ~= st.owner
+ if not change_stn and not change_name and not change_owner then
+ ch_core.systemovy_kanal(custom_state.player_name, "Nic nezměněno.")
+ return
+ end
+ local t = advtrains.lines.stations[st.stn]
+ if t == nil then
+ ch_core.systemovy_kanal(custom_state.player_name, "CHYBA: zastávka k úpravě nebyla nalezena! Toto je pravděpodobně vnitřní chyba editoru.")
+ return
+ end
+ if change_stn then
+ -- zkontrolovat, že cílový kód je volný
+ if advtrains.lines.stations[new_stn] ~= nil then
+ ch_core.systemovy_kanal(custom_state.player_name, "CHYBA: zastávka s kódem "..new_stn.." již existuje!")
+ return
+ end
+ end
+ if change_owner then
+ t.owner = ch_core.jmeno_na_prihlasovaci(fields.owner)
+ ch_core.systemovy_kanal(custom_state.player_name, "Správa zastávky změněna.")
+ end
+ if change_name then
+ t.name = new_name
+ ch_core.systemovy_kanal(custom_state.player_name, "Jmeno zastávky změněno.")
+ end
+ if change_stn then
+ advtrains.lines.stations[new_stn] = t
+ local stops = advtrains.lines.stops
+ local count = 0
+ for _, trackinfo in ipairs(st.tracks) do
+ local stop = stops[trackinfo.epos]
+ if stop == nil then
+ minetest.log("error", "Expected track at position "..trackinfo.epos.." not found!")
+ elseif stop.stn ~= st.stn then
+ minetest.log("error", "Track at position "..trackinfo.epos.." has unexpected stn '"..tostring(stop.stn).."' instead of '"..st.stn.."'!")
+ else
+ stop.stn = new_stn
+ count = count + 1
+ end
+ end
+ advtrains.lines.stations[st.stn] = nil
+ ch_core.systemovy_kanal(custom_state.player_name, "Kód zastávky změněn, "..count.." bloků kolejí aktualizováno.")
+ end
+ custom_state.stations = nil
+ update_formspec = true
+ elseif fields.smazat then
+ local pinfo = ch_core.normalize_player(player)
+ local st = custom_state.stations[(custom_state.selection_index or 0) - 1]
+ if st == nil then
+ ch_core.systemovy_kanal(custom_state.player_name, "CHYBA: není vybrána žádná zastávka!")
+ return
+ end
+ if st.tracks[1] ~= nil then
+ ch_core.systemovy_kanal(custom_state.player_name, "Nelze smazat zastávku, k níž jsou přiřazeny koleje!")
+ return
+ end
+ local t = advtrains.lines.stations[st.stn]
+ if t == nil then
+ ch_core.systemovy_kanal(custom_state.player_name, "CHYBA: zastávka k úpravě nebyla nalezena! Toto je pravděpodobně vnitřní chyba editoru.")
+ return
+ end
+ advtrains.lines.stations[st.stn] = nil
+ ch_core.systemovy_kanal(custom_state.player_name, "Zastávka úspěšně smazána.")
+ custom_state.stations = nil
+ update_formspec = true
+ elseif fields.jrad then
+ local st = custom_state.stations[(custom_state.selection_index or 0) - 1]
+ if st == nil then
+ ch_core.systemovy_kanal(custom_state.player_name, "CHYBA: není vybrána žádná zastávka!")
+ return
+ end
+ advtrains.lines.show_jr_formspec(player, nil, assert(st.stn))
+ return
+ elseif fields.priradit_kolej then
+ local st = custom_state.stations[(custom_state.selection_index or 0) - 1]
+ if st == nil then
+ ch_core.systemovy_kanal(custom_state.player_name, "CHYBA: není vybrána žádná zastávka!")
+ return
+ end
+ local linevar_to_change = custom_state.linevars[custom_state.current_linevar]
+ if linevar_to_change == nil then
+ ch_core.systemovy_kanal(custom_state.player_name, "CHYBA: vnitřní chyba 1!")
+ return
+ end
+ local linevar_def = advtrains.lines.try_get_linevar_def(linevar_to_change.linevar, linevar_to_change.stn)
+ if linevar_def == nil then
+ ch_core.systemovy_kanal(custom_state.player_name, "CHYBA: vnitřní chyba 2!")
+ return
+ end
+ local stop = linevar_def.stops[linevar_to_change.linevar_index]
+ if stop == nil then
+ ch_core.systemovy_kanal(custom_state.player_name, "CHYBA: vnitřní chyba 3!")
+ return
+ end
+ if stop.stn ~= st.stn then
+ ch_core.systemovy_kanal(custom_state.player_name, "CHYBA: vnitřní chyba 4!")
+ return
+ end
+ stop.track = tostring(fields.kolej)
+ linevar_to_change.track = stop.track
+ ch_core.systemovy_kanal(custom_state.player_name, "Přiřazená kolej úspěšně nastavena.")
+ update_formspec = true
+ elseif fields.quit then
+ return
+ end
+
+ if fields.dopravna then
+ local event = minetest.explode_table_event(fields.dopravna)
+ if event.type == "CHG" then
+ custom_state.selection_index = ifthenelse(event.row > 1, event.row, nil)
+ local st = custom_state.stations[(custom_state.selection_index or 0) - 1]
+ local new_linevars = {}
+ custom_state.linevars = new_linevars
+ custom_state.current_linevar = 1
+ if st ~= nil then
+ for _, r in ipairs(advtrains.lines.get_linevars_by_station(st.stn)) do
+ local line, stn = advtrains.lines.linevar_decompose(r.linevar)
+ for _, i in ipairs(r.indices) do
+ local stop = r.linevar_def.stops[i]
+ table.insert(new_linevars, {
+ stn = stn,
+ linevar = r.linevar,
+ linevar_index = i,
+ dep = assert(stop.dep),
+ track = stop.track or "",
+ })
+ end
+ end
+ end
+ update_formspec = true
+ end
+ end
+
+ if fields.klinevar then
+ local n = tonumber(fields.klinevar)
+ if n ~= nil and custom_state.linevars[n] ~= nil then
+ custom_state.current_linevar = n
+ end
+ end
+
+ -- filter (must be the last)
+ if fields.filter then
+ local new_filter = tonumber(fields.filter)
+ if filters[new_filter] ~= nil and new_filter ~= custom_state.active_filter then
+ custom_state.active_filter = new_filter
+ custom_state.stations = nil
+ update_formspec = true
+ end
+ end
+ if update_formspec then
+ return get_formspec(custom_state)
+ end
+end
+
+local function show_formspec(player)
+
+ local custom_state = {
+ player_name = assert(player:get_player_name()),
+ player_pos = vector.round(player:get_pos()),
+ active_filter = 1,
+ -- selection_index = nil,
+ linevars = {--[[
+ {stn = string, linevar = string, linevar_index = int, track = string}...
+ ]]},
+ current_linevar = 1,
+ }
+ -- ch_core.show_formspec(player_or_player_name, formname, formspec, formspec_callback, custom_state, options)
+ ch_core.show_formspec(player, "advtrains_line_automation:editor_dopraven", get_formspec(custom_state), formspec_callback, custom_state, {})
+end
+
+advtrains.lines.open_station_editor = show_formspec
+
+def = {
+ -- params = "",
+ description = "Otevře editor dopraven (stanic, zastávek a odboček)",
+ privs = {},
+ func = function(player_name, param) show_formspec(minetest.get_player_by_name(player_name)) end,
+}
+core.register_chatcommand("zastavky", def)
+core.register_chatcommand("zastávky", def)
+
+
+-- Jízdní řád
+
+--[[
+ custom_state = {
+ pos = vector or nil, -- node position (will use metadata to determine the owner)
+ player_name = string, -- player to whom the formspec is to be shown (will use this to determine privs)
+ stn = int, -- station selection from stns
+ stns = {
+ {stn = "", fs = "(vyberte dopravnu)", name_fs = ""},
+ {stn = string, fs = string, name_fs = string}...
+ }, -- the list of stations to select from; the first must be "", which means no station
+ track = int, -- selection of track from "tracks" list
+ tracks = {"", string...}, -- the list of tracks to select from; the first must be "", which means all tracks
+ linevar = int,
+ linevars = {{
+ stn = string, -- station from linevar
+ linevar = string, -- linevar
+ line_fs = string, -- line for formspec
+ target_fs = string, -- terminus name for formspec
+ track_fs = string, -- track info for formspec ("" if not available)
+ status_fs = string, -- line status for formspec (including color column)
+ train_name_fs = string, -- train name for formspec ("" if not available)
+ linevar_index = int, -- index of the selected station (stn) in linevar_def.stops of this linevar
+ }...},
+ stop = int,
+ stops = {{
+ stn = string,
+ name_fs = string,
+ track_fs = string,
+ dep = int,
+ wait = int,
+ linevar_index = int, -- index of this stop in linevar_def.stops
+ }...},
+ message = string, -- message for label[] on the bottom; "" to disable
+ }
+]]
+
+local all_stations_record = {stn = "", fs = F("(vyberte dopravnu)"), name_fs = ""}
+local is_visible_mode = assert(advtrains.lines.is_visible_mode)
+
+-- refresh custom_state.stops according to custom_state.linevar
+local function jr_refresh_stops(custom_state, stn_to_select)
+ if stn_to_select == "" then
+ stn_to_select = nil
+ end
+ local linevar_info = custom_state.linevars[custom_state.linevar]
+ local index_to_select = 0
+ local result = {}
+ if linevar_info ~= nil then
+ local linevar_def = advtrains.lines.try_get_linevar_def(linevar_info.linevar, linevar_info.stn)
+ if linevar_def == nil then
+ -- invalid linevar => no stops
+ core.log("error", "Player "..custom_state.player_name.." selected invalid linevar "..linevars[linevar].linevar.."!")
+ else
+ local stops = linevar_def.stops
+ for linevar_index, stop_data in ipairs(linevar_def.stops) do
+ if is_visible_mode(stop_data.mode) then
+ local r = {
+ stn = assert(stop_data.stn),
+ name_fs = F(advtrains.lines.get_station_name(stop_data.stn)),
+ track_fs = stop_data.track or "",
+ dep = stop_data.dep,
+ wait = stop_data.wait or 10,
+ linevar_index = linevar_index,
+ }
+ if r.track_fs ~= "" then
+ r.track_fs = F("["..r.track_fs.."]")
+ end
+ table.insert(result, r)
+ if stn_to_select ~= nil and index_to_select == 0 and r.stn == stn_to_select then
+ index_to_select = #result -- stop selected
+ end
+ end
+ end
+ end
+ end
+ if index_to_select == 0 and result[1] ~= nil then
+ index_to_select = 1
+ end
+ custom_state.stop = index_to_select
+ custom_state.stops = result
+ custom_state.message = ""
+end
+
+local function get_all_linevars()
+ local result = {}
+ local empty_table = {}
+ local trains_by_linevar = advtrains.lines.get_trains_by_linevar()
+ for stn, stdata in pairs(advtrains.lines.stations) do
+ for linevar, linevar_def in pairs(stdata.linevars or empty_table) do
+ local line, stn = advtrains.lines.linevar_decompose(linevar)
+ local target_fs = F(advtrains.lines.get_line_description(linevar_def, {line_number = false, last_stop = true, last_stop_prefix = "",
+ last_stop_uppercase = false, train_name = false}))
+ local status_fs
+ if trains_by_linevar[linevar] ~= nil then
+ status_fs = "#00ff00,v provozu"
+ else
+ status_fs = "#999999,neznámý"
+ end
+ table.insert(result, {
+ stn = stn,
+ linevar = linevar,
+ line_fs = F(line),
+ target_fs = target_fs,
+ track_fs = "",
+ status_fs = status_fs,
+ train_name_fs = F(linevar_def.train_name or ""),
+ linevar_index = 1,
+ })
+ end
+ end
+ table.sort(result, function(a, b) return a.linevar < b.linevar end)
+ return result
+end
+
+--[[
+ stn_filter = string,
+ track_filter = string or nil,
+]]
+local function get_linevars_by_filter(stn_filter, track_filter)
+ local result = {}
+ local empty_table = {}
+ local line_description_options = {line_number = false, last_stop = true, last_stop_prefix = "", last_stop_uppercase = false, train_name = false}
+ local trains_by_linevar = advtrains.lines.get_trains_by_linevar()
+ assert(stn_filter)
+ if track_filter == "" then
+ track_filter = nil
+ end
+ for stn, stdata in pairs(advtrains.lines.stations) do
+ for linevar, linevar_def in pairs(stdata.linevars or empty_table) do
+ local last_stop_index = advtrains.lines.get_last_stop(linevar_def, false)
+ if last_stop_index ~= nil then
+ for linevar_index = 1, last_stop_index - 1 do -- NOTE: the last visible station is intentionally ignored here!
+ local stop_data = linevar_def.stops[linevar_index]
+ local initialized = false
+ local line, stn, line_fs, target_fs, status_fs
+ if
+ stop_data.stn == stn_filter and
+ is_visible_mode(stop_data.mode) and
+ (track_filter == nil or track_filter == stop_data.track)
+ then
+ if not initialized then
+ initialized = true
+ line, stn = advtrains.lines.linevar_decompose(linevar)
+ line_fs = F(line)
+ target_fs = F(advtrains.lines.get_line_description(linevar_def, line_description_options))
+ if trains_by_linevar[linevar] ~= nil then
+ status_fs = "#00ff00,v provozu"
+ else
+ status_fs = "#999999,neznámý"
+ end
+ end
+ local track_fs = stop_data.track
+ if track_fs == nil or track_fs == "" then
+ track_fs = ""
+ else
+ track_fs = F("["..track_fs.."]")
+ end
+ table.insert(result, {
+ stn = stn,
+ linevar = linevar,
+ line_fs = line_fs,
+ target_fs = target_fs,
+ track_fs = track_fs,
+ status_fs = status_fs,
+ train_name_fs = F(linevar_def.train_name or ""),
+ linevar_index = assert(linevar_index),
+ })
+ end
+ end
+ end
+ end
+ end
+ table.sort(result, function(a, b)
+ if a.linevar ~= b.linevar then
+ return a.linevar < b.linevar
+ else
+ return a.linevar_index < b.linevar_index
+ end
+ end)
+ return result
+end
+
+local function is_jr_node_name(name)
+ return core.get_item_group(name, "ch_jrad") ~= 0
+end
+
+-- refresh custom_state.linevars according to custom_state.stn and custom_state.track
+local function jr_refresh_linevars(custom_state, linevar_to_select, linevar_index_to_select)
+ if linevar_to_select == "" then
+ linevar_to_select = nil
+ end
+ assert(linevar_index_to_select == nil or type(linevar_index_to_select) == "number")
+ local stn = assert(custom_state.stn)
+ local stn_info = assert(custom_state.stns[stn])
+ local track = assert(custom_state.track)
+ local tracks = assert(custom_state.tracks)
+
+ local new_linevars
+ if stn_info.stn == "" then
+ new_linevars = get_all_linevars()
+ else
+ new_linevars = get_linevars_by_filter(stn_info.stn, assert(tracks[track]))
+ end
+ local index_to_select = 1
+ if linevar_to_select ~= nil then
+ for i, r in ipairs(new_linevars) do
+ if r.linevar == linevar_to_select and (linevar_index_to_select == nil or r.linevar_index == linevar_index_to_select) then
+ index_to_select = i
+ break
+ end
+ end
+ end
+ if new_linevars[index_to_select] == nil then
+ index_to_select = 0
+ end
+ custom_state.linevars = new_linevars
+ custom_state.linevar = index_to_select
+ custom_state.message = ""
+
+ local pos = custom_state.pos
+ if pos ~= nil and is_jr_node_name(core.get_node(pos).name) then
+ local meta = core.get_meta(pos)
+ meta:set_string("stn", custom_state.stns[custom_state.stn].stn)
+ meta:set_string("track", custom_state.tracks[custom_state.track])
+ local infotext = {"jízdní řád\n"}
+ if stn_info.stn == "" then
+ table.insert(infotext, "<všechny linky>")
+ else
+ table.insert(infotext, advtrains.lines.get_station_name(stn_info.stn))
+ if custom_state.tracks[custom_state.track] ~= "" then
+ table.insert(infotext, " ["..custom_state.tracks[custom_state.track].."]")
+ end
+ end
+ if new_linevars[1] ~= nil then
+ local prefix = "\n"
+ local set = {[""] = true}
+ for _, linevar_info in ipairs(new_linevars) do
+ local line = advtrains.lines.linevar_decompose(linevar_info.linevar)
+ if not set[line] then
+ set[line] = true
+ table.insert(infotext, prefix.."["..line.."]")
+ prefix = " "
+ end
+ end
+ end
+ meta:set_string("infotext", table.concat(infotext))
+ end
+end
+
+-- refresh custom_state.tracks according to custom_state.stn
+-- and selects a specified track, if available
+local function jr_refresh_tracks(custom_state, track_to_select)
+ if track_to_select == "" then
+ track_to_select = nil
+ end
+ local result = {""}
+ local index_to_select = 1
+ local current_stn = custom_state.stns[custom_state.stn].stn
+ if current_stn ~= "" and advtrains.lines.stations[current_stn] ~= nil then
+ local track_set = {[""] = true}
+ -- search for tracks:
+ for epos, stdata in pairs(advtrains.lines.stops) do
+ if stdata.stn == current_stn and stdata.track ~= nil and not track_set[stdata.track] then
+ track_set[stdata.track] = true
+ table.insert(result, tostring(stdata.track))
+ end
+ end
+ if #result > 1 then
+ table.sort(result)
+ assert(result[1] == "")
+ if track_to_select ~= nil then
+ for i, track in ipairs(result) do
+ if track_to_select == track then
+ index_to_select = i
+ end
+ end
+ end
+ end
+ end
+ custom_state.tracks = result
+ custom_state.track = index_to_select
+ custom_state.message = ""
+end
+
+--[[
+ -- will refresh custom_state.stns[] and (optionally) select a wanted station if it's on the list,
+ -- otherwise the default 'select station' option will be chosen
+ custom_state = table,
+ stn_to_select = string or nil,
+]]
+local function jr_refresh_stns(custom_state, stn_to_select)
+ if stn_to_select == "" then
+ stn_to_select = nil
+ end
+ local result = {all_stations_record}
+ local index_to_select = 1
+ for i, st in ipairs(load_stations()) do
+ result[1 + i] = {
+ stn = assert(st.stn),
+ fs = F(st.stn.." | "..st.name),
+ name_fs = F(st.name),
+ }
+ if stn_to_select ~= nil and st.stn == stn_to_select then
+ index_to_select = 1 + i
+ end
+ end
+ custom_state.stns = result
+ custom_state.stn = index_to_select
+ custom_state.message = ""
+end
+
+-- will refresh a departure message according to linevar + stop
+local function jr_refresh_departure(custom_state)
+ local linevar_info = custom_state.linevars[custom_state.linevar]
+ local stop_info = custom_state.stops[custom_state.stop]
+ if linevar_info == nil or stop_info == nil then
+ custom_state.message = ""
+ return
+ end
+ local linevar_def = advtrains.lines.try_get_linevar_def(linevar_info.linevar)
+ if linevar_def == nil then
+ custom_state.message = ""
+ return
+ end
+ local rwtime = rwt.to_secs(rwt.get_time())
+ local prediction = advtrains.lines.predict_station_departures(linevar_def, assert(stop_info.linevar_index), rwtime)
+ if #prediction == 0 then
+ custom_state.message = "v nejbližší době nenalezeny žádné odjezdy zvolené linky"
+ return
+ end
+ local deps = {}
+ for i, pred in ipairs(prediction) do
+ deps[i] = tostring(pred.dep - rwtime)
+ end
+ custom_state.message = "nejbližší odjezdy zvolené linky za: "..table.concat(deps, ", ").." s"
+end
+
+local function get_jr_formspec(custom_state)
+ local formspec = {
+ ch_core.formspec_header({
+ formspec_version = 6,
+ size = {20, 12},
+ auto_background = true,
+ })
+ }
+ local access_level = "player" -- player | owner | admin
+ local node_owner
+ if custom_state.pos ~= nil then
+ node_owner = core.get_meta(custom_state.pos):get_string("owner")
+ if node_owner == "" then
+ node_owner = nil
+ end
+ end
+ local stn, stn_owner
+ if custom_state.stn > 1 and custom_state.stns[custom_state.stn] ~= nil then
+ stn_owner = (advtrains.lines.stations[custom_state.stns[custom_state.stn].stn] or {}).owner -- may be nil
+ end
+
+ if not custom_state.force_unprivileged then
+ if ch_core.get_player_role(custom_state.player_name) == "admin" then
+ access_level = "admin"
+ elseif custom_state.player_name == node_owner or custom_state.player_name == stn_owner then
+ access_level = "owner"
+ end
+ end
+
+ if node_owner ~= nil then
+ if access_level ~= "player" then
+ -- admin or owner:
+ table.insert(formspec, "label[0.5,0.6;Jízdní řády]"..
+ "dropdown[5,0.3;10,0.6;dopravna;")
+ for i, r in ipairs(custom_state.stns) do
+ table.insert(formspec, ifthenelse(i == 1, r.fs, ","..r.fs))
+ end
+ table.insert(formspec, ";"..custom_state.stn..";true]"..
+ "dropdown[15.25,0.3;3.5,0.6;kolej;")
+ for i, r in ipairs(custom_state.tracks) do
+ if i == 1 then
+ table.insert(formspec, "(všechny koleje)")
+ else
+ table.insert(formspec, ","..F(r))
+ end
+ end
+ table.insert(formspec, ";"..custom_state.track..";true]")
+ else
+ -- player (including 'new' players)
+ local stn_info = custom_state.stns[custom_state.stn]
+ if stn_info.stn == "" then
+ table.insert(formspec, "label[0.5,0.6;Jízdní řády (všechny linky)]")
+ else
+ local track = custom_state.tracks[custom_state.track]
+ if track ~= "" then
+ track = F(" ["..track.."]")
+ end
+ table.insert(formspec, "label[0.5,0.6;"..F("Jízdní řády: ")..stn_info.name_fs..track.."]")
+ end
+ end
+ if access_level ~= "admin" then
+ -- player/owner
+ table.insert(formspec, "label[0.5,1.65;vlastník/ice j. řádu: ")
+ table.insert(formspec, ch_core.prihlasovaci_na_zobrazovaci(node_owner))
+ if stn_owner ~= nil then
+ table.insert(formspec, " | dopravnu spravuje: ")
+ table.insert(formspec, ch_core.prihlasovaci_na_zobrazovaci(stn_owner))
+ end
+ table.insert(formspec, "]")
+ else
+ -- admin only
+ table.insert(formspec, "label[0.5,1.65;vlastník/ice:]"..
+ "field[2.75,1.25;5,0.75;owner;;")
+ table.insert(formspec, ch_core.prihlasovaci_na_zobrazovaci(node_owner))
+ table.insert(formspec, "]button[8,1.25;3,0.75;setowner;nastavit]")
+ if stn_owner ~= nil then
+ table.insert(formspec, "label[11.25,1.65;dopravnu spravuje: "..ch_core.prihlasovaci_na_zobrazovaci(stn_owner).."]")
+ end
+ end
+ else
+ table.insert(formspec, "label[0.5,0.6;Příruční jízdní řády (všechny linky)]")
+ end
+
+ table.insert(formspec, "tablecolumns[text,align=right,tooltip=linka;text,width=12,tooltip=cíl;text,tooltip=kolej;color,span=1;text,tooltip=stav;color,span=1;text,tooltip=jméno vlaku]"..
+ "table[0.5,2.25;11,5;linka;")
+ for i, r in ipairs(custom_state.linevars) do
+ if i > 1 then
+ table.insert(formspec, ",")
+ end
+ table.insert(formspec, r.line_fs..","..r.target_fs..","..r.track_fs..","..r.status_fs..",#cccccc,"..r.train_name_fs)
+ end
+ table.insert(formspec, ifthenelse(custom_state.linevar > 0, ";"..custom_state.linevar.."]", ";]"))
+ table.insert(formspec, "tablecolumns[text,align=right;text;text]"..
+ "table[12.5,2.25;7,8.75;zastavka;")
+ if custom_state.stops[1] ~= nil then
+ local selected_stop_index = custom_state.stop
+ if custom_state.stops[selected_stop_index] == nil then
+ selected_stop_index = 1
+ end
+ local base_dep
+ for i, r in ipairs(custom_state.stops) do
+ if i > 1 then
+ table.insert(formspec, ",")
+ end
+ if i >= selected_stop_index then
+ if i == selected_stop_index then
+ base_dep = assert(r.dep)
+ table.insert(formspec, "0,")
+ else
+ table.insert(formspec, (r.dep - base_dep)..",")
+ end
+ else
+ table.insert(formspec, " ,")
+ end
+ table.insert(formspec, r.name_fs..","..r.track_fs)
+ end
+ table.insert(formspec, ";"..selected_stop_index.."]")
+ else
+ table.insert(formspec, ";]") -- empty list
+ end
+ table.insert(formspec, "button_exit[18.75,0.3;0.75,0.75;close;X]")
+ if custom_state.message ~= "" then
+ table.insert(formspec, "label[5.25,11.35;"..F(custom_state.message).."]")
+ end
+ table.insert(formspec, "button[0.5,11;4.5,0.75;refresh;zjistit nejbližší odjezdy...]")
+ return table.concat(formspec)
+end
+
+local function jr_formspec_callback(custom_state, player, formname, fields)
+ if fields.dopravna then
+ local new_stn = tonumber(fields.dopravna)
+ if new_stn ~= nil and new_stn ~= custom_state.stn and custom_state.stns[new_stn] ~= nil then
+ custom_state.stn = new_stn
+ local current_track = custom_state.tracks[custom_state.track] or ""
+ local current_linevar_info = custom_state.linevars[custom_state.linevar]
+ jr_refresh_tracks(custom_state, current_track)
+ if current_linevar_info ~= nil then
+ jr_refresh_linevars(custom_state, current_linevar_info.linevar, current_linevar_info.linevar_index)
+ else
+ jr_refresh_linevars(custom_state)
+ end
+ jr_refresh_stops(custom_state, custom_state.stns[new_stn].stn)
+ jr_refresh_departure(custom_state)
+ return get_jr_formspec(custom_state)
+ end
+ end
+ if fields.kolej then
+ local new_track = tonumber(fields.kolej)
+ if new_track ~= nil and new_track ~= custom_state.track and custom_state.tracks[new_track] ~= nil then
+ custom_state.track = new_track
+ local current_linevar_info = custom_state.linevars[custom_state.linevar]
+ if current_linevar_info ~= nil then
+ jr_refresh_linevars(custom_state, current_linevar_info.linevar, current_linevar_info.linevar_index)
+ else
+ jr_refresh_linevars(custom_state)
+ end
+ jr_refresh_stops(custom_state, custom_state.stns[custom_state.stn].stn)
+ jr_refresh_departure(custom_state)
+ return get_jr_formspec(custom_state)
+ end
+ end
+ if fields.setowner and custom_state.pos ~= nil and is_jr_node_name(core.get_node(custom_state.pos).name) then
+ local meta = core.get_meta(custom_state.pos)
+ local jm = ch_core.jmeno_na_existujici_prihlasovaci(fields.owner)
+ if jm ~= nil then
+ meta:set_string("owner", jm)
+ else
+ core.chat_send_player(custom_state.player_name, "*** Postava '"..fields.owner.."' neexistuje!")
+ end
+ return get_jr_formspec(custom_state)
+ end
+ if fields.linka then
+ local event = core.explode_table_event(fields.linka)
+ local new_line
+ if event.type == "CHG" or event.type == "DCL" then
+ new_line = tonumber(event.row)
+ end
+ if new_line ~= nil and new_line ~= custom_state.linevar then
+ local new_linevar_info = custom_state.linevars[new_line]
+ if new_linevar_info ~= nil then
+ jr_refresh_linevars(custom_state, new_linevar_info.linevar, new_linevar_info.linevar_index)
+ else
+ jr_refresh_linevars(custom_state)
+ end
+ jr_refresh_stops(custom_state, custom_state.stns[custom_state.stn].stn)
+ jr_refresh_departure(custom_state)
+ return get_jr_formspec(custom_state)
+ end
+ end
+ if fields.zastavka then
+ local event = core.explode_table_event(fields.zastavka)
+ if event.type == "CHG" or event.type == "DCL" then
+ local new_stop = tonumber(event.row)
+ if new_stop ~= nil and new_stop ~= custom_state.stop and custom_state.stops[new_stop] ~= nil then
+ custom_state.stop = new_stop
+ if event.type == "DCL" then
+ jr_refresh_departure(custom_state)
+ end
+ return get_jr_formspec(custom_state)
+ end
+ end
+ end
+ if fields.refresh then
+ jr_refresh_departure(custom_state)
+ return get_jr_formspec(custom_state)
+ end
+end
+
+function advtrains.lines.show_jr_formspec(player, pos, stn, track, linevar, stop_stn, force_unprivileged)
+ assert(core.is_player(player))
+ local custom_state = {
+ player_name = player:get_player_name(),
+ stn = 1,
+ stns = {all_stations_record},
+ track = 1,
+ tracks = {""},
+ linevar = 0,
+ linevars = {},
+ stop = 0,
+ stops = {},
+ message = "",
+ force_unprivileged = force_unprivileged,
+ }
+ if pos ~= nil then
+ custom_state.pos = pos
+ end
+ jr_refresh_stns(custom_state, stn)
+ jr_refresh_tracks(custom_state, track)
+ jr_refresh_linevars(custom_state, linevar)
+ if stop_stn == nil then
+ stop_stn = custom_state.stns[custom_state.stn].stn
+ end
+ jr_refresh_stops(custom_state, stop_stn)
+ jr_refresh_departure(custom_state)
+
+ -- show formspec:
+ return ch_core.show_formspec(player, "advtrains_line_automation:jizdni_rad",
+ get_jr_formspec(custom_state), jr_formspec_callback, custom_state, {})
+end
diff --git a/advtrains_line_automation/stoprail.lua b/advtrains_line_automation/stoprail.lua
index aa8dfdd..8553c97 100644
--- a/advtrains_line_automation/stoprail.lua
+++ b/advtrains_line_automation/stoprail.lua
@@ -1,6 +1,7 @@
-- stoprail.lua
-- adds "stop rail". Recognized by lzb. (part of behavior is implemented there)
+local rwt = assert(advtrains.lines.rwt)
-- Translation
S = attrans
@@ -17,16 +18,39 @@ local function updatemeta(pos)
local pe = advtrains.encode_pos(pos)
local stdata = advtrains.lines.stops[pe]
if not stdata then
- meta:set_string("infotext", "Error")
+ meta:set_string("infotext", attrans("Error"))
end
+ local stn = advtrains.lines.stations[stdata.stn]
+ local stn_viewname = stn and stn.name or "-"
- meta:set_string("infotext", "Stn. "..stdata.stn.." T. "..stdata.track)
+ meta:set_string("infotext", attrans("Stn. @1 (@2) T. @3", stn_viewname, stdata.stn or "", stdata.track or ""))
end
local door_dropdown = {L=1, R=2, C=3}
---local door_dropdown_rev = {Right="R", Left="L", Closed="C"} -- Code review : why are the value in an order different than the one in the dropdown box ?
local door_dropdown_code = {"L", "R", "C"} -- switch to numerical index of selection : for conversion of the numerical index in the opening side selection dropdown box to the internal codification
+local function get_stn_dropdown(stn, player_name)
+ local stations = advtrains.lines.load_stations_for_formspec()
+ local selected_index
+ local result = {"dropdown[0.25,2;6,0.75;stn;(nepřiřazeno)"}
+ local right_mark
+ for i, st in ipairs(stations) do
+ if player_name ~= nil and player_name ~= st.owner then
+ right_mark = "(cizí) "
+ else
+ right_mark = ""
+ end
+ table.insert(result, ","..right_mark..minetest.formspec_escape(st.stn.." | "..st.name))
+ if st.stn == stn then
+ selected_index = i + 1
+ end
+ end
+ table.insert(result, ";"..(selected_index or "1")..";true]")
+ return table.concat(result)
+end
+
+local player_to_stn_override = {}
+
local function show_stoprailform(pos, player)
local pe = advtrains.encode_pos(pos)
local pname = player:get_player_name()
@@ -52,23 +76,67 @@ local function show_stoprailform(pos, player)
stdata.speed = "M"
end
- local form = "size[8,7]"
- form = form.."style[stn,ars;font=mono]"
- form = form.."field[0.8,0.8;2,1;stn;"..S("Station Code")..";"..minetest.formspec_escape(stdata.stn).."]"
- form = form.."field[2.8,0.8;5,1;stnname;"..S("Station Name")..";"..minetest.formspec_escape(stnname).."]"
- form = form.."field[0.80,2.0;1.75,1;ddelay;"..S("Door Delay")..";"..minetest.formspec_escape(stdata.ddelay).."]"
- form = form.."field[2.55,2.0;1.75,1;speed;"..S("Dep. Speed")..";"..minetest.formspec_escape(stdata.speed).."]"
- form = form.."field[4.30,2.0;1.75,1;track;"..S("Track")..";"..minetest.formspec_escape(stdata.track).."]"
- form = form.."field[6.05,2.0;1.75,1;wait;"..S("Stop Time")..";"..stdata.wait.."]"
- form = form.."label[0.5,2.6;"..S("Door Side").."]"
- form = form.."dropdown[0.51,3.0;2;doors;"..S("Left")..","..S("Right")..","..S("Closed")..";"..door_dropdown[stdata.doors]..";true]" -- switch to numerical index of the selection
- form = form.."checkbox[3.00,2.4;reverse;"..S("Reverse train")..";"..(stdata.reverse and "true" or "false").."]"
- form = form.."checkbox[3.00,2.8;kick;"..S("Kick out passengers")..";"..(stdata.kick and "true" or "false").."]"
- form = form.."checkbox[3.00,3.2;waitsig;"..S("Wait for signal to clear")..";"..(stdata.waitsig and "true" or "false").."]"
- form = form.."textarea[0.8,4.2;7,2;ars;"..S("Trains stopping here (ARS rules)")..";"..advtrains.interlocking.ars_to_text(stdata.ars).."]"
- form = form.."button[0.5,6;7,1;save;"..S("Save").."]"
-
- minetest.show_formspec(pname, "at_lines_stop_"..pe, form)
+ local item_name = (minetest.registered_items["advtrains_line_automation:dtrack_stop_placer"] or {}).description or ""
+ local pname_unless_admin
+ if not minetest.check_player_privs(pname, "train_admin") then
+ pname_unless_admin = pname
+ end
+ local formspec = "formspec_version[4]"..
+ "size[8,12]"..
+ "item_image[0.25,0.25;1,1;advtrains_line_automation:dtrack_stop_placer]"..
+ "textarea[1.35,0.6;5.5,0.6;;;"..minetest.formspec_escape(string.format("%s %d,%d,%d", item_name, pos.x, pos.y, pos.z)).."]"..
+ "button_exit[7,0.25;0.75,0.75;close;X]"..
+ "style[ars,line,routingcode;font=mono]"..
+ "label[0.25,1.75;"..attrans("Station Code").." | "..attrans("Station Name").."]"..
+ get_stn_dropdown(player_to_stn_override[pname] or stdata.stn, pname_unless_admin)..
+ "field[6.75,2;1,0.75;track;"..attrans("Track")..";"..minetest.formspec_escape(stdata.track).."]"..
+ "label[0.25,3.4;"..attrans("Door Side").."]"..
+ "dropdown[2.25,3;2,0.75;doors;"..S("Left")..","..S("Right")..","..S("Closed")..";"..door_dropdown[stdata.doors]..";true]".. -- switch to numerical index of the selection
+ "checkbox[4.5,3.25;reverse;"..attrans("Reverse train")..";"..(stdata.reverse and "true" or "false").."]"..
+ "checkbox[4.5,3.75;kick;"..attrans("Kick out passengers")..";"..(stdata.kick and "true" or "false").."]"..
+ "checkbox[4.5,4.25;keepopen;Nezavírat dveře na odj.;"..(stdata.keepopen and "true" or "false").."]"..
+ "label[0.25,4.3;"..attrans("Stop Time").."]"..
+ "field[0.25,4.5;1,0.75;wait;;"..stdata.wait.."]"..
+ "label[1.5,4.9;+]"..
+ "field[2,4.5;1,0.75;ddelay;;"..minetest.formspec_escape(stdata.ddelay).."]".. -- "..attrans("Door Delay").."
+ (advtrains.lines.open_station_editor ~= nil and "button[3.5,4.5;4,0.75;editstn;Editor dopraven]" or "")..
+ "field[0.25,6;2,0.75;speed;"..attrans("Dep. Speed")..";"..minetest.formspec_escape(stdata.speed).."]"..
+ "field[2.5,6;2,0.75;line;Linka na odj.;"..minetest.formspec_escape(stdata.line or "").."]"..
+ "field[4.75,6;2,0.75;routingcode;Sm.kód na odj.;"..minetest.formspec_escape(stdata.routingcode or "").."]"..
+ "field[0.25,7.25;2,0.75;interval;Interval \\[s\\]:;"..minetest.formspec_escape(stdata.interval or "").."]"..
+ "field[2.5,7.25;2,0.75;ioffset;Jeho posun:;"..minetest.formspec_escape(stdata.ioffset or "0").."]"..
+ "button[4.75,7;3,1.0;ioffsetnow;Nastavit posun\nna odjezd teď + uložit]"..
+ "textarea[0.25,8.4;7.5,1.5;ars;"..attrans("Trains stopping here (ARS rules)")..";"..advtrains.interlocking.ars_to_text(stdata.ars).."]"..
+ "label[0.3,10.25;Platí jen pro vlaky s]"..
+ "field[3,10;1,0.5;minparts;;"..minetest.formspec_escape(stdata.minparts or "0").."]"..
+ "label[4.15,10.25;až]"..
+ "field[4.6,10;1,0.5;maxparts;;"..minetest.formspec_escape(stdata.maxparts or "128").."]"..
+ "label[5.75,10.25;vozy.]"..
+ "button_exit[0.25,11;7.5,0.75;save;"..attrans("Save").."]"..
+ "tooltip[close;Zavře dialogové okno]"..
+ "tooltip[stn;Dopravna\\, ke které tato zastávka patří. Jedna dopravna může mít víc kolejí. K vytvoření a úpravám dopraven použijte Editor dopraven.]"..
+ "tooltip[track;Číslo koleje]"..
+ "tooltip[wait;Základní doba stání s otevřenými dveřmi]"..
+ "tooltip[ddelay;Dodatečná doba stání před odjezdem po uzavření dveří]"..
+ "tooltip[speed;Cílová rychlost zastavivšího vlaku na odjezdu. Platné hodnoty jsou M pro nejvyšší rychlost vlaku a čísla 0 až 20.]"..
+ "tooltip[line;Nová linka na odjezdu. Prázdné pole = zachovat stávající linku. Pro smazání linky zadejte znak -]"..
+ "tooltip[routingcode;Nový směrový kód na odjezdu. Prázdné pole = zachovat stávající směrový kód. Pro smazání kódu vlaku zadejte znak -]"..
+ "tooltip[ars;Seznam podmínek\\, z nichž musí vlak splnit alespoň jednu\\, aby zde zastavil:\nLN {linka}\nRC {směrovací kód}\n"..
+ "* = jakýkoliv vlak\n\\# značí komentář a ! na začátku řádky danou podmínku neguje (nedoporučuje se)]"..
+ "tooltip[minparts;Minimální počet vozů\\, které musí vlak mít\\, aby zde zastavil. Výchozí hodnota je 0.]"..
+ "tooltip[maxparts;Maximální počet vozů\\, které vlak může mít\\, aby zde zastavil. Výchozí (a maximální) hodnota je 128.]"..
+ "tooltip[editsn;Otevře v novém okně editor dopraven.\nPoužijte tento editor pro založení nové dopravny\\, jíž budete moci přiřadit koleje.]"..
+ "tooltip[interval;Hodnota v sekundách 1 až 3600 nebo nevyplněno. Je-li vyplněno\\, rozdělí čas na intervaly zadané délky,\n"..
+ "a pokud z této zastávkové koleje v rámci jednoho z nich odjede vlak\\, odjezd dalšího bude pozdržen do začátku\n"..
+ "následujícího intervalu. Výchozí začátky intervalů stejné délky jsou v celé železniční síti společné.\n"..
+ "Slouží k nastavení intervalového provozu.]"..
+ "tooltip[ioffset;Hodnota v sekundách 0 až (interval - 1). Posune začátek intervalů oproti výchozímu stavu\n"..
+ "o zadaný počet sekund vpřed. Slouží k detailnímu vyladění času odjezdů relativně vůči ostatním linkám.]"..
+ "tooltip[ioffsetnow;Nastaví posun intervalu tak\\, aby pro tuto kolej nový interval začínal právě teď.\n"..
+ "Také uloží ostatní provedené změny.]"
+
+
+ minetest.show_formspec(pname, "at_lines_stop_"..pe, formspec)
end
local tmp_checkboxes = {}
minetest.register_on_player_receive_fields(function(player, formname, fields)
@@ -91,34 +159,54 @@ minetest.register_on_player_receive_fields(function(player, formname, fields)
if fields.reverse then
tmp_checkboxes[pe].reverse = (fields.reverse == "true")
end
+ if fields.keepopen then
+ tmp_checkboxes[pe].keepopen = (fields.keepopen == "true")
+ end
if fields.waitsig then
tmp_checkboxes[pe].waitsig = (fields.waitsig == "true")
end
- if fields.save then
- if fields.stn and stdata.stn ~= fields.stn and fields.stn ~= "" then
- local stn = advtrains.lines.stations[fields.stn]
- if stn then
- if (stn.owner == pname or minetest.check_player_privs(pname, "train_admin")) then
- stdata.stn = fields.stn
- else
- minetest.chat_send_player(pname, S("Station code \"@1\" already exists and is owned by @2.", fields.stn, stn.owner))
- show_stoprailform(pos,player)
- return
- end
- else
- advtrains.lines.stations[fields.stn] = {name = fields.stnname, owner = pname}
- stdata.stn = fields.stn
- end
+
+ if fields.stn then
+ local new_index = tonumber(fields.stn)
+ if new_index ~= nil then
+ player_to_stn_override[pname] = new_index
+ end
+ end
+
+ local set_offset
+
+ 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 rwtime = rwt.to_secs(rwt.get_time())
+ set_offset = rwtime % interval
end
- local stn = advtrains.lines.stations[stdata.stn]
- if stn and fields.stnname and fields.stnname~="" and fields.stnname ~= stn.name then
- if (stn.owner == pname or minetest.check_player_privs(pname, "train_admin")) then
- stn.name = fields.stnname
+ end
+
+ if fields.save or set_offset ~= nil then
+ local new_index = player_to_stn_override[pname]
+ if new_index ~= nil then
+ if new_index == 1 then
+ -- no name station
+ stdata.stn = ""
+ minetest.log("action", pname.." set track at "..minetest.pos_to_string(pos).." to no station.")
else
- minetest.chat_send_player(pname, S("This station is owned by @1. You are not allowed to edit its name.", stn.owner))
+ local stations = advtrains.lines.load_stations_for_formspec()
+ local station = stations[new_index - 1]
+ if station ~= nil then
+ if station.owner == pname or minetest.check_player_privs(pname, "train_admin") then
+ stdata.stn = station.stn
+ minetest.log("action", pname.." set track at "..minetest.pos_to_string(pos).." to station '"..tostring(station.stn).."'.")
+ else
+ minetest.chat_send_player(pname, attrans("Station code '@1' does already exist and is owned by @2", station.stn, station.owner))
+ show_stoprailform(pos,player)
+ return
+ end
+ end
end
+ player_to_stn_override[pname] = nil
end
-
+
-- dropdowns
if fields.doors then
stdata.doors = door_dropdown_code[tonumber(fields.doors)] or "C" -- switch to numerical index of selection; attention : fields.doors is string typed, needed to be converted to an integer typed index in door_dropdown_code table
@@ -141,6 +229,48 @@ minetest.register_on_player_receive_fields(function(player, formname, fields)
if fields.speed then
stdata.speed = to_int(fields.speed) or "M"
end
+ if fields.line then
+ stdata.line = fields.line
+ end
+ if fields.routingcode then
+ stdata.routingcode = fields.routingcode
+ end
+ if fields.minparts then
+ local v = to_int(fields.minparts)
+ if v <= 0 or v > 128 then v = nil end
+ stdata.minparts = v
+ end
+ if fields.maxparts then
+ local v = to_int(fields.maxparts)
+ if v <= 0 or v > 128 then v = nil end
+ stdata.maxparts = v
+ end
+ if fields.interval then
+ if fields.interval == "" or fields.interval == "0" then
+ stdata.interval = nil
+ else
+ local n = to_int(fields.interval)
+ if 0 < n and n <= 3600 then
+ stdata.interval = n
+ end
+ end
+ end
+ if stdata.interval == nil then
+ stdata.ioffset = nil
+ elseif set_offset ~= nil then
+ stdata.ioffset = set_offset
+ elseif fields.ioffset then
+ if fields.ioffset == "" or fields.ioffset == "0" then
+ stdata.ioffset = nil
+ else
+ local n = to_int(fields.ioffset)
+ if n > 0 then
+ stdata.ioffset = n % stdata.interval
+ else
+ stdata.ioffset = nil
+ end
+ end
+ end
for k,v in pairs(tmp_checkboxes[pe]) do --handle checkboxes
stdata[k] = v or nil
@@ -148,13 +278,17 @@ minetest.register_on_player_receive_fields(function(player, formname, fields)
tmp_checkboxes[pe] = nil
--TODO: signal
updatemeta(pos)
+ minetest.log("action", pname.." saved stoprail at "..minetest.pos_to_string(pos))
show_stoprailform(pos, player)
+ elseif fields.editstn and advtrains.lines.open_station_editor ~= nil then
+ minetest.close_formspec(pname, formname)
+ minetest.after(0.25, advtrains.lines.open_station_editor, player)
+ return
end
end
end)
-
local adefunc = function(def, preset, suffix, rotation)
return {
after_place_node=function(pos)
@@ -168,59 +302,42 @@ local adefunc = function(def, preset, suffix, rotation)
local pe = advtrains.encode_pos(pos)
advtrains.lines.stops[pe] = nil
end,
+ on_punch = function(pos, node, puncher, pointed_thing)
+ updatemeta(pos)
+ end,
on_rightclick = function(pos, node, player)
+ if minetest.is_player(player) then
+ player_to_stn_override[player:get_player_name()] = nil
+ end
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 stdata.ars and (stdata.ars.default or advtrains.interlocking.ars_check_rule_match(stdata.ars, train) ) then
- advtrains.lzb_add_checkpoint(train, index, 2, nil)
- local stn = advtrains.lines.stations[stdata.stn]
- local stnname = stn and stn.name or S("Unknown Station")
- train.text_inside = S("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 stdata.ars and (stdata.ars.default or advtrains.interlocking.ars_check_rule_match(stdata.ars, train) ) then
- local stn = advtrains.lines.stations[stdata.stn]
- local stnname = stn and stn.name or S("Unknown Station")
-
- -- Send ATC command and set text
- advtrains.atc.train_set_command(train, "B0 W O"..stdata.doors..(stdata.kick and "K" or "")
- .." D"..stdata.wait.." "..(stdata.reverse and "R" or "")
- .." A1 "..(stdata.waitsig and "G" or "")
- .." OC D"..(stdata.ddelay or 1) .. " S" ..(stdata.speed or "M"), true)
- train.text_inside = stnname
- if tonumber(stdata.wait) then
- minetest.after(tonumber(stdata.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
+advtrains.station_stop_rail_additional_definition = adefunc -- HACK for tieless_tracks
+
+minetest.register_lbm({
+ label = "Update line track metadata",
+ name = "advtrains_line_automation:update_metadata",
+ nodenames = {
+ "advtrains_line_automation:dtrack_stop_st",
+ "advtrains_line_automation:dtrack_stop_st_30",
+ "advtrains_line_automation:dtrack_stop_st_45",
+ "advtrains_line_automation:dtrack_stop_st_60",
+ "advtrains_line_automation:dtrack_stop_tieless_st",
+ "advtrains_line_automation:dtrack_stop_tieless_st_30",
+ "advtrains_line_automation:dtrack_stop_tieless_st_40",
+ "advtrains_line_automation:dtrack_stop_tieless_st_60",
+ },
+ run_at_every_load = true,
+ action = updatemeta,
+})
+
if minetest.get_modpath("advtrains_train_track") ~= nil then
advtrains.register_tracks("default", {
nodename_prefix="advtrains_line_automation:dtrack_stop",
@@ -232,4 +349,12 @@ if minetest.get_modpath("advtrains_train_track") ~= nil then
formats={},
get_additional_definiton = adefunc,
}, advtrains.trackpresets.t_30deg_straightonly)
+
+ minetest.register_craft({
+ output = "advtrains_line_automation:dtrack_stop_placer 2",
+ recipe = {
+ {"default:coal_lump", ""},
+ {"advtrains:dtrack_placer", "advtrains:dtrack_placer"},
+ },
+ })
end
diff --git a/advtrains_line_automation/structs.md b/advtrains_line_automation/structs.md
new file mode 100644
index 0000000..5b2c6c0
--- /dev/null
+++ b/advtrains_line_automation/structs.md
@@ -0,0 +1,190 @@
+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,
+
+ -- údaje o poslední skončené jízdě na lince, dokud se nezmění číslo linky a dokud neuplyne 24 žel. hodin (cyklů)
+ -- nevyplní se, pokud vlak skončí jízdu jinak než zastavením na koncové zastávce
+ linevar_past = {
+ -- označení linky (LINE z linevar)
+ line = string,
+ -- linevar poslední jízdy na lince
+ linevar = string,
+ -- kód koncové zastávky, kde vlak skončil jízdu na lince
+ station = string,
+ -- žel. čas příjezdu na koncovou zastávku
+ arrival = int,
+ } 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,
+
+ -- 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,
+
+ -- je-li neprázdný řetězec, udává označení linky, na kterou bude vlak pravděpodobně pokračovat
+ -- ze zastávky v režimu MODE_FINAL_CONTINUE
+ continue_line = string or nil,
+
+ -- je-li continue_line neprázdný řetězec, udává směrový kód pro pokračování
+ continue_rc = string or nil,
+
+ -- index zobrazované výchozí zastávky v poli 'stops'; nil značí, že taková zastávka nebyla nalezena
+ index_vychozi = int or nil,
+
+ -- index zobrazované cílové zastávky v poli 'stops'; nil značí, že taková zastávka nebyla nalezena
+ index_cil = int 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,
+
+ -- předpokládaný čas stání před časem odjezdu (používá se k zjištění času příjezdu)
+ -- je-li nil, počítá se 10 sekund
+ -- výjimka: pro koncové zastávky udává předpokládanou dobu stání po čase 'dep'
+ wait = int or nil,
+
+ -- 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,
+ }...
+ }
+ }
+ },
+ anns = { -- staniční rozhlasy
+ [encoded_pos] = {
+ cedule = {
+ -- formát prázdné řádky pro danou ceduli
+ empty = string,
+ -- pozice připojené cedule ve tvaru pro použití ve formspecu, nebo "", pokud daná cedule není připojená
+ fs = string,
+ -- pozice připojené cedule ve formě vektoru
+ pos = vector,
+ -- formát řádky s odjezdem pro danou ceduli
+ row = string,
+ -- formát pro sestavení textu cedule z řádků; může používat značky {1} až {9} a může mít víc řádek
+ text = "{1}{2}",
+ -- seznam řádků, které jsou odkazovány v poli 'text', nebo prázdný řetězec, pokud nejsou odkazovány žádné
+ text_rtf = {int, ...} or "",
+ },
+ -- dosah zpráv v četu (>= 0, nil znamená 50):
+ chat_dosah = int or nil,
+
+ -- formát pro kladné zpoždění
+ fmt_delay = string,
+
+ -- formát pro záporné zpoždění
+ fmt_negdelay = string,
+
+ -- formát pro „bez zpoždění“
+ fmt_nodelay = string,
+
+ -- udává, zda na cedulích bude první znak každého řádku s odjezdem (textu dosazeného za značku {1} až {9})
+ -- převeden na velké písmeno:
+ fn_firstupper = bool,
+
+ -- obsah pole "koleje" zformátovaný pro použití ve formspecu; prázdný řetězec "" znamená, že st. rozhlas platí pro všechny koleje
+ -- a na .koleje pak nezáleží
+ fs_koleje = string,
+
+ -- pokud st. rozhlas není omezený na určité koleje, nil nebo ""
+ -- je-li omezen na jednu konkrétní kolej, pak jde o název této koleje
+ -- je-li omezen na více kolejí, pak jde o množinu indexovanou označeními kolejí
+ koleje = {[string] = true, ...} or string or nil,
+
+ -- vlastník/ice staničního rozhlasu
+ owner = string,
+
+ -- režim rozhlasu (RMODE_*)
+ rmode = int,
+
+ -- číslo verze systému staničního rozhlasu (pro detekci zastaralých rozhlasů)
+ version = int,
+
+ -- řetězce pro formátování hlášení v četu; nemusí být uvedeny všechny, nil znamená použít výchozí text
+ tx_* = 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,
+ -- původně naplánovaná doba stání vlaku vztahující se k last_dep
+ last_wait = int or nil,
+}
+
+local current_passages = {--[[
+ [train_id] = {[1] = rwtime, ..., [n] = rwtime (časy *odjezdu*, kromě koncových zastávek, kde jde o čas příjezdu)}
+]]}
+
+local last_passages = {--[[
+ [linevar] = {
+ [1..10] = {[1] = rwtime, ...} -- jízdy seřazeny od nejstarší (1) po nejnovější (až 10) podle odjezdu z výchozí zastávky
+ }
+]]}
diff --git a/advtrains_line_automation/textures/advtrains_dtrack_shared_stop.png b/advtrains_line_automation/textures/advtrains_dtrack_shared_stop.png
index b6629cf..956b6b4 100644
--- a/advtrains_line_automation/textures/advtrains_dtrack_shared_stop.png
+++ b/advtrains_line_automation/textures/advtrains_dtrack_shared_stop.png
Binary files differ
diff --git a/advtrains_line_automation/textures/advtrains_line_automation_jrad.png b/advtrains_line_automation/textures/advtrains_line_automation_jrad.png
new file mode 100644
index 0000000..627ddf8
--- /dev/null
+++ b/advtrains_line_automation/textures/advtrains_line_automation_jrad.png
Binary files differ
diff --git a/advtrains_line_automation/time_table.lua b/advtrains_line_automation/time_table.lua
new file mode 100644
index 0000000..9e4885e
--- /dev/null
+++ b/advtrains_line_automation/time_table.lua
@@ -0,0 +1,93 @@
+local visual_scale = 15/16
+local node_box = {type = "fixed", fixed = {
+ -16/32, -16/32, 15/32 / visual_scale,
+ 16/32, 16/32, 16/32 / visual_scale,
+}}
+local sbox = {type = "fixed", fixed = {
+ -16/32 * visual_scale, -16/32 * visual_scale, 15/32,
+ 16/32 * visual_scale, 16/32 * visual_scale, 16/32,
+}}
+
+local def = {
+ description = "jízdní řád",
+ drawtype = "nodebox",
+ node_box = node_box,
+ selection_box = sbox,
+ collision_box = sbox,
+ tiles = {
+ {name = "ch_core_white_pixel.png^[multiply:#aaaaaa"},
+ {name = "ch_core_white_pixel.png^[multiply:#aaaaaa"},
+ {name = "ch_core_white_pixel.png^[multiply:#aaaaaa"},
+ {name = "ch_core_white_pixel.png^[multiply:#aaaaaa"},
+ {name = "advtrains_line_automation_jrad.png"},
+ {name = "advtrains_line_automation_jrad.png"},
+ },
+ paramtype = "light",
+ paramtype2 = "4dir",
+ sunlight_propagates = true,
+ groups = {cracky = 3, ch_jrad = 1},
+ sounds = default.node_sound_metal_defaults(),
+ visual_scale = visual_scale,
+ _ch_help = "Použitím (levým klikem) zobrazí jízdní řády všech linek.\nLze umístit do světa a nastavit na jízdní řády v konkrétní stanici/zastávce.\nPo umístění lze také barvit barvicí pistolí.",
+ _ch_help_group = "jrad",
+ after_place_node = function(pos, placer, itemstack, pointed_thing)
+ local player_name = placer and placer:get_player_name()
+ if player_name ~= nil then
+ local meta = core.get_meta(pos)
+ meta:set_string("infotext", "jízdní řád (spravuje: "..ch_core.prihlasovaci_na_zobrazovaci(player_name)..")")
+ meta:set_string("owner", player_name)
+ end
+ end,
+ can_dig = function(pos, player)
+ if player == nil then
+ return false
+ end
+ local player_name = player:get_player_name()
+ if ch_core.get_player_role(player_name) == "admin" then
+ return true
+ end
+ if core.is_protected(pos, player_name) then
+ core.record_protection_violation(pos, player_name)
+ return false
+ end
+ local meta = core.get_meta(pos)
+ local owner = meta:get_string("owner")
+ return owner == "" or owner == player_name
+ end,
+ on_use = function(itemstack, user, pointed_thing)
+ if core.is_player(user) then
+ advtrains.lines.show_jr_formspec(user)
+ end
+ end,
+ on_rightclick = function(pos, node, clicker, itemstack, pointed_thing)
+ if clicker ~= nil and core.is_player(clicker) then
+ local meta = core.get_meta(pos)
+ -- show_jr_formspec(player, pos, stn, track, linevar, stop_stn, force_unprivileged)
+ local force_unprivileged = false
+ if clicker:get_player_control().aux1 then
+ force_unprivileged = true
+ end
+ advtrains.lines.show_jr_formspec(clicker, pos, meta:get_string("stn"), meta:get_string("track"), nil, nil, force_unprivileged)
+ end
+ end,
+}
+
+core.register_node("advtrains_line_automation:jrad", table.copy(def))
+
+def.description = "jízdní řád (na tyč)"
+def.tiles = table.copy(def.tiles)
+def.tiles[5] = def.tiles[1]
+def.node_box = {
+ type = "fixed",
+ fixed = {
+ -16/32, -16/32, 27/32 / visual_scale,
+ 16/32, 16/32, 28/32 / visual_scale,
+ }}
+def.selection_box = {
+ type = "fixed",
+ fixed = {
+ -16/32 * visual_scale, -16/32 * visual_scale, 27/32,
+ 16/32 * visual_scale, 16/32 * visual_scale, 28/32,
+}}
+def.collision_box = def.selection_box
+core.register_node("advtrains_line_automation:jrad_on_pole", def)