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( = 0, = 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( = 0, = 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( = 0, = 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() // 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() // When this orbital is killed, all friendly empires get 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( = "", = True) // This orbital can be packed into a ship of . 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( = 0, = 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(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 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 };