Files
starruler-linux/scripts/shared/util/design_designer.as
T
2018-07-17 14:15:37 +02:00

1358 lines
33 KiB
ActionScript

import util.design_export;
import designs;
import int getTraitID(const string&) from "traits";
final class SubsystemData {
int sysId;
vec2u core;
vec2u[] hexes;
vec3d direction;
SubsystemData() {
direction = vec3d_front();
}
const SubsystemDef@ get_def() {
return getSubsystemDef(sysId);
}
};
final class HexData {
int subsystem;
int module;
HexData() {
subsystem = -1;
module = -1;
}
HexData(int sys, int mod) {
subsystem = sys;
module = mod;
}
};
void randomize(array<int>& arr) {
for(int i = 0, bnd = arr.length - 1; i < bnd; ++i) {
int ind = randomi(i, bnd);
if(i == ind)
continue;
int s = arr[i];
arr[i] = arr[ind];
arr[ind] = s;
}
}
void randomize(array<HexStep>& arr) {
HexStep local;
for(int i = 0, bnd = arr.length - 1; i < bnd; ++i) {
int ind = randomi(i, bnd);
if(i == ind)
continue;
HexStep@ first = arr[i];
HexStep@ other = arr[ind];
local = first;
first = other;
other = local;
}
}
void randomize(array<HexGridAdjacency>& arr) {
HexGridAdjacency local;
for(int i = 0, bnd = arr.length - 1; i < bnd; ++i) {
int ind = randomi(i, bnd);
if(i == ind)
continue;
HexGridAdjacency first = arr[i];
HexGridAdjacency other = arr[ind];
local = first;
first = other;
other = local;
}
}
enum DesignType {
DT_Support,
DT_Flagship,
DT_Station,
DT_Satellite,
};
string[] autoSupportNames = {
"Fiddle",
"Rose",
"Evergreen",
"Hammer",
"Sickle",
"River",
"Grave",
"Demon",
"Aquaria",
"Tiny",
"Bear",
"Beast",
"Cliff",
"Fjord",
"Delta",
"Needle",
"Stick",
"Sheen",
"Trunk"
};
string[] autoFlagNames = {
"Azrael",
"Olympus",
"Volcanic",
"Diamond",
"Grandiose",
"Voyage",
"Erebus",
"Weaver",
"Hive",
"Justice",
"Venture"
};
HexGridAdjacency flipDir(HexGridAdjacency d) {
switch(d) {
case HEX_Up:
return HEX_Down;
case HEX_UpRight:
return HEX_DownRight;
case HEX_DownRight:
return HEX_UpRight;
case HEX_Down:
return HEX_Up;
case HEX_DownLeft:
return HEX_UpLeft;
case HEX_UpLeft:
return HEX_DownLeft;
}
return HEX_Up;
}
HexGridAdjacency stepDir(HexGridAdjacency from, int dir) {
int d = int(from) - dir;
if(d < 0)
d += 6;
else if(d >= 6)
d -= 6;
return HexGridAdjacency(d);
}
final class HexStep {
vec2u hex;
HexGridAdjacency dir;
HexStep() {}
HexStep(const vec2u& Pos, HexGridAdjacency Dir) {
hex = Pos;
dir = Dir;
}
};
enum HexLocation {
HL_Internal,
HL_Back,
HL_Side,
HL_Front
};
class Designer {
DesignType type;
vec2u bound;
vec2u topLeft, botRight;
SubsystemData@[] subsystems;
HexData[] hexes;
vec2u center;
HexGridb grid, used, place;
HexGridi location;
Empire@ owner;
double scale = 1.0;
string className, name;
int weaponCount = -1;
bool supplies = true;
bool support = true;
int engineSize = 0;
int hyperSize = 0;
int armorLevel = 0;
bool needShrine = false;
bool randomHull = false;
uint hexLimit = 0;
void prepare(DesignType Type, double size, Empire@ emp, const string& ClassName, const string& Name = "") {
@owner = emp;
scale = size;
type = Type;
bound = vec2u(getDesignGridSize(type == DT_Support ? "Support" : "Flagship", size));
center = vec2u(bound.x / 2, bound.y / 2 - 1);
grid.resize(bound.x, bound.y);
used.resize(bound.x, bound.y);
place.resize(bound.x, bound.y);
location.resize(bound.x, bound.y);
className = ClassName;
name = Name;
weaponCount = -1;
supplies = true;
support = true;
engineSize = 0;
hyperSize = 0;
armorLevel = 0;
needShrine = emp.hasTrait(getTraitID("Devout"));
switch(Type) {
case DT_Support: hexLimit = 60; break;
case DT_Flagship: hexLimit = 128; break;
case DT_Station: hexLimit = 160; break;
}
}
vec2u flip(vec2u p) const {
if(p.x % 2 == center.x % 2)
return vec2u(p.x, center.y * 2 - p.y);
else if(center.x % 2 == 0)
return vec2u(p.x, center.y * 2 - p.y - 1);
else
return vec2u(p.x, center.y * 2 - p.y + 1);
}
void test() {
uint pass = 0;
for(uint i = 0; i < 100; ++i)
if(design(1, false) !is null)
++pass;
error("Pass: " + pass + "/" + 100 + " (" + (pass) + "%)");
}
//Attempt to design a ship with the given specs
const Design@ design(uint maxTries = 128, bool doTest = false) {
if(doTest)
test();
for(uint i = 0; i < maxTries; ++i) {
subsystems.length = 0;
hexes.length = 0;
hexes.length = bound.x * bound.y;
grid.clear(false);
used.clear(false);
location.clear(HL_Internal);
bool generated = false;
for(uint i = 0; i < 10; ++i) {
if(generate(center, hexLimit)) {
generated = true;
break;
}
}
if(!generated)
continue;
bool success = false;
switch(type) {
case DT_Support:
success = _designSupport(); break;
case DT_Flagship:
success = _designFlagship(); break;
}
if(!success)
continue;
DesignDescriptor desc;
desc.size = scale;
desc.gridSize = bound;
@desc.owner = owner;
desc.className = className;
desc.name = name;
if(name.length > 0)
desc.name = name;
else if(type == DT_Support)
desc.name = autoSupportNames[randomi(0,autoSupportNames.length-1)];
else
desc.name = autoFlagNames[randomi(0,autoFlagNames.length-1)];
for(uint i = 0, cnt = subsystems.length(); i < cnt; ++i) {
const SubsystemDef@ def = subsystems[i].def;
desc.addSystem(def);
desc.setDirection(subsystems[i].direction);
for(uint j = 0, jcnt = subsystems[i].hexes.length(); j < jcnt; ++j) {
vec2u pos = subsystems[i].hexes[j];
HexData@ hdata = hex[pos];
desc.addHex(pos, def.modules[hdata.module]);
}
}
if(randomHull && owner.shipset !is null) {
string hullTag = type == DT_Support ? "Support" : "Flagship";
uint hullCount = 0;
for(uint i = 0, cnt = owner.shipset.hullCount; i < cnt; ++i) {
const Hull@ hull = owner.shipset.hulls[i];
//Check if it matches the tag
if(!hull.hasTag(hullTag))
continue;
//Make sure we can use this hull
if(hull.minSize >= 0 && hull.minSize > desc.size)
continue;
if(hull.maxSize >= 0 && hull.maxSize < desc.size)
continue;
hullCount += 1;
if(randomd() < 1.0 / double(hullCount))
@desc.hull = hull;
}
}
else {
@desc.hull = getBestHull(desc, type == DT_Support ? "Support" : "Flagship", owner);
}
if(desc.hull is null)
return null;
auto@ dsg = makeDesign(desc);
if(dsg !is null && !dsg.hasFatalErrors())
return dsg;
}
return null;
}
HexData@ get_hex(vec2u p) {
return hexes[p.x + p.y * bound.width];
}
void categorize() {
for(uint y = 0; y < bound.y; ++y) {
for(uint x = 0; x < bound.x; ++x) {
vec2u pos = vec2u(x, y);
if(place[pos]) break;
location[pos] = HL_Back;
}
}
for(uint x = 0; x < bound.x; ++x) {
vec2u pos = vec2u(x, 0);
do {
if(place[pos]) break;
location[pos] = HL_Side;
} while(location.advance(pos, HEX_Down));
pos = vec2u(x, bound.y-1);
do {
if(place[pos]) break;
location[pos] = HL_Side;
} while(location.advance(pos, HEX_Up));
}
for(uint y = 0; y < bound.y; ++y) {
for(int x = bound.x-1; x >= 0; --x) {
vec2u pos = vec2u(x, y);
if(place[pos]) break;
location[pos] = HL_Front;
}
}
}
SubsystemData@ addSubsystem(const SubsystemDef@ type, vec2u at, bool onlyInternal = true) {
if(type is null || !grid.valid(at) || used[at] || (onlyInternal && !place[at]))
return null;
SubsystemData@ data;
if(type.hasCore) {
@data = SubsystemData();
int index = subsystems.length;
subsystems.insertLast(@data);
data.sysId = type.index;
data.hexes.insertLast(at);
data.core = at;
hex[at] = HexData(index, type.coreModule.index);
grid[at] = true;
used[at] = true;
}
else {
int index = -1;
for(uint i = 0, cnt = subsystems.length; i < cnt; ++i) {
if(subsystems[i].def is type) {
index = i;
break;
}
}
if(index >= 0) {
@data = subsystems[index];
}
else {
@data = SubsystemData();
index = subsystems.length;
subsystems.insertLast(@data);
data.sysId = type.index;
}
data.hexes.insertLast(at);
hex[at] = HexData(index, type.defaultModule.index);
grid[at] = true;
used[at] = true;
}
return data;
}
void expand(SubsystemData@ sys, vec2u to, bool onlyInternal = true) {
if(sys is null || !grid.valid(to) || used[to] || (onlyInternal && !place[to]))
return;
int index = subsystems.find(sys);
sys.hexes.insertLast(to);
hex[to] = HexData(index, sys.def.defaultModule.index);
grid[to] = true;
used[to] = true;
}
void occupyLine(vec2u from, HexGridAdjacency dir) {
if(grid.valid(from))
used[from] = true;
while(grid.advance(from, dir)) {
used[from] = true;
location[from] = HL_Back;
}
}
vec2u advance(vec2u p, HexGridAdjacency dir) const {
grid.advance(p, dir);
return p;
}
vec2u advance(vec2u p, HexGridAdjacency dir, HexGridAdjacency dir2) const {
grid.advance(p, dir);
grid.advance(p, dir2);
return p;
}
vec2u advance(vec2u p, HexGridAdjacency dir, HexGridAdjacency dir2, HexGridAdjacency dir3) const {
grid.advance(p, dir);
grid.advance(p, dir2);
grid.advance(p, dir3);
return p;
}
vec2u advance(vec2u p, HexGridAdjacency dir, HexGridAdjacency dir2, HexGridAdjacency dir3, HexGridAdjacency dir4) const {
grid.advance(p, dir);
grid.advance(p, dir2);
grid.advance(p, dir3);
grid.advance(p, dir4);
return p;
}
double biased(int bias) {
double r = randomd();
if(bias > 0) {
while(bias-- > 0)
r = max(r, randomd());
}
else {
while(bias++ < 0)
r = min(r, randomd());
}
return r;
}
array<const SubsystemDef@>@ getSubsystems(const string& tag, const string& tag2 = "", const string& tag3 = "") {
array<const SubsystemDef@> list;
string hullTag;
switch(type) {
case DT_Support: hullTag = "Support"; break;
case DT_Flagship: hullTag = "Flagship"; break;
case DT_Station: hullTag = "Station"; break;
}
for(uint i = 0, cnt = getSubsystemDefCount(); i < cnt; ++i) {
auto@ type = getSubsystemDef(i);
if(owner.isUnlocked(type) && type.hasTag(tag) && type.hasHullTag(hullTag) && !type.hasTag(ST_SpecialCost)
&& (tag2.length == 0 || type.hasTag(tag2))
&& (tag3.length == 0 || type.hasTag(tag3)) )
list.insertLast(type);
}
return list;
}
void rebound(const vec2u &in pos) {
topLeft.x = min(pos.x, topLeft.x);
topLeft.y = min(pos.y, topLeft.y);
botRight.x = max(pos.x, botRight.x);
botRight.y = max(pos.y, botRight.y);
}
array<int> rotDirs = {-2,-1,0,1,2};
array<HexStep> cellsActive, cellSwap;
bool generate(vec2u center, int hexes) {
auto@ cells = @cellsActive;
place.clear(false);
topLeft = center;
botRight = center;
cells.length = 0;
place[center] = true;
hexes -= 1;
for(uint i = 0; i < 6; ++i)
cells.insertLast(HexStep(advance(center, HexGridAdjacency(i)), HexGridAdjacency(i)));
randomize(cells);
uint newCount = max(randomi(1,cells.length), randomi(1, cells.length));
if(newCount < cells.length)
cells.length = newCount;
auto@ swap = @cellSwap;
while(hexes > 0 && cells.length > 0) {
swap.length = 0;
bool addedAny = false;
uint i = 0;
for(uint cnt = min(randomi(0, cells.length-1), randomi(0, cells.length-1)); i < cnt; ++i)
swap.insertLast(cells[i]);
for(uint cnt = cells.length; i < cnt && hexes > 0; ++i) {
auto@ cell = cells[i];
vec2u p = cell.hex;
if(!place.valid(p))
continue;
auto f = flip(p);
if(!place.valid(f))
continue;
if(!place[p]) {
if(f == p) {
hexes -= 1;
rebound(p);
}
else {
if(hexes == 1)
continue;
place[f] = true;
hexes -= 2;
rebound(p);
rebound(f);
}
place[p] = true;
addedAny = true;
uint split = min(randomi(1,5), randomi(3,5));
randomize(rotDirs);
for(uint d = 0; d < split; ++d) {
auto dir = stepDir(cell.dir, rotDirs[d]);
swap.insertLast(HexStep(advance(cell.hex, dir), dir));
}
}
}
if(swap.length == 0)
break;
randomize(swap);
auto@ s = @swap;
@swap = cells;
@cells = s;
}
return hexes == 0;
}
bool spread(const SubsystemDef@ type, vec2u pos, int size, bool mirror, const array<HexGridAdjacency>& dirs, const array<HexGridAdjacency>@ occupy = null, double angle = 0) {
auto@ cells = @cellsActive;
int initSize = size;
if(occupy !is null) {
for(uint i = 0, cnt = occupy.length; i < cnt; ++i) {
vec2u p = pos;
auto dir = occupy[i];
bool first = true;
while(grid.valid(p)) {
if(used[p])
return false;
if(!first && place[p])
return false;
first = false;
if(!grid.advance(p, dir))
break;
}
}
}
auto@ upper = addSubsystem(type, pos);
if(upper is null)
return false;
if(angle != 0)
upper.direction = quaterniond_fromAxisAngle(vec3d_up(), angle) * upper.direction;
SubsystemData@ lower;
if(mirror) {
@lower = addSubsystem(type, flip(pos));
if(lower is null)
return false;
if(angle != 0)
lower.direction = quaterniond_fromAxisAngle(vec3d_up(), -angle) * lower.direction;
}
if(occupy !is null) {
for(uint i = 0, cnt = occupy.length; i < cnt; ++i) {
occupyLine(pos, occupy[i]);
if(lower !is null)
occupyLine(flip(pos), flipDir(occupy[i]));
}
}
cells.length = 0;
for(uint i = 0, cnt = dirs.length; i < cnt; ++i)
cells.insertLast(HexStep(advance(pos, dirs[i]), dirs[i]));
auto@ swap = @cellSwap;
while(size > 0) {
swap.length = 0;
randomize(cells);
bool addedAny = false;
uint i = 0;
for(uint cnt = min(randomi(0, cells.length/2), randomi(0, cells.length/2)); i < cnt; ++i)
swap.insertLast(cells[i]);
for(uint cnt = cells.length; i < cnt && size > 0; ++i) {
vec2u p = cells[i].hex;
if(!used[p] && place[p]) {
auto@ cell = cells[i];
expand(upper, p);
auto f = flip(p);
if(p == f) {
size -= 1;
}
else {
expand(mirror ? lower : upper, f);
size -= 2;
}
addedAny = true;
uint split = 5;//min(randomi(1,5), randomi(3,5));
randomize(rotDirs);
for(uint d = 0; d < split; ++d) {
auto dir = stepDir(cell.dir, rotDirs[d]);
swap.insertLast(HexStep(advance(cell.hex, dir), dir));
}
}
}
if(swap.length == 0)
break;
auto@ s = @swap;
@swap = cells;
@cells = s;
}
return true;
}
vec2u findExternal(vec2u pos, HexGridAdjacency dir) {
vec2u prev = pos;
while(place[pos]) {
prev = pos;
if(!place.advance(pos, dir))
break;
}
return prev;
}
vec2u findExternal(vec2u pos, vec2i dir) {
vec2u prev = pos;
while(place.valid(pos) && place[pos]) {
prev = pos;
pos.x += dir.x;
pos.y += dir.y;
}
return prev;
}
bool _designFlagship() {
vec2u pos;
array<HexGridAdjacency> dirs, occupy;
dirs.length = 0; occupy.length = 0;
auto@ bridgeTypes = getSubsystems("ControlCore");
if(bridgeTypes.length == 0)
return false;
//error("Bridge");
if(randomi(0,2) != 0) {
dirs.length = 0; occupy.length = 0;
dirs.insertLast(HEX_Up);
dirs.insertLast(HEX_UpLeft);
dirs.insertLast(HEX_UpRight);
bool success = spread(bridgeTypes[randomi(0,bridgeTypes.length-1)], center,
randomi(1,3), false, dirs);
if(!success)
return false;
if(randomi(0,1) == 0)
addSubsystem(bridgeTypes[randomi(0,bridgeTypes.length-1)],
randomi(0,1) == 0 ? center - vec2u(randomi(1,2)*2,0) : center + vec2u(randomi(1,2)*2,0));
}
else {
dirs.length = 0; occupy.length = 0;
dirs.insertLast(HEX_Up);
dirs.insertLast(HEX_UpLeft);
dirs.insertLast(HEX_UpRight);
dirs.insertLast(HEX_Down);
dirs.insertLast(HEX_DownLeft);
dirs.insertLast(HEX_DownRight);
bool success = spread(bridgeTypes[randomi(0,bridgeTypes.length-1)], center - vec2u(0, randomi(1,2)),
randomi(0,2), true, dirs);
if(!success)
return false;
}
//error("Shrine, race specific");
if(needShrine) {
auto@ shrineTypes = getSubsystems("Prayer");
if(shrineTypes.length != 0) {
dirs.length = 0; occupy.length = 0;
dirs.insertLast(HEX_Up);
dirs.insertLast(HEX_UpLeft);
dirs.insertLast(HEX_UpRight);
dirs.insertLast(HEX_Down);
dirs.insertLast(HEX_DownLeft);
dirs.insertLast(HEX_DownRight);
if(randomi(0,1) == 0)
spread(shrineTypes[randomi(0, shrineTypes.length-1)], center + vec2u(randomi(1, center.x * 1 / 3) * 2, 0),
randomi(8, 20), false, dirs, occupy);
else
spread(shrineTypes[randomi(0, shrineTypes.length-1)], vec2u(randomi(center.x / 2, center.x * 5 / 3), randomi(center.y/2, center.y-1)),
randomi(8, 20), true, dirs, occupy);
}
}
//error("Reactor");
if(supplies || randomi(0,1) == 0) {
auto@ reactors = getSubsystems("IsReactor");
if(reactors.length == 0)
return false;
const SubsystemDef@ reactor = reactors[randomi(0,reactors.length-1)];
if(randomi(0,1) == 0) {
array<HexGridAdjacency> dirs;
dirs.insertLast(HEX_Up);
dirs.insertLast(HEX_UpLeft);
dirs.insertLast(HEX_UpRight);
bool success = false;
for(uint i = 0; i < 3; ++i) {
bool s = spread(reactor, randomi(0,2) == 0 ? center + vec2u(randomi(1,center.x/4) * 2, 0) : center - vec2u(randomi(1,center.x/3) * 2, 0),
randomi(2,7), false, dirs);
if(s)
success = true;
}
if(!success)
return false;
}
else {
array<HexGridAdjacency> dirs;
dirs.insertLast(HEX_Up);
dirs.insertLast(HEX_UpLeft);
dirs.insertLast(HEX_UpRight);
dirs.insertLast(HEX_Down);
dirs.insertLast(HEX_DownLeft);
dirs.insertLast(HEX_DownRight);
bool success = false;
for(uint i = 0; i < 3; ++i) {
bool s = spread(reactor, vec2u(randomi(topLeft.x, botRight.x), randomi(topLeft.y, center.y-1)),
randomi(4,9), true, dirs);
if(s)
success = true;
}
if(!success)
return false;
}
}
//error("2nd Defense");
auto@ secondaryDefense = getSubsystems("SecondaryDefense");
if(secondaryDefense.length != 0 && randomi(0,2) == 0) {
dirs.length = 0; occupy.length = 0;
dirs.insertLast(HEX_Up);
dirs.insertLast(HEX_DownRight);
dirs.insertLast(HEX_UpLeft);
dirs.insertLast(HEX_UpRight);
dirs.insertLast(HEX_DownLeft);
dirs.insertLast(HEX_Down);
if(randomi(0,1) == 0)
spread(secondaryDefense[randomi(0, secondaryDefense.length-1)], center + vec2u(randomi(1, center.x * 1 / 3) * 2, 0),
randomi(0, hexes.length / 28), false, dirs, occupy);
else
spread(secondaryDefense[randomi(0, secondaryDefense.length-1)], vec2u(randomi(center.x / 2, center.x * 5 / 3), randomi(center.y/2, center.y-1)),
randomi(0, hexes.length / 28), true, dirs, occupy);
}
//error("Engines");
auto@ engineTypes = getSubsystems("Engine", "GivesThrust");
if(engineTypes.length == 0)
return false;
bool engineSuccess = false;
for(uint try = 0; try < 6; ++try) {
auto@ engineType = engineTypes[randomi(0,engineTypes.length-1)];
int engines = randomi(1,2);
if(engines == 1) {
if(engineType.hasTag("ExteriorCore")) {
occupy.insertLast(HEX_UpLeft);
occupy.insertLast(HEX_DownLeft);
}
else {
dirs.insertLast(HEX_UpLeft);
}
dirs.insertLast(HEX_Up);
dirs.insertLast(HEX_UpRight);
int dLeft = int(topLeft.x - center.x) / 2;
int dRight = -1;
bool success = false;
for(uint try = 0; try < 2; ++try) {
vec2u p = center + vec2u(randomi(dLeft,dRight) * 2, 0);
if(occupy.length != 0)
p = findExternal(p, vec2i(-2, 0));
success = spread(engineType, p,
min(randomi(5, 22), randomi(5, 22)) + engineSize, false, dirs, occupy);
if(success)
break;
}
if(success) {
engineSuccess = true;
break;
}
}
else if(engines == 2) {
dirs.length = 0; occupy.length = 0;
if(engineType.hasTag("ExteriorCore")) {
occupy.insertLast(HEX_UpLeft);
if(randomi(0,1) == 0)
dirs.insertLast(HEX_Up);
}
else {
dirs.insertLast(HEX_Up);
dirs.insertLast(HEX_UpLeft);
dirs.insertLast(HEX_DownLeft);
}
dirs.insertLast(HEX_UpRight);
dirs.insertLast(HEX_DownRight);
dirs.insertLast(HEX_Down);
bool success = false;
for(uint try = 0; try < 2; ++try) {
vec2u p = vec2u(randomi(topLeft.x,center.x), randomi(topLeft.y, center.y-1));
if(occupy.length != 0)
p = findExternal(p, occupy[0]);
success = spread(engineType, p,
min(randomi(0, 6), randomi(2, 6)) + engineSize + hexes.length / 28, true, dirs, occupy);
if(success)
break;
}
if(success) {
engineSuccess = true;
break;
}
}
}
if(!engineSuccess)
return false;
//error("Hyperdrive");
auto@ hyperTypes = getSubsystems("Hyperengine");
if(hyperTypes.length != 0) {
auto@ hyperType = hyperTypes[randomi(0,hyperTypes.length-1)];
dirs.length = 0; occupy.length = 0;
dirs.insertLast(HEX_Up);
dirs.insertLast(HEX_UpRight);
dirs.insertLast(HEX_UpLeft);
int dLeft = int(topLeft.x - center.x) / 2;
int dRight = int(botRight.x - center.x) / 2;
bool success = false;
for(uint try = 0; try < 3; ++try) {
success = spread(hyperType, center + vec2u(randomi(dLeft,dRight) * 2, 0),
randomi(5, 30) + hyperSize, false, dirs);
if(success)
break;
}
if(!success)
return false;
//Very small hyperdrives will render ships unusable
if(subsystems.last.hexes.length == 1)
return false;
}
//error("Weapons");
auto@ weapons = getSubsystems("Weapon", "MainDPS");
if(weapons.length == 0)
return false;
bool weaponSuccess = (weaponCount == 0);
for(uint i = 0, cnt = weaponCount >= 0 ? weaponCount : randomi(1,3); i < cnt; ++i) {
dirs.length = 0; occupy.length = 0;
if(i == 2) {
auto@ secondary = getSubsystems("Weapon", "SecondaryDPS");
for(uint i = 0, cnt = secondary.length; i < cnt; ++i)
weapons.insertLast(secondary[i]);
}
const SubsystemDef@ wepType = weapons[randomi(0,weapons.length-1)];
bool homing = wepType.hasTag("Homing");
bool success = false;
if(i != 0 || randomi(0,homing ? 2 : 1) != 0) {
if(!homing)
occupy.insertLast(HEX_UpRight);
else {
switch(randomi(0,2)) {
case 0: occupy.insertLast(HEX_UpLeft); break;
case 1: occupy.insertLast(HEX_Up); break;
case 2: occupy.insertLast(HEX_UpRight); break;
}
}
dirs.insertLast(HEX_Up);
dirs.insertLast(HEX_DownRight);
dirs.insertLast(HEX_UpLeft);
dirs.insertLast(HEX_UpRight);
dirs.insertLast(HEX_DownLeft);
dirs.insertLast(HEX_Down);
for(uint try = 0; try < 6; ++try) {
success = spread(wepType, findExternal(vec2u(randomi(topLeft.x, botRight.x), randomi(topLeft.y, center.y-1)), occupy[0]),
(randomi(2, 8) + hexes.length / 24) * (3 / min(cnt,3)), true, dirs, occupy, hexToRadians(occupy[0]) + pi * randomd(-0.05, 0.05));
if(success)
break;
}
}
else {
int dLeft = 1;
int dRight = int(botRight.x - center.x) / 2;
occupy.insertLast(HEX_UpRight);
occupy.insertLast(HEX_DownRight);
dirs.insertLast(HEX_Up);
dirs.insertLast(HEX_UpLeft);
for(uint try = 0; try < 2; ++try) {
success = spread(wepType, findExternal(center + vec2u(randomi(dLeft, dRight) * 2, 0), vec2i(2, 0)),
(randomi(2, 8) + hexes.length / 24) * (3 / min(cnt,3)), false, dirs, occupy);
if(success)
break;
}
}
if(success)
weaponSuccess = true;
}
if(!weaponSuccess)
return false;
auto@ supplyModule = getSubsystemDef("SupplyModule");
auto@ supportCap = getSubsystemDef("SupportCapModule");
//error("Supplies");
if(supplies) {
dirs.length = 0; occupy.length = 0;
dirs.insertLast(HEX_Up);
dirs.insertLast(HEX_UpRight);
dirs.insertLast(HEX_UpLeft);
dirs.insertLast(HEX_DownRight);
dirs.insertLast(HEX_DownLeft);
dirs.insertLast(HEX_Down);
int count = randomi(1, 3);
bool success = false;
for(uint try = 0; try < 6; ++try) {
success = spread(supplyModule, vec2u(randomi(topLeft.x, botRight.x), randomi(topLeft.y, center.y)),
count, true, dirs);
if(success)
break;
}
if(!success)
return false;
}
//error("Support");
if(support) {
dirs.length = 0; occupy.length = 0;
dirs.insertLast(HEX_Up);
dirs.insertLast(HEX_UpRight);
dirs.insertLast(HEX_UpLeft);
dirs.insertLast(HEX_DownRight);
dirs.insertLast(HEX_DownLeft);
dirs.insertLast(HEX_Down);
int count = randomi(1,4);
bool success = false;
for(uint try = 0; try < 6; ++try) {
success = spread(supportCap, vec2u(randomi(topLeft.x, botRight.x), randomi(topLeft.y, center.y)),
count, true, dirs);
if(success)
break;
}
if(!success)
return false;
}
double rearArmor = biased(-2 + armorLevel);
double sideArmor = biased(-1 + armorLevel);
double frontArmor = biased(+1 + armorLevel);
double internalBits = 1.0;
auto@ armors = getSubsystems("IsArmor");
auto@ frontPlate = armors.length != 0 ? armors[randomi(0,armors.length-1)] : null;
auto@ sidePlate = armors.length != 0 ? armors[randomi(0,armors.length-1)] : null;
auto@ rearPlate = armors.length != 0 ? armors[randomi(0,armors.length-1)] : null;
categorize();
//error("Fillin");
for(pos.x = 0; pos.x < bound.width; ++pos.x) {
for(pos.y = 0; pos.y <= center.y; ++pos.y) {
if(used[pos])
continue;
int expandDir = -1;
auto placeType = HL_Internal;
if(!place[pos]) {
placeType = HexLocation(location[pos]);
bool placeable = false;
if(placeType == HL_Internal)
placeable = true;
if(!placeable) {
uint base = randomi(0,6);
for(uint d = 0; d < 6; ++d) {
vec2u p = pos;
if(grid.advance(p, HexGridAdjacency((d + base) % 6)) && location[p] == HL_Internal) {
if(expandDir == -1 && used[p])
expandDir = d;
placeable = true;
}
}
}
if(!placeable)
continue;
}
SubsystemData@ expandUpper, expandLower;
vec2u posUpper, posLower;
const SubsystemDef@ armorType;
bool makeSupply = false, makeSupport = false;
switch(placeType) {
case HL_Internal:
if(randomd() < internalBits) {
if(expandDir != -1 && randomd() < 0.85) {
posUpper = pos;
grid.advance(posUpper, HexGridAdjacency(expandDir));
@expandUpper = subsystems[hex[posUpper].subsystem];
posLower = flip(pos);
grid.advance(posLower, flipDir(HexGridAdjacency(expandDir)));
@expandLower = subsystems[hex[posLower].subsystem];
}
else {
switch(randomi(0,6)) {
case 0: case 1: makeSupply = supplies; break;
case 2: case 3: makeSupport = support; break;
case 4: default:
//@armorType = frontPlate;
break;
}
//If we didn't generate supplies or support, fall back to armor
@armorType = frontPlate;
}
}
break;
case HL_Back:
if(randomd() < rearArmor)
@armorType = rearPlate;
break;
case HL_Side:
if(randomd() < sideArmor)
@armorType = sidePlate;
break;
case HL_Front:
if(randomd() < frontArmor)
@armorType = frontPlate;
break;
}
if(expandUpper !is null) {
expand(expandUpper, posUpper);
expand(expandLower, posLower);
}
else if(makeSupply) {
auto@ m = addSubsystem(supplyModule, pos);
expand(m, flip(pos));
}
else if(makeSupport) {
auto@ m = addSubsystem(supportCap, pos);
expand(m, flip(pos));
}
else if(armorType !is null) {
auto@ m = addSubsystem(armorType, pos, onlyInternal = false);
expand(m, flip(pos), onlyInternal = false);
}
}
}
return true;
}
bool _designSupport() {
vec2u pos;
auto@ armors = getSubsystems("IsArmor");
auto@ plate = armors.length != 0 ? armors[randomi(0,armors.length-1)] : null;
uint engines = 1;
if(randomi(0,2) == 0)
engines = 2;
uint guns = 1;
switch(randomi(0,4)) {
case 2: case 3:
guns = 2; break;
case 4:
guns = 3; break;
}
auto@ bridgeTypes = getSubsystems("ControlCore");
if(bridgeTypes.length == 0)
return false;
auto@ bridge = addSubsystem(bridgeTypes[randomi(0,bridgeTypes.length-1)], center);
if(randomi(0,1) == 0) {
expand(bridge, advance(bridge.core, HEX_Down));
expand(bridge, advance(bridge.core, HEX_Up));
}
auto@ weapons = getSubsystems("Weapon");
if(weapons.length == 0)
return false;
const SubsystemDef@ wepType = weapons[randomi(0,weapons.length-1)];
double wepFraction = normald(0.4, 0.65);
int wepCount = weaponCount >= 0 ? weaponCount : guns;
bool gunSuccess = wepCount == 0;
while(wepCount > 0) {
bool homing = wepType.hasTag("Homing");
bool success = false;
if(wepCount == 1) {
wepCount -= 1;
array<HexGridAdjacency> dirs, occupy;
occupy.insertLast(HEX_UpRight);
occupy.insertLast(HEX_DownRight);
dirs.insertLast(HEX_Up);
dirs.insertLast(HEX_UpLeft);
bool success = spread(wepType, findExternal(center + vec2u(randomi(1, center.x/4) * 2, 0), vec2i(2,0)),
wepFraction * hexLimit, false, dirs, occupy);
if(success)
gunSuccess = true;
}
else if(wepCount >= 2) {
wepCount -= 2;
array<HexGridAdjacency> dirs, occupy;
if(!homing)
occupy.insertLast(HEX_UpRight);
else {
switch(randomi(0,2)) {
case 0: occupy.insertLast(HEX_UpLeft); break;
case 1: occupy.insertLast(HEX_Up); break;
case 2: occupy.insertLast(HEX_UpRight); break;
}
}
dirs.insertLast(HEX_Up);
dirs.insertLast(HEX_DownRight);
dirs.insertLast(HEX_UpLeft);
dirs.insertLast(HEX_DownLeft);
dirs.insertLast(HEX_Down);
vec2u attempt = homing ?
vec2u(randomi(topLeft.x, botRight.x), randomi(topLeft.y, center.y-1)) :
vec2u(randomi(center.x, botRight.x), randomi(topLeft.y, center.y-1));
bool success = spread(wepType, findExternal(attempt, occupy[0]),
wepFraction * hexLimit, true, dirs, occupy, pi * randomd(0.2, 0.3));
if(success)
gunSuccess = true;
}
}
if(!gunSuccess)
return false;
auto@ engineTypes = getSubsystems("Engine");
if(engineTypes.length == 0)
return false;
auto@ engineType = engineTypes[randomi(0,engineTypes.length-1)];
if(engines == 1) {
array<HexGridAdjacency> dirs, occupy;
occupy.insertLast(HEX_UpLeft);
occupy.insertLast(HEX_DownLeft);
dirs.insertLast(HEX_Up);
dirs.insertLast(HEX_UpRight);
bool success = spread(engineType, findExternal(center - vec2u(randomi(1,center.x/2 - 1) * 2, 0), vec2i(-2,0)),
(1.0 - wepFraction) * hexLimit, false, dirs, occupy);
if(!success)
return false;
}
else if(engines == 2) {
array<HexGridAdjacency> dirs, occupy;
occupy.insertLast(HEX_UpLeft);
dirs.insertLast(HEX_Up);
dirs.insertLast(HEX_UpRight);
dirs.insertLast(HEX_DownRight);
dirs.insertLast(HEX_Down);
bool success = spread(engineType, findExternal(vec2u(randomi(0,center.x/2) * 2 + 1, randomi(center.y/2, center.y-1)), occupy[0]),
(1.0 - wepFraction) * hexLimit, true, dirs, occupy);
if(!success)
return false;
}
//Attach armor to the design
double rearArmor = biased(-3 + armorLevel);
double sideArmor = biased(-2 + armorLevel);
double frontArmor = biased(armorLevel);
double internalArmor = 1.0;
SubsystemData@ armor;
categorize();
for(pos.x = 0; pos.x < bound.width; ++pos.x) {
for(pos.y = 0; pos.y <= center.y; ++pos.y) {
if(used[pos])
continue;
int expandDir = -1;
auto placeType = HL_Internal;
if(!place[pos]) {
placeType = HexLocation(location[pos]);
bool placeable = false;
if(placeType == HL_Internal)
placeable = true;
if(!placeable) {
uint base = randomi(0,6);
for(uint d = 0; d < 6; ++d) {
vec2u p = pos;
if(grid.advance(p, HexGridAdjacency((d + base) % 6)) && location[p] == HL_Internal) {
if(expandDir == -1 && used[p])
expandDir = d;
placeable = true;
}
}
}
if(!placeable)
continue;
}
SubsystemData@ expandUpper, expandLower;
vec2u posUpper, posLower;
bool makeArmor = false;
switch(placeType) {
case HL_Internal:
if(randomd() < internalArmor) {
if(expandDir != -1) {
posUpper = pos;
grid.advance(posUpper, HexGridAdjacency(expandDir));
@expandUpper = subsystems[hex[posUpper].subsystem];
posLower = flip(pos);
grid.advance(posLower, flipDir(HexGridAdjacency(expandDir)));
@expandLower = subsystems[hex[posLower].subsystem];
}
else {
makeArmor = true;
}
}
break;
case HL_Back:
if(randomd() < rearArmor)
makeArmor = true;
break;
case HL_Side:
if(randomd() < sideArmor)
makeArmor = true;
break;
case HL_Front:
if(randomd() < frontArmor)
makeArmor = true;
break;
}
if(makeArmor) {
if(armor !is null)
expand(armor, pos, onlyInternal = false);
else
@armor = addSubsystem(plate, pos, onlyInternal = false);
if(armor !is null)
expand(armor, flip(pos), onlyInternal = false);
else
@armor = addSubsystem(plate, flip(pos), onlyInternal = false);
}
else if(expandUpper !is null) {
expand(expandUpper, posUpper);
expand(expandLower, posLower);
}
}
}
return true;
}
};