[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.1.0 - bookmarks, HUD and better precision and speed

Thomas (Jul 23, 2023, 4:26 PM +0200) d8b6388a d543ff4b

+317 -61
+2
.gitignore
··· 1 + .idea 2 + .vscode
+23 -9
README.MD
··· 14 14 15 15 [![img](https://du-lua.dev/img/open_in_editor_button.png)](https://du-lua.dev/#/editor/github/Jericho1060/du-elevator) 16 16 17 - # Instalation 17 + # Installation 18 18 19 - ### Important notice 19 + ## Important notice 20 20 21 21 - the script was only tested on a remote control 22 22 23 - ### Links 23 + ## Links 24 24 25 25 You must link the following elements to the remote control (order doesn't matter, the script will detect them automatically): 26 26 - Link the core to the remote control 27 27 - Link the fuel tank to the remote control (only if you want to see the fuel gauge) 28 28 29 - ### Setup 29 + ## Setup 30 30 31 31 - you need at least a vertical engine pointing the bottom 32 32 - you need at least one engine on each horizontal direction (forward, right, left, backward) ··· 38 38 39 39 You may need to adjust the power depending on the weight of your construct and on the total weight you want to lift. 40 40 41 - ### Installation of the script 41 + ## Installation of the script 42 42 43 43 - copy the content of the config.json file and paste it on the remote control (right click on the remote control -> advanced -> paste lua configuration from clipboard) 44 44 45 - ### Lua parameters 45 + ## Lua parameters 46 46 47 47 - `DEBUG` : if set to true, the script will print debug messages in the chat 48 - - `BaseAltitude` : the default altitude of the elevator when landed on the floor 48 + - `ShowParentingWidget` : if set to true, the script will display a widget showing the parenting of the elevator 49 + - `ShowControlUnitWidget` : if set to true, the script will display the default control unit widget 50 + - `YawSpeedFactor` : the speed multiplier for auto yaw. 49 51 - `StrafeSpeedFactor` : the speed multiplier when strafing. adjust it depending on how many engines you have on the sides 50 52 51 - ### Usage 53 + ## Usage 54 + 55 + ### Bookmarks 56 + 57 + You can set bookmarks to save altitudes with a name. Edit the script and go to Unit > OnStart, you will see a Bookmark list (table) at the top of the script. You can edit the list to add your custom points. The first element will be used ad the base altitude when the elevator is landed on the floor. 58 + 59 + Navigating using bookmarks : 60 + - you can navigate in the list in the HUD with your keyboard keys arrows up and down 61 + - pressing the key arrow right will start the travel to the selected bookmark 62 + 63 + Current selected bookmark is displayed in blue, the current bookmark the elevator is going to is displayed in green. 64 + 65 + ### Chat Command 52 66 53 67 - open the "lua" chat channel and type `goto:<altitude>` to go to the specified altitude with `<altitude>` the altitude in meters, if the altitude is missing or invalid, it will send you the `BaseAltitude` 54 68 ··· 61 75 62 76 # Support or donation 63 77 64 - if you like it, [<img src="https://github.com/Jericho1060/DU-Industry-HUD/blob/main/ressources/images/ko-fi.png?raw=true" width="150">](https://ko-fi.com/jericho1060) 78 + if you like it, [<img src="https://github.com/Jericho1060/DU-Industry-HUD/blob/main/ressources/images/ko-fi.png?raw=true" width="150">](https://ko-fi.com/jericho1060)
+291 -51
Source/Unit/OnStart.lua
··· 2 2 DU-ELEVATOR by Jericho 3 3 ]] 4 4 5 + --[[ 6 + Bookmarks: you can store several altitudes here to navigate easily 7 + ]] 8 + local Bookmarks = { 9 + { name = 'Start Point', altitude = 285 }, 10 + { name = 'Hovering', altitude = 300 }, 11 + { name = 'Floor 1', altitude = 350 }, 12 + { name = 'Floor 2', altitude = 500 } 13 + } 14 + 15 + --[[ 16 + LUA PARAMETERS 17 + ]] 5 18 __DEBUG = false --export: Debug mode, will print more information in the console 19 + ShowParentingWidget = false --export: Show the parenting widget 20 + ShowControlUnitWidget = false --export: show the default widget of the construct 6 21 7 22 --[[ 8 23 Version Management 9 24 ]] 10 - 11 - local version = "V 1.0.0" 25 + local version = "V 1.1.0" 12 26 local log_split = "=================================================" 13 27 --printing version in lua chat 14 28 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) 15 - 29 + --[[ 30 + Fuel Tanks Metatable By Jericho 31 + Version: 1.0.0 32 + Unminified Source available at: https://github.com/Jericho1060/DualUniverse/blob/master/ElementsMetatable/FuelTank.lua 33 + ]] 34 + FuelTank={__index={name='',percentage=0,timeLeft=0,lastRefresh=0,fuelType='rocket',refresh=function(self)local a=system.getUtcTime()if lastRefresh~=a then local b=self.getClass():lower()if b:find('atmo')then self.fuelType="atmo"elseif b:find('space')then self.fuelType="space"end;local c=self.getWidgetData()local d=json.decode(c)self.name=d.name;self.percentage=d.percentage;self.timeLeft=tonumber(d.timeLeft)self.lastRefresh=a end end,getName=function(self)self:refresh()return self.name end,getPercentFilled=function(self)self:refresh()return self.percentage end,getSecondsLeft=function(self)self:refresh()return self.timeLeft end,getFuelType=function(self)self:refresh()return self.fuelType end,getTimeLeftString=function(self)local e=self:getSecondsLeft()if e==nil or e<=0 then return"-"end;days=string.format("%2.f",math.floor(e/(3600*24)))hours=string.format("%2.f",math.floor(e/3600-days*24))mins=string.format("%2.f",math.floor(e/60-hours*60-days*24*60))secs=string.format("%2.f",math.floor(e-hours*3600-days*24*60*60-mins*60))str=""if tonumber(days)>0 then str=str..days.."d "end;if tonumber(hours)>0 then str=str..hours.."h "end;if tonumber(mins)>0 then str=str..mins.."m "end;if tonumber(secs)>0 then str=str..secs.."s"end;return str end,dump=function(self)self:refresh()system.print('{name="'..self.name..'",percentage='..self.percentage..',timeLeft='..self.timeLeft..',fuelType="'..self.fuelType..'"}')end}} 16 35 --[[ 17 36 Detecting elements linked 18 37 ]] 19 38 core = nil 20 39 fuelTanks = {} 21 - 22 40 for slot_name, slot in pairs(unit) do 23 41 if 24 - type(slot) == "table" 25 - and type(slot.export) == "table" 26 - and slot.getClass 42 + type(slot) == "table" 43 + and type(slot.export) == "table" 44 + and slot.getClass 27 45 then 28 46 local class = slot.getClass():lower() 29 47 if class:find("coreunit") then 30 48 core = slot 31 49 elseif class:find("fuelcontainer") then 32 - table.insert(fuelTanks, slot) 50 + fuelTanks[#fuelTanks + 1] = setmetatable(slot, FuelTank) 33 51 end 34 52 end 35 53 end ··· 64 82 unit.deactivateGroundEngineAltitudeStabilization() 65 83 66 84 -- Parenting widget 67 - parentingPanelId = system.createWidgetPanel("Docking") 68 - parentingWidgetId = system.createWidget(parentingPanelId,"parenting") 69 - system.addDataToWidget(unit.getWidgetDataId(),parentingWidgetId) 70 - 71 - -- Fuel widget generation (Updated code By Jericho) 72 - --TODO: replace by custom HUD 73 - if (#fuelTanks > 0) then 74 - fuelTankPanelId = system.createWidgetPanel("Fuel Tanks") 75 - fuelTankWidgetId = system.createWidget(fuelTankPanelId,"fuel_container") 76 - for _,tank in pairs(fuelTanks) do 77 - system.addDataToWidget(tank.getWidgetDataId(),fuelTankWidgetId) 78 - end 85 + if ShowParentingWidget then 86 + parentingPanelId = system.createWidgetPanel("Docking") 87 + parentingWidgetId = system.createWidget(parentingPanelId,"parenting") 88 + system.addDataToWidget(unit.getWidgetDataId(),parentingWidgetId) 89 + end 90 + if not ShowControlUnitWidget then 91 + unit.hideWidget() 79 92 end 80 93 81 94 --[[ ··· 87 100 if __DEBUG then system.print('ConstructInitPos: ' .. json.encode(ConstructInitPos)) end 88 101 BaseForward = vec3(construct.getWorldForward()) 89 102 if __DEBUG then system.print('BaseForward: ' .. json.encode(BaseForward)) end 90 - BaseAltitute = 285 --export: the start altitude of the elevator (lower position altitude) 103 + BaseAltitute = Bookmarks[1].altitude --getting the base altitude from the 1st bookmark 104 + --init a value to store the target altitude 105 + TargetAltitude = core.getAltitude() --by default to the start altitude to avoid falling down if in the air 106 + 107 + ElevatorData = { 108 + isBreaking = brakeInput == 1, 109 + verticalSpeed = 0, 110 + verticalSpeedSigned = 0, 111 + lateralSpeed = 0, 112 + longitudinalSpeed = 0, 113 + altitude = TargetAltitude, 114 + direction = 'stabilizing', 115 + } 91 116 92 117 --TODO: to replace with a computing in flush from the current acceleration and the friction acceleration (construct.getWorldAirFrictionAcceleration) 93 118 MaxSpeed = construct.getFrictionBurnSpeed() -- for security to avoid burning if going too fast 94 119 if __DEBUG then system.print('MaxSpeed: ' .. MaxSpeed) end 95 120 96 - --init a value to store the target altitude 97 - TargetAltitude = core.getAltitude() --by default to the start altitude to avoid falling down if in the air 121 + --base selected bookmark is 0 -> none selected as index 0 doesn't exist in lua 122 + selectedBookmarkIndex = 0 123 + 124 + --[[ 125 + init the HUD 126 + ]] 127 + system.showScreen(true) 98 128 99 129 --[[ 100 130 Kinematics functions by Jaylebreak 101 131 Source available at https://gitlab.com/JayleBreak/dualuniverse/-/blob/master/DUflightfiles/autoconf/custom/kinematics.lua 102 - ]] 132 + ]] 103 133 function computeAccelerationTime(initial, acceleration, final) return (final - initial)/acceleration end 104 134 function computeDistanceAndTime(initial, final, mass, thrust, t50, brakeThrust) 105 135 t50 = t50 or 0 ··· 126 156 while math.abs(timeToMax - lasttime) > 0.25 do 127 157 local t = (timeToMax + lasttime)/2 128 158 if speedchk(v(t)) then 129 - timeToMax = t 159 + timeToMax = t 130 160 else 131 161 lasttime = t 132 162 end ··· 146 176 end 147 177 148 178 --[[ 149 - Commpute angle betwwen vectors by Jericho 179 + Compute angle between vectors by Jericho 150 180 ]] 151 181 function signedAngleBetween(vec1, vec2, planeNormal) 152 182 local normVec1 = vec1:project_on_plane(planeNormal):normalize() ··· 159 189 end 160 190 return angle 161 191 end 162 - 192 + --[[ 193 + formatting numbers by adding a space between thousands by Jericho 194 + ]] 195 + function format_number(a)local b=a;while true do b,k=string.gsub(b,"^(-?%d+)(%d%d%d)",'%1 %2')if k==0 then break end end;local c=string.sub(b,-2)if c=='.0'then b=string.sub(b,1,b:len()-2)end;return b end 163 196 --[[ 164 197 DU-LUA-Framework by Jericho 165 198 Permit to code easier by grouping most of the code in a single event Unit > Start ··· 187 220 string = setmetatable(string, String) 188 221 string.__index = string 189 222 190 - local systemOnUpdate = { 191 - function () 192 - Nav:update() 193 - end 194 - } 195 - 196 223 local systemOnFlush = { 197 224 function() 198 - local yawSpeedFactor = 1.5 225 + local yawSpeedFactor = 1 --export: the auto yaw speed multiplier 199 226 local yawAccelerationFactor = 3 200 227 local lateralAntiDriftFactor = 1 201 228 local lateralStrafeFactor = 5 202 229 local brakeSpeedFactor = 1 203 - local brakeFlatFactor = 4 230 + local brakeFlatFactor = 4 204 231 local autoBrakeSpeed = 15 205 232 -- validate params 206 233 brakeSpeedFactor = math.max(brakeSpeedFactor, 0.01) ··· 233 260 234 261 -- Axis 235 262 local worldVertical = vec3(core.getWorldVertical()) 263 + local worldRight = vec3(core.getWorldRight()) 264 + local worldForward = vec3(core.getWorldForward()) 236 265 local constructUp = vec3(construct.getWorldOrientationUp()) 237 266 local constructForward = vec3(construct.getWorldOrientationForward()) 238 267 local constructRight = vec3(construct.getWorldOrientationRight()) ··· 248 277 local dontKeepCollinearity = 1 -- for easier reading 249 278 local tolerancePercentToSkipOtherPriorities = 1 -- if we are within this tolerance (in%), we don't go to the next priorities 250 279 280 + --fake braking value 281 + local canBrake = false 282 + 251 283 -- keeping the start position alignement 252 284 local StrafeSpeedFactor = 50 --export: useg to increase the force of the alignement, decrease the value if the alignement is too strong or increase it if it's too slow 253 285 local positionDifference = constructTargetPosition - constructWorldPosition 254 286 local lateralOffset = positionDifference:project_on(constructRight):len() * utils.sign(positionDifference:dot(constructRight)) 287 + ElevatorData.lateralSpeed = constructVelocity:project_on(worldRight):len() 288 + local lateralDistance = math.abs(lateralOffset) 255 289 local longitudinalOffset = positionDifference:project_on(constructForward):len() * utils.sign(positionDifference:dot(constructForward)) 290 + ElevatorData.longitudinalSpeed = constructVelocity:project_on(worldForward):len() 291 + local longitudinalDistance = math.abs(longitudinalOffset) 292 + if (lateralDistance < .25) and (longitudinalDistance < .25) then canBrake = true end 293 + if ((ElevatorData.lateralSpeed*3.6) > lateralDistance) or ((ElevatorData.longitudinalSpeed*3.6) > longitudinalDistance) then canBrake = true end 256 294 lateralPID:inject(lateralOffset) 257 295 Nav.axisCommandManager:setThrottleCommand(axisCommandId.lateral, lateralPID:get() * StrafeSpeedFactor) 258 296 longitudinalPID:inject(longitudinalOffset) ··· 268 306 pitchPID:inject(targetPitchDeg - currentPitchDeg) 269 307 yawPID:inject(-targetYawDeg*yawSpeedFactor) 270 308 271 - local constructYawTargetVelocity = -combinedRollYawInput * yawSpeedFactor 272 - local constructYawTargetAcceleration = yawAccelerationFactor * (constructYawTargetVelocity - constructYawVelocity) 273 309 local constructTargetAngularVelocity = rollPID:get() * constructForward + pitchPID:get() * constructRight + yawPID:get() * constructUp 274 310 275 311 Nav:setEngineTorqueCommand('torque', constructTargetAngularVelocity, keepCollinearity, 'airfoil', '', '', tolerancePercentToSkipOtherPriorities) 276 312 277 313 -- moving up or down from TargetAltitude 278 - local verticalSpeed = constructVelocity:project_on(worldVertical):len() 279 - local verticalSpeedSigned = verticalSpeed * -utils.sign(constructVelocity:dot(worldVertical)) 314 + ElevatorData.verticalSpeed = constructVelocity:project_on(worldVertical):len() 315 + ElevatorData.verticalSpeedSigned = ElevatorData.verticalSpeed * -utils.sign(constructVelocity:dot(worldVertical)) 280 316 local brakeDistance = 0 281 317 local maxBrake = construct.getMaxBrake() 282 318 if maxBrake ~= nil then 283 - brakeDistance, _ = computeDistanceAndTime(verticalSpeed, 0, construct.getInertialMass(), 0, 0, maxBrake - (core.getGravityIntensity() * construct.getInertialMass()) * utils.sign(verticalSpeedSigned)) 319 + brakeDistance, _ = computeDistanceAndTime(ElevatorData.verticalSpeed, 0, construct.getInertialMass(), 0, 0, maxBrake - (core.getGravityIntensity() * construct.getInertialMass()) * utils.sign(ElevatorData.verticalSpeedSigned)) 284 320 end 285 - local coreAltitude = core.getAltitude() 286 - local distance = TargetAltitude - coreAltitude 321 + ElevatorData.altitude = core.getAltitude() 322 + local distance = TargetAltitude - ElevatorData.altitude 287 323 local targetDistance = utils.sign(distance) * (math.abs(distance)-brakeDistance) 288 324 distancePID:inject(targetDistance) 289 - if distancePID:get() > 0.5 or verticalSpeedSigned < -MaxSpeed then --using a 0.5 meter deadband for stabilizing 325 + if distancePID:get() > 0.5 or ElevatorData.verticalSpeedSigned < -MaxSpeed then --using a 0.5 meter deadband for stabilizing 290 326 Nav.axisCommandManager:setThrottleCommand(axisCommandId.vertical, 1) 291 - elseif distancePID:get() < -0.5 or verticalSpeedSigned > MaxSpeed then 327 + ElevatorData.direction = 'up' 328 + if ElevatorData.verticalSpeedSigned < 0 then 329 + finalBrakeInput = 1 330 + end 331 + elseif distancePID:get() < -0.5 or ElevatorData.verticalSpeedSigned > MaxSpeed then 292 332 Nav.axisCommandManager:setThrottleCommand(axisCommandId.vertical, -1) 333 + ElevatorData.direction = 'down' 334 + if ElevatorData.verticalSpeedSigned > 0 then 335 + finalBrakeInput = 1 336 + end 293 337 else 294 338 Nav.axisCommandManager:resetCommand(axisCommandId.vertical) 339 + ElevatorData.direction = 'stabilizing' 295 340 end 296 341 297 342 --Brakes 298 343 if 299 - (math.abs(distance) < verticalSpeed) 300 - or verticalSpeed > MaxSpeed 301 - or (brakeDistance > math.abs(distance)) 302 - or (finalBrakeInput == 0 and autoBrakeSpeed > 0 and Nav.axisCommandManager.throttle == 0 and constructVelocity:len() < autoBrakeSpeed) 344 + (math.abs(distance) < (ElevatorData.verticalSpeed*3.6) and canBrake) 345 + or (ElevatorData.verticalSpeed >= MaxSpeed) 346 + or ((brakeDistance > math.abs(distance)) and canBrake) 347 + or (finalBrakeInput == 0 and autoBrakeSpeed > 0 and Nav.axisCommandManager.throttle == 0 and constructVelocity:len() < autoBrakeSpeed) 303 348 then 304 349 finalBrakeInput = 1 305 350 end 351 + ElevatorData.isBreaking = (finalBrakeInput == 1) 306 352 local brakeAcceleration = -finalBrakeInput * (brakeSpeedFactor * constructVelocity + brakeFlatFactor * constructVelocityDir) 307 - 353 + 308 354 Nav:setEngineForceCommand('brake', brakeAcceleration) 309 355 310 356 -- AutoNavigation regroups all the axis command by 'TargetSpeed' ··· 323 369 autoNavigationEngineTags = autoNavigationEngineTags .. ' , ' .. longitudinalEngineTags 324 370 autoNavigationAcceleration = autoNavigationAcceleration + longitudinalAcceleration 325 371 if (Nav.axisCommandManager:getTargetSpeed(axisCommandId.longitudinal) == 0 or -- we want to stop 326 - Nav.axisCommandManager:getCurrentToTargetDeltaSpeed(axisCommandId.longitudinal) < - Nav.axisCommandManager:getTargetSpeedCurrentStep(axisCommandId.longitudinal) * 0.5) -- if the longitudinal velocity would need some braking 372 + Nav.axisCommandManager:getCurrentToTargetDeltaSpeed(axisCommandId.longitudinal) < - Nav.axisCommandManager:getTargetSpeedCurrentStep(axisCommandId.longitudinal) * 0.5) -- if the longitudinal velocity would need some braking 327 373 then 328 374 autoNavigationUseBrake = true 329 375 end ··· 367 413 end 368 414 } 369 415 416 + local systemOnUpdate = { 417 + function () 418 + Nav:update() 419 + end, 420 + function () 421 + --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 422 + local direction = 'Stabilizing' 423 + if ElevatorData.verticalSpeedSigned > 0 then 424 + direction = 'Up' 425 + elseif ElevatorData.verticalSpeedSigned < 0 then 426 + direction = 'Down' 427 + end 428 + 429 + local brakeStatus = 'Off' 430 + if ElevatorData.isBreaking then 431 + brakeStatus = 'On' 432 + end 433 + local html = [[ 434 + <style> 435 + * { 436 + font-size:1vh !important; 437 + } 438 + .hud { 439 + position: absolute; 440 + left: 5vh; 441 + top: 5vh; 442 + right: 5vh; 443 + display: flex; 444 + flex-direction: column; 445 + justify-content: left; 446 + align-items: left; 447 + } 448 + .widget_container { 449 + border: 2px solid orange; 450 + border-radius:.5vh; 451 + background-color: rgba(0, 0, 0, .5); 452 + display: flex; 453 + flex-direction: column; 454 + padding:.5vh; 455 + margin-top:1vh; 456 + } 457 + .widget_container div { 458 + display: flex; 459 + flex-direction: row; 460 + justify-content: space-between; 461 + } 462 + .widget_container div div { 463 + margin:.25vh; 464 + } 465 + .widget_container div div:first-child { 466 + text-transform: uppercase; 467 + font-weight: bold; 468 + } 469 + .selected { 470 + color: teal; 471 + } 472 + .movingto { 473 + color: green; 474 + } 475 + .atmo .gauge { 476 + background-color: #22d3ee; 477 + } 478 + .atmo .gauge_label { 479 + color: #155e75; 480 + } 481 + .space .gauge { 482 + background-color: #fbbf24; 483 + } 484 + .space .gauge_label { 485 + color: #92400e; 486 + } 487 + .rocket .gauge { 488 + background-color: #a78bfa; 489 + } 490 + .rocket .gauge_label { 491 + color: #5b21b6; 492 + } 493 + .gauge_container { 494 + display:block; 495 + width:100%; 496 + min-width:10vw; 497 + position: relative; 498 + border: 1px solid black; 499 + background-color: rgba(0, 0, 0, .75); 500 + height: 1.5vh; 501 + } 502 + .gauge { 503 + position: absolute; 504 + z-index:1; 505 + top:-.25vh; 506 + left:-.25vh; 507 + height:100%; 508 + margin:0; 509 + background-color: cyan; 510 + } 511 + .gauge_label { 512 + position:relative; 513 + display:block; 514 + z-index:10; 515 + margin:0; 516 + padding:0; 517 + width:100%; 518 + text-align:center; 519 + } 520 + </style> 521 + <div class="hud"> 522 + <div class="widget_container"> 523 + <div> 524 + <div>Base Altitude</div><div>]] .. format_number(utils.round(BaseAltitute)) .. [[m</div> 525 + </div> 526 + <div> 527 + <div>Target Altitude</div><div>]] .. format_number(utils.round(TargetAltitude)) .. [[m</div> 528 + </div> 529 + <div> 530 + <div>Current Altitude</div><div>]] .. format_number(utils.round(ElevatorData.altitude)) .. [[m</div> 531 + </div> 532 + <div> 533 + <div>Vertical Speed</div><div>]] .. format_number(math.abs(utils.round(ElevatorData.verticalSpeed*3.6))) .. [[km/h</div> 534 + </div> 535 + <div> 536 + <div>Lateral Speed</div><div>]] .. format_number(math.abs(utils.round(ElevatorData.lateralSpeed*3.6))) .. [[km/h</div> 537 + </div> 538 + <div> 539 + <div>Longitudinal Speed</div><div>]] .. format_number(math.abs(utils.round(ElevatorData.longitudinalSpeed*3.6))) .. [[km/h</div> 540 + </div> 541 + <div> 542 + <div>Direction</div><div>]] .. ElevatorData.direction .. [[</div> 543 + </div> 544 + <div> 545 + <div>Brake</div><div>]] .. brakeStatus .. [[</div> 546 + </div> 547 + </div> 548 + ]] 549 + if #fuelTanks > 0 then 550 + html = html .. '<div class="widget_container">' 551 + for _, tank in pairs(fuelTanks) do 552 + 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>' 553 + end 554 + html = html .. '</div>' 555 + end 556 + if #Bookmarks > 0 then 557 + html = html .. '<div class="widget_container"><div><div>Bookmarks</div></div>' 558 + for index, bookmark in ipairs(Bookmarks) do 559 + local class='' 560 + if selectedBookmarkIndex == index then 561 + class = 'selected' 562 + end 563 + local displayName = bookmark.name 564 + if selectedBookmarkIndex > 0 and bookmark.altitude == TargetAltitude then 565 + displayName = ' >> ' .. bookmark.name 566 + class = 'movingto' 567 + end 568 + html = html .. '<div class="' .. class .. '"><div>' .. displayName .. '</div><div>' .. format_number(bookmark.altitude) .. 'm</div></div>' 569 + end 570 + html = html .. '</div>' 571 + end 572 + 573 + html = html .. '</div>' 574 + system.setScreen(html) 575 + end 576 + } 370 577 371 578 Script.system:onUpdate(systemOnUpdate) 372 579 Script.system:onFlush(systemOnFlush) 373 580 374 581 --[[ 582 + Actions 583 + ]] 584 + local systemActionsStart = {} 585 + 586 + systemActionsStart[Script.system.ACTIONS.DOWN] = function () 587 + if selectedBookmarkIndex < #Bookmarks then 588 + selectedBookmarkIndex = selectedBookmarkIndex + 1 589 + else 590 + selectedBookmarkIndex = 1 591 + end 592 + if __DEBUG then system.print('Selected Bookmark Index: ' .. selectedBookmarkIndex) end 593 + end 594 + systemActionsStart[Script.system.ACTIONS.UP] = function () 595 + if selectedBookmarkIndex > 1 then 596 + selectedBookmarkIndex = selectedBookmarkIndex - 1 597 + else 598 + selectedBookmarkIndex = #Bookmarks 599 + end 600 + if __DEBUG then system.print('Selected Bookmark Index: ' .. selectedBookmarkIndex) end 601 + end 602 + systemActionsStart[Script.system.ACTIONS.STRAFE_RIGHT] = function () 603 + brakeInput = 0 604 + if selectedBookmarkIndex > 0 then 605 + TargetAltitude = Bookmarks[selectedBookmarkIndex].altitude 606 + if __DEBUG then system.print('Target Altitude: ' .. TargetAltitude) end 607 + else 608 + system.print('No bookmark selected') 609 + end 610 + end 611 + Script.system:onActionStart(systemActionsStart) --loading all "actionStart" functions 612 + 613 + --[[ 375 614 Chat Commands 376 615 ]] 377 616 ··· 383 622 else 384 623 system.print('Unknown command') 385 624 end 386 - end) 625 + end) 626 +
+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__DEBUG = false --export: Debug mode, will print more information in the console\n\n--[[\n Version Management\n]]\n\nlocal version = \"V 1.0.1\"\nlocal log_split = \"=================================================\"\n--printing version in lua chat\nsystem.print(log_split)local a=\"\"local b=math.ceil((50-#version-2)/2)for c=1,b,1 do a=a..'='end;a=a..\" \"..version..\" \"for c=1,b,1 do a=a..'='end;system.print(a)system.print(log_split)\n\n--[[\n Detecting elements linked\n]]\ncore = nil\nfuelTanks = {}\n\nfor slot_name, slot in pairs(unit) do\n if\n type(slot) == \"table\"\n and type(slot.export) == \"table\"\n and slot.getClass\n then\n local class = slot.getClass():lower()\n if class:find(\"coreunit\") then\n core = slot\n elseif class:find(\"fuelcontainer\") then\n table.insert(fuelTanks, slot)\n end\n end\nend\n\n--[[\n stopping the script if required elements are not linked\n]]\nif core == nil then\n system.print('Core unit is not linked, exiting the script')\n unit.exit()\nend\n\n--[[\n Default Unit Start Mechanics\n]]\npitchInput = 0\npitchInputFromDevice = 0\nrollInput = 0\nyawInput = 0\nverticalStrafeInput = 0\nlateralStrafeInput = 0\nbrakeInput = 1 --braking by default\ngoingBack = false\ngoingForward = false\nshiftPressed = false\njumpDelta = 0\nbaseAcceleration = 0.8\n\nNav = Navigator.new(system, core, unit)\nNav.axisCommandManager:setupCustomTargetSpeedRanges(axisCommandId.longitudinal, {100, 500, 1000, 2000,3000, 5000})\nNav.axisCommandManager:setTargetGroundAltitude(0)\nunit.deactivateGroundEngineAltitudeStabilization()\n\n-- Parenting widget\nparentingPanelId = system.createWidgetPanel(\"Docking\")\nparentingWidgetId = system.createWidget(parentingPanelId,\"parenting\")\nsystem.addDataToWidget(unit.getWidgetDataId(),parentingWidgetId)\n\n-- Fuel widget generation (Updated code By Jericho)\n--TODO: replace by custom HUD\nif (#fuelTanks > 0) then\n fuelTankPanelId = system.createWidgetPanel(\"Fuel Tanks\")\n fuelTankWidgetId = system.createWidget(fuelTankPanelId,\"fuel_container\")\n for _,tank in pairs(fuelTanks) do\n system.addDataToWidget(tank.getWidgetDataId(),fuelTankWidgetId)\n end\nend\n\n--[[\n Storing base position and orientation of the elevator\n]]\nConstructInitPos = construct.getWorldPosition()\n--replace that pos by a hand written value to be sure the construct will always realign the same position\n--ConstructInitPos = {-1231538.185042, 1201297.6879598, -2617220.2766464}\nif __DEBUG then system.print('ConstructInitPos: ' .. json.encode(ConstructInitPos)) end\nBaseForward = vec3(construct.getWorldForward())\nif __DEBUG then system.print('BaseForward: ' .. json.encode(BaseForward)) end\nBaseAltitute = 285 --export: the start altitude of the elevator (lower position altitude)\n\n--TODO: to replace with a computing in flush from the current acceleration and the friction acceleration (construct.getWorldAirFrictionAcceleration)\nMaxSpeed = construct.getFrictionBurnSpeed() -- for security to avoid burning if going too fast\nif __DEBUG then system.print('MaxSpeed: ' .. MaxSpeed) end\n\n--init a value to store the target altitude\nTargetAltitude = core.getAltitude() --by default to the start altitude to avoid falling down if in the air\n\n--[[\n Kinematics functions by Jaylebreak\n Source available at https://gitlab.com/JayleBreak/dualuniverse/-/blob/master/DUflightfiles/autoconf/custom/kinematics.lua\n]] \nfunction computeAccelerationTime(initial, acceleration, final) return (final - initial)/acceleration end\nfunction computeDistanceAndTime(initial, final, mass, thrust, t50, brakeThrust)\n t50 = t50 or 0\n brakeThrust = brakeThrust or 0\n local speedUp = initial < final\n local a0 = thrust / (speedUp and mass or -mass)\n local b0 = -brakeThrust/mass\n local totA = a0+b0\n if initial == final then\n return 0, 0\n elseif speedUp and totA <= 0 or not speedUp and totA >= 0 then\n return -1, -1\n end\n local distanceToMax, timeToMax = 0, 0\n if a0 ~= 0 and t50 > 0 then\n local c1 = math.pi/t50/2\n local v = function(t)\n return a0*(t/2 - t50*math.sin(c1*t)/math.pi) + b0*t + initial\n end\n local speedchk = speedUp and function(s) return s >= final end or function(s) return s <= final end\n timeToMax = 2*t50\n if speedchk(v(timeToMax)) then\n local lasttime = 0\n while math.abs(timeToMax - lasttime) > 0.25 do\n local t = (timeToMax + lasttime)/2\n if speedchk(v(t)) then\n timeToMax = t \n else\n lasttime = t\n end\n end\n end\n local K = 2*a0*t50^2/math.pi^2\n distanceToMax = K*(math.cos(c1*timeToMax) - 1) + (a0+2*b0)*timeToMax^2/4 + initial*timeToMax\n if timeToMax < 2*t50 then\n return distanceToMax, timeToMax\n end\n initial = v(timeToMax)\n end\n local a = a0+b0\n local t = computeAccelerationTime(initial, a, final)\n local d = initial*t + a*t*t/2\n return distanceToMax+d, timeToMax+t\nend\n\n--[[\n Commpute angle betwwen vectors by Jericho\n]]\nfunction signedAngleBetween(vec1, vec2, planeNormal)\n local normVec1 = vec1:project_on_plane(planeNormal):normalize()\n local normVec2 = vec2:normalize()\n local v1v2dot = normVec1:dot(normVec2)\n local angle = math.acos(utils.clamp(v1v2dot,-1,1))\n local crossProduct = vec1:cross(vec2)\n if crossProduct:dot(planeNormal) < 0 then\n return -angle\n end\n return angle\nend\n\n--[[\n DU-LUA-Framework by Jericho\n Permit to code easier by grouping most of the code in a single event Unit > Start\n Unminified Source available here: https://github.com/Jericho1060/du-lua-framework\n]]--\nlocal a=system.print;local b=error;local c=pcall;local d=assert;local e=coroutine;local f=e.create;local g=e.status;local h=e.resume;local i=\"dead\"local j=\"suspended\"function runFunction(k,l,...)local m,n=c(k,...)if not m then b(l..n)end end;local o={__index={cos={update={},flush={}},fns={update={},flush={},action={start={},stop={},loop={}},inputText=nil,start=nil,stop=nil},ACTIONS={FORWARD=\"forward\",BACKWARD=\"backward\",YAW_LEFT=\"yawleft\",YAW_RIGHT=\"yawright\",STRAFE_LEFT=\"strafeleft\",STRAFE_RIGHT=\"straferight\",LEFT=\"left\",RIGHT=\"right\",UP=\"up\",DOWN=\"down\",GROUND_ALTITUDE_UP=\"groundaltitudeup\",GROUND_ALTITUDE_DOWN=\"groundaltitudedown\",LEFT_ALT=\"lalt\",LEFT_SHIFT=\"lshift\",GEAR=\"gear\",LIGHT=\"light\",BRAKE=\"brake\",OPTION_1=\"option1\",OPTION_2=\"option2\",OPTION_3=\"option3\",OPTION_4=\"option4\",OPTION_5=\"option5\",OPTION_6=\"option6\",OPTION_7=\"option7\",OPTION_8=\"option8\",OPTION_9=\"option9\",OPTION_10=\"option10\",OPTION_11=\"option11\",OPTION_12=\"option12\",OPTION_13=\"option13\",OPTION_14=\"option14\",OPTION_15=\"option15\",OPTION_16=\"option16\",OPTION_17=\"option17\",OPTION_18=\"option18\",OPTION_19=\"option19\",OPTION_20=\"option20\",OPTION_21=\"option21\",OPTION_22=\"option22\",OPTION_23=\"option23\",OPTION_24=\"option24\",OPTION_25=\"option25\",OPTION_26=\"option26\",OPTION_27=\"option27\",OPTION_28=\"option28\",OPTION_29=\"option29\",LEFT_MOUSE=\"leftmouse\",STOP_ENGINES=\"stopengines\",SPEED_UP=\"speedup\",SPEED_DOWN=\"speeddown\",ANTIGRAVITY=\"antigravity\",BOOSTER=\"booster\"},main={update=f(function()end),flush=f(function()end)},update=function(self)local p=g(self.main.update)if p==i then self.main.update=f(function()self:runUpdate()end)elseif p==j then d(h(self.main.update))end end,flush=function(self)local p=g(self.main.flush)if p==i then self.main.flush=f(function()self:runFlush()end)elseif p==j then d(h(self.main.flush))end end,action=function(self,q,r)if self.fns.action[q][r]then runFunction(self.fns.action[q][r],\"System Action \"..q..\" Error: \")end end,actionStart=function(self,r)self:action('start',r)end,actionStop=function(self,r)self:action('stop',r)end,actionLoop=function(self,r)self:action('loop',r)end,inputText=function(self,s)if self.fns.inputText then runFunction(self.fns.inputText,\"System Input Text Error: \",s)end end,runUpdate=function(self)for t,u in pairs(self.cos.update)do local m=g(u)if m==i then self.cos.update[t]=f(self.fns.update[t])elseif m==j then d(h(u))end end end,runFlush=function(self)for t,u in pairs(self.cos.flush)do local m=g(u)if m==i then self.cos.flush[t]=f(self.fns.flush[t])elseif m==j then d(h(u))end end end,onUpdate=function(self,v)for t,w in pairs(v)do self.fns.update[t]=w;self.cos.update[t]=f(w)end end,onFlush=function(self,v)for t,w in pairs(v)do self.fns.flush[t]=w;self.cos.flush[t]=f(w)end end,onAction=function(self,q,v)for t,w in pairs(v)do self.fns.action[q][t]=w end end,onActionStart=function(self,v)self:onAction(\"start\",v)end,onActionStop=function(self,v)self:onAction(\"stop\",v)end,onActionLoop=function(self,v)self:onAction(\"loop\",v)end,onInputText=function(self,k)self.fns.inputText=k end}}local x={__index={timers={},stopFn=function()end,timer=function(self,y)if self.timers[y]then runFunction(self.timers[y],\"Unit Timer \"..y..\" Error: \")end end,setTimer=function(self,y,z,k)self.timers[y]=k;unit.setTimer(y,z)end,stopTimer=function(self,y)unit.stopTimer(y)self.timers[y]=nil end,onStop=function(self,k)self.stopFn=k end,stop=function(self)if self.stopFn then runFunction(self.stopFn,\"Unit Stop Error: \")end end}}local A={__index={parentChangedFn=function(B,C)end,onParentChange=function(self,k)self.parentChangedFn=k end,parentChanged=function(self,B,C)if self.parentChangedFn then runFunction(self.parentChangedFn,\"Player Parent Changed Error: \",B,C)end end}}local D={__index={dockedFn=function(E)end,onDocked=function(self,k)self.dockedFn=k end,docked=function(self,E)if self.dockedFn then runFunction(self.dockedFn,\"Construct Docked Error: \",E)end end,undockedFn=function(E)end,onUndocked=function(self,k)self.undockedFn=k end,undocked=function(self,E)if self.undockedFn then runFunction(self.undockedFn,\"Construct Undocked Error: \",E)end end,playerBoardedFn=function(E)end,onPlayerBoarded=function(self,k)self.playerBoardedFn=k end,playerBoarded=function(self,E)if self.playerBoardedFn then runFunction(self.playerBoardedFn,\"Construct Player Boarded Error: \",E)end end,VRStationEnteredFn=function(E)end,onVRStationEntered=function(self,k)self.VRStationEnteredFn=k end,VRStationEntered=function(self,E)if self.VRStationEnteredFn then runFunction(self.VRStationEnteredFn,\"Construct VR Station Entered Error: \",E)end end,constructDockedFn=function(E)end,onConstructDocked=function(self,k)self.constructDockedFn=k end,constructDocked=function(self,E)if self.constructDockedFn then runFunction(self.constructDockedFn,\"Construct Construct Docked Error: \",E)end end,PvPTimerFn=function(F)end,onPvPTimer=function(self,k)self.PvPTimerFn=k end,PvPTimer=function(self,F)if self.PvPTimerFn then runFunction(self.PvPTimerFn,\"Construct PvP Timer Error: \",F)end end}}DU_Framework={__index={system=setmetatable({},o),unit=setmetatable({},x),player=setmetatable({},A),construct=setmetatable({},D)}}\n\nScript = {}\nsetmetatable(Script, DU_Framework)\n\n--[[\n String Helpers For Lua By Jericho\n]]\nString = {\n __index = {\n split = function(self, delimiter)\n local result = {}\n for match in (self..delimiter):gmatch(\"(.-)\"..delimiter) do\n table.insert(result, match)\n end\n return result\n end\n }\n}\nstring = setmetatable(string, String)\nstring.__index = string\n\nlocal systemOnUpdate = {\n function ()\n Nav:update()\n end\n}\n\nlocal systemOnFlush = {\n function()\n local yawSpeedFactor = 1.5\n local yawAccelerationFactor = 3\n local lateralAntiDriftFactor = 1\n local lateralStrafeFactor = 5\n local brakeSpeedFactor = 1\n local brakeFlatFactor = 4 \n local autoBrakeSpeed = 15\n -- validate params\n brakeSpeedFactor = math.max(brakeSpeedFactor, 0.01)\n brakeFlatFactor = math.max(brakeFlatFactor, 0.01)\n yawSpeedFactor = math.max(yawSpeedFactor, 0.01)\n yawAccelerationFactor = math.max(yawAccelerationFactor, 0.01)\n --init all the PIDs as global if first flush\n if (rollPID == nil) then rollPID = pid.new(0.2, 0, 10) end\n if (pitchPID == nil) then pitchPID = pid.new(0.2, 0, 10) end\n if (yawPID == nil) then yawPID = pid.new(0.2, 0, 10) end\n if (lateralPID == nil) then lateralPID = pid.new(0.2, 0, 10) end\n if (longitudinalPID == nil) then longitudinalPID = pid.new(0.2, 0, 10) end\n if (distancePID == nil) then distancePID = pid.new(0.2, 0, 10) end\n\n -- final inputs\n if unit.isMouseDirectControlActivated() then\n -- in direct control, we tweak the pitch to behave inbetween virtual joystick and direct control\n -- this helps a lot for ground construct control\n pitchInputFromDevice = utils.clamp(pitchInputFromDevice + system.getControlDeviceForwardInput() * system.getActionUpdateDeltaTime(), -1.0, 1.0)\n else\n pitchInputFromDevice = system.getControlDeviceForwardInput()\n end\n local finalPitchInput = pitchInput + pitchInputFromDevice\n local finalRollInput = rollInput + system.getControlDeviceYawInput()\n local finalYawInput = yawInput - system.getControlDeviceLeftRightInput()\n local combinedRollYawInput = utils.clamp(finalRollInput - finalYawInput, -1.0, 1.0);\n local finalVerticalStrafeInput = verticalStrafeInput\n local finalLateralStrafeInput = lateralStrafeInput;\n local finalBrakeInput = brakeInput\n\n -- Axis\n local worldVertical = vec3(core.getWorldVertical())\n local constructUp = vec3(construct.getWorldOrientationUp())\n local constructForward = vec3(construct.getWorldOrientationForward())\n local constructRight = vec3(construct.getWorldOrientationRight())\n local constructVelocity = vec3(construct.getWorldVelocity())\n local constructVelocityDir = vec3(construct.getWorldVelocity()):normalize()\n local constructAngularVelocity = vec3(construct.getWorldAngularVelocity())\n local constructYawVelocity = constructAngularVelocity:dot(constructUp)\n local constructWorldPosition = vec3(construct.getWorldPosition())\n local constructTargetPosition = vec3(ConstructInitPos)\n\n -- Engine commands\n local keepCollinearity = 0 -- for easier reading\n local dontKeepCollinearity = 1 -- for easier reading\n local tolerancePercentToSkipOtherPriorities = 1 -- if we are within this tolerance (in%), we don't go to the next priorities\n\n -- keeping the start position alignement\n local StrafeSpeedFactor = 50 --export: useg to increase the force of the alignement, decrease the value if the alignement is too strong or increase it if it's too slow\n local positionDifference = constructTargetPosition - constructWorldPosition\n local lateralOffset = positionDifference:project_on(constructRight):len() * utils.sign(positionDifference:dot(constructRight))\n local longitudinalOffset = positionDifference:project_on(constructForward):len() * utils.sign(positionDifference:dot(constructForward))\n lateralPID:inject(lateralOffset)\n Nav.axisCommandManager:setThrottleCommand(axisCommandId.lateral, lateralPID:get() * StrafeSpeedFactor)\n longitudinalPID:inject(longitudinalOffset)\n Nav.axisCommandManager:setThrottleCommand(axisCommandId.longitudinal, longitudinalPID:get() * StrafeSpeedFactor)\n\n -- Rotation\n local currentRollDeg = getRoll(worldVertical, constructForward, constructRight)\n local currentPitchDeg = -math.asin(constructForward:dot(worldVertical)) * constants.rad2deg\n local targetRollDeg = 0\n local targetPitchDeg = 0\n local targetYawDeg = signedAngleBetween(BaseForward,constructForward,constructUp)*180/math.pi\n rollPID:inject(targetRollDeg - currentRollDeg)\n pitchPID:inject(targetPitchDeg - currentPitchDeg)\n yawPID:inject(-targetYawDeg*yawSpeedFactor)\n \n local constructTargetAngularVelocity = rollPID:get() * constructForward + pitchPID:get() * constructRight + yawPID:get() * constructUp\n\n Nav:setEngineTorqueCommand('torque', constructTargetAngularVelocity, keepCollinearity, 'airfoil', '', '', tolerancePercentToSkipOtherPriorities)\n\n -- moving up or down from TargetAltitude\n local verticalSpeed = constructVelocity:project_on(worldVertical):len()\n local verticalSpeedSigned = verticalSpeed * -utils.sign(constructVelocity:dot(worldVertical))\n local brakeDistance = 0\n local maxBrake = construct.getMaxBrake()\n if maxBrake ~= nil then\n brakeDistance, _ = computeDistanceAndTime(verticalSpeed, 0, construct.getInertialMass(), 0, 0, maxBrake - (core.getGravityIntensity() * construct.getInertialMass()) * utils.sign(verticalSpeedSigned))\n end\n local coreAltitude = core.getAltitude()\n local distance = TargetAltitude - coreAltitude\n local targetDistance = utils.sign(distance) * (math.abs(distance)-brakeDistance)\n distancePID:inject(targetDistance)\n if distancePID:get() > 0.5 or verticalSpeedSigned < -MaxSpeed then --using a 0.5 meter deadband for stabilizing\n Nav.axisCommandManager:setThrottleCommand(axisCommandId.vertical, 1)\n elseif distancePID:get() < -0.5 or verticalSpeedSigned > MaxSpeed then\n Nav.axisCommandManager:setThrottleCommand(axisCommandId.vertical, -1)\n else\n Nav.axisCommandManager:resetCommand(axisCommandId.vertical)\n end\n\n --Brakes\n if\n (math.abs(distance) < verticalSpeed)\n or verticalSpeed > MaxSpeed\n or (brakeDistance > math.abs(distance))\n or (finalBrakeInput == 0 and autoBrakeSpeed > 0 and Nav.axisCommandManager.throttle == 0 and constructVelocity:len() < autoBrakeSpeed)\n then\n finalBrakeInput = 1\n end\n local brakeAcceleration = -finalBrakeInput * (brakeSpeedFactor * constructVelocity + brakeFlatFactor * constructVelocityDir)\n \n Nav:setEngineForceCommand('brake', brakeAcceleration)\n\n -- AutoNavigation regroups all the axis command by 'TargetSpeed'\n local autoNavigationEngineTags = ''\n local autoNavigationAcceleration = vec3()\n local autoNavigationUseBrake = false\n\n -- Longitudinal Translation\n local longitudinalEngineTags = 'thrust analog longitudinal'\n local longitudinalCommandType = Nav.axisCommandManager:getAxisCommandType(axisCommandId.longitudinal)\n if (longitudinalCommandType == axisCommandType.byThrottle) then\n local longitudinalAcceleration = Nav.axisCommandManager:composeAxisAccelerationFromThrottle(longitudinalEngineTags,axisCommandId.longitudinal)\n Nav:setEngineForceCommand(longitudinalEngineTags, longitudinalAcceleration, keepCollinearity)\n elseif (longitudinalCommandType == axisCommandType.byTargetSpeed) then\n local longitudinalAcceleration = Nav.axisCommandManager:composeAxisAccelerationFromTargetSpeed(axisCommandId.longitudinal)\n autoNavigationEngineTags = autoNavigationEngineTags .. ' , ' .. longitudinalEngineTags\n autoNavigationAcceleration = autoNavigationAcceleration + longitudinalAcceleration\n if (Nav.axisCommandManager:getTargetSpeed(axisCommandId.longitudinal) == 0 or -- we want to stop\n Nav.axisCommandManager:getCurrentToTargetDeltaSpeed(axisCommandId.longitudinal) < - Nav.axisCommandManager:getTargetSpeedCurrentStep(axisCommandId.longitudinal) * 0.5) -- if the longitudinal velocity would need some braking\n then\n autoNavigationUseBrake = true\n end\n end\n\n -- Lateral Translation\n local lateralStrafeEngineTags = 'thrust analog lateral'\n local lateralCommandType = Nav.axisCommandManager:getAxisCommandType(axisCommandId.lateral)\n if (lateralCommandType == axisCommandType.byThrottle) then\n local lateralStrafeAcceleration = Nav.axisCommandManager:composeAxisAccelerationFromThrottle(lateralStrafeEngineTags,axisCommandId.lateral)\n Nav:setEngineForceCommand(lateralStrafeEngineTags, lateralStrafeAcceleration, keepCollinearity)\n elseif (lateralCommandType == axisCommandType.byTargetSpeed) then\n local lateralAcceleration = Nav.axisCommandManager:composeAxisAccelerationFromTargetSpeed(axisCommandId.lateral)\n autoNavigationEngineTags = autoNavigationEngineTags .. ' , ' .. lateralStrafeEngineTags\n autoNavigationAcceleration = autoNavigationAcceleration + lateralAcceleration\n end\n\n -- Vertical Translation\n local verticalStrafeEngineTags = 'thrust analog vertical'\n local verticalCommandType = Nav.axisCommandManager:getAxisCommandType(axisCommandId.vertical)\n if (verticalCommandType == axisCommandType.byThrottle) then\n local verticalStrafeAcceleration = Nav.axisCommandManager:composeAxisAccelerationFromThrottle(verticalStrafeEngineTags,axisCommandId.vertical)\n Nav:setEngineForceCommand(verticalStrafeEngineTags, verticalStrafeAcceleration, keepCollinearity, 'airfoil', 'ground', '', tolerancePercentToSkipOtherPriorities)\n elseif (verticalCommandType == axisCommandType.byTargetSpeed) then\n local verticalAcceleration = Nav.axisCommandManager:composeAxisAccelerationFromTargetSpeed(axisCommandId.vertical)\n autoNavigationEngineTags = autoNavigationEngineTags .. ' , ' .. verticalStrafeEngineTags\n autoNavigationAcceleration = autoNavigationAcceleration + verticalAcceleration\n end\n\n -- Auto Navigation (Cruise Control)\n if (autoNavigationAcceleration:len() > constants.epsilon) then\n if (brakeInput ~= 0 or autoNavigationUseBrake or math.abs(constructVelocityDir:dot(constructForward)) < 0.95) -- if the velocity is not properly aligned with the forward\n then\n autoNavigationEngineTags = autoNavigationEngineTags .. ', brake'\n end\n Nav:setEngineForceCommand(autoNavigationEngineTags, autoNavigationAcceleration, dontKeepCollinearity, '', '', '', tolerancePercentToSkipOtherPriorities)\n end\n\n -- Rockets\n Nav:setBoosterCommand('rocket_engine')\n end\n}\n\n\nScript.system:onUpdate(systemOnUpdate)\nScript.system:onFlush(systemOnFlush)\n\n--[[\n Chat Commands\n]]\n\nScript.system:onInputText(function (text)\n if text:lower():find('goto:') then\n TargetAltitude = tonumber(text:split(':')[2]) or BaseAltitute\n brakeInput = 0\n system.print('Target Altitude set to ' .. TargetAltitude .. 'm')\n else\n system.print('Unknown command')\n end\nend)","filter":{"args":[],"signature":"onStart()","slotKey":"-1"},"key":"2"}],"methods":[],"slots":{"0":{"name":"slot1","type":{"events":[],"methods":[]}},"1":{"name":"slot2","type":{"events":[],"methods":[]}},"2":{"name":"slot3","type":{"events":[],"methods":[]}},"3":{"name":"slot4","type":{"events":[],"methods":[]}},"4":{"name":"slot5","type":{"events":[],"methods":[]}},"5":{"name":"slot6","type":{"events":[],"methods":[]}},"6":{"name":"slot7","type":{"events":[],"methods":[]}},"7":{"name":"slot8","type":{"events":[],"methods":[]}},"8":{"name":"slot9","type":{"events":[],"methods":[]}},"9":{"name":"slot10","type":{"events":[],"methods":[]}},"-5":{"name":"library","type":{"events":[],"methods":[]}},"-4":{"name":"system","type":{"events":[],"methods":[]}},"-3":{"name":"player","type":{"events":[],"methods":[]}},"-2":{"name":"construct","type":{"events":[],"methods":[]}},"-1":{"name":"unit","type":{"events":[],"methods":[]}}}} 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 = 285 },\n { name = 'Hovering', altitude = 300 },\n { name = 'Floor 1', altitude = 350 },\n { name = 'Floor 2', altitude = 500 }\n}\n\n--[[\n LUA PARAMETERS\n]]\n__DEBUG = false --export: Debug mode, will print more information in the console\nShowParentingWidget = false --export: Show the parenting widget\nShowControlUnitWidget = false --export: show the default widget of the construct\n\n--[[\n Version Management\n]]\nlocal version = \"V 1.1.0\"\nlocal log_split = \"=================================================\"\n--printing version in lua chat\nsystem.print(log_split)local a=\"\"local b=math.ceil((50-#version-2)/2)for c=1,b,1 do a=a..'='end;a=a..\" \"..version..\" \"for c=1,b,1 do a=a..'='end;system.print(a)system.print(log_split)\n--[[\n Fuel Tanks Metatable By Jericho\n Version: 1.0.0\n Unminified Source available at: https://github.com/Jericho1060/DualUniverse/blob/master/ElementsMetatable/FuelTank.lua\n]]\nFuelTank={__index={name='',percentage=0,timeLeft=0,lastRefresh=0,fuelType='rocket',refresh=function(self)local a=system.getUtcTime()if lastRefresh~=a then local b=self.getClass():lower()if b:find('atmo')then self.fuelType=\"atmo\"elseif b:find('space')then self.fuelType=\"space\"end;local c=self.getWidgetData()local d=json.decode(c)self.name=d.name;self.percentage=d.percentage;self.timeLeft=tonumber(d.timeLeft)self.lastRefresh=a end end,getName=function(self)self:refresh()return self.name end,getPercentFilled=function(self)self:refresh()return self.percentage end,getSecondsLeft=function(self)self:refresh()return self.timeLeft end,getFuelType=function(self)self:refresh()return self.fuelType end,getTimeLeftString=function(self)local e=self:getSecondsLeft()if e==nil or e<=0 then return\"-\"end;days=string.format(\"%2.f\",math.floor(e/(3600*24)))hours=string.format(\"%2.f\",math.floor(e/3600-days*24))mins=string.format(\"%2.f\",math.floor(e/60-hours*60-days*24*60))secs=string.format(\"%2.f\",math.floor(e-hours*3600-days*24*60*60-mins*60))str=\"\"if tonumber(days)>0 then str=str..days..\"d \"end;if tonumber(hours)>0 then str=str..hours..\"h \"end;if tonumber(mins)>0 then str=str..mins..\"m \"end;if tonumber(secs)>0 then str=str..secs..\"s\"end;return str end,dump=function(self)self:refresh()system.print('{name=\"'..self.name..'\",percentage='..self.percentage..',timeLeft='..self.timeLeft..',fuelType=\"'..self.fuelType..'\"}')end}}\n--[[\n Detecting elements linked\n]]\ncore = nil\nfuelTanks = {}\nfor slot_name, slot in pairs(unit) do\n if\n type(slot) == \"table\"\n and type(slot.export) == \"table\"\n and slot.getClass\n then\n local class = slot.getClass():lower()\n if class:find(\"coreunit\") then\n core = slot\n elseif class:find(\"fuelcontainer\") then\n fuelTanks[#fuelTanks + 1] = setmetatable(slot, FuelTank)\n end\n end\nend\n\n--[[\n stopping the script if required elements are not linked\n]]\nif core == nil then\n system.print('Core unit is not linked, exiting the script')\n unit.exit()\nend\n\n--[[\n Default Unit Start Mechanics\n]]\npitchInput = 0\npitchInputFromDevice = 0\nrollInput = 0\nyawInput = 0\nverticalStrafeInput = 0\nlateralStrafeInput = 0\nbrakeInput = 1 --braking by default\ngoingBack = false\ngoingForward = false\nshiftPressed = false\njumpDelta = 0\nbaseAcceleration = 0.8\n\nNav = Navigator.new(system, core, unit)\nNav.axisCommandManager:setupCustomTargetSpeedRanges(axisCommandId.longitudinal, {100, 500, 1000, 2000,3000, 5000})\nNav.axisCommandManager:setTargetGroundAltitude(0)\nunit.deactivateGroundEngineAltitudeStabilization()\n\n-- Parenting widget\nif ShowParentingWidget then\n parentingPanelId = system.createWidgetPanel(\"Docking\")\n parentingWidgetId = system.createWidget(parentingPanelId,\"parenting\")\n system.addDataToWidget(unit.getWidgetDataId(),parentingWidgetId)\nend\nif not ShowControlUnitWidget then\n unit.hideWidget()\nend\n\n--[[\n Storing base position and orientation of the elevator\n]]\nConstructInitPos = construct.getWorldPosition()\n--replace that pos by a hand written value to be sure the construct will always realign the same position\n--ConstructInitPos = {-1231538.185042, 1201297.6879598, -2617220.2766464}\nif __DEBUG then system.print('ConstructInitPos: ' .. json.encode(ConstructInitPos)) end\nBaseForward = vec3(construct.getWorldForward())\nif __DEBUG then system.print('BaseForward: ' .. json.encode(BaseForward)) end\nBaseAltitute = Bookmarks[1].altitude --getting the base altitude from the 1st bookmark\n--init a value to store the target altitude\nTargetAltitude = core.getAltitude() --by default to the start altitude to avoid falling down if in the air\n\nElevatorData = {\n isBreaking = brakeInput == 1,\n verticalSpeed = 0,\n verticalSpeedSigned = 0,\n lateralSpeed = 0,\n longitudinalSpeed = 0,\n altitude = TargetAltitude,\n direction = 'stabilizing',\n}\n\n--TODO: to replace with a computing in flush from the current acceleration and the friction acceleration (construct.getWorldAirFrictionAcceleration)\nMaxSpeed = construct.getFrictionBurnSpeed() -- for security to avoid burning if going too fast\nif __DEBUG then system.print('MaxSpeed: ' .. MaxSpeed) end\n\n--base selected bookmark is 0 -> none selected as index 0 doesn't exist in lua\nselectedBookmarkIndex = 0\n\n--[[\n init the HUD\n]]\nsystem.showScreen(true)\n\n--[[\n Kinematics functions by Jaylebreak\n Source available at https://gitlab.com/JayleBreak/dualuniverse/-/blob/master/DUflightfiles/autoconf/custom/kinematics.lua\n]]\nfunction computeAccelerationTime(initial, acceleration, final) return (final - initial)/acceleration end\nfunction computeDistanceAndTime(initial, final, mass, thrust, t50, brakeThrust)\n t50 = t50 or 0\n brakeThrust = brakeThrust or 0\n local speedUp = initial < final\n local a0 = thrust / (speedUp and mass or -mass)\n local b0 = -brakeThrust/mass\n local totA = a0+b0\n if initial == final then\n return 0, 0\n elseif speedUp and totA <= 0 or not speedUp and totA >= 0 then\n return -1, -1\n end\n local distanceToMax, timeToMax = 0, 0\n if a0 ~= 0 and t50 > 0 then\n local c1 = math.pi/t50/2\n local v = function(t)\n return a0*(t/2 - t50*math.sin(c1*t)/math.pi) + b0*t + initial\n end\n local speedchk = speedUp and function(s) return s >= final end or function(s) return s <= final end\n timeToMax = 2*t50\n if speedchk(v(timeToMax)) then\n local lasttime = 0\n while math.abs(timeToMax - lasttime) > 0.25 do\n local t = (timeToMax + lasttime)/2\n if speedchk(v(t)) then\n timeToMax = t\n else\n lasttime = t\n end\n end\n end\n local K = 2*a0*t50^2/math.pi^2\n distanceToMax = K*(math.cos(c1*timeToMax) - 1) + (a0+2*b0)*timeToMax^2/4 + initial*timeToMax\n if timeToMax < 2*t50 then\n return distanceToMax, timeToMax\n end\n initial = v(timeToMax)\n end\n local a = a0+b0\n local t = computeAccelerationTime(initial, a, final)\n local d = initial*t + a*t*t/2\n return distanceToMax+d, timeToMax+t\nend\n\n--[[\n Compute angle between vectors by Jericho\n]]\nfunction signedAngleBetween(vec1, vec2, planeNormal)\n local normVec1 = vec1:project_on_plane(planeNormal):normalize()\n local normVec2 = vec2:normalize()\n local v1v2dot = normVec1:dot(normVec2)\n local angle = math.acos(utils.clamp(v1v2dot,-1,1))\n local crossProduct = vec1:cross(vec2)\n if crossProduct:dot(planeNormal) < 0 then\n return -angle\n end\n return angle\nend\n--[[\n\tformatting numbers by adding a space between thousands by Jericho\n]]\nfunction format_number(a)local b=a;while true do b,k=string.gsub(b,\"^(-?%d+)(%d%d%d)\",'%1 %2')if k==0 then break end end;local c=string.sub(b,-2)if c=='.0'then b=string.sub(b,1,b:len()-2)end;return b end\n--[[\n DU-LUA-Framework by Jericho\n Permit to code easier by grouping most of the code in a single event Unit > Start\n Unminified Source available here: https://github.com/Jericho1060/du-lua-framework\n]]--\nlocal a=system.print;local b=error;local c=pcall;local d=assert;local e=coroutine;local f=e.create;local g=e.status;local h=e.resume;local i=\"dead\"local j=\"suspended\"function runFunction(k,l,...)local m,n=c(k,...)if not m then b(l..n)end end;local o={__index={cos={update={},flush={}},fns={update={},flush={},action={start={},stop={},loop={}},inputText=nil,start=nil,stop=nil},ACTIONS={FORWARD=\"forward\",BACKWARD=\"backward\",YAW_LEFT=\"yawleft\",YAW_RIGHT=\"yawright\",STRAFE_LEFT=\"strafeleft\",STRAFE_RIGHT=\"straferight\",LEFT=\"left\",RIGHT=\"right\",UP=\"up\",DOWN=\"down\",GROUND_ALTITUDE_UP=\"groundaltitudeup\",GROUND_ALTITUDE_DOWN=\"groundaltitudedown\",LEFT_ALT=\"lalt\",LEFT_SHIFT=\"lshift\",GEAR=\"gear\",LIGHT=\"light\",BRAKE=\"brake\",OPTION_1=\"option1\",OPTION_2=\"option2\",OPTION_3=\"option3\",OPTION_4=\"option4\",OPTION_5=\"option5\",OPTION_6=\"option6\",OPTION_7=\"option7\",OPTION_8=\"option8\",OPTION_9=\"option9\",OPTION_10=\"option10\",OPTION_11=\"option11\",OPTION_12=\"option12\",OPTION_13=\"option13\",OPTION_14=\"option14\",OPTION_15=\"option15\",OPTION_16=\"option16\",OPTION_17=\"option17\",OPTION_18=\"option18\",OPTION_19=\"option19\",OPTION_20=\"option20\",OPTION_21=\"option21\",OPTION_22=\"option22\",OPTION_23=\"option23\",OPTION_24=\"option24\",OPTION_25=\"option25\",OPTION_26=\"option26\",OPTION_27=\"option27\",OPTION_28=\"option28\",OPTION_29=\"option29\",LEFT_MOUSE=\"leftmouse\",STOP_ENGINES=\"stopengines\",SPEED_UP=\"speedup\",SPEED_DOWN=\"speeddown\",ANTIGRAVITY=\"antigravity\",BOOSTER=\"booster\"},main={update=f(function()end),flush=f(function()end)},update=function(self)local p=g(self.main.update)if p==i then self.main.update=f(function()self:runUpdate()end)elseif p==j then d(h(self.main.update))end end,flush=function(self)local p=g(self.main.flush)if p==i then self.main.flush=f(function()self:runFlush()end)elseif p==j then d(h(self.main.flush))end end,action=function(self,q,r)if self.fns.action[q][r]then runFunction(self.fns.action[q][r],\"System Action \"..q..\" Error: \")end end,actionStart=function(self,r)self:action('start',r)end,actionStop=function(self,r)self:action('stop',r)end,actionLoop=function(self,r)self:action('loop',r)end,inputText=function(self,s)if self.fns.inputText then runFunction(self.fns.inputText,\"System Input Text Error: \",s)end end,runUpdate=function(self)for t,u in pairs(self.cos.update)do local m=g(u)if m==i then self.cos.update[t]=f(self.fns.update[t])elseif m==j then d(h(u))end end end,runFlush=function(self)for t,u in pairs(self.cos.flush)do local m=g(u)if m==i then self.cos.flush[t]=f(self.fns.flush[t])elseif m==j then d(h(u))end end end,onUpdate=function(self,v)for t,w in pairs(v)do self.fns.update[t]=w;self.cos.update[t]=f(w)end end,onFlush=function(self,v)for t,w in pairs(v)do self.fns.flush[t]=w;self.cos.flush[t]=f(w)end end,onAction=function(self,q,v)for t,w in pairs(v)do self.fns.action[q][t]=w end end,onActionStart=function(self,v)self:onAction(\"start\",v)end,onActionStop=function(self,v)self:onAction(\"stop\",v)end,onActionLoop=function(self,v)self:onAction(\"loop\",v)end,onInputText=function(self,k)self.fns.inputText=k end}}local x={__index={timers={},stopFn=function()end,timer=function(self,y)if self.timers[y]then runFunction(self.timers[y],\"Unit Timer \"..y..\" Error: \")end end,setTimer=function(self,y,z,k)self.timers[y]=k;unit.setTimer(y,z)end,stopTimer=function(self,y)unit.stopTimer(y)self.timers[y]=nil end,onStop=function(self,k)self.stopFn=k end,stop=function(self)if self.stopFn then runFunction(self.stopFn,\"Unit Stop Error: \")end end}}local A={__index={parentChangedFn=function(B,C)end,onParentChange=function(self,k)self.parentChangedFn=k end,parentChanged=function(self,B,C)if self.parentChangedFn then runFunction(self.parentChangedFn,\"Player Parent Changed Error: \",B,C)end end}}local D={__index={dockedFn=function(E)end,onDocked=function(self,k)self.dockedFn=k end,docked=function(self,E)if self.dockedFn then runFunction(self.dockedFn,\"Construct Docked Error: \",E)end end,undockedFn=function(E)end,onUndocked=function(self,k)self.undockedFn=k end,undocked=function(self,E)if self.undockedFn then runFunction(self.undockedFn,\"Construct Undocked Error: \",E)end end,playerBoardedFn=function(E)end,onPlayerBoarded=function(self,k)self.playerBoardedFn=k end,playerBoarded=function(self,E)if self.playerBoardedFn then runFunction(self.playerBoardedFn,\"Construct Player Boarded Error: \",E)end end,VRStationEnteredFn=function(E)end,onVRStationEntered=function(self,k)self.VRStationEnteredFn=k end,VRStationEntered=function(self,E)if self.VRStationEnteredFn then runFunction(self.VRStationEnteredFn,\"Construct VR Station Entered Error: \",E)end end,constructDockedFn=function(E)end,onConstructDocked=function(self,k)self.constructDockedFn=k end,constructDocked=function(self,E)if self.constructDockedFn then runFunction(self.constructDockedFn,\"Construct Construct Docked Error: \",E)end end,PvPTimerFn=function(F)end,onPvPTimer=function(self,k)self.PvPTimerFn=k end,PvPTimer=function(self,F)if self.PvPTimerFn then runFunction(self.PvPTimerFn,\"Construct PvP Timer Error: \",F)end end}}DU_Framework={__index={system=setmetatable({},o),unit=setmetatable({},x),player=setmetatable({},A),construct=setmetatable({},D)}}\n\nScript = {}\nsetmetatable(Script, DU_Framework)\n\n--[[\n String Helpers For Lua By Jericho\n]]\nString = {\n __index = {\n split = function(self, delimiter)\n local result = {}\n for match in (self..delimiter):gmatch(\"(.-)\"..delimiter) do\n table.insert(result, match)\n end\n return result\n end\n }\n}\nstring = setmetatable(string, String)\nstring.__index = string\n\nlocal systemOnFlush = {\n function()\n local yawSpeedFactor = 1 --export: the auto yaw speed multiplier\n local yawAccelerationFactor = 3\n local lateralAntiDriftFactor = 1\n local lateralStrafeFactor = 5\n local brakeSpeedFactor = 1\n local brakeFlatFactor = 4\n local autoBrakeSpeed = 15\n -- validate params\n brakeSpeedFactor = math.max(brakeSpeedFactor, 0.01)\n brakeFlatFactor = math.max(brakeFlatFactor, 0.01)\n yawSpeedFactor = math.max(yawSpeedFactor, 0.01)\n yawAccelerationFactor = math.max(yawAccelerationFactor, 0.01)\n --init all the PIDs as global if first flush\n if (rollPID == nil) then rollPID = pid.new(0.2, 0, 10) end\n if (pitchPID == nil) then pitchPID = pid.new(0.2, 0, 10) end\n if (yawPID == nil) then yawPID = pid.new(0.2, 0, 10) end\n if (lateralPID == nil) then lateralPID = pid.new(0.2, 0, 10) end\n if (longitudinalPID == nil) then longitudinalPID = pid.new(0.2, 0, 10) end\n if (distancePID == nil) then distancePID = pid.new(0.2, 0, 10) end\n\n -- final inputs\n if unit.isMouseDirectControlActivated() then\n -- in direct control, we tweak the pitch to behave inbetween virtual joystick and direct control\n -- this helps a lot for ground construct control\n pitchInputFromDevice = utils.clamp(pitchInputFromDevice + system.getControlDeviceForwardInput() * system.getActionUpdateDeltaTime(), -1.0, 1.0)\n else\n pitchInputFromDevice = system.getControlDeviceForwardInput()\n end\n local finalPitchInput = pitchInput + pitchInputFromDevice\n local finalRollInput = rollInput + system.getControlDeviceYawInput()\n local finalYawInput = yawInput - system.getControlDeviceLeftRightInput()\n local combinedRollYawInput = utils.clamp(finalRollInput - finalYawInput, -1.0, 1.0);\n local finalVerticalStrafeInput = verticalStrafeInput\n local finalLateralStrafeInput = lateralStrafeInput;\n local finalBrakeInput = brakeInput\n\n -- Axis\n local worldVertical = vec3(core.getWorldVertical())\n local worldRight = vec3(core.getWorldRight())\n local worldForward = vec3(core.getWorldForward())\n local constructUp = vec3(construct.getWorldOrientationUp())\n local constructForward = vec3(construct.getWorldOrientationForward())\n local constructRight = vec3(construct.getWorldOrientationRight())\n local constructVelocity = vec3(construct.getWorldVelocity())\n local constructVelocityDir = vec3(construct.getWorldVelocity()):normalize()\n local constructAngularVelocity = vec3(construct.getWorldAngularVelocity())\n local constructYawVelocity = constructAngularVelocity:dot(constructUp)\n local constructWorldPosition = vec3(construct.getWorldPosition())\n local constructTargetPosition = vec3(ConstructInitPos)\n\n -- Engine commands\n local keepCollinearity = 0 -- for easier reading\n local dontKeepCollinearity = 1 -- for easier reading\n local tolerancePercentToSkipOtherPriorities = 1 -- if we are within this tolerance (in%), we don't go to the next priorities\n\n --fake braking value\n local canBrake = false\n\n -- keeping the start position alignement\n local StrafeSpeedFactor = 50 --export: useg to increase the force of the alignement, decrease the value if the alignement is too strong or increase it if it's too slow\n local positionDifference = constructTargetPosition - constructWorldPosition\n local lateralOffset = positionDifference:project_on(constructRight):len() * utils.sign(positionDifference:dot(constructRight))\n ElevatorData.lateralSpeed = constructVelocity:project_on(worldRight):len()\n local lateralDistance = math.abs(lateralOffset)\n local longitudinalOffset = positionDifference:project_on(constructForward):len() * utils.sign(positionDifference:dot(constructForward))\n ElevatorData.longitudinalSpeed = constructVelocity:project_on(worldForward):len()\n local longitudinalDistance = math.abs(longitudinalOffset)\n if (lateralDistance < .25) and (longitudinalDistance < .25) then canBrake = true end\n if ((ElevatorData.lateralSpeed*3.6) > lateralDistance) or ((ElevatorData.longitudinalSpeed*3.6) > longitudinalDistance) then canBrake = true end\n lateralPID:inject(lateralOffset)\n Nav.axisCommandManager:setThrottleCommand(axisCommandId.lateral, lateralPID:get() * StrafeSpeedFactor)\n longitudinalPID:inject(longitudinalOffset)\n Nav.axisCommandManager:setThrottleCommand(axisCommandId.longitudinal, longitudinalPID:get() * StrafeSpeedFactor)\n\n -- Rotation\n local currentRollDeg = getRoll(worldVertical, constructForward, constructRight)\n local currentPitchDeg = -math.asin(constructForward:dot(worldVertical)) * constants.rad2deg\n local targetRollDeg = 0\n local targetPitchDeg = 0\n local targetYawDeg = signedAngleBetween(BaseForward,constructForward,constructUp)*180/math.pi\n rollPID:inject(targetRollDeg - currentRollDeg)\n pitchPID:inject(targetPitchDeg - currentPitchDeg)\n yawPID:inject(-targetYawDeg*yawSpeedFactor)\n\n local constructTargetAngularVelocity = rollPID:get() * constructForward + pitchPID:get() * constructRight + yawPID:get() * constructUp\n\n Nav:setEngineTorqueCommand('torque', constructTargetAngularVelocity, keepCollinearity, 'airfoil', '', '', tolerancePercentToSkipOtherPriorities)\n\n -- moving up or down from TargetAltitude\n ElevatorData.verticalSpeed = constructVelocity:project_on(worldVertical):len()\n ElevatorData.verticalSpeedSigned = ElevatorData.verticalSpeed * -utils.sign(constructVelocity:dot(worldVertical))\n local brakeDistance = 0\n local maxBrake = construct.getMaxBrake()\n if maxBrake ~= nil then\n brakeDistance, _ = computeDistanceAndTime(ElevatorData.verticalSpeed, 0, construct.getInertialMass(), 0, 0, maxBrake - (core.getGravityIntensity() * construct.getInertialMass()) * utils.sign(ElevatorData.verticalSpeedSigned))\n end\n ElevatorData.altitude = core.getAltitude()\n local distance = TargetAltitude - ElevatorData.altitude\n local targetDistance = utils.sign(distance) * (math.abs(distance)-brakeDistance)\n distancePID:inject(targetDistance)\n if distancePID:get() > 0.5 or ElevatorData.verticalSpeedSigned < -MaxSpeed then --using a 0.5 meter deadband for stabilizing\n Nav.axisCommandManager:setThrottleCommand(axisCommandId.vertical, 1)\n ElevatorData.direction = 'up'\n if ElevatorData.verticalSpeedSigned < 0 then\n finalBrakeInput = 1\n end\n elseif distancePID:get() < -0.5 or ElevatorData.verticalSpeedSigned > MaxSpeed then\n Nav.axisCommandManager:setThrottleCommand(axisCommandId.vertical, -1)\n ElevatorData.direction = 'down'\n if ElevatorData.verticalSpeedSigned > 0 then\n finalBrakeInput = 1\n end\n else\n Nav.axisCommandManager:resetCommand(axisCommandId.vertical)\n ElevatorData.direction = 'stabilizing'\n end\n\n --Brakes\n if\n (math.abs(distance) < (ElevatorData.verticalSpeed*3.6) and canBrake)\n or (ElevatorData.verticalSpeed >= MaxSpeed)\n or ((brakeDistance > math.abs(distance)) and canBrake)\n or (finalBrakeInput == 0 and autoBrakeSpeed > 0 and Nav.axisCommandManager.throttle == 0 and constructVelocity:len() < autoBrakeSpeed)\n then\n finalBrakeInput = 1\n end\n ElevatorData.isBreaking = (finalBrakeInput == 1)\n local brakeAcceleration = -finalBrakeInput * (brakeSpeedFactor * constructVelocity + brakeFlatFactor * constructVelocityDir)\n\n Nav:setEngineForceCommand('brake', brakeAcceleration)\n\n -- AutoNavigation regroups all the axis command by 'TargetSpeed'\n local autoNavigationEngineTags = ''\n local autoNavigationAcceleration = vec3()\n local autoNavigationUseBrake = false\n\n -- Longitudinal Translation\n local longitudinalEngineTags = 'thrust analog longitudinal'\n local longitudinalCommandType = Nav.axisCommandManager:getAxisCommandType(axisCommandId.longitudinal)\n if (longitudinalCommandType == axisCommandType.byThrottle) then\n local longitudinalAcceleration = Nav.axisCommandManager:composeAxisAccelerationFromThrottle(longitudinalEngineTags,axisCommandId.longitudinal)\n Nav:setEngineForceCommand(longitudinalEngineTags, longitudinalAcceleration, keepCollinearity)\n elseif (longitudinalCommandType == axisCommandType.byTargetSpeed) then\n local longitudinalAcceleration = Nav.axisCommandManager:composeAxisAccelerationFromTargetSpeed(axisCommandId.longitudinal)\n autoNavigationEngineTags = autoNavigationEngineTags .. ' , ' .. longitudinalEngineTags\n autoNavigationAcceleration = autoNavigationAcceleration + longitudinalAcceleration\n if (Nav.axisCommandManager:getTargetSpeed(axisCommandId.longitudinal) == 0 or -- we want to stop\n Nav.axisCommandManager:getCurrentToTargetDeltaSpeed(axisCommandId.longitudinal) < - Nav.axisCommandManager:getTargetSpeedCurrentStep(axisCommandId.longitudinal) * 0.5) -- if the longitudinal velocity would need some braking\n then\n autoNavigationUseBrake = true\n end\n end\n\n -- Lateral Translation\n local lateralStrafeEngineTags = 'thrust analog lateral'\n local lateralCommandType = Nav.axisCommandManager:getAxisCommandType(axisCommandId.lateral)\n if (lateralCommandType == axisCommandType.byThrottle) then\n local lateralStrafeAcceleration = Nav.axisCommandManager:composeAxisAccelerationFromThrottle(lateralStrafeEngineTags,axisCommandId.lateral)\n Nav:setEngineForceCommand(lateralStrafeEngineTags, lateralStrafeAcceleration, keepCollinearity)\n elseif (lateralCommandType == axisCommandType.byTargetSpeed) then\n local lateralAcceleration = Nav.axisCommandManager:composeAxisAccelerationFromTargetSpeed(axisCommandId.lateral)\n autoNavigationEngineTags = autoNavigationEngineTags .. ' , ' .. lateralStrafeEngineTags\n autoNavigationAcceleration = autoNavigationAcceleration + lateralAcceleration\n end\n\n -- Vertical Translation\n local verticalStrafeEngineTags = 'thrust analog vertical'\n local verticalCommandType = Nav.axisCommandManager:getAxisCommandType(axisCommandId.vertical)\n if (verticalCommandType == axisCommandType.byThrottle) then\n local verticalStrafeAcceleration = Nav.axisCommandManager:composeAxisAccelerationFromThrottle(verticalStrafeEngineTags,axisCommandId.vertical)\n Nav:setEngineForceCommand(verticalStrafeEngineTags, verticalStrafeAcceleration, keepCollinearity, 'airfoil', 'ground', '', tolerancePercentToSkipOtherPriorities)\n elseif (verticalCommandType == axisCommandType.byTargetSpeed) then\n local verticalAcceleration = Nav.axisCommandManager:composeAxisAccelerationFromTargetSpeed(axisCommandId.vertical)\n autoNavigationEngineTags = autoNavigationEngineTags .. ' , ' .. verticalStrafeEngineTags\n autoNavigationAcceleration = autoNavigationAcceleration + verticalAcceleration\n end\n\n -- Auto Navigation (Cruise Control)\n if (autoNavigationAcceleration:len() > constants.epsilon) then\n if (brakeInput ~= 0 or autoNavigationUseBrake or math.abs(constructVelocityDir:dot(constructForward)) < 0.95) -- if the velocity is not properly aligned with the forward\n then\n autoNavigationEngineTags = autoNavigationEngineTags .. ', brake'\n end\n Nav:setEngineForceCommand(autoNavigationEngineTags, autoNavigationAcceleration, dontKeepCollinearity, '', '', '', tolerancePercentToSkipOtherPriorities)\n end\n\n -- Rockets\n Nav:setBoosterCommand('rocket_engine')\n end\n}\n\nlocal systemOnUpdate = {\n function ()\n Nav:update()\n end,\n function ()\n --That function is used for rendering the HUD, you can update it or replace all its content to customise it. You can also remove it if you don't want a HUD\n local direction = 'Stabilizing'\n if ElevatorData.verticalSpeedSigned > 0 then\n direction = 'Up'\n elseif ElevatorData.verticalSpeedSigned < 0 then\n direction = 'Down'\n end\n\n local brakeStatus = 'Off'\n if ElevatorData.isBreaking then\n brakeStatus = 'On'\n end\n local html = [[\n <style>\n * {\n font-size:1vh !important;\n }\n .hud {\n position: absolute;\n left: 5vh;\n top: 5vh;\n right: 5vh;\n display: flex;\n flex-direction: column;\n justify-content: left;\n align-items: left;\n }\n .widget_container {\n border: 2px solid orange;\n border-radius:.5vh;\n background-color: rgba(0, 0, 0, .5);\n display: flex;\n flex-direction: column;\n padding:.5vh;\n margin-top:1vh;\n }\n .widget_container div {\n display: flex;\n flex-direction: row;\n justify-content: space-between;\n }\n .widget_container div div {\n margin:.25vh;\n }\n .widget_container div div:first-child {\n text-transform: uppercase;\n font-weight: bold;\n }\n .selected {\n color: teal;\n }\n .movingto {\n color: green;\n }\n .atmo .gauge {\n background-color: #22d3ee;\n }\n .atmo .gauge_label {\n color: #155e75;\n }\n .space .gauge {\n background-color: #fbbf24;\n }\n .space .gauge_label {\n color: #92400e;\n }\n .rocket .gauge {\n background-color: #a78bfa;\n }\n .rocket .gauge_label {\n color: #5b21b6;\n }\n .gauge_container {\n display:block;\n width:100%;\n min-width:10vw;\n position: relative;\n border: 1px solid black;\n background-color: rgba(0, 0, 0, .75);\n height: 1.5vh;\n }\n .gauge {\n position: absolute;\n z-index:1;\n top:-.25vh;\n left:-.25vh;\n height:100%;\n margin:0;\n background-color: cyan;\n }\n .gauge_label {\n position:relative;\n display:block;\n z-index:10;\n margin:0;\n padding:0;\n width:100%;\n text-align:center;\n }\n </style>\n <div class=\"hud\">\n <div class=\"widget_container\">\n <div>\n <div>Base Altitude</div><div>]] .. format_number(utils.round(BaseAltitute)) .. [[m</div>\n </div>\n <div>\n <div>Target Altitude</div><div>]] .. format_number(utils.round(TargetAltitude)) .. [[m</div>\n </div>\n <div>\n <div>Current Altitude</div><div>]] .. format_number(utils.round(ElevatorData.altitude)) .. [[m</div>\n </div>\n <div>\n <div>Vertical Speed</div><div>]] .. format_number(math.abs(utils.round(ElevatorData.verticalSpeed*3.6))) .. [[km/h</div>\n </div>\n <div>\n <div>Lateral Speed</div><div>]] .. format_number(math.abs(utils.round(ElevatorData.lateralSpeed*3.6))) .. [[km/h</div>\n </div>\n <div>\n <div>Longitudinal Speed</div><div>]] .. format_number(math.abs(utils.round(ElevatorData.longitudinalSpeed*3.6))) .. [[km/h</div>\n </div>\n <div>\n <div>Direction</div><div>]] .. ElevatorData.direction .. [[</div>\n </div>\n <div>\n <div>Brake</div><div>]] .. brakeStatus .. [[</div>\n </div>\n </div>\n ]]\n if #fuelTanks > 0 then\n html = html .. '<div class=\"widget_container\">'\n for _, tank in pairs(fuelTanks) do\n html = html .. '<div><div>' .. tank:getName() .. '</div><div>' .. tank:getTimeLeftString() .. '</div></div><div class=\"gauge_container ' .. tank:getFuelType() .. '\"><div class=\"gauge\" style=\"width:' .. utils.round(tank:getPercentFilled()) .. '%;\"></div><div class=\"gauge_label\"><div style=\"margin-left:auto;margin-right:auto;padding:0;margin-top:-.25vh;\">' .. format_number(utils.round(tank:getPercentFilled())) .. '%</div></div></div>'\n end\n html = html .. '</div>'\n end\n if #Bookmarks > 0 then\n html = html .. '<div class=\"widget_container\"><div><div>Bookmarks</div></div>'\n for index, bookmark in ipairs(Bookmarks) do\n local class=''\n if selectedBookmarkIndex == index then\n class = 'selected'\n end\n local displayName = bookmark.name\n if selectedBookmarkIndex > 0 and bookmark.altitude == TargetAltitude then\n displayName = ' >> ' .. bookmark.name\n class = 'movingto'\n end\n html = html .. '<div class=\"' .. class .. '\"><div>' .. displayName .. '</div><div>' .. format_number(bookmark.altitude) .. 'm</div></div>'\n end\n html = html .. '</div>'\n end\n\n html = html .. '</div>'\n system.setScreen(html)\n end\n}\n\nScript.system:onUpdate(systemOnUpdate)\nScript.system:onFlush(systemOnFlush)\n\n--[[\n Actions\n]]\nlocal systemActionsStart = {}\n\nsystemActionsStart[Script.system.ACTIONS.DOWN] = function ()\n if selectedBookmarkIndex < #Bookmarks then\n selectedBookmarkIndex = selectedBookmarkIndex + 1\n else\n selectedBookmarkIndex = 1\n end\n if __DEBUG then system.print('Selected Bookmark Index: ' .. selectedBookmarkIndex) end\nend\nsystemActionsStart[Script.system.ACTIONS.UP] = function ()\n if selectedBookmarkIndex > 1 then\n selectedBookmarkIndex = selectedBookmarkIndex - 1\n else\n selectedBookmarkIndex = #Bookmarks\n end\n if __DEBUG then system.print('Selected Bookmark Index: ' .. selectedBookmarkIndex) end\nend\nsystemActionsStart[Script.system.ACTIONS.STRAFE_RIGHT] = function ()\n brakeInput = 0\n if selectedBookmarkIndex > 0 then\n TargetAltitude = Bookmarks[selectedBookmarkIndex].altitude\n if __DEBUG then system.print('Target Altitude: ' .. TargetAltitude) end\n else\n system.print('No bookmark selected')\n end\nend\nScript.system:onActionStart(systemActionsStart) --loading all \"actionStart\" functions\n\n--[[\n Chat Commands\n]]\n\nScript.system:onInputText(function (text)\n if text:lower():find('goto:') then\n TargetAltitude = tonumber(text:split(':')[2]) or BaseAltitute\n brakeInput = 0\n system.print('Target Altitude set to ' .. TargetAltitude .. 'm')\n else\n system.print('Unknown command')\n end\nend)\n\n","filter":{"args":[],"signature":"onStart()","slotKey":"-1"},"key":"2"}],"methods":[],"slots":{"0":{"name":"slot1","type":{"events":[],"methods":[]}},"1":{"name":"slot2","type":{"events":[],"methods":[]}},"2":{"name":"slot3","type":{"events":[],"methods":[]}},"3":{"name":"slot4","type":{"events":[],"methods":[]}},"4":{"name":"slot5","type":{"events":[],"methods":[]}},"5":{"name":"slot6","type":{"events":[],"methods":[]}},"6":{"name":"slot7","type":{"events":[],"methods":[]}},"7":{"name":"slot8","type":{"events":[],"methods":[]}},"8":{"name":"slot9","type":{"events":[],"methods":[]}},"9":{"name":"slot10","type":{"events":[],"methods":[]}},"-5":{"name":"library","type":{"events":[],"methods":[]}},"-4":{"name":"system","type":{"events":[],"methods":[]}},"-3":{"name":"player","type":{"events":[],"methods":[]}},"-2":{"name":"construct","type":{"events":[],"methods":[]}},"-1":{"name":"unit","type":{"events":[],"methods":[]}}}}