aboutsummaryrefslogtreecommitdiff
path: root/advtrains_line_automation/line_editor.lua
diff options
context:
space:
mode:
Diffstat (limited to 'advtrains_line_automation/line_editor.lua')
-rw-r--r--advtrains_line_automation/line_editor.lua864
1 files changed, 864 insertions, 0 deletions
diff --git a/advtrains_line_automation/line_editor.lua b/advtrains_line_automation/line_editor.lua
new file mode 100644
index 0000000..2854fb7
--- /dev/null
+++ b/advtrains_line_automation/line_editor.lua
@@ -0,0 +1,864 @@
+local def
+local F = minetest.formspec_escape
+local ch_core = advtrains.lines.ch_core
+local S = advtrains.lines.translate
+--[[
+Jednoduchá funkce, která vyhodnotí condition jako podmínku
+a podle výsledku vrátí buď true_result, nebo false_result.
+]]
+local function ifthenelse(condition, true_result, false_result)
+ if condition then
+ return true_result
+ else
+ return false_result
+ end
+end
+-- Singularis-Merge: Emulate certain ch functionality
+local function prihlasovaci_na_zobrazovaci(prihlasovaci)
+ return prihlasovaci -- no-op. On CH used to colorize the name to reflect the rank of the player
+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, S("No such station")
+ 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, S("Cannot replace, because lineval @1 does not yet exist", linevar)
+ 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, S("@1 trains restarted due to changes", restart_count)
+ 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, S("No such station")
+ end
+ local linevars = station.linevars
+ if linevars[assert(linevar)] == nil then
+ return false, S("Cannot delete, because lineval @1 does not yet exist", linevar)
+ 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;"..S("Line Editor").."]"..
+ "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,"..S("LINE,ROUTE,VAR.")..",#ffffff,"..S("OWNER")..",#ffffff,STATUS")
+
+ 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(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;"..S("New Line...").."]")
+ 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;"..S("Delete Line").."]")
+ 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;"..S("Line:")..";"..F(custom_state.line).."]"..
+ "field[2,7;1.5,0.75;rc;"..S("Variant:")..";"..F(custom_state.rc).."]"..
+ "field[3.75,7;3,0.75;train_name;"..S("Dest. Display:")..";"..F(custom_state.train_name).."]")
+ if pinfo.role ~= "admin" then
+ table.insert(formspec, "label[7,6.75;"..S("Owner:").."\n")
+ else
+ table.insert(formspec, "field[7,7;4,0.75;owner;"..S("Owner:")..";")
+ end
+ table.insert(formspec, F(custom_state.owner).."]"..
+ "checkbox[11.25,7.25;disable_linevar;"..S("Disable")..";"..custom_state.disable_linevar.."]"..
+ "field[13.5,7;3,0.75;continues;"..S("Continues:")..";"..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;"..S("Last passages").."]"..
+ "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, S("Verify/Save").."]", S("Save changes").."]"))
+ end
+ table.insert(formspec, "button[15,15.25;4,0.5;reset;"..S("Reset changes").."]")
+ table.insert(formspec, "tooltip[line;"..
+ S("Line designation. Must not be empty. This identifies a certain line.").."]"..
+ "tooltip[rc;"..S("Line variant code. Optional, can be used to identify different variants of a line (e.g. branches).").."]"..
+ "tooltip[train_name;"..S("Optional field, name of the line as used in departure displays and on timetables.").."]"..
+ "tooltip[disable_linevar;"..S("Check to disable the timetable.\nA disabled timetable will not be used for new trains, trains already on the line will finish it.").."]")
+
+ table.insert(formspec, "container[0,8.75]"..
+ "label[0.5,0.25;"..S("Dep.").."]"..
+ "label[2,0.25;"..S("St.Time").."]"..
+ "label[3.5,0.25;"..S("Station Code").."]"..
+ "label[6.25,0.25;"..S("Mode").."]"..
+ "label[11,0.25;"..S("Track").."]"..
+ "label[12.5,0.25;"..S("Position").."]"..
+ "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;"..S("Normal")..","..S("Hidden (normal)")..";"..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;"..S("Normal")..","..S("Request Stop (experimental)")..","..S("Hidden")..","..S("Inactive")..","..S("Terminus")..","..S("Terminus (hidden)")..","..
+ S("Terminus (continuing)")..";"..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;"..S("Departure: Time (in seconds) after the start of the timetable at which the train departs from this station.\nIt is used to calculate delays. Stations on the line are sorted by their departure time.\nSet this field empty to delete a station from the timetable.").."]"..
+ "tooltip[1.5,0;1.5,1;"..S("Stop time: Minimum time (in seconds) for which the train stops at the station before departing.").."]"..
+ "tooltip[3.5,0;2.75,1;"..S("Station code: The code of the station to stop/pass.").."]"..
+ "tooltip[6.25,0;4.75,1;"..S("Stop mode:\nNormal - Train stops and departs after the stop time\nRequest stop - Train stops when the 'stop request' button is pressed\nHidden - like Normal, but is not shown in passenger timetable\nInactive - passes without stopping\nTerminus - Train stops and ends the timetable operation\nTerminus (hidden) - like Terminus, but will not appear in the passenger timetable\nTerminus (continuing) - Train will continue as timetable configured in the stop rail at the terminus location").."]"..
+ "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 = 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, S("Line name must not be empty!")
+ elseif line:find("[/|\\]") then
+ return false, S("Line name must not contain characters '/', '|' or '\\'!")
+ elseif line:len() > 256 then
+ return false, S("Line name must not exceed 256 characters!")
+ elseif stn == "" then
+ return false, S("Starting station code must not be empty!")
+ elseif rc:find("[/|\\]") then
+ return false, S("Routing code must not contain '/', '|' or '\\'!")
+ elseif owner == "" then
+ return false, S("Line owner must not be empty!")
+ elseif train_name:len() > 256 then
+ return false, S("Train name must not exceed 256 characters!")
+ elseif custom_state.continues:len() - custom_state.continues:gsub("/", ""):len() > 1 then
+ return false, S("Field 'Continuation' must contain maximum 1 '/'!")
+ 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..S("Incorrect departure time format!")
+ elseif tonumber(stop.dep) < 0 or tonumber(stop.dep) > 3600 then
+ errcount = errcount + 1
+ stop.label = color_red..S("Departure time must lie within 0-3600 seconds!")
+ elseif dep_to_index[tonumber(stop.dep)] ~= nil then
+ errcount = errcount + 1
+ stop.label = color_red..S("Duplicate departure time!")
+ 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..S("Unknown station code!")
+ elseif stop.stn:find("[/|\\]") then
+ errcount = errcount + 1
+ stop.label = color_red..S("Station code must not contain '/', '|' or '\\'!")
+ elseif stop.track:len() > 16 then
+ errcount = errcount + 1
+ stop.label = color_red..S("Line name must not exceed 16 characters!")
+ 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..S("Incorrect format for stop position!")
+ elseif stop.pos:len() > 22 then
+ errcount = errcount + 1
+ stop.label = color_red..S("Stop position must not exceed 22 characters!")
+ 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, S("@1 errors while checking line setup!", errcount)
+ end
+ if finalcount == 0 then
+ return false, S("Line setup must contain at least 1 terminus stop!")
+ 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 = 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..S("No issues found, the line can now be saved!")
+ else
+ custom_state.message = color_red..S("Error in line setup: ")..(errmsg or S("Unknown reason"))
+ 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 = S("Insufficient permissions to edit '@1'.", 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 = S("Insufficient permissions to edit '@1'.", 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 = S("Insufficient permissions to edit '@1'.", selected_linevar)
+ end
+ else
+ errmsg = S("Insufficient permissions to edit '@1'.", 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 = S("Insufficient permissions to edit '@1'.", to_linevar)
+ end
+ end
+
+ if success then
+ custom_state.message = color_green..S("Line '@1' successfully saved!", new_linevar)
+ custom_state_refresh_linevars(custom_state, new_linevar)
+ else
+ custom_state.message = color_red..S("Error in line setup: ")..(errmsg or S("Unknown reason"))
+ 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 = S("Insufficient permissions to edit '@1'.", selected_linevar)
+ end
+ if success then
+ custom_state.message = S("Line '@1' successfully deleted!", selected_linevar)
+ custom_state_refresh_linevars(custom_state)
+ custom_state_set_selection_index(custom_state, 1)
+ else
+ custom_state.message = S("Deletion failed: ")..(errmsg or S("Unknown reason"))
+ 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
+
+-- make line editor globally available
+advtrains.lines.open_line_editor = show_editor_formspec
+
+local function lp_formspec_callback(custom_state, player, formname, fields)
+ if fields.back then
+ show_editor_formspec(player, custom_state.selected_linevar)
+ elseif fields.update then
+ local selected_linevar_def = try_get_linevar_def(custom_state.selected_linevar)
+ if selected_linevar_def ~= nil then
+ show_last_passages_formspec(player, selected_linevar_def, custom_state.selected_linevar)
+ end
+ end
+end
+
+show_last_passages_formspec = function(player, linevar_def, selected_linevar)
+ local rwt = advtrains.lines.rwt
+ local formspec = {
+ "formspec_version[6]"..
+ "size[20,10]"..
+ "label[0.5,0.6;",
+ S("Last passages of line @1", 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;",S("Code"),",",S("Station"),",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, ",,"..S("At start station:"))
+ 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]).." "..S("(departed)"))
+ for j = 1, 10 do
+ local time = passages[j][1]
+ if time ~= nil then
+ table.insert(formspec, ",("..core.formspec_escape(rwt.t_str(time, 1))..")")
+ if rwt.is_before(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, ","..(rwt.diff(dep_vych, time)))
+ if rwt.is_before(max_time[j], time) then
+ max_time[j] = time
+ end
+ else
+ table.insert(formspec, ",-")
+ end
+ end
+ end
+ table.insert(formspec, ",,"..S("Travelling:"))
+ for i = 1, 10 do
+ if max_time[i] ~= 0 then
+ table.insert(formspec, ",_"..(rwt.diff(passages[i][1], max_time[i])).."_")
+ else
+ table.insert(formspec, ",-")
+ end
+ end
+ end
+ table.insert(formspec, ";]"..
+ "button[13.75,0.3;1.75,0.75;update;"..S("Update").."]"..
+ "button[17.75,0.3;1.75,0.75;back;"..S("Back").."]"..
+ "tooltip[jizdy;"..S("Time data in seconds of railway time").."]")
+ 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 = S("Open line editor"),
+ privs = {railway_operator = true},
+ func = function(player_name, param) show_editor_formspec(minetest.get_player_by_name(player_name)) end,
+}
+core.register_chatcommand("line_editor", def)