Jump to content

infyrno

Member
  • Posts

    7
  • Joined

  • Last visited

Reputation Activity

  1. Like
    infyrno reacted to Leniver in Would you want voice chat in the game?   
    No, we have discord/guilded. 
    And they plenty other things to work on.
  2. Like
    infyrno reacted to NQ-Wanderer in NEW BLUEPRINT TOOLS AT LAUNCH   
    Rebuilding Helios will be easier with our enhanced Blueprint Deployment Tools.
     
    bptoolv3.mp4  
    You can omit any element, swap missing honeycomb, and soon after launch, snap blueprints to deployed cores.
     
    What will you build first, Novean?
  3. Like
    infyrno reacted to Hirnsausen in Export: Construct to STL and other 3D Formats   
    - reminder -
  4. Like
    infyrno reacted to Hirnsausen in Export: Construct to STL and other 3D Formats   
    It would be fine if we could export our constructs to the STL format, or other 3D formats. This would allow us to 3D print our best creations (and some of us to paint them as well). An eüport from virtual world to rwal world. Heck, I really want that.   ?
  5. Like
    infyrno reacted to NQ-Deckard in INSIDE NOVAQUARK: DEMETER EDITION - Discussion Thread   
    Hello Noveans!
     
    We have just released a new video where we go over some of the changes that are coming with Demeter, and how we intend to aid our players in the transition.
     
    We would love to hear your feedback, and thoughts!
  6. Like
    infyrno reacted to NQ-Deckard in Apollo & Ares - Question Thread   
    Hello Noveans!
     
    We invite you to pose questions about the Apollo (0.26.2) and Ares (0.26.12) updates in this thread. We’ll answer what we can in an upcoming, postmortem-ish Q&A.
     
    Thank you all for your support!
  7. Like
    infyrno reacted to Devian in IT's a Trap Trap - AAR Battle report   
    Detailed after action report of todays fight Zombieland vs Legion includes great video Google Doc to AAR
  8. Like
    infyrno reacted to aliensalmon in Thank you, Dual Universe.   
    I'll just keep it brief: this may not be the best game with highest population of people. But I still enjoyed my time playing this game. I had a blast building stuff and exploring the planets and space.
     
    And in some ways, it opened up my curiosity for other types of games.
     
    I'm thankful to have played this game. Thank you, Dual Universe.
  9. Like
    infyrno reacted to space_man in LEGION has won DU   
    lol winner of what contest, who can spend 50m on a junk scanner?
  10. Like
    infyrno reacted to NQ-Deckard in PTS DATABASE UPDATED AND NEW FEATURES ADDED   
    The Dual Universe public test server (PTS) is reopening today with a preview of some of the improvements and additions that will make their way to the live server in a few weeks. Before we dive into these changes, we’ve got good news for you!

    A long-standing request from our community, this week’s PTS update includes a database refresh that mirrors the state of the live server from August 9. Players who created characters on or before that date will find them, and all their beautiful possessions, readily accessible. This should make your time on PTS more enjoyable. 
     
    Players heading to the PTS today will be able to test the following main changes:
    The docking and boarding revamp revealed in this devblog. Several changes to the way warp drives operate. Optimized planet operations. … and many more bug fixes and tweaks which can be found here.
     
    We would greatly appreciate constructive feedback on the docking and warp drive features in particular. How well do the tools work? Is the interface user-friendly? How can we make them better? Your input now will help us get them in tip-top shape before they are integrated into the Live server. 
     
    ABOUT THE TEST SERVER 

    Please note that PTS is solely for testing purposes and may go offline at any time without prior notice. We’d like to manage expectations that PTS updates may include feature prototypes and gameplay changes that might not ever come to the Live server. 
     
    On PTS, you’ll have some perks to help you speed up your progress and have an easier time getting the things you need: 
    Dispensers that provide most items for free at the Alioth institute district (11) by the Arkship. A daily allowance of one billion quanta. Teleporters at the Market 1 of each major planet, allowing for free teleportation around the universe. For Alioth, the teleporter is located by the dispensers in the Institute District (11) by the Arkship. Three additional teleportation platforms are deployed in space on the edge of the PvP zones. Players can respec Talent points at-will. (By typing /respec in chat) Use of the Fetch feature with no limit of range or cooldown.  
    Feedback and bug reports related to PTS should be posted in this designated forum, which we monitor closely as part of our commitment to improve the quality of the game and its performance. 
     
  11. Like
    infyrno got a reaction from Cabana in Legion   
    That's the best time to join.
  12. Like
    infyrno reacted to NQ-Deckard in DEVBLOG: PVP COMMUNIQUÉ   
    Okay, PvPers, this one’s for you! 
     
    As foretold in The Future of Dual Universe Part 3: Finding the Fun and the recently-published 2021 roadmap, we’re adding some new toys to your arsenal and making some of the existing toys even better. 
     
    These changes will come over time and in waves. Here’s what you can expect in the first pass as part of the Apollo (0.26) update, now available on our public test server. 

    New PvP element: Shield generators
    Shield generators serve as the first layer of protection for dynamic constructs during combat. Shields will absorb damage until depleted, at which point they will stay depleted until the end of combat as defined by the combat timer. Future plans will bring shields into play for space core units, but for now, they will only be available on dynamic constructs. 
     
    These elements come in sizes XS through L with different shield strengths respective to their size and are not restricted to core sizes.
     
    This is the V1 technical phase of introducing shields, making sure that the foundation is solid before more bells and whistles are added.

    Honeycomb Health Points
    Adjustments to honeycomb HP--rebalanced and significantly reduced--have been done in the context of the implementation of shields and the weapons rebalance. With these changes, we should see a general reduction in fight duration with a faster time to construct destruction (TTCD).
     
    Specifically, gold has been brought back in line. While still technically the highest HP honeycomb, it will now come at a serious mass cost compared to other options. This should open new options in honeycomb selection when building ships and reduce the feeling of “gold or nothing” players may currently sometimes feel.

    Weapons adjustments
    We found that the disparity between the smallest and largest weapons DPS was too great. This made it difficult to justify using anything other than L weapons. While L weapons previously did roughly eight times more damage than XS weapons, they will now only do just under three times more damage than their smallest cousins. 
     
    Revised damage per second:
    XS weapons: On average, extra small weaponry will do 2.5 to 3 times more DPS. S weapons: On average, small weaponry will do 1.75 to 2 times more DPS. M weapons: On average, medium weaponry will do 1.5 times more DPS. L weapons: On average, large weaponry will do about the same DPS.  
    In regards to DPS, larger weapons of the same type will take more time to cycle, allowing larger weapons to keep hitting hard per round, at the cost of rate of fire.
     
    Lastly, adjustments have been made on various other parameters, such as reload times and weapon capacity, to help balance overall damage output, and how it feels to use.
     
    We hope this brings more diversity to the offensive options you have when building ships and allowing more space for smaller ships and weapons.
     
    Cones
    Weapon cones’ optimal and falloff values have been augmented by about three times (with the exception of missiles). This change was based on feedback from the community who said that weapon cones felt too limiting and restricted flexibility when designing ships. 
     
    We hope this allows more options when placing different weapon types, having more weapons that approach similar levels of flexibility to missiles with weapons like lasers and cannons having a much wider range of operation. Railguns will remain relatively limited as a heavy sniping platform but will still enjoy more cone than before.
     
    Tracking
    Tracking on weapons is the ability to reliably track and hit targets. Weapons with improved tracking will generally have better chances of hitting targets, especially smaller and faster ones.
     
    There was always an intention that smaller ships should be able to evade damage from larger guns. In practice, this was not the case as L-based ships easily dispatched smaller constructs at various ranges. 
     
    To help correct this, tracking has been significantly decreased across the board by about half on every weapon. These initial adjustments will help, but likely not suffice in helping smaller ships survive combat engagements. We will continue to look at other options for further iteration in future updates and already have plans on the subject.
     
    As a parting comment on weapon balance, please note that this is more of a global rebalance to address macro issues we had across the board to set a healthier base. Expect more specific adjustments to come later for specific weapon changing and balancing as more PVP-focused releases are done in the future. 
     
    Opinions Wanted
    As noted above, and in nearly all of our communications, player feedback is always welcomed and encouraged. Particularly as these changes are on PTS where you can experience them first-hand, your opinions go a long way in helping us make needed adjustments before Apollo (0.26) debuts on the live server. Please take a moment to share your thoughts in this thread.
     
  13. Like
    infyrno reacted to Mulligan in Legion   
    Hello all, I thought I would take the time to write up a summary of the events that led to the creation of Legion. For those unaware, Legion is a new pvp alliance formed from a group of former Ascendancy member orgs and as far as i know, Legion is the largest alliance in DU at the moment in terms of character count and fleet size.

    The buildup to this mass exodus from AC was a long time coming. The first issue being an issue of many orgs in DU, afk leadership. AC was plagued with afk leaders and in a few cases entire organizations that had moved on to other games but did not step down from leadership or giving up their voting powers. These people would continue to vote on important issues and control the actions of the people who actually play the game. For example a vote was started in the AC to ban the shooting of ships in pvp space unless they shot first, effectively banning any control over space resources. This was voted on and decided by people who primarily didn’t play the game.

    After months of only a small subset of AC players actually helping and contributing to the growth of the Alliance the catalyst for the departure was the announcement of asteroids. ATOM attempted to pass a vote requiring all AC orgs to participate in the defense of space assets. This vote ended up failing with the majority of the votes cast by players who no longer played the game or who never participated in the alliance in general.

    Du has evolved from the beginning, while at first the only thing that mattered was the amount of people in your du community org, to building as a group because of a lack of a market, to single players being able to buy and build everything possible in the game. The game has changed from being a solo minecraft type game to a game where cooperation matters and more, specifically pvp Space matters. Having easy access to missions, the best way to make money in the game. To asteroids in space having the most valuable resources in the coming patch. Knowing who has your back, who will be there to help you without thinking, and who is in it only for themselves has become extremely important.

    Legion was founded on these ideas. Getting access to the best income and resources in the game through pvp. Creating the largest fleet to ensure the safety of this income. Generating content to keep people engaged and playing. And generally improving our skill and members standing in the universe.

    Signed,
    Mulligan
    Super legate of ATOM
    Executive of Legion
  14. Like
    infyrno reacted to DarkHorizon in An announcement on the recent scam activity on the DU Discord.   
    Hey everybody,

    We've been getting increasing reports of non-game-related scams through the DU Discord server. While in-game scamming is permissible (here's looking at you people putting requests and buying ore at 0.1h/L) these types of scams are not. 

    The two we’re seeing most frequently on the DU Discord and Discord in general are along the lines of:

    "I accidentally reported your Steam account"
    "Straight to the point--NO BS" with Wall Street War

    At almost 2000 words, I’m told this is a fairly lengthy post so I’ll split it into two parts. The first will have to do with what these scams are, and how to deal with them. The second part will feature some of my own tips and advice to improve your account security and general account security on the web. Part One, Part Two.
     
    TLDR at the end for those of you that need it. Let’s get to it!
     
     
    Part One: The Scams

    The steam account scam is the most concerning so I'll touch on this first.

    It starts with someone asking if a Steam profile is yours, usually because you have your Steam profile linked in your Discord profile. If you confirm it, then they'll say that they accidentally reported your Steam profile for illegal purchases and that your account will be suspended if no action is taken. They'll then try and connect you with a "support agent" to resolve the issue and clear your name. Seems innocent enough, but hang with me.

    The "support agent" will ask you to log out of your account everywhere so they can run a "validation" process that would be supposedly interrupted by a login. They’ll then ask you for your login details as a part of the verification process. This should be the first red flag.

    If you have Steam Guard enabled as your 2FA option, they’ll mention that your phone should have sent you a notification just now, and to provide them with that code. This should be a second red flag and tantamount to a bat light up in the sky.

    Since most of us have a purchase history associated with our account, an excuse will usually be given about the above "validation" exposing errors due to “black-market purchases with stolen accounts”. You'll be asked to purchase crypto and send it to their wallet as proof that you're the cardholder. This should be the third red flag, but by now your steam account is already compromised since you provided your login details and 2FA code.
     
    With your account now up for ransom, they'll start asking for money to get it back and bring you through a whole long process to ‘unlock’ or ‘unblock’ your account. A way to unscrew yourself, notice how I’m saying ‘un-’ a lot?
     
    However, the end result, is always the same, "a fool and his money are soon parted." In the particular instance that was brought to our attention, the individual’s bank intervened which left them out only $270 as opposed to the $1,000 that they were being lined up for.
     
    For support-related things like Steam, Amazon, DU support tickets, etc, they'll all use some form of an official means of communication. Since DU makes a prime example, all support tickets are handled on their support website. If something like an email seems suspicious even though it looks official, don't hesitate to sign into your account and handle it on their website directly, email support scams happen too!
     
    Additionally, support will never ask for your login details, and you should never ever-ever-ever-ever-ever give out your 2FA codes. Never, got it? I could say that until I'm blue in the face. If they needed to, I'd bet a ham sandwich with good mustard that an authentic support person would easily bypass that since they're already an employee of the company, verified and all. That, and they would never ask you to pay them since usually you'll just be banned anyways.
     
    NQ threw in this extra bit but just so things are crystal clear: No support agent of any company (especially Steam) should ask you any login details. If a support agent does, it's obviously a scam or an agent breaking security protocol. In both cases NEVER gave such information. Support agents are not supposed to ask you any login details for the simple reason that they don't need to: if they are what they're pretending they are, they have other means to access your account for technical problems or punishment topics.
     
    These are malicious because these types of scams depend on our anxieties. Naturally, no one wants to be in trouble and it’s something commonly pulled on senior citizens as they’re usually an easier target since we’re still at the start of the digital age and computers weren’t something they grew up with. Have your grandparents console with other trusted persons if something seems fishy.
     
    A final red flag and something I’m hesitant to get into because it involves stereotyping, but folks that pull these kinds of scams in my experience don’t often have the best English skills, and it’s pretty easy to tell that they are non-native speakers since they come from a very particular part of the world. I’m not going to spotlight any group in particular, but Steam would never ask for you to share your screen, make a crypto transaction, or inquire about your banking details. And I’m equally sure that a prince in some distant land wouldn’t transfer you a great sum of his wealth to help him with some health expenses. Yes, they have a solid grip and can hold an understandable conversation, but it’s not as fluid as more experienced speakers like with NovaQuarks support for example.
     
    I’m not saying that everyone offering some form of tech support is out to get you, especially if you are aware of a real problem, tech support is an easily outsourced job and I’m sure at some point you’ve experienced robots and automated machines in these roles already. No doubt many non-native speakers have excellent skills as I'm sure many have shown on these very forums, but again my personal experience has dictated that more often than not, a weak grasp of English should serve as caution in these types of situations.
     
     
     
    Finally, someone or multiple someones has been making alternate accounts, pushing a 'no bs' crypto server very aggressively. Not only does this break the unsolicited advertising policy, but you're potentially pushing away people from the idea of crypto which hurts the whole thing in general.
     
    Us moderators and NQ discussed briefly boosting the server moderation level from high (server member for 10m) to maximum (verified phone linked to the account) but this was seen as too extreme. For now, if you get one of these messages, send a link to the message (‘copy message link’ in the right-click menu) and a screenshot to the mods via ModMail and we’ll take care of them. NovaQuark is attempting to work with Discord on this issue and those DM links are needed as a part of Discords investigations. Links are no longer required, scroll down a bit for further guidance.
     
    When I started writing, ten separate accounts were banned, plus one more while I wrote, for their “no-bs” activity. Since then another 9 have fallen between NQ reviewing and approving this and my own goings-on getting in the way. They’re persistent, these lads, but rest assured they’re not as persistent as a dragon, or my fellow moderators on the community discord team! While I've taken a step back for some Halo in the past week, I'm looking at 11 modmails that I haven't read yet that I suspect run along similar lines... Yes, a potential 30 accounts.
     
    Not much to say here since they're just pushing a discord invite and its nothing more malicious in general chat. Just letting you guys know that we’re aware of these events, along with what actions to take, so I'll leave it at that.
     
     
    Update 7/22: NQ has advised me that since Discord is not taking action, that all logging on Wall Street War has ended. Please send further incidents to Discords Trust and Safety team as this falls under the Self-botting category of spam.
    https://support.discord.com/hc/en-us/requests/new?ticket_form_id=360000029731
     
     
     
    Part Two: Security

    Strong passwords, you knew this was coming right? "Blaarg I can't reeeee-member", enough with the excuses. There are plenty of solutions out there that allow you to create long, complex, and secure passwords that you don't have to remember or type in manually. In years past I used and recommended LastPass but they had to go and ruin a good thing and separate computers and mobile devices for free account holders. If you can afford their $3/month premium plan, I'd still recommend them since they're very well known and have a lot of great features, but I'm cheap so I dropped them. With the hundreds of online accounts some of us might have, my replacement is Zoho Vault. For all my uses, it has the features I require on both desktop and mobile devices in the form of autofill and password generation.
     
    If sticking all your passwords in some online database sounds like a bad idea, there are also solutions for building and managing your own homebrewed vault, just be sure to stick it somewhere greedy little hands and pets can't find it. If the hamsters aren't running on their wheels, they're destroying your hardware.
     
    2 Factor Authentication, if it’s an option, use it. It will take a little bit longer to log in since it’s an extra step, but it can make a lot of difference if your username and password are compromised. Is it foolproof, no. Is something better than nothing, absolutely. Setting up 2FA is easy, just find a specific app for it, scan a QR code, verify it by entering the string of numbers (usually six but I've encountered no more than nine), then once it’s confirmed, you're all set up. Now after you enter your username and password, you'll need to check the app you downloaded and enter in that string of numbers before the timer expires to fully log in.
     
    While the extra security is great, it comes with some extra caveats:

    An online database can be breached. The database can fail and the data become lost. Your master password can be breached or forgotten.
    Your hardware can be stolen, misplaced, lost, or even destroyed. If you read this far, you get a cookie.
    You can forget to transfer your 2FA app data to your new phone before deleting your old phone’s data.
     
    Should any of that happen, you can be well and truly screwed, especially when you're excited about getting a new phone like I was a few years back. At least if you mess up, you know your accounts are still protected, it's just a big time kill resetting everything. Don't let that scare you though, better to be safe than sorry with your account partly out of your hands because you forgot your strong password, versus in someone elses hands entirely.
     
    Above all, use common sense. It's in pretty short supply these days but I'm sure you can activate enough neurons to tell the difference between your coworker’s email that comes from a familiar email (or physical) address versus something about some guys relative in the hospital and he needs $500 for the bills and wants you to wire them money and... oh hey, is that gold? OwO
     
    This isn't a defacto guide, just some easy things you can do right now to get started and have an immediate impact on your online security. There's a lot more reading and videos (even physical hardware you can purchase) out on the internet if you care to look which I would encourage you to do.
     
     
     
    Again, if you get a message from anyone using either of these methods of unsolicited advertising or anything else, be it related to Dual Universe or not, please send them to us community mods via Modmail (works just like sending a DM to anyone else) and we'll handle things.
     
     
     
    TLDR: Firstly, the Discord community mods are aware of the scammers going around. Send us a modmail with a screenshot and we'll handle things.
    Secondly; password hygine, 2FA, if it looks sketchy it probably is.
     
    That's all I wanted to say, thanks for reading.
  15. Like
    infyrno reacted to Perry666 in Please support your DU content creators   
    Please help support your DU content creators on you tube, twitch and whatever other platform you see fit. Give em a like, a follow a subscription if you can. The more people creating content for the game the better chance DU has of attracting new players. New players means more money for NQ Which will hopefully trickle down to us in faster, better and more updates for the game. Think big picture friends.
  16. Like
    infyrno reacted to Mjrlun in My list of various Quality of Life features that would be vital to have   
    Now, before I start, I made a mention of this at the end of my post called "Quality of Life and beyond", however, to NQ directly: If able, I highly recommend employing 1 or more (coding) developers to permanently play test the game, and to fix any various bugs, and/or quality of life features thought up, because that would allow for better polish within the game for everyone, while also taking the least amount of time from the base development team. Additionally, just having some sort of board to post various small quality of life features internally from devs to devs, in case anyone has suggestions on such features and bug fixes, would also be highly productive.
     
    UI changes
     
    In build mode, the game doesn't really tell the player much about what elements are destroyed. It just says either "there are no elements damaged", or "there is at least 1 element damaged". The funny part about this, is there's literally LUA scripting that can detect damaged elements, and those specific elements (and their names) can be put on a screen, to tell the player what needs repairing. Of course, while this is great for LUA scripting, it is important to realize that the game cannot rely primarily on community made tools (unless you think Elite Dangerous is a perfectly designed game), and such, my suggestion is as follows:
     
    Under the construct tab in the build helper, put a list of all damaged elements (if there is any). When they are double-clicked, highlight them (through everything) with a unique color that is distinct from the repairing tool's highlights (such as orange). This would persist when you get out of build mode, and would time out after a minute. Should the player start to repair the element damaged, it will persist till the element is fully repaired.
     
     
    Another reference to my Quality of Life and beyond post, but better layout, and more dense information for item descriptions is sorely needed. Of course, you can find that post here. To build this, as well as upon other ideas under "Better Item/element descriptions", having better ways to judge element sizes without actually having to buy that element would be a great addition, for blueprinting, and visualizing ship builds. This of course can use pre-existing assets, with not too much effort (I hope?).

    This feature would require its own tool most likely, named the "blueprint tool". Similar to the "element" tool, it allows players to place elements, but in this case, very differently. On the right side, where the selection for elements for placement occurs, a button above all of the elements in the list (named the "selection menu"), when opened will create an inventory-like menu which will house any and all elements that exist within the game (assuming polish exists), excludes elements that the player cannot access naturally. It would have a search function similar to a container menu, as well as filtering functions, and so on. The list would be alphabetical, to make it easier to find elements manually as well. Lastly, the right-click function would not work on any of the elements inside the menu, as well as any form of moving the elements around. All of their information (just like a container window) will appear on the right side of the inventory panel.
     
    When it comes to actually using these items, it functions very similarly to the element placement tool. Firstly, you select elements the same way you select them with the element tool, however, of course only using the "element selection", menu, or (hypothetically), dragging elements into the bar as well from the player's inventory. When placing the element (with the same criteria as normal), instead of the actual element placing, a hologram-highlight would appear, in either light blue, or yellow. This hologram would only appear in build mode, and the player cannot collide with it either. Lastly, it of course would not be right clickable, and would be ignored with right-click. Lastly, alt-clicking the element with either the element tool, OR, the blueprint tool in hand would remove the hologram.
     
    In markets, simple changes such as improvements to how linked containers interact with the market container UI, as well as simple changes such as removing the "instant buy" and "instant sell" buttons when there is no stock, as well as creating more search filters, such as "has stock at X market", or "tier X", etc. would also be great. Additionally, improvements to how items can be selected and moved (select multiple, and drag), as well as being able to select multiple items, and then click "move to inventory" and have all items move, instead of just that slot.
     
    Finally, for the search function itself in all uses and forms, a better algorithm to include the sub-strings that match, not only just the search input string, would be greatly appreciated to find items that you don't know the precise name of, especially considering just how inconsistent the naming scheme is. In this, also having tags associated with item types, such as "Thruster" for all engines, or "alloy" to search for all alloys, etc. would make searching a lot easier and quicker.
     
    Ship piloting improvements
     
    Moving on to the default UI (and controlling) for construct controllers (cockpits, really), major improvements to both the UI, and functionality, would be a great idea. Of course, DU prides itself on community-made tools, and scripts, however, a development team should not sacrifice good quality of life for that "quirk". Additionally, a feature change would also allow the developers to clean up the LUA functions themselves, which is also a good thing.
     
     
    To the first suggestion, major changes to the way that omni-directional thrust works would be a great idea for ships that use this feature. At the moment, the movement is very lack-luster, with literally just hover function included to make the ship float against gravity, and literally the rest is the same compared to normal throttle-design. There is no auto-dampening, such as many other games, and there is no "tap w to move forward slightly", like games such as Empyrion, and Space Engineers, which, while having more basic flight models, can be credited for it being very intuitive and smooth. The current version of this is nothing like this, and has a mish-mash of that feature, in every direction but forward and backward, but for those directions, uses throttle for acceleration. This of course ruins the primary perk of omni-directional thrust, which is the fine tuned aspect of piloting. Additionally, smaller features like having keybinds for specific throttle amounts would also be great to see.
     
    To change both of these issues, I suggest the following. First, for auto-dampeners, have a button that auto-toggles between 3 different modes (identical to Empyrion, btw). No dampening, gravity-dampening, and full dampening. These different modes are self explanatory, just observe the mechanics in those games.
    For the actual movement changes, to counteract the issue of not having a throttle slider, having a key bind once again that locks the current state of acceleration from the thrusters would be the solution here. To clarify acceleration, I mean what the thrusters are currently doing/outputting. changing any movement manually would cancel this, as well as pressing the button again. 
     
    If you wanted to get more fancy with these two features, to make them not clash, having a distinction between what the dampeners are doing, compared to what the player has inputted would be my solution to this. The auto-dampeners would work separately to the thruster-lock, and in this would allow the ship to still stay airborne without constant adjustment.
     
     
    Moving onto the UI itself (assuming that the features above have indicators within the UI), decluttering the different UI boxes to be more clean, and take up less of the screen would be preferable. Currently, when attaching a weapon of some form to a flight seat, the amount of UI that it introduces clutters the screen so bad, that in my experience makes it nearly unusable for piloting. I do not have a suggestion really to fix this, however, the issue is still persistent, and bugs me greatly. 

    Secondly, there is literally a scroll bar for a cockpit that uses mouse wheel for the throttle. Due to this, the information hidden by this is lost due to this issue. 

     
    Additionally, some tiny improvements to fuel tanks within the piloting UI, adding a number to indicate the fraction of fuel compared to the total would be helpful for fuel usage calculations on the fly. An example as simple as this would suffice in my opinion.

     
    Tank relays/Tank hubs
    I'm sure this feature has been suggested basically everywhere, but the amount of flexibility this feature would provide for ship designs makes it a great suggestion for this post. My take on this is as follows. Similar to a container hub, it connects tanks. While this is cool and all, there are 3 different types of tanks in the game, of which are not compatible. In this, my suggestion to solve this is to color code a tank hub (change its coloration of the texture itself) when it connects to a type of tank. Light-blue for Nitron, yellow for Kergon, and white for Xeron. While a type is active, when attempting to connect a tank of different type to it, an error message will appear, stating that different tank types cannot connect to a tank hub at once. Lastly, when there are no connections to the tank hub, it reverts to the original state (and texture).
     
    Gameplay Balance
     
    While at this rate you could probably claim I'm sponsored by Empyrion Galactic Survival or something, I just really like the game. In this, a lot of survival-oriented features in that game I find quite compatible and important for this game to exhibit in some manner as well. Starting with the biggest issue in my opinion, ore distribution and the tech tree.
     
    Currently, limestone and malachite are only present on the same body on Alioth, where they are used to craft tier 2 components. This issue with this is the following: You cannot craft tier 2 components without a static core, and therefore by extension a territory unit, which that territory unit requires tier 2 components. Due to this, it is physically IMPOSSIBLE to start from scratch on any celestial body in the star system in almost any manner, therefore breaking a core gameplay pillar (in my opinion) of any progression-based game, something Empyrion (for example) does incredibly well. In that game, you can get to the end game in almost any situation from scratch (no matter if it's extremely difficult), due to the nature of the progression. 
     
    To fix this major issue, excluding of course the need for Malachite and Limestone to exist on all celestial bodies (in my opinion), allowing territory units to be crafted using tier 1 components, AND in a nanocrafter I see as a necessity for this game. Of course, it would have to be highly expensive in that regard, and additionally take a long time to craft (example being 6-12 hours).
     
     
    Switching to a similar topic, brought up in the previous feature mention, a change of ore distribution and planet revamp is sorely in need. While of course a planet revamp is not a quality of life feature that is simple to answer (and solve), the ore distribution, which of course requires a planet revamp, is highly important to the current progression of the game. Without good level design, a game will most likely suffer for that issue. I do plan on making a post about the planet revamp though, so ill link it here when it happens. Anyways.

    Take this situation: Currently, you can get stranded on a moon. These moons do not exhibit atmosphere, and a lot of them do not contain tier 2 ore either. Due to this issue, it is physically impossible to progress without a market, and to even move a ship starting with no fuel. Because of this chicken and egg issue, the player physically cannot get off of a celestial body without any tier 2's, due to space engines (and technically rocket engines) being impossible to fuel without them, and being required to move in space properly.
     
    Funnily enough, the player can craft space engines (and tanks) in their nanocrafter, but of course cannot fuel them, making that feature quite questionable.
     
     
    Lastly, any major changes to progression can be linked here. This post is about player movement, as well as ship diversity, engineering, and piloting.
     
     
    This post will be updated over time, and in that, any major changes will have a call-back post which will link back to this page. Thanks for reading!
  17. Like
    infyrno reacted to NQ-Deckard in Look back at 0.25, look ahead at 0.26 - Questions thread   
    Hello Noveans!

    We would like to take the opportunity to gather some questions from you regarding, 0.25 and 0.26 for an upcoming Q&A.
    The announcement can be found here.
     
    Ask away!
    - NQ-Deckard
  18. Like
    infyrno reacted to Eternal in Social game or a socialist game?   
    I've been thinking hard about this: the typical MMOs that you play is about character development including growing the assets of that character. It's about individual development. Ofcourse you got guilds to affiliate yourself with other people, to leverage your connection with other people, but the game still revolves around character development. This is what I call an equitable game!

    When I play games like Eve Online and Dual Universe, it's about empire development. There is no individuality. Everything is about your contribution to the empire. You made the game a socialist game for the sake of making it social! A social game means there is an interactivity between players. It does not mean making the game group-based making it a socialist game.   

    Organizations have asset that the members have contributed. An organization does not exists physically. What exists in this material world are the people and the people are individuals. Is there like a legal framework in this game to settle the claims of an individual in an organization? None, it is all player-driven! So what we end up having in this game is the communist socialist structuring of organizations that players contribute to without personal rights to your share in that organization, to your stake in that organization that is never distributed and is reinvested into the equity of that organization, as you have no way to assert them but through player-driven way, in an authoritarian system. 

    Legates set rights? Are you serious? If I contribute 2 tons of steel rolled, do I not have the rights to the ownership of those steel? It's called a "share"! And what, it is up to the legate to set these rights? That is why you should f*cking put it on your name! You own it, you should f*cking put it on your name! In that way, you can assert what is yours since we don't have any legal system! 

    In this game, we don't have personal property rights under organization which is needed for capitalism to exists. We do if you name it under yourself, but never in an organization. Organizations in this game is dictatorship, communism, and socialism. Why, you ask? Where is the personal rights? You expect me to wait for it from the top? What is he, a god? 

    This game is about worshiping dictators, it's never about equity for people who are playing this game in a group, and believe me, this game forces you to play this game in a group! no equity, no capitalism! What do you call it? Answer it for yourself!
  19. Like
    infyrno reacted to Abilton in Dual Universe Today Magazine - Today's Issue   
    Edition 100.pdf
  20. Like
    infyrno reacted to NQ-Deckard in Accounts sanctioned for Missions exploit   
    A number of accounts have received warnings or bans today due to exploits related to Missions. 
     
    While we will not comment further on those cases, it is a good time to remind the community: 
    Don’t use exploits. (derr) Do not publicly accuse another player of exploiting. Instead, contact Support. We will investigate and take action as required.   
    If you aren’t certain whether or not something could be considered an exploit, please don’t hesitate to contact Support to ask. It’s always better to be safe than sorry. 
     
  21. Like
    infyrno reacted to MasterOfAll in Space Map with Warp Cell Calculator   
    I updated the math and fixed calculation issue, I will add rounding next..
     
     
    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 local selection = 0 if buttonNo == 1 then destX = 0 destY = 0 selection = 1 sudistance = distalioth elseif buttonNo == 2 then destX = 43 destY = -56 sudistance = distmadis selection = 2 elseif buttonNo == 3 then destX = 73 destY = -27 selection = 3 sudistance = distthades elseif buttonNo == 4 then destX = -33 destY = -139 selection = 4 sudistance = disttalemai elseif buttonNo == 5 then destX = -109 destY = -56 selection = 5 sudistance = distfeli elseif buttonNo == 6 then destX = 131 destY = -68 selection = 6 sudistance = distsicari elseif buttonNo == 7 then destX = 35 destY = 214 selection = 7 sudistance = distsymeon elseif buttonNo == 8 then destX = 146 destY = -74 selection = 8 sudistance = distsinnen elseif buttonNo == 9 then destX = -235 destY = -32 selection = 9 sudistance = distjago elseif buttonNo == 10 then destX = 202 destY = -137 selection = 10 sudistance = distteoma elseif buttonNo == 11 then destX = 7 destY = 247 selection = 11 sudistance = distion elseif buttonNo == 12 then destX = 247 destY = 34 selection = 12 sudistance = distlacobus elseif buttonNo == 13 then unit.exit() end end function updateScreen() loc = vec3(core.getConstructWorldPos()) local shipVelocity = vec3(core.getVelocity()):len() * 3.6 local shipAcceleration = vec3(core.getVelocity()):len() * 3.6 local time_to_distance = 0 if selection == 1 then alioth = galaxyReference[0][2] ---uses Atlas functions distalioth = math.floor(alioth:getDistance(loc)/200000) ---uses getDistance functions---- time_to_distance = distalioth * 200 / shipVelocity elseif selection == 2 then madis = vec3(17465536.000, 22665536.000, -34464.000) distmadis = string.format("%.2f", math.sqrt((loc.x-madis.x)^2+(loc.y-madis.y)^2+(loc.z-madis.z)^2)/200000) time_to_distance = distmadis * 200 / shipVelocity elseif selection == 3 then thades = vec3(29165536.000, 10865536.000, 65536.000) distthades = string.format("%.2f", math.sqrt((loc.x-thades.x)^2+(loc.y-thades.y)^2+(loc.z-thades.z)^2)/200000) time_to_distance = distthades * 200 / shipVelocity elseif selection == 4 then talemai = vec3(-13234464.000, 55765536.000, 465536.000) disttalemai = string.format("%.2f", math.sqrt((loc.x-talemai.x)^2+(loc.y-talemai.y)^2+(loc.z-talemai.z)^2)/200000) time_to_distance = disttalemai * 200 / shipVelocity elseif selection == 5 then feli = vec3(-43534464.000, 22565536.000, -48934464.000) distfeli = string.format("%.2f", math.sqrt((loc.x-feli.x)^2+(loc.y-feli.y)^2+(loc.z-feli.z)^2)/200000) time_to_distance = distfeli * 200 / shipVelocity elseif selection == 6 then sicari = vec3(52765536.000, 27165536.000, 52065536.000) distsicari = string.format("%.2f", math.sqrt((loc.x-sicari.x)^2+(loc.y-sicari.y)^2+(loc.z-sicari.z)^2)/200000) time_to_distance = distsicari * 200 / shipVelocity elseif selection == 7 then symeon = vec3(14165536.000, -85634464.000, -934464.000) distsymeon = string.format("%.2f", math.sqrt((loc.x-symeon.x)^2+(loc.y-symeon.y)^2+(loc.z-symeon.z)^2)/200000) time_to_distance = distsymeon * 200 / shipVelocity elseif selection == 8 then sinnen = vec3(58665536.000, 29665536.000, 58165536.000) distsinnen = string.format("%.2f", math.sqrt((loc.x-sinnen.x)^2+(loc.y-sinnen.y)^2+(loc.z-sinnen.z)^2)/200000) time_to_distance = distsinnen * 200 / shipVelocity elseif selection == 9 then jago = vec3(-94134464.000, 12765536.000, -3634464.000) distjago = string.format("%.2f", math.sqrt((loc.x-jago.x)^2+(loc.y-jago.y)^2+(loc.z-jago.z)^2)/200000) time_to_distance = distjago * 200 / shipVelocity elseif selection == 10 then teoma = vec3(80865536.000, 54665536.000, -934464.000) distteoma = string.format("%.2f", math.sqrt((loc.x-teoma.x)^2+(loc.y-teoma.y)^2+(loc.z-teoma.z)^2)/200000) time_to_distance = distteoma * 200 / shipVelocity elseif selection == 11 then ion = galaxyReference[0][120] ---uses Atlas functions distion = math.floor(ion:getDistance(loc)/200000) ---uses getDistance functions---- time_to_distance = distion * 200 / shipVelocity elseif selection == 12 then lacobus = vec3(98865536.000, -13534464.000, -934464.000) distlacobus = string.format("%.2f", math.sqrt((loc.x-lacobus.x)^2+(loc.y-lacobus.y)^2+(loc.z-lacobus.z)^2)/200000) time_to_distance = distlacobus * 200 / shipVelocity else time_to_distance = sudistance * 200 / shipVelocity end warpmath = math.floor(math.floor(core.getConstructMass()/ 1000) * sudistance * 0.00025) html= ([[ <svg width="1024" height="1024" viewBox="0 0 1024 1640"><circle cx="500" cy="500" r="400" stroke="darkgreen" stroke-width="3" transform=""></circle><circle cx="500" cy="500" r="350" stroke="#FFF" stroke-width="3" transform="" stroke-opacity="0.2"></circle><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><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><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 width="1024" height="612" xmlns="http://www.w3.org/2000/svg">> <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="10" stroke="#FFF" fill="none"/> <rect rx="10" id="svg_3" height="50" width="250" y="105" x="15" stroke-width="10" stroke="#FFF" fill="none"/> <rect rx="10" id="svg_7" height="50" width="250" y="180" x="15" stroke-width="10" stroke="#FFF" fill="none"/> <rect rx="10" id="svg_9" height="50" width="250" y="255" x="15" stroke-width="10" stroke="#FFF" fill="none"/> <rect rx="10" id="svg_10" height="50" width="250" y="330" x="15" stroke-width="10" stroke="#FFF" fill="none"/> <rect rx="10" id="svg_11" height="50" width="250" y="405" x="15" stroke-width="10" stroke="#FFF" 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="10" stroke="#FFF" fill="none"/> <rect rx="10" id="svg_34" height="50" width="250" y="105" x="760" stroke-width="10" stroke="#FFF" fill="none"/> <rect rx="10" id="svg_35" height="50" width="250" y="180" x="760" stroke-width="10" stroke="#FFF" fill="none"/> <rect rx="10" id="svg_36" height="50" width="250" y="255" x="760" stroke-width="10" stroke="#FFF" fill="none"/> <rect rx="10" id="svg_37" height="50" width="250" y="330" x="760" stroke-width="10" stroke="#FFF" fill="none"/> <rect rx="10" id="svg_38" height="50" width="250" y="405" x="760" stroke-width="10" stroke="#FFF" 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> <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..[[ hrs</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="500" stroke-width="0" fill="LightBlue">Vel: ]]..shipVelocity..[[ km/h</text> </svg> ]]) screen.setHTML(html) screen2.setHTML(html) end unit.setTimer("spacemap",.08)  
  22. Like
    infyrno reacted to NQ-Naunet in [Update] Recent Patch Information   
    Hello Noveans,

    Following our recent patch, we have decided to clear all restorations from all elements that are both placed on constructs and currently present in inventories and containers. Permanently destroyed elements that have not been "scavenged" will be restored as well.

    Active health of elements will however not be affected, so elements that are partially damaged will remain that way and will have their restore count reset to the base number. 

    This is a one time event following the patch, any damage sustained through PvP going forth will still reduce the restore counts on elements.

    Thanks, all! ?
  23. Like
    infyrno reacted to NQ-Naunet in Talent Points   
    The points will be applied after a brief downtime starting at 9 am UTC | 4 am EST on December 17th.  
    _______________________________________________________________________________________________________
     
    Hello Noveans,
     
    December is halfway over, meaning the arrival of the new year is tantalizingly close! 2021 is sure to usher in some big changes, and we couldn’t be more excited to experience those with you. ?
     
    While we were rolling out 0.23.1, we noticed that players who had talents in training lost what was accumulated during our downtime. So, in the spirit of bringing out the old and ringing in the new we would like to offer everyone 1 million talent points (~1 week’s worth) to not only replace lost points, but also as a bit of a holiday gift! Build, explore and be merry!
     
    Thank you for your continued support. We sincerely hope that you and yours enjoy a restful holiday season!

    Sincerely,
    The Novaquark Team
     
×
×
  • Create New...