I read https://wiki.beamng.com/Vehicle_Specific_Bindings It says: I says: Code: { "splitter": {"order:1", "onChange":"electrics.values.splitter = VALUE", "title":"Splitter control", "desc":"Splitter box control toggle Low/High" }, } Game says: Now I feel something in here is not in agreement, probably it is my eyes or my brain, but for me it appears I have done nothing wrong, yet game is being vocal at me, she has no manners! I have "input_actions_offroad_fufsgfen.lua" in Documents/BeamNG/vehicles/semi/lua/ Also I have "keyboard.json" in Documents/BeamNG/vehicles/semi/inputmaps/ Code: { "bindings" : [ { "control" : "s" , "action" : "splitter" }, ], "name" : "Keyboard" , "version" : 1 , "devicetype" : "keyboard" , "vidpid" : "6F1D2B61" , "guid" : "{6F1D2B61-D5A0-11CF-BFC7-444553540000}" } So it says unexpected symbol near { at line 1 of my lua file, which to me looks exactly like example. there is only two { symbols and after both comes symbol " so that is unexpected, but that is exactly the symbol that is instructed to put there, I guess, maybe. There is now something I have not properly figured out I guess. I guess I need custom lua thing to make my splitterBox to actually change between low and high when I make action, but mapping should work before that, right? Update: One of those ROFL and D'Oh moments once again, I should not have .lua extension, but .json and file is at wrong place, I did rush ahead of what I should of been doing! Oh, there will be more trouble coming with lua part, I'm sure of. --- Post updated --- Okay, I read more, maybe I understand little more, game should create keyboard.diff file by itself: No it does not, actually game completely ignores my input_actions_semi_offroad_fufsgfen.json, or whatever I name that. When I go to controls-bindings there is nothing about splitter even that by my best understanding there should be that splitter custom control now visible, but nothing at all shows up. --- Post updated --- Right, these instructions are really difficult to read properly, so path for input_actions is directly in vehicle folder, while path for keyboard.json is in inputmaps. To the next problem, Unexpected symbol near line two " " ", excepted : or = So I have this at Line 2: Code: "splitter": {"order":1, "onChange":"electrics.values.splitter = VALUE", "title":"Splitter control", "desc":"Splitter box control toggle Low/High" }, Instructions have: Code: "shoot": {"order":1, "onChange":"electrics.values.shoot = VALUE", "title":"Shoot ball", "desc":"Shoots the cannon ball. Cannon will need to be reset to fire again" }, With my greatest ability I try to read and see what there is different, where is extra " but despite trying very very hard I can't yet spot it, need to try harder I guess. --- Post updated --- I don't know, it looks same as files in other mods, but json decode error: Oh, there it was, see "order:1", is bad while "order":1, is good, took only 30 times reading character by character until could spot that, fun --- Post updated --- So to the next fail, eventually I have found all possible problems and information can be compiled to trouble shooting list of input maps So this is the code which toggles rangebox between low and high by default, it is in input_actions.json at steamapps\common\BeamNG.drive\lua\ge Code: "toggleRangeStatus" :{"cat":"vehicle", "order": 30, "ctx": "vlua", "onDown" :"for _,v in pairs(controller.getControllersByType('4wd')) do v.toggleRange() end", "title": "Toggle Range Status", "desc": "Switches between low and high ranges" }, My understanding is that this code goes trough controllers which are of type 4wd, from lua side of things at steamapps\common\BeamNG.drive\lua\vehicle\controller I find 4wd.lua that has code which makes rangebox to toggle status and I guess that 4wd in code snippet refers to filename of that lua, but this is only a guess. So I made splitterControl.lua at vehicles\semi\lua cut a bit of 4wd stuff out, attempted to hardcode name variable to name splitterBox Code: -- This Source Code Form is subject to the terms of the bCDDL, v. 1.1. -- If a copy of the bCDDL was not distributed with this -- file, You can obtain one at http://beamng.com/bCDDL-1.1.txt local M = {} M.type = "auxilliary" M.relevantDevice = "transfercase" local shaft = nil local rangeBox = nil --rangeBox is type local name = "splitterBox" --name of my splitterbox, which really is rangeBox type local hasBuiltPie = false local function updateGFX(dt) electrics.values.modeRangeBox = rangeBox and (rangeBox.mode == "low" and 1 or 0) or 0 end local function toggleRange() if rangeBox then powertrain.toggleDeviceMode(rangeBox.name) gui.message("Range status: "..rangeBox.mode, 10, "vehicle.powertrain.rangestatus") end end local function setRangeMode(mode) if rangeBox then powertrain.setDeviceMode(rangeBox.name, mode) gui.message("Range status: "..rangeBox.mode, 10, "vehicle.powertrain.rangestatus") end end local function serialize() return { modeRange = rangeBox and rangeBox.mode or nil } end local function deserialize(data) if data then if rangeBox and data.modeRange then setRangeMode(data.modeRange) end end end local function init(jbeamData) --name = jbeamData.name shaft = powertrain.getDevice(jbeamData.shaftName) rangeBox = powertrain.getDevice("splitterBox")--jbeamData.rangeBoxName) electrics.values.modeRangeBox = rangeBox and (rangeBox.mode == "low" and 1 or 0) or 0 if not hasBuiltPie then if rangeBox then core_quickAccess.addEntry({ level = '/powertrain/', generator = function(entries) local rmicon if rangeBox.mode == "low" then rmicon = "radial_lowrangebox" else rmicon = "radial_highrangebox" end table.insert(entries, { title = 'ui.radialmenu2.powertrain.rangebox_mode', priority = 40, ["goto"] = '/powertrain/rangebox/', icon = rmicon }) end}) core_quickAccess.addEntry({ level = '/powertrain/rangebox/', generator = function(entries) local lowEntry = { title = 'ui.radialmenu2.powertrain.rangebox_mode.low', icon="radial_lowrangebox", onSelect = function() controller.getController(name).setRangeMode("low") return {'reload'} end} local highEntry = { title = 'ui.radialmenu2.powertrain.rangebox_mode.high', icon="radial_highrangebox", onSelect = function() controller.getController(name).setRangeMode("high") return {'reload'} end} if rangeBox.mode == "low" then lowEntry.color = '#ff6600' else highEntry.color = '#ff6600' end table.insert(entries, lowEntry) table.insert(entries, highEntry) end}) end hasBuiltPie = true end end M.init = init M.updateGFX = updateGFX M.toggleRange = toggleRange M.setRangeMode = setRangeMode M.serialize = serialize M.deserialize = deserialize return M[code] Then made input_actions_semi_offroad_fufsgfen.json at vehicles\semi to to this: [code]"toggle_splitter":{"cat":"vehicle", "order": 30, "ctx": "vlua", "onDown" :"for _,v in pairs(controller.getControllersByType('splitterControl')) do v.toggleRange() end", "title": "Splitter Control", "desc": "Splitter box control toggle Low/High" }, So if that 4wd refers to file name, it should now launch my lua splitterControl.lua I doubt it does, there is no errors, there are no any indication of any function happening. If I write splitterControl.toggleRange() to vehicle lua in console it just shows yellow text of it and nothing happens, same if I write splitterControl.setRangeMode("low") However if I write gibberishControl.serRangeMode("low") then I get errors, so something works for sure, but maybe that lua code above just is not quite right yet. Maybe name is not text type but some object type, so I did try this, but then how it would know I want one named splitterBox, because there is two rangeboxes it probably finds two and gets confused? Code: -- This Source Code Form is subject to the terms of the bCDDL, v. 1.1. -- If a copy of the bCDDL was not distributed with this -- file, You can obtain one at http://beamng.com/bCDDL-1.1.txt local M = {} M.type = "auxilliary" M.relevantDevice = "transfercase" local shaft = nil local rangeBox = nil --rangeBox is type local name = nil --"splitterBox" --name of my splitterbox, which really is rangeBox type local hasBuiltPie = false local function updateGFX(dt) electrics.values.modeRangeBox = rangeBox and (rangeBox.mode == "low" and 1 or 0) or 0 end local function toggleRange() if rangeBox then powertrain.toggleDeviceMode(rangeBox.name) gui.message("Range status: "..rangeBox.mode, 10, "vehicle.powertrain.rangestatus") end end local function setRangeMode(mode) if rangeBox then powertrain.setDeviceMode(rangeBox.name, mode) gui.message("Range status: "..rangeBox.mode, 10, "vehicle.powertrain.rangestatus") end end local function serialize() return { modeRange = rangeBox and rangeBox.mode or nil } end local function deserialize(data) if data then if rangeBox and data.modeRange then setRangeMode(data.modeRange) end end end local function init(jbeamData) name = jbeamData.name shaft = powertrain.getDevice(jbeamData.shaftName) rangeBox = powertrain.getDevice(jbeamData.rangeBoxName)--jbeamData.rangeBoxName) electrics.values.modeRangeBox = rangeBox and (rangeBox.mode == "low" and 1 or 0) or 0 if not hasBuiltPie then if rangeBox then core_quickAccess.addEntry({ level = '/powertrain/', generator = function(entries) local rmicon if rangeBox.mode == "low" then rmicon = "radial_lowrangebox" else rmicon = "radial_highrangebox" end table.insert(entries, { title = 'ui.radialmenu2.powertrain.rangebox_mode', priority = 40, ["goto"] = '/powertrain/rangebox/', icon = rmicon }) end}) core_quickAccess.addEntry({ level = '/powertrain/rangebox/', generator = function(entries) local lowEntry = { title = 'ui.radialmenu2.powertrain.rangebox_mode.low', icon="radial_lowrangebox", onSelect = function() controller.getController(name).setRangeMode("low") return {'reload'} end} local highEntry = { title = 'ui.radialmenu2.powertrain.rangebox_mode.high', icon="radial_highrangebox", onSelect = function() controller.getController(name).setRangeMode("high") return {'reload'} end} if rangeBox.mode == "low" then lowEntry.color = '#ff6600' else highEntry.color = '#ff6600' end table.insert(entries, lowEntry) table.insert(entries, highEntry) end}) end hasBuiltPie = true end end M.init = init M.updateGFX = updateGFX M.toggleRange = toggleRange M.setRangeMode = setRangeMode M.serialize = serialize M.deserialize = deserialize return M Can't talk fluent Lua I'm afraid, but I do have some vague idea how it might work, however probably I understand it completely wrong Update: Most likely I could get it to work if I could figure out how to tell jbeamData.name to be splitterBox These two are responsible of finding rangeBox, but as there are two of rangeBox type things don't work out. name = jbeamData.name rangeBox = powertrain.getDevice(jbeamData.rangeBoxName) Then again I should not need to find the device, I just would need to tell rangebox called splitterBox, however nothing seems to be working. I try: jbeamData.rangeBoxName = 'splitterBox' jbeamData.rangeBoxName = "splitterBox" jbeamData.rangeBoxName = splitterBox Before rangeBox = powertrain.getDevice(jbeamData.rangeBoxName) line and it is about same as not having whole line at all. There should be some easy way to address specifically named part and probably is, but can't find list of commands or how to address name of that part properly so that stuff would work. Then I try from console: powertrain.getDevice("splitterBox"):setRange(0) powertrain.getDevice("splitterBox"):setRange(Low) powertrain.getDevice("splitterBox"):setRange("Low") powertrain.getDevice("splitterBox"):setRange() All gives error, so there is no setRange thing at all. Finally then I figure out that powertrain.toggleDeviceMode("splitterBox") works. So with this code in input_actions I get my lua script called properly: "toggle_splitter" :{"order": 1, "onDown":"splitterControl.toggleRange()", "title": "Toggle Splitter", "desc": "Splitter box control toggle Low/High" }, My lua script then is just this, notice how snippet above contains that toggleRange() and below here is same word: local function toggleRange() powertrain.toggleDeviceMode("splitterBox") gui.message("Range status: "..rangeBox.mode, 10, "vehicle.powertrain.rangestatus") end So splitterControl. is lua filename that is called and .toggleRange() is function called in lua filename. ("splitterBox") is name of my rangeBox in semi_transmission_Splitter_fufsgfen.jbeam file: Code: "powertrain" : [ ["type", "name", "inputName", "inputIndex"], ["torqueConverter", "torqueConverter", "mainEngine", 1], ["automaticGearbox", "gearbox", "torqueConverter", 1], ["rangeBox", "splitterBox", "gearbox", 1, {"gearRatios":[0.75,1.0], "uiName":"Splitter"}] ["rangeBox", "rangebox", "splitterBox", 1, {"gearRatios":[1,4], "uiName":"Rangebox"}], It's frustrating, but eventually mostly always solution is found.
Maybe I missed it, but I don't think you actually added the controller in jbeam? Just creating that file is not enough, controllers are reusable code parts that need to be loaded in a given vehicle first.
I'm not certain even myself at this point, but I got it to work. Perhaps there would be some other way via controller too, it's very confusing at times Or maybe semi already has an controller so I don't need to add one. Update: I did attached a zip which has files, put those to Documents\BeamNG\vehicles\semi and load provided splitter_mixer config, it should in theory work then, it is just example or proof of concept, lua file at least is a big mess there still, but if someone needs to see how control is added and splitter functionality created, files might help in there.