Community Forum

Steering vehicles with a script. What have i got wrong

Forum Overview >> Scripting

CategoryScripting
Created12.09.2018 19:56


William Beard (willbobber) 12.09.2018 19:56
Hi all
I am trying to make a script that will steer a vehicle when I turn the script on.
My aim is to be able to turn it on and off so when I get in a skidsteer or a crawler tractor then I can turn it on and a wheeled tractor I can turn it off.

This is the script to start with

joystick = {};

local joystick_directory = g_currentModDirectory;
joystick.enable = false


function joystick:load()

end;

function joystick:delete()

end;

function joystick:mouseEvent(posX, posY, isDown, isUp, button)

end;

function joystick:keyEvent(unicode, sym, modifier, isDown)

end;

function joystick:update(dt)
if self.isEntered and self.isClient and not self.isHired then

if InputBinding.hasEvent(InputBinding.joystick_Toggle) then
joystick.enable = not joystick.enable


end
end
end;

function joystick:updateTick(dt)

end;

function joystick:readStream(streamId, connection)

end;

function joystick:writeStream(streamId, connection)

end;

function joystick:onEnter()

end;

function joystick:onLeave()

end;

function joystick:draw()

if joystick.enable then
g_currentMission:addHelpButtonText(string.format(g_i18n:getText("Turn_joystick_Off"), self.typeDesc), InputBinding.joystick_Toggle);
else
g_currentMission:addHelpButtonText(string.format(g_i18n:getText("Turn_joystick_On"), self.typeDesc), InputBinding.joystick_Toggle);
end
end;

This as it is will turn on the steering, the problem I have is that when I add the section of code to work the steering it doesnt work.
I either get nothing or it will steer but only when I turn the wheel to one side and then move the joystick.

How can I write the code so that when I press the button to turn on the script it will forget AXIS_MOVE_VEHICLE_SIDE and use my input binding that is in the modDesc.
I dont want to change any other function or make it do anything else other than use a different inputAxis for control

Thanks all

Bilbo Beutlin (BBeutlin) 13.09.2018 05:56
With a trivial code you won't succeed.
The steering is handled in the Drivable:update() function and uses InputBinding.getDigitalInputAxis(InputBinding.AXIS_...). There's no easy way to change the InputBinding keys and devices dynamically.
You'll need to overwrite this update function with your own custom function by "Utils.overwrittenFunction()".

But where is the sense of your project?

William Beard (willbobber) 13.09.2018 19:22
Hi Bilbo Beutlin
The idea behind the script is that for tracked vehicles or skidsteers I want to be able to steer them with the joystick as IRL.
The script above is the base script I have cobbled together and I need to add an extra part to it to make the steering work.
When I add this

function joystick:update(dt)
if self.isEntered and self.isClient and not self.isHired then

if InputBinding.hasEvent(InputBinding.joystick_Toggle) then
joystick = not joystick
end

if joystick then

self.axisSide = InputBinding.getAnalogInputAxis(InputBinding.AXIS_CRANE_ARM)
if not InputBinding.isAxisZero(self.axisSide) then
self.axisSideIsAnalog = true;
end
else
self.axisSideIsAnalog = false;
end

if g_isServerStreamingVersion then self.axisSide = self.axisSide * 0.5; end -- This is the factor to slow down the steering
end
if self.isServer then
if self.steeringEnabled then
Drivable.updateVehiclePhysics(self, self.axisForward, self.axisForwardIsAnalog, self.axisSide, self.axisSideIsAnalog, self.doHandbrake, dt);
end
else
self:raiseDirtyFlags(self.drivableGroundFlag);
end
end

It works of a fashion, I can drive the tractor but it wont accelerate past 6mph and jumps between 6mph and 5mph, it will turn on the joystick script so I can steer with the joystick but it will only steer by turning the steering wheel lets say all the way right and the wheels stay straight until I move the joystick right then the wheels turn right.

I cant work out the piece of code to put in after this

if InputBinding.hasEvent(InputBinding.joystick_Toggle) then
joystick.enable = not joystick.enable

To make this work.

Bilbo Beutlin (BBeutlin) 13.09.2018 20:45
No - that will not work.
That's similiar you add a function "go left" where the original functions says "go right". You might imagine what happens? ;)

With your intention you must disable the original control and overwrite with your specific one.

William Beard (willbobber) 13.09.2018 21:45
No it doesnt work. It lets me steer of a fashion with the joystick so I know its possible but Im not sure how to write it.
I did think that I just needed to make the script change the inputBinding from one to the other but again had no luck doing it

Bilbo Beutlin (BBeutlin) 13.09.2018 22:39
"many roads lead to Rome" - but if I would have the intention of yours, I would overwork the Drivable:update() function to specific Input Bindings.
It's since the Drivable functions are well documented and easily to change. While grabbing deeper into InputBindings and general input stuff is a theme one has to examine first. Might take much longer than modifying the Drivable codes. If you're ready, probably the FS 21 is out. *g*

William Beard (willbobber) 13.09.2018 23:52
All the roads I set off on lead to Rome but I end up in Paris.
The section of code above is from the Drivable.update section. My thought was that I could add that section of code with a different inputBinding and it would work.
I did add the section of the update code with an update to the utils and the mod work. I could drive the tractor with the joystick but the issue I had with with that was that cruise control would turn on by itself and wouldn't turn off.


Bilbo Beutlin (BBeutlin) 14.09.2018 02:11
My recommendation:
Copy the Drivable:update() 1:1 into your surrogate update() function and establish it by
Drivable.update = Utils.overwrittenFunction(Drivable.update, myMod.update)
Then you modify the code in your "myMod.update()" to fulfill your purposes. There you can easily examine your basic variables and branch to your code or execute the original.

William Beard (willbobber) 14.09.2018 06:35
Thanks I will go back and have a second look at that. As I said I did do that and it worked apart from being unable to stop the vehicle or turn off the mod. But it does seem the way to go.
I'll let you know how I get on.
Thanks.

Bilbo Beutlin (BBeutlin) 14.09.2018 15:29
Just an idea:
Calling your mod'ed Drivable function "myMod.update() "is perhaps not the best method. It results this function is executed twice: first by your overwritten function and once more by the dispatcher which executes all xyz.update() functions. This may lead to unexpectable results.

Better you call your function different, let's say eg. "myMod.drivableUpdate()" which overwrites the orig. function.
Your "myMod.update()" you can leave empty (as dummy) if you handle your input within your "drivableUpdate()".

William Beard (willbobber) 17.09.2018 18:27
Hi
I have put this in now but still get no results.
I just get this error

Error: C:/Users/willb/Documents/My Games/FarmingSimulator2017/mods/Joystick_William/joystick.lua:7: '(' expected near '='


function Drivable.update = (Utils.overwrittenFunction(Drivable.update, joystick:update)
function joystick:update(dt)

if InputBinding.hasEvent(InputBinding.joystick_Toggle) then
joystick.enable = not joystick.enable
end

if joystick.enable then

if self.isEntered and self.isClient then

self.axisSide = InputBinding.getAnalogInputAxis(InputBinding.AXIS_CRANE_ARM)
if not InputBinding.isAxisZero(self.axisSide) then
self.axisSideIsAnalog = true;
end
else
self.axisSideIsAnalog = false;
end
if not self:getIsActiveForInput(false, true) then
if not self.axisSideIsAnalog or g_gui.currentGuiName == "PlacementScreen" then
self.axisSide = 0;
end
if not self.axisForwardIsAnalog or g_gui.currentGuiName == "PlacementScreen" then
self.axisForward = 0;
end
if self.steeringEnabled then
if g_gui:getIsGuiVisible() and g_gui.currentGuiName ~= "ChatDialog" then
self:setCruiseControlState(Drivable.CRUISECONTROL_STATE_OFF);
end
end
end
if g_isServerStreamingVersion then self.axisSide = self.axisSide * 0.5; end -- This is the factor to slow down the steering
end
if self.isServer then
if self.steeringEnabled then
Drivable.updateVehiclePhysics(self, self.axisForward, self.axisForwardIsAnalog, self.axisSide, self.axisSideIsAnalog, self.doHandbrake, dt);
end
else
self:raiseDirtyFlags(self.drivableGroundFlag);
end
end
end)

function joystick:draw()

if joystick then
g_currentMission:addHelpButtonText(string.format(g_i18n:getText("Turn_joystick_Off"), self.typeDesc), InputBinding.joystick_Toggle);
else
g_currentMission:addHelpButtonText(string.format(g_i18n:getText("Turn_joystick_On"), self.typeDesc), InputBinding.joystick_Toggle);
end
end;

Bilbo Beutlin (BBeutlin) 18.09.2018 00:47
Your code is faulty from structure and you have some syntax errors. You should use a syntax checker.

Try this basic structure:
---------------------------
joystick = {}
joystick.enable = false;

function joystick.drivableUpdate(dt)
-- here you copy the complete! code of the orig. Drivable:update()
-- then you change it according your requirements
end;

-- this overrides the orig. function
Drivable.update = Utils.overwrittenFunction(Drivable.update, joystick.drivableUpdate);

function joystick:update(dt)
-- here you handle your inputBindings and other specific stuff
end;

-- then you add the other obligatory functions like joystick:draw() etc.

-- and finally, if your mod is not a vehicle specialization
addModEventListener(joystick);
----------------------------

For the first tests it is useful you enable debug and print out essential values.

William Beard (willbobber) 18.09.2018 23:22
Hi Bilbo
I will give that a go. I tried all sorts of ways and couldnt get it right.

Does notepad++ have a syntax checker or is it an additional program?
Thanks

Bilbo Beutlin (BBeutlin) 19.09.2018 00:17
I use also Notepad++ and the inbuilt syntax highlighting and indication of 'begin-end' nesting is sufficient in most cases.
Futhermore are some special lua syntax checkers around which do a deeper analysis. If required, you'll find them via Google. So far I didn't use one, so I can't say what is best. ;)

William Beard (willbobber) 19.09.2018 00:57
Hi Bilbo
I have tried the script and it does indeed work.
You should change your name from Bilbo to Yoda.
I just now need to work out how to get the addHelpButtonText to work, it has stopped showing for some reason and then work out what to write in the joystick.update(dt).
I added the full update function in the top part and I can change the input so it works off either controller but I just need to work out to be able to switch controllers so I can pick and choose when I want to activate the mod.

If I add this
if InputBinding.hasEvent(InputBinding.joystick_Toggle) then
joystick.enable = not joystick.enable
end

if joystick.enable then

to the function joystick.drivableUpdate(dt) section the vehicle locks out when i turn it on and wont let me control it, when I deactivate I can control it again.
If I add it to the
function joystick:update(dt)
-- here you handle your inputBindings and other specific stuff
end;
section then it wont do anything.


Bilbo Beutlin (BBeutlin) 19.09.2018 01:46
If your inputBinding in your joystick:update() isn't recognized, probably the function isn't called at all or you have some other errors.
Did you add the important "addModEventListener(joystick)"? Without this your regular functions won't be called at all.
This is why I recommended to print out important steps/values.

For changing the "Drivable:update()" you must read and UNDERSTAND the code to implement your modifications.
See Drivable doc line# 427+ for accelerate/brake and line# 464+ for steering left/right. Here you must branch to your own or standard input depending on your settings.

The "addHelpButtonText" you can add either in your own "update()" or "draw()" function. For examples see the Drivable:draw() function.

And btw:
Always look into log after changes. Fatal errors you'll see there.

William Beard (willbobber) 20.09.2018 10:10
Hi Bilbo
I have been trying to get this to work but am failing. I did think that is there not a way to tell the game to use a different input without updating Drivable function?
I used the throttle script as a starting point
--------------------------------------------------------------------------------------------
Throttle = {};

local throttle_directory = g_currentModDirectory;
Throttle.enable = false


function Throttle:load()

self.throttle = {};
self.throttle.changeDelay = 250;
self.throttle.changeCurrentDelay = 0;
self.throttle.speedSent = self.throttle.speed;
self.throttle.vgetold = 0
self.throttle.elide = true

if self.cruiseControl.maxSpeed > 0 then
self.throttle.vreduced = 0
else
self.throttle.vreduced = self.cruiseControl.maxSpeed
end;
self.throttle.vset = self.cruiseControl.maxSpeed


end;

function Throttle:delete()
end;

function Throttle:mouseEvent(posX, posY, isDown, isUp, button)
end;

function Throttle:keyEvent(unicode, sym, modifier, isDown)
end;


function Throttle:update(dt)
if self.isEntered and self.isClient and not self.isHired then
-- do all the input handling
if self:getIsActiveForInput(false) and self.cruiseControl.isActive then
-- throttle on/off
if InputBinding.hasEvent(InputBinding.THROTTLE_TOGGLE) then
Throttle.enable = not Throttle.enable
end;
if Throttle.enable then
-- change modus
if InputBinding.hasEvent(InputBinding.THROTTLE_MODUS) then
if self.throttle.vset == self.cruiseControl.maxSpeed then
self.throttle.vset = self.throttle.vreduced
else
self.throttle.vset = self.cruiseControl.maxSpeed
end;
end;
-- Cruise Control
local inputW = InputBinding.getAnalogInputAxis(InputBinding.THROTTLE_AXIS)
if inputW == 0 and self.throttle.elide then -- problem on farm silo elide
self.throttle.elide = false
else
self.throttle.vget = math.floor((self.throttle.vset) / 0 * (inputW + 0.9))
if self.throttle.vget <= 0 then
self.throttle.vget = 0
if self.cruiseControl.speed == 1 and self:getLastSpeed() < 2 then
self:setCruiseControlState(Drivable.CRUISECONTROL_STATE_OFF)
end;
elseif self.throttle.vget >= self.cruiseControl.maxSpeed then
self.throttle.vget = self.cruiseControl.maxSpeed
end;
if self.throttle.vget ~= self.cruiseControl.speed and not (self.throttle.vget == 0 and self.cruiseControl.speed == 1) then
self.throttle.changeCurrentDelay = self.throttle.changeCurrentDelay - dt*4;
if self.throttle.changeCurrentDelay < 0 then
self.throttle.setspeed = self.cruiseControl.speed
if self.throttle.vget > self.cruiseControl.speed then
self.throttle.setspeed = self.throttle.vget
elseif self.throttle.vget < self.cruiseControl.speed and self.cruiseControl.speed > 1 then
if self:getLastSpeed() - self.cruiseControl.speed < -10 then
if self:getLastSpeed() < self.throttle.vget then
self.throttle.setspeed = self.throttle.vget
else
self.throttle.setspeed = self:getLastSpeed() - 1
end;
else
self.throttle.setspeed = self.cruiseControl.speed - 1
end;
end;
if self.cruiseControl.state == Drivable.CRUISECONTROL_STATE_OFF and self.throttle.vgetold ~= self.throttle.vget then
self:setCruiseControlState(Drivable.CRUISECONTROL_STATE_ACTIVE);
end;
self.throttle.wasSpeedChanged = true;
self:setCruiseControlMaxSpeed(self.throttle.setspeed);
self.throttle.changeCurrentDelay = self.throttle.changeDelay;
end;
self.throttle.vgetold = self.throttle.vget
else
self.throttle.wasSpeedChanged = false;
self.throttle.changeCurrentDelay = 0;
end;
self.throttle.elide = true
end;

if not self.throttle.wasSpeedChanged and self.cruiseControl.speed ~= self.throttle.speedSent then
if g_server ~= nil then
g_server:broadcastEvent(SetCruiseControlSpeedEvent:new(self, self.cruiseControl.speed), nil, nil, self);
else
g_client:getServerConnection():sendEvent(SetCruiseControlSpeedEvent:new(self, self.cruiseControl.speed));
end;
self.throttle.speedSent = self.cruiseControl.speed;
end;
end;
end;
end;
end;

function Throttle:updateTick(dt)
end;

function Throttle:readStream(streamId, connection)
end;

function Throttle:writeStream(streamId, connection)
end;


function Throttle:onEnter()
end;

function Throttle:onLeave()
end;

function Throttle:draw()
end;
----------------------------------------------------------------------------------------------
This script works by turning throttle on and off so you can maove forward and reverse with the joystick.
All the code in the middle is to change the behaviour of the vehicle to work on the joystick.
I dont want to change the behaviour just the input. The script you showed me works but it overwrites the whole Drivable function.
I dont want to overwrite the function as a whole just change the control when needed

Thanks

Bilbo Beutlin (BBeutlin) 20.09.2018 17:58
*sigh* I really don't know what's so difficult?

All you need is the basic structure as suggested above.
A tricky way without having to change the orig. code massively is replacing the input by variables.
For this you change from orig. code
InputBinding.AXIS_ACCELERATE_VEHICLE => joystick.inputForward;
InputBinding.AXIS_BRAKE_VEHICLE => joystick.inputBackward;
InputBinding.AXIS_MOVE_SIDE_VEHICLE => joystick.inputMoveSide;


At begin of your code you define:
----------
joystick = {}
joystick.enable = false;
joystick.inputForward = InputBinding.AXIS_ACCELERATE_VEHICLE;
joystick.inputBackward = InputBinding.AXIS_BRAKE_VEHICLE;
joystick.inputMoveSide = InputBinding.AXIS_MOVE_SIDE_VEHICLE;
----------


The switching you do in your
----------
function joystick:update(dt)
if InputBinding.hasEvent(InputBinding.joystick_Toggle) then
if joystick.enable == true then
joystick.enable = false;
joystick.inputForward = InputBinding.AXIS_ACCELERATE_VEHICLE;
joystick.inputBackward = InputBinding.AXIS_BRAKE_VEHICLE;
joystick.inputMoveSide = InputBinding.AXIS_MOVE_SIDE_VEHICLE;
else
joystick.enable = true;
joystick.inputForward = InputBinding.jsAxisForward;
joystick.inputBackward = InputBinding.jsAxisBackward;
joystick.inputMoveSide = InputBinding.jsAxisMoveSide;
end;
end;
end;
----------
The jsAxis... you define as usual in the modDesc.xml in <inputBindings>


Sure there are further possibilities - like I said above "many roads lead to Rome".
However going deeper into the code of input and inputBinding requires much additional research since there's nothing documented.
And merely adding some code like this "Throttle" is no programming, but simply a "hack".

William Beard (willbobber) 17.10.2018 20:09
Hi bilbo
Just wanted to say thankyou for your help with this. I have it working now and its just how I wanted it.

Many thanks for your time and patience




Note: Log in to post. Create a new account here.