import hooks; import influence; import attributes; import systems; import saving; from bonus_effects import BonusEffect; import empire_effects; #section server from influence_global import modInfluenceStackSize, getLastInfluenceVoteId, getActiveInfluenceVotes, getInfluenceVotesSince, getLastInfluenceEffectId, getInfluenceEffectsSince, getSenateLeader; #section all #section server tidy final class VisionEffect { array currentVision; array empires; uint mask = 0; //A new system that matches 'mask' should be //given vision over roughly every interval seconds. double interval = 30.0; double intervalMod = 0.8; double minInterval = 2.5; double maxInterval = 40.0; double timer = 0.0; uint sysIndex = 0; void save(SaveFile& file) { file << sysIndex; file << timer; file << interval; file << intervalMod; file << minInterval; file << maxInterval; file << mask; uint cnt = currentVision.length; file << cnt; for(uint i = 0; i < cnt; ++i) file << currentVision[i]; cnt = empires.length; file << cnt; for(uint i = 0; i < cnt; ++i) file << empires[i]; } void load(SaveFile& file) { if(file < SV_0015) { init(); return; } file >> sysIndex; file >> timer; file >> interval; file >> intervalMod; file >> minInterval; file >> maxInterval; file >> mask; uint cnt = 0; file >> cnt; currentVision.length = cnt; for(uint i = 0; i < cnt; ++i) file >> currentVision[i]; file >> cnt; empires.length = cnt; for(uint i = 0; i < cnt; ++i) file >> empires[i]; } void init() { currentVision.length = systemCount; for(uint i = 0, cnt = currentVision.length; i < cnt; ++i) currentVision[i] = false; } void update(double time) { timer -= time; if(timer >= 0.0) return; double sysInt = interval / double(currentVision.length); const SystemDesc@ found; timer -= sysInt; while(timer < 0) { timer += sysInt; auto@ sys = getSystem(sysIndex); if(!currentVision[sysIndex]) { if(sys.object.TradeMask & mask != 0) { @found = sys; break; } } else { if(sys.object.TradeMask & mask == 0) { for(uint i = 0, cnt = empires.length; i < cnt; ++i) sys.object.revokeVision(empires[i]); currentVision[sysIndex] = false; } } sysIndex = (sysIndex + 1) % currentVision.length; if(sysIndex == 0 && found is null) { interval = min(interval / intervalMod, maxInterval); timer = sysInt; return; } } if(found !is null) { for(uint i = 0, cnt = empires.length; i < cnt; ++i) found.object.grantVision(empires[i]); currentVision[sysIndex] = true; timer = sysInt * double(currentVision.length - sysIndex); interval = max(interval * intervalMod, minInterval); } else { timer = sysInt; } } void disable() { for(uint n = 0, ncnt = currentVision.length; n < ncnt; ++n) { if(currentVision[n]) { auto@ sys = getSystem(n); for(uint i = 0, cnt = empires.length; i < cnt; ++i) sys.object.revokeVision(empires[i]); } } } }; #section all class GrantVisionOver : InfluenceEffectEffect { Document doc("Grants vision over a target empire."); Argument targ(TT_Empire); #section server void onStart(InfluenceEffect@ eff) const override { Empire@ to = arguments[0].fromTarget(eff.targets).emp; VisionEffect ve; ve.init(); ve.empires.insertLast(eff.owner); ve.mask = to.mask; eff.data[hookIndex].store(@ve); } bool onTick(InfluenceEffect@ eff, double time) const override { VisionEffect@ ve; eff.data[hookIndex].retrieve(@ve); if(ve is null) return true; ve.update(time); return false; } void onEnd(InfluenceEffect@ eff) const override { VisionEffect@ ve; eff.data[hookIndex].retrieve(@ve); if(ve !is null) ve.disable(); @ve = null; eff.data[hookIndex].store(@ve); } void save(InfluenceEffect@ effect, SaveFile& file) const { VisionEffect@ ve; effect.data[hookIndex].retrieve(@ve); ve.save(file); } void load(InfluenceEffect@ effect, SaveFile& file) const { VisionEffect ve; effect.data[hookIndex].store(@ve); ve.load(file); } #section all }; class GrantEveryoneVisionOver : InfluenceEffectEffect { Document doc("Grants vision over a empire to everyone."); Argument targ(TT_Empire); #section server void onStart(InfluenceEffect@ eff) const override { Empire@ to = arguments[0].fromTarget(eff.targets).emp; VisionEffect ve; ve.init(); ve.mask = to.mask; for(uint i = 0, cnt = getEmpireCount(); i < cnt; ++i) { auto@ emp = getEmpire(i); if(emp.major && emp !is to) ve.empires.insertLast(emp); } eff.data[hookIndex].store(@ve); } bool onTick(InfluenceEffect@ eff, double time) const override { VisionEffect@ ve; eff.data[hookIndex].retrieve(@ve); if(ve is null) return true; ve.update(time); return false; } void onEnd(InfluenceEffect@ eff) const override { VisionEffect@ ve; eff.data[hookIndex].retrieve(@ve); if(ve !is null) ve.disable(); @ve = null; eff.data[hookIndex].store(@ve); } void save(InfluenceEffect@ effect, SaveFile& file) const { VisionEffect@ ve; effect.data[hookIndex].retrieve(@ve); ve.save(file); } void load(InfluenceEffect@ effect, SaveFile& file) const { VisionEffect ve; effect.data[hookIndex].store(@ve); ve.load(file); } #section all }; //CancelOnWar() // Cancel the effect when the owner becomes at war with the target. class CancelOnWar : InfluenceEffectEffect { Document doc("Ends the influence effect when war is declared between this empire and the target."); Argument targ(TT_Empire); #section server bool onTick(InfluenceEffect@ eff, double time) const override { Empire@ to = arguments[0].fromTarget(eff.targets).emp; if(eff.owner !is null && eff.owner.isHostile(to)) eff.end(); return false; } #section all }; class CancelIfNotLeader : InfluenceEffectEffect { Document doc("Cancel the effect if the owner is not the senate leader."); #section server bool onTick(InfluenceEffect@ eff, double time) const override { if(eff.owner !is getSenateLeader()) eff.end(); return false; } #section all }; class CancelIfAttributeLT : InfluenceEffectEffect { Document doc("Cancel the effect if the owner's attribute is too low."); Argument attribute(AT_EmpAttribute, doc="Attribute to check."); Argument value(AT_Decimal, "1", doc="Value to test against."); #section server bool onTick(InfluenceEffect@ eff, double time) const override { Empire@ owner = eff.owner; if(owner is null || !owner.valid) return false; if(owner.getAttribute(attribute.integer) < value.decimal) eff.end(); return false; } #section all }; //ModAttribute(, , ) // Modify an empire attribute while the effect is active. // Mode is one of: Add, AddBase, AddFactor, Multiply class ModAttribute : InfluenceEffectEffect { Document doc("Changes an empire's specified attribute."); Argument attr("Attribute", AT_EmpAttribute, doc="ID of the empire attribute to affect."); Argument mode("Mode", AT_AttributeMode, doc="How to change the attribute (Add, AddBase, AddFactor, Multiply)."); Argument val("Value", AT_Decimal, doc="Value to change by."); #section server void onStart(InfluenceEffect@ eff) const override { eff.owner.modAttribute(uint(arguments[0].integer), arguments[1].integer, arguments[2].decimal); } void onEnd(InfluenceEffect@ eff) const override { double value = arguments[2].decimal; if(arguments[1].integer == int(AC_Multiply)) value = 1.0 / value; else value = -value; eff.owner.modAttribute(uint(arguments[0].integer), arguments[1].integer, value); } #section all }; //ModAttributeAll(, , ) // Modify an attribute on all empires while an effect is active. // Mode is one of: Add, AddBase, AddFactor, Multiply class ModAttributeAll : InfluenceEffectEffect { Document doc("Changes all empires' specified attribute."); Argument attr("Attribute", AT_EmpAttribute, doc="ID of the empire attribute to affect."); Argument mode("Mode", AT_AttributeMode, doc="How to change the attribute (Add, AddBase, AddFactor, Multiply)."); Argument val("Value", AT_Decimal, doc="Value to change by."); #section server void onStart(InfluenceEffect@ eff) const override { for(uint i = 0, cnt = getEmpireCount(); i < cnt; ++i) { auto@ emp = getEmpire(i); if(!emp.major) continue; emp.modAttribute(uint(arguments[0].integer), arguments[1].integer, arguments[2].decimal); } } void onEnd(InfluenceEffect@ eff) const override { double value = arguments[2].decimal; if(arguments[1].integer == int(AC_Multiply)) value = 1.0 / value; else value = -value; for(uint i = 0, cnt = getEmpireCount(); i < cnt; ++i) { auto@ emp = getEmpire(i); if(!emp.major) continue; emp.modAttribute(uint(arguments[0].integer), arguments[1].integer, value); } } #section all }; //ModAttributeOther(, , ) // Modify an attribute on all other empires while an effect is active. // Mode is one of: Add, AddBase, AddFactor, Multiply class ModAttributeOther: InfluenceEffectEffect { Document doc("Changes all other empires' specified attribute."); Argument attr("Attribute", AT_EmpAttribute, doc="ID of the empire attribute to affect."); Argument mode("Mode", AT_AttributeMode, doc="How to change the attribute (Add, AddBase, AddFactor, Multiply)."); Argument val("Value", AT_Decimal, doc="Value to change by."); #section server void onStart(InfluenceEffect@ eff) const override { for(uint i = 0, cnt = getEmpireCount(); i < cnt; ++i) { auto@ emp = getEmpire(i); if(!emp.major) continue; if(emp is eff.owner) continue; emp.modAttribute(uint(arguments[0].integer), arguments[1].integer, arguments[2].decimal); } } void onEnd(InfluenceEffect@ eff) const override { double value = arguments[2].decimal; if(arguments[1].integer == int(AC_Multiply)) value = 1.0 / value; else value = -value; for(uint i = 0, cnt = getEmpireCount(); i < cnt; ++i) { auto@ emp = getEmpire(i); if(!emp.major) continue; if(emp is eff.owner) continue; emp.modAttribute(uint(arguments[0].integer), arguments[1].integer, value); } } #section all }; //ModAttributeTarget(, , , ) // Modify an attribute on a particular empire while the effect is active. // Mode is one of: Add, AddBase, AddFactor, Multiply class ModAttributeTarget: InfluenceEffectEffect { Document doc("Changes a targeted empire's specified attribute."); Argument targ(TT_Empire); Argument attr("Attribute", AT_EmpAttribute, doc="ID of the empire attribute to affect."); Argument mode("Mode", AT_AttributeMode, doc="How to change the attribute (Add, AddBase, AddFactor, Multiply)."); Argument val("Value", AT_Decimal, doc="Value to change by."); #section server void onStart(InfluenceEffect@ eff) const override { Target@ targ = arguments[0].fromTarget(eff.targets); if(targ is null || targ.emp is null) return; targ.emp.modAttribute(uint(arguments[1].integer), arguments[2].integer, arguments[3].decimal); } void onEnd(InfluenceEffect@ eff) const override { Target@ targ = arguments[0].fromTarget(eff.targets); if(targ is null || targ.emp is null) return; double value = arguments[3].decimal; if(arguments[2].integer == int(AC_Multiply)) value = 1.0 / value; else value = -value; targ.emp.modAttribute(uint(arguments[1].integer), arguments[2].integer, value); } #section all }; //ModInfluenceStackSize() // Increase the size of the influence stack by . class ModInfluenceStackSize : InfluenceEffectEffect { Document doc("Changes how many cards are available to purchase at a time while active."); Argument val("Amount", AT_Integer, doc="Number of cards to add."); #section server void onStart(InfluenceEffect@ eff) const override { modInfluenceStackSize(+arguments[0].integer); } void onEnd(InfluenceEffect@ eff) const override { modInfluenceStackSize(-arguments[0].integer); } #section all }; //ProtectSystem() // Declare the system as protected. class ProtectSystem : InfluenceEffectEffect { Document doc("Prevents an empire's planets in a targeted system from sieges while active."); Argument targ(TT_Object); #section server bool onTick(InfluenceEffect@ eff, double time) const override { Target@ targ = arguments[0].fromTarget(eff.targets); if(targ is null || targ.obj is null || !targ.obj.isRegion) return false; cast(targ.obj).ProtectedMask |= eff.owner.mask; return false; } void onEnd(InfluenceEffect@ eff) const override { Target@ targ = arguments[0].fromTarget(eff.targets); if(targ is null || targ.obj is null || !targ.obj.isRegion) return; cast(targ.obj).ProtectedMask &= ~eff.owner.mask; } #section all }; //GainLeverageWhenVoteStarted( = 1.0) // Whenever a vote is started, gain leverage on the starter. class GainLeverageWhenVoteStarted : InfluenceEffectEffect { Document doc("Gives leverage against any empire which starts a vote to this empire."); Argument qual("Quality Factor", AT_Decimal, "1.0", doc="Magic quality value to determine how much is rewarded."); #section server void onStart(InfluenceEffect@ eff) const override { int last = getLastInfluenceVoteId(); eff.data[hookIndex].store(last); } bool onTick(InfluenceEffect@ eff, double time) const override { int last = -1; eff.data[hookIndex].retrieve(last); auto@ list = getInfluenceVotesSince(last); if(list is null) return false; for(uint i = 0, cnt = list.length; i < cnt; ++i) { auto@ vote = list[i]; if(vote.startedBy !is eff.owner && vote.startedBy.valid && eff.owner.ContactMask & vote.startedBy.mask != 0) eff.owner.gainRandomLeverage(vote.startedBy, arguments[0].decimal); last = max(last, int(vote.id)); } eff.data[hookIndex].store(last); return false; } void save(InfluenceEffect@ eff, SaveFile& file) const override { int last = -1; eff.data[hookIndex].retrieve(last); file << last; } void load(InfluenceEffect@ eff, SaveFile& file) const override { int last = -1; if(file >= SV_0089) file >> last; else last = getLastInfluenceVoteId(); eff.data[hookIndex].store(last); } #section all }; //GainLeverageWhenEffectStarted( = 1.0) // Whenever a effect is started, gain leverage on the starter. class GainLeverageWhenEffectStarted : InfluenceEffectEffect { Document doc("Gives leverage against any empire which starts an effect to this empire."); Argument qual("Quality Factor", AT_Decimal, "1.0", doc="Magic quality value to determine how much is rewarded."); #section server void onStart(InfluenceEffect@ eff) const override { int last = getLastInfluenceEffectId(); eff.data[hookIndex].store(last); } bool onTick(InfluenceEffect@ eff, double time) const override { int last = 0; eff.data[hookIndex].retrieve(last); auto@ list = getInfluenceEffectsSince(last); if(list is null) return false; for(uint i = 0, cnt = list.length; i < cnt; ++i) { auto@ effect = list[i]; if(effect.owner !is eff.owner && effect.owner.valid && eff.owner.ContactMask & effect.owner.mask != 0) eff.owner.gainRandomLeverage(effect.owner, arguments[0].decimal); last = max(last, int(effect.id)); } eff.data[hookIndex].store(last); return false; } void save(InfluenceEffect@ eff, SaveFile& file) const override { int last = -1; eff.data[hookIndex].retrieve(last); file << last; } void load(InfluenceEffect@ eff, SaveFile& file) const override { int last = -1; if(file >= SV_0089) file >> last; else last = getLastInfluenceEffectId(); eff.data[hookIndex].store(last); } #section all }; class OnEnable : InfluenceEffectEffect { BonusEffect@ hook; Document doc("Trigger a bonus effect when the effect enables."); Argument function(AT_Hook, "bonus_effects::EmpireTrigger"); Argument all_empires(AT_Boolean, "False", doc="When set, trigger the hook on all empires, instead of just the effect owner."); bool instantiate() override { @hook = cast(parseHook(arguments[0].str, "bonus_effects::", required=false)); if(hook is null) { error("OnEnable(): could not find inner hook: "+escape(arguments[0].str)); return false; } return InfluenceEffectEffect::instantiate(); } #section server void onStart(InfluenceEffect@ eff) const override { if(hook !is null) { if(all_empires.boolean) { for(uint i = 0, cnt = getEmpireCount(); i < cnt; ++i) { auto@ emp = getEmpire(i); if(emp.valid && emp.major) hook.activate(null, emp); } } else { hook.activate(null, eff.owner); } } } #section all }; class OnDisable : InfluenceEffectEffect { BonusEffect@ hook; Document doc("Trigger a bonus effect when the effect disables."); Argument function(AT_Hook, "bonus_effects::EmpireTrigger"); Argument all_empires(AT_Boolean, "False", doc="When set, trigger the hook on all empires, instead of just the effect owner."); bool instantiate() override { @hook = cast(parseHook(arguments[0].str, "bonus_effects::", required=false)); if(hook is null) { error("OnDisable(): could not find inner hook: "+escape(arguments[0].str)); return false; } return InfluenceEffectEffect::instantiate(); } #section server void onEnd(InfluenceEffect@ eff) const override { if(hook !is null) { if(all_empires.boolean) { for(uint i = 0, cnt = getEmpireCount(); i < cnt; ++i) { auto@ emp = getEmpire(i); if(emp.valid && emp.major) hook.activate(null, emp); } } else { hook.activate(null, eff.owner); } } } #section all }; class OnAllEmpires : InfluenceEffectEffect { EmpireEffect@ eff; Document doc("Apply an effect to all empires, instead of just the owner of the effect."); Argument hookID("Hook", AT_Hook, "empire_effects::EmpireEffect"); Argument apply_on_owner(AT_Boolean, "True", doc="Whether to also apply the effect on the owner, or just others."); bool instantiate() override { @eff = cast(parseHook(hookID.str, "empire_effects::", required=false)); if(eff is null) { error("OnAllEmpires(): could not find inner hook: "+escape(hookID.str)); return false; } return InfluenceEffectEffect::instantiate(); } void onStart(InfluenceEffect@ effect) const override { array arr(getEmpireCount()); effect.data[hookIndex].store(@arr); for(uint i = 0, cnt = getEmpireCount(); i < cnt; ++i) { Empire@ emp = getEmpire(i); if(!emp.major) continue; if(emp is effect.owner && !apply_on_owner.boolean) continue; eff.enable(emp, arr[i]); } } bool onTick(InfluenceEffect@ effect, double time) const override { array@ arr; effect.data[hookIndex].retrieve(@arr); for(uint i = 0, cnt = getEmpireCount(); i < cnt; ++i) { Empire@ emp = getEmpire(i); if(!emp.major) continue; if(emp is effect.owner && !apply_on_owner.boolean) continue; eff.tick(emp, arr[i], time); } return false; } void onEnd(InfluenceEffect@ effect) const override { array@ arr; effect.data[hookIndex].retrieve(@arr); for(uint i = 0, cnt = getEmpireCount(); i < cnt; ++i) { Empire@ emp = getEmpire(i); if(!emp.major) continue; if(emp is effect.owner && !apply_on_owner.boolean) continue; eff.disable(emp, arr[i]); } } void save(InfluenceEffect@ effect, SaveFile& file) const { array@ arr; effect.data[hookIndex].retrieve(@arr); for(uint i = 0, cnt = getEmpireCount(); i < cnt; ++i) { Empire@ emp = getEmpire(i); if(!emp.major) continue; if(emp is effect.owner && !apply_on_owner.boolean) continue; eff.save(arr[i], file); } } void load(InfluenceEffect@ effect, SaveFile& file) const { array arr(getEmpireCount()); effect.data[hookIndex].store(@arr); for(uint i = 0, cnt = getEmpireCount(); i < cnt; ++i) { Empire@ emp = getEmpire(i); if(!emp.major) continue; if(emp is effect.owner && !apply_on_owner.boolean) continue; eff.load(arr[i], file); } } };