diff --git a/library/lib_circle_nav.ks b/library/lib_circle_nav.ks
new file mode 100644
index 0000000..5927a60
--- /dev/null
+++ b/library/lib_circle_nav.ks
@@ -0,0 +1,53 @@
+// lib_circle_nav.ks provides a set of functions that use Great Circle equations.
+// Copyright © 2015,2019,2023 KSLib team
+// Lic. MIT
+@LAZYGLOBAL OFF.
+@CLOBBERBUILTINS OFF.
+
+//use to find the initial bearing for the shortest path around a sphere from...
+function circle_bearing {
+ parameter
+ p1, //...this point...
+ p2. //...to this point.
+
+ return mod(360+arctan2(sin(p2:lng-p1:lng)*cos(p2:lat),cos(p1:lat)*sin(p2:lat)-sin(p1:lat)*cos(p2:lat)*cos(p2:lng-p1:lng)),360).
+}.
+
+//use to find where you will end up if you travel from...
+function circle_destination {
+ parameter
+ p1, //...this point...
+ b, // ...with this as your initial bearing...
+ d, // ...for this distance...
+ radius. // ...around a sphere of this radius.
+
+ local lat is arcsin(sin(p1:lat)*cos((d*180)/(radius*constant():pi))+cos(p1:lat)*sin((d*180)/(radius*constant():pi))*cos(b)).
+ local lng is 0.
+ if abs(Lat) <> 90 {
+ set lng to p1:lng+arctan2(sin(b)*sin((d*180)/(radius*constant():pi))*cos(p1:lat),cos((d*180)/(radius*constant():pi))-sin(p1:lat)*sin(lat)).
+ }.
+
+ return latlng(lat,lng).
+}.
+
+//use to find the distance from...
+function circle_distance {
+ parameter
+ p1, //...this point...
+ p2, //...to this point...
+ radius. //...around a body of this radius. (note: if you are flying you may want to use ship:body:radius + altitude).
+ local A is sin((p1:lat-p2:lat)/2)^2 + cos(p1:lat)*cos(p2:lat)*sin((p1:lng-p2:lng)/2)^2.
+
+ return radius*constant():PI*arctan2(sqrt(A),sqrt(1-A))/90.
+}.
+
+//use to find the mid point on the outside of a sphere between...
+function circle_midpoint {
+ parameter
+ p1, //...this point...
+ p2. //...and this point.
+ local A is cos(p2:lat)*cos(p2:lng-p1:lng).
+ local B is cos(p2:lat)*sin(p2:lng-P1:lng).
+
+ return latlng(arctan2(sin(p1:lat)+sin(p2:lat),sqrt((cos(p1:lat)+resultA)^2+resultB^2)),p1:lng+arctan2(resultB,cos(p1:lat)+resultA)).
+}.
diff --git a/library/lib_enum.ks b/library/lib_enum.ks
new file mode 100644
index 0000000..7083384
--- /dev/null
+++ b/library/lib_enum.ks
@@ -0,0 +1,112 @@
+// lib_enum.ks provides a set of functions for manipulating lists, queues, and stacks using the new delegates syntax introduced in kOS version 0.19.0.
+// Copyright © 2015,2016 KSLib team
+// Lic. MIT
+
+{global Enum is lex(
+"version", "0.1.1",
+"all", all@,
+"any", any@,
+"count", count@,
+"each", each@,
+"each_slice", each_slice@,
+"each_with_index", each_with_index@,
+"find", find@,
+"find_index", find_index@,
+"group_by", group_by@,
+"map", map@,
+"map_with_index", map_with_index@,
+"max", _max@,
+"min", _min@,
+"partition", partition@,
+"reduce", reduce@,
+"reject", reject@,
+"reverse", reverse@,
+"select", select@,
+"sort", sort@
+).
+
+local y is true. local n is false.
+
+function cast{
+ parameter a,b,p is 0.
+ if a:typename=b return a.
+ if b="List"{set p to list(). for i in a p:add(i). return p.}
+ if b="Queue"{set p to queue(). for i in a p:push(i). return p.}
+ if b="Stack"{
+ local l is stack().
+ set p to stack(). for i in a p:push(i).
+ for i in p l:push(i). return l.}}
+
+function to_l{parameter c. return cast(c,"List").}
+
+function all{parameter l,c. for i in l if not c(i) return n. return y.}
+
+function any{parameter l,c. for i in l if c(i) return y. return n.}
+
+function count{
+ parameter l,c,p is 0. for i in l if c(i) set p to p+1. return p.}
+
+function each{parameter l,o. for i in l o(i).}
+
+function each_slice{
+ parameter l,m,o,c is to_l(l),i is 0.
+ until i > c:length-1 {
+ o(cast(c:sublist(i,min(m,c:length-1)),l:typename)). set i to i+m.}}
+
+function each_with_index{
+ parameter l,o,i is 0. for j in to_l(l) {o(j,i+1). set i to i+1.}}
+
+function find{parameter l,c. for i in l if c(i) return i. return n.}
+
+function find_index{
+ parameter l,c,i is 0. for j in to_l(l) {if c(j) return i. set i to i+1.}
+ return -1.}
+
+function group_by{parameter l,t,p is lex(). for i in l{
+ local u is t(i). if p:haskey(u) p[u]:add(i). else set p[u] to list(i).}
+ for k in p:keys set p[k] to cast(p[k],l:typename). return p.}
+
+function map{
+ parameter l,t,p is list(). for i in to_l(l) p:add(t(i)).
+ return cast(p,l:typename).}
+
+function map_with_index{parameter l,t,p is list(), i is 0, c is to_l(l).
+ until i=c:length { p:add(t(c[i],i+1)). set i to i+1. }
+ return cast(p,l:typename).}
+
+function _max{parameter l,c is to_l(l). if c:length=0 return n.
+ local p is c[0]. for i in c if i>p set p to i. return p.}
+
+function _min{parameter l, c is to_l(l). if c:length=0 return n.
+ local p is c[0]. for i in c if i
=0 break.}
+ if i 255 {
+ set tmpNum to tmpNum - 128.
+ set carry to true.
+ }
+ local subStringHigh IS _exec_idString:substring(i,_exec_idString:LENGTH - i).
+ set subStringHigh to subStringHigh:remove(0,1).
+ local subStringLow is _exec_idString:substring(0,i).
+ set _exec_idString to subStringLow + char(tmpNum) + subStringHigh.
+
+ }
+ if carry { set _exec_idString to char(128) + _exec_idString. }
+ //end of string incrementing
+ set filePath to path("1:/_execute_" + _exec_idString + ".tmp").
+ _past_exec_strings:ADD(command,filePath).
+ }
+ log_run_del(command,filePath).
+}
+
+function evaluate {
+ parameter expression.
+
+ execute("global _evaluate_result is " + expression + ".").
+ local result is _evaluate_result.
+ if defined _evaluate_result {
+ unset _evaluate_result.
+ }
+ return result.
+}
+
+function evaluate_function {
+ parameter
+ function_name,
+ parameter_list.
+
+ global _exec__param_list is parameter_list.
+ local expression is "".
+ local separator is "".
+ local index is 0.
+ until index = parameter_list:length {
+ set expression to expression + separator + "_exec__param_list[" + index + "]".
+ set separator to ", ".
+ set index to index + 1.
+ }
+ set expression to function_name + "(" + expression + ")".
+ local result is evaluate(expression).
+ if defined _exec__param_list {
+ unset _exec__param_list.
+ }
+ return result.
+}
+
+function get_suffix {
+ parameter
+ structure, //the structure to get the suffix of
+ suffix, //the suffix to get
+ parameter_list IS false. //if the suffix is a function call this is the list of parameters for the suffix
+
+ local filePath is "1:/_get_suffix" + suffix.
+ local logStr IS "global _evaluate_result is { parameter o. return o:" + suffix.
+ if parameter_list:istype("list") {
+ set filePath to filePath + parameter_list:length.
+ local separator is "(".
+ global _exec__param_list IS parameter_list.
+ local i IS 0.
+ until i >= parameter_list:length
+ {
+ set logStr to logStr + separator + "_exec__param_list[" + i + "]".
+ set separator to ", ".
+ set i to i + 1.
+ }
+ set logStr to logStr + ")".
+ }
+ set filePath to path(filePath + ".tmp").
+ log_run_del(logStr + ". }.",filePath).
+ local result is _evaluate_result:call(structure).
+ if defined _exec__param_list {
+ unset _exec__param_list.
+ }
+ return result.
+}
+
+function set_suffix {
+ parameter
+ structure, //the structure to set the suffix of
+ suffix, //the suffix to set
+ val. //the value to set the suffix to
+
+ local filePath is path("1:/_set_suffix" + suffix + ".tmp").
+ local logStr IS "global _evaluate_result is { parameter o,s. set o:" + suffix + " to s. }.".
+ log_run_del(logStr,filePath).
+ local result is _evaluate_result:call(structure,val).
+ unset _evaluate_result.
+ return result.
+}
+
+local function log_run_del {
+ parameter
+ log_string,//the string to be executed
+ file_path. //the path to where the string should be stored temporarily so it can be executed.
+
+ if exists(file_path) {
+ deletepath(file_path).
+ }
+ log log_string to file_path.
+ wait 0.
+ runpath(file_path).
+ deletepath(file_path).
+}
diff --git a/library/lib_file_exists.ks b/library/lib_file_exists.ks
new file mode 100644
index 0000000..4b6d967
--- /dev/null
+++ b/library/lib_file_exists.ks
@@ -0,0 +1,20 @@
+// lib_file_exists.ks is a means of checking for optional dependencies, or checking whether a file exists before attempting to run it.
+// Copyright © 2015,2023 KSLib team
+// Lic. MIT
+
+@LAZYGLOBAL off.
+@CLOBBERBUILTINS off.
+
+function file_exists{
+ parameter fileName.
+ local fileList is list().
+ local found is false.
+ list files in fileList.
+ for file in fileList {
+ if file = fileName {
+ set found to true.
+ break.
+ }
+ }
+ return found.
+}
diff --git a/library/lib_geodec.ks b/library/lib_geodec.ks
new file mode 100644
index 0000000..eff936c
--- /dev/null
+++ b/library/lib_geodec.ks
@@ -0,0 +1,35 @@
+// lib_geodec.ks provides two functions to convert between geographic coordinates (latitude, longitude) and cartesian coordinates (x, y, z).
+// Copyright © 2015,2019,2023 KSLib team
+// Lic. MIT
+@LAZYGLOBAL off.
+@CLOBBERBUILTINS off.
+
+function geo2dec {
+ parameter
+ _____vlat, //geoposition:lat
+ _____vlng, //geoposition:lng
+ _____valt. //altitude
+ local _____talt is body:radius+_____valt.
+ local _____tlat is _____vlat+90.
+ local _____tsin is sin(_____tlat).
+ local _____vdec is list(
+ _____talt*_____tsin*cos(_____vlng),
+ _____talt*_____tsin*sin(_____vlng),
+ _____talt*cos(_____tlat)
+ ).
+ return _____vdec.
+}.
+
+function dec2geo {
+ parameter
+ _____vx, //x from geo2dec array[0]
+ _____vy, //y from geo2dec array[1]
+ _____vz. //z from geo2dec array[2]
+ local _____tsqrt is sqrt(_____vx*_____vx+_____vy*_____vy+_____vz*_____vz).
+ local _____vgeo is list(
+ arccos(_____vz/_____tsqrt)-90,
+ arctan2(_____vy,_____vx),
+ _____tsqrt-body:radius
+ ).
+ return _____vgeo.
+}.
diff --git a/library/lib_gui_box.ks b/library/lib_gui_box.ks
new file mode 100644
index 0000000..75f8fc2
--- /dev/null
+++ b/library/lib_gui_box.ks
@@ -0,0 +1,68 @@
+// lib_gui_box.ks contains functions to draw boxes in the terminal.
+// Copyright © 2015,2019 KSLib team
+// Lic. MIT
+// This file is based on lib_window.ks from akrOS by akrasuski1.
+
+function draw_custom_gui_box {
+ parameter
+ x, y, w, h,
+ horizontal_char,
+ vertical_char,
+ corner_char.
+
+// Start Input Sanitization
+
+ if x < 0 or x >= terminal:width {
+ set x to max(0,min(terminal:width - 1,x)).
+ HUDTEXT("Error: [draw_custom_gui_box] X value outside terminal.", 10, 2, 30, RED, FALSE).
+ }
+
+ if y < 0 or y >= (terminal:height - 1) {
+ set y to max(0,min(terminal:height - 2,y)).
+ HUDTEXT("Error: [draw_custom_gui_box] Y value outside terminal", 10, 2, 30, RED, FALSE).
+ }
+
+ if w < 1 or x + w > terminal:width {
+ set w to max(1,min(terminal:width - x,w)).
+ HUDTEXT("Error: [draw_custom_gui_box] W value outside terminal.", 10, 2, 30, RED, FALSE).
+ }
+
+ if h < 1 or y + h >= terminal:height {
+ set h to max(1,min(terminal:height - 1 - y,h)).
+ HUDTEXT("Error: [draw_custom_gui_box] H value outside terminal.", 10, 2, 30, RED, FALSE).
+ }
+
+ // End Input Sanitization
+
+ local horizontal_str is "".
+ local i is 1.
+ until i > w {
+ if i = 1 or i = w {
+ set horizontal_str to horizontal_str + corner_char.
+ } else {
+ set horizontal_str to horizontal_str + horizontal_char.
+ }
+ set i to i + 1.
+ }
+ print horizontal_str at(x, y).
+ print horizontal_str at(x, y + h - 1).
+ set i to 1.
+ until i >= h - 1 {
+ print vertical_char at(x , y + i).
+ print vertical_char at(x + w - 1, y + i).
+ set i to i + 1.
+ }
+}
+
+function draw_gui_box {
+ parameter
+ x, y, w, h.
+ draw_custom_gui_box(x, y, w, h, "-", "|", "+").
+}
+
+function draw_one_char_gui_box {
+ parameter
+ x, y, w, h,
+ border_char.
+ draw_custom_gui_box(x, y, w, h, border_char, border_char, border_char).
+}
diff --git a/library/lib_hyperbolic_trigonometry.ks b/library/lib_hyperbolic_trigonometry.ks
new file mode 100644
index 0000000..25189ed
--- /dev/null
+++ b/library/lib_hyperbolic_trigonometry.ks
@@ -0,0 +1,45 @@
+// lib_hyperbolic_trigonometry.ks provides the usual hyperbolic functions and their inverses.
+// Copyright © 2021,2023 KSLib team
+// Lic. MIT
+
+@lazyglobal off.
+@clobberbuiltins off.
+
+function cosh {
+ parameter x.
+
+ local toRadian to x * constant:degToRad.
+ return (constant:e^toRadian + constant:e^(-toRadian)) / 2.
+}
+
+function sinh {
+ parameter x.
+
+ local toRadian to x * constant:degToRad.
+ return (constant:e^toRadian - constant:e^(-toRadian)) / 2.
+}
+
+function tanh {
+ parameter x.
+
+ local toRadian to x * constant:degToRad.
+ return (1 - constant:e^(-2 * toRadian)) / (1 + constant:e^(-2 * toRadian)).
+}
+
+function arccosh {
+ parameter n.
+
+ return ln(n + sqrt(n + 1) * sqrt(n - 1)) * constant:radToDeg.
+}
+
+function arcsinh {
+ parameter n.
+
+ return ln(n + sqrt(1 + n^2)) * constant:radToDeg.
+}
+
+function arctanh {
+ parameter n.
+
+ return (ln((1 + n) / (1 - n)) / 2) * constant:radToDeg.
+}
diff --git a/library/lib_input_string.ks b/library/lib_input_string.ks
new file mode 100644
index 0000000..75d91f5
--- /dev/null
+++ b/library/lib_input_string.ks
@@ -0,0 +1,182 @@
+// lib_input_string.ks provides a method of inputting custom strings while a script is running via an on-screen keyboard.
+// Copyright © 2015,2019,2023 KSLib team
+// Lic. MIT
+@LAZYGLOBAL off.
+@CLOBBERBUILTINS off.
+
+function input_string
+{
+ parameter
+ line, // top edge of the keyboard
+ x0, y0, // where to display the input on screen
+ hidden, // set to true to display "*" instead of character
+ help. //true or false (the info above the keyboard).
+
+////////////////// internal functions ////////////////////////////////
+
+ function refresh_board // this is done as 1 large print as drawing the boxes individually lags.
+ {
+ if shift = 0 {
+ print "+---+---+---+---+---+---+---+---+---+---+---+---+---+" at (0,line).
+ print "| ` | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 0 | - | = |" at (0,line+1).
+ print "+---+---+---+---+---+---+---+---+---+---+---+---+---+" at (0,line+2).
+ print "| q | w | e | r | t | y | u | i | o | p | [ | ] |Ent|" at (0,line+3).
+ print "+---+---+---+---+---+---+---+---+---+---+---+---+---+" at (0,line+4).
+ print "| a | s | d | f | g | h | j | k | l | ; | ' | # |Cap|" at (0,line+5).
+ print "+---+---+---+---+---+---+---+---+---+---+---+---+---+" at (0,line+6).
+ print "| \ | z | x | c | v | b | n | m | , | . | / | |" at (0,line+7).
+ print "+---+---+---+---+---+---+---+---+---+---+---+---+" at (0,line+8).
+ } else if shift = 1 {
+ print "+---+---+---+---+---+---+---+---+---+---+---+---+---+" at (0,line).
+ print "|N/A| ! | "+quote+" |N/A| $ | % | ^ | & | * | ( | ) | _ | + |" at (0,line+1).
+ print "+---+---+---+---+---+---+---+---+---+---+---+---+---+" at (0,line+2).
+ print "| Q | W | E | R | T | Y | U | I | O | P | { | } |Ent|" at (0,line+3).
+ print "+---+---+---+---+---+---+---+---+---+---+---+---+---+" at (0,line+4).
+ print "| A | S | D | F | G | H | J | K | L | : | @ | ~ |Cap|" at (0,line+5).
+ print "+---+---+---+---+---+---+---+---+---+---+---+---+---+" at (0,line+6).
+ print "| | | Z | X | C | V | B | N | M | < | > | ? | |" at (0,line+7).
+ print "+---+---+---+---+---+---+---+---+---+---+---+---+" at (0,line+8).
+ }
+ refresh_position("N/A",0).
+ }
+
+ function refresh_position
+ {
+ parameter dir, increment.
+ if dir = "row" {
+ set row to mod(row+increment+keyboard[shift]:length, keyboard[shift]:length).
+ set col to mod(col+keyboard[shift][row]:length, keyboard[shift][row]:length).
+ } else if dir = "col" {
+ set col to mod(col+increment+keyboard[shift][row]:length, keyboard[shift][row]:length).
+ }
+ print "+---+" at (4*oldCol,2*oldRow+line).
+ print "|" at (4*oldCol,2*oldRow+line+1).
+ print "|" at (4*(oldCol+1),2*oldRow+line+1).
+ print "+---+" at (4*oldCol,2*oldRow+line+2).
+ print "#===#" at (4*col,2*row+line).
+ print "H" at (4*col,2*row+line+1).
+ print "H" at (4*(col+1),2*row+line+1).
+ print "#===#" at (4*col,2*row+line+2).
+ set oldrow to row.
+ set oldCol to col.
+ }
+
+ function string_add {
+ if keyboard[shift][row][col] = "Cap" {
+ set shift to mod(shift+1,2).
+ refresh_board().
+ } else if keyboard[shift][row][col] = "Ent" {
+ set Enter to True.
+ } else {
+ string:add(keyboard[shift][row][col]).
+ if hidden {
+ print "*" at (x0+string:length+2,y0).
+ } else {
+ print keyboard[shift][row][col] at (x0+string:length+2,y0).
+ }
+ }
+ }
+ function string_remove {
+ if not string:empty {
+ string:remove(string:length-1).
+ print " " at (x0+string:length+3,y0).
+ }
+ }
+
+
+/////////////function body/////////////////////////////////////
+
+ local col is 0.
+ local row is 0.
+ local oldCol is 0.
+ local oldRow is 0.
+ local shift is 0.
+ local controlMap is ship:control.
+ local oldTop is controlMap:pilottop.
+ local oldStar is controlMap:pilotstarboard.
+ local oldFore is controlMap:pilotfore.
+ local Enter is False.
+ local string is list().
+ local returnString is "".
+ local charr is 0.
+ local oldT is 0.
+ local blink is false.
+ local quote is char(34).
+
+ local keyboard is list(
+ list( //lower case.
+ list("`","1","2","3","4","5","6","7","8","9","0","-","="),
+ list("q","w","e","r","t","y","u","i","o","p","[","]","Ent"),
+ list("a","s","d","f","g","h","j","k","l",";","'","#","Cap"),
+ list("\","z","x","c","v","b","n","m",",",".","/"," ")
+ ),
+ list( //upper case.
+ list("¬","!",quote,"£","$","%","^","&","*","(",")","_","+"),
+ list("Q","W","E","R","T","Y","U","I","O","P","{","}","Ent"),
+ list("A","S","D","F","G","H","J","K","L",":","@","~","Cap"),
+ list("|","Z","X","C","V","B","N","M","<",">","?"," ")
+ )
+ ).
+
+ local oldHeight is terminal:height.
+ local oldWidth is terminal:width.
+ if oldwidth < 53 {
+ set terminal:width to 53.
+ }
+ if oldHeight < line+9 {
+ set terminal:height to line+9.
+ }
+ refresh_board().
+ if help {
+ Print "Navigate using the IJKL keys. Use the H and N" at (3,line-3).
+ Print "keys to add and remove characters respectively." at (3,line-2).
+ }.
+
+ until Enter
+ {
+ if oldt + 0.3 < time {
+ if blink {
+ print " " at (x0+string:length+3,y0).
+ toggle blink.
+ } else {
+ print "_" at (x0+string:length+3,y0).
+ toggle blink.
+ }
+ set oldT to time.
+ }.
+ if (controlMap:PILOTTOP <> oldTop) or (controlMap:PILOTSTARBOARD <> oldStar) or (controlMap:PILOTFORE <> oldFore)
+ {
+ if help {
+ print " " at (3,line-3).
+ print " " at (3,line-2).
+ set help to false.
+ }
+ if controlMap:pilottop > 0
+ {
+ refresh_position("row",+1).
+ } else if controlMap:pilottop < 0 {
+ refresh_position("row", -1).
+ }
+ if controlMap:pilotstarboard > 0 {
+ refresh_position("col", +1).
+ } else if controlMap:pilotstarboard < 0 {
+ refresh_position("col", -1).
+ }
+ if controlMap:pilotfore > 0 {
+ string_add().
+ } else if controlMap:pilotfore < 0 {
+ string_remove().
+ }
+ SET oldTop TO controlMap:PILOTTOP.
+ SET oldStar TO controlMap:PILOTSTARBOARD.
+ SET oldFore TO controlMap:PILOTFORE.
+ }
+ }
+ print " " at (x0+string:length+3,y0).
+ set terminal:width to oldWidth.
+ set terminal:height to oldHeight.
+ for charr IN string {
+ set returnString to returnString + charr.
+ }
+ return returnString.
+}
diff --git a/library/lib_input_terminal.ks b/library/lib_input_terminal.ks
new file mode 100644
index 0000000..452c508
--- /dev/null
+++ b/library/lib_input_terminal.ks
@@ -0,0 +1,196 @@
+// lib_input_terminal.ks provides functions for getting user entered strings or numbers though terminal inputs.
+// Copyright © 2020,2023 KSLib team
+// Lic. MIT
+@LAZYGLOBAL OFF.
+@CLOBBERBUILTINS OFF.
+
+LOCAL termIn IS TERMINAL:INPUT.
+LOCAL backChar IS termIn:BACKSPACE.
+LOCAL delChar IS termIn:DELETERIGHT.
+LOCAL enterChar IS termIn:ENTER.
+LOCAL bellChar IS CHAR(7).
+
+FUNCTION terminal_input_string {
+ PARAMETER col,
+ row,
+ maxLength IS (TERMINAL:WIDTH - col),
+ inValue IS "",
+ cursorBlink IS TRUE.
+
+ IF inValue:ISTYPE("scalar") {
+ SET inValue TO scalar_to_string(inValue).
+ }
+ RETURN input_loop(col,row,maxLength,0,inValue,cursorBlink,string_concatnation@).
+}
+
+FUNCTION terminal_input_number {
+ PARAMETER col,
+ row,
+ maxLength IS (TERMINAL:WIDTH - col),
+ inValue IS " ",
+ cursorBlink IS TRUE.
+
+ IF inValue:ISTYPE("string") {
+ IF inValue:TOSCALAR(0) = 0 {
+ SET inValue TO " ".
+ }
+ }
+ IF inValue:ISTYPE("scalar") {
+ SET inValue TO scalar_to_string(inValue).
+ }
+
+ IF (inValue:LENGTH < 1) OR (NOT inValue[0]:MATCHESPATTERN("[ -]")) {
+ SET inValue TO " ".
+ }
+ RETURN number_protect(input_loop(col,row,maxLength,1,inValue,cursorBlink,number_concatnation@)).
+}
+
+FUNCTION input_loop {
+ PARAMETER col,
+ row,
+ maxLength,
+ minLength,
+ workingStr,
+ cursorBlink,
+ concatenator.
+
+ IF workingStr:LENGTH > maxLength {
+ SET workingStr TO workingStr:SUBSTRING(0,maxLength).
+ }
+
+ LOCAL blinkInter IS CHOOSE 0.5 IF cursorBlink ELSE 1000.
+ LOCAL blinkNextState IS TIME:SECONDS.
+ LOCAL blinkChar IS "_".
+
+ LOCAL doPrint IS TRUE.
+ LOCAL done IS FALSE.
+ UNTIL done {
+ IF blinkNextState < TIME:SECONDS {
+ SET blinkNextState TO blinkNextState + blinkInter.
+ IF cursorBlink {
+ SET blinkChar TO CHOOSE "█" IF blinkChar <> "█" ELSE "_".
+ SET doPrint TO TRUE.
+ }
+ }
+ IF termIn:HASCHAR {
+ LOCAL cha IS termIn:GETCHAR().
+ SET doPrint TO TRUE.
+
+ IF cha = backChar {
+ IF workingStr:LENGTH > minLength {
+ SET workingStr TO workingStr:REMOVE(workingStr:LENGTH - 1,1).
+ } ELSE {
+ PRINT bellChar.
+ }
+ } ELSE IF cha = delChar {
+ SET workingStr TO "".
+ PRINT bellChar.
+ } ELSE IF cha = enterChar {
+ SET done TO TRUE.
+ SET blinkChar TO " ".
+ } ELSE {
+ SET workingStr TO concatenator(workingStr,cha,maxLength).
+ }
+ }
+ IF doPrint {
+ SET doPrint TO FALSE.
+ LOCAL padChar IS (CHOOSE blinkChar IF workingStr:LENGTH < maxLength ELSE "").
+ PRINT (workingStr + padChar):PADRIGHT(maxLength) AT(col,row).
+ }
+ WAIT UNTIL (termIn:HASCHAR) OR (blinkNextState < TIME:SECONDS) OR done.
+ }
+ RETURN workingStr.
+}
+
+LOCAL FUNCTION scalar_to_string {
+ PARAMETER scalar.
+ LOCAL signChar IS CHOOSE "-" IF scalar < 0 ELSE " ".
+ LOCAL returnStr TO ABS(scalar):TOSTRING().
+ IF returnStr:CONTAINS("E") {
+ LOCAL strSplit IS returnStr:SPLIT("E").
+ LOCAL mantissa IS strSplit[0].
+ LOCAL exponent IS strSplit[1]:TOSCALAR().
+ IF mantissa:CONTAINS(".") {
+ LOCAL splitMant IS mantissa:SPLIT(".").
+ SET mantissa TO splitMant[0] + splitMant[1].
+ SET exponent TO exponent - splitMant[1]:LENGTH.
+ }
+ IF exponent < 0 {
+ SET returnStr TO "0." + (" ":PADRIGHT(ABS(exponent + 1))):REPLACE(" ","0") + mantissa.
+ } ELSE IF exponent > 0 {
+ SET returnStr TO mantissa + (" ":PADRIGHT(exponent)):REPLACE(" ","0").
+ } ELSE {
+ SET returnStr TO mantissa.
+ }
+ }
+ RETURN signChar + returnStr.
+}
+
+LOCAL FUNCTION number_protect {
+ PARAMETER curentStr.
+ IF curentStr:LENGTH <= 1 {
+ RETURN " 0".
+ }
+ IF curentStr[curentStr:LENGTH - 1] = "." {
+ RETURN number_protect(curentStr:REMOVE(curentStr:LENGTH - 1,1)).
+ }
+ IF curentStr:TOSCALAR(0) = 0 {
+ RETURN " 0".
+ }
+ RETURN curentStr.
+}
+
+LOCAL FUNCTION number_concatnation {
+ PARAMETER curentStr,//expects " " as the base string to start with
+ cha,
+ maxLength.
+ IF curentStr:LENGTH < 1 {
+ SET curentStr TO " ".
+ }
+ IF cha:MATCHESPATTERN("[0-9-.+]") {
+ IF curentStr:LENGTH < maxLength {
+ IF cha:MATCHESPATTERN("[0-9]") {
+ RETURN curentStr + cha.
+ } ELSE IF cha = "." {
+ IF NOT curentStr:CONTAINS(".") {
+ RETURN curentStr + cha.
+ }
+ }
+ }
+ IF cha = "-" OR cha = "+" {
+ IF curentStr:CONTAINS("-") OR cha = "+" {
+ RETURN " " + curentStr:REMOVE(0,1).
+ } ELSE {
+ RETURN cha + curentStr:REMOVE(0,1).
+ }
+ }
+ }
+ PRINT bellChar.
+ RETURN curentStr.
+}
+
+LOCAL toIgnore IS LIST(
+ CHAR(127),
+ delChar,
+ backChar,
+ termIn:UPCURSORONE,
+ termIn:DOWNCURSORONE,
+ termIn:LEFTCURSORONE,
+ termIn:RIGHTCURSORONE,
+ termIn:HOMECURSOR,
+ termIn:ENDCURSOR,
+ termIn:PAGEDOWNCURSOR,
+ termIn:PAGEUPCURSOR
+).
+LOCAL FUNCTION string_concatnation {
+ PARAMETER curentStr,
+ cha,
+ maxLength.
+ IF (UNCHAR(cha) > 31) AND (NOT toIgnore:CONTAINS(cha)) {
+ IF curentStr:LENGTH < maxLength {
+ RETURN curentStr + cha.
+ }
+ }
+ PRINT bellChar.
+ RETURN curentStr.
+}
\ No newline at end of file
diff --git a/library/lib_lazcalc.ks b/library/lib_lazcalc.ks
new file mode 100644
index 0000000..7601818
--- /dev/null
+++ b/library/lib_lazcalc.ks
@@ -0,0 +1,101 @@
+// lib_lazcalc.ks - provides the user with a launch azimuth based on a desired target orbit altitude and inclination and can continued to be used throughout ascent to update the heading. It bases this calculation on the vessel's launch and current geoposition.
+// Copyright © 2015,2017,2023 KSLib team
+// Lic. MIT
+
+//~~Version 2.2~~
+//~~Created by space-is-hard~~
+//~~Updated by TDW89~~
+//~~Auto north/south switch by undercoveryankee~~
+
+//To use: RUN LAZcalc.ks. SET data TO LAZcalc_init([desired circular orbit altitude in meters],[desired orbital inclination; negative if launching from descending node, positive otherwise]). Then loop SET myAzimuth TO LAZcalc(data).
+
+@LAZYGLOBAL OFF.
+@CLOBBERBUILTINS OFF.
+
+FUNCTION LAZcalc_init {
+ PARAMETER
+ desiredAlt, //Altitude of desired target orbit (in *meters*)
+ desiredInc. //Inclination of desired target orbit
+
+ PARAMETER autoNodeEpsilon IS 10. // How many m/s north or south
+ // will be needed to cause a north/south switch. Pass zero to disable
+ // the feature.
+ SET autoNodeEpsilon to ABS(autoNodeEpsilon).
+
+ //We'll pull the latitude now so we aren't sampling it multiple times
+ LOCAL launchLatitude IS SHIP:LATITUDE.
+
+ LOCAL data IS LIST(). // A list is used to store information used by LAZcalc
+
+ //Orbital altitude can't be less than sea level
+ IF desiredAlt <= 0 {
+ PRINT "Target altitude cannot be below sea level".
+ SET launchAzimuth TO 1/0. //Throws error
+ }.
+
+ //Determines whether we're trying to launch from the ascending or descending node
+ LOCAL launchNode TO "Ascending".
+ IF desiredInc < 0 {
+ SET launchNode TO "Descending".
+
+ //We'll make it positive for now and convert to southerly heading later
+ SET desiredInc TO ABS(desiredInc).
+ }.
+
+ //Orbital inclination can't be less than launch latitude or greater than 180 - launch latitude
+ IF ABS(launchLatitude) > desiredInc {
+ SET desiredInc TO ABS(launchLatitude).
+ HUDTEXT("Inclination impossible from current latitude, setting for lowest possible inclination.", 10, 2, 30, RED, FALSE).
+ }.
+
+ IF 180 - ABS(launchLatitude) < desiredInc {
+ SET desiredInc TO 180 - ABS(launchLatitude).
+ HUDTEXT("Inclination impossible from current latitude, setting for highest possible inclination.", 10, 2, 30, RED, FALSE).
+ }.
+
+ //Does all the one time calculations and stores them in a list to help reduce the overhead or continuously updating
+ LOCAL equatorialVel IS (2 * CONSTANT():Pi * BODY:RADIUS) / BODY:ROTATIONPERIOD.
+ LOCAL targetOrbVel IS SQRT(BODY:MU/ (BODY:RADIUS + desiredAlt)).
+ data:ADD(desiredInc). //[0]
+ data:ADD(launchLatitude). //[1]
+ data:ADD(equatorialVel). //[2]
+ data:ADD(targetOrbVel). //[3]
+ data:ADD(launchNode). //[4]
+ data:ADD(autoNodeEpsilon). //[5]
+ RETURN data.
+}.
+
+FUNCTION LAZcalc {
+ PARAMETER
+ data. //pointer to the list created by LAZcalc_init
+ LOCAL inertialAzimuth IS ARCSIN(MAX(MIN(COS(data[0]) / COS(SHIP:LATITUDE), 1), -1)).
+ LOCAL VXRot IS data[3] * SIN(inertialAzimuth) - data[2] * COS(data[1]).
+ LOCAL VYRot IS data[3] * COS(inertialAzimuth).
+
+ // This clamps the result to values between 0 and 360.
+ LOCAL Azimuth IS MOD(ARCTAN2(VXRot, VYRot) + 360, 360).
+
+ IF data[5] {
+ LOCAL NorthComponent IS VDOT(SHIP:VELOCITY:ORBIT, SHIP:NORTH:VECTOR).
+ IF NorthComponent > data[5] {
+ SET data[4] TO "Ascending".
+ } ELSE IF NorthComponent < -data[5] {
+ SET data[4] to "Descending".
+ }.
+ }.
+
+ //Returns northerly azimuth if launching from the ascending node
+ IF data[4] = "Ascending" {
+ RETURN Azimuth.
+
+ //Returns southerly azimuth if launching from the descending node
+ } ELSE IF data[4] = "Descending" {
+ IF Azimuth <= 90 {
+ RETURN 180 - Azimuth.
+
+ } ELSE IF Azimuth >= 270 {
+ RETURN 540 - Azimuth.
+
+ }.
+ }.
+}.
diff --git a/library/lib_list_dialog.ks b/library/lib_list_dialog.ks
new file mode 100644
index 0000000..a2a98e7
--- /dev/null
+++ b/library/lib_list_dialog.ks
@@ -0,0 +1,58 @@
+// lib_list_dialog.ks helps when you want the user to choose a value from a long list.
+// Copyright © 2015,2023 KSLib team
+// Lic. MIT
+@LAZYGLOBAL OFF.
+@CLOBBERBUILTINS OFF.
+
+run lib_menu.
+
+function open_list_dialog {
+ parameter
+ title,
+ option_list.
+
+ local action_list is list("Next page", "Previous page").
+ return _list_dialog(title, option_list, action_list, -1).
+}
+
+function open_cancelable_list_dialog {
+ parameter
+ title,
+ option_list.
+
+ local action_list is list("Next page", "Previous page", "Cancel").
+ return _list_dialog(title, option_list, action_list, -2).
+}
+
+
+// this function is internal and should not be used outside lib_list_dialog
+function _list_dialog {
+ parameter
+ title,
+ option_list,
+ action_list,
+ internal_action_offset.
+
+ local inaccessible_lines is 11.
+ local page_start is 0.
+ local result is internal_action_offset.
+ until result > internal_action_offset {
+ local page_height is terminal:height - inaccessible_lines - action_list:length.
+ set page_start to page_start - mod(page_start, page_height).
+ local menu_list is action_list:copy.
+ for option in option_list:sublist(page_start, page_height) {
+ menu_list:add(option).
+ }
+ local title is title + " [Page: " + (page_start / page_height + 1) + " / " + (ceiling(option_list:length/page_height)) + "]".
+ set result to open_menu_indexed(title, menu_list) - action_list:length.
+ if result = internal_action_offset - 1 { // next
+ set page_start to min(page_start + page_height, option_list:length - 1).
+ } else if result = internal_action_offset { // previous
+ set page_start to max(0, page_start - page_height).
+ }
+ }
+ if result >= 0 {
+ set result to page_start + result.
+ }
+ return result.
+}
diff --git a/library/lib_location_constants.ks b/library/lib_location_constants.ks
new file mode 100644
index 0000000..da4452c
--- /dev/null
+++ b/library/lib_location_constants.ks
@@ -0,0 +1,119 @@
+// lib_location_constants.ks provides geolocations for places that would be relatively easy to locate for players flying manually (i.e., without the use of scripting mods such as kOS).
+// Copyright © 2020,2023 KSLib team
+// Lic. MIT
+@LAZYGLOBAL off.
+@CLOBBERBUILTINS off.
+
+if not (defined location_constants) {
+global location_constants is lex().
+//runways should be added in this pattern "name_runway_##_start" where name is the name of the runway and ## is the runway number
+// If not added with this pattern then the automatic alias creation that add an end position based on the start of the same name but opposite number will fail
+
+if bodyexists("Sun") and body("Sun"):radius = 261_600_000 and bodyexists("Kerbin") {//check for if the craft is in the stock solar system
+ local kerbinLocations is lex().
+ // vertical landing locations
+ kerbinLocations:add("launchpad", Kerbin:GeoPositionLatLng(-0.0972, -74.5577)).
+ kerbinLocations:add("woomerang_launchpad", Kerbin:GeoPositionLatLng(45.2896, 136.1100)).
+ kerbinLocations:add("desert_launchpad", Kerbin:GeoPositionLatLng(-6.5604, -143.9500)).
+ kerbinLocations:add("VAB", Kerbin:GeoPositionLatLng(-0.0968, -74.6187)).
+ location_constants:add("launchpad",kerbinLocations["launchpad"]).
+
+ // horizontal landing locations
+ kerbinLocations:add("runway_09_start", Kerbin:GeoPositionLatLng(-0.0486, -74.7247)).
+ kerbinLocations:add("runway_09_overrun", Kerbin:GeoPositionLatLng(-0.0502, -74.4880)). // runway "lip"
+ kerbinLocations:add("runway_27_start", Kerbin:GeoPositionLatLng(-0.0502, -74.4925)).
+ kerbinLocations:add("runway_27_overrun", Kerbin:GeoPositionLatLng(-0.0486, -74.7292)). // runway "lip"
+ kerbinLocations:add("l1_runway_09_start", Kerbin:GeoPositionLatLng(-.0489, -74.7101)).
+ kerbinLocations:add("l1_runway_27_start", Kerbin:GeoPositionLatLng(-.0501, -74.5076)).
+ kerbinLocations:add("l2_runway_09_start", Kerbin:GeoPositionLatLng(-.0486, -74.7134)).
+ kerbinLocations:add("l2_runway_27_start", Kerbin:GeoPositionLatLng(-.0501, -74.5046)).
+ kerbinLocations:add("island_runway_09_start", Kerbin:GeoPositionLatLng(-1.5177, -71.9663)).
+ kerbinLocations:add("island_runway_27_start", Kerbin:GeoPositionLatLng(-1.5158, -71.8524)).
+ kerbinLocations:add("desert_runway_36_start", Kerbin:GeoPositionLatLng(-6.5998, -144.0409)).
+ kerbinLocations:add("desert_runway_18_start", Kerbin:GeoPositionLatLng(-6.4480, -144.0383)).
+
+ location_constants:add("runway_start",kerbinLocations["runway_09_start"]).
+ location_constants:add("reverse_runway_start",kerbinLocations["runway_27_start"]).
+
+ location_constants:add("kerbin",kerbinLocations).
+}
+
+// JNSQ locations
+if bodyexists("Sun") and body("Sun"):radius = 175_750_000 and bodyexists("Kerbin") {
+ local kerbinLocations is lex().
+ // vertical landing locations
+ kerbinLocations:add("launchpad", Kerbin:GeoPositionLatLng(-1.75133971218638E-08,-91.7839867917987)).
+ kerbinLocations:add("woomerang_launchpad", Kerbin:GeoPositionLatLng(45.2898572995131,136.109991969089)).
+ kerbinLocations:add("desert_launchpad", Kerbin:GeoPositionLatLng(-6.56014471162071,-143.949999962632)).
+ kerbinLocations:add("VAB", Kerbin:GeoPositionLatLng(-3.59779685817755E-05,-91.8082182694506)).
+ location_constants:add("launchpad",kerbinLocations["launchpad"]).
+
+ // horizontal landing locations
+ kerbinLocations:add("runway_09_start", Kerbin:GeoPositionLatLng(0.0177914364838286,-91.8485588846624)).
+ kerbinLocations:add("runway_09_overrun", Kerbin:GeoPositionLatLng(0.017771315430981,-91.7560165023844)). // runway "lip"
+ kerbinLocations:add("runway_27_start", Kerbin:GeoPositionLatLng(0.0177872750049561,-91.758043638339)).
+ kerbinLocations:add("runway_27_overrun", Kerbin:GeoPositionLatLng(0.0177981075373338,-91.8503058587583)). // runway "lip"
+ kerbinLocations:add("l1_runway_09_start", Kerbin:GeoPositionLatLng(0.0177201817416171,-91.8418633582038)).
+ kerbinLocations:add("l1_runway_27_start", Kerbin:GeoPositionLatLng(0.0177610480469539,-91.7644480917721)).
+ kerbinLocations:add("l2_runway_09_start", Kerbin:GeoPositionLatLng(0.0178308568526199,-91.8434577819091)).
+ kerbinLocations:add("l2_runway_27_start", Kerbin:GeoPositionLatLng(0.0178110871731228,-91.7632143441808)).
+ kerbinLocations:add("island_runway_09_start", Kerbin:GeoPositionLatLng(-1.5323269901046,-71.9321971014811)).
+ kerbinLocations:add("island_runway_27_start", Kerbin:GeoPositionLatLng(-1.53151635515729,-71.8874890013379)).
+ kerbinLocations:add("desert_runway_36_start", Kerbin:GeoPositionLatLng(-6.55057590520998,-144.040382288094)).
+ kerbinLocations:add("desert_runway_18_start", Kerbin:GeoPositionLatLng(-6.49233191848109,-144.039374890239)).
+
+ location_constants:add("runway_start",kerbinLocations["runway_09_start"]).
+ location_constants:add("reverse_runway_start",kerbinLocations["runway_27_start"]).
+
+ location_constants:add("kerbin",kerbinLocations).
+}
+
+// RealSolarSystem locations
+if bodyexists("Sun") and body("Sun"):radius = 696_342_000 and bodyexists("Earth") {
+ // local earthLocations is lex().
+ // TODO: populate at least the most commonly used of the RealSolarSystem locations.
+}
+
+// aliases
+until not aliasing(location_constants) {}.
+
+local function aliasing {
+ parameter locationConstants.
+ local didChange is false.
+ for key in locationConstants:keys {
+ local bodyLex is locationConstants[key].
+ if bodyLex:istype("lexicon") {
+ local bodyKeys is bodyLex:keys:copy.
+ for currentKey in bodyKeys {
+ if currentKey:contains("desert") {//desert to dessert aliasing
+ local alias to currentKey:replace("desert", "dessert").
+ if not bodyLex:haskey(alias) {
+ bodyLex:add(alias,bodyLex[currentKey]).
+ set didChange to true.
+ }
+ }
+
+ if currentKey:matchespattern("runway_\d{1,2}_start$") {//runway_##_end aliasing
+ local alias is currentKey:replace("start", "end").
+ // now find the key that `alias` is an alias of
+ if not bodyLex:haskey(alias) {
+ local splitKey is currentKey:split("_").
+ local currentNum is splitKey[splitKey:length - 2].
+ local reverseNum is MOD(currentNum:toscalar(0) + 18,36):tostring.
+ if reverseNum = "0" { set reverseNum to "36". }
+ local reverseStr is currentKey:replace(currentNum,reverseNum).
+ if not bodyLex:haskey(reverseStr) {
+ set reverseStr to currentKey:replace(currentNum,"0" + reverseNum).
+ }
+ if bodyLex:haskey(reverseStr) {
+ bodyLex:add(alias,bodyLex[reverseStr]).
+ set didChange to true.
+ }
+ }
+ }
+ }
+ }
+ }
+ return didChange.
+}
+}
diff --git a/library/lib_math.ks b/library/lib_math.ks
new file mode 100644
index 0000000..b6befd5
--- /dev/null
+++ b/library/lib_math.ks
@@ -0,0 +1,15 @@
+declare function lerp{
+ parameter a.
+ parameter b.
+ parameter t.
+ return a + (b - a) * t.
+}
+
+declare function map{
+ parameter a1.
+ parameter b1.
+ parameter a2.
+ parameter b2.
+ parameter v.
+ return a2 + (v - a1) * (b2 - a2) / (b1 - a1).
+}
\ No newline at end of file
diff --git a/library/lib_menu.ks b/library/lib_menu.ks
new file mode 100644
index 0000000..fe80752
--- /dev/null
+++ b/library/lib_menu.ks
@@ -0,0 +1,62 @@
+// lib_menu.ks - select one of the options and return it to calling script.
+// Copyright © 2015,2023 KSLib team
+// Lic. MIT
+
+@lazyglobal off.
+@clobberbuiltins off.
+
+run lib_gui_box.
+
+function open_menu_indexed{
+ parameter title.
+ parameter list_of_names.
+
+ if list_of_names:empty{
+ print "error: list_of_names should not be empty". print 1/0.
+ }
+
+ local current_option is 0.
+ local len is list_of_names:length().
+
+ clearscreen.
+ print title at(2,2).
+ local i is 0.
+ until i=len{
+ print "[ ] "+list_of_names[i] at(2,i+4).
+ set i to i+1.
+ }
+ draw_gui_box(0, 0, terminal:width, len + 6).
+ print "AG7/8 - move up/down, AG9 - select" at(2,len+7).
+ draw_gui_box(0, len + 5, terminal:width, 5).
+ print "*" at(3,4).
+
+ until false{
+ local last_up is ag7.
+ local last_down is ag8.
+ local last_sel is ag9.
+ wait until ag7<>last_up or ag8<>last_down or ag9<>last_sel.
+ if ag7<>last_up{
+ print " " at(3,4+current_option).
+ set current_option to mod(current_option-1+len,len).
+ print "*" at(3,4+current_option).
+ }
+ else if ag8<>last_down{
+ print " " at(3,4+current_option).
+ set current_option to mod(current_option+1,len).
+ print "*" at(3,4+current_option).
+ }
+ else if ag9<>last_sel{
+ break.
+ }
+ }
+
+ clearscreen.
+ return current_option.
+}
+
+function open_menu{
+ parameter title.
+ parameter list_of_names.
+
+ return list_of_names[open_menu_indexed(title, list_of_names)].
+}
diff --git a/library/lib_navball.ks b/library/lib_navball.ks
new file mode 100644
index 0000000..3f49dd1
--- /dev/null
+++ b/library/lib_navball.ks
@@ -0,0 +1,122 @@
+// lib_navball.ks - A library of functions to calculate navball-based directions.
+// Copyright © 2015,2017,2019,2023 KSLib team
+// Lic. MIT
+
+@lazyglobal off.
+@clobberbuiltins off.
+
+function east_for {
+ parameter ves is ship.
+
+ return vcrs(ves:up:vector, ves:north:vector).
+}
+
+function compass_for {
+ parameter ves is ship,thing is "default".
+
+ local pointing is ves:facing:forevector.
+ if not thing:istype("string") {
+ set pointing to type_to_vector(ves,thing).
+ }
+
+ local east is east_for(ves).
+
+ local trig_x is vdot(ves:north:vector, pointing).
+ local trig_y is vdot(east, pointing).
+
+ local result is arctan2(trig_y, trig_x).
+
+ if result < 0 {
+ return 360 + result.
+ } else {
+ return result.
+ }
+}
+
+function pitch_for {
+ parameter ves is ship,thing is "default".
+
+ local pointing is ves:facing:forevector.
+ if not thing:istype("string") {
+ set pointing to type_to_vector(ves,thing).
+ }
+
+ return 90 - vang(ves:up:vector, pointing).
+}
+
+function roll_for {
+ parameter ves is ship,thing is "default".
+
+ local pointing is ves:facing.
+ if not thing:istype("string") {
+ if thing:istype("vessel") or pointing:istype("part") {
+ set pointing to thing:facing.
+ } else if thing:istype("direction") {
+ set pointing to thing.
+ } else {
+ print "type: " + thing:typename + " is not recognized by roll_for".
+ }
+ }
+
+ if vang(pointing:topvector,ves:up:vector) < 0.2 {//this is the dead zone for roll when within 0.2 degrees of vertical
+ return 0.
+ } else {
+ local trig_x is vdot(pointing:topvector,ves:up:vector).
+ local vec_y is vcrs(ves:up:vector,ves:facing:forevector).
+ local trig_y is vdot(pointing:topvector,vec_y).
+ return arctan2(trig_y,trig_x).
+ }
+}
+
+function compass_and_pitch_for {
+ parameter ves is ship,thing is "default".
+
+ local pointing is ves:facing:forevector.
+ if not thing:istype("string") {
+ set pointing to type_to_vector(ves,thing).
+ }
+
+ local east is east_for(ves).
+
+ local trig_x is vdot(ves:north:vector, pointing).
+ local trig_y is vdot(east, pointing).
+ local trig_z is vdot(ves:up:vector, pointing).
+
+ local compass is arctan2(trig_y, trig_x).
+ if compass < 0 {
+ set compass to 360 + compass.
+ }
+ local pitch is arctan2(trig_z, sqrt(trig_x^2 + trig_y^2)).
+
+ return list(compass,pitch).
+}
+
+function bearing_between {
+ parameter ves,thing_1,thing_2.
+
+ local vec_1 is type_to_vector(ves,thing_1).
+ local vec_2 is type_to_vector(ves,thing_2).
+
+ local fake_north is vxcl(ves:up:vector, vec_1).
+ local fake_east is vcrs(ves:up:vector, fake_north).
+
+ local trig_x is vdot(fake_north, vec_2).
+ local trig_y is vdot(fake_east, vec_2).
+
+ return arctan2(trig_y, trig_x).
+}
+
+function type_to_vector {
+ parameter ves,thing.
+ if thing:istype("vector") {
+ return thing:normalized.
+ } else if thing:istype("direction") {
+ return thing:forevector.
+ } else if thing:istype("vessel") or thing:istype("part") {
+ return thing:facing:forevector.
+ } else if thing:istype("geoposition") or thing:istype("waypoint") {
+ return (thing:position - ves:position):normalized.
+ } else {
+ print "type: " + thing:typename + " is not recognized by lib_navball".
+ }
+}
diff --git a/library/lib_navigation.ks b/library/lib_navigation.ks
new file mode 100644
index 0000000..bbf133e
--- /dev/null
+++ b/library/lib_navigation.ks
@@ -0,0 +1,266 @@
+// lib_navigation.ks provides a plethora of useful functions to aid in writing navigational scripts, whether it's space navigation or surface navigation.
+// Copyright © 2019,2020,2023 KSLib team
+// Lic. MIT
+@LAZYGLOBAL OFF.
+@CLOBBERBUILTINS OFF.
+
+// Same as orbital prograde vector for ves
+function orbitTangent {
+ parameter ves is ship.
+
+ return ves:velocity:orbit:normalized.
+}
+
+// In the direction of orbital angular momentum of ves
+// Typically same as Normal
+function orbitBinormal {
+ parameter ves is ship.
+
+ return vcrs((ves:position - ves:body:position):normalized, orbitTangent(ves)):normalized.
+}
+
+// Perpendicular to both tangent and binormal
+// Typically same as Radial In
+function orbitNormal {
+ parameter ves is ship.
+
+ return vcrs(orbitBinormal(ves), orbitTangent(ves)):normalized.
+}
+
+// Vector pointing in the direction of longitude of ascending node
+function orbitLAN {
+ parameter ves is ship.
+
+ return angleAxis(ves:orbit:LAN, ves:body:angularVel:normalized) * solarPrimeVector.
+}
+
+// Same as surface prograde vector for ves
+function surfaceTangent {
+ parameter ves is ship.
+
+ return ves:velocity:surface:normalized.
+}
+
+// In the direction of surface angular momentum of ves
+// Typically same as Normal
+function surfaceBinormal {
+ parameter ves is ship.
+
+ return vcrs((ves:position - ves:body:position):normalized, surfaceTangent(ves)):normalized.
+}
+
+// Perpendicular to both tangent and binormal
+// Typically same as Radial In
+function surfaceNormal {
+ parameter ves is ship.
+
+ return vcrs(surfaceBinormal(ves), surfaceTangent(ves)):normalized.
+}
+
+// Vector pointing in the direction of longitude of ascending node
+function surfaceLAN {
+ parameter ves is ship.
+
+ return angleAxis(ves:orbit:LAN - 90, ves:body:angularVel:normalized) * solarPrimeVector.
+}
+
+// Vector directly away from the body at ves' position
+function localVertical {
+ parameter ves is ship.
+
+ return ves:up:vector.
+}
+
+// Angle to ascending node with respect to ves' body's equator
+function angleToBodyAscendingNode {
+ parameter ves is ship.
+
+ local joinVector is orbitLAN(ves).
+ local angle is vang((ves:position - ves:body:position):normalized, joinVector).
+ if ves:status = "LANDED" {
+ set angle to angle - 90.
+ }
+ else {
+ local signVector is vcrs(-body:position, joinVector).
+ local sign is vdot(orbitBinormal(ves), signVector).
+ if sign < 0 {
+ set angle to angle * -1.
+ }
+ }
+ return angle.
+}
+
+// Angle to descending node with respect to ves' body's equator
+function angleToBodyDescendingNode {
+ parameter ves is ship.
+
+ local joinVector is -orbitLAN(ves).
+ local angle is vang((ves:position - ves:body:position):normalized, joinVector).
+ if ves:status = "LANDED" {
+ set angle to angle - 90.
+ }
+ else {
+ local signVector is vcrs(-body:position, joinVector).
+ local sign is vdot(orbitBinormal(ves), signVector).
+ if sign < 0 {
+ set angle to angle * -1.
+ }
+ }
+ return angle.
+}
+
+// Vector directed from the relative descending node to the ascending node
+function relativeNodalVector {
+ parameter orbitBinormal.
+ parameter targetBinormal.
+
+ return vcrs(orbitBinormal, targetBinormal):normalized.
+}
+
+// Angle to relative ascending node determined from args
+function angleToRelativeAscendingNode {
+ parameter orbitBinormal.
+ parameter targetBinormal.
+
+ local joinVector is relativeNodalVector(orbitBinormal, targetBinormal).
+ local angle is vang(-body:position:normalized, joinVector).
+ local signVector is vcrs(-body:position, joinVector).
+ local sign is vdot(orbitBinormal, signVector).
+ if sign < 0 {
+ set angle to angle * -1.
+ }
+ return angle.
+}
+
+// Angle to relative descending node determined from args
+function angleToRelativeDescendingNode {
+ parameter orbitBinormal.
+ parameter targetBinormal.
+
+ local joinVector is -relativeNodalVector(orbitBinormal, targetBinormal).
+ local angle is vang(-body:position:normalized, joinVector).
+ local signVector is vcrs(-body:position, joinVector).
+ local sign is vdot(orbitBinormal, signVector).
+ if sign < 0 {
+ set angle to angle * -1.
+ }
+ return angle.
+}
+
+// Orbital phase angle with assumed target
+// Positive when you are behind the target, negative when ahead
+function phaseAngle {
+ local common_ancestor is 0.
+ local my_ancestors is list().
+ local your_ancestors is list().
+
+ my_ancestors:add(ship:body).
+ until not(my_ancestors[my_ancestors:length-1]:hasBody) {
+ my_ancestors:add(my_ancestors[my_ancestors:length-1]:body).
+ }
+ your_ancestors:add(target:body).
+ until not(your_ancestors[your_ancestors:length-1]:hasBody) {
+ your_ancestors:add(your_ancestors[your_ancestors:length-1]:body).
+ }
+
+ for my_ancestor in my_ancestors {
+ local found is false.
+ for your_ancestor in your_ancestors {
+ if my_ancestor = your_ancestor {
+ set common_ancestor to my_ancestor.
+ set found to true.
+ break.
+ }
+ }
+ if found {
+ break.
+ }
+ }
+
+ local vel is ship:velocity:orbit.
+ local my_ancestor is my_ancestors[0].
+ until my_ancestor = common_ancestor {
+ set vel to vel + my_ancestor:orbit:velocity:orbit.
+ set my_ancestor to my_ancestor:body.
+ }
+ local binormal is vcrs(-common_ancestor:position:normalized, vel:normalized):normalized.
+
+ local phase is vang(
+ -common_ancestor:position:normalized,
+ vxcl(binormal, target:position - common_ancestor:position):normalized
+ ).
+ local signVector is vcrs(
+ -common_ancestor:position:normalized,
+ (target:position - common_ancestor:position):normalized
+ ).
+ local sign is vdot(binormal, signVector).
+ if sign < 0 {
+ return -phase.
+ }
+ else {
+ return phase.
+ }
+}
+
+// Average Isp calculation
+function _avg_isp {
+ local burnEngines is list().
+ list engines in burnEngines.
+ local massBurnRate is 0.
+ for e in burnEngines {
+ if e:ignition {
+ set massBurnRate to massBurnRate + e:availableThrust/(e:ISP * constant:g0).
+ }
+ }
+ local isp is -1.
+ if massBurnRate <> 0 {
+ set isp to ship:availablethrust / massBurnRate.
+ }
+ return isp.
+}
+
+// Burn time from rocket equation
+function getBurnTime {
+ parameter deltaV.
+ parameter isp is 0.
+
+ if deltaV:typename() = "Vector" {
+ set deltaV to deltaV:mag.
+ }
+ if isp = 0 {
+ set isp to _avg_isp().
+ }
+
+ local burnTime is -1.
+ if ship:availablethrust <> 0 {
+ set burnTime to ship:mass * (1 - CONSTANT:E ^ (-deltaV / isp)) / (ship:availablethrust / isp).
+ }
+ return burnTime.
+}
+
+// Instantaneous azimuth
+function azimuth {
+ parameter inclination.
+ parameter orbit_alt.
+ parameter auto_switch is false.
+
+ local shipLat is ship:latitude.
+ if abs(inclination) < abs(shipLat) {
+ set inclination to shipLat.
+ }
+
+ local head is arcsin(cos(inclination) / cos(shipLat)).
+ if auto_switch {
+ if angleToBodyDescendingNode(ship) < angleToBodyAscendingNode(ship) {
+ set head to 180 - head.
+ }
+ }
+ else if inclination < 0 {
+ set head to 180 - head.
+ }
+ local vOrbit is sqrt(body:mu / (orbit_alt + body:radius)).
+ local vRotX is vOrbit * sin(head) - vdot(ship:velocity:orbit, heading(90, 0):vector).
+ local vRotY is vOrbit * cos(head) - vdot(ship:velocity:orbit, heading(0, 0):vector).
+ set head to 90 - arctan2(vRotY, vRotX).
+ return mod(head + 360, 360).
+}
diff --git a/library/lib_num_to_formatted_str.ks b/library/lib_num_to_formatted_str.ks
new file mode 100644
index 0000000..43ab4a7
--- /dev/null
+++ b/library/lib_num_to_formatted_str.ks
@@ -0,0 +1,159 @@
+// lib_num_to_formatted_str.ks provides several functions for changing numbers (scalers) into strings with specified formats
+// Copyright © 2019,2020,2021,2023 KSLib team
+// Lic. MIT
+
+@LAZYGLOBAL OFF.
+@CLOBBERBUILTINS OFF.
+
+LOCAL FUNCTION time_converter {
+ PARAMETER timeValue, // the time in seconds to convert
+ places. // how many time places (e.g HH:MM:SS has 3 places)
+ SET timeValue TO TIMESPAN(timeValue).
+
+ IF timeValue:MINUTES < 1 OR places = 1 {
+ RETURN LIST(ROUND(timeValue:SECONDS,2)).
+
+ } ELSE IF timeValue:HOURS < 1 OR places = 2 {
+ RETURN LIST(ROUND(MOD(timeValue:SECONDS,60),2), timeValue:MINUTES).
+
+ } ELSE IF timeValue:DAYS < 1 OR places = 3 {
+ RETURN LIST(ROUND(MOD(timeValue:SECONDS,60),2), timeValue:MINUTE, timeValue:HOURS).
+
+ } ELSE IF timeValue:YEARS < 1 OR places = 4 {
+ RETURN LIST(ROUND(MOD(timeValue:SECONDS,60),2), timeValue:MINUTE, timeValue:HOUR, timeValue:DAYS).
+
+ } ELSE {
+ RETURN LIST(ROUND(MOD(timeValue:SECONDS,60),2), timeValue:MINUTE, timeValue:HOUR, timeValue:DAY, timeValue:YEARS).
+ }
+}
+
+//adding list of format types
+//formats should be a list of two items
+// the first item is the fixed number of places to show ie a 2 will always show 2 places be it MM:SS or HH:MM or DDD:HH
+// the second item is a list of strings to include after the neumeric value of the place ie "s" to show after the number or seconds
+// the order is seconds, minutes, hours, days, years
+// it must also be at least as long as the fixed places number
+LOCAL timeFormats IS LIST().
+timeFormats:ADD(LIST(0,LIST("s","m ","h ","d ","y "),2)).
+timeFormats:ADD(LIST(0,LIST("",":",":"," Days, "," Years, "),2)).
+timeFormats:ADD(LIST(0,LIST(" Seconds"," Minutes, "," Hours, "," Days, "," Years, "),2)).
+timeFormats:ADD(LIST(0,LIST("",":",":"),2)).
+timeFormats:ADD(LIST(3,timeFormats[3][1],2)).
+timeFormats:ADD(LIST(2,LIST("s ","m ","h ","d ","y "),0)).
+timeFormats:ADD(LIST(2,LIST(" Seconds "," Minutes "," Hours "," Days "," Years "),0)).
+
+LOCAL leading0List IS LIST(2,2,2,3,3).//presumed maximum leading zeros applied to sec,min,hour,day,year values
+
+FUNCTION time_formatting {
+ PARAMETER timeSec, // the time in seconds to be formatted
+ formatType IS 0, // type of format to use, range 0 to 6
+ rounding IS 0, // rounding for the seconds place, range 0 to 2
+ prependT IS FALSE, // prepend a T- or T+ to format (T- or T if showPlus is FALSE)
+ showPlus IS prependT. // by default only display "+" when prependT is TRUE
+
+ LOCAL timeFormat IS timeFormats[formatType].
+ LOCAL fixedPlaces IS timeFormat[0]. // number of required time places (e.g. HH:MM:SS has 3 fixedPlaces)
+ LOCAL stringList IS timeFormat[1]. // separators for each time place (must have at least fixedPlaces elements!)
+
+ // start by rounding the input so we don't have to "carry the one" in time_converter
+ LOCAL roundingList IS LIST(MIN(rounding,timeFormat[2]), 0, 0, 0, 0).
+ SET timeSec TO ROUND(timeSec, roundingList[0]).
+
+ LOCAL maxPlaces IS stringList:LENGTH.
+ LOCAL timeList IS time_converter(ABS(timeSec), maxPlaces).
+ LOCAL maxLength IS MIN(timeList:LENGTH, maxPlaces).
+ LOCAL returnString IS "".
+
+ // fill in missing time places until format is reached
+ // e.g. for HH:MM:SS, given 4s, put 0 in HH and MM place
+ IF fixedPlaces > 0 {
+ UNTIL timeList:LENGTH >= fixedPlaces {
+ timeList:ADD(0).
+ }
+ SET maxLength TO MIN(timeList:LENGTH, maxPlaces).
+ } ELSE {
+ SET fixedPlaces TO maxLength.
+ }
+
+ // add padding to each element in timeList and prepend to returnString
+ FROM {LOCAL i IS maxLength - fixedPlaces.}
+ UNTIL i >= maxLength STEP {SET i TO i + 1.} DO {
+ LOCAL paddedStr IS padding(timeList[i], leading0List[i], roundingList[i], FALSE, 1).
+ SET returnString TO paddedStr + stringList[i] + returnString.
+ }
+
+ // the prependT strings have one space padding
+ IF prependT SET returnString TO returnString:INSERT(0, " ").
+
+ // all time_string formats have either '+','-', or ' ' for padding
+ IF timeSec < 0 {
+ SET returnString TO returnString:INSERT(0, "-").
+ } ELSE IF showPlus {
+ SET returnString TO returnString:INSERT(0, "+").
+ } ELSE {
+ SET returnString TO returnString:INSERT(0, " ").
+ }
+
+ IF prependT SET returnString TO returnString:INSERT(0, "T").
+
+ RETURN returnString.
+}
+
+
+LOCAL siPrefixList IS LIST(" y"," z"," a"," f"," p"," n"," μ"," m"," "," k"," M"," G"," T"," P"," E"," Z"," Y").
+
+FUNCTION si_formatting {
+ PARAMETER num, // number to format, should be in range 10^-24 to 10^24
+ unit IS "". // user supplied unit, output will prepend SI prefix
+
+ IF num = 0 {
+ RETURN padding(num,1,3) + " " + unit.
+ } ELSE {
+ LOCAL powerOfTen IS MAX(MIN(FLOOR(LOG10(ABS(num))),26),-24).
+ LOCAL SIfactor IS FLOOR(powerOfTen / 3).
+ LOCAL trailingLength IS 3 - (powerOfTen - SIfactor * 3).
+
+ SET num TO ROUND(num/1000^SIfactor,trailingLength) * 1000^SIfactor.
+
+ SET powerOfTen TO MAX(MIN(FLOOR(LOG10(ABS(num))),26),-24).
+ SET SIfactor TO FLOOR(powerOfTen / 3).
+ SET trailingLength TO 3 - (powerOfTen - SIfactor * 3).
+
+ LOCAL prefix IS siPrefixList[SIfactor + 8].
+ RETURN padding(num/1000^SIfactor,1,trailingLength,TRUE,0) + prefix + unit.
+ }
+}
+
+
+LOCAL roundingFunctions IS LIST(ROUND @,FLOOR @,CEILING @).
+
+FUNCTION padding {
+ PARAMETER num, // number to be formatted
+ leadingLength, // minimum digits to the left of the decimal
+ trailingLength, // digits to the right of the decimal
+ positiveLeadingSpace IS TRUE, // whether to prepend a single space to the output
+ roundType IS 0. // 0 for normal rounding, 1 for floor, 2 for ceiling
+
+ LOCAL returnString IS ABS(roundingFunctions[roundType](num,trailingLength)):TOSTRING.
+
+ IF trailingLength > 0 {
+ IF returnString:CONTAINS(".") {
+ LOCAL splitString IS returnString:SPLIT(".").
+ SET returnString TO (splitString[0]:PADLEFT(leadingLength) + "." + splitString[1]:PADRIGHT(trailingLength)):REPLACE(" ","0").
+ } ELSE {
+ SET returnString TO (returnString:PADLEFT(leadingLength) + "." + "0":PADRIGHT(trailingLength)):REPLACE(" ","0").
+ }
+ } ELSE IF returnString:LENGTH < leadingLength {
+ SET returnString TO returnString:PADLEFT(leadingLength):REPLACE(" ","0").
+ }
+
+ IF num < 0 {
+ RETURN "-" + returnString.
+ } ELSE {
+ IF positiveLeadingSpace {
+ RETURN " " + returnString.
+ } ELSE {
+ RETURN returnString.
+ }
+ }
+}
\ No newline at end of file
diff --git a/library/lib_num_to_str.ks b/library/lib_num_to_str.ks
new file mode 100644
index 0000000..3a7b7cc
--- /dev/null
+++ b/library/lib_num_to_str.ks
@@ -0,0 +1,48 @@
+// lib_num_to_str.ks can be used to to format a number in a string of constant length for use in a display.
+// Copyright © 2015,2023 KSLib team
+// Lic. MIT
+@LAZYGLOBAL off.
+@CLOBBERBUILTINS off.
+
+function num_to_str {
+ parameter
+ number, //input number
+ ip, //number of digits before the decimal point.
+ dp. //number of decimal places
+
+ local string is "".
+ local padder is "".
+ local absNumber is abs(number).
+ local index is ip-1.
+ local firstNum is false.
+ until firstNum or index = 0 { // stop adding spacers when the first number is found
+ if mod(floor(absNumber/10^index),10) = 0 {
+ set padder to padder +" ".
+ }
+ else {
+ set firstNum to true.
+ }
+ set index to index-1.
+ }.
+ if dp = 0 {
+ set string to string +round(absNumber).
+ }.
+ else {
+// set index to index-1.
+ set string to string +floor(absNumber).
+ set index to -1.
+ set string to string +".".
+ until index = -dp {
+ set string to string +mod(floor(absNumber/10^index),10).
+ set index to index-1.
+ }.
+ set string to string + mod(round(absNumber/10^index),10).
+ }.
+ if number < 0 {
+ set string to padder +"-" +string.
+ }
+ else {
+ set string to padder +" " +string.
+ }.
+ return string.
+}.
diff --git a/library/lib_number_dialog.ks b/library/lib_number_dialog.ks
new file mode 100644
index 0000000..7bdc316
--- /dev/null
+++ b/library/lib_number_dialog.ks
@@ -0,0 +1,53 @@
+// lib_number_dialog.ks provides a dialog where the user can enter any number (including negative and fractional).
+// Copyright © 2015 KSLib team
+// Lic. MIT
+run lib_gui_box.
+
+function open_number_dialog
+{
+ parameter title.
+ parameter number.
+
+ local increment is 1.
+
+ clearscreen.
+
+ draw_gui_box(0, 0, terminal:width, 10).
+
+ print "6/7 - number -/+" at (2, 5).
+ print "8/9 - increment -/+" at (2, 6).
+ print "0 - enter" at (2, 7).
+
+ local old_decrease is ag6.
+ local old_increase is ag7.
+ local old_div10 is ag8.
+ local old_mul10 is ag9.
+ local old_enter is ag10.
+ local spaces is " ".
+
+ until old_enter <> ag10{
+ print title + " "+number+spaces at (2, 2).
+ print "Increment: " + increment+spaces at (2, 3).
+
+ if old_increase <> ag7{
+ set old_increase to ag7.
+ set number to number + increment.
+ }
+ if old_decrease <> ag6{
+ set old_decrease to ag6.
+ set number to number - increment.
+ }
+ if old_div10 <> ag8{
+ set old_div10 to ag8.
+ set increment to increment / 10.
+ }
+ if old_mul10 <> ag9{
+ set old_mul10 to ag9.
+ set increment to increment * 10.
+ }
+ }
+
+ clearscreen.
+
+ return number.
+}
diff --git a/library/lib_pid.ks b/library/lib_pid.ks
new file mode 100644
index 0000000..7c26e1c
--- /dev/null
+++ b/library/lib_pid.ks
@@ -0,0 +1,87 @@
+// lib_pid.ks provides routines to implement a simple generic PID controller.
+// Copyright © 2015,2016,2019,2023 KSLib team
+// Lic. MIT
+@LAZYGLOBAL off.
+@CLOBBERBUILTINS off.
+
+HUDTEXT("lib_pid.ks has been superseded by the kOS inbuilt PIDloop() function.", 10, 2, 30, RED, FALSE).
+wait 0.5.
+HUDTEXT("It is maintained for example purposes only, please see the kOS documentation for more.", 10, 4, 30, RED, FALSE).
+
+function PID_init {
+ parameter
+ Kp, // gain of position
+ Ki, // gain of integral
+ Kd, // gain of derivative
+ cMin, // the bottom limit of the control range (to protect against integral windup)
+ cMax. // the the upper limit of the control range (to protect against integral windup)
+
+ local SeekP is 0. // desired value for P (will get set later).
+ local P is 0. // phenomenon P being affected.
+ local I is 0. // crude approximation of Integral of P.
+ local D is 0. // crude approximation of Derivative of P.
+ local oldT is -1. // (old time) start value flags the fact that it hasn't been calculated
+ local oldInput is 0. // previous return value of PID controller.
+
+ // Because we don't have proper user structures in kOS (yet?)
+ // I'll store the PID tracking values in a list like so:
+ //
+ local PID_array is list(Kp, Ki, Kd, cMin, cMax, SeekP, P, I, D, oldT, oldInput).
+
+ return PID_array.
+}.
+
+function PID_seek {
+ parameter
+ PID_array, // array built with PID_init.
+ seekVal, // value we want.
+ curVal. // value we currently have.
+
+ // Using LIST() as a poor-man's struct.
+
+ local Kp is PID_array[0].
+ local Ki is PID_array[1].
+ local Kd is PID_array[2].
+ local cMin is PID_array[3].
+ local cMax is PID_array[4].
+ local oldS is PID_array[5].
+ local oldP is PID_array[6].
+ local oldI is PID_array[7].
+ local oldD is PID_array[8].
+ local oldT is PID_array[9]. // Old Time
+ local oldInput is PID_array[10]. // prev return value, just in case we have to do nothing and return it again.
+
+ local P is seekVal - curVal.
+ local D is oldD. // default if we do no work this time.
+ local I is oldI. // default if we do no work this time.
+ local newInput is oldInput. // default if we do no work this time.
+
+ local t is time:seconds.
+ local dT is t - oldT.
+
+ if oldT < 0 {
+ // I have never been called yet - so don't trust any
+ // of the settings yet.
+ } else {
+ if dT > 0 { // Do nothing if no physics tick has passed from prev call to now.
+ set D to (P - oldP)/dT. // crude fake derivative of P
+ local onlyPD is Kp*P + Kd*D.
+ if (oldI > 0 or onlyPD > cMin) and (oldI < 0 or onlyPD < cMax) { // only do the I turm when within the control range
+ set I to oldI + P*dT. // crude fake integral of P
+ }.
+ set newInput to onlyPD + Ki*I.
+ }.
+ }.
+
+ set newInput to max(cMin,min(cMax,newInput)).
+
+ // remember old values for next time.
+ set PID_array[5] to seekVal.
+ set PID_array[6] to P.
+ set PID_array[7] to I.
+ set PID_array[8] to D.
+ set PID_array[9] to t.
+ set PID_array[10] to newInput.
+
+ return newInput.
+}.
diff --git a/library/lib_raw_user_input.ks b/library/lib_raw_user_input.ks
new file mode 100644
index 0000000..f55d418
--- /dev/null
+++ b/library/lib_raw_user_input.ks
@@ -0,0 +1,58 @@
+// lib_raw_user_input.ks provides a low level library to do user input via action groups. I believe it supports AGX to.
+// Copyright © 2015,2020,2023 KSLib team
+// Lic. MIT
+// Originally developed by abenkovskii
+@LAZYGLOBAL OFF.
+@CLOBBERBUILTINS OFF.
+
+run lib_exec.
+
+function wait_for_action_groups
+{
+ parameter ag_list. // list of strings
+
+ function first_diff
+ {
+ parameter list_a.
+ parameter list_b.
+
+ local iter_a is list_a:iterator.
+ local iter_b is list_b:iterator.
+
+ until not (iter_a:next and iter_b:next)
+ {
+ if iter_a:value <> iter_b:value
+ {
+ break.
+ }
+ }
+ local result is iter_a:index.
+ if iter_a:atend or iter_b:atend
+ {
+ set result to -1.
+ }
+ return result.
+ }
+
+ local arg_string is "".
+ local separator is "".
+
+ for ag_name in ag_list
+ {
+ set arg_string to arg_string + separator + ag_name.
+ set separator to ", ".
+ }
+
+ lock _raw_input_ag_list to -1.
+ execute("lock _raw_input_ag_list to list(" + arg_string + ").").
+ local old_values is _raw_input_ag_list.
+
+ local result is -1.
+ until result <> -1
+ {
+ set result to first_diff(old_values, _raw_input_ag_list).
+ wait 0.
+ }
+
+ return result.
+}
diff --git a/library/lib_realchute.ks b/library/lib_realchute.ks
new file mode 100644
index 0000000..1585f08
--- /dev/null
+++ b/library/lib_realchute.ks
@@ -0,0 +1,13 @@
+// lib_realchute.ks - adds support for RealChute
+// Copyright © 2015,2023 KSLib team
+// Lic. MIT
+
+@LAZYGLOBAL OFF.
+@CLOBBERBUILTINS OFF.
+
+declare function R_chutes {
+ parameter event.
+ for RealChute in ship:modulesNamed("RealChuteModule") {
+ RealChute:doevent(event).
+ }.
+}.
diff --git a/library/lib_running_average_filter.ks b/library/lib_running_average_filter.ks
new file mode 100644
index 0000000..e6f61e5
--- /dev/null
+++ b/library/lib_running_average_filter.ks
@@ -0,0 +1,50 @@
+// lib_running_average_filter.ks provides a function that will filter out noise from a dataset by storing a number of previous values of that dataset and outputting the average (mean) of those values.
+// Copyright © 2015,2023 KSLib team
+// Lic. MIT
+//Authored by space_is_hard
+
+//These functions will set up and implement a running
+//average filter that will help filter out noise in a
+//continuously-updating set of data. It's useful for
+//removing noise from a dataset that is expected to remain
+//relatively stable. It will lag behind any large or
+//continuous changes in the dataset
+
+@LAZYGLOBAL OFF.
+@CLOBBERBUILTINS OFF.
+
+//Builds the list that will be passed on to the filter
+FUNCTION running_average_filter_init {
+ PARAMETER
+ maxLength, //How many previous values should be stored and averaged
+ initValue. //What starting values to use to build the list of [maxLength] units long
+
+ LOCAL inputList TO LIST().
+
+ FROM {LOCAL i TO 0.} UNTIL i >= maxLength STEP {SET i TO i + 1.} DO {
+ inputList:ADD(initValue).
+ }.
+
+ RETURN inputList.
+
+}.
+
+//Takes in a new value and spits out an average of it and the previous values
+FUNCTION running_average_filter {
+ PARAMETER
+ inputList, //The list we built with PA_filter_init
+ newValue. //The next value to tack onto the list
+
+ inputList:REMOVE(0). //Removes the first item and bumps the rest down
+ inputList:ADD(newValue). //Adds the new value onto the end
+
+ //Adds all of the values in the list together
+ LOCAL sum TO 0.
+ FOR item IN inputList {
+ SET sum TO sum + item.
+ }.
+
+ //Returns the average of all of the values in the list
+ RETURN sum / inputList:LENGTH.
+
+}.
\ No newline at end of file
diff --git a/library/lib_seven_seg.ks b/library/lib_seven_seg.ks
new file mode 100644
index 0000000..14bde01
--- /dev/null
+++ b/library/lib_seven_seg.ks
@@ -0,0 +1,89 @@
+// lib_seven_seg.ks prints a seven segment display on the terminal showing the input value at the location specified.
+// Copyright © 2015,2019 KSLib team
+// Lic. MIT
+function seven_seg {
+ parameter
+ num,
+ col,
+ row.
+ if num:istype("scalar") {
+ if num > -1 {
+ if num < 5 {
+ if num < 2 {
+ if num < 1 {
+ print "_" at (col+1,row).
+ print "| |" at (col,row+1).
+ print "|_|" at (col,row+2).
+ } else {
+ print " " at (col+1,row).
+ print " |" at (col,row+1).
+ print " |" at (col,row+2).
+ }
+ } else {
+ if num < 4 {
+ if num < 3 {
+ print "_" at (col+1,row).
+ print " _|" at (col,row+1).
+ print "|_ " at (col,row+2).
+ } else {
+ print "_" at (col+1,row).
+ print " _|" at (col,row+1).
+ print " _|" at (col,row+2).
+ }
+ } else {
+ print " " at (col+1,row).
+ print "|_|" at (col,row+1).
+ print " |" at (col,row+2).
+ }
+ }
+ } else {
+ if num < 7 {
+ if num < 6 {
+ print "_" at (col+1,row).
+ print "|_ " at (col,row+1).
+ print " _|" at (col,row+2).
+ } else {
+ print "_" at (col+1,row).
+ print "|_ " at (col,row+1).
+ print "|_|" at (col,row+2).
+ }
+ } else {
+ if num < 9 {
+ if num < 8 {
+ print "_" at (col+1,row).
+ print " |" at (col,row+1).
+ print " |" at (col,row+2).
+ } else {
+ print "_" at (col+1,row).
+ print "|_|" at (col,row+1).
+ print "|_|" at (col,row+2).
+ }
+ } else if num < 10 {
+ print "_" at (col+1,row).
+ print "|_|" at (col,row+1).
+ print " |" at (col,row+2).
+ } else {
+ hudtext("ERROR: [lib_seven_seg.ks] number: " + num +" is out of bounds high" , 10, 2, 30, RED, FALSE).
+ }
+ }
+ }
+ } else {
+ hudtext("ERROR: [lib_seven_seg.ks] number: " + num +" is out of bounds low" , 10, 2, 30, RED, FALSE).
+ }
+ // special cases
+ } else if num:ISTYPE("string") {
+ if num = "b" { //"b" = blank
+ print " " at (col+1,row).
+ print " " at (col,row+1).
+ print " " at (col,row+2).
+ } else if num = "-" { //"-" = minus
+ print " " at (col+1,row).
+ print " _ " at (col,row+1).
+ print " " at (col,row+2).
+ } else {// else if num = // add more special cases here.
+ hudtext("ERROR: [lib_seven_seg.ks] unrecognized string: " +num, 10, 2, 30, RED, FALSE).
+ }
+ } else {
+ hudtext("ERROR: [lib_seven_seg.ks] unrecognized type: " +num:typename, 10, 2, 30, RED, FALSE).
+ }
+}
diff --git a/library/lib_str_to_num.ks b/library/lib_str_to_num.ks
new file mode 100644
index 0000000..7a6ec53
--- /dev/null
+++ b/library/lib_str_to_num.ks
@@ -0,0 +1,63 @@
+// lib_str_to_num.ks can be used to convert a stringified number back into a legitimate number for use in computation.
+// Copyright © 2015,2023 KSLib team
+// Lic. MIT
+@LAZYGLOBAL off.
+@CLOBBERBUILTINS off.
+
+local num_lex is lexicon().
+
+num_lex:add("0", 0).
+num_lex:add("1", 1).
+num_lex:add("2", 2).
+num_lex:add("3", 3).
+num_lex:add("4", 4).
+num_lex:add("5", 5).
+num_lex:add("6", 6).
+num_lex:add("7", 7).
+num_lex:add("8", 8).
+num_lex:add("9", 9).
+
+function str_to_num {
+ parameter s.
+
+ // Handle negative numbers
+ if s:startswith("-") {
+ return str_to_num(s:substring(1,s:length-1)) * -1.
+ }
+
+ // Scientific Notation
+ local e is s:find("e").
+ if e <> -1 {
+ local m is s:substring(e+1,1).
+ if m <> "+" and m <> "-" { return "NaN". }
+ local p is s:split("e" + m).
+ if p:length <> 2 { return "NaN". }
+ local p0 is str_to_num(p[0]).
+ local p1 is str_to_num(p[1]).
+ if p0 = "NaN" or p1 = "NaN" { return "NaN". }
+ if m = "+" {
+ return p0 * 10^p1.
+ } else {
+ return (p0 / 10^p1).
+ }
+ }
+
+ // Decimals
+ if s:contains(".") {
+ local p is s:split(".").
+ if p:length <> 2 { return "NaN". }
+ local p0 is str_to_num(p[0]).
+ local p1 is str_to_num(p[1]).
+ if p0 = "NaN" or p1 = "NaN" { return "NaN". }
+ return p0 + (p1 / (10^p[1]:length)).
+ }
+
+ // Integers (match on tokens, and bit-shift)
+ local val is 0.
+ for i IN s:split(""):sublist(1,s:length) {
+ if num_lex:haskey(i) { set val to val + num_lex[i]. } else { return "NaN". }
+ set val TO val * 10.
+ }
+ return val / 10.
+
+}
diff --git a/library/lib_term.ks b/library/lib_term.ks
new file mode 100644
index 0000000..b878998
--- /dev/null
+++ b/library/lib_term.ks
@@ -0,0 +1,66 @@
+// lib_term.ks - terminal manipulations
+// Copyright © 2015,2023 KSLib team
+// Lic. MIT
+// Original starting work by Github user: Dunbaratu
+
+@lazyglobal off.
+@clobberbuiltins off.
+
+// -----------
+// char_line
+// -----------
+// Perform a low level line drawing algorithm
+// with characters on the terminal.
+function char_line {
+ parameter
+ ch, // string (character) to draw with.
+ x0, y0, x1, y1. // locations to draw from/to
+
+ local dX is x1-x0.
+ local dY is y1-y0.
+ local len is sqrt(dX^2+dY^2).
+ local incSize is 2. // make loop only execute once if it's just a point.
+ if len > 0 {
+ set incSize to 1/len. // or let it execute a number of times if it's not.
+ }
+ local d is 0.
+ until d > 1 {
+ print ch at (d*dX+x0, d*dY+y0).
+ set d to d + incSize.
+ }.
+}
+
+
+// Draw an arc of an ellipse, using characters.
+// Note that since the screen's Y axis goes bigger going
+// DOWN, the result may be inverted from what you'd imagine.
+function char_ellipse_arc {
+ parameter
+ ch, // string (character) to draw with.
+ x0, y0, xRad, yRad, deg0, deg1.
+
+ // swap if arc degrees are given backward:
+ if deg0 > deg1 {
+ local tmp is deg0.
+ set deg0 to deg1.
+ set deg1 to tmp.
+ }.
+ // incSize is the biggest number of degrees that is guaranteed
+ // to be just barely small enough not to skip a character when drawing.
+ local longRad is max(xRad,yRad).
+ local incSize is arcsin(1/longRad).
+
+ local d is deg0.
+ until d > deg1 {
+ print ch at (xRad*cos(d)+x0, yRad*sin(d)+y0).
+ set d to d + incSize.
+ }.
+}.
+
+// Draw a circle. This is a special case of char_ellipse_arc.
+function char_circle {
+ parameter
+ ch,
+ x0, y0, rad.
+ char_ellipse_arc(ch, x0, y0, rad, rad, 0, 360).
+}.
diff --git a/poweredLanding.ks b/poweredLanding.ks
new file mode 100644
index 0000000..4708bb7
--- /dev/null
+++ b/poweredLanding.ks
@@ -0,0 +1,58 @@
+
+//-126
+function DeOrbit{
+ parameter burnLng, deltaV.
+
+ lock pos to SHIP:geoposition.
+
+ lock curLng to pos:lng.
+
+ DECLARE LOCAL tgtLng IS burnLng - 5.
+ if curLng > burnLng or curLng < tgtLng {
+ SET WARPMODE TO "RAILS".
+ SET WARP TO 3.
+ print "warping to " + tgtLng.
+ WAIT UNTIL curLng > tgtLng and curLng < burnLng.
+ SET WARP TO 0.
+ print "arived at dst " + tgtLng.
+ print "burn at " + burnLng.
+ }
+ print "currently at" + curLng.
+
+ BRAKES ON.
+ RCS ON.
+ LOCK steering to retrograde.
+ wait until curLng >= burnLng.
+
+}
+
+function CalculateBurnDuration{
+ parameter dv.
+
+ declare local isp is GetIsp().
+ declare local finalMass is mass * exp(-dv / (isp * constant:g0)).
+ declare local twr is ship:maxThrust / (((mass + finalMass)/2) * constant:g0).
+
+
+ local t is dv / (twr * constant.g0).
+ return t.
+}
+
+function GetIsp{
+ LIST ENGINES IN allEngines.
+ declare local totalThrust is ship:maxThrust.
+ local sum is 0.
+ local weights is 0.
+ for eng in allEngines{
+ if eng:IGNITION and not eng:flameout{
+ local w is eng:AVAILABLETHRUST / totalThrust.
+ local ispW is eng:isp * w.
+ set sum to sum + ispW.
+ set weights to weights + w.
+ }
+ }
+ return sum / weights.
+}
+
+// DeOrbit(-126, 250).
+print CalculateBurnDuration(250).
\ No newline at end of file
diff --git a/radenLaunch.ks b/radenLaunch.ks
new file mode 100644
index 0000000..3c9a673
--- /dev/null
+++ b/radenLaunch.ks
@@ -0,0 +1,52 @@
+run "library/lib_math".
+
+clearScreen.
+BRAKES on.
+print "Preparing to launch".
+from { local c is 5.} until c = 0 step {set c to c - 1.} do {
+ print c.
+ wait 1.
+}
+print "Launching!".
+lock throttle to 100.
+lock steering to heading(90, 0, 0).
+
+print "Phase: Takeoff".
+wait until surfaceSpeed > 140.
+
+print "Phase: Rotate".
+lock steering to heading(90, 3, 0).
+wait 1.
+print "Gear up".
+gear off.
+
+print "Phase: Athmospheric Climb".
+lock steering to heading(90, 15, 0).
+wait until altitude > 20000.
+
+print "Phase: Mode Switch".
+ag1 on. //Switch engine mode
+lock steering to heading(90, 25, 0).
+wait until apoapsis > 80000.
+
+print "Phase: Sub-Orbital Coast".
+lock throttle to 0.
+lock steering to prograde.
+
+when altitude > 70000 then {
+ print "Deploying Solar".
+ ag2 on. //deploy solar
+ print "Opening Docking Port".
+ ag3 on. //open docking port
+}
+
+lock distToApo to apoapsis - altitude.
+wait until distToApo < 100.
+print "Phase: Circularize".
+lock throttle to 100.
+wait until periapsis > 79000.
+unlock throttle.
+
+print "In orbit. Releasing controls".
+
+