Files
starruler-linux/maps/Spiral/SpiralMap.as
T
2018-07-17 14:15:37 +02:00

202 lines
6.0 KiB
ActionScript 3

#include "include/map.as"
enum MapSetting {
M_SystemCount,
M_SystemSpacing,
M_Flatten,
};
#section server
final class SpiralArm {
double angle = 0;
array<SystemData@> systems;
uint sysCount = 0;
array<uint> homeworlds;
};
#section all
class SpiralMap : Map {
SpiralMap() {
super();
name = locale::SPIRAL_GALAXY;
description = locale::SPIRAL_GALAXY_DESC;
color = 0x00e9ffff;
icon = "maps/Spiral/spiral.png";
sortIndex = 1000;
}
#section client
void makeSettings() {
Number(locale::SYSTEM_COUNT, M_SystemCount, DEFAULT_SYSTEM_COUNT, decimals=0, step=10, min=4, halfWidth=true);
Number(locale::SYSTEM_SPACING, M_SystemSpacing, DEFAULT_SPACING, decimals=0, step=1000, min=MIN_SPACING, halfWidth=true);
Toggle(locale::FLATTEN, M_Flatten, false);
}
#section server
void placeSystems() {
uint players = estPlayerCount;
if(players == 0)
players = 1;
const uint armCount = max(min(players, 6), 3);
const uint systemCount = max(uint(getSetting(M_SystemCount, DEFAULT_SYSTEM_COUNT)), armCount + 1);
bool flatten = getSetting(M_Flatten, 0.0) != 0.0;
uint coreSystems = max(systemCount / 4, 1);
uint perArm = (systemCount - coreSystems) / armCount;
coreSystems = systemCount - (perArm * armCount);
double systemSpacing = modSpacing(getSetting(M_SystemSpacing, DEFAULT_SPACING));
const double coreHeightVariation = flatten ? 0.0 : 1000.0;
const double heightVariation = flatten ? 0.0 : 600.0;
const double spiralBase = systemSpacing * double(systemCount) / 75.0;
const double spiralCurve = 0.5;
int coreSysType = -1;
if(systemCount > 50) {
auto@ bh = getSystemType("CoreBlackhole");
if(bh !is null)
coreSysType = bh.id;
}
addSystem(vec3d(), 500, false, coreSysType);
coreSystems -= 1;
double coreRingDist = 0.0;
//Create a bar until we can create a ring with a nice thicknes/radius ratio
{
const double barAngle = randomd(0,pi);
const double sysArea = sqr(systemSpacing*0.5);
const double goalArea = sysArea * double(coreSystems);
//area = pi*r_o^2 - pi*r_i^2 = pi*(r_o^2 - r_i^2)
//sqrt(area/pi - r_i^2) = r_o
double minRadius = systemSpacing;
double maxRadius = sqrt(goalArea/pi - minRadius*minRadius);
//Add two bar systems
while((maxRadius - minRadius) > 0.2 * minRadius && coreSystems >= 5) {
addSystem(vec3d(cos(barAngle) * minRadius, randomd(-1.0,1.0) * coreHeightVariation, sin(barAngle) * minRadius), quality=250, canHaveHomeworld=false);
addSystem(vec3d(cos(barAngle) * -minRadius, randomd(-1.0,1.0) * coreHeightVariation, sin(barAngle) * -minRadius), quality=250, canHaveHomeworld=false);
coreSystems -= 2;
minRadius += systemSpacing;
maxRadius = sqrt(goalArea/pi - minRadius*minRadius);
}
uint ring = 1;
coreRingDist = minRadius;
while(coreSystems > 0) {
uint ringSystems = min(coreSystems, 7 + ring);
ringSystems = min(uint(coreRingDist * twopi / systemSpacing), coreSystems);
if(ringSystems < coreSystems && ringSystems * 2 > coreSystems)
ringSystems = (coreSystems + 1) / 2;
for(uint i = 0; i < ringSystems; ++i) {
double r = coreRingDist + randomd(-0.3, 0.0) * systemSpacing;
double ang = twopi * (double(i) + randomd(-0.25,0.25)) / double(ringSystems);
addSystem(vec3d(cos(ang) * r, randomd(-1.0, 1.0) * coreHeightVariation, sin(ang) * r), quality=100, canHaveHomeworld=false);
}
coreSystems -= ringSystems;
if(coreSystems > 0)
coreRingDist += systemSpacing;
++ring;
}
}
double armWidth = (twopi * 0.85) / double(armCount);
array<SpiralArm> arms(armCount);
{
uint playersPerArm = players / armCount;
array<uint> armPlayers(armCount, playersPerArm);
uint left = players - (playersPerArm * armCount);
while(left != 0) {
uint index = randomi(0, armCount-1);
if(armPlayers[index] == playersPerArm) {
armPlayers[index] += 1;
left -= 1;
}
}
double ang = randomd(0, pi);
for(uint i = 0; i < armCount; ++i) {
SpiralArm@ arm = arms[i];
arm.sysCount = perArm;
arm.angle = ang;
ang += twopi / double(armCount);
uint armPlayerCount = armPlayers[i];
for(uint i = 0; i < armPlayerCount; ++i)
arm.homeworlds.insertLast(uint(float(arm.sysCount) * float(i + 1) / float(armPlayerCount + 1)));
}
}
double curve = randomi(0,1) == 1 ? 1.0 : -1.0;
for(uint i = 0; i < armCount; ++i) {
SpiralArm@ arm = arms[i];
double rSq = sqr(coreRingDist + systemSpacing);
const double angFactor = max(min(coreRingDist,spiralBase*2.0), spiralBase * 0.5) * 3.0 / double(armCount);
const uint maxRing = 30;
array<vec3d> recent;
uint ringInd = 0;
for(uint j = 0, cnt = arm.sysCount; j < cnt; ++j) {
//Fill in systems, picking a variety of points and choosing a decent sensible one
vec3d pos;
while(true) {
double radius = sqrt(rSq);
double angle = arm.angle + curve * log(radius/spiralBase) / spiralCurve;
if(angle >= pi) angle -= twopi;
double sysAngSize = systemSpacing / (twopi * radius);
angle += randomd(-0.5,0.5) * (armWidth - sysAngSize) / (0.5*radius/angFactor);
double rad = radius + randomd(-0.1,0.1) * systemSpacing;
pos = vec3d(cos(angle) * rad, 0.0, sin(angle) * rad);
rSq += systemSpacing * 8000.0;
bool validPos = true;
for(uint r = 0, rcnt = recent.length; r < rcnt; ++r) {
if(pos.distanceToSQ(recent[r]) < sqr(systemSpacing)) {
validPos = false;
break;
}
}
if(validPos)
break;
}
rSq += systemSpacing * 10000.0;
if((j+1)%10 == 0) //Disrupt periodic structure that tends to form
rSq += systemSpacing * 10000.0;
if(recent.length < maxRing)
recent.insertLast(pos);
else
recent[ringInd++ % maxRing] = pos;
pos.y = randomd(-heightVariation, heightVariation);
arm.systems.insertLast(addSystem(pos));
}
for(uint j = 0, cnt = arm.homeworlds.length; j < cnt; ++j)
addPossibleHomeworld(arm.systems[arm.homeworlds[j]]);
}
}
#section all
};