diff options
| author | orwell96 <orwell@bleipb.de> | 2018-10-09 11:36:34 +0200 | 
|---|---|---|
| committer | orwell96 <orwell@bleipb.de> | 2018-10-09 12:07:23 +0200 | 
| commit | 8f8f009425a4d3341d3d00e6a537b5af320b5639 (patch) | |
| tree | cc2abc9df9581003abf602acbf1d9cbeb10837ea | |
| parent | 8df7bcf6b6ea6333b3df925af528e23574974d69 (diff) | |
Make signal influence point (~halt point) specifiable
Also extend signal api necessarily
| -rw-r--r-- | advtrains/signals.lua | 26 | ||||
| -rw-r--r-- | advtrains_interlocking/database.lua | 66 | ||||
| -rw-r--r-- | advtrains_interlocking/route_prog.lua | 1 | ||||
| -rw-r--r-- | advtrains_interlocking/signal_api.lua | 197 | ||||
| -rw-r--r-- | advtrains_interlocking/tcb_ts_ui.lua | 25 | ||||
| -rw-r--r-- | advtrains_interlocking/textures/at_il_signal_ip.png | bin | 0 -> 285 bytes | 
6 files changed, 300 insertions, 15 deletions
| diff --git a/advtrains/signals.lua b/advtrains/signals.lua index 75f9213..669e825 100644 --- a/advtrains/signals.lua +++ b/advtrains/signals.lua @@ -10,6 +10,23 @@ local function can_dig_func(pos)  	return true  end +local function aspect(b) +return { +	main = { +		free = b, +		speed = -1, +	}, +	shunt = { +		free = false, +	}, +	dst = { +		free = true, +		speed = -1, +	}, +	info = {} +} +end +  for r,f in pairs({on={as="off", ls="green", als="red"}, off={as="on", ls="red", als="green"}}) do  	advtrains.trackplacer.register_tracktype("advtrains:retrosignal", "") @@ -64,6 +81,9 @@ for r,f in pairs({on={as="off", ls="green", als="red"}, off={as="on", ls="red",  					else  						advtrains.ndb.swap_node(pos, {name = "advtrains:retrosignal_off"..rotation, param2 = node.param2}, true)  					end +				end, +				get_aspect = function(pos, node) +					return aspect(r=="on")  				end  			},  			can_dig = can_dig_func, @@ -117,6 +137,9 @@ for r,f in pairs({on={as="off", ls="green", als="red"}, off={as="on", ls="red",  						advtrains.ndb.swap_node(pos, {name = "advtrains:signal_off"..rotation, param2 = node.param2}, true)  					end  				end, +				get_aspect = function(pos, node) +					return aspect(r=="on") +				end,  				getstate = f.ls,  				setstate = function(pos, node, newstate)  					if newstate == f.als then @@ -180,6 +203,9 @@ for r,f in pairs({on={as="off", ls="green", als="red"}, off={as="on", ls="red",  						advtrains.ndb.swap_node(pos, {name = "advtrains:signal_wall_"..loc.."_off", param2 = node.param2}, true)  					end  				end, +				get_aspect = function(pos, node) +					return aspect(r=="on") +				end,  				getstate = f.ls,  				setstate = function(pos, node, newstate)  					if newstate == f.als then diff --git a/advtrains_interlocking/database.lua b/advtrains_interlocking/database.lua index f3df5e1..030a5e0 100644 --- a/advtrains_interlocking/database.lua +++ b/advtrains_interlocking/database.lua @@ -1,6 +1,9 @@  -- interlocking/database.lua  -- saving the location of TCB's, their neighbors and their state  --[[ + +== THIS COMMENT IS PARTIALLY INCORRECT AND OUTDATED! == +  The interlocking system is based on track circuits.  Track circuit breaks must be manually set by the user. Signals must be assigned to track circuit breaks and to a direction(connid).  To simplify the whole system, there is no overlap. @@ -96,8 +99,12 @@ local ildb = {}  local track_circuit_breaks = {}  local track_sections = {} +-- Assignment of signals to TCBs  local signal_assignments = {} +-- track+direction -> signal position +local influence_points = {} +  function ildb.load(data)  	if not data then return end  	if data.tcbs then @@ -112,6 +119,9 @@ function ildb.load(data)  	if data.rs_locks then  		advtrains.interlocking.route.rte_locks = data.rs_locks  	end +	if data.influence_points then +		influence_points = data.influence_points +	end  end  function ildb.save() @@ -120,6 +130,7 @@ function ildb.save()  		ts=track_sections,  		signalass = signal_assignments,  		rs_locks = advtrains.interlocking.route.rte_locks, +		influence_points = influence_points,  	}  end @@ -455,6 +466,61 @@ function ildb.set_sigd_for_signal(pos, sigd)  end +-- checks if a signal is influencing here +function ildb.get_ip_signal(pts, connid) +	if influence_points[pts] then +		return influence_points[pts][connid] +	end +end + +-- Tries to get aspect to obey here, if there +-- is a signal ip at this location +-- auto-clears invalid assignments +function ildb.get_ip_signal_asp(pts, connid) +	local p = ildb.get_ip_signal(pts, connid) +	if p then +		local asp = advtrains.interlocking.signal_get_aspect(p) +		if not asp then +			atlog("Clearing orphaned signal influence point", pts, "/", connid) +			ildb.clear_ip_signal(pts, connid) +			return nil +		end +		return asp +	end +	return nil +end + +-- set signal assignment. +function ildb.set_ip_signal(pts, connid, spos) +	if not influence_points[pts] then +		influence_points[pts] = {} +	end +	influence_points[pts][connid] = spos +end +-- clear signal assignment. +function ildb.clear_ip_signal(pts, connid) +	influence_points[pts][connid] = nil +	for _,_ in pairs(influence_points[pts]) do +		return +	end +	influence_points[pts] = nil +end + +function ildb.get_ip_by_signalpos(spos) +	for pts,tab in pairs(influence_points) do +		for connid,pos in pairs(tab) do +			if vector.equals(pos, spos) then +				return pts, connid +			end +		end +	end +end +-- clear signal assignment given the signal position +function ildb.clear_ip_by_signalpos(spos) +	local pts, connid = ildb.get_ip_by_signalpos(spos) +	if pts then ildb.clear_ip_signal(pts, connid) end +end +  advtrains.interlocking.db = ildb diff --git a/advtrains_interlocking/route_prog.lua b/advtrains_interlocking/route_prog.lua index 7abca66..2ec9375 100644 --- a/advtrains_interlocking/route_prog.lua +++ b/advtrains_interlocking/route_prog.lua @@ -227,7 +227,6 @@ function advtrains.interlocking.init_route_prog(pname, sigd)  	}  	advtrains.interlocking.visualize_route(sigd, player_rte_prog[pname].route, "prog_"..pname, player_rte_prog[pname].tmp_lcks, pname)  	minetest.chat_send_player(pname, "Route programming mode active. Punch TCBs to add route segments, punch turnouts to lock them.") -	minetest.chat_send_player(pname, "Type /at_rp_set <name> when you are done, /at_rp_discard to cancel route programming")  end  local function get_last_route_item(origin, route) 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) diff --git a/advtrains_interlocking/tcb_ts_ui.lua b/advtrains_interlocking/tcb_ts_ui.lua index 5ef7ca9..ea0e736 100644 --- a/advtrains_interlocking/tcb_ts_ui.lua +++ b/advtrains_interlocking/tcb_ts_ui.lua @@ -49,8 +49,11 @@ minetest.register_node("advtrains_interlocking:tcb_node", {  			local tcbpos = minetest.string_to_pos(tcbpts)  			advtrains.interlocking.show_tcb_form(tcbpos, pname)  		else +			if not minetest.check_player_privs(pname, "interlocking") then +				minetest.chat_send_player(pname, "Insufficient privileges to use this!") +				return +			end  			--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 @@ -143,6 +146,7 @@ minetest.register_on_punchnode(function(pos, node, player, pointed_thing)  					tcbs.routes = {}  					ildb.set_sigd_for_signal(pos, sigd)  					minetest.chat_send_player(pname, "Configuring TCB: Successfully assigned signal.") +					advtrains.interlocking.show_ip_form(pos, pname, true)  				else  					minetest.chat_send_player(pname, "Configuring TCB: Internal error, TCBS doesn't exist. Aborted.")  				end @@ -173,11 +177,11 @@ local function mktcbformspec(tcbs, btnpref, offset, pname)  		tcbs.ts_id = nil  		form = form.."label[0.5,"..offset..";Side "..btnpref..": ".."End of interlocking]"  		form = form.."button[0.5,"..(offset+0.5)..";5,1;"..btnpref.."_makeil;Create Interlocked Track Section]" -		if tcbs.section_free then -			form = form.."button[0.5,"..(offset+1.5)..";5,1;"..btnpref.."_setlocked;Section is free]" -		else -			form = form.."button[0.5,"..(offset+1.5)..";5,1;"..btnpref.."_setfree;Section is blocked]"		 -		end +		--if tcbs.section_free then +			--form = form.."button[0.5,"..(offset+1.5)..";5,1;"..btnpref.."_setlocked;Section is free]" +		--else +			--form = form.."button[0.5,"..(offset+1.5)..";5,1;"..btnpref.."_setfree;Section is blocked]"		 +		--end  	end  	if tcbs.signal then  		form = form.."button[0.5,"..(offset+2.5)..";5,1;"..btnpref.."_sigdia;Signalling]"	 @@ -483,7 +487,7 @@ function advtrains.interlocking.show_signalling_form(sigd, pname, sel_rte)  	if not tcbs.signal_name then tcbs.signal_name = "Signal at "..minetest.pos_to_string(sigd.p) end  	if not tcbs.routes then tcbs.routes = {} end -	local form = "size[7,9]label[0.5,0.5;Signal at "..minetest.pos_to_string(sigd.p).."]" +	local form = "size[7,10]label[0.5,0.5;Signal at "..minetest.pos_to_string(sigd.p).."]"  	form = form.."field[0.8,1.5;5.2,1;name;Signal name;"..tcbs.signal_name.."]"  	form = form.."button[5.5,1.2;1,1;setname;Set]" @@ -524,12 +528,13 @@ function advtrains.interlocking.show_signalling_form(sigd, pname, sel_rte)  			form = form.."button[0.5,7;2,1;dsproute;Show]"  			if hasprivs then  				form = form.."button[2.5,7;1,1;delroute;Delete]" -				form = form.."button[3.5,7;2,1;renroute;Rename]" +				form = form.."button[3.5,7;2,1;editroute;Edit]"  			end  		end  		if hasprivs then  			form = form.."button[0.5,8;2.5,1;newroute;New Route]"  			form = form.."button[  3,8;2.5,1;unassign;Unassign Signal]" +			form = form.."button[  3,9;2.5,1;influp;Influence Point]"  		end  	end	  	sig_pselidx[pname] = sel_rte @@ -617,6 +622,10 @@ minetest.register_on_player_receive_fields(function(player, formname, fields)  				minetest.chat_send_player(pname, "Please cancel route first!")  			end  		end +		if fields.influp and hasprivs then +			advtrains.interlocking.show_ip_form(tcbs.signal, pname) +			return +		end  		if fields.auto then  			tcbs.route_auto = true diff --git a/advtrains_interlocking/textures/at_il_signal_ip.png b/advtrains_interlocking/textures/at_il_signal_ip.pngBinary files differ new file mode 100644 index 0000000..bf1618a --- /dev/null +++ b/advtrains_interlocking/textures/at_il_signal_ip.png | 
