99dbb27c2cc3b71716004771dc3f9b55086684f3
[advtrains.git] / tracks.lua
1 --advtrains by orwell96, see readme.txt
2
3 --dev-time settings:
4 --EDIT HERE
5 --If the old non-model rails on straight tracks should be replaced by the new...
6 --false: no
7 --true: yes
8 advtrains.register_replacement_lbms=false
9
10 --[[TracksDefinition
11 nodename_prefix
12 texture_prefix
13 description
14 common={}
15 straight={}
16 straight45={}
17 curve={}
18 curve45={}
19 lswitchst={}
20 lswitchst45={}
21 rswitchst={}
22 rswitchst45={}
23 lswitchcr={}
24 lswitchcr45={}
25 rswitchcr={}
26 rswitchcr45={}
27 vert1={
28 --you'll probably want to override mesh here
29 }
30 vert2={
31 --you'll probably want to override mesh here
32 }
33 ]]--
34 advtrains.all_tracktypes={}
35
36 --definition preparation
37 local function conns(c1, c2, r1, r2, rh, rots) return {conn1=c1, conn2=c2, rely1=r1, rely2=r2, railheight=rh} end
38
39 local t_30deg={
40 regstep=1,
41 variant={
42 st=conns(0,8),
43 cr=conns(0,7),
44 swlst=conns(0,8),
45 swlcr=conns(0,7),
46 swrst=conns(0,8),
47 swrcr=conns(0,9),
48 vst1=conns(8,0,0,0.5,0.25),
49 vst2=conns(8,0,0.5,1,0.75),
50 vst31=conns(8,0,0,0.33,0.16),
51 vst32=conns(8,0,0.33,0.66,0.5),
52 vst33=conns(8,0,0.66,1,0.83),
53 },
54 description={
55 st="straight",
56 cr="curve",
57 swlst="left switch (straight)",
58 swlcr="left switch (curve)",
59 swrst="right switch (straight)",
60 swrcr="right switch (curve)",
61 vst1="steep uphill 1/2",
62 vst2="steep uphill 2/2",
63 vst31="uphill 1/3",
64 vst32="uphill 2/3",
65 vst33="uphill 3/3",
66 },
67 switch={
68 swlst="swlcr",
69 swlcr="swlst",
70 swrst="swrcr",
71 swrcr="swrst",
72 },
73 switchmc={
74 swlst="on",
75 swlcr="off",
76 swrst="on",
77 swrcr="off",
78 },
79 regtp=true,
80 trackplacer={
81 st=true,
82 cr=true,
83 },
84 tpsingle={
85 st=true,
86 },
87 tpdefault="st",
88 trackworker={
89 ["swrcr"]="st",
90 ["swrst"]="st",
91 ["st"]="cr",
92 ["cr"]="swlst",
93 ["swlcr"]="swrcr",
94 ["swlst"]="swrst",
95 },
96 regsp=true,
97 slopenodes={
98 vst1=true, vst2=true,
99 vst31=true, vst32=true, vst33=true,
100 },
101 slopeplacer={
102 [2]={"vst1", "vst2"},
103 [3]={"vst31", "vst32", "vst33"},
104 max=3,--highest entry
105 },
106 slopeplacer_45={
107 [2]={"vst1_45", "vst2_45"},
108 max=2,
109 },
110 rotation={"", "_30", "_45", "_60"},
111 increativeinv={},
112 }
113 local t_30deg_straightonly={
114 regstep=1,
115 variant={
116 st=conns(0,8),
117 },
118 description={
119 st="straight",
120 },
121 switch={
122 },
123 switchmc={
124 },
125 regtp=true,
126 trackplacer={
127 },
128 tpsingle={
129 },
130 tpdefault="st",
131 trackworker={
132 ["st"]="st",
133 },
134 slopenodes={},
135 rotation={"", "_30", "_45", "_60"},
136 increativeinv={st},
137 }
138 local t_30deg_straightonly_noplacer={
139 regstep=1,
140 variant={
141 st=conns(0,8),
142 },
143 description={
144 st="straight",
145 },
146 switch={
147 },
148 switchmc={
149 },
150 regtp=false,
151 trackplacer={
152 },
153 tpsingle={
154 },
155 tpdefault="st",
156 trackworker={
157 ["st"]="st",
158 },
159 slopenodes={},
160 rotation={"", "_30", "_45", "_60"},
161 increativeinv={st},
162 }
163 local t_45deg={
164 regstep=2,
165 variant={
166 st=conns(0,8),
167 cr=conns(0,6),
168 swlst=conns(0,8),
169 swlcr=conns(0,6),
170 swrst=conns(0,8),
171 swrcr=conns(0,10),
172 vst1=conns(8,0,0,0.5,0.25),
173 vst2=conns(8,0,0.5,1,0.75),
174 },
175 description={
176 st="straight",
177 cr="curve",
178 swlst="left switch (straight)",
179 swlcr="left switch (curve)",
180 swrst="right switch (straight)",
181 swrcr="right switch (curve)",
182 vst1="vertical lower node",
183 vst2="vertical upper node",
184 },
185 switch={
186 swlst="swlcr",
187 swlcr="swlst",
188 swrst="swrcr",
189 swrcr="swrst",
190 },
191 switchmc={
192 swlst="on",
193 swlcr="off",
194 swrst="on",
195 swrcr="off",
196 },
197 regtp=true,
198 trackplacer={
199 st=true,
200 cr=true,
201 },
202 tpsingle={
203 st=true,
204 },
205 tpdefault="st",
206 trackworker={
207 ["swrcr"]="st",
208 ["swrst"]="st",
209 ["st"]="cr",
210 ["cr"]="swlst",
211 ["swlcr"]="swrcr",
212 ["swlst"]="swrst",
213 },
214 slopenodes={},
215 rotation={"", "_45"},
216 increativeinv={vst1=true, vst2=true}
217 }
218
219 --definition format: ([] optional)
220 --[[{
221 nodename_prefix
222 texture_prefix
223 [shared_texture]
224 models_prefix
225 models_suffix (with dot)
226 [shared_model]
227 formats={
228 st,cr,swlst,swlcr,swrst,swrcr,vst1,vst2
229 (each a table with indices 0-3, for if to register a rail with this 'rotation' table entry. nil is assumed as 'all', set {} to not register at all)
230 }
231 common={} change something on common rail appearance
232 }]]
233 function advtrains.register_tracks(tracktype, def, preset)
234 local function make_switchfunc(suffix_target, mesecon_state)
235 local switchfunc=function(pos, node)
236 if advtrains.is_train_at_pos(pos) then return end
237 minetest.set_node(pos, {name=def.nodename_prefix.."_"..suffix_target, param2=node.param2})
238 advtrains.invalidate_all_paths()
239 advtrains.reset_trackdb_position(pos)
240 end
241 return switchfunc, {effector = {
242 ["action_"..mesecon_state] = switchfunc,
243 rules=advtrains.meseconrules
244 }}
245 end
246 local function make_overdef(suffix, rotation, conns, switchfunc, mesecontbl, in_creative_inv, drop_slope)
247 local img_suffix=suffix..rotation
248 return {
249 mesh = def.shared_model or (def.models_prefix.."_"..img_suffix..def.models_suffix),
250 tiles = {def.shared_texture or (def.texture_prefix.."_"..img_suffix..".png")},
251 --inventory_image = def.texture_prefix.."_"..img_suffix..".png",
252 --wield_image = def.texture_prefix.."_"..img_suffix..".png",
253 description=def.description.."("..preset.description[suffix]..rotation..")",
254 connect1=conns.conn1,
255 connect2=conns.conn2,
256 rely1=conns.rely1 or 0,
257 rely2=conns.rely2 or 0,
258 railheight=conns.railheight or 0,
259 on_rightclick=switchfunc,
260 groups = {
261 attached_node=1,
262 ["advtrains_track_"..tracktype]=1,
263 dig_immediate=2,
264 not_in_creative_inventory=(not in_creative_inv and 1 or nil),
265 not_blocking_trains=1,
266 },
267 mesecons=mesecontbl,
268 drop = increativeinv and def.nodename_prefix.."_"..suffix..rotation or (drop_slope and def.nodename_prefix.."_slopeplacer" or def.nodename_prefix.."_placer"),
269 }
270 end
271 local function cycle_conns(conns, rotid)
272 local add=(rotid-1)*preset.regstep
273 return {
274 conn1=(conns.conn1+add)%16,
275 conn2=(conns.conn2+add)%16,
276 rely1=conns.rely1 or 0,
277 rely2=conns.rely2 or 0,
278 railheight=conns.railheight or 0,
279 }
280 end
281 local common_def=advtrains.merge_tables({
282 description = def.description,
283 drawtype = "mesh",
284 paramtype="light",
285 paramtype2="facedir",
286 walkable = false,
287 selection_box = {
288 type = "fixed",
289 fixed = {-1/2, -1/2, -1/2, 1/2, -1/2+1/16, 1/2},
290 },
291 rely1=0,
292 rely2=0,
293 railheight=0,
294 drop=def.nodename_prefix.."_placer",
295 can_dig=function(pos)
296 return not advtrains.is_train_at_pos(pos)
297 end,
298 after_dig_node=function(pos)
299 advtrains.invalidate_all_paths()
300 advtrains.reset_trackdb_position(pos)
301 end,
302 after_place_node=function(pos)
303 advtrains.reset_trackdb_position(pos)
304 end,
305 }, def.common or {})
306 --make trackplacer base def
307 advtrains.trackplacer.register_tracktype(def.nodename_prefix, preset.tpdefault)
308 if preset.regtp then
309 advtrains.trackplacer.register_track_placer(def.nodename_prefix, def.texture_prefix, def.description)
310 end
311 if preset.regsp then
312 advtrains.slope.register_placer(def, preset)
313 end
314 for suffix, conns in pairs(preset.variant) do
315 for rotid, rotation in ipairs(preset.rotation) do
316 if not def.formats[suffix] or def.formats[suffix][rotid] then
317 local switchfunc, mesecontbl
318 if preset.switch[suffix] then
319 switchfunc, mesecontbl=make_switchfunc(preset.switch[suffix]..rotation, preset.switchmc[suffix])
320 end
321 local adef={}
322 if def.get_additional_definiton then
323 adef=def.get_additional_definiton(def, preset, suffix, rotation)
324 end
325
326 minetest.register_node(def.nodename_prefix.."_"..suffix..rotation, advtrains.merge_tables(
327 common_def,
328 make_overdef(
329 suffix, rotation,
330 cycle_conns(conns, rotid),
331 switchfunc, mesecontbl, preset.increativeinv[suffix], preset.slopenodes[suffix]
332 ),
333 adef
334 )
335 )
336 --trackplacer
337 if preset.regtp then
338 if preset.trackplacer[suffix] then
339 advtrains.trackplacer.add_double_conn(def.nodename_prefix, suffix, rotation, cycle_conns(conns, rotid))
340 end
341 if preset.tpsingle[suffix] then
342 advtrains.trackplacer.add_single_conn(def.nodename_prefix, suffix, rotation, cycle_conns(conns, rotid))
343 end
344 end
345 advtrains.trackplacer.add_worked(def.nodename_prefix, suffix, rotation, preset.trackworker[suffix])
346 end
347 end
348 end
349 table.insert(advtrains.all_tracktypes, tracktype)
350 end
351
352
353 function advtrains.is_track_and_drives_on(nodename, drives_on)
354 if not minetest.registered_nodes[nodename] then
355 return false
356 end
357 local nodedef=minetest.registered_nodes[nodename]
358 for k,v in ipairs(drives_on) do
359 if nodedef.groups["advtrains_track_"..v] then
360 return true
361 end
362 end
363 return false
364 end
365
366 function advtrains.get_track_connections(name, param2)
367 local nodedef=minetest.registered_nodes[name]
368 if not nodedef then print("[advtrains] get_track_connections couldn't find nodedef for nodename "..(name or "nil")) return 0, 8, 0, 0, 0 end
369 local noderot=param2
370 if not param2 then noderot=0 end
371 if noderot > 3 then print("[advtrains] get_track_connections: rail has invaild param2 of "..noderot) noderot=0 end
372
373 return (nodedef.connect1 + 4 * noderot)%16, (nodedef.connect2 + 4 * noderot)%16, nodedef.rely1 or 0, nodedef.rely2 or 0, nodedef.railheight or 0
374 end
375
376 --detector code
377 --holds a table with nodes on which trains are on.
378
379 advtrains.detector = {}
380 advtrains.detector.on_node = {}
381 advtrains.detector.on_node_restore = {}
382 --set if paths were invalidated before. tells trainlogic.lua to call advtrains.detector.finalize_restore()
383 advtrains.detector.clean_step_before = false
384
385 --when train enters a node, call this
386 --The entry already being contained in advtrains.detector.on_node_restore will not trigger an on_train_enter event on the node. (when path is reset, this is saved).
387 function advtrains.detector.enter_node(pos, train_id)
388 local pts = minetest.pos_to_string(advtrains.round_vector_floor_y(pos))
389 --print("enterNode "..pts.." "..train_id)
390 if not advtrains.detector.on_node[pts] then
391 advtrains.detector.on_node[pts]=train_id
392 if advtrains.detector.on_node_restore[pts] then
393 advtrains.detector.on_node_restore[pts]=nil
394 else
395 advtrains.detector.call_enter_callback(advtrains.round_vector_floor_y(pos), train_id)
396 end
397 end
398 end
399 function advtrains.detector.leave_node(pos, train_id)
400 local pts = minetest.pos_to_string(advtrains.round_vector_floor_y(pos))
401 --print("leaveNode "..pts.." "..train_id)
402 if advtrains.detector.on_node[pts] then
403 advtrains.detector.call_leave_callback(advtrains.round_vector_floor_y(pos), train_id)
404 advtrains.detector.on_node[pts]=nil
405 end
406 end
407 --called immediately before invalidating paths
408 function advtrains.detector.setup_restore()
409 --print("setup_restore")
410 advtrains.detector.on_node_restore = advtrains.detector.on_node
411 advtrains.detector.on_node = {}
412 end
413 --called one step after invalidating paths, when all trains have restored their path and called enter_node for their contents.
414 function advtrains.detector.finalize_restore()
415 --print("finalize_restore")
416 for pts, train_id in pairs(advtrains.detector.on_node_restore) do
417 --print("called leave callback "..pts.." "..train_id)
418 advtrains.detector.call_leave_callback(minetest.string_to_pos(pts), train_id)
419 end
420 advtrains.detector.on_node_restore = {}
421 end
422 function advtrains.detector.call_enter_callback(pos, train_id)
423 --print("instructed to call enter calback")
424
425 local node = minetest.get_node(pos) --this spares the check if node is nil, it has a name in any case
426 local mregnode=minetest.registered_nodes[node.name]
427 if mregnode and mregnode.advtrains and mregnode.advtrains.on_train_enter then
428 mregnode.advtrains.on_train_enter(pos, train_id)
429 end
430 end
431 function advtrains.detector.call_leave_callback(pos, train_id)
432 --print("instructed to call leave calback")
433
434 local node = minetest.get_node(pos) --this spares the check if node is nil, it has a name in any case
435 local mregnode=minetest.registered_nodes[node.name]
436 if mregnode and mregnode.advtrains and mregnode.advtrains.on_train_leave then
437 mregnode.advtrains.on_train_leave(pos, train_id)
438 end
439 end
440
441 -- slope placer. Defined in register_tracks.
442 --crafted with rail and gravel
443 local sl={}
444 function sl.register_placer(def, preset)
445 minetest.register_craftitem(def.nodename_prefix.."_slopeplacer",{
446 description = def.description.." Slope",
447 inventory_image = def.texture_prefix.."_slopeplacer.png",
448 wield_image = def.texture_prefix.."_slopeplacer.png",
449 groups={},
450 on_place = sl.create_slopeplacer_on_place(def, preset)
451 })
452 end
453 --(itemstack, placer, pointed_thing)
454 function sl.create_slopeplacer_on_place(def, preset)
455 return function(istack, player, pt)
456 if not pt.type=="node" then
457 minetest.chat_send_player(player:get_player_name(), "Can't place: not pointing at node")
458 return istack
459 end
460 local pos=pt.above
461 if not pos then
462 minetest.chat_send_player(player:get_player_name(), "Can't place: not pointing at node")
463 return istack
464 end
465 local node=minetest.get_node(pos)
466 if not minetest.registered_nodes[node.name] or not minetest.registered_nodes[node.name].buildable_to then
467 minetest.chat_send_player(player:get_player_name(), "Can't place: space occupied!")
468 return istack
469 end
470 if minetest.is_protected(pos, player:get_player_name()) then
471 minetest.chat_send_player(player:get_player_name(), "Can't place: protected position!")
472 return istack
473 end
474 --determine player orientation (only horizontal component)
475 --get_look_horizontal may not be available
476 local yaw=player.get_look_horizontal and player:get_look_horizontal() or (player:get_look_yaw() - math.pi/2)
477
478 --rounding unit vectors is a nice way for selecting 1 of 8 directions since sin(30°) is 0.5.
479 dirvec={x=math.floor(math.sin(-yaw)+0.5), y=0, z=math.floor(math.cos(-yaw)+0.5)}
480 --translate to direction to look up inside the preset table
481 local param2, rot45=({
482 [-1]={
483 [-1]=2,
484 [0]=3,
485 [1]=3,
486 },
487 [0]={
488 [-1]=2,
489 [1]=0,
490 },
491 [1]={
492 [-1]=1,
493 [0]=1,
494 [1]=0,
495 },
496 })[dirvec.x][dirvec.z], dirvec.x~=0 and dirvec.z~=0
497 local lookup=preset.slopeplacer
498 if rot45 then lookup=preset.slopeplacer_45 end
499
500 --go unitvector forward and look how far the next node is
501 local step=1
502 while step<=lookup.max do
503 local node=minetest.get_node(vector.add(pos, dirvec))
504 --next node solid?
505 if not minetest.registered_nodes[node.name] or not minetest.registered_nodes[node.name].buildable_to or minetest.is_protected(pos, player:get_player_name()) then
506 --do slopes of this distance exist?
507 if lookup[step] then
508 if minetest.setting_getbool("creative_mode") or istack:get_count()>=step then
509 --start placing
510 local placenodes=lookup[step]
511 while step>0 do
512 minetest.set_node(pos, {name=def.nodename_prefix.."_"..placenodes[step], param2=param2})
513 if not minetest.setting_getbool("creative_mode") then
514 istack:take_item()
515 end
516 step=step-1
517 pos=vector.subtract(pos, dirvec)
518 end
519 else
520 minetest.chat_send_player(player:get_player_name(), "Can't place: Not enough slope items left ("..step.." required)")
521 end
522 else
523 minetest.chat_send_player(player:get_player_name(), "Can't place: There's no slope of length "..step)
524 end
525 return istack
526 end
527 step=step+1
528 pos=vector.add(pos, dirvec)
529 end
530 minetest.chat_send_player(player:get_player_name(), "Can't place: no supporting node at upper end.")
531 return itemstack
532 end
533 end
534
535 advtrains.slope=sl
536
537 --END code, BEGIN definition
538 --definition format: ([] optional)
539 --[[{
540 nodename_prefix
541 texture_prefix
542 [shared_texture]
543 models_prefix
544 models_suffix (with dot)
545 [shared_model]
546 formats={
547 st,cr,swlst,swlcr,swrst,swrcr,vst1,vst2
548 (each a table with indices 0-3, for if to register a rail with this 'rotation' table entry. nil is assumed as 'all', set {} to not register at all)
549 }
550 common={} change something on common rail appearance
551 }]]
552
553 advtrains.register_tracks("regular", {
554 nodename_prefix="advtrains:track",
555 texture_prefix="advtrains_track",
556 shared_model="trackplane.b3d",
557 description="Deprecated Track",
558 formats={vst1={}, vst2={}},
559 }, t_45deg)
560
561
562 advtrains.register_tracks("default", {
563 nodename_prefix="advtrains:dtrack",
564 texture_prefix="advtrains_dtrack",
565 models_prefix="advtrains_dtrack",
566 models_suffix=".b3d",
567 shared_texture="advtrains_dtrack_rail.png",
568 description="Track",
569 formats={vst1={true, false, true}, vst2={true, false, true}, vst31={true}, vst32={true}, vst33={true}},
570 }, t_30deg)
571
572 --bumpers
573 advtrains.register_tracks("default", {
574 nodename_prefix="advtrains:dtrack_bumper",
575 texture_prefix="advtrains_dtrack_bumper",
576 models_prefix="advtrains_dtrack_bumper",
577 models_suffix=".b3d",
578 shared_texture="advtrains_dtrack_rail.png",
579 description="Bumper",
580 formats={},
581 }, t_30deg_straightonly)
582 --legacy bumpers
583 for _,rot in ipairs({"", "_30", "_45", "_60"}) do
584 minetest.register_alias("advtrains:dtrack_bumper"..rot, "advtrains:dtrack_bumper_st"..rot)
585 end
586
587 if mesecon then
588 advtrains.register_tracks("default", {
589 nodename_prefix="advtrains:dtrack_detector_off",
590 texture_prefix="advtrains_dtrack_detector",
591 models_prefix="advtrains_dtrack_detector",
592 models_suffix=".b3d",
593 shared_texture="advtrains_dtrack_rail.png",
594 description="Detector Rail",
595 formats={},
596 get_additional_definiton = function(def, preset, suffix, rotation)
597 return {
598 mesecons = {
599 receptor = {
600 state = mesecon.state.off,
601 rules = advtrains.meseconrules
602 }
603 },
604 advtrains = {
605 on_train_enter=function(pos, train_id)
606 minetest.swap_node(pos, {name="advtrains:dtrack_detector_on".."_"..suffix..rotation, param2=minetest.get_node(pos).param2})
607 mesecon.receptor_on(pos, advtrains.meseconrules)
608 end
609 }
610 }
611 end
612 }, t_30deg_straightonly)
613 advtrains.register_tracks("default", {
614 nodename_prefix="advtrains:dtrack_detector_on",
615 texture_prefix="advtrains_dtrack_detector",
616 models_prefix="advtrains_dtrack_detector",
617 models_suffix=".b3d",
618 shared_texture="advtrains_dtrack_rail_detector_on.png",
619 description="Detector(on)(you hacker you)",
620 formats={},
621 get_additional_definiton = function(def, preset, suffix, rotation)
622 return {
623 mesecons = {
624 receptor = {
625 state = mesecon.state.on,
626 rules = advtrains.meseconrules
627 }
628 },
629 advtrains = {
630 on_train_leave=function(pos, train_id)
631 minetest.swap_node(pos, {name="advtrains:dtrack_detector_off".."_"..suffix..rotation, param2=minetest.get_node(pos).param2})
632 mesecon.receptor_off(pos, advtrains.meseconrules)
633 end
634 }
635 }
636 end
637 }, t_30deg_straightonly_noplacer)
638 end
639 --TODO legacy
640 --I know lbms are better for this purpose
641 for name,rep in pairs({swl_st="swlst", swr_st="swrst", swl_cr="swlcr", swr_cr="swrcr", }) do
642 minetest.register_abm({
643 -- In the following two fields, also group:groupname will work.
644 nodenames = {"advtrains:track_"..name},
645 interval = 1.0, -- Operation interval in seconds
646 chance = 1, -- Chance of trigger per-node per-interval is 1.0 / this
647 action = function(pos, node, active_object_count, active_object_count_wider) minetest.set_node(pos, {name="advtrains:track_"..rep, param2=node.param2}) end,
648 })
649 minetest.register_abm({
650 -- In the following two fields, also group:groupname will work.
651 nodenames = {"advtrains:track_"..name.."_45"},
652 interval = 1.0, -- Operation interval in seconds
653 chance = 1, -- Chance of trigger per-node per-interval is 1.0 / this
654 action = function(pos, node, active_object_count, active_object_count_wider) minetest.set_node(pos, {name="advtrains:track_"..rep.."_45", param2=node.param2}) end,
655 })
656 end
657
658 if advtrains.register_replacement_lbms then
659 minetest.register_lbm({
660 name = "advtrains:ramp_replacement_1",
661 -- In the following two fields, also group:groupname will work.
662 nodenames = {"advtrains:track_vert1"},
663 action = function(pos, node, active_object_count, active_object_count_wider) minetest.set_node(pos, {name="advtrains:dtrack_vst1", param2=(node.param2+2)%4}) end,
664 })
665 minetest.register_lbm({
666 name = "advtrains:ramp_replacement_1",
667 -- -- In the following two fields, also group:groupname will work.
668 nodenames = {"advtrains:track_vert2"},
669 action = function(pos, node, active_object_count, active_object_count_wider) minetest.set_node(pos, {name="advtrains:dtrack_vst2", param2=(node.param2+2)%4}) end,
670 })
671 minetest.register_abm({
672 name = "advtrains:st_rep_1",
673 -- In the following two fields, also group:groupname will work.
674 nodenames = {"advtrains:track_st"},
675 interval=1,
676 chance=1,
677 action = function(pos, node, active_object_count, active_object_count_wider) minetest.set_node(pos, {name="advtrains:dtrack_st", param2=node.param2}) end,
678 })
679 minetest.register_lbm({
680 name = "advtrains:st_rep_1",
681 -- -- In the following two fields, also group:groupname will work.
682 nodenames = {"advtrains:track_st_45"},
683 action = function(pos, node, active_object_count, active_object_count_wider) minetest.set_node(pos, {name="advtrains:dtrack_st_45", param2=node.param2}) end,
684 })
685 end
686
687
688
689
690
691
692
693