atc_rail.lua 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282
  1. -- atc_rail.lua
  2. -- registers and handles the ATC rail. Active component.
  3. -- This is the only component that can interface with trains, so train interface goes here too.
  4. --Using subtable
  5. local r={}
  6. -- Note on appr_internal:
  7. -- The Approach callback is a special corner case: the train is not on the node, and it is executed synchronized
  8. -- (in the train step right during LZB traversal). We therefore need access to the train id and the lzbdata table
  9. function r.fire_event(pos, evtdata, appr_internal)
  10. local ph=minetest.pos_to_string(pos)
  11. local railtbl = atlatc.active.nodes[ph]
  12. if not railtbl then
  13. atwarn("LuaATC interface rail at",ph,": Data not in memory! Please visit position and click 'Save'!")
  14. return
  15. end
  16. --prepare ingame API for ATC. Regenerate each time since pos needs to be known
  17. --If no train, then return false.
  18. -- try to get the train from the event data
  19. -- This workaround is required because the callback is one step delayed, and a fast train may have already left the node.
  20. -- Also used for approach callback
  21. local train_id = evtdata._train_id
  22. local atc_arrow = evtdata._train_arrow
  23. local train, tvel
  24. if train_id then
  25. train=advtrains.trains[train_id]
  26. -- speed
  27. tvel=train.velocity
  28. -- if still no train_id available, try to get the train at my position
  29. else
  30. train_id=advtrains.get_train_at_pos(pos)
  31. if train_id then
  32. train=advtrains.trains[train_id]
  33. advtrains.train_ensure_init(train_id, train)
  34. -- look up atc_arrow
  35. local index = advtrains.path_lookup(train, pos)
  36. atc_arrow = (train.path_cn[index] == 1)
  37. -- speed
  38. tvel=train.velocity
  39. end
  40. end
  41. local customfct={
  42. atc_send = function(cmd)
  43. if not train_id then return false end
  44. assertt(cmd, "string")
  45. advtrains.atc.train_set_command(train, cmd, atc_arrow)
  46. return true
  47. end,
  48. split_at_index = function(index, cmd)
  49. if not train_id then return false end
  50. assertt(cmd, "string")
  51. if type(index) ~= "number" or index < 2 then
  52. return false
  53. end
  54. local new_id = advtrains.split_train_at_index(train, index)
  55. if new_id then
  56. minetest.after(1,advtrains.atc.train_set_command,advtrains.trains[new_id], cmd, atc_arrow)
  57. return true
  58. end
  59. return false
  60. end,
  61. split_at_fc = function(cmd, len)
  62. assertt(cmd, "string")
  63. if not train_id then return false end
  64. local new_id, fc = advtrains.split_train_at_fc(train, false, len)
  65. if new_id then
  66. minetest.after(1,advtrains.atc.train_set_command,advtrains.trains[new_id], cmd, atc_arrow)
  67. end
  68. return fc or ""
  69. end,
  70. split_off_locomotive = function(cmd, len)
  71. assertt(cmd, "string")
  72. if not train_id then return false end
  73. local new_id, fc = advtrains.split_train_at_fc(train, true, len)
  74. if new_id then
  75. minetest.after(1,advtrains.atc.train_set_command,advtrains.trains[new_id], cmd, atc_arrow)
  76. end
  77. end,
  78. train_length = function ()
  79. if not train_id then return false end
  80. return #train.trainparts
  81. end,
  82. step_fc = function()
  83. if not train_id then return false end
  84. advtrains.train_step_fc(train)
  85. end,
  86. get_fc = function()
  87. if not train_id then return end
  88. local fc_list = {}
  89. for index,wagon_id in ipairs(train.trainparts) do
  90. fc_list[index] = table.concat(advtrains.wagons[wagon_id].fc,"!") or ""
  91. end
  92. return fc_list
  93. end,
  94. set_fc = function(fc_list)
  95. assertt(fc_list, "table")
  96. if not train_id then return false end
  97. -- safety type-check for entered values
  98. for _,v in ipairs(fc_list) do
  99. if v and type(v) ~= "string" then
  100. error("FC entries must be a string")
  101. return
  102. end
  103. end
  104. for index,wagon_id in ipairs(train.trainparts) do
  105. if fc_list[index] then -- has FC to enter to this wagon
  106. local data = advtrains.wagons[wagon_id]
  107. if data then -- wagon actually exists
  108. for _,wagon in pairs(minetest.luaentities) do -- find wagon entity
  109. if wagon.is_wagon and wagon.initialized and wagon.id==wagon_id then
  110. wagon.set_fc(data,fc_list[index]) -- overwrite to new FC
  111. break -- no point cycling through every other entity. we found our wagon
  112. end
  113. end
  114. end
  115. end
  116. end
  117. end,
  118. set_shunt = function()
  119. -- enable shunting mode
  120. if not train_id then return false end
  121. train.is_shunt = true
  122. end,
  123. unset_shunt = function()
  124. if not train_id then return false end
  125. train.is_shunt = nil
  126. end,
  127. set_autocouple = function ()
  128. if not train_id then return false end
  129. train.autocouple = true
  130. end,
  131. unset_autocouple = function ()
  132. if not train_id then return false end
  133. train.autocouple = nil
  134. end,
  135. set_line = function(line)
  136. if type(line)~="string" and type(line)~="number" then
  137. return false
  138. end
  139. train.line = line .. ""
  140. minetest.after(0, advtrains.invalidate_path, train_id)
  141. return true
  142. end,
  143. get_line = function()
  144. return train.line
  145. end,
  146. set_rc = function(rc)
  147. if type(rc)~="string"then
  148. return false
  149. end
  150. train.routingcode = rc
  151. minetest.after(0, advtrains.invalidate_path, train_id)
  152. return true
  153. end,
  154. get_rc = function()
  155. return train.routingcode
  156. end,
  157. atc_reset = function()
  158. if not train_id then return false end
  159. advtrains.atc.train_reset_command(train)
  160. return true
  161. end,
  162. atc_arrow = atc_arrow,
  163. atc_id = train_id,
  164. atc_speed = tvel,
  165. atc_set_text_outside = function(text)
  166. if not train_id then return false end
  167. if text then assertt(text, "string") end
  168. advtrains.trains[train_id].text_outside=text
  169. return true
  170. end,
  171. atc_set_text_inside = function(text)
  172. if not train_id then return false end
  173. if text then assertt(text, "string") end
  174. advtrains.trains[train_id].text_inside=text
  175. return true
  176. end,
  177. atc_get_text_outside = function()
  178. if not train_id then return false end
  179. return advtrains.trains[train_id].text_outside
  180. end,
  181. atc_get_text_inside = function(text)
  182. if not train_id then return false end
  183. return advtrains.trains[train_id].text_inside
  184. end,
  185. atc_set_lzb_tsr = function(speed)
  186. if not appr_internal then
  187. error("atc_set_lzb_tsr() can only be used during 'approach' events!")
  188. end
  189. assert(tonumber(speed), "Number expected!")
  190. local index = appr_internal.index
  191. advtrains.lzb_add_checkpoint(train, index, speed, nil)
  192. return true
  193. end,
  194. }
  195. -- interlocking specific
  196. if advtrains.interlocking then
  197. customfct.atc_set_ars_disable = function(value)
  198. advtrains.interlocking.ars_set_disable(train, value)
  199. end
  200. end
  201. atlatc.active.run_in_env(pos, evtdata, customfct)
  202. end
  203. advtrains.register_tracks("default", {
  204. nodename_prefix="advtrains_luaautomation:dtrack",
  205. texture_prefix="advtrains_dtrack_atc",
  206. models_prefix="advtrains_dtrack",
  207. models_suffix=".b3d",
  208. shared_texture="advtrains_dtrack_shared_atc.png",
  209. description=atltrans("LuaATC Rail"),
  210. formats={},
  211. get_additional_definiton = function(def, preset, suffix, rotation)
  212. return {
  213. after_place_node = atlatc.active.after_place_node,
  214. after_dig_node = atlatc.active.after_dig_node,
  215. on_receive_fields = function(pos, ...)
  216. atlatc.active.on_receive_fields(pos, ...)
  217. --set arrowconn (for ATC)
  218. local ph=minetest.pos_to_string(pos)
  219. local _, conns=advtrains.get_rail_info_at(pos, advtrains.all_tracktypes)
  220. local nodeent = atlatc.active.nodes[ph]
  221. if nodeent then
  222. nodeent.arrowconn=conns[1].c
  223. end
  224. end,
  225. advtrains = {
  226. on_train_enter = function(pos, train_id, train, index)
  227. --do async. Event is fired in train steps
  228. atlatc.interrupt.add(0, pos, {type="train", train=true, id=train_id,
  229. _train_id = train_id, _train_arrow = (train.path_cn[index] == 1)})
  230. end,
  231. on_train_approach = function(pos, train_id, train, index, has_entered, lzbdata)
  232. -- Insert an event only if the rail indicated that it supports approach callbacks
  233. local ph=minetest.pos_to_string(pos)
  234. local railtbl = atlatc.active.nodes[ph]
  235. -- uses a "magic variable" in the local environment of the node
  236. -- This hack is necessary because code might not be prepared to get approach events...
  237. if railtbl and railtbl.data and railtbl.data.__approach_callback_mode then
  238. local acm = railtbl.data.__approach_callback_mode
  239. local in_arrow = (train.path_cn[index] == 1)
  240. if acm==2 or (acm==1 and in_arrow) then
  241. local evtdata = {type="approach", approach=true, id=train_id, has_entered = has_entered,
  242. _train_id = train_id, _train_arrow = in_arrow} -- reuses code from train_enter
  243. -- This event is *required* to run synchronously, because it might set the ars_disable flag on the train and add LZB checkpoints,
  244. -- although this is generally discouraged because this happens right in a train step
  245. -- At this moment, I am not aware whether this may cause side effects, and I must encourage users not to do expensive calculations here.
  246. r.fire_event(pos, evtdata, {train_id = train_id, train = train, index = index, lzbdata = lzbdata})
  247. end
  248. end
  249. end,
  250. },
  251. luaautomation = {
  252. fire_event=r.fire_event
  253. },
  254. digiline = {
  255. receptor = {},
  256. effector = {
  257. action = atlatc.active.on_digiline_receive
  258. },
  259. },
  260. }
  261. end,
  262. }, advtrains.trackpresets.t_30deg_straightonly)
  263. atlatc.rail = r