Jump to content

Lavayar

Alpha Tester
  • Posts

    49
  • Joined

  • Last visited

Reputation Activity

  1. Like
    Lavayar reacted to Maxim Kammerer in NEW SCHEMATICS - Discussion Thread   
    You also need them in transfer units and NQ got me to a point that I'm not even sure if this is a bug or intended.
  2. Like
    Lavayar got a reaction from Maxim Kammerer in NEW SCHEMATICS - Discussion Thread   
    Shematics needed for t1 alloys. They have like 2 min cycle. NQ you are [filtered]ing kidding me. Fix this asap
  3. Like
    Lavayar got a reaction from Moulinex in NEW SCHEMATICS - Discussion Thread   
    Shematics needed for t1 alloys. They have like 2 min cycle. NQ you are [filtered]ing kidding me. Fix this asap
  4. Like
    Lavayar got a reaction from Shiromar in NEW SCHEMATICS - Discussion Thread   
    Shematics needed for t1 alloys. They have like 2 min cycle. NQ you are [filtered]ing kidding me. Fix this asap
  5. Like
    Lavayar got a reaction from tobitege in NEW SCHEMATICS - Discussion Thread   
    Shematics needed for t1 alloys. They have like 2 min cycle. NQ you are [filtered]ing kidding me. Fix this asap
  6. Like
    Lavayar got a reaction from Mncdk1 in NEW SCHEMATICS - Discussion Thread   
    Shematics needed for t1 alloys. They have like 2 min cycle. NQ you are [filtered]ing kidding me. Fix this asap
  7. Like
    Lavayar reacted to Novean-32184 in NEW SCHEMATICS - Discussion Thread   
    One thing that really irks me here is the inconsistency this new system has with previous systems and with the rest of Industry.
     
    Everything is based on tiers being named as Basic, Uncommon, Advanced, Rare and Exotic
    Schematics are suddenly Tier 1-5
     
    The UI is really terrible > a simple GET SCHEMATIC option in the industry UI would already improve this a lot
     
    Now you have to open the industry UI, check which tier and for what type you need, exit the UI, enter the schematic UI and "order" the UI.
     
    Better would be open industry > click "GET schematic" and go straight there
     
    The schematic UI itself should have a built-in filter by using a tree structure for the list
     
    But that would mean you actually have devs who design UI and understand how to get a good UX ..
     
     
  8. Like
    Lavayar got a reaction from Facial_Fracture in NEW SCHEMATICS - Discussion Thread   
    Shematics needed for t1 alloys. They have like 2 min cycle. NQ you are [filtered]ing kidding me. Fix this asap
  9. Like
    Lavayar reacted to NQ-Naerais in Expansions for market landing pads   
    In consideration of player feedback, we’ve been investigating the best possible solution to address a situation in public markets where parked ships have been causing clutter and performance issues. 
     
    To address this, we will be creating additional landing pads in those areas, then moving ships to them, taking care to spread them out so that the end result will be a better “feel”, aesthetically and performance-wise. 

    The first phase will begin today in Districts 6 and 7 on PTS where we will try out the tool to make sure it’s working properly. If so, we’ll announce when the operation will happen on the Live server. That will require an extended downtime, so we want to do it when it will be the least disruptive to peak playing times for the community.
     
  10. Like
    Lavayar reacted to GraXXoR in [Discuss] We've Heard You!   
    Agreed... 

    If you crash, you still have to spend ages repairing the ship.. a lot of crashes (not all, but a proportion) are still caused by janky framerates, lags and (in my case yesterday losing a life on two L adjusters!) an uncontrollable spin due to high school level control programming whereby, if your computer skips a frame or two just as you let go the key, the LUA adjuster-stop-function subroutine that is supposed to return the adjuster level to 0 is never called and you end up offset from zero permanently... 

    Also remember that in a collision, damage is applied ONLY to the elements regardless of how many voxels you have. You think you can protect your elements with a thick shield of voxels or embed them... LOOOOOL Nope... Impact damage completely disregards and skips the voxels and jumps from element to element based on distance from impact until all the damage is absorbed... potentially with enough damage transmitted from component to component to completely destroy all elements on your ship regardless of intervening concrete/titanium.... gold...

    Clip the wingtip of an XS core ship at 150kph into another XS ship sitting on a landing pad: no biggie... Clip a stationary abandoned started speeder with an  M wing of your 10kT superfreighter at 150kph and boom: enough damage to destroy the wing AND transmit the remaining damage to surrounding components. And the little XS ship just sits there giving you the middle finger. ...

    You can bump into a single floaty voxel that renders as your ship is passing (either just in front or even effectively inside it if your ship is larger) and if it's a 1kT beast it will be completely destroyed if travelling at >100kph (28 mps)... that's what all the complaints are about on the forums due to players building single voxel towers to cater to the broken-ass mechanics of AGG ships. Now add to that permanent death AND schematics to rebuild and you end up with complete, undaulterated bullshit.
     
    So to cut a long story... well,  not at all.... I am 100% a proponent of rolling back crash damage on impacts until their flight and damage mechanics are CAST IRON level done and tested.

    This first bit!!! 100%
  11. Like
    Lavayar reacted to RONinja in Space Map with Warp Cell Calculator   
    ----Edits

    09/27      Changed math from U.S. tons to tonnes.  907.185 -> 1000
    10/07      Updated svg bootstrap and ADDED MasterOfAll's Newer Version.


    NEW VERSION MADE BY MasterOfAll  

    Features
    Time till Destination   = TTD
    Displays Destination that was chosen.
    Does Speed displayed as Velocity
    Comes with Sleek White Buttons.

    Pasteable Form Here Just Paste to PB and Link Screen and Core.
    {"slots":{"0":{"name":"screen","type":{"events":[],"methods":[]}},"1":{"name":"core","type":{"events":[],"methods":[]}},"2":{"name":"slot3","type":{"events":[],"methods":[]}},"3":{"name":"slot4","type":{"events":[],"methods":[]}},"4":{"name":"slot5","type":{"events":[],"methods":[]}},"5":{"name":"slot6","type":{"events":[],"methods":[]}},"6":{"name":"slot7","type":{"events":[],"methods":[]}},"7":{"name":"slot8","type":{"events":[],"methods":[]}},"8":{"name":"slot9","type":{"events":[],"methods":[]}},"9":{"name":"slot10","type":{"events":[],"methods":[]}},"-1":{"name":"unit","type":{"events":[],"methods":[]}},"-2":{"name":"system","type":{"events":[],"methods":[]}},"-3":{"name":"library","type":{"events":[],"methods":[]}}},"handlers":[{"code":"MapScreenMouseX = x\nMapScreenMouseY = y\nMapScreenMouseDown = false\nlocal buttonNo = evaluateButtons()\nif MapScreenButtonSelected > 0 and MapScreenButtonSelected == buttonNo then\n onButtonUp(buttonNo)\n onClick(buttonNo)\nend\nMapScreenButtonSelected = -buttonNo","filter":{"args":[{"variable":"*"},{"variable":"*"}],"signature":"mouseUp(x,y)","slotKey":"0"},"key":"0"},{"code":"MapScreenMouseX = x\nMapScreenMouseY = y\nMapScreenMouseDown = true\nMapScreenButtonSelected = evaluateButtons()\nonButtonDown(MapScreenButtonSelected)\n","filter":{"args":[{"variable":"*"},{"variable":"*"}],"signature":"mouseDown(x,y)","slotKey":"0"},"key":"1"},{"code":"function Atlas()\n return {\n [0] = {\n [1]={\n GM=6930729684,\n bodyId=1,\n center={x=17465536.000,y=22665536.000,z=-34464.000},\n name='Madis',\n planetarySystemId=0,\n radius=44300\n },\n [2]={\n GM=157470826617,\n bodyId=2,\n center={x=-8.000,y=-8.000,z=-126303.000},\n name='Alioth',\n planetarySystemId=0,\n radius=126068\n },\n [3]={\n GM=11776905000,\n bodyId=3,\n center={x=29165536.000,y=10865536.000,z=65536.000},\n name='Thades',\n planetarySystemId=0,\n radius=49000\n },\n [4]={\n GM=14893847582,\n bodyId=4,\n center={x=-13234464.000,y=55765536.000,z=465536.000},\n name='Talemai',\n planetarySystemId=0,\n radius=57450\n },\n [5]={\n GM=16951680000,\n bodyId=5,\n center={x=-43534464.000,y=22565536.000,z=-48934464.000},\n name='Feli',\n planetarySystemId=0,\n radius=60000\n },\n [6]={\n GM=10502547741,\n bodyId=6,\n center={x=52765536.000,y=27165538.000,z=52065535.000},\n name='Sicari',\n planetarySystemId=0,\n radius=51100\n },\n [7]={\n GM=13033380591,\n bodyId=7,\n center={x=58665538.000,y=29665535.000,z=58165535.000},\n name='Sinnen',\n planetarySystemId=0,\n radius=54950\n },\n [8]={\n GM=18477723600,\n bodyId=8,\n center={x=80865538.000,y=54665536.000,z=-934463.940},\n name='Teoma',\n planetarySystemId=0,\n radius=62000\n },\n [9]={\n GM=18606274330,\n bodyId=9,\n center={x=-94134462.000,y=12765534.000,z=-3634464.000},\n name='Jago',\n planetarySystemId=0,\n radius=61590\n },\n [10]={\n GM=78480000,\n bodyId=10,\n center={x=17448118.224,y=22966846.286,z=143078.820},\n name='Madis Moon 1',\n planetarySystemId=0,\n radius=10000\n },\n [11]={\n GM=237402000,\n bodyId=11,\n center={x=17194626.000,y=22243633.880,z=-214962.810},\n name='Madis Moon 2',\n planetarySystemId=0,\n radius=11000\n },\n [12]={\n GM=265046609,\n bodyId=12,\n center={x=17520614.000,y=22184730.000,z=-309989.990},\n name='Madis Moon 3',\n planetarySystemId=0,\n radius=15005\n },\n [21]={\n GM=2118960000,\n bodyId=21,\n center={x=457933.000,y=-1509011.000,z=115524.000},\n name='Alioth Moon 1',\n planetarySystemId=0,\n radius=30000\n },\n [22]={\n GM=2165833514,\n bodyId=22,\n center={x=-1692694.000,y=729681.000,z=-411464.000},\n name='Alioth Moon 4',\n planetarySystemId=0,\n radius=30330\n },\n [26]={\n GM=68234043600,\n bodyId=26,\n center={x=-1404835.000,y=562655.000,z=-285074.000},\n name='Sanctuary',\n planetarySystemId=0,\n radius=83400\n },\n [30]={\n GM=211564034,\n bodyId=30,\n center={x=29214402.000,y=10907080.695,z=433858.200},\n name='Thades Moon 1',\n planetarySystemId=0,\n radius=14002\n },\n [31]={\n GM=264870000,\n bodyId=31,\n center={x=29404193.000,y=10432768.000,z=19554.131},\n name='Thades Moon 2',\n planetarySystemId=0,\n radius=15000\n },\n [40]={\n GM=141264000,\n bodyId=40,\n center={x=-13503090.000,y=55594325.000,z=769838.640},\n name='Talemai Moon 2',\n planetarySystemId=0,\n radius=12000\n },\n [41]={\n GM=106830900,\n bodyId=41,\n center={x=-12800515.000,y=55700259.000,z=325207.840},\n name='Talemai Moon 3',\n planetarySystemId=0,\n radius=11000\n },\n [42]={\n GM=264870000,\n bodyId=42,\n center={x=-13058408.000,y=55781856.000,z=740177.760},\n name='Talemai Moon 1',\n planetarySystemId=0,\n radius=15000\n },\n [50]={\n GM=499917600,\n bodyId=50,\n center={x=-43902841.780,y=22261034.700,z=-48862386.000},\n name='Feli Moon 1',\n planetarySystemId=0,\n radius=14000\n },\n [70]={\n GM=396912600,\n bodyId=70,\n center={x=58969616.000,y=29797945.000,z=57969449.000},\n name='Sinnen Moon 1',\n planetarySystemId=0,\n radius=17000\n },\n [100]={\n GM=13975172474,\n bodyId=100,\n center={x=98865536.000,y=-13534464.000,z=-934461.990},\n name='Lacobus',\n planetarySystemId=0,\n radius=55650\n },\n [101]={\n GM=264870000,\n bodyId=101,\n center={x=98905288.170,y=-13950921.100,z=-647589.530},\n name='Lacobus Moon 3',\n planetarySystemId=0,\n radius=15000\n },\n [102]={\n GM=444981600,\n bodyId=102,\n center={x=99180968.000,y=-13783862.000,z=-926156.400},\n name='Lacobus Moon 1',\n planetarySystemId=0,\n radius=18000\n },\n [103]={\n GM=211503600,\n bodyId=103,\n center={x=99250052.000,y=-13629215.000,z=-1059341.400},\n name='Lacobus Moon 2',\n planetarySystemId=0,\n radius=14000\n },\n [110]={\n GM=9204742375,\n bodyId=110,\n center={x=14165536.000,y=-85634465.000,z=-934464.300},\n name='Symeon',\n planetarySystemId=0,\n radius=49050\n },\n [120]={\n GM=7135606629,\n bodyId=120,\n center={x=2865536.700,y=-99034464.000,z=-934462.020},\n name='Ion',\n planetarySystemId=0,\n radius=44950\n },\n [121]={\n GM=106830900,\n bodyId=121,\n center={x=2472916.800,y=-99133747.000,z=-1133582.800},\n name='Ion Moon 1',\n planetarySystemId=0,\n radius=11000\n },\n [122]={\n GM=176580000,\n bodyId=122,\n center={x=2995424.500,y=-99275010.000,z=-1378480.700},\n name='Ion Moon 2',\n planetarySystemId=0,\n radius=15000\n }\n }\n }\n end\nfunction PlanetRef()\n--[[\n Provide coordinate transforms and access to kinematic related parameters\n Author: JayleBreak\n Usage (unit.start):\n PlanetaryReference = require('planetref')\n galaxyReference = PlanetaryReference(referenceTableSource)\n helios = galaxyReference[0] -- PlanetaryReference.PlanetarySystem instance\n alioth = helios[2] -- PlanetaryReference.BodyParameters instance\n Methods:\n PlanetaryReference:getPlanetarySystem - based on planetary system ID.\n PlanetaryReference.isMapPosition - 'true' if an instance of 'MapPosition'\n PlanetaryReference.createBodyParameters - for entry into reference table\n PlanetaryReference.BodyParameters - a class containing a body's information.\n PlanetaryReference.MapPosition - a class for map coordinates\n PlanetaryReference.PlanetarySystem - a container for planetary system info.\n PlanetarySystem:castIntersections - from a position in a given direction.\n PlanetarySystem:closestBody - to the specified coordinates.\n PlanetarySystem:convertToBodyIdAndWorldCoordinates - from map coordinates.\n PlanetarySystem:getBodyParameters - from reference table.\n PlanetarySystem:getPlanetarySystemId - for the instance.\n BodyParameters:convertToWorldCoordinates - from map coordinates\n BodyParameters:convertToMapPosition - from world coordinates\n BodyParameters:getAltitude - of world coordinates\n BodyParameters:getDistance - from center to world coordinates\n BodyParameters:getGravity - at a given position in world coordinates.\n Description\n An instance of the 'PlanetaryReference' \"class\" can contain transform and\n kinematic reference information for all planetary systems in DualUniverse.\n Each planetary system is identified by a numeric identifier. Currently,\n the only planetary system, Helios, has the identifier: zero. This \"class\"\n supports the indexing ('[]') operation which is equivalent to the\n use of the 'getPlanetarySystem' method. It also supports the 'pairs()'\n method for iterating over planetary systems.\n\n An instance of the 'PlanetarySystem' \"class\" contains all reference\n information for a specific system. It supports the indexing ('[]') and\n 'pairs()' functions which allows iteration over each \"body\" in the\n system where the key is the numeric body ID. It also supports the\n 'tostring()' method.\n An instance of the 'BodyParameters' \"class\" contains all reference\n information for a single celestial \"body\" (a moon or planet). It supports\n the 'tostring()' method, and contains the data members:\n planetarySystemId - numeric planetary system ID\n bodyId - numeric body ID\n radius - radius of the body in meters (zero altitude)\n center - world coordinates of the body's center position\n GM - the gravitation parameter (g = GM/radius^2)\n Note that the user is allowed to add custom fields (e.g. body name), but\n should insure that complex table values have the '__tostring' metamethod\n implemented.\n Transform and Kinematics:\n \"World\" coordinates is a cartesian coordinate system with an origin at an\n arbitrary fixed point in a planetary system and with distances measured in\n meters. The coordinates are expressible either as a simple table of 3 values\n or an instance of the 'vec3' class. In either case, the planetary system\n identity is implicit.\n \"Map\" coordinates is a geographic coordinate system with an origin at the\n center of an identified (by a numeric value) celestial body which is a\n member of an identified (also a numeric value) planetary system. Note that\n the convention that latitude, longitude, and altitude values will be the\n position's x, y, and z world coordinates in the special case of body ID 0.\n The kinematic parameters in the reference data permit calculations of the\n gravitational attraction of the celestial body on other objects.\n Reference Data:\n This is an example of reference data with a single entry assigned to\n planetary system ID 0, and body ID 2 ('Alioth'):\n referenceTable = {\n [0] = { [2] = { planetarySystemId = 0,\n bodyId = 2,\n radius = 126068,\n center = vec3({x=-8, y=-8, z=-126303}),\n GM = 1.572199+11 } -- as in F=-GMm/r^2\n }\n }\n ref=PlanetaryReference(referenceTable)\n Collecting Reference Data:\n A combination of information from the \"Map\" screen in the DU user interface,\n and values reported by the DU Lua API can be the source of the reference\n table's data (planetarySystemId, bodyId, and surfaceArea is from the user\n interface):\n referenceTable = {}\n referenceTable[planetarySystemId][bodyId] =\n PlanetaryReference.createBodyParameters(planetarySystemId,\n bodyId,\n surfaceArea,\n core.getConstructWorldPos(),\n core.getWorldVertical(),\n core.getAltitude(),\n core.g())\n Adapting Data Sources:\n Other sources of data can be adapted or converted. An example of adapting a\n table, defined in the file: 'planets.lua', containing information on a single\n planetary system and using celestial body name as the key follows (note that\n a 'name' field is added to the BodyParameters instance transparently after\n construction, and the '__pairs' meta function is required to support the\n 'closestBody' and '__tostring' methods):\n ref=PlanetaryReference(\n {[0] = setmetatable(require('planets'),\n { __index = function(bodies, bodyId)\n for _,v in pairs(bodies) do\n if v and v.bodyId == bodyId then return v end\n end\n return nil\n end,\n __pairs = function(bodies)\n return function(t, k)\n local nk, nv = next(t, k)\n if nv then\n local GM = nv.gravity * nv.radius^2\n local bp = BodyParameters(0,\n nv.id,\n nv.radius,\n nv.pos,\n GM)\n bp.name = nk\n return nk, bp\n end\n return nk, nv\n end, bodies, nil\n end })\n })\n\n Converting Data Sources:\n An instance of 'PlanetaryReference' that has been adapted to a data source\n can be used to convert that source to simple table. For example,\n using the adapted instance shown above:\n load('convertedData=' .. tostring(ref))()\n newRef=PlanetaryReference(convertedData)\n Also See: kepler.lua\n ]]--\n--[[ START OF LOCAL IMPLEMENTATION DETAILS ]]--\n-- Type checks\nlocal function isNumber(n) return type(n) == 'number' end\nlocal function isSNumber(n) return type(tonumber(n)) == 'number' end\nlocal function isTable(t) return type(t) == 'table' end\nlocal function isString(s) return type(s) == 'string' end\nlocal function isVector(v) return isTable(v)\n and isNumber(v.x and v.y and v.z) end\nlocal function isMapPosition(m) return isTable(m) and isNumber(m.latitude and\n m.longitude and\n m.altitude and\n m.bodyId and\n m.systemId) end\n-- Constants\nlocal deg2rad = math.pi/180\nlocal rad2deg = 180/math.pi\nlocal epsilon = 1e-10\nlocal num = ' *([+-]?%d+%.?%d*e?[+-]?%d*)'\nlocal posPattern = '::pos{' .. num .. ',' .. num .. ',' .. num .. ',' ..\n num .. ',' .. num .. '}'\n-- Utilities\nlocal utils = require('cpml.utils')\nlocal vec3 = require('cpml.vec3')\nlocal clamp = utils.clamp\nlocal function float_eq(a,b)\n if a == 0 then return math.abs(b) < 1e-09 end\n if b == 0 then return math.abs(a) < 1e-09 end\n return math.abs(a - b) < math.max(math.abs(a),math.abs(b))*epsilon\nend\nlocal function formatNumber(n)\n local result = string.gsub(\n string.reverse(string.format('%.4f',n)),\n '^0*%.?','')\n return result == '' and '0' or string.reverse(result)\nend\nlocal function formatValue(obj)\n if isVector(obj) then\n return string.format('{x=%.3f,y=%.3f,z=%.3f}', obj.x, obj.y, obj.z)\n end\n if isTable(obj) and not getmetatable(obj) then\n local list = {}\n local nxt = next(obj)\n if type(nxt) == 'nil' or nxt == 1 then -- assume this is an array\n list = obj\n else\n for k,v in pairs(obj) do\n local value = formatValue(v)\n if type(k) == 'number' then\n table.insert(list, string.format('[%s]=%s', k, value))\n else\n table.insert(list, string.format('%s=%s', k, value))\n end\n end\n end\n return string.format('{%s}', table.concat(list, ','))\n end\n if isString(obj) then\n return string.format(\"'%s'\", obj:gsub(\"'\",[[\\']]))\n end\n return tostring(obj)\nend\n-- CLASSES\n-- BodyParameters: Attributes of planetary bodies (planets and moons)\nlocal BodyParameters = {}\nBodyParameters.__index = BodyParameters\nBodyParameters.__tostring =\n function(obj, indent)\n local sep = indent or ''\n local keys = {}\n for k in pairs(obj) do table.insert(keys, k) end\n table.sort(keys)\n local list = {}\n for _, k in ipairs(keys) do\n local value = formatValue(obj[k])\n if type(k) == 'number' then\n table.insert(list, string.format('[%s]=%s', k, value))\n else\n table.insert(list, string.format('%s=%s', k, value))\n end\n end\n if indent then\n return string.format('%s%s',\n indent,\n table.concat(list, ',\\n' .. indent))\n end\n return string.format('{%s}', table.concat(list, ','))\n end\nBodyParameters.__eq = function(lhs, rhs)\n return lhs.planetarySystemId == rhs.planetarySystemId and\n lhs.bodyId == rhs.bodyId and\n float_eq(lhs.radius, rhs.radius) and\n float_eq(lhs.center.x, rhs.center.x) and\n float_eq(lhs.center.y, rhs.center.y) and\n float_eq(lhs.center.z, rhs.center.z) and\n float_eq(lhs.GM, rhs.GM)\n end\nlocal function mkBodyParameters(systemId, bodyId, radius, worldCoordinates, GM)\n -- 'worldCoordinates' can be either table or vec3\n assert(isSNumber(systemId),\n 'Argument 1 (planetarySystemId) must be a number:' .. type(systemId))\n assert(isSNumber(bodyId),\n 'Argument 2 (bodyId) must be a number:' .. type(bodyId))\n assert(isSNumber(radius),\n 'Argument 3 (radius) must be a number:' .. type(radius))\n assert(isTable(worldCoordinates),\n 'Argument 4 (worldCoordinates) must be a array or vec3.' ..\n type(worldCoordinates))\n assert(isSNumber(GM),\n 'Argument 5 (GM) must be a number:' .. type(GM))\n return setmetatable({planetarySystemId = tonumber(systemId),\n bodyId = tonumber(bodyId),\n radius = tonumber(radius),\n center = vec3(worldCoordinates),\n GM = tonumber(GM) }, BodyParameters)\nend\n-- MapPosition: Geographical coordinates of a point on a planetary body.\nlocal MapPosition = {}\nMapPosition.__index = MapPosition\nMapPosition.__tostring = function(p)\n return string.format('::pos{%d,%d,%s,%s,%s}',\n p.systemId,\n p.bodyId,\n formatNumber(p.latitude*rad2deg),\n formatNumber(p.longitude*rad2deg),\n formatNumber(p.altitude))\n end\nMapPosition.__eq = function(lhs, rhs)\n return lhs.bodyId == rhs.bodyId and\n lhs.systemId == rhs.systemId and\n float_eq(lhs.latitude, rhs.latitude) and\n float_eq(lhs.altitude, rhs.altitude) and\n (float_eq(lhs.longitude, rhs.longitude) or\n float_eq(lhs.latitude, math.pi/2) or\n float_eq(lhs.latitude, -math.pi/2))\n end\n-- latitude and longitude are in degrees while altitude is in meters\nlocal function mkMapPosition(overload, bodyId, latitude, longitude, altitude)\n local systemId = overload -- Id or '::pos{...}' string\n if isString(overload) and not longitude and not altitude and\n not bodyId and not latitude then\n systemId, bodyId, latitude, longitude, altitude =\n string.match(overload, posPattern)\n assert(systemId, 'Argument 1 (position string) is malformed.')\n else\n assert(isSNumber(systemId),\n 'Argument 1 (systemId) must be a number:' .. type(systemId))\n assert(isSNumber(bodyId),\n 'Argument 2 (bodyId) must be a number:' .. type(bodyId))\n assert(isSNumber(latitude),\n 'Argument 3 (latitude) must be in degrees:' .. type(latitude))\n assert(isSNumber(longitude),\n 'Argument 4 (longitude) must be in degrees:' .. type(longitude))\n assert(isSNumber(altitude),\n 'Argument 5 (altitude) must be in meters:' .. type(altitude))\n end\n systemId = tonumber(systemId)\n bodyId = tonumber(bodyId)\n latitude = tonumber(latitude)\n longitude = tonumber(longitude)\n altitude = tonumber(altitude)\n if bodyId == 0 then -- this is a hack to represent points in space\n return setmetatable({latitude = latitude,\n longitude = longitude,\n altitude = altitude,\n bodyId = bodyId,\n systemId = systemId}, MapPosition)\n end\n return setmetatable({latitude = deg2rad*clamp(latitude, -90, 90),\n longitude = deg2rad*(longitude % 360),\n altitude = altitude,\n bodyId = bodyId,\n systemId = systemId}, MapPosition)\nend\n-- PlanetarySystem - map body IDs to BodyParameters\nlocal PlanetarySystem = {}\nPlanetarySystem.__index = PlanetarySystem\nPlanetarySystem.__tostring =\n function (obj, indent)\n local sep = indent and (indent .. ' ' )\n local bdylist = {}\n local keys = {}\n for k in pairs(obj) do table.insert(keys, k) end\n table.sort(keys)\n for _, bi in ipairs(keys) do\n bdy = obj[bi]\n local bdys = BodyParameters.__tostring(bdy, sep)\n if indent then\n table.insert(bdylist,\n string.format('[%s]={\\n%s\\n%s}',\n bi, bdys, indent))\n else\n table.insert(bdylist, string.format(' [%s]=%s', bi, bdys))\n end\n end\n if indent then\n return string.format('\\n%s%s%s',\n indent,\n table.concat(bdylist, ',\\n' .. indent),\n indent)\n end\n return string.format('{\\n%s\\n}', table.concat(bdylist, ',\\n'))\n end\nlocal function mkPlanetarySystem(referenceTable)\n local atlas = {}\n local pid\n for _, v in pairs(referenceTable) do\n local id = v.planetarySystemId\n if type(id) ~= 'number' then\n error('Invalid planetary system ID: ' .. tostring(id))\n elseif pid and id ~= pid then\n error('Mismatch planetary system IDs: ' .. id .. ' and '\n .. pid)\n end\n local bid = v.bodyId\n if type(bid) ~= 'number' then\n error('Invalid body ID: ' .. tostring(bid))\n elseif atlas[bid] then\n error('Duplicate body ID: ' .. tostring(bid))\n end\n setmetatable(v.center, getmetatable(vec3.unit_x))\n atlas[bid] = setmetatable(v, BodyParameters)\n pid = id\n end\n return setmetatable(atlas, PlanetarySystem)\nend\n-- PlanetaryReference - map planetary system ID to PlanetarySystem\nPlanetaryReference = {}\nlocal function mkPlanetaryReference(referenceTable)\n return setmetatable({ galaxyAtlas = referenceTable or {} },\n PlanetaryReference)\nend\nPlanetaryReference.__index =\n function(t,i)\n if type(i) == 'number' then\n local system = t.galaxyAtlas[i]\n return mkPlanetarySystem(system)\n end\n return rawget(PlanetaryReference, i)\n end\nPlanetaryReference.__pairs =\n function(obj)\n return function(t, k)\n local nk, nv = next(t, k)\n return nk, nv and mkPlanetarySystem(nv)\n end, obj.galaxyAtlas, nil\n end\nPlanetaryReference.__tostring =\n function (obj)\n local pslist = {}\n for _,ps in pairs(obj or {}) do\n local psi = ps:getPlanetarySystemId()\n local pss = PlanetarySystem.__tostring(ps, ' ')\n table.insert(pslist,\n string.format(' [%s]={%s\\n }', psi, pss))\n end\n return string.format('{\\n%s\\n}\\n', table.concat(pslist,',\\n'))\n end\n--[[ START OF PUBLIC INTERFACE ]]--\n-- PlanetaryReference CLASS METHODS:\n--\n-- BodyParameters - create an instance of BodyParameters class\n-- planetarySystemId [in]: the body's planetary system ID.\n-- bodyId [in]: the body's ID.\n-- radius [in]: the radius in meters of the planetary body.\n-- bodyCenter [in]: the world coordinates of the center (vec3 or table).\n-- GM [in]: the body's standard gravitational parameter.\n-- return: an instance of BodyParameters class.\n--\nPlanetaryReference.BodyParameters = mkBodyParameters\n--\n-- MapPosition - create an instance of the MapPosition class\n-- overload [in]: either a planetary system ID or a position string ('::pos...')\n-- bodyId [in]: (ignored if overload is a position string) the body's ID.\n-- latitude [in]: (ignored if overload is a position string) the latitude.\n-- longitude [in]:(ignored if overload is a position string) the longitude.\n-- altitude [in]: (ignored if overload is a position string) the altitude.\n-- return: the class instance\n--\nPlanetaryReference.MapPosition = mkMapPosition\n--\n-- PlanetarySystem - create an instance of PlanetarySystem class\n-- referenceData [in]: a table (indexed by bodyId) of body reference info.\n-- return: the class instance\n--\nPlanetaryReference.PlanetarySystem = mkPlanetarySystem\n--\n-- createBodyParameters - create an instance of BodyParameters class\n-- planetarySystemId [in]: the body's planetary system ID.\n-- bodyId [in]: the body's ID.\n-- surfaceArea [in]: the body's surface area in square meters.\n-- aPosition [in]: world coordinates of a position near the body.\n-- verticalAtPosition [in]: a vector pointing towards the body center.\n-- altitudeAtPosition [in]: the altitude in meters at the position.\n-- gravityAtPosition [in]: the magnitude of the gravitational acceleration.\n-- return: an instance of BodyParameters class.\n--\nfunction PlanetaryReference.createBodyParameters(planetarySystemId,\n bodyId,\n surfaceArea,\n aPosition,\n verticalAtPosition,\n altitudeAtPosition,\n gravityAtPosition)\n assert(isSNumber(planetarySystemId),\n 'Argument 1 (planetarySystemId) must be a number:' ..\n type(planetarySystemId))\n assert(isSNumber(bodyId),\n 'Argument 2 (bodyId) must be a number:' .. type(bodyId))\n assert(isSNumber(surfaceArea),\n 'Argument 3 (surfaceArea) must be a number:' .. type(surfaceArea))\n assert(isTable(aPosition),\n 'Argument 4 (aPosition) must be an array or vec3:' ..\n type(aPosition))\n assert(isTable(verticalAtPosition),\n 'Argument 5 (verticalAtPosition) must be an array or vec3:' ..\n type(verticalAtPosition))\n assert(isSNumber(altitudeAtPosition),\n 'Argument 6 (altitude) must be in meters:' ..\n type(altitudeAtPosition))\n assert(isSNumber(gravityAtPosition),\n 'Argument 7 (gravityAtPosition) must be number:' ..\n type(gravityAtPosition))\n local radius = math.sqrt(surfaceArea/4/math.pi)\n local distance = radius + altitudeAtPosition\n local center = vec3(aPosition) + distance*vec3(verticalAtPosition)\n local GM = gravityAtPosition * distance * distance\n return mkBodyParameters(planetarySystemId, bodyId, radius, center, GM)\nend\n--\n-- isMapPosition - check for the presence of the 'MapPosition' fields\n-- valueToTest [in]: the value to be checked\n-- return: 'true' if all required fields are present in the input value\n--\nPlanetaryReference.isMapPosition = isMapPosition\n-- PlanetaryReference INSTANCE METHODS:\n--\n-- getPlanetarySystem - get the planetary system using ID or MapPosition as key\n-- overload [in]: either the planetary system ID or a MapPosition that has it.\n-- return: instance of 'PlanetarySystem' class or nil on error\n--\nfunction PlanetaryReference:getPlanetarySystem(overload)\n --if galaxyAtlas then\n local planetarySystemId = overload\n if isMapPosition(overload) then\n planetarySystemId = overload.systemId\n end\n if type(planetarySystemId) == 'number' then\n local system = self.galaxyAtlas[i]\n if system then\n if getmetatable(nv) ~= PlanetarySystem then\n system = mkPlanetarySystem(system)\n end\n return system\n end\n end\n --end\n --return nil\nend\n-- PlanetarySystem INSTANCE METHODS:\n--\n-- castIntersections - Find the closest body that intersects a \"ray cast\".\n-- origin [in]: the origin of the \"ray cast\" in world coordinates\n-- direction [in]: the direction of the \"ray cast\" as a 'vec3' instance.\n-- sizeCalculator [in]: (default: returns 1.05*radius) Returns size given body.\n-- bodyIds[in]: (default: all IDs in system) check only the given IDs.\n-- return: The closest body that blocks the cast or 'nil' if none.\n--\nfunction PlanetarySystem:castIntersections(origin,\n direction,\n sizeCalculator,\n bodyIds)\n local sizeCalculator = sizeCalculator or\n function (body) return 1.05*body.radius end\n local candidates = {}\n if bodyIds then\n for _,i in ipairs(bodyIds) do candidates[i] = self[i] end\n else\n bodyIds = {}\n for k,body in pairs(self) do\n table.insert(bodyIds, k)\n candidates[k] = body\n end\n end\n local function compare(b1,b2)\n local v1 = candidates[b1].center - origin\n local v2 = candidates[b2].center - origin\n return v1:len() < v2:len()\n end\n table.sort(bodyIds, compare)\n local dir = direction:normalize()\n for i, id in ipairs(bodyIds) do\n local body = candidates[id]\n local c_oV3 = body.center - origin\n local radius = sizeCalculator(body)\n local dot = c_oV3:dot(dir)\n local desc = dot^2 - (c_oV3:len2() - radius^2)\n if desc >= 0 then\n local root = math.sqrt(desc)\n local farSide = dot + root\n local nearSide = dot - root\n if nearSide > 0 then\n return body, farSide, nearSide\n elseif farSide > 0 then\n return body, farSide, nil\n end\n end\n end\n return nil, nil, nil\nend\n--\n-- closestBody - find the closest body to a given set of world coordinates\n-- coordinates [in]: the world coordinates of position in space\n-- return: an instance of the BodyParameters object closest to 'coordinates'\n--\nfunction PlanetarySystem:closestBody(coordinates)\n assert(type(coordinates) == 'table', 'Invalid coordinates.')\n local minDistance2, body\n local coord = vec3(coordinates)\n for _,params in pairs(self) do\n local distance2 = (params.center - coord):len2()\n if not body or distance2 < minDistance2 then\n body = params\n minDistance2 = distance2\n end\n end\n return body\nend\n--\n-- convertToBodyIdAndWorldCoordinates - map to body Id and world coordinates\n-- overload [in]: an instance of MapPosition or a position string ('::pos...)\n-- return: a vec3 instance containing the world coordinates or 'nil' on error.\n--\nfunction PlanetarySystem:convertToBodyIdAndWorldCoordinates(overload)\n local mapPosition = overload\n if isString(overload) then\n mapPosition = mkMapPosition(overload)\n end\n if mapPosition.bodyId == 0 then\n return 0, vec3(mapPosition.latitude,\n mapPosition.longitude,\n mapPosition.altitude)\n end\n local params = self:getBodyParameters(mapPosition)\n if params then\n return mapPosition.bodyId,\n params:convertToWorldCoordinates(mapPosition)\n end\nend\n--\n-- getBodyParameters - get or create an instance of BodyParameters class\n-- overload [in]: either an instance of MapPosition or a body's ID.\n-- return: a BodyParameters instance or 'nil' if body ID is not found.\n--\nfunction PlanetarySystem:getBodyParameters(overload)\n local bodyId = overload\n if isMapPosition(overload) then\n bodyId = overload.bodyId\n end\n assert(isSNumber(bodyId),\n 'Argument 1 (bodyId) must be a number:' .. type(bodyId))\n return self[bodyId]\nend\n--\n-- getPlanetarySystemId - get the planetary system ID for this instance\n-- return: the planetary system ID or nil if no planets are in the system.\n--\nfunction PlanetarySystem:getPlanetarySystemId()\n local k, v = next(self)\n return v and v.planetarySystemId\nend\n-- BodyParameters INSTANCE METHODS:\n--\n-- convertToMapPosition - create an instance of MapPosition from coordinates\n-- worldCoordinates [in]: the world coordinates of the map position.\n-- return: an instance of MapPosition class\n--\nfunction BodyParameters:convertToMapPosition(worldCoordinates)\n assert(isTable(worldCoordinates),\n 'Argument 1 (worldCoordinates) must be an array or vec3:' ..\n type(worldCoordinates))\n local worldVec = vec3(worldCoordinates)\n if self.bodyId == 0 then\n return setmetatable({latitude = worldVec.x,\n longitude = worldVec.y,\n altitude = worldVec.z,\n bodyId = 0,\n systemId = self.planetarySystemId}, MapPosition)\n end\n local coords = worldVec - self.center\n local distance = coords:len()\n local altitude = distance - self.radius\n local latitude = 0\n local longitude = 0\n if not float_eq(distance, 0) then\n local phi = math.atan(coords.y, coords.x)\n longitude = phi >= 0 and phi or (2*math.pi + phi)\n latitude = math.pi/2 - math.acos(coords.z/distance)\n end\n return setmetatable({latitude = latitude,\n longitude = longitude,\n altitude = altitude,\n bodyId = self.bodyId,\n systemId = self.planetarySystemId}, MapPosition)\nend\n--\n-- convertToWorldCoordinates - convert a map position to world coordinates\n-- overload [in]: an instance of MapPosition or a position string ('::pos...')\n--\nfunction BodyParameters:convertToWorldCoordinates(overload)\n local mapPosition = isString(overload) and\n mkMapPosition(overload) or overload\n if mapPosition.bodyId == 0 then -- support deep space map position\n return vec3(mapPosition.latitude,\n mapPosition.longitude,\n mapPosition.altitude)\n end\n assert(isMapPosition(mapPosition),\n 'Argument 1 (mapPosition) is not an instance of \"MapPosition\".')\n assert(mapPosition.systemId == self.planetarySystemId,\n 'Argument 1 (mapPosition) has a different planetary system ID.')\n assert(mapPosition.bodyId == self.bodyId,\n 'Argument 1 (mapPosition) has a different planetary body ID.')\n local xproj = math.cos(mapPosition.latitude)\n return self.center + (self.radius + mapPosition.altitude) *\n vec3(xproj*math.cos(mapPosition.longitude),\n xproj*math.sin(mapPosition.longitude),\n math.sin(mapPosition.latitude))\nend\n--\n-- getAltitude - calculate the altitude of a point given in world coordinates.\n-- worldCoordinates [in]: the world coordinates of the point.\n-- return: the altitude in meters\n--\nfunction BodyParameters:getAltitude(worldCoordinates)\n return (vec3(worldCoordinates) - self.center):len() - self.radius\nend\n--\n-- getDistance - calculate the distance to a point given in world coordinates.\n-- worldCoordinates [in]: the world coordinates of the point.\n-- return: the distance in meters\n--\nfunction BodyParameters:getDistance(worldCoordinates)\n return (vec3(worldCoordinates) - self.center):len()\nend\n--\n-- getGravity - calculate the gravity vector induced by the body.\n-- worldCoordinates [in]: the world coordinates of the point.\n-- return: the gravity vector in meter/seconds^2\n--\nfunction BodyParameters:getGravity(worldCoordinates)\n local radial = self.center - vec3(worldCoordinates) -- directed towards body\n local len2 = radial:len2()\n return (self.GM/len2) * radial/math.sqrt(len2)\nend\n-- end of module\nreturn setmetatable(PlanetaryReference,\n { __call = function(_,...)\n return mkPlanetaryReference(...)\n end })\nend\nfunction Keplers()\n --[[\n Provides methods for computing orbital information for an object\n Usage:\n Kepler = require('autoconf.custom.kepler')\n alioth = Kepler({ GM=157470826617,\n bodyId=2,\n center={x=-8.000,y=-8.000,z=-126303.000},\n name='Alioth',\n planetarySystemId=0,\n radius=126068\n })\n altitude = 6000\n position = '::pos{0,2,0,0,6000}'\n e, o = alioth:escapeAndOrbitalSpeed(altitude)\n orbit = alioth:orbitalParameters(position, {0, o+1, 0})\n print(\"Eccentricity \" .. orbit.eccentricity)\n print(\"Perihelion \" .. orbit.periapsis.altitude)\n print(\"Max. speed \" .. orbit.periapsis.speed)\n print(\"Circular orbit speed \" .. orbit.periapsis.circularOrbitSpeed)\n print(\"Aphelion \" .. orbit.apoapsis.altitude)\n print(\"Min. speed \" .. orbit.apoapsis.speed)\n print(\"Orbital period \" .. orbit.period)\n --- output:\n Eccentricity 0.0018324307017878\n Perihelion 6000.0\n Max. speed 1092.9462297033\n Circular orbit speed 1091.9462297033\n Aphelion 6484.8994605062\n Min. speed 1088.9480596194\n Orbital period 762.02818214049\n Methods:\n Kepler:escapeAndOrbitalSpeed - for a given celestial body and altitude.\n Kepler:orbitalParameters - for a given massless object and a celestial body.\n Description\n The motion of an object in the vicinity of substantially larger mass is\n in the domain of the \"2-body problem\". By assuming the object whose motion\n is of interest is of negligable mass simplifies the calculations of:\n the speed to escape the body, the speed of a circular orbit, and the\n parameters defining the orbit of the object (or the lack of orbit as the\n case may be).\n Orbital Parameters:\n periapsis - the closest approach to the planet\n apoapsis - the furthest point from the planet if in orbit (otherwise nil)\n eccentricity - 0 for circular orbits\n <1 for elliptical orbits\n 1 for parabiolic trajectory\n >1 for hyperbolic trajectory\n period - time (in seconds) to complete an orbit\n Also See: planetref.lua\n]]--\nlocal vec3 = require('cpml.vec3')\nlocal PlanetRef = PlanetRef()\nlocal function isString(s) return type(s) == 'string' end\nlocal function isTable(t) return type(t) == 'table' end\nlocal function float_eq(a,b)\n if a == 0 then return math.abs(b) < 1e-09 end\n if b == 0 then return math.abs(a) < 1e-09 end\n return math.abs(a - b) < math.max(math.abs(a),math.abs(b))*epsilon\nend\nKepler = {}\nKepler.__index = Kepler\n--\n-- escapeAndOrbitalSpeed - speed required to escape and for a circular orbit\n-- altitude [in]: the height of the orbit in meters above \"sea-level\"\n-- return: the speed in m/s needed to escape the celestial body and to orbit it.\n--\nfunction Kepler:escapeAndOrbitalSpeed(altitude)\n assert(self.body)\n -- P = -GMm/r and KE = mv^2/2 (no lorentz factor used)\n -- mv^2/2 = GMm/r\n -- v^2 = 2GM/r\n -- v = sqrt(2GM/r1)\n local distance = altitude + self.body.radius\n if not float_eq(distance, 0) then\n local orbit = math.sqrt(self.body.GM/distance)\n return math.sqrt(2)*orbit, orbit\n end\n return nil, nil\nend\n--\n-- orbitalParameters: determine the orbital elements for a two-body system.\n-- overload [in]: the world coordinates or map coordinates of a massless object.\n-- velocity [in]: The velocity of the massless point object in m/s.\n-- return: the 6 orbital elements for the massless object.\n--\nfunction Kepler:orbitalParameters(overload, velocity)\n assert(self.body)\n assert(isTable(overload) or isString(overload))\n assert(isTable(velocity))\n local pos = (isString(overload) or PlanetRef.isMapPosition(overload)) and\n self.body:convertToWorldCoordinates(overload) or\n vec3(overload)\n local v = vec3(velocity)\n local r = pos - self.body.center\n local v2 = v:len2()\n local d = r:len()\n local mu = self.body.GM\n local e = ((v2 - mu/d)*r - r:dot(v)*v)/mu\n local a = mu/(2*mu/d - v2)\n local ecc = e:len()\n local dir = e:normalize()\n local pd = a*(1-ecc)\n local ad = a*(1+ecc)\n local per = pd*dir + self.body.center\n local apo = ecc <= 1 and -ad*dir + self.body.center or nil\n local trm = math.sqrt(a*mu*(1-ecc*ecc))\n local Period = apo and 2*math.pi*math.sqrt(a^3/mu)\n -- These are great and all, but, I need more.\n local trueAnomaly = math.acos((e:dot(r))/(ecc*d))\n if r:dot(v) < 0 then\n trueAnomaly = -(trueAnomaly - 2*math.pi)\n end\n -- Apparently... cos(EccentricAnomaly) = (cos(trueAnomaly) + eccentricity)/(1 + eccentricity * cos(trueAnomaly))\n local EccentricAnomaly = math.acos((math.cos(trueAnomaly) + ecc)/(1 + ecc * math.cos(trueAnomaly)))\n -- Then.... apparently if this is below 0, we should add 2pi to it\n -- I think also if it's below 0, we're past the apoapsis?\n local timeTau = EccentricAnomaly\n if timeTau < 0 then\n timeTau = timeTau + 2*math.pi\n end\n -- So... time since periapsis...\n -- Is apparently easy if you get mean anomly. t = M/n where n is mean motion, = 2*pi/Period\n\n\n local MeanAnomaly = timeTau - ecc * math.sin(timeTau)\n local TimeSincePeriapsis = MeanAnomaly/(2*math.pi/Period)\n --system.print(MeanAnomaly .. \" - \" .. TimeSincePeriapsis .. \" - \" .. Period .. \" - \" .. EccentricAnomaly .. \" - \" .. timeTau .. \" - \" .. trueAnomaly)\n -- Mean anom is 0 at periapsis, positive before it... and positive after it.\n -- I guess this is why I needed to use timeTau and not EccentricAnomaly here\n\n local TimeToPeriapsis = Period - TimeSincePeriapsis\n local TimeToApoapsis = TimeToPeriapsis + Period/2\n if trueAnomaly - math.pi > 0 then -- TBH I think something's wrong in my formulas because I needed this.\n TimeToPeriapsis = TimeSincePeriapsis\n TimeToApoapsis = TimeToPeriapsis + Period/2\n end\n if TimeToApoapsis > Period then\n TimeToApoapsis = TimeToApoapsis - Period\n end\n return { periapsis = { position = per,\n speed = trm/pd,\n circularOrbitSpeed = math.sqrt(mu/pd),\n altitude = pd - self.body.radius},\n apoapsis = apo and\n { position = apo,\n speed = trm/ad,\n circularOrbitSpeed = math.sqrt(mu/ad),\n altitude = ad - self.body.radius},\n currentVelocity = v,\n currentPosition = pos,\n eccentricity = ecc,\n period = Period,\n eccentricAnomaly = EccentricAnomaly,\n meanAnomaly = MeanAnomaly,\n timeToPeriapsis = TimeToPeriapsis,\n timeToApoapsis = TimeToApoapsis\n }\nend\n\nlocal function new(bodyParameters)\n local params = PlanetRef.BodyParameters(bodyParameters.planetarySystemId,\n bodyParameters.bodyId,\n bodyParameters.radius,\n bodyParameters.center,\n bodyParameters.GM)\n return setmetatable({body = params}, Kepler)\nend\nreturn setmetatable(Kepler, { __call = function(_,...) return new(...) end })\nend\nfunction Kinematics()\n --[[\n DualUniverse kinematic equations\n Author: JayleBreak\n Usage (unit.start):\n Kinematics = require('autoconf.custom.kinematics')\n Methods:\n computeAccelerationTime - \"relativistic\" version of t = (vf - vi)/a\n computeDistanceAndTime - Return distance & time needed to reach final speed.\n computeTravelTime - \"relativistic\" version of t=(sqrt(2ad+v^2)-v)/a\n Description\n DualUniverse increases the effective mass of constructs as their absolute\n speed increases by using the \"lorentz\" factor (from relativity) as the scale\n factor. This results in an upper bound on the absolute speed of constructs\n (excluding \"warp\" drive) that is set to 30 000 KPH (8 333 MPS). This module\n provides utilities for computing some physical quantities taking this\n scaling into account.\n]]--\nlocal Kinematic = {} -- just a namespace\nlocal C = 30000000/3600\nlocal C2 = C*C\nlocal ITERATIONS = 100 -- iterations over engine \"warm-up\" period\nlocal function lorentz(v) return 1/math.sqrt(1 - v*v/C2) end\n--\n-- computeAccelerationTime - \"relativistic\" version of t = (vf - vi)/a\n-- initial [in]: initial (positive) speed in meters per second.\n-- acceleration [in]: constant acceleration until 'finalSpeed' is reached.\n-- final [in]: the speed at the end of the time interval.\n-- return: the time in seconds spent in traversing the distance\n--\nfunction Kinematic.computeAccelerationTime(initial, acceleration, final)\n -- The low speed limit of following is: t=(vf-vi)/a (from: vf=vi+at)\n local k1 = C*math.asin(initial/C)\n return (C * math.asin(final/C) - k1)/acceleration\nend\n--\n-- computeDistanceAndTime - Return distance & time needed to reach final speed.\n-- initial[in]: Initial speed in meters per second.\n-- final[in]: Final speed in meters per second.\n-- restMass[in]: Mass of the construct at rest in Kg.\n-- thrust[in]: Engine's maximum thrust in Newtons.\n-- t50[in]: (default: 0) Time interval to reach 50% thrust in seconds.\n-- brakeThrust[in]: (default: 0) Constant thrust term when braking.\n-- return: Distance (in meters), time (in seconds) required for change.\n--\nfunction Kinematic.computeDistanceAndTime(initial,\n final,\n restMass,\n thrust,\n t50,\n brakeThrust)\n -- This function assumes that the applied thrust is colinear with the\n -- velocity. Furthermore, it does not take into account the influence\n -- of gravity, not just in terms of its impact on velocity, but also\n -- its impact on the orientation of thrust relative to velocity.\n -- These factors will introduce (usually) small errors which grow as\n -- the length of the trip increases.\n t50 = t50 or 0\n brakeThrust = brakeThrust or 0 -- usually zero when accelerating\n local tau0 = lorentz(initial)\n local speedUp = initial <= final\n local a0 = thrust * (speedUp and 1 or -1)/restMass\n local b0 = -brakeThrust/restMass\n local totA = a0+b0\n if speedUp and totA <= 0 or not speedUp and totA >= 0 then\n return -1, -1 -- no solution\n end\n local distanceToMax, timeToMax = 0, 0\n -- If, the T50 time is set, then assume engine is at zero thrust and will\n -- reach full thrust in 2*T50 seconds. Thrust curve is given by:\n -- Thrust: F(z)=(a0*(1+sin(z))+2*b0)/2 where z=pi*(t/t50 - 1)/2\n -- Acceleration is given by F(z)/m(z) where m(z) = m/sqrt(1-v^2/c^2)\n -- or v(z)' = (a0*(1+sin(z))+2*b0)*sqrt(1-v(z)^2/c^2)/2\n if a0 ~= 0 and t50 > 0 then\n -- Closed form solution for velocity exists:\n -- v(t) = -c*tan(w)/sqrt(tan(w)^2+1) => w = -asin(v/c)\n -- w=(pi*t*(a0/2+b0)-a0*t50*sin(pi*t/2/t50)+*pi*c*k1)/pi/c\n -- @ t=0, v(0) = vi\n -- pi*c*k1/pi/c = -asin(vi/c)\n -- k1 = asin(vi/c)\n local k1 = math.asin(initial/C)\n local c1 = math.pi*(a0/2+b0)\n local c2 = a0*t50\n local c3 = C*math.pi\n local v = function(t)\n local w = (c1*t - c2*math.sin(math.pi*t/2/t50) + c3*k1)/c3\n local tan = math.tan(w)\n return C*tan/math.sqrt(tan*tan+1)\n end\n local speedchk = speedUp and function(s) return s >= final end or\n function(s) return s <= final end\n timeToMax = 2*t50\n if speedchk(v(timeToMax)) then\n local lasttime = 0\n while math.abs(timeToMax - lasttime) > 0.5 do\n local t = (timeToMax + lasttime)/2\n if speedchk(v(t)) then\n timeToMax = t\n else\n lasttime = t\n end\n end\n end\n -- There is no closed form solution for distance in this case.\n -- Numerically integrate for time t=0 to t=2*T50 (or less)\n local lastv = initial\n local tinc = timeToMax/ITERATIONS\n for step = 1, ITERATIONS do\n local speed = v(step*tinc)\n distanceToMax = distanceToMax + (speed+lastv)*tinc/2\n lastv = speed\n end\n if timeToMax < 2*t50 then\n return distanceToMax, timeToMax\n end\n initial = lastv\n end\n -- At full thrust, acceleration only depends on the Lorentz factor:\n -- v(t)' = (F/m(v)) = a*sqrt(1-v(t)^2/c^2) where a = a0+b0\n -- -> v = c*sin((at+k1)/c)\n -- @ t=0, v=vi: k1 = c*asin(vi/c)\n -- -> t = (c*asin(v/c) - k1)/a\n -- x(t)' = c*sin((at+k1)/c)\n -- x = k2 - c^2 cos((at+k1)/c)/a\n -- @ t=0, x=0: k2 = c^2 * cos(k1/c)/a\n local k1 = C*math.asin(initial/C)\n local time = (C * math.asin(final/C) - k1)/totA\n local k2 = C2 *math.cos(k1/C)/totA\n local distance = k2 - C2 * math.cos((totA*time + k1)/C)/totA\n return distance+distanceToMax, time+timeToMax\nend\n--\n-- computeTravelTime - \"relativistic\" version of t=(sqrt(2ad+v^2)-v)/a\n-- initialSpeed [in]: initial (positive) speed in meters per second\n-- acceleration [in]: constant acceleration until 'distance' is traversed\n-- distance [in]: the distance traveled in meters\n-- return: the time in seconds spent in traversing the distance\n--\nfunction Kinematic.computeTravelTime(initial, acceleration, distance)\n -- The low speed limit of following is: t=(sqrt(2ad+v^2)-v)/a\n -- (from: d=vt+at^2/2)\n if distance == 0 then return 0 end\n if acceleration > 0 then\n local k1 = C*math.asin(initial/C)\n local k2 = C2*math.cos(k1/C)/acceleration\n return (C*math.acos(acceleration*(k2 - distance)/C2) - k1)/acceleration\n end\n assert(initial > 0, 'Acceleration and initial speed are both zero.')\n return distance/initial\nend\nfunction Kinematic.lorentz(v) return lorentz(v) end\nreturn Kinematic\nend\nPlanetaryReference = PlanetRef()\ngalaxyReference = PlanetaryReference(Atlas())\nKinematic = Kinematics()\nKep = Keplers()\nfunction getDistanceDisplayString(distance)\n local su = distance > 100000\n local result = \"\"\n if su then\n -- Convert to SU\n result = round(distance/1000/200,1) .. \" SU\"\n else\n -- Convert to KM\n result = round(distance/1000,1) .. \" KM\"\n end\n\n return result\nend\n\nPlanetaryReference = PlanetRef()\ngalaxyReference = PlanetaryReference(Atlas())\n\nMapScreenButtons = {}\nMapScreenMouseX = 0\nMapScreenMouseY = 0\nMapScreenMouseDown = false\nMapScreenButtonSelected = 0\nlocal worldPos = vec3(core.getConstructWorldPos())\nlocal locX = (worldPos.x/400000)\nlocal locY = (worldPos.y/400000)*(-1)\nlocal destX = 0\nlocal destY = 0\nlocal sudistance = 0\nlocal loc = vec3(core.getConstructWorldPos())\nlocal ion = galaxyReference[0][120] ---uses Atlas functions\nlocal thades = vec3(29165536.000, 10865536.000, 65536.000)\nlocal sinnen = vec3(58665536.000, 29665536.000, 58165536.000)\nlocal alioth = galaxyReference[0][2] ---uses Atlas functions\nlocal madis = vec3(17465536.000, 22665536.000, -34464.000)\nlocal jago = vec3(-94134464.000, 12765536.000, -3634464.000)\nlocal symeon = vec3(14165536.000, -85634464.000, -934464.000)\nlocal lacobus = vec3(98865536.000, -13534464.000, -934464.000)\nlocal teoma = vec3(80865536.000, 54665536.000, -934464.000)\nlocal feli = vec3(-43534464.000, 22565536.000, -48934464.000)\nlocal talemai = vec3(-13234464.000, 55765536.000, 465536.000)\nlocal sicari = vec3(52765536.000, 27165536.000, 52065536.000)\nlocal distion = math.floor(ion:getDistance(loc)/200000) ---uses getDistance functions----\nlocal distthades = string.format(\"%.2f\", math.sqrt((loc.x-thades.x)^2+(loc.y-thades.y)^2+(loc.z-thades.z)^2)/200000)\nlocal distalioth = math.floor(alioth:getDistance(loc)/200000) ---uses getDistance functions----\nlocal distmadis = string.format(\"%.2f\", math.sqrt((loc.x-madis.x)^2+(loc.y-madis.y)^2+(loc.z-madis.z)^2)/200000)\nlocal distjago = string.format(\"%.2f\", math.sqrt((loc.x-jago.x)^2+(loc.y-jago.y)^2+(loc.z-jago.z)^2)/200000)\nlocal distlacobus = string.format(\"%.2f\", math.sqrt((loc.x-lacobus.x)^2+(loc.y-lacobus.y)^2+(loc.z-lacobus.z)^2)/200000)\nlocal distteoma = string.format(\"%.2f\", math.sqrt((loc.x-teoma.x)^2+(loc.y-teoma.y)^2+(loc.z-teoma.z)^2)/200000)\nlocal distsymeon = string.format(\"%.2f\", math.sqrt((loc.x-symeon.x)^2+(loc.y-symeon.y)^2+(loc.z-symeon.z)^2)/200000)\nlocal distfeli = string.format(\"%.2f\", math.sqrt((loc.x-feli.x)^2+(loc.y-feli.y)^2+(loc.z-feli.z)^2)/200000)\nlocal distsinnen = string.format(\"%.2f\", math.sqrt((loc.x-sinnen.x)^2+(loc.y-sinnen.y)^2+(loc.z-sinnen.z)^2)/200000)\nlocal disttalemai = string.format(\"%.2f\", math.sqrt((loc.x-talemai.x)^2+(loc.y-talemai.y)^2+(loc.z-talemai.z)^2)/200000)\nlocal distsicari = string.format(\"%.2f\", math.sqrt((loc.x-sicari.x)^2+(loc.y-sicari.y)^2+(loc.z-sicari.z)^2)/200000)\nselection = 0\n\n\n for i = 1,1 do\n local button = {id = (\"b\"..1), enabled=true, td=\"<td>\", top=2/100, bottom=13/100, left=1/100, right=28/100}\n table.insert(MapScreenButtons, button)\nend\n for i = 2,2 do\n local button = {id = (\"b\"..2), enabled=true, td=\"<td>\", top=15/100, bottom=26/100, left=1/100, right=30/100}\n table.insert(MapScreenButtons, button)\nend\n for i = 3,3 do\n local button = {id = (\"b\"..3), enabled=true, td=\"<td>\", top=27/100, bottom=38/100, left=1/100, right=28/100}\n table.insert(MapScreenButtons, button)\nend\n for i = 4,4 do\n local button = {id = (\"b\"..4), enabled=true, td=\"<td>\", top=39/100, bottom=50/100, left=1/100, right=28/100}\n table.insert(MapScreenButtons, button)\nend\n for i = 5,5 do\n local button = {id = (\"b\"..5), enabled=true, td=\"<td>\", top=51/100, bottom=62/100, left=1/100, right=28/100}\n table.insert(MapScreenButtons, button)\nend\n for i = 6,6 do\n local button = {id = (\"b\"..6), enabled=true, td=\"<td>\", top=64/100, bottom=75/100, left=1/100, right=28/100}\n table.insert(MapScreenButtons, button)\nend\n for i = 7,7 do\n local button = {id = (\"b\"..7), enabled=true, td=\"<td>\", top=2/100, bottom=13/100, left=75/100, right=100/100}\n table.insert(MapScreenButtons, button)\nend\n for i = 8,8 do\n local button = {id = (\"b\"..8), enabled=true, td=\"<td>\", top=15/100, bottom=26/100, left=75/100, right=100/100}\n table.insert(MapScreenButtons, button)\nend\n for i = 9,9 do\n local button = {id = (\"b\"..9), enabled=true, td=\"<td>\", top=27/100, bottom=38/100, left=75/100, right=100/100}\n table.insert(MapScreenButtons, button)\nend\n for i = 10,10 do\n local button = {id = (\"b\"..10), enabled=true, td=\"<td>\", top=39/100, bottom=50/100, left=75/100, right=100/100}\n table.insert(MapScreenButtons, button)\nend\n for i = 11,11 do\n local button = {id = (\"b\"..11), enabled=true, td=\"<td>\", top=51/100, bottom=62/100, left=75/100, right=100/100}\n table.insert(MapScreenButtons, button)\nend\n for i = 12,12 do\n local button = {id = (\"b\"..12), enabled=true, td=\"<td>\", top=64/100, bottom=75/100, left=75/100, right=100/100}\n table.insert(MapScreenButtons, button)\nend\n for i = 13,13 do\n local button = {id = (\"b\"..13), enabled=true, td=\"<td>\", top=90/100, bottom=100/100, left=1/100, right=18/100}\n table.insert(MapScreenButtons, button)\nend\nfunction evaluateButtons()\n local selected = 0\n\n if #MapScreenButtons >= 1 then\n -- Set button styles\n for i, button in ipairs(MapScreenButtons) do\n if button.left < MapScreenMouseX and MapScreenMouseX < button.right and button.top < MapScreenMouseY and MapScreenMouseY < button.bottom then\n if MapScreenMouseDown and MapScreenButtonSelected == i then\n end\n selected = i\n end\n if not button.enabled then\n end\n\n end\n end\n return selected\nend\n\nfunction onButtonDown(buttonNo)\n local button = MapScreenButtons[buttonNo]\n if not button or not button.enabled then\n\treturn\n end\nend\nfunction onButtonUp(buttonNo)\n local button = MapScreenButtons[buttonNo]\n if not button or not button.enabled then\n return\n end\nfunction onClick(buttonNo)\n local button = MapScreenButtons[buttonNo]\n if not button or not button.enabled then\n return\n end\nend\n\n\n\n if buttonNo == 1 then\ndestX = 0\ndestY = 0\nselection = 1\nsudistance = distalioth\n elseif buttonNo == 2 then\ndestX = 43\ndestY = -56\nselection = 2\nsudistance = distmadis\n elseif buttonNo == 3 then\ndestX = 73\ndestY = -27\nselection = 3\nsudistance = distthades\n elseif buttonNo == 4 then\ndestX = -33\ndestY = -139\nselection = 4\nsudistance = disttalemai\n elseif buttonNo == 5 then\ndestX = -109\ndestY = -56\nselection = 5\nsudistance = distfeli\n elseif buttonNo == 6 then\ndestX = 131\ndestY = -68\nselection = 6\nsudistance = distsicari\n elseif buttonNo == 7 then\ndestX = 35\ndestY = 214\nselection = 7\nsudistance = distsymeon\n elseif buttonNo == 8 then\ndestX = 146\ndestY = -74\nselection = 8\nsudistance = distsinnen\n elseif buttonNo == 9 then\ndestX = -235\ndestY = -32\nselection = 9\nsudistance = distjago\n elseif buttonNo == 10 then\ndestX = 202\ndestY = -137\nselection = 10\nsudistance = distteoma\n elseif buttonNo == 11 then\ndestX = 7\ndestY = 247\nselection = 11\nsudistance = distion\n elseif buttonNo == 12 then\ndestX = 247\ndestY = 34\nselection = 12\nsudistance = distlacobus\n elseif buttonNo == 13 then\n unit.exit()\n end\nend\n\n\nlocal floor = math.floor\nlocal concat = table.concat\n\nlocal secondsInMinute = 60\nlocal secondsInHour = secondsInMinute * 60\nlocal secondsInDay = secondsInHour * 24\nlocal secondsInYear = 365.2419 * secondsInDay\n\nlocal minTotalSecondsToShowOnlyYears = secondsInYear * 10\n\n---@param totalSeconds number\n---@param maxComponents nil|number\nlocal function formatTimeWithUnits (totalSeconds, maxComponents)\n maxComponents = maxComponents or 2\n\n local buffer = {}\n\n if totalSeconds < 0 then\n buffer[#buffer + 1] = \"-\"\n totalSeconds = -totalSeconds\n maxComponents = maxComponents + 1\n end\n\n local showOnlyYears = totalSeconds > minTotalSecondsToShowOnlyYears\n\n local years = floor(totalSeconds / secondsInYear)\n if years > 0 then buffer[#buffer + 1] = years .. \"y\" end\n\n if #buffer < maxComponents and not showOnlyYears then\n local days = floor(totalSeconds % secondsInYear / secondsInDay)\n if days > 0 then buffer[#buffer + 1] = days .. \"d\" end\n end\n\n if #buffer < maxComponents and not showOnlyYears then\n local hours = floor(totalSeconds % secondsInDay / secondsInHour)\n if hours > 0 then buffer[#buffer + 1] = hours .. \"h\" end\n end\n\n if #buffer < maxComponents and not showOnlyYears then\n local minutes = floor(totalSeconds % secondsInHour / secondsInMinute)\n if minutes > 0 then buffer[#buffer + 1] = minutes .. \"m\" end\n end\n\n if #buffer < maxComponents and not showOnlyYears then\n local seconds = floor(totalSeconds % secondsInMinute)\n if seconds > 0 then buffer[#buffer + 1] = seconds .. \"s\" end\n end\n\n if #buffer == 0 then return \"0s\" end\n\n return concat(buffer, \" \")\n\nend\n\n\n\n\nfunction updateScreen()\n\nloc = vec3(core.getConstructWorldPos())\n\nlocal shipVelocity = vec3(core.getVelocity()):len() * 3.6\nlocal shipAcceleration = vec3(core.getVelocity()):len() * 3.6\nlocal time_to_distance = 0\nlocal display_selection = \"\"\n\nif selection == 1 then\n alioth = galaxyReference[0][2] ---uses Atlas functions\n distalioth = math.floor(alioth:getDistance(loc)/200000) ---uses getDistance functions----\n time_to_distance = distalioth * 200 / shipVelocity\n display_selection = \"alioth\"\nelseif selection == 2 then\n madis = vec3(17465536.000, 22665536.000, -34464.000)\n distmadis = string.format(\"%.2f\", math.sqrt((loc.x-madis.x)^2+(loc.y-madis.y)^2+(loc.z-madis.z)^2)/200000)\n time_to_distance = distmadis * 200 / shipVelocity\n display_selection = \"madis\"\nelseif selection == 3 then\n thades = vec3(29165536.000, 10865536.000, 65536.000)\n distthades = string.format(\"%.2f\", math.sqrt((loc.x-thades.x)^2+(loc.y-thades.y)^2+(loc.z-thades.z)^2)/200000)\n time_to_distance = distthades * 200 / shipVelocity\n display_selection = \"thades\"\nelseif selection == 4 then\n talemai = vec3(-13234464.000, 55765536.000, 465536.000)\n disttalemai = string.format(\"%.2f\", math.sqrt((loc.x-talemai.x)^2+(loc.y-talemai.y)^2+(loc.z-talemai.z)^2)/200000)\n time_to_distance = disttalemai * 200 / shipVelocity\n display_selection = \"talemai\"\nelseif selection == 5 then\n feli = vec3(-43534464.000, 22565536.000, -48934464.000)\n distfeli = string.format(\"%.2f\", math.sqrt((loc.x-feli.x)^2+(loc.y-feli.y)^2+(loc.z-feli.z)^2)/200000)\n time_to_distance = distfeli * 200 / shipVelocity\n display_selection = \"feli\"\nelseif selection == 6 then\n sicari = vec3(52765536.000, 27165536.000, 52065536.000)\n distsicari = string.format(\"%.2f\", math.sqrt((loc.x-sicari.x)^2+(loc.y-sicari.y)^2+(loc.z-sicari.z)^2)/200000)\n time_to_distance = distsicari * 200 / shipVelocity\n display_selection = \"sicari\"\nelseif selection == 7 then\n symeon = vec3(14165536.000, -85634464.000, -934464.000)\n distsymeon = string.format(\"%.2f\", math.sqrt((loc.x-symeon.x)^2+(loc.y-symeon.y)^2+(loc.z-symeon.z)^2)/200000)\n time_to_distance = distsymeon * 200 / shipVelocity\n display_selection = \"symeon\"\nelseif selection == 8 then\n sinnen = vec3(58665536.000, 29665536.000, 58165536.000)\n distsinnen = string.format(\"%.2f\", math.sqrt((loc.x-sinnen.x)^2+(loc.y-sinnen.y)^2+(loc.z-sinnen.z)^2)/200000)\n time_to_distance = distsinnen * 200 / shipVelocity\n display_selection = \"sinnen\"\nelseif selection == 9 then\n jago = vec3(-94134464.000, 12765536.000, -3634464.000)\n distjago = string.format(\"%.2f\", math.sqrt((loc.x-jago.x)^2+(loc.y-jago.y)^2+(loc.z-jago.z)^2)/200000)\n time_to_distance = distjago * 200 / shipVelocity\n display_selection = \"jago\"\nelseif selection == 10 then\n teoma = vec3(80865536.000, 54665536.000, -934464.000)\n distteoma = string.format(\"%.2f\", math.sqrt((loc.x-teoma.x)^2+(loc.y-teoma.y)^2+(loc.z-teoma.z)^2)/200000)\n time_to_distance = distteoma * 200 / shipVelocity\n display_selection = \"teoma\"\nelseif selection == 11 then\n ion = galaxyReference[0][120] ---uses Atlas functions\n distion = math.floor(ion:getDistance(loc)/200000) ---uses getDistance functions----\n time_to_distance = distion * 200 / shipVelocity\n display_selection = \"ion\"\nelseif selection == 12 then\n lacobus = vec3(98865536.000, -13534464.000, -934464.000)\n distlacobus = string.format(\"%.2f\", math.sqrt((loc.x-lacobus.x)^2+(loc.y-lacobus.y)^2+(loc.z-lacobus.z)^2)/200000)\n time_to_distance = distlacobus * 200 / shipVelocity\n display_selection = \"lacobus\"\nelse\n time_to_distance = sudistance * 200 / shipVelocity\n display_selection = \"none\"\nend\nshipVelocity = string.format(\"%.2f\", shipVelocity)\nwarpmath = math.floor(math.floor(core.getConstructMass()/ 1000) * sudistance * 0.00025)\n\ntime_to_distance_min = time_to_distance * 60\ntime_to_distance_min_sec = time_to_distance_min * 60\n\ntime_to_distance = formatTimeWithUnits (time_to_distance_min_sec, 3)\n\nhtml= ([[\n<svg class=\"bootstrap\" viewBox=\"0 0 1024 1024\" style=\"width:100%; height:100%\">\n\n<circle cx=\"500\" cy=\"500\" r=\"400\" stroke=\"#FFF\" stroke-width=\"3\" transform=\"\"></circle>\n<circle cx=\"500\" cy=\"500\" r=\"350\" stroke=\"#FFF\" stroke-width=\"3\" transform=\"\" stroke-opacity=\"0.2\"></circle>\n<circle cx=\"500\" cy=\"500\" r=\"300\" stroke=\"#FFF\" stroke-width=\"3\" transform=\"\"></circle><circle cx=\"500\" cy=\"500\" r=\"250\" stroke=\"#FFF\" stroke-width=\"3\" transform=\"\" stroke-opacity=\"0.2\"></circle>\n<circle cx=\"500\" cy=\"500\" r=\"200\" stroke=\"#FFF\" stroke-width=\"3\" transform=\"\"></circle><circle cx=\"500\" cy=\"500\" r=\"150\" stroke=\"#FFF\" stroke-width=\"3\" transform=\"\" stroke-opacity=\"0.2\"></circle>\n<circle cx=\"500\" cy=\"500\" r=\"100\" stroke=\"lightblue\" stroke-width=\"3\" transform=\"\"></circle><circle cx=\"500\" cy=\"500\" r=\"50\" stroke=\"lightblue\" stroke-width=\"3\" transform=\"\" stroke-opacity=\"0.2\"></circle>\n<circle cx=\"500\" cy=\"500\" r=\"20\" stroke=\"Orange\" stroke-width=\"2\" transform=\"\"></circle><text x=\"510\" y=\"510\" fill=\"Yellow\">Helios</text>\n<circle cx=\"-0.00\" cy=\"0\" r=\"10\" stroke=\"black\" stroke-width=\"1\" fill=\"blue\" transform=\"translate(500,500)\"></circle>\n<text x=\"-0.00\" y=\"0\" transform=\"translate(500,480)\" fill=\"white\" font-size=\"20\">Alioth</text><circle cx=\"7.16\" cy=\"247.59\" r=\"10\" stroke=\"black\" stroke-width=\"1\" fill=\"blue\" transform=\"translate(500,500)\"></circle>\n<text x=\"7.16\" y=\"247.59\" transform=\"translate(480,480)\" fill=\"white\" font-size=\"20\">Ion</text><circle cx=\"35.41\" cy=\"214.09\" r=\"10\" stroke=\"black\" stroke-width=\"1\" fill=\"blue\" transform=\"translate(500,500)\"></circle>\n<text x=\"35.41\" y=\"214.09\" transform=\"translate(500,480)\" fill=\"white\" font-size=\"20\">Symeon</text><circle cx=\"-33.09\" cy=\"-139.41\" r=\"10\" stroke=\"black\" stroke-width=\"1\" fill=\"blue\" transform=\"translate(500,500)\"></circle>\n<text x=\"-33.09\" y=\"-139.41\" transform=\"translate(500,480)\" fill=\"white\" font-size=\"20\">Talemai</text><circle cx=\"202.16\" cy=\"-136.66\" r=\"10\" stroke=\"black\" stroke-width=\"1\" fill=\"blue\" transform=\"translate(500,500)\"></circle>\n<text x=\"202.16\" y=\"-136.66\" transform=\"translate(500,480)\" fill=\"white\" font-size=\"20\">Teoma</text><circle cx=\"247.16\" cy=\"33.84\" r=\"10\" stroke=\"black\" stroke-width=\"1\" fill=\"blue\" transform=\"translate(500,500)\"></circle>\n<text x=\"247.16\" y=\"33.84\" transform=\"translate(500,480)\" fill=\"white\" font-size=\"20\">Lacobus</text><circle cx=\"-108.84\" cy=\"-56.41\" r=\"10\" stroke=\"black\" stroke-width=\"1\" fill=\"blue\" transform=\"translate(500,500)\"></circle>\n<text x=\"-108.84\" y=\"-56.41\" transform=\"translate(500,480)\" fill=\"white\" font-size=\"20\">Feli</text><circle cx=\"72.91\" cy=\"-27.16\" r=\"10\" stroke=\"black\" stroke-width=\"1\" fill=\"blue\" transform=\"translate(500,500)\"></circle>\n<text x=\"72.91\" y=\"-27.16\" transform=\"translate(500,485)\" fill=\"white\" font-size=\"20\">Thades</text><circle cx=\"43.66\" cy=\"-56.66\" r=\"10\" stroke=\"black\" stroke-width=\"1\" fill=\"blue\" transform=\"translate(500,500)\"></circle>\n<text x=\"43.66\" y=\"-56.66\" transform=\"translate(500,480)\" fill=\"white\" font-size=\"20\">Madis</text><circle cx=\"-235.34\" cy=\"-31.91\" r=\"10\" stroke=\"black\" stroke-width=\"1\" fill=\"blue\" transform=\"translate(500,500)\"></circle>\n<text x=\"-235.34\" y=\"-31.91\" transform=\"translate(500,480)\" fill=\"white\" font-size=\"20\">Jago</text><circle cx=\"131.91\" cy=\"-67.91\" r=\"10\" stroke=\"black\" stroke-width=\"1\" fill=\"blue\" transform=\"translate(500,500)\"></circle>\n<text x=\"131.91\" y=\"-67.91\" transform=\"translate(475,480)\" fill=\"white\" font-size=\"20\">Sicari</text><circle cx=\"146.66\" cy=\"-74.16\" r=\"10\" stroke=\"black\" stroke-width=\"1\" fill=\"blue\" transform=\"translate(500,500)\"></circle>\n<text x=\"146.66\" y=\"-74.16\" transform=\"translate(515,480)\" fill=\"white\" font-size=\"20\">Sinnen</text>\n<line stroke-linecap=\"undefined\" stroke-linejoin=\"undefined\" id=\"svg_1\" y2=\"]]..destY..[[\" x2=\"]]..destX..[[\" y1=\"]]..locY..[[\" x1=\"]]..locX..[[\" transform=\"translate(500,500)\" stroke-width=\"5\" stroke=\"#ff0000\" fill=\"none\"/>\n<circle cx=\"]]..locX..[[\" cy=\"]]..locY..[[\" r=\"3\" stroke=\"black\" stroke-width=\"1\" fill=\"limegreen\" transform=\"translate(500,500)\"></circle>\n<text x=\"]]..locX..[[\" y=\"]]..locY..[[\" transform=\"translate(500,500)\"\nfill=\"limegreen\" font-size= \"4.5vh\" font-weight= \"bold\">//SHIP POSITION</text>\n</svg>\n<svg class=\"bootstrap\" viewBox=\"0 0 1024 612\" style=\"width:100%; height:100%\">\n\n <g>\n <title>Layer 1</title>\n <g id=\"svg_24\">\n <text stroke=\"null\" transform=\"matrix(0.7907331239400577,0,0,0.7600725676692406,3.135703637258853,5.731969683147472) \" xml:space=\"preserve\" text-anchor=\"start\" font-family=\"Helvetica, Arial, sans-serif\" font-size=\"20\" id=\"svg_8\" y=\"70\" x=\"55\" stroke-width=\"0\" fill=\"Yellow\">Alioth :]]..distalioth..[[ SU</text>\n <text stroke=\"null\" transform=\"matrix(0.7907331239400577,0,0,0.7600725676692406,3.135703637258853,5.731969683147472) \" xml:space=\"preserve\" text-anchor=\"start\" font-family=\"Helvetica, Arial, sans-serif\" font-size=\"20\" id=\"svg_14\" y=\"170\" x=\"55\" stroke-width=\"0\" fill=\"Yellow\">Madis :]]..distmadis..[[ SU</text>\n <text stroke=\"null\" transform=\"matrix(0.7907331239400577,0,0,0.7600725676692406,3.135703637258853,5.731969683147472) \" xml:space=\"preserve\" text-anchor=\"start\" font-family=\"Helvetica, Arial, sans-serif\" font-size=\"20\" id=\"svg_17\" y=\"270\" x=\"55\" stroke-width=\"0\" fill=\"Yellow\">Thades :]]..distthades..[[ SU</text>\n <text stroke=\"null\" transform=\"matrix(0.7907331239400577,0,0,0.7600725676692406,3.135703637258853,5.731969683147472) \" xml:space=\"preserve\" text-anchor=\"start\" font-family=\"Helvetica, Arial, sans-serif\" font-size=\"20\" id=\"svg_20\" y=\"370\" x=\"55\" stroke-width=\"0\" fill=\"Yellow\">Talemai :]]..disttalemai..[[ SU</text>\n <text stroke=\"null\" transform=\"matrix(0.7907331239400577,0,0,0.7600725676692406,3.135703637258853,5.731969683147472) \" xml:space=\"preserve\" text-anchor=\"start\" font-family=\"Helvetica, Arial, sans-serif\" font-size=\"20\" id=\"svg_23\" y=\"470\" x=\"55\" stroke-width=\"0\" fill=\"Yellow\">Feli :]]..distfeli..[[ SU</text>\n <text stroke=\"null\" transform=\"matrix(0.7907331239400577,0,0,0.7600725676692406,3.135703637258853,5.731969683147472) \" xml:space=\"preserve\" text-anchor=\"start\" font-family=\"Helvetica, Arial, sans-serif\" font-size=\"20\" id=\"svg_26\" y=\"570\" x=\"55\" stroke-width=\"0\" fill=\"Yellow\">Sicari :]]..distsicari..[[ SU</text>\n <g id=\"svg_12\">\n <rect rx=\"10\" id=\"svg_1\" height=\"50\" width=\"250\" y=\"30\" x=\"15\" stroke-width=\"10\" stroke=\"#FFF\" fill=\"none\"/>\n <rect rx=\"10\" id=\"svg_3\" height=\"50\" width=\"250\" y=\"105\" x=\"15\" stroke-width=\"10\" stroke=\"#FFF\" fill=\"none\"/>\n <rect rx=\"10\" id=\"svg_7\" height=\"50\" width=\"250\" y=\"180\" x=\"15\" stroke-width=\"10\" stroke=\"#FFF\" fill=\"none\"/>\n <rect rx=\"10\" id=\"svg_9\" height=\"50\" width=\"250\" y=\"255\" x=\"15\" stroke-width=\"10\" stroke=\"#FFF\" fill=\"none\"/>\n <rect rx=\"10\" id=\"svg_10\" height=\"50\" width=\"250\" y=\"330\" x=\"15\" stroke-width=\"10\" stroke=\"#FFF\" fill=\"none\"/>\n <rect rx=\"10\" id=\"svg_11\" height=\"50\" width=\"250\" y=\"405\" x=\"15\" stroke-width=\"10\" stroke=\"#FFF\" fill=\"none\"/>\n </g>\n </g>\n <g id=\"svg_40\">\n <text stroke=\"null\" transform=\"matrix(0.7907331239400577,0,0,0.7600725676692406,3.135703637258853,5.731969683147472) \" xml:space=\"preserve\" text-anchor=\"start\" font-family=\"Helvetica, Arial, sans-serif\" font-size=\"20\" id=\"svg_25\" y=\"70\" x=\"997.163642\" stroke-width=\"0\" fill=\"Yellow\">Symeon :]]..distsymeon..[[ SU</text>\n <text stroke=\"null\" transform=\"matrix(0.7907331239400577,0,0,0.7600725676692406,3.135703637258853,5.731969683147472) \" xml:space=\"preserve\" text-anchor=\"start\" font-family=\"Helvetica, Arial, sans-serif\" font-size=\"20\" id=\"svg_27\" y=\"170\" x=\"997.163642\" stroke-width=\"0\" fill=\"Yellow\">Sinnen :]]..distsinnen..[[ SU</text>\n <text stroke=\"null\" transform=\"matrix(0.7907331239400577,0,0,0.7600725676692406,3.135703637258853,5.731969683147472) \" xml:space=\"preserve\" text-anchor=\"start\" font-family=\"Helvetica, Arial, sans-serif\" font-size=\"20\" id=\"svg_28\" y=\"270\" x=\"997.163642\" stroke-width=\"0\" fill=\"Yellow\">Jago :]]..distjago..[[ SU</text>\n <text stroke=\"null\" transform=\"matrix(0.7907331239400577,0,0,0.7600725676692406,3.135703637258853,5.731969683147472) \" xml:space=\"preserve\" text-anchor=\"start\" font-family=\"Helvetica, Arial, sans-serif\" font-size=\"20\" id=\"svg_30\" y=\"370\" x=\"997.163642\" stroke-width=\"0\" fill=\"Yellow\">Teoma :]]..distteoma..[[ SU</text>\n <text stroke=\"null\" transform=\"matrix(0.7907331239400577,0,0,0.7600725676692406,3.135703637258853,5.731969683147472) \" xml:space=\"preserve\" text-anchor=\"start\" font-family=\"Helvetica, Arial, sans-serif\" font-size=\"20\" id=\"svg_31\" y=\"470\" x=\"997.163642\" stroke-width=\"0\" fill=\"Yellow\">Ion :]]..distion..[[ SU</text>\n <text stroke=\"null\" transform=\"matrix(0.7907331239400577,0,0,0.7600725676692406,3.135703637258853,5.731969683147472) \" xml:space=\"preserve\" text-anchor=\"start\" font-family=\"Helvetica, Arial, sans-serif\" font-size=\"20\" id=\"svg_32\" y=\"570\" x=\"997.163642\" stroke-width=\"0\" fill=\"Yellow\">Lacobus :]]..distlacobus..[[ SU</text>\n <g id=\"svg_39\">\n <rect rx=\"10\" id=\"svg_33\" height=\"50\" width=\"250\" y=\"30\" x=\"760\" stroke-width=\"10\" stroke=\"#FFF\" fill=\"none\"/>\n <rect rx=\"10\" id=\"svg_34\" height=\"50\" width=\"250\" y=\"105\" x=\"760\" stroke-width=\"10\" stroke=\"#FFF\" fill=\"none\"/>\n <rect rx=\"10\" id=\"svg_35\" height=\"50\" width=\"250\" y=\"180\" x=\"760\" stroke-width=\"10\" stroke=\"#FFF\" fill=\"none\"/>\n <rect rx=\"10\" id=\"svg_36\" height=\"50\" width=\"250\" y=\"255\" x=\"760\" stroke-width=\"10\" stroke=\"#FFF\" fill=\"none\"/>\n <rect rx=\"10\" id=\"svg_37\" height=\"50\" width=\"250\" y=\"330\" x=\"760\" stroke-width=\"10\" stroke=\"#FFF\" fill=\"none\"/>\n <rect rx=\"10\" id=\"svg_38\" height=\"50\" width=\"250\" y=\"405\" x=\"760\" stroke-width=\"10\" stroke=\"#FFF\" fill=\"none\"/>\n </g>\n </g>\n </g>\n <text stroke=\"null\" transform=\"matrix(0.7907331239400577,0,0,0.7600725676692406,3.135703637258853,5.731969683147472) \" xml:space=\"preserve\" text-anchor=\"start\" font-family=\"Helvetica, Arial, sans-serif\" font-size=\"30\" id=\"svg_32\" y=\"700\" x=\"20\" stroke-width=\"0\" fill=\"LightBlue\">Est. Warp Cost: ]]..warpmath..[[</text>\n <text stroke=\"null\" transform=\"matrix(0.7907331239400577,0,0,0.7600725676692406,3.135703637258853,5.731969683147472) \" xml:space=\"preserve\" text-anchor=\"start\" font-family=\"Helvetica, Arial, sans-serif\" font-size=\"30\" id=\"svg_32\" y=\"750\" x=\"20\" stroke-width=\"0\" fill=\"LightBlue\">Construct Weight: ]]..math.floor(core.getConstructMass()/ 1000)..[[ tons</text>\n <text stroke=\"null\" transform=\"matrix(0.7907331239400577,0,0,0.7600725676692406,3.135703637258853,5.731969683147472) \" xml:space=\"preserve\" text-anchor=\"start\" font-family=\"Helvetica, Arial, sans-serif\" font-size=\"30\" id=\"svg_32\" y=\"700\" x=\"500\" stroke-width=\"0\" fill=\"LightBlue\">TTD: ]]..time_to_distance..[[ </text>\n <text stroke=\"null\" transform=\"matrix(0.7907331239400577,0,0,0.7600725676692406,3.135703637258853,5.731969683147472) \" xml:space=\"preserve\" text-anchor=\"start\" font-family=\"Helvetica, Arial, sans-serif\" font-size=\"30\" id=\"svg_32\" y=\"750\" x=\"500\" stroke-width=\"0\" fill=\"LightBlue\">VELOC: ]]..shipVelocity..[[ km/h</text>\n <text stroke=\"null\" transform=\"matrix(0.7907331239400577,0,0,0.7600725676692406,3.135703637258853,5.731969683147472) \" xml:space=\"preserve\" text-anchor=\"start\" font-family=\"Helvetica, Arial, sans-serif\" font-size=\"30\" id=\"svg_32\" y=\"700\" x=\"900\" stroke-width=\"0\" fill=\"LightBlue\">DEST: ]]..display_selection..[[ </text>\n\n</svg>\n\n ]])\n\nscreen.setHTML(html)\n--screen2.setHTML(html)\nend\nunit.setTimer(\"spacemap\",.08)\n\n","filter":{"args":[],"signature":"start()","slotKey":"-1"},"key":"2"},{"code":"updateScreen()","filter":{"args":[{"value":"spacemap"}],"signature":"tick(timerId)","slotKey":"-1"},"key":"3"}],"methods":[],"events":[]}



    ----Older Version In Multiple formats for Learning Purposes. DU IT
    Space Map with Warp Calculation
    Credits to All of DU Lua Community

    Requirements:
    1 Dynamic Construct
    1 Screen
    1 Program Board

    Link Screen then Core

    Two Versions are available and do the same thing.
    One uses variables and math with some built in functions from Lua to get the values.
    One uses Functions from JayleBreak's atlas and functions to get the values.
     

    *KNOWN BUGS*
    ----On fresh login, Core has to be ran one time to send Construct weight data over the server, otherwise math will be wrong.
    So sit down in pilot seat one time and then run programming board. After that its always set. ---Server Bug

    ----Currently has Bug with Screen Flickers on half of the svg during setHTML redraw update-- 9/23 Patch
     
     

    Planets are organized similar to DU ingame map and are stationary svg.
    ---------- Math and Variables Version---------
    ---Space Map ---Unit Start MapScreenButtons = {} MapScreenMouseX = 0 MapScreenMouseY = 0 MapScreenMouseDown = false MapScreenButtonSelected = 0 local worldPos = vec3(core.getConstructWorldPos()) local locX = (worldPos.x/400000) local locY = (worldPos.y/400000)*(-1) local destX = 0 local destY = 0 local sudistance = 0 local loc = vec3(core.getConstructWorldPos()) local ion = vec3(2865536.000, -99034464.000, -934464.000) local thades = vec3(29165536.000, 10865536.000, 65536.000) local sinnen = vec3(58665536.000, 29665536.000, 58165536.000) local alioth = vec3(-8.000, -8.000, -126303.000) local madis = vec3(17465536.000, 22665536.000, -34464.000) local jago = vec3(-94134464.000, 12765536.000, -3634464.000) local symeon = vec3(14165536.000, -85634464.000, -934464.000) local lacobus = vec3(98865536.000, -13534464.000, -934464.000) local teoma = vec3(80865536.000, 54665536.000, -934464.000) local feli = vec3(-43534464.000, 22565536.000, -48934464.000) local talemai = vec3(-13234464.000, 55765536.000, 465536.000) local sicari = vec3(52765536.000, 27165536.000, 52065536.000) local distion = string.format("%.2f", math.sqrt((loc.x-ion.x)^2+(loc.y-ion.y)^2+(loc.z-ion.z)^2)/200000) local distthades = string.format("%.2f", math.sqrt((loc.x-thades.x)^2+(loc.y-thades.y)^2+(loc.z-thades.z)^2)/200000) local distalioth = string.format("%.2f", math.sqrt((loc.x-alioth.x)^2+(loc.y-alioth.y)^2+(loc.z-alioth.z)^2)/200000) local distmadis = string.format("%.2f", math.sqrt((loc.x-madis.x)^2+(loc.y-madis.y)^2+(loc.z-madis.z)^2)/200000) local distjago = string.format("%.2f", math.sqrt((loc.x-jago.x)^2+(loc.y-jago.y)^2+(loc.z-jago.z)^2)/200000) local distlacobus = string.format("%.2f", math.sqrt((loc.x-lacobus.x)^2+(loc.y-lacobus.y)^2+(loc.z-lacobus.z)^2)/200000) local distteoma = string.format("%.2f", math.sqrt((loc.x-teoma.x)^2+(loc.y-teoma.y)^2+(loc.z-teoma.z)^2)/200000) local distsymeon = string.format("%.2f", math.sqrt((loc.x-symeon.x)^2+(loc.y-symeon.y)^2+(loc.z-symeon.z)^2)/200000) local distfeli = string.format("%.2f", math.sqrt((loc.x-feli.x)^2+(loc.y-feli.y)^2+(loc.z-feli.z)^2)/200000) local distsinnen = string.format("%.2f", math.sqrt((loc.x-sinnen.x)^2+(loc.y-sinnen.y)^2+(loc.z-sinnen.z)^2)/200000) local disttalemai = string.format("%.2f", math.sqrt((loc.x-talemai.x)^2+(loc.y-talemai.y)^2+(loc.z-talemai.z)^2)/200000) local distsicari = string.format("%.2f", math.sqrt((loc.x-sicari.x)^2+(loc.y-sicari.y)^2+(loc.z-sicari.z)^2)/200000) for i = 1,1 do local button = {id = ("b"..1), enabled=true, td="<td>", top=2/100, bottom=13/100, left=1/100, right=28/100} table.insert(MapScreenButtons, button) end for i = 2,2 do local button = {id = ("b"..2), enabled=true, td="<td>", top=15/100, bottom=26/100, left=1/100, right=30/100} table.insert(MapScreenButtons, button) end for i = 3,3 do local button = {id = ("b"..3), enabled=true, td="<td>", top=27/100, bottom=38/100, left=1/100, right=28/100} table.insert(MapScreenButtons, button) end for i = 4,4 do local button = {id = ("b"..4), enabled=true, td="<td>", top=39/100, bottom=50/100, left=1/100, right=28/100} table.insert(MapScreenButtons, button) end for i = 5,5 do local button = {id = ("b"..5), enabled=true, td="<td>", top=51/100, bottom=62/100, left=1/100, right=28/100} table.insert(MapScreenButtons, button) end for i = 6,6 do local button = {id = ("b"..6), enabled=true, td="<td>", top=64/100, bottom=75/100, left=1/100, right=28/100} table.insert(MapScreenButtons, button) end for i = 7,7 do local button = {id = ("b"..7), enabled=true, td="<td>", top=2/100, bottom=13/100, left=75/100, right=100/100} table.insert(MapScreenButtons, button) end for i = 8,8 do local button = {id = ("b"..8), enabled=true, td="<td>", top=15/100, bottom=26/100, left=75/100, right=100/100} table.insert(MapScreenButtons, button) end for i = 9,9 do local button = {id = ("b"..9), enabled=true, td="<td>", top=27/100, bottom=38/100, left=75/100, right=100/100} table.insert(MapScreenButtons, button) end for i = 10,10 do local button = {id = ("b"..10), enabled=true, td="<td>", top=39/100, bottom=50/100, left=75/100, right=100/100} table.insert(MapScreenButtons, button) end for i = 11,11 do local button = {id = ("b"..11), enabled=true, td="<td>", top=51/100, bottom=62/100, left=75/100, right=100/100} table.insert(MapScreenButtons, button) end for i = 12,12 do local button = {id = ("b"..12), enabled=true, td="<td>", top=64/100, bottom=75/100, left=75/100, right=100/100} table.insert(MapScreenButtons, button) end for i = 13,13 do local button = {id = ("b"..13), enabled=true, td="<td>", top=90/100, bottom=100/100, left=1/100, right=18/100} table.insert(MapScreenButtons, button) end function evaluateButtons() local selected = 0 if #MapScreenButtons >= 1 then -- Set button styles for i, button in ipairs(MapScreenButtons) do if button.left < MapScreenMouseX and MapScreenMouseX < button.right and button.top < MapScreenMouseY and MapScreenMouseY < button.bottom then if MapScreenMouseDown and MapScreenButtonSelected == i then end selected = i end if not button.enabled then end end end return selected end function onButtonDown(buttonNo) local button = MapScreenButtons[buttonNo] if not button or not button.enabled then return end end function onButtonUp(buttonNo) local button = MapScreenButtons[buttonNo] if not button or not button.enabled then return end function onClick(buttonNo) local button = MapScreenButtons[buttonNo] if not button or not button.enabled then return end end if buttonNo == 1 then destX = 0 destY = 0 sudistance = distalioth elseif buttonNo == 2 then destX = 43 destY = -56 sudistance = distmadis elseif buttonNo == 3 then destX = 73 destY = -27 sudistance = distthades elseif buttonNo == 4 then destX = -33 destY = -139 sudistance = disttalemai elseif buttonNo == 5 then destX = -109 destY = -56 sudistance = distfeli elseif buttonNo == 6 then destX = 131 destY = -68 sudistance = distsicari elseif buttonNo == 7 then destX = 35 destY = 214 sudistance = distsymeon elseif buttonNo == 8 then destX = 146 destY = -74 sudistance = distsinnen elseif buttonNo == 9 then destX = -235 destY = -32 sudistance = distjago elseif buttonNo == 10 then destX = 202 destY = -137 sudistance = distteoma elseif buttonNo == 11 then destX = 7 destY = 247 sudistance = distion elseif buttonNo == 12 then destX = 247 destY = 34 sudistance = distlacobus elseif buttonNo == 13 then unit.exit() end end function updateScreen() warpmath = math.floor(math.floor(core.getConstructMass()/ 1000) * sudistance * 0.00025) html= ([[ <svg class="bootstrap" viewBox="0 0 1024 1024" style="width:100%; height:100%"> <circle cx="500" cy="500" r="400" stroke="darkgreen" stroke-width="3" transform=""></circle><circle cx="500" cy="500" r="350" stroke="darkgreen" stroke-width="3" transform="" stroke-opacity="0.2"></circle><circle cx="500" cy="500" r="300" stroke="darkgreen" stroke-width="3" transform=""></circle><circle cx="500" cy="500" r="250" stroke="darkgreen" stroke-width="3" transform="" stroke-opacity="0.2"></circle><circle cx="500" cy="500" r="200" stroke="darkgreen" stroke-width="3" transform=""></circle><circle cx="500" cy="500" r="150" stroke="darkgreen" stroke-width="3" transform="" stroke-opacity="0.2"></circle><circle cx="500" cy="500" r="100" stroke="lightblue" stroke-width="3" transform=""></circle><circle cx="500" cy="500" r="50" stroke="lightblue" stroke-width="3" transform="" stroke-opacity="0.2"></circle><circle cx="500" cy="500" r="20" stroke="Orange" stroke-width="2" transform=""></circle><text x="510" y="510" fill="Yellow">Helios</text><circle cx="-0.00" cy="0" r="10" stroke="black" stroke-width="1" fill="blue" transform="translate(500,500)"></circle><text x="-0.00" y="0" transform="translate(500,480)" fill="white" font-size="20">Alioth</text><circle cx="7.16" cy="247.59" r="10" stroke="black" stroke-width="1" fill="blue" transform="translate(500,500)"></circle><text x="7.16" y="247.59" transform="translate(480,480)" fill="white" font-size="20">Ion</text><circle cx="35.41" cy="214.09" r="10" stroke="black" stroke-width="1" fill="blue" transform="translate(500,500)"></circle><text x="35.41" y="214.09" transform="translate(500,480)" fill="white" font-size="20">Symeon</text><circle cx="-33.09" cy="-139.41" r="10" stroke="black" stroke-width="1" fill="blue" transform="translate(500,500)"></circle><text x="-33.09" y="-139.41" transform="translate(500,480)" fill="white" font-size="20">Talemai</text><circle cx="202.16" cy="-136.66" r="10" stroke="black" stroke-width="1" fill="blue" transform="translate(500,500)"></circle><text x="202.16" y="-136.66" transform="translate(500,480)" fill="white" font-size="20">Teoma</text><circle cx="247.16" cy="33.84" r="10" stroke="black" stroke-width="1" fill="blue" transform="translate(500,500)"></circle><text x="247.16" y="33.84" transform="translate(500,480)" fill="white" font-size="20">Lacobus</text><circle cx="-108.84" cy="-56.41" r="10" stroke="black" stroke-width="1" fill="blue" transform="translate(500,500)"></circle><text x="-108.84" y="-56.41" transform="translate(500,480)" fill="white" font-size="20">Feli</text><circle cx="72.91" cy="-27.16" r="10" stroke="black" stroke-width="1" fill="blue" transform="translate(500,500)"></circle><text x="72.91" y="-27.16" transform="translate(500,485)" fill="white" font-size="20">Thades</text><circle cx="43.66" cy="-56.66" r="10" stroke="black" stroke-width="1" fill="blue" transform="translate(500,500)"></circle><text x="43.66" y="-56.66" transform="translate(500,480)" fill="white" font-size="20">Madis</text><circle cx="-235.34" cy="-31.91" r="10" stroke="black" stroke-width="1" fill="blue" transform="translate(500,500)"></circle><text x="-235.34" y="-31.91" transform="translate(500,480)" fill="white" font-size="20">Jago</text><circle cx="131.91" cy="-67.91" r="10" stroke="black" stroke-width="1" fill="blue" transform="translate(500,500)"></circle><text x="131.91" y="-67.91" transform="translate(475,480)" fill="white" font-size="20">Sicari</text><circle cx="146.66" cy="-74.16" r="10" stroke="black" stroke-width="1" fill="blue" transform="translate(500,500)"></circle><text x="146.66" y="-74.16" transform="translate(515,480)" fill="white" font-size="20">Sinnen</text> <line stroke-linecap="undefined" stroke-linejoin="undefined" id="svg_1" y2="]]..destY..[[" x2="]]..destX..[[" y1="]]..locY..[[" x1="]]..locX..[[" transform="translate(500,500)" stroke-width="5" stroke="#ff0000" fill="none"/> <circle cx="]]..locX..[[" cy="]]..locY..[[" r="3" stroke="black" stroke-width="1" fill="limegreen" transform="translate(500,500)"></circle> <text x="]]..locX..[[" y="]]..locY..[[" transform="translate(500,500)" fill="limegreen" font-size= "4.5vh" font-weight= "bold">//SHIP POSITION</text> </svg> <svg class="bootstrap" viewBox="0 0 1024 612" style="width:100%; height:100%"> <g> <title>Layer 1</title> <g id="svg_24"> <text stroke="null" transform="matrix(0.7907331239400577,0,0,0.7600725676692406,3.135703637258853,5.731969683147472) " xml:space="preserve" text-anchor="start" font-family="Helvetica, Arial, sans-serif" font-size="20" id="svg_8" y="70" x="55" stroke-width="0" fill="Yellow">Alioth :]]..distalioth..[[ SU</text> <text stroke="null" transform="matrix(0.7907331239400577,0,0,0.7600725676692406,3.135703637258853,5.731969683147472) " xml:space="preserve" text-anchor="start" font-family="Helvetica, Arial, sans-serif" font-size="20" id="svg_14" y="170" x="55" stroke-width="0" fill="Yellow">Madis :]]..distmadis..[[ SU</text> <text stroke="null" transform="matrix(0.7907331239400577,0,0,0.7600725676692406,3.135703637258853,5.731969683147472) " xml:space="preserve" text-anchor="start" font-family="Helvetica, Arial, sans-serif" font-size="20" id="svg_17" y="270" x="55" stroke-width="0" fill="Yellow">Thades :]]..distthades..[[ SU</text> <text stroke="null" transform="matrix(0.7907331239400577,0,0,0.7600725676692406,3.135703637258853,5.731969683147472) " xml:space="preserve" text-anchor="start" font-family="Helvetica, Arial, sans-serif" font-size="20" id="svg_20" y="370" x="55" stroke-width="0" fill="Yellow">Talemai :]]..disttalemai..[[ SU</text> <text stroke="null" transform="matrix(0.7907331239400577,0,0,0.7600725676692406,3.135703637258853,5.731969683147472) " xml:space="preserve" text-anchor="start" font-family="Helvetica, Arial, sans-serif" font-size="20" id="svg_23" y="470" x="55" stroke-width="0" fill="Yellow">Feli :]]..distfeli..[[ SU</text> <text stroke="null" transform="matrix(0.7907331239400577,0,0,0.7600725676692406,3.135703637258853,5.731969683147472) " xml:space="preserve" text-anchor="start" font-family="Helvetica, Arial, sans-serif" font-size="20" id="svg_26" y="570" x="55" stroke-width="0" fill="Yellow">Sicari :]]..distsicari..[[ SU</text> <g id="svg_12"> <rect rx="10" id="svg_1" height="50" width="250" y="30" x="15" stroke-width="20" stroke="#00ff00" fill="none"/> <rect rx="10" id="svg_3" height="50" width="250" y="105" x="15" stroke-width="20" stroke="#00ff00" fill="none"/> <rect rx="10" id="svg_7" height="50" width="250" y="180" x="15" stroke-width="20" stroke="#00ff00" fill="none"/> <rect rx="10" id="svg_9" height="50" width="250" y="255" x="15" stroke-width="20" stroke="#00ff00" fill="none"/> <rect rx="10" id="svg_10" height="50" width="250" y="330" x="15" stroke-width="20" stroke="#00ff00" fill="none"/> <rect rx="10" id="svg_11" height="50" width="250" y="405" x="15" stroke-width="20" stroke="#00ff00" fill="none"/> </g> </g> <g id="svg_40"> <text stroke="null" transform="matrix(0.7907331239400577,0,0,0.7600725676692406,3.135703637258853,5.731969683147472) " xml:space="preserve" text-anchor="start" font-family="Helvetica, Arial, sans-serif" font-size="20" id="svg_25" y="70" x="997.163642" stroke-width="0" fill="Yellow">Symeon :]]..distsymeon..[[ SU</text> <text stroke="null" transform="matrix(0.7907331239400577,0,0,0.7600725676692406,3.135703637258853,5.731969683147472) " xml:space="preserve" text-anchor="start" font-family="Helvetica, Arial, sans-serif" font-size="20" id="svg_27" y="170" x="997.163642" stroke-width="0" fill="Yellow">Sinnen :]]..distsinnen..[[ SU</text> <text stroke="null" transform="matrix(0.7907331239400577,0,0,0.7600725676692406,3.135703637258853,5.731969683147472) " xml:space="preserve" text-anchor="start" font-family="Helvetica, Arial, sans-serif" font-size="20" id="svg_28" y="270" x="997.163642" stroke-width="0" fill="Yellow">Jago :]]..distjago..[[ SU</text> <text stroke="null" transform="matrix(0.7907331239400577,0,0,0.7600725676692406,3.135703637258853,5.731969683147472) " xml:space="preserve" text-anchor="start" font-family="Helvetica, Arial, sans-serif" font-size="20" id="svg_30" y="370" x="997.163642" stroke-width="0" fill="Yellow">Teoma :]]..distteoma..[[ SU</text> <text stroke="null" transform="matrix(0.7907331239400577,0,0,0.7600725676692406,3.135703637258853,5.731969683147472) " xml:space="preserve" text-anchor="start" font-family="Helvetica, Arial, sans-serif" font-size="20" id="svg_31" y="470" x="997.163642" stroke-width="0" fill="Yellow">Ion :]]..distion..[[ SU</text> <text stroke="null" transform="matrix(0.7907331239400577,0,0,0.7600725676692406,3.135703637258853,5.731969683147472) " xml:space="preserve" text-anchor="start" font-family="Helvetica, Arial, sans-serif" font-size="20" id="svg_32" y="570" x="997.163642" stroke-width="0" fill="Yellow">Lacobus :]]..distlacobus..[[ SU</text> <g id="svg_39"> <rect rx="10" id="svg_33" height="50" width="250" y="30" x="760" stroke-width="20" stroke="#00ff00" fill="none"/> <rect rx="10" id="svg_34" height="50" width="250" y="105" x="760" stroke-width="20" stroke="#00ff00" fill="none"/> <rect rx="10" id="svg_35" height="50" width="250" y="180" x="760" stroke-width="20" stroke="#00ff00" fill="none"/> <rect rx="10" id="svg_36" height="50" width="250" y="255" x="760" stroke-width="20" stroke="#00ff00" fill="none"/> <rect rx="10" id="svg_37" height="50" width="250" y="330" x="760" stroke-width="20" stroke="#00ff00" fill="none"/> <rect rx="10" id="svg_38" height="50" width="250" y="405" x="760" stroke-width="20" stroke="#00ff00" fill="none"/> </g> </g> </g> <text stroke="null" transform="matrix(0.7907331239400577,0,0,0.7600725676692406,3.135703637258853,5.731969683147472) " xml:space="preserve" text-anchor="start" font-family="Helvetica, Arial, sans-serif" font-size="30" id="svg_32" y="700" x="20" stroke-width="0" fill="LightBlue">Est. Warp Cost: ]]..warpmath..[[</text> <text stroke="null" transform="matrix(0.7907331239400577,0,0,0.7600725676692406,3.135703637258853,5.731969683147472) " xml:space="preserve" text-anchor="start" font-family="Helvetica, Arial, sans-serif" font-size="30" id="svg_32" y="750" x="20" stroke-width="0" fill="LightBlue">Construct Weight: ]]..math.floor(core.getConstructMass()/ 1000)..[[ tons</text> </svg> ]]) screen.setHTML(html) end unit.setTimer("spacemap",.08) ---Unit Tick spacemap updateScreen() ---MouseDown *,* MapScreenMouseX = x MapScreenMouseY = y MapScreenMouseDown = true MapScreenButtonSelected = evaluateButtons() onButtonDown(MapScreenButtonSelected) ---MouseUp *,* MapScreenMouseX = x MapScreenMouseY = y MapScreenMouseDown = false local buttonNo = evaluateButtons() if MapScreenButtonSelected > 0 and MapScreenButtonSelected == buttonNo then onButtonUp(buttonNo) onClick(buttonNo) end MapScreenButtonSelected = -buttonNo Json Paste
     
    {"slots":{"0":{"name":"screen","type":{"events":[],"methods":[]}},"1":{"name":"core","type":{"events":[],"methods":[]}},"2":{"name":"slot3","type":{"events":[],"methods":[]}},"3":{"name":"slot4","type":{"events":[],"methods":[]}},"4":{"name":"slot5","type":{"events":[],"methods":[]}},"5":{"name":"slot6","type":{"events":[],"methods":[]}},"6":{"name":"slot7","type":{"events":[],"methods":[]}},"7":{"name":"slot8","type":{"events":[],"methods":[]}},"8":{"name":"slot9","type":{"events":[],"methods":[]}},"9":{"name":"slot10","type":{"events":[],"methods":[]}},"-1":{"name":"unit","type":{"events":[],"methods":[]}},"-2":{"name":"system","type":{"events":[],"methods":[]}},"-3":{"name":"library","type":{"events":[],"methods":[]}}},"handlers":[{"code":"MapScreenMouseX = x\nMapScreenMouseY = y\nMapScreenMouseDown = false\nlocal buttonNo = evaluateButtons()\nif MapScreenButtonSelected > 0 and MapScreenButtonSelected == buttonNo then\n onButtonUp(buttonNo)\n onClick(buttonNo)\nend\nMapScreenButtonSelected = -buttonNo","filter":{"args":[{"variable":"*"},{"variable":"*"}],"signature":"mouseUp(x,y)","slotKey":"0"},"key":"0"},{"code":"MapScreenMouseX = x\nMapScreenMouseY = y\nMapScreenMouseDown = true\nMapScreenButtonSelected = evaluateButtons()\nonButtonDown(MapScreenButtonSelected)\n","filter":{"args":[{"variable":"*"},{"variable":"*"}],"signature":"mouseDown(x,y)","slotKey":"0"},"key":"1"},{"code":"MapScreenButtons = {}\nMapScreenMouseX = 0\nMapScreenMouseY = 0\nMapScreenMouseDown = false\nMapScreenButtonSelected = 0\n\n\n\nlocal worldPos = vec3(core.getConstructWorldPos())\nlocal locX = (worldPos.x/400000)\nlocal locY = (worldPos.y/400000)*(-1)\nlocal destX = 0\nlocal destY = 0\nlocal sudistance = 0\nlocal loc = vec3(core.getConstructWorldPos())\nlocal ion = vec3(2865536.000, -99034464.000, -934464.000)\nlocal thades = vec3(29165536.000, 10865536.000, 65536.000)\nlocal sinnen = vec3(58665536.000, 29665536.000, 58165536.000)\nlocal alioth = vec3(-8.000, -8.000, -126303.000)\nlocal madis = vec3(17465536.000, 22665536.000, -34464.000)\nlocal jago = vec3(-94134464.000, 12765536.000, -3634464.000)\nlocal symeon = vec3(14165536.000, -85634464.000, -934464.000)\nlocal lacobus = vec3(98865536.000, -13534464.000, -934464.000)\nlocal teoma = vec3(80865536.000, 54665536.000, -934464.000)\nlocal feli = vec3(-43534464.000, 22565536.000, -48934464.000)\nlocal talemai = vec3(-13234464.000, 55765536.000, 465536.000)\nlocal sicari = vec3(52765536.000, 27165536.000, 52065536.000)\nlocal distion = string.format(\"%.2f\", math.sqrt((loc.x-ion.x)^2+(loc.y-ion.y)^2+(loc.z-ion.z)^2)/200000)\nlocal distthades = string.format(\"%.2f\", math.sqrt((loc.x-thades.x)^2+(loc.y-thades.y)^2+(loc.z-thades.z)^2)/200000)\nlocal distalioth = string.format(\"%.2f\", math.sqrt((loc.x-alioth.x)^2+(loc.y-alioth.y)^2+(loc.z-alioth.z)^2)/200000)\nlocal distmadis = string.format(\"%.2f\", math.sqrt((loc.x-madis.x)^2+(loc.y-madis.y)^2+(loc.z-madis.z)^2)/200000)\nlocal distjago = string.format(\"%.2f\", math.sqrt((loc.x-jago.x)^2+(loc.y-jago.y)^2+(loc.z-jago.z)^2)/200000)\nlocal distlacobus = string.format(\"%.2f\", math.sqrt((loc.x-lacobus.x)^2+(loc.y-lacobus.y)^2+(loc.z-lacobus.z)^2)/200000)\nlocal distteoma = string.format(\"%.2f\", math.sqrt((loc.x-teoma.x)^2+(loc.y-teoma.y)^2+(loc.z-teoma.z)^2)/200000)\nlocal distsymeon = string.format(\"%.2f\", math.sqrt((loc.x-symeon.x)^2+(loc.y-symeon.y)^2+(loc.z-symeon.z)^2)/200000)\nlocal distfeli = string.format(\"%.2f\", math.sqrt((loc.x-feli.x)^2+(loc.y-feli.y)^2+(loc.z-feli.z)^2)/200000)\nlocal distsinnen = string.format(\"%.2f\", math.sqrt((loc.x-sinnen.x)^2+(loc.y-sinnen.y)^2+(loc.z-sinnen.z)^2)/200000)\nlocal disttalemai = string.format(\"%.2f\", math.sqrt((loc.x-talemai.x)^2+(loc.y-talemai.y)^2+(loc.z-talemai.z)^2)/200000)\nlocal distsicari = string.format(\"%.2f\", math.sqrt((loc.x-sicari.x)^2+(loc.y-sicari.y)^2+(loc.z-sicari.z)^2)/200000)\n\n\n\n for i = 1,1 do\n local button = {id = (\"b\"..1), enabled=true, td=\"<td>\", top=2/100, bottom=13/100, left=1/100, right=28/100}\n table.insert(MapScreenButtons, button)\nend\n for i = 2,2 do\n local button = {id = (\"b\"..2), enabled=true, td=\"<td>\", top=15/100, bottom=26/100, left=1/100, right=30/100}\n table.insert(MapScreenButtons, button)\nend\n for i = 3,3 do\n local button = {id = (\"b\"..3), enabled=true, td=\"<td>\", top=27/100, bottom=38/100, left=1/100, right=28/100}\n table.insert(MapScreenButtons, button)\nend\n for i = 4,4 do \n local button = {id = (\"b\"..4), enabled=true, td=\"<td>\", top=39/100, bottom=50/100, left=1/100, right=28/100}\n table.insert(MapScreenButtons, button)\nend\n for i = 5,5 do \n local button = {id = (\"b\"..5), enabled=true, td=\"<td>\", top=51/100, bottom=62/100, left=1/100, right=28/100}\n table.insert(MapScreenButtons, button)\nend\n for i = 6,6 do \n local button = {id = (\"b\"..6), enabled=true, td=\"<td>\", top=64/100, bottom=75/100, left=1/100, right=28/100}\n table.insert(MapScreenButtons, button)\nend\n for i = 7,7 do \n local button = {id = (\"b\"..7), enabled=true, td=\"<td>\", top=2/100, bottom=13/100, left=75/100, right=100/100}\n table.insert(MapScreenButtons, button)\nend\n for i = 8,8 do \n local button = {id = (\"b\"..8), enabled=true, td=\"<td>\", top=15/100, bottom=26/100, left=75/100, right=100/100}\n table.insert(MapScreenButtons, button)\nend\n for i = 9,9 do \n local button = {id = (\"b\"..9), enabled=true, td=\"<td>\", top=27/100, bottom=38/100, left=75/100, right=100/100}\n table.insert(MapScreenButtons, button)\nend\n for i = 10,10 do \n local button = {id = (\"b\"..10), enabled=true, td=\"<td>\", top=39/100, bottom=50/100, left=75/100, right=100/100}\n table.insert(MapScreenButtons, button)\nend\n for i = 11,11 do \n local button = {id = (\"b\"..11), enabled=true, td=\"<td>\", top=51/100, bottom=62/100, left=75/100, right=100/100}\n table.insert(MapScreenButtons, button)\nend\n for i = 12,12 do \n local button = {id = (\"b\"..12), enabled=true, td=\"<td>\", top=64/100, bottom=75/100, left=75/100, right=100/100}\n table.insert(MapScreenButtons, button)\nend\n for i = 13,13 do \n local button = {id = (\"b\"..13), enabled=true, td=\"<td>\", top=90/100, bottom=100/100, left=1/100, right=18/100}\n table.insert(MapScreenButtons, button)\nend\nfunction evaluateButtons()\n local selected = 0\n \n if #MapScreenButtons >= 1 then\n -- Set button styles\n for i, button in ipairs(MapScreenButtons) do\n if button.left < MapScreenMouseX and MapScreenMouseX < button.right and button.top < MapScreenMouseY and MapScreenMouseY < button.bottom then\n if MapScreenMouseDown and MapScreenButtonSelected == i then\n end\n selected = i\n end\n if not button.enabled then\n end\n\n end\n end\n return selected\nend\n\nfunction onButtonDown(buttonNo)\n local button = MapScreenButtons[buttonNo] \n if not button or not button.enabled then\n\treturn\n end\nend\nfunction onButtonUp(buttonNo)\n local button = MapScreenButtons[buttonNo] \n if not button or not button.enabled then\n return\n end\nfunction onClick(buttonNo)\n local button = MapScreenButtons[buttonNo] \n if not button or not button.enabled then\n return\n end\nend\n if buttonNo == 1 then\ndestX = 0\ndestY = 0\nsudistance = distalioth\n elseif buttonNo == 2 then\ndestX = 43\ndestY = -56\nsudistance = distmadis\n elseif buttonNo == 3 then\ndestX = 73\ndestY = -27\nsudistance = distthades \n elseif buttonNo == 4 then\ndestX = -33\ndestY = -139\nsudistance = disttalemai\n elseif buttonNo == 5 then\ndestX = -109\ndestY = -56\nsudistance = distfeli \n elseif buttonNo == 6 then\ndestX = 131\ndestY = -68\nsudistance = distsicari \n elseif buttonNo == 7 then\ndestX = 35\ndestY = 214\nsudistance = distsymeon\n elseif buttonNo == 8 then\ndestX = 146\ndestY = -74\nsudistance = distsinnen \n elseif buttonNo == 9 then\ndestX = -235\ndestY = -32\nsudistance = distjago \n elseif buttonNo == 10 then\ndestX = 202\ndestY = -137\nsudistance = distteoma \n elseif buttonNo == 11 then\ndestX = 7\ndestY = 247\nsudistance = distion \n elseif buttonNo == 12 then\ndestX = 247\ndestY = 34\nsudistance = distlacobus\n elseif buttonNo == 13 then\n unit.exit()\n end\nend\n\nfunction updateScreen() \nwarpmath = math.floor(math.floor(core.getConstructMass()/ 1000) * sudistance * 0.00025)\nhtml= ([[\n<svg class=\"bootstrap\" viewBox=\"0 0 1024 1024\" style=\"width:100%; height:100%\"> \n<circle cx=\"500\" cy=\"500\" r=\"400\" stroke=\"darkgreen\" stroke-width=\"3\" transform=\"\"></circle><circle cx=\"500\" cy=\"500\" r=\"350\" stroke=\"darkgreen\" stroke-width=\"3\" transform=\"\" stroke-opacity=\"0.2\"></circle><circle cx=\"500\" cy=\"500\" r=\"300\" stroke=\"darkgreen\" stroke-width=\"3\" transform=\"\"></circle><circle cx=\"500\" cy=\"500\" r=\"250\" stroke=\"darkgreen\" stroke-width=\"3\" transform=\"\" stroke-opacity=\"0.2\"></circle><circle cx=\"500\" cy=\"500\" r=\"200\" stroke=\"darkgreen\" stroke-width=\"3\" transform=\"\"></circle><circle cx=\"500\" cy=\"500\" r=\"150\" stroke=\"darkgreen\" stroke-width=\"3\" transform=\"\" stroke-opacity=\"0.2\"></circle><circle cx=\"500\" cy=\"500\" r=\"100\" stroke=\"lightblue\" stroke-width=\"3\" transform=\"\"></circle><circle cx=\"500\" cy=\"500\" r=\"50\" stroke=\"lightblue\" stroke-width=\"3\" transform=\"\" stroke-opacity=\"0.2\"></circle><circle cx=\"500\" cy=\"500\" r=\"20\" stroke=\"Orange\" stroke-width=\"2\" transform=\"\"></circle><text x=\"510\" y=\"510\" fill=\"Yellow\">Helios</text><circle cx=\"-0.00\" cy=\"0\" r=\"10\" stroke=\"black\" stroke-width=\"1\" fill=\"blue\" transform=\"translate(500,500)\"></circle><text x=\"-0.00\" y=\"0\" transform=\"translate(500,480)\" fill=\"white\" font-size=\"20\">Alioth</text><circle cx=\"7.16\" cy=\"247.59\" r=\"10\" stroke=\"black\" stroke-width=\"1\" fill=\"blue\" transform=\"translate(500,500)\"></circle><text x=\"7.16\" y=\"247.59\" transform=\"translate(480,480)\" fill=\"white\" font-size=\"20\">Ion</text><circle cx=\"35.41\" cy=\"214.09\" r=\"10\" stroke=\"black\" stroke-width=\"1\" fill=\"blue\" transform=\"translate(500,500)\"></circle><text x=\"35.41\" y=\"214.09\" transform=\"translate(500,480)\" fill=\"white\" font-size=\"20\">Symeon</text><circle cx=\"-33.09\" cy=\"-139.41\" r=\"10\" stroke=\"black\" stroke-width=\"1\" fill=\"blue\" transform=\"translate(500,500)\"></circle><text x=\"-33.09\" y=\"-139.41\" transform=\"translate(500,480)\" fill=\"white\" font-size=\"20\">Talemai</text><circle cx=\"202.16\" cy=\"-136.66\" r=\"10\" stroke=\"black\" stroke-width=\"1\" fill=\"blue\" transform=\"translate(500,500)\"></circle><text x=\"202.16\" y=\"-136.66\" transform=\"translate(500,480)\" fill=\"white\" font-size=\"20\">Teoma</text><circle cx=\"247.16\" cy=\"33.84\" r=\"10\" stroke=\"black\" stroke-width=\"1\" fill=\"blue\" transform=\"translate(500,500)\"></circle><text x=\"247.16\" y=\"33.84\" transform=\"translate(500,480)\" fill=\"white\" font-size=\"20\">Lacobus</text><circle cx=\"-108.84\" cy=\"-56.41\" r=\"10\" stroke=\"black\" stroke-width=\"1\" fill=\"blue\" transform=\"translate(500,500)\"></circle><text x=\"-108.84\" y=\"-56.41\" transform=\"translate(500,480)\" fill=\"white\" font-size=\"20\">Feli</text><circle cx=\"72.91\" cy=\"-27.16\" r=\"10\" stroke=\"black\" stroke-width=\"1\" fill=\"blue\" transform=\"translate(500,500)\"></circle><text x=\"72.91\" y=\"-27.16\" transform=\"translate(500,485)\" fill=\"white\" font-size=\"20\">Thades</text><circle cx=\"43.66\" cy=\"-56.66\" r=\"10\" stroke=\"black\" stroke-width=\"1\" fill=\"blue\" transform=\"translate(500,500)\"></circle><text x=\"43.66\" y=\"-56.66\" transform=\"translate(500,480)\" fill=\"white\" font-size=\"20\">Madis</text><circle cx=\"-235.34\" cy=\"-31.91\" r=\"10\" stroke=\"black\" stroke-width=\"1\" fill=\"blue\" transform=\"translate(500,500)\"></circle><text x=\"-235.34\" y=\"-31.91\" transform=\"translate(500,480)\" fill=\"white\" font-size=\"20\">Jago</text><circle cx=\"131.91\" cy=\"-67.91\" r=\"10\" stroke=\"black\" stroke-width=\"1\" fill=\"blue\" transform=\"translate(500,500)\"></circle><text x=\"131.91\" y=\"-67.91\" transform=\"translate(475,480)\" fill=\"white\" font-size=\"20\">Sicari</text><circle cx=\"146.66\" cy=\"-74.16\" r=\"10\" stroke=\"black\" stroke-width=\"1\" fill=\"blue\" transform=\"translate(500,500)\"></circle><text x=\"146.66\" y=\"-74.16\" transform=\"translate(515,480)\" fill=\"white\" font-size=\"20\">Sinnen</text>\n<line stroke-linecap=\"undefined\" stroke-linejoin=\"undefined\" id=\"svg_1\" y2=\"]]..destY..[[\" x2=\"]]..destX..[[\" y1=\"]]..locY..[[\" x1=\"]]..locX..[[\" transform=\"translate(500,500)\" stroke-width=\"5\" stroke=\"#ff0000\" fill=\"none\"/> \n<circle cx=\"]]..locX..[[\" cy=\"]]..locY..[[\" r=\"3\" stroke=\"black\" stroke-width=\"1\" fill=\"limegreen\" transform=\"translate(500,500)\"></circle>\n<text x=\"]]..locX..[[\" y=\"]]..locY..[[\" transform=\"translate(500,500)\" \nfill=\"limegreen\" font-size= \"4.5vh\" font-weight= \"bold\">//SHIP POSITION</text>\n</svg>\n<svg class=\"bootstrap\" viewBox=\"0 0 1024 612\" style=\"width:100%; height:100%\">\n <g>\n <title>Layer 1</title>\n <g id=\"svg_24\">\n <text stroke=\"null\" transform=\"matrix(0.7907331239400577,0,0,0.7600725676692406,3.135703637258853,5.731969683147472) \" xml:space=\"preserve\" text-anchor=\"start\" font-family=\"Helvetica, Arial, sans-serif\" font-size=\"20\" id=\"svg_8\" y=\"70\" x=\"55\" stroke-width=\"0\" fill=\"Yellow\">Alioth :]]..distalioth..[[ SU</text>\n <text stroke=\"null\" transform=\"matrix(0.7907331239400577,0,0,0.7600725676692406,3.135703637258853,5.731969683147472) \" xml:space=\"preserve\" text-anchor=\"start\" font-family=\"Helvetica, Arial, sans-serif\" font-size=\"20\" id=\"svg_14\" y=\"170\" x=\"55\" stroke-width=\"0\" fill=\"Yellow\">Madis :]]..distmadis..[[ SU</text>\n <text stroke=\"null\" transform=\"matrix(0.7907331239400577,0,0,0.7600725676692406,3.135703637258853,5.731969683147472) \" xml:space=\"preserve\" text-anchor=\"start\" font-family=\"Helvetica, Arial, sans-serif\" font-size=\"20\" id=\"svg_17\" y=\"270\" x=\"55\" stroke-width=\"0\" fill=\"Yellow\">Thades :]]..distthades..[[ SU</text>\n <text stroke=\"null\" transform=\"matrix(0.7907331239400577,0,0,0.7600725676692406,3.135703637258853,5.731969683147472) \" xml:space=\"preserve\" text-anchor=\"start\" font-family=\"Helvetica, Arial, sans-serif\" font-size=\"20\" id=\"svg_20\" y=\"370\" x=\"55\" stroke-width=\"0\" fill=\"Yellow\">Talemai :]]..disttalemai..[[ SU</text>\n <text stroke=\"null\" transform=\"matrix(0.7907331239400577,0,0,0.7600725676692406,3.135703637258853,5.731969683147472) \" xml:space=\"preserve\" text-anchor=\"start\" font-family=\"Helvetica, Arial, sans-serif\" font-size=\"20\" id=\"svg_23\" y=\"470\" x=\"55\" stroke-width=\"0\" fill=\"Yellow\">Feli :]]..distfeli..[[ SU</text>\n <text stroke=\"null\" transform=\"matrix(0.7907331239400577,0,0,0.7600725676692406,3.135703637258853,5.731969683147472) \" xml:space=\"preserve\" text-anchor=\"start\" font-family=\"Helvetica, Arial, sans-serif\" font-size=\"20\" id=\"svg_26\" y=\"570\" x=\"55\" stroke-width=\"0\" fill=\"Yellow\">Sicari :]]..distsicari..[[ SU</text>\n <g id=\"svg_12\">\n <rect rx=\"10\" id=\"svg_1\" height=\"50\" width=\"250\" y=\"30\" x=\"15\" stroke-width=\"20\" stroke=\"#00ff00\" fill=\"none\"/>\n <rect rx=\"10\" id=\"svg_3\" height=\"50\" width=\"250\" y=\"105\" x=\"15\" stroke-width=\"20\" stroke=\"#00ff00\" fill=\"none\"/>\n <rect rx=\"10\" id=\"svg_7\" height=\"50\" width=\"250\" y=\"180\" x=\"15\" stroke-width=\"20\" stroke=\"#00ff00\" fill=\"none\"/>\n <rect rx=\"10\" id=\"svg_9\" height=\"50\" width=\"250\" y=\"255\" x=\"15\" stroke-width=\"20\" stroke=\"#00ff00\" fill=\"none\"/>\n <rect rx=\"10\" id=\"svg_10\" height=\"50\" width=\"250\" y=\"330\" x=\"15\" stroke-width=\"20\" stroke=\"#00ff00\" fill=\"none\"/>\n <rect rx=\"10\" id=\"svg_11\" height=\"50\" width=\"250\" y=\"405\" x=\"15\" stroke-width=\"20\" stroke=\"#00ff00\" fill=\"none\"/>\n </g>\n </g>\n <g id=\"svg_40\">\n <text stroke=\"null\" transform=\"matrix(0.7907331239400577,0,0,0.7600725676692406,3.135703637258853,5.731969683147472) \" xml:space=\"preserve\" text-anchor=\"start\" font-family=\"Helvetica, Arial, sans-serif\" font-size=\"20\" id=\"svg_25\" y=\"70\" x=\"997.163642\" stroke-width=\"0\" fill=\"Yellow\">Symeon :]]..distsymeon..[[ SU</text>\n <text stroke=\"null\" transform=\"matrix(0.7907331239400577,0,0,0.7600725676692406,3.135703637258853,5.731969683147472) \" xml:space=\"preserve\" text-anchor=\"start\" font-family=\"Helvetica, Arial, sans-serif\" font-size=\"20\" id=\"svg_27\" y=\"170\" x=\"997.163642\" stroke-width=\"0\" fill=\"Yellow\">Sinnen :]]..distsinnen..[[ SU</text>\n <text stroke=\"null\" transform=\"matrix(0.7907331239400577,0,0,0.7600725676692406,3.135703637258853,5.731969683147472) \" xml:space=\"preserve\" text-anchor=\"start\" font-family=\"Helvetica, Arial, sans-serif\" font-size=\"20\" id=\"svg_28\" y=\"270\" x=\"997.163642\" stroke-width=\"0\" fill=\"Yellow\">Jago :]]..distjago..[[ SU</text>\n <text stroke=\"null\" transform=\"matrix(0.7907331239400577,0,0,0.7600725676692406,3.135703637258853,5.731969683147472) \" xml:space=\"preserve\" text-anchor=\"start\" font-family=\"Helvetica, Arial, sans-serif\" font-size=\"20\" id=\"svg_30\" y=\"370\" x=\"997.163642\" stroke-width=\"0\" fill=\"Yellow\">Teoma :]]..distteoma..[[ SU</text>\n <text stroke=\"null\" transform=\"matrix(0.7907331239400577,0,0,0.7600725676692406,3.135703637258853,5.731969683147472) \" xml:space=\"preserve\" text-anchor=\"start\" font-family=\"Helvetica, Arial, sans-serif\" font-size=\"20\" id=\"svg_31\" y=\"470\" x=\"997.163642\" stroke-width=\"0\" fill=\"Yellow\">Ion :]]..distion..[[ SU</text>\n <text stroke=\"null\" transform=\"matrix(0.7907331239400577,0,0,0.7600725676692406,3.135703637258853,5.731969683147472) \" xml:space=\"preserve\" text-anchor=\"start\" font-family=\"Helvetica, Arial, sans-serif\" font-size=\"20\" id=\"svg_32\" y=\"570\" x=\"997.163642\" stroke-width=\"0\" fill=\"Yellow\">Lacobus :]]..distlacobus..[[ SU</text>\n <g id=\"svg_39\">\n <rect rx=\"10\" id=\"svg_33\" height=\"50\" width=\"250\" y=\"30\" x=\"760\" stroke-width=\"20\" stroke=\"#00ff00\" fill=\"none\"/>\n <rect rx=\"10\" id=\"svg_34\" height=\"50\" width=\"250\" y=\"105\" x=\"760\" stroke-width=\"20\" stroke=\"#00ff00\" fill=\"none\"/>\n <rect rx=\"10\" id=\"svg_35\" height=\"50\" width=\"250\" y=\"180\" x=\"760\" stroke-width=\"20\" stroke=\"#00ff00\" fill=\"none\"/>\n <rect rx=\"10\" id=\"svg_36\" height=\"50\" width=\"250\" y=\"255\" x=\"760\" stroke-width=\"20\" stroke=\"#00ff00\" fill=\"none\"/>\n <rect rx=\"10\" id=\"svg_37\" height=\"50\" width=\"250\" y=\"330\" x=\"760\" stroke-width=\"20\" stroke=\"#00ff00\" fill=\"none\"/>\n <rect rx=\"10\" id=\"svg_38\" height=\"50\" width=\"250\" y=\"405\" x=\"760\" stroke-width=\"20\" stroke=\"#00ff00\" fill=\"none\"/>\n </g>\n </g>\n </g>\n<text stroke=\"null\" transform=\"matrix(0.7907331239400577,0,0,0.7600725676692406,3.135703637258853,5.731969683147472) \" xml:space=\"preserve\" text-anchor=\"start\" font-family=\"Helvetica, Arial, sans-serif\" font-size=\"30\" id=\"svg_32\" y=\"700\" x=\"20\" stroke-width=\"0\" fill=\"LightBlue\">Est. Warp Cost: ]]..warpmath..[[</text>\n <text stroke=\"null\" transform=\"matrix(0.7907331239400577,0,0,0.7600725676692406,3.135703637258853,5.731969683147472) \" xml:space=\"preserve\" text-anchor=\"start\" font-family=\"Helvetica, Arial, sans-serif\" font-size=\"30\" id=\"svg_32\" y=\"750\" x=\"20\" stroke-width=\"0\" fill=\"LightBlue\">Construct Weight: ]]..math.floor(core.getConstructMass()/ 1000)..[[ tons</text>\n \n</svg> \n ]])\nscreen.setHTML(html)\nend\nunit.setTimer(\"spacemap\",.08)\n\n","filter":{"args":[],"signature":"start()","slotKey":"-1"},"key":"2"},{"code":"updateScreen()","filter":{"args":[{"value":"spacemap"}],"signature":"tick(timerId)","slotKey":"-1"},"key":"3"}],"methods":[],"events":[]}
    ---------- JayleBreak Atlas and Functions Version---------
    function Atlas() return { [0] = { [1]={ GM=6930729684, bodyId=1, center={x=17465536.000,y=22665536.000,z=-34464.000}, name='Madis', planetarySystemId=0, radius=44300 }, [2]={ GM=157470826617, bodyId=2, center={x=-8.000,y=-8.000,z=-126303.000}, name='Alioth', planetarySystemId=0, radius=126068 }, [3]={ GM=11776905000, bodyId=3, center={x=29165536.000,y=10865536.000,z=65536.000}, name='Thades', planetarySystemId=0, radius=49000 }, [4]={ GM=14893847582, bodyId=4, center={x=-13234464.000,y=55765536.000,z=465536.000}, name='Talemai', planetarySystemId=0, radius=57450 }, [5]={ GM=16951680000, bodyId=5, center={x=-43534464.000,y=22565536.000,z=-48934464.000}, name='Feli', planetarySystemId=0, radius=60000 }, [6]={ GM=10502547741, bodyId=6, center={x=52765536.000,y=27165538.000,z=52065535.000}, name='Sicari', planetarySystemId=0, radius=51100 }, [7]={ GM=13033380591, bodyId=7, center={x=58665538.000,y=29665535.000,z=58165535.000}, name='Sinnen', planetarySystemId=0, radius=54950 }, [8]={ GM=18477723600, bodyId=8, center={x=80865538.000,y=54665536.000,z=-934463.940}, name='Teoma', planetarySystemId=0, radius=62000 }, [9]={ GM=18606274330, bodyId=9, center={x=-94134462.000,y=12765534.000,z=-3634464.000}, name='Jago', planetarySystemId=0, radius=61590 }, [10]={ GM=78480000, bodyId=10, center={x=17448118.224,y=22966846.286,z=143078.820}, name='Madis Moon 1', planetarySystemId=0, radius=10000 }, [11]={ GM=237402000, bodyId=11, center={x=17194626.000,y=22243633.880,z=-214962.810}, name='Madis Moon 2', planetarySystemId=0, radius=11000 }, [12]={ GM=265046609, bodyId=12, center={x=17520614.000,y=22184730.000,z=-309989.990}, name='Madis Moon 3', planetarySystemId=0, radius=15005 }, [21]={ GM=2118960000, bodyId=21, center={x=457933.000,y=-1509011.000,z=115524.000}, name='Alioth Moon 1', planetarySystemId=0, radius=30000 }, [22]={ GM=2165833514, bodyId=22, center={x=-1692694.000,y=729681.000,z=-411464.000}, name='Alioth Moon 4', planetarySystemId=0, radius=30330 }, [26]={ GM=68234043600, bodyId=26, center={x=-1404835.000,y=562655.000,z=-285074.000}, name='Sanctuary', planetarySystemId=0, radius=83400 }, [30]={ GM=211564034, bodyId=30, center={x=29214402.000,y=10907080.695,z=433858.200}, name='Thades Moon 1', planetarySystemId=0, radius=14002 }, [31]={ GM=264870000, bodyId=31, center={x=29404193.000,y=10432768.000,z=19554.131}, name='Thades Moon 2', planetarySystemId=0, radius=15000 }, [40]={ GM=141264000, bodyId=40, center={x=-13503090.000,y=55594325.000,z=769838.640}, name='Talemai Moon 2', planetarySystemId=0, radius=12000 }, [41]={ GM=106830900, bodyId=41, center={x=-12800515.000,y=55700259.000,z=325207.840}, name='Talemai Moon 3', planetarySystemId=0, radius=11000 }, [42]={ GM=264870000, bodyId=42, center={x=-13058408.000,y=55781856.000,z=740177.760}, name='Talemai Moon 1', planetarySystemId=0, radius=15000 }, [50]={ GM=499917600, bodyId=50, center={x=-43902841.780,y=22261034.700,z=-48862386.000}, name='Feli Moon 1', planetarySystemId=0, radius=14000 }, [70]={ GM=396912600, bodyId=70, center={x=58969616.000,y=29797945.000,z=57969449.000}, name='Sinnen Moon 1', planetarySystemId=0, radius=17000 }, [100]={ GM=13975172474, bodyId=100, center={x=98865536.000,y=-13534464.000,z=-934461.990}, name='Lacobus', planetarySystemId=0, radius=55650 }, [101]={ GM=264870000, bodyId=101, center={x=98905288.170,y=-13950921.100,z=-647589.530}, name='Lacobus Moon 3', planetarySystemId=0, radius=15000 }, [102]={ GM=444981600, bodyId=102, center={x=99180968.000,y=-13783862.000,z=-926156.400}, name='Lacobus Moon 1', planetarySystemId=0, radius=18000 }, [103]={ GM=211503600, bodyId=103, center={x=99250052.000,y=-13629215.000,z=-1059341.400}, name='Lacobus Moon 2', planetarySystemId=0, radius=14000 }, [110]={ GM=9204742375, bodyId=110, center={x=14165536.000,y=-85634465.000,z=-934464.300}, name='Symeon', planetarySystemId=0, radius=49050 }, [120]={ GM=7135606629, bodyId=120, center={x=2865536.700,y=-99034464.000,z=-934462.020}, name='Ion', planetarySystemId=0, radius=44950 }, [121]={ GM=106830900, bodyId=121, center={x=2472916.800,y=-99133747.000,z=-1133582.800}, name='Ion Moon 1', planetarySystemId=0, radius=11000 }, [122]={ GM=176580000, bodyId=122, center={x=2995424.500,y=-99275010.000,z=-1378480.700}, name='Ion Moon 2', planetarySystemId=0, radius=15000 } } } end function PlanetRef() --[[ Provide coordinate transforms and access to kinematic related parameters Author: JayleBreak Usage (unit.start): PlanetaryReference = require('planetref') galaxyReference = PlanetaryReference(referenceTableSource) helios = galaxyReference[0] -- PlanetaryReference.PlanetarySystem instance alioth = helios[2] -- PlanetaryReference.BodyParameters instance Methods: PlanetaryReference:getPlanetarySystem - based on planetary system ID. PlanetaryReference.isMapPosition - 'true' if an instance of 'MapPosition' PlanetaryReference.createBodyParameters - for entry into reference table PlanetaryReference.BodyParameters - a class containing a body's information. PlanetaryReference.MapPosition - a class for map coordinates PlanetaryReference.PlanetarySystem - a container for planetary system info. PlanetarySystem:castIntersections - from a position in a given direction. PlanetarySystem:closestBody - to the specified coordinates. PlanetarySystem:convertToBodyIdAndWorldCoordinates - from map coordinates. PlanetarySystem:getBodyParameters - from reference table. PlanetarySystem:getPlanetarySystemId - for the instance. BodyParameters:convertToWorldCoordinates - from map coordinates BodyParameters:convertToMapPosition - from world coordinates BodyParameters:getAltitude - of world coordinates BodyParameters:getDistance - from center to world coordinates BodyParameters:getGravity - at a given position in world coordinates. Description An instance of the 'PlanetaryReference' "class" can contain transform and kinematic reference information for all planetary systems in DualUniverse. Each planetary system is identified by a numeric identifier. Currently, the only planetary system, Helios, has the identifier: zero. This "class" supports the indexing ('[]') operation which is equivalent to the use of the 'getPlanetarySystem' method. It also supports the 'pairs()' method for iterating over planetary systems. An instance of the 'PlanetarySystem' "class" contains all reference information for a specific system. It supports the indexing ('[]') and 'pairs()' functions which allows iteration over each "body" in the system where the key is the numeric body ID. It also supports the 'tostring()' method. An instance of the 'BodyParameters' "class" contains all reference information for a single celestial "body" (a moon or planet). It supports the 'tostring()' method, and contains the data members: planetarySystemId - numeric planetary system ID bodyId - numeric body ID radius - radius of the body in meters (zero altitude) center - world coordinates of the body's center position GM - the gravitation parameter (g = GM/radius^2) Note that the user is allowed to add custom fields (e.g. body name), but should insure that complex table values have the '__tostring' metamethod implemented. Transform and Kinematics: "World" coordinates is a cartesian coordinate system with an origin at an arbitrary fixed point in a planetary system and with distances measured in meters. The coordinates are expressible either as a simple table of 3 values or an instance of the 'vec3' class. In either case, the planetary system identity is implicit. "Map" coordinates is a geographic coordinate system with an origin at the center of an identified (by a numeric value) celestial body which is a member of an identified (also a numeric value) planetary system. Note that the convention that latitude, longitude, and altitude values will be the position's x, y, and z world coordinates in the special case of body ID 0. The kinematic parameters in the reference data permit calculations of the gravitational attraction of the celestial body on other objects. Reference Data: This is an example of reference data with a single entry assigned to planetary system ID 0, and body ID 2 ('Alioth'): referenceTable = { [0] = { [2] = { planetarySystemId = 0, bodyId = 2, radius = 126068, center = vec3({x=-8, y=-8, z=-126303}), GM = 1.572199+11 } -- as in F=-GMm/r^2 } } ref=PlanetaryReference(referenceTable) Collecting Reference Data: A combination of information from the "Map" screen in the DU user interface, and values reported by the DU Lua API can be the source of the reference table's data (planetarySystemId, bodyId, and surfaceArea is from the user interface): referenceTable = {} referenceTable[planetarySystemId][bodyId] = PlanetaryReference.createBodyParameters(planetarySystemId, bodyId, surfaceArea, core.getConstructWorldPos(), core.getWorldVertical(), core.getAltitude(), core.g()) Adapting Data Sources: Other sources of data can be adapted or converted. An example of adapting a table, defined in the file: 'planets.lua', containing information on a single planetary system and using celestial body name as the key follows (note that a 'name' field is added to the BodyParameters instance transparently after construction, and the '__pairs' meta function is required to support the 'closestBody' and '__tostring' methods): ref=PlanetaryReference( {[0] = setmetatable(require('planets'), { __index = function(bodies, bodyId) for _,v in pairs(bodies) do if v and v.bodyId == bodyId then return v end end return nil end, __pairs = function(bodies) return function(t, k) local nk, nv = next(t, k) if nv then local GM = nv.gravity * nv.radius^2 local bp = BodyParameters(0, nv.id, nv.radius, nv.pos, GM) bp.name = nk return nk, bp end return nk, nv end, bodies, nil end }) }) Converting Data Sources: An instance of 'PlanetaryReference' that has been adapted to a data source can be used to convert that source to simple table. For example, using the adapted instance shown above: load('convertedData=' .. tostring(ref))() newRef=PlanetaryReference(convertedData) Also See: kepler.lua ]]-- --[[ START OF LOCAL IMPLEMENTATION DETAILS ]]-- -- Type checks local function isNumber(n) return type(n) == 'number' end local function isSNumber(n) return type(tonumber(n)) == 'number' end local function isTable(t) return type(t) == 'table' end local function isString(s) return type(s) == 'string' end local function isVector(v) return isTable(v) and isNumber(v.x and v.y and v.z) end local function isMapPosition(m) return isTable(m) and isNumber(m.latitude and m.longitude and m.altitude and m.bodyId and m.systemId) end -- Constants local deg2rad = math.pi/180 local rad2deg = 180/math.pi local epsilon = 1e-10 local num = ' *([+-]?%d+%.?%d*e?[+-]?%d*)' local posPattern = '::pos{' .. num .. ',' .. num .. ',' .. num .. ',' .. num .. ',' .. num .. '}' -- Utilities local utils = require('cpml.utils') local vec3 = require('cpml.vec3') local clamp = utils.clamp local function float_eq(a,b) if a == 0 then return math.abs(b) < 1e-09 end if b == 0 then return math.abs(a) < 1e-09 end return math.abs(a - b) < math.max(math.abs(a),math.abs(b))*epsilon end local function formatNumber(n) local result = string.gsub( string.reverse(string.format('%.4f',n)), '^0*%.?','') return result == '' and '0' or string.reverse(result) end local function formatValue(obj) if isVector(obj) then return string.format('{x=%.3f,y=%.3f,z=%.3f}', obj.x, obj.y, obj.z) end if isTable(obj) and not getmetatable(obj) then local list = {} local nxt = next(obj) if type(nxt) == 'nil' or nxt == 1 then -- assume this is an array list = obj else for k,v in pairs(obj) do local value = formatValue(v) if type(k) == 'number' then table.insert(list, string.format('[%s]=%s', k, value)) else table.insert(list, string.format('%s=%s', k, value)) end end end return string.format('{%s}', table.concat(list, ',')) end if isString(obj) then return string.format("'%s'", obj:gsub("'",[[\']])) end return tostring(obj) end -- CLASSES -- BodyParameters: Attributes of planetary bodies (planets and moons) local BodyParameters = {} BodyParameters.__index = BodyParameters BodyParameters.__tostring = function(obj, indent) local sep = indent or '' local keys = {} for k in pairs(obj) do table.insert(keys, k) end table.sort(keys) local list = {} for _, k in ipairs(keys) do local value = formatValue(obj[k]) if type(k) == 'number' then table.insert(list, string.format('[%s]=%s', k, value)) else table.insert(list, string.format('%s=%s', k, value)) end end if indent then return string.format('%s%s', indent, table.concat(list, ',\n' .. indent)) end return string.format('{%s}', table.concat(list, ',')) end BodyParameters.__eq = function(lhs, rhs) return lhs.planetarySystemId == rhs.planetarySystemId and lhs.bodyId == rhs.bodyId and float_eq(lhs.radius, rhs.radius) and float_eq(lhs.center.x, rhs.center.x) and float_eq(lhs.center.y, rhs.center.y) and float_eq(lhs.center.z, rhs.center.z) and float_eq(lhs.GM, rhs.GM) end local function mkBodyParameters(systemId, bodyId, radius, worldCoordinates, GM) -- 'worldCoordinates' can be either table or vec3 assert(isSNumber(systemId), 'Argument 1 (planetarySystemId) must be a number:' .. type(systemId)) assert(isSNumber(bodyId), 'Argument 2 (bodyId) must be a number:' .. type(bodyId)) assert(isSNumber(radius), 'Argument 3 (radius) must be a number:' .. type(radius)) assert(isTable(worldCoordinates), 'Argument 4 (worldCoordinates) must be a array or vec3.' .. type(worldCoordinates)) assert(isSNumber(GM), 'Argument 5 (GM) must be a number:' .. type(GM)) return setmetatable({planetarySystemId = tonumber(systemId), bodyId = tonumber(bodyId), radius = tonumber(radius), center = vec3(worldCoordinates), GM = tonumber(GM) }, BodyParameters) end -- MapPosition: Geographical coordinates of a point on a planetary body. local MapPosition = {} MapPosition.__index = MapPosition MapPosition.__tostring = function(p) return string.format('::pos{%d,%d,%s,%s,%s}', p.systemId, p.bodyId, formatNumber(p.latitude*rad2deg), formatNumber(p.longitude*rad2deg), formatNumber(p.altitude)) end MapPosition.__eq = function(lhs, rhs) return lhs.bodyId == rhs.bodyId and lhs.systemId == rhs.systemId and float_eq(lhs.latitude, rhs.latitude) and float_eq(lhs.altitude, rhs.altitude) and (float_eq(lhs.longitude, rhs.longitude) or float_eq(lhs.latitude, math.pi/2) or float_eq(lhs.latitude, -math.pi/2)) end -- latitude and longitude are in degrees while altitude is in meters local function mkMapPosition(overload, bodyId, latitude, longitude, altitude) local systemId = overload -- Id or '::pos{...}' string if isString(overload) and not longitude and not altitude and not bodyId and not latitude then systemId, bodyId, latitude, longitude, altitude = string.match(overload, posPattern) assert(systemId, 'Argument 1 (position string) is malformed.') else assert(isSNumber(systemId), 'Argument 1 (systemId) must be a number:' .. type(systemId)) assert(isSNumber(bodyId), 'Argument 2 (bodyId) must be a number:' .. type(bodyId)) assert(isSNumber(latitude), 'Argument 3 (latitude) must be in degrees:' .. type(latitude)) assert(isSNumber(longitude), 'Argument 4 (longitude) must be in degrees:' .. type(longitude)) assert(isSNumber(altitude), 'Argument 5 (altitude) must be in meters:' .. type(altitude)) end systemId = tonumber(systemId) bodyId = tonumber(bodyId) latitude = tonumber(latitude) longitude = tonumber(longitude) altitude = tonumber(altitude) if bodyId == 0 then -- this is a hack to represent points in space return setmetatable({latitude = latitude, longitude = longitude, altitude = altitude, bodyId = bodyId, systemId = systemId}, MapPosition) end return setmetatable({latitude = deg2rad*clamp(latitude, -90, 90), longitude = deg2rad*(longitude % 360), altitude = altitude, bodyId = bodyId, systemId = systemId}, MapPosition) end -- PlanetarySystem - map body IDs to BodyParameters local PlanetarySystem = {} PlanetarySystem.__index = PlanetarySystem PlanetarySystem.__tostring = function (obj, indent) local sep = indent and (indent .. ' ' ) local bdylist = {} local keys = {} for k in pairs(obj) do table.insert(keys, k) end table.sort(keys) for _, bi in ipairs(keys) do bdy = obj[bi] local bdys = BodyParameters.__tostring(bdy, sep) if indent then table.insert(bdylist, string.format('[%s]={\n%s\n%s}', bi, bdys, indent)) else table.insert(bdylist, string.format(' [%s]=%s', bi, bdys)) end end if indent then return string.format('\n%s%s%s', indent, table.concat(bdylist, ',\n' .. indent), indent) end return string.format('{\n%s\n}', table.concat(bdylist, ',\n')) end local function mkPlanetarySystem(referenceTable) local atlas = {} local pid for _, v in pairs(referenceTable) do local id = v.planetarySystemId if type(id) ~= 'number' then error('Invalid planetary system ID: ' .. tostring(id)) elseif pid and id ~= pid then error('Mismatch planetary system IDs: ' .. id .. ' and ' .. pid) end local bid = v.bodyId if type(bid) ~= 'number' then error('Invalid body ID: ' .. tostring(bid)) elseif atlas[bid] then error('Duplicate body ID: ' .. tostring(bid)) end setmetatable(v.center, getmetatable(vec3.unit_x)) atlas[bid] = setmetatable(v, BodyParameters) pid = id end return setmetatable(atlas, PlanetarySystem) end -- PlanetaryReference - map planetary system ID to PlanetarySystem PlanetaryReference = {} local function mkPlanetaryReference(referenceTable) return setmetatable({ galaxyAtlas = referenceTable or {} }, PlanetaryReference) end PlanetaryReference.__index = function(t,i) if type(i) == 'number' then local system = t.galaxyAtlas[i] return mkPlanetarySystem(system) end return rawget(PlanetaryReference, i) end PlanetaryReference.__pairs = function(obj) return function(t, k) local nk, nv = next(t, k) return nk, nv and mkPlanetarySystem(nv) end, obj.galaxyAtlas, nil end PlanetaryReference.__tostring = function (obj) local pslist = {} for _,ps in pairs(obj or {}) do local psi = ps:getPlanetarySystemId() local pss = PlanetarySystem.__tostring(ps, ' ') table.insert(pslist, string.format(' [%s]={%s\n }', psi, pss)) end return string.format('{\n%s\n}\n', table.concat(pslist,',\n')) end --[[ START OF PUBLIC INTERFACE ]]-- -- PlanetaryReference CLASS METHODS: -- -- BodyParameters - create an instance of BodyParameters class -- planetarySystemId [in]: the body's planetary system ID. -- bodyId [in]: the body's ID. -- radius [in]: the radius in meters of the planetary body. -- bodyCenter [in]: the world coordinates of the center (vec3 or table). -- GM [in]: the body's standard gravitational parameter. -- return: an instance of BodyParameters class. -- PlanetaryReference.BodyParameters = mkBodyParameters -- -- MapPosition - create an instance of the MapPosition class -- overload [in]: either a planetary system ID or a position string ('::pos...') -- bodyId [in]: (ignored if overload is a position string) the body's ID. -- latitude [in]: (ignored if overload is a position string) the latitude. -- longitude [in]:(ignored if overload is a position string) the longitude. -- altitude [in]: (ignored if overload is a position string) the altitude. -- return: the class instance -- PlanetaryReference.MapPosition = mkMapPosition -- -- PlanetarySystem - create an instance of PlanetarySystem class -- referenceData [in]: a table (indexed by bodyId) of body reference info. -- return: the class instance -- PlanetaryReference.PlanetarySystem = mkPlanetarySystem -- -- createBodyParameters - create an instance of BodyParameters class -- planetarySystemId [in]: the body's planetary system ID. -- bodyId [in]: the body's ID. -- surfaceArea [in]: the body's surface area in square meters. -- aPosition [in]: world coordinates of a position near the body. -- verticalAtPosition [in]: a vector pointing towards the body center. -- altitudeAtPosition [in]: the altitude in meters at the position. -- gravityAtPosition [in]: the magnitude of the gravitational acceleration. -- return: an instance of BodyParameters class. -- function PlanetaryReference.createBodyParameters(planetarySystemId, bodyId, surfaceArea, aPosition, verticalAtPosition, altitudeAtPosition, gravityAtPosition) assert(isSNumber(planetarySystemId), 'Argument 1 (planetarySystemId) must be a number:' .. type(planetarySystemId)) assert(isSNumber(bodyId), 'Argument 2 (bodyId) must be a number:' .. type(bodyId)) assert(isSNumber(surfaceArea), 'Argument 3 (surfaceArea) must be a number:' .. type(surfaceArea)) assert(isTable(aPosition), 'Argument 4 (aPosition) must be an array or vec3:' .. type(aPosition)) assert(isTable(verticalAtPosition), 'Argument 5 (verticalAtPosition) must be an array or vec3:' .. type(verticalAtPosition)) assert(isSNumber(altitudeAtPosition), 'Argument 6 (altitude) must be in meters:' .. type(altitudeAtPosition)) assert(isSNumber(gravityAtPosition), 'Argument 7 (gravityAtPosition) must be number:' .. type(gravityAtPosition)) local radius = math.sqrt(surfaceArea/4/math.pi) local distance = radius + altitudeAtPosition local center = vec3(aPosition) + distance*vec3(verticalAtPosition) local GM = gravityAtPosition * distance * distance return mkBodyParameters(planetarySystemId, bodyId, radius, center, GM) end -- -- isMapPosition - check for the presence of the 'MapPosition' fields -- valueToTest [in]: the value to be checked -- return: 'true' if all required fields are present in the input value -- PlanetaryReference.isMapPosition = isMapPosition -- PlanetaryReference INSTANCE METHODS: -- -- getPlanetarySystem - get the planetary system using ID or MapPosition as key -- overload [in]: either the planetary system ID or a MapPosition that has it. -- return: instance of 'PlanetarySystem' class or nil on error -- function PlanetaryReference:getPlanetarySystem(overload) --if galaxyAtlas then local planetarySystemId = overload if isMapPosition(overload) then planetarySystemId = overload.systemId end if type(planetarySystemId) == 'number' then local system = self.galaxyAtlas[i] if system then if getmetatable(nv) ~= PlanetarySystem then system = mkPlanetarySystem(system) end return system end end --end --return nil end -- PlanetarySystem INSTANCE METHODS: -- -- castIntersections - Find the closest body that intersects a "ray cast". -- origin [in]: the origin of the "ray cast" in world coordinates -- direction [in]: the direction of the "ray cast" as a 'vec3' instance. -- sizeCalculator [in]: (default: returns 1.05*radius) Returns size given body. -- bodyIds[in]: (default: all IDs in system) check only the given IDs. -- return: The closest body that blocks the cast or 'nil' if none. -- function PlanetarySystem:castIntersections(origin, direction, sizeCalculator, bodyIds) local sizeCalculator = sizeCalculator or function (body) return 1.05*body.radius end local candidates = {} if bodyIds then for _,i in ipairs(bodyIds) do candidates[i] = self[i] end else bodyIds = {} for k,body in pairs(self) do table.insert(bodyIds, k) candidates[k] = body end end local function compare(b1,b2) local v1 = candidates[b1].center - origin local v2 = candidates[b2].center - origin return v1:len() < v2:len() end table.sort(bodyIds, compare) local dir = direction:normalize() for i, id in ipairs(bodyIds) do local body = candidates[id] local c_oV3 = body.center - origin local radius = sizeCalculator(body) local dot = c_oV3:dot(dir) local desc = dot^2 - (c_oV3:len2() - radius^2) if desc >= 0 then local root = math.sqrt(desc) local farSide = dot + root local nearSide = dot - root if nearSide > 0 then return body, farSide, nearSide elseif farSide > 0 then return body, farSide, nil end end end return nil, nil, nil end -- -- closestBody - find the closest body to a given set of world coordinates -- coordinates [in]: the world coordinates of position in space -- return: an instance of the BodyParameters object closest to 'coordinates' -- function PlanetarySystem:closestBody(coordinates) assert(type(coordinates) == 'table', 'Invalid coordinates.') local minDistance2, body local coord = vec3(coordinates) for _,params in pairs(self) do local distance2 = (params.center - coord):len2() if not body or distance2 < minDistance2 then body = params minDistance2 = distance2 end end return body end -- -- convertToBodyIdAndWorldCoordinates - map to body Id and world coordinates -- overload [in]: an instance of MapPosition or a position string ('::pos...) -- return: a vec3 instance containing the world coordinates or 'nil' on error. -- function PlanetarySystem:convertToBodyIdAndWorldCoordinates(overload) local mapPosition = overload if isString(overload) then mapPosition = mkMapPosition(overload) end if mapPosition.bodyId == 0 then return 0, vec3(mapPosition.latitude, mapPosition.longitude, mapPosition.altitude) end local params = self:getBodyParameters(mapPosition) if params then return mapPosition.bodyId, params:convertToWorldCoordinates(mapPosition) end end -- -- getBodyParameters - get or create an instance of BodyParameters class -- overload [in]: either an instance of MapPosition or a body's ID. -- return: a BodyParameters instance or 'nil' if body ID is not found. -- function PlanetarySystem:getBodyParameters(overload) local bodyId = overload if isMapPosition(overload) then bodyId = overload.bodyId end assert(isSNumber(bodyId), 'Argument 1 (bodyId) must be a number:' .. type(bodyId)) return self[bodyId] end -- -- getPlanetarySystemId - get the planetary system ID for this instance -- return: the planetary system ID or nil if no planets are in the system. -- function PlanetarySystem:getPlanetarySystemId() local k, v = next(self) return v and v.planetarySystemId end -- BodyParameters INSTANCE METHODS: -- -- convertToMapPosition - create an instance of MapPosition from coordinates -- worldCoordinates [in]: the world coordinates of the map position. -- return: an instance of MapPosition class -- function BodyParameters:convertToMapPosition(worldCoordinates) assert(isTable(worldCoordinates), 'Argument 1 (worldCoordinates) must be an array or vec3:' .. type(worldCoordinates)) local worldVec = vec3(worldCoordinates) if self.bodyId == 0 then return setmetatable({latitude = worldVec.x, longitude = worldVec.y, altitude = worldVec.z, bodyId = 0, systemId = self.planetarySystemId}, MapPosition) end local coords = worldVec - self.center local distance = coords:len() local altitude = distance - self.radius local latitude = 0 local longitude = 0 if not float_eq(distance, 0) then local phi = math.atan(coords.y, coords.x) longitude = phi >= 0 and phi or (2*math.pi + phi) latitude = math.pi/2 - math.acos(coords.z/distance) end return setmetatable({latitude = latitude, longitude = longitude, altitude = altitude, bodyId = self.bodyId, systemId = self.planetarySystemId}, MapPosition) end -- -- convertToWorldCoordinates - convert a map position to world coordinates -- overload [in]: an instance of MapPosition or a position string ('::pos...') -- function BodyParameters:convertToWorldCoordinates(overload) local mapPosition = isString(overload) and mkMapPosition(overload) or overload if mapPosition.bodyId == 0 then -- support deep space map position return vec3(mapPosition.latitude, mapPosition.longitude, mapPosition.altitude) end assert(isMapPosition(mapPosition), 'Argument 1 (mapPosition) is not an instance of "MapPosition".') assert(mapPosition.systemId == self.planetarySystemId, 'Argument 1 (mapPosition) has a different planetary system ID.') assert(mapPosition.bodyId == self.bodyId, 'Argument 1 (mapPosition) has a different planetary body ID.') local xproj = math.cos(mapPosition.latitude) return self.center + (self.radius + mapPosition.altitude) * vec3(xproj*math.cos(mapPosition.longitude), xproj*math.sin(mapPosition.longitude), math.sin(mapPosition.latitude)) end -- -- getAltitude - calculate the altitude of a point given in world coordinates. -- worldCoordinates [in]: the world coordinates of the point. -- return: the altitude in meters -- function BodyParameters:getAltitude(worldCoordinates) return (vec3(worldCoordinates) - self.center):len() - self.radius end -- -- getDistance - calculate the distance to a point given in world coordinates. -- worldCoordinates [in]: the world coordinates of the point. -- return: the distance in meters -- function BodyParameters:getDistance(worldCoordinates) return (vec3(worldCoordinates) - self.center):len() end -- -- getGravity - calculate the gravity vector induced by the body. -- worldCoordinates [in]: the world coordinates of the point. -- return: the gravity vector in meter/seconds^2 -- function BodyParameters:getGravity(worldCoordinates) local radial = self.center - vec3(worldCoordinates) -- directed towards body local len2 = radial:len2() return (self.GM/len2) * radial/math.sqrt(len2) end -- end of module return setmetatable(PlanetaryReference, { __call = function(_,...) return mkPlanetaryReference(...) end }) end function Keplers() --[[ Provides methods for computing orbital information for an object Usage: Kepler = require('autoconf.custom.kepler') alioth = Kepler({ GM=157470826617, bodyId=2, center={x=-8.000,y=-8.000,z=-126303.000}, name='Alioth', planetarySystemId=0, radius=126068 }) altitude = 6000 position = '::pos{0,2,0,0,6000}' e, o = alioth:escapeAndOrbitalSpeed(altitude) orbit = alioth:orbitalParameters(position, {0, o+1, 0}) print("Eccentricity " .. orbit.eccentricity) print("Perihelion " .. orbit.periapsis.altitude) print("Max. speed " .. orbit.periapsis.speed) print("Circular orbit speed " .. orbit.periapsis.circularOrbitSpeed) print("Aphelion " .. orbit.apoapsis.altitude) print("Min. speed " .. orbit.apoapsis.speed) print("Orbital period " .. orbit.period) --- output: Eccentricity 0.0018324307017878 Perihelion 6000.0 Max. speed 1092.9462297033 Circular orbit speed 1091.9462297033 Aphelion 6484.8994605062 Min. speed 1088.9480596194 Orbital period 762.02818214049 Methods: Kepler:escapeAndOrbitalSpeed - for a given celestial body and altitude. Kepler:orbitalParameters - for a given massless object and a celestial body. Description The motion of an object in the vicinity of substantially larger mass is in the domain of the "2-body problem". By assuming the object whose motion is of interest is of negligable mass simplifies the calculations of: the speed to escape the body, the speed of a circular orbit, and the parameters defining the orbit of the object (or the lack of orbit as the case may be). Orbital Parameters: periapsis - the closest approach to the planet apoapsis - the furthest point from the planet if in orbit (otherwise nil) eccentricity - 0 for circular orbits <1 for elliptical orbits 1 for parabiolic trajectory >1 for hyperbolic trajectory period - time (in seconds) to complete an orbit Also See: planetref.lua ]]-- local vec3 = require('cpml.vec3') local PlanetRef = PlanetRef() local function isString(s) return type(s) == 'string' end local function isTable(t) return type(t) == 'table' end local function float_eq(a,b) if a == 0 then return math.abs(b) < 1e-09 end if b == 0 then return math.abs(a) < 1e-09 end return math.abs(a - b) < math.max(math.abs(a),math.abs(b))*epsilon end Kepler = {} Kepler.__index = Kepler -- -- escapeAndOrbitalSpeed - speed required to escape and for a circular orbit -- altitude [in]: the height of the orbit in meters above "sea-level" -- return: the speed in m/s needed to escape the celestial body and to orbit it. -- function Kepler:escapeAndOrbitalSpeed(altitude) assert(self.body) -- P = -GMm/r and KE = mv^2/2 (no lorentz factor used) -- mv^2/2 = GMm/r -- v^2 = 2GM/r -- v = sqrt(2GM/r1) local distance = altitude + self.body.radius if not float_eq(distance, 0) then local orbit = math.sqrt(self.body.GM/distance) return math.sqrt(2)*orbit, orbit end return nil, nil end -- -- orbitalParameters: determine the orbital elements for a two-body system. -- overload [in]: the world coordinates or map coordinates of a massless object. -- velocity [in]: The velocity of the massless point object in m/s. -- return: the 6 orbital elements for the massless object. -- function Kepler:orbitalParameters(overload, velocity) assert(self.body) assert(isTable(overload) or isString(overload)) assert(isTable(velocity)) local pos = (isString(overload) or PlanetRef.isMapPosition(overload)) and self.body:convertToWorldCoordinates(overload) or vec3(overload) local v = vec3(velocity) local r = pos - self.body.center local v2 = v:len2() local d = r:len() local mu = self.body.GM local e = ((v2 - mu/d)*r - r:dot(v)*v)/mu local a = mu/(2*mu/d - v2) local ecc = e:len() local dir = e:normalize() local pd = a*(1-ecc) local ad = a*(1+ecc) local per = pd*dir + self.body.center local apo = ecc <= 1 and -ad*dir + self.body.center or nil local trm = math.sqrt(a*mu*(1-ecc*ecc)) local Period = apo and 2*math.pi*math.sqrt(a^3/mu) -- These are great and all, but, I need more. local trueAnomaly = math.acos((e:dot(r))/(ecc*d)) if r:dot(v) < 0 then trueAnomaly = -(trueAnomaly - 2*math.pi) end -- Apparently... cos(EccentricAnomaly) = (cos(trueAnomaly) + eccentricity)/(1 + eccentricity * cos(trueAnomaly)) local EccentricAnomaly = math.acos((math.cos(trueAnomaly) + ecc)/(1 + ecc * math.cos(trueAnomaly))) -- Then.... apparently if this is below 0, we should add 2pi to it -- I think also if it's below 0, we're past the apoapsis? local timeTau = EccentricAnomaly if timeTau < 0 then timeTau = timeTau + 2*math.pi end -- So... time since periapsis... -- Is apparently easy if you get mean anomly. t = M/n where n is mean motion, = 2*pi/Period local MeanAnomaly = timeTau - ecc * math.sin(timeTau) local TimeSincePeriapsis = MeanAnomaly/(2*math.pi/Period) --system.print(MeanAnomaly .. " - " .. TimeSincePeriapsis .. " - " .. Period .. " - " .. EccentricAnomaly .. " - " .. timeTau .. " - " .. trueAnomaly) -- Mean anom is 0 at periapsis, positive before it... and positive after it. -- I guess this is why I needed to use timeTau and not EccentricAnomaly here local TimeToPeriapsis = Period - TimeSincePeriapsis local TimeToApoapsis = TimeToPeriapsis + Period/2 if trueAnomaly - math.pi > 0 then -- TBH I think something's wrong in my formulas because I needed this. TimeToPeriapsis = TimeSincePeriapsis TimeToApoapsis = TimeToPeriapsis + Period/2 end if TimeToApoapsis > Period then TimeToApoapsis = TimeToApoapsis - Period end return { periapsis = { position = per, speed = trm/pd, circularOrbitSpeed = math.sqrt(mu/pd), altitude = pd - self.body.radius}, apoapsis = apo and { position = apo, speed = trm/ad, circularOrbitSpeed = math.sqrt(mu/ad), altitude = ad - self.body.radius}, currentVelocity = v, currentPosition = pos, eccentricity = ecc, period = Period, eccentricAnomaly = EccentricAnomaly, meanAnomaly = MeanAnomaly, timeToPeriapsis = TimeToPeriapsis, timeToApoapsis = TimeToApoapsis } end local function new(bodyParameters) local params = PlanetRef.BodyParameters(bodyParameters.planetarySystemId, bodyParameters.bodyId, bodyParameters.radius, bodyParameters.center, bodyParameters.GM) return setmetatable({body = params}, Kepler) end return setmetatable(Kepler, { __call = function(_,...) return new(...) end }) end function Kinematics() --[[ DualUniverse kinematic equations Author: JayleBreak Usage (unit.start): Kinematics = require('autoconf.custom.kinematics') Methods: computeAccelerationTime - "relativistic" version of t = (vf - vi)/a computeDistanceAndTime - Return distance & time needed to reach final speed. computeTravelTime - "relativistic" version of t=(sqrt(2ad+v^2)-v)/a Description DualUniverse increases the effective mass of constructs as their absolute speed increases by using the "lorentz" factor (from relativity) as the scale factor. This results in an upper bound on the absolute speed of constructs (excluding "warp" drive) that is set to 30 000 KPH (8 333 MPS). This module provides utilities for computing some physical quantities taking this scaling into account. ]]-- local Kinematic = {} -- just a namespace local C = 30000000/3600 local C2 = C*C local ITERATIONS = 100 -- iterations over engine "warm-up" period local function lorentz(v) return 1/math.sqrt(1 - v*v/C2) end -- -- computeAccelerationTime - "relativistic" version of t = (vf - vi)/a -- initial [in]: initial (positive) speed in meters per second. -- acceleration [in]: constant acceleration until 'finalSpeed' is reached. -- final [in]: the speed at the end of the time interval. -- return: the time in seconds spent in traversing the distance -- function Kinematic.computeAccelerationTime(initial, acceleration, final) -- The low speed limit of following is: t=(vf-vi)/a (from: vf=vi+at) local k1 = C*math.asin(initial/C) return (C * math.asin(final/C) - k1)/acceleration end -- -- computeDistanceAndTime - Return distance & time needed to reach final speed. -- initial[in]: Initial speed in meters per second. -- final[in]: Final speed in meters per second. -- restMass[in]: Mass of the construct at rest in Kg. -- thrust[in]: Engine's maximum thrust in Newtons. -- t50[in]: (default: 0) Time interval to reach 50% thrust in seconds. -- brakeThrust[in]: (default: 0) Constant thrust term when braking. -- return: Distance (in meters), time (in seconds) required for change. -- function Kinematic.computeDistanceAndTime(initial, final, restMass, thrust, t50, brakeThrust) -- This function assumes that the applied thrust is colinear with the -- velocity. Furthermore, it does not take into account the influence -- of gravity, not just in terms of its impact on velocity, but also -- its impact on the orientation of thrust relative to velocity. -- These factors will introduce (usually) small errors which grow as -- the length of the trip increases. t50 = t50 or 0 brakeThrust = brakeThrust or 0 -- usually zero when accelerating local tau0 = lorentz(initial) local speedUp = initial <= final local a0 = thrust * (speedUp and 1 or -1)/restMass local b0 = -brakeThrust/restMass local totA = a0+b0 if speedUp and totA <= 0 or not speedUp and totA >= 0 then return -1, -1 -- no solution end local distanceToMax, timeToMax = 0, 0 -- If, the T50 time is set, then assume engine is at zero thrust and will -- reach full thrust in 2*T50 seconds. Thrust curve is given by: -- Thrust: F(z)=(a0*(1+sin(z))+2*b0)/2 where z=pi*(t/t50 - 1)/2 -- Acceleration is given by F(z)/m(z) where m(z) = m/sqrt(1-v^2/c^2) -- or v(z)' = (a0*(1+sin(z))+2*b0)*sqrt(1-v(z)^2/c^2)/2 if a0 ~= 0 and t50 > 0 then -- Closed form solution for velocity exists: -- v(t) = -c*tan(w)/sqrt(tan(w)^2+1) => w = -asin(v/c) -- w=(pi*t*(a0/2+b0)-a0*t50*sin(pi*t/2/t50)+*pi*c*k1)/pi/c -- @ t=0, v(0) = vi -- pi*c*k1/pi/c = -asin(vi/c) -- k1 = asin(vi/c) local k1 = math.asin(initial/C) local c1 = math.pi*(a0/2+b0) local c2 = a0*t50 local c3 = C*math.pi local v = function(t) local w = (c1*t - c2*math.sin(math.pi*t/2/t50) + c3*k1)/c3 local tan = math.tan(w) return C*tan/math.sqrt(tan*tan+1) end local speedchk = speedUp and function(s) return s >= final end or function(s) return s <= final end timeToMax = 2*t50 if speedchk(v(timeToMax)) then local lasttime = 0 while math.abs(timeToMax - lasttime) > 0.5 do local t = (timeToMax + lasttime)/2 if speedchk(v(t)) then timeToMax = t else lasttime = t end end end -- There is no closed form solution for distance in this case. -- Numerically integrate for time t=0 to t=2*T50 (or less) local lastv = initial local tinc = timeToMax/ITERATIONS for step = 1, ITERATIONS do local speed = v(step*tinc) distanceToMax = distanceToMax + (speed+lastv)*tinc/2 lastv = speed end if timeToMax < 2*t50 then return distanceToMax, timeToMax end initial = lastv end -- At full thrust, acceleration only depends on the Lorentz factor: -- v(t)' = (F/m(v)) = a*sqrt(1-v(t)^2/c^2) where a = a0+b0 -- -> v = c*sin((at+k1)/c) -- @ t=0, v=vi: k1 = c*asin(vi/c) -- -> t = (c*asin(v/c) - k1)/a -- x(t)' = c*sin((at+k1)/c) -- x = k2 - c^2 cos((at+k1)/c)/a -- @ t=0, x=0: k2 = c^2 * cos(k1/c)/a local k1 = C*math.asin(initial/C) local time = (C * math.asin(final/C) - k1)/totA local k2 = C2 *math.cos(k1/C)/totA local distance = k2 - C2 * math.cos((totA*time + k1)/C)/totA return distance+distanceToMax, time+timeToMax end -- -- computeTravelTime - "relativistic" version of t=(sqrt(2ad+v^2)-v)/a -- initialSpeed [in]: initial (positive) speed in meters per second -- acceleration [in]: constant acceleration until 'distance' is traversed -- distance [in]: the distance traveled in meters -- return: the time in seconds spent in traversing the distance -- function Kinematic.computeTravelTime(initial, acceleration, distance) -- The low speed limit of following is: t=(sqrt(2ad+v^2)-v)/a -- (from: d=vt+at^2/2) if distance == 0 then return 0 end if acceleration > 0 then local k1 = C*math.asin(initial/C) local k2 = C2*math.cos(k1/C)/acceleration return (C*math.acos(acceleration*(k2 - distance)/C2) - k1)/acceleration end assert(initial > 0, 'Acceleration and initial speed are both zero.') return distance/initial end function Kinematic.lorentz(v) return lorentz(v) end return Kinematic end PlanetaryReference = PlanetRef() galaxyReference = PlanetaryReference(Atlas()) Kinematic = Kinematics() Kep = Keplers() function getDistanceDisplayString(distance) local su = distance > 100000 local result = "" if su then -- Convert to SU result = round(distance/1000/200,1) .. " SU" else -- Convert to KM result = round(distance/1000,1) .. " KM" end return result end PlanetaryReference = PlanetRef() galaxyReference = PlanetaryReference(Atlas()) MapScreenButtons = {} MapScreenMouseX = 0 MapScreenMouseY = 0 MapScreenMouseDown = false MapScreenButtonSelected = 0 local worldPos = vec3(core.getConstructWorldPos()) local locX = (worldPos.x/400000) local locY = (worldPos.y/400000)*(-1) local destX = 0 local destY = 0 local sudistance = 0 local loc = vec3(core.getConstructWorldPos()) local ion = galaxyReference[0][120] ---uses Atlas functions local thades = vec3(29165536.000, 10865536.000, 65536.000) local sinnen = vec3(58665536.000, 29665536.000, 58165536.000) local alioth = galaxyReference[0][2] ---uses Atlas functions local madis = vec3(17465536.000, 22665536.000, -34464.000) local jago = vec3(-94134464.000, 12765536.000, -3634464.000) local symeon = vec3(14165536.000, -85634464.000, -934464.000) local lacobus = vec3(98865536.000, -13534464.000, -934464.000) local teoma = vec3(80865536.000, 54665536.000, -934464.000) local feli = vec3(-43534464.000, 22565536.000, -48934464.000) local talemai = vec3(-13234464.000, 55765536.000, 465536.000) local sicari = vec3(52765536.000, 27165536.000, 52065536.000) local distion = math.floor(ion:getDistance(loc)/200000) ---uses getDistance functions---- local distthades = string.format("%.2f", math.sqrt((loc.x-thades.x)^2+(loc.y-thades.y)^2+(loc.z-thades.z)^2)/200000) local distalioth = math.floor(alioth:getDistance(loc)/200000) ---uses getDistance functions---- local distmadis = string.format("%.2f", math.sqrt((loc.x-madis.x)^2+(loc.y-madis.y)^2+(loc.z-madis.z)^2)/200000) local distjago = string.format("%.2f", math.sqrt((loc.x-jago.x)^2+(loc.y-jago.y)^2+(loc.z-jago.z)^2)/200000) local distlacobus = string.format("%.2f", math.sqrt((loc.x-lacobus.x)^2+(loc.y-lacobus.y)^2+(loc.z-lacobus.z)^2)/200000) local distteoma = string.format("%.2f", math.sqrt((loc.x-teoma.x)^2+(loc.y-teoma.y)^2+(loc.z-teoma.z)^2)/200000) local distsymeon = string.format("%.2f", math.sqrt((loc.x-symeon.x)^2+(loc.y-symeon.y)^2+(loc.z-symeon.z)^2)/200000) local distfeli = string.format("%.2f", math.sqrt((loc.x-feli.x)^2+(loc.y-feli.y)^2+(loc.z-feli.z)^2)/200000) local distsinnen = string.format("%.2f", math.sqrt((loc.x-sinnen.x)^2+(loc.y-sinnen.y)^2+(loc.z-sinnen.z)^2)/200000) local disttalemai = string.format("%.2f", math.sqrt((loc.x-talemai.x)^2+(loc.y-talemai.y)^2+(loc.z-talemai.z)^2)/200000) local distsicari = string.format("%.2f", math.sqrt((loc.x-sicari.x)^2+(loc.y-sicari.y)^2+(loc.z-sicari.z)^2)/200000) for i = 1,1 do local button = {id = ("b"..1), enabled=true, td="<td>", top=2/100, bottom=13/100, left=1/100, right=28/100} table.insert(MapScreenButtons, button) end for i = 2,2 do local button = {id = ("b"..2), enabled=true, td="<td>", top=15/100, bottom=26/100, left=1/100, right=30/100} table.insert(MapScreenButtons, button) end for i = 3,3 do local button = {id = ("b"..3), enabled=true, td="<td>", top=27/100, bottom=38/100, left=1/100, right=28/100} table.insert(MapScreenButtons, button) end for i = 4,4 do local button = {id = ("b"..4), enabled=true, td="<td>", top=39/100, bottom=50/100, left=1/100, right=28/100} table.insert(MapScreenButtons, button) end for i = 5,5 do local button = {id = ("b"..5), enabled=true, td="<td>", top=51/100, bottom=62/100, left=1/100, right=28/100} table.insert(MapScreenButtons, button) end for i = 6,6 do local button = {id = ("b"..6), enabled=true, td="<td>", top=64/100, bottom=75/100, left=1/100, right=28/100} table.insert(MapScreenButtons, button) end for i = 7,7 do local button = {id = ("b"..7), enabled=true, td="<td>", top=2/100, bottom=13/100, left=75/100, right=100/100} table.insert(MapScreenButtons, button) end for i = 8,8 do local button = {id = ("b"..8), enabled=true, td="<td>", top=15/100, bottom=26/100, left=75/100, right=100/100} table.insert(MapScreenButtons, button) end for i = 9,9 do local button = {id = ("b"..9), enabled=true, td="<td>", top=27/100, bottom=38/100, left=75/100, right=100/100} table.insert(MapScreenButtons, button) end for i = 10,10 do local button = {id = ("b"..10), enabled=true, td="<td>", top=39/100, bottom=50/100, left=75/100, right=100/100} table.insert(MapScreenButtons, button) end for i = 11,11 do local button = {id = ("b"..11), enabled=true, td="<td>", top=51/100, bottom=62/100, left=75/100, right=100/100} table.insert(MapScreenButtons, button) end for i = 12,12 do local button = {id = ("b"..12), enabled=true, td="<td>", top=64/100, bottom=75/100, left=75/100, right=100/100} table.insert(MapScreenButtons, button) end for i = 13,13 do local button = {id = ("b"..13), enabled=true, td="<td>", top=90/100, bottom=100/100, left=1/100, right=18/100} table.insert(MapScreenButtons, button) end function evaluateButtons() local selected = 0 if #MapScreenButtons >= 1 then -- Set button styles for i, button in ipairs(MapScreenButtons) do if button.left < MapScreenMouseX and MapScreenMouseX < button.right and button.top < MapScreenMouseY and MapScreenMouseY < button.bottom then if MapScreenMouseDown and MapScreenButtonSelected == i then end selected = i end if not button.enabled then end end end return selected end function onButtonDown(buttonNo) local button = MapScreenButtons[buttonNo] if not button or not button.enabled then return end end function onButtonUp(buttonNo) local button = MapScreenButtons[buttonNo] if not button or not button.enabled then return end function onClick(buttonNo) local button = MapScreenButtons[buttonNo] if not button or not button.enabled then return end end if buttonNo == 1 then destX = 0 destY = 0 sudistance = distalioth elseif buttonNo == 2 then destX = 43 destY = -56 sudistance = distmadis elseif buttonNo == 3 then destX = 73 destY = -27 sudistance = distthades elseif buttonNo == 4 then destX = -33 destY = -139 sudistance = disttalemai elseif buttonNo == 5 then destX = -109 destY = -56 sudistance = distfeli elseif buttonNo == 6 then destX = 131 destY = -68 sudistance = distsicari elseif buttonNo == 7 then destX = 35 destY = 214 sudistance = distsymeon elseif buttonNo == 8 then destX = 146 destY = -74 sudistance = distsinnen elseif buttonNo == 9 then destX = -235 destY = -32 sudistance = distjago elseif buttonNo == 10 then destX = 202 destY = -137 sudistance = distteoma elseif buttonNo == 11 then destX = 7 destY = 247 sudistance = distion elseif buttonNo == 12 then destX = 247 destY = 34 sudistance = distlacobus elseif buttonNo == 13 then unit.exit() end end function updateScreen() warpmath = math.floor(math.floor(core.getConstructMass()/ 1000) * sudistance * 0.00025) html= ([[ <svg class="bootstrap" viewBox="0 0 1024 1024" style="width:100%; height:100%"><circle cx="500" cy="500" r="400" stroke="darkgreen" stroke-width="3" transform=""></circle><circle cx="500" cy="500" r="350" stroke="darkgreen" stroke-width="3" transform="" stroke-opacity="0.2"></circle><circle cx="500" cy="500" r="300" stroke="darkgreen" stroke-width="3" transform=""></circle><circle cx="500" cy="500" r="250" stroke="darkgreen" stroke-width="3" transform="" stroke-opacity="0.2"></circle><circle cx="500" cy="500" r="200" stroke="darkgreen" stroke-width="3" transform=""></circle><circle cx="500" cy="500" r="150" stroke="darkgreen" stroke-width="3" transform="" stroke-opacity="0.2"></circle><circle cx="500" cy="500" r="100" stroke="lightblue" stroke-width="3" transform=""></circle><circle cx="500" cy="500" r="50" stroke="lightblue" stroke-width="3" transform="" stroke-opacity="0.2"></circle><circle cx="500" cy="500" r="20" stroke="Orange" stroke-width="2" transform=""></circle><text x="510" y="510" fill="Yellow">Helios</text><circle cx="-0.00" cy="0" r="10" stroke="black" stroke-width="1" fill="blue" transform="translate(500,500)"></circle><text x="-0.00" y="0" transform="translate(500,480)" fill="white" font-size="20">Alioth</text><circle cx="7.16" cy="247.59" r="10" stroke="black" stroke-width="1" fill="blue" transform="translate(500,500)"></circle><text x="7.16" y="247.59" transform="translate(480,480)" fill="white" font-size="20">Ion</text><circle cx="35.41" cy="214.09" r="10" stroke="black" stroke-width="1" fill="blue" transform="translate(500,500)"></circle><text x="35.41" y="214.09" transform="translate(500,480)" fill="white" font-size="20">Symeon</text><circle cx="-33.09" cy="-139.41" r="10" stroke="black" stroke-width="1" fill="blue" transform="translate(500,500)"></circle><text x="-33.09" y="-139.41" transform="translate(500,480)" fill="white" font-size="20">Talemai</text><circle cx="202.16" cy="-136.66" r="10" stroke="black" stroke-width="1" fill="blue" transform="translate(500,500)"></circle><text x="202.16" y="-136.66" transform="translate(500,480)" fill="white" font-size="20">Teoma</text><circle cx="247.16" cy="33.84" r="10" stroke="black" stroke-width="1" fill="blue" transform="translate(500,500)"></circle><text x="247.16" y="33.84" transform="translate(500,480)" fill="white" font-size="20">Lacobus</text><circle cx="-108.84" cy="-56.41" r="10" stroke="black" stroke-width="1" fill="blue" transform="translate(500,500)"></circle><text x="-108.84" y="-56.41" transform="translate(500,480)" fill="white" font-size="20">Feli</text><circle cx="72.91" cy="-27.16" r="10" stroke="black" stroke-width="1" fill="blue" transform="translate(500,500)"></circle><text x="72.91" y="-27.16" transform="translate(500,485)" fill="white" font-size="20">Thades</text><circle cx="43.66" cy="-56.66" r="10" stroke="black" stroke-width="1" fill="blue" transform="translate(500,500)"></circle><text x="43.66" y="-56.66" transform="translate(500,480)" fill="white" font-size="20">Madis</text><circle cx="-235.34" cy="-31.91" r="10" stroke="black" stroke-width="1" fill="blue" transform="translate(500,500)"></circle><text x="-235.34" y="-31.91" transform="translate(500,480)" fill="white" font-size="20">Jago</text><circle cx="131.91" cy="-67.91" r="10" stroke="black" stroke-width="1" fill="blue" transform="translate(500,500)"></circle><text x="131.91" y="-67.91" transform="translate(475,480)" fill="white" font-size="20">Sicari</text><circle cx="146.66" cy="-74.16" r="10" stroke="black" stroke-width="1" fill="blue" transform="translate(500,500)"></circle><text x="146.66" y="-74.16" transform="translate(515,480)" fill="white" font-size="20">Sinnen</text> <line stroke-linecap="undefined" stroke-linejoin="undefined" id="svg_1" y2="]]..destY..[[" x2="]]..destX..[[" y1="]]..locY..[[" x1="]]..locX..[[" transform="translate(500,500)" stroke-width="5" stroke="#ff0000" fill="none"/> <circle cx="]]..locX..[[" cy="]]..locY..[[" r="3" stroke="black" stroke-width="1" fill="limegreen" transform="translate(500,500)"></circle> <text x="]]..locX..[[" y="]]..locY..[[" transform="translate(500,500)" fill="limegreen" font-size= "4.5vh" font-weight= "bold">//SHIP POSITION</text> </svg> <svg class="bootstrap" viewBox="0 0 1024 612" style="width:100%; height:100%"> <g> <title>Layer 1</title> <g id="svg_24"> <text stroke="null" transform="matrix(0.7907331239400577,0,0,0.7600725676692406,3.135703637258853,5.731969683147472) " xml:space="preserve" text-anchor="start" font-family="Helvetica, Arial, sans-serif" font-size="20" id="svg_8" y="70" x="55" stroke-width="0" fill="Yellow">Alioth :]]..distalioth..[[ SU</text> <text stroke="null" transform="matrix(0.7907331239400577,0,0,0.7600725676692406,3.135703637258853,5.731969683147472) " xml:space="preserve" text-anchor="start" font-family="Helvetica, Arial, sans-serif" font-size="20" id="svg_14" y="170" x="55" stroke-width="0" fill="Yellow">Madis :]]..distmadis..[[ SU</text> <text stroke="null" transform="matrix(0.7907331239400577,0,0,0.7600725676692406,3.135703637258853,5.731969683147472) " xml:space="preserve" text-anchor="start" font-family="Helvetica, Arial, sans-serif" font-size="20" id="svg_17" y="270" x="55" stroke-width="0" fill="Yellow">Thades :]]..distthades..[[ SU</text> <text stroke="null" transform="matrix(0.7907331239400577,0,0,0.7600725676692406,3.135703637258853,5.731969683147472) " xml:space="preserve" text-anchor="start" font-family="Helvetica, Arial, sans-serif" font-size="20" id="svg_20" y="370" x="55" stroke-width="0" fill="Yellow">Talemai :]]..disttalemai..[[ SU</text> <text stroke="null" transform="matrix(0.7907331239400577,0,0,0.7600725676692406,3.135703637258853,5.731969683147472) " xml:space="preserve" text-anchor="start" font-family="Helvetica, Arial, sans-serif" font-size="20" id="svg_23" y="470" x="55" stroke-width="0" fill="Yellow">Feli :]]..distfeli..[[ SU</text> <text stroke="null" transform="matrix(0.7907331239400577,0,0,0.7600725676692406,3.135703637258853,5.731969683147472) " xml:space="preserve" text-anchor="start" font-family="Helvetica, Arial, sans-serif" font-size="20" id="svg_26" y="570" x="55" stroke-width="0" fill="Yellow">Sicari :]]..distsicari..[[ SU</text> <g id="svg_12"> <rect rx="10" id="svg_1" height="50" width="250" y="30" x="15" stroke-width="20" stroke="#00ff00" fill="none"/> <rect rx="10" id="svg_3" height="50" width="250" y="105" x="15" stroke-width="20" stroke="#00ff00" fill="none"/> <rect rx="10" id="svg_7" height="50" width="250" y="180" x="15" stroke-width="20" stroke="#00ff00" fill="none"/> <rect rx="10" id="svg_9" height="50" width="250" y="255" x="15" stroke-width="20" stroke="#00ff00" fill="none"/> <rect rx="10" id="svg_10" height="50" width="250" y="330" x="15" stroke-width="20" stroke="#00ff00" fill="none"/> <rect rx="10" id="svg_11" height="50" width="250" y="405" x="15" stroke-width="20" stroke="#00ff00" fill="none"/> </g> </g> <g id="svg_40"> <text stroke="null" transform="matrix(0.7907331239400577,0,0,0.7600725676692406,3.135703637258853,5.731969683147472) " xml:space="preserve" text-anchor="start" font-family="Helvetica, Arial, sans-serif" font-size="20" id="svg_25" y="70" x="997.163642" stroke-width="0" fill="Yellow">Symeon :]]..distsymeon..[[ SU</text> <text stroke="null" transform="matrix(0.7907331239400577,0,0,0.7600725676692406,3.135703637258853,5.731969683147472) " xml:space="preserve" text-anchor="start" font-family="Helvetica, Arial, sans-serif" font-size="20" id="svg_27" y="170" x="997.163642" stroke-width="0" fill="Yellow">Sinnen :]]..distsinnen..[[ SU</text> <text stroke="null" transform="matrix(0.7907331239400577,0,0,0.7600725676692406,3.135703637258853,5.731969683147472) " xml:space="preserve" text-anchor="start" font-family="Helvetica, Arial, sans-serif" font-size="20" id="svg_28" y="270" x="997.163642" stroke-width="0" fill="Yellow">Jago :]]..distjago..[[ SU</text> <text stroke="null" transform="matrix(0.7907331239400577,0,0,0.7600725676692406,3.135703637258853,5.731969683147472) " xml:space="preserve" text-anchor="start" font-family="Helvetica, Arial, sans-serif" font-size="20" id="svg_30" y="370" x="997.163642" stroke-width="0" fill="Yellow">Teoma :]]..distteoma..[[ SU</text> <text stroke="null" transform="matrix(0.7907331239400577,0,0,0.7600725676692406,3.135703637258853,5.731969683147472) " xml:space="preserve" text-anchor="start" font-family="Helvetica, Arial, sans-serif" font-size="20" id="svg_31" y="470" x="997.163642" stroke-width="0" fill="Yellow">Ion :]]..distion..[[ SU</text> <text stroke="null" transform="matrix(0.7907331239400577,0,0,0.7600725676692406,3.135703637258853,5.731969683147472) " xml:space="preserve" text-anchor="start" font-family="Helvetica, Arial, sans-serif" font-size="20" id="svg_32" y="570" x="997.163642" stroke-width="0" fill="Yellow">Lacobus :]]..distlacobus..[[ SU</text> <g id="svg_39"> <rect rx="10" id="svg_33" height="50" width="250" y="30" x="760" stroke-width="20" stroke="#00ff00" fill="none"/> <rect rx="10" id="svg_34" height="50" width="250" y="105" x="760" stroke-width="20" stroke="#00ff00" fill="none"/> <rect rx="10" id="svg_35" height="50" width="250" y="180" x="760" stroke-width="20" stroke="#00ff00" fill="none"/> <rect rx="10" id="svg_36" height="50" width="250" y="255" x="760" stroke-width="20" stroke="#00ff00" fill="none"/> <rect rx="10" id="svg_37" height="50" width="250" y="330" x="760" stroke-width="20" stroke="#00ff00" fill="none"/> <rect rx="10" id="svg_38" height="50" width="250" y="405" x="760" stroke-width="20" stroke="#00ff00" fill="none"/> </g> </g> </g> <text stroke="null" transform="matrix(0.7907331239400577,0,0,0.7600725676692406,3.135703637258853,5.731969683147472) " xml:space="preserve" text-anchor="start" font-family="Helvetica, Arial, sans-serif" font-size="30" id="svg_32" y="700" x="20" stroke-width="0" fill="LightBlue">Est. Warp Cost: ]]..warpmath..[[</text> <text stroke="null" transform="matrix(0.7907331239400577,0,0,0.7600725676692406,3.135703637258853,5.731969683147472) " xml:space="preserve" text-anchor="start" font-family="Helvetica, Arial, sans-serif" font-size="30" id="svg_32" y="750" x="20" stroke-width="0" fill="LightBlue">Construct Weight: ]]..math.floor(core.getConstructMass()/ 1000)..[[ tons</text> </svg> ]]) screen.setHTML(html) end unit.setTimer("spacemap",.08)  
    Json Paste
    {"slots":{"0":{"name":"screen","type":{"events":[],"methods":[]}},"1":{"name":"core","type":{"events":[],"methods":[]}},"2":{"name":"slot3","type":{"events":[],"methods":[]}},"3":{"name":"slot4","type":{"events":[],"methods":[]}},"4":{"name":"slot5","type":{"events":[],"methods":[]}},"5":{"name":"slot6","type":{"events":[],"methods":[]}},"6":{"name":"slot7","type":{"events":[],"methods":[]}},"7":{"name":"slot8","type":{"events":[],"methods":[]}},"8":{"name":"slot9","type":{"events":[],"methods":[]}},"9":{"name":"slot10","type":{"events":[],"methods":[]}},"-1":{"name":"unit","type":{"events":[],"methods":[]}},"-2":{"name":"system","type":{"events":[],"methods":[]}},"-3":{"name":"library","type":{"events":[],"methods":[]}}},"handlers":[{"code":"MapScreenMouseX = x\nMapScreenMouseY = y\nMapScreenMouseDown = false\nlocal buttonNo = evaluateButtons()\nif MapScreenButtonSelected > 0 and MapScreenButtonSelected == buttonNo then\n onButtonUp(buttonNo)\n onClick(buttonNo)\nend\nMapScreenButtonSelected = -buttonNo","filter":{"args":[{"variable":"*"},{"variable":"*"}],"signature":"mouseUp(x,y)","slotKey":"0"},"key":"0"},{"code":"MapScreenMouseX = x\nMapScreenMouseY = y\nMapScreenMouseDown = true\nMapScreenButtonSelected = evaluateButtons()\nonButtonDown(MapScreenButtonSelected)\n","filter":{"args":[{"variable":"*"},{"variable":"*"}],"signature":"mouseDown(x,y)","slotKey":"0"},"key":"1"},{"code":"function Atlas()\n return {\n [0] = {\n [1]={\n GM=6930729684,\n bodyId=1,\n center={x=17465536.000,y=22665536.000,z=-34464.000},\n name='Madis',\n planetarySystemId=0,\n radius=44300\n },\n [2]={\n GM=157470826617,\n bodyId=2,\n center={x=-8.000,y=-8.000,z=-126303.000},\n name='Alioth',\n planetarySystemId=0,\n radius=126068\n },\n [3]={\n GM=11776905000,\n bodyId=3,\n center={x=29165536.000,y=10865536.000,z=65536.000},\n name='Thades',\n planetarySystemId=0,\n radius=49000\n },\n [4]={\n GM=14893847582,\n bodyId=4,\n center={x=-13234464.000,y=55765536.000,z=465536.000},\n name='Talemai',\n planetarySystemId=0,\n radius=57450\n },\n [5]={\n GM=16951680000,\n bodyId=5,\n center={x=-43534464.000,y=22565536.000,z=-48934464.000},\n name='Feli',\n planetarySystemId=0,\n radius=60000\n },\n [6]={\n GM=10502547741,\n bodyId=6,\n center={x=52765536.000,y=27165538.000,z=52065535.000},\n name='Sicari',\n planetarySystemId=0,\n radius=51100\n },\n [7]={\n GM=13033380591,\n bodyId=7,\n center={x=58665538.000,y=29665535.000,z=58165535.000},\n name='Sinnen',\n planetarySystemId=0,\n radius=54950\n },\n [8]={\n GM=18477723600,\n bodyId=8,\n center={x=80865538.000,y=54665536.000,z=-934463.940},\n name='Teoma',\n planetarySystemId=0,\n radius=62000\n },\n [9]={\n GM=18606274330,\n bodyId=9,\n center={x=-94134462.000,y=12765534.000,z=-3634464.000},\n name='Jago',\n planetarySystemId=0,\n radius=61590\n },\n [10]={\n GM=78480000,\n bodyId=10,\n center={x=17448118.224,y=22966846.286,z=143078.820},\n name='Madis Moon 1',\n planetarySystemId=0,\n radius=10000\n },\n [11]={\n GM=237402000,\n bodyId=11,\n center={x=17194626.000,y=22243633.880,z=-214962.810},\n name='Madis Moon 2',\n planetarySystemId=0,\n radius=11000\n },\n [12]={\n GM=265046609,\n bodyId=12,\n center={x=17520614.000,y=22184730.000,z=-309989.990},\n name='Madis Moon 3',\n planetarySystemId=0,\n radius=15005\n },\n [21]={\n GM=2118960000,\n bodyId=21,\n center={x=457933.000,y=-1509011.000,z=115524.000},\n name='Alioth Moon 1',\n planetarySystemId=0,\n radius=30000\n },\n [22]={\n GM=2165833514,\n bodyId=22,\n center={x=-1692694.000,y=729681.000,z=-411464.000},\n name='Alioth Moon 4',\n planetarySystemId=0,\n radius=30330\n },\n [26]={\n GM=68234043600,\n bodyId=26,\n center={x=-1404835.000,y=562655.000,z=-285074.000},\n name='Sanctuary',\n planetarySystemId=0,\n radius=83400\n },\n [30]={\n GM=211564034,\n bodyId=30,\n center={x=29214402.000,y=10907080.695,z=433858.200},\n name='Thades Moon 1',\n planetarySystemId=0,\n radius=14002\n },\n [31]={\n GM=264870000,\n bodyId=31,\n center={x=29404193.000,y=10432768.000,z=19554.131},\n name='Thades Moon 2',\n planetarySystemId=0,\n radius=15000\n },\n [40]={\n GM=141264000,\n bodyId=40,\n center={x=-13503090.000,y=55594325.000,z=769838.640},\n name='Talemai Moon 2',\n planetarySystemId=0,\n radius=12000\n },\n [41]={\n GM=106830900,\n bodyId=41,\n center={x=-12800515.000,y=55700259.000,z=325207.840},\n name='Talemai Moon 3',\n planetarySystemId=0,\n radius=11000\n },\n [42]={\n GM=264870000,\n bodyId=42,\n center={x=-13058408.000,y=55781856.000,z=740177.760},\n name='Talemai Moon 1',\n planetarySystemId=0,\n radius=15000\n },\n [50]={\n GM=499917600,\n bodyId=50,\n center={x=-43902841.780,y=22261034.700,z=-48862386.000},\n name='Feli Moon 1',\n planetarySystemId=0,\n radius=14000\n },\n [70]={\n GM=396912600,\n bodyId=70,\n center={x=58969616.000,y=29797945.000,z=57969449.000},\n name='Sinnen Moon 1',\n planetarySystemId=0,\n radius=17000\n },\n [100]={\n GM=13975172474,\n bodyId=100,\n center={x=98865536.000,y=-13534464.000,z=-934461.990},\n name='Lacobus',\n planetarySystemId=0,\n radius=55650\n },\n [101]={\n GM=264870000,\n bodyId=101,\n center={x=98905288.170,y=-13950921.100,z=-647589.530},\n name='Lacobus Moon 3',\n planetarySystemId=0,\n radius=15000\n },\n [102]={\n GM=444981600,\n bodyId=102,\n center={x=99180968.000,y=-13783862.000,z=-926156.400},\n name='Lacobus Moon 1',\n planetarySystemId=0,\n radius=18000\n },\n [103]={\n GM=211503600,\n bodyId=103,\n center={x=99250052.000,y=-13629215.000,z=-1059341.400},\n name='Lacobus Moon 2',\n planetarySystemId=0,\n radius=14000\n },\n [110]={\n GM=9204742375,\n bodyId=110,\n center={x=14165536.000,y=-85634465.000,z=-934464.300},\n name='Symeon',\n planetarySystemId=0,\n radius=49050\n },\n [120]={\n GM=7135606629,\n bodyId=120,\n center={x=2865536.700,y=-99034464.000,z=-934462.020},\n name='Ion',\n planetarySystemId=0,\n radius=44950\n },\n [121]={\n GM=106830900,\n bodyId=121,\n center={x=2472916.800,y=-99133747.000,z=-1133582.800},\n name='Ion Moon 1',\n planetarySystemId=0,\n radius=11000\n },\n [122]={\n GM=176580000,\n bodyId=122,\n center={x=2995424.500,y=-99275010.000,z=-1378480.700},\n name='Ion Moon 2',\n planetarySystemId=0,\n radius=15000\n } \n }\n }\n end\nfunction PlanetRef() \n--[[ \n Provide coordinate transforms and access to kinematic related parameters\n Author: JayleBreak\n Usage (unit.start):\n PlanetaryReference = require('planetref')\n galaxyReference = PlanetaryReference(referenceTableSource)\n helios = galaxyReference[0] -- PlanetaryReference.PlanetarySystem instance\n alioth = helios[2] -- PlanetaryReference.BodyParameters instance\n Methods:\n PlanetaryReference:getPlanetarySystem - based on planetary system ID.\n PlanetaryReference.isMapPosition - 'true' if an instance of 'MapPosition'\n PlanetaryReference.createBodyParameters - for entry into reference table\n PlanetaryReference.BodyParameters - a class containing a body's information.\n PlanetaryReference.MapPosition - a class for map coordinates\n PlanetaryReference.PlanetarySystem - a container for planetary system info.\n PlanetarySystem:castIntersections - from a position in a given direction.\n PlanetarySystem:closestBody - to the specified coordinates.\n PlanetarySystem:convertToBodyIdAndWorldCoordinates - from map coordinates.\n PlanetarySystem:getBodyParameters - from reference table.\n PlanetarySystem:getPlanetarySystemId - for the instance.\n BodyParameters:convertToWorldCoordinates - from map coordinates\n BodyParameters:convertToMapPosition - from world coordinates\n BodyParameters:getAltitude - of world coordinates\n BodyParameters:getDistance - from center to world coordinates\n BodyParameters:getGravity - at a given position in world coordinates.\n Description\n An instance of the 'PlanetaryReference' \"class\" can contain transform and\n kinematic reference information for all planetary systems in DualUniverse.\n Each planetary system is identified by a numeric identifier. Currently,\n the only planetary system, Helios, has the identifier: zero. This \"class\"\n supports the indexing ('[]') operation which is equivalent to the\n use of the 'getPlanetarySystem' method. It also supports the 'pairs()'\n method for iterating over planetary systems.\n\n An instance of the 'PlanetarySystem' \"class\" contains all reference\n information for a specific system. It supports the indexing ('[]') and\n 'pairs()' functions which allows iteration over each \"body\" in the\n system where the key is the numeric body ID. It also supports the\n 'tostring()' method.\n An instance of the 'BodyParameters' \"class\" contains all reference\n information for a single celestial \"body\" (a moon or planet). It supports\n the 'tostring()' method, and contains the data members:\n planetarySystemId - numeric planetary system ID\n bodyId - numeric body ID\n radius - radius of the body in meters (zero altitude)\n center - world coordinates of the body's center position\n GM - the gravitation parameter (g = GM/radius^2)\n Note that the user is allowed to add custom fields (e.g. body name), but\n should insure that complex table values have the '__tostring' metamethod\n implemented.\n Transform and Kinematics:\n \"World\" coordinates is a cartesian coordinate system with an origin at an\n arbitrary fixed point in a planetary system and with distances measured in\n meters. The coordinates are expressible either as a simple table of 3 values\n or an instance of the 'vec3' class. In either case, the planetary system\n identity is implicit.\n \"Map\" coordinates is a geographic coordinate system with an origin at the\n center of an identified (by a numeric value) celestial body which is a\n member of an identified (also a numeric value) planetary system. Note that\n the convention that latitude, longitude, and altitude values will be the\n position's x, y, and z world coordinates in the special case of body ID 0.\n The kinematic parameters in the reference data permit calculations of the\n gravitational attraction of the celestial body on other objects.\n Reference Data:\n This is an example of reference data with a single entry assigned to\n planetary system ID 0, and body ID 2 ('Alioth'):\n referenceTable = {\n [0] = { [2] = { planetarySystemId = 0,\n bodyId = 2,\n radius = 126068,\n center = vec3({x=-8, y=-8, z=-126303}),\n GM = 1.572199+11 } -- as in F=-GMm/r^2\n }\n }\n ref=PlanetaryReference(referenceTable)\n Collecting Reference Data:\n A combination of information from the \"Map\" screen in the DU user interface,\n and values reported by the DU Lua API can be the source of the reference\n table's data (planetarySystemId, bodyId, and surfaceArea is from the user\n interface):\n referenceTable = {}\n referenceTable[planetarySystemId][bodyId] =\n PlanetaryReference.createBodyParameters(planetarySystemId,\n bodyId,\n surfaceArea,\n core.getConstructWorldPos(),\n core.getWorldVertical(),\n core.getAltitude(),\n core.g())\n Adapting Data Sources:\n Other sources of data can be adapted or converted. An example of adapting a\n table, defined in the file: 'planets.lua', containing information on a single\n planetary system and using celestial body name as the key follows (note that\n a 'name' field is added to the BodyParameters instance transparently after\n construction, and the '__pairs' meta function is required to support the\n 'closestBody' and '__tostring' methods):\n ref=PlanetaryReference(\n {[0] = setmetatable(require('planets'),\n { __index = function(bodies, bodyId)\n for _,v in pairs(bodies) do\n if v and v.bodyId == bodyId then return v end\n end\n return nil\n end,\n __pairs = function(bodies)\n return function(t, k)\n local nk, nv = next(t, k)\n if nv then\n local GM = nv.gravity * nv.radius^2\n local bp = BodyParameters(0,\n nv.id,\n nv.radius,\n nv.pos,\n GM)\n bp.name = nk\n return nk, bp\n end\n return nk, nv\n end, bodies, nil\n end })\n })\n\n Converting Data Sources:\n An instance of 'PlanetaryReference' that has been adapted to a data source\n can be used to convert that source to simple table. For example,\n using the adapted instance shown above:\n load('convertedData=' .. tostring(ref))()\n newRef=PlanetaryReference(convertedData)\n Also See: kepler.lua\n ]]--\n--[[ START OF LOCAL IMPLEMENTATION DETAILS ]]--\n-- Type checks\nlocal function isNumber(n) return type(n) == 'number' end\nlocal function isSNumber(n) return type(tonumber(n)) == 'number' end\nlocal function isTable(t) return type(t) == 'table' end\nlocal function isString(s) return type(s) == 'string' end\nlocal function isVector(v) return isTable(v)\n and isNumber(v.x and v.y and v.z) end\nlocal function isMapPosition(m) return isTable(m) and isNumber(m.latitude and\n m.longitude and\n m.altitude and\n m.bodyId and\n m.systemId) end\n-- Constants\nlocal deg2rad = math.pi/180\nlocal rad2deg = 180/math.pi\nlocal epsilon = 1e-10\nlocal num = ' *([+-]?%d+%.?%d*e?[+-]?%d*)'\nlocal posPattern = '::pos{' .. num .. ',' .. num .. ',' .. num .. ',' ..\n num .. ',' .. num .. '}'\n-- Utilities\nlocal utils = require('cpml.utils')\nlocal vec3 = require('cpml.vec3')\nlocal clamp = utils.clamp\nlocal function float_eq(a,b)\n if a == 0 then return math.abs(b) < 1e-09 end\n if b == 0 then return math.abs(a) < 1e-09 end\n return math.abs(a - b) < math.max(math.abs(a),math.abs(b))*epsilon\nend\nlocal function formatNumber(n)\n local result = string.gsub(\n string.reverse(string.format('%.4f',n)),\n '^0*%.?','')\n return result == '' and '0' or string.reverse(result)\nend\nlocal function formatValue(obj)\n if isVector(obj) then\n return string.format('{x=%.3f,y=%.3f,z=%.3f}', obj.x, obj.y, obj.z)\n end\n if isTable(obj) and not getmetatable(obj) then\n local list = {}\n local nxt = next(obj)\n if type(nxt) == 'nil' or nxt == 1 then -- assume this is an array\n list = obj\n else\n for k,v in pairs(obj) do\n local value = formatValue(v)\n if type(k) == 'number' then\n table.insert(list, string.format('[%s]=%s', k, value))\n else\n table.insert(list, string.format('%s=%s', k, value))\n end\n end\n end\n return string.format('{%s}', table.concat(list, ','))\n end\n if isString(obj) then\n return string.format(\"'%s'\", obj:gsub(\"'\",[[\\']]))\n end\n return tostring(obj)\nend\n-- CLASSES\n-- BodyParameters: Attributes of planetary bodies (planets and moons)\nlocal BodyParameters = {}\nBodyParameters.__index = BodyParameters\nBodyParameters.__tostring =\n function(obj, indent)\n local sep = indent or ''\n local keys = {}\n for k in pairs(obj) do table.insert(keys, k) end\n table.sort(keys)\n local list = {}\n for _, k in ipairs(keys) do\n local value = formatValue(obj[k])\n if type(k) == 'number' then\n table.insert(list, string.format('[%s]=%s', k, value))\n else\n table.insert(list, string.format('%s=%s', k, value))\n end\n end\n if indent then\n return string.format('%s%s',\n indent,\n table.concat(list, ',\\n' .. indent))\n end\n return string.format('{%s}', table.concat(list, ','))\n end\nBodyParameters.__eq = function(lhs, rhs)\n return lhs.planetarySystemId == rhs.planetarySystemId and\n lhs.bodyId == rhs.bodyId and\n float_eq(lhs.radius, rhs.radius) and\n float_eq(lhs.center.x, rhs.center.x) and\n float_eq(lhs.center.y, rhs.center.y) and\n float_eq(lhs.center.z, rhs.center.z) and\n float_eq(lhs.GM, rhs.GM)\n end\nlocal function mkBodyParameters(systemId, bodyId, radius, worldCoordinates, GM)\n -- 'worldCoordinates' can be either table or vec3\n assert(isSNumber(systemId),\n 'Argument 1 (planetarySystemId) must be a number:' .. type(systemId))\n assert(isSNumber(bodyId),\n 'Argument 2 (bodyId) must be a number:' .. type(bodyId))\n assert(isSNumber(radius),\n 'Argument 3 (radius) must be a number:' .. type(radius))\n assert(isTable(worldCoordinates),\n 'Argument 4 (worldCoordinates) must be a array or vec3.' ..\n type(worldCoordinates))\n assert(isSNumber(GM),\n 'Argument 5 (GM) must be a number:' .. type(GM))\n return setmetatable({planetarySystemId = tonumber(systemId),\n bodyId = tonumber(bodyId),\n radius = tonumber(radius),\n center = vec3(worldCoordinates),\n GM = tonumber(GM) }, BodyParameters)\nend\n-- MapPosition: Geographical coordinates of a point on a planetary body.\nlocal MapPosition = {}\nMapPosition.__index = MapPosition\nMapPosition.__tostring = function(p)\n return string.format('::pos{%d,%d,%s,%s,%s}',\n p.systemId,\n p.bodyId,\n formatNumber(p.latitude*rad2deg),\n formatNumber(p.longitude*rad2deg),\n formatNumber(p.altitude))\n end\nMapPosition.__eq = function(lhs, rhs)\n return lhs.bodyId == rhs.bodyId and\n lhs.systemId == rhs.systemId and\n float_eq(lhs.latitude, rhs.latitude) and\n float_eq(lhs.altitude, rhs.altitude) and\n (float_eq(lhs.longitude, rhs.longitude) or\n float_eq(lhs.latitude, math.pi/2) or\n float_eq(lhs.latitude, -math.pi/2))\n end\n-- latitude and longitude are in degrees while altitude is in meters\nlocal function mkMapPosition(overload, bodyId, latitude, longitude, altitude)\n local systemId = overload -- Id or '::pos{...}' string\n if isString(overload) and not longitude and not altitude and\n not bodyId and not latitude then\n systemId, bodyId, latitude, longitude, altitude =\n string.match(overload, posPattern)\n assert(systemId, 'Argument 1 (position string) is malformed.')\n else\n assert(isSNumber(systemId),\n 'Argument 1 (systemId) must be a number:' .. type(systemId))\n assert(isSNumber(bodyId),\n 'Argument 2 (bodyId) must be a number:' .. type(bodyId))\n assert(isSNumber(latitude),\n 'Argument 3 (latitude) must be in degrees:' .. type(latitude))\n assert(isSNumber(longitude),\n 'Argument 4 (longitude) must be in degrees:' .. type(longitude))\n assert(isSNumber(altitude),\n 'Argument 5 (altitude) must be in meters:' .. type(altitude))\n end\n systemId = tonumber(systemId)\n bodyId = tonumber(bodyId)\n latitude = tonumber(latitude)\n longitude = tonumber(longitude)\n altitude = tonumber(altitude)\n if bodyId == 0 then -- this is a hack to represent points in space\n return setmetatable({latitude = latitude,\n longitude = longitude,\n altitude = altitude,\n bodyId = bodyId,\n systemId = systemId}, MapPosition)\n end\n return setmetatable({latitude = deg2rad*clamp(latitude, -90, 90),\n longitude = deg2rad*(longitude % 360),\n altitude = altitude,\n bodyId = bodyId,\n systemId = systemId}, MapPosition)\nend\n-- PlanetarySystem - map body IDs to BodyParameters\nlocal PlanetarySystem = {}\nPlanetarySystem.__index = PlanetarySystem\nPlanetarySystem.__tostring =\n function (obj, indent)\n local sep = indent and (indent .. ' ' )\n local bdylist = {}\n local keys = {}\n for k in pairs(obj) do table.insert(keys, k) end\n table.sort(keys)\n for _, bi in ipairs(keys) do\n bdy = obj[bi]\n local bdys = BodyParameters.__tostring(bdy, sep)\n if indent then\n table.insert(bdylist,\n string.format('[%s]={\\n%s\\n%s}',\n bi, bdys, indent))\n else\n table.insert(bdylist, string.format(' [%s]=%s', bi, bdys))\n end\n end\n if indent then\n return string.format('\\n%s%s%s',\n indent,\n table.concat(bdylist, ',\\n' .. indent),\n indent)\n end\n return string.format('{\\n%s\\n}', table.concat(bdylist, ',\\n'))\n end\nlocal function mkPlanetarySystem(referenceTable)\n local atlas = {}\n local pid\n for _, v in pairs(referenceTable) do\n local id = v.planetarySystemId\n if type(id) ~= 'number' then\n error('Invalid planetary system ID: ' .. tostring(id))\n elseif pid and id ~= pid then\n error('Mismatch planetary system IDs: ' .. id .. ' and '\n .. pid)\n end\n local bid = v.bodyId\n if type(bid) ~= 'number' then\n error('Invalid body ID: ' .. tostring(bid))\n elseif atlas[bid] then\n error('Duplicate body ID: ' .. tostring(bid))\n end\n setmetatable(v.center, getmetatable(vec3.unit_x))\n atlas[bid] = setmetatable(v, BodyParameters)\n pid = id\n end\n return setmetatable(atlas, PlanetarySystem)\nend\n-- PlanetaryReference - map planetary system ID to PlanetarySystem\nPlanetaryReference = {}\nlocal function mkPlanetaryReference(referenceTable)\n return setmetatable({ galaxyAtlas = referenceTable or {} },\n PlanetaryReference)\nend\nPlanetaryReference.__index = \n function(t,i)\n if type(i) == 'number' then\n local system = t.galaxyAtlas[i]\n return mkPlanetarySystem(system)\n end\n return rawget(PlanetaryReference, i)\n end\nPlanetaryReference.__pairs =\n function(obj)\n return function(t, k)\n local nk, nv = next(t, k)\n return nk, nv and mkPlanetarySystem(nv)\n end, obj.galaxyAtlas, nil\n end\nPlanetaryReference.__tostring =\n function (obj)\n local pslist = {}\n for _,ps in pairs(obj or {}) do\n local psi = ps:getPlanetarySystemId()\n local pss = PlanetarySystem.__tostring(ps, ' ')\n table.insert(pslist,\n string.format(' [%s]={%s\\n }', psi, pss))\n end\n return string.format('{\\n%s\\n}\\n', table.concat(pslist,',\\n'))\n end\n--[[ START OF PUBLIC INTERFACE ]]--\n-- PlanetaryReference CLASS METHODS:\n--\n-- BodyParameters - create an instance of BodyParameters class\n-- planetarySystemId [in]: the body's planetary system ID.\n-- bodyId [in]: the body's ID.\n-- radius [in]: the radius in meters of the planetary body.\n-- bodyCenter [in]: the world coordinates of the center (vec3 or table).\n-- GM [in]: the body's standard gravitational parameter.\n-- return: an instance of BodyParameters class.\n--\nPlanetaryReference.BodyParameters = mkBodyParameters\n--\n-- MapPosition - create an instance of the MapPosition class\n-- overload [in]: either a planetary system ID or a position string ('::pos...')\n-- bodyId [in]: (ignored if overload is a position string) the body's ID.\n-- latitude [in]: (ignored if overload is a position string) the latitude.\n-- longitude [in]:(ignored if overload is a position string) the longitude.\n-- altitude [in]: (ignored if overload is a position string) the altitude.\n-- return: the class instance\n--\nPlanetaryReference.MapPosition = mkMapPosition\n--\n-- PlanetarySystem - create an instance of PlanetarySystem class\n-- referenceData [in]: a table (indexed by bodyId) of body reference info.\n-- return: the class instance\n--\nPlanetaryReference.PlanetarySystem = mkPlanetarySystem\n--\n-- createBodyParameters - create an instance of BodyParameters class\n-- planetarySystemId [in]: the body's planetary system ID.\n-- bodyId [in]: the body's ID.\n-- surfaceArea [in]: the body's surface area in square meters.\n-- aPosition [in]: world coordinates of a position near the body.\n-- verticalAtPosition [in]: a vector pointing towards the body center.\n-- altitudeAtPosition [in]: the altitude in meters at the position.\n-- gravityAtPosition [in]: the magnitude of the gravitational acceleration.\n-- return: an instance of BodyParameters class.\n--\nfunction PlanetaryReference.createBodyParameters(planetarySystemId,\n bodyId,\n surfaceArea,\n aPosition,\n verticalAtPosition,\n altitudeAtPosition,\n gravityAtPosition)\n assert(isSNumber(planetarySystemId),\n 'Argument 1 (planetarySystemId) must be a number:' ..\n type(planetarySystemId))\n assert(isSNumber(bodyId),\n 'Argument 2 (bodyId) must be a number:' .. type(bodyId))\n assert(isSNumber(surfaceArea),\n 'Argument 3 (surfaceArea) must be a number:' .. type(surfaceArea))\n assert(isTable(aPosition),\n 'Argument 4 (aPosition) must be an array or vec3:' ..\n type(aPosition))\n assert(isTable(verticalAtPosition),\n 'Argument 5 (verticalAtPosition) must be an array or vec3:' ..\n type(verticalAtPosition))\n assert(isSNumber(altitudeAtPosition),\n 'Argument 6 (altitude) must be in meters:' ..\n type(altitudeAtPosition))\n assert(isSNumber(gravityAtPosition),\n 'Argument 7 (gravityAtPosition) must be number:' ..\n type(gravityAtPosition))\n local radius = math.sqrt(surfaceArea/4/math.pi)\n local distance = radius + altitudeAtPosition\n local center = vec3(aPosition) + distance*vec3(verticalAtPosition)\n local GM = gravityAtPosition * distance * distance\n return mkBodyParameters(planetarySystemId, bodyId, radius, center, GM)\nend\n--\n-- isMapPosition - check for the presence of the 'MapPosition' fields\n-- valueToTest [in]: the value to be checked\n-- return: 'true' if all required fields are present in the input value\n--\nPlanetaryReference.isMapPosition = isMapPosition\n-- PlanetaryReference INSTANCE METHODS:\n--\n-- getPlanetarySystem - get the planetary system using ID or MapPosition as key\n-- overload [in]: either the planetary system ID or a MapPosition that has it.\n-- return: instance of 'PlanetarySystem' class or nil on error\n--\nfunction PlanetaryReference:getPlanetarySystem(overload)\n --if galaxyAtlas then\n local planetarySystemId = overload\n if isMapPosition(overload) then\n planetarySystemId = overload.systemId\n end\n if type(planetarySystemId) == 'number' then\n local system = self.galaxyAtlas[i]\n if system then\n if getmetatable(nv) ~= PlanetarySystem then\n system = mkPlanetarySystem(system)\n end\n return system\n end\n end\n --end\n --return nil\nend\n-- PlanetarySystem INSTANCE METHODS:\n--\n-- castIntersections - Find the closest body that intersects a \"ray cast\".\n-- origin [in]: the origin of the \"ray cast\" in world coordinates\n-- direction [in]: the direction of the \"ray cast\" as a 'vec3' instance.\n-- sizeCalculator [in]: (default: returns 1.05*radius) Returns size given body.\n-- bodyIds[in]: (default: all IDs in system) check only the given IDs.\n-- return: The closest body that blocks the cast or 'nil' if none.\n--\nfunction PlanetarySystem:castIntersections(origin,\n direction,\n sizeCalculator,\n bodyIds)\n local sizeCalculator = sizeCalculator or \n function (body) return 1.05*body.radius end\n local candidates = {}\n if bodyIds then\n for _,i in ipairs(bodyIds) do candidates[i] = self[i] end\n else\n bodyIds = {}\n for k,body in pairs(self) do\n table.insert(bodyIds, k)\n candidates[k] = body\n end\n end\n local function compare(b1,b2)\n local v1 = candidates[b1].center - origin\n local v2 = candidates[b2].center - origin\n return v1:len() < v2:len()\n end\n table.sort(bodyIds, compare)\n local dir = direction:normalize()\n for i, id in ipairs(bodyIds) do\n local body = candidates[id]\n local c_oV3 = body.center - origin\n local radius = sizeCalculator(body)\n local dot = c_oV3:dot(dir)\n local desc = dot^2 - (c_oV3:len2() - radius^2)\n if desc >= 0 then\n local root = math.sqrt(desc)\n local farSide = dot + root\n local nearSide = dot - root\n if nearSide > 0 then\n return body, farSide, nearSide\n elseif farSide > 0 then\n return body, farSide, nil\n end\n end\n end\n return nil, nil, nil\nend\n--\n-- closestBody - find the closest body to a given set of world coordinates\n-- coordinates [in]: the world coordinates of position in space\n-- return: an instance of the BodyParameters object closest to 'coordinates'\n--\nfunction PlanetarySystem:closestBody(coordinates)\n assert(type(coordinates) == 'table', 'Invalid coordinates.')\n local minDistance2, body\n local coord = vec3(coordinates)\n for _,params in pairs(self) do\n local distance2 = (params.center - coord):len2()\n if not body or distance2 < minDistance2 then\n body = params\n minDistance2 = distance2\n end\n end\n return body\nend\n--\n-- convertToBodyIdAndWorldCoordinates - map to body Id and world coordinates\n-- overload [in]: an instance of MapPosition or a position string ('::pos...)\n-- return: a vec3 instance containing the world coordinates or 'nil' on error.\n--\nfunction PlanetarySystem:convertToBodyIdAndWorldCoordinates(overload)\n local mapPosition = overload\n if isString(overload) then\n mapPosition = mkMapPosition(overload)\n end\n if mapPosition.bodyId == 0 then\n return 0, vec3(mapPosition.latitude,\n mapPosition.longitude,\n mapPosition.altitude)\n end\n local params = self:getBodyParameters(mapPosition)\n if params then\n return mapPosition.bodyId,\n params:convertToWorldCoordinates(mapPosition)\n end\nend\n--\n-- getBodyParameters - get or create an instance of BodyParameters class\n-- overload [in]: either an instance of MapPosition or a body's ID.\n-- return: a BodyParameters instance or 'nil' if body ID is not found.\n--\nfunction PlanetarySystem:getBodyParameters(overload)\n local bodyId = overload\n if isMapPosition(overload) then\n bodyId = overload.bodyId\n end\n assert(isSNumber(bodyId),\n 'Argument 1 (bodyId) must be a number:' .. type(bodyId))\n return self[bodyId]\nend\n--\n-- getPlanetarySystemId - get the planetary system ID for this instance\n-- return: the planetary system ID or nil if no planets are in the system.\n--\nfunction PlanetarySystem:getPlanetarySystemId()\n local k, v = next(self)\n return v and v.planetarySystemId\nend\n-- BodyParameters INSTANCE METHODS:\n--\n-- convertToMapPosition - create an instance of MapPosition from coordinates\n-- worldCoordinates [in]: the world coordinates of the map position.\n-- return: an instance of MapPosition class\n--\nfunction BodyParameters:convertToMapPosition(worldCoordinates)\n assert(isTable(worldCoordinates),\n 'Argument 1 (worldCoordinates) must be an array or vec3:' ..\n type(worldCoordinates))\n local worldVec = vec3(worldCoordinates) \n if self.bodyId == 0 then\n return setmetatable({latitude = worldVec.x,\n longitude = worldVec.y,\n altitude = worldVec.z,\n bodyId = 0,\n systemId = self.planetarySystemId}, MapPosition)\n end\n local coords = worldVec - self.center\n local distance = coords:len()\n local altitude = distance - self.radius\n local latitude = 0\n local longitude = 0\n if not float_eq(distance, 0) then\n local phi = math.atan(coords.y, coords.x)\n longitude = phi >= 0 and phi or (2*math.pi + phi)\n latitude = math.pi/2 - math.acos(coords.z/distance)\n end\n return setmetatable({latitude = latitude,\n longitude = longitude,\n altitude = altitude,\n bodyId = self.bodyId,\n systemId = self.planetarySystemId}, MapPosition)\nend\n--\n-- convertToWorldCoordinates - convert a map position to world coordinates\n-- overload [in]: an instance of MapPosition or a position string ('::pos...')\n--\nfunction BodyParameters:convertToWorldCoordinates(overload)\n local mapPosition = isString(overload) and\n mkMapPosition(overload) or overload\n if mapPosition.bodyId == 0 then -- support deep space map position\n return vec3(mapPosition.latitude,\n mapPosition.longitude,\n mapPosition.altitude)\n end\n assert(isMapPosition(mapPosition),\n 'Argument 1 (mapPosition) is not an instance of \"MapPosition\".')\n assert(mapPosition.systemId == self.planetarySystemId,\n 'Argument 1 (mapPosition) has a different planetary system ID.')\n assert(mapPosition.bodyId == self.bodyId,\n 'Argument 1 (mapPosition) has a different planetary body ID.')\n local xproj = math.cos(mapPosition.latitude)\n return self.center + (self.radius + mapPosition.altitude) *\n vec3(xproj*math.cos(mapPosition.longitude),\n xproj*math.sin(mapPosition.longitude),\n math.sin(mapPosition.latitude))\nend\n--\n-- getAltitude - calculate the altitude of a point given in world coordinates.\n-- worldCoordinates [in]: the world coordinates of the point.\n-- return: the altitude in meters\n--\nfunction BodyParameters:getAltitude(worldCoordinates)\n return (vec3(worldCoordinates) - self.center):len() - self.radius\nend\n--\n-- getDistance - calculate the distance to a point given in world coordinates.\n-- worldCoordinates [in]: the world coordinates of the point.\n-- return: the distance in meters\n--\nfunction BodyParameters:getDistance(worldCoordinates)\n return (vec3(worldCoordinates) - self.center):len()\nend\n--\n-- getGravity - calculate the gravity vector induced by the body.\n-- worldCoordinates [in]: the world coordinates of the point.\n-- return: the gravity vector in meter/seconds^2\n--\nfunction BodyParameters:getGravity(worldCoordinates)\n local radial = self.center - vec3(worldCoordinates) -- directed towards body\n local len2 = radial:len2()\n return (self.GM/len2) * radial/math.sqrt(len2)\nend\n-- end of module\nreturn setmetatable(PlanetaryReference,\n { __call = function(_,...)\n return mkPlanetaryReference(...)\n end })\nend\nfunction Keplers()\n --[[ \n Provides methods for computing orbital information for an object\n Usage:\n Kepler = require('autoconf.custom.kepler')\n alioth = Kepler({ GM=157470826617,\n bodyId=2,\n center={x=-8.000,y=-8.000,z=-126303.000},\n name='Alioth',\n planetarySystemId=0,\n radius=126068\n })\n altitude = 6000\n position = '::pos{0,2,0,0,6000}'\n e, o = alioth:escapeAndOrbitalSpeed(altitude)\n orbit = alioth:orbitalParameters(position, {0, o+1, 0})\n print(\"Eccentricity \" .. orbit.eccentricity)\n print(\"Perihelion \" .. orbit.periapsis.altitude)\n print(\"Max. speed \" .. orbit.periapsis.speed)\n print(\"Circular orbit speed \" .. orbit.periapsis.circularOrbitSpeed)\n print(\"Aphelion \" .. orbit.apoapsis.altitude)\n print(\"Min. speed \" .. orbit.apoapsis.speed)\n print(\"Orbital period \" .. orbit.period)\n --- output:\n Eccentricity 0.0018324307017878\n Perihelion 6000.0\n Max. speed 1092.9462297033\n Circular orbit speed 1091.9462297033\n Aphelion 6484.8994605062\n Min. speed 1088.9480596194\n Orbital period 762.02818214049\n Methods:\n Kepler:escapeAndOrbitalSpeed - for a given celestial body and altitude.\n Kepler:orbitalParameters - for a given massless object and a celestial body.\n Description\n The motion of an object in the vicinity of substantially larger mass is\n in the domain of the \"2-body problem\". By assuming the object whose motion\n is of interest is of negligable mass simplifies the calculations of:\n the speed to escape the body, the speed of a circular orbit, and the\n parameters defining the orbit of the object (or the lack of orbit as the\n case may be).\n Orbital Parameters:\n periapsis - the closest approach to the planet\n apoapsis - the furthest point from the planet if in orbit (otherwise nil)\n eccentricity - 0 for circular orbits\n <1 for elliptical orbits\n 1 for parabiolic trajectory\n >1 for hyperbolic trajectory\n period - time (in seconds) to complete an orbit\n Also See: planetref.lua\n]]--\nlocal vec3 = require('cpml.vec3')\nlocal PlanetRef = PlanetRef()\nlocal function isString(s) return type(s) == 'string' end\nlocal function isTable(t) return type(t) == 'table' end\nlocal function float_eq(a,b)\n if a == 0 then return math.abs(b) < 1e-09 end\n if b == 0 then return math.abs(a) < 1e-09 end\n return math.abs(a - b) < math.max(math.abs(a),math.abs(b))*epsilon\nend\nKepler = {}\nKepler.__index = Kepler\n--\n-- escapeAndOrbitalSpeed - speed required to escape and for a circular orbit\n-- altitude [in]: the height of the orbit in meters above \"sea-level\"\n-- return: the speed in m/s needed to escape the celestial body and to orbit it.\n--\nfunction Kepler:escapeAndOrbitalSpeed(altitude)\n assert(self.body)\n -- P = -GMm/r and KE = mv^2/2 (no lorentz factor used)\n -- mv^2/2 = GMm/r\n -- v^2 = 2GM/r\n -- v = sqrt(2GM/r1)\n local distance = altitude + self.body.radius\n if not float_eq(distance, 0) then\n local orbit = math.sqrt(self.body.GM/distance)\n return math.sqrt(2)*orbit, orbit\n end\n return nil, nil\nend\n--\n-- orbitalParameters: determine the orbital elements for a two-body system.\n-- overload [in]: the world coordinates or map coordinates of a massless object.\n-- velocity [in]: The velocity of the massless point object in m/s.\n-- return: the 6 orbital elements for the massless object.\n--\nfunction Kepler:orbitalParameters(overload, velocity)\n assert(self.body)\n assert(isTable(overload) or isString(overload))\n assert(isTable(velocity))\n local pos = (isString(overload) or PlanetRef.isMapPosition(overload)) and\n self.body:convertToWorldCoordinates(overload) or\n vec3(overload)\n local v = vec3(velocity)\n local r = pos - self.body.center\n local v2 = v:len2()\n local d = r:len()\n local mu = self.body.GM\n local e = ((v2 - mu/d)*r - r:dot(v)*v)/mu\n local a = mu/(2*mu/d - v2)\n local ecc = e:len()\n local dir = e:normalize()\n local pd = a*(1-ecc)\n local ad = a*(1+ecc)\n local per = pd*dir + self.body.center\n local apo = ecc <= 1 and -ad*dir + self.body.center or nil\n local trm = math.sqrt(a*mu*(1-ecc*ecc)) \n local Period = apo and 2*math.pi*math.sqrt(a^3/mu)\n -- These are great and all, but, I need more.\n local trueAnomaly = math.acos((e:dot(r))/(ecc*d))\n if r:dot(v) < 0 then\n trueAnomaly = -(trueAnomaly - 2*math.pi)\n end \n -- Apparently... cos(EccentricAnomaly) = (cos(trueAnomaly) + eccentricity)/(1 + eccentricity * cos(trueAnomaly))\n local EccentricAnomaly = math.acos((math.cos(trueAnomaly) + ecc)/(1 + ecc * math.cos(trueAnomaly)))\n -- Then.... apparently if this is below 0, we should add 2pi to it\n -- I think also if it's below 0, we're past the apoapsis?\n local timeTau = EccentricAnomaly\n if timeTau < 0 then\n timeTau = timeTau + 2*math.pi\n end\n -- So... time since periapsis...\n -- Is apparently easy if you get mean anomly. t = M/n where n is mean motion, = 2*pi/Period\n \n \n local MeanAnomaly = timeTau - ecc * math.sin(timeTau)\n local TimeSincePeriapsis = MeanAnomaly/(2*math.pi/Period)\n --system.print(MeanAnomaly .. \" - \" .. TimeSincePeriapsis .. \" - \" .. Period .. \" - \" .. EccentricAnomaly .. \" - \" .. timeTau .. \" - \" .. trueAnomaly)\n -- Mean anom is 0 at periapsis, positive before it... and positive after it.\n -- I guess this is why I needed to use timeTau and not EccentricAnomaly here\n \n local TimeToPeriapsis = Period - TimeSincePeriapsis\n local TimeToApoapsis = TimeToPeriapsis + Period/2\n if trueAnomaly - math.pi > 0 then -- TBH I think something's wrong in my formulas because I needed this.\n TimeToPeriapsis = TimeSincePeriapsis\n TimeToApoapsis = TimeToPeriapsis + Period/2\n end\n if TimeToApoapsis > Period then\n TimeToApoapsis = TimeToApoapsis - Period\n end\n return { periapsis = { position = per,\n speed = trm/pd,\n circularOrbitSpeed = math.sqrt(mu/pd),\n altitude = pd - self.body.radius},\n apoapsis = apo and\n { position = apo,\n speed = trm/ad,\n circularOrbitSpeed = math.sqrt(mu/ad),\n altitude = ad - self.body.radius},\n currentVelocity = v,\n currentPosition = pos,\n eccentricity = ecc,\n period = Period,\n eccentricAnomaly = EccentricAnomaly,\n meanAnomaly = MeanAnomaly,\n timeToPeriapsis = TimeToPeriapsis,\n timeToApoapsis = TimeToApoapsis\n }\nend\n\nlocal function new(bodyParameters)\n local params = PlanetRef.BodyParameters(bodyParameters.planetarySystemId,\n bodyParameters.bodyId,\n bodyParameters.radius,\n bodyParameters.center,\n bodyParameters.GM)\n return setmetatable({body = params}, Kepler)\nend\nreturn setmetatable(Kepler, { __call = function(_,...) return new(...) end })\nend\nfunction Kinematics()\n --[[ \n DualUniverse kinematic equations\n Author: JayleBreak\n Usage (unit.start):\n Kinematics = require('autoconf.custom.kinematics')\n Methods:\n computeAccelerationTime - \"relativistic\" version of t = (vf - vi)/a\n computeDistanceAndTime - Return distance & time needed to reach final speed.\n computeTravelTime - \"relativistic\" version of t=(sqrt(2ad+v^2)-v)/a\n Description\n DualUniverse increases the effective mass of constructs as their absolute\n speed increases by using the \"lorentz\" factor (from relativity) as the scale\n factor. This results in an upper bound on the absolute speed of constructs\n (excluding \"warp\" drive) that is set to 30 000 KPH (8 333 MPS). This module\n provides utilities for computing some physical quantities taking this\n scaling into account.\n]]--\nlocal Kinematic = {} -- just a namespace\nlocal C = 30000000/3600\nlocal C2 = C*C\nlocal ITERATIONS = 100 -- iterations over engine \"warm-up\" period\nlocal function lorentz(v) return 1/math.sqrt(1 - v*v/C2) end\n--\n-- computeAccelerationTime - \"relativistic\" version of t = (vf - vi)/a\n-- initial [in]: initial (positive) speed in meters per second.\n-- acceleration [in]: constant acceleration until 'finalSpeed' is reached.\n-- final [in]: the speed at the end of the time interval.\n-- return: the time in seconds spent in traversing the distance\n--\nfunction Kinematic.computeAccelerationTime(initial, acceleration, final)\n -- The low speed limit of following is: t=(vf-vi)/a (from: vf=vi+at)\n local k1 = C*math.asin(initial/C)\n return (C * math.asin(final/C) - k1)/acceleration\nend\n--\n-- computeDistanceAndTime - Return distance & time needed to reach final speed.\n-- initial[in]: Initial speed in meters per second.\n-- final[in]: Final speed in meters per second.\n-- restMass[in]: Mass of the construct at rest in Kg.\n-- thrust[in]: Engine's maximum thrust in Newtons.\n-- t50[in]: (default: 0) Time interval to reach 50% thrust in seconds.\n-- brakeThrust[in]: (default: 0) Constant thrust term when braking.\n-- return: Distance (in meters), time (in seconds) required for change.\n--\nfunction Kinematic.computeDistanceAndTime(initial,\n final,\n restMass,\n thrust,\n t50,\n brakeThrust)\n -- This function assumes that the applied thrust is colinear with the\n -- velocity. Furthermore, it does not take into account the influence\n -- of gravity, not just in terms of its impact on velocity, but also\n -- its impact on the orientation of thrust relative to velocity.\n -- These factors will introduce (usually) small errors which grow as\n -- the length of the trip increases.\n t50 = t50 or 0\n brakeThrust = brakeThrust or 0 -- usually zero when accelerating\n local tau0 = lorentz(initial)\n local speedUp = initial <= final\n local a0 = thrust * (speedUp and 1 or -1)/restMass\n local b0 = -brakeThrust/restMass\n local totA = a0+b0\n if speedUp and totA <= 0 or not speedUp and totA >= 0 then\n return -1, -1 -- no solution\n end\n local distanceToMax, timeToMax = 0, 0\n -- If, the T50 time is set, then assume engine is at zero thrust and will\n -- reach full thrust in 2*T50 seconds. Thrust curve is given by:\n -- Thrust: F(z)=(a0*(1+sin(z))+2*b0)/2 where z=pi*(t/t50 - 1)/2\n -- Acceleration is given by F(z)/m(z) where m(z) = m/sqrt(1-v^2/c^2)\n -- or v(z)' = (a0*(1+sin(z))+2*b0)*sqrt(1-v(z)^2/c^2)/2\n if a0 ~= 0 and t50 > 0 then\n -- Closed form solution for velocity exists:\n -- v(t) = -c*tan(w)/sqrt(tan(w)^2+1) => w = -asin(v/c)\n -- w=(pi*t*(a0/2+b0)-a0*t50*sin(pi*t/2/t50)+*pi*c*k1)/pi/c\n -- @ t=0, v(0) = vi\n -- pi*c*k1/pi/c = -asin(vi/c)\n -- k1 = asin(vi/c)\n local k1 = math.asin(initial/C)\n local c1 = math.pi*(a0/2+b0)\n local c2 = a0*t50\n local c3 = C*math.pi\n local v = function(t)\n local w = (c1*t - c2*math.sin(math.pi*t/2/t50) + c3*k1)/c3\n local tan = math.tan(w)\n return C*tan/math.sqrt(tan*tan+1)\n end\n local speedchk = speedUp and function(s) return s >= final end or\n function(s) return s <= final end\n timeToMax = 2*t50\n if speedchk(v(timeToMax)) then\n local lasttime = 0\n while math.abs(timeToMax - lasttime) > 0.5 do\n local t = (timeToMax + lasttime)/2\n if speedchk(v(t)) then\n timeToMax = t \n else\n lasttime = t\n end\n end\n end\n -- There is no closed form solution for distance in this case.\n -- Numerically integrate for time t=0 to t=2*T50 (or less)\n local lastv = initial\n local tinc = timeToMax/ITERATIONS\n for step = 1, ITERATIONS do\n local speed = v(step*tinc)\n distanceToMax = distanceToMax + (speed+lastv)*tinc/2\n lastv = speed\n end\n if timeToMax < 2*t50 then\n return distanceToMax, timeToMax\n end\n initial = lastv\n end\n -- At full thrust, acceleration only depends on the Lorentz factor:\n -- v(t)' = (F/m(v)) = a*sqrt(1-v(t)^2/c^2) where a = a0+b0\n -- -> v = c*sin((at+k1)/c)\n -- @ t=0, v=vi: k1 = c*asin(vi/c)\n -- -> t = (c*asin(v/c) - k1)/a\n -- x(t)' = c*sin((at+k1)/c)\n -- x = k2 - c^2 cos((at+k1)/c)/a\n -- @ t=0, x=0: k2 = c^2 * cos(k1/c)/a\n local k1 = C*math.asin(initial/C)\n local time = (C * math.asin(final/C) - k1)/totA\n local k2 = C2 *math.cos(k1/C)/totA\n local distance = k2 - C2 * math.cos((totA*time + k1)/C)/totA\n return distance+distanceToMax, time+timeToMax\nend\n--\n-- computeTravelTime - \"relativistic\" version of t=(sqrt(2ad+v^2)-v)/a\n-- initialSpeed [in]: initial (positive) speed in meters per second\n-- acceleration [in]: constant acceleration until 'distance' is traversed\n-- distance [in]: the distance traveled in meters\n-- return: the time in seconds spent in traversing the distance\n--\nfunction Kinematic.computeTravelTime(initial, acceleration, distance)\n -- The low speed limit of following is: t=(sqrt(2ad+v^2)-v)/a\n -- (from: d=vt+at^2/2)\n if distance == 0 then return 0 end\n if acceleration > 0 then\n local k1 = C*math.asin(initial/C)\n local k2 = C2*math.cos(k1/C)/acceleration\n return (C*math.acos(acceleration*(k2 - distance)/C2) - k1)/acceleration\n end\n assert(initial > 0, 'Acceleration and initial speed are both zero.')\n return distance/initial\nend\nfunction Kinematic.lorentz(v) return lorentz(v) end\nreturn Kinematic\nend\nPlanetaryReference = PlanetRef()\ngalaxyReference = PlanetaryReference(Atlas())\nKinematic = Kinematics()\nKep = Keplers()\nfunction getDistanceDisplayString(distance)\n local su = distance > 100000\n local result = \"\"\n if su then\n -- Convert to SU\n result = round(distance/1000/200,1) .. \" SU\"\n else\n -- Convert to KM\n result = round(distance/1000,1) .. \" KM\"\n end\n\n return result\nend\n\nPlanetaryReference = PlanetRef()\ngalaxyReference = PlanetaryReference(Atlas())\n\nMapScreenButtons = {}\nMapScreenMouseX = 0\nMapScreenMouseY = 0\nMapScreenMouseDown = false\nMapScreenButtonSelected = 0\nlocal worldPos = vec3(core.getConstructWorldPos())\nlocal locX = (worldPos.x/400000)\nlocal locY = (worldPos.y/400000)*(-1)\nlocal destX = 0\nlocal destY = 0\nlocal sudistance = 0\nlocal loc = vec3(core.getConstructWorldPos())\nlocal ion = galaxyReference[0][120] ---uses Atlas functions\nlocal thades = vec3(29165536.000, 10865536.000, 65536.000)\nlocal sinnen = vec3(58665536.000, 29665536.000, 58165536.000)\nlocal alioth = galaxyReference[0][2] ---uses Atlas functions\nlocal madis = vec3(17465536.000, 22665536.000, -34464.000)\nlocal jago = vec3(-94134464.000, 12765536.000, -3634464.000)\nlocal symeon = vec3(14165536.000, -85634464.000, -934464.000)\nlocal lacobus = vec3(98865536.000, -13534464.000, -934464.000)\nlocal teoma = vec3(80865536.000, 54665536.000, -934464.000)\nlocal feli = vec3(-43534464.000, 22565536.000, -48934464.000)\nlocal talemai = vec3(-13234464.000, 55765536.000, 465536.000)\nlocal sicari = vec3(52765536.000, 27165536.000, 52065536.000)\nlocal distion = math.floor(ion:getDistance(loc)/200000) ---uses getDistance functions----\nlocal distthades = string.format(\"%.2f\", math.sqrt((loc.x-thades.x)^2+(loc.y-thades.y)^2+(loc.z-thades.z)^2)/200000)\nlocal distalioth = math.floor(alioth:getDistance(loc)/200000) ---uses getDistance functions----\nlocal distmadis = string.format(\"%.2f\", math.sqrt((loc.x-madis.x)^2+(loc.y-madis.y)^2+(loc.z-madis.z)^2)/200000)\nlocal distjago = string.format(\"%.2f\", math.sqrt((loc.x-jago.x)^2+(loc.y-jago.y)^2+(loc.z-jago.z)^2)/200000)\nlocal distlacobus = string.format(\"%.2f\", math.sqrt((loc.x-lacobus.x)^2+(loc.y-lacobus.y)^2+(loc.z-lacobus.z)^2)/200000)\nlocal distteoma = string.format(\"%.2f\", math.sqrt((loc.x-teoma.x)^2+(loc.y-teoma.y)^2+(loc.z-teoma.z)^2)/200000)\nlocal distsymeon = string.format(\"%.2f\", math.sqrt((loc.x-symeon.x)^2+(loc.y-symeon.y)^2+(loc.z-symeon.z)^2)/200000)\nlocal distfeli = string.format(\"%.2f\", math.sqrt((loc.x-feli.x)^2+(loc.y-feli.y)^2+(loc.z-feli.z)^2)/200000)\nlocal distsinnen = string.format(\"%.2f\", math.sqrt((loc.x-sinnen.x)^2+(loc.y-sinnen.y)^2+(loc.z-sinnen.z)^2)/200000)\nlocal disttalemai = string.format(\"%.2f\", math.sqrt((loc.x-talemai.x)^2+(loc.y-talemai.y)^2+(loc.z-talemai.z)^2)/200000)\nlocal distsicari = string.format(\"%.2f\", math.sqrt((loc.x-sicari.x)^2+(loc.y-sicari.y)^2+(loc.z-sicari.z)^2)/200000)\n\n\n\n for i = 1,1 do\n local button = {id = (\"b\"..1), enabled=true, td=\"<td>\", top=2/100, bottom=13/100, left=1/100, right=28/100}\n table.insert(MapScreenButtons, button)\nend\n for i = 2,2 do\n local button = {id = (\"b\"..2), enabled=true, td=\"<td>\", top=15/100, bottom=26/100, left=1/100, right=30/100}\n table.insert(MapScreenButtons, button)\nend\n for i = 3,3 do\n local button = {id = (\"b\"..3), enabled=true, td=\"<td>\", top=27/100, bottom=38/100, left=1/100, right=28/100}\n table.insert(MapScreenButtons, button)\nend\n for i = 4,4 do \n local button = {id = (\"b\"..4), enabled=true, td=\"<td>\", top=39/100, bottom=50/100, left=1/100, right=28/100}\n table.insert(MapScreenButtons, button)\nend\n for i = 5,5 do \n local button = {id = (\"b\"..5), enabled=true, td=\"<td>\", top=51/100, bottom=62/100, left=1/100, right=28/100}\n table.insert(MapScreenButtons, button)\nend\n for i = 6,6 do \n local button = {id = (\"b\"..6), enabled=true, td=\"<td>\", top=64/100, bottom=75/100, left=1/100, right=28/100}\n table.insert(MapScreenButtons, button)\nend\n for i = 7,7 do \n local button = {id = (\"b\"..7), enabled=true, td=\"<td>\", top=2/100, bottom=13/100, left=75/100, right=100/100}\n table.insert(MapScreenButtons, button)\nend\n for i = 8,8 do \n local button = {id = (\"b\"..8), enabled=true, td=\"<td>\", top=15/100, bottom=26/100, left=75/100, right=100/100}\n table.insert(MapScreenButtons, button)\nend\n for i = 9,9 do \n local button = {id = (\"b\"..9), enabled=true, td=\"<td>\", top=27/100, bottom=38/100, left=75/100, right=100/100}\n table.insert(MapScreenButtons, button)\nend\n for i = 10,10 do \n local button = {id = (\"b\"..10), enabled=true, td=\"<td>\", top=39/100, bottom=50/100, left=75/100, right=100/100}\n table.insert(MapScreenButtons, button)\nend\n for i = 11,11 do \n local button = {id = (\"b\"..11), enabled=true, td=\"<td>\", top=51/100, bottom=62/100, left=75/100, right=100/100}\n table.insert(MapScreenButtons, button)\nend\n for i = 12,12 do \n local button = {id = (\"b\"..12), enabled=true, td=\"<td>\", top=64/100, bottom=75/100, left=75/100, right=100/100}\n table.insert(MapScreenButtons, button)\nend\n for i = 13,13 do \n local button = {id = (\"b\"..13), enabled=true, td=\"<td>\", top=90/100, bottom=100/100, left=1/100, right=18/100}\n table.insert(MapScreenButtons, button)\nend\nfunction evaluateButtons()\n local selected = 0\n \n if #MapScreenButtons >= 1 then\n -- Set button styles\n for i, button in ipairs(MapScreenButtons) do\n if button.left < MapScreenMouseX and MapScreenMouseX < button.right and button.top < MapScreenMouseY and MapScreenMouseY < button.bottom then\n if MapScreenMouseDown and MapScreenButtonSelected == i then\n end\n selected = i\n end\n if not button.enabled then\n end\n\n end\n end\n return selected\nend\n\nfunction onButtonDown(buttonNo)\n local button = MapScreenButtons[buttonNo] \n if not button or not button.enabled then\n\treturn\n end\nend\nfunction onButtonUp(buttonNo)\n local button = MapScreenButtons[buttonNo] \n if not button or not button.enabled then\n return\n end\nfunction onClick(buttonNo)\n local button = MapScreenButtons[buttonNo] \n if not button or not button.enabled then\n return\n end\nend\n if buttonNo == 1 then\ndestX = 0\ndestY = 0\nsudistance = distalioth\n elseif buttonNo == 2 then\ndestX = 43\ndestY = -56\nsudistance = distmadis\n elseif buttonNo == 3 then\ndestX = 73\ndestY = -27\nsudistance = distthades \n elseif buttonNo == 4 then\ndestX = -33\ndestY = -139\nsudistance = disttalemai\n elseif buttonNo == 5 then\ndestX = -109\ndestY = -56\nsudistance = distfeli \n elseif buttonNo == 6 then\ndestX = 131\ndestY = -68\nsudistance = distsicari \n elseif buttonNo == 7 then\ndestX = 35\ndestY = 214\nsudistance = distsymeon\n elseif buttonNo == 8 then\ndestX = 146\ndestY = -74\nsudistance = distsinnen \n elseif buttonNo == 9 then\ndestX = -235\ndestY = -32\nsudistance = distjago \n elseif buttonNo == 10 then\ndestX = 202\ndestY = -137\nsudistance = distteoma \n elseif buttonNo == 11 then\ndestX = 7\ndestY = 247\nsudistance = distion \n elseif buttonNo == 12 then\ndestX = 247\ndestY = 34\nsudistance = distlacobus\n elseif buttonNo == 13 then\n unit.exit()\n end\nend\n\nfunction updateScreen() \nwarpmath = math.floor(math.floor(core.getConstructMass()/ 1000) * sudistance * 0.00025)\nhtml= ([[\n<svg class=\"bootstrap\" viewBox=\"0 0 1024 1024\" style=\"width:100%; height:100%\"><circle cx=\"500\" cy=\"500\" r=\"400\" stroke=\"darkgreen\" stroke-width=\"3\" transform=\"\"></circle><circle cx=\"500\" cy=\"500\" r=\"350\" stroke=\"darkgreen\" stroke-width=\"3\" transform=\"\" stroke-opacity=\"0.2\"></circle><circle cx=\"500\" cy=\"500\" r=\"300\" stroke=\"darkgreen\" stroke-width=\"3\" transform=\"\"></circle><circle cx=\"500\" cy=\"500\" r=\"250\" stroke=\"darkgreen\" stroke-width=\"3\" transform=\"\" stroke-opacity=\"0.2\"></circle><circle cx=\"500\" cy=\"500\" r=\"200\" stroke=\"darkgreen\" stroke-width=\"3\" transform=\"\"></circle><circle cx=\"500\" cy=\"500\" r=\"150\" stroke=\"darkgreen\" stroke-width=\"3\" transform=\"\" stroke-opacity=\"0.2\"></circle><circle cx=\"500\" cy=\"500\" r=\"100\" stroke=\"lightblue\" stroke-width=\"3\" transform=\"\"></circle><circle cx=\"500\" cy=\"500\" r=\"50\" stroke=\"lightblue\" stroke-width=\"3\" transform=\"\" stroke-opacity=\"0.2\"></circle><circle cx=\"500\" cy=\"500\" r=\"20\" stroke=\"Orange\" stroke-width=\"2\" transform=\"\"></circle><text x=\"510\" y=\"510\" fill=\"Yellow\">Helios</text><circle cx=\"-0.00\" cy=\"0\" r=\"10\" stroke=\"black\" stroke-width=\"1\" fill=\"blue\" transform=\"translate(500,500)\"></circle><text x=\"-0.00\" y=\"0\" transform=\"translate(500,480)\" fill=\"white\" font-size=\"20\">Alioth</text><circle cx=\"7.16\" cy=\"247.59\" r=\"10\" stroke=\"black\" stroke-width=\"1\" fill=\"blue\" transform=\"translate(500,500)\"></circle><text x=\"7.16\" y=\"247.59\" transform=\"translate(480,480)\" fill=\"white\" font-size=\"20\">Ion</text><circle cx=\"35.41\" cy=\"214.09\" r=\"10\" stroke=\"black\" stroke-width=\"1\" fill=\"blue\" transform=\"translate(500,500)\"></circle><text x=\"35.41\" y=\"214.09\" transform=\"translate(500,480)\" fill=\"white\" font-size=\"20\">Symeon</text><circle cx=\"-33.09\" cy=\"-139.41\" r=\"10\" stroke=\"black\" stroke-width=\"1\" fill=\"blue\" transform=\"translate(500,500)\"></circle><text x=\"-33.09\" y=\"-139.41\" transform=\"translate(500,480)\" fill=\"white\" font-size=\"20\">Talemai</text><circle cx=\"202.16\" cy=\"-136.66\" r=\"10\" stroke=\"black\" stroke-width=\"1\" fill=\"blue\" transform=\"translate(500,500)\"></circle><text x=\"202.16\" y=\"-136.66\" transform=\"translate(500,480)\" fill=\"white\" font-size=\"20\">Teoma</text><circle cx=\"247.16\" cy=\"33.84\" r=\"10\" stroke=\"black\" stroke-width=\"1\" fill=\"blue\" transform=\"translate(500,500)\"></circle><text x=\"247.16\" y=\"33.84\" transform=\"translate(500,480)\" fill=\"white\" font-size=\"20\">Lacobus</text><circle cx=\"-108.84\" cy=\"-56.41\" r=\"10\" stroke=\"black\" stroke-width=\"1\" fill=\"blue\" transform=\"translate(500,500)\"></circle><text x=\"-108.84\" y=\"-56.41\" transform=\"translate(500,480)\" fill=\"white\" font-size=\"20\">Feli</text><circle cx=\"72.91\" cy=\"-27.16\" r=\"10\" stroke=\"black\" stroke-width=\"1\" fill=\"blue\" transform=\"translate(500,500)\"></circle><text x=\"72.91\" y=\"-27.16\" transform=\"translate(500,485)\" fill=\"white\" font-size=\"20\">Thades</text><circle cx=\"43.66\" cy=\"-56.66\" r=\"10\" stroke=\"black\" stroke-width=\"1\" fill=\"blue\" transform=\"translate(500,500)\"></circle><text x=\"43.66\" y=\"-56.66\" transform=\"translate(500,480)\" fill=\"white\" font-size=\"20\">Madis</text><circle cx=\"-235.34\" cy=\"-31.91\" r=\"10\" stroke=\"black\" stroke-width=\"1\" fill=\"blue\" transform=\"translate(500,500)\"></circle><text x=\"-235.34\" y=\"-31.91\" transform=\"translate(500,480)\" fill=\"white\" font-size=\"20\">Jago</text><circle cx=\"131.91\" cy=\"-67.91\" r=\"10\" stroke=\"black\" stroke-width=\"1\" fill=\"blue\" transform=\"translate(500,500)\"></circle><text x=\"131.91\" y=\"-67.91\" transform=\"translate(475,480)\" fill=\"white\" font-size=\"20\">Sicari</text><circle cx=\"146.66\" cy=\"-74.16\" r=\"10\" stroke=\"black\" stroke-width=\"1\" fill=\"blue\" transform=\"translate(500,500)\"></circle><text x=\"146.66\" y=\"-74.16\" transform=\"translate(515,480)\" fill=\"white\" font-size=\"20\">Sinnen</text>\n<line stroke-linecap=\"undefined\" stroke-linejoin=\"undefined\" id=\"svg_1\" y2=\"]]..destY..[[\" x2=\"]]..destX..[[\" y1=\"]]..locY..[[\" x1=\"]]..locX..[[\" transform=\"translate(500,500)\" stroke-width=\"5\" stroke=\"#ff0000\" fill=\"none\"/> \n<circle cx=\"]]..locX..[[\" cy=\"]]..locY..[[\" r=\"3\" stroke=\"black\" stroke-width=\"1\" fill=\"limegreen\" transform=\"translate(500,500)\"></circle>\n<text x=\"]]..locX..[[\" y=\"]]..locY..[[\" transform=\"translate(500,500)\" \nfill=\"limegreen\" font-size= \"4.5vh\" font-weight= \"bold\">//SHIP POSITION</text>\n</svg>\n<svg class=\"bootstrap\" viewBox=\"0 0 1024 612\" style=\"width:100%; height:100%\">\n <g>\n <title>Layer 1</title>\n <g id=\"svg_24\">\n <text stroke=\"null\" transform=\"matrix(0.7907331239400577,0,0,0.7600725676692406,3.135703637258853,5.731969683147472) \" xml:space=\"preserve\" text-anchor=\"start\" font-family=\"Helvetica, Arial, sans-serif\" font-size=\"20\" id=\"svg_8\" y=\"70\" x=\"55\" stroke-width=\"0\" fill=\"Yellow\">Alioth :]]..distalioth..[[ SU</text>\n <text stroke=\"null\" transform=\"matrix(0.7907331239400577,0,0,0.7600725676692406,3.135703637258853,5.731969683147472) \" xml:space=\"preserve\" text-anchor=\"start\" font-family=\"Helvetica, Arial, sans-serif\" font-size=\"20\" id=\"svg_14\" y=\"170\" x=\"55\" stroke-width=\"0\" fill=\"Yellow\">Madis :]]..distmadis..[[ SU</text>\n <text stroke=\"null\" transform=\"matrix(0.7907331239400577,0,0,0.7600725676692406,3.135703637258853,5.731969683147472) \" xml:space=\"preserve\" text-anchor=\"start\" font-family=\"Helvetica, Arial, sans-serif\" font-size=\"20\" id=\"svg_17\" y=\"270\" x=\"55\" stroke-width=\"0\" fill=\"Yellow\">Thades :]]..distthades..[[ SU</text>\n <text stroke=\"null\" transform=\"matrix(0.7907331239400577,0,0,0.7600725676692406,3.135703637258853,5.731969683147472) \" xml:space=\"preserve\" text-anchor=\"start\" font-family=\"Helvetica, Arial, sans-serif\" font-size=\"20\" id=\"svg_20\" y=\"370\" x=\"55\" stroke-width=\"0\" fill=\"Yellow\">Talemai :]]..disttalemai..[[ SU</text>\n <text stroke=\"null\" transform=\"matrix(0.7907331239400577,0,0,0.7600725676692406,3.135703637258853,5.731969683147472) \" xml:space=\"preserve\" text-anchor=\"start\" font-family=\"Helvetica, Arial, sans-serif\" font-size=\"20\" id=\"svg_23\" y=\"470\" x=\"55\" stroke-width=\"0\" fill=\"Yellow\">Feli :]]..distfeli..[[ SU</text>\n <text stroke=\"null\" transform=\"matrix(0.7907331239400577,0,0,0.7600725676692406,3.135703637258853,5.731969683147472) \" xml:space=\"preserve\" text-anchor=\"start\" font-family=\"Helvetica, Arial, sans-serif\" font-size=\"20\" id=\"svg_26\" y=\"570\" x=\"55\" stroke-width=\"0\" fill=\"Yellow\">Sicari :]]..distsicari..[[ SU</text>\n <g id=\"svg_12\">\n <rect rx=\"10\" id=\"svg_1\" height=\"50\" width=\"250\" y=\"30\" x=\"15\" stroke-width=\"20\" stroke=\"#00ff00\" fill=\"none\"/>\n <rect rx=\"10\" id=\"svg_3\" height=\"50\" width=\"250\" y=\"105\" x=\"15\" stroke-width=\"20\" stroke=\"#00ff00\" fill=\"none\"/>\n <rect rx=\"10\" id=\"svg_7\" height=\"50\" width=\"250\" y=\"180\" x=\"15\" stroke-width=\"20\" stroke=\"#00ff00\" fill=\"none\"/>\n <rect rx=\"10\" id=\"svg_9\" height=\"50\" width=\"250\" y=\"255\" x=\"15\" stroke-width=\"20\" stroke=\"#00ff00\" fill=\"none\"/>\n <rect rx=\"10\" id=\"svg_10\" height=\"50\" width=\"250\" y=\"330\" x=\"15\" stroke-width=\"20\" stroke=\"#00ff00\" fill=\"none\"/>\n <rect rx=\"10\" id=\"svg_11\" height=\"50\" width=\"250\" y=\"405\" x=\"15\" stroke-width=\"20\" stroke=\"#00ff00\" fill=\"none\"/>\n </g>\n </g>\n <g id=\"svg_40\">\n <text stroke=\"null\" transform=\"matrix(0.7907331239400577,0,0,0.7600725676692406,3.135703637258853,5.731969683147472) \" xml:space=\"preserve\" text-anchor=\"start\" font-family=\"Helvetica, Arial, sans-serif\" font-size=\"20\" id=\"svg_25\" y=\"70\" x=\"997.163642\" stroke-width=\"0\" fill=\"Yellow\">Symeon :]]..distsymeon..[[ SU</text>\n <text stroke=\"null\" transform=\"matrix(0.7907331239400577,0,0,0.7600725676692406,3.135703637258853,5.731969683147472) \" xml:space=\"preserve\" text-anchor=\"start\" font-family=\"Helvetica, Arial, sans-serif\" font-size=\"20\" id=\"svg_27\" y=\"170\" x=\"997.163642\" stroke-width=\"0\" fill=\"Yellow\">Sinnen :]]..distsinnen..[[ SU</text>\n <text stroke=\"null\" transform=\"matrix(0.7907331239400577,0,0,0.7600725676692406,3.135703637258853,5.731969683147472) \" xml:space=\"preserve\" text-anchor=\"start\" font-family=\"Helvetica, Arial, sans-serif\" font-size=\"20\" id=\"svg_28\" y=\"270\" x=\"997.163642\" stroke-width=\"0\" fill=\"Yellow\">Jago :]]..distjago..[[ SU</text>\n <text stroke=\"null\" transform=\"matrix(0.7907331239400577,0,0,0.7600725676692406,3.135703637258853,5.731969683147472) \" xml:space=\"preserve\" text-anchor=\"start\" font-family=\"Helvetica, Arial, sans-serif\" font-size=\"20\" id=\"svg_30\" y=\"370\" x=\"997.163642\" stroke-width=\"0\" fill=\"Yellow\">Teoma :]]..distteoma..[[ SU</text>\n <text stroke=\"null\" transform=\"matrix(0.7907331239400577,0,0,0.7600725676692406,3.135703637258853,5.731969683147472) \" xml:space=\"preserve\" text-anchor=\"start\" font-family=\"Helvetica, Arial, sans-serif\" font-size=\"20\" id=\"svg_31\" y=\"470\" x=\"997.163642\" stroke-width=\"0\" fill=\"Yellow\">Ion :]]..distion..[[ SU</text>\n <text stroke=\"null\" transform=\"matrix(0.7907331239400577,0,0,0.7600725676692406,3.135703637258853,5.731969683147472) \" xml:space=\"preserve\" text-anchor=\"start\" font-family=\"Helvetica, Arial, sans-serif\" font-size=\"20\" id=\"svg_32\" y=\"570\" x=\"997.163642\" stroke-width=\"0\" fill=\"Yellow\">Lacobus :]]..distlacobus..[[ SU</text>\n <g id=\"svg_39\">\n <rect rx=\"10\" id=\"svg_33\" height=\"50\" width=\"250\" y=\"30\" x=\"760\" stroke-width=\"20\" stroke=\"#00ff00\" fill=\"none\"/>\n <rect rx=\"10\" id=\"svg_34\" height=\"50\" width=\"250\" y=\"105\" x=\"760\" stroke-width=\"20\" stroke=\"#00ff00\" fill=\"none\"/>\n <rect rx=\"10\" id=\"svg_35\" height=\"50\" width=\"250\" y=\"180\" x=\"760\" stroke-width=\"20\" stroke=\"#00ff00\" fill=\"none\"/>\n <rect rx=\"10\" id=\"svg_36\" height=\"50\" width=\"250\" y=\"255\" x=\"760\" stroke-width=\"20\" stroke=\"#00ff00\" fill=\"none\"/>\n <rect rx=\"10\" id=\"svg_37\" height=\"50\" width=\"250\" y=\"330\" x=\"760\" stroke-width=\"20\" stroke=\"#00ff00\" fill=\"none\"/>\n <rect rx=\"10\" id=\"svg_38\" height=\"50\" width=\"250\" y=\"405\" x=\"760\" stroke-width=\"20\" stroke=\"#00ff00\" fill=\"none\"/>\n </g>\n </g>\n </g>\n <text stroke=\"null\" transform=\"matrix(0.7907331239400577,0,0,0.7600725676692406,3.135703637258853,5.731969683147472) \" xml:space=\"preserve\" text-anchor=\"start\" font-family=\"Helvetica, Arial, sans-serif\" font-size=\"30\" id=\"svg_32\" y=\"700\" x=\"20\" stroke-width=\"0\" fill=\"LightBlue\">Est. Warp Cost: ]]..warpmath..[[</text>\n <text stroke=\"null\" transform=\"matrix(0.7907331239400577,0,0,0.7600725676692406,3.135703637258853,5.731969683147472) \" xml:space=\"preserve\" text-anchor=\"start\" font-family=\"Helvetica, Arial, sans-serif\" font-size=\"30\" id=\"svg_32\" y=\"750\" x=\"20\" stroke-width=\"0\" fill=\"LightBlue\">Construct Weight: ]]..math.floor(core.getConstructMass()/ 1000)..[[ tons</text>\n \n</svg> \n ]])\nscreen.setHTML(html)\nend\nunit.setTimer(\"spacemap\",.08)\n\n\n","filter":{"args":[],"signature":"start()","slotKey":"-1"},"key":"2"},{"code":"updateScreen()","filter":{"args":[{"value":"spacemap"}],"signature":"tick(timerId)","slotKey":"-1"},"key":"3"}],"methods":[],"events":[]}


     
     
  12. Like
    Lavayar reacted to Mordgier in Wishing NQ the best, Oh man this patch.   
    You're only allowed to build civilization as JC sees it.
     
    Everyone having a mega factory was too civilized. People building their own iphones from scratch?! What futuristic nonsense!
     
    Now get out of your space ship and go hand mine that ore....
  13. Like
    Lavayar reacted to Biblitz in Wishing NQ the best, Oh man this patch.   
    So near the end of my 3 month sub, played for a solid 2 months of it then just occasionally the last couple of weeks up until the patch. I will not be renewing (decided this pre-patch already)
     
    This patch has honestly terrified me, I can see what NQ is trying to do - but IMO its a big gamble that may backfire. I'll use myself as an example:
     
    I've been playing the game to have fun (like most players I hope!), amass some wealth, mine and had the most fun hooking up factories to produce everything of all sizes up to T3.  After producing almost everything, (and honestly have more fun knitting the factory together than anything else) and being happy to have amassed ~32m (which is not much relative to some players and a lot to others) I thought after this patch I'd be able to have some fun still buying a few schematics and setting up a few specialized assembly lines for a few of the T2-3 things. I should add I'm out on Feli and kinda like it here, along with probably dozen other players or so... 
     
    Wow, the price to get started is not only steep, they are astronomical - this game would be a complete grind fest for me to continue moving forward. I do not want to make and sell 300 billion xs & s parts to eventually afford enough to buy an M schematic and M related components. I'm afraid to buy any higher priced schematic because the game is still in flux and i have a feeling they may have to adjust pricing a few times, or everyone else has the same idea and will build the same thing.  Additionally, i'll likely have to move back to the soul crushing FPS Destroying markets on Alioth... around people... this is really against the grain with my own playstyle.
     
    Looks like solving complex factory wirings will be going away for me as well. I had the most fun making a do-everything factory - now I'm forced to make a simple widget factory in order to grind out enough widgets and dollars to maybe one day afford another widget factory to do this all over again. ? 
     
    I sure hope this game works out in the long run, I've had a blast playing it so far - Good luck NQ! I'll check back in a year and see where it's at
     
  14. Like
    Lavayar reacted to Greydog in Schematics   
    I'll never understand why some people are so intent on prescribing how other peoples playstyles should be. It just so happens some people enjoy the challenge of trying to do it all. You want to limit yourself to the boring task of producing one item be my guest. I buy multiple accounts just so I can "do it all" ...now it seems that playstyle is not in the vision so my multiple accounts have been unsubbed. What folks don't realize is that solo players are actually the majority of paying customers is these games, not because of the number of individual solo players but because we usually have multiple accounts. (it's hard to be self sufficient with one character)
     
    What should have been done would have been to offer the basic schems needed to get a start in the game for free. Then as items produced get more complex they could start charging for them, with the more common ones being dirt cheap and the most rare being the most expensive and difficult to acquire.
     
    We also should not need a schematic for each machine. This could have been handled by uploading them to database's of varying sizes that could then be linked to a series of industries. Small factories would only need the smaller, relatively inexpensive db's, while the mega-factories would need many much larger, much more expensive db's to operate. 
     
    I understand why they did it this way ...IMO they tripped over themselves in the process. The upcoming need for power generation will accomplish their goal. The current implementation of schematics has come in as more of a wall than the curb they intended.
     
    Schematics are good and should stay as they add a level of complexity. But implement them so as to add to the fun, not impede it.
  15. Like
    Lavayar reacted to XKentX in Introduce inefficient generic schematic for t1   
    As many people are against the v0.23 i wanted to share a possible solution without getting everything upside down again.
     
    Introduce a "generic schematic" for t1 of each type per price range. That schematics still costs 75k but allows building any item in that category (any item that has a schematic for 75k) but at double the material cost.
     
    This will not impact economy, it's inefficient.
     
    This will allow people to build their first stuff relatively cheap and not force them to always buy all their simple stuff from market.
     
    Those can as well be limited in how many cycles they have.
  16. Like
  17. Like
    Lavayar reacted to Ethariel in Let's talk DU quits   
    I was very new in DU (up till 10/12/20 anyway) and really having a lot of fun, i ignored most of the tutorial (having seen it twice on you tume) and was just itching to go find a hex on Sanctuary.
     
    Un-eventful journey there, particularly unsettling was flying through trees only to being slammed into an invisible rock on top of a hill and brought to a halt but none the less i made it to an empty hex, had a look about and didn't see any obvious holes in the ground so thought, 'ok that's mine, mine' (pun intended).
    Started with the nano crafter and a mining tool and found the talent queue.
    Over the next few weeks I've managed to amass a whopping 2.3Mil in the bank, L,M,S,XS assemblers and 2 or more of most of the assemblers and i'm happy as a pig in muck, not sure if I left my hex once other than to try a test ride in a Hover then flying vessel (always carry scrap with you....) of some personal freakish design.
     
    Take a couple of weeks off due to work and living out of a hotel room just to get home and find out about some big patch.
     
    Oh dear, no longer fun to me in any way shape or form, I can no longer tinker with building or make even the components for parts to do what I was doing before and don't have remotley close to enough cash to buy the schematics to even think about building anything.
     
    Suggestions from Discord - Pimp myself out as a slave miner to supply ore to someone with a lot of money.............
     
    Sorry but a game should be fun, i found the un-sub method, have employed it and it is with great regret I've uninstalled
     
    As an aside I still have EQ installed on my PC and an active account that's 20 years old, Eve installed and a subbed toon there that's on a 15 year old account, but no DU after just 6 short weeks.
     
    My personal take on this is very similar to SWG, it was a thriving game, player cities, crafting up the wazoo and PvP, then along came the CU - the player base shrank over a couple of weeks and after that it's time was numbered.
     
    I do wish you well in building a working release game but until such time as the developers and owners actually try playing the game they are creating there is no hope. 
     
    Final thing
     
    PvP - I detest PvP with a vengeance these days, I've had 15 years of it in various games and just can't be bothered with it anymore any game that forces me into the position of having to PvP is a no go and unfortunately DU seems to be caving in to the vocal PvP playerbase as most games seem to these days (which is depressing and to be honest they don't have a great population a couple of years down the line)

    Why not have a 'PvP switch,

    In the off position, you don't even show up on radar can't be targeted and are an 'Illegal Target'
    You can flip it to 'On' when on a planet and after XX - say 30 minutes - it switches your status to PvP enabled - you are now a legitimate target for anyone else who has pvp up and you show on radar.
    To turn off PvP, simply land on a planet and switch off, after 30 mins your status switches to non PvP
    Allow say 2 state changes in 24 hours as we know all PvP'ers just love PvP so they won't mind having restrictions in turning it on and off as they would run with it on all the time anyway.....right?
  18. Like
    Lavayar reacted to michaelk in Roll Back   
    I'm really confused by the blanket silence from NQ.
     
    Have they already commented on the patch...or was it only discussed on some random Streamer's channel...? I heard here that there would be a discussion yesterday but didn't see anything.
     
    Are they just waiting around until things quiet down?  
     
    Yet again, NQ needs to work on communication: "We see that many players do not like this update and are carefully monitoring feedback. We will consider if a rollback is appropriate. Thank you for taking the time to give us honest feedback, which is super generous of our players considering you're all paying for this experience and getting feedback is the whole point of beta! Thank you for your patience." 
     
    I've said many times that NQ has an issue with arrogance. This patch and the lack of reaction to player feedback from NQ is not doing much to dispel the idea that JC just doesn't care what the players think because he's smart and we're dumb. No apologies for a rough patch. No comments. No "thank you for being patient as we figure stuff out". Nothing. 
     
    For all the love and passion players have brought into this game, it's baffling that NQ isn't more interested in interacting with them beyond listening to JC's voice (if you happen to be a streamer). 
     
    Even if DU worked exactly as promised and was flawless...that arrogant attitude just isn't attractive and isn't something I'd want to support. 
     
  19. Like
    Lavayar reacted to Mordgier in How to give PRO feedback!   
    Yeah - too bad the person whose vision the game is doesn't play the game...
  20. Like
    Lavayar reacted to lucagrabacr in Making it Harder for Individual Players, Forcing Interdependencies Through Arbitrary Mechanics & The Overall Condescending Presumption That Comes With it Won't Kickstart "Civilization" - Stop This Top-Down Approach on Forcing Civilizations to Materialize   
    Let it sprout organically
     
    Civilizations won't exist or sprout just because you practically force players to specialize, the reason it happens in the real world is because people NEED to, they don't want to do that in games, that they pay a subscription for nonetheless, to even presume people would is condescending to your playerbase
     
    Let the pace of the game be viable for individual players to achieve things they would normally expect to achieve by playing solo, then the civilizations would sprout up by people who decide to come together and make even greater things
     
    Interdependencies will still happen organically from people being better at or enjoying doing certain things than others, you don't have to put game mechanics which arbitrarily make it harder for everyone to do those things
     
    Here's how a lot of people feel about the update from my recording during the AMA, and my input as well;
     
     
  21. Like
    Lavayar reacted to CptLoRes in Roll Back   
    This game used to have a steep learning curve for new players. Now it has become a wall with barb wire on the top..
  22. Like
    Lavayar reacted to Monk_NL in Roll Back   
    Guys, don't be so salty. This is just one of the many patches that NQ will mess the game up with. Downwards we go!

    No banana for NQ!
  23. Like
    Lavayar reacted to Rimezx in Schematic price formula needs changes   
    The schematic prices are fine for T1 but as the tier progresses it is becoming ridiculous.
     
    For example check warp cell price make a calculation of how much the schematics would cost to get 1 cell every 50 mins production.
    After that do the math of how many warp cells you can produce per day and how much profit you make per warp cell. If you do the math
    to regain the initial investment out of profit you need something like 140 days of selling warp cells to the market. 
     
    Haven't calculated for other rare recipes like AGG, which will take like a year of selling AGGs to start making profit. Doesn't this seem too 
    much? Was this intentional? 
     
    I think the power per rarity level in the formula needs adjustment. It should be around 2 weeks of time to start making profit even for the rarest
    items. Any more than that and it is too much in my opinion.
     
     
     
  24. Like
    Lavayar reacted to Helios77 in Dec 7 Twitch Q&A - Let's Discuss!   
    I liked the game before, but with this last update, I do not like the way the game is going. We do not wish to grind endlessly to build our creations.  My corporation members and I will no longer support this game.  Good luck to you.
  25. Like
    Lavayar reacted to echa in Some serious questions for JC   
    While I'm sure he's an absolutely brilliant AI engineer, JC is utterly incompetent at game design.
     
    With pvp as broken as it is, there were really only 3 attractions to the game: mining, industry, and building. There's a reason everyone was building their own giga factory. There's simply nothing else to do.
     
    You could mine and sell ore then buy elements or mine and build your own industry. Most everyone opted for building their own industry because it's both easier and a more efficient use of time.
     
    They noticed that markets weren't being used so instead of adding more content to allow players to do something other than mining and industry they simply took away industry. You can't immediately remove 50% of a game's content overnight and expect everyone to be happy. I'm honestly shocked that NQ/JC either didn't anticipate the backlash or simply didn't care.
     
    What do we have now? Mine for ore, sell it,  then build stuff. There's literally nothing else in the game. No way to make money besides mining. This patch should have been deployed after implementing additional mechanics for making money. As it stands now, you mine to sell ore (at continuously dropping prices thanks to supply & demand) or you do... nothing.
     
    I'm actually impressed. I haven't seen this kind of incompetence in game design since no man's sky and even that was arguably more fraud and deceit than incompetence.
×
×
  • Create New...