1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
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
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
|
--- Signal aspect accessors
-- @module advtrains.interlocking
local A = advtrains.interlocking.aspects
local D = advtrains.distant
local I = advtrains.interlocking
local N = advtrains.ndb
local pts = advtrains.roundfloorpts
local signal_aspect_metatable = {
__tostring = function(asp)
local st = {}
if asp.type2group and asp.type2name then
table.insert(st, string.format("%q in group %q", asp.type2name, asp.type2group))
end
if asp.main then
table.insert(st, string.format("current %d", asp.main))
end
if asp.main ~= 0 then
if asp.dst then
table.insert(st, string.format("next %d", asp.dst))
end
if asp.proceed_as_main then
table.insert(st, "proceed as main")
end
end
return string.format("[%s]", table.concat(st, ", "))
end,
}
local get_aspect
local supposed_aspects = {}
--- Replace the signal aspect cache.
-- @function load_supposed_aspects
-- @param db The new database.
function I.load_supposed_aspects(tbl)
if tbl then
supposed_aspects = tbl
for _, v in pairs(tbl) do
setmetatable(v, signal_aspect_metatable)
end
end
end
--- Retrieve the signal aspect cache.
-- @function save_supposed_aspects
-- @return The current database in use.
function I.save_supposed_aspects()
return supposed_aspects
end
--- Read the aspect of a signal strictly from cache.
-- @param pos The position of the signal.
-- @return[1] The aspect of the signal (if present in cache).
-- @return[2] The nil constant (otherwise).
local function get_supposed_aspect(pos)
return supposed_aspects[pts(pos)]
end
--- Update the signal aspect information in cache.
-- @param pos The position of the signal.
-- @param asp The new signal aspect
local function set_supposed_aspect(pos, asp)
supposed_aspects[pts(pos)] = asp
end
--- Get the definition of a node.
-- @param pos The position of the node.
-- @return[1] The definition of the node (if present).
-- @return[2] An empty table (otherwise).
local function get_ndef(pos)
local node = N.get_node(pos)
return minetest.registered_nodes[node.name] or {}
end
--- Get the aspects supported by a signal.
-- @function signal_get_supported_aspects
-- @param pos The position of the signal.
-- @return[1] The table of supported aspects (if present).
-- @return[2] The nil constant (otherwise).
local function get_supported_aspects(pos)
local ndef = get_ndef(pos)
if ndef.advtrains and ndef.advtrains.supported_aspects then
return ndef.advtrains.supported_aspects
end
return nil
end
--- Adjust a new signal aspect to fit a signal.
-- @param pos The position of the signal.
-- @param asp The new signal aspect.
-- @return The adjusted signal aspect.
-- @return The information to pass to the `advtrains.set_aspect` field in the node definitions.
local function adjust_aspect(pos, asp)
asp = table.copy(I.signal_convert_aspect_if_necessary(asp))
setmetatable(asp, signal_aspect_metatable)
local mainpos = D.get_main(pos)
local nxtasp
if mainpos then
nxtasp = get_aspect(mainpos)
end
if asp.main ~= 0 and mainpos then
asp.dst = nxtasp.main
else
asp.dst = nil
end
local suppasp = get_supported_aspects(pos)
if not suppasp then
return asp, asp
end
local stype = suppasp.type
if stype == 2 then
local group = suppasp.group
local name
if suppasp.dst_shift and nxtasp then
asp.main = nil
name = A.type1_to_type2main(nxtasp, group, suppasp.dst_shift)
elseif asp.main ~= 0 and nxtasp and nxtasp.type2group == group and nxtasp.type2name then
name = A.get_type2_dst(group, nxtasp.type2name)
else
name = A.type1_to_type2main(asp, group)
end
asp.type2group = group
asp.type2name = name
return asp, name
end
asp.type2name = nil
asp.type2group = nil
return asp, asp
end
--- Get the aspect of a signal without accessing the cache.
-- For most cases, `get_aspect` should be used instead.
-- @function signal_get_real_aspect
-- @param pos The position of the signal.
-- @return[1] The signal aspect adjusted using `adjust_aspect` (if present).
-- @return[2] The nil constant (otherwise).
local function get_real_aspect(pos)
local ndef = get_ndef(pos)
if ndef.advtrains and ndef.advtrains.get_aspect then
local asp = ndef.advtrains.get_aspect(pos, node) or I.DANGER
local suppasp = get_supported_aspects(pos)
if suppasp.type == 2 then
asp = A.type2_to_type1(suppasp, asp)
end
return adjust_aspect(pos, asp)
end
return nil
end
--- Get the aspect of a signal.
-- @function signal_get_aspect
-- @param pos The position of the signal.
-- @return[1] The aspect of the signal (if present).
-- @return[2] The nil constant (otherwise).
get_aspect = function(pos)
local asp = get_supposed_aspect(pos)
if not asp then
asp = get_real_aspect(pos)
set_supposed_aspect(pos, asp)
end
return asp
end
--- Set the aspect of a signal.
-- @function signal_set_aspect
-- @param pos The position of the signal.
-- @param asp The new signal aspect.
-- @param[opt=false] skipdst Whether to skip updating distant signals.
local function set_aspect(pos, asp, skipdst)
local node = N.get_node(pos)
local ndef = minetest.registered_nodes[node.name]
if ndef and ndef.advtrains and ndef.advtrains.set_aspect then
local oldasp = I.signal_get_aspect(pos) or DANGER
local newasp, aspval = adjust_aspect(pos, asp)
set_supposed_aspect(pos, newasp)
ndef.advtrains.set_aspect(pos, node, aspval)
I.signal_on_aspect_changed(pos)
local aspect_changed = A.not_equalp(oldasp, newasp)
if (not skipdst) and aspect_changed then
D.update_main(pos)
end
end
end
--- Remove a signal from cache.
-- @function signal_clear_aspect
-- @param pos The position of the signal.
local function clear_aspect(pos)
set_supposed_aspect(pos, nil)
end
--- Readjust the aspect of a signal.
-- @function signal_readjust_aspect
-- @param pos The position of the signal.
local function readjust_aspect(pos)
set_aspect(pos, get_aspect(pos))
end
I.signal_get_supported_aspects = get_supported_aspects
I.signal_get_real_aspect = get_real_aspect
I.signal_get_aspect = get_aspect
I.signal_set_aspect = set_aspect
I.signal_clear_aspect = clear_aspect
I.signal_readjust_aspect = readjust_aspect
|