行星绕日.exe

运行源代码需要EasyX库,运行exe不用

#include <graphics.h>
#include <bits/stdc++.h>
#include <windows.h>
#include <stdio.h>
using namespace std;
struct Planet {
    double x, y;
    double vx, vy;
    double radius;
    double mass;
    COLORREF color;
    vector<double> trailX;
    vector<double> trailY;
    bool destroyed;
    bool isAsteroid;
};
struct Sun {
    double x, y;
    double radius;
    double mass;
    COLORREF color;
};
struct BlackHole {
    double x, y;
    double radius;
    double mass;
    bool active;
    COLORREF color;
};
const int WIDTH = 800;
const int HEIGHT = 600;
Sun sun;
vector<Planet> planets;
vector<BlackHole> blackHoles;
double G;
double timeStep = 0.0001;
double timeScale = 50.0;
double scale = 0.3;
double mapScale = 1.0;
double viewOffsetX = 0;
double viewOffsetY = 0;
int planetCount = 7;
const double SOFTENING = 0.1;
bool menuOpen = false;
bool manualOpen = false;
bool paused = false;
int selectedPlanet = -1;
bool placingBlackHole = false;
const int MAX_TRAIL = 10000000;
const int MAX_BLACK_HOLES = 100;
void initSun() {
    sun.x = WIDTH / 2;
    sun.y = HEIGHT / 2;
    sun.radius = 25 * scale;
    sun.mass = 10000.0;
    sun.color = YELLOW;
}
void initBlackHoles() {
    blackHoles.clear();
}
void initPlanets() {
    planets.clear();
    struct PlanetData {
        double dist;
        double radius;
        double mass;
        COLORREF color;
        double angle;
        double eccentricity;
        double angleOffset;
    };
    PlanetData data[] = {
        {100 * scale, 4 * scale, 100.0, RGB(200, 50, 50), 0, 0.0, 0.0},
        {140 * scale, 5 * scale, 150.0, RGB(50, 200, 50), 1.2, 0.02, 0.3},
        {180 * scale, 5 * scale, 200.0, RGB(50, 50, 200), 2.5, -0.01, -0.2},
        {220 * scale, 6 * scale, 250.0, RGB(200, 200, 50), 3.8, 0.015, 0.5},
        {260 * scale, 6 * scale, 300.0, RGB(200, 50, 200), 4.9, -0.02, -0.4},
        {300 * scale, 7 * scale, 400.0, RGB(50, 200, 200), 0.7, 0.01, 0.6},
        {340 * scale, 7 * scale, 500.0, RGB(200, 150, 50), 1.8, -0.015, -0.3},
        {120 * scale, 4 * scale, 110.0, RGB(255, 100, 100), 3.5, 0.02, 0.1},
        {160 * scale, 5 * scale, 180.0, RGB(100, 255, 100), 4.2, -0.02, -0.1},
        {200 * scale, 5 * scale, 220.0, RGB(100, 100, 255), 1.0, 0.025, 0.4},
        {240 * scale, 6 * scale, 280.0, RGB(255, 255, 100), 2.8, -0.015, -0.5},
        {280 * scale, 6 * scale, 350.0, RGB(255, 100, 255), 4.0, 0.02, 0.3},
        {320 * scale, 7 * scale, 450.0, RGB(100, 255, 255), 5.5, -0.01, -0.2},
        {360 * scale, 7 * scale, 550.0, RGB(255, 200, 100), 1.3, 0.025, 0.5},
        {130 * scale, 4 * scale, 130.0, RGB(255, 150, 150), 4.5, -0.03, 0.1},
        {170 * scale, 5 * scale, 160.0, RGB(150, 255, 150), 0.8, 0.02, -0.3},
        {210 * scale, 5 * scale, 210.0, RGB(150, 150, 255), 5.8, -0.015, 0.6},
        {250 * scale, 6 * scale, 270.0, RGB(255, 255, 150), 3.0, 0.025, -0.4},
        {290 * scale, 6 * scale, 330.0, RGB(255, 150, 255), 1.0, -0.02, 0.2},
        {330 * scale, 7 * scale, 430.0, RGB(150, 255, 255), 4.0, 0.01, -0.5},
        {370 * scale, 7 * scale, 580.0, RGB(255, 220, 150), 2.5, -0.025, 0.4},
        {110 * scale, 4 * scale, 120.0, RGB(200, 100, 200), 5.2, 0.02, -0.1},
        {155 * scale, 5 * scale, 140.0, RGB(100, 200, 100), 1.8, -0.025, 0.5},
        {195 * scale, 5 * scale, 190.0, RGB(100, 100, 200), 4.8, 0.015, -0.2},
        {230 * scale, 6 * scale, 240.0, RGB(200, 200, 100), 0.5, -0.02, 0.3},
        {270 * scale, 6 * scale, 300.0, RGB(200, 100, 200), 3.7, 0.025, -0.6},
        {310 * scale, 7 * scale, 420.0, RGB(100, 200, 200), 5.9, -0.015, 0.1},
        {350 * scale, 7 * scale, 530.0, RGB(200, 180, 100), 1.2, 0.02, -0.3},
        {380 * scale, 8 * scale, 600.0, RGB(255, 180, 180), 4.5, -0.02, 0.5},
        {400 * scale, 8 * scale, 650.0, RGB(180, 255, 180), 2.8, 0.015, -0.4}
    };
    int count = (planetCount > 30) ? 30 : planetCount;
    if (count < 1) count = 7;
    for (int i = 0; i < count; i++) {
        Planet p;
        double angle = data[i].angle;
        double r = data[i].dist;
        double eccentricity = data[i].eccentricity;
        p.x = sun.x + r * cos(angle);
        p.y = sun.y + r * sin(angle);
        double circularSpeed = sqrt(G * sun.mass / r);
        double speedFactor = 1.0 + eccentricity * 0.3;
        if (speedFactor < 0.85) speedFactor = 0.85;
        if (speedFactor > 1.15) speedFactor = 1.15;
        double v = circularSpeed * speedFactor;
        p.vx = -v * sin(angle + data[i].angleOffset * 0.15);
        p.vy = v * cos(angle + data[i].angleOffset * 0.15);
        p.radius = data[i].radius;
        p.mass = data[i].mass;
        p.color = data[i].color;
        p.trailX.reserve(10000);
        p.trailY.reserve(10000);
        p.trailX.clear();
        p.trailY.clear();
        p.destroyed = false;
        p.isAsteroid = false;
        planets.push_back(p);
    }
}
void spawnAsteroid() {
    if ((int)planets.size() >= 50) return;
    Planet a;
    a.isAsteroid = true;
    a.radius = 2 * scale;
    a.mass = 10.0;
    a.color = RGB(150, 150, 150);
    a.destroyed = false;
    a.trailX.reserve(10000);
    a.trailY.reserve(10000);
    a.trailX.clear();
    a.trailY.clear();
    double angle = (rand() % 360) * 3.14159 / 180.0;
    double dist = 150 * scale + (rand() % 300) * scale;
    a.x = sun.x + dist * cos(angle);
    a.y = sun.y + dist * sin(angle);
    double circularSpeed = sqrt(G * sun.mass / dist);
    double speed = circularSpeed * (0.6 + (rand() % 80) / 100.0);
    double dirAngle = atan2(sun.y - a.y, sun.x - a.x) + (rand() % 60 - 30) * 3.14159 / 180.0;
    a.vx = speed * cos(dirAngle);
    a.vy = speed * sin(dirAngle);
    planets.push_back(a);
}
void handleCollisions() {
    int n = planets.size();
    for (int i = 0; i < n; i++) {
        if (planets[i].destroyed) continue;
        for (int j = i + 1; j < n; j++) {
            if (planets[j].destroyed) continue;
            Planet& a = planets[i];
            Planet& b = planets[j];
            double dx = b.x - a.x;
            double dy = b.y - a.y;
            double distSq = dx * dx + dy * dy;
            double minDist = a.radius + b.radius;
            if (distSq < minDist * minDist && distSq > 0) {
                double dist = sqrt(distSq);
                double nx = dx / dist;
                double ny = dy / dist;
                double overlap = minDist - dist;
                a.x -= nx * overlap * 0.5;
                a.y -= ny * overlap * 0.5;
                b.x += nx * overlap * 0.5;
                b.y += ny * overlap * 0.5;
                double dvx = a.vx - b.vx;
                double dvy = a.vy - b.vy;
                double dvn = dvx * nx + dvy * ny;
                if (dvn < 0) {
                    double restitution = 0.3;
                    double invMass = 1.0 / a.mass + 1.0 / b.mass;
                    double impulse = (1 + restitution) * dvn / invMass;
                    a.vx -= impulse * nx / a.mass;
                    a.vy -= impulse * ny / a.mass;
                    b.vx += impulse * nx / b.mass;
                    b.vy += impulse * ny / b.mass;
                    if (a.isAsteroid || b.isAsteroid) {
                        if (a.isAsteroid && !b.isAsteroid) {
                            b.color = RGB(255, 200, 100);
                        } else if (!a.isAsteroid && b.isAsteroid) {
                            a.color = RGB(255, 200, 100);
                        }
                    }
                }
            }
        }
    }
}
void computeGravityAndUpdate() {
    double currentTimeStep = timeStep * timeScale;
    if (currentTimeStep > 0.02) currentTimeStep = 0.02;
    if (currentTimeStep < 0.000001) currentTimeStep = 0.000001;
    int subSteps = (int)(0.002 / currentTimeStep);
    if (subSteps < 1) subSteps = 1;
    if (subSteps > 800) subSteps = 800;
    double subTimeStep = currentTimeStep / subSteps;
    int n = planets.size();
    int bhCount = blackHoles.size();
    for (int step = 0; step < subSteps; step++) {
        vector<double> ax(n), ay(n);
        for (int i = 0; i < n; i++) {
            if (planets[i].destroyed) {
                ax[i] = 0;
                ay[i] = 0;
                continue;
            }
            double ax_total = 0, ay_total = 0;
            Planet& p = planets[i];
            double dx_sun = sun.x - p.x;
            double dy_sun = sun.y - p.y;
            double distSq_sun = dx_sun * dx_sun + dy_sun * dy_sun;
            if (distSq_sun < 0.01) distSq_sun = 0.01;
            double dist_sun = sqrt(distSq_sun);
            double accel_sun = G * sun.mass / distSq_sun;
            ax_total += accel_sun * dx_sun / dist_sun;
            ay_total += accel_sun * dy_sun / dist_sun;
            for (int k = 0; k < bhCount; k++) {
                if (!blackHoles[k].active) continue;
                double dx_bh = blackHoles[k].x - p.x;
                double dy_bh = blackHoles[k].y - p.y;
                double distSq_bh = dx_bh * dx_bh + dy_bh * dy_bh;
                if (distSq_bh < 0.01) distSq_bh = 0.01;
                double dist_bh = sqrt(distSq_bh);
                double accel_bh = G * blackHoles[k].mass / distSq_bh;
                ax_total += accel_bh * dx_bh / dist_bh;
                ay_total += accel_bh * dy_bh / dist_bh;
            }
            for (int j = 0; j < n; j++) {
                if (i == j || planets[j].destroyed) continue;
                Planet& p2 = planets[j];
                double dx = p2.x - p.x;
                double dy = p2.y - p.y;
                double distSq = dx * dx + dy * dy;
                if (distSq < 0.01) distSq = 0.01;
                double dist = sqrt(distSq);
                double accel = G * p2.mass / distSq;
                ax_total += accel * dx / dist;
                ay_total += accel * dy / dist;
            }
            ax[i] = ax_total;
            ay[i] = ay_total;
        }
        for (int i = 0; i < n; i++) {
            if (planets[i].destroyed) continue;
            Planet& p = planets[i];
            p.x += p.vx * subTimeStep + 0.5 * ax[i] * subTimeStep * subTimeStep;
            p.y += p.vy * subTimeStep + 0.5 * ay[i] * subTimeStep * subTimeStep;
        }
        vector<double> ax_new(n), ay_new(n);
        for (int i = 0; i < n; i++) {
            if (planets[i].destroyed) {
                ax_new[i] = 0;
                ay_new[i] = 0;
                continue;
            }
            double ax_total = 0, ay_total = 0;
            Planet& p = planets[i];
            double dx_sun = sun.x - p.x;
            double dy_sun = sun.y - p.y;
            double distSq_sun = dx_sun * dx_sun + dy_sun * dy_sun;
            if (distSq_sun < 0.01) distSq_sun = 0.01;
            double dist_sun = sqrt(distSq_sun);
            double accel_sun = G * sun.mass / distSq_sun;
            ax_total += accel_sun * dx_sun / dist_sun;
            ay_total += accel_sun * dy_sun / dist_sun;
            for (int k = 0; k < bhCount; k++) {
                if (!blackHoles[k].active) continue;
                double dx_bh = blackHoles[k].x - p.x;
                double dy_bh = blackHoles[k].y - p.y;
                double distSq_bh = dx_bh * dx_bh + dy_bh * dy_bh;
                if (distSq_bh < 0.01) distSq_bh = 0.01;
                double dist_bh = sqrt(distSq_bh);
                double accel_bh = G * blackHoles[k].mass / distSq_bh;
                ax_total += accel_bh * dx_bh / dist_bh;
                ay_total += accel_bh * dy_bh / dist_bh;
            }
            for (int j = 0; j < n; j++) {
                if (i == j || planets[j].destroyed) continue;
                Planet& p2 = planets[j];
                double dx = p2.x - p.x;
                double dy = p2.y - p.y;
                double distSq = dx * dx + dy * dy;
                if (distSq < 0.01) distSq = 0.01;
                double dist = sqrt(distSq);
                double accel = G * p2.mass / distSq;
                ax_total += accel * dx / dist;
                ay_total += accel * dy / dist;
            }
            ax_new[i] = ax_total;
            ay_new[i] = ay_total;
        }
        for (int i = 0; i < n; i++) {
            if (planets[i].destroyed) continue;
            Planet& p = planets[i];
            p.vx += 0.5 * (ax[i] + ax_new[i]) * subTimeStep;
            p.vy += 0.5 * (ay[i] + ay_new[i]) * subTimeStep;
        }
        handleCollisions();
        for (int i = 0; i < n; i++) {
            if (planets[i].destroyed) continue;
            Planet& p = planets[i];
            double dx = p.x - sun.x;
            double dy = p.y - sun.y;
            double distSq = dx * dx + dy * dy;
            double dist = sqrt(distSq);
            if (dist < sun.radius + p.radius) {
                p.destroyed = true;
                continue;
            }
            for (int k = 0; k < bhCount; k++) {
                if (!blackHoles[k].active) continue;
                double dx_bh = p.x - blackHoles[k].x;
                double dy_bh = p.y - blackHoles[k].y;
                double distSq_bh = dx_bh * dx_bh + dy_bh * dy_bh;
                double dist_bh = sqrt(distSq_bh);
                if (dist_bh < blackHoles[k].radius + p.radius) {
                    p.destroyed = true;
                    blackHoles[k].mass += p.mass * 0.3;
                    blackHoles[k].radius = 5 * scale * (1 + blackHoles[k].mass / 500.0);
                    break;
                }
            }
            if (p.destroyed) continue;
            double speed = sqrt(p.vx * p.vx + p.vy * p.vy);
            if (speed > 1000000.0) {
                p.destroyed = true;
            }
        }
        for (int k = 0; k < bhCount; k++) {
            if (!blackHoles[k].active) continue;
            blackHoles[k].mass *= 0.99995;
            if (blackHoles[k].mass < 1.0) {
                blackHoles[k].active = false;
            } else {
                blackHoles[k].radius = 5 * scale * (1 + blackHoles[k].mass / 500.0);
            }
        }
    }
}
void updatePositions() {
    for (int i = 0; i < (int)planets.size(); i++) {
        Planet& p = planets[i];
        if (p.destroyed) continue;
        p.trailX.push_back(p.x);
        p.trailY.push_back(p.y);
        if (p.trailX.size() > MAX_TRAIL) {
            int removeCount = MAX_TRAIL / 10;
            p.trailX.erase(p.trailX.begin(), p.trailX.begin() + removeCount);
            p.trailY.erase(p.trailY.begin(), p.trailY.begin() + removeCount);
        }
    }
}
void clearTrails() {
    for (int i = 0; i < (int)planets.size(); i++) {
        planets[i].trailX.clear();
        planets[i].trailY.clear();
        planets[i].destroyed = false;
        planets[i].isAsteroid = false;
        double r = sqrt((planets[i].x - sun.x) * (planets[i].x - sun.x) + (planets[i].y - sun.y) * (planets[i].y - sun.y));
        if (r < 1.0) r = 100 * scale;
        double v = sqrt(G * sun.mass / r);
        double angle = atan2(planets[i].y - sun.y, planets[i].x - sun.x);
        planets[i].vx = -v * sin(angle);
        planets[i].vy = v * cos(angle);
    }
    selectedPlanet = -1;
}
void clearBlackHoles() {
    for (int i = 0; i < (int)blackHoles.size(); i++) {
        blackHoles[i].active = false;
    }
    blackHoles.clear();
}
void drawMinimap() {
    int mx = WIDTH - 110, my = 10, mw = 100, mh = 100;
    setfillcolor(RGB(0, 0, 0));
    setlinecolor(RGB(60, 60, 60));
    fillrectangle(mx, my, mx + mw, my + mh);
    setlinecolor(RGB(60, 60, 60));
    rectangle(mx, my, mx + mw, my + mh);
    double mapSize = 500 * scale;
    double sx = mx + 50;
    double sy = my + 50;
    for (int i = 0; i < (int)planets.size(); i++) {
        if (planets[i].destroyed) continue;
        double dx = (planets[i].x - sun.x) / mapSize * 40;
        double dy = (planets[i].y - sun.y) / mapSize * 40;
        setfillcolor(planets[i].color);
        fillcircle((int)(sx + dx), (int)(sy + dy), 1);
    }
    setfillcolor(RGB(255, 200, 50));
    fillcircle((int)sx, (int)sy, 3);
    for (int k = 0; k < (int)blackHoles.size(); k++) {
        if (!blackHoles[k].active) continue;
        double dx = (blackHoles[k].x - sun.x) / mapSize * 40;
        double dy = (blackHoles[k].y - sun.y) / mapSize * 40;
        setfillcolor(RGB(80, 0, 0));
        fillcircle((int)(sx + dx), (int)(sy + dy), 2);
        setlinecolor(RGB(120, 0, 0));
        circle((int)(sx + dx), (int)(sy + dy), 3);
    }
    settextcolor(RGB(80, 80, 80));
    settextstyle(9, 0, "宋体");
    outtextxy(mx + 5, my + mh - 12, "小地图");
}
void drawManual() {
    int mx = 50, my = 30, mw = 700, mh = 540;
    setfillcolor(RGB(10, 10, 30));
    setlinecolor(RGB(150, 150, 255));
    fillrectangle(mx, my, mx + mw, my + mh);
    setlinecolor(RGB(200, 200, 255));
    rectangle(mx, my, mx + mw, my + mh);
    settextcolor(RGB(255, 255, 200));
    settextstyle(22, 0, "宋体");
    outtextxy(mx + 250, my + 10, "=== 使用手册 ===");
    settextstyle(14, 0, "宋体");
    int y = my + 50;
    settextcolor(RGB(200, 255, 200));
    outtextxy(mx + 20, y, "【基本操作】");
    y += 28;
    settextcolor(RGB(200, 200, 255));
    outtextxy(mx + 30, y, "W A S D    -  移动视角");
    y += 24;
    outtextxy(mx + 30, y, "M            -  视角回到太阳");
    y += 24;
    outtextxy(mx + 30, y, "上/下方向键  -  调整时间流速");
    y += 24;
    outtextxy(mx + 30, y, "左/右方向键  -  调整地图缩放");
    y += 24;
    outtextxy(mx + 30, y, "Shift+右     -  时间流速设为 10000x");
    y += 24;
    outtextxy(mx + 30, y, "Shift+左     -  时间流速设为 1x");
    y += 24;
    outtextxy(mx + 30, y, "空格键       -  清除所有轨迹");
    y += 24;
    outtextxy(mx + 30, y, "F            -  发射小行星");
    y += 24;
    outtextxy(mx + 30, y, "Enter        -  重开模拟");
    y += 24;
    outtextxy(mx + 30, y, "ESC          -  暂停/继续");
    y += 24;
    outtextxy(mx + 30, y, "E            -  打开/关闭控制菜单");
    y += 24;
    outtextxy(mx + 30, y, "Q            -  打开/关闭本手册");
    y += 30;
    settextcolor(RGB(200, 255, 200));
    outtextxy(mx + 20, y, "【菜单模式 (按E打开)】");
    y += 28;
    settextcolor(RGB(200, 200, 255));
    outtextxy(mx + 30, y, "B            -  开启/关闭黑洞放置");
    y += 24;
    outtextxy(mx + 30, y, "N            -  清除所有黑洞");
    y += 24;
    outtextxy(mx + 30, y, "S            -  增大太阳半径");
    y += 24;
    outtextxy(mx + 30, y, "X            -  减小太阳半径");
    y += 24;
    outtextxy(mx + 30, y, "点击行星     -  选择行星(黄色高亮)");
    y += 24;
    outtextxy(mx + 30, y, "菜单中点击+-  -  调整速度/质量");
    y += 24;
    outtextxy(mx + 30, y, "R            -  重置所有轨道");
    y += 30;
    settextcolor(RGB(255, 200, 150));
    outtextxy(mx + 20, y, "提示: 菜单打开时自动暂停");
    y += 24;
    outtextxy(mx + 20, y, "      黑洞最多100个, 吞噬行星会增大");
    settextcolor(RGB(200, 200, 255));
    outtextxy(mx + 20, mh + my - 30, "按 Q 或 ESC 关闭手册");
}
void drawMenu() {
    int menuX = 420;
    int menuY = 30;
    int menuW = 360;
    int menuH = 540;
    setfillcolor(RGB(20, 20, 30));
    setlinecolor(RGB(100, 100, 150));
    fillrectangle(menuX, menuY, menuX + menuW, menuY + menuH);
    setlinecolor(RGB(200, 200, 255));
    rectangle(menuX, menuY, menuX + menuW, menuY + menuH);
    settextcolor(RGB(200, 200, 255));
    settextstyle(18, 0, "宋体");
    outtextxy(menuX + 100, menuY + 15, "=== 控制菜单 ===");
    settextstyle(14, 0, "宋体");
    int y = menuY + 55;
    outtextxy(menuX + 20, y, "[F] 发射小行星");
    y += 28;
    outtextxy(menuX + 20, y, "[B] 放置黑洞 (点击放置, 最多100个)");
    y += 28;
    outtextxy(menuX + 20, y, "[N] 清除所有黑洞");
    y += 28;
    outtextxy(menuX + 20, y, "[S] 增大太阳  [X] 减小太阳");
    y += 28;
    outtextxy(menuX + 20, y, "[点击行星] 选择行星 (黄色高亮)");
    y += 28;
    outtextxy(menuX + 20, y, "[R] 重置轨道");
    y += 28;
    outtextxy(menuX + 20, y, "[空格] 清除轨迹");
    y += 28;
    outtextxy(menuX + 20, y, "[Enter] 重开模拟");
    y += 28;
    outtextxy(menuX + 20, y, "[E] 关闭菜单");
    y += 30;
    settextcolor(RGB(255, 255, 100));
    char info[100];
    if (selectedPlanet >= 0 && selectedPlanet < (int)planets.size() && !planets[selectedPlanet].destroyed) {
        sprintf(info, "选中行星: %d", selectedPlanet + 1);
        outtextxy(menuX + 20, y, info);
        y += 26;
        double speed = sqrt(planets[selectedPlanet].vx * planets[selectedPlanet].vx +
                           planets[selectedPlanet].vy * planets[selectedPlanet].vy);
        sprintf(info, "速度: %.2f", speed);
        outtextxy(menuX + 20, y, info);
        outtextxy(menuX + 160, y, "[+]");
        outtextxy(menuX + 200, y, "[-]");
        y += 26;
        sprintf(info, "质量: %.1f", planets[selectedPlanet].mass);
        outtextxy(menuX + 20, y, info);
        outtextxy(menuX + 160, y, "[+]");
        outtextxy(menuX + 200, y, "[-]");
        y += 26;
        if (GetAsyncKeyState(VK_LBUTTON) & 0x8000) {
            POINT p;
            GetCursorPos(&p);
            ScreenToClient(GetHWnd(), &p);
            if (p.x >= menuX + 155 && p.x <= menuX + 175 && p.y >= y - 50 && p.y <= y - 26) {
                planets[selectedPlanet].vx *= 1.05;
                planets[selectedPlanet].vy *= 1.05;
                Sleep(200);
            }
            if (p.x >= menuX + 195 && p.x <= menuX + 215 && p.y >= y - 50 && p.y <= y - 26) {
                planets[selectedPlanet].vx *= 0.95;
                planets[selectedPlanet].vy *= 0.95;
                Sleep(200);
            }
            if (p.x >= menuX + 155 && p.x <= menuX + 175 && p.y >= y - 24 && p.y <= y) {
                planets[selectedPlanet].mass *= 1.1;
                planets[selectedPlanet].radius *= 1.05;
                Sleep(200);
            }
            if (p.x >= menuX + 195 && p.x <= menuX + 215 && p.y >= y - 24 && p.y <= y) {
                planets[selectedPlanet].mass *= 0.9;
                planets[selectedPlanet].radius *= 0.95;
                if (planets[selectedPlanet].mass < 1.0) planets[selectedPlanet].mass = 1.0;
                Sleep(200);
            }
        }
    } else {
        outtextxy(menuX + 20, y, "未选中行星 (点击行星选择)");
        y += 26;
    }
    y += 10;
    sprintf(info, "太阳半径: %.1f", sun.radius);
    outtextxy(menuX + 20, y, info);
    y += 26;
    int activeBH = 0;
    for (int i = 0; i < (int)blackHoles.size(); i++) {
        if (blackHoles[i].active) activeBH++;
    }
    sprintf(info, "黑洞: %d / %d", activeBH, MAX_BLACK_HOLES);
    outtextxy(menuX + 20, y, info);
    y += 35;
    if (placingBlackHole) {
        settextcolor(RGB(255, 200, 0));
        outtextxy(menuX + 20, y, "点击画面放置黑洞");
    }
}
void drawScene() {
    cleardevice();
    double screenCenterX = WIDTH / 2;
    double screenCenterY = HEIGHT / 2;
    double sx = screenCenterX + (sun.x - WIDTH/2) / mapScale + viewOffsetX;
    double sy = screenCenterY + (sun.y - HEIGHT/2) / mapScale + viewOffsetY;
    setfillcolor(sun.color);
    setlinecolor(RGB(255, 200, 50));
    fillcircle((int)sx, (int)sy, (int)(sun.radius / mapScale));
    setlinecolor(RGB(255, 220, 100));
    circle((int)sx, (int)sy, (int)((sun.radius + 8 * scale) / mapScale));
    setlinecolor(RGB(255, 200, 50));
    circle((int)sx, (int)sy, (int)((sun.radius + 16 * scale) / mapScale));
    for (int i = 0; i < (int)planets.size(); i++) {
        const Planet& p = planets[i];
        int trailSize = p.trailX.size();
        if (trailSize < 2) continue;
        setcolor(p.color);
        setlinecolor(p.color);
        setlinestyle(PS_SOLID, 1);
        int step = trailSize > 50000 ? 2 : 1;
        for (int j = step; j < trailSize; j += step) {
            double x1 = screenCenterX + (p.trailX[j-step] - WIDTH/2) / mapScale + viewOffsetX;
            double y1 = screenCenterY + (p.trailY[j-step] - HEIGHT/2) / mapScale + viewOffsetY;
            double x2 = screenCenterX + (p.trailX[j] - WIDTH/2) / mapScale + viewOffsetX;
            double y2 = screenCenterY + (p.trailY[j] - HEIGHT/2) / mapScale + viewOffsetY;
            line((int)x1, (int)y1, (int)x2, (int)y2);
        }
    }
    for (int i = 0; i < (int)planets.size(); i++) {
        const Planet& p = planets[i];
        if (p.destroyed) continue;
        setfillcolor(p.color);
        if (i == selectedPlanet && menuOpen) {
            setlinecolor(RGB(255, 255, 0));
            setlinestyle(PS_SOLID, 2);
        } else {
            setlinecolor(WHITE);
            setlinestyle(PS_SOLID, 1);
        }
        double px = screenCenterX + (p.x - WIDTH/2) / mapScale + viewOffsetX;
        double py = screenCenterY + (p.y - HEIGHT/2) / mapScale + viewOffsetY;
        fillcircle((int)px, (int)py, (int)(p.radius / mapScale));
        setcolor(p.color);
        circle((int)px, (int)py, (int)((p.radius + 2 * scale) / mapScale));
    }
    for (int k = 0; k < (int)blackHoles.size(); k++) {
        if (!blackHoles[k].active) continue;
        double bx = screenCenterX + (blackHoles[k].x - WIDTH/2) / mapScale + viewOffsetX;
        double by = screenCenterY + (blackHoles[k].y - HEIGHT/2) / mapScale + viewOffsetY;
        double br = blackHoles[k].radius / mapScale;
        for (int r = (int)br; r > 0; r -= 2) {
            setfillcolor(RGB(0, 0, 0));
            setlinecolor(RGB(50, 50, 50));
            fillcircle((int)bx, (int)by, r);
        }
        setfillcolor(RGB(0, 0, 0));
        setlinecolor(RGB(100, 0, 0));
        fillcircle((int)bx, (int)by, (int)br);
        setcolor(RGB(200, 0, 0));
        circle((int)bx, (int)by, (int)(br * 1.5));
        setcolor(RGB(150, 0, 0));
        circle((int)bx, (int)by, (int)(br * 2.0));
    }
    drawMinimap();
    settextcolor(WHITE);
    settextstyle(14, 0, "宋体");
    char info1[100];
    sprintf(info1, "G = %.0f  时间流速: %.2fx  行星: %d", G, timeScale, (int)planets.size());
    outtextxy(10, 10, info1);
    char info2[100];
    sprintf(info2, "WASD移动 M回太阳 F小行星 Enter重开");
    outtextxy(10, 30, info2);
    char info3[100];
    sprintf(info3, "ESC暂停  Shift+右10000x Shift+左1x  E菜单 Q手册");
    outtextxy(10, 50, info3);
    int destroyedCount = 0;
    for (int i = 0; i < (int)planets.size(); i++) {
        if (planets[i].destroyed) destroyedCount++;
    }
    char info4[100];
    sprintf(info4, "毁灭: %d", destroyedCount);
    outtextxy(10, 70, info4);
    if (paused && !menuOpen && !manualOpen) {
        settextcolor(RGB(255, 200, 100));
        outtextxy(10, 90, "=== 暂停 ===");
    }
    if (manualOpen) {
        drawManual();
    } else if (menuOpen) {
        drawMenu();
        settextcolor(RGB(255, 200, 100));
        settextstyle(14, 0, "宋体");
        outtextxy(10, 110, "=== 暂停 ===");
    }
}
int main() {
    system("title 行星绕日模拟程序");
    char input[20];
    char countInput[20];
    printf("请输入引力常数 G : ");
    scanf("%s", input);
    G = atof(input);
    if (G < 10) G = 100;
    if (G > 100000000) G = 500;
    printf("请输入行星数量 (1-50): ");
    scanf("%s", countInput);
    planetCount = atoi(countInput);
    if (planetCount < 1) planetCount = 7;
    if (planetCount > 50) planetCount = 50;
    initgraph(WIDTH, HEIGHT);
    HWND hwnd = GetHWnd();
    SetWindowLong(hwnd, GWL_STYLE, GetWindowLong(hwnd, GWL_STYLE) | WS_SIZEBOX);
    SetWindowPos(hwnd, NULL, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_FRAMECHANGED);
    srand((unsigned)time(NULL));
    initSun();
    initBlackHoles();
    initPlanets();
    BeginBatchDraw();
    while (true) {
        if (GetAsyncKeyState(VK_UP) & 0x8000) {
            timeScale *= 1.02;
            if (timeScale > 10000.0) timeScale = 10000.0;
        }
        if (GetAsyncKeyState(VK_DOWN) & 0x8000) {
            timeScale /= 1.02;
            if (timeScale < 0.01) timeScale = 0.01;
        }
        if (GetAsyncKeyState(VK_LEFT) & 0x8000) {
            mapScale /= 1.05;
            if (mapScale < 0.1) mapScale = 0.1;
        }
        if (GetAsyncKeyState(VK_RIGHT) & 0x8000) {
            mapScale *= 1.05;
            if (mapScale > 100.0) mapScale = 100.0;
        }
        if (GetAsyncKeyState('W') & 0x8000) {
            viewOffsetY += 8;
        }
        if (GetAsyncKeyState('S') & 0x8000) {
            viewOffsetY -= 8;
        }
        if (GetAsyncKeyState('A') & 0x8000) {
            viewOffsetX += 8;
        }
        if (GetAsyncKeyState('D') & 0x8000) {
            viewOffsetX -= 8;
        }
        if (GetAsyncKeyState('M') & 0x8000) {
            viewOffsetX = 0;
            viewOffsetY = 0;
            mapScale = 1.0;
            Sleep(150);
        }
        if (GetAsyncKeyState('F') & 0x8000) {
            spawnAsteroid();
            Sleep(200);
        }
        if (GetAsyncKeyState(VK_RETURN) & 0x8000) {
            planets.clear();
            blackHoles.clear();
            viewOffsetX = 0;
            viewOffsetY = 0;
            mapScale = 1.0;
            timeScale = 50.0;
            paused = false;
            menuOpen = false;
            manualOpen = false;
            selectedPlanet = -1;
            placingBlackHole = false;
            initSun();
            initBlackHoles();
            initPlanets();
            Sleep(300);
        }
        if ((GetAsyncKeyState(VK_RSHIFT) & 0x8000) && (GetAsyncKeyState(VK_RIGHT) & 0x8000)) {
            timeScale = 10000.0;
        }
        if ((GetAsyncKeyState(VK_RSHIFT) & 0x8000) && (GetAsyncKeyState(VK_LEFT) & 0x8000)) {
            timeScale = 1.0;
        }
        if (GetAsyncKeyState(VK_SPACE) & 0x8000) {
            clearTrails();
            Sleep(200);
        }
        if (GetAsyncKeyState(VK_ESCAPE) & 0x8000) {
            if (manualOpen) {
                manualOpen = false;
                Sleep(200);
            } else if (menuOpen) {
                menuOpen = false;
                selectedPlanet = -1;
                Sleep(200);
            } else {
                paused = !paused;
                Sleep(200);
            }
        }
        if (GetAsyncKeyState('Q') & 0x8000) {
            manualOpen = !manualOpen;
            if (manualOpen) { menuOpen = false; }
            Sleep(200);
        }
        if (GetAsyncKeyState(0x45) & 0x8000) {
            menuOpen = !menuOpen;
            if (menuOpen) { manualOpen = false; }
            if (!menuOpen) selectedPlanet = -1;
            Sleep(200);
        }
        if (menuOpen) {
            if (GetAsyncKeyState(VK_LBUTTON) & 0x8000 && !placingBlackHole) {
                POINT p;
                GetCursorPos(&p);
                ScreenToClient(GetHWnd(), &p);
                if (p.x < 420) {
                    double screenCenterX = WIDTH / 2;
                    double screenCenterY = HEIGHT / 2;
                    double clickX = WIDTH/2 + (p.x - screenCenterX - viewOffsetX) * mapScale;
                    double clickY = HEIGHT/2 + (p.y - screenCenterY - viewOffsetY) * mapScale;
                    double minDist = 20 * scale;
                    selectedPlanet = -1;
                    for (int i = 0; i < (int)planets.size(); i++) {
                        if (planets[i].destroyed) continue;
                        double dx = planets[i].x - clickX;
                        double dy = planets[i].y - clickY;
                        double dist = sqrt(dx * dx + dy * dy);
                        if (dist < minDist) {
                            minDist = dist;
                            selectedPlanet = i;
                        }
                    }
                }
                Sleep(200);
            }
            if (GetAsyncKeyState('B') & 0x8000) {
                placingBlackHole = !placingBlackHole;
                Sleep(200);
            }
            if (GetAsyncKeyState('N') & 0x8000) {
                clearBlackHoles();
                Sleep(200);
            }
            if (GetAsyncKeyState('S') & 0x8000) {
                sun.radius *= 1.1;
                if (sun.radius > 100 * scale) sun.radius = 100 * scale;
                Sleep(100);
            }
            if (GetAsyncKeyState('X') & 0x8000) {
                sun.radius /= 1.1;
                if (sun.radius < 5 * scale) sun.radius = 5 * scale;
                Sleep(100);
            }
            if (GetAsyncKeyState('R') & 0x8000) {
                clearTrails();
                clearBlackHoles();
                Sleep(200);
            }
            if (GetAsyncKeyState(VK_LBUTTON) & 0x8000 && placingBlackHole) {
                int activeBH = 0;
                for (int i = 0; i < (int)blackHoles.size(); i++) {
                    if (blackHoles[i].active) activeBH++;
                }
                if (activeBH < MAX_BLACK_HOLES) {
                    POINT p;
                    GetCursorPos(&p);
                    ScreenToClient(GetHWnd(), &p);
                    double screenCenterX = WIDTH / 2;
                    double screenCenterY = HEIGHT / 2;
                    BlackHole bh;
                    bh.x = WIDTH/2 + (p.x - screenCenterX - viewOffsetX) * mapScale;
                    bh.y = HEIGHT/2 + (p.y - screenCenterY - viewOffsetY) * mapScale;
                    bh.active = true;
                    bh.radius = 5 * scale;
                    bh.mass = 500.0;
                    bh.color = RGB(0, 0, 0);
                    blackHoles.push_back(bh);
                }
                Sleep(300);
            }
        }
        if (!menuOpen && !manualOpen && !paused) {
            computeGravityAndUpdate();
            updatePositions();
        }
        drawScene();
        FlushBatchDraw();
        Sleep(5);
    }
    EndBatchDraw();
    closegraph();
    return 0;
}