Open source Star Ruler 2 source code!

This commit is contained in:
Lucas de Vries
2018-07-17 14:15:37 +02:00
commit cc307720ff
4342 changed files with 2365070 additions and 0 deletions
@@ -0,0 +1,55 @@
import construction.Constructible;
import resources;
tidy class AsteroidConstructible : Constructible {
Asteroid@ asteroid;
const ResourceType@ resource;
AsteroidConstructible(Object& obj, Asteroid@ a, const ResourceType@ r, double cost) {
@asteroid = a;
@resource = r;
totalLabor = cost;
}
AsteroidConstructible(SaveFile& file) {
Constructible::load(file);
uint rid = file.readIdentifier(SI_Resource);
@resource = getResource(rid);
file >> asteroid;
}
void save(SaveFile& file) {
Constructible::save(file);
file.writeIdentifier(SI_Resource, resource.id);
file << asteroid;
}
ConstructibleType get_type() {
return CT_Asteroid;
}
string get_name() {
return format(locale::BUILD_ASTEROID, resource.name);
}
TickResult tick(Object& obj, double time) override {
if(!asteroid.valid)
return TR_Remove;
if(!asteroid.canDevelop(obj.owner))
return TR_Remove;
return TR_UsedLabor;
}
void complete(Object& obj) {
asteroid.setup(obj, obj.owner, resource.id);
}
void write(Message& msg) {
Constructible::write(msg);
msg << resource.id;
}
bool repeat(Object& obj) {
return false;
}
};
@@ -0,0 +1,88 @@
import construction.Constructible;
import buildings;
import saving;
tidy class BuildingConstructible : Constructible {
vec2i position;
const BuildingType@ building;
bool isTimed = false;
double timeProgress = 0;
BuildingConstructible(Object& obj, vec2i pos, const BuildingType@ type) {
position = pos;
@building = type;
totalLabor = type.laborCost;
if(totalLabor == 0) {
totalLabor = max(type.buildTime, 1.0) / obj.buildingConstructRate / obj.owner.BuildingConstructRate / obj.owner.ImperialBldConstructionRate;
isTimed = true;
}
}
BuildingConstructible(SaveFile& file) {
Constructible::load(file);
uint id = file.readIdentifier(SI_Building);
@building = getBuildingType(id);
file >> position;
if(file >= SV_0149) {
file >> isTimed;
file >> timeProgress;
}
}
void save(SaveFile& file) {
Constructible::save(file);
file.writeIdentifier(SI_Building, building.id);
file << position;
file << isTimed;
file << timeProgress;
}
ConstructibleType get_type() {
return CT_Building;
}
bool get_canComplete() {
return !isTimed || timeProgress >= totalLabor;
}
string get_name() {
return building.name;
}
TickResult tick(Object& obj, double time) override {
if(!building.canProgress(obj))
return TR_UnusedLabor;
if(isTimed) {
timeProgress += time;
curLabor = timeProgress;
}
double progress = curLabor / max(totalLabor, 0.001);
obj.setBuildingCompletion(position.x, position.y, progress);
return isTimed ? TR_VanishLabor : TR_UsedLabor;
}
void cancel(Object& obj) {
Constructible::cancel(obj);
vec2i pos = position;
position = vec2i(-1, -1);
obj.forceDestroyBuilding(pos);
}
void complete(Object& obj) {
obj.setBuildingCompletion(position.x, position.y, 1.f);
}
void write(Message& msg) {
Constructible::write(msg);
msg << building.id;
msg << isTimed;
}
bool repeat(Object& obj) {
return false;
}
};
@@ -0,0 +1,130 @@
from constructible import ConstructibleType;
from saving import SaveVersion;
from resources import MoneyType;
enum TickResult {
TR_Remove, //Remove this constructible from the queue
TR_UsedLabor, //Mark as using labor, use it
TR_VanishLabor, //Don't mark as using labor, but use it
TR_UnusedLabor, //Don't mark as using labor, pass labor through
};
tidy class Constructible : Serializable {
int id = -1;
double curLabor = 0;
double totalLabor = 1.0;
int buildCost = 0;
int maintainCost = 0;
int budgetCycle = -1;
bool started = false;
bool paid = false;
bool repeated = false;
string get_name() {
return "(null)";
}
ConstructibleType get_type() {
return CT_Invalid;
}
bool get_canComplete() {
return true;
}
bool pay(Object& obj) {
if(buildCost != 0) {
budgetCycle = obj.owner.consumeBudget(buildCost, borrow=!repeated);
if(budgetCycle == -1)
return false;
}
paid = true;
return true;
}
bool start(Object& obj) {
if(started)
return true;
if(maintainCost != 0)
obj.owner.modMaintenance(maintainCost, MoT_Construction);
started = true;
return true;
}
void remove(Object& obj) {
if(started) {
started = false;
if(maintainCost != 0)
obj.owner.modMaintenance(-maintainCost, MoT_Construction);
}
}
void move(Object& obj, uint toPosition) {
}
TickResult tick(Object& obj, double time) {
return TR_UsedLabor;
}
void cancel(Object& obj) {
if(buildCost != 0)
obj.owner.refundBudget(buildCost, budgetCycle);
}
void complete(Object& obj) {
}
void read(Message& msg) {
throw("Attempting to read into a server constructible.");
}
bool repeat(Object& obj) {
budgetCycle = -1;
curLabor = 0;
paid = false;
started = false;
repeated = true;
return true;
}
void write(Message& msg) {
uint8 tp = type;
msg << tp;
msg << id;
msg << started;
msg << curLabor;
msg << totalLabor;
msg << maintainCost;
msg << buildCost;
}
void save(SaveFile& msg) {
uint8 tp = type;
msg << tp;
msg << id;
msg << started;
msg << curLabor;
msg << totalLabor;
msg << maintainCost;
msg << buildCost;
msg << paid;
msg << repeated;
}
void load(SaveFile& msg) {
msg >> id;
msg >> started;
msg >> curLabor;
msg >> totalLabor;
msg >> maintainCost;
msg >> buildCost;
if(msg >= SV_0117)
msg >> paid;
else
paid = true;
if(msg >= SV_0118)
msg >> repeated;
else
repeated = false;
}
};
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,118 @@
import construction.Constructible;
import constructions;
import resources;
tidy class ConstructionConstructible : Constructible {
Construction@ cons;
bool isTimed = false;
double timeProgress = 0;
ConstructionConstructible(Object& obj, const ConstructionType@ type, const Targets@ targs) {
@cons = Construction(type);
cons.targets = targs;
@cons.obj = obj;
buildCost = type.getBuildCost(obj, targs);
maintainCost = type.getMaintainCost(obj, targs);
totalLabor = type.getLaborCost(obj, targs);
if(totalLabor == 0) {
totalLabor = max(type.getTimeCost(obj, targs), 1.0);
isTimed = true;
}
}
bool get_canComplete() {
return !isTimed || timeProgress >= totalLabor;
}
bool repeat(Object& obj) {
if(!Constructible::repeat(obj))
return false;
if(!cons.type.canBuild(obj, cons.targets))
return false;
buildCost = cons.type.getBuildCost(obj, cons.targets);
maintainCost = cons.type.getMaintainCost(obj, cons.targets);
totalLabor = cons.type.getLaborCost(obj, cons.targets);
if(totalLabor == 0) {
totalLabor = max(cons.type.getTimeCost(obj, cons.targets), 1.0);
isTimed = true;
}
else {
isTimed = false;
}
timeProgress = 0;
return true;
}
bool pay(Object& obj) {
for(uint i = 0, cnt = cons.type.hooks.length; i < cnt; ++i) {
if(!cons.type.hooks[i].consume(cons, cons.data[i], cons.targets)) {
for(uint j = 0; j < i; ++j)
cons.type.hooks[j].reverse(cons, cons.data[j], cons.targets);
return false;
}
}
if(buildCost != 0) {
if(cons.type.alwaysBorrowable && !repeated) {
budgetCycle = obj.owner.lowerBudget(buildCost);
}
else {
budgetCycle = obj.owner.consumeBudget(buildCost, borrow=!repeated);
if(budgetCycle == -1)
return false;
}
}
paid = true;
cons.start(this);
return true;
}
ConstructionConstructible(SaveFile& file) {
Constructible::load(file);
@cons = Construction();
file >> cons;
if(file >= SV_0110) {
file >> isTimed;
file >> timeProgress;
}
}
void save(SaveFile& file) {
Constructible::save(file);
file << cons;
file << isTimed;
file << timeProgress;
}
ConstructibleType get_type() {
return CT_Construction;
}
string get_name() {
return cons.type.name;
}
TickResult tick(Object& obj, double time) override {
if(isTimed) {
timeProgress += time;
curLabor = timeProgress;
}
cons.tick(this, time);
return isTimed ? TR_VanishLabor : TR_UsedLabor;
}
void cancel(Object& obj) override {
Constructible::cancel(obj);
cons.cancel(this);
}
void complete(Object& obj) {
cons.finish(this);
}
void write(Message& msg) {
Constructible::write(msg);
msg << cons.type.id;
msg << isTimed;
}
};
@@ -0,0 +1,83 @@
import construction.Constructible;
import orbitals;
import resources;
tidy class DryDockConstructible : Constructible {
Orbital@ orbital;
double givenLabor = 0.0;
DryDockConstructible(Object& obj, Orbital& orb) {
@orbital = orb;
totalLabor = INT_MAX;
}
DryDockConstructible(SaveFile& file) {
Constructible::load(file);
file >> orbital;
file >> givenLabor;
}
void save(SaveFile& file) {
Constructible::save(file);
file << orbital;
file << givenLabor;
}
bool repeat(Object& obj) {
return false;
}
ConstructibleType get_type() {
return CT_DryDock;
}
string get_name() {
return format(locale::BUILD_DRY_DOCK, "-");
}
bool start(Object& obj) {
if(started)
return true;
if(!Constructible::start(obj))
return false;
givenLabor = obj.laborIncome;
orbital.sendValue(OV_DRY_ModLabor, +givenLabor);
return true;
}
void remove(Object& obj) {
Constructible::remove(obj);
if(givenLabor != 0)
orbital.sendValue(OV_DRY_ModLabor, -givenLabor);
}
bool get_canComplete() {
return false;
}
TickResult tick(Object& obj, double time) override {
if(!orbital.valid)
return TR_Remove;
double income = max(obj.laborIncome, curLabor / max(time, 0.001));
curLabor = 0;
if(income != givenLabor) {
orbital.sendValue(OV_DRY_ModLabor, income - givenLabor);
givenLabor = income;
}
return orbital.usingLabor ? TR_UsedLabor : TR_UnusedLabor;
}
void move(Object& obj, uint toPosition) {
if(toPosition != 0 && givenLabor > 0 && orbital.valid) {
orbital.sendValue(OV_DRY_ModLabor, -givenLabor);
givenLabor = 0;
}
}
void write(Message& msg) {
Constructible::write(msg);
msg << orbital;
}
};
@@ -0,0 +1,80 @@
import construction.Constructible;
import resources;
tidy class ExportConstructible : Constructible {
Object@ exportTo;
double givenLabor = 0.0;
ExportConstructible(Object& obj, Object& exportTo) {
@this.exportTo = exportTo;
totalLabor = INT_MAX;
}
ExportConstructible(SaveFile& file) {
Constructible::load(file);
file >> exportTo;
file >> givenLabor;
}
bool repeat(Object& obj) {
return false;
}
void save(SaveFile& file) {
Constructible::save(file);
file << exportTo;
file << givenLabor;
}
ConstructibleType get_type() {
return CT_Export;
}
string get_name() {
return format(locale::EXPORT_LABOR, exportTo.name);
}
bool start(Object& obj) {
if(started)
return true;
if(!Constructible::start(obj))
return false;
return true;
}
void remove(Object& obj) {
Constructible::remove(obj);
if(givenLabor != 0)
exportTo.modLaborIncome(-givenLabor);
}
bool get_canComplete() {
return false;
}
TickResult tick(Object& obj, double time) override {
if(!exportTo.valid || !obj.canExportLabor || !exportTo.canImportLabor || exportTo.owner !is obj.owner)
return TR_Remove;
double income = max(obj.laborIncome, curLabor / max(time, 0.001));
if(obj.queuePosition(id) != 0)
income = 0.0;
curLabor = 0;
if(income != givenLabor) {
exportTo.modLaborIncome(income - givenLabor);
givenLabor = income;
}
return exportTo.isUsingLabor ? TR_UsedLabor : TR_UnusedLabor;
}
void move(Object& obj, uint toPosition) {
if(toPosition != 0 && givenLabor > 0 && exportTo.valid) {
exportTo.modLaborIncome(-givenLabor);
givenLabor = 0;
}
}
void write(Message& msg) {
Constructible::write(msg);
msg << exportTo;
}
};
@@ -0,0 +1,113 @@
import construction.Constructible;
import orbitals;
import object_creation;
import attributes;
tidy class OrbitalConstructible : Constructible {
const OrbitalModule@ def;
vec3d position;
Orbital@ target;
OrbitalConstructible(Object& obj, const OrbitalModule@ Def, const vec3d& pos) {
@def = Def;
buildCost = def.buildCost * obj.owner.OrbitalBuildCostFactor;
totalLabor = def.laborCost * obj.owner.OrbitalLaborCostFactor;
position = pos;
}
bool repeat(Object& obj) {
if(!Constructible::repeat(obj))
return false;
if(!def.canBuild(obj, position))
return false;
vec2d offset = random2d(def.size * 2.0, def.size * 4.0);
position.x += offset.x;
position.z += offset.y;
return true;
}
OrbitalConstructible(SaveFile& file) {
Constructible::load(file);
uint defId = file.readIdentifier(SI_Orbital);
@def = getOrbitalModule(defId);
file >> position;
file >> target;
}
void save(SaveFile& file) {
Constructible::save(file);
file.writeIdentifier(SI_Orbital, def.id);
file << position;
file << target;
}
bool pay(Object& obj) {
for(uint i = 0, cnt = def.hooks.length; i < cnt; ++i) {
if(!def.hooks[i].consume(obj)) {
for(uint j = 0; j < i; ++j)
def.hooks[j].reverse(obj, false);
return false;
}
}
if(!Constructible::pay(obj)) {
for(uint i = 0, cnt = def.hooks.length; i < cnt; ++i)
def.hooks[i].reverse(obj, false);
return false;
}
@target = createOrbital(position, def, obj.owner, disabled=true);
return true;
}
void cancel(Object& obj) {
if(buildCost != 0 && target !is null) {
if(!target.valid) {
buildCost = 0;
}
else {
double maxHealth = target.maxHealth + target.maxArmor;
double pct = 0.0;
if(totalLabor > 0)
maxHealth *= curLabor / totalLabor;
if(maxHealth != 0)
pct = clamp((target.health + target.armor) / maxHealth, 0.0, 1.0);
pct = clamp((pct-0.01) / 0.99, 0.0, 1.0);
buildCost = int(double(buildCost) * pct);
}
}
for(uint i = 0, cnt = def.hooks.length; i < cnt; ++i)
def.hooks[i].reverse(obj, true);
Constructible::cancel(obj);
if(target !is null && target.valid)
target.destroy();
}
ConstructibleType get_type() {
return CT_Orbital;
}
string get_name() {
return def.name;
}
TickResult tick(Object& obj, double time) override {
if(target is null || !target.valid || !def.canBuild(obj, target.position, initial=false)) {
cancel(obj);
return TR_Remove;
}
target.setBuildPct(curLabor / totalLabor);
return TR_UsedLabor;
}
void complete(Object& obj) {
if(target !is null) {
target.setDisabled(false);
obj.owner.modAttribute(EA_OrbitalsBuilt, AC_Add, 1.0);
}
}
void write(Message& msg) {
Constructible::write(msg);
msg << def.id;
}
};
@@ -0,0 +1,73 @@
import construction.Constructible;
import resources;
tidy class RetrofitConstructible : Constructible {
Object@ fleet;
Object@ constructFrom;
RetrofitConstructible(Object& obj, Object@ Fleet, int cost, double labor, int extraMaint) {
@fleet = Fleet;
buildCost = cost;
maintainCost = extraMaint;
totalLabor = labor;
}
RetrofitConstructible(SaveFile& file) {
Constructible::load(file);
file >> fleet;
if(file >= SV_0149)
file >> constructFrom;
}
void save(SaveFile& file) {
Constructible::save(file);
file << fleet;
file << constructFrom;
}
bool repeat(Object& obj) {
return false;
}
ConstructibleType get_type() {
return CT_Retrofit;
}
string get_name() {
return format(locale::BUILD_RETROFIT, fleet.name);
}
void cancel(Object& obj) {
fleet.stopFleetRetrofit(obj);
Constructible::cancel(obj);
}
void complete(Object& obj) {
fleet.finishFleetRetrofit(obj);
}
TickResult tick(Object& obj, double time) override {
if(obj.owner !is fleet.owner || obj.region is null) {
cancel(obj);
return TR_Remove;
}
if(constructFrom !is null) {
if(constructFrom.region !is fleet.region) {
cancel(obj);
return TR_Remove;
}
}
else {
if(obj.region !is fleet.region) {
cancel(obj);
return TR_Remove;
}
}
return TR_UsedLabor;
}
void write(Message& msg) {
Constructible::write(msg);
msg << fleet;
}
};
@@ -0,0 +1,255 @@
import construction.Constructible;
import resources;
import object_creation;
import ship_groups;
import cargo;
tidy class ShipConstructible : Constructible {
const Design@ design;
array<GroupData@> supports;
double savedLabor = 0;
Object@ constructFrom;
ShipConstructible(const Design@ Design) {
if(Design.outdated)
@Design = Design.owner.updateDesign(Design, true);
@design = Design;
getBuildCost(design, buildCost, maintainCost, totalLabor, 1);
}
ShipConstructible(SaveFile& msg) {
Constructible::load(msg);
uint dsgId = 0;
Empire@ owner;
msg >> owner;
msg >> dsgId;
@design = owner.getDesign(dsgId);
if(msg >= SV_0149)
msg >> constructFrom;
uint cnt = 0;
msg >> cnt;
supports.length = cnt;
for(uint i = 0; i < cnt; ++i) {
GroupData dat;
msg >> dat;
@supports[i] = dat;
}
msg >> savedLabor;
}
void save(SaveFile& msg) {
Constructible::save(msg);
msg << design.owner;
msg << design.id;
msg << constructFrom;
uint cnt = supports.length;
msg << cnt;
for(uint i = 0; i < cnt; ++i)
msg << supports[i];
msg << savedLabor;
}
bool pay(Object& obj) {
if(!payDesignCosts(obj, design))
return false;
if(!Constructible::pay(obj)) {
reverseDesignCosts(obj, design);
return false;
}
for(uint i = 0, cnt = supports.length; i < cnt; ++i)
supports[i].orderCycle = obj.owner.BudgetCycleId;
return true;
}
bool repeat(Object& obj) {
if(!Constructible::repeat(obj))
return false;
for(uint i = 0, cnt = supports.length; i < cnt; ++i) {
@supports[i].dsg = supports[i].dsg.mostUpdated();
supports[i].ordered += supports[i].amount;
supports[i].amount = 0;
}
return true;
}
void cancel(Object& obj) {
reverseDesignCosts(obj, design, cancel=true);
Constructible::cancel(obj);
}
string get_name() {
return design.name;
}
ConstructibleType get_type() {
return CT_Flagship;
}
void complete(Object& obj) {
double hpBonus = obj.constructionHPBonus;
Object@ spawnFrom;
if(constructFrom !is null && constructFrom.valid && constructFrom.owner is obj.owner)
@spawnFrom = constructFrom;
else
@spawnFrom = obj;
Ship@ ship = createShip(spawnFrom, design);
if(hpBonus != 0)
ship.modHPFactor(+hpBonus);
for(uint i = 0, cnt = supports.length; i < cnt; ++i) {
GroupData@ d = supports[i];
const Design@ dsg = d.dsg.mostUpdated();
ship.addSupportOrdered(dsg, d.ordered);
for(uint n = 0; n < d.amount; ++n) {
Ship@ sup = createShip(spawnFrom, dsg, obj.owner, ship);
if(hpBonus != 0)
sup.modHPFactor(+hpBonus);
}
}
spawnFrom.doRally(ship);
obj.owner.recordStatDelta(stat::ShipsBuilt, 1);
obj.owner.notifyFlagship(ship);
}
void write(Message& msg) {
Constructible::write(msg);
msg << design;
uint cnt = supports.length;
msg << cnt;
for(uint i = 0; i < cnt; ++i)
msg << supports[i];
}
bool addSupports(Object& obj, const Design@ dsg, uint amount, int cycle = -1) {
GroupData@ d;
@dsg = dsg.mostUpdated();
for(uint i = 0, cnt = supports.length; i < cnt; ++i) {
if(supports[i].dsg.mostUpdated() is dsg) {
@d = supports[i];
@d.dsg = dsg;
}
}
if(d is null) {
@d = GroupData();
@d.dsg = dsg;
supports.insertLast(d);
}
d.ordered += amount;
if(cycle == d.orderCycle) {
d.orderAmount += amount;
}
else {
d.orderCycle = cycle;
d.orderAmount = amount;
}
return true;
}
uint removeSupports(const Design@ dsg, uint amount, Object@ refund = null) {
GroupData@ d;
@dsg = dsg.mostUpdated();
for(uint i = 0, cnt = supports.length; i < cnt; ++i) {
if(supports[i].dsg.mostUpdated() is dsg) {
@d = supports[i];
@d.dsg = dsg;
}
}
if(d is null)
return 0;
uint prev = d.totalSize;
uint take = min(d.ordered, amount);
d.ordered -= take;
amount -= take;
if(amount > 0)
d.amount -= min(amount, d.amount);
uint removed = prev - d.totalSize;
if(refund !is null) {
int cost = getBuildCost(dsg, removed);
int maint = getMaintenanceCost(dsg, removed);
maintainCost -= maint;
buildCost -= cost;
if(d.orderCycle != -1) {
uint refd = min(d.orderAmount, removed);
refund.owner.refundBudget(getBuildCost(dsg, refd), d.orderCycle);
d.orderAmount -= refd;
}
if(started)
refund.owner.modMaintenance(-maint, MoT_Construction);
reverseDesignCosts(refund, dsg, removed, true);
}
if(d.totalSize <= 0)
supports.remove(d);
return removed;
}
bool get_hasSupports() {
return supports.length != 0;
}
bool get_hasSupportsBuilding() {
for(uint i = 0, cnt = supports.length; i < cnt; ++i) {
GroupData@ d = supports[i];
if(d.ordered > 0)
return true;
}
return false;
}
double getSupportSupplyFree() {
double supply = design.total(SV_SupportCapacity);
for(uint i = 0, cnt = supports.length; i < cnt; ++i) {
GroupData@ d = supports[i];
supply -= double(d.totalSize) * d.dsg.size;
}
return supply;
}
double addSupportLabor(double amount) {
if(supports.length == 0)
return amount;
if(!paid)
return amount;
savedLabor += amount;
for(uint i = 0, cnt = supports.length; i < cnt; ++i) {
GroupData@ d = supports[i];
if(d.ordered > 0) {
double laborCost = getLaborCost(d.dsg);
if(savedLabor < laborCost)
return 0.0;
while(d.ordered > 0 && savedLabor >= laborCost) {
savedLabor -= laborCost;
d.amount += 1;
d.ordered -= 1;
}
if(d.ordered > 0)
return 0.0;
}
}
savedLabor = 0.0;
return savedLabor;
}
};
@@ -0,0 +1,165 @@
import construction.Constructible;
import construction.ShipConstructible;
import resources;
import object_creation;
import ship_groups;
import util.formatting;
tidy class StationConstructible : ShipConstructible {
double laborPenalty = 1.0;
vec3d position;
Orbital@ target;
StationConstructible(const Design@ Design, vec3d pos, double penalty) {
super(Design);
laborPenalty = penalty;
position = pos;
totalLabor *= penalty;
}
StationConstructible(SaveFile& msg) {
super(msg);
msg >> laborPenalty;
msg >> position;
msg >> target;
}
void save(SaveFile& msg) {
ShipConstructible::save(msg);
msg << laborPenalty;
msg << position;
msg << target;
}
ConstructibleType get_type() {
return CT_Station;
}
bool repeat(Object& obj) {
if(!ShipConstructible::repeat(obj))
return false;
@target = null;
double size = stationRadiusFactor * pow(design.size,1.0/shipVolumePower);
vec2d offset = random2d(size * 2.0, size * 4.0);
position.x += offset.x;
position.z += offset.y;
return true;
}
bool pay(Object& obj) {
if(!ShipConstructible::pay(obj))
return false;
double hp = design.totalHP;
string name = format(locale::NAME_SCAFFOLDING, formatShipName(design));
@target = createOrbital(position,
getOrbitalModule("Scaffolding"),
obj.owner, disabled=true,
nameOverride=name);
target.modMaxHealth(hp);
target.setBuildPct(0.0);
return true;
}
void cancel(Object& obj) {
if(buildCost != 0 && target !is null) {
if(!target.valid) {
buildCost = 0;
}
else {
double maxHealth = target.maxHealth + target.maxArmor;
double pct = 0.0;
if(totalLabor > 0)
maxHealth *= curLabor / totalLabor;
if(maxHealth != 0)
pct = clamp((target.health + target.armor) / maxHealth, 0.0, 1.0);
pct = clamp((pct-0.01) / 0.99, 0.0, 1.0);
buildCost = int(double(buildCost) * pct);
}
}
ShipConstructible::cancel(obj);
if(target !is null && target.valid)
target.destroy();
}
TickResult tick(Object& obj, double time) override {
if(target is null || !target.valid) {
cancel(obj);
return TR_Remove;
}
target.setBuildPct(curLabor / totalLabor);
return TR_UsedLabor;
}
void complete(Object& obj) {
Ship@ ship = createShip(target.position, design, obj.owner);
for(uint i = 0, cnt = supports.length; i < cnt; ++i) {
GroupData@ d = supports[i];
const Design@ dsg = d.dsg.mostUpdated();
ship.addSupportOrdered(dsg, d.ordered);
for(uint n = 0; n < d.amount; ++n)
createShip(obj, dsg, obj.owner, ship);
}
obj.doRally(ship);
if(target !is null && target.valid) {
ship.setHealthPct(target.health / target.maxHealth);
target.destroy();
}
obj.owner.recordStatDelta(stat::ShipsBuilt, 1);
obj.owner.notifyFlagship(ship);
}
void write(Message& msg) {
Constructible::write(msg);
msg << design;
uint cnt = supports.length;
msg << cnt;
for(uint i = 0; i < cnt; ++i)
msg << supports[i];
}
bool addSupports(Object& obj, const Design@ dsg, uint amount, int cycle = -1) override {
if(target is null)
return false;
Region@ myRegion = obj.region;
Region@ targRegion = target.region;
if(myRegion is null || myRegion !is targRegion)
return false;
ShipConstructible::addSupports(obj, dsg, amount, cycle=cycle);
return true;
}
double addSupportLabor(double amount) {
if(supports.length == 0)
return amount;
if(!paid)
return amount;
savedLabor += amount / laborPenalty;
for(uint i = 0, cnt = supports.length; i < cnt; ++i) {
GroupData@ d = supports[i];
if(d.ordered > 0) {
double laborCost = getLaborCost(d.dsg);
if(savedLabor < laborCost)
return 0.0;
while(d.ordered > 0 && savedLabor >= laborCost) {
savedLabor -= laborCost;
d.amount += 1;
d.ordered -= 1;
}
if(d.ordered > 0)
return 0.0;
}
}
savedLabor = 0.0;
return savedLabor;
}
};
@@ -0,0 +1,69 @@
import construction.Constructible;
import resources;
tidy class TerraformConstructible : Constructible {
Planet@ planet;
const ResourceType@ resource;
TerraformConstructible(Object& obj, Planet@ p, const ResourceType@ r, double cost, double labor) {
@planet = p;
@resource = r;
buildCost = cost;
totalLabor = labor;
}
TerraformConstructible(SaveFile& file) {
Constructible::load(file);
uint rid = file.readIdentifier(SI_Resource);
@resource = getResource(rid);
file >> planet;
}
bool pay(Object& obj) override {
if(!Constructible::pay(obj))
return false;
planet.startTerraform();
return true;
}
bool repeat(Object& obj) {
return false;
}
void save(SaveFile& file) {
Constructible::save(file);
file.writeIdentifier(SI_Resource, resource.id);
file << planet;
}
ConstructibleType get_type() {
return CT_Terraform;
}
string get_name() {
return format(locale::BUILD_ASTEROID, resource.name);
}
void cancel(Object& obj) {
planet.stopTerraform();
Constructible::cancel(obj);
}
TickResult tick(Object& obj, double time) override {
if(obj.owner !is planet.owner || !planet.isTerraforming()) {
cancel(obj);
return TR_Remove;
}
return TR_UsedLabor;
}
void complete(Object& obj) {
planet.terraformTo(resource.id);
}
void write(Message& msg) {
Constructible::write(msg);
msg << resource.id;
msg << planet;
}
};