diff options
Diffstat (limited to 'advtrains_interlocking/signal_api.lua')
-rw-r--r-- | advtrains_interlocking/signal_api.lua | 197 |
1 files changed, 191 insertions, 6 deletions
diff --git a/advtrains_interlocking/signal_api.lua b/advtrains_interlocking/signal_api.lua index cd1ab54..90fe1fc 100644 --- a/advtrains_interlocking/signal_api.lua +++ b/advtrains_interlocking/signal_api.lua @@ -1,7 +1,8 @@ -- Signal API implementation ---[[ Signal aspect table: +--[[ +Signal aspect table: asp = { main = { free = <boolean>, @@ -9,6 +10,14 @@ asp = { }, shunt = { free = <boolean>, + -- Whether train may proceed as shunt move, on sight + -- main aspect takes precedence over this + proceed_as_main = <boolean>, + -- If an approaching train is a shunt move and "main.free" is set, + -- the train may proceed as a train move under the "main" aspect + -- If this is not set, shunt moves are NOT allowed to switch to + -- a train move, and must stop even if "main.free" is set. + -- This is intended to be used for "Halt for shunt moves" signs. } dst = { free = <boolean>, @@ -17,21 +26,69 @@ asp = { info = { call_on = <boolean>, -- Call-on route, expect train in track ahead dead_end = <boolean>, -- Route ends on a dead end (e.g. bumper) + w_speed = <integer>, + -- "Warning speed restriction". Supposed for short-term speed + -- restrictions which always override any other restrictions + -- imposed by "speed" fields, until lifted by a value of -1 } } -Signals API: +-- For "speed" and "w_speed" fields, a value of -1 means that the +-- restriction is lifted. If they are omitted, the value imposed at +-- the last aspect received remains valid. +-- The "dst" subtable can be completely omitted when no explicit dst +-- aspect should be signalled to the train. In this case, the last +-- signalled dst aspect remains valid. + +== How signals actually work in here == +Each signal (in the advtrains universe) is some node that has at least the +following things: +- An "influence point" that is set somewhere on a rail +- An aspect which trains that pass the "influence point" have to obey + +There can be static and dynamic signals. Static signals are, roughly +spoken, signs, while dynamic signals are "real" signals which can display +different things. + +The node definition of a signal node should contain those fields: groups = { - advtrains_signal = 2, + advtrains_signal = 2, save_in_at_nodedb = 1, } advtrains = { function set_aspect(pos, node, asp) - ... + -- This function gets called whenever the signal should display + -- a new or changed signal aspect. It is not required that + -- the signal actually displays the exact same aspect, since + -- some signals can not do this by design. + -- Example: pure shunt signals can not display a "main" aspect + -- and have no effect on train moves, so they will only ever + -- honor the shunt.free field for their aspect. + + -- The aspect passed in here can always be queried using the + -- advtrains.interlocking.signal_get_supposed_aspect(pos) function. + + -- For static signals, this function should be completely omitted + -- If this function is ommitted, it won't be possible to use + -- route setting on this signal. + end + function get_aspect(pos, node) + -- This function gets called by the train safety system. It + should return the aspect that this signal actually displays, + not preferably the input of set_aspect. + -- For regular, full-featured light signals, they will probably + honor all entries in the original aspect, however, e.g. + simple shunt signals always return main.free=true regardless of + the set_aspect input because they can not signal "Halt" to + train moves. + -- advtrains.interlocking.DANGER contains a default "all-danger" aspect. end } on_rightclick = advtrains.interlocking.signal_rc_handler can_dig = advtrains.interlocking.signal_can_dig +after_dig_node = advtrains.interlocking.signal_after_dig +(If you need to specify custom can_dig or after_dig_node callbacks, +please call those functions anyway!) ]]-- local DANGER = { @@ -48,6 +105,7 @@ local DANGER = { }, info = {} } +advtrains.interlocking.DANGER = DANGER function advtrains.interlocking.update_signal_aspect(tcbs) if tcbs.signal then @@ -60,6 +118,11 @@ function advtrains.interlocking.signal_can_dig(pos) return not advtrains.interlocking.db.get_sigd_for_signal(pos) end +function advtrains.interlocking.signal_after_dig(pos) + -- clear influence point + advtrains.interlocking.db.clear_ip_by_signalpos(pos) +end + function advtrains.interlocking.signal_set_aspect(pos, asp) local node=advtrains.ndb.get_node(pos) local ndef=minetest.registered_nodes[node.name] @@ -75,7 +138,7 @@ function advtrains.interlocking.signal_rc_handler(pos, node, player, itemstack, advtrains.interlocking.show_signalling_form(sigd, pname) else -- permit to set aspect manually - minetest.show_formspec(pname, "at_il_sigasp_"..minetest.pos_to_string(pos), "field[aspect;Set Aspect (F/D)Speed(F/D)Speed(F/D);D0D0D]") + minetest.show_formspec(pname, "at_il_sigasp_"..minetest.pos_to_string(pos), "field[aspect;Set Aspect (F/D)Speed(F/D)Speed(F/D) ['A' to assign IP];D0D0D]") end end @@ -85,6 +148,10 @@ minetest.register_on_player_receive_fields(function(player, formname, fields) local pos if pts then pos = minetest.string_to_pos(pts) end if pos and fields.aspect then + if fields.aspect == "A" then + advtrains.interlocking.show_ip_form(pos, pname) + return + end local mfs, msps, dfs, dsps, shs = string.match(fields.aspect, "^([FD])([0-9]+)([FD])([0-9]+)([FD])$") local asp = { main = { @@ -108,7 +175,7 @@ minetest.register_on_player_receive_fields(function(player, formname, fields) end) -- Returns the aspect the signal at pos is supposed to show -function advtrains.interlocking.signal_get_aspect(pos) +function advtrains.interlocking.signal_get_supposed_aspect(pos) local sigd = advtrains.interlocking.db.get_sigd_for_signal(pos) if sigd then local tcbs = advtrains.interlocking.db.get_tcbs(sigd) @@ -118,3 +185,121 @@ function advtrains.interlocking.signal_get_aspect(pos) end return DANGER; end + +-- Returns the actual aspect of the signal at position, as returned by the nodedef. +-- returns nil +function advtrains.interlocking.signal_get_aspect(pos) + local node=advtrains.ndb.get_node(pos) + local ndef=minetest.registered_nodes[node.name] + if ndef and ndef.advtrains and ndef.advtrains.get_aspect then + return ndef.advtrains.get_aspect(pos, node) + end +end + +local players_assign_ip = {} + +-- shows small info form for signal IP state/assignment +-- only_notset: show only if it is not set yet (used by signal tcb assignment) +function advtrains.interlocking.show_ip_form(pos, pname, only_notset) + local form = "size[7,5]label[0.5,0.5;Signal at "..minetest.pos_to_string(pos).."]" + local pts, connid = advtrains.interlocking.db.get_ip_by_signalpos(pos) + if pts then + form = form.."label[0.5,1.5;Influence point is set at "..pts.."/"..connid.."]" + form = form.."button_exit[0.5,2.5; 5,1;show;Show]" + form = form.."button_exit[0.5,3.5; 5,1;clear;Clear]" + else + form = form.."label[0.5,1.5;Influence point is not set.]" + form = form.."label[0.5,2.0;It is recommended to set an influence point.]" + form = form.."label[0.5,2.5;This is the point where trains will obey the signal.]" + + form = form.."button_exit[0.5,3.5; 5,1;set;Set]" + end + if not only_notset or not pts then + minetest.show_formspec(pname, "at_il_ipassign_"..minetest.pos_to_string(pos), form) + end +end + +local function ipmarker(ipos, connid) + local node_ok, conns, rhe = advtrains.get_rail_info_at(ipos, advtrains.all_tracktypes) + if not node_ok then return end + local yaw = advtrains.dir_to_angle(conns[connid].c) + + -- using tcbmarker here + local obj = minetest.add_entity(vector.add(ipos, {x=0, y=0.2, z=0}), "advtrains_interlocking:tcbmarker") + if not obj then return end + obj:set_yaw(yaw) + obj:set_properties({ + textures = { "at_il_signal_ip.png" }, + }) +end + +minetest.register_on_player_receive_fields(function(player, formname, fields) + local pname = player:get_player_name() + if not minetest.check_player_privs(pname, {train_operator=true, interlocking=true}) then + return + end + local pts = string.match(formname, "^at_il_ipassign_([^_]+)$") + local pos + if pts then + pos = minetest.string_to_pos(pts) + end + if pos then + if fields.set then + advtrains.interlocking.signal_init_ip_assign(pos, pname) + elseif fields.clear then + advtrains.interlocking.db.clear_ip_by_signalpos(pos) + elseif fields.show then + local ipts, connid = advtrains.interlocking.db.get_ip_by_signalpos(pos) + if not ipts then return end + local ipos = minetest.string_to_pos(ipts) + ipmarker(ipos, connid) + end + end +end) + +-- inits the signal IP assignment process +function advtrains.interlocking.signal_init_ip_assign(pos, pname) + if not minetest.check_player_privs(pname, "interlocking") then + minetest.chat_send_player(pname, "Insufficient privileges to use this!") + return + end + --remove old IP + advtrains.interlocking.db.clear_ip_by_signalpos(pos) + minetest.chat_send_player(pname, "Configuring Signal: Please look in train's driving direction and punch rail to set influence point.") + + players_assign_ip[pname] = pos +end + +minetest.register_on_punchnode(function(pos, node, player, pointed_thing) + local pname = player:get_player_name() + if not minetest.check_player_privs(pname, "interlocking") then + return + end + -- IP assignment + local signalpos = players_assign_ip[pname] + if signalpos then + if vector.distance(pos, signalpos)<=50 then + local node_ok, conns, rhe = advtrains.get_rail_info_at(pos, advtrains.all_tracktypes) + if node_ok and #conns == 2 then + + local yaw = player:get_look_horizontal() + local plconnid = advtrains.yawToClosestConn(yaw, conns) + + -- add assignment if not already present. + local pts = advtrains.roundfloorpts(pos) + if not advtrains.interlocking.db.get_ip_signal_asp(pts, plconnid) then + advtrains.interlocking.db.set_ip_signal(pts, plconnid, signalpos) + ipmarker(pos, plconnid) + minetest.chat_send_player(pname, "Configuring Signal: Successfully set influence point") + else + minetest.chat_send_player(pname, "Configuring Signal: Influence point of another signal is already present!") + end + else + minetest.chat_send_player(pname, "Configuring Signal: This is not a normal two-connection rail! Aborted.") + end + else + minetest.chat_send_player(pname, "Configuring Signal: Node is too far away. Aborted.") + end + players_assign_ip[pname] = nil + end +end) |