From 6fd845baec0f5aa8b7cdee1adf8d05061a643242 Mon Sep 17 00:00:00 2001 From: orwell Date: Thu, 23 May 2024 00:58:24 +0200 Subject: Connect the ropes, start on making the UI work --- advtrains_interlocking/signal_api.lua | 220 +++++++--------------------------- 1 file changed, 43 insertions(+), 177 deletions(-) (limited to 'advtrains_interlocking/signal_api.lua') diff --git a/advtrains_interlocking/signal_api.lua b/advtrains_interlocking/signal_api.lua index d27a045..5216594 100644 --- a/advtrains_interlocking/signal_api.lua +++ b/advtrains_interlocking/signal_api.lua @@ -5,9 +5,15 @@ local F = advtrains.formspec local signal = {} signal.MASP_HALT = { - name = "halt", - description = "HALT", - halt = true, + name = nil, + speed = nil, + remote = nil, +} + +signal.MASP_FREE = { + name = "_free", + speed = -1, + remote = nil, } signal.ASPI_HALT = { @@ -50,11 +56,12 @@ Note that once apply_aspect returns, there is no need for advtrains anymore to q When the signal, for any reason, wants to change its aspect by itself *without* going through the signal API then it should update the aspect info cache by calling advtrains.interlocking.signal.update_aspect_info(pos) -Note that the apply_aspect function MUST accept the following main aspect, even if it is not defined in the main_aspects table: -{ name = "halt", halt = true } -It should cause the signal to show its most restrictive aspect. Typically it is a halt aspect, but e.g. for distant-only +Apply_aspect may also receive nil as the main aspect. It usually means that the signal is not assigned to anything particular, +and it should cause the signal to show its most restrictive aspect. Typically it is a halt aspect, but e.g. for distant-only signals this would be "expect stop". +Main aspect names starting with underscore (e.g. "_default") are reserved and must not be used! + == Aspect Info == The actual signal aspect in the already-known format. This is what the trains use to determine halt/proceed and speed. asp = { @@ -152,7 +159,7 @@ end -- - Storing the main aspect and dst pos for this signal permanently (until next change) -- - Assigning the distant signal for this signal -- - Calling apply_aspect() in the signal's node definition to make the signal show the aspect --- - Calling apply_aspect() again whenever the distant signal changes its aspect +-- - Calling apply_aspect() again whenever the remote signal changes its aspect -- - Notifying this signal's distant signals about changes to this signal (unless skip_dst_notify is specified) function signal.set_aspect(pos, main_asp_name, main_asp_speed, rem_pos, skip_dst_notify) local main_pts = advtrains.encode_pos(pos) @@ -252,10 +259,7 @@ function signal.get_aspect(pos) end local function cache_mainaspects(ndefat) - ndefat.main_aspects_lookup = { - -- always define halt aspect - halt = signal.MASP_HALT - } + ndefat.main_aspects_lookup = {} for _,ma in ipairs(ndefat.main_aspects) do ndefat.main_aspects_lookup[ma.name] = ma end @@ -264,7 +268,7 @@ end function signal.get_aspect_internal(pos, aspt) if not aspt then -- oh, no main aspect, nevermind - return nil, aspt.remote, nil + return nil, nil, nil end atdebug("get_aspect_internal",pos,aspt) -- look aspect in nodedef @@ -277,6 +281,10 @@ function signal.get_aspect_internal(pos, aspt) cache_mainaspects(ndefat) end local masp = ndefat.main_aspects_lookup[aspt.name] + -- special handling for the default free aspect ("_free") + if aspt.name == "_free" then + masp = ndefat.main_aspects[1] + end if not masp then atwarn(pos,"invalid main aspect",aspt.name,"valid are",ndefat.main_aspects_lookup) return nil, aspt.remote, node, ndef @@ -355,10 +363,30 @@ end function signal.update_route_aspect(tcbs, skip_dst_notify) if tcbs.signal then local asp = tcbs.aspect or signal.MASP_HALT - signal.set_aspect(tcbs.signal, asp, skip_dst_notify) + signal.set_aspect(tcbs.signal, asp.name, asp.speed, asp.remote, skip_dst_notify) end end +-- Returns how capable the signal is with regards to aspect setting +-- 0: not a signal at all +-- 1: signal has get_aspect_info() but the aspect is not variable (e.g. a sign) +-- 2: signal has apply_aspect() but does not have main aspects (e.g. a pure distant signal) +-- 3: Full capabilities, signal has main aspects and can be used as main/shunt signal (can be start/endpoint of a route) +function signal.get_signal_cap_level(pos) + local node = advtrains.ndb.get_node_or_nil(pos) + local ndef = node and minetest.registered_nodes[node.name] + local ndefat = ndef and ndef.advtrains + if ndefat and ndefat.get_aspect_info then + if ndefat.apply_aspect then + if ndefat.main_aspects then + return 3 + end + return 2 + end + return 1 + end + return 0 +end ---------------- @@ -366,7 +394,7 @@ function signal.can_dig(pos) return not advtrains.interlocking.db.get_sigd_for_signal(pos) end -function advtrains.interlocking.signal_after_dig(pos) +function signal.after_dig(pos) -- TODO clear influence point advtrains.interlocking.signal.clear_aspect(pos) end @@ -374,169 +402,7 @@ end function signal.on_rightclick(pos, node, player, itemstack, pointed_thing) local pname = player:get_player_name() local control = player:get_player_control() - if control.aux1 then - advtrains.interlocking.show_ip_form(pos, pname) - return - end - advtrains.interlocking.show_signal_form(pos, node, pname) -end - -function advtrains.interlocking.show_signal_form(pos, node, pname) - local sigd = advtrains.interlocking.db.get_sigd_for_signal(pos) - if sigd then - advtrains.interlocking.show_signalling_form(sigd, pname) - else - local ndef = minetest.registered_nodes[node.name] - if ndef.advtrains and ndef.advtrains.set_aspect then - -- permit to set aspect manually - local function callback(pname, aspect) - signal.set_aspect(pos, aspect) - end - local isasp = advtrains.interlocking.signal_get_aspect(pos, node) - - advtrains.interlocking.show_signal_aspect_selector( - pname, - ndef.advtrains.supported_aspects, - pos, callback, - isasp) - else - --static signal - only IP - advtrains.interlocking.show_ip_form(pos, pname) - end - end -end - -local players_assign_ip = {} - -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 - -function advtrains.interlocking.make_ip_formspec_component(pos, x, y, w) - advtrains.interlocking.db.check_for_duplicate_ip(pos) - local pts, connid = advtrains.interlocking.db.get_ip_by_signalpos(pos) - if pts then - return table.concat { - F.S_label(x, y, "Influence point is set at @1.", string.format("%s/%s", pts, connid)), - F.S_button_exit(x, y+0.5, w/2-0.125, "ip_set", "Modify"), - F.S_button_exit(x+w/2+0.125, y+0.5, w/2-0.125, "ip_clear", "Clear"), - }, pts, connid - else - return table.concat { - F.S_label(x, y, "Influence point is not set."), - F.S_button_exit(x, y+0.5, w, "ip_set", "Set influence point"), - } - end -end - --- shows small info form for signal properties --- This function is named show_ip_form because it was originally only intended --- for assigning/changing the influence point. --- 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) - if not minetest.check_player_privs(pname, "interlocking") then - return - end - local ipform, pts, connid = advtrains.interlocking.make_ip_formspec_component(pos, 0.5, 0.5, 7) - local form = { - "formspec_version[4]", - "size[8,2.25]", - ipform, - } - if pts then - local ipos = minetest.string_to_pos(pts) - ipmarker(ipos, connid) - end - if advtrains.distant.appropriate_signal(pos) then - form[#form+1] = advtrains.interlocking.make_dst_formspec_component(pos, 0.5, 2, 7, 4.25) - form[2] = "size[8,6.75]" - end - form = table.concat(form) - if not only_notset or not pts then - minetest.show_formspec(pname, "at_il_propassign_"..minetest.pos_to_string(pos), form) - end + advtrains.interlocking.show_signal_form(pos, node, pname, control.aux1) end -function advtrains.interlocking.handle_ip_formspec_fields(pname, pos, fields) - if not (pos and minetest.check_player_privs(pname, {train_operator=true, interlocking=true})) then - return - end - if fields.ip_set then - advtrains.interlocking.signal_init_ip_assign(pos, pname) - elseif fields.ip_clear then - advtrains.interlocking.db.clear_ip_by_signalpos(pos) - end -end - -minetest.register_on_player_receive_fields(function(player, formname, fields) - local pname = player:get_player_name() - local pts = string.match(formname, "^at_il_propassign_([^_]+)$") - local pos - if pts then - pos = minetest.string_to_pos(pts) - end - if pos then - advtrains.interlocking.handle_ip_formspec_fields(pname, pos, fields) - advtrains.interlocking.handle_dst_formspec_fields(pname, pos, fields) - end -end) - --- inits the signal IP assignment process -function 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) - - advtrains.interlocking.signal = signal -- cgit v1.2.3