import hooks; import systems; import influence; import resources; #section server from influence_global import getRandomActiveInfluenceEffect, getSenateLeader, getTaggedEffectOwner; #section all interface AIDiplomacy { Empire@ get_empire(); uint get_allyMask(); uint get_selfMask(); uint enemiesMask(int maxStanding = -20); int getStanding(Empire@ against); array& get_cardList(); Object@ considerOwnedPlanets(const CardAI& hook, Targets& targets, VoteState@ vote = null, const InfluenceCard@ card = null); Object@ considerImportantPlanets(const CardAI& hook, Targets& targets, VoteState@ vote = null, const InfluenceCard@ card = null); Object@ considerOwnedSystems(const CardAI& hook, Targets& targets, VoteState@ vote = null, const InfluenceCard@ card = null); Object@ considerDefendingSystems(const CardAI& hook, Targets& targets, VoteState@ vote = null, const InfluenceCard@ card = null); Object@ considerDefendingPlanets(const CardAI& hook, Targets& targets, VoteState@ vote = null, const InfluenceCard@ card = null); Object@ considerEnemySystems(const CardAI& hook, Targets& targets, VoteState@ vote = null, const InfluenceCard@ card = null); Object@ considerImportantSystems(const CardAI& hook, Targets& targets, VoteState@ vote = null, const InfluenceCard@ card = null); Object@ considerFleets(const CardAI& hook, Targets& targets, VoteState@ vote = null, const InfluenceCard@ card = null); Object@ considerEnemyFleets(const CardAI& hook, Targets& targets, VoteState@ vote = null, const InfluenceCard@ card = null); Object@ considerEnemyPlanets(const CardAI& hook, Targets& targets, VoteState@ vote = null, const InfluenceCard@ card = null); Object@ considerMatchingImportRequests(const CardAI& hook, Targets& targets, VoteState@ vote, const InfluenceCard@ card, const ResourceType@ type, bool considerExisting); void markProtecting(Object@ obj); }; interface VoteState { InfluenceVote@ get_data(); bool get_side(); bool get_isRaceVote(); void set_isRaceVote(bool value); double get_priority(); void set_priority(double value); double get_weightOppose(); void set_weightOppose(double value); double get_weightSupport(); void set_weightSupport(double value); }; uint getTargeted(const InfluenceVote@ vote, const Argument@ targArg) { auto@ targ = vote.targets[targArg.integer]; if(targ is null) return 0; uint emps = 0; if(targ.emp !is null) { emps = targ.emp.mask; } else if(targ.obj !is null) { if(targ.obj.isRegion) emps = cast(targ.obj).PlanetsMask; else emps = targ.obj.owner.mask; } return emps & ~vote.startedBy.mask; } class CardAI : Hook { void considerBuy(AIDiplomacy& ai, const StackInfluenceCard@ card, double& weight) const {} void considerAct(AIDiplomacy& ai, const InfluenceCard@ card, any@ data, Targets& targs, double& weight) const {} bool act(AIDiplomacy& ai, const InfluenceCard@ card, any@ data, Targets& targs) const { return true; } void considerVote(AIDiplomacy& ai, VoteState& state, const InfluenceCard@ card, any@ data, Targets& targs, double& weight) const {} bool vote(AIDiplomacy& ai, VoteState& state, const InfluenceCard@ card, any@ data, Targets& targs) const { return true; } double consider(AIDiplomacy& ai, Targets& targets, VoteState@ vote, const InfluenceCard@ card, Object@ obj) const { return 0.0; } double consider(AIDiplomacy& ai, Targets& targets, VoteState@ vote, const InfluenceCard@ card, Object@ requestedAt, Object@ currentSupplier) const { return 0.0; } }; class VoteAI : Hook { void consider(AIDiplomacy& ai, VoteState& state, const InfluenceVote@ vote) const {} }; class BuyWeight : CardAI { Document doc("This card can always be bought, and has a particular weight."); Argument amount(AT_Decimal, "1.0", doc="Buy weight modification."); #section server void considerBuy(AIDiplomacy& ai, const StackInfluenceCard@ card, double& weight) const override { weight *= amount.decimal; } #section all }; class BuyAgainstEnemies : CardAI { Document doc("If this card is against a specific empire, value it more if that empire is an enemy."); Argument targ(TT_Empire, doc="Target to check."); Argument amount(AT_Decimal, "1.5", doc="Buy weight modification."); Argument max_standing(AT_Integer, "-20", doc="Anything above this standing is considered not an enemy."); #section server void considerBuy(AIDiplomacy& ai, const StackInfluenceCard@ card, double& weight) const override { auto@ consTarg = card.targets[targ.integer]; if(consTarg is null || consTarg.emp is null || !consTarg.filled) return; if(consTarg.emp.isHostile(ai.empire) || ai.getStanding(consTarg.emp) < max_standing.integer) weight *= amount.decimal; else weight /= amount.decimal; } #section all }; class VoteSupport : CardAI { Document doc("This card is played as a simple vote support card into votes."); Argument weight(AT_Decimal, "1.0", doc="Weight of the card in relation to other cards."); #section server void considerVote(AIDiplomacy& ai, VoteState& state, const InfluenceCard@ card, any@ data, Targets& targs, double& weight) const override { if(card.type.sideTarget != -1) targs.fill(card.type.sideTarget).side = state.side; if(!state.data.canVote(ai.empire, state.side)) { weight *= 0.0; return; } weight *= this.weight.decimal; } bool vote(AIDiplomacy& ai, VoteState& state, const InfluenceCard@ card, any@ data, Targets& targs) const override { if(card.type.sideTarget != -1) targs.fill(card.type.sideTarget).side = state.side; return true; } #section all } class VoteAlwaysNegative : CardAI { Document doc("This card should be played as a bad card into votes we don't like."); Argument weight(AT_Decimal, "1.0", doc="Weight of the card in relation to other cards."); #section server void considerVote(AIDiplomacy& ai, VoteState& state, const InfluenceCard@ card, any@ data, Targets& targs, double& weight) const override { if(!state.side) weight *= this.weight.decimal; else weight *= 0.0; } #section all }; class VoteAlwaysPositive : CardAI { Document doc("This card should be played as a card into votes we like."); Argument weight(AT_Decimal, "1.0", doc="Weight of the card in relation to other cards."); #section server void considerVote(AIDiplomacy& ai, VoteState& state, const InfluenceCard@ card, any@ data, Targets& targs, double& weight) const override { if(state.side) weight *= this.weight.decimal; else weight *= 0.0; } #section all }; class VoteNotOurs : CardAI { Document doc("This card should only be played if we aren't the one who started this vote."); Argument weight(AT_Decimal, "1.0", doc="Weight of the card in relation to other cards."); #section server void considerVote(AIDiplomacy& ai, VoteState& state, const InfluenceCard@ card, any@ data, Targets& targs, double& weight) const override { if(state.data.startedBy is ai.empire) weight *= 0.0; } #section all }; class VoteAgainstEmpire : CardAI { Document doc("This card should be played against empires voting in the opposite direction to us."); Argument targ(TT_Empire, doc="Empire target this is against."); Argument weight(AT_Decimal, "1.0", doc="Weight of the card in relation to other cards."); Argument min_vote(AT_Integer, "1", doc="Minimum amount of votes they should have cast opposite to us."); Argument most_only(AT_Boolean, "False", doc="Only use this card against empires that have the most opposition of everyone."); #section server void considerVote(AIDiplomacy& ai, VoteState& state, const InfluenceCard@ card, any@ data, Targets& targs, double& weight) const override { Empire@ emp; auto@ consTarg = card.targets[targ.integer]; if(consTarg !is null && consTarg.filled && consTarg.emp !is null) { @emp = consTarg.emp; } else if(most_only.boolean) { int bestVote = 0; for(uint i = 0, cnt = getEmpireCount(); i < cnt; ++i) { Empire@ check = getEmpire(i); if(check is ai.empire) continue; if(!state.data.isPresent(check)) continue; int votes = state.data.getVoteFrom(check); if(state.side) { if(votes < bestVote) { bestVote = votes; @emp = check; } } else { if(votes > bestVote) { bestVote = votes; @emp = check; } } } } else { @emp = getEmpire(randomi(0, getEmpireCount()-1)); } if(emp is null || !state.data.isPresent(emp)) { weight *= 0.0; return; } if(state.side) { if(state.data.getVoteFrom(emp) > -min_vote.integer) { weight *= 0.0; return; } } else { if(state.data.getVoteFrom(emp) < min_vote.integer) { weight *= 0.0; return; } } data.store(@emp); } bool vote(AIDiplomacy& ai, VoteState& state, const InfluenceCard@ card, any@ data, Targets& targs) const override { Empire@ emp; if(!data.retrieve(@emp)) return false; @targs.fill(targ.integer).emp = emp; if(card.type.sideTarget != -1) targs.fill(card.type.sideTarget).side = state.side; return true; } #section all }; class NotInRace : CardAI { Document doc("This card should not be used in a 'race' vote such as zeitgeists."); #section server void considerVote(AIDiplomacy& ai, VoteState& state, const InfluenceCard@ card, any@ data, Targets& targs, double& weight) const override { if(state.isRaceVote) weight *= 0.0; } #section all } class VoteOnAlly : CardAI { Document doc("This card should be used on allies who are voting the same way as we are."); Argument targ(TT_Empire, doc="Empire target this is against."); Argument weight(AT_Decimal, "1.0", doc="Weight of the card in relation to other cards."); Argument min_vote(AT_Integer, "1", doc="Minimum amount of votes they should have cast supporting us."); Argument allied_only(AT_Boolean, "True", doc="Only consider empires that are actually allied to us, instead of just voting our way."); Argument most_only(AT_Boolean, "False", doc="Only use this card against empires that have the most support of everyone."); #section server void considerVote(AIDiplomacy& ai, VoteState& state, const InfluenceCard@ card, any@ data, Targets& targs, double& weight) const override { Empire@ emp; auto@ consTarg = card.targets[targ.integer]; if(consTarg !is null && consTarg.filled && consTarg.emp !is null) { @emp = consTarg.emp; } else if(most_only.boolean) { int bestVote = 0; for(uint i = 0, cnt = getEmpireCount(); i < cnt; ++i) { Empire@ check = getEmpire(i); if(check is ai.empire) continue; if(!state.data.isPresent(check)) continue; if(allied_only.boolean && ai.allyMask & check.mask == 0) continue; int votes = state.data.getVoteFrom(check); if(state.side) { if(votes > bestVote) { bestVote = votes; @emp = check; } } else { if(votes < bestVote) { bestVote = votes; @emp = check; } } } } else { @emp = getEmpire(randomi(0, getEmpireCount()-1)); } if(emp is null || !state.data.isPresent(emp)) { weight *= 0.0; return; } if(state.side) { if(state.data.getVoteFrom(emp) < min_vote.integer) { weight *= 0.0; return; } } else { if(state.data.getVoteFrom(emp) > -min_vote.integer) { weight *= 0.0; return; } } data.store(@emp); } bool vote(AIDiplomacy& ai, VoteState& state, const InfluenceCard@ card, any@ data, Targets& targs) const override { Empire@ emp; if(!data.retrieve(@emp)) return false; @targs.fill(targ.integer).emp = emp; if(card.type.sideTarget != -1) targs.fill(card.type.sideTarget).side = state.side; return true; } #section all }; class PlayOnImportantPlanets : CardAI { Document doc("This card should be played on important planets in the empire when possible."); Argument targ(TT_Object, doc="Target to fill in."); Argument weight(AT_Decimal, "1.0", doc="Weight of the card in relation to other cards."); Argument min_level(AT_Integer, "2", doc="Don't target planets of a lower level than this."); #section server double consider(AIDiplomacy& ai, Targets& targets, VoteState@ vote, const InfluenceCard@ card, Object@ obj) const { if(int(obj.level) < min_level.integer) return 0.0; @targets.fill(targ.integer).obj = obj; if(!card.isValidTarget(targ.integer, targets[targ.integer])) return 0.0; double w = 0.8; if(obj.primaryResourceExported) w *= 0.5; return w; } void considerAct(AIDiplomacy& ai, const InfluenceCard@ card, any@ data, Targets& targs, double& weight) const override { Object@ check = ai.considerImportantPlanets(this, targs, null, card); if(check !is null) { data.store(@check); if(check.primaryResourceExported) weight *= 0.5; weight *= this.weight.decimal; return; } weight *= 0.0; } bool act(AIDiplomacy& ai, const InfluenceCard@ card, any@ data, Targets& targs) const override { Object@ playOn; if(!data.retrieve(@playOn)) return false; @targs.fill(targ.integer).obj = playOn; return true; } #section all }; class PlayOnImportantSystems : CardAI { Document doc("This card should be played on important systems in the empire when possible."); Argument targ(TT_Object, doc="Target to fill in."); Argument weight(AT_Decimal, "1.0", doc="Weight of the card in relation to other cards."); #section server double consider(AIDiplomacy& ai, Targets& targs, VoteState@ vote, const InfluenceCard@ card, Object@ obj) const { @targs.fill(targ.integer).obj = obj; if(!card.isValidTarget(targ.integer, targs[targ.integer])) return 0.0; return 1.0; } void considerAct(AIDiplomacy& ai, const InfluenceCard@ card, any@ data, Targets& targs, double& weight) const override { Object@ best = ai.considerImportantSystems(this, targs, null, card); if(best !is null) { data.store(@best); weight *= this.weight.decimal; return; } weight *= 0.0; } bool act(AIDiplomacy& ai, const InfluenceCard@ card, any@ data, Targets& targs) const override { Object@ playOn; if(!data.retrieve(@playOn)) return false; @targs.fill(targ.integer).obj = playOn; return true; } #section all }; class PlayOnImportantFleets : CardAI { Document doc("This card should be played on important fleets."); Argument targ(TT_Object, doc="Target to fill in."); Argument weight(AT_Decimal, "1.0", doc="Weight of the card in relation to other cards."); Argument min_size(AT_Integer, "128", doc="Minimum size to consider the flagship."); Argument enemies(AT_Boolean, "False", doc="Whether to play on an enemy's visible fleet instead of our own."); #section server double consider(AIDiplomacy& ai, Targets& targs, VoteState@ vote, const InfluenceCard@ card, Object@ obj) const { @targs.fill(targ.integer).obj = obj; if(!card.isValidTarget(targ.integer, targs[targ.integer])) return 0.0; Ship@ ship = cast(obj); if(ship !is null) { const Design@ dsg = ship.blueprint.design; if(dsg is null) return 0.0; if(dsg.hasTag(ST_Mothership) || dsg.hasTag(ST_Slipstream) || dsg.hasTag(ST_Gate)) return 0.0; } return obj.getFleetStrength(); } void considerAct(AIDiplomacy& ai, const InfluenceCard@ card, any@ data, Targets& targs, double& weight) const override { //Find our strongest combat fleet Ship@ strongest; if(!enemies.boolean) @strongest = cast(ai.considerFleets(this, targs, null, card)); else @strongest = cast(ai.considerEnemyFleets(this, targs, null, card)); if(strongest is null) { weight *= 0.0; return; } auto@ dsg = strongest.blueprint.design; if(dsg is null || dsg.size < min_size.integer) { weight *= 0.0; return; } //Consider playing on that fleet @targs.fill(targ.integer).obj = strongest; if(!card.isValidTarget(targ.integer, targs[targ.integer])) { weight *= 0.0; return; } data.store(@strongest); weight *= this.weight.decimal; } bool act(AIDiplomacy& ai, const InfluenceCard@ card, any@ data, Targets& targs) const override { Ship@ playOn; if(!data.retrieve(@playOn)) return false; @targs.fill(targ.integer).obj = playOn; return true; } #section all }; class PlayOnValuableCard : CardAI { Document doc("Play this card on another valuable card we have."); Argument targ(TT_Card, doc="Target to fill in."); Argument weight(AT_Decimal, "1.0", doc="Weight of the card in relation to other cards."); Argument min_value(AT_Integer, "2", doc="Minimum value of the card to play this on."); #section server void considerAct(AIDiplomacy& ai, const InfluenceCard@ card, any@ data, Targets& targs, double& weight) const override { double totalValue = 0.0; InfluenceCard@ otherCard; auto@ cards = ai.cardList; for(uint i = 0, cnt = cards.length; i < cnt; ++i) { auto@ check = cards[i]; if(check.id == card.id) continue; targs.fill(targ.integer).id = check.id; if(!card.isValidTarget(targ.integer, targs[targ.integer])) continue; double val = check.getPurchaseCost(ai.empire, uses=1); if(val < min_value.integer) continue; totalValue += val; if(randomd() < val / totalValue) @otherCard = check; } if(otherCard !is null) { weight *= this.weight.decimal; int playOn = otherCard.id; data.store(playOn); } else { weight *= 0.0; } } bool act(AIDiplomacy& ai, const InfluenceCard@ card, any@ data, Targets& targs) const override { int playOn = -1; if(!data.retrieve(playOn)) return false; targs.fill(targ.integer).id = playOn; return true; } #section all }; class PlayOnDefendingPlanet : CardAI { Document doc("This card should be played on planets that are currently being defended and under contestion."); Argument targ(TT_Object, doc="Target to fill in."); Argument weight(AT_Decimal, "5.0", doc="Weight of the card in relation to other cards."); #section server double consider(AIDiplomacy& ai, Targets& targs, VoteState@ vote, const InfluenceCard@ card, Object@ obj) const { @targs.fill(targ.integer).obj = obj; if(!card.isValidTarget(targ.integer, targs[targ.integer])) return 0.0; if(!obj.isContested) return 0.0; if(obj.isProtected(ai.empire)) return 0.0; if(obj.capturePct <= 0.01) return 0.0; if(!obj.enemiesInOrbit) return 0.0; return 1.0; } void considerAct(AIDiplomacy& ai, const InfluenceCard@ card, any@ data, Targets& targs, double& weight) const override { Object@ best = ai.considerDefendingPlanets(this, targs, null, card); if(best !is null) { data.store(@best); weight *= this.weight.decimal; return; } weight *= 0.0; } bool act(AIDiplomacy& ai, const InfluenceCard@ card, any@ data, Targets& targs) const override { Object@ playOn; if(!data.retrieve(@playOn)) return false; @targs.fill(targ.integer).obj = playOn; ai.markProtecting(playOn); return true; } #section all }; class PlayOnDefendingSystem : CardAI { Document doc("This card should be played on systems that are currently being defended and under contestion."); Argument targ(TT_Object, doc="Target to fill in."); Argument weight(AT_Decimal, "5.0", doc="Weight of the card in relation to other cards."); #section server double consider(AIDiplomacy& ai, Targets& targs, VoteState@ vote, const InfluenceCard@ card, Object@ obj) const { Region@ reg = cast(obj); if(reg is null) return 0.0; @targs.fill(targ.integer).obj = obj; if(!card.isValidTarget(targ.integer, targs[targ.integer])) return 0.0; if(reg.ProtectedMask & ai.empire.mask != 0) return 0.0; return 1.0; } void considerAct(AIDiplomacy& ai, const InfluenceCard@ card, any@ data, Targets& targs, double& weight) const override { Object@ best = ai.considerDefendingSystems(this, targs, null, card); if(best !is null) { data.store(@best); weight *= this.weight.decimal; return; } weight *= 0.0; } bool act(AIDiplomacy& ai, const InfluenceCard@ card, any@ data, Targets& targs) const override { Object@ playOn; if(!data.retrieve(@playOn)) return false; @targs.fill(targ.integer).obj = playOn; ai.markProtecting(playOn); return true; } #section all }; class PlayOnEnemyEffect : CardAI { Document doc("Play this card on an influence effect of someone we don't like."); Argument targ(TT_Effect, doc="Target to fill in."); Argument weight(AT_Decimal, "1.0", doc="Weight of the card in relation to other cards."); #section server void considerAct(AIDiplomacy& ai, const InfluenceCard@ card, any@ data, Targets& targs, double& weight) const override { auto@ eff = getRandomActiveInfluenceEffect(); if(eff is null || eff.owner is null) { weight *= 0.0; return; } //Only cast on effects we dislike the owner of if(!ai.empire.isHostile(eff.owner) && ai.getStanding(eff.owner) >= -10) { weight *= 0.0; return; } targs.fill(targ.integer).id = eff.id; if(!card.isValidTarget(targ.integer, targs[targ.integer])) { weight *= 0.0; return; } int playOn = eff.id; data.store(playOn); weight *= this.weight.decimal; } bool act(AIDiplomacy& ai, const InfluenceCard@ card, any@ data, Targets& targs) const override { int playOn = -1; if(!data.retrieve(playOn)) return false; targs.fill(targ.integer).id = playOn; return true; } #section all }; class PlayWhenInfluenceStronk : CardAI { Document doc("Play this card if we have strong influence compared to other empires."); Argument weight(AT_Decimal, "1.0", doc="Weight of the card in relation to other cards."); #section server void considerAct(AIDiplomacy& ai, const InfluenceCard@ card, any@ data, Targets& targs, double& weight) const override { if(ai.empire.PoliticalStrength != +1) weight *= 0.0; else weight *= this.weight.decimal; } #section all }; class PlayOnEnemy : CardAI { Document doc("Play this card on enemies we dislike."); Argument targ(TT_Empire, doc="Target to fill in."); Argument weight(AT_Decimal, "1.0", doc="Weight of the card in relation to other cards."); Argument war_only(AT_Boolean, "False", doc="Only play against enemies we are at war with, instead of everyone we dislike."); #section server void considerAct(AIDiplomacy& ai, const InfluenceCard@ card, any@ data, Targets& targs, double& weight) const override { Empire@ emp = getEmpire(randomi(0, getEmpireCount()-1)); auto@ curTarg = targs[targ.integer]; if(curTarg !is null && curTarg.filled) @emp = curTarg.emp; if(emp is null) { weight *= 0.0; return; } if(!ai.empire.isHostile(emp) && (war_only.boolean || ai.getStanding(emp) >= -20)) { weight *= 0.0; return; } @targs.fill(targ.integer).emp = emp; if(!card.isValidTarget(targ.integer, targs[targ.integer])) { weight *= 0.0; return; } data.store(@emp); weight *= this.weight.decimal; } bool act(AIDiplomacy& ai, const InfluenceCard@ card, any@ data, Targets& targs) const override { Empire@ playOn; if(!data.retrieve(@playOn)) return false; @targs.fill(targ.integer).emp = playOn; return true; } #section all }; class IgnoreIfSenateLeader : CardAI { Document doc("Ignore this card if you are senate leader."); #section server void considerBuy(AIDiplomacy& ai, const StackInfluenceCard@ card, double& weight) const override { if(getSenateLeader() is ai.empire) weight *= 0.0; } void considerAct(AIDiplomacy& ai, const InfluenceCard@ card, any@ data, Targets& targs, double& weight) const override { if(getSenateLeader() is ai.empire) weight *= 0.0; } #section all }; class IgnoreIfAttributeGTE : CardAI { Document doc("Ignore this card if you have a particular attribute above a value."); Argument attrib(AT_EmpAttribute, doc="Attribute to check."); Argument value(AT_Decimal, doc="Value to check for."); #section server void considerBuy(AIDiplomacy& ai, const StackInfluenceCard@ card, double& weight) const override { if(ai.empire.getAttribute(attrib.integer) >= value.decimal) weight *= 0.0; } void considerAct(AIDiplomacy& ai, const InfluenceCard@ card, any@ data, Targets& targs, double& weight) const override { if(ai.empire.getAttribute(attrib.integer) >= value.decimal) weight *= 0.0; } #section all }; class PlayOnNearbyEnemyPlanet : CardAI { Document doc("This card should be played on enemy planets that are nearby our space."); Argument targ(TT_Object, doc="Target to fill in."); Argument weight(AT_Decimal, "1.0", doc="Weight of the card in relation to other cards."); #section server double consider(AIDiplomacy& ai, Targets& targs, VoteState@ vote, const InfluenceCard@ card, Object@ obj) const { @targs.fill(targ.integer).obj = obj; if(!card.isValidTarget(targ.integer, targs[targ.integer])) return 0.0; return 1.0; } void considerAct(AIDiplomacy& ai, const InfluenceCard@ card, any@ data, Targets& targs, double& weight) const override { Object@ planet = ai.considerEnemyPlanets(this, targs, null, card); data.store(@planet); if(planet !is null) weight *= this.weight.decimal; else weight = 0.0; } bool act(AIDiplomacy& ai, const InfluenceCard@ card, any@ data, Targets& targs) const override { Object@ playOn; if(!data.retrieve(@playOn)) return false; @targs.fill(targ.integer).obj = playOn; return true; } #section all }; class PlayOnNearbyEnemySystem : CardAI { Document doc("This card should be played on enemy systems that are nearby our space."); Argument targ(TT_Object, doc="Target to fill in."); Argument weight(AT_Decimal, "1.0", doc="Weight of the card in relation to other cards."); #section server double consider(AIDiplomacy& ai, Targets& targs, VoteState@ vote, const InfluenceCard@ card, Object@ obj) const { @targs.fill(targ.integer).obj = obj; if(!card.isValidTarget(targ.integer, targs[targ.integer])) return 0.0; return 1.0; } void considerAct(AIDiplomacy& ai, const InfluenceCard@ card, any@ data, Targets& targs, double& weight) const override { Object@ system = ai.considerEnemySystems(this, targs, null, card); data.store(@system); if(system !is null) weight *= this.weight.decimal; else weight = 0.0; } bool act(AIDiplomacy& ai, const InfluenceCard@ card, any@ data, Targets& targs) const override { Object@ playOn; if(!data.retrieve(@playOn)) return false; @targs.fill(targ.integer).obj = playOn; return true; } #section all }; class Important : VoteAI { Document doc("This vote is important."); Argument importance(AT_Decimal, doc="Importance multiplier for the vote."); #section server void consider(AIDiplomacy& ai, VoteState& state, const InfluenceVote@ vote) const { state.priority *= importance.decimal; } #section all }; class BadFor : VoteAI { Document doc("This vote is bad for whoever is in the target."); Argument targ(TT_Any); Argument weight(AT_Decimal, "2.0", doc="Just how bad is this vote?"); Argument importance(AT_Decimal, "1.0", doc="Modifier to important for related empires."); #section server void consider(AIDiplomacy& ai, VoteState& state, const InfluenceVote@ vote) const { uint emps = getTargeted(vote, targ); //If it's us, this is bad if(ai.selfMask & emps != 0) { state.weightOppose *= this.weight.decimal; state.priority *= this.importance.decimal; } //Try to protect allies a little as well else if(ai.allyMask & emps != 0) { state.weightOppose *= sqrt(this.weight.decimal); state.priority *= this.importance.decimal; } } #section all }; class GoodFor : VoteAI { Document doc("This vote is good for whoever is in the target."); Argument targ(TT_Any); Argument weight(AT_Decimal, "2.0", doc="Just how good is this vote?"); Argument importance(AT_Decimal, "1.0", doc="Modifier to important for related empires."); #section server void consider(AIDiplomacy& ai, VoteState& state, const InfluenceVote@ vote) const { uint emps = getTargeted(vote, targ); //If it's us, this is bad if(ai.selfMask & emps != 0) { state.weightSupport *= this.weight.decimal; state.priority *= this.importance.decimal; } //Try to help allies a little as well else if(ai.allyMask & emps != 0) { state.weightSupport *= sqrt(this.weight.decimal); state.priority *= this.importance.decimal; } } #section all }; class GoodForNot : VoteAI { Document doc("This vote is good for whoever is not the target."); Argument targ(TT_Any); Argument weight(AT_Decimal, "2.0", doc="Just how good is this vote?"); Argument importance(AT_Decimal, "1.0", doc="Modifier to important for related empires."); #section server void consider(AIDiplomacy& ai, VoteState& state, const InfluenceVote@ vote) const { uint emps = getTargeted(vote, targ); if((ai.selfMask | ai.allyMask) & emps == 0) { state.weightSupport *= this.weight.decimal; state.priority *= this.importance.decimal; } else if(ai.selfMask & emps == 0) { state.weightSupport *= sqrt(this.weight.decimal); state.priority *= this.importance.decimal; } } #section all }; class BadForSenateLeader : VoteAI { Document doc("This vote is bad for the current senate leader."); Argument weight(AT_Decimal, "2.0", doc="Just how bad is this vote?"); #section server void consider(AIDiplomacy& ai, VoteState& state, const InfluenceVote@ vote) const { Empire@ curLeader = getSenateLeader(); uint emps = 0; if(curLeader !is null && curLeader !is vote.startedBy) emps |= curLeader.mask; //If it's us, this is bad if(ai.selfMask & emps != 0) state.weightOppose *= this.weight.decimal; //Try to protect allies a little as well else if(ai.allyMask & emps != 0) state.weightOppose *= sqrt(this.weight.decimal); } #section all }; class BenefitRace : VoteAI { Document doc("This vote is a race where the highest contributor benefits."); Argument weight(AT_Decimal, "2.0", doc="The weight of winning this vote."); #section server void consider(AIDiplomacy& ai, VoteState& state, const InfluenceVote@ vote) const { state.isRaceVote = true; state.weightSupport *= weight.decimal; } #section all }; class BadIfAttributeGTE : VoteAI { Document doc("This vote is bad if an attribute is at least a particular value."); Argument attrib(AT_EmpAttribute, doc="Attribute to check."); Argument value(AT_Decimal, doc="Value to check for."); Argument weight(AT_Decimal, "2.0", doc="Just how bad is this vote?"); Argument importance(AT_Decimal, "1.0", doc="Importance multiplier to the vote."); #section server void consider(AIDiplomacy& ai, VoteState& state, const InfluenceVote@ vote) const { if(ai.empire.getAttribute(attrib.integer) >= value.decimal) { state.weightOppose *= this.weight.decimal; state.priority *= this.importance.decimal; } } #section all }; class ZeitgeistVote : VoteAI { Document doc("This vote is a zeitgeist vote."); Argument tag(AT_Custom, "Zeitgeist", doc="Tag to determine for current zeitgeist effect."); Argument weight(AT_Decimal, "10.0", doc="Weight modification."); #section server void consider(AIDiplomacy& ai, VoteState& state, const InfluenceVote@ vote) const { state.isRaceVote = true; Empire@ current = getTaggedEffectOwner(tag.str); if(current !is null && current.mask & ai.selfMask != 0) state.weightOppose *= weight.decimal; else state.weightSupport *= weight.decimal; } #section all }; class PlayAsCreatedResource : CardAI { Document doc("This card is used to create a resource somewhere it is useful."); Argument targ(TT_Object, doc="Target to fill in."); Argument resource(AT_PlanetResource, doc="Resource to match import requests to."); Argument weight(AT_Decimal, "1.0", doc="Value for activating this."); Argument replaces_existing(AT_Boolean, "True", doc="Whether this can be used to replace an existing import of that type or not."); #section server double consider(AIDiplomacy& ai, Targets& targets, VoteState@ vote, const InfluenceCard@ card, Object@ requestedAt, Object@ currentSupplier) const { @targets.fill(targ.integer).obj = requestedAt; if(!card.isValidTarget(targ.integer, targets[targ.integer])) return 0.0; if(currentSupplier !is null) return 0.5; return 1.0; } void considerAct(AIDiplomacy& ai, const InfluenceCard@ card, any@ data, Targets& targs, double& weight) const override { Object@ check = ai.considerMatchingImportRequests(this, targs, null, card, getResource(resource.integer), replaces_existing.boolean); if(check !is null) { data.store(@check); weight *= this.weight.decimal; return; } weight *= 0.0; } bool act(AIDiplomacy& ai, const InfluenceCard@ card, any@ data, Targets& targs) const override { Object@ playOn; if(!data.retrieve(@playOn)) return false; @targs.fill(targ.integer).obj = playOn; return true; } #section all };