Jump to content

Elrood

Alpha Tester
  • Posts

    147
  • Joined

  • Last visited

Reputation Activity

  1. Like
    Elrood got a reaction from WildChild85 in Not Punishing Desolation was weak and poor from NQ   
    I will defend NQ on this one - partially. Enough people argued for "this is fine" both on forum and discord - that i understand why the waters get so muddy that they decide not to punish people. It pisses me off too, but i do understand logic behind it. True test will be first report which will come after this announcement.

    The biggest problem i see is time they needed to react to that. If this is what we are going to expect in future - so "not on the list than fine" and week or longer to add to the list - this game for long months will be trolls feast. 

     
  2. Like
    Elrood got a reaction from michaelk in Here's the thing   
    Guys, it really depend on how much time one can play : ) 
    I totally get you - 20 dolars is really cheap - price for good indie game, far cry from AAA.
    But still if one have an hour or two a day 3 days of week, monthly subscription is bad idea (not to mention that playing DU is bad idea too in this case ? ).
    Same if you are short of cash to spend on throwaway thing, especially when you don't even know if game will be playable on your machine. Or is playable enough to spend dozens of hours - not everyone can stomach all the missing qol stuff of beta and could be turn away after few hours after all. With current economy i wouldn't ever assume that 20 dollars is easy to come by for everyone. 
     
  3. Like
    Elrood reacted to DenysS in Here's the thing   
    yeah, just few more years and he can try : D
     
    in general 20EUR for 3 months sub is totally ok, other games can bill it for 1. ESO sub costs 15EUR, WoW sub costs 13EUR, I see no problems to put 20EUR price tag for 3 months sub. Devs have to get money somehow now fur further development, as 90% of new players arrived on a hype train will never pay for a 2nd month in the current state of the game : D
  4. Like
    Elrood reacted to Zamiel7 in Here's the thing   
    I honestly think $20 for three months is an incredible bargain, but I can see how many might consider it a potential waste if it turns out they don't like the game in the first week.
  5. Like
    Elrood got a reaction from Heidenherz in PVP possible in "Safe Zone"   
    Finally. Hopefully nobody will be able to muddy waters again enough to get away without consequences in future ?
  6. Like
    Elrood got a reaction from Frigidman in PVP possible in "Safe Zone"   
    Finally. Hopefully nobody will be able to muddy waters again enough to get away without consequences in future ?
  7. Like
    Elrood reacted to NQ-Naerais in Clarification regarding Bug Exploits & Griefing   
    Hello Noveans, 
     
    We realize there is some confusion regarding whether certain scenarios occurring within the game world are permitted or not, and would like to clarify these points for everyone.  In short, we’d like to ask our community to use common sense when encountering issues in the game;  If it looks like an exploit, smells like an exploit (do pixels smell?), or sounds like an exploit, chances are it is an exploit. Don’t DU it! Report it. (support.dualthegame.com)
     
    We are in a testing phase, and reporting these bugs/issues is important. Abusing them may lead to sanctions against accounts, up to and including removal from the game. We will not take ignorance as an excuse, especially in the following list of scenarios. Please note this is not an exhaustive list, and we will continue to expand upon it as needed: 
     
     “ALT + F4 Emergency Brake”: We acknowledge this is not an intended game play loop that is being intentionally used by players. We do plan to counter this action in the future but do not consider it high priority at this time. At this time, we will not take action against an account for using such. [Allowed]  
    Parenting Ships - Dragged to PVP Space: This is a hot topic and one we wish to be very clear on. Intentionally parenting any construct without permission of the owner is not intended for game play. A fix has been rolled out that will address the ability to parent constructs together via the maneuver tool. As we have not previously clarified this point, we will not retroactively punish this abuse, as of this moment forward abusing similar bugs/tools to replicate this maneuver in its current state can result in disciplinary actions on an account. [Not Allowed]  
    Attacking PVP Zone from Safe Space: Our team has been investigating complaints of ships being attacked in the PVP zone from ships outside of the zone. Our investigations strongly point to sync issues between clients. We will continue to work with this feature, but recommend players consider the zone lines for PVP to be somewhat fluid and not absolute. Our version of the neutral zone! We ask that you continue to report cases with positions, but will not action accounts at this time, unless an additional exploit is discovered. [Allowed]  
    Overlapping engines with other elements (obscuring): A fix will be rolled out that will prevent this from occurring. No action will be taken, unless additional abuse is occurring. [Allowed]  
    Theft Via RDMS: RDMS permissions and settings are the sole discretion of each player. We advise you take the time to get to know and understand the system and be cautious when making a construct or element usable by unknown players, including the use of your friends list. Not every player has your best interest at heart. We can not get involved with permission based theft, whether as an individual or an organization. We encourage you to review your friends list each time you add or remove someone and ensure your construct permissions are set accordingly. The context menu options that set public access currently do not have a confirmation prompt, be careful as setting public access to said construct will allow every player in Dual Universe to go into build mode and remove/place elements and voxels.  [Allowed]
       
    Environmental Walls/Creations: Use of the environmental and voxel tools allows the manipulation of terrain on owned and unowned tiles. You may build walls or other structures up to the allowed height, however, said constructions must not:  Block access to market places or tutorials Must abide by our Community Standards (phallic/sexual, political, religious or otherwise offensive shapes are not permitted) Must not be placed with the intention of interfering with neighboring players. When players can not agree, the final decision on what is and is not acceptable is decided on a case-by-case basis by Novaquark staff.
       
    Marketplace Construct Parking: Players are permitted to park at marketplaces with their transport vehicles so long as they are not preventing use to other players or obstructing entry or exit points. Vehicles whose purpose is advertising (organization, service or otherwise) must abide by the following rules.  Organizations and individuals wishing to advertise at markets may have one advertising construct per district maximum Constructs may not be larger than “Small” sized Constructs may not block access to any building, entry points or dispensers Advertisements must not be placed/parented on constructs that don’t belong to you Constructs that violate these terms will be removed from the game without warning or compensation.
      Mass Manipulation in Transportation:  Players who utilize bugs to bypass the weight penalty of their inventory, other players, other constructs, etc to move mass through the game are not permitted. This includes: Adding additional mass to an already piloted ship [Fixed] Docking mass to an already piloted ship [Fixed] Circumventing linked container range [Fix Pending] [Not Allowed]  
    Item Duplication: Any method of duplicating items or resources (including quanta) is not allowed at any time. Any incidents of bugs of this nature must be reported to the support team at support.dualthegame.com [Not Allowed]
      Boarding without permission: As the deployment of the docking update is pending, the boarding of other players' constructs without consent will be considered an exploit. [Not Allowed]
     
    Offenders will be teleported away; repeat offenders may be subject to more serious repercussions.

    If you are boarded by another player and wish to have them removed, please report it either by creating a ticket here or by messaging @gm through the in-game Help channel.

    We do recommend you keep regular backups of your constructs in the form of blueprints in the case you lose a ship (through intended means (sale, give away etc),or undesired results such as in PVP, you can still recreate it later.    
     
    How to Blueprint (to save your ship/build):
    Enter build-mode on the construct, right click anywhere on the construct and click “Create Blueprint”. The blueprint will then be generated inside your currently active inventory. It is wise to keep hold of a copy of any of your blueprints inside your personal nanopack, as these are safe and not lost upon respawning.
     
    We realize this is not an exhaustive list, and we will expand upon it as time goes on. We also understand some members of our community feel that using a bug or exploit prior to acknowledgement by Novaquark is considered okay. We’d like to state that any intentional use of a  bug or exploit will be treated harshly going forward. This is the one and only warning we will issue on this topic. Please just don’t DU it! 

    On behalf of the entire team, we thank you for helping us make Dual Universe a better place! 
     
    Sincerely,
    The Novaquark Team
     
  8. Like
    Elrood reacted to Mordgier in NQ, Griefers are a problem   
    JC hasn't been griefed enough and hasn't griefed enough frankly.
     
    It was pretty funny to watch the most recent stream when he said that he doesn't want automated turrets of any kind....
     
    Right now the griefing is just for lulz.
     
    Wait till territories are on the line and EVE style warfare doctrine goes into full effect - you know - when you win by utterly demoralizing your enemies and causing them to quit the game in frustration through any means necessary. You know, the same way you won at ARK, ATLAS, Empyrion etc....
  9. Like
    Elrood reacted to blazemonger in NQ, Griefers are a problem   
    While I hear what you are saying, the problem here goes way deeper and is created by NQ's inability to handle support tickets. It's one thing to ask people to create tickets but when these then go unanswered and not actioned for three or more weeks there is really no point. NQ really needs to start acting like a developer with a released game (which they are) and _quickly_ improve their support system.
     
    And yes, they need to stop discussing and debating internally and start taking action as there is a number of serious issues around the game which will quickly become a problem as already the sense in some circles is that this is just another ARK/ATLAS grief fest.
  10. Like
    Elrood reacted to ByteSmarter in Space Map with Warp Cell Calculator   
    Hey Op 

    Not sure if you know about the Open Source initiative discord    (I am on it not part of the running of it) 
     
    https://discord.gg/sRaqzmS
     
  11. Like
    Elrood 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
    Elrood reacted to CasraTX in My first impressions of DU   
    It's not choosing the grind life, I was giving the Dev's an honest "first impression" that's all.  I'll dig a little deeper into the game proper, and probably join an org to get the most of the time I am in the game.   I think, for them, it would be useful to hear feedback from people in a beta, as honestly as can be done.  That's why I laid out my gaming experience, and my "first impressions".  First impressions are super important in life, you don't show up at a job interview in slacker attire, and then expect the boss to hire you because maybe later you'll be well dressed...

    (Not saying that applies here just an example of First Impressions)

    I know a lot of gamers whose first 2 hours with a game makes or breaks their interest in said gaming experience.   As I have a lot more experience in games and know that maybe if I push a little harder, there is a gem to be had I'll play out the beta, but my initial feeling on the matter is not a mostly positive long term view of the game.  Maybe it will change and I'll post an updated "My on going impression of Dual Universe".
  13. Like
    Elrood reacted to Dreamstar in Thank you, everyone.   
    Hello, it has been a long time for me, yet I still follow news of this game and communities progress.

    I want to say thank you to the developers that work tediously at their stations to create the very medium by which so many stories and tales will be told.

    Thank you to the supporting staff that help us with our issues on the forums, responding to tickets of all sorts of issues, be you paid or a volunteer.
     
    Thank you to the guild leaders that have carried on through the years you've done a great job keeping people interested in the game as well as constructing mini communities that keep people active.
     
    Thanks to all you independent contributors who endlessly pick at flaws in the rules, scratch your brains to conjure up new ideas to suggest, and keep the world of ideas turning.
     
    Thanks to the artist's that have drawn and rendered great art works.
     
    Even thanks to those I have clashed with in the distant past when I was active here, you've gone on to keeping people active and make it a better place when I could no longer do so. I hope all you old friends and frenemies are still doing ok regardless of who you were or are now.
     
     
    Really, good job to everyone, working on the team or just a fan of the game. You should be proud.
    Thank you very much for the efforts, contributions, and trying so hard.
     
     
    I want to say to you all, that even though there are many heated conversations, debates, arguments even, that you are still apart of the community which is Dual Universe. You are all connected in a great web of activity and even the guy that just tricked your group into a bad deal, the guy you just argued with for 6 hours about your' org's policies, the people you just dusted in pvp; you're all brothers and sisters in the Dual Universe. I tried to think of a less corny way of saying all this, but really all that matters is I'd like to see everyone smile more, and be a little less hard on each other. Have a nice day.
  14. Like
    Elrood reacted to Moosegun in What is the deal with voxel obstruction?   
    Not actually true, voxels SHOULD effect element placing and they do show as effecting it within build mode, if you place voxels behind an engine etc it will turn red, which is supposed to show it has reduced output, if you look in your reports when flying the ship you will see that all the elements should have reduced effectiveness due to being blocked.  This mean that whilst you ship can function, its performance is greatly reduced.  How well this reduction is working in game I couldnt tell you, slightly surprised you didnt show us an image of your actually flight performance, rather than this image, which really doesnt show us much.

    Trying to work out if this is a post based on actual evidence (that the output reduction is not working) or a knee jerk post from the new player.
     

    Here is a test i did, see one engine is covered by voxels and is showing red

    Here is the readout showing the element is 86% obstructed, which suggests that you conclusion that voxels do not effect element performance might be a little wide of the mark.
  15. Like
    Elrood reacted to XKentX in PVP possible in "Safe Zone"   
    Can we get official NQ reply on this ? WTF is "not intended game mechanics ?" It's a sandbox, it doesn't play by intended mechanics. Discord is not a thing to get data from, I am not to about to scroll 100500 pages.
     
    Do I get a ban for doing it ? If someone does it to my ship does he get banned ? We need a clear answer here.
  16. Like
    Elrood got a reaction from HairballHacker in Leaving safe zone is consenting to PvP.   
    I don't understand why is this even a topic...

    PvP space is a space where killing player is possible and actually should happen.... If you enter willingly - you face consequences. If you decide to enter again - you can die again. If you enter 10 times in a row, you can die 10 times in a row. Doesn't matter if this will be same player ganking a spot or 10 different players. Doesn't matter what the balance is, how disproportional forces are, is going to pvp zone even necessary, etc. All of that is beside the point. Whoever is in the pvp zone signed in blood and tears contract to be allowed to be killed and kill by solo action of being there.

    To be brutally honest - in this game if you can't run or fight, you are nothing more but flying paycheck waiting to be cashed in. if you are dying over and over again at the same spot, you are minimal effort, probably lucrative (and dumb) paycheck. End of story. 

    For the records, all that "doesn't matter" has an exception - exploits - those do matter, should never be used in practice and be dealt with by NQ asap.
  17. Like
    Elrood got a reaction from Aremes in The Point of Designing a Cool Ship?   
    I see it very simple - for now we build nice stuff cause its fun. PvP is not fully implemented, so even said cubes are not probably going to last. NQ will have to do something about that meta because if all the players see are meta ships and all the builders emigrated - this game will have neither graphics nor looks nor gameplay to find audience. Not to mention investment in voxel buildings going to waste. So i'm not worried and waiting for the future. 

    Edit: and lets be real - if they make building / playing pacifically / not pvp oriented role unsustainable, i (and probably other people too) will just emigrate to different games. Not the first time, nor the last.
  18. Like
    Elrood reacted to Frigidman in The Point of Designing a Cool Ship?   
    Interesting you bring up Empyrion (you had probably blown up a few of the POI I built or upgraded lol). It is a decent example of some of the struggles of trying to appease the pvp-vs-builders spectrum. However it was not all a single server everyone is on. This allowed a wider audience to play the game how they wanted to play (small servers, co-ops, single pve or just creative mode), but when they make sweeping changes that only benefit one side (the pvp end), they lost momentum.
     
    You mention their CPU addition... arguably it was one of the worst decisions they made. And as you said, it came about as an attempt to 'balance' the pvp end of things... however in doing so, it negatively impacted the creative side of things (the Builders). The devs had their view though, and refused to budge on the overwhelming negative feedback during internal testing, and pushed it out the door regardless that most all internal testers were against it and brought up bountiful arguments and alternatives to just make it a bit better without all the drawbacks alienating their creative audiences. Same thing happened with their method of making Shields require the most rarest of element in the galaxy to function. Another bad decision they ignored their internal testing group on... "it was for pvp balance".
     
    While Empyrion does do a lot of interesting things, and has more content (so to speak)... many of its growing pains, DU could learn from. And yes, the structural integrity system is a laughable mess  When they added it, it reduced almost every building into "a mound of blocks" because anything remotely creative would fall apart with just one hit. Space Engineers has ship-based integrity, which you can rip parts off for not building correctly... but that game suffers from a lot of physics issues too (maybe because of it).
     
    DU is in a unique position I think. It has Space Engineers, Empyrion, Starship EVO, Satisfactory, and even Intersteller Rift and Astrox to draw from.... seeing 'what failed', and 'what jives'. I'm not sure it could be compared to Elite Dangerous, Star Citizen, or any of the X series... just due to those are not 'builders'.
     
    Hope is a dangerous thing  
  19. Like
    Elrood reacted to ExPLiCiT in The Point of Designing a Cool Ship?   
    There are some really good points and perspectives here. 


     
    Keeping in mind that this is new territory and the overall design of the universe is key to an enjoyable experience for everyone I would like to state a few things I have observed and learned from previous games that follow along some of the same concepts as this game.


     
    The game that this reminds me the most of is Empyrion Galactic Survival. Empyrion had, and still has, some very similar challenges to its overall design. Some design challenges I think they solved pretty well, others not so well. 
     
    Similarly Empyrion was significantly focused on the pvp part of the game. Of course the most important thing to pull off for successful pvp is balance so you can keep it fair and possibly even more importantly, interesting. This is particularly challenging in a game where you don't have pre-designed mechanisms of warfare as people are excellent at finding workarounds to the ways the game was "intended" to be played and with a game that is completely open ended like this is it's virtually impossible to prevent any possible exploit creative people that play these games can think up. 
     
    One of the first major issues in Empyrion pvp was the same thing we see happening on here in DU. In order to be competitive, people are just building bigger and thicker bricks because at the end of the day pvp skill takes a back seat to it being mostly a numbers game, whoever has the most blocks and guns wins. There were a couple of ways this was addressed in Empyrion, first they implemented a system to discourage this type of building with a system they called "cpu" which basically had a certain amount of points that were available for a build and each item you added has a point value. Once you pass 100% of the available points you start to see a proportional degradation to the performance of your build. 
     
    The second thing they did that had a big impact was the implementation of shields. Shields used two consumables to function, one was a mineral you would mine and the other was power to recharge. These changes greatly reduced the number of bricks flying around because they simply wouldn't move because they were over the max point value so much. The shield actually allowed a ship that was not specifically designed for pvp to survive long enough to escape the pvp area and get to a pve zone. 
     
    Something Empyrion could do better is with the zone design. I don't have any great ideas to offer here. This is a hard balance to maintain as well. If you give people a pathway where they can completely avoid the pvp zone it kills the pvp experience. On the other hand if you force everyone to go through pvp you are going to hurt the player base that just enjoys the building or other non-pvp aspects of the game. 
     
    Empyrion has "systems" that you warp into and in some of those systems you may have all PVE areas and some all PVP and others a mixture of both. One key to the systems layout are the warp paths. They use some that are one way and some that are longer so you have to have a bigger warp drive to use them and this allows some gating to help keep things under control for balance purposes although it definitely has an impact on the game immersion. 
     
    Another aspect that I really think DU could learn from Empyrion is the Creativity Mode. You could always start the game in single player and select creative mode and it would allow you everything available to build with. Then you can save your blueprint and use it in game. I don't understand why they don't implement something similar DU, it sorely needs it, it would only improve the ship designs in the game and make it more imersive.


     
    Another game this reminds me of is Satisfactory. I think DU has this aspect of the game down pretty well without overdoing it. I always thought that Empyrion made this part of the game way too easy. Don't get me wrong though there is still some big improvements that I think that can be made with this part of the game as well. Its definitely a bit sloppy in the tier design.

     
    The game physics of DU is done really well too. Punishing but doable. I know it is probably really complex to implement properly but I would love to see some level of structural integrity implemented as well. Empyrion did not do this well but it also didn't have as robust of a physics engine as DU.  I could just imagine a big ship tearing itself apart by someone making the structural framework too thin and placing gigantic thrusters on it and someone doing a turn and burn too fast. I know I am probably asking too much with this though. I always wished while playing Empyrion that it had the impact on flying from the physics of the ships like DU does and it's one of the many reasons I switched over to DU.
     
    Mining in this type of game is obviously a major part and I think it's particularly important to keep it interesting. The game I believe that has done this best is Elite Dangerous. It has the perfect ratio of complexity to reward. Think core mining…
     
    Anyway I am getting off subject of the OP here so I will stop but as with any game in development you hit a point where your decisions have a major impact on the future course of the game and to me it looks like that's where Dual Universe is currently. The more successful indy games I have seen, and I play almost exclusively indy games, have all listened carefully to the feedback of their communities and made smart decisions based off that feedback.
     
    I wish Novaquark the best, I know it has to be the most challenging genre of game to be designing. Good luck!

     
  20. Like
    Elrood reacted to blazemonger in Why does NQ-Sophon own 37 Tiles on Alioth?   
    https://clips.twitch.tv/EmpathicRenownedHarePogChamp
     
     
    There..
  21. Like
    Elrood reacted to Mordgier in The Point of Designing a Cool Ship?   
    I don't think it's as bleak as that - but I do think that the devs have more ideas about WHAT they want rather than HOW they want it.
     
    The devil is in the details and we just don't have them - especially in how territory control will work, what will and won't be PvP.
     
    The fact that we do not know what will happen to Alioth is something I just can't wrap my mind around. Will it be pvp? Will it stay pve? What is this nonsense about half and half? How can we be committed into building large structures if we don't know what will happen to them?
     
  22. Like
    Elrood got a reaction from CptLoRes in The Point of Designing a Cool Ship?   
    I see it very simple - for now we build nice stuff cause its fun. PvP is not fully implemented, so even said cubes are not probably going to last. NQ will have to do something about that meta because if all the players see are meta ships and all the builders emigrated - this game will have neither graphics nor looks nor gameplay to find audience. Not to mention investment in voxel buildings going to waste. So i'm not worried and waiting for the future. 

    Edit: and lets be real - if they make building / playing pacifically / not pvp oriented role unsustainable, i (and probably other people too) will just emigrate to different games. Not the first time, nor the last.
  23. Like
    Elrood got a reaction from Frigidman in The Point of Designing a Cool Ship?   
    I see it very simple - for now we build nice stuff cause its fun. PvP is not fully implemented, so even said cubes are not probably going to last. NQ will have to do something about that meta because if all the players see are meta ships and all the builders emigrated - this game will have neither graphics nor looks nor gameplay to find audience. Not to mention investment in voxel buildings going to waste. So i'm not worried and waiting for the future. 

    Edit: and lets be real - if they make building / playing pacifically / not pvp oriented role unsustainable, i (and probably other people too) will just emigrate to different games. Not the first time, nor the last.
  24. Like
    Elrood reacted to blazemonger in Power Consumption - Starting Areas   
    Ark is not voxel based in the way DU is, is it a game purely based on elements you build with and thus technically vastly more simple. As previously said "I spend a small fortune" means very little in this regard if your focus is fairly basic games computing wise. No one here will deny DU drives your PC harder that most other games and for good reason, while yes, over the course of beta optimization will improve performance , this is what beta is for, but it will always be more demanding. 
     
    NQ is aware that the game needs these optimizations and it is something they are working on (as they have said several times). That said, this is not a console level game and your hardware will need to do work.
     
    Back to @Tashkin
    For reference, I run a 3700X with 2080, 32GB 3200 RAM and NVMe storage running at 1440p. CPU usage is around 65%, GPU at 75-85% with temperatures sitting around 75 Celcius overall (all fan cooled, no water) when running DU. FPS around markets is roughly 28-35 fps and elsewhere mostly capped at 60. In my in-game circle this is the general experience and I know several people running te game on 3rd gen Intel even quite comfortably.
     
    Unfortunately I could not see the DXdiag file but from what you said I take it you do keep your gear up to date but from the symptoms you describe I frankly suspect there is some sort of hardware issue in play here, possible GPU or PSU which is  triggered by the game. If I were in your position I would try and find a ways to test this with replacement hardware to see if that can exclude hardware as a possible cause. Specs wise your system should not have any issues running the game at all and if the spike is as bad as you say it is then yes, from what I know now I'd suspect a hardware issue.
     
     
    It is easy to say "DU is breaking my hardware" when in reality DU is exposing/triggering a fault which is breaking your hardware
     
  25. Like
    Elrood reacted to blazemonger in Leaving safe zone is consenting to PvP.   
    I like these two comments a lot actually. and I agree.
     
    What we currently see in space is purely PVP, it's limited because the mechanics are half baked and there are no current countermeasures but I trust NQ will bring those in over time. People who cry about players shooting at haulers need to stop, I mean seriously.  This is not griefing and by entering the game and flying your ship through space you by default consent to the possibility of being attacked. No ifs or buts here.
     
    I think planetside PVP will be the gamechanger like Anderson said. I also think that it will not be the mechanic us "law abiding" citizens should fear as it will indeed be what will give the PVP groups what they want/need and by that their attention will shift. From the little information we know I'd say it will be expensive but fairly easy to build solid defenses in case the occasional rogue groups tries to go around and attack random groups on the ground but I expect that to be the rare exception more than a rule.
     
    Territory warfare will be _very_ complex and costly for those attacking and so they would prefer to direct their efforts toward where they can actually get a fight worth their time I'd imagine
×
×
  • Create New...