aboutsummaryrefslogtreecommitdiff
path: root/advtrains/trainhud.lua
blob: b2744adeddf635ff48f14ed107a743fe53b62632 (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
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
--trainhud.lua: holds all the code for train controlling

local T = advtrains.texture

advtrains.hud = {}
advtrains.hhud = {}

minetest.register_on_leaveplayer(function(player)
advtrains.hud[player:get_player_name()] = nil
advtrains.hhud[player:get_player_name()] = nil
end)

local mletter={[1]="F", [-1]="R", [0]="N"}

function advtrains.on_control_change(pc, train, flip)
   	local maxspeed = train.max_speed or 10
	if pc.sneak then
		if pc.up then
			train.tarvelocity = maxspeed
		end
		if pc.down then
			train.tarvelocity = 0
		end
		if pc.left then
			train.tarvelocity = 4
		end
		if pc.right then
			train.tarvelocity = 8
		end
		--[[if pc.jump then
			train.brake = true
			--0: released, 1: brake and pressed, 2: released and brake, 3: pressed and brake
			if not train.brake_hold_state or train.brake_hold_state==0 then
				train.brake_hold_state = 1
			elseif train.brake_hold_state==2 then
				train.brake_hold_state = 3
			end
		elseif train.brake_hold_state==1 then
			train.brake_hold_state = 2
		elseif train.brake_hold_state==3 then
			train.brake = false
			train.brake_hold_state = 0
		end]]
		--shift+use:see wagons.lua
	else
		local act=false
		if pc.jump then
			train.ctrl_user = 1
			act=true
		end
		-- If atc command set, only "Jump" key can clear command. To prevent accidental control.
		if train.tarvelocity or train.atc_command then
			return
		end
		if pc.up then
		   train.ctrl_user=4
		   act=true
		end
		if pc.down then
			if train.velocity>0 then
				if pc.jump then
					train.ctrl_user = 0
				else
					train.ctrl_user = 2
				end
				act=true
			else
				advtrains.invert_train(train.id)
				advtrains.atc.train_reset_command(train)
			end
		end
		if pc.left then
			if train.door_open ~= 0 then
				train.door_open = 0
			else
				train.door_open = -1
			end
		end
		if pc.right then
			if train.door_open ~= 0 then
				train.door_open = 0
			else
				train.door_open = 1
			end
		end
		if not act then
			train.ctrl_user = nil
		end
	end
end
function advtrains.update_driver_hud(pname, train, flip)
	local inside=train.text_inside or ""
	local ft, ht = advtrains.hud_train_format(train, flip)
	advtrains.set_trainhud(pname, inside.."\n"..ft, ht)
end
function advtrains.clear_driver_hud(pname)
	advtrains.set_trainhud(pname, "")
end

function advtrains.set_trainhud(name, text, driver)
	local hud = advtrains.hud[name]
	local player=minetest.get_player_by_name(name)
	if not player then
	   return
	end
	local driverhud = {
		hud_elem_type = "image",
		name = "ADVTRAINS_DRIVER",
		position = {x=0.5, y=1},
		offset = {x=0,y=-170},
		text = driver or "",
		alignment = {x=0,y=-1},
		scale = {x=1,y=1},}
	if not hud then
		hud = {["driver"]={}}
		advtrains.hud[name] = hud
		hud.id = player:hud_add({
			hud_elem_type = "text",
			name = "ADVTRAINS",
			number = 0xFFFFFF,
			position = {x=0.5, y=1},
			offset = {x=0, y=-300},
			text = text,
			scale = {x=200, y=60},
			alignment = {x=0, y=-1},
		})
		hud.oldText=text
		hud.driver = player:hud_add(driverhud)
	else
		if hud.oldText ~= text then
			player:hud_change(hud.id, "text", text)
			hud.oldText=text
		end
		if hud.driver then
			player:hud_change(hud.driver, "text", driver or "")
		elseif driver then
			hud.driver = player:hud_add(driverhud)
		end
	end
end

function advtrains.set_help_hud(name, text)
	local hud = advtrains.hhud[name]
	local player=minetest.get_player_by_name(name)
	if not player then
	   return
	end
	if not hud then
		hud = {}
		advtrains.hhud[name] = hud
		hud.id = player:hud_add({
			hud_elem_type = "text",
			name = "ADVTRAINS_HELP",
			number = 0xFFFFFF,
			position = {x=1, y=0.3},
			offset = {x=0, y=0},
			text = text,
			scale = {x=200, y=60},
			alignment = {x=1, y=0},
		})
		hud.oldText=text
		return
	elseif hud.oldText ~= text then
		player:hud_change(hud.id, "text", text)
		hud.oldText=text
	end
end

--train.lever:
--Speed control lever in train, for new train control system.
--[[
Value	Disp	Control	Meaning
0		BB		S+Space	Emergency Brake
1		B		Space	Normal Brake
2		-		S		Roll
3		o		<none>	Stay at speed
4		+		W		Accelerate
]]

function advtrains.hud_train_format(train, flip)
	if not train then return "","" end
	local sformat = string.format -- this appears to be faster than (...):format
	
	local max = train.max_speed or 10
	local res = train.speed_restriction
	local vel = advtrains.abs_ceil(train.velocity)
	local vel_kmh=advtrains.abs_ceil(advtrains.ms_to_kmh(train.velocity))
	
	local tlev=train.lever or 3
	if train.velocity==0 and not train.active_control then tlev=1 end
	if train.hud_lzb_effect_tmr then
		tlev=1
	end
	
	local hud = T.combine(440, 110, "black")
	local st = {}
	if train.debug then st = {train.debug} end
	
	-- lever
	hud:add_multicolor_fill_topdown(275, 10, 5, 90, 1, "cyan", 1, "white", 2, "orange", 1, "red")
	hud:add_lever_topdown(280, 10, 30, 90, 18, 6, (4-tlev)/4, "gray", "darkslategray")
	-- reverser
	hud:add(245, 10, T"advtrains_hud_arrow.png":transform"FY":multiply(flip and "gray" or "cyan"))
	hud:add(245, 85, T"advtrains_hud_arrow.png":multiply(flip and "orange" or "gray"))
	hud:add_lever_topdown(240, 30, 25, 50, 15, 5, flip and 1 or 0, "gray", "darkslategray")
	-- train control/safety indication
	hud:add(10, 10, T"advtrains_hud_atc.png":resize(30, 30):multiply((train.tarvelocity or train.atc_command) and "cyan" or "darkslategray"))
	hud:add(50, 10, T"advtrains_hud_lzb.png":resize(30, 30):multiply(train.hud_lzb_effect_tmr and "red" or "darkslategray"))
	hud:add(90, 10, T"advtrains_hud_shunt.png":resize(30, 30):multiply(train.is_shunt and "orange" or "darkslategray"))
	-- door
	hud:add_fill(187, 10, 26, 30, "white"):add_fill(189, 12, 22, 11, "black")
	hud:add_fill(170, 10, 15, 30, train.door_open==-1 and "white" or "darkslategray"):add_fill(172, 12, 11, 11, "black")
	hud:add_fill(215, 10, 15, 30, train.door_open==1 and "white" or "darkslategray"):add_fill(217, 12, 11, 11, "black")
	-- speed indication(s)
	hud:add_n7seg(320, 10, 110, 90, vel, 2, "red")
	hud:add_segmentbar_leftright(10, 65, 217, 20, 3, 20, max, 20, "darkslategray", 0, vel, "white")
	if res and res > 0 then
		hud:add_fill(7+res*11, 60, 3, 30, "red")
	end
	if train.tarvelocity then
		hud:add(1+train.tarvelocity*11, 85, T"advtrains_hud_arrow.png":transform"FY":multiply"cyan")
	end
	local lzbdisp
	local lzb = train.lzb
	if lzb and lzb.checkpoints then
		local oc = lzb.checkpoints
		for i = 1, #oc do
			if advtrains.interlocking then
				local udata = oc[i].udata
				if udata and udata.signal_pos then
					local sigd = advtrains.interlocking.db.get_sigd_for_signal(udata.signal_pos)
					if sigd then
						local tcbs = advtrains.interlocking.db.get_tcbs(sigd) or {}
						if tcbs.route_rsn then
							table.insert(st, ("%s: %s"):format(minetest.pos_to_string(sigd.p), tcbs.route_rsn))
						end
					end
				end
			end
			local spd = oc[i].speed
			spd = advtrains.speed.min(spd, train.speed_restriction)
			if spd == -1 then spd = nil end
			local c = not spd and "lime" or (type(spd) == "number" and (spd == 0) and "red" or "orange") or nil
			if c then
				if spd and spd~=0 then
					hud:add(1+spd*11, 50, T"advtrains_hud_arrow.png":multiply"red")
				end
				local dist = math.floor(((oc[i].index or train.index)-train.index))
				dist = math.max(0, math.min(999, dist))
				lzbdisp = {c = c, d = dist}
				break
			end
		end
	end
	if not lzbdisp then
		lzbdisp = {c = "darkslategray", d = 888}
	end
	hud:add_fill(130, 10, 30, 5, lzbdisp.c)
	hud:add_fill(130, 35, 30, 5, lzbdisp.c)
	hud:add_n7seg(131, 18, 28, 14, lzbdisp.d, 3, lzbdisp.c)
	
	if res and res == 0 then
		table.insert(st, attrans("OVERRUN RED SIGNAL! Examine situation and reverse train to move again."))
	end
	
	if train.atc_command then
		table.insert(st, ("ATC: %s%s"):format(train.atc_delay and advtrains.abs_ceil(train.atc_delay).."s " or "", train.atc_command or ""))
	end
	
	return table.concat(st,"\n"), tostring(hud)
end

local _, texture = advtrains.hud_train_format { -- dummy train object to demonstrate the train hud
	max_speed = 15, speed_restriction = 15, velocity = 15, tarvelocity = 12,
	active_control = true, lever = 3, ctrl = {lzb = true}, is_shunt = true,
	door_open = 1, lzb = {checkpoints = {{speed=6, index=125.7}}}, index = 100,
}

minetest.register_node("advtrains:hud_demo",{
	description = "Train HUD demonstration",
	tiles = {texture},
	groups = {cracky = 3, not_in_creative_inventory = 1}
})

minetest.register_craft {
	output = "advtrains:hud_demo",
	recipe = {
		{"default:paper", "default:paper", "default:paper"},
		{"default:paper", "advtrains:trackworker", "default:paper"},
		{"default:paper", "default:paper", "default:paper"},
	}
}
class="hl kwa">end dofile(advtrains.modpath.."/lzb.lua") --load/save -- backup variables, used if someone should accidentally delete a sub-mod -- As of version 4, only used once during migration from version 3 to 4 -- Since version 4, each of the mods stores a separate save file. local MDS_interlocking, MDS_lines advtrains.fpath=minetest.get_worldpath().."/advtrains" dofile(advtrains.modpath.."/log.lua") function advtrains.read_component(name) local path = advtrains.fpath.."_"..name minetest.log("action", "[advtrains] loading "..path) local file, err = io.open(path, "r") if not file then minetest.log("warning", " Failed to read advtrains save data from file "..path..": "..(err or "Unknown Error")) minetest.log("warning", " (this is normal when first enabling advtrains on this world)") return end local tbl = minetest.deserialize(file:read("*a")) file:close() return tbl end function advtrains.avt_load() -- check for new, split advtrains save file local version = advtrains.read_component("version") local tbl if version and version == 4 then advtrains.load_version_4() return -- NOTE: From here, legacy loading code! elseif version and version == 3 then -- we are dealing with the split-up system minetest.log("action", "[advtrains] loading savefiles version 3") local il_save = { tcbs = true, ts = true, signalass = true, rs_locks = true, rs_callbacks = true, influence_points = true, npr_rails = true, } tbl={ trains = true, wagon_save = true, ptmap = true, atc = true, ndb = true, lines = true, version = 2, } for i,k in pairs(il_save) do il_save[i] = advtrains.read_component("interlocking_"..i) end for i,k in pairs(tbl) do tbl[i] = advtrains.read_component(i) end tbl["interlocking"] = il_save else local file, err = io.open(advtrains.fpath, "r") if not file then minetest.log("warning", " Failed to read advtrains save data from file "..advtrains.fpath..": "..(err or "Unknown Error")) minetest.log("warning", " (this is normal when first enabling advtrains on this world)") return else tbl = minetest.deserialize(file:read("*a")) file:close() end end if type(tbl) == "table" then if tbl.version then --congrats, we have the new save format. advtrains.trains = tbl.trains --Save the train id into the train table to avoid having to pass id around for id, train in pairs(advtrains.trains) do train.id = id end advtrains.wagons = tbl.wagon_save advtrains.player_to_train_mapping = tbl.ptmap or {} advtrains.ndb.load_data_pre_v4(tbl.ndb) advtrains.atc.load_data(tbl.atc) if advtrains.interlocking then advtrains.interlocking.db.load(tbl.interlocking) else MDS_interlocking = tbl.interlocking end if advtrains.lines then advtrains.lines.load(tbl.lines) else MDS_lines = tbl.lines end --remove wagon_save entries that are not part of a train local todel=advtrains.merge_tables(advtrains.wagon_save) for tid, train in pairs(advtrains.trains) do train.id = tid for _, wid in ipairs(train.trainparts) do todel[wid]=nil end end for wid, _ in pairs(todel) do atwarn("Removing unused wagon", wid, "from wagon_save table.") advtrains.wagon_save[wid]=nil end else --oh no, its the old one... advtrains.trains=tbl --load ATC advtrains.fpath_atc=minetest.get_worldpath().."/advtrains_atc" local file, err = io.open(advtrains.fpath_atc, "r") if not file then local er=err or "Unknown Error" atprint("Failed loading advtrains atc save file "..er) else local tbl = minetest.deserialize(file:read("*a")) if type(tbl) == "table" then advtrains.atc.controllers=tbl.controllers end file:close() end --load wagon saves advtrains.fpath_ws=minetest.get_worldpath().."/advtrains_wagon_save" local file, err = io.open(advtrains.fpath_ws, "r") if not file then local er=err or "Unknown Error" atprint("Failed loading advtrains save file "..er) else local tbl = minetest.deserialize(file:read("*a")) if type(tbl) == "table" then advtrains.wagon_save=tbl end file:close() end end else minetest.log("error", " Failed to deserialize advtrains save data: Not a table!") end -- moved from advtrains.load() atlatc.load_pre_v4() -- end of legacy loading code end function advtrains.load_version_4() minetest.log("action", "[advtrains] loading savefiles version 4 (serialize_lib)") --== load core == local at_save = serialize_lib.load_atomic(advtrains.fpath.."_core.ls") if at_save then advtrains.trains = at_save.trains --Save the train id into the train table to avoid having to pass id around for id, train in pairs(advtrains.trains) do train.id = id end advtrains.wagons = at_save.wagons advtrains.player_to_train_mapping = at_save.ptmap or {} advtrains.atc.load_data(at_save.atc) --remove wagon_save entries that are not part of a train local todel=advtrains.merge_tables(advtrains.wagon_save) for tid, train in pairs(advtrains.trains) do train.id = tid for _, wid in ipairs(train.trainparts) do todel[wid]=nil end end for wid, _ in pairs(todel) do atwarn("Removing unused wagon", wid, "from wagon_save table.") advtrains.wagon_save[wid]=nil end end --== load ndb serialize_lib.load_atomic(advtrains.fpath.."_ndb4.ls", advtrains.ndb.load_callback) --== load interlocking == if advtrains.interlocking then local il_save = serialize_lib.load_atomic(advtrains.fpath.."_interlocking.ls") if il_save then advtrains.interlocking.db.load(il_save) end end --== load lines == if advtrains.lines then local ln_save = serialize_lib.load_atomic(advtrains.fpath.."_lines.ls") if ln_save then advtrains.lines.load(ln_save) end end --== load luaatc == if atlatc then local la_save = serialize_lib.load_atomic(advtrains.fpath.."_atlatc.ls") if la_save then atlatc.load(la_save) end end end advtrains.save_component = function (tbl, name) -- Saves each component of the advtrains file separately -- -- required for now to shrink the advtrains db to overcome lua -- limitations. -- Note: as of version 4, only used for the "advtrains_version" file local datastr = minetest.serialize(tbl) if not datastr then minetest.log("error", " Failed to serialize advtrains save data!") return end local path = advtrains.fpath.."_"..name local success = minetest.safe_file_write(path, datastr) if not success then minetest.log("error", " Failed to write advtrains save data to file "..path) end end advtrains.avt_save = function(remove_players_from_wagons) --atdebug("Saving advtrains files (version 4)") if remove_players_from_wagons then for w_id, data in pairs(advtrains.wagons) do data.seatp={} end advtrains.player_to_train_mapping={} end local tmp_trains={} for id, train in pairs(advtrains.trains) do --first, deep_copy the train if #train.trainparts > 0 then local v=advtrains.save_keys(train, { "last_pos", "last_connid", "last_frac", "velocity", "tarvelocity", "trainparts", "recently_collided_with_env", "atc_brake_target", "atc_wait_finish", "atc_command", "atc_delay", "door_open", "text_outside", "text_inside", "line", "routingcode", "il_sections", "speed_restriction", "speed_restrictions_t", "is_shunt", "points_split", "autocouple", "atc_wait_autocouple", "ars_disable", }) --then save it tmp_trains[id]=v else atwarn("Train",id,"had no wagons left because of some bug. It is being deleted. Wave it goodbye!") advtrains.remove_train(id) end end for id, wdata in pairs(advtrains.wagons) do local _,proto = advtrains.get_wagon_prototype(wdata) if proto.has_inventory then local inv=minetest.get_inventory({type="detached", name="advtrains_wgn_"..id}) if inv then -- inventory is not initialized when wagon was never loaded -- TOOD: What happens with unloading rails when they don't find the inventory? wdata.ser_inv=advtrains.serialize_inventory(inv) end end -- TODO apply save-keys here too -- TODO temp wdata.dcpl_lock = nil end --versions: -- 1 - Initial new save format. -- 2 - version as of tss branch 11-2018+ -- 3 - split-up savefile system by gabriel -- 4 - serialize_lib -- save of core advtrains local at_save={ trains = tmp_trains, wagons = advtrains.wagons, ptmap = advtrains.player_to_train_mapping, atc = advtrains.atc.save_data(), } --save of interlocking local il_save if advtrains.interlocking then il_save = advtrains.interlocking.db.save() else il_save = MDS_interlocking end -- save of lines local ln_save if advtrains.lines then ln_save = advtrains.lines.save() else ln_save = MDS_lines end -- save of luaatc local la_save if atlatc then la_save = atlatc.save() end -- parts table for serialize_lib API: -- any table that is nil will not be included and thus not be overwritten local parts_table = { ["core.ls"] = at_save, ["interlocking.ls"] = il_save, ["lines.ls"] = ln_save, ["atlatc.ls"] = la_save, ["ndb4.ls"] = true, -- data not used } local callbacks_table = { ["ndb4.ls"] = advtrains.ndb.save_callback } if DUMP_DEBUG_SAVE then local file, err = io.open(advtrains.fpath.."_DUMP", "w") if err then return end file:write(dump(parts_table)) file:close() end --THE MAGIC HAPPENS HERE local succ, err = serialize_lib.save_atomic_multiple(parts_table, advtrains.fpath.."_", callbacks_table) if not succ then atwarn("Saving failed: "..err) else -- store version advtrains.save_component(4, "version") end end --## MAIN LOOP ##-- --Calls all subsequent main tasks of both advtrains and atlatc local init_load=false local save_timer = advtrains.SAVE_INTERVAL advtrains.mainloop_runcnt=0 advtrains.global_slowdown = 1 local t = 0 local within_mainstep = false minetest.register_globalstep(function(dtime_mt) if no_action then -- the advtrains globalstep is skipped by command. Return immediately return end within_mainstep = true advtrains.mainloop_runcnt=advtrains.mainloop_runcnt+1 --atprint("Running the main loop, runcnt",advtrains.mainloop_runcnt) --call load once. see advtrains.load() comment if not init_load then advtrains.load() end local dtime = dtime_mt * advtrains.global_slowdown if GENERATE_ATRICIFIAL_LAG then dtime = HOW_MANY_LAG if os.clock()<t then within_mainstep = false return end t = os.clock()+HOW_MANY_LAG end -- if dtime is too high, decrease global slowdown if advtrains.DTIME_LIMIT~=0 then if dtime > advtrains.DTIME_LIMIT then if advtrains.global_slowdown > 0.1 then advtrains.global_slowdown = advtrains.global_slowdown - 0.05 else advtrains.global_slowdown = advtrains.global_slowdown / 2 end dtime = advtrains.DTIME_LIMIT end -- recover global slowdown slowly over time advtrains.global_slowdown = math.min(advtrains.global_slowdown*1.02, 1) end advtrains.mainloop_trainlogic(dtime,advtrains.mainloop_runcnt) if advtrains_itm_mainloop then advtrains_itm_mainloop(dtime) end if atlatc then --atlatc.mainloop_stepcode(dtime) atlatc.interrupt.mainloop(dtime) end if advtrains.lines then advtrains.lines.step(dtime) end --trigger a save when necessary save_timer=save_timer-dtime if save_timer<=0 then local t=os.clock() --save advtrains.save() save_timer = advtrains.SAVE_INTERVAL atprintbm("saving", t) end within_mainstep = false end) --if something goes wrong in these functions, there is no help. no pcall here. --## MAIN LOAD ROUTINE ## -- Causes the loading of everything -- first time called in main loop (after the init phase) because luaautomation has to initialize first. function advtrains.load() advtrains.avt_load() --loading advtrains. includes ndb at advtrains.ndb.load_data() --if atlatc then -- atlatc.load() --includes interrupts --end == No longer loading here. Now part of avt_save() legacy loading. if advtrains_itm_init then advtrains_itm_init() end init_load=true no_action=false atlog("[load_all]Loaded advtrains save files") end --## MAIN SAVE ROUTINE ## -- Causes the saving of everything function advtrains.save(remove_players_from_wagons) if not init_load then --wait... we haven't loaded yet?! atwarn("Instructed to save() but load() was never called!") return end if advtrains.IGNORE_WORLD then advtrains.ndb.restore_all() end if NO_SAVE then return end if no_action then atlog("[save] Saving requested externally, but Advtrains step is disabled. Not saving any data as state may be inconsistent.") return end local t1 = os.clock() advtrains.avt_save(remove_players_from_wagons) --saving advtrains. includes ndb at advtrains.ndb.save_data() if atlatc then atlatc.save() end atlog("Saved advtrains save files, took",math.floor((os.clock()-t1) * 1000),"ms") -- Cleanup actions --TODO very simple yet hacky workaround for the "green signals" bug advtrains.invalidate_all_paths() end minetest.register_on_shutdown(function() if within_mainstep then atwarn("Crash during advtrains main step - skipping the shutdown save operation to not save inconsistent data!") else advtrains.save() end end) -- This chat command provides a solution to the problem known on the LinuxWorks server -- There are many players that joined a single time, got on a train and then left forever -- These players still occupy seats in the trains. minetest.register_chatcommand("at_empty_seats", { params = "", -- Short parameter description description = "Detach all players, especially the offline ones, from all trains. Use only when no one serious is on a train.", -- Full description privs = {train_operator=true, server=true}, -- Require the "privs" privilege to run func = function(name, param) atwarn("Data is being saved. While saving, advtrains will remove the players from trains. Save files will be reloaded afterwards!") advtrains.save(true) reload_saves() end, }) -- This chat command solves another problem: Trains getting randomly stuck. minetest.register_chatcommand("at_reroute", { params = "", description = "Delete all train routes, force them to recalculate", privs = {train_operator=true}, -- Only train operator is required, since this is relatively safe. func = function(name, param) advtrains.invalidate_all_paths() return true, "Successfully invalidated train routes" end, }) minetest.register_chatcommand("at_whereis", { params = "<train id>", description = "Returns the position of the train with the given id", privs = {train_operator = true}, func = function(name,param) local train = advtrains.trains[param] if not train or not train.last_pos then return false, "Train "..param.." does not exist or is invalid" else return true, "Train "..param.." is at "..minetest.pos_to_string(train.last_pos) end end, }) minetest.register_chatcommand("at_disable_step", { params = "<yes/no>", description = "Disable the advtrains globalstep temporarily", privs = {server=true}, func = function(name, param) if minetest.is_yes(param) then -- disable everything, and turn off saving no_action = true; atwarn("The advtrains globalstep has been disabled. Trains are not moving, and no data is saved! Run '/at_disable_step no' to enable again!") return true, "Disabled advtrains successfully" elseif no_action then atwarn("Re-enabling advtrains globalstep...") reload_saves() return true else return false, "Advtrains is already running normally!" end end, }) advtrains.is_no_action = function() return no_action end local tot=(os.clock()-lot)*1000 minetest.log("action", "[advtrains] Loaded in "..tot.."ms")