Open source Star Ruler 2 source code!
@@ -0,0 +1,276 @@
|
||||
#include "include/map.as"
|
||||
|
||||
enum MapSetting {
|
||||
M_SystemCount,
|
||||
M_SystemSpacing,
|
||||
M_Flatten,
|
||||
};
|
||||
|
||||
#section server
|
||||
class Room {
|
||||
vec3d position;
|
||||
uint circle = 0;
|
||||
SystemData@ core;
|
||||
array<SystemData@> systems;
|
||||
array<uint> circles;
|
||||
array<Room@> corrids;
|
||||
};
|
||||
#section all
|
||||
|
||||
class ClustersMap : Map {
|
||||
ClustersMap() {
|
||||
super();
|
||||
|
||||
name = locale::CLUSTERS_MAP;
|
||||
description = locale::CLUSTERS_MAP_DESC;
|
||||
|
||||
sortIndex = -100;
|
||||
|
||||
color = 0xd8ff00ff;
|
||||
icon = "maps/Clusters/clusters.png";
|
||||
}
|
||||
|
||||
#section client
|
||||
void makeSettings() {
|
||||
Number(locale::SYSTEM_COUNT, M_SystemCount, DEFAULT_SYSTEM_COUNT, decimals=0, step=10, min=20, 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, halfWidth=true);
|
||||
}
|
||||
|
||||
#section server
|
||||
void placeSystems() {
|
||||
uint systemCount = uint(getSetting(M_SystemCount, DEFAULT_SYSTEM_COUNT));
|
||||
double spacing = modSpacing(getSetting(M_SystemSpacing, DEFAULT_SPACING));
|
||||
bool flatten = getSetting(M_Flatten, 0.0) != 0.0;
|
||||
|
||||
//Calculate amount of 'rooms'
|
||||
uint roomCnt = max(ceil(pow(double(systemCount), 0.33)), double(max(estPlayerCount+1, 4)));
|
||||
uint corridorLength = clamp(ceil(double(systemCount) / 50.0), 1, 5);
|
||||
uint sysPerRoom = max(double(systemCount) / double(roomCnt) - double(corridorLength), 4.0);
|
||||
if(sysPerRoom % 2 != 0)
|
||||
sysPerRoom -= 1;
|
||||
double roomRadius = spacing * 1.5 * sqrt(double(sysPerRoom)) + spacing;
|
||||
double glxRadius = roomRadius * sqrt(double(roomCnt));
|
||||
double roomHeightVar = flatten ? 0.0 : roomRadius * 0.2;
|
||||
double sysHeightVar = flatten ? 0.0 : spacing * 0.25;
|
||||
|
||||
//Generate rooms
|
||||
double angle = 0.0;
|
||||
double radius = 0.0;
|
||||
double angleStep = twopi;
|
||||
double anglePct = 0.0;
|
||||
double startAngle = randomd(0.0, twopi);
|
||||
|
||||
array<Room@> rooms;
|
||||
Room@ circleStart;
|
||||
uint circle = 0;
|
||||
uint circleCnt = 0;
|
||||
|
||||
for(uint i = 0; i < roomCnt; ++i) {
|
||||
Room room;
|
||||
room.position = quaterniond_fromAxisAngle(vec3d_up(), angle + startAngle) * vec3d_front(radius);
|
||||
room.position.y = randomd(-roomHeightVar, roomHeightVar);
|
||||
room.circle = circle;
|
||||
|
||||
//Make room connections
|
||||
if(rooms.length != 0 && (roomCnt-i+circleCnt) > 2) {
|
||||
if(rooms[i-1].circle == circle)
|
||||
room.corrids.insertLast(rooms[i-1]);
|
||||
}
|
||||
if(circleStart is null)
|
||||
@circleStart = room;
|
||||
rooms.insertLast(room);
|
||||
++circleCnt;
|
||||
|
||||
//Generate links to previous circle
|
||||
if(circle > 0) {
|
||||
Room@ closest;
|
||||
double closestDist = INFINITY;
|
||||
Room@ second;
|
||||
double secondDist = INFINITY;
|
||||
|
||||
for(int j = int(i); j >= 0; --j) {
|
||||
if(rooms[j].circle < circle-1)
|
||||
break;
|
||||
if(rooms[j].circle > circle-1)
|
||||
continue;
|
||||
|
||||
Room@ other = rooms[j];
|
||||
double d = other.position.distanceToSQ(room.position);
|
||||
|
||||
if(d < closestDist) {
|
||||
@second = closest;
|
||||
secondDist = closestDist;
|
||||
closestDist = d;
|
||||
@closest = other;
|
||||
}
|
||||
else if(d < secondDist) {
|
||||
@second = other;
|
||||
secondDist = d;
|
||||
}
|
||||
}
|
||||
|
||||
if(closest !is null)
|
||||
room.corrids.insertLast(closest);
|
||||
if(second !is null)
|
||||
room.corrids.insertLast(second);
|
||||
}
|
||||
|
||||
//Proceed to next room position
|
||||
angle += angleStep;
|
||||
if(angle + angleStep > twopi) {
|
||||
angle = 0.0;
|
||||
radius += roomRadius;
|
||||
startAngle = randomd(0.0, twopi);
|
||||
|
||||
anglePct = (roomRadius / (2.0 * pi * radius));
|
||||
if(roomCnt - i - 1 > 0)
|
||||
angleStep = max(anglePct * twopi, twopi / double(roomCnt - i - 1));
|
||||
circle += 1;
|
||||
|
||||
if(circleStart !is null && circle != 1 && circleStart !is room && circleCnt > 2)
|
||||
room.corrids.insertLast(circleStart);
|
||||
@circleStart = null;
|
||||
circleCnt = 0;
|
||||
}
|
||||
}
|
||||
|
||||
//Generate systems
|
||||
for(uint i = 0; i < roomCnt; ++i) {
|
||||
Room@ room = rooms[i];
|
||||
|
||||
//Generate systems
|
||||
angle = 0.0;
|
||||
radius = 0.0;
|
||||
angleStep = twopi;
|
||||
startAngle = randomd(0.0, twopi);
|
||||
circle = 0;
|
||||
int quality = 0;
|
||||
if(i == 0 && roomCnt > estPlayerCount)
|
||||
quality = 100;
|
||||
uint hwInd = randomi(0, sysPerRoom-1);
|
||||
for(uint n = 0; n < sysPerRoom; ++n) {
|
||||
//Generate system
|
||||
vec3d pos = quaterniond_fromAxisAngle(vec3d_up(), angle + startAngle) * vec3d_front(radius);
|
||||
pos.y = randomd(-sysHeightVar, sysHeightVar);
|
||||
pos += room.position;
|
||||
SystemData@ sys = addSystem(pos, quality=quality);
|
||||
if(i != 0) {
|
||||
if(hwInd == n)
|
||||
addPossibleHomeworld(sys);
|
||||
}
|
||||
else if(circle == 0) {
|
||||
auto@ blackHole = getSystemType("CoreBlackhole");
|
||||
if(blackHole !is null)
|
||||
sys.systemType = int(blackHole.id);
|
||||
}
|
||||
|
||||
room.systems.insertLast(sys);
|
||||
room.circles.insertLast(circle);
|
||||
|
||||
//Generate links to previous circle
|
||||
if(circle > 0) {
|
||||
SystemData@ closest;
|
||||
double closestDist = INFINITY;
|
||||
SystemData@ second;
|
||||
double secondDist = INFINITY;
|
||||
|
||||
for(int j = int(n); j >= 0; --j) {
|
||||
if(room.circles[j] < circle-1)
|
||||
break;
|
||||
if(room.circles[j] > circle-1)
|
||||
continue;
|
||||
|
||||
SystemData@ other = room.systems[j];
|
||||
double d = other.position.distanceToSQ(sys.position);
|
||||
|
||||
if(d < closestDist) {
|
||||
@second = closest;
|
||||
secondDist = closestDist;
|
||||
closestDist = d;
|
||||
@closest = other;
|
||||
}
|
||||
else if(d < secondDist) {
|
||||
@second = other;
|
||||
secondDist = d;
|
||||
}
|
||||
}
|
||||
|
||||
if(closest !is null)
|
||||
addLink(sys, closest);
|
||||
if(second !is null)
|
||||
addLink(sys, second);
|
||||
}
|
||||
|
||||
//Proceed to next system position
|
||||
angle += angleStep;
|
||||
if(angle + angleStep > twopi) {
|
||||
angle = 0.0;
|
||||
radius += spacing;
|
||||
startAngle = randomd(0.0, twopi);
|
||||
|
||||
anglePct = (spacing / (2.0 * pi * radius));
|
||||
if(sysPerRoom - n - 1 > 0)
|
||||
angleStep = max(anglePct * twopi, twopi / double(sysPerRoom - n - 1));
|
||||
circle += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Generate corridors
|
||||
for(uint i = 0; i < roomCnt; ++i) {
|
||||
Room@ room = rooms[i];
|
||||
|
||||
for(uint n = 0, ncnt = room.corrids.length; n < ncnt; ++n) {
|
||||
Room@ other = room.corrids[n];
|
||||
|
||||
//Build corridor between rooms
|
||||
vec3d offset = (other.position - room.position);
|
||||
double offdist = offset.length;
|
||||
double spacStep = spacing / offdist;
|
||||
double corStart = roomRadius * 0.5 / offdist;
|
||||
double corEnd = 1.0 - corStart;
|
||||
uint len = clamp(floor((corEnd - corStart) / spacStep), 1, corridorLength);
|
||||
double corStep = (corEnd - corStart) / double(len);
|
||||
corStart += corStep * 0.5;
|
||||
|
||||
SystemData@ first, last;
|
||||
for(uint x = 0; x < len; ++x) {
|
||||
vec3d corPos = room.position + offset * (corStep * x + corStart);
|
||||
if(room.circle == other.circle)
|
||||
corPos = corPos.normalized(room.position.length - roomRadius * 0.25);
|
||||
|
||||
auto@ sys = addSystem(corPos, quality=200);
|
||||
if(last !is null)
|
||||
addLink(last, sys);
|
||||
@last = sys;
|
||||
if(x == 0)
|
||||
@first = last;
|
||||
}
|
||||
|
||||
//Find systems to link to
|
||||
double dist = INFINITY;
|
||||
SystemData@ closest;
|
||||
for(uint j = 0, jcnt = room.systems.length; j < jcnt; ++j) {
|
||||
double d = room.systems[j].position.distanceToSQ(first.position);
|
||||
if(d < dist) {
|
||||
@closest = room.systems[j];
|
||||
dist = d;
|
||||
}
|
||||
}
|
||||
addLink(closest, first);
|
||||
|
||||
dist = INFINITY;
|
||||
for(uint j = 0, jcnt = other.systems.length; j < jcnt; ++j) {
|
||||
double d = other.systems[j].position.distanceToSQ(last.position);
|
||||
if(d < dist) {
|
||||
@closest = other.systems[j];
|
||||
dist = d;
|
||||
}
|
||||
}
|
||||
addLink(closest, last);
|
||||
}
|
||||
}
|
||||
}
|
||||
#section all
|
||||
};
|
||||
|
After Width: | Height: | Size: 287 KiB |
@@ -0,0 +1,125 @@
|
||||
#include "include/map.as"
|
||||
|
||||
enum MapSetting {
|
||||
M_SystemCount,
|
||||
M_SystemSpacing,
|
||||
M_Flatten,
|
||||
M_ConnectEnds,
|
||||
};
|
||||
|
||||
class CorkscrewMap : Map {
|
||||
CorkscrewMap() {
|
||||
super();
|
||||
|
||||
name = locale::CORKSCREW_GALAXY;
|
||||
description = locale::CORKSCREW_GALAXY_DESC;
|
||||
|
||||
color = 0xff5900ff;
|
||||
icon = "maps/Corkscrew/corkscrew.png";
|
||||
}
|
||||
|
||||
#section client
|
||||
void makeSettings() {
|
||||
Number(locale::SYSTEM_COUNT, M_SystemCount, DEFAULT_SYSTEM_COUNT, decimals=0, step=10, min=6, 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, halfWidth=true);
|
||||
Toggle(locale::CONNECT_ENDS, M_ConnectEnds, true, halfWidth=true);
|
||||
}
|
||||
|
||||
#section server
|
||||
void placeSystems() {
|
||||
uint systemCount = uint(getSetting(M_SystemCount, DEFAULT_SYSTEM_COUNT));
|
||||
double spacing = modSpacing(getSetting(M_SystemSpacing, DEFAULT_SPACING));
|
||||
bool flatten = getSetting(M_Flatten, 0.0) != 0.0;
|
||||
bool connectEnds = getSetting(M_ConnectEnds, 1.0) != 0.0;
|
||||
autoGenerateLinks = false;
|
||||
|
||||
uint width = max(1, uint(ceil(log(double(systemCount))/log(10.0))));
|
||||
uint length = ceil(double(systemCount) / double(width));
|
||||
double startAngle = randomd(0.0, twopi);
|
||||
double angle = 0.0;
|
||||
double radius = spacing * 2.0;
|
||||
double height = 0.0;
|
||||
double heightDiff = spacing * 0.25;
|
||||
|
||||
int xStart = -floor(double(width)/2.0);
|
||||
int xEnd = ceil(double(width)/2.0);
|
||||
|
||||
vec3d pos;
|
||||
vec3d prevPos = quaterniond_fromAxisAngle(vec3d_up(), startAngle - 0.1) * vec3d_front(radius);
|
||||
prevPos.y = spacing;
|
||||
|
||||
int hwStep, hwOffset;
|
||||
if(connectEnds) {
|
||||
hwStep = floor(double(systemCount) / double(max(estPlayerCount, 1)));
|
||||
hwOffset = (hwStep / 2) + randomi(-width,width);
|
||||
}
|
||||
else {
|
||||
hwStep = floor(double(systemCount) / double(max(estPlayerCount - 1, 1)));
|
||||
hwOffset = hwStep - 1 + randomi(-width,0);
|
||||
}
|
||||
|
||||
array<SystemData@> cur(width);
|
||||
array<SystemData@> prev(width);
|
||||
int count = 0;
|
||||
for(uint n = 0; n < length; ++n) {
|
||||
//Make systems in strip
|
||||
vec3d offset;
|
||||
for(int x = xStart, i = 0; x < xEnd; ++x) {
|
||||
pos = quaterniond_fromAxisAngle(vec3d_up(), startAngle + angle) * vec3d_front(radius);
|
||||
pos.y = height + randomd(-heightDiff, heightDiff);
|
||||
|
||||
if(x == xStart)
|
||||
offset = (pos - prevPos).cross(vec3d_up()).normalized(spacing);
|
||||
pos += offset * -double(x);
|
||||
|
||||
if(flatten)
|
||||
pos.y = 0;
|
||||
if(x == 0)
|
||||
prevPos = pos;
|
||||
|
||||
SystemData@ sys = addSystem(pos);
|
||||
@cur[i] = sys;
|
||||
++i;
|
||||
|
||||
++count;
|
||||
if((count + hwOffset) % hwStep == 0)
|
||||
addPossibleHomeworld(sys);
|
||||
}
|
||||
|
||||
//Make links
|
||||
for(uint i = 0; i < width; ++i) {
|
||||
if(i != 0)
|
||||
addLink(cur[i-1], cur[i]);
|
||||
if(i != width-1)
|
||||
addLink(cur[i], cur[i+1]);
|
||||
if(prev[i] !is null)
|
||||
addLink(prev[i], cur[i]);
|
||||
}
|
||||
prev = cur;
|
||||
|
||||
//Update next position
|
||||
double anglePct = (spacing / (2.0 * pi * radius));
|
||||
angle += anglePct * twopi;
|
||||
radius += spacing * anglePct * double(width) * 1.5;
|
||||
height -= spacing * anglePct * 4.0;
|
||||
|
||||
if(angle >= twopi)
|
||||
angle -= twopi;
|
||||
}
|
||||
|
||||
|
||||
uint genCnt = systemData.length;
|
||||
|
||||
//Add final homeworld
|
||||
if(possibleHomeworlds.length < estPlayerCount)
|
||||
addPossibleHomeworld(systemData[randomi(genCnt-(width*2), genCnt-1)]);
|
||||
|
||||
//Create the wormhole
|
||||
if(connectEnds) {
|
||||
createWormhole( systemData[randomi(0, width-1)],
|
||||
systemData[randomi(genCnt-width, genCnt-1)]);
|
||||
}
|
||||
}
|
||||
#section all
|
||||
};
|
||||
|
After Width: | Height: | Size: 272 KiB |
@@ -0,0 +1,125 @@
|
||||
#include "include/map.as"
|
||||
|
||||
enum MapSetting {
|
||||
M_SystemCount,
|
||||
M_SystemSpacing,
|
||||
M_Flatten,
|
||||
M_Mirror,
|
||||
};
|
||||
|
||||
class DumbbellMap : Map {
|
||||
DumbbellMap() {
|
||||
super();
|
||||
|
||||
name = locale::DUMBBELL_MAP;
|
||||
description = locale::DUMBBELL_MAP_DESC;
|
||||
|
||||
sortIndex = -100;
|
||||
|
||||
color = 0xd252ffff;
|
||||
icon = "maps/Dumbbell/dumbbell.png";
|
||||
}
|
||||
|
||||
#section client
|
||||
void makeSettings() {
|
||||
Number(locale::SYSTEM_COUNT, M_SystemCount, DEFAULT_SYSTEM_COUNT, decimals=0, step=10, min=10, 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, halfWidth=true);
|
||||
Toggle(locale::PERFECT_MIRROR, M_Mirror, false, halfWidth=true, tooltip=locale::TT_PERFECT_MIRROR);
|
||||
}
|
||||
|
||||
#section server
|
||||
void placeSystems() {
|
||||
uint systemCount = uint(getSetting(M_SystemCount, DEFAULT_SYSTEM_COUNT));
|
||||
double spacing = modSpacing(getSetting(M_SystemSpacing, DEFAULT_SPACING));
|
||||
bool flatten = getSetting(M_Flatten, 0.0) != 0.0;
|
||||
bool mirror = getSetting(M_Mirror, 0.0) != 0.0;
|
||||
|
||||
//Calculate values
|
||||
double bellSystems = ceil(double(systemCount) * 0.43);
|
||||
double bellRadius = sqrt(bellSystems) * spacing * 0.5;
|
||||
|
||||
//Make left bell
|
||||
genBell(vec3d(0, 0, -bellRadius*3.0), bellRadius, bellSystems);
|
||||
genCorridor(vec3d(0, 0, -bellRadius*2.0+spacing*0.75), vec3d(0, 0, -spacing), ceil(double(systemCount) * 0.07));
|
||||
|
||||
if(!mirror) {
|
||||
//Make right bell
|
||||
genBell(vec3d(0, 0, bellRadius*3.0), bellRadius, bellSystems);
|
||||
genCorridor(vec3d(0, 0, 0), vec3d(0, 0, bellRadius*2.0-spacing*0.75), ceil(double(systemCount) * 0.07));
|
||||
}
|
||||
else {
|
||||
//Create empty systems opposite every generated system
|
||||
for(uint i = 0, cnt = systemData.length; i < cnt; ++i) {
|
||||
auto@ other = systemData[i];
|
||||
vec3d pos = other.position;
|
||||
pos.z = -pos.z;
|
||||
|
||||
auto@ sys = addSystem(pos, mirrorSystem = other);
|
||||
|
||||
if(possibleHomeworlds.find(other) != -1)
|
||||
addPossibleHomeworld(sys);
|
||||
}
|
||||
|
||||
genCorridor(vec3d(0, 0, 0), vec3d(0, 0, 0), ceil(double(systemCount) * 0.07), quality=500);
|
||||
}
|
||||
}
|
||||
|
||||
void genBell(vec3d around, double radius, uint systemCount) {
|
||||
double spacing = modSpacing(getSetting(M_SystemSpacing, DEFAULT_SPACING));
|
||||
bool flatten = getSetting(M_Flatten, 0.0) != 0.0;
|
||||
|
||||
Poisson2D gen;
|
||||
gen.circleRadius = radius;
|
||||
gen.generate(radius*2.0, radius*2.0, spacing);
|
||||
|
||||
SystemData@ centerMost;
|
||||
double centerDist = INFINITY;
|
||||
for(uint i = 0, cnt = min(gen.length, systemCount); i < cnt; ++i) {
|
||||
vec3d pos = around;
|
||||
vec2d offset = gen[i] - vec2d(radius, radius);
|
||||
pos.x += offset.x;
|
||||
pos.z += offset.y;
|
||||
if(!flatten)
|
||||
pos.y += randomd(-spacing * 0.3, spacing * 0.3);
|
||||
|
||||
auto@ sys = addSystem(pos);
|
||||
|
||||
double d = offset.length;
|
||||
if(d < centerDist) {
|
||||
@centerMost = sys;
|
||||
centerDist = d;
|
||||
}
|
||||
}
|
||||
|
||||
addPossibleHomeworld(centerMost);
|
||||
}
|
||||
|
||||
void genCorridor(vec3d origin, vec3d dest, uint systemWidth, int quality = 0) {
|
||||
double spacing = modSpacing(getSetting(M_SystemSpacing, DEFAULT_SPACING));
|
||||
bool flatten = getSetting(M_Flatten, 0.0) != 0.0;
|
||||
|
||||
double wstep = 1.0 / ceil(sqrt(double(systemWidth)) / 0.75);
|
||||
if(origin.distanceTo(dest) < spacing)
|
||||
wstep = 1.1;
|
||||
else
|
||||
wstep = max(wstep, 1.0 / max(floor(dest.distanceTo(origin) / spacing), 1.0));
|
||||
uint height = ceil(sqrt(double(systemWidth) * 0.75));
|
||||
double w = 0.0;
|
||||
while(w <= 1.0) {
|
||||
vec3d pos = origin + (dest-origin)*w;
|
||||
pos.x -= randomd(0.8, 1.5) * spacing * (double(height-1)/2);
|
||||
|
||||
for(uint n = 0; n < height; ++n) {
|
||||
vec3d sysPos = pos;
|
||||
if(!flatten)
|
||||
sysPos.y += randomd(-spacing * 0.3, spacing * 0.3);
|
||||
addSystem(sysPos, quality=quality);
|
||||
pos.x += randomd(0.8, 1.5) * spacing;
|
||||
}
|
||||
|
||||
w += wstep;
|
||||
}
|
||||
}
|
||||
#section all
|
||||
}
|
||||
|
After Width: | Height: | Size: 32 KiB |
@@ -0,0 +1,571 @@
|
||||
#include "include/map.as"
|
||||
|
||||
#section server
|
||||
import saving;
|
||||
import map_loader;
|
||||
import util.design_designer;
|
||||
from empire import Creeps;
|
||||
from game_start import generateNewSystem, SystemGenerateHook;
|
||||
import object_creation;
|
||||
import ship_groups;
|
||||
import map_systems;
|
||||
import statuses;
|
||||
from remnant_designs import spawnRemnantFleet, loadRemnantDesigns;
|
||||
|
||||
class Room {
|
||||
vec3d position;
|
||||
SystemData@ core;
|
||||
uint circle = 0;
|
||||
double angle = 0;
|
||||
array<SystemData@> systems;
|
||||
array<uint> circles;
|
||||
array<Room@> corrids;
|
||||
};
|
||||
#section all
|
||||
|
||||
enum MapSetting {
|
||||
M_SysCount,
|
||||
M_SystemSpacing,
|
||||
M_Flatten,
|
||||
};
|
||||
|
||||
const uint INITIAL_EXPANSE = 3;
|
||||
const double REMNANT_FACTOR = 2.0;
|
||||
|
||||
class ExpanseMap : Map {
|
||||
ExpanseMap() {
|
||||
super();
|
||||
|
||||
name = locale::EXPANSE_MAP;
|
||||
description = locale::EXPANSE_MAP_DESC;
|
||||
|
||||
sortIndex = -120;
|
||||
eatsPlayers = true;
|
||||
|
||||
color = 0x5273ffff;
|
||||
icon = "maps/Expanse/expanse.png";
|
||||
}
|
||||
|
||||
#section client
|
||||
void makeSettings() {
|
||||
Toggle(locale::FLATTEN, M_Flatten, false, halfWidth=true);
|
||||
Number(locale::SYSTEM_SPACING, M_SystemSpacing, DEFAULT_SPACING, decimals=0, step=1000, min=MIN_SPACING, halfWidth=true);
|
||||
Description(locale::EXPANSE_MAP_TEXT, lines=3);
|
||||
}
|
||||
|
||||
#section server
|
||||
array<Room@> rooms;
|
||||
|
||||
void preGenerate() {
|
||||
Map::preGenerate();
|
||||
radius *= 3.0;
|
||||
}
|
||||
|
||||
void generateRegions() {
|
||||
radius /= 3.0;
|
||||
Map::generateRegions();
|
||||
}
|
||||
|
||||
void placeSystems() {
|
||||
//Generate remnant homeworld
|
||||
loadMap("maps/Expanse/coreMap.txt").generate(this);
|
||||
|
||||
//Generate base clusters
|
||||
double spacing = modSpacing(getSetting(M_SystemSpacing, DEFAULT_SPACING));
|
||||
bool flatten = getSetting(M_Flatten, 0.0) != 0.0;
|
||||
|
||||
//Calculate amount of 'rooms'
|
||||
uint players = estPlayerCount;
|
||||
if(players == 0)
|
||||
players = 3;
|
||||
|
||||
uint roomCnt = players+1;
|
||||
uint corridorLength = 2;
|
||||
uint sysPerRoom = 7;
|
||||
double roomRadius = spacing * 1.5 * sqrt(double(sysPerRoom)) + spacing;
|
||||
double roomHeightVar = flatten ? 0.0 : roomRadius * 0.2;
|
||||
double sysHeightVar = flatten ? 0.0 : spacing * 0.25;
|
||||
|
||||
double playerOffset = max(roomRadius / (twopi / double(players)), roomRadius);
|
||||
|
||||
//Generate rooms
|
||||
double angle = 0.0;
|
||||
double radius = 0.0;
|
||||
double angleStep = twopi;
|
||||
double anglePct = 0.0;
|
||||
double startAngle = randomd(0.0, twopi);
|
||||
|
||||
Room@ circleStart;
|
||||
uint circle = 0;
|
||||
uint circleCnt = 0;
|
||||
|
||||
for(uint i = 0; i < roomCnt; ++i) {
|
||||
Room room;
|
||||
room.position = quaterniond_fromAxisAngle(vec3d_up(), angle + startAngle) * vec3d_front(radius);
|
||||
if(i != 0)
|
||||
room.position.y = randomd(-roomHeightVar, roomHeightVar);
|
||||
room.circle = circle;
|
||||
room.angle = angle + startAngle;
|
||||
|
||||
rooms.insertLast(room);
|
||||
if(i != 0)
|
||||
room.corrids.insertLast(rooms[0]);
|
||||
++circleCnt;
|
||||
|
||||
//Proceed to next room position
|
||||
angle += angleStep;
|
||||
if(angle + angleStep > twopi) {
|
||||
angle = 0.0;
|
||||
radius += playerOffset;
|
||||
startAngle = randomd(0.0, twopi);
|
||||
angleStep = twopi / max(double(roomCnt - i - 1), 1.0);
|
||||
circle += 1;
|
||||
|
||||
circleCnt = 0;
|
||||
}
|
||||
}
|
||||
|
||||
//Generate systems
|
||||
for(uint i = 0; i < roomCnt; ++i) {
|
||||
Room@ room = rooms[i];
|
||||
|
||||
//Generate systems
|
||||
uint sysCount = sysPerRoom;
|
||||
angle = 0.0;
|
||||
radius = i == 0 ? 9000 : 0;
|
||||
angleStep = twopi;
|
||||
if(radius != 0) {
|
||||
anglePct = (spacing / (2.0 * pi * radius));
|
||||
if(sysCount > 0)
|
||||
angleStep = max(anglePct * twopi, twopi / double(sysCount));
|
||||
}
|
||||
startAngle = randomd(0.0, twopi);
|
||||
circle = 0;
|
||||
int quality = 0;
|
||||
if(i == 0 && roomCnt > estPlayerCount)
|
||||
quality = 200;
|
||||
for(uint n = 0; n < sysCount; ++n) {
|
||||
//Generate system
|
||||
vec3d pos = quaterniond_fromAxisAngle(vec3d_up(), angle + startAngle) * vec3d_front(radius);
|
||||
if(!flatten)
|
||||
pos.y = randomd(-sysHeightVar, sysHeightVar);
|
||||
pos += room.position;
|
||||
SystemData@ sys = addSystem(pos, quality=quality);
|
||||
if(circle == 0 && (i != 0 || roomCnt == estPlayerCount))
|
||||
addPossibleHomeworld(sys);
|
||||
sys.assignGroup = i;
|
||||
|
||||
room.systems.insertLast(sys);
|
||||
room.circles.insertLast(circle);
|
||||
|
||||
//Generate links to previous circle
|
||||
if(circle > 0) {
|
||||
SystemData@ closest;
|
||||
double closestDist = INFINITY;
|
||||
SystemData@ second;
|
||||
double secondDist = INFINITY;
|
||||
|
||||
for(int j = int(n); j >= 0; --j) {
|
||||
if(room.circles[j] < circle-1)
|
||||
break;
|
||||
if(room.circles[j] > circle-1)
|
||||
continue;
|
||||
|
||||
SystemData@ other = room.systems[j];
|
||||
double d = other.position.distanceToSQ(sys.position);
|
||||
|
||||
if(d < closestDist) {
|
||||
@second = closest;
|
||||
secondDist = closestDist;
|
||||
closestDist = d;
|
||||
@closest = other;
|
||||
}
|
||||
else if(d < secondDist) {
|
||||
@second = other;
|
||||
secondDist = d;
|
||||
}
|
||||
}
|
||||
|
||||
if(closest !is null)
|
||||
addLink(sys, closest);
|
||||
if(second !is null)
|
||||
addLink(sys, second);
|
||||
}
|
||||
|
||||
//Proceed to next system position
|
||||
angle += angleStep;
|
||||
if(angle + angleStep > twopi) {
|
||||
angle = 0.0;
|
||||
radius += spacing;
|
||||
startAngle = randomd(0.0, twopi);
|
||||
|
||||
anglePct = (spacing / (2.0 * pi * radius));
|
||||
if(sysCount - n - 1 > 0)
|
||||
angleStep = max(anglePct * twopi, twopi / double(sysCount - n - 1));
|
||||
circle += 1;
|
||||
}
|
||||
}
|
||||
|
||||
//Make expanse systems
|
||||
if(i != 0) {
|
||||
double aStep = min(twopi / double(roomCnt - 1), pi*0.33);
|
||||
double minAngle = room.angle - aStep * 0.5;
|
||||
double maxAngle = room.angle + aStep * 0.5;
|
||||
aStep = (maxAngle - minAngle) / double(INITIAL_EXPANSE);
|
||||
for(uint n = 0; n < INITIAL_EXPANSE; ++n) {
|
||||
double ang = minAngle + double(n) * aStep + aStep * 0.5;
|
||||
double rad = room.position.length + radius;
|
||||
vec3d pos = quaterniond_fromAxisAngle(vec3d_up(), ang) * vec3d_front(rad);
|
||||
pos.y = room.position.y;
|
||||
if(!flatten)
|
||||
pos.y += randomd(-spacing*0.5, spacing*0.5);
|
||||
|
||||
auto@ sys = addSystem(pos);
|
||||
sys.assignGroup = i;
|
||||
|
||||
ExpanseSystem es;
|
||||
es.index = sys.index;
|
||||
es.shouldExpand = true;
|
||||
es.minAngle = minAngle;
|
||||
es.maxAngle = maxAngle;
|
||||
es.circle = 0;
|
||||
es.radius = rad;
|
||||
es.field = i;
|
||||
es.position = n;
|
||||
es.spacing = spacing;
|
||||
es.flatten = flatten;
|
||||
trackSystems.insertLast(es);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Generate corridors
|
||||
for(uint i = 0; i < roomCnt; ++i) {
|
||||
Room@ room = rooms[i];
|
||||
|
||||
for(uint n = 0, ncnt = room.corrids.length; n < ncnt; ++n) {
|
||||
Room@ other = room.corrids[n];
|
||||
|
||||
//Build corridor between rooms
|
||||
vec3d offset = (other.position - room.position);
|
||||
double offdist = offset.length;
|
||||
double spacStep = spacing / offdist;
|
||||
double corStart = roomRadius * 0.5 / offdist;
|
||||
double corEnd = 1.0 - corStart;
|
||||
uint len = clamp(floor((corEnd - corStart) / spacStep), 1, corridorLength);
|
||||
double corStep = (corEnd - corStart) / double(len);
|
||||
corStart += corStep * 0.5;
|
||||
|
||||
SystemData@ first, last;
|
||||
for(uint x = 0; x < len; ++x) {
|
||||
vec3d corPos = room.position + offset * (corStep * x + corStart);
|
||||
if(room.circle == other.circle)
|
||||
corPos = corPos.normalized(room.position.length - roomRadius * 0.25);
|
||||
|
||||
auto@ sys = addSystem(corPos, quality=200);
|
||||
if(last !is null)
|
||||
addLink(last, sys);
|
||||
@last = sys;
|
||||
if(x == 0)
|
||||
@first = last;
|
||||
}
|
||||
|
||||
//Find systems to link to
|
||||
double dist = INFINITY;
|
||||
SystemData@ closest;
|
||||
for(uint j = 0, jcnt = room.systems.length; j < jcnt; ++j) {
|
||||
double d = room.systems[j].position.distanceToSQ(first.position);
|
||||
if(d < dist) {
|
||||
@closest = room.systems[j];
|
||||
dist = d;
|
||||
}
|
||||
}
|
||||
addLink(closest, first);
|
||||
|
||||
dist = INFINITY;
|
||||
for(uint j = 0, jcnt = other.systems.length; j < jcnt; ++j) {
|
||||
double d = other.systems[j].position.distanceToSQ(last.position);
|
||||
if(d < dist) {
|
||||
@closest = other.systems[j];
|
||||
dist = d;
|
||||
}
|
||||
}
|
||||
addLink(closest, last);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void createRemnant(SystemDesc@ system, double size, ExpanseSystem@ es = null) {
|
||||
int fleetSize = ceil(size / double(es.defenders.length + es.queuedFleets) / 25.0) * 25;
|
||||
|
||||
vec3d pos = system.position;
|
||||
vec2d offset = random2d(500, system.radius*0.85);
|
||||
pos.x += offset.x;
|
||||
pos.z += offset.y;
|
||||
|
||||
auto@ ship = spawnRemnantFleet(pos, fleetSize, 0.4);
|
||||
if(es !is null && ship !is null) {
|
||||
es.defenders.insertLast(ship);
|
||||
if(es.queuedFleets > 0)
|
||||
es.queuedFleets--;
|
||||
}
|
||||
}
|
||||
|
||||
void createRemnants(SystemDesc@ system, double size, ExpanseSystem@ es, bool queued = false) {
|
||||
es.queuedFleets = max(randomd(pow(size, 0.14), pow(size, 0.24)), 1.0);
|
||||
if(queued)
|
||||
return;
|
||||
|
||||
while(es.queuedFleets > 0)
|
||||
createRemnant(system, size, es);
|
||||
}
|
||||
|
||||
void setPlanetsLocked(SystemDesc@ system, bool value) {
|
||||
auto@ status = getStatusType("BlockedColonization");
|
||||
uint plCnt = system.object.planetCount;
|
||||
for(uint i = 0, cnt = plCnt; i < cnt; ++i) {
|
||||
if(status !is null) {
|
||||
if(value)
|
||||
system.object.planets[i].addStatus(status.id);
|
||||
else
|
||||
system.object.planets[i].removeStatusInstanceOfType(status.id);
|
||||
}
|
||||
else {
|
||||
system.object.planets[i].setQuarantined(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ExpanseSystem@ setExpanseSystem(SystemDesc@ sys, double remnantSize) {
|
||||
ExpanseSystem es;
|
||||
@es.system = sys;
|
||||
es.remnantSize = remnantSize;
|
||||
es.origin = origin;
|
||||
|
||||
createRemnants(sys, remnantSize, es);
|
||||
setPlanetsLocked(sys, true);
|
||||
|
||||
trackSystems.insertLast(es);
|
||||
|
||||
return es;
|
||||
}
|
||||
|
||||
array<ExpanseSystem@> trackSystems;
|
||||
|
||||
void init() {
|
||||
for(uint i = 0, cnt = trackSystems.length; i < cnt; ++i) {
|
||||
auto@ es = trackSystems[i];
|
||||
es.origin = origin;
|
||||
@es.system = getSystem(systems[0].index + es.index);
|
||||
es.remnantSize = 100;
|
||||
createRemnants(es.system, es.remnantSize, es);
|
||||
setPlanetsLocked(es.system, true);
|
||||
}
|
||||
|
||||
setExpanseSystem(getSystem(0), randomd(1200, 1800));
|
||||
for(uint i = 0, cnt = rooms[0].systems.length; i < cnt; ++i) {
|
||||
auto@ sys = getSystem(systems[0].index + rooms[0].systems[i].index);
|
||||
setExpanseSystem(sys, randomd(200, 600));
|
||||
}
|
||||
}
|
||||
|
||||
void tick(double time) {
|
||||
bool madeFleet = false;
|
||||
|
||||
for(uint i = 0, cnt = trackSystems.length; i < cnt; ++i) {
|
||||
auto@ es = trackSystems[i];
|
||||
if(es.system is null)
|
||||
continue;
|
||||
if(es.queuedFleets > 0) {
|
||||
if(!madeFleet) {
|
||||
createRemnant(es.system, es.remnantSize, es);
|
||||
madeFleet = true;
|
||||
}
|
||||
}
|
||||
else if(es.defended) {
|
||||
bool defended = false;
|
||||
for(uint n = 0, ncnt = es.defenders.length; n < ncnt; ++n) {
|
||||
auto@ defender = es.defenders[n];
|
||||
if(defender !is null && defender.valid) {
|
||||
defended = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(!defended) {
|
||||
setPlanetsLocked(es.system, false);
|
||||
es.defended = false;
|
||||
es.defenders.length = 0;
|
||||
|
||||
if(es.shouldExpand)
|
||||
expandSystem(es);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void expandSystem(ExpanseSystem@ es) {
|
||||
//Reveal the two closest systems in the next ring
|
||||
for(uint i = 0; i < 2; ++i) {
|
||||
bool found = false;
|
||||
for(uint n = 0, ncnt = trackSystems.length; n < ncnt; ++n) {
|
||||
auto@ other = trackSystems[n];
|
||||
if(other.field == es.field && other.circle == es.circle+1 && other.position == es.position+i) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(found)
|
||||
continue;
|
||||
|
||||
ExpanseSystem new;
|
||||
new.origin = es.origin;
|
||||
new.circle = es.circle+1;
|
||||
new.spacing = es.spacing;
|
||||
new.position = es.position+i;
|
||||
new.flatten = es.flatten;
|
||||
new.field = es.field;
|
||||
new.minAngle = es.minAngle;
|
||||
new.maxAngle = es.maxAngle;
|
||||
new.radius = es.radius + es.spacing;
|
||||
new.index = generatedSystems.length;
|
||||
new.shouldExpand = true;
|
||||
new.remnantSize = es.remnantSize * REMNANT_FACTOR;
|
||||
|
||||
double aStep = (new.maxAngle - new.minAngle) / double(INITIAL_EXPANSE + new.circle);
|
||||
double ang = new.minAngle + double(new.position) * aStep + aStep * 0.5;
|
||||
|
||||
vec3d pos = quaterniond_fromAxisAngle(vec3d_up(), ang) * vec3d_front(new.radius) + new.origin;
|
||||
pos.y = es.system.position.y;
|
||||
|
||||
trackSystems.insertLast(new);
|
||||
|
||||
SystemIniter hook;
|
||||
@hook.mp = this;
|
||||
@hook.es = new;
|
||||
generateNewSystem(pos, 2000.0, hook);
|
||||
}
|
||||
}
|
||||
|
||||
void initSystem(ExpanseSystem@ es) {
|
||||
auto@ desc = es.system;
|
||||
const SystemType@ sysType;
|
||||
do {
|
||||
@sysType = getDistributedSystemType();
|
||||
}
|
||||
while(sysType.unique != SU_NonUnique);
|
||||
|
||||
SystemData data;
|
||||
data.index = es.system.index;
|
||||
data.position = es.system.position;
|
||||
desc.radius = 250;
|
||||
|
||||
sysType.generate(data, es.system);
|
||||
|
||||
desc.object.InnerRadius = desc.radius;
|
||||
desc.object.OuterRadius = desc.radius * 1.5;
|
||||
desc.object.radius = desc.object.OuterRadius;
|
||||
desc.radius = desc.object.OuterRadius;
|
||||
desc.object.getNode().scale = desc.object.radius + 128.0;
|
||||
desc.object.getNode().rebuildTransform();
|
||||
|
||||
postGenerateSystem(data, es.system);
|
||||
finalizeSystem(data, es.system);
|
||||
|
||||
createRemnants(es.system, es.remnantSize * randomd(0.85, 1.15), es, queued=true);
|
||||
setPlanetsLocked(es.system, true);
|
||||
}
|
||||
|
||||
void save(SaveFile& file) {
|
||||
uint cnt = trackSystems.length;
|
||||
file << cnt;
|
||||
for(uint i = 0; i < cnt; ++i)
|
||||
file << trackSystems[i];
|
||||
}
|
||||
|
||||
void load(SaveFile& file) {
|
||||
uint cnt = 0;
|
||||
file >> cnt;
|
||||
trackSystems.length = cnt;
|
||||
for(uint i = 0; i < cnt; ++i) {
|
||||
@trackSystems[i] = ExpanseSystem();
|
||||
file >> trackSystems[i];
|
||||
}
|
||||
|
||||
if(file < SV_0107)
|
||||
loadRemnantDesigns(file);
|
||||
}
|
||||
|
||||
#section all
|
||||
}
|
||||
|
||||
#section server
|
||||
class SystemIniter : SystemGenerateHook {
|
||||
ExpanseMap@ mp;
|
||||
ExpanseSystem@ es;
|
||||
void call(SystemDesc@ desc) {
|
||||
@es.system = desc;
|
||||
mp.initSystem(es);
|
||||
}
|
||||
}
|
||||
|
||||
class ExpanseSystem : Savable {
|
||||
uint index = 0;
|
||||
uint circle = 0;
|
||||
uint position = 0;
|
||||
uint field = 0;
|
||||
SystemDesc@ system;
|
||||
vec3d origin;
|
||||
array<Ship@> defenders;
|
||||
double minAngle = 0, maxAngle = 0;
|
||||
double radius = 0;
|
||||
double remnantSize = 0;
|
||||
double spacing = 6500;
|
||||
bool defended = true;
|
||||
bool shouldExpand = false;
|
||||
bool flatten = false;
|
||||
uint queuedFleets = 0;
|
||||
|
||||
void save(SaveFile& file) {
|
||||
file << index << circle;
|
||||
file << position << field;
|
||||
file << system.index;
|
||||
|
||||
uint cnt = defenders.length;
|
||||
file << cnt;
|
||||
for(uint i = 0; i < cnt; ++i)
|
||||
file << defenders[i];
|
||||
|
||||
file << minAngle << maxAngle;
|
||||
file << radius << remnantSize;
|
||||
file << defended << shouldExpand;
|
||||
file << spacing << flatten;
|
||||
file << origin;
|
||||
file << queuedFleets;
|
||||
}
|
||||
|
||||
void load(SaveFile& file) {
|
||||
file >> index >> circle;
|
||||
file >> position >> field;
|
||||
|
||||
uint index = 0;
|
||||
file >> index;
|
||||
@system = getSystem(index);
|
||||
|
||||
uint cnt = 0;
|
||||
file >> cnt;
|
||||
defenders.length = cnt;
|
||||
for(uint i = 0; i < cnt; ++i)
|
||||
file >> defenders[i];
|
||||
|
||||
file >> minAngle >> maxAngle;
|
||||
file >> radius >> remnantSize;
|
||||
file >> defended >> shouldExpand;
|
||||
file >> spacing >> flatten;
|
||||
file >> origin;
|
||||
if(file >= SV_0088)
|
||||
file >> queuedFleets;
|
||||
}
|
||||
};
|
||||
#section all
|
||||
@@ -0,0 +1,44 @@
|
||||
System: Orathi
|
||||
Position: (0, 0, 0)
|
||||
Homeworld: False
|
||||
|
||||
MakeStar(Temperature = 1000, Radius = 200)
|
||||
ExpandSystem(500)
|
||||
TriggerRegion(SetSystemAssignGroup(-2))
|
||||
|
||||
MakePlanet(AncientCity, Radius = 24, Conditions = False)
|
||||
Rename(Orathi Prime)
|
||||
|
||||
AddPlanetResource(Water)
|
||||
AddPlanetResource(Grain)
|
||||
AddPlanetResource(Grain)
|
||||
AddPlanetResource(Oil)
|
||||
AddPlanetResource(AncientCity)
|
||||
|
||||
SpawnBuilding(Factory, (4, 3))
|
||||
SpawnBuilding(Factory, (9, 6))
|
||||
Trigger(DevelopTiles(1000))
|
||||
|
||||
AddResource(Money, +2.5)
|
||||
|
||||
MakePlanet(RandomLevel:1, Radius = 16, Conditions = False)
|
||||
AddPlanetResource(Water)
|
||||
AddPlanetResource(Meat)
|
||||
|
||||
MakePlanet(RandomLevel:1, Radius = 16, Conditions = False)
|
||||
AddPlanetResource(Water)
|
||||
AddPlanetResource(Fruit)
|
||||
|
||||
ExpandSystem(1000)
|
||||
|
||||
MakeAsteroid()
|
||||
MakeAsteroid()
|
||||
MakeAsteroid()
|
||||
MakeAsteroid()
|
||||
MakeAsteroid()
|
||||
|
||||
MakeArtifact()
|
||||
MakeArtifact()
|
||||
MakeArtifact()
|
||||
MakeArtifact()
|
||||
MakeArtifact()
|
||||
|
After Width: | Height: | Size: 53 KiB |
@@ -0,0 +1,837 @@
|
||||
#priority init 5001
|
||||
#include "include/map.as"
|
||||
|
||||
#section server
|
||||
import systems;
|
||||
from game_start import galaxies;
|
||||
import camps;
|
||||
from empire import Creeps, Pirates;
|
||||
import empire_data;
|
||||
import object_creation;
|
||||
import research;
|
||||
import remnant_designs;
|
||||
import artifacts;
|
||||
from objects.Artifact import createArtifact;
|
||||
from achievements import giveAchievement;
|
||||
|
||||
import void addModifierToEmpire(Empire@ emp, const string& spec) from "bonus_effects";
|
||||
#section all
|
||||
|
||||
enum MapSetting {
|
||||
M_SysCount,
|
||||
M_SystemSpacing,
|
||||
M_Flatten,
|
||||
};
|
||||
|
||||
const double BASE_STRENGTH = 6000.0;
|
||||
const double DOUBLE_WAVES = 3.0;
|
||||
const double HARD_DIFF_MIN = 1.0;
|
||||
|
||||
class InvasionMap : Map {
|
||||
InvasionMap() {
|
||||
super();
|
||||
|
||||
name = locale::INVASION_MAP;
|
||||
description = locale::INVASION_MAP_DESC;
|
||||
|
||||
color = 0xff4eaeff;
|
||||
icon = "maps/Invasion/invasion.png";
|
||||
|
||||
sortIndex = -140;
|
||||
dlc = "Heralds";
|
||||
|
||||
eatsPlayers = true;
|
||||
isUnique = true;
|
||||
}
|
||||
|
||||
#section client
|
||||
void makeSettings() {
|
||||
Toggle(locale::FLATTEN, M_Flatten, false, halfWidth=true);
|
||||
Number(locale::SYSTEM_SPACING, M_SystemSpacing, DEFAULT_SPACING, decimals=0, step=1000, min=MIN_SPACING, halfWidth=true);
|
||||
|
||||
auto@ diff = Dropdown(locale::INV_DIFFICULTY, "INVASION_DIFFICULTY");
|
||||
diff.addOption(locale::EASY, 0.25);
|
||||
diff.addOption(locale::NORMAL, 0.5);
|
||||
diff.addOption(locale::HARD, 1.0);
|
||||
diff.set(0.5);
|
||||
|
||||
Description(locale::INVASION_MAP_TEXT, lines=2);
|
||||
}
|
||||
|
||||
#section server
|
||||
void modSettings(GameSettings& settings) override {
|
||||
config::ENABLE_INFLUENCE_VICTORY = 0.0;
|
||||
settings.setNamed("ENABLE_INFLUENCE_VICTORY", 0.0);
|
||||
|
||||
config::ENABLE_DREAD_PIRATE = 0.0;
|
||||
settings.setNamed("ENABLE_DREAD_PIRATE", 0.0);
|
||||
}
|
||||
|
||||
bool ensureConnectedLinks() override {
|
||||
return true;
|
||||
}
|
||||
|
||||
void placeSystems() {
|
||||
double spacing = modSpacing(getSetting(M_SystemSpacing, DEFAULT_SPACING));
|
||||
bool flatten = getSetting(M_Flatten, 0.0) != 0.0;
|
||||
|
||||
uint players = estPlayerCount;
|
||||
if(players == 0)
|
||||
players = 3;
|
||||
|
||||
double radSize = spacing * 10.0 * double(players+1);
|
||||
double startDist = (radSize / (2.0 * pi)) + (spacing * 6.0);
|
||||
|
||||
double heightDiff = spacing * 0.2;
|
||||
|
||||
double radial = twopi / double(players+1);
|
||||
double startRad = radial / double(players+1);
|
||||
|
||||
int edgeType = -1;
|
||||
auto@ sysType = getSystemType("AncientSystem");
|
||||
if(sysType !is null)
|
||||
edgeType = sysType.id;
|
||||
|
||||
for(uint i = 0; i < players; ++i) {
|
||||
PlayerData dat;
|
||||
playerData.insertLast(dat);
|
||||
|
||||
vec3d homePos = quaterniond_fromAxisAngle(vec3d_up(), startRad) * vec3d_front(startDist);
|
||||
if(!flatten)
|
||||
homePos.y += randomd(-heightDiff, heightDiff);
|
||||
|
||||
//Create the homeworld
|
||||
auto@ hwSys = addSystem(homePos);
|
||||
hwSys.assignGroup = i;
|
||||
addPossibleHomeworld(hwSys);
|
||||
|
||||
EndSystem hwEnd;
|
||||
hwEnd.index = hwSys.index;
|
||||
@dat.homeworld = hwEnd;
|
||||
|
||||
//Create the edge systems
|
||||
double edgeRad = -0.3 * pi;
|
||||
for(uint n = 0; n < 3; ++n) {
|
||||
vec3d edgePos = homePos;
|
||||
edgePos += quaterniond_fromAxisAngle(vec3d_up(), edgeRad) * homePos.normalized(spacing * 0.6);
|
||||
|
||||
auto@ sys = addSystem(edgePos, sysType=edgeType);
|
||||
sys.assignGroup = i;
|
||||
addLink(sys, hwSys);
|
||||
|
||||
EndSystem es;
|
||||
es.index = sys.index;
|
||||
dat.edges.insertLast(es);
|
||||
|
||||
edgeRad += 0.3 * pi;
|
||||
}
|
||||
|
||||
//Create the extra layers
|
||||
for(uint l = 0; l < 5; ++l) {
|
||||
uint layCnt = 2+l;
|
||||
|
||||
double totRad = pi * 0.25 * double(layCnt);
|
||||
double radStep = totRad / double(layCnt);
|
||||
double rad = totRad * -0.5 + radStep * 0.5;
|
||||
|
||||
double sysDist = spacing * double(l+1);
|
||||
|
||||
for(uint n = 0; n < layCnt; ++n) {
|
||||
vec3d sysPos = homePos;
|
||||
sysPos -= quaterniond_fromAxisAngle(vec3d_up(), rad) * homePos.normalized(sysDist);
|
||||
if(!flatten)
|
||||
sysPos.y += randomd(-heightDiff, heightDiff);
|
||||
|
||||
auto@ sys = addSystem(sysPos);
|
||||
sys.assignGroup = i;
|
||||
|
||||
EndSystem es;
|
||||
es.index = sys.index;
|
||||
dat.inner.insertLast(es);
|
||||
|
||||
rad += radStep;
|
||||
}
|
||||
}
|
||||
|
||||
startRad += radial + (radial / double(players+1));
|
||||
}
|
||||
}
|
||||
|
||||
void preInit() {
|
||||
//Modify creep empire
|
||||
Pirates.name = locale::REMNANT_DEFENSE_LINE;
|
||||
Pirates.color = Color(0x4fd07bff);
|
||||
|
||||
//Create invasion empire
|
||||
{
|
||||
@Invaders = Empire();
|
||||
Invaders.name = locale::INVADER;
|
||||
Invaders.color = Color(0xff4040ff);
|
||||
Invaders.major = false;
|
||||
Invaders.visionMask = ~0;
|
||||
|
||||
auto@ flag = getEmpireFlag(randomi(0, getEmpireFlagCount()-1));
|
||||
Invaders.flagDef = flag.flagDef;
|
||||
Invaders.flagID = flag.id;
|
||||
@Invaders.flag = getMaterial(Creeps.flagDef);
|
||||
@Invaders.shipset = getShipset("ALL");
|
||||
}
|
||||
|
||||
//Empires are allied with creeps here
|
||||
for(uint i = 0, cnt = getEmpireCount(); i < cnt; ++i) {
|
||||
Empire@ emp = getEmpire(i);
|
||||
if(!emp.major)
|
||||
continue;
|
||||
|
||||
Pirates.setHostile(emp, false);
|
||||
emp.setHostile(Pirates, false);
|
||||
|
||||
Invaders.setHostile(emp, true);
|
||||
emp.setHostile(Invaders, true);
|
||||
|
||||
emp.modTotalBudget(+500);
|
||||
}
|
||||
|
||||
Pirates.setHostile(Invaders, true);
|
||||
Invaders.setHostile(Pirates, true);
|
||||
}
|
||||
|
||||
void init() {
|
||||
if(isLoadedSave)
|
||||
return;
|
||||
|
||||
//See if we should load strengths
|
||||
loadStrengthList("maps/Invasion/strengths.txt");
|
||||
|
||||
//Add the global modifiers
|
||||
uint majorMask = 0;
|
||||
for(uint i = 0, cnt = getEmpireCount(); i < cnt; ++i) {
|
||||
Empire@ emp = getEmpire(i);
|
||||
if(!emp.major)
|
||||
continue;
|
||||
|
||||
emp.Victory = -2;
|
||||
|
||||
addModifierToEmpire(emp, "StationHull::MaintenanceModFactor(0.0)");
|
||||
addModifierToEmpire(emp, "StationHull::HPModFactor(0.25)");
|
||||
addModifierToEmpire(emp, "StationHull::RangeModFactor(0.5)");
|
||||
addModifierToEmpire(emp, "StationHull::LaborModFactor(2.0)");
|
||||
addModifierToEmpire(emp, "StationHull::MaintCostFactor(0.0)");
|
||||
addModifierToEmpire(emp, "MaintCostFactor(0.0)");
|
||||
majorMask |= emp.mask;
|
||||
|
||||
Object@ hw = emp.Homeworld;
|
||||
if(hw is null)
|
||||
@hw = emp.HomeObj;
|
||||
if(hw !is null)
|
||||
hw.modLaborIncome(+5.0 / 60.0);
|
||||
}
|
||||
|
||||
//YOU WILL SIT IN THE CIRCLE AND SING KUMBAYA DAMNIT
|
||||
for(uint i = 0, cnt = getEmpireCount(); i < cnt; ++i) {
|
||||
Empire@ emp = getEmpire(i);
|
||||
if(emp.major)
|
||||
emp.ForcedPeaceMask |= majorMask;
|
||||
}
|
||||
|
||||
//Spawn remnant defenses
|
||||
const Design@ defStation = Creeps.getDesign("Defense Station");
|
||||
for(uint i = 0, cnt = playerData.length; i < cnt; ++i) {
|
||||
auto@ dat = playerData[i];
|
||||
|
||||
dat.homeworld.index += systems[0].index;
|
||||
@dat.homeworld.desc = getSystem(dat.homeworld.index);
|
||||
|
||||
for(uint n = 0, ncnt = dat.inner.length; n < ncnt; ++n) {
|
||||
auto@ sys = dat.inner[n];
|
||||
sys.index += systems[0].index;
|
||||
@sys.desc = getSystem(sys.index);
|
||||
}
|
||||
|
||||
for(uint n = 0, ncnt = dat.edges.length; n < ncnt; ++n) {
|
||||
auto@ sys = dat.edges[n];
|
||||
sys.index += systems[0].index;
|
||||
@sys.desc = getSystem(sys.index);
|
||||
double radius = sys.desc.radius;
|
||||
|
||||
//Give everyone vision over this system
|
||||
for(uint i = 0, cnt = getEmpireCount(); i < cnt; ++i)
|
||||
sys.desc.object.grantVision(getEmpire(i));
|
||||
|
||||
//Spawn defense stations
|
||||
vec3d offset = (sys.desc.position - dat.homeworld.desc.position).normalized(radius * 0.75);
|
||||
|
||||
auto@ obj = createShip(sys.desc.position + offset, defStation, Pirates, free=true);
|
||||
obj.orbitDuration(1000000000000.0);
|
||||
sys.defenses.insertLast(obj);
|
||||
|
||||
@obj = createShip(sys.desc.position + quaterniond_fromAxisAngle(vec3d_up(), pi * 0.2) * offset, defStation, Pirates, free=true);
|
||||
obj.orbitDuration(1000000000000.0);
|
||||
sys.defenses.insertLast(obj);
|
||||
|
||||
@obj = createShip(sys.desc.position + quaterniond_fromAxisAngle(vec3d_up(), -pi * 0.2) * offset, defStation, Pirates, free=true);
|
||||
obj.orbitDuration(1000000000000.0);
|
||||
sys.defenses.insertLast(obj);
|
||||
}
|
||||
}
|
||||
}
|
||||
#section all
|
||||
};
|
||||
|
||||
#section server
|
||||
class PlayerData : Savable {
|
||||
Empire@ empire;
|
||||
EndSystem@ homeworld;
|
||||
array<EndSystem@> inner;
|
||||
array<EndSystem@> edges;
|
||||
uint previousWave = uint(-1);
|
||||
|
||||
double stationHealth = 0.0;
|
||||
double stationEff = 0.0;
|
||||
|
||||
void save(SaveFile& file) {
|
||||
file << homeworld;
|
||||
file << empire;
|
||||
file << stationHealth;
|
||||
file << stationEff;
|
||||
file << previousWave;
|
||||
|
||||
uint cnt = inner.length;
|
||||
file << cnt;
|
||||
for(uint i = 0; i < cnt; ++i)
|
||||
file << inner[i];
|
||||
|
||||
cnt = edges.length;
|
||||
file << cnt;
|
||||
for(uint i = 0; i < cnt; ++i)
|
||||
file << edges[i];
|
||||
}
|
||||
|
||||
void load(SaveFile& file) {
|
||||
@homeworld = EndSystem();
|
||||
file >> homeworld;
|
||||
file >> empire;
|
||||
file >> stationHealth;
|
||||
file >> stationEff;
|
||||
if(file >= SV_0156)
|
||||
file >> previousWave;
|
||||
|
||||
uint cnt = 0;
|
||||
|
||||
file >> cnt;
|
||||
inner.length = cnt;
|
||||
for(uint i = 0; i < cnt; ++i) {
|
||||
@inner[i] = EndSystem();
|
||||
file >> inner[i];
|
||||
}
|
||||
|
||||
file >> cnt;
|
||||
edges.length = cnt;
|
||||
for(uint i = 0; i < cnt; ++i) {
|
||||
@edges[i] = EndSystem();
|
||||
file >> edges[i];
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class EndSystem : Savable {
|
||||
uint index;
|
||||
SystemDesc@ desc;
|
||||
array<Ship@> defenses;
|
||||
array<Ship@> invaders;
|
||||
bool isFighting = false;
|
||||
|
||||
void save(SaveFile& file) {
|
||||
file << index;
|
||||
file << isFighting;
|
||||
|
||||
uint cnt = defenses.length;
|
||||
file << cnt;
|
||||
for(uint i = 0; i < cnt; ++i)
|
||||
file << defenses[i];
|
||||
|
||||
cnt = invaders.length;
|
||||
file << cnt;
|
||||
for(uint i = 0; i < cnt; ++i)
|
||||
file << invaders[i];
|
||||
}
|
||||
|
||||
void load(SaveFile& file) {
|
||||
file >> index;
|
||||
file >> isFighting;
|
||||
|
||||
uint cnt = 0;
|
||||
|
||||
file >> cnt;
|
||||
defenses.length = cnt;
|
||||
for(uint i = 0; i < cnt; ++i)
|
||||
file >> defenses[i];
|
||||
|
||||
file >> cnt;
|
||||
invaders.length = cnt;
|
||||
for(uint i = 0; i < cnt; ++i)
|
||||
file >> invaders[i];
|
||||
}
|
||||
};
|
||||
|
||||
Mutex mtx;
|
||||
array<PlayerData@> playerData;
|
||||
Empire@ Invaders;
|
||||
double symbolicStrength = 0.0;
|
||||
double waveTimer = 0.0;
|
||||
array<double> WAVE_STRENGTHS;
|
||||
|
||||
void save(SaveFile& file) {
|
||||
if(hasInvasionMap()) {
|
||||
file.write1();
|
||||
|
||||
file << Invaders;
|
||||
file << symbolicStrength;
|
||||
file << waveTimer;
|
||||
|
||||
uint cnt = playerData.length;
|
||||
file << cnt;
|
||||
for(uint i = 0; i < cnt; ++i)
|
||||
file << playerData[i];
|
||||
}
|
||||
else {
|
||||
file.write0();
|
||||
}
|
||||
}
|
||||
|
||||
void loadStrengthList(const string& fname) {
|
||||
WAVE_STRENGTHS.length = 0;
|
||||
|
||||
ReadFile file(fname, true);
|
||||
while(file++)
|
||||
WAVE_STRENGTHS.insertLast(toDouble(file.line));
|
||||
}
|
||||
|
||||
void load(SaveFile& file) {
|
||||
if(file.readBit()) {
|
||||
file >> Invaders;
|
||||
file >> symbolicStrength;
|
||||
file >> waveTimer;
|
||||
|
||||
uint cnt = 0;
|
||||
file >> cnt;
|
||||
playerData.length = cnt;
|
||||
for(uint i = 0; i < cnt; ++i) {
|
||||
@playerData[i] = PlayerData();
|
||||
file >> playerData[i];
|
||||
}
|
||||
}
|
||||
|
||||
loadStrengthList("maps/Invasion/strengths.txt");
|
||||
}
|
||||
|
||||
double getInvasionInterval() {
|
||||
return 3.0 * 60.0;
|
||||
}
|
||||
|
||||
double getInvasionTimer() {
|
||||
return waveTimer;
|
||||
}
|
||||
|
||||
double getInvasionStrength() {
|
||||
return symbolicStrength;
|
||||
}
|
||||
|
||||
void increaseInvasionStrength(double amount, bool modify = true) {
|
||||
if(modify)
|
||||
amount /= double(playerData.length);
|
||||
symbolicStrength += amount;
|
||||
}
|
||||
|
||||
bool hasInvasionMap() {
|
||||
for(uint i = 0, cnt = galaxies.length; i < cnt; ++i) {
|
||||
if(cast<InvasionMap>(galaxies[i]) !is null)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool initialized = false;
|
||||
void tick(double time) {
|
||||
if(!hasInvasionMap())
|
||||
return;
|
||||
|
||||
if(!initialized && playerEmpire.valid) {
|
||||
guiDialogueAction(CURRENT_PLAYER, "Invasion.InvasionMap::SetupGUI");
|
||||
if(!isLoadedSave) {
|
||||
for(uint i = 0, cnt = getEmpireCount(); i < cnt; ++i) {
|
||||
Empire@ emp = getEmpire(i);
|
||||
if(!emp.major)
|
||||
continue;
|
||||
|
||||
emp.replaceResearchOfType(getTechnologyID("OrbitalConstruction"), getTechnologyID("SupportCap"));
|
||||
emp.replaceResearchOfType(getTechnologyID("OrbitalConstruction2"), getTechnologyID("Damage"));
|
||||
}
|
||||
}
|
||||
initialized = true;
|
||||
}
|
||||
|
||||
if(time == 0.0 || gameSpeed == 0.0 || gameTime < 0.5)
|
||||
return;
|
||||
|
||||
//Check for losing systems
|
||||
for(uint i = 0, cnt = playerData.length; i < cnt; ++i) {
|
||||
auto@ dat = playerData[i];
|
||||
if(dat.homeworld.desc is null)
|
||||
@dat.homeworld.desc = getSystem(dat.homeworld.index);
|
||||
|
||||
if(dat.empire is null) {
|
||||
for(uint i = 0, cnt = getEmpireCount(); i < cnt; ++i) {
|
||||
Empire@ emp = getEmpire(i);
|
||||
Region@ homeReg;
|
||||
if(emp.Homeworld !is null)
|
||||
@homeReg = emp.Homeworld.region;
|
||||
else if(emp.HomeObj !is null)
|
||||
@homeReg = emp.HomeObj.region;
|
||||
|
||||
if(homeReg !is null) {
|
||||
if(dat.homeworld.desc.object is homeReg)
|
||||
@dat.empire = emp;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for(uint j = 0, jcnt = dat.edges.length; j < jcnt; ++j) {
|
||||
auto@ sys = dat.edges[j];
|
||||
if(sys.desc is null)
|
||||
@sys.desc = getSystem(sys.index);
|
||||
|
||||
//Remove lost defenses
|
||||
for(uint n = 0, ncnt = sys.defenses.length; n < ncnt; ++n) {
|
||||
if(sys.defenses[n] is null || !sys.defenses[n].valid) {
|
||||
sys.defenses.removeAt(n);
|
||||
--n; --ncnt;
|
||||
}
|
||||
}
|
||||
|
||||
//Replace invaders with pickups
|
||||
vec3d lastInvader;
|
||||
for(uint n = 0, ncnt = sys.invaders.length; n < ncnt; ++n) {
|
||||
auto@ obj = sys.invaders[n];
|
||||
if(obj !is null && obj.valid)
|
||||
continue;
|
||||
|
||||
if(obj !is null)
|
||||
lastInvader = obj.position;
|
||||
sys.invaders.removeAt(n);
|
||||
--n; --ncnt;
|
||||
}
|
||||
|
||||
if(sys.isFighting && sys.invaders.length == 0) {
|
||||
sys.isFighting = false;
|
||||
|
||||
const ArtifactType@ type;
|
||||
uint possibs = 0;
|
||||
for(uint n = 0, ncnt = getArtifactTypeCount(); n < ncnt; ++n) {
|
||||
auto@ artif = getArtifactType(n);
|
||||
if(!artif.hasTag("Invasion"))
|
||||
continue;
|
||||
possibs += 1;
|
||||
if(randomd() < 1.0 / double(possibs))
|
||||
@type = artif;
|
||||
}
|
||||
|
||||
if(type !is null) {
|
||||
vec3d pos = lastInvader;
|
||||
if(pos.zero) {
|
||||
pos = sys.desc.object.position;
|
||||
vec2d off = random2d(200.0, sys.desc.object.radius * 0.8);
|
||||
pos.x += off.x;
|
||||
pos.y += randomd(-20.0, 20.0);
|
||||
pos.z += off.y;
|
||||
}
|
||||
|
||||
if(symbolicStrength >= 20.0)
|
||||
giveAchievement(dat.empire, "ACH_INVASION_20");
|
||||
if(symbolicStrength >= 25.0 && config::INVASION_DIFFICULTY >= HARD_DIFF_MIN)
|
||||
giveAchievement(dat.empire, "ACH_INVASION_40");
|
||||
|
||||
Artifact@ artif = createArtifact(pos, type);
|
||||
@artif.owner = dat.empire;
|
||||
}
|
||||
}
|
||||
|
||||
//Remove lost edges
|
||||
if(sys.defenses.length == 0) {
|
||||
sys.desc.object.dealStarDamage(1000000000000000000);
|
||||
sys.desc.object.addSystemDPS(1000000000000000000);
|
||||
|
||||
increaseInvasionStrength(2.0);
|
||||
|
||||
for(uint i = 0, cnt = sys.desc.object.planetCount; i < cnt; ++i)
|
||||
sys.desc.object.planets[i].destroyQuiet();
|
||||
|
||||
dat.edges.removeAt(j);
|
||||
--j; --jcnt;
|
||||
}
|
||||
}
|
||||
|
||||
//Remove lost systems
|
||||
if(dat.edges.length == 0) {
|
||||
for(uint j = 0, jcnt = dat.inner.length; j < jcnt; ++j) {
|
||||
auto@ sys = dat.inner[j];
|
||||
if(sys.desc is null)
|
||||
@sys.desc = getSystem(sys.index);
|
||||
|
||||
sys.desc.object.dealStarDamage(1000000000000000000);
|
||||
sys.desc.object.addSystemDPS(1000000000000000000);
|
||||
|
||||
for(uint i = 0, cnt = sys.desc.object.planetCount; i < cnt; ++i)
|
||||
sys.desc.object.planets[i].destroyQuiet();
|
||||
}
|
||||
auto@ sys = dat.homeworld;
|
||||
if(sys !is null) {
|
||||
if(sys.desc is null)
|
||||
@sys.desc = getSystem(sys.index);
|
||||
|
||||
sys.desc.object.dealStarDamage(1000000000000000000);
|
||||
sys.desc.object.addSystemDPS(1000000000000000000);
|
||||
|
||||
for(uint i = 0, cnt = sys.desc.object.planetCount; i < cnt; ++i)
|
||||
sys.desc.object.planets[i].destroyQuiet();
|
||||
}
|
||||
dat.inner.length = 0;
|
||||
}
|
||||
|
||||
//Remove lost players
|
||||
if(dat.inner.length == 0) {
|
||||
playerData.removeAt(i);
|
||||
--i; --cnt;
|
||||
}
|
||||
}
|
||||
|
||||
//Increase remnant strength
|
||||
waveTimer -= time;
|
||||
if(waveTimer <= 0.0) {
|
||||
waveTimer += 3.0 * 60.0;
|
||||
|
||||
//Update defense station strength
|
||||
double stHP = -1.0 + 0.5 * pow(1.3, symbolicStrength / DOUBLE_WAVES);
|
||||
double stDPS = min(-1.0+ 0.2 * pow(1.4, symbolicStrength / DOUBLE_WAVES), 0.0);
|
||||
|
||||
for(uint i = 0, cnt = playerData.length; i < cnt; ++i) {
|
||||
auto@ dat = playerData[i];
|
||||
for(uint n = 0, ncnt = dat.edges.length; n < ncnt; ++n) {
|
||||
auto@ edge = dat.edges[n];
|
||||
for(uint j = 0, jcnt = edge.defenses.length; j < jcnt; ++j) {
|
||||
edge.defenses[j].modHPFactor(stHP - dat.stationHealth);
|
||||
edge.defenses[j].modFleetEffectiveness(stDPS - dat.stationEff);
|
||||
}
|
||||
}
|
||||
|
||||
dat.stationHealth = stHP;
|
||||
dat.stationEff = stDPS;
|
||||
}
|
||||
|
||||
//Spawn enemies
|
||||
double targetStrength;
|
||||
|
||||
int waveDown = int(floor(symbolicStrength));
|
||||
int waveUp = int(ceil(symbolicStrength));
|
||||
|
||||
if(uint(waveUp) >= WAVE_STRENGTHS.length) {
|
||||
double finalWave = WAVE_STRENGTHS.length;
|
||||
double baseStrength = BASE_STRENGTH;
|
||||
if(WAVE_STRENGTHS.length != 0)
|
||||
baseStrength = WAVE_STRENGTHS[WAVE_STRENGTHS.length-1];
|
||||
|
||||
targetStrength = baseStrength * pow(2.0, double(symbolicStrength - finalWave) / DOUBLE_WAVES);
|
||||
}
|
||||
else {
|
||||
double downStr = WAVE_STRENGTHS[waveDown];
|
||||
double upStr = WAVE_STRENGTHS[waveUp];
|
||||
|
||||
targetStrength = downStr;
|
||||
if(upStr != downStr && waveDown != waveUp)
|
||||
targetStrength += (upStr - downStr) * (symbolicStrength - double(waveDown)) / double(waveUp - waveDown);
|
||||
}
|
||||
|
||||
if(config::INVASION_DIFFICULTY > 0)
|
||||
targetStrength *= config::INVASION_DIFFICULTY;
|
||||
|
||||
symbolicStrength += 1.0;
|
||||
|
||||
uint fleetCount = 1;
|
||||
uint maxFleets = ceil(symbolicStrength / 5.0);
|
||||
while(fleetCount < maxFleets && randomd() < 0.33)
|
||||
fleetCount += 1;
|
||||
|
||||
double fleetStrength = sqr(sqrt(targetStrength) / double(fleetCount));
|
||||
array<RemnantComposition@> fleets;
|
||||
|
||||
for(uint f = 0, fcnt = fleetCount; f < fcnt; ++f) {
|
||||
auto@ comp = composeRemnantFleet(fleetStrength, 0.2, emp=Invaders);
|
||||
if(comp is null)
|
||||
continue;
|
||||
fleets.insertLast(comp);
|
||||
}
|
||||
|
||||
for(uint i = 0, cnt = playerData.length; i < cnt; ++i) {
|
||||
auto@ dat = playerData[i];
|
||||
if(dat.edges.length == 0)
|
||||
continue;
|
||||
|
||||
EndSystem@ sys;
|
||||
uint checkCount = 0;
|
||||
for(uint i = 0, cnt = dat.edges.length; i < cnt; ++i) {
|
||||
auto@ edge = dat.edges[i];
|
||||
if(cnt != 1 && edge.index == dat.previousWave)
|
||||
continue;
|
||||
|
||||
checkCount += 1;
|
||||
if(randomd() < 1.0 / double(checkCount))
|
||||
@sys = edge;
|
||||
}
|
||||
|
||||
sys.isFighting = true;
|
||||
for(uint f = 0, fcnt = fleets.length; f < fcnt; ++f) {
|
||||
vec3d pos = (sys.desc.position - dat.homeworld.desc.position).normalized();
|
||||
pos = quaterniond_fromAxisAngle(vec3d_up(), randomd(-0.2, 0.2)*pi) * pos;
|
||||
|
||||
vec3d spawnPos = pos * sys.desc.object.radius * 0.95;
|
||||
spawnPos += sys.desc.position;
|
||||
|
||||
Ship@ invader = spawnRemnantFleet(spawnPos, fleets[f], emp=Invaders, alwaysVisible=true);
|
||||
sys.invaders.insertLast(invader);
|
||||
|
||||
if(f == 0 && dat.empire !is null && dat.empire.player !is null)
|
||||
showPing(dat.empire.player, Invaders, spawnPos, 2);
|
||||
}
|
||||
|
||||
dat.previousWave = sys.index;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool sendPeriodic(Message& msg) {
|
||||
if(!hasInvasionMap())
|
||||
return false;
|
||||
msg << symbolicStrength;
|
||||
msg << waveTimer;
|
||||
return true;
|
||||
}
|
||||
|
||||
#section shadow
|
||||
from game_start import galaxies;
|
||||
|
||||
double symbolicStrength = 0.0;
|
||||
double waveTimer = 0.0;
|
||||
|
||||
void recvPeriodic(Message& msg) {
|
||||
msg >> symbolicStrength;
|
||||
msg >> waveTimer;
|
||||
}
|
||||
|
||||
bool hasInvasionMap() {
|
||||
for(uint i = 0, cnt = galaxies.length; i < cnt; ++i) {
|
||||
if(cast<InvasionMap>(galaxies[i]) !is null)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
double getInvasionInterval() {
|
||||
return 3.0 * 60.0;
|
||||
}
|
||||
|
||||
double getInvasionTimer() {
|
||||
return waveTimer;
|
||||
}
|
||||
|
||||
double getInvasionStrength() {
|
||||
return symbolicStrength;
|
||||
}
|
||||
|
||||
bool initialized = false;
|
||||
void tick(double time) {
|
||||
if(!hasInvasionMap())
|
||||
return;
|
||||
|
||||
if(!initialized && playerEmpire.valid) {
|
||||
guiDialogueAction("Invasion.InvasionMap::SetupGUI");
|
||||
initialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
#section gui
|
||||
import dialogue;
|
||||
import elements.BaseGuiElement;
|
||||
import elements.GuiText;
|
||||
import elements.GuiMarkupText;
|
||||
import elements.GuiProgressbar;
|
||||
import util.formatting;
|
||||
import tabs.tabbar;
|
||||
|
||||
class InvasionUI : BaseGuiElement {
|
||||
GuiText@ waveLabel;
|
||||
GuiProgressbar@ bar;
|
||||
GuiText@ strengthLabel;
|
||||
GuiText@ strength;
|
||||
|
||||
InvasionUI() {
|
||||
super(null, Alignment(Left+0.5f-200, Top+TAB_HEIGHT+GLOBAL_BAR_HEIGHT+1, Width=400, Height=40));
|
||||
|
||||
@waveLabel = GuiText(this, Alignment(Left+8, Top+4, Left+90, Bottom-4), locale::NEXT_WAVE);
|
||||
waveLabel.color = Color(0xaaaaaaff);
|
||||
|
||||
@bar = GuiProgressbar(this, Alignment(Left+90, Top+8, Left+250, Bottom-8));
|
||||
bar.frontColor = Color(0xff8080ff);
|
||||
bar.font = FT_Small;
|
||||
bar.strokeColor = colors::Black;
|
||||
|
||||
@strengthLabel = GuiText(this, Alignment(Left+270, Top+4, Left+330, Bottom-4), locale::NEXT_STRENGTH);
|
||||
strengthLabel.color = Color(0xaaaaaaff);
|
||||
|
||||
@strength = GuiText(this, Alignment(Left+330, Top+6, Left+392, Bottom-4));
|
||||
strength.horizAlign = 0.5;
|
||||
strength.color = Color(0xff4040ff);
|
||||
strength.font = FT_Bold;
|
||||
|
||||
updateAbsolutePosition();
|
||||
}
|
||||
|
||||
void tick(double time) {
|
||||
visible = ActiveTab.category == TC_Galaxy;
|
||||
|
||||
double timer = getInvasionTimer();
|
||||
double interval = getInvasionInterval();
|
||||
double str = getInvasionStrength();
|
||||
|
||||
strength.text = standardize(str, true);
|
||||
bar.text = formatTime(timer);
|
||||
bar.progress = 1.f - (timer / interval);
|
||||
}
|
||||
|
||||
void draw() {
|
||||
skin.draw(SS_PlainOverlay, SF_Normal, AbsolutePosition.padded(0,-2,0,0));
|
||||
BaseGuiElement::draw();
|
||||
}
|
||||
};
|
||||
|
||||
class SetupGUI : DialogueAction {
|
||||
void call() {
|
||||
@ui = InvasionUI();
|
||||
}
|
||||
}
|
||||
|
||||
InvasionUI@ ui;
|
||||
void tick(double time) {
|
||||
if(ui !is null)
|
||||
ui.tick(time);
|
||||
}
|
||||
|
||||
void preReload(Message& msg) {
|
||||
msg.writeBit(ui !is null);
|
||||
if(ui !is null)
|
||||
ui.remove();
|
||||
}
|
||||
|
||||
void postReload(Message& msg) {
|
||||
if(msg.readBit())
|
||||
@ui = InvasionUI();
|
||||
}
|
||||
|
After Width: | Height: | Size: 418 KiB |
@@ -0,0 +1,25 @@
|
||||
1000
|
||||
2000
|
||||
4000
|
||||
6000
|
||||
12000
|
||||
16000
|
||||
30000
|
||||
35000
|
||||
45000
|
||||
55000
|
||||
70000
|
||||
120000
|
||||
120000
|
||||
120000
|
||||
350000
|
||||
350000
|
||||
375000
|
||||
750000
|
||||
750000
|
||||
1000000
|
||||
1000000
|
||||
1200000
|
||||
1500000
|
||||
2000000
|
||||
2500000
|
||||
@@ -0,0 +1,122 @@
|
||||
#priority init 1501
|
||||
import maps;
|
||||
|
||||
#section game
|
||||
import dialogue;
|
||||
#section all
|
||||
|
||||
ModEditor _map;
|
||||
class ModEditor : Map {
|
||||
ModEditor() {
|
||||
super();
|
||||
|
||||
isListed = false;
|
||||
isScenario = true;
|
||||
}
|
||||
|
||||
#section server
|
||||
void prepareSystem(SystemData@ data, SystemDesc@ desc) {
|
||||
@data.homeworlds = null;
|
||||
Map::prepareSystem(data, desc);
|
||||
}
|
||||
|
||||
bool canHaveHomeworld(SystemData@ data, Empire@ emp) {
|
||||
return false;
|
||||
}
|
||||
|
||||
void preGenerate() {
|
||||
Map::preGenerate();
|
||||
radius = 40000;
|
||||
}
|
||||
|
||||
void placeSystems() {
|
||||
addSystem(vec3d(0, 0, 0), code=SystemCode()
|
||||
<< "NameSystem(Sandbox)"
|
||||
<< "MakeStar(Temperature = 5778, Radius = 75)"
|
||||
<< "ExpandSystem(2000)"
|
||||
);
|
||||
}
|
||||
|
||||
void modSettings(GameSettings& settings) {
|
||||
settings.empires.length = 2;
|
||||
settings.empires[0].name = "Player";
|
||||
settings.empires[1].name = "Enemy";
|
||||
settings.empires[1].shipset = "Gevron";
|
||||
settings.empires[1].type = ET_NoAI;
|
||||
config::ENABLE_UNIQUE_SPREADS = 0.0;
|
||||
config::DISABLE_STARTING_FLEETS = 1.0;
|
||||
config::ENABLE_DREAD_PIRATE = 0.0;
|
||||
config::ENABLE_INFLUENCE_EVENTS = 0.0;
|
||||
}
|
||||
|
||||
void init() {
|
||||
Empire@ player = getEmpire(0);
|
||||
Empire@ enemy = getEmpire(1);
|
||||
|
||||
player.Victory = -2;
|
||||
enemy.Victory = -2;
|
||||
}
|
||||
|
||||
bool initialized = false;
|
||||
void tick(double time) {
|
||||
if(!initialized) {
|
||||
guiDialogueAction(CURRENT_PLAYER, "ModEditor.ModEditor::SetupGUI");
|
||||
initialized = true;
|
||||
}
|
||||
}
|
||||
#section all
|
||||
};
|
||||
|
||||
#section gui
|
||||
import tabs.tabbar;
|
||||
import tabs.HomeTab;
|
||||
import editor.editor;
|
||||
import editor.modinfo;
|
||||
import editor.overview;
|
||||
from tabs.GlobalBar import GlobalBar;
|
||||
from tabs.WikiTab import createWikiTab;
|
||||
|
||||
class SetupGUI : DialogueAction {
|
||||
void call() {
|
||||
isEditor = true;
|
||||
|
||||
//Clean global bar
|
||||
auto@ gbar = cast<GlobalBar>(globalBar);
|
||||
gbar.container.visible = false;
|
||||
|
||||
//Remove existing tabs
|
||||
tabBar.goButton.visible = false;
|
||||
tabBar.homeButton.visible = false;
|
||||
for(uint i = 1, cnt = tabs.length; i < cnt; ++i)
|
||||
closeTab(tabs[1]);
|
||||
|
||||
auto@ infoTab = createModInfoTab();
|
||||
infoTab.locked = true;
|
||||
newTab(infoTab);
|
||||
switchToTab(infoTab);
|
||||
|
||||
auto@ overviewTab = createOverviewTab();
|
||||
overviewTab.locked = true;
|
||||
newTab(overviewTab);
|
||||
|
||||
auto@ wikiTab = createWikiTab("Modding");
|
||||
wikiTab.locked = true;
|
||||
newTab(wikiTab);
|
||||
|
||||
closeTab(tabs[0]);
|
||||
}
|
||||
};
|
||||
|
||||
bool isEditor = false;
|
||||
void tick(double time) {
|
||||
if(!isEditor)
|
||||
return;
|
||||
|
||||
//Make any home tabs into overview tabs
|
||||
for(uint i = 0, cnt = tabs.length; i < cnt; ++i) {
|
||||
auto@ tab = tabs[i];
|
||||
if(cast<HomeTab>(tab) !is null)
|
||||
browseTab(tab, createOverviewTab());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
#include "include/map.as"
|
||||
|
||||
#section server
|
||||
import util.map_util;
|
||||
#section all
|
||||
|
||||
enum MapSetting {
|
||||
M_SystemCount,
|
||||
M_SystemSpacing,
|
||||
M_Flatten,
|
||||
};
|
||||
|
||||
class RingsMap : Map {
|
||||
RingsMap() {
|
||||
super();
|
||||
|
||||
name = locale::RINGS_MAP;
|
||||
description = locale::RINGS_MAP_DESC;
|
||||
|
||||
dlc = "Heralds";
|
||||
sortIndex = -110;
|
||||
|
||||
color = 0xffd73cff;
|
||||
icon = "maps/Rings/rings.png";
|
||||
}
|
||||
|
||||
#section client
|
||||
void makeSettings() {
|
||||
Number(locale::SYSTEM_COUNT, M_SystemCount, DEFAULT_SYSTEM_COUNT, decimals=0, step=10, min=6, 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, halfWidth=true);
|
||||
}
|
||||
|
||||
#section server
|
||||
void placeSystems() {
|
||||
RegionMap regionGroup;
|
||||
|
||||
uint systemCount = uint(getSetting(M_SystemCount, DEFAULT_SYSTEM_COUNT));
|
||||
double spacing = modSpacing(getSetting(M_SystemSpacing, DEFAULT_SPACING));
|
||||
bool flatten = getSetting(M_Flatten, 0.0) != 0.0;
|
||||
|
||||
uint ringCount = max(floor(pow(double(systemCount), 0.33)), 1.0);
|
||||
uint sysPerRing = ceil(double(systemCount) / double(ringCount));
|
||||
double baseRadius = (sysPerRing * spacing * 1.2) / twopi;
|
||||
|
||||
vec3d ringPos;
|
||||
double ringRadius;
|
||||
vec3d ringAxis = vec3d_up();
|
||||
|
||||
for(uint i = 0; i < ringCount; ++i) {
|
||||
if(i == 0) {
|
||||
ringPos = vec3d(0.0, 0.0, 0.0);
|
||||
ringRadius = randomd(0.9, 1.5) * baseRadius;
|
||||
}
|
||||
else {
|
||||
//Start somewhere random on the previous ring
|
||||
ringPos += quaterniond_fromAxisAngle(ringAxis, randomd(0, twopi)) * vec3d(ringRadius, 0.0, 0.0);
|
||||
ringRadius = randomd(0.9, 1.5) * baseRadius;
|
||||
|
||||
if(!flatten)
|
||||
ringAxis = quaterniond_fromAxisAngle(vec3d_right(), randomd(-0.2*pi, 0.2*pi)) * quaterniond_fromAxisAngle(vec3d_front(), randomd(-0.2*pi, 0.2*pi)) * vec3d_up();
|
||||
}
|
||||
|
||||
double angleStep = twopi / double(sysPerRing);
|
||||
double angle = randomd(0, angleStep);
|
||||
for(uint n = 0; n < sysPerRing; ++n) {
|
||||
vec3d sysPos = ringPos;
|
||||
sysPos += quaterniond_fromAxisAngle(ringAxis, angle) * vec3d(ringRadius, 0.0, 0.0);
|
||||
|
||||
vec3d flatPos = sysPos;
|
||||
flatPos.y = 0;
|
||||
|
||||
if(regionGroup.findRegion(flatPos) !is null) {
|
||||
angle += angleStep;
|
||||
continue;
|
||||
}
|
||||
|
||||
auto@ sys = addSystem(sysPos);
|
||||
regionGroup.addSystem(flatPos, spacing * 0.8);
|
||||
angle += angleStep;
|
||||
}
|
||||
}
|
||||
|
||||
move(vec3d() - getAveragePosition());
|
||||
}
|
||||
#section all
|
||||
};
|
||||
|
After Width: | Height: | Size: 399 KiB |
@@ -0,0 +1,624 @@
|
||||
#priority init 1501
|
||||
import maps;
|
||||
|
||||
#section game
|
||||
import dialogue;
|
||||
#section all
|
||||
|
||||
#section server
|
||||
import object_creation;
|
||||
import systems;
|
||||
import ship_groups;
|
||||
import warandpeace;
|
||||
import influence_global;
|
||||
from cheats import setCheatsEnabled;
|
||||
#section shadow
|
||||
import systems;
|
||||
#section all
|
||||
|
||||
const string SHIPSET = "ALL";
|
||||
const string FOLDER = modProfile["designs"];
|
||||
//const string FOLDER = "data/designs/pirates";
|
||||
const bool OBSOLETE_DEFAULT = false;
|
||||
const bool ENABLE_DISABLED = false;
|
||||
|
||||
Sandbox _map;
|
||||
class Sandbox : Map {
|
||||
Sandbox() {
|
||||
super();
|
||||
|
||||
isListed = false;
|
||||
isScenario = true;
|
||||
}
|
||||
|
||||
#section server
|
||||
void prepareSystem(SystemData@ data, SystemDesc@ desc) {
|
||||
@data.homeworlds = null;
|
||||
Map::prepareSystem(data, desc);
|
||||
}
|
||||
|
||||
bool canHaveHomeworld(SystemData@ data, Empire@ emp) {
|
||||
return false;
|
||||
}
|
||||
|
||||
void preGenerate() {
|
||||
Map::preGenerate();
|
||||
radius = 40000;
|
||||
}
|
||||
|
||||
void placeSystems() {
|
||||
//Enemy gate system (Odatak)
|
||||
addSystem(vec3d(0, 0, 0), code=SystemCode()
|
||||
<< "NameSystem(Sandbox)"
|
||||
<< "MakeStar(Temperature = 5778, Radius = 75)"
|
||||
<< "ExpandSystem(2000)"
|
||||
);
|
||||
}
|
||||
|
||||
void modSettings(GameSettings& settings) {
|
||||
settings.empires.length = 2;
|
||||
settings.empires[0].name = "Player";
|
||||
settings.empires[0].shipset = SHIPSET;
|
||||
settings.empires[1].name = "Enemy";
|
||||
settings.empires[1].shipset = "Gevron";
|
||||
settings.empires[1].type = ET_NoAI;
|
||||
config::ENABLE_UNIQUE_SPREADS = 0.0;
|
||||
config::DISABLE_STARTING_FLEETS = 1.0;
|
||||
config::ENABLE_DREAD_PIRATE = 0.0;
|
||||
config::ENABLE_INFLUENCE_EVENTS = 0.0;
|
||||
config::START_EXPLORED_MAP = 1.0;
|
||||
}
|
||||
|
||||
void init() {
|
||||
Region@ system = getSystem(0).object;
|
||||
Empire@ player = getEmpire(0);
|
||||
Empire@ enemy = getEmpire(1);
|
||||
|
||||
@player.shipset = getShipset(SHIPSET);
|
||||
|
||||
player.Victory = -2;
|
||||
enemy.Victory = -2;
|
||||
|
||||
player.modFTLCapacity(+1000000);
|
||||
enemy.modFTLCapacity(+1000000);
|
||||
|
||||
player.modEnergyStored(+100000);
|
||||
enemy.modEnergyStored(+100000);
|
||||
|
||||
player.modEnergyIncome(+500);
|
||||
enemy.modEnergyIncome(+500);
|
||||
|
||||
player.setHostile(enemy, true);
|
||||
enemy.setHostile(player, true);
|
||||
|
||||
player.ContactMask = int(~0);
|
||||
enemy.ContactMask = int(~0);
|
||||
|
||||
player.visionMask |= enemy.mask;
|
||||
enemy.visionMask |= player.mask;
|
||||
|
||||
system.grantVision(player);
|
||||
system.grantVision(enemy);
|
||||
|
||||
for(uint i = 0, cnt = getSubsystemDefCount(); i < cnt; ++i) {
|
||||
auto@ def = getSubsystemDef(i);
|
||||
if(ENABLE_DISABLED || !def.hasTag("Disabled")) {
|
||||
if(def.hasTag("HeraldsDLC") && !hasDLC("Heralds"))
|
||||
continue;
|
||||
|
||||
player.setUnlocked(def, true);
|
||||
enemy.setUnlocked(def, true);
|
||||
|
||||
for(uint n = 0, ncnt = def.moduleCount; n < ncnt; ++n) {
|
||||
auto@ mod = def.modules[n];
|
||||
if(mod.hasTag("Disabled"))
|
||||
continue;
|
||||
if(mod.hasTag("HeraldsDLC") && !hasDLC("Heralds"))
|
||||
continue;
|
||||
player.setUnlocked(def, mod, true);
|
||||
enemy.setUnlocked(def, mod, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
setCheatsEnabled(HOST_PLAYER, true);
|
||||
}
|
||||
|
||||
int empTickIndex = 0;
|
||||
|
||||
bool initialized = false;
|
||||
array<GroupData> groups;
|
||||
void tick(double time) {
|
||||
Region@ system = getSystem(0).object;
|
||||
Empire@ player = getEmpire(0);
|
||||
Empire@ enemy = getEmpire(1);
|
||||
|
||||
if(!initialized) {
|
||||
guiDialogueAction(CURRENT_PLAYER, "Sandbox.Sandbox::SetupGUI");
|
||||
initialized = true;
|
||||
}
|
||||
|
||||
int money = INT_MAX>>1;
|
||||
{
|
||||
int i = (empTickIndex++) % 2;
|
||||
Empire@ emp = getEmpire(i);
|
||||
|
||||
//Make sure we have lodsamone
|
||||
if(emp.RemainingBudget < money/2)
|
||||
emp.addBonusBudget(money);
|
||||
if(emp.FTLStored < 500000)
|
||||
emp.modFTLStored(+500000);
|
||||
|
||||
//Automatically spawn any ordered support ships on fleets
|
||||
uint SPAWN_LIMIT = 10;
|
||||
for(uint i = 0, cnt = emp.fleetCount; i < cnt && SPAWN_LIMIT > 0; ++i) {
|
||||
Ship@ fleet = cast<Ship>(emp.fleets[i]);
|
||||
if(fleet is null)
|
||||
continue;
|
||||
if(!fleet.hasOrderedSupports)
|
||||
continue;
|
||||
|
||||
groups.syncFrom(fleet.getSupportGroups());
|
||||
for(uint n = 0, ncnt = groups.length; n < ncnt; ++n) {
|
||||
auto@ grp = groups[n];
|
||||
while(grp.ordered > 0 && SPAWN_LIMIT > 0) {
|
||||
Ship@ ship = createShip(fleet.position, grp.dsg, fleet.owner, groupLeader=null, free=true);
|
||||
fleet.supportBuildFinished(0, grp.dsg, null, ship);
|
||||
--grp.ordered;
|
||||
--SPAWN_LIMIT;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(emp.player !is null) {
|
||||
emp.player.controlMask |= player.mask | enemy.mask;
|
||||
emp.player.viewMask |= player.mask | enemy.mask;
|
||||
}
|
||||
}
|
||||
|
||||
//Make sure we always accept peace
|
||||
Lock lck(influenceLock);
|
||||
for(uint i = 0, cnt = activeTreaties.length; i < cnt; ++i) {
|
||||
if(activeTreaties[i].inviteMask & enemy.mask != 0)
|
||||
joinTreaty(enemy, activeTreaties[i].id);
|
||||
}
|
||||
}
|
||||
#section shadow
|
||||
bool initialized = false;
|
||||
void tick(double time) {
|
||||
Region@ system = getSystem(0).object;
|
||||
Empire@ player = getEmpire(0);
|
||||
Empire@ enemy = getEmpire(1);
|
||||
|
||||
if(!initialized && playerEmpire.valid) {
|
||||
CURRENT_PLAYER.controlMask |= player.mask | enemy.mask;
|
||||
CURRENT_PLAYER.viewMask |= player.mask | enemy.mask;
|
||||
|
||||
guiDialogueAction("Sandbox.Sandbox::SetupGUI");
|
||||
initialized = true;
|
||||
}
|
||||
}
|
||||
#section all
|
||||
};
|
||||
|
||||
#section gui
|
||||
from tabs.tabbar import tabBar, globalBar, closeTab, tabs, newTab, ActiveTab, browseTab;
|
||||
from tabs.DesignOverviewTab import createDesignOverviewTab, DesignOverview;
|
||||
from tabs.DesignEditorTab import DesignEditor;
|
||||
from tabs.GlobalBar import GlobalBar;
|
||||
from tabs.GalaxyTab import GalaxyTab;
|
||||
from tabs.HomeTab import HomeTab;
|
||||
from community.Home import createCommunityHome;
|
||||
import elements.BaseGuiElement;
|
||||
import elements.GuiButton;
|
||||
import elements.GuiDropdown;
|
||||
import elements.GuiListbox;
|
||||
import elements.GuiSprite;
|
||||
import elements.GuiMarkupText;
|
||||
import elements.GuiContextMenu;
|
||||
import dialogs.QuestionDialog;
|
||||
import util.design_export;
|
||||
from targeting.PointTarget import PointTargeting, targetPoint;
|
||||
import icons;
|
||||
|
||||
uint LastExportedDesign = 0;
|
||||
dictionary designFileNames;
|
||||
class SetupGUI : DialogueAction {
|
||||
void call() {
|
||||
isSandbox = true;
|
||||
|
||||
//Global bar
|
||||
auto@ gbar = cast<GlobalBar>(globalBar);
|
||||
gbar.container.visible = false;
|
||||
|
||||
@ui = SandboxUI(gbar);
|
||||
|
||||
//Tab bar
|
||||
tabBar.goButton.visible = false;
|
||||
tabBar.homeButton.visible = false;
|
||||
|
||||
for(uint i = 1, cnt = tabs.length; i < cnt; ++i)
|
||||
closeTab(tabs[1]);
|
||||
tabs[0].locked = true;
|
||||
|
||||
//Quickbar
|
||||
auto@ gtab = cast<GalaxyTab>(tabs[0]);
|
||||
gtab.quickbar.visible = false;
|
||||
|
||||
//Obsolete old designs
|
||||
if(OBSOLETE_DEFAULT) {
|
||||
for(uint i = 0, cnt = playerEmpire.designCount; i < cnt; ++i) {
|
||||
auto@ dsg = playerEmpire.designs[i];
|
||||
dsg.setObsolete(true);
|
||||
obsoletedDesigns.insert(dsg.id);
|
||||
}
|
||||
}
|
||||
|
||||
//Import designs
|
||||
FileList list(FOLDER, "*.design", true);
|
||||
uint cnt = list.length;
|
||||
for(uint i = 0; i < cnt; ++i) {
|
||||
DesignDescriptor desc;
|
||||
read_design(list.path[i], desc);
|
||||
|
||||
string fname = list.basename[i];
|
||||
fname = fname.substr(0, fname.length - list.extension[i].length - 1);
|
||||
designFileNames.set(desc.name, fname);
|
||||
|
||||
if(desc.hull is null)
|
||||
continue;
|
||||
if(!playerEmpire.shipset.hasHull(desc.hull))
|
||||
@desc.hull = getBestHull(desc, getHullTypeTag(desc.hull));
|
||||
const Design@ newDesign = makeDesign(desc);
|
||||
if(newDesign is null)
|
||||
continue;
|
||||
if(desc.settings !is null)
|
||||
newDesign.setSettings(desc.settings);
|
||||
const DesignClass@ cls = playerEmpire.getDesignClass(desc.className);
|
||||
const Design@ orig = playerEmpire.getDesign(desc.name);
|
||||
if(orig !is null)
|
||||
playerEmpire.changeDesign(orig, newDesign, cls);
|
||||
else
|
||||
playerEmpire.addDesign(cls, newDesign);
|
||||
}
|
||||
LastExportedDesign = playerEmpire.designCount;
|
||||
|
||||
//Designs tabs
|
||||
newTab(createDesignOverviewTab());
|
||||
newTab(createCommunityHome());
|
||||
tabs[2].locked = true;
|
||||
}
|
||||
};
|
||||
|
||||
class FlagshipElement : GuiMarkupContextOption {
|
||||
const Design@ dsg;
|
||||
Empire@ forEmpire;
|
||||
|
||||
FlagshipElement(const Design@ dsg, Empire@ emp) {
|
||||
@this.dsg = dsg;
|
||||
@this.forEmpire = emp;
|
||||
super(format("[offset=10][color=$5][b]$2[/b][/color] [offset=260]([loc=SIZE/] $3)[/offset][/offset]",
|
||||
toString(dsg.color),
|
||||
dsg.name,
|
||||
standardize(dsg.size, true),
|
||||
getSpriteDesc(dsg.icon),
|
||||
toString(dsg.color.interpolate(colors::White, 0.5))),
|
||||
FT_Subtitle);
|
||||
icon = dsg.icon;
|
||||
icon.color = dsg.color;
|
||||
}
|
||||
|
||||
void call(GuiContextMenu@ menu) {
|
||||
targetPoint(FlagshipTarget(dsg, forEmpire));
|
||||
}
|
||||
|
||||
int opCmp(const GuiListElement@ other) const {
|
||||
auto@ cmp = cast<const FlagshipElement@>(other);
|
||||
if(cmp.dsg.size < dsg.size)
|
||||
return 1;
|
||||
if(cmp.dsg.size > dsg.size)
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
class FlagshipTarget : PointTargeting {
|
||||
const Design@ dsg;
|
||||
Empire@ emp;
|
||||
|
||||
FlagshipTarget(const Design@ dsg, Empire@ emp) {
|
||||
@this.dsg = dsg;
|
||||
@this.emp = emp;
|
||||
icon = dsg.icon;
|
||||
allowMultiple = true;
|
||||
}
|
||||
|
||||
string message(const vec3d& pos, bool valid) {
|
||||
return dsg.name;
|
||||
}
|
||||
|
||||
void call(const vec3d& pos) {
|
||||
cheatSpawnFlagship(pos, dsg, emp);
|
||||
}
|
||||
|
||||
Color get_color() {
|
||||
return emp.color;
|
||||
}
|
||||
};
|
||||
|
||||
class ConfirmClear : QuestionDialogCallback {
|
||||
void questionCallback(QuestionDialog@ dialog, int answer) {
|
||||
if(answer == QA_Yes) {
|
||||
for(uint i = 0; i < 2; ++i) {
|
||||
Empire@ emp = getEmpire(i);
|
||||
uint objCnt = emp.objectCount;
|
||||
for(uint n = 0; n < objCnt; ++n)
|
||||
cheatDestroy(emp.objects[n]);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const string str_Support = "Support";
|
||||
class RootText : GuiText {
|
||||
RootText(IGuiElement@ ParentElement, Alignment@ Align, const string& Txt, FontType font = FT_Normal) {
|
||||
super(ParentElement, Align, Txt, font);
|
||||
}
|
||||
|
||||
IGuiElement@ elementFromPosition(const vec2i& pos) {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
class SandboxUI : BaseGuiElement {
|
||||
GuiText@ myStr;
|
||||
GuiText@ vsLabel;
|
||||
GuiText@ theirStr;
|
||||
|
||||
GuiButton@ warToggle;
|
||||
GuiSprite@ warIcon;
|
||||
GuiSprite@ peaceIcon;
|
||||
GuiSprite@ warArrow;
|
||||
GuiText@ warText;
|
||||
|
||||
GuiButton@ allyButton;
|
||||
GuiButton@ enemyButton;
|
||||
GuiButton@ clearButton;
|
||||
|
||||
GuiText@ descText;
|
||||
|
||||
SandboxUI(IGuiElement@ parent) {
|
||||
super(parent, Alignment().fill());
|
||||
|
||||
@myStr = GuiText(this, Alignment(Left+4, Top+4, Left+204, Bottom-5));
|
||||
myStr.color = getEmpire(0).color;
|
||||
myStr.vertAlign = 0.0;
|
||||
|
||||
@vsLabel = GuiText(this, Alignment(Left+4, Top+3, Left+204, Bottom-5), locale::VS);
|
||||
vsLabel.color = Color(0xaaaaaaff);
|
||||
vsLabel.vertAlign = 0.5;
|
||||
vsLabel.horizAlign = 0.5;
|
||||
|
||||
@theirStr = GuiText(this, Alignment(Left+4, Top+3, Left+204, Bottom-4));
|
||||
theirStr.color = getEmpire(1).color;
|
||||
theirStr.vertAlign = 0.9;
|
||||
theirStr.horizAlign = 1.0;
|
||||
|
||||
@warToggle = GuiButton(this, Alignment(Right-204, Top+3, Right-4, Bottom-5));
|
||||
|
||||
@warIcon = GuiSprite(warToggle, recti(), Sprite(material::StatusWar));
|
||||
@peaceIcon = GuiSprite(warToggle, recti(), Sprite(material::StatusPeace));
|
||||
|
||||
@warArrow = GuiSprite(warToggle, recti_area(84,16, 32,32), Sprite(spritesheet::ContextIcons, 0));
|
||||
|
||||
@warText = GuiText(warToggle, Alignment().padded(4));
|
||||
warText.stroke = Color(0x00000080);
|
||||
warText.font = FT_Bold;
|
||||
warText.vertAlign = 0.2;
|
||||
warText.horizAlign = 0.5;
|
||||
|
||||
Empire@ otherEmp = playerEmpire is getEmpire(0) ? getEmpire(1) : getEmpire(0);
|
||||
|
||||
int w = 160;
|
||||
@allyButton = GuiButton(this, Alignment(Left+0.5f-w-w/2-6, Top+5, Left+0.5f-w/2-6, Bottom-5));
|
||||
allyButton.text = locale::SPAWN_ALLIED;
|
||||
allyButton.color = colors::Green;
|
||||
allyButton.buttonIcon = Sprite(spritesheet::ShipIcons, 1, playerEmpire.color);
|
||||
|
||||
@enemyButton = GuiButton(this, Alignment(Left+0.5f-w/2, Top+5, Left+0.5f+w/2, Bottom-5));
|
||||
enemyButton.text = locale::SPAWN_ENEMY;
|
||||
enemyButton.color = colors::Red;
|
||||
enemyButton.buttonIcon = Sprite(spritesheet::ShipIcons, 1, otherEmp.color);
|
||||
|
||||
@clearButton = GuiButton(this, Alignment(Left+0.5f+w/2+6, Top+5, Left+0.5f+w+w/2+6, Bottom-5));
|
||||
clearButton.text = locale::SANDBOX_CLEAR;
|
||||
clearButton.color = colors::Orange;
|
||||
clearButton.buttonIcon = icons::Close;
|
||||
|
||||
@descText = RootText(this, Alignment(Left+250, Bottom+4, Right-4, Bottom+74), locale::SANDBOX_DESC);
|
||||
descText.noClip = true;
|
||||
descText.font = FT_Bold;
|
||||
descText.color = Color(0xaaaaaaff);
|
||||
descText.stroke = colors::Black;
|
||||
descText.horizAlign = 1.0;
|
||||
|
||||
updateAbsolutePosition();
|
||||
}
|
||||
|
||||
void openContext(Empire@ forEmp) {
|
||||
GuiContextMenu menu(mousePos);
|
||||
menu.itemHeight = 36;
|
||||
menu.flexWidth = false;
|
||||
menu.width = 400;
|
||||
|
||||
uint cnt = playerEmpire.designCount;
|
||||
for(uint i = 0; i < cnt; ++i) {
|
||||
auto@ dsg = playerEmpire.designs[i];
|
||||
if(dsg.obsolete || dsg.newer !is null || dsg.updated !is null)
|
||||
continue;
|
||||
if(dsg.hull.hasTag(str_Support))
|
||||
continue;
|
||||
menu.addOption(FlagshipElement(dsg, forEmp));
|
||||
}
|
||||
|
||||
menu.list.sortDesc();
|
||||
menu.updateAbsolutePosition();
|
||||
}
|
||||
|
||||
void update() {
|
||||
double str = getEmpire(0).TotalMilitary;
|
||||
double enemyStr = getEmpire(1).TotalMilitary;
|
||||
|
||||
myStr.text = format("$1 $2",
|
||||
standardize(sqr(str) * 0.001, true),
|
||||
locale::STRENGTH);
|
||||
theirStr.text = format("$1 $2",
|
||||
standardize(sqr(enemyStr) * 0.001, true),
|
||||
locale::STRENGTH);
|
||||
|
||||
if(str > enemyStr) {
|
||||
myStr.font = FT_Bold;
|
||||
theirStr.font = FT_Normal;
|
||||
}
|
||||
else if(enemyStr > str) {
|
||||
myStr.font = FT_Normal;
|
||||
theirStr.font = FT_Bold;
|
||||
}
|
||||
else {
|
||||
myStr.font = FT_Normal;
|
||||
theirStr.font = FT_Normal;
|
||||
}
|
||||
}
|
||||
|
||||
double timer = 0.0;
|
||||
void tick(double time) {
|
||||
bool atWar = getEmpire(0).isHostile(getEmpire(1));
|
||||
if(atWar) {
|
||||
warText.text = locale::SANDBOX_WAR;
|
||||
warText.color = Color(0xff8080ff);
|
||||
warToggle.color = Color(0xff8080ff);
|
||||
|
||||
warArrow.desc = Sprite(spritesheet::ContextIcons, 0);
|
||||
|
||||
warIcon.rect = recti_area(6,4, 36,36);
|
||||
peaceIcon.rect = recti_area(174,20, 20,20);
|
||||
}
|
||||
else {
|
||||
warText.text = locale::SANDBOX_PEACE;
|
||||
warText.color = Color(0x80ff80ff);
|
||||
warToggle.color = Color(0x80ff80ff);
|
||||
|
||||
warArrow.desc = Sprite(spritesheet::ContextIcons, 2);
|
||||
|
||||
peaceIcon.rect = recti_area(158,4, 36,36);
|
||||
warIcon.rect = recti_area(6,20, 20,20);
|
||||
}
|
||||
|
||||
timer += time;
|
||||
if(timer >= 0.5) {
|
||||
update();
|
||||
timer = 0.0;
|
||||
}
|
||||
|
||||
descText.visible = cast<GalaxyTab>(ActiveTab) !is null;
|
||||
}
|
||||
|
||||
bool onGuiEvent(const GuiEvent& evt) override {
|
||||
if(evt.type == GUI_Clicked) {
|
||||
Empire@ otherEmp = playerEmpire is getEmpire(0) ? getEmpire(1) : getEmpire(0);
|
||||
if(evt.caller is warToggle) {
|
||||
if(playerEmpire.isHostile(otherEmp))
|
||||
sendPeaceOffer(otherEmp);
|
||||
else
|
||||
declareWar(otherEmp);
|
||||
return true;
|
||||
}
|
||||
if(evt.caller is allyButton) {
|
||||
openContext(playerEmpire);
|
||||
return true;
|
||||
}
|
||||
if(evt.caller is enemyButton) {
|
||||
openContext(otherEmp);
|
||||
return true;
|
||||
}
|
||||
if(evt.caller is clearButton) {
|
||||
question(locale::SANDBOX_CONFIRM_CLEAR, ConfirmClear());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return BaseGuiElement::onGuiEvent(evt);
|
||||
}
|
||||
};
|
||||
|
||||
SandboxUI@ ui;
|
||||
bool isSandbox = false;
|
||||
set_int obsoletedDesigns;
|
||||
void deleteDesign(const Design@ design) {
|
||||
string fname;
|
||||
if(!designFileNames.get(design.name, fname))
|
||||
fname = design.name;
|
||||
string path = FOLDER+"/"+fname+".design";
|
||||
if(fileExists(path))
|
||||
deleteFile(path);
|
||||
}
|
||||
void exportDesign(const Design@ design) {
|
||||
string fname;
|
||||
if(!designFileNames.get(design.name, fname))
|
||||
fname = design.name;
|
||||
write_design(design, FOLDER+"/"+fname+".design", design.cls);
|
||||
}
|
||||
void tick(double time) {
|
||||
if(!isSandbox)
|
||||
return;
|
||||
|
||||
//Make any home tabs into galaxy tabs
|
||||
for(uint i = 0, cnt = tabs.length; i < cnt; ++i) {
|
||||
auto@ tab = tabs[i];
|
||||
if(cast<HomeTab>(tab) !is null) {
|
||||
browseTab(tab, createDesignOverviewTab());
|
||||
}
|
||||
}
|
||||
|
||||
//Update UI
|
||||
if(ui !is null)
|
||||
ui.tick(time);
|
||||
|
||||
//Export any designs we have
|
||||
for(uint i = 0, cnt = playerEmpire.designCount; i < cnt; ++i) {
|
||||
auto@ dsg = playerEmpire.designs[i];
|
||||
if(dsg.updated !is null)
|
||||
continue;
|
||||
if(dsg.newer !is null) {
|
||||
if(!obsoletedDesigns.contains(dsg.id) && dsg.newer.name != dsg.name) {
|
||||
auto@ check = playerEmpire.getDesign(dsg.name);
|
||||
if(check is null || check.newer !is null || check.obsolete) {
|
||||
//Act as obsolete
|
||||
deleteDesign(dsg);
|
||||
obsoletedDesigns.insert(dsg.id);
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if(dsg.obsolete) {
|
||||
//Delete obsoleted designs
|
||||
if(!obsoletedDesigns.contains(dsg.id)) {
|
||||
deleteDesign(dsg);
|
||||
obsoletedDesigns.insert(dsg.id);
|
||||
}
|
||||
}
|
||||
else {
|
||||
//Export new designs
|
||||
if(i >= LastExportedDesign) {
|
||||
exportDesign(dsg);
|
||||
}
|
||||
//Re-export unobsoleted designs
|
||||
if(obsoletedDesigns.contains(dsg.id)) {
|
||||
exportDesign(dsg);
|
||||
obsoletedDesigns.erase(dsg.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
LastExportedDesign = playerEmpire.designCount;
|
||||
}
|
||||
#section all
|
||||
@@ -0,0 +1,201 @@
|
||||
#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
|
||||
};
|
||||
|
After Width: | Height: | Size: 429 KiB |
@@ -0,0 +1,241 @@
|
||||
#priority init 1501
|
||||
import maps;
|
||||
|
||||
#section game
|
||||
import dialogue;
|
||||
#section all
|
||||
|
||||
#section server
|
||||
import object_creation;
|
||||
import influence;
|
||||
import tile_resources;
|
||||
import scenario;
|
||||
import systems;
|
||||
import map_loader;
|
||||
import campaign;
|
||||
#include "include/resource_constants.as"
|
||||
#section all
|
||||
|
||||
Scenario _map;
|
||||
class Scenario : Map {
|
||||
Scenario() {
|
||||
super();
|
||||
|
||||
isListed = false;
|
||||
isScenario = true;
|
||||
}
|
||||
|
||||
#section server
|
||||
PersistentGfx@ wormhole;
|
||||
vec3d wormholePos = vec3d(1400.0, 15.0, -750.0);
|
||||
double whLife = -1.0;
|
||||
double nextOpen = 180.0;
|
||||
bool periodicOpen = true;
|
||||
|
||||
void prepareSystem(SystemData@ data, SystemDesc@ desc) {
|
||||
@data.homeworlds = null;
|
||||
Map::prepareSystem(data, desc);
|
||||
}
|
||||
|
||||
bool canHaveHomeworld(SystemData@ data, Empire@ emp) {
|
||||
return false;
|
||||
}
|
||||
|
||||
void placeSystems() {
|
||||
loadMap("maps/Story/scenario1.txt").generate(this);
|
||||
}
|
||||
|
||||
void preGenerate() {
|
||||
Map::preGenerate();
|
||||
radius = 40000;
|
||||
}
|
||||
|
||||
void modSettings(GameSettings& settings) {
|
||||
settings.empires.length = 2;
|
||||
settings.empires[0].name = locale::SCEN1_PLAYER_EMP;
|
||||
settings.empires[0].shipset = "Gevron";
|
||||
settings.empires[1].name = locale::SCEN1_ENEMY_EMP;
|
||||
settings.empires[1].shipset = "Volkur";
|
||||
settings.empires[1].type = ET_NoAI;
|
||||
}
|
||||
|
||||
Planet@ planet(uint sys, uint ind) {
|
||||
return systemData[sys].planets[ind];
|
||||
}
|
||||
|
||||
void populate(Planet@ pl, Empire@ owner, double pop = 1.0, Object@ exportTo = null, double defense = 0.0) {
|
||||
@pl.owner = owner;
|
||||
pl.addPopulation(pop);
|
||||
if(exportTo !is null)
|
||||
pl.exportResource(owner, 0, exportTo);
|
||||
if(defense > 0)
|
||||
pl.spawnDefenseShips(defense);
|
||||
}
|
||||
|
||||
void init() {
|
||||
Empire@ enemy = getEmpire(1);
|
||||
|
||||
playerEmpire.setHostile(enemy, true);
|
||||
enemy.setHostile(playerEmpire, true);
|
||||
playerEmpire.Victory = -2;
|
||||
|
||||
wormholePos.normalize(planet(0,0).region.radius - 100.0);
|
||||
|
||||
populate(planet(1,1), enemy, 7.0, defense=70.0);
|
||||
populate(planet(1,0), enemy, exportTo=planet(1,1), defense = 5.0);
|
||||
populate(planet(2,0), enemy, 3.0, planet(1,1), defense = 44.0);
|
||||
populate(planet(2,1), enemy, exportTo=planet(2,0), defense = 12.0);
|
||||
populate(planet(2,3), enemy, exportTo=planet(2,0), defense = 5.0);
|
||||
populate(planet(0,1), enemy, 3.0, planet(1,1), defense = 27.0);
|
||||
populate(planet(0,0), enemy, exportTo=planet(0,1), defense = 7.0);
|
||||
populate(planet(0,2), enemy, exportTo=planet(0,1), defense = 8.0);
|
||||
populate(planet(0,3), enemy, exportTo=planet(1,1), defense = 15.0);
|
||||
planet(0,1).colonize(planet(1,2));
|
||||
planet(1,2).exportResource(enemy, 0, planet(1,1));
|
||||
|
||||
spawnFleet(enemy, planet(1,1).position + vec3d(50.0,0.0,0.0), "Defense Platform", 10);
|
||||
spawnFleet(enemy, planet(1,1).position + vec3d(40.0,0.0,0.0), "Defense Platform", 10);
|
||||
spawnFleet(enemy, planet(2,0).position + vec3d(40.0,0.0,0.0), "Defense Platform", 10);
|
||||
spawnFleet(enemy, planet(0,1).position + vec3d(50.0,0.0,0.0), "Defense Platform", 10);
|
||||
|
||||
Dialogue("SCEN1_INTRO");
|
||||
Dialogue("SCEN1_INTRO2")
|
||||
.newObjective.checker(1, IntroCinematic(this));
|
||||
Dialogue("SCEN1_CONQUER")
|
||||
.checker(1, Conquer(this));
|
||||
Dialogue("SCEN1_COMPLETE")
|
||||
.onStart(EndCinematic(this));
|
||||
}
|
||||
|
||||
Ship@ spawnFleet(Empire@ emp, const vec3d& pos, const string& base = "Titan", uint supBase = 25) {
|
||||
auto@ leaderDsg = emp.getDesign(base);
|
||||
auto@ sup3Dsg = emp.getDesign("Missile Boat");
|
||||
auto@ sup1Dsg = emp.getDesign("Beamship");
|
||||
auto@ sup2Dsg = emp.getDesign("Heavy Gunship");
|
||||
|
||||
Ship@ leader = createShip(pos, leaderDsg, emp, free=true);
|
||||
for(uint i = 0; i < supBase / 2; ++i)
|
||||
createShip(pos, sup1Dsg, emp, leader);
|
||||
for(uint i = 0; i < supBase / 4; ++i)
|
||||
createShip(pos, sup2Dsg, emp, leader);
|
||||
for(uint i = 0; i < supBase / 8; ++i)
|
||||
createShip(pos, sup3Dsg, emp, leader);
|
||||
leader.setHoldPosition(true);
|
||||
return leader;
|
||||
}
|
||||
|
||||
void openWormhole(double timeout = -1.0) {
|
||||
if(wormhole is null) {
|
||||
@wormhole = PersistentGfx();
|
||||
if(wormhole !is null)
|
||||
wormhole.establish(wormholePos, "Wormhole", 50.0, systemData[0].planets[0].region);
|
||||
}
|
||||
whLife = timeout;
|
||||
|
||||
nextOpen = 180.0;
|
||||
}
|
||||
|
||||
void closeWormhole() {
|
||||
if(wormhole !is null) {
|
||||
wormhole.stop();
|
||||
@wormhole = null;
|
||||
}
|
||||
}
|
||||
|
||||
void lockWormhole() {
|
||||
periodicOpen = false;
|
||||
}
|
||||
|
||||
void tick(double time) {
|
||||
if(wormhole !is null) {
|
||||
if(whLife > 0) {
|
||||
whLife -= time;
|
||||
if(whLife <= 0)
|
||||
closeWormhole();
|
||||
}
|
||||
}
|
||||
else if(periodicOpen) {
|
||||
nextOpen -= time;
|
||||
if(nextOpen < 0) {
|
||||
openWormhole(15.0);
|
||||
if(playerEmpire.TotalMilitary * playerEmpire.TotalMilitary / 1000 < 5000.0)
|
||||
spawnFleet(playerEmpire, wormholePos, "Dreadnaught", randomi(20,40))
|
||||
.addMoveOrder(wormholePos + random3d(80.0,150.0));
|
||||
}
|
||||
}
|
||||
}
|
||||
#section all
|
||||
};
|
||||
|
||||
#section server
|
||||
class IntroCinematic : GuiObjectiveCheck {
|
||||
uint sent = 0;
|
||||
double nextSend;
|
||||
PersistentGfx@ wh;
|
||||
Scenario@ scen;
|
||||
|
||||
IntroCinematic(Scenario@ _scen) {
|
||||
@scen = _scen;
|
||||
}
|
||||
|
||||
bool start() {
|
||||
scen.openWormhole();
|
||||
nextSend = gameTime + 8.0;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool check() {
|
||||
if(sent == 2) {
|
||||
if(gameTime > nextSend) {
|
||||
scen.closeWormhole();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else if(gameTime > nextSend) {
|
||||
nextSend = gameTime + 16.0;
|
||||
if(sent == 0)
|
||||
scen.spawnFleet(playerEmpire, scen.wormholePos, "Scout", 0)
|
||||
.addMoveOrder(scen.wormholePos + vec3d(-50.0, 0.0, -100.0));
|
||||
else
|
||||
scen.spawnFleet(playerEmpire, scen.wormholePos, "Dreadnaught", 35)
|
||||
.addMoveOrder(scen.wormholePos + vec3d(100.0, 0.0, 50.0));
|
||||
++sent;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
class Conquer : GuiObjectiveCheck {
|
||||
Scenario@ scen;
|
||||
Conquer(Scenario@ _scen) {
|
||||
@scen = _scen;
|
||||
}
|
||||
|
||||
bool check() {
|
||||
if(getEmpire(1).fleetCount > 0)
|
||||
return false;
|
||||
|
||||
auto@ systemData = scen.systemData;
|
||||
for(uint i = 0, cnt = systemData.length; i < cnt; ++i) {
|
||||
auto@ sys = systemData[i];
|
||||
for(uint j = 0, jcnt = sys.planets.length; j < jcnt; ++j) {
|
||||
auto@ pl = sys.planets[j];
|
||||
if(pl.owner.valid && pl.owner !is playerEmpire)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
class EndCinematic : DialogueAction {
|
||||
Scenario@ scen;
|
||||
EndCinematic(Scenario@ _scen) { @scen = _scen; }
|
||||
bool start() {
|
||||
scen.openWormhole();
|
||||
completeCampaignScenario("InevitableConquest");
|
||||
return true;
|
||||
}
|
||||
};
|
||||
#section all
|
||||
@@ -0,0 +1,26 @@
|
||||
//Starting System
|
||||
System: Varkus
|
||||
Position: (0, 0, 0)
|
||||
Effect: MakeStar(Temperature = 5778, Radius = 75)
|
||||
Effect: MakePlanet(Water)
|
||||
Effect: MakePlanet(Titanium)
|
||||
Effect: MakePlanet(Meat)
|
||||
Effect: MakePlanet(Grain)
|
||||
Effect: MakeAsteroid(Iron)
|
||||
Effect: MakeAsteroid(Aluminum)
|
||||
|
||||
System: Pidion
|
||||
Position: (3900, -450, 2400)
|
||||
Effect: MakeStar(Temperature = 8500, Radius = 80)
|
||||
Effect: MakePlanet(Water)
|
||||
Effect: MakePlanet(Grain)
|
||||
Effect: MakePlanet(Iron)
|
||||
Effect: MakeAsteroid(Water)
|
||||
|
||||
System: Nolvir
|
||||
Position: (-1850, 100, 3600)
|
||||
Effect: MakeStar(Temperature = 3200, Radius = 110)
|
||||
Effect: MakePlanet(Textiles)
|
||||
Effect: MakePlanet(Fruit)
|
||||
Effect: MakePlanet(Fulrate)
|
||||
Effect: MakePlanet(Water)
|
||||
@@ -0,0 +1,65 @@
|
||||
//Enemy gate system
|
||||
System: Odatak
|
||||
MakeStar(Temperature = 5778, Radius = 75)
|
||||
MakePlanet(Water, Conditions = False)
|
||||
MakePlanet(Meat, Conditions = False)
|
||||
MakePlanet(Salts, Conditions = False)
|
||||
MakePlanet(Water, Conditions = False)
|
||||
MakeAsteroid()
|
||||
MakeAsteroid()
|
||||
MakeAsteroid()
|
||||
|
||||
//Spacewood's system
|
||||
System: Lihls
|
||||
Position: (5000, 0, 0)
|
||||
|
||||
MakeStar(Temperature = 1000, Radius = 75)
|
||||
MakePlanet(Meat, Grid Size = (17, 10), Conditions = False, Moons = False)
|
||||
AddPlanetResource(Water)
|
||||
AddResource(Labor, +20)
|
||||
AddPopulation(3.0)
|
||||
Rename(Spacewood)
|
||||
NoNeedPopulationForLevel()
|
||||
MakePlanet(NativeGold, Conditions = False)
|
||||
MakePlanet(Fruit, Conditions = False)
|
||||
MakePlanet(Grain, Conditions = False)
|
||||
MakeAsteroid()
|
||||
|
||||
//Phasite system
|
||||
System: Xaste
|
||||
Position: (8000, 120, 8000)
|
||||
|
||||
MakeStar(Temperature = 2500, Radius = 75)
|
||||
MakePlanet(Meat, Conditions = False)
|
||||
MakePlanet(Fish, Conditions = False)
|
||||
MakePlanet(Water, Conditions = False)
|
||||
MakePlanet(Phasite, Conditions = False)
|
||||
MakeAsteroid()
|
||||
MakeAsteroid()
|
||||
MakeArtifact(PowerCell)
|
||||
|
||||
//Enemy colonized system
|
||||
System: Yingmow
|
||||
Position: (9000, -60, 15000)
|
||||
|
||||
MakeStar(Temperature = 8000, Radius = 75)
|
||||
MakePlanet(Neutronium, Conditions = False)
|
||||
MakePlanet(Fish, Conditions = False)
|
||||
MakePlanet(Water, Conditions = False)
|
||||
MakePlanet(Titanium, Conditions = False)
|
||||
MakeAsteroid()
|
||||
MakeAsteroid()
|
||||
MakeAsteroid()
|
||||
MakeAsteroid()
|
||||
|
||||
//Neutral system
|
||||
System: Darolco
|
||||
Position: (2000, -60, 9400)
|
||||
|
||||
NameSystem(Darolco)
|
||||
MakeStar(Temperature = 4000, Radius = 75)
|
||||
MakePlanet(Pekelm, Conditions = False)
|
||||
MakePlanet(Meat, Conditions = False)
|
||||
MakePlanet(Water, Conditions = False)
|
||||
MakePlanet(Water, Conditions = False)
|
||||
MakeAsteroid()
|
||||