import hooks; import abilities; import artifacts; from abilities import AbilityHook; import orbitals; import target_filters; from generic_effects import GenericEffect; import systems; import bonus_effects; from map_effects import MakePlanet, MakeStar; import listed_values; import orders; import attitudes; #section server import util.target_search; from objects.Artifact import createArtifact; import bool getCheatsEverOn() from "cheats"; from game_start import generateNewSystem; from oddity_navigation import getPathDistance; #section all //SpawnOrbitalAt(, , = False) // Spawn an orbital at with . // If is true, the design will be passed for packing. class SpawnOrbitalAt : AbilityHook { Document doc("Creates an orbital at a 3D destination."); Argument destination(TT_Point); Argument core(AT_OrbitalModule, doc="Orbital core to create the orbital with."); Argument set_design(AT_Boolean, "False", doc="Whether this orbital should be able to unpack into the creating ship's design."); Argument add_status(AT_Status, EMPTY_DEFAULT, doc="Status effect to add to the orbital after it is created."); #section server void activate(Ability@ abl, any@ data, const Targets@ targs) const override { vec3d point = destination.fromConstTarget(targs).point; Orbital@ orb = createOrbital(point, getOrbitalModule(core.integer), abl.emp); if(set_design.boolean) { const Design@ dsg; if(abl.obj !is null && abl.obj.isShip) @dsg = cast(abl.obj).blueprint.design; if(dsg !is null) orb.sendDesign(OV_PackUp, dsg); } if(add_status.integer != -1) orb.addStatus(add_status.integer); } #section all }; class ReplaceWithOrbital : AbilityHook { Document doc("Replace the ability's object with a newly spawned orbital."); Argument type(AT_OrbitalModule, doc="Type of orbital core to use."); Argument creep_owned(AT_Boolean, "False", doc="Whether the orbital should be owned by the creeps."); Argument free(AT_Boolean, "False", doc="Whether to make the orbital free of maintenance."); #section server void activate(Ability@ abl, any@ data, const Targets@ targs) const override { if(abl.obj is null) return; auto@ def = getOrbitalModule(type.integer); vec3d pos = abl.obj.position; auto@ orb = createOrbital(pos, def, creep_owned.boolean ? Creeps : abl.emp); if(free.boolean && !creep_owned.boolean) orb.makeFree(); @abl.obj = orb; } #section all }; //GiveAchievement() // Unlocks the specified achievement for the empire's player, when activated class GiveAchievement : AbilityHook { Document doc("Grants an achievement when the ability is activated."); Argument achievement(AT_Custom, doc="ID of the achievement."); #section server void activate(Ability@ abl, any@ data, const Targets@ targs) const override { Empire@ owner = abl.emp; if(!owner.valid || getCheatsEverOn()) return; if(owner is playerEmpire) unlockAchievement(achievement.str); if(mpServer && owner.player !is null) clientAchievement(owner.player, achievement.str); } #section all }; //ConsumeFTL() // Consume an amount of FTL energy to activate this ability. class ConsumeFTL : AbilityHook { Document doc("Requires a payment of FTL to activate this ability."); Argument cost("Amount", AT_Decimal, doc="FTL Cost."); bool canActivate(const Ability@ abl, const Targets@ targs, bool ignoreCost) const override { if(ignoreCost) return true; return abl.emp.FTLStored >= cost.decimal; } bool formatCost(const Ability@ abl, const Targets@ targs, string& value) const override { value = format(locale::FTL_COST, toString(cost.decimal, 0)); return true; } #section server bool consume(Ability@ abl, any@ data, const Targets@ targs) const override { if(abl.emp.consumeFTL(cost.decimal, partial=false, record=false) == 0.0) return false; return true; } void reverse(Ability@ abl, any@ data, const Targets@ targs) const override { abl.emp.modFTLStored(cost.decimal); } #section all }; class ConsumeMoney : AbilityHook { Document doc("Activating this ability requires a payment of money."); Argument cost("Amount", AT_Integer, doc="Money cost to activate."); bool canActivate(const Ability@ abl, const Targets@ targs, bool ignoreCost) const override { if(ignoreCost) return true; return abl.emp.canPay(cost.integer); } bool formatCost(const Ability@ abl, const Targets@ targs, string& value) const override { value = formatMoney(cost.integer); return true; } #section server bool consume(Ability@ abl, any@ data, const Targets@ targs) const override { if(abl.emp.consumeBudget(cost.integer) == -1) return false; return true; } void reverse(Ability@ abl, any@ data, const Targets@ targs) const override { abl.emp.refundBudget(cost.integer, abl.emp.BudgetCycleId); } #section all }; class ConsumeStatus : AbilityHook { Document doc("Consumes an instance of a status on the object."); Argument type(AT_Status, doc="Type of status to take."); Argument amount(AT_Integer, "1", doc="Amount of cargo taken to build per status."); Argument hide(AT_Boolean, "False", doc="If the object has _no_ statuses of this type, hide the option."); bool canActivate(const Ability@ abl, const Targets@ targs, bool ignoreCost) const override { Object@ obj = abl.obj; if(obj is null || !obj.hasStatuses) return false; int val = obj.getStatusStackCountAny(type.integer); if(hide.boolean && val < amount.integer) return false; if(ignoreCost) return true; return val >= amount.integer; } #section server bool consume(Ability@ abl, any@ data, const Targets@ targs) const override { Object@ obj = abl.obj; if(obj is null || !obj.hasStatuses) return false; int count = obj.getStatusStackCount(type.integer); if(count < amount.integer) return false; for(int i = 0; i < amount.integer; ++i) obj.removeStatusInstanceOfType(type.integer); return true; } void reverse(Ability@ abl, any@ data, const Targets@ targs) const override { Object@ obj = abl.obj; if(obj !is null && obj.hasStatuses) obj.addStatus(type.integer); } #section all }; class DistanceMoneyCost : AbilityHook { Document doc("This ability costs money to cast based on distance."); Argument destination(TT_Any, doc="Point to play them at."); Argument base_cost(AT_Decimal, "0", doc="Base cost per distance."); Argument sqrt_cost(AT_Decimal, "0", doc="Cost per square root of distance."); Argument square_cost(AT_Decimal, "0", doc="Cost per squared distance."); Argument extra_cost(AT_Decimal, "0", doc="Extra cost independent of distance."); int get(const Ability@ abl, const Targets@ targs) const { if(abl.obj is null || targs is null) return 0; auto@ tt = destination.fromConstTarget(targs); vec3d point; if(tt.type == TT_Point) point = tt.point; else if(tt.type == TT_Object && tt.obj !is null) point = tt.obj.position; double dist = point.distanceTo(abl.obj.position); double cost = extra_cost.decimal; if(base_cost.decimal != 0) cost += base_cost.decimal * dist; if(sqrt_cost.decimal != 0) cost += sqrt_cost.decimal * sqrt(dist); if(square_cost.decimal != 0) cost += square_cost.decimal * sqr(dist); return round(cost); } bool canActivate(const Ability@ abl, const Targets@ targs, bool ignoreCost) const override { if(ignoreCost) return true; return abl.emp.canPay(get(abl, targs)); } bool formatCost(const Ability@ abl, const Targets@ targs, string& value) const override { value = formatMoney(get(abl, targs)); return true; } #section server bool consume(Ability@ abl, any@ data, const Targets@ targs) const override { if(abl.emp.consumeBudget(get(abl, targs)) == -1) return false; return true; } void reverse(Ability@ abl, any@ data, const Targets@ targs) const override { abl.emp.refundBudget(get(abl, targs), abl.emp.BudgetCycleId); } #section all }; class ConsumeInfluence : AbilityHook { Document doc("Activating this ability requires a payment of influence."); Argument cost("Amount", AT_Integer, doc="Influence cost to activate."); bool canActivate(const Ability@ abl, const Targets@ targs, bool ignoreCost) const override { if(ignoreCost) return true; return abl.emp.Influence >= cost.integer; } bool formatCost(const Ability@ abl, const Targets@ targs, string& value) const override { value = toString(cost.integer, 0)+" "+locale::RESOURCE_INFLUENCE; return true; } #section server bool consume(Ability@ abl, any@ data, const Targets@ targs) const override { if(abl.emp.consumeInfluence(cost.integer)) return true; return false; } void reverse(Ability@ abl, any@ data, const Targets@ targs) const override { abl.emp.modInfluence(+cost.integer); } #section all }; class ConsumeDistanceFTL : AbilityHook { Document doc("Ability consumes FTL based on the distance to the target."); Argument targ(TT_Object); Argument base_cost(AT_Decimal, "0", doc="Base FTL Cost."); Argument distance_cost(AT_Decimal, "0", doc="FTL Cost per unit of distance."); Argument sqrt_cost(AT_Decimal, "0", doc="FTL Cost per square root unit of distance."); Argument obey_free_ftl(AT_Boolean, "True", doc="Whether to reduce the cost to 0 if departing from a free ftl system."); Argument obey_block_ftl(AT_Boolean, "True", doc="Whether to disable the ability if departing or arriving in a blocked ftl system."); Argument path_distance(AT_Boolean, "False", doc="If set, use total path distance taking into account gates, slipstreams and wormholes."); double getCost(const Ability@ abl, const Targets@ targs) const{ double cost = base_cost.decimal; auto@ t = targ.fromConstTarget(targs); if(t !is null && t.obj !is null && abl.obj !is null) { double dist = t.obj.position.distanceTo(abl.obj.position); if(path_distance.boolean) { #section server dist = getPathDistance(abl.emp, t.obj.position, abl.obj.position); #section client dist = getPathDistance(t.obj.position, abl.obj.position); #section all } cost += distance_cost.decimal * dist; cost += sqrt_cost.decimal * sqrt(dist); } if(obey_free_ftl.boolean && abl.emp !is null && abl.obj !is null) { Region@ myReg = abl.obj.region; if(myReg !is null && myReg.FreeFTLMask & abl.emp.mask != 0) return 0.0; } return cost; } bool canActivate(const Ability@ abl, const Targets@ targs, bool ignoreCost) const override { if(ignoreCost || targs is null) return true; if(obey_block_ftl.boolean && abl.emp !is null) { auto@ t = targ.fromConstTarget(targs); if(t !is null && t.obj !is null && abl.obj !is null) { Region@ myReg = abl.obj.region; if(myReg !is null && myReg.BlockFTLMask & abl.emp.mask != 0) return false; Region@ targReg = t.obj.region; if(targReg !is null && targReg.BlockFTLMask & abl.emp.mask != 0) return false; } } return abl.emp.FTLStored >= getCost(abl, targs); } bool formatCost(const Ability@ abl, const Targets@ targs, string& value) const override { if(targs is null) return false; value = format(locale::FTL_COST, toString(getCost(abl, targs), 0)); return true; } #section server bool consume(Ability@ abl, any@ data, const Targets@ targs) const override { double cost = getCost(abl, targs); if(cost == 0) return true; if(abl.emp.consumeFTL(cost, partial=false) == 0.0) return false; return true; } void reverse(Ability@ abl, any@ data, const Targets@ targs) const override { abl.emp.modFTLStored(getCost(abl, targs)); } #section all }; //EnergyCostFromSubsystem() // Sets the energy cost based on the subsystem variable class EnergyCostFromSubsystem : AbilityHook { Document doc("Bases ability energy cost on a subsystem variable."); Argument varName("Variable", AT_Custom, doc="Name of subsystem variable."); SubsystemVariable var = SubsystemVariable(-1); bool instantiate() override { int ind = getSubsystemVariable(varName.str); if(ind < 0) { error("EnergyCostFromSubsystem(): No Subsystem variable '" + varName.str + "'"); return false; } var = SubsystemVariable(ind); return AbilityHook::instantiate(); } void modEnergyCost(const Ability@ abl, const Targets@ targs, double& cost) const override { if(abl.subsystem is null || !abl.subsystem.has(var)) return; cost = abl.subsystem[var]; } }; //EmergencyResupply() // Resupplies the fleet using this ability based on a subsystem's variable class EmergencyResupply : AbilityHook { Document doc("Resupplies a fleet an amount based on a subsystem variable."); Argument varName("Variable", AT_Custom, doc="Name of subsystem variable."); SubsystemVariable var = SubsystemVariable(-1); bool instantiate() override { int ind = getSubsystemVariable(varName.str); if(ind < 0) { error("EmergencyResupply(): No Subsystem variable '" + varName.str + "'"); return false; } var = SubsystemVariable(ind); return AbilityHook::instantiate(); } bool canActivate(const Ability@ abl, const Targets@ targs, bool ignoreCost) const override { Ship@ ship = cast(abl.obj); if(ship is null) return false; return ship.Supply < ship.MaxSupply; } #section server void activate(Ability@ abl, any@ data, const Targets@ targs) const override { if(abl.subsystem is null || !abl.subsystem.has(var)) return; Ship@ ship = cast(abl.obj); if(ship is null) return; ship.refundSupply(abl.subsystem[var]); } #section all }; //RemotePlanetSiege(,