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
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
|
--advtrains by orwell96, see readme.txt
advtrains.dir_trans_tbl={
[0]={x=0, z=1},
[1]={x=1, z=2},
[2]={x=1, z=1},
[3]={x=2, z=1},
[4]={x=1, z=0},
[5]={x=2, z=-1},
[6]={x=1, z=-1},
[7]={x=1, z=-2},
[8]={x=0, z=-1},
[9]={x=-1, z=-2},
[10]={x=-1, z=-1},
[11]={x=-2, z=-1},
[12]={x=-1, z=0},
[13]={x=-2, z=1},
[14]={x=-1, z=1},
[15]={x=-1, z=2},
}
function advtrains.dirCoordSet(coord, dir)
local x,z
if advtrains.dir_trans_tbl[dir] then
x,z=advtrains.dir_trans_tbl[dir].x, advtrains.dir_trans_tbl[dir].z
else
error("advtrains: in helpers.lua/dirCoordSet() given dir="..(dir or "nil"))
end
return {x=coord.x+x, y=coord.y, z=coord.z+z}
end
advtrains.pos_add_dir = advtrains.dirCoordSet
function advtrains.pos_add_angle(pos, ang)
-- 0 is +Z -> meaning of sin/cos swapped
return vector.add(pos, {x=math.sin(ang), y=0, z=math.cos(ang)})
end
function advtrains.dirToCoord(dir)
return advtrains.dirCoordSet({x=0, y=0, z=0}, dir)
end
advtrains.dir_to_vector = advtrains.dirToCoord
function advtrains.maxN(list, expectstart)
local n=expectstart or 0
while list[n] do
n=n+1
end
return n-1
end
function advtrains.minN(list, expectstart)
local n=expectstart or 0
while list[n] do
n=n-1
end
return n+1
end
function atround(number)
return math.floor(number+0.5)
end
atfloor = math.floor
function advtrains.round_vector_floor_y(vec)
return {x=math.floor(vec.x+0.5), y=math.floor(vec.y), z=math.floor(vec.z+0.5)}
end
function advtrains.yawToDirection(yaw, conn1, conn2)
if not conn1 or not conn2 then
error("given nil to yawToDirection: conn1="..(conn1 or "nil").." conn2="..(conn1 or "nil"))
end
local yaw1 = advtrains.dir_to_angle(conn1)
local yaw2 = advtrains.dir_to_angle(conn2)
local adiff1 = advtrains.minAngleDiffRad(yaw, yaw1)
local adiff2 = advtrains.minAngleDiffRad(yaw, yaw2)
if math.abs(adiff2)<math.abs(adiff1) then
return conn2
else
return conn1
end
end
function advtrains.yawToAnyDir(yaw)
local min_conn, min_diff=0, 10
for conn, vec in pairs(advtrains.dir_trans_tbl) do
local yaw1 = advtrains.dir_to_angle(conn)
local diff = advtrains.minAngleDiffRad(yaw, yaw1)
if diff < min_diff then
min_conn = conn
min_diff = diff
end
end
return min_conn
end
function advtrains.yawToClosestConn(yaw, conns)
local min_connid, min_diff=1, 10
for connid, conn in ipairs(conns) do
local yaw1 = advtrains.dir_to_angle(conn.c)
local diff = advtrains.minAngleDiffRad(yaw, yaw1)
if diff < min_diff then
min_connid = connid
min_diff = diff
end
end
return min_connid
end
function advtrains.dir_to_angle(dir)
local uvec = vector.normalize(advtrains.dirToCoord(dir))
return math.atan2(uvec.x, uvec.z)
end
local pi, pi2 = math.pi, 2*math.pi
function advtrains.minAngleDiffRad(r1, r2)
while r1>pi2 do
r1=r1-pi2
end
while r1<0 do
r1=r1+pi2
end
while r2>pi2 do
r2=r2-pi2
end
while r1<0 do
r2=r2+pi2
end
local try1=r2-r1
local try2=r2+pi2-r1
local try3=r2-pi2-r1
local minabs = math.min(math.abs(try1), math.abs(try2), math.abs(try3))
if minabs==math.abs(try1) then
return try1
end
if minabs==math.abs(try2) then
return try2
end
if minabs==math.abs(try3) then
return try3
end
end
-- Takes 2 connections (0...AT_CMAX) as argument
-- Returns the angle median of those 2 positions from the pov
-- of standing on the cdir1 side and looking towards cdir2
-- cdir1 - >NODE> - cdir2
function advtrains.conn_angle_median(cdir1, cdir2)
local ang1 = advtrains.dir_to_angle(advtrains.oppd(cdir1))
local ang2 = advtrains.dir_to_angle(cdir2)
return ang1 + advtrains.minAngleDiffRad(ang1, ang2)/2
end
function advtrains.merge_tables(a, ...)
local new={}
for _,t in ipairs({a,...}) do
for k,v in pairs(t) do new[k]=v end
end
return new
end
function advtrains.save_keys(tbl, keys)
local new={}
for _,key in ipairs(keys) do
new[key] = tbl[key]
end
return new
end
function advtrains.get_real_index_position(path, index)
if not path or not index then return end
local first_pos=path[math.floor(index)]
local second_pos=path[math.floor(index)+1]
if not first_pos or not second_pos then return nil end
local factor=index-math.floor(index)
local actual_pos={x=first_pos.x-(first_pos.x-second_pos.x)*factor, y=first_pos.y-(first_pos.y-second_pos.y)*factor, z=first_pos.z-(first_pos.z-second_pos.z)*factor,}
return actual_pos
end
function advtrains.pos_median(pos1, pos2)
return {x=pos1.x-(pos1.x-pos2.x)*0.5, y=pos1.y-(pos1.y-pos2.y)*0.5, z=pos1.z-(pos1.z-pos2.z)*0.5}
end
function advtrains.abs_ceil(i)
return math.ceil(math.abs(i))*math.sign(i)
end
function advtrains.serialize_inventory(inv)
local ser={}
local liszts=inv:get_lists()
for lisztname, liszt in pairs(liszts) do
ser[lisztname]={}
for idx, item in ipairs(liszt) do
local istring=item:to_string()
if istring~="" then
ser[lisztname][idx]=istring
end
end
end
return minetest.serialize(ser)
end
function advtrains.deserialize_inventory(sers, inv)
local ser=minetest.deserialize(sers)
if ser then
inv:set_lists(ser)
return true
end
return false
end
--is_protected wrapper that checks for protection_bypass privilege
function advtrains.is_protected(pos, name)
if not name then
error("advtrains.is_protected() called without name parameter!")
end
if minetest.check_player_privs(name, {protection_bypass=true}) then
--player can bypass protection
return false
end
return minetest.is_protected(pos, name)
end
function advtrains.is_creative(name)
if not name then
error("advtrains.is_creative() called without name parameter!")
end
if minetest.check_player_privs(name, {creative=true}) then
return true
end
return minetest.settings:get_bool("creative_mode")
end
function advtrains.ms_to_kmh(speed)
return speed * 3.6
end
-- 4 possible inputs:
-- integer: just do that modulo calculation
-- table with c set: rotate c
-- table with tables: rotate each
-- table with integers: rotate each (probably no use case)
function advtrains.rotate_conn_by(conn, rotate)
if tonumber(conn) then
return (conn+rotate)%AT_CMAX
elseif conn.c then
return { c = (conn.c+rotate)%AT_CMAX, y = conn.y}
end
local tmp={}
for connid, data in ipairs(conn) do
tmp[connid]=advtrains.rotate_conn_by(data, rotate)
end
return tmp
end
function advtrains.oppd(dir)
return advtrains.rotate_conn_by(dir, AT_CMAX/2)
end
--conn_to_match like rotate_conn_by
--other_conns have to be a table of conn tables!
function advtrains.conn_matches_to(conn, other_conns)
if tonumber(conn) then
for connid, data in ipairs(other_conns) do
if advtrains.oppd(conn) == data.c then return connid end
end
return false
elseif conn.c then
for connid, data in ipairs(other_conns) do
local cmp = advtrains.oppd(conn)
if cmp.c == data.c and (cmp.y or 0) == (data.y or 0) then return connid end
end
return false
end
local tmp={}
for connid, data in ipairs(conn) do
local backmatch = advtrains.conn_matches_to(data, other_conns)
if backmatch then return backmatch, connid end --returns <connid of other rail> <connid of this rail>
end
return false
end
-- returns: <adjacent pos>, <conn index of adjacent>, <my conn index>, <railheight of adjacent>
function advtrains.get_adjacent_rail(this_posnr, this_conns_p, conn_idx, drives_on)
local this_pos = advtrains.round_vector_floor_y(this_posnr)
local this_conns = this_conns_p
if not this_conns then
_, this_conns = advtrains.get_rail_info_at(this_pos)
end
if not conn_idx then
for coni, _ in ipairs(this_conns) do
local adj_pos, adj_conn_idx, _, nry, nco = advtrains.get_adjacent_rail(this_pos, this_conns, coni)
if adj_pos then return adj_pos,adj_conn_idx,coni,nry, nco end
end
return nil
end
local conn = this_conns[conn_idx]
local conn_y = conn.y or 0
local adj_pos = advtrains.dirCoordSet(this_pos, conn.c);
while conn_y>=1 do
conn_y = conn_y - 1
adj_pos.y = adj_pos.y + 1
end
local nextnode_ok, nextconns, nextrail_y=advtrains.get_rail_info_at(adj_pos, drives_on)
if not nextnode_ok then
adj_pos.y = adj_pos.y - 1
conn_y = conn_y + 1
nextnode_ok, nextconns, nextrail_y=advtrains.get_rail_info_at(adj_pos, drives_on)
if not nextnode_ok then
return nil
end
end
local adj_connid = advtrains.conn_matches_to({c=conn.c, y=conn_y}, nextconns)
if adj_connid then
return adj_pos, adj_connid, conn_idx, nextrail_y, nextconns
end
return nil
end
local connlku={[2]={2,1}, [3]={2,1,1}, [4]={2,1,4,3}}
function advtrains.get_matching_conn(conn, nconns)
return connlku[nconns][conn]
end
function advtrains.random_id()
local idst=""
for i=0,5 do
idst=idst..(math.random(0,9))
end
return idst
end
|