I for one am happy they stuck with their decision.
Accept it and keep playing, or move along as you all proclaimed. But it's time to stop throwing a tantrum all over the community because you didn't get it your way.
Its done, over. The amount of attention drawing is just really bad for any normal discussion. Heck, even Duscussion is back to normal and having good conversations again.
I think the awnser is in the term used: you can't scam or threat a USER i.e. the human being, not the character : in the game, scam and threats are allowed, it's in real world that it is forbiden I think
Just not sure what you are deeming 'clearly unacceptable' that is all, presuming you are referring the that little gobshite that got banned, personally I find that perfectly acceptable. Think you are confusing personal opinions, with matters of fact. All good though, I am presuming English is not your first language.
If it was written in something resembling English I would. Using the words like 'clearly' and 'obvious' in such an ambiguous statement is quite amusing actually. Think the irony might have been missed on you.
I've been playing for almost a week now and I am very glad with the game in its current state!
It has been captivating and awesome and as a silver founder I must say: money well spent!
So thank you Novaquark for the great game and the fantastic job you did!
If you don't want player perspectives, this isn't the place to post...NQ isn't going to look at most of these posts, never mind reply.
Terms like these are designed to be vague.
Yes, "inappropriate" can be literally anything. There's no way they will lay this out explicitly, ever. This is industry standard and normal. It is meant to be subjective because the line for inappropriate is often vague, even in real life courts.
This is how most MMO terms are worded...whatever devious stuff you have in mind, you're probably fine...
I am taken to understand that the first statement in question refers to overt attempts to insult or threaten another player through the use of hateful language pertaining to the mentioned categories. I agree that things should be as specific as possible, but this reads like most statements made in codes of conduct that are designed to remind players that they should not use bigoted language or make actual threats against the players themselves. I do not think it refers to threatening to destroying someone's ship, org, etc. if they, for example, don't pay you a ransom. I fully expect at some point for certain organizations to, say, threaten smaller organizations or individual players with destruction if they have the strength to do so. All part of the sandbox experience.
The same goes for the other rules. I think they refer to the real-world players themselves, not their avatars in the game. So, don't try to steal someone's password by imitating an NQ employee, for example.
You guys are so stuck on this idea that the ban was some sort of punishment for a theft.
When NQ makes a mistake that creates an exploit. And a player discovers that mistake and exploits it, then posts publicly about it. That hurts the game and makes NQ look bad. That's why it's in the TOS. Because NQ doesn't want you to do it.
The ban is to remind people not to do stuff that NQ specifically asked us not to do.
You can't honestly believe that? A player having access to something that they absolutely were never intended to have access to, by the developers of the game.
It's not that the player USED an exploit to access the market. It's the fact that they were able to, that IS an exploit, to anyone who knows about it.
For all we know, at one point all the markets had open RDMS, and NQ managed to fix them all except market 15. I think you can see how it's important to keep something like that quiet.
NQ specifically put wording in the TOS, so when they screw up like this, players don't go post about it on Reddit.
I agree 100% that the entire concept of RDMS theft, with a flawed RDMS system and no pvp, is a mess, and is doing nothing but reflect badly on the game right now.
That doesn't change the fact that intentionally violating the TOS gets you banned.
Counterpoint: Dismantling an NPC Market hub most definitely is an exploit and at no point during the heist should these players have thought this was anything but a bug that they were exploiting. Any claim otherwise is just guilty children using the ignorance excuse to gain sympathy so that their parents won't punish them. The permaban is unfortunate but necessary to prevent this kind of behavior in the future. The offenders may even get the chance to play again after Beta if they are lucky.
This doesn't sound like a supposition but like a fact
I mean, if (for exemple) they came with cargo, put the voxel/elements inside, and come back to there base, and repeat 20 times the operations... it's far far away from, they get ban because they have press B
1. I would actually apply common sense and know that having edit powers at any market was likely a bug and report it.
2. I would then not use said edit powers and continue on as normal.
If folks are unable to apply common sense in these type of games, then the community is better off without them, as these type of players are the ones that create more issues and want to fight, kick, have a tantrum and use the time worn excuses of "but the game let me do it", "the coding sucks! redirect blame to devs!", "my definition of what a bug and exploit is different from the majority of common sense folks, but that doesn't matter!", "where was my warning!! QQ"
Devs gave a very clear warning weeks ago regarding using unintended gameplay and the use of bugs / exploits, even if the devs had not yet acknowledged it, yet some folks feel their lack of common sense and playing the ignorance card should absolve them from punishment. We don't need these folks in this game.
The fact that they Wrote Pls NO Ban, just shows that they knew that they would be banned. This is a Beta game still in development and part of being in the beta is your testing and reporting bugs. Which you should have reported it and left the building alone. This was just a blatant bad response that you knew would affect many things in the Beta, but you choose to do it anyway.
You knew you would be banned and you still did it. So you deserver what you knew was going to happen in the first place.
While I find the heist inacceptable, I am shocked by the decision of permaban, a temporary ban (even long) would be better imo. Maybe this has been done to send a message to players for future abuses...
You know markets are not player structures and this rule does not apply here, common sense should have stopped you from stealing anything.
Man guys, be real. They didnt expose a bug. They broke down a market including its link to the market system which was never intended to be deleted. They never reported, they only intended to break and remove.
Hilarious would've been leave it intact but add something funny to it like a meme.
By now, some of you may be aware that unlucky number Market 15 has been stripped bare and left to create ugly memes for generations to come. We’re trying to look at this in good humour as, from the front, it appears to be an issue that was created when we moved the markets, making it editable by players. It doesn’t take a rocket scientist (though we know there are a few of you out there) to understand that the markets are not a community construction, and as such not intended to be handled by players on this level. The destruction of the build isn’t a quick fix, and was clearly done knowing it shouldn't be.
An important aspect we are considering in all cases and investigations is intention. The intention behind this destruction is very clear to us. The players involved did not report this bug to us, but instead simply filled their pockets. Had this stopped with a single voxel removed it would be a different story. This is, at its core, a violation of the EULA and against the intentions of beta. We have been as understanding as we can until this point, but there must be a line.
Let us be clear, we will not tolerate this kind of behaviour during any phase of the development of Dual Universe.
The players responsible for the destruction of the market have been permanently banned from Dual Universe, and all salvaged materials and assets gained will be removed without compensation.
We realize that the districts currently represent a performance bottleneck, as they are a choke point between markets, institutes and the starting zone for new players. Even though we have identified performance improvements that should help solve these issues, we have decided to make some immediate changes to prevent these issues from affecting the performance for new players, until we are able to roll out these improvements.
So we are making some temporary changes such as relocating many of the services you have come to know and expect in the districts:
New Stand Alone Markets will be moved approx. 2km from the associated district.
All pending orders will be transferred to the new market building.
All institutes will be removed from districts on Alioth and Sanctuary.
A new single Institute will be created near the Arkship.
Transport stations will be placed at all Districts to be able to get to the Institutes.
The institutes appear to be a heavy driver of performance issues, by removing them from higher traffic areas we hope to provide a better market experience for players overall.
Players who log out on one of the affected construct buildings may find themselves in a new location when returning post maintenance. We recommend bookmarking important places and construct locations.
As always, we appreciate your patience while we work, and welcome your feedback on the changes and performance improvements once complete. We expect these changes will occur over the next few update cycles during October.
Feature request: Reduce factory noise.
Any chance you could make the factory pieces not transmit sound past a certain amount of blocks. They overpower a lot of base designs with the amount of sound they generate. I always here in discord, "one second I am in or near the factory, I can't hear you".
....and if it were - that would NOT be a good thing!
Alright, this is something that's been bothering me for some time from JCs interviews - and yesterday's Easter Egg interview pushed me over the edge to where I really have to say something...
First and foremost - lets face the reality that Ready Player One would be a horrible game for most people nor would the events of the book be enjoyable for the majority of the player base.
If you're looking at your game and going "Hey look it's just like RPO!" - what I hear is "Look just a handful of no lifers got to have all the fun!". That IS the plot of RPO. A tiny group of players who take the game WAY WAY WAY WAY too seriously get to enjoy the content while the rest may as well not exist.
The puzzle fit this perfectly. A tiny group of players with tons of NDA covered information going back to pre-alpha solve a puzzle while the rest of the community doesn't even know it exists. Yup - very RPO.
JC - The takeaway from the puzzle event should not be that "Look it was just like RPO! I did a good job!" - it should be that "The event was a complete failure and did not involve the majority of the community in a meaningful way, and was solved largely due to a bug and alpha knowledge that was not available to the bulk of the community."
Please please please for the sake of DU - stop trying to make this game like a fictional book about a fictional game. You need to take a serious look at how other games have done community events and have managed to involve much larger groups of players. Everything from opening the gates of Ahn'Qiraj to the Elite Dangerous Alien events - yes they were flawed in their own ways - but by and large involved more people.
Finally, this doesn't just cover events. The general game mechanics cannot be based on the mindset "Just like RPO!" - because I repeat - RPO would be a horrible game. In fact, I would go as far as saying if you are looking at any upcoming part of the game like AvA or Territory Warfare or Atmospheric combat and can say "This is just like RPO!" - then it probably needs to be reworked to NOT be like RPO but to be like an actual game that would be fun for more people.
Once again - face reality - RPO was a no life simulator where only the no lifers mattered.
This means designing things in consideration of players who are not willing to play DU 16 hours a day - and entire teams of such players.
Players are not going to want to spend hours waiting for an attack on a territory to man the manual guns because you refuse to consider automatic defenses.
Players are not going to want to fly dedicated gunners or escorts for every single hauler just because they may get attacked.
Players are not going to want to sit in their ship for hours watching the radar while their orgmates mine out an asteroid.
I could go on and on, but the core point is that the game mechanics need to be designed around giving people who do not no life the game a chance to have fun and stand a chance in pvp vs those that do.
Whats with these oversized furniture? 3 meter(12 blocks) long beds, 4 meter wide couch, 1.5 meter wide doors??? A normal chair is 50cm (2 blocks) wide, so why jumping to these "big mom Olin" sizes?
Not really possible to make normal crew-quarters on your ship now. Or a high building with appartements, or a hotel.
There is no reason for NQ to make these mistakes and I think it is really stupid that these elements have these stupid sizes.
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":[]}
This is correct. The reason for the delay is that it's currently a bit hard to keep up - some people don't seem to have read the forum rules and working through reports takes its time.
The forum rules can be found here:
Excerpt
This is the most relevant forum rule and reason why that thread was hidden. @Cytoxx please remove the link and title-quote from your original message - thanks! The only two things to do with exploits are 1. report them to NQ and 2. avoid using them.
I'd like point out this recent announcement by @NQ-Naerais
A bug or exploit does not need to be listed there in order to be prosecuted if used, so I'd strongly advise to always report at support.dualthegame.com if there is any doubt. I'm not familiar with the development schedule, but I'd think that every issue gets tracked, assigned a priority and will be taken care of as soon as possible. If the support is delayed or the issue doesn't get fixed within a day, a week or even a month it doesn't invalidate the forum rule posted above: promoting exploits is prohibited. As posted by @NQ-Naerais: exploiting is prohibited as well.
I'm not a GM or CM, but as it's hard to keep up with the frequency of forum rule violations, I imagine it might just be as time consuming to keep up with in-game rule violations - so I'd personally advise to not mistake it as okay or not prosecuted to use an exploit, just because there's no notice or sanction shortly after. Analog to this I too have my todo list for threads and members to address/sanction for spamming, trolling (provoking), flaming (insulting, attacking), profanity and so on. Meaning: better not let yourself be inspired by other's violations
If it's just one answer on a thread the moderation team would just hide that one answer. But if others already quoted and commented it can get very difficult to clean up. If it's too difficult or the original posting already violates the forum rules (which can't be cleaned up either) the according thread will be hidden entirely. Sorry for the resulting inconvenience - in order to avoid it I suggest to just report clear violations of the forum rules and not engage the violation on such a thread.
If there's a valuable discussion to be had in line with the rules it's always an option to open up a new thread for this. But as thread starter be mindful that if it's a sensitive topic it can easily get out of hand and might be treated as mentioned above - so it's in the interest of everyone who wants to have a friendly and open discussion, to not engage to provocation, but just report the according comment and keep it calm and polite otherwise. Thanks!
@LouHodo no, he just wasn't sure why that thread was gone and made a suggestion for improvement. Your posting reads as if you think this was wrong, but please try to see the positive side of it and keep it constructive, as I take the positive from your posting and agree that issues with moderation should be taken to moderators and not discussed directly on the forums. Two reason for this are that 1. the moderation staff can miss such topics while 2. members on their own can't answer the question "Why?" or solve issues or change how moderation is done without talking to us, the moderators.
Next time something vanishes and it's unclear why, please just send a private message to me and I'll look into it to provide an answer