aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--advtrains/helpers.lua4
-rw-r--r--advtrains_interlocking/database.lua192
-rw-r--r--advtrains_interlocking/init.lua1
-rw-r--r--advtrains_interlocking/tcb.lua180
4 files changed, 377 insertions, 0 deletions
diff --git a/advtrains/helpers.lua b/advtrains/helpers.lua
index ce059de..2160444 100644
--- a/advtrains/helpers.lua
+++ b/advtrains/helpers.lua
@@ -334,4 +334,8 @@ function advtrains.random_id()
end
return idst
end
+-- Shorthand for pos_to_string and round_vector_floor_y
+function advtrains.roundfloorpts(pos)
+ return minetest.pos_to_string(advtrains.round_vector_floor_y(pos))
+end
diff --git a/advtrains_interlocking/database.lua b/advtrains_interlocking/database.lua
index 61bf5ad..e318dd2 100644
--- a/advtrains_interlocking/database.lua
+++ b/advtrains_interlocking/database.lua
@@ -32,6 +32,9 @@ Route setting fails whenever any TC that we want to set ROUTE to is already set
Apart from this, we need to set turnouts
- Turnouts on the track are set held as ROUTE
- Turnouts that purpose as flank protection are set held as FLANK (NOTE: left as an idea for later, because it's not clear how to do this properly without an engineer)
+Note: In SimSig, it is possible to set a route into an still occupied section on the victoria line sim. (at the depot exit at seven sisters), although
+ there are still segments set ahead of the first train passing, remaining from another route.
+ Because our system will be able to remember "requested routes" and set them automatically once ready, this is not necessary here.
== Call-On/Multiple Trains ==
It will be necessary to join and split trains using call-on routes. A call-on route may be set when:
- there are no ROUTE reservations
@@ -50,7 +53,196 @@ CALL_ON_ALLOWED - Whether this TC being blocked (TRAIN or ROUTE) does not preven
== More notes ==
- It may not be possible to switch turnouts when their TC has any state entry
+== Route releasing (TORR) ==
+A train passing through a route happens as follows:
+Route set from entry to exit signal
+Train passes entry signal and enters first TC past the signal
+-> Route from signal cleared (TCs remain locked)
+-> ROUTE status of first TC past signal cleared
+Train continues along the route.
+Whenever train leaves a TC
+-> Clearing any routes set from this TC outward recursively - see "Reversing problem"
+Whenever train enters a TC
+-> Clear route status from the just entered TC
+== Reversing Problem ==
+Encountered at the Royston simulation in SimSig. It is solved there by imposing a time limit on the set route. Call-on routes can somehow be set anyway.
+Imagine this setup: (T=Train, R=Route, >=in_dir TCB)
+ O-| Royston P2 |-O
+T->---|->RRR-|->RRR-|--
+Train T enters from the left, the route is set to the right signal. But train is supposed to reverse here and stops this way:
+ O-| Royston P2 |-O
+------|-TTTT-|->RRR-|--
+The "Route" on the right is still set. Imposing a timeout here is a thing only professional engineers can determine, not an algorithm.
+ O-| Royston P2 |-O
+<-T---|------|->RRR-|--
+The train has left again, while route on the right is still set.
+So, we have to clear the set route when the train has left the left TC.
+This does not conflict with call-on routes, because both station tracks are set as "allow call-on"
+Because none of the routes extends past any non-call-on sections, call-on route would be allowed here, even though the route
+is locked in opposite direction at the time of routesetting.
+Another case of this:
+--TTT/--|->RRR--
+The / here is a non-interlocked turnout (to a non-frequently used siding). For some reason, there is no exit node there,
+so the route is set to the signal at the right end. The train is taking the exit to the siding and frees the TC, without ever
+having touched the right TC.
]]--
+local TRAVERSER_LIMIT = 100
+
+
+local ildb = {}
+
+local track_circuit_breaks = {}
+
+function ildb.load(data)
+
+end
+
+function ildb.save()
+ return {}
+end
+
+--
+--[[
+TCB data structure
+{
+[1] = { -- Variant: with adjacent TCs.
+ == Synchronized properties == Apply to the whole TC
+ adjacent = { <signal specifier>,... } -- Adjacent TCBs, forms a TC with these
+ conflict = { <signal specifier>,... } -- Conflicting TC's (chosen as a representative TCB member)
+ -- Used e.g. for crossing rails that do not have nodes in common (like it's currently done)
+ incomplete = <boolean> -- Set when the recursion counter hit during traverse. Probably needs to add
+ -- another tcb at some far-away place
+ route = {origin = <signal>, in_dir = <boolean>}
+ -- Set whenever a route has been set through this TC. It saves the origin tcb id and side
+ -- (=the origin signal). in_dir is set when the train will enter the TC from this side
+
+ == Unsynchronized properties == Apply only to this side of the TC
+ signal = <pos> -- optional: when set, routes can be set from this tcb/direction and signal
+ -- aspect will be set accordingly.
+ routetar = <signal> -- Route set from this signal. This is the entry that is cleared once
+ -- train has passed the signal. (which will set the aspect to "danger" again)
+ route_committed = <boolean> -- When setting/requesting a route, routetar will be set accordingly,
+ -- while the signal still displays danger and nothing is written to the TCs
+ -- As soon as the route can actually be set, all relevant TCs and turnouts are set and this field
+ -- is set true, clearing the signal
+},
+[2] = { -- Variant: end of track-circuited area (initial state of TC)
+ end_of_interlocking = true,
+ section_free = <boolean>, --this can be set by an exit node via mesecons or atlatc,
+ -- or from the tc formspec.
+}
+}
+Signal specifier (a pair of TCB/Side):
+{p = <pos>, s = <1/2>}
+]]
+
+
+--
+function ildb.create_tcb(pos)
+ local new_tcb = {
+ [1] = {end_of_interlocking = true},
+ [2] = {end_of_interlocking = true},
+ }
+ local pts = advtrains.roundfloorpts(pos)
+ track_circuit_breaks[pts] = new_tcb
+end
+
+function ildb.get_tcb(pos)
+ local pts = advtrains.roundfloorpts(pos)
+ return track_circuit_breaks[pts]
+end
+
+-- This function will actually handle the node that is in connid direction from the node at pos
+-- so, this needs the conns of the node at pos, since these are already calculated
+local function traverser(found_tcbs, pos, conns, connid, count)
+ local adj_pos, adj_connid, conn_idx, nextrail_y, next_conns = advtrains.get_adjacent_rail(pos, conns, connid, advtrains.all_tracktypes)
+ if not adj_pos then
+ -- end of track
+ return
+ end
+ -- look whether there is a TCB here
+ if #next_conns == 2 then --if not, don't even try!
+ local tcb = ildb.get_tcb(adj_pos)
+ if tcb then
+ -- done with this branch
+ table.insert(found_tcbs, {p=adj_pos, s=adj_connid})
+ return
+ end
+ end
+ -- recursion abort condition
+ if count > TRAVERSER_LIMIT then
+ atdebug("Traverser hit counter at",adj_pos, adj_connid,"found tcb's:",found_tcbs)
+ return true
+ end
+ -- continue traversing
+ local counter_hit = false
+ for nconnid, nconn in ipairs(next_conns) do
+ if adj_connid ~= nconnid then
+ counter_hit = counter_hit or traverser(found_tcbs, adj_pos, next_conns, nconnid, count + 1, hit_counter)
+ end
+ end
+ return counter_hit
+end
+
+local function sigd_equal(sigd, cmp)
+ return vector.equals(sigd.p, cmp.p) and sigd.s==cmp.s
+end
+
+
+
+
+
+-- Updates the neighbors of this TCB using the traverser function (see comments above)
+-- returns true if the traverser hit the counter, which means that there could be another
+-- TCB outside of the traversed range.
+function ildb.update_tcb_neighbors(pos, connid)
+ local found_tcbs = { {p = pos, s = connid} }
+ local node_ok, conns, rhe = advtrains.get_rail_info_at(pos, advtrains.all_tracktypes)
+ if not node_ok then
+ error("update_tcb_neighbors but node is NOK: "..minetest.pos_to_string(pos))
+ end
+
+ local counter_hit = traverser(found_tcbs, pos, conns, connid, 0, hit_counter)
+
+ for idx, sigd in pairs(found_tcbs) do
+ local tcb = ildb.get_tcb(sigd.p)
+ local tcbs = tcb[sigd.s]
+
+ tcbs.end_of_interlocking = nil
+ tcbs.incomplete = counter_hit
+ tcbs.adjacent = {}
+
+ for idx2, other_sigd in pairs(found_tcbs) do
+ if idx~=idx2 then
+ ildb.add_adjacent(tcbs, sigd.p, sigd.s, other_sigd)
+ end
+ end
+ end
+
+ return hit_counter
+end
+
+-- Add the adjacency entry into the tcbs, but without duplicating it
+-- and without adding a self-reference
+function ildb.add_adjacent(tcbs, this_pos, this_connid, sigd)
+ if sigd_equal(sigd, {p=this_pos, s=this_connid}) then
+ return
+ end
+ tcbs.end_of_interlocking = nil
+ if not tcbs.adjacent then
+ tcbs.adjacent = {}
+ end
+ for idx, cmp in pairs(tcbs.adjacent) do
+ if sigd_equal(sigd, cmp) then
+ return
+ end
+ end
+ table.insert(tcbs.adjacent, sigd)
+end
+
+advtrains.interlocking.db = ildb
+
+
diff --git a/advtrains_interlocking/init.lua b/advtrains_interlocking/init.lua
index ab79573..e3be234 100644
--- a/advtrains_interlocking/init.lua
+++ b/advtrains_interlocking/init.lua
@@ -6,5 +6,6 @@ advtrains.interlocking = {}
local modpath = minetest.get_modpath(minetest.get_current_modname()) .. DIR_DELIM
dofile(modpath.."database.lua")
+dofile(modpath.."tcb.lua")
dofile(modpath.."signal_api.lua")
dofile(modpath.."demosignals.lua")
diff --git a/advtrains_interlocking/tcb.lua b/advtrains_interlocking/tcb.lua
new file mode 100644
index 0000000..742bb62
--- /dev/null
+++ b/advtrains_interlocking/tcb.lua
@@ -0,0 +1,180 @@
+-- Track Circuit Breaks - Player interaction
+
+local players_assign_tcb = {}
+local players_addfar_tcb = {}
+
+local lntrans = { "A", "B" }
+
+local function sigd_to_string(sigd)
+ return minetest.pos_to_string(sigd.p).." / "..lntrans[sigd.s]
+end
+
+minetest.register_node("advtrains_interlocking:tcb_node", {
+ drawtype = "mesh",
+ paramtype="light",
+ paramtype2="facedir",
+ walkable = true,
+ selection_box = {
+ type = "fixed",
+ fixed = {-1/4, -1/2, -1/4, 1/4, 1/2, 1/4},
+ },
+ mesh = "at_il_tcb_node.obj",
+ tiles = {"at_il_tcb_node.png"},
+ description="Track Circuit Break",
+ sunlight_propagates=true,
+ groups = {
+ cracky=3,
+ not_blocking_trains=1,
+ --save_in_at_nodedb=2,
+ },
+ after_place_node = function(pos, node, player)
+ local meta = minetest.get_meta(pos)
+ meta:set_string("infotext", "Unconfigured Track Circuit Break, right-click to assign.")
+ end,
+ on_rightclick = function(pos, node, player)
+ local meta = minetest.get_meta(pos)
+ local tcbpts = meta:get_string("tcb_pos")
+ local pname = player:get_player_name()
+ if tcbpts ~= "" then
+ local tcbpos = minetest.string_to_pos(tcbpts)
+ advtrains.interlocking.show_tcb_form(tcbpos, pname)
+ else
+ --unconfigured
+ --TODO security
+ minetest.chat_send_player(pname, "Configuring TCB: Please punch the rail you want to assign this TCB to.")
+
+ players_assign_tcb[pname] = pos
+ end
+ end,
+ on_punch = function(pos, node, player)
+ local meta = minetest.get_meta(pos)
+ atwarn("Would show tcb marker.")
+ -- TODO TCB-Marker anzeigen
+ end,
+})
+
+minetest.register_on_punchnode(function(pos, node, player, pointed_thing)
+ local pname = player:get_player_name()
+ local tcbnpos = players_assign_tcb[pname]
+ if tcbnpos then
+ if vector.distance(pos, tcbnpos)<=20 then
+ local node_ok, conns, rhe = advtrains.get_rail_info_at(pos, advtrains.all_tracktypes)
+ if node_ok and #conns == 2 then
+ advtrains.interlocking.db.create_tcb(pos)
+
+ advtrains.interlocking.db.update_tcb_neighbors(pos, 1)
+ advtrains.interlocking.db.update_tcb_neighbors(pos, 2)
+
+ local meta = minetest.get_meta(tcbnpos)
+ meta:set_string("tcb_pos", minetest.pos_to_string(pos))
+ meta:set_string("infotext", "TCB assigned to "..minetest.pos_to_string(pos))
+ minetest.chat_send_player(pname, "Configuring TCB: Successfully configured TCB")
+ else
+ minetest.chat_send_player(pname, "Configuring TCB: This is not a normal two-connection rail! Aborted.")
+ end
+ else
+ minetest.chat_send_player(pname, "Configuring TCB: Node is too far away. Aborted.")
+ end
+ players_assign_tcb[pname] = nil
+ end
+end)
+
+
+local function mkformspec(tcbs, btnpref, offset, pname)
+ local form = "label[0.5,"..offset..";Side "..btnpref..": "..(tcbs.end_of_interlocking and "End of interlocking" or "Track Circuit").."]"
+ if tcbs.end_of_interlocking then
+ form = form.."button[0.5,"..(offset+1)..";3,1;"..btnpref.."_clearadj;Activate Interlocking]"
+ if tcbs.section_free then
+ form = form.."button[4.5,"..(offset+1)..";3,1;"..btnpref.."_setlocked;Section is free]"
+ else
+ form = form.."button[4.5,"..(offset+1)..";3,1;"..btnpref.."_setfree;Section is blocked]"
+ end
+ else
+ local strtab = {}
+ for idx, sigd in ipairs(tcbs.adjacent) do
+ strtab[idx] = minetest.formspec_escape(sigd_to_string(sigd))
+ end
+ form = form.."textlist[0.5,"..(offset+1)..";5,3;"..btnpref.."_adjlist;"..table.concat(strtab, ",").."]"
+ if players_addfar_tcb[pname] then
+ local sigd = players_addfar_tcb[pname]
+ form = form.."button[5.5,"..(offset+2)..";2.5,1;"..btnpref.."_addadj;Link TCB to "..sigd_to_string(sigd).."]"
+ form = form.."button[8,"..(offset+2)..";0.5,1;"..btnpref.."_canceladdadj;X]"
+ else
+ form = form.."button[5.5,"..(offset+2)..";3,1;"..btnpref.."_addadj;Add far TCB]"
+ end
+ form = form.."button[5.5,"..(offset+1)..";3,1;"..btnpref.."_clearadj;Clear&Update]"
+ form = form.."button[5.5,"..(offset+3)..";3,1;"..btnpref.."_mknonint;Make non-interlocked]"
+ if tcbs.incomplete then
+ form = form.."label[0.5,"..(offset+0.5)..";Warning: You possibly need to add TCBs manually!]"
+ end
+ end
+ return form
+end
+
+
+
+function advtrains.interlocking.show_tcb_form(pos, pname)
+ local tcb = advtrains.interlocking.db.get_tcb(pos)
+ if not tcb then return end
+
+ local form = "size[10,10] label[0.5,0.5;Track Circuit Break Configuration]"
+ form = form .. mkformspec(tcb[1], "A", 1, pname)
+ form = form .. mkformspec(tcb[2], "B", 6, pname)
+
+ minetest.show_formspec(pname, "at_il_tcbconfig_"..minetest.pos_to_string(pos), form)
+end
+
+
+minetest.register_on_player_receive_fields(function(player, formname, fields)
+ local pname = player:get_player_name()
+ local pts = string.match(formname, "^at_il_tcbconfig_(.+)$")
+ local pos
+ if pts then
+ pos = minetest.string_to_pos(pts)
+ end
+ if pos and not fields.quit then
+ local tcb = advtrains.interlocking.db.get_tcb(pos)
+ if not tcb then return end
+ local f_clearadj = {fields.A_clearadj, fields.B_clearadj}
+ local f_addadj = {fields.A_addadj, fields.B_addadj}
+ local f_canceladdadj = {fields.A_canceladdadj, fields.B_canceladdadj}
+ local f_setlocked = {fields.A_setlocked, fields.B_setlocked}
+ local f_setfree = {fields.A_setfree, fields.B_setfree}
+ local f_mknonint = {fields.A_mknonint, fields.B_mknonint}
+
+ for connid=1,2 do
+ if f_clearadj[connid] then
+ advtrains.interlocking.db.update_tcb_neighbors(pos, connid)
+ end
+ if f_mknonint[connid] then
+ --TODO: remove this from the other tcb's
+ tcb[connid].end_of_interlocking = true
+ end
+ if f_addadj[connid] then
+ if players_addfar_tcb[pname] then
+ local sigd = players_addfar_tcb[pname]
+ advtrains.interlocking.db.add_adjacent(tcb[connid], pos, connid, sigd)
+ players_addfar_tcb[pname] = nil
+ else
+ players_addfar_tcb[pname] = {p = pos, s = connid}
+ end
+ end
+ if f_canceladdadj[connid] then
+ players_addfar_tcb[pname] = nil
+ end
+ if f_setfree[connid] then
+ tcb[connid].section_free = true
+ end
+ if f_setlocked[connid] then
+ tcb[connid].section_free = nil
+ end
+ end
+ advtrains.interlocking.show_tcb_form(pos, pname)
+ end
+
+end)
+
+
+
+
+
a> 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181
msgid ""
msgstr ""
"Project-Id-Version: advtrains\n"
"Report-Msgid-Bugs-To: advtrains-discuss@lists.sr.ht\n"
"POT-Creation-Date: 2026-01-31 22:06+0100\n"
"PO-Revision-Date: 2023-10-09 11:24+0200\n"
"Last-Translator: Y. Wang <yw05@forksworld.de>\n"
"Language-Team: Chinese (Simplified)\n"
"Language: zh_CN\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Generator: Poedit 3.3.2\n"

#: line_editor.lua
msgid "(departed)"
msgstr ""

#: line_editor.lua
msgid "@1 errors while checking line setup!"
msgstr ""

#: line_editor.lua
msgid "@1 trains restarted due to changes"
msgstr ""

#: line_editor.lua
msgid "At start station:"
msgstr ""

#: line_editor.lua
msgid "Back"
msgstr ""

#: line_editor.lua
msgid "Cannot delete, because lineval @1 does not yet exist"
msgstr ""

#: line_editor.lua
msgid "Cannot replace, because lineval @1 does not yet exist"
msgstr ""

#: line_editor.lua
msgid "Code"
msgstr ""

#: line_editor.lua
msgid "Continues:"
msgstr ""

#: line_editor.lua
msgid "Delete Line"
msgstr ""

#: line_editor.lua
msgid "Deletion failed: "
msgstr ""

#: line_editor.lua
msgid "Dep."
msgstr ""

#: line_editor.lua
msgid "Departure time must lie within 0-3600 seconds!"
msgstr ""

#: line_editor.lua
msgid "Dest. Display:"
msgstr ""

#: line_editor.lua
msgid "Disable"
msgstr ""

#: line_editor.lua
msgid "Duplicate departure time!"
msgstr ""

#: line_editor.lua
msgid "Error in line setup: "
msgstr ""

#: line_editor.lua
msgid "Field 'Continuation' must contain maximum 1 '/'!"
msgstr ""

#: line_editor.lua
msgid "Hidden"
msgstr ""

#: line_editor.lua
msgid "Hidden (normal)"
msgstr ""

#: line_editor.lua
msgid "Inactive"
msgstr ""

#: line_editor.lua
msgid "Incorrect departure time format!"
msgstr ""

#: line_editor.lua
msgid "Incorrect format for stop position!"
msgstr ""

#: line_editor.lua
msgid "Insufficient permissions to edit '@1'."
msgstr ""

#: line_editor.lua
msgid "LINE,ROUTE,VAR."
msgstr ""

#: line_editor.lua
msgid "Last passages"
msgstr ""

#: line_editor.lua
msgid "Last passages of line @1"
msgstr ""

#: line_editor.lua
msgid "Line '@1' successfully deleted!"
msgstr ""

#: line_editor.lua
msgid "Line '@1' successfully saved!"
msgstr ""

#: line_editor.lua stoprail.lua
msgid "Line Editor"
msgstr ""

#: line_editor.lua
msgid "Line name must not be empty!"
msgstr ""

#: line_editor.lua
msgid "Line name must not contain characters '/', '|' or '\\'!"
msgstr ""

#: line_editor.lua
msgid "Line name must not exceed 16 characters!"
msgstr ""

#: line_editor.lua
msgid "Line name must not exceed 256 characters!"
msgstr ""

#: line_editor.lua
msgid "Line owner must not be empty!"
msgstr ""

#: line_editor.lua
msgid "Line setup must contain at least 1 terminus stop!"
msgstr ""

#: line_editor.lua
#, fuzzy
msgid "Line:"
msgstr "火车线路"

#: line_editor.lua
msgid "Mode"
msgstr ""

#: line_editor.lua
msgid "New Line..."
msgstr ""

#: line_editor.lua
msgid "No issues found, the line can now be saved!"
msgstr ""

#: line_editor.lua
#, fuzzy
msgid "No such station"
msgstr "ID 为“@1”的列车不存在。"

#: line_editor.lua
msgid "Normal"
msgstr ""

#: line_editor.lua station_editor.lua
msgid "OWNER"
msgstr ""

#: line_editor.lua
msgid "Open line editor"
msgstr ""

#: line_editor.lua
msgid "Owner:"
msgstr ""

#: line_editor.lua
msgid "Position"
msgstr ""

#: line_editor.lua
msgid "Request Stop (experimental)"
msgstr ""

#: line_editor.lua
msgid "Reset changes"
msgstr ""

#: line_editor.lua
msgid "Routing code must not contain '/', '|' or '\\'!"
msgstr ""

#: line_editor.lua
msgid "Save changes"
msgstr ""

#: line_editor.lua
#, fuzzy
msgid "St.Time"
msgstr "停站时间"

#: line_editor.lua
msgid "Starting station code must not be empty!"
msgstr ""

#: line_editor.lua
#, fuzzy
msgid "Station"
msgstr "车站代码"

#: line_editor.lua stoprail.lua
msgid "Station Code"
msgstr "车站代码"

#: line_editor.lua
msgid "Station code must not contain '/', '|' or '\\'!"
msgstr ""

#: line_editor.lua
msgid "Stop position must not exceed 22 characters!"
msgstr ""

#: line_editor.lua line_functions.lua
msgid "Terminus"
msgstr ""

#: line_editor.lua
msgid "Terminus (continuing)"
msgstr ""

#: line_editor.lua
msgid "Terminus (hidden)"
msgstr ""

#: line_editor.lua station_editor.lua stoprail.lua
msgid "Track"
msgstr "轨道"

#: line_editor.lua
msgid "Train name must not exceed 256 characters!"
msgstr ""

#: line_editor.lua
msgid "Travelling:"
msgstr ""

#: line_editor.lua
msgid "Unknown reason"
msgstr ""

#: line_editor.lua
#, fuzzy
msgid "Unknown station code!"
msgstr "车站代码"

#: line_editor.lua
msgid "Update"
msgstr ""

#: line_editor.lua
msgid "Variant:"
msgstr ""

#: line_editor.lua
msgid "Verify/Save"
msgstr ""

#: line_functions.lua
msgid " (on request)"
msgstr ""

#: line_functions.lua
msgid "Next Stop:"
msgstr ""

#: line_functions.lua
msgid "Next Stop: "
msgstr ""

#: line_functions.lua
msgid "Next stop: "
msgstr ""

#: line_functions.lua
msgid "Unknown Station"
msgstr ""

#: station_editor.lua
msgid "(all platforms)"
msgstr ""

#: station_editor.lua
#, fuzzy
msgid "(select station)"
msgstr "ID 为“@1”的列车不存在。"

#: station_editor.lua
msgid "@1 tracks"
msgstr ""

#: station_editor.lua
msgid "Assign"
msgstr ""

#: station_editor.lua
#, fuzzy
msgid "Assign Track"
msgstr "装货轨道"

#: station_editor.lua
msgid "By Distance"
msgstr ""

#: station_editor.lua
msgid "CODE"
msgstr ""

#: station_editor.lua
msgid "Code changed, @1 station tracks updated!"
msgstr ""

#: station_editor.lua
msgid "Create New"
msgstr ""

#: station_editor.lua
msgid "DISTANCE"
msgstr ""

#: station_editor.lua
msgid "Delete"
msgstr ""

#: station_editor.lua
msgid "ERROR: Code must not be empty!"
msgstr ""

#: station_editor.lua
msgid "ERROR: No stop selected!"
msgstr ""

#: station_editor.lua
msgid "ERROR: No such station!"
msgstr ""

#: station_editor.lua
msgid "ERROR: Station '@1' already exists!"
msgstr ""

#: station_editor.lua
msgid "ERROR: There are station tracks assigned, cannot delete the station!"
msgstr ""

#: station_editor.lua
msgid "INFO"
msgstr ""

#: station_editor.lua
#, fuzzy
msgid "Lines"
msgstr "火车线路"

#: station_editor.lua
msgid "My Stations (By Distance)"
msgstr ""

#: station_editor.lua
msgid "My Stations (Station Code A-Z)"
msgstr ""

#: station_editor.lua
msgid "My Stations (Station Name A-Z)"
msgstr ""

#: station_editor.lua
msgid "NAME"
msgstr ""

#: station_editor.lua
msgid "Name changed!"
msgstr ""

#: station_editor.lua
msgid "No departures for the selected line found in the near future"
msgstr ""

#: station_editor.lua
msgid "Nothing has changed."
msgstr ""

#: station_editor.lua
msgid "Open station editor"
msgstr ""

#: station_editor.lua
msgid "Owner changed!"
msgstr ""

#: station_editor.lua
msgid "Owner: "
msgstr ""

#: station_editor.lua
msgid "Positions of tracks:"
msgstr ""

#: station_editor.lua
msgid "Save Changes"
msgstr ""

#: station_editor.lua
msgid "Set"
msgstr ""

#: station_editor.lua
#, fuzzy
msgid "Station Code (A-Z)"
msgstr "车站代码"

#: station_editor.lua
#, fuzzy
msgid "Station Name (A-Z)"
msgstr "车站名称"

#: station_editor.lua
#, fuzzy
msgid "Station Operator: "
msgstr "车站代码"

#: station_editor.lua
#, fuzzy
msgid "Station deleted!"
msgstr "车站代码"

#: station_editor.lua
msgid "Station successfully created."
msgstr ""

#: station_editor.lua
msgid "Timetable for current station (all lines)"
msgstr ""

#: station_editor.lua
msgid "Timetables"
msgstr ""

#: station_editor.lua
msgid "Timetables (all lines)"
msgstr ""

#: station_editor.lua
msgid "Timetables: "
msgstr ""

#: station_editor.lua
msgid "Track successfully assigned!"
msgstr ""

#: station_editor.lua
msgid "Update next departures..."
msgstr ""

#: station_editor.lua
#, fuzzy
msgid "[Station Editor]"
msgstr "车站代码"

#: station_editor.lua
msgid "in operation"
msgstr ""

#: station_editor.lua
msgid "lines"
msgstr ""

#: station_editor.lua
msgid "next departures of the selected line in: "
msgstr ""

#: station_editor.lua
#, fuzzy
msgid "unknown state"
msgstr "车站代码"

#: stoprail.lua
msgid "(foreign)"
msgstr ""

#: stoprail.lua
msgid "(unassigned)"
msgstr ""

#: stoprail.lua
msgid "Arrival Actions"
msgstr ""

#: stoprail.lua
msgid "Departure Actions"
msgstr ""

#: stoprail.lua
msgid "Departure Mode"
msgstr ""

#: stoprail.lua
msgid "Do not close the doors when departing, if they are open"
msgstr ""

#: stoprail.lua
msgid ""
"Do not depart immediately, instead first enable ARS and wait until the next "
"signal ahead clears (ATC G command) before closing the doors and departing."
msgstr ""

#: stoprail.lua
msgid ""
"Do not disable ARS on approaching. Signals behind the stop rail already set "
"ARS routes when the train arrives, not just before departure. (currently not "
"implemented)"
msgstr ""

#: stoprail.lua
msgid "Door Side"
msgstr ""

#: stoprail.lua
msgid "Interval:"
msgstr ""

#: stoprail.lua
msgid "Keep ARS enabled"
msgstr ""

#: stoprail.lua
msgid "Keep doors open"
msgstr ""

#: stoprail.lua
msgid "Kick out passengers"
msgstr "踢出乘客"

#: stoprail.lua
msgid ""
"List of actions to perform on arrival (currently not implemented, later will "
"allow actions such as setting line, RC and displays)"
msgstr ""

#: stoprail.lua
msgid ""
"List of actions to perform on departure (currently not implemented, later "
"will allow actions such as setting line, RC and displays)"
msgstr ""

#: stoprail.lua
msgid ""
"No linevars\n"
"available!"
msgstr ""

#: stoprail.lua
msgid ""
"Non-timetabled trains matching these ARS rules will stop at this position.\n"
"Note: Train must also match the 'Selector' filter above!"
msgstr ""

#: stoprail.lua
msgid ""
"Non-timetabled trains matching these ARS rules will stop at this position.\n"
"Trains under timetable will use the timetable's settings."
msgstr ""

#: stoprail.lua
msgid "Offset:"
msgstr ""

#: stoprail.lua
msgid ""
"Only trains matching these ARS rules will consider this stop rail as "
"suitable timing point/stop position.\n"
"Affects both timetabled and non-timetabled trains.\n"
"Example: define a stop position for long trains (TL 30) and another for "
"short trains (TL 0-30)."
msgstr ""

#: stoprail.lua
msgid "Reverse train"
msgstr "改变行车方向"

#: stoprail.lua
msgid "Save"
msgstr "保存"

#: stoprail.lua
msgid "Select if and on which side the train will open its doors once stopped"
msgstr ""

#: stoprail.lua
msgid ""
"Select the time for departure:\n"
"Normal: depart immediately after the stop time elapsed\n"
"Interval: depart at the next time position set by interval and offset\n"
"Begin Timetable: The train gets the given timetable assigned and departs "
"according to its settings (currently not implemented)"
msgstr ""

#: stoprail.lua
msgid "Selector for stop pos (ARS)"
msgstr ""

#: stoprail.lua
msgid "Selector for stop position..."
msgstr ""

#: stoprail.lua
#, fuzzy
msgid "Speed"
msgstr "速度"

#: stoprail.lua
msgid "Speed that the train sets when departing. Set 'M' for maximum speed."
msgstr ""

#: stoprail.lua
#, fuzzy
msgid "Station Editor"
msgstr "车站代码"

#: stoprail.lua
msgid "Station Name"
msgstr "车站名称"

#: stoprail.lua
msgid "Station code '@1' does already exist and is owned by @2"
msgstr ""

#: stoprail.lua
msgid "Station/Stop Track"
msgstr "车站轨道"

#: stoprail.lua
msgid "Stn. @1 (@2) T. @3"
msgstr ""

#: stoprail.lua
msgid "Stop Time"
msgstr "停站时间"

#: stoprail.lua
msgid "Stopping trains (ARS)"
msgstr ""

#: stoprail.lua
msgid ""
"The interval / time distance between departures in seconds. E.g. every two "
"minutes -> set interval = 120"
msgstr ""

#: stoprail.lua
msgid ""
"The offset of departures from time 0:00 in seconds. E.g. interval 120 offset "
"60 -> departure at every odd minute"
msgstr ""

#: stoprail.lua
msgid "Track number, for informational purposes"
msgstr ""

#: stoprail.lua
msgid "Train will depart in the direction from where it arrived"
msgstr ""

#: stoprail.lua
msgid ""
"Train will remain stopped with open doors for at least this time before "
"departure is attempted."
msgstr ""

#: stoprail.lua
msgid ""
"Use when multiple stop rails are located in the same track of a station. "
"Allows to select a suitable stop position depending on the class of train."
msgstr ""

#: stoprail.lua
msgid "Wait for signal to clear"
msgstr ""

#: stoprail.lua
msgid "You are not allowed to configure this track."
msgstr "您不能调整这段轨道。"

#: stoprail.lua
#, fuzzy
msgid "closed"
msgstr "(车门已关闭)"

#: stoprail.lua
msgid "left"
msgstr ""

#: stoprail.lua
msgid "right"
msgstr ""

#: time_table.lua
msgid "Timetable"
msgstr ""

#: time_table.lua
msgid "Timetable (on pole)"
msgstr ""

#: time_table.lua
msgid "Timetable (owner: @1)"
msgstr ""

#~ msgid "3-way turnout"
#~ msgstr "三开道岔"

#~ msgid "90+Angle Diamond Crossing Track"
#~ msgstr "交叉轨道 (其中一条轨道与坐标轴平行)"

#~ msgid "<No coupler>"
#~ msgstr "<没有车钩>"

#~ msgid "@1 Platform (45 degree)"
#~ msgstr "较高的@1站台 (45°)"

#~ msgid "@1 Platform (high)"
#~ msgstr "较高的@1站台"

#~ msgid "@1 Platform (low)"
#~ msgstr "较低的@1站台"

#~ msgid "@1 Platform (low, 45 degree)"
#~ msgstr "较低的@1站台 (45°)"

#~ msgid "@1 Slope"
#~ msgstr "@1斜坡"

#~ msgid "ATC Kick command warning: doors are closed."
#~ msgstr "ATC 警告:车门已关闭,无法踢出乘客。"

#~ msgid "ATC Kick command warning: train moving."
#~ msgstr "ATC 警告:火车正在移动,无法踢出乘客。"

#~ msgid "ATC Reverse command warning: didn't reverse train, train moving."
#~ msgstr "ATC 警告:火车正在移动,无法改变行车方向。"

#~ msgid "ATC command parse error: Unknown command: @1"
#~ msgstr "ATC 语法错误:未知命令:@1"

#~ msgid "ATC command syntax error: I statement not closed: @1"
#~ msgstr "ATC 语法错误:“I”命令不完整:@1"

#~ msgid "ATC controller"
#~ msgstr "ATC 控制器"

#~ msgid ""
#~ "ATC controller, mode @1\n"
#~ "Channel: @2"
#~ msgstr ""
#~ "ATC 控制器\n"
#~ "模式:@1\n"
#~ "频道:@2"

#~ msgid ""
#~ "ATC controller, mode @1\n"
#~ "Command: @2"
#~ msgstr ""
#~ "ATC 控制器\n"
#~ "模式:@1\n"
#~ "命令:@2"

#~ msgid "Access to @1"
#~ msgstr "可前往@1"

#~ msgid "Andrew's Cross"
#~ msgstr "铁路道口信号灯"

#~ msgid "Back of train would end up off track, cancelling."
#~ msgstr "火车后部不在轨道上。"

#~ msgid "Big Industrial Train Engine"
#~ msgstr "大型工业用火车头"

#~ msgid "Boiler"
#~ msgstr "锅炉"

#~ msgid "Box Wagon"
#~ msgstr "货运车厢"

#~ msgid "Buffer and Chain Coupler"
#~ msgstr "链式车钩"

#~ msgid "Bumper"
#~ msgstr "保险杠"

#~ msgid "Can not couple: The couplers of the trains do not match (@1 and @2)."
#~ msgstr "您无法连接这两节车厢:这两节车厢使用不同的车钩 (@1和@2)。"

#~ msgid "Can't get on: wagon full or doors closed!"
#~ msgstr "无法上车:车门已关闭或车厢已满。"

#, fuzzy
#~ msgid "Can't place: Not enough slope items left (@1 required)"
#~ msgstr "无法放置斜坡:您没有足够的铁路斜坡放置工具 (您总共需要@1个)"

#, fuzzy
#~ msgid "Can't place: There's no slope of length @1"
#~ msgstr "无法放置斜坡:advtrains 不支持长度为@1米的斜坡。"

#, fuzzy
#~ msgid "Can't place: no supporting node at upper end."
#~ msgstr "无法放置斜坡:较高端没有支撑方块。"

#, fuzzy
#~ msgid "Can't place: not pointing at node"
#~ msgstr "无法放置斜坡:您没有选择任何方块。"

#~ msgid "Can't place: protected position!"
#~ msgstr "无法放置:此区域已被保护。"

#, fuzzy
#~ msgid "Can't place: space occupied!"
#~ msgstr "无法放置斜坡:此区域已被占用。"

#~ msgid "Chimney"
#~ msgstr "烟囱"

#~ msgid "Command"
#~ msgstr "命令"

#~ msgid "Command (on)"
#~ msgstr "命令 (激活时)"

#~ msgid "Default Seat"
#~ msgstr "默认座位"

#~ msgid "Default Seat (driver stand)"
#~ msgstr "默认座位 (司机座位)"

#~ msgid "Dep. Speed"
#~ msgstr "出发速度"

#~ msgid "Deprecated Track"
#~ msgstr "请不要使用"

#~ msgid "Detailed Steam Engine"
#~ msgstr "精细的蒸汽机车"

#~ msgid "Detector Rail"
#~ msgstr "探测轨道"

#~ msgid "Diagonal Diamond Crossing Track"
#~ msgstr "交叉轨道"

#~ msgid "Digiline channel"
#~ msgstr "Digiline 频道"

#~ msgid "Door Delay"
#~ msgstr "车门关闭时间"

#~ msgid ""
#~ "Doors are closed. Use Sneak+rightclick to ignore the closed doors and get "
#~ "off."
#~ msgstr "车门已关闭,请使用潜行+右键单击下车。"

#, fuzzy
#~ msgid "Driver Stand"
#~ msgstr "司机座位"

#~ msgid "Driver Stand (left)"
#~ msgstr "左侧司机座位"

#~ msgid "Driver Stand (right)"
#~ msgstr "右侧司机座位"

#~ msgid "Driver stand"
#~ msgstr "司机座位"

#~ msgid "Driver's cab"
#~ msgstr "驾驶室"

#~ msgid "Get off"
#~ msgstr "下车"

#~ msgid "Get off (forced)"
#~ msgstr "强制下车"

#~ msgid "Industrial Train Engine"
#~ msgstr "工业用火车头"

#~ msgid "Industrial tank wagon"
#~ msgstr "液体运输车厢"

#~ msgid "Industrial wood wagon"
#~ msgstr "木材运输车厢"

#~ msgid "Japanese Train Engine"
#~ msgstr "高速列车车头"

#~ msgid "Japanese Train Inter-Wagon Connection"
#~ msgstr "日本火车车钩"

#~ msgid "Japanese Train Wagon"
#~ msgstr "高速列车车厢"

#, fuzzy
#~ msgid "Japanese signal pole"
#~ msgstr "高速列车车厢"

#~ msgid "Lampless Signal"
#~ msgstr "臂板信号机"

#~ msgid "Lock couples"
#~ msgstr "锁定连接处"

#~ msgid "No such lua entity."
#~ msgstr "您没有指向一个可以用火车复制工具复制的物体。"

#~ msgid "No such wagon: @1."
#~ msgstr "ID 为“@1”的车厢不存在。"

#, fuzzy
#~ msgid "Not allowed to do this."
#~ msgstr "您不能调整这段轨道。"

#~ msgid "Passenger Wagon"
#~ msgstr "客车"

#, fuzzy
#~ msgid "Passenger area"
#~ msgstr "客车"

#~ msgid ""
#~ "Passive Component Naming Tool\n"
#~ "\n"
#~ "Right-click to name a passive component."
#~ msgstr ""
#~ "被动元件命名工具\n"
#~ "\n"
#~ "右键单击命名所选元件。"

#~ msgid "Perpendicular Diamond Crossing Track"
#~ msgstr "垂直交叉轨道"

#~ msgid "Routingcode"
#~ msgstr "路由码"

#~ msgid "Save wagon properties"
#~ msgstr "保存车厢属性"

#~ msgid "Scharfenberg Coupler"
#~ msgstr "Scharfenberg 式车钩"

#~ msgid "Select seat:"
#~ msgstr "请选择座位:"

#~ msgid "Show Inventory"
#~ msgstr "显示物品栏"

#~ msgid "Signal"
#~ msgstr "信号灯"

#~ msgid "Steam Engine"
#~ msgstr "蒸汽机车"

#~ msgid "Subway Passenger Wagon"
#~ msgstr "地铁车厢"

#~ msgid "Target:"
#~ msgstr "目标速度"

#~ msgid "Text displayed inside train"
#~ msgstr "车厢内部显示"

#~ msgid "Text displayed outside on train"
#~ msgstr "车厢外部显示"

#, fuzzy
#~ msgid "That wagon does not exist!"
#~ msgstr "这节车厢没有座位。"

#~ msgid "The clipboard couldn't access the metadata. Copy failed."
#~ msgstr "无法复制:剪贴板无法访问元数据。"

#~ msgid "The clipboard couldn't access the metadata. Paste failed."
#~ msgstr "无法粘贴:剪贴板无法访问元数据。"

#~ msgid "The clipboard is empty."
#~ msgstr "剪贴板是空的。"

#, fuzzy
#~ msgid "The track you are trying to place the wagon on is not long enough!"
#~ msgstr "轨道太短。"

#~ msgid "The track you are trying to place the wagon on is not long enough."
#~ msgstr "轨道太短。"

#, fuzzy
#~ msgid "This Wagon ID"
#~ msgstr "车厢已满。"

#, fuzzy
#~ msgid "This node can't be changed using the trackworker!"
#~ msgstr "您不能使用铁路调整工具调整这个方块。"

#, fuzzy
#~ msgid "This node can't be rotated using the trackworker!"
#~ msgstr "您不能使用铁路调整工具旋转这个方块。"

#, fuzzy
#~ msgid "This node can't be rotated using the trackworker,"
#~ msgstr "您不能使用铁路调整工具旋转这个方块。"

#~ msgid "This position is protected!"
#~ msgstr "这里已被保护。"

#~ msgid "This track can not be changed."
#~ msgstr "您不能调整这段轨道。"

#, fuzzy
#~ msgid "This track can not be removed!"
#~ msgstr "您不能移除这段轨道。"

#, fuzzy
#~ msgid "This track can not be rotated!"
#~ msgstr "您不能旋转这段轨道。"

#~ msgid "This wagon has no seats."
#~ msgstr "这节车厢没有座位。"

#~ msgid "This wagon is full."
#~ msgstr "车厢已满。"

#~ msgid "This wagon is owned by @1, you can't destroy it."
#~ msgstr "这是 @1 的车厢,您不能摧毁它。"

#~ msgid ""
#~ "Track Worker Tool\n"
#~ "\n"
#~ "Left-click: change rail type (straight/curve/switch)\n"
#~ "Right-click: rotate object"
#~ msgstr ""
#~ "铁路调整工具\n"
#~ "\n"
#~ "左键单击:切换轨道类型\n"
#~ "右键单击:旋转方块"

#, fuzzy
#~ msgid "Train "
#~ msgstr "已复制列车。"

#~ msgid "Train copied."
#~ msgstr "已复制列车。"

#~ msgid ""
#~ "Train copy/paste tool\n"
#~ "\n"
#~ "Left-click: copy train\n"
#~ "Right-click: paste train"
#~ msgstr ""
#~ "火车复制工具\n"
#~ "\n"
#~ "左键单击:复制\n"
#~ "右键单击:粘帖"

#~ msgid "Unconfigured ATC controller"
#~ msgstr "ATC 控制器 (未配置)"

#~ msgid "Unconfigured LuaATC component"
#~ msgstr "LuaATC 部件 (未配置)"

#~ msgid "Unloading Track"
#~ msgstr "卸货轨道"

#~ msgid "Use Sneak+rightclick to bypass closed doors!"
#~ msgstr "请使用潜行+右键上车。"

#, fuzzy
#~ msgid "Wagon Properties Tool"
#~ msgstr "车厢属性"

#~ msgid "Wagon properties"
#~ msgstr "车厢属性"

#~ msgid "Wallmounted Signal (left)"
#~ msgstr "壁挂式信号灯 (左侧)"

#~ msgid "Wallmounted Signal (right)"
#~ msgstr "壁挂式信号灯 (右侧)"

#~ msgid "Wallmounted Signal (top)"
#~ msgstr "悬挂式信号灯"

#~ msgid ""
#~ "Warning: If you destroy this wagon, you only get some steel back! If you "
#~ "are sure, hold Sneak and left-click the wagon."
#~ msgstr ""
#~ "警告:如果您摧毁此车厢,您只能拿到一些钢方块。如果您确定要摧毁这节车厢,请"
#~ "按潜行键并左键单击此车厢。"

#~ msgid "Wheel"
#~ msgstr "车轮"

#~ msgid "Y-turnout"
#~ msgstr "对称道岔"

#~ msgid "You are not allowed to build near tracks at this protected position."
#~ msgstr "这里已被保护,您不能在这里的铁路附近建任何东西。"

#~ msgid ""
#~ "You are not allowed to build near tracks without the track_builder "
#~ "privilege."
#~ msgstr "您没有“train_operator”权限,不能在铁路附近建任何东西。"

#~ msgid "You are not allowed to build tracks at this protected position."
#~ msgstr "这里已被保护,您不能在这里建造铁路。"

#~ msgid ""
#~ "You are not allowed to build tracks without the track_builder privilege."
#~ msgstr "您没有“train_operator”权限,不能在这里建造铁路。"

#~ msgid ""
#~ "You are not allowed to configure this LuaATC component without the @1 "
#~ "privilege."
#~ msgstr "您没有“@1”权限,不能配置这个 LuaATC 部件。"

#~ msgid ""
#~ "You are not allowed to configure this track without the @1 privilege."
#~ msgstr "您没有“@1”权限,不能调整这段轨道。"

#~ msgid ""
#~ "You are not allowed to couple trains without the train_operator privilege."
#~ msgstr "您没有“train_operator”权限,不能连接这两节车厢。"

#, fuzzy
#~ msgid "You are not allowed to modify this protected track."
#~ msgstr "这里已被保护,您不能在这里建造铁路。"

#~ msgid ""
#~ "You are not allowed to name LuaATC passive components without the @1 "
#~ "privilege."
#~ msgstr "您没有“@1”权限,不能命名被动元件。"

#~ msgid ""
#~ "You are not allowed to operate turnouts and signals without the "
#~ "railway_operator privilege."
#~ msgstr "您没有“railway_operator”权限,不能控制铁路设施。"

#~ msgid "You do not have the @1 privilege."
#~ msgstr "您没有“@1”权限。"

#, fuzzy
#~ msgid "You don't have the train_operator privilege."
#~ msgstr "您没有“@1”权限。"

#~ msgid ""
#~ "You need to own at least one neighboring wagon to destroy this couple."
#~ msgstr "您必须至少拥有其中一节车厢才能分开这两节车厢。"