1610 lines
48 KiB
ActionScript
1610 lines
48 KiB
ActionScript
import settings.map_lib;
|
|
import hooks;
|
|
import biomes;
|
|
import buildings;
|
|
import resources;
|
|
import planet_types;
|
|
import camps;
|
|
import anomalies;
|
|
import pickups;
|
|
import block_effects;
|
|
import artifacts;
|
|
import statuses;
|
|
from map_systems import IMapHook, MapHook;
|
|
import generic_hooks;
|
|
from resources import RARITY_DISTRIBUTION;
|
|
import cargo;
|
|
import ship_groups;
|
|
|
|
#section server
|
|
import systems;
|
|
import object_creation;
|
|
import remnant_designs;
|
|
from objects.Asteroid import createAsteroid;
|
|
from objects.Anomaly import createAnomaly;
|
|
from objects.Artifact import createArtifact;
|
|
from objects.Oddity import createNebula;
|
|
from empire import Creeps;
|
|
#section all
|
|
|
|
//MakeStar(<Temperature>, <Radius> = 100, <Position> = (0, 0, 0), Suffix = "")
|
|
// Generate a star with the specified temperature, radius and position.
|
|
class MakeStar : MapHook {
|
|
double AVG_STAR_HEALTH = 20000000000;
|
|
|
|
Document doc("Creates a star in the system.");
|
|
Argument tempK("Temperature", AT_Range, doc="Star temperature in Kelvin.");
|
|
Argument rad("Radius", AT_Range, "100.0", doc="Raidus of the star");
|
|
Argument position("Position", AT_Position, "(0, 0, 0)", doc="Position relative to the center of the system to create the star.");
|
|
Argument suffix("Suffix", AT_Locale, "", doc="Suffix to append to the star name.");
|
|
Argument normalDist("NormalTempRange", AT_Boolean, "False", doc="Whether to use a normal or flat distribution for the star temperature.");
|
|
Argument moves(AT_Boolean, "False", doc="Whether the star can move and the light should track it.");
|
|
|
|
#section server
|
|
void trigger(SystemData@ data, SystemDesc@ system, Object@& current) const override {
|
|
double temp = 0.0;
|
|
if(arguments[0].isRange && arguments[4].boolean)
|
|
temp = normald(arguments[0].decimal,arguments[1].decimal2);
|
|
else
|
|
temp = arguments[0].fromRange();
|
|
double radius = arguments[1].fromRange();
|
|
vec3d pos = arguments[2].fromPosition();
|
|
|
|
//Create star
|
|
ObjectDesc starDesc;
|
|
starDesc.type = OT_Star;
|
|
starDesc.flags |= objNoDamage;
|
|
if(system !is null) {
|
|
starDesc.name = format(locale::SYSTEM_STAR, system.name);
|
|
if(arguments[3].str.length > 0)
|
|
starDesc.name += " "+arguments[3].str;
|
|
starDesc.position = system.position + pos;
|
|
}
|
|
else {
|
|
starDesc.name = locale::SYSTEM_STAR;
|
|
}
|
|
starDesc.radius = radius;
|
|
starDesc.delayedCreation = true;
|
|
|
|
Star@ star = cast<Star>(makeObject(starDesc));
|
|
|
|
if(system !is null)
|
|
@star.region = system.object;
|
|
star.temperature = temp;
|
|
star.finalizeCreation();
|
|
if(system !is null)
|
|
system.object.enterRegion(star);
|
|
|
|
//Create star node
|
|
Node@ node = bindNode(star, "StarNode");
|
|
node.color = blackBody(temp, max((temp + 15000.0) / 40000.0, 1.0));
|
|
if(system !is null)
|
|
node.hintParentObject(system.object, false);
|
|
|
|
double hp = AVG_STAR_HEALTH * (radius / 75.0);
|
|
star.Health = hp;
|
|
star.MaxHealth = hp;
|
|
|
|
//Create light
|
|
LightDesc lightDesc;
|
|
lightDesc.att_quadratic = 1.f/(2000.f*2000.f);
|
|
lightDesc.position = vec3f(star.position);
|
|
lightDesc.diffuse = node.color * 1.0f;
|
|
lightDesc.specular = lightDesc.diffuse;
|
|
lightDesc.radius = star.radius;
|
|
|
|
if(moves.boolean)
|
|
makeLight(lightDesc, node);
|
|
else
|
|
makeLight(lightDesc);
|
|
|
|
if(data !is null)
|
|
@data.star = star;
|
|
@current = star;
|
|
}
|
|
#section all
|
|
};
|
|
|
|
//MakeBlackhole(<Radius> = 20, <Position> = (0, 0, 0))
|
|
// Generate a star with the specified temperature, radius and position.
|
|
class MakeBlackhole : MapHook {
|
|
double BLACKHOLE_HEALTH = 200000000000;
|
|
|
|
Document doc("Creates a black hole in the system.");
|
|
Argument rad("Radius", AT_Range, "100.0", doc="Radius of the event horizon.");
|
|
Argument position("Position", AT_Position, "(0, 0, 0)", doc="Position relative to the center of the system to create the black hole.");
|
|
|
|
#section server
|
|
void trigger(SystemData@ data, SystemDesc@ system, Object@& current) const override {
|
|
double radius = arguments[0].fromRange();
|
|
vec3d pos = arguments[1].fromPosition();
|
|
|
|
//Create star
|
|
ObjectDesc starDesc;
|
|
starDesc.type = OT_Star;
|
|
starDesc.flags |= objNoDamage;
|
|
starDesc.name = format(locale::SYSTEM_BLACKHOLE, system.name);
|
|
starDesc.position = system.position + pos;
|
|
starDesc.radius = radius;
|
|
starDesc.delayedCreation = true;
|
|
|
|
Star@ star = cast<Star>(makeObject(starDesc));
|
|
star.alwaysVisible = true;
|
|
|
|
@star.region = system.object;
|
|
star.temperature = 0.0;
|
|
star.finalizeCreation();
|
|
system.object.enterRegion(star);
|
|
|
|
star.Health = BLACKHOLE_HEALTH;
|
|
star.MaxHealth = BLACKHOLE_HEALTH;
|
|
|
|
//Create star node
|
|
Node@ node = bindNode(star, "BlackholeNode");
|
|
node.color = blackBody(16000.0, max((16000.0 + 15000.0) / 40000.0, 1.0));
|
|
node.hintParentObject(system.object, false);
|
|
cast<BlackholeNode>(node).establish(star);
|
|
|
|
//Create light
|
|
LightDesc lightDesc;
|
|
lightDesc.att_quadratic = 1.f/(2000.f*2000.f);
|
|
lightDesc.position = vec3f(star.position);
|
|
lightDesc.diffuse = node.color * 1.0f;
|
|
lightDesc.diffuse.a = 0.f;
|
|
lightDesc.specular = lightDesc.diffuse;
|
|
lightDesc.radius = star.radius;
|
|
makeLight(lightDesc);
|
|
|
|
if(data !is null)
|
|
@data.star = star;
|
|
@current = star;
|
|
}
|
|
#section all
|
|
};
|
|
|
|
int parseRarity(const string& v) {
|
|
if(v.equals_nocase("common")) {
|
|
return RR_Common;
|
|
}
|
|
else if(v.equals_nocase("uncommon")) {
|
|
return RR_Uncommon;
|
|
}
|
|
else if(v.equals_nocase("rare")) {
|
|
return RR_Rare;
|
|
}
|
|
else if(v.equals_nocase("epic")) {
|
|
return RR_Epic;
|
|
}
|
|
else {
|
|
error(" Error: Invalid rarity spec "+escape(v));
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
bool parseResourceSpec(array<const ResourceType@>@ resPossib, const string& v) {
|
|
if(v.findFirst(":") == -1) {
|
|
if(v == "null")
|
|
return true;
|
|
const ResourceType@ type = getResource(v);
|
|
if(type is null) {
|
|
error(" Error: Invalid resource "+escape(v));
|
|
return false;
|
|
}
|
|
resPossib.insertLast(type);
|
|
return true;
|
|
}
|
|
else {
|
|
array<string>@ args = v.split(":");
|
|
if(args.length > 3) {
|
|
error(" Error: Invalid resource spec "+escape(v));
|
|
return false;
|
|
}
|
|
|
|
int rarity = INT_MAX;
|
|
if(args[0].equals_nocase("randomtype")) {
|
|
if(args.length == 3)
|
|
rarity = parseRarity(args[2]);
|
|
const ResourceClass@ cls = getResourceClass(args[1]);
|
|
if(cls is null) {
|
|
error(" Error: Invalid resource type "+escape(v));
|
|
return false;
|
|
}
|
|
|
|
for(uint i = 0, cnt = cls.types.length; i < cnt; ++i) {
|
|
if(rarity != INT_MAX && cls.types[i].rarity != rarity)
|
|
continue;
|
|
if(cls.types[i].artificial || cls.types[i].distribution <= 0)
|
|
continue;
|
|
if(cls.types[i].unique)
|
|
continue;
|
|
resPossib.insertLast(cls.types[i]);
|
|
}
|
|
return true;
|
|
}
|
|
else if(args[0].equals_nocase("randomlevel")) {
|
|
uint lv = toInt(args[1]);
|
|
if(args.length == 3)
|
|
rarity = parseRarity(args[2]);
|
|
for(uint i = 0, cnt = getResourceCount(); i < cnt; ++i) {
|
|
auto@ res = getResource(i);
|
|
if(res.level != lv)
|
|
continue;
|
|
if(rarity != INT_MAX && res.rarity != rarity)
|
|
continue;
|
|
if(res.artificial || res.unique)
|
|
continue;
|
|
if(res.limitlessLevel)
|
|
continue;
|
|
if(res.frequency <= 0 || res.distribution <= 0)
|
|
continue;
|
|
resPossib.insertLast(res);
|
|
}
|
|
return true;
|
|
}
|
|
else if(args[0].equals_nocase("randomrarity")) {
|
|
if(args.length == 2)
|
|
rarity = parseRarity(args[1]);
|
|
for(uint i = 0, cnt = getResourceCount(); i < cnt; ++i) {
|
|
auto@ res = getResource(i);
|
|
if(rarity != INT_MAX && res.rarity != rarity)
|
|
continue;
|
|
if(res.artificial || res.unique)
|
|
continue;
|
|
if(res.frequency <= 0 || res.distribution <= 0)
|
|
continue;
|
|
resPossib.insertLast(res);
|
|
}
|
|
return true;
|
|
}
|
|
else if(args[0].equals_nocase("levelgte")) {
|
|
uint lv = toInt(args[1]);
|
|
if(args.length == 3)
|
|
rarity = parseRarity(args[2]);
|
|
for(uint i = 0, cnt = getResourceCount(); i < cnt; ++i) {
|
|
auto@ res = getResource(i);
|
|
if(res.level < lv)
|
|
continue;
|
|
if(rarity != INT_MAX && res.rarity != rarity)
|
|
continue;
|
|
if(res.artificial || res.unique)
|
|
continue;
|
|
if(res.limitlessLevel)
|
|
continue;
|
|
if(res.frequency <= 0 || res.distribution <= 0)
|
|
continue;
|
|
resPossib.insertLast(res);
|
|
}
|
|
return true;
|
|
}
|
|
else {
|
|
for(uint i = 0, cnt = args.length; i < cnt; ++i) {
|
|
auto@ res = getResource(args[i]);
|
|
if(res is null) {
|
|
error(" Error: Invalid resource spec "+escape(v));
|
|
return false;
|
|
}
|
|
resPossib.insertLast(res);
|
|
}
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
//MakePlanet(<Resource> = Destributed, <Radius> = 6:14, <Orbit Spacing> = 125:275,
|
|
// <Grid Size> = (-1, -1))
|
|
// Create a new planet with <Resource> and size <Radius>. Spaced in the orbit from
|
|
// the last planet by <Orbit Spacing>.
|
|
class MakePlanet : MapHook {
|
|
int AVG_PLANET_GRID_WIDTH = 15;
|
|
int AVG_PLANET_GRID_HEIGHT = 9;
|
|
double AVG_PLANET_HEALTH = 1500000000;
|
|
array<const ResourceType@> resPossib;
|
|
bool distribute = false;
|
|
|
|
Document doc("Create a new planet in the system.");
|
|
Argument resource(AT_Custom, "distributed", doc="The primary resource on the planet. 'distributed' to randomize.");
|
|
Argument radius(AT_Range, "6:14", doc="Size of the planet, can be a random range.");
|
|
Argument orbit_spacing(AT_Range, "125:275", doc="Distance from the previous planet.");
|
|
Argument grid_size(AT_Position2D, "(-1, -1)", doc="Size of the planet's surface grid. (-1,-1) to randomize based on radius.");
|
|
Argument conditions(AT_Boolean, "True", doc="Whether to let the planet randomly generate a condition.");
|
|
Argument distribute_resource(AT_Boolean, "False", doc="Whether or not the selected resources should be frequency distributed.");
|
|
Argument moons(AT_Boolean, "True", doc="Whether this planet should have a randomized amount of moons generated on it to start with.");
|
|
Argument rings(AT_Boolean, "True", doc="Whether the planet can have rings generated around it.");
|
|
Argument physics(AT_Boolean, "True", doc="Whether the planet should be a physical object.");
|
|
|
|
bool instantiate() override {
|
|
if(arguments[0].str.equals_nocase("distributed"))
|
|
distribute = true;
|
|
else if(!parseResourceSpec(resPossib, arguments[0].str))
|
|
return false;
|
|
return MapHook::instantiate();
|
|
}
|
|
|
|
#section server
|
|
void trigger(SystemData@ data, SystemDesc@ system, Object@& current) const override {
|
|
const ResourceType@ resource;
|
|
if(resPossib.length > 0) {
|
|
if(resPossib.length == 1)
|
|
@resource = resPossib[0];
|
|
else if(distribute_resource.boolean) {
|
|
double roll = randomd();
|
|
double freq = 0.0;
|
|
for(uint i = 0, cnt = resPossib.length; i < cnt; ++i) {
|
|
freq += resPossib[i].frequency;
|
|
double chance = resPossib[i].frequency / freq;
|
|
if(roll < chance) {
|
|
@resource = resPossib[i];
|
|
roll /= chance;
|
|
}
|
|
else {
|
|
roll = (roll - chance) / (1.0 - chance);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
@resource = resPossib[randomi(0, resPossib.length-1)];
|
|
}
|
|
if(data is null && distribute)
|
|
@resource = getDistributedResource();
|
|
if(resource !is null)
|
|
markResourceUsed(resource);
|
|
|
|
double radius = arguments[1].decimal;
|
|
if(arguments[1].isRange)
|
|
radius = normald(arguments[1].decimal, arguments[1].decimal2);
|
|
double spacing = arguments[2].fromRange() * config::SYSTEM_SIZE;
|
|
|
|
system.radius += spacing;
|
|
|
|
double pos = system.radius;
|
|
if(spacing == 0)
|
|
pos = randomd(200, system.radius);
|
|
|
|
double angle = randomd(0,twopi);
|
|
vec3d offset(cos(angle) * pos, 0, sin(angle) * -pos);
|
|
|
|
ObjectDesc planetDesc;
|
|
planetDesc.flags |= objNoDamage;
|
|
planetDesc.flags |= objMemorable;
|
|
planetDesc.type = OT_Planet;
|
|
planetDesc.delayedCreation = true;
|
|
@planetDesc.owner = null;
|
|
planetDesc.radius = radius;
|
|
planetDesc.position = system.position + offset;
|
|
|
|
if(!physics.boolean) {
|
|
planetDesc.flags |= objNoPhysics;
|
|
planetDesc.flags |= objNoCollide;
|
|
}
|
|
|
|
planetDesc.name = system.name + " ";
|
|
if(data !is null)
|
|
appendRoman(data.planets.length + 1, planetDesc.name);
|
|
else
|
|
appendRoman(system.object.planetCount + 1, planetDesc.name);
|
|
|
|
Planet@ planet = cast<Planet>(makeObject(planetDesc));
|
|
@planet.region = system.object;
|
|
|
|
if(data !is null)
|
|
data.planets.insertLast(planet);
|
|
|
|
//Generate biomes
|
|
const Biome@ biome1;
|
|
if(resource is null) {
|
|
@biome1 = getDistributedBiome();
|
|
}
|
|
else {
|
|
@biome1 = getBiome(resource.nativeBiome);
|
|
if(biome1 is null)
|
|
@biome1 = getDistributedBiome();
|
|
}
|
|
const Biome@ biome2 = getDistributedBiome();
|
|
const Biome@ biome3 = getDistributedBiome();
|
|
|
|
//Figure out planet size
|
|
double sizeFact = clamp(radius / 10.0, 0.1, 5.0);
|
|
int gridW = round(AVG_PLANET_GRID_WIDTH * sizeFact);
|
|
int gridH = round(AVG_PLANET_GRID_HEIGHT * sizeFact);
|
|
|
|
vec2d givenGrid = arguments[3].fromPosition2D();
|
|
if(givenGrid.x > 0)
|
|
gridW = ceil(givenGrid.x);
|
|
if(givenGrid.y > 0)
|
|
gridH = ceil(givenGrid.y);
|
|
|
|
//Figure out planet type
|
|
const PlanetType@ planetType = getBestPlanetType(biome1, biome2, biome3);
|
|
planet.PlanetType = planetType.id;
|
|
planet.OrbitSize = 100 + radius;
|
|
|
|
//Setup orbit
|
|
planet.orbitAround(system.position, offset.length);
|
|
planet.orbitSpin(randomd(35.0, 90.0));
|
|
|
|
//Create the planet surface;
|
|
uint resId = uint(-1);
|
|
if(resource !is null)
|
|
resId = resource.id;
|
|
planet.initSurface(gridW, gridH, biome1.id, biome2.id, biome3.id, resId);
|
|
|
|
//Make node
|
|
PlanetNode@ plNode = cast<PlanetNode>(bindNode(planet, "PlanetNode"));
|
|
plNode.establish(planet);
|
|
plNode.planetType = planet.PlanetType;
|
|
if(rings.boolean && randomi(0,9) == 0) {
|
|
uint style = randomi();
|
|
plNode.addRing(style);
|
|
planet.setRing(style);
|
|
}
|
|
|
|
if(resource !is null)
|
|
resource.applyGraphics(planet, plNode);
|
|
else if(distribute && data !is null)
|
|
data.distributedResources.insertLast(planet);
|
|
|
|
double health = AVG_PLANET_HEALTH * sizeFact;
|
|
planet.Health = health;
|
|
planet.MaxHealth = health;
|
|
|
|
planet.finalizeCreation();
|
|
|
|
//Setup condition
|
|
if(conditions.boolean && data !is null) {
|
|
if(randomd() < config::PLANET_CONDITION_CHANCE)
|
|
data.distributedConditions.insertLast(planet);
|
|
}
|
|
|
|
//Setup moons
|
|
if(moons.boolean) {
|
|
while(randomd() < config::PLANET_MOON_CHANCE) {
|
|
planet.addMoon();
|
|
planet.addStatus(getStatusID("Moon"));
|
|
}
|
|
}
|
|
|
|
//Place in region
|
|
system.object.enterRegion(planet);
|
|
plNode.hintParentObject(system.object, false);
|
|
|
|
@current = planet;
|
|
}
|
|
#section all
|
|
};
|
|
|
|
#section server
|
|
Planet@ spawnPlanetSpec(const vec3d& point, const string& resourceSpec, bool distributeResource = true, double radius = 0.0, bool physics = true) {
|
|
MakePlanet plHook;
|
|
plHook.initClass();
|
|
plHook.resource.str = resourceSpec;
|
|
plHook.distribute_resource.boolean = distributeResource;
|
|
plHook.moons.boolean = false;
|
|
plHook.rings.boolean = false;
|
|
plHook.conditions.boolean = false;
|
|
plHook.physics.boolean = physics;
|
|
if(radius != 0)
|
|
plHook.radius.set(radius);
|
|
plHook.instantiate();
|
|
|
|
Object@ current;
|
|
|
|
auto@ reg = getRegion(point);
|
|
auto@ sys = getSystem(reg);
|
|
plHook.trigger(null, sys, current);
|
|
|
|
auto@ planet = cast<Planet>(current);
|
|
planet.orbitAround(point, sys.position);
|
|
return planet;
|
|
}
|
|
#section all
|
|
|
|
class MakeMoon : MapHook {
|
|
Document doc("Add a moon to the previously generated planet.");
|
|
Argument size(AT_Decimal, "0", doc="Radius size of the moon.");
|
|
|
|
#section server
|
|
void trigger(SystemData@ data, SystemDesc@ system, Object@& current) const override {
|
|
Object@ obj = current;
|
|
if(obj.isPlanet) {
|
|
cast<Planet>(obj).addMoon(size.decimal);
|
|
obj.addStatus(getStatusID("Moon"));
|
|
}
|
|
}
|
|
#section all
|
|
};
|
|
|
|
class BonusHealth : MapHook {
|
|
Document doc("Add bonus health to the star or planet.");
|
|
Argument amount(AT_Decimal, doc="Amount of bonus health to add.");
|
|
|
|
#section server
|
|
void trigger(SystemData@ data, SystemDesc@ system, Object@& current) const override {
|
|
Object@ obj = current;
|
|
if(obj.isPlanet) {
|
|
Planet@ pl = cast<Planet>(obj);
|
|
pl.Health += amount.decimal;
|
|
pl.MaxHealth += amount.decimal;
|
|
}
|
|
else if(obj.isStar) {
|
|
Star@ star = cast<Star>(obj);
|
|
star.Health += amount.decimal;
|
|
star.MaxHealth += amount.decimal;
|
|
}
|
|
}
|
|
#section all
|
|
};
|
|
|
|
class MakeNebula : MapHook {
|
|
Document doc("Turn the system into a nebula.");
|
|
Argument color(AT_Color, "#f0c870", doc="Color of the nebula.");
|
|
|
|
array<Color> colorPossibs;
|
|
|
|
bool instantiate() override {
|
|
auto@ txt = color.str.split(":");
|
|
for(uint i = 0, cnt = txt.length; i < cnt; ++i)
|
|
colorPossibs.insertLast(toColor(txt[i]));
|
|
return MapHook::instantiate();
|
|
}
|
|
|
|
#section server
|
|
void trigger(SystemData@ data, SystemDesc@ system, Object@& current) const override {
|
|
Color col = colorPossibs[randomi(0, colorPossibs.length-1)];
|
|
createNebula(system.position, system.radius, color=col.rgba, region=system.object);
|
|
}
|
|
#section all
|
|
};
|
|
|
|
//ExpandSystem(<Radius> = 1000)
|
|
// Expand the current system's size by <Radius>.
|
|
class ExpandSystem : MapHook {
|
|
Document doc("Expands the system boundary.");
|
|
Argument radius("Radius", AT_Range, "1000", doc="Amount to add to the current system radius.");
|
|
|
|
#section server
|
|
void trigger(SystemData@ data, SystemDesc@ system, Object@& current) const override {
|
|
system.radius += arguments[0].fromRange();;
|
|
}
|
|
#section all
|
|
};
|
|
|
|
//SetupOrbit(<Radius> = 100, <Position> = (0, 0, 0), Orbit Pct = -1.0)
|
|
// Setup an orbit on the previous object.
|
|
class SetupOrbit : MapHook {
|
|
Document doc("Gives the previously created object an orbit.");
|
|
Argument radius("Radius", AT_Range, "100.0", doc="Radius at which the object should orbit.");
|
|
Argument position("Position", AT_Position, "(0, 0, 0)", doc="Position relative to the system to orbit around.");
|
|
Argument pct("Orbit Pct", AT_Range, "-1.0", doc="Percent through the year to start the orbit (0-1).");
|
|
|
|
#section server
|
|
void trigger(SystemData@ data, SystemDesc@ system, Object@& current) const override {
|
|
Object@ cur = current;
|
|
if(cur is null || !cur.hasOrbit)
|
|
return;
|
|
|
|
double radius = arguments[0].fromRange();
|
|
vec3d pos = arguments[1].fromPosition() + system.position;
|
|
double pct = arguments[2].fromRange();
|
|
vec2d off = random2d();
|
|
cur.position = pos + vec3d(off.x, 0, off.y);
|
|
cur.orbitAround(pos, radius);
|
|
if(pct >= 0)
|
|
cur.setOrbitPct(pct);
|
|
}
|
|
#section all
|
|
};
|
|
|
|
class AddRegionStatus : MapHook {
|
|
Document doc("Add a status that is given to all objects in the system it can apply to.");
|
|
Argument status(AT_Status, doc="Type of status to add to objects in the system.");
|
|
|
|
#section server
|
|
void trigger(SystemData@ data, SystemDesc@ system, Object@& current) const override {
|
|
system.object.addRegionStatus(null, status.integer);
|
|
}
|
|
#section all
|
|
};
|
|
|
|
class NoRegionVision : MapHook {
|
|
Document doc("Indicate that this system does not have automatic shared region vision.");
|
|
|
|
#section server
|
|
void trigger(SystemData@ data, SystemDesc@ system, Object@& current) const override {
|
|
system.donateVision = false;
|
|
}
|
|
#section all
|
|
};
|
|
|
|
//AddPlanetResource(<Resource> = Distributed)
|
|
// Add another resource to the previously generated object.
|
|
class AddPlanetResource : MapHook {
|
|
Document doc("Adds a single resource to the most recently created planet.");
|
|
Argument resID("Resource", AT_Custom, "distributed", doc="Type or types of resource to choose from.");
|
|
array<const ResourceType@> resPossib;
|
|
bool distribute = false;
|
|
|
|
bool instantiate() {
|
|
if(arguments[0].str.equals_nocase("distributed"))
|
|
distribute = true;
|
|
else if(!parseResourceSpec(resPossib, arguments[0].str))
|
|
return false;
|
|
return MapHook::instantiate();
|
|
}
|
|
|
|
#section server
|
|
void trigger(SystemData@ data, SystemDesc@ system, Object@& current) const override {
|
|
Object@ cur = current;
|
|
if(cur is null || !cur.hasResources)
|
|
return;
|
|
|
|
const ResourceType@ resource;
|
|
if(resPossib.length > 0) {
|
|
if(resPossib.length == 1)
|
|
@resource = resPossib[0];
|
|
else
|
|
@resource = resPossib[randomi(0, resPossib.length-1)];
|
|
}
|
|
if(data is null && distribute)
|
|
@resource = getDistributedResource();
|
|
if(cur.isAsteroid) {
|
|
return;
|
|
}
|
|
else {
|
|
if(resource !is null) {
|
|
markResourceUsed(resource);
|
|
cur.addResource(resource.id);
|
|
}
|
|
else if(distribute && data !is null)
|
|
data.distributedResources.insertLast(cur);
|
|
}
|
|
}
|
|
#section all
|
|
};
|
|
|
|
//AddResource(<Tile Resource>, <Amount>)
|
|
// Add an income to the previously generated object.
|
|
class AddResource : MapHook {
|
|
Document doc("Adds a basic resource income to the most recently created planet.");
|
|
Argument tileRes("Tile Resource", AT_TileResource, doc="Type of basic resource to produce.");
|
|
Argument rate("Rate", AT_Integer, "1", doc="Amount of generation to add.");
|
|
|
|
#section server
|
|
void trigger(SystemData@ data, SystemDesc@ system, Object@& current) const override {
|
|
Object@ cur = current;
|
|
if(cur is null || !cur.hasSurfaceComponent)
|
|
return;
|
|
cur.modResource(arguments[0].integer, +arguments[1].integer);
|
|
}
|
|
#section all
|
|
};
|
|
|
|
//NoNeedPopulationForLevel()
|
|
// Set the current planet to not need population to level up.
|
|
class NoNeedPopulationForLevel : MapHook {
|
|
#section server
|
|
void trigger(SystemData@ data, SystemDesc@ system, Object@& current) const override {
|
|
Object@ cur = current;
|
|
if(cur is null || !cur.hasSurfaceComponent)
|
|
return;
|
|
cur.setNeedsPopulationForLevel(false);
|
|
}
|
|
#section all
|
|
};
|
|
|
|
//Rename(<Name>)
|
|
// Rename the object.
|
|
class Rename : MapHook {
|
|
Document doc("Renames the most recently created object.");
|
|
Argument name("Name", AT_Locale, doc="Name for the object.");
|
|
|
|
#section server
|
|
void trigger(SystemData@ data, SystemDesc@ system, Object@& current) const override {
|
|
Object@ cur = current;
|
|
if(cur is null)
|
|
return;
|
|
cur.name = arguments[0].str;
|
|
}
|
|
#section all
|
|
};
|
|
|
|
//NameSystem(<Name>)
|
|
// Rename the system.
|
|
class NameSystem : MapHook {
|
|
Document doc("Renames the system.");
|
|
Argument name("Name", AT_Locale, doc="Name for the system.");
|
|
|
|
#section server
|
|
void trigger(SystemData@ data, SystemDesc@ system, Object@& current) const override {
|
|
system.object.name = arguments[0].str;
|
|
system.name = arguments[0].str;
|
|
}
|
|
#section all
|
|
};
|
|
|
|
//AddQuality(<Amount> = 100)
|
|
// Add extra quality to the system. Affects various distributions.
|
|
class AddQuality : MapHook {
|
|
Document doc("Adds quality to the system. Quality decides quantity and value of resources, anomalies, etc.");
|
|
Argument amt("Amount", AT_Range, doc="Quality to add. 100 is a typical improvement.");
|
|
|
|
#section server
|
|
void trigger(SystemData@ data, SystemDesc@ system, Object@& current) const override {
|
|
if(data !is null)
|
|
data.quality += arguments[0].fromRange();
|
|
}
|
|
#section all
|
|
};
|
|
|
|
//AddPopulation(<Amount>)
|
|
// Add <Amount> population to the previous planet.
|
|
class AddPopulation : MapHook {
|
|
Document doc("Adds population to the most recently created planet.");
|
|
Argument amt("Amount", AT_Range, doc="Population to add.");
|
|
|
|
#section server
|
|
void trigger(SystemData@ data, SystemDesc@ system, Object@& current) const override {
|
|
Object@ cur = current;
|
|
if(cur is null || !cur.hasSurfaceComponent)
|
|
return;
|
|
cur.addPopulation(arguments[0].fromRange());
|
|
}
|
|
#section all
|
|
};
|
|
|
|
//SpawnBuilding(<Type>, <Position>)
|
|
// Spawn a <Type> building on the previous planet at <Position>.
|
|
class SpawnBuilding : MapHook {
|
|
Document doc("Places a building on the most recently created planet.");
|
|
Argument type("Type", AT_Custom, doc="Building ID to place.");
|
|
Argument pos("Position", AT_Position2D, doc="Where to place the center of the building on the planet.");
|
|
Argument develop(AT_Boolean, "False", doc="Whether to mark all tiles its on as developed.");
|
|
const BuildingType@ bldType;
|
|
|
|
bool instantiate() {
|
|
@bldType = getBuildingType(arguments[0].str);
|
|
if(bldType is null) {
|
|
error(" Error: Could not find building type: '"+escape(arguments[0].str)+"'");
|
|
return false;
|
|
}
|
|
return MapHook::instantiate();
|
|
}
|
|
|
|
#section server
|
|
void trigger(SystemData@ data, SystemDesc@ system, Object@& current) const override {
|
|
Object@ cur = current;
|
|
if(cur is null || !cur.hasSurfaceComponent)
|
|
return;
|
|
cur.spawnBuilding(bldType.id, vec2i(arguments[1].fromPosition2D()), develop.boolean);
|
|
}
|
|
#section all
|
|
};
|
|
|
|
class ForceUsefulSurface : MapHook {
|
|
Document doc("Force the planet to have a percentage of useful region on its surface.");
|
|
Argument percent(AT_Decimal, doc="Percent of the surface that needs to be useful.");
|
|
Argument biome(AT_PlanetBiome, doc="Biome to fill the planet with to ensure surface.");
|
|
|
|
#section server
|
|
void trigger(SystemData@ data, SystemDesc@ system, Object@& current) const override {
|
|
Object@ cur = current;
|
|
if(cur is null || !cur.hasSurfaceComponent)
|
|
return;
|
|
auto@ type = getBiome(biome.str);
|
|
if(type !is null)
|
|
cur.forceUsefulSurface(percent.decimal, type.id);
|
|
else
|
|
print("No such biome: "+biome.str);
|
|
}
|
|
#section all
|
|
};
|
|
|
|
//MakeAsteroid(<Resource> = Distributed, <Distribution Chance> = 0.4)
|
|
// Create a new asteroid with the given resource available.
|
|
// If <Resource> is set to Distributed, distribute some random resources
|
|
// with <Distribution Chance>.
|
|
class MakeAsteroid : MapHook {
|
|
Document doc("Creates an asteroid in the system.");
|
|
Argument cargo(AT_Cargo, EMPTY_DEFAULT, doc="Type of cargo to create on the asteroid.");
|
|
Argument cargo_amount(AT_Range, "500:10000", doc="Amount of cargo for the asteroid to have.");
|
|
Argument resource(AT_Custom, EMPTY_DEFAULT, doc="Resource to put on the asteroid.");
|
|
Argument distribution_chance(AT_Decimal, "0.4", doc="For distributed resources, chance to add additional resource. Repeats until failure.");
|
|
|
|
#section server
|
|
array<const ResourceType@> resPossib;
|
|
bool distribute = false;
|
|
bool noResource = false;
|
|
|
|
bool instantiate() {
|
|
if(resource.str.length != 0) {
|
|
if(resource.str.equals_nocase("distributed"))
|
|
distribute = true;
|
|
else if(resource.str.equals_nocase("none"))
|
|
noResource = true;
|
|
else if(!parseResourceSpec(resPossib, resource.str))
|
|
return false;
|
|
}
|
|
return MapHook::instantiate();
|
|
}
|
|
|
|
void trigger(SystemData@ data, SystemDesc@ system, Object@& current) const override {
|
|
vec2d rpos;
|
|
if(data !is null)
|
|
rpos = random2d(150.0, system.radius * 1.5 - 250.0);
|
|
else
|
|
rpos = random2d(150.0, system.radius * 0.8);
|
|
vec3d pos = system.position + vec3d(rpos.x, randomd(-50.0, 50.0), rpos.y);
|
|
Asteroid@ roid = createAsteroid(pos, system.object, delay=true);
|
|
roid.orbitAround(system.position);
|
|
roid.orbitSpin(randomd(20.0, 60.0));
|
|
@current = roid;
|
|
|
|
if(noResource) {
|
|
roid.initMesh();
|
|
return;
|
|
}
|
|
|
|
double totChance = config::ASTEROID_OCCURANCE + config::RESOURCE_ASTEROID_OCCURANCE;
|
|
double resChance = config::RESOURCE_ASTEROID_OCCURANCE;
|
|
double roll = randomd(0, totChance);
|
|
|
|
int cargoType = cargo.integer;
|
|
if(cargoType == -1) {
|
|
auto@ ore = getCargoType("Ore");
|
|
if(ore !is null)
|
|
cargoType = ore.id;
|
|
}
|
|
|
|
bool cargoSpec = cargo.integer != -1 && config::ASTEROID_OCCURANCE != 0;
|
|
bool resSpec = (resPossib.length != 0 || distribute) && config::RESOURCE_ASTEROID_OCCURANCE != 0;
|
|
|
|
if((cargoSpec && !resSpec) || ((cargoSpec == resSpec) && roll >= resChance)) {
|
|
roid.addCargo(cargoType, cargo_amount.fromRange());
|
|
}
|
|
else {
|
|
if(distribute || resPossib.length == 0) {
|
|
do {
|
|
const ResourceType@ type = getDistributedAsteroidResource();
|
|
if(roid.getAvailableCostFor(type.id) < 0.0)
|
|
roid.addAvailable(type.id, type.asteroidCost);
|
|
}
|
|
while(randomd() < distribution_chance.decimal);
|
|
}
|
|
else {
|
|
const ResourceType@ resource;
|
|
if(resPossib.length > 0) {
|
|
if(resPossib.length == 1)
|
|
@resource = resPossib[0];
|
|
else
|
|
@resource = resPossib[randomi(0, resPossib.length-1)];
|
|
}
|
|
|
|
if(resource !is null) {
|
|
if(roid.getAvailableCostFor(resource.id) < 0.0)
|
|
roid.addAvailable(resource.id, resource.asteroidCost);
|
|
}
|
|
}
|
|
}
|
|
|
|
roid.initMesh();
|
|
}
|
|
#section all
|
|
};
|
|
|
|
class MakeAsteroidBelt : MapHook {
|
|
Document doc("Creates an asteroid belt in the system.");
|
|
Argument count("Count", AT_Integer, "8", doc="Number of asteroids in the belt.");
|
|
Argument cargo(AT_Cargo, "Ore", doc="Type of cargo to create on the asteroid belt.");
|
|
Argument cargo_amount(AT_Range, "500:10000", doc="Amount of cargo for the asteroids to have.");
|
|
Argument distribution_chance(AT_Decimal, "0.4", doc="For distributed resources, chance to add additional resource. Repeats until failure.");
|
|
|
|
bool instantiate() {
|
|
if(arguments[0].integer <= 0)
|
|
return false;
|
|
return MapHook::instantiate();
|
|
}
|
|
|
|
#section server
|
|
void trigger(SystemData@ data, SystemDesc@ system, Object@& current) const override {
|
|
if(config::ASTEROID_OCCURANCE == 0 && config::RESOURCE_ASTEROID_OCCURANCE == 0)
|
|
return;
|
|
double radius = randomd(0.5,1.5) * system.radius;
|
|
double angle = randomd(0, twopi);
|
|
double totChance = config::ASTEROID_OCCURANCE + config::RESOURCE_ASTEROID_OCCURANCE;
|
|
double resChance = config::RESOURCE_ASTEROID_OCCURANCE;
|
|
|
|
for(uint i = 0, cnt = arguments[0].integer; i < cnt; ++i) {
|
|
angle += twopi / double(cnt);
|
|
double ang = angle + randomd(-0.25,0.25) * twopi / double(cnt);
|
|
|
|
vec3d pos = system.position + vec3d(cos(ang) * radius, randomd(-50.0, 50.0), sin(ang) * radius);
|
|
|
|
Asteroid@ roid = createAsteroid(pos, system.object, delay=true);
|
|
roid.orbitAround(system.position);
|
|
roid.orbitSpin(randomd(20.0, 60.0));
|
|
@current = roid;
|
|
|
|
if(randomd(0, totChance) >= resChance && cargo.integer != -1) {
|
|
roid.addCargo(cargo.integer, cargo_amount.fromRange());
|
|
}
|
|
else {
|
|
do {
|
|
const ResourceType@ type = getDistributedAsteroidResource();
|
|
if(roid.getAvailableCostFor(type.id) < 0.0)
|
|
roid.addAvailable(type.id, type.asteroidCost);
|
|
}
|
|
while(randomd() < distribution_chance.decimal);
|
|
}
|
|
|
|
roid.initMesh();
|
|
}
|
|
}
|
|
#section all
|
|
};
|
|
|
|
//MakeAnomaly(<Type> = Distributed)
|
|
// Generate a anomaly field in this system of type <Type>.
|
|
class MakeAnomaly : MapHook {
|
|
Document doc("Creates an anomaly in the system.");
|
|
Argument anomType("Type", AT_Custom, "distributed", doc="Type of anomaly to create.");
|
|
const AnomalyType@ type;
|
|
|
|
bool instantiate() {
|
|
if(!arguments[0].str.equals_nocase("distributed")) {
|
|
@type = getAnomalyType(arguments[0].str);
|
|
if(type is null) {
|
|
error(" Error: Could not find anomaly type: "+arguments[0].str);
|
|
return false;
|
|
}
|
|
}
|
|
return MapHook::instantiate();
|
|
}
|
|
|
|
#section server
|
|
void trigger(SystemData@ data, SystemDesc@ system, Object@& current) const override {
|
|
const AnomalyType@ gen = type;
|
|
if(gen is null)
|
|
@gen = getDistributedAnomalyType();
|
|
|
|
vec2d rpos = random2d(150.0, system.radius - 250.0);
|
|
vec3d pos = system.position + vec3d(rpos.x, randomd(-50.0, 50.0), rpos.y);
|
|
Anomaly@ anomaly = createAnomaly(pos, gen.id);
|
|
@current = anomaly;
|
|
}
|
|
#section all
|
|
};
|
|
|
|
class SpawnRandomRemnants : MapHook {
|
|
Document doc("Spawn a remnant fleet with a random design at a particular size.");
|
|
Argument size(AT_Integer, doc="Base size of the remnant flagship.");
|
|
Argument occupation(AT_Range, "0.8", doc="Percentage of support that is filled.");
|
|
Argument gametime_size(AT_Range, "0", doc="If spawned after game start, increase the flagship size by this much per minute of game time.");
|
|
Argument offset(AT_Decimal, "0", doc="Minimum offset from the edge of the system to the fleet.");
|
|
|
|
#section server
|
|
void postTrigger(SystemData@ data, SystemDesc@ system, Object@& current) const override {
|
|
int sz = size.integer;
|
|
sz += round(gametime_size.fromRange() * gameTime / 60.0);
|
|
|
|
vec2d campPos = random2d(200.0, (system.radius - offset.decimal) * 0.95);
|
|
vec3d pos = system.position + vec3d(campPos.x, 0, campPos.y);
|
|
|
|
spawnRemnantFleet(pos, sz, occupation.fromRange());
|
|
}
|
|
#section all
|
|
}
|
|
|
|
//MakeCreepCamp(<Type> = Distributed)
|
|
// Generate a creep camp in this system of type <Type>.
|
|
class MakeCreepCamp : MapHook {
|
|
Document doc("Creates a creep camp in the system.");
|
|
Argument campID("Type", AT_Custom, "distributed", doc="Type of camp to create.");
|
|
Argument offset(AT_Decimal, "0", doc="Minimum offset from the edge of the system to the camp.");
|
|
const CampType@ campType;
|
|
|
|
MakeCreepCamp() {
|
|
argument("Type", AT_Custom, "distributed");
|
|
}
|
|
|
|
bool instantiate() {
|
|
if(!arguments[0].str.equals_nocase("distributed")) {
|
|
@campType = getCreepCamp(arguments[0].str);
|
|
if(campType is null) {
|
|
error(" Error: Could not find creep camp type: '"+escape(arguments[0].str)+"'");
|
|
return false;
|
|
}
|
|
}
|
|
return MapHook::instantiate();
|
|
}
|
|
|
|
#section server
|
|
void postTrigger(SystemData@ data, SystemDesc@ system, Object@& current) const override {
|
|
if(config::REMNANT_OCCURANCE == 0.0)
|
|
return;
|
|
|
|
const CampType@ type = campType;
|
|
if(type is null)
|
|
@type = getDistributedCreepCamp();
|
|
|
|
vec2d campPos = random2d(200.0, (system.radius - offset.decimal) * 0.95);
|
|
vec3d pos = system.position + vec3d(campPos.x, 0, campPos.y);
|
|
|
|
makeCreepCamp(pos, type, system.object);
|
|
}
|
|
#section all
|
|
};
|
|
|
|
#section server
|
|
void makeCreepCamp(const vec3d& pos, const CampType@ type, Region@ region = null) {
|
|
//Create pickup
|
|
Pickup@ pickup;
|
|
double roll = randomd(0.0, type.pickupFrequency);
|
|
for(uint i = 0, cnt = type.pickups.length; i < cnt; ++i) {
|
|
const PickupType@ ptype = type.pickups[i];
|
|
if(roll <= ptype.frequency) {
|
|
double minRad = 20.0 + ptype.physicalSize * 2.0;
|
|
double maxRad = 40.0 + ptype.physicalSize * 4.0;
|
|
vec2d off = random2d(minRad, maxRad);
|
|
|
|
@pickup = createPickup(pos + vec3d(off.x, randomd(5.0, 10.0), off.y), ptype.id, defaultEmpire);
|
|
pickup.finalizeCreation();
|
|
pickup.setCampType(type.id);
|
|
|
|
if(region !is null) {
|
|
@pickup.region = region;
|
|
region.enterRegion(pickup);
|
|
}
|
|
break;
|
|
}
|
|
else {
|
|
roll -= ptype.frequency;
|
|
}
|
|
}
|
|
|
|
//Add statuses to region
|
|
if(region !is null) {
|
|
for(uint i = 0, cnt = type.region_statuses.length; i < cnt; ++i)
|
|
region.addRegionStatus(null, type.region_statuses[i].id);
|
|
}
|
|
|
|
//Create defenders
|
|
Object@ lastLeader;
|
|
for(uint i = 0, cnt = type.ships.length; i < cnt; ++i) {
|
|
const Design@ dsg = type.ships[i];
|
|
uint minCnt = type.shipMins[i];
|
|
uint maxCnt = type.shipMaxes[i];
|
|
int amt = randomi(minCnt, maxCnt);
|
|
|
|
if(!dsg.hull.hasTag(ST_IsSupport)) {
|
|
for(; amt > 0; --amt) {
|
|
vec3d leaderPos = pos;
|
|
if(lastLeader !is null)
|
|
leaderPos += random3d(10.0, 40.0);
|
|
|
|
@lastLeader = createShip(leaderPos, dsg, Creeps, free=true, memorable=true);
|
|
lastLeader.setAutoMode(config::REMNANT_AGGRESSION == 0 ? AM_HoldPosition : AM_RegionBound);
|
|
lastLeader.sightRange = 0;
|
|
lastLeader.setRotation(quaterniond_fromAxisAngle(vec3d_up(), randomd(-pi, pi)));
|
|
|
|
for(uint i = 0, cnt = type.statuses.length; i < cnt; ++i)
|
|
lastLeader.addStatus(type.statuses[i].id);
|
|
|
|
if(type.shipNames[i].length != 0) {
|
|
lastLeader.name = type.shipNames[i];
|
|
lastLeader.named = true;
|
|
}
|
|
|
|
pickup.addPickupProtector(lastLeader);
|
|
}
|
|
}
|
|
else {
|
|
for(; amt > 0; --amt)
|
|
createShip(lastLeader.position, dsg, Creeps, lastLeader, free=true);
|
|
}
|
|
}
|
|
|
|
//Create randomized defenders
|
|
if(type.flagshipSize > 0) {
|
|
vec3d leaderPos = pos;
|
|
if(lastLeader !is null)
|
|
leaderPos += random3d(10.0, 40.0);
|
|
@lastLeader = spawnRemnantFleet(leaderPos, type.flagshipSize, type.supportOccupation);
|
|
lastLeader.setAutoMode(config::REMNANT_AGGRESSION == 0 ? AM_HoldPosition : AM_RegionBound);
|
|
|
|
for(uint i = 0, cnt = type.statuses.length; i < cnt; ++i)
|
|
lastLeader.addStatus(type.statuses[i].id);
|
|
pickup.addPickupProtector(lastLeader);
|
|
}
|
|
if(type.targetStrength > 0) {
|
|
vec3d leaderPos = pos;
|
|
if(lastLeader !is null)
|
|
leaderPos += random3d(10.0, 40.0);
|
|
@lastLeader = spawnRemnantFleet(leaderPos, composeRemnantFleet(type.targetStrength));
|
|
lastLeader.setAutoMode(config::REMNANT_AGGRESSION == 0 ? AM_HoldPosition : AM_RegionBound);
|
|
|
|
for(uint i = 0, cnt = type.statuses.length; i < cnt; ++i)
|
|
lastLeader.addStatus(type.statuses[i].id);
|
|
pickup.addPickupProtector(lastLeader);
|
|
}
|
|
}
|
|
#section all
|
|
|
|
class SetStaticSeeableRange : MapHook {
|
|
Document doc("Set the seeable range of static objects (anomalies, asteroids, etc) in this system.");
|
|
Argument range(AT_Decimal, doc="Seeable range to set.");
|
|
|
|
#section server
|
|
void postTrigger(SystemData@ data, SystemDesc@ system, Object@& current) const override {
|
|
for(uint i = 0, cnt = system.object.objectCount; i < cnt; ++i) {
|
|
Object@ obj = system.object.objects[i];
|
|
if(obj.hasStatuses)
|
|
continue;
|
|
obj.seeableRange = range.decimal;
|
|
}
|
|
}
|
|
#section all
|
|
};
|
|
|
|
//MakeAdjacentCreepCamp(<Type> = Distributed, <Place Far> = False)
|
|
// Generate a creep camp in an adjacent system of type <Type>.
|
|
class MakeAdjacentCreepCamp : MapHook {
|
|
Document doc("Creates a creep camp in an adjacent system.");
|
|
Argument campID("Type", AT_Custom, "distributed", doc="Type of camp to create.");
|
|
Argument placeFar("Place Far", AT_Boolean, "True", doc="Whether the camp should be pushed to the edge of the system.");
|
|
const CampType@ campType;
|
|
|
|
bool instantiate() {
|
|
if(!arguments[0].str.equals_nocase("distributed")) {
|
|
@campType = getCreepCamp(arguments[0].str);
|
|
if(campType is null) {
|
|
error(" Error: Could not find creep camp type: '"+escape(arguments[0].str)+"'");
|
|
return false;
|
|
}
|
|
}
|
|
return MapHook::instantiate();
|
|
}
|
|
|
|
#section server
|
|
void postTrigger(SystemData@ data, SystemDesc@ system, Object@& current) const override {
|
|
if(data !is null && data.ignoreAdjacencies)
|
|
return;
|
|
if(config::REMNANT_OCCURANCE == 0.0)
|
|
return;
|
|
const CampType@ type = campType;
|
|
if(type is null)
|
|
@type = getDistributedCreepCamp();
|
|
|
|
if(system.adjacent.length == 0)
|
|
return;
|
|
int at = randomi(0, system.adjacent.length-1);
|
|
SystemDesc@ other = getSystem(system.adjacent[at]);
|
|
|
|
vec2d campPos = random2d(200.0, other.radius);
|
|
if(arguments[1].boolean) {
|
|
vec3d off = (other.position - system.position).normalized(other.radius * 0.8);
|
|
campPos = vec2d(off.x, off.z);
|
|
}
|
|
|
|
vec3d pos = other.position + vec3d(campPos.x, 0, campPos.y);
|
|
|
|
makeCreepCamp(pos, type);
|
|
}
|
|
#section all
|
|
};
|
|
|
|
//MakeArtifact(<Type> = Distributed)
|
|
// Generate a anomaly field in this system of type <Type>.
|
|
class MakeArtifact : MapHook {
|
|
Document doc("Creates an artifact in the system.");
|
|
Argument artifactType("Type", AT_Custom, "distributed", doc="Type of artifact to create.");
|
|
const ArtifactType@ type;
|
|
|
|
bool instantiate() {
|
|
if(!arguments[0].str.equals_nocase("distributed")) {
|
|
@type = getArtifactType(arguments[0].str);
|
|
if(type is null) {
|
|
error(" Error: Could not find artifact type: "+arguments[0].str);
|
|
return false;
|
|
}
|
|
}
|
|
return MapHook::instantiate();
|
|
}
|
|
|
|
#section server
|
|
void trigger(SystemData@ data, SystemDesc@ system, Object@& current) const override {
|
|
if(data !is null)
|
|
data.artifacts += 1;
|
|
@current = makeArtifact(system, type is null ? getDistributedArtifactType(system.contestation).id : type.id);
|
|
}
|
|
#section all
|
|
};
|
|
|
|
#section server
|
|
Artifact@ makeArtifact(SystemDesc@ system, uint type = uint(-1)) {
|
|
const ArtifactType@ gen;
|
|
if(type == uint(-1))
|
|
@gen = getDistributedArtifactType();
|
|
else
|
|
@gen = getArtifactType(type);
|
|
vec2d rpos = random2d(150.0, system.radius - 250.0);
|
|
vec3d pos = system.position + vec3d(rpos.x, randomd(-50.0, 50.0), rpos.y);
|
|
Artifact@ obj = createArtifact(pos, gen, system.object);
|
|
obj.orbitAround(system.position);
|
|
@obj.region = system.object;
|
|
system.object.enterRegion(obj);
|
|
return obj;
|
|
}
|
|
#section all
|
|
|
|
//RepeatHomeworlds()
|
|
// Repeats the block for every homeworld in this system, and sets
|
|
// the object to the appropriate empire's homeworld.
|
|
class RepeatHomeworlds : BlockEffect {
|
|
#section server
|
|
void trigger(SystemData@ data, SystemDesc@ system, Object@& current) const {
|
|
if(data.homeworlds is null)
|
|
return;
|
|
for(uint n = 0, ncnt = data.homeworlds.length; n < ncnt; ++n) {
|
|
Empire@ emp = data.homeworlds[n];
|
|
for(uint i = 0, cnt = inner.length; i < cnt; ++i) {
|
|
auto@ cur = cast<IMapHook@>(inner[i]);
|
|
if(cur !is null)
|
|
cur.trigger(data, system, current);
|
|
}
|
|
|
|
Object@ cur = current;
|
|
Planet@ pl = cast<Planet>(cur);
|
|
if(pl !is null) {
|
|
@pl.owner = emp;
|
|
@emp.Homeworld = pl;
|
|
}
|
|
}
|
|
}
|
|
#section all
|
|
};
|
|
|
|
//AddAdjacentAnomalies(<Amount>)
|
|
// Add <Amount> anomaly to random adjacent systems.
|
|
class AddAdjacentAnomalies : MapHook {
|
|
Document doc("Creates anomalies in adjacent systems.");
|
|
Argument count("Count", AT_Range, doc="Number to create.");
|
|
MakeAnomaly maker;
|
|
|
|
#section server
|
|
void postTrigger(SystemData@ data, SystemDesc@ system, Object@& current) const override {
|
|
if(system.adjacent.length == 0)
|
|
return;
|
|
if(data !is null && data.ignoreAdjacencies)
|
|
return;
|
|
if(config::ANOMALY_OCCURANCE == 0.0)
|
|
return;
|
|
|
|
uint cnt = arguments[0].fromRange();
|
|
for(uint i = 0; i < cnt; ++i) {
|
|
int at = randomi(0, system.adjacent.length-1);
|
|
SystemDesc@ other = getSystem(system.adjacent[at]);
|
|
maker.trigger(null, other, current);
|
|
}
|
|
}
|
|
#section all
|
|
};
|
|
|
|
//AddAdjacentArtifacts(<Amount>)
|
|
// Add <Amount> anomaly to random adjacent systems.
|
|
class AddAdjacentArtifacts : MapHook {
|
|
Document doc("Creates artifacts in adjacent systems.");
|
|
Argument count("Count", AT_Range, doc="Number to create.");
|
|
MakeArtifact maker;
|
|
|
|
#section server
|
|
void postTrigger(SystemData@ data, SystemDesc@ system, Object@& current) const override {
|
|
if(system.adjacent.length == 0)
|
|
return;
|
|
if(data !is null && data.ignoreAdjacencies)
|
|
return;
|
|
|
|
uint cnt = arguments[0].fromRange();
|
|
for(uint i = 0; i < cnt; ++i) {
|
|
int at = randomi(0, system.adjacent.length-1);
|
|
SystemDesc@ other = getSystem(system.adjacent[at]);
|
|
maker.trigger(null, other, current);
|
|
}
|
|
}
|
|
#section all
|
|
};
|
|
|
|
//GuaranteeAdjacentResource(<Resource>, <Distance> = 1)
|
|
// Guarantee an adjacent resource out of a type spec.
|
|
class GuaranteeAdjacentResource : MapHook {
|
|
Document doc("Attempts to guarantee that specific reosurces will be available in adjacent systems. Logs on failure.");
|
|
Argument res("Resource", AT_Custom, doc="Type(s) of resource to guarantee. Only one will be picked.");
|
|
Argument count("Distance", AT_Integer, "1", doc="System hop distance within which to allow the resource.");
|
|
array<const ResourceType@> resPossib;
|
|
|
|
bool instantiate() {
|
|
if(!parseResourceSpec(resPossib, arguments[0].str))
|
|
return false;
|
|
return MapHook::instantiate();
|
|
}
|
|
|
|
#section server
|
|
void add(array<Object@>& objects, array<SystemData@>& sources, SystemData@ data, int distance) {
|
|
for(uint i = 0, cnt = data.adjacentData.length; i < cnt; ++i) {
|
|
auto@ other = data.adjacentData[i];
|
|
for(uint n = 0, ncnt = other.distributedResources.length; n < ncnt; ++n) {
|
|
Planet@ pl = cast<Planet>(other.distributedResources[n]);
|
|
if(pl !is null) {
|
|
objects.insertLast(pl);
|
|
sources.insertLast(other);
|
|
}
|
|
}
|
|
if(distance > 1)
|
|
add(objects, sources, other, distance-1);
|
|
}
|
|
}
|
|
|
|
void postTrigger(SystemData@ data, SystemDesc@ system, Object@& current) const override {
|
|
if(data is null)
|
|
return;
|
|
if(data.ignoreAdjacencies)
|
|
return;
|
|
|
|
const ResourceType@ resource;
|
|
if(resPossib.length > 0) {
|
|
if(resPossib.length == 1)
|
|
@resource = resPossib[0];
|
|
else
|
|
@resource = resPossib[randomi(0, resPossib.length-1)];
|
|
}
|
|
if(resource is null)
|
|
return;
|
|
|
|
array<Object@> objects;
|
|
array<SystemData@> sources;
|
|
add(objects, sources, data, arguments[1].integer);
|
|
|
|
if(objects.length != 0) {
|
|
uint i = randomi(0, objects.length-1);
|
|
Object@ target = objects[i];
|
|
sources[i].distributedResources.remove(target);
|
|
|
|
target.addResource(resource.id);
|
|
}
|
|
else {
|
|
warn("WARNING: Map guarantee '"+arguments[0].str+"' could not find planet around system '"+system.name+"'.");
|
|
}
|
|
}
|
|
#section all
|
|
};
|
|
|
|
class OnPlanet : MapHook {
|
|
GenericEffect@ eff;
|
|
Document doc("Run the passed hook as a single-time planet effect hook on the object. Because it only runs once, hooks with ticks or data storage will not work!");
|
|
Argument hook(AT_Custom, doc="Hook to run.");
|
|
|
|
bool instantiate() override {
|
|
@eff = cast<GenericEffect>(parseHook(hook.str, "planet_effects::"));
|
|
if(eff is null) {
|
|
error("GenericEffect(): could not find inner hook: "+escape(hook.str));
|
|
return false;
|
|
}
|
|
return MapHook::instantiate();
|
|
}
|
|
|
|
#section server
|
|
void trigger(SystemData@ data, SystemDesc@ system, Object@& current) const override {
|
|
Object@ c = current;
|
|
if(c !is null)
|
|
eff.enable(c, null);
|
|
}
|
|
#section all
|
|
};
|
|
|
|
class Trigger : MapHook {
|
|
BonusEffect@ eff;
|
|
Document doc("Run the specified hook on the current object.");
|
|
Argument hook(AT_Custom, doc="Hook to run.");
|
|
|
|
bool instantiate() override {
|
|
@eff = cast<BonusEffect>(parseHook(hook.str, "bonus_effects::"));
|
|
if(eff is null) {
|
|
error("Trigger(): could not find inner hook: "+escape(hook.str));
|
|
return false;
|
|
}
|
|
return MapHook::instantiate();
|
|
}
|
|
|
|
#section server
|
|
void trigger(SystemData@ data, SystemDesc@ system, Object@& current) const override {
|
|
Object@ c = current;
|
|
if(c is null)
|
|
eff.activate(c, null);
|
|
}
|
|
#section all
|
|
};
|
|
|
|
class TriggerRegion : MapHook {
|
|
BonusEffect@ eff;
|
|
Document doc("Run the specified hook on the current region.");
|
|
Argument hook(AT_Custom, doc="Hook to run.");
|
|
|
|
bool instantiate() override {
|
|
@eff = cast<BonusEffect>(parseHook(hook.str, "bonus_effects::"));
|
|
if(eff is null) {
|
|
error("TriggerRegion(): could not find inner hook: "+escape(hook.str));
|
|
return false;
|
|
}
|
|
return MapHook::instantiate();
|
|
}
|
|
|
|
#section server
|
|
void trigger(SystemData@ data, SystemDesc@ system, Object@& current) const override {
|
|
if(system !is null)
|
|
eff.activate(system.object, null);
|
|
}
|
|
#section all
|
|
};
|
|
|
|
//Copy a region during map generation
|
|
void mapCopyRegion(SystemDesc@ from, SystemDesc@ to, uint typeMask = ~0) {
|
|
#section server
|
|
Region@ reg = from.object;
|
|
reg.wait();
|
|
|
|
Object@ current;
|
|
|
|
MakePlanet plHook;
|
|
plHook.initClass();
|
|
plHook.instantiate();
|
|
plHook.distribute = false;
|
|
plHook.resPossib.length = 1;
|
|
|
|
MakeAsteroid roidHook;
|
|
roidHook.noResource = true;
|
|
roidHook.initClass();
|
|
roidHook.instantiate();
|
|
|
|
MakeStar starHook;
|
|
starHook.initClass();
|
|
starHook.instantiate();
|
|
|
|
MakeAnomaly anomalyHook;
|
|
anomalyHook.initClass();
|
|
anomalyHook.instantiate();
|
|
|
|
MakeArtifact artifactHook;
|
|
artifactHook.initClass();
|
|
artifactHook.instantiate();
|
|
|
|
//Mirror region properties
|
|
to.donateVision = from.donateVision;
|
|
from.object.mirrorRegionStatusTo(to.object);
|
|
|
|
//Copy
|
|
for(uint i = 0, cnt = reg.objectCount; i < cnt; ++i) {
|
|
Object@ obj = reg.objects[i];
|
|
|
|
if(typeMask & (1<<obj.type) == 0)
|
|
continue;
|
|
|
|
vec3d destPos = obj.position - from.position;
|
|
destPos.z = -destPos.z;
|
|
destPos += to.position;
|
|
|
|
if(obj.isPlanet) {
|
|
obj.wait();
|
|
|
|
@plHook.resPossib[0] = getResource(obj.primaryResourceType);
|
|
plHook.radius.set(obj.radius);
|
|
plHook.grid_size.set(vec2d(obj.surfaceGridSize));
|
|
|
|
plHook.trigger(null, to, current);
|
|
|
|
Planet@ pl = cast<Planet>(current);
|
|
pl.orbitAround(destPos, to.position);
|
|
to.radius = from.radius;
|
|
|
|
if(pl.statusInstanceCount != 0 || obj.statusInstanceCount != 0) {
|
|
for(uint j = 0, jcnt = pl.statusInstanceCount; j < jcnt; ++j)
|
|
pl.removeStatus(pl.statusInstanceId[j]);
|
|
for(uint j = 0, jcnt = obj.statusInstanceCount; j < jcnt; ++j)
|
|
pl.addStatus(obj.statusInstanceType[j]);
|
|
}
|
|
|
|
pl.mirrorSurfaceFrom(obj);
|
|
}
|
|
else if(obj.isAsteroid) {
|
|
Asteroid@ base = cast<Asteroid>(obj);
|
|
base.wait();
|
|
|
|
roidHook.trigger(null, to, current);
|
|
|
|
Asteroid@ roid = cast<Asteroid>(current);
|
|
roid.orbitAround(destPos, to.position);
|
|
|
|
for(uint i = 0, cnt = base.getAvailableCount(); i < cnt; ++i)
|
|
roid.addAvailable(base.getAvailable(i), base.getAvailableCost(i));
|
|
|
|
for(uint i = 0, cnt = base.cargoTypes; i < cnt; ++i)
|
|
roid.addCargo(base.cargoType[i], base.getCargoStored(base.cargoType[i]));
|
|
}
|
|
else if(obj.isStar) {
|
|
Star@ base = cast<Star>(obj);
|
|
|
|
starHook.arguments[0].set(base.temperature);
|
|
starHook.arguments[1].set(base.radius);
|
|
starHook.arguments[2].set(destPos - to.position);
|
|
|
|
starHook.trigger(null, to, current);
|
|
}
|
|
else if(obj.isArtifact) {
|
|
Artifact@ base = cast<Artifact>(obj);
|
|
|
|
@artifactHook.type = getArtifactType(base.ArtifactType);
|
|
artifactHook.trigger(null, to, current);
|
|
|
|
Artifact@ artifact = cast<Artifact>(current);
|
|
artifact.orbitAround(destPos, to.position);
|
|
}
|
|
else if(obj.isAnomaly) {
|
|
Anomaly@ base = cast<Anomaly>(obj);
|
|
|
|
@anomalyHook.type = getAnomalyType(base.anomalyType);
|
|
|
|
anomalyHook.trigger(null, to, current);
|
|
|
|
Anomaly@ anomaly = cast<Anomaly>(current);
|
|
anomaly.position = destPos;
|
|
anomaly.clearOptions();
|
|
for(uint i = 0, cnt = base.optionCount; i < cnt; ++i)
|
|
anomaly.addOption(base.option[i]);
|
|
}
|
|
else if(obj.isPickup) {
|
|
Pickup@ base = cast<Pickup>(obj);
|
|
|
|
Pickup@ pickup = createPickup(destPos, base.PickupType, defaultEmpire);
|
|
pickup.finalizeCreation();
|
|
|
|
Ship@ baseProt = cast<Ship>(base.getProtector());
|
|
if(baseProt !is null) {
|
|
baseProt.wait();
|
|
|
|
vec3d leadPos = baseProt.position - from.position;
|
|
leadPos.z = -leadPos.z;
|
|
leadPos += to.position;
|
|
|
|
Ship@ leader = createShip(leadPos,
|
|
baseProt.blueprint.design, Creeps, free=true, memorable=true);
|
|
leader.name = baseProt.name;
|
|
leader.named = baseProt.named;
|
|
leader.setAutoMode(config::REMNANT_AGGRESSION == 0 ? AM_HoldPosition : AM_RegionBound);
|
|
leader.sightRange = 0;
|
|
|
|
pickup.addPickupProtector(leader);
|
|
|
|
for(uint n = 0, ncnt = baseProt.supportCount; n < ncnt; ++n) {
|
|
Ship@ supp = cast<Ship>(baseProt.supportShip[n]);
|
|
if(supp !is null)
|
|
createShip(leadPos, supp.blueprint.design, Creeps, leader, free=true);
|
|
}
|
|
}
|
|
}
|
|
else if(obj.isOddity) {
|
|
Oddity@ odd = cast<Oddity>(obj);
|
|
if(odd.isGate())
|
|
continue;
|
|
|
|
ObjectDesc desc;
|
|
desc.type = OT_Oddity;
|
|
desc.radius = odd.radius;
|
|
desc.position = destPos;
|
|
desc.flags |= objNoPhysics;
|
|
desc.flags |= objNoDamage;
|
|
|
|
auto@ mirr = Oddity(desc);
|
|
mirr.noCollide = true;
|
|
mirr.makeVisuals(odd.getVisualType(), color=odd.getVisualColor());
|
|
}
|
|
}
|
|
#section all
|
|
}
|