added raden launch

This commit is contained in:
2025-11-19 12:52:55 -05:00
parent 86ade33bef
commit 533ec9d520
28 changed files with 2412 additions and 0 deletions

53
library/lib_circle_nav.ks Normal file
View File

@@ -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)).
}.

112
library/lib_enum.ks Normal file
View File

@@ -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<p set p to i. return p.}
function partition{parameter l,o,c is to_l(l), p is list(list(),list()).
for i in c { if o(i) p[0]:add(i). else p[1]:add(i). }
set p[0] to cast(p[0],l:typename). set p[1] to cast(p[1],l:typename).
return p.
}
function reduce{parameter l,m,t. for i in to_l(l) set m to t(m,i). return m.}
function reject{parameter l,c,p is list().
for i in to_l(l) if not c(i) p:add(i). return cast(p,l:typename). }
function reverse{parameter l,p is stack().
for i in l p:push(i). return cast(p,l:typename).}
function select{parameter l,c,p is list().
for i in to_l(l) if c(i) p:add(i). return cast(p,l:typename).}
function sort{
parameter l,c,p is to_l(l):copy.
function qs{parameter A,lo,hi.
if lo<hi{local p is pt(A, lo, hi). qs(A, lo, p). qs(A, p + 1, hi).}}
function pt{parameter A, lo, hi, pivot is A[lo], i is lo-1, j is hi+1.
until 0 {
until 0 {set j to j-1. if c(A[j],pivot)<=0 break.}
until 0 {set i to i+1. if c(A[i],pivot)>=0 break.}
if i<j{local s is A[i]. set A[i] to A[j]. set A[j] to s.} else return j.
}
}
qs(p,0,p:length-1). return cast(p,l:typename).
}}

162
library/lib_exec.ks Normal file
View File

@@ -0,0 +1,162 @@
// lib_exec.ks - executes a command from its string representation.
// Copyright © 2015,2019,2020,2023 KSLib team
// Lic. MIT
// Originally developed by abenkovskii
// The mess below declares a function execute that executes a command from it's string representation.
// I'm 95% sure there is no easy way to achieve this behavior in the current version of kOS (0.17).
// All easier solutions I know fail to work as expected in a range of special cases.
// If you think you know an easy way to implement it there are unit tests
// in KSLib/unit_tests/lib_exec to test your implementation.
// See "run is weird" for more detailed information: [link is coming soon (tm)]
// The execute function is now implemented using an incrementing string so that each time the function is called there is a unique file name.
// This modification was done in kOS version (1.1.9.0) and works as of that version.
// It should be backwards compatible with earlier versions of kOS but this has not been tested.
// nuggreat's notes about run/runpath as of kOS version (1.1.9.0)
// When a file is run for the first time it gets stored in memory so that any additional calls to run the given file can be served a lot faster.
// This means that even if you delete a file and then recreate it with different stuff it that won't update the version kOS is storing as the user has no way to make kOS forget a stored instance of a file.
// And as files are IDed by there name (and possibly path haven't tested that) if the name doesn't change even if the contents do then kOS will run the version it stored.
// The version kOS stores only leaves memory when kOS falls back to the terminal level.
// Thus for a file to be runnable with different internal commands it must have a different name.
// NOTE: This might change in the future but at least as of kOS version (1.1.9.0) this is the way run appears to work.
@LAZYGLOBAL OFF.
@CLOBBERBUILTINS OFF.
if not (defined _exec_idString) {
global _exec_idString is char(127).//starts at char 127 to avoid reserved charters in windows file names
}
if not (defined _past_exec_strings) {
global _past_exec_strings is lexicon().//stores previously executed commands and the associated path, intended to prevent increment of _exec_idString if calling the same command repeatedly.
set _past_exec_strings:casesensitive to true.
}
function execute {
parameter command.
local filePath IS path().
if _past_exec_strings:haskey(command) {//if we have already run a command recall the path used the old path
set filePath to _past_exec_strings[command].
} else {
//start of string incrementing
local carry is false.
local strStart is _exec_idString:LENGTH - 1.
from { local i is strStart. } until i < 0 step { SET i to i - 1. } do {
local tmpNum is unchar(_exec_idString[i]).
//print tmpNum.
if carry or (i = strStart) {
set tmpNum to tmpNum + 1.
set carry to false.
}
if tmpNum > 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).
}

View File

@@ -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.
}

35
library/lib_geodec.ks Normal file
View File

@@ -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.
}.

68
library/lib_gui_box.ks Normal file
View File

@@ -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).
}

View File

@@ -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.
}

182
library/lib_input_string.ks Normal file
View File

@@ -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.
}

View File

@@ -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.
}

101
library/lib_lazcalc.ks Normal file
View File

@@ -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.
}.
}.
}.

View File

@@ -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.
}

View File

@@ -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.
}
}

15
library/lib_math.ks Normal file
View File

@@ -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).
}

62
library/lib_menu.ks Normal file
View File

@@ -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)].
}

122
library/lib_navball.ks Normal file
View File

@@ -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".
}
}

266
library/lib_navigation.ks Normal file
View File

@@ -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).
}

View File

@@ -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.
}
}
}

48
library/lib_num_to_str.ks Normal file
View File

@@ -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.
}.

View File

@@ -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.
}

87
library/lib_pid.ks Normal file
View File

@@ -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.
}.

View File

@@ -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.
}

13
library/lib_realchute.ks Normal file
View File

@@ -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).
}.
}.

View File

@@ -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.
}.

89
library/lib_seven_seg.ks Normal file
View File

@@ -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).
}
}

63
library/lib_str_to_num.ks Normal file
View File

@@ -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.
}

66
library/lib_term.ks Normal file
View File

@@ -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).
}.