aboutsummaryrefslogtreecommitdiff
path: root/advtrains_luaautomation/atc_rail.lua
blob: bdb4bcfc428ec06dd043f0fa12c797e3ccc513e2 (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
-- 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("LuaAutomation ATC interface rail at",ph,": Data not in memory! Please visit position and click 'Save'!")
		return
	end
	
	
	local arrowconn = railtbl.arrowconn
	if not arrowconn then
		atwarn("LuaAutomation ATC interface rail at",ph,": Incomplete Data! 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.
	local train_id=advtrains.get_train_at_pos(pos)
	-- 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.
	if not train_id then
		train_id = evtdata._train_id
	end
	local train, atc_arrow, tvel
	if train_id then train=advtrains.trains[train_id] end
	
	if not train and appr_internal then
		-- also use the train when called as an approach callback
		train_id = appr_internal.train_id
		train = appr_internal.train
	end
	
	if train then 
		if not train.path then
			--we happened to get in between an invalidation step
			--delay
			atlatc.interrupt.add(0,pos,evtdata)
			return
		end
		local index = advtrains.path_lookup(train, pos)
				
		local iconnid = 1
		if index then
			iconnid = train.path_cn[index]
		else
			atwarn("ATC rail at", pos, ": Rail not on train's path! Can't determine arrow direction. Assuming +!")
		end
		atc_arrow = iconnid == 1
		
		tvel=train.velocity
	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,
		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_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("LuaAutomation ATC 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)
				atlatc.active.nodes[ph].arrowconn=conns[1].c
			end,

			advtrains = {
				on_train_enter = function(pos, train_id)
					--do async. Event is fired in train steps
					atlatc.interrupt.add(0, pos, {type="train", train=true, id=train_id, _train_id = train_id})
				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
						if acm==2 or (acm==1 and train.path_cn[index] == 1) then
							local evtdata = {type="approach", approach=true, id=train_id, has_entered = has_entered}
							-- 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