summaryrefslogtreecommitdiff
path: root/src/quicktune.h
Commit message (Expand)AuthorAge
* Update Copyright YearsSfan52013-02-24
* Change Minetest-c55 to MinetestPilzAdam2013-02-24
* Optimize headersPerttu Ahola2012-06-17
* Switch the license to be LGPLv2/later, with small parts still remaining as GP...Perttu Ahola2012-06-05
* Make finish quicktune and leave it unused (as intended)Perttu Ahola2012-03-12
* Add quicktune.{cpp,h}Perttu Ahola2012-03-12
a> 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157
-- ars.lua
-- automatic routesetting

--[[
	The "ARS table" and its effects:
	Every route has (or can have) an associated ARS table. This can either be
	ars = { [n] = {ln="<line>"}/{rc="<routingcode>"}/{c="<a comment>"} }
	a list of rules involving either line or routingcode matchers (or comments, those are ignored)
	The first matching rule determines the route to set.
	- or -
	ars = {default = true}
	this means that all trains that no other rule matches on should use this route
	
	Compound ("and") conjunctions are not supported (--TODO should they?)
	
	For editing, those tables are transformed into lines in a text area:
	{ln=...} -> LN ...
	{rc=...} -> RC ...
	{c=...}  -> #...
	{default=true} -> *
	See also route_ui.lua
]]

local il = advtrains.interlocking

-- The ARS data are saved in a table format, but are entered in text format. Utility functions to transform between both.
function il.ars_to_text(arstab)
	if not arstab then
		return ""
	end
	
	local txt = {}

	for i, arsent in ipairs(arstab) do
		local n = ""
		if arsent.n then
			n = "!"
		end
		if arsent.ln then
			txt[#txt+1] = n.."LN "..arsent.ln
		elseif arsent.rc then
			txt[#txt+1] = n.."RC "..arsent.rc
		elseif arsent.c then
			txt[#txt+1] = "#"..arsent.c
		end
	end
	
	if arstab.default then
		return "*\n" .. table.concat(txt, "\n")
	end
	return table.concat(txt, "\n")
end

function il.text_to_ars(t)
	if t=="" then
		return nil
	elseif t=="*" then
		return {default=true}
	end
	local arstab = {}
	for line in string.gmatch(t, "[^\r\n]+") do
		if line=="*" then
			arstab.default = true
		else
			local c, v = string.match(line, "^(...?)%s(.*)$")
			if c and v then
				local n = nil
				if string.sub(c,1,1) == "!" then
					n = true
					c = string.sub(c,2)
				end
				local tt=string.upper(c)
				if tt=="LN" then
					arstab[#arstab+1] = {ln=v, n=n}
				elseif tt=="RC" then
					arstab[#arstab+1] = {rc=v, n=n}
				end
			else
				local ct = string.match(line, "^#(.*)$")
				if ct then arstab[#arstab+1] = {c = ct} end
			end
		end
	end
	return arstab
end

local function find_rtematch(routes, train)
	local default
	for rteid, route in ipairs(routes) do
		if route.ars then
			if route.ars.default then
				default = rteid
			else
				if il.ars_check_rule_match(route.ars, train) then
					return rteid
				end
			end
		end
	end
	return default
end

-- Checks whether ARS rule explicitly matches. This does not take into account the "default" field, since a wider context is required for this.
-- Returns the rule number that matched, or nil if nothing matched
function il.ars_check_rule_match(ars, train)
		if not ars then
			return nil
		end
		local line = train.line
		local routingcode = train.routingcode
		for arskey, arsent in ipairs(ars) do
			--atdebug(arsent, line, routingcode)
			if arsent.n then
				-- rule is inverse...
				if arsent.ln and (not line or arsent.ln ~= line) then
					return arskey
				elseif arsent.rc and (not routingcode or not string.find(" "..routingcode.." ", " "..arsent.rc.." ", nil, true)) then
					return arskey
				end
				return nil
			end
				
			if arsent.ln and line and arsent.ln == line then
				return arskey
			elseif arsent.rc and routingcode and string.find(" "..routingcode.." ", " "..arsent.rc.." ", nil, true) then
				return arskey
			end
		end
		return nil
end

function advtrains.interlocking.ars_check(sigd, train)
	local tcbs = il.db.get_tcbs(sigd)
	if not tcbs or not tcbs.routes then return end
	
	if tcbs.ars_disabled or tcbs.ars_ignore_next then
		-- No-ARS mode of signal.
		-- ignore...
		-- Note: ars_ignore_next is set by signalling formspec when route is cancelled
		tcbs.ars_ignore_next = nil
		return
	end
	
	if tcbs.routeset then
		-- ARS is not in effect when a route is already set
		-- just "punch" routesetting, just in case callback got lost.
		minetest.after(0, il.route.update_route, sigd, tcbs, nil, nil)
		return
	end
	
	local rteid = find_rtematch(tcbs.routes, train)
	if rteid then
		--delay routesetting, it should not occur inside train step
		-- using after here is OK because that gets called on every path recalculation
		minetest.after(0, il.route.update_route, sigd, tcbs, rteid, nil)
	end
end