Open source Star Ruler 2 source code!
This commit is contained in:
@@ -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;
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user