From 0f5b38ae9b5103da7125c5d2252ef845c73c2f61 Mon Sep 17 00:00:00 2001 From: archaegeo Date: Thu, 7 Jan 2021 12:28:02 -0500 Subject: [PATCH] Refactor of functions --- ButtonHUD.conf | 276 +- ChangeLog.md | 5 + src/ButtonHUD.lua | 7241 +++++++++++++++++++++++---------------------- 3 files changed, 3776 insertions(+), 3746 deletions(-) diff --git a/ButtonHUD.conf b/ButtonHUD.conf index ecff2e9..af45341 100644 --- a/ButtonHUD.conf +++ b/ButtonHUD.conf @@ -1,4 +1,4 @@ -name: ButtonsHud - Dimencia and Archaegeo v4.912 (Minified) +name: ButtonsHud - Dimencia and Archaegeo v4.914 (Minified) slots: core: class: CoreUnit @@ -109,8 +109,8 @@ handlers: brightHud = false --export: Enable to prevent hud dimming when in freelook. VanillaRockets = false --export: If on, rockets behave like vanilla userControlScheme = "virtual joystick" --export: Set to "virtual joystick", "mouse", or "keyboard" - ResolutionX = 1920 --export: Default is 1920, automatically scales, variable for use for wierd resolutions (1920x1200, etc) - ResolutionY = 1080 --export: Default is 1080, automatically scales, variable for use for wierd resolutions (1920x1200, etc) + ResolutionX = 1920 --export: Default is 1920, does not need to be set to same as game resolution. You can set 1920 on a 2560 to get larger resolution + ResolutionY = 1080 --export: Default is 1080, does not need to be set to same as game resolution. You can set 1080 on a 1440 to get larger resolution PrimaryR = 130 --export: Primary HUD color PrimaryG = 224 --export: Primary HUD color PrimaryB = 255 --export: Primary HUD color @@ -166,149 +166,149 @@ handlers: ExternalAGG = false --export: Toggle On if using an external AGG system. If on will prevent this HUD from doing anything with AGG. UseSatNav = false --export: Toggle on if using Trog SatNav script. This will provide SatNav support. apTickRate = 0.0166667 --export: Set the Tick Rate for your HUD. 0.016667 is effectively 60 fps and the default value. 0.03333333 is 30 fps. The bigger the number the less often the autopilot and hud updates but may help peformance on slower machings. - Nav=Navigator.new(system,core,unit)script={}BrakeToggleStatus=BrakeToggleDefault;BrakeIsOn=false;RetrogradeIsOn=false;ProgradeIsOn=false;Autopilot=false;TurnBurn=false;AltitudeHold=false;BrakeLanding=false;AutoTakeoff=false;DisplayOrbit=true;Reentry=false;HoldAltitude=1000;AutopilotAccelerating=false;AutopilotRealigned=false;AutopilotBraking=false;AutopilotCruising=false;AutopilotEndSpeed=0;AutopilotStatus="Aligning"AutopilotPlanetGravity=0;PrevViewLock=1;AutopilotTargetName="None"AutopilotTargetCoords=nil;AutopilotTargetIndex=0;GearExtended=nil;TargetGroundAltitude=LandingGearGroundHeight;TotalDistanceTravelled=0.0;TotalFlightTime=0;SavedLocations={}VectorToTarget=false;LocationIndex=0;LastMaxBrake=0;LockPitch=nil;LastMaxBrakeInAtmo=0;AntigravTargetAltitude=core.getAltitude()LastStartTime=0;local a={"userControlScheme","AutopilotTargetOrbit","apTickRate","freeLookToggle","turnAssist","PrimaryR","PrimaryG","PrimaryB","warmup","DeadZone","circleRad","MouseXSensitivity","MouseYSensitivity","MaxGameVelocity","showHud","autoRollPreference","pitchSpeedFactor","yawSpeedFactor","rollSpeedFactor","brakeSpeedFactor","brakeFlatFactor","autoRollFactor","turnAssistFactor","torqueFactor","AutoTakeoffAltitude","TargetHoverHeight","AutopilotInterplanetaryThrottle","hideHudOnToggleWidgets","DampingMultiplier","fuelTankHandlingAtmo","fuelTankHandlingSpace","fuelTankHandlingRocket","RemoteFreeze","speedChangeLarge","speedChangeSmall","brightHud","brakeLandingRate","MaxPitch","ReentrySpeed","AtmoSpeedLimit","ReentryAltitude","centerX","centerY","vSpdMeterX","vSpdMeterY","altMeterX","altMeterY","fuelX","fuelY","LandingGearGroundHeight","TrajectoryAlignmentStrength","RemoteHud","StallAngle","ResolutionX","ResolutionY","UseSatNav","FuelTankOptimization","ContainerOptimization"}local b={"BrakeToggleStatus","BrakeIsOn","RetrogradeIsOn","ProgradeIsOn","Autopilot","TurnBurn","AltitudeHold","DisplayOrbit","BrakeLanding","Reentry","AutoTakeoff","HoldAltitude","AutopilotAccelerating","AutopilotBraking","AutopilotCruising","AutopilotRealigned","AutopilotEndSpeed","AutopilotStatus","AutopilotPlanetGravity","PrevViewLock","AutopilotTargetName","AutopilotTargetCoords","AutopilotTargetIndex","GearExtended","TargetGroundAltitude","TotalDistanceTravelled","TotalFlightTime","SavedLocations","VectorToTarget","LocationIndex","LastMaxBrake","LockPitch","LastMaxBrakeInAtmo","AntigravTargetAltitude","LastStartTime"}local c=system.print;local d=math.floor;local e=string.format;local f=json.decode;local g=json.encode;local h=core.getElementMaxHitPointsById;local j=unit.getAtmosphereDensity;local k=core.getElementHitPointsById;local l=core.getElementTypeById;local m=core.getElementMassById;local n=core.getConstructMass;local o=Nav.control.isRemoteControlled;function round(p,q)local r=10^(q or 0)return d(p*r+0.5)/r end;local s=round(ResolutionX/2,0)local t=round(ResolutionY/2,0)local u=false;local v=true;local w=55;local x=false;local y=1;local z=1;local A=false;local B=0;local C=0;local D=0;local E=0;local F=0;local G=0;local H=0;local I=false;local J=false;local K="empty"local L=1;local M=5;local N=5;local O=false;local P,Q=0;local R,S=0;local T=false;local U=false;local V=nil;local W=0;local X=0;local Y=false;local Z=0;local a0=0;local a1=0;local a2=3;local a3=0;local a4=""local a5=""local a6=0;local a7=false;local a8=false;local a9=false;local aa=-1;local ab=false;local ac=""local ad=j()>0;local ae=core.getAltitude()local af=core.getElementIdList()local ag=system.getTime()local ah=nil;local ai=false;local aj={}local ak=0;local al=0;local am=""local an=true;local ao={}local ap=1;local aq=0.001;local ar=ResolutionX;local as=ResolutionY;local at=nil;local au=nil;local av=nil;local aw=nil;local ax=false;local ay=false;local az=0;local aA=nil;local aB={}local aC={}local aD={}local aE=0;local aF=false;local aG={}local aH={}local aI=d(1/apTickRate)*2;local aJ={}local aK={}local aL={}local aM={}local aN=false;local aO=16;local aP=0;function script.onStart()VERSION_NUMBER=4.912;SetupComplete=false;beginSetup=coroutine.create(function()Nav.axisCommandManager:setupCustomTargetSpeedRanges(axisCommandId.longitudinal,{1000,5000,10000,20000,30000})if dbHud_1 then local aQ=dbHud_1.hasKey;if not useTheseSettings then for aR,aS in pairs(a)do if aQ(aS)then local aT=f(dbHud_1.getStringValue(aS))if aT~=nil then c(aS.." "..dbHud_1.getStringValue(aS))_G[aS]=aT;ax=true end end end end;coroutine.yield()for aR,aS in pairs(b)do if aQ(aS)then local aT=f(dbHud_1.getStringValue(aS))if aT~=nil then c(aS.." "..dbHud_1.getStringValue(aS))_G[aS]=aT;ax=true end end end;if useTheseSettings then K="Updated user preferences used. Will be saved when you exit seat.\nToggle off useTheseSettings to use saved values"a2=5 elseif ax then K="Loaded Saved Variables (see Lua Chat Tab for list)"else K="No Saved Variables Found - Stand up / leave remote to save settings"end else K="No databank found, install one anywhere and rerun the autoconfigure to save variables"end;coroutine.yield()local aU=system.getTime()if LastStartTime+18010000 then aO=128 elseif aX>1000 then aO=64 elseif aX>150 then aO=32 end end;aE=aE+h(af[aR])if fuelX~=0 and fuelY~=0 then if type=="Atmospheric Fuel Tank"or type=="Space Fuel Tank"or type=="Rocket Fuel Tank"then local aX=h(af[aR])local aY=m(af[aR])local aZ=0;local a_=system.getTime()if type=="Atmospheric Fuel Tank"then local b0=400;local b1=35.03;if aX>10000 then b0=51200;b1=5480 elseif aX>1300 then b0=6400;b1=988.67 elseif aX>150 then b0=1600;b1=182.67 end;aZ=aY-b1;if fuelTankHandlingAtmo>0 then b0=b0+b0*fuelTankHandlingAtmo*0.2 end;if aZ>b0 then b0=aZ end;if ContainerOptimization>0 then b0=b0-b0*ContainerOptimization*0.05 end;if FuelTankOptimization>0 then b0=b0-b0*FuelTankOptimization*0.05 end;aB[#aB+1]={af[aR],core.getElementNameById(af[aR]),b0,b1,aZ,a_}end;if type=="Rocket Fuel Tank"then local b0=320;local b1=173.42;if aX>65000 then b0=40000;b1=25740 elseif aX>6000 then b0=5120;b1=4720 elseif aX>700 then b0=640;b1=886.72 end;aZ=aY-b1;if fuelTankHandlingRocket>0 then b0=b0+b0*fuelTankHandlingRocket*0.2 end;if aZ>b0 then b0=aZ end;if ContainerOptimization>0 then b0=b0-b0*ContainerOptimization*0.05 end;if FuelTankOptimization>0 then b0=b0-b0*FuelTankOptimization*0.05 end;aD[#aD+1]={af[aR],core.getElementNameById(af[aR]),b0,b1,aZ,a_}end;if type=="Space Fuel Tank"then local b0=2400;local b1=182.67;if aX>10000 then b0=76800;b1=5480 elseif aX>1300 then b0=9600;b1=988.67 end;aZ=aY-b1;if fuelTankHandlingSpace>0 then b0=b0+b0*fuelTankHandlingSpace*0.2 end;if aZ>b0 then b0=aZ end;if ContainerOptimization>0 then b0=b0-b0*ContainerOptimization*0.05 end;if FuelTankOptimization>0 then b0=b0-b0*FuelTankOptimization*0.05 end;aC[#aC+1]={af[aR],core.getElementNameById(af[aR]),b0,b1,aZ,a_}end end end end;coroutine.yield()if gyro~=nil then ah=gyro.getState()==1 end;if userControlScheme~="keyboard"then system.lockView(1)else system.lockView(0)end;if ad then BrakeIsOn=true end;if radar_1 then if l(radar_1.getId())=="Space Radar"then T=true else U=true end end;if door then for _,aS in pairs(door)do aS.toggle()end end;if switch then for _,aS in pairs(switch)do aS.toggle()end end;if forcefield then for _,aS in pairs(forcefield)do aS.toggle()end end;if antigrav~=nil and not ExternalAGG then if antigrav.getState()==1 then antigrav.show()end end;if o()==1 and RemoteFreeze then system.freeze(1)else system.freeze(0)end;if A then GearExtended=Nav.control.isAnyLandingGearExtended()==1;if GearExtended then Nav.control.extendLandingGears()else Nav.control.retractLandingGears()end end;if TargetGroundAltitude~=nil then Nav.axisCommandManager:setTargetGroundAltitude(TargetGroundAltitude)if TargetGroundAltitude==0 and not A then GearExtended=true end else if GearExtended or not A then Nav.axisCommandManager:setTargetGroundAltitude(LandingGearGroundHeight)GearExtended=true else Nav.axisCommandManager:setTargetGroundAltitude(TargetHoverHeight)end end;if ad and not dbHud_1 and(GearExtended or not A)then BrakeIsOn=true end;WasInAtmo=ad;unit.hide()function ConvertResolutionX(aS)if ResolutionX==1920 then return aS else return round(ResolutionX*aS/1920,0)end end;function ConvertResolutionY(aS)if ResolutionY==1080 then return aS else return round(ResolutionY*aS/1080,0)end end;function RefreshLastMaxBrake(b2,b3)if b2==nil then b2=core.g()end;b2=round(b2,5)local b4=j()if b3~=nil and b3 or(aA==nil or aA~=b2)then local velocity=core.getVelocity()local b5=vec3(velocity):len()local b6=f(unit.getData()).maxBrake;if b6~=nil and b6>0 and ad then b6=b6/utils.clamp(b5/100,0.1,1)b6=b6/b4;if b6>LastMaxBrakeInAtmo and b4>0.10 then LastMaxBrakeInAtmo=b6 end end;if b6~=nil and b6>0 then LastMaxBrake=b6 end;aA=b2 end end;function MakeButton(b7,b8,b9,ba,bb,bc,bd,be,bf)local bg={enableName=b7,disableName=b8,width=b9,height=ba,x=bb,y=bc,toggleVar=bd,toggleFunction=be,drawCondition=bf,hovered=false}table.insert(ao,bg)return bg end;function UpdateAtlasLocationsList()AtlasOrdered={}for aR,aS in pairs(atlas[0])do table.insert(AtlasOrdered,{name=aS.name,index=aR})end;local function bh(bi,bj)return bi.name-1 then atlas[0][bn]=bm end;UpdateAtlasLocationsList()K=CustomTarget.name.." position updated"end end;function ClearCurrentPosition()local bn=-1;for aR,aS in pairs(atlas[0])do if aS.name and aS.name==CustomTarget.name then bn=aR end end;if bn>-1 then table.remove(atlas[0],bn)end;bn=-1;for aR,aS in pairs(SavedLocations)do if aS.name and aS.name==CustomTarget.name then K=aS.name.." saved location cleared"bn=aR;break end end;if bn~=-1 then table.remove(SavedLocations,bn)end;DecrementAutopilotTargetIndex()UpdateAtlasLocationsList()end;function DrawDeadZone(bo)bo[#bo+1]=e([[]],DeadZone)end;function ToggleRadarPanel()if radarPanelID~=nil and a6==0 then system.destroyWidgetPanel(radarPanelID)radarPanelID=nil;if perisPanelID~=nil then system.destroyWidgetPanel(perisPanelID)perisPanelID=nil end else if a6==1 then system.destroyWidgetPanel(radarPanelID)radarPanelID=nil;_autoconf.displayCategoryPanel(radar,radar_size,L_TEXT("ui_lua_widget_periscope", "Periscope"),"periscope")perisPanelID=_autoconf.panels[_autoconf.panels_size]end;placeRadar=true;if radarPanelID==nil and placeRadar then _autoconf.displayCategoryPanel(radar,radar_size,L_TEXT("ui_lua_widget_radar", "Radar"),"radar")radarPanelID=_autoconf.panels[_autoconf.panels_size]placeRadar=false end;a6=0 end end;function ToggleWidgets()if an then unit.show()core.show()if atmofueltank_size>0 then _autoconf.displayCategoryPanel(atmofueltank,atmofueltank_size,L_TEXT("ui_lua_widget_atmofuel", "Atmo Fuel"),"fuel_container")fuelPanelID=_autoconf.panels[_autoconf.panels_size]end;if spacefueltank_size>0 then _autoconf.displayCategoryPanel(spacefueltank,spacefueltank_size,L_TEXT("ui_lua_widget_spacefuel", "Space Fuel"),"fuel_container")spacefuelPanelID=_autoconf.panels[_autoconf.panels_size]end;if rocketfueltank_size>0 then _autoconf.displayCategoryPanel(rocketfueltank,rocketfueltank_size,L_TEXT("ui_lua_widget_rocketfuel", "Rocket Fuel"),"fuel_container")rocketfuelPanelID=_autoconf.panels[_autoconf.panels_size]end;an=false else unit.hide()core.hide()if fuelPanelID~=nil then system.destroyWidgetPanel(fuelPanelID)fuelPanelID=nil end;if spacefuelPanelID~=nil then system.destroyWidgetPanel(spacefuelPanelID)spacefuelPanelID=nil end;if rocketfuelPanelID~=nil then system.destroyWidgetPanel(rocketfuelPanelID)rocketfuelPanelID=nil end;an=true end end;function SetupInterplanetaryPanel()panelInterplanetary=system.createWidgetPanel("Interplanetary Helper")interplanetaryHeader=system.createWidget(panelInterplanetary,"value")interplanetaryHeaderText=system.createData('{"label": "Target Planet", "value": "N/A", "unit":""}')system.addDataToWidget(interplanetaryHeaderText,interplanetaryHeader)widgetDistance=system.createWidget(panelInterplanetary,"value")widgetDistanceText=system.createData('{"label": "distance", "value": "N/A", "unit":""}')system.addDataToWidget(widgetDistanceText,widgetDistance)widgetTravelTime=system.createWidget(panelInterplanetary,"value")widgetTravelTimeText=system.createData('{"label": "Travel Time", "value": "N/A", "unit":""}')system.addDataToWidget(widgetTravelTimeText,widgetTravelTime)widgetMaxMass=system.createWidget(panelInterplanetary,"value")widgetMaxMassText=system.createData('{"label": "Maximum Mass", "value": "N/A", "unit":""}')system.addDataToWidget(widgetMaxMassText,widgetMaxMass)widgetCurBrakeDistance=system.createWidget(panelInterplanetary,"value")widgetCurBrakeDistanceText=system.createData('{"label": "Cur Brake distance", "value": "N/A", "unit":""}')if not ad then system.addDataToWidget(widgetCurBrakeDistanceText,widgetCurBrakeDistance)end;widgetCurBrakeTime=system.createWidget(panelInterplanetary,"value")widgetCurBrakeTimeText=system.createData('{"label": "Cur Brake Time", "value": "N/A", "unit":""}')if not ad then system.addDataToWidget(widgetCurBrakeTimeText,widgetCurBrakeTime)end;widgetMaxBrakeDistance=system.createWidget(panelInterplanetary,"value")widgetMaxBrakeDistanceText=system.createData('{"label": "Max Brake distance", "value": "N/A", "unit":""}')if not ad then system.addDataToWidget(widgetMaxBrakeDistanceText,widgetMaxBrakeDistance)end;widgetMaxBrakeTime=system.createWidget(panelInterplanetary,"value")widgetMaxBrakeTimeText=system.createData('{"label": "Max Brake Time", "value": "N/A", "unit":""}')if not ad then system.addDataToWidget(widgetMaxBrakeTimeText,widgetMaxBrakeTime)end;widgetTrajectoryAltitude=system.createWidget(panelInterplanetary,"value")widgetTrajectoryAltitudeText=system.createData('{"label": "Projected Altitude", "value": "N/A", "unit":""}')if not ad then system.addDataToWidget(widgetTrajectoryAltitudeText,widgetTrajectoryAltitude)end end;function Contains(bp,bq,bb,bc,b9,ba)if bp>bb and bpbc and bqw then K="WARNING: Insufficient Brakes - Attempting coast landing, beware obstacles"end;AltitudeHold=false;AutoTakeoff=false;LockPitch=nil;BrakeLanding=true;Nav.axisCommandManager:setThrottleCommand(axisCommandId.longitudinal,0)end end;function ToggleAutoTakeoff()if AutoTakeoff then AutoTakeoff=false;if AltitudeHold then ToggleAltitudeHold()end else if not AltitudeHold then ToggleAltitudeHold()end;AutoTakeoff=true;HoldAltitude=ae+AutoTakeoffAltitude;GearExtended=false;Nav.control.retractLandingGears()Nav.axisCommandManager:setTargetGroundAltitude(500)BrakeIsOn=true end end;function ToggleLockPitch()if LockPitch==nil then local br=vec3(core.getConstructWorldOrientationForward())local bs=vec3(core.getConstructWorldOrientationRight())local bt=vec3(core.getWorldVertical())local bu=getPitch(bt,br,bs)LockPitch=bu;AutoTakeoff=false;AltitudeHold=false;BrakeLanding=false else LockPitch=nil end end;function ToggleAltitudeHold()AltitudeHold=not AltitudeHold;if AltitudeHold then Autopilot=false;ProgradeIsOn=false;RetrogradeIsOn=false;I=false;BrakeLanding=false;Reentry=false;autoRoll=true;LockPitch=nil;if not GearExtended and not BrakeIsOn or not ad then AutoTakeoff=false;HoldAltitude=ae;if not a8 and Nav.axisCommandManager:getAxisCommandType(0)==0 then Nav.control.cancelCurrentControlMasterMode()end else AutoTakeoff=true;HoldAltitude=ae+AutoTakeoffAltitude;GearExtended=false;Nav.control.retractLandingGears()Nav.axisCommandManager:setTargetGroundAltitude(500)BrakeIsOn=true end;if a8 then HoldAltitude=100000 end else autoRoll=autoRollPreference;AutoTakeoff=false;BrakeLanding=false;Reentry=false;AutoTakeoff=false;VectorToTarget=false end end;function ToggleFollowMode()if o()==1 then I=not I;if I then Autopilot=false;RetrogradeIsOn=false;ProgradeIsOn=false;AltitudeHold=false;Reentry=false;BrakeLanding=false;AutoTakeoff=false;OldGearExtended=GearExtended;GearExtended=false;Nav.control.retractLandingGears()Nav.axisCommandManager:setTargetGroundAltitude(TargetHoverHeight)else BrakeIsOn=true;autoRoll=autoRollPreference;GearExtended=OldGearExtended;if GearExtended then Nav.control.extendLandingGears()Nav.axisCommandManager:setTargetGroundAltitude(LandingGearGroundHeight)end end else K="Follow Mode only works with Remote controller"I=false end end;function ToggleAutopilot()if AutopilotTargetIndex>0 and not Autopilot and not VectorToTarget then if CustomTarget~=nil then LockPitch=nil;if planet.name==CustomTarget.planetname then StrongBrakes=planet.gravity*9.80665*n()w then K="Insufficient Brake Force\nCoast landing will be inaccurate"end;if j()>0 then if not AltitudeHold then if not VectorToTarget then ToggleVectorToTarget()end else if not VectorToTarget then ToggleVectorToTarget()end end else a7=true end else a8=true;RetrogradeIsOn=false;ProgradeIsOn=false;if j()~=0 then ToggleAltitudeHold()else Autopilot=true end end elseif j()==0 then Autopilot=true;RetrogradeIsOn=false;ProgradeIsOn=false;AutopilotRealigned=false;I=false;AltitudeHold=false;BrakeLanding=false;Reentry=false;AutoTakeoff=false;u=false;LockPitch=nil else a8=true;ToggleAltitudeHold()end else Autopilot=false;AutopilotRealigned=false;VectorToTarget=false;u=false;AutoTakeoff=false;AltitudeHold=false;VectorToTarget=false end end;function ProgradeToggle()ProgradeIsOn=not ProgradeIsOn;RetrogradeIsOn=false;Autopilot=false;AltitudeHold=false;I=false;BrakeLanding=false;LockPitch=nil;Reentry=false;AutoTakeoff=false end;function RetrogradeToggle()RetrogradeIsOn=not RetrogradeIsOn;ProgradeIsOn=false;Autopilot=false;AltitudeHold=false;I=false;BrakeLanding=false;LockPitch=nil;Reentry=false;AutoTakeoff=false end;function BrakeToggle()BrakeIsOn=not BrakeIsOn;if BrakeLanding then BrakeLanding=false;autoRoll=autoRollPreference end;if BrakeIsOn then AltitudeHold=false;VectorToTarget=false;AutoTakeoff=false;Reentry=false;ProgradeIsOn=false;BrakeLanding=false;AutoLanding=false;AltitudeHold=false;LockPitch=nil;autoRoll=autoRollPreference end end;function CheckDamage(bo)local bv=0;am=""local bw=aE;local bx=0;local by=0;local bz=0;local bA=0;local bB=""for aR in pairs(af)do local aX=0;local bC=0;bC=h(af[aR])aX=k(af[aR])bx=bx+aX;if aX0 and aj[11]==af[aR]then for bE in pairs(aj)do core.deleteSticker(aj[bE])end;aj={}end end;bv=d(bx/bw*100)if bv<100 then bo[#bo+1]=[[]]bA=d(bv*2.55)bB=e("rgb(%d,%d,%d)",255-bA,bA,0)if bv<100 then bo[#bo+1]=e([[Elemental Integrity: %i %%]],bB,bv)if bz>0 then bo[#bo+1]=e([[Disabled Modules: %i Damaged Modules: %i]],bB,bz,by)elseif by>0 then bo[#bo+1]=e([[Damaged Modules: %i]],bB,by)end end;bo[#bo+1]=[[<\g>]]end end;function DrawCursorLine(bo)local bF=d(utils.clamp(a3/(ar/4)*255,0,255))bo[#bo+1]=e("",a0,a1,d(PrimaryR+0.5)+bF,d(PrimaryG+0.5)-bF,d(PrimaryB+0.5)-bF)end;function getPitch(bG,bH,bj)local bI=bG:cross(bj):normalize_inplace()local bu=math.acos(utils.clamp(bI:dot(-bH),-1,1))*constants.rad2deg;if bI:cross(-bH):dot(bj)<0 then bu=-bu end;return bu end;function clearAll()if ab then ab=false;AutopilotAccelerating=false;AutopilotBraking=false;AutopilotCruising=false;Autopilot=false;AutopilotRealigned=false;AutopilotStatus="Aligning"RetrogradeIsOn=false;ProgradeIsOn=false;AltitudeHold=false;Reentry=false;BrakeLanding=false;BrakeIsOn=false;AutoTakeoff=false;I=false;u=false;a7=false;a8=false;x=false;autoRoll=autoRollPreference;VectorToTarget=false;TurnBurn=false;ah=false;LockPitch=nil else ab=true end end;function wipeSaveVariables()if not dbHud_1 then K="No Databank Found, unable to wipe. \nYou must have a Databank attached to ship prior to running the HUD autoconfigure"a2=5 else if ay then for aR,aS in pairs(a)do dbHud_1.setStringValue(aS,g(nil))end;for aR,aS in pairs(b)do if aS~="SavedLocations"then dbHud_1.setStringValue(aS,g(nil))end end;K="Databank wiped. New variables will save after re-enter seat and exit"a2=5;ay=false;ax=false;Y=true else K="Press ALT-7 again to confirm wipe of ALL data"ay=true end end end;function CheckButtons()for _,aS in pairs(ao)do if aS.hovered then if not aS.drawCondition or aS.drawCondition()then aS.toggleFunction()end;aS.hovered=false end end end;function SetButtonContains()local bb=a0+ar/2;local bc=a1+as/2;for _,aS in pairs(ao)do aS.hovered=Contains(bb,bc,aS.x,aS.y,aS.width,aS.height)end end;function DrawButton(bo,bJ,hover,bb,bc,bK,bL,bM,bN,bO,bP)if type(bO)=="function"then bO=bO()end;if type(bP)=="function"then bP=bP()end;bo[#bo+1]=e(""if bJ then bo[#bo+1]=e("%s",bO)else bo[#bo+1]=e("%s",bP)end end;function DrawButtons(bo)local bQ="rgb(50,50,50)'"local bR="rgb(210,200,200)"local bS=DrawButton;for _,aS in pairs(ao)do local b8=aS.disableName;local b7=aS.enableName;if type(b8)=="function"then b8=b8()end;if type(b7)=="function"then b7=b7()end;if not aS.drawCondition or aS.drawCondition()then bS(bo,aS.toggleVar(),aS.hovered,aS.x,aS.y,aS.width,aS.height,bR,bQ,b8,b7)end end end;function DrawTank(bo,aN,bb,bT,bU,bV,bW,bX)local bY=1;local bZ=2;local b_=3;local c0=4;local c1=5;local c2=6;local c3=""local c4=0;local c5=fuelY;local c6=fuelY+10;if o()==1 and not RemoteHud then c5=c5-50;c6=c6-50 end;bo[#bo+1]=[[]]if bU=="ATMO"then c3="atmofueltank"elseif bU=="SPACE"then c3="spacefueltank"else c3="rocketfueltank"end;c4=_G[c3 .."_size"]if#bV>0 then for i=1,#bV do local bk=string.sub(bV[i][bZ],1,12)local c7=0;for bE=1,c4 do if bV[i][bZ]==f(unit[c3 .."_"..bE].getData()).name then c7=bE;break end end;if aN or bW[i]==nil or bX[i]==nil then local c8=0;local c9=0;local ca=0;local cb=0;local a_=system.getTime()if c7~=0 then bX[i]=f(unit[c3 .."_"..c7].getData()).percentage;bW[i]=f(unit[c3 .."_"..c7].getData()).timeLeft;if bW[i]=="n/a"then bW[i]=0 end else ca=m(bV[i][bY])-bV[i][c0]c8=bV[i][b_]bX[i]=d(0.5+ca*100/c8)c9=bV[i][c1]cb=bV[i][c2]if c9<=ca then bW[i]=0 else bW[i]=d(0.5+ca/((c9-ca)/(a_-cb)))end;bV[i][c1]=ca;bV[i][c2]=a_ end end;if bk==bT then bk=e("%s %d",bU,i)end;if c7==0 then bk=bk.." *"end;local cc;if bW[i]==0 then cc="n/a"else cc=FormatTimeString(bW[i])end;if bX[i]~=nil then local bA=d(bX[i]*2.55)local bB=e("rgb(%d,%d,%d)",255-bA,bA,0)local cd=""if cc~="n/a"and bW[i]<120 or bX[i]<5 then if aN then cd=[[class="red"]]end end;bo[#bo+1]=e([[ - %s - %d%% %s - ]],bb,c5,cd,bk,bb,c6,bB,bX[i],cc)c5=c5+30;c6=c6+30 end end end;bo[#bo+1]=""end;function HideInterplanetaryPanel()system.destroyWidgetPanel(panelInterplanetary)panelInterplanetary=nil end;system.showScreen(1)function getRelativePitch(velocity)velocity=vec3(velocity)local bu=-math.deg(math.atan(velocity.y,velocity.z))+180;bu=bu-90;if bu<0 then bu=360+bu end;if bu>180 then bu=-180+bu-180 end;return-bu end;function getRelativeYaw(velocity)velocity=vec3(velocity)local ce=math.deg(math.atan(velocity.y,velocity.x))-90;if ce<-180 then ce=360+ce end;return ce end;function AlignToWorldVector(cf,cg)if not ad or RateOfChange>MinimumRateOfChange+0.08 or aa~=-1 then if cg==nil then cg=aq end;cf=vec3(cf):normalize()local ch=vec3(core.getConstructWorldOrientationForward())-cf;local ci=-getMagnitudeInDirection(ch,core.getConstructWorldOrientationRight())*ap;local cj=-getMagnitudeInDirection(ch,core.getConstructWorldOrientationUp())*ap;if ak==0 then ak=ci/2 end;if al==0 then al=cj/2 end;D=D-(ci+(ci-ak)*DampingMultiplier)C=C+cj+(cj-al)*DampingMultiplier;ak=ci;al=cj;if math.abs(ci)0 and CustomTarget~=nil end)MakeButton("Clear Position","Clear Position",200,cn.height,cn.x-200-30,cn.y,function()return true end,ClearCurrentPosition,function()return AutopilotTargetIndex>0 and CustomTarget~=nil end)ck=60;cl=300;local bb=10;local bc=as/2-300;MakeButton("Enable Turn and Burn","Disable Turn and Burn",cl,ck,bb,bc,function()return TurnBurn end,ToggleTurnBurn)MakeButton("Engage Altitude Hold","Disable Altitude Hold",cl,ck,bb+cl+20,bc,function()return AltitudeHold end,ToggleAltitudeHold)bc=bc+ck+20;MakeButton("Engage Autoland","Disable Autoland",cl,ck,bb,bc,function()return AutoLanding end,ToggleAutoLanding)MakeButton("Engage Auto Takeoff","Disable Auto Takeoff",cl,ck,bb+cl+20,bc,function()return AutoTakeoff end,ToggleAutoTakeoff)bc=bc+ck+20;MakeButton("Show Orbit Display","Hide Orbit Display",cl,ck,bb,bc,function()return DisplayOrbit end,function()DisplayOrbit=not DisplayOrbit;if DisplayOrbit then K="Orbit Display Enabled"else K="Orbit Display Disabled"end end)bc=bc+ck+20;MakeButton("Glide Re-Entry","Cancel Glide Re-Entry",cl,ck,bb,bc,function()return Reentry end,function()x=true;BeginReentry()end,function()return ae>ReentryAltitude end)MakeButton("Parachute Re-Entry","Cancel Parachute Re-Entry",cl,ck,bb+cl+20,bc,function()return Reentry end,BeginReentry,function()return ae>ReentryAltitude end)bc=bc+ck+20;MakeButton("Engage Follow Mode","Disable Follow Mode",cl,ck,bb,bc,function()return I end,ToggleFollowMode,function()return o()==1 end)MakeButton("Enable Repair Arrows","Disable Repair Arrows",cl,ck,bb+cl+20,bc,function()return aF end,function()aF=not aF;if aF then K="Repair Arrows Enabled"else K="Repair Arrows Diabled"end end,function()return o()==1 end)bc=bc+ck+20;if not ExternalAGG then MakeButton("Enable AGG","Disable AGG",cl,ck,bb,bc,function()return antigrav.getState()==1 end,ToggleAntigrav,function()return antigrav~=nil end)end;bc=bc+ck+20;MakeButton(function()return e("Toggle Control Scheme - Current: %s",userControlScheme)end,function()return e("Control Scheme: %s",userControlScheme)end,cl*2,ck,bb,bc,function()return false end,function()if userControlScheme=="keyboard"then userControlScheme="mouse"elseif userControlScheme=="mouse"then userControlScheme="virtual joystick"else userControlScheme="keyboard"end end)coroutine.yield()function GetFlightStyle()local co=Nav.axisCommandManager:getAxisCommandType(0)local cp="TRAVEL"if co==1 then cp="CRUISE"end;if Autopilot then cp="AUTOPILOT"end;return cp end;function updateHud(bo)local cq=ae;local velocity=core.getVelocity()local b5=vec3(velocity):len()local bt=vec3(core.getWorldVertical())local br=vec3(core.getConstructWorldOrientationForward())local bs=vec3(core.getConstructWorldOrientationRight())local cr=vec3(core.getConstructWorldOrientationUp())local cs=getRoll(bt,br,bs)local ct=cs/180*math.pi;local cu=math.cos(ct)local cv=math.sin(ct)local bu=getPitch(bt,br,bs*cu+cr*cv)local cw=cs;local cx=bu;local cy=j()local cz=d(unit.getThrottle())local cA=b5*3.6;local cB=unit.getAxisCommandValue(0)local cp=GetFlightStyle()local cC="ROLL"local cD=unit.getClosestPlanetInfluence()>0;if cz==nil then cz=0 end;if not cD then if b5>5 then bu=getRelativePitch(velocity)cs=getRelativeYaw(velocity)else bu=0;cs=0 end;cC="YAW"end;bo[#bo+1]=a5;bo[#bo+1]=am;bo[#bo+1]=a4;if aP%aI==0 then aN=true end;if fuelX~=0 and fuelY~=0 then DrawTank(bo,aN,fuelX,"Atmospheric ","ATMO",aB,aL,aM)DrawTank(bo,aN,fuelX+100,"Space fuel t","SPACE",aC,aJ,aK)DrawTank(bo,aN,fuelX+200,"Rocket fuel ","ROCKET",aD,aG,aH)end;if aN then aN=false;aP=0 end;aP=aP+1;DrawVerticalSpeed(bo,cq)if o()==0 or RemoteHud then if not IsInFreeLook()or brightHud then if cD then DrawRollLines(bo,centerX,centerY,cw,cC,cD)DrawArtificialHorizon(bo,cx,cw,centerX,centerY,cD,d(getRelativeYaw(velocity)),b5)else DrawRollLines(bo,centerX,centerY,cs,cC,cD)DrawArtificialHorizon(bo,bu,cs,centerX,centerY,cD,d(cs),b5)end;DrawAltitudeDisplay(bo,cq,cD)DrawPrograde(bo,velocity,b5,centerX,centerY)end end;DrawThrottle(bo,cp,cz,cB)DrawSpeed(bo,cA)DrawWarnings(bo)DisplayOrbitScreen(bo)if screen_2 then local cE=vec3(core.getConstructWorldPos())local bb=960+cE.x/MapXRatio;local bc=450+cE.y/MapYRatio;screen_2.moveContent(YouAreHere,(bb-80)/19.2,(bc-80)/10.8)end end;function IsInFreeLook()return system.isViewLocked()==0 and userControlScheme~="keyboard"and o()==0 end;function HUDPrologue(bo)local cF=aV;local cG=aW;local cH=aV;local cI=aW;if IsInFreeLook()and not brightHud then cF=[[rgb(]]..d(PrimaryR*0.4+0.5)..","..d(PrimaryG*0.4+0.5)..","..d(PrimaryB*0.3+0.5)..[[)]]cG=[[rgb(]]..d(PrimaryR*0.3+0.5)..","..d(PrimaryG*0.3+0.5)..","..d(PrimaryB*0.2+0.5)..[[)]]end;bo[#bo+1]=e([[ - - - - - - ]],cF,cF,cH,cH,cG,cG,cI,cI,ResolutionX,ResolutionY)end;function HUDEpilogue(bo)bo[#bo+1]=""end;function DrawSpeed(bo,cA)local cJ=throtPosY-10;local cK=throtPosX+10;bo[#bo+1]=[[]]if o()==1 and not RemoteHud then cJ=75 end;bo[#bo+1]=e([[ - - %d km/h - - ]],cK,cJ,d(cA))end;function DrawOdometer(bo,W,TotalDistanceTravelled,cp,X,cy)local cL=ConvertResolutionX(1240)local cM=ConvertResolutionY(55)local cN=cM+10;local cy=j()local b2=core.g()local cO=0;local cP=0;local cQ=0;RefreshLastMaxBrake(b2)if ad then cQ=LastMaxBrakeInAtmo else cQ=LastMaxBrake end;maxThrust=Nav:maxForceForward()az=n()if not ShowOdometer then return end;local cR=vec3(core.getWorldAcceleration()):len()/9.80665;if b2>0.1 then cP=az*b2;cO=maxThrust/b2 end;bo[#bo+1]=[[]]if o()==1 and not RemoteHud then cL=ConvertResolutionX(1120)cM=ConvertResolutionY(55)cN=cM+10 elseif ad then local cS=ConvertResolutionX(770)bo[#bo+1]=e([[ - ATMOSPHERE - %.2f - ]],cS,cM,cS,cN,cy)end;bo[#bo+1]=e([[ - - - GRAVITY - %.2f g - ACCEL - %.2f g - ]],cL,cM,cL,cN,b2/9.80665,cL,cM+20,cL,cN+20,cR)bo[#bo+1]=e([[ - - ]],ConvertResolutionX(660),ConvertResolutionX(700),ConvertResolutionY(35),ConvertResolutionX(960),ConvertResolutionY(55),ConvertResolutionX(1240),ConvertResolutionY(35),ConvertResolutionX(1280))if o()==0 or RemoteHud then bo[#bo+1]=e([[ - Trip: %.2f km - Lifetime: %.2f Mm - Trip Time: %s - Total Time: %s - Mass: %.2f Tons - Max Brake: %.2f kN - Max Thrust: %.2f kN - %s]],ConvertResolutionX(700),ConvertResolutionY(20),W,ConvertResolutionX(700),ConvertResolutionY(30),TotalDistanceTravelled/1000,ConvertResolutionX(830),ConvertResolutionY(20),FormatTimeString(X),ConvertResolutionX(830),ConvertResolutionY(30),FormatTimeString(TotalFlightTime),ConvertResolutionX(970),ConvertResolutionY(20),az/1000,ConvertResolutionX(1240),ConvertResolutionY(10),cQ/1000,ConvertResolutionX(1240),ConvertResolutionY(30),maxThrust/1000,ConvertResolutionX(960),ConvertResolutionY(180),cp)if b2>0.1 then bo[#bo+1]=e([[ - Max Mass: %.2f Tons - Req Thrust: %.2f kN - ]],ConvertResolutionX(970),ConvertResolutionY(30),cO/1000,ConvertResolutionX(1240),ConvertResolutionY(20),cP/1000)else bo[#bo+1]=e([[ - Max Mass: n/a - Req Thrust: n/a - ]],ConvertResolutionX(970),ConvertResolutionY(30),ConvertResolutionX(1240),ConvertResolutionY(20))end else bo[#bo+1]=e([[%s]],ConvertResolutionX(960),ConvertResolutionY(33),cp)end;bo[#bo+1]=""end;function DrawThrottle(bo,cp,cz,cB)local c5=throtPosY+10;local c6=throtPosY+20;if o()==1 and not RemoteHud then c5=55;c6=65 end;local cT="CRUISE"local unit="km/h"local cU=cB;if cp=="TRAVEL"or cp=="AUTOPILOT"then cT="THROT"unit="%"cU=cz;local cV="dim"if cz<0 then cV="red"end;bo[#bo+1]=e([[ - - - - ]],cV,throtPosX-7,throtPosY-50,throtPosX,throtPosY-50,throtPosX,throtPosY+50,throtPosX-7,throtPosY+50,1-math.abs(cz),throtPosX-10,throtPosY+50,throtPosX-15,throtPosY+53,throtPosX-15,throtPosY+47)end;bo[#bo+1]=e([[ - - %s - %d %s + Nav=Navigator.new(system,core,unit)script={}BrakeToggleStatus=BrakeToggleDefault;BrakeIsOn=false;RetrogradeIsOn=false;ProgradeIsOn=false;Autopilot=false;TurnBurn=false;AltitudeHold=false;BrakeLanding=false;AutoTakeoff=false;DisplayOrbit=true;Reentry=false;HoldAltitude=1000;AutopilotAccelerating=false;AutopilotRealigned=false;AutopilotBraking=false;AutopilotCruising=false;AutopilotEndSpeed=0;AutopilotStatus="Aligning"AutopilotPlanetGravity=0;PrevViewLock=1;AutopilotTargetName="None"AutopilotTargetCoords=nil;AutopilotTargetIndex=0;GearExtended=nil;TargetGroundAltitude=LandingGearGroundHeight;TotalDistanceTravelled=0.0;TotalFlightTime=0;SavedLocations={}VectorToTarget=false;LocationIndex=0;LastMaxBrake=0;LockPitch=nil;LastMaxBrakeInAtmo=0;AntigravTargetAltitude=core.getAltitude()LastStartTime=0;local a={"userControlScheme","AutopilotTargetOrbit","apTickRate","freeLookToggle","turnAssist","PrimaryR","PrimaryG","PrimaryB","warmup","DeadZone","circleRad","MouseXSensitivity","MouseYSensitivity","MaxGameVelocity","showHud","autoRollPreference","pitchSpeedFactor","yawSpeedFactor","rollSpeedFactor","brakeSpeedFactor","brakeFlatFactor","autoRollFactor","turnAssistFactor","torqueFactor","AutoTakeoffAltitude","TargetHoverHeight","AutopilotInterplanetaryThrottle","hideHudOnToggleWidgets","DampingMultiplier","fuelTankHandlingAtmo","fuelTankHandlingSpace","fuelTankHandlingRocket","RemoteFreeze","speedChangeLarge","speedChangeSmall","brightHud","brakeLandingRate","MaxPitch","ReentrySpeed","AtmoSpeedLimit","ReentryAltitude","centerX","centerY","vSpdMeterX","vSpdMeterY","altMeterX","altMeterY","fuelX","fuelY","LandingGearGroundHeight","TrajectoryAlignmentStrength","RemoteHud","StallAngle","ResolutionX","ResolutionY","UseSatNav","FuelTankOptimization","ContainerOptimization"}local b={"BrakeToggleStatus","BrakeIsOn","RetrogradeIsOn","ProgradeIsOn","Autopilot","TurnBurn","AltitudeHold","DisplayOrbit","BrakeLanding","Reentry","AutoTakeoff","HoldAltitude","AutopilotAccelerating","AutopilotBraking","AutopilotCruising","AutopilotRealigned","AutopilotEndSpeed","AutopilotStatus","AutopilotPlanetGravity","PrevViewLock","AutopilotTargetName","AutopilotTargetCoords","AutopilotTargetIndex","GearExtended","TargetGroundAltitude","TotalDistanceTravelled","TotalFlightTime","SavedLocations","VectorToTarget","LocationIndex","LastMaxBrake","LockPitch","LastMaxBrakeInAtmo","AntigravTargetAltitude","LastStartTime"}local c=system.print;local d=math.floor;local e=string.format;local f=json.decode;local g=json.encode;local h=core.getElementMaxHitPointsById;local j=unit.getAtmosphereDensity;local k=core.getElementHitPointsById;local l=core.getElementTypeById;local m=core.getElementMassById;local n=core.getConstructMass;local o=Nav.control.isRemoteControlled;function round(p,q)local r=10^(q or 0)return d(p*r+0.5)/r end;local s=round(ResolutionX/2,0)local t=round(ResolutionY/2,0)local u=false;local v=true;local w=55;local x=false;local y=1;local z=1;local A=false;local B=0;local C=0;local D=0;local E=0;local F=0;local G=0;local H=0;local I=false;local J=false;local K="empty"local L=1;local M=5;local N=5;local O=false;local P,Q=0;local R,S=0;local T=false;local U=false;local V=nil;local W=0;local X=0;local Y=false;local Z=0;local a0=0;local a1=0;local a2=3;local a3=0;local a4=""local a5=""local a6=0;local a7=false;local a8=false;local a9=false;local aa=-1;local ab=false;local ac=""local ad=j()>0;local ae=core.getAltitude()local af=core.getElementIdList()local ag=system.getTime()local ah=nil;local ai=false;local aj=[[rgb(]]..d(PrimaryR+0.5)..","..d(PrimaryG+0.5)..","..d(PrimaryB+0.5)..[[)]]local ak=[[rgb(]]..d(PrimaryR*0.9+0.5)..","..d(PrimaryG*0.9+0.5)..","..d(PrimaryB*0.9+0.5)..[[)]]local al={}local am=0;local an=0;local ao=""local ap=true;local aq={}local ar=1;local as=0.001;local at=ResolutionX;local au=ResolutionY;local av=nil;local aw=nil;local ax=nil;local ay=nil;local az=false;local aA=false;local aB=0;local aC=nil;local aD={}local aE={}local aF={}local aG=0;local aH=false;local aI={}local aJ={}local aK=d(1/apTickRate)*2;local aL={}local aM={}local aN={}local aO={}local aP=false;local aQ=16;local aR=0;local aS=nil;local aT=""local aU=nil;local aV=nil;local aW=nil;local aX=nil;local aY=nil;local aZ=nil;local a_=nil;local b0=false;local b1=false;function LoadVariables()if dbHud_1 then local b2=dbHud_1.hasKey;if not useTheseSettings then for b3,b4 in pairs(a)do if b2(b4)then local b5=f(dbHud_1.getStringValue(b4))if b5~=nil then c(b4 .." "..dbHud_1.getStringValue(b4))_G[b4]=b5;az=true end end end end;coroutine.yield()for b3,b4 in pairs(b)do if b2(b4)then local b5=f(dbHud_1.getStringValue(b4))if b5~=nil then c(b4 .." "..dbHud_1.getStringValue(b4))_G[b4]=b5;az=true end end end;if useTheseSettings then K="Updated user preferences used. Will be saved when you exit seat.\nToggle off useTheseSettings to use saved values"a2=5 elseif az then K="Loaded Saved Variables (see Lua Chat Tab for list)"else K="No Saved Variables Found - Stand up / leave remote to save settings"end else K="No databank found, install one anywhere and rerun the autoconfigure to save variables"end;local b6=system.getTime()if LastStartTime+180b8 then b8=b7 end;if ContainerOptimization>0 then b8=b8-b8*ContainerOptimization*0.05 end;if FuelTankOptimization>0 then b8=b8-b8*FuelTankOptimization*0.05 end;return b8 end;function ProcessElements()for b3 in pairs(af)do local type=l(af[b3])if type=="landing gear"then A=true end;if type=="dynamic core"then local b9=h(af[b3])if b9>10000 then aQ=128 elseif b9>1000 then aQ=64 elseif b9>150 then aQ=32 end end;aG=aG+h(af[b3])if fuelX~=0 and fuelY~=0 then if type=="Atmospheric Fuel Tank"or type=="Space Fuel Tank"or type=="Rocket Fuel Tank"then local b9=h(af[b3])local ba=m(af[b3])local b7=0;local bb=system.getTime()if type=="Atmospheric Fuel Tank"then local b8=400;local bc=35.03;if b9>10000 then b8=51200;bc=5480 elseif b9>1300 then b8=6400;bc=988.67 elseif b9>150 then b8=1600;bc=182.67 end;b7=ba-bc;if fuelTankHandlingAtmo>0 then b8=b8+b8*fuelTankHandlingAtmo*0.2 end;b8=CalculateFuelVolume(b7,b8)aD[#aD+1]={af[b3],core.getElementNameById(af[b3]),b8,bc,b7,bb}end;if type=="Rocket Fuel Tank"then local b8=320;local bc=173.42;if b9>65000 then b8=40000;bc=25740 elseif b9>6000 then b8=5120;bc=4720 elseif b9>700 then b8=640;bc=886.72 end;b7=ba-bc;if fuelTankHandlingRocket>0 then b8=b8+b8*fuelTankHandlingRocket*0.2 end;b8=CalculateFuelVolume(b7,b8)aF[#aF+1]={af[b3],core.getElementNameById(af[b3]),b8,bc,b7,bb}end;if type=="Space Fuel Tank"then local b8=2400;local bc=182.67;if b9>10000 then b8=76800;bc=5480 elseif b9>1300 then b8=9600;bc=988.67 end;b7=ba-bc;if fuelTankHandlingSpace>0 then b8=b8+b8*fuelTankHandlingSpace*0.2 end;b8=CalculateFuelVolume(b7,b8)aE[#aE+1]={af[b3],core.getElementNameById(af[b3]),b8,bc,b7,bb}end end end end end;function SetupChecks()if gyro~=nil then ah=gyro.getState()==1 end;if userControlScheme~="keyboard"then system.lockView(1)else system.lockView(0)end;if ad then BrakeIsOn=true end;if radar_1 then if l(radar_1.getId())=="Space Radar"then T=true else U=true end end;if door then for _,b4 in pairs(door)do b4.toggle()end end;if switch then for _,b4 in pairs(switch)do b4.toggle()end end;if forcefield then for _,b4 in pairs(forcefield)do b4.toggle()end end;if antigrav~=nil and not ExternalAGG then if antigrav.getState()==1 then antigrav.show()end end;if o()==1 and RemoteFreeze then system.freeze(1)else system.freeze(0)end;if A then GearExtended=Nav.control.isAnyLandingGearExtended()==1;if GearExtended then Nav.control.extendLandingGears()else Nav.control.retractLandingGears()end end;if TargetGroundAltitude~=nil then Nav.axisCommandManager:setTargetGroundAltitude(TargetGroundAltitude)if TargetGroundAltitude==0 and not A then GearExtended=true end else if GearExtended or not A then Nav.axisCommandManager:setTargetGroundAltitude(LandingGearGroundHeight)GearExtended=true else Nav.axisCommandManager:setTargetGroundAltitude(TargetHoverHeight)end end;if ad and not dbHud_1 and(GearExtended or not A)then BrakeIsOn=true end;WasInAtmo=ad end;function ConvertResolutionX(b4)if ResolutionX==1920 then return b4 else return round(ResolutionX*b4/1920,0)end end;function ConvertResolutionY(b4)if ResolutionY==1080 then return b4 else return round(ResolutionY*b4/1080,0)end end;function RefreshLastMaxBrake(bd,be)if bd==nil then bd=core.g()end;bd=round(bd,5)local bf=j()if be~=nil and be or(aC==nil or aC~=bd)then local velocity=core.getVelocity()local bg=vec3(velocity):len()local bh=f(unit.getData()).maxBrake;if bh~=nil and bh>0 and ad then bh=bh/utils.clamp(bg/100,0.1,1)bh=bh/bf;if bh>LastMaxBrakeInAtmo and bf>0.10 then LastMaxBrakeInAtmo=bh end end;if bh~=nil and bh>0 then LastMaxBrake=bh end;aC=bd end end;function MakeButton(bi,bj,bk,bl,bm,bn,bo,bp,bq)local br={enableName=bi,disableName=bj,width=bk,height=bl,x=bm,y=bn,toggleVar=bo,toggleFunction=bp,drawCondition=bq,hovered=false}table.insert(aq,br)return br end;function UpdateAtlasLocationsList()AtlasOrdered={}for b3,b4 in pairs(aS[0])do table.insert(AtlasOrdered,{name=b4.name,index=b3})end;local function bs(bt,bu)return bt.name-1 then aS[0][by]=bx end;UpdateAtlasLocationsList()K=CustomTarget.name.." position updated"end end;function ClearCurrentPosition()local by=-1;for b3,b4 in pairs(aS[0])do if b4.name and b4.name==CustomTarget.name then by=b3 end end;if by>-1 then table.remove(aS[0],by)end;by=-1;for b3,b4 in pairs(SavedLocations)do if b4.name and b4.name==CustomTarget.name then K=b4.name.." saved location cleared"by=b3;break end end;if by~=-1 then table.remove(SavedLocations,by)end;DecrementAutopilotTargetIndex()UpdateAtlasLocationsList()end;function DrawDeadZone(bz)bz[#bz+1]=e([[]],DeadZone)end;function ToggleRadarPanel()if radarPanelID~=nil and a6==0 then system.destroyWidgetPanel(radarPanelID)radarPanelID=nil;if perisPanelID~=nil then system.destroyWidgetPanel(perisPanelID)perisPanelID=nil end else if a6==1 then system.destroyWidgetPanel(radarPanelID)radarPanelID=nil;_autoconf.displayCategoryPanel(radar,radar_size,L_TEXT("ui_lua_widget_periscope", "Periscope"),"periscope")perisPanelID=_autoconf.panels[_autoconf.panels_size]end;placeRadar=true;if radarPanelID==nil and placeRadar then _autoconf.displayCategoryPanel(radar,radar_size,L_TEXT("ui_lua_widget_radar", "Radar"),"radar")radarPanelID=_autoconf.panels[_autoconf.panels_size]placeRadar=false end;a6=0 end end;function ToggleWidgets()if ap then unit.show()core.show()if atmofueltank_size>0 then _autoconf.displayCategoryPanel(atmofueltank,atmofueltank_size,L_TEXT("ui_lua_widget_atmofuel", "Atmo Fuel"),"fuel_container")fuelPanelID=_autoconf.panels[_autoconf.panels_size]end;if spacefueltank_size>0 then _autoconf.displayCategoryPanel(spacefueltank,spacefueltank_size,L_TEXT("ui_lua_widget_spacefuel", "Space Fuel"),"fuel_container")spacefuelPanelID=_autoconf.panels[_autoconf.panels_size]end;if rocketfueltank_size>0 then _autoconf.displayCategoryPanel(rocketfueltank,rocketfueltank_size,L_TEXT("ui_lua_widget_rocketfuel", "Rocket Fuel"),"fuel_container")rocketfuelPanelID=_autoconf.panels[_autoconf.panels_size]end;ap=false else unit.hide()core.hide()if fuelPanelID~=nil then system.destroyWidgetPanel(fuelPanelID)fuelPanelID=nil end;if spacefuelPanelID~=nil then system.destroyWidgetPanel(spacefuelPanelID)spacefuelPanelID=nil end;if rocketfuelPanelID~=nil then system.destroyWidgetPanel(rocketfuelPanelID)rocketfuelPanelID=nil end;ap=true end end;function SetupInterplanetaryPanel()panelInterplanetary=system.createWidgetPanel("Interplanetary Helper")interplanetaryHeader=system.createWidget(panelInterplanetary,"value")interplanetaryHeaderText=system.createData('{"label": "Target Planet", "value": "N/A", "unit":""}')system.addDataToWidget(interplanetaryHeaderText,interplanetaryHeader)widgetDistance=system.createWidget(panelInterplanetary,"value")widgetDistanceText=system.createData('{"label": "distance", "value": "N/A", "unit":""}')system.addDataToWidget(widgetDistanceText,widgetDistance)widgetTravelTime=system.createWidget(panelInterplanetary,"value")widgetTravelTimeText=system.createData('{"label": "Travel Time", "value": "N/A", "unit":""}')system.addDataToWidget(widgetTravelTimeText,widgetTravelTime)widgetMaxMass=system.createWidget(panelInterplanetary,"value")widgetMaxMassText=system.createData('{"label": "Maximum Mass", "value": "N/A", "unit":""}')system.addDataToWidget(widgetMaxMassText,widgetMaxMass)widgetCurBrakeDistance=system.createWidget(panelInterplanetary,"value")widgetCurBrakeDistanceText=system.createData('{"label": "Cur Brake distance", "value": "N/A", "unit":""}')if not ad then system.addDataToWidget(widgetCurBrakeDistanceText,widgetCurBrakeDistance)end;widgetCurBrakeTime=system.createWidget(panelInterplanetary,"value")widgetCurBrakeTimeText=system.createData('{"label": "Cur Brake Time", "value": "N/A", "unit":""}')if not ad then system.addDataToWidget(widgetCurBrakeTimeText,widgetCurBrakeTime)end;widgetMaxBrakeDistance=system.createWidget(panelInterplanetary,"value")widgetMaxBrakeDistanceText=system.createData('{"label": "Max Brake distance", "value": "N/A", "unit":""}')if not ad then system.addDataToWidget(widgetMaxBrakeDistanceText,widgetMaxBrakeDistance)end;widgetMaxBrakeTime=system.createWidget(panelInterplanetary,"value")widgetMaxBrakeTimeText=system.createData('{"label": "Max Brake Time", "value": "N/A", "unit":""}')if not ad then system.addDataToWidget(widgetMaxBrakeTimeText,widgetMaxBrakeTime)end;widgetTrajectoryAltitude=system.createWidget(panelInterplanetary,"value")widgetTrajectoryAltitudeText=system.createData('{"label": "Projected Altitude", "value": "N/A", "unit":""}')if not ad then system.addDataToWidget(widgetTrajectoryAltitudeText,widgetTrajectoryAltitude)end end;function Contains(bA,bB,bm,bn,bk,bl)if bA>bm and bAbn and bBw then K="WARNING: Insufficient Brakes - Attempting coast landing, beware obstacles"end;AltitudeHold=false;AutoTakeoff=false;LockPitch=nil;BrakeLanding=true;Nav.axisCommandManager:setThrottleCommand(axisCommandId.longitudinal,0)end end;function ToggleAutoTakeoff()if AutoTakeoff then AutoTakeoff=false;if AltitudeHold then ToggleAltitudeHold()end else if not AltitudeHold then ToggleAltitudeHold()end;AutoTakeoff=true;HoldAltitude=ae+AutoTakeoffAltitude;GearExtended=false;Nav.control.retractLandingGears()Nav.axisCommandManager:setTargetGroundAltitude(500)BrakeIsOn=true end end;function ToggleLockPitch()if LockPitch==nil then local bC=vec3(core.getConstructWorldOrientationForward())local bD=vec3(core.getConstructWorldOrientationRight())local bE=vec3(core.getWorldVertical())local bF=getPitch(bE,bC,bD)LockPitch=bF;AutoTakeoff=false;AltitudeHold=false;BrakeLanding=false else LockPitch=nil end end;function ToggleAltitudeHold()AltitudeHold=not AltitudeHold;if AltitudeHold then Autopilot=false;ProgradeIsOn=false;RetrogradeIsOn=false;I=false;BrakeLanding=false;Reentry=false;autoRoll=true;LockPitch=nil;if not GearExtended and not BrakeIsOn or not ad then AutoTakeoff=false;HoldAltitude=ae;if not a8 and Nav.axisCommandManager:getAxisCommandType(0)==0 then Nav.control.cancelCurrentControlMasterMode()end else AutoTakeoff=true;HoldAltitude=ae+AutoTakeoffAltitude;GearExtended=false;Nav.control.retractLandingGears()Nav.axisCommandManager:setTargetGroundAltitude(500)BrakeIsOn=true end;if a8 then HoldAltitude=100000 end else autoRoll=autoRollPreference;AutoTakeoff=false;BrakeLanding=false;Reentry=false;AutoTakeoff=false;VectorToTarget=false end end;function ToggleFollowMode()if o()==1 then I=not I;if I then Autopilot=false;RetrogradeIsOn=false;ProgradeIsOn=false;AltitudeHold=false;Reentry=false;BrakeLanding=false;AutoTakeoff=false;OldGearExtended=GearExtended;GearExtended=false;Nav.control.retractLandingGears()Nav.axisCommandManager:setTargetGroundAltitude(TargetHoverHeight)else BrakeIsOn=true;autoRoll=autoRollPreference;GearExtended=OldGearExtended;if GearExtended then Nav.control.extendLandingGears()Nav.axisCommandManager:setTargetGroundAltitude(LandingGearGroundHeight)end end else K="Follow Mode only works with Remote controller"I=false end end;function ToggleAutopilot()if AutopilotTargetIndex>0 and not Autopilot and not VectorToTarget then if CustomTarget~=nil then LockPitch=nil;if planet.name==CustomTarget.planetname then StrongBrakes=planet.gravity*9.80665*n()w then K="Insufficient Brake Force\nCoast landing will be inaccurate"end;if j()>0 then if not AltitudeHold then if not VectorToTarget then ToggleVectorToTarget()end else if not VectorToTarget then ToggleVectorToTarget()end end else a7=true end else a8=true;RetrogradeIsOn=false;ProgradeIsOn=false;if j()~=0 then ToggleAltitudeHold()else Autopilot=true end end elseif j()==0 then Autopilot=true;RetrogradeIsOn=false;ProgradeIsOn=false;AutopilotRealigned=false;I=false;AltitudeHold=false;BrakeLanding=false;Reentry=false;AutoTakeoff=false;u=false;LockPitch=nil else a8=true;ToggleAltitudeHold()end else Autopilot=false;AutopilotRealigned=false;VectorToTarget=false;u=false;AutoTakeoff=false;AltitudeHold=false;VectorToTarget=false end end;function ProgradeToggle()ProgradeIsOn=not ProgradeIsOn;RetrogradeIsOn=false;Autopilot=false;AltitudeHold=false;I=false;BrakeLanding=false;LockPitch=nil;Reentry=false;AutoTakeoff=false end;function RetrogradeToggle()RetrogradeIsOn=not RetrogradeIsOn;ProgradeIsOn=false;Autopilot=false;AltitudeHold=false;I=false;BrakeLanding=false;LockPitch=nil;Reentry=false;AutoTakeoff=false end;function BrakeToggle()BrakeIsOn=not BrakeIsOn;if BrakeLanding then BrakeLanding=false;autoRoll=autoRollPreference end;if BrakeIsOn then AltitudeHold=false;VectorToTarget=false;AutoTakeoff=false;Reentry=false;ProgradeIsOn=false;BrakeLanding=false;AutoLanding=false;AltitudeHold=false;LockPitch=nil;autoRoll=autoRollPreference end end;function CheckDamage(bz)local bG=0;ao=""local bH=aG;local bI=0;local bJ=0;local bK=0;local bL=0;local bM=""for b3 in pairs(af)do local b9=0;local bN=0;bN=h(af[b3])b9=k(af[b3])bI=bI+b9;if b90 and al[11]==af[b3]then for bP in pairs(al)do core.deleteSticker(al[bP])end;al={}end end;bG=d(bI/bH*100)if bG<100 then bz[#bz+1]=[[]]bL=d(bG*2.55)bM=e("rgb(%d,%d,%d)",255-bL,bL,0)if bG<100 then bz[#bz+1]=e([[Elemental Integrity: %i %%]],bM,bG)if bK>0 then bz[#bz+1]=e([[Disabled Modules: %i Damaged Modules: %i]],bM,bK,bJ)elseif bJ>0 then bz[#bz+1]=e([[Damaged Modules: %i]],bM,bJ)end end;bz[#bz+1]=[[<\g>]]end end;function DrawCursorLine(bz)local bQ=d(utils.clamp(a3/(at/4)*255,0,255))bz[#bz+1]=e("",a0,a1,d(PrimaryR+0.5)+bQ,d(PrimaryG+0.5)-bQ,d(PrimaryB+0.5)-bQ)end;function getPitch(bR,bS,bu)local bT=bR:cross(bu):normalize_inplace()local bF=math.acos(utils.clamp(bT:dot(-bS),-1,1))*constants.rad2deg;if bT:cross(-bS):dot(bu)<0 then bF=-bF end;return bF end;function clearAll()if ab then ab=false;AutopilotAccelerating=false;AutopilotBraking=false;AutopilotCruising=false;Autopilot=false;AutopilotRealigned=false;AutopilotStatus="Aligning"RetrogradeIsOn=false;ProgradeIsOn=false;AltitudeHold=false;Reentry=false;BrakeLanding=false;BrakeIsOn=false;AutoTakeoff=false;I=false;u=false;a7=false;a8=false;x=false;autoRoll=autoRollPreference;VectorToTarget=false;TurnBurn=false;ah=false;LockPitch=nil else ab=true end end;function wipeSaveVariables()if not dbHud_1 then K="No Databank Found, unable to wipe. \nYou must have a Databank attached to ship prior to running the HUD autoconfigure"a2=5 else if aA then for b3,b4 in pairs(a)do dbHud_1.setStringValue(b4,g(nil))end;for b3,b4 in pairs(b)do if b4~="SavedLocations"then dbHud_1.setStringValue(b4,g(nil))end end;K="Databank wiped. New variables will save after re-enter seat and exit"a2=5;aA=false;az=false;Y=true else K="Press ALT-7 again to confirm wipe of ALL data"aA=true end end end;function CheckButtons()for _,b4 in pairs(aq)do if b4.hovered then if not b4.drawCondition or b4.drawCondition()then b4.toggleFunction()end;b4.hovered=false end end end;function SetButtonContains()local bm=a0+at/2;local bn=a1+au/2;for _,b4 in pairs(aq)do b4.hovered=Contains(bm,bn,b4.x,b4.y,b4.width,b4.height)end end;function DrawButton(bz,bU,hover,bm,bn,bV,bW,bX,bY,bZ,b_)if type(bZ)=="function"then bZ=bZ()end;if type(b_)=="function"then b_=b_()end;bz[#bz+1]=e(""if bU then bz[#bz+1]=e("%s",bZ)else bz[#bz+1]=e("%s",b_)end end;function DrawButtons(bz)local c0="rgb(50,50,50)'"local c1="rgb(210,200,200)"local c2=DrawButton;for _,b4 in pairs(aq)do local bj=b4.disableName;local bi=b4.enableName;if type(bj)=="function"then bj=bj()end;if type(bi)=="function"then bi=bi()end;if not b4.drawCondition or b4.drawCondition()then c2(bz,b4.toggleVar(),b4.hovered,b4.x,b4.y,b4.width,b4.height,c1,c0,bj,bi)end end end;function DrawTank(bz,aP,bm,c3,c4,c5,c6,c7)local c8=1;local c9=2;local ca=3;local cb=4;local cc=5;local cd=6;local ce=""local cf=0;local cg=fuelY;local ch=fuelY+10;if o()==1 and not RemoteHud then cg=cg-50;ch=ch-50 end;bz[#bz+1]=[[]]if c4=="ATMO"then ce="atmofueltank"elseif c4=="SPACE"then ce="spacefueltank"else ce="rocketfueltank"end;cf=_G[ce.."_size"]if#c5>0 then for i=1,#c5 do local bv=string.sub(c5[i][c9],1,12)local ci=0;for bP=1,cf do if c5[i][c9]==f(unit[ce.."_"..bP].getData()).name then ci=bP;break end end;if aP or c6[i]==nil or c7[i]==nil then local cj=0;local ck=0;local cl=0;local cm=0;local bb=system.getTime()if ci~=0 then c7[i]=f(unit[ce.."_"..ci].getData()).percentage;c6[i]=f(unit[ce.."_"..ci].getData()).timeLeft;if c6[i]=="n/a"then c6[i]=0 end else cl=m(c5[i][c8])-c5[i][cb]cj=c5[i][ca]c7[i]=d(0.5+cl*100/cj)ck=c5[i][cc]cm=c5[i][cd]if ck<=cl then c6[i]=0 else c6[i]=d(0.5+cl/((ck-cl)/(bb-cm)))end;c5[i][cc]=cl;c5[i][cd]=bb end end;if bv==c3 then bv=e("%s %d",c4,i)end;if ci==0 then bv=bv.." *"end;local cn;if c6[i]==0 then cn="n/a"else cn=FormatTimeString(c6[i])end;if c7[i]~=nil then local bL=d(c7[i]*2.55)local bM=e("rgb(%d,%d,%d)",255-bL,bL,0)local co=""if cn~="n/a"and c6[i]<120 or c7[i]<5 then if aP then co=[[class="red"]]end end;bz[#bz+1]=e([[ + %s + %d%% %s + ]],bm,cg,co,bv,bm,ch,bM,c7[i],cn)cg=cg+30;ch=ch+30 end end end;bz[#bz+1]=""end;function HideInterplanetaryPanel()system.destroyWidgetPanel(panelInterplanetary)panelInterplanetary=nil end;function getRelativePitch(velocity)velocity=vec3(velocity)local bF=-math.deg(math.atan(velocity.y,velocity.z))+180;bF=bF-90;if bF<0 then bF=360+bF end;if bF>180 then bF=-180+bF-180 end;return-bF end;function getRelativeYaw(velocity)velocity=vec3(velocity)local cp=math.deg(math.atan(velocity.y,velocity.x))-90;if cp<-180 then cp=360+cp end;return cp end;function AlignToWorldVector(cq,cr)if not ad or RateOfChange>MinimumRateOfChange+0.08 or aa~=-1 then if cr==nil then cr=as end;cq=vec3(cq):normalize()local cs=vec3(core.getConstructWorldOrientationForward())-cq;local ct=-getMagnitudeInDirection(cs,core.getConstructWorldOrientationRight())*ar;local cu=-getMagnitudeInDirection(cs,core.getConstructWorldOrientationUp())*ar;if am==0 then am=ct/2 end;if an==0 then an=cu/2 end;D=D-(ct+(ct-am)*DampingMultiplier)C=C+cu+(cu-an)*DampingMultiplier;am=ct;an=cu;if math.abs(ct)0 and CustomTarget~=nil end)MakeButton("Clear Position","Clear Position",200,cy.height,cy.x-200-30,cy.y,function()return true end,ClearCurrentPosition,function()return AutopilotTargetIndex>0 and CustomTarget~=nil end)cv=60;cw=300;local bm=10;local bn=au/2-300;MakeButton("Enable Turn and Burn","Disable Turn and Burn",cw,cv,bm,bn,function()return TurnBurn end,ToggleTurnBurn)MakeButton("Engage Altitude Hold","Disable Altitude Hold",cw,cv,bm+cw+20,bn,function()return AltitudeHold end,ToggleAltitudeHold)bn=bn+cv+20;MakeButton("Engage Autoland","Disable Autoland",cw,cv,bm,bn,function()return AutoLanding end,ToggleAutoLanding)MakeButton("Engage Auto Takeoff","Disable Auto Takeoff",cw,cv,bm+cw+20,bn,function()return AutoTakeoff end,ToggleAutoTakeoff)bn=bn+cv+20;MakeButton("Show Orbit Display","Hide Orbit Display",cw,cv,bm,bn,function()return DisplayOrbit end,function()DisplayOrbit=not DisplayOrbit;if DisplayOrbit then K="Orbit Display Enabled"else K="Orbit Display Disabled"end end)bn=bn+cv+20;MakeButton("Glide Re-Entry","Cancel Glide Re-Entry",cw,cv,bm,bn,function()return Reentry end,function()x=true;BeginReentry()end,function()return ae>ReentryAltitude end)MakeButton("Parachute Re-Entry","Cancel Parachute Re-Entry",cw,cv,bm+cw+20,bn,function()return Reentry end,BeginReentry,function()return ae>ReentryAltitude end)bn=bn+cv+20;MakeButton("Engage Follow Mode","Disable Follow Mode",cw,cv,bm,bn,function()return I end,ToggleFollowMode,function()return o()==1 end)MakeButton("Enable Repair Arrows","Disable Repair Arrows",cw,cv,bm+cw+20,bn,function()return aH end,function()aH=not aH;if aH then K="Repair Arrows Enabled"else K="Repair Arrows Diabled"end end,function()return o()==1 end)bn=bn+cv+20;if not ExternalAGG then MakeButton("Enable AGG","Disable AGG",cw,cv,bm,bn,function()return antigrav.getState()==1 end,ToggleAntigrav,function()return antigrav~=nil end)end;bn=bn+cv+20;MakeButton(function()return e("Toggle Control Scheme - Current: %s",userControlScheme)end,function()return e("Control Scheme: %s",userControlScheme)end,cw*2,cv,bm,bn,function()return false end,function()if userControlScheme=="keyboard"then userControlScheme="mouse"elseif userControlScheme=="mouse"then userControlScheme="virtual joystick"else userControlScheme="keyboard"end end)end;function GetFlightStyle()local cz=Nav.axisCommandManager:getAxisCommandType(0)local cA="TRAVEL"if cz==1 then cA="CRUISE"end;if Autopilot then cA="AUTOPILOT"end;return cA end;function updateHud(bz)local cB=ae;local velocity=core.getVelocity()local bg=vec3(velocity):len()local bE=vec3(core.getWorldVertical())local bC=vec3(core.getConstructWorldOrientationForward())local bD=vec3(core.getConstructWorldOrientationRight())local cC=vec3(core.getConstructWorldOrientationUp())local cD=getRoll(bE,bC,bD)local cE=cD/180*math.pi;local cF=math.cos(cE)local cG=math.sin(cE)local bF=getPitch(bE,bC,bD*cF+cC*cG)local cH=cD;local cI=bF;local cJ=j()local cK=d(unit.getThrottle())local cL=bg*3.6;local cM=unit.getAxisCommandValue(0)local cA=GetFlightStyle()local cN="ROLL"local cO=unit.getClosestPlanetInfluence()>0;if cK==nil then cK=0 end;if not cO then if bg>5 then bF=getRelativePitch(velocity)cD=getRelativeYaw(velocity)else bF=0;cD=0 end;cN="YAW"end;bz[#bz+1]=a5;bz[#bz+1]=ao;bz[#bz+1]=a4;if aR%aK==0 then aP=true end;if fuelX~=0 and fuelY~=0 then DrawTank(bz,aP,fuelX,"Atmospheric ","ATMO",aD,aN,aO)DrawTank(bz,aP,fuelX+100,"Space fuel t","SPACE",aE,aL,aM)DrawTank(bz,aP,fuelX+200,"Rocket fuel ","ROCKET",aF,aI,aJ)end;if aP then aP=false;aR=0 end;aR=aR+1;DrawVerticalSpeed(bz,cB)if o()==0 or RemoteHud then if not IsInFreeLook()or brightHud then if cO then DrawRollLines(bz,centerX,centerY,cH,cN,cO)DrawArtificialHorizon(bz,cI,cH,centerX,centerY,cO,d(getRelativeYaw(velocity)),bg)else DrawRollLines(bz,centerX,centerY,cD,cN,cO)DrawArtificialHorizon(bz,bF,cD,centerX,centerY,cO,d(cD),bg)end;DrawAltitudeDisplay(bz,cB,cO)DrawPrograde(bz,velocity,bg,centerX,centerY)end end;DrawThrottle(bz,cA,cK,cM)DrawSpeed(bz,cL)DrawWarnings(bz)DisplayOrbitScreen(bz)if screen_2 then local cP=vec3(core.getConstructWorldPos())local bm=960+cP.x/aU;local bn=450+cP.y/aV;screen_2.moveContent(aW,(bm-80)/19.2,(bn-80)/10.8)end end;function IsInFreeLook()return system.isViewLocked()==0 and userControlScheme~="keyboard"and o()==0 end;function HUDPrologue(bz)local cQ=aj;local cR=ak;local cS=aj;local cT=ak;if IsInFreeLook()and not brightHud then cQ=[[rgb(]]..d(PrimaryR*0.4+0.5)..","..d(PrimaryG*0.4+0.5)..","..d(PrimaryB*0.3+0.5)..[[)]]cR=[[rgb(]]..d(PrimaryR*0.3+0.5)..","..d(PrimaryG*0.3+0.5)..","..d(PrimaryB*0.2+0.5)..[[)]]end;bz[#bz+1]=e([[ + + + + + + ]],cQ,cQ,cS,cS,cR,cR,cT,cT,ResolutionX,ResolutionY)end;function HUDEpilogue(bz)bz[#bz+1]=""end;function DrawSpeed(bz,cL)local cU=throtPosY-10;local cV=throtPosX+10;bz[#bz+1]=[[]]if o()==1 and not RemoteHud then cU=75 end;bz[#bz+1]=e([[ + + %d km/h + + ]],cV,cU,d(cL))end;function DrawOdometer(bz,W,TotalDistanceTravelled,cA,X,cJ)local cW=ConvertResolutionX(1240)local cX=ConvertResolutionY(55)local cY=cX+10;local cJ=j()local bd=core.g()local cZ=0;local c_=0;local d0=0;RefreshLastMaxBrake(bd)if ad then d0=LastMaxBrakeInAtmo else d0=LastMaxBrake end;maxThrust=Nav:maxForceForward()aB=n()if not ShowOdometer then return end;local d1=vec3(core.getWorldAcceleration()):len()/9.80665;if bd>0.1 then c_=aB*bd;cZ=maxThrust/bd end;bz[#bz+1]=[[]]if o()==1 and not RemoteHud then cW=ConvertResolutionX(1120)cX=ConvertResolutionY(55)cY=cX+10 elseif ad then local d2=ConvertResolutionX(770)bz[#bz+1]=e([[ + ATMOSPHERE + %.2f + ]],d2,cX,d2,cY,cJ)end;bz[#bz+1]=e([[ + + + GRAVITY + %.2f g + ACCEL + %.2f g + ]],cW,cX,cW,cY,bd/9.80665,cW,cX+20,cW,cY+20,d1)bz[#bz+1]=e([[ + + ]],ConvertResolutionX(660),ConvertResolutionX(700),ConvertResolutionY(35),ConvertResolutionX(960),ConvertResolutionY(55),ConvertResolutionX(1240),ConvertResolutionY(35),ConvertResolutionX(1280))if o()==0 or RemoteHud then bz[#bz+1]=e([[ + Trip: %.2f km + Lifetime: %.2f Mm + Trip Time: %s + Total Time: %s + Mass: %.2f Tons + Max Brake: %.2f kN + Max Thrust: %.2f kN + %s]],ConvertResolutionX(700),ConvertResolutionY(20),W,ConvertResolutionX(700),ConvertResolutionY(30),TotalDistanceTravelled/1000,ConvertResolutionX(830),ConvertResolutionY(20),FormatTimeString(X),ConvertResolutionX(830),ConvertResolutionY(30),FormatTimeString(TotalFlightTime),ConvertResolutionX(970),ConvertResolutionY(20),aB/1000,ConvertResolutionX(1240),ConvertResolutionY(10),d0/1000,ConvertResolutionX(1240),ConvertResolutionY(30),maxThrust/1000,ConvertResolutionX(960),ConvertResolutionY(180),cA)if bd>0.1 then bz[#bz+1]=e([[ + Max Mass: %.2f Tons + Req Thrust: %.2f kN + ]],ConvertResolutionX(970),ConvertResolutionY(30),cZ/1000,ConvertResolutionX(1240),ConvertResolutionY(20),c_/1000)else bz[#bz+1]=e([[ + Max Mass: n/a + Req Thrust: n/a + ]],ConvertResolutionX(970),ConvertResolutionY(30),ConvertResolutionX(1240),ConvertResolutionY(20))end else bz[#bz+1]=e([[%s]],ConvertResolutionX(960),ConvertResolutionY(33),cA)end;bz[#bz+1]=""end;function DrawThrottle(bz,cA,cK,cM)local cg=throtPosY+10;local ch=throtPosY+20;if o()==1 and not RemoteHud then cg=55;ch=65 end;local d3="CRUISE"local unit="km/h"local d4=cM;if cA=="TRAVEL"or cA=="AUTOPILOT"then d3="THROT"unit="%"d4=cK;local d5="dim"if cK<0 then d5="red"end;bz[#bz+1]=e([[ + + + + ]],d5,throtPosX-7,throtPosY-50,throtPosX,throtPosY-50,throtPosX,throtPosY+50,throtPosX-7,throtPosY+50,1-math.abs(cK),throtPosX-10,throtPosY+50,throtPosX-15,throtPosY+53,throtPosX-15,throtPosY+47)end;bz[#bz+1]=e([[ + + %s + %d %s + + ]],throtPosX+10,cg,d3,throtPosX+10,ch,d4,unit)end;function DrawVerticalSpeed(bz,cB)if cB<200000 and not ad or cB and ad then local d6=-vec3(core.getWorldVertical()):dot(vec3(core.getWorldVelocity()))local d7=0;if math.abs(d6)>1 then d7=45*math.log(math.abs(d6),10)if d6<0 then d7=-d7 end end;bz[#bz+1]=e([[ + + 1000 + 100 + 10 + O + -10 + -100 + -1000 + %d m/s + + + - ]],throtPosX+10,c5,cT,throtPosX+10,c6,cU,unit)end;function DrawVerticalSpeed(bo,cq)if cq<200000 and not ad or cq and ad then local cW=-vec3(core.getWorldVertical()):dot(vec3(core.getWorldVelocity()))local cX=0;if math.abs(cW)>1 then cX=45*math.log(math.abs(cW),10)if cW<0 then cX=-cX end end;bo[#bo+1]=e([[ - - 1000 - 100 - 10 - O - -10 - -100 - -1000 - %d m/s - - - - - - - ]],vSpdMeterX,vSpdMeterY,d(cW),d(cX))end end;function getHeading(bH)local cY=-vec3(core.getWorldVertical())bH=bH-bH:project_on(cY)local cZ=vec3(0,0,1)cZ=cZ-cZ:project_on(cY)local c_=cZ:cross(cY)local cX=cZ:angle_between(bH)*constants.rad2deg;if bH:dot(c_)<0 then cX=360-cX end;return cX end;function DrawRollLines(bo,centerX,centerY,cw,cC,cD)local d0=circleRad;local d1=20;d1=d(d1)local d2=d(cw)if cD then for i=-45,45,5 do local d3=i;bo[#bo+1]=e([[]],d3,centerX,centerY)len=5;if i%15==0 then len=15 elseif i%10==0 then len=10 end;bo[#bo+1]=e([[]],centerX,centerY+d0+d1-len,centerX,centerY+d0+d1)end;bo[#bo+1]=e([[" + + + ]],vSpdMeterX,vSpdMeterY,d(d6),d(d7))end end;function getHeading(bS)local d8=-vec3(core.getWorldVertical())bS=bS-bS:project_on(d8)local d9=vec3(0,0,1)d9=d9-d9:project_on(d8)local da=d9:cross(d8)local d7=d9:angle_between(bS)*constants.rad2deg;if bS:dot(da)<0 then d7=360-d7 end;return d7 end;function DrawRollLines(bz,centerX,centerY,cH,cN,cO)local db=circleRad;local dc=20;dc=d(dc)local dd=d(cH)if cO then for i=-45,45,5 do local de=i;bz[#bz+1]=e([[]],de,centerX,centerY)len=5;if i%15==0 then len=15 elseif i%10==0 then len=10 end;bz[#bz+1]=e([[]],centerX,centerY+db+dc-len,centerX,centerY+db+dc)end;bz[#bz+1]=e([[" + + %s + %d deg + + ]],centerX,centerY+db+dc-35,cN,centerX,centerY+db+dc-25,dd)bz[#bz+1]=e([[]],-cH,centerX,centerY)bz[#bz+1]=e([[<]],centerX-5,centerY+db+dc-20,centerX+5,centerY+db+dc-20,centerX,centerY+db+dc-15)bz[#bz+1]=""end;local cp=dd;if cO then cp=getHeading(vec3(core.getConstructWorldOrientationForward()))end;local df=20;local dg=d(cp)local dh=0;local di=centerY+db+dc+20;local dj=centerX;if cN~="YAW"then di=ConvertResolutionY(130)dj=ConvertResolutionX(960)end;local dk=[[%d]],bm+5,di-12,p)elseif i%5==0 then dh=5 end;if dh==10 then dk=e([[%s M %f %f v %d]],dk,bm,di-5,dh)else dk=e([[%s M %f %f v %d]],dk,bm,di-2.5,dh)end end;bz[#bz+1]=dk..[["/>]]bz[#bz+1]=e([[<]],dj-5,di+10,dj+5,di+10,dj,di+5)if cO then cN="HDG"end;bz[#bz+1]=e([[" + + %d deg + %s + + ]],dj,di+25,dg,dj,di+35,cN)end;function DrawArtificialHorizon(bz,cI,cH,centerX,centerY,cO,dl,bg)local db=circleRad;local dm=d(db*3/5)if db>0 then local dn=d(cI)local len=0;local dk=e([[]],db-1,centerX,centerY)bz[#bz+1]=[[]]for i=d(dn-30-dn%5+0.5),d(dn+30+dn%5+0.5),5 do if i%10==0 then len=30 elseif i%5==0 then len=20 end;local bn=centerY+-i*5+cI*5;if len==30 then dk=e([[%s M %d %f h %d]],dk,centerX-dm-len,bn,len)if ad then bz[#bz+1]=e([[%d]],-1*cH,centerX,centerY,centerX-dm+10,bn,i)bz[#bz+1]=e([[%d]],-1*cH,centerX,centerY,centerX+dm-10,bn,i)if i==0 or i==180 or i==-180 then bz[#bz+1]=e([[]],-1*cH,centerX,centerY,centerX-dm+20,bn,dm*2-40)end else bz[#bz+1]=e([[%d]],centerX-dm+10,bn,i)bz[#bz+1]=e([[%d]],centerX+dm-10,bn,i)end;dk=e([[%s M %d %f h %d]],dk,centerX+dm,bn,len)else dk=e([[%s M %d %f h %d]],dk,centerX-dm-len,bn,len)dk=e([[%s M %d %f h %d]],dk,centerX+dm,bn,len)end end;bz[#bz+1]=dk..[["/>]]local dp="PITCH"if not cO then dp="REL PITCH"end;if cI>90 and not ad then cI=90-(cI-90)elseif cI<-90 and not ad then cI=-90-(cI+90)end;if db>200 then if ad then if bg>w then bz[#bz+1]=e([[" - %s - %d deg + %s + %d deg - ]],centerX,centerY+d0+d1-35,cC,centerX,centerY+d0+d1-25,d2)bo[#bo+1]=e([[]],-cw,centerX,centerY)bo[#bo+1]=e([[<]],centerX-5,centerY+d0+d1-20,centerX+5,centerY+d0+d1-20,centerX,centerY+d0+d1-15)bo[#bo+1]=""end;local ce=d2;if cD then ce=getHeading(vec3(core.getConstructWorldOrientationForward()))end;local d4=20;local d5=d(ce)local d6=0;local d7=centerY+d0+d1+20;local d8=centerX;if cC~="YAW"then d7=ConvertResolutionY(130)d8=ConvertResolutionX(960)end;local d9=[[%d]],bb+5,d7-12,p)elseif i%5==0 then d6=5 end;if d6==10 then d9=e([[%s M %f %f v %d]],d9,bb,d7-5,d6)else d9=e([[%s M %f %f v %d]],d9,bb,d7-2.5,d6)end end;bo[#bo+1]=d9 ..[["/>]]bo[#bo+1]=e([[<]],d8-5,d7+10,d8+5,d7+10,d8,d7+5)if cD then cC="HDG"end;bo[#bo+1]=e([[" + ]],centerX,centerY-15,"Yaw",centerX,centerY+20,dl)end;bz[#bz+1]=e([[]],-cH,centerX,centerY)else bz[#bz+1]=e([[]],centerX,centerY)end;bz[#bz+1]=e([[< class="pdim txtend">%d]],centerX-dm+25,centerY-5,centerX-dm+20,centerY,centerX-dm+25,centerY+5,centerX-dm+50,centerY+4,dn)bz[#bz+1]=e([[< class="pdim txtend">%d]],centerX+dm-25,centerY-5,centerX+dm-20,centerY,centerX+dm-25,centerY+5,centerX+dm-30,centerY+4,dn)bz[#bz+1]=""end;local dq=d(db/3)bz[#bz+1]=e([[]],centerX-dq,centerY,db-dq)if not ad and cO then bz[#bz+1]=e([[]],-1*cH,centerX,centerY,centerX-dm+10,centerY,dm*2-20)end;bz[#bz+1]=""if db<200 then if ad and bg>w then bz[#bz+1]=e([[" + %s + %d deg + %s %d deg + + ]],centerX,centerY-db,dp,centerX,centerY-db+10,dn,centerX,centerY-15,"Yaw",centerX,centerY+20,dl)else bz[#bz+1]=e([[" + %s + %d deg - ]],d8,d7+25,d5,d8,d7+35,cC)end;function DrawArtificialHorizon(bo,cx,cw,centerX,centerY,cD,da,b5)local d0=circleRad;local db=d(d0*3/5)if d0>0 then local dc=d(cx)local len=0;local d9=e([[]],d0-1,centerX,centerY)bo[#bo+1]=[[]]for i=d(dc-30-dc%5+0.5),d(dc+30+dc%5+0.5),5 do if i%10==0 then len=30 elseif i%5==0 then len=20 end;local bc=centerY+-i*5+cx*5;if len==30 then d9=e([[%s M %d %f h %d]],d9,centerX-db-len,bc,len)if ad then bo[#bo+1]=e([[%d]],-1*cw,centerX,centerY,centerX-db+10,bc,i)bo[#bo+1]=e([[%d]],-1*cw,centerX,centerY,centerX+db-10,bc,i)if i==0 or i==180 or i==-180 then bo[#bo+1]=e([[]],-1*cw,centerX,centerY,centerX-db+20,bc,db*2-40)end else bo[#bo+1]=e([[%d]],centerX-db+10,bc,i)bo[#bo+1]=e([[%d]],centerX+db-10,bc,i)end;d9=e([[%s M %d %f h %d]],d9,centerX+db,bc,len)else d9=e([[%s M %d %f h %d]],d9,centerX-db-len,bc,len)d9=e([[%s M %d %f h %d]],d9,centerX+db,bc,len)end end;bo[#bo+1]=d9 ..[["/>]]local dd="PITCH"if not cD then dd="REL PITCH"end;if cx>90 and not ad then cx=90-(cx-90)elseif cx<-90 and not ad then cx=-90-(cx+90)end;if d0>200 then if ad then if b5>w then bo[#bo+1]=e([[" - - %s - %d deg - - ]],centerX,centerY-15,"Yaw",centerX,centerY+20,da)end;bo[#bo+1]=e([[]],-cw,centerX,centerY)else bo[#bo+1]=e([[]],centerX,centerY)end;bo[#bo+1]=e([[< class="pdim txtend">%d]],centerX-db+25,centerY-5,centerX-db+20,centerY,centerX-db+25,centerY+5,centerX-db+50,centerY+4,dc)bo[#bo+1]=e([[< class="pdim txtend">%d]],centerX+db-25,centerY-5,centerX+db-20,centerY,centerX+db-25,centerY+5,centerX+db-30,centerY+4,dc)bo[#bo+1]=""end;local de=d(d0/3)bo[#bo+1]=e([[]],centerX-de,centerY,d0-de)if not ad and cD then bo[#bo+1]=e([[]],-1*cw,centerX,centerY,centerX-db+10,centerY,db*2-20)end;bo[#bo+1]=""if d0<200 then if ad and b5>w then bo[#bo+1]=e([[" - - %s - %d deg - %s - %d deg - - ]],centerX,centerY-d0,dd,centerX,centerY-d0+10,dc,centerX,centerY-15,"Yaw",centerX,centerY+20,da)else bo[#bo+1]=e([[" - - %s - %d deg - - ]],centerX,centerY-d0,dd,centerX,centerY-d0+15,dc)end end end end;function DrawAltitudeDisplay(bo,cq,cD)local df=altMeterX;local dg=altMeterY;local dh=78;local di=19;local dj=AboveGroundLevel()if dj~=-1 then table.insert(bo,e([[ - - AGL: %.1fm + ]],centerX,centerY-db,dp,centerX,centerY-db+15,dn)end end end end;function DrawAltitudeDisplay(bz,cB,cO)local dr=altMeterX;local ds=altMeterY;local dt=78;local du=19;local dv=AboveGroundLevel()if dv~=-1 then table.insert(bz,e([[ + + AGL: %.1fm + + ]],dr+dt,ds+du+20,dv))end;if cO and(cB<200000 and not ad or cB and ad)then table.insert(bz,e([[ + + + + ]],dr-1,ds-4,dt+2,du+6,dr+1,ds-1,dt-4,du))local by=0;local dw=1;local dx=0;local dy=cB<0;local dz=9;if dy then dz=0 end;local cB=math.abs(cB)while by<6 do local dA=11;local dB=16;local dC=9;local dD=14;local co="altsm"if by>2 then dB=dB+3;dA=dA+2;dD=dD+2;dC=dC-6;co="altbig"end;if dy then co=co.." red"end;local dE=cB/dw%10;local dF=d(dE)local dG=d((dF+1)%10)local dH=dx;if by==0 then dH=dE-dF;if dy then dH=1-dH end end;if dy and(by==0 or dx~=0)then local dI=dG;dG=dF;dF=dI end;local dJ=dB*(dH-1)local dK=dJ+dB;local bm=dr+dC+(6-by)*dA;local bn=ds+dD;table.insert(bz,e([[ + + %d + %d - ]],df+dh,dg+di+20,dj))end;if cD and(cq<200000 and not ad or cq and ad)then table.insert(bo,e([[ - - - - ]],df-1,dg-4,dh+2,di+6,df+1,dg-1,dh-4,di))local bn=0;local dk=1;local dl=0;local dm=cq<0;local dn=9;if dm then dn=0 end;local cq=math.abs(cq)while bn<6 do local dp=11;local dq=16;local dr=9;local ds=14;local cd="altsm"if bn>2 then dq=dq+3;dp=dp+2;ds=ds+2;dr=dr-6;cd="altbig"end;if dm then cd=cd.." red"end;local dt=cq/dk%10;local du=d(dt)local dv=d((du+1)%10)local dw=dl;if bn==0 then dw=dt-du;if dm then dw=1-dw end end;if dm and(bn==0 or dl~=0)then local dx=dv;dv=du;du=dx end;local dy=dq*(dw-1)local dz=dy+dq;local bb=df+dr+(6-bn)*dp;local bc=dg+ds;table.insert(bo,e([[ - - %d - %d - - ]],cd,bb,bc+dy,dv,bb,bc+dz,du))bn=bn+1;dk=dk*10;if du==dn then dl=dw else dl=0 end end;table.insert(bo,[[]])end end;function DrawPrograde(bo,velocity,b5,centerX,centerY)if b5>5 and not ad or b5>w then local d0=circleRad;local dA=20;local dB=20;local dC=vec3(velocity)local dD=getRelativePitch(dC)local dE=getRelativeYaw(dC)local dF=-dE/dB*d0;local dG=dD/dA*d0;local bb=centerX+dF;local bc=centerY+dG;local a3=math.sqrt(dF^2+dG^2)if a3',bb,bc)else local cX=math.atan(dG,dF)local dH=centerX+d0*math.cos(cX)local dI=centerY+d0*math.sin(cX)bo[#bo+1]=e('',dH,dI)end;dD=getRelativePitch(-dC)dE=getRelativeYaw(-dC)dF=-dE/dB*d0;dG=dD/dA*d0;bb=centerX+dF;bc=centerY+dG;a3=math.sqrt(dF^2+dG^2)if not ad then if a3',bb,bc)else local cX=math.atan(dG,dF)local dH=centerX+d0*math.cos(cX)local dI=centerY+d0*math.sin(cX)bo[#bo+1]=e('',dH,dI)end end end end;function DrawWarnings(bo)bo[#bo+1]=e([[DU Hud Version: %.3f]],ConvertResolutionX(1900),ConvertResolutionY(1070),VERSION_NUMBER)bo[#bo+1]=[[]]if unit.isMouseControlActivated()==1 then bo[#bo+1]=e([[ - Warning: Invalid Control Scheme Detected]],ConvertResolutionX(960),ConvertResolutionY(550))bo[#bo+1]=e([[ - Keyboard Scheme must be selected]],ConvertResolutionX(960),ConvertResolutionY(600))bo[#bo+1]=e([[ - Set your preferred scheme in Lua Parameters instead]],ConvertResolutionX(960),ConvertResolutionY(650))end;local dJ=ConvertResolutionX(960)local dK=ConvertResolutionY(860)local dL=ConvertResolutionY(880)local dM=ConvertResolutionY(900)local dN=ConvertResolutionY(960)local dO=ConvertResolutionY(200)local dP=ConvertResolutionY(150)local dQ=ConvertResolutionY(960)if o()==1 and not RemoteHud then dK=ConvertResolutionY(135)dL=ConvertResolutionY(155)dM=ConvertResolutionY(175)dO=ConvertResolutionY(115)dP=ConvertResolutionY(95)end;if BrakeIsOn then bo[#bo+1]=e([[Brake Engaged]],dJ,dK)end;if ad and RateOfChangebrakeLandingRate+5 then bo[#bo+1]=e([[** STALL WARNING **]],dJ,dO+50)end;if ah then bo[#bo+1]=e([[Gyro Enabled]],dJ,dQ)end;if GearExtended then if A then bo[#bo+1]=e([[Gear Extended]],dJ,dL)else bo[#bo+1]=e([[Landed (G: Takeoff)]],dJ,dL)end;bo[#bo+1]=e([[Hover Height: %s]],dJ,dM,getDistanceDisplayString(Nav:getTargetGroundAltitude()))end;if O then bo[#bo+1]=e([[ROCKET BOOST ENABLED]],dJ,dN+20)end;if antigrav and not ExternalAGG and antigrav.getState()==1 and AntigravTargetAltitude~=nil then if math.abs(ae-antigrav.getBaseAltitude())<501 then bo[#bo+1]=e([[AGG On - Target Altitude: %d Singluarity Altitude: %d]],dJ,dO+20,d(AntigravTargetAltitude),d(antigrav.getBaseAltitude()))else bo[#bo+1]=e([[AGG On - Target Altitude: %d Singluarity Altitude: %d]],dJ,dO+20,d(AntigravTargetAltitude),d(antigrav.getBaseAltitude()))end elseif Autopilot and AutopilotTargetName~="None"then bo[#bo+1]=e([[Autopilot %s]],dJ,dO+20,AutopilotStatus)elseif LockPitch~=nil then bo[#bo+1]=e([[LockedPitch: %d]],dJ,dO+20,d(LockPitch))elseif I then bo[#bo+1]=e([[Follow Mode Engaged]],dJ,dO+20)elseif Reentry then bo[#bo+1]=e([[Parachute Re-entry in Progress]],dJ,dO+20)end;if AltitudeHold then if AutoTakeoff then bo[#bo+1]=e([[Ascent to %s]],dJ,dO,getDistanceDisplayString(HoldAltitude))if BrakeIsOn then bo[#bo+1]=e([[Throttle Up and Disengage Brake For Takeoff]],dJ,dO+50)end else bo[#bo+1]=e([[Altitude Hold: %s]],dJ,dO,getDistanceDisplayString2(HoldAltitude))end end;if BrakeLanding then if StrongBrakes then bo[#bo+1]=e([[Brake-Landing]],dJ,dO)else bo[#bo+1]=e([[Coast-Landing]],dJ,dO)end end;if ProgradeIsOn then bo[#bo+1]=e([[Prograde Alignment]],dJ,dO)end;if RetrogradeIsOn then bo[#bo+1]=e([[Retrograde Alignment]],dJ,dO)end;if TurnBurn then bo[#bo+1]=e([[Turn & Burn Braking]],dJ,dP)end;if VectorToTarget then bo[#bo+1]=e([[%s]],dJ,dO+30,VectorStatus)end;bo[#bo+1]=""end;function DisplayOrbitScreen(bo)if orbit~=nil and j()<0.2 and planet~=nil and orbit.apoapsis~=nil and orbit.periapsis~=nil and orbit.period~=nil and orbit.apoapsis.speed>5 and DisplayOrbit then local dR=75;local dS=0;local dT=250;local dU=4;dS=dS+dU;local dV=15;local bb=dR+dT+dR/2+dU;local bc=dS+dT/2+5+dU;local dW,dX,dY,dZ;dW=dT/4;dZ=0;bo[#bo+1]=[[]]bo[#bo+1]=e('',dT+dR*2,dT+dS,dU,dU)if orbit.periapsis~=nil and orbit.apoapsis~=nil then dY=(orbit.apoapsis.altitude+orbit.periapsis.altitude+planet.radius*2)/(dW*2)dX=(planet.radius+orbit.periapsis.altitude+(orbit.apoapsis.altitude-orbit.periapsis.altitude)/2)/dY*(1-orbit.eccentricity)dZ=dW-orbit.periapsis.altitude/dY-planet.radius/dY;local d_=""if orbit.periapsis.altitude<=0 then d_='redout'end;bo[#bo+1]=e([[]],d_,dR+dT/2+dZ+dU,dS+dT/2+dU,dW,dX)bo[#bo+1]=e('',dR+dT/2+dU,dS+dT/2+dU,planet.radius/dY)end;if orbit.apoapsis~=nil and orbit.apoapsis.speed1 then bo[#bo+1]=e([[]],bb-35,bc-5,dR+dT/2+dW+dZ,bc-5)bo[#bo+1]=e([[Apoapsis]],bb,bc)bc=bc+dV;bo[#bo+1]=e([[%s]],bb,bc,getDistanceDisplayString(orbit.apoapsis.altitude))bc=bc+dV;bo[#bo+1]=e([[%s]],bb,bc,FormatTimeString(orbit.timeToApoapsis))bc=bc+dV;bo[#bo+1]=e([[%s]],bb,bc,getSpeedDisplayString(orbit.apoapsis.speed))end;bc=dS+dT/2+5+dU;bb=dR-dR/2+10+dU;if orbit.periapsis~=nil and orbit.periapsis.speed1 then bo[#bo+1]=e([[]],bb+35,bc-5,dR+dT/2-dW+dZ,bc-5)bo[#bo+1]=e([[Periapsis]],bb,bc)bc=bc+dV;bo[#bo+1]=e([[%s]],bb,bc,getDistanceDisplayString(orbit.periapsis.altitude))bc=bc+dV;bo[#bo+1]=e([[%s]],bb,bc,FormatTimeString(orbit.timeToPeriapsis))bc=bc+dV;bo[#bo+1]=e([[%s]],bb,bc,getSpeedDisplayString(orbit.periapsis.speed))end;bo[#bo+1]=e([[%s]],dR+dT/2+dU,20+dU,planet.name)if orbit.period~=nil and orbit.periapsis~=nil and orbit.apoapsis~=nil and orbit.apoapsis.speed>1 then local e0=orbit.timeToApoapsis/orbit.period*2*math.pi;local e1=dW*math.cos(e0)local e2=dX*math.sin(e0)bo[#bo+1]=e('',dR+dT/2+e1+dZ+dU,dS+dT/2+e2+dU)end;bo[#bo+1]=[[]]end end;function Atlas()return{[0]={[1]={GM=6930729684,bodyId=1,center={x=17465536.000,y=22665536.000,z=-34464.000},name='Madis',planetarySystemId=0,radius=44300,atmos=true,gravity=0.36},[2]={GM=157470826617,bodyId=2,center={x=-8.000,y=-8.000,z=-126303.000},name='Alioth',planetarySystemId=0,radius=126068,atmos=true,gravity=1.01},[3]={GM=11776905000,bodyId=3,center={x=29165536.000,y=10865536.000,z=65536.000},name='Thades',planetarySystemId=0,radius=49000,atmos=true,gravity=0.50},[4]={GM=14893847582,bodyId=4,center={x=-13234464.000,y=55765536.000,z=465536.000},name='Talemai',planetarySystemId=0,radius=57450,atmos=true,gravity=0.46},[5]={GM=16951680000,bodyId=5,center={x=-43534464.000,y=22565536.000,z=-48934464.000},name='Feli',planetarySystemId=0,radius=60000,atmos=true,gravity=0.48},[6]={GM=10502547741,bodyId=6,center={x=52765536.000,y=27165538.000,z=52065535.000},name='Sicari',planetarySystemId=0,radius=51100,atmos=true,gravity=0.41},[7]={GM=13033380591,bodyId=7,center={x=58665538.000,y=29665535.000,z=58165535.000},name='Sinnen',planetarySystemId=0,radius=54950,atmos=true,gravity=0.44},[8]={GM=18477723600,bodyId=8,center={x=80865538.000,y=54665536.000,z=-934463.940},name='Teoma',planetarySystemId=0,radius=62000,atmos=true,gravity=0.49},[9]={GM=18606274330,bodyId=9,center={x=-94134462.000,y=12765534.000,z=-3634464.000},name='Jago',planetarySystemId=0,radius=61590,atmos=true,gravity=0.50},[10]={GM=78480000,bodyId=10,center={x=17448118.224,y=22966846.286,z=143078.820},name='Madis Moon 1',planetarySystemId=0,radius=10000,atmos=false,gravity=0.08},[11]={GM=237402000,bodyId=11,center={x=17194626.000,y=22243633.880,z=-214962.810},name='Madis Moon 2',planetarySystemId=0,radius=11000,atmos=false,gravity=0.10},[12]={GM=265046609,bodyId=12,center={x=17520614.000,y=22184730.000,z=-309989.990},name='Madis Moon 3',planetarySystemId=0,radius=15005,atmos=false,gravity=0.12},[21]={GM=2118960000,bodyId=21,center={x=457933.000,y=-1509011.000,z=115524.000},name='Alioth Moon 1',planetarySystemId=0,radius=30000,atmos=false,gravity=0.24},[22]={GM=2165833514,bodyId=22,center={x=-1692694.000,y=729681.000,z=-411464.000},name='Alioth Moon 4',planetarySystemId=0,radius=30330,atmos=false,gravity=0.24},[26]={GM=68234043600,bodyId=26,center={x=-1404835.000,y=562655.000,z=-285074.000},name='Sanctuary',planetarySystemId=0,radius=83400,atmos=true,gravity=1.00},[30]={GM=211564034,bodyId=30,center={x=29214402.000,y=10907080.695,z=433858.200},name='Thades Moon 1',planetarySystemId=0,radius=14002,atmos=false,gravity=0.11},[31]={GM=264870000,bodyId=31,center={x=29404193.000,y=10432768.000,z=19554.131},name='Thades Moon 2',planetarySystemId=0,radius=15000,atmos=false,gravity=0.12},[40]={GM=141264000,bodyId=40,center={x=-13503090.000,y=55594325.000,z=769838.640},name='Talemai Moon 2',planetarySystemId=0,radius=12000,atmos=false,gravity=0.10},[41]={GM=106830900,bodyId=41,center={x=-12800515.000,y=55700259.000,z=325207.840},name='Talemai Moon 3',planetarySystemId=0,radius=11000,atmos=false,gravity=0.09},[42]={GM=264870000,bodyId=42,center={x=-13058408.000,y=55781856.000,z=740177.760},name='Talemai Moon 1',planetarySystemId=0,radius=15000,atmos=false,gravity=0.12},[50]={GM=499917600,bodyId=50,center={x=-43902841.780,y=22261034.700,z=-48862386.000},name='Feli Moon 1',planetarySystemId=0,radius=14000,atmos=false,gravity=0.11},[70]={GM=396912600,bodyId=70,center={x=58969616.000,y=29797945.000,z=57969449.000},name='Sinnen Moon 1',planetarySystemId=0,radius=17000,atmos=false,gravity=0.14},[100]={GM=13975172474,bodyId=100,center={x=98865536.000,y=-13534464.000,z=-934461.990},name='Lacobus',planetarySystemId=0,radius=55650,atmos=true,gravity=0.46},[101]={GM=264870000,bodyId=101,center={x=98905288.170,y=-13950921.100,z=-647589.530},name='Lacobus Moon 3',planetarySystemId=0,radius=15000,atmos=false,gravity=0.12},[102]={GM=444981600,bodyId=102,center={x=99180968.000,y=-13783862.000,z=-926156.400},name='Lacobus Moon 1',planetarySystemId=0,radius=18000,atmos=false,gravity=0.14},[103]={GM=211503600,bodyId=103,center={x=99250052.000,y=-13629215.000,z=-1059341.400},name='Lacobus Moon 2',planetarySystemId=0,radius=14000,atmos=false,gravity=0.11},[110]={GM=9204742375,bodyId=110,center={x=14165536.000,y=-85634465.000,z=-934464.300},name='Symeon',planetarySystemId=0,radius=49050,atmos=true,gravity=0.39},[120]={GM=7135606629,bodyId=120,center={x=2865536.700,y=-99034464.000,z=-934462.020},name='Ion',planetarySystemId=0,radius=44950,atmos=true,gravity=0.36},[121]={GM=106830900,bodyId=121,center={x=2472916.800,y=-99133747.000,z=-1133582.800},name='Ion Moon 1',planetarySystemId=0,radius=11000,atmos=false,gravity=0.09},[122]={GM=176580000,bodyId=122,center={x=2995424.500,y=-99275010.000,z=-1378480.700},name='Ion Moon 2',planetarySystemId=0,radius=15000,atmos=false,gravity=0.12}}}end;atlas=Atlas()for aR,aS in pairs(atlas[0])do if at==nil or aS.center.xau then au=aS.center.x end;if av==nil or aS.center.yaw then aw=aS.center.y end end;GalaxyMapHTML=""local e3=1.1*(au-at)/1920;local e4=1.4*(aw-av)/1080;for aR,aS in pairs(atlas[0])do local bb=960+aS.center.x/e3;local bc=540+aS.center.y/e4;GalaxyMapHTML=GalaxyMapHTML..''if not string.match(aS.name,"Moon")and not string.match(aS.name,"Sanctuary")then GalaxyMapHTML=GalaxyMapHTML..""..aS.name..""end end;local cE=vec3(core.getConstructWorldPos())local bb=960+cE.x/e3;local bc=540+cE.y/e4;GalaxyMapHTML=GalaxyMapHTML..''GalaxyMapHTML=GalaxyMapHTML.."You Are Here"GalaxyMapHTML=GalaxyMapHTML..[[]]MapXRatio=e3;MapYRatio=e4;if screen_2 then screen_2.setHTML(''..GalaxyMapHTML)local cE=vec3(core.getConstructWorldPos())local bb=960+cE.x/e3;local bc=540+cE.y/e4;GalaxyMapHTML=''GalaxyMapHTML=GalaxyMapHTML.."You Are Here"YouAreHere=screen_2.addContent((bb-80)/19.20,(bc-80)/10.80,GalaxyMapHTML)end;function PlanetRef()local function e5(e6)return type(e6)=='number'end;local function e7(e6)return type(tonumber(e6))=='number'end;local function e8(e9)return type(e9)=='table'end;local function ea(eb)return type(eb)=='string'end;local function ec(aS)return e8(aS)and e5(aS.x and aS.y and aS.z)end;local function ed(ee)return e8(ee)and e5(ee.latitude and ee.longitude and ee.altitude and ee.bodyId and ee.systemId)end;local ef=math.pi/180;local eg=180/math.pi;local eh=1e-10;local p=' *([+-]?%d+%.?%d*e?[+-]?%d*)'local ei='::pos{'..p..','..p..','..p..','..p..','..p..'}'local utils=require('cpml.utils')local vec3=require('cpml.vec3')local ej=utils.clamp;local function ek(el,em)if el==0 then return math.abs(em)<1e-09 end;if em==0 then return math.abs(el)<1e-09 end;return math.abs(el-em)=0 then local fi=math.sqrt(fh)local fj=fg+fi;local fk=fg-fi;if fk>0 then return f7,fj,fk elseif fj>0 then return f7,fj,nil end end end;return nil,nil,nil end;function eJ:closestBody(fl)assert(type(fl)=='table','Invalid coordinates.')local fm,f7;local fn=vec3(fl)for _,fo in pairs(self)do local fp=(fo.center-fn):len2()if not f7 or fp=0 and ft or 2*math.pi+ft;eH=math.pi/2-math.acos(fs.z/a3)end;return setmetatable({latitude=eH,longitude=eI,altitude=cq,bodyId=self.bodyId,systemId=self.planetarySystemId},eD)end;function es:convertToWorldCoordinates(eG)local fq=ea(eG)and eF(eG)or eG;if fq.bodyId==0 then return vec3(fq.latitude,fq.longitude,fq.altitude)end;assert(ed(fq),'Argument 1 (mapPosition) is not an instance of "MapPosition".')assert(fq.systemId==self.planetarySystemId,'Argument 1 (mapPosition) has a different planetary system ID.')assert(fq.bodyId==self.bodyId,'Argument 1 (mapPosition) has a different planetary body ID.')local fu=math.cos(fq.latitude)return self.center+(self.radius+fq.altitude)*vec3(fu*math.cos(fq.longitude),fu*math.sin(fq.longitude),math.sin(fq.latitude))end;function es:getAltitude(eB)return(vec3(eB)-self.center):len()-self.radius end;function es:getDistance(eB)return(vec3(eB)-self.center):len()end;function es:getGravity(eB)local fv=self.center-vec3(eB)local fw=fv:len2()return self.GM/fw*fv/math.sqrt(fw)end;return setmetatable(PlanetaryReference,{__call=function(_,...)return eR(...)end})end;function Keplers()local vec3=require('cpml.vec3')local PlanetRef=PlanetRef()local function ea(eb)return type(eb)=='string'end;local function e8(e9)return type(e9)=='table'end;local function ek(el,em)if el==0 then return math.abs(em)<1e-09 end;if em==0 then return math.abs(el)<1e-09 end;return math.abs(el-em)0 then fN=fM;fO=fN+fH/2 end;if fO>fH then fO=fO-fH end end;return{periapsis={position=fE,speed=fG/fC,circularOrbitSpeed=math.sqrt(fz/fC),altitude=fC-self.body.radius},apoapsis=fF and{position=fF,speed=fG/fD,circularOrbitSpeed=math.sqrt(fz/fD),altitude=fD-self.body.radius},currentVelocity=aS,currentPosition=cE,eccentricity=fB,period=fH,eccentricAnomaly=fJ,meanAnomaly=fL,timeToPeriapsis=fN,timeToApoapsis=fO}end;local function fP(fQ)local fo=PlanetRef.BodyParameters(fQ.planetarySystemId,fQ.bodyId,fQ.radius,fQ.center,fQ.GM)return setmetatable({body=fo},Kepler)end;return setmetatable(Kepler,{__call=function(_,...)return fP(...)end})end;function Kinematics()local Kinematic={}local fR=30000000/3600;local fS=fR*fR;local fT=100;local function fU(aS)return 1/math.sqrt(1-aS*aS/fS)end;function Kinematic.computeAccelerationTime(fV,fW,fX)local fY=fR*math.asin(fV/fR)return(fR*math.asin(fX/fR)-fY)/fW end;function Kinematic.computeDistanceAndTime(fV,fX,fZ,f_,g0,g1)g0=g0 or 0;g1=g1 or 0;local g2=fV<=fX;local g3=f_*(g2 and 1 or-1)/fZ;local g4=-g1/fZ;local g5=g3+g4;if g2 and g5<=0 or not g2 and g5>=0 then return-1,-1 end;local g6,g7=0,0;if g3~=0 and g0>0 then local fY=math.asin(fV/fR)local g8=math.pi*(g3/2+g4)local g9=g3*g0;local ga=fR*math.pi;local aS=function(e9)local bK=(g8*e9-g9*math.sin(math.pi*e9/2/g0)+ga*fY)/ga;local gb=math.tan(bK)return fR*gb/math.sqrt(gb*gb+1)end;local gc=g2 and function(eb)return eb>=fX end or function(eb)return eb<=fX end;g7=2*g0;if gc(aS(g7))then local gd=0;while math.abs(g7-gd)>0.5 do local e9=(g7+gd)/2;if gc(aS(e9))then g7=e9 else gd=e9 end end end;local ge=fV;local gf=g7/fT;for gg=1,fT do local b5=aS(gg*gf)g6=g6+(b5+ge)*gf/2;ge=b5 end;if g7<2*g0 then return g6,g7 end;fV=ge end;local fY=fR*math.asin(fV/fR)local aU=(fR*math.asin(fX/fR)-fY)/g5;local gh=fS*math.cos(fY/fR)/g5;local a3=gh-fS*math.cos((g5*aU+fY)/fR)/g5;return a3+g6,aU+g7 end;function Kinematic.computeTravelTime(fV,fW,a3)if a3==0 then return 0 end;if fW>0 then local fY=fR*math.asin(fV/fR)local gh=fS*math.cos(fY/fR)/fW;return(fR*math.acos(fW*(gh-a3)/fS)-fY)/fW end;assert(fV>0,'Acceleration and initial speed are both zero.')return a3/fV end;function Kinematic.lorentz(aS)return fU(aS)end;return Kinematic end;PlanetaryReference=PlanetRef()galaxyReference=PlanetaryReference(Atlas())Kinematic=Kinematics()Kep=Keplers()function getDistanceDisplayString(a3)local gi=a3>100000;local aT=""if gi then aT=round(a3/1000/200,1).." SU"elseif a3<1000 then aT=round(a3,1).." M"else aT=round(a3/1000,1).." KM"end;return aT end;function getDistanceDisplayString2(a3)local gi=a3>100000;local aT=""if gi then aT=round(a3/1000/200,2).." SU"elseif a3<1000 then aT=round(a3,2).." M"else aT=round(a3/1000,2).." KM"end;return aT end;function getSpeedDisplayString(b5)return d(round(b5*3.6,0)+0.5).." km/h"end;function FormatTimeString(gj)local gk=0;local gl=0;local gm=0;if gj<60 then gj=d(gj)elseif gj<3600 then gk=d(gj/60)gj=d(gj%60)elseif gj<86400 then gl=d(gj/3600)gk=d(gj%3600/60)else gm=d(gj/86400)gl=d(gj%86400/60)end;if gm>0 then return gm.."d "..gl.."h "elseif gl>0 then return gl.."h "..gk.."m "elseif gk>0 then return gk.."m "..gj.."s"elseif gj>0 then return gj.."s"else return"0s"end end;function getMagnitudeInDirection(cf,f4)cf=vec3(cf)f4=vec3(f4):normalize()local aT=cf*f4;return aT.x+aT.y+aT.z end;function UpdateAutopilotTarget()if AutopilotTargetIndex==0 then AutopilotTargetName="None"V=nil;return true end;local gn=AtlasOrdered[AutopilotTargetIndex].index;local go=atlas[0][gn]if go.center then AutopilotTargetName=go.name;V=galaxyReference[0][gn]if CustomTarget~=nil then if j()==0 then if system.updateData(widgetMaxBrakeTimeText,widgetMaxBrakeTime)~=1 then system.addDataToWidget(widgetMaxBrakeTimeText,widgetMaxBrakeTime)end;if system.updateData(widgetMaxBrakeDistanceText,widgetMaxBrakeDistance)~=1 then system.addDataToWidget(widgetMaxBrakeDistanceText,widgetMaxBrakeDistance)end;if system.updateData(widgetCurBrakeTimeText,widgetCurBrakeTime)~=1 then system.addDataToWidget(widgetCurBrakeTimeText,widgetCurBrakeTime)end;if system.updateData(widgetCurBrakeDistanceText,widgetCurBrakeDistance)~=1 then system.addDataToWidget(widgetCurBrakeDistanceText,widgetCurBrakeDistance)end;if system.updateData(widgetTrajectoryAltitudeText,widgetTrajectoryAltitude)~=1 then system.addDataToWidget(widgetTrajectoryAltitudeText,widgetTrajectoryAltitude)end end;if system.updateData(widgetMaxMassText,widgetMaxMass)~=1 then system.addDataToWidget(widgetMaxMassText,widgetMaxMass)end;if system.updateData(widgetTravelTimeText,widgetTravelTime)~=1 then system.addDataToWidget(widgetTravelTimeText,widgetTravelTime)end end;CustomTarget=nil else CustomTarget=go;for _,aS in pairs(galaxyReference[0])do if aS.name==CustomTarget.planetname then V=aS;AutopilotTargetName=CustomTarget.name;break end end;if system.updateData(widgetMaxMassText,widgetMaxMass)~=1 then system.addDataToWidget(widgetMaxMassText,widgetMaxMass)end;if system.updateData(widgetTravelTimeText,widgetTravelTime)~=1 then system.addDataToWidget(widgetTravelTimeText,widgetTravelTime)end end;AutopilotTargetCoords=vec3(V.center)_,AutopilotEndSpeed=Kep(V):escapeAndOrbitalSpeed(AutopilotTargetOrbit)AutopilotPlanetGravity=0;AutopilotAccelerating=false;AutopilotBraking=false;AutopilotCruising=false;Autopilot=false;AutopilotRealigned=false;AutopilotStatus="Aligning"return true end;function IncrementAutopilotTargetIndex()AutopilotTargetIndex=AutopilotTargetIndex+1;if AutopilotTargetIndex>#AtlasOrdered then AutopilotTargetIndex=0 end;UpdateAutopilotTarget()end;function DecrementAutopilotTargetIndex()AutopilotTargetIndex=AutopilotTargetIndex-1;if AutopilotTargetIndex<0 then AutopilotTargetIndex=#AtlasOrdered end;UpdateAutopilotTarget()end;function GetAutopilotMaxMass()local gp=LastMaxBrakeInAtmo/V:getGravity(V.center+vec3(0,0,1)*V.radius):len()return gp end;function GetAutopilotTravelTime()if not Autopilot then if CustomTarget==nil or CustomTarget.planetname~=planet.name then AutopilotDistance=(V.center-vec3(core.getConstructWorldPos())):len()else AutopilotDistance=(CustomTarget.position-vec3(core.getConstructWorldPos())):len()end end;local velocity=core.getWorldVelocity()local b5=vec3(velocity):len()local gq,gr=Kinematic.computeDistanceAndTime(vec3(velocity):len(),MaxGameVelocity,n(),Nav:maxForceForward(),warmup,0)local P,Q;if not TurnBurn then P,Q=GetAutopilotBrakeDistanceAndTime(MaxGameVelocity)else P,Q=GetAutopilotTBBrakeDistanceAndTime(MaxGameVelocity)end;local _,gs;if not TurnBurn then _,gs=GetAutopilotBrakeDistanceAndTime(b5)else _,gs=GetAutopilotTBBrakeDistanceAndTime(b5)end;local gt=0;local gu=0;if AutopilotCruising or not Autopilot and b5>5 then gu=Kinematic.computeTravelTime(b5,0,AutopilotDistance)elseif P+gq0 then return Kinematic.computeDistanceAndTime(b5,AutopilotEndSpeed,n(),0,0,LastMaxBrakeInAtmo-AutopilotPlanetGravity*n())else return 0,0 end end end;function GetAutopilotTBBrakeDistanceAndTime(b5)RefreshLastMaxBrake()return Kinematic.computeDistanceAndTime(b5,AutopilotEndSpeed,n(),Nav:maxForceForward(),warmup,LastMaxBrake-AutopilotPlanetGravity*n())end;function hoverDetectGround()local gw=-1;local gx=-1;if vBooster then gw=vBooster.distance()end;if hover then gx=hover.distance()end;if gw~=-1 and gx~=-1 then if gwProfileTimeMax then ProfileTimeMax=gD end;if gD0 then local gN=gK:find('identifiedConstructs":%[%]')if gN==nil and perisPanelID==nil then a6=1;ToggleRadarPanel()end;if gN~=nil and perisPanelID~=nil then ToggleRadarPanel()end;if radarPanelID==nil then ToggleRadarPanel()end;a4=e([[Radar: %i contacts]],gL,gM,#gJ)local gO={}for aR,aS in pairs(gJ)do if radar_1.hasMatchingTransponder(aS)==1 then gO[#gO+1]=aS end end;if#gO>0 then local bc=ConvertResolutionY(15)local bb=ConvertResolutionX(1370)a4=e([[%sFriendlies In Range]],a4,bb,bc)for aR,aS in pairs(gO)do bc=bc+20;a4=e([[%s%s]],a4,bb,bc,radar_1.getConstructName(aS))end end else local gP;gP=gK:find('worksInEnvironment":false')if gP then a4=e([[ - Radar: Jammed]],gL,gM)else a4=e([[ - Radar: No Contacts]],gL,gM)end;if radarPanelID~=nil then a6=0;ToggleRadarPanel()end end end end;Animating=false;Animated=false;AddLocationsToAtlas()UpdateAutopilotTarget()collectgarbage("collect")unit.setTimer("apTick",apTickRate)unit.setTimer("oneSecond",1)unit.setTimer("tenthSecond",1/10)if UseSatNav then unit.setTimer("fiveSecond",5)end end)end;function script.onStop()_autoconf.hideCategoryPanels()if antigrav~=nil and not ExternalAGG then antigrav.hide()end;if warpdrive~=nil then warpdrive.hide()end;core.hide()Nav.control.switchOffHeadlights()local gQ=j()if door and(gQ>0 or gQ==0 and ae<10000)then for _,aS in pairs(door)do aS.toggle()end end;if switch then for _,aS in pairs(switch)do aS.toggle()end end;if forcefield and(gQ>0 or gQ==0 and ae<10000)then for _,aS in pairs(forcefield)do aS.toggle()end end;if dbHud_1 then if not Y then for aR,aS in pairs(b)do dbHud_1.setStringValue(aS,g(_G[aS]))end;for aR,aS in pairs(a)do dbHud_1.setStringValue(aS,g(_G[aS]))end;c("Saved Variables to Datacore")end end;if button then button.activate()end end;function script.onTick(gR)if gR=="tenthSecond"then if AutopilotTargetName~="None"then if panelInterplanetary==nil then SetupInterplanetaryPanel()end;if AutopilotTargetName~=nil then local gS=CustomTarget~=nil;planetMaxMass=GetAutopilotMaxMass()system.updateData(interplanetaryHeaderText,'{"label": "Target", "value": "'..AutopilotTargetName..'", "unit":""}')travelTime=GetAutopilotTravelTime()if gS then a3=(vec3(core.getConstructWorldPos())-CustomTarget.position):len()else a3=(AutopilotTargetCoords-vec3(core.getConstructWorldPos())):len()end;if not TurnBurn then P,Q=GetAutopilotBrakeDistanceAndTime(velMag)R,S=GetAutopilotBrakeDistanceAndTime(MaxGameVelocity)else P,Q=GetAutopilotTBBrakeDistanceAndTime(velMag)R,S=GetAutopilotTBBrakeDistanceAndTime(MaxGameVelocity)end;system.updateData(widgetDistanceText,'{"label": "distance", "value": "'..getDistanceDisplayString(a3)..'", "unit":""}')system.updateData(widgetTravelTimeText,'{"label": "Travel Time", "value": "'..FormatTimeString(travelTime)..'", "unit":""}')system.updateData(widgetCurBrakeDistanceText,'{"label": "Cur Brake distance", "value": "'..getDistanceDisplayString(P)..'", "unit":""}')system.updateData(widgetCurBrakeTimeText,'{"label": "Cur Brake Time", "value": "'..FormatTimeString(Q)..'", "unit":""}')system.updateData(widgetMaxBrakeDistanceText,'{"label": "Max Brake distance", "value": "'..getDistanceDisplayString(R)..'", "unit":""}')system.updateData(widgetMaxBrakeTimeText,'{"label": "Max Brake Time", "value": "'..FormatTimeString(S)..'", "unit":""}')system.updateData(widgetMaxMassText,'{"label": "Maximum Mass", "value": "'..e("%.2f tons",planetMaxMass/1000)..'", "unit":""}')if j()>0 and not WasInAtmo then system.removeDataFromWidget(widgetMaxBrakeTimeText,widgetMaxBrakeTime)system.removeDataFromWidget(widgetMaxBrakeDistanceText,widgetMaxBrakeDistance)system.removeDataFromWidget(widgetCurBrakeTimeText,widgetCurBrakeTime)system.removeDataFromWidget(widgetCurBrakeDistanceText,widgetCurBrakeDistance)system.removeDataFromWidget(widgetTrajectoryAltitudeText,widgetTrajectoryAltitude)WasInAtmo=true end;if j()==0 and WasInAtmo then if system.updateData(widgetMaxBrakeTimeText,widgetMaxBrakeTime)==1 then system.addDataToWidget(widgetMaxBrakeTimeText,widgetMaxBrakeTime)end;if system.updateData(widgetMaxBrakeDistanceText,widgetMaxBrakeDistance)==1 then system.addDataToWidget(widgetMaxBrakeDistanceText,widgetMaxBrakeDistance)end;if system.updateData(widgetCurBrakeTimeText,widgetCurBrakeTime)==1 then system.addDataToWidget(widgetCurBrakeTimeText,widgetCurBrakeTime)end;if system.updateData(widgetCurBrakeDistanceText,widgetCurBrakeDistance)==1 then system.addDataToWidget(widgetCurBrakeDistanceText,widgetCurBrakeDistance)end;if system.updateData(widgetTrajectoryAltitudeText,widgetTrajectoryAltitude)==1 then system.addDataToWidget(widgetTrajectoryAltitudeText,widgetTrajectoryAltitude)end;WasInAtmo=false end end else HideInterplanetaryPanel()end;if warpdrive~=nil then if f(warpdrive.getData()).destination~="Unknown"and f(warpdrive.getData()).distance>400000 then warpdrive.show()showWarpWidget=true else warpdrive.hide()showWarpWidget=false end end elseif gR=="oneSecond"then ab=false;RefreshLastMaxBrake(nil,true)updateDistance()updateRadar()updateWeapons()local bo={}local cp=GetFlightStyle()DrawOdometer(bo,W,TotalDistanceTravelled,cp,X)CheckDamage(bo)a5=table.concat(bo,"")collectgarbage("collect")elseif gR=="fiveSecond"then ac=dbHud_1.getStringValue("SPBAutopilotTargetName")if ac~=nil and ac~=""and ac~="SatNavNotChanged"then local aT=json.decode(dbHud_1.getStringValue("SavedLocations"))if aT~=nil then _G["SavedLocations"]=aT;local bn=-1;local bm;for aR,aS in pairs(SavedLocations)do if aS.name and aS.name=="SatNav Location"then bn=aR;break end end;if bn~=-1 then bm=SavedLocations[bn]bn=-1;for aR,aS in pairs(atlas[0])do if aS.name and aS.name=="SatNav Location"then bn=aR;break end end;if bn>-1 then atlas[0][bn]=bm end;UpdateAtlasLocationsList()K=bm.name.." position updated"end end;for i=1,#AtlasOrdered do if AtlasOrdered[i].name==ac then AutopilotTargetIndex=i;system.print("Index = "..AutopilotTargetIndex.." "..AtlasOrdered[i].name)UpdateAutopilotTarget()dbHud_1.setStringValue("SPBAutopilotTargetName","SatNavNotChanged")break end end end elseif gR=="msgTick"then local bo={}DisplayMessage(bo,"empty")K="empty"unit.stopTimer("msgTick")a2=3 elseif gR=="animateTick"then Animated=true;Animating=false;a0=0;a1=0;unit.stopTimer("animateTick")elseif gR=="apTick"then local o=o;RateOfChange=vec3(core.getConstructWorldOrientationForward()):dot(vec3(core.getWorldVelocity()):normalize())ad=j()>0;D=0;H=0;C=0;LastApsDiff=-1;velocity=vec3(core.getWorldVelocity())velMag=vec3(velocity):len()sys=galaxyReference[0]planet=sys:closestBody(core.getConstructWorldPos())kepPlanet=Kep(planet)orbit=kepPlanet:orbitalParameters(core.getConstructWorldPos(),velocity)aa=hoverDetectGround()local gT=system.getMouseDeltaX()local gU=system.getMouseDeltaY()TargetGroundAltitude=Nav:getTargetGroundAltitude()local gV=velMag>8334;if not gV and LastIsWarping then if not BrakeIsOn then BrakeToggle()end;if Autopilot then ToggleAutopilot()end end;LastIsWarping=gV;if ad and j()>0.09 then if not ai then if velMag>AtmoSpeedLimit/3.6 then BrakeIsOn=true;ai=true end else if velMag]],ResolutionX,ResolutionY)if K~="empty"then DisplayMessage(bo,K)end;if o()==0 and userControlScheme=="virtual joystick"then DrawDeadZone(bo)end;if o()==1 and screen_1 and screen_1.getMouseY()~=-1 then a0=screen_1.getMouseX()*ResolutionX;a1=screen_1.getMouseY()*ResolutionY;SetButtonContains()DrawButtons(bo)if screen_1.getMouseState()==1 then CheckButtons()end;bo[#bo+1]=e([[]],s,t,a0,a1)elseif system.isViewLocked()==0 then if o()==1 and J then if not Animating then a0=a0+gT;a1=a1+gU end;SetButtonContains()DrawButtons(bo)if not Animating and not Animated then local gW=table.concat(bo,"")bo={}bo[#bo+1]=e("",ResolutionX,ResolutionY)bo[#bo+1]=GalaxyMapHTML;bo[#bo+1]=gW;bo[#bo+1]=""Animating=true;bo[#bo+1]=[[]]unit.setTimer("animateTick",0.5)local content=table.concat(bo,"")system.setScreen(content)elseif Animated then local gW=table.concat(bo,"")bo={}bo[#bo+1]=e("",ResolutionX,ResolutionY)bo[#bo+1]=GalaxyMapHTML;bo[#bo+1]=gW;bo[#bo+1]=""end;if not Animating then bo[#bo+1]=e([[]],s,t,a0,a1)end else CheckButtons()a0=0;a1=0 end else a0=a0+gT;a1=a1+gU;a3=math.sqrt(a0*a0+a1*a1)if not J and o()==0 then if userControlScheme=="virtual joystick"then if a0>0 and a0>DeadZone then D=D-(a0-DeadZone)*MouseXSensitivity elseif a0<0 and a00 and a1>DeadZone then C=C-(a1-DeadZone)*MouseYSensitivity elseif a1<0 and a1DeadZone then DrawCursorLine(bo)end else SetButtonContains()DrawButtons(bo)end;bo[#bo+1]=e([[]],s,t,a0,a1)end;bo[#bo+1]=[[]]content=table.concat(bo,"")if not DidLogOutput then system.logInfo(LastContent)DidLogOutput=true end;if ProgradeIsOn then if velMag>w then local gX=AlignToWorldVector(vec3(velocity),0.01)if a7 then autoRoll=true;if gX then ProgradeIsOn=false;x=true;BeginReentry()a7=false;a9=true;autoRoll=autoRollPreference end end end end;if RetrogradeIsOn then if ad then RetrogradeIsOn=false elseif velMag>w then AlignToWorldVector(-vec3(velocity))end end;if not ProgradeIsOn and a7 then if j()==0 then x=true;BeginReentry()a7=false;a9=true else a7=false;ToggleAutopilot()end end;if a9 and aeReentrySpeed-100 then ToggleAutopilot()a9=false end;if Autopilot and j()==0 then local P,Q;if not TurnBurn then P,Q=GetAutopilotBrakeDistanceAndTime(velMag)else P,Q=GetAutopilotTBBrakeDistanceAndTime(velMag)end;P=P;Q=Q;local gY=AutopilotTargetCoords;if orbit.apoapsis==nil and velMag>300 and AutopilotAccelerating then local gZ=(vec3(AutopilotTargetCoords)-vec3(core.getConstructWorldPos())):normalize()-vec3(velocity):normalize()local g_=getMagnitudeInDirection(gZ,AutopilotShipUp)local h0=getMagnitudeInDirection(gZ,AutopilotShipRight)local h1=-h0*AutopilotDistance*velMag*TrajectoryAlignmentStrength;local h2=-g_*AutopilotDistance*velMag*TrajectoryAlignmentStrength;gY=AutopilotTargetCoords+-h1*vec3(AutopilotShipRight)+-h2*vec3(AutopilotShipUp)end;AutopilotDistance=(vec3(gY)-vec3(core.getConstructWorldPos())):len()local h3=(AutopilotTargetCoords-vec3(core.getConstructWorldPos())):len()system.updateData(widgetDistanceText,'{"label": "distance", "value": "'..getDistanceDisplayString(h3)..'", "unit":""}')local h4=true;local h5=(V.center-(vec3(core.getConstructWorldPos())+vec3(velocity):normalize()*AutopilotDistance)):len()-V.radius;system.updateData(widgetTrajectoryAltitudeText,'{"label": "Projected Altitude", "value": "'..getDistanceDisplayString(h5)..'", "unit":""}')if not AutopilotCruising and not AutopilotBraking then h4=AlignToWorldVector((gY-vec3(core.getConstructWorldPos())):normalize())elseif TurnBurn then h4=AlignToWorldVector(-vec3(velocity):normalize())end;if AutopilotAccelerating then if not h4 or BrakeIsOn then AutopilotStatus="Adjusting Trajectory"else AutopilotStatus="Accelerating"end;if vec3(core.getConstructWorldOrientationForward()):dot(velocity)<0 and velMag>300 then BrakeIsOn=true;Nav.axisCommandManager:setThrottleCommand(axisCommandId.longitudinal,0)u=false elseif not u then BrakeIsOn=false;Nav.axisCommandManager:setThrottleCommand(axisCommandId.longitudinal,AutopilotInterplanetaryThrottle)u=true end;if vec3(core.getVelocity()):len()>=MaxGameVelocity and math.abs(h5-AutopilotTargetOrbit)<1000 or unit.getThrottle()==0 and u then AutopilotAccelerating=false;AutopilotStatus="Cruising"AutopilotCruising=true;Nav.axisCommandManager:setThrottleCommand(axisCommandId.longitudinal,0)u=false end;if AutopilotDistance<=P then AutopilotAccelerating=false;AutopilotStatus="Braking"AutopilotBraking=true;Nav.axisCommandManager:setThrottleCommand(axisCommandId.longitudinal,0)u=false end elseif AutopilotBraking then BrakeIsOn=true;G=1;if TurnBurn then Nav.axisCommandManager:setThrottleCommand(axisCommandId.longitudinal,100)end;if orbit.periapsis~=nil and orbit.eccentricity<1 then AutopilotStatus="Circularizing"if orbit.eccentricity>L or orbit.apoapsis.altitude0 then AutopilotAccelerating=true;AutopilotStatus="Accelerating"AutopilotCruising=false end else if h4 then if not AutopilotRealigned then AutopilotTargetCoords=vec3(V.center)+(AutopilotTargetOrbit+V.radius)*vec3(core.getConstructWorldOrientationRight())AutopilotRealigned=true;AutopilotShipUp=core.getConstructWorldOrientationUp()AutopilotShipRight=core.getConstructWorldOrientationRight()elseif h4 then AutopilotAccelerating=true;AutopilotStatus="Accelerating"if not u then Nav.axisCommandManager:setThrottleCommand(axisCommandId.longitudinal,AutopilotInterplanetaryThrottle)u=true;BrakeIsOn=false end end end end end;if I then autoRoll=true;local h6=0;local cE=vec3(core.getConstructWorldPos())+vec3(unit.getMasterPlayerRelativePosition())local h7=cE-vec3(core.getConstructWorldPos())local h8=vec3(h7):project_on(vec3(core.getConstructWorldOrientationForward())):len()local h9=vec3(h7):project_on(vec3(core.getConstructWorldOrientationRight())):len()local a3=math.sqrt(h8*h8+h9*h9)AlignToWorldVector(h7:normalize())local ha=40;local hb=a3he then if pitchPID==nil then pitchPID=pid.new(2*0.01,0,2*0.1)end;pitchPID:inject(h6-bu)local hf=pitchPID:get()C=hf end end;local cY=vec3(core.getWorldVertical())*-1;if AltitudeHold or BrakeLanding or Reentry or VectorToTarget or LockPitch~=nil then local cD=unit.getClosestPlanetInfluence()>0;local cq=ae;local hg=HoldAltitude-cq;local hh=500+velMag;local h6=(utils.smoothstep(hg,-hh,hh)-0.5)*2*MaxPitch;if not AltitudeHold then h6=0 end;if LockPitch~=nil then if cD then h6=LockPitch else LockPitch=nil end end;autoRoll=true;if Reentry then local hi=ReentrySpeed;if Nav.axisCommandManager:getTargetSpeed(axisCommandId.longitudinal)~=hi then Nav.axisCommandManager:setTargetSpeedCommand(axisCommandId.longitudinal,hi)Nav.axisCommandManager:setTargetSpeedCommand(axisCommandId.vertical,0)Nav.axisCommandManager:setTargetSpeedCommand(axisCommandId.lateral,0)end;if not x then h6=-80;if j()>0.02 then K="PARACHUTE DEPLOYED"Reentry=false;BrakeLanding=true;h6=0;autoRoll=autoRollPreference end elseif Nav.axisCommandManager:getTargetSpeed(axisCommandId.longitudinal)==ReentrySpeed then x=false;Reentry=false;autoRoll=autoRollPreference end end;local hj=C;if velMag>w then AlignToWorldVector(vec3(velocity))end;if VectorToTarget and CustomTarget~=nil and AutopilotTargetIndex>0 then local ch=CustomTarget.position-vec3(core.getConstructWorldPos())AlignToWorldVector(ch)local hk=ch:len()-ch:project_on(cY):len()local b6=LastMaxBrakeInAtmo;local cW=velocity.x*cY.x+velocity.y*cY.y+velocity.z*cY.z;local hl=velocity:len()-math.abs(cW)local hm=vec3(core.getWorldAirFrictionAcceleration())if b6~=nil then P,Q=Kinematic.computeDistanceAndTime(hl,0,n(),0,0,b6+(hm:len()-hm:project_on(cY):len())*n())else P,Q=Kinematic.computeDistanceAndTime(hl,0,n(),0,0,LastMaxBrake+vec3(core.getWorldAirFrictionAcceleration()):len()*n())end;StrongBrakes=planet.gravity*9.80665*n()LastTargetDistance and not AltitudeHold and not AutoTakeoff then BrakeLanding=true;VectorToTarget=false end;LastTargetDistance=hk end;C=hj;local br=vec3(core.getConstructWorldOrientationForward())local bs=vec3(core.getConstructWorldOrientationRight())local bt=vec3(core.getWorldVertical())local gy=-1;local bu=getPitch(bt,br,bs)local he=0.1;if BrakeLanding then h6=0;if Nav.axisCommandManager:getAxisCommandType(0)==1 then Nav.control.cancelCurrentControlMasterMode()end;Nav.axisCommandManager:setTargetGroundAltitude(500)Nav.axisCommandManager:activateGroundEngineAltitudeStabilization(500)local cW=velocity.x*cY.x+velocity.y*cY.y+velocity.z*cY.z;gy=aa;if gy>-1 then if math.abs(h6-bu)50000 then if Nav.axisCommandManager:getAxisCommandType(0)==0 then Nav.control.cancelCurrentControlMasterMode()end;if Nav.axisCommandManager:getTargetSpeed(axisCommandId.longitudinal)~=5000 then Nav.axisCommandManager:setTargetSpeedCommand(axisCommandId.longitudinal,5000)Nav.axisCommandManager:setTargetSpeedCommand(axisCommandId.vertical,0)Nav.axisCommandManager:setTargetSpeedCommand(axisCommandId.lateral,0)end end end;if math.abs(h6-bu)>he then if pitchPID==nil then pitchPID=pid.new(8*0.01,0,8*0.1)end;pitchPID:inject(h6-bu)local hf=pitchPID:get()C=C+hf end end;L=orbit.eccentricity;if antigrav and not ExternalAGG and ae<200000 then if AntigravTargetAltitude==nil then AntigravTargetAltitude=1000 end;if desiredBaseAltitude~=AntigravTargetAltitude then desiredBaseAltitude=AntigravTargetAltitude;antigrav.setBaseAltitude(desiredBaseAltitude)end end end end;function script.onFlush()if antigrav and not ExternalAGG then if antigrav.getState()==0 and antigrav.getBaseAltitude()~=AntigravTargetAltitude then antigrav.setBaseAltitude(AntigravTargetAltitude)end end;local hn=2;pitchSpeedFactor=math.max(pitchSpeedFactor,0.01)yawSpeedFactor=math.max(yawSpeedFactor,0.01)rollSpeedFactor=math.max(rollSpeedFactor,0.01)hn=math.max(hn,0.01)brakeSpeedFactor=math.max(brakeSpeedFactor,0.01)brakeFlatFactor=math.max(brakeFlatFactor,0.01)autoRollFactor=math.max(autoRollFactor,0.01)turnAssistFactor=math.max(turnAssistFactor,0.01)local ho=utils.clamp(B+C+system.getControlDeviceForwardInput(),-1,1)local hp=utils.clamp(E+H+system.getControlDeviceYawInput(),-1,1)local hq=utils.clamp(F+D-system.getControlDeviceLeftRightInput(),-1,1)local hr=G;local hs=vec3(core.getWorldVertical())local ht=vec3(core.getConstructWorldOrientationUp())local hu=vec3(core.getConstructWorldOrientationForward())local hv=vec3(core.getConstructWorldOrientationRight())local hw=vec3(core.getWorldVelocity())local hx=vec3(core.getWorldVelocity()):normalize()local hy=getRoll(hs,hu,hv)local hz=math.abs(hy)local hA=utils.sign(hy)local j=j()local hB=vec3(core.getWorldAngularVelocity())local hC=ho*pitchSpeedFactor*hv+hp*rollSpeedFactor*hu+hq*yawSpeedFactor*ht;if hs:len()>0.01 and j>0.0 or ProgradeIsOn then local hD=1.0;if autoRoll==true and hz>hD and hp==0 then local hE=utils.clamp(0,hz-30,hz+30)if rollPID==nil then rollPID=pid.new(autoRollFactor*0.01,0,autoRollFactor*0.1)end;rollPID:inject(hE-hy)local hF=rollPID:get()hC=hC+hF*hu end end;if hs:len()>0.01 and j>0.0 then local hG=20.0;if turnAssist==true and hz>hG and ho==0 and hq==0 then local hH=turnAssistFactor*0.1;local hI=turnAssistFactor*0.025;local hJ=(hz-hG)/(180-hG)*180;local hK=0;if hJ<90 then hK=hJ/90 elseif hJ<180 then hK=(180-hJ)/90 end;hK=hK*hK;local hL=-hA*hI*(1.0-hK)local hM=hH*hK;hC=hC+hM*hv+hL*ht end end;local hN=1;local hO=0;local hP=1;local hQ=hn*(hC-hB)local hR=vec3(core.getWorldAirFrictionAngularAcceleration())hQ=hQ-hR;Nav:setEngineTorqueCommand('torque',hQ,hN,'airfoil','','',hP)local hS=-hr*(brakeSpeedFactor*hw+brakeFlatFactor*hx)Nav:setEngineForceCommand('brake',hS)local hT=''local hU=vec3()local hV=false;local hW='thrust analog longitudinal'local hX=Nav.axisCommandManager:getAxisCommandType(axisCommandId.longitudinal)if hX==axisCommandType.byThrottle then local hY=Nav.axisCommandManager:composeAxisAccelerationFromThrottle(hW,axisCommandId.longitudinal)Nav:setEngineForceCommand(hW,hY,hN)elseif hX==axisCommandType.byTargetSpeed then local hY=Nav.axisCommandManager:composeAxisAccelerationFromTargetSpeed(axisCommandId.longitudinal)hT=hT..' , '..hW;hU=hU+hY;if Nav.axisCommandManager:getTargetSpeed(axisCommandId.longitudinal)==0 or Nav.axisCommandManager:getCurrentToTargetDeltaSpeed(axisCommandId.longitudinal)<-Nav.axisCommandManager:getTargetSpeedCurrentStep(axisCommandId.longitudinal)*0.5 then hV=true end end;local hZ='thrust analog lateral'local h_=Nav.axisCommandManager:getAxisCommandType(axisCommandId.lateral)if h_==axisCommandType.byThrottle then local i0=Nav.axisCommandManager:composeAxisAccelerationFromThrottle(hZ,axisCommandId.lateral)Nav:setEngineForceCommand(hZ,i0,hN)elseif h_==axisCommandType.byTargetSpeed then local i1=Nav.axisCommandManager:composeAxisAccelerationFromTargetSpeed(axisCommandId.lateral)hT=hT..' , '..hZ;hU=hU+i1 end;local i2='thrust analog vertical'local i3=Nav.axisCommandManager:getAxisCommandType(axisCommandId.vertical)if i3==axisCommandType.byThrottle then local i4=Nav.axisCommandManager:composeAxisAccelerationFromThrottle(i2,axisCommandId.vertical)if Z~=0 or BrakeLanding and BrakeIsOn then Nav:setEngineForceCommand(i2,i4,hN,'airfoil','ground','',hP)else Nav:setEngineForceCommand(i2,vec3(),hN)Nav:setEngineForceCommand('airfoil vertical',i4,hN,'airfoil','','',hP)Nav:setEngineForceCommand('ground vertical',i4,hN,'ground','','',hP)end elseif i3==axisCommandType.byTargetSpeed then if Z==0 then Nav:setEngineForceCommand('hover',vec3(),hN)end;local i5=Nav.axisCommandManager:composeAxisAccelerationFromTargetSpeed(axisCommandId.vertical)hT=hT..' , '..i2;hU=hU+i5 end;if hU:len()>constants.epsilon then if G~=0 or hV or math.abs(hx:dot(hu))<0.95 then hT=hT..', brake'end;Nav:setEngineForceCommand(hT,hU,hO,'','','',hP)end;Nav:setBoosterCommand('rocket_engine')if O and not VanillaRockets then local b5=vec3(core.getVelocity()):len()local i6=0.15;if Nav.axisCommandManager:getAxisCommandType(0)==1 then local i7=Nav.axisCommandManager:getTargetSpeed(axisCommandId.longitudinal)if b5*3.6>i7*(1-i6)and IsRocketOn then IsRocketOn=false;Nav:toggleBoosters()elseif b5*3.6=hd*(1-i6)and IsRocketOn then IsRocketOn=false;Nav:toggleBoosters()elseif b5=hd*(1-i6)and IsRocketOn then IsRocketOn=false;Nav:toggleBoosters()elseif b50 or aew then K="WARNING: Insufficient Brakes - Attempting landing anyway"end;Reentry=false;AutoTakeoff=false;AltitudeHold=false;BrakeLanding=true;autoRoll=true;GearExtended=false else BrakeIsOn=true;Nav.control.extendLandingGears()Nav.axisCommandManager:setTargetGroundAltitude(LandingGearGroundHeight)end else Nav.control.retractLandingGears()Nav.axisCommandManager:setTargetGroundAltitude(TargetHoverHeight)end elseif i9=="light"then if Nav.control.isAnyHeadlightSwitchedOn()==1 then Nav.control.switchOffHeadlights()else Nav.control.switchOnHeadlights()end elseif i9=="forward"then B=B-1 elseif i9=="backward"then B=B+1 elseif i9=="left"then E=E-1 elseif i9=="right"then E=E+1 elseif i9=="yawright"then F=F-1 elseif i9=="yawleft"then F=F+1 elseif i9=="straferight"then Nav.axisCommandManager:updateCommandFromActionStart(axisCommandId.lateral,1.0)elseif i9=="strafeleft"then Nav.axisCommandManager:updateCommandFromActionStart(axisCommandId.lateral,-1.0)elseif i9=="up"then Z=Z+1;Nav.axisCommandManager:deactivateGroundEngineAltitudeStabilization()Nav.axisCommandManager:updateCommandFromActionStart(axisCommandId.vertical,1.0)elseif i9=="down"then Z=Z-1;Nav.axisCommandManager:deactivateGroundEngineAltitudeStabilization()Nav.axisCommandManager:updateCommandFromActionStart(axisCommandId.vertical,-1.0)elseif i9=="groundaltitudeup"then OldButtonMod=M;OldAntiMod=N;if antigrav and not ExternalAGG and antigrav.getState()==1 then if AntigravTargetAltitude~=nil then if AltitudeHold and AntigravTargetAltitudeHoldAltitude-10 then AntigravTargetAltitude=AntigravTargetAltitude+N;HoldAltitude=AntigravTargetAltitude else AntigravTargetAltitude=AntigravTargetAltitude+N end else AntigravTargetAltitude=desiredBaseAltitude+100 end elseif AltitudeHold then HoldAltitude=HoldAltitude+M else Nav.axisCommandManager:updateTargetGroundAltitudeFromActionStart(1.0)end elseif i9=="groundaltitudedown"then OldButtonMod=M;OldAntiMod=N;if antigrav and not ExternalAGG and antigrav.getState()==1 then if AntigravTargetAltitude~=nil then if AltitudeHold and AntigravTargetAltitudeHoldAltitude-10 then AntigravTargetAltitude=AntigravTargetAltitude-N;if AntigravTargetAltitude<1000 then AntigravTargetAltitude=1000 end;HoldAltitude=AntigravTargetAltitude else AntigravTargetAltitude=AntigravTargetAltitude-N;if AntigravTargetAltitude<1000 then AntigravTargetAltitude=1000 end end else AntigravTargetAltitude=desiredBaseAltitude end elseif AltitudeHold then HoldAltitude=HoldAltitude-M else Nav.axisCommandManager:updateTargetGroundAltitudeFromActionStart(-1.0)end elseif i9=="option1"then IncrementAutopilotTargetIndex()v=false elseif i9=="option2"then DecrementAutopilotTargetIndex()v=false elseif i9=="option3"then if hideHudOnToggleWidgets then if showHud then showHud=false else showHud=true end end;v=false;ToggleWidgets()elseif i9=="option4"then ToggleAutopilot()v=false elseif i9=="option5"then ToggleLockPitch()v=false elseif i9=="option6"then ToggleAltitudeHold()v=false elseif i9=="option7"then wipeSaveVariables()v=false elseif i9=="option8"then ToggleFollowMode()v=false elseif i9=="option9"then if gyro~=nil then gyro.toggle()ah=gyro.getState()==1 end;v=false elseif i9=="lshift"then if system.isViewLocked()==1 then J=true;PrevViewLock=system.isViewLocked()system.lockView(1)elseif o()==1 and ShiftShowsRemoteButtons then J=true;Animated=false;Animating=false end elseif i9=="brake"then if BrakeToggleStatus then BrakeToggle()elseif not BrakeIsOn then BrakeToggle()else BrakeIsOn=true end elseif i9=="lalt"then if o()==0 and not freeLookToggle and userControlScheme=="keyboard"then system.lockView(1)end elseif i9=="booster"then if VanillaRockets then Nav:toggleBoosters()elseif not O then if not IsRocketOn then Nav:toggleBoosters()IsRocketOn=true end;O=true else if IsRocketOn then Nav:toggleBoosters()IsRocketOn=false end;O=false end elseif i9=="stopengines"then Nav.axisCommandManager:resetCommand(axisCommandId.longitudinal)clearAll()elseif i9=="speedup"then if not J then Nav.axisCommandManager:updateCommandFromActionStart(axisCommandId.longitudinal,speedChangeLarge)else IncrementAutopilotTargetIndex()end elseif i9=="speeddown"then if not J then Nav.axisCommandManager:updateCommandFromActionStart(axisCommandId.longitudinal,-speedChangeLarge)else DecrementAutopilotTargetIndex()end elseif i9=="antigravity"and not ExternalAGG then if antigrav~=nil then ToggleAntigrav()end end end;function script.onActionStop(i9)if i9=="forward"then B=0 elseif i9=="backward"then B=0 elseif i9=="left"then E=0 elseif i9=="right"then E=0 elseif i9=="yawright"then F=0 elseif i9=="yawleft"then F=0 elseif i9=="straferight"then Nav.axisCommandManager:updateCommandFromActionStop(axisCommandId.lateral,-1.0)elseif i9=="strafeleft"then Nav.axisCommandManager:updateCommandFromActionStop(axisCommandId.lateral,1.0)elseif i9=="up"then Z=0;Nav.axisCommandManager:updateCommandFromActionStop(axisCommandId.vertical,-1.0)Nav.axisCommandManager:activateGroundEngineAltitudeStabilization(currentGroundAltitudeStabilization)Nav:setEngineForceCommand('hover',vec3(),1)elseif i9=="down"then Z=0;Nav.axisCommandManager:updateCommandFromActionStop(axisCommandId.vertical,1.0)Nav.axisCommandManager:activateGroundEngineAltitudeStabilization(currentGroundAltitudeStabilization)Nav:setEngineForceCommand('hover',vec3(),1)elseif i9=="groundaltitudeup"then if antigrav and not ExternalAGG and antigrav.getState()==1 then N=OldAntiMod end;if AltitudeHold then M=OldButtonMod end;v=false elseif i9=="groundaltitudedown"then if antigrav and not ExternalAGG and antigrav.getState()==1 then N=OldAntiMod end;if AltitudeHold then M=OldButtonMod end;v=false elseif i9=="lshift"then if system.isViewLocked()==1 then J=false;a0=0;a1=0;system.lockView(PrevViewLock)elseif o()==1 and ShiftShowsRemoteButtons then J=false;Animated=false;Animating=false end elseif i9=="brake"then if not BrakeToggleStatus then if BrakeIsOn then BrakeToggle()else BrakeIsOn=false end end elseif i9=="lalt"then if o()==0 and freeLookToggle then if v then if system.isViewLocked()==1 then system.lockView(0)else system.lockView(1)end else v=true end elseif o()==0 and not freeLookToggle and userControlScheme=="keyboard"then system.lockView(0)end end end;function script.onActionLoop(i9)if i9=="groundaltitudeup"then if antigrav and not ExternalAGG and antigrav.getState()==1 then if AntigravTargetAltitude~=nil then if AltitudeHold and AntigravTargetAltitudeHoldAltitude-10 then AntigravTargetAltitude=AntigravTargetAltitude+N;HoldAltitude=AntigravTargetAltitude else AntigravTargetAltitude=AntigravTargetAltitude+N end;N=N*1.05;BrakeIsOn=false else AntigravTargetAltitude=desiredBaseAltitude+100;BrakeIsOn=false end elseif AltitudeHold then HoldAltitude=HoldAltitude+M;M=M*1.05 else Nav.axisCommandManager:updateTargetGroundAltitudeFromActionLoop(1.0)end elseif i9=="groundaltitudedown"then if antigrav and not ExternalAGG and antigrav.getState()==1 then if AntigravTargetAltitude~=nil then if AltitudeHold and AntigravTargetAltitudeHoldAltitude-10 then AntigravTargetAltitude=AntigravTargetAltitude-N;if AntigravTargetAltitude<1000 then AntigravTargetAltitude=1000 end;HoldAltitude=AntigravTargetAltitude else AntigravTargetAltitude=AntigravTargetAltitude-N;if AntigravTargetAltitude<1000 then AntigravTargetAltitude=1000 end end;N=N*1.05;BrakeIsOn=false else AntigravTargetAltitude=desiredBaseAltitude-100;BrakeIsOn=false end elseif AltitudeHold then HoldAltitude=HoldAltitude-M;M=M*1.05 else Nav.axisCommandManager:updateTargetGroundAltitudeFromActionLoop(-1.0)end elseif i9=="speedup"then if not J then Nav.axisCommandManager:updateCommandFromActionLoop(axisCommandId.longitudinal,speedChangeSmall)end elseif i9=="speeddown"then if not J then Nav.axisCommandManager:updateCommandFromActionLoop(axisCommandId.longitudinal,-speedChangeSmall)end end end;function DisplayMessage(bo,ia)if ia~="empty"then bo[#bo+1]=[[]]for ib in string.gmatch(ia,"([^\n]+)")do bo[#bo+1]=e([[%s]],ib)end;bo[#bo+1]=[[]]end;if a2~=0 then unit.setTimer("msgTick",a2)a2=0 end end;function updateDistance()local a_=system.getTime()local velocity=vec3(core.getWorldVelocity())local cA=vec3(velocity):len()local ic=a_-ag;if cA>1.38889 then cA=cA/1000;local id=cA*(a_-ag)TotalDistanceTravelled=TotalDistanceTravelled+id;W=W+id end;X=X+ic;TotalFlightTime=TotalFlightTime+ic;ag=a_ end;script.onStart() + ]],co,bm,bn+dJ,dG,bm,bn+dK,dF))by=by+1;dw=dw*10;if dF==dz then dx=dH else dx=0 end end;table.insert(bz,[[]])end end;function DrawPrograde(bz,velocity,bg,centerX,centerY)if bg>5 and not ad or bg>w then local db=circleRad;local dL=20;local dM=20;local dN=vec3(velocity)local dO=getRelativePitch(dN)local dP=getRelativeYaw(dN)local dQ=-dP/dM*db;local dR=dO/dL*db;local bm=centerX+dQ;local bn=centerY+dR;local a3=math.sqrt(dQ^2+dR^2)if a3',bm,bn)else local d7=math.atan(dR,dQ)local dS=centerX+db*math.cos(d7)local dT=centerY+db*math.sin(d7)bz[#bz+1]=e('',dS,dT)end;dO=getRelativePitch(-dN)dP=getRelativeYaw(-dN)dQ=-dP/dM*db;dR=dO/dL*db;bm=centerX+dQ;bn=centerY+dR;a3=math.sqrt(dQ^2+dR^2)if not ad then if a3',bm,bn)else local d7=math.atan(dR,dQ)local dS=centerX+db*math.cos(d7)local dT=centerY+db*math.sin(d7)bz[#bz+1]=e('',dS,dT)end end end end;function DrawWarnings(bz)bz[#bz+1]=e([[DU Hud Version: %.3f]],ConvertResolutionX(1900),ConvertResolutionY(1070),VERSION_NUMBER)bz[#bz+1]=[[]]if unit.isMouseControlActivated()==1 then bz[#bz+1]=e([[ + Warning: Invalid Control Scheme Detected]],ConvertResolutionX(960),ConvertResolutionY(550))bz[#bz+1]=e([[ + Keyboard Scheme must be selected]],ConvertResolutionX(960),ConvertResolutionY(600))bz[#bz+1]=e([[ + Set your preferred scheme in Lua Parameters instead]],ConvertResolutionX(960),ConvertResolutionY(650))end;local dU=ConvertResolutionX(960)local dV=ConvertResolutionY(860)local dW=ConvertResolutionY(880)local dX=ConvertResolutionY(900)local dY=ConvertResolutionY(960)local dZ=ConvertResolutionY(200)local d_=ConvertResolutionY(150)local e0=ConvertResolutionY(960)if o()==1 and not RemoteHud then dV=ConvertResolutionY(135)dW=ConvertResolutionY(155)dX=ConvertResolutionY(175)dZ=ConvertResolutionY(115)d_=ConvertResolutionY(95)end;if BrakeIsOn then bz[#bz+1]=e([[Brake Engaged]],dU,dV)end;if ad and RateOfChangebrakeLandingRate+5 then bz[#bz+1]=e([[** STALL WARNING **]],dU,dZ+50)end;if ah then bz[#bz+1]=e([[Gyro Enabled]],dU,e0)end;if GearExtended then if A then bz[#bz+1]=e([[Gear Extended]],dU,dW)else bz[#bz+1]=e([[Landed (G: Takeoff)]],dU,dW)end;bz[#bz+1]=e([[Hover Height: %s]],dU,dX,getDistanceDisplayString(Nav:getTargetGroundAltitude()))end;if O then bz[#bz+1]=e([[ROCKET BOOST ENABLED]],dU,dY+20)end;if antigrav and not ExternalAGG and antigrav.getState()==1 and AntigravTargetAltitude~=nil then if math.abs(ae-antigrav.getBaseAltitude())<501 then bz[#bz+1]=e([[AGG On - Target Altitude: %d Singluarity Altitude: %d]],dU,dZ+20,d(AntigravTargetAltitude),d(antigrav.getBaseAltitude()))else bz[#bz+1]=e([[AGG On - Target Altitude: %d Singluarity Altitude: %d]],dU,dZ+20,d(AntigravTargetAltitude),d(antigrav.getBaseAltitude()))end elseif Autopilot and AutopilotTargetName~="None"then bz[#bz+1]=e([[Autopilot %s]],dU,dZ+20,AutopilotStatus)elseif LockPitch~=nil then bz[#bz+1]=e([[LockedPitch: %d]],dU,dZ+20,d(LockPitch))elseif I then bz[#bz+1]=e([[Follow Mode Engaged]],dU,dZ+20)elseif Reentry then bz[#bz+1]=e([[Parachute Re-entry in Progress]],dU,dZ+20)end;if AltitudeHold then if AutoTakeoff then bz[#bz+1]=e([[Ascent to %s]],dU,dZ,getDistanceDisplayString(HoldAltitude))if BrakeIsOn then bz[#bz+1]=e([[Throttle Up and Disengage Brake For Takeoff]],dU,dZ+50)end else bz[#bz+1]=e([[Altitude Hold: %s]],dU,dZ,getDistanceDisplayString2(HoldAltitude))end end;if BrakeLanding then if StrongBrakes then bz[#bz+1]=e([[Brake-Landing]],dU,dZ)else bz[#bz+1]=e([[Coast-Landing]],dU,dZ)end end;if ProgradeIsOn then bz[#bz+1]=e([[Prograde Alignment]],dU,dZ)end;if RetrogradeIsOn then bz[#bz+1]=e([[Retrograde Alignment]],dU,dZ)end;if TurnBurn then bz[#bz+1]=e([[Turn & Burn Braking]],dU,d_)end;if VectorToTarget then bz[#bz+1]=e([[%s]],dU,dZ+30,VectorStatus)end;bz[#bz+1]=""end;function DisplayOrbitScreen(bz)if orbit~=nil and j()<0.2 and planet~=nil and orbit.apoapsis~=nil and orbit.periapsis~=nil and orbit.period~=nil and orbit.apoapsis.speed>5 and DisplayOrbit then local e1=75;local e2=0;local e3=250;local e4=4;e2=e2+e4;local e5=15;local bm=e1+e3+e1/2+e4;local bn=e2+e3/2+5+e4;local e6,e7,e8,e9;e6=e3/4;e9=0;bz[#bz+1]=[[]]bz[#bz+1]=e('',e3+e1*2,e3+e2,e4,e4)if orbit.periapsis~=nil and orbit.apoapsis~=nil then e8=(orbit.apoapsis.altitude+orbit.periapsis.altitude+planet.radius*2)/(e6*2)e7=(planet.radius+orbit.periapsis.altitude+(orbit.apoapsis.altitude-orbit.periapsis.altitude)/2)/e8*(1-orbit.eccentricity)e9=e6-orbit.periapsis.altitude/e8-planet.radius/e8;local ea=""if orbit.periapsis.altitude<=0 then ea='redout'end;bz[#bz+1]=e([[]],ea,e1+e3/2+e9+e4,e2+e3/2+e4,e6,e7)bz[#bz+1]=e('',e1+e3/2+e4,e2+e3/2+e4,planet.radius/e8)end;if orbit.apoapsis~=nil and orbit.apoapsis.speed1 then bz[#bz+1]=e([[]],bm-35,bn-5,e1+e3/2+e6+e9,bn-5)bz[#bz+1]=e([[Apoapsis]],bm,bn)bn=bn+e5;bz[#bz+1]=e([[%s]],bm,bn,getDistanceDisplayString(orbit.apoapsis.altitude))bn=bn+e5;bz[#bz+1]=e([[%s]],bm,bn,FormatTimeString(orbit.timeToApoapsis))bn=bn+e5;bz[#bz+1]=e([[%s]],bm,bn,getSpeedDisplayString(orbit.apoapsis.speed))end;bn=e2+e3/2+5+e4;bm=e1-e1/2+10+e4;if orbit.periapsis~=nil and orbit.periapsis.speed1 then bz[#bz+1]=e([[]],bm+35,bn-5,e1+e3/2-e6+e9,bn-5)bz[#bz+1]=e([[Periapsis]],bm,bn)bn=bn+e5;bz[#bz+1]=e([[%s]],bm,bn,getDistanceDisplayString(orbit.periapsis.altitude))bn=bn+e5;bz[#bz+1]=e([[%s]],bm,bn,FormatTimeString(orbit.timeToPeriapsis))bn=bn+e5;bz[#bz+1]=e([[%s]],bm,bn,getSpeedDisplayString(orbit.periapsis.speed))end;bz[#bz+1]=e([[%s]],e1+e3/2+e4,20+e4,planet.name)if orbit.period~=nil and orbit.periapsis~=nil and orbit.apoapsis~=nil and orbit.apoapsis.speed>1 then local eb=orbit.timeToApoapsis/orbit.period*2*math.pi;local ec=e6*math.cos(eb)local ed=e7*math.sin(eb)bz[#bz+1]=e('',e1+e3/2+ec+e9+e4,e2+e3/2+ed+e4)end;bz[#bz+1]=[[]]end end;function getDistanceDisplayString(a3)local ee=a3>100000;local b5=""if ee then b5=round(a3/1000/200,1).." SU"elseif a3<1000 then b5=round(a3,1).." M"else b5=round(a3/1000,1).." KM"end;return b5 end;function getDistanceDisplayString2(a3)local ee=a3>100000;local b5=""if ee then b5=round(a3/1000/200,2).." SU"elseif a3<1000 then b5=round(a3,2).." M"else b5=round(a3/1000,2).." KM"end;return b5 end;function getSpeedDisplayString(bg)return d(round(bg*3.6,0)+0.5).." km/h"end;function FormatTimeString(ef)local eg=0;local eh=0;local ei=0;if ef<60 then ef=d(ef)elseif ef<3600 then eg=d(ef/60)ef=d(ef%60)elseif ef<86400 then eh=d(ef/3600)eg=d(ef%3600/60)else ei=d(ef/86400)eh=d(ef%86400/60)end;if ei>0 then return ei.."d "..eh.."h "elseif eh>0 then return eh.."h "..eg.."m "elseif eg>0 then return eg.."m "..ef.."s"elseif ef>0 then return ef.."s"else return"0s"end end;function getMagnitudeInDirection(cq,ej)cq=vec3(cq)ej=vec3(ej):normalize()local b5=cq*ej;return b5.x+b5.y+b5.z end;function UpdateAutopilotTarget()if AutopilotTargetIndex==0 then AutopilotTargetName="None"V=nil;return true end;local ek=AtlasOrdered[AutopilotTargetIndex].index;local el=aS[0][ek]if el.center then AutopilotTargetName=el.name;V=aY[0][ek]if CustomTarget~=nil then if j()==0 then if system.updateData(widgetMaxBrakeTimeText,widgetMaxBrakeTime)~=1 then system.addDataToWidget(widgetMaxBrakeTimeText,widgetMaxBrakeTime)end;if system.updateData(widgetMaxBrakeDistanceText,widgetMaxBrakeDistance)~=1 then system.addDataToWidget(widgetMaxBrakeDistanceText,widgetMaxBrakeDistance)end;if system.updateData(widgetCurBrakeTimeText,widgetCurBrakeTime)~=1 then system.addDataToWidget(widgetCurBrakeTimeText,widgetCurBrakeTime)end;if system.updateData(widgetCurBrakeDistanceText,widgetCurBrakeDistance)~=1 then system.addDataToWidget(widgetCurBrakeDistanceText,widgetCurBrakeDistance)end;if system.updateData(widgetTrajectoryAltitudeText,widgetTrajectoryAltitude)~=1 then system.addDataToWidget(widgetTrajectoryAltitudeText,widgetTrajectoryAltitude)end end;if system.updateData(widgetMaxMassText,widgetMaxMass)~=1 then system.addDataToWidget(widgetMaxMassText,widgetMaxMass)end;if system.updateData(widgetTravelTimeText,widgetTravelTime)~=1 then system.addDataToWidget(widgetTravelTimeText,widgetTravelTime)end end;CustomTarget=nil else CustomTarget=el;for _,b4 in pairs(aY[0])do if b4.name==CustomTarget.planetname then V=b4;AutopilotTargetName=CustomTarget.name;break end end;if system.updateData(widgetMaxMassText,widgetMaxMass)~=1 then system.addDataToWidget(widgetMaxMassText,widgetMaxMass)end;if system.updateData(widgetTravelTimeText,widgetTravelTime)~=1 then system.addDataToWidget(widgetTravelTimeText,widgetTravelTime)end end;AutopilotTargetCoords=vec3(V.center)_,AutopilotEndSpeed=a_(V):escapeAndOrbitalSpeed(AutopilotTargetOrbit)AutopilotPlanetGravity=0;AutopilotAccelerating=false;AutopilotBraking=false;AutopilotCruising=false;Autopilot=false;AutopilotRealigned=false;AutopilotStatus="Aligning"return true end;function IncrementAutopilotTargetIndex()AutopilotTargetIndex=AutopilotTargetIndex+1;if AutopilotTargetIndex>#AtlasOrdered then AutopilotTargetIndex=0 end;UpdateAutopilotTarget()end;function DecrementAutopilotTargetIndex()AutopilotTargetIndex=AutopilotTargetIndex-1;if AutopilotTargetIndex<0 then AutopilotTargetIndex=#AtlasOrdered end;UpdateAutopilotTarget()end;function GetAutopilotMaxMass()local em=LastMaxBrakeInAtmo/V:getGravity(V.center+vec3(0,0,1)*V.radius):len()return em end;function GetAutopilotTravelTime()if not Autopilot then if CustomTarget==nil or CustomTarget.planetname~=planet.name then AutopilotDistance=(V.center-vec3(core.getConstructWorldPos())):len()else AutopilotDistance=(CustomTarget.position-vec3(core.getConstructWorldPos())):len()end end;local velocity=core.getWorldVelocity()local bg=vec3(velocity):len()local en,eo=aZ.computeDistanceAndTime(vec3(velocity):len(),MaxGameVelocity,n(),Nav:maxForceForward(),warmup,0)local P,Q;if not TurnBurn then P,Q=GetAutopilotBrakeDistanceAndTime(MaxGameVelocity)else P,Q=GetAutopilotTBBrakeDistanceAndTime(MaxGameVelocity)end;local _,ep;if not TurnBurn then _,ep=GetAutopilotBrakeDistanceAndTime(bg)else _,ep=GetAutopilotTBBrakeDistanceAndTime(bg)end;local eq=0;local er=0;if AutopilotCruising or not Autopilot and bg>5 then er=aZ.computeTravelTime(bg,0,AutopilotDistance)elseif P+en0 then return aZ.computeDistanceAndTime(bg,AutopilotEndSpeed,n(),0,0,LastMaxBrakeInAtmo-AutopilotPlanetGravity*n())else return 0,0 end end end;function GetAutopilotTBBrakeDistanceAndTime(bg)RefreshLastMaxBrake()return aZ.computeDistanceAndTime(bg,AutopilotEndSpeed,n(),Nav:maxForceForward(),warmup,LastMaxBrake-AutopilotPlanetGravity*n())end;function hoverDetectGround()local et=-1;local eu=-1;if vBooster then et=vBooster.distance()end;if hover then eu=hover.distance()end;if et~=-1 and eu~=-1 then if etProfileTimeMax then ProfileTimeMax=eA end;if eA0 then local eK=eH:find('identifiedConstructs":%[%]')if eK==nil and perisPanelID==nil then a6=1;ToggleRadarPanel()end;if eK~=nil and perisPanelID~=nil then ToggleRadarPanel()end;if radarPanelID==nil then ToggleRadarPanel()end;a4=e([[Radar: %i contacts]],eI,eJ,#eG)local eL={}for b3,b4 in pairs(eG)do if radar_1.hasMatchingTransponder(b4)==1 then eL[#eL+1]=b4 end end;if#eL>0 then local bn=ConvertResolutionY(15)local bm=ConvertResolutionX(1370)a4=e([[%sFriendlies In Range]],a4,bm,bn)for b3,b4 in pairs(eL)do bn=bn+20;a4=e([[%s%s]],a4,bm,bn,radar_1.getConstructName(b4))end end else local eM;eM=eH:find('worksInEnvironment":false')if eM then a4=e([[ + Radar: Jammed]],eI,eJ)else a4=e([[ + Radar: No Contacts]],eI,eJ)end;if radarPanelID~=nil then a6=0;ToggleRadarPanel()end end end end;function DisplayMessage(bz,eN)if eN~="empty"then bz[#bz+1]=[[]]for eO in string.gmatch(eN,"([^\n]+)")do bz[#bz+1]=e([[%s]],eO)end;bz[#bz+1]=[[]]end;if a2~=0 then unit.setTimer("msgTick",a2)a2=0 end end;function updateDistance()local bb=system.getTime()local velocity=vec3(core.getWorldVelocity())local cL=vec3(velocity):len()local eP=bb-ag;if cL>1.38889 then cL=cL/1000;local eQ=cL*(bb-ag)TotalDistanceTravelled=TotalDistanceTravelled+eQ;W=W+eQ end;X=X+eP;TotalFlightTime=TotalFlightTime+eP;ag=bb end;function Atlas()return{[0]={[1]={GM=6930729684,bodyId=1,center={x=17465536.000,y=22665536.000,z=-34464.000},name='Madis',planetarySystemId=0,radius=44300,atmos=true,gravity=0.36},[2]={GM=157470826617,bodyId=2,center={x=-8.000,y=-8.000,z=-126303.000},name='Alioth',planetarySystemId=0,radius=126068,atmos=true,gravity=1.01},[3]={GM=11776905000,bodyId=3,center={x=29165536.000,y=10865536.000,z=65536.000},name='Thades',planetarySystemId=0,radius=49000,atmos=true,gravity=0.50},[4]={GM=14893847582,bodyId=4,center={x=-13234464.000,y=55765536.000,z=465536.000},name='Talemai',planetarySystemId=0,radius=57450,atmos=true,gravity=0.46},[5]={GM=16951680000,bodyId=5,center={x=-43534464.000,y=22565536.000,z=-48934464.000},name='Feli',planetarySystemId=0,radius=60000,atmos=true,gravity=0.48},[6]={GM=10502547741,bodyId=6,center={x=52765536.000,y=27165538.000,z=52065535.000},name='Sicari',planetarySystemId=0,radius=51100,atmos=true,gravity=0.41},[7]={GM=13033380591,bodyId=7,center={x=58665538.000,y=29665535.000,z=58165535.000},name='Sinnen',planetarySystemId=0,radius=54950,atmos=true,gravity=0.44},[8]={GM=18477723600,bodyId=8,center={x=80865538.000,y=54665536.000,z=-934463.940},name='Teoma',planetarySystemId=0,radius=62000,atmos=true,gravity=0.49},[9]={GM=18606274330,bodyId=9,center={x=-94134462.000,y=12765534.000,z=-3634464.000},name='Jago',planetarySystemId=0,radius=61590,atmos=true,gravity=0.50},[10]={GM=78480000,bodyId=10,center={x=17448118.224,y=22966846.286,z=143078.820},name='Madis Moon 1',planetarySystemId=0,radius=10000,atmos=false,gravity=0.08},[11]={GM=237402000,bodyId=11,center={x=17194626.000,y=22243633.880,z=-214962.810},name='Madis Moon 2',planetarySystemId=0,radius=11000,atmos=false,gravity=0.10},[12]={GM=265046609,bodyId=12,center={x=17520614.000,y=22184730.000,z=-309989.990},name='Madis Moon 3',planetarySystemId=0,radius=15005,atmos=false,gravity=0.12},[21]={GM=2118960000,bodyId=21,center={x=457933.000,y=-1509011.000,z=115524.000},name='Alioth Moon 1',planetarySystemId=0,radius=30000,atmos=false,gravity=0.24},[22]={GM=2165833514,bodyId=22,center={x=-1692694.000,y=729681.000,z=-411464.000},name='Alioth Moon 4',planetarySystemId=0,radius=30330,atmos=false,gravity=0.24},[26]={GM=68234043600,bodyId=26,center={x=-1404835.000,y=562655.000,z=-285074.000},name='Sanctuary',planetarySystemId=0,radius=83400,atmos=true,gravity=1.00},[30]={GM=211564034,bodyId=30,center={x=29214402.000,y=10907080.695,z=433858.200},name='Thades Moon 1',planetarySystemId=0,radius=14002,atmos=false,gravity=0.11},[31]={GM=264870000,bodyId=31,center={x=29404193.000,y=10432768.000,z=19554.131},name='Thades Moon 2',planetarySystemId=0,radius=15000,atmos=false,gravity=0.12},[40]={GM=141264000,bodyId=40,center={x=-13503090.000,y=55594325.000,z=769838.640},name='Talemai Moon 2',planetarySystemId=0,radius=12000,atmos=false,gravity=0.10},[41]={GM=106830900,bodyId=41,center={x=-12800515.000,y=55700259.000,z=325207.840},name='Talemai Moon 3',planetarySystemId=0,radius=11000,atmos=false,gravity=0.09},[42]={GM=264870000,bodyId=42,center={x=-13058408.000,y=55781856.000,z=740177.760},name='Talemai Moon 1',planetarySystemId=0,radius=15000,atmos=false,gravity=0.12},[50]={GM=499917600,bodyId=50,center={x=-43902841.780,y=22261034.700,z=-48862386.000},name='Feli Moon 1',planetarySystemId=0,radius=14000,atmos=false,gravity=0.11},[70]={GM=396912600,bodyId=70,center={x=58969616.000,y=29797945.000,z=57969449.000},name='Sinnen Moon 1',planetarySystemId=0,radius=17000,atmos=false,gravity=0.14},[100]={GM=13975172474,bodyId=100,center={x=98865536.000,y=-13534464.000,z=-934461.990},name='Lacobus',planetarySystemId=0,radius=55650,atmos=true,gravity=0.46},[101]={GM=264870000,bodyId=101,center={x=98905288.170,y=-13950921.100,z=-647589.530},name='Lacobus Moon 3',planetarySystemId=0,radius=15000,atmos=false,gravity=0.12},[102]={GM=444981600,bodyId=102,center={x=99180968.000,y=-13783862.000,z=-926156.400},name='Lacobus Moon 1',planetarySystemId=0,radius=18000,atmos=false,gravity=0.14},[103]={GM=211503600,bodyId=103,center={x=99250052.000,y=-13629215.000,z=-1059341.400},name='Lacobus Moon 2',planetarySystemId=0,radius=14000,atmos=false,gravity=0.11},[110]={GM=9204742375,bodyId=110,center={x=14165536.000,y=-85634465.000,z=-934464.300},name='Symeon',planetarySystemId=0,radius=49050,atmos=true,gravity=0.39},[120]={GM=7135606629,bodyId=120,center={x=2865536.700,y=-99034464.000,z=-934462.020},name='Ion',planetarySystemId=0,radius=44950,atmos=true,gravity=0.36},[121]={GM=106830900,bodyId=121,center={x=2472916.800,y=-99133747.000,z=-1133582.800},name='Ion Moon 1',planetarySystemId=0,radius=11000,atmos=false,gravity=0.09},[122]={GM=176580000,bodyId=122,center={x=2995424.500,y=-99275010.000,z=-1378480.700},name='Ion Moon 2',planetarySystemId=0,radius=15000,atmos=false,gravity=0.12}}}end;function SetupAtlas()aS=Atlas()for b3,b4 in pairs(aS[0])do if av==nil or b4.center.xaw then aw=b4.center.x end;if ax==nil or b4.center.yay then ay=b4.center.y end end;aT=""local eR=1.1*(aw-av)/1920;local eS=1.4*(ay-ax)/1080;for b3,b4 in pairs(aS[0])do local bm=960+b4.center.x/eR;local bn=540+b4.center.y/eS;aT=aT..''if not string.match(b4.name,"Moon")and not string.match(b4.name,"Sanctuary")then aT=aT..""..b4.name..""end end;local cP=vec3(core.getConstructWorldPos())local bm=960+cP.x/eR;local bn=540+cP.y/eS;aT=aT..''aT=aT.."You Are Here"aT=aT..[[]]aU=eR;aV=eS;if screen_2 then screen_2.setHTML(''..aT)local cP=vec3(core.getConstructWorldPos())local bm=960+cP.x/eR;local bn=540+cP.y/eS;aT=''aT=aT.."You Are Here"aW=screen_2.addContent((bm-80)/19.20,(bn-80)/10.80,aT)end end;function PlanetRef()local function eT(eU)return type(eU)=='number'end;local function eV(eU)return type(tonumber(eU))=='number'end;local function eW(eX)return type(eX)=='table'end;local function eY(eZ)return type(eZ)=='string'end;local function e_(b4)return eW(b4)and eT(b4.x and b4.y and b4.z)end;local function f0(f1)return eW(f1)and eT(f1.latitude and f1.longitude and f1.altitude and f1.bodyId and f1.systemId)end;local f2=math.pi/180;local f3=180/math.pi;local f4=1e-10;local p=' *([+-]?%d+%.?%d*e?[+-]?%d*)'local f5='::pos{'..p..','..p..','..p..','..p..','..p..'}'local utils=require('cpml.utils')local vec3=require('cpml.vec3')local f6=utils.clamp;local function f7(f8,f9)if f8==0 then return math.abs(f9)<1e-09 end;if f9==0 then return math.abs(f8)<1e-09 end;return math.abs(f8-f9)=0 then local g4=math.sqrt(g3)local g5=g2+g4;local g6=g2-g4;if g6>0 then return fU,g5,g6 elseif g5>0 then return fU,g5,nil end end end;return nil,nil,nil end;function fw:closestBody(g7)assert(type(g7)=='table','Invalid coordinates.')local g8,fU;local g9=vec3(g7)for _,ga in pairs(self)do local gb=(ga.center-g9):len2()if not fU or gb=0 and gf or 2*math.pi+gf;fu=math.pi/2-math.acos(ge.z/a3)end;return setmetatable({latitude=fu,longitude=fv,altitude=cB,bodyId=self.bodyId,systemId=self.planetarySystemId},fq)end;function ff:convertToWorldCoordinates(ft)local gc=eY(ft)and fs(ft)or ft;if gc.bodyId==0 then return vec3(gc.latitude,gc.longitude,gc.altitude)end;assert(f0(gc),'Argument 1 (mapPosition) is not an instance of "MapPosition".')assert(gc.systemId==self.planetarySystemId,'Argument 1 (mapPosition) has a different planetary system ID.')assert(gc.bodyId==self.bodyId,'Argument 1 (mapPosition) has a different planetary body ID.')local gg=math.cos(gc.latitude)return self.center+(self.radius+gc.altitude)*vec3(gg*math.cos(gc.longitude),gg*math.sin(gc.longitude),math.sin(gc.latitude))end;function ff:getAltitude(fo)return(vec3(fo)-self.center):len()-self.radius end;function ff:getDistance(fo)return(vec3(fo)-self.center):len()end;function ff:getGravity(fo)local gh=self.center-vec3(fo)local gi=gh:len2()return self.GM/gi*gh/math.sqrt(gi)end;return setmetatable(aX,{__call=function(_,...)return fE(...)end})end;function Keplers()local vec3=require('cpml.vec3')local PlanetRef=PlanetRef()local function eY(eZ)return type(eZ)=='string'end;local function eW(eX)return type(eX)=='table'end;local function f7(f8,f9)if f8==0 then return math.abs(f9)<1e-09 end;if f9==0 then return math.abs(f8)<1e-09 end;return math.abs(f8-f9)0 then gz=gy;gA=gz+gt/2 end;if gA>gt then gA=gA-gt end end;return{periapsis={position=gq,speed=gs/go,circularOrbitSpeed=math.sqrt(gl/go),altitude=go-self.body.radius},apoapsis=gr and{position=gr,speed=gs/gp,circularOrbitSpeed=math.sqrt(gl/gp),altitude=gp-self.body.radius},currentVelocity=b4,currentPosition=cP,eccentricity=gn,period=gt,eccentricAnomaly=gv,meanAnomaly=gx,timeToPeriapsis=gz,timeToApoapsis=gA}end;local function gB(gC)local ga=PlanetRef.BodyParameters(gC.planetarySystemId,gC.bodyId,gC.radius,gC.center,gC.GM)return setmetatable({body=ga},Kepler)end;return setmetatable(Kepler,{__call=function(_,...)return gB(...)end})end;function Kinematics()local aZ={}local gD=30000000/3600;local gE=gD*gD;local gF=100;local function gG(b4)return 1/math.sqrt(1-b4*b4/gE)end;function aZ.computeAccelerationTime(gH,gI,gJ)local gK=gD*math.asin(gH/gD)return(gD*math.asin(gJ/gD)-gK)/gI end;function aZ.computeDistanceAndTime(gH,gJ,gL,gM,gN,gO)gN=gN or 0;gO=gO or 0;local gP=gH<=gJ;local gQ=gM*(gP and 1 or-1)/gL;local gR=-gO/gL;local gS=gQ+gR;if gP and gS<=0 or not gP and gS>=0 then return-1,-1 end;local gT,gU=0,0;if gQ~=0 and gN>0 then local gK=math.asin(gH/gD)local gV=math.pi*(gQ/2+gR)local gW=gQ*gN;local gX=gD*math.pi;local b4=function(eX)local bV=(gV*eX-gW*math.sin(math.pi*eX/2/gN)+gX*gK)/gX;local gY=math.tan(bV)return gD*gY/math.sqrt(gY*gY+1)end;local gZ=gP and function(eZ)return eZ>=gJ end or function(eZ)return eZ<=gJ end;gU=2*gN;if gZ(b4(gU))then local g_=0;while math.abs(gU-g_)>0.5 do local eX=(gU+g_)/2;if gZ(b4(eX))then gU=eX else g_=eX end end end;local h0=gH;local h1=gU/gF;for h2=1,gF do local bg=b4(h2*h1)gT=gT+(bg+h0)*h1/2;h0=bg end;if gU<2*gN then return gT,gU end;gH=h0 end;local gK=gD*math.asin(gH/gD)local b6=(gD*math.asin(gJ/gD)-gK)/gS;local h3=gE*math.cos(gK/gD)/gS;local a3=h3-gE*math.cos((gS*b6+gK)/gD)/gS;return a3+gT,b6+gU end;function aZ.computeTravelTime(gH,gI,a3)if a3==0 then return 0 end;if gI>0 then local gK=gD*math.asin(gH/gD)local h3=gE*math.cos(gK/gD)/gI;return(gD*math.acos(gI*(h3-a3)/gE)-gK)/gI end;assert(gH>0,'Acceleration and initial speed are both zero.')return a3/gH end;function aZ.lorentz(b4)return gG(b4)end;return aZ end;function script.onStart()VERSION_NUMBER=4.914;SetupComplete=false;beginSetup=coroutine.create(function()Nav.axisCommandManager:setupCustomTargetSpeedRanges(axisCommandId.longitudinal,{1000,5000,10000,20000,30000})LoadVariables()coroutine.yield()ProcessElements()coroutine.yield()SetupChecks()SetupButtons()coroutine.yield()SetupAtlas()aX=PlanetRef()aY=aX(Atlas())aZ=Kinematics()a_=Keplers()AddLocationsToAtlas()UpdateAutopilotTarget()coroutine.yield()unit.hide()system.showScreen(1)collectgarbage("collect")unit.setTimer("apTick",apTickRate)unit.setTimer("oneSecond",1)unit.setTimer("tenthSecond",1/10)if UseSatNav then unit.setTimer("fiveSecond",5)end end)end;function script.onStop()_autoconf.hideCategoryPanels()if antigrav~=nil and not ExternalAGG then antigrav.hide()end;if warpdrive~=nil then warpdrive.hide()end;core.hide()Nav.control.switchOffHeadlights()local h4=j()if door and(h4>0 or h4==0 and ae<10000)then for _,b4 in pairs(door)do b4.toggle()end end;if switch then for _,b4 in pairs(switch)do b4.toggle()end end;if forcefield and(h4>0 or h4==0 and ae<10000)then for _,b4 in pairs(forcefield)do b4.toggle()end end;if dbHud_1 then if not Y then for b3,b4 in pairs(b)do dbHud_1.setStringValue(b4,g(_G[b4]))end;for b3,b4 in pairs(a)do dbHud_1.setStringValue(b4,g(_G[b4]))end;c("Saved Variables to Datacore")end end;if button then button.activate()end end;function script.onTick(h5)if h5=="tenthSecond"then if AutopilotTargetName~="None"then if panelInterplanetary==nil then SetupInterplanetaryPanel()end;if AutopilotTargetName~=nil then local h6=CustomTarget~=nil;planetMaxMass=GetAutopilotMaxMass()system.updateData(interplanetaryHeaderText,'{"label": "Target", "value": "'..AutopilotTargetName..'", "unit":""}')travelTime=GetAutopilotTravelTime()if h6 then a3=(vec3(core.getConstructWorldPos())-CustomTarget.position):len()else a3=(AutopilotTargetCoords-vec3(core.getConstructWorldPos())):len()end;if not TurnBurn then P,Q=GetAutopilotBrakeDistanceAndTime(velMag)R,S=GetAutopilotBrakeDistanceAndTime(MaxGameVelocity)else P,Q=GetAutopilotTBBrakeDistanceAndTime(velMag)R,S=GetAutopilotTBBrakeDistanceAndTime(MaxGameVelocity)end;system.updateData(widgetDistanceText,'{"label": "distance", "value": "'..getDistanceDisplayString(a3)..'", "unit":""}')system.updateData(widgetTravelTimeText,'{"label": "Travel Time", "value": "'..FormatTimeString(travelTime)..'", "unit":""}')system.updateData(widgetCurBrakeDistanceText,'{"label": "Cur Brake distance", "value": "'..getDistanceDisplayString(P)..'", "unit":""}')system.updateData(widgetCurBrakeTimeText,'{"label": "Cur Brake Time", "value": "'..FormatTimeString(Q)..'", "unit":""}')system.updateData(widgetMaxBrakeDistanceText,'{"label": "Max Brake distance", "value": "'..getDistanceDisplayString(R)..'", "unit":""}')system.updateData(widgetMaxBrakeTimeText,'{"label": "Max Brake Time", "value": "'..FormatTimeString(S)..'", "unit":""}')system.updateData(widgetMaxMassText,'{"label": "Maximum Mass", "value": "'..e("%.2f tons",planetMaxMass/1000)..'", "unit":""}')if j()>0 and not WasInAtmo then system.removeDataFromWidget(widgetMaxBrakeTimeText,widgetMaxBrakeTime)system.removeDataFromWidget(widgetMaxBrakeDistanceText,widgetMaxBrakeDistance)system.removeDataFromWidget(widgetCurBrakeTimeText,widgetCurBrakeTime)system.removeDataFromWidget(widgetCurBrakeDistanceText,widgetCurBrakeDistance)system.removeDataFromWidget(widgetTrajectoryAltitudeText,widgetTrajectoryAltitude)WasInAtmo=true end;if j()==0 and WasInAtmo then if system.updateData(widgetMaxBrakeTimeText,widgetMaxBrakeTime)==1 then system.addDataToWidget(widgetMaxBrakeTimeText,widgetMaxBrakeTime)end;if system.updateData(widgetMaxBrakeDistanceText,widgetMaxBrakeDistance)==1 then system.addDataToWidget(widgetMaxBrakeDistanceText,widgetMaxBrakeDistance)end;if system.updateData(widgetCurBrakeTimeText,widgetCurBrakeTime)==1 then system.addDataToWidget(widgetCurBrakeTimeText,widgetCurBrakeTime)end;if system.updateData(widgetCurBrakeDistanceText,widgetCurBrakeDistance)==1 then system.addDataToWidget(widgetCurBrakeDistanceText,widgetCurBrakeDistance)end;if system.updateData(widgetTrajectoryAltitudeText,widgetTrajectoryAltitude)==1 then system.addDataToWidget(widgetTrajectoryAltitudeText,widgetTrajectoryAltitude)end;WasInAtmo=false end end else HideInterplanetaryPanel()end;if warpdrive~=nil then if f(warpdrive.getData()).destination~="Unknown"and f(warpdrive.getData()).distance>400000 then warpdrive.show()showWarpWidget=true else warpdrive.hide()showWarpWidget=false end end elseif h5=="oneSecond"then ab=false;RefreshLastMaxBrake(nil,true)updateDistance()updateRadar()updateWeapons()local bz={}local cA=GetFlightStyle()DrawOdometer(bz,W,TotalDistanceTravelled,cA,X)CheckDamage(bz)a5=table.concat(bz,"")collectgarbage("collect")elseif h5=="fiveSecond"then ac=dbHud_1.getStringValue("SPBAutopilotTargetName")if ac~=nil and ac~=""and ac~="SatNavNotChanged"then local b5=json.decode(dbHud_1.getStringValue("SavedLocations"))if b5~=nil then _G["SavedLocations"]=b5;local by=-1;local bx;for b3,b4 in pairs(SavedLocations)do if b4.name and b4.name=="SatNav Location"then by=b3;break end end;if by~=-1 then bx=SavedLocations[by]by=-1;for b3,b4 in pairs(aS[0])do if b4.name and b4.name=="SatNav Location"then by=b3;break end end;if by>-1 then aS[0][by]=bx end;UpdateAtlasLocationsList()K=bx.name.." position updated"end end;for i=1,#AtlasOrdered do if AtlasOrdered[i].name==ac then AutopilotTargetIndex=i;system.print("Index = "..AutopilotTargetIndex.." "..AtlasOrdered[i].name)UpdateAutopilotTarget()dbHud_1.setStringValue("SPBAutopilotTargetName","SatNavNotChanged")break end end end elseif h5=="msgTick"then local bz={}DisplayMessage(bz,"empty")K="empty"unit.stopTimer("msgTick")a2=3 elseif h5=="animateTick"then b1=true;b0=false;a0=0;a1=0;unit.stopTimer("animateTick")elseif h5=="apTick"then local o=o;RateOfChange=vec3(core.getConstructWorldOrientationForward()):dot(vec3(core.getWorldVelocity()):normalize())ad=j()>0;D=0;H=0;C=0;LastApsDiff=-1;velocity=vec3(core.getWorldVelocity())velMag=vec3(velocity):len()sys=aY[0]planet=sys:closestBody(core.getConstructWorldPos())kepPlanet=a_(planet)orbit=kepPlanet:orbitalParameters(core.getConstructWorldPos(),velocity)aa=hoverDetectGround()local h7=system.getMouseDeltaX()local h8=system.getMouseDeltaY()TargetGroundAltitude=Nav:getTargetGroundAltitude()local h9=velMag>8334;if not h9 and LastIsWarping then if not BrakeIsOn then BrakeToggle()end;if Autopilot then ToggleAutopilot()end end;LastIsWarping=h9;if ad and j()>0.09 then if not ai then if velMag>AtmoSpeedLimit/3.6 then BrakeIsOn=true;ai=true end else if velMag]],ResolutionX,ResolutionY)if K~="empty"then DisplayMessage(bz,K)end;if o()==0 and userControlScheme=="virtual joystick"then DrawDeadZone(bz)end;if o()==1 and screen_1 and screen_1.getMouseY()~=-1 then a0=screen_1.getMouseX()*ResolutionX;a1=screen_1.getMouseY()*ResolutionY;SetButtonContains()DrawButtons(bz)if screen_1.getMouseState()==1 then CheckButtons()end;bz[#bz+1]=e([[]],s,t,a0,a1)elseif system.isViewLocked()==0 then if o()==1 and J then if not b0 then a0=a0+h7;a1=a1+h8 end;SetButtonContains()DrawButtons(bz)if not b0 and not b1 then local ha=table.concat(bz,"")bz={}bz[#bz+1]=e("",ResolutionX,ResolutionY)bz[#bz+1]=aT;bz[#bz+1]=ha;bz[#bz+1]=""b0=true;bz[#bz+1]=[[]]unit.setTimer("animateTick",0.5)local content=table.concat(bz,"")system.setScreen(content)elseif b1 then local ha=table.concat(bz,"")bz={}bz[#bz+1]=e("",ResolutionX,ResolutionY)bz[#bz+1]=aT;bz[#bz+1]=ha;bz[#bz+1]=""end;if not b0 then bz[#bz+1]=e([[]],s,t,a0,a1)end else CheckButtons()a0=0;a1=0 end else a0=a0+h7;a1=a1+h8;a3=math.sqrt(a0*a0+a1*a1)if not J and o()==0 then if userControlScheme=="virtual joystick"then if a0>0 and a0>DeadZone then D=D-(a0-DeadZone)*MouseXSensitivity elseif a0<0 and a00 and a1>DeadZone then C=C-(a1-DeadZone)*MouseYSensitivity elseif a1<0 and a1DeadZone then DrawCursorLine(bz)end else SetButtonContains()DrawButtons(bz)end;bz[#bz+1]=e([[]],s,t,a0,a1)end;bz[#bz+1]=[[]]content=table.concat(bz,"")if not DidLogOutput then system.logInfo(LastContent)DidLogOutput=true end;if ProgradeIsOn then if velMag>w then local hb=AlignToWorldVector(vec3(velocity),0.01)if a7 then autoRoll=true;if hb then ProgradeIsOn=false;x=true;BeginReentry()a7=false;a9=true;autoRoll=autoRollPreference end end end end;if RetrogradeIsOn then if ad then RetrogradeIsOn=false elseif velMag>w then AlignToWorldVector(-vec3(velocity))end end;if not ProgradeIsOn and a7 then if j()==0 then x=true;BeginReentry()a7=false;a9=true else a7=false;ToggleAutopilot()end end;if a9 and aeReentrySpeed-100 then ToggleAutopilot()a9=false end;if Autopilot and j()==0 then local P,Q;if not TurnBurn then P,Q=GetAutopilotBrakeDistanceAndTime(velMag)else P,Q=GetAutopilotTBBrakeDistanceAndTime(velMag)end;P=P;Q=Q;local hc=AutopilotTargetCoords;if orbit.apoapsis==nil and velMag>300 and AutopilotAccelerating then local hd=(vec3(AutopilotTargetCoords)-vec3(core.getConstructWorldPos())):normalize()-vec3(velocity):normalize()local he=getMagnitudeInDirection(hd,AutopilotShipUp)local hf=getMagnitudeInDirection(hd,AutopilotShipRight)local hg=-hf*AutopilotDistance*velMag*TrajectoryAlignmentStrength;local hh=-he*AutopilotDistance*velMag*TrajectoryAlignmentStrength;hc=AutopilotTargetCoords+-hg*vec3(AutopilotShipRight)+-hh*vec3(AutopilotShipUp)end;AutopilotDistance=(vec3(hc)-vec3(core.getConstructWorldPos())):len()local hi=(AutopilotTargetCoords-vec3(core.getConstructWorldPos())):len()system.updateData(widgetDistanceText,'{"label": "distance", "value": "'..getDistanceDisplayString(hi)..'", "unit":""}')local hj=true;local hk=(V.center-(vec3(core.getConstructWorldPos())+vec3(velocity):normalize()*AutopilotDistance)):len()-V.radius;system.updateData(widgetTrajectoryAltitudeText,'{"label": "Projected Altitude", "value": "'..getDistanceDisplayString(hk)..'", "unit":""}')if not AutopilotCruising and not AutopilotBraking then hj=AlignToWorldVector((hc-vec3(core.getConstructWorldPos())):normalize())elseif TurnBurn then hj=AlignToWorldVector(-vec3(velocity):normalize())end;if AutopilotAccelerating then if not hj or BrakeIsOn then AutopilotStatus="Adjusting Trajectory"else AutopilotStatus="Accelerating"end;if vec3(core.getConstructWorldOrientationForward()):dot(velocity)<0 and velMag>300 then BrakeIsOn=true;Nav.axisCommandManager:setThrottleCommand(axisCommandId.longitudinal,0)u=false elseif not u then BrakeIsOn=false;Nav.axisCommandManager:setThrottleCommand(axisCommandId.longitudinal,AutopilotInterplanetaryThrottle)u=true end;if vec3(core.getVelocity()):len()>=MaxGameVelocity and math.abs(hk-AutopilotTargetOrbit)<1000 or unit.getThrottle()==0 and u then AutopilotAccelerating=false;AutopilotStatus="Cruising"AutopilotCruising=true;Nav.axisCommandManager:setThrottleCommand(axisCommandId.longitudinal,0)u=false end;if AutopilotDistance<=P then AutopilotAccelerating=false;AutopilotStatus="Braking"AutopilotBraking=true;Nav.axisCommandManager:setThrottleCommand(axisCommandId.longitudinal,0)u=false end elseif AutopilotBraking then BrakeIsOn=true;G=1;if TurnBurn then Nav.axisCommandManager:setThrottleCommand(axisCommandId.longitudinal,100)end;if orbit.periapsis~=nil and orbit.eccentricity<1 then AutopilotStatus="Circularizing"if orbit.eccentricity>L or orbit.apoapsis.altitude0 then AutopilotAccelerating=true;AutopilotStatus="Accelerating"AutopilotCruising=false end else if hj then if not AutopilotRealigned then AutopilotTargetCoords=vec3(V.center)+(AutopilotTargetOrbit+V.radius)*vec3(core.getConstructWorldOrientationRight())AutopilotRealigned=true;AutopilotShipUp=core.getConstructWorldOrientationUp()AutopilotShipRight=core.getConstructWorldOrientationRight()elseif hj then AutopilotAccelerating=true;AutopilotStatus="Accelerating"if not u then Nav.axisCommandManager:setThrottleCommand(axisCommandId.longitudinal,AutopilotInterplanetaryThrottle)u=true;BrakeIsOn=false end end end end end;if I then autoRoll=true;local hl=0;local cP=vec3(core.getConstructWorldPos())+vec3(unit.getMasterPlayerRelativePosition())local hm=cP-vec3(core.getConstructWorldPos())local hn=vec3(hm):project_on(vec3(core.getConstructWorldOrientationForward())):len()local ho=vec3(hm):project_on(vec3(core.getConstructWorldOrientationRight())):len()local a3=math.sqrt(hn*hn+ho*ho)AlignToWorldVector(hm:normalize())local hp=40;local hq=a3ht then if pitchPID==nil then pitchPID=pid.new(2*0.01,0,2*0.1)end;pitchPID:inject(hl-bF)local hu=pitchPID:get()C=hu end end;local d8=vec3(core.getWorldVertical())*-1;if AltitudeHold or BrakeLanding or Reentry or VectorToTarget or LockPitch~=nil then local cO=unit.getClosestPlanetInfluence()>0;local cB=ae;local hv=HoldAltitude-cB;local hw=500+velMag;local hl=(utils.smoothstep(hv,-hw,hw)-0.5)*2*MaxPitch;if not AltitudeHold then hl=0 end;if LockPitch~=nil then if cO then hl=LockPitch else LockPitch=nil end end;autoRoll=true;if Reentry then local hx=ReentrySpeed;if Nav.axisCommandManager:getTargetSpeed(axisCommandId.longitudinal)~=hx then Nav.axisCommandManager:setTargetSpeedCommand(axisCommandId.longitudinal,hx)Nav.axisCommandManager:setTargetSpeedCommand(axisCommandId.vertical,0)Nav.axisCommandManager:setTargetSpeedCommand(axisCommandId.lateral,0)end;if not x then hl=-80;if j()>0.02 then K="PARACHUTE DEPLOYED"Reentry=false;BrakeLanding=true;hl=0;autoRoll=autoRollPreference end elseif Nav.axisCommandManager:getTargetSpeed(axisCommandId.longitudinal)==ReentrySpeed then x=false;Reentry=false;autoRoll=autoRollPreference end end;local hy=C;if velMag>w then AlignToWorldVector(vec3(velocity))end;if VectorToTarget and CustomTarget~=nil and AutopilotTargetIndex>0 then local cs=CustomTarget.position-vec3(core.getConstructWorldPos())AlignToWorldVector(cs)local hz=cs:len()-cs:project_on(d8):len()local bh=LastMaxBrakeInAtmo;local d6=velocity.x*d8.x+velocity.y*d8.y+velocity.z*d8.z;local hA=velocity:len()-math.abs(d6)local hB=vec3(core.getWorldAirFrictionAcceleration())if bh~=nil then P,Q=aZ.computeDistanceAndTime(hA,0,n(),0,0,bh+(hB:len()-hB:project_on(d8):len())*n())else P,Q=aZ.computeDistanceAndTime(hA,0,n(),0,0,LastMaxBrake+vec3(core.getWorldAirFrictionAcceleration()):len()*n())end;StrongBrakes=planet.gravity*9.80665*n()LastTargetDistance and not AltitudeHold and not AutoTakeoff then BrakeLanding=true;VectorToTarget=false end;LastTargetDistance=hz end;C=hy;local bC=vec3(core.getConstructWorldOrientationForward())local bD=vec3(core.getConstructWorldOrientationRight())local bE=vec3(core.getWorldVertical())local ev=-1;local bF=getPitch(bE,bC,bD)local ht=0.1;if BrakeLanding then hl=0;if Nav.axisCommandManager:getAxisCommandType(0)==1 then Nav.control.cancelCurrentControlMasterMode()end;Nav.axisCommandManager:setTargetGroundAltitude(500)Nav.axisCommandManager:activateGroundEngineAltitudeStabilization(500)local d6=velocity.x*d8.x+velocity.y*d8.y+velocity.z*d8.z;ev=aa;if ev>-1 then if math.abs(hl-bF)50000 then if Nav.axisCommandManager:getAxisCommandType(0)==0 then Nav.control.cancelCurrentControlMasterMode()end;if Nav.axisCommandManager:getTargetSpeed(axisCommandId.longitudinal)~=5000 then Nav.axisCommandManager:setTargetSpeedCommand(axisCommandId.longitudinal,5000)Nav.axisCommandManager:setTargetSpeedCommand(axisCommandId.vertical,0)Nav.axisCommandManager:setTargetSpeedCommand(axisCommandId.lateral,0)end end end;if math.abs(hl-bF)>ht then if pitchPID==nil then pitchPID=pid.new(8*0.01,0,8*0.1)end;pitchPID:inject(hl-bF)local hu=pitchPID:get()C=C+hu end end;L=orbit.eccentricity;if antigrav and not ExternalAGG and ae<200000 then if AntigravTargetAltitude==nil then AntigravTargetAltitude=1000 end;if desiredBaseAltitude~=AntigravTargetAltitude then desiredBaseAltitude=AntigravTargetAltitude;antigrav.setBaseAltitude(desiredBaseAltitude)end end end end;function script.onFlush()if antigrav and not ExternalAGG then if antigrav.getState()==0 and antigrav.getBaseAltitude()~=AntigravTargetAltitude then antigrav.setBaseAltitude(AntigravTargetAltitude)end end;local hC=2;pitchSpeedFactor=math.max(pitchSpeedFactor,0.01)yawSpeedFactor=math.max(yawSpeedFactor,0.01)rollSpeedFactor=math.max(rollSpeedFactor,0.01)hC=math.max(hC,0.01)brakeSpeedFactor=math.max(brakeSpeedFactor,0.01)brakeFlatFactor=math.max(brakeFlatFactor,0.01)autoRollFactor=math.max(autoRollFactor,0.01)turnAssistFactor=math.max(turnAssistFactor,0.01)local hD=utils.clamp(B+C+system.getControlDeviceForwardInput(),-1,1)local hE=utils.clamp(E+H+system.getControlDeviceYawInput(),-1,1)local hF=utils.clamp(F+D-system.getControlDeviceLeftRightInput(),-1,1)local hG=G;local hH=vec3(core.getWorldVertical())local hI=vec3(core.getConstructWorldOrientationUp())local hJ=vec3(core.getConstructWorldOrientationForward())local hK=vec3(core.getConstructWorldOrientationRight())local hL=vec3(core.getWorldVelocity())local hM=vec3(core.getWorldVelocity()):normalize()local hN=getRoll(hH,hJ,hK)local hO=math.abs(hN)local hP=utils.sign(hN)local j=j()local hQ=vec3(core.getWorldAngularVelocity())local hR=hD*pitchSpeedFactor*hK+hE*rollSpeedFactor*hJ+hF*yawSpeedFactor*hI;if hH:len()>0.01 and j>0.0 or ProgradeIsOn then local hS=1.0;if autoRoll==true and hO>hS and hE==0 then local hT=utils.clamp(0,hO-30,hO+30)if rollPID==nil then rollPID=pid.new(autoRollFactor*0.01,0,autoRollFactor*0.1)end;rollPID:inject(hT-hN)local hU=rollPID:get()hR=hR+hU*hJ end end;if hH:len()>0.01 and j>0.0 then local hV=20.0;if turnAssist==true and hO>hV and hD==0 and hF==0 then local hW=turnAssistFactor*0.1;local hX=turnAssistFactor*0.025;local hY=(hO-hV)/(180-hV)*180;local hZ=0;if hY<90 then hZ=hY/90 elseif hY<180 then hZ=(180-hY)/90 end;hZ=hZ*hZ;local h_=-hP*hX*(1.0-hZ)local i0=hW*hZ;hR=hR+i0*hK+h_*hI end end;local i1=1;local i2=0;local i3=1;local i4=hC*(hR-hQ)local i5=vec3(core.getWorldAirFrictionAngularAcceleration())i4=i4-i5;Nav:setEngineTorqueCommand('torque',i4,i1,'airfoil','','',i3)local i6=-hG*(brakeSpeedFactor*hL+brakeFlatFactor*hM)Nav:setEngineForceCommand('brake',i6)local i7=''local i8=vec3()local i9=false;local ia='thrust analog longitudinal'local ib=Nav.axisCommandManager:getAxisCommandType(axisCommandId.longitudinal)if ib==axisCommandType.byThrottle then local ic=Nav.axisCommandManager:composeAxisAccelerationFromThrottle(ia,axisCommandId.longitudinal)Nav:setEngineForceCommand(ia,ic,i1)elseif ib==axisCommandType.byTargetSpeed then local ic=Nav.axisCommandManager:composeAxisAccelerationFromTargetSpeed(axisCommandId.longitudinal)i7=i7 ..' , '..ia;i8=i8+ic;if Nav.axisCommandManager:getTargetSpeed(axisCommandId.longitudinal)==0 or Nav.axisCommandManager:getCurrentToTargetDeltaSpeed(axisCommandId.longitudinal)<-Nav.axisCommandManager:getTargetSpeedCurrentStep(axisCommandId.longitudinal)*0.5 then i9=true end end;local id='thrust analog lateral'local ie=Nav.axisCommandManager:getAxisCommandType(axisCommandId.lateral)if ie==axisCommandType.byThrottle then local ig=Nav.axisCommandManager:composeAxisAccelerationFromThrottle(id,axisCommandId.lateral)Nav:setEngineForceCommand(id,ig,i1)elseif ie==axisCommandType.byTargetSpeed then local ih=Nav.axisCommandManager:composeAxisAccelerationFromTargetSpeed(axisCommandId.lateral)i7=i7 ..' , '..id;i8=i8+ih end;local ii='thrust analog vertical'local ij=Nav.axisCommandManager:getAxisCommandType(axisCommandId.vertical)if ij==axisCommandType.byThrottle then local ik=Nav.axisCommandManager:composeAxisAccelerationFromThrottle(ii,axisCommandId.vertical)if Z~=0 or BrakeLanding and BrakeIsOn then Nav:setEngineForceCommand(ii,ik,i1,'airfoil','ground','',i3)else Nav:setEngineForceCommand(ii,vec3(),i1)Nav:setEngineForceCommand('airfoil vertical',ik,i1,'airfoil','','',i3)Nav:setEngineForceCommand('ground vertical',ik,i1,'ground','','',i3)end elseif ij==axisCommandType.byTargetSpeed then if Z==0 then Nav:setEngineForceCommand('hover',vec3(),i1)end;local il=Nav.axisCommandManager:composeAxisAccelerationFromTargetSpeed(axisCommandId.vertical)i7=i7 ..' , '..ii;i8=i8+il end;if i8:len()>constants.epsilon then if G~=0 or i9 or math.abs(hM:dot(hJ))<0.95 then i7=i7 ..', brake'end;Nav:setEngineForceCommand(i7,i8,i2,'','','',i3)end;Nav:setBoosterCommand('rocket_engine')if O and not VanillaRockets then local bg=vec3(core.getVelocity()):len()local im=0.15;if Nav.axisCommandManager:getAxisCommandType(0)==1 then local io=Nav.axisCommandManager:getTargetSpeed(axisCommandId.longitudinal)if bg*3.6>io*(1-im)and IsRocketOn then IsRocketOn=false;Nav:toggleBoosters()elseif bg*3.6=hs*(1-im)and IsRocketOn then IsRocketOn=false;Nav:toggleBoosters()elseif bg=hs*(1-im)and IsRocketOn then IsRocketOn=false;Nav:toggleBoosters()elseif bg0 or aew then K="WARNING: Insufficient Brakes - Attempting landing anyway"end;Reentry=false;AutoTakeoff=false;AltitudeHold=false;BrakeLanding=true;autoRoll=true;GearExtended=false else BrakeIsOn=true;Nav.control.extendLandingGears()Nav.axisCommandManager:setTargetGroundAltitude(LandingGearGroundHeight)end else Nav.control.retractLandingGears()Nav.axisCommandManager:setTargetGroundAltitude(TargetHoverHeight)end elseif iq=="light"then if Nav.control.isAnyHeadlightSwitchedOn()==1 then Nav.control.switchOffHeadlights()else Nav.control.switchOnHeadlights()end elseif iq=="forward"then B=B-1 elseif iq=="backward"then B=B+1 elseif iq=="left"then E=E-1 elseif iq=="right"then E=E+1 elseif iq=="yawright"then F=F-1 elseif iq=="yawleft"then F=F+1 elseif iq=="straferight"then Nav.axisCommandManager:updateCommandFromActionStart(axisCommandId.lateral,1.0)elseif iq=="strafeleft"then Nav.axisCommandManager:updateCommandFromActionStart(axisCommandId.lateral,-1.0)elseif iq=="up"then Z=Z+1;Nav.axisCommandManager:deactivateGroundEngineAltitudeStabilization()Nav.axisCommandManager:updateCommandFromActionStart(axisCommandId.vertical,1.0)elseif iq=="down"then Z=Z-1;Nav.axisCommandManager:deactivateGroundEngineAltitudeStabilization()Nav.axisCommandManager:updateCommandFromActionStart(axisCommandId.vertical,-1.0)elseif iq=="groundaltitudeup"then OldButtonMod=M;OldAntiMod=N;if antigrav and not ExternalAGG and antigrav.getState()==1 then if AntigravTargetAltitude~=nil then if AltitudeHold and AntigravTargetAltitudeHoldAltitude-10 then AntigravTargetAltitude=AntigravTargetAltitude+N;HoldAltitude=AntigravTargetAltitude else AntigravTargetAltitude=AntigravTargetAltitude+N end else AntigravTargetAltitude=desiredBaseAltitude+100 end elseif AltitudeHold then HoldAltitude=HoldAltitude+M else Nav.axisCommandManager:updateTargetGroundAltitudeFromActionStart(1.0)end elseif iq=="groundaltitudedown"then OldButtonMod=M;OldAntiMod=N;if antigrav and not ExternalAGG and antigrav.getState()==1 then if AntigravTargetAltitude~=nil then if AltitudeHold and AntigravTargetAltitudeHoldAltitude-10 then AntigravTargetAltitude=AntigravTargetAltitude-N;if AntigravTargetAltitude<1000 then AntigravTargetAltitude=1000 end;HoldAltitude=AntigravTargetAltitude else AntigravTargetAltitude=AntigravTargetAltitude-N;if AntigravTargetAltitude<1000 then AntigravTargetAltitude=1000 end end else AntigravTargetAltitude=desiredBaseAltitude end elseif AltitudeHold then HoldAltitude=HoldAltitude-M else Nav.axisCommandManager:updateTargetGroundAltitudeFromActionStart(-1.0)end elseif iq=="option1"then IncrementAutopilotTargetIndex()v=false elseif iq=="option2"then DecrementAutopilotTargetIndex()v=false elseif iq=="option3"then if hideHudOnToggleWidgets then if showHud then showHud=false else showHud=true end end;v=false;ToggleWidgets()elseif iq=="option4"then ToggleAutopilot()v=false elseif iq=="option5"then ToggleLockPitch()v=false elseif iq=="option6"then ToggleAltitudeHold()v=false elseif iq=="option7"then wipeSaveVariables()v=false elseif iq=="option8"then ToggleFollowMode()v=false elseif iq=="option9"then if gyro~=nil then gyro.toggle()ah=gyro.getState()==1 end;v=false elseif iq=="lshift"then if system.isViewLocked()==1 then J=true;PrevViewLock=system.isViewLocked()system.lockView(1)elseif o()==1 and ShiftShowsRemoteButtons then J=true;b1=false;b0=false end elseif iq=="brake"then if BrakeToggleStatus then BrakeToggle()elseif not BrakeIsOn then BrakeToggle()else BrakeIsOn=true end elseif iq=="lalt"then if o()==0 and not freeLookToggle and userControlScheme=="keyboard"then system.lockView(1)end elseif iq=="booster"then if VanillaRockets then Nav:toggleBoosters()elseif not O then if not IsRocketOn then Nav:toggleBoosters()IsRocketOn=true end;O=true else if IsRocketOn then Nav:toggleBoosters()IsRocketOn=false end;O=false end elseif iq=="stopengines"then Nav.axisCommandManager:resetCommand(axisCommandId.longitudinal)clearAll()elseif iq=="speedup"then if not J then Nav.axisCommandManager:updateCommandFromActionStart(axisCommandId.longitudinal,speedChangeLarge)else IncrementAutopilotTargetIndex()end elseif iq=="speeddown"then if not J then Nav.axisCommandManager:updateCommandFromActionStart(axisCommandId.longitudinal,-speedChangeLarge)else DecrementAutopilotTargetIndex()end elseif iq=="antigravity"and not ExternalAGG then if antigrav~=nil then ToggleAntigrav()end end end;function script.onActionStop(iq)if iq=="forward"then B=0 elseif iq=="backward"then B=0 elseif iq=="left"then E=0 elseif iq=="right"then E=0 elseif iq=="yawright"then F=0 elseif iq=="yawleft"then F=0 elseif iq=="straferight"then Nav.axisCommandManager:updateCommandFromActionStop(axisCommandId.lateral,-1.0)elseif iq=="strafeleft"then Nav.axisCommandManager:updateCommandFromActionStop(axisCommandId.lateral,1.0)elseif iq=="up"then Z=0;Nav.axisCommandManager:updateCommandFromActionStop(axisCommandId.vertical,-1.0)Nav.axisCommandManager:activateGroundEngineAltitudeStabilization(currentGroundAltitudeStabilization)Nav:setEngineForceCommand('hover',vec3(),1)elseif iq=="down"then Z=0;Nav.axisCommandManager:updateCommandFromActionStop(axisCommandId.vertical,1.0)Nav.axisCommandManager:activateGroundEngineAltitudeStabilization(currentGroundAltitudeStabilization)Nav:setEngineForceCommand('hover',vec3(),1)elseif iq=="groundaltitudeup"then if antigrav and not ExternalAGG and antigrav.getState()==1 then N=OldAntiMod end;if AltitudeHold then M=OldButtonMod end;v=false elseif iq=="groundaltitudedown"then if antigrav and not ExternalAGG and antigrav.getState()==1 then N=OldAntiMod end;if AltitudeHold then M=OldButtonMod end;v=false elseif iq=="lshift"then if system.isViewLocked()==1 then J=false;a0=0;a1=0;system.lockView(PrevViewLock)elseif o()==1 and ShiftShowsRemoteButtons then J=false;b1=false;b0=false end elseif iq=="brake"then if not BrakeToggleStatus then if BrakeIsOn then BrakeToggle()else BrakeIsOn=false end end elseif iq=="lalt"then if o()==0 and freeLookToggle then if v then if system.isViewLocked()==1 then system.lockView(0)else system.lockView(1)end else v=true end elseif o()==0 and not freeLookToggle and userControlScheme=="keyboard"then system.lockView(0)end end end;function script.onActionLoop(iq)if iq=="groundaltitudeup"then if antigrav and not ExternalAGG and antigrav.getState()==1 then if AntigravTargetAltitude~=nil then if AltitudeHold and AntigravTargetAltitudeHoldAltitude-10 then AntigravTargetAltitude=AntigravTargetAltitude+N;HoldAltitude=AntigravTargetAltitude else AntigravTargetAltitude=AntigravTargetAltitude+N end;N=N*1.05;BrakeIsOn=false else AntigravTargetAltitude=desiredBaseAltitude+100;BrakeIsOn=false end elseif AltitudeHold then HoldAltitude=HoldAltitude+M;M=M*1.05 else Nav.axisCommandManager:updateTargetGroundAltitudeFromActionLoop(1.0)end elseif iq=="groundaltitudedown"then if antigrav and not ExternalAGG and antigrav.getState()==1 then if AntigravTargetAltitude~=nil then if AltitudeHold and AntigravTargetAltitudeHoldAltitude-10 then AntigravTargetAltitude=AntigravTargetAltitude-N;if AntigravTargetAltitude<1000 then AntigravTargetAltitude=1000 end;HoldAltitude=AntigravTargetAltitude else AntigravTargetAltitude=AntigravTargetAltitude-N;if AntigravTargetAltitude<1000 then AntigravTargetAltitude=1000 end end;N=N*1.05;BrakeIsOn=false else AntigravTargetAltitude=desiredBaseAltitude-100;BrakeIsOn=false end elseif AltitudeHold then HoldAltitude=HoldAltitude-M;M=M*1.05 else Nav.axisCommandManager:updateTargetGroundAltitudeFromActionLoop(-1.0)end elseif iq=="speedup"then if not J then Nav.axisCommandManager:updateCommandFromActionLoop(axisCommandId.longitudinal,speedChangeSmall)end elseif iq=="speeddown"then if not J then Nav.axisCommandManager:updateCommandFromActionLoop(axisCommandId.longitudinal,-speedChangeSmall)end end end;script.onStart() -- error handling code added by wrap.lua diff --git a/ChangeLog.md b/ChangeLog.md index 45af7b6..573d30e 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -1,5 +1,10 @@ ## ChangeLog - Most recent changes at the top +Version 4.914 +- Fix versioning issue from 4.913 for some reason +- Major refactor of function definitions outside of script.onStart() (users ignore this) +- Begin adding remarks to code to help explain things + Version 4.913 - Added ContainerOptimization user setting in Edit LUA Parameters, default 0. (This is NOT Fuel Tank Handling, but instead the skill in Stock Control of Mining and Industry). To get accurate values of UNSLOTTED fuel tanks, you must set this, FuelTankOptimization and the diff --git a/src/ButtonHUD.lua b/src/ButtonHUD.lua index 65b3f54..393273b 100644 --- a/src/ButtonHUD.lua +++ b/src/ButtonHUD.lua @@ -1,5 +1,5 @@ require 'src.slots' - +-- Script is laid out variables, functions, control, control (the Hud proper) starts around line 4000 Nav = Navigator.new(system, core, unit) script = {} -- wrappable container for all the code. Different than normal DU Lua in that things are not seperated out. @@ -13,8 +13,8 @@ RemoteHud = false -- export: Whether you want full HUD while in remote mode, exp brightHud = false -- export: Enable to prevent hud dimming when in freelook. VanillaRockets = false -- export: If on, rockets behave like vanilla userControlScheme = "virtual joystick" -- export: Set to "virtual joystick", "mouse", or "keyboard" -ResolutionX = 1920 -- export: Default is 1920, automatically scales, variable for use for wierd resolutions (1920x1200, etc) -ResolutionY = 1080 -- export: Default is 1080, automatically scales, variable for use for wierd resolutions (1920x1200, etc) +ResolutionX = 1920 -- export: Default is 1920, does not need to be set to same as game resolution. You can set 1920 on a 2560 to get larger resolution +ResolutionY = 1080 -- export: Default is 1080, does not need to be set to same as game resolution. You can set 1080 on a 1440 to get larger resolution PrimaryR = 130 -- export: Primary HUD color PrimaryG = 224 -- export: Primary HUD color PrimaryB = 255 -- export: Primary HUD color @@ -203,8 +203,9 @@ local elementsID = core.getElementIdList() local lastTravelTime = system.getTime() local gyroIsOn = nil local speedLimitBreaking = false +local rgb = [[rgb(]] .. mfloor(PrimaryR + 0.5) .. "," .. mfloor(PrimaryG + 0.5) .. "," .. mfloor(PrimaryB + 0.5) .. [[)]] +local rgbdim = [[rgb(]] .. mfloor(PrimaryR * 0.9 + 0.5) .. "," .. mfloor(PrimaryG * 0.9 + 0.5) .. "," .. mfloor(PrimaryB * 0.9 + 0.5) .. [[)]] --- Local Variables used only within onStart local markers = {} local previousYawAmount = 0 local previousPitchAmount = 0 @@ -238,33 +239,25 @@ local fuelPercent = {} local updateTanks = false local coreOffset = 16 local updateCount = 0 - --- Start of actual HUD Script. Written by Dimencia and Archaegeo. Optimization and Automation of scripting by ChronosWS Linked sources where appropriate, most have been modified. -function script.onStart() - VERSION_NUMBER = 4.913 - SetupComplete = false - beginSetup = coroutine.create(function() - Nav.axisCommandManager:setupCustomTargetSpeedRanges(axisCommandId.longitudinal, - {1000, 5000, 10000, 20000, 30000}) - - -- BEGIN CONDITIONAL CHECKS DURING STARTUP - -- Load Saved Variables - if dbHud_1 then - local hasKey = dbHud_1.hasKey - if not useTheseSettings then - for k, v in pairs(saveableVariables) do - if hasKey(v) then - local result = jdecode(dbHud_1.getStringValue(v)) - if result ~= nil then - sprint(v .. " " .. dbHud_1.getStringValue(v)) - _G[v] = result - valuesAreSet = true - end - end - end - end - coroutine.yield() - for k, v in pairs(autoVariables) do +local atlas = nil +local GalaxyMapHTML = "" +local MapXRatio = nil +local MapYRatio = nil +local YouAreHere = nil +local PlanetaryReference = nil +local galaxyReference = nil +local Kinematic = nil +local Kep = nil +local Animating = false +local Animated = false + +-- BEGIN FUNCTION DEFINITIONS + +function LoadVariables() + if dbHud_1 then + local hasKey = dbHud_1.hasKey + if not useTheseSettings then + for k, v in pairs(saveableVariables) do if hasKey(v) then local result = jdecode(dbHud_1.getStringValue(v)) if result ~= nil then @@ -274,3823 +267,3885 @@ function script.onStart() end end end - if useTheseSettings then - msgText = "Updated user preferences used. Will be saved when you exit seat.\nToggle off useTheseSettings to use saved values" - msgTimer = 5 - elseif valuesAreSet then - msgText = "Loaded Saved Variables (see Lua Chat Tab for list)" - else - msgText = "No Saved Variables Found - Stand up / leave remote to save settings" + end + coroutine.yield() + for k, v in pairs(autoVariables) do + if hasKey(v) then + local result = jdecode(dbHud_1.getStringValue(v)) + if result ~= nil then + sprint(v .. " " .. dbHud_1.getStringValue(v)) + _G[v] = result + valuesAreSet = true + end end + end + if useTheseSettings then + msgText = "Updated user preferences used. Will be saved when you exit seat.\nToggle off useTheseSettings to use saved values" + msgTimer = 5 + elseif valuesAreSet then + msgText = "Loaded Saved Variables (see Lua Chat Tab for list)" else - msgText = "No databank found, install one anywhere and rerun the autoconfigure to save variables" + msgText = "No Saved Variables Found - Stand up / leave remote to save settings" end - coroutine.yield() -- Give it some time to breathe before we do the rest - -- Loading saved vars is hard on it - local time = system.getTime() - if (LastStartTime + 180) < time then -- Variables to reset if out of seat (and not on hud) for more than 3 min - LastMaxBrakeInAtmo = 0 - end - halfResolutionX = round(ResolutionX / 2,0) - halfResolutionY = round(ResolutionY / 2,0) - LastStartTime = time - BrakeToggleStatus = BrakeToggleDefault - userControlScheme = string.lower(userControlScheme) - if string.find("keyboard virtual joystick mouse", userControlScheme) == nil then - msgText = "Invalid User Control Scheme selected. Change userControlScheme in Lua Parameters to keyboard, mouse, or virtual joystick" - end - MinimumRateOfChange = math.cos(StallAngle*constants.deg2rad) - autoRoll = autoRollPreference - if antigrav and not ExternalAGG then - if AntigravTargetAltitude == nil then - AntigravTargetAltitude = coreAltitude - end - antigrav.setBaseAltitude(AntigravTargetAltitude) + else + msgText = "No databank found, install one anywhere and rerun the autoconfigure to save variables" + end + local time = system.getTime() + if (LastStartTime + 180) < time then -- Variables to reset if out of seat (and not on hud) for more than 3 min + LastMaxBrakeInAtmo = 0 + end + halfResolutionX = round(ResolutionX / 2,0) + halfResolutionY = round(ResolutionY / 2,0) + LastStartTime = time + BrakeToggleStatus = BrakeToggleDefault + userControlScheme = string.lower(userControlScheme) + if string.find("keyboard virtual joystick mouse", userControlScheme) == nil then + msgText = "Invalid User Control Scheme selected. Change userControlScheme in Lua Parameters to keyboard, mouse, or virtual joystick\nOr use shift and button in screen" + msgTimer = 5 + end + MinimumRateOfChange = math.cos(StallAngle*constants.deg2rad) + autoRoll = autoRollPreference + if antigrav and not ExternalAGG then + if AntigravTargetAltitude == nil then + AntigravTargetAltitude = coreAltitude end - local rgb = [[rgb(]] .. mfloor(PrimaryR + 0.5) .. "," .. mfloor(PrimaryG + 0.5) .. "," .. mfloor(PrimaryB + 0.5) .. - [[)]] - local rgbdim = [[rgb(]] .. mfloor(PrimaryR * 0.9 + 0.5) .. "," .. mfloor(PrimaryG * 0.9 + 0.5) .. "," .. - mfloor(PrimaryB * 0.9 + 0.5) .. [[)]] - - -- Find elements we care about - for k in pairs(elementsID) do + antigrav.setBaseAltitude(AntigravTargetAltitude) + end + rgb = [[rgb(]] .. mfloor(PrimaryR + 0.5) .. "," .. mfloor(PrimaryG + 0.5) .. "," .. mfloor(PrimaryB + 0.5) .. + [[)]] + rgbdim = [[rgb(]] .. mfloor(PrimaryR * 0.9 + 0.5) .. "," .. mfloor(PrimaryG * 0.9 + 0.5) .. "," .. + mfloor(PrimaryB * 0.9 + 0.5) .. [[)]] +end - local type = eleType(elementsID[k]) - if (type == "landing gear") then - hasGear = true - end - if (type == "dynamic core") then +function CalculateFuelVolume(curMass, vanillaMaxVolume) + if curMass > vanillaMaxVolume then + vanillaMaxVolume = curMass + end + if ContainerOptimization > 0 then + vanillaMaxVolume = vanillaMaxVolume - (vanillaMaxVolume * ContainerOptimization * 0.05) + end + if FuelTankOptimization > 0 then + vanillaMaxVolume = vanillaMaxVolume - (vanillaMaxVolume * FuelTankOptimization * 0.05) + end + return vanillaMaxVolume +end + +function ProcessElements() + for k in pairs(elementsID) do + local type = eleType(elementsID[k]) + if (type == "landing gear") then + hasGear = true + end + if (type == "dynamic core") then + local hp = eleMaxHp(elementsID[k]) + if hp > 10000 then + coreOffset = 128 + elseif hp > 1000 then + coreOffset = 64 + elseif hp > 150 then + coreOffset = 32 + end + end + eleTotalMaxHp = eleTotalMaxHp + eleMaxHp(elementsID[k]) + if (fuelX ~= 0 and fuelY ~= 0) then + if (type == "Atmospheric Fuel Tank" or type == "Space Fuel Tank" or type == "Rocket Fuel Tank") then local hp = eleMaxHp(elementsID[k]) - if hp > 10000 then - coreOffset = 128 - elseif hp > 1000 then - coreOffset = 64 - elseif hp > 150 then - coreOffset = 32 - end - end - eleTotalMaxHp = eleTotalMaxHp + eleMaxHp(elementsID[k]) - if (fuelX ~= 0 and fuelY ~= 0) then - if (type == "Atmospheric Fuel Tank" or type == "Space Fuel Tank" or type == "Rocket Fuel Tank") then - local hp = eleMaxHp(elementsID[k]) - local mass = eleMass(elementsID[k]) - local curMass = 0 - local curTime = system.getTime() - if (type == "Atmospheric Fuel Tank") then - local vanillaMaxVolume = 400 - local massEmpty = 35.03 - if hp > 10000 then - vanillaMaxVolume = 51200 -- volume in kg of L tank - massEmpty = 5480 - elseif hp > 1300 then - vanillaMaxVolume = 6400 -- volume in kg of M - massEmpty = 988.67 - elseif hp > 150 then - vanillaMaxVolume = 1600 --- volume in kg small - massEmpty = 182.67 - end - curMass = mass - massEmpty - if fuelTankHandlingAtmo > 0 then - vanillaMaxVolume = vanillaMaxVolume + (vanillaMaxVolume * (fuelTankHandlingAtmo * 0.2)) - end - if curMass > vanillaMaxVolume then - vanillaMaxVolume = curMass - end - if ContainerOptimization > 0 then - vanillaMaxVolume = vanillaMaxVolume - (vanillaMaxVolume * ContainerOptimization * 0.05) - end - if FuelTankOptimization > 0 then - vanillaMaxVolume = vanillaMaxVolume - (vanillaMaxVolume * FuelTankOptimization * 0.05) - end - atmoTanks[#atmoTanks + 1] = {elementsID[k], core.getElementNameById(elementsID[k]), - vanillaMaxVolume, massEmpty, curMass, curTime} + local mass = eleMass(elementsID[k]) + local curMass = 0 + local curTime = system.getTime() + if (type == "Atmospheric Fuel Tank") then + local vanillaMaxVolume = 400 + local massEmpty = 35.03 + if hp > 10000 then + vanillaMaxVolume = 51200 -- volume in kg of L tank + massEmpty = 5480 + elseif hp > 1300 then + vanillaMaxVolume = 6400 -- volume in kg of M + massEmpty = 988.67 + elseif hp > 150 then + vanillaMaxVolume = 1600 --- volume in kg small + massEmpty = 182.67 end - if (type == "Rocket Fuel Tank") then - local vanillaMaxVolume = 320 - local massEmpty = 173.42 - if hp > 65000 then - vanillaMaxVolume = 40000 -- volume in kg of L tank - massEmpty = 25740 - elseif hp > 6000 then - vanillaMaxVolume = 5120 -- volume in kg of M - massEmpty = 4720 - elseif hp > 700 then - vanillaMaxVolume = 640 --- volume in kg small - massEmpty = 886.72 - end - curMass = mass - massEmpty - if fuelTankHandlingRocket > 0 then - vanillaMaxVolume = vanillaMaxVolume + (vanillaMaxVolume * (fuelTankHandlingRocket * 0.2)) - end - if curMass > vanillaMaxVolume then - vanillaMaxVolume = curMass - end - if ContainerOptimization > 0 then - vanillaMaxVolume = vanillaMaxVolume - (vanillaMaxVolume * ContainerOptimization * 0.05) - end - if FuelTankOptimization > 0 then - vanillaMaxVolume = vanillaMaxVolume - (vanillaMaxVolume * FuelTankOptimization * 0.05) - end - rocketTanks[#rocketTanks + 1] = {elementsID[k], core.getElementNameById(elementsID[k]), - vanillaMaxVolume, massEmpty, curMass, curTime} + curMass = mass - massEmpty + if fuelTankHandlingAtmo > 0 then + vanillaMaxVolume = vanillaMaxVolume + (vanillaMaxVolume * (fuelTankHandlingAtmo * 0.2)) end - if (type == "Space Fuel Tank") then - local vanillaMaxVolume = 2400 - local massEmpty = 182.67 - if hp > 10000 then - vanillaMaxVolume = 76800 -- volume in kg of L tank - massEmpty = 5480 - elseif hp > 1300 then - vanillaMaxVolume = 9600 -- volume in kg of M - massEmpty = 988.67 - end - curMass = mass - massEmpty - if fuelTankHandlingSpace > 0 then - vanillaMaxVolume = vanillaMaxVolume + (vanillaMaxVolume * (fuelTankHandlingSpace * 0.2)) - end - if curMass > vanillaMaxVolume then - vanillaMaxVolume = curMass - end - if ContainerOptimization > 0 then - vanillaMaxVolume = vanillaMaxVolume - (vanillaMaxVolume * ContainerOptimization * 0.05) - end - if FuelTankOptimization > 0 then - vanillaMaxVolume = vanillaMaxVolume - (vanillaMaxVolume * FuelTankOptimization * 0.05) - end - spaceTanks[#spaceTanks + 1] = {elementsID[k], core.getElementNameById(elementsID[k]), + vanillaMaxVolume = CalculateFuelVolume(curMass, vanillaMaxVolume) + atmoTanks[#atmoTanks + 1] = {elementsID[k], core.getElementNameById(elementsID[k]), + vanillaMaxVolume, massEmpty, curMass, curTime} + end + if (type == "Rocket Fuel Tank") then + local vanillaMaxVolume = 320 + local massEmpty = 173.42 + if hp > 65000 then + vanillaMaxVolume = 40000 -- volume in kg of L tank + massEmpty = 25740 + elseif hp > 6000 then + vanillaMaxVolume = 5120 -- volume in kg of M + massEmpty = 4720 + elseif hp > 700 then + vanillaMaxVolume = 640 --- volume in kg small + massEmpty = 886.72 + end + curMass = mass - massEmpty + if fuelTankHandlingRocket > 0 then + vanillaMaxVolume = vanillaMaxVolume + (vanillaMaxVolume * (fuelTankHandlingRocket * 0.2)) + end + vanillaMaxVolume = CalculateFuelVolume(curMass, vanillaMaxVolume) + rocketTanks[#rocketTanks + 1] = {elementsID[k], core.getElementNameById(elementsID[k]), vanillaMaxVolume, massEmpty, curMass, curTime} + end + if (type == "Space Fuel Tank") then + local vanillaMaxVolume = 2400 + local massEmpty = 182.67 + if hp > 10000 then + vanillaMaxVolume = 76800 -- volume in kg of L tank + massEmpty = 5480 + elseif hp > 1300 then + vanillaMaxVolume = 9600 -- volume in kg of M + massEmpty = 988.67 + end + curMass = mass - massEmpty + if fuelTankHandlingSpace > 0 then + vanillaMaxVolume = vanillaMaxVolume + (vanillaMaxVolume * (fuelTankHandlingSpace * 0.2)) end + vanillaMaxVolume = CalculateFuelVolume(curMass, vanillaMaxVolume) + spaceTanks[#spaceTanks + 1] = {elementsID[k], core.getElementNameById(elementsID[k]), + vanillaMaxVolume, massEmpty, curMass, curTime} end end end - coroutine.yield() -- Give it some time to breathe before we do the rest - if gyro ~= nil then - gyroIsOn = gyro.getState() == 1 - end + end +end - if userControlScheme ~= "keyboard" then - system.lockView(1) +function SetupChecks() + if gyro ~= nil then + gyroIsOn = gyro.getState() == 1 + end + if userControlScheme ~= "keyboard" then + system.lockView(1) + else + system.lockView(0) + end + if inAtmo then + BrakeIsOn = true + end + if radar_1 then + if eleType(radar_1.getId()) == "Space Radar" then + hasSpaceRadar = true else - system.lockView(0) + hasAtmoRadar = true end - if inAtmo then - BrakeIsOn = true + end + -- Close door and retract ramp if available + if door then + for _, v in pairs(door) do + v.toggle() end - if radar_1 then - if eleType(radar_1.getId()) == "Space Radar" then - hasSpaceRadar = true - else - hasAtmoRadar = true - end + end + if switch then + for _, v in pairs(switch) do + v.toggle() end - -- Close door and retract ramp if available - if door then - for _, v in pairs(door) do - v.toggle() - end + end + if forcefield then + for _, v in pairs(forcefield) do + v.toggle() end - if switch then - for _, v in pairs(switch) do - v.toggle() - end + end + if antigrav ~= nil and not ExternalAGG then + if(antigrav.getState() == 1) then + antigrav.show() end - if forcefield then - for _, v in pairs(forcefield) do - v.toggle() - end + end + -- unfreeze the player if he is remote controlling the construct + if isRemote() == 1 and RemoteFreeze then + system.freeze(1) + else + system.freeze(0) + end + if hasGear then + GearExtended = (Nav.control.isAnyLandingGearExtended() == 1) + if GearExtended then + Nav.control.extendLandingGears() + else + Nav.control.retractLandingGears() end - if antigrav ~= nil and not ExternalAGG then - if(antigrav.getState() == 1) then - antigrav.show() - end + end + if TargetGroundAltitude ~= nil then + Nav.axisCommandManager:setTargetGroundAltitude(TargetGroundAltitude) + if TargetGroundAltitude == 0 and not hasGear then + GearExtended = true + end + else + if GearExtended or not hasGear then + Nav.axisCommandManager:setTargetGroundAltitude(LandingGearGroundHeight) + GearExtended = true + else + Nav.axisCommandManager:setTargetGroundAltitude(TargetHoverHeight) end + end + if inAtmo and not dbHud_1 and (GearExtended or not hasGear) then + BrakeIsOn = true + end + WasInAtmo = inAtmo +end - -- unfreeze the player if he is remote controlling the construct +function ConvertResolutionX (v) + if ResolutionX == 1920 then + return v + else + return round(ResolutionX * v / 1920, 0) + end +end - if isRemote() == 1 and RemoteFreeze then - system.freeze(1) - else - system.freeze(0) - end - if hasGear then - GearExtended = (Nav.control.isAnyLandingGearExtended() == 1) - if GearExtended then - Nav.control.extendLandingGears() - else - Nav.control.retractLandingGears() - end +function ConvertResolutionY (v) + if ResolutionY == 1080 then + return v + else + return round(ResolutionY * v / 1080, 0) + end +end + +function RefreshLastMaxBrake(gravity, force) + if gravity == nil then + gravity = core.g() + end + gravity = round(gravity, 5) -- round to avoid insignificant updates + local atmoden = atmosphere() + if (force ~= nil and force) or (lastMaxBrakeAtG == nil or lastMaxBrakeAtG ~= gravity) then + local velocity = core.getVelocity() + local speed = vec3(velocity):len() + local maxBrake = jdecode(unit.getData()).maxBrake + if maxBrake ~= nil and maxBrake > 0 and inAtmo then + maxBrake = maxBrake / utils.clamp(speed/100, 0.1, 1) + maxBrake = maxBrake / atmoden + if maxBrake > LastMaxBrakeInAtmo and atmoden > 0.10 then LastMaxBrakeInAtmo = maxBrake end + end + if maxBrake ~= nil and maxBrake > 0 then + LastMaxBrake = maxBrake + end + lastMaxBrakeAtG = gravity + end +end + +function MakeButton(enableName, disableName, width, height, x, y, toggleVar, toggleFunction, drawCondition) + local newButton = { + enableName = enableName, + disableName = disableName, + width = width, + height = height, + x = x, + y = y, + toggleVar = toggleVar, + toggleFunction = toggleFunction, + drawCondition = drawCondition, + hovered = false + } + table.insert(Buttons, newButton) + return newButton -- readonly, I don't think it will be saved if we change these? Maybe. + +end + +function UpdateAtlasLocationsList() + AtlasOrdered = {} + for k, v in pairs(atlas[0]) do + table.insert(AtlasOrdered, { name = v.name, index = k} ) + end + local function atlasCmp (left, right) + return left.name < right.name + end + + table.sort(AtlasOrdered, atlasCmp) +end + +function AddLocationsToAtlas() -- Just called once during init really + for k, v in pairs(SavedLocations) do + table.insert(atlas[0], v) + end + UpdateAtlasLocationsList() +end + +function AddNewLocation() -- Don't call this unless they have a databank or it's kinda pointless + -- Add a new location to SavedLocations + if dbHud_1 then + local position = vec3(core.getConstructWorldPos()) + local name = planet.name .. ". " .. #SavedLocations + + if radar_1 then -- Just match the first one + local id,_ = radar_1.getData():match('"constructId":"([0-9]*)","distance":([%d%.]*)') + if id ~= nil and id ~= "" then + name = name .. " " .. radar_1.getConstructName(id) + end + end + local newLocation = { + position = position, + name = name, + atmosphere = atmosphere(), + planetname = planet.name, + gravity = unit.getClosestPlanetInfluence() + } + SavedLocations[#SavedLocations + 1] = newLocation + -- Nearest planet, gravity also important - if it's 0, we don't autopilot to the target planet, the target isn't near a planet. + table.insert(atlas[0], newLocation) + UpdateAtlasLocationsList() + -- Store atmosphere so we know whether the location is in space or not + msgText = "Location saved as " .. name + else + msgText = "Databank must be installed to save locations" + end +end + +function UpdatePosition() + local index = -1 + local newLocation + for k, v in pairs(SavedLocations) do + if v.name and v.name == CustomTarget.name then + --msgText = v.name .. " saved location cleared" + index = k + break end - if TargetGroundAltitude ~= nil then - Nav.axisCommandManager:setTargetGroundAltitude(TargetGroundAltitude) - if TargetGroundAltitude == 0 and not hasGear then - GearExtended = true - end - else - if GearExtended or not hasGear then - Nav.axisCommandManager:setTargetGroundAltitude(LandingGearGroundHeight) - GearExtended = true - else - Nav.axisCommandManager:setTargetGroundAltitude(TargetHoverHeight) + end + if index ~= -1 then + newLocation = { + position = vec3(core.getConstructWorldPos()), + name = SavedLocations[index].name, + atmosphere = unit.getAtmosphereDensity(), + planetname = planet.name, + gravity = unit.getClosestPlanetInfluence() + } + + SavedLocations[index] = newLocation + index = -1 + for k, v in pairs(atlas[0]) do + if v.name and v.name == CustomTarget.name then + index = k end end - if inAtmo and not dbHud_1 and (GearExtended or not hasGear) then - BrakeIsOn = true + if index > -1 then + atlas[0][index] = newLocation end - WasInAtmo = inAtmo - unit.hide() + UpdateAtlasLocationsList() + msgText = CustomTarget.name .. " position updated" + end +end - -- BEGIN FUNCTION DEFINITIONS - - function ConvertResolutionX (v) - if ResolutionX == 1920 then - return v - else - return round(ResolutionX * v / 1920, 0) - end +function ClearCurrentPosition() + -- So AutopilotTargetIndex is special and not a real index. We have to do this by hand. + local index = -1 + for k, v in pairs(atlas[0]) do + if v.name and v.name == CustomTarget.name then + index = k end - - function ConvertResolutionY (v) - if ResolutionY == 1080 then - return v - else - return round(ResolutionY * v / 1080, 0) - end + end + if index > -1 then + table.remove(atlas[0], index) + end + -- And SavedLocations + index = -1 + for k, v in pairs(SavedLocations) do + if v.name and v.name == CustomTarget.name then + msgText = v.name .. " saved location cleared" + index = k + break end + end + if index ~= -1 then + table.remove(SavedLocations, index) + end + DecrementAutopilotTargetIndex() + UpdateAtlasLocationsList() +end - function RefreshLastMaxBrake(gravity, force) - if gravity == nil then - gravity = core.g() - end - gravity = round(gravity, 5) -- round to avoid insignificant updates - local atmoden = atmosphere() - if (force ~= nil and force) or (lastMaxBrakeAtG == nil or lastMaxBrakeAtG ~= gravity) then - local velocity = core.getVelocity() - local speed = vec3(velocity):len() - local maxBrake = jdecode(unit.getData()).maxBrake - if maxBrake ~= nil and maxBrake > 0 and inAtmo then - maxBrake = maxBrake / utils.clamp(speed/100, 0.1, 1) - maxBrake = maxBrake / atmoden - if maxBrake > LastMaxBrakeInAtmo and atmoden > 0.10 then LastMaxBrakeInAtmo = maxBrake end - end - if maxBrake ~= nil and maxBrake > 0 then - LastMaxBrake = maxBrake - end - lastMaxBrakeAtG = gravity - end - end +function DrawDeadZone(newContent) + newContent[#newContent + 1] = stringf( + [[]], + DeadZone) +end - function MakeButton(enableName, disableName, width, height, x, y, toggleVar, toggleFunction, drawCondition) - local newButton = { - enableName = enableName, - disableName = disableName, - width = width, - height = height, - x = x, - y = y, - toggleVar = toggleVar, - toggleFunction = toggleFunction, - drawCondition = drawCondition, - hovered = false - } - table.insert(Buttons, newButton) - return newButton -- readonly, I don't think it will be saved if we change these? Maybe. - +function ToggleRadarPanel() + if radarPanelID ~= nil and peris == 0 then + system.destroyWidgetPanel(radarPanelID) + radarPanelID = nil + if perisPanelID ~= nil then + system.destroyWidgetPanel(perisPanelID) + perisPanelID = nil end + else + -- If radar is installed but no weapon, don't show periscope + if peris == 1 then + system.destroyWidgetPanel(radarPanelID) + radarPanelID = nil + _autoconf.displayCategoryPanel(radar, radar_size, L_TEXT("ui_lua_widget_periscope", "Periscope"), + "periscope") + perisPanelID = _autoconf.panels[_autoconf.panels_size] + end + placeRadar = true + if radarPanelID == nil and placeRadar then + _autoconf.displayCategoryPanel(radar, radar_size, L_TEXT("ui_lua_widget_radar", "Radar"), "radar") + radarPanelID = _autoconf.panels[_autoconf.panels_size] + placeRadar = false + end + peris = 0 + end +end - function UpdateAtlasLocationsList() - AtlasOrdered = {} - for k, v in pairs(atlas[0]) do - table.insert(AtlasOrdered, { name = v.name, index = k} ) - end - local function atlasCmp (left, right) - return left.name < right.name - end - - table.sort(AtlasOrdered, atlasCmp) +function ToggleWidgets() + if UnitHidden then + unit.show() + core.show() + if atmofueltank_size > 0 then + _autoconf.displayCategoryPanel(atmofueltank, atmofueltank_size, + L_TEXT("ui_lua_widget_atmofuel", "Atmo Fuel"), "fuel_container") + fuelPanelID = _autoconf.panels[_autoconf.panels_size] + end + if spacefueltank_size > 0 then + _autoconf.displayCategoryPanel(spacefueltank, spacefueltank_size, + L_TEXT("ui_lua_widget_spacefuel", "Space Fuel"), "fuel_container") + spacefuelPanelID = _autoconf.panels[_autoconf.panels_size] + end + if rocketfueltank_size > 0 then + _autoconf.displayCategoryPanel(rocketfueltank, rocketfueltank_size, + L_TEXT("ui_lua_widget_rocketfuel", "Rocket Fuel"), "fuel_container") + rocketfuelPanelID = _autoconf.panels[_autoconf.panels_size] + end + UnitHidden = false + else + unit.hide() + core.hide() + if fuelPanelID ~= nil then + system.destroyWidgetPanel(fuelPanelID) + fuelPanelID = nil end - - function AddLocationsToAtlas() -- Just called once during init really - for k, v in pairs(SavedLocations) do - table.insert(atlas[0], v) - end - UpdateAtlasLocationsList() - end - - function AddNewLocation() -- Don't call this unless they have a databank or it's kinda pointless - -- Add a new location to SavedLocations - if dbHud_1 then - local position = vec3(core.getConstructWorldPos()) - local name = planet.name .. ". " .. #SavedLocations - - if radar_1 then -- Just match the first one - local id,_ = radar_1.getData():match('"constructId":"([0-9]*)","distance":([%d%.]*)') - if id ~= nil and id ~= "" then - name = name .. " " .. radar_1.getConstructName(id) - end - end - local newLocation = { - position = position, - name = name, - atmosphere = atmosphere(), - planetname = planet.name, - gravity = unit.getClosestPlanetInfluence() - } - SavedLocations[#SavedLocations + 1] = newLocation - -- Nearest planet, gravity also important - if it's 0, we don't autopilot to the target planet, the target isn't near a planet. - table.insert(atlas[0], newLocation) - UpdateAtlasLocationsList() - -- Store atmosphere so we know whether the location is in space or not - msgText = "Location saved as " .. name - else - msgText = "Databank must be installed to save locations" - end + if spacefuelPanelID ~= nil then + system.destroyWidgetPanel(spacefuelPanelID) + spacefuelPanelID = nil end - - function UpdatePosition() - local index = -1 - local newLocation - for k, v in pairs(SavedLocations) do - if v.name and v.name == CustomTarget.name then - --msgText = v.name .. " saved location cleared" - index = k - break - end - end - if index ~= -1 then - newLocation = { - position = vec3(core.getConstructWorldPos()), - name = SavedLocations[index].name, - atmosphere = unit.getAtmosphereDensity(), - planetname = planet.name, - gravity = unit.getClosestPlanetInfluence() - } - - SavedLocations[index] = newLocation - index = -1 - for k, v in pairs(atlas[0]) do - if v.name and v.name == CustomTarget.name then - index = k - end - end - if index > -1 then - atlas[0][index] = newLocation - end - UpdateAtlasLocationsList() - msgText = CustomTarget.name .. " position updated" - end + if rocketfuelPanelID ~= nil then + system.destroyWidgetPanel(rocketfuelPanelID) + rocketfuelPanelID = nil end + UnitHidden = true + end +end - function ClearCurrentPosition() - -- So AutopilotTargetIndex is special and not a real index. We have to do this by hand. - local index = -1 - for k, v in pairs(atlas[0]) do - if v.name and v.name == CustomTarget.name then - index = k - end - end - if index > -1 then - table.remove(atlas[0], index) - end - -- And SavedLocations - index = -1 - for k, v in pairs(SavedLocations) do - if v.name and v.name == CustomTarget.name then - msgText = v.name .. " saved location cleared" - index = k - break - end - end - if index ~= -1 then - table.remove(SavedLocations, index) - end - DecrementAutopilotTargetIndex() - UpdateAtlasLocationsList() - end - function DrawDeadZone(newContent) - newContent[#newContent + 1] = stringf( - [[]], - DeadZone) - end +function SetupInterplanetaryPanel() -- Interplanetary helper + panelInterplanetary = system.createWidgetPanel("Interplanetary Helper") + interplanetaryHeader = system.createWidget(panelInterplanetary, "value") + interplanetaryHeaderText = system.createData('{"label": "Target Planet", "value": "N/A", "unit":""}') + system.addDataToWidget(interplanetaryHeaderText, interplanetaryHeader) + widgetDistance = system.createWidget(panelInterplanetary, "value") + widgetDistanceText = system.createData('{"label": "distance", "value": "N/A", "unit":""}') + system.addDataToWidget(widgetDistanceText, widgetDistance) + widgetTravelTime = system.createWidget(panelInterplanetary, "value") + widgetTravelTimeText = system.createData('{"label": "Travel Time", "value": "N/A", "unit":""}') + system.addDataToWidget(widgetTravelTimeText, widgetTravelTime) + widgetMaxMass = system.createWidget(panelInterplanetary, "value") + widgetMaxMassText = system.createData('{"label": "Maximum Mass", "value": "N/A", "unit":""}') + system.addDataToWidget(widgetMaxMassText, widgetMaxMass) + widgetCurBrakeDistance = system.createWidget(panelInterplanetary, "value") + widgetCurBrakeDistanceText = system.createData('{"label": "Cur Brake distance", "value": "N/A", "unit":""}') + if not inAtmo then + system.addDataToWidget(widgetCurBrakeDistanceText, widgetCurBrakeDistance) + end + widgetCurBrakeTime = system.createWidget(panelInterplanetary, "value") + widgetCurBrakeTimeText = system.createData('{"label": "Cur Brake Time", "value": "N/A", "unit":""}') + if not inAtmo then + system.addDataToWidget(widgetCurBrakeTimeText, widgetCurBrakeTime) + end + widgetMaxBrakeDistance = system.createWidget(panelInterplanetary, "value") + widgetMaxBrakeDistanceText = system.createData('{"label": "Max Brake distance", "value": "N/A", "unit":""}') + if not inAtmo then + system.addDataToWidget(widgetMaxBrakeDistanceText, widgetMaxBrakeDistance) + end + widgetMaxBrakeTime = system.createWidget(panelInterplanetary, "value") + widgetMaxBrakeTimeText = system.createData('{"label": "Max Brake Time", "value": "N/A", "unit":""}') + if not inAtmo then + system.addDataToWidget(widgetMaxBrakeTimeText, widgetMaxBrakeTime) + end + widgetTrajectoryAltitude = system.createWidget(panelInterplanetary, "value") + widgetTrajectoryAltitudeText = system.createData( + '{"label": "Projected Altitude", "value": "N/A", "unit":""}') + if not inAtmo then + system.addDataToWidget(widgetTrajectoryAltitudeText, widgetTrajectoryAltitude) + end +end - function ToggleRadarPanel() - if radarPanelID ~= nil and peris == 0 then - system.destroyWidgetPanel(radarPanelID) - radarPanelID = nil - if perisPanelID ~= nil then - system.destroyWidgetPanel(perisPanelID) - perisPanelID = nil - end - else - -- If radar is installed but no weapon, don't show periscope - if peris == 1 then - system.destroyWidgetPanel(radarPanelID) - radarPanelID = nil - _autoconf.displayCategoryPanel(radar, radar_size, L_TEXT("ui_lua_widget_periscope", "Periscope"), - "periscope") - perisPanelID = _autoconf.panels[_autoconf.panels_size] - end - placeRadar = true - if radarPanelID == nil and placeRadar then - _autoconf.displayCategoryPanel(radar, radar_size, L_TEXT("ui_lua_widget_radar", "Radar"), "radar") - radarPanelID = _autoconf.panels[_autoconf.panels_size] - placeRadar = false - end - peris = 0 - end - end +function Contains(mousex, mousey, x, y, width, height) + if mousex > x and mousex < (x + width) and mousey > y and mousey < (y + height) then + return true + else + return false + end +end - function ToggleWidgets() - if UnitHidden then - unit.show() - core.show() - if atmofueltank_size > 0 then - _autoconf.displayCategoryPanel(atmofueltank, atmofueltank_size, - L_TEXT("ui_lua_widget_atmofuel", "Atmo Fuel"), "fuel_container") - fuelPanelID = _autoconf.panels[_autoconf.panels_size] - end - if spacefueltank_size > 0 then - _autoconf.displayCategoryPanel(spacefueltank, spacefueltank_size, - L_TEXT("ui_lua_widget_spacefuel", "Space Fuel"), "fuel_container") - spacefuelPanelID = _autoconf.panels[_autoconf.panels_size] - end - if rocketfueltank_size > 0 then - _autoconf.displayCategoryPanel(rocketfueltank, rocketfueltank_size, - L_TEXT("ui_lua_widget_rocketfuel", "Rocket Fuel"), "fuel_container") - rocketfuelPanelID = _autoconf.panels[_autoconf.panels_size] - end - UnitHidden = false - else - unit.hide() - core.hide() - if fuelPanelID ~= nil then - system.destroyWidgetPanel(fuelPanelID) - fuelPanelID = nil - end - if spacefuelPanelID ~= nil then - system.destroyWidgetPanel(spacefuelPanelID) - spacefuelPanelID = nil - end - if rocketfuelPanelID ~= nil then - system.destroyWidgetPanel(rocketfuelPanelID) - rocketfuelPanelID = nil - end - UnitHidden = true - end - end +function ToggleTurnBurn() + TurnBurn = not TurnBurn +end - -- Interplanetary helper - function SetupInterplanetaryPanel() - panelInterplanetary = system.createWidgetPanel("Interplanetary Helper") - interplanetaryHeader = system.createWidget(panelInterplanetary, "value") - interplanetaryHeaderText = system.createData('{"label": "Target Planet", "value": "N/A", "unit":""}') - system.addDataToWidget(interplanetaryHeaderText, interplanetaryHeader) - widgetDistance = system.createWidget(panelInterplanetary, "value") - widgetDistanceText = system.createData('{"label": "distance", "value": "N/A", "unit":""}') - system.addDataToWidget(widgetDistanceText, widgetDistance) - widgetTravelTime = system.createWidget(panelInterplanetary, "value") - widgetTravelTimeText = system.createData('{"label": "Travel Time", "value": "N/A", "unit":""}') - system.addDataToWidget(widgetTravelTimeText, widgetTravelTime) - widgetMaxMass = system.createWidget(panelInterplanetary, "value") - widgetMaxMassText = system.createData('{"label": "Maximum Mass", "value": "N/A", "unit":""}') - system.addDataToWidget(widgetMaxMassText, widgetMaxMass) - widgetCurBrakeDistance = system.createWidget(panelInterplanetary, "value") - widgetCurBrakeDistanceText = system.createData('{"label": "Cur Brake distance", "value": "N/A", "unit":""}') - if not inAtmo then - system.addDataToWidget(widgetCurBrakeDistanceText, widgetCurBrakeDistance) - end - widgetCurBrakeTime = system.createWidget(panelInterplanetary, "value") - widgetCurBrakeTimeText = system.createData('{"label": "Cur Brake Time", "value": "N/A", "unit":""}') - if not inAtmo then - system.addDataToWidget(widgetCurBrakeTimeText, widgetCurBrakeTime) - end - widgetMaxBrakeDistance = system.createWidget(panelInterplanetary, "value") - widgetMaxBrakeDistanceText = system.createData('{"label": "Max Brake distance", "value": "N/A", "unit":""}') - if not inAtmo then - system.addDataToWidget(widgetMaxBrakeDistanceText, widgetMaxBrakeDistance) - end - widgetMaxBrakeTime = system.createWidget(panelInterplanetary, "value") - widgetMaxBrakeTimeText = system.createData('{"label": "Max Brake Time", "value": "N/A", "unit":""}') - if not inAtmo then - system.addDataToWidget(widgetMaxBrakeTimeText, widgetMaxBrakeTime) - end - widgetTrajectoryAltitude = system.createWidget(panelInterplanetary, "value") - widgetTrajectoryAltitudeText = system.createData( - '{"label": "Projected Altitude", "value": "N/A", "unit":""}') - if not inAtmo then - system.addDataToWidget(widgetTrajectoryAltitudeText, widgetTrajectoryAltitude) - end +function ToggleVectorToTarget() + -- This is a feature to vector toward the target destination in atmo or otherwise on-planet + -- Uses altitude hold. + VectorToTarget = not VectorToTarget + if VectorToTarget then + TurnBurn = false + if not AltitudeHold then + ToggleAltitudeHold() end + end + VectorStatus = "Proceeding to Waypoint" +end - function Contains(mousex, mousey, x, y, width, height) - if mousex > x and mousex < (x + width) and mousey > y and mousey < (y + height) then - return true - else - return false - end - end +function ToggleAutoLanding() + if BrakeLanding then + BrakeLanding = false + -- Don't disable alt hold for auto land + else + StrongBrakes = ((planet.gravity * 9.80665 * constructMass()) < LastMaxBrake) + if not StrongBrakes and velMag > minAutopilotSpeed then + msgText = "WARNING: Insufficient Brakes - Attempting coast landing, beware obstacles" + end + AltitudeHold = false + AutoTakeoff = false + LockPitch = nil + BrakeLanding = true + Nav.axisCommandManager:setThrottleCommand(axisCommandId.longitudinal, 0) + end +end - function ToggleTurnBurn() - TurnBurn = not TurnBurn +function ToggleAutoTakeoff() + if AutoTakeoff then + -- Turn it off, and also AltitudeHold cuz it's weird if you cancel and that's still going + AutoTakeoff = false + if AltitudeHold then + ToggleAltitudeHold() end + else + if not AltitudeHold then + ToggleAltitudeHold() + end + AutoTakeoff = true + HoldAltitude = coreAltitude + AutoTakeoffAltitude + GearExtended = false + Nav.control.retractLandingGears() + Nav.axisCommandManager:setTargetGroundAltitude(500) -- Hard set this for takeoff, you wouldn't use takeoff from a hangar + BrakeIsOn = true + end +end - function ToggleVectorToTarget() - -- This is a feature to vector toward the target destination in atmo or otherwise on-planet - -- Uses altitude hold. - VectorToTarget = not VectorToTarget - if VectorToTarget then - TurnBurn = false - if not AltitudeHold then - ToggleAltitudeHold() - end +function ToggleLockPitch() + if LockPitch == nil then + local constrF = vec3(core.getConstructWorldOrientationForward()) + local constrR = vec3(core.getConstructWorldOrientationRight()) + local worldV = vec3(core.getWorldVertical()) + local pitch = getPitch(worldV, constrF, constrR) + LockPitch = pitch + AutoTakeoff = false + AltitudeHold = false + BrakeLanding = false + else + LockPitch = nil + end +end + +function ToggleAltitudeHold() + AltitudeHold = not AltitudeHold + if AltitudeHold then + Autopilot = false + ProgradeIsOn = false + RetrogradeIsOn = false + followMode = false + BrakeLanding = false + Reentry = false + autoRoll = true + LockPitch = nil + if (not GearExtended and not BrakeIsOn) or not inAtmo then -- Never autotakeoff in space + AutoTakeoff = false + HoldAltitude = coreAltitude + if not spaceLaunch and Nav.axisCommandManager:getAxisCommandType(0) == 0 then + Nav.control.cancelCurrentControlMasterMode() end - VectorStatus = "Proceeding to Waypoint" + else + AutoTakeoff = true + HoldAltitude = coreAltitude + AutoTakeoffAltitude + GearExtended = false + Nav.control.retractLandingGears() + Nav.axisCommandManager:setTargetGroundAltitude(500) + BrakeIsOn = true -- Engage brake for warmup end + if spaceLaunch then HoldAltitude = 100000 end + else + autoRoll = autoRollPreference + AutoTakeoff = false + BrakeLanding = false + Reentry = false + AutoTakeoff = false + VectorToTarget = false + end +end - function ToggleAutoLanding() - if BrakeLanding then - BrakeLanding = false - -- Don't disable alt hold for auto land - else - StrongBrakes = ((planet.gravity * 9.80665 * constructMass()) < LastMaxBrake) - if not StrongBrakes and velMag > minAutopilotSpeed then - msgText = "WARNING: Insufficient Brakes - Attempting coast landing, beware obstacles" - end - AltitudeHold = false - AutoTakeoff = false - LockPitch = nil - BrakeLanding = true - Nav.axisCommandManager:setThrottleCommand(axisCommandId.longitudinal, 0) - end - end - - function ToggleAutoTakeoff() - if AutoTakeoff then - -- Turn it off, and also AltitudeHold cuz it's weird if you cancel and that's still going - AutoTakeoff = false - if AltitudeHold then - ToggleAltitudeHold() - end - else - if not AltitudeHold then - ToggleAltitudeHold() - end - AutoTakeoff = true - HoldAltitude = coreAltitude + AutoTakeoffAltitude - GearExtended = false - Nav.control.retractLandingGears() - Nav.axisCommandManager:setTargetGroundAltitude(500) -- Hard set this for takeoff, you wouldn't use takeoff from a hangar - BrakeIsOn = true - end - end - - function ToggleLockPitch() - if LockPitch == nil then - local constrF = vec3(core.getConstructWorldOrientationForward()) - local constrR = vec3(core.getConstructWorldOrientationRight()) - local worldV = vec3(core.getWorldVertical()) - local pitch = getPitch(worldV, constrF, constrR) - LockPitch = pitch - AutoTakeoff = false - AltitudeHold = false - BrakeLanding = false - else - LockPitch = nil - end - end - - function ToggleAltitudeHold() - AltitudeHold = not AltitudeHold - if AltitudeHold then - Autopilot = false - ProgradeIsOn = false - RetrogradeIsOn = false - followMode = false - BrakeLanding = false - Reentry = false - autoRoll = true - LockPitch = nil - if (not GearExtended and not BrakeIsOn) or not inAtmo then -- Never autotakeoff in space - AutoTakeoff = false - HoldAltitude = coreAltitude - if not spaceLaunch and Nav.axisCommandManager:getAxisCommandType(0) == 0 then - Nav.control.cancelCurrentControlMasterMode() - end - else - AutoTakeoff = true - HoldAltitude = coreAltitude + AutoTakeoffAltitude - GearExtended = false - Nav.control.retractLandingGears() - Nav.axisCommandManager:setTargetGroundAltitude(500) - BrakeIsOn = true -- Engage brake for warmup - end - if spaceLaunch then HoldAltitude = 100000 end - else - autoRoll = autoRollPreference - AutoTakeoff = false - BrakeLanding = false - Reentry = false - AutoTakeoff = false - VectorToTarget = false - end - end - - function ToggleFollowMode() - if isRemote() == 1 then - followMode = not followMode - if followMode then - Autopilot = false - RetrogradeIsOn = false - ProgradeIsOn = false - AltitudeHold = false - Reentry = false - BrakeLanding = false - AutoTakeoff = false - OldGearExtended = GearExtended - GearExtended = false - Nav.control.retractLandingGears() - Nav.axisCommandManager:setTargetGroundAltitude(TargetHoverHeight) - else - BrakeIsOn = true - autoRoll = autoRollPreference - GearExtended = OldGearExtended - if GearExtended then - Nav.control.extendLandingGears() - Nav.axisCommandManager:setTargetGroundAltitude(LandingGearGroundHeight) - end - end - else - msgText = "Follow Mode only works with Remote controller" - followMode = false +function ToggleFollowMode() + if isRemote() == 1 then + followMode = not followMode + if followMode then + Autopilot = false + RetrogradeIsOn = false + ProgradeIsOn = false + AltitudeHold = false + Reentry = false + BrakeLanding = false + AutoTakeoff = false + OldGearExtended = GearExtended + GearExtended = false + Nav.control.retractLandingGears() + Nav.axisCommandManager:setTargetGroundAltitude(TargetHoverHeight) + else + BrakeIsOn = true + autoRoll = autoRollPreference + GearExtended = OldGearExtended + if GearExtended then + Nav.control.extendLandingGears() + Nav.axisCommandManager:setTargetGroundAltitude(LandingGearGroundHeight) end end + else + msgText = "Follow Mode only works with Remote controller" + followMode = false + end +end - function ToggleAutopilot() - -- Toggle Autopilot, as long as the target isn't None - if AutopilotTargetIndex > 0 and not Autopilot and not VectorToTarget then - -- If it's a custom location... - -- Behavior is probably - -- a. If not at the same nearest planet and in space and the target has gravity, autopilot to that planet - -- a1. - -- b. If at nearest planet but not in atmo (and the destination is in atmo), and destination is less than (radius) away or our orbit is not stable, auto-reentry - -- (IE if in an orbit, like from AP, it should wait until destination is on the correct side of the planet before engaging reentry) - -- c. If at correct planet and in atmo and alt hold isn't on and they aren't landed, engage altitude hold at that alt and speed - -- d. If alt hold is on and we're within tolerance of our target altitude, slowly yaw toward the target position - -- e. If our velocity vector is lined up to go over the target position, calculate our brake distance at current speed in atmo - -- f. If our distance to the target (ignoring altitude) is within our current brakeDistance, engage brake-landing - -- f2. Should we even try to let this happen on ships with bad brakes. Eventually, try that. For now just don't let them use this - if CustomTarget ~= nil then - LockPitch = nil - if planet.name == CustomTarget.planetname then - StrongBrakes = ((planet.gravity * 9.80665 * constructMass()) < LastMaxBrakeInAtmo) - if not StrongBrakes and velMag > minAutopilotSpeed then - msgText = "Insufficient Brake Force\nCoast landing will be inaccurate" - end - -- Going to need to add all those conditions here. Let's start with the easiest. - if atmosphere() > 0 then - if not AltitudeHold then - -- Autotakeoff gets engaged inside the toggle if conditions are met - if not VectorToTarget then - ToggleVectorToTarget() - end - else - -- Vector to target - if not VectorToTarget then - ToggleVectorToTarget() - end - end -- TBH... this is the only thing we need to do, make sure Alt Hold is on. - else - spaceLand = true +function ToggleAutopilot() + -- Toggle Autopilot, as long as the target isn't None + if AutopilotTargetIndex > 0 and not Autopilot and not VectorToTarget then + -- If it's a custom location... + -- Behavior is probably + -- a. If not at the same nearest planet and in space and the target has gravity, autopilot to that planet + -- a1. + -- b. If at nearest planet but not in atmo (and the destination is in atmo), and destination is less than (radius) away or our orbit is not stable, auto-reentry + -- (IE if in an orbit, like from AP, it should wait until destination is on the correct side of the planet before engaging reentry) + -- c. If at correct planet and in atmo and alt hold isn't on and they aren't landed, engage altitude hold at that alt and speed + -- d. If alt hold is on and we're within tolerance of our target altitude, slowly yaw toward the target position + -- e. If our velocity vector is lined up to go over the target position, calculate our brake distance at current speed in atmo + -- f. If our distance to the target (ignoring altitude) is within our current brakeDistance, engage brake-landing + -- f2. Should we even try to let this happen on ships with bad brakes. Eventually, try that. For now just don't let them use this + if CustomTarget ~= nil then + LockPitch = nil + if planet.name == CustomTarget.planetname then + StrongBrakes = ((planet.gravity * 9.80665 * constructMass()) < LastMaxBrakeInAtmo) + if not StrongBrakes and velMag > minAutopilotSpeed then + msgText = "Insufficient Brake Force\nCoast landing will be inaccurate" + end + -- Going to need to add all those conditions here. Let's start with the easiest. + if atmosphere() > 0 then + if not AltitudeHold then + -- Autotakeoff gets engaged inside the toggle if conditions are met + if not VectorToTarget then + ToggleVectorToTarget() end else - spaceLaunch = true - RetrogradeIsOn = false - ProgradeIsOn = false - if atmosphere() ~= 0 then - ToggleAltitudeHold() - else - Autopilot = true + -- Vector to target + if not VectorToTarget then + ToggleVectorToTarget() end - end - elseif atmosphere() == 0 then -- Planetary autopilot - Autopilot = true - RetrogradeIsOn = false - ProgradeIsOn = false - AutopilotRealigned = false - followMode = false - AltitudeHold = false - BrakeLanding = false - Reentry = false - AutoTakeoff = false - apThrottleSet = false - LockPitch = nil + end -- TBH... this is the only thing we need to do, make sure Alt Hold is on. else - spaceLaunch = true - ToggleAltitudeHold() + spaceLand = true end else - Autopilot = false - AutopilotRealigned = false - VectorToTarget = false - apThrottleSet = false - AutoTakeoff = false - AltitudeHold = false - VectorToTarget = false + spaceLaunch = true + RetrogradeIsOn = false + ProgradeIsOn = false + if atmosphere() ~= 0 then + ToggleAltitudeHold() + else + Autopilot = true + end end - end - - function ProgradeToggle() - -- Toggle Progrades - ProgradeIsOn = not ProgradeIsOn - RetrogradeIsOn = false -- Don't let both be on - Autopilot = false - AltitudeHold = false + elseif atmosphere() == 0 then -- Planetary autopilot + Autopilot = true + RetrogradeIsOn = false + ProgradeIsOn = false + AutopilotRealigned = false followMode = false - BrakeLanding = false - LockPitch = nil - Reentry = false - AutoTakeoff = false - end - - function RetrogradeToggle() - -- Toggle Retrogrades - RetrogradeIsOn = not RetrogradeIsOn - ProgradeIsOn = false -- Don't let both be on - Autopilot = false AltitudeHold = false - followMode = false BrakeLanding = false - LockPitch = nil Reentry = false AutoTakeoff = false + apThrottleSet = false + LockPitch = nil + else + spaceLaunch = true + ToggleAltitudeHold() end + else + Autopilot = false + AutopilotRealigned = false + VectorToTarget = false + apThrottleSet = false + AutoTakeoff = false + AltitudeHold = false + VectorToTarget = false + end +end - function BrakeToggle() - -- Toggle brakes - BrakeIsOn = not BrakeIsOn - if BrakeLanding then - BrakeLanding = false - autoRoll = autoRollPreference - end - if BrakeIsOn then - -- If they turn on brakes, disable a few things - AltitudeHold = false - VectorToTarget = false - AutoTakeoff = false - Reentry = false - -- We won't abort interplanetary because that would fuck everyone. - ProgradeIsOn = false -- No reason to brake while facing prograde, but retrograde yes. - BrakeLanding = false - AutoLanding = false - AltitudeHold = false -- And stop alt hold - LockPitch = nil - autoRoll = autoRollPreference - end - end +function ProgradeToggle() + -- Toggle Progrades + ProgradeIsOn = not ProgradeIsOn + RetrogradeIsOn = false -- Don't let both be on + Autopilot = false + AltitudeHold = false + followMode = false + BrakeLanding = false + LockPitch = nil + Reentry = false + AutoTakeoff = false +end - function CheckDamage(newContent) - local percentDam = 0 - damageMessage = "" - local maxShipHP = eleTotalMaxHp - local curShipHP = 0 - local damagedElements = 0 - local disabledElements = 0 - local colorMod = 0 - local color = "" - for k in pairs(elementsID) do - local hp = 0 - local mhp = 0 - mhp = eleMaxHp(elementsID[k]) - hp = eleHp(elementsID[k]) - curShipHP = curShipHP + hp - if (hp < mhp) then - if (hp == 0) then - disabledElements = disabledElements + 1 - else - damagedElements = damagedElements + 1 - end - -- Thanks to Jerico for the help and code starter for arrow markers! - if repairArrows and #markers == 0 then - position = vec3(core.getElementPositionById(elementsID[k])) - local x = position.x - coreOffset - local y = position.y - coreOffset - local z = position.z - coreOffset - table.insert(markers, core.spawnArrowSticker(x, y, z + 1, "down")) - table.insert(markers, core.spawnArrowSticker(x, y, z + 1, "down")) - core.rotateSticker(markers[2], 0, 0, 90) - table.insert(markers, core.spawnArrowSticker(x + 1, y, z, "north")) - table.insert(markers, core.spawnArrowSticker(x + 1, y, z, "north")) - core.rotateSticker(markers[4], 90, 90, 0) - table.insert(markers, core.spawnArrowSticker(x - 1, y, z, "south")) - table.insert(markers, core.spawnArrowSticker(x - 1, y, z, "south")) - core.rotateSticker(markers[6], 90, -90, 0) - table.insert(markers, core.spawnArrowSticker(x, y - 1, z, "east")) - table.insert(markers, core.spawnArrowSticker(x, y - 1, z, "east")) - core.rotateSticker(markers[8], 90, 0, 90) - table.insert(markers, core.spawnArrowSticker(x, y + 1, z, "west")) - table.insert(markers, core.spawnArrowSticker(x, y + 1, z, "west")) - core.rotateSticker(markers[10], -90, 0, 90) - table.insert(markers, elementsID[k]) - end - elseif repairArrows and #markers > 0 and markers[11] == elementsID[k] then - for j in pairs(markers) do - core.deleteSticker(markers[j]) - end - markers = {} - end - end - percentDam = mfloor((curShipHP / maxShipHP)*100) - if percentDam < 100 then - newContent[#newContent + 1] = [[]] - colorMod = mfloor(percentDam * 2.55) - color = stringf("rgb(%d,%d,%d)", 255 - colorMod, colorMod, 0) - if percentDam < 100 then - newContent[#newContent + 1] = stringf( - [[Elemental Integrity: %i %%]], - color, percentDam) - if (disabledElements > 0) then - newContent[#newContent + 1] = stringf( - [[Disabled Modules: %i Damaged Modules: %i]], - color, disabledElements, damagedElements) - elseif damagedElements > 0 then - newContent[#newContent + 1] = stringf( - [[Damaged Modules: %i]], - color, damagedElements) - end - end - newContent[#newContent + 1] = [[<\g>]] - end - end +function RetrogradeToggle() + -- Toggle Retrogrades + RetrogradeIsOn = not RetrogradeIsOn + ProgradeIsOn = false -- Don't let both be on + Autopilot = false + AltitudeHold = false + followMode = false + BrakeLanding = false + LockPitch = nil + Reentry = false + AutoTakeoff = false +end - function DrawCursorLine(newContent) - local strokeColor = mfloor(utils.clamp((distance / (resolutionWidth / 4)) * 255, 0, 255)) - newContent[#newContent + 1] = stringf( - "", - simulatedX, simulatedY, mfloor(PrimaryR + 0.5) + strokeColor, - mfloor(PrimaryG + 0.5) - strokeColor, mfloor(PrimaryB + 0.5) - strokeColor) - end +function BrakeToggle() + -- Toggle brakes + BrakeIsOn = not BrakeIsOn + if BrakeLanding then + BrakeLanding = false + autoRoll = autoRollPreference + end + if BrakeIsOn then + -- If they turn on brakes, disable a few things + AltitudeHold = false + VectorToTarget = false + AutoTakeoff = false + Reentry = false + -- We won't abort interplanetary because that would fuck everyone. + ProgradeIsOn = false -- No reason to brake while facing prograde, but retrograde yes. + BrakeLanding = false + AutoLanding = false + AltitudeHold = false -- And stop alt hold + LockPitch = nil + autoRoll = autoRollPreference + end +end - function getPitch(gravityDirection, forward, right) - local horizontalForward = gravityDirection:cross(right):normalize_inplace() -- Cross forward? - local pitch = math.acos(utils.clamp(horizontalForward:dot(-forward), -1, 1)) * constants.rad2deg -- acos? - - if horizontalForward:cross(-forward):dot(right) < 0 then - pitch = -pitch - end -- Cross right dot forward? - return pitch - end - - function clearAll() - if clearAllCheck then - clearAllCheck = false - AutopilotAccelerating = false - AutopilotBraking = false - AutopilotCruising = false - Autopilot = false - AutopilotRealigned = false - AutopilotStatus = "Aligning" - RetrogradeIsOn = false - ProgradeIsOn = false - AltitudeHold = false - Reentry = false - BrakeLanding = false - BrakeIsOn = false - AutoTakeoff = false - followMode = false - apThrottleSet = false - spaceLand = false - spaceLaunch = false - reentryMode = false - autoRoll = autoRollPreference - VectorToTarget = false - TurnBurn = false - gyroIsOn = false - LockPitch = nil +function CheckDamage(newContent) + local percentDam = 0 + damageMessage = "" + local maxShipHP = eleTotalMaxHp + local curShipHP = 0 + local damagedElements = 0 + local disabledElements = 0 + local colorMod = 0 + local color = "" + for k in pairs(elementsID) do + local hp = 0 + local mhp = 0 + mhp = eleMaxHp(elementsID[k]) + hp = eleHp(elementsID[k]) + curShipHP = curShipHP + hp + if (hp < mhp) then + if (hp == 0) then + disabledElements = disabledElements + 1 else - clearAllCheck = true - end + damagedElements = damagedElements + 1 + end + -- Thanks to Jerico for the help and code starter for arrow markers! + if repairArrows and #markers == 0 then + position = vec3(core.getElementPositionById(elementsID[k])) + local x = position.x - coreOffset + local y = position.y - coreOffset + local z = position.z - coreOffset + table.insert(markers, core.spawnArrowSticker(x, y, z + 1, "down")) + table.insert(markers, core.spawnArrowSticker(x, y, z + 1, "down")) + core.rotateSticker(markers[2], 0, 0, 90) + table.insert(markers, core.spawnArrowSticker(x + 1, y, z, "north")) + table.insert(markers, core.spawnArrowSticker(x + 1, y, z, "north")) + core.rotateSticker(markers[4], 90, 90, 0) + table.insert(markers, core.spawnArrowSticker(x - 1, y, z, "south")) + table.insert(markers, core.spawnArrowSticker(x - 1, y, z, "south")) + core.rotateSticker(markers[6], 90, -90, 0) + table.insert(markers, core.spawnArrowSticker(x, y - 1, z, "east")) + table.insert(markers, core.spawnArrowSticker(x, y - 1, z, "east")) + core.rotateSticker(markers[8], 90, 0, 90) + table.insert(markers, core.spawnArrowSticker(x, y + 1, z, "west")) + table.insert(markers, core.spawnArrowSticker(x, y + 1, z, "west")) + core.rotateSticker(markers[10], -90, 0, 90) + table.insert(markers, elementsID[k]) + end + elseif repairArrows and #markers > 0 and markers[11] == elementsID[k] then + for j in pairs(markers) do + core.deleteSticker(markers[j]) + end + markers = {} end - - function wipeSaveVariables() - if not dbHud_1 then - msgText = - "No Databank Found, unable to wipe. \nYou must have a Databank attached to ship prior to running the HUD autoconfigure" - msgTimer = 5 - else--if valuesAreSet then - if doubleCheck then - -- If any values are set, wipe them all - for k, v in pairs(saveableVariables) do - dbHud_1.setStringValue(v, jencode(nil)) - end - for k, v in pairs(autoVariables) do - if v ~= "SavedLocations" then dbHud_1.setStringValue(v, jencode(nil)) end - end - --dbHud_1.clear() - msgText = - "Databank wiped. New variables will save after re-enter seat and exit" - msgTimer = 5 - doubleCheck = false - valuesAreSet = false - wipedDatabank = true - else - msgText = "Press ALT-7 again to confirm wipe of ALL data" - doubleCheck = true - end + end + percentDam = mfloor((curShipHP / maxShipHP)*100) + if percentDam < 100 then + newContent[#newContent + 1] = [[]] + colorMod = mfloor(percentDam * 2.55) + color = stringf("rgb(%d,%d,%d)", 255 - colorMod, colorMod, 0) + if percentDam < 100 then + newContent[#newContent + 1] = stringf( + [[Elemental Integrity: %i %%]], + color, percentDam) + if (disabledElements > 0) then + newContent[#newContent + 1] = stringf( + [[Disabled Modules: %i Damaged Modules: %i]], + color, disabledElements, damagedElements) + elseif damagedElements > 0 then + newContent[#newContent + 1] = stringf( + [[Damaged Modules: %i]], + color, damagedElements) end end + newContent[#newContent + 1] = [[<\g>]] + end +end - function CheckButtons() - for _, v in pairs(Buttons) do - if v.hovered then - if not v.drawCondition or v.drawCondition() then - v.toggleFunction() - end - v.hovered = false - end - end - end +function DrawCursorLine(newContent) + local strokeColor = mfloor(utils.clamp((distance / (resolutionWidth / 4)) * 255, 0, 255)) + newContent[#newContent + 1] = stringf( + "", + simulatedX, simulatedY, mfloor(PrimaryR + 0.5) + strokeColor, + mfloor(PrimaryG + 0.5) - strokeColor, mfloor(PrimaryB + 0.5) - strokeColor) +end + +function getPitch(gravityDirection, forward, right) + local horizontalForward = gravityDirection:cross(right):normalize_inplace() -- Cross forward? + local pitch = math.acos(utils.clamp(horizontalForward:dot(-forward), -1, 1)) * constants.rad2deg -- acos? + + if horizontalForward:cross(-forward):dot(right) < 0 then + pitch = -pitch + end -- Cross right dot forward? + return pitch +end + +function clearAll() + if clearAllCheck then + clearAllCheck = false + AutopilotAccelerating = false + AutopilotBraking = false + AutopilotCruising = false + Autopilot = false + AutopilotRealigned = false + AutopilotStatus = "Aligning" + RetrogradeIsOn = false + ProgradeIsOn = false + AltitudeHold = false + Reentry = false + BrakeLanding = false + BrakeIsOn = false + AutoTakeoff = false + followMode = false + apThrottleSet = false + spaceLand = false + spaceLaunch = false + reentryMode = false + autoRoll = autoRollPreference + VectorToTarget = false + TurnBurn = false + gyroIsOn = false + LockPitch = nil + else + clearAllCheck = true + end +end - function SetButtonContains() - local x = simulatedX + resolutionWidth / 2 - local y = simulatedY + resolutionHeight / 2 - for _, v in pairs(Buttons) do - -- enableName, disableName, width, height, x, y, toggleVar, toggleFunction, drawCondition - v.hovered = Contains(x, y, v.x, v.y, v.width, v.height) +function wipeSaveVariables() + if not dbHud_1 then + msgText = + "No Databank Found, unable to wipe. \nYou must have a Databank attached to ship prior to running the HUD autoconfigure" + msgTimer = 5 + else--if valuesAreSet then + if doubleCheck then + -- If any values are set, wipe them all + for k, v in pairs(saveableVariables) do + dbHud_1.setStringValue(v, jencode(nil)) end + for k, v in pairs(autoVariables) do + if v ~= "SavedLocations" then dbHud_1.setStringValue(v, jencode(nil)) end + end + --dbHud_1.clear() + msgText = + "Databank wiped. New variables will save after re-enter seat and exit" + msgTimer = 5 + doubleCheck = false + valuesAreSet = false + wipedDatabank = true + else + msgText = "Press ALT-7 again to confirm wipe of ALL data" + doubleCheck = true end + end +end - function DrawButton(newContent, toggle, hover, x, y, w, h, activeColor, inactiveColor, activeText, inactiveText) - if type(activeText) == "function" then - activeText = activeText() - end - if type(inactiveText) == "function" then - inactiveText = inactiveText() - end - newContent[#newContent + 1] = stringf("" - if toggle then - newContent[#newContent + 1] = stringf("%s", activeText) - else - newContent[#newContent + 1] = stringf("%s", inactiveText) +function CheckButtons() + for _, v in pairs(Buttons) do + if v.hovered then + if not v.drawCondition or v.drawCondition() then + v.toggleFunction() end + v.hovered = false end + end +end - function DrawButtons(newContent) - local defaultColor = "rgb(50,50,50)'" - local onColor = "rgb(210,200,200)" - local draw = DrawButton - for _, v in pairs(Buttons) do - -- enableName, disableName, width, height, x, y, toggleVar, toggleFunction, drawCondition - local disableName = v.disableName - local enableName = v.enableName - if type(disableName) == "function" then - disableName = disableName() - end - if type(enableName) == "function" then - enableName = enableName() - end - if not v.drawCondition or v.drawCondition() then -- If they gave us a nil condition - draw(newContent, v.toggleVar(), v.hovered, v.x, v.y, v.width, v.height, onColor, defaultColor, - disableName, enableName) - end - end +function SetButtonContains() + local x = simulatedX + resolutionWidth / 2 + local y = simulatedY + resolutionHeight / 2 + for _, v in pairs(Buttons) do + -- enableName, disableName, width, height, x, y, toggleVar, toggleFunction, drawCondition + v.hovered = Contains(x, y, v.x, v.y, v.width, v.height) + end +end + +function DrawButton(newContent, toggle, hover, x, y, w, h, activeColor, inactiveColor, activeText, inactiveText) + if type(activeText) == "function" then + activeText = activeText() + end + if type(inactiveText) == "function" then + inactiveText = inactiveText() + end + newContent[#newContent + 1] = stringf("" + if toggle then + newContent[#newContent + 1] = stringf("%s", activeText) + else + newContent[#newContent + 1] = stringf("%s", inactiveText) + end +end + +function DrawButtons(newContent) + local defaultColor = "rgb(50,50,50)'" + local onColor = "rgb(210,200,200)" + local draw = DrawButton + for _, v in pairs(Buttons) do + -- enableName, disableName, width, height, x, y, toggleVar, toggleFunction, drawCondition + local disableName = v.disableName + local enableName = v.enableName + if type(disableName) == "function" then + disableName = disableName() end + if type(enableName) == "function" then + enableName = enableName() + end + if not v.drawCondition or v.drawCondition() then -- If they gave us a nil condition + draw(newContent, v.toggleVar(), v.hovered, v.x, v.y, v.width, v.height, onColor, defaultColor, + disableName, enableName) + end + end +end - function DrawTank(newContent, updateTanks, x, nameSearchPrefix, nameReplacePrefix, tankTable, fuelTimeLeftTable, - fuelPercentTable) - local tankID = 1 - local tankName = 2 - local tankMaxVol = 3 - local tankMassEmpty = 4 - local tankLastMass = 5 - local tankLastTime = 6 - local slottedTankType = "" - local slottedTanks = 0 - - local y1 = fuelY - local y2 = fuelY+10 - if isRemote() == 1 and not RemoteHud then - y1 = y1 - 50 - y2 = y2 - 50 - end +function DrawTank(newContent, updateTanks, x, nameSearchPrefix, nameReplacePrefix, tankTable, fuelTimeLeftTable, + fuelPercentTable) + local tankID = 1 + local tankName = 2 + local tankMaxVol = 3 + local tankMassEmpty = 4 + local tankLastMass = 5 + local tankLastTime = 6 + local slottedTankType = "" + local slottedTanks = 0 + + local y1 = fuelY + local y2 = fuelY+10 + if isRemote() == 1 and not RemoteHud then + y1 = y1 - 50 + y2 = y2 - 50 + end - newContent[#newContent + 1] = [[]] + newContent[#newContent + 1] = [[]] - if nameReplacePrefix == "ATMO" then - slottedTankType = "atmofueltank" - elseif nameReplacePrefix == "SPACE" then - slottedTankType = "spacefueltank" - else - slottedTankType = "rocketfueltank" + if nameReplacePrefix == "ATMO" then + slottedTankType = "atmofueltank" + elseif nameReplacePrefix == "SPACE" then + slottedTankType = "spacefueltank" + else + slottedTankType = "rocketfueltank" + end + slottedTanks = _G[slottedTankType .. "_size"] + if (#tankTable > 0) then + for i = 1, #tankTable do + local name = string.sub(tankTable[i][tankName], 1, 12) + local slottedIndex = 0 + for j = 1, slottedTanks do + if tankTable[i][tankName] == jdecode(unit[slottedTankType .. "_" .. j].getData()).name then + slottedIndex = j + break + end end - slottedTanks = _G[slottedTankType .. "_size"] - if (#tankTable > 0) then - for i = 1, #tankTable do - local name = string.sub(tankTable[i][tankName], 1, 12) - local slottedIndex = 0 - for j = 1, slottedTanks do - if tankTable[i][tankName] == jdecode(unit[slottedTankType .. "_" .. j].getData()).name then - slottedIndex = j - break - end - end - if updateTanks or fuelTimeLeftTable[i] == nil or fuelPercentTable[i] == nil then - local fuelMassMax = 0 - local fuelMassLast = 0 - local fuelMass = 0 - local fuelLastTime = 0 - local curTime = system.getTime() - if slottedIndex ~= 0 then - fuelPercentTable[i] = jdecode(unit[slottedTankType .. "_" .. slottedIndex].getData()) - .percentage - fuelTimeLeftTable[i] = jdecode(unit[slottedTankType .. "_" .. slottedIndex].getData()) - .timeLeft - if fuelTimeLeftTable[i] == "n/a" then - fuelTimeLeftTable[i] = 0 - end - else - fuelMass = (eleMass(tankTable[i][tankID]) - tankTable[i][tankMassEmpty]) - fuelMassMax = tankTable[i][tankMaxVol] - fuelPercentTable[i] = mfloor(0.5 + fuelMass * 100 / fuelMassMax) - fuelMassLast = tankTable[i][tankLastMass] - fuelLastTime = tankTable[i][tankLastTime] - if fuelMassLast <= fuelMass then - fuelTimeLeftTable[i] = 0 - else - fuelTimeLeftTable[i] = mfloor( - 0.5 + fuelMass / - ((fuelMassLast - fuelMass) / (curTime - fuelLastTime))) - end - tankTable[i][tankLastMass] = fuelMass - tankTable[i][tankLastTime] = curTime - end - end - if name == nameSearchPrefix then - name = stringf("%s %d", nameReplacePrefix, i) + if updateTanks or fuelTimeLeftTable[i] == nil or fuelPercentTable[i] == nil then + local fuelMassMax = 0 + local fuelMassLast = 0 + local fuelMass = 0 + local fuelLastTime = 0 + local curTime = system.getTime() + if slottedIndex ~= 0 then + fuelPercentTable[i] = jdecode(unit[slottedTankType .. "_" .. slottedIndex].getData()) + .percentage + fuelTimeLeftTable[i] = jdecode(unit[slottedTankType .. "_" .. slottedIndex].getData()) + .timeLeft + if fuelTimeLeftTable[i] == "n/a" then + fuelTimeLeftTable[i] = 0 end - if slottedIndex == 0 then - name = name .. " *" - end - local fuelTimeDisplay - if fuelTimeLeftTable[i] == 0 then - fuelTimeDisplay = "n/a" + else + fuelMass = (eleMass(tankTable[i][tankID]) - tankTable[i][tankMassEmpty]) + fuelMassMax = tankTable[i][tankMaxVol] + fuelPercentTable[i] = mfloor(0.5 + fuelMass * 100 / fuelMassMax) + fuelMassLast = tankTable[i][tankLastMass] + fuelLastTime = tankTable[i][tankLastTime] + if fuelMassLast <= fuelMass then + fuelTimeLeftTable[i] = 0 else - fuelTimeDisplay = FormatTimeString(fuelTimeLeftTable[i]) + fuelTimeLeftTable[i] = mfloor( + 0.5 + fuelMass / + ((fuelMassLast - fuelMass) / (curTime - fuelLastTime))) end - if fuelPercentTable[i] ~= nil then - local colorMod = mfloor(fuelPercentTable[i] * 2.55) - local color = stringf("rgb(%d,%d,%d)", 255 - colorMod, colorMod, 0) - local class = "" - if ((fuelTimeDisplay ~= "n/a" and fuelTimeLeftTable[i] < 120) or fuelPercentTable[i] < 5) then - if updateTanks then - class = [[class="red"]] - end - end - newContent[#newContent + 1] = stringf( - [[ - %s - %d%% %s - ]], x, y1, class, name, x, y2, color, fuelPercentTable[i], fuelTimeDisplay) - y1 = y1 + 30 - y2 = y2 + 30 + tankTable[i][tankLastMass] = fuelMass + tankTable[i][tankLastTime] = curTime + end + end + if name == nameSearchPrefix then + name = stringf("%s %d", nameReplacePrefix, i) + end + if slottedIndex == 0 then + name = name .. " *" + end + local fuelTimeDisplay + if fuelTimeLeftTable[i] == 0 then + fuelTimeDisplay = "n/a" + else + fuelTimeDisplay = FormatTimeString(fuelTimeLeftTable[i]) + end + if fuelPercentTable[i] ~= nil then + local colorMod = mfloor(fuelPercentTable[i] * 2.55) + local color = stringf("rgb(%d,%d,%d)", 255 - colorMod, colorMod, 0) + local class = "" + if ((fuelTimeDisplay ~= "n/a" and fuelTimeLeftTable[i] < 120) or fuelPercentTable[i] < 5) then + if updateTanks then + class = [[class="red"]] end end + newContent[#newContent + 1] = stringf( + [[ + %s + %d%% %s + ]], x, y1, class, name, x, y2, color, fuelPercentTable[i], fuelTimeDisplay) + y1 = y1 + 30 + y2 = y2 + 30 end - newContent[#newContent + 1] = "" end + end + newContent[#newContent + 1] = "" +end - function HideInterplanetaryPanel() - system.destroyWidgetPanel(panelInterplanetary) - panelInterplanetary = nil - end +function HideInterplanetaryPanel() + system.destroyWidgetPanel(panelInterplanetary) + panelInterplanetary = nil +end - system.showScreen(1) +function getRelativePitch(velocity) + velocity = vec3(velocity) + local pitch = -math.deg(math.atan(velocity.y, velocity.z)) + 180 + -- This is 0-360 where 0 is straight up + pitch = pitch - 90 + -- So now 0 is straight, but we can now get angles up to 420 + if pitch < 0 then + pitch = 360 + pitch + end + -- Now, if it's greater than 180, say 190, make it go to like -170 + if pitch > 180 then + pitch = -180 + (pitch - 180) + end + -- And it's backwards. + return -pitch +end - function getRelativePitch(velocity) - velocity = vec3(velocity) - local pitch = -math.deg(math.atan(velocity.y, velocity.z)) + 180 - -- This is 0-360 where 0 is straight up - pitch = pitch - 90 - -- So now 0 is straight, but we can now get angles up to 420 - if pitch < 0 then - pitch = 360 + pitch - end - -- Now, if it's greater than 180, say 190, make it go to like -170 - if pitch > 180 then - pitch = -180 + (pitch - 180) - end - -- And it's backwards. - return -pitch - end +function getRelativeYaw(velocity) + velocity = vec3(velocity) + local yaw = math.deg(math.atan(velocity.y, velocity.x)) - 90 + if yaw < -180 then + yaw = 360 + yaw + end + return yaw +end - function getRelativeYaw(velocity) - velocity = vec3(velocity) - local yaw = math.deg(math.atan(velocity.y, velocity.x)) - 90 - if yaw < -180 then - yaw = 360 + yaw - end - return yaw +function AlignToWorldVector(vector, tolerance) + -- Sets inputs to attempt to point at the autopilot target + -- Meant to be called from Update or Tick repeatedly + if not inAtmo or RateOfChange > (MinimumRateOfChange+0.08) or hovGndDet ~= -1 then + if tolerance == nil then + tolerance = alignmentTolerance + end + vector = vec3(vector):normalize() + local targetVec = (vec3(core.getConstructWorldOrientationForward()) - vector) + local yawAmount = -getMagnitudeInDirection(targetVec, core.getConstructWorldOrientationRight()) * + autopilotStrength + local pitchAmount = -getMagnitudeInDirection(targetVec, core.getConstructWorldOrientationUp()) * + autopilotStrength + if previousYawAmount == 0 then previousYawAmount = yawAmount / 2 end + if previousPitchAmount == 0 then previousPitchAmount = pitchAmount / 2 end + yawInput2 = yawInput2 - (yawAmount + (yawAmount - previousYawAmount) * DampingMultiplier) + pitchInput2 = pitchInput2 + (pitchAmount + (pitchAmount - previousPitchAmount) * DampingMultiplier) + previousYawAmount = yawAmount + previousPitchAmount = pitchAmount + -- Return true or false depending on whether or not we're aligned + if math.abs(yawAmount) < tolerance and math.abs(pitchAmount) < tolerance then + return true end + return false + end +end - function AlignToWorldVector(vector, tolerance) - -- Sets inputs to attempt to point at the autopilot target - -- Meant to be called from Update or Tick repeatedly - if not inAtmo or RateOfChange > (MinimumRateOfChange+0.08) or hovGndDet ~= -1 then - if tolerance == nil then - tolerance = alignmentTolerance - end - vector = vec3(vector):normalize() - local targetVec = (vec3(core.getConstructWorldOrientationForward()) - vector) - local yawAmount = -getMagnitudeInDirection(targetVec, core.getConstructWorldOrientationRight()) * - autopilotStrength - local pitchAmount = -getMagnitudeInDirection(targetVec, core.getConstructWorldOrientationUp()) * - autopilotStrength - if previousYawAmount == 0 then previousYawAmount = yawAmount / 2 end - if previousPitchAmount == 0 then previousPitchAmount = pitchAmount / 2 end - yawInput2 = yawInput2 - (yawAmount + (yawAmount - previousYawAmount) * DampingMultiplier) - pitchInput2 = pitchInput2 + (pitchAmount + (pitchAmount - previousPitchAmount) * DampingMultiplier) - previousYawAmount = yawAmount - previousPitchAmount = pitchAmount - -- Return true or false depending on whether or not we're aligned - if math.abs(yawAmount) < tolerance and math.abs(pitchAmount) < tolerance then - return true - end - return false - end - end +function getAPEnableName() + local name = AutopilotTargetName + if name == nil then + name = CustomTarget.name .. " " .. + getDistanceDisplayString((vec3(core.getConstructWorldPos()) - CustomTarget.position):len()) + end + if name == nil then + name = "None" + end + return "Engage Autopilot: " .. name +end - function getAPEnableName() - local name = AutopilotTargetName - if name == nil then - name = CustomTarget.name .. " " .. - getDistanceDisplayString((vec3(core.getConstructWorldPos()) - CustomTarget.position):len()) - end - if name == nil then - name = "None" - end - return "Engage Autopilot: " .. name - end +function getAPDisableName() + local name = AutopilotTargetName + if name == nil then + name = CustomTarget.name + end + if name == nil then + name = "None" + end + return "Disable Autopilot: " .. name +end - function getAPDisableName() - local name = AutopilotTargetName - if name == nil then - name = CustomTarget.name +function ToggleAntigrav() + if antigrav and not ExternalAGG then + if antigrav.getState() == 1 then + antigrav.deactivate() + antigrav.hide() + else + if AntigravTargetAltitude == nil then AntigravTargetAltitude = coreAltitude end + if AntigravTargetAltitude < 1000 then + AntigravTargetAltitude = 1000 end - if name == nil then - name = "None" - end - return "Disable Autopilot: " .. name + antigrav.activate() + antigrav.show() end + end +end - function ToggleAntigrav() - if antigrav and not ExternalAGG then - if antigrav.getState() == 1 then - antigrav.deactivate() - antigrav.hide() - else - if AntigravTargetAltitude == nil then AntigravTargetAltitude = coreAltitude end - if AntigravTargetAltitude < 1000 then - AntigravTargetAltitude = 1000 - end - antigrav.activate() - antigrav.show() - end - end - end +function BeginReentry() + if Reentry then + msgText = "Re-Entry cancelled" + Reentry = false + autoRoll = autoRollPreference + AltitudeHold = false + elseif atmosphere() ~= 0 or unit.getClosestPlanetInfluence() <= 0 or Reentry or not planet.atmos then + msgText = "Re-Entry requirements not met: you must start out of atmosphere and within a planets gravity well over a planet with atmosphere" + msgTimer = 5 + elseif not reentryMode then-- Parachute ReEntry + StrongBrakes = ((planet.gravity * 9.80665 * constructMass()) < LastMaxBrakeInAtmo) + if not StrongBrakes then + msgText = "WARNING: Insufficient Brakes for Parachute Re-Entry" + else + Reentry = true + if Nav.axisCommandManager:getAxisCommandType(0) ~= controlMasterModeId.cruise then + Nav.control.cancelCurrentControlMasterMode() + end + autoRoll = true + BrakeIsOn = false + msgText = "Beginning Parachute Re-Entry - Strap In. Target speed: " .. ReentrySpeed + end + else --Glide Reentry + Reentry = true + if Nav.axisCommandManager:getAxisCommandType(0) ~= controlMasterModeId.cruise then + Nav.control.cancelCurrentControlMasterMode() + end + AltitudeHold = true + autoRoll = true + BrakeIsOn = false + HoldAltitude = ReentryAltitude + msgText = "Beginning Re-entry. Target speed: " .. ReentrySpeed .. " Target Altitude: " .. + ReentryAltitude + end +end - function BeginReentry() - if Reentry then - msgText = "Re-Entry cancelled" - Reentry = false - autoRoll = autoRollPreference - AltitudeHold = false - elseif atmosphere() ~= 0 or unit.getClosestPlanetInfluence() <= 0 or Reentry or not planet.atmos then - msgText = "Re-Entry requirements not met: you must start out of atmosphere and within a planets gravity well over a planet with atmosphere" - msgTimer = 5 - elseif not reentryMode then-- Parachute ReEntry - StrongBrakes = ((planet.gravity * 9.80665 * constructMass()) < LastMaxBrakeInAtmo) - if not StrongBrakes then - msgText = "WARNING: Insufficient Brakes for Parachute Re-Entry" - else - Reentry = true - if Nav.axisCommandManager:getAxisCommandType(0) ~= controlMasterModeId.cruise then - Nav.control.cancelCurrentControlMasterMode() - end - autoRoll = true - BrakeIsOn = false - msgText = "Beginning Parachute Re-Entry - Strap In. Target speed: " .. ReentrySpeed - end - else --Glide Reentry - Reentry = true - if Nav.axisCommandManager:getAxisCommandType(0) ~= controlMasterModeId.cruise then - Nav.control.cancelCurrentControlMasterMode() - end - AltitudeHold = true - autoRoll = true - BrakeIsOn = false - HoldAltitude = ReentryAltitude - msgText = "Beginning Re-entry. Target speed: " .. ReentrySpeed .. " Target Altitude: " .. - ReentryAltitude - end - end - -- BEGIN BUTTON DEFINITIONS +function SetupButtons() + -- BEGIN BUTTON DEFINITIONS - -- enableName, disableName, width, height, x, y, toggleVar, toggleFunction, drawCondition - local buttonHeight = 50 - local buttonWidth = 260 -- Defaults - local brake = MakeButton("Enable Brake Toggle", "Disable Brake Toggle", buttonWidth, buttonHeight, - resolutionWidth / 2 - buttonWidth / 2, resolutionHeight / 2 + 350, function() - return BrakeToggleStatus - end, function() - BrakeToggleStatus = not BrakeToggleStatus - if (BrakeToggleStatus) then - msgText = "Brakes in Toggle Mode" - else - msgText = "Brakes in Default Mode" - end - end) - MakeButton("Align Prograde", "Disable Prograde", buttonWidth, buttonHeight, - resolutionWidth / 2 - buttonWidth / 2 - 50 - brake.width, resolutionHeight / 2 - buttonHeight + 380, - function() - return ProgradeIsOn - end, ProgradeToggle) - MakeButton("Align Retrograde", "Disable Retrograde", buttonWidth, buttonHeight, - resolutionWidth / 2 - buttonWidth / 2 + brake.width + 50, resolutionHeight / 2 - buttonHeight + 380, - function() - return RetrogradeIsOn - end, RetrogradeToggle, function() - return atmosphere() == 0 - end) -- Hope this works - local apbutton = MakeButton(getAPEnableName, getAPDisableName, 600, 60, resolutionWidth / 2 - 600 / 2, - resolutionHeight / 2 - 60 / 2 - 400, function() - return Autopilot - end, ToggleAutopilot) - MakeButton("Save Position", "Save Position", 200, apbutton.height, apbutton.x + apbutton.width + 30, apbutton.y, - function() - return false - end, AddNewLocation, function() - return AutopilotTargetIndex == 0 or CustomTarget == nil - end) - MakeButton("Update Position", "Update Position", 200, apbutton.height, apbutton.x + apbutton.width + 30, apbutton.y, - function() - return false - end, UpdatePosition, function() - return AutopilotTargetIndex > 0 and CustomTarget ~= nil - end) - MakeButton("Clear Position", "Clear Position", 200, apbutton.height, apbutton.x - 200 - 30, apbutton.y, - function() - return true - end, ClearCurrentPosition, function() - return AutopilotTargetIndex > 0 and CustomTarget ~= nil - end) - -- The rest are sort of standardized - buttonHeight = 60 - buttonWidth = 300 - local x = 10 - local y = resolutionHeight / 2 - 300 - MakeButton("Enable Turn and Burn", "Disable Turn and Burn", buttonWidth, buttonHeight, x, y, function() - return TurnBurn - end, ToggleTurnBurn) - MakeButton("Engage Altitude Hold", "Disable Altitude Hold", buttonWidth, buttonHeight, x + buttonWidth + 20, y, - function() - return AltitudeHold - end, ToggleAltitudeHold) - y = y + buttonHeight + 20 - MakeButton("Engage Autoland", "Disable Autoland", buttonWidth, buttonHeight, x, y, function() - return AutoLanding - end, ToggleAutoLanding) - MakeButton("Engage Auto Takeoff", "Disable Auto Takeoff", buttonWidth, buttonHeight, x + buttonWidth + 20, y, - function() - return AutoTakeoff - end, ToggleAutoTakeoff) - y = y + buttonHeight + 20 - MakeButton("Show Orbit Display", "Hide Orbit Display", buttonWidth, buttonHeight, x, y, - function() - return DisplayOrbit - end, function() - DisplayOrbit = not DisplayOrbit - if (DisplayOrbit) then - msgText = "Orbit Display Enabled" - else - msgText = "Orbit Display Disabled" - end - end) - y = y + buttonHeight + 20 - MakeButton("Glide Re-Entry", "Cancel Glide Re-Entry", buttonWidth, buttonHeight, x, y, - function() return Reentry end, function() reentryMode = true BeginReentry() end, function() return (coreAltitude > ReentryAltitude) end ) - MakeButton("Parachute Re-Entry", "Cancel Parachute Re-Entry", buttonWidth, buttonHeight, x + buttonWidth + 20, y, - function() return Reentry end, BeginReentry, function() return (coreAltitude > ReentryAltitude) end ) - y = y + buttonHeight + 20 - MakeButton("Engage Follow Mode", "Disable Follow Mode", buttonWidth, buttonHeight, x, y, function() - return followMode - end, ToggleFollowMode, function() - return isRemote() == 1 - end) - MakeButton("Enable Repair Arrows", "Disable Repair Arrows", buttonWidth, buttonHeight, x + buttonWidth + 20, y, function() - return repairArrows + -- enableName, disableName, width, height, x, y, toggleVar, toggleFunction, drawCondition + local buttonHeight = 50 + local buttonWidth = 260 -- Defaults + local brake = MakeButton("Enable Brake Toggle", "Disable Brake Toggle", buttonWidth, buttonHeight, + resolutionWidth / 2 - buttonWidth / 2, resolutionHeight / 2 + 350, function() + return BrakeToggleStatus end, function() - repairArrows = not repairArrows - if (repairArrows) then - msgText = "Repair Arrows Enabled" + BrakeToggleStatus = not BrakeToggleStatus + if (BrakeToggleStatus) then + msgText = "Brakes in Toggle Mode" else - msgText = "Repair Arrows Diabled" + msgText = "Brakes in Default Mode" end - end, function() - return isRemote() == 1 end) - y = y + buttonHeight + 20 - if not ExternalAGG then - MakeButton("Enable AGG", "Disable AGG", buttonWidth, buttonHeight, x, y, function() - return antigrav.getState() == 1 end, ToggleAntigrav, function() - return antigrav ~= nil end) - end - y = y + buttonHeight + 20 - MakeButton(function() - return stringf("Toggle Control Scheme - Current: %s", userControlScheme) - end, function() - return stringf("Control Scheme: %s", userControlScheme) - end, buttonWidth * 2, buttonHeight, x, y, function() + MakeButton("Align Prograde", "Disable Prograde", buttonWidth, buttonHeight, + resolutionWidth / 2 - buttonWidth / 2 - 50 - brake.width, resolutionHeight / 2 - buttonHeight + 380, + function() + return ProgradeIsOn + end, ProgradeToggle) + MakeButton("Align Retrograde", "Disable Retrograde", buttonWidth, buttonHeight, + resolutionWidth / 2 - buttonWidth / 2 + brake.width + 50, resolutionHeight / 2 - buttonHeight + 380, + function() + return RetrogradeIsOn + end, RetrogradeToggle, function() + return atmosphere() == 0 + end) -- Hope this works + local apbutton = MakeButton(getAPEnableName, getAPDisableName, 600, 60, resolutionWidth / 2 - 600 / 2, + resolutionHeight / 2 - 60 / 2 - 400, function() + return Autopilot + end, ToggleAutopilot) + MakeButton("Save Position", "Save Position", 200, apbutton.height, apbutton.x + apbutton.width + 30, apbutton.y, + function() return false + end, AddNewLocation, function() + return AutopilotTargetIndex == 0 or CustomTarget == nil + end) + MakeButton("Update Position", "Update Position", 200, apbutton.height, apbutton.x + apbutton.width + 30, apbutton.y, + function() + return false + end, UpdatePosition, function() + return AutopilotTargetIndex > 0 and CustomTarget ~= nil + end) + MakeButton("Clear Position", "Clear Position", 200, apbutton.height, apbutton.x - 200 - 30, apbutton.y, + function() + return true + end, ClearCurrentPosition, function() + return AutopilotTargetIndex > 0 and CustomTarget ~= nil + end) + -- The rest are sort of standardized + buttonHeight = 60 + buttonWidth = 300 + local x = 10 + local y = resolutionHeight / 2 - 300 + MakeButton("Enable Turn and Burn", "Disable Turn and Burn", buttonWidth, buttonHeight, x, y, function() + return TurnBurn + end, ToggleTurnBurn) + MakeButton("Engage Altitude Hold", "Disable Altitude Hold", buttonWidth, buttonHeight, x + buttonWidth + 20, y, + function() + return AltitudeHold + end, ToggleAltitudeHold) + y = y + buttonHeight + 20 + MakeButton("Engage Autoland", "Disable Autoland", buttonWidth, buttonHeight, x, y, function() + return AutoLanding + end, ToggleAutoLanding) + MakeButton("Engage Auto Takeoff", "Disable Auto Takeoff", buttonWidth, buttonHeight, x + buttonWidth + 20, y, + function() + return AutoTakeoff + end, ToggleAutoTakeoff) + y = y + buttonHeight + 20 + MakeButton("Show Orbit Display", "Hide Orbit Display", buttonWidth, buttonHeight, x, y, + function() + return DisplayOrbit end, function() - if userControlScheme == "keyboard" then - userControlScheme = "mouse" - elseif userControlScheme == "mouse" then - userControlScheme = "virtual joystick" + DisplayOrbit = not DisplayOrbit + if (DisplayOrbit) then + msgText = "Orbit Display Enabled" else - userControlScheme = "keyboard" + msgText = "Orbit Display Disabled" end end) - coroutine.yield() -- Just to make sure - - function GetFlightStyle() - local flightType = Nav.axisCommandManager:getAxisCommandType(0) - local flightStyle = "TRAVEL" - if (flightType == 1) then - flightStyle = "CRUISE" - end - if Autopilot then - flightStyle = "AUTOPILOT" - end - return flightStyle + y = y + buttonHeight + 20 + MakeButton("Glide Re-Entry", "Cancel Glide Re-Entry", buttonWidth, buttonHeight, x, y, + function() return Reentry end, function() reentryMode = true BeginReentry() end, function() return (coreAltitude > ReentryAltitude) end ) + MakeButton("Parachute Re-Entry", "Cancel Parachute Re-Entry", buttonWidth, buttonHeight, x + buttonWidth + 20, y, + function() return Reentry end, BeginReentry, function() return (coreAltitude > ReentryAltitude) end ) + y = y + buttonHeight + 20 + MakeButton("Engage Follow Mode", "Disable Follow Mode", buttonWidth, buttonHeight, x, y, function() + return followMode + end, ToggleFollowMode, function() + return isRemote() == 1 + end) + MakeButton("Enable Repair Arrows", "Disable Repair Arrows", buttonWidth, buttonHeight, x + buttonWidth + 20, y, function() + return repairArrows + end, function() + repairArrows = not repairArrows + if (repairArrows) then + msgText = "Repair Arrows Enabled" + else + msgText = "Repair Arrows Diabled" end + end, function() + return isRemote() == 1 + end) + y = y + buttonHeight + 20 + if not ExternalAGG then + MakeButton("Enable AGG", "Disable AGG", buttonWidth, buttonHeight, x, y, function() + return antigrav.getState() == 1 end, ToggleAntigrav, function() + return antigrav ~= nil end) + end + y = y + buttonHeight + 20 + MakeButton(function() + return stringf("Toggle Control Scheme - Current: %s", userControlScheme) + end, function() + return stringf("Control Scheme: %s", userControlScheme) + end, buttonWidth * 2, buttonHeight, x, y, function() + return false + end, function() + if userControlScheme == "keyboard" then + userControlScheme = "mouse" + elseif userControlScheme == "mouse" then + userControlScheme = "virtual joystick" + else + userControlScheme = "keyboard" + end + end) +end - function updateHud(newContent) - - local altitude = coreAltitude - local velocity = core.getVelocity() - local speed = vec3(velocity):len() - local worldV = vec3(core.getWorldVertical()) - local constrF = vec3(core.getConstructWorldOrientationForward()) - local constrR = vec3(core.getConstructWorldOrientationRight()) - local constrU = vec3(core.getConstructWorldOrientationUp()) - local roll = getRoll(worldV, constrF, constrR) - local radianRoll = (roll / 180) * math.pi - local corrX = math.cos(radianRoll) - local corrY = math.sin(radianRoll) - local pitch = getPitch(worldV, constrF, (constrR * corrX) + (constrU * corrY)) -- 180 - getRoll(worldV, constrR, constrF) - local originalRoll = roll - local originalPitch = pitch - local atmos = atmosphere() - local throt = mfloor(unit.getThrottle()) - local spd = speed * 3.6 - local flightValue = unit.getAxisCommandValue(0) - local flightStyle = GetFlightStyle() - local bottomText = "ROLL" - local nearPlanet = unit.getClosestPlanetInfluence() > 0 - if throt == nil then throt = 0 end +function GetFlightStyle() + local flightType = Nav.axisCommandManager:getAxisCommandType(0) + local flightStyle = "TRAVEL" + if (flightType == 1) then + flightStyle = "CRUISE" + end + if Autopilot then + flightStyle = "AUTOPILOT" + end + return flightStyle +end - if (not nearPlanet) then - if (speed > 5) then - pitch = getRelativePitch(velocity) - roll = getRelativeYaw(velocity) - else - pitch = 0 - roll = 0 - end - bottomText = "YAW" - end +function updateHud(newContent) + + local altitude = coreAltitude + local velocity = core.getVelocity() + local speed = vec3(velocity):len() + local worldV = vec3(core.getWorldVertical()) + local constrF = vec3(core.getConstructWorldOrientationForward()) + local constrR = vec3(core.getConstructWorldOrientationRight()) + local constrU = vec3(core.getConstructWorldOrientationUp()) + local roll = getRoll(worldV, constrF, constrR) + local radianRoll = (roll / 180) * math.pi + local corrX = math.cos(radianRoll) + local corrY = math.sin(radianRoll) + local pitch = getPitch(worldV, constrF, (constrR * corrX) + (constrU * corrY)) -- 180 - getRoll(worldV, constrR, constrF) + local originalRoll = roll + local originalPitch = pitch + local atmos = atmosphere() + local throt = mfloor(unit.getThrottle()) + local spd = speed * 3.6 + local flightValue = unit.getAxisCommandValue(0) + local flightStyle = GetFlightStyle() + local bottomText = "ROLL" + local nearPlanet = unit.getClosestPlanetInfluence() > 0 + if throt == nil then throt = 0 end + + if (not nearPlanet) then + if (speed > 5) then + pitch = getRelativePitch(velocity) + roll = getRelativeYaw(velocity) + else + pitch = 0 + roll = 0 + end + bottomText = "YAW" + end - -- CRUISE/ODOMETER + -- CRUISE/ODOMETER - newContent[#newContent + 1] = lastOdometerOutput + newContent[#newContent + 1] = lastOdometerOutput - -- DAMAGE + -- DAMAGE - newContent[#newContent + 1] = damageMessage + newContent[#newContent + 1] = damageMessage - -- RADAR + -- RADAR - newContent[#newContent + 1] = radarMessage + newContent[#newContent + 1] = radarMessage - -- FUEL TANKS + -- FUEL TANKS - if (updateCount % fuelUpdateDelay == 0) then - updateTanks = true - end - if (fuelX ~= 0 and fuelY ~= 0) then - DrawTank(newContent, updateTanks, fuelX, "Atmospheric ", "ATMO", atmoTanks, fuelTimeLeft, fuelPercent) - DrawTank(newContent, updateTanks, fuelX+100, "Space fuel t", "SPACE", spaceTanks, fuelTimeLeftS, fuelPercentS) - DrawTank(newContent, updateTanks, fuelX+200, "Rocket fuel ", "ROCKET", rocketTanks, fuelTimeLeftR, fuelPercentR) - end + if (updateCount % fuelUpdateDelay == 0) then + updateTanks = true + end + if (fuelX ~= 0 and fuelY ~= 0) then + DrawTank(newContent, updateTanks, fuelX, "Atmospheric ", "ATMO", atmoTanks, fuelTimeLeft, fuelPercent) + DrawTank(newContent, updateTanks, fuelX+100, "Space fuel t", "SPACE", spaceTanks, fuelTimeLeftS, fuelPercentS) + DrawTank(newContent, updateTanks, fuelX+200, "Rocket fuel ", "ROCKET", rocketTanks, fuelTimeLeftR, fuelPercentR) + end - if updateTanks then - updateTanks = false - updateCount = 0 - end - updateCount = updateCount + 1 + if updateTanks then + updateTanks = false + updateCount = 0 + end + updateCount = updateCount + 1 - -- PRIMARY FLIGHT INSTRUMENTS + -- PRIMARY FLIGHT INSTRUMENTS - DrawVerticalSpeed(newContent, altitude) -- Weird this is draw during remote control...? + DrawVerticalSpeed(newContent, altitude) -- Weird this is draw during remote control...? - if isRemote() == 0 or RemoteHud then - -- Don't even draw this in freelook - if not IsInFreeLook() or brightHud then - if nearPlanet then -- use real pitch, roll, and heading - DrawRollLines (newContent, centerX, centerY, originalRoll, bottomText, nearPlanet) - DrawArtificialHorizon(newContent, originalPitch, originalRoll, centerX, centerY, nearPlanet, mfloor(getRelativeYaw(velocity)), speed) - else -- use Relative Pitch and Relative Yaw - DrawRollLines (newContent, centerX, centerY, roll, bottomText, nearPlanet) - DrawArtificialHorizon(newContent, pitch, roll, centerX, centerY, nearPlanet, mfloor(roll), speed) - end - DrawAltitudeDisplay(newContent, altitude, nearPlanet) - DrawPrograde(newContent, velocity, speed, centerX, centerY) - end + if isRemote() == 0 or RemoteHud then + -- Don't even draw this in freelook + if not IsInFreeLook() or brightHud then + if nearPlanet then -- use real pitch, roll, and heading + DrawRollLines (newContent, centerX, centerY, originalRoll, bottomText, nearPlanet) + DrawArtificialHorizon(newContent, originalPitch, originalRoll, centerX, centerY, nearPlanet, mfloor(getRelativeYaw(velocity)), speed) + else -- use Relative Pitch and Relative Yaw + DrawRollLines (newContent, centerX, centerY, roll, bottomText, nearPlanet) + DrawArtificialHorizon(newContent, pitch, roll, centerX, centerY, nearPlanet, mfloor(roll), speed) end - DrawThrottle(newContent, flightStyle, throt, flightValue) + DrawAltitudeDisplay(newContent, altitude, nearPlanet) + DrawPrograde(newContent, velocity, speed, centerX, centerY) + end + end + DrawThrottle(newContent, flightStyle, throt, flightValue) - -- PRIMARY DATA DISPLAYS + -- PRIMARY DATA DISPLAYS - DrawSpeed(newContent, spd) + DrawSpeed(newContent, spd) - DrawWarnings(newContent) - DisplayOrbitScreen(newContent) - if screen_2 then - local pos = vec3(core.getConstructWorldPos()) - local x = 960 + pos.x / MapXRatio - local y = 450 + pos.y / MapYRatio - screen_2.moveContent(YouAreHere, (x - 80) / 19.2, (y - 80) / 10.8) - end - end + DrawWarnings(newContent) + DisplayOrbitScreen(newContent) + if screen_2 then + local pos = vec3(core.getConstructWorldPos()) + local x = 960 + pos.x / MapXRatio + local y = 450 + pos.y / MapYRatio + screen_2.moveContent(YouAreHere, (x - 80) / 19.2, (y - 80) / 10.8) + end +end - function IsInFreeLook() - return system.isViewLocked() == 0 and userControlScheme ~= "keyboard" and isRemote() == 0 - end +function IsInFreeLook() + return system.isViewLocked() == 0 and userControlScheme ~= "keyboard" and isRemote() == 0 +end - function HUDPrologue(newContent) - local bright = rgb - local dim = rgbdim - local brightOrig = rgb - local dimOrig = rgbdim - if IsInFreeLook() and not brightHud then - bright = [[rgb(]] .. mfloor(PrimaryR * 0.4 + 0.5) .. "," .. mfloor(PrimaryG * 0.4 + 0.5) .. "," .. - mfloor(PrimaryB * 0.3 + 0.5) .. [[)]] - dim = [[rgb(]] .. mfloor(PrimaryR * 0.3 + 0.5) .. "," .. mfloor(PrimaryG * 0.3 + 0.5) .. "," .. - mfloor(PrimaryB * 0.2 + 0.5) .. [[)]] - end +function HUDPrologue(newContent) + local bright = rgb + local dim = rgbdim + local brightOrig = rgb + local dimOrig = rgbdim + if IsInFreeLook() and not brightHud then + bright = [[rgb(]] .. mfloor(PrimaryR * 0.4 + 0.5) .. "," .. mfloor(PrimaryG * 0.4 + 0.5) .. "," .. + mfloor(PrimaryB * 0.3 + 0.5) .. [[)]] + dim = [[rgb(]] .. mfloor(PrimaryR * 0.3 + 0.5) .. "," .. mfloor(PrimaryG * 0.3 + 0.5) .. "," .. + mfloor(PrimaryB * 0.2 + 0.5) .. [[)]] + end - -- When applying styles, apply color first, then type (e.g. "bright line") - -- so that "fill:none" gets applied + -- When applying styles, apply color first, then type (e.g. "bright line") + -- so that "fill:none" gets applied + + newContent[#newContent + 1] = stringf([[ + + + + + + ]], bright, bright, brightOrig, brightOrig, dim, dim, dimOrig, dimOrig, ResolutionX, ResolutionY) +end +function HUDEpilogue(newContent) + newContent[#newContent + 1] = "" +end + +function DrawSpeed(newContent, spd) + local ys = throtPosY-10 + local x1 = throtPosX + 10 + newContent[#newContent + 1] = [[]] + if isRemote() == 1 and not RemoteHud then + ys = 75 + end + newContent[#newContent + 1] = stringf([[ + + %d km/h + + ]], x1, ys, mfloor(spd)) +end + +function DrawOdometer(newContent, totalDistanceTrip, TotalDistanceTravelled, flightStyle, flightTime, atmos) + local xg = ConvertResolutionX(1240) + local yg1 = ConvertResolutionY(55) + local yg2 = yg1+10 + local atmos = atmosphere() + local gravity = core.g() + local maxMass = 0 + local reqThrust = 0 + local brakeValue = 0 + RefreshLastMaxBrake(gravity) + if inAtmo then brakeValue = LastMaxBrakeInAtmo else brakeValue = LastMaxBrake end + maxThrust = Nav:maxForceForward() + totalMass = constructMass() + if not ShowOdometer then return end + local accel = (vec3(core.getWorldAcceleration()):len() / 9.80665) + if gravity > 0.1 then + reqThrust = totalMass * gravity + maxMass = maxThrust / gravity + end + newContent[#newContent + 1] = [[]] + if isRemote() == 1 and not RemoteHud then + xg = ConvertResolutionX(1120) + yg1 = ConvertResolutionY(55) + yg2 = yg1+10 + elseif inAtmo then -- We only show atmo when not remote + local atX = ConvertResolutionX(770) + newContent[#newContent + 1] = stringf([[ + ATMOSPHERE + %.2f + ]], atX, yg1, atX, yg2, atmos) + end + newContent[#newContent + 1] = stringf([[ + + + GRAVITY + %.2f g + ACCEL + %.2f g + ]], xg, yg1, xg, yg2, (gravity / 9.80665), xg, yg1 + 20, xg, yg2 + 20, accel) + newContent[#newContent + 1] = stringf([[ + + ]], + ConvertResolutionX(660), ConvertResolutionX(700), ConvertResolutionY(35), ConvertResolutionX(960), ConvertResolutionY(55), + ConvertResolutionX(1240), ConvertResolutionY(35), ConvertResolutionX(1280)) + if isRemote() == 0 or RemoteHud then + newContent[#newContent + 1] = stringf([[ + Trip: %.2f km + Lifetime: %.2f Mm + Trip Time: %s + Total Time: %s + Mass: %.2f Tons + Max Brake: %.2f kN + Max Thrust: %.2f kN + %s]], + ConvertResolutionX(700), ConvertResolutionY(20), totalDistanceTrip, ConvertResolutionX(700), ConvertResolutionY(30), (TotalDistanceTravelled / 1000), + ConvertResolutionX(830), ConvertResolutionY(20), FormatTimeString(flightTime), ConvertResolutionX(830), ConvertResolutionY(30), FormatTimeString(TotalFlightTime), + ConvertResolutionX(970), ConvertResolutionY(20), (totalMass / 1000), ConvertResolutionX(1240), ConvertResolutionY(10), (brakeValue / 1000), + ConvertResolutionX(1240), ConvertResolutionY(30), (maxThrust / 1000), ConvertResolutionX(960), ConvertResolutionY(180), flightStyle) + if gravity > 0.1 then newContent[#newContent + 1] = stringf([[ - - - - - - ]], bright, bright, brightOrig, brightOrig, dim, dim, dimOrig, dimOrig, ResolutionX, ResolutionY) - end - - function HUDEpilogue(newContent) - newContent[#newContent + 1] = "" - end - - function DrawSpeed(newContent, spd) - local ys = throtPosY-10 - local x1 = throtPosX + 10 - newContent[#newContent + 1] = [[]] - if isRemote() == 1 and not RemoteHud then - ys = 75 - end - newContent[#newContent + 1] = stringf([[ - - %d km/h - - ]], x1, ys, mfloor(spd)) - end - - function DrawOdometer(newContent, totalDistanceTrip, TotalDistanceTravelled, flightStyle, flightTime, atmos) - local xg = ConvertResolutionX(1240) - local yg1 = ConvertResolutionY(55) - local yg2 = yg1+10 - local atmos = atmosphere() - local gravity = core.g() - local maxMass = 0 - local reqThrust = 0 - local brakeValue = 0 - RefreshLastMaxBrake(gravity) - if inAtmo then brakeValue = LastMaxBrakeInAtmo else brakeValue = LastMaxBrake end - maxThrust = Nav:maxForceForward() - totalMass = constructMass() - if not ShowOdometer then return end - local accel = (vec3(core.getWorldAcceleration()):len() / 9.80665) - if gravity > 0.1 then - reqThrust = totalMass * gravity - maxMass = maxThrust / gravity - end - newContent[#newContent + 1] = [[]] - if isRemote() == 1 and not RemoteHud then - xg = ConvertResolutionX(1120) - yg1 = ConvertResolutionY(55) - yg2 = yg1+10 - elseif inAtmo then -- We only show atmo when not remote - local atX = ConvertResolutionX(770) - newContent[#newContent + 1] = stringf([[ - ATMOSPHERE - %.2f - ]], atX, yg1, atX, yg2, atmos) - end - newContent[#newContent + 1] = stringf([[ - - - GRAVITY - %.2f g - ACCEL - %.2f g - ]], xg, yg1, xg, yg2, (gravity / 9.80665), xg, yg1 + 20, xg, yg2 + 20, accel) + Max Mass: %.2f Tons + Req Thrust: %.2f kN + ]], ConvertResolutionX(970), ConvertResolutionY(30), (maxMass / 1000), ConvertResolutionX(1240), ConvertResolutionY(20), (reqThrust / 1000)) + else newContent[#newContent + 1] = stringf([[ - - ]], - ConvertResolutionX(660), ConvertResolutionX(700), ConvertResolutionY(35), ConvertResolutionX(960), ConvertResolutionY(55), - ConvertResolutionX(1240), ConvertResolutionY(35), ConvertResolutionX(1280)) - if isRemote() == 0 or RemoteHud then - newContent[#newContent + 1] = stringf([[ - Trip: %.2f km - Lifetime: %.2f Mm - Trip Time: %s - Total Time: %s - Mass: %.2f Tons - Max Brake: %.2f kN - Max Thrust: %.2f kN - %s]], - ConvertResolutionX(700), ConvertResolutionY(20), totalDistanceTrip, ConvertResolutionX(700), ConvertResolutionY(30), (TotalDistanceTravelled / 1000), - ConvertResolutionX(830), ConvertResolutionY(20), FormatTimeString(flightTime), ConvertResolutionX(830), ConvertResolutionY(30), FormatTimeString(TotalFlightTime), - ConvertResolutionX(970), ConvertResolutionY(20), (totalMass / 1000), ConvertResolutionX(1240), ConvertResolutionY(10), (brakeValue / 1000), - ConvertResolutionX(1240), ConvertResolutionY(30), (maxThrust / 1000), ConvertResolutionX(960), ConvertResolutionY(180), flightStyle) - if gravity > 0.1 then - newContent[#newContent + 1] = stringf([[ - Max Mass: %.2f Tons - Req Thrust: %.2f kN - ]], ConvertResolutionX(970), ConvertResolutionY(30), (maxMass / 1000), ConvertResolutionX(1240), ConvertResolutionY(20), (reqThrust / 1000)) - else - newContent[#newContent + 1] = stringf([[ - Max Mass: n/a - Req Thrust: n/a - ]], ConvertResolutionX(970), ConvertResolutionY(30), ConvertResolutionX(1240), ConvertResolutionY(20)) - end - else -- If remote controlled, draw stuff near the top so it's out of the way - newContent[#newContent + 1] = stringf([[%s]], - ConvertResolutionX(960), ConvertResolutionY(33), flightStyle) - end - newContent[#newContent + 1] = "" + Max Mass: n/a + Req Thrust: n/a + ]], ConvertResolutionX(970), ConvertResolutionY(30), ConvertResolutionX(1240), ConvertResolutionY(20)) end + else -- If remote controlled, draw stuff near the top so it's out of the way + newContent[#newContent + 1] = stringf([[%s]], + ConvertResolutionX(960), ConvertResolutionY(33), flightStyle) + end + newContent[#newContent + 1] = "" +end - function DrawThrottle(newContent, flightStyle, throt, flightValue) +function DrawThrottle(newContent, flightStyle, throt, flightValue) - local y1 = throtPosY+10 - local y2 = throtPosY+20 - if isRemote() == 1 and not RemoteHud then - y1 = 55 - y2 = 65 - end + local y1 = throtPosY+10 + local y2 = throtPosY+20 + if isRemote() == 1 and not RemoteHud then + y1 = 55 + y2 = 65 + end - local label = "CRUISE" - local unit = "km/h" - local value = flightValue - if (flightStyle == "TRAVEL" or flightStyle == "AUTOPILOT") then - label = "THROT" - unit = "%" - value = throt - local throtclass = "dim" - if throt < 0 then - throtclass = "red" - end - newContent[#newContent + 1] = stringf([[ - - - - ]], throtclass, throtPosX-7, throtPosY-50, throtPosX, throtPosY-50, throtPosX, throtPosY+50, throtPosX-7, throtPosY+50, (1 - math.abs(throt)), - throtPosX-10, throtPosY+50, throtPosX-15, throtPosY+53, throtPosX-15, throtPosY+47) + local label = "CRUISE" + local unit = "km/h" + local value = flightValue + if (flightStyle == "TRAVEL" or flightStyle == "AUTOPILOT") then + label = "THROT" + unit = "%" + value = throt + local throtclass = "dim" + if throt < 0 then + throtclass = "red" + end + newContent[#newContent + 1] = stringf([[ + + + + ]], throtclass, throtPosX-7, throtPosY-50, throtPosX, throtPosY-50, throtPosX, throtPosY+50, throtPosX-7, throtPosY+50, (1 - math.abs(throt)), + throtPosX-10, throtPosY+50, throtPosX-15, throtPosY+53, throtPosX-15, throtPosY+47) + end + newContent[#newContent + 1] = stringf([[ + + %s + %d %s + + ]], throtPosX+10, y1, label, throtPosX+10, y2, value, unit) +end + + +function DrawVerticalSpeed(newContent, altitude) -- Draw vertical speed indicator - Code by lisa-lionheart + if (altitude < 200000 and not inAtmo) or (altitude and inAtmo) then + local vSpd = -vec3(core.getWorldVertical()):dot(vec3(core.getWorldVelocity())) + local angle = 0 + if math.abs(vSpd) > 1 then + angle = 45 * math.log(math.abs(vSpd), 10) + if vSpd < 0 then + angle = -angle + end + end + newContent[#newContent + 1] = stringf([[ + + 1000 + 100 + 10 + O + -10 + -100 + -1000 + %d m/s + + + + + + + ]], vSpdMeterX, vSpdMeterY, mfloor(vSpd), mfloor(angle)) + end +end + +function getHeading(forward) -- code provided by tomisunlucky + local up = -vec3(core.getWorldVertical()) + forward = forward - forward:project_on(up) + local north = vec3(0, 0, 1) + north = north - north:project_on(up) + local east = north:cross(up) + local angle = north:angle_between(forward) * constants.rad2deg + if forward:dot(east) < 0 then + angle = 360-angle + end + return angle +end + +function DrawRollLines (newContent, centerX, centerY, originalRoll, bottomText, nearPlanet) + local horizonRadius = circleRad -- Aliased global + local OFFSET = 20 + OFFSET = mfloor(OFFSET ) + local rollC = mfloor(originalRoll) + if nearPlanet then + for i = -45, 45, 5 do + local rot = i + newContent[#newContent + 1] = stringf([[]], rot, centerX, centerY) + len = 5 + if (i % 15 == 0) then + len = 15 + elseif (i % 10 == 0) then + len = 10 + end + newContent[#newContent + 1] = stringf([[]], centerX, centerY + horizonRadius + OFFSET - len, centerX, centerY + horizonRadius + OFFSET) + end + newContent[#newContent + 1] = stringf([[" + + %s + %d deg + + ]], centerX, centerY+horizonRadius+OFFSET-35, bottomText, centerX, centerY+horizonRadius+OFFSET-25, rollC) + newContent[#newContent + 1] = stringf([[]], -originalRoll, centerX, centerY) + newContent[#newContent + 1] = stringf([[<]], + centerX-5, centerY+horizonRadius+OFFSET-20, centerX+5, centerY+horizonRadius+OFFSET-20, centerX, centerY+horizonRadius+OFFSET-15) + newContent[#newContent +1] = "" + end + local yaw = rollC + if nearPlanet then yaw = getHeading(vec3(core.getConstructWorldOrientationForward())) end + local range = 20 + local yawC = mfloor(yaw) + local yawlen = 0 + local yawy = (centerY + horizonRadius + OFFSET + 20) + local yawx = centerX + if bottomText ~= "YAW" then + yawy = ConvertResolutionY(130) + yawx = ConvertResolutionX(960) + end + local tickerPath = [[ - %s - %d %s - - ]], throtPosX+10, y1, label, throtPosX+10, y2, value, unit) - end - - -- Draw vertical speed indicator - Code by lisa-lionheart - function DrawVerticalSpeed(newContent, altitude) - if (altitude < 200000 and not inAtmo) or (altitude and inAtmo) then - local vSpd = -vec3(core.getWorldVertical()):dot(vec3(core.getWorldVelocity())) - local angle = 0 - if math.abs(vSpd) > 1 then - angle = 45 * math.log(math.abs(vSpd), 10) - if vSpd < 0 then - angle = -angle + %d]],x+5,yawy-12, num) + elseif (i % 5 == 0) then + yawlen = 5 + end + if yawlen == 10 then + tickerPath = stringf([[%s M %f %f v %d]], tickerPath, x, yawy-5, yawlen) + else + tickerPath = stringf([[%s M %f %f v %d]], tickerPath, x, yawy-2.5, yawlen) + end + end + newContent[#newContent + 1] = tickerPath .. [["/>]] + newContent[#newContent + 1] = stringf([[<]], + yawx-5, yawy+10, yawx+5, yawy+10, yawx, yawy+5) + if nearPlanet then bottomText = "HDG" end + newContent[#newContent + 1] = stringf([[" + + %d deg + %s + + ]], yawx, yawy+25, yawC, yawx, yawy+35, bottomText) +end + +function DrawArtificialHorizon(newContent, originalPitch, originalRoll, centerX, centerY, nearPlanet, atmoYaw, speed) + -- ** CIRCLE ALTIMETER - Base Code from Discord @Rainsome = Youtube CaptainKilmar** + local horizonRadius = circleRad -- Aliased global + local pitchX = mfloor(horizonRadius * 3 / 5) + if horizonRadius > 0 then + local pitchC = mfloor(originalPitch) + local len = 0 + local tickerPath = stringf([[]],(horizonRadius - 1), centerX, centerY) + newContent[#newContent + 1] = [[]] + for i = mfloor(pitchC - 30 - pitchC % 5 + 0.5), mfloor(pitchC + 30 + pitchC % 5 + 0.5), 5 do + if (i % 10 == 0) then + len = 30 + elseif (i % 5 == 0) then + len = 20 + end + local y = centerY + (-i * 5 + originalPitch * 5) + if len == 30 then + tickerPath = stringf([[%s M %d %f h %d]], tickerPath, centerX-pitchX-len, y, len) + if inAtmo then + newContent[#newContent + 1] = stringf([[%d]],(-1 * originalRoll), centerX, centerY, centerX-pitchX+10, y, i) + newContent[#newContent + 1] = stringf([[%d]],(-1 * originalRoll), centerX, centerY, centerX+pitchX-10, y, i) + if i == 0 or i == 180 or i == -180 then + newContent[#newContent + 1] = stringf([[]], + (-1 * originalRoll), centerX, centerY, centerX-pitchX+20, y, pitchX*2-40) end - end - newContent[#newContent + 1] = stringf([[ - - 1000 - 100 - 10 - O - -10 - -100 - -1000 - %d m/s - - - - - - - ]], vSpdMeterX, vSpdMeterY, mfloor(vSpd), mfloor(angle)) + else + newContent[#newContent + 1] = stringf([[%d]], centerX-pitchX+10, y, i) + newContent[#newContent + 1] = stringf([[%d]], centerX+pitchX-10, y, i) + end + tickerPath = stringf([[%s M %d %f h %d]], tickerPath, centerX+pitchX, y, len) + else + tickerPath = stringf([[%s M %d %f h %d]], tickerPath, centerX-pitchX-len, y, len) + tickerPath = stringf([[%s M %d %f h %d]], tickerPath, centerX+pitchX, y, len) end end - - function getHeading(forward) -- code provided by tomisunlucky - local up = -vec3(core.getWorldVertical()) - forward = forward - forward:project_on(up) - local north = vec3(0, 0, 1) - north = north - north:project_on(up) - local east = north:cross(up) - local angle = north:angle_between(forward) * constants.rad2deg - if forward:dot(east) < 0 then - angle = 360-angle - end - return angle - end - - function DrawRollLines (newContent, centerX, centerY, originalRoll, bottomText, nearPlanet) - local horizonRadius = circleRad -- Aliased global - local OFFSET = 20 - OFFSET = mfloor(OFFSET ) - local rollC = mfloor(originalRoll) - if nearPlanet then - for i = -45, 45, 5 do - local rot = i - newContent[#newContent + 1] = stringf([[]], rot, centerX, centerY) - len = 5 - if (i % 15 == 0) then - len = 15 - elseif (i % 10 == 0) then - len = 10 - end - newContent[#newContent + 1] = stringf([[]], centerX, centerY + horizonRadius + OFFSET - len, centerX, centerY + horizonRadius + OFFSET) - end - newContent[#newContent + 1] = stringf([[" + newContent[#newContent + 1] = tickerPath .. [["/>]] + local pitchstring = "PITCH" + if not nearPlanet then + pitchstring = "REL PITCH" + end + if originalPitch > 90 and not inAtmo then + originalPitch = 90 - (originalPitch - 90) + elseif originalPitch < -90 and not inAtmo then + originalPitch = -90 - (originalPitch + 90) + end + if horizonRadius > 200 then + if inAtmo then + if speed > minAutopilotSpeed then + newContent[#newContent + 1] = stringf([[" - %s - %d deg + %s + %d deg - ]], centerX, centerY+horizonRadius+OFFSET-35, bottomText, centerX, centerY+horizonRadius+OFFSET-25, rollC) - newContent[#newContent + 1] = stringf([[]], -originalRoll, centerX, centerY) - newContent[#newContent + 1] = stringf([[<]], - centerX-5, centerY+horizonRadius+OFFSET-20, centerX+5, centerY+horizonRadius+OFFSET-20, centerX, centerY+horizonRadius+OFFSET-15) - newContent[#newContent +1] = "" - end - local yaw = rollC - if nearPlanet then yaw = getHeading(vec3(core.getConstructWorldOrientationForward())) end - local range = 20 - local yawC = mfloor(yaw) - local yawlen = 0 - local yawy = (centerY + horizonRadius + OFFSET + 20) - local yawx = centerX - if bottomText ~= "YAW" then - yawy = ConvertResolutionY(130) - yawx = ConvertResolutionX(960) - end - local tickerPath = [[%d]],x+5,yawy-12, num) - elseif (i % 5 == 0) then - yawlen = 5 - end - if yawlen == 10 then - tickerPath = stringf([[%s M %f %f v %d]], tickerPath, x, yawy-5, yawlen) - else - tickerPath = stringf([[%s M %f %f v %d]], tickerPath, x, yawy-2.5, yawlen) + ]], centerX, centerY-15, "Yaw", centerX, centerY+20, atmoYaw) end - end - newContent[#newContent + 1] = tickerPath .. [["/>]] - newContent[#newContent + 1] = stringf([[<]], - yawx-5, yawy+10, yawx+5, yawy+10, yawx, yawy+5) - if nearPlanet then bottomText = "HDG" end - newContent[#newContent + 1] = stringf([[" + newContent[#newContent + 1] = stringf([[]], -originalRoll, centerX, centerY) + else + newContent[#newContent + 1] = stringf([[]], centerX, centerY) + end + newContent[#newContent + 1] = stringf([[< class="pdim txtend">%d]], + centerX-pitchX+25, centerY-5, centerX-pitchX+20, centerY, centerX-pitchX+25, centerY+5, centerX-pitchX+50, centerY+4, pitchC) + newContent[#newContent + 1] = stringf([[< class="pdim txtend">%d]], + centerX+pitchX-25, centerY-5, centerX+pitchX-20, centerY, centerX+pitchX-25, centerY+5, centerX+pitchX-30, centerY+4, pitchC) + newContent[#newContent +1] = "" + end + local thirdHorizontal = mfloor(horizonRadius/3) + newContent[#newContent + 1] = stringf([[]], + centerX-thirdHorizontal, centerY, horizonRadius-thirdHorizontal) + if not inAtmo and nearPlanet then + newContent[#newContent + 1] = stringf([[]], + (-1 * originalRoll), centerX, centerY, centerX-pitchX+10, centerY, pitchX*2-20) + end + newContent[#newContent + 1] = "" + if horizonRadius < 200 then + if inAtmo and speed > minAutopilotSpeed then + newContent[#newContent + 1] = stringf([[" + %s %d deg %s + %d deg - ]], yawx, yawy+25, yawC, yawx, yawy+35, bottomText) - end - - function DrawArtificialHorizon(newContent, originalPitch, originalRoll, centerX, centerY, nearPlanet, atmoYaw, speed) - -- ** CIRCLE ALTIMETER - Base Code from Discord @Rainsome = Youtube CaptainKilmar** - local horizonRadius = circleRad -- Aliased global - local pitchX = mfloor(horizonRadius * 3 / 5) - if horizonRadius > 0 then - local pitchC = mfloor(originalPitch) - local len = 0 - local tickerPath = stringf([[]],(horizonRadius - 1), centerX, centerY) - newContent[#newContent + 1] = [[]] - for i = mfloor(pitchC - 30 - pitchC % 5 + 0.5), mfloor(pitchC + 30 + pitchC % 5 + 0.5), 5 do - if (i % 10 == 0) then - len = 30 - elseif (i % 5 == 0) then - len = 20 - end - local y = centerY + (-i * 5 + originalPitch * 5) - if len == 30 then - tickerPath = stringf([[%s M %d %f h %d]], tickerPath, centerX-pitchX-len, y, len) - if inAtmo then - newContent[#newContent + 1] = stringf([[%d]],(-1 * originalRoll), centerX, centerY, centerX-pitchX+10, y, i) - newContent[#newContent + 1] = stringf([[%d]],(-1 * originalRoll), centerX, centerY, centerX+pitchX-10, y, i) - if i == 0 or i == 180 or i == -180 then - newContent[#newContent + 1] = stringf([[]], - (-1 * originalRoll), centerX, centerY, centerX-pitchX+20, y, pitchX*2-40) - end - else - newContent[#newContent + 1] = stringf([[%d]], centerX-pitchX+10, y, i) - newContent[#newContent + 1] = stringf([[%d]], centerX+pitchX-10, y, i) - end - tickerPath = stringf([[%s M %d %f h %d]], tickerPath, centerX+pitchX, y, len) - else - tickerPath = stringf([[%s M %d %f h %d]], tickerPath, centerX-pitchX-len, y, len) - tickerPath = stringf([[%s M %d %f h %d]], tickerPath, centerX+pitchX, y, len) - end - end - newContent[#newContent + 1] = tickerPath .. [["/>]] - local pitchstring = "PITCH" - if not nearPlanet then - pitchstring = "REL PITCH" - end - if originalPitch > 90 and not inAtmo then - originalPitch = 90 - (originalPitch - 90) - elseif originalPitch < -90 and not inAtmo then - originalPitch = -90 - (originalPitch + 90) - end - if horizonRadius > 200 then - if inAtmo then - if speed > minAutopilotSpeed then - newContent[#newContent + 1] = stringf([[" - - %s - %d deg - - ]], centerX, centerY-15, "Yaw", centerX, centerY+20, atmoYaw) - end - newContent[#newContent + 1] = stringf([[]], -originalRoll, centerX, centerY) - else - newContent[#newContent + 1] = stringf([[]], centerX, centerY) - end - newContent[#newContent + 1] = stringf([[< class="pdim txtend">%d]], - centerX-pitchX+25, centerY-5, centerX-pitchX+20, centerY, centerX-pitchX+25, centerY+5, centerX-pitchX+50, centerY+4, pitchC) - newContent[#newContent + 1] = stringf([[< class="pdim txtend">%d]], - centerX+pitchX-25, centerY-5, centerX+pitchX-20, centerY, centerX+pitchX-25, centerY+5, centerX+pitchX-30, centerY+4, pitchC) - newContent[#newContent +1] = "" - end - local thirdHorizontal = mfloor(horizonRadius/3) - newContent[#newContent + 1] = stringf([[]], - centerX-thirdHorizontal, centerY, horizonRadius-thirdHorizontal) - if not inAtmo and nearPlanet then - newContent[#newContent + 1] = stringf([[]], - (-1 * originalRoll), centerX, centerY, centerX-pitchX+10, centerY, pitchX*2-20) - end - newContent[#newContent + 1] = "" - if horizonRadius < 200 then - if inAtmo and speed > minAutopilotSpeed then - newContent[#newContent + 1] = stringf([[" - - %s - %d deg - %s - %d deg - - ]], centerX, centerY-horizonRadius, pitchstring, centerX, centerY-horizonRadius+10, pitchC, centerX, centerY-15, "Yaw", centerX, centerY+20, atmoYaw) - else - newContent[#newContent + 1] = stringf([[" - - %s - %d deg - - ]], centerX, centerY-horizonRadius, pitchstring, centerX, centerY-horizonRadius+15, pitchC ) - end - end + ]], centerX, centerY-horizonRadius, pitchstring, centerX, centerY-horizonRadius+10, pitchC, centerX, centerY-15, "Yaw", centerX, centerY+20, atmoYaw) + else + newContent[#newContent + 1] = stringf([[" + + %s + %d deg + + ]], centerX, centerY-horizonRadius, pitchstring, centerX, centerY-horizonRadius+15, pitchC ) end end + end +end - function DrawAltitudeDisplay(newContent, altitude, nearPlanet) - local rectX = altMeterX - local rectY = altMeterY - local rectW = 78 - local rectH = 19 +function DrawAltitudeDisplay(newContent, altitude, nearPlanet) + local rectX = altMeterX + local rectY = altMeterY + local rectW = 78 + local rectH = 19 - local gndHeight = AboveGroundLevel() + local gndHeight = AboveGroundLevel() - if gndHeight ~= -1 then - table.insert(newContent, stringf([[ - - AGL: %.1fm - - ]], rectX+rectW, rectY+rectH+20, gndHeight)) - end + if gndHeight ~= -1 then + table.insert(newContent, stringf([[ + + AGL: %.1fm + + ]], rectX+rectW, rectY+rectH+20, gndHeight)) + end - if nearPlanet and ((altitude < 200000 and not inAtmo) or (altitude and inAtmo)) then - table.insert(newContent, stringf([[ - - - - ]], - rectX - 1, rectY - 4, rectW + 2, rectH + 6, - rectX + 1, rectY - 1, rectW - 4, rectH)) - - local index = 0 - local divisor = 1 - local forwardFract = 0 - local isNegative = altitude < 0 - local rolloverDigit = 9 + if nearPlanet and ((altitude < 200000 and not inAtmo) or (altitude and inAtmo)) then + table.insert(newContent, stringf([[ + + + + ]], + rectX - 1, rectY - 4, rectW + 2, rectH + 6, + rectX + 1, rectY - 1, rectW - 4, rectH)) + + local index = 0 + local divisor = 1 + local forwardFract = 0 + local isNegative = altitude < 0 + local rolloverDigit = 9 + if isNegative then + rolloverDigit = 0 + end + local altitude = math.abs(altitude) + while index < 6 do + local glyphW = 11 + local glyphH = 16 + local glyphXOffset = 9 + local glyphYOffset = 14 + local class = "altsm" + + if index > 2 then + glyphH = glyphH + 3 + glyphW = glyphW + 2 + glyphYOffset = glyphYOffset + 2 + glyphXOffset = glyphXOffset - 6 + class = "altbig" + end + + if isNegative then + class = class .. " red" + end + + local digit = (altitude / divisor) % 10 + local intDigit = mfloor(digit) + local fracDigit = mfloor((intDigit + 1) % 10) + + local fract = forwardFract + if index == 0 then + fract = digit - intDigit if isNegative then - rolloverDigit = 0 + fract = 1 - fract end - local altitude = math.abs(altitude) - while index < 6 do - local glyphW = 11 - local glyphH = 16 - local glyphXOffset = 9 - local glyphYOffset = 14 - local class = "altsm" - - if index > 2 then - glyphH = glyphH + 3 - glyphW = glyphW + 2 - glyphYOffset = glyphYOffset + 2 - glyphXOffset = glyphXOffset - 6 - class = "altbig" - end - - if isNegative then - class = class .. " red" - end - - local digit = (altitude / divisor) % 10 - local intDigit = mfloor(digit) - local fracDigit = mfloor((intDigit + 1) % 10) + end - local fract = forwardFract - if index == 0 then - fract = digit - intDigit - if isNegative then - fract = 1 - fract - end - end + if isNegative and (index == 0 or forwardFract ~= 0) then + local temp = fracDigit + fracDigit = intDigit + intDigit = temp + end - if isNegative and (index == 0 or forwardFract ~= 0) then - local temp = fracDigit - fracDigit = intDigit - intDigit = temp - end + local topGlyphOffset = glyphH * (fract - 1) + local botGlyphOffset = topGlyphOffset + glyphH - local topGlyphOffset = glyphH * (fract - 1) - local botGlyphOffset = topGlyphOffset + glyphH - - local x = rectX + glyphXOffset + (6 - index) * glyphW - local y = rectY + glyphYOffset - - -- - table.insert(newContent, stringf([[ - - %d - %d - - ]], class, x, y + topGlyphOffset, fracDigit, x, y + botGlyphOffset, intDigit)) - - index = index + 1 - divisor = divisor * 10 - if intDigit == rolloverDigit then - forwardFract = fract - else - forwardFract = 0 - end - end - table.insert(newContent, [[]]) + local x = rectX + glyphXOffset + (6 - index) * glyphW + local y = rectY + glyphYOffset + + -- + table.insert(newContent, stringf([[ + + %d + %d + + ]], class, x, y + topGlyphOffset, fracDigit, x, y + botGlyphOffset, intDigit)) + + index = index + 1 + divisor = divisor * 10 + if intDigit == rolloverDigit then + forwardFract = fract + else + forwardFract = 0 end end + table.insert(newContent, [[]]) + end +end - function DrawPrograde (newContent, velocity, speed, centerX, centerY) - if (speed > 5 and not inAtmo) or (speed > minAutopilotSpeed) then - local horizonRadius = circleRad -- Aliased globa - local pitchRange = 20 - local yawRange = 20 - local velo = vec3(velocity) - local relativePitch = getRelativePitch(velo) - local relativeYaw = getRelativeYaw(velo) - - local dx = (-relativeYaw/yawRange)*horizonRadius -- Values from -1 to 1 indicating offset from the center - local dy = (relativePitch/pitchRange)*horizonRadius - local x = centerX + dx - local y = centerY + dy - - local distance = math.sqrt((dx)^2 + (dy)^2) - - if distance < horizonRadius then - newContent[#newContent + 1] = stringf('', x, y) - -- Draw a dot or whatever at x,y, it's inside the AH - else - -- x,y is outside the AH. Figure out how to draw an arrow on the edge of the circle pointing to it. - -- First get the angle - -- tan(ang) = o/a, tan(ang) = x/y - -- atan(x/y) = ang (in radians) - -- There is a special overload for doing this on a circle and setting up the signs correctly for the quadrants - local angle = math.atan(dy,dx) - -- Project this onto the circle - -- These are backwards from what they're supposed to be. Don't know why, that's just what makes it work apparently - local projectedX = centerX + horizonRadius*math.cos(angle) -- Needs to be converted to deg? Probably not - local projectedY = centerY + horizonRadius*math.sin(angle) - newContent[#newContent + 1] = stringf('', projectedX, projectedY) - end - relativePitch = getRelativePitch(-velo) - relativeYaw = getRelativeYaw(-velo) - - dx = (-relativeYaw/yawRange)*horizonRadius -- Values from -1 to 1 indicating offset from the center - dy = (relativePitch/pitchRange)*horizonRadius - x = centerX + dx - y = centerY + dy - - distance = math.sqrt((dx)^2 + (dy)^2) - -- Retrograde Dot - if( not inAtmo) then - if distance < horizonRadius then - newContent[#newContent + 1] = stringf('', x, y) - -- Draw a dot or whatever at x,y, it's inside the AH - else - local angle = math.atan(dy,dx) - local projectedX = centerX + horizonRadius*math.cos(angle) -- Needs to be converted to deg? Probably not - local projectedY = centerY + horizonRadius*math.sin(angle) - newContent[#newContent + 1] = stringf('', projectedX, projectedY) - end - end +function DrawPrograde (newContent, velocity, speed, centerX, centerY) + if (speed > 5 and not inAtmo) or (speed > minAutopilotSpeed) then + local horizonRadius = circleRad -- Aliased globa + local pitchRange = 20 + local yawRange = 20 + local velo = vec3(velocity) + local relativePitch = getRelativePitch(velo) + local relativeYaw = getRelativeYaw(velo) + + local dx = (-relativeYaw/yawRange)*horizonRadius -- Values from -1 to 1 indicating offset from the center + local dy = (relativePitch/pitchRange)*horizonRadius + local x = centerX + dx + local y = centerY + dy + + local distance = math.sqrt((dx)^2 + (dy)^2) + + if distance < horizonRadius then + newContent[#newContent + 1] = stringf('', x, y) + -- Draw a dot or whatever at x,y, it's inside the AH + else + -- x,y is outside the AH. Figure out how to draw an arrow on the edge of the circle pointing to it. + -- First get the angle + -- tan(ang) = o/a, tan(ang) = x/y + -- atan(x/y) = ang (in radians) + -- There is a special overload for doing this on a circle and setting up the signs correctly for the quadrants + local angle = math.atan(dy,dx) + -- Project this onto the circle + -- These are backwards from what they're supposed to be. Don't know why, that's just what makes it work apparently + local projectedX = centerX + horizonRadius*math.cos(angle) -- Needs to be converted to deg? Probably not + local projectedY = centerY + horizonRadius*math.sin(angle) + newContent[#newContent + 1] = stringf('', projectedX, projectedY) + end + relativePitch = getRelativePitch(-velo) + relativeYaw = getRelativeYaw(-velo) + + dx = (-relativeYaw/yawRange)*horizonRadius -- Values from -1 to 1 indicating offset from the center + dy = (relativePitch/pitchRange)*horizonRadius + x = centerX + dx + y = centerY + dy + + distance = math.sqrt((dx)^2 + (dy)^2) + -- Retrograde Dot + if( not inAtmo) then + if distance < horizonRadius then + newContent[#newContent + 1] = stringf('', x, y) + -- Draw a dot or whatever at x,y, it's inside the AH + else + local angle = math.atan(dy,dx) + local projectedX = centerX + horizonRadius*math.cos(angle) -- Needs to be converted to deg? Probably not + local projectedY = centerY + horizonRadius*math.sin(angle) + newContent[#newContent + 1] = stringf('', projectedX, projectedY) end end + end +end - function DrawWarnings(newContent) - newContent[#newContent + 1] = stringf( - [[DU Hud Version: %.3f]], - ConvertResolutionX(1900), ConvertResolutionY(1070), VERSION_NUMBER) - newContent[#newContent + 1] = [[]] - if unit.isMouseControlActivated() == 1 then - newContent[#newContent + 1] = stringf([[ - Warning: Invalid Control Scheme Detected]], - ConvertResolutionX(960), ConvertResolutionY(550)) - newContent[#newContent + 1] = stringf([[ - Keyboard Scheme must be selected]], - ConvertResolutionX(960), ConvertResolutionY(600)) - newContent[#newContent + 1] = stringf([[ - Set your preferred scheme in Lua Parameters instead]], - ConvertResolutionX(960), ConvertResolutionY(650)) - end - local warningX = ConvertResolutionX(960) - local brakeY = ConvertResolutionY(860) - local gearY = ConvertResolutionY(880) - local hoverY = ConvertResolutionY(900) - local ewarpY = ConvertResolutionY(960) - local apY = ConvertResolutionY(200) - local turnBurnY = ConvertResolutionY(150) - local gyroY = ConvertResolutionY(960) - if isRemote() == 1 and not RemoteHud then - brakeY = ConvertResolutionY(135) - gearY = ConvertResolutionY(155) - hoverY = ConvertResolutionY(175) - apY = ConvertResolutionY(115) - turnBurnY = ConvertResolutionY(95) - end +function DrawWarnings(newContent) + newContent[#newContent + 1] = stringf( + [[DU Hud Version: %.3f]], + ConvertResolutionX(1900), ConvertResolutionY(1070), VERSION_NUMBER) + newContent[#newContent + 1] = [[]] + if unit.isMouseControlActivated() == 1 then + newContent[#newContent + 1] = stringf([[ + Warning: Invalid Control Scheme Detected]], + ConvertResolutionX(960), ConvertResolutionY(550)) + newContent[#newContent + 1] = stringf([[ + Keyboard Scheme must be selected]], + ConvertResolutionX(960), ConvertResolutionY(600)) + newContent[#newContent + 1] = stringf([[ + Set your preferred scheme in Lua Parameters instead]], + ConvertResolutionX(960), ConvertResolutionY(650)) + end + local warningX = ConvertResolutionX(960) + local brakeY = ConvertResolutionY(860) + local gearY = ConvertResolutionY(880) + local hoverY = ConvertResolutionY(900) + local ewarpY = ConvertResolutionY(960) + local apY = ConvertResolutionY(200) + local turnBurnY = ConvertResolutionY(150) + local gyroY = ConvertResolutionY(960) + if isRemote() == 1 and not RemoteHud then + brakeY = ConvertResolutionY(135) + gearY = ConvertResolutionY(155) + hoverY = ConvertResolutionY(175) + apY = ConvertResolutionY(115) + turnBurnY = ConvertResolutionY(95) + end + if BrakeIsOn then + newContent[#newContent + 1] = stringf([[Brake Engaged]], warningX, brakeY) + end + if inAtmo and RateOfChange < MinimumRateOfChange and velMag > brakeLandingRate+5 then + newContent[#newContent + 1] = stringf([[** STALL WARNING **]], warningX, apY+50) + end + if gyroIsOn then + newContent[#newContent + 1] = stringf([[Gyro Enabled]], warningX, gyroY) + end + if GearExtended then + if hasGear then + newContent[#newContent + 1] = stringf([[Gear Extended]], + warningX, gearY) + else + newContent[#newContent + 1] = stringf([[Landed (G: Takeoff)]], warningX, + gearY) + end + newContent[#newContent + 1] = stringf([[Hover Height: %s]], + warningX, hoverY, + getDistanceDisplayString(Nav:getTargetGroundAltitude())) + end + if isBoosting then + newContent[#newContent + 1] = stringf([[ROCKET BOOST ENABLED]], + warningX, ewarpY+20) + end + if antigrav and not ExternalAGG and antigrav.getState() == 1 and AntigravTargetAltitude ~= nil then + if math.abs(coreAltitude - antigrav.getBaseAltitude()) < 501 then + newContent[#newContent + 1] = stringf([[AGG On - Target Altitude: %d Singluarity Altitude: %d]], + warningX, apY+20, mfloor(AntigravTargetAltitude), mfloor(antigrav.getBaseAltitude())) + else + newContent[#newContent + 1] = stringf([[AGG On - Target Altitude: %d Singluarity Altitude: %d]], + warningX, apY+20, mfloor(AntigravTargetAltitude), mfloor(antigrav.getBaseAltitude())) + end + elseif Autopilot and AutopilotTargetName ~= "None" then + newContent[#newContent + 1] = stringf([[Autopilot %s]], + warningX, apY+20, AutopilotStatus) + elseif LockPitch ~= nil then + newContent[#newContent + 1] = stringf([[LockedPitch: %d]], + warningX, apY+20, mfloor(LockPitch)) + elseif followMode then + newContent[#newContent + 1] = stringf([[Follow Mode Engaged]], + warningX, apY+20) + elseif Reentry then + newContent[#newContent + 1] = stringf([[Parachute Re-entry in Progress]], + warningX, apY+20) + end + if AltitudeHold then + if AutoTakeoff then + newContent[#newContent + 1] = stringf([[Ascent to %s]], + warningX, apY, getDistanceDisplayString(HoldAltitude)) if BrakeIsOn then - newContent[#newContent + 1] = stringf([[Brake Engaged]], warningX, brakeY) - end - if inAtmo and RateOfChange < MinimumRateOfChange and velMag > brakeLandingRate+5 then - newContent[#newContent + 1] = stringf([[** STALL WARNING **]], warningX, apY+50) - end - if gyroIsOn then - newContent[#newContent + 1] = stringf([[Gyro Enabled]], warningX, gyroY) - end - if GearExtended then - if hasGear then - newContent[#newContent + 1] = stringf([[Gear Extended]], - warningX, gearY) - else - newContent[#newContent + 1] = stringf([[Landed (G: Takeoff)]], warningX, - gearY) - end - newContent[#newContent + 1] = stringf([[Hover Height: %s]], - warningX, hoverY, - getDistanceDisplayString(Nav:getTargetGroundAltitude())) - end - if isBoosting then - newContent[#newContent + 1] = stringf([[ROCKET BOOST ENABLED]], - warningX, ewarpY+20) - end - if antigrav and not ExternalAGG and antigrav.getState() == 1 and AntigravTargetAltitude ~= nil then - if math.abs(coreAltitude - antigrav.getBaseAltitude()) < 501 then - newContent[#newContent + 1] = stringf([[AGG On - Target Altitude: %d Singluarity Altitude: %d]], - warningX, apY+20, mfloor(AntigravTargetAltitude), mfloor(antigrav.getBaseAltitude())) - else - newContent[#newContent + 1] = stringf([[AGG On - Target Altitude: %d Singluarity Altitude: %d]], - warningX, apY+20, mfloor(AntigravTargetAltitude), mfloor(antigrav.getBaseAltitude())) - end - elseif Autopilot and AutopilotTargetName ~= "None" then - newContent[#newContent + 1] = stringf([[Autopilot %s]], - warningX, apY+20, AutopilotStatus) - elseif LockPitch ~= nil then - newContent[#newContent + 1] = stringf([[LockedPitch: %d]], - warningX, apY+20, mfloor(LockPitch)) - elseif followMode then - newContent[#newContent + 1] = stringf([[Follow Mode Engaged]], - warningX, apY+20) - elseif Reentry then - newContent[#newContent + 1] = stringf([[Parachute Re-entry in Progress]], - warningX, apY+20) - end - if AltitudeHold then - if AutoTakeoff then - newContent[#newContent + 1] = stringf([[Ascent to %s]], - warningX, apY, getDistanceDisplayString(HoldAltitude)) - if BrakeIsOn then - newContent[#newContent + 1] = stringf( - [[Throttle Up and Disengage Brake For Takeoff]], - warningX, apY + 50) - end - else - newContent[#newContent + 1] = stringf([[Altitude Hold: %s]], - warningX, apY, getDistanceDisplayString2(HoldAltitude)) - end - end - if BrakeLanding then - if StrongBrakes then - newContent[#newContent + 1] = stringf([[Brake-Landing]], warningX, apY) - else - newContent[#newContent + 1] = stringf([[Coast-Landing]], warningX, apY) - end - end - if ProgradeIsOn then - newContent[#newContent + 1] = stringf([[Prograde Alignment]], - warningX, apY) - end - if RetrogradeIsOn then - newContent[#newContent + 1] = stringf([[Retrograde Alignment]], - warningX, apY) - end - if TurnBurn then - newContent[#newContent + 1] = stringf([[Turn & Burn Braking]], - warningX, turnBurnY) - end - if VectorToTarget then - newContent[#newContent + 1] = stringf([[%s]], warningX, - apY+30, VectorStatus) + newContent[#newContent + 1] = stringf( + [[Throttle Up and Disengage Brake For Takeoff]], + warningX, apY + 50) end - - newContent[#newContent + 1] = "" + else + newContent[#newContent + 1] = stringf([[Altitude Hold: %s]], + warningX, apY, getDistanceDisplayString2(HoldAltitude)) end + end + if BrakeLanding then + if StrongBrakes then + newContent[#newContent + 1] = stringf([[Brake-Landing]], warningX, apY) + else + newContent[#newContent + 1] = stringf([[Coast-Landing]], warningX, apY) + end + end + if ProgradeIsOn then + newContent[#newContent + 1] = stringf([[Prograde Alignment]], + warningX, apY) + end + if RetrogradeIsOn then + newContent[#newContent + 1] = stringf([[Retrograde Alignment]], + warningX, apY) + end + if TurnBurn then + newContent[#newContent + 1] = stringf([[Turn & Burn Braking]], + warningX, turnBurnY) + end + if VectorToTarget then + newContent[#newContent + 1] = stringf([[%s]], warningX, + apY+30, VectorStatus) + end - function DisplayOrbitScreen(newContent) - if orbit ~= nil and atmosphere() < 0.2 and planet ~= nil and orbit.apoapsis ~= nil and - orbit.periapsis ~= nil and orbit.period ~= nil and orbit.apoapsis.speed > 5 and DisplayOrbit then - -- If orbits are up, let's try drawing a mockup - local orbitMapX = 75 - local orbitMapY = 0 - local orbitMapSize = 250 -- Always square - local pad = 4 - orbitMapY = orbitMapY + pad - local orbitInfoYOffset = 15 - local x = orbitMapX + orbitMapSize + orbitMapX / 2 + pad - local y = orbitMapY + orbitMapSize / 2 + 5 + pad - - local rx, ry, scale, xOffset - rx = orbitMapSize / 4 - xOffset = 0 - - newContent[#newContent + 1] = [[]] - -- Draw a darkened box around it to keep it visible - newContent[#newContent + 1] = stringf( - '', - orbitMapSize + orbitMapX * 2, orbitMapSize + orbitMapY, pad, pad) - - if orbit.periapsis ~= nil and orbit.apoapsis ~= nil then - scale = (orbit.apoapsis.altitude + orbit.periapsis.altitude + planet.radius * 2) / (rx * 2) - ry = (planet.radius + orbit.periapsis.altitude + - (orbit.apoapsis.altitude - orbit.periapsis.altitude) / 2) / scale * - (1 - orbit.eccentricity) - xOffset = rx - orbit.periapsis.altitude / scale - planet.radius / scale - - local ellipseColor = "" - if orbit.periapsis.altitude <= 0 then - ellipseColor = 'redout' - end - newContent[#newContent + 1] = stringf( - [[]], - ellipseColor, orbitMapX + orbitMapSize / 2 + xOffset + pad, - orbitMapY + orbitMapSize / 2 + pad, rx, ry) - newContent[#newContent + 1] = stringf( - '', - orbitMapX + orbitMapSize / 2 + pad, - orbitMapY + orbitMapSize / 2 + pad, planet.radius / scale) - end - - if orbit.apoapsis ~= nil and orbit.apoapsis.speed < MaxGameVelocity and orbit.apoapsis.speed > 1 then - newContent[#newContent + 1] = stringf( - [[]], - x - 35, y - 5, orbitMapX + orbitMapSize / 2 + rx + xOffset, y - 5) - newContent[#newContent + 1] = stringf([[Apoapsis]], x, y) - y = y + orbitInfoYOffset - newContent[#newContent + 1] = stringf([[%s]], x, y, - getDistanceDisplayString(orbit.apoapsis.altitude)) - y = y + orbitInfoYOffset - newContent[#newContent + 1] = stringf([[%s]], x, y, - FormatTimeString(orbit.timeToApoapsis)) - y = y + orbitInfoYOffset - newContent[#newContent + 1] = stringf([[%s]], x, y, - getSpeedDisplayString(orbit.apoapsis.speed)) - end - - y = orbitMapY + orbitMapSize / 2 + 5 + pad - x = orbitMapX - orbitMapX / 2 + 10 + pad - - if orbit.periapsis ~= nil and orbit.periapsis.speed < MaxGameVelocity and orbit.periapsis.speed > 1 then - newContent[#newContent + 1] = stringf( - [[]], - x + 35, y - 5, orbitMapX + orbitMapSize / 2 - rx + xOffset, y - 5) - newContent[#newContent + 1] = stringf([[Periapsis]], x, y) - y = y + orbitInfoYOffset - newContent[#newContent + 1] = stringf([[%s]], x, y, - getDistanceDisplayString(orbit.periapsis.altitude)) - y = y + orbitInfoYOffset - newContent[#newContent + 1] = stringf([[%s]], x, y, - FormatTimeString(orbit.timeToPeriapsis)) - y = y + orbitInfoYOffset - newContent[#newContent + 1] = stringf([[%s]], x, y, - getSpeedDisplayString(orbit.periapsis.speed)) - end - - -- Add a label for the planet - newContent[#newContent + 1] = stringf([[%s]], - orbitMapX + orbitMapSize / 2 + pad, 20 + pad, planet.name) - - if orbit.period ~= nil and orbit.periapsis ~= nil and orbit.apoapsis ~= nil and orbit.apoapsis.speed > 1 then - local apsisRatio = (orbit.timeToApoapsis / orbit.period) * 2 * math.pi - -- x = xr * cos(t) - -- y = yr * sin(t) - local shipX = rx * math.cos(apsisRatio) - local shipY = ry * math.sin(apsisRatio) + newContent[#newContent + 1] = "" +end - newContent[#newContent + 1] = stringf( - '', - orbitMapX + orbitMapSize / 2 + shipX + xOffset + pad, - orbitMapY + orbitMapSize / 2 + shipY + pad) - end +function DisplayOrbitScreen(newContent) + if orbit ~= nil and atmosphere() < 0.2 and planet ~= nil and orbit.apoapsis ~= nil and + orbit.periapsis ~= nil and orbit.period ~= nil and orbit.apoapsis.speed > 5 and DisplayOrbit then + -- If orbits are up, let's try drawing a mockup + local orbitMapX = 75 + local orbitMapY = 0 + local orbitMapSize = 250 -- Always square + local pad = 4 + orbitMapY = orbitMapY + pad + local orbitInfoYOffset = 15 + local x = orbitMapX + orbitMapSize + orbitMapX / 2 + pad + local y = orbitMapY + orbitMapSize / 2 + 5 + pad + + local rx, ry, scale, xOffset + rx = orbitMapSize / 4 + xOffset = 0 + + newContent[#newContent + 1] = [[]] + -- Draw a darkened box around it to keep it visible + newContent[#newContent + 1] = stringf( + '', + orbitMapSize + orbitMapX * 2, orbitMapSize + orbitMapY, pad, pad) - newContent[#newContent + 1] = [[]] - -- Once we have all that, we should probably rotate the entire thing so that the ship is always at the bottom so you can see AP and PE move? + if orbit.periapsis ~= nil and orbit.apoapsis ~= nil then + scale = (orbit.apoapsis.altitude + orbit.periapsis.altitude + planet.radius * 2) / (rx * 2) + ry = (planet.radius + orbit.periapsis.altitude + + (orbit.apoapsis.altitude - orbit.periapsis.altitude) / 2) / scale * + (1 - orbit.eccentricity) + xOffset = rx - orbit.periapsis.altitude / scale - planet.radius / scale + local ellipseColor = "" + if orbit.periapsis.altitude <= 0 then + ellipseColor = 'redout' end + newContent[#newContent + 1] = stringf( + [[]], + ellipseColor, orbitMapX + orbitMapSize / 2 + xOffset + pad, + orbitMapY + orbitMapSize / 2 + pad, rx, ry) + newContent[#newContent + 1] = stringf( + '', + orbitMapX + orbitMapSize / 2 + pad, + orbitMapY + orbitMapSize / 2 + pad, planet.radius / scale) end - -- Planet Info - https://gitlab.com/JayleBreak/dualuniverse/-/tree/master/DUflightfiles/autoconf/custom with minor modifications - function Atlas() - return { - [0] = { - [1] = { - GM = 6930729684, - bodyId = 1, - center = { - x = 17465536.000, - y = 22665536.000, - z = -34464.000 - }, - name = 'Madis', - planetarySystemId = 0, - radius = 44300, - atmos = true, - gravity = 0.36 - }, - [2] = { - GM = 157470826617, - bodyId = 2, - center = { - x = -8.000, - y = -8.000, - z = -126303.000 - }, - name = 'Alioth', - planetarySystemId = 0, - radius = 126068, - atmos = true, - gravity = 1.01 - }, - [3] = { - GM = 11776905000, - bodyId = 3, - center = { - x = 29165536.000, - y = 10865536.000, - z = 65536.000 - }, - name = 'Thades', - planetarySystemId = 0, - radius = 49000, - atmos = true, - gravity = 0.50 - }, - [4] = { - GM = 14893847582, - bodyId = 4, - center = { - x = -13234464.000, - y = 55765536.000, - z = 465536.000 - }, - name = 'Talemai', - planetarySystemId = 0, - radius = 57450, - atmos = true, - gravity = 0.46 - }, - [5] = { - GM = 16951680000, - bodyId = 5, - center = { - x = -43534464.000, - y = 22565536.000, - z = -48934464.000 - }, - name = 'Feli', - planetarySystemId = 0, - radius = 60000, - atmos = true, - gravity = 0.48 - }, - [6] = { - GM = 10502547741, - bodyId = 6, - center = { - x = 52765536.000, - y = 27165538.000, - z = 52065535.000 - }, - name = 'Sicari', - planetarySystemId = 0, - radius = 51100, - atmos = true, - gravity = 0.41 - }, - [7] = { - GM = 13033380591, - bodyId = 7, - center = { - x = 58665538.000, - y = 29665535.000, - z = 58165535.000 - }, - name = 'Sinnen', - planetarySystemId = 0, - radius = 54950, - atmos = true, - gravity = 0.44 - }, - [8] = { - GM = 18477723600, - bodyId = 8, - center = { - x = 80865538.000, - y = 54665536.000, - z = -934463.940 - }, - name = 'Teoma', - planetarySystemId = 0, - radius = 62000, - atmos = true, - gravity = 0.49 - }, - [9] = { - GM = 18606274330, - bodyId = 9, - center = { - x = -94134462.000, - y = 12765534.000, - z = -3634464.000 - }, - name = 'Jago', - planetarySystemId = 0, - radius = 61590, - atmos = true, - gravity = 0.50 - }, - [10] = { - GM = 78480000, - bodyId = 10, - center = { - x = 17448118.224, - y = 22966846.286, - z = 143078.820 - }, - name = 'Madis Moon 1', - planetarySystemId = 0, - radius = 10000, - atmos = false, - gravity = 0.08 - }, - [11] = { - GM = 237402000, - bodyId = 11, - center = { - x = 17194626.000, - y = 22243633.880, - z = -214962.810 - }, - name = 'Madis Moon 2', - planetarySystemId = 0, - radius = 11000, - atmos = false, - gravity = 0.10 - }, - [12] = { - GM = 265046609, - bodyId = 12, - center = { - x = 17520614.000, - y = 22184730.000, - z = -309989.990 - }, - name = 'Madis Moon 3', - planetarySystemId = 0, - radius = 15005, - atmos = false, - gravity = 0.12 - }, - [21] = { - GM = 2118960000, - bodyId = 21, - center = { - x = 457933.000, - y = -1509011.000, - z = 115524.000 - }, - name = 'Alioth Moon 1', - planetarySystemId = 0, - radius = 30000, - atmos = false, - gravity = 0.24 - }, - [22] = { - GM = 2165833514, - bodyId = 22, - center = { - x = -1692694.000, - y = 729681.000, - z = -411464.000 - }, - name = 'Alioth Moon 4', - planetarySystemId = 0, - radius = 30330, - atmos = false, - gravity = 0.24 - }, - [26] = { - GM = 68234043600, - bodyId = 26, - center = { - x = -1404835.000, - y = 562655.000, - z = -285074.000 - }, - name = 'Sanctuary', - planetarySystemId = 0, - radius = 83400, - atmos = true, - gravity = 1.00 - }, - [30] = { - GM = 211564034, - bodyId = 30, - center = { - x = 29214402.000, - y = 10907080.695, - z = 433858.200 - }, - name = 'Thades Moon 1', - planetarySystemId = 0, - radius = 14002, - atmos = false, - gravity = 0.11 - }, - [31] = { - GM = 264870000, - bodyId = 31, - center = { - x = 29404193.000, - y = 10432768.000, - z = 19554.131 - }, - name = 'Thades Moon 2', - planetarySystemId = 0, - radius = 15000, - atmos = false, - gravity = 0.12 - }, - [40] = { - GM = 141264000, - bodyId = 40, - center = { - x = -13503090.000, - y = 55594325.000, - z = 769838.640 - }, - name = 'Talemai Moon 2', - planetarySystemId = 0, - radius = 12000, - atmos = false, - gravity = 0.10 - }, - [41] = { - GM = 106830900, - bodyId = 41, - center = { - x = -12800515.000, - y = 55700259.000, - z = 325207.840 - }, - name = 'Talemai Moon 3', - planetarySystemId = 0, - radius = 11000, - atmos = false, - gravity = 0.09 - }, - [42] = { - GM = 264870000, - bodyId = 42, - center = { - x = -13058408.000, - y = 55781856.000, - z = 740177.760 - }, - name = 'Talemai Moon 1', - planetarySystemId = 0, - radius = 15000, - atmos = false, - gravity = 0.12 - }, - [50] = { - GM = 499917600, - bodyId = 50, - center = { - x = -43902841.780, - y = 22261034.700, - z = -48862386.000 - }, - name = 'Feli Moon 1', - planetarySystemId = 0, - radius = 14000, - atmos = false, - gravity = 0.11 - }, - [70] = { - GM = 396912600, - bodyId = 70, - center = { - x = 58969616.000, - y = 29797945.000, - z = 57969449.000 - }, - name = 'Sinnen Moon 1', - planetarySystemId = 0, - radius = 17000, - atmos = false, - gravity = 0.14 - }, - [100] = { - GM = 13975172474, - bodyId = 100, - center = { - x = 98865536.000, - y = -13534464.000, - z = -934461.990 - }, - name = 'Lacobus', - planetarySystemId = 0, - radius = 55650, - atmos = true, - gravity = 0.46 - }, - [101] = { - GM = 264870000, - bodyId = 101, - center = { - x = 98905288.170, - y = -13950921.100, - z = -647589.530 - }, - name = 'Lacobus Moon 3', - planetarySystemId = 0, - radius = 15000, - atmos = false, - gravity = 0.12 - }, - [102] = { - GM = 444981600, - bodyId = 102, - center = { - x = 99180968.000, - y = -13783862.000, - z = -926156.400 - }, - name = 'Lacobus Moon 1', - planetarySystemId = 0, - radius = 18000, - atmos = false, - gravity = 0.14 - }, - [103] = { - GM = 211503600, - bodyId = 103, - center = { - x = 99250052.000, - y = -13629215.000, - z = -1059341.400 - }, - name = 'Lacobus Moon 2', - planetarySystemId = 0, - radius = 14000, - atmos = false, - gravity = 0.11 - }, - [110] = { - GM = 9204742375, - bodyId = 110, - center = { - x = 14165536.000, - y = -85634465.000, - z = -934464.300 - }, - name = 'Symeon', - planetarySystemId = 0, - radius = 49050, - atmos = true, - gravity = 0.39 - }, - [120] = { - GM = 7135606629, - bodyId = 120, - center = { - x = 2865536.700, - y = -99034464.000, - z = -934462.020 - }, - name = 'Ion', - planetarySystemId = 0, - radius = 44950, - atmos = true, - gravity = 0.36 - }, - [121] = { - GM = 106830900, - bodyId = 121, - center = { - x = 2472916.800, - y = -99133747.000, - z = -1133582.800 - }, - name = 'Ion Moon 1', - planetarySystemId = 0, - radius = 11000, - atmos = false, - gravity = 0.09 - }, - [122] = { - GM = 176580000, - bodyId = 122, - center = { - x = 2995424.500, - y = -99275010.000, - z = -1378480.700 - }, - name = 'Ion Moon 2', - planetarySystemId = 0, - radius = 15000, - atmos = false, - gravity = 0.12 - }, - } - } - end - atlas = Atlas() - for k, v in pairs(atlas[0]) do - if minAtlasX == nil or v.center.x < minAtlasX then - minAtlasX = v.center.x - end - if maxAtlasX == nil or v.center.x > maxAtlasX then - maxAtlasX = v.center.x - end - if minAtlasY == nil or v.center.y < minAtlasY then - minAtlasY = v.center.y - end - if maxAtlasY == nil or v.center.y > maxAtlasY then - maxAtlasY = v.center.y - end - end - GalaxyMapHTML = "" -- No starting SVG tag so we can add it where we want it - -- Figure out our scale here... - local xRatio = 1.1 * (maxAtlasX - minAtlasX) / 1920 -- Add 10% for padding - local yRatio = 1.4 * (maxAtlasY - minAtlasY) / 1080 -- Extra so we can get ion back in - for k, v in pairs(atlas[0]) do - -- Draw a circle at the scaled coordinates - local x = 960 + (v.center.x / xRatio) - local y = 540 + (v.center.y / yRatio) - GalaxyMapHTML = - GalaxyMapHTML .. '' - if not string.match(v.name, "Moon") and not string.match(v.name, "Sanctuary") then - GalaxyMapHTML = GalaxyMapHTML .. "" .. - v.name .. "" - end + if orbit.apoapsis ~= nil and orbit.apoapsis.speed < MaxGameVelocity and orbit.apoapsis.speed > 1 then + newContent[#newContent + 1] = stringf( + [[]], + x - 35, y - 5, orbitMapX + orbitMapSize / 2 + rx + xOffset, y - 5) + newContent[#newContent + 1] = stringf([[Apoapsis]], x, y) + y = y + orbitInfoYOffset + newContent[#newContent + 1] = stringf([[%s]], x, y, + getDistanceDisplayString(orbit.apoapsis.altitude)) + y = y + orbitInfoYOffset + newContent[#newContent + 1] = stringf([[%s]], x, y, + FormatTimeString(orbit.timeToApoapsis)) + y = y + orbitInfoYOffset + newContent[#newContent + 1] = stringf([[%s]], x, y, + getSpeedDisplayString(orbit.apoapsis.speed)) + end + + y = orbitMapY + orbitMapSize / 2 + 5 + pad + x = orbitMapX - orbitMapX / 2 + 10 + pad + + if orbit.periapsis ~= nil and orbit.periapsis.speed < MaxGameVelocity and orbit.periapsis.speed > 1 then + newContent[#newContent + 1] = stringf( + [[]], + x + 35, y - 5, orbitMapX + orbitMapSize / 2 - rx + xOffset, y - 5) + newContent[#newContent + 1] = stringf([[Periapsis]], x, y) + y = y + orbitInfoYOffset + newContent[#newContent + 1] = stringf([[%s]], x, y, + getDistanceDisplayString(orbit.periapsis.altitude)) + y = y + orbitInfoYOffset + newContent[#newContent + 1] = stringf([[%s]], x, y, + FormatTimeString(orbit.timeToPeriapsis)) + y = y + orbitInfoYOffset + newContent[#newContent + 1] = stringf([[%s]], x, y, + getSpeedDisplayString(orbit.periapsis.speed)) + end + + -- Add a label for the planet + newContent[#newContent + 1] = stringf([[%s]], + orbitMapX + orbitMapSize / 2 + pad, 20 + pad, planet.name) + + if orbit.period ~= nil and orbit.periapsis ~= nil and orbit.apoapsis ~= nil and orbit.apoapsis.speed > 1 then + local apsisRatio = (orbit.timeToApoapsis / orbit.period) * 2 * math.pi + -- x = xr * cos(t) + -- y = yr * sin(t) + local shipX = rx * math.cos(apsisRatio) + local shipY = ry * math.sin(apsisRatio) + + newContent[#newContent + 1] = stringf( + '', + orbitMapX + orbitMapSize / 2 + shipX + xOffset + pad, + orbitMapY + orbitMapSize / 2 + shipY + pad) end - -- Draw a 'You Are Here' - face edition - local pos = vec3(core.getConstructWorldPos()) - local x = 960 + pos.x / xRatio - local y = 540 + pos.y / yRatio - GalaxyMapHTML = GalaxyMapHTML .. '' - GalaxyMapHTML = GalaxyMapHTML .. "You Are Here" - GalaxyMapHTML = GalaxyMapHTML .. [[]] - MapXRatio = xRatio - MapYRatio = yRatio - if screen_2 then - screen_2.setHTML('' .. GalaxyMapHTML) -- This is permanent and doesn't change - -- Draw a 'You Are Here' - screen edition - local pos = vec3(core.getConstructWorldPos()) - local x = 960 + pos.x / xRatio - local y = 540 + pos.y / yRatio - GalaxyMapHTML = '' - GalaxyMapHTML = GalaxyMapHTML .. "You Are Here" - YouAreHere = screen_2.addContent((x - 80) / 19.20, (y - 80) / 10.80, GalaxyMapHTML) - end - - function PlanetRef() - --[[ START OF LOCAL IMPLEMENTATION DETAILS ]]-- - -- Type checks - local function isNumber(n) - return type(n) == 'number' - end - local function isSNumber(n) - return type(tonumber(n)) == 'number' - end - local function isTable(t) - return type(t) == 'table' - end - local function isString(s) - return type(s) == 'string' - end - local function isVector(v) - return isTable(v) and isNumber(v.x and v.y and v.z) - end - local function isMapPosition(m) - return isTable(m) and isNumber(m.latitude and m.longitude and m.altitude and m.bodyId and m.systemId) - end - -- Constants - local deg2rad = math.pi / 180 - local rad2deg = 180 / math.pi - local epsilon = 1e-10 - local num = ' *([+-]?%d+%.?%d*e?[+-]?%d*)' - local posPattern = '::pos{' .. num .. ',' .. num .. ',' .. num .. ',' .. num .. ',' .. num .. '}' - -- Utilities - local utils = require('cpml.utils') - local vec3 = require('cpml.vec3') - local clamp = utils.clamp - local function float_eq(a, b) - if a == 0 then - return math.abs(b) < 1e-09 - end - if b == 0 then - return math.abs(a) < 1e-09 - end - return math.abs(a - b) < math.max(math.abs(a), math.abs(b)) * epsilon - end - local function formatNumber(n) - local result = string.gsub(string.reverse(stringf('%.4f', n)), '^0*%.?', '') - return result == '' and '0' or string.reverse(result) - end - local function formatValue(obj) - if isVector(obj) then - return stringf('{x=%.3f,y=%.3f,z=%.3f}', obj.x, obj.y, obj.z) - end - if isTable(obj) and not getmetatable(obj) then - local list = {} - local nxt = next(obj) - if type(nxt) == 'nil' or nxt == 1 then -- assume this is an array - list = obj - else - for k, v in pairs(obj) do - local value = formatValue(v) - if type(k) == 'number' then - table.insert(list, stringf('[%s]=%s', k, value)) - else - table.insert(list, stringf('%s=%s', k, value)) - end - end - end - return stringf('{%s}', table.concat(list, ',')) - end - if isString(obj) then - return stringf("'%s'", obj:gsub("'", [[\']])) - end - return tostring(obj) - end - -- CLASSES - -- BodyParameters: Attributes of planetary bodies (planets and moons) - local BodyParameters = {} - BodyParameters.__index = BodyParameters - BodyParameters.__tostring = function(obj, indent) - local keys = {} - for k in pairs(obj) do - table.insert(keys, k) - end - table.sort(keys) - local list = {} - for _, k in ipairs(keys) do - local value = formatValue(obj[k]) - if type(k) == 'number' then - table.insert(list, stringf('[%s]=%s', k, value)) - else - table.insert(list, stringf('%s=%s', k, value)) - end - end - if indent then - return stringf('%s%s', indent, table.concat(list, ',\n' .. indent)) - end - return stringf('{%s}', table.concat(list, ',')) - end - BodyParameters.__eq = function(lhs, rhs) - return lhs.planetarySystemId == rhs.planetarySystemId and lhs.bodyId == rhs.bodyId and - float_eq(lhs.radius, rhs.radius) and float_eq(lhs.center.x, rhs.center.x) and - float_eq(lhs.center.y, rhs.center.y) and float_eq(lhs.center.z, rhs.center.z) and - float_eq(lhs.GM, rhs.GM) - end - local function mkBodyParameters(systemId, bodyId, radius, worldCoordinates, GM) - -- 'worldCoordinates' can be either table or vec3 - assert(isSNumber(systemId), 'Argument 1 (planetarySystemId) must be a number:' .. type(systemId)) - assert(isSNumber(bodyId), 'Argument 2 (bodyId) must be a number:' .. type(bodyId)) - assert(isSNumber(radius), 'Argument 3 (radius) must be a number:' .. type(radius)) - assert(isTable(worldCoordinates), - 'Argument 4 (worldCoordinates) must be a array or vec3.' .. type(worldCoordinates)) - assert(isSNumber(GM), 'Argument 5 (GM) must be a number:' .. type(GM)) - return setmetatable({ - planetarySystemId = tonumber(systemId), - bodyId = tonumber(bodyId), - radius = tonumber(radius), - center = vec3(worldCoordinates), - GM = tonumber(GM) - }, BodyParameters) - end - -- MapPosition: Geographical coordinates of a point on a planetary body. - local MapPosition = {} - MapPosition.__index = MapPosition - MapPosition.__tostring = function(p) - return stringf('::pos{%d,%d,%s,%s,%s}', p.systemId, p.bodyId, formatNumber(p.latitude * rad2deg), - formatNumber(p.longitude * rad2deg), formatNumber(p.altitude)) - end - MapPosition.__eq = function(lhs, rhs) - return lhs.bodyId == rhs.bodyId and lhs.systemId == rhs.systemId and - float_eq(lhs.latitude, rhs.latitude) and float_eq(lhs.altitude, rhs.altitude) and - (float_eq(lhs.longitude, rhs.longitude) or float_eq(lhs.latitude, math.pi / 2) or - float_eq(lhs.latitude, -math.pi / 2)) - end - -- latitude and longitude are in degrees while altitude is in meters - local function mkMapPosition(overload, bodyId, latitude, longitude, altitude) - local systemId = overload -- Id or '::pos{...}' string - - if isString(overload) and not longitude and not altitude and not bodyId and not latitude then - systemId, bodyId, latitude, longitude, altitude = string.match(overload, posPattern) - assert(systemId, 'Argument 1 (position string) is malformed.') - else - assert(isSNumber(systemId), 'Argument 1 (systemId) must be a number:' .. type(systemId)) - assert(isSNumber(bodyId), 'Argument 2 (bodyId) must be a number:' .. type(bodyId)) - assert(isSNumber(latitude), 'Argument 3 (latitude) must be in degrees:' .. type(latitude)) - assert(isSNumber(longitude), 'Argument 4 (longitude) must be in degrees:' .. type(longitude)) - assert(isSNumber(altitude), 'Argument 5 (altitude) must be in meters:' .. type(altitude)) - end - systemId = tonumber(systemId) - bodyId = tonumber(bodyId) - latitude = tonumber(latitude) - longitude = tonumber(longitude) - altitude = tonumber(altitude) - if bodyId == 0 then -- this is a hack to represent points in space - return setmetatable({ - latitude = latitude, - longitude = longitude, - altitude = altitude, - bodyId = bodyId, - systemId = systemId - }, MapPosition) - end - return setmetatable({ - latitude = deg2rad * clamp(latitude, -90, 90), - longitude = deg2rad * (longitude % 360), - altitude = altitude, - bodyId = bodyId, - systemId = systemId - }, MapPosition) - end - -- PlanetarySystem - map body IDs to BodyParameters - local PlanetarySystem = {} - PlanetarySystem.__index = PlanetarySystem - PlanetarySystem.__tostring = function(obj, indent) - local sep = indent and (indent .. ' ') - local bdylist = {} - local keys = {} - for k in pairs(obj) do - table.insert(keys, k) - end - table.sort(keys) - for _, bi in ipairs(keys) do - bdy = obj[bi] - local bdys = BodyParameters.__tostring(bdy, sep) - if indent then - table.insert(bdylist, stringf('[%s]={\n%s\n%s}', bi, bdys, indent)) - else - table.insert(bdylist, stringf(' [%s]=%s', bi, bdys)) - end - end - if indent then - return stringf('\n%s%s%s', indent, table.concat(bdylist, ',\n' .. indent), indent) - end - return stringf('{\n%s\n}', table.concat(bdylist, ',\n')) - end - local function mkPlanetarySystem(referenceTable) - local atlas = {} - local pid - for _, v in pairs(referenceTable) do - local id = v.planetarySystemId - if type(id) ~= 'number' then - error('Invalid planetary system ID: ' .. tostring(id)) - elseif pid and id ~= pid then - error('Mismatch planetary system IDs: ' .. id .. ' and ' .. pid) - end - local bid = v.bodyId - if type(bid) ~= 'number' then - error('Invalid body ID: ' .. tostring(bid)) - elseif atlas[bid] then - error('Duplicate body ID: ' .. tostring(bid)) - end - setmetatable(v.center, getmetatable(vec3.unit_x)) - atlas[bid] = setmetatable(v, BodyParameters) - pid = id - end - return setmetatable(atlas, PlanetarySystem) - end - -- PlanetaryReference - map planetary system ID to PlanetarySystem - PlanetaryReference = {} - local function mkPlanetaryReference(referenceTable) - return setmetatable({ - galaxyAtlas = referenceTable or {} - }, PlanetaryReference) - end - PlanetaryReference.__index = function(t, i) - if type(i) == 'number' then - local system = t.galaxyAtlas[i] - return mkPlanetarySystem(system) - end - return rawget(PlanetaryReference, i) - end - PlanetaryReference.__pairs = function(obj) - return function(t, k) - local nk, nv = next(t, k) - return nk, nv and mkPlanetarySystem(nv) - end, obj.galaxyAtlas, nil - end - PlanetaryReference.__tostring = function(obj) - local pslist = {} - for _, ps in pairs(obj or {}) do - local psi = ps:getPlanetarySystemId() - local pss = PlanetarySystem.__tostring(ps, ' ') - table.insert(pslist, stringf(' [%s]={%s\n }', psi, pss)) - end - return stringf('{\n%s\n}\n', table.concat(pslist, ',\n')) - end - PlanetaryReference.BodyParameters = mkBodyParameters - PlanetaryReference.MapPosition = mkMapPosition - PlanetaryReference.PlanetarySystem = mkPlanetarySystem - function PlanetaryReference.createBodyParameters(planetarySystemId, bodyId, surfaceArea, aPosition, - verticalAtPosition, altitudeAtPosition, gravityAtPosition) - assert(isSNumber(planetarySystemId), - 'Argument 1 (planetarySystemId) must be a number:' .. type(planetarySystemId)) - assert(isSNumber(bodyId), 'Argument 2 (bodyId) must be a number:' .. type(bodyId)) - assert(isSNumber(surfaceArea), 'Argument 3 (surfaceArea) must be a number:' .. type(surfaceArea)) - assert(isTable(aPosition), 'Argument 4 (aPosition) must be an array or vec3:' .. type(aPosition)) - assert(isTable(verticalAtPosition), - 'Argument 5 (verticalAtPosition) must be an array or vec3:' .. type(verticalAtPosition)) - assert(isSNumber(altitudeAtPosition), - 'Argument 6 (altitude) must be in meters:' .. type(altitudeAtPosition)) - assert(isSNumber(gravityAtPosition), - 'Argument 7 (gravityAtPosition) must be number:' .. type(gravityAtPosition)) - local radius = math.sqrt(surfaceArea / 4 / math.pi) - local distance = radius + altitudeAtPosition - local center = vec3(aPosition) + distance * vec3(verticalAtPosition) - local GM = gravityAtPosition * distance * distance - return mkBodyParameters(planetarySystemId, bodyId, radius, center, GM) - end - PlanetaryReference.isMapPosition = isMapPosition - function PlanetaryReference:getPlanetarySystem(overload) - -- if galaxyAtlas then - if i == nil then i = 0 end - if nv == nil then nv = 0 end - local planetarySystemId = overload - if isMapPosition(overload) then - planetarySystemId = overload.systemId - end - if type(planetarySystemId) == 'number' then - local system = self.galaxyAtlas[i] - if system then - if getmetatable(nv) ~= PlanetarySystem then - system = mkPlanetarySystem(system) - end - return system - end - end - -- end - -- return nil - end + newContent[#newContent + 1] = [[]] + -- Once we have all that, we should probably rotate the entire thing so that the ship is always at the bottom so you can see AP and PE move? - function PlanetarySystem:castIntersections(origin, direction, sizeCalculator, bodyIds) - local sizeCalculator = sizeCalculator or function(body) - return 1.05 * body.radius - end - local candidates = {} - if bodyIds then - for _, i in ipairs(bodyIds) do - candidates[i] = self[i] - end - else - bodyIds = {} - for k, body in pairs(self) do - table.insert(bodyIds, k) - candidates[k] = body - end - end - local function compare(b1, b2) - local v1 = candidates[b1].center - origin - local v2 = candidates[b2].center - origin - return v1:len() < v2:len() - end - table.sort(bodyIds, compare) - local dir = direction:normalize() - for i, id in ipairs(bodyIds) do - local body = candidates[id] - local c_oV3 = body.center - origin - local radius = sizeCalculator(body) - local dot = c_oV3:dot(dir) - local desc = dot ^ 2 - (c_oV3:len2() - radius ^ 2) - if desc >= 0 then - local root = math.sqrt(desc) - local farSide = dot + root - local nearSide = dot - root - if nearSide > 0 then - return body, farSide, nearSide - elseif farSide > 0 then - return body, farSide, nil - end - end - end - return nil, nil, nil - end + end +end - function PlanetarySystem:closestBody(coordinates) - assert(type(coordinates) == 'table', 'Invalid coordinates.') - local minDistance2, body - local coord = vec3(coordinates) - for _, params in pairs(self) do - local distance2 = (params.center - coord):len2() - if not body or distance2 < minDistance2 then - body = params - minDistance2 = distance2 - end - end - return body - end +function getDistanceDisplayString(distance) + local su = distance > 100000 + local result = "" + if su then + -- Convert to SU + result = round(distance / 1000 / 200, 1) .. " SU" + elseif distance < 1000 then + result = round(distance, 1) .. " M" + else + -- Convert to KM + result = round(distance / 1000, 1) .. " KM" + end - function PlanetarySystem:convertToBodyIdAndWorldCoordinates(overload) - local mapPosition = overload - if isString(overload) then - mapPosition = mkMapPosition(overload) - end - if mapPosition.bodyId == 0 then - return 0, vec3(mapPosition.latitude, mapPosition.longitude, mapPosition.altitude) - end - local params = self:getBodyParameters(mapPosition) - if params then - return mapPosition.bodyId, params:convertToWorldCoordinates(mapPosition) - end - end + return result +end - function PlanetarySystem:getBodyParameters(overload) - local bodyId = overload - if isMapPosition(overload) then - bodyId = overload.bodyId - end - assert(isSNumber(bodyId), 'Argument 1 (bodyId) must be a number:' .. type(bodyId)) - return self[bodyId] - end +function getDistanceDisplayString2(distance) + local su = distance > 100000 + local result = "" + if su then + -- Convert to SU + result = round(distance / 1000 / 200, 2) .. " SU" + elseif distance < 1000 then + result = round(distance, 2) .. " M" + else + -- Convert to KM + result = round(distance / 1000, 2) .. " KM" + end - function PlanetarySystem:getPlanetarySystemId() - local _, v = next(self) - return v and v.planetarySystemId - end + return result +end - function BodyParameters:convertToMapPosition(worldCoordinates) - assert(isTable(worldCoordinates), - 'Argument 1 (worldCoordinates) must be an array or vec3:' .. type(worldCoordinates)) - local worldVec = vec3(worldCoordinates) - if self.bodyId == 0 then - return setmetatable({ - latitude = worldVec.x, - longitude = worldVec.y, - altitude = worldVec.z, - bodyId = 0, - systemId = self.planetarySystemId - }, MapPosition) - end - local coords = worldVec - self.center - local distance = coords:len() - local altitude = distance - self.radius - local latitude = 0 - local longitude = 0 - if not float_eq(distance, 0) then - local phi = math.atan(coords.y, coords.x) - longitude = phi >= 0 and phi or (2 * math.pi + phi) - latitude = math.pi / 2 - math.acos(coords.z / distance) - end - return setmetatable({ - latitude = latitude, - longitude = longitude, - altitude = altitude, - bodyId = self.bodyId, - systemId = self.planetarySystemId - }, MapPosition) - end +function getSpeedDisplayString(speed) -- TODO: Allow options, for now just do kph + return mfloor(round(speed * 3.6, 0) + 0.5) .. " km/h" -- And generally it's not accurate enough to not twitch unless we round 0 +end - function BodyParameters:convertToWorldCoordinates(overload) - local mapPosition = isString(overload) and mkMapPosition(overload) or overload - if mapPosition.bodyId == 0 then -- support deep space map position - return vec3(mapPosition.latitude, mapPosition.longitude, mapPosition.altitude) - end - assert(isMapPosition(mapPosition), 'Argument 1 (mapPosition) is not an instance of "MapPosition".') - assert(mapPosition.systemId == self.planetarySystemId, - 'Argument 1 (mapPosition) has a different planetary system ID.') - assert(mapPosition.bodyId == self.bodyId, 'Argument 1 (mapPosition) has a different planetary body ID.') - local xproj = math.cos(mapPosition.latitude) - return self.center + (self.radius + mapPosition.altitude) * - vec3(xproj * math.cos(mapPosition.longitude), xproj * math.sin(mapPosition.longitude), - math.sin(mapPosition.latitude)) - end +function FormatTimeString(seconds) + local minutes = 0 + local hours = 0 + local days = 0 + if seconds < 60 then + seconds = mfloor(seconds) + elseif seconds < 3600 then + minutes = mfloor(seconds / 60) + seconds = mfloor(seconds % 60) + elseif seconds < 86400 then + hours = mfloor(seconds / 3600) + minutes = mfloor( (seconds % 3600) / 60) + else + days = mfloor ( seconds / 86400) + hours = mfloor ( (seconds % 86400) / 60) + end + if days > 0 then + return days .. "d " .. hours .."h " + elseif hours > 0 then + return hours .. "h " .. minutes .. "m " + elseif minutes > 0 then + return minutes .. "m " .. seconds .. "s" + elseif seconds > 0 then + return seconds .. "s" + else + return "0s" + end +end - function BodyParameters:getAltitude(worldCoordinates) - return (vec3(worldCoordinates) - self.center):len() - self.radius - end +function getMagnitudeInDirection(vector, direction) + -- return vec3(vector):project_on(vec3(direction)):len() + vector = vec3(vector) + direction = vec3(direction):normalize() + local result = vector * direction -- To preserve sign, just add them I guess + + return result.x + result.y + result.z +end - function BodyParameters:getDistance(worldCoordinates) - return (vec3(worldCoordinates) - self.center):len() - end +function UpdateAutopilotTarget() + -- So the indices are weird. I think we need to do a pairs + if AutopilotTargetIndex == 0 then + AutopilotTargetName = "None" + autopilotTargetPlanet = nil + return true + end - function BodyParameters:getGravity(worldCoordinates) - local radial = self.center - vec3(worldCoordinates) -- directed towards body - local len2 = radial:len2() - return (self.GM / len2) * radial / math.sqrt(len2) - end - -- end of module - return setmetatable(PlanetaryReference, { - __call = function(_, ...) - return mkPlanetaryReference(...) - end - }) - end - function Keplers() - local vec3 = require('cpml.vec3') - local PlanetRef = PlanetRef() - local function isString(s) - return type(s) == 'string' - end - local function isTable(t) - return type(t) == 'table' - end - local function float_eq(a, b) - if a == 0 then - return math.abs(b) < 1e-09 - end - if b == 0 then - return math.abs(a) < 1e-09 - end - return math.abs(a - b) < math.max(math.abs(a), math.abs(b)) * constants.epsilon - end - Kepler = {} - Kepler.__index = Kepler - - function Kepler:escapeAndOrbitalSpeed(altitude) - assert(self.body) - -- P = -GMm/r and KE = mv^2/2 (no lorentz factor used) - -- mv^2/2 = GMm/r - -- v^2 = 2GM/r - -- v = sqrt(2GM/r1) - local distance = altitude + self.body.radius - if not float_eq(distance, 0) then - local orbit = math.sqrt(self.body.GM / distance) - return math.sqrt(2) * orbit, orbit - end - return nil, nil - end + local atlasIndex = AtlasOrdered[AutopilotTargetIndex].index + local autopilotEntry = atlas[0][atlasIndex] + if autopilotEntry.center then -- Is a real atlas entry + AutopilotTargetName = autopilotEntry.name + autopilotTargetPlanet = galaxyReference[0][atlasIndex] + if CustomTarget ~= nil then + if atmosphere() == 0 then + if system.updateData(widgetMaxBrakeTimeText, widgetMaxBrakeTime) ~= 1 then + system.addDataToWidget(widgetMaxBrakeTimeText, widgetMaxBrakeTime) end + if system.updateData(widgetMaxBrakeDistanceText, widgetMaxBrakeDistance) ~= 1 then + system.addDataToWidget(widgetMaxBrakeDistanceText, widgetMaxBrakeDistance) end + if system.updateData(widgetCurBrakeTimeText, widgetCurBrakeTime) ~= 1 then + system.addDataToWidget(widgetCurBrakeTimeText, widgetCurBrakeTime) end + if system.updateData(widgetCurBrakeDistanceText, widgetCurBrakeDistance) ~= 1 then + system.addDataToWidget(widgetCurBrakeDistanceText, widgetCurBrakeDistance) end + if system.updateData(widgetTrajectoryAltitudeText, widgetTrajectoryAltitude) ~= 1 then + system.addDataToWidget(widgetTrajectoryAltitudeText, widgetTrajectoryAltitude) end + end + if system.updateData(widgetMaxMassText, widgetMaxMass) ~= 1 then + system.addDataToWidget(widgetMaxMassText, widgetMaxMass) end + if system.updateData(widgetTravelTimeText, widgetTravelTime) ~= 1 then + system.addDataToWidget(widgetTravelTimeText, widgetTravelTime) end + end + CustomTarget = nil + else + CustomTarget = autopilotEntry + for _, v in pairs(galaxyReference[0]) do + if v.name == CustomTarget.planetname then + autopilotTargetPlanet = v + AutopilotTargetName = CustomTarget.name + break + end + end + if system.updateData(widgetMaxMassText, widgetMaxMass) ~= 1 then + system.addDataToWidget(widgetMaxMassText, widgetMaxMass) end + if system.updateData(widgetTravelTimeText, widgetTravelTime) ~= 1 then + system.addDataToWidget(widgetTravelTimeText, widgetTravelTime) end + end + AutopilotTargetCoords = vec3(autopilotTargetPlanet.center) -- Aim center until we align + -- Determine the end speed + _, AutopilotEndSpeed = Kep(autopilotTargetPlanet):escapeAndOrbitalSpeed(AutopilotTargetOrbit) + -- AutopilotEndSpeed = 0 + -- AutopilotPlanetGravity = autopilotTargetPlanet:getGravity(autopilotTargetPlanet.center + vec3({1,0,0}) * AutopilotTargetOrbit):len() -- Any direction, at our orbit height + AutopilotPlanetGravity = 0 -- This is inaccurate unless we integrate and we're not doing that. + AutopilotAccelerating = false + AutopilotBraking = false + AutopilotCruising = false + Autopilot = false + AutopilotRealigned = false + AutopilotStatus = "Aligning" + return true +end - function Kepler:orbitalParameters(overload, velocity) - assert(self.body) - assert(isTable(overload) or isString(overload)) - assert(isTable(velocity)) - local pos = (isString(overload) or PlanetRef.isMapPosition(overload)) and - self.body:convertToWorldCoordinates(overload) or vec3(overload) - local v = vec3(velocity) - local r = pos - self.body.center - local v2 = v:len2() - local d = r:len() - local mu = self.body.GM - local e = ((v2 - mu / d) * r - r:dot(v) * v) / mu - local a = mu / (2 * mu / d - v2) - local ecc = e:len() - local dir = e:normalize() - local pd = a * (1 - ecc) - local ad = a * (1 + ecc) - local per = pd * dir + self.body.center - local apo = ecc <= 1 and -ad * dir + self.body.center or nil - local trm = math.sqrt(a * mu * (1 - ecc * ecc)) - local Period = apo and 2 * math.pi * math.sqrt(a ^ 3 / mu) - -- These are great and all, but, I need more. - local trueAnomaly = math.acos((e:dot(r)) / (ecc * d)) - if r:dot(v) < 0 then - trueAnomaly = -(trueAnomaly - 2 * math.pi) - end - -- Apparently... cos(EccentricAnomaly) = (cos(trueAnomaly) + eccentricity)/(1 + eccentricity * cos(trueAnomaly)) - local EccentricAnomaly = math.acos((math.cos(trueAnomaly) + ecc) / (1 + ecc * math.cos(trueAnomaly))) - -- Then.... apparently if this is below 0, we should add 2pi to it - -- I think also if it's below 0, we're past the apoapsis? - local timeTau = EccentricAnomaly - if timeTau < 0 then - timeTau = timeTau + 2 * math.pi - end - -- So... time since periapsis... - -- Is apparently easy if you get mean anomly. t = M/n where n is mean motion, = 2*pi/Period - local MeanAnomaly = timeTau - ecc * math.sin(timeTau) - local TimeSincePeriapsis = 0 - local TimeToPeriapsis = 0 - local TimeToApoapsis = 0 - if Period ~= nil then - TimeSincePeriapsis = MeanAnomaly / (2 * math.pi / Period) - -- Mean anom is 0 at periapsis, positive before it... and positive after it. - -- I guess this is why I needed to use timeTau and not EccentricAnomaly here - - TimeToPeriapsis = Period - TimeSincePeriapsis - TimeToApoapsis = TimeToPeriapsis + Period / 2 - if trueAnomaly - math.pi > 0 then -- TBH I think something's wrong in my formulas because I needed this. - TimeToPeriapsis = TimeSincePeriapsis - TimeToApoapsis = TimeToPeriapsis + Period / 2 - end - if TimeToApoapsis > Period then - TimeToApoapsis = TimeToApoapsis - Period - end - end - return { - periapsis = { - position = per, - speed = trm / pd, - circularOrbitSpeed = math.sqrt(mu / pd), - altitude = pd - self.body.radius - }, - apoapsis = apo and { - position = apo, - speed = trm / ad, - circularOrbitSpeed = math.sqrt(mu / ad), - altitude = ad - self.body.radius - }, - currentVelocity = v, - currentPosition = pos, - eccentricity = ecc, - period = Period, - eccentricAnomaly = EccentricAnomaly, - meanAnomaly = MeanAnomaly, - timeToPeriapsis = TimeToPeriapsis, - timeToApoapsis = TimeToApoapsis - } - end - local function new(bodyParameters) - local params = PlanetRef.BodyParameters(bodyParameters.planetarySystemId, bodyParameters.bodyId, - bodyParameters.radius, bodyParameters.center, bodyParameters.GM) - return setmetatable({ - body = params - }, Kepler) - end - return setmetatable(Kepler, { - __call = function(_, ...) - return new(...) - end - }) - end - function Kinematics() +function IncrementAutopilotTargetIndex() + AutopilotTargetIndex = AutopilotTargetIndex + 1 + -- if AutopilotTargetIndex > tablelength(atlas[0]) then + if AutopilotTargetIndex > #AtlasOrdered then + AutopilotTargetIndex = 0 + end + UpdateAutopilotTarget() +end - local Kinematic = {} -- just a namespace - local C = 30000000 / 3600 - local C2 = C * C - local ITERATIONS = 100 -- iterations over engine "warm-up" period - local function lorentz(v) - return 1 / math.sqrt(1 - v * v / C2) - end +function DecrementAutopilotTargetIndex() + AutopilotTargetIndex = AutopilotTargetIndex - 1 + + if AutopilotTargetIndex < 0 then + -- AutopilotTargetIndex = tablelength(atlas[0]) + AutopilotTargetIndex = #AtlasOrdered + end + UpdateAutopilotTarget() +end - function Kinematic.computeAccelerationTime(initial, acceleration, final) - -- The low speed limit of following is: t=(vf-vi)/a (from: vf=vi+at) - local k1 = C * math.asin(initial / C) - return (C * math.asin(final / C) - k1) / acceleration - end +function GetAutopilotMaxMass() + local apmaxmass = LastMaxBrakeInAtmo / + (autopilotTargetPlanet:getGravity( + autopilotTargetPlanet.center + (vec3(0, 0, 1) * autopilotTargetPlanet.radius)) + :len()) + return apmaxmass +end - function Kinematic.computeDistanceAndTime(initial, final, restMass, thrust, t50, brakeThrust) +function GetAutopilotTravelTime() + if not Autopilot then + if CustomTarget == nil or CustomTarget.planetname ~= planet.name then + AutopilotDistance = (autopilotTargetPlanet.center - vec3(core.getConstructWorldPos())):len() -- This updates elsewhere if we're already piloting + else + AutopilotDistance = (CustomTarget.position - vec3(core.getConstructWorldPos())):len() + end + end + local velocity = core.getWorldVelocity() + local speed = vec3(velocity):len() + local accelDistance, accelTime = + Kinematic.computeDistanceAndTime(vec3(velocity):len(), MaxGameVelocity, -- From currently velocity to max + constructMass(), Nav:maxForceForward(), warmup, -- T50? Assume none, negligible for this + 0) -- Brake thrust, none for this + -- accelDistance now has the amount of distance for which we will be accelerating + -- Then we need the distance we'd brake from full speed + -- Note that for some nearby moons etc, it may never reach full speed though. + local brakeDistance, brakeTime + if not TurnBurn then + brakeDistance, brakeTime = GetAutopilotBrakeDistanceAndTime(MaxGameVelocity) + else + brakeDistance, brakeTime = GetAutopilotTBBrakeDistanceAndTime(MaxGameVelocity) + end + local _, curBrakeTime + if not TurnBurn then + _, curBrakeTime = GetAutopilotBrakeDistanceAndTime(speed) + else + _, curBrakeTime = GetAutopilotTBBrakeDistanceAndTime(speed) + end + local cruiseDistance = 0 + local cruiseTime = 0 + -- So, time is in seconds + -- If cruising or braking, use real cruise/brake values + if AutopilotCruising or (not Autopilot and speed > 5) then -- If already cruising, use current speed + cruiseTime = Kinematic.computeTravelTime(speed, 0, AutopilotDistance) + elseif brakeDistance + accelDistance < AutopilotDistance then + -- Add any remaining distance + cruiseDistance = AutopilotDistance - (brakeDistance + accelDistance) + cruiseTime = Kinematic.computeTravelTime(8333.0556, 0, cruiseDistance) + else + local accelRatio = (AutopilotDistance - brakeDistance) / accelDistance + accelDistance = AutopilotDistance - brakeDistance -- Accel until we brake + + accelTime = accelTime * accelRatio + end + if CustomTarget ~= nil and CustomTarget.planetname == planet.name and not Autopilot then + return cruiseTime + elseif AutopilotBraking then + return curBrakeTime + elseif AutopilotCruising then + return cruiseTime + curBrakeTime + else -- If not cruising or braking, assume we'll get to max speed + return accelTime + brakeTime + cruiseTime + end +end - t50 = t50 or 0 - brakeThrust = brakeThrust or 0 -- usually zero when accelerating - local speedUp = initial <= final - local a0 = thrust * (speedUp and 1 or -1) / restMass - local b0 = -brakeThrust / restMass - local totA = a0 + b0 - if speedUp and totA <= 0 or not speedUp and totA >= 0 then - return -1, -1 -- no solution - end - local distanceToMax, timeToMax = 0, 0 - - if a0 ~= 0 and t50 > 0 then - - local k1 = math.asin(initial / C) - local c1 = math.pi * (a0 / 2 + b0) - local c2 = a0 * t50 - local c3 = C * math.pi - local v = function(t) - local w = (c1 * t - c2 * math.sin(math.pi * t / 2 / t50) + c3 * k1) / c3 - local tan = math.tan(w) - return C * tan / math.sqrt(tan * tan + 1) - end - local speedchk = speedUp and function(s) - return s >= final - end or function(s) - return s <= final - end - timeToMax = 2 * t50 - if speedchk(v(timeToMax)) then - local lasttime = 0 - while math.abs(timeToMax - lasttime) > 0.5 do - local t = (timeToMax + lasttime) / 2 - if speedchk(v(t)) then - timeToMax = t - else - lasttime = t - end - end - end - -- There is no closed form solution for distance in this case. - -- Numerically integrate for time t=0 to t=2*T50 (or less) - local lastv = initial - local tinc = timeToMax / ITERATIONS - for step = 1, ITERATIONS do - local speed = v(step * tinc) - distanceToMax = distanceToMax + (speed + lastv) * tinc / 2 - lastv = speed - end - if timeToMax < 2 * t50 then - return distanceToMax, timeToMax - end - initial = lastv - end +function GetAutopilotBrakeDistanceAndTime(speed) + -- If we're in atmo, just return some 0's or LastMaxBrake, whatever's bigger + -- So we don't do unnecessary API calls when atmo brakes don't tell us what we want + if not inAtmo then + RefreshLastMaxBrake() + return Kinematic.computeDistanceAndTime(speed, AutopilotEndSpeed, constructMass(), 0, 0, + LastMaxBrake - (AutopilotPlanetGravity * constructMass())) + else + if LastMaxBrakeInAtmo and LastMaxBrakeInAtmo > 0 then + return Kinematic.computeDistanceAndTime(speed, AutopilotEndSpeed, constructMass(), 0, 0, + LastMaxBrakeInAtmo - (AutopilotPlanetGravity * constructMass())) + else + return 0, 0 + end + end +end - local k1 = C * math.asin(initial / C) - local time = (C * math.asin(final / C) - k1) / totA - local k2 = C2 * math.cos(k1 / C) / totA - local distance = k2 - C2 * math.cos((totA * time + k1) / C) / totA - return distance + distanceToMax, time + timeToMax - end +function GetAutopilotTBBrakeDistanceAndTime(speed) -- Uses thrust and a configured T50 + RefreshLastMaxBrake() + return Kinematic.computeDistanceAndTime(speed, AutopilotEndSpeed, constructMass(), Nav:maxForceForward(), + warmup, LastMaxBrake - (AutopilotPlanetGravity * constructMass())) +end - function Kinematic.computeTravelTime(initial, acceleration, distance) - -- The low speed limit of following is: t=(sqrt(2ad+v^2)-v)/a - -- (from: d=vt+at^2/2) - if distance == 0 then - return 0 - end - if acceleration > 0 then - local k1 = C * math.asin(initial / C) - local k2 = C2 * math.cos(k1 / C) / acceleration - return (C * math.acos(acceleration * (k2 - distance) / C2) - k1) / acceleration - end - assert(initial > 0, 'Acceleration and initial speed are both zero.') - return distance / initial - end +function hoverDetectGround() + local vgroundDistance = -1 + local hgroundDistance = -1 + if vBooster then + vgroundDistance = vBooster.distance() + end + if hover then + hgroundDistance = hover.distance() + end + if vgroundDistance ~= -1 and hgroundDistance ~= -1 then + if vgroundDistance < hgroundDistance then + return vgroundDistance + else + return hgroundDistance + end + elseif vgroundDistance ~= -1 then + return vgroundDistance + elseif hgroundDistance ~= -1 then + return hgroundDistance + else + return -1 + end +end - function Kinematic.lorentz(v) - return lorentz(v) - end - return Kinematic +function AboveGroundLevel() + local groundDistance = -1 + local hgroundDet = hovGndDet + if telemeter_1 then + groundDistance = telemeter_1.getDistance() + end + if hgroundDet ~= -1 and groundDistance ~= -1 then + if hgroundDet < groundDistance then + return hgroundDet + else + return groundDistance end + elseif hgroundDet ~= -1 then + return hgroundDet + else + return groundDistance + end +end + +function tablelength(T) + local count = 0 + for _ in pairs(T) do + count = count + 1 + end + return count +end - PlanetaryReference = PlanetRef() - galaxyReference = PlanetaryReference(Atlas()) - Kinematic = Kinematics() - Kep = Keplers() +function BeginProfile(profileName) + ProfileTimeStart = system.getTime() +end - function getDistanceDisplayString(distance) - local su = distance > 100000 - local result = "" - if su then - -- Convert to SU - result = round(distance / 1000 / 200, 1) .. " SU" - elseif distance < 1000 then - result = round(distance, 1) .. " M" - else - -- Convert to KM - result = round(distance / 1000, 1) .. " KM" - end +function EndProfile(profileName) + local profileTime = system.getTime() - ProfileTimeStart + ProfileTimeSum = ProfileTimeSum + profileTime + ProfileCount = ProfileCount + 1 + if profileTime > ProfileTimeMax then + ProfileTimeMax = profileTime + end - return result - end + if profileTime < ProfileTimeMin then + ProfileTimeMin = profileTime + end +end - function getDistanceDisplayString2(distance) - local su = distance > 100000 - local result = "" - if su then - -- Convert to SU - result = round(distance / 1000 / 200, 2) .. " SU" - elseif distance < 1000 then - result = round(distance, 2) .. " M" - else - -- Convert to KM - result = round(distance / 1000, 2) .. " KM" - end +function ResetProfiles() + ProfileTimeMin = 9999 + ProfileTimeMax = 0 + ProfileCount = 0 + ProfileTimeSum = 0 +end - return result - end +function ReportProfiling() + local totalTime = ProfileTimeSum + local averageTime = ProfileTimeSum / ProfileCount + local min = ProfileTimeMin + local max = ProfileTimeMax + local samples = ProfileCount + sprint(stringf("SUM: %.4f AVG: %.4f MIN: %.4f MAX: %.4f CNT: %d", totalTime, averageTime, min, + max, samples)) +end - function getSpeedDisplayString(speed) -- TODO: Allow options, for now just do kph - return mfloor(round(speed * 3.6, 0) + 0.5) .. " km/h" -- And generally it's not accurate enough to not twitch unless we round 0 +function updateWeapons() + if weapon then + if WeaponPanelID==nil and (radarPanelID ~= nil or GearExtended) then + _autoconf.displayCategoryPanel(weapon, weapon_size, L_TEXT("ui_lua_widget_weapon", "Weapons"), "weapon", true) + WeaponPanelID = _autoconf.panels[_autoconf.panels_size] + elseif WeaponPanelID ~= nil and radarPanelID == nil and not GearExtended then + system.destroyWidgetPanel(WeaponPanelID) + WeaponPanelID = nil end + end +end - function FormatTimeString(seconds) - local minutes = 0 - local hours = 0 - local days = 0 - if seconds < 60 then - seconds = mfloor(seconds) - elseif seconds < 3600 then - minutes = mfloor(seconds / 60) - seconds = mfloor(seconds % 60) - elseif seconds < 86400 then - hours = mfloor(seconds / 3600) - minutes = mfloor( (seconds % 3600) / 60) - else - days = mfloor ( seconds / 86400) - hours = mfloor ( (seconds % 86400) / 60) +function updateRadar() + if (radar_1) then + local radarContacts = radar_1.getEntries() + local radarData = radar_1.getData() + local radarX = ConvertResolutionX(1770) + local radarY = ConvertResolutionY(330) + if #radarContacts > 0 then + local target = radarData:find('identifiedConstructs":%[%]') + if target == nil and perisPanelID == nil then + peris = 1 + ToggleRadarPanel() + end + if target ~= nil and perisPanelID ~= nil then + ToggleRadarPanel() + end + if radarPanelID == nil then + ToggleRadarPanel() + end + radarMessage = stringf( + [[Radar: %i contacts]], + radarX, radarY, #radarContacts) + local friendlies = {} + for k, v in pairs(radarContacts) do + if radar_1.hasMatchingTransponder(v) == 1 then + friendlies[#friendlies + 1] = v + end + end + if #friendlies > 0 then + local y = ConvertResolutionY(15) + local x = ConvertResolutionX(1370) + radarMessage = stringf( + [[%sFriendlies In Range]], + radarMessage, x, y) + for k, v in pairs(friendlies) do + y = y + 20 + radarMessage = stringf([[%s%s]], + radarMessage, x, y, radar_1.getConstructName(v)) + end end - if days > 0 then - return days .. "d " .. hours .."h " - elseif hours > 0 then - return hours .. "h " .. minutes .. "m " - elseif minutes > 0 then - return minutes .. "m " .. seconds .. "s" - elseif seconds > 0 then - return seconds .. "s" + else + local data + data = radarData:find('worksInEnvironment":false') + if data then + radarMessage = stringf([[ + Radar: Jammed]], + radarX, radarY) else - return "0s" + radarMessage = stringf([[ + Radar: No Contacts]], + radarX, radarY) + end + if radarPanelID ~= nil then + peris = 0 + ToggleRadarPanel() end end + end +end - function getMagnitudeInDirection(vector, direction) - -- return vec3(vector):project_on(vec3(direction)):len() - vector = vec3(vector) - direction = vec3(direction):normalize() - local result = vector * direction -- To preserve sign, just add them I guess - - return result.x + result.y + result.z +function DisplayMessage(newContent, displayText) + if displayText ~= "empty" then + newContent[#newContent + 1] = [[]] + for str in string.gmatch(displayText, "([^\n]+)") do + newContent[#newContent + 1] = stringf([[%s]], str) end + newContent[#newContent + 1] = [[]] + end + if msgTimer ~= 0 then + unit.setTimer("msgTick", msgTimer) + msgTimer = 0 + end +end - function UpdateAutopilotTarget() - -- So the indices are weird. I think we need to do a pairs - if AutopilotTargetIndex == 0 then - AutopilotTargetName = "None" - autopilotTargetPlanet = nil - return true - end +function updateDistance() + local curTime = system.getTime() + local velocity = vec3(core.getWorldVelocity()) + local spd = vec3(velocity):len() + local elapsedTime = curTime - lastTravelTime + if (spd > 1.38889) then + spd = spd / 1000 + local newDistance = spd * (curTime - lastTravelTime) + TotalDistanceTravelled = TotalDistanceTravelled + newDistance + totalDistanceTrip = totalDistanceTrip + newDistance + end + flightTime = flightTime + elapsedTime + TotalFlightTime = TotalFlightTime + elapsedTime + lastTravelTime = curTime +end - local atlasIndex = AtlasOrdered[AutopilotTargetIndex].index - local autopilotEntry = atlas[0][atlasIndex] - if autopilotEntry.center then -- Is a real atlas entry - AutopilotTargetName = autopilotEntry.name - autopilotTargetPlanet = galaxyReference[0][atlasIndex] - if CustomTarget ~= nil then - if atmosphere() == 0 then - if system.updateData(widgetMaxBrakeTimeText, widgetMaxBrakeTime) ~= 1 then - system.addDataToWidget(widgetMaxBrakeTimeText, widgetMaxBrakeTime) end - if system.updateData(widgetMaxBrakeDistanceText, widgetMaxBrakeDistance) ~= 1 then - system.addDataToWidget(widgetMaxBrakeDistanceText, widgetMaxBrakeDistance) end - if system.updateData(widgetCurBrakeTimeText, widgetCurBrakeTime) ~= 1 then - system.addDataToWidget(widgetCurBrakeTimeText, widgetCurBrakeTime) end - if system.updateData(widgetCurBrakeDistanceText, widgetCurBrakeDistance) ~= 1 then - system.addDataToWidget(widgetCurBrakeDistanceText, widgetCurBrakeDistance) end - if system.updateData(widgetTrajectoryAltitudeText, widgetTrajectoryAltitude) ~= 1 then - system.addDataToWidget(widgetTrajectoryAltitudeText, widgetTrajectoryAltitude) end - end - if system.updateData(widgetMaxMassText, widgetMaxMass) ~= 1 then - system.addDataToWidget(widgetMaxMassText, widgetMaxMass) end - if system.updateData(widgetTravelTimeText, widgetTravelTime) ~= 1 then - system.addDataToWidget(widgetTravelTimeText, widgetTravelTime) end - end - CustomTarget = nil - else - CustomTarget = autopilotEntry - for _, v in pairs(galaxyReference[0]) do - if v.name == CustomTarget.planetname then - autopilotTargetPlanet = v - AutopilotTargetName = CustomTarget.name - break - end - end - if system.updateData(widgetMaxMassText, widgetMaxMass) ~= 1 then - system.addDataToWidget(widgetMaxMassText, widgetMaxMass) end - if system.updateData(widgetTravelTimeText, widgetTravelTime) ~= 1 then - system.addDataToWidget(widgetTravelTimeText, widgetTravelTime) end - end - AutopilotTargetCoords = vec3(autopilotTargetPlanet.center) -- Aim center until we align - -- Determine the end speed - _, AutopilotEndSpeed = Kep(autopilotTargetPlanet):escapeAndOrbitalSpeed(AutopilotTargetOrbit) - -- AutopilotEndSpeed = 0 - -- AutopilotPlanetGravity = autopilotTargetPlanet:getGravity(autopilotTargetPlanet.center + vec3({1,0,0}) * AutopilotTargetOrbit):len() -- Any direction, at our orbit height - AutopilotPlanetGravity = 0 -- This is inaccurate unless we integrate and we're not doing that. - AutopilotAccelerating = false - AutopilotBraking = false - AutopilotCruising = false - Autopilot = false - AutopilotRealigned = false - AutopilotStatus = "Aligning" - return true - end +-- Planet Info - https://gitlab.com/JayleBreak/dualuniverse/-/tree/master/DUflightfiles/autoconf/custom with minor modifications +function Atlas() + return { + [0] = { + [1] = { + GM = 6930729684, + bodyId = 1, + center = { + x = 17465536.000, + y = 22665536.000, + z = -34464.000 + }, + name = 'Madis', + planetarySystemId = 0, + radius = 44300, + atmos = true, + gravity = 0.36 + }, + [2] = { + GM = 157470826617, + bodyId = 2, + center = { + x = -8.000, + y = -8.000, + z = -126303.000 + }, + name = 'Alioth', + planetarySystemId = 0, + radius = 126068, + atmos = true, + gravity = 1.01 + }, + [3] = { + GM = 11776905000, + bodyId = 3, + center = { + x = 29165536.000, + y = 10865536.000, + z = 65536.000 + }, + name = 'Thades', + planetarySystemId = 0, + radius = 49000, + atmos = true, + gravity = 0.50 + }, + [4] = { + GM = 14893847582, + bodyId = 4, + center = { + x = -13234464.000, + y = 55765536.000, + z = 465536.000 + }, + name = 'Talemai', + planetarySystemId = 0, + radius = 57450, + atmos = true, + gravity = 0.46 + }, + [5] = { + GM = 16951680000, + bodyId = 5, + center = { + x = -43534464.000, + y = 22565536.000, + z = -48934464.000 + }, + name = 'Feli', + planetarySystemId = 0, + radius = 60000, + atmos = true, + gravity = 0.48 + }, + [6] = { + GM = 10502547741, + bodyId = 6, + center = { + x = 52765536.000, + y = 27165538.000, + z = 52065535.000 + }, + name = 'Sicari', + planetarySystemId = 0, + radius = 51100, + atmos = true, + gravity = 0.41 + }, + [7] = { + GM = 13033380591, + bodyId = 7, + center = { + x = 58665538.000, + y = 29665535.000, + z = 58165535.000 + }, + name = 'Sinnen', + planetarySystemId = 0, + radius = 54950, + atmos = true, + gravity = 0.44 + }, + [8] = { + GM = 18477723600, + bodyId = 8, + center = { + x = 80865538.000, + y = 54665536.000, + z = -934463.940 + }, + name = 'Teoma', + planetarySystemId = 0, + radius = 62000, + atmos = true, + gravity = 0.49 + }, + [9] = { + GM = 18606274330, + bodyId = 9, + center = { + x = -94134462.000, + y = 12765534.000, + z = -3634464.000 + }, + name = 'Jago', + planetarySystemId = 0, + radius = 61590, + atmos = true, + gravity = 0.50 + }, + [10] = { + GM = 78480000, + bodyId = 10, + center = { + x = 17448118.224, + y = 22966846.286, + z = 143078.820 + }, + name = 'Madis Moon 1', + planetarySystemId = 0, + radius = 10000, + atmos = false, + gravity = 0.08 + }, + [11] = { + GM = 237402000, + bodyId = 11, + center = { + x = 17194626.000, + y = 22243633.880, + z = -214962.810 + }, + name = 'Madis Moon 2', + planetarySystemId = 0, + radius = 11000, + atmos = false, + gravity = 0.10 + }, + [12] = { + GM = 265046609, + bodyId = 12, + center = { + x = 17520614.000, + y = 22184730.000, + z = -309989.990 + }, + name = 'Madis Moon 3', + planetarySystemId = 0, + radius = 15005, + atmos = false, + gravity = 0.12 + }, + [21] = { + GM = 2118960000, + bodyId = 21, + center = { + x = 457933.000, + y = -1509011.000, + z = 115524.000 + }, + name = 'Alioth Moon 1', + planetarySystemId = 0, + radius = 30000, + atmos = false, + gravity = 0.24 + }, + [22] = { + GM = 2165833514, + bodyId = 22, + center = { + x = -1692694.000, + y = 729681.000, + z = -411464.000 + }, + name = 'Alioth Moon 4', + planetarySystemId = 0, + radius = 30330, + atmos = false, + gravity = 0.24 + }, + [26] = { + GM = 68234043600, + bodyId = 26, + center = { + x = -1404835.000, + y = 562655.000, + z = -285074.000 + }, + name = 'Sanctuary', + planetarySystemId = 0, + radius = 83400, + atmos = true, + gravity = 1.00 + }, + [30] = { + GM = 211564034, + bodyId = 30, + center = { + x = 29214402.000, + y = 10907080.695, + z = 433858.200 + }, + name = 'Thades Moon 1', + planetarySystemId = 0, + radius = 14002, + atmos = false, + gravity = 0.11 + }, + [31] = { + GM = 264870000, + bodyId = 31, + center = { + x = 29404193.000, + y = 10432768.000, + z = 19554.131 + }, + name = 'Thades Moon 2', + planetarySystemId = 0, + radius = 15000, + atmos = false, + gravity = 0.12 + }, + [40] = { + GM = 141264000, + bodyId = 40, + center = { + x = -13503090.000, + y = 55594325.000, + z = 769838.640 + }, + name = 'Talemai Moon 2', + planetarySystemId = 0, + radius = 12000, + atmos = false, + gravity = 0.10 + }, + [41] = { + GM = 106830900, + bodyId = 41, + center = { + x = -12800515.000, + y = 55700259.000, + z = 325207.840 + }, + name = 'Talemai Moon 3', + planetarySystemId = 0, + radius = 11000, + atmos = false, + gravity = 0.09 + }, + [42] = { + GM = 264870000, + bodyId = 42, + center = { + x = -13058408.000, + y = 55781856.000, + z = 740177.760 + }, + name = 'Talemai Moon 1', + planetarySystemId = 0, + radius = 15000, + atmos = false, + gravity = 0.12 + }, + [50] = { + GM = 499917600, + bodyId = 50, + center = { + x = -43902841.780, + y = 22261034.700, + z = -48862386.000 + }, + name = 'Feli Moon 1', + planetarySystemId = 0, + radius = 14000, + atmos = false, + gravity = 0.11 + }, + [70] = { + GM = 396912600, + bodyId = 70, + center = { + x = 58969616.000, + y = 29797945.000, + z = 57969449.000 + }, + name = 'Sinnen Moon 1', + planetarySystemId = 0, + radius = 17000, + atmos = false, + gravity = 0.14 + }, + [100] = { + GM = 13975172474, + bodyId = 100, + center = { + x = 98865536.000, + y = -13534464.000, + z = -934461.990 + }, + name = 'Lacobus', + planetarySystemId = 0, + radius = 55650, + atmos = true, + gravity = 0.46 + }, + [101] = { + GM = 264870000, + bodyId = 101, + center = { + x = 98905288.170, + y = -13950921.100, + z = -647589.530 + }, + name = 'Lacobus Moon 3', + planetarySystemId = 0, + radius = 15000, + atmos = false, + gravity = 0.12 + }, + [102] = { + GM = 444981600, + bodyId = 102, + center = { + x = 99180968.000, + y = -13783862.000, + z = -926156.400 + }, + name = 'Lacobus Moon 1', + planetarySystemId = 0, + radius = 18000, + atmos = false, + gravity = 0.14 + }, + [103] = { + GM = 211503600, + bodyId = 103, + center = { + x = 99250052.000, + y = -13629215.000, + z = -1059341.400 + }, + name = 'Lacobus Moon 2', + planetarySystemId = 0, + radius = 14000, + atmos = false, + gravity = 0.11 + }, + [110] = { + GM = 9204742375, + bodyId = 110, + center = { + x = 14165536.000, + y = -85634465.000, + z = -934464.300 + }, + name = 'Symeon', + planetarySystemId = 0, + radius = 49050, + atmos = true, + gravity = 0.39 + }, + [120] = { + GM = 7135606629, + bodyId = 120, + center = { + x = 2865536.700, + y = -99034464.000, + z = -934462.020 + }, + name = 'Ion', + planetarySystemId = 0, + radius = 44950, + atmos = true, + gravity = 0.36 + }, + [121] = { + GM = 106830900, + bodyId = 121, + center = { + x = 2472916.800, + y = -99133747.000, + z = -1133582.800 + }, + name = 'Ion Moon 1', + planetarySystemId = 0, + radius = 11000, + atmos = false, + gravity = 0.09 + }, + [122] = { + GM = 176580000, + bodyId = 122, + center = { + x = 2995424.500, + y = -99275010.000, + z = -1378480.700 + }, + name = 'Ion Moon 2', + planetarySystemId = 0, + radius = 15000, + atmos = false, + gravity = 0.12 + }, + } + } +end - function IncrementAutopilotTargetIndex() - AutopilotTargetIndex = AutopilotTargetIndex + 1 - -- if AutopilotTargetIndex > tablelength(atlas[0]) then - if AutopilotTargetIndex > #AtlasOrdered then - AutopilotTargetIndex = 0 - end - UpdateAutopilotTarget() +function SetupAtlas() + atlas = Atlas() + for k, v in pairs(atlas[0]) do + if minAtlasX == nil or v.center.x < minAtlasX then + minAtlasX = v.center.x end - - function DecrementAutopilotTargetIndex() - AutopilotTargetIndex = AutopilotTargetIndex - 1 - - if AutopilotTargetIndex < 0 then - -- AutopilotTargetIndex = tablelength(atlas[0]) - AutopilotTargetIndex = #AtlasOrdered - end - UpdateAutopilotTarget() + if maxAtlasX == nil or v.center.x > maxAtlasX then + maxAtlasX = v.center.x end - - function GetAutopilotMaxMass() - local apmaxmass = LastMaxBrakeInAtmo / - (autopilotTargetPlanet:getGravity( - autopilotTargetPlanet.center + (vec3(0, 0, 1) * autopilotTargetPlanet.radius)) - :len()) - return apmaxmass + if minAtlasY == nil or v.center.y < minAtlasY then + minAtlasY = v.center.y + end + if maxAtlasY == nil or v.center.y > maxAtlasY then + maxAtlasY = v.center.y + end + end + GalaxyMapHTML = "" -- No starting SVG tag so we can add it where we want it + -- Figure out our scale here... + local xRatio = 1.1 * (maxAtlasX - minAtlasX) / 1920 -- Add 10% for padding + local yRatio = 1.4 * (maxAtlasY - minAtlasY) / 1080 -- Extra so we can get ion back in + for k, v in pairs(atlas[0]) do + -- Draw a circle at the scaled coordinates + local x = 960 + (v.center.x / xRatio) + local y = 540 + (v.center.y / yRatio) + GalaxyMapHTML = + GalaxyMapHTML .. '' + if not string.match(v.name, "Moon") and not string.match(v.name, "Sanctuary") then + GalaxyMapHTML = GalaxyMapHTML .. "" .. + v.name .. "" end + end + -- Draw a 'You Are Here' - face edition + local pos = vec3(core.getConstructWorldPos()) + local x = 960 + pos.x / xRatio + local y = 540 + pos.y / yRatio + GalaxyMapHTML = GalaxyMapHTML .. '' + GalaxyMapHTML = GalaxyMapHTML .. "You Are Here" + GalaxyMapHTML = GalaxyMapHTML .. [[]] + MapXRatio = xRatio + MapYRatio = yRatio + if screen_2 then + screen_2.setHTML('' .. GalaxyMapHTML) -- This is permanent and doesn't change + -- Draw a 'You Are Here' - screen edition + local pos = vec3(core.getConstructWorldPos()) + local x = 960 + pos.x / xRatio + local y = 540 + pos.y / yRatio + GalaxyMapHTML = '' + GalaxyMapHTML = GalaxyMapHTML .. "You Are Here" + YouAreHere = screen_2.addContent((x - 80) / 19.20, (y - 80) / 10.80, GalaxyMapHTML) + end +end - function GetAutopilotTravelTime() - if not Autopilot then - if CustomTarget == nil or CustomTarget.planetname ~= planet.name then - AutopilotDistance = (autopilotTargetPlanet.center - vec3(core.getConstructWorldPos())):len() -- This updates elsewhere if we're already piloting - else - AutopilotDistance = (CustomTarget.position - vec3(core.getConstructWorldPos())):len() - end - end - local velocity = core.getWorldVelocity() - local speed = vec3(velocity):len() - local accelDistance, accelTime = - Kinematic.computeDistanceAndTime(vec3(velocity):len(), MaxGameVelocity, -- From currently velocity to max - constructMass(), Nav:maxForceForward(), warmup, -- T50? Assume none, negligible for this - 0) -- Brake thrust, none for this - -- accelDistance now has the amount of distance for which we will be accelerating - -- Then we need the distance we'd brake from full speed - -- Note that for some nearby moons etc, it may never reach full speed though. - local brakeDistance, brakeTime - if not TurnBurn then - brakeDistance, brakeTime = GetAutopilotBrakeDistanceAndTime(MaxGameVelocity) +function PlanetRef() + --[[ START OF LOCAL IMPLEMENTATION DETAILS ]]-- + -- Type checks + local function isNumber(n) + return type(n) == 'number' + end + local function isSNumber(n) + return type(tonumber(n)) == 'number' + end + local function isTable(t) + return type(t) == 'table' + end + local function isString(s) + return type(s) == 'string' + end + local function isVector(v) + return isTable(v) and isNumber(v.x and v.y and v.z) + end + local function isMapPosition(m) + return isTable(m) and isNumber(m.latitude and m.longitude and m.altitude and m.bodyId and m.systemId) + end + -- Constants + local deg2rad = math.pi / 180 + local rad2deg = 180 / math.pi + local epsilon = 1e-10 + local num = ' *([+-]?%d+%.?%d*e?[+-]?%d*)' + local posPattern = '::pos{' .. num .. ',' .. num .. ',' .. num .. ',' .. num .. ',' .. num .. '}' + -- Utilities + local utils = require('cpml.utils') + local vec3 = require('cpml.vec3') + local clamp = utils.clamp + local function float_eq(a, b) + if a == 0 then + return math.abs(b) < 1e-09 + end + if b == 0 then + return math.abs(a) < 1e-09 + end + return math.abs(a - b) < math.max(math.abs(a), math.abs(b)) * epsilon + end + local function formatNumber(n) + local result = string.gsub(string.reverse(stringf('%.4f', n)), '^0*%.?', '') + return result == '' and '0' or string.reverse(result) + end + local function formatValue(obj) + if isVector(obj) then + return stringf('{x=%.3f,y=%.3f,z=%.3f}', obj.x, obj.y, obj.z) + end + if isTable(obj) and not getmetatable(obj) then + local list = {} + local nxt = next(obj) + if type(nxt) == 'nil' or nxt == 1 then -- assume this is an array + list = obj else - brakeDistance, brakeTime = GetAutopilotTBBrakeDistanceAndTime(MaxGameVelocity) + for k, v in pairs(obj) do + local value = formatValue(v) + if type(k) == 'number' then + table.insert(list, stringf('[%s]=%s', k, value)) + else + table.insert(list, stringf('%s=%s', k, value)) + end + end end - local _, curBrakeTime - if not TurnBurn then - _, curBrakeTime = GetAutopilotBrakeDistanceAndTime(speed) + return stringf('{%s}', table.concat(list, ',')) + end + if isString(obj) then + return stringf("'%s'", obj:gsub("'", [[\']])) + end + return tostring(obj) + end + -- CLASSES + -- BodyParameters: Attributes of planetary bodies (planets and moons) + local BodyParameters = {} + BodyParameters.__index = BodyParameters + BodyParameters.__tostring = function(obj, indent) + local keys = {} + for k in pairs(obj) do + table.insert(keys, k) + end + table.sort(keys) + local list = {} + for _, k in ipairs(keys) do + local value = formatValue(obj[k]) + if type(k) == 'number' then + table.insert(list, stringf('[%s]=%s', k, value)) else - _, curBrakeTime = GetAutopilotTBBrakeDistanceAndTime(speed) + table.insert(list, stringf('%s=%s', k, value)) end - local cruiseDistance = 0 - local cruiseTime = 0 - -- So, time is in seconds - -- If cruising or braking, use real cruise/brake values - if AutopilotCruising or (not Autopilot and speed > 5) then -- If already cruising, use current speed - cruiseTime = Kinematic.computeTravelTime(speed, 0, AutopilotDistance) - elseif brakeDistance + accelDistance < AutopilotDistance then - -- Add any remaining distance - cruiseDistance = AutopilotDistance - (brakeDistance + accelDistance) - cruiseTime = Kinematic.computeTravelTime(8333.0556, 0, cruiseDistance) + end + if indent then + return stringf('%s%s', indent, table.concat(list, ',\n' .. indent)) + end + return stringf('{%s}', table.concat(list, ',')) + end + BodyParameters.__eq = function(lhs, rhs) + return lhs.planetarySystemId == rhs.planetarySystemId and lhs.bodyId == rhs.bodyId and + float_eq(lhs.radius, rhs.radius) and float_eq(lhs.center.x, rhs.center.x) and + float_eq(lhs.center.y, rhs.center.y) and float_eq(lhs.center.z, rhs.center.z) and + float_eq(lhs.GM, rhs.GM) + end + local function mkBodyParameters(systemId, bodyId, radius, worldCoordinates, GM) + -- 'worldCoordinates' can be either table or vec3 + assert(isSNumber(systemId), 'Argument 1 (planetarySystemId) must be a number:' .. type(systemId)) + assert(isSNumber(bodyId), 'Argument 2 (bodyId) must be a number:' .. type(bodyId)) + assert(isSNumber(radius), 'Argument 3 (radius) must be a number:' .. type(radius)) + assert(isTable(worldCoordinates), + 'Argument 4 (worldCoordinates) must be a array or vec3.' .. type(worldCoordinates)) + assert(isSNumber(GM), 'Argument 5 (GM) must be a number:' .. type(GM)) + return setmetatable({ + planetarySystemId = tonumber(systemId), + bodyId = tonumber(bodyId), + radius = tonumber(radius), + center = vec3(worldCoordinates), + GM = tonumber(GM) + }, BodyParameters) + end + -- MapPosition: Geographical coordinates of a point on a planetary body. + local MapPosition = {} + MapPosition.__index = MapPosition + MapPosition.__tostring = function(p) + return stringf('::pos{%d,%d,%s,%s,%s}', p.systemId, p.bodyId, formatNumber(p.latitude * rad2deg), + formatNumber(p.longitude * rad2deg), formatNumber(p.altitude)) + end + MapPosition.__eq = function(lhs, rhs) + return lhs.bodyId == rhs.bodyId and lhs.systemId == rhs.systemId and + float_eq(lhs.latitude, rhs.latitude) and float_eq(lhs.altitude, rhs.altitude) and + (float_eq(lhs.longitude, rhs.longitude) or float_eq(lhs.latitude, math.pi / 2) or + float_eq(lhs.latitude, -math.pi / 2)) + end + -- latitude and longitude are in degrees while altitude is in meters + local function mkMapPosition(overload, bodyId, latitude, longitude, altitude) + local systemId = overload -- Id or '::pos{...}' string + + if isString(overload) and not longitude and not altitude and not bodyId and not latitude then + systemId, bodyId, latitude, longitude, altitude = string.match(overload, posPattern) + assert(systemId, 'Argument 1 (position string) is malformed.') + else + assert(isSNumber(systemId), 'Argument 1 (systemId) must be a number:' .. type(systemId)) + assert(isSNumber(bodyId), 'Argument 2 (bodyId) must be a number:' .. type(bodyId)) + assert(isSNumber(latitude), 'Argument 3 (latitude) must be in degrees:' .. type(latitude)) + assert(isSNumber(longitude), 'Argument 4 (longitude) must be in degrees:' .. type(longitude)) + assert(isSNumber(altitude), 'Argument 5 (altitude) must be in meters:' .. type(altitude)) + end + systemId = tonumber(systemId) + bodyId = tonumber(bodyId) + latitude = tonumber(latitude) + longitude = tonumber(longitude) + altitude = tonumber(altitude) + if bodyId == 0 then -- this is a hack to represent points in space + return setmetatable({ + latitude = latitude, + longitude = longitude, + altitude = altitude, + bodyId = bodyId, + systemId = systemId + }, MapPosition) + end + return setmetatable({ + latitude = deg2rad * clamp(latitude, -90, 90), + longitude = deg2rad * (longitude % 360), + altitude = altitude, + bodyId = bodyId, + systemId = systemId + }, MapPosition) + end + -- PlanetarySystem - map body IDs to BodyParameters + local PlanetarySystem = {} + PlanetarySystem.__index = PlanetarySystem + PlanetarySystem.__tostring = function(obj, indent) + local sep = indent and (indent .. ' ') + local bdylist = {} + local keys = {} + for k in pairs(obj) do + table.insert(keys, k) + end + table.sort(keys) + for _, bi in ipairs(keys) do + bdy = obj[bi] + local bdys = BodyParameters.__tostring(bdy, sep) + if indent then + table.insert(bdylist, stringf('[%s]={\n%s\n%s}', bi, bdys, indent)) else - local accelRatio = (AutopilotDistance - brakeDistance) / accelDistance - accelDistance = AutopilotDistance - brakeDistance -- Accel until we brake - - accelTime = accelTime * accelRatio - end - if CustomTarget ~= nil and CustomTarget.planetname == planet.name and not Autopilot then - return cruiseTime - elseif AutopilotBraking then - return curBrakeTime - elseif AutopilotCruising then - return cruiseTime + curBrakeTime - else -- If not cruising or braking, assume we'll get to max speed - return accelTime + brakeTime + cruiseTime + table.insert(bdylist, stringf(' [%s]=%s', bi, bdys)) end end + if indent then + return stringf('\n%s%s%s', indent, table.concat(bdylist, ',\n' .. indent), indent) + end + return stringf('{\n%s\n}', table.concat(bdylist, ',\n')) + end + local function mkPlanetarySystem(referenceTable) + local atlas = {} + local pid + for _, v in pairs(referenceTable) do + local id = v.planetarySystemId + if type(id) ~= 'number' then + error('Invalid planetary system ID: ' .. tostring(id)) + elseif pid and id ~= pid then + error('Mismatch planetary system IDs: ' .. id .. ' and ' .. pid) + end + local bid = v.bodyId + if type(bid) ~= 'number' then + error('Invalid body ID: ' .. tostring(bid)) + elseif atlas[bid] then + error('Duplicate body ID: ' .. tostring(bid)) + end + setmetatable(v.center, getmetatable(vec3.unit_x)) + atlas[bid] = setmetatable(v, BodyParameters) + pid = id + end + return setmetatable(atlas, PlanetarySystem) + end + -- PlanetaryReference - map planetary system ID to PlanetarySystem + PlanetaryReference = {} + local function mkPlanetaryReference(referenceTable) + return setmetatable({ + galaxyAtlas = referenceTable or {} + }, PlanetaryReference) + end + PlanetaryReference.__index = function(t, i) + if type(i) == 'number' then + local system = t.galaxyAtlas[i] + return mkPlanetarySystem(system) + end + return rawget(PlanetaryReference, i) + end + PlanetaryReference.__pairs = function(obj) + return function(t, k) + local nk, nv = next(t, k) + return nk, nv and mkPlanetarySystem(nv) + end, obj.galaxyAtlas, nil + end + PlanetaryReference.__tostring = function(obj) + local pslist = {} + for _, ps in pairs(obj or {}) do + local psi = ps:getPlanetarySystemId() + local pss = PlanetarySystem.__tostring(ps, ' ') + table.insert(pslist, stringf(' [%s]={%s\n }', psi, pss)) + end + return stringf('{\n%s\n}\n', table.concat(pslist, ',\n')) + end + PlanetaryReference.BodyParameters = mkBodyParameters + PlanetaryReference.MapPosition = mkMapPosition + PlanetaryReference.PlanetarySystem = mkPlanetarySystem + function PlanetaryReference.createBodyParameters(planetarySystemId, bodyId, surfaceArea, aPosition, + verticalAtPosition, altitudeAtPosition, gravityAtPosition) + assert(isSNumber(planetarySystemId), + 'Argument 1 (planetarySystemId) must be a number:' .. type(planetarySystemId)) + assert(isSNumber(bodyId), 'Argument 2 (bodyId) must be a number:' .. type(bodyId)) + assert(isSNumber(surfaceArea), 'Argument 3 (surfaceArea) must be a number:' .. type(surfaceArea)) + assert(isTable(aPosition), 'Argument 4 (aPosition) must be an array or vec3:' .. type(aPosition)) + assert(isTable(verticalAtPosition), + 'Argument 5 (verticalAtPosition) must be an array or vec3:' .. type(verticalAtPosition)) + assert(isSNumber(altitudeAtPosition), + 'Argument 6 (altitude) must be in meters:' .. type(altitudeAtPosition)) + assert(isSNumber(gravityAtPosition), + 'Argument 7 (gravityAtPosition) must be number:' .. type(gravityAtPosition)) + local radius = math.sqrt(surfaceArea / 4 / math.pi) + local distance = radius + altitudeAtPosition + local center = vec3(aPosition) + distance * vec3(verticalAtPosition) + local GM = gravityAtPosition * distance * distance + return mkBodyParameters(planetarySystemId, bodyId, radius, center, GM) + end - function GetAutopilotBrakeDistanceAndTime(speed) - -- If we're in atmo, just return some 0's or LastMaxBrake, whatever's bigger - -- So we don't do unnecessary API calls when atmo brakes don't tell us what we want - if not inAtmo then - RefreshLastMaxBrake() - return Kinematic.computeDistanceAndTime(speed, AutopilotEndSpeed, constructMass(), 0, 0, - LastMaxBrake - (AutopilotPlanetGravity * constructMass())) - else - if LastMaxBrakeInAtmo and LastMaxBrakeInAtmo > 0 then - return Kinematic.computeDistanceAndTime(speed, AutopilotEndSpeed, constructMass(), 0, 0, - LastMaxBrakeInAtmo - (AutopilotPlanetGravity * constructMass())) - else - return 0, 0 + PlanetaryReference.isMapPosition = isMapPosition + function PlanetaryReference:getPlanetarySystem(overload) + -- if galaxyAtlas then + if i == nil then i = 0 end + if nv == nil then nv = 0 end + local planetarySystemId = overload + if isMapPosition(overload) then + planetarySystemId = overload.systemId + end + if type(planetarySystemId) == 'number' then + local system = self.galaxyAtlas[i] + if system then + if getmetatable(nv) ~= PlanetarySystem then + system = mkPlanetarySystem(system) end + return system end end + -- end + -- return nil + end - function GetAutopilotTBBrakeDistanceAndTime(speed) -- Uses thrust and a configured T50 - RefreshLastMaxBrake() - return Kinematic.computeDistanceAndTime(speed, AutopilotEndSpeed, constructMass(), Nav:maxForceForward(), - warmup, LastMaxBrake - (AutopilotPlanetGravity * constructMass())) + function PlanetarySystem:castIntersections(origin, direction, sizeCalculator, bodyIds) + local sizeCalculator = sizeCalculator or function(body) + return 1.05 * body.radius end - - function hoverDetectGround() - local vgroundDistance = -1 - local hgroundDistance = -1 - if vBooster then - vgroundDistance = vBooster.distance() + local candidates = {} + if bodyIds then + for _, i in ipairs(bodyIds) do + candidates[i] = self[i] end - if hover then - hgroundDistance = hover.distance() - end - if vgroundDistance ~= -1 and hgroundDistance ~= -1 then - if vgroundDistance < hgroundDistance then - return vgroundDistance - else - return hgroundDistance - end - elseif vgroundDistance ~= -1 then - return vgroundDistance - elseif hgroundDistance ~= -1 then - return hgroundDistance - else - return -1 - end - end - - function AboveGroundLevel() - local groundDistance = -1 - local hgroundDet = hovGndDet - if telemeter_1 then - groundDistance = telemeter_1.getDistance() - end - if hgroundDet ~= -1 and groundDistance ~= -1 then - if hgroundDet < groundDistance then - return hgroundDet - else - return groundDistance - end - elseif hgroundDet ~= -1 then - return hgroundDet - else - return groundDistance + else + bodyIds = {} + for k, body in pairs(self) do + table.insert(bodyIds, k) + candidates[k] = body + end + end + local function compare(b1, b2) + local v1 = candidates[b1].center - origin + local v2 = candidates[b2].center - origin + return v1:len() < v2:len() + end + table.sort(bodyIds, compare) + local dir = direction:normalize() + for i, id in ipairs(bodyIds) do + local body = candidates[id] + local c_oV3 = body.center - origin + local radius = sizeCalculator(body) + local dot = c_oV3:dot(dir) + local desc = dot ^ 2 - (c_oV3:len2() - radius ^ 2) + if desc >= 0 then + local root = math.sqrt(desc) + local farSide = dot + root + local nearSide = dot - root + if nearSide > 0 then + return body, farSide, nearSide + elseif farSide > 0 then + return body, farSide, nil + end + end + end + return nil, nil, nil + end + + function PlanetarySystem:closestBody(coordinates) + assert(type(coordinates) == 'table', 'Invalid coordinates.') + local minDistance2, body + local coord = vec3(coordinates) + for _, params in pairs(self) do + local distance2 = (params.center - coord):len2() + if not body or distance2 < minDistance2 then + body = params + minDistance2 = distance2 end end + return body + end - function tablelength(T) - local count = 0 - for _ in pairs(T) do - count = count + 1 - end - return count + function PlanetarySystem:convertToBodyIdAndWorldCoordinates(overload) + local mapPosition = overload + if isString(overload) then + mapPosition = mkMapPosition(overload) end + if mapPosition.bodyId == 0 then + return 0, vec3(mapPosition.latitude, mapPosition.longitude, mapPosition.altitude) + end + local params = self:getBodyParameters(mapPosition) + if params then + return mapPosition.bodyId, params:convertToWorldCoordinates(mapPosition) + end + end - function BeginProfile(profileName) - ProfileTimeStart = system.getTime() + function PlanetarySystem:getBodyParameters(overload) + local bodyId = overload + if isMapPosition(overload) then + bodyId = overload.bodyId end + assert(isSNumber(bodyId), 'Argument 1 (bodyId) must be a number:' .. type(bodyId)) + return self[bodyId] + end - function EndProfile(profileName) - local profileTime = system.getTime() - ProfileTimeStart - ProfileTimeSum = ProfileTimeSum + profileTime - ProfileCount = ProfileCount + 1 - if profileTime > ProfileTimeMax then - ProfileTimeMax = profileTime - end + function PlanetarySystem:getPlanetarySystemId() + local _, v = next(self) + return v and v.planetarySystemId + end - if profileTime < ProfileTimeMin then - ProfileTimeMin = profileTime - end - end + function BodyParameters:convertToMapPosition(worldCoordinates) + assert(isTable(worldCoordinates), + 'Argument 1 (worldCoordinates) must be an array or vec3:' .. type(worldCoordinates)) + local worldVec = vec3(worldCoordinates) + if self.bodyId == 0 then + return setmetatable({ + latitude = worldVec.x, + longitude = worldVec.y, + altitude = worldVec.z, + bodyId = 0, + systemId = self.planetarySystemId + }, MapPosition) + end + local coords = worldVec - self.center + local distance = coords:len() + local altitude = distance - self.radius + local latitude = 0 + local longitude = 0 + if not float_eq(distance, 0) then + local phi = math.atan(coords.y, coords.x) + longitude = phi >= 0 and phi or (2 * math.pi + phi) + latitude = math.pi / 2 - math.acos(coords.z / distance) + end + return setmetatable({ + latitude = latitude, + longitude = longitude, + altitude = altitude, + bodyId = self.bodyId, + systemId = self.planetarySystemId + }, MapPosition) + end + + function BodyParameters:convertToWorldCoordinates(overload) + local mapPosition = isString(overload) and mkMapPosition(overload) or overload + if mapPosition.bodyId == 0 then -- support deep space map position + return vec3(mapPosition.latitude, mapPosition.longitude, mapPosition.altitude) + end + assert(isMapPosition(mapPosition), 'Argument 1 (mapPosition) is not an instance of "MapPosition".') + assert(mapPosition.systemId == self.planetarySystemId, + 'Argument 1 (mapPosition) has a different planetary system ID.') + assert(mapPosition.bodyId == self.bodyId, 'Argument 1 (mapPosition) has a different planetary body ID.') + local xproj = math.cos(mapPosition.latitude) + return self.center + (self.radius + mapPosition.altitude) * + vec3(xproj * math.cos(mapPosition.longitude), xproj * math.sin(mapPosition.longitude), + math.sin(mapPosition.latitude)) + end + + function BodyParameters:getAltitude(worldCoordinates) + return (vec3(worldCoordinates) - self.center):len() - self.radius + end + + function BodyParameters:getDistance(worldCoordinates) + return (vec3(worldCoordinates) - self.center):len() + end - function ResetProfiles() - ProfileTimeMin = 9999 - ProfileTimeMax = 0 - ProfileCount = 0 - ProfileTimeSum = 0 + function BodyParameters:getGravity(worldCoordinates) + local radial = self.center - vec3(worldCoordinates) -- directed towards body + local len2 = radial:len2() + return (self.GM / len2) * radial / math.sqrt(len2) + end + -- end of module + return setmetatable(PlanetaryReference, { + __call = function(_, ...) + return mkPlanetaryReference(...) end + }) +end - function ReportProfiling() - local totalTime = ProfileTimeSum - local averageTime = ProfileTimeSum / ProfileCount - local min = ProfileTimeMin - local max = ProfileTimeMax - local samples = ProfileCount - sprint(stringf("SUM: %.4f AVG: %.4f MIN: %.4f MAX: %.4f CNT: %d", totalTime, averageTime, min, - max, samples)) +function Keplers() + local vec3 = require('cpml.vec3') + local PlanetRef = PlanetRef() + local function isString(s) + return type(s) == 'string' + end + local function isTable(t) + return type(t) == 'table' + end + local function float_eq(a, b) + if a == 0 then + return math.abs(b) < 1e-09 end - - function updateWeapons() - if weapon then - if WeaponPanelID==nil and (radarPanelID ~= nil or GearExtended) then - _autoconf.displayCategoryPanel(weapon, weapon_size, L_TEXT("ui_lua_widget_weapon", "Weapons"), "weapon", true) - WeaponPanelID = _autoconf.panels[_autoconf.panels_size] - elseif WeaponPanelID ~= nil and radarPanelID == nil and not GearExtended then - system.destroyWidgetPanel(WeaponPanelID) - WeaponPanelID = nil - end - end + if b == 0 then + return math.abs(a) < 1e-09 end + return math.abs(a - b) < math.max(math.abs(a), math.abs(b)) * constants.epsilon + end + Kepler = {} + Kepler.__index = Kepler + + function Kepler:escapeAndOrbitalSpeed(altitude) + assert(self.body) + -- P = -GMm/r and KE = mv^2/2 (no lorentz factor used) + -- mv^2/2 = GMm/r + -- v^2 = 2GM/r + -- v = sqrt(2GM/r1) + local distance = altitude + self.body.radius + if not float_eq(distance, 0) then + local orbit = math.sqrt(self.body.GM / distance) + return math.sqrt(2) * orbit, orbit + end + return nil, nil + end - function updateRadar() - if (radar_1) then - local radarContacts = radar_1.getEntries() - local radarData = radar_1.getData() - local radarX = ConvertResolutionX(1770) - local radarY = ConvertResolutionY(330) - if #radarContacts > 0 then - local target = radarData:find('identifiedConstructs":%[%]') - if target == nil and perisPanelID == nil then - peris = 1 - ToggleRadarPanel() - end - if target ~= nil and perisPanelID ~= nil then - ToggleRadarPanel() - end - if radarPanelID == nil then - ToggleRadarPanel() - end - radarMessage = stringf( - [[Radar: %i contacts]], - radarX, radarY, #radarContacts) - local friendlies = {} - for k, v in pairs(radarContacts) do - if radar_1.hasMatchingTransponder(v) == 1 then - friendlies[#friendlies + 1] = v - end - end - if #friendlies > 0 then - local y = ConvertResolutionY(15) - local x = ConvertResolutionX(1370) - radarMessage = stringf( - [[%sFriendlies In Range]], - radarMessage, x, y) - for k, v in pairs(friendlies) do - y = y + 20 - radarMessage = stringf([[%s%s]], - radarMessage, x, y, radar_1.getConstructName(v)) - end - end - else - local data - data = radarData:find('worksInEnvironment":false') - if data then - radarMessage = stringf([[ - Radar: Jammed]], - radarX, radarY) + function Kepler:orbitalParameters(overload, velocity) + assert(self.body) + assert(isTable(overload) or isString(overload)) + assert(isTable(velocity)) + local pos = (isString(overload) or PlanetRef.isMapPosition(overload)) and + self.body:convertToWorldCoordinates(overload) or vec3(overload) + local v = vec3(velocity) + local r = pos - self.body.center + local v2 = v:len2() + local d = r:len() + local mu = self.body.GM + local e = ((v2 - mu / d) * r - r:dot(v) * v) / mu + local a = mu / (2 * mu / d - v2) + local ecc = e:len() + local dir = e:normalize() + local pd = a * (1 - ecc) + local ad = a * (1 + ecc) + local per = pd * dir + self.body.center + local apo = ecc <= 1 and -ad * dir + self.body.center or nil + local trm = math.sqrt(a * mu * (1 - ecc * ecc)) + local Period = apo and 2 * math.pi * math.sqrt(a ^ 3 / mu) + -- These are great and all, but, I need more. + local trueAnomaly = math.acos((e:dot(r)) / (ecc * d)) + if r:dot(v) < 0 then + trueAnomaly = -(trueAnomaly - 2 * math.pi) + end + -- Apparently... cos(EccentricAnomaly) = (cos(trueAnomaly) + eccentricity)/(1 + eccentricity * cos(trueAnomaly)) + local EccentricAnomaly = math.acos((math.cos(trueAnomaly) + ecc) / (1 + ecc * math.cos(trueAnomaly))) + -- Then.... apparently if this is below 0, we should add 2pi to it + -- I think also if it's below 0, we're past the apoapsis? + local timeTau = EccentricAnomaly + if timeTau < 0 then + timeTau = timeTau + 2 * math.pi + end + -- So... time since periapsis... + -- Is apparently easy if you get mean anomly. t = M/n where n is mean motion, = 2*pi/Period + local MeanAnomaly = timeTau - ecc * math.sin(timeTau) + local TimeSincePeriapsis = 0 + local TimeToPeriapsis = 0 + local TimeToApoapsis = 0 + if Period ~= nil then + TimeSincePeriapsis = MeanAnomaly / (2 * math.pi / Period) + -- Mean anom is 0 at periapsis, positive before it... and positive after it. + -- I guess this is why I needed to use timeTau and not EccentricAnomaly here + + TimeToPeriapsis = Period - TimeSincePeriapsis + TimeToApoapsis = TimeToPeriapsis + Period / 2 + if trueAnomaly - math.pi > 0 then -- TBH I think something's wrong in my formulas because I needed this. + TimeToPeriapsis = TimeSincePeriapsis + TimeToApoapsis = TimeToPeriapsis + Period / 2 + end + if TimeToApoapsis > Period then + TimeToApoapsis = TimeToApoapsis - Period + end + end + return { + periapsis = { + position = per, + speed = trm / pd, + circularOrbitSpeed = math.sqrt(mu / pd), + altitude = pd - self.body.radius + }, + apoapsis = apo and { + position = apo, + speed = trm / ad, + circularOrbitSpeed = math.sqrt(mu / ad), + altitude = ad - self.body.radius + }, + currentVelocity = v, + currentPosition = pos, + eccentricity = ecc, + period = Period, + eccentricAnomaly = EccentricAnomaly, + meanAnomaly = MeanAnomaly, + timeToPeriapsis = TimeToPeriapsis, + timeToApoapsis = TimeToApoapsis + } + end + local function new(bodyParameters) + local params = PlanetRef.BodyParameters(bodyParameters.planetarySystemId, bodyParameters.bodyId, + bodyParameters.radius, bodyParameters.center, bodyParameters.GM) + return setmetatable({ + body = params + }, Kepler) + end + return setmetatable(Kepler, { + __call = function(_, ...) + return new(...) + end + }) +end + +function Kinematics() + + local Kinematic = {} -- just a namespace + local C = 30000000 / 3600 + local C2 = C * C + local ITERATIONS = 100 -- iterations over engine "warm-up" period + local function lorentz(v) + return 1 / math.sqrt(1 - v * v / C2) + end + + function Kinematic.computeAccelerationTime(initial, acceleration, final) + -- The low speed limit of following is: t=(vf-vi)/a (from: vf=vi+at) + local k1 = C * math.asin(initial / C) + return (C * math.asin(final / C) - k1) / acceleration + end + + function Kinematic.computeDistanceAndTime(initial, final, restMass, thrust, t50, brakeThrust) + + t50 = t50 or 0 + brakeThrust = brakeThrust or 0 -- usually zero when accelerating + local speedUp = initial <= final + local a0 = thrust * (speedUp and 1 or -1) / restMass + local b0 = -brakeThrust / restMass + local totA = a0 + b0 + if speedUp and totA <= 0 or not speedUp and totA >= 0 then + return -1, -1 -- no solution + end + local distanceToMax, timeToMax = 0, 0 + + if a0 ~= 0 and t50 > 0 then + + local k1 = math.asin(initial / C) + local c1 = math.pi * (a0 / 2 + b0) + local c2 = a0 * t50 + local c3 = C * math.pi + local v = function(t) + local w = (c1 * t - c2 * math.sin(math.pi * t / 2 / t50) + c3 * k1) / c3 + local tan = math.tan(w) + return C * tan / math.sqrt(tan * tan + 1) + end + local speedchk = speedUp and function(s) + return s >= final + end or function(s) + return s <= final + end + timeToMax = 2 * t50 + if speedchk(v(timeToMax)) then + local lasttime = 0 + while math.abs(timeToMax - lasttime) > 0.5 do + local t = (timeToMax + lasttime) / 2 + if speedchk(v(t)) then + timeToMax = t else - radarMessage = stringf([[ - Radar: No Contacts]], - radarX, radarY) - end - if radarPanelID ~= nil then - peris = 0 - ToggleRadarPanel() + lasttime = t end end end + -- There is no closed form solution for distance in this case. + -- Numerically integrate for time t=0 to t=2*T50 (or less) + local lastv = initial + local tinc = timeToMax / ITERATIONS + for step = 1, ITERATIONS do + local speed = v(step * tinc) + distanceToMax = distanceToMax + (speed + lastv) * tinc / 2 + lastv = speed + end + if timeToMax < 2 * t50 then + return distanceToMax, timeToMax + end + initial = lastv end - Animating = false - Animated = false + local k1 = C * math.asin(initial / C) + local time = (C * math.asin(final / C) - k1) / totA + local k2 = C2 * math.cos(k1 / C) / totA + local distance = k2 - C2 * math.cos((totA * time + k1) / C) / totA + return distance + distanceToMax, time + timeToMax + end + + function Kinematic.computeTravelTime(initial, acceleration, distance) + -- The low speed limit of following is: t=(sqrt(2ad+v^2)-v)/a + -- (from: d=vt+at^2/2) + if distance == 0 then + return 0 + end + if acceleration > 0 then + local k1 = C * math.asin(initial / C) + local k2 = C2 * math.cos(k1 / C) / acceleration + return (C * math.acos(acceleration * (k2 - distance) / C2) - k1) / acceleration + end + assert(initial > 0, 'Acceleration and initial speed are both zero.') + return distance / initial + end + + function Kinematic.lorentz(v) + return lorentz(v) + end + return Kinematic +end + +-- Start of actual HUD Script. Written by Dimencia and Archaegeo. Optimization and Automation of scripting by ChronosWS Linked sources where appropriate, most have been modified. +function script.onStart() + VERSION_NUMBER = 4.914 + SetupComplete = false + beginSetup = coroutine.create(function() + Nav.axisCommandManager:setupCustomTargetSpeedRanges(axisCommandId.longitudinal, + {1000, 5000, 10000, 20000, 30000}) + + -- Load Saved Variables + LoadVariables() + + coroutine.yield() -- Give it some time to breathe before we do the rest + + -- Find elements we care about + ProcessElements() + + coroutine.yield() -- Give it some time to breathe before we do the rest + + SetupChecks() -- All the if-thens to set up for particular ship + + SetupButtons() -- Set up all the pushable buttons. + + coroutine.yield() -- Just to make sure + -- Set up Jaylebreak and atlas + SetupAtlas() + PlanetaryReference = PlanetRef() + galaxyReference = PlanetaryReference(Atlas()) + Kinematic = Kinematics() + Kep = Keplers() AddLocationsToAtlas() UpdateAutopilotTarget() + coroutine.yield() + + unit.hide() + system.showScreen(1) + -- That was a lot of work with dirty strings and json. Clean up collectgarbage("collect") + -- Start timers unit.setTimer("apTick", apTickRate) unit.setTimer("oneSecond", 1) unit.setTimer("tenthSecond", 1/10) - if UseSatNav then unit.setTimer("fiveSecond", 5) end + if UseSatNav then + unit.setTimer("fiveSecond", 5) + end end) end @@ -5496,34 +5551,4 @@ function script.onActionLoop(action) end end -function DisplayMessage(newContent, displayText) - if displayText ~= "empty" then - newContent[#newContent + 1] = [[]] - for str in string.gmatch(displayText, "([^\n]+)") do - newContent[#newContent + 1] = stringf([[%s]], str) - end - newContent[#newContent + 1] = [[]] - end - if msgTimer ~= 0 then - unit.setTimer("msgTick", msgTimer) - msgTimer = 0 - end -end - -function updateDistance() - local curTime = system.getTime() - local velocity = vec3(core.getWorldVelocity()) - local spd = vec3(velocity):len() - local elapsedTime = curTime - lastTravelTime - if (spd > 1.38889) then - spd = spd / 1000 - local newDistance = spd * (curTime - lastTravelTime) - TotalDistanceTravelled = TotalDistanceTravelled + newDistance - totalDistanceTrip = totalDistanceTrip + newDistance - end - flightTime = flightTime + elapsedTime - TotalFlightTime = TotalFlightTime + elapsedTime - lastTravelTime = curTime -end - script.onStart()