aboutsummaryrefslogtreecommitdiff
path: root/advtrains_luaautomation/atc_rail.lua
blob: bfd7357903b9c32249760a3045704615cef28acc (plain)
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
-- atc_rail.lua
-- registers and handles the ATC rail. Active component.
-- This is the only component that can interface with trains, so train interface goes here too.

--Using subtable
local r={}

-- Note on appr_internal:
-- The Approach callback is a special corner case: the train is not on the node, and it is executed synchronized 
-- (in the train step right during LZB traversal). We therefore need access to the train id and the lzbdata table
function r.fire_event(pos, evtdata, appr_internal)
	
	local ph=minetest.pos_to_string(pos)
	local railtbl = atlatc.active.nodes[ph]
	
	if not railtbl then
		atwarn("LuaATC interface rail at",ph,": Data not in memory! Please visit position and click 'Save'!")
		return
	end
	
	--prepare ingame API for ATC. Regenerate each time since pos needs to be known
	--If no train, then return false.
	
	-- try to get the train from the event data
	-- This workaround is required because the callback is one step delayed, and a fast train may have already left the node.
	-- Also used for approach callback
	local train_id = evtdata._train_id
	local atc_arrow = evtdata._train_arrow
	local train, tvel
	
	if train_id then
		train=advtrains.trains[train_id]
		-- speed
		tvel=train.velocity
	-- if still no train_id available, try to get the train at my position
	else
		train_id=advtrains.get_train_at_pos(pos)
		if train_id then
			train=advtrains.trains[train_id]
			advtrains.train_ensure_init(train_id, train)
			-- look up atc_arrow
			local index = advtrains.path_lookup(train, pos)
			atc_arrow = (train.path_cn[index] == 1)
			-- speed
			tvel=train.velocity
		end
	end
	
	local customfct={
		atc_send = function(cmd)
			if not train_id then return false end
			assertt(cmd, "string")
			advtrains.atc.train_set_command(train, cmd, atc_arrow)
			return true
		end,
		split_at_index = function(index, cmd)
			if not train_id then return false end
			assertt(cmd, "string")
			if type(index) ~= "number" then
				return false
			end
			local new_id = advtrains.split_train_at_index(train, index)
			if new_id then
				minetest.after(1,advtrains.atc.train_set_command,advtrains.trains[new_id], cmd, atc_arrow)
				return true
			end
			return false
		end,
		split_at_fc = function(cmd, len)
			assertt(cmd, "string")
			if not train_id then return false end
			local new_id, fc = advtrains.split_train_at_fc(train, false, len)
			if new_id then
				minetest.after(1,advtrains.atc.train_set_command,advtrains.trains[new_id], cmd, atc_arrow)
			end
			return fc or ""
		end,
		split_off_locomotive = function(cmd, len)
			assertt(cmd, "string")
			if not train_id then return false end
			local new_id, fc = advtrains.split_train_at_fc(train, true, len)
			if new_id then
				minetest.after(1,advtrains.atc.train_set_command,advtrains.trains[new_id], cmd, atc_arrow)
			end						
		end,
		train_length = function ()
			if not train_id then return false end
			return #train.trainparts
		end,
		step_fc = function()
			if not train_id then return false end
			advtrains.train_step_fc(train)
		end,
		set_shunt = function()
			-- enable shunting mode
			if not train_id then return false end
			train.is_shunt = true
		end,
		unset_shunt = function()
			if not train_id then return false end
			train.is_shunt = nil
		end,
		set_autocouple = function ()
			if not train_id then return false end
			train.autocouple = true			
		end,
		unset_autocouple = function ()
			if not train_id then return false end
			train.autocouple = nil
 		end,
		set_line = function(line)
			if type(line)~="string" and type(line)~="number" then
				return false
			end
			train.line = line .. ""
			minetest.after(0, advtrains.invalidate_path, train_id)
			return true
		end,
		get_line = function()
			return train.line
		end,
		set_rc = function(rc)
			if type(rc)~="string"then
				return false
			end
			train.routingcode = rc
			minetest.after(0, advtrains.invalidate_path, train_id)
			return true
		end,
		get_rc = function()
			return train.routingcode
		end,
		atc_reset = function(cmd)
			if not train_id then return false end
			assertt(cmd, "string")
			advtrains.atc.train_reset_command(train)
			return true
		end,
		atc_arrow = atc_arrow,
		atc_id = train_id,
		atc_speed = tvel,
		atc_set_text_outside = function(text)
			if not train_id then return false end
			if text then assertt(text, "string") end
			advtrains.trains[train_id].text_outside=text
			return true
		end,
		atc_set_text_inside = function(text)
			if not train_id then return false end
			if text then assertt(text, "string") end
			advtrains.trains[train_id].text_inside=text
			return true
		end,
		atc_get_text_outside = function()
			if not train_id then return false end
			return advtrains.trains[train_id].text_outside
		end,
		atc_get_text_inside = function(text)
			if not train_id then return false end
			return advtrains.trains[train_id].text_inside
		end,
		atc_set_lzb_tsr = function(speed)
			if not appr_internal then
				error("atc_set_lzb_tsr() can only be used during 'approach' events!")
			end
			assert(tonumber(speed), "Number expected!")
			
			local index = appr_internal.index
			advtrains.lzb_add_checkpoint(train, index, speed, nil)
			
			return true
		end,
	}
	-- interlocking specific
	if advtrains.interlocking then
		customfct.atc_set_ars_disable = function(value)
			advtrains.interlocking.ars_set_disable(train, value)
		end
	end
	
	atlatc.active.run_in_env(pos, evtdata, customfct)
	
end

advtrains.register_tracks("default", {
	nodename_prefix="advtrains_luaautomation:dtrack",
	texture_prefix="advtrains_dtrack_atc",
	models_prefix="advtrains_dtrack",
	models_suffix=".b3d",
	shared_texture="advtrains_dtrack_shared_atc.png",
	description=atltrans("LuaATC Rail"),
	formats={},
	get_additional_definiton = function(def, preset, suffix, rotation)
		return {
			after_place_node = atlatc.active.after_place_node,
			after_dig_node = atlatc.active.after_dig_node,

			on_receive_fields = function(pos, ...)
				atlatc.active.on_receive_fields(pos, ...)
				
				--set arrowconn (for ATC)
				local ph=minetest.pos_to_string(pos)
				local _, conns=advtrains.get_rail_info_at(pos, advtrains.all_tracktypes)
				local nodeent = atlatc.active.nodes[ph]
				if nodeent then
					nodeent.arrowconn=conns[1].c
				end
			end,

			advtrains = {
				on_train_enter = function(pos, train_id, train, index)
					--do async. Event is fired in train steps
					atlatc.interrupt.add(0, pos, {type="train", train=true, id=train_id,
							_train_id = train_id, _train_arrow = (train.path_cn[index] == 1)})
				end,
				on_train_approach = function(pos, train_id, train, index, has_entered, lzbdata)
					-- Insert an event only if the rail indicated that it supports approach callbacks
					local ph=minetest.pos_to_string(pos)
					local railtbl = atlatc.active.nodes[ph]
					-- uses a "magic variable" in the local environment of the node
					-- This hack is necessary because code might not be prepared to get approach events...
					if railtbl and railtbl.data and railtbl.data.__approach_callback_mode then
						local acm = railtbl.data.__approach_callback_mode
						local in_arrow = (train.path_cn[index] == 1)
						if acm==2 or (acm==1 and in_arrow) then
							local evtdata = {type="approach", approach=true, id=train_id, has_entered = has_entered,
									_train_id = train_id, _train_arrow = in_arrow} -- reuses code from train_enter
							-- This event is *required* to run synchronously, because it might set the ars_disable flag on the train and add LZB checkpoints,
							-- although this is generally discouraged because this happens right in a train step
							-- At this moment, I am not aware whether this may cause side effects, and I must encourage users not to do expensive calculations here.
							r.fire_event(pos, evtdata, {train_id = train_id, train = train, index = index, lzbdata = lzbdata})
						end
					end
				end,
			},
			luaautomation = {
				fire_event=r.fire_event
			},
			digiline = {
				receptor = {},
				effector = {
					action = atlatc.active.on_digiline_receive
				},
			},
		}
	end,
}, advtrains.trackpresets.t_30deg_straightonly)


atlatc.rail = r