Files
2018-07-17 14:15:37 +02:00

1166 lines
33 KiB
ActionScript

import hooks;
import statuses;
from statuses import StatusHook;
import artifacts;
import planet_effects;
import tile_resources;
from bonus_effects import BonusEffect;
import listed_values;
from resources import MoneyType;
#section server
from empire import Creeps;
from objects.Artifact import createArtifact;
#section all
//ArtifactOnDestroy(<Type>, <Destroy Chance> = 0)
// Create an artifact of <Type> if the object gets destroyed.
class ArtifactOnDestroy : StatusHook {
Document doc("When the object carrying this status is destroyed, spawn an artifact at its position.");
Argument type(AT_Artifact, doc="Type of artifact to spawn.");
Argument destroy_chance(AT_Decimal, "0", doc="Chance that the artifact is destroyed instead of dropping from the ship.");
#section server
void onObjectDestroy(Object& obj, Status@ status, any@ data) {
if(randomd() > arguments[1].decimal) {
auto@ type = getArtifactType(arguments[0].integer);
vec3d pos = obj.position + random3d(type.physicalSize * 4.0, type.physicalSize * 12.0);
auto@ artifact = createArtifact(pos, type);
if(obj.region !is null)
artifact.orbitAround(obj.region.position);
}
}
#section all
};
//BombardEffect()
// Applies the bombardment status on a planet
class BombardEffect : StatusHook {
Document doc("Applies the bombardment effect on a planet.");
#section server
void onAddStack(Object& obj, Status@ status, StatusInstance@ instance, any@ data) {
Planet@ pl = cast<Planet>(obj);
if(pl !is null)
pl.modBombardment(1);
}
void onRemoveStack(Object& obj, Status@ status, StatusInstance@ instance, any@ data) {
Planet@ pl = cast<Planet>(obj);
if(pl !is null)
pl.modBombardment(-1);
}
#section all
};
//ReduceProductionPerStack(<Per Stack> = 1)
// Reduces any non-money income by <Per Stack> per stack.
class ReduceProductionPerStack : StatusHook {
Document doc("Reduce random non-money incomes by a pressure-equivalent amount for every stack of this status.");
Argument per_stack(AT_Decimal, "1", doc="Amount of pressure-equivalent income to remove per stack.");
#section server
void onCreate(Object& obj, Status@ status, any@ data) override {
if(!obj.isPlanet)
return;
array<double> current(TR_COUNT, 0.0);
data.store(@current);
}
void onDestroy(Object& obj, Status@ status, any@ data) override {
if(!obj.isPlanet)
return;
array<double>@ current;
data.retrieve(@current);
for(uint i = 1; i < TR_COUNT; ++i) {
if(current[i] > 0)
obj.modResource(i, +current[i]);
}
@current = null;
data.store(@current);
}
bool onTick(Object& obj, Status@ status, any@ data, double time) override {
if(!obj.isPlanet)
return true;
double target = arguments[0].decimal * double(status.stacks);
double currentTotal = 0.0;
array<double>@ current;
data.retrieve(@current);
for(uint i = 1; i < TR_COUNT; ++i) {
double prod = obj.getResourceProduction(i);
double cur = current[i];
if(prod < 0) {
if(cur > 0) {
double amt = min(-prod, cur);
obj.modResource(i, +amt);
prod += amt;
cur -= amt;
}
}
currentTotal += cur;
}
if(currentTotal < target - 0.001) {
uint base = randomi(0, TR_COUNT-1);
for(uint i = 0; i < TR_COUNT-1 && currentTotal < target; ++i) {
uint index = 1 + (base + i) % (TR_COUNT-1);
double prod = obj.getResourceProduction(index);
double cur = current[index];
if(prod > 0) {
double amt = min(prod, target - currentTotal);
obj.modResource(index, -amt);
currentTotal += amt;
current[index] += amt;
}
}
}
else if(currentTotal > target + 0.001) {
uint base = randomi(0, TR_COUNT-1);
for(uint i = 0; i < TR_COUNT-1 && currentTotal > target; ++i) {
uint index = 1 + (base + i) % (TR_COUNT-1);
double prod = obj.getResourceProduction(index);
double cur = current[index];
if(cur > 0) {
double amt = min(cur, currentTotal - target);
obj.modResource(index, +amt);
currentTotal -= amt;
current[index] -= amt;
}
}
}
return true;
}
void save(Status@ status, any@ data, SaveFile& file) override {
array<double>@ current;
data.retrieve(@current);
for(uint i = 1; i < TR_COUNT; ++i)
file << current[i];
}
void load(Status@ status, any@ data, SaveFile& file) override {
array<double> current(TR_COUNT, 0.0);
data.store(@current);
for(uint i = 1; i < TR_COUNT; ++i)
file >> current[i];
}
#section all
};
//ApplyToFlagships()
// Only apply this status to flagships.
class ApplyToFlagships : StatusHook {
Document doc("When this status is added to a system, it applies to flagships only.");
bool shouldApply(Empire@ emp, Region@ region, Object@ obj) const override {
return obj !is null && obj.isShip && obj.hasLeaderAI;
}
};
class ApplyToPlanets : StatusHook {
Document doc("When this status is added to a system, it applies to planets only.");
bool shouldApply(Empire@ emp, Region@ region, Object@ obj) const override {
return obj !is null && obj.isPlanet;
}
};
class ApplyToUncolonizedPlanets : StatusHook {
Document doc("When this status is added to a system, it applies to uncolonized planets only.");
bool shouldApply(Empire@ emp, Region@ region, Object@ obj) const override {
return obj !is null && obj.isPlanet && (obj.owner is null || !obj.owner.valid);
}
};
class ApplyToColonizedPlanets : StatusHook {
Document doc("When this status is added to a system, it applies to colonized planets only.");
bool shouldApply(Empire@ emp, Region@ region, Object@ obj) const override {
return obj !is null && obj.isPlanet && (obj.owner !is null && obj.owner.valid);
}
};
tidy final class RepeatStacks : StatusHook {
Document doc("Repeat a generic hook for every stack present.");
Argument code(AT_Hook, "status_effects::IStatusHook", doc="Hook to repeat.");
Argument per_stack(AT_Integer, "1", doc="Amount of times to repeat per stack.");
Argument max_repeats(AT_Integer, "0", doc="If set to more than 0, the hook will never repeat more than that amount of times.");
IStatusHook@ hook;
bool instantiate() override {
@hook = cast<IStatusHook>(parseHook(arguments[0].str, "status_effects::"));
if(hook is null) {
error("RepeatStacks(): could not find inner hook: "+escape(arguments[0].str));
return false;
}
return StatusHook::instantiate();
}
#section server
void onCreate(Object& obj, Status@ status, any@ data) override {
array<any> datlist;
data.store(@datlist);
}
bool onTick(Object& obj, Status@ status, any@ data, double time) {
array<any>@ datlist;
data.retrieve(@datlist);
if(datlist is null)
return true;
uint oldCnt = datlist.length;
uint newCnt = per_stack.integer * status.stacks;
if(max_repeats.integer > 0 && newCnt > uint(max_repeats.integer))
newCnt = max_repeats.integer;
for(uint i = newCnt; i < oldCnt; ++i)
hook.onDestroy(obj, status, datlist[i]);
datlist.length = newCnt;
for(uint i = oldCnt; i < newCnt; ++i)
hook.onCreate(obj, status, datlist[i]);
for(uint i = 0; i < newCnt; ++i)
hook.onTick(obj, status, datlist[i], time);
return true;
}
void onDestroy(Object& obj, Status@ status, any@ data) override {
array<any>@ datlist;
data.retrieve(@datlist);
if(datlist is null)
return;
for(uint i = 0, cnt = datlist.length; i < cnt; ++i)
hook.onDestroy(obj, status, datlist[i]);
datlist.length = 0;
@datlist = null;
data.store(@datlist);
}
bool onOwnerChange(Object& obj, Status@ status, any@ data, Empire@ prevOwner, Empire@ newOwner) override {
array<any>@ datlist;
data.retrieve(@datlist);
if(datlist is null)
return true;
for(uint i = 0, cnt = datlist.length; i < cnt; ++i)
hook.onOwnerChange(obj, status, datlist[i], prevOwner, newOwner);
return true;
}
bool onRegionChange(Object& obj, Status@ status, any@ data, Region@ prevRegion, Region@ newRegion) override {
array<any>@ datlist;
data.retrieve(@datlist);
if(datlist is null)
return true;
for(uint i = 0, cnt = datlist.length; i < cnt; ++i)
hook.onRegionChange(obj, status, datlist[i], prevRegion, newRegion);
return true;
}
void save(Status@ status, any@ data, SaveFile& file) override {
array<any>@ datlist;
data.retrieve(@datlist);
uint cnt = 0;
if(datlist is null) {
file << cnt;
return;
}
cnt = datlist.length;
file << cnt;
for(uint i = 0; i < cnt; ++i)
hook.save(status, datlist[i], file);
}
void load(Status@ status, any@ data, SaveFile& file) override {
array<any> datlist;
uint cnt = 0;
file >> cnt;
datlist.length = cnt;
for(uint i = 0; i < cnt; ++i)
hook.load(status, datlist[i], file);
data.store(@datlist);
}
#section all
};
tidy final class TriggerCreate : StatusHook {
BonusEffect@ eff;
Argument hook(AT_Hook, "bonus_effects::BonusEffect", doc="Hook to call.");
bool instantiate() override {
@eff = cast<BonusEffect>(parseHook(hook.str, "bonus_effects::"));
if(eff is null) {
error("TriggerCreate(): could not find inner hook: "+escape(hook.str));
return false;
}
return StatusHook::instantiate();
}
#section server
void onCreate(Object& obj, Status@ status, any@ data) override {
eff.activate(obj, obj.owner);
}
#section all
};
class ConditionSinglePressureType : StatusHook {
Document doc("This condition can only be on planets with a primary resource with a single pressure type.");
bool shouldApply(Empire@ emp, Region@ region, Object@ obj) const override {
if(!obj.hasResources)
return false;
auto@ type = getResource(obj.primaryResourceType);
if(type is null)
return false;
uint count = 0;
for(uint i = 0, cnt = type.tilePressure.length; i < cnt; ++i) {
if(type.tilePressure[i] > 0)
count += 1;
}
return count == 1;
}
};
class ConditionBiome : StatusHook {
Document doc("This condition can only be on planets that have a particular biome.");
Argument biome(AT_PlanetBiome, doc="Biome to check for.");
#section server
bool shouldApply(Empire@ emp, Region@ region, Object@ obj) const override {
if(!obj.hasSurfaceComponent)
return false;
return obj.hasBiome(biome.integer);
}
#section all
};
class ConditionMaxLevel : StatusHook {
Document doc("This effect only applies on planets with resources at or below a maximum level.");
Argument max_level(AT_Integer, doc="Highest level this can apply to.");
bool shouldApply(Empire@ emp, Region@ region, Object@ obj) const override {
if(!obj.hasResources)
return false;
auto@ type = getResource(obj.primaryResourceType);
if(type is null)
return false;
return int(type.level) <= max_level.integer;
}
};
class ConditionMinLevel : StatusHook {
Document doc("This effect only applies on planets with resources at or above a minimum level.");
Argument min_level(AT_Integer, doc="Lowest level this can apply to.");
bool shouldApply(Empire@ emp, Region@ region, Object@ obj) const override {
if(!obj.hasResources)
return false;
auto@ type = getResource(obj.primaryResourceType);
if(type is null)
return false;
return int(type.level) >= min_level.integer;
}
};
class ConditionDLC : StatusHook {
Document doc("This condition can only be generated when a dlc is present.");
Argument dlc(AT_Custom, doc="DLC to check for.");
bool activated = false;
bool instantiate() override {
if(!StatusHook::instantiate())
return false;
activated = hasDLC(dlc.str);
return true;
}
bool shouldApply(Empire@ emp, Region@ region, Object@ obj) const override {
return activated;
}
};
class ConditionMinPressure : StatusHook {
Document doc("This effect only applies on planets with resources with at least a certain amount of pressure.");
Argument min_pressure(AT_Integer, doc="Least amount of pressure this can apply to.");
bool shouldApply(Empire@ emp, Region@ region, Object@ obj) const override {
if(!obj.hasResources)
return false;
auto@ type = getResource(obj.primaryResourceType);
if(type is null)
return false;
return type.totalPressure >= min_pressure.integer;
}
};
class ConditionMaxPressure : StatusHook {
Document doc("This effect only applies on planets with resources up to a certain amount of pressure.");
Argument max_pressure(AT_Integer, doc="Maximum amount of pressure this can apply to.");
bool shouldApply(Empire@ emp, Region@ region, Object@ obj) const override {
if(!obj.hasResources)
return false;
auto@ type = getResource(obj.primaryResourceType);
if(type is null)
return false;
return type.totalPressure <= max_pressure.integer;
}
};
tidy final class TriggerColonized : StatusHook {
BonusEffect@ eff;
Argument hook(AT_Hook, "bonus_effects::BonusEffect", doc="Hook to call.");
bool instantiate() override {
@eff = cast<BonusEffect>(parseHook(hook.str, "bonus_effects::"));
if(eff is null) {
error("TriggerColonized(): could not find inner hook: "+escape(hook.str));
return false;
}
return StatusHook::instantiate();
}
#section server
bool onTick(Object& obj, Status@ status, any@ data, double time) override {
if(!obj.isPlanet)
return false;
Empire@ owner = obj.owner;
if(owner is null || !owner.valid)
return true;
if(obj.population < 0.9999)
return true;
eff.activate(obj, owner);
return false;
}
#section all
};
class VisibleToOriginEmpire : StatusHook {
Document doc("The object is always visible to the origin empire of this status.");
#section server
bool onTick(Object& obj, Status@ status, any@ data, double time) override {
if(status.originEmpire !is null)
obj.donatedVision |= status.originEmpire.mask;
return true;
}
#section all
};
class GivesVisionToOriginEmpire : StatusHook {
Document doc("The object gives vision in the system it is in to the origin empire of this status.");
#section server
void onCreate(Object& obj, Status@ status, any@ data) override {
Region@ reg = obj.region;
if(status.originEmpire !is null && reg !is null)
reg.grantVision(status.originEmpire);
}
void onDestroy(Object& obj, Status@ status, any@ data) override {
Region@ reg = obj.region;
if(status.originEmpire !is null && reg !is null)
reg.revokeVision(status.originEmpire);
}
bool onRegionChange(Object& obj, Status@ status, any@ data, Region@ prevRegion, Region@ newRegion) override {
if(status.originEmpire !is null && prevRegion !is null)
prevRegion.revokeVision(status.originEmpire);
if(status.originEmpire !is null && newRegion !is null)
newRegion.grantVision(status.originEmpire);
return true;
}
#section all
};
tidy final class RoamSystems : StatusHook {
Document doc("Roam through systems, triggering a bonus hook for each visited.");
Argument hook(AT_Hook, "bonus_effects::BonusEffect");
Argument origin_empires(AT_Boolean, "False", doc="Whether to only visit empires that are related to the origin object.");
#section server
BonusEffect@ eff;
bool instantiate() override {
@eff = cast<BonusEffect>(parseHook(hook.str, "bonus_effects::"));
if(eff is null) {
error("RoamOccupiedSystems(): could not find inner hook: "+escape(hook.str));
return false;
}
return StatusHook::instantiate();
}
void onCreate(Object& obj, Status@ status, any@ data) override {
if(status.originObject !is null) {
obj.addGotoOrder(status.originObject, true);
if(origin_empires.boolean) {
uint mask = 0;
if(status.originObject.isRegion)
mask = cast<Region>(status.originObject).PlanetsMask;
else {
Empire@ owner = status.originObject.owner;
if(owner !is null)
mask = owner.mask;
}
data.store(mask);
}
}
}
bool onTick(Object& obj, Status@ status, any@ data, double time) override {
if(!obj.valid)
return false;
if(obj.orderCount == 0) {
//Trigger for this system
Region@ reg = obj.region;
if(reg !is null)
eff.activate(obj.region, status.originEmpire);
//Find next system to go to
if(reg is null) {
if(status.originObject is null || !status.originObject.isRegion)
return false;
obj.addGotoOrder(status.originObject, true);
}
uint mask = ~0;
if(origin_empires.boolean)
data.retrieve(mask);
auto@ sys = getSystem(reg);
array<SystemDesc@> poss;
for(uint i = 0, cnt = sys.adjacent.length; i < cnt; ++i) {
auto@ other = getSystem(sys.adjacent[i]);
if(mask == ~0 || other.object.PlanetsMask & mask != 0)
poss.insertLast(other);
}
SystemDesc@ target;
if(poss.length == 0) {
if(sys.adjacent.length == 0) {
if(status.originObject is null || !status.originObject.isRegion)
return false;
obj.addGotoOrder(status.originObject, true);
return true;
}
else {
@target = getSystem(sys.adjacent[randomi(0, sys.adjacent.length-1)]);
}
}
else {
@target = poss[randomi(0, poss.length-1)];
}
obj.addGotoOrder(target.object, true);
}
return true;
}
void save(Status@ status, any@ data, SaveFile& file) override {
uint mask = 0;
data.retrieve(mask);
file << mask;
}
void load(Status@ status, any@ data, SaveFile& file) override {
uint mask = 0;
file >> mask;
data.store(mask);
}
#section all
};
class RemoveOnOwnerChange : StatusHook {
Document doc("This status is removed when the object changes owners.");
#section server
bool onOwnerChange(Object& obj, Status@ status, any@ data, Empire@ prevOwner, Empire@ newOwner) override {
return false;
}
#section all
};
class IsTriggerStatus : StatusHook {
Document doc("This status is removed immediately after triggering.");
#section server
bool onTick(Object& obj, Status@ status, any@ data, double time) override {
return false;
}
#section all
};
class RemoveIfInCombat : StatusHook {
Document doc("Remove this status when the object is in combat.");
Argument timer(AT_Decimal, "0", doc="Time to wait after creation of the status before checking for combat.");
Argument skip_timer_nocombat(AT_Boolean, "True", doc="Whether to skip the wait timer if the ship was not in combat when the status was created.");
#section server
void onCreate(Object& obj, Status@ status, any@ data) override {
double delay = timer.decimal;
if(skip_timer_nocombat.boolean && !obj.inCombat)
delay = 0;
data.store(delay);
}
bool onTick(Object& obj, Status@ status, any@ data, double time) override {
double delay = 0;
data.retrieve(delay);
if(delay > 0) {
delay -= time;
data.store(delay);
}
else {
if(obj.inCombat)
return false;
}
return true;
}
void save(Status@ status, any@ data, SaveFile& file) override {
double delay = 0;
data.retrieve(delay);
file << delay;
}
void load(Status@ status, any@ data, SaveFile& file) override {
double delay = 0;
file >> delay;
data.store(delay);
}
#section all
};
class RemoveOnAttackOrder : StatusHook {
Document doc("Remove the status when the ship receives an attack order.");
Argument remove_when_range(AT_Boolean, "True", doc="Only remove the status when in range.");
#section server
bool onTick(Object& obj, Status@ status, any@ data, double time) override {
if(!obj.hasLeaderAI)
return true;
Object@ targ = obj.getAttackTarget();
if(targ is null)
return true;
if(!remove_when_range.boolean)
return false;
return obj.getAttackDistance() > obj.getEngagementRange() + obj.radius + targ.radius;
}
#section all
};
class EnergyMaintenance : StatusHook {
Document doc("This status requires an amount of energy per second in maintenance, or it will fail.");
Argument amount(AT_Decimal, "0", doc="Base amount of energy per second it costs.");
Argument per_shipsize(AT_Decimal, "0", doc="When on a ship, increase the energy per second by the ship design size multiplied by this.");
#section server
void onCreate(Object& obj, Status@ status, any@ data) override {
double amt = amount.decimal;
if(per_shipsize.decimal != 0 && obj.isShip)
amt += cast<Ship>(obj).blueprint.design.size * per_shipsize.decimal;
if(obj.owner !is null)
obj.owner.modEnergyUse(amt);
data.store(amt);
}
void onDestroy(Object& obj, Status@ status, any@ data) override {
double amt = 0;
data.retrieve(amt);
if(obj.owner !is null)
obj.owner.modEnergyUse(-amt);
}
bool onOwnerChange(Object& obj, Status@ status, any@ data, Empire@ prevOwner, Empire@ newOwner) override {
double amt = 0;
data.retrieve(amt);
if(prevOwner !is null)
prevOwner.modEnergyUse(-amt);
if(newOwner !is null)
newOwner.modEnergyUse(amt);
return true;
}
bool onTick(Object& obj, Status@ status, any@ data, double time) override {
if(obj.owner !is null && obj.owner.EnergyShortage)
return false;
return true;
}
void save(Status@ status, any@ data, SaveFile& file) override {
double amt = 0;
data.retrieve(amt);
file << amt;
}
void load(Status@ status, any@ data, SaveFile& file) override {
double amt = 0;
file >> amt;
data.store(amt);
}
#section all
};
class ConstructOrbital : StatusHook {
Document doc("This status automatically constructs the orbital it is on over time.");
Argument duration(AT_Decimal, doc="Time that the orbital takes before it finishes construction.");
Argument remove_status(AT_Boolean, "True", doc="Whether to remove this status after the construction finishes.");
#section server
void onCreate(Object& obj, Status@ status, any@ data) override {
double timer = 0;
data.store(timer);
if(obj.isOrbital)
cast<Orbital>(obj).setDisabled(true);
}
void onDestroy(Object& obj, Status@ status, any@ data) override {
if(obj.isOrbital)
cast<Orbital>(obj).setDisabled(false);
}
bool onTick(Object& obj, Status@ status, any@ data, double time) override {
double timer = 0;
data.retrieve(timer);
if(timer < duration.decimal) {
timer += time;
data.store(timer);
if(obj.isOrbital)
cast<Orbital>(obj).setBuildPct(timer / duration.decimal);
if(timer >= duration.decimal) {
if(obj.isOrbital)
cast<Orbital>(obj).setDisabled(false);
if(remove_status.boolean)
return false;
}
}
return true;
}
void save(Status@ status, any@ data, SaveFile& file) override {
double time = 0;
data.retrieve(time);
file << time;
}
void load(Status@ status, any@ data, SaveFile& file) override {
double time = 0;
file >> time;
data.store(time);
}
#section all
};
class AbandonOnDisableIfSameOrigin : StatusHook {
Document doc("If this status has the same origin empire as the object's owner, abandon the planet when it gets disabled.");
#section server
void onDestroy(Object& obj, Status@ status, any@ data) override {
if(obj.hasSurfaceComponent) {
if(obj.owner is status.originEmpire)
obj.forceAbandon();
}
}
#section all
};
class ForcePopulationToStacks : StatusHook {
Document doc("Force the planet to always have the same amount of population as this status' stack count.");
Argument factor(AT_Decimal, "1.0", doc="Multiplication factor to stack count.");
#section server
bool onTick(Object& obj, Status@ status, any@ data, double time) override {
if(obj.hasSurfaceComponent) {
double curPop = obj.population;
double targPop = double(status.stacks) * factor.decimal;
if(curPop != targPop)
obj.addPopulation(targPop - curPop);
}
return true;
}
#section all
};
tidy final class EnabledData {
bool enabled = false;
any data;
};
tidy final class OnOriginEmpire : StatusHook {
EmpireEffect@ eff;
Document doc("Apply an effect on the origin empire of the status, if it has one.");
Argument hookID("Hook", AT_Hook, "empire_effects::EmpireEffect");
Argument allow_same(AT_Boolean, "True", doc="Whether to also apply the effect if the origin empire is the object's current owner.");
bool instantiate() override {
@eff = cast<EmpireEffect>(parseHook(hookID.str, "empire_effects::", required=false));
if(eff is null) {
error("OnOriginEmpire(): could not find inner hook: "+escape(hookID.str));
return false;
}
return StatusHook::instantiate();
}
bool check(Object& obj, Status@ status) {
if(status.originEmpire is null)
return false;
if(!allow_same.boolean && obj.owner is status.originEmpire)
return false;
return true;
}
void onCreate(Object& obj, Status@ status, any@ data) {
EnabledData enab;
data.store(@enab);
}
void onDestroy(Object& obj, Status@ status, any@ data) {
EnabledData@ enab;
data.retrieve(@enab);
if(enab !is null && enab.enabled)
eff.disable(status.originEmpire, enab.data);
}
bool onTick(Object& obj, Status@ status, any@ data, double time) {
EnabledData@ enab;
data.retrieve(@enab);
bool shouldEnable = check(obj, status);
if(enab.enabled != shouldEnable) {
if(shouldEnable) {
eff.enable(status.originEmpire, enab.data);
enab.enabled = true;
}
else {
eff.disable(status.originEmpire, enab.data);
enab.enabled = false;
}
}
if(enab.enabled)
eff.tick(status.originEmpire, enab.data, time);
return true;
}
void save(Status@ status, any@ data, SaveFile& file) {
EnabledData@ enab;
data.retrieve(@enab);
file << enab.enabled;
if(enab.enabled)
eff.save(enab.data, file);
}
void load(Status@ status, any@ data, SaveFile& file) {
EnabledData enab;
data.store(@enab);
file >> enab.enabled;
if(enab.enabled)
eff.load(enab.data, file);
}
};
tidy final class TriggerWithOriginEmpire : StatusHook {
Document doc("Trigger the effect with the empire set to the origin empire of this status.");
Argument hookID(AT_Hook, "bonus_effects::BonusEffect", doc="Hook to call.");
BonusEffect@ hook;
bool instantiate() override {
if(hookID.str != "bonus_effects::BonusEffect")
@hook = cast<BonusEffect>(parseHook(hookID.str, "bonus_effects::"));
return StatusHook::instantiate();
}
#section server
void onCreate(Object& obj, Status@ status, any@ data) override {
if(hook !is null)
hook.activate(obj, status.originEmpire);
}
#section all
};
tidy final class RemoveOnWarWithOriginEmpire : StatusHook {
Document doc("This status is removed when the empire goes to war with the origin empire.");
Argument trigger_owner(AT_Hook, "bonus_effects::EmpireTrigger", doc="Hook to call on the owner empire when it happens.");
Argument trigger_origin(AT_Hook, "bonus_effects::EmpireTrigger", doc="Hook to call on the origin empire when it happens.");
BonusEffect@ ownerHook;
BonusEffect@ originHook;
bool instantiate() override {
if(trigger_owner.str != "bonus_effects::EmpireTrigger")
@ownerHook = cast<BonusEffect>(parseHook(trigger_owner.str, "bonus_effects::"));
if(trigger_origin.str != "bonus_effects::EmpireTrigger")
@originHook = cast<BonusEffect>(parseHook(trigger_origin.str, "bonus_effects::"));
return StatusHook::instantiate();
}
#section server
bool onTick(Object& obj, Status@ status, any@ data, double time) override {
if(status.originEmpire is null)
return true;
if(obj.owner.isHostile(status.originEmpire)) {
if(ownerHook !is null)
ownerHook.activate(obj, obj.owner);
if(originHook !is null)
originHook.activate(obj, status.originEmpire);
return false;
}
return true;
}
#section all
};
class GivePlanetIncomeToOriginEmpire : StatusHook {
Document doc("Give a percentage of this planet's income to the origin empire.");
Argument percentage(AT_Decimal, doc="Percentage of planet income to give.");
Argument allow_same(AT_Boolean, "False", doc="Whether to also apply the effect if the origin empire is the object's current owner.");
#section server
void onCreate(Object& obj, Status@ status, any@ data) {
int amount = 0;
data.store(amount);
}
void onDestroy(Object& obj, Status@ status, any@ data) {
int amount = 0;
data.retrieve(amount);
if(amount != 0 && status.originEmpire !is null)
status.originEmpire.modTotalBudget(-amount, MoT_Misc);
}
bool onTick(Object& obj, Status@ status, any@ data, double time) {
if(status.originEmpire is null)
return true;
int curAmount = 0;
data.retrieve(curAmount);
int newAmount = 0;
if(obj.owner !is status.originEmpire || allow_same.boolean) {
if(obj.hasSurfaceComponent)
newAmount = max(double(obj.income) * percentage.decimal, 0.0);
}
if(newAmount != curAmount) {
status.originEmpire.modTotalBudget(newAmount - curAmount, MoT_Misc);
data.store(newAmount);
}
return true;
}
void save(Status@ status, any@ data, SaveFile& file) {
int amount = 0;
data.retrieve(amount);
file << amount;
}
void load(Status@ status, any@ data, SaveFile& file) {
int amount = 0;
file >> amount;
data.store(amount);
}
#section all
};
class OnlyOriginEmpireCanCapture : StatusHook {
Document doc("While this status is active, only the origin empire can capture the planet through siege.");
Argument allow_same(AT_Boolean, "True", doc="Whether to allow the protection even if the owner is the same as the origin.");
#section server
bool onTick(Object& obj, Status@ status, any@ data, double time) {
uint mask = ~0;
if(status.originEmpire !is null) {
if(allow_same.boolean || obj.owner !is status.originEmpire)
mask &= ~status.originEmpire.mask;
}
if(mask != 0 && obj.hasSurfaceComponent)
obj.protectFrom(mask);
return true;
}
void onDestroy(Object& obj, Status@ status, any@ data) {
if(obj.hasSurfaceComponent)
obj.clearProtectedFrom();
}
#section all
};
class SiegeFromOrigin : StatusHook {
Document doc("This status is considered equivalent to a sieging fleet in orbit for the origin empire of the status.");
#section server
bool onTick(Object& obj, Status@ status, any@ data, double time) override {
if(status.originEmpire !is null) {
obj.fakeSiege(status.originEmpire.mask);
if(obj.getLoyaltyFacing(status.originEmpire) <= 0)
obj.annex(status.originEmpire);
}
return true;
}
void onDestroy(Object& obj, Status@ status, any@ data) {
if(status.originEmpire !is null)
obj.clearFakeSiege(status.originEmpire.mask);
}
#section all
};
class RemoveIfNotWar : StatusHook {
Document doc("Remove this status if the owner is not at war with the status origin empire.");
#section server
bool onTick(Object& obj, Status@ status, any@ data, double time) override {
if(status.originEmpire !is null) {
if(!status.originEmpire.isHostile(obj.owner))
return false;
}
return true;
}
#section all
};
class RemoveIfNoRemnantsInSystem : StatusHook {
Document doc("Remove this status if there are no remnants in the system.");
Argument timer(AT_Decimal, "30", doc="Don't remove the status for this amount of time after it is created.");
#section server
void onCreate(Object& obj, Status@ status, any@ data) override {
double delay = timer.decimal;
data.store(delay);
}
bool onTick(Object& obj, Status@ status, any@ data, double time) override {
double delay = 0;
data.retrieve(delay);
if(delay > 0) {
delay -= time;
data.store(delay);
}
else {
Region@ reg = obj.region;
if(reg !is null && reg.getStrength(Creeps) <= 0)
return false;
}
return true;
}
void save(Status@ status, any@ data, SaveFile& file) override {
double delay = 0;
data.retrieve(delay);
file << delay;
}
void load(Status@ status, any@ data, SaveFile& file) override {
double delay = 0;
file >> delay;
data.store(delay);
}
#section all
};
class ProduceNativePressurePct : StatusHook {
Document doc("Produce a percentage of the native resource's pressure.");
Argument base(AT_Decimal, "0", doc="Percentage to produce.");
Argument per_stack(AT_Decimal, "0", doc="Percentage to add per stack.");
#section server
void onCreate(Object& obj, Status@ status, any@ data) override {
double pct = 0.0;
data.store(pct);
}
bool onTick(Object& obj, Status@ status, any@ data, double time) override {
double prevPct = 0.0;
data.retrieve(prevPct);
double newPct = base.decimal;
newPct += per_stack.decimal * status.stacks;
if(newPct != prevPct) {
auto@ resource = getResource(obj.primaryResourceType);
if(resource is null) {
newPct = 0.0;
}
else {
for(uint i = 0; i < TR_COUNT; ++i) {
double prev = double(resource.tilePressure[i]) * prevPct;
double cur = double(resource.tilePressure[i]) * newPct;
if(prev != cur)
obj.modResource(i, cur - prev);
}
}
data.store(newPct);
}
return true;
}
void onDestroy(Object& obj, Status@ status, any@ data) override {
double pct = 0;
data.retrieve(pct);
auto@ resource = getResource(obj.primaryResourceType);
if(resource !is null) {
for(uint i = 0; i < TR_COUNT; ++i) {
double prev = double(resource.tilePressure[i]) * pct;
obj.modResource(i, -prev);
}
}
}
void save(Status@ status, any@ data, SaveFile& file) override {
double pct = 0;
data.retrieve(pct);
file << pct;
}
void load(Status@ status, any@ data, SaveFile& file) override {
double pct = 0;
file >> pct;
data.store(pct);
}
#section all
};
tidy final class OnRemoveStatusAttribLT : StatusHook {
Document doc("When this status is removed and an attribute is lower than a specified value, trigger the effect.");
Argument attribute(AT_EmpAttribute, doc="Attribute to check.");
Argument value(AT_Decimal, doc="Value to check.");
BonusEffect@ eff;
Argument hook(AT_Hook, "bonus_effects::BonusEffect", doc="Hook to call.");
bool instantiate() override {
@eff = cast<BonusEffect>(parseHook(hook.str, "bonus_effects::"));
if(eff is null) {
error("TriggerCreate(): could not find inner hook: "+escape(hook.str));
return false;
}
return StatusHook::instantiate();
}
#section server
void onDestroy(Object& obj, Status@ status, any@ data) {
if(obj.owner.getAttribute(attribute.integer) < value.decimal)
eff.activate(obj, obj.owner);
}
#section all
};