···14141515[](https://du-lua.dev/#/editor/github/Jericho1060/du-elevator)
16161717-# Instalation
1717+# Installation
18181919-### Important notice
1919+## Important notice
20202121- the script was only tested on a remote control
22222323-### Links
2323+## Links
24242525You must link the following elements to the remote control (order doesn't matter, the script will detect them automatically):
2626- Link the core to the remote control
2727- Link the fuel tank to the remote control (only if you want to see the fuel gauge)
28282929-### Setup
2929+## Setup
30303131- you need at least a vertical engine pointing the bottom
3232- you need at least one engine on each horizontal direction (forward, right, left, backward)
···38383939You may need to adjust the power depending on the weight of your construct and on the total weight you want to lift.
40404141-### Installation of the script
4141+## Installation of the script
42424343- copy the content of the config.json file and paste it on the remote control (right click on the remote control -> advanced -> paste lua configuration from clipboard)
44444545-### Lua parameters
4545+## Lua parameters
46464747- `DEBUG` : if set to true, the script will print debug messages in the chat
4848-- `BaseAltitude` : the default altitude of the elevator when landed on the floor
4848+- `ShowParentingWidget` : if set to true, the script will display a widget showing the parenting of the elevator
4949+- `ShowControlUnitWidget` : if set to true, the script will display the default control unit widget
5050+- `YawSpeedFactor` : the speed multiplier for auto yaw.
4951- `StrafeSpeedFactor` : the speed multiplier when strafing. adjust it depending on how many engines you have on the sides
50525151-### Usage
5353+## Usage
5454+5555+### Bookmarks
5656+5757+You can set bookmarks to save altitudes with a name. Edit the script and go to Unit > OnStart, you will see a Bookmark list (table) at the top of the script. You can edit the list to add your custom points. The first element will be used ad the base altitude when the elevator is landed on the floor.
5858+5959+Navigating using bookmarks :
6060+- you can navigate in the list in the HUD with your keyboard keys arrows up and down
6161+- pressing the key arrow right will start the travel to the selected bookmark
6262+6363+Current selected bookmark is displayed in blue, the current bookmark the elevator is going to is displayed in green.
6464+6565+### Chat Command
52665367- open the "lua" chat channel and type `goto:<altitude>` to go to the specified altitude with `<altitude>` the altitude in meters, if the altitude is missing or invalid, it will send you the `BaseAltitude`
5468···61756276# Support or donation
63776464-if you like it, [<img src="https://github.com/Jericho1060/DU-Industry-HUD/blob/main/ressources/images/ko-fi.png?raw=true" width="150">](https://ko-fi.com/jericho1060)7878+if you like it, [<img src="https://github.com/Jericho1060/DU-Industry-HUD/blob/main/ressources/images/ko-fi.png?raw=true" width="150">](https://ko-fi.com/jericho1060)
+291-51
Source/Unit/OnStart.lua
···22 DU-ELEVATOR by Jericho
33]]
4455+--[[
66+ Bookmarks: you can store several altitudes here to navigate easily
77+]]
88+local Bookmarks = {
99+ { name = 'Start Point', altitude = 285 },
1010+ { name = 'Hovering', altitude = 300 },
1111+ { name = 'Floor 1', altitude = 350 },
1212+ { name = 'Floor 2', altitude = 500 }
1313+}
1414+1515+--[[
1616+ LUA PARAMETERS
1717+]]
518__DEBUG = false --export: Debug mode, will print more information in the console
1919+ShowParentingWidget = false --export: Show the parenting widget
2020+ShowControlUnitWidget = false --export: show the default widget of the construct
621722--[[
823 Version Management
924]]
1010-1111-local version = "V 1.0.0"
2525+local version = "V 1.1.0"
1226local log_split = "================================================="
1327--printing version in lua chat
1428system.print(log_split)local a=""local b=math.ceil((50-#version-2)/2)for c=1,b,1 do a=a..'='end;a=a.." "..version.." "for c=1,b,1 do a=a..'='end;system.print(a)system.print(log_split)
1515-2929+--[[
3030+ Fuel Tanks Metatable By Jericho
3131+ Version: 1.0.0
3232+ Unminified Source available at: https://github.com/Jericho1060/DualUniverse/blob/master/ElementsMetatable/FuelTank.lua
3333+]]
3434+FuelTank={__index={name='',percentage=0,timeLeft=0,lastRefresh=0,fuelType='rocket',refresh=function(self)local a=system.getUtcTime()if lastRefresh~=a then local b=self.getClass():lower()if b:find('atmo')then self.fuelType="atmo"elseif b:find('space')then self.fuelType="space"end;local c=self.getWidgetData()local d=json.decode(c)self.name=d.name;self.percentage=d.percentage;self.timeLeft=tonumber(d.timeLeft)self.lastRefresh=a end end,getName=function(self)self:refresh()return self.name end,getPercentFilled=function(self)self:refresh()return self.percentage end,getSecondsLeft=function(self)self:refresh()return self.timeLeft end,getFuelType=function(self)self:refresh()return self.fuelType end,getTimeLeftString=function(self)local e=self:getSecondsLeft()if e==nil or e<=0 then return"-"end;days=string.format("%2.f",math.floor(e/(3600*24)))hours=string.format("%2.f",math.floor(e/3600-days*24))mins=string.format("%2.f",math.floor(e/60-hours*60-days*24*60))secs=string.format("%2.f",math.floor(e-hours*3600-days*24*60*60-mins*60))str=""if tonumber(days)>0 then str=str..days.."d "end;if tonumber(hours)>0 then str=str..hours.."h "end;if tonumber(mins)>0 then str=str..mins.."m "end;if tonumber(secs)>0 then str=str..secs.."s"end;return str end,dump=function(self)self:refresh()system.print('{name="'..self.name..'",percentage='..self.percentage..',timeLeft='..self.timeLeft..',fuelType="'..self.fuelType..'"}')end}}
1635--[[
1736 Detecting elements linked
1837]]
1938core = nil
2039fuelTanks = {}
2121-2240for slot_name, slot in pairs(unit) do
2341 if
2424- type(slot) == "table"
2525- and type(slot.export) == "table"
2626- and slot.getClass
4242+ type(slot) == "table"
4343+ and type(slot.export) == "table"
4444+ and slot.getClass
2745 then
2846 local class = slot.getClass():lower()
2947 if class:find("coreunit") then
3048 core = slot
3149 elseif class:find("fuelcontainer") then
3232- table.insert(fuelTanks, slot)
5050+ fuelTanks[#fuelTanks + 1] = setmetatable(slot, FuelTank)
3351 end
3452 end
3553end
···6482unit.deactivateGroundEngineAltitudeStabilization()
65836684-- Parenting widget
6767-parentingPanelId = system.createWidgetPanel("Docking")
6868-parentingWidgetId = system.createWidget(parentingPanelId,"parenting")
6969-system.addDataToWidget(unit.getWidgetDataId(),parentingWidgetId)
7070-7171--- Fuel widget generation (Updated code By Jericho)
7272---TODO: replace by custom HUD
7373-if (#fuelTanks > 0) then
7474- fuelTankPanelId = system.createWidgetPanel("Fuel Tanks")
7575- fuelTankWidgetId = system.createWidget(fuelTankPanelId,"fuel_container")
7676- for _,tank in pairs(fuelTanks) do
7777- system.addDataToWidget(tank.getWidgetDataId(),fuelTankWidgetId)
7878- end
8585+if ShowParentingWidget then
8686+ parentingPanelId = system.createWidgetPanel("Docking")
8787+ parentingWidgetId = system.createWidget(parentingPanelId,"parenting")
8888+ system.addDataToWidget(unit.getWidgetDataId(),parentingWidgetId)
8989+end
9090+if not ShowControlUnitWidget then
9191+ unit.hideWidget()
7992end
80938194--[[
···87100if __DEBUG then system.print('ConstructInitPos: ' .. json.encode(ConstructInitPos)) end
88101BaseForward = vec3(construct.getWorldForward())
89102if __DEBUG then system.print('BaseForward: ' .. json.encode(BaseForward)) end
9090-BaseAltitute = 285 --export: the start altitude of the elevator (lower position altitude)
103103+BaseAltitute = Bookmarks[1].altitude --getting the base altitude from the 1st bookmark
104104+--init a value to store the target altitude
105105+TargetAltitude = core.getAltitude() --by default to the start altitude to avoid falling down if in the air
106106+107107+ElevatorData = {
108108+ isBreaking = brakeInput == 1,
109109+ verticalSpeed = 0,
110110+ verticalSpeedSigned = 0,
111111+ lateralSpeed = 0,
112112+ longitudinalSpeed = 0,
113113+ altitude = TargetAltitude,
114114+ direction = 'stabilizing',
115115+}
9111692117--TODO: to replace with a computing in flush from the current acceleration and the friction acceleration (construct.getWorldAirFrictionAcceleration)
93118MaxSpeed = construct.getFrictionBurnSpeed() -- for security to avoid burning if going too fast
94119if __DEBUG then system.print('MaxSpeed: ' .. MaxSpeed) end
951209696---init a value to store the target altitude
9797-TargetAltitude = core.getAltitude() --by default to the start altitude to avoid falling down if in the air
121121+--base selected bookmark is 0 -> none selected as index 0 doesn't exist in lua
122122+selectedBookmarkIndex = 0
123123+124124+--[[
125125+ init the HUD
126126+]]
127127+system.showScreen(true)
9812899129--[[
100130 Kinematics functions by Jaylebreak
101131 Source available at https://gitlab.com/JayleBreak/dualuniverse/-/blob/master/DUflightfiles/autoconf/custom/kinematics.lua
102102-]]
132132+]]
103133function computeAccelerationTime(initial, acceleration, final) return (final - initial)/acceleration end
104134function computeDistanceAndTime(initial, final, mass, thrust, t50, brakeThrust)
105135 t50 = t50 or 0
···126156 while math.abs(timeToMax - lasttime) > 0.25 do
127157 local t = (timeToMax + lasttime)/2
128158 if speedchk(v(t)) then
129129- timeToMax = t
159159+ timeToMax = t
130160 else
131161 lasttime = t
132162 end
···146176end
147177148178--[[
149149- Commpute angle betwwen vectors by Jericho
179179+ Compute angle between vectors by Jericho
150180]]
151181function signedAngleBetween(vec1, vec2, planeNormal)
152182 local normVec1 = vec1:project_on_plane(planeNormal):normalize()
···159189 end
160190 return angle
161191end
162162-192192+--[[
193193+ formatting numbers by adding a space between thousands by Jericho
194194+]]
195195+function format_number(a)local b=a;while true do b,k=string.gsub(b,"^(-?%d+)(%d%d%d)",'%1 %2')if k==0 then break end end;local c=string.sub(b,-2)if c=='.0'then b=string.sub(b,1,b:len()-2)end;return b end
163196--[[
164197 DU-LUA-Framework by Jericho
165198 Permit to code easier by grouping most of the code in a single event Unit > Start
···187220string = setmetatable(string, String)
188221string.__index = string
189222190190-local systemOnUpdate = {
191191- function ()
192192- Nav:update()
193193- end
194194-}
195195-196223local systemOnFlush = {
197224 function()
198198- local yawSpeedFactor = 1.5
225225+ local yawSpeedFactor = 1 --export: the auto yaw speed multiplier
199226 local yawAccelerationFactor = 3
200227 local lateralAntiDriftFactor = 1
201228 local lateralStrafeFactor = 5
202229 local brakeSpeedFactor = 1
203203- local brakeFlatFactor = 4
230230+ local brakeFlatFactor = 4
204231 local autoBrakeSpeed = 15
205232 -- validate params
206233 brakeSpeedFactor = math.max(brakeSpeedFactor, 0.01)
···233260234261 -- Axis
235262 local worldVertical = vec3(core.getWorldVertical())
263263+ local worldRight = vec3(core.getWorldRight())
264264+ local worldForward = vec3(core.getWorldForward())
236265 local constructUp = vec3(construct.getWorldOrientationUp())
237266 local constructForward = vec3(construct.getWorldOrientationForward())
238267 local constructRight = vec3(construct.getWorldOrientationRight())
···248277 local dontKeepCollinearity = 1 -- for easier reading
249278 local tolerancePercentToSkipOtherPriorities = 1 -- if we are within this tolerance (in%), we don't go to the next priorities
250279280280+ --fake braking value
281281+ local canBrake = false
282282+251283 -- keeping the start position alignement
252284 local StrafeSpeedFactor = 50 --export: useg to increase the force of the alignement, decrease the value if the alignement is too strong or increase it if it's too slow
253285 local positionDifference = constructTargetPosition - constructWorldPosition
254286 local lateralOffset = positionDifference:project_on(constructRight):len() * utils.sign(positionDifference:dot(constructRight))
287287+ ElevatorData.lateralSpeed = constructVelocity:project_on(worldRight):len()
288288+ local lateralDistance = math.abs(lateralOffset)
255289 local longitudinalOffset = positionDifference:project_on(constructForward):len() * utils.sign(positionDifference:dot(constructForward))
290290+ ElevatorData.longitudinalSpeed = constructVelocity:project_on(worldForward):len()
291291+ local longitudinalDistance = math.abs(longitudinalOffset)
292292+ if (lateralDistance < .25) and (longitudinalDistance < .25) then canBrake = true end
293293+ if ((ElevatorData.lateralSpeed*3.6) > lateralDistance) or ((ElevatorData.longitudinalSpeed*3.6) > longitudinalDistance) then canBrake = true end
256294 lateralPID:inject(lateralOffset)
257295 Nav.axisCommandManager:setThrottleCommand(axisCommandId.lateral, lateralPID:get() * StrafeSpeedFactor)
258296 longitudinalPID:inject(longitudinalOffset)
···268306 pitchPID:inject(targetPitchDeg - currentPitchDeg)
269307 yawPID:inject(-targetYawDeg*yawSpeedFactor)
270308271271- local constructYawTargetVelocity = -combinedRollYawInput * yawSpeedFactor
272272- local constructYawTargetAcceleration = yawAccelerationFactor * (constructYawTargetVelocity - constructYawVelocity)
273309 local constructTargetAngularVelocity = rollPID:get() * constructForward + pitchPID:get() * constructRight + yawPID:get() * constructUp
274310275311 Nav:setEngineTorqueCommand('torque', constructTargetAngularVelocity, keepCollinearity, 'airfoil', '', '', tolerancePercentToSkipOtherPriorities)
276312277313 -- moving up or down from TargetAltitude
278278- local verticalSpeed = constructVelocity:project_on(worldVertical):len()
279279- local verticalSpeedSigned = verticalSpeed * -utils.sign(constructVelocity:dot(worldVertical))
314314+ ElevatorData.verticalSpeed = constructVelocity:project_on(worldVertical):len()
315315+ ElevatorData.verticalSpeedSigned = ElevatorData.verticalSpeed * -utils.sign(constructVelocity:dot(worldVertical))
280316 local brakeDistance = 0
281317 local maxBrake = construct.getMaxBrake()
282318 if maxBrake ~= nil then
283283- brakeDistance, _ = computeDistanceAndTime(verticalSpeed, 0, construct.getInertialMass(), 0, 0, maxBrake - (core.getGravityIntensity() * construct.getInertialMass()) * utils.sign(verticalSpeedSigned))
319319+ brakeDistance, _ = computeDistanceAndTime(ElevatorData.verticalSpeed, 0, construct.getInertialMass(), 0, 0, maxBrake - (core.getGravityIntensity() * construct.getInertialMass()) * utils.sign(ElevatorData.verticalSpeedSigned))
284320 end
285285- local coreAltitude = core.getAltitude()
286286- local distance = TargetAltitude - coreAltitude
321321+ ElevatorData.altitude = core.getAltitude()
322322+ local distance = TargetAltitude - ElevatorData.altitude
287323 local targetDistance = utils.sign(distance) * (math.abs(distance)-brakeDistance)
288324 distancePID:inject(targetDistance)
289289- if distancePID:get() > 0.5 or verticalSpeedSigned < -MaxSpeed then --using a 0.5 meter deadband for stabilizing
325325+ if distancePID:get() > 0.5 or ElevatorData.verticalSpeedSigned < -MaxSpeed then --using a 0.5 meter deadband for stabilizing
290326 Nav.axisCommandManager:setThrottleCommand(axisCommandId.vertical, 1)
291291- elseif distancePID:get() < -0.5 or verticalSpeedSigned > MaxSpeed then
327327+ ElevatorData.direction = 'up'
328328+ if ElevatorData.verticalSpeedSigned < 0 then
329329+ finalBrakeInput = 1
330330+ end
331331+ elseif distancePID:get() < -0.5 or ElevatorData.verticalSpeedSigned > MaxSpeed then
292332 Nav.axisCommandManager:setThrottleCommand(axisCommandId.vertical, -1)
333333+ ElevatorData.direction = 'down'
334334+ if ElevatorData.verticalSpeedSigned > 0 then
335335+ finalBrakeInput = 1
336336+ end
293337 else
294338 Nav.axisCommandManager:resetCommand(axisCommandId.vertical)
339339+ ElevatorData.direction = 'stabilizing'
295340 end
296341297342 --Brakes
298343 if
299299- (math.abs(distance) < verticalSpeed)
300300- or verticalSpeed > MaxSpeed
301301- or (brakeDistance > math.abs(distance))
302302- or (finalBrakeInput == 0 and autoBrakeSpeed > 0 and Nav.axisCommandManager.throttle == 0 and constructVelocity:len() < autoBrakeSpeed)
344344+ (math.abs(distance) < (ElevatorData.verticalSpeed*3.6) and canBrake)
345345+ or (ElevatorData.verticalSpeed >= MaxSpeed)
346346+ or ((brakeDistance > math.abs(distance)) and canBrake)
347347+ or (finalBrakeInput == 0 and autoBrakeSpeed > 0 and Nav.axisCommandManager.throttle == 0 and constructVelocity:len() < autoBrakeSpeed)
303348 then
304349 finalBrakeInput = 1
305350 end
351351+ ElevatorData.isBreaking = (finalBrakeInput == 1)
306352 local brakeAcceleration = -finalBrakeInput * (brakeSpeedFactor * constructVelocity + brakeFlatFactor * constructVelocityDir)
307307-353353+308354 Nav:setEngineForceCommand('brake', brakeAcceleration)
309355310356 -- AutoNavigation regroups all the axis command by 'TargetSpeed'
···323369 autoNavigationEngineTags = autoNavigationEngineTags .. ' , ' .. longitudinalEngineTags
324370 autoNavigationAcceleration = autoNavigationAcceleration + longitudinalAcceleration
325371 if (Nav.axisCommandManager:getTargetSpeed(axisCommandId.longitudinal) == 0 or -- we want to stop
326326- Nav.axisCommandManager:getCurrentToTargetDeltaSpeed(axisCommandId.longitudinal) < - Nav.axisCommandManager:getTargetSpeedCurrentStep(axisCommandId.longitudinal) * 0.5) -- if the longitudinal velocity would need some braking
372372+ Nav.axisCommandManager:getCurrentToTargetDeltaSpeed(axisCommandId.longitudinal) < - Nav.axisCommandManager:getTargetSpeedCurrentStep(axisCommandId.longitudinal) * 0.5) -- if the longitudinal velocity would need some braking
327373 then
328374 autoNavigationUseBrake = true
329375 end
···367413 end
368414}
369415416416+local systemOnUpdate = {
417417+ function ()
418418+ Nav:update()
419419+ end,
420420+ function ()
421421+ --That function is used for rendering the HUD, you can update it or replace all its content to customise it. You can also remove it if you don't want a HUD
422422+ local direction = 'Stabilizing'
423423+ if ElevatorData.verticalSpeedSigned > 0 then
424424+ direction = 'Up'
425425+ elseif ElevatorData.verticalSpeedSigned < 0 then
426426+ direction = 'Down'
427427+ end
428428+429429+ local brakeStatus = 'Off'
430430+ if ElevatorData.isBreaking then
431431+ brakeStatus = 'On'
432432+ end
433433+ local html = [[
434434+ <style>
435435+ * {
436436+ font-size:1vh !important;
437437+ }
438438+ .hud {
439439+ position: absolute;
440440+ left: 5vh;
441441+ top: 5vh;
442442+ right: 5vh;
443443+ display: flex;
444444+ flex-direction: column;
445445+ justify-content: left;
446446+ align-items: left;
447447+ }
448448+ .widget_container {
449449+ border: 2px solid orange;
450450+ border-radius:.5vh;
451451+ background-color: rgba(0, 0, 0, .5);
452452+ display: flex;
453453+ flex-direction: column;
454454+ padding:.5vh;
455455+ margin-top:1vh;
456456+ }
457457+ .widget_container div {
458458+ display: flex;
459459+ flex-direction: row;
460460+ justify-content: space-between;
461461+ }
462462+ .widget_container div div {
463463+ margin:.25vh;
464464+ }
465465+ .widget_container div div:first-child {
466466+ text-transform: uppercase;
467467+ font-weight: bold;
468468+ }
469469+ .selected {
470470+ color: teal;
471471+ }
472472+ .movingto {
473473+ color: green;
474474+ }
475475+ .atmo .gauge {
476476+ background-color: #22d3ee;
477477+ }
478478+ .atmo .gauge_label {
479479+ color: #155e75;
480480+ }
481481+ .space .gauge {
482482+ background-color: #fbbf24;
483483+ }
484484+ .space .gauge_label {
485485+ color: #92400e;
486486+ }
487487+ .rocket .gauge {
488488+ background-color: #a78bfa;
489489+ }
490490+ .rocket .gauge_label {
491491+ color: #5b21b6;
492492+ }
493493+ .gauge_container {
494494+ display:block;
495495+ width:100%;
496496+ min-width:10vw;
497497+ position: relative;
498498+ border: 1px solid black;
499499+ background-color: rgba(0, 0, 0, .75);
500500+ height: 1.5vh;
501501+ }
502502+ .gauge {
503503+ position: absolute;
504504+ z-index:1;
505505+ top:-.25vh;
506506+ left:-.25vh;
507507+ height:100%;
508508+ margin:0;
509509+ background-color: cyan;
510510+ }
511511+ .gauge_label {
512512+ position:relative;
513513+ display:block;
514514+ z-index:10;
515515+ margin:0;
516516+ padding:0;
517517+ width:100%;
518518+ text-align:center;
519519+ }
520520+ </style>
521521+ <div class="hud">
522522+ <div class="widget_container">
523523+ <div>
524524+ <div>Base Altitude</div><div>]] .. format_number(utils.round(BaseAltitute)) .. [[m</div>
525525+ </div>
526526+ <div>
527527+ <div>Target Altitude</div><div>]] .. format_number(utils.round(TargetAltitude)) .. [[m</div>
528528+ </div>
529529+ <div>
530530+ <div>Current Altitude</div><div>]] .. format_number(utils.round(ElevatorData.altitude)) .. [[m</div>
531531+ </div>
532532+ <div>
533533+ <div>Vertical Speed</div><div>]] .. format_number(math.abs(utils.round(ElevatorData.verticalSpeed*3.6))) .. [[km/h</div>
534534+ </div>
535535+ <div>
536536+ <div>Lateral Speed</div><div>]] .. format_number(math.abs(utils.round(ElevatorData.lateralSpeed*3.6))) .. [[km/h</div>
537537+ </div>
538538+ <div>
539539+ <div>Longitudinal Speed</div><div>]] .. format_number(math.abs(utils.round(ElevatorData.longitudinalSpeed*3.6))) .. [[km/h</div>
540540+ </div>
541541+ <div>
542542+ <div>Direction</div><div>]] .. ElevatorData.direction .. [[</div>
543543+ </div>
544544+ <div>
545545+ <div>Brake</div><div>]] .. brakeStatus .. [[</div>
546546+ </div>
547547+ </div>
548548+ ]]
549549+ if #fuelTanks > 0 then
550550+ html = html .. '<div class="widget_container">'
551551+ for _, tank in pairs(fuelTanks) do
552552+ html = html .. '<div><div>' .. tank:getName() .. '</div><div>' .. tank:getTimeLeftString() .. '</div></div><div class="gauge_container ' .. tank:getFuelType() .. '"><div class="gauge" style="width:' .. utils.round(tank:getPercentFilled()) .. '%;"></div><div class="gauge_label"><div style="margin-left:auto;margin-right:auto;padding:0;margin-top:-.25vh;">' .. format_number(utils.round(tank:getPercentFilled())) .. '%</div></div></div>'
553553+ end
554554+ html = html .. '</div>'
555555+ end
556556+ if #Bookmarks > 0 then
557557+ html = html .. '<div class="widget_container"><div><div>Bookmarks</div></div>'
558558+ for index, bookmark in ipairs(Bookmarks) do
559559+ local class=''
560560+ if selectedBookmarkIndex == index then
561561+ class = 'selected'
562562+ end
563563+ local displayName = bookmark.name
564564+ if selectedBookmarkIndex > 0 and bookmark.altitude == TargetAltitude then
565565+ displayName = ' >> ' .. bookmark.name
566566+ class = 'movingto'
567567+ end
568568+ html = html .. '<div class="' .. class .. '"><div>' .. displayName .. '</div><div>' .. format_number(bookmark.altitude) .. 'm</div></div>'
569569+ end
570570+ html = html .. '</div>'
571571+ end
572572+573573+ html = html .. '</div>'
574574+ system.setScreen(html)
575575+ end
576576+}
370577371578Script.system:onUpdate(systemOnUpdate)
372579Script.system:onFlush(systemOnFlush)
373580374581--[[
582582+ Actions
583583+]]
584584+local systemActionsStart = {}
585585+586586+systemActionsStart[Script.system.ACTIONS.DOWN] = function ()
587587+ if selectedBookmarkIndex < #Bookmarks then
588588+ selectedBookmarkIndex = selectedBookmarkIndex + 1
589589+ else
590590+ selectedBookmarkIndex = 1
591591+ end
592592+ if __DEBUG then system.print('Selected Bookmark Index: ' .. selectedBookmarkIndex) end
593593+end
594594+systemActionsStart[Script.system.ACTIONS.UP] = function ()
595595+ if selectedBookmarkIndex > 1 then
596596+ selectedBookmarkIndex = selectedBookmarkIndex - 1
597597+ else
598598+ selectedBookmarkIndex = #Bookmarks
599599+ end
600600+ if __DEBUG then system.print('Selected Bookmark Index: ' .. selectedBookmarkIndex) end
601601+end
602602+systemActionsStart[Script.system.ACTIONS.STRAFE_RIGHT] = function ()
603603+ brakeInput = 0
604604+ if selectedBookmarkIndex > 0 then
605605+ TargetAltitude = Bookmarks[selectedBookmarkIndex].altitude
606606+ if __DEBUG then system.print('Target Altitude: ' .. TargetAltitude) end
607607+ else
608608+ system.print('No bookmark selected')
609609+ end
610610+end
611611+Script.system:onActionStart(systemActionsStart) --loading all "actionStart" functions
612612+613613+--[[
375614 Chat Commands
376615]]
377616···383622 else
384623 system.print('Unknown command')
385624 end
386386-end)625625+end)
626626+
+1-1
config.json
···11-{"events":[],"handlers":[{"code":"Script.system:update()","filter":{"args":[],"signature":"onUpdate()","slotKey":"-4"},"key":"9"},{"code":"Script.system:inputText(text)","filter":{"args":[{"variable":"*"}],"signature":"onInputText(text)","slotKey":"-4"},"key":"10"},{"code":"Script.system:flush()","filter":{"args":[],"signature":"onFlush()","slotKey":"-4"},"key":"11"},{"code":"Script.system:actionStop(action)","filter":{"args":[{"variable":"*"}],"signature":"onActionStop(action)","slotKey":"-4"},"key":"12"},{"code":"Script.system:actionStart(action)","filter":{"args":[{"variable":"*"}],"signature":"onActionStart(action)","slotKey":"-4"},"key":"13"},{"code":"Script.system:actionLoop(action)","filter":{"args":[{"variable":"*"}],"signature":"onActionLoop(action)","slotKey":"-4"},"key":"14"},{"code":"Script.player:parentChanged(oldId, newId)","filter":{"args":[{"variable":"*"},{"variable":"*"}],"signature":"onParentChanged(oldId,newId)","slotKey":"-3"},"key":"3"},{"code":"Script.construct:VRStationEntered(id)","filter":{"args":[{"variable":"*"}],"signature":"onVRStationEntered(id)","slotKey":"-2"},"key":"4"},{"code":"Script.construct:undocked(id)","filter":{"args":[{"variable":"*"}],"signature":"onUndocked(id)","slotKey":"-2"},"key":"5"},{"code":"Script.construct:PvPTimer(active)","filter":{"args":[{"variable":"*"}],"signature":"onPvPTimer(active)","slotKey":"-2"},"key":"6"},{"code":"Script.construct:playerBoarded(id)","filter":{"args":[{"variable":"*"}],"signature":"onPlayerBoarded(id)","slotKey":"-2"},"key":"7"},{"code":"Script.construct:constructDocked(id)","filter":{"args":[{"variable":"*"}],"signature":"onConstructDocked(id)","slotKey":"-2"},"key":"8"},{"code":"Script.unit:timer(tag)","filter":{"args":[{"variable":"*"}],"signature":"onTimer(tag)","slotKey":"-1"},"key":"0"},{"code":"Script.unit:stop()","filter":{"args":[],"signature":"onStop()","slotKey":"-1"},"key":"1"},{"code":"--[[\n DU-ELEVATOR by Jericho\n]]\n\n__DEBUG = false --export: Debug mode, will print more information in the console\n\n--[[\n Version Management\n]]\n\nlocal version = \"V 1.0.1\"\nlocal log_split = \"=================================================\"\n--printing version in lua chat\nsystem.print(log_split)local a=\"\"local b=math.ceil((50-#version-2)/2)for c=1,b,1 do a=a..'='end;a=a..\" \"..version..\" \"for c=1,b,1 do a=a..'='end;system.print(a)system.print(log_split)\n\n--[[\n Detecting elements linked\n]]\ncore = nil\nfuelTanks = {}\n\nfor slot_name, slot in pairs(unit) do\n if\n type(slot) == \"table\"\n and type(slot.export) == \"table\"\n and slot.getClass\n then\n local class = slot.getClass():lower()\n if class:find(\"coreunit\") then\n core = slot\n elseif class:find(\"fuelcontainer\") then\n table.insert(fuelTanks, slot)\n end\n end\nend\n\n--[[\n stopping the script if required elements are not linked\n]]\nif core == nil then\n system.print('Core unit is not linked, exiting the script')\n unit.exit()\nend\n\n--[[\n Default Unit Start Mechanics\n]]\npitchInput = 0\npitchInputFromDevice = 0\nrollInput = 0\nyawInput = 0\nverticalStrafeInput = 0\nlateralStrafeInput = 0\nbrakeInput = 1 --braking by default\ngoingBack = false\ngoingForward = false\nshiftPressed = false\njumpDelta = 0\nbaseAcceleration = 0.8\n\nNav = Navigator.new(system, core, unit)\nNav.axisCommandManager:setupCustomTargetSpeedRanges(axisCommandId.longitudinal, {100, 500, 1000, 2000,3000, 5000})\nNav.axisCommandManager:setTargetGroundAltitude(0)\nunit.deactivateGroundEngineAltitudeStabilization()\n\n-- Parenting widget\nparentingPanelId = system.createWidgetPanel(\"Docking\")\nparentingWidgetId = system.createWidget(parentingPanelId,\"parenting\")\nsystem.addDataToWidget(unit.getWidgetDataId(),parentingWidgetId)\n\n-- Fuel widget generation (Updated code By Jericho)\n--TODO: replace by custom HUD\nif (#fuelTanks > 0) then\n fuelTankPanelId = system.createWidgetPanel(\"Fuel Tanks\")\n fuelTankWidgetId = system.createWidget(fuelTankPanelId,\"fuel_container\")\n for _,tank in pairs(fuelTanks) do\n system.addDataToWidget(tank.getWidgetDataId(),fuelTankWidgetId)\n end\nend\n\n--[[\n Storing base position and orientation of the elevator\n]]\nConstructInitPos = construct.getWorldPosition()\n--replace that pos by a hand written value to be sure the construct will always realign the same position\n--ConstructInitPos = {-1231538.185042, 1201297.6879598, -2617220.2766464}\nif __DEBUG then system.print('ConstructInitPos: ' .. json.encode(ConstructInitPos)) end\nBaseForward = vec3(construct.getWorldForward())\nif __DEBUG then system.print('BaseForward: ' .. json.encode(BaseForward)) end\nBaseAltitute = 285 --export: the start altitude of the elevator (lower position altitude)\n\n--TODO: to replace with a computing in flush from the current acceleration and the friction acceleration (construct.getWorldAirFrictionAcceleration)\nMaxSpeed = construct.getFrictionBurnSpeed() -- for security to avoid burning if going too fast\nif __DEBUG then system.print('MaxSpeed: ' .. MaxSpeed) end\n\n--init a value to store the target altitude\nTargetAltitude = core.getAltitude() --by default to the start altitude to avoid falling down if in the air\n\n--[[\n Kinematics functions by Jaylebreak\n Source available at https://gitlab.com/JayleBreak/dualuniverse/-/blob/master/DUflightfiles/autoconf/custom/kinematics.lua\n]] \nfunction computeAccelerationTime(initial, acceleration, final) return (final - initial)/acceleration end\nfunction computeDistanceAndTime(initial, final, mass, thrust, t50, brakeThrust)\n t50 = t50 or 0\n brakeThrust = brakeThrust or 0\n local speedUp = initial < final\n local a0 = thrust / (speedUp and mass or -mass)\n local b0 = -brakeThrust/mass\n local totA = a0+b0\n if initial == final then\n return 0, 0\n elseif speedUp and totA <= 0 or not speedUp and totA >= 0 then\n return -1, -1\n end\n local distanceToMax, timeToMax = 0, 0\n if a0 ~= 0 and t50 > 0 then\n local c1 = math.pi/t50/2\n local v = function(t)\n return a0*(t/2 - t50*math.sin(c1*t)/math.pi) + b0*t + initial\n end\n local speedchk = speedUp and function(s) return s >= final end or function(s) return s <= final end\n timeToMax = 2*t50\n if speedchk(v(timeToMax)) then\n local lasttime = 0\n while math.abs(timeToMax - lasttime) > 0.25 do\n local t = (timeToMax + lasttime)/2\n if speedchk(v(t)) then\n timeToMax = t \n else\n lasttime = t\n end\n end\n end\n local K = 2*a0*t50^2/math.pi^2\n distanceToMax = K*(math.cos(c1*timeToMax) - 1) + (a0+2*b0)*timeToMax^2/4 + initial*timeToMax\n if timeToMax < 2*t50 then\n return distanceToMax, timeToMax\n end\n initial = v(timeToMax)\n end\n local a = a0+b0\n local t = computeAccelerationTime(initial, a, final)\n local d = initial*t + a*t*t/2\n return distanceToMax+d, timeToMax+t\nend\n\n--[[\n Commpute angle betwwen vectors by Jericho\n]]\nfunction signedAngleBetween(vec1, vec2, planeNormal)\n local normVec1 = vec1:project_on_plane(planeNormal):normalize()\n local normVec2 = vec2:normalize()\n local v1v2dot = normVec1:dot(normVec2)\n local angle = math.acos(utils.clamp(v1v2dot,-1,1))\n local crossProduct = vec1:cross(vec2)\n if crossProduct:dot(planeNormal) < 0 then\n return -angle\n end\n return angle\nend\n\n--[[\n DU-LUA-Framework by Jericho\n Permit to code easier by grouping most of the code in a single event Unit > Start\n Unminified Source available here: https://github.com/Jericho1060/du-lua-framework\n]]--\nlocal a=system.print;local b=error;local c=pcall;local d=assert;local e=coroutine;local f=e.create;local g=e.status;local h=e.resume;local i=\"dead\"local j=\"suspended\"function runFunction(k,l,...)local m,n=c(k,...)if not m then b(l..n)end end;local o={__index={cos={update={},flush={}},fns={update={},flush={},action={start={},stop={},loop={}},inputText=nil,start=nil,stop=nil},ACTIONS={FORWARD=\"forward\",BACKWARD=\"backward\",YAW_LEFT=\"yawleft\",YAW_RIGHT=\"yawright\",STRAFE_LEFT=\"strafeleft\",STRAFE_RIGHT=\"straferight\",LEFT=\"left\",RIGHT=\"right\",UP=\"up\",DOWN=\"down\",GROUND_ALTITUDE_UP=\"groundaltitudeup\",GROUND_ALTITUDE_DOWN=\"groundaltitudedown\",LEFT_ALT=\"lalt\",LEFT_SHIFT=\"lshift\",GEAR=\"gear\",LIGHT=\"light\",BRAKE=\"brake\",OPTION_1=\"option1\",OPTION_2=\"option2\",OPTION_3=\"option3\",OPTION_4=\"option4\",OPTION_5=\"option5\",OPTION_6=\"option6\",OPTION_7=\"option7\",OPTION_8=\"option8\",OPTION_9=\"option9\",OPTION_10=\"option10\",OPTION_11=\"option11\",OPTION_12=\"option12\",OPTION_13=\"option13\",OPTION_14=\"option14\",OPTION_15=\"option15\",OPTION_16=\"option16\",OPTION_17=\"option17\",OPTION_18=\"option18\",OPTION_19=\"option19\",OPTION_20=\"option20\",OPTION_21=\"option21\",OPTION_22=\"option22\",OPTION_23=\"option23\",OPTION_24=\"option24\",OPTION_25=\"option25\",OPTION_26=\"option26\",OPTION_27=\"option27\",OPTION_28=\"option28\",OPTION_29=\"option29\",LEFT_MOUSE=\"leftmouse\",STOP_ENGINES=\"stopengines\",SPEED_UP=\"speedup\",SPEED_DOWN=\"speeddown\",ANTIGRAVITY=\"antigravity\",BOOSTER=\"booster\"},main={update=f(function()end),flush=f(function()end)},update=function(self)local p=g(self.main.update)if p==i then self.main.update=f(function()self:runUpdate()end)elseif p==j then d(h(self.main.update))end end,flush=function(self)local p=g(self.main.flush)if p==i then self.main.flush=f(function()self:runFlush()end)elseif p==j then d(h(self.main.flush))end end,action=function(self,q,r)if self.fns.action[q][r]then runFunction(self.fns.action[q][r],\"System Action \"..q..\" Error: \")end end,actionStart=function(self,r)self:action('start',r)end,actionStop=function(self,r)self:action('stop',r)end,actionLoop=function(self,r)self:action('loop',r)end,inputText=function(self,s)if self.fns.inputText then runFunction(self.fns.inputText,\"System Input Text Error: \",s)end end,runUpdate=function(self)for t,u in pairs(self.cos.update)do local m=g(u)if m==i then self.cos.update[t]=f(self.fns.update[t])elseif m==j then d(h(u))end end end,runFlush=function(self)for t,u in pairs(self.cos.flush)do local m=g(u)if m==i then self.cos.flush[t]=f(self.fns.flush[t])elseif m==j then d(h(u))end end end,onUpdate=function(self,v)for t,w in pairs(v)do self.fns.update[t]=w;self.cos.update[t]=f(w)end end,onFlush=function(self,v)for t,w in pairs(v)do self.fns.flush[t]=w;self.cos.flush[t]=f(w)end end,onAction=function(self,q,v)for t,w in pairs(v)do self.fns.action[q][t]=w end end,onActionStart=function(self,v)self:onAction(\"start\",v)end,onActionStop=function(self,v)self:onAction(\"stop\",v)end,onActionLoop=function(self,v)self:onAction(\"loop\",v)end,onInputText=function(self,k)self.fns.inputText=k end}}local x={__index={timers={},stopFn=function()end,timer=function(self,y)if self.timers[y]then runFunction(self.timers[y],\"Unit Timer \"..y..\" Error: \")end end,setTimer=function(self,y,z,k)self.timers[y]=k;unit.setTimer(y,z)end,stopTimer=function(self,y)unit.stopTimer(y)self.timers[y]=nil end,onStop=function(self,k)self.stopFn=k end,stop=function(self)if self.stopFn then runFunction(self.stopFn,\"Unit Stop Error: \")end end}}local A={__index={parentChangedFn=function(B,C)end,onParentChange=function(self,k)self.parentChangedFn=k end,parentChanged=function(self,B,C)if self.parentChangedFn then runFunction(self.parentChangedFn,\"Player Parent Changed Error: \",B,C)end end}}local D={__index={dockedFn=function(E)end,onDocked=function(self,k)self.dockedFn=k end,docked=function(self,E)if self.dockedFn then runFunction(self.dockedFn,\"Construct Docked Error: \",E)end end,undockedFn=function(E)end,onUndocked=function(self,k)self.undockedFn=k end,undocked=function(self,E)if self.undockedFn then runFunction(self.undockedFn,\"Construct Undocked Error: \",E)end end,playerBoardedFn=function(E)end,onPlayerBoarded=function(self,k)self.playerBoardedFn=k end,playerBoarded=function(self,E)if self.playerBoardedFn then runFunction(self.playerBoardedFn,\"Construct Player Boarded Error: \",E)end end,VRStationEnteredFn=function(E)end,onVRStationEntered=function(self,k)self.VRStationEnteredFn=k end,VRStationEntered=function(self,E)if self.VRStationEnteredFn then runFunction(self.VRStationEnteredFn,\"Construct VR Station Entered Error: \",E)end end,constructDockedFn=function(E)end,onConstructDocked=function(self,k)self.constructDockedFn=k end,constructDocked=function(self,E)if self.constructDockedFn then runFunction(self.constructDockedFn,\"Construct Construct Docked Error: \",E)end end,PvPTimerFn=function(F)end,onPvPTimer=function(self,k)self.PvPTimerFn=k end,PvPTimer=function(self,F)if self.PvPTimerFn then runFunction(self.PvPTimerFn,\"Construct PvP Timer Error: \",F)end end}}DU_Framework={__index={system=setmetatable({},o),unit=setmetatable({},x),player=setmetatable({},A),construct=setmetatable({},D)}}\n\nScript = {}\nsetmetatable(Script, DU_Framework)\n\n--[[\n String Helpers For Lua By Jericho\n]]\nString = {\n __index = {\n split = function(self, delimiter)\n local result = {}\n for match in (self..delimiter):gmatch(\"(.-)\"..delimiter) do\n table.insert(result, match)\n end\n return result\n end\n }\n}\nstring = setmetatable(string, String)\nstring.__index = string\n\nlocal systemOnUpdate = {\n function ()\n Nav:update()\n end\n}\n\nlocal systemOnFlush = {\n function()\n local yawSpeedFactor = 1.5\n local yawAccelerationFactor = 3\n local lateralAntiDriftFactor = 1\n local lateralStrafeFactor = 5\n local brakeSpeedFactor = 1\n local brakeFlatFactor = 4 \n local autoBrakeSpeed = 15\n -- validate params\n brakeSpeedFactor = math.max(brakeSpeedFactor, 0.01)\n brakeFlatFactor = math.max(brakeFlatFactor, 0.01)\n yawSpeedFactor = math.max(yawSpeedFactor, 0.01)\n yawAccelerationFactor = math.max(yawAccelerationFactor, 0.01)\n --init all the PIDs as global if first flush\n if (rollPID == nil) then rollPID = pid.new(0.2, 0, 10) end\n if (pitchPID == nil) then pitchPID = pid.new(0.2, 0, 10) end\n if (yawPID == nil) then yawPID = pid.new(0.2, 0, 10) end\n if (lateralPID == nil) then lateralPID = pid.new(0.2, 0, 10) end\n if (longitudinalPID == nil) then longitudinalPID = pid.new(0.2, 0, 10) end\n if (distancePID == nil) then distancePID = pid.new(0.2, 0, 10) end\n\n -- final inputs\n if unit.isMouseDirectControlActivated() then\n -- in direct control, we tweak the pitch to behave inbetween virtual joystick and direct control\n -- this helps a lot for ground construct control\n pitchInputFromDevice = utils.clamp(pitchInputFromDevice + system.getControlDeviceForwardInput() * system.getActionUpdateDeltaTime(), -1.0, 1.0)\n else\n pitchInputFromDevice = system.getControlDeviceForwardInput()\n end\n local finalPitchInput = pitchInput + pitchInputFromDevice\n local finalRollInput = rollInput + system.getControlDeviceYawInput()\n local finalYawInput = yawInput - system.getControlDeviceLeftRightInput()\n local combinedRollYawInput = utils.clamp(finalRollInput - finalYawInput, -1.0, 1.0);\n local finalVerticalStrafeInput = verticalStrafeInput\n local finalLateralStrafeInput = lateralStrafeInput;\n local finalBrakeInput = brakeInput\n\n -- Axis\n local worldVertical = vec3(core.getWorldVertical())\n local constructUp = vec3(construct.getWorldOrientationUp())\n local constructForward = vec3(construct.getWorldOrientationForward())\n local constructRight = vec3(construct.getWorldOrientationRight())\n local constructVelocity = vec3(construct.getWorldVelocity())\n local constructVelocityDir = vec3(construct.getWorldVelocity()):normalize()\n local constructAngularVelocity = vec3(construct.getWorldAngularVelocity())\n local constructYawVelocity = constructAngularVelocity:dot(constructUp)\n local constructWorldPosition = vec3(construct.getWorldPosition())\n local constructTargetPosition = vec3(ConstructInitPos)\n\n -- Engine commands\n local keepCollinearity = 0 -- for easier reading\n local dontKeepCollinearity = 1 -- for easier reading\n local tolerancePercentToSkipOtherPriorities = 1 -- if we are within this tolerance (in%), we don't go to the next priorities\n\n -- keeping the start position alignement\n local StrafeSpeedFactor = 50 --export: useg to increase the force of the alignement, decrease the value if the alignement is too strong or increase it if it's too slow\n local positionDifference = constructTargetPosition - constructWorldPosition\n local lateralOffset = positionDifference:project_on(constructRight):len() * utils.sign(positionDifference:dot(constructRight))\n local longitudinalOffset = positionDifference:project_on(constructForward):len() * utils.sign(positionDifference:dot(constructForward))\n lateralPID:inject(lateralOffset)\n Nav.axisCommandManager:setThrottleCommand(axisCommandId.lateral, lateralPID:get() * StrafeSpeedFactor)\n longitudinalPID:inject(longitudinalOffset)\n Nav.axisCommandManager:setThrottleCommand(axisCommandId.longitudinal, longitudinalPID:get() * StrafeSpeedFactor)\n\n -- Rotation\n local currentRollDeg = getRoll(worldVertical, constructForward, constructRight)\n local currentPitchDeg = -math.asin(constructForward:dot(worldVertical)) * constants.rad2deg\n local targetRollDeg = 0\n local targetPitchDeg = 0\n local targetYawDeg = signedAngleBetween(BaseForward,constructForward,constructUp)*180/math.pi\n rollPID:inject(targetRollDeg - currentRollDeg)\n pitchPID:inject(targetPitchDeg - currentPitchDeg)\n yawPID:inject(-targetYawDeg*yawSpeedFactor)\n \n local constructTargetAngularVelocity = rollPID:get() * constructForward + pitchPID:get() * constructRight + yawPID:get() * constructUp\n\n Nav:setEngineTorqueCommand('torque', constructTargetAngularVelocity, keepCollinearity, 'airfoil', '', '', tolerancePercentToSkipOtherPriorities)\n\n -- moving up or down from TargetAltitude\n local verticalSpeed = constructVelocity:project_on(worldVertical):len()\n local verticalSpeedSigned = verticalSpeed * -utils.sign(constructVelocity:dot(worldVertical))\n local brakeDistance = 0\n local maxBrake = construct.getMaxBrake()\n if maxBrake ~= nil then\n brakeDistance, _ = computeDistanceAndTime(verticalSpeed, 0, construct.getInertialMass(), 0, 0, maxBrake - (core.getGravityIntensity() * construct.getInertialMass()) * utils.sign(verticalSpeedSigned))\n end\n local coreAltitude = core.getAltitude()\n local distance = TargetAltitude - coreAltitude\n local targetDistance = utils.sign(distance) * (math.abs(distance)-brakeDistance)\n distancePID:inject(targetDistance)\n if distancePID:get() > 0.5 or verticalSpeedSigned < -MaxSpeed then --using a 0.5 meter deadband for stabilizing\n Nav.axisCommandManager:setThrottleCommand(axisCommandId.vertical, 1)\n elseif distancePID:get() < -0.5 or verticalSpeedSigned > MaxSpeed then\n Nav.axisCommandManager:setThrottleCommand(axisCommandId.vertical, -1)\n else\n Nav.axisCommandManager:resetCommand(axisCommandId.vertical)\n end\n\n --Brakes\n if\n (math.abs(distance) < verticalSpeed)\n or verticalSpeed > MaxSpeed\n or (brakeDistance > math.abs(distance))\n or (finalBrakeInput == 0 and autoBrakeSpeed > 0 and Nav.axisCommandManager.throttle == 0 and constructVelocity:len() < autoBrakeSpeed)\n then\n finalBrakeInput = 1\n end\n local brakeAcceleration = -finalBrakeInput * (brakeSpeedFactor * constructVelocity + brakeFlatFactor * constructVelocityDir)\n \n Nav:setEngineForceCommand('brake', brakeAcceleration)\n\n -- AutoNavigation regroups all the axis command by 'TargetSpeed'\n local autoNavigationEngineTags = ''\n local autoNavigationAcceleration = vec3()\n local autoNavigationUseBrake = false\n\n -- Longitudinal Translation\n local longitudinalEngineTags = 'thrust analog longitudinal'\n local longitudinalCommandType = Nav.axisCommandManager:getAxisCommandType(axisCommandId.longitudinal)\n if (longitudinalCommandType == axisCommandType.byThrottle) then\n local longitudinalAcceleration = Nav.axisCommandManager:composeAxisAccelerationFromThrottle(longitudinalEngineTags,axisCommandId.longitudinal)\n Nav:setEngineForceCommand(longitudinalEngineTags, longitudinalAcceleration, keepCollinearity)\n elseif (longitudinalCommandType == axisCommandType.byTargetSpeed) then\n local longitudinalAcceleration = Nav.axisCommandManager:composeAxisAccelerationFromTargetSpeed(axisCommandId.longitudinal)\n autoNavigationEngineTags = autoNavigationEngineTags .. ' , ' .. longitudinalEngineTags\n autoNavigationAcceleration = autoNavigationAcceleration + longitudinalAcceleration\n if (Nav.axisCommandManager:getTargetSpeed(axisCommandId.longitudinal) == 0 or -- we want to stop\n Nav.axisCommandManager:getCurrentToTargetDeltaSpeed(axisCommandId.longitudinal) < - Nav.axisCommandManager:getTargetSpeedCurrentStep(axisCommandId.longitudinal) * 0.5) -- if the longitudinal velocity would need some braking\n then\n autoNavigationUseBrake = true\n end\n end\n\n -- Lateral Translation\n local lateralStrafeEngineTags = 'thrust analog lateral'\n local lateralCommandType = Nav.axisCommandManager:getAxisCommandType(axisCommandId.lateral)\n if (lateralCommandType == axisCommandType.byThrottle) then\n local lateralStrafeAcceleration = Nav.axisCommandManager:composeAxisAccelerationFromThrottle(lateralStrafeEngineTags,axisCommandId.lateral)\n Nav:setEngineForceCommand(lateralStrafeEngineTags, lateralStrafeAcceleration, keepCollinearity)\n elseif (lateralCommandType == axisCommandType.byTargetSpeed) then\n local lateralAcceleration = Nav.axisCommandManager:composeAxisAccelerationFromTargetSpeed(axisCommandId.lateral)\n autoNavigationEngineTags = autoNavigationEngineTags .. ' , ' .. lateralStrafeEngineTags\n autoNavigationAcceleration = autoNavigationAcceleration + lateralAcceleration\n end\n\n -- Vertical Translation\n local verticalStrafeEngineTags = 'thrust analog vertical'\n local verticalCommandType = Nav.axisCommandManager:getAxisCommandType(axisCommandId.vertical)\n if (verticalCommandType == axisCommandType.byThrottle) then\n local verticalStrafeAcceleration = Nav.axisCommandManager:composeAxisAccelerationFromThrottle(verticalStrafeEngineTags,axisCommandId.vertical)\n Nav:setEngineForceCommand(verticalStrafeEngineTags, verticalStrafeAcceleration, keepCollinearity, 'airfoil', 'ground', '', tolerancePercentToSkipOtherPriorities)\n elseif (verticalCommandType == axisCommandType.byTargetSpeed) then\n local verticalAcceleration = Nav.axisCommandManager:composeAxisAccelerationFromTargetSpeed(axisCommandId.vertical)\n autoNavigationEngineTags = autoNavigationEngineTags .. ' , ' .. verticalStrafeEngineTags\n autoNavigationAcceleration = autoNavigationAcceleration + verticalAcceleration\n end\n\n -- Auto Navigation (Cruise Control)\n if (autoNavigationAcceleration:len() > constants.epsilon) then\n if (brakeInput ~= 0 or autoNavigationUseBrake or math.abs(constructVelocityDir:dot(constructForward)) < 0.95) -- if the velocity is not properly aligned with the forward\n then\n autoNavigationEngineTags = autoNavigationEngineTags .. ', brake'\n end\n Nav:setEngineForceCommand(autoNavigationEngineTags, autoNavigationAcceleration, dontKeepCollinearity, '', '', '', tolerancePercentToSkipOtherPriorities)\n end\n\n -- Rockets\n Nav:setBoosterCommand('rocket_engine')\n end\n}\n\n\nScript.system:onUpdate(systemOnUpdate)\nScript.system:onFlush(systemOnFlush)\n\n--[[\n Chat Commands\n]]\n\nScript.system:onInputText(function (text)\n if text:lower():find('goto:') then\n TargetAltitude = tonumber(text:split(':')[2]) or BaseAltitute\n brakeInput = 0\n system.print('Target Altitude set to ' .. TargetAltitude .. 'm')\n else\n system.print('Unknown command')\n end\nend)","filter":{"args":[],"signature":"onStart()","slotKey":"-1"},"key":"2"}],"methods":[],"slots":{"0":{"name":"slot1","type":{"events":[],"methods":[]}},"1":{"name":"slot2","type":{"events":[],"methods":[]}},"2":{"name":"slot3","type":{"events":[],"methods":[]}},"3":{"name":"slot4","type":{"events":[],"methods":[]}},"4":{"name":"slot5","type":{"events":[],"methods":[]}},"5":{"name":"slot6","type":{"events":[],"methods":[]}},"6":{"name":"slot7","type":{"events":[],"methods":[]}},"7":{"name":"slot8","type":{"events":[],"methods":[]}},"8":{"name":"slot9","type":{"events":[],"methods":[]}},"9":{"name":"slot10","type":{"events":[],"methods":[]}},"-5":{"name":"library","type":{"events":[],"methods":[]}},"-4":{"name":"system","type":{"events":[],"methods":[]}},"-3":{"name":"player","type":{"events":[],"methods":[]}},"-2":{"name":"construct","type":{"events":[],"methods":[]}},"-1":{"name":"unit","type":{"events":[],"methods":[]}}}}11+{"events":[],"handlers":[{"code":"Script.system:update()","filter":{"args":[],"signature":"onUpdate()","slotKey":"-4"},"key":"9"},{"code":"Script.system:inputText(text)","filter":{"args":[{"variable":"*"}],"signature":"onInputText(text)","slotKey":"-4"},"key":"10"},{"code":"Script.system:flush()","filter":{"args":[],"signature":"onFlush()","slotKey":"-4"},"key":"11"},{"code":"Script.system:actionStop(action)","filter":{"args":[{"variable":"*"}],"signature":"onActionStop(action)","slotKey":"-4"},"key":"12"},{"code":"Script.system:actionStart(action)","filter":{"args":[{"variable":"*"}],"signature":"onActionStart(action)","slotKey":"-4"},"key":"13"},{"code":"Script.system:actionLoop(action)","filter":{"args":[{"variable":"*"}],"signature":"onActionLoop(action)","slotKey":"-4"},"key":"14"},{"code":"Script.player:parentChanged(oldId, newId)","filter":{"args":[{"variable":"*"},{"variable":"*"}],"signature":"onParentChanged(oldId,newId)","slotKey":"-3"},"key":"3"},{"code":"Script.construct:VRStationEntered(id)","filter":{"args":[{"variable":"*"}],"signature":"onVRStationEntered(id)","slotKey":"-2"},"key":"4"},{"code":"Script.construct:undocked(id)","filter":{"args":[{"variable":"*"}],"signature":"onUndocked(id)","slotKey":"-2"},"key":"5"},{"code":"Script.construct:PvPTimer(active)","filter":{"args":[{"variable":"*"}],"signature":"onPvPTimer(active)","slotKey":"-2"},"key":"6"},{"code":"Script.construct:playerBoarded(id)","filter":{"args":[{"variable":"*"}],"signature":"onPlayerBoarded(id)","slotKey":"-2"},"key":"7"},{"code":"Script.construct:constructDocked(id)","filter":{"args":[{"variable":"*"}],"signature":"onConstructDocked(id)","slotKey":"-2"},"key":"8"},{"code":"Script.unit:timer(tag)","filter":{"args":[{"variable":"*"}],"signature":"onTimer(tag)","slotKey":"-1"},"key":"0"},{"code":"Script.unit:stop()","filter":{"args":[],"signature":"onStop()","slotKey":"-1"},"key":"1"},{"code":"--[[\n DU-ELEVATOR by Jericho\n]]\n\n--[[\n Bookmarks: you can store several altitudes here to navigate easily\n]]\nlocal Bookmarks = {\n { name = 'Start Point', altitude = 285 },\n { name = 'Hovering', altitude = 300 },\n { name = 'Floor 1', altitude = 350 },\n { name = 'Floor 2', altitude = 500 }\n}\n\n--[[\n LUA PARAMETERS\n]]\n__DEBUG = false --export: Debug mode, will print more information in the console\nShowParentingWidget = false --export: Show the parenting widget\nShowControlUnitWidget = false --export: show the default widget of the construct\n\n--[[\n Version Management\n]]\nlocal version = \"V 1.1.0\"\nlocal log_split = \"=================================================\"\n--printing version in lua chat\nsystem.print(log_split)local a=\"\"local b=math.ceil((50-#version-2)/2)for c=1,b,1 do a=a..'='end;a=a..\" \"..version..\" \"for c=1,b,1 do a=a..'='end;system.print(a)system.print(log_split)\n--[[\n Fuel Tanks Metatable By Jericho\n Version: 1.0.0\n Unminified Source available at: https://github.com/Jericho1060/DualUniverse/blob/master/ElementsMetatable/FuelTank.lua\n]]\nFuelTank={__index={name='',percentage=0,timeLeft=0,lastRefresh=0,fuelType='rocket',refresh=function(self)local a=system.getUtcTime()if lastRefresh~=a then local b=self.getClass():lower()if b:find('atmo')then self.fuelType=\"atmo\"elseif b:find('space')then self.fuelType=\"space\"end;local c=self.getWidgetData()local d=json.decode(c)self.name=d.name;self.percentage=d.percentage;self.timeLeft=tonumber(d.timeLeft)self.lastRefresh=a end end,getName=function(self)self:refresh()return self.name end,getPercentFilled=function(self)self:refresh()return self.percentage end,getSecondsLeft=function(self)self:refresh()return self.timeLeft end,getFuelType=function(self)self:refresh()return self.fuelType end,getTimeLeftString=function(self)local e=self:getSecondsLeft()if e==nil or e<=0 then return\"-\"end;days=string.format(\"%2.f\",math.floor(e/(3600*24)))hours=string.format(\"%2.f\",math.floor(e/3600-days*24))mins=string.format(\"%2.f\",math.floor(e/60-hours*60-days*24*60))secs=string.format(\"%2.f\",math.floor(e-hours*3600-days*24*60*60-mins*60))str=\"\"if tonumber(days)>0 then str=str..days..\"d \"end;if tonumber(hours)>0 then str=str..hours..\"h \"end;if tonumber(mins)>0 then str=str..mins..\"m \"end;if tonumber(secs)>0 then str=str..secs..\"s\"end;return str end,dump=function(self)self:refresh()system.print('{name=\"'..self.name..'\",percentage='..self.percentage..',timeLeft='..self.timeLeft..',fuelType=\"'..self.fuelType..'\"}')end}}\n--[[\n Detecting elements linked\n]]\ncore = nil\nfuelTanks = {}\nfor slot_name, slot in pairs(unit) do\n if\n type(slot) == \"table\"\n and type(slot.export) == \"table\"\n and slot.getClass\n then\n local class = slot.getClass():lower()\n if class:find(\"coreunit\") then\n core = slot\n elseif class:find(\"fuelcontainer\") then\n fuelTanks[#fuelTanks + 1] = setmetatable(slot, FuelTank)\n end\n end\nend\n\n--[[\n stopping the script if required elements are not linked\n]]\nif core == nil then\n system.print('Core unit is not linked, exiting the script')\n unit.exit()\nend\n\n--[[\n Default Unit Start Mechanics\n]]\npitchInput = 0\npitchInputFromDevice = 0\nrollInput = 0\nyawInput = 0\nverticalStrafeInput = 0\nlateralStrafeInput = 0\nbrakeInput = 1 --braking by default\ngoingBack = false\ngoingForward = false\nshiftPressed = false\njumpDelta = 0\nbaseAcceleration = 0.8\n\nNav = Navigator.new(system, core, unit)\nNav.axisCommandManager:setupCustomTargetSpeedRanges(axisCommandId.longitudinal, {100, 500, 1000, 2000,3000, 5000})\nNav.axisCommandManager:setTargetGroundAltitude(0)\nunit.deactivateGroundEngineAltitudeStabilization()\n\n-- Parenting widget\nif ShowParentingWidget then\n parentingPanelId = system.createWidgetPanel(\"Docking\")\n parentingWidgetId = system.createWidget(parentingPanelId,\"parenting\")\n system.addDataToWidget(unit.getWidgetDataId(),parentingWidgetId)\nend\nif not ShowControlUnitWidget then\n unit.hideWidget()\nend\n\n--[[\n Storing base position and orientation of the elevator\n]]\nConstructInitPos = construct.getWorldPosition()\n--replace that pos by a hand written value to be sure the construct will always realign the same position\n--ConstructInitPos = {-1231538.185042, 1201297.6879598, -2617220.2766464}\nif __DEBUG then system.print('ConstructInitPos: ' .. json.encode(ConstructInitPos)) end\nBaseForward = vec3(construct.getWorldForward())\nif __DEBUG then system.print('BaseForward: ' .. json.encode(BaseForward)) end\nBaseAltitute = Bookmarks[1].altitude --getting the base altitude from the 1st bookmark\n--init a value to store the target altitude\nTargetAltitude = core.getAltitude() --by default to the start altitude to avoid falling down if in the air\n\nElevatorData = {\n isBreaking = brakeInput == 1,\n verticalSpeed = 0,\n verticalSpeedSigned = 0,\n lateralSpeed = 0,\n longitudinalSpeed = 0,\n altitude = TargetAltitude,\n direction = 'stabilizing',\n}\n\n--TODO: to replace with a computing in flush from the current acceleration and the friction acceleration (construct.getWorldAirFrictionAcceleration)\nMaxSpeed = construct.getFrictionBurnSpeed() -- for security to avoid burning if going too fast\nif __DEBUG then system.print('MaxSpeed: ' .. MaxSpeed) end\n\n--base selected bookmark is 0 -> none selected as index 0 doesn't exist in lua\nselectedBookmarkIndex = 0\n\n--[[\n init the HUD\n]]\nsystem.showScreen(true)\n\n--[[\n Kinematics functions by Jaylebreak\n Source available at https://gitlab.com/JayleBreak/dualuniverse/-/blob/master/DUflightfiles/autoconf/custom/kinematics.lua\n]]\nfunction computeAccelerationTime(initial, acceleration, final) return (final - initial)/acceleration end\nfunction computeDistanceAndTime(initial, final, mass, thrust, t50, brakeThrust)\n t50 = t50 or 0\n brakeThrust = brakeThrust or 0\n local speedUp = initial < final\n local a0 = thrust / (speedUp and mass or -mass)\n local b0 = -brakeThrust/mass\n local totA = a0+b0\n if initial == final then\n return 0, 0\n elseif speedUp and totA <= 0 or not speedUp and totA >= 0 then\n return -1, -1\n end\n local distanceToMax, timeToMax = 0, 0\n if a0 ~= 0 and t50 > 0 then\n local c1 = math.pi/t50/2\n local v = function(t)\n return a0*(t/2 - t50*math.sin(c1*t)/math.pi) + b0*t + initial\n end\n local speedchk = speedUp and function(s) return s >= final end or function(s) return s <= final end\n timeToMax = 2*t50\n if speedchk(v(timeToMax)) then\n local lasttime = 0\n while math.abs(timeToMax - lasttime) > 0.25 do\n local t = (timeToMax + lasttime)/2\n if speedchk(v(t)) then\n timeToMax = t\n else\n lasttime = t\n end\n end\n end\n local K = 2*a0*t50^2/math.pi^2\n distanceToMax = K*(math.cos(c1*timeToMax) - 1) + (a0+2*b0)*timeToMax^2/4 + initial*timeToMax\n if timeToMax < 2*t50 then\n return distanceToMax, timeToMax\n end\n initial = v(timeToMax)\n end\n local a = a0+b0\n local t = computeAccelerationTime(initial, a, final)\n local d = initial*t + a*t*t/2\n return distanceToMax+d, timeToMax+t\nend\n\n--[[\n Compute angle between vectors by Jericho\n]]\nfunction signedAngleBetween(vec1, vec2, planeNormal)\n local normVec1 = vec1:project_on_plane(planeNormal):normalize()\n local normVec2 = vec2:normalize()\n local v1v2dot = normVec1:dot(normVec2)\n local angle = math.acos(utils.clamp(v1v2dot,-1,1))\n local crossProduct = vec1:cross(vec2)\n if crossProduct:dot(planeNormal) < 0 then\n return -angle\n end\n return angle\nend\n--[[\n\tformatting numbers by adding a space between thousands by Jericho\n]]\nfunction format_number(a)local b=a;while true do b,k=string.gsub(b,\"^(-?%d+)(%d%d%d)\",'%1 %2')if k==0 then break end end;local c=string.sub(b,-2)if c=='.0'then b=string.sub(b,1,b:len()-2)end;return b end\n--[[\n DU-LUA-Framework by Jericho\n Permit to code easier by grouping most of the code in a single event Unit > Start\n Unminified Source available here: https://github.com/Jericho1060/du-lua-framework\n]]--\nlocal a=system.print;local b=error;local c=pcall;local d=assert;local e=coroutine;local f=e.create;local g=e.status;local h=e.resume;local i=\"dead\"local j=\"suspended\"function runFunction(k,l,...)local m,n=c(k,...)if not m then b(l..n)end end;local o={__index={cos={update={},flush={}},fns={update={},flush={},action={start={},stop={},loop={}},inputText=nil,start=nil,stop=nil},ACTIONS={FORWARD=\"forward\",BACKWARD=\"backward\",YAW_LEFT=\"yawleft\",YAW_RIGHT=\"yawright\",STRAFE_LEFT=\"strafeleft\",STRAFE_RIGHT=\"straferight\",LEFT=\"left\",RIGHT=\"right\",UP=\"up\",DOWN=\"down\",GROUND_ALTITUDE_UP=\"groundaltitudeup\",GROUND_ALTITUDE_DOWN=\"groundaltitudedown\",LEFT_ALT=\"lalt\",LEFT_SHIFT=\"lshift\",GEAR=\"gear\",LIGHT=\"light\",BRAKE=\"brake\",OPTION_1=\"option1\",OPTION_2=\"option2\",OPTION_3=\"option3\",OPTION_4=\"option4\",OPTION_5=\"option5\",OPTION_6=\"option6\",OPTION_7=\"option7\",OPTION_8=\"option8\",OPTION_9=\"option9\",OPTION_10=\"option10\",OPTION_11=\"option11\",OPTION_12=\"option12\",OPTION_13=\"option13\",OPTION_14=\"option14\",OPTION_15=\"option15\",OPTION_16=\"option16\",OPTION_17=\"option17\",OPTION_18=\"option18\",OPTION_19=\"option19\",OPTION_20=\"option20\",OPTION_21=\"option21\",OPTION_22=\"option22\",OPTION_23=\"option23\",OPTION_24=\"option24\",OPTION_25=\"option25\",OPTION_26=\"option26\",OPTION_27=\"option27\",OPTION_28=\"option28\",OPTION_29=\"option29\",LEFT_MOUSE=\"leftmouse\",STOP_ENGINES=\"stopengines\",SPEED_UP=\"speedup\",SPEED_DOWN=\"speeddown\",ANTIGRAVITY=\"antigravity\",BOOSTER=\"booster\"},main={update=f(function()end),flush=f(function()end)},update=function(self)local p=g(self.main.update)if p==i then self.main.update=f(function()self:runUpdate()end)elseif p==j then d(h(self.main.update))end end,flush=function(self)local p=g(self.main.flush)if p==i then self.main.flush=f(function()self:runFlush()end)elseif p==j then d(h(self.main.flush))end end,action=function(self,q,r)if self.fns.action[q][r]then runFunction(self.fns.action[q][r],\"System Action \"..q..\" Error: \")end end,actionStart=function(self,r)self:action('start',r)end,actionStop=function(self,r)self:action('stop',r)end,actionLoop=function(self,r)self:action('loop',r)end,inputText=function(self,s)if self.fns.inputText then runFunction(self.fns.inputText,\"System Input Text Error: \",s)end end,runUpdate=function(self)for t,u in pairs(self.cos.update)do local m=g(u)if m==i then self.cos.update[t]=f(self.fns.update[t])elseif m==j then d(h(u))end end end,runFlush=function(self)for t,u in pairs(self.cos.flush)do local m=g(u)if m==i then self.cos.flush[t]=f(self.fns.flush[t])elseif m==j then d(h(u))end end end,onUpdate=function(self,v)for t,w in pairs(v)do self.fns.update[t]=w;self.cos.update[t]=f(w)end end,onFlush=function(self,v)for t,w in pairs(v)do self.fns.flush[t]=w;self.cos.flush[t]=f(w)end end,onAction=function(self,q,v)for t,w in pairs(v)do self.fns.action[q][t]=w end end,onActionStart=function(self,v)self:onAction(\"start\",v)end,onActionStop=function(self,v)self:onAction(\"stop\",v)end,onActionLoop=function(self,v)self:onAction(\"loop\",v)end,onInputText=function(self,k)self.fns.inputText=k end}}local x={__index={timers={},stopFn=function()end,timer=function(self,y)if self.timers[y]then runFunction(self.timers[y],\"Unit Timer \"..y..\" Error: \")end end,setTimer=function(self,y,z,k)self.timers[y]=k;unit.setTimer(y,z)end,stopTimer=function(self,y)unit.stopTimer(y)self.timers[y]=nil end,onStop=function(self,k)self.stopFn=k end,stop=function(self)if self.stopFn then runFunction(self.stopFn,\"Unit Stop Error: \")end end}}local A={__index={parentChangedFn=function(B,C)end,onParentChange=function(self,k)self.parentChangedFn=k end,parentChanged=function(self,B,C)if self.parentChangedFn then runFunction(self.parentChangedFn,\"Player Parent Changed Error: \",B,C)end end}}local D={__index={dockedFn=function(E)end,onDocked=function(self,k)self.dockedFn=k end,docked=function(self,E)if self.dockedFn then runFunction(self.dockedFn,\"Construct Docked Error: \",E)end end,undockedFn=function(E)end,onUndocked=function(self,k)self.undockedFn=k end,undocked=function(self,E)if self.undockedFn then runFunction(self.undockedFn,\"Construct Undocked Error: \",E)end end,playerBoardedFn=function(E)end,onPlayerBoarded=function(self,k)self.playerBoardedFn=k end,playerBoarded=function(self,E)if self.playerBoardedFn then runFunction(self.playerBoardedFn,\"Construct Player Boarded Error: \",E)end end,VRStationEnteredFn=function(E)end,onVRStationEntered=function(self,k)self.VRStationEnteredFn=k end,VRStationEntered=function(self,E)if self.VRStationEnteredFn then runFunction(self.VRStationEnteredFn,\"Construct VR Station Entered Error: \",E)end end,constructDockedFn=function(E)end,onConstructDocked=function(self,k)self.constructDockedFn=k end,constructDocked=function(self,E)if self.constructDockedFn then runFunction(self.constructDockedFn,\"Construct Construct Docked Error: \",E)end end,PvPTimerFn=function(F)end,onPvPTimer=function(self,k)self.PvPTimerFn=k end,PvPTimer=function(self,F)if self.PvPTimerFn then runFunction(self.PvPTimerFn,\"Construct PvP Timer Error: \",F)end end}}DU_Framework={__index={system=setmetatable({},o),unit=setmetatable({},x),player=setmetatable({},A),construct=setmetatable({},D)}}\n\nScript = {}\nsetmetatable(Script, DU_Framework)\n\n--[[\n String Helpers For Lua By Jericho\n]]\nString = {\n __index = {\n split = function(self, delimiter)\n local result = {}\n for match in (self..delimiter):gmatch(\"(.-)\"..delimiter) do\n table.insert(result, match)\n end\n return result\n end\n }\n}\nstring = setmetatable(string, String)\nstring.__index = string\n\nlocal systemOnFlush = {\n function()\n local yawSpeedFactor = 1 --export: the auto yaw speed multiplier\n local yawAccelerationFactor = 3\n local lateralAntiDriftFactor = 1\n local lateralStrafeFactor = 5\n local brakeSpeedFactor = 1\n local brakeFlatFactor = 4\n local autoBrakeSpeed = 15\n -- validate params\n brakeSpeedFactor = math.max(brakeSpeedFactor, 0.01)\n brakeFlatFactor = math.max(brakeFlatFactor, 0.01)\n yawSpeedFactor = math.max(yawSpeedFactor, 0.01)\n yawAccelerationFactor = math.max(yawAccelerationFactor, 0.01)\n --init all the PIDs as global if first flush\n if (rollPID == nil) then rollPID = pid.new(0.2, 0, 10) end\n if (pitchPID == nil) then pitchPID = pid.new(0.2, 0, 10) end\n if (yawPID == nil) then yawPID = pid.new(0.2, 0, 10) end\n if (lateralPID == nil) then lateralPID = pid.new(0.2, 0, 10) end\n if (longitudinalPID == nil) then longitudinalPID = pid.new(0.2, 0, 10) end\n if (distancePID == nil) then distancePID = pid.new(0.2, 0, 10) end\n\n -- final inputs\n if unit.isMouseDirectControlActivated() then\n -- in direct control, we tweak the pitch to behave inbetween virtual joystick and direct control\n -- this helps a lot for ground construct control\n pitchInputFromDevice = utils.clamp(pitchInputFromDevice + system.getControlDeviceForwardInput() * system.getActionUpdateDeltaTime(), -1.0, 1.0)\n else\n pitchInputFromDevice = system.getControlDeviceForwardInput()\n end\n local finalPitchInput = pitchInput + pitchInputFromDevice\n local finalRollInput = rollInput + system.getControlDeviceYawInput()\n local finalYawInput = yawInput - system.getControlDeviceLeftRightInput()\n local combinedRollYawInput = utils.clamp(finalRollInput - finalYawInput, -1.0, 1.0);\n local finalVerticalStrafeInput = verticalStrafeInput\n local finalLateralStrafeInput = lateralStrafeInput;\n local finalBrakeInput = brakeInput\n\n -- Axis\n local worldVertical = vec3(core.getWorldVertical())\n local worldRight = vec3(core.getWorldRight())\n local worldForward = vec3(core.getWorldForward())\n local constructUp = vec3(construct.getWorldOrientationUp())\n local constructForward = vec3(construct.getWorldOrientationForward())\n local constructRight = vec3(construct.getWorldOrientationRight())\n local constructVelocity = vec3(construct.getWorldVelocity())\n local constructVelocityDir = vec3(construct.getWorldVelocity()):normalize()\n local constructAngularVelocity = vec3(construct.getWorldAngularVelocity())\n local constructYawVelocity = constructAngularVelocity:dot(constructUp)\n local constructWorldPosition = vec3(construct.getWorldPosition())\n local constructTargetPosition = vec3(ConstructInitPos)\n\n -- Engine commands\n local keepCollinearity = 0 -- for easier reading\n local dontKeepCollinearity = 1 -- for easier reading\n local tolerancePercentToSkipOtherPriorities = 1 -- if we are within this tolerance (in%), we don't go to the next priorities\n\n --fake braking value\n local canBrake = false\n\n -- keeping the start position alignement\n local StrafeSpeedFactor = 50 --export: useg to increase the force of the alignement, decrease the value if the alignement is too strong or increase it if it's too slow\n local positionDifference = constructTargetPosition - constructWorldPosition\n local lateralOffset = positionDifference:project_on(constructRight):len() * utils.sign(positionDifference:dot(constructRight))\n ElevatorData.lateralSpeed = constructVelocity:project_on(worldRight):len()\n local lateralDistance = math.abs(lateralOffset)\n local longitudinalOffset = positionDifference:project_on(constructForward):len() * utils.sign(positionDifference:dot(constructForward))\n ElevatorData.longitudinalSpeed = constructVelocity:project_on(worldForward):len()\n local longitudinalDistance = math.abs(longitudinalOffset)\n if (lateralDistance < .25) and (longitudinalDistance < .25) then canBrake = true end\n if ((ElevatorData.lateralSpeed*3.6) > lateralDistance) or ((ElevatorData.longitudinalSpeed*3.6) > longitudinalDistance) then canBrake = true end\n lateralPID:inject(lateralOffset)\n Nav.axisCommandManager:setThrottleCommand(axisCommandId.lateral, lateralPID:get() * StrafeSpeedFactor)\n longitudinalPID:inject(longitudinalOffset)\n Nav.axisCommandManager:setThrottleCommand(axisCommandId.longitudinal, longitudinalPID:get() * StrafeSpeedFactor)\n\n -- Rotation\n local currentRollDeg = getRoll(worldVertical, constructForward, constructRight)\n local currentPitchDeg = -math.asin(constructForward:dot(worldVertical)) * constants.rad2deg\n local targetRollDeg = 0\n local targetPitchDeg = 0\n local targetYawDeg = signedAngleBetween(BaseForward,constructForward,constructUp)*180/math.pi\n rollPID:inject(targetRollDeg - currentRollDeg)\n pitchPID:inject(targetPitchDeg - currentPitchDeg)\n yawPID:inject(-targetYawDeg*yawSpeedFactor)\n\n local constructTargetAngularVelocity = rollPID:get() * constructForward + pitchPID:get() * constructRight + yawPID:get() * constructUp\n\n Nav:setEngineTorqueCommand('torque', constructTargetAngularVelocity, keepCollinearity, 'airfoil', '', '', tolerancePercentToSkipOtherPriorities)\n\n -- moving up or down from TargetAltitude\n ElevatorData.verticalSpeed = constructVelocity:project_on(worldVertical):len()\n ElevatorData.verticalSpeedSigned = ElevatorData.verticalSpeed * -utils.sign(constructVelocity:dot(worldVertical))\n local brakeDistance = 0\n local maxBrake = construct.getMaxBrake()\n if maxBrake ~= nil then\n brakeDistance, _ = computeDistanceAndTime(ElevatorData.verticalSpeed, 0, construct.getInertialMass(), 0, 0, maxBrake - (core.getGravityIntensity() * construct.getInertialMass()) * utils.sign(ElevatorData.verticalSpeedSigned))\n end\n ElevatorData.altitude = core.getAltitude()\n local distance = TargetAltitude - ElevatorData.altitude\n local targetDistance = utils.sign(distance) * (math.abs(distance)-brakeDistance)\n distancePID:inject(targetDistance)\n if distancePID:get() > 0.5 or ElevatorData.verticalSpeedSigned < -MaxSpeed then --using a 0.5 meter deadband for stabilizing\n Nav.axisCommandManager:setThrottleCommand(axisCommandId.vertical, 1)\n ElevatorData.direction = 'up'\n if ElevatorData.verticalSpeedSigned < 0 then\n finalBrakeInput = 1\n end\n elseif distancePID:get() < -0.5 or ElevatorData.verticalSpeedSigned > MaxSpeed then\n Nav.axisCommandManager:setThrottleCommand(axisCommandId.vertical, -1)\n ElevatorData.direction = 'down'\n if ElevatorData.verticalSpeedSigned > 0 then\n finalBrakeInput = 1\n end\n else\n Nav.axisCommandManager:resetCommand(axisCommandId.vertical)\n ElevatorData.direction = 'stabilizing'\n end\n\n --Brakes\n if\n (math.abs(distance) < (ElevatorData.verticalSpeed*3.6) and canBrake)\n or (ElevatorData.verticalSpeed >= MaxSpeed)\n or ((brakeDistance > math.abs(distance)) and canBrake)\n or (finalBrakeInput == 0 and autoBrakeSpeed > 0 and Nav.axisCommandManager.throttle == 0 and constructVelocity:len() < autoBrakeSpeed)\n then\n finalBrakeInput = 1\n end\n ElevatorData.isBreaking = (finalBrakeInput == 1)\n local brakeAcceleration = -finalBrakeInput * (brakeSpeedFactor * constructVelocity + brakeFlatFactor * constructVelocityDir)\n\n Nav:setEngineForceCommand('brake', brakeAcceleration)\n\n -- AutoNavigation regroups all the axis command by 'TargetSpeed'\n local autoNavigationEngineTags = ''\n local autoNavigationAcceleration = vec3()\n local autoNavigationUseBrake = false\n\n -- Longitudinal Translation\n local longitudinalEngineTags = 'thrust analog longitudinal'\n local longitudinalCommandType = Nav.axisCommandManager:getAxisCommandType(axisCommandId.longitudinal)\n if (longitudinalCommandType == axisCommandType.byThrottle) then\n local longitudinalAcceleration = Nav.axisCommandManager:composeAxisAccelerationFromThrottle(longitudinalEngineTags,axisCommandId.longitudinal)\n Nav:setEngineForceCommand(longitudinalEngineTags, longitudinalAcceleration, keepCollinearity)\n elseif (longitudinalCommandType == axisCommandType.byTargetSpeed) then\n local longitudinalAcceleration = Nav.axisCommandManager:composeAxisAccelerationFromTargetSpeed(axisCommandId.longitudinal)\n autoNavigationEngineTags = autoNavigationEngineTags .. ' , ' .. longitudinalEngineTags\n autoNavigationAcceleration = autoNavigationAcceleration + longitudinalAcceleration\n if (Nav.axisCommandManager:getTargetSpeed(axisCommandId.longitudinal) == 0 or -- we want to stop\n Nav.axisCommandManager:getCurrentToTargetDeltaSpeed(axisCommandId.longitudinal) < - Nav.axisCommandManager:getTargetSpeedCurrentStep(axisCommandId.longitudinal) * 0.5) -- if the longitudinal velocity would need some braking\n then\n autoNavigationUseBrake = true\n end\n end\n\n -- Lateral Translation\n local lateralStrafeEngineTags = 'thrust analog lateral'\n local lateralCommandType = Nav.axisCommandManager:getAxisCommandType(axisCommandId.lateral)\n if (lateralCommandType == axisCommandType.byThrottle) then\n local lateralStrafeAcceleration = Nav.axisCommandManager:composeAxisAccelerationFromThrottle(lateralStrafeEngineTags,axisCommandId.lateral)\n Nav:setEngineForceCommand(lateralStrafeEngineTags, lateralStrafeAcceleration, keepCollinearity)\n elseif (lateralCommandType == axisCommandType.byTargetSpeed) then\n local lateralAcceleration = Nav.axisCommandManager:composeAxisAccelerationFromTargetSpeed(axisCommandId.lateral)\n autoNavigationEngineTags = autoNavigationEngineTags .. ' , ' .. lateralStrafeEngineTags\n autoNavigationAcceleration = autoNavigationAcceleration + lateralAcceleration\n end\n\n -- Vertical Translation\n local verticalStrafeEngineTags = 'thrust analog vertical'\n local verticalCommandType = Nav.axisCommandManager:getAxisCommandType(axisCommandId.vertical)\n if (verticalCommandType == axisCommandType.byThrottle) then\n local verticalStrafeAcceleration = Nav.axisCommandManager:composeAxisAccelerationFromThrottle(verticalStrafeEngineTags,axisCommandId.vertical)\n Nav:setEngineForceCommand(verticalStrafeEngineTags, verticalStrafeAcceleration, keepCollinearity, 'airfoil', 'ground', '', tolerancePercentToSkipOtherPriorities)\n elseif (verticalCommandType == axisCommandType.byTargetSpeed) then\n local verticalAcceleration = Nav.axisCommandManager:composeAxisAccelerationFromTargetSpeed(axisCommandId.vertical)\n autoNavigationEngineTags = autoNavigationEngineTags .. ' , ' .. verticalStrafeEngineTags\n autoNavigationAcceleration = autoNavigationAcceleration + verticalAcceleration\n end\n\n -- Auto Navigation (Cruise Control)\n if (autoNavigationAcceleration:len() > constants.epsilon) then\n if (brakeInput ~= 0 or autoNavigationUseBrake or math.abs(constructVelocityDir:dot(constructForward)) < 0.95) -- if the velocity is not properly aligned with the forward\n then\n autoNavigationEngineTags = autoNavigationEngineTags .. ', brake'\n end\n Nav:setEngineForceCommand(autoNavigationEngineTags, autoNavigationAcceleration, dontKeepCollinearity, '', '', '', tolerancePercentToSkipOtherPriorities)\n end\n\n -- Rockets\n Nav:setBoosterCommand('rocket_engine')\n end\n}\n\nlocal systemOnUpdate = {\n function ()\n Nav:update()\n end,\n function ()\n --That function is used for rendering the HUD, you can update it or replace all its content to customise it. You can also remove it if you don't want a HUD\n local direction = 'Stabilizing'\n if ElevatorData.verticalSpeedSigned > 0 then\n direction = 'Up'\n elseif ElevatorData.verticalSpeedSigned < 0 then\n direction = 'Down'\n end\n\n local brakeStatus = 'Off'\n if ElevatorData.isBreaking then\n brakeStatus = 'On'\n end\n local html = [[\n <style>\n * {\n font-size:1vh !important;\n }\n .hud {\n position: absolute;\n left: 5vh;\n top: 5vh;\n right: 5vh;\n display: flex;\n flex-direction: column;\n justify-content: left;\n align-items: left;\n }\n .widget_container {\n border: 2px solid orange;\n border-radius:.5vh;\n background-color: rgba(0, 0, 0, .5);\n display: flex;\n flex-direction: column;\n padding:.5vh;\n margin-top:1vh;\n }\n .widget_container div {\n display: flex;\n flex-direction: row;\n justify-content: space-between;\n }\n .widget_container div div {\n margin:.25vh;\n }\n .widget_container div div:first-child {\n text-transform: uppercase;\n font-weight: bold;\n }\n .selected {\n color: teal;\n }\n .movingto {\n color: green;\n }\n .atmo .gauge {\n background-color: #22d3ee;\n }\n .atmo .gauge_label {\n color: #155e75;\n }\n .space .gauge {\n background-color: #fbbf24;\n }\n .space .gauge_label {\n color: #92400e;\n }\n .rocket .gauge {\n background-color: #a78bfa;\n }\n .rocket .gauge_label {\n color: #5b21b6;\n }\n .gauge_container {\n display:block;\n width:100%;\n min-width:10vw;\n position: relative;\n border: 1px solid black;\n background-color: rgba(0, 0, 0, .75);\n height: 1.5vh;\n }\n .gauge {\n position: absolute;\n z-index:1;\n top:-.25vh;\n left:-.25vh;\n height:100%;\n margin:0;\n background-color: cyan;\n }\n .gauge_label {\n position:relative;\n display:block;\n z-index:10;\n margin:0;\n padding:0;\n width:100%;\n text-align:center;\n }\n </style>\n <div class=\"hud\">\n <div class=\"widget_container\">\n <div>\n <div>Base Altitude</div><div>]] .. format_number(utils.round(BaseAltitute)) .. [[m</div>\n </div>\n <div>\n <div>Target Altitude</div><div>]] .. format_number(utils.round(TargetAltitude)) .. [[m</div>\n </div>\n <div>\n <div>Current Altitude</div><div>]] .. format_number(utils.round(ElevatorData.altitude)) .. [[m</div>\n </div>\n <div>\n <div>Vertical Speed</div><div>]] .. format_number(math.abs(utils.round(ElevatorData.verticalSpeed*3.6))) .. [[km/h</div>\n </div>\n <div>\n <div>Lateral Speed</div><div>]] .. format_number(math.abs(utils.round(ElevatorData.lateralSpeed*3.6))) .. [[km/h</div>\n </div>\n <div>\n <div>Longitudinal Speed</div><div>]] .. format_number(math.abs(utils.round(ElevatorData.longitudinalSpeed*3.6))) .. [[km/h</div>\n </div>\n <div>\n <div>Direction</div><div>]] .. ElevatorData.direction .. [[</div>\n </div>\n <div>\n <div>Brake</div><div>]] .. brakeStatus .. [[</div>\n </div>\n </div>\n ]]\n if #fuelTanks > 0 then\n html = html .. '<div class=\"widget_container\">'\n for _, tank in pairs(fuelTanks) do\n html = html .. '<div><div>' .. tank:getName() .. '</div><div>' .. tank:getTimeLeftString() .. '</div></div><div class=\"gauge_container ' .. tank:getFuelType() .. '\"><div class=\"gauge\" style=\"width:' .. utils.round(tank:getPercentFilled()) .. '%;\"></div><div class=\"gauge_label\"><div style=\"margin-left:auto;margin-right:auto;padding:0;margin-top:-.25vh;\">' .. format_number(utils.round(tank:getPercentFilled())) .. '%</div></div></div>'\n end\n html = html .. '</div>'\n end\n if #Bookmarks > 0 then\n html = html .. '<div class=\"widget_container\"><div><div>Bookmarks</div></div>'\n for index, bookmark in ipairs(Bookmarks) do\n local class=''\n if selectedBookmarkIndex == index then\n class = 'selected'\n end\n local displayName = bookmark.name\n if selectedBookmarkIndex > 0 and bookmark.altitude == TargetAltitude then\n displayName = ' >> ' .. bookmark.name\n class = 'movingto'\n end\n html = html .. '<div class=\"' .. class .. '\"><div>' .. displayName .. '</div><div>' .. format_number(bookmark.altitude) .. 'm</div></div>'\n end\n html = html .. '</div>'\n end\n\n html = html .. '</div>'\n system.setScreen(html)\n end\n}\n\nScript.system:onUpdate(systemOnUpdate)\nScript.system:onFlush(systemOnFlush)\n\n--[[\n Actions\n]]\nlocal systemActionsStart = {}\n\nsystemActionsStart[Script.system.ACTIONS.DOWN] = function ()\n if selectedBookmarkIndex < #Bookmarks then\n selectedBookmarkIndex = selectedBookmarkIndex + 1\n else\n selectedBookmarkIndex = 1\n end\n if __DEBUG then system.print('Selected Bookmark Index: ' .. selectedBookmarkIndex) end\nend\nsystemActionsStart[Script.system.ACTIONS.UP] = function ()\n if selectedBookmarkIndex > 1 then\n selectedBookmarkIndex = selectedBookmarkIndex - 1\n else\n selectedBookmarkIndex = #Bookmarks\n end\n if __DEBUG then system.print('Selected Bookmark Index: ' .. selectedBookmarkIndex) end\nend\nsystemActionsStart[Script.system.ACTIONS.STRAFE_RIGHT] = function ()\n brakeInput = 0\n if selectedBookmarkIndex > 0 then\n TargetAltitude = Bookmarks[selectedBookmarkIndex].altitude\n if __DEBUG then system.print('Target Altitude: ' .. TargetAltitude) end\n else\n system.print('No bookmark selected')\n end\nend\nScript.system:onActionStart(systemActionsStart) --loading all \"actionStart\" functions\n\n--[[\n Chat Commands\n]]\n\nScript.system:onInputText(function (text)\n if text:lower():find('goto:') then\n TargetAltitude = tonumber(text:split(':')[2]) or BaseAltitute\n brakeInput = 0\n system.print('Target Altitude set to ' .. TargetAltitude .. 'm')\n else\n system.print('Unknown command')\n end\nend)\n\n","filter":{"args":[],"signature":"onStart()","slotKey":"-1"},"key":"2"}],"methods":[],"slots":{"0":{"name":"slot1","type":{"events":[],"methods":[]}},"1":{"name":"slot2","type":{"events":[],"methods":[]}},"2":{"name":"slot3","type":{"events":[],"methods":[]}},"3":{"name":"slot4","type":{"events":[],"methods":[]}},"4":{"name":"slot5","type":{"events":[],"methods":[]}},"5":{"name":"slot6","type":{"events":[],"methods":[]}},"6":{"name":"slot7","type":{"events":[],"methods":[]}},"7":{"name":"slot8","type":{"events":[],"methods":[]}},"8":{"name":"slot9","type":{"events":[],"methods":[]}},"9":{"name":"slot10","type":{"events":[],"methods":[]}},"-5":{"name":"library","type":{"events":[],"methods":[]}},"-4":{"name":"system","type":{"events":[],"methods":[]}},"-3":{"name":"player","type":{"events":[],"methods":[]}},"-2":{"name":"construct","type":{"events":[],"methods":[]}},"-1":{"name":"unit","type":{"events":[],"methods":[]}}}}