Open source Star Ruler 2 source code!
This commit is contained in:
@@ -0,0 +1,501 @@
|
||||
import resources;
|
||||
import util.formatting;
|
||||
import planet_effects;
|
||||
import saving;
|
||||
from bonus_effects import BonusEffect;
|
||||
|
||||
//MorphIntoImport()
|
||||
// Morphs into the first imported resource.
|
||||
class MorphIntoImport : ResourceHook {
|
||||
Document doc("This resource morphs into a copy of the first resource imported to its object.");
|
||||
|
||||
bool get_hasEffect() const override {
|
||||
return true;
|
||||
}
|
||||
|
||||
string formatEffect(Object& obj, array<const IResourceHook@>& hooks) const override {
|
||||
return locale::UNOBTANIUM_EFFECT;
|
||||
}
|
||||
|
||||
#section server
|
||||
void onTick(Object& obj, Resource@ r, double time) const override {
|
||||
uint availCnt = obj.availableResourceCount;
|
||||
if(availCnt > 1) {
|
||||
for(uint i = 0; i < availCnt; ++i) {
|
||||
auto@ type = getResource(obj.availableResourceType[i]);
|
||||
if(type !is null && type !is r.type && !type.artificial) {
|
||||
obj.removeResource(r.id);
|
||||
obj.addResource(type.id);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#section all
|
||||
};
|
||||
|
||||
|
||||
//PopResearch(<Per Billion>)
|
||||
// Produces <Per Billion> tiles worth of research per billion population.
|
||||
class PopResearch : ResourceHook {
|
||||
Document doc("Increases reseearch income by a pressure-equivalent amount based on the amount of population present.");
|
||||
Argument per_billion(AT_Decimal, doc="Amount of research to add for each population.");
|
||||
|
||||
bool get_hasEffect() const override {
|
||||
return true;
|
||||
}
|
||||
|
||||
string formatEffect(Object& obj, array<const IResourceHook@>& hooks) const override {
|
||||
return format(locale::EFFECT_POPRESEARCH, toString(1.0 / arguments[0].decimal,0));
|
||||
}
|
||||
|
||||
#section server
|
||||
void onTick(Object& obj, Resource@ r, double time) const override {
|
||||
Planet@ pl = cast<Planet>(obj);
|
||||
if(pl is null)
|
||||
return;
|
||||
|
||||
int prev = 0;
|
||||
r.data[hookIndex].retrieve(prev);
|
||||
|
||||
int amt = int(pl.Population * arguments[0].decimal);
|
||||
if(amt != prev) {
|
||||
obj.modResource(TR_Research, amt - prev);
|
||||
r.data[hookIndex].store(amt);
|
||||
}
|
||||
}
|
||||
|
||||
void load(Resource@ r, SaveFile& file) const override {
|
||||
int amt = 0;
|
||||
file >> amt;
|
||||
r.data[hookIndex].store(amt);
|
||||
}
|
||||
|
||||
void save(Resource@ r, SaveFile& file) const override {
|
||||
int amt = 0;
|
||||
r.data[hookIndex].retrieve(amt);
|
||||
file << amt;
|
||||
}
|
||||
|
||||
void onAdd(Object& obj, Resource@ r) const override {
|
||||
int amt = 0;
|
||||
r.data[hookIndex].store(amt);
|
||||
}
|
||||
|
||||
void onRemove(Object& obj, Resource@ r) const override {
|
||||
int prev = 0;
|
||||
r.data[hookIndex].retrieve(prev);
|
||||
if(prev != 0) {
|
||||
obj.modResource(TR_Research, -prev);
|
||||
r.data[hookIndex].store(0);
|
||||
}
|
||||
}
|
||||
#section all
|
||||
};
|
||||
|
||||
//AsteroidGraphics()
|
||||
// Add some floating asteroid graphics around the planet.
|
||||
class AsteroidGraphics : ResourceHook {
|
||||
Document doc("Add some floating asteroid graphics around the planet.");
|
||||
|
||||
#section server
|
||||
void applyGraphics(Object& obj, Node& node) const {
|
||||
if(obj.isPlanet) {
|
||||
PlanetNode@ plNode = cast<PlanetNode>(node);
|
||||
if(plNode !is null)
|
||||
plNode.giveAsteroids();
|
||||
}
|
||||
}
|
||||
#section all
|
||||
};
|
||||
|
||||
//AddPermanentFTLStorage(<Amount>)
|
||||
// Adds <Amount> FTL storage permanently.
|
||||
class AddPermanentFTLStorage : ResourceHook {
|
||||
Document doc("Add permanent FTL storage capacity, even if the resource is later lost.");
|
||||
Argument amount(AT_Integer, doc="Amount of FTL storage capacity to add.");
|
||||
|
||||
#section server
|
||||
void onAdd(Object& obj, Resource@ r) const override {
|
||||
if(obj.owner !is null && obj.owner.valid)
|
||||
obj.owner.modFTLCapacity(+arguments[0].integer);
|
||||
}
|
||||
|
||||
void onOwnerChange(Object& obj, Resource@ r, Empire@ prevOwner, Empire@ newOwner) const override {
|
||||
if(newOwner !is null && newOwner.valid)
|
||||
newOwner.modFTLCapacity(+arguments[0].integer);
|
||||
}
|
||||
#section all
|
||||
};
|
||||
|
||||
//VanishWhenConstructing()
|
||||
// Custom vanish mode for vanishing when the consuming object is constructing something.
|
||||
class VanishWhenConstructing : ResourceHook {
|
||||
Document doc("Used for resources with Vanish Mode: Custom. Only tick down the resource while the planet is actively using labor.");
|
||||
|
||||
#section server
|
||||
bool shouldVanish(Object& obj, Resource@ native) const override {
|
||||
Object@ target = native.exportedTo;
|
||||
if(target is null)
|
||||
@target = native.origin;
|
||||
if(target is null || !target.hasConstruction)
|
||||
return false;
|
||||
return target.isUsingLabor;
|
||||
}
|
||||
#section all
|
||||
};
|
||||
|
||||
//VanishUnlessFTLFull()
|
||||
// Custom vanish mode that doesn't vanish if the ftl storage is full.
|
||||
class VanishUnlessFTLFull : ResourceHook {
|
||||
Document doc("Used for resources with Vanish Mode: Custom. Only tick down the resource while the empire's FTL storage is not full.");
|
||||
|
||||
#section server
|
||||
bool shouldVanish(Object& obj, Resource@ native) const override {
|
||||
Empire@ owner = obj.owner;
|
||||
if(owner is null || !owner.valid)
|
||||
return false;
|
||||
if(owner.FTLStored >= owner.FTLCapacity - 0.0001)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
#section all
|
||||
};
|
||||
|
||||
//ResourceBonding(<Amount> = <Planet Resource>, ....)
|
||||
// Bonds this resource into creating other resources
|
||||
final class BondData {
|
||||
uint amount = 2;
|
||||
string resourceName;
|
||||
const ResourceType@ type;
|
||||
|
||||
int opCmp(const BondData& other) const {
|
||||
if(amount < other.amount)
|
||||
return -1;
|
||||
if(amount > other.amount)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
class ResourceBonding : ResourceHook {
|
||||
array<BondData> list;
|
||||
Document doc("Generate new resources by bonding with copies of the current resource. Arguments must be added manually.");
|
||||
|
||||
bool parse(const string& name, array<string>& args) {
|
||||
list.length = args.length;
|
||||
for(uint i = 0, cnt = args.length; i < cnt; ++i) {
|
||||
int pos = args[i].findFirst("=");
|
||||
if(pos == -1) {
|
||||
error("Invalid resource bond argument: "+escape(args[i]));
|
||||
return false;
|
||||
}
|
||||
list[i].amount = max(2, toUInt(args[i].substr(0, pos)));
|
||||
list[i].resourceName = args[i].substr(pos+1).trimmed();
|
||||
}
|
||||
list.sortAsc();
|
||||
return true;
|
||||
}
|
||||
|
||||
#section server
|
||||
void initialize(ResourceType@ type, uint index) override {
|
||||
ResourceHook::initialize(type, index);
|
||||
for(uint i = 0, cnt = list.length; i < cnt; ++i) {
|
||||
@list[i].type = getResource(list[i].resourceName);
|
||||
if(list[i].type is null)
|
||||
error("Error: ResourceBonding(): could not find resource "+list[i].resourceName);
|
||||
}
|
||||
}
|
||||
|
||||
void onGenerate(Object& obj, Resource@ r) const {
|
||||
int64 id = -1;
|
||||
r.data[hookIndex].store(id);
|
||||
}
|
||||
|
||||
void onDestroy(Object& obj, Resource@ r) const {
|
||||
int64 id = -1;
|
||||
r.data[hookIndex].retrieve(id);
|
||||
if(id != -1) {
|
||||
obj.removeResource(id);
|
||||
id = -1;
|
||||
r.data[hookIndex].store(id);
|
||||
}
|
||||
}
|
||||
|
||||
void setResource(Object& obj, Resource@ r, int64& id, any@ data, const ResourceType@ type) {
|
||||
if(type is null) {
|
||||
if(id == -1)
|
||||
return;
|
||||
}
|
||||
else if(type.id == obj.nativeResourceByID[id])
|
||||
return;
|
||||
|
||||
Object@ target;
|
||||
if(id != -1) {
|
||||
@target = obj.getNativeResourceDestinationByID(obj.owner, id);
|
||||
obj.removeResource(id);
|
||||
}
|
||||
else {
|
||||
@target = r.exportedTo;
|
||||
}
|
||||
|
||||
if(type !is null) {
|
||||
id = obj.addResource(type.id);
|
||||
|
||||
if(target !is null) {
|
||||
obj.exportResourceByID(id, target);
|
||||
if(r.exportedTo !is null)
|
||||
obj.exportResourceByID(r.id, null);
|
||||
}
|
||||
}
|
||||
else {
|
||||
id = -1;
|
||||
|
||||
if(target !is null)
|
||||
obj.exportResourceByID(r.id, target);
|
||||
}
|
||||
data.store(id);
|
||||
}
|
||||
|
||||
void nativeTick(Object& obj, Resource@ r, double time) const override {
|
||||
int64 id = -1;
|
||||
r.data[hookIndex].retrieve(id);
|
||||
|
||||
Object@ curExp;
|
||||
if(id != -1)
|
||||
@curExp = obj.getNativeResourceDestinationByID(obj.owner, id);
|
||||
|
||||
uint count = obj.getAvailableResourceAmount(r.type.id);
|
||||
if(r.exportedTo !is null && r.exportedTo is curExp && r.usable)
|
||||
count += 1;
|
||||
|
||||
if(count > 1) {
|
||||
//Should only work for the first native water
|
||||
for(uint i = 0, cnt = obj.nativeResourceCount; i < cnt; ++i) {
|
||||
if(obj.nativeResourceType[i] == r.type.id) {
|
||||
if(obj.nativeResourceId[i] != r.id)
|
||||
count = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const ResourceType@ res;
|
||||
for(uint i = 0, cnt = list.length; i < cnt; ++i) {
|
||||
if(count >= list[i].amount)
|
||||
@res = list[i].type;
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
setResource(obj, r, id, r.data[hookIndex], res);
|
||||
}
|
||||
|
||||
void load(Resource@ r, SaveFile& file) const override {
|
||||
if(file < SV_0007) {
|
||||
int64 id = 0;
|
||||
file >> id;
|
||||
}
|
||||
}
|
||||
|
||||
void nativeSave(Resource@ r, SaveFile& file) const override {
|
||||
int64 id = 0;
|
||||
r.data[hookIndex].retrieve(id);
|
||||
file << id;
|
||||
}
|
||||
|
||||
void nativeLoad(Resource@ r, SaveFile& file) const override {
|
||||
if(file >= SV_0007) {
|
||||
int64 id = 0;
|
||||
file >> id;
|
||||
r.data[hookIndex].store(id);
|
||||
}
|
||||
}
|
||||
#section all
|
||||
};
|
||||
|
||||
//LockOnExport(Destroy Invalid = True)
|
||||
// Locks the resource whenever it is first exported.
|
||||
class LockOnExport : ResourceHook {
|
||||
Document doc("When the resource is first exported, it is locked and can no longer change its destination.");
|
||||
Argument destroy_invalid(AT_Boolean, "True", doc="If set, the resource is destroyed when the locked destination is no longer valid.");
|
||||
|
||||
#section server
|
||||
void nativeTick(Object& obj, Resource@ native, double time) const {
|
||||
if(native.exportedTo !is null && !native.locked)
|
||||
obj.setResourceLocked(native.id, true);
|
||||
if(arguments[0].boolean) {
|
||||
Object@ dest = obj.getNativeResourceDestinationByID(obj.owner, native.id);
|
||||
if(dest !is null && (!dest.valid || (dest.owner !is obj.owner && dest.owner.valid)))
|
||||
obj.removeResource(native.id);
|
||||
}
|
||||
}
|
||||
#section all
|
||||
};
|
||||
|
||||
//OnTradeDeliver(<Hook>())
|
||||
// Triggers a bonus effect when delivering the resource with a civilian trade ship.
|
||||
class OnTradeDeliver : ResourceHook {
|
||||
BonusEffect@ hook;
|
||||
|
||||
Document doc("The inner hook is triggered when a civilian ship carrying this resource delivers its cargo.");
|
||||
Argument hookID(AT_Hook, "bonus_effects::BonusEffect");
|
||||
|
||||
bool instantiate() override {
|
||||
@hook = cast<BonusEffect>(parseHook(arguments[0].str, "bonus_effects::"));
|
||||
if(hook is null) {
|
||||
error("OnTradeDeliver(): could not find inner hook: "+escape(arguments[0].str));
|
||||
return false;
|
||||
}
|
||||
return ResourceHook::instantiate();
|
||||
}
|
||||
|
||||
#section server
|
||||
void onTradeDeliver(Civilian& civ, Object@ origin, Object@ target) const override {
|
||||
if(target is null || !target.isRegion)
|
||||
hook.activate(target, civ.owner);
|
||||
}
|
||||
#section all
|
||||
};
|
||||
|
||||
//OnTradeDeliver(<Hook>())
|
||||
// Triggers a bonus effect when delivering the resource with a civilian trade ship.
|
||||
class OnTradeDestroy : ResourceHook {
|
||||
BonusEffect@ hook;
|
||||
|
||||
Document doc("The inner hook is triggered when a civilian ship carrying this resource is destroyed.");
|
||||
Argument hookID(AT_Hook, "bonus_effects::BonusEffect");
|
||||
|
||||
bool instantiate() override {
|
||||
@hook = cast<BonusEffect>(parseHook(arguments[0].str, "bonus_effects::"));
|
||||
if(hook is null) {
|
||||
error("OnTradeDeliver(): could not find inner hook: "+escape(arguments[0].str));
|
||||
return false;
|
||||
}
|
||||
return ResourceHook::instantiate();
|
||||
}
|
||||
|
||||
#section server
|
||||
void onTradeDestroy(Civilian& civ, Object@ origin, Object@ target, Object@ destroyer) const override {
|
||||
hook.activate(target, civ.owner);
|
||||
}
|
||||
#section all
|
||||
};
|
||||
|
||||
class RegenSurface : ResourceHook {
|
||||
Document doc("When the planet this resource is on is generated, create a new surface with a particular size and biome count.");
|
||||
Argument width(AT_Integer, doc="Surface grid width.");
|
||||
Argument height(AT_Integer, doc="Surface grid width.");
|
||||
Argument biome_count(AT_Integer, "3", doc="Amount of biomes on the planet.");
|
||||
Argument force_biome(AT_PlanetBiome, EMPTY_DEFAULT, doc="Force a particular biome as the planet's base biome.");
|
||||
|
||||
#section server
|
||||
void onGenerate(Object& obj, Resource@ native) const {
|
||||
obj.regenSurface(width.integer, height.integer, biome_count.integer);
|
||||
if(force_biome.str.length != 0) {
|
||||
auto@ type = getBiome(force_biome.str);
|
||||
if(type !is null)
|
||||
obj.replaceFirstBiomeWith(type.id);
|
||||
}
|
||||
}
|
||||
#section all
|
||||
};
|
||||
|
||||
class ForcePlanetType : ResourceHook {
|
||||
Document doc("Force the planet this is on to be a particular type.");
|
||||
Argument planet_type(AT_Custom, doc="Planet type identifier.");
|
||||
|
||||
#section server
|
||||
void onGenerate(Object& obj, Resource@ native) const {
|
||||
if(!obj.isPlanet)
|
||||
return;
|
||||
Planet@ pl = cast<Planet>(obj);
|
||||
const PlanetType@ planetType = getPlanetType(planet_type.str);
|
||||
if(planetType is null)
|
||||
return;
|
||||
pl.PlanetType = planetType.id;
|
||||
PlanetNode@ plNode = cast<PlanetNode>(pl.getNode());
|
||||
if(plNode !is null)
|
||||
plNode.planetType = planetType.id;
|
||||
}
|
||||
#section all
|
||||
};
|
||||
|
||||
class OnNative : ResourceHook {
|
||||
GenericEffect@ hook;
|
||||
Document doc("Run this hook on the resource's native planet, regardless of whether or to where it's being exported.");
|
||||
Argument hookID("Hook", AT_Hook, "planet_effects::GenericEffect");
|
||||
|
||||
bool instantiate() override {
|
||||
@hook = cast<GenericEffect>(parseHook(hookID.str, "planet_effects::"));
|
||||
if(hook is null) {
|
||||
error("OnNative(): could not find inner hook: "+escape(hookID.str));
|
||||
return false;
|
||||
}
|
||||
return ResourceHook::instantiate();
|
||||
}
|
||||
|
||||
#section server
|
||||
void onGenerate(Object& obj, Resource@ native) const override {
|
||||
if(hook !is null)
|
||||
hook.enable(obj, native.data[hookIndex]);
|
||||
}
|
||||
|
||||
void nativeTick(Object& obj, Resource@ native, double time) const override {
|
||||
if(hook !is null)
|
||||
hook.tick(obj, native.data[hookIndex], time);
|
||||
}
|
||||
|
||||
void onDestroy(Object& obj, Resource@ native) const override {
|
||||
if(hook !is null)
|
||||
hook.disable(obj, native.data[hookIndex]);
|
||||
}
|
||||
|
||||
void nativeSave(Resource@ native, SaveFile& file) const override {
|
||||
if(hook !is null)
|
||||
hook.save(native.data[hookIndex], file);
|
||||
}
|
||||
|
||||
void nativeLoad(Resource@ native, SaveFile& file) const override {
|
||||
if(hook !is null)
|
||||
hook.load(native.data[hookIndex], file);
|
||||
}
|
||||
#section all
|
||||
};
|
||||
|
||||
class OnExportMoveLocalCargo : ResourceHook {
|
||||
Document doc("All cargo of a particular type on this object is moved to wherever this resource is exported.");
|
||||
Argument cargo_type(AT_Cargo, doc="Type of cargo to move.");
|
||||
|
||||
#section server
|
||||
void nativeTick(Object& obj, Resource@ native, double time) const override {
|
||||
Object@ targ = native.exportedTo;
|
||||
if(targ !is null && obj.getCargoStored(cargo_type.integer) > 0
|
||||
&& targ.hasCargo && targ.cargoCapacity > targ.cargoStored) {
|
||||
obj.transferCargoTo(cargo_type.integer, targ);
|
||||
}
|
||||
}
|
||||
#section all
|
||||
};
|
||||
|
||||
class NativeLevelChain : GenericEffect {
|
||||
Document doc("Change the native resource planet's level chain and requirements.");
|
||||
Argument chain(AT_Custom, doc="Chain to change to.");
|
||||
uint chainId = 0;
|
||||
|
||||
bool instantiate() override {
|
||||
chainId = getLevelChainID(chain.str);
|
||||
if(chainId == uint(-1)) {
|
||||
chainId = 0;
|
||||
error("Cannot find planet level chain: "+chain.str);
|
||||
return false;
|
||||
}
|
||||
return GenericEffect::instantiate();
|
||||
}
|
||||
|
||||
#section server
|
||||
void onGenerate(Object& obj, Resource@ r) const {
|
||||
obj.setLevelChain(chainId);
|
||||
}
|
||||
#section all
|
||||
};
|
||||
Reference in New Issue
Block a user