aboutsummaryrefslogtreecommitdiff
path: root/advtrains/atc.lua
diff options
context:
space:
mode:
authororwell96 <mono96.mml@gmail.com>2017-01-04 12:02:00 +0100
committerorwell96 <mono96.mml@gmail.com>2017-01-04 12:02:00 +0100
commit853a9e690eeeb48aa1a13faa66db0a91d5b49390 (patch)
tree51451d08e10cedda4128c1e29848524a00baf2a7 /advtrains/atc.lua
parenta9d43ce2ca3bfd703428d248c0839e53ffd76f27 (diff)
downloadadvtrains-853a9e690eeeb48aa1a13faa66db0a91d5b49390.tar.gz
advtrains-853a9e690eeeb48aa1a13faa66db0a91d5b49390.tar.bz2
advtrains-853a9e690eeeb48aa1a13faa66db0a91d5b49390.zip
Add Automatic Train Control system
Diffstat (limited to 'advtrains/atc.lua')
-rw-r--r--advtrains/atc.lua292
1 files changed, 291 insertions, 1 deletions
diff --git a/advtrains/atc.lua b/advtrains/atc.lua
index 0afddb0..f070b20 100644
--- a/advtrains/atc.lua
+++ b/advtrains/atc.lua
@@ -1,4 +1,294 @@
--atc.lua
--registers and controls the ATC system
---(simple)mesecon detector rails
+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