572 lines
14 KiB
ActionScript
572 lines
14 KiB
ActionScript
#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
|