[READ-ONLY] Mirror of https://github.com/thoda-dev/du-elevator. a generic elevator script for Dual Universe
0

Configure Feed

Select the types of activity you want to include in your feed.

v1.3.0 - HUD rendering rework, infinite altitude, atlas data

Thomas (Aug 6, 2023, 11:36 AM +0200) 94265d4c 80bfbfc9

+235 -164
+26 -1
README.MD
··· 49 49 - `ShowControlUnitWidget` : if set to true, the script will display the default control unit widget 50 50 - `YawSpeedFactor` : the speed multiplier for auto yaw. 51 51 - `StrafeSpeedFactor` : the speed multiplier when strafing. adjust it depending on how many engines you have on the sides 52 + - `DisplayAtlasData` : show the Atlas widget with closest planet informations 52 53 53 54 ## Usage 54 55 ··· 70 71 71 72 - Option 1 (default Alt+1): save the current position and orientation as initial values in the databank 72 73 74 + # Customize the HUD 75 + 76 + This is script is build to be customizable. A function rendering the HUD can be replaced by your own rendering to add informations on the HUD. 77 + 78 + ## How to customize the HUD 79 + 80 + In Unit > OnStart, around line 28, you will fin a method name RENDER_HUD. This method is called every frame to render the HUD. 81 + 82 + You can replace its content by your own code to render your own HUD. 83 + 84 + The method has one argument named ElevatorData which is a table containing all the informations you need to render your HUD. 85 + 86 + ### ElevatorData structure 87 + | Name | Type | Description | 88 + | --- | --- | --- | 89 + | ElevatorData.isBreaking | boolean | if the brakes a are enabled or not. | 90 + | ElevatorData.verticalSpeed | number | the absolute vertical speed of the elevator in m/s. | 91 + | ElevatorData.verticalSpeedSigned | number | the real vertical speed of the elevator in m/s. if <0 the elevator is falling. | 92 + | ElevatorData.lateralSpeed | numver | the absolute lateral speed of the elevator in m/s. | 93 + | ElevatorData.longitudinalSpeed | number | the absolute Longitudinal speed of the elevator in m/s. | 94 + | ElevatorData.coreAltitude | number | the altitude from sea level returned by the core in meters. (warning: this altitude in space when far from planets is 0) | 95 + | ElevatorData.altitude | number | computed altitude from the distance between the construct and the sea level of the closest planet. (not 0 when in space) | 96 + | ElevatorData.planetData | table | Atlas Data of the closest planet. See atlas in game file for the structure (Game Directory\data\lua\atlas.lua) | 97 + 73 98 # Coming Soon 74 99 75 - - support for emitter to be able to send a signal to a specific channel to close a door to lock the elevator in position 76 100 - screen support to display position informations and bookmarks and navigate by clicking on it 101 + - ~~support for emitter to be able to send a signal to a specific channel to close a door to lock the elevator in position~~ This feature will not be done but replaced by a more generic system to manage several events than can be used to interact with any thing in the construct (doors, lights, etc...) or on another construct (like a door on the floor) via an emitter. 77 102 78 103 # Support or donation 79 104
+208 -162
Source/Unit/OnStart.lua
··· 14 14 { name = 'Space 2', altitude = 6000 }, 15 15 } 16 16 17 - atlas = require "atlas" 18 - 19 17 --[[ 20 18 LUA PARAMETERS 21 19 ]] 22 20 __DEBUG = false --export: Debug mode, will print more information in the console 23 21 ShowParentingWidget = false --export: Show the parenting widget 24 22 ShowControlUnitWidget = false --export: show the default widget of the construct 23 + DisplayAtlasData = true --export: show the Atlas widget with closest planet informations 24 + 25 + --[[ 26 + HUD Rendering 27 + ]] 28 + function RENDER_HUD(ElevatorData) 29 + --[[ 30 + That function is used for rendering the HUD, 31 + You can update it or replace all its content to customise it. 32 + You can also remove its content if you don't want a HUD. 33 + It's not affecting in any way how the elevator is working and it's just a display 34 + 35 + ElevatorData is an object containing values that are refresh all the time. 36 + ElevatorData.isBreaking - boolean - if the brakes a are enabled or not. 37 + ElevatorData.verticalSpeed - number - the absolute vertical speed of the elevator in m/s. 38 + ElevatorData.verticalSpeedSigned - number - the real vertical speed of the elevator in m/s. if <0 the elevator is falling. 39 + ElevatorData.lateralSpeed - numver - the absolute lateral speed of the elevator in m/s. 40 + ElevatorData.longitudinalSpeed - number - the absolute Longitudinal speed of the elevator in m/s. 41 + ElevatorData.coreAltitude - number - the altitude from sea level returned by the core in meters. (warning: this altitude in space when far from planets is 0) 42 + ElevatorData.altitude - number - computed altitude from the distance between the construct and the sea level of the closest planet. (not 0 when in space) 43 + ElevatorData.planetData - table - Atlas Data of the closest planet. See atlas in game file for the structure (Game Directory\data\lua\atlas.lua) 44 + ]] 45 + --locale for Translation 46 + local locale = system.getLocale() 47 + localeIndex = 1 --default to english 48 + if locale == 'fr-FR' then 49 + localeIndex = 2 --french index in atlas 50 + elseif locale == 'de-DE' then 51 + localeIndex = 3 --german index in atlas 52 + end 53 + --direction if the elevator from the speed 54 + local direction = 'Stabilizing' 55 + if ElevatorData.verticalSpeedSigned > 0 then 56 + direction = 'Up' 57 + elseif ElevatorData.verticalSpeedSigned < 0 then 58 + direction = 'Down' 59 + end 60 + --braking status text for hud 61 + local brakeStatus = 'Off' 62 + if ElevatorData.isBreaking then 63 + brakeStatus = 'On' 64 + end 65 + --html rendering 66 + local html = [[ 67 + <style> 68 + * {font-size:1vh !important;} 69 + .hud { 70 + position: absolute; 71 + left: 5vh; 72 + top: 5vh; 73 + right: 5vh; 74 + display: flex; 75 + flex-direction: column; 76 + justify-content: left; 77 + align-items: left; 78 + } 79 + .widget_container { 80 + border: 2px solid orange; 81 + border-radius:.5vh; 82 + background-color: rgba(0, 0, 0, .5); 83 + display: flex; 84 + flex-direction: column; 85 + padding:.5vh; 86 + margin-top:1vh; 87 + } 88 + .widget_container div { 89 + display: flex; 90 + flex-direction: row; 91 + justify-content: space-between; 92 + } 93 + .widget_container div div { 94 + margin:.25vh; 95 + } 96 + .widget_container div div:first-child { 97 + text-transform: uppercase; 98 + font-weight: bold; 99 + } 100 + .selected { 101 + color: teal; 102 + } 103 + .movingto { 104 + color: green; 105 + } 106 + .atmo .gauge { 107 + background-color: #22d3ee; 108 + } 109 + .atmo .gauge_label { 110 + color: #155e75; 111 + } 112 + .space .gauge { 113 + background-color: #fbbf24; 114 + } 115 + .space .gauge_label { 116 + color: #92400e; 117 + } 118 + .rocket .gauge { 119 + background-color: #a78bfa; 120 + } 121 + .rocket .gauge_label { 122 + color: #5b21b6; 123 + } 124 + .gauge_container { 125 + display:block; 126 + width:100%; 127 + min-width:10vw; 128 + position: relative; 129 + border: 1px solid black; 130 + background-color: rgba(0, 0, 0, .75); 131 + height: 1.5vh; 132 + } 133 + .gauge { 134 + position: absolute; 135 + z-index:1; 136 + top:-.25vh; 137 + left:-.25vh; 138 + height:100%; 139 + margin:0; 140 + background-color: cyan; 141 + } 142 + .gauge_label { 143 + position:relative; 144 + display:block; 145 + z-index:10; 146 + margin:0; 147 + padding:0; 148 + width:100%; 149 + text-align:center; 150 + } 151 + </style> 152 + <div class="hud"> 153 + <div class="widget_container"> 154 + <div> 155 + <div>Base Altitude</div><div>]] .. format_number(utils.round(BaseAltitute)) .. [[m</div> 156 + </div> 157 + <div> 158 + <div>Target Altitude</div><div>]] .. format_number(utils.round(TargetAltitude)) .. [[m</div> 159 + </div> 160 + <div> 161 + <div>Current Altitude</div><div>]] .. format_number(utils.round(ElevatorData.altitude)) .. [[m</div> 162 + </div> 163 + <div> 164 + <div>Vertical Speed</div><div>]] .. format_number(math.abs(utils.round(ElevatorData.verticalSpeed*3.6))) .. [[km/h</div> 165 + </div> 166 + <div> 167 + <div>Lateral Speed</div><div>]] .. format_number(math.abs(utils.round(ElevatorData.lateralSpeed*3.6))) .. [[km/h</div> 168 + </div> 169 + <div> 170 + <div>Longitudinal Speed</div><div>]] .. format_number(math.abs(utils.round(ElevatorData.longitudinalSpeed*3.6))) .. [[km/h</div> 171 + </div> 172 + <div> 173 + <div>Direction</div><div>]] .. ElevatorData.direction .. [[</div> 174 + </div> 175 + <div> 176 + <div>Brake</div><div>]] .. brakeStatus .. [[</div> 177 + </div> 178 + </div> 179 + ]] 180 + if #fuelTanks > 0 then 181 + html = html .. '<div class="widget_container">' 182 + for _, tank in pairs(fuelTanks) do 183 + 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>' 184 + end 185 + html = html .. '</div>' 186 + end 187 + if #Bookmarks > 0 then 188 + html = html .. '<div class="widget_container"><div><div style="text-align:center !important;border-bottom:1px solid black;width:100%;">Bookmarks</div></div>' 189 + for index, bookmark in ipairs(Bookmarks) do 190 + local class='' 191 + if selectedBookmarkIndex == index then 192 + class = 'selected' 193 + end 194 + local displayName = bookmark.name 195 + if selectedBookmarkIndex > 0 and bookmark.altitude == TargetAltitude then 196 + displayName = ' >> ' .. bookmark.name 197 + class = 'movingto' 198 + end 199 + html = html .. '<div class="' .. class .. '"><div>' .. displayName .. '</div><div>' .. format_number(bookmark.altitude) .. 'm</div></div>' 200 + end 201 + html = html .. '</div>' 202 + end 203 + if DisplayAtlasData then 204 + html = html .. '<div class="widget_container">' 205 + html = html .. '<div><div style="text-align:center;border-bottom:1px solid black;width:100%;">Atlas Data</div></div>' 206 + --html = html .. '<div><img src="/'..ElevatorData.planetData.iconPath..'" style="width:10vh;height:10vh;"></div>' --image is blinking in hud, you can display it here but by default i'm removing it 207 + html = html .. '<div><div>Planet Name</div><div>'..ElevatorData.planetData.name[localeIndex]..'</div></div>' 208 + html = html .. '<div><div>Planet Type</div><div>'..ElevatorData.planetData.type[localeIndex]..'</div></div>' 209 + html = html .. '<div><div>Planet gravity</div><div>'..ElevatorData.planetData.gravity..'m/s²</div></div>' 210 + html = html .. '<div><div>Planet Surface Average Altitude</div><div>'..ElevatorData.planetData.surfaceAverageAltitude..'m</div></div>' 211 + html = html .. '<div><div>Planet Surface Min Altitude</div><div>'..ElevatorData.planetData.surfaceMinAltitude..'m</div></div>' 212 + html = html .. '<div><div>Planet Surface Max Altitude</div><div>'..ElevatorData.planetData.surfaceMaxAltitude..'m</div></div>' 213 + html = html .. '<div><div>Planet Max Altitude For Satic Core</div><div>'..ElevatorData.planetData.maxStaticAltitude..'m</div></div>' 214 + html = html .. '</div>' 215 + end 216 + html = html .. '</div>' 217 + system.setScreen(html) --render the HUD 218 + end 25 219 26 220 --[[ 27 221 Version Management 28 222 ]] 29 - local version = "V 1.2.0" 223 + local version = "V 1.3.0" 30 224 local log_split = "=================================================" 31 225 --printing version in lua chat 32 226 system.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) ··· 73 267 system.print('Core unit is not linked, exiting the script') 74 268 unit.exit() 75 269 end 270 + 271 + --[[ 272 + Atlas 273 + ]] 274 + atlas = require"atlas" 76 275 77 276 --[[ 78 277 Default Unit Start Mechanics ··· 156 355 verticalSpeedSigned = 0, 157 356 lateralSpeed = 0, 158 357 longitudinalSpeed = 0, 358 + coreAltitude = TargetAltitude, 159 359 altitude = TargetAltitude, 160 - direction = 'stabilizing', 360 + planetData = atlas[0][core.getCurrentPlanetId()] 161 361 } 162 362 163 363 --TODO: to replace with a computing in flush from the current acceleration and the friction acceleration (construct.getWorldAirFrictionAcceleration) ··· 363 563 if maxBrake ~= nil then 364 564 local g = core.getGravityIntensity() 365 565 if ElevatorData.verticalSpeedSigned < 0 then 366 - g = atlas[0][core.getCurrentPlanetId()].gravity --when falling, apply the max gravity to the braking computing to increase braking 566 + g = ElevatorData.planetData.gravity --when falling, apply the max gravity to the braking computing to increase braking 367 567 end 368 568 local inertialMass = construct.getInertialMass() 369 569 local speedSign = utils.sign(ElevatorData.verticalSpeedSigned) 370 570 brakeDistance, _ = computeDistanceAndTime(ElevatorData.verticalSpeed, 0, inertialMass, 0, 0, maxBrake - (g * inertialMass * speedSign)) 371 571 end 372 - ElevatorData.altitude = core.getAltitude() 572 + ElevatorData.coreAltitude = core.getAltitude() 573 + ElevatorData.altitude = (vec3(ElevatorData.planetData.center) - vec3(constructWorldPosition)):len() - ElevatorData.planetData.radius 373 574 local distance = TargetAltitude - ElevatorData.altitude 374 575 local targetDistance = utils.sign(distance) * (math.abs(distance)-brakeDistance) 375 576 distancePID:inject(targetDistance) ··· 473 674 local systemOnUpdate = { 474 675 function () 475 676 Nav:update() 476 - end, 477 - function () 478 - --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 479 - local direction = 'Stabilizing' 480 - if ElevatorData.verticalSpeedSigned > 0 then 481 - direction = 'Up' 482 - elseif ElevatorData.verticalSpeedSigned < 0 then 483 - direction = 'Down' 484 - end 485 - 486 - local brakeStatus = 'Off' 487 - if ElevatorData.isBreaking then 488 - brakeStatus = 'On' 489 - end 490 - local html = [[ 491 - <style> 492 - * { 493 - font-size:1vh !important; 494 - } 495 - .hud { 496 - position: absolute; 497 - left: 5vh; 498 - top: 5vh; 499 - right: 5vh; 500 - display: flex; 501 - flex-direction: column; 502 - justify-content: left; 503 - align-items: left; 504 - } 505 - .widget_container { 506 - border: 2px solid orange; 507 - border-radius:.5vh; 508 - background-color: rgba(0, 0, 0, .5); 509 - display: flex; 510 - flex-direction: column; 511 - padding:.5vh; 512 - margin-top:1vh; 513 - } 514 - .widget_container div { 515 - display: flex; 516 - flex-direction: row; 517 - justify-content: space-between; 518 - } 519 - .widget_container div div { 520 - margin:.25vh; 521 - } 522 - .widget_container div div:first-child { 523 - text-transform: uppercase; 524 - font-weight: bold; 525 - } 526 - .selected { 527 - color: teal; 528 - } 529 - .movingto { 530 - color: green; 531 - } 532 - .atmo .gauge { 533 - background-color: #22d3ee; 534 - } 535 - .atmo .gauge_label { 536 - color: #155e75; 537 - } 538 - .space .gauge { 539 - background-color: #fbbf24; 540 - } 541 - .space .gauge_label { 542 - color: #92400e; 543 - } 544 - .rocket .gauge { 545 - background-color: #a78bfa; 546 - } 547 - .rocket .gauge_label { 548 - color: #5b21b6; 549 - } 550 - .gauge_container { 551 - display:block; 552 - width:100%; 553 - min-width:10vw; 554 - position: relative; 555 - border: 1px solid black; 556 - background-color: rgba(0, 0, 0, .75); 557 - height: 1.5vh; 558 - } 559 - .gauge { 560 - position: absolute; 561 - z-index:1; 562 - top:-.25vh; 563 - left:-.25vh; 564 - height:100%; 565 - margin:0; 566 - background-color: cyan; 567 - } 568 - .gauge_label { 569 - position:relative; 570 - display:block; 571 - z-index:10; 572 - margin:0; 573 - padding:0; 574 - width:100%; 575 - text-align:center; 576 - } 577 - </style> 578 - <div class="hud"> 579 - <div class="widget_container"> 580 - <div> 581 - <div>Base Altitude</div><div>]] .. format_number(utils.round(BaseAltitute)) .. [[m</div> 582 - </div> 583 - <div> 584 - <div>Target Altitude</div><div>]] .. format_number(utils.round(TargetAltitude)) .. [[m</div> 585 - </div> 586 - <div> 587 - <div>Current Altitude</div><div>]] .. format_number(utils.round(ElevatorData.altitude)) .. [[m</div> 588 - </div> 589 - <div> 590 - <div>Vertical Speed</div><div>]] .. format_number(math.abs(utils.round(ElevatorData.verticalSpeed*3.6))) .. [[km/h</div> 591 - </div> 592 - <div> 593 - <div>Lateral Speed</div><div>]] .. format_number(math.abs(utils.round(ElevatorData.lateralSpeed*3.6))) .. [[km/h</div> 594 - </div> 595 - <div> 596 - <div>Longitudinal Speed</div><div>]] .. format_number(math.abs(utils.round(ElevatorData.longitudinalSpeed*3.6))) .. [[km/h</div> 597 - </div> 598 - <div> 599 - <div>Direction</div><div>]] .. ElevatorData.direction .. [[</div> 600 - </div> 601 - <div> 602 - <div>Brake</div><div>]] .. brakeStatus .. [[</div> 603 - </div> 604 - </div> 605 - ]] 606 - if #fuelTanks > 0 then 607 - html = html .. '<div class="widget_container">' 608 - for _, tank in pairs(fuelTanks) do 609 - 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>' 610 - end 611 - html = html .. '</div>' 612 - end 613 - if #Bookmarks > 0 then 614 - html = html .. '<div class="widget_container"><div><div>Bookmarks</div></div>' 615 - for index, bookmark in ipairs(Bookmarks) do 616 - local class='' 617 - if selectedBookmarkIndex == index then 618 - class = 'selected' 619 - end 620 - local displayName = bookmark.name 621 - if selectedBookmarkIndex > 0 and bookmark.altitude == TargetAltitude then 622 - displayName = ' >> ' .. bookmark.name 623 - class = 'movingto' 624 - end 625 - html = html .. '<div class="' .. class .. '"><div>' .. displayName .. '</div><div>' .. format_number(bookmark.altitude) .. 'm</div></div>' 626 - end 627 - html = html .. '</div>' 628 - end 629 - 630 - html = html .. '</div>' 631 - system.setScreen(html) 677 + RENDER_HUD(ElevatorData) 632 678 end 633 679 } 634 680
+1 -1
config.json
··· 1 - {"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 = 294 },\n { name = 'Hovering', altitude = 300 },\n { name = 'Floor 1', altitude = 350 },\n { name = 'Floor 2', altitude = 500 },\n { name = 'Space 1', altitude = 5000 },\n { name = 'Space 2', altitude = 6000 },\n}\n\natlas = require \"atlas\"\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.2.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 Lua Serializer by Jericho\n Version: 1.0.0\n Source available at: https://github.com/Jericho1060/DualUniverse/blob/master/Serializer/Serializer.lua\n]]\nSerializer={__index={stringify=function(self,a)if type(a)=='number'then return self:stringifyNumber(a)elseif type(a)=='string'then return self:stringifyString(a)elseif type(a)=='table'then return self:stringifyTable(a)elseif type(a)=='boolean'then return self:stringifyBoolean(a)end;return nil end,parse=function(self,b)return load(\"return \"..b)()end,stringifyTableKey=function(self,c)if type(c)=='number'then return'['..self:stringifyNumber(c)..']'end;return c end,stringifyNumber=function(self,d)return tostring(d)end,stringifyString=function(self,b)return string.format('%q',b):gsub('\\\\\\n','\\n'):gsub('\\\\\\r','\\r'):gsub('\\\\\\t','\\t')end,stringifyTable=function(self,e)local b='{'local f=1;local g=self:tableLength(e)for h,i in pairs(e)do b=b..self:stringifyTableKey(h)..'='..self:stringify(i)if f<g then b=b..','end;f=f+1 end;return b..'}'end,stringifyBoolean=function(self,j)return tostring(j)end,tableLength=function(self,e)local k=0;for l in pairs(e)do k=k+1 end;return k end}}\nSerializer = setmetatable({}, Serializer)\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 = {}\ndatabank = nil\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 elseif class:find(\"databank\") then\n databank = 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\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]]\nfunction storeIniPosAndForward(force)\n if force == nil then force = false end\n ConstructInitPos = construct.getWorldPosition()\n --replace that pos by a hand written value to be sure the construct will always realign the same position\n --ConstructInitPos = {-1231461.6525868,1201334.4594833,-2617227.3718653}\n if __DEBUG then system.print('ConstructInitPos: ' .. json.encode(ConstructInitPos)) end\n BaseForward = construct.getWorldForward()\n --replace that pos by a hand written value to be sure the construct will always realign the same forward orientation\n --BaseForward = {-0.085554607212543,0.2445342447567,-0.9658600687807}\n if __DEBUG then system.print('BaseForward: ' .. json.encode(BaseForward)) end\n if databank ~= nil then\n if databank.hasKey('ConstructInitPos') and not force then\n BaseForward = Serializer:parse(databank.getStringValue('ConstructInitPos'))\n else\n local toStore = Serializer:stringify(ConstructInitPos)\n databank.setStringValue('ConstructInitPos', toStore)\n system.print(\"ConstructInitPos stored in databank\")\n system.print(toStore)\n end\n if databank.hasKey('BaseForward') and not force then\n BaseForward = Serializer:parse(databank.getStringValue('BaseForward'))\n else\n local toStore = Serializer:stringify(BaseForward)\n databank.setStringValue('BaseForward', toStore)\n system.print(\"BaseForward stored in databank\")\n system.print(toStore)\n end\n else\n system.print(\"No Databank detected\")\n end\n BaseForward = vec3(BaseForward)\nend\nConstructInitPos = nil\nBaseForward = nil\nstoreIniPosAndForward()\nif ConstructInitPos == nil or BaseForward == nil then\n system.print(\"An Error occurs during Init. Try removing the databank, remove its dynamic properties and install it again ...\")\nend\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*3.6) .. 'km/h') end\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 = 0.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 = 1 --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 local g = core.getGravityIntensity()\n if ElevatorData.verticalSpeedSigned < 0 then\n g = atlas[0][core.getCurrentPlanetId()].gravity --when falling, apply the max gravity to the braking computing to increase braking\n end\n local inertialMass = construct.getInertialMass()\n local speedSign = utils.sign(ElevatorData.verticalSpeedSigned)\n brakeDistance, _ = computeDistanceAndTime(ElevatorData.verticalSpeed, 0, inertialMass, 0, 0, maxBrake - (g * inertialMass * speedSign))\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 --use engines to help braking\n if ElevatorData.verticalSpeedSigned < 0 then\n Nav.axisCommandManager:setThrottleCommand(axisCommandId.vertical, 1)\n elseif ElevatorData.verticalSpeedSigned > 0 then\n Nav.axisCommandManager:setThrottleCommand(axisCommandId.vertical, -1)\n end\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\nsystemActionsStart[Script.system.ACTIONS.OPTION_1] = function()\n storeIniPosAndForward(true)\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","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":[]}}}} 1 + {"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 = 294 },\n { name = 'Hovering', altitude = 300 },\n { name = 'Floor 1', altitude = 350 },\n { name = 'Floor 2', altitude = 500 },\n { name = 'Space 1', altitude = 5000 },\n { name = 'Space 2', altitude = 6000 },\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\nDisplayAtlasData = true --export: show the Atlas widget with closest planet informations\n\n--[[\n HUD Rendering\n]]\nfunction RENDER_HUD(ElevatorData)\n --[[\n That function is used for rendering the HUD,\n You can update it or replace all its content to customise it.\n You can also remove its content if you don't want a HUD.\n It's not affecting in any way how the elevator is working and it's just a display\n \n ElevatorData is an object containing values that are refresh all the time.\n ElevatorData.isBreaking - boolean - if the brakes a are enabled or not.\n ElevatorData.verticalSpeed - number - the absolute vertical speed of the elevator in m/s.\n ElevatorData.verticalSpeedSigned - number - the real vertical speed of the elevator in m/s. if <0 the elevator is falling.\n ElevatorData.lateralSpeed - numver - the absolute lateral speed of the elevator in m/s.\n ElevatorData.longitudinalSpeed - number - the absolute Longitudinal speed of the elevator in m/s.\n ElevatorData.coreAltitude - number - the altitude from sea level returned by the core in meters. (warning: this altitude in space when far from planets is 0)\n ElevatorData.altitude - number - computed altitude from the distance between the construct and the sea level of the closest planet. (not 0 when in space)\n ElevatorData.planetData - table - Atlas Data of the closest planet. See atlas in game file for the structure (Game Directory\\data\\lua\\atlas.lua)\n ]]\n --locale for Translation\n local locale = system.getLocale()\n localeIndex = 1 --default to english\n if locale == 'fr-FR' then\n localeIndex = 2 --french index in atlas\n elseif locale == 'de-DE' then\n localeIndex = 3 --german index in atlas\n end\n --direction if the elevator from the speed\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 --braking status text for hud\n local brakeStatus = 'Off'\n if ElevatorData.isBreaking then\n brakeStatus = 'On'\n end\n --html rendering\n local html = [[\n <style>\n * {font-size:1vh !important;}\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 style=\"text-align:center !important;border-bottom:1px solid black;width:100%;\">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 if DisplayAtlasData then\n html = html .. '<div class=\"widget_container\">'\n html = html .. '<div><div style=\"text-align:center;border-bottom:1px solid black;width:100%;\">Atlas Data</div></div>'\n --html = html .. '<div><img src=\"/'..ElevatorData.planetData.iconPath..'\" style=\"width:10vh;height:10vh;\"></div>' --image is blinking in hud, you can display it here but by default i'm removing it\n html = html .. '<div><div>Planet Name</div><div>'..ElevatorData.planetData.name[localeIndex]..'</div></div>'\n html = html .. '<div><div>Planet Type</div><div>'..ElevatorData.planetData.type[localeIndex]..'</div></div>'\n html = html .. '<div><div>Planet gravity</div><div>'..ElevatorData.planetData.gravity..'m/s²</div></div>'\n html = html .. '<div><div>Planet Surface Average Altitude</div><div>'..ElevatorData.planetData.surfaceAverageAltitude..'m</div></div>'\n html = html .. '<div><div>Planet Surface Min Altitude</div><div>'..ElevatorData.planetData.surfaceMinAltitude..'m</div></div>'\n html = html .. '<div><div>Planet Surface Max Altitude</div><div>'..ElevatorData.planetData.surfaceMaxAltitude..'m</div></div>'\n html = html .. '<div><div>Planet Max Altitude For Satic Core</div><div>'..ElevatorData.planetData.maxStaticAltitude..'m</div></div>'\n html = html .. '</div>'\n end\n html = html .. '</div>'\n system.setScreen(html) --render the HUD\nend\n\n--[[\n Version Management\n]]\nlocal version = \"V 1.3.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 Lua Serializer by Jericho\n Version: 1.0.0\n Source available at: https://github.com/Jericho1060/DualUniverse/blob/master/Serializer/Serializer.lua\n]]\nSerializer={__index={stringify=function(self,a)if type(a)=='number'then return self:stringifyNumber(a)elseif type(a)=='string'then return self:stringifyString(a)elseif type(a)=='table'then return self:stringifyTable(a)elseif type(a)=='boolean'then return self:stringifyBoolean(a)end;return nil end,parse=function(self,b)return load(\"return \"..b)()end,stringifyTableKey=function(self,c)if type(c)=='number'then return'['..self:stringifyNumber(c)..']'end;return c end,stringifyNumber=function(self,d)return tostring(d)end,stringifyString=function(self,b)return string.format('%q',b):gsub('\\\\\\n','\\n'):gsub('\\\\\\r','\\r'):gsub('\\\\\\t','\\t')end,stringifyTable=function(self,e)local b='{'local f=1;local g=self:tableLength(e)for h,i in pairs(e)do b=b..self:stringifyTableKey(h)..'='..self:stringify(i)if f<g then b=b..','end;f=f+1 end;return b..'}'end,stringifyBoolean=function(self,j)return tostring(j)end,tableLength=function(self,e)local k=0;for l in pairs(e)do k=k+1 end;return k end}}\nSerializer = setmetatable({}, Serializer)\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 = {}\ndatabank = nil\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 elseif class:find(\"databank\") then\n databank = 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 Atlas\n]]\natlas = require\"atlas\"\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]]\nfunction storeIniPosAndForward(force)\n if force == nil then force = false end\n ConstructInitPos = construct.getWorldPosition()\n --replace that pos by a hand written value to be sure the construct will always realign the same position\n --ConstructInitPos = {-1231461.6525868,1201334.4594833,-2617227.3718653}\n if __DEBUG then system.print('ConstructInitPos: ' .. json.encode(ConstructInitPos)) end\n BaseForward = construct.getWorldForward()\n --replace that pos by a hand written value to be sure the construct will always realign the same forward orientation\n --BaseForward = {-0.085554607212543,0.2445342447567,-0.9658600687807}\n if __DEBUG then system.print('BaseForward: ' .. json.encode(BaseForward)) end\n if databank ~= nil then\n if databank.hasKey('ConstructInitPos') and not force then\n BaseForward = Serializer:parse(databank.getStringValue('ConstructInitPos'))\n else\n local toStore = Serializer:stringify(ConstructInitPos)\n databank.setStringValue('ConstructInitPos', toStore)\n system.print(\"ConstructInitPos stored in databank\")\n system.print(toStore)\n end\n if databank.hasKey('BaseForward') and not force then\n BaseForward = Serializer:parse(databank.getStringValue('BaseForward'))\n else\n local toStore = Serializer:stringify(BaseForward)\n databank.setStringValue('BaseForward', toStore)\n system.print(\"BaseForward stored in databank\")\n system.print(toStore)\n end\n else\n system.print(\"No Databank detected\")\n end\n BaseForward = vec3(BaseForward)\nend\nConstructInitPos = nil\nBaseForward = nil\nstoreIniPosAndForward()\nif ConstructInitPos == nil or BaseForward == nil then\n system.print(\"An Error occurs during Init. Try removing the databank, remove its dynamic properties and install it again ...\")\nend\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 coreAltitude = TargetAltitude,\n altitude = TargetAltitude,\n planetData = atlas[0][core.getCurrentPlanetId()]\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*3.6) .. 'km/h') end\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 = 0.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 = 1 --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 local g = core.getGravityIntensity()\n if ElevatorData.verticalSpeedSigned < 0 then\n g = ElevatorData.planetData.gravity --when falling, apply the max gravity to the braking computing to increase braking\n end\n local inertialMass = construct.getInertialMass()\n local speedSign = utils.sign(ElevatorData.verticalSpeedSigned)\n brakeDistance, _ = computeDistanceAndTime(ElevatorData.verticalSpeed, 0, inertialMass, 0, 0, maxBrake - (g * inertialMass * speedSign))\n end\n ElevatorData.coreAltitude = core.getAltitude()\n ElevatorData.altitude = (vec3(ElevatorData.planetData.center) - vec3(constructWorldPosition)):len() - ElevatorData.planetData.radius\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 --use engines to help braking\n if ElevatorData.verticalSpeedSigned < 0 then\n Nav.axisCommandManager:setThrottleCommand(axisCommandId.vertical, 1)\n elseif ElevatorData.verticalSpeedSigned > 0 then\n Nav.axisCommandManager:setThrottleCommand(axisCommandId.vertical, -1)\n end\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 RENDER_HUD(ElevatorData)\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\nsystemActionsStart[Script.system.ACTIONS.OPTION_1] = function()\n storeIniPosAndForward(true)\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","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":[]}}}}