· 5 years ago · Sep 23, 2020, 10:08 PM
1Skip to content
2Why GitHub?
3Team
4Enterprise
5Explore
6Marketplace
7Pricing
8Search
9
10Sign in
11Sign up
12Dimencia
13/
14DU-Orbital-Hud
15153211
16Code
17Issues
186
19Pull requests
20Actions
21Projects
22Security
23Insights
24Join GitHub today
25GitHub is home to over 50 million developers working together to host and review code, manage projects, and build software together.
26
27DU-Orbital-Hud/ButtonHUD.conf
28@Dimencia
29Dimencia Minor fixes
30…
31Latest commit 42514df 5 hours ago
32 History
33 2 contributors
34@Archaegeo@Dimencia
353139 lines (2971 sloc) 184 KB
36
37name: ButtonsHud - Dimencia and Archaegeo V2.0
38
39slots:
40 core:
41 class: CoreUnit
42 antigrav:
43 class: AntiGravityGeneratorUnit
44 warpdrive:
45 class: WarpDriveUnit
46 atmofueltank:
47 class: AtmoFuelContainer
48 select: all
49 spacefueltank:
50 class: SpaceFuelContainer
51 select: all
52 rocketfueltank:
53 class: RocketFuelContainer
54 select: all
55 gyro:
56 class: GyroUnit
57 weapon:
58 class: WeaponUnit
59 select: manual
60 radar:
61 class: RadarPVPUnit
62 select: manual
63 dbHud:
64 class: databank
65
66
67handlers:
68 unit:
69 start:
70 lua: |
71 Nav = Navigator.new(system, core, unit)
72 Nav.axisCommandManager:setupCustomTargetSpeedRanges(axisCommandId.longitudinal, {1000, 5000, 10000, 20000, 30000})
73
74 -- Written by Dimencia. Linked sources where appropriate, most have been modified. HUD by Archeageo
75
76 -- USER DEFINABLE GLOBAL AND LOCAL VARIABLES
77 AutopilotTargetOrbit = 100000 --export: How far you want the orbit to be from the planet in m. 200,000 = 1SU
78 DeadZone = 50 --export: Number of pixels of deadzone at the center of the screen
79 MouseYSensitivity = 0.003 --export: For virtual joystick only
80 MouseXSensitivity = 0.003 --export: For virtual joystick only
81 MinAutopilotSpeed = 55 --export: Minimum speed for autopilot to maneuver in m/s. Keep above 25m/s to prevent nosedives when boosters kick in
82 userControlScheme = "Virtual Joystick" --export: Set to "Virtual Joystick", "Mouse", or "Keyboard"
83 freeLookToggle = "true" --export: Set to false for default free look behavior.
84 brakeToggle = "true" --export: Set to false to use hold to brake vice toggle brake.
85 LastMaxBrake = 0
86
87 mousePitchFactor = 1 --export: Mouse control only
88 mouseYawFactor = 1 --export: Mouse control only
89
90 local PrimaryR = 130 --export: Primary HUD color
91 local PrimaryG = 224 --export: Primary HUD color
92 local PrimaryB = 255 --export: Primary HUD color
93 local AutopilotStrength = 1 --export: How strongly autopilot tries to point at a target
94 local DampingMultiplier = 40 --export: How strongly it dampens when nearing the correct orientation
95 local alignmentTolerance = 0.001 --export: How closely it must align to a planet before accelerating to it
96 local circleRad = 99 --export: The size of the roll/pitch circle, set to 0 to remove.
97
98 local ResolutionWidth = 2560
99 local ResolutionHeight = 1440
100
101 local ButtonBrakeWidth = 240 --export: Size and positioning for brake button
102 local ButtonBrakeHeight = 50 --export: Size and positioning for brake button
103 local ButtonBrakeX = ResolutionWidth/2 - ButtonBrakeWidth/2 --export: Size and positioning for brake button
104 local ButtonBrakeY = ResolutionHeight/2 - ButtonBrakeHeight + 400 --export: Size and positioning for brake button
105
106 local ButtonProgradeWidth = 260 --export: Size and positioning for prograde button
107 local ButtonProgradeHeight = 50 --export: Size and positioning for prograde button
108 local ButtonProgradeX = ResolutionWidth/2 - ButtonProgradeWidth/2 - ButtonBrakeWidth - 50 --export: Size and positioning for prograde button
109 local ButtonProgradeY = ResolutionHeight/2 - ButtonProgradeHeight + 380 --export: Size and positioning for prograde button
110
111 local ButtonRetrogradeWidth = 260 --export: Size and positioning for retrograde button
112 local ButtonRetrogradeHeight = 50 --export: Size and positioning for retrograde button
113 local ButtonRetrogradeX = ResolutionWidth/2 - ButtonRetrogradeWidth/2 + ButtonBrakeWidth + 50 --export: Size and positioning for retrograde button
114 local ButtonRetrogradeY = ResolutionHeight/2 - ButtonRetrogradeHeight + 380 --export: Size and positioning for retrograde button
115
116 local ButtonAutopilotWidth = 600 --export: Size and positioning for autopilot button
117 local ButtonAutopilotHeight = 60 --export: Size and positioning for autopilot button
118 local ButtonAutopilotX = ResolutionWidth/2 - ButtonAutopilotWidth/2
119 local ButtonAutopilotY = ResolutionHeight/2 - ButtonAutopilotHeight/2 - 400
120
121 warmup = 32 --export: How long it takes your engines to warmup. Basic Space Engines, from XS to XL: 0.25,1,4,16,32
122
123 -- GLOBAL VARIABLES SECTION, IF NOT USED OUTSIDE THIS START, MAKE IT LOCAL
124 hasGear = false
125 pitchInput = 0
126 rollInput = 0
127 yawInput = 0
128 brakeInput = 0
129 pitchInput2 = 0
130 rollInput2 = 0
131 yawInput2 = 0
132 BrakeIsOn = false
133 RetrogradeIsOn = false
134 ProgradeIsOn = false
135 AutoBrake = false
136 Autopilot = false
137 TurnBurn = false
138 AutopilotAccelerating = false
139 AutopilotBraking = false
140 AutopilotCruising = false
141 AutopilotRealigned = false
142 AutopilotEndSpeed = 0
143 AutopilotStatus = "Aligning"
144 simulatedX = 0
145 simulatedY = 0
146 HoldingCtrl = false
147 PrevViewLock = 1
148 PreviousYawAmount = 0
149 PreviousPitchAmount = 0
150
151 isBoosting = false -- Dodgin's Don't Die Rocket Govenor - Cruise Control Edition
152
153 distance = 0
154 brakeDistance, brakeTime = 0
155 maxBrakeDistance, maxBrakeTime = 0
156 hasGear = false
157 hasDB = false
158 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.
159
160
161 -- LOCAL VARIABLES, USERS DO NOT CHANGE
162 BrakeButtonHovered = false
163 RetrogradeButtonHovered = false
164 ProgradeButtonHovered = false
165 AutopilotButtonHovered = false
166 if userControlScheme ~= "Keyboard" then
167 system.lockView(1)
168 else
169 system.lockView(0)
170 end
171 if unit.getAtmosphereDensity() > 0 then
172 BrakeIsOn = true
173 end
174
175 local elements = core.getElementIdList()
176 for k in pairs(elements) do
177 if (core.getElementTypeById(elements[k]) == "landing gear") then
178 hasGear = true
179 end
180 if (core.getElementTypeById(elements[k]) == "Databank") then
181 hasDB = true
182 end
183 end
184
185 -- LOAD SAVED VARIABLES IF ANY
186 if(hasDB) then
187 if(dbHud.getNbKeys() == 8 ) then -- Important that it not load if the number in the databank doesnt match the number expected. Would end up loading nil values or 0's.
188 -- VARIABLES TO BE LOADED GO HERE
189 userControlScheme = dbHud.getStringValue("userControlScheme")
190 AutopilotTargetOrbit = dbHud.getIntValue("AutopilotTargetOrbit")
191 brakeToggle = dbHud.getStringValue("brakeToggle")
192 apTickRate = dbHud.getFloatValue("apTickRate")
193 freeLookToggle = dbHud.getStringValue("freeLookToggle")
194 PrimaryR = dbHud.getIntValue("PrimaryR")
195 PrimaryG = dbHud.getIntValue("PrimaryG")
196 PrimaryB = dbHud.getIntValue("PrimaryB")
197 end
198 end
199
200 -- Close door and retract ramp if available
201 if door ~= nil then door.deactivate() end
202 if forcefield ~= nil then forcefield.deactivate() end
203
204 -- element widgets
205 -- For now we have to alternate between PVP and non-PVP widgets to have them on the same side.
206 _autoconf.displayCategoryPanel(weapon, weapon_size, L_TEXT("ui_lua_widget_weapon", "Weapons"), "weapon", true)
207 -- If radar is installed but no weapon, don't show periscope
208 if weapon_size > 0 then
209 _autoconf.displayCategoryPanel(radar, radar_size, L_TEXT("ui_lua_widget_periscope", "Periscope"), "periscope")
210 end
211 --core.show()
212 placeRadar = true
213 if placeRadar then
214 _autoconf.displayCategoryPanel(radar, radar_size, L_TEXT("ui_lua_widget_radar", "Radar"), "radar")
215 placeRadar = false
216 end
217 _autoconf.displayCategoryPanel(rocketfueltank, rocketfueltank_size, L_TEXT("ui_lua_widget_rocketfuel", "Rocket Fuel"), "fuel_container")
218 if placeRadar then -- We either have only rockets or no fuel tanks at all, uncommon for usual vessels
219 _autoconf.displayCategoryPanel(radar, radar_size, L_TEXT("ui_lua_widget_radar", "Radar"), "radar")
220 placeRadar = false
221 end
222 if antigrav ~= nil then antigrav.show() end
223 if warpdrive ~= nil then warpdrive.show() end
224 --if gyro ~= nil then gyro.show() end
225
226 -- freeze the player in he is remote controlling the construct
227 if Nav.control.isRemoteControlled() == 1 then
228 system.freeze(1)
229 end
230
231 -- landing gear
232 -- make sure every gears are synchonized with the first
233
234 if hasGear then
235 gearExtended = (Nav.control.isAnyLandingGearExtended() == 1) -- make sure it's a lua boolean
236 if gearExtended then
237 Nav.control.extendLandingGears()
238 else
239 Nav.control.retractLandingGears()
240 end
241 if gearExtended then
242 Nav.axisCommandManager:setTargetGroundAltitude(0)
243 else
244 Nav.axisCommandManager:setTargetGroundAltitude(500) -- lol I wish
245 end
246 else
247 if unit.getAtmosphereDensity() == 0 then
248 gearExtended = false
249 Nav.axisCommandManager:setTargetGroundAltitude(500)
250 else
251 gearExtended = true -- Show warning message and set behavior
252 Nav.axisCommandManager:setTargetGroundAltitude(0)
253 end
254 end
255
256 unit.setTimer("apTick", apTickRate)
257 UnitHidden = true
258 unit.hide()
259
260
261 -- BEGIN FUNCTION DEFINITIONS
262 function saveVariables()
263 if(not hasDB) then
264 system.print("No Databank Found, unable to save. You must have a Databank attached to ship prior to running the HUD autoconfigure")
265 elseif(dbHud.getNbKeys() > 0) then
266 dbHud.clear()
267 system.print("Databank wiped. Get out of the seat, set the savable variables, then re-enter seat and hit ALT-7 again")
268 else
269 -- VARIABLES TO BE SAVED GO HERE
270 dbHud.setStringValue("userControlScheme",userControlScheme)
271 dbHud.setIntValue("AutopilotTargetOrbit", AutopilotTargetOrbit)
272 dbHud.setStringValue("brakeToggle", brakeToggle)
273 dbHud.setFloatValue("apTickRate", apTickRate)
274 dbHud.setStringValue("freeLookToggle", freeLookToggle)
275 dbHud.setIntValue("PrimaryR", PrimaryR)
276 dbHud.setIntValue("PrimaryG", PrimaryG)
277 dbHud.setIntValue("PrimaryB", PrimaryB)
278 system.print("Saved: userControlScheme, AutopilotTargetOrbit, brakeToggle, apTickRate, freeLookToggle, Primary R/G/B")
279 end
280 end
281 function ShowInterplanetaryPanel()
282 -- Interplanetary helper
283 if panelInterplanetary == nil then
284 panelInterplanetary = system.createWidgetPanel("Interplanetary Helper")
285 interplanetaryHeader = system.createWidget(panelInterplanetary, "value")
286 interplanetaryHeaderText = system.createData('{"label": "Target Planet", "value": "N/A", "unit":""}')
287 system.addDataToWidget(interplanetaryHeaderText, interplanetaryHeader)
288 widgetDistance = system.createWidget(panelInterplanetary, "value")
289 widgetDistanceText = system.createData('{"label": "Distance", "value": "N/A", "unit":""}')
290 system.addDataToWidget(widgetDistanceText, widgetDistance)
291 widgetTravelTime = system.createWidget(panelInterplanetary, "value")
292 widgetTravelTimeText = system.createData('{"label": "Travel Time", "value": "N/A", "unit":""}')
293 system.addDataToWidget(widgetTravelTimeText, widgetTravelTime)
294 widgetCurBrakeDistance = system.createWidget(panelInterplanetary, "value")
295 widgetCurBrakeDistanceText = system.createData('{"label": "Cur Brake Distance", "value": "N/A", "unit":""}')
296 system.addDataToWidget(widgetCurBrakeDistanceText, widgetCurBrakeDistance)
297 widgetCurBrakeTime = system.createWidget(panelInterplanetary, "value")
298 widgetCurBrakeTimeText = system.createData('{"label": "Cur Brake Time", "value": "N/A", "unit":""}')
299 system.addDataToWidget(widgetCurBrakeTimeText, widgetCurBrakeTime)
300 widgetMaxBrakeDistance = system.createWidget(panelInterplanetary, "value")
301 widgetMaxBrakeDistanceText = system.createData('{"label": "Max Brake Distance", "value": "N/A", "unit":""}')
302 system.addDataToWidget(widgetMaxBrakeDistanceText, widgetMaxBrakeDistance)
303 widgetMaxBrakeTime = system.createWidget(panelInterplanetary, "value")
304 widgetMaxBrakeTimeText = system.createData('{"label": "Max Brake Time", "value": "N/A", "unit":""}')
305 system.addDataToWidget(widgetMaxBrakeTimeText, widgetMaxBrakeTime)
306 widgetTrajectoryAltitude = system.createWidget(panelInterplanetary, "value")
307 widgetTrajectoryAltitudeText = system.createData('{"label": "Projected Altitude", "value": "N/A", "unit":""}')
308 system.addDataToWidget(widgetTrajectoryAltitudeText, widgetTrajectoryAltitude)
309 end
310 end
311 function ToggleFuelPanels()
312 if fuelPanelID ~= nil then
313 system.destroyWidgetPanel(fuelPanelID)
314 fuelPanelID = nil
315 elseif atmofueltank_size > 0 then
316 _autoconf.displayCategoryPanel(atmofueltank, atmofueltank_size, L_TEXT("ui_lua_widget_atmofuel", "Atmo Fuel"), "fuel_container")
317 fuelPanelID = _autoconf.panels[_autoconf.panels_size]
318 end
319 if spacefuelPanelID ~= nil then
320 system.destroyWidgetPanel(spacefuelPanelID)
321 spacefuelPanelID = nil
322 elseif spacefueltank_size > 0 then
323 _autoconf.displayCategoryPanel(spacefueltank, spacefueltank_size, L_TEXT("ui_lua_widget_spacefuel", "Space Fuel"), "fuel_container")
324 spacefuelPanelID = _autoconf.panels[_autoconf.panels_size]
325 end
326 end
327 function Contains(mousex, mousey, x, y, width, height)
328 if mousex > x and mousex < (x + width) and mousey > y and mousey < (y + height) then
329 return true
330 else
331 return false
332 end
333 end
334 function ProgradeToggle()
335 -- Toggle Progrades
336 ProgradeIsOn = not ProgradeIsOn
337 RetrogradeIsOn = false -- Don't let both be on
338 Autopilot = false
339 ProgradeButtonHovered = false
340 local Progradestring = "Off"
341 if ProgradeIsOn then
342 Progradestring = "On"
343 end
344 end
345
346 function RetrogradeToggle()
347 -- Toggle Retrogrades
348 RetrogradeIsOn = not RetrogradeIsOn
349 ProgradeIsOn = false -- Don't let both be on
350 Autopilot = false
351 RetrogradeButtonHovered = false
352 local Retrogradestring = "Off"
353 if RetrogradeIsOn then
354 Retrogradestring = "On"
355 end
356 end
357
358 function BrakeToggle()
359 -- Toggle brakes
360 BrakeIsOn = not BrakeIsOn
361 BrakeButtonHovered = false
362 local Brakestring = "Off"
363 if BrakeIsOn then
364 Brakestring = "On"
365 end
366 end
367
368 function AutopilotToggle()
369 -- Toggle Autopilot, as long as the target isn't None
370 if AutopilotTargetName ~= "None" and not Autopilot then
371 Autopilot = true
372 RetrogradeIsOn = false
373 ProgradeIsOn = false
374 AutopilotButtonHovered = false
375 AutopilotRealigned = false
376 else
377 Autopilot = false
378 AutopilotButtonHovered = false
379 AutopilotRealigned = false
380 end
381 end
382
383 function CheckButtons()
384 if BrakeButtonHovered then
385 BrakeToggle()
386 end
387 if ProgradeButtonHovered then
388 ProgradeToggle()
389 end
390 if RetrogradeButtonHovered then
391 RetrogradeToggle()
392 end
393 if AutopilotButtonHovered then
394 AutopilotToggle()
395 end
396 end
397 function DrawDeadZone()
398 if system.isViewLocked() == 0 then
399 content = content .. "<circle cx='50%' cy='50%' r='".. DeadZone .. "' stroke=rgb(" .. math.floor(PrimaryR*0.3) .. "," .. math.floor(PrimaryG*0.3) .. "," .. math.floor(PrimaryB*0.3) .. ") stroke-width='2' fill='none' />"
400 else
401 content = content .. "<circle cx='50%' cy='50%' r='".. DeadZone .. "' stroke=rgb(" .. math.floor(PrimaryR*0.8) .. "," .. math.floor(PrimaryG*0.8) .. "," .. math.floor(PrimaryB*0.8) .. ") stroke-width='2' fill='none' />"
402 end
403 end
404 function DrawCursorLine()
405 local strokeColor = math.floor(utils.clamp((distance/(ResolutionWidth/4))*255,0,255))
406 content = content .. "<line x1='0' y1='0' x2='" .. simulatedX .. "px' y2='" .. simulatedY .. "px' style='stroke:rgb(" .. PrimaryR + strokeColor .. "," .. PrimaryG-strokeColor .. "," .. PrimaryB-strokeColor .. ");stroke-width:2;transform:translate(50%, 50%)' />"
407 end
408 function SetButtonContains()
409 BrakeButtonHovered = Contains(simulatedX + ResolutionWidth/2, simulatedY + ResolutionHeight/2, ButtonBrakeX, ButtonBrakeY, ButtonBrakeWidth, ButtonBrakeHeight)
410 ProgradeButtonHovered = Contains(simulatedX + ResolutionWidth/2, simulatedY + ResolutionHeight/2, ButtonProgradeX, ButtonProgradeY, ButtonProgradeWidth, ButtonProgradeHeight)
411 RetrogradeButtonHovered = Contains(simulatedX + ResolutionWidth/2, simulatedY + ResolutionHeight/2, ButtonRetrogradeX, ButtonRetrogradeY, ButtonRetrogradeWidth, ButtonRetrogradeHeight)
412 AutopilotButtonHovered = Contains(simulatedX + ResolutionWidth/2, simulatedY + ResolutionHeight/2, ButtonAutopilotX, ButtonAutopilotY, ButtonAutopilotWidth, ButtonAutopilotHeight)
413 end
414
415 function DrawButtons()
416 --local defaultColor = "rgb(" .. math.floor(PrimaryR*0.25+0.5) .. "," .. math.floor(PrimaryG*0.25+0.5) .. "," .. math.floor(PrimaryB*0.25+0.5) .. ")'"
417 local defaultColor = "rgb(" .. 0 .. "," .. 18 .. "," .. 133 .. ")'"
418 -- Brake button
419 content = content .. "<rect rx='5' ry='5' x='" .. ButtonBrakeX .. "' y='" .. ButtonBrakeY .. "' width='" .. ButtonBrakeWidth .. "' height='" .. ButtonBrakeHeight .. "' fill='"
420 if BrakeIsOn then
421 content = content .. "#CC0000'" -- Red if it's on
422 else
423 content = content .. defaultColor
424 end
425 if BrakeButtonHovered then
426 content = content .. " style='stroke:white; stroke-width:2;stroke-opacity:0.5;fill-opacity:1'"
427 else
428 content = content .. " style='stroke:black; stroke-width:1;stroke-opacity:0.5;fill-opacity:0.5'"
429 end
430
431 content = content .. "></rect>"
432
433 content = content .. "<text x='" .. ButtonBrakeX + ButtonBrakeWidth/2 .. "' y='" .. ButtonBrakeY + (ButtonBrakeHeight/2) + 5 .. "' font-size='24' fill='"
434 if BrakeIsOn then
435 content = content .. "black"
436 else
437 content = content .. "white"
438 end
439
440 content = content .. "' text-anchor='middle' font-family='Montserrat'>"
441
442 if BrakeIsOn then
443 content = content .. "Disengage Brake</text>"
444 else
445 content = content .. "Engage Brake</text>"
446 end
447
448
449 -- Prograde button
450 content = content .. "<rect rx='5' ry='5' x='" .. ButtonProgradeX .. "' y='" .. ButtonProgradeY .. "' width='" .. ButtonProgradeWidth .. "' height='" .. ButtonProgradeHeight .. "' fill='"
451 if ProgradeIsOn then
452 content = content .. "#FFEECC'" -- Orange if it's on
453 else
454 content = content .. defaultColor
455 end
456 if ProgradeButtonHovered then
457 content = content .. " style='stroke:white; stroke-width:2;stroke-opacity:0.5;fill-opacity:1'"
458 else
459 content = content .. " style='stroke:black; stroke-width:1;stroke-opacity:0.5;fill-opacity:0.5'"
460 end
461 content = content .. "></rect>"
462
463 content = content .. "<text x='" .. ButtonProgradeX + ButtonProgradeWidth/2 .. "' y='" .. ButtonProgradeY + (ButtonProgradeHeight/2) + 5 .. "' font-size='24' fill='"
464 if ProgradeIsOn then
465 content = content .. "black"
466 else
467 content = content .. "white"
468 end
469
470 content = content .. "' text-anchor='middle' font-family='Montserrat'>"
471
472 if ProgradeIsOn then
473 content = content .. "Disable Prograde</text>"
474 else
475 content = content .. "Align Prograde</text>"
476 end
477
478
479 -- Retrograde button
480 content = content .. "<rect rx='5' ry='5' x='" .. ButtonRetrogradeX .. "' y='" .. ButtonRetrogradeY .. "' width='" .. ButtonRetrogradeWidth .. "' height='" .. ButtonRetrogradeHeight .. "' fill='"
481 if RetrogradeIsOn then
482 content = content .. "#42006b'" -- Purple if it's on
483 else
484 content = content .. defaultColor
485 end
486 if RetrogradeButtonHovered then
487 content = content .. " style='stroke:white; stroke-width:2;stroke-opacity:0.5;fill-opacity:1'"
488 else
489 content = content .. " style='stroke:black; stroke-width:1;stroke-opacity:0.5;fill-opacity:0.5'"
490 end
491 content = content .. "></rect>"
492
493 content = content .. "<text x='" .. ButtonRetrogradeX + ButtonRetrogradeWidth/2 .. "' y='" .. ButtonRetrogradeY + (ButtonRetrogradeHeight/2) + 5 .. "' font-size='24' fill='"
494 if RetrogradeIsOn then
495 content = content .. "black"
496 else
497 content = content .. "white"
498 end
499
500
501 content = content .. "' text-anchor='middle' font-family='Montserrat'>"
502
503 if RetrogradeIsOn then
504 content = content .. "Disable Retrograde</text>"
505 else
506 content = content .. "Align Retrograde</text>"
507 end
508
509
510 -- Autopilot button
511 content = content .. "<rect rx='5' ry='5' x='" .. ButtonAutopilotX .. "' y='" .. ButtonAutopilotY .. "' width='" .. ButtonAutopilotWidth .. "' height='" .. ButtonAutopilotHeight .. "' fill='"
512 if Autopilot then
513 content = content .. "red'" -- Red if it's on
514 else
515 content = content .. defaultColor
516 end
517 if AutopilotButtonHovered then
518 content = content .. " style='stroke:white; stroke-width:2;stroke-opacity:0.5;fill-opacity:1'"
519 else
520 content = content .. " style='stroke:black; stroke-width:1;stroke-opacity:0.5;fill-opacity:0.5'"
521 end
522 content = content .. "></rect>"
523
524 content = content .. "<text x='" .. ButtonAutopilotX + ButtonAutopilotWidth/2 .. "' y='" .. ButtonAutopilotY + (ButtonAutopilotHeight/2) + 5 .. "' font-size='22' fill='"
525 if Autopilot then
526 content = content .. "black"
527 else
528 content = content .. "white"
529 end
530
531
532 content = content .. "' text-anchor='middle' font-family='Montserrat'>"
533
534 if Autopilot then
535 content = content .. "Disable Autopilot</text>"
536 else
537 content = content .. "Engage Autopilot: " .. AutopilotTargetName .. "</text>"
538 end
539 end
540 function HideInterplanetaryPanel()
541 system.destroyWidgetPanel(panelInterplanetary)
542 panelInterplanetary = nil
543 end
544 function ToggleAutoBrake()
545 if AutopilotTargetPlanetName ~= "None" and brakeInput == 0 and not AutoBrake then
546 AutoBrake = true
547 Autopilot = false
548 else
549 AutoBrake = false
550 end
551 end
552 function ToggleAutoPilot()
553 if AutopilotTargetPlanetName ~= "None" and brakeInput == 0 and unit.getAtmosphereDensity() == 0 and not Autopilot then
554 Autopilot = true
555 AutoBrake = false
556 else
557 Autopilot = false
558 end
559 end
560 function ToggleTurnBurn()
561 TurnBurn = not TurnBurn
562 end
563 system.showScreen(1)
564 function getRelativePitch(velocity)
565 velocity = vec3(velocity)
566 local pitch = -math.deg(math.atan(velocity.y, velocity.z)) + 180
567 -- This is 0-360 where 0 is straight up
568 pitch = pitch - 90
569 -- So now 0 is straight, but we can now get angles up to 420
570 if pitch < 0 then
571 pitch = 360 + pitch
572 end
573 -- Now, if it's greater than 180, say 190, make it go to like -170
574 if pitch > 180 then
575 pitch = -180 + (pitch-180)
576 end
577 -- And it's backwards.
578 return -pitch
579 end
580 function getRelativeYaw(velocity)
581 velocity = vec3(velocity)
582 return math.deg(math.atan(velocity.y, velocity.x)) - 90
583 end
584 function AlignToWorldVector(vector)
585 -- Sets inputs to attempt to point at the autopilot target
586 -- Meant to be called from Update or Tick repeatedly
587 vector = vec3(vector):normalize()
588 local targetVec = (vec3(core.getConstructWorldOrientationForward()) - vector)
589 local yawAmount = -getMagnitudeInDirection(targetVec, core.getConstructWorldOrientationRight()) * AutopilotStrength
590 local pitchAmount = -getMagnitudeInDirection(targetVec, core.getConstructWorldOrientationUp()) * AutopilotStrength
591
592 yawInput2 = yawInput2 - (yawAmount + (yawAmount - PreviousYawAmount) * DampingMultiplier)
593 pitchInput2 = pitchInput2 + (pitchAmount + (pitchAmount - PreviousPitchAmount) * DampingMultiplier)
594
595 PreviousYawAmount = yawAmount
596 PreviousPitchAmount = pitchAmount
597 --system.print(math.abs(vector:dot(vec3(core.getConstructWorldOrientationForward()))))
598 -- Return true or false depending on whether or not we're aligned
599 if math.abs(yawAmount) < alignmentTolerance and math.abs(pitchAmount) < alignmentTolerance then
600 return true
601 end
602 return false
603 end
604 -- HUD - https://github.com/Rezoix/DU-hud with major modifications by Archeageo
605 function updateHud()
606 local altitude = core.getAltitude()
607 local velocity = core.getVelocity()
608 local speed = vec3(velocity):len()
609 local worldV = vec3(core.getWorldVertical())
610 local constrF = vec3(core.getConstructWorldOrientationForward())
611 local constrR = vec3(core.getConstructWorldOrientationRight())
612 local constrV = vec3(core.getConstructWorldOrientationUp())
613 local pitch = getPitch(worldV, constrF, constrR)--180 - getRoll(worldV, constrR, constrF)
614 local roll = getRoll(worldV, constrF, constrR) --getRoll(worldV, constrF, constrR)
615 local originalRoll = roll
616 local originalPitch = math.floor(pitch)
617 local bottomText = "ROLL"
618 local grav = core.getWorldGravity()
619 local gravity = math.floor(vec3(grav):len()*100)/100
620 local atmos = unit.getAtmosphereDensity()
621 local throt = math.floor(unit.getThrottle())
622 local spd = speed*3.6
623 local flightValue = unit.getAxisCommandValue(0)
624 local flightType = Nav.axisCommandManager:getAxisCommandType(0)
625 local flightStyle = "TRAVEL"
626 local rgb = [[rgb(]] .. PrimaryR .. "," .. PrimaryG .. "," .. PrimaryB .. [[)]]
627 local rgbdim = [[rgb(]] .. math.floor(PrimaryR *0.9 + 0.5) .. "," .. math.floor(PrimaryG * 0.9 + 0.5) .. "," .. math.floor(PrimaryB * 0.9 + 0.5) .. [[)]]
628 local rgbdimmer = [[rgb(]] .. math.floor(PrimaryR *0.8 + 0.5) .. "," .. math.floor(PrimaryG * 0.8 + 0.5) .. "," .. math.floor(PrimaryB * 0.8 + 0.5) .. [[)]]
629 local rgbO = rgb
630 local rgbdimO = rgbdim
631 local rgbdimmerO = rgbdimmer
632 if system.isViewLocked() == 0 and userControlScheme ~= "Keyboard" then
633 rgb = [[rgb(]] .. math.floor(PrimaryR *0.5 + 0.5) .. "," .. math.floor(PrimaryG * 0.5 + 0.5) .. "," .. math.floor(PrimaryB * 0.5 + 0.5) .. [[)]]
634 rgbdim = [[rgb(]] .. math.floor(PrimaryR *0.4 + 0.5) .. "," .. math.floor(PrimaryG * 0.4 + 0.5) .. "," .. math.floor(PrimaryB * 0.4 + 0.5) .. [[)]]
635 rgbdimmer = [[rgb(]] .. math.floor(PrimaryR *0.3 + 0.5) .. "," .. math.floor(PrimaryG * 0.3 + 0.5) .. "," .. math.floor(PrimaryB * 0.3 + 0.5) .. [[)]]
636 end
637 if (flightType == 1) then
638 flightStyle = "CRUISE"
639
640 end
641 if Autopilot then
642 flightStyle = "AUTOPILOT"
643 end
644
645 if (atmos == 0) then
646 if (speed > 5) then
647 pitch = getRelativePitch(velocity)
648 roll = getRelativeYaw(velocity)
649 else
650 pitch = 0
651 roll = 0
652 end
653 bottomText = "YAW"
654 end
655
656 content = [[
657 <head>
658 <style>
659 body {margin: 0}
660 svg {display:block; position:absolute; top:0; left:0}
661 .majorLine {stroke:]] .. rgbO .. [[;opacity:0.7;stroke-width:3;fill-opacity:0;}
662 .minorLine {stroke:]] .. rgb .. [[;opacity:0.7;stroke-width:3;fill-opacity:0;}
663 .text {fill:]] .. rgbdimmer .. [[;font-family:Montserrat;font-weight:bold}
664 </style>
665 </head>
666 <body>
667 <svg height="100%" width="100%" viewBox="0 0 1920 1080">
668 <defs>
669 <filter id="shadow">
670 <feDropShadow dx="0.2" dy="0.4" stdDeviation="0.2"/>
671 </filter>
672 </defs>
673 <g class="majorLine">
674 <path d="M 700 0 L 740 35 Q 960 55 1180 35 L 1220 0"/>
675 </g>
676 <g class="minorLine">
677 <path d="M 792 550 L 785 550 L 785 650 L 792 650"/>
678 </g>
679 <g>
680 <polygon points="1138,540 1120,535 1120,545" style="fill:]] ..rgb.. [[;opacity:0.7"/>
681 </g>
682 <g class="text">
683 <g font-size=10>
684 <text x="960" y="375" text-anchor="middle" style="fill:]] .. rgbO .. [[">SPEED</text>
685 <text x="960" y="390" text-anchor="middle" style="fill:]] .. rgbO .. [[;font-size:14;">]]..math.floor(spd)..[[ km/h</text>
686 <text x="1200" y="710" text-anchor="end">GRAVITY</text>
687 <text x="1200" y="720" text-anchor="end">]]..gravity..[[ m/s2</text>
688 </g>
689 <g font-size=15>
690 <text x="960" y="33" text-anchor="middle" style="fill:]] .. rgbO .. [[">]]..flightStyle..[[</text>
691 </g>
692 </g>]]
693 if (flightStyle == "TRAVEL" or flightStyle == "AUTOPILOT") then
694 content = content..[[
695 <g class="text">
696 <g font-size=10>
697 <text x="790" y="660" text-anchor="start" style="fill:]] .. rgbO .. [[">THROT</text>
698 <text x="790" y="670" text-anchor="start" style="fill:]] .. rgbO .. [[">]]..throt..[[%</text>
699 </g>
700 </g>]]
701 else
702 content = content..[[
703 <g class="text">
704 <g font-size=10>
705 <text x="790" y="660" text-anchor="start" style="fill:]] .. rgbO .. [[">CRUISE</text>
706 <text x="790" y="670" text-anchor="start" style="fill:]] .. rgbO .. [[">]]..flightValue..[[ km/h</text>
707 </g>
708 </g>]]
709 end
710
711 local color, colorMod
712 local y1 = 740
713 local y2 = 750
714 if (atmofueltank_size > 0) then
715 for i = 1, atmofueltank_size do
716 fuelTimeLeft = json.decode(atmofueltank[i].getData()).timeLeft
717 fuelPercent = json.decode(atmofueltank[i].getData()).percentage
718 if fuelPercent ~= nil then
719 colorMod = math.floor(fuelPercent*2.55)
720 if (fuelTimeLeft ~= "n/a") then
721 fuelTimeLeft = math.floor(fuelTimeLeft / 60)
722 if (fuelTimeLeft > 1) then
723 color = [[rgb(]] .. 255-colorMod .. "," .. colorMod .. "," .. 0 .. [[)]]
724 else
725 color = [[rgb(]] .. 255 .. "," .. 0 .. "," .. 0 .. [[)]]
726 end
727 else
728 color = [[rgb(]] .. 255-colorMod .. "," .. colorMod .. "," .. 0 .. [[)]]
729 end
730 content = content..[[
731 <g class="text">
732 <g font-size=10>
733 <text x="770" y="]]..y1..[[" text-anchor="end" style="fill:]] .. color .. [[">ATMOFUEL ]]..i..[[</text>
734 <text x="770" y="]]..y2..[[" text-anchor="end" style="fill:]] .. color .. [[">]]..fuelPercent..[[% ]]..fuelTimeLeft..[[ min</text>
735 </g>
736 </g>]]
737 y1 = y1+20
738 y2 = y2+20
739 end
740 end
741 end
742 y1 = 740
743 y2 = 750
744 if (spacefueltank_size > 0) then
745 for i = 1, spacefueltank_size do
746 fuelTimeLeft = json.decode(spacefueltank[i].getData()).timeLeft
747 fuelPercent = json.decode(spacefueltank[i].getData()).percentage
748 if fuelPercent ~= nil then
749 colorMod = math.floor(fuelPercent*2.55)
750 if (fuelTimeLeft ~= "n/a") then
751 fuelTimeLeft = math.floor(fuelTimeLeft / 60)
752 if (fuelTimeLeft > 1) then
753 color = [[rgb(]] .. 255-colorMod .. "," .. colorMod .. "," .. 0 .. [[)]]
754 else
755 color = [[rgb(]] .. 255 .. "," .. 0 .. "," .. 0 .. [[)]]
756 end
757 else
758 color = [[rgb(]] .. 255-colorMod .. "," .. colorMod .. "," .. 0 .. [[)]]
759 end
760 content = content..[[
761 <g class="text">
762 <g font-size=10>
763 <text x="1200" y="]]..y1..[[" text-anchor="end" style="fill:]] .. color .. [[">SPACEFUEL ]]..i..[[</text>
764 <text x="1200" y="]]..y2..[[" text-anchor="end" style="fill:]] .. color .. [[">]]..fuelPercent..[[% ]]..fuelTimeLeft..[[ min</text>
765 </g>
766 </g>]]
767 y1 = y1+20
768 y2 = y2+20
769 end
770 end
771 end
772 speedC=math.floor(spd)
773 rollC = math.floor(roll)
774 pitchC = math.floor(pitch)
775 for i = pitchC-25,pitchC+25 do
776 if (i%10==0) then
777 num = i
778 if (num > 180) then
779 num = -180 + (num-180)
780 elseif (num < -180) then
781 num = 180 + (num+180)
782 end
783 content = content..[[<g transform="translate(0 ]]..(-i*5 + pitch*5 + 5)..[[)">
784 <text x="1180" y="540" style="fill:]] ..rgbdim.. [[;text-anchor:start;font-size:12;font-family:Montserrat;font-weight:bold">]]..num..[[</text></g>]]
785 end
786 if (i%10==0) then
787 len = 30
788 elseif (i%5==0) then
789 len = 20
790 else
791 len = 7
792 end
793 content = content..[[
794 <g transform="translate(0 ]]..(-i*5 + pitch*5)..[[)">
795 <line x1="]]..(1140+len)..[[" y1="540" x2="1140" y2="540"style="stroke:]] ..rgbdim.. [[;opacity:0.3;stroke-width:2"/></g>]]
796 end
797 content = content..[[
798 <g class="text">
799 <g font-size=10>
800 <text x="1180" y="380" text-anchor="end" style="fill:]] .. rgbO .. [[">PITCH</text>
801 <text x="1180" y="390" text-anchor="end" style="fill:]] .. rgbO .. [[">]]..pitchC..[[ deg</text>
802 </g>
803 </g>
804 ]]
805
806
807 --** CIRCLE ALTIMETER - Base Code from Discord @Rainsome = Youtube CaptainKilmar**
808 if circleRad > 0 and unit.getClosestPlanetInfluence() > 0 then
809 if originalPitch > 90 and atmos == 0 then
810 originalPitch = 90-(originalPitch-90)
811 elseif originalPitch < -90 and atmos == 0 then
812 originalPitch = -90 - (originalPitch+90)
813 end
814 --system.print(originalPitch)
815 content = content..[[<circle r="]]..circleRad..[[" cx="960" cy="540" opacity="0.1" fill="]] .."#0083cb".. [[" stroke="black" stroke-width="2"/><clipPath id="cut"><circle r="]]..(circleRad-1)..[[" cx="960" cy="540"/></clipPath>
816 <rect x="]]..(960-circleRad)..[[" y="]] ..(540 + circleRad*(originalPitch/90)).. [[" height="]]..(circleRad*2)..[[" width="]]..(circleRad*2)..[[" opacity="0.3" fill="]] .."#6b5835".. [[" clip-path="url(#cut)" transform="rotate(]] ..(-1*originalRoll).. [[ 960 540)"/>]]
817 end
818 content = content..[[
819 <g class="text">
820 <g font-size=10>
821 <text x="960" y="688" text-anchor="middle" style="fill:]] .. rgbO .. [[">]]..bottomText..[[</text>
822 <text x="960" y="698" text-anchor="middle" style="fill:]] .. rgbO .. [[">]]..math.floor(roll)..[[ deg</text>]]
823 content = content..[[<g>
824 <polygon points="960,725 955,707 965,707" style="fill:]] ..rgb.. [[;opacity:0.7"/>
825 </g>]]
826 for i = rollC-35,rollC+35 do
827 if (i%10==0) then
828 local sign = i/math.abs(i)
829 if i == 0 then
830 sign = 0
831 end
832 num = math.abs(i)
833 if (num > 180) then
834 num = 180 + (180-num)
835 end
836 content = content..[[<g transform="rotate(]]..(i - roll)..[[,960,460)">
837 <text x="960" y="760" style="fill:]] ..rgbdim.. [[;text-anchor:middle;font-size:12;font-family:Montserrat;font-weight:bold">]]..math.floor(sign*num+0.5)..[[</text></g>]]
838 end
839 len = 5
840 if (i%10==0) then
841 len = 15
842 elseif (i%5==0) then
843 len = 10
844 end
845 content = content..[[<g transform="rotate(]]..(i - roll)..[[,960,460)">
846 <line x1="960" y1="730" x2="960" y2="]]..(730+len)..[[" style="stroke:]] ..rgbdim ..[[;opacity:0.3;stroke-width:2"/></g>]]
847 end
848
849 if altitude > 0 then
850 content = content..[[
851 <g>
852 <polygon points="782,540 800,535 800,545" style="fill:]] ..rgb.. [[;opacity:0.7"/>
853 </g>
854 <g class="text">
855 <g font-size=10>
856 <text x="770" y="380" text-anchor="end">ALTITUDE</text>
857 <text x="770" y="390" text-anchor="end">]]..math.floor(altitude)..[[ m</text>
858 <text x="770" y="710" text-anchor="end">ATMOSPHERE</text>
859 <text x="770" y="720" text-anchor="end">]]..(math.floor((atmos)*100)/100)..[[ m</text>
860 </g>
861 </g>]]
862 altC = math.floor(altitude)
863 for i = altC-25,altC+25 do
864 if (i%10==0) then
865 num = i
866 if (num < 0) then
867 num = 0
868 end
869 content = content..[[<g transform="translate(0 ]]..(-i*5 + altitude*5)..[[)">
870 <text x="745" y="540" style="fill:]] ..rgbdim.. [[;text-anchor:end;font-size:12;font-family:Montserrat;font-weight:bold">]]..num..[[</text></g>]]
871 end
872 len = 5
873 if (i%10==0) then
874 len = 30
875 elseif (i%5==0) then
876 len = 15
877 end
878 content = content..[[
879 <g transform="translate(0 ]]..(-i*5 + altitude*5)..[[)">
880 <line x1="]]..(780-len)..[[" y1="540" x2="780" y2="540"style="stroke:]]..rgbdim..[[;opacity:0.3;stroke-width:2"/></g>]]
881 end
882 end
883 content = content..[[<g transform="translate(0 ]]..(1-throt)..[[)">
884 <polygon points="798,650 810,647 810,653" style="fill:]] ..rgbdim.. [[;opacity:0.7"/>
885 </g>]]
886
887 -- After the HUD, set RGB values back to undimmed even if view is unlocked
888 rgb = [[rgb(]] .. PrimaryR .. "," .. PrimaryG .. "," .. PrimaryB .. [[)]]
889 rgbdim = [[rgb(]] .. math.floor(PrimaryR *0.9 + 0.5) .. "," .. math.floor(PrimaryG * 0.9 + 0.5) .. "," .. math.floor(PrimaryB * 0.9 + 0.5) .. [[)]]
890 rgbdimmer = [[rgb(]] .. math.floor(PrimaryR *0.8 + 0.5) .. "," .. math.floor(PrimaryG * 0.8 + 0.5) .. "," .. math.floor(PrimaryB * 0.8 + 0.5) .. [[)]]
891
892 if unit.isMouseControlActivated() == 1 then
893 content = content .. "<text x='960' y='550' font-size='20' fill='red' text-anchor='middle' font-family='Montserrat'>Warning: Invalid Control Scheme Detected</text>"
894 content = content .. "<text x='960' y='600' font-size='20' fill='red' text-anchor='middle' font-family='Montserrat'>Keyboard Scheme must be selected</text>"
895 content = content .. "<text x='960' y='650' font-size='20' fill='red' text-anchor='middle' font-family='Montserrat'>Set your preferred scheme in Lua Parameters instead</text>"
896 end
897 if brakeInput == 1 then
898 content = content .. "<text x='960' y='860' font-size='20' fill='red' text-anchor='middle' font-family='Montserrat'>Brake Engaged</text>"
899 end
900 if gearExtended then
901 if hasGear then
902 content = content .. "<text x='960' y='900' font-size='20' fill='red' text-anchor='middle' font-family='Montserrat'>Gear Extended</text>"
903 else
904 content = content .. "<text x='960' y='900' font-size='20' fill='red' text-anchor='middle' font-family='Montserrat'>Landing Mode - Press G to Takeoff</text>"
905 end
906 content = content .. "<text x='960' y='930' font-size='20' fill='red' text-anchor='middle' font-family='Montserrat'>Hover Height: " .. getDistanceDisplayString(Nav:getTargetGroundAltitude()) .. "</text>"
907 end
908 if AutoBrake and AutopilotTargetPlanetName ~= "None" then
909 if brakeInput == 0 then
910 content = content .. "<text x='960' y='225' font-size='20' fill='orange' text-anchor='middle' font-family='Montserrat'>Auto-Braking when within " .. getDistanceDisplayString(maxBrakeDistance) .. " of " .. AutopilotTargetPlanet.name .. "</text>"
911 else
912 content = content .. "<text x='960' y='225' font-size='20' fill='orange' text-anchor='middle' font-family='Montserrat'>Auto-Braking until eccentricity:" .. round(orbit.eccentricity,2) .. " begins to increase</text>"
913 end
914 elseif Autopilot and AutopilotTargetPlanetName ~= "None" then
915 content = content .. "<text x='960' y='225' font-size='20' fill='orange' text-anchor='middle' font-family='Montserrat'>Autopilot Engaged - " .. AutopilotStatus .. "</text>"
916 end
917 if TurnBurn then
918 content = content .. "<text x='960' y='150' font-size='20' fill='darkred' text-anchor='middle' font-family='Montserrat'>Turn & Burn Braking</text>"
919 end
920 if orbit ~= nil and orbit.eccentricity < 1 and orbit.eccentricity > 0 and unit.getAtmosphereDensity() < 0.25 and planet ~= nil and orbit.period ~= nil then
921 -- If orbits are up, let's try drawing a mockup
922 -- We don't really care about scale.
923 -- First let's define an area to draw it
924 local orbitMapX = 75
925 local orbitMapY = 0
926 local orbitMapSize = 250 -- Always square
927 local pad = 4
928 orbitMapY = orbitMapY + pad
929
930 -- Draw a darkened box around it to keep it visible
931 content = content .. '<rect width="' .. orbitMapSize+orbitMapX*2 .. '" height="' .. orbitMapSize+orbitMapY .. '" rx="10" ry="10" x="' .. pad .. '" y="' .. pad .. '" style="fill:rgb(0,0,100);stroke-width:4;stroke:white;fill-opacity:0.3;" />'
932
933 -- Then, draw a 'planet' in the center
934 -- Now draw an ellipse, with both foci at the planet because we're assuming 0 mass ship
935 -- Where rx is apoapsis - periapsis (scaled), and ry is... related to eccentricity
936 -- Such that an eccentricity of 0 gives ry = rx, and eccentricity of 1+ gives ry = 0 (don't draw it)
937 -- So ry would be (1-eccentricity)*rx
938 -- And actually. To scale rx would to be to make it always equal to width.
939 local rx = orbitMapSize/4
940
941 -- And, the center of the thing should be shifted so that the apo is far and periapsis is near
942 -- We have arbitrarily chosen the AP to be to the right
943 -- So shift it right by (AP-PE)... scaled...
944 -- To figure out the scale, compare (AP + PE + radius*2) to rx
945 local scale = (orbit.apoapsis.altitude + orbit.periapsis.altitude + planet.radius*2)/(rx*2)
946 --local ry = (1-orbit.eccentricity)*rx
947 local ry = (planet.radius + orbit.periapsis.altitude + (orbit.apoapsis.altitude - orbit.periapsis.altitude)/2)/scale * (1-orbit.eccentricity)
948 -- Then if we divide our xOffset by this it should be good
949 --local xOffset = ((orbit.apoapsis.altitude - orbit.periapsis.altitude)/scale)
950 local xOffset = rx - orbit.periapsis.altitude/scale - planet.radius/scale
951
952 local ellipseColor = rgbdim
953 if orbit.periapsis.altitude <= 0 then
954 ellipseColor = 'red'
955 end
956 content = content .. '<ellipse cx="' .. orbitMapX + orbitMapSize/2 + xOffset + pad .. '" cy="' .. orbitMapY + orbitMapSize/2 + pad .. '" rx="' .. rx .. '" ry="' .. ry .. '" style="fill:none;stroke:' .. ellipseColor .. ';stroke-width:2" />'
957 content = content .. '<circle cx="' .. orbitMapX + orbitMapSize/2 + pad .. '" cy="' .. orbitMapY + orbitMapSize/2 + pad .. '" r="' .. planet.radius/scale .. '" stroke="white" stroke-width="3" fill="blue" />' -- I think the stroke-width is extending past the radius so -3? IDK though. Things just get weird on scales like these.
958 -- Mark the apoapsis and periapsis, AP is on the right
959
960 local orbitInfoYOffset = 15
961 local x = orbitMapX + orbitMapSize + orbitMapX/2 + pad
962 local y = orbitMapY + orbitMapSize/2 + 5 + pad
963
964 -- Draw guide lines on the ellipse somehow, from the edge of where the title should be
965 content = content .. [[<line x1="]].. x - 35 ..[[" y1="]] .. y-5 .. [[" x2="]] .. orbitMapX + orbitMapSize/2 + rx + xOffset .. [[" y2="]] .. y-5 .. [["style="stroke:]] .. rgbdim .. [[;opacity:0.3;stroke-width:3"/>]]
966
967 content = content .. "<text x='" .. x .. "' y='" .. y .. "' font-size='14' fill=" .. rgb .. " text-anchor='middle' font-family='Montserrat'>Apoapsis</text>"
968 y = y + orbitInfoYOffset
969 content = content .. "<text x='" .. x .. "' y='" .. y .. "' font-size='12' fill=" .. rgbdimmer .. " text-anchor='middle' font-family='Montserrat'>" .. getDistanceDisplayString(orbit.apoapsis.altitude) .. "</text>"
970 y = y + orbitInfoYOffset
971 content = content .. "<text x='" .. x .. "' y='" .. y .. "' font-size='12' fill=" .. rgbdimmer .. " text-anchor='middle' font-family='Montserrat'>" .. FormatTimeString(orbit.timeToApoapsis) .. "</text>"
972 y = y + orbitInfoYOffset
973 content = content .. "<text x='" .. x .. "' y='" .. y .. "' font-size='12' fill=" .. rgbdimmer .. " text-anchor='middle' font-family='Montserrat'>" .. getSpeedDisplayString(orbit.apoapsis.speed) .. "</text>"
974
975 y = orbitMapY + orbitMapSize/2 + 5 + pad
976 x = orbitMapX - orbitMapX/2+10 + pad
977
978 content = content .. [[<line x1="]].. x + 35 ..[[" y1="]] .. y-5 .. [[" x2="]] .. orbitMapX + orbitMapSize/2 - rx + xOffset .. [[" y2="]] .. y-5 .. [["style="stroke:]] .. rgbdim .. [[;opacity:0.3;stroke-width:3"/>]]
979
980 content = content .. "<text x='" .. x .. "' y='" .. y .. "' font-size='14' fill=" .. rgb .. " text-anchor='middle' font-family='Montserrat'>Periapsis</text>"
981
982 y = y + orbitInfoYOffset
983 content = content .. "<text x='" .. x .. "' y='" .. y .. "' font-size='12' fill=" .. rgbdimmer .. " text-anchor='middle' font-family='Montserrat'>" .. getDistanceDisplayString(orbit.periapsis.altitude) .. "</text>"
984 y = y + orbitInfoYOffset
985 content = content .. "<text x='" .. x .. "' y='" .. y .. "' font-size='12' fill=" .. rgbdimmer .. " text-anchor='middle' font-family='Montserrat'>" .. FormatTimeString(orbit.timeToPeriapsis) .. "</text>"
986 y = y + orbitInfoYOffset
987 content = content .. "<text x='" .. x .. "' y='" .. y .. "' font-size='12' fill=" .. rgbdimmer .. " text-anchor='middle' font-family='Montserrat'>" .. getSpeedDisplayString(orbit.periapsis.speed) .. "</text>"
988
989 -- Add a label for the planet
990 content = content .. "<text x='" .. orbitMapX + orbitMapSize/2 + pad .. "' y='" .. 20 + pad .. "' font-size='18' fill=" .. rgb .. " text-anchor='middle' font-family='Montserrat'>" .. planet.name .. "</text>"
991
992
993
994 -- And ... the hard part. Figure out how to mark the ship.
995 -- We could use the scale we calced and get the ship's distance from the planet center
996 -- And that would tell us a part of it.
997 -- If we take TimeToPeriapsis/Period, it should give us a value from 0 to 1 where 0.5 means we're at the apoapsis, 0 and 1 both mean basically at periapsis
998 -- So from 1 to 0.5 is the bottom half of the ellipse
999 -- And from 0.5 to 0 is the top half
1000 -- And then from 0.5 to 0, and 1 to 0.5, is the right half...
1001 -- Or, if ratio is more than 0.25 or less than 0.75
1002
1003 local apsisRatio = (orbit.timeToApoapsis/orbit.period) * 2 * math.pi
1004 -- So now that we have directions, we need to calc a distance from the center to the orbit
1005
1006 -- Or am I stupid.
1007 -- I have a number from 0 to 1 representing an angle. If I multiply by 2pi that's radians right?
1008
1009 -- Alright alright.
1010 -- x = xr * cos(t)
1011 -- y = yr * sin(t)
1012 -- Where t ranges from 0 to 2pi radians
1013 local shipX = rx * math.cos(apsisRatio)
1014 local shipY = ry * math.sin(apsisRatio)
1015
1016 content = content .. '<circle cx="' .. orbitMapX + orbitMapSize/2 + shipX + xOffset + pad .. '" cy="' .. orbitMapY + orbitMapSize/2 + shipY + pad .. '" r="5" stroke="white" stroke-width="3" fill="white" />'
1017
1018
1019 -- 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?
1020
1021 end
1022 content = content..[[</svg>]]
1023 --system.setScreen(content)
1024
1025 end
1026 function getPitch(gravityDirection, forward, right)
1027 local horizontalForward = gravityDirection:cross(right):normalize_inplace() -- Cross forward?
1028 local pitch = math.acos(utils.clamp(horizontalForward:dot(-forward), -1, 1)) * constants.rad2deg -- acos?
1029 if horizontalForward:cross(-forward):dot(right) < 0 then pitch = -pitch end -- Cross right dot forward?
1030 return pitch
1031 end
1032 -- Planet Info - https://gitlab.com/JayleBreak/dualuniverse/-/tree/master/DUflightfiles/autoconf/custom with minor modifications
1033 function Atlas()
1034 return {
1035 [0] = {
1036 [1]={
1037 GM=6930729684,
1038 bodyId=1,
1039 center={x=17465536.000,y=22665536.000,z=-34464.000},
1040 name='Madis',
1041 planetarySystemId=0,
1042 radius=44300
1043 },
1044 [2]={
1045 GM=157470826617,
1046 bodyId=2,
1047 center={x=-8.000,y=-8.000,z=-126303.000},
1048 name='Alioth',
1049 planetarySystemId=0,
1050 radius=126068
1051 },
1052 [3]={
1053 GM=11776905000,
1054 bodyId=3,
1055 center={x=29165536.000,y=10865536.000,z=65536.000},
1056 name='Thades',
1057 planetarySystemId=0,
1058 radius=49000
1059 },
1060 [4]={
1061 GM=14893847582,
1062 bodyId=4,
1063 center={x=-13234464.000,y=55765536.000,z=465536.000},
1064 name='Talemai',
1065 planetarySystemId=0,
1066 radius=57450
1067 },
1068 [5]={
1069 GM=16951680000,
1070 bodyId=5,
1071 center={x=-43534464.000,y=22565536.000,z=-48934464.000},
1072 name='Feli',
1073 planetarySystemId=0,
1074 radius=60000
1075 },
1076 [6]={
1077 GM=10502547741,
1078 bodyId=6,
1079 center={x=52765536.000,y=27165538.000,z=52065535.000},
1080 name='Sicari',
1081 planetarySystemId=0,
1082 radius=51100
1083 },
1084 [7]={
1085 GM=13033380591,
1086 bodyId=7,
1087 center={x=58665538.000,y=29665535.000,z=58165535.000},
1088 name='Sinnen',
1089 planetarySystemId=0,
1090 radius=54950
1091 },
1092 [8]={
1093 GM=18477723600,
1094 bodyId=8,
1095 center={x=80865538.000,y=54665536.000,z=-934463.940},
1096 name='Teoma',
1097 planetarySystemId=0,
1098 radius=62000
1099 },
1100 [9]={
1101 GM=18606274330,
1102 bodyId=9,
1103 center={x=-94134462.000,y=12765534.000,z=-3634464.000},
1104 name='Jago',
1105 planetarySystemId=0,
1106 radius=61590
1107 },
1108 [10]={
1109 GM=78480000,
1110 bodyId=10,
1111 center={x=17448118.224,y=22966846.286,z=143078.820},
1112 name='Madis Moon 1',
1113 planetarySystemId=0,
1114 radius=10000
1115 },
1116 [11]={
1117 GM=237402000,
1118 bodyId=11,
1119 center={x=17194626.000,y=22243633.880,z=-214962.810},
1120 name='Madis Moon 2',
1121 planetarySystemId=0,
1122 radius=11000
1123 },
1124 [12]={
1125 GM=265046609,
1126 bodyId=12,
1127 center={x=17520614.000,y=22184730.000,z=-309989.990},
1128 name='Madis Moon 3',
1129 planetarySystemId=0,
1130 radius=15005
1131 },
1132 [21]={
1133 GM=2118960000,
1134 bodyId=21,
1135 center={x=457933.000,y=-1509011.000,z=115524.000},
1136 name='Alioth Moon 1',
1137 planetarySystemId=0,
1138 radius=30000
1139 },
1140 [22]={
1141 GM=2165833514,
1142 bodyId=22,
1143 center={x=-1692694.000,y=729681.000,z=-411464.000},
1144 name='Alioth Moon 4',
1145 planetarySystemId=0,
1146 radius=30330
1147 },
1148 [26]={
1149 GM=68234043600,
1150 bodyId=26,
1151 center={x=-1404835.000,y=562655.000,z=-285074.000},
1152 name='Sanctuary',
1153 planetarySystemId=0,
1154 radius=83400
1155 },
1156 [30]={
1157 GM=211564034,
1158 bodyId=30,
1159 center={x=29214402.000,y=10907080.695,z=433858.200},
1160 name='Thades Moon 1',
1161 planetarySystemId=0,
1162 radius=14002
1163 },
1164 [31]={
1165 GM=264870000,
1166 bodyId=31,
1167 center={x=29404193.000,y=10432768.000,z=19554.131},
1168 name='Thades Moon 2',
1169 planetarySystemId=0,
1170 radius=15000
1171 },
1172 [40]={
1173 GM=141264000,
1174 bodyId=40,
1175 center={x=-13503090.000,y=55594325.000,z=769838.640},
1176 name='Talemai Moon 2',
1177 planetarySystemId=0,
1178 radius=12000
1179 },
1180 [41]={
1181 GM=106830900,
1182 bodyId=41,
1183 center={x=-12800515.000,y=55700259.000,z=325207.840},
1184 name='Talemai Moon 3',
1185 planetarySystemId=0,
1186 radius=11000
1187 },
1188 [42]={
1189 GM=264870000,
1190 bodyId=42,
1191 center={x=-13058408.000,y=55781856.000,z=740177.760},
1192 name='Talemai Moon 1',
1193 planetarySystemId=0,
1194 radius=15000
1195 },
1196 [50]={
1197 GM=499917600,
1198 bodyId=50,
1199 center={x=-43902841.780,y=22261034.700,z=-48862386.000},
1200 name='Feli Moon 1',
1201 planetarySystemId=0,
1202 radius=14000
1203 },
1204 [70]={
1205 GM=396912600,
1206 bodyId=70,
1207 center={x=58969616.000,y=29797945.000,z=57969449.000},
1208 name='Sinnen Moon 1',
1209 planetarySystemId=0,
1210 radius=17000
1211 },
1212 [100]={
1213 GM=13975172474,
1214 bodyId=100,
1215 center={x=98865536.000,y=-13534464.000,z=-934461.990},
1216 name='Lacobus',
1217 planetarySystemId=0,
1218 radius=55650
1219 },
1220 [101]={
1221 GM=264870000,
1222 bodyId=101,
1223 center={x=98905288.170,y=-13950921.100,z=-647589.530},
1224 name='Lacobus Moon 3',
1225 planetarySystemId=0,
1226 radius=15000
1227 },
1228 [102]={
1229 GM=444981600,
1230 bodyId=102,
1231 center={x=99180968.000,y=-13783862.000,z=-926156.400},
1232 name='Lacobus Moon 1',
1233 planetarySystemId=0,
1234 radius=18000
1235 },
1236 [103]={
1237 GM=211503600,
1238 bodyId=103,
1239 center={x=99250052.000,y=-13629215.000,z=-1059341.400},
1240 name='Lacobus Moon 2',
1241 planetarySystemId=0,
1242 radius=14000
1243 },
1244 [110]={
1245 GM=9204742375,
1246 bodyId=110,
1247 center={x=14165536.000,y=-85634465.000,z=-934464.300},
1248 name='Symeon',
1249 planetarySystemId=0,
1250 radius=49050
1251 },
1252 [120]={
1253 GM=7135606629,
1254 bodyId=120,
1255 center={x=2865536.700,y=-99034464.000,z=-934462.020},
1256 name='Ion',
1257 planetarySystemId=0,
1258 radius=44950
1259 },
1260 [121]={
1261 GM=106830900,
1262 bodyId=121,
1263 center={x=2472916.800,y=-99133747.000,z=-1133582.800},
1264 name='Ion Moon 1',
1265 planetarySystemId=0,
1266 radius=11000
1267 },
1268 [122]={
1269 GM=176580000,
1270 bodyId=122,
1271 center={x=2995424.500,y=-99275010.000,z=-1378480.700},
1272 name='Ion Moon 2',
1273 planetarySystemId=0,
1274 radius=15000
1275 }
1276 }
1277 }
1278 end
1279 function PlanetRef()
1280 --[[
1281 Provide coordinate transforms and access to kinematic related parameters
1282 Author: JayleBreak
1283 Usage (unit.start):
1284 PlanetaryReference = require('planetref')
1285 galaxyReference = PlanetaryReference(referenceTableSource)
1286 helios = galaxyReference[0] -- PlanetaryReference.PlanetarySystem instance
1287 alioth = helios[2] -- PlanetaryReference.BodyParameters instance
1288 Methods:
1289 PlanetaryReference:getPlanetarySystem - based on planetary system ID.
1290 PlanetaryReference.isMapPosition - 'true' if an instance of 'MapPosition'
1291 PlanetaryReference.createBodyParameters - for entry into reference table
1292 PlanetaryReference.BodyParameters - a class containing a body's information.
1293 PlanetaryReference.MapPosition - a class for map coordinates
1294 PlanetaryReference.PlanetarySystem - a container for planetary system info.
1295 PlanetarySystem:castIntersections - from a position in a given direction.
1296 PlanetarySystem:closestBody - to the specified coordinates.
1297 PlanetarySystem:convertToBodyIdAndWorldCoordinates - from map coordinates.
1298 PlanetarySystem:getBodyParameters - from reference table.
1299 PlanetarySystem:getPlanetarySystemId - for the instance.
1300 BodyParameters:convertToWorldCoordinates - from map coordinates
1301 BodyParameters:convertToMapPosition - from world coordinates
1302 BodyParameters:getAltitude - of world coordinates
1303 BodyParameters:getDistance - from center to world coordinates
1304 BodyParameters:getGravity - at a given position in world coordinates.
1305 Description
1306 An instance of the 'PlanetaryReference' "class" can contain transform and
1307 kinematic reference information for all planetary systems in DualUniverse.
1308 Each planetary system is identified by a numeric identifier. Currently,
1309 the only planetary system, Helios, has the identifier: zero. This "class"
1310 supports the indexing ('[]') operation which is equivalent to the
1311 use of the 'getPlanetarySystem' method. It also supports the 'pairs()'
1312 method for iterating over planetary systems.
1313
1314 An instance of the 'PlanetarySystem' "class" contains all reference
1315 information for a specific system. It supports the indexing ('[]') and
1316 'pairs()' functions which allows iteration over each "body" in the
1317 system where the key is the numeric body ID. It also supports the
1318 'tostring()' method.
1319 An instance of the 'BodyParameters' "class" contains all reference
1320 information for a single celestial "body" (a moon or planet). It supports
1321 the 'tostring()' method, and contains the data members:
1322 planetarySystemId - numeric planetary system ID
1323 bodyId - numeric body ID
1324 radius - radius of the body in meters (zero altitude)
1325 center - world coordinates of the body's center position
1326 GM - the gravitation parameter (g = GM/radius^2)
1327 Note that the user is allowed to add custom fields (e.g. body name), but
1328 should insure that complex table values have the '__tostring' metamethod
1329 implemented.
1330 Transform and Kinematics:
1331 "World" coordinates is a cartesian coordinate system with an origin at an
1332 arbitrary fixed point in a planetary system and with distances measured in
1333 meters. The coordinates are expressible either as a simple table of 3 values
1334 or an instance of the 'vec3' class. In either case, the planetary system
1335 identity is implicit.
1336 "Map" coordinates is a geographic coordinate system with an origin at the
1337 center of an identified (by a numeric value) celestial body which is a
1338 member of an identified (also a numeric value) planetary system. Note that
1339 the convention that latitude, longitude, and altitude values will be the
1340 position's x, y, and z world coordinates in the special case of body ID 0.
1341 The kinematic parameters in the reference data permit calculations of the
1342 gravitational attraction of the celestial body on other objects.
1343 Reference Data:
1344 This is an example of reference data with a single entry assigned to
1345 planetary system ID 0, and body ID 2 ('Alioth'):
1346 referenceTable = {
1347 [0] = { [2] = { planetarySystemId = 0,
1348 bodyId = 2,
1349 radius = 126068,
1350 center = vec3({x=-8, y=-8, z=-126303}),
1351 GM = 1.572199+11 } -- as in F=-GMm/r^2
1352 }
1353 }
1354 ref=PlanetaryReference(referenceTable)
1355 Collecting Reference Data:
1356 A combination of information from the "Map" screen in the DU user interface,
1357 and values reported by the DU Lua API can be the source of the reference
1358 table's data (planetarySystemId, bodyId, and surfaceArea is from the user
1359 interface):
1360 referenceTable = {}
1361 referenceTable[planetarySystemId][bodyId] =
1362 PlanetaryReference.createBodyParameters(planetarySystemId,
1363 bodyId,
1364 surfaceArea,
1365 core.getConstructWorldPos(),
1366 core.getWorldVertical(),
1367 core.getAltitude(),
1368 core.g())
1369 Adapting Data Sources:
1370 Other sources of data can be adapted or converted. An example of adapting a
1371 table, defined in the file: 'planets.lua', containing information on a single
1372 planetary system and using celestial body name as the key follows (note that
1373 a 'name' field is added to the BodyParameters instance transparently after
1374 construction, and the '__pairs' meta function is required to support the
1375 'closestBody' and '__tostring' methods):
1376 ref=PlanetaryReference(
1377 {[0] = setmetatable(require('planets'),
1378 { __index = function(bodies, bodyId)
1379 for _,v in pairs(bodies) do
1380 if v and v.bodyId == bodyId then return v end
1381 end
1382 return nil
1383 end,
1384 __pairs = function(bodies)
1385 return function(t, k)
1386 local nk, nv = next(t, k)
1387 if nv then
1388 local GM = nv.gravity * nv.radius^2
1389 local bp = BodyParameters(0,
1390 nv.id,
1391 nv.radius,
1392 nv.pos,
1393 GM)
1394 bp.name = nk
1395 return nk, bp
1396 end
1397 return nk, nv
1398 end, bodies, nil
1399 end })
1400 })
1401
1402 Converting Data Sources:
1403 An instance of 'PlanetaryReference' that has been adapted to a data source
1404 can be used to convert that source to simple table. For example,
1405 using the adapted instance shown above:
1406 load('convertedData=' .. tostring(ref))()
1407 newRef=PlanetaryReference(convertedData)
1408 Also See: kepler.lua
1409 ]]--
1410 --[[ START OF LOCAL IMPLEMENTATION DETAILS ]]--
1411 -- Type checks
1412 local function isNumber(n) return type(n) == 'number' end
1413 local function isSNumber(n) return type(tonumber(n)) == 'number' end
1414 local function isTable(t) return type(t) == 'table' end
1415 local function isString(s) return type(s) == 'string' end
1416 local function isVector(v) return isTable(v)
1417 and isNumber(v.x and v.y and v.z) end
1418 local function isMapPosition(m) return isTable(m) and isNumber(m.latitude and
1419 m.longitude and
1420 m.altitude and
1421 m.bodyId and
1422 m.systemId) end
1423 -- Constants
1424 local deg2rad = math.pi/180
1425 local rad2deg = 180/math.pi
1426 local epsilon = 1e-10
1427 local num = ' *([+-]?%d+%.?%d*e?[+-]?%d*)'
1428 local posPattern = '::pos{' .. num .. ',' .. num .. ',' .. num .. ',' ..
1429 num .. ',' .. num .. '}'
1430 -- Utilities
1431 local utils = require('cpml.utils')
1432 local vec3 = require('cpml.vec3')
1433 local clamp = utils.clamp
1434 local function float_eq(a,b)
1435 if a == 0 then return math.abs(b) < 1e-09 end
1436 if b == 0 then return math.abs(a) < 1e-09 end
1437 return math.abs(a - b) < math.max(math.abs(a),math.abs(b))*epsilon
1438 end
1439 local function formatNumber(n)
1440 local result = string.gsub(
1441 string.reverse(string.format('%.4f',n)),
1442 '^0*%.?','')
1443 return result == '' and '0' or string.reverse(result)
1444 end
1445 local function formatValue(obj)
1446 if isVector(obj) then
1447 return string.format('{x=%.3f,y=%.3f,z=%.3f}', obj.x, obj.y, obj.z)
1448 end
1449 if isTable(obj) and not getmetatable(obj) then
1450 local list = {}
1451 local nxt = next(obj)
1452 if type(nxt) == 'nil' or nxt == 1 then -- assume this is an array
1453 list = obj
1454 else
1455 for k,v in pairs(obj) do
1456 local value = formatValue(v)
1457 if type(k) == 'number' then
1458 table.insert(list, string.format('[%s]=%s', k, value))
1459 else
1460 table.insert(list, string.format('%s=%s', k, value))
1461 end
1462 end
1463 end
1464 return string.format('{%s}', table.concat(list, ','))
1465 end
1466 if isString(obj) then
1467 return string.format("'%s'", obj:gsub("'",[[\']]))
1468 end
1469 return tostring(obj)
1470 end
1471 -- CLASSES
1472 -- BodyParameters: Attributes of planetary bodies (planets and moons)
1473 local BodyParameters = {}
1474 BodyParameters.__index = BodyParameters
1475 BodyParameters.__tostring =
1476 function(obj, indent)
1477 local sep = indent or ''
1478 local keys = {}
1479 for k in pairs(obj) do table.insert(keys, k) end
1480 table.sort(keys)
1481 local list = {}
1482 for _, k in ipairs(keys) do
1483 local value = formatValue(obj[k])
1484 if type(k) == 'number' then
1485 table.insert(list, string.format('[%s]=%s', k, value))
1486 else
1487 table.insert(list, string.format('%s=%s', k, value))
1488 end
1489 end
1490 if indent then
1491 return string.format('%s%s',
1492 indent,
1493 table.concat(list, ',\n' .. indent))
1494 end
1495 return string.format('{%s}', table.concat(list, ','))
1496 end
1497 BodyParameters.__eq = function(lhs, rhs)
1498 return lhs.planetarySystemId == rhs.planetarySystemId and
1499 lhs.bodyId == rhs.bodyId and
1500 float_eq(lhs.radius, rhs.radius) and
1501 float_eq(lhs.center.x, rhs.center.x) and
1502 float_eq(lhs.center.y, rhs.center.y) and
1503 float_eq(lhs.center.z, rhs.center.z) and
1504 float_eq(lhs.GM, rhs.GM)
1505 end
1506 local function mkBodyParameters(systemId, bodyId, radius, worldCoordinates, GM)
1507 -- 'worldCoordinates' can be either table or vec3
1508 assert(isSNumber(systemId),
1509 'Argument 1 (planetarySystemId) must be a number:' .. type(systemId))
1510 assert(isSNumber(bodyId),
1511 'Argument 2 (bodyId) must be a number:' .. type(bodyId))
1512 assert(isSNumber(radius),
1513 'Argument 3 (radius) must be a number:' .. type(radius))
1514 assert(isTable(worldCoordinates),
1515 'Argument 4 (worldCoordinates) must be a array or vec3.' ..
1516 type(worldCoordinates))
1517 assert(isSNumber(GM),
1518 'Argument 5 (GM) must be a number:' .. type(GM))
1519 return setmetatable({planetarySystemId = tonumber(systemId),
1520 bodyId = tonumber(bodyId),
1521 radius = tonumber(radius),
1522 center = vec3(worldCoordinates),
1523 GM = tonumber(GM) }, BodyParameters)
1524 end
1525 -- MapPosition: Geographical coordinates of a point on a planetary body.
1526 local MapPosition = {}
1527 MapPosition.__index = MapPosition
1528 MapPosition.__tostring = function(p)
1529 return string.format('::pos{%d,%d,%s,%s,%s}',
1530 p.systemId,
1531 p.bodyId,
1532 formatNumber(p.latitude*rad2deg),
1533 formatNumber(p.longitude*rad2deg),
1534 formatNumber(p.altitude))
1535 end
1536 MapPosition.__eq = function(lhs, rhs)
1537 return lhs.bodyId == rhs.bodyId and
1538 lhs.systemId == rhs.systemId and
1539 float_eq(lhs.latitude, rhs.latitude) and
1540 float_eq(lhs.altitude, rhs.altitude) and
1541 (float_eq(lhs.longitude, rhs.longitude) or
1542 float_eq(lhs.latitude, math.pi/2) or
1543 float_eq(lhs.latitude, -math.pi/2))
1544 end
1545 -- latitude and longitude are in degrees while altitude is in meters
1546 local function mkMapPosition(overload, bodyId, latitude, longitude, altitude)
1547 local systemId = overload -- Id or '::pos{...}' string
1548 if isString(overload) and not longitude and not altitude and
1549 not bodyId and not latitude then
1550 systemId, bodyId, latitude, longitude, altitude =
1551 string.match(overload, posPattern)
1552 assert(systemId, 'Argument 1 (position string) is malformed.')
1553 else
1554 assert(isSNumber(systemId),
1555 'Argument 1 (systemId) must be a number:' .. type(systemId))
1556 assert(isSNumber(bodyId),
1557 'Argument 2 (bodyId) must be a number:' .. type(bodyId))
1558 assert(isSNumber(latitude),
1559 'Argument 3 (latitude) must be in degrees:' .. type(latitude))
1560 assert(isSNumber(longitude),
1561 'Argument 4 (longitude) must be in degrees:' .. type(longitude))
1562 assert(isSNumber(altitude),
1563 'Argument 5 (altitude) must be in meters:' .. type(altitude))
1564 end
1565 systemId = tonumber(systemId)
1566 bodyId = tonumber(bodyId)
1567 latitude = tonumber(latitude)
1568 longitude = tonumber(longitude)
1569 altitude = tonumber(altitude)
1570 if bodyId == 0 then -- this is a hack to represent points in space
1571 return setmetatable({latitude = latitude,
1572 longitude = longitude,
1573 altitude = altitude,
1574 bodyId = bodyId,
1575 systemId = systemId}, MapPosition)
1576 end
1577 return setmetatable({latitude = deg2rad*clamp(latitude, -90, 90),
1578 longitude = deg2rad*(longitude % 360),
1579 altitude = altitude,
1580 bodyId = bodyId,
1581 systemId = systemId}, MapPosition)
1582 end
1583 -- PlanetarySystem - map body IDs to BodyParameters
1584 local PlanetarySystem = {}
1585 PlanetarySystem.__index = PlanetarySystem
1586 PlanetarySystem.__tostring =
1587 function (obj, indent)
1588 local sep = indent and (indent .. ' ' )
1589 local bdylist = {}
1590 local keys = {}
1591 for k in pairs(obj) do table.insert(keys, k) end
1592 table.sort(keys)
1593 for _, bi in ipairs(keys) do
1594 bdy = obj[bi]
1595 local bdys = BodyParameters.__tostring(bdy, sep)
1596 if indent then
1597 table.insert(bdylist,
1598 string.format('[%s]={\n%s\n%s}',
1599 bi, bdys, indent))
1600 else
1601 table.insert(bdylist, string.format(' [%s]=%s', bi, bdys))
1602 end
1603 end
1604 if indent then
1605 return string.format('\n%s%s%s',
1606 indent,
1607 table.concat(bdylist, ',\n' .. indent),
1608 indent)
1609 end
1610 return string.format('{\n%s\n}', table.concat(bdylist, ',\n'))
1611 end
1612 local function mkPlanetarySystem(referenceTable)
1613 local atlas = {}
1614 local pid
1615 for _, v in pairs(referenceTable) do
1616 local id = v.planetarySystemId
1617 if type(id) ~= 'number' then
1618 error('Invalid planetary system ID: ' .. tostring(id))
1619 elseif pid and id ~= pid then
1620 error('Mismatch planetary system IDs: ' .. id .. ' and '
1621 .. pid)
1622 end
1623 local bid = v.bodyId
1624 if type(bid) ~= 'number' then
1625 error('Invalid body ID: ' .. tostring(bid))
1626 elseif atlas[bid] then
1627 error('Duplicate body ID: ' .. tostring(bid))
1628 end
1629 setmetatable(v.center, getmetatable(vec3.unit_x))
1630 atlas[bid] = setmetatable(v, BodyParameters)
1631 pid = id
1632 end
1633 return setmetatable(atlas, PlanetarySystem)
1634 end
1635 -- PlanetaryReference - map planetary system ID to PlanetarySystem
1636 PlanetaryReference = {}
1637 local function mkPlanetaryReference(referenceTable)
1638 return setmetatable({ galaxyAtlas = referenceTable or {} },
1639 PlanetaryReference)
1640 end
1641 PlanetaryReference.__index =
1642 function(t,i)
1643 if type(i) == 'number' then
1644 local system = t.galaxyAtlas[i]
1645 return mkPlanetarySystem(system)
1646 end
1647 return rawget(PlanetaryReference, i)
1648 end
1649 PlanetaryReference.__pairs =
1650 function(obj)
1651 return function(t, k)
1652 local nk, nv = next(t, k)
1653 return nk, nv and mkPlanetarySystem(nv)
1654 end, obj.galaxyAtlas, nil
1655 end
1656 PlanetaryReference.__tostring =
1657 function (obj)
1658 local pslist = {}
1659 for _,ps in pairs(obj or {}) do
1660 local psi = ps:getPlanetarySystemId()
1661 local pss = PlanetarySystem.__tostring(ps, ' ')
1662 table.insert(pslist,
1663 string.format(' [%s]={%s\n }', psi, pss))
1664 end
1665 return string.format('{\n%s\n}\n', table.concat(pslist,',\n'))
1666 end
1667 --[[ START OF PUBLIC INTERFACE ]]--
1668 -- PlanetaryReference CLASS METHODS:
1669 --
1670 -- BodyParameters - create an instance of BodyParameters class
1671 -- planetarySystemId [in]: the body's planetary system ID.
1672 -- bodyId [in]: the body's ID.
1673 -- radius [in]: the radius in meters of the planetary body.
1674 -- bodyCenter [in]: the world coordinates of the center (vec3 or table).
1675 -- GM [in]: the body's standard gravitational parameter.
1676 -- return: an instance of BodyParameters class.
1677 --
1678 PlanetaryReference.BodyParameters = mkBodyParameters
1679 --
1680 -- MapPosition - create an instance of the MapPosition class
1681 -- overload [in]: either a planetary system ID or a position string ('::pos...')
1682 -- bodyId [in]: (ignored if overload is a position string) the body's ID.
1683 -- latitude [in]: (ignored if overload is a position string) the latitude.
1684 -- longitude [in]:(ignored if overload is a position string) the longitude.
1685 -- altitude [in]: (ignored if overload is a position string) the altitude.
1686 -- return: the class instance
1687 --
1688 PlanetaryReference.MapPosition = mkMapPosition
1689 --
1690 -- PlanetarySystem - create an instance of PlanetarySystem class
1691 -- referenceData [in]: a table (indexed by bodyId) of body reference info.
1692 -- return: the class instance
1693 --
1694 PlanetaryReference.PlanetarySystem = mkPlanetarySystem
1695 --
1696 -- createBodyParameters - create an instance of BodyParameters class
1697 -- planetarySystemId [in]: the body's planetary system ID.
1698 -- bodyId [in]: the body's ID.
1699 -- surfaceArea [in]: the body's surface area in square meters.
1700 -- aPosition [in]: world coordinates of a position near the body.
1701 -- verticalAtPosition [in]: a vector pointing towards the body center.
1702 -- altitudeAtPosition [in]: the altitude in meters at the position.
1703 -- gravityAtPosition [in]: the magnitude of the gravitational acceleration.
1704 -- return: an instance of BodyParameters class.
1705 --
1706 function PlanetaryReference.createBodyParameters(planetarySystemId,
1707 bodyId,
1708 surfaceArea,
1709 aPosition,
1710 verticalAtPosition,
1711 altitudeAtPosition,
1712 gravityAtPosition)
1713 assert(isSNumber(planetarySystemId),
1714 'Argument 1 (planetarySystemId) must be a number:' ..
1715 type(planetarySystemId))
1716 assert(isSNumber(bodyId),
1717 'Argument 2 (bodyId) must be a number:' .. type(bodyId))
1718 assert(isSNumber(surfaceArea),
1719 'Argument 3 (surfaceArea) must be a number:' .. type(surfaceArea))
1720 assert(isTable(aPosition),
1721 'Argument 4 (aPosition) must be an array or vec3:' ..
1722 type(aPosition))
1723 assert(isTable(verticalAtPosition),
1724 'Argument 5 (verticalAtPosition) must be an array or vec3:' ..
1725 type(verticalAtPosition))
1726 assert(isSNumber(altitudeAtPosition),
1727 'Argument 6 (altitude) must be in meters:' ..
1728 type(altitudeAtPosition))
1729 assert(isSNumber(gravityAtPosition),
1730 'Argument 7 (gravityAtPosition) must be number:' ..
1731 type(gravityAtPosition))
1732 local radius = math.sqrt(surfaceArea/4/math.pi)
1733 local distance = radius + altitudeAtPosition
1734 local center = vec3(aPosition) + distance*vec3(verticalAtPosition)
1735 local GM = gravityAtPosition * distance * distance
1736 return mkBodyParameters(planetarySystemId, bodyId, radius, center, GM)
1737 end
1738 --
1739 -- isMapPosition - check for the presence of the 'MapPosition' fields
1740 -- valueToTest [in]: the value to be checked
1741 -- return: 'true' if all required fields are present in the input value
1742 --
1743 PlanetaryReference.isMapPosition = isMapPosition
1744 -- PlanetaryReference INSTANCE METHODS:
1745 --
1746 -- getPlanetarySystem - get the planetary system using ID or MapPosition as key
1747 -- overload [in]: either the planetary system ID or a MapPosition that has it.
1748 -- return: instance of 'PlanetarySystem' class or nil on error
1749 --
1750 function PlanetaryReference:getPlanetarySystem(overload)
1751 --if galaxyAtlas then
1752 local planetarySystemId = overload
1753 if isMapPosition(overload) then
1754 planetarySystemId = overload.systemId
1755 end
1756 if type(planetarySystemId) == 'number' then
1757 local system = self.galaxyAtlas[i]
1758 if system then
1759 if getmetatable(nv) ~= PlanetarySystem then
1760 system = mkPlanetarySystem(system)
1761 end
1762 return system
1763 end
1764 end
1765 --end
1766 --return nil
1767 end
1768 -- PlanetarySystem INSTANCE METHODS:
1769 --
1770 -- castIntersections - Find the closest body that intersects a "ray cast".
1771 -- origin [in]: the origin of the "ray cast" in world coordinates
1772 -- direction [in]: the direction of the "ray cast" as a 'vec3' instance.
1773 -- sizeCalculator [in]: (default: returns 1.05*radius) Returns size given body.
1774 -- bodyIds[in]: (default: all IDs in system) check only the given IDs.
1775 -- return: The closest body that blocks the cast or 'nil' if none.
1776 --
1777 function PlanetarySystem:castIntersections(origin,
1778 direction,
1779 sizeCalculator,
1780 bodyIds)
1781 local sizeCalculator = sizeCalculator or
1782 function (body) return 1.05*body.radius end
1783 local candidates = {}
1784 if bodyIds then
1785 for _,i in ipairs(bodyIds) do candidates[i] = self[i] end
1786 else
1787 bodyIds = {}
1788 for k,body in pairs(self) do
1789 table.insert(bodyIds, k)
1790 candidates[k] = body
1791 end
1792 end
1793 local function compare(b1,b2)
1794 local v1 = candidates[b1].center - origin
1795 local v2 = candidates[b2].center - origin
1796 return v1:len() < v2:len()
1797 end
1798 table.sort(bodyIds, compare)
1799 local dir = direction:normalize()
1800 for i, id in ipairs(bodyIds) do
1801 local body = candidates[id]
1802 local c_oV3 = body.center - origin
1803 local radius = sizeCalculator(body)
1804 local dot = c_oV3:dot(dir)
1805 local desc = dot^2 - (c_oV3:len2() - radius^2)
1806 if desc >= 0 then
1807 local root = math.sqrt(desc)
1808 local farSide = dot + root
1809 local nearSide = dot - root
1810 if nearSide > 0 then
1811 return body, farSide, nearSide
1812 elseif farSide > 0 then
1813 return body, farSide, nil
1814 end
1815 end
1816 end
1817 return nil, nil, nil
1818 end
1819 --
1820 -- closestBody - find the closest body to a given set of world coordinates
1821 -- coordinates [in]: the world coordinates of position in space
1822 -- return: an instance of the BodyParameters object closest to 'coordinates'
1823 --
1824 function PlanetarySystem:closestBody(coordinates)
1825 assert(type(coordinates) == 'table', 'Invalid coordinates.')
1826 local minDistance2, body
1827 local coord = vec3(coordinates)
1828 for _,params in pairs(self) do
1829 local distance2 = (params.center - coord):len2()
1830 if not body or distance2 < minDistance2 then
1831 body = params
1832 minDistance2 = distance2
1833 end
1834 end
1835 return body
1836 end
1837 --
1838 -- convertToBodyIdAndWorldCoordinates - map to body Id and world coordinates
1839 -- overload [in]: an instance of MapPosition or a position string ('::pos...)
1840 -- return: a vec3 instance containing the world coordinates or 'nil' on error.
1841 --
1842 function PlanetarySystem:convertToBodyIdAndWorldCoordinates(overload)
1843 local mapPosition = overload
1844 if isString(overload) then
1845 mapPosition = mkMapPosition(overload)
1846 end
1847 if mapPosition.bodyId == 0 then
1848 return 0, vec3(mapPosition.latitude,
1849 mapPosition.longitude,
1850 mapPosition.altitude)
1851 end
1852 local params = self:getBodyParameters(mapPosition)
1853 if params then
1854 return mapPosition.bodyId,
1855 params:convertToWorldCoordinates(mapPosition)
1856 end
1857 end
1858 --
1859 -- getBodyParameters - get or create an instance of BodyParameters class
1860 -- overload [in]: either an instance of MapPosition or a body's ID.
1861 -- return: a BodyParameters instance or 'nil' if body ID is not found.
1862 --
1863 function PlanetarySystem:getBodyParameters(overload)
1864 local bodyId = overload
1865 if isMapPosition(overload) then
1866 bodyId = overload.bodyId
1867 end
1868 assert(isSNumber(bodyId),
1869 'Argument 1 (bodyId) must be a number:' .. type(bodyId))
1870 return self[bodyId]
1871 end
1872 --
1873 -- getPlanetarySystemId - get the planetary system ID for this instance
1874 -- return: the planetary system ID or nil if no planets are in the system.
1875 --
1876 function PlanetarySystem:getPlanetarySystemId()
1877 local k, v = next(self)
1878 return v and v.planetarySystemId
1879 end
1880 -- BodyParameters INSTANCE METHODS:
1881 --
1882 -- convertToMapPosition - create an instance of MapPosition from coordinates
1883 -- worldCoordinates [in]: the world coordinates of the map position.
1884 -- return: an instance of MapPosition class
1885 --
1886 function BodyParameters:convertToMapPosition(worldCoordinates)
1887 assert(isTable(worldCoordinates),
1888 'Argument 1 (worldCoordinates) must be an array or vec3:' ..
1889 type(worldCoordinates))
1890 local worldVec = vec3(worldCoordinates)
1891 if self.bodyId == 0 then
1892 return setmetatable({latitude = worldVec.x,
1893 longitude = worldVec.y,
1894 altitude = worldVec.z,
1895 bodyId = 0,
1896 systemId = self.planetarySystemId}, MapPosition)
1897 end
1898 local coords = worldVec - self.center
1899 local distance = coords:len()
1900 local altitude = distance - self.radius
1901 local latitude = 0
1902 local longitude = 0
1903 if not float_eq(distance, 0) then
1904 local phi = math.atan(coords.y, coords.x)
1905 longitude = phi >= 0 and phi or (2*math.pi + phi)
1906 latitude = math.pi/2 - math.acos(coords.z/distance)
1907 end
1908 return setmetatable({latitude = latitude,
1909 longitude = longitude,
1910 altitude = altitude,
1911 bodyId = self.bodyId,
1912 systemId = self.planetarySystemId}, MapPosition)
1913 end
1914 --
1915 -- convertToWorldCoordinates - convert a map position to world coordinates
1916 -- overload [in]: an instance of MapPosition or a position string ('::pos...')
1917 --
1918 function BodyParameters:convertToWorldCoordinates(overload)
1919 local mapPosition = isString(overload) and
1920 mkMapPosition(overload) or overload
1921 if mapPosition.bodyId == 0 then -- support deep space map position
1922 return vec3(mapPosition.latitude,
1923 mapPosition.longitude,
1924 mapPosition.altitude)
1925 end
1926 assert(isMapPosition(mapPosition),
1927 'Argument 1 (mapPosition) is not an instance of "MapPosition".')
1928 assert(mapPosition.systemId == self.planetarySystemId,
1929 'Argument 1 (mapPosition) has a different planetary system ID.')
1930 assert(mapPosition.bodyId == self.bodyId,
1931 'Argument 1 (mapPosition) has a different planetary body ID.')
1932 local xproj = math.cos(mapPosition.latitude)
1933 return self.center + (self.radius + mapPosition.altitude) *
1934 vec3(xproj*math.cos(mapPosition.longitude),
1935 xproj*math.sin(mapPosition.longitude),
1936 math.sin(mapPosition.latitude))
1937 end
1938 --
1939 -- getAltitude - calculate the altitude of a point given in world coordinates.
1940 -- worldCoordinates [in]: the world coordinates of the point.
1941 -- return: the altitude in meters
1942 --
1943 function BodyParameters:getAltitude(worldCoordinates)
1944 return (vec3(worldCoordinates) - self.center):len() - self.radius
1945 end
1946 --
1947 -- getDistance - calculate the distance to a point given in world coordinates.
1948 -- worldCoordinates [in]: the world coordinates of the point.
1949 -- return: the distance in meters
1950 --
1951 function BodyParameters:getDistance(worldCoordinates)
1952 return (vec3(worldCoordinates) - self.center):len()
1953 end
1954 --
1955 -- getGravity - calculate the gravity vector induced by the body.
1956 -- worldCoordinates [in]: the world coordinates of the point.
1957 -- return: the gravity vector in meter/seconds^2
1958 --
1959 function BodyParameters:getGravity(worldCoordinates)
1960 local radial = self.center - vec3(worldCoordinates) -- directed towards body
1961 local len2 = radial:len2()
1962 return (self.GM/len2) * radial/math.sqrt(len2)
1963 end
1964 -- end of module
1965 return setmetatable(PlanetaryReference,
1966 { __call = function(_,...)
1967 return mkPlanetaryReference(...)
1968 end })
1969 end
1970 function Keplers()
1971 --[[
1972 Provides methods for computing orbital information for an object
1973 Usage:
1974 Kepler = require('autoconf.custom.kepler')
1975 alioth = Kepler({ GM=157470826617,
1976 bodyId=2,
1977 center={x=-8.000,y=-8.000,z=-126303.000},
1978 name='Alioth',
1979 planetarySystemId=0,
1980 radius=126068
1981 })
1982 altitude = 6000
1983 position = '::pos{0,2,0,0,6000}'
1984 e, o = alioth:escapeAndOrbitalSpeed(altitude)
1985 orbit = alioth:orbitalParameters(position, {0, o+1, 0})
1986 print("Eccentricity " .. orbit.eccentricity)
1987 print("Perihelion " .. orbit.periapsis.altitude)
1988 print("Max. speed " .. orbit.periapsis.speed)
1989 print("Circular orbit speed " .. orbit.periapsis.circularOrbitSpeed)
1990 print("Aphelion " .. orbit.apoapsis.altitude)
1991 print("Min. speed " .. orbit.apoapsis.speed)
1992 print("Orbital period " .. orbit.period)
1993 --- output:
1994 Eccentricity 0.0018324307017878
1995 Perihelion 6000.0
1996 Max. speed 1092.9462297033
1997 Circular orbit speed 1091.9462297033
1998 Aphelion 6484.8994605062
1999 Min. speed 1088.9480596194
2000 Orbital period 762.02818214049
2001 Methods:
2002 Kepler:escapeAndOrbitalSpeed - for a given celestial body and altitude.
2003 Kepler:orbitalParameters - for a given massless object and a celestial body.
2004 Description
2005 The motion of an object in the vicinity of substantially larger mass is
2006 in the domain of the "2-body problem". By assuming the object whose motion
2007 is of interest is of negligable mass simplifies the calculations of:
2008 the speed to escape the body, the speed of a circular orbit, and the
2009 parameters defining the orbit of the object (or the lack of orbit as the
2010 case may be).
2011 Orbital Parameters:
2012 periapsis - the closest approach to the planet
2013 apoapsis - the furthest point from the planet if in orbit (otherwise nil)
2014 eccentricity - 0 for circular orbits
2015 <1 for elliptical orbits
2016 1 for parabiolic trajectory
2017 >1 for hyperbolic trajectory
2018 period - time (in seconds) to complete an orbit
2019 Also See: planetref.lua
2020 ]]--
2021 local vec3 = require('cpml.vec3')
2022 local PlanetRef = PlanetRef()
2023 local function isString(s) return type(s) == 'string' end
2024 local function isTable(t) return type(t) == 'table' end
2025 local function float_eq(a,b)
2026 if a == 0 then return math.abs(b) < 1e-09 end
2027 if b == 0 then return math.abs(a) < 1e-09 end
2028 return math.abs(a - b) < math.max(math.abs(a),math.abs(b))*epsilon
2029 end
2030 Kepler = {}
2031 Kepler.__index = Kepler
2032 --
2033 -- escapeAndOrbitalSpeed - speed required to escape and for a circular orbit
2034 -- altitude [in]: the height of the orbit in meters above "sea-level"
2035 -- return: the speed in m/s needed to escape the celestial body and to orbit it.
2036 --
2037 function Kepler:escapeAndOrbitalSpeed(altitude)
2038 assert(self.body)
2039 -- P = -GMm/r and KE = mv^2/2 (no lorentz factor used)
2040 -- mv^2/2 = GMm/r
2041 -- v^2 = 2GM/r
2042 -- v = sqrt(2GM/r1)
2043 local distance = altitude + self.body.radius
2044 if not float_eq(distance, 0) then
2045 local orbit = math.sqrt(self.body.GM/distance)
2046 return math.sqrt(2)*orbit, orbit
2047 end
2048 return nil, nil
2049 end
2050 --
2051 -- orbitalParameters: determine the orbital elements for a two-body system.
2052 -- overload [in]: the world coordinates or map coordinates of a massless object.
2053 -- velocity [in]: The velocity of the massless point object in m/s.
2054 -- return: the 6 orbital elements for the massless object.
2055 --
2056 function Kepler:orbitalParameters(overload, velocity)
2057 assert(self.body)
2058 assert(isTable(overload) or isString(overload))
2059 assert(isTable(velocity))
2060 local pos = (isString(overload) or PlanetRef.isMapPosition(overload)) and
2061 self.body:convertToWorldCoordinates(overload) or
2062 vec3(overload)
2063 local v = vec3(velocity)
2064 local r = pos - self.body.center
2065 local v2 = v:len2()
2066 local d = r:len()
2067 local mu = self.body.GM
2068 local e = ((v2 - mu/d)*r - r:dot(v)*v)/mu
2069 local a = mu/(2*mu/d - v2)
2070 local ecc = e:len()
2071 local dir = e:normalize()
2072 local pd = a*(1-ecc)
2073 local ad = a*(1+ecc)
2074 local per = pd*dir + self.body.center
2075 local apo = ecc <= 1 and -ad*dir + self.body.center or nil
2076 local trm = math.sqrt(a*mu*(1-ecc*ecc))
2077 local Period = apo and 2*math.pi*math.sqrt(a^3/mu)
2078 -- These are great and all, but, I need more.
2079 local trueAnomaly = math.acos((e:dot(r))/(ecc*d))
2080 if r:dot(v) < 0 then
2081 trueAnomaly = -(trueAnomaly - 2*math.pi)
2082 end
2083 -- Apparently... cos(EccentricAnomaly) = (cos(trueAnomaly) + eccentricity)/(1 + eccentricity * cos(trueAnomaly))
2084 local EccentricAnomaly = math.acos((math.cos(trueAnomaly) + ecc)/(1 + ecc * math.cos(trueAnomaly)))
2085 -- Then.... apparently if this is below 0, we should add 2pi to it
2086 -- I think also if it's below 0, we're past the apoapsis?
2087 local timeTau = EccentricAnomaly
2088 if timeTau < 0 then
2089 timeTau = timeTau + 2*math.pi
2090 end
2091 -- So... time since periapsis...
2092 -- Is apparently easy if you get mean anomly. t = M/n where n is mean motion, = 2*pi/Period
2093
2094
2095 local MeanAnomaly = timeTau - ecc * math.sin(timeTau)
2096 local TimeSincePeriapsis = 0
2097 local TimeToPeriapsis = 0
2098 local TimeToApoapsis = 0
2099 if Period ~= nil then
2100 TimeSincePeriapsis = MeanAnomaly/(2*math.pi/Period)
2101
2102 --system.print(MeanAnomaly .. " - " .. TimeSincePeriapsis .. " - " .. Period .. " - " .. EccentricAnomaly .. " - " .. timeTau .. " - " .. trueAnomaly)
2103 -- Mean anom is 0 at periapsis, positive before it... and positive after it.
2104 -- I guess this is why I needed to use timeTau and not EccentricAnomaly here
2105
2106 TimeToPeriapsis = Period - TimeSincePeriapsis
2107 TimeToApoapsis = TimeToPeriapsis + Period/2
2108 if trueAnomaly - math.pi > 0 then -- TBH I think something's wrong in my formulas because I needed this.
2109 TimeToPeriapsis = TimeSincePeriapsis
2110 TimeToApoapsis = TimeToPeriapsis + Period/2
2111 end
2112 if TimeToApoapsis > Period then
2113 TimeToApoapsis = TimeToApoapsis - Period
2114 end
2115 end
2116 return { periapsis = { position = per,
2117 speed = trm/pd,
2118 circularOrbitSpeed = math.sqrt(mu/pd),
2119 altitude = pd - self.body.radius},
2120 apoapsis = apo and
2121 { position = apo,
2122 speed = trm/ad,
2123 circularOrbitSpeed = math.sqrt(mu/ad),
2124 altitude = ad - self.body.radius},
2125 currentVelocity = v,
2126 currentPosition = pos,
2127 eccentricity = ecc,
2128 period = Period,
2129 eccentricAnomaly = EccentricAnomaly,
2130 meanAnomaly = MeanAnomaly,
2131 timeToPeriapsis = TimeToPeriapsis,
2132 timeToApoapsis = TimeToApoapsis
2133 }
2134 end
2135
2136 local function new(bodyParameters)
2137 local params = PlanetRef.BodyParameters(bodyParameters.planetarySystemId,
2138 bodyParameters.bodyId,
2139 bodyParameters.radius,
2140 bodyParameters.center,
2141 bodyParameters.GM)
2142 return setmetatable({body = params}, Kepler)
2143 end
2144 return setmetatable(Kepler, { __call = function(_,...) return new(...) end })
2145 end
2146 function Kinematics()
2147 --[[
2148 DualUniverse kinematic equations
2149 Author: JayleBreak
2150 Usage (unit.start):
2151 Kinematics = require('autoconf.custom.kinematics')
2152 Methods:
2153 computeAccelerationTime - "relativistic" version of t = (vf - vi)/a
2154 computeDistanceAndTime - Return distance & time needed to reach final speed.
2155 computeTravelTime - "relativistic" version of t=(sqrt(2ad+v^2)-v)/a
2156 Description
2157 DualUniverse increases the effective mass of constructs as their absolute
2158 speed increases by using the "lorentz" factor (from relativity) as the scale
2159 factor. This results in an upper bound on the absolute speed of constructs
2160 (excluding "warp" drive) that is set to 30 000 KPH (8 333 MPS). This module
2161 provides utilities for computing some physical quantities taking this
2162 scaling into account.
2163 ]]--
2164 local Kinematic = {} -- just a namespace
2165 local C = 30000000/3600
2166 local C2 = C*C
2167 local ITERATIONS = 100 -- iterations over engine "warm-up" period
2168 local function lorentz(v) return 1/math.sqrt(1 - v*v/C2) end
2169 --
2170 -- computeAccelerationTime - "relativistic" version of t = (vf - vi)/a
2171 -- initial [in]: initial (positive) speed in meters per second.
2172 -- acceleration [in]: constant acceleration until 'finalSpeed' is reached.
2173 -- final [in]: the speed at the end of the time interval.
2174 -- return: the time in seconds spent in traversing the distance
2175 --
2176 function Kinematic.computeAccelerationTime(initial, acceleration, final)
2177 -- The low speed limit of following is: t=(vf-vi)/a (from: vf=vi+at)
2178 local k1 = C*math.asin(initial/C)
2179 return (C * math.asin(final/C) - k1)/acceleration
2180 end
2181 --
2182 -- computeDistanceAndTime - Return distance & time needed to reach final speed.
2183 -- initial[in]: Initial speed in meters per second.
2184 -- final[in]: Final speed in meters per second.
2185 -- restMass[in]: Mass of the construct at rest in Kg.
2186 -- thrust[in]: Engine's maximum thrust in Newtons.
2187 -- t50[in]: (default: 0) Time interval to reach 50% thrust in seconds.
2188 -- brakeThrust[in]: (default: 0) Constant thrust term when braking.
2189 -- return: Distance (in meters), time (in seconds) required for change.
2190 --
2191 function Kinematic.computeDistanceAndTime(initial,
2192 final,
2193 restMass,
2194 thrust,
2195 t50,
2196 brakeThrust)
2197 -- This function assumes that the applied thrust is colinear with the
2198 -- velocity. Furthermore, it does not take into account the influence
2199 -- of gravity, not just in terms of its impact on velocity, but also
2200 -- its impact on the orientation of thrust relative to velocity.
2201 -- These factors will introduce (usually) small errors which grow as
2202 -- the length of the trip increases.
2203 t50 = t50 or 0
2204 brakeThrust = brakeThrust or 0 -- usually zero when accelerating
2205 local tau0 = lorentz(initial)
2206 local speedUp = initial <= final
2207 local a0 = thrust * (speedUp and 1 or -1)/restMass
2208 local b0 = -brakeThrust/restMass
2209 local totA = a0+b0
2210 if speedUp and totA <= 0 or not speedUp and totA >= 0 then
2211 return -1, -1 -- no solution
2212 end
2213 local distanceToMax, timeToMax = 0, 0
2214 -- If, the T50 time is set, then assume engine is at zero thrust and will
2215 -- reach full thrust in 2*T50 seconds. Thrust curve is given by:
2216 -- Thrust: F(z)=(a0*(1+sin(z))+2*b0)/2 where z=pi*(t/t50 - 1)/2
2217 -- Acceleration is given by F(z)/m(z) where m(z) = m/sqrt(1-v^2/c^2)
2218 -- or v(z)' = (a0*(1+sin(z))+2*b0)*sqrt(1-v(z)^2/c^2)/2
2219 if a0 ~= 0 and t50 > 0 then
2220 -- Closed form solution for velocity exists:
2221 -- v(t) = -c*tan(w)/sqrt(tan(w)^2+1) => w = -asin(v/c)
2222 -- w=(pi*t*(a0/2+b0)-a0*t50*sin(pi*t/2/t50)+*pi*c*k1)/pi/c
2223 -- @ t=0, v(0) = vi
2224 -- pi*c*k1/pi/c = -asin(vi/c)
2225 -- k1 = asin(vi/c)
2226 local k1 = math.asin(initial/C)
2227 local c1 = math.pi*(a0/2+b0)
2228 local c2 = a0*t50
2229 local c3 = C*math.pi
2230 local v = function(t)
2231 local w = (c1*t - c2*math.sin(math.pi*t/2/t50) + c3*k1)/c3
2232 local tan = math.tan(w)
2233 return C*tan/math.sqrt(tan*tan+1)
2234 end
2235 local speedchk = speedUp and function(s) return s >= final end or
2236 function(s) return s <= final end
2237 timeToMax = 2*t50
2238 if speedchk(v(timeToMax)) then
2239 local lasttime = 0
2240 while math.abs(timeToMax - lasttime) > 0.5 do
2241 local t = (timeToMax + lasttime)/2
2242 if speedchk(v(t)) then
2243 timeToMax = t
2244 else
2245 lasttime = t
2246 end
2247 end
2248 end
2249 -- There is no closed form solution for distance in this case.
2250 -- Numerically integrate for time t=0 to t=2*T50 (or less)
2251 local lastv = initial
2252 local tinc = timeToMax/ITERATIONS
2253 for step = 1, ITERATIONS do
2254 local speed = v(step*tinc)
2255 distanceToMax = distanceToMax + (speed+lastv)*tinc/2
2256 lastv = speed
2257 end
2258 if timeToMax < 2*t50 then
2259 return distanceToMax, timeToMax
2260 end
2261 initial = lastv
2262 end
2263 -- At full thrust, acceleration only depends on the Lorentz factor:
2264 -- v(t)' = (F/m(v)) = a*sqrt(1-v(t)^2/c^2) where a = a0+b0
2265 -- -> v = c*sin((at+k1)/c)
2266 -- @ t=0, v=vi: k1 = c*asin(vi/c)
2267 -- -> t = (c*asin(v/c) - k1)/a
2268 -- x(t)' = c*sin((at+k1)/c)
2269 -- x = k2 - c^2 cos((at+k1)/c)/a
2270 -- @ t=0, x=0: k2 = c^2 * cos(k1/c)/a
2271 local k1 = C*math.asin(initial/C)
2272 local time = (C * math.asin(final/C) - k1)/totA
2273 local k2 = C2 *math.cos(k1/C)/totA
2274 local distance = k2 - C2 * math.cos((totA*time + k1)/C)/totA
2275 return distance+distanceToMax, time+timeToMax
2276 end
2277 --
2278 -- computeTravelTime - "relativistic" version of t=(sqrt(2ad+v^2)-v)/a
2279 -- initialSpeed [in]: initial (positive) speed in meters per second
2280 -- acceleration [in]: constant acceleration until 'distance' is traversed
2281 -- distance [in]: the distance traveled in meters
2282 -- return: the time in seconds spent in traversing the distance
2283 --
2284 function Kinematic.computeTravelTime(initial, acceleration, distance)
2285 -- The low speed limit of following is: t=(sqrt(2ad+v^2)-v)/a
2286 -- (from: d=vt+at^2/2)
2287 if distance == 0 then return 0 end
2288 if acceleration > 0 then
2289 local k1 = C*math.asin(initial/C)
2290 local k2 = C2*math.cos(k1/C)/acceleration
2291 return (C*math.acos(acceleration*(k2 - distance)/C2) - k1)/acceleration
2292 end
2293 assert(initial > 0, 'Acceleration and initial speed are both zero.')
2294 return distance/initial
2295 end
2296 function Kinematic.lorentz(v) return lorentz(v) end
2297 return Kinematic
2298 end
2299 PlanetaryReference = PlanetRef()
2300 galaxyReference = PlanetaryReference(Atlas())
2301 Kinematic = Kinematics()
2302 Kep = Keplers()
2303 AutopilotTargetIndex = 0
2304 AutopilotTargetName = "None"
2305 AutopilotTargetPlanet = nil
2306 MaxGameVelocity = 8333.05 --export: Max speed for your autopilot in m/s, do not go above 8333.055
2307 function getDistanceDisplayString(distance)
2308 local su = distance > 100000
2309 local result = ""
2310 if su then
2311 -- Convert to SU
2312 result = round(distance/1000/200,1) .. " SU"
2313 elseif distance < 1000 then
2314 result = round(distance,1) .. " M"
2315 else
2316 -- Convert to KM
2317 result = round(distance/1000,1) .. " KM"
2318 end
2319
2320 return result
2321 end
2322 function getSpeedDisplayString(speed) -- TODO: Allow options, for now just do kph
2323 return math.floor(round(speed*3.6,0)+0.5) .. " km/h" -- And generally it's not accurate enough to not twitch unless we round 0
2324 end
2325 function FormatTimeString(seconds)
2326 local hours = math.floor(seconds/3600)
2327 local minutes = math.floor(seconds/60%60)
2328 local seconds = math.floor(seconds%60)
2329 if seconds < 0 or hours < 0 or minutes < 0 then
2330 return "0s"
2331 end
2332 return hours .. "h " .. minutes .. "m" .. seconds .. "s"
2333 end
2334 function getMagnitudeInDirection(vector, direction)
2335 --return vec3(vector):project_on(vec3(direction)):len()
2336 vector = vec3(vector)
2337 direction = vec3(direction):normalize()
2338 local result = vector*direction -- To preserve sign, just add them I guess
2339 return result.x + result.y + result.z
2340 end
2341 function UpdateAutopilotTarget()
2342 -- So the indices are weird. I think we need to do a pairs
2343 if AutopilotTargetIndex == 0 then
2344 AutopilotTargetName = "None"
2345 AutopilotTargetPlanet = nil
2346 return true
2347 end
2348 local count = 0
2349 for k,v in pairs(Atlas()[0]) do
2350 count = count + 1
2351 if count == AutopilotTargetIndex then
2352 AutopilotTargetName = v.name
2353 AutopilotTargetPlanet = galaxyReference[0][k]
2354 AutopilotTargetCoords = vec3(AutopilotTargetPlanet.center) -- Aim center until we align
2355 -- Determine the end speed
2356 _, AutopilotEndSpeed = kepPlanet:escapeAndOrbitalSpeed(AutopilotTargetOrbit)
2357 --AutopilotEndSpeed = 0
2358 --AutopilotPlanetGravity = AutopilotTargetPlanet:getGravity(AutopilotTargetPlanet.center + vec3({1,0,0}) * AutopilotTargetOrbit):len() -- Any direction, at our orbit height
2359 AutopilotPlanetGravity = 0 -- This is inaccurate unless we integrate and we're not doing that.
2360 AutopilotAccelerating = false
2361 AutopilotBraking = false
2362 AutopilotCruising = false
2363 Autoilot = false
2364 AutopilotRealigned = false
2365 AutopilotStatus = "Aligning"
2366 return true
2367 end
2368 end
2369 system.print("Error: Autopilot index was outside the bounds of the target range")
2370 return false
2371 end
2372 function IncrementAutopilotTargetIndex()
2373 AutopilotTargetIndex = AutopilotTargetIndex + 1
2374 if AutopilotTargetIndex > tablelength(Atlas()[0]) then
2375 AutopilotTargetIndex = 0
2376 end
2377 UpdateAutopilotTarget()
2378 end
2379 function DecrementAutopilotTargetIndex()
2380 AutopilotTargetIndex = AutopilotTargetIndex - 1
2381 if AutopilotTargetIndex < 0 then
2382 AutopilotTargetIndex = tablelength(Atlas()[0])
2383 end
2384 UpdateAutopilotTarget()
2385 end
2386 function GetAutopilotTravelTime()
2387 AutopilotDistance = (AutopilotTargetPlanet.center - vec3(core.getConstructWorldPos())):len()
2388 local velocity = core.getWorldVelocity()
2389 local accelDistance, accelTime = Kinematic.computeDistanceAndTime(vec3(velocity):len(),
2390 MaxGameVelocity, -- From currently velocity to max
2391 core.getConstructMass(),
2392 Nav:maxForceForward(),
2393 warmup, -- T50? Assume none, negligible for this
2394 0) -- Brake thrust, none for this
2395 -- accelDistance now has the amount of distance for which we will be accelerating
2396 -- Then we need the distance we'd brake from full speed
2397 -- Note that for some nearby moons etc, it may never reach full speed though.
2398 local brakeDistance, brakeTime
2399 if not TurnBurn then
2400 brakeDistance, brakeTime = GetAutopilotBrakeDistanceAndTime(MaxGameVelocity)
2401 else
2402 brakeDistance, brakeTime = GetAutopilotTBBrakeDistanceAndTime(MaxGameVelocity)
2403 end
2404 local curBrakeDistance, curBrakeTime
2405 if not TurnBurn then
2406 curBrakeDistance, curBrakeTime = GetAutopilotBrakeDistanceAndTime(vec3(velocity):len())
2407 else
2408 curBrakeDistance, curBrakeTime = GetAutopilotTBBrakeDistanceAndTime(vec3(velocity):len())
2409 end
2410 local cruiseDistance = 0
2411 local cruiseTime = 0
2412
2413 -- So, time is in seconds
2414 -- If cruising or braking, use real cruise/brake values
2415 if brakeDistance + accelDistance < AutopilotDistance then
2416 -- Add any remaining distance
2417 cruiseDistance = AutopilotDistance - (brakeDistance + accelDistance)
2418 cruiseTime = Kinematic.computeTravelTime(8333.0556, 0, cruiseDistance)
2419 else
2420 local accelRatio = (AutopilotDistance - brakeDistance)/accelDistance
2421 accelDistance = AutopilotDistance - brakeDistance -- Accel until we brake
2422 accelTime = accelTime * accelRatio
2423 end
2424 if AutopilotBraking then
2425 return curBrakeTime
2426 elseif AutopilotCruising then
2427 return cruiseTime + curBrakeTime
2428 else -- If not cruising or braking, assume we'll get to max speed
2429 return accelTime + brakeTime + cruiseTime
2430 end
2431 end
2432 function GetAutopilotBrakeDistanceAndTime(speed)
2433 local maxBrake = json.decode(unit.getData()).maxBrake
2434 if maxBrake ~= nil then
2435 LastMaxBrake = maxBrake
2436 return Kinematic.computeDistanceAndTime(speed, AutopilotEndSpeed, core.getConstructMass(), 0, 0, maxBrake - (AutopilotPlanetGravity * core.getConstructMass()))
2437 else
2438 return Kinematic.computeDistanceAndTime(speed, AutopilotEndSpeed, core.getConstructMass(), 0, 0, LastMaxBrake - (AutopilotPlanetGravity * core.getConstructMass()))
2439 end
2440 end
2441 function GetAutopilotTBBrakeDistanceAndTime(speed) -- Uses thrust and a configured T50
2442 local maxBrake = json.decode(unit.getData()).maxBrake
2443 if maxBrake ~= nil then
2444 LastMaxBrake = maxBrake
2445 return Kinematic.computeDistanceAndTime(speed, AutopilotEndSpeed, core.getConstructMass(), Nav:maxForceForward(), warmup, maxBrake - (AutopilotPlanetGravity * core.getConstructMass()))
2446 else
2447 return Kinematic.computeDistanceAndTime(speed, AutopilotEndSpeed, core.getConstructMass(), Nav:maxForceForward(), warmup, LastMaxBrake - (AutopilotPlanetGravity * core.getConstructMass()))
2448 end
2449 end
2450 function round(num, numDecimalPlaces)
2451 local mult = 10^(numDecimalPlaces or 0)
2452 return math.floor(num * mult + 0.5) / mult
2453 end
2454 function tablelength(T)
2455 local count = 0
2456 for _ in pairs(T) do count = count + 1 end
2457 return count
2458 end
2459
2460 stop:
2461 lua: |
2462 _autoconf.hideCategoryPanels()
2463 if antigrav ~= nil then antigrav.hide() end
2464 if warpdrive ~= nil then warpdrive.hide() end
2465 if gyro ~= nil then gyro.hide() end
2466 core.hide()
2467 Nav.control.switchOffHeadlights()
2468 -- Open door and extend ramp if available
2469 if door ~= nil then door.activate() end
2470 if forcefield ~= nil then forcefield.activate() end
2471 tick:
2472 args: ["apTick"]
2473 lua: |
2474 -- NO USER CHANGES
2475 yawInput2 = 0
2476 rollInput2 = 0
2477 pitchInput2 = 0
2478 LastApsDiff = -1
2479 local velocity = vec3(core.getWorldVelocity())
2480 local velMag = vec3(velocity):len()
2481 local sys = galaxyReference[0]
2482 planet = sys:closestBody(core.getConstructWorldPos())
2483 kepPlanet = Kep(planet)
2484 orbit = kepPlanet:orbitalParameters(core.getConstructWorldPos(), velocity)
2485
2486 local deltaX = system.getMouseDeltaX()
2487 local deltaY = system.getMouseDeltaY()
2488
2489 local TrajectoryAlignmentStrength = 0.002 --export: How strongly AP tries to align your velocity vector to the target when not in orbit
2490
2491 if BrakeIsOn then
2492 brakeInput = 1
2493 elseif brakeToggle == "true" then
2494 brakeInput = 0
2495 end
2496
2497 if AutopilotTargetName ~= "None" then
2498
2499 ShowInterplanetaryPanel()
2500
2501 system.updateData(interplanetaryHeaderText, '{"label": "Target", "value": "' .. AutopilotTargetName .. '", "unit":""}')
2502 travelTime = GetAutopilotTravelTime() -- This also sets AutopilotDistance so we don't have to calc it again
2503 distance = AutopilotDistance
2504 if not TurnBurn then
2505 brakeDistance, brakeTime = GetAutopilotBrakeDistanceAndTime(velMag)
2506 maxBrakeDistance, maxBrakeTime = GetAutopilotBrakeDistanceAndTime(MaxGameVelocity)
2507 else
2508 brakeDistance, brakeTime = GetAutopilotTBBrakeDistanceAndTime(velMag)
2509 maxBrakeDistance, maxBrakeTime = GetAutopilotTBBrakeDistanceAndTime(MaxGameVelocity)
2510 end
2511
2512 system.updateData(widgetDistanceText, '{"label": "Distance", "value": "' .. getDistanceDisplayString(distance) .. '", "unit":""}')
2513 system.updateData(widgetTravelTimeText, '{"label": "Travel Time", "value": "' .. FormatTimeString(travelTime) .. '", "unit":""}')
2514 system.updateData(widgetCurBrakeDistanceText, '{"label": "Cur Brake Distance", "value": "' .. getDistanceDisplayString(brakeDistance) .. '", "unit":""}')
2515 system.updateData(widgetCurBrakeTimeText, '{"label": "Cur Brake Time", "value": "' .. FormatTimeString(brakeTime) .. '", "unit":""}')
2516 system.updateData(widgetMaxBrakeDistanceText, '{"label": "Max Brake Distance", "value": "' .. getDistanceDisplayString(maxBrakeDistance) .. '", "unit":""}')
2517 system.updateData(widgetMaxBrakeTimeText, '{"label": "Max Brake Time", "value": "' .. FormatTimeString(maxBrakeTime) .. '", "unit":""}')
2518 else
2519 HideInterplanetaryPanel()
2520 end
2521 updateHud() -- sets up Content for us
2522 content = content .. [[<svg width="100%" height="100%" style="position:absolute;top:0;left:0" viewBox="0 0 2560 1440">]]
2523 DrawDeadZone()
2524 if system.isViewLocked() == 0 then
2525 CheckButtons()
2526 simulatedX = 0
2527 simulatedY = 0 -- Reset after they do view things, and don't keep sending inputs while unlocked view
2528 -- Except of course autopilot, which is later.
2529 else
2530 simulatedX = simulatedX + deltaX
2531 simulatedY = simulatedY + deltaY
2532 distance = math.sqrt(simulatedX*simulatedX + simulatedY*simulatedY)
2533 if not HoldingCtrl then -- Draw deadzone circle if it's navigating
2534 if userControlScheme == "Virtual Joystick" then -- Virtual Joystick
2535 -- Do navigation things
2536
2537 if simulatedX > 0 and simulatedX > DeadZone then
2538 yawInput2 = yawInput2 - (simulatedX - DeadZone) * MouseXSensitivity
2539 elseif simulatedX < 0 and simulatedX < (DeadZone * -1) then
2540 yawInput2 = yawInput2 - (simulatedX + DeadZone) * MouseXSensitivity
2541 else
2542 yawInput2 = 0
2543 end
2544
2545 if simulatedY > 0 and simulatedY > DeadZone then
2546 pitchInput2 = pitchInput2 - (simulatedY - DeadZone) * MouseYSensitivity
2547 elseif simulatedY < 0 and simulatedY < (DeadZone * -1) then
2548 pitchInput2 = pitchInput2 - (simulatedY + DeadZone) * MouseYSensitivity
2549 else
2550 pitchInput2 = 0
2551 end
2552 elseif userControlScheme == "Mouse" then -- Mouse Direct
2553 simulatedX = 0
2554 simulatedY = 0
2555 --pitchInput2 = pitchInput2 - deltaY * mousePitchFactor
2556 --yawInput2 = yawInput2 - deltaX * mouseYawFactor
2557 -- So... this is weird.
2558 -- It's doing some odd things and giving us some weird values.
2559
2560 -- utils.smoothstep(progress, low, high)*2-1
2561 pitchInput2 = (-utils.smoothstep(deltaY, -100, 100) + 0.5)*2*mousePitchFactor
2562 yawInput2 = (-utils.smoothstep(deltaX, -100, 100) + 0.5)*2*mouseYawFactor
2563 else -- Keyboard mode
2564 simulatedX = 0
2565 simulatedY = 0
2566 -- Don't touch anything, they have it with kb only.
2567 end
2568
2569
2570
2571 -- Right so. We can't detect a mouse click. That's stupid.
2572 -- We have two options. 1. Use mouse wheel movement as a click, or 2. If you're hovered over a button and let go of Ctrl, it's a click
2573 -- I think 2 is a much smoother solution. Even if we later want to have them input some coords
2574 -- We'd have to hook 0-9 in their events, and they'd have to point at the target, so it wouldn't be while this screen is open
2575
2576 -- What that means is, if we get here, check our hovers. If one of them is active, trigger the thing and deactivate the hover
2577 CheckButtons()
2578
2579
2580 if distance > DeadZone then -- Draw a line to the cursor from the screen center
2581 -- Note that because SVG lines fucking suck, we have to do a translate and they can't use calc in their params
2582 DrawCursorLine()
2583 end
2584 else
2585 -- Ctrl is being held, draw buttons.
2586 -- Brake toggle, face prograde, face retrograde (for now)
2587 -- We've got some vars setup in Start for them to make this easier to work with
2588
2589
2590 SetButtonContains()
2591
2592
2593 DrawButtons()
2594
2595
2596 end
2597
2598 -- Cursor always on top, draw it last
2599
2600 content = content .. "<circle stroke='white' cx='calc(50% + " .. simulatedX .. "px)' cy='calc(50% + " .. simulatedY .. "px)' r='5'/>"
2601
2602 end
2603 content = content .. [[</svg></body>]]
2604 system.setScreen(content)
2605
2606 if AutoBrake and AutopilotTargetPlanetName ~= "None" and (vec3(core.getConstructWorldPos())-vec3(AutopilotTargetPlanet.center)):len() <= brakeDistance then
2607 brakeInput = 1
2608 if planet.name == AutopilotTargetPlanet.name and orbit.apoapsis ~= nil and orbit.eccentricity < 1 then
2609 -- We're increasing eccentricity by braking, time to stop
2610 brakeInput = 0
2611 AutoBrake = false
2612 end
2613 end
2614 if ProgradeIsOn then
2615 if velMag > MinAutopilotSpeed then -- Help with div by 0 errors and careening into terrain at low speed
2616 AlignToWorldVector(vec3(velocity))
2617 end
2618 end
2619 if RetrogradeIsOn then
2620 if velMag > MinAutopilotSpeed then -- Help with div by 0 errors and careening into terrain at low speed
2621 AlignToWorldVector(-(vec3(velocity)))
2622 end
2623 end
2624 if Autopilot and unit.getAtmosphereDensity() == 0 then
2625 -- Planetary autopilot engaged, we are out of atmo, and it has a target
2626 -- Do it.
2627 -- And tbh we should calc the brakeDistance live too, and of course it's also in meters
2628 local brakeDistance, brakeTime = GetAutopilotBrakeDistanceAndTime(velMag)
2629 --system.print(brakeDistance)
2630 brakeDistance = brakeDistance
2631 brakeTime = brakeTime -- * 1.05 -- Padding?
2632 -- Maybe instead of pointing at our vector, we point at our vector + how far off our velocity vector is
2633 -- This is gonna be hard to get the negatives right.
2634 -- If we're still in orbit, don't do anything, that velocity will suck
2635 local targetCoords = AutopilotTargetCoords
2636 if orbit.apoapsis == nil and velMag > 300 and AutopilotAccelerating then
2637 -- Get the angle between forward and velocity
2638 -- Get the magnitude for each of yaw and pitch
2639 -- Consider a right triangle, with side a being distance to our target
2640 -- get side b, where have the angle. Do this once for each of yaw and pitch
2641 -- The result of each of those would then be multiplied by something to make them vectors...
2642
2643
2644 -- Okay another idea.
2645 -- Normalize forward and velocity, then get the ratio of normvelocity:velocity
2646 -- And scale forward back up by that amount. Then take forward-velocity, the
2647
2648
2649 -- No no.
2650 -- Okay so, first, when we realign, we store shipright and shipup, just for this
2651 -- Get the difference between ship forward and normalized worldvel
2652 -- Get the components in each of the stored shipright and shipup directions
2653 -- Get the ratio of velocity to normalized velocity and scale up that component (Hey this is just velmag btw)
2654 -- Add that component * shipright or shipup
2655 local velVectorOffset = (vec3(AutopilotTargetCoords) - vec3(core.getConstructWorldPos())):normalize() - vec3(velocity):normalize()
2656 local pitchComponent = getMagnitudeInDirection(velVectorOffset, AutopilotShipUp)
2657 local yawComponent = getMagnitudeInDirection(velVectorOffset, AutopilotShipRight)
2658 local leftAmount = -yawComponent * AutopilotDistance * velMag*TrajectoryAlignmentStrength
2659 local downAmount = -pitchComponent * AutopilotDistance * velMag*TrajectoryAlignmentStrength
2660 targetCoords = AutopilotTargetCoords + (-leftAmount * vec3(AutopilotShipRight)) + (-downAmount * vec3(AutopilotShipUp))
2661 end
2662 -- If we're here, sadly, we really need to calc the distance every update (or tick)
2663 AutopilotDistance = (vec3(targetCoords) - vec3(core.getConstructWorldPos())):len()
2664 system.updateData(widgetDistanceText, '{"label": "Distance", "value": "' .. getDistanceDisplayString(AutopilotDistance) .. '", "unit":""}')
2665 local aligned = true -- It shouldn't be used if the following condition isn't met, but just in case
2666
2667 local projectedAltitude = (AutopilotTargetPlanet.center - (vec3(core.getConstructWorldPos()) + (vec3(velocity):normalize() * AutopilotDistance))):len() - AutopilotTargetPlanet.radius
2668 system.updateData(widgetTrajectoryAltitudeText, '{"label": "Projected Altitude", "value": "' .. getDistanceDisplayString(projectedAltitude) .. '", "unit":""}')
2669
2670 if not AutopilotCruising and not AutopilotBraking then
2671 aligned = AlignToWorldVector((targetCoords-vec3(core.getConstructWorldPos())):normalize())
2672 elseif TurnBurn then
2673 aligned = AlignToWorldVector(-vec3(velocity):normalize())
2674 end
2675 if AutopilotAccelerating then
2676 if not aligned then
2677 AutopilotStatus = "Adjusting Trajectory"
2678 else
2679 AutopilotStatus = "Accelerating"
2680 end
2681 Nav.axisCommandManager:setThrottleCommand(axisCommandId.longitudinal, 100)
2682 if vec3(core.getVelocity()):len() >= MaxGameVelocity then -- This is 29999 kph
2683 AutopilotAccelerating = false
2684 AutopilotStatus = "Cruising"
2685 AutopilotCruising = true
2686 Nav.axisCommandManager:setThrottleCommand(axisCommandId.longitudinal, 0)
2687 end
2688 -- Check if accel needs to stop for braking
2689 if AutopilotDistance <= brakeDistance then
2690 AutopilotAccelerating = false
2691 AutopilotStatus = "Braking"
2692 AutopilotBraking = true
2693 Nav.axisCommandManager:setThrottleCommand(axisCommandId.longitudinal, 0)
2694 end
2695 elseif AutopilotBraking then
2696 BrakeIsOn = true
2697 brakeInput = 1
2698 if TurnBurn then
2699 Nav.axisCommandManager:setThrottleCommand(axisCommandId.longitudinal, 100)
2700 end
2701 -- Check if an orbit has been established and cut brakes and disable autopilot if so
2702
2703 -- We'll try <0.9 instead of <1 so that we don't end up in a barely-orbit where touching the controls will make it an escape orbit
2704 -- Though we could probably keep going until it starts getting more eccentric, so we'd maybe have a circular orbit
2705
2706 if orbit.periapsis ~= nil and orbit.eccentricity < 1 then
2707 AutopilotStatus = "Circularizing Orbit"
2708 -- Keep going until the apoapsis and periapsis start getting further apart
2709 -- Rather than: orbit.periapsis ~= nil and orbit.periapsis.altitude < ((vec3(planet.center) - vec3(core.getConstructWorldPos())):len() - planet.radius)-1000
2710 --local apsDiff = math.abs(orbit.apoapsis.altitude - orbit.periapsis.altitude)
2711 --if LastApsDiff ~= -1 and apsDiff > LastApsDiff then
2712 if orbit.eccentricity > LastEccentricity or (orbit.apoapsis.altitude < AutopilotTargetOrbit and orbit.periapsis.altitude < AutopilotTargetOrbit) then
2713 --LastApsDiff = -1
2714 BrakeIsOn = false
2715 AutopilotBraking = false
2716 Autopilot = false
2717 AutopilotStatus = "Aligning" -- Disable autopilot and reset
2718 system.print("Autopilot completed, orbit established")
2719 brakeInput = 0
2720 Nav.axisCommandManager:setThrottleCommand(axisCommandId.longitudinal, 0)
2721 end
2722 LastApsDiff = apsDiff
2723 end
2724 elseif AutopilotCruising then
2725 if AutopilotDistance <= brakeDistance then
2726 AutopilotAccelerating = false
2727 AutopilotStatus = "Braking"
2728 AutopilotBraking = true
2729 end
2730 else
2731 -- It's engaged but hasn't started accelerating yet.
2732 if aligned then
2733 -- Re-align to 200km from our aligned right
2734 if not AutopilotRealigned then -- Removed radius from this because it makes our readouts look inaccurate?
2735 AutopilotTargetCoords = vec3(AutopilotTargetPlanet.center) + ((AutopilotTargetOrbit + AutopilotTargetPlanet.radius) * vec3(core.getConstructWorldOrientationRight()))
2736 AutopilotRealigned = true
2737 AutopilotShipUp = core.getConstructWorldOrientationUp()
2738 AutopilotShipRight = core.getConstructWorldOrientationRight()
2739 elseif aligned then
2740 AutopilotAccelerating = true
2741 AutopilotStatus = "Accelerating"
2742 -- Set throttle to max
2743 Nav.axisCommandManager:setThrottleCommand(axisCommandId.longitudinal, 100) -- 100?
2744 end
2745 end
2746 -- If it's not aligned yet, don't try to burn yet.
2747 end
2748 end
2749 LastEccentricity = orbit.eccentricity
2750 system:
2751 start:
2752 lua: |
2753 -- Moved to Unit.Start()
2754 flush:
2755 lua: |
2756 -- constants: use 'myvar = defaultValue --export: description' to expose the variable in context menu
2757
2758 pitchSpeedFactor = 0.8 --export: For keyboard control
2759 yawSpeedFactor = 1 --export: For keyboard control
2760 rollSpeedFactor = 1.5 --export: This factor will increase/decrease the player input along the roll axis<br>(higher value may be unstable)<br>Valid values: Superior or equal to 0.01
2761
2762 local brakeSpeedFactor = 3 --export: When braking, this factor will increase the brake force by brakeSpeedFactor * velocity<br>Valid values: Superior or equal to 0.01
2763 local brakeFlatFactor = 1 --export: When braking, this factor will increase the brake force by a flat brakeFlatFactor * velocity direction><br>(higher value may be unstable)<br>Valid values: Superior or equal to 0.01
2764
2765 local autoRoll = false --export: [Only in atmosphere]<br>When the pilot stops rolling, flight model will try to get back to horizontal (no roll)
2766 local autoRollFactor = 2 --export: [Only in atmosphere]<br>When autoRoll is engaged, this factor will increase to strength of the roll back to 0<br>Valid values: Superior or equal to 0.01
2767
2768 local turnAssist = true --export: [Only in atmosphere]<br>When the pilot is rolling, the flight model will try to add yaw and pitch to make the construct turn better<br>The flight model will start by adding more yaw the more horizontal the construct is and more pitch the more vertical it is
2769 local turnAssistFactor = 2 --export: [Only in atmosphere]<br>This factor will increase/decrease the turnAssist effect<br>(higher value may be unstable)<br>Valid values: Superior or equal to 0.01
2770
2771 local torqueFactor = 2 -- Force factor applied to reach rotationSpeed<br>(higher value may be unstable)<br>Valid values: Superior or equal to 0.01
2772
2773 -- validate params
2774 pitchSpeedFactor = math.max(pitchSpeedFactor, 0.01)
2775 yawSpeedFactor = math.max(yawSpeedFactor, 0.01)
2776 rollSpeedFactor = math.max(rollSpeedFactor, 0.01)
2777 torqueFactor = math.max(torqueFactor, 0.01)
2778 brakeSpeedFactor = math.max(brakeSpeedFactor, 0.01)
2779 brakeFlatFactor = math.max(brakeFlatFactor, 0.01)
2780 autoRollFactor = math.max(autoRollFactor, 0.01)
2781 turnAssistFactor = math.max(turnAssistFactor, 0.01)
2782
2783 -- final inputs
2784 local finalPitchInput = pitchInput + pitchInput2 + system.getControlDeviceForwardInput()
2785 local finalRollInput = rollInput + rollInput2 + system.getControlDeviceYawInput()
2786 local finalYawInput = (yawInput + yawInput2) - system.getControlDeviceLeftRightInput()
2787 local finalBrakeInput = brakeInput
2788
2789 -- Axis
2790 local worldVertical = vec3(core.getWorldVertical()) -- along gravity
2791 local constructUp = vec3(core.getConstructWorldOrientationUp())
2792 local constructForward = vec3(core.getConstructWorldOrientationForward())
2793 local constructRight = vec3(core.getConstructWorldOrientationRight())
2794 local constructVelocity = vec3(core.getWorldVelocity())
2795 local constructVelocityDir = vec3(core.getWorldVelocity()):normalize()
2796 local currentRollDeg = getRoll(worldVertical, constructForward, constructRight)
2797 local currentRollDegAbs = math.abs(currentRollDeg)
2798 local currentRollDegSign = utils.sign(currentRollDeg)
2799
2800 -- Rotation
2801 local constructAngularVelocity = vec3(core.getWorldAngularVelocity())
2802 local targetAngularVelocity = finalPitchInput * pitchSpeedFactor * constructRight
2803 + finalRollInput * rollSpeedFactor * constructForward
2804 + finalYawInput * yawSpeedFactor * constructUp
2805
2806 -- In atmosphere?
2807 if worldVertical:len() > 0.01 and unit.getAtmosphereDensity() > 0.0 then
2808 local autoRollRollThreshold = 1.0
2809 -- autoRoll on AND currentRollDeg is big enough AND player is not rolling
2810 if autoRoll == true and currentRollDegAbs > autoRollRollThreshold and finalRollInput == 0 then
2811 local targetRollDeg = utils.clamp(0,currentRollDegAbs-30, currentRollDegAbs+30); -- we go back to 0 within a certain limit
2812 if (rollPID == nil) then
2813 rollPID = pid.new(autoRollFactor * 0.01, 0, autoRollFactor * 0.1) -- magic number tweaked to have a default factor in the 1-10 range
2814 end
2815 rollPID:inject(targetRollDeg - currentRollDeg)
2816 local autoRollInput = rollPID:get()
2817
2818 targetAngularVelocity = targetAngularVelocity + autoRollInput * constructForward
2819 end
2820 local turnAssistRollThreshold = 20.0
2821 -- turnAssist AND currentRollDeg is big enough AND player is not pitching or yawing
2822 if turnAssist == true and currentRollDegAbs > turnAssistRollThreshold and finalPitchInput == 0 and finalYawInput == 0 then
2823 local rollToPitchFactor = turnAssistFactor * 0.1 -- magic number tweaked to have a default factor in the 1-10 range
2824 local rollToYawFactor = turnAssistFactor * 0.025 -- magic number tweaked to have a default factor in the 1-10 range
2825
2826 -- rescale (turnAssistRollThreshold -> 180) to (0 -> 180)
2827 local rescaleRollDegAbs = ((currentRollDegAbs - turnAssistRollThreshold) / (180 - turnAssistRollThreshold)) * 180
2828 local rollVerticalRatio = 0
2829 if rescaleRollDegAbs < 90 then
2830 rollVerticalRatio = rescaleRollDegAbs / 90
2831 elseif rescaleRollDegAbs < 180 then
2832 rollVerticalRatio = (180 - rescaleRollDegAbs) / 90
2833 end
2834
2835 rollVerticalRatio = rollVerticalRatio * rollVerticalRatio
2836
2837 local turnAssistYawInput = - currentRollDegSign * rollToYawFactor * (1.0 - rollVerticalRatio)
2838 local turnAssistPitchInput = rollToPitchFactor * rollVerticalRatio
2839
2840 targetAngularVelocity = targetAngularVelocity
2841 + turnAssistPitchInput * constructRight
2842 + turnAssistYawInput * constructUp
2843 end
2844 end
2845
2846 -- Engine commands
2847 local keepCollinearity = 1 -- for easier reading
2848 local dontKeepCollinearity = 0 -- for easier reading
2849 local tolerancePercentToSkipOtherPriorities = 1 -- if we are within this tolerance (in%), we don't go to the next priorities
2850
2851 -- Rotation
2852 local angularAcceleration = torqueFactor * (targetAngularVelocity - constructAngularVelocity)
2853 local airAcceleration = vec3(core.getWorldAirFrictionAngularAcceleration())
2854 angularAcceleration = angularAcceleration - airAcceleration -- Try to compensate air friction
2855 Nav:setEngineTorqueCommand('torque', angularAcceleration, keepCollinearity, 'airfoil', '', '', tolerancePercentToSkipOtherPriorities)
2856
2857 -- Brakes
2858 local brakeAcceleration = -finalBrakeInput * (brakeSpeedFactor * constructVelocity + brakeFlatFactor * constructVelocityDir)
2859 Nav:setEngineForceCommand('brake', brakeAcceleration)
2860
2861 -- AutoNavigation regroups all the axis command by 'TargetSpeed'
2862 local autoNavigationEngineTags = ''
2863 local autoNavigationAcceleration = vec3()
2864 local autoNavigationUseBrake = false
2865
2866 -- Longitudinal Translation
2867 local longitudinalEngineTags = 'thrust analog longitudinal'
2868 local longitudinalCommandType = Nav.axisCommandManager:getAxisCommandType(axisCommandId.longitudinal)
2869 if (longitudinalCommandType == axisCommandType.byThrottle) then
2870 local longitudinalAcceleration = Nav.axisCommandManager:composeAxisAccelerationFromThrottle(longitudinalEngineTags,axisCommandId.longitudinal)
2871 Nav:setEngineForceCommand(longitudinalEngineTags, longitudinalAcceleration, keepCollinearity)
2872 elseif (longitudinalCommandType == axisCommandType.byTargetSpeed) then
2873 local longitudinalAcceleration = Nav.axisCommandManager:composeAxisAccelerationFromTargetSpeed(axisCommandId.longitudinal)
2874 autoNavigationEngineTags = autoNavigationEngineTags .. ' , ' .. longitudinalEngineTags
2875 autoNavigationAcceleration = autoNavigationAcceleration + longitudinalAcceleration
2876 if (Nav.axisCommandManager:getTargetSpeed(axisCommandId.longitudinal) == 0 or -- we want to stop
2877 Nav.axisCommandManager:getCurrentToTargetDeltaSpeed(axisCommandId.longitudinal) < - Nav.axisCommandManager:getTargetSpeedCurrentStep(axisCommandId.longitudinal) * 0.5) -- if the longitudinal velocity would need some braking
2878 then
2879 autoNavigationUseBrake = true
2880 end
2881
2882 end
2883
2884 -- Lateral Translation
2885 local lateralStrafeEngineTags = 'thrust analog lateral'
2886 local lateralCommandType = Nav.axisCommandManager:getAxisCommandType(axisCommandId.lateral)
2887 if (lateralCommandType == axisCommandType.byThrottle) then
2888 local lateralStrafeAcceleration = Nav.axisCommandManager:composeAxisAccelerationFromThrottle(lateralStrafeEngineTags,axisCommandId.lateral)
2889 Nav:setEngineForceCommand(lateralStrafeEngineTags, lateralStrafeAcceleration, keepCollinearity)
2890 elseif (lateralCommandType == axisCommandType.byTargetSpeed) then
2891 local lateralAcceleration = Nav.axisCommandManager:composeAxisAccelerationFromTargetSpeed(axisCommandId.lateral)
2892 autoNavigationEngineTags = autoNavigationEngineTags .. ' , ' .. lateralStrafeEngineTags
2893 autoNavigationAcceleration = autoNavigationAcceleration + lateralAcceleration
2894 end
2895
2896 -- Vertical Translation
2897 local verticalStrafeEngineTags = 'thrust analog vertical'
2898 local verticalCommandType = Nav.axisCommandManager:getAxisCommandType(axisCommandId.vertical)
2899 if (verticalCommandType == axisCommandType.byThrottle) then
2900 local verticalStrafeAcceleration = Nav.axisCommandManager:composeAxisAccelerationFromThrottle(verticalStrafeEngineTags,axisCommandId.vertical)
2901 Nav:setEngineForceCommand(verticalStrafeEngineTags, verticalStrafeAcceleration, keepCollinearity, 'airfoil', 'ground', '', tolerancePercentToSkipOtherPriorities)
2902 elseif (verticalCommandType == axisCommandType.byTargetSpeed) then
2903 local verticalAcceleration = Nav.axisCommandManager:composeAxisAccelerationFromTargetSpeed(axisCommandId.vertical)
2904 autoNavigationEngineTags = autoNavigationEngineTags .. ' , ' .. verticalStrafeEngineTags
2905 autoNavigationAcceleration = autoNavigationAcceleration + verticalAcceleration
2906 end
2907
2908 -- Auto Navigation (Cruise Control)
2909 if (autoNavigationAcceleration:len() > constants.epsilon) then
2910 if (brakeInput ~= 0 or autoNavigationUseBrake or math.abs(constructVelocityDir:dot(constructForward)) < 0.95) -- if the velocity is not properly aligned with the forward
2911 then
2912 autoNavigationEngineTags = autoNavigationEngineTags .. ', brake'
2913 end
2914 Nav:setEngineForceCommand(autoNavigationEngineTags, autoNavigationAcceleration, dontKeepCollinearity, '', '', '', tolerancePercentToSkipOtherPriorities)
2915 end
2916
2917 -- Rockets
2918 Nav:setBoosterCommand('rocket_engine')
2919 -- Dodgin's Don't Die Rocket Govenor - Cruise Control Edition
2920 speed = vec3(core.getVelocity()):len()
2921 cc_speed = Nav.axisCommandManager:getTargetSpeed(axisCommandId.longitudinal)
2922 if(speed * 3.6 > cc_speed) then
2923 unit.setEngineThrust('rocket_engine',0)
2924 else if(isboosting) then
2925 unit.setEngineThrust('rocket_engine',1) end
2926 end
2927 update:
2928 lua: Nav:update()
2929
2930 actionStart:
2931 args: [gear]
2932 lua: |
2933 gearExtended = not gearExtended
2934 if gearExtended then
2935 Nav.control.extendLandingGears()
2936 Nav.axisCommandManager:setTargetGroundAltitude(0)
2937 else
2938 Nav.control.retractLandingGears()
2939 Nav.axisCommandManager:setTargetGroundAltitude(500) -- What's max?
2940 end
2941
2942 actionStart:
2943 args: [light]
2944 lua: |
2945 if Nav.control.isAnyHeadlightSwitchedOn() == 1 then
2946 Nav.control.switchOffHeadlights()
2947 else
2948 Nav.control.switchOnHeadlights()
2949 end
2950
2951 actionStart:
2952 args: [forward]
2953 lua: pitchInput = pitchInput - 1
2954 actionStop:
2955 args: [forward]
2956 lua: pitchInput = pitchInput + 1
2957 actionStart:
2958 args: [backward]
2959 lua: pitchInput = pitchInput + 1
2960 actionStop:
2961 args: [backward]
2962 lua: pitchInput = pitchInput - 1
2963 actionStart:
2964 args: [left]
2965 lua: rollInput = rollInput - 1
2966 actionStop:
2967 args: [left]
2968 lua: rollInput = rollInput + 1
2969 actionStart:
2970 args: [right]
2971 lua: rollInput = rollInput + 1
2972 actionStop:
2973 args: [right]
2974 lua: rollInput = rollInput - 1
2975
2976 actionStart:
2977 args: [straferight]
2978 lua: Nav.axisCommandManager:updateCommandFromActionStart(axisCommandId.lateral, 1.0)
2979 actionStop:
2980 args: [straferight]
2981 lua: Nav.axisCommandManager:updateCommandFromActionStop(axisCommandId.lateral, -1.0)
2982
2983 actionStart:
2984 args: [strafeleft]
2985 lua: Nav.axisCommandManager:updateCommandFromActionStart(axisCommandId.lateral, -1.0)
2986 actionStop:
2987 args: [strafeleft]
2988 lua: Nav.axisCommandManager:updateCommandFromActionStop(axisCommandId.lateral, 1.0)
2989
2990 actionStart:
2991 args: [up]
2992 lua: |
2993 Nav.axisCommandManager:deactivateGroundEngineAltitudeStabilization()
2994 Nav.axisCommandManager:updateCommandFromActionStart(axisCommandId.vertical, 1.0)
2995 actionStop:
2996 args: [up]
2997 lua: |
2998 Nav.axisCommandManager:updateCommandFromActionStop(axisCommandId.vertical, -1.0)
2999 Nav.axisCommandManager:activateGroundEngineAltitudeStabilization(currentGroundAltitudeStabilization)
3000 actionStart:
3001 args: [down]
3002 lua: |
3003 Nav.axisCommandManager:deactivateGroundEngineAltitudeStabilization()
3004 Nav.axisCommandManager:updateCommandFromActionStart(axisCommandId.vertical, -1.0)
3005 actionStop:
3006 args: [down]
3007 lua: |
3008 Nav.axisCommandManager:updateCommandFromActionStop(axisCommandId.vertical, 1.0)
3009 Nav.axisCommandManager:activateGroundEngineAltitudeStabilization(currentGroundAltitudeStabilization)
3010
3011 actionStart:
3012 args: [groundaltitudeup]
3013 lua: Nav.axisCommandManager:updateTargetGroundAltitudeFromActionStart(1.0)
3014
3015 actionLoop:
3016 args: [groundaltitudeup]
3017 lua: Nav.axisCommandManager:updateTargetGroundAltitudeFromActionLoop(1.0)
3018
3019 actionStart:
3020 args: [groundaltitudedown]
3021 lua: Nav.axisCommandManager:updateTargetGroundAltitudeFromActionStart(-1.0)
3022
3023 actionLoop:
3024 args: [groundaltitudedown]
3025 lua: Nav.axisCommandManager:updateTargetGroundAltitudeFromActionLoop(-1.0)
3026
3027 actionStart:
3028 args: [yawright]
3029 lua: yawInput = yawInput - 1
3030 actionStop:
3031 args: [yawright]
3032 lua: yawInput = yawInput + 1
3033 actionStart:
3034 args: [yawleft]
3035 lua: yawInput = yawInput + 1
3036 actionStop:
3037 args: [yawleft]
3038 lua: yawInput = yawInput - 1
3039 actionStart:
3040 args: [option1]
3041 lua: IncrementAutopilotTargetIndex()
3042 actionStart:
3043 args: [option2]
3044 lua: DecrementAutopilotTargetIndex()
3045 actionStart:
3046 args: [option3]
3047 lua: ToggleAutoBrake()
3048 actionStart:
3049 args: [option4]
3050 lua: ToggleAutoPilot()
3051 actionStart:
3052 args: [option5]
3053 lua: ToggleTurnBurn()
3054 actionStart:
3055 args: [option6]
3056 lua: |
3057 ToggleFuelPanels()
3058 if UnitHidden then
3059 unit.show()
3060 UnitHidden = false
3061 else
3062 unit.hide()
3063 UnitHidden = true
3064 end
3065 actionStart:
3066 args: [option7]
3067 lua: saveVariables()
3068 actionStart:
3069 args: [lshift]
3070 lua: |
3071 if system.isViewLocked() == 1 then
3072 HoldingCtrl = true
3073 PrevViewLock = system.isViewLocked()
3074 system.lockView(1)
3075 end
3076 actionStop:
3077 args: [lshift]
3078 lua: |
3079 if system.isViewLocked() == 1 then
3080 HoldingCtrl = false
3081 simulatedX = 0
3082 simulatedY = 0 -- Reset for steering purposes
3083 system.lockView(PrevViewLock)
3084 end
3085 actionStart:
3086 args: [brake]
3087 lua: |
3088 if brakeToggle == "true" then
3089 BrakeToggle()
3090 elseif BrakeIsOn then
3091 BrakeToggle()
3092 else
3093 brakeInput = brakeInput + 1
3094 local longitudinalCommandType = Nav.axisCommandManager:getAxisCommandType(axisCommandId.longitudinal)
3095 if (longitudinalCommandType == axisCommandType.byTargetSpeed) then
3096 local targetSpeed = Nav.axisCommandManager:getTargetSpeed(axisCommandId.longitudinal)
3097 if (math.abs(targetSpeed) > constants.epsilon) then
3098 Nav.axisCommandManager:updateCommandFromActionStart(axisCommandId.longitudinal, - utils.sign(targetSpeed))
3099 end
3100 end
3101 end
3102 actionLoop:
3103 args: [brake]
3104 lua: |
3105 if brakeToggle ~= "true" then
3106 local longitudinalCommandType = Nav.axisCommandManager:getAxisCommandType(axisCommandId.longitudinal)
3107 if (longitudinalCommandType == axisCommandType.byTargetSpeed) then
3108 local targetSpeed = Nav.axisCommandManager:getTargetSpeed(axisCommandId.longitudinal)
3109 if (math.abs(targetSpeed) > constants.epsilon) then
3110 Nav.axisCommandManager:updateCommandFromActionLoop(axisCommandId.longitudinal, - utils.sign(targetSpeed))
3111 end
3112 end
3113 end
3114 actionStop:
3115 args: [brake]
3116 lua: |
3117 if brakeToggle ~= "true" then
3118 brakeInput = brakeInput - 1
3119 end
3120 actionStart:
3121 args: [lalt]
3122 lua: |
3123 if freeLookToggle == "true" then
3124 if system.isViewLocked() == 1 then
3125 system.lockView(0)
3126 else
3127 system.lockView(1)
3128 end
3129 end
3130 actionStart:
3131 args: [booster]
3132 lua: |
3133 --Nav:toggleBoosters()
3134 -- Dodgin's Don't Die Rocket Govenor - Cruise Control Edition
3135 isboosting = not isboosting
3136 if(isboosting) then unit.setEngineThrust('rocket_engine',1)
3137 else unit.setEngineThrust('rocket_engine',0)
3138 end
3139 actionStart:
3140 args: [stopengines]
3141 lua: Nav.axisCommandManager:resetCommand(axisCommandId.longitudinal)
3142 actionStart:
3143 args: [speedup]
3144 lua: |
3145 if not HoldingCtrl then
3146 Nav.axisCommandManager:updateCommandFromActionStart(axisCommandId.longitudinal, 5.0)
3147 else
3148 IncrementAutopilotTargetIndex()
3149 end
3150 actionLoop:
3151 args: [speedup]
3152 lua: |
3153 if not HoldingCtrl then
3154 Nav.axisCommandManager:updateCommandFromActionLoop(axisCommandId.longitudinal, 1.0)
3155 end
3156 actionStart:
3157 args: [speeddown]
3158 lua: |
3159 if not HoldingCtrl then
3160 Nav.axisCommandManager:updateCommandFromActionStart(axisCommandId.longitudinal, -5.0)
3161 else
3162 DecrementAutopilotTargetIndex()
3163 end
3164 actionLoop:
3165 args: [speeddown]
3166 lua: |
3167 if not HoldingCtrl then
3168 Nav.axisCommandManager:updateCommandFromActionLoop(axisCommandId.longitudinal, -1.0)
3169 end
3170 actionStart:
3171 args: [antigravity]
3172 lua: if antigrav ~= nil then antigrav.toggle() end
3173 actionStart:
3174 args: [warp]
3175 lua: if warpdrive ~= nil then warpdrive.activateWarp() end
3176© 2020 GitHub, Inc.
3177Terms
3178Privacy
3179Security
3180Status
3181Help
3182Contact GitHub
3183Pricing
3184API
3185Training
3186Blog
3187About
3188