trackplacer.lua 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434
  1. --trackplacer.lua
  2. --holds code for the track-placing system. the default 'track' item will be a craftitem that places rails as needed. this will neither place or change switches nor place vertical rails.
  3. --all new trackplacer code
  4. local tp={
  5. tracks={}
  6. }
  7. function tp.register_tracktype(nnprefix, n_suffix)
  8. if tp.tracks[nnprefix] then return end--due to the separate registration of slopes and flats for the same nnpref, definition would be overridden here. just don't.
  9. tp.tracks[nnprefix]={
  10. default=n_suffix,
  11. single_conn={},
  12. single_conn_1={},
  13. single_conn_2={},
  14. double_conn={},
  15. double_conn_1={},
  16. double_conn_2={},
  17. --keys:conn1_conn2 (example:1_4)
  18. --values:{name=x, param2=x}
  19. twcycle={},
  20. twrotate={},--indexed by suffix, list, tells order of rotations
  21. modify={},
  22. }
  23. end
  24. function tp.add_double_conn(nnprefix, suffix, rotation, conns)
  25. local nodename=nnprefix.."_"..suffix..rotation
  26. for i=0,3 do
  27. tp.tracks[nnprefix].double_conn[((conns.conn1+4*i)%16).."_"..((conns.conn2+4*i)%16)]={name=nodename, param2=i}
  28. tp.tracks[nnprefix].double_conn[((conns.conn2+4*i)%16).."_"..((conns.conn1+4*i)%16)]={name=nodename, param2=i}
  29. tp.tracks[nnprefix].double_conn_1[((conns.conn1+4*i)%16).."_"..((conns.conn2+4*i)%16)]={name=nodename, param2=i}
  30. tp.tracks[nnprefix].double_conn_2[((conns.conn2+4*i)%16).."_"..((conns.conn1+4*i)%16)]={name=nodename, param2=i}
  31. end
  32. tp.tracks[nnprefix].modify[nodename]=true
  33. end
  34. function tp.add_single_conn(nnprefix, suffix, rotation, conns)
  35. local nodename=nnprefix.."_"..suffix..rotation
  36. for i=0,3 do
  37. tp.tracks[nnprefix].single_conn[((conns.conn1+4*i)%16)]={name=nodename, param2=i}
  38. tp.tracks[nnprefix].single_conn[((conns.conn2+4*i)%16)]={name=nodename, param2=i}
  39. tp.tracks[nnprefix].single_conn_1[((conns.conn1+4*i)%16)]={name=nodename, param2=i}
  40. tp.tracks[nnprefix].single_conn_2[((conns.conn2+4*i)%16)]={name=nodename, param2=i}
  41. end
  42. tp.tracks[nnprefix].modify[nodename]=true
  43. end
  44. function tp.add_worked(nnprefix, suffix, rotation, cycle_follows)
  45. tp.tracks[nnprefix].twcycle[suffix]=cycle_follows
  46. if not tp.tracks[nnprefix].twrotate[suffix] then tp.tracks[nnprefix].twrotate[suffix]={} end
  47. table.insert(tp.tracks[nnprefix].twrotate[suffix], rotation)
  48. end
  49. --[[
  50. rewrite algorithm.
  51. selection criteria: these will never be changed or even selected:
  52. - tracks being already connected on both sides
  53. - tracks that are already connected on one side but are not bendable to the desired position
  54. the following situations can occur:
  55. 1. there are two more than two rails around
  56. 1.1 there is one or more subset(s) that can be directly connected
  57. -> choose the first possibility
  58. 2.2 not
  59. -> choose the first one and orient straight
  60. 2. there's exactly 1 rail around
  61. -> choose and orient straight
  62. 3. there's no rail around
  63. -> set straight
  64. ]]
  65. local function istrackandbc(pos_p, conn)
  66. local tpos = pos_p
  67. local cnode=minetest.get_node(advtrains.dirCoordSet(tpos, conn.c))
  68. if advtrains.is_track_and_drives_on(cnode.name, advtrains.all_tracktypes) then
  69. local cconns=advtrains.get_track_connections(cnode.name, cnode.param2)
  70. return advtrains.conn_matches_to(conn, cconns)
  71. end
  72. --try the same 1 node below
  73. tpos = {x=tpos.x, y=tpos.y-1, z=tpos.z}
  74. cnode=minetest.get_node(advtrains.dirCoordSet(tpos, conn.c))
  75. if advtrains.is_track_and_drives_on(cnode.name, advtrains.all_tracktypes) then
  76. local cconns=advtrains.get_track_connections(cnode.name, cnode.param2)
  77. return advtrains.conn_matches_to(conn, cconns)
  78. end
  79. return false
  80. end
  81. function tp.find_already_connected(pos)
  82. local dnode=minetest.get_node(pos)
  83. local dconns=advtrains.get_track_connections(dnode.name, dnode.param2)
  84. local found_conn
  85. for connid, conn in ipairs(dconns) do
  86. if istrackandbc(pos, conn) then
  87. if found_conn then --we found one in previous iteration
  88. return true, true --signal that it's connected
  89. else
  90. found_conn = conn.c
  91. end
  92. end
  93. end
  94. return found_conn
  95. end
  96. function tp.rail_and_can_be_bent(originpos, conn)
  97. local pos=advtrains.dirCoordSet(originpos, conn)
  98. local newdir=(conn+8)%16
  99. local node=minetest.get_node(pos)
  100. if not advtrains.is_track_and_drives_on(node.name, advtrains.all_tracktypes) then
  101. return false
  102. end
  103. local ndef=minetest.registered_nodes[node.name]
  104. local nnpref = ndef and ndef.at_nnpref
  105. if not nnpref then return false end
  106. local tr=tp.tracks[nnpref]
  107. if not tr then return false end
  108. if not tr.modify[node.name] then
  109. --we actually can use this rail, but only if it already points to the desired direction.
  110. if advtrains.is_track_and_drives_on(node.name, advtrains.all_tracktypes) then
  111. local cconns=advtrains.get_track_connections(node.name, node.param2)
  112. return advtrains.conn_matches_to(conn, cconns)
  113. end
  114. end
  115. -- If the rail is not allowed to be modified, also only use if already in desired direction
  116. if not advtrains.can_dig_or_modify_track(pos) then
  117. local cconns=advtrains.get_track_connections(node.name, node.param2)
  118. return advtrains.conn_matches_to(conn, cconns)
  119. end
  120. --rail at other end?
  121. local adj1, adj2=tp.find_already_connected(pos)
  122. if adj1 and adj2 then
  123. return false--dont destroy existing track
  124. elseif adj1 and not adj2 then
  125. if tr.double_conn[adj1.."_"..newdir] then
  126. return true--if exists, connect new rail and old end
  127. end
  128. return false
  129. else
  130. if tr.single_conn[newdir] then--just rotate old rail to right orientation
  131. return true
  132. end
  133. return false
  134. end
  135. end
  136. function tp.bend_rail(originpos, conn)
  137. local pos=advtrains.dirCoordSet(originpos, conn)
  138. local newdir=advtrains.oppd(conn)
  139. local node=minetest.get_node(pos)
  140. local ndef=minetest.registered_nodes[node.name]
  141. local nnpref = ndef and ndef.at_nnpref
  142. if not nnpref then return false end
  143. local tr=tp.tracks[nnpref]
  144. if not tr then return false end
  145. --is rail already connected? no need to bend.
  146. local conns=advtrains.get_track_connections(node.name, node.param2)
  147. if advtrains.conn_matches_to(conn, conns) then
  148. return
  149. end
  150. --rail at other end?
  151. local adj1, adj2=tp.find_already_connected(pos)
  152. if adj1 and adj2 then
  153. return false--dont destroy existing track
  154. elseif adj1 and not adj2 then
  155. if tr.double_conn[adj1.."_"..newdir] then
  156. advtrains.ndb.swap_node(pos, tr.double_conn[adj1.."_"..newdir])
  157. return true--if exists, connect new rail and old end
  158. end
  159. return false
  160. else
  161. if tr.single_conn[newdir] then--just rotate old rail to right orientation
  162. advtrains.ndb.swap_node(pos, tr.single_conn[newdir])
  163. return true
  164. end
  165. return false
  166. end
  167. end
  168. function tp.placetrack(pos, nnpref, placer, itemstack, pointed_thing, yaw)
  169. --1. find all rails that are likely to be connected
  170. local tr=tp.tracks[nnpref]
  171. local p_rails={}
  172. local p_railpos={}
  173. for i=0,15 do
  174. if tp.rail_and_can_be_bent(pos, i, nnpref) then
  175. p_rails[#p_rails+1]=i
  176. p_railpos[i] = pos
  177. else
  178. local upos = {x=pos.x, y=pos.y-1, z=pos.z}
  179. if tp.rail_and_can_be_bent(upos, i, nnpref) then
  180. p_rails[#p_rails+1]=i
  181. p_railpos[i] = upos
  182. end
  183. end
  184. end
  185. -- try double_conn
  186. if #p_rails > 1 then
  187. --iterate subsets
  188. for k1, conn1 in ipairs(p_rails) do
  189. for k2, conn2 in ipairs(p_rails) do
  190. if k1~=k2 then
  191. local dconn1 = tr.double_conn_1
  192. local dconn2 = tr.double_conn_2
  193. if not (advtrains.yawToDirection(yaw, conn1, conn2) == conn1) then
  194. dconn1 = tr.double_conn_2
  195. dconn2 = tr.double_conn_1
  196. end
  197. -- Checks are made this way round so that dconn1 has priority (this will make arrows of atc rails
  198. -- point in the right direction)
  199. local using
  200. if (dconn2[conn1.."_"..conn2]) then
  201. using = dconn2[conn1.."_"..conn2]
  202. end
  203. if (dconn1[conn1.."_"..conn2]) then
  204. using = dconn1[conn1.."_"..conn2]
  205. end
  206. if using then
  207. -- has found a fitting rail in either direction
  208. -- if not, continue loop
  209. tp.bend_rail(p_railpos[conn1], conn1, nnpref)
  210. tp.bend_rail(p_railpos[conn2], conn2, nnpref)
  211. advtrains.ndb.swap_node(pos, using)
  212. local nname=using.name
  213. if minetest.registered_nodes[nname] and minetest.registered_nodes[nname].after_place_node then
  214. minetest.registered_nodes[nname].after_place_node(pos, placer, itemstack, pointed_thing)
  215. end
  216. return
  217. end
  218. end
  219. end
  220. end
  221. end
  222. -- try single_conn
  223. if #p_rails > 0 then
  224. for ix, p_rail in ipairs(p_rails) do
  225. local sconn1 = tr.single_conn_1
  226. local sconn2 = tr.single_conn_2
  227. if not (advtrains.yawToDirection(yaw, p_rail, (p_rail+8)%16) == p_rail) then
  228. sconn1 = tr.single_conn_2
  229. sconn2 = tr.single_conn_1
  230. end
  231. if sconn1[p_rail] then
  232. local using = sconn1[p_rail]
  233. tp.bend_rail(p_railpos[p_rail], p_rail, nnpref)
  234. advtrains.ndb.swap_node(pos, using)
  235. local nname=using.name
  236. if minetest.registered_nodes[nname] and minetest.registered_nodes[nname].after_place_node then
  237. minetest.registered_nodes[nname].after_place_node(pos, placer, itemstack, pointed_thing)
  238. end
  239. return
  240. end
  241. if sconn2[p_rail] then
  242. local using = sconn2[p_rail]
  243. tp.bend_rail(p_railpos[p_rail], p_rail, nnpref)
  244. advtrains.ndb.swap_node(pos, using)
  245. local nname=using.name
  246. if minetest.registered_nodes[nname] and minetest.registered_nodes[nname].after_place_node then
  247. minetest.registered_nodes[nname].after_place_node(pos, placer, itemstack, pointed_thing)
  248. end
  249. return
  250. end
  251. end
  252. end
  253. --use default
  254. minetest.set_node(pos, {name=nnpref.."_"..tr.default})
  255. if minetest.registered_nodes[nnpref.."_"..tr.default] and minetest.registered_nodes[nnpref.."_"..tr.default].after_place_node then
  256. minetest.registered_nodes[nnpref.."_"..tr.default].after_place_node(pos, placer, itemstack, pointed_thing)
  257. end
  258. end
  259. function tp.register_track_placer(nnprefix, imgprefix, dispname, def)
  260. minetest.register_craftitem(":"..nnprefix.."_placer",{
  261. description = dispname,
  262. inventory_image = imgprefix.."_placer.png",
  263. wield_image = imgprefix.."_placer.png",
  264. groups={advtrains_trackplacer=1, digtron_on_place=1},
  265. liquids_pointable = def.liquids_pointable,
  266. on_place = function(itemstack, placer, pointed_thing)
  267. local name = placer:get_player_name()
  268. if not name then
  269. return itemstack, false
  270. end
  271. if pointed_thing.type=="node" then
  272. local pos=pointed_thing.above
  273. local upos=vector.subtract(pointed_thing.above, {x=0, y=1, z=0})
  274. if not advtrains.check_track_protection(pos, name) then
  275. return itemstack, false
  276. end
  277. if minetest.registered_nodes[minetest.get_node(pos).name] and minetest.registered_nodes[minetest.get_node(pos).name].buildable_to then
  278. local s
  279. if def.suitable_substrate then
  280. s = def.suitable_substrate(upos)
  281. else
  282. s = minetest.registered_nodes[minetest.get_node(upos).name] and minetest.registered_nodes[minetest.get_node(upos).name].walkable
  283. end
  284. if s then
  285. -- minetest.chat_send_all(nnprefix)
  286. local yaw = placer:get_look_horizontal()
  287. tp.placetrack(pos, nnprefix, placer, itemstack, pointed_thing, yaw)
  288. if not advtrains.is_creative(name) then
  289. itemstack:take_item()
  290. end
  291. end
  292. end
  293. end
  294. return itemstack, true
  295. end,
  296. })
  297. end
  298. minetest.register_craftitem("advtrains:trackworker",{
  299. description = attrans("Track Worker Tool\n\nLeft-click: change rail type (straight/curve/switch)\nRight-click: rotate rail/bumper/signal/etc."),
  300. groups = {cracky=1}, -- key=name, value=rating; rating=1..3.
  301. inventory_image = "advtrains_trackworker.png",
  302. wield_image = "advtrains_trackworker.png",
  303. stack_max = 1,
  304. on_place = function(itemstack, placer, pointed_thing)
  305. local name = placer:get_player_name()
  306. if not name then
  307. return
  308. end
  309. local has_aux1_down = placer:get_player_control().aux1
  310. if pointed_thing.type=="node" then
  311. local pos=pointed_thing.under
  312. if not advtrains.check_track_protection(pos, name) then
  313. return
  314. end
  315. local node=minetest.get_node(pos)
  316. --if not advtrains.is_track_and_drives_on(minetest.get_node(pos).name, advtrains.all_tracktypes) then return end
  317. local nnprefix, suffix, rotation=string.match(node.name, "^(.+)_([^_]+)(_[^_]+)$")
  318. --atdebug(node.name.."\npattern recognizes:"..nnprefix.." / "..suffix.." / "..rotation)
  319. --atdebug("nntab: ",tp.tracks[nnprefix])
  320. if not tp.tracks[nnprefix] or not tp.tracks[nnprefix].twrotate[suffix] then
  321. nnprefix, suffix=string.match(node.name, "^(.+)_([^_]+)$")
  322. rotation = ""
  323. if not tp.tracks[nnprefix] or not tp.tracks[nnprefix].twrotate[suffix] then
  324. minetest.chat_send_player(placer:get_player_name(), attrans("This node can't be rotated using the trackworker!"))
  325. return
  326. end
  327. end
  328. -- check if the node is modify-protected
  329. if advtrains.is_track_and_drives_on(minetest.get_node(pos).name, advtrains.all_tracktypes) then
  330. -- is a track, we can query
  331. local can_modify, reason = advtrains.can_dig_or_modify_track(pos)
  332. if not can_modify then
  333. local str = attrans("This track can not be rotated!")
  334. if reason then
  335. str = str .. " " .. reason
  336. end
  337. minetest.chat_send_player(placer:get_player_name(), str)
  338. return
  339. end
  340. end
  341. if has_aux1_down then
  342. --feature: flip the node by 180°
  343. --i've always wanted this!
  344. advtrains.ndb.swap_node(pos, {name=node.name, param2=(node.param2+2)%4})
  345. return
  346. end
  347. local modext=tp.tracks[nnprefix].twrotate[suffix]
  348. if rotation==modext[#modext] then --increase param2
  349. advtrains.ndb.swap_node(pos, {name=nnprefix.."_"..suffix..modext[1], param2=(node.param2+1)%4})
  350. return
  351. else
  352. local modpos
  353. for k,v in pairs(modext) do
  354. if v==rotation then modpos=k end
  355. end
  356. if not modpos then
  357. minetest.chat_send_player(placer:get_player_name(), attrans("This node can't be rotated using the trackworker!"))
  358. return
  359. end
  360. advtrains.ndb.swap_node(pos, {name=nnprefix.."_"..suffix..modext[modpos+1], param2=node.param2})
  361. end
  362. end
  363. end,
  364. on_use=function(itemstack, user, pointed_thing)
  365. local name = user:get_player_name()
  366. if not name then
  367. return
  368. end
  369. if pointed_thing.type=="node" then
  370. local pos=pointed_thing.under
  371. local node=minetest.get_node(pos)
  372. if not advtrains.check_track_protection(pos, name) then
  373. return
  374. end
  375. --if not advtrains.is_track_and_drives_on(minetest.get_node(pos).name, advtrains.all_tracktypes) then return end
  376. if advtrains.get_train_at_pos(pos) then return end
  377. local nnprefix, suffix, rotation=string.match(node.name, "^(.+)_([^_]+)(_[^_]+)$")
  378. --atdebug(node.name.."\npattern recognizes:"..nodeprefix.." / "..railtype.." / "..rotation)
  379. if not tp.tracks[nnprefix] or not tp.tracks[nnprefix].twcycle[suffix] then
  380. nnprefix, suffix=string.match(node.name, "^(.+)_([^_]+)$")
  381. rotation = ""
  382. if not tp.tracks[nnprefix] or not tp.tracks[nnprefix].twcycle[suffix] then
  383. minetest.chat_send_player(user:get_player_name(), attrans("This node can't be changed using the trackworker!"))
  384. return
  385. end
  386. end
  387. -- check if the node is modify-protected
  388. if advtrains.is_track_and_drives_on(minetest.get_node(pos).name, advtrains.all_tracktypes) then
  389. -- is a track, we can query
  390. local can_modify, reason = advtrains.can_dig_or_modify_track(pos)
  391. if not can_modify then
  392. local str = attrans("This track can not be changed!")
  393. if reason then
  394. str = str .. " " .. reason
  395. end
  396. minetest.chat_send_player(user:get_player_name(), str)
  397. return
  398. end
  399. end
  400. local nextsuffix=tp.tracks[nnprefix].twcycle[suffix]
  401. advtrains.ndb.swap_node(pos, {name=nnprefix.."_"..nextsuffix..rotation, param2=node.param2})
  402. else
  403. atprint(name, dump(tp.tracks))
  404. end
  405. end,
  406. })
  407. --putting into right place
  408. advtrains.trackplacer=tp