1336 lines
34 KiB
ActionScript
1336 lines
34 KiB
ActionScript
import hooks;
|
|
import orbitals;
|
|
import generic_effects;
|
|
import traits;
|
|
from orbitals import OrbitalEffect;
|
|
import requirement_effects;
|
|
import consume_effects;
|
|
|
|
#section server-side
|
|
from regions.regions import getRegion;
|
|
#section all
|
|
|
|
#section server
|
|
import object_creation;
|
|
#section all
|
|
|
|
class AddParticleSystem : OrbitalEffect {
|
|
Document doc("Adds a permanent particle system to the orbital.");
|
|
Argument particle("Particle System", AT_Custom, doc="ID of the particle system to create.");
|
|
Argument size("Size", AT_Decimal, "1.0", doc="Scale factor for the effect.");
|
|
|
|
#section server-side
|
|
void onMakeGraphics(Orbital& obj, any@ data, OrbitalNode@ node) const override {
|
|
auto@ gfx = PersistentGfx();
|
|
gfx.establish(obj, arguments[0].str, arguments[1].decimal);
|
|
gfx.rotate(obj.rotation);
|
|
}
|
|
#section all
|
|
};
|
|
|
|
//AddArmor(<Amount> = 0, <Per Import> = 0)
|
|
// Add maximum armor to the orbital.
|
|
class AddArmor : OrbitalEffect {
|
|
Document doc("Add maximum armor to the orbital.");
|
|
Argument amt("Amount", AT_Decimal, "0.0", doc="Base armor to add.");
|
|
Argument perImport("Per Import", AT_Decimal, "0.0", doc="Armor added per import.");
|
|
|
|
#section server
|
|
double amount(Orbital& obj) {
|
|
double amt = arguments[0].decimal;
|
|
double per = arguments[1].decimal;
|
|
if(per != 0)
|
|
amt += per * double(obj.usableResourceCount);
|
|
return amt;
|
|
}
|
|
|
|
void onEnable(Orbital& obj, any@ data) const override {
|
|
double amt = amount(obj);
|
|
data.store(amt);
|
|
obj.modMaxArmor(amt);
|
|
}
|
|
|
|
void onTick(Orbital& obj, any@ data, double time) const override {
|
|
double amt = amount(obj);
|
|
double prev = 0;
|
|
data.retrieve(prev);
|
|
if(amt != prev) {
|
|
data.store(amt);
|
|
obj.modMaxArmor(amt - prev);
|
|
}
|
|
}
|
|
|
|
void onDisable(Orbital& obj, any@ data) const override {
|
|
double amt = 0;
|
|
data.retrieve(amt);
|
|
obj.modMaxArmor(-amt);
|
|
}
|
|
|
|
void save(any@ data, SaveFile& file) const override {
|
|
double amt = 0;
|
|
data.retrieve(amt);
|
|
file << amt;
|
|
}
|
|
|
|
void load(any@ data, SaveFile& file) const override {
|
|
double amt = 0;
|
|
file >> amt;
|
|
data.store(amt);
|
|
}
|
|
#section all
|
|
};
|
|
|
|
//AddHealth(<Amount> = 0, <Per Import> = 0)
|
|
// Add maximum health to the orbital.
|
|
class AddHealth : OrbitalEffect {
|
|
Document doc("Add maximum health to the orbital.");
|
|
Argument amt("Amount", AT_Decimal, "0.0", doc="Base health to add.");
|
|
Argument perImport("Per Import", AT_Decimal, "0.0", doc="Health added per import.");
|
|
|
|
#section server
|
|
double amount(Orbital& obj) {
|
|
double amt = arguments[0].decimal;
|
|
double per = arguments[1].decimal;
|
|
if(per != 0)
|
|
amt += per * double(obj.usableResourceCount);
|
|
return amt;
|
|
}
|
|
|
|
void onEnable(Orbital& obj, any@ data) const override {
|
|
double amt = amount(obj);
|
|
data.store(amt);
|
|
obj.modMaxHealth(amt);
|
|
}
|
|
|
|
void onTick(Orbital& obj, any@ data, double time) const override {
|
|
double amt = amount(obj);
|
|
double prev = 0;
|
|
data.retrieve(prev);
|
|
if(amt != prev) {
|
|
data.store(amt);
|
|
obj.modMaxHealth(amt - prev);
|
|
}
|
|
}
|
|
|
|
void onDisable(Orbital& obj, any@ data) const override {
|
|
double amt = 0;
|
|
data.retrieve(amt);
|
|
obj.modMaxHealth(-amt);
|
|
}
|
|
|
|
void save(any@ data, SaveFile& file) const override {
|
|
double amt = 0;
|
|
data.retrieve(amt);
|
|
file << amt;
|
|
}
|
|
|
|
void load(any@ data, SaveFile& file) const override {
|
|
double amt = 0;
|
|
file >> amt;
|
|
data.store(amt);
|
|
}
|
|
#section all
|
|
};
|
|
|
|
class AddHealthEmpireAttribute : OrbitalEffect {
|
|
Document doc("Add base maximum health to the orbital based on an empire attribute.");
|
|
Argument attribute(AT_EmpAttribute, doc="Attribute to take the extra health from.");
|
|
Argument multiplier(AT_Decimal, "1.0", doc="Multiplication factor to the attribute value.");
|
|
|
|
#section server
|
|
double amount(Orbital& obj) {
|
|
double amt = obj.owner.getAttribute(attribute.integer);
|
|
amt *= multiplier.decimal;
|
|
return amt;
|
|
}
|
|
|
|
void onEnable(Orbital& obj, any@ data) const override {
|
|
double amt = amount(obj);
|
|
data.store(amt);
|
|
obj.modMaxHealth(amt);
|
|
}
|
|
|
|
void onTick(Orbital& obj, any@ data, double time) const override {
|
|
double amt = amount(obj);
|
|
double prev = 0;
|
|
data.retrieve(prev);
|
|
if(amt != prev) {
|
|
data.store(amt);
|
|
obj.modMaxHealth(amt - prev);
|
|
}
|
|
}
|
|
|
|
void onDisable(Orbital& obj, any@ data) const override {
|
|
double amt = 0;
|
|
data.retrieve(amt);
|
|
obj.modMaxHealth(-amt);
|
|
}
|
|
|
|
void save(any@ data, SaveFile& file) const override {
|
|
double amt = 0;
|
|
data.retrieve(amt);
|
|
file << amt;
|
|
}
|
|
|
|
void load(any@ data, SaveFile& file) const override {
|
|
double amt = 0;
|
|
file >> amt;
|
|
data.store(amt);
|
|
}
|
|
#section all
|
|
};
|
|
|
|
class AddArmorEmpireAttribute : OrbitalEffect {
|
|
Document doc("Add base maximum armor to the orbital based on an empire attribute.");
|
|
Argument attribute(AT_EmpAttribute, doc="Attribute to take the extra armor from.");
|
|
Argument multiplier(AT_Decimal, "1.0", doc="Multiplication factor to the attribute value.");
|
|
|
|
#section server
|
|
double amount(Orbital& obj) {
|
|
double amt = obj.owner.getAttribute(attribute.integer);
|
|
amt *= multiplier.decimal;
|
|
return amt;
|
|
}
|
|
|
|
void onEnable(Orbital& obj, any@ data) const override {
|
|
double amt = amount(obj);
|
|
data.store(amt);
|
|
obj.modMaxArmor(amt);
|
|
}
|
|
|
|
void onTick(Orbital& obj, any@ data, double time) const override {
|
|
double amt = amount(obj);
|
|
double prev = 0;
|
|
data.retrieve(prev);
|
|
if(amt != prev) {
|
|
data.store(amt);
|
|
obj.modMaxArmor(amt - prev);
|
|
}
|
|
}
|
|
|
|
void onDisable(Orbital& obj, any@ data) const override {
|
|
double amt = 0;
|
|
data.retrieve(amt);
|
|
obj.modMaxArmor(-amt);
|
|
}
|
|
|
|
void save(any@ data, SaveFile& file) const override {
|
|
double amt = 0;
|
|
data.retrieve(amt);
|
|
file << amt;
|
|
}
|
|
|
|
void load(any@ data, SaveFile& file) const override {
|
|
double amt = 0;
|
|
file >> amt;
|
|
data.store(amt);
|
|
}
|
|
#section all
|
|
};
|
|
|
|
//AddResistance(<Amount> = 0, <Per Import> = 0)
|
|
// Add damage resistance to the orbital.
|
|
class AddResistance : OrbitalEffect {
|
|
Document doc("Add armor resistance to the orbital.");
|
|
Argument amt("Amount", AT_Decimal, "0.0", doc="Base resistance to add.");
|
|
Argument perImport("Per Import", AT_Decimal, "0.0", doc="Resistance added per import.");
|
|
|
|
#section server
|
|
double amount(Orbital& obj) {
|
|
double amt = arguments[0].decimal;
|
|
double per = arguments[1].decimal;
|
|
if(per != 0)
|
|
amt += per * double(obj.usableResourceCount);
|
|
return amt;
|
|
}
|
|
|
|
void onEnable(Orbital& obj, any@ data) const override {
|
|
double amt = amount(obj);
|
|
data.store(amt);
|
|
obj.modDR(amt);
|
|
}
|
|
|
|
void onTick(Orbital& obj, any@ data, double time) const override {
|
|
double amt = amount(obj);
|
|
double prev = 0;
|
|
data.retrieve(prev);
|
|
if(amt != prev) {
|
|
data.store(amt);
|
|
obj.modDR(amt - prev);
|
|
}
|
|
}
|
|
|
|
void onDisable(Orbital& obj, any@ data) const override {
|
|
double amt = 0;
|
|
data.retrieve(amt);
|
|
obj.modDR(-amt);
|
|
}
|
|
|
|
void save(any@ data, SaveFile& file) const override {
|
|
double amt = 0;
|
|
data.retrieve(amt);
|
|
file << amt;
|
|
}
|
|
|
|
void load(any@ data, SaveFile& file) const override {
|
|
double amt = 0;
|
|
file >> amt;
|
|
data.store(amt);
|
|
}
|
|
#section all
|
|
};
|
|
|
|
class AddResistanceEmpireAttribute : OrbitalEffect {
|
|
Document doc("Add base resistance to the orbital based on an empire attribute.");
|
|
Argument attribute(AT_EmpAttribute, doc="Attribute to take the extra resistance from.");
|
|
Argument multiplier(AT_Decimal, "1.0", doc="Multiplication factor to the attribute value.");
|
|
|
|
#section server
|
|
double amount(Orbital& obj) {
|
|
double amt = obj.owner.getAttribute(attribute.integer);
|
|
amt *= multiplier.decimal;
|
|
return amt;
|
|
}
|
|
|
|
void onEnable(Orbital& obj, any@ data) const override {
|
|
double amt = amount(obj);
|
|
data.store(amt);
|
|
obj.modDR(amt);
|
|
}
|
|
|
|
void onTick(Orbital& obj, any@ data, double time) const override {
|
|
double amt = amount(obj);
|
|
double prev = 0;
|
|
data.retrieve(prev);
|
|
if(amt != prev) {
|
|
data.store(amt);
|
|
obj.modDR(amt - prev);
|
|
}
|
|
}
|
|
|
|
void onDisable(Orbital& obj, any@ data) const override {
|
|
double amt = 0;
|
|
data.retrieve(amt);
|
|
obj.modDR(-amt);
|
|
}
|
|
|
|
void save(any@ data, SaveFile& file) const override {
|
|
double amt = 0;
|
|
data.retrieve(amt);
|
|
file << amt;
|
|
}
|
|
|
|
void load(any@ data, SaveFile& file) const override {
|
|
double amt = 0;
|
|
file >> amt;
|
|
data.store(amt);
|
|
}
|
|
#section all
|
|
};
|
|
|
|
//FTLMaintenance(<Amount>)
|
|
// Add an FTL maintenance cost to the module.
|
|
class FTLMaintenance : OrbitalEffect {
|
|
Document doc("Makes the orbital cost FTL to maintain.");
|
|
Argument amt("Amount", AT_Decimal, doc="FTL maintenance cost.");
|
|
|
|
#section server
|
|
void onEnable(Orbital& obj, any@ data) const override {
|
|
if(obj.owner !is null && obj.owner.valid)
|
|
obj.owner.modFTLUse(arguments[0].decimal);
|
|
}
|
|
|
|
void onOwnerChange(Orbital& obj, any@ data, Empire@ prevOwner, Empire@ newOwner) const override {
|
|
if(prevOwner !is null && prevOwner.valid)
|
|
prevOwner.modFTLUse(-arguments[0].decimal);
|
|
if(newOwner !is null && newOwner.valid)
|
|
newOwner.modFTLUse(arguments[0].decimal);
|
|
}
|
|
|
|
void onDisable(Orbital& obj, any@ data) const override {
|
|
if(obj.owner !is null && obj.owner.valid)
|
|
obj.owner.modFTLUse(-arguments[0].decimal);
|
|
}
|
|
|
|
bool shouldDisable(Orbital& obj, any@ data) const override {
|
|
if(obj.owner is null || !obj.owner.valid)
|
|
return true;
|
|
return obj.owner.FTLShortage;
|
|
}
|
|
|
|
bool shouldEnable(Orbital& obj, any@ data) const override {
|
|
if(obj.owner is null || !obj.owner.valid)
|
|
return false;
|
|
return !obj.owner.isFTLShortage(arguments[0].decimal);
|
|
}
|
|
#section all
|
|
};
|
|
|
|
//DisableOnFTLBlock()
|
|
// Disables the module if ftl is blocked in the region.
|
|
class DisableOnFTLBlock : OrbitalEffect {
|
|
Document doc("Disables this orbital if FTL jamming is affecting its region.");
|
|
|
|
#section server
|
|
bool shouldDisable(Orbital& obj, any@ data) const override {
|
|
if(obj.owner is null || !obj.owner.valid)
|
|
return false;
|
|
Region@ reg = obj.region;
|
|
if(reg is null)
|
|
return false;
|
|
if(reg.BlockFTLMask & obj.owner.mask != 0)
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
bool shouldEnable(Orbital& obj, any@ data) const override {
|
|
if(obj.owner is null || !obj.owner.valid)
|
|
return true;
|
|
Region@ reg = obj.region;
|
|
if(reg is null)
|
|
return true;
|
|
if(reg.BlockFTLMask & obj.owner.mask != 0)
|
|
return false;
|
|
return true;
|
|
}
|
|
#section all
|
|
};
|
|
|
|
//CannotBuildManually()
|
|
// Module cannot be built manually.
|
|
class CannotBuildManually : OrbitalEffect {
|
|
Document doc("Prevents this orbital from being manually contructed.");
|
|
|
|
bool canBuildBy(Object@ obj, bool ignoreCost) const override {
|
|
return false;
|
|
}
|
|
|
|
bool canBuildOn(Orbital& obj) const override {
|
|
return false;
|
|
}
|
|
};
|
|
|
|
class RequireBlackHoleSystem : OrbitalEffect {
|
|
Document doc("This orbital can only be constructed in systems that have a black hole.");
|
|
|
|
string getBuildError(Object@ obj, const vec3d& pos) const {
|
|
return locale::OERR_BLACKHOLE;
|
|
}
|
|
|
|
bool canBuildAt(Object@ obj, const vec3d& pos) const {
|
|
Region@ target = getRegion(pos);
|
|
if(target is null)
|
|
return false;
|
|
return target.starCount > 0 && target.starTemperature == 0;
|
|
}
|
|
};
|
|
|
|
class RequireCloseToStar : OrbitalEffect {
|
|
Document doc("This orbital must be placed close to the star.");
|
|
Argument max_distance(AT_Decimal, "110", doc="Maximum radius away from star.");
|
|
Argument min_distance(AT_Decimal, "10", doc="Minimum radius away from star.");
|
|
|
|
string getBuildError(Object@ obj, const vec3d& pos) const {
|
|
return locale::OERR_CLOSE_TO_STAR;
|
|
}
|
|
|
|
bool canBuildAt(Object@ obj, const vec3d& pos) const {
|
|
Region@ target = getRegion(pos);
|
|
if(target is null)
|
|
return false;
|
|
double dist = pos.distanceTo(target.position);
|
|
double starRad = target.starRadius;
|
|
return dist > starRad && dist <= starRad + max_distance.decimal;
|
|
}
|
|
};
|
|
|
|
class LimitInOrbitStatus : OrbitalEffect {
|
|
Document doc("This orbital must be constructed in orbit of a planet, and can only be constructed if there is a limited amount of a status present.");
|
|
Argument status(AT_Status, doc="Status type to check for.");
|
|
Argument max_stacks(AT_Integer, "1", doc="Maximum stacks for the orbital.");
|
|
Argument min_distance(AT_Decimal, "5", doc="Minimum distance this needs to be from the planet.");
|
|
|
|
string getBuildError(Object@ obj, const vec3d& pos) const {
|
|
Region@ target = getRegion(pos);
|
|
if(target is null)
|
|
return locale::OERR_PLANET_ORBIT;
|
|
Object@ orbit = target.getOrbitObject(pos);
|
|
if(orbit is null || obj is null || orbit.owner !is obj.owner)
|
|
return locale::OERR_PLANET_ORBIT;
|
|
if(orbit.position.distanceTo(pos) < orbit.radius + min_distance.decimal)
|
|
return locale::OERR_PLANET_ORBIT;
|
|
return locale::OERR_PLANET_LIMIT;
|
|
}
|
|
|
|
bool canBuildAt(Object@ obj, const vec3d& pos) const {
|
|
Region@ target = getRegion(pos);
|
|
if(target is null)
|
|
return false;
|
|
|
|
Object@ orbit = target.getOrbitObject(pos);
|
|
if(orbit is null || obj is null || obj.owner !is orbit.owner)
|
|
return false;
|
|
|
|
if(orbit.position.distanceTo(pos) < orbit.radius + min_distance.decimal)
|
|
return false;
|
|
|
|
int count = orbit.getStatusStackCountAny(status.integer);
|
|
if(count >= max_stacks.integer)
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
#section server
|
|
void onEnable(Orbital& obj, any@ data) const override {
|
|
Region@ target = getRegion(obj.position);
|
|
if(target is null) {
|
|
obj.destroy();
|
|
return;
|
|
}
|
|
|
|
Object@ orbit = target.getOrbitObject(obj.position);
|
|
if(orbit is null) {
|
|
obj.destroy();
|
|
return;
|
|
}
|
|
|
|
int count = orbit.getStatusStackCountAny(status.integer);
|
|
if(count >= max_stacks.integer) {
|
|
obj.destroy();
|
|
return;
|
|
}
|
|
}
|
|
#section all
|
|
};
|
|
|
|
//OnKillSpreadLeverageToFriendlies(<Quality Factor>)
|
|
// When this orbital is killed, all friendly empires get <Quality Factor> leverage on the killer.
|
|
class OnKillSpreadLeverageToFriendlies : OrbitalEffect {
|
|
Document doc("Gives leverage to the orbital's owner and allies when destroyed.");
|
|
Argument qual("Quality Factor", AT_Decimal, "1.0", doc="Magic number to decide the overall value of leverage granted.");
|
|
|
|
#section server
|
|
void onKill(Orbital& obj, any@ data, Empire@ killedBy) const override {
|
|
Empire@ owner = obj.owner;
|
|
if(owner is null || !owner.valid)
|
|
return;
|
|
|
|
for(uint i = 0, cnt = getEmpireCount(); i < cnt; ++i) {
|
|
Empire@ other = getEmpire(i);
|
|
if(!other.major || other is killedBy)
|
|
continue;
|
|
if(other !is owner && other.isHostile(owner))
|
|
continue;
|
|
|
|
other.gainRandomLeverage(killedBy, arguments[0].decimal);
|
|
}
|
|
}
|
|
#section all
|
|
};
|
|
|
|
//DryDockConstruction()
|
|
// Allow this orbital to do dry dock construction.
|
|
tidy final class DryDockStatus {
|
|
const Design@ design;
|
|
int maintainCost = 0;
|
|
int buildCost = 0;
|
|
double percentPaid = 0;
|
|
double laborGen = 0;
|
|
double curLabor = 0;
|
|
double totalLabor = 1;
|
|
bool isFree = false;
|
|
};
|
|
|
|
class DryDockConstruction : OrbitalEffect {
|
|
Document doc("Allows this orbital to act as a drydock.");
|
|
|
|
#section server-side
|
|
void write(any@ data, Message& msg) const {
|
|
DryDockStatus@ status;
|
|
data.retrieve(@status);
|
|
|
|
if(status is null) {
|
|
msg.write0();
|
|
return;
|
|
}
|
|
|
|
msg.write1();
|
|
msg << status.design;
|
|
msg.writeFixed(status.percentPaid, bits=8);
|
|
msg << float(status.curLabor);
|
|
msg << float(status.totalLabor);
|
|
msg << float(status.laborGen);
|
|
msg.writeBit(status.isFree);
|
|
}
|
|
|
|
void read(any@ data, Message& msg) const {
|
|
if(!msg.readBit())
|
|
return;
|
|
|
|
DryDockStatus@ status;
|
|
data.retrieve(@status);
|
|
if(status is null) {
|
|
@status = DryDockStatus();
|
|
data.store(@status);
|
|
}
|
|
|
|
msg >> status.design;
|
|
status.percentPaid = msg.readFixed(bits=8);
|
|
status.curLabor = msg.read_float();
|
|
status.totalLabor = msg.read_float();
|
|
status.laborGen = msg.read_float();
|
|
status.isFree = msg.readBit();
|
|
}
|
|
#section server
|
|
void save(any@ data, SaveFile& file) const {
|
|
DryDockStatus@ status;
|
|
data.retrieve(@status);
|
|
|
|
file << status.design;
|
|
file << status.maintainCost;
|
|
file << status.buildCost;
|
|
file << status.percentPaid;
|
|
file << status.laborGen;
|
|
file << status.curLabor;
|
|
file << status.totalLabor;
|
|
file << status.isFree;
|
|
}
|
|
|
|
void load(any@ data, SaveFile& file) const {
|
|
DryDockStatus status;
|
|
data.store(@status);
|
|
|
|
file >> status.design;
|
|
file >> status.maintainCost;
|
|
file >> status.buildCost;
|
|
file >> status.percentPaid;
|
|
file >> status.laborGen;
|
|
file >> status.curLabor;
|
|
file >> status.totalLabor;
|
|
file >> status.isFree;
|
|
}
|
|
|
|
void onEnable(Orbital& obj, any@ data) const override {
|
|
DryDockStatus status;
|
|
data.store(@status);
|
|
}
|
|
|
|
void onDisable(Orbital& obj, any@ data) const override {
|
|
DryDockStatus@ status;
|
|
data.retrieve(@status);
|
|
|
|
if(!status.isFree) {
|
|
int prevMaint = ceil(double(status.maintainCost) * status.percentPaid);
|
|
obj.owner.modMaintenance(-prevMaint, MoT_Construction);
|
|
}
|
|
|
|
@status = null;
|
|
data.store(@status);
|
|
}
|
|
|
|
void onTick(Orbital& obj, any@ data, double time) const override {
|
|
DryDockStatus@ status;
|
|
data.retrieve(@status);
|
|
if(status is null || status.design is null)
|
|
return;
|
|
int pct = int(status.curLabor / status.totalLabor * 1000);
|
|
if(status.laborGen != 0) {
|
|
double prevLabor = status.curLabor;
|
|
status.curLabor = clamp(status.curLabor + status.laborGen * time,
|
|
0, max(status.totalLabor * status.percentPaid, status.curLabor));
|
|
obj.usingLabor = abs(prevLabor - status.curLabor) > 0.001;
|
|
if(pct != int(status.curLabor / status.totalLabor * 1000))
|
|
obj.triggerDelta();
|
|
}
|
|
else {
|
|
obj.usingLabor = false;
|
|
}
|
|
if(status.curLabor >= status.totalLabor && status.percentPaid >= 1.f) {
|
|
createShip(obj, status.design, move=false, free=status.isFree);
|
|
obj.usingLabor = false;
|
|
obj.destroy();
|
|
}
|
|
}
|
|
|
|
bool sendDesign(Player& pl, Orbital& obj, any@ data, uint index, const Design@ value) const {
|
|
if(pl != SERVER_PLAYER)
|
|
return false;
|
|
if(index == OV_DRY_Design) {
|
|
DryDockStatus@ status;
|
|
data.retrieve(@status);
|
|
if(status is null)
|
|
return false;
|
|
|
|
@status.design = value;
|
|
getBuildCost(value, status.buildCost, status.maintainCost, status.totalLabor);
|
|
status.buildCost = double(status.buildCost) * config::DRYDOCK_BUILDCOST_FACTOR * obj.owner.DrydockCostFactor;
|
|
status.curLabor = 0;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool sendValue(Player& pl, Orbital& obj, any@ data, uint index, double value) const {
|
|
if(index == OV_DRY_SetFinanced) {
|
|
if(pl != SERVER_PLAYER)
|
|
return false;
|
|
|
|
DryDockStatus@ status;
|
|
data.retrieve(@status);
|
|
if(status is null)
|
|
return false;
|
|
|
|
if(!status.isFree) {
|
|
int prevMaint = ceil(double(status.maintainCost) * status.percentPaid);
|
|
int newMaint = ceil(double(status.maintainCost) * value);
|
|
obj.owner.modMaintenance(newMaint - prevMaint, MoT_Construction);
|
|
}
|
|
status.percentPaid = value;
|
|
obj.triggerDelta();
|
|
return true;
|
|
}
|
|
if(index == OV_DRY_Progress) {
|
|
if(pl != SERVER_PLAYER)
|
|
return false;
|
|
|
|
DryDockStatus@ status;
|
|
data.retrieve(@status);
|
|
if(status is null)
|
|
return false;
|
|
|
|
status.curLabor = value * status.totalLabor;
|
|
obj.triggerDelta();
|
|
return true;
|
|
}
|
|
if(index == OV_DRY_Free) {
|
|
if(pl != SERVER_PLAYER)
|
|
return false;
|
|
|
|
DryDockStatus@ status;
|
|
data.retrieve(@status);
|
|
if(status is null)
|
|
return false;
|
|
|
|
bool free = value != 0.0;
|
|
if(free != status.isFree) {
|
|
if(status.maintainCost != 0) {
|
|
if(status.isFree) {
|
|
int prevMaint = ceil(double(status.maintainCost) * status.percentPaid);
|
|
obj.owner.modMaintenance(+prevMaint, MoT_Construction);
|
|
}
|
|
else {
|
|
int prevMaint = ceil(double(status.maintainCost) * status.percentPaid);
|
|
obj.owner.modMaintenance(-prevMaint, MoT_Construction);
|
|
}
|
|
}
|
|
status.isFree = free;
|
|
}
|
|
obj.triggerDelta();
|
|
return true;
|
|
}
|
|
if(index == OV_DRY_ModLabor) {
|
|
if(pl != SERVER_PLAYER)
|
|
return false;
|
|
|
|
DryDockStatus@ status;
|
|
data.retrieve(@status);
|
|
if(status is null)
|
|
return false;
|
|
|
|
status.laborGen += value;
|
|
obj.triggerDelta();
|
|
return true;
|
|
}
|
|
if(index == OV_DRY_Financed) {
|
|
DryDockStatus@ status;
|
|
data.retrieve(@status);
|
|
if(status is null)
|
|
return false;
|
|
|
|
double pct = value;
|
|
double prevPct = status.percentPaid;
|
|
double newPct = clamp(pct, 0.0, 1.0);
|
|
pct = newPct - prevPct;
|
|
|
|
int cost = ceil(double(status.buildCost) * pct);
|
|
if(obj.owner.consumeBudget(cost) == -1)
|
|
return true;
|
|
|
|
if(!status.isFree) {
|
|
int prevMaint = ceil(double(status.maintainCost) * prevPct);
|
|
int newMaint = ceil(double(status.maintainCost) * newPct);
|
|
obj.owner.modMaintenance(newMaint - prevMaint, MoT_Construction);
|
|
}
|
|
status.percentPaid = newPct;
|
|
obj.triggerDelta();
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
#section server-side
|
|
bool getValue(Player& pl, Orbital& obj, any@ data, uint index, double& value) const {
|
|
DryDockStatus@ status;
|
|
data.retrieve(@status);
|
|
if(status is null)
|
|
return false;
|
|
|
|
switch(index) {
|
|
case OV_DRY_Free: value = status.isFree ? 1.0 : 0.0; return true;
|
|
case OV_DRY_Progress: value = status.curLabor / status.totalLabor; return true;
|
|
case OV_DRY_Financed: value = status.percentPaid; return true;
|
|
case OV_DRY_ETA:
|
|
value = (status.laborGen == 0) ? INFINITY :
|
|
(status.totalLabor - status.curLabor) / status.laborGen;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool getDesign(Player& pl, Orbital& obj, any@ data, uint index, const Design@& value) const {
|
|
if(index == OV_DRY_Design) {
|
|
DryDockStatus@ status;
|
|
data.retrieve(@status);
|
|
if(status is null)
|
|
return false;
|
|
|
|
@value = status.design;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
#section all
|
|
};
|
|
|
|
//PacksIntoShip(<Design> = "", <Maintenance> = True)
|
|
// This orbital can be packed into a ship of <Design>.
|
|
class PacksIntoShip : OrbitalEffect {
|
|
Document doc("Allows the orbital to be packed into a specific ship design.");
|
|
Argument design("Design", AT_Custom, "", doc="Design name to packup into.");
|
|
Argument maint("Maintenance", AT_Boolean, "True", doc="Whether the created ship should cost maintenance.");
|
|
|
|
#section server-side
|
|
void write(any@ data, Message& msg) const {
|
|
const Design@ dsg;
|
|
data.retrieve(@dsg);
|
|
msg << dsg;
|
|
}
|
|
|
|
void read(any@ data, Message& msg) const {
|
|
const Design@ dsg;
|
|
msg >> dsg;
|
|
data.store(@dsg);
|
|
}
|
|
#section server
|
|
void save(any@ data, SaveFile& file) const {
|
|
const Design@ dsg;
|
|
data.retrieve(@dsg);
|
|
file << dsg;
|
|
}
|
|
|
|
void load(any@ data, SaveFile& file) const {
|
|
const Design@ dsg;
|
|
file >> dsg;
|
|
data.store(@dsg);
|
|
}
|
|
|
|
void onEnable(Orbital& obj, any@ data) const override {
|
|
const Design@ dsg;
|
|
data.retrieve(@dsg);
|
|
if(dsg is null) {
|
|
for(uint i = 0, cnt = obj.owner.designCount; i < cnt; ++i) {
|
|
auto@ check = obj.owner.designs[i];
|
|
if(check.name == arguments[0].str) {
|
|
@dsg = check.mostUpdated();
|
|
break;
|
|
}
|
|
}
|
|
data.store(@dsg);
|
|
}
|
|
if(arguments[1].boolean && obj.owner !is null && obj.owner.valid)
|
|
obj.owner.modMaintenance(+getMaintenance(obj, data), MoT_Orbitals);
|
|
}
|
|
|
|
int getMaintenance(Orbital& obj, any@ data) const {
|
|
const Design@ dsg;
|
|
data.retrieve(@dsg);
|
|
if(dsg is null)
|
|
return 0;
|
|
const OrbitalModule@ mod = getOrbitalModule(obj.coreModule);
|
|
if(mod is null)
|
|
return getMaintenanceCost(dsg);
|
|
return getMaintenanceCost(dsg) - mod.maintenance;
|
|
}
|
|
|
|
void onDisable(Orbital& obj, any@ data) const override {
|
|
if(arguments[1].boolean && obj.owner !is null && obj.owner.valid)
|
|
obj.owner.modMaintenance(-getMaintenance(obj, data), MoT_Orbitals);
|
|
const Design@ dsg = null;
|
|
data.store(@dsg);
|
|
}
|
|
|
|
void onOwnerChange(Orbital& obj, any@ data, Empire@ prevOwner, Empire@ newOwner) const override {
|
|
if(arguments[1].boolean) {
|
|
int maint = getMaintenance(obj, data);
|
|
if(prevOwner !is null && prevOwner.valid)
|
|
prevOwner.modMaintenance(-maint, MoT_Orbitals);
|
|
if(newOwner !is null && newOwner.valid)
|
|
newOwner.modMaintenance(+maint, MoT_Orbitals);
|
|
}
|
|
}
|
|
|
|
bool sendDesign(Player& pl, Orbital& obj, any@ data, uint index, const Design@ value) const {
|
|
if(pl != SERVER_PLAYER)
|
|
return false;
|
|
if(index == OV_PackUp) {
|
|
int prevMaintenance = 0, newMaintenance = 0;
|
|
if(arguments[1].boolean)
|
|
prevMaintenance = getMaintenance(obj, data);
|
|
data.store(@value);
|
|
if(arguments[1].boolean) {
|
|
newMaintenance = getMaintenance(obj, data);
|
|
if(prevMaintenance != newMaintenance && obj.owner !is null && obj.owner.valid)
|
|
obj.owner.modMaintenance(newMaintenance - prevMaintenance, MoT_Orbitals);
|
|
}
|
|
obj.triggerDelta();
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool sendValue(Player& pl, Orbital& obj, any@ data, uint index, double value) const {
|
|
if(index == OV_PackUp) {
|
|
if(pl != SERVER_PLAYER && pl.emp !is obj.owner)
|
|
return false;
|
|
|
|
const Design@ dsg;
|
|
data.retrieve(@dsg);
|
|
if(dsg is null)
|
|
return false;
|
|
createShip(obj.position, dsg, obj.owner);
|
|
obj.destroy();
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
#section server-side
|
|
bool getDesign(Player& pl, Orbital& obj, any@ data, uint index, const Design@& value) const {
|
|
if(index == OV_PackUp) {
|
|
const Design@ dsg;
|
|
data.retrieve(@dsg);
|
|
@value = dsg;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
#section all
|
|
};
|
|
|
|
//RecordOrbitalDPS(<Amount> = 0, <Per Import> = 0)
|
|
// Add recorded dps to the orbital.
|
|
class RecordOrbitalDPS : OrbitalEffect {
|
|
Document doc("Adds to the listed damage per second on an orbital.");
|
|
Argument amt("Amount", AT_Decimal, "0.0", doc="Base DPS to add.");
|
|
Argument perImport("Per Import", AT_Decimal, "0.0", doc="DPS to add per import");
|
|
|
|
#section server
|
|
double amount(Orbital& obj) {
|
|
double amt = arguments[0].decimal;
|
|
double per = arguments[1].decimal;
|
|
if(per != 0)
|
|
amt += per * double(obj.usableResourceCount);
|
|
return amt;
|
|
}
|
|
|
|
void onEnable(Orbital& obj, any@ data) const override {
|
|
double amt = amount(obj);
|
|
data.store(amt);
|
|
obj.modDPS(amt);
|
|
}
|
|
|
|
void onTick(Orbital& obj, any@ data, double time) const override {
|
|
double amt = amount(obj);
|
|
double prev = 0;
|
|
data.retrieve(prev);
|
|
if(amt != prev) {
|
|
data.store(amt);
|
|
obj.modDPS(amt - prev);
|
|
}
|
|
}
|
|
|
|
void onDisable(Orbital& obj, any@ data) const override {
|
|
double amt = 0;
|
|
data.retrieve(amt);
|
|
obj.modDPS(-amt);
|
|
}
|
|
|
|
void save(any@ data, SaveFile& file) const override {
|
|
double amt = 0;
|
|
data.retrieve(amt);
|
|
file << amt;
|
|
}
|
|
|
|
void load(any@ data, SaveFile& file) const override {
|
|
double amt = 0;
|
|
file >> amt;
|
|
data.store(amt);
|
|
}
|
|
#section all
|
|
};
|
|
|
|
class LimitOncePerSystem : OrbitalEffect {
|
|
Document doc("This orbital can only be constructed once per system.");
|
|
Argument flag(AT_SystemFlag, doc="System flag to base the once per system limit on. Can be set to any arbitrary unique name.");
|
|
Argument any_empire(AT_Boolean, "False", doc="If set to true, only one empire may build this in a system.");
|
|
|
|
bool canBuildAt(Object@ obj, const vec3d& pos) const {
|
|
auto@ system = getRegion(pos);
|
|
if(system is null)
|
|
return false;
|
|
if(obj is null)
|
|
return false;
|
|
return !check(obj, system);
|
|
}
|
|
|
|
bool check(Object& obj, Region@ system) {
|
|
if(any_empire.boolean)
|
|
return system.getSystemFlagAny(flag.integer);
|
|
else
|
|
return system.getSystemFlag(obj.owner, flag.integer);
|
|
}
|
|
|
|
string getBuildError(Object@ obj, const vec3d& pos) const {
|
|
return locale::OERR_LIMIT;
|
|
}
|
|
|
|
#section server
|
|
void onOwnerChange(Orbital& obj, any@ data, Empire@ prevOwner, Empire@ newOwner) const override {
|
|
Region@ region = obj.region;
|
|
if(region !is null) {
|
|
if(prevOwner !is null && prevOwner.valid)
|
|
region.setSystemFlag(prevOwner, flag.integer, false);
|
|
if(newOwner !is null && newOwner.valid) {
|
|
if(region.getSystemFlag(newOwner, flag.integer))
|
|
obj.destroy();
|
|
else
|
|
region.setSystemFlag(newOwner, flag.integer, true);
|
|
}
|
|
}
|
|
}
|
|
|
|
void onRegionChange(Orbital& obj, any@ data, Region@ fromRegion, Region@ toRegion) const override {
|
|
Empire@ owner = obj.owner;
|
|
if(owner !is null && owner.valid) {
|
|
if(fromRegion !is null)
|
|
fromRegion.setSystemFlag(owner, flag.integer, false);
|
|
if(toRegion !is null) {
|
|
if(check(obj, toRegion)) {
|
|
if(!obj.inFTL)
|
|
obj.destroy();
|
|
}
|
|
else
|
|
toRegion.setSystemFlag(owner, flag.integer, true);
|
|
}
|
|
}
|
|
}
|
|
|
|
void onEnable(Orbital& obj, any@ data) const override {
|
|
Region@ region = obj.region;
|
|
Empire@ owner = obj.owner;
|
|
if(region !is null && owner !is null && owner.valid) {
|
|
if(check(obj, region))
|
|
obj.destroy();
|
|
else
|
|
region.setSystemFlag(owner, flag.integer, true);
|
|
}
|
|
}
|
|
|
|
void onDisable(Orbital& obj, any@ data) const override {
|
|
Region@ region = obj.region;
|
|
Empire@ owner = obj.owner;
|
|
if(region !is null && owner !is null && owner.valid)
|
|
region.setSystemFlag(owner, flag.integer, false);
|
|
}
|
|
|
|
bool shouldDisable(Orbital& obj, any@ data) const override {
|
|
return obj.inFTL || obj.region is null;
|
|
}
|
|
|
|
bool shouldEnable(Orbital& obj, any@ data) const override {
|
|
return !obj.inFTL && obj.region !is null;
|
|
}
|
|
#section all
|
|
};
|
|
|
|
class SetAsDefense : OrbitalEffect {
|
|
Document doc("This orbital is set to make use of defense production by default.");
|
|
|
|
#section server
|
|
void onEnable(Orbital& obj, any@ data) const override {
|
|
Empire@ owner = obj.owner;
|
|
if(owner !is null && owner.valid)
|
|
owner.setDefending(obj, true);
|
|
}
|
|
|
|
void onOwnerChange(Orbital& obj, any@ data, Empire@ prevOwner, Empire@ newOwner) const override {
|
|
if(prevOwner !is null && prevOwner.valid)
|
|
prevOwner.setDefending(obj, false);
|
|
if(newOwner !is null && newOwner.valid)
|
|
newOwner.setDefending(obj, true);
|
|
}
|
|
#section all
|
|
};
|
|
|
|
class FrameConstruction : OrbitalEffect {
|
|
Document doc("This orbital is a construction frame for other orbitals.");
|
|
Argument cost_factor(AT_Decimal, "1.0", doc="Factor to build cost of orbitals built on this frame.");
|
|
Argument labor_factor(AT_Decimal, "1.0", doc="Factor to labor cost of orbitals built on this frame.");
|
|
Argument labor_penalty_factor(AT_Decimal, "1.0", doc="Factor to labor cost penalty of orbitals built on this frame.");
|
|
|
|
#section server
|
|
bool sendObject(Player& pl, Orbital& obj, any@ data, uint index, Object@ value) const {
|
|
if(index == OV_FRAME_Target) {
|
|
obj.destroy();
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
#section server-side
|
|
bool getValue(Player& pl, Orbital& obj, any@ data, uint index, double& value) const {
|
|
if(index == OV_FRAME_Usable) {
|
|
value = 1.0;
|
|
return true;
|
|
}
|
|
else if(index == OV_FRAME_CostFactor) {
|
|
value = cost_factor.decimal;
|
|
return true;
|
|
}
|
|
else if(index == OV_FRAME_LaborFactor) {
|
|
value = labor_factor.decimal;
|
|
return true;
|
|
}
|
|
else if(index == OV_FRAME_LaborPenaltyFactor) {
|
|
value = labor_penalty_factor.decimal;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
#section all
|
|
};
|
|
|
|
class BreakOrbitOnFTL : OrbitalEffect {
|
|
Document doc("When the planet this orbital is orbiting FTLs, the orbital breaks its orbit.");
|
|
|
|
#section server
|
|
void onTick(Orbital& obj, any@ data, double time) const override {
|
|
Object@ around = obj.getOrbitingAround();
|
|
if(around !is null && around.inFTL)
|
|
obj.stopOrbit();
|
|
}
|
|
#section all
|
|
};
|
|
|
|
class AllowResourceImport : OrbitalEffect {
|
|
Document doc("This orbital can import resources.");
|
|
|
|
#section server
|
|
void onEnable(Orbital& obj, any@ data) const override {
|
|
obj.setImportEnabled(true);
|
|
}
|
|
#section all
|
|
};
|
|
|
|
class AutoMasterSlave : OrbitalEffect {
|
|
Document doc("Automatically slave this to the first orbital of this type.");
|
|
|
|
#section server
|
|
void onEnable(Orbital& obj, any@ data) const override {
|
|
uint core = obj.coreModule;
|
|
uint id = uint(obj.id);
|
|
|
|
auto@ datalist = obj.owner.getOrbitals();
|
|
Object@ check;
|
|
Orbital@ newMaster;
|
|
while(receive(datalist, check)) {
|
|
Orbital@ orb = cast<Orbital>(check);
|
|
if(orb is null || !orb.valid)
|
|
continue;
|
|
if(uint(orb.id) >= id)
|
|
continue;
|
|
if(orb.coreModule != core)
|
|
continue;
|
|
|
|
@newMaster = orb;
|
|
id = uint(orb.id);
|
|
}
|
|
|
|
obj.setMaster(newMaster);
|
|
}
|
|
|
|
void onTick(Orbital& obj, any@ data, double time) const override {
|
|
Orbital@ master = obj.getMaster();
|
|
if(master !is null) {
|
|
if(!master.valid || master.owner !is obj.owner)
|
|
onEnable(obj, data);
|
|
}
|
|
}
|
|
|
|
void onOwnerChange(Orbital& obj, any@ data, Empire@ prevOwner, Empire@ newOwner) const override {
|
|
onEnable(obj, data);
|
|
}
|
|
#section all
|
|
};
|
|
|
|
class CopyLaborFromMaster : OrbitalEffect {
|
|
Document doc("Copy the labor income that this orbital's master is giving.");
|
|
|
|
#section server
|
|
void onEnable(Orbital& obj, any@ data) const override {
|
|
double amount = 0;
|
|
data.store(amount);
|
|
}
|
|
|
|
void onTick(Orbital& obj, any@ data, double time) const override {
|
|
double prevAmount = 0;
|
|
data.retrieve(prevAmount);
|
|
|
|
double newAmount = 0;
|
|
Orbital@ master = obj.getMaster();
|
|
if(master !is null)
|
|
newAmount = master.laborIncome;
|
|
|
|
if(prevAmount != newAmount) {
|
|
obj.modLaborIncome(newAmount - prevAmount);
|
|
data.store(newAmount);
|
|
}
|
|
}
|
|
|
|
void onDisable(Orbital& obj, any@ data) const override {
|
|
double amount = 0;
|
|
data.retrieve(amount);
|
|
|
|
obj.modLaborIncome(-amount);
|
|
}
|
|
|
|
void save(any@ data, SaveFile& file) const override {
|
|
double amount = 0;
|
|
data.retrieve(amount);
|
|
file << amount;
|
|
}
|
|
|
|
void load(any@ data, SaveFile& file) const override {
|
|
double amount = 0;
|
|
file >> amount;
|
|
data.store(amount);
|
|
}
|
|
#section all
|
|
};
|
|
|
|
tidy final class PressureCopier {
|
|
Orbital@ copyTo;
|
|
array<double> amount(TR_COUNT, 0.0);
|
|
};
|
|
|
|
class CopyPressureToMaster : OrbitalEffect {
|
|
Document doc("Copy over all the pressure this orbital is importing to the orbital's master if it has one.");
|
|
|
|
#section server
|
|
void onEnable(Orbital& obj, any@ data) const override {
|
|
PressureCopier pc;
|
|
data.store(@pc);
|
|
}
|
|
|
|
void onTick(Orbital& obj, any@ data, double time) const override {
|
|
PressureCopier@ pc;
|
|
data.retrieve(@pc);
|
|
|
|
//Deal with master changes
|
|
Orbital@ newMaster = obj.getMaster();
|
|
if(newMaster !is pc.copyTo) {
|
|
for(uint i = 0, cnt = pc.amount.length; i < cnt; ++i) {
|
|
if(pc.amount[i] != 0) {
|
|
pc.copyTo.modPressure(i, -pc.amount[i]);
|
|
newMaster.modPressure(i, +pc.amount[i]);
|
|
}
|
|
}
|
|
@pc.copyTo = newMaster;
|
|
}
|
|
|
|
//Deal with pressure changes
|
|
if(pc.copyTo !is null) {
|
|
for(uint i = 0, cnt = pc.amount.length; i < cnt; ++i) {
|
|
double newValue = obj.resourcePressure[i];
|
|
if(pc.amount[i] != newValue) {
|
|
pc.copyTo.modPressure(i, newValue - pc.amount[i]);
|
|
pc.amount[i] = newValue;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void onDisable(Orbital& obj, any@ data) const override {
|
|
PressureCopier@ pc;
|
|
data.retrieve(@pc);
|
|
|
|
if(pc.copyTo !is null) {
|
|
for(uint i = 0, cnt = pc.amount.length; i < cnt; ++i) {
|
|
if(pc.amount[i] != 0)
|
|
pc.copyTo.modPressure(i, -pc.amount[i]);
|
|
}
|
|
}
|
|
}
|
|
|
|
void save(any@ data, SaveFile& file) const override {
|
|
PressureCopier@ pc;
|
|
data.retrieve(@pc);
|
|
if(pc is null)
|
|
@pc = PressureCopier();
|
|
|
|
file << pc.copyTo;
|
|
|
|
uint cnt = pc.amount.length;
|
|
file << cnt;
|
|
for(uint i = 0; i < cnt; ++i)
|
|
file << pc.amount[i];
|
|
}
|
|
|
|
void load(any@ data, SaveFile& file) const override {
|
|
PressureCopier pc;
|
|
data.store(@pc);
|
|
|
|
file >> pc.copyTo;
|
|
|
|
uint cnt = 0;
|
|
file >> cnt;
|
|
pc.amount.length = cnt;
|
|
|
|
for(uint i = 0; i < cnt; ++i)
|
|
file >> pc.amount[i];
|
|
}
|
|
#section all
|
|
};
|
|
|
|
class MoveCargoToMaster : OrbitalEffect {
|
|
Document doc("Move all the cargo on this orbital to the orbital's master if it has one.");
|
|
|
|
#section server
|
|
void onTick(Orbital& obj, any@ data, double time) const override {
|
|
if(obj.hasCargo && obj.cargoStored > 0 && obj.hasMaster())
|
|
obj.transferAllCargoTo(obj.getMaster());
|
|
}
|
|
#section all
|
|
};
|
|
|
|
class MoveImportsToMaster : OrbitalEffect {
|
|
Document doc("Redirect all imports to this orbital to its master.");
|
|
|
|
#section server
|
|
void onTick(Orbital& obj, any@ data, double time) const override {
|
|
if(obj.hasMaster()) {
|
|
Object@ master = obj.getMaster();
|
|
obj.redirectAllImports(master);
|
|
}
|
|
}
|
|
#section all
|
|
};
|