aboutsummaryrefslogtreecommitdiff
path: root/advtrains_line_automation/station_announcement.lua
diff options
context:
space:
mode:
Diffstat (limited to 'advtrains_line_automation/station_announcement.lua')
-rw-r--r--advtrains_line_automation/station_announcement.lua1579
1 files changed, 1579 insertions, 0 deletions
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"},
+ },
+})