local S = core.get_translator("celevator") local iorules = { vector.new(-1,0,0), vector.new(1,0,0), vector.new(0,0,-1), vector.new(0,0,1), } local function getpos(mem,searchpos,pioffset) local carpos = 0 if not searchpos then searchpos = mem.drive.status.apos end if pioffset then searchpos = searchpos+0.5 end for k,v in ipairs(mem.params.floorheights) do carpos = carpos+v if carpos > searchpos then return k end end end local outputoptions = { { id = "normal", desc = S("Normal Operation"), func = function(mem) return (mem.carstate == "normal") end, needsfloor = false, }, { id = "fault", desc = S("Fault"), func = function(mem) return (mem.carstate == "fault") end, needsfloor = false, }, { id = "estop", desc = S("Emergency Stop"), func = function(mem) return (mem.carstate == "stop") end, needsfloor = false, }, { id = "inspect", desc = S("Inspection (Any)"), func = function(mem) return (mem.carstate == "mrinspect" or mem.carstate == "inspconflict" or mem.carstate == "carinspect") end, needsfloor = false, }, { id = "fire", desc = S("Fire Service"), func = function(mem) return (mem.carstate == "fs1" or mem.carstate == "fs2" or mem.carstate == "fs2hold") end, needsfloor = false, }, { id = "fire1", desc = S("Fire Service Phase 1"), func = function(mem) return (mem.carstate == "fs1") end, needsfloor = false, }, { id = "fire2", desc = S("Fire Service Phase 2"), func = function(mem) return (mem.carstate == "fs2" or mem.carstate == "fs2hold") end, needsfloor = false, }, { id = "independent", desc = S("Independent Service"), func = function(mem) return (mem.carstate == "indep") end, needsfloor = false, }, { id = "swing", desc = S("Swing Operation"), func = function(mem) return (mem.carstate == "swing") end, needsfloor = false, }, { id = "opening", desc = S("Doors Opening"), func = function(mem) return (mem.doorstate == "opening") end, needsfloor = false, }, { id = "open", desc = S("Doors Open"), func = function(mem) return (mem.doorstate == "open") end, needsfloor = false, }, { id = "closing", desc = S("Doors Closing"), func = function(mem) return (mem.doorstate == "closing") end, needsfloor = false, }, { id = "closed", desc = S("Doors Closed"), func = function(mem) return (mem.doorstate == "closed") end, needsfloor = false, }, { id = "moveup", desc = S("Moving Up"), func = function(mem) return (mem.drive.status and mem.drive.status.vel > 0) end, needsfloor = false, }, { id = "movedown", desc = S("Moving Down"), func = function(mem) return (mem.drive.status and mem.drive.status.vel < 0) end, needsfloor = false, }, { id = "moving", desc = S("Moving (Any Direction)"), func = function(mem) return (mem.drive.status and mem.drive.status.vel ~= 0) end, needsfloor = false, }, { id = "collectorup", desc = S("Collecting Up Calls"), func = function(mem) return (mem.carstate == "normal" and mem.direction == "up") end, needsfloor = false, }, { id = "collectordown", desc = S("Collecting Down Calls"), func = function(mem) return (mem.carstate == "normal" and mem.direction == "down") end, needsfloor = false, }, { id = "lightsw", desc = S("Car Light Switch"), func = function(mem) return mem.lightsw end, needsfloor = false, }, { id = "fansw", desc = S("Car Fan Switch"), func = function(mem) return mem.fansw end, needsfloor = false, }, { id = "upcall", desc = S("Up Call Exists at Landing:"), func = function(mem,floor) return mem.upcalls[floor] end, needsfloor = true, }, { id = "downcall", desc = S("Down Call Exists at Landing:"), func = function(mem,floor) return mem.dncalls[floor] end, needsfloor = true, }, { id = "carcall", desc = S("Car Call Exists at Landing:"), func = function(mem,floor) return mem.carcalls[floor] end, needsfloor = true, }, { id = "apos", desc = S("Car at Landing:"), func = function(mem,floor) if mem.drive.status then return (getpos(mem,nil,true) == floor) end end, needsfloor = true, }, { id = "dpos", desc = S("Moving to Landing:"), func = function(mem,floor) if mem.drive.status then return (getpos(mem,mem.drive.status.dpos) == floor) end end, needsfloor = true, }, } local doutputoptions = { { id = "fire", desc = S("Fire Service"), func = function(mem) return mem.fs1led end, needsfloor = false, }, { id = "upcall", desc = S("Up Call Exists at Landing:"), func = function(mem,floor) return mem.upcalls[floor] end, needsfloor = true, }, { id = "downcall", desc = S("Down Call Exists at Landing:"), func = function(mem,floor) return mem.dncalls[floor] end, needsfloor = true, }, } local inputoptions = { { id = "carcall", desc = S("Car Call at Landing:"), func_on = function(controllerpos,floor) celevator.controller.run(controllerpos,{ type = "remotemsg", channel = "carcall", msg = floor, }) end, needsfloor = true, }, { id = "upcall", desc = S("Up Call (simplex car) at Landing:"), func_on = function(controllerpos,floor) celevator.controller.run(controllerpos,{ type = "remotemsg", channel = "upcall", msg = floor, }) end, needsfloor = true, }, { id = "downcall", desc = S("Down Call (simplex car) at Landing:"), func_on = function(controllerpos,floor) celevator.controller.run(controllerpos,{ type = "remotemsg", channel = "dncall", msg = floor, }) end, needsfloor = true, }, { id = "swingupcall", desc = S("Up Call (swing) at Landing:"), func_on = function(controllerpos,floor) celevator.controller.run(controllerpos,{ type = "remotemsg", channel = "swingupcall", msg = floor, }) end, needsfloor = true, }, { id = "swingdowncall", desc = S("Down Call (swing) at Landing:"), func_on = function(controllerpos,floor) celevator.controller.run(controllerpos,{ type = "remotemsg", channel = "swingdncall", msg = floor, }) end, needsfloor = true, }, { id = "fs1off", desc = S("Deactivate Fire Service Phase 1"), func_on = function(controllerpos) celevator.controller.run(controllerpos,{ type = "fs1switch", state = false, }) end, needsfloor = false, }, { id = "fs1on", desc = S("Activate Fire Service (main landing) Phase 1"), func_on = function(controllerpos) celevator.controller.run(controllerpos,{ type = "fs1switch", state = true, }) end, needsfloor = false, }, { id = "fs1onalt", desc = S("Activate Fire Service (alternate landing) Phase 1"), func_on = function(controllerpos) celevator.controller.run(controllerpos,{ type = "fs1switch", state = true, mode = "alternate", }) end, needsfloor = false, }, { id = "mrsmoke", desc = S("Machine Room or Hoistway Smoke Detector"), func_on = function(controllerpos) celevator.controller.run(controllerpos,{ type = "mrsmoke", }) end, needsfloor = false, }, { id = "secdeny", desc = S("Lock Car Calls at Landing:"), func_on = function(controllerpos,floor) celevator.controller.run(controllerpos,{ type = "remotemsg", channel = "security", msg = { floor = floor, mode = "deny", }, }) end, needsfloor = true, }, { id = "secauth", desc = S("Require Auth for Car Calls at Landing:"), func_on = function(controllerpos,floor) celevator.controller.run(controllerpos,{ type = "remotemsg", channel = "security", msg = { floor = floor, mode = "auth", }, }) end, needsfloor = true, }, { id = "secallow", desc = S("Unlock Car Calls at Landing:"), func_on = function(controllerpos,floor) celevator.controller.run(controllerpos,{ type = "remotemsg", channel = "security", msg = { floor = floor, mode = nil, }, }) end, needsfloor = true, }, { id = "swingon", desc = S("Activate Swing Operation"), func_on = function(controllerpos) celevator.controller.run(controllerpos,{ type = "remotemsg", channel = "swing", msg = true, }) end, needsfloor = false, }, { id = "swingoff", desc = S("Deactivate Swing Operation"), func_on = function(controllerpos) celevator.controller.run(controllerpos,{ type = "remotemsg", channel = "swing", msg = false, }) end, needsfloor = false, }, } local dinputoptions = { { id = "upcall", desc = S("Up Call at Landing:"), func_on = function(dispatcherpos,floor) celevator.dispatcher.run(dispatcherpos,{ type = "remotemsg", channel = "upcall", msg = floor, }) end, needsfloor = true, }, { id = "downcall", desc = S("Down Call at Landing:"), func_on = function(dispatcherpos,floor) celevator.dispatcher.run(dispatcherpos,{ type = "remotemsg", channel = "dncall", msg = floor, }) end, needsfloor = true, }, { id = "fs1off", desc = S("Deactivate Fire Service Phase 1"), func_on = function(dispatcherpos) celevator.dispatcher.run(dispatcherpos,{ type = "fs1switch", state = false, }) end, needsfloor = false, }, { id = "fs1on", desc = S("Activate Fire Service Phase 1"), func_on = function(dispatcherpos) celevator.dispatcher.run(dispatcherpos,{ type = "fs1switch", state = true, }) end, needsfloor = false, }, } local function updateoutputform(pos) local meta = core.get_meta(pos) local dmode = meta:get_int("dispatcher") == 1 local fs = "formspec_version[7]size[9,6.5]" fs = fs.."tabheader[0,0;1;tab;"..S("Controller")..","..S("Dispatcher")..";"..(dmode and "2" or "1")..";true;true]" fs = fs.."dropdown[1,0.5;7,1;signal;" local selected = 1 local currentid = meta:get_string("signal") for k,v in ipairs(dmode and doutputoptions or outputoptions) do fs = fs..core.formspec_escape(v.desc).."," if v.id == currentid then selected = k end end fs = string.sub(fs,1,-2) fs = fs..";"..selected..";false]" fs = fs.."field[0.5,2.5;3,1;carid;"..(dmode and S("Dispatcher ID") or S("Car ID"))..";${carid}]" fs = fs.."field[4.5,2.5;3,1;floor;"..S("Landing Number")..";${floor}]" fs = fs.."label[1.5,4;"..S("Not all signal options require a landing number.").."]" fs = fs.."button_exit[2.5,5;3,1;save;"..S("Save").."]" meta:set_string("formspec",fs) end local function handleoutputfields(pos,_,fields,player) local meta = core.get_meta(pos) if fields.quit and not fields.save then return end local name = player:get_player_name() if core.is_protected(pos,name) and not core.check_player_privs(name,{protection_bypass=true}) then if player:is_player() then core.record_protection_violation(pos,name) end return end if fields.save then local dmode = meta:get_int("dispatcher") == 1 if not tonumber(fields.carid) then return end meta:set_int("carid",fields.carid) local carid = tonumber(fields.carid) local carinfo = core.deserialize(celevator.storage:get_string(string.format("car%d",carid))) or {} if dmode then if not carinfo.dispatcherpos then return end if not celevator.dispatcher.isdispatcher(carinfo.dispatcherpos) then return end if core.is_protected(carinfo.dispatcherpos,name) and not core.check_player_privs(name,{protection_bypass=true}) then if player:is_player() then core.chat_send_player(name,S("Can't connect to a dispatcher you don't have access to.")) core.record_protection_violation(carinfo.dispatcherpos,name) end return end else if not carinfo.controllerpos then return end if not celevator.controller.iscontroller(carinfo.controllerpos) then return end if core.is_protected(carinfo.controllerpos,name) and not core.check_player_privs(name,{protection_bypass=true}) then if player:is_player() then core.chat_send_player(name,S("Can't connect to a controller you don't have access to.")) core.record_protection_violation(carinfo.controllerpos,name) end return end end local floor = tonumber(fields.floor) if floor then meta:set_int("floor",floor) end local def for _,v in ipairs(dmode and doutputoptions or outputoptions) do if v.desc == fields.signal then def = v end end if not def then return end if def.needsfloor and not floor then return end meta:set_string("signal",def.id) updateoutputform(pos) local infotext = " - "..def.desc..(def.needsfloor and " "..floor or "") if dmode then infotext = S("Dispatcher: @1",carid)..infotext else infotext = S("Car: @1",carid)..infotext end meta:set_string("infotext",infotext) elseif fields.tab then meta:set_int("dispatcher",tonumber(fields.tab)-1) updateoutputform(pos) end end core.register_node("celevator:mesecons_output_off",{ description = S("Elevator Mesecons Output"), tiles = { "celevator_meseconsoutput_top_off.png", "celevator_cabinet_sides.png", }, groups = { dig_immediate = 2, }, paramtype = "light", drawtype = "nodebox", node_box = { type = "fixed", fixed = { {-0.5,-0.5,-0.5,0.5,-0.47,0.5}, {-0.438,-0.47,-0.438,0.438,-0.42,0.438}, }, }, mesecons = { receptor = { state = mesecon.state.off, rules = iorules, }, }, after_place_node = function(pos) local meta = core.get_meta(pos) meta:set_int("floor",1) updateoutputform(pos) end, on_receive_fields = handleoutputfields, }) core.register_node("celevator:mesecons_output_on",{ description = S("Elevator Mesecons Output (on state - you hacker you!)"), tiles = { "celevator_meseconsoutput_top_on.png", "celevator_cabinet_sides.png", }, drop = "celevator:mesecons_output_off", groups = { dig_immediate = 2, not_in_creative_inventory = 1, }, paramtype = "light", drawtype = "nodebox", node_box = { type = "fixed", fixed = { {-0.5,-0.5,-0.5,0.5,-0.47,0.5}, {-0.438,-0.47,-0.438,0.438,-0.42,0.438}, }, }, mesecons = { receptor = { state = mesecon.state.on, rules = iorules, }, }, after_place_node = function(pos) local meta = core.get_meta(pos) meta:set_int("floor",1) updateoutputform(pos) end, on_receive_fields = handleoutputfields, }) core.register_abm({ label = "Update mesecons output", nodenames = {"celevator:mesecons_output_off","celevator:mesecons_output_on",}, interval = 1, chance = 1, action = function(pos,node) local meta = core.get_meta(pos) local dmode = meta:get_int("dispatcher") == 1 local oldstate = (node.name == "celevator:mesecons_output_on") local carid = meta:get_int("carid") if carid == 0 then return end local carinfo = core.deserialize(celevator.storage:get_string(string.format("car%d",carid))) or {} if dmode then if not carinfo.dispatcherpos then return end if not celevator.dispatcher.isdispatcher(carinfo.dispatcherpos) then return end else if not carinfo.controllerpos then return end if not celevator.controller.iscontroller(carinfo.controllerpos) then return end end local floor = meta:get_int("floor") local mem = core.deserialize(core.get_meta(dmode and carinfo.dispatcherpos or carinfo.controllerpos):get_string("mem")) or {} local signal = meta:get_string("signal") local def for _,v in ipairs(dmode and doutputoptions or outputoptions) do if v.id == signal then def = v break end end if not def then return end local newstate = def.func(mem,floor) if newstate ~= oldstate then node.name = (newstate and "celevator:mesecons_output_on" or "celevator:mesecons_output_off") core.swap_node(pos,node) if newstate then mesecon.receptor_on(pos,iorules) else mesecon.receptor_off(pos,iorules) end end end, }) local function updateinputform(pos) local meta = core.get_meta(pos) local dmode = meta:get_int("dispatcher") == 1 local fs = "formspec_version[7]size[9,6.5]" fs = fs.."tabheader[0,0;1;tab;"..S("Controller")..","..S("Dispatcher")..";"..(dmode and "2" or "1")..";true;true]" fs = fs.."dropdown[1,0.5;7,1;signal;" local selected = 1 local currentid = meta:get_string("signal") for k,v in ipairs(dmode and dinputoptions or inputoptions) do fs = fs..core.formspec_escape(v.desc).."," if v.id == currentid then selected = k end end fs = string.sub(fs,1,-2) fs = fs..";"..selected..";false]" fs = fs.."field[0.5,2.5;3,1;carid;"..(dmode and S("Dispatcher ID") or S("Car ID"))..";${carid}]" fs = fs.."field[4.5,2.5;3,1;floor;"..S("Landing Number")..";${floor}]" fs = fs.."label[1.5,4;"..S("Not all signal options require a landing number.").."]" fs = fs.."button_exit[2.5,5;3,1;save;"..S("Save").."]" meta:set_string("formspec",fs) end local function handleinputfields(pos,_,fields,player) if fields.quit and not fields.save then return end local meta = core.get_meta(pos) local name = player:get_player_name() if core.is_protected(pos,name) and not core.check_player_privs(name,{protection_bypass=true}) then if player:is_player() then core.record_protection_violation(pos,name) end return end if fields.save then local dmode = meta:get_int("dispatcher") == 1 if not tonumber(fields.carid) then return end meta:set_int("carid",fields.carid) local carid = tonumber(fields.carid) local carinfo = core.deserialize(celevator.storage:get_string(string.format("car%d",carid))) or {} if dmode then if not carinfo.dispatcherpos then return end if not celevator.dispatcher.isdispatcher(carinfo.dispatcherpos) then return end if core.is_protected(carinfo.dispatcherpos,name) and not core.check_player_privs(name,{protection_bypass=true}) then if player:is_player() then core.chat_send_player(name,S("Can't connect to a dispatcher you don't have access to.")) core.record_protection_violation(carinfo.dispatcherpos,name) end return end else if not carinfo.controllerpos then return end if not celevator.controller.iscontroller(carinfo.controllerpos) then return end if core.is_protected(carinfo.controllerpos,name) and not core.check_player_privs(name,{protection_bypass=true}) then if player:is_player() then core.chat_send_player(name,S("Can't connect to a controller you don't have access to.")) core.record_protection_violation(carinfo.controllerpos,name) end return end end local floor = tonumber(fields.floor) if floor then meta:set_int("floor",floor) end local def for _,v in ipairs(dmode and dinputoptions or inputoptions) do if v.desc == fields.signal then def = v end end if not def then return end if def.needsfloor and not floor then return end meta:set_string("signal",def.id) updateinputform(pos) local infotext = " - "..def.desc..(def.needsfloor and " "..floor or "") if dmode then infotext = S("Dispatcher: @1",carid)..infotext else infotext = S("Car: @1",carid)..infotext end meta:set_string("infotext",infotext) elseif fields.tab then meta:set_int("dispatcher",tonumber(fields.tab)-1) updateinputform(pos) end end local function handleinput(pos,on) local meta = core.get_meta(pos) local carid = meta:get_int("carid") if carid == 0 then return end local carinfo = core.deserialize(celevator.storage:get_string(string.format("car%d",carid))) or {} local dmode = meta:get_int("dispatcher") == 1 if dmode then if not carinfo.dispatcherpos then return end if not celevator.dispatcher.isdispatcher(carinfo.dispatcherpos) then return end else if not carinfo.controllerpos then return end if not celevator.controller.iscontroller(carinfo.controllerpos) then return end end local floor = meta:get_int("floor") local signal = meta:get_string("signal") local def for _,v in ipairs(dmode and dinputoptions or inputoptions) do if v.id == signal then def = v break end end if not def then return end if dmode then if on then if def.func_on then def.func_on(carinfo.dispatcherpos,floor) end else if def.func_off then def.func_off(carinfo.dispatcherpos,floor) end end else if on then if def.func_on then def.func_on(carinfo.controllerpos,floor) end else if def.func_off then def.func_off(carinfo.controllerpos,floor) end end end end core.register_node("celevator:mesecons_input_off",{ description = S("Elevator Mesecons Input"), tiles = { "celevator_meseconsinput_top_off.png", "celevator_cabinet_sides.png", }, groups = { dig_immediate = 2, }, paramtype = "light", drawtype = "nodebox", node_box = { type = "fixed", fixed = { {-0.5,-0.5,-0.5,0.5,-0.47,0.5}, {-0.438,-0.47,-0.438,0.438,-0.42,0.438}, }, }, mesecons = { effector = { rules = iorules, action_on = function(pos,node) node.name = "celevator:mesecons_input_on" core.swap_node(pos,node) handleinput(pos,true) end, }, }, after_place_node = function(pos) local meta = core.get_meta(pos) meta:set_int("floor",1) updateinputform(pos) end, on_receive_fields = handleinputfields, }) core.register_node("celevator:mesecons_input_on",{ description = S("Elevator Mesecons Input (on state - you hacker you!)"), tiles = { "celevator_meseconsinput_top_on.png", "celevator_cabinet_sides.png", }, drop = "celevator:mesecons_input_off", groups = { dig_immediate = 2, not_in_creative_inventory = 1, }, paramtype = "light", drawtype = "nodebox", node_box = { type = "fixed", fixed = { {-0.5,-0.5,-0.5,0.5,-0.47,0.5}, {-0.438,-0.47,-0.438,0.438,-0.42,0.438}, }, }, mesecons = { effector = { rules = iorules, action_off = function(pos,node) node.name = "celevator:mesecons_input_off" core.swap_node(pos,node) handleinput(pos,false) end, }, }, after_place_node = function(pos) local meta = core.get_meta(pos) meta:set_int("floor",1) updateinputform(pos) end, on_receive_fields = handleinputfields, })