-- 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" or index < 2 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, get_fc = function() if not train_id then return end local fc_list = {} for index,wagon_id in ipairs(train.trainparts) do fc_list[index] = table.concat(advtrains.wagons[wagon_id].fc,"!") or "" end return fc_list end, set_fc = function(fc_list,reset_index) assertt(fc_list, "table") if not train_id then return false end -- safety type-check for entered values for _,v in ipairs(fc_list) do if v and type(v) ~= "string" then error("FC entries must be a string") return end end for index,wagon_id in ipairs(train.trainparts) do if fc_list[index] then -- has FC to enter to this wagon local data = advtrains.wagons[wagon_id] if data then -- wagon actually exists --effectively copyied from wagons.lua, allowing for the :split function and reset_index data.fc = fc_list[index]:split("!") if reset_index or not data.fcind then data.fcind = 1 elseif data.fcind > #data.fc then data.fcind = #data.fc end end end end 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() if not train_id then return false end 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 Track"), 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