295 lines
8.6 KiB
Lua
295 lines
8.6 KiB
Lua
--atc.lua
|
|
--registers and controls the ATC system
|
|
|
|
local atc={}
|
|
-- ATC persistence table
|
|
atc.controllers = {}
|
|
--contents: {command="...", arrowconn=0-15 where arrow points}
|
|
|
|
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"
|
|
print("[advtrains]Failed loading advtrains atc save file "..er)
|
|
else
|
|
local tbl = minetest.deserialize(file:read("*a"))
|
|
if type(tbl) == "table" then
|
|
atc.controllers=tbl.controllers
|
|
end
|
|
file:close()
|
|
end
|
|
function atc.save()
|
|
--leave space for more save data.
|
|
local datastr = minetest.serialize({controllers = atc.controllers})
|
|
if not datastr then
|
|
minetest.log("error", "[advtrains] Failed to serialize trackdb data!")
|
|
return
|
|
end
|
|
local file, err = io.open(advtrains.fpath_atc, "w")
|
|
if err then
|
|
return err
|
|
end
|
|
file:write(datastr)
|
|
file:close()
|
|
end
|
|
|
|
--call from advtrains.detector subprogram
|
|
|
|
function atc.trigger_controller_train_enter(pos, train_id)
|
|
atc.send_command(pos)
|
|
end
|
|
|
|
--general
|
|
|
|
function atc.send_command(pos)
|
|
local pts=minetest.pos_to_string(pos)
|
|
if atc.controllers[pts] then
|
|
print("Called send_command at "..pts)
|
|
local train_id = advtrains.detector.on_node[pts]
|
|
if train_id then
|
|
if advtrains.trains[train_id] then
|
|
print("send_command inside if: "..sid(train_id))
|
|
atc.train_reset_command(train_id)
|
|
local arrowconn=atc.controllers[pts].arrowconn
|
|
local train=advtrains.trains[train_id]
|
|
for index, ppos in pairs(train.path) do
|
|
if vector.equals(advtrains.round_vector_floor_y(ppos), pos) then
|
|
advtrains.trains[train_id].atc_arrow =
|
|
vector.equals(
|
|
advtrains.dirCoordSet(pos, arrowconn),
|
|
advtrains.round_vector_floor_y(train.path[index+train.movedir])
|
|
)
|
|
advtrains.trains[train_id].atc_command=atc.controllers[pts].command
|
|
print("Sending ATC Command: "..atc.controllers[pts].command)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
return false
|
|
end
|
|
|
|
function atc.train_reset_command(train_id)
|
|
advtrains.trains[train_id].atc_command=nil
|
|
advtrains.trains[train_id].atc_delay=0
|
|
advtrains.trains[train_id].atc_brake_target=nil
|
|
advtrains.trains[train_id].atc_wait_finish=nil
|
|
advtrains.trains[train_id].atc_arrow=nil
|
|
end
|
|
|
|
--nodes
|
|
local idxtrans={static=1, mesecon=2, digiline=3}
|
|
local apn_func=function(pos)
|
|
advtrains.reset_trackdb_position(pos)
|
|
local meta=minetest.get_meta(pos)
|
|
if meta then
|
|
meta:set_string("infotext", "ATC controller, unconfigured.")
|
|
meta:set_string("formspec", atc.get_atc_controller_formspec(pos, meta))
|
|
end
|
|
end
|
|
|
|
advtrains.register_tracks("default", {
|
|
nodename_prefix="advtrains:dtrack_atc",
|
|
texture_prefix="advtrains_dtrack_atc",
|
|
models_prefix="advtrains_dtrack_detector",
|
|
models_suffix=".b3d",
|
|
shared_texture="advtrains_dtrack_rail_atc.png",
|
|
description="ATC controller",
|
|
formats={},
|
|
get_additional_definiton = function(def, preset, suffix, rotation)
|
|
return {
|
|
after_place_node=apn_func,
|
|
on_place_rail=apn_func,
|
|
after_dig_node=function(pos)
|
|
advtrains.invalidate_all_paths()
|
|
advtrains.reset_trackdb_position(pos)
|
|
local pts=minetest.pos_to_string(pos)
|
|
atc.controllers[pts]=nil
|
|
end,
|
|
on_receive_fields = function(pos, formname, fields, player)
|
|
if minetest.is_protected(pos, player:get_player_name()) then
|
|
minetest.chat_send_player(player:get_player_name(), "This position is protected!")
|
|
return
|
|
end
|
|
local meta=minetest.get_meta(pos)
|
|
if meta then
|
|
if not fields.save then
|
|
--maybe only the dropdown changed
|
|
if fields.mode then
|
|
meta:set_string("mode", idxtrans[fields.mode])
|
|
meta:set_string("infotext", "ATC controller, mode "..fields.mode.."\n"..( fields.mode=="digiline" and "Channel: "..meta:get_string("channel") or "Command: "..meta:get_string("command") ) )
|
|
meta:set_string("formspec", atc.get_atc_controller_formspec(pos, meta))
|
|
end
|
|
return
|
|
end
|
|
meta:set_string("mode", idxtrans[fields.mode])
|
|
meta:set_string("command", fields.command)
|
|
meta:set_string("command_on", fields.command_on)
|
|
meta:set_string("channel", fields.channel)
|
|
meta:set_string("infotext", "ATC controller, mode "..fields.mode.."\n"..( fields.mode=="digiline" and "Channel: "..meta:get_string("channel") or "Command: "..meta:get_string("command") ) )
|
|
meta:set_string("formspec", atc.get_atc_controller_formspec(pos, meta))
|
|
|
|
local pts=minetest.pos_to_string(pos)
|
|
local _, conn1=advtrains.get_rail_info_at(pos, advtrains.all_tracktypes)
|
|
atc.controllers[pts]={command=fields.command, arrowconn=conn1}
|
|
atc.send_command(pos)
|
|
end
|
|
end,
|
|
}
|
|
end
|
|
}, advtrains.trackpresets.t_30deg_straightonly)
|
|
|
|
|
|
function atc.get_atc_controller_formspec(pos, meta)
|
|
local mode=tonumber(meta:get_string("mode")) or 1
|
|
local command=meta:get_string("command")
|
|
local command_on=meta:get_string("command_on")
|
|
local channel=meta:get_string("channel")
|
|
local formspec="size[8,6]"..
|
|
"dropdown[0,0;3;mode;static,mesecon,digiline;"..mode.."]"
|
|
if mode<3 then
|
|
formspec=formspec.."field[0.5,1.5;7,1;command;Command;"..minetest.formspec_escape(command).."]"
|
|
if tonumber(mode)==2 then
|
|
formspec=formspec.."field[0.5,3;7,1;command_on;Command (on);"..minetest.formspec_escape(command_on).."]"
|
|
end
|
|
else
|
|
formspec=formspec.."field[0.5,1.5;7,1;channel;Digiline channel;"..minetest.formspec_escape(channel).."]"
|
|
end
|
|
return formspec.."button_exit[0.5,4.5;7,1;save;Save]"
|
|
end
|
|
|
|
--from trainlogic.lua train step
|
|
local matchptn={
|
|
["SM"]=function(id, train)
|
|
train.tarvelocity=train.max_speed
|
|
return 2
|
|
end,
|
|
["S([0-9]+)"]=function(id, train, match)
|
|
train.tarvelocity=tonumber(match)
|
|
return #match+1
|
|
end,
|
|
["B([0-9]+)"]=function(id, train, match)
|
|
if train.velocity>tonumber(match) then
|
|
train.atc_brake_target=tonumber(match)
|
|
if train.tarvelocity>train.atc_brake_target then
|
|
train.tarvelocity=train.atc_brake_target
|
|
end
|
|
end
|
|
return #match+1
|
|
end,
|
|
["W"]=function(id, train)
|
|
train.atc_wait_finish=true
|
|
return 1
|
|
end,
|
|
["D([0-9]+)"]=function(id, train, match)
|
|
train.atc_delay=tonumber(match)
|
|
return #match+1
|
|
end,
|
|
["R"]=function(id, train)
|
|
if train.velocity<=0 then
|
|
train.movedir=train.movedir*-1
|
|
train.atc_arrow = not train.atc_arrow
|
|
else
|
|
print("ATC Reverse command warning: didn't reverse train!")
|
|
end
|
|
return 1
|
|
end,
|
|
}
|
|
|
|
function atc.execute_atc_command(id, train)
|
|
--strip whitespaces
|
|
local command=string.match(train.atc_command, "^%s*(.*)$")
|
|
|
|
|
|
if string.match(command, "^%s*$") then
|
|
train.atc_command=nil
|
|
return
|
|
end
|
|
--conditional statement?
|
|
local is_cond, cond_applies
|
|
local cond, rest=string.match(command, "^I([%+%-])(.+)$")
|
|
if cond then
|
|
is_cond=true
|
|
if cond=="+" then
|
|
cond_applies=train.atc_arrow
|
|
end
|
|
if cond=="-" then
|
|
cond_applies=not train.atc_arrow
|
|
end
|
|
else
|
|
cond, compare, rest=string.match(command, "^I([<>]=?)([0-9]+)(.+)$")
|
|
if cond and compare then
|
|
is_cond=true
|
|
if cond=="<" then
|
|
cond_applies=train.velocity<tonumber(compare)
|
|
end
|
|
if cond==">" then
|
|
cond_applies=train.velocity>tonumber(compare)
|
|
end
|
|
if cond=="<=" then
|
|
cond_applies=train.velocity<=tonumber(compare)
|
|
end
|
|
if cond==">=" then
|
|
cond_applies=train.velocity>=tonumber(compare)
|
|
end
|
|
end
|
|
end
|
|
if is_cond then
|
|
print("Evaluating if statement: "..command)
|
|
print("Cond: "..(cond or "nil"))
|
|
print("Applies: "..(cond_applies and "true" or "false"))
|
|
print("Rest: "..rest)
|
|
--find end of conditional statement
|
|
local nest, pos, elsepos=0, 1
|
|
while nest>=0 do
|
|
if pos>#rest then
|
|
print("ATC command syntax error: I statement not closed: "..command)
|
|
atc.train_reset_command(id)
|
|
return
|
|
end
|
|
local char=string.sub(rest, pos, pos)
|
|
if char=="I" then
|
|
nest=nest+1
|
|
end
|
|
if char==";" then
|
|
nest=nest-1
|
|
end
|
|
if nest==0 and char=="E" then
|
|
elsepos=pos+0
|
|
end
|
|
pos=pos+1
|
|
end
|
|
if not elsepos then elsepos=pos-1 end
|
|
if cond_applies then
|
|
command=string.sub(rest, 1, elsepos-1)..string.sub(rest, pos)
|
|
else
|
|
command=string.sub(rest, elsepos+1, pos-2)..string.sub(rest, pos)
|
|
end
|
|
print("Result: "..command)
|
|
train.atc_command=command
|
|
atc.execute_atc_command(id, train)
|
|
return
|
|
else
|
|
for pattern, func in pairs(matchptn) do
|
|
local match=string.match(command, "^"..pattern)
|
|
if match then
|
|
local patlen=func(id, train, match)
|
|
|
|
print("Executing: "..string.sub(command, 1, patlen))
|
|
|
|
train.atc_command=string.sub(command, patlen+1)
|
|
if train.atc_delay<=0 and not train.atc_wait_finish then
|
|
--continue (recursive, cmds shouldn't get too long, and it's a end-recursion.)
|
|
atc.execute_atc_command(id, train)
|
|
end
|
|
return
|
|
end
|
|
end
|
|
end
|
|
print("ATC command parse error: "..command)
|
|
atc.train_reset_command(id)
|
|
end
|
|
|
|
--move table to desired place
|
|
advtrains.atc=atc
|