/* Procedural Generation: Programming The Universe "Here we go again! Year 4 begins now..." - javidx9 License (OLC-3) ~~~~~~~~~~~~~~~ Copyright 2018-2020 OneLoneCoder.com Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions or derivations of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions or derivative works in binary form must reproduce the above copyright notice. This list of conditions and the following disclaimer must be reproduced in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. Relevant Video: https://youtu.be/ZZY9YE7rZJw Links ~~~~~ YouTube: https://www.youtube.com/javidx9 https://www.youtube.com/javidx9extra Discord: https://discord.gg/WhwHUMV Twitter: https://www.twitter.com/javidx9 Twitch: https://www.twitch.tv/javidx9 GitHub: https://www.github.com/onelonecoder Patreon: https://www.patreon.com/javidx9 Homepage: https://www.onelonecoder.com Author ~~~~~~ David Barr, aka javidx9, ©OneLoneCoder 2018, 2019, 2020 */ #define OLC_PGE_APPLICATION #include "olcPixelGameEngine.h" #include constexpr uint32_t g_starColours[8] = { 0xFFFFFFFF, 0xFFD9FFFF, 0xFFA3FFFF, 0xFFFFC8C8, 0xFFFFCB9D, 0xFF9F9FFF, 0xFF415EFF, 0xFF28199D }; struct sPlanet { double distance = 0.0; double diameter = 0.0; double foliage = 0.0; double minerals = 0.0; double water = 0.0; double gases = 0.0; double temperature = 0.0; double population = 0.0; bool ring = false; std::vector vMoons; }; class cStarSystem { public: cStarSystem(uint32_t x, uint32_t y, bool bGenerateFullSystem = false) { // Set seed based on location of star system nProcGen = (x & 0xFFFF) << 16 | (y & 0xFFFF); // Not all locations contain a system starExists = (rndInt(0, 20) == 1); if (!starExists) return; // Generate Star starDiameter = rndDouble(10.0, 40.0); starColour.n = g_starColours[rndInt(0, 8)]; // When viewing the galaxy map, we only care about the star // so abort early if (!bGenerateFullSystem) return; // If we are viewing the system map, we need to generate the // full system // Generate Planets double dDistanceFromStar = rndDouble(60.0, 200.0); int nPlanets = rndInt(0, 10); for (int i = 0; i < nPlanets; i++) { sPlanet p; p.distance = dDistanceFromStar; dDistanceFromStar += rndDouble(20.0, 200.0); p.diameter = rndDouble(4.0, 20.0); // Could make temeprature a function of distance from star p.temperature = rndDouble(-200.0, 300.0); // Composition of planet p.foliage = rndDouble(0.0, 1.0); p.minerals = rndDouble(0.0, 1.0); p.gases = rndDouble(0.0, 1.0); p.water = rndDouble(0.0, 1.0); // Normalise to 100% double dSum = 1.0 / (p.foliage + p.minerals + p.gases + p.water); p.foliage *= dSum; p.minerals *= dSum; p.gases *= dSum; p.water *= dSum; // Population could be a function of other habitat encouraging // properties, such as temperature and water p.population = std::max(rndInt(-5000000, 20000000), 0); // 10% of planets have a ring p.ring = rndInt(0, 10) == 1; // Satellites (Moons) int nMoons = std::max(rndInt(-5, 5), 0); for (int n = 0; n < nMoons; n++) { // A moon is just a diameter for now, but it could be // whatever you want! p.vMoons.push_back(rndDouble(1.0, 5.0)); } // Add planet to vector vPlanets.push_back(p); } } ~cStarSystem() { } public: std::vector vPlanets; public: bool starExists = false; double starDiameter = 0.0f; olc::Pixel starColour = olc::WHITE; private: uint32_t nProcGen = 0; double rndDouble(double min, double max) { return ((double)rnd() / (double)(0x7FFFFFFF)) * (max - min) + min; } int rndInt(int min, int max) { return (rnd() % (max - min)) + min; } // Modified from this for 64-bit systems: // https://lemire.me/blog/2019/03/19/the-fastest-conventional-random-number-generator-that-can-pass-big-crush/ // Now I found the link again - Also, check out his blog, it's a fantastic resource! uint32_t rnd() { nProcGen += 0xe120fc15; uint64_t tmp; tmp = (uint64_t)nProcGen * 0x4a39b70d; uint32_t m1 = (tmp >> 32) ^ tmp; tmp = (uint64_t)m1 * 0x12fad5c9; uint32_t m2 = (tmp >> 32) ^ tmp; return m2; } }; class olcGalaxy : public olc::PixelGameEngine { public: olcGalaxy() { sAppName = "olcGalaxy"; } public: bool OnUserCreate() override { return true; } olc::vf2d vGalaxyOffset = { 0,0 }; bool bStarSelected = false; uint32_t nSelectedStarSeed1 = 0; uint32_t nSelectedStarSeed2 = 0; /*uint32_t nLehmer = 0; uint32_t Lehmer32() { nLehmer += 0xe120fc15; uint64_t tmp; tmp = (uint64_t)nLehmer * 0x4a39b70d; uint32_t m1 = (tmp >> 32) ^ tmp; tmp = (uint64_t)m1 * 0x12fad5c9; uint32_t m2 = (tmp >> 32) ^ tmp; return m2; }*/ bool OnUserUpdate(float fElapsedTime) override { if (fElapsedTime <= 0.0001f) return true; Clear(olc::BLACK); //if (GetKey(olc::SPACE).bReleased) //{ // //srand(1000); // std::random_device rd; // std::mt19937 mt(1000); // std::uniform_int_distribution dist(0, 256); // auto tp1 = std::chrono::system_clock::now(); // // Ranomness Tests // for (int x = 0; x < ScreenWidth(); x++) // { // for (int y = 0; y < ScreenHeight(); y++) // { // bool bIsStar = false; // int nSeed = y << 16 | x; // // // Standard C++ rand() // //srand(nSeed); // //bIsStar = rand() % 256 < 32; // // std::random // //mt.seed(nSeed); // //bIsStar = dist(mt) < 32; // // Lehmer32 // nLehmer = nSeed; // bIsStar = Lehmer32() % 256 < 32; // Draw(x, y, bIsStar ? olc::WHITE : olc::BLACK); // } // } // auto tp2 = std::chrono::system_clock::now(); // std::chrono::duration elapsedTime = tp2 - tp1; // DrawString(3, 3, "Time: " + std::to_string(elapsedTime.count()), olc::RED, 2); //} //return true; if (GetKey(olc::W).bHeld) vGalaxyOffset.y -= 50.0f * fElapsedTime; if (GetKey(olc::S).bHeld) vGalaxyOffset.y += 50.0f * fElapsedTime; if (GetKey(olc::A).bHeld) vGalaxyOffset.x -= 50.0f * fElapsedTime; if (GetKey(olc::D).bHeld) vGalaxyOffset.x += 50.0f * fElapsedTime; int nSectorsX = ScreenWidth() / 16; int nSectorsY = ScreenHeight() / 16; olc::vi2d mouse = { GetMouseX() / 16, GetMouseY() / 16 }; olc::vi2d galaxy_mouse = mouse + vGalaxyOffset; olc::vi2d screen_sector = { 0,0 }; for (screen_sector.x = 0; screen_sector.x < nSectorsX; screen_sector.x++) for (screen_sector.y = 0; screen_sector.y < nSectorsY; screen_sector.y++) { uint32_t seed1 = (uint32_t)vGalaxyOffset.x + (uint32_t)screen_sector.x; uint32_t seed2 = (uint32_t)vGalaxyOffset.y + (uint32_t)screen_sector.y; cStarSystem star(seed1, seed2); if (star.starExists) { FillCircle(screen_sector.x * 16 + 8, screen_sector.y * 16 + 8, (int)star.starDiameter / 8, star.starColour); // For convenience highlight hovered star if (mouse.x == screen_sector.x && mouse.y == screen_sector.y) { DrawCircle(screen_sector.x * 16 + 8, screen_sector.y * 16 + 8, 12, olc::YELLOW); } } } // Handle Mouse Click if (GetMouse(0).bPressed) { uint32_t seed1 = (uint32_t)vGalaxyOffset.x + (uint32_t)mouse.x; uint32_t seed2 = (uint32_t)vGalaxyOffset.y + (uint32_t)mouse.y; cStarSystem star(seed1, seed2); if (star.starExists) { bStarSelected = true; nSelectedStarSeed1 = seed1; nSelectedStarSeed2 = seed2; } else bStarSelected = false; } // Draw Details of selected star system if (bStarSelected) { // Generate full star system cStarSystem star(nSelectedStarSeed1, nSelectedStarSeed2, true); // Draw Window FillRect(8, 240, 496, 232, olc::DARK_BLUE); DrawRect(8, 240, 496, 232, olc::WHITE); // Draw Star olc::vi2d vBody = { 14, 356 }; vBody.x += star.starDiameter * 1.375; FillCircle(vBody, (int)(star.starDiameter * 1.375), star.starColour); vBody.x += (star.starDiameter * 1.375) + 8; // Draw Planets for (auto& planet : star.vPlanets) { if (vBody.x + planet.diameter >= 496) break; vBody.x += planet.diameter; FillCircle(vBody, (int)(planet.diameter * 1.0), olc::RED); olc::vi2d vMoon = vBody; vMoon.y += planet.diameter + 10; // Draw Moons for (auto& moon : planet.vMoons) { vMoon.y += moon; FillCircle(vMoon, (int)(moon * 1.0), olc::GREY); vMoon.y += moon + 10; } vBody.x += planet.diameter + 8; } } return true; } }; int main() { olcGalaxy demo; if (demo.Construct(512, 480, 2, 2, false, false)) demo.Start(); return 0; }