#pragma once #include "raylib.h" #include "Planet.hpp" #include "Colors.hpp" // ColorMode #include "Overlays.hpp" // PlateLabel #include "Map2D.hpp" // Map2D #include #include #include // The interactive viewer: owns all window/sim/view state and runs the frame // loop. The old free-standing main() lived as one giant function with capturing // lambdas; those lambdas are now methods and their captured locals are members, // so the body splits cleanly across Viewer.cpp (setup + sim orchestration), // ViewerInput.cpp (input/picking/keys) and ViewerRender.cpp (drawing). struct Viewer { // ---- Files / save format ------------------------------------------------ static constexpr uint32_t SAVE_VERSION = 23; // v23: civ cultural evolution; v22: civ diplomacy; v21: civ conflict/wars; v20: civ settlements; v19: ecoregions; v18: geography reshuffle salt; v17: +geography/atlas; v16: +event log; v15: stateful volcanoes; v14: old volcanoes; v13: +liveRate; v12: +step-back history; v11: +weather systems; v10: +weather fields; v9: +moons; v8: +Live World clock; v7: +biota; v6: self-describing config; v4: +biome; v3: +phase3 static constexpr int wxSaveMax = 40; // most recent step-back frames persisted in a save static constexpr int EVENT_LOG_MAX = 200; const char* CONFIG_PATH = "planet.cfg"; const char* SAVE_PATH = "planet.save"; std::string configPath = "planet.cfg"; // initial config (--config overrides) // ---- Window / layout (set in init) -------------------------------------- int screenW = 1920, screenH = 1080; int leftW = 0, rightX = 0, rightW = 0, rightH = 0; int view3DW = 0, view3DH = 0; RenderTexture2D rt3d{}; Rectangle mapRect{}, hoverRect{}, panelRect{}, gridRect{}; Rectangle liveInfoRect{}; // free space right of the (left-aligned) 2D map: moon/tide phase Rectangle pauseBtn{}, p3ContinueBtn{}, p3StartBtn{}; float pbCx = 0.0f, pbCy = 0.0f; const float visBase = 2.0f; const float elevExagg = 0.00000004f; const float borderR = visBase + 0.004f; const float driftR = visBase + 0.006f; const float riverR = visBase + 0.005f; const float gratR = visBase + 0.003f; const int subRes = 16; // ---- Sim / world -------------------------------------------------------- Planet planet; PlanetConfig cfg; Camera3D cam{}; float camYaw = 0.4f, camPitch = 0.3f, camDist = 6.0f; ColorMode mode = ColorMode::Elevation; std::vector vcolors; std::vector borders, ridgeBorders; bool showBorders = true; std::vector driftArrows; bool showDrift = true; std::vector plateLabels; std::vector rivers, bigRivers; bool showRivers = true; std::vector> graticule; bool showGrat = false; Map2D map2D; // Phase-1 forming model. bool paused = false, settled = false; long long stepCount = 0; double maxChange = 0.0; int settleRun = 0; const double settleThresh = 2.0; // m/tick at or below which it's "settled" const int settleNeed = 3; // consecutive settled ticks before pausing const double formRate = 55.0; // forming ticks per second (watchable) double formAccum = 0.0; double minE = 0.0, maxE = 0.0; // Phase 2 drift. double elapsedMy = 0.0, dtMy = 0.0, driftRate = 4.0, driftAccum = 0.0; // Phase 3 hydrology. bool phase3 = false, phase3Prompt = false; double phase3PromptAt = 0.0; // Live World: a slow real-time clock (hours -> weeks/months) over the finished planet. // Geological drift freezes while it runs; a moving day/night terminator, a live seasonal // temperature cycle and a moving snow line animate. liveRate is sim hours per real second. bool liveWorld = false, dayNightOn = true; double liveTime = 0.0; // hours since the live clock started double liveRate = 1.0; // sim hours advanced per real second (ramps hour -> ~20 years) // Top clock speed: ~20 sim-years per real second (a century in ~5 s). Kept a code constant like the // old 720 (1 month/s); the [ / ] ramp and the load-resume clamp both clamp to this. double liveRateMax() const { return 20.0 * planet.cfg.dayLengthHours * planet.cfg.yearLengthDays; } // Step-back undo history: each forward step snapshots the clock + full weather state so a // backward step restores everything (weather is an integrated path, not analytically reversible). struct WxFrame { double t; WeatherSnapshot w; }; std::vector wxUndo; static constexpr int wxUndoMax = 180; std::vector illum; // per-cell day/night brightness (1 = day, floor = night) std::vector shadedColors;// vcolors + snow/ice tint + day/night dim (live overlay) Vector3 sunDir{0.0f, 0.0f, 1.0f}; // model-space sub-solar direction (for the 3D sun marker) std::vector moonDirs; // model-space sub-lunar directions (one per moon, for render) std::vector moonNormals;// model-space orbit-plane normals (one per moon, for the ring) std::vector coast; // coastline segments (land/ocean boundary, rebuilt with terrain) std::vector coastOcean; // ocean cell per coast segment (to sample tide) std::vector coastCols; // per-segment tide colour (filled each frame when showTides) bool showTides = false; // colour the coastline by the live tide level (key T) std::vector currentSegs; std::vector currentCols; // ocean-current arrows bool showCurrents = false; // ocean current arrows, warm/cold (key O) bool showClouds = true; // Live World cloud/rain cover overlay (key K) bool showVolcanoes = true; // Live World volcano markers (cones + eruption glow, key V) bool showNames = false; // geographic place-name labels (the atlas, key M) bool showSettlements = true; // civilization settlement markers (key U seeds + toggles) std::vector atlasRowCells; // cell to focus per visible Atlas/Eco/Civ/Realms-tab row (parallel to the list) std::vector nationBorders; // political border segments (rebuilt on year tick / placement / load) bool showNationBorders = false; // draw nation/realm borders (on with the Territory view) std::vector cultureBorders; // cultural-region border segments (civ Step 4, rebuilt with territory) bool showCultureBorders = false; // draw cultural-region borders (on with the Culture view) std::vector warFrontier; // red frontier segments between realms currently at war (civ Step 5) std::vector allyLinks, rivalLinks; // civ Step 6: capital-to-capital arcs (allies green, rivals red) std::vector tradeSea, tradeLand; // civ Step 7: trade routes (sea cyan / river+land amber) bool showTradeRoutes = false; // draw trade routes (on with the Wealth view) long lastTerritoryYear = -1; // sim year territory was last recomputed (recompute when it ticks) // World event journal: currently Live World events, shaped to be reused by later phases. struct WorldEvent { uint32_t id = 0; uint8_t kind = 0; // 1 weather, 2 volcano, later phases can append new kinds uint8_t severity = 0; // 0 info, 1 notable, 2 severe double timeHours = 0.0; int cell = -1; uint32_t sourceId = 0; std::string title, detail; }; std::vector events; uint32_t nextEventId = 1; int liveInfoTab = 0; // 0 Sky, 1 Tides, 2 Weather, 3 Events, 4 Atlas, 5 Eco std::vector liveInfoTabRects; std::vector eventRowRects; std::vector eventRowIndices; // indices into events for visible event rows // Selection + subgrid (phase 4/5 preview). int selectedCell = -1; double selectedThresh = 0.06; std::vector> subgrids; // Transient on-screen status line. std::string statusMsg; double statusUntil = 0.0; // Input state. float dragDist = 0.0f; double mapLon = 0.0; // 2D map longitude pan (radians) double mapZoom = 1.0; // 2D map zoom factor (1 = whole map; up to 8x) double mapPanX = 0.0, mapPanY = 0.0; // 2D map screen-space pan (pixels, used when zoomed) bool pressInMap = false; // a drag that started on the map pans it uint32_t followId = 0; // Live World: id of the storm the 3D camera follows (0 = none) // Per-frame picking state (set by handleInput, read by render). Vector2 mp{}; bool onPause = false; int hovered = -1; bool hasHoverSub = false; SubCell hoverSub; int hoveredSubIdx = -1; // ---- Lifecycle ---------------------------------------------------------- bool init(int argc, char** argv); // window, layout, config, first world void run(); // the frame loop (until window closes) // ---- Sim orchestration (Viewer.cpp) ------------------------------------- void rebuildSub(); void selectCell(int idx); void recolor(); void refreshView(); void rebuildTerritory(); // recompute nations/territory + nation-border segments void rebuildLiveOverlay(); // Live World: fill illum + shadedColors from sim fields // Colors the 3D globe + 2D map actually draw: the live overlay when in Live World, else the // plain per-cell colours. const std::vector& displayColors() const { return liveWorld ? shadedColors : vcolors; } void regenWorld(); // after generate(): geometry changed void regen(); // generate(cfg) + regenWorld() void stepOnce(); // one tick + settle bookkeeping void pauseAction(); void setStatus(const std::string& m); void saveGame(const char* path); void loadGame(const char* path); void stepSim(); // advance forming / drift+hydrology this frame void liveAdvance(double dtClock, double dtWeather); // advance the Live World clock + fields void liveStepForward(); // step the clock forward one rate-unit (snapshots for undo) void liveStepBack(); // step everything back one frame (restores weather/storms) void wxPushSnapshot(); // push the current weather state onto the step-back ring Rectangle mapViewRect() const; // 2D map projection rect after zoom/pan (scissor stays mapRect) void appendEvent(uint8_t kind, uint8_t severity, double timeHours, int cell, uint32_t sourceId, const std::string& title, const std::string& detail); void detectLiveEvents(const std::vector& beforeStorms, const std::vector& beforeVolcanoes, const std::vector& beforeSettlements, const std::vector& beforePlague); void detectNationEvents(const std::vector& beforeNations); void focusCell(int idx, const std::string& status = ""); // ---- Input (ViewerInput.cpp) -------------------------------------------- void handleInput(); // ---- Render (ViewerRender.cpp) ------------------------------------------ void renderFrame(); void renderGlobe3D(); void renderMap2D(); void renderLiveInfo(); // Live World: moon phases + (coastal) tidal phase, beside the 2D map void renderPanels(); void renderHUD(); void renderPrompt(); };