diff --git a/src/clj/game/cards/agendas.clj b/src/clj/game/cards/agendas.clj index 799a18db8e..29290f2676 100644 --- a/src/clj/game/cards/agendas.clj +++ b/src/clj/game/cards/agendas.clj @@ -1,7 +1,7 @@ (ns game.cards.agendas (:require [game.core :refer :all] [game.core.eid :refer [effect-completed]] - [game.core.card-defs :refer [card-def define-card]] + [game.core.card-defs :refer [card-def]] [game.utils :refer :all] [game.macros :refer [effect req msg wait-for continue-ability]] [clojure.string :refer [split-lines split join lower-case includes? starts-with?]] @@ -18,1515 +18,1516 @@ {:msg (msg "gain " (count-ice corp) " [Credits]") :interactive (req true) :effect (effect (gain-credits (count-ice corp)) - (update-all-ice)) + (update-all-ice)) :swapped {:effect (req (update-all-ice state side))} :events {:pre-ice-strength {:req (req (has-subtype? target subtype)) :effect (effect (ice-strength-bonus 1 target))}}})) ;; Card definitions -(define-card "15 Minutes" - {:abilities [{:cost [:click 1] :msg "shuffle 15 Minutes into R&D" - :label "Shuffle 15 Minutes into R&D" - :effect (req (let [corp-agendas (get-in corp [:scored]) - agenda-owner (if (some #(same-card? % card) corp-agendas) :corp :runner)] - (gain-agenda-point state agenda-owner (- (:agendapoints card)))) - ; refresh agendapoints to 1 before shuffle in case it was modified by e.g. The Board - (move state :corp (dissoc (assoc card :agendapoints 1) :seen :rezzed) :deck {:front true}) - (shuffle! state :corp :deck))}] - :flags {:has-abilities-when-stolen true}}) - -(define-card "Accelerated Beta Test" - (letfn [(abt [n i] - (if (pos? i) - {:async true - :prompt "Select a piece of ICE from the Temporary Zone to install" - :choices {:req #(and (corp? %) - (ice? %) - (in-play-area? %))} - :effect (req (wait-for (corp-install state side target nil - {:ignore-all-cost true :install-state :rezzed-no-cost}) - (let [card (get-card state card)] - (unregister-events state side card) - (if (not (:shuffle-occurred card)) - (if (< n i) - (continue-ability state side (abt (inc n) i) card nil) - (do (doseq [c (get-in @state [:corp :play-area])] - (system-msg state side "trashes a card") - (trash state side c {:unpreventable true})) - (effect-completed state side eid))) - (do (doseq [c (get-in @state [:corp :play-area])] - (move state side c :deck)) - (shuffle! state side :deck) - (effect-completed state side eid)))))) - :cancel-effect (req (doseq [c (get-in @state [:corp :play-area])] - (system-msg state side "trashes a card") - (trash state side c {:unpreventable true})))} - {:prompt "None of the cards are ice. Say goodbye!" - :choices ["I have no regrets"] - :effect (req (doseq [c (get-in @state [:corp :play-area])] - (system-msg state side "trashes a card") - (trash state side c {:unpreventable true})))}))] - {:interactive (req true) - :optional {:prompt "Look at the top 3 cards of R&D?" - :yes-ability {:async true - :msg "look at the top 3 cards of R&D" - :effect (req (register-events state side - {:corp-shuffle-deck - {:effect (effect (update! (assoc card :shuffle-occurred true)))}} - card) - (let [n (count (filter ice? (take 3 (:deck corp))))] - (doseq [c (take (min (count (:deck corp)) 3) (:deck corp))] - (move state side c :play-area)) - (continue-ability state side (abt 1 n) card nil)))}}})) - -(define-card "Advanced Concept Hopper" - {:events - {:run - {:req (req (first-event? state side :run)) - :effect (effect (show-wait-prompt :runner "Corp to use Advanced Concept Hopper") - (continue-ability - {:player :corp - :prompt "Use Advanced Concept Hopper to draw 1 card or gain 1 [Credits]?" - :once :per-turn - :choices ["Draw 1 card" "Gain 1 [Credits]" "No action"] - :effect (req (case target - "Gain 1 [Credits]" - (do (gain-credits state :corp 1) - (system-msg state :corp (str "uses Advanced Concept Hopper to gain 1 [Credits]"))) - "Draw 1 card" - (do (draw state :corp) - (system-msg state :corp (str "uses Advanced Concept Hopper to draw 1 card"))) - "No action" - (system-msg state :corp (str "doesn't use Advanced Concept Hopper"))) - (clear-wait-prompt state :runner) - (effect-completed state side eid))} - card nil))}}}) - -(define-card "Ancestral Imager" - {:events {:jack-out {:msg "do 1 net damage" - :effect (effect (damage :net 1))}}}) - -(define-card "AR-Enhanced Security" - {:events {:runner-trash {:once :per-turn - :async true - :req (req (some corp? targets)) - :msg "give the Runner a tag for trashing a Corp card" - :effect (effect (gain-tags eid 1))}}}) - -(define-card "Architect Deployment Test" - {:interactive (req true) - :async true - :msg "look at the top 5 cards of R&D" - :prompt (msg "The top cards of R&D are (top->bottom) " (join ", " (map :title (take 5 (:deck corp))))) - :choices ["OK"] - :effect (effect (continue-ability - {:prompt "Install a card?" - :choices (filter corp-installable-type? (take 5 (:deck corp))) - :effect (effect (corp-install eid target nil - {:ignore-all-cost true - :install-state :rezzed-no-rez-cost})) - :cancel-effect (effect (system-msg "does not install any of the top 5 cards") - (effect-completed eid))} - card nil))}) - -(define-card "Armed Intimidation" - {:async true - :effect (effect (show-wait-prompt :corp "Runner to suffer 5 meat damage or take 2 tags") - (continue-ability - :runner - {:async true - :choices ["Suffer 5 meat damage" "Take 2 tags"] - :prompt "Choose Armed Intimidation score effect" - :effect (req (clear-wait-prompt state :corp) - (case target - "Suffer 5 meat damage" - (do (damage state :runner eid :meat 5 {:card card :unboostable true}) - (system-msg state :runner "chooses to suffer 5 meat damage from Armed Intimidation")) - "Take 2 tags" - (do (gain-tags state :runner eid 2 {:card card}) - (system-msg state :runner "chooses to take 2 tags from Armed Intimidation"))))} - card nil))}) - -(define-card "Armored Servers" - {:implementation "Runner must trash cards manually when required" - :effect (effect (add-counter card :agenda 1)) - :silent (req true) - :abilities [{:counter-cost [:agenda 1] - :req (req (:run @state)) - :msg "make the Runner trash a card from their grip to jack out or break subroutines for the remainder of the run"}]}) - -(define-card "AstroScript Pilot Program" - {:effect (effect (add-counter card :agenda 1)) - :silent (req true) - :abilities [{:counter-cost [:agenda 1] - :msg (msg "place 1 advancement token on " (card-str state target)) - :choices {:req can-be-advanced?} - :effect (effect (add-prop target :advance-counter 1 {:placed true}))}]}) - -(define-card "Award Bait" - {:flags {:rd-reveal (req true)} - :access {:async true - :req (req (not-empty (filter #(can-be-advanced? %) (all-installed state :corp)))) - :effect (effect (show-wait-prompt :runner "Corp to place advancement tokens with Award Bait") - (continue-ability - {:async true - :choices ["0", "1", "2"] - :prompt "How many advancement tokens?" - :effect (req (let [c (str->int target)] - (continue-ability - state side - {:choices {:req can-be-advanced?} - :msg (msg "place " c " advancement tokens on " (card-str state target)) - :cancel-effect (req (clear-wait-prompt state :runner) - (effect-completed state side eid)) - :effect (effect (add-prop :corp target :advance-counter c {:placed true}) - (clear-wait-prompt :runner))} card nil)))} - card nil))}}) - -(define-card "Bacterial Programming" - (letfn [(hq-step [remaining to-trash to-hq] - {:async true - :prompt "Select a card to move to HQ" - :choices (conj (vec remaining) "Done") - :effect (req (if (= "Done" target) - (do - (doseq [t to-trash] - (trash state :corp t {:unpreventable true})) - (doseq [h to-hq] - (move state :corp h :hand)) - (if (not-empty remaining) - (continue-ability state :corp (reorder-choice :corp (vec remaining)) card nil) - (do (clear-wait-prompt state :runner) - (effect-completed state :corp eid))) - (system-msg state :corp (str "uses Bacterial Programming to add " (count to-hq) - " cards to HQ, discard " (count to-trash) - ", and arrange the top cards of R&D"))) - (continue-ability state :corp (hq-step - (clojure.set/difference (set remaining) (set [target])) - to-trash - (conj to-hq target)) card nil)))}) - (trash-step [remaining to-trash] - {:async true - :prompt "Select a card to discard" - :choices (conj (vec remaining) "Done") - :effect (req (if (= "Done" target) - (continue-ability state :corp (hq-step remaining to-trash `()) card nil) - (continue-ability state :corp (trash-step - (clojure.set/difference (set remaining) (set [target])) - (conj to-trash target)) card nil)))})] - (let [arrange-rd (effect (continue-ability - {:optional - {:async true - :prompt "Arrange top 7 cards of R&D?" - :yes-ability {:async true - :effect (req (let [c (take 7 (:deck corp))] - (when (:run @state) - (swap! state assoc-in [:run :shuffled-during-access :rd] true)) - (show-wait-prompt state :runner "Corp to use Bacterial Programming") - (continue-ability state :corp (trash-step c `()) card nil)))}}} - card nil))] - {:effect arrange-rd - :async true - :stolen {:async true - :effect arrange-rd} - :interactive (req true)}))) - -(define-card "Better Citizen Program" - (letfn [(ability [kind] - (effect (show-wait-prompt :runner "Corp to use Better Citizen Program") +(def card-definitions + {"15 Minutes" + {:abilities [{:cost [:click 1] :msg "shuffle 15 Minutes into R&D" + :label "Shuffle 15 Minutes into R&D" + :effect (req (let [corp-agendas (get-in corp [:scored]) + agenda-owner (if (some #(same-card? % card) corp-agendas) :corp :runner)] + (gain-agenda-point state agenda-owner (- (:agendapoints card)))) + ; refresh agendapoints to 1 before shuffle in case it was modified by e.g. The Board + (move state :corp (dissoc (assoc card :agendapoints 1) :seen :rezzed) :deck {:front true}) + (shuffle! state :corp :deck))}] + :flags {:has-abilities-when-stolen true}} + + "Accelerated Beta Test" + (letfn [(abt [n i] + (if (pos? i) + {:async true + :prompt "Select a piece of ICE from the Temporary Zone to install" + :choices {:req #(and (corp? %) + (ice? %) + (in-play-area? %))} + :effect (req (wait-for (corp-install state side target nil + {:ignore-all-cost true :install-state :rezzed-no-cost}) + (let [card (get-card state card)] + (unregister-events state side card) + (if (not (:shuffle-occurred card)) + (if (< n i) + (continue-ability state side (abt (inc n) i) card nil) + (do (doseq [c (get-in @state [:corp :play-area])] + (system-msg state side "trashes a card") + (trash state side c {:unpreventable true})) + (effect-completed state side eid))) + (do (doseq [c (get-in @state [:corp :play-area])] + (move state side c :deck)) + (shuffle! state side :deck) + (effect-completed state side eid)))))) + :cancel-effect (req (doseq [c (get-in @state [:corp :play-area])] + (system-msg state side "trashes a card") + (trash state side c {:unpreventable true})))} + {:prompt "None of the cards are ice. Say goodbye!" + :choices ["I have no regrets"] + :effect (req (doseq [c (get-in @state [:corp :play-area])] + (system-msg state side "trashes a card") + (trash state side c {:unpreventable true})))}))] + {:interactive (req true) + :optional {:prompt "Look at the top 3 cards of R&D?" + :yes-ability {:async true + :msg "look at the top 3 cards of R&D" + :effect (req (register-events state side + {:corp-shuffle-deck + {:effect (effect (update! (assoc card :shuffle-occurred true)))}} + card) + (let [n (count (filter ice? (take 3 (:deck corp))))] + (doseq [c (take (min (count (:deck corp)) 3) (:deck corp))] + (move state side c :play-area)) + (continue-ability state side (abt 1 n) card nil)))}}}) + + "Advanced Concept Hopper" + {:events + {:run + {:req (req (first-event? state side :run)) + :effect (effect (show-wait-prompt :runner "Corp to use Advanced Concept Hopper") + (continue-ability + {:player :corp + :prompt "Use Advanced Concept Hopper to draw 1 card or gain 1 [Credits]?" + :once :per-turn + :choices ["Draw 1 card" "Gain 1 [Credits]" "No action"] + :effect (req (case target + "Gain 1 [Credits]" + (do (gain-credits state :corp 1) + (system-msg state :corp (str "uses Advanced Concept Hopper to gain 1 [Credits]"))) + "Draw 1 card" + (do (draw state :corp) + (system-msg state :corp (str "uses Advanced Concept Hopper to draw 1 card"))) + "No action" + (system-msg state :corp (str "doesn't use Advanced Concept Hopper"))) + (clear-wait-prompt state :runner) + (effect-completed state side eid))} + card nil))}}} + + "Ancestral Imager" + {:events {:jack-out {:msg "do 1 net damage" + :effect (effect (damage :net 1))}}} + + "AR-Enhanced Security" + {:events {:runner-trash {:once :per-turn + :async true + :req (req (some corp? targets)) + :msg "give the Runner a tag for trashing a Corp card" + :effect (effect (gain-tags eid 1))}}} + + "Architect Deployment Test" + {:interactive (req true) + :async true + :msg "look at the top 5 cards of R&D" + :prompt (msg "The top cards of R&D are (top->bottom) " (join ", " (map :title (take 5 (:deck corp))))) + :choices ["OK"] + :effect (effect (continue-ability + {:prompt "Install a card?" + :choices (filter corp-installable-type? (take 5 (:deck corp))) + :effect (effect (corp-install eid target nil + {:ignore-all-cost true + :install-state :rezzed-no-rez-cost})) + :cancel-effect (effect (system-msg "does not install any of the top 5 cards") + (effect-completed eid))} + card nil))} + + "Armed Intimidation" + {:async true + :effect (effect (show-wait-prompt :corp "Runner to suffer 5 meat damage or take 2 tags") (continue-ability - :corp + :runner + {:async true + :choices ["Suffer 5 meat damage" "Take 2 tags"] + :prompt "Choose Armed Intimidation score effect" + :effect (req (clear-wait-prompt state :corp) + (case target + "Suffer 5 meat damage" + (do (damage state :runner eid :meat 5 {:card card :unboostable true}) + (system-msg state :runner "chooses to suffer 5 meat damage from Armed Intimidation")) + "Take 2 tags" + (do (gain-tags state :runner eid 2 {:card card}) + (system-msg state :runner "chooses to take 2 tags from Armed Intimidation"))))} + card nil))} + + "Armored Servers" + {:implementation "Runner must trash cards manually when required" + :effect (effect (add-counter card :agenda 1)) + :silent (req true) + :abilities [{:counter-cost [:agenda 1] + :req (req (:run @state)) + :msg "make the Runner trash a card from their grip to jack out or break subroutines for the remainder of the run"}]} + + "AstroScript Pilot Program" + {:effect (effect (add-counter card :agenda 1)) + :silent (req true) + :abilities [{:counter-cost [:agenda 1] + :msg (msg "place 1 advancement token on " (card-str state target)) + :choices {:req can-be-advanced?} + :effect (effect (add-prop target :advance-counter 1 {:placed true}))}]} + + "Award Bait" + {:flags {:rd-reveal (req true)} + :access {:async true + :req (req (not-empty (filter #(can-be-advanced? %) (all-installed state :corp)))) + :effect (effect (show-wait-prompt :runner "Corp to place advancement tokens with Award Bait") + (continue-ability + {:async true + :choices ["0", "1", "2"] + :prompt "How many advancement tokens?" + :effect (req (let [c (str->int target)] + (continue-ability + state side + {:choices {:req can-be-advanced?} + :msg (msg "place " c " advancement tokens on " (card-str state target)) + :cancel-effect (req (clear-wait-prompt state :runner) + (effect-completed state side eid)) + :effect (effect (add-prop :corp target :advance-counter c {:placed true}) + (clear-wait-prompt :runner))} card nil)))} + card nil))}} + + "Bacterial Programming" + (letfn [(hq-step [remaining to-trash to-hq] + {:async true + :prompt "Select a card to move to HQ" + :choices (conj (vec remaining) "Done") + :effect (req (if (= "Done" target) + (do + (doseq [t to-trash] + (trash state :corp t {:unpreventable true})) + (doseq [h to-hq] + (move state :corp h :hand)) + (if (not-empty remaining) + (continue-ability state :corp (reorder-choice :corp (vec remaining)) card nil) + (do (clear-wait-prompt state :runner) + (effect-completed state :corp eid))) + (system-msg state :corp (str "uses Bacterial Programming to add " (count to-hq) + " cards to HQ, discard " (count to-trash) + ", and arrange the top cards of R&D"))) + (continue-ability state :corp (hq-step + (clojure.set/difference (set remaining) (set [target])) + to-trash + (conj to-hq target)) card nil)))}) + (trash-step [remaining to-trash] + {:async true + :prompt "Select a card to discard" + :choices (conj (vec remaining) "Done") + :effect (req (if (= "Done" target) + (continue-ability state :corp (hq-step remaining to-trash `()) card nil) + (continue-ability state :corp (trash-step + (clojure.set/difference (set remaining) (set [target])) + (conj to-trash target)) card nil)))})] + (let [arrange-rd (effect (continue-ability + {:optional + {:async true + :prompt "Arrange top 7 cards of R&D?" + :yes-ability {:async true + :effect (req (let [c (take 7 (:deck corp))] + (when (:run @state) + (swap! state assoc-in [:run :shuffled-during-access :rd] true)) + (show-wait-prompt state :runner "Corp to use Bacterial Programming") + (continue-ability state :corp (trash-step c `()) card nil)))}}} + card nil))] + {:effect arrange-rd + :async true + :stolen {:async true + :effect arrange-rd} + :interactive (req true)})) + + "Better Citizen Program" + (letfn [(ability [kind] + (effect (show-wait-prompt :runner "Corp to use Better Citizen Program") + (continue-ability + :corp + {:optional + {:prompt "Give the runner 1 tag?" + :yes-ability {:async true + :msg (str "give the Runner a tag for " kind) + :effect (req (swap! state assoc-in [:per-turn (:cid card)] true) + (gain-tags state :corp eid 1))} + :end-effect (effect (clear-wait-prompt :runner))}} + card nil)))] + {:events {:play-event {:req (req (and (first-event? state :runner :run) + (has-subtype? target "Run") + (not (used-this-turn? (:cid card) state)))) + :async true + :effect (ability "playing a run event")} + :runner-install {:silent (req true) + :req (req (and (has-subtype? target "Icebreaker") + (first-event? state :runner :runner-install #(has-subtype? (first %) "Icebreaker")) + (not (used-this-turn? (:cid card) state)))) + :async true + :effect (ability "installing an icebreaker")}}}) + + "Bifrost Array" + {:req (req (not (empty? (filter #(not= (:title %) + "Bifrost Array") + (:scored corp))))) + :optional {:prompt "Trigger the ability of a scored agenda?" + :yes-ability {:prompt "Select an agenda to trigger the \"when scored\" ability of" + :choices {:req #(and (agenda? %) + (not= (:title %) + "Bifrost Array") + (= (first (:zone %)) + :scored) + (when-scored? %) + (:abilities %))} + :msg (msg "trigger the \"when scored\" ability of " (:title target)) + :effect (effect (continue-ability (card-def target) target nil))} + :no-ability {:effect (effect (clear-wait-prompt :runner))}}} + + "Brain Rewiring" + {:effect (effect (show-wait-prompt :runner "Corp to use Brain Rewiring") + (resolve-ability {:optional - {:prompt "Give the runner 1 tag?" - :yes-ability {:async true - :msg (str "give the Runner a tag for " kind) - :effect (req (swap! state assoc-in [:per-turn (:cid card)] true) - (gain-tags state :corp eid 1))} - :end-effect (effect (clear-wait-prompt :runner))}} - card nil)))] - {:events {:play-event {:req (req (and (first-event? state :runner :run) - (has-subtype? target "Run") - (not (used-this-turn? (:cid card) state)))) - :async true - :effect (ability "playing a run event")} - :runner-install {:silent (req true) - :req (req (and (has-subtype? target "Icebreaker") - (first-event? state :runner :runner-install #(has-subtype? (first %) "Icebreaker")) - (not (used-this-turn? (:cid card) state)))) - :async true - :effect (ability "installing an icebreaker")}}})) - -(define-card "Bifrost Array" - {:req (req (not (empty? (filter #(not= (:title %) - "Bifrost Array") - (:scored corp))))) - :optional {:prompt "Trigger the ability of a scored agenda?" - :yes-ability {:prompt "Select an agenda to trigger the \"when scored\" ability of" - :choices {:req #(and (agenda? %) - (not= (:title %) - "Bifrost Array") - (= (first (:zone %)) - :scored) - (when-scored? %) - (:abilities %))} - :msg (msg "trigger the \"when scored\" ability of " (:title target)) - :effect (effect (continue-ability (card-def target) target nil))} - :no-ability {:effect (effect (clear-wait-prompt :runner))}}}) - -(define-card "Brain Rewiring" - {:effect (effect (show-wait-prompt :runner "Corp to use Brain Rewiring") - (resolve-ability - {:optional - {:prompt "Pay credits to add random cards from Runner's Grip to the bottom of their Stack?" - :yes-ability {:prompt "How many credits?" - :choices {:number (req (min (:credit corp) - (count (:hand runner))))} - :async true - :effect (req (when (pos? target) - (pay state :corp card :credit target) - (let [from (take target (shuffle (:hand runner)))] - (doseq [c from] - (move state :runner c :deck)) - (system-msg state side (str "uses Brain Rewiring to pay " target - " [Credits] and add " target - " cards from the Runner's Grip" - " to the bottom of their Stack." - " The Runner draws 1 card")) - (wait-for (draw state :runner 1 nil) - (clear-wait-prompt state :runner) - (effect-completed state side eid)))))} - :no-ability {:effect (effect (clear-wait-prompt :runner))}}} - card nil))}) - -(define-card "Braintrust" - {:effect (effect (add-counter card :agenda (quot (- (get-counters card :advancement) 3) 2))) - :silent (req true) - :events {:pre-rez-cost {:req (req (ice? target)) - :effect (req (rez-cost-bonus state side (- (get-counters card :agenda))))}}}) - -(define-card "Breaking News" - {:async true - :effect (effect (gain-tags :corp eid 2) - (register-events - {:corp-turn-ends {:msg "make the Runner lose 2 tags" - :effect (effect (lose :runner :tag 2) - (unregister-events card))} - :runner-turn-ends {:msg "make the Runner lose 2 tags" - :effect (effect (lose :runner :tag 2) - (unregister-events card))}} - card)) - :silent (req true) - :msg "give the Runner 2 tags" - :events {:corp-turn-ends nil - :runner-turn-ends nil}}) - -(define-card "Broad Daylight" - (letfn [(add-counters [state side card eid] - (add-counter state :corp card :agenda (count-bad-pub state)) - (effect-completed state side eid))] - {:effect (effect - (continue-ability - {:optional - {:prompt "Take 1 bad publicity?" + {:prompt "Pay credits to add random cards from Runner's Grip to the bottom of their Stack?" + :yes-ability {:prompt "How many credits?" + :choices {:number (req (min (:credit corp) + (count (:hand runner))))} + :async true + :effect (req (when (pos? target) + (pay state :corp card :credit target) + (let [from (take target (shuffle (:hand runner)))] + (doseq [c from] + (move state :runner c :deck)) + (system-msg state side (str "uses Brain Rewiring to pay " target + " [Credits] and add " target + " cards from the Runner's Grip" + " to the bottom of their Stack." + " The Runner draws 1 card")) + (wait-for (draw state :runner 1 nil) + (clear-wait-prompt state :runner) + (effect-completed state side eid)))))} + :no-ability {:effect (effect (clear-wait-prompt :runner))}}} + card nil))} + + "Braintrust" + {:effect (effect (add-counter card :agenda (quot (- (get-counters card :advancement) 3) 2))) + :silent (req true) + :events {:pre-rez-cost {:req (req (ice? target)) + :effect (req (rez-cost-bonus state side (- (get-counters card :agenda))))}}} + + "Breaking News" + {:async true + :effect (effect (gain-tags :corp eid 2) + (register-events + {:corp-turn-ends {:msg "make the Runner lose 2 tags" + :effect (effect (lose :runner :tag 2) + (unregister-events card))} + :runner-turn-ends {:msg "make the Runner lose 2 tags" + :effect (effect (lose :runner :tag 2) + (unregister-events card))}} + card)) + :silent (req true) + :msg "give the Runner 2 tags" + :events {:corp-turn-ends nil + :runner-turn-ends nil}} + + "Broad Daylight" + (letfn [(add-counters [state side card eid] + (add-counter state :corp card :agenda (count-bad-pub state)) + (effect-completed state side eid))] + {:effect (effect + (continue-ability + {:optional + {:prompt "Take 1 bad publicity?" + :async true + :yes-ability {:effect (req (wait-for (gain-bad-publicity state :corp 1) + (system-msg state :corp "used Broad Daylight to take 1 bad publicity") + (add-counters state side card eid)))} + :no-ability {:effect (effect (add-counters card eid))}}} + card nil)) + :abilities [{:cost [:click 1] + :counter-cost [:agenda 1] :async true - :yes-ability {:effect (req (wait-for (gain-bad-publicity state :corp 1) - (system-msg state :corp "used Broad Daylight to take 1 bad publicity") - (add-counters state side card eid)))} - :no-ability {:effect (effect (add-counters card eid))}}} - card nil)) - :abilities [{:cost [:click 1] - :counter-cost [:agenda 1] + :label "Do 2 meat damage" + :once :per-turn + :msg "do 2 meat damage" + :effect (effect (damage eid :meat 2 {:card card}))}]}) + + "CFC Excavation Contract" + {:effect (req (let [bios (count (filter #(has-subtype? % "Bioroid") (all-active-installed state :corp))) + bucks (* bios 2)] + (gain-credits state side bucks) + (system-msg state side (str "gains " bucks " [Credits] from CFC Excavation Contract"))))} + + "Character Assassination" + {:prompt "Select a resource to trash" + :choices {:req #(and (installed? %) + (resource? %))} + :msg (msg "trash " (:title target)) + :interactive (req true) + :async true + :effect (effect (trash eid target {:unpreventable true}))} + + "Chronos Project" + {:msg "remove all cards in the Runner's Heap from the game" + :interactive (req true) + :effect (effect (move-zone :runner :discard :rfg))} + + "City Works Project" + (letfn [(meat-damage [s c] (+ 2 (get-counters (get-card s c) :advancement)))] + {:install-state :face-up + :access {:req (req installed) + :msg (msg "do " (meat-damage state card) " meat damage") + :async true + :effect (effect (damage eid :meat (meat-damage state card) {:card card}))}}) + + "Clone Retirement" + {:msg "remove 1 bad publicity" + :effect (effect (lose-bad-publicity 1)) + :silent (req true) + :stolen {:msg "force the Corp to take 1 bad publicity" + :effect (effect (gain-bad-publicity :corp 1))}} + + "Corporate Sales Team" + (let [e {:effect (req (when (pos? (get-counters card :credit)) + (gain-credits state :corp 1) + (system-msg state :corp (str "uses Corporate Sales Team to gain 1 [Credits]")) + (add-counter state side card :credit -1)))}] + {:effect (effect (add-counter card :credit 10)) + :silent (req true) + :events {:runner-turn-begins e + :corp-turn-begins e}}) + + "Corporate War" + {:msg (msg (if (> (:credit corp) 6) "gain 7 [Credits]" "lose all credits")) + :interactive (req true) + :effect (req (if (> (:credit corp) 6) + (gain-credits state :corp 7) (lose-credits state :corp :all)))} + + "Crisis Management" + (let [ability {:req (req tagged) :async true - :label "Do 2 meat damage" + :label "Do 1 meat damage (start of turn)" :once :per-turn - :msg "do 2 meat damage" - :effect (effect (damage eid :meat 2 {:card card}))}]})) - -(define-card "CFC Excavation Contract" - {:effect (req (let [bios (count (filter #(has-subtype? % "Bioroid") (all-active-installed state :corp))) - bucks (* bios 2)] - (gain-credits state side bucks) - (system-msg state side (str "gains " bucks " [Credits] from CFC Excavation Contract"))))}) - -(define-card "Character Assassination" - {:prompt "Select a resource to trash" - :choices {:req #(and (installed? %) - (resource? %))} - :msg (msg "trash " (:title target)) - :interactive (req true) - :async true - :effect (effect (trash eid target {:unpreventable true}))}) - -(define-card "Chronos Project" - {:msg "remove all cards in the Runner's Heap from the game" - :interactive (req true) - :effect (effect (move-zone :runner :discard :rfg))}) - -(define-card "City Works Project" - (letfn [(meat-damage [s c] (+ 2 (get-counters (get-card s c) :advancement)))] - {:install-state :face-up - :access {:req (req installed) - :msg (msg "do " (meat-damage state card) " meat damage") - :async true - :effect (effect (damage eid :meat (meat-damage state card) {:card card}))}})) - -(define-card "Clone Retirement" - {:msg "remove 1 bad publicity" - :effect (effect (lose-bad-publicity 1)) - :silent (req true) - :stolen {:msg "force the Corp to take 1 bad publicity" - :effect (effect (gain-bad-publicity :corp 1))}}) - -(define-card "Corporate Sales Team" - (let [e {:effect (req (when (pos? (get-counters card :credit)) - (gain-credits state :corp 1) - (system-msg state :corp (str "uses Corporate Sales Team to gain 1 [Credits]")) - (add-counter state side card :credit -1)))}] - {:effect (effect (add-counter card :credit 10)) - :silent (req true) - :events {:runner-turn-begins e - :corp-turn-begins e}})) - -(define-card "Corporate War" - {:msg (msg (if (> (:credit corp) 6) "gain 7 [Credits]" "lose all credits")) - :interactive (req true) - :effect (req (if (> (:credit corp) 6) - (gain-credits state :corp 7) (lose-credits state :corp :all)))}) - -(define-card "Crisis Management" - (let [ability {:req (req tagged) - :async true - :label "Do 1 meat damage (start of turn)" + :msg "do 1 meat damage" + :effect (effect (damage eid :meat 1 {:card card}))}] + {:events {:corp-turn-begins ability} + :abilities [ability]}) + + "Dedicated Neural Net" + (let [psi-effect + {:async true + :mandatory true + :effect (req (if (not-empty (:hand corp)) + (do (show-wait-prompt state :runner "Corp to select cards in HQ to be accessed") + (continue-ability + state :corp + {:prompt (msg "Select " (access-count state side :hq-access) " cards in HQ for the Runner to access") + :choices {:req #(and (in-hand? %) (corp? %)) + :max (req (access-count state side :hq-access))} + :effect (effect (clear-wait-prompt :runner) + (continue-ability + :runner + (access-helper-hq + state (access-count state side :hq-access) + ; access-helper-hq uses a set to keep track of which cards have already + ; been accessed. Using the set difference we make the runner unable to + ; access non-selected cards from the corp prompt + (clojure.set/difference (set (:hand corp)) (set targets))) + card nil))} + card nil)) + (effect-completed state side eid)))}] + {:events {:successful-run {:interactive (req true) + :psi {:req (req (= target :hq)) + :once :per-turn + :not-equal {:effect (req (when-not (:replace-access (get-in @state [:run :run-effect])) + (swap! state update-in [:run :run-effect] + #(assoc % :replace-access psi-effect))) + (effect-completed state side eid))}}}}}) + + "Degree Mill" + {:steal-cost-bonus (req [:shuffle-installed-to-stack 2])} + + "Director Haas' Pet Project" + (letfn [(install-ability [server-name n] + {:prompt "Select a card to install" + :show-discard true + :choices {:req #(and (corp? %) + (not (operation? %)) + (#{[:hand] [:discard]} (:zone %)))} + :effect (req (corp-install state side target server-name {:ignore-all-cost true}) + (if (< n 2) + (continue-ability state side + (install-ability (last (get-remote-names state)) (inc n)) + card nil) + (effect-completed state side eid))) + :msg (msg (corp-install-msg target) + (when (zero? n) + ", creating a new remote server") + ", ignoring all install costs")})] + {:optional {:prompt "Install cards in a new remote server?" + :yes-ability (install-ability "New remote" 0)}}) + + "Divested Trust" + {:events + {:agenda-stolen + {:async true + :interactive (req true) + :effect (req (if (:winner @state) + (effect-completed state side eid) + (let [stolen-agenda target + title (:title stolen-agenda) + prompt (str "Forfeit Divested Trust to add " title + " to HQ and gain 5[Credits]?") + message (str "add " title " to HQ and gain 5 [Credits]")] + (show-wait-prompt state :runner "Corp to use Divested Trust") + (continue-ability + state side + {:optional + {:prompt prompt + :yes-ability + {:msg message + :effect (effect (forfeit card) + (move stolen-agenda :hand) + (gain-agenda-point :runner (- (:agendapoints stolen-agenda))) + (gain-credits 5) + (effect-completed eid))} + :end-effect (effect (clear-wait-prompt :runner))}} + card nil))))}}} + + "Domestic Sleepers" + {:agendapoints-runner (req 0) + :abilities [{:cost [:click 3] :msg "place 1 agenda counter on Domestic Sleepers" + :req (req (not (:counter card))) + :effect (effect (gain-agenda-point 1) + (set-prop card :counter {:agenda 1} :agendapoints 1))}]} + + "Eden Fragment" + {:events {:pre-corp-install + {:req (req (and (ice? target) + (empty? (let [cards (map first (turn-events state side :corp-install))] + (filter ice? cards))))) + :effect (effect (ignore-install-cost true))} + :corp-install + {:req (req (and (ice? target) + (empty? (let [cards (map first (turn-events state side :corp-install))] + (filter ice? cards))))) + :msg (msg "ignore the install cost of the first ICE this turn")}}} + + "Efficiency Committee" + {:silent (req true) + :effect (effect (add-counter card :agenda 3)) + :abilities [{:cost [:click 1] :counter-cost [:agenda 1] + :effect (effect (gain :click 2) + (register-turn-flag! + card :can-advance + (fn [state side card] + ((constantly false) + (toast state :corp "Cannot advance cards this turn due to Efficiency Committee." "warning"))))) + :msg "gain [Click][Click]"}]} + + "Elective Upgrade" + {:silent (req true) + :effect (effect (add-counter card :agenda 2)) + :abilities [{:cost [:click 1] + :counter-cost [:agenda 1] :once :per-turn - :msg "do 1 meat damage" - :effect (effect (damage eid :meat 1 {:card card}))}] - {:events {:corp-turn-begins ability} - :abilities [ability]})) - -(define-card "Dedicated Neural Net" - (let [psi-effect - {:async true - :mandatory true - :effect (req (if (not-empty (:hand corp)) - (do (show-wait-prompt state :runner "Corp to select cards in HQ to be accessed") - (continue-ability - state :corp - {:prompt (msg "Select " (access-count state side :hq-access) " cards in HQ for the Runner to access") - :choices {:req #(and (in-hand? %) (corp? %)) - :max (req (access-count state side :hq-access))} - :effect (effect (clear-wait-prompt :runner) - (continue-ability - :runner - (access-helper-hq - state (access-count state side :hq-access) - ; access-helper-hq uses a set to keep track of which cards have already - ; been accessed. Using the set difference we make the runner unable to - ; access non-selected cards from the corp prompt - (clojure.set/difference (set (:hand corp)) (set targets))) - card nil))} - card nil)) - (effect-completed state side eid)))}] - {:events {:successful-run {:interactive (req true) - :psi {:req (req (= target :hq)) - :once :per-turn - :not-equal {:effect (req (when-not (:replace-access (get-in @state [:run :run-effect])) - (swap! state update-in [:run :run-effect] - #(assoc % :replace-access psi-effect))) - (effect-completed state side eid))}}}}})) - -(define-card "Degree Mill" - {:steal-cost-bonus (req [:shuffle-installed-to-stack 2])}) - -(define-card "Director Haas' Pet Project" - (letfn [(install-ability [server-name n] - {:prompt "Select a card to install" - :show-discard true - :choices {:req #(and (corp? %) - (not (operation? %)) - (#{[:hand] [:discard]} (:zone %)))} - :effect (req (corp-install state side target server-name {:ignore-all-cost true}) - (if (< n 2) - (continue-ability state side - (install-ability (last (get-remote-names state)) (inc n)) - card nil) - (effect-completed state side eid))) - :msg (msg (corp-install-msg target) - (when (zero? n) - ", creating a new remote server") - ", ignoring all install costs")})] - {:optional {:prompt "Install cards in a new remote server?" - :yes-ability (install-ability "New remote" 0)}})) - -(define-card "Divested Trust" - {:events - {:agenda-stolen - {:async true - :interactive (req true) - :effect (req (if (:winner @state) - (effect-completed state side eid) - (let [stolen-agenda target - title (:title stolen-agenda) - prompt (str "Forfeit Divested Trust to add " title - " to HQ and gain 5[Credits]?") - message (str "add " title " to HQ and gain 5 [Credits]")] - (show-wait-prompt state :runner "Corp to use Divested Trust") - (continue-ability - state side - {:optional - {:prompt prompt - :yes-ability - {:msg message - :effect (effect (forfeit card) - (move stolen-agenda :hand) - (gain-agenda-point :runner (- (:agendapoints stolen-agenda))) - (gain-credits 5) - (effect-completed eid))} - :end-effect (effect (clear-wait-prompt :runner))}} - card nil))))}}}) - -(define-card "Domestic Sleepers" - {:agendapoints-runner (req 0) - :abilities [{:cost [:click 3] :msg "place 1 agenda counter on Domestic Sleepers" - :req (req (not (:counter card))) - :effect (effect (gain-agenda-point 1) - (set-prop card :counter {:agenda 1} :agendapoints 1))}]}) - -(define-card "Eden Fragment" - {:events {:pre-corp-install - {:req (req (and (ice? target) - (empty? (let [cards (map first (turn-events state side :corp-install))] - (filter ice? cards))))) - :effect (effect (ignore-install-cost true))} - :corp-install - {:req (req (and (ice? target) - (empty? (let [cards (map first (turn-events state side :corp-install))] - (filter ice? cards))))) - :msg (msg "ignore the install cost of the first ICE this turn")}}}) - -(define-card "Efficiency Committee" - {:silent (req true) - :effect (effect (add-counter card :agenda 3)) - :abilities [{:cost [:click 1] :counter-cost [:agenda 1] - :effect (effect (gain :click 2) - (register-turn-flag! - card :can-advance - (fn [state side card] - ((constantly false) - (toast state :corp "Cannot advance cards this turn due to Efficiency Committee." "warning"))))) - :msg "gain [Click][Click]"}]}) - -(define-card "Elective Upgrade" - {:silent (req true) - :effect (effect (add-counter card :agenda 2)) - :abilities [{:cost [:click 1] - :counter-cost [:agenda 1] - :once :per-turn - :effect (effect (gain :click 2)) - :msg "gain [Click][Click]"}]}) - -(define-card "Encrypted Portals" - (ice-boost-agenda "Code Gate")) - -(define-card "Escalate Vitriol" - {:abilities [{:label "Gain 1 [Credit] for each Runner tag" - :cost [:click 1] - :once :per-turn - :msg (msg "gain " (count-tags state) " [Credits]") - :effect (effect (gain-credits (count-tags state)))}]}) - -(define-card "Executive Retreat" - {:effect (effect (add-counter card :agenda 1) - (shuffle-into-deck :hand)) - :interactive (req true) - :abilities [{:cost [:click 1] - :counter-cost [:agenda 1] - :msg "draw 5 cards" - :effect (effect (draw 5))}]}) - -(define-card "Explode-a-palooza" - {:flags {:rd-reveal (req true)} - :access {:async true - :effect (effect (show-wait-prompt :runner "Corp to use Explode-a-palooza") - (continue-ability - {:optional {:prompt "Gain 5 [Credits] with Explode-a-palooza ability?" - :yes-ability {:msg "gain 5 [Credits]" - :effect (effect (gain-credits :corp 5) - (clear-wait-prompt :runner))} - :no-ability {:effect (effect (clear-wait-prompt :runner))}}} - card nil))}}) - -(define-card "False Lead" - {:abilities [{:req (req (>= (:click runner) 2)) - :msg "force the Runner to lose [Click][Click]" - :effect (effect (forfeit card) - (lose :runner :click 2))}]}) - -(define-card "Fetal AI" - {:flags {:rd-reveal (req true)} - :access {:async true - :req (req (not= (first (:zone card)) :discard)) :msg "do 2 net damage" - :effect (effect (damage eid :net 2 {:card card}))} - :steal-cost-bonus (req [:credit 2])}) - -(define-card "Firmware Updates" - {:silent (req true) - :effect (effect (add-counter card :agenda 3)) - :abilities [{:counter-cost [:agenda 1] - :choices {:req #(and (ice? %) - (can-be-advanced? %))} - :req (req (pos? (get-counters card :agenda))) - :msg (msg "place 1 advancement token on " (card-str state target)) - :once :per-turn - :effect (effect (add-prop target :advance-counter 1))}]}) - -(define-card "Fly on the Wall" - {:msg "give the runner 1 tag" - :async true - :effect (req (gain-tags state :runner eid 1))}) - -(define-card "Genetic Resequencing" - {:choices {:req #(= (last (:zone %)) :scored)} - :msg (msg "add 1 agenda counter on " (:title target)) - :effect (effect (add-counter target :agenda 1)) - :silent (req true)}) - -(define-card "Geothermal Fracking" - {:effect (effect (add-counter card :agenda 2)) - :silent (req true) - :abilities [{:cost [:click 1] - :counter-cost [:agenda 1] - :msg "gain 7 [Credits] and take 1 bad publicity" - :effect (effect (gain-credits 7) - (gain-bad-publicity :corp 1))}]}) - -(define-card "Gila Hands Arcology" - {:abilities [{:cost [:click 2] - :msg "gain 3 [Credits]" - :effect (effect (gain-credits 3))}]}) - -(define-card "Glenn Station" - {:implementation "Doesn't prohibit hosting multiple cards" - :abilities [{:label "Host a card from HQ on Glenn Station" - :cost [:click 1] - :msg "host a card from HQ" - :prompt "Choose a card to host on Glenn Station" - :choices (req (:hand corp)) - :effect (effect (host card target {:facedown true}))} - {:label "Add a card on Glenn Station to HQ" - :cost [:click 1] - :msg "add a hosted card to HQ" - :prompt "Choose a card on Glenn Station" - :choices (req (:hosted card)) - :effect (effect (move target :hand))}]}) - -(define-card "Global Food Initiative" - {:agendapoints-runner (req 2)}) - -(define-card "Government Contracts" - {:abilities [{:cost [:click 2] - :effect (effect (gain-credits 4)) - :msg "gain 4 [Credits]"}]}) - -(define-card "Government Takeover" - {:abilities [{:cost [:click 1] - :effect (effect (gain-credits 3)) - :msg "gain 3 [Credits]"}]}) - -(define-card "Graft" - (letfn [(graft [n] {:prompt "Choose a card to add to HQ with Graft" - :async true - :choices (req (cancellable (:deck corp) :sorted)) - :msg (msg "add " (:title target) " to HQ from R&D") - :cancel-effect (req (shuffle! state side :deck) + :effect (effect (gain :click 2)) + :msg "gain [Click][Click]"}]} + + "Encrypted Portals" + (ice-boost-agenda "Code Gate") + + "Escalate Vitriol" + {:abilities [{:label "Gain 1 [Credit] for each Runner tag" + :cost [:click 1] + :once :per-turn + :msg (msg "gain " (count-tags state) " [Credits]") + :effect (effect (gain-credits (count-tags state)))}]} + + "Executive Retreat" + {:effect (effect (add-counter card :agenda 1) + (shuffle-into-deck :hand)) + :interactive (req true) + :abilities [{:cost [:click 1] + :counter-cost [:agenda 1] + :msg "draw 5 cards" + :effect (effect (draw 5))}]} + + "Explode-a-palooza" + {:flags {:rd-reveal (req true)} + :access {:async true + :effect (effect (show-wait-prompt :runner "Corp to use Explode-a-palooza") + (continue-ability + {:optional {:prompt "Gain 5 [Credits] with Explode-a-palooza ability?" + :yes-ability {:msg "gain 5 [Credits]" + :effect (effect (gain-credits :corp 5) + (clear-wait-prompt :runner))} + :no-ability {:effect (effect (clear-wait-prompt :runner))}}} + card nil))}} + + "False Lead" + {:abilities [{:req (req (>= (:click runner) 2)) + :msg "force the Runner to lose [Click][Click]" + :effect (effect (forfeit card) + (lose :runner :click 2))}]} + + "Fetal AI" + {:flags {:rd-reveal (req true)} + :access {:async true + :req (req (not= (first (:zone card)) :discard)) :msg "do 2 net damage" + :effect (effect (damage eid :net 2 {:card card}))} + :steal-cost-bonus (req [:credit 2])} + + "Firmware Updates" + {:silent (req true) + :effect (effect (add-counter card :agenda 3)) + :abilities [{:counter-cost [:agenda 1] + :choices {:req #(and (ice? %) + (can-be-advanced? %))} + :req (req (pos? (get-counters card :agenda))) + :msg (msg "place 1 advancement token on " (card-str state target)) + :once :per-turn + :effect (effect (add-prop target :advance-counter 1))}]} + + "Fly on the Wall" + {:msg "give the runner 1 tag" + :async true + :effect (req (gain-tags state :runner eid 1))} + + "Genetic Resequencing" + {:choices {:req #(= (last (:zone %)) :scored)} + :msg (msg "add 1 agenda counter on " (:title target)) + :effect (effect (add-counter target :agenda 1)) + :silent (req true)} + + "Geothermal Fracking" + {:effect (effect (add-counter card :agenda 2)) + :silent (req true) + :abilities [{:cost [:click 1] + :counter-cost [:agenda 1] + :msg "gain 7 [Credits] and take 1 bad publicity" + :effect (effect (gain-credits 7) + (gain-bad-publicity :corp 1))}]} + + "Gila Hands Arcology" + {:abilities [{:cost [:click 2] + :msg "gain 3 [Credits]" + :effect (effect (gain-credits 3))}]} + + "Glenn Station" + {:implementation "Doesn't prohibit hosting multiple cards" + :abilities [{:label "Host a card from HQ on Glenn Station" + :cost [:click 1] + :msg "host a card from HQ" + :prompt "Choose a card to host on Glenn Station" + :choices (req (:hand corp)) + :effect (effect (host card target {:facedown true}))} + {:label "Add a card on Glenn Station to HQ" + :cost [:click 1] + :msg "add a hosted card to HQ" + :prompt "Choose a card on Glenn Station" + :choices (req (:hosted card)) + :effect (effect (move target :hand))}]} + + "Global Food Initiative" + {:agendapoints-runner (req 2)} + + "Government Contracts" + {:abilities [{:cost [:click 2] + :effect (effect (gain-credits 4)) + :msg "gain 4 [Credits]"}]} + + "Government Takeover" + {:abilities [{:cost [:click 1] + :effect (effect (gain-credits 3)) + :msg "gain 3 [Credits]"}]} + + "Graft" + (letfn [(graft [n] {:prompt "Choose a card to add to HQ with Graft" + :async true + :choices (req (cancellable (:deck corp) :sorted)) + :msg (msg "add " (:title target) " to HQ from R&D") + :cancel-effect (req (shuffle! state side :deck) + (system-msg state side (str "shuffles R&D")) + (effect-completed state side eid)) + :effect (req (move state side target :hand) + (if (< n 3) + (continue-ability state side (graft (inc n)) card nil) + (do (shuffle! state side :deck) (system-msg state side (str "shuffles R&D")) - (effect-completed state side eid)) - :effect (req (move state side target :hand) - (if (< n 3) - (continue-ability state side (graft (inc n)) card nil) - (do (shuffle! state side :deck) - (system-msg state side (str "shuffles R&D")) - (effect-completed state side eid))))})] - {:async true - :msg "add up to 3 cards from R&D to HQ" - :effect (effect (continue-ability (graft 1) card nil))})) - -(define-card "Hades Fragment" - {:flags {:corp-phase-12 (req (and (not-empty (get-in @state [:corp :discard])) - (is-scored? state :corp card)))} - :abilities [{:prompt "Select a card to add to the bottom of R&D" - :show-discard true - :choices {:req #(and (corp? %) - (in-discard? %))} - :effect (effect (move target :deck)) - :msg (msg "add " - (if (:seen target) - (:title target) - "a card") - " to the bottom of R&D")}]}) - -(define-card "Helium-3 Deposit" - {:async true - :interactive (req true) - :prompt "How many power counters?" - :choices ["0" "1" "2"] - :effect (req (let [c (str->int target)] - (continue-ability - state side - {:choices {:req #(pos? (get-counters % :power))} - :msg (msg "add " c " power counters on " (:title target)) - :effect (effect (add-counter target :power c))} - card nil)))}) - -(define-card "High-Risk Investment" - {:effect (effect (add-counter card :agenda 1)) - :silent (req true) - :abilities [{:cost [:click 1] - :counter-cost [:agenda 1] - :msg (msg "gain " (:credit runner) " [Credits]") - :effect (effect (gain-credits (:credit runner)))}]}) - -(define-card "Hollywood Renovation" - {:install-state :face-up - :events {:advance - {:async true - :req (req (same-card? card target)) - :effect (req (let [n (if (>= (get-counters (get-card state card) :advancement) 6) 2 1)] - (continue-ability - state side - {:choices {:req #(and (not (same-card? % card)) - (can-be-advanced? %))} - :msg (msg "place " n - " advancement tokens on " - (card-str state target)) - :effect (effect (add-prop :corp target :advance-counter n {:placed true}))} - card nil)))}}}) - -(define-card "Hostile Takeover" - {:msg "gain 7 [Credits] and take 1 bad publicity" - :effect (effect (gain-credits 7) - (gain-bad-publicity :corp 1)) - :interactive (req true)}) - -(define-card "House of Knives" - {:effect (effect (add-counter card :agenda 3)) - :silent (req true) - :abilities [{:counter-cost [:agenda 1] - :msg "do 1 net damage" - :req (req (:run @state)) - :once :per-run - :effect (effect (damage eid :net 1 {:card card}))}]}) - -(define-card "Hyperloop Extension" - (let [he (req (gain-credits state :corp 3) - (system-msg state side (str "uses Hyperloop Extension to gain 3 [Credits]")))] - {:effect he - :stolen {:effect he}})) - -(define-card "Ikawah Project" - {:steal-cost-bonus (req [:credit 2 :click 1])}) - -(define-card "Illicit Sales" - {:async true - :effect (req (wait-for (resolve-ability - state side - {:optional - {:prompt "Take 1 bad publicity from Illicit Sales?" - :yes-ability {:msg "take 1 bad publicity" - :effect (effect (gain-bad-publicity :corp 1))}}} - card nil) - (let [n (* 3 (count-bad-pub state))] - (gain-credits state side n) - (system-msg state side (str "gains " n " [Credits] from Illicit Sales")) - (effect-completed state side eid))))}) - -(define-card "Improved Protein Source" - {:msg "make the Runner gain 4 [Credits]" - :effect (effect (gain-credits :runner 4)) - :interactive (req true) - :stolen {:msg "make the Runner gain 4 [Credits]" - :effect (effect (gain-credits :runner 4))}}) - -(define-card "Improved Tracers" - {:silent (req true) - :effect (req (update-all-ice state side)) - :swapped {:effect (req (update-all-ice state side))} - :events {:pre-ice-strength {:req (req (has-subtype? target "Tracer")) - :effect (effect (ice-strength-bonus 1 target))} - :pre-init-trace {:req (req (and (has-subtype? target "Tracer") - (= :subroutine (:source-type (second targets))))) - :effect (effect (init-trace-bonus 1))}}}) - -(define-card "Jumon" - {:events - {:corp-turn-ends - {:req (req (some #(and (= (last (:zone %)) :content) - (is-remote? (second (:zone %)))) - (all-installed state :corp))) - :prompt "Select a card to place 2 advancement tokens on" - :player :corp - :choices {:req #(and (= (last (:zone %)) :content) - (is-remote? (second (:zone %))))} - :msg (msg "place 2 advancement token on " (card-str state target)) - :effect (effect (add-prop :corp target :advance-counter 2 {:placed true}))}}}) - -(define-card "Labyrinthine Servers" - {:interactions {:prevent [{:type #{:jack-out} - :req (req (pos? (get-counters card :power)))}]} - :silent (req true) - :effect (effect (add-counter card :power 2)) - :abilities [{:req (req (:run @state)) - :counter-cost [:power 1] - :effect (req (let [ls (filter #(= "Labyrinthine Servers" (:title %)) (:scored corp))] - (jack-out-prevent state side) - (when (zero? (reduce + (for [c ls] (get-counters c :power)))) - (swap! state update-in [:prevent] dissoc :jack-out)))) - :msg "prevent the Runner from jacking out"}]}) - -(define-card "License Acquisition" - {:interactive (req true) - :prompt "Select an asset or upgrade to install from Archives or HQ" - :show-discard true - :choices {:req #(and (#{"Asset" "Upgrade"} (:type %)) - (#{[:hand] [:discard]} (:zone %)) - (corp? %))} - :msg (msg "install and rez " (:title target) ", ignoring all costs") - :effect (effect (corp-install eid target nil {:install-state :rezzed-no-cost}))}) - -(define-card "Mandatory Seed Replacement" - (letfn [(msr [] {:prompt "Select two pieces of ICE to swap positions" - :choices {:req #(and (installed? %) - (ice? %)) - :max 2} - :async true - :effect (req (if (= (count targets) 2) - (do (swap-ice state side (first targets) (second targets)) - (system-msg state side - (str "swaps the position of " - (card-str state (first targets)) - " and " - (card-str state (second targets)))) - (continue-ability state side (msr) card nil)) - (do (system-msg state :corp (str "has finished rearranging ICE")) - (effect-completed state side eid))))})] - {:async true - :msg "rearrange any number of ICE" - :effect (effect (continue-ability (msr) card nil))})) - -(define-card "Mandatory Upgrades" - {:msg "gain an additional [Click] per turn" - :silent (req true) - :effect (req (gain state :corp - :click-per-turn 1)) - :swapped {:msg "gain an additional [Click] per turn" - :effect (req (when (= (:active-player @state) :corp) - (gain state :corp :click 1)) - (gain state :corp :click-per-turn 1))} - :leave-play (req (lose state :corp - :click 1 - :click-per-turn 1))}) - -(define-card "Market Research" - {:interactive (req true) - :req (req tagged) - :effect (effect (add-counter card :agenda 1) - (set-prop card :agendapoints 3))}) - -(define-card "Medical Breakthrough" - {:silent (req true) - :effect (effect (update-all-advancement-costs)) - :stolen {:effect (effect (update-all-advancement-costs))} - :advancement-cost-bonus (req (- (count (filter #(= (:title %) "Medical Breakthrough") - (concat (:scored corp) (:scored runner))))))}) - -(define-card "Merger" - {:agendapoints-runner (req 3)}) - -(define-card "Meteor Mining" - {:interactive (req true) - :async true - :prompt "Use Meteor Mining?" - :choices (req (if (< (count-tags state) 2) - ["Gain 7 [Credits]" "No action"] - ["Gain 7 [Credits]" "Do 7 meat damage" "No action"])) - :effect (req (case target - "Gain 7 [Credits]" - (do (gain-credits state side 7) - (system-msg state side "uses Meteor Mining to gain 7 [Credits]") - (effect-completed state side eid)) - "Do 7 meat damage" - (do (damage state side eid :meat 7 {:card card}) - (system-msg state side "uses Meteor Mining do 7 meat damage")) - "No action" - (do (system-msg state side "does not use Meteor Mining") - (effect-completed state side eid))))}) - -(define-card "NAPD Contract" - {:steal-cost-bonus (req [:credit 4]) - :advancement-cost-bonus (req (count-bad-pub state))}) - -(define-card "Net Quarantine" - (let [nq {:effect (req (let [extra (int (/ (:runner-spent target) 2))] - (when (pos? extra) - (gain-credits state side extra) - (system-msg state :corp (str "uses Net Quarantine to gain " extra "[Credits]")))))}] - {:events {:pre-init-trace {:once :per-turn - :silent (req true) - :effect (req (system-msg state :corp "uses Net Quarantine to reduce Runner's base link to zero") - (swap! state assoc-in [:trace :force-link] 0))} - :successful-trace nq - :unsuccessful-trace nq}})) - -(define-card "New Construction" - {:install-state :face-up - :events {:advance - {:optional - {:req (req (same-card? card target)) - :prompt "Install a card from HQ in a new remote?" - :yes-ability {:prompt "Select a card to install" - :choices {:req #(and (not (operation? %)) - (not (ice? %)) - (corp? %) - (in-hand? %))} - :msg (msg "install a card from HQ" - (when (>= (get-counters (get-card state card) :advancement) 5) - " and rez it, ignoring all costs")) - :effect (req (if (>= (get-counters (get-card state card) :advancement) 5) - (do (corp-install state side target "New remote" - {:install-state :rezzed-no-cost}) - (trigger-event state side :rez target)) - (corp-install state side target "New remote")))}}}}}) - -(define-card "NEXT Wave 2" - {:not-when-scored true - :req (req (some #(and (rezzed? %) - (ice? %) - (has-subtype? % "NEXT")) - (all-installed state :corp))) - :optional {:prompt "Do 1 brain damage with NEXT Wave 2?" - :yes-ability {:msg "do 1 brain damage" - :effect (effect (damage eid :brain 1 {:card card}))}}}) - -(define-card "Nisei MK II" - {:silent (req true) - :effect (effect (add-counter card :agenda 1)) - :abilities [{:req (req (:run @state)) - :counter-cost [:agenda 1] - :msg "end the run" - :async true - :effect (effect (end-run eid card))}]}) - -(define-card "Oaktown Renovation" - {:install-state :face-up - :events {:advance {:req (req (same-card? card target)) - :msg (msg "gain " (if (>= (get-counters (get-card state card) :advancement) 5) "3" "2") " [Credits]") - :effect (req (gain-credits state side - (if (>= (get-counters (get-card state card) :advancement) 5) 3 2)))}}}) - -(define-card "Obokata Protocol" - {:steal-cost-bonus (req [:net 4])}) - -(define-card "Paper Trail" - {:trace {:base 6 - :successful {:msg "trash all connection and job resources" - :effect (req (doseq [resource (filter #(or (has-subtype? % "Job") - (has-subtype? % "Connection")) - (all-active-installed state :runner))] - (trash state side resource)))}}}) - -(define-card "Personality Profiles" - (let [pp {:req (req (pos? (count (:hand runner)))) - :effect (effect (trash (first (shuffle (:hand runner))))) - :msg (msg "force the Runner to trash " (:title (last (:discard runner))) " from their Grip at random")}] - {:events {:searched-stack pp - :runner-install (assoc pp :req (req (and (some #{:discard} (:previous-zone target)) - (pos? (count (:hand runner))))))}})) - -(define-card "Philotic Entanglement" - {:interactive (req true) - :req (req (pos? (count (:scored runner)))) - :msg (msg "do " (count (:scored runner)) " net damage") - :effect (effect (damage eid :net (count (:scored runner)) {:card card}))}) - -(define-card "Posted Bounty" - {:optional {:prompt "Forfeit Posted Bounty to give the Runner 1 tag and take 1 bad publicity?" - :yes-ability {:msg "give the Runner 1 tag and take 1 bad publicity" - :async true - :effect (effect (gain-bad-publicity :corp eid 1) - (gain-tags :corp eid 1) - (forfeit card))}}}) - -(define-card "Priority Requisition" - {:interactive (req true) - :choices {:req #(and (ice? %) - (not (rezzed? %)) - (installed? %))} - :effect (effect (rez target {:ignore-cost :all-costs}))}) - -(define-card "Private Security Force" - {:abilities [{:req (req tagged) - :cost [:click 1] - :effect (effect (damage eid :meat 1 {:card card})) - :msg "do 1 meat damage"}]}) - -(define-card "Profiteering" - {:interactive (req true) - :choices ["0" "1" "2" "3"] - :prompt "How many bad publicity?" - :msg (msg "take " target " bad publicity and gain " (* 5 (str->int target)) " [Credits]") - :effect (req (let [bp (count-bad-pub state)] - (gain-bad-publicity state :corp eid (str->int target)) - (if (< bp (count-bad-pub state)) - (gain-credits state :corp (* 5 (str->int target))))))}) - -(define-card "Project Ares" - (letfn [(trash-count-str [card] - (quantify (- (get-counters card :advancement) 4) "installed card"))] - {:silent (req true) - :req (req (and (> (get-counters card :advancement) 4) - (pos? (count (all-installed state :runner))))) - :msg (msg "force the Runner to trash " (trash-count-str card) " and take 1 bad publicity") - :async true - :effect (effect (show-wait-prompt :corp "Runner to trash installed cards") - (continue-ability - :runner - {:prompt (msg "Select " (trash-count-str card) " installed cards to trash") - :choices {:max (min (- (get-counters card :advancement) 4) - (count (all-installed state :runner))) - :req #(and (runner? %) - (installed? %))} - :effect (effect (trash-cards targets) - (system-msg (str "trashes " (join ", " (map :title targets)))) - (gain-bad-publicity :corp 1))} - card nil) - (clear-wait-prompt :corp))})) - -(define-card "Project Atlas" - {:silent (req true) - :effect (effect (add-counter card :agenda (max 0 (- (get-counters card :advancement) 3)))) - :abilities [{:counter-cost [:agenda 1] - :prompt "Choose a card" - :label "Search R&D and add 1 card to HQ" - ;; we need the req or the prompt will still show - :req (req (pos? (get-counters card :agenda))) - :msg (msg "add " (:title target) " to HQ from R&D") - :choices (req (cancellable (:deck corp) :sorted)) - :cancel-effect (effect (system-msg "cancels the effect of Project Atlas")) - :effect (effect (shuffle! :deck) - (move target :hand))}]}) - -(define-card "Project Beale" - {:interactive (req true) - :agendapoints-runner (req 2) - :effect (req (let [n (quot (- (get-counters card :advancement) 3) 2)] - (set-prop state side card - :counter {:agenda n} - :agendapoints (+ 2 n))))}) - -(define-card "Project Kusanagi" - {:silent (req true) - :effect (effect (add-counter card :agenda (- (get-counters card :advancement) 2))) - :abilities [{:counter-cost [:agenda 1] - :msg "make a piece of ICE gain \"[Subroutine] Do 1 net damage\" after all its other subroutines for the remainder of the run"}]}) - -(define-card "Project Vitruvius" - {:silent (req true) - :effect (effect (add-counter card :agenda (- (get-counters card :advancement) 3))) - :abilities [{:counter-cost [:agenda 1] - :prompt "Choose a card in Archives to add to HQ" - :show-discard true - :choices {:req #(and (in-discard? %) - (corp? %))} - :req (req (pos? (get-counters card :agenda))) - :msg (msg "add " - (if (:seen target) - (:title target) "an unseen card ") - " to HQ from Archives") - :effect (effect (move target :hand))}]}) - -(define-card "Project Wotan" - {:silent (req true) - :effect (effect (add-counter card :agenda 3)) - :abilities [{:req (req (and (ice? current-ice) - (rezzed? current-ice) - (has-subtype? current-ice "Bioroid"))) - :counter-cost [:agenda 1] - :msg (str "make the approached piece of Bioroid ICE gain \"[Subroutine] End the run\"" - "after all its other subroutines for the remainder of this run")}]}) - -(define-card "Project Yagi-Uda" - (letfn [(put-back-counter [state side card] - (set-prop state side card :counter - (merge - (:counter card) - {:agenda (+ 1 (get-counters card :agenda))}))) - (choose-swap [to-swap] - {:prompt (str "Select a card in HQ to swap with " (:title to-swap)) - :choices {:not-self true - :req #(and (corp? %) - (in-hand? %) - (if (ice? to-swap) - (ice? %) - (or (agenda? %) - (asset? %) - (upgrade? %))))} - :msg (msg "swap " (card-str state to-swap) " with a card from HQ") - :effect (req (move state :corp to-swap (:zone target) {:keep-server-alive true}) - (move state :corp target (:zone to-swap) {:keep-server-alive true}) - (clear-wait-prompt state :runner)) - :cancel-effect (effect (put-back-counter card) - (clear-wait-prompt :runner))}) - (choose-card [run-server] - {:prompt "Choose a card in or protecting the attacked server." - :choices {:req #(= (first run-server) (second (:zone %)))} - :effect (effect (continue-ability (choose-swap target) card nil)) - :cancel-effect (effect (put-back-counter card) - (clear-wait-prompt :runner))})] - {:silent (req true) - :effect (effect (add-counter card :agenda (- (get-counters card :advancement) 3))) - :abilities [{:counter-cost [:agenda 1] - :req (req run) - :effect (effect (show-wait-prompt :runner "Corp to use Project Yagi-Uda") - (continue-ability (choose-card (:server run)) - card nil))}]})) -(define-card "Puppet Master" - {:events {:successful-run - {:interactive (req true) - :async true - :effect (req (show-wait-prompt state :runner "Corp to use Puppet Master") - (continue-ability - state :corp - {:prompt "Select a card to place 1 advancement token on" - :player :corp - :choices {:req can-be-advanced?} - :cancel-effect (effect (clear-wait-prompt :runner) - (effect-completed eid)) - :msg (msg "place 1 advancement token on " (card-str state target)) - :effect (effect (add-prop :corp target :advance-counter 1 {:placed true}) - (clear-wait-prompt :runner))} card nil))}}}) - -(define-card "Quantum Predictive Model" - {:flags {:rd-reveal (req true)} - :access {:req (req tagged) - :async true - :interactive (req true) - :effect (req (wait-for (as-agenda state side card 1) - (continue-ability - state :runner - {:prompt "Quantum Predictive Model was added to the corp's score area" - :choices ["OK"]} - card nil))) - :msg "add it to their score area and gain 1 agenda point"}}) - -(define-card "Rebranding Team" - (letfn [(get-assets [state corp] - (filter asset? (concat (all-installed state :corp) - (:deck corp) - (:hand corp) - (:discard corp)))) - (add-ad [state side c] - (update! state side (assoc-in c [:persistent :subtype] "Advertisement")))] - {:interactive (req true) - :msg "make all assets gain Advertisement" - :effect (req (doseq [c (get-assets state corp)] (add-ad state side c))) - :swapped {:msg "make all assets gain Advertisement" - :effect (req (doseq [c (get-assets state corp)] (add-ad state side c)))} - :leave-play (req (doseq [c (get-assets state corp)] - (update! state side (assoc-in c [:persistent :subtype] - (->> (split (or (-> c :persistent :subtype) "") #" - ") - (drop 1) ;so that all actual ads remain ads if agenda leaves play - (join " - "))))))})) - -(define-card "Reeducation" - (letfn [(corp-final [chosen original] - {:prompt (str "The bottom cards of R&D will be " (join ", " (map :title chosen)) ".") - :choices ["Done" "Start over"] - :async true - :msg (req (let [n (count chosen)] - (str "add " n " cards from HQ to the bottom of R&D and draw " n " cards." - " The Runner randomly adds " (if (<= n (count (:hand runner))) n 0) - " cards from their Grip to the bottom of the Stack"))) - :effect (req (let [n (count chosen)] - (if (= target "Done") - (do (doseq [c (reverse chosen)] (move state :corp c :deck)) - (draw state :corp n) - ; if corp chooses more cards than runner's hand, don't shuffle runner hand back into Stack - (when (<= n (count (:hand runner))) - (doseq [r (take n (shuffle (:hand runner)))] (move state :runner r :deck))) - (clear-wait-prompt state :runner) - (effect-completed state side eid)) - (continue-ability state side (corp-choice original '() original) card nil))))}) - (corp-choice [remaining chosen original] ; Corp chooses cards until they press 'Done' - {:prompt "Choose a card to move to bottom of R&D" - :choices (conj (vec remaining) "Done") + (effect-completed state side eid))))})] + {:async true + :msg "add up to 3 cards from R&D to HQ" + :effect (effect (continue-ability (graft 1) card nil))}) + + "Hades Fragment" + {:flags {:corp-phase-12 (req (and (not-empty (get-in @state [:corp :discard])) + (is-scored? state :corp card)))} + :abilities [{:prompt "Select a card to add to the bottom of R&D" + :show-discard true + :choices {:req #(and (corp? %) + (in-discard? %))} + :effect (effect (move target :deck)) + :msg (msg "add " + (if (:seen target) + (:title target) + "a card") + " to the bottom of R&D")}]} + + "Helium-3 Deposit" + {:async true + :interactive (req true) + :prompt "How many power counters?" + :choices ["0" "1" "2"] + :effect (req (let [c (str->int target)] + (continue-ability + state side + {:choices {:req #(pos? (get-counters % :power))} + :msg (msg "add " c " power counters on " (:title target)) + :effect (effect (add-counter target :power c))} + card nil)))} + + "High-Risk Investment" + {:effect (effect (add-counter card :agenda 1)) + :silent (req true) + :abilities [{:cost [:click 1] + :counter-cost [:agenda 1] + :msg (msg "gain " (:credit runner) " [Credits]") + :effect (effect (gain-credits (:credit runner)))}]} + + "Hollywood Renovation" + {:install-state :face-up + :events {:advance + {:async true + :req (req (same-card? card target)) + :effect (req (let [n (if (>= (get-counters (get-card state card) :advancement) 6) 2 1)] + (continue-ability + state side + {:choices {:req #(and (not (same-card? % card)) + (can-be-advanced? %))} + :msg (msg "place " n + " advancement tokens on " + (card-str state target)) + :effect (effect (add-prop :corp target :advance-counter n {:placed true}))} + card nil)))}}} + + "Hostile Takeover" + {:msg "gain 7 [Credits] and take 1 bad publicity" + :effect (effect (gain-credits 7) + (gain-bad-publicity :corp 1)) + :interactive (req true)} + + "House of Knives" + {:effect (effect (add-counter card :agenda 3)) + :silent (req true) + :abilities [{:counter-cost [:agenda 1] + :msg "do 1 net damage" + :req (req (:run @state)) + :once :per-run + :effect (effect (damage eid :net 1 {:card card}))}]} + + "Hyperloop Extension" + (let [he (req (gain-credits state :corp 3) + (system-msg state side (str "uses Hyperloop Extension to gain 3 [Credits]")))] + {:effect he + :stolen {:effect he}}) + + "Ikawah Project" + {:steal-cost-bonus (req [:credit 2 :click 1])} + + "Illicit Sales" + {:async true + :effect (req (wait-for (resolve-ability + state side + {:optional + {:prompt "Take 1 bad publicity from Illicit Sales?" + :yes-ability {:msg "take 1 bad publicity" + :effect (effect (gain-bad-publicity :corp 1))}}} + card nil) + (let [n (* 3 (count-bad-pub state))] + (gain-credits state side n) + (system-msg state side (str "gains " n " [Credits] from Illicit Sales")) + (effect-completed state side eid))))} + + "Improved Protein Source" + {:msg "make the Runner gain 4 [Credits]" + :effect (effect (gain-credits :runner 4)) + :interactive (req true) + :stolen {:msg "make the Runner gain 4 [Credits]" + :effect (effect (gain-credits :runner 4))}} + + "Improved Tracers" + {:silent (req true) + :effect (req (update-all-ice state side)) + :swapped {:effect (req (update-all-ice state side))} + :events {:pre-ice-strength {:req (req (has-subtype? target "Tracer")) + :effect (effect (ice-strength-bonus 1 target))} + :pre-init-trace {:req (req (and (has-subtype? target "Tracer") + (= :subroutine (:source-type (second targets))))) + :effect (effect (init-trace-bonus 1))}}} + + "Jumon" + {:events + {:corp-turn-ends + {:req (req (some #(and (= (last (:zone %)) :content) + (is-remote? (second (:zone %)))) + (all-installed state :corp))) + :prompt "Select a card to place 2 advancement tokens on" + :player :corp + :choices {:req #(and (= (last (:zone %)) :content) + (is-remote? (second (:zone %))))} + :msg (msg "place 2 advancement token on " (card-str state target)) + :effect (effect (add-prop :corp target :advance-counter 2 {:placed true}))}}} + + "Labyrinthine Servers" + {:interactions {:prevent [{:type #{:jack-out} + :req (req (pos? (get-counters card :power)))}]} + :silent (req true) + :effect (effect (add-counter card :power 2)) + :abilities [{:req (req (:run @state)) + :counter-cost [:power 1] + :effect (req (let [ls (filter #(= "Labyrinthine Servers" (:title %)) (:scored corp))] + (jack-out-prevent state side) + (when (zero? (reduce + (for [c ls] (get-counters c :power)))) + (swap! state update-in [:prevent] dissoc :jack-out)))) + :msg "prevent the Runner from jacking out"}]} + + "License Acquisition" + {:interactive (req true) + :prompt "Select an asset or upgrade to install from Archives or HQ" + :show-discard true + :choices {:req #(and (#{"Asset" "Upgrade"} (:type %)) + (#{[:hand] [:discard]} (:zone %)) + (corp? %))} + :msg (msg "install and rez " (:title target) ", ignoring all costs") + :effect (effect (corp-install eid target nil {:install-state :rezzed-no-cost}))} + + "Mandatory Seed Replacement" + (letfn [(msr [] {:prompt "Select two pieces of ICE to swap positions" + :choices {:req #(and (installed? %) + (ice? %)) + :max 2} + :async true + :effect (req (if (= (count targets) 2) + (do (swap-ice state side (first targets) (second targets)) + (system-msg state side + (str "swaps the position of " + (card-str state (first targets)) + " and " + (card-str state (second targets)))) + (continue-ability state side (msr) card nil)) + (do (system-msg state :corp (str "has finished rearranging ICE")) + (effect-completed state side eid))))})] + {:async true + :msg "rearrange any number of ICE" + :effect (effect (continue-ability (msr) card nil))}) + + "Mandatory Upgrades" + {:msg "gain an additional [Click] per turn" + :silent (req true) + :effect (req (gain state :corp + :click-per-turn 1)) + :swapped {:msg "gain an additional [Click] per turn" + :effect (req (when (= (:active-player @state) :corp) + (gain state :corp :click 1)) + (gain state :corp :click-per-turn 1))} + :leave-play (req (lose state :corp + :click 1 + :click-per-turn 1))} + + "Market Research" + {:interactive (req true) + :req (req tagged) + :effect (effect (add-counter card :agenda 1) + (set-prop card :agendapoints 3))} + + "Medical Breakthrough" + {:silent (req true) + :effect (effect (update-all-advancement-costs)) + :stolen {:effect (effect (update-all-advancement-costs))} + :advancement-cost-bonus (req (- (count (filter #(= (:title %) "Medical Breakthrough") + (concat (:scored corp) (:scored runner))))))} + + "Merger" + {:agendapoints-runner (req 3)} + + "Meteor Mining" + {:interactive (req true) + :async true + :prompt "Use Meteor Mining?" + :choices (req (if (< (count-tags state) 2) + ["Gain 7 [Credits]" "No action"] + ["Gain 7 [Credits]" "Do 7 meat damage" "No action"])) + :effect (req (case target + "Gain 7 [Credits]" + (do (gain-credits state side 7) + (system-msg state side "uses Meteor Mining to gain 7 [Credits]") + (effect-completed state side eid)) + "Do 7 meat damage" + (do (damage state side eid :meat 7 {:card card}) + (system-msg state side "uses Meteor Mining do 7 meat damage")) + "No action" + (do (system-msg state side "does not use Meteor Mining") + (effect-completed state side eid))))} + + "NAPD Contract" + {:steal-cost-bonus (req [:credit 4]) + :advancement-cost-bonus (req (count-bad-pub state))} + + "Net Quarantine" + (let [nq {:effect (req (let [extra (int (/ (:runner-spent target) 2))] + (when (pos? extra) + (gain-credits state side extra) + (system-msg state :corp (str "uses Net Quarantine to gain " extra "[Credits]")))))}] + {:events {:pre-init-trace {:once :per-turn + :silent (req true) + :effect (req (system-msg state :corp "uses Net Quarantine to reduce Runner's base link to zero") + (swap! state assoc-in [:trace :force-link] 0))} + :successful-trace nq + :unsuccessful-trace nq}}) + + "New Construction" + {:install-state :face-up + :events {:advance + {:optional + {:req (req (same-card? card target)) + :prompt "Install a card from HQ in a new remote?" + :yes-ability {:prompt "Select a card to install" + :choices {:req #(and (not (operation? %)) + (not (ice? %)) + (corp? %) + (in-hand? %))} + :msg (msg "install a card from HQ" + (when (>= (get-counters (get-card state card) :advancement) 5) + " and rez it, ignoring all costs")) + :effect (req (if (>= (get-counters (get-card state card) :advancement) 5) + (do (corp-install state side target "New remote" + {:install-state :rezzed-no-cost}) + (trigger-event state side :rez target)) + (corp-install state side target "New remote")))}}}}} + + "NEXT Wave 2" + {:not-when-scored true + :req (req (some #(and (rezzed? %) + (ice? %) + (has-subtype? % "NEXT")) + (all-installed state :corp))) + :optional {:prompt "Do 1 brain damage with NEXT Wave 2?" + :yes-ability {:msg "do 1 brain damage" + :effect (effect (damage eid :brain 1 {:card card}))}}} + + "Nisei MK II" + {:silent (req true) + :effect (effect (add-counter card :agenda 1)) + :abilities [{:req (req (:run @state)) + :counter-cost [:agenda 1] + :msg "end the run" + :async true + :effect (effect (end-run eid card))}]} + + "Oaktown Renovation" + {:install-state :face-up + :events {:advance {:req (req (same-card? card target)) + :msg (msg "gain " (if (>= (get-counters (get-card state card) :advancement) 5) "3" "2") " [Credits]") + :effect (req (gain-credits state side + (if (>= (get-counters (get-card state card) :advancement) 5) 3 2)))}}} + + "Obokata Protocol" + {:steal-cost-bonus (req [:net 4])} + + "Paper Trail" + {:trace {:base 6 + :successful {:msg "trash all connection and job resources" + :effect (req (doseq [resource (filter #(or (has-subtype? % "Job") + (has-subtype? % "Connection")) + (all-active-installed state :runner))] + (trash state side resource)))}}} + + "Personality Profiles" + (let [pp {:req (req (pos? (count (:hand runner)))) + :effect (effect (trash (first (shuffle (:hand runner))))) + :msg (msg "force the Runner to trash " (:title (last (:discard runner))) " from their Grip at random")}] + {:events {:searched-stack pp + :runner-install (assoc pp :req (req (and (some #{:discard} (:previous-zone target)) + (pos? (count (:hand runner))))))}}) + + "Philotic Entanglement" + {:interactive (req true) + :req (req (pos? (count (:scored runner)))) + :msg (msg "do " (count (:scored runner)) " net damage") + :effect (effect (damage eid :net (count (:scored runner)) {:card card}))} + + "Posted Bounty" + {:optional {:prompt "Forfeit Posted Bounty to give the Runner 1 tag and take 1 bad publicity?" + :yes-ability {:msg "give the Runner 1 tag and take 1 bad publicity" + :async true + :effect (effect (gain-bad-publicity :corp eid 1) + (gain-tags :corp eid 1) + (forfeit card))}}} + + "Priority Requisition" + {:interactive (req true) + :choices {:req #(and (ice? %) + (not (rezzed? %)) + (installed? %))} + :effect (effect (rez target {:ignore-cost :all-costs}))} + + "Private Security Force" + {:abilities [{:req (req tagged) + :cost [:click 1] + :effect (effect (damage eid :meat 1 {:card card})) + :msg "do 1 meat damage"}]} + + "Profiteering" + {:interactive (req true) + :choices ["0" "1" "2" "3"] + :prompt "How many bad publicity?" + :msg (msg "take " target " bad publicity and gain " (* 5 (str->int target)) " [Credits]") + :effect (req (let [bp (count-bad-pub state)] + (gain-bad-publicity state :corp eid (str->int target)) + (if (< bp (count-bad-pub state)) + (gain-credits state :corp (* 5 (str->int target))))))} + + "Project Ares" + (letfn [(trash-count-str [card] + (quantify (- (get-counters card :advancement) 4) "installed card"))] + {:silent (req true) + :req (req (and (> (get-counters card :advancement) 4) + (pos? (count (all-installed state :runner))))) + :msg (msg "force the Runner to trash " (trash-count-str card) " and take 1 bad publicity") + :async true + :effect (effect (show-wait-prompt :corp "Runner to trash installed cards") + (continue-ability + :runner + {:prompt (msg "Select " (trash-count-str card) " installed cards to trash") + :choices {:max (min (- (get-counters card :advancement) 4) + (count (all-installed state :runner))) + :req #(and (runner? %) + (installed? %))} + :effect (effect (trash-cards targets) + (system-msg (str "trashes " (join ", " (map :title targets)))) + (gain-bad-publicity :corp 1))} + card nil) + (clear-wait-prompt :corp))}) + + "Project Atlas" + {:silent (req true) + :effect (effect (add-counter card :agenda (max 0 (- (get-counters card :advancement) 3)))) + :abilities [{:counter-cost [:agenda 1] + :prompt "Choose a card" + :label "Search R&D and add 1 card to HQ" + ;; we need the req or the prompt will still show + :req (req (pos? (get-counters card :agenda))) + :msg (msg "add " (:title target) " to HQ from R&D") + :choices (req (cancellable (:deck corp) :sorted)) + :cancel-effect (effect (system-msg "cancels the effect of Project Atlas")) + :effect (effect (shuffle! :deck) + (move target :hand))}]} + + "Project Beale" + {:interactive (req true) + :agendapoints-runner (req 2) + :effect (req (let [n (quot (- (get-counters card :advancement) 3) 2)] + (set-prop state side card + :counter {:agenda n} + :agendapoints (+ 2 n))))} + + "Project Kusanagi" + {:silent (req true) + :effect (effect (add-counter card :agenda (- (get-counters card :advancement) 2))) + :abilities [{:counter-cost [:agenda 1] + :msg "make a piece of ICE gain \"[Subroutine] Do 1 net damage\" after all its other subroutines for the remainder of the run"}]} + + "Project Vitruvius" + {:silent (req true) + :effect (effect (add-counter card :agenda (- (get-counters card :advancement) 3))) + :abilities [{:counter-cost [:agenda 1] + :prompt "Choose a card in Archives to add to HQ" + :show-discard true + :choices {:req #(and (in-discard? %) + (corp? %))} + :req (req (pos? (get-counters card :agenda))) + :msg (msg "add " + (if (:seen target) + (:title target) "an unseen card ") + " to HQ from Archives") + :effect (effect (move target :hand))}]} + + "Project Wotan" + {:silent (req true) + :effect (effect (add-counter card :agenda 3)) + :abilities [{:req (req (and (ice? current-ice) + (rezzed? current-ice) + (has-subtype? current-ice "Bioroid"))) + :counter-cost [:agenda 1] + :msg (str "make the approached piece of Bioroid ICE gain \"[Subroutine] End the run\"" + "after all its other subroutines for the remainder of this run")}]} + + "Project Yagi-Uda" + (letfn [(put-back-counter [state side card] + (set-prop state side card :counter + (merge + (:counter card) + {:agenda (+ 1 (get-counters card :agenda))}))) + (choose-swap [to-swap] + {:prompt (str "Select a card in HQ to swap with " (:title to-swap)) + :choices {:not-self true + :req #(and (corp? %) + (in-hand? %) + (if (ice? to-swap) + (ice? %) + (or (agenda? %) + (asset? %) + (upgrade? %))))} + :msg (msg "swap " (card-str state to-swap) " with a card from HQ") + :effect (req (move state :corp to-swap (:zone target) {:keep-server-alive true}) + (move state :corp target (:zone to-swap) {:keep-server-alive true}) + (clear-wait-prompt state :runner)) + :cancel-effect (effect (put-back-counter card) + (clear-wait-prompt :runner))}) + (choose-card [run-server] + {:prompt "Choose a card in or protecting the attacked server." + :choices {:req #(= (first run-server) (second (:zone %)))} + :effect (effect (continue-ability (choose-swap target) card nil)) + :cancel-effect (effect (put-back-counter card) + (clear-wait-prompt :runner))})] + {:silent (req true) + :effect (effect (add-counter card :agenda (- (get-counters card :advancement) 3))) + :abilities [{:counter-cost [:agenda 1] + :req (req run) + :effect (effect (show-wait-prompt :runner "Corp to use Project Yagi-Uda") + (continue-ability (choose-card (:server run)) + card nil))}]}) + "Puppet Master" + {:events {:successful-run + {:interactive (req true) + :async true + :effect (req (show-wait-prompt state :runner "Corp to use Puppet Master") + (continue-ability + state :corp + {:prompt "Select a card to place 1 advancement token on" + :player :corp + :choices {:req can-be-advanced?} + :cancel-effect (effect (clear-wait-prompt :runner) + (effect-completed eid)) + :msg (msg "place 1 advancement token on " (card-str state target)) + :effect (effect (add-prop :corp target :advance-counter 1 {:placed true}) + (clear-wait-prompt :runner))} card nil))}}} + + "Quantum Predictive Model" + {:flags {:rd-reveal (req true)} + :access {:req (req tagged) :async true - :effect (req (let [chosen (cons target chosen)] - (if (not= target "Done") - (continue-ability - state side - (corp-choice (remove-once #(= target %) remaining) chosen original) - card nil) - (if (pos? (count (remove #(= % "Done") chosen))) - (continue-ability state side (corp-final (remove #(= % "Done") chosen) original) card nil) - (do (system-msg state side "does not add any cards from HQ to bottom of R&D") - (clear-wait-prompt state :runner) - (effect-completed state side eid))))))})] - {:async true - :effect (req (show-wait-prompt state :runner "Corp to add cards from HQ to bottom of R&D") - (let [from (get-in @state [:corp :hand])] - (if (pos? (count from)) - (continue-ability state :corp (corp-choice from '() from) card nil) - (do (system-msg state side "does not add any cards from HQ to bottom of R&D") - (effect-completed state side eid)))))})) - -(define-card "Remastered Edition" - {:effect (effect (add-counter card :agenda 1)) - :silent (req true) - :abilities [{:counter-cost [:agenda 1] - :msg (msg "place 1 advancement token on " (card-str state target)) - :choices {:req installed?} - :effect (effect (add-prop target :advance-counter 1 {:placed true}))}]}) - -(define-card "Remote Data Farm" - {:silent (req true) - :msg "increase their maximum hand size by 2" - :effect (effect (gain :hand-size 2)) - :swapped {:msg "increase their maximum hand size by 2" - :effect (effect (gain :hand-size 2))} - :leave-play (effect (lose :hand-size 2))}) - -(define-card "Remote Enforcement" - {:interactive (req true) - :optional {:prompt "Search R&D for a piece of ice to install protecting a remote server?" - :yes-ability - {:async true - :effect (req (when (not-empty (filter ice? (:deck corp))) - (continue-ability - state side - {:async true - :prompt "Choose a piece of ice" - :choices (req (filter ice? (:deck corp))) - :effect (req (let [chosen-ice target] - (continue-ability - state side - {:async true - :prompt (str "Select a server to install " (:title chosen-ice) " on") - :choices (filter #(not (#{"HQ" "Archives" "R&D"} %)) - (corp-install-list state chosen-ice)) - :effect (effect (shuffle! :deck) - (corp-install eid chosen-ice target - {:install-state :rezzed-no-rez-cost}))} - card nil)))} - card nil)))}}}) - -(define-card "Research Grant" - {:interactive (req true) - :silent (req (empty? (filter #(= (:title %) "Research Grant") (all-installed state :corp)))) - :async true - :effect (effect (continue-ability - {:prompt "Select another installed copy of Research Grant to score" - :choices {:req #(= (:title %) "Research Grant")} - :interactive (req true) - :async true - :req (req (not (empty? (filter #(= (:title %) "Research Grant") (all-installed state :corp))))) - :effect (effect (set-prop target :advance-counter (:advancementcost target)) - (score eid (get-card state target))) - :msg "score another installed copy of Research Grant"} - card nil))}) - -(define-card "Restructured Datapool" - {:abilities [{:cost [:click 1] - :trace {:base 2 - :successful {:msg "give the Runner 1 tag" - :async true - :effect (effect (gain-tags eid 1))}}}]}) - -(define-card "SDS Drone Deployment" - {:steal-cost-bonus (req [:program 1]) - :effect (req (show-wait-prompt state :runner "Corp to use SDS Drone Deployment") - (if (seq (all-installed-runner-type state :program)) - (continue-ability - state side - {:prompt "Select a program to trash" - :label "Trash a program" - :msg (msg "trash " (:title target)) - :choices {:req #(and (installed? %) - (program? %))} - :effect (effect (trash target) - (clear-wait-prompt :runner)) - :end-effect (effect (clear-wait-prompt :runner))} - card nil) - (clear-wait-prompt state :runner)))}) - -(define-card "Self-Destruct Chips" - {:silent (req true) - :msg "decrease the Runner's maximum hand size by 1" - :effect (effect (lose :runner :hand-size 1)) - :swapped {:msg "decrease the Runner's maximum hand size by 1" - :effect (effect (lose :runner :hand-size 1))} - :leave-play (effect (gain :runner :hand-size 1))}) - -(define-card "Sensor Net Activation" - {:effect (effect (add-counter card :agenda 1)) - :silent (req true) - :abilities [{:counter-cost [:agenda 1] - :req (req (some #(and (has-subtype? % "Bioroid") (not (rezzed? %))) (all-installed state :corp))) - :prompt "Choose a bioroid to rez, ignoring all costs" - :choices {:req #(and (has-subtype? % "Bioroid") (not (rezzed? %)))} - :msg (msg "rez " (card-str state target) ", ignoring all costs") - :effect (req (let [c target] - (rez state side c {:ignore-cost :all-costs}) - (register-events - state side - {:corp-turn-ends {:effect (effect (derez c) - (unregister-events card))} - :runner-turn-ends {:effect (effect (derez c) - (unregister-events card))}} card)))}] - :events {:corp-turn-ends nil - :runner-turn-ends nil}}) - -(define-card "Sentinel Defense Program" - {:events {:pre-resolve-damage {:req (req (and (= target :brain) - (pos? (last targets)))) - :msg "do 1 net damage" - :effect (effect (damage eid :net 1 {:card card}))}}}) - -(define-card "Show of Force" - {:async true - :msg "do 2 meat damage" - :effect (effect (damage eid :meat 2 {:card card}))}) - -(define-card "SSL Endorsement" - (let [add-credits (effect (add-counter card :credit 9)) - remove-credits {:optional {:req (req (pos? (get-counters card :credit))) - :once :per-turn - :prompt "Gain 3 [Credits] from SSL Endorsement?" - :autoresolve (get-autoresolve :auto-fire) - :yes-ability - {:effect (req (when (pos? (get-counters card :credit)) - (gain-credits state :corp 3) - (system-msg state :corp (str "uses SSL Endorsement to gain 3 [Credits]")) - (add-counter state side card :credit -3)))}}}] - {:effect add-credits - :abilities [(set-autoresolve :auto-fire "whether to take credits off SSL")] - :stolen {:effect add-credits} - :interactive (req true) - :events {:corp-turn-begins remove-credits} - :flags {:has-events-when-stolen true}})) - -(define-card "Standoff" - (letfn [(stand [side] - {:async true - :prompt "Choose one of your installed cards to trash due to Standoff" - :choices {:req #(and (installed? %) - (same-side? side (:side %)))} - :cancel-effect (req (if (= side :runner) - (do (draw state :corp) - (gain-credits state :corp 5) - (clear-wait-prompt state :corp) - (system-msg state :runner "declines to trash a card due to Standoff") - (system-msg state :corp "draws a card and gains 5 [Credits] from Standoff") - (effect-completed state :corp eid)) - (do (system-msg state :corp "declines to trash a card from Standoff") - (clear-wait-prompt state :runner) - (effect-completed state :corp eid)))) - :effect (req (wait-for (trash state side target {:unpreventable true}) - (do - (system-msg state side (str "trashes " (card-str state target) " due to Standoff")) - (clear-wait-prompt state (other-side side)) - (show-wait-prompt state side (str (side-str (other-side side)) " to trash a card for Standoff")) - (continue-ability state (other-side side) (stand (other-side side)) card nil))))})] - {:interactive (req true) - :async true - :effect (effect (show-wait-prompt (str (side-str (other-side side)) " to trash a card for Standoff")) - (continue-ability :runner (stand :runner) card nil))})) - -(define-card "Sting!" - (letfn [(count-opp-stings [state side] - (count (filter #(= (:title %) "Sting!") (get-in @state [(other-side side) :scored]))))] - {:msg (msg "deal " (inc (count-opp-stings state :corp)) " net damage") - :async true - :effect (effect (damage eid :net (inc (count-opp-stings state :corp)) {:card card})) - :stolen {:msg (msg "deal " (inc (count-opp-stings state :runner)) " net damage") + :interactive (req true) + :effect (req (wait-for (as-agenda state side card 1) + (continue-ability + state :runner + {:prompt "Quantum Predictive Model was added to the corp's score area" + :choices ["OK"]} + card nil))) + :msg "add it to their score area and gain 1 agenda point"}} + + "Rebranding Team" + (letfn [(get-assets [state corp] + (filter asset? (concat (all-installed state :corp) + (:deck corp) + (:hand corp) + (:discard corp)))) + (add-ad [state side c] + (update! state side (assoc-in c [:persistent :subtype] "Advertisement")))] + {:interactive (req true) + :msg "make all assets gain Advertisement" + :effect (req (doseq [c (get-assets state corp)] (add-ad state side c))) + :swapped {:msg "make all assets gain Advertisement" + :effect (req (doseq [c (get-assets state corp)] (add-ad state side c)))} + :leave-play (req (doseq [c (get-assets state corp)] + (update! state side (assoc-in c [:persistent :subtype] + (->> (split (or (-> c :persistent :subtype) "") #" - ") + (drop 1) ;so that all actual ads remain ads if agenda leaves play + (join " - "))))))}) + + "Reeducation" + (letfn [(corp-final [chosen original] + {:prompt (str "The bottom cards of R&D will be " (join ", " (map :title chosen)) ".") + :choices ["Done" "Start over"] :async true - :effect (effect (damage eid :net (inc (count-opp-stings state :runner)) {:card card}))}})) - -(define-card "Successful Field Test" - (letfn [(sft [n max] {:prompt "Select a card in HQ to install with Successful Field Test" - :priority -1 - :async true - :choices {:req #(and (corp? %) - (not (operation? %)) - (in-hand? %))} - :effect (req (wait-for - (corp-install state side target nil {:ignore-all-cost true}) - (if (< n max) - (continue-ability state side (sft (inc n) max) card nil) - (effect-completed state side eid))))})] - {:async true - :msg "install cards from HQ, ignoring all costs" - :effect (req (let [max (count (filter (complement operation?) (:hand corp)))] - (continue-ability state side (sft 1 max) card nil)))})) - -(define-card "Superior Cyberwalls" - (ice-boost-agenda "Barrier")) - -(define-card "TGTBT" - {:flags {:rd-reveal (req true)} - :access {:msg "give the Runner 1 tag" - :async true - :effect (effect (gain-tags eid 1))}}) - -(define-card "The Cleaners" - {:events {:pre-damage {:req (req (and (= target :meat) - (= side :corp))) - :msg "do 1 additional meat damage" - :effect (effect (damage-bonus :meat 1))}}}) - -(define-card "The Future is Now" - {:interactive (req true) - :prompt "Choose a card to add to HQ" - :choices (req (:deck corp)) - :msg (msg "add a card from R&D to HQ and shuffle R&D") - :req (req (pos? (count (:deck corp)))) - :effect (effect (shuffle! :deck) - (move target :hand))}) - -(define-card "The Future Perfect" - {:flags {:rd-reveal (req true)} - :access - {:psi {:req (req (not installed)) - :not-equal {:msg "prevent it from being stolen" - :effect (effect (register-run-flag! - card :can-steal - (fn [_ _ c] (not (same-card? c card)))) - (effect-completed eid))}}}}) - -(define-card "Timely Public Release" - {:silent (req true) - :effect (effect (add-counter card :agenda 1)) - :abilities [{:counter-cost [:agenda 1] - :label "Install a piece of ice in any position, ignoring all costs" - :prompt "Select a piece of ice to install" - :show-discard true - :choices {:req #(and (ice? %) - (or (in-hand? %) - (in-discard? %)))} - :msg (msg "install " - (if (and (in-discard? target) - (or (faceup? target) - (not (facedown? target)))) - (:title target) - "ICE") - " from " (zone->name (:zone target))) - :effect (effect - (continue-ability - (let [chosen-ice target] - {:prompt "Choose a server" - :choices (req servers) - :effect (effect - (continue-ability - (let [chosen-server target - num-ice (count (get-in (:corp @state) - (conj (server->zone state target) :ices)))] - {:prompt "Which position to install in? (0 is innermost)" - :choices (vec (reverse (map str (range (inc num-ice))))) - :effect (req (corp-install state side chosen-ice chosen-server - {:ignore-all-cost true :index (Integer/parseInt target)}) - (if (and run - (= (zone->name (first (:server run))) - chosen-server)) - (let [curr-pos (get-in @state [:run :position])] - (if (>= curr-pos (Integer/parseInt target)) - (swap! state assoc-in [:run :position] (inc curr-pos))))))}) - card nil))}) - card nil))}]}) - -(define-card "Underway Renovation" - (letfn [(adv4? [s c] (if (>= (get-counters (get-card s c) :advancement) 4) 2 1))] - {:install-state :face-up - :events {:advance {:req (req (same-card? card target)) - :msg (msg (if (pos? (count (:deck runner))) - (str "trash " - (join ", " (map :title (take (adv4? state card) (:deck runner)))) - " from the Runner's stack") - "trash from the Runner's stack but it is empty")) - :effect (effect (mill :corp :runner (adv4? state card)))}}})) - -(define-card "Unorthodox Predictions" - {:async false - :implementation "Prevention of subroutine breaking is not enforced" - :prompt "Choose an ICE type for Unorthodox Predictions" - :choices ["Barrier" "Code Gate" "Sentry"] - :msg (msg "prevent subroutines on " target " ICE from being broken until next turn.")}) - -(define-card "Utopia Fragment" - {:events {:pre-steal-cost {:req (req (pos? (get-counters target :advancement))) - :effect (req (let [counter (get-counters target :advancement)] - (steal-cost-bonus state side [:credit (* 2 counter)])))}}}) - -(define-card "Vanity Project" - ;; No special implementation - {}) - -(define-card "Veterans Program" - {:interactive (req true) - :msg "lose 2 bad publicity" - :effect (effect (lose-bad-publicity 2))}) - -(define-card "Viral Weaponization" - (let [dmg {:msg "do 1 net damage for each card in the grip" + :msg (req (let [n (count chosen)] + (str "add " n " cards from HQ to the bottom of R&D and draw " n " cards." + " The Runner randomly adds " (if (<= n (count (:hand runner))) n 0) + " cards from their Grip to the bottom of the Stack"))) + :effect (req (let [n (count chosen)] + (if (= target "Done") + (do (doseq [c (reverse chosen)] (move state :corp c :deck)) + (draw state :corp n) + ; if corp chooses more cards than runner's hand, don't shuffle runner hand back into Stack + (when (<= n (count (:hand runner))) + (doseq [r (take n (shuffle (:hand runner)))] (move state :runner r :deck))) + (clear-wait-prompt state :runner) + (effect-completed state side eid)) + (continue-ability state side (corp-choice original '() original) card nil))))}) + (corp-choice [remaining chosen original] ; Corp chooses cards until they press 'Done' + {:prompt "Choose a card to move to bottom of R&D" + :choices (conj (vec remaining) "Done") + :async true + :effect (req (let [chosen (cons target chosen)] + (if (not= target "Done") + (continue-ability + state side + (corp-choice (remove-once #(= target %) remaining) chosen original) + card nil) + (if (pos? (count (remove #(= % "Done") chosen))) + (continue-ability state side (corp-final (remove #(= % "Done") chosen) original) card nil) + (do (system-msg state side "does not add any cards from HQ to bottom of R&D") + (clear-wait-prompt state :runner) + (effect-completed state side eid))))))})] + {:async true + :effect (req (show-wait-prompt state :runner "Corp to add cards from HQ to bottom of R&D") + (let [from (get-in @state [:corp :hand])] + (if (pos? (count from)) + (continue-ability state :corp (corp-choice from '() from) card nil) + (do (system-msg state side "does not add any cards from HQ to bottom of R&D") + (effect-completed state side eid)))))}) + + "Remastered Edition" + {:effect (effect (add-counter card :agenda 1)) + :silent (req true) + :abilities [{:counter-cost [:agenda 1] + :msg (msg "place 1 advancement token on " (card-str state target)) + :choices {:req installed?} + :effect (effect (add-prop target :advance-counter 1 {:placed true}))}]} + + "Remote Data Farm" + {:silent (req true) + :msg "increase their maximum hand size by 2" + :effect (effect (gain :hand-size 2)) + :swapped {:msg "increase their maximum hand size by 2" + :effect (effect (gain :hand-size 2))} + :leave-play (effect (lose :hand-size 2))} + + "Remote Enforcement" + {:interactive (req true) + :optional {:prompt "Search R&D for a piece of ice to install protecting a remote server?" + :yes-ability + {:async true + :effect (req (when (not-empty (filter ice? (:deck corp))) + (continue-ability + state side + {:async true + :prompt "Choose a piece of ice" + :choices (req (filter ice? (:deck corp))) + :effect (req (let [chosen-ice target] + (continue-ability + state side + {:async true + :prompt (str "Select a server to install " (:title chosen-ice) " on") + :choices (filter #(not (#{"HQ" "Archives" "R&D"} %)) + (corp-install-list state chosen-ice)) + :effect (effect (shuffle! :deck) + (corp-install eid chosen-ice target + {:install-state :rezzed-no-rez-cost}))} + card nil)))} + card nil)))}}} + + "Research Grant" + {:interactive (req true) + :silent (req (empty? (filter #(= (:title %) "Research Grant") (all-installed state :corp)))) + :async true + :effect (effect (continue-ability + {:prompt "Select another installed copy of Research Grant to score" + :choices {:req #(= (:title %) "Research Grant")} + :interactive (req true) + :async true + :req (req (not (empty? (filter #(= (:title %) "Research Grant") (all-installed state :corp))))) + :effect (effect (set-prop target :advance-counter (:advancementcost target)) + (score eid (get-card state target))) + :msg "score another installed copy of Research Grant"} + card nil))} + + "Restructured Datapool" + {:abilities [{:cost [:click 1] + :trace {:base 2 + :successful {:msg "give the Runner 1 tag" + :async true + :effect (effect (gain-tags eid 1))}}}]} + + "SDS Drone Deployment" + {:steal-cost-bonus (req [:program 1]) + :effect (req (show-wait-prompt state :runner "Corp to use SDS Drone Deployment") + (if (seq (all-installed-runner-type state :program)) + (continue-ability + state side + {:prompt "Select a program to trash" + :label "Trash a program" + :msg (msg "trash " (:title target)) + :choices {:req #(and (installed? %) + (program? %))} + :effect (effect (trash target) + (clear-wait-prompt :runner)) + :end-effect (effect (clear-wait-prompt :runner))} + card nil) + (clear-wait-prompt state :runner)))} + + "Self-Destruct Chips" + {:silent (req true) + :msg "decrease the Runner's maximum hand size by 1" + :effect (effect (lose :runner :hand-size 1)) + :swapped {:msg "decrease the Runner's maximum hand size by 1" + :effect (effect (lose :runner :hand-size 1))} + :leave-play (effect (gain :runner :hand-size 1))} + + "Sensor Net Activation" + {:effect (effect (add-counter card :agenda 1)) + :silent (req true) + :abilities [{:counter-cost [:agenda 1] + :req (req (some #(and (has-subtype? % "Bioroid") (not (rezzed? %))) (all-installed state :corp))) + :prompt "Choose a bioroid to rez, ignoring all costs" + :choices {:req #(and (has-subtype? % "Bioroid") (not (rezzed? %)))} + :msg (msg "rez " (card-str state target) ", ignoring all costs") + :effect (req (let [c target] + (rez state side c {:ignore-cost :all-costs}) + (register-events + state side + {:corp-turn-ends {:effect (effect (derez c) + (unregister-events card))} + :runner-turn-ends {:effect (effect (derez c) + (unregister-events card))}} card)))}] + :events {:corp-turn-ends nil + :runner-turn-ends nil}} + + "Sentinel Defense Program" + {:events {:pre-resolve-damage {:req (req (and (= target :brain) + (pos? (last targets)))) + :msg "do 1 net damage" + :effect (effect (damage eid :net 1 {:card card}))}}} + + "Show of Force" + {:async true + :msg "do 2 meat damage" + :effect (effect (damage eid :meat 2 {:card card}))} + + "SSL Endorsement" + (let [add-credits (effect (add-counter card :credit 9)) + remove-credits {:optional {:req (req (pos? (get-counters card :credit))) + :once :per-turn + :prompt "Gain 3 [Credits] from SSL Endorsement?" + :autoresolve (get-autoresolve :auto-fire) + :yes-ability + {:effect (req (when (pos? (get-counters card :credit)) + (gain-credits state :corp 3) + (system-msg state :corp (str "uses SSL Endorsement to gain 3 [Credits]")) + (add-counter state side card :credit -3)))}}}] + {:effect add-credits + :abilities [(set-autoresolve :auto-fire "whether to take credits off SSL")] + :stolen {:effect add-credits} + :interactive (req true) + :events {:corp-turn-begins remove-credits} + :flags {:has-events-when-stolen true}}) + + "Standoff" + (letfn [(stand [side] + {:async true + :prompt "Choose one of your installed cards to trash due to Standoff" + :choices {:req #(and (installed? %) + (same-side? side (:side %)))} + :cancel-effect (req (if (= side :runner) + (do (draw state :corp) + (gain-credits state :corp 5) + (clear-wait-prompt state :corp) + (system-msg state :runner "declines to trash a card due to Standoff") + (system-msg state :corp "draws a card and gains 5 [Credits] from Standoff") + (effect-completed state :corp eid)) + (do (system-msg state :corp "declines to trash a card from Standoff") + (clear-wait-prompt state :runner) + (effect-completed state :corp eid)))) + :effect (req (wait-for (trash state side target {:unpreventable true}) + (do + (system-msg state side (str "trashes " (card-str state target) " due to Standoff")) + (clear-wait-prompt state (other-side side)) + (show-wait-prompt state side (str (side-str (other-side side)) " to trash a card for Standoff")) + (continue-ability state (other-side side) (stand (other-side side)) card nil))))})] + {:interactive (req true) + :async true + :effect (effect (show-wait-prompt (str (side-str (other-side side)) " to trash a card for Standoff")) + (continue-ability :runner (stand :runner) card nil))}) + + "Sting!" + (letfn [(count-opp-stings [state side] + (count (filter #(= (:title %) "Sting!") (get-in @state [(other-side side) :scored]))))] + {:msg (msg "deal " (inc (count-opp-stings state :corp)) " net damage") + :async true + :effect (effect (damage eid :net (inc (count-opp-stings state :corp)) {:card card})) + :stolen {:msg (msg "deal " (inc (count-opp-stings state :runner)) " net damage") + :async true + :effect (effect (damage eid :net (inc (count-opp-stings state :runner)) {:card card}))}}) + + "Successful Field Test" + (letfn [(sft [n max] {:prompt "Select a card in HQ to install with Successful Field Test" + :priority -1 + :async true + :choices {:req #(and (corp? %) + (not (operation? %)) + (in-hand? %))} + :effect (req (wait-for + (corp-install state side target nil {:ignore-all-cost true}) + (if (< n max) + (continue-ability state side (sft (inc n) max) card nil) + (effect-completed state side eid))))})] + {:async true + :msg "install cards from HQ, ignoring all costs" + :effect (req (let [max (count (filter (complement operation?) (:hand corp)))] + (continue-ability state side (sft 1 max) card nil)))}) + + "Superior Cyberwalls" + (ice-boost-agenda "Barrier") + + "TGTBT" + {:flags {:rd-reveal (req true)} + :access {:msg "give the Runner 1 tag" :async true - :effect (req (let [cnt (count (:hand runner))] - (unregister-events state side card) - (damage state side eid :net cnt {:card card})))}] - {:effect (effect (register-events - {:corp-turn-ends dmg - :runner-turn-ends dmg} - card)) - :events {:corp-turn-ends nil - :runner-turn-ends nil}})) - -(define-card "Voting Machine Initiative" - {:silent (req true) - :effect (effect (add-counter card :agenda 3)) - :events {:runner-turn-begins - {:async true - :req (req (pos? (get-counters card :agenda))) - :effect (effect (show-wait-prompt :runner "Corp to use Voting Machine Initiative") - (continue-ability - {:optional - {:player :corp - :prompt "Use Voting Machine Initiative to make the Runner lose 1 [Click]?" - :yes-ability {:msg "make the Runner lose 1 [Click]" - :effect (effect (lose :runner :click 1) - (add-counter card :agenda -1) - (clear-wait-prompt :runner))} - :no-ability {:effect (effect (clear-wait-prompt :runner))}}} - card nil))}}}) - -(define-card "Vulnerability Audit" - (let [reg-no-score-flag - (effect (register-turn-flag! card :can-score - (fn [state side other-card] - (if (same-card? other-card card) - ((constantly false) (toast state :corp "Cannot score Vulnerability Audit the turn it was installed." "warning")) - true))))] - {:derezzed-events {:pre-agenda-scored {:req (req (and (same-card? target card) - (let [agenda-cids (map #(:cid (first %)) - (filter #(agenda? (first %)) - (turn-events state :corp :corp-install)))] - (contains? (into #{} agenda-cids) (:cid card))))) - :effect reg-no-score-flag}}})) - -(define-card "Vulcan Coverup" - {:interactive (req true) - :msg "do 2 meat damage" - :effect (effect (damage eid :meat 2 {:card card})) - :stolen {:msg "force the Corp to take 1 bad publicity" - :effect (effect (gain-bad-publicity :corp 1))}}) - -(define-card "Water Monopoly" - {:events {:pre-install {:req (req (and (resource? target) - (not (has-subtype? target "Virtual")) - (not (second targets)))) ; not facedown - :effect (effect (install-cost-bonus [:credit 1]))}}}) + :effect (effect (gain-tags eid 1))}} + + "The Cleaners" + {:events {:pre-damage {:req (req (and (= target :meat) + (= side :corp))) + :msg "do 1 additional meat damage" + :effect (effect (damage-bonus :meat 1))}}} + + "The Future is Now" + {:interactive (req true) + :prompt "Choose a card to add to HQ" + :choices (req (:deck corp)) + :msg (msg "add a card from R&D to HQ and shuffle R&D") + :req (req (pos? (count (:deck corp)))) + :effect (effect (shuffle! :deck) + (move target :hand))} + + "The Future Perfect" + {:flags {:rd-reveal (req true)} + :access + {:psi {:req (req (not installed)) + :not-equal {:msg "prevent it from being stolen" + :effect (effect (register-run-flag! + card :can-steal + (fn [_ _ c] (not (same-card? c card)))) + (effect-completed eid))}}}} + + "Timely Public Release" + {:silent (req true) + :effect (effect (add-counter card :agenda 1)) + :abilities [{:counter-cost [:agenda 1] + :label "Install a piece of ice in any position, ignoring all costs" + :prompt "Select a piece of ice to install" + :show-discard true + :choices {:req #(and (ice? %) + (or (in-hand? %) + (in-discard? %)))} + :msg (msg "install " + (if (and (in-discard? target) + (or (faceup? target) + (not (facedown? target)))) + (:title target) + "ICE") + " from " (zone->name (:zone target))) + :effect (effect + (continue-ability + (let [chosen-ice target] + {:prompt "Choose a server" + :choices (req servers) + :effect (effect + (continue-ability + (let [chosen-server target + num-ice (count (get-in (:corp @state) + (conj (server->zone state target) :ices)))] + {:prompt "Which position to install in? (0 is innermost)" + :choices (vec (reverse (map str (range (inc num-ice))))) + :effect (req (corp-install state side chosen-ice chosen-server + {:ignore-all-cost true :index (Integer/parseInt target)}) + (if (and run + (= (zone->name (first (:server run))) + chosen-server)) + (let [curr-pos (get-in @state [:run :position])] + (if (>= curr-pos (Integer/parseInt target)) + (swap! state assoc-in [:run :position] (inc curr-pos))))))}) + card nil))}) + card nil))}]} + + "Underway Renovation" + (letfn [(adv4? [s c] (if (>= (get-counters (get-card s c) :advancement) 4) 2 1))] + {:install-state :face-up + :events {:advance {:req (req (same-card? card target)) + :msg (msg (if (pos? (count (:deck runner))) + (str "trash " + (join ", " (map :title (take (adv4? state card) (:deck runner)))) + " from the Runner's stack") + "trash from the Runner's stack but it is empty")) + :effect (effect (mill :corp :runner (adv4? state card)))}}}) + + "Unorthodox Predictions" + {:async false + :implementation "Prevention of subroutine breaking is not enforced" + :prompt "Choose an ICE type for Unorthodox Predictions" + :choices ["Barrier" "Code Gate" "Sentry"] + :msg (msg "prevent subroutines on " target " ICE from being broken until next turn.")} + + "Utopia Fragment" + {:events {:pre-steal-cost {:req (req (pos? (get-counters target :advancement))) + :effect (req (let [counter (get-counters target :advancement)] + (steal-cost-bonus state side [:credit (* 2 counter)])))}}} + + "Vanity Project" + ;; No special implementation + {} + + "Veterans Program" + {:interactive (req true) + :msg "lose 2 bad publicity" + :effect (effect (lose-bad-publicity 2))} + + "Viral Weaponization" + (let [dmg {:msg "do 1 net damage for each card in the grip" + :async true + :effect (req (let [cnt (count (:hand runner))] + (unregister-events state side card) + (damage state side eid :net cnt {:card card})))}] + {:effect (effect (register-events + {:corp-turn-ends dmg + :runner-turn-ends dmg} + card)) + :events {:corp-turn-ends nil + :runner-turn-ends nil}}) + + "Voting Machine Initiative" + {:silent (req true) + :effect (effect (add-counter card :agenda 3)) + :events {:runner-turn-begins + {:async true + :req (req (pos? (get-counters card :agenda))) + :effect (effect (show-wait-prompt :runner "Corp to use Voting Machine Initiative") + (continue-ability + {:optional + {:player :corp + :prompt "Use Voting Machine Initiative to make the Runner lose 1 [Click]?" + :yes-ability {:msg "make the Runner lose 1 [Click]" + :effect (effect (lose :runner :click 1) + (add-counter card :agenda -1) + (clear-wait-prompt :runner))} + :no-ability {:effect (effect (clear-wait-prompt :runner))}}} + card nil))}}} + + "Vulnerability Audit" + (let [reg-no-score-flag + (effect (register-turn-flag! card :can-score + (fn [state side other-card] + (if (same-card? other-card card) + ((constantly false) (toast state :corp "Cannot score Vulnerability Audit the turn it was installed." "warning")) + true))))] + {:derezzed-events {:pre-agenda-scored {:req (req (and (same-card? target card) + (let [agenda-cids (map #(:cid (first %)) + (filter #(agenda? (first %)) + (turn-events state :corp :corp-install)))] + (contains? (into #{} agenda-cids) (:cid card))))) + :effect reg-no-score-flag}}}) + + "Vulcan Coverup" + {:interactive (req true) + :msg "do 2 meat damage" + :effect (effect (damage eid :meat 2 {:card card})) + :stolen {:msg "force the Corp to take 1 bad publicity" + :effect (effect (gain-bad-publicity :corp 1))}} + + "Water Monopoly" + {:events {:pre-install {:req (req (and (resource? target) + (not (has-subtype? target "Virtual")) + (not (second targets)))) ; not facedown + :effect (effect (install-cost-bonus [:credit 1]))}}}}) diff --git a/src/clj/game/cards/assets.clj b/src/clj/game/cards/assets.clj index 323043d5e6..5743b53ae5 100644 --- a/src/clj/game/cards/assets.clj +++ b/src/clj/game/cards/assets.clj @@ -1,7 +1,7 @@ (ns game.cards.assets (:require [game.core :refer :all] [game.core.eid :refer [effect-completed]] - [game.core.card-defs :refer [card-def define-card]] + [game.core.card-defs :refer [card-def]] [game.utils :refer :all] [game.macros :refer [effect req msg wait-for continue-ability]] [clojure.string :refer [split-lines split join lower-case includes? starts-with?]] @@ -65,2132 +65,2133 @@ (effect-completed state side eid))))) ;; Card definitions -(define-card "Adonis Campaign" - (campaign 12 3)) - -(define-card "Advanced Assembly Lines" - {:effect (effect (gain-credits 3)) - :msg (msg "gain 3 [Credits]") - :abilities [{:label "[Trash]: Install a non-agenda card from HQ" - :async true - :prompt "Select a non-agenda card to install from HQ" - :req (req (not (:run @state))) - :choices {:req #(and (not (operation? %)) - (not (agenda? %)) - (in-hand? %) - (corp? %))} - :msg (msg (corp-install-msg target)) - :effect (req (wait-for (trash state side card {:cause :ability-cost}) - (corp-install state side eid target nil nil)))}]}) - -(define-card "Aggressive Secretary" - (advance-ambush 2 {:req (req (pos? (get-counters (get-card state card) :advancement))) +(def card-definitions + {"Adonis Campaign" + (campaign 12 3) + + "Advanced Assembly Lines" + {:effect (effect (gain-credits 3)) + :msg (msg "gain 3 [Credits]") + :abilities [{:label "[Trash]: Install a non-agenda card from HQ" + :async true + :prompt "Select a non-agenda card to install from HQ" + :req (req (not (:run @state))) + :choices {:req #(and (not (operation? %)) + (not (agenda? %)) + (in-hand? %) + (corp? %))} + :msg (msg (corp-install-msg target)) + :effect (req (wait-for (trash state side card {:cause :ability-cost}) + (corp-install state side eid target nil nil)))}]} + + "Aggressive Secretary" + (advance-ambush 2 {:req (req (pos? (get-counters (get-card state card) :advancement))) + :async true + :effect (req (let [agg (get-counters (get-card state card) :advancement) + ab {:prompt (msg "Choose " (quantify agg "program") " to trash") + :async true + :cost [:credit 2] + :choices {:max agg + :req #(and (installed? %) + (program? %))} + :effect (effect (trash-cards eid targets nil)) + :msg (msg "trash " (join ", " (map :title targets)))}] + (continue-ability state side ab card nil)))} + ;; This is needed because we're embedding the cost in the continued + ;; ability (so it prints in the log), and thus can't use the 2 arg + ;; version of installed-access-trigger + "Pay 2 [Credits] to use Aggressive Secretary ability?") + + "Alexa Belsky" + {:abilities [{:label "[Trash]: Shuffle all cards in HQ into R&D" + :effect (effect (trash card {:cause :ability-cost}) + (show-wait-prompt :corp "Runner to decide whether or not to prevent Alexa Belsky") + (resolve-ability + {:prompt "Prevent Alexa Belsky from shuffling back in 1 card for every 2 [Credits] spent. How many credits?" + :choices :credit + :player :runner + :priority 2 + :msg (msg "shuffle " + (quantify (- (count (:hand corp)) (quot target 2)) "card") + " in HQ into R&D") + :effect (req (if (pos? (quot target 2)) + (let [prevented (quot target 2) + unprevented (- (count (:hand corp)) prevented)] + (doseq [c (take unprevented (shuffle (:hand corp)))] + (move state :corp c :deck)) + (when (pos? unprevented) + (shuffle! state :corp :deck)) + (system-msg state :runner + (str "pays " target " [Credits] to prevent " + (quantify prevented "random card") + " in HQ from being shuffled into R&D"))) + (shuffle-into-deck state :corp :hand))) + :end-effect (effect (clear-wait-prompt :corp))} + card nil))}]} + + "Alix T4LB07" + {:events {:corp-install {:effect (effect (add-counter card :power 1))}} + :abilities [{:label "Gain 2 [Credits] for each counter on Alix T4LB07" + :cost [:click 1] + :msg (msg "gain " (* 2 (get-counters card :power)) " [Credits]") + :effect (effect (trash card {:cause :ability-cost}) + (gain-credits (* 2 (get-counters card :power))))}]} + + "Allele Repression" + {:implementation "Card swapping is manual" + :advanceable :always + :abilities [{:label "Swap 1 card in HQ and Archives for each advancement token" + :effect (effect (trash card {:cause :ability-cost})) + :msg (msg "swap " (get-counters card :advancement) " cards in HQ and Archives")}]} + + "Amani Senai" + (letfn [(senai-ability [agenda] + {:interactive (req true) + :optional {:prompt "Trace with Amani Senai?" + :player :corp + :autoresolve (get-autoresolve :auto-fire) + :yes-ability {:trace {:base (effect (advancement-cost agenda)) + :successful + {:choices {:req #(and (installed? %) + (runner? %))} + :label "add an installed card to the Grip" + :msg (msg "add " (:title target) " to the Runner's Grip") + :effect (effect (move :runner target :hand true))}}}}})] + {:events {:agenda-scored {:interactive (req true) + :effect (effect (continue-ability (senai-ability target) card nil))} + :agenda-stolen {:interactive (req true) + :effect (effect (continue-ability (senai-ability target) card nil))}} + :abilities [(set-autoresolve :auto-fire "whether to fire Amani Senai")]}) + + "Anson Rose" + (let [ability {:label "Place 1 advancement token on Anson Rose (start of turn)" + :once :per-turn + :effect (effect (system-msg (str "places 1 advancement counter on Anson Rose")) + (add-prop card :advance-counter 1 {:placed true}))}] + {:derezzed-events {:runner-turn-ends corp-rez-toast} + :flags {:corp-phase-12 (req true)} + :events {:corp-turn-begins ability + :rez {:req (req (and (ice? target) + (pos? (get-counters card :advancement)))) :async true - :effect (req (let [agg (get-counters (get-card state card) :advancement) - ab {:prompt (msg "Choose " (quantify agg "program") " to trash") - :async true - :cost [:credit 2] - :choices {:max agg - :req #(and (installed? %) - (program? %))} - :effect (effect (trash-cards eid targets nil)) - :msg (msg "trash " (join ", " (map :title targets)))}] - (continue-ability state side ab card nil)))} - ;; This is needed because we're embedding the cost in the continued - ;; ability (so it prints in the log), and thus can't use the 2 arg - ;; version of installed-access-trigger - "Pay 2 [Credits] to use Aggressive Secretary ability?")) - -(define-card "Alexa Belsky" - {:abilities [{:label "[Trash]: Shuffle all cards in HQ into R&D" - :effect (effect (trash card {:cause :ability-cost}) - (show-wait-prompt :corp "Runner to decide whether or not to prevent Alexa Belsky") - (resolve-ability - {:prompt "Prevent Alexa Belsky from shuffling back in 1 card for every 2 [Credits] spent. How many credits?" - :choices :credit - :player :runner - :priority 2 - :msg (msg "shuffle " - (quantify (- (count (:hand corp)) (quot target 2)) "card") - " in HQ into R&D") - :effect (req (if (pos? (quot target 2)) - (let [prevented (quot target 2) - unprevented (- (count (:hand corp)) prevented)] - (doseq [c (take unprevented (shuffle (:hand corp)))] - (move state :corp c :deck)) - (when (pos? unprevented) - (shuffle! state :corp :deck)) - (system-msg state :runner - (str "pays " target " [Credits] to prevent " - (quantify prevented "random card") - " in HQ from being shuffled into R&D"))) - (shuffle-into-deck state :corp :hand))) - :end-effect (effect (clear-wait-prompt :corp))} - card nil))}]}) - -(define-card "Alix T4LB07" - {:events {:corp-install {:effect (effect (add-counter card :power 1))}} - :abilities [{:label "Gain 2 [Credits] for each counter on Alix T4LB07" - :cost [:click 1] - :msg (msg "gain " (* 2 (get-counters card :power)) " [Credits]") - :effect (effect (trash card {:cause :ability-cost}) - (gain-credits (* 2 (get-counters card :power))))}]}) - -(define-card "Allele Repression" - {:implementation "Card swapping is manual" - :advanceable :always - :abilities [{:label "Swap 1 card in HQ and Archives for each advancement token" - :effect (effect (trash card {:cause :ability-cost})) - :msg (msg "swap " (get-counters card :advancement) " cards in HQ and Archives")}]}) - -(define-card "Amani Senai" - (letfn [(senai-ability [agenda] - {:interactive (req true) - :optional {:prompt "Trace with Amani Senai?" - :player :corp - :autoresolve (get-autoresolve :auto-fire) - :yes-ability {:trace {:base (effect (advancement-cost agenda)) - :successful - {:choices {:req #(and (installed? %) - (runner? %))} - :label "add an installed card to the Grip" - :msg (msg "add " (:title target) " to the Runner's Grip") - :effect (effect (move :runner target :hand true))}}}}})] - {:events {:agenda-scored {:interactive (req true) - :effect (effect (continue-ability (senai-ability target) card nil))} - :agenda-stolen {:interactive (req true) - :effect (effect (continue-ability (senai-ability target) card nil))}} - :abilities [(set-autoresolve :auto-fire "whether to fire Amani Senai")]})) - -(define-card "Anson Rose" - (let [ability {:label "Place 1 advancement token on Anson Rose (start of turn)" + :effect (req (let [ice (get-card state target) + icename (:title ice)] + (show-wait-prompt state :runner "Corp to use Anson Rose") + (continue-ability + state side + {:optional + {:prompt (msg "Move advancement tokens from Anson Rose to " icename "?") + :yes-ability + {:prompt "Choose how many advancement tokens to remove from Anson Rose" + :choices {:number (req (get-counters card :advancement))} + :effect (effect (add-prop :corp ice :advance-counter target {:placed true}) + (add-prop :corp card :advance-counter (- target) {:placed true}) + (system-msg (str "uses Anson Rose to move " target + " advancement tokens to " (card-str state ice))))} + :end-effect (effect (clear-wait-prompt :runner))}} + card nil)))}} + :abilities [ability]}) + + "API-S Keeper Isobel" + (letfn [(counters-available? [state] (some #(pos? (get-counters % :advancement)) (all-installed state :corp)))] + {:flags {:corp-phase-12 (req (counters-available? state))} + :abilities [{:req (req (and (:corp-phase-12 @state) + (counters-available? state))) + :once :per-turn + :label "Remove an advancement token (start of turn)" + :prompt "Select a card to remove an advancement token from" + :choices {:req #(and (pos? (get-counters % :advancement)) + (installed? %))} + :effect (req (let [cnt (get-counters target :advancement)] + (set-prop state side target :advance-counter (dec cnt)) + (gain-credits state :corp 3) + (system-msg state :corp (str "uses API-S Keeper Isobel to remove an advancement token from " + (card-str state target) " and gains 3 [Credits]"))))}]}) + + "Aryabhata Tech" + {:events {:successful-trace {:msg "gain 1 [Credit] and force the Runner to lose 1 [Credit]" + :effect (effect (gain-credits 1) + (lose-credits :runner 1))}}} + + "Bio-Ethics Association" + (let [ability {:req (req unprotected) + :async true + :label "Do 1 net damage (start of turn)" + :once :per-turn + :msg "do 1 net damage" + :effect (effect (damage eid :net 1 {:card card}))}] + {:derezzed-events {:runner-turn-ends corp-rez-toast} + :events {:corp-turn-begins ability} + :abilities [ability]}) + + "Bioroid Work Crew" + {:implementation "Timing restriction of ability use not enforced" + :abilities [{:label "[Trash]: Install 1 card, paying all costs" + :req (req (= (:active-player @state) :corp)) + :prompt "Select a card in HQ to install" + :choices {:req #(and (not (operation? %)) + (in-hand? %) + (corp? %))} + :effect (effect (trash card {:cause :ability-cost}) + (corp-install target nil)) + :msg (msg (corp-install-msg target))}]} + + "Blacklist" + {:effect (effect (lock-zone (:cid card) :runner :discard)) + :leave-play (effect (release-zone (:cid card) :runner :discard))} + + "Brain-Taping Warehouse" + {:events {:pre-rez + {:req (req (and (ice? target) + (has-subtype? target "Bioroid"))) + :effect (effect (rez-cost-bonus (- (:click runner))))}}} + + "Breached Dome" + {:flags {:rd-reveal (req true)} + :access {:async true + :effect (req (let [c (first (get-in @state [:runner :deck]))] + (system-msg state :corp (str "uses Breached Dome to do one meat damage and to trash " (:title c) + " from the top of the Runner's Stack")) + (mill state :corp :runner 1) + (damage state side eid :meat 1 {:card card})))}} + + "Broadcast Square" + {:events {:pre-bad-publicity {:async true + :trace {:base 3 + :successful {:msg "prevents all bad publicity" + :effect (effect (bad-publicity-prevent Integer/MAX_VALUE))}}}}} + + "Calvin B4L3Y" + {:abilities [{:cost [:click 1] + :msg "draw 2 cards" :once :per-turn - :effect (effect (system-msg (str "places 1 advancement counter on Anson Rose")) - (add-prop card :advance-counter 1 {:placed true}))}] - {:derezzed-events {:runner-turn-ends corp-rez-toast} - :flags {:corp-phase-12 (req true)} - :events {:corp-turn-begins ability - :rez {:req (req (and (ice? target) - (pos? (get-counters card :advancement)))) - :async true - :effect (req (let [ice (get-card state target) - icename (:title ice)] - (show-wait-prompt state :runner "Corp to use Anson Rose") - (continue-ability - state side + :effect (effect (draw 2))}] + :trash-effect {:req (req (= :servers (first (:previous-zone card)))) + :async true + :effect (effect (show-wait-prompt :runner "Corp to use Calvin B4L3Y") + (continue-ability :corp {:optional - {:prompt (msg "Move advancement tokens from Anson Rose to " icename "?") - :yes-ability - {:prompt "Choose how many advancement tokens to remove from Anson Rose" - :choices {:number (req (get-counters card :advancement))} - :effect (effect (add-prop :corp ice :advance-counter target {:placed true}) - (add-prop :corp card :advance-counter (- target) {:placed true}) - (system-msg (str "uses Anson Rose to move " target - " advancement tokens to " (card-str state ice))))} + {:prompt "Draw 2 cards?" + :priority 1 + :player :corp + :yes-ability {:msg "draw 2 cards" + :effect (effect (draw :eid 2 nil))} :end-effect (effect (clear-wait-prompt :runner))}} - card nil)))}} - :abilities [ability]})) + card nil))}} -(define-card "API-S Keeper Isobel" - (letfn [(counters-available? [state] (some #(pos? (get-counters % :advancement)) (all-installed state :corp)))] - {:flags {:corp-phase-12 (req (counters-available? state))} - :abilities [{:req (req (and (:corp-phase-12 @state) - (counters-available? state))) + "C.I. Fund" + {:derezzed-events {:runner-turn-ends corp-rez-toast} + :flags {:corp-phase-12 (req (pos? (:credit corp)))} + :abilities [{:label "Move up to 3 [Credit] from credit pool to C.I. Fund" + :prompt "Choose how many [Credit] to move" + :once :per-turn + :choices {:number (req (min (:credit corp) 3))} + :effect (effect (lose-credits target) + (add-counter card :credit target)) + :msg (msg "move " target " [Credit] to C.I. Fund")} + {:label "Take all credits from C.I. Fund" + :cost [:credit 2] + :msg (msg "trash it and gain " (get-counters card :credit) " [Credits]") + :effect (effect (trash card {:cause :ability-cost}) + (gain-credits (get-counters card :credit)))}] + :events {:corp-turn-begins {:req (req (>= (get-counters card :credit) 6)) + :effect (effect (add-counter card :credit 2) + (system-msg (str "adds 2 [Credits] to C.I. Fund")))}}} + + "Capital Investors" + {:abilities [{:cost [:click 1] + :msg "gain 2 [Credits]" + :effect (effect (gain-credits 2))}]} + + "Cerebral Overwriter" + (advance-ambush 3 {:async true + :req (req (pos? (get-counters (get-card state card) :advancement))) + :msg (msg "do " (get-counters (get-card state card) :advancement) " brain damage") + :effect (effect (damage eid :brain (get-counters (get-card state card) :advancement) {:card card}))}) + + "Chairman Hiro" + {:effect (effect (lose :runner :hand-size 2)) + :leave-play (effect (gain :runner :hand-size 2)) + :trash-effect {:when-inactive true + :req (req (:access @state)) + :msg "add it to the Runner's score area as an agenda worth 2 agenda points" + :async true + :effect (req (as-agenda state :runner eid card 2))}} + + "Chief Slee" + {:abilities [{:label "Add 1 power counter" + :effect (effect (add-counter card :power 1) + (system-msg (str "adds 1 power counter to Chief Slee")))} + {:counter-cost [:power 5] + :cost [:click 1] + :async true + :msg "do 5 meat damage" + :effect (effect (damage eid :meat 5 {:card card}))}]} + + "City Surveillance" + {:derezzed-events {:corp-turn-ends corp-rez-toast} + :flags {:runner-phase-12 (req (pos? (:credit runner)))} + :events {:runner-turn-begins + {:player :runner + :prompt "Pay 1 [Credits] or take 1 tag" + :choices (req (concat (when (pos? (:credit runner)) + ["Pay 1 [Credits]"]) + ["Take 1 tag"])) + :msg "make the Runner pay 1 [Credits] or take 1 tag" + :async true + :effect (req (case target + "Pay 1 [Credits]" + (do (system-msg state :runner "pays 1 [Credits]") + (pay state :runner card :credit 1) + (effect-completed state side eid)) + (do (system-msg state :runner "takes 1 tag") + (gain-tags state :corp eid 1))))}}} + + "Clone Suffrage Movement" + {:derezzed-events {:runner-turn-ends corp-rez-toast} + :flags {:corp-phase-12 (req (and (some operation? (:discard corp)) + unprotected))} + :abilities [{:label "Add 1 operation from Archives to HQ" + :effect (effect (show-wait-prompt :runner "Corp to use Clone Suffrage Movement") + (continue-ability + {:prompt "Select an operation in Archives to add to HQ" + :once :per-turn + :show-discard true + :choices {:req #(and (operation? %) + (in-discard? %))} + :msg (msg "add " + (if (:seen target) + (:title target) + "a facedown card") + " to HQ") + :effect (effect (move target :hand)) + :end-effect (effect (clear-wait-prompt :runner))} + card nil))}]} + + "Clyde Van Rite" + (let [ability {:req (req (or (pos? (:credit runner)) + (pos? (count (:deck runner))))) + :player :runner :once :per-turn - :label "Remove an advancement token (start of turn)" - :prompt "Select a card to remove an advancement token from" - :choices {:req #(and (pos? (get-counters % :advancement)) - (installed? %))} - :effect (req (let [cnt (get-counters target :advancement)] - (set-prop state side target :advance-counter (dec cnt)) - (gain-credits state :corp 3) - (system-msg state :corp (str "uses API-S Keeper Isobel to remove an advancement token from " - (card-str state target) " and gains 3 [Credits]"))))}]})) - -(define-card "Aryabhata Tech" - {:events {:successful-trace {:msg "gain 1 [Credit] and force the Runner to lose 1 [Credit]" - :effect (effect (gain-credits 1) - (lose-credits :runner 1))}}}) - -(define-card "Bio-Ethics Association" - (let [ability {:req (req unprotected) + :prompt "Pay 1 [Credits] or trash the top card of the Stack" + :choices (req (concat (when (pos? (:credit runner)) + ["Pay 1 [Credits]"]) + (when (pos? (count (:deck runner))) + ["Trash top card"]))) + :msg "make the Runner pay 1 [Credits] or trash the top card of the Stack" + :effect (req (case target + "Pay 1 [Credits]" + (do (system-msg state side "pays 1 [Credits]") + (pay state side card :credit 1)) + "Trash top card" + (do (system-msg state side "trashes the top card of the Stack") + (mill state :runner))))}] + {:derezzed-events {:runner-turn-ends corp-rez-toast} + :flags {:corp-phase-12 (req true)} + :events {:corp-turn-begins ability} + :abilities [ability]}) + + "Commercial Bankers Group" + (let [ability {:req (req unprotected) + :label "Gain 3 [Credits] (start of turn)" + :once :per-turn + :msg "gain 3 [Credits]" + :effect (effect (gain-credits 3))}] + {:derezzed-events {:runner-turn-ends corp-rez-toast} + :events {:corp-turn-begins ability} + :abilities [ability]}) + + "Constellation Protocol" + {:derezzed-events {:runner-turn-ends corp-rez-toast} + :flags {:corp-phase-12 + (req (let [a-token (->> (all-installed state :corp) + (filter ice?) + (filter #(pos? (get-counters % :advancement))) + (remove empty?) + first + :title)] + (as-> (all-installed state :corp) it + (filter ice? it) + (filter can-be-advanced? it) + (remove empty? it) + (map :title it) + (split-with (partial not= a-token) it) + (concat (first it) (-> it rest first rest)) + (count it) + (pos? it))))} + :abilities [{:label "Move an advancement counter between ICE" + :once :per-turn + :effect (req (show-wait-prompt state :runner "Corp to use Constellation Protocol") + (continue-ability + state side + {:choices {:req #(and (ice? %) + (get-counters % :advancement))} + :effect (req (let [from-ice target] + (continue-ability + state side + {:prompt "Move to where?" + :choices {:req #(and (ice? %) + (not (same-card? from-ice %)) + (can-be-advanced? %))} + :effect (effect (add-prop :corp target :advance-counter 1) + (add-prop :corp from-ice :advance-counter -1) + (system-msg + (str "uses Constellation Protocol to move an advancement token from " + (card-str state from-ice) + " to " + (card-str state target))) + (clear-wait-prompt :runner))} + card nil)))} + card nil))}]} + + "Contract Killer" + {:advanceable :always + :abilities [{:label "Trash a connection" + :async true + :cost [:click 1] + :req (req (>= (get-counters card :advancement) 2)) + :choices {:req #(has-subtype? % "Connection")} + :msg (msg "trash " (:title target)) + :effect (effect (trash card {:cause :ability-cost}) + (trash eid target nil))} + {:label "Do 2 meat damage" :async true - :label "Do 1 net damage (start of turn)" + :cost [:click 1] + :req (req (>= (get-counters card :advancement) 2)) + :msg "do 2 meat damage" + :effect (effect (trash card {:cause :ability-cost}) + (damage eid :meat 2 {:card card}))}]} + + "Corporate Town" + {:derezzed-events {:runner-turn-ends corp-rez-toast} + :additional-cost [:forfeit] + :flags {:corp-phase-12 (req (and (rezzed? card) + (->> (all-active-installed state :runner) + (filter resource?) + count + pos?)))} + :abilities [{:label "Trash a resource" :once :per-turn - :msg "do 1 net damage" - :effect (effect (damage eid :net 1 {:card card}))}] - {:derezzed-events {:runner-turn-ends corp-rez-toast} - :events {:corp-turn-begins ability} - :abilities [ability]})) - -(define-card "Bioroid Work Crew" - {:implementation "Timing restriction of ability use not enforced" - :abilities [{:label "[Trash]: Install 1 card, paying all costs" - :req (req (= (:active-player @state) :corp)) - :prompt "Select a card in HQ to install" - :choices {:req #(and (not (operation? %)) - (in-hand? %) - (corp? %))} - :effect (effect (trash card {:cause :ability-cost}) - (corp-install target nil)) - :msg (msg (corp-install-msg target))}]}) - -(define-card "Blacklist" - {:effect (effect (lock-zone (:cid card) :runner :discard)) - :leave-play (effect (release-zone (:cid card) :runner :discard))}) - -(define-card "Brain-Taping Warehouse" - {:events {:pre-rez - {:req (req (and (ice? target) - (has-subtype? target "Bioroid"))) - :effect (effect (rez-cost-bonus (- (:click runner))))}}}) - -(define-card "Breached Dome" - {:flags {:rd-reveal (req true)} - :access {:async true - :effect (req (let [c (first (get-in @state [:runner :deck]))] - (system-msg state :corp (str "uses Breached Dome to do one meat damage and to trash " (:title c) - " from the top of the Runner's Stack")) - (mill state :corp :runner 1) - (damage state side eid :meat 1 {:card card})))}}) - -(define-card "Broadcast Square" - {:events {:pre-bad-publicity {:async true - :trace {:base 3 - :successful {:msg "prevents all bad publicity" - :effect (effect (bad-publicity-prevent Integer/MAX_VALUE))}}}}}) - -(define-card "Calvin B4L3Y" - {:abilities [{:cost [:click 1] - :msg "draw 2 cards" - :once :per-turn - :effect (effect (draw 2))}] - :trash-effect {:req (req (= :servers (first (:previous-zone card)))) - :async true - :effect (effect (show-wait-prompt :runner "Corp to use Calvin B4L3Y") - (continue-ability :corp - {:optional - {:prompt "Draw 2 cards?" - :priority 1 - :player :corp - :yes-ability {:msg "draw 2 cards" - :effect (effect (draw :eid 2 nil))} - :end-effect (effect (clear-wait-prompt :runner))}} - card nil))}}) - -(define-card "C.I. Fund" - {:derezzed-events {:runner-turn-ends corp-rez-toast} - :flags {:corp-phase-12 (req (pos? (:credit corp)))} - :abilities [{:label "Move up to 3 [Credit] from credit pool to C.I. Fund" - :prompt "Choose how many [Credit] to move" - :once :per-turn - :choices {:number (req (min (:credit corp) 3))} - :effect (effect (lose-credits target) - (add-counter card :credit target)) - :msg (msg "move " target " [Credit] to C.I. Fund")} - {:label "Take all credits from C.I. Fund" - :cost [:credit 2] - :msg (msg "trash it and gain " (get-counters card :credit) " [Credits]") - :effect (effect (trash card {:cause :ability-cost}) - (gain-credits (get-counters card :credit)))}] - :events {:corp-turn-begins {:req (req (>= (get-counters card :credit) 6)) - :effect (effect (add-counter card :credit 2) - (system-msg (str "adds 2 [Credits] to C.I. Fund")))}}}) - -(define-card "Capital Investors" - {:abilities [{:cost [:click 1] - :msg "gain 2 [Credits]" - :effect (effect (gain-credits 2))}]}) - -(define-card "Cerebral Overwriter" - (advance-ambush 3 {:async true - :req (req (pos? (get-counters (get-card state card) :advancement))) - :msg (msg "do " (get-counters (get-card state card) :advancement) " brain damage") - :effect (effect (damage eid :brain (get-counters (get-card state card) :advancement) {:card card}))})) - -(define-card "Chairman Hiro" - {:effect (effect (lose :runner :hand-size 2)) - :leave-play (effect (gain :runner :hand-size 2)) - :trash-effect {:when-inactive true - :req (req (:access @state)) - :msg "add it to the Runner's score area as an agenda worth 2 agenda points" + :async true + :prompt "Select a resource to trash with Corporate Town" + :choices {:req resource?} + :msg (msg "trash " (:title target)) + :effect (effect (trash eid target {:unpreventable true}))}]} + + "CPC Generator" + {:events {:runner-click-credit {:req (req (first-event? state side :runner-click-credit)) + :msg "gain 1 [Credits]" + :effect (effect (gain-credits :corp 1))}}} + + "CSR Campaign" + (let [ability {:once :per-turn :async true - :effect (req (as-agenda state :runner eid card 2))}}) - -(define-card "Chief Slee" - {:abilities [{:label "Add 1 power counter" - :effect (effect (add-counter card :power 1) - (system-msg (str "adds 1 power counter to Chief Slee")))} - {:counter-cost [:power 5] - :cost [:click 1] - :async true - :msg "do 5 meat damage" - :effect (effect (damage eid :meat 5 {:card card}))}]}) - -(define-card "City Surveillance" - {:derezzed-events {:corp-turn-ends corp-rez-toast} - :flags {:runner-phase-12 (req (pos? (:credit runner)))} - :events {:runner-turn-begins - {:player :runner - :prompt "Pay 1 [Credits] or take 1 tag" - :choices (req (concat (when (pos? (:credit runner)) - ["Pay 1 [Credits]"]) - ["Take 1 tag"])) - :msg "make the Runner pay 1 [Credits] or take 1 tag" - :async true - :effect (req (case target - "Pay 1 [Credits]" - (do (system-msg state :runner "pays 1 [Credits]") - (pay state :runner card :credit 1) - (effect-completed state side eid)) - (do (system-msg state :runner "takes 1 tag") - (gain-tags state :corp eid 1))))}}}) - -(define-card "Clone Suffrage Movement" - {:derezzed-events {:runner-turn-ends corp-rez-toast} - :flags {:corp-phase-12 (req (and (some operation? (:discard corp)) - unprotected))} - :abilities [{:label "Add 1 operation from Archives to HQ" - :effect (effect (show-wait-prompt :runner "Corp to use Clone Suffrage Movement") - (continue-ability - {:prompt "Select an operation in Archives to add to HQ" - :once :per-turn - :show-discard true - :choices {:req #(and (operation? %) - (in-discard? %))} - :msg (msg "add " - (if (:seen target) - (:title target) - "a facedown card") - " to HQ") - :effect (effect (move target :hand)) - :end-effect (effect (clear-wait-prompt :runner))} - card nil))}]}) - -(define-card "Clyde Van Rite" - (let [ability {:req (req (or (pos? (:credit runner)) - (pos? (count (:deck runner))))) - :player :runner - :once :per-turn - :prompt "Pay 1 [Credits] or trash the top card of the Stack" - :choices (req (concat (when (pos? (:credit runner)) - ["Pay 1 [Credits]"]) - (when (pos? (count (:deck runner))) - ["Trash top card"]))) - :msg "make the Runner pay 1 [Credits] or trash the top card of the Stack" - :effect (req (case target - "Pay 1 [Credits]" - (do (system-msg state side "pays 1 [Credits]") - (pay state side card :credit 1)) - "Trash top card" - (do (system-msg state side "trashes the top card of the Stack") - (mill state :runner))))}] - {:derezzed-events {:runner-turn-ends corp-rez-toast} - :flags {:corp-phase-12 (req true)} - :events {:corp-turn-begins ability} - :abilities [ability]})) - -(define-card "Commercial Bankers Group" - (let [ability {:req (req unprotected) - :label "Gain 3 [Credits] (start of turn)" + :label "Draw 1 card (start of turn)" + :interactive (req true) + :effect (effect (continue-ability + {:optional + {:prompt "Use CSR Campaign to draw 1 card?" + :yes-ability {:async true + :msg "draw 1 card" + :effect (effect (draw eid 1 nil))}}} + card nil))}] + {:derezzed-events {:runner-turn-ends corp-rez-toast} + :flags {:corp-phase-12 (req true)} + :events {:corp-turn-begins ability} + :abilities [ability]}) + + "Cybernetics Court" + {:in-play [:hand-size 4]} + + "Daily Business Show" + {:events {:pre-corp-draw + {:msg "draw additional cards" + ;; The req catches draw events that happened before DBS was rezzed. + :req (req (first-event? state :corp :pre-corp-draw)) + ;; The once and once-key force a single DBS to act on behalf of all rezzed DBS's. + :once :per-turn + :once-key :daily-business-show-draw-bonus + :effect (req (let [dbs (count (filter #(and (= "Daily Business Show" (:title %)) + (rezzed? %)) + (all-installed state :corp)))] + (draw-bonus state side dbs)))} + :post-corp-draw + {:req (req (first-event? state :corp :post-corp-draw)) + :once :per-turn + :once-key :daily-business-show-put-bottom + :async true + :effect (req (let [dbs (count (filter #(and (= "Daily Business Show" (:title %)) + (rezzed? %)) + (all-installed state :corp))) + drawn (get-in @state [:corp :register :most-recent-drawn])] + (show-wait-prompt state :runner "Corp to use Daily Business Show") + (wait-for (resolve-ability + state side + {:prompt (str "Select " (quantify dbs "card") " to add to the bottom of R&D") + :msg (msg "add " (quantify dbs "card") " to the bottom of R&D") + :choices {:max dbs + :req #(some (fn [c] (same-card? c %)) drawn)} + :effect (req (doseq [c targets] + (move state side c :deck)))} + card targets) + (do (clear-wait-prompt state :runner) + (effect-completed state side eid)))))}}} + + "Daily Quest" + {:rez-req (req (= (:active-player @state) :corp)) + :events {:successful-run {:req (req this-server) + :effect (effect (gain-credits :runner 2) + (system-msg :runner (str "gains 2 [Credits] for a successful run " + "on the Daily Quest server")))} + :corp-turn-begins {:req (req (let [servers (get-in @state [:runner :register-last-turn :successful-run])] + (not (some #{(second (:zone card)) + (second (:zone (:host card)))} + servers)))) + :msg "gain 3 [Credits]" + :effect (effect (gain-credits :corp 3))}}} + + "Dedicated Response Team" + {:events {:successful-run-ends {:req (req tagged) + :msg "do 2 meat damage" + :async true + :effect (effect (damage eid :meat 2 {:card card}))}}} + + "Dedicated Server" + {:recurring 2 + :interactions {:pay-credits {:req (req (and (= :rez (:source-type eid)) + (ice? target))) + :type :recurring}}} + + "Director Haas" + {:in-play [:click-per-turn 1] + :trash-effect {:when-inactive true + :req (req (:access @state)) + :msg "add it to the Runner's score area as an agenda worth 2 agenda points" + :async true + :effect (req (as-agenda state :runner eid card 2))}} + + "Docklands Crackdown" + {:abilities [{:cost [:click 2] + :msg "add 1 power counter" + :effect (effect (add-counter card :power 1))}] + :events {:pre-install {:req (req (and (pos? (get-counters card :power)) + (not (get-in @state [:per-turn (:cid card)])))) + :effect (effect (install-cost-bonus [:credit (get-counters card :power)]))} + :runner-install {:silent (req true) + :req (req (and (pos? (get-counters card :power)) + (not (get-in @state [:per-turn (:cid card)])))) + :msg (msg "increase the install cost of " (:title target) " by " (get-counters card :power) " [Credits]") + :effect (req (swap! state assoc-in [:per-turn (:cid card)] true))}}} + + "Drudge Work" + {:effect (effect (add-counter card :power 3)) + :abilities [{:cost [:click 1] + :counter-cost [:power 1] + :async true + :choices {:req #(and (agenda? %) + (or (in-hand? %) + (in-discard? %)))} + :label "Reveal an agenda from HQ or Archives" + :msg (msg "reveal " (:title target) " from " (zone->name (:zone target)) + (let [target-agenda-points (get-agenda-points state :corp target)] + (when (pos? target-agenda-points) + (str ", gain " target-agenda-points " [Credits], "))) + " and shuffle it into R&D") + :effect (req (reveal state side target) + (gain-credits state :corp (get-agenda-points state :corp target)) + (move state :corp target :deck) + (shuffle! state :corp :deck) + (if (zero? (get-counters card :power)) + (trash state side eid card nil) + (effect-completed state side eid)))}]} + + "Early Premiere" + {:derezzed-events {:runner-turn-ends corp-rez-toast} + :flags {:corp-phase-12 (req (some #(and (can-be-advanced? %) + (in-server? %)) + (all-installed state :corp)))} + :abilities [{:cost [:credit 1] + :label "Place 1 advancement token on a card that can be advanced in a server" + :choices {:req #(and (can-be-advanced? %) + (installed? %) + (in-server? %))} ; should be *in* a server :once :per-turn - :msg "gain 3 [Credits]" - :effect (effect (gain-credits 3))}] - {:derezzed-events {:runner-turn-ends corp-rez-toast} - :events {:corp-turn-begins ability} - :abilities [ability]})) + :msg (msg "place 1 advancement token on " (card-str state target)) + :effect (effect (add-prop target :advance-counter 1 {:placed true}))}]} -(define-card "Constellation Protocol" - {:derezzed-events {:runner-turn-ends corp-rez-toast} - :flags {:corp-phase-12 - (req (let [a-token (->> (all-installed state :corp) - (filter ice?) - (filter #(pos? (get-counters % :advancement))) - (remove empty?) - first - :title)] - (as-> (all-installed state :corp) it - (filter ice? it) - (filter can-be-advanced? it) - (remove empty? it) - (map :title it) - (split-with (partial not= a-token) it) - (concat (first it) (-> it rest first rest)) - (count it) - (pos? it))))} - :abilities [{:label "Move an advancement counter between ICE" - :once :per-turn - :effect (req (show-wait-prompt state :runner "Corp to use Constellation Protocol") - (continue-ability - state side - {:choices {:req #(and (ice? %) - (get-counters % :advancement))} - :effect (req (let [from-ice target] - (continue-ability - state side - {:prompt "Move to where?" - :choices {:req #(and (ice? %) - (not (same-card? from-ice %)) - (can-be-advanced? %))} - :effect (effect (add-prop :corp target :advance-counter 1) - (add-prop :corp from-ice :advance-counter -1) - (system-msg - (str "uses Constellation Protocol to move an advancement token from " - (card-str state from-ice) - " to " - (card-str state target))) - (clear-wait-prompt :runner))} - card nil)))} - card nil))}]}) - -(define-card "Contract Killer" - {:advanceable :always - :abilities [{:label "Trash a connection" - :async true - :cost [:click 1] - :req (req (>= (get-counters card :advancement) 2)) - :choices {:req #(has-subtype? % "Connection")} - :msg (msg "trash " (:title target)) - :effect (effect (trash card {:cause :ability-cost}) - (trash eid target nil))} - {:label "Do 2 meat damage" - :async true - :cost [:click 1] - :req (req (>= (get-counters card :advancement) 2)) - :msg "do 2 meat damage" - :effect (effect (trash card {:cause :ability-cost}) - (damage eid :meat 2 {:card card}))}]}) - -(define-card "Corporate Town" - {:derezzed-events {:runner-turn-ends corp-rez-toast} - :additional-cost [:forfeit] - :flags {:corp-phase-12 (req (and (rezzed? card) - (->> (all-active-installed state :runner) - (filter resource?) - count - pos?)))} - :abilities [{:label "Trash a resource" - :once :per-turn - :async true - :prompt "Select a resource to trash with Corporate Town" - :choices {:req resource?} - :msg (msg "trash " (:title target)) - :effect (effect (trash eid target {:unpreventable true}))}]}) - -(define-card "CPC Generator" - {:events {:runner-click-credit {:req (req (first-event? state side :runner-click-credit)) - :msg "gain 1 [Credits]" - :effect (effect (gain-credits :corp 1))}}}) - -(define-card "CSR Campaign" - (let [ability {:once :per-turn + "Echo Chamber" + {:abilities [{:label "Add Echo Chamber to your score area as an agenda worth 1 agenda point" + :cost [:click 3] + :msg "add it to their score area as an agenda worth 1 agenda point" :async true - :label "Draw 1 card (start of turn)" - :interactive (req true) - :effect (effect (continue-ability - {:optional - {:prompt "Use CSR Campaign to draw 1 card?" - :yes-ability {:async true - :msg "draw 1 card" - :effect (effect (draw eid 1 nil))}}} - card nil))}] - {:derezzed-events {:runner-turn-ends corp-rez-toast} - :flags {:corp-phase-12 (req true)} - :events {:corp-turn-begins ability} - :abilities [ability]})) + :effect (req (as-agenda state :corp eid card 1))}]} -(define-card "Cybernetics Court" - {:in-play [:hand-size 4]}) - -(define-card "Daily Business Show" - {:events {:pre-corp-draw - {:msg "draw additional cards" - ;; The req catches draw events that happened before DBS was rezzed. - :req (req (first-event? state :corp :pre-corp-draw)) - ;; The once and once-key force a single DBS to act on behalf of all rezzed DBS's. - :once :per-turn - :once-key :daily-business-show-draw-bonus - :effect (req (let [dbs (count (filter #(and (= "Daily Business Show" (:title %)) - (rezzed? %)) - (all-installed state :corp)))] - (draw-bonus state side dbs)))} - :post-corp-draw - {:req (req (first-event? state :corp :post-corp-draw)) - :once :per-turn - :once-key :daily-business-show-put-bottom - :async true - :effect (req (let [dbs (count (filter #(and (= "Daily Business Show" (:title %)) - (rezzed? %)) - (all-installed state :corp))) - drawn (get-in @state [:corp :register :most-recent-drawn])] - (show-wait-prompt state :runner "Corp to use Daily Business Show") - (wait-for (resolve-ability - state side - {:prompt (str "Select " (quantify dbs "card") " to add to the bottom of R&D") - :msg (msg "add " (quantify dbs "card") " to the bottom of R&D") - :choices {:max dbs - :req #(some (fn [c] (same-card? c %)) drawn)} - :effect (req (doseq [c targets] - (move state side c :deck)))} - card targets) - (do (clear-wait-prompt state :runner) - (effect-completed state side eid)))))}}}) - -(define-card "Daily Quest" - {:rez-req (req (= (:active-player @state) :corp)) - :events {:successful-run {:req (req this-server) - :effect (effect (gain-credits :runner 2) - (system-msg :runner (str "gains 2 [Credits] for a successful run " - "on the Daily Quest server")))} - :corp-turn-begins {:req (req (let [servers (get-in @state [:runner :register-last-turn :successful-run])] - (not (some #{(second (:zone card)) - (second (:zone (:host card)))} - servers)))) - :msg "gain 3 [Credits]" - :effect (effect (gain-credits :corp 3))}}}) - -(define-card "Dedicated Response Team" - {:events {:successful-run-ends {:req (req tagged) - :msg "do 2 meat damage" + "Edge of World" + (letfn [(ice-count [state] + (count (get-in (:corp @state) [:servers (last (:server (:run @state))) :ices])))] + (installed-access-trigger 3 {:msg (msg "do " (ice-count state) " brain damage") :async true - :effect (effect (damage eid :meat 2 {:card card}))}}}) - -(define-card "Dedicated Server" - {:recurring 2 - :interactions {:pay-credits {:req (req (and (= :rez (:source-type eid)) - (ice? target))) - :type :recurring}}}) - -(define-card "Director Haas" - {:in-play [:click-per-turn 1] - :trash-effect {:when-inactive true - :req (req (:access @state)) - :msg "add it to the Runner's score area as an agenda worth 2 agenda points" - :async true - :effect (req (as-agenda state :runner eid card 2))}}) - -(define-card "Docklands Crackdown" - {:abilities [{:cost [:click 2] - :msg "add 1 power counter" - :effect (effect (add-counter card :power 1))}] - :events {:pre-install {:req (req (and (pos? (get-counters card :power)) - (not (get-in @state [:per-turn (:cid card)])))) - :effect (effect (install-cost-bonus [:credit (get-counters card :power)]))} - :runner-install {:silent (req true) - :req (req (and (pos? (get-counters card :power)) - (not (get-in @state [:per-turn (:cid card)])))) - :msg (msg "increase the install cost of " (:title target) " by " (get-counters card :power) " [Credits]") - :effect (req (swap! state assoc-in [:per-turn (:cid card)] true))}}}) - -(define-card "Drudge Work" - {:effect (effect (add-counter card :power 3)) - :abilities [{:cost [:click 1] - :counter-cost [:power 1] - :async true - :choices {:req #(and (agenda? %) - (or (in-hand? %) - (in-discard? %)))} - :label "Reveal an agenda from HQ or Archives" - :msg (msg "reveal " (:title target) " from " (zone->name (:zone target)) - (let [target-agenda-points (get-agenda-points state :corp target)] - (when (pos? target-agenda-points) - (str ", gain " target-agenda-points " [Credits], "))) - " and shuffle it into R&D") - :effect (req (reveal state side target) - (gain-credits state :corp (get-agenda-points state :corp target)) - (move state :corp target :deck) - (shuffle! state :corp :deck) - (if (zero? (get-counters card :power)) - (trash state side eid card nil) - (effect-completed state side eid)))}]}) - -(define-card "Early Premiere" - {:derezzed-events {:runner-turn-ends corp-rez-toast} - :flags {:corp-phase-12 (req (some #(and (can-be-advanced? %) - (in-server? %)) - (all-installed state :corp)))} - :abilities [{:cost [:credit 1] - :label "Place 1 advancement token on a card that can be advanced in a server" - :choices {:req #(and (can-be-advanced? %) - (installed? %) - (in-server? %))} ; should be *in* a server - :once :per-turn - :msg (msg "place 1 advancement token on " (card-str state target)) - :effect (effect (add-prop target :advance-counter 1 {:placed true}))}]}) - -(define-card "Echo Chamber" - {:abilities [{:label "Add Echo Chamber to your score area as an agenda worth 1 agenda point" - :cost [:click 3] - :msg "add it to their score area as an agenda worth 1 agenda point" - :async true - :effect (req (as-agenda state :corp eid card 1))}]}) - -(define-card "Edge of World" - (letfn [(ice-count [state] - (count (get-in (:corp @state) [:servers (last (:server (:run @state))) :ices])))] - (installed-access-trigger 3 {:msg (msg "do " (ice-count state) " brain damage") - :async true - :effect (effect (damage eid :brain (ice-count state) - {:card card}))}))) - -(define-card "Eliza's Toybox" - {:abilities [{:cost [:click 3] :choices {:req #(not (:rezzed %))} - :label "Rez a card at no cost" :msg (msg "rez " (:title target) " at no cost") - :effect (effect (rez target {:ignore-cost :all-costs}))}]}) - -(define-card "Elizabeth Mills" - {:effect (effect (lose-bad-publicity 1)) - :msg "remove 1 bad publicity" - :abilities [{:cost [:click 1] :label "Trash a location" - :msg (msg "trash " (:title target) " and take 1 bad publicity") - :choices {:req #(has-subtype? % "Location")} - :effect (effect (trash card {:cause :ability-cost}) - (trash target) - (gain-bad-publicity :corp 1))}]}) - -(define-card "Encryption Protocol" - {:events {:pre-trash {:req (req (installed? target)) - :effect (effect (trash-cost-bonus 1))}}}) - -(define-card "Estelle Moon" - {:events {:corp-install {:req (req (and (#{"Asset" "Agenda" "Upgrade"} (:type target)) - (is-remote? (second (:zone target))))) - :effect (effect (add-counter card :power 1) - (system-msg (str "places 1 power counter on Estelle Moon")))}} - :abilities [{:label "Draw 1 card and gain 2 [Credits] for each power counter" - :effect (req (let [counters (get-counters card :power) - credits (* 2 counters)] - (trash state side card {:cause :ability-cost}) - (draw state side counters) - (gain-credits state side credits) - (system-msg state side (str "uses Estelle Moon to draw " counters - " cards and gain " credits " [Credits]"))))}]}) - -(define-card "Eve Campaign" - (campaign 16 2)) - -(define-card "Executive Boot Camp" - {:derezzed-events {:runner-turn-ends corp-rez-toast} - :flags {:corp-phase-12 (req (some #(not (rezzed? %)) (all-installed state :corp)))} - ; A card rezzed by Executive Bootcamp is ineligible to receive the turn-begins event for this turn. - :suppress {:corp-turn-begins {:req (req (= (:cid target) (:ebc-rezzed (get-card state card))))}} - :events {:corp-turn-ends {:req (req (:ebc-rezzed card)) - :effect (effect (update! (dissoc card :ebc-rezzed)))}} - :abilities [{:async true - :once :per-turn - :choices {:req (complement rezzed?)} - :label "Rez a card, lowering the cost by 1 [Credits]" - :msg (msg "rez " (:title target)) - :effect (req (rez-cost-bonus state side -1) - (wait-for (rez state side target {:no-warning true}) - (update! state side (assoc card :ebc-rezzed (:cid target)))))} - {:prompt "Choose an asset to reveal and add to HQ" - :msg (msg "reveal " (:title target) " and add it to HQ") - :activatemsg "searches R&D for an asset" - :choices (req (cancellable (filter asset? - (:deck corp)) - :sorted)) - :cost [:credit 1] - :label "Search R&D for an asset" - :effect (effect (trash card {:cause :ability-cost}) - (reveal target) - (shuffle! :deck) - (move target :hand))}]}) - -(define-card "Executive Search Firm" - {:abilities [{:prompt "Choose an Executive, Sysop, or Character to add to HQ" - :msg (msg "add " (:title target) " to HQ and shuffle R&D") - :activatemsg "searches R&D for an Executive, Sysop, or Character" - :choices (req (cancellable (filter #(or (has-subtype? % "Executive") - (has-subtype? % "Sysop") - (has-subtype? % "Character")) - (:deck corp)) - :sorted)) - :cost [:click 1] - :label "Search R&D for an Executive, Sysop, or Character" - :effect (effect (move target :hand) - (shuffle! :deck))}]}) - -(define-card "Exposé" - {:advanceable :always - :abilities [{:label "Remove 1 bad publicity for each advancement token on Exposé" - :msg (msg "remove " (get-counters card :advancement) " bad publicity") - :effect (effect (trash card {:cause :ability-cost}) - (lose-bad-publicity (get-counters card :advancement)))}]}) - -(define-card "False Flag" - (letfn [(tag-count [false-flag] - (int (/ (get-counters false-flag :advancement) 2)))] - {:advanceable :always - :access {:req (req (pos? (get-counters (get-card state card) :advancement))) - :msg (msg "give the runner " (quantify (tag-count (get-card state card)) "tag")) - :async true - :effect (effect (gain-tags :corp eid (tag-count (get-card state card))))} - :abilities [{:cost [:click 1] - :advance-counter-cost 7 - :label "Add False Flag to your score area as an agenda worth 3 agenda points" - :msg "add it to their score area as an agenda worth 3 agenda points" - :async true - :effect (req (as-agenda state :corp eid card 3))}]})) - -(define-card "Franchise City" - {:events {:access {:req (req (agenda? target)) - :msg "add it to their score area as an agenda worth 1 agenda point" - :async true - :effect (req (as-agenda state :corp eid card 1))}}}) - -(define-card "Full Immersion RecStudio" - {:can-host (req (and (or (asset? target) (agenda? target)) - (> 2 (count (:hosted card))))) - :trash-cost-bonus (req (* 3 (count (:hosted card)))) - :abilities [{:label "Install an asset or agenda on Full Immersion RecStudio" - :req (req (< (count (:hosted card)) 2)) - :cost [:click 1] - :prompt "Select an asset or agenda to install" - :choices {:req #(and (or (asset? %) (agenda? %)) - (in-hand? %) - (corp? %))} - :msg "install and host an asset or agenda" - :effect (req (corp-install state side target card))} - {:label "Install a previously-installed asset or agenda on Full Immersion RecStudio (fixes only)" - :req (req (< (count (:hosted card)) 2)) - :prompt "Select an installed asset or agenda to host on Full Immersion RecStudio" - :choices {:req #(and (or (asset? %) (agenda? %)) - (installed? %) - (corp? %))} - :msg "install and host an asset or agenda" - :effect (req (host state side card target))}]}) - -(define-card "Fumiko Yamamori" - {:events {:reveal-spent-credits - {:async true - :req (req (and (some? (first targets)) - (some? (second targets)) - (not= (first targets) (second targets)))) - :msg "do 1 meat damage" - :effect (effect (damage eid :meat 1 {:card card}))}}}) - -(define-card "Gene Splicer" - {:advanceable :always - :access {:req (req (pos? (get-counters (get-card state card) :advancement))) - :msg (msg "do " (get-counters (get-card state card) :advancement) " net damage") - :async true - :effect (effect (damage eid :net (get-counters (get-card state card) :advancement) - {:card card}))} - :abilities [{:cost [:click 1] - :advance-counter-cost 3 - :label "Add Gene Splicing to your score area as an agenda worth 1 agenda point" - :msg "add it to their score area as an agenda worth 1 agenda point" - :async true - :effect (req (as-agenda state :corp eid card 1))}]}) - -(define-card "Genetics Pavilion" - {:msg "prevent the Runner from drawing more than 2 cards during their turn" - :effect (req (max-draw state :runner 2) - (when (zero? (remaining-draws state :runner)) - (prevent-draw state :runner))) - :events {:runner-turn-begins {:effect (effect (max-draw :runner 2))}} - :leave-play (req (swap! state update-in [:runner :register] dissoc :max-draw :cannot-draw))}) - -(define-card "Ghost Branch" - (advance-ambush 0 {:async true - :req (req (pos? (get-counters (get-card state card) :advancement))) - :msg (msg "give the Runner " (quantify (get-counters (get-card state card) :advancement) "tag")) - :effect (effect (gain-tags :corp eid (get-counters (get-card state card) :advancement)))})) - -(define-card "GRNDL Refinery" - {:advanceable :always - :abilities [{:label "Gain 4 [Credits] for each advancement token on GRNDL Refinery" - :cost [:click 1] - :msg (msg "gain " (* 4 (get-counters card :advancement)) " [Credits]") - :effect (effect (trash card {:cause :ability-cost}) - (gain-credits (* 4 (get-counters card :advancement))))}]}) - -(define-card "Haas Arcology AI" - {:advanceable :while-unrezzed - :abilities [{:label "Gain [Click][Click]" - :once :per-turn - :msg "gain [Click][Click]" - :cost [:click 1] - :advance-counter-cost 1 - :effect (effect (gain :click 2))}]}) - -(define-card "Honeyfarm" - {:flags {:rd-reveal (req true)} - :access {:msg "force the Runner to lose 1 [Credits]" - :effect (effect (lose-credits :runner 1))}}) - -(define-card "Hostile Infrastructure" - {:events {:runner-trash {:async true - :req (req (some corp? targets)) - :msg (msg (str "do " (count (filter corp? targets)) - " net damage")) - :effect (req (letfn [(do-damage [t] - (if-not (empty? t) - (wait-for (damage state side :net 1 {:card card}) - (do-damage (rest t))) - (effect-completed state side eid)))] - (do-damage (filter corp? targets))))}} - :abilities [{:msg "do 1 net damage" - :async true - :effect (effect (damage eid :net 1 {:card card}))}]}) - -(define-card "Hyoubu Research Facility" - {:events {:reveal-spent-credits - {:req (req (some? (first targets))) - :once :per-turn - :msg (msg "gain " target " [Credits]") - :effect (effect (gain-credits :corp target))}}}) - -(define-card "Ibrahim Salem" - (let [trash-ability (fn [card-type] - {:req (req (seq (filter #(is-type? % card-type) (:hand runner)))) - :prompt (str "Choose a " card-type " to trash") - :choices (req (filter #(is-type? % card-type) (:hand runner))) - :effect (effect (trash target)) - :msg (msg " trash " (:title target) " from the Runner's Grip")}) - choose-ability {:label "Trash 1 card in the Runner's Grip of a named type" - :once :per-turn - :req (req (seq (:hand runner))) - :prompt "Choose a card type" - :choices ["Event" "Hardware" "Program" "Resource"] - :msg (msg "reveal " (join ", " (map :title (:hand runner))) " and trash a " target) - :effect (effect (resolve-ability (trash-ability target) card nil))}] - {:additional-cost [:forfeit] - :flags {:corp-phase-12 (constantly true)} - :derezzed-events {:runner-turn-ends corp-rez-toast} - :abilities [choose-ability]})) - -(define-card "Illegal Arms Factory" - (let [ability {:msg "gain 1 [Credits] and draw 1 card" - :label "Gain 1 [Credits] and draw 1 card (start of turn)" + :effect (effect (damage eid :brain (ice-count state) + {:card card}))})) + + "Eliza's Toybox" + {:abilities [{:cost [:click 3] :choices {:req #(not (:rezzed %))} + :label "Rez a card at no cost" :msg (msg "rez " (:title target) " at no cost") + :effect (effect (rez target {:ignore-cost :all-costs}))}]} + + "Elizabeth Mills" + {:effect (effect (lose-bad-publicity 1)) + :msg "remove 1 bad publicity" + :abilities [{:cost [:click 1] :label "Trash a location" + :msg (msg "trash " (:title target) " and take 1 bad publicity") + :choices {:req #(has-subtype? % "Location")} + :effect (effect (trash card {:cause :ability-cost}) + (trash target) + (gain-bad-publicity :corp 1))}]} + + "Encryption Protocol" + {:events {:pre-trash {:req (req (installed? target)) + :effect (effect (trash-cost-bonus 1))}}} + + "Estelle Moon" + {:events {:corp-install {:req (req (and (#{"Asset" "Agenda" "Upgrade"} (:type target)) + (is-remote? (second (:zone target))))) + :effect (effect (add-counter card :power 1) + (system-msg (str "places 1 power counter on Estelle Moon")))}} + :abilities [{:label "Draw 1 card and gain 2 [Credits] for each power counter" + :effect (req (let [counters (get-counters card :power) + credits (* 2 counters)] + (trash state side card {:cause :ability-cost}) + (draw state side counters) + (gain-credits state side credits) + (system-msg state side (str "uses Estelle Moon to draw " counters + " cards and gain " credits " [Credits]"))))}]} + + "Eve Campaign" + (campaign 16 2) + + "Executive Boot Camp" + {:derezzed-events {:runner-turn-ends corp-rez-toast} + :flags {:corp-phase-12 (req (some #(not (rezzed? %)) (all-installed state :corp)))} + ; A card rezzed by Executive Bootcamp is ineligible to receive the turn-begins event for this turn. + :suppress {:corp-turn-begins {:req (req (= (:cid target) (:ebc-rezzed (get-card state card))))}} + :events {:corp-turn-ends {:req (req (:ebc-rezzed card)) + :effect (effect (update! (dissoc card :ebc-rezzed)))}} + :abilities [{:async true :once :per-turn + :choices {:req (complement rezzed?)} + :label "Rez a card, lowering the cost by 1 [Credits]" + :msg (msg "rez " (:title target)) + :effect (req (rez-cost-bonus state side -1) + (wait-for (rez state side target {:no-warning true}) + (update! state side (assoc card :ebc-rezzed (:cid target)))))} + {:prompt "Choose an asset to reveal and add to HQ" + :msg (msg "reveal " (:title target) " and add it to HQ") + :activatemsg "searches R&D for an asset" + :choices (req (cancellable (filter asset? + (:deck corp)) + :sorted)) + :cost [:credit 1] + :label "Search R&D for an asset" + :effect (effect (trash card {:cause :ability-cost}) + (reveal target) + (shuffle! :deck) + (move target :hand))}]} + + "Executive Search Firm" + {:abilities [{:prompt "Choose an Executive, Sysop, or Character to add to HQ" + :msg (msg "add " (:title target) " to HQ and shuffle R&D") + :activatemsg "searches R&D for an Executive, Sysop, or Character" + :choices (req (cancellable (filter #(or (has-subtype? % "Executive") + (has-subtype? % "Sysop") + (has-subtype? % "Character")) + (:deck corp)) + :sorted)) + :cost [:click 1] + :label "Search R&D for an Executive, Sysop, or Character" + :effect (effect (move target :hand) + (shuffle! :deck))}]} + + "Exposé" + {:advanceable :always + :abilities [{:label "Remove 1 bad publicity for each advancement token on Exposé" + :msg (msg "remove " (get-counters card :advancement) " bad publicity") + :effect (effect (trash card {:cause :ability-cost}) + (lose-bad-publicity (get-counters card :advancement)))}]} + + "False Flag" + (letfn [(tag-count [false-flag] + (int (/ (get-counters false-flag :advancement) 2)))] + {:advanceable :always + :access {:req (req (pos? (get-counters (get-card state card) :advancement))) + :msg (msg "give the runner " (quantify (tag-count (get-card state card)) "tag")) + :async true + :effect (effect (gain-tags :corp eid (tag-count (get-card state card))))} + :abilities [{:cost [:click 1] + :advance-counter-cost 7 + :label "Add False Flag to your score area as an agenda worth 3 agenda points" + :msg "add it to their score area as an agenda worth 3 agenda points" + :async true + :effect (req (as-agenda state :corp eid card 3))}]}) + + "Franchise City" + {:events {:access {:req (req (agenda? target)) + :msg "add it to their score area as an agenda worth 1 agenda point" + :async true + :effect (req (as-agenda state :corp eid card 1))}}} + + "Full Immersion RecStudio" + {:can-host (req (and (or (asset? target) (agenda? target)) + (> 2 (count (:hosted card))))) + :trash-cost-bonus (req (* 3 (count (:hosted card)))) + :abilities [{:label "Install an asset or agenda on Full Immersion RecStudio" + :req (req (< (count (:hosted card)) 2)) + :cost [:click 1] + :prompt "Select an asset or agenda to install" + :choices {:req #(and (or (asset? %) (agenda? %)) + (in-hand? %) + (corp? %))} + :msg "install and host an asset or agenda" + :effect (req (corp-install state side target card))} + {:label "Install a previously-installed asset or agenda on Full Immersion RecStudio (fixes only)" + :req (req (< (count (:hosted card)) 2)) + :prompt "Select an installed asset or agenda to host on Full Immersion RecStudio" + :choices {:req #(and (or (asset? %) (agenda? %)) + (installed? %) + (corp? %))} + :msg "install and host an asset or agenda" + :effect (req (host state side card target))}]} + + "Fumiko Yamamori" + {:events {:reveal-spent-credits + {:async true + :req (req (and (some? (first targets)) + (some? (second targets)) + (not= (first targets) (second targets)))) + :msg "do 1 meat damage" + :effect (effect (damage eid :meat 1 {:card card}))}}} + + "Gene Splicer" + {:advanceable :always + :access {:req (req (pos? (get-counters (get-card state card) :advancement))) + :msg (msg "do " (get-counters (get-card state card) :advancement) " net damage") + :async true + :effect (effect (damage eid :net (get-counters (get-card state card) :advancement) + {:card card}))} + :abilities [{:cost [:click 1] + :advance-counter-cost 3 + :label "Add Gene Splicing to your score area as an agenda worth 1 agenda point" + :msg "add it to their score area as an agenda worth 1 agenda point" :async true - :req (req (:corp-phase-12 @state)) - :effect (effect (gain-credits 1) - (draw eid 1 nil))}] - {:derezzed-events {:runner-turn-ends corp-rez-toast} - :events {:corp-turn-begins ability} - :abilities [ability] - :trash-effect {:req (req (and (= :servers (first (:previous-zone card))) - (= side :runner))) - :effect (effect (gain-bad-publicity :corp 1) - (system-msg :corp (str "takes 1 bad publicity from Illegal Arms Factory")))}})) - -(define-card "Indian Union Stock Exchange" - (let [iuse {:req (req (not= (:faction target) (:faction (:identity corp)))) - :msg "gain 1 [Credits]" - :effect (effect (gain-credits 1))}] - {:events {:play-operation iuse - :rez iuse}})) - -(define-card "Isabel McGuire" - {:abilities [{:label "Add an installed card to HQ" - :cost [:click 1] - :choices {:req installed?} - :msg (msg "move " (card-str state target) " to HQ") - :effect (effect (move target :hand))}]}) - -(define-card "IT Department" - {:abilities [{:counter-cost [:power 1] - :label "Add strength to a rezzed ICE" - :choices {:req #(and (ice? %) (:rezzed %))} - :req (req (pos? (get-counters card :power))) - :msg (msg "add strength to a rezzed ICE") - :effect (req (update! state side (update-in card [:it-targets (keyword (str (:cid target)))] - (fnil inc 0))) - (update-ice-strength state side target))} - {:cost [:click 1] - :msg "add 1 counter" - :effect (effect (add-counter card :power 1))}] - :events (let [it {:req (req (:it-targets card)) - :effect (req (update! state side (dissoc card :it-targets)) - (update-all-ice state side))}] - {:pre-ice-strength {:req (req (get-in card [:it-targets (keyword (str (:cid target)))])) - :effect (effect (ice-strength-bonus - (* (get-in card [:it-targets (keyword (str (:cid target)))]) - (inc (get-counters card :power))) target))} - :runner-turn-ends it - :corp-turn-ends it})}) - -(define-card "Jackson Howard" - {:abilities [{:cost [:click 1] - :msg "draw 2 cards" - :effect (effect (draw 2))} - {:label "Shuffle up to 3 cards from Archives into R&D" - :activatemsg "removes Jackson Howard from the game" - :effect (effect (rfg-and-shuffle-rd-effect card 3))}]}) - -(define-card "Jeeves Model Bioroids" - (let [jeeves (effect (gain :click 1)) - ability {:label "Gain [Click]" - :msg "gain [Click]" - :once :per-turn - :effect jeeves} - cleanup (effect (update! (dissoc card :seen-this-turn)))] - {:abilities [ability] - :leave-play cleanup - :trash-effect {:effect cleanup} - :events {:corp-spent-click - {:effect (req (when-not target - (print-stack-trace (Exception. (str "WHY JEEVES WHY: " targets)))) - (update! state side (update-in card [:seen-this-turn (or target :this-is-a-hack)] - (fnil + 0) (second targets))) - (when (>= (get-in (get-card state card) [:seen-this-turn (or target :this-is-a-hack)]) 3) - (resolve-ability state side ability card nil)))} - :corp-turn-ends {:effect cleanup}}})) - -(define-card "Kala Ghoda Real TV" - {:derezzed-events {:runner-turn-ends corp-rez-toast} - :flags {:corp-phase-12 (req true)} - :abilities [{:msg "look at the top card of the Runner's Stack" - :effect (effect (prompt! card (str "The top card of the Runner's Stack is " - (:title (first (:deck runner)))) ["OK"] {}))} - {:label "[Trash]: Trash the top card of the Runner's Stack" - :msg (msg "trash " (:title (first (:deck runner))) " from the Runner's Stack") - :effect (effect (trash card {:cause :ability-cost}) - (mill :runner))}]}) - -(define-card "Kuwinda K4H1U3" - {:derezzed-events {:runner-turn-ends corp-rez-toast} - :flags {:corp-phase-12 (req true)} - :abilities [{:label "Trace X - do 1 brain damage (start of turn)" - :trace {:base (req (get-counters card :power)) - :successful {:async true - :msg "do 1 brain damage" - :effect (effect (damage :runner eid :brain 1 {:card card}) - (trash card))} - :unsuccessful {:effect (effect (add-counter card :power 1) - (system-msg "adds 1 power counter to Kuwinda K4H1U3"))}}}]}) - -(define-card "Lady Liberty" - {:abilities [{:cost [:click 3] - :label "Add agenda from HQ to score area" - :req (req (let [counters (get-counters (get-card state card) :power)] - (some #(and (agenda? %) - (= counters (:agendapoints %))) - (:hand corp)))) - :async true - :effect (req (show-wait-prompt state :runner "Corp to select an agenda for Lady Liberty") - (continue-ability - state side - {:prompt "Select an Agenda in HQ to move to score area" - :choices {:req #(and (agenda? %) - (= (:agendapoints %) (get-counters (get-card state card) :power)) - (in-hand? %))} - :msg (msg "add " (:title target) " to score area") - :async true - :effect (req (wait-for (as-agenda state :corp target (:agendapoints target) - {:register-events true}) - (clear-wait-prompt state :runner) - (effect-completed state side eid)))} - card nil))}] - :events {:corp-turn-begins {:effect (effect (add-counter card :power 1))}}}) - -(define-card "Lakshmi Smartfabrics" - {:events {:rez {:effect (effect (add-counter card :power 1))}} - :abilities [{:req (req (seq (filter #(and (agenda? %) - (>= (get-counters card :power) - (:agendapoints %))) - (:hand corp)))) - :label "X power counters: Reveal an agenda worth X points from HQ" - :effect (req (let [c (get-counters card :power)] - (resolve-ability - state side - {:prompt "Select an agenda in HQ to reveal" - :choices {:req #(and (agenda? %) - (>= c (:agendapoints %)))} - :msg (msg "reveal " (:title target) " from HQ") - :effect (req (reveal state side target) - (let [title (:title target) - pts (:agendapoints target)] - (register-turn-flag! - state side - card :can-steal - (fn [state side card] - (if (= (:title card) title) - ((constantly false) - (toast state :runner "Cannot steal due to Lakshmi Smartfabrics." "warning")) - true))) - (add-counter state side card :power (- pts))))} - card nil)))}]}) - -(define-card "Launch Campaign" - (campaign 6 2)) - -(define-card "Levy University" - {:abilities [{:prompt "Choose an ICE" - :msg (msg "adds " (:title target) " to HQ") - :choices (req (cancellable (filter ice? (:deck corp)) :sorted)) - :label "Search R&D for a piece of ICE" - :cost [:click 1 :credit 1] - :effect (effect (move target :hand) - (shuffle! :deck))}]}) - -(define-card "Lily Lockwell" - {:async true - :effect (effect (draw eid 3 nil)) - :msg (msg "draw 3 cards") - :abilities [{:label "Remove a tag to search R&D for an operation" - :prompt "Choose an operation to put on top of R&D" - :cost [:click 1] - :choices (req (cancellable (filter operation? (:deck corp)) :sorted)) - :req (req (pos? (get-in @state [:runner :tag :base]))) - :effect (req (lose-tags state :corp 1) - (let [c (move state :corp target :play-area)] - (shuffle! state :corp :deck) - (move state :corp c :deck {:front true}) - (system-msg state side (str "uses Lily Lockwell to put " (:title c) " on top of R&D")))) - :cancel-effect (effect (lose-tags :corp 1) - (shuffle! :corp :deck) - (system-msg (str "uses Lily Lockwell, but did not find an Operation in R&D")))}]}) - -(define-card "Long-Term Investment" - {:derezzed-events {:runner-turn-ends corp-rez-toast} - :abilities [{:label "Move any number of [Credits] to your credit pool" - :req (req (>= (get-counters card :credit) 8)) - :cost [:click 1] - :prompt "How many [Credits]?" - :choices {:counter :credit} - :msg (msg "gain " target " [Credits]") - :effect (effect (gain-credits target))}] - :events {:corp-turn-begins {:effect (effect (add-counter card :credit 2) - (system-msg (str "adds 2 [Credit] to Long-Term Investment")))}}}) - -(define-card "Malia Z0L0K4" - (let [re-enable-target (req (when-let [malia-target (:malia-target card)] - (when (:disabled (get-card state malia-target)) - (system-msg state side (str "uses " (:title card) " to unblank " - (card-str state malia-target))) - (enable-card state :runner (get-card state malia-target)) - (when-let [reactivate-effect (:reactivate (card-def malia-target))] - (resolve-ability state :runner reactivate-effect (get-card state malia-target) nil)))))] - {:effect (effect (update! (assoc card :malia-target target)) - (disable-card :runner target)) - :msg (msg (str "blank the text box of " (card-str state target))) - :choices {:req #(and (runner? %) - (installed? %) - (resource? %) - (not (has-subtype? % "Virtual")))} - :leave-play re-enable-target - :move-zone re-enable-target})) - -(define-card "Marilyn Campaign" - (let [ability {:msg "gain 2 [Credits]" - :counter-cost [:credit 2] + :effect (req (as-agenda state :corp eid card 1))}]} + + "Genetics Pavilion" + {:msg "prevent the Runner from drawing more than 2 cards during their turn" + :effect (req (max-draw state :runner 2) + (when (zero? (remaining-draws state :runner)) + (prevent-draw state :runner))) + :events {:runner-turn-begins {:effect (effect (max-draw :runner 2))}} + :leave-play (req (swap! state update-in [:runner :register] dissoc :max-draw :cannot-draw))} + + "Ghost Branch" + (advance-ambush 0 {:async true + :req (req (pos? (get-counters (get-card state card) :advancement))) + :msg (msg "give the Runner " (quantify (get-counters (get-card state card) :advancement) "tag")) + :effect (effect (gain-tags :corp eid (get-counters (get-card state card) :advancement)))}) + + "GRNDL Refinery" + {:advanceable :always + :abilities [{:label "Gain 4 [Credits] for each advancement token on GRNDL Refinery" + :cost [:click 1] + :msg (msg "gain " (* 4 (get-counters card :advancement)) " [Credits]") + :effect (effect (trash card {:cause :ability-cost}) + (gain-credits (* 4 (get-counters card :advancement))))}]} + + "Haas Arcology AI" + {:advanceable :while-unrezzed + :abilities [{:label "Gain [Click][Click]" :once :per-turn - :interactive (req true) - :req (req (:corp-phase-12 @state)) - :label (str "Gain 2 [Credits] (start of turn)") + :msg "gain [Click][Click]" + :cost [:click 1] + :advance-counter-cost 1 + :effect (effect (gain :click 2))}]} + + "Honeyfarm" + {:flags {:rd-reveal (req true)} + :access {:msg "force the Runner to lose 1 [Credits]" + :effect (effect (lose-credits :runner 1))}} + + "Hostile Infrastructure" + {:events {:runner-trash {:async true + :req (req (some corp? targets)) + :msg (msg (str "do " (count (filter corp? targets)) + " net damage")) + :effect (req (letfn [(do-damage [t] + (if-not (empty? t) + (wait-for (damage state side :net 1 {:card card}) + (do-damage (rest t))) + (effect-completed state side eid)))] + (do-damage (filter corp? targets))))}} + :abilities [{:msg "do 1 net damage" :async true - :effect (req (gain-credits state :corp 2) - (if (zero? (get-counters (get-card state card) :credit)) - (trash state :corp eid card {:unpreventable true}) - (effect-completed state :corp eid)))}] - {:effect (effect (add-counter card :credit 8)) - :derezzed-events {:runner-turn-ends corp-rez-toast} - :events {:corp-turn-begins ability} - :abilities [(set-autoresolve :auto-reshuffle "Marilyn reshuffle")] - :trash-effect {:req (req (= :servers (first (:previous-zone card)))) - :async true - :effect (effect (show-wait-prompt :runner "Corp to use Marilyn Campaign") - (continue-ability - :corp - {:optional - {:prompt "Shuffle Marilyn Campaign into R&D?" - :autoresolve (get-autoresolve :auto-reshuffle) - :priority 1 - :player :corp - :yes-ability {:msg "shuffle it back into R&D" - :effect (effect (move :corp card :deck) - (shuffle! :corp :deck) - (effect-completed eid))} - :end-effect (effect (clear-wait-prompt :runner))}} - card nil))}})) - -(define-card "Mark Yale" - {:events {:agenda-counter-spent {:msg "gain 1 [Credits]" - :effect (effect (gain-credits 1))}} - :abilities [{:label "Trash to gain 2 [Credits]" - :msg "gain 2 [Credits]" - :effect (effect (trash card {:cause :ability-cost}) - (gain-credits 2))} - {:label "Spend an agenda counter to gain 2 [Credits]" - :effect (effect (continue-ability - {:prompt "Select an agenda with a counter" + :effect (effect (damage eid :net 1 {:card card}))}]} + + "Hyoubu Research Facility" + {:events {:reveal-spent-credits + {:req (req (some? (first targets))) + :once :per-turn + :msg (msg "gain " target " [Credits]") + :effect (effect (gain-credits :corp target))}}} + + "Ibrahim Salem" + (let [trash-ability (fn [card-type] + {:req (req (seq (filter #(is-type? % card-type) (:hand runner)))) + :prompt (str "Choose a " card-type " to trash") + :choices (req (filter #(is-type? % card-type) (:hand runner))) + :effect (effect (trash target)) + :msg (msg " trash " (:title target) " from the Runner's Grip")}) + choose-ability {:label "Trash 1 card in the Runner's Grip of a named type" + :once :per-turn + :req (req (seq (:hand runner))) + :prompt "Choose a card type" + :choices ["Event" "Hardware" "Program" "Resource"] + :msg (msg "reveal " (join ", " (map :title (:hand runner))) " and trash a " target) + :effect (effect (resolve-ability (trash-ability target) card nil))}] + {:additional-cost [:forfeit] + :flags {:corp-phase-12 (constantly true)} + :derezzed-events {:runner-turn-ends corp-rez-toast} + :abilities [choose-ability]}) + + "Illegal Arms Factory" + (let [ability {:msg "gain 1 [Credits] and draw 1 card" + :label "Gain 1 [Credits] and draw 1 card (start of turn)" + :once :per-turn + :async true + :req (req (:corp-phase-12 @state)) + :effect (effect (gain-credits 1) + (draw eid 1 nil))}] + {:derezzed-events {:runner-turn-ends corp-rez-toast} + :events {:corp-turn-begins ability} + :abilities [ability] + :trash-effect {:req (req (and (= :servers (first (:previous-zone card))) + (= side :runner))) + :effect (effect (gain-bad-publicity :corp 1) + (system-msg :corp (str "takes 1 bad publicity from Illegal Arms Factory")))}}) + + "Indian Union Stock Exchange" + (let [iuse {:req (req (not= (:faction target) (:faction (:identity corp)))) + :msg "gain 1 [Credits]" + :effect (effect (gain-credits 1))}] + {:events {:play-operation iuse + :rez iuse}}) + + "Isabel McGuire" + {:abilities [{:label "Add an installed card to HQ" + :cost [:click 1] + :choices {:req installed?} + :msg (msg "move " (card-str state target) " to HQ") + :effect (effect (move target :hand))}]} + + "IT Department" + {:abilities [{:counter-cost [:power 1] + :label "Add strength to a rezzed ICE" + :choices {:req #(and (ice? %) (:rezzed %))} + :req (req (pos? (get-counters card :power))) + :msg (msg "add strength to a rezzed ICE") + :effect (req (update! state side (update-in card [:it-targets (keyword (str (:cid target)))] + (fnil inc 0))) + (update-ice-strength state side target))} + {:cost [:click 1] + :msg "add 1 counter" + :effect (effect (add-counter card :power 1))}] + :events (let [it {:req (req (:it-targets card)) + :effect (req (update! state side (dissoc card :it-targets)) + (update-all-ice state side))}] + {:pre-ice-strength {:req (req (get-in card [:it-targets (keyword (str (:cid target)))])) + :effect (effect (ice-strength-bonus + (* (get-in card [:it-targets (keyword (str (:cid target)))]) + (inc (get-counters card :power))) target))} + :runner-turn-ends it + :corp-turn-ends it})} + + "Jackson Howard" + {:abilities [{:cost [:click 1] + :msg "draw 2 cards" + :effect (effect (draw 2))} + {:label "Shuffle up to 3 cards from Archives into R&D" + :activatemsg "removes Jackson Howard from the game" + :effect (effect (rfg-and-shuffle-rd-effect card 3))}]} + + "Jeeves Model Bioroids" + (let [jeeves (effect (gain :click 1)) + ability {:label "Gain [Click]" + :msg "gain [Click]" + :once :per-turn + :effect jeeves} + cleanup (effect (update! (dissoc card :seen-this-turn)))] + {:abilities [ability] + :leave-play cleanup + :trash-effect {:effect cleanup} + :events {:corp-spent-click + {:effect (req (when-not target + (print-stack-trace (Exception. (str "WHY JEEVES WHY: " targets)))) + (update! state side (update-in card [:seen-this-turn (or target :this-is-a-hack)] + (fnil + 0) (second targets))) + (when (>= (get-in (get-card state card) [:seen-this-turn (or target :this-is-a-hack)]) 3) + (resolve-ability state side ability card nil)))} + :corp-turn-ends {:effect cleanup}}}) + + "Kala Ghoda Real TV" + {:derezzed-events {:runner-turn-ends corp-rez-toast} + :flags {:corp-phase-12 (req true)} + :abilities [{:msg "look at the top card of the Runner's Stack" + :effect (effect (prompt! card (str "The top card of the Runner's Stack is " + (:title (first (:deck runner)))) ["OK"] {}))} + {:label "[Trash]: Trash the top card of the Runner's Stack" + :msg (msg "trash " (:title (first (:deck runner))) " from the Runner's Stack") + :effect (effect (trash card {:cause :ability-cost}) + (mill :runner))}]} + + "Kuwinda K4H1U3" + {:derezzed-events {:runner-turn-ends corp-rez-toast} + :flags {:corp-phase-12 (req true)} + :abilities [{:label "Trace X - do 1 brain damage (start of turn)" + :trace {:base (req (get-counters card :power)) + :successful {:async true + :msg "do 1 brain damage" + :effect (effect (damage :runner eid :brain 1 {:card card}) + (trash card))} + :unsuccessful {:effect (effect (add-counter card :power 1) + (system-msg "adds 1 power counter to Kuwinda K4H1U3"))}}}]} + + "Lady Liberty" + {:abilities [{:cost [:click 3] + :label "Add agenda from HQ to score area" + :req (req (let [counters (get-counters (get-card state card) :power)] + (some #(and (agenda? %) + (= counters (:agendapoints %))) + (:hand corp)))) + :async true + :effect (req (show-wait-prompt state :runner "Corp to select an agenda for Lady Liberty") + (continue-ability + state side + {:prompt "Select an Agenda in HQ to move to score area" + :choices {:req #(and (agenda? %) + (= (:agendapoints %) (get-counters (get-card state card) :power)) + (in-hand? %))} + :msg (msg "add " (:title target) " to score area") + :async true + :effect (req (wait-for (as-agenda state :corp target (:agendapoints target) + {:register-events true}) + (clear-wait-prompt state :runner) + (effect-completed state side eid)))} + card nil))}] + :events {:corp-turn-begins {:effect (effect (add-counter card :power 1))}}} + + "Lakshmi Smartfabrics" + {:events {:rez {:effect (effect (add-counter card :power 1))}} + :abilities [{:req (req (seq (filter #(and (agenda? %) + (>= (get-counters card :power) + (:agendapoints %))) + (:hand corp)))) + :label "X power counters: Reveal an agenda worth X points from HQ" + :effect (req (let [c (get-counters card :power)] + (resolve-ability + state side + {:prompt "Select an agenda in HQ to reveal" :choices {:req #(and (agenda? %) - (pos? (get-counters % :agenda)))} - :msg (msg "spend an agenda token on " (:title target) " and gain 2 [Credits]") - :effect (effect (add-counter target :agenda -1) - (gain-credits 2) - (trigger-event :agenda-counter-spent card))} - card nil))}]}) - -(define-card "Marked Accounts" - (let [ability {:msg "take 1 [Credits]" - :label "Take 1 [Credits] (start of turn)" + (>= c (:agendapoints %)))} + :msg (msg "reveal " (:title target) " from HQ") + :effect (req (reveal state side target) + (let [title (:title target) + pts (:agendapoints target)] + (register-turn-flag! + state side + card :can-steal + (fn [state side card] + (if (= (:title card) title) + ((constantly false) + (toast state :runner "Cannot steal due to Lakshmi Smartfabrics." "warning")) + true))) + (add-counter state side card :power (- pts))))} + card nil)))}]} + + "Launch Campaign" + (campaign 6 2) + + "Levy University" + {:abilities [{:prompt "Choose an ICE" + :msg (msg "adds " (:title target) " to HQ") + :choices (req (cancellable (filter ice? (:deck corp)) :sorted)) + :label "Search R&D for a piece of ICE" + :cost [:click 1 :credit 1] + :effect (effect (move target :hand) + (shuffle! :deck))}]} + + "Lily Lockwell" + {:async true + :effect (effect (draw eid 3 nil)) + :msg (msg "draw 3 cards") + :abilities [{:label "Remove a tag to search R&D for an operation" + :prompt "Choose an operation to put on top of R&D" + :cost [:click 1] + :choices (req (cancellable (filter operation? (:deck corp)) :sorted)) + :req (req (pos? (get-in @state [:runner :tag :base]))) + :effect (req (lose-tags state :corp 1) + (let [c (move state :corp target :play-area)] + (shuffle! state :corp :deck) + (move state :corp c :deck {:front true}) + (system-msg state side (str "uses Lily Lockwell to put " (:title c) " on top of R&D")))) + :cancel-effect (effect (lose-tags :corp 1) + (shuffle! :corp :deck) + (system-msg (str "uses Lily Lockwell, but did not find an Operation in R&D")))}]} + + "Long-Term Investment" + {:derezzed-events {:runner-turn-ends corp-rez-toast} + :abilities [{:label "Move any number of [Credits] to your credit pool" + :req (req (>= (get-counters card :credit) 8)) + :cost [:click 1] + :prompt "How many [Credits]?" + :choices {:counter :credit} + :msg (msg "gain " target " [Credits]") + :effect (effect (gain-credits target))}] + :events {:corp-turn-begins {:effect (effect (add-counter card :credit 2) + (system-msg (str "adds 2 [Credit] to Long-Term Investment")))}}} + + "Malia Z0L0K4" + (let [re-enable-target (req (when-let [malia-target (:malia-target card)] + (when (:disabled (get-card state malia-target)) + (system-msg state side (str "uses " (:title card) " to unblank " + (card-str state malia-target))) + (enable-card state :runner (get-card state malia-target)) + (when-let [reactivate-effect (:reactivate (card-def malia-target))] + (resolve-ability state :runner reactivate-effect (get-card state malia-target) nil)))))] + {:effect (effect (update! (assoc card :malia-target target)) + (disable-card :runner target)) + :msg (msg (str "blank the text box of " (card-str state target))) + :choices {:req #(and (runner? %) + (installed? %) + (resource? %) + (not (has-subtype? % "Virtual")))} + :leave-play re-enable-target + :move-zone re-enable-target}) + + "Marilyn Campaign" + (let [ability {:msg "gain 2 [Credits]" + :counter-cost [:credit 2] + :once :per-turn + :interactive (req true) + :req (req (:corp-phase-12 @state)) + :label (str "Gain 2 [Credits] (start of turn)") + :async true + :effect (req (gain-credits state :corp 2) + (if (zero? (get-counters (get-card state card) :credit)) + (trash state :corp eid card {:unpreventable true}) + (effect-completed state :corp eid)))}] + {:effect (effect (add-counter card :credit 8)) + :derezzed-events {:runner-turn-ends corp-rez-toast} + :events {:corp-turn-begins ability} + :abilities [(set-autoresolve :auto-reshuffle "Marilyn reshuffle")] + :trash-effect {:req (req (= :servers (first (:previous-zone card)))) + :async true + :effect (effect (show-wait-prompt :runner "Corp to use Marilyn Campaign") + (continue-ability + :corp + {:optional + {:prompt "Shuffle Marilyn Campaign into R&D?" + :autoresolve (get-autoresolve :auto-reshuffle) + :priority 1 + :player :corp + :yes-ability {:msg "shuffle it back into R&D" + :effect (effect (move :corp card :deck) + (shuffle! :corp :deck) + (effect-completed eid))} + :end-effect (effect (clear-wait-prompt :runner))}} + card nil))}}) + + "Mark Yale" + {:events {:agenda-counter-spent {:msg "gain 1 [Credits]" + :effect (effect (gain-credits 1))}} + :abilities [{:label "Trash to gain 2 [Credits]" + :msg "gain 2 [Credits]" + :effect (effect (trash card {:cause :ability-cost}) + (gain-credits 2))} + {:label "Spend an agenda counter to gain 2 [Credits]" + :effect (effect (continue-ability + {:prompt "Select an agenda with a counter" + :choices {:req #(and (agenda? %) + (pos? (get-counters % :agenda)))} + :msg (msg "spend an agenda token on " (:title target) " and gain 2 [Credits]") + :effect (effect (add-counter target :agenda -1) + (gain-credits 2) + (trigger-event :agenda-counter-spent card))} + card nil))}]} + + "Marked Accounts" + (let [ability {:msg "take 1 [Credits]" + :label "Take 1 [Credits] (start of turn)" + :once :per-turn + :counter-cost [:credit 1] + :effect (effect (gain-credits 1))}] + {:abilities [ability + {:cost [:click 1] + :msg "store 3 [Credits]" + :effect (effect (add-counter card :credit 3))}] + :events {:corp-turn-begins ability}}) + + "MCA Austerity Policy" + {:abilities [{:cost [:click 1] :once :per-turn - :counter-cost [:credit 1] - :effect (effect (gain-credits 1))}] - {:abilities [ability - {:cost [:click 1] - :msg "store 3 [Credits]" - :effect (effect (add-counter card :credit 3))}] - :events {:corp-turn-begins ability}})) - -(define-card "MCA Austerity Policy" - {:abilities [{:cost [:click 1] - :once :per-turn - :msg "to force the Runner to lose a [Click] next turn and place a power counter on itself" - :effect (req (swap! state update-in [:runner :extra-click-temp] (fnil dec 0)) - (add-counter state side card :power 1))} - {:cost [:click 1] - :counter-cost [:power 3] - :msg "gain 4 [Click] and trash itself" - :effect (effect (trash card {:cause :ability-cost - :unpreventable true}) - (gain :click 4))}]}) - -(define-card "Melange Mining Corp." - {:abilities [{:cost [:click 3] - :effect (effect (gain-credits 7)) - :msg "gain 7 [Credits]"}]}) - -(define-card "Mental Health Clinic" - (let [ability {:msg "gain 1 [Credits]" - :label "Gain 1 [Credits] (start of turn)" + :msg "to force the Runner to lose a [Click] next turn and place a power counter on itself" + :effect (req (swap! state update-in [:runner :extra-click-temp] (fnil dec 0)) + (add-counter state side card :power 1))} + {:cost [:click 1] + :counter-cost [:power 3] + :msg "gain 4 [Click] and trash itself" + :effect (effect (trash card {:cause :ability-cost + :unpreventable true}) + (gain :click 4))}]} + + "Melange Mining Corp." + {:abilities [{:cost [:click 3] + :effect (effect (gain-credits 7)) + :msg "gain 7 [Credits]"}]} + + "Mental Health Clinic" + (let [ability {:msg "gain 1 [Credits]" + :label "Gain 1 [Credits] (start of turn)" + :once :per-turn + :effect (effect (gain-credits 1))}] + {:effect (effect (gain :runner :hand-size 1)) + :leave-play (effect (lose :runner :hand-size 1)) + :derezzed-events {:runner-turn-ends corp-rez-toast} + :events {:corp-turn-begins ability} + :abilities [ability]}) + + "Mr. Stone" + {:events {:runner-gain-tag {:async true + :msg "do 1 meat damage" + :effect (effect (damage :corp eid :meat 1 {:card card}))}}} + + "Mumba Temple" + {:recurring 2 + :interactions {:pay-credits {:req (req (= :rez (:source-type eid))) + :type :recurring}}} + + "Mumbad City Hall" + {:abilities [{:label "Search R&D for an Alliance card" + :cost [:click 1] + :prompt "Choose an Alliance card to play or install" + :choices (req (cancellable (filter #(and (has-subtype? % "Alliance") + (if (operation? %) + (<= (:cost %) (:credit corp)) + true)) + (:deck corp)) + :sorted)) + :msg (msg "reveal " (:title target) + " from R&D and " + (if (= (:type target) "Operation") "play" "install") + " it") + :effect (req (reveal state side target) + (shuffle! state side :deck) + (if (= (:type target) "Operation") + (play-instant state side target) + (corp-install state side target nil nil)))}]} + + "Mumbad Construction Co." + {:derezzed-events {:runner-turn-ends corp-rez-toast} + :events {:corp-turn-begins {:effect (effect (add-prop card :advance-counter 1 {:placed true}))}} + :abilities [{:cost [:credit 2] + :req (req (and (pos? (get-counters card :advancement)) + (not-empty (all-active-installed state :corp)))) + :label "Move an advancement token to a faceup card" + :prompt "Select a faceup card" + :choices {:req rezzed?} + :msg (msg "move an advancement token to " (card-str state target)) + :effect (effect (add-prop card :advance-counter -1 {:placed true}) + (add-prop target :advance-counter 1 {:placed true}))}]} + + "Museum of History" + {:derezzed-events {:runner-turn-ends corp-rez-toast} + :flags {:corp-phase-12 (req (pos? (count (get-in @state [:corp :discard]))))} + :abilities [{:label "Shuffle cards in Archives into R&D" + :prompt (msg (let [mus (count (filter #(and (= "10019" (:code %)) + (rezzed? %)) + (all-installed state :corp)))] + (str "Select " + (if (> mus 1) "a card " (str mus " cards ")) + "in Archives to shuffle into R&D"))) + :choices {:req #(and (corp? %) + (in-discard? %)) + :max (req (count (filter #(and (= "10019" (:code %)) + (rezzed? %)) + (all-installed state :corp))))} + :show-discard true + :priority 1 :once :per-turn - :effect (effect (gain-credits 1))}] - {:effect (effect (gain :runner :hand-size 1)) - :leave-play (effect (lose :runner :hand-size 1)) - :derezzed-events {:runner-turn-ends corp-rez-toast} - :events {:corp-turn-begins ability} - :abilities [ability]})) - -(define-card "Mr. Stone" - {:events {:runner-gain-tag {:async true - :msg "do 1 meat damage" - :effect (effect (damage :corp eid :meat 1 {:card card}))}}}) - -(define-card "Mumba Temple" - {:recurring 2 - :interactions {:pay-credits {:req (req (= :rez (:source-type eid))) - :type :recurring}}}) - -(define-card "Mumbad City Hall" - {:abilities [{:label "Search R&D for an Alliance card" - :cost [:click 1] - :prompt "Choose an Alliance card to play or install" - :choices (req (cancellable (filter #(and (has-subtype? % "Alliance") - (if (operation? %) - (<= (:cost %) (:credit corp)) - true)) - (:deck corp)) - :sorted)) - :msg (msg "reveal " (:title target) - " from R&D and " - (if (= (:type target) "Operation") "play" "install") - " it") - :effect (req (reveal state side target) - (shuffle! state side :deck) - (if (= (:type target) "Operation") - (play-instant state side target) - (corp-install state side target nil nil)))}]}) - -(define-card "Mumbad Construction Co." - {:derezzed-events {:runner-turn-ends corp-rez-toast} - :events {:corp-turn-begins {:effect (effect (add-prop card :advance-counter 1 {:placed true}))}} - :abilities [{:cost [:credit 2] - :req (req (and (pos? (get-counters card :advancement)) - (not-empty (all-active-installed state :corp)))) - :label "Move an advancement token to a faceup card" - :prompt "Select a faceup card" - :choices {:req rezzed?} - :msg (msg "move an advancement token to " (card-str state target)) - :effect (effect (add-prop card :advance-counter -1 {:placed true}) - (add-prop target :advance-counter 1 {:placed true}))}]}) - -(define-card "Museum of History" - {:derezzed-events {:runner-turn-ends corp-rez-toast} - :flags {:corp-phase-12 (req (pos? (count (get-in @state [:corp :discard]))))} - :abilities [{:label "Shuffle cards in Archives into R&D" - :prompt (msg (let [mus (count (filter #(and (= "10019" (:code %)) - (rezzed? %)) - (all-installed state :corp)))] - (str "Select " - (if (> mus 1) "a card " (str mus " cards ")) - "in Archives to shuffle into R&D"))) - :choices {:req #(and (corp? %) - (in-discard? %)) - :max (req (count (filter #(and (= "10019" (:code %)) - (rezzed? %)) - (all-installed state :corp))))} - :show-discard true - :priority 1 - :once :per-turn - :once-key :museum-of-history - :msg (msg "shuffle " - (let [seen (filter :seen targets) - n (count (filter #(not (:seen %)) targets))] - (str (join ", " (map :title seen)) - (when (pos? n) - (str (when-not (empty? seen) " and ") - (quantify n "card"))))) - " into R&D") - :effect (req (doseq [c targets] - (move state side c :deck)) - (shuffle! state side :deck))}] - :implementation "Errata from FAQ 3.1: should be unique"}) - -(define-card "Nanoetching Matrix" - {:events {:runner-trash {:req (req (same-card? card target)) - :effect (effect (show-wait-prompt :runner "Corp to use Nanoetching Matrix") - (continue-ability - :corp - {:optional - {:prompt "Gain 2 [credits]?" - :yes-ability {:msg (msg "gain 2 [Credits]") - :effect (effect (gain-credits :corp 2))} - :end-effect (effect (clear-wait-prompt :runner))}} - card nil))}} - :abilities [{:cost [:click 1] - :once :per-turn - :msg "gain 2 [Credits]" - :effect (effect (gain-credits 2))}]}) - -(define-card "NASX" - (let [ability {:msg "gain 1 [Credits]" - :label "Gain 1 [Credits] (start of turn)" + :once-key :museum-of-history + :msg (msg "shuffle " + (let [seen (filter :seen targets) + n (count (filter #(not (:seen %)) targets))] + (str (join ", " (map :title seen)) + (when (pos? n) + (str (when-not (empty? seen) " and ") + (quantify n "card"))))) + " into R&D") + :effect (req (doseq [c targets] + (move state side c :deck)) + (shuffle! state side :deck))}] + :implementation "Errata from FAQ 3.1: should be unique"} + + "Nanoetching Matrix" + {:events {:runner-trash {:req (req (same-card? card target)) + :effect (effect (show-wait-prompt :runner "Corp to use Nanoetching Matrix") + (continue-ability + :corp + {:optional + {:prompt "Gain 2 [credits]?" + :yes-ability {:msg (msg "gain 2 [Credits]") + :effect (effect (gain-credits :corp 2))} + :end-effect (effect (clear-wait-prompt :runner))}} + card nil))}} + :abilities [{:cost [:click 1] :once :per-turn - :effect (effect (gain-credits 1))}] - {:implementation "Manual - click NASX to add power counters" - :derezzed-events {:runner-turn-ends corp-rez-toast} - :events {:corp-turn-begins ability} - :abilities [ability - {:label "Place 1 power counter" - :cost [:credit 1] - :effect (effect (add-counter card :power 1) - (system-msg (str "places 1 power counter on NASX")))} - {:label "Place 2 power counters" - :cost [:credit 2] - :effect (effect (add-counter card :power 2) - (system-msg (str "places 2 power counters on NASX")))} - {:label "[Trash] and gain 2 [Credits] for each power counter" - :cost [:click 1] - :msg (msg "gain " (* 2 (get-counters card :power)) " [Credits]") - :effect (effect (trash card {:cause :ability-cost}) - (gain-credits (* 2 (get-counters card :power))))}]})) - -(define-card "Net Analytics" - (let [ability {:req (req (seq (filter #(some #{:tag} %) targets))) - :effect (effect (show-wait-prompt :runner "Corp to use Net Analytics") - (continue-ability - :corp - {:optional - {:prompt "Draw from Net Analytics?" - :yes-ability {:msg (msg "draw a card") - :effect (effect (draw :corp 1))} - :end-effect (effect (clear-wait-prompt :runner))}} - card nil))}] - {:events {:runner-lose-tag (assoc ability :req (req (= side :runner))) - :runner-prevent (assoc ability :req (req (seq (filter #(some #{:tag} %) targets))))}})) - -(define-card "Net Police" - {:recurring (effect (set-prop card :rec-counter (:link runner))) - :effect (effect (set-prop card :rec-counter (:link runner))) - :interactions {:pay-credits {:req (req (= :trace (:source-type eid))) - :type :recurring}}}) - -(define-card "Neurostasis" - (advance-ambush 3 {:req (req (pos? (get-counters (get-card state card) :advancement))) - :async true - :effect (req (let [cnt (get-counters (get-card state card) :advancement)] - (continue-ability - state side - {:prompt (msg "Choose " (quantify cnt "installed card") " to shuffle into the stack") - :player :corp - :cost [:credit 3] - :choices {:req #(and (installed? %) - (runner? %)) - :max cnt} - :msg (msg "shuffle " (join ", " (map :title targets)) " into the stack") - :effect (req (doseq [c targets] - (move state :runner c :deck)) - (shuffle! state :runner :deck))} - card nil)))} - "Pay 3 [Credits] to use Neurostasis ability?")) - -(define-card "News Team" - {:flags {:rd-reveal (req true)} - :access {:msg (msg "force the Runner take 2 tags or add it to their score area as an agenda worth -1 agenda point") - :async true - :effect (effect (continue-ability - {:player :runner - :async true - :prompt "Take 2 tags or add News Team to your score area as an agenda worth -1 agenda point?" - :choices ["Take 2 tags" "Add News Team to score area"] - :effect (req (if (= target "Add News Team to score area") - (do (system-msg state :runner (str "adds News Team to their score area as an agenda worth -1 agenda point")) - (trigger-event state side :no-trash card) - (as-trashed-agenda state :runner eid card -1 {:force true})) - (do (system-msg state :runner (str "takes 2 tags from News Team")) - (gain-tags state :runner eid 2))))} - card targets))}}) - -(define-card "NGO Front" - (letfn [(builder [cost cred] - {:advance-counter-cost cost - :effect (effect (trash card {:cause :ability-cost}) - (gain-credits cred)) - :label (str "[Trash]: Gain " cred " [Credits]") - :msg (str "gain " cred " [Credits]")})] - {:advanceable :always - :abilities [(builder 1 5) - (builder 2 8)]})) - -(define-card "Open Forum" - {:events {:corp-mandatory-draw - {:interactive (req true) - :msg (msg (if (-> corp :deck count pos?) - (str "reveal and draw " (-> corp :deck first :title) " from R&D") - "reveal and draw from R&D but it is empty")) + :msg "gain 2 [Credits]" + :effect (effect (gain-credits 2))}]} + + "NASX" + (let [ability {:msg "gain 1 [Credits]" + :label "Gain 1 [Credits] (start of turn)" + :once :per-turn + :effect (effect (gain-credits 1))}] + {:implementation "Manual - click NASX to add power counters" + :derezzed-events {:runner-turn-ends corp-rez-toast} + :events {:corp-turn-begins ability} + :abilities [ability + {:label "Place 1 power counter" + :cost [:credit 1] + :effect (effect (add-counter card :power 1) + (system-msg (str "places 1 power counter on NASX")))} + {:label "Place 2 power counters" + :cost [:credit 2] + :effect (effect (add-counter card :power 2) + (system-msg (str "places 2 power counters on NASX")))} + {:label "[Trash] and gain 2 [Credits] for each power counter" + :cost [:click 1] + :msg (msg "gain " (* 2 (get-counters card :power)) " [Credits]") + :effect (effect (trash card {:cause :ability-cost}) + (gain-credits (* 2 (get-counters card :power))))}]}) + + "Net Analytics" + (let [ability {:req (req (seq (filter #(some #{:tag} %) targets))) + :effect (effect (show-wait-prompt :runner "Corp to use Net Analytics") + (continue-ability + :corp + {:optional + {:prompt "Draw from Net Analytics?" + :yes-ability {:msg (msg "draw a card") + :effect (effect (draw :corp 1))} + :end-effect (effect (clear-wait-prompt :runner))}} + card nil))}] + {:events {:runner-lose-tag (assoc ability :req (req (= side :runner))) + :runner-prevent (assoc ability :req (req (seq (filter #(some #{:tag} %) targets))))}}) + + "Net Police" + {:recurring (effect (set-prop card :rec-counter (:link runner))) + :effect (effect (set-prop card :rec-counter (:link runner))) + :interactions {:pay-credits {:req (req (= :trace (:source-type eid))) + :type :recurring}}} + + "Neurostasis" + (advance-ambush 3 {:req (req (pos? (get-counters (get-card state card) :advancement))) + :async true + :effect (req (let [cnt (get-counters (get-card state card) :advancement)] + (continue-ability + state side + {:prompt (msg "Choose " (quantify cnt "installed card") " to shuffle into the stack") + :player :corp + :cost [:credit 3] + :choices {:req #(and (installed? %) + (runner? %)) + :max cnt} + :msg (msg "shuffle " (join ", " (map :title targets)) " into the stack") + :effect (req (doseq [c targets] + (move state :runner c :deck)) + (shuffle! state :runner :deck))} + card nil)))} + "Pay 3 [Credits] to use Neurostasis ability?") + + "News Team" + {:flags {:rd-reveal (req true)} + :access {:msg (msg "force the Runner take 2 tags or add it to their score area as an agenda worth -1 agenda point") :async true - :effect (effect (reveal (-> corp :deck first)) - (draw 1) - (continue-ability - {:prompt "Choose a card in HQ to put on top of R&D" + :effect (effect (continue-ability + {:player :runner :async true - :choices {:req #(and (in-hand? %) - (corp? %))} - :msg "add 1 card from HQ to the top of R&D" - :effect (effect (move target :deck {:front true}) - (effect-completed eid))} - card nil))}}}) - -(define-card "PAD Campaign" - (let [ability {:msg "gain 1 [Credits]" - :label "Gain 1 [Credits] (start of turn)" - :once :per-turn - :effect (effect (gain-credits 1))}] - {:derezzed-events {:runner-turn-ends corp-rez-toast} - :events {:corp-turn-begins ability} - :abilities [ability]})) - -(define-card "PAD Factory" - {:abilities [{:cost [:click 1] - :label "Place 1 advancement token on a card" - :choices {:req installed?} - :msg (msg "place 1 advancement token on " (card-str state target)) - :effect (req (add-prop state :corp target :advance-counter 1 {:placed true}) - (let [tgtcid (:cid target)] - (register-turn-flag! - state side - target :can-score - (fn [state side card] - (if (and (= tgtcid - (:cid card)) - (>= (get-counters card :advancement) - (or (:current-cost card) - (:advancementcost card)))) - ((constantly false) (toast state :corp "Cannot score due to PAD Factory." "warning")) - true)))))}]}) - -(define-card "Pālanā Agroplex" - (let [ability {:msg "make each player draw 1 card" - :label "Make each player draw 1 card (start of turn)" - :once :per-turn - :async true - :effect (req (wait-for (draw state :corp 1 nil) - (draw state :runner eid 1 nil)))}] - {:derezzed-events {:runner-turn-ends corp-rez-toast} - :flags {:corp-phase-12 (req true)} - :events {:corp-turn-begins ability} - :abilities [ability]})) - -(define-card "Personalized Portal" - {:events {:corp-turn-begins - {:async true - :effect (req (wait-for (draw state :runner 1 nil) - (let [cnt (count (get-in @state [:runner :hand])) - credits (quot cnt 2)] - (gain-credits state :corp credits) - (system-msg state :corp - (str "uses Personalized Portal to force the runner to draw " - "1 card and gain " credits " [Credits]")) - (effect-completed state side eid))))}}}) - -(define-card "Plan B" - (advance-ambush - 0 - {:req (req (pos? (get-counters (get-card state card) :advancement))) - :effect (req (show-wait-prompt state :runner "Corp to select an agenda to score with Plan B") - (doseq [ag (filter agenda? (:hand corp))] - (update-advancement-cost state side ag)) - (continue-ability - state side - {:prompt "Select an Agenda in HQ to score" - :choices {:req #(and (agenda? %) - (<= (:current-cost %) (get-counters (get-card state card) :advancement)) - (in-hand? %))} - :msg (msg "score " (:title target)) - :effect (effect (score (assoc target :advance-counter - (:current-cost target))) - (clear-wait-prompt :runner))} - card nil))} - "Score an Agenda from HQ?")) - -(define-card "Political Dealings" - (letfn [(pdhelper [agendas n] - {:optional - {:prompt (msg "Reveal and install " (:title (nth agendas n)) "?") - :yes-ability {:async true - :msg (msg "reveal " (:title (nth agendas n))) - :effect (req (reveal state side (nth agendas n)) - (wait-for (corp-install - state side (nth agendas n) nil - {:install-state - (:install-state - (card-def (nth agendas n)) - :unrezzed)}) - (if (< (inc n) (count agendas)) - (continue-ability state side (pdhelper agendas (inc n)) card nil) - (effect-completed state side eid))))} - :no-ability {:async true - :effect (req (if (< (inc n) (count agendas)) - (continue-ability state side (pdhelper agendas (inc n)) card nil) - (effect-completed state side eid)))}}})] - {:events - {:corp-draw - {:async true - :req (req (let [drawn (get-in @state [:corp :register :most-recent-drawn]) - agendas (filter agenda? drawn)] - (seq agendas))) - :effect (req (let [drawn (get-in @state [:corp :register :most-recent-drawn]) - agendas (filter agenda? drawn)] - (continue-ability state side (pdhelper agendas 0) card nil)))}}})) - -(define-card "Primary Transmission Dish" - {:recurring 3 - :interactions {:pay-credits {:req (req (= :trace (:source-type eid))) - :type :recurring}}}) - -(define-card "Private Contracts" - {:effect (effect (add-counter card :credit 14)) - :abilities [{:cost [:click 1] - :counter-cost [:credit 2] - :msg "gain 2 [Credits]" - :effect (req (gain-credits state :corp 2) - (when (zero? (get-counters (get-card state card) :credit)) - (trash state :corp card)))}]}) - -(define-card "Project Junebug" - (advance-ambush 1 {:req (req (pos? (get-counters (get-card state card) :advancement))) - :msg (msg "do " (* 2 (get-counters (get-card state card) :advancement)) " net damage") - :async true - :effect (effect (damage eid :net (* 2 (get-counters (get-card state card) :advancement)) - {:card card}))})) - -(define-card "Psychic Field" - (let [ab {:async true - :req (req installed) - :effect (req (let [hand (count (:hand runner)) - message (str "do " hand " net damage")] - (continue-ability - state side - {:psi {:not-equal - {:msg message - :async true - :effect (effect (damage eid :net hand {:card card}))}}} - card nil)))}] - {:expose ab - :access ab})) - -(define-card "Public Health Portal" - (let [ability {:once :per-turn - :label "Reveal top card of R&D and gain 2 [Credits] (start of turn)" - :interactive (req true) - :msg (msg " reveal " (-> @state :corp :deck first :title) - " from the top of R&D" - " and gain 2 [Credits]") - :effect (effect (reveal (-> @state :corp :deck first)) - (gain-credits 2))}] - {:derezzed-events {:runner-turn-ends corp-rez-toast} - :events {:corp-turn-begins ability} - :abilities [ability]})) - -(define-card "Public Support" - {:effect (effect (add-counter card :power 3)) - :derezzed-events {:runner-turn-ends corp-rez-toast} - :events {:corp-turn-begins - {:async true - :effect (req (add-counter state side card :power -1) - (if (zero? (get-counters (get-card state card) :power)) - (do (system-msg state :corp "uses Public Support to add it to their score area as an agenda worth 1 agenda point") - (as-agenda state :corp eid (dissoc card :counter) 1)) - (effect-completed state side eid)))}}}) - -(define-card "Quarantine System" - (letfn [(rez-ice [cnt] {:prompt "Select an ICE to rez" - :async true - :choices {:req #(and (ice? %) (not (rezzed? %)))} - :msg (msg "rez " (:title target)) - :effect (req (let [agenda (last (:rfg corp)) - ap (:agendapoints agenda 0)] - (rez-cost-bonus state side (* ap -2)) - (rez state side target {:no-warning true}) - (if (< cnt 3) - (continue-ability state side (rez-ice (inc cnt)) card nil) - (effect-completed state side eid))))})] - {:abilities [{:label "Forfeit agenda to rez up to 3 ICE with a 2 [Credit] discount per agenda point" - :req (req (pos? (count (:scored corp)))) - :cost [:forfeit] - :effect (req (continue-ability state side (rez-ice 1) card nil))}]})) - -(define-card "Raman Rai" - {:abilities [{:once :per-turn - :label "Lose [Click] and swap a card in HQ you just drew for a card in Archives" - :req (req (and (pos? (:click corp)) - (not-empty (turn-events state side :corp-draw)))) - :effect (req (let [drawn (get-in @state [:corp :register :most-recent-drawn])] - (lose state :corp :click 1) - (resolve-ability - state side - {:prompt "Choose a card in HQ that you just drew to swap for a card of the same type in Archives" - :choices {:req #(some (fn [c] (same-card? c %)) drawn)} - :effect (req (let [hqcard target - t (:type hqcard)] - (resolve-ability - state side - {:show-discard true - :prompt (msg "Choose an " t " in Archives to reveal and swap into HQ for " (:title hqcard)) - :choices {:req #(and (corp? %) - (= (:type %) t) - (in-discard? %))} - :msg (msg "lose [Click], reveal " (:title hqcard) " from HQ, and swap it for " (:title target) " from Archives") - :effect (req (let [swappedcard (assoc hqcard :zone [:discard]) - archndx (ice-index state target) - arch (get-in @state [:corp :discard]) - newarch (apply conj (subvec arch 0 archndx) swappedcard (subvec arch archndx))] - (reveal state side hqcard) - (swap! state assoc-in [:corp :discard] newarch) - (swap! state update-in [:corp :hand] - (fn [coll] (remove-once #(same-card? % hqcard) coll))) - (move state side target :hand)))} + :prompt "Take 2 tags or add News Team to your score area as an agenda worth -1 agenda point?" + :choices ["Take 2 tags" "Add News Team to score area"] + :effect (req (if (= target "Add News Team to score area") + (do (system-msg state :runner (str "adds News Team to their score area as an agenda worth -1 agenda point")) + (trigger-event state side :no-trash card) + (as-trashed-agenda state :runner eid card -1 {:force true})) + (do (system-msg state :runner (str "takes 2 tags from News Team")) + (gain-tags state :runner eid 2))))} + card targets))}} + + "NGO Front" + (letfn [(builder [cost cred] + {:advance-counter-cost cost + :effect (effect (trash card {:cause :ability-cost}) + (gain-credits cred)) + :label (str "[Trash]: Gain " cred " [Credits]") + :msg (str "gain " cred " [Credits]")})] + {:advanceable :always + :abilities [(builder 1 5) + (builder 2 8)]}) + + "Open Forum" + {:events {:corp-mandatory-draw + {:interactive (req true) + :msg (msg (if (-> corp :deck count pos?) + (str "reveal and draw " (-> corp :deck first :title) " from R&D") + "reveal and draw from R&D but it is empty")) + :async true + :effect (effect (reveal (-> corp :deck first)) + (draw 1) + (continue-ability + {:prompt "Choose a card in HQ to put on top of R&D" + :async true + :choices {:req #(and (in-hand? %) + (corp? %))} + :msg "add 1 card from HQ to the top of R&D" + :effect (effect (move target :deck {:front true}) + (effect-completed eid))} + card nil))}}} + + "PAD Campaign" + (let [ability {:msg "gain 1 [Credits]" + :label "Gain 1 [Credits] (start of turn)" + :once :per-turn + :effect (effect (gain-credits 1))}] + {:derezzed-events {:runner-turn-ends corp-rez-toast} + :events {:corp-turn-begins ability} + :abilities [ability]}) + + "PAD Factory" + {:abilities [{:cost [:click 1] + :label "Place 1 advancement token on a card" + :choices {:req installed?} + :msg (msg "place 1 advancement token on " (card-str state target)) + :effect (req (add-prop state :corp target :advance-counter 1 {:placed true}) + (let [tgtcid (:cid target)] + (register-turn-flag! + state side + target :can-score + (fn [state side card] + (if (and (= tgtcid + (:cid card)) + (>= (get-counters card :advancement) + (or (:current-cost card) + (:advancementcost card)))) + ((constantly false) (toast state :corp "Cannot score due to PAD Factory." "warning")) + true)))))}]} + + "Pālanā Agroplex" + (let [ability {:msg "make each player draw 1 card" + :label "Make each player draw 1 card (start of turn)" + :once :per-turn + :async true + :effect (req (wait-for (draw state :corp 1 nil) + (draw state :runner eid 1 nil)))}] + {:derezzed-events {:runner-turn-ends corp-rez-toast} + :flags {:corp-phase-12 (req true)} + :events {:corp-turn-begins ability} + :abilities [ability]}) + + "Personalized Portal" + {:events {:corp-turn-begins + {:async true + :effect (req (wait-for (draw state :runner 1 nil) + (let [cnt (count (get-in @state [:runner :hand])) + credits (quot cnt 2)] + (gain-credits state :corp credits) + (system-msg state :corp + (str "uses Personalized Portal to force the runner to draw " + "1 card and gain " credits " [Credits]")) + (effect-completed state side eid))))}}} + + "Plan B" + (advance-ambush + 0 + {:req (req (pos? (get-counters (get-card state card) :advancement))) + :effect (req (show-wait-prompt state :runner "Corp to select an agenda to score with Plan B") + (doseq [ag (filter agenda? (:hand corp))] + (update-advancement-cost state side ag)) + (continue-ability + state side + {:prompt "Select an Agenda in HQ to score" + :choices {:req #(and (agenda? %) + (<= (:current-cost %) (get-counters (get-card state card) :advancement)) + (in-hand? %))} + :msg (msg "score " (:title target)) + :effect (effect (score (assoc target :advance-counter + (:current-cost target))) + (clear-wait-prompt :runner))} + card nil))} + "Score an Agenda from HQ?") + + "Political Dealings" + (letfn [(pdhelper [agendas n] + {:optional + {:prompt (msg "Reveal and install " (:title (nth agendas n)) "?") + :yes-ability {:async true + :msg (msg "reveal " (:title (nth agendas n))) + :effect (req (reveal state side (nth agendas n)) + (wait-for (corp-install + state side (nth agendas n) nil + {:install-state + (:install-state + (card-def (nth agendas n)) + :unrezzed)}) + (if (< (inc n) (count agendas)) + (continue-ability state side (pdhelper agendas (inc n)) card nil) + (effect-completed state side eid))))} + :no-ability {:async true + :effect (req (if (< (inc n) (count agendas)) + (continue-ability state side (pdhelper agendas (inc n)) card nil) + (effect-completed state side eid)))}}})] + {:events + {:corp-draw + {:async true + :req (req (let [drawn (get-in @state [:corp :register :most-recent-drawn]) + agendas (filter agenda? drawn)] + (seq agendas))) + :effect (req (let [drawn (get-in @state [:corp :register :most-recent-drawn]) + agendas (filter agenda? drawn)] + (continue-ability state side (pdhelper agendas 0) card nil)))}}}) + + "Primary Transmission Dish" + {:recurring 3 + :interactions {:pay-credits {:req (req (= :trace (:source-type eid))) + :type :recurring}}} + + "Private Contracts" + {:effect (effect (add-counter card :credit 14)) + :abilities [{:cost [:click 1] + :counter-cost [:credit 2] + :msg "gain 2 [Credits]" + :effect (req (gain-credits state :corp 2) + (when (zero? (get-counters (get-card state card) :credit)) + (trash state :corp card)))}]} + + "Project Junebug" + (advance-ambush 1 {:req (req (pos? (get-counters (get-card state card) :advancement))) + :msg (msg "do " (* 2 (get-counters (get-card state card) :advancement)) " net damage") + :async true + :effect (effect (damage eid :net (* 2 (get-counters (get-card state card) :advancement)) + {:card card}))}) + + "Psychic Field" + (let [ab {:async true + :req (req installed) + :effect (req (let [hand (count (:hand runner)) + message (str "do " hand " net damage")] + (continue-ability + state side + {:psi {:not-equal + {:msg message + :async true + :effect (effect (damage eid :net hand {:card card}))}}} + card nil)))}] + {:expose ab + :access ab}) + + "Public Health Portal" + (let [ability {:once :per-turn + :label "Reveal top card of R&D and gain 2 [Credits] (start of turn)" + :interactive (req true) + :msg (msg " reveal " (-> @state :corp :deck first :title) + " from the top of R&D" + " and gain 2 [Credits]") + :effect (effect (reveal (-> @state :corp :deck first)) + (gain-credits 2))}] + {:derezzed-events {:runner-turn-ends corp-rez-toast} + :events {:corp-turn-begins ability} + :abilities [ability]}) + + "Public Support" + {:effect (effect (add-counter card :power 3)) + :derezzed-events {:runner-turn-ends corp-rez-toast} + :events {:corp-turn-begins + {:async true + :effect (req (add-counter state side card :power -1) + (if (zero? (get-counters (get-card state card) :power)) + (do (system-msg state :corp "uses Public Support to add it to their score area as an agenda worth 1 agenda point") + (as-agenda state :corp eid (dissoc card :counter) 1)) + (effect-completed state side eid)))}}} + + "Quarantine System" + (letfn [(rez-ice [cnt] {:prompt "Select an ICE to rez" + :async true + :choices {:req #(and (ice? %) (not (rezzed? %)))} + :msg (msg "rez " (:title target)) + :effect (req (let [agenda (last (:rfg corp)) + ap (:agendapoints agenda 0)] + (rez-cost-bonus state side (* ap -2)) + (rez state side target {:no-warning true}) + (if (< cnt 3) + (continue-ability state side (rez-ice (inc cnt)) card nil) + (effect-completed state side eid))))})] + {:abilities [{:label "Forfeit agenda to rez up to 3 ICE with a 2 [Credit] discount per agenda point" + :req (req (pos? (count (:scored corp)))) + :cost [:forfeit] + :effect (req (continue-ability state side (rez-ice 1) card nil))}]}) + + "Raman Rai" + {:abilities [{:once :per-turn + :label "Lose [Click] and swap a card in HQ you just drew for a card in Archives" + :req (req (and (pos? (:click corp)) + (not-empty (turn-events state side :corp-draw)))) + :effect (req (let [drawn (get-in @state [:corp :register :most-recent-drawn])] + (lose state :corp :click 1) + (resolve-ability + state side + {:prompt "Choose a card in HQ that you just drew to swap for a card of the same type in Archives" + :choices {:req #(some (fn [c] (same-card? c %)) drawn)} + :effect (req (let [hqcard target + t (:type hqcard)] + (resolve-ability + state side + {:show-discard true + :prompt (msg "Choose an " t " in Archives to reveal and swap into HQ for " (:title hqcard)) + :choices {:req #(and (corp? %) + (= (:type %) t) + (in-discard? %))} + :msg (msg "lose [Click], reveal " (:title hqcard) " from HQ, and swap it for " (:title target) " from Archives") + :effect (req (let [swappedcard (assoc hqcard :zone [:discard]) + archndx (ice-index state target) + arch (get-in @state [:corp :discard]) + newarch (apply conj (subvec arch 0 archndx) swappedcard (subvec arch archndx))] + (reveal state side hqcard) + (swap! state assoc-in [:corp :discard] newarch) + (swap! state update-in [:corp :hand] + (fn [coll] (remove-once #(same-card? % hqcard) coll))) + (move state side target :hand)))} card nil)))} - card nil)))}]}) + card nil)))}]} -(define-card "Rashida Jaheem" - (let [ability {:once :per-turn + "Rashida Jaheem" + (let [ability {:once :per-turn + :async true + :label "Gain 3 [Credits] and draw 3 cards (start of turn)" + :effect (effect (continue-ability + {:optional + {:prompt "Trash Rashida Jaheem to gain 3 [Credits] and draw 3 cards?" + :yes-ability {:async true + :msg "gain 3 [Credits] and draw 3 cards" + :effect (req (wait-for (trash state side card nil) + (do (gain-credits state side 3) + (draw state side eid 3 nil))))}}} + card nil))}] + {:derezzed-events {:runner-turn-ends corp-rez-toast} + :flags {:corp-phase-12 (req true)} + :events {:corp-turn-begins ability} + :abilities [ability]}) + + "Reality Threedee" + (let [ability {:effect (req (gain-credits state side (if tagged 2 1))) + :label "Gain credits (start of turn)" + :once :per-turn + :msg (msg (if tagged "gain 2 [Credits]" "gain 1 [Credits]"))}] + {:effect (effect (gain-bad-publicity :corp 1) + (system-msg "takes 1 bad publicity")) + :derezzed-events {:runner-turn-ends corp-rez-toast} + :events {:corp-turn-begins ability} + :abilities [ability]}) + + "Reconstruction Contract" + {:events {:damage {:req (req (and (pos? (nth targets 2)) (= :meat target))) + :effect (effect (add-counter card :advancement 1) + (system-msg "adds 1 advancement token to Reconstruction Contract"))}} + :abilities [{:label "[Trash]: Move advancement tokens to another card" + :prompt "Select a card that can be advanced" + :choices {:req can-be-advanced?} + :effect (req (let [move-to target] + (resolve-ability + state side + {:prompt "Move how many tokens?" + :choices {:number (req (get-counters card :advancement)) + :default (req (get-counters card :advancement))} + :effect (effect (trash card {:cause :ability-cost}) + (add-counter move-to :advancement target {:placed true}) + (system-msg (str "trashes Reconstruction Contract to move " target + (pluralize " advancement token" target) " to " + (card-str state move-to))))} + card nil)))}]} + + "Reversed Accounts" + {:advanceable :always + :abilities [{:cost [:click 1] + :label "Force the Runner to lose 4 [Credits] per advancement" + :msg (msg "force the Runner to lose " (min (* 4 (get-counters card :advancement)) (:credit runner)) " [Credits]") + :effect (effect (trash card {:cause :ability-cost}) + (lose-credits :runner (* 4 (get-counters card :advancement))))}]} + + "Rex Campaign" + (let [ability {:once :per-turn + :req (req (:corp-phase-12 @state)) + :label "Remove 1 counter (start of turn)" + :effect (req (add-counter state side card :power -1) + (when (zero? (get-counters (get-card state card) :power)) + (trash state side card) + (resolve-ability + state side + {:prompt "Remove 1 bad publicity or gain 5 [Credits]?" + :choices ["Remove 1 bad publicity" "Gain 5 [Credits]"] + :msg (msg (if (= target "Remove 1 bad publicity") + "remove 1 bad publicity" "gain 5 [Credits]")) + :effect (req (if (= target "Remove 1 bad publicity") + (lose-bad-publicity state side 1) + (gain-credits state side 5)))} + card targets)))}] + {:effect (effect (add-counter card :power 3)) + :derezzed-events {:runner-turn-ends corp-rez-toast} + :events {:corp-turn-begins ability} + :ability [ability]}) + + "Ronald Five" + {:events {:runner-trash {:req (req (and (= (:side target) "Corp") + (pos? (:click runner)))) + :msg "force the runner to lose 1 [Click]" + :effect (effect (lose :runner :click 1))}}} + + "Ronin" + {:advanceable :always + :abilities [{:cost [:click 1] + :req (req (>= (get-counters card :advancement) 4)) + :msg "do 3 net damage" :async true - :label "Gain 3 [Credits] and draw 3 cards (start of turn)" - :effect (effect (continue-ability - {:optional - {:prompt "Trash Rashida Jaheem to gain 3 [Credits] and draw 3 cards?" - :yes-ability {:async true - :msg "gain 3 [Credits] and draw 3 cards" - :effect (req (wait-for (trash state side card nil) - (do (gain-credits state side 3) - (draw state side eid 3 nil))))}}} - card nil))}] - {:derezzed-events {:runner-turn-ends corp-rez-toast} - :flags {:corp-phase-12 (req true)} - :events {:corp-turn-begins ability} - :abilities [ability]})) - -(define-card "Reality Threedee" - (let [ability {:effect (req (gain-credits state side (if tagged 2 1))) - :label "Gain credits (start of turn)" + :effect (effect (trash card {:cause :ability-cost}) + (damage eid :net 3 {:card card}))}]} + + "Roughneck Repair Squad" + {:abilities [{:label "Gain 6 [Credits], may remove 1 bad publicity" + :cost [:click 3] + :msg "gain 6 [Credits]" + :effect (effect (gain-credits 6) + (continue-ability + {:optional {:req (req (pos? (count-bad-pub state))) + :player :corp + :prompt "Remove 1 bad publicity?" + :yes-ability {:msg "remove 1 bad publicity" + :effect (effect (lose-bad-publicity 1))}}} + card nil))}]} + + "Sandburg" + {:effect (req (add-watch state :sandburg + (fn [k ref old new] + (let [credit (get-in new [:corp :credit])] + (when (not= (get-in old [:corp :credit]) credit) + (update-all-ice ref side))))) + (update-all-ice state side)) + :events {:pre-ice-strength {:req (req (and (ice? target) + (>= (:credit corp) 10))) + :effect (effect (ice-strength-bonus (quot (:credit corp) 5) target))}} + :leave-play (req (remove-watch state :sandburg) + (update-all-ice state side))} + + "Sealed Vault" + {:abilities [{:label "Store any number of [Credits] on Sealed Vault" + :cost [:credit 1] + :prompt "How many [Credits]?" + :choices {:number (req (- (:credit corp) 1))} + :msg (msg "store " target " [Credits]") + :effect (effect (lose-credits target) + (add-counter card :credit target))} + {:label "Move any number of [Credits] to your credit pool" + :cost [:click 1] + :prompt "How many [Credits]?" + :choices {:counter :credit} + :msg (msg "gain " target " [Credits]") + :effect (effect (gain-credits target))} + {:label "[Trash]: Move any number of [Credits] to your credit pool" + :prompt "How many [Credits]?" + :choices {:counter :credit} + :msg (msg "trash it and gain " target " [Credits]") + :effect (effect (trash card {:cause :ability-cost}) + (gain-credits target))}]} + + "Security Subcontract" + {:abilities [{:choices {:req #(and (ice? %) + (rezzed? %))} + :cost [:click 1] + :msg (msg "trash " (:title target) " to gain 4 [Credits]") + :label "Trash a rezzed ICE to gain 4 [Credits]" + :effect (effect (trash target {:cause :ability-cost}) + (gain-credits 4))}]} + + "Sensie Actors Union" + {:derezzed-events {:runner-turn-ends corp-rez-toast} + :flags {:corp-phase-12 (req unprotected)} + :abilities [{:label "Draw 3 cards and add 1 card in HQ to the bottom of R&D" :once :per-turn - :msg (msg (if tagged "gain 2 [Credits]" "gain 1 [Credits]"))}] - {:effect (effect (gain-bad-publicity :corp 1) - (system-msg "takes 1 bad publicity")) - :derezzed-events {:runner-turn-ends corp-rez-toast} - :events {:corp-turn-begins ability} - :abilities [ability]})) + :msg "draw 3 cards" + :effect (effect (draw 3) + (resolve-ability + {:prompt "Select a card in HQ to add to the bottom of R&D" + :choices {:req #(and (corp? %) + (in-hand? %))} + :msg "add 1 card from HQ to the bottom of R&D" + :effect (effect (move target :deck))} + card nil))}]} + + "Server Diagnostics" + (let [ability {:effect (effect (gain-credits 2)) + :once :per-turn + :label "Gain 2 [Credits] (start of turn)" + :msg "gain 2 [Credits]"}] + {:derezzed-events {:runner-turn-ends corp-rez-toast} + :abilities [ability] + :events {:corp-turn-begins ability + :corp-install {:req (req (ice? target)) + :async true + :effect (req (wait-for (trash state side card nil) + (do (system-msg state :runner "trashes Server Diagnostics") + (effect-completed state side eid))))}}}) + + "Shannon Claire" + {:abilities [{:cost [:click 1] + :msg "draw 1 card from the bottom of R&D" + :effect (effect (move (last (:deck corp)) :hand))} + {:label "[Trash]: Search R&D for an agenda" + :prompt "Choose an agenda to add to the bottom of R&D" + :msg (msg "reveal " (:title target) " from R&D and add it to the bottom of R&D") + :choices (req (cancellable (filter agenda? (:deck corp)) :sorted)) + :effect (effect (reveal target) + (trash card {:cause :ability-cost}) + (shuffle! :deck) + (move target :deck))} + {:label "[Trash]: Search Archives for an agenda" + :prompt "Choose an agenda to add to the bottom of R&D" + :msg (msg "reveal " (:title target) " from Archives and add it to the bottom of R&D") + :choices (req (cancellable (filter agenda? (:discard corp)) :sorted)) + :effect (effect (reveal target) + (trash card {:cause :ability-cost}) + (move target :deck))}]} + + "Shattered Remains" + (advance-ambush 1 {:async true + :req (req (pos? (get-counters (get-card state card) :advancement))) + :effect (effect + (continue-ability + (let [counters (get-counters (get-card state card) :advancement)] + {:prompt (msg "Select " (quantify counters "piece") " of hardware to trash") + :msg (msg "trash " (join ", " (map :title targets))) + :cost [:credit 1] + :choices {:max counters + :req #(and (installed? %) + (hardware? %))} + :effect (effect (trash-cards targets))}) + card nil))} + "Pay 1 [Credits] to use Shattered Remains ability?") + + "Shi.Kyū" + {:access + {:async true + :req (req (not= (first (:zone card)) :deck)) + :effect (effect (show-wait-prompt :runner "Corp to use Shi.Kyū") + (continue-ability + {:optional + {:prompt "Pay [Credits] to use Shi.Kyū?" + :yes-ability + {:prompt "How many [Credits] for Shi.Kyū?" + :choices :credit + :msg (msg "attempt to do " target " net damage") + :async true + :effect (req (let [dmg target] + (clear-wait-prompt state :runner) + (continue-ability + state :corp + {:player :runner + :prompt (str "Take " dmg " net damage or add Shi.Kyū to your score area as an agenda worth -1 agenda point?") + :choices [(str "Take " dmg " net damage") "Add Shi.Kyū to score area"] + :async true + :effect (req (if (= target "Add Shi.Kyū to score area") + (do (system-msg state :runner (str "adds Shi.Kyū to their score area as as an agenda worth -1 agenda point")) + (trigger-event state side :no-trash card) + (as-trashed-agenda state :runner eid card -1 {:force true})) + (do (damage state :corp eid :net dmg {:card card}) + (system-msg state :runner (str "takes " dmg " net damage from Shi.Kyū")))))} + card targets)))} + :no-ability {:effect (effect (clear-wait-prompt :runner))}}} + card targets))}} + + "Shock!" + {:flags {:rd-reveal (req true)} + :access {:msg "do 1 net damage" + :async true + :effect (effect (damage eid :net 1 {:card card}))}} -(define-card "Reconstruction Contract" - {:events {:damage {:req (req (and (pos? (nth targets 2)) (= :meat target))) - :effect (effect (add-counter card :advancement 1) - (system-msg "adds 1 advancement token to Reconstruction Contract"))}} - :abilities [{:label "[Trash]: Move advancement tokens to another card" - :prompt "Select a card that can be advanced" - :choices {:req can-be-advanced?} - :effect (req (let [move-to target] - (resolve-ability - state side - {:prompt "Move how many tokens?" - :choices {:number (req (get-counters card :advancement)) - :default (req (get-counters card :advancement))} - :effect (effect (trash card {:cause :ability-cost}) - (add-counter move-to :advancement target {:placed true}) - (system-msg (str "trashes Reconstruction Contract to move " target - (pluralize " advancement token" target) " to " - (card-str state move-to))))} - card nil)))}]}) - -(define-card "Reversed Accounts" - {:advanceable :always - :abilities [{:cost [:click 1] - :label "Force the Runner to lose 4 [Credits] per advancement" - :msg (msg "force the Runner to lose " (min (* 4 (get-counters card :advancement)) (:credit runner)) " [Credits]") - :effect (effect (trash card {:cause :ability-cost}) - (lose-credits :runner (* 4 (get-counters card :advancement))))}]}) - -(define-card "Rex Campaign" - (let [ability {:once :per-turn + "SIU" + {:derezzed-events {:runner-turn-ends corp-rez-toast} + :flags {:corp-phase-12 (req true)} + :abilities [{:label "Trace 3 - Give the Runner 1 tag" :req (req (:corp-phase-12 @state)) - :label "Remove 1 counter (start of turn)" - :effect (req (add-counter state side card :power -1) - (when (zero? (get-counters (get-card state card) :power)) - (trash state side card) - (resolve-ability - state side - {:prompt "Remove 1 bad publicity or gain 5 [Credits]?" - :choices ["Remove 1 bad publicity" "Gain 5 [Credits]"] - :msg (msg (if (= target "Remove 1 bad publicity") - "remove 1 bad publicity" "gain 5 [Credits]")) - :effect (req (if (= target "Remove 1 bad publicity") - (lose-bad-publicity state side 1) - (gain-credits state side 5)))} - card targets)))}] - {:effect (effect (add-counter card :power 3)) - :derezzed-events {:runner-turn-ends corp-rez-toast} - :events {:corp-turn-begins ability} - :ability [ability]})) - -(define-card "Ronald Five" - {:events {:runner-trash {:req (req (and (= (:side target) "Corp") - (pos? (:click runner)))) - :msg "force the runner to lose 1 [Click]" - :effect (effect (lose :runner :click 1))}}}) - -(define-card "Ronin" - {:advanceable :always - :abilities [{:cost [:click 1] - :req (req (>= (get-counters card :advancement) 4)) - :msg "do 3 net damage" - :async true - :effect (effect (trash card {:cause :ability-cost}) - (damage eid :net 3 {:card card}))}]}) - -(define-card "Roughneck Repair Squad" - {:abilities [{:label "Gain 6 [Credits], may remove 1 bad publicity" - :cost [:click 3] - :msg "gain 6 [Credits]" - :effect (effect (gain-credits 6) - (continue-ability - {:optional {:req (req (pos? (count-bad-pub state))) - :player :corp - :prompt "Remove 1 bad publicity?" - :yes-ability {:msg "remove 1 bad publicity" - :effect (effect (lose-bad-publicity 1))}}} - card nil))}]}) - -(define-card "Sandburg" - {:effect (req (add-watch state :sandburg - (fn [k ref old new] - (let [credit (get-in new [:corp :credit])] - (when (not= (get-in old [:corp :credit]) credit) - (update-all-ice ref side))))) - (update-all-ice state side)) - :events {:pre-ice-strength {:req (req (and (ice? target) - (>= (:credit corp) 10))) - :effect (effect (ice-strength-bonus (quot (:credit corp) 5) target))}} - :leave-play (req (remove-watch state :sandburg) - (update-all-ice state side))}) - -(define-card "Sealed Vault" - {:abilities [{:label "Store any number of [Credits] on Sealed Vault" - :cost [:credit 1] - :prompt "How many [Credits]?" - :choices {:number (req (- (:credit corp) 1))} - :msg (msg "store " target " [Credits]") - :effect (effect (lose-credits target) - (add-counter card :credit target))} - {:label "Move any number of [Credits] to your credit pool" - :cost [:click 1] - :prompt "How many [Credits]?" - :choices {:counter :credit} - :msg (msg "gain " target " [Credits]") - :effect (effect (gain-credits target))} - {:label "[Trash]: Move any number of [Credits] to your credit pool" - :prompt "How many [Credits]?" - :choices {:counter :credit} - :msg (msg "trash it and gain " target " [Credits]") - :effect (effect (trash card {:cause :ability-cost}) - (gain-credits target))}]}) - -(define-card "Security Subcontract" - {:abilities [{:choices {:req #(and (ice? %) - (rezzed? %))} - :cost [:click 1] - :msg (msg "trash " (:title target) " to gain 4 [Credits]") - :label "Trash a rezzed ICE to gain 4 [Credits]" - :effect (effect (trash target {:cause :ability-cost}) - (gain-credits 4))}]}) - -(define-card "Sensie Actors Union" - {:derezzed-events {:runner-turn-ends corp-rez-toast} - :flags {:corp-phase-12 (req unprotected)} - :abilities [{:label "Draw 3 cards and add 1 card in HQ to the bottom of R&D" - :once :per-turn - :msg "draw 3 cards" - :effect (effect (draw 3) - (resolve-ability - {:prompt "Select a card in HQ to add to the bottom of R&D" - :choices {:req #(and (corp? %) - (in-hand? %))} - :msg "add 1 card from HQ to the bottom of R&D" - :effect (effect (move target :deck))} - card nil))}]}) - -(define-card "Server Diagnostics" - (let [ability {:effect (effect (gain-credits 2)) + :async true + :effect (effect (trash card {:cause :ability-cost}) + (resolve-ability + {:trace {:base 3 + :label "Trace 3 - Give the Runner 1 tag" + :successful {:msg "give the Runner 1 tag" + :async true + :effect (effect (gain-tags :runner eid 1))}}} + card nil))}]} + + "Snare!" + {:flags {:rd-reveal (req true)} + :access {:req (req (not= (first (:zone card)) :discard)) + :async true + :effect (effect (show-wait-prompt :runner "Corp to use Snare!") + (continue-ability + {:optional + {:prompt "Pay 4 [Credits] to use Snare! ability?" + :end-effect (effect (clear-wait-prompt :runner)) + :yes-ability {:async true + :cost [:credit 4] + :msg "do 3 net damage and give the Runner 1 tag" + :effect (req (wait-for (damage state side :net 3 {:card card}) + (gain-tags state :corp eid 1)))}}} + card nil))}} + + "Space Camp" + {:flags {:rd-reveal (req true)} + :access {:async true + :effect (effect (show-wait-prompt :runner "Corp to use Space Camp") + (continue-ability + {:optional + {:prompt "Place 1 advancement token with Space Camp?" + :cancel-effect (req (clear-wait-prompt state :runner) + (effect-completed state side eid)) + :yes-ability {:msg (msg "place 1 advancement token on " (card-str state target)) + :prompt "Select a card to place an advancement token on with Space Camp" + :choices {:req can-be-advanced?} + :effect (effect (add-prop target :advance-counter 1 {:placed true}) + (clear-wait-prompt :runner))} + :no-ability {:effect (req (clear-wait-prompt state :runner) + (effect-completed state side eid))}}} + card nil))}} + + "Storgotic Resonator" + {:abilities [{:cost [:click 1] + :counter-cost [:power 1] + :label "Do 1 net damage" + :msg "do 1 net damage" + :async true + :effect (effect (damage eid :net 1 {:card card}))}] + :events {:corp-trash {:once :per-turn + :req (req (first-event? + state side :corp-trash + #(= (:faction (:identity runner)) (:faction (first %))))) + :effect (effect (system-msg :corp "adds 1 power counter on Storgotic Resonator") + (add-counter card :power 1))}}} + + "Student Loans" + {:events {:pre-play-instant + {:req (req (and (event? target) (seq (filter #(= (:title %) (:title target)) (:discard runner))))) + :effect (effect (system-msg :corp (str "makes the runner pay an extra 2 [Credits] due to Student Loans")) + (play-cost-bonus [:credit 2]))}}} + + "Sundew" + ; If this a run event then handle in :begin-run as we do not know the server + ; being run on in :runner-spent-click. + {:events {:runner-spent-click {:req (req (first-event? state side :runner-spent-click)) + :msg (req (if (not= :run (get-in @state [:runner :register :click-type])) + "gain 2 [Credits]")) + :effect (req (if (not= :run (get-in @state [:runner :register :click-type])) + (gain-credits state :corp 2)))} + :begin-run {:once :per-turn + :req (req (first-event? state side :runner-spent-click)) + :msg (req (if (and (= :run (get-in @state [:runner :register :click-type])) + (not this-server)) + "gain 2 [Credits]")) + :effect (req (if (and (= :run (get-in @state [:runner :register :click-type])) + (not this-server)) + (gain-credits state :corp 2)))}}} + + "Synth DNA Modification" + {:implementation "Manual fire once subroutine is broken" + :abilities [{:msg "do 1 net damage" + :label "Do 1 net damage after AP subroutine broken" :once :per-turn - :label "Gain 2 [Credits] (start of turn)" - :msg "gain 2 [Credits]"}] - {:derezzed-events {:runner-turn-ends corp-rez-toast} - :abilities [ability] - :events {:corp-turn-begins ability - :corp-install {:req (req (ice? target)) + :effect (effect (damage eid :net 1 {:card card}))}]} + + "Team Sponsorship" + {:events {:agenda-scored {:label "Install a card from Archives or HQ" + :prompt "Select a card from Archives or HQ to install" + :show-discard true + :interactive (req true) :async true - :effect (req (wait-for (trash state side card nil) - (do (system-msg state :runner "trashes Server Diagnostics") - (effect-completed state side eid))))}}})) - -(define-card "Shannon Claire" - {:abilities [{:cost [:click 1] - :msg "draw 1 card from the bottom of R&D" - :effect (effect (move (last (:deck corp)) :hand))} - {:label "[Trash]: Search R&D for an agenda" - :prompt "Choose an agenda to add to the bottom of R&D" - :msg (msg "reveal " (:title target) " from R&D and add it to the bottom of R&D") - :choices (req (cancellable (filter agenda? (:deck corp)) :sorted)) - :effect (effect (reveal target) - (trash card {:cause :ability-cost}) - (shuffle! :deck) - (move target :deck))} - {:label "[Trash]: Search Archives for an agenda" - :prompt "Choose an agenda to add to the bottom of R&D" - :msg (msg "reveal " (:title target) " from Archives and add it to the bottom of R&D") - :choices (req (cancellable (filter agenda? (:discard corp)) :sorted)) - :effect (effect (reveal target) - (trash card {:cause :ability-cost}) - (move target :deck))}]}) - -(define-card "Shattered Remains" - (advance-ambush 1 {:async true - :req (req (pos? (get-counters (get-card state card) :advancement))) - :effect (effect - (continue-ability - (let [counters (get-counters (get-card state card) :advancement)] - {:prompt (msg "Select " (quantify counters "piece") " of hardware to trash") - :msg (msg "trash " (join ", " (map :title targets))) - :cost [:credit 1] - :choices {:max counters - :req #(and (installed? %) - (hardware? %))} - :effect (effect (trash-cards targets))}) - card nil))} - "Pay 1 [Credits] to use Shattered Remains ability?")) - -(define-card "Shi.Kyū" - {:access - {:async true - :req (req (not= (first (:zone card)) :deck)) - :effect (effect (show-wait-prompt :runner "Corp to use Shi.Kyū") - (continue-ability - {:optional - {:prompt "Pay [Credits] to use Shi.Kyū?" - :yes-ability - {:prompt "How many [Credits] for Shi.Kyū?" - :choices :credit - :msg (msg "attempt to do " target " net damage") - :async true - :effect (req (let [dmg target] - (clear-wait-prompt state :runner) + :choices {:req #(and (not (operation? %)) + (corp? %) + (#{[:hand] [:discard]} (:zone %)))} + :msg (msg (corp-install-msg target)) + :effect (effect (corp-install eid target nil {:ignore-install-cost true}))}}} + + "Tech Startup" + {:derezzed-events {:runner-turn-ends corp-rez-toast} + :flags {:corp-phase-12 (req true)} + :abilities [{:label "Install an asset from R&D" + :prompt "Choose an asset to install" + :msg (msg "install " (:title target)) + :choices (req (filter asset? (:deck corp))) + :effect (effect (trash card) + (shuffle! :deck) + (corp-install target nil))}]} + + "TechnoCo" + (letfn [(is-techno-target [card] + (or (program? card) + (hardware? card) + (and (resource? card) (has-subtype? card "Virtual"))))] + {:events {:pre-install {:req (req (and (is-techno-target target) + (not (second targets)))) ; not facedown + :effect (effect (install-cost-bonus [:credit 1]))} + :runner-install {:req (req (and (is-techno-target target) + (not (second targets)))) ; not facedown + :msg "gain 1 [Credits]" + :effect (req (gain-credits state :corp 1))}}}) + + "Tenma Line" + {:abilities [{:label "Swap 2 pieces of installed ICE" + :cost [:click 1] + :prompt "Select two pieces of ICE to swap positions" + :choices {:req #(and (installed? %) + (ice? %)) + :max 2 + :all true} + :effect (req (when (= (count targets) 2) + (swap-ice state side (first targets) (second targets)))) + :msg (msg "swap the positions of " + (card-str state (first targets)) + " and " + (card-str state (second targets)))}]} + + "Test Ground" + (letfn [(derez-card [advancements] + {:async true + :prompt "Derez a card" + :choices {:req #(and (installed? %) + (rezzed? %))} + :effect (req (derez state side target) + (if (pos? (dec advancements)) + (continue-ability state side (derez-card (dec advancements)) card nil) + (effect-completed state side eid)))})] + {:advanceable :always + :abilities [{:label "Derez 1 card for each advancement token" + :req (req (pos? (get-counters card :advancement))) + :msg (msg "derez " (quantify (get-counters card :advancement) "card")) + :effect (req (let [advancements (get-counters card :advancement)] + (trash state side card {:cause :ability-cost}) + (show-wait-prompt state :runner (str "Corp to derez " + (quantify advancements "card"))) + (wait-for (resolve-ability state side (derez-card advancements) card nil) + (clear-wait-prompt state :runner))))}]}) + + "Tiered Subscription" + {:events {:run {:req (req (first-event? state side :run)) + :msg "gain 1 [Credits]" + :effect (effect (gain-credits :corp 1))}}} + + "The Board" + (let [the-board {:req (req (and (= :runner (:as-agenda-side target)) + (not (same-card? target card)))) + :effect (effect (lose :runner :agenda-point 1))}] + {:effect (effect (lose :runner :agenda-point (count (:scored runner)))) + :leave-play (effect (gain :runner :agenda-point (count (:scored runner)))) + :trash-effect {:when-inactive true + :req (req (:access @state)) + :msg "add it to the Runner's score area as an agenda worth 2 agenda points" + :async true + :effect (req (as-agenda state :runner eid card 2))} + :events {:agenda-stolen (dissoc the-board :req) + :as-agenda the-board + :pre-card-moved {:req (req (let [c (first targets) + c-cid (:cid c)] + (some #(when (= c-cid (:cid %)) %) (:scored runner)))) + :effect (req (gain state :runner :agenda-point 1))}}}) + + "The News Now Hour" + {:events {:runner-turn-begins {:effect (req (prevent-current state side))}} + :effect (req (prevent-current state side)) + :leave-play (req (swap! state assoc-in [:runner :register :cannot-play-current] false))} + + "The Root" + {:recurring 3 + :interactions {:pay-credits {:req (req (or (= :advance (:source-type eid)) + (= :corp-install (:source-type eid)) + (= :rez (:source-type eid)))) + :type :recurring}}} + + "Thomas Haas" + {:advanceable :always + :abilities [{:label "Gain credits" + :msg (msg "gain " (* 2 (get-counters card :advancement)) " [Credits]") + :effect (effect (trash card {:cause :ability-cost}) + (gain-credits (* 2 (get-counters card :advancement))))}]} + + "Toshiyuki Sakai" + (advance-ambush + 0 + {:effect (effect (continue-ability + {:prompt "Select an asset or agenda in HQ" + :choices {:req #(and (or (agenda? %) + (asset? %)) + (in-hand? %))} + :msg "swap it for an asset or agenda from HQ" + :effect (req (let [tidx (ice-index state card) + srvcont (get-in @state (cons :corp (:zone card))) + c (get-counters (get-card state card) :advancement) + newcard (assoc target :zone (:zone card) :advance-counter c) + newcont (apply conj (subvec srvcont 0 tidx) newcard (subvec srvcont tidx))] + (resolve-ability + state side + {:effect (req (swap! state assoc-in (cons :corp (:zone card)) newcont) + (swap! state update-in [:corp :hand] + (fn [coll] (remove-once #(same-card? % newcard) coll))) + (trigger-event state side :corp-install newcard) + (move state side card :hand))} card nil) + (resolve-prompt state :runner {:choice "No action"}) + ; gets rid of prompt to trash Toshiyuki since it's back in HQ now + (resolve-ability + state :runner + {:optional + {:player :runner + :priority true + :prompt "Access the newly installed card?" + :yes-ability {:effect (effect (access-card newcard))}}} + card nil)))} + card nil))} + "Swap Toshiyuki Sakai with an agenda or asset from HQ?") + + "Turtlebacks" + {:events {:server-created {:msg "gain 1 [Credits]" + :effect (effect (gain-credits 1))}}} + + "Urban Renewal" + {:effect (effect (add-counter card :power 3)) + :derezzed-events {:runner-turn-ends corp-rez-toast} + :events {:corp-turn-begins + {:async true + :effect (req (add-counter state side card :power -1) + (if (zero? (get-counters (get-card state card) :power)) + (wait-for (trash state side card nil) + (do (system-msg state :corp "uses Urban Renewal to do 4 meat damage") + (damage state side eid :meat 4 {:card card}))) + (effect-completed state side eid)))}}} + + "Victoria Jenkins" + {:effect (req (lose state :runner :click-per-turn 1)) + :leave-play (req (gain state :runner :click-per-turn 1)) + :trash-effect {:when-inactive true + :req (req (:access @state)) + :msg "add it to the Runner's score area as an agenda worth 2 agenda points" + :async true + :effect (req (as-agenda state :runner eid card 2))}} + + "Warden Fatuma" + (let [new-sub {:label "[Warden Fatuma] Force the Runner to lose 1 [Click], if able"}] + (letfn [(all-rezzed-bios [state] + (filter #(and (ice? %) + (has-subtype? % "Bioroid") + (rezzed? %)) + (all-installed state :corp))) + (remove-one [cid state ice] + (remove-extra-subs state :corp cid ice)) + (add-one [cid state ice] + (add-extra-sub state :corp cid ice 0 new-sub)) + (update-all [state func] + (doseq [i (all-rezzed-bios state)] + (func state i)))] + {:effect (req (system-msg + state :corp + "uses Warden Fatuma to add \"[Subroutine] The Runner loses [Click], if able\" before all other subroutines") + (update-all state (partial add-one (:cid card)))) + :leave-play (req (system-msg state :corp "loses Warden Fatuma additional subroutines") + (update-all state (partial remove-one (:cid card)))) + :sub-effect {:msg "force the Runner to lose 1 [Click], if able" + :effect (req (lose state :runner :click 1))} + :events {:rez {:req (req (and (ice? target) + (has-subtype? target "Bioroid"))) + :effect (req (add-one (:cid card) state (get-card state target)))}}})) + + "Watchdog" + {:events {:pre-rez {:req (req (and (ice? target) (not (get-in @state [:per-turn (:cid card)])))) + :effect (effect (rez-cost-bonus (- (count-tags state))))} + :rez {:req (req (and (ice? target) (not (get-in @state [:per-turn (:cid card)])))) + :effect (req (swap! state assoc-in [:per-turn (:cid card)] true))}}} + + "Whampoa Reclamation" + {:abilities [{:label "Trash 1 card from HQ: Add 1 card from Archives to the bottom of R&D" + :once :per-turn + :req (req (and (pos? (count (:hand corp))) + (pos? (count (:discard corp))))) + :async true + :effect (req (show-wait-prompt state :runner "Corp to use Whampoa Reclamation") + (wait-for (resolve-ability state side + {:prompt "Choose a card in HQ to trash" + :choices {:req #(and (in-hand? %) (corp? %))} + :effect (effect (trash target))} + card nil) (continue-ability - state :corp - {:player :runner - :prompt (str "Take " dmg " net damage or add Shi.Kyū to your score area as an agenda worth -1 agenda point?") - :choices [(str "Take " dmg " net damage") "Add Shi.Kyū to score area"] - :async true - :effect (req (if (= target "Add Shi.Kyū to score area") - (do (system-msg state :runner (str "adds Shi.Kyū to their score area as as an agenda worth -1 agenda point")) - (trigger-event state side :no-trash card) - (as-trashed-agenda state :runner eid card -1 {:force true})) - (do (damage state :corp eid :net dmg {:card card}) - (system-msg state :runner (str "takes " dmg " net damage from Shi.Kyū")))))} - card targets)))} - :no-ability {:effect (effect (clear-wait-prompt :runner))}}} - card targets))}}) - -(define-card "Shock!" - {:flags {:rd-reveal (req true)} - :access {:msg "do 1 net damage" - :async true - :effect (effect (damage eid :net 1 {:card card}))}}) - -(define-card "SIU" - {:derezzed-events {:runner-turn-ends corp-rez-toast} - :flags {:corp-phase-12 (req true)} - :abilities [{:label "Trace 3 - Give the Runner 1 tag" - :req (req (:corp-phase-12 @state)) - :async true - :effect (effect (trash card {:cause :ability-cost}) - (resolve-ability - {:trace {:base 3 - :label "Trace 3 - Give the Runner 1 tag" - :successful {:msg "give the Runner 1 tag" - :async true - :effect (effect (gain-tags :runner eid 1))}}} - card nil))}]}) - -(define-card "Snare!" - {:flags {:rd-reveal (req true)} - :access {:req (req (not= (first (:zone card)) :discard)) - :async true - :effect (effect (show-wait-prompt :runner "Corp to use Snare!") - (continue-ability - {:optional - {:prompt "Pay 4 [Credits] to use Snare! ability?" - :end-effect (effect (clear-wait-prompt :runner)) - :yes-ability {:async true - :cost [:credit 4] - :msg "do 3 net damage and give the Runner 1 tag" - :effect (req (wait-for (damage state side :net 3 {:card card}) - (gain-tags state :corp eid 1)))}}} - card nil))}}) - -(define-card "Space Camp" - {:flags {:rd-reveal (req true)} - :access {:async true - :effect (effect (show-wait-prompt :runner "Corp to use Space Camp") - (continue-ability - {:optional - {:prompt "Place 1 advancement token with Space Camp?" - :cancel-effect (req (clear-wait-prompt state :runner) - (effect-completed state side eid)) - :yes-ability {:msg (msg "place 1 advancement token on " (card-str state target)) - :prompt "Select a card to place an advancement token on with Space Camp" - :choices {:req can-be-advanced?} - :effect (effect (add-prop target :advance-counter 1 {:placed true}) - (clear-wait-prompt :runner))} - :no-ability {:effect (req (clear-wait-prompt state :runner) - (effect-completed state side eid))}}} - card nil))}}) - -(define-card "Storgotic Resonator" - {:abilities [{:cost [:click 1] - :counter-cost [:power 1] - :label "Do 1 net damage" - :msg "do 1 net damage" - :async true - :effect (effect (damage eid :net 1 {:card card}))}] - :events {:corp-trash {:once :per-turn - :req (req (first-event? - state side :corp-trash - #(= (:faction (:identity runner)) (:faction (first %))))) - :effect (effect (system-msg :corp "adds 1 power counter on Storgotic Resonator") - (add-counter card :power 1))}}}) - -(define-card "Student Loans" - {:events {:pre-play-instant - {:req (req (and (event? target) (seq (filter #(= (:title %) (:title target)) (:discard runner))))) - :effect (effect (system-msg :corp (str "makes the runner pay an extra 2 [Credits] due to Student Loans")) - (play-cost-bonus [:credit 2]))}}}) - -(define-card "Sundew" - ; If this a run event then handle in :begin-run as we do not know the server - ; being run on in :runner-spent-click. - {:events {:runner-spent-click {:req (req (first-event? state side :runner-spent-click)) - :msg (req (if (not= :run (get-in @state [:runner :register :click-type])) - "gain 2 [Credits]")) - :effect (req (if (not= :run (get-in @state [:runner :register :click-type])) - (gain-credits state :corp 2)))} - :begin-run {:once :per-turn - :req (req (first-event? state side :runner-spent-click)) - :msg (req (if (and (= :run (get-in @state [:runner :register :click-type])) - (not this-server)) - "gain 2 [Credits]")) - :effect (req (if (and (= :run (get-in @state [:runner :register :click-type])) - (not this-server)) - (gain-credits state :corp 2)))}}}) - -(define-card "Synth DNA Modification" - {:implementation "Manual fire once subroutine is broken" - :abilities [{:msg "do 1 net damage" - :label "Do 1 net damage after AP subroutine broken" - :once :per-turn - :effect (effect (damage eid :net 1 {:card card}))}]}) - -(define-card "Team Sponsorship" - {:events {:agenda-scored {:label "Install a card from Archives or HQ" - :prompt "Select a card from Archives or HQ to install" - :show-discard true - :interactive (req true) - :async true - :choices {:req #(and (not (operation? %)) - (corp? %) - (#{[:hand] [:discard]} (:zone %)))} - :msg (msg (corp-install-msg target)) - :effect (effect (corp-install eid target nil {:ignore-install-cost true}))}}}) - -(define-card "Tech Startup" - {:derezzed-events {:runner-turn-ends corp-rez-toast} - :flags {:corp-phase-12 (req true)} - :abilities [{:label "Install an asset from R&D" - :prompt "Choose an asset to install" - :msg (msg "install " (:title target)) - :choices (req (filter asset? (:deck corp))) - :effect (effect (trash card) - (shuffle! :deck) - (corp-install target nil))}]}) - -(define-card "TechnoCo" - (letfn [(is-techno-target [card] - (or (program? card) - (hardware? card) - (and (resource? card) (has-subtype? card "Virtual"))))] - {:events {:pre-install {:req (req (and (is-techno-target target) - (not (second targets)))) ; not facedown - :effect (effect (install-cost-bonus [:credit 1]))} - :runner-install {:req (req (and (is-techno-target target) - (not (second targets)))) ; not facedown - :msg "gain 1 [Credits]" - :effect (req (gain-credits state :corp 1))}}})) - -(define-card "Tenma Line" - {:abilities [{:label "Swap 2 pieces of installed ICE" - :cost [:click 1] - :prompt "Select two pieces of ICE to swap positions" - :choices {:req #(and (installed? %) - (ice? %)) - :max 2 - :all true} - :effect (req (when (= (count targets) 2) - (swap-ice state side (first targets) (second targets)))) - :msg (msg "swap the positions of " - (card-str state (first targets)) - " and " - (card-str state (second targets)))}]}) - -(define-card "Test Ground" - (letfn [(derez-card [advancements] - {:async true - :prompt "Derez a card" - :choices {:req #(and (installed? %) - (rezzed? %))} - :effect (req (derez state side target) - (if (pos? (dec advancements)) - (continue-ability state side (derez-card (dec advancements)) card nil) - (effect-completed state side eid)))})] - {:advanceable :always - :abilities [{:label "Derez 1 card for each advancement token" - :req (req (pos? (get-counters card :advancement))) - :msg (msg "derez " (quantify (get-counters card :advancement) "card")) - :effect (req (let [advancements (get-counters card :advancement)] - (trash state side card {:cause :ability-cost}) - (show-wait-prompt state :runner (str "Corp to derez " - (quantify advancements "card"))) - (wait-for (resolve-ability state side (derez-card advancements) card nil) - (clear-wait-prompt state :runner))))}]})) - -(define-card "Tiered Subscription" - {:events {:run {:req (req (first-event? state side :run)) - :msg "gain 1 [Credits]" - :effect (effect (gain-credits :corp 1))}}}) - -(define-card "The Board" - (let [the-board {:req (req (and (= :runner (:as-agenda-side target)) - (not (same-card? target card)))) - :effect (effect (lose :runner :agenda-point 1))}] - {:effect (effect (lose :runner :agenda-point (count (:scored runner)))) - :leave-play (effect (gain :runner :agenda-point (count (:scored runner)))) - :trash-effect {:when-inactive true - :req (req (:access @state)) - :msg "add it to the Runner's score area as an agenda worth 2 agenda points" - :async true - :effect (req (as-agenda state :runner eid card 2))} - :events {:agenda-stolen (dissoc the-board :req) - :as-agenda the-board - :pre-card-moved {:req (req (let [c (first targets) - c-cid (:cid c)] - (some #(when (= c-cid (:cid %)) %) (:scored runner)))) - :effect (req (gain state :runner :agenda-point 1))}}})) - -(define-card "The News Now Hour" - {:events {:runner-turn-begins {:effect (req (prevent-current state side))}} - :effect (req (prevent-current state side)) - :leave-play (req (swap! state assoc-in [:runner :register :cannot-play-current] false))}) - -(define-card "The Root" - {:recurring 3 - :interactions {:pay-credits {:req (req (or (= :advance (:source-type eid)) - (= :corp-install (:source-type eid)) - (= :rez (:source-type eid)))) - :type :recurring}}}) - -(define-card "Thomas Haas" - {:advanceable :always - :abilities [{:label "Gain credits" - :msg (msg "gain " (* 2 (get-counters card :advancement)) " [Credits]") - :effect (effect (trash card {:cause :ability-cost}) - (gain-credits (* 2 (get-counters card :advancement))))}]}) - -(define-card "Toshiyuki Sakai" - (advance-ambush - 0 - {:effect (effect (continue-ability - {:prompt "Select an asset or agenda in HQ" - :choices {:req #(and (or (agenda? %) - (asset? %)) - (in-hand? %))} - :msg "swap it for an asset or agenda from HQ" - :effect (req (let [tidx (ice-index state card) - srvcont (get-in @state (cons :corp (:zone card))) - c (get-counters (get-card state card) :advancement) - newcard (assoc target :zone (:zone card) :advance-counter c) - newcont (apply conj (subvec srvcont 0 tidx) newcard (subvec srvcont tidx))] - (resolve-ability - state side - {:effect (req (swap! state assoc-in (cons :corp (:zone card)) newcont) - (swap! state update-in [:corp :hand] - (fn [coll] (remove-once #(same-card? % newcard) coll))) - (trigger-event state side :corp-install newcard) - (move state side card :hand))} card nil) - (resolve-prompt state :runner {:choice "No action"}) - ; gets rid of prompt to trash Toshiyuki since it's back in HQ now - (resolve-ability - state :runner - {:optional - {:player :runner - :priority true - :prompt "Access the newly installed card?" - :yes-ability {:effect (effect (access-card newcard))}}} - card nil)))} - card nil))} - "Swap Toshiyuki Sakai with an agenda or asset from HQ?")) - -(define-card "Turtlebacks" - {:events {:server-created {:msg "gain 1 [Credits]" - :effect (effect (gain-credits 1))}}}) - -(define-card "Urban Renewal" - {:effect (effect (add-counter card :power 3)) - :derezzed-events {:runner-turn-ends corp-rez-toast} - :events {:corp-turn-begins - {:async true - :effect (req (add-counter state side card :power -1) - (if (zero? (get-counters (get-card state card) :power)) - (wait-for (trash state side card nil) - (do (system-msg state :corp "uses Urban Renewal to do 4 meat damage") - (damage state side eid :meat 4 {:card card}))) - (effect-completed state side eid)))}}}) - -(define-card "Victoria Jenkins" - {:effect (req (lose state :runner :click-per-turn 1)) - :leave-play (req (gain state :runner :click-per-turn 1)) - :trash-effect {:when-inactive true - :req (req (:access @state)) - :msg "add it to the Runner's score area as an agenda worth 2 agenda points" - :async true - :effect (req (as-agenda state :runner eid card 2))}}) - -(define-card "Warden Fatuma" - (let [new-sub {:label "[Warden Fatuma] Force the Runner to lose 1 [Click], if able"}] - (letfn [(all-rezzed-bios [state] - (filter #(and (ice? %) - (has-subtype? % "Bioroid") - (rezzed? %)) - (all-installed state :corp))) - (remove-one [cid state ice] - (remove-extra-subs state :corp cid ice)) - (add-one [cid state ice] - (add-extra-sub state :corp cid ice 0 new-sub)) - (update-all [state func] - (doseq [i (all-rezzed-bios state)] - (func state i)))] - {:effect (req (system-msg - state :corp - "uses Warden Fatuma to add \"[Subroutine] The Runner loses [Click], if able\" before all other subroutines") - (update-all state (partial add-one (:cid card)))) - :leave-play (req (system-msg state :corp "loses Warden Fatuma additional subroutines") - (update-all state (partial remove-one (:cid card)))) - :sub-effect {:msg "force the Runner to lose 1 [Click], if able" - :effect (req (lose state :runner :click 1))} - :events {:rez {:req (req (and (ice? target) - (has-subtype? target "Bioroid"))) - :effect (req (add-one (:cid card) state (get-card state target)))}}}))) - -(define-card "Watchdog" - {:events {:pre-rez {:req (req (and (ice? target) (not (get-in @state [:per-turn (:cid card)])))) - :effect (effect (rez-cost-bonus (- (count-tags state))))} - :rez {:req (req (and (ice? target) (not (get-in @state [:per-turn (:cid card)])))) - :effect (req (swap! state assoc-in [:per-turn (:cid card)] true))}}}) - -(define-card "Whampoa Reclamation" - {:abilities [{:label "Trash 1 card from HQ: Add 1 card from Archives to the bottom of R&D" - :once :per-turn - :req (req (and (pos? (count (:hand corp))) - (pos? (count (:discard corp))))) - :async true - :effect (req (show-wait-prompt state :runner "Corp to use Whampoa Reclamation") - (wait-for (resolve-ability state side - {:prompt "Choose a card in HQ to trash" - :choices {:req #(and (in-hand? %) (corp? %))} - :effect (effect (trash target))} - card nil) - (continue-ability - state side - {:prompt "Select a card in Archives to add to the bottom of R&D" - :show-discard true - :choices {:req #(and (in-discard? %) (corp? %))} - :msg (msg "trash 1 card from HQ and add " - (if (:seen target) (:title target) "a card") " from Archives to the bottom of R&D") - :effect (effect (move target :deck) - (clear-wait-prompt :runner))} - card nil)))}]}) - -(define-card "Worlds Plaza" - {:abilities [{:label "Install an asset on Worlds Plaza" - :req (req (< (count (:hosted card)) 3)) - :cost [:click 1] - :prompt "Select an asset to install on Worlds Plaza" - :choices {:req #(and (asset? %) - (in-hand? %) - (corp? %))} - :msg (msg "host " (:title target)) - :effect (req (corp-install state side target card) ;; install target onto card - (rez-cost-bonus state side -2) - (rez state side (last (:hosted (get-card state card)))))}]}) - -(define-card "Zaibatsu Loyalty" - {:interactions {:prevent [{:type #{:expose} - :req (req true)}]} - :derezzed-events - {:pre-expose - {:async true - :effect (req (let [etarget target] - (continue-ability - state side - {:optional {:req (req (not (rezzed? card))) - :player :corp - :prompt (msg "The Runner is about to expose " (:title etarget) ". Rez Zaibatsu Loyalty?") - :yes-ability {:effect (effect (rez card))}}} - card nil)))}} - :abilities [{:msg "prevent 1 card from being exposed" - :cost [:credit 1] - :effect (effect (expose-prevent 1))} - {:msg "prevent 1 card from being exposed" - :label "[Trash]: Prevent 1 card from being exposed" - :effect (effect (trash card {:cause :ability-cost}) - (expose-prevent 1))}]}) - -(define-card "Zealous Judge" - {:rez-req (req tagged) - :abilities [{:async true - :label "Give the Runner 1 tag" - :cost [:click 1 :credit 1] - :msg (msg "give the Runner 1 tag") - :effect (effect (gain-tags eid 1))}]}) + state side + {:prompt "Select a card in Archives to add to the bottom of R&D" + :show-discard true + :choices {:req #(and (in-discard? %) (corp? %))} + :msg (msg "trash 1 card from HQ and add " + (if (:seen target) (:title target) "a card") " from Archives to the bottom of R&D") + :effect (effect (move target :deck) + (clear-wait-prompt :runner))} + card nil)))}]} + + "Worlds Plaza" + {:abilities [{:label "Install an asset on Worlds Plaza" + :req (req (< (count (:hosted card)) 3)) + :cost [:click 1] + :prompt "Select an asset to install on Worlds Plaza" + :choices {:req #(and (asset? %) + (in-hand? %) + (corp? %))} + :msg (msg "host " (:title target)) + :effect (req (corp-install state side target card) ;; install target onto card + (rez-cost-bonus state side -2) + (rez state side (last (:hosted (get-card state card)))))}]} + + "Zaibatsu Loyalty" + {:interactions {:prevent [{:type #{:expose} + :req (req true)}]} + :derezzed-events + {:pre-expose + {:async true + :effect (req (let [etarget target] + (continue-ability + state side + {:optional {:req (req (not (rezzed? card))) + :player :corp + :prompt (msg "The Runner is about to expose " (:title etarget) ". Rez Zaibatsu Loyalty?") + :yes-ability {:effect (effect (rez card))}}} + card nil)))}} + :abilities [{:msg "prevent 1 card from being exposed" + :cost [:credit 1] + :effect (effect (expose-prevent 1))} + {:msg "prevent 1 card from being exposed" + :label "[Trash]: Prevent 1 card from being exposed" + :effect (effect (trash card {:cause :ability-cost}) + (expose-prevent 1))}]} + + "Zealous Judge" + {:rez-req (req tagged) + :abilities [{:async true + :label "Give the Runner 1 tag" + :cost [:click 1 :credit 1] + :msg (msg "give the Runner 1 tag") + :effect (effect (gain-tags eid 1))}]}}) diff --git a/src/clj/game/cards/events.clj b/src/clj/game/cards/events.clj index dace17335c..73a34d5151 100644 --- a/src/clj/game/cards/events.clj +++ b/src/clj/game/cards/events.clj @@ -1,7 +1,7 @@ (ns game.cards.events (:require [game.core :refer :all] [game.core.eid :refer [make-eid make-result effect-completed]] - [game.core.card-defs :refer [card-def define-card]] + [game.core.card-defs :refer [card-def]] [game.utils :refer :all] [game.macros :refer [effect req msg wait-for continue-ability]] [clojure.string :refer [split-lines split join lower-case includes? starts-with?]] @@ -33,159 +33,147 @@ card nil))}) ;; Card definitions -(define-card "Account Siphon" - {:req (req hq-runnable) - :makes-run true - :effect (effect (make-run :hq {:req (req (= target :hq)) - :replace-access - {:msg (msg "force the Corp to lose " (min 5 (:credit corp)) - " [Credits], gain " (* 2 (min 5 (:credit corp))) - " [Credits] and take 2 tags") - :async true - :effect (req (wait-for (gain-tags state :runner 2) - (do (gain-credits state :runner (* 2 (min 5 (:credit corp)))) - (lose-credits state :corp (min 5 (:credit corp))) - (effect-completed state side eid))))}} - card))}) - -(define-card "Always Have a Backup Plan" - (letfn [(run-again [server] - {:optional {:prompt "Run again?" - :msg (msg "to make a run on " (zone->name server) ", ignoring additional costs") - :yes-ability {:effect (effect (make-run eid server nil card {:ignore-costs true}))}}})] - {:prompt "Choose a server" - :choices (req runnable-servers) - :async true - :msg (msg "make a run on " target) - :effect (req (let [run-server (server->zone state target)] - (wait-for (make-run state side (make-eid state) target nil card nil) - (continue-ability state side (run-again run-server) card nil))))})) - -(define-card "Amped Up" - {:msg "gain [Click][Click][Click] and suffer 1 brain damage" - :effect (effect (gain :click 3) (damage eid :brain 1 {:unpreventable true :card card}))}) - -(define-card "Another Day, Another Paycheck" - {:events {:agenda-stolen - {:trace {:base 0 - :unsuccessful - {:effect (effect (gain-credits - :runner (+ (:agenda-point runner) (:agenda-point corp)))) - :msg (msg (str "gain " (+ (:agenda-point runner) (:agenda-point corp)) " [Credits]"))}}}}}) - -(define-card "Apocalypse" - (let [corp-trash {:async true - :effect (req (let [ai (all-installed state :corp) - onhost (filter #(= '(:onhost) (:zone %)) ai) - unhosted (->> ai - (remove #(= '(:onhost) (:zone %))) - (sort-by #(vec (:zone %))) - (reverse)) - allcorp (concat onhost unhosted)] - (trash-cards state :runner eid allcorp)))} - runner-facedown {:effect (req (let [installedcards (all-active-installed state :runner) - ishosted (fn [c] (or (= ["onhost"] (get c :zone)) (= '(:onhost) (get c :zone)))) - hostedcards (filter ishosted installedcards) - nonhostedcards (remove ishosted installedcards)] - (doseq [oc hostedcards :let [c (get-card state oc)]] - (flip-facedown state side c)) - (doseq [oc nonhostedcards :let [c (get-card state oc)]] - (flip-facedown state side c))))}] - {:req (req (and (some #{:hq} (:successful-run runner-reg)) - (some #{:rd} (:successful-run runner-reg)) - (some #{:archives} (:successful-run runner-reg)))) - :async true - ;; trash cards from right to left - ;; otherwise, auto-killing servers would move the cards to the next server - ;; so they could no longer be trashed in the same loop - :msg "trash all installed Corp cards and turn all installed Runner cards facedown" - :effect (req (wait-for - (resolve-ability state side corp-trash card nil) - (continue-ability state side runner-facedown card nil)))})) - -(define-card "Because I Can" - (run-event - {:choices (req (filter #(can-run-server? state %) remotes))} - {:req (req (is-remote? target)) - :replace-access {:msg "shuffle all cards in the server into R&D" - :effect (req (doseq [c (:content run-server)] - (move state :corp c :deck)) - (shuffle! state :corp :deck))}})) - -(define-card "Black Hat" - {:trace {:base 4 - :unsuccessful {:effect (effect (register-events (:events (card-def card)) - (assoc card :zone '(:discard))))}} - :events {:pre-access {:req (req (#{:hq :rd} target)) - :effect (effect (access-bonus target 2))} - :runner-turn-ends {:effect (effect (unregister-events card))}}}) - -(define-card "Blueberry!™ Diesel" - {:async true - :prompt "Move a card to the bottom of the stack?" - :not-distinct true - :choices (req (conj (vec (take 2 (:deck runner))) "No")) - :effect (req (when-not (string? target) - (move state side target :deck)) - (system-msg state side - (str "looks at the top 2 cards of the stack" - (when-not (string? target) - " and adds one to the bottom of the stack"))) - (system-msg state side "uses Blueberry!™ Diesel to draw 2 cards") - (draw state :runner eid 2 nil))}) - -(define-card "Blackmail" - (run-event - {:req (req (has-bad-pub? state)) - :msg "prevent ICE from being rezzed during this run"} - nil - (effect (register-run-flag! - card - :can-rez - (fn [state side card] - (if (ice? card) - ((constantly false) - (toast state :corp "Cannot rez ICE on this run due to Blackmail")) - true)))))) - -(define-card "Bribery" - {:implementation "ICE chosen for cost increase is specified at start of run, not on approach" - :prompt "How many credits?" - :choices :credit - :msg (msg "increase the rez cost of the first unrezzed ICE approached by " target " [Credits]") - :effect (effect (continue-ability - (let [bribery-x target] - {:prompt "Choose a server" - :choices (req runnable-servers) - :effect (req (make-run state side target nil card) - (let [run-ices (get-in @state (concat [:corp :servers] (:server (:run @state)) [:ices])) - foremost-ice (last (remove rezzed? run-ices))] - (update! state side (assoc foremost-ice :bribery true)) - (register-events - state side - {:pre-rez-cost {:req (req (:bribery target)) - :once :per-turn - :effect (effect (rez-additional-cost-bonus [:credit bribery-x]))} - :run-ends {:effect (effect (unregister-events card) - (update! (dissoc (find-latest state foremost-ice) :bribery)))}} - (assoc card :zone '(:discard)))))}) - card nil))}) - -(define-card "Brute-Force-Hack" - {:req (req (seq (filter - some? - (for [ice (all-installed state :corp) - :when (and (ice? ice) - (rezzed? ice))] - (let [_ (trigger-event state side :pre-rez-cost ice) - cost (rez-cost state side ice) - _ (swap! state update-in [:bonus] dissoc :cost :rez)] - (when (<= cost (:credit runner)) - true)))))) - :effect - (req (let [credits (:credit runner) - affordable-ice - (seq (filter +(def card-definitions + {"Account Siphon" + {:req (req hq-runnable) + :makes-run true + :effect (effect (make-run :hq {:req (req (= target :hq)) + :replace-access + {:msg (msg "force the Corp to lose " (min 5 (:credit corp)) + " [Credits], gain " (* 2 (min 5 (:credit corp))) + " [Credits] and take 2 tags") + :async true + :effect (req (wait-for (gain-tags state :runner 2) + (do (gain-credits state :runner (* 2 (min 5 (:credit corp)))) + (lose-credits state :corp (min 5 (:credit corp))) + (effect-completed state side eid))))}} + card))} + + "Always Have a Backup Plan" + (letfn [(run-again [server] + {:optional {:prompt "Run again?" + :msg (msg "to make a run on " (zone->name server) ", ignoring additional costs") + :yes-ability {:effect (effect (make-run eid server nil card {:ignore-costs true}))}}})] + {:prompt "Choose a server" + :choices (req runnable-servers) + :async true + :msg (msg "make a run on " target) + :effect (req (let [run-server (server->zone state target)] + (wait-for (make-run state side (make-eid state) target nil card nil) + (continue-ability state side (run-again run-server) card nil))))}) + + "Amped Up" + {:msg "gain [Click][Click][Click] and suffer 1 brain damage" + :effect (effect (gain :click 3) (damage eid :brain 1 {:unpreventable true :card card}))} + + "Another Day, Another Paycheck" + {:events {:agenda-stolen + {:trace {:base 0 + :unsuccessful + {:effect (effect (gain-credits + :runner (+ (:agenda-point runner) (:agenda-point corp)))) + :msg (msg (str "gain " (+ (:agenda-point runner) (:agenda-point corp)) " [Credits]"))}}}}} + + "Apocalypse" + (let [corp-trash {:async true + :effect (req (let [ai (all-installed state :corp) + onhost (filter #(= '(:onhost) (:zone %)) ai) + unhosted (->> ai + (remove #(= '(:onhost) (:zone %))) + (sort-by #(vec (:zone %))) + (reverse)) + allcorp (concat onhost unhosted)] + (trash-cards state :runner eid allcorp)))} + runner-facedown {:effect (req (let [installedcards (all-active-installed state :runner) + ishosted (fn [c] (or (= ["onhost"] (get c :zone)) (= '(:onhost) (get c :zone)))) + hostedcards (filter ishosted installedcards) + nonhostedcards (remove ishosted installedcards)] + (doseq [oc hostedcards :let [c (get-card state oc)]] + (flip-facedown state side c)) + (doseq [oc nonhostedcards :let [c (get-card state oc)]] + (flip-facedown state side c))))}] + {:req (req (and (some #{:hq} (:successful-run runner-reg)) + (some #{:rd} (:successful-run runner-reg)) + (some #{:archives} (:successful-run runner-reg)))) + :async true + ;; trash cards from right to left + ;; otherwise, auto-killing servers would move the cards to the next server + ;; so they could no longer be trashed in the same loop + :msg "trash all installed Corp cards and turn all installed Runner cards facedown" + :effect (req (wait-for + (resolve-ability state side corp-trash card nil) + (continue-ability state side runner-facedown card nil)))}) + + "Because I Can" + (run-event + {:choices (req (filter #(can-run-server? state %) remotes))} + {:req (req (is-remote? target)) + :replace-access {:msg "shuffle all cards in the server into R&D" + :effect (req (doseq [c (:content run-server)] + (move state :corp c :deck)) + (shuffle! state :corp :deck))}}) + + "Black Hat" + {:trace {:base 4 + :unsuccessful {:effect (effect (register-events (:events (card-def card)) + (assoc card :zone '(:discard))))}} + :events {:pre-access {:req (req (#{:hq :rd} target)) + :effect (effect (access-bonus target 2))} + :runner-turn-ends {:effect (effect (unregister-events card))}}} + + "Blueberry!™ Diesel" + {:async true + :prompt "Move a card to the bottom of the stack?" + :not-distinct true + :choices (req (conj (vec (take 2 (:deck runner))) "No")) + :effect (req (when-not (string? target) + (move state side target :deck)) + (system-msg state side + (str "looks at the top 2 cards of the stack" + (when-not (string? target) + " and adds one to the bottom of the stack"))) + (system-msg state side "uses Blueberry!™ Diesel to draw 2 cards") + (draw state :runner eid 2 nil))} + + "Blackmail" + (run-event + {:req (req (has-bad-pub? state)) + :msg "prevent ICE from being rezzed during this run"} + nil + (effect (register-run-flag! + card + :can-rez + (fn [state side card] + (if (ice? card) + ((constantly false) + (toast state :corp "Cannot rez ICE on this run due to Blackmail")) + true))))) + + "Bribery" + {:implementation "ICE chosen for cost increase is specified at start of run, not on approach" + :prompt "How many credits?" + :choices :credit + :msg (msg "increase the rez cost of the first unrezzed ICE approached by " target " [Credits]") + :effect (effect (continue-ability + (let [bribery-x target] + {:prompt "Choose a server" + :choices (req runnable-servers) + :effect (req (make-run state side target nil card) + (let [run-ices (get-in @state (concat [:corp :servers] (:server (:run @state)) [:ices])) + foremost-ice (last (remove rezzed? run-ices))] + (update! state side (assoc foremost-ice :bribery true)) + (register-events + state side + {:pre-rez-cost {:req (req (:bribery target)) + :once :per-turn + :effect (effect (rez-additional-cost-bonus [:credit bribery-x]))} + :run-ends {:effect (effect (unregister-events card) + (update! (dissoc (find-latest state foremost-ice) :bribery)))}} + (assoc card :zone '(:discard)))))}) + card nil))} + + "Brute-Force-Hack" + {:req (req (seq (filter some? (for [ice (all-installed state :corp) :when (and (ice? ice) @@ -193,1064 +181,1077 @@ (let [_ (trigger-event state side :pre-rez-cost ice) cost (rez-cost state side ice) _ (swap! state update-in [:bonus] dissoc :cost :rez)] - (when (<= cost credits) - [(:cid ice) cost])))))] - (continue-ability - state side - {:prompt "How many [Credits]?" - :choices :credit - :msg (msg "spends " target " [Credit] on Brute-Force-Hack") - :effect (effect (continue-ability - {:choices {:req #(and (rezzed? %) - (some (fn [c] (and (= (first c) - (:cid %)) - (<= (second c) target))) - affordable-ice))} - :msg (msg "derez " (card-str state target)) - :effect (effect (derez target))} - card nil))} - card nil)))}) - -(define-card "Build Script" - {:msg "gain 1 [Credits] and draw 2 cards" - :async true - :effect (effect (gain-credits 1) (draw eid 2 nil))}) - -(define-card "By Any Means" - {:effect (effect (register-events (:events (card-def card)) - (dissoc card :zone))) - :events {:runner-turn-ends {:effect (effect (unregister-events card))} - :access {:req (req (not= [:discard] (:zone target))) - :interactive (req true) - :async true - :msg (msg "trash " (:title target) " at no cost and suffer 1 meat damage") - :effect (req (wait-for (trash state side (assoc target :seen true) nil) - (do (swap! state assoc-in [:runner :register :trashed-card] true) - (damage state :runner eid :meat 1 {:unboostable true}))))}}}) - -(define-card "Calling in Favors" - {:msg (msg "gain " (count (filter #(and (has-subtype? % "Connection") (resource? %)) - (all-active-installed state :runner))) " [Credits]") - :effect (effect (gain-credits (count (filter #(and (has-subtype? % "Connection") (resource? %)) - (all-active-installed state :runner)))))}) - -(define-card "Career Fair" - {:prompt "Select a resource to install from your Grip" - :choices {:req #(and (resource? %) - (in-hand? %))} - :effect (effect (install-cost-bonus [:credit -3]) (runner-install target))}) - -(define-card "Careful Planning" - {:prompt "Choose a card in or protecting a remote server" - :choices {:req #(is-remote? (second (:zone %)))} - :effect (effect (add-icon card target "CP" "red") - (system-msg (str "prevents the rezzing of " (card-str state target) - " for the rest of this turn via Careful Planning")) - (register-events {:runner-turn-ends {:effect (effect (remove-icon card target) - (unregister-events - card - {:effects {:runner-turn-ends nil}}))}} + (when (<= cost (:credit runner)) + true)))))) + :effect + (req (let [credits (:credit runner) + affordable-ice + (seq (filter + some? + (for [ice (all-installed state :corp) + :when (and (ice? ice) + (rezzed? ice))] + (let [_ (trigger-event state side :pre-rez-cost ice) + cost (rez-cost state side ice) + _ (swap! state update-in [:bonus] dissoc :cost :rez)] + (when (<= cost credits) + [(:cid ice) cost])))))] + (continue-ability + state side + {:prompt "How many [Credits]?" + :choices :credit + :msg (msg "spends " target " [Credit] on Brute-Force-Hack") + :effect (effect (continue-ability + {:choices {:req #(and (rezzed? %) + (some (fn [c] (and (= (first c) + (:cid %)) + (<= (second c) target))) + affordable-ice))} + :msg (msg "derez " (card-str state target)) + :effect (effect (derez target))} + card nil))} + card nil)))} + + "Build Script" + {:msg "gain 1 [Credits] and draw 2 cards" + :async true + :effect (effect (gain-credits 1) (draw eid 2 nil))} + + "By Any Means" + {:effect (effect (register-events (:events (card-def card)) + (dissoc card :zone))) + :events {:runner-turn-ends {:effect (effect (unregister-events card))} + :access {:req (req (not= [:discard] (:zone target))) + :interactive (req true) + :async true + :msg (msg "trash " (:title target) " at no cost and suffer 1 meat damage") + :effect (req (wait-for (trash state side (assoc target :seen true) nil) + (do (swap! state assoc-in [:runner :register :trashed-card] true) + (damage state :runner eid :meat 1 {:unboostable true}))))}}} + + "Calling in Favors" + {:msg (msg "gain " (count (filter #(and (has-subtype? % "Connection") (resource? %)) + (all-active-installed state :runner))) " [Credits]") + :effect (effect (gain-credits (count (filter #(and (has-subtype? % "Connection") (resource? %)) + (all-active-installed state :runner)))))} + + "Career Fair" + {:prompt "Select a resource to install from your Grip" + :choices {:req #(and (resource? %) + (in-hand? %))} + :effect (effect (install-cost-bonus [:credit -3]) (runner-install target))} + + "Careful Planning" + {:prompt "Choose a card in or protecting a remote server" + :choices {:req #(is-remote? (second (:zone %)))} + :effect (effect (add-icon card target "CP" "red") + (system-msg (str "prevents the rezzing of " (card-str state target) + " for the rest of this turn via Careful Planning")) + (register-events {:runner-turn-ends {:effect (effect (remove-icon card target) + (unregister-events + card + {:effects {:runner-turn-ends nil}}))}} + (assoc card :zone '(:discard))) + (register-turn-flag! card :can-rez + (fn [state side card] + (if (same-card? card target) + ((constantly false) + (toast state :corp "Cannot rez the rest of this turn due to Careful Planning")) + true))))} + + "CBI Raid" + (letfn [(cbi-final [chosen original] + {:prompt (str "The top cards of R&D will be " (clojure.string/join ", " (map :title chosen)) ".") + :choices ["Done" "Start over"] + :async true + :effect (req (if (= target "Done") + (do (doseq [c (reverse chosen)] (move state :corp c :deck {:front true})) + (clear-wait-prompt state :runner) + (effect-completed state side eid)) + (continue-ability state side (cbi-choice original '() (count original) original) + card nil)))}) + (cbi-choice [remaining chosen n original] + {:prompt "Choose a card to move next onto R&D" + :choices remaining + :async true + :effect (req (let [chosen (cons target chosen)] + (if (< (count chosen) n) + (continue-ability state side (cbi-choice (remove-once #(= target %) remaining) + chosen n original) card nil) + (continue-ability state side (cbi-final chosen original) card nil))))})] + {:req (req hq-runnable) + :async true + :effect (effect (make-run :hq {:replace-access + {:msg "force the Corp to add all cards in HQ to the top of R&D" + :async true + :mandatory true + :effect (req (show-wait-prompt state :runner "Corp to add all cards in HQ to the top of R&D") + (let [from (:hand corp)] + (if (pos? (count from)) + (continue-ability state :corp (cbi-choice from '() (count from) from) card nil) + (do (clear-wait-prompt state :runner) + (effect-completed state side eid)))))}} + card))}) + + "Code Siphon" + {:req (req rd-runnable) + :effect (effect (make-run :rd + {:replace-access + {:async true + :prompt "Choose a program to install" + :msg (msg "install " (:title target) " and take 1 tag") + :choices (req (filter program? (:deck runner))) + :effect (effect (trigger-event :searched-stack nil) + (shuffle! :deck) + (install-cost-bonus [:credit (* -3 (count (get-in corp [:servers :rd :ices])))]) + (runner-install target) + (gain-tags eid 1))}} + card))} + + "Cold Read" + (let [end-effect {:prompt "Choose a program that was used during the run to trash " + :choices {:req program?} + :msg (msg "trash " (:title target)) + :effect (effect (trash target {:unpreventable true}))}] + {:async true + :prompt "Choose a server" + :recurring 4 + :choices (req runnable-servers) + :effect (req (let [c (move state side (assoc card :zone '(:discard)) :play-area {:force true})] + (card-init state side c {:resolve-effect false}) + (update! state side (assoc (get-card state c) :cold-read-active true)) + (make-run state side (make-eid state) target + {:end-run {:async true + :effect (effect (trash card) + (continue-ability end-effect nil nil))}} + (assoc c :cold-read-active true)))) + :interactions {:pay-credits {:req (req (:cold-read-active card)) + :type :recurring}}}) + + "Compile" + (letfn [(compile-fn [where] + {:prompt "Choose a program to install" + :choices (req (cancellable (filter program? (get runner where)))) + :effect (req (when (= :deck where) + (trigger-event state side :searched-stack nil) + (shuffle! state side :deck)) + (runner-install state side (assoc-in target [:special :compile-installed] true) {:ignore-all-cost true}))})] + {:implementation "Trigger only on first encounter not enforced" + :prompt "Choose a server" + :msg "make a run and install a program on encounter with the first piece of ICE" + :choices (req runnable-servers) + :async true + :abilities [{:label "Install a program using Compile" + :prompt "Install a program from your Stack or Heap?" + :choices ["Stack" "Heap"] + :msg (msg "install a program from their " target) + :effect (effect (continue-ability + (compile-fn (if (= "Stack" target) :deck :heap)) + card nil))}] + :effect (effect (make-run target nil card) + (prompt! card (str "Click Compile in the Temporary Zone to install a Program") ["OK"] {}) + (continue-ability + {:effect (effect (card-init (move state side (last (:discard runner)) :play-area) {:resolve-effect false}))} + card nil)) + :events {:run-ends {:effect + (req (when-let [compile-installed (some #(when (get-in % [:special :compile-installed]) %) (all-installed state :runner))] + (system-msg state side (str "moved " (:title compile-installed) " to the bottom of the Stack at the end of the run from Compile")) + (move state :runner compile-installed :deck)) + (unregister-events state side card) + (trash state side card))}}}) + + "Contaminate" + {:effect (req (resolve-ability + state side + {:msg (msg "place 3 virus tokens on " (:title target)) + :choices {:req #(and (installed? %) + (runner? %) + (zero? (get-virus-counters state %)))} + :effect (req (add-counter state :runner target :virus 3))} + card nil))} + + "Corporate \"Grant\"" + ;; there are no current interactions where we'd want Grant to not be last, and this fixes a bug with Hayley + {:events {:runner-install {:silent (req true) + :req (req (first-event? state side :runner-install)) + :msg "force the Corp to lose 1 [Credit]" + :effect (effect (lose-credits :corp 1))}}} + + "Corporate Scandal" + {:msg "give the Corp 1 additional bad publicity" + :implementation "No enforcement that this Bad Pub cannot be removed" + :effect (req (swap! state update-in [:corp :bad-publicity :additional] inc)) + :leave-play (req (swap! state update-in [:corp :bad-publicity :additional] dec))} + + "Credit Crash" + {:prompt "Choose a server" :choices (req runnable-servers) + :effect (effect (make-run target nil card) + (register-events (:events (card-def card)) + (assoc card :zone '(:discard)))) + :events {:pre-access-card + {:once :per-run + :async true + :req (req (not= (:type target) "Agenda")) + :effect (req (let [c target + cost (:cost c) + title (:title c)] + (if (can-pay? state :corp eid card nil :credit cost) + (do (show-wait-prompt state :runner "Corp to decide whether or not to prevent the trash") + (continue-ability + state :corp + {:optional + {:prompt (msg "Spend " cost " [Credits] to prevent the trash of " title "?") + :player :corp + :yes-ability {:effect (req (lose-credits state :corp cost) + (system-msg state :corp (str "spends " cost " [Credits] to prevent " + title " from being trashed at no cost")) + (clear-wait-prompt state :runner))} + :no-ability {:msg (msg "trash " title " at no cost") + :async true + :effect (effect (clear-wait-prompt :runner) + (trash-no-cost eid c))}}} + card nil)) + (do (system-msg state side (str "uses Credit Crash to trash " title " at no cost")) + (trash-no-cost state side eid c)))))} + :run-ends {:effect (effect (unregister-events card))}}} + + "Credit Kiting" + {:req (req (some #{:hq :rd :archives} (:successful-run runner-reg))) + :prompt "Select a card to install from your Grip" + :choices {:req #(and (or (hardware? %) + (program? %) + (resource? %)) + (in-hand? %))} + :async true + :effect (req (install-cost-bonus state :runner [:credit -8]) + (wait-for (runner-install state :runner target nil) + (gain-tags state eid :runner 1)))} + + "Cyber Threat" + {:prompt "Choose a server" + :choices (req runnable-servers) + :async true + :effect (req (let [serv target] + (continue-ability + state :corp + {:optional + {:prompt (msg "Rez a piece of ICE protecting " serv "?") + :yes-ability {:prompt (msg "Select a piece of ICE protecting " serv " to rez") + :player :corp + :choices {:req #(and (not (:rezzed %)) + (= (last (:zone %)) :ices))} + :effect (req (rez state :corp target nil))} + :no-ability {:effect (effect (make-run eid serv nil card)) + :msg (msg "make a run on " serv " during which no ICE can be rezzed")}}} + card nil)))} + + "Data Breach" + {:req (req rd-runnable) + :async true + :effect (req (let [db-eid (make-eid state) + events (:events (card-def card))] + (register-events state side + (assoc-in events [:successful-run-ends :eid] db-eid) (assoc card :zone '(:discard))) - (register-turn-flag! card :can-rez - (fn [state side card] - (if (same-card? card target) - ((constantly false) - (toast state :corp "Cannot rez the rest of this turn due to Careful Planning")) - true))))}) - -(define-card "CBI Raid" - (letfn [(cbi-final [chosen original] - {:prompt (str "The top cards of R&D will be " (clojure.string/join ", " (map :title chosen)) ".") - :choices ["Done" "Start over"] - :async true - :effect (req (if (= target "Done") - (do (doseq [c (reverse chosen)] (move state :corp c :deck {:front true})) - (clear-wait-prompt state :runner) - (effect-completed state side eid)) - (continue-ability state side (cbi-choice original '() (count original) original) - card nil)))}) - (cbi-choice [remaining chosen n original] - {:prompt "Choose a card to move next onto R&D" - :choices remaining - :async true - :effect (req (let [chosen (cons target chosen)] - (if (< (count chosen) n) - (continue-ability state side (cbi-choice (remove-once #(= target %) remaining) - chosen n original) card nil) - (continue-ability state side (cbi-final chosen original) card nil))))})] - {:req (req hq-runnable) - :async true - :effect (effect (make-run :hq {:replace-access - {:msg "force the Corp to add all cards in HQ to the top of R&D" - :async true - :mandatory true - :effect (req (show-wait-prompt state :runner "Corp to add all cards in HQ to the top of R&D") - (let [from (:hand corp)] - (if (pos? (count from)) - (continue-ability state :corp (cbi-choice from '() (count from) from) card nil) - (do (clear-wait-prompt state :runner) - (effect-completed state side eid)))))}} - card))})) - -(define-card "Code Siphon" - {:req (req rd-runnable) - :effect (effect (make-run :rd - {:replace-access - {:async true - :prompt "Choose a program to install" - :msg (msg "install " (:title target) " and take 1 tag") - :choices (req (filter program? (:deck runner))) - :effect (effect (trigger-event :searched-stack nil) - (shuffle! :deck) - (install-cost-bonus [:credit (* -3 (count (get-in corp [:servers :rd :ices])))]) - (runner-install target) - (gain-tags eid 1))}} - card))}) - -(define-card "Cold Read" - (let [end-effect {:prompt "Choose a program that was used during the run to trash " - :choices {:req program?} - :msg (msg "trash " (:title target)) - :effect (effect (trash target {:unpreventable true}))}] - {:async true - :prompt "Choose a server" - :recurring 4 - :choices (req runnable-servers) - :effect (req (let [c (move state side (assoc card :zone '(:discard)) :play-area {:force true})] - (card-init state side c {:resolve-effect false}) - (update! state side (assoc (get-card state c) :cold-read-active true)) - (make-run state side (make-eid state) target - {:end-run {:async true - :effect (effect (trash card) - (continue-ability end-effect nil nil))}} - (assoc c :cold-read-active true)))) - :interactions {:pay-credits {:req (req (:cold-read-active card)) - :type :recurring}}})) - -(define-card "Compile" - (letfn [(compile-fn [where] - {:prompt "Choose a program to install" - :choices (req (cancellable (filter program? (get runner where)))) - :effect (req (when (= :deck where) - (trigger-event state side :searched-stack nil) - (shuffle! state side :deck)) - (runner-install state side (assoc-in target [:special :compile-installed] true) {:ignore-all-cost true}))})] - {:implementation "Trigger only on first encounter not enforced" - :prompt "Choose a server" - :msg "make a run and install a program on encounter with the first piece of ICE" - :choices (req runnable-servers) - :async true - :abilities [{:label "Install a program using Compile" - :prompt "Install a program from your Stack or Heap?" - :choices ["Stack" "Heap"] - :msg (msg "install a program from their " target) - :effect (effect (continue-ability - (compile-fn (if (= "Stack" target) :deck :heap)) - card nil))}] - :effect (effect (make-run target nil card) - (prompt! card (str "Click Compile in the Temporary Zone to install a Program") ["OK"] {}) - (continue-ability - {:effect (effect (card-init (move state side (last (:discard runner)) :play-area) {:resolve-effect false}))} - card nil)) - :events {:run-ends {:effect - (req (when-let [compile-installed (some #(when (get-in % [:special :compile-installed]) %) (all-installed state :runner))] - (system-msg state side (str "moved " (:title compile-installed) " to the bottom of the Stack at the end of the run from Compile")) - (move state :runner compile-installed :deck)) - (unregister-events state side card) - (trash state side card))}}})) - -(define-card "Contaminate" - {:effect (req (resolve-ability - state side - {:msg (msg "place 3 virus tokens on " (:title target)) - :choices {:req #(and (installed? %) - (runner? %) - (zero? (get-virus-counters state %)))} - :effect (req (add-counter state :runner target :virus 3))} - card nil))}) - -(define-card "Corporate \"Grant\"" - ;; there are no current interactions where we'd want Grant to not be last, and this fixes a bug with Hayley - {:events {:runner-install {:silent (req true) - :req (req (first-event? state side :runner-install)) - :msg "force the Corp to lose 1 [Credit]" - :effect (effect (lose-credits :corp 1))}}}) - -(define-card "Corporate Scandal" - {:msg "give the Corp 1 additional bad publicity" - :implementation "No enforcement that this Bad Pub cannot be removed" - :effect (req (swap! state update-in [:corp :bad-publicity :additional] inc)) - :leave-play (req (swap! state update-in [:corp :bad-publicity :additional] dec))}) - -(define-card "Credit Crash" - {:prompt "Choose a server" :choices (req runnable-servers) - :effect (effect (make-run target nil card) - (register-events (:events (card-def card)) - (assoc card :zone '(:discard)))) - :events {:pre-access-card - {:once :per-run - :async true - :req (req (not= (:type target) "Agenda")) - :effect (req (let [c target - cost (:cost c) - title (:title c)] - (if (can-pay? state :corp eid card nil :credit cost) - (do (show-wait-prompt state :runner "Corp to decide whether or not to prevent the trash") - (continue-ability - state :corp - {:optional - {:prompt (msg "Spend " cost " [Credits] to prevent the trash of " title "?") - :player :corp - :yes-ability {:effect (req (lose-credits state :corp cost) - (system-msg state :corp (str "spends " cost " [Credits] to prevent " - title " from being trashed at no cost")) - (clear-wait-prompt state :runner))} - :no-ability {:msg (msg "trash " title " at no cost") - :async true - :effect (effect (clear-wait-prompt :runner) - (trash-no-cost eid c))}}} - card nil)) - (do (system-msg state side (str "uses Credit Crash to trash " title " at no cost")) - (trash-no-cost state side eid c)))))} - :run-ends {:effect (effect (unregister-events card))}}}) - -(define-card "Credit Kiting" - {:req (req (some #{:hq :rd :archives} (:successful-run runner-reg))) - :prompt "Select a card to install from your Grip" - :choices {:req #(and (or (hardware? %) - (program? %) - (resource? %)) - (in-hand? %))} - :async true - :effect (req (install-cost-bonus state :runner [:credit -8]) - (wait-for (runner-install state :runner target nil) - (gain-tags state eid :runner 1)))}) - -(define-card "Cyber Threat" - {:prompt "Choose a server" - :choices (req runnable-servers) - :async true - :effect (req (let [serv target] - (continue-ability - state :corp - {:optional - {:prompt (msg "Rez a piece of ICE protecting " serv "?") - :yes-ability {:prompt (msg "Select a piece of ICE protecting " serv " to rez") - :player :corp - :choices {:req #(and (not (:rezzed %)) - (= (last (:zone %)) :ices))} - :effect (req (rez state :corp target nil))} - :no-ability {:effect (effect (make-run eid serv nil card)) - :msg (msg "make a run on " serv " during which no ICE can be rezzed")}}} - card nil)))}) - -(define-card "Data Breach" - {:req (req rd-runnable) - :async true - :effect (req (let [db-eid (make-eid state) - events (:events (card-def card))] - (register-events state side - (assoc-in events [:successful-run-ends :eid] db-eid) - (assoc card :zone '(:discard))) - (wait-for (make-run state side db-eid :rd nil card) - (let [card (get-card state (assoc card :zone '(:discard)))] - (unregister-events state side card) - (when (:run-again card) - (make-run state side db-eid :rd nil card)) - (update! state side (dissoc card :run-again)))))) - :events {:successful-run-ends - {:optional {:req (req (= [:rd] (:server target))) - :prompt "Make another run on R&D?" - :yes-ability {:effect (effect (clear-wait-prompt :corp) - (update! (assoc card :run-again true)))}}}}}) - -(define-card "Day Job" - {:additional-cost [:click 3] - :msg "gain 10 [Credits]" :effect (effect (gain-credits 10))}) - -(define-card "Deep Data Mining" - {:req (req rd-runnable) - :effect (effect (make-run :rd nil card) - (register-events (:events (card-def card)) (assoc card :zone '(:discard)))) - :events {:successful-run {:silent (req true) - :effect (effect (access-bonus :rd (max 0 (min 4 (available-mu state)))))} - :run-ends {:effect (effect (unregister-events card))}}}) - -(define-card "Déjà Vu" - {:prompt "Choose a card to add to Grip" :choices (req (cancellable (:discard runner) :sorted)) - :msg (msg "add " (:title target) " to their Grip") - :effect (req (move state side target :hand) - (when (has-subtype? target "Virus") - (resolve-ability state side - {:prompt "Choose a virus to add to Grip" - :msg (msg "add " (:title target) " to their Grip") - :choices (req (cancellable - (filter #(has-subtype? % "Virus") (:discard runner)) :sorted)) - :effect (effect (move target :hand))} card nil)))}) - -(define-card "Demolition Run" - {:req (req (or rd-runnable hq-runnable)) - :prompt "Choose a server" - :choices ["HQ" "R&D"] - :effect (effect (make-run target nil card) - (resolve-ability - {:effect (req (let [c (move state side (last (:discard runner)) :play-area)] - (card-init state side c {:resolve-effect false}) - (register-events state side - {:run-ends {:effect (effect (trash c))}} - c)))} - card nil)) - :events {:run-ends nil} - :interactions {:access-ability - {:label "[Demolition Run]: Trash card" - :msg (msg "trash " (:title target) " at no cost") + (wait-for (make-run state side db-eid :rd nil card) + (let [card (get-card state (assoc card :zone '(:discard)))] + (unregister-events state side card) + (when (:run-again card) + (make-run state side db-eid :rd nil card)) + (update! state side (dissoc card :run-again)))))) + :events {:successful-run-ends + {:optional {:req (req (= [:rd] (:server target))) + :prompt "Make another run on R&D?" + :yes-ability {:effect (effect (clear-wait-prompt :corp) + (update! (assoc card :run-again true)))}}}}} + + "Day Job" + {:additional-cost [:click 3] + :msg "gain 10 [Credits]" :effect (effect (gain-credits 10))} + + "Deep Data Mining" + {:req (req rd-runnable) + :effect (effect (make-run :rd nil card) + (register-events (:events (card-def card)) (assoc card :zone '(:discard)))) + :events {:successful-run {:silent (req true) + :effect (effect (access-bonus :rd (max 0 (min 4 (available-mu state)))))} + :run-ends {:effect (effect (unregister-events card))}}} + + "Déjà Vu" + {:prompt "Choose a card to add to Grip" :choices (req (cancellable (:discard runner) :sorted)) + :msg (msg "add " (:title target) " to their Grip") + :effect (req (move state side target :hand) + (when (has-subtype? target "Virus") + (resolve-ability state side + {:prompt "Choose a virus to add to Grip" + :msg (msg "add " (:title target) " to their Grip") + :choices (req (cancellable + (filter #(has-subtype? % "Virus") (:discard runner)) :sorted)) + :effect (effect (move target :hand))} card nil)))} + + "Demolition Run" + {:req (req (or rd-runnable hq-runnable)) + :prompt "Choose a server" + :choices ["HQ" "R&D"] + :effect (effect (make-run target nil card) + (resolve-ability + {:effect (req (let [c (move state side (last (:discard runner)) :play-area)] + (card-init state side c {:resolve-effect false}) + (register-events state side + {:run-ends {:effect (effect (trash c))}} + c)))} + card nil)) + :events {:run-ends nil} + :interactions {:access-ability + {:label "[Demolition Run]: Trash card" + :msg (msg "trash " (:title target) " at no cost") + :async true + :effect (effect (trash-no-cost eid target))}}} + + "Deuces Wild" + (let [all [{:effect (effect (gain-credits 3)) + :msg "gain 3 [Credits]"} + {:async true + :effect (effect (draw eid 2 nil)) + :msg "draw 2 cards"} + {:effect (effect (lose-tags 1)) + :msg "remove 1 tag"} + {:prompt "Select 1 piece of ice to expose" + :msg "expose 1 ice and make a run" + :choices {:req #(and (installed? %) (ice? %))} + :async true + :effect (req (wait-for (expose state side target) + (continue-ability + state side + {:prompt "Choose a server" + :choices (req runnable-servers) + :async true + :effect (effect (make-run eid target))} + card nil)))}] + choice (fn choice [abis] + {:prompt "Choose an ability to resolve" + :choices (map #(capitalize (:msg %)) abis) :async true - :effect (effect (trash-no-cost eid target))}}}) - -(define-card "Deuces Wild" - (let [all [{:effect (effect (gain-credits 3)) - :msg "gain 3 [Credits]"} + :effect (req (let [chosen (some #(when (= target (capitalize (:msg %))) %) abis)] + (wait-for + (resolve-ability state side chosen card nil) + (if (= (count abis) 4) + (continue-ability state side (choice (remove-once #(= % chosen) abis)) card nil) + (effect-completed state side eid)))))})] + {:async true + :effect (effect (continue-ability (choice all) card nil))}) + + "Diana's Hunt" + {:implementation "One program per encounter not enforced" + :prompt "Choose a server" + :msg "make a run and install a program on encounter with each ICE" + :choices (req runnable-servers) + :async true + :abilities [{:label "Install a program using Diana's Hunt?" + :async true + :effect (effect (resolve-ability + {:prompt "Choose a program in your Grip to install" + :choices {:req #(and (program? %) + (runner-can-install? state side % false) + (in-hand? %))} + :msg (msg "install " (:title target)) + :effect (req (let [diana-card (assoc-in target [:special :diana-installed] true)] + (runner-install state side diana-card {:ignore-all-cost true}) + (swap! state update :diana #(conj % diana-card))))} + card nil))}] + :effect (effect (make-run target nil card) + (prompt! card (str "Click Diana's Hunt in the Temporary Zone to install a Program") ["OK"] {}) + (resolve-ability + {:effect (req (let [c (move state side (last (:discard runner)) :play-area)] + (card-init state side c {:resolve-effect false}) + (register-events state side + {:run-ends {:effect (req (let [hunt (:diana @state)] + (doseq [c hunt] + (let [installed (find-cid (:cid c) (all-installed state side))] + (when (get-in installed [:special :diana-installed]) + (system-msg state side (str "trashes " (:title c) " at the end of the run from Diana's Hunt")) + (trash state side installed {:unpreventable true})))) + (swap! state dissoc :diana) + (unregister-events state side card) + (trash state side c)))}} + c)))} + card nil)) + :events {:run-ends nil}} + + "Diesel" + {:msg "draw 3 cards" + :async true + :effect (effect (draw eid 3 nil))} + + "Direct Access" + (let [maybe-reshuffle {:optional {:autoresolve (get-autoresolve :auto-reshuffle) + :prompt "Shuffle Direct Access into the Stack?" + :yes-ability {:msg (msg "shuffles Direct Access into the Stack") + :effect (effect (move (get-card state card) :deck) + (shuffle! :deck) + (effect-completed eid))} + :no-ability {:effect (effect (trash (get-card state card) {:unpreventable true :suppress-event true}) + (effect-completed eid))}}}] + {:effect (req (doseq [s [:corp :runner]] + (disable-identity state s)) + (continue-ability state side + {:prompt "Choose a server" + :choices (req runnable-servers) + :async true + :effect (req (let [c (move state side (find-latest state card) :play-area)] + (card-init state side c {:resolve-effect false}) + (wait-for (make-run state side (make-eid state) target nil card) + (doseq [s [:corp :runner]] + (enable-identity state s)) + (continue-ability state side maybe-reshuffle c nil))))} + card nil)) + :abilities [(set-autoresolve :auto-reshuffle "reshuffle")]}) + + "Dirty Laundry" + (run-event + {:end-run {:req (req (:successful run)) + :msg "gain 5 [Credits]" + :effect (effect (gain-credits :runner 5))}}) + + "Diversion of Funds" + {:req (req hq-runnable) + :effect (effect (make-run :hq + {:req (req (= target :hq)) + :replace-access + (let [five-or-all (fn [corp] (min 5 (:credit corp)))] + {:msg (msg "force the Corp to lose " (five-or-all corp) + "[Credits], and gain " (five-or-all corp) "[Credits]") + :effect (effect (lose-credits :corp (five-or-all corp)) + (gain-credits :runner (five-or-all corp)))})} + card))} + + "Divide and Conquer" + {:req (req archives-runnable) + :makes-run true + :async true + :effect (effect (make-run :archives nil card) + (register-events (:events (card-def card)) (assoc card :zone '(:discard)))) + :events {:end-access-phase {:async true + :req (req (= :archives (:from-server target))) + :effect (req (wait-for (do-access state side [:hq] {:no-root true}) + (do-access state side eid [:rd] {:no-root true})))} + :run-ends {:effect (effect (unregister-events card))}}} + + "Drive By" + {:choices {:req #(let [topmost (get-nested-host %)] + (and (is-remote? (second (:zone topmost))) + (= (last (:zone topmost)) :content) + (not (:rezzed %))))} + :async true + :effect (req (wait-for (expose state side target) ;; would be nice if this could return a value on completion + (if async-result ;; expose was successful + (if (#{"Asset" "Upgrade"} (:type target)) + (do (system-msg state :runner (str "uses Drive By to trash " (:title target))) + (trash state side (assoc target :seen true)) + ;; Turn on Reprisal cards + (swap! state assoc-in [:runner :register :trashed-card] true) + (effect-completed state side eid)) + (effect-completed state side eid)) + (effect-completed state side eid))))} + + "Early Bird" + (run-event + {:msg (msg "make a run on " target " and gain [Click]")} + nil + (effect (gain :click 1))) + + "Easy Mark" + {:msg "gain 3 [Credits]" :effect (effect (gain-credits 3))} + + "Embezzle" + (letfn [(name-string [cards] + (join " and " (map :title cards)))] ; either 'card' or 'card1 and card2' + {:req (req hq-runnable) + :effect (effect + (make-run :hq {:req (req (= target :hq)) + :replace-access + {:mandatory true + :msg (msg "reveal 2 cards from HQ and trash all " + target (when (not= "ICE" (:type target)) "s")) + :prompt "Choose a card type" + :choices ["Asset" "Upgrade" "Operation" "ICE"] + :effect (req (let [chosen-type target + cards-to-reveal (take 2 (shuffle (:hand corp))) + cards-to-trash (filter #(is-type? % chosen-type) cards-to-reveal)] + (system-msg state side (str " reveals " (name-string cards-to-reveal) " from HQ")) + (reveal state side cards-to-reveal) + (when (seq cards-to-trash) + (system-msg state side (str " trashes " (name-string cards-to-trash) + " from HQ and gain " (* 4 (count cards-to-trash)) "[Credits]")) + (doseq [c cards-to-trash] + (trash state :runner (assoc c :seen true))) + (gain-credits state :runner (* 4 (count cards-to-trash))))))}} + card))}) + + "Emergency Shutdown" + {:req (req (some #{:hq} (:successful-run runner-reg))) + :msg (msg "derez " (:title target)) + :choices {:req #(and (ice? %) + (rezzed? %))} + :effect (effect (derez target))} + + "Emergent Creativity" + (letfn [(ec [trash-cost to-trash] {:async true - :effect (effect (draw eid 2 nil)) - :msg "draw 2 cards"} - {:effect (effect (lose-tags 1)) - :msg "remove 1 tag"} - {:prompt "Select 1 piece of ice to expose" - :msg "expose 1 ice and make a run" - :choices {:req #(and (installed? %) (ice? %))} - :async true - :effect (req (wait-for (expose state side target) - (continue-ability - state side - {:prompt "Choose a server" - :choices (req runnable-servers) - :async true - :effect (effect (make-run eid target))} - card nil)))}] - choice (fn choice [abis] - {:prompt "Choose an ability to resolve" - :choices (map #(capitalize (:msg %)) abis) - :async true - :effect (req (let [chosen (some #(when (= target (capitalize (:msg %))) %) abis)] - (wait-for - (resolve-ability state side chosen card nil) - (if (= (count abis) 4) - (continue-ability state side (choice (remove-once #(= % chosen) abis)) card nil) - (effect-completed state side eid)))))})] - {:async true - :effect (effect (continue-ability (choice all) card nil))})) - -(define-card "Diana's Hunt" - {:implementation "One program per encounter not enforced" - :prompt "Choose a server" - :msg "make a run and install a program on encounter with each ICE" - :choices (req runnable-servers) - :async true - :abilities [{:label "Install a program using Diana's Hunt?" - :async true - :effect (effect (resolve-ability - {:prompt "Choose a program in your Grip to install" - :choices {:req #(and (program? %) - (runner-can-install? state side % false) - (in-hand? %))} - :msg (msg "install " (:title target)) - :effect (req (let [diana-card (assoc-in target [:special :diana-installed] true)] - (runner-install state side diana-card {:ignore-all-cost true}) - (swap! state update :diana #(conj % diana-card))))} - card nil))}] - :effect (effect (make-run target nil card) - (prompt! card (str "Click Diana's Hunt in the Temporary Zone to install a Program") ["OK"] {}) + :prompt "Choose a hardware or program to install" + :msg (msg "trash " (if (empty? to-trash) "no cards" (join ", " (map :title to-trash))) + " and install " (:title target) " lowering the cost by " trash-cost) + :choices (req (cancellable (filter #(or (program? %) + (hardware? %)) + (:deck runner)) :sorted)) + :effect (req (trigger-event state side :searched-stack nil) + (shuffle! state side :deck) + (doseq [c to-trash] + (trash state side c {:unpreventable true})) + (install-cost-bonus state side [:credit (- trash-cost)]) + (runner-install state side target) + (effect-completed state side eid))})] + {:prompt "Choose Hardware and Programs to trash from your Grip" + :choices {:req #(and (or (hardware? %) + (program? %)) + (in-hand? %)) + :max (req (count (:hand runner)))} + :cancel-effect (effect (resolve-ability (ec 0 []) card nil)) + :effect (req (let [trash-cost (apply + (map :cost targets)) + to-trash targets] + (resolve-ability state side (ec trash-cost to-trash) card nil)))}) + + "Employee Strike" + {:msg "disable the Corp's identity" + :disable-id true + :effect (effect (disable-identity :corp)) + :leave-play (effect (enable-identity :corp))} + + "En Passant" + {:req (req (:successful-run runner-reg)) + :effect (req (let [runtgt (first (flatten (turn-events state side :run))) + serv (zone->name runtgt)] (resolve-ability - {:effect (req (let [c (move state side (last (:discard runner)) :play-area)] - (card-init state side c {:resolve-effect false}) - (register-events state side - {:run-ends {:effect (req (let [hunt (:diana @state)] - (doseq [c hunt] - (let [installed (find-cid (:cid c) (all-installed state side))] - (when (get-in installed [:special :diana-installed]) - (system-msg state side (str "trashes " (:title c) " at the end of the run from Diana's Hunt")) - (trash state side installed {:unpreventable true})))) - (swap! state dissoc :diana) - (unregister-events state side card) - (trash state side c)))}} - c)))} - card nil)) - :events {:run-ends nil}}) - -(define-card "Diesel" - {:msg "draw 3 cards" - :async true - :effect (effect (draw eid 3 nil))}) - -(define-card "Direct Access" - (let [maybe-reshuffle {:optional {:autoresolve (get-autoresolve :auto-reshuffle) - :prompt "Shuffle Direct Access into the Stack?" - :yes-ability {:msg (msg "shuffles Direct Access into the Stack") - :effect (effect (move (get-card state card) :deck) - (shuffle! :deck) - (effect-completed eid))} - :no-ability {:effect (effect (trash (get-card state card) {:unpreventable true :suppress-event true}) - (effect-completed eid))}}}] - {:effect (req (doseq [s [:corp :runner]] - (disable-identity state s)) - (continue-ability state side - {:prompt "Choose a server" - :choices (req runnable-servers) - :async true - :effect (req (let [c (move state side (find-latest state card) :play-area)] - (card-init state side c {:resolve-effect false}) - (wait-for (make-run state side (make-eid state) target nil card) - (doseq [s [:corp :runner]] - (enable-identity state s)) - (continue-ability state side maybe-reshuffle c nil))))} - card nil)) - :abilities [(set-autoresolve :auto-reshuffle "reshuffle")]})) - -(define-card "Dirty Laundry" - (run-event - {:end-run {:req (req (:successful run)) - :msg "gain 5 [Credits]" - :effect (effect (gain-credits :runner 5))}})) - -(define-card "Diversion of Funds" - {:req (req hq-runnable) - :effect (effect (make-run :hq - {:req (req (= target :hq)) - :replace-access - (let [five-or-all (fn [corp] (min 5 (:credit corp)))] - {:msg (msg "force the Corp to lose " (five-or-all corp) - "[Credits], and gain " (five-or-all corp) "[Credits]") - :effect (effect (lose-credits :corp (five-or-all corp)) - (gain-credits :runner (five-or-all corp)))})} - card))}) - -(define-card "Divide and Conquer" - {:req (req archives-runnable) - :makes-run true - :async true - :effect (effect (make-run :archives nil card) - (register-events (:events (card-def card)) (assoc card :zone '(:discard)))) - :events {:end-access-phase {:async true - :req (req (= :archives (:from-server target))) - :effect (req (wait-for (do-access state side [:hq] {:no-root true}) - (do-access state side eid [:rd] {:no-root true})))} - :run-ends {:effect (effect (unregister-events card))}}}) - -(define-card "Drive By" - {:choices {:req #(let [topmost (get-nested-host %)] - (and (is-remote? (second (:zone topmost))) - (= (last (:zone topmost)) :content) - (not (:rezzed %))))} - :async true - :effect (req (wait-for (expose state side target) ;; would be nice if this could return a value on completion - (if async-result ;; expose was successful - (if (#{"Asset" "Upgrade"} (:type target)) - (do (system-msg state :runner (str "uses Drive By to trash " (:title target))) - (trash state side (assoc target :seen true)) - ;; Turn on Reprisal cards - (swap! state assoc-in [:runner :register :trashed-card] true) - (effect-completed state side eid)) - (effect-completed state side eid)) - (effect-completed state side eid))))}) - -(define-card "Early Bird" - (run-event - {:msg (msg "make a run on " target " and gain [Click]")} - nil - (effect (gain :click 1)))) - -(define-card "Easy Mark" - {:msg "gain 3 [Credits]" :effect (effect (gain-credits 3))}) - -(define-card "Embezzle" - (letfn [(name-string [cards] - (join " and " (map :title cards)))] ; either 'card' or 'card1 and card2' - {:req (req hq-runnable) - :effect (effect - (make-run :hq {:req (req (= target :hq)) - :replace-access - {:mandatory true - :msg (msg "reveal 2 cards from HQ and trash all " - target (when (not= "ICE" (:type target)) "s")) - :prompt "Choose a card type" - :choices ["Asset" "Upgrade" "Operation" "ICE"] - :effect (req (let [chosen-type target - cards-to-reveal (take 2 (shuffle (:hand corp))) - cards-to-trash (filter #(is-type? % chosen-type) cards-to-reveal)] - (system-msg state side (str " reveals " (name-string cards-to-reveal) " from HQ")) - (reveal state side cards-to-reveal) - (when (seq cards-to-trash) - (system-msg state side (str " trashes " (name-string cards-to-trash) - " from HQ and gain " (* 4 (count cards-to-trash)) "[Credits]")) - (doseq [c cards-to-trash] - (trash state :runner (assoc c :seen true))) - (gain-credits state :runner (* 4 (count cards-to-trash))))))}} - card))})) - -(define-card "Emergency Shutdown" - {:req (req (some #{:hq} (:successful-run runner-reg))) - :msg (msg "derez " (:title target)) - :choices {:req #(and (ice? %) - (rezzed? %))} - :effect (effect (derez target))}) - -(define-card "Emergent Creativity" - (letfn [(ec [trash-cost to-trash] - {:async true - :prompt "Choose a hardware or program to install" - :msg (msg "trash " (if (empty? to-trash) "no cards" (join ", " (map :title to-trash))) - " and install " (:title target) " lowering the cost by " trash-cost) - :choices (req (cancellable (filter #(or (program? %) - (hardware? %)) - (:deck runner)) :sorted)) - :effect (req (trigger-event state side :searched-stack nil) - (shuffle! state side :deck) - (doseq [c to-trash] - (trash state side c {:unpreventable true})) - (install-cost-bonus state side [:credit (- trash-cost)]) - (runner-install state side target) - (effect-completed state side eid))})] - {:prompt "Choose Hardware and Programs to trash from your Grip" - :choices {:req #(and (or (hardware? %) - (program? %)) - (in-hand? %)) - :max (req (count (:hand runner)))} - :cancel-effect (effect (resolve-ability (ec 0 []) card nil)) - :effect (req (let [trash-cost (apply + (map :cost targets)) - to-trash targets] - (resolve-ability state side (ec trash-cost to-trash) card nil)))})) - -(define-card "Employee Strike" - {:msg "disable the Corp's identity" - :disable-id true - :effect (effect (disable-identity :corp)) - :leave-play (effect (enable-identity :corp))}) - -(define-card "En Passant" - {:req (req (:successful-run runner-reg)) - :effect (req (let [runtgt (first (flatten (turn-events state side :run))) - serv (zone->name runtgt)] - (resolve-ability - state side - {:prompt (msg "Choose an unrezzed piece of ICE protecting " serv " that you passed on your last run") - :choices {:req #(and (ice? %) - (not (rezzed? %)))} - :msg (msg "trash " (card-str state target)) - :effect (req (trash state side target) - (swap! state assoc-in [:runner :register :trashed-card] true))} - card nil)))}) - -(define-card "Encore" - {:req (req (and (some #{:hq} (:successful-run runner-reg)) - (some #{:rd} (:successful-run runner-reg)) - (some #{:archives} (:successful-run runner-reg)))) - :effect (req (swap! state update-in [:runner :extra-turns] (fnil inc 0)) - (move state side (first (:play-area runner)) :rfg)) - :msg "take an additional turn after this one"}) - -(define-card "Escher" - (letfn [(es [] {:prompt "Select two pieces of ICE to swap positions" - :choices {:req #(and (installed? %) (ice? %)) :max 2} - :effect (req (if (= (count targets) 2) - (do (swap-ice state side (first targets) (second targets)) - (resolve-ability state side (es) card nil)) - (system-msg state side "has finished rearranging ICE")))})] - {:req (req hq-runnable) - :effect (effect (make-run :hq {:replace-access - {:msg "rearrange installed ICE" - :effect (effect (resolve-ability (es) card nil))}} card))})) - -(define-card "Eureka!" - {:effect (req (let [topcard (first (:deck runner)) - caninst (or (hardware? topcard) - (program? topcard) - (resource? topcard))] - (if caninst - (resolve-ability - state side - {:optional {:prompt (msg "Install " (:title topcard) "?") - :yes-ability {:effect (effect (install-cost-bonus [:credit -10]) - (runner-install topcard))} - :no-ability {:effect (effect (trash topcard {:unpreventable true}) - (reveal topcard) - (system-msg (str "reveals and trashes " - (:title topcard))))}}} card nil) - (do (reveal state side topcard) - (trash state side topcard {:unpreventable true}) - (system-msg state side (str "reveals and trashes " (:title topcard)))))))}) - -(define-card "Exclusive Party" - {:msg (msg "draw 1 card and gain " - (count (filter #(= (:title %) "Exclusive Party") (:discard runner))) - " [Credits]") - :async true - :effect (req (wait-for (draw state side 1 nil) - (gain-credits state side (count (filter #(= (:title %) "Exclusive Party") (:discard runner)))) - (effect-completed state side eid)))}) - -(define-card "Executive Wiretaps" - {:msg (msg "reveal cards in HQ: " (join ", " (map :title (:hand corp))))}) - -(define-card "Exploit" - {:req (req (and (some #{:hq} (:successful-run runner-reg)) - (some #{:rd} (:successful-run runner-reg)) - (some #{:archives} (:successful-run runner-reg)))) - :prompt "Choose up to 3 pieces of ICE to derez" - :choices {:max 3 :req #(and (rezzed? %) (ice? %))} - :msg (msg "derez " (join ", " (map :title targets))) - :effect (req (doseq [c targets] - (derez state side c)))}) - -(define-card "Exploratory Romp" - (run-event - {:replace-access - {:prompt "Advancements to remove from a card in or protecting this server?" - :choices ["0", "1", "2", "3"] - :async true - :effect (req (let [c (str->int target)] - (show-wait-prompt state :corp "Runner to remove advancements") - (continue-ability + state side + {:prompt (msg "Choose an unrezzed piece of ICE protecting " serv " that you passed on your last run") + :choices {:req #(and (ice? %) + (not (rezzed? %)))} + :msg (msg "trash " (card-str state target)) + :effect (req (trash state side target) + (swap! state assoc-in [:runner :register :trashed-card] true))} + card nil)))} + + "Encore" + {:req (req (and (some #{:hq} (:successful-run runner-reg)) + (some #{:rd} (:successful-run runner-reg)) + (some #{:archives} (:successful-run runner-reg)))) + :effect (req (swap! state update-in [:runner :extra-turns] (fnil inc 0)) + (move state side (first (:play-area runner)) :rfg)) + :msg "take an additional turn after this one"} + + "Escher" + (letfn [(es [] {:prompt "Select two pieces of ICE to swap positions" + :choices {:req #(and (installed? %) (ice? %)) :max 2} + :effect (req (if (= (count targets) 2) + (do (swap-ice state side (first targets) (second targets)) + (resolve-ability state side (es) card nil)) + (system-msg state side "has finished rearranging ICE")))})] + {:req (req hq-runnable) + :effect (effect (make-run :hq {:replace-access + {:msg "rearrange installed ICE" + :effect (effect (resolve-ability (es) card nil))}} card))}) + + "Eureka!" + {:effect (req (let [topcard (first (:deck runner)) + caninst (or (hardware? topcard) + (program? topcard) + (resource? topcard))] + (if caninst + (resolve-ability state side - {:choices {:req #(and (contains? % :advance-counter) - (= (first (:server run)) (second (:zone %))))} - :msg (msg "remove " (quantify c "advancement token") - " from " (card-str state target)) - :effect (req (let [to-remove (min c (get-counters target :advancement))] - (add-prop state :corp target :advance-counter (- to-remove)) - (clear-wait-prompt state :corp) - (effect-completed state side eid)))} - card nil)))}})) - -(define-card "Express Delivery" - {:prompt "Choose a card to add to your Grip" :choices (req (take 4 (:deck runner))) - :msg "look at the top 4 cards of their Stack and add 1 of them to their Grip" - :effect (effect (move target :hand) (shuffle! :deck))}) - -(define-card "Falsified Credentials" - {:prompt "Choose a type" - :choices ["Agenda" "Asset" "Upgrade"] - :msg (msg "guess " target) - :async true - :effect (effect - (continue-ability - (let [chosen-type target] - {:choices {:req #(let [topmost (get-nested-host %)] - (and (is-remote? (second (:zone topmost))) - (= (last (:zone topmost)) :content) - (not (rezzed? %))))} - :async true - :effect (req ;taken from Drive By - maybe refactor - (wait-for (expose state side target) - (if (and async-result ;; expose was successful - (= chosen-type (:type target))) - (continue-ability - state :runner - {:effect (effect (gain-credits 5)) - :msg "gain 5 [Credits] "} - card nil) - (effect-completed state side eid))))}) - card nil))}) - -(define-card "Fear the Masses" - {:req (req hq-runnable) - :effect (effect - (make-run - :hq {:req (req (= target :hq)) - :replace-access - {:async true - :mandatory true - :msg "force the Corp to trash the top card of R&D" - :effect (req (mill state :corp) - (let [n (count (filter #(= (:title card) (:title %)) (:hand runner)))] - (if (pos? n) - (continue-ability - state side - {:prompt "Reveal how many copies of Fear the Masses?" - :choices {:number (req n)} - :effect (req (when (pos? target) - (mill state :corp target) - (system-msg - state side - (str "reveals " target " copies of Fear the Masses," - " forcing the Corp to trash " target " cards" - " from the top of R&D"))))} - card nil) - (effect-completed state side eid))))}} - card))}) - -(define-card "Feint" - {:req (req hq-runnable) - :implementation "Bypass is manual" - :effect (effect (make-run :hq nil card) - (register-events (:events (card-def card)) - (assoc card :zone '(:discard)))) - ;; Don't need a msg since game will print that card access is prevented - :events {:successful-run {:effect (effect (prevent-access))} - :run-ends {:effect (effect (unregister-events card))}}}) - -(define-card "Fisk Investment Seminar" - {:msg "make each player draw 3 cards" - :async true - :effect (req (wait-for (draw state :runner 3 nil) - (draw state :corp eid 3 nil)))}) - -(define-card "Forged Activation Orders" - {:choices {:req #(and (ice? %) - (not (rezzed? %)))} - :effect (req (let [ice target - serv (zone->name (second (:zone ice))) - icepos (ice-index state ice)] - (resolve-ability - state :corp - {:prompt (msg "Rez " (:title ice) " at position " icepos - " of " serv " or trash it?") :choices ["Rez" "Trash"] - :effect (effect (resolve-ability - (if (and (= target "Rez") (<= (rez-cost state :corp ice) (:credit corp))) - {:msg (msg "force the rez of " (:title ice)) - :effect (effect (rez :corp ice))} - {:msg (msg "trash the ICE at position " icepos " of " serv) - :effect (effect (trash :corp ice))}) - card nil))} - card nil)))}) - -(define-card "Forked" - (cutlery "Sentry")) - -(define-card "Frame Job" - {:prompt "Choose an agenda to forfeit" - :choices (req (:scored runner)) - :effect (effect (forfeit target) - (gain-bad-publicity :corp 1)) - :msg (msg "forfeit " (:title target) " and give the Corp 1 bad publicity")}) - -(define-card "Frantic Coding" - {:async true - :events {:runner-shuffle-deck nil} - :effect - (req (let [topten (take 10 (:deck runner))] - (prompt! state :runner card (str "The top 10 cards of the Stack are " - (join ", " (map :title topten))) ["OK"] {}) - (continue-ability - state side - {:prompt "Install a program?" - :choices (conj (vec (sort-by :title (filter #(and (program? %) - (can-pay? state side eid card nil - (modified-install-cost state side % [:credit -5]))) - topten))) "No install") - :async true - :effect (req (if (not= target "No install") - (do (register-events state side - {:runner-shuffle-deck - {:effect (effect (update! (assoc card :shuffle-occurred true)))}} - (assoc card :zone '(:discard))) - (install-cost-bonus state side [:credit -5]) - (let [to-trash (remove #(same-card? % target) topten)] - (wait-for (runner-install state side target nil) - (let [card (get-card state (assoc card :zone '(:discard)))] - (if (not (:shuffle-occurred card)) - (do (system-msg state side (str "trashes " (join ", " (map :title to-trash)))) - (doseq [c to-trash] (trash state side c {:unpreventable true})) - (effect-completed state side eid)) - (do (system-msg state side "does not have to trash cards because the stack was shuffled") - (effect-completed state side eid))))))) - (do (doseq [c topten] (trash state side c {:unpreventable true})) - (system-msg state side (str "trashes " (join ", " (map :title topten)))))))} - card nil)))}) - -(define-card "\"Freedom Through Equality\"" - {:events {:agenda-stolen {:msg "add it to their score area as an agenda worth 1 agenda point" - :async true - :effect (req (as-agenda state :runner eid card 1))}}}) - -(define-card "Freelance Coding Contract" - {:choices {:max 5 - :req #(and (program? %) - (in-hand? %))} - :msg (msg "trash " (join ", " (map :title targets)) " and gain " - (* 2 (count targets)) " [Credits]") - :effect (req (doseq [c targets] - (trash state side c {:unpreventable true})) - (gain-credits state side (* 2 (count targets))))}) - -(define-card "Game Day" - {:msg (msg "draw " (- (hand-size state :runner) (count (:hand runner))) " cards") - :async true - :effect (effect (draw eid (- (hand-size state :runner) (count (:hand runner))) nil))}) - -(define-card "Glut Cipher" - (let [corp-choose {:show-discard true - :async true - :player :corp - :prompt (msg "Select 5 cards from Archives to add to HQ") - :choices {:max 5 - :all true - :req #(and (corp? %) - (in-discard? %))} - :msg (msg "move " - (let [seen (filter :seen targets) - m (count (remove :seen targets))] - (str (join ", " (map :title seen)) - (when (pos? m) - (str (when-not (empty? seen) " and ") - (quantify m "unseen card"))) - " into HQ, then trash 5 cards"))) - :effect (req (wait-for - (resolve-ability state side - {:effect (req (doseq [c targets] - (move state side c :hand)))} - card targets) - (continue-ability state side - {:async true - :effect (req (doseq [c (take 5 (shuffle (:hand corp)))] - (trash state :corp c)) - (clear-wait-prompt state :runner) - (effect-completed state :runner eid))} - card nil)))} - access-effect {:mandatory true - :async true - :req (req (>= (count (:discard corp)) 5)) - :effect (req (show-wait-prompt - state :runner - "Corp to choose which cards to pick up from Archives") ;; For some reason it just shows successful-run-trigger-message, but this works!? - (continue-ability state side - corp-choose - card nil))}] - {:req (req archives-runnable) - :makes-run true - :effect (effect (make-run :archives - {:req (req (= target :archives)) - :replace-access access-effect} - card))})) - -(define-card "Government Investigations" - {:flags {:prevent-secretly-spend (req 2)}}) - -(define-card "Guinea Pig" - {:msg "trash all cards in the grip and gain 10 [Credits]" - :effect (req (doseq [c (:hand runner)] - (trash state :runner c {:unpreventable true})) - (gain-credits state :runner 10))}) - -(define-card "Hacktivist Meeting" - {:events {:pre-rez-cost {:req (req (not (ice? target))) - :effect (effect (rez-additional-cost-bonus [:randomly-trash-from-hand 1]))}}}) - -(define-card "High-Stakes Job" - (run-event - {:choices (req (let [unrezzed-ice #(seq (filter (complement rezzed?) (:ices (second %)))) - bad-zones (keys (filter (complement unrezzed-ice) (get-in @state [:corp :servers])))] - (zones->sorted-names (remove (set bad-zones) (get-runnable-zones state)))))} - {:end-run {:req (req (:successful run)) - :msg "gain 12 [Credits]" - :effect (effect (gain-credits :runner 12))}})) - -(define-card "Hostage" - {:prompt "Choose a Connection" - :choices (req (cancellable (filter #(has-subtype? % "Connection") (:deck runner)) :sorted)) - :msg (msg "add " (:title target) " to their Grip and shuffle their Stack") - :effect (effect (trigger-event :searched-stack nil) - (continue-ability - (let [connection target] - {:optional - {:prompt (str "Install " (:title connection) "?") - :yes-ability {:effect (effect (runner-install connection) - (shuffle! :deck))} - :no-ability {:effect (effect (move connection :hand) - (shuffle! :deck))}}}) - card nil))}) - -(define-card "Hot Pursuit" - {:req (req hq-runnable) - :makes-run true - :effect (effect (make-run :hq {:req (req (= target :hq)) - :successful-run {:async true - :msg "gain 9 [Credits] and take 1 tag" - :effect (req (wait-for (gain-tags state :runner 1) - (gain-credits state :runner 9) - (effect-completed state side eid)))}} card))}) - -(define-card "Isolation" - {:additional-cost [:resource 1] - :msg "gain 7 [Credits]" - :effect (effect (gain-credits 7))}) - -(define-card "I've Had Worse" - {:async true - :effect (effect (draw eid 3 nil)) - :trash-effect {:when-inactive true - :async true - :req (req (#{:meat :net} target)) - :effect (effect (draw :runner eid 3 nil)) :msg "draw 3 cards"}}) - -(define-card "Immolation Script" - {:req (req archives-runnable) - :effect (effect (make-run :archives nil card) - (register-events (:events (card-def card)) (assoc card :zone '(:discard)))) - :events {:pre-access - {:async true - :req (req (and (= target :archives) - ;; don't prompt unless there's at least 1 rezzed ICE matching one in Archives - (not-empty (clojure.set/intersection - (into #{} (map :title (filter ice? (:discard corp)))) - (into #{} (map :title (filter rezzed? (all-installed state :corp)))))))) - :effect (req (continue-ability - state side - {:async true - :prompt "Choose a piece of ICE in Archives" - :choices (req (filter ice? (:discard corp))) - :effect (req (let [icename (:title target)] + {:optional {:prompt (msg "Install " (:title topcard) "?") + :yes-ability {:effect (effect (install-cost-bonus [:credit -10]) + (runner-install topcard))} + :no-ability {:effect (effect (trash topcard {:unpreventable true}) + (reveal topcard) + (system-msg (str "reveals and trashes " + (:title topcard))))}}} card nil) + (do (reveal state side topcard) + (trash state side topcard {:unpreventable true}) + (system-msg state side (str "reveals and trashes " (:title topcard)))))))} + + "Exclusive Party" + {:msg (msg "draw 1 card and gain " + (count (filter #(= (:title %) "Exclusive Party") (:discard runner))) + " [Credits]") + :async true + :effect (req (wait-for (draw state side 1 nil) + (gain-credits state side (count (filter #(= (:title %) "Exclusive Party") (:discard runner)))) + (effect-completed state side eid)))} + + "Executive Wiretaps" + {:msg (msg "reveal cards in HQ: " (join ", " (map :title (:hand corp))))} + + "Exploit" + {:req (req (and (some #{:hq} (:successful-run runner-reg)) + (some #{:rd} (:successful-run runner-reg)) + (some #{:archives} (:successful-run runner-reg)))) + :prompt "Choose up to 3 pieces of ICE to derez" + :choices {:max 3 :req #(and (rezzed? %) (ice? %))} + :msg (msg "derez " (join ", " (map :title targets))) + :effect (req (doseq [c targets] + (derez state side c)))} + + "Exploratory Romp" + (run-event + {:replace-access + {:prompt "Advancements to remove from a card in or protecting this server?" + :choices ["0", "1", "2", "3"] + :async true + :effect (req (let [c (str->int target)] + (show-wait-prompt state :corp "Runner to remove advancements") + (continue-ability + state side + {:choices {:req #(and (contains? % :advance-counter) + (= (first (:server run)) (second (:zone %))))} + :msg (msg "remove " (quantify c "advancement token") + " from " (card-str state target)) + :effect (req (let [to-remove (min c (get-counters target :advancement))] + (add-prop state :corp target :advance-counter (- to-remove)) + (clear-wait-prompt state :corp) + (effect-completed state side eid)))} + card nil)))}}) + + "Express Delivery" + {:prompt "Choose a card to add to your Grip" :choices (req (take 4 (:deck runner))) + :msg "look at the top 4 cards of their Stack and add 1 of them to their Grip" + :effect (effect (move target :hand) (shuffle! :deck))} + + "Falsified Credentials" + {:prompt "Choose a type" + :choices ["Agenda" "Asset" "Upgrade"] + :msg (msg "guess " target) + :async true + :effect (effect + (continue-ability + (let [chosen-type target] + {:choices {:req #(let [topmost (get-nested-host %)] + (and (is-remote? (second (:zone topmost))) + (= (last (:zone topmost)) :content) + (not (rezzed? %))))} + :async true + :effect (req ;taken from Drive By - maybe refactor + (wait-for (expose state side target) + (if (and async-result ;; expose was successful + (= chosen-type (:type target))) (continue-ability - state side - {:async true - :prompt (msg "Select a rezzed copy of " icename " to trash") - :choices {:req #(and (ice? %) - (rezzed? %) - (= (:title %) icename))} - :msg (msg "trash " (card-str state target)) - :effect (req (trash state :corp target) - (unregister-events state side card) - (effect-completed state side eid))} - card nil)))} - card nil))}}}) - -(define-card "In the Groove" - {:effect (req (register-events state side (:events (card-def card)) - (dissoc card :zone))) - :events {:runner-turn-ends {:effect (effect (unregister-events card))} - :runner-install {:req (req (<= 1 (:cost target))) - :interactive (req (has-subtype? target "Cybernetic")) + state :runner + {:effect (effect (gain-credits 5)) + :msg "gain 5 [Credits] "} + card nil) + (effect-completed state side eid))))}) + card nil))} + + "Fear the Masses" + {:req (req hq-runnable) + :effect (effect + (make-run + :hq {:req (req (= target :hq)) + :replace-access + {:async true + :mandatory true + :msg "force the Corp to trash the top card of R&D" + :effect (req (mill state :corp) + (let [n (count (filter #(= (:title card) (:title %)) (:hand runner)))] + (if (pos? n) + (continue-ability + state side + {:prompt "Reveal how many copies of Fear the Masses?" + :choices {:number (req n)} + :effect (req (when (pos? target) + (mill state :corp target) + (system-msg + state side + (str "reveals " target " copies of Fear the Masses," + " forcing the Corp to trash " target " cards" + " from the top of R&D"))))} + card nil) + (effect-completed state side eid))))}} + card))} + + "Feint" + {:req (req hq-runnable) + :implementation "Bypass is manual" + :effect (effect (make-run :hq nil card) + (register-events (:events (card-def card)) + (assoc card :zone '(:discard)))) + ;; Don't need a msg since game will print that card access is prevented + :events {:successful-run {:effect (effect (prevent-access))} + :run-ends {:effect (effect (unregister-events card))}}} + + "Fisk Investment Seminar" + {:msg "make each player draw 3 cards" + :async true + :effect (req (wait-for (draw state :runner 3 nil) + (draw state :corp eid 3 nil)))} + + "Forged Activation Orders" + {:choices {:req #(and (ice? %) + (not (rezzed? %)))} + :effect (req (let [ice target + serv (zone->name (second (:zone ice))) + icepos (ice-index state ice)] + (resolve-ability + state :corp + {:prompt (msg "Rez " (:title ice) " at position " icepos + " of " serv " or trash it?") :choices ["Rez" "Trash"] + :effect (effect (resolve-ability + (if (and (= target "Rez") (<= (rez-cost state :corp ice) (:credit corp))) + {:msg (msg "force the rez of " (:title ice)) + :effect (effect (rez :corp ice))} + {:msg (msg "trash the ICE at position " icepos " of " serv) + :effect (effect (trash :corp ice))}) + card nil))} + card nil)))} + + "Forked" + (cutlery "Sentry") + + "Frame Job" + {:prompt "Choose an agenda to forfeit" + :choices (req (:scored runner)) + :effect (effect (forfeit target) + (gain-bad-publicity :corp 1)) + :msg (msg "forfeit " (:title target) " and give the Corp 1 bad publicity")} + + "Frantic Coding" + {:async true + :events {:runner-shuffle-deck nil} + :effect + (req (let [topten (take 10 (:deck runner))] + (prompt! state :runner card (str "The top 10 cards of the Stack are " + (join ", " (map :title topten))) ["OK"] {}) + (continue-ability + state side + {:prompt "Install a program?" + :choices (conj (vec (sort-by :title (filter #(and (program? %) + (can-pay? state side eid card nil + (modified-install-cost state side % [:credit -5]))) + topten))) "No install") + :async true + :effect (req (if (not= target "No install") + (do (register-events state side + {:runner-shuffle-deck + {:effect (effect (update! (assoc card :shuffle-occurred true)))}} + (assoc card :zone '(:discard))) + (install-cost-bonus state side [:credit -5]) + (let [to-trash (remove #(same-card? % target) topten)] + (wait-for (runner-install state side target nil) + (let [card (get-card state (assoc card :zone '(:discard)))] + (if (not (:shuffle-occurred card)) + (do (system-msg state side (str "trashes " (join ", " (map :title to-trash)))) + (doseq [c to-trash] (trash state side c {:unpreventable true})) + (effect-completed state side eid)) + (do (system-msg state side "does not have to trash cards because the stack was shuffled") + (effect-completed state side eid))))))) + (do (doseq [c topten] (trash state side c {:unpreventable true})) + (system-msg state side (str "trashes " (join ", " (map :title topten)))))))} + card nil)))} + + "\"Freedom Through Equality\"" + {:events {:agenda-stolen {:msg "add it to their score area as an agenda worth 1 agenda point" :async true - :prompt "What to get from In the Groove?" - :choices ["Draw 1 card" "Gain 1 [Credits]"] - :effect (req (if (= target "Draw 1 card") - (draw state side eid 1 nil) - (do (gain-credits state side 1) - (effect-completed state side eid)))) - :msg (msg (lower-case target))}}}) - -(define-card "Independent Thinking" - (letfn [(cards-to-draw [targets] - (* (count targets) - (if (some #(and (not (facedown? %)) (has-subtype? % "Directive")) targets) 2 1)))] - {:async true - :prompt "Choose up to 5 installed cards to trash with Independent Thinking" - :choices {:max 5 - :req #(and (installed? %) - (runner? %))} - :effect (req (wait-for (trash-cards state side targets nil) - (draw state :runner eid (cards-to-draw targets) nil))) - :msg (msg "trash " (join ", " (map :title targets)) " and draw " (quantify (cards-to-draw targets) "card"))})) - -(define-card "Indexing" - {:req (req rd-runnable) - :async true - :effect (effect (make-run - :rd - {:req (req (= target :rd)) - :replace-access - {:msg "rearrange the top 5 cards of R&D" - :async true - :effect (req (show-wait-prompt state :corp "Runner to rearrange the top cards of R&D") - (let [from (take 5 (:deck corp))] - (if (pos? (count from)) - (continue-ability state side (reorder-choice :corp :corp from '() - (count from) from) card nil) - (do (clear-wait-prompt state :corp) - (effect-completed state side eid)))))}} - card))}) - -(define-card "Infiltration" - {:prompt "Gain 2 [Credits] or expose a card?" - :choices ["Gain 2 [Credits]" "Expose a card"] - :effect (effect (continue-ability - (if (= target "Expose a card") - {:choices {:req installed?} + :effect (req (as-agenda state :runner eid card 1))}}} + + "Freelance Coding Contract" + {:choices {:max 5 + :req #(and (program? %) + (in-hand? %))} + :msg (msg "trash " (join ", " (map :title targets)) " and gain " + (* 2 (count targets)) " [Credits]") + :effect (req (doseq [c targets] + (trash state side c {:unpreventable true})) + (gain-credits state side (* 2 (count targets))))} + + "Game Day" + {:msg (msg "draw " (- (hand-size state :runner) (count (:hand runner))) " cards") + :async true + :effect (effect (draw eid (- (hand-size state :runner) (count (:hand runner))) nil))} + + "Glut Cipher" + (let [corp-choose {:show-discard true + :async true + :player :corp + :prompt (msg "Select 5 cards from Archives to add to HQ") + :choices {:max 5 + :all true + :req #(and (corp? %) + (in-discard? %))} + :msg (msg "move " + (let [seen (filter :seen targets) + m (count (remove :seen targets))] + (str (join ", " (map :title seen)) + (when (pos? m) + (str (when-not (empty? seen) " and ") + (quantify m "unseen card"))) + " into HQ, then trash 5 cards"))) + :effect (req (wait-for + (resolve-ability state side + {:effect (req (doseq [c targets] + (move state side c :hand)))} + card targets) + (continue-ability state side + {:async true + :effect (req (doseq [c (take 5 (shuffle (:hand corp)))] + (trash state :corp c)) + (clear-wait-prompt state :runner) + (effect-completed state :runner eid))} + card nil)))} + access-effect {:mandatory true :async true - :effect (effect (expose eid target))} - {:msg "gain 2 [Credits]" - :effect (effect (gain-credits 2))}) - card nil))}) - -(define-card "Information Sifting" - (letfn [(access-pile [cards pile pile-size] - {:prompt "Choose a card to access. You must access all cards." - :choices [(str "Card from pile " pile)] - :async true - :req (req (if (:max-access run) - (< (total-cards-accessed run) (:max-access run)) - true)) - :effect (req (wait-for - (access-card state side (first cards)) - (if (< 1 (count cards)) - (continue-ability state side (access-pile (next cards) pile pile-size) card nil) - (effect-completed state side eid))))}) - (which-pile [p1 p2] - {:prompt "Choose a pile to access" - :choices [(str "Pile 1 (" (quantify (count p1) "card") ")") - (str "Pile 2 (" (quantify (count p2) "card") ")")] - :async true - :effect (req (let [choice (if (starts-with? target "Pile 1") 1 2)] - (clear-wait-prompt state :corp) - (system-msg state side (str "chooses to access " target)) - (continue-ability - state side - (access-pile (if (= 1 choice) p1 p2) choice (count (if (= 1 choice) p1 p2))) - card nil)))})] - (let [access-effect - {:async true - :mandatory true - :effect (req (if (< 1 (count (:hand corp))) - (do (show-wait-prompt state :runner "Corp to create two piles") - (continue-ability - state :corp - {:async true - :prompt (msg "Select up to " (dec (count (:hand corp))) " cards for the first pile") - :choices {:req #(and (in-hand? %) (corp? %)) - :max (req (dec (count (:hand corp))))} - :effect (effect (clear-wait-prompt :runner) - (show-wait-prompt :corp "Runner to select a pile") - (continue-ability - :runner - (which-pile (shuffle targets) - (shuffle (vec (clojure.set/difference - (set (:hand corp)) (set targets))))) - card nil))} - card nil)) - (effect-completed state side eid)))}] - {:req (req hq-runnable) - :effect (effect (make-run :hq {:req (req (= target :hq)) - :replace-access access-effect} - card))}))) - -(define-card "Inject" - {:effect (req (doseq [c (take 4 (get-in @state [:runner :deck]))] - (if (program? c) - (do (trash state side c {:unpreventable true}) - (gain-credits state side 1) - (system-msg state side (str "trashes " (:title c) " and gains 1 [Credits]"))) - (do (move state side c :hand) - (system-msg state side (str "adds " (:title c) " to Grip"))))))}) - -(define-card "Injection Attack" - (run-event - {:async true} - nil - nil - (effect (continue-ability - {:prompt "Select an icebreaker" - :choices {:req #(and (installed? %) (has-subtype? % "Icebreaker"))} - :effect (effect (pump target 2 :all-run))} - card nil)))) - -(define-card "Inside Job" - {:implementation "Bypass is manual" - :prompt "Choose a server" - :choices (req runnable-servers) - :effect (effect (make-run target nil card))}) - -(define-card "Insight" - {:async true - :effect (req + :req (req (>= (count (:discard corp)) 5)) + :effect (req (show-wait-prompt + state :runner + "Corp to choose which cards to pick up from Archives") ;; For some reason it just shows successful-run-trigger-message, but this works!? + (continue-ability state side + corp-choose + card nil))}] + {:req (req archives-runnable) + :makes-run true + :effect (effect (make-run :archives + {:req (req (= target :archives)) + :replace-access access-effect} + card))}) + + "Government Investigations" + {:flags {:prevent-secretly-spend (req 2)}} + + "Guinea Pig" + {:msg "trash all cards in the grip and gain 10 [Credits]" + :effect (req (doseq [c (:hand runner)] + (trash state :runner c {:unpreventable true})) + (gain-credits state :runner 10))} + + "Hacktivist Meeting" + {:events {:pre-rez-cost {:req (req (not (ice? target))) + :effect (effect (rez-additional-cost-bonus [:randomly-trash-from-hand 1]))}}} + + "High-Stakes Job" + (run-event + {:choices (req (let [unrezzed-ice #(seq (filter (complement rezzed?) (:ices (second %)))) + bad-zones (keys (filter (complement unrezzed-ice) (get-in @state [:corp :servers])))] + (zones->sorted-names (remove (set bad-zones) (get-runnable-zones state)))))} + {:end-run {:req (req (:successful run)) + :msg "gain 12 [Credits]" + :effect (effect (gain-credits :runner 12))}}) + + "Hostage" + {:prompt "Choose a Connection" + :choices (req (cancellable (filter #(has-subtype? % "Connection") (:deck runner)) :sorted)) + :msg (msg "add " (:title target) " to their Grip and shuffle their Stack") + :effect (effect (trigger-event :searched-stack nil) + (continue-ability + (let [connection target] + {:optional + {:prompt (str "Install " (:title connection) "?") + :yes-ability {:effect (effect (runner-install connection) + (shuffle! :deck))} + :no-ability {:effect (effect (move connection :hand) + (shuffle! :deck))}}}) + card nil))} + + "Hot Pursuit" + {:req (req hq-runnable) + :makes-run true + :effect (effect (make-run :hq {:req (req (= target :hq)) + :successful-run {:async true + :msg "gain 9 [Credits] and take 1 tag" + :effect (req (wait-for (gain-tags state :runner 1) + (gain-credits state :runner 9) + (effect-completed state side eid)))}} card))} + + "Isolation" + {:additional-cost [:resource 1] + :msg "gain 7 [Credits]" + :effect (effect (gain-credits 7))} + + "I've Had Worse" + {:async true + :effect (effect (draw eid 3 nil)) + :trash-effect {:when-inactive true + :async true + :req (req (#{:meat :net} target)) + :effect (effect (draw :runner eid 3 nil)) :msg "draw 3 cards"}} + + "Immolation Script" + {:req (req archives-runnable) + :effect (effect (make-run :archives nil card) + (register-events (:events (card-def card)) (assoc card :zone '(:discard)))) + :events {:pre-access + {:async true + :req (req (and (= target :archives) + ;; don't prompt unless there's at least 1 rezzed ICE matching one in Archives + (not-empty (clojure.set/intersection + (into #{} (map :title (filter ice? (:discard corp)))) + (into #{} (map :title (filter rezzed? (all-installed state :corp)))))))) + :effect (req (continue-ability + state side + {:async true + :prompt "Choose a piece of ICE in Archives" + :choices (req (filter ice? (:discard corp))) + :effect (req (let [icename (:title target)] + (continue-ability + state side + {:async true + :prompt (msg "Select a rezzed copy of " icename " to trash") + :choices {:req #(and (ice? %) + (rezzed? %) + (= (:title %) icename))} + :msg (msg "trash " (card-str state target)) + :effect (req (trash state :corp target) + (unregister-events state side card) + (effect-completed state side eid))} + card nil)))} + card nil))}}} + + "In the Groove" + {:effect (req (register-events state side (:events (card-def card)) + (dissoc card :zone))) + :events {:runner-turn-ends {:effect (effect (unregister-events card))} + :runner-install {:req (req (<= 1 (:cost target))) + :interactive (req (has-subtype? target "Cybernetic")) + :async true + :prompt "What to get from In the Groove?" + :choices ["Draw 1 card" "Gain 1 [Credits]"] + :effect (req (if (= target "Draw 1 card") + (draw state side eid 1 nil) + (do (gain-credits state side 1) + (effect-completed state side eid)))) + :msg (msg (lower-case target))}}} + + "Independent Thinking" + (letfn [(cards-to-draw [targets] + (* (count targets) + (if (some #(and (not (facedown? %)) (has-subtype? % "Directive")) targets) 2 1)))] + {:async true + :prompt "Choose up to 5 installed cards to trash with Independent Thinking" + :choices {:max 5 + :req #(and (installed? %) + (runner? %))} + :effect (req (wait-for (trash-cards state side targets nil) + (draw state :runner eid (cards-to-draw targets) nil))) + :msg (msg "trash " (join ", " (map :title targets)) " and draw " (quantify (cards-to-draw targets) "card"))}) + + "Indexing" + {:req (req rd-runnable) + :async true + :effect (effect (make-run + :rd + {:req (req (= target :rd)) + :replace-access + {:msg "rearrange the top 5 cards of R&D" + :async true + :effect (req (show-wait-prompt state :corp "Runner to rearrange the top cards of R&D") + (let [from (take 5 (:deck corp))] + (if (pos? (count from)) + (continue-ability state side (reorder-choice :corp :corp from '() + (count from) from) card nil) + (do (clear-wait-prompt state :corp) + (effect-completed state side eid)))))}} + card))} + + "Infiltration" + {:prompt "Gain 2 [Credits] or expose a card?" + :choices ["Gain 2 [Credits]" "Expose a card"] + :effect (effect (continue-ability + (if (= target "Expose a card") + {:choices {:req installed?} + :async true + :effect (effect (expose eid target))} + {:msg "gain 2 [Credits]" + :effect (effect (gain-credits 2))}) + card nil))} + + "Information Sifting" + (letfn [(access-pile [cards pile pile-size] + {:prompt "Choose a card to access. You must access all cards." + :choices [(str "Card from pile " pile)] + :async true + :req (req (if (:max-access run) + (< (total-cards-accessed run) (:max-access run)) + true)) + :effect (req (wait-for + (access-card state side (first cards)) + (if (< 1 (count cards)) + (continue-ability state side (access-pile (next cards) pile pile-size) card nil) + (effect-completed state side eid))))}) + (which-pile [p1 p2] + {:prompt "Choose a pile to access" + :choices [(str "Pile 1 (" (quantify (count p1) "card") ")") + (str "Pile 2 (" (quantify (count p2) "card") ")")] + :async true + :effect (req (let [choice (if (starts-with? target "Pile 1") 1 2)] + (clear-wait-prompt state :corp) + (system-msg state side (str "chooses to access " target)) + (continue-ability + state side + (access-pile (if (= 1 choice) p1 p2) choice (count (if (= 1 choice) p1 p2))) + card nil)))})] + (let [access-effect + {:async true + :mandatory true + :effect (req (if (< 1 (count (:hand corp))) + (do (show-wait-prompt state :runner "Corp to create two piles") + (continue-ability + state :corp + {:async true + :prompt (msg "Select up to " (dec (count (:hand corp))) " cards for the first pile") + :choices {:req #(and (in-hand? %) (corp? %)) + :max (req (dec (count (:hand corp))))} + :effect (effect (clear-wait-prompt :runner) + (show-wait-prompt :corp "Runner to select a pile") + (continue-ability + :runner + (which-pile (shuffle targets) + (shuffle (vec (clojure.set/difference + (set (:hand corp)) (set targets))))) + card nil))} + card nil)) + (effect-completed state side eid)))}] + {:req (req hq-runnable) + :effect (effect (make-run :hq {:req (req (= target :hq)) + :replace-access access-effect} + card))})) + + "Inject" + {:effect (req (doseq [c (take 4 (get-in @state [:runner :deck]))] + (if (program? c) + (do (trash state side c {:unpreventable true}) + (gain-credits state side 1) + (system-msg state side (str "trashes " (:title c) " and gains 1 [Credits]"))) + (do (move state side c :hand) + (system-msg state side (str "adds " (:title c) " to Grip"))))))} + + "Injection Attack" + (run-event + {:async true} + nil + nil + (effect (continue-ability + {:prompt "Select an icebreaker" + :choices {:req #(and (installed? %) (has-subtype? % "Icebreaker"))} + :effect (effect (pump target 2 :all-run))} + card nil))) + + "Inside Job" + {:implementation "Bypass is manual" + :prompt "Choose a server" + :choices (req runnable-servers) + :effect (effect (make-run target nil card))} + + "Insight" + {:async true + :effect (req (let [from (take 4 (:deck corp))] (when (pos? (count from)) (show-wait-prompt state :runner (str "Corp to rearrange the top " (count from) " cards of R&D")) @@ -1259,1193 +1260,1193 @@ (let [top-4 (take 4 (get-in @state [:corp :deck]))] (reveal state side top-4) (system-msg state :runner (str " reveals (top:) " (join ", " (map :title top-4)) " from the top of R&D"))) - (effect-completed state side eid)))))}) - -(define-card "Interdiction" - (let [ab (effect (register-turn-flag! - card :can-rez - (fn [state side card] - (if (and (= (:active-player @state) :runner) (not (ice? card))) - ((constantly false) - (toast state :corp "Cannot rez non-ICE on the Runner's turn due to Interdiction")) - true))))] - {:msg "prevent the Corp from rezzing non-ICE cards on the Runner's turn" - :effect ab - :events {:runner-turn-begins {:effect ab}} - :leave-play (req (clear-all-flags-for-card! state side card))})) - -(define-card "Itinerant Protesters" - {:msg "reduce the Corp's maximum hand size by 1 for each bad publicity" - :effect (req (change-hand-size state :corp (- (count-bad-pub state))) - (add-watch state :itin - (fn [k ref old new] - (let [bpnew (count-bad-pub new) - bpold (count-bad-pub old) - bpchange (- bpnew bpold)] - (when-not (zero? bpchange) - (change-hand-size state :corp (- bpchange))))))) - :leave-play (req (remove-watch state :itin) - (change-hand-size state :corp (count-bad-pub state)))}) - -(define-card "Khusyuk" - (let [access-revealed (fn [revealed] - {:async true - :mandatory true - :prompt "Which of the revealed cards would you like to access (first card is on top)?" - :choices revealed - :req (req (not= (:max-access run) 0)) - :effect (effect (access-card eid target))}) - select-install-cost (fn [state] - (let [current-values - (->> (all-active-installed state :runner) - (map :cost) - (filter identity) - (remove zero?) - frequencies - (merge {1 0}) - (into (sorted-map)))] - {:async true - :prompt "Select an install cost from among your installed cards." - ;; We don't want to generate 99 prompt buttons, so only add 99 at the end - :choices (mapv str (for [x (->> current-values keys last inc (range 1) (#(concat % [99])))] - (str x " [Credit]: " - (quantify (get current-values x 0) "card")))) - :effect (effect (effect-completed - (make-result eid [(str->int (first (split target #" "))) - (min 6 (str->int (nth (split target #" ") 2)))])))}))] - {:req (req rd-runnable) - :async true - :effect (req - (make-run - state side - :rd - {:req (req (= target :rd)) - :async true - :replace-access - {:async true - :effect (req - (wait-for - (resolve-ability state side (select-install-cost state) card nil) - (let [revealed (seq (take (second async-result) (:deck corp)))] - (system-msg state :runner (str "uses Khusyuk to choose an install cost of " - (first async-result) - " [Credit] and reveals " - (if revealed - (str "(top:) " (join ", " (map :title revealed)) - " from the top of R&D") - "no cards"))) - (if revealed - (do (reveal state side revealed) - (wait-for - (resolve-ability state side (access-revealed revealed) card nil) - (shuffle! state :corp :deck) - (system-msg state :runner " shuffles R&D") - (effect-completed state side eid))) - (do (shuffle! state :corp :deck) - (system-msg state :runner " shuffles R&D") - (effect-completed state side eid))))))}} - card))})) - -(define-card "Knifed" - (cutlery "Barrier")) - -(define-card "Kraken" - {:req (req (:stole-agenda runner-reg)) :prompt "Choose a server" :choices (req servers) - :msg (msg "force the Corp to trash an ICE protecting " target) - :effect (req (let [serv (next (server->zone state target)) - servname target] - (resolve-ability - state :corp - {:prompt (msg "Select a piece of ICE in " target " to trash") - :choices {:req #(and (= (last (:zone %)) :ices) - (= serv (rest (butlast (:zone %)))))} - :effect (req (trash state :corp target) - (system-msg state side (str "trashes " - (card-str state target))))} - card nil)))}) - -(define-card "Labor Rights" - {:req (req (pos? (+ (count (:deck runner)) (count (:discard runner))))) - :effect (req (let [mill-count (min 3 (count (:deck runner)))] - (mill state :runner :runner mill-count) - (system-msg state :runner (str "trashes the top " (quantify mill-count "card") " of their Stack")) - (let [heap-count (min 3 (count (get-in @state [:runner :discard])))] - (continue-ability - state side - {:prompt (str "Choose " (quantify heap-count "card") " to shuffle into the stack") - :show-discard true - :async true - :choices {:max heap-count - :all true - :not-self true - :req #(and (runner? %) - (in-discard? %))} - :effect (req (doseq [c targets] - (move state side c :deck)) - (system-msg state :runner (str "shuffles " (join ", " (map :title targets)) - " from their Heap into their Stack, and draws 1 card")) - (shuffle! state :runner :deck) - (wait-for (draw state :runner 1 nil) - (move state side (find-latest state card) :rfg) - (system-msg state :runner "removes Labor Rights from the game") - (effect-completed state side eid)))} - card nil))))}) - -(define-card "Lawyer Up" - {:msg "remove 2 tags and draw 3 cards" - :async true - :effect (req (wait-for (draw state side 3 nil) (lose-tags state side eid 2)))}) - -(define-card "Lean and Mean" - {:prompt "Choose a server" - :choices (req runnable-servers) - :async true - :msg (msg "make a run on " target (when (< (count (filter program? (all-active-installed state :runner))) 4) - ", adding +2 strength to all icebreakers")) - :effect (req (when (< (count (filter program? (all-active-installed state :runner))) 4) - (doseq [c (filter #(has-subtype? % "Icebreaker") (all-active-installed state :runner))] - (pump state side c 2 :all-run))) - (make-run state side (make-eid state) target nil card))}) - -(define-card "Leave No Trace" - (letfn [(get-rezzed-cids [ice] - (map :cid (filter #(and (rezzed? %) - (ice? %)) - ice)))] - {:prompt "Choose a server" - :msg "make a run and derez any ICE that are rezzed during this run" - :choices (req runnable-servers) - :async true - :effect (req - (let [old-ice-cids (get-rezzed-cids (all-installed state :corp))] - (swap! state assoc :lnt old-ice-cids) - (register-events state side (:events (card-def card)) (assoc card :zone '(:discard))) - (make-run state side (make-eid state) target nil card))) - :events {:run-ends {:effect (req (let [new (set (get-rezzed-cids (all-installed state :corp))) - old (set (:lnt @state)) - diff-cid (seq (clojure.set/difference new old)) - diff (map #(find-cid % (all-installed state :corp)) diff-cid)] - (doseq [ice diff] - (derez state :runner ice)) - (when-not (empty? diff) - (system-msg state side (str "derezzes " (join ", " (map :title diff)) " via Leave No Trace"))) - (swap! state dissoc :lnt) - (unregister-events state side card)))}}})) - -(define-card "Legwork" - {:req (req hq-runnable) - :effect (effect (make-run :hq nil card) - (register-events (:events (card-def card)) - (assoc card :zone '(:discard)))) - :events {:successful-run {:silent (req true) - :effect (effect (access-bonus :hq 2))} - :run-ends {:effect (effect (unregister-events card))}}}) - -(define-card "Leverage" - {:req (req (some #{:hq} (:successful-run runner-reg))) - :player :corp - :prompt "Take 2 bad publicity?" - :yes-ability {:player :corp - :msg "takes 2 bad publicity" - :effect (effect (gain-bad-publicity :corp 2))} - :no-ability {:player :runner - :msg "is immune to damage until the beginning of the Runner's next turn" - :effect (effect - (register-events - {:pre-damage {:effect (effect (damage-prevent :net Integer/MAX_VALUE) - (damage-prevent :meat Integer/MAX_VALUE) - (damage-prevent :brain Integer/MAX_VALUE))} - :runner-turn-begins {:effect (effect (unregister-events - card - {:events {:runner-turn-begins nil - :pre-damage nil}}))}} - (assoc card :zone '(:discard))))}}) - -(define-card "Levy AR Lab Access" - {:msg "shuffle their Grip and Heap into their Stack and draw 5 cards" - :async true - :effect (req (shuffle-into-deck state :runner :hand :discard) - (wait-for (draw state :runner 5 nil) - (move state side (first (:play-area runner)) :rfg) - (effect-completed state side eid)))}) - -(define-card "Lucky Find" - {:msg "gain 9 [Credits]" - :effect (effect (gain-credits 9))}) - -(define-card "Mad Dash" - {:prompt "Choose a server" - :choices (req runnable-servers) - :async true - :effect (effect (make-run target nil card) - (register-events (:events (card-def card)) (assoc card :zone '(:discard)))) - :events {:agenda-stolen {:silent (req true) - :effect (effect (update! (assoc card :steal true)))} - :run-ends {:async true - :effect (req (if (:steal card) - (wait-for (as-agenda state :runner (get-card state card) 1) - (system-msg state :runner - (str "adds Mad Dash to their score area as an agenda worth 1 agenda point"))) - (do (system-msg state :runner - (str "suffers 1 meat damage from Mad Dash")) - (damage state side eid :meat 1 {:card card}))) - (unregister-events state side card))}}}) - -(define-card "Making an Entrance" - (letfn [(entrance-trash [cards] - {:prompt "Choose a card to trash" - :choices (cons "None" cards) - :async true - :msg (req (when (not= target "None") (str "trash " (:title target)))) - :effect (req (if (= target "None") - (if (not-empty cards) - (continue-ability state side (reorder-choice :runner :corp cards '() - (count cards) cards) card nil) - (do (clear-wait-prompt state :corp) - (effect-completed state side eid))) - (do (trash state side target {:unpreventable true}) - (continue-ability state side (entrance-trash (remove-once #(= % target) cards)) - card nil))))})] - {:msg "look at and trash or rearrange the top 6 cards of their Stack" - :async true - :effect (req (show-wait-prompt state :corp "Runner to rearrange the top cards of their stack") - (let [from (take 6 (:deck runner))] - (continue-ability state side (entrance-trash from) card nil)))})) - -(define-card "Marathon" - (run-event - {:choices (req (filter #(can-run-server? state %) remotes))} - {:end-run {:effect (req (prevent-run-on-server state card (:server run)) - (when (:successful run) - (system-msg state :runner "gains 1 [Click] and adds Marathon to their grip") - (gain state :runner :click 1) - (move state :runner (assoc card :zone [:discard]) :hand)))}})) - -(define-card "Mars for Martians" - (letfn [(count-clan [state] (count (filter #(and (has-subtype? % "Clan") (resource? %)) - (all-active-installed state :runner))))] - {:msg (msg "draw " (count-clan state) " cards and gain " (count-tags state) " [Credits]") - :async true - :effect (req (wait-for (draw state side (count-clan state) nil) - (gain-credits state side (count-tags state)) - (effect-completed state side eid)))})) - -(define-card "Mass Install" - (let [mhelper (fn mi [n] {:prompt "Select a program to install" - :choices {:req #(and (program? %) - (in-hand? %))} - :effect (req (runner-install state side target) - (when (< n 3) - (resolve-ability state side (mi (inc n)) card nil)))})] - {:effect (effect (resolve-ability (mhelper 1) card nil))})) - -(define-card "Mining Accident" - {:req (req (some #{:hq :rd :archives} (:successful-run runner-reg))) - :async true - :msg "make the Corp pay 5 [Credits] or take 1 bad publicity" - :effect (effect (move (first (:play-area runner)) :rfg) - (show-wait-prompt :runner "Corp to choose to pay or take bad publicity") - (continue-ability - {:player :corp - :async true - :prompt "Pay 5 [Credits] or take 1 Bad Publicity?" - :choices (concat (when (can-pay? state :corp eid card "Mining Accident" :credit 5) - ["Pay 5 [Credits]"]) - ["Take 1 Bad Publicity"]) - :effect (req (clear-wait-prompt state :runner) - (if (= target "Pay 5 [Credits]") - (do (lose-credits state :corp 5) - (system-msg state side "pays 5 [Credits] from Mining Accident") - (effect-completed state side eid)) - (do (gain-bad-publicity state :corp 1) - (system-msg state side "takes 1 bad publicity from Mining Accident") - (effect-completed state side eid))))} - card nil))}) - -(define-card "Möbius" - {:req (req rd-runnable) - :async true - :effect (req (let [mob-eid (make-eid state) - events (:events (card-def card))] - (register-events state side - (assoc-in events [:successful-run-ends :eid] mob-eid) - (assoc card :zone '(:discard))) - (wait-for (make-run state side mob-eid :rd nil card) - (let [card (get-card state (assoc card :zone '(:discard)))] - (unregister-events state side card) - (when (:run-again card) - (update! state side (dissoc card :run-again)) - (register-events state side {:successful-run - {:req (req (= target :rd)) - :msg "gain 4 [Credits]" - :effect (effect (gain-credits 4))}} - card) - (wait-for (make-run state side (make-eid state mob-eid) :rd nil card) - (unregister-events state side card))))))) - :events {:successful-run nil - :successful-run-ends {:interactive (req true) - :optional {:req (req (= [:rd] (:server target))) - :prompt "Make another run on R&D?" - :yes-ability {:effect (effect (clear-wait-prompt :corp) - (update! (assoc card :run-again true)))}}}}}) - -(define-card "Modded" - {:prompt "Select a program or piece of hardware to install from your Grip" - :choices {:req #(and (or (hardware? %) - (program? %)) - (in-hand? %))} - :effect (effect (install-cost-bonus [:credit -3]) (runner-install target))}) - -(define-card "Net Celebrity" - {:recurring 1 - :interactions {:pay-credits {:req (req (:run @state)) - :type :recurring}}}) - -(define-card "Networking" - {:msg "remove 1 tag" - :effect (effect (lose-tags 1)) - :optional {:prompt "Pay 1 [Credits] to add Networking to Grip?" - :yes-ability {:cost [:credit 1] - :msg "add it to their Grip" - :effect (effect (move (last (:discard runner)) :hand))}}}) - -(define-card "Notoriety" - {:req (req (and (some #{:hq} (:successful-run runner-reg)) - (some #{:rd} (:successful-run runner-reg)) - (some #{:archives} (:successful-run runner-reg)))) - :async true - :effect (req (as-agenda state :runner eid (first (:play-area runner)) 1)) - :msg "add it to their score area as an agenda worth 1 agenda point"}) - -(define-card "Office Supplies" - {:play-cost-bonus (req [:credit (- (:link runner 0))]) - :prompt "Gain 4 [Credits] or draw 4 cards?" - :choices ["Gain 4 [Credits]" "Draw 4 cards"] - :async true - :msg (msg (if (= target "Gain 4 [Credits]") - "gain 4 [Credits]" - "draw 4 cards")) - :effect (req (if (= target "Gain 4 [Credits]") - (do (gain-credits state :runner 4) - (effect-completed state side eid)) - (draw state :runner eid 4 nil)))}) - -(define-card "On the Lam" - {:req (req (some resource? (all-active-installed state :runner))) - :prompt "Choose a resource to host On the Lam" - :choices {:req #(and (resource? %) - (installed? %))} - :effect (effect (host target (assoc card :zone [:discard] :installed true)) - (card-init (find-latest state card) {:resolve-effect false}) - (system-msg (str "hosts On the Lam on " (:title target)))) - :interactions {:prevent [{:type #{:net :brain :meat :tag} - :req (req true)}]} - :abilities [{:label "[Trash]: Avoid 3 tags" - :msg "avoid up to 3 tags" - :effect (effect (tag-prevent :runner 3) - (trash card {:cause :ability-cost}))} - {:label "[Trash]: Prevent up to 3 damage" - :msg "prevent up to 3 damage" - :effect (effect (damage-prevent :net 3) - (damage-prevent :meat 3) - (damage-prevent :brain 3) - (trash card {:cause :ability-cost}))}]}) - -(define-card "Out of the Ashes" - (let [ashes-run {:prompt "Choose a server" - :choices (req runnable-servers) - :async true - :effect (effect (make-run eid target nil card))} - ashes-recur (fn ashes-recur [n] - {:optional - {:prompt "Remove Out of the Ashes from the game to make a run?" - :yes-ability - {:msg "removes Out of the Ashes from the game to make a run" - :effect - (req (let [card (some #(when (= "Out of the Ashes" (:title %)) %) (:discard runner))] - (move state side card :rfg) - (unregister-events state side card) - (wait-for (resolve-ability state side ashes-run card nil) - (if (< 1 n) - (continue-ability state side (ashes-recur (dec n)) card nil) - (effect-completed state side eid)))))}}}) - ashes-flag {:runner-phase-12 - {:priority -1 - :once :per-turn - :once-key :out-of-ashes - :effect (effect (continue-ability - (ashes-recur (count (filter #(= "Out of the Ashes" (:title %)) - (:discard runner)))) - card nil))}}] - (run-event - {:move-zone (req (if (= [:discard] (:zone card)) - (register-events state side ashes-flag (assoc card :zone [:discard])) - (unregister-events state side card {:events {:runner-phase-12 nil}})))} - nil))) - -(define-card "Paper Tripping" - {:msg "remove all tags" :effect (effect (lose-tags :all))}) - -(define-card "Peace in Our Time" - {:req (req (not (:scored-agenda corp-reg))) - :msg "gain 10 [Credits]. The Corp gains 5 [Credits]" - :effect (req (gain-credits state :runner 10) - (gain-credits state :corp 5) - (apply prevent-run-on-server - state card (get-zones state)) - (register-events state side - {:runner-turn-ends {:effect (req (apply enable-run-on-server state card (get-zones state)))}} - (assoc card :zone '(:discard)))) - :events {:runner-turn-ends nil}}) - -(define-card "Planned Assault" - {:msg (msg "play " (:title target)) - :choices (req (cancellable (filter #(and (has-subtype? % "Run") - (<= (:cost %) (:credit runner))) (:deck runner)) :sorted)) - :prompt "Choose a Run event" :effect (effect (trigger-event :searched-stack nil) - (shuffle! :deck) - (play-instant target {:no-additional-cost true}))}) - -(define-card "Political Graffiti" - (let [update-agenda-points (fn [state side target amount] - (set-prop state side (get-card state target) :agendapoints (+ amount (:agendapoints (get-card state target)))) - (gain-agenda-point state side amount))] - {:req (req archives-runnable) - :events {:purge {:effect (effect (trash card {:cause :purge}))}} - :trash-effect {:effect (req (let [current-side (get-scoring-owner state {:cid (:agenda-cid card)})] - (update-agenda-points state current-side (find-cid (:agenda-cid card) (get-in @state [current-side :scored])) 1)))} - :effect (effect (make-run - :archives - {:req (req (= target :archives)) - :replace-access - {:prompt "Select an agenda to host Political Graffiti" - :choices {:req #(in-corp-scored? state side %)} - :msg (msg "host Political Graffiti on " (:title target) " as a hosted condition counter") - :effect (req (host state :runner (get-card state target) - ; keep host cid in :agenda-cid because `trash` will clear :host - (assoc card :zone [:discard] :installed true :agenda-cid (:cid (get-card state target)))) - (update-agenda-points state :corp target -1))}} card))})) - -(define-card "Populist Rally" - {:req (req (seq (filter #(has-subtype? % "Seedy") (all-active-installed state :runner)))) - :msg "give the Corp 1 fewer [Click] to spend on their next turn" - :effect (effect (lose :corp :click-per-turn 1) - (register-events (:events (card-def card)) - (assoc card :zone '(:discard)))) - :events {:corp-turn-ends {:effect (effect (gain :corp :click-per-turn 1) - (unregister-events card))}}}) - -(define-card "Power Nap" - {:effect (effect (gain-credits (+ 2 (count (filter #(has-subtype? % "Double") - (:discard runner)))))) - :msg (msg "gain " (+ 2 (count (filter #(has-subtype? % "Double") (:discard runner)))) " [Credits]")}) - -(define-card "Power to the People" - {:effect (effect (register-events - {:pre-steal-cost {:once :per-turn - :effect (effect (gain-credits 7)) - :msg "gain 7 [Credits]"} - :runner-turn-ends {:effect (effect (unregister-events card))}} - (assoc card :zone '(:discard)))) - :events {:pre-steal-cost nil - :runner-turn-ends nil}}) - -(define-card "Prey" - (run-event {:implementation "Ice trash is manual"} nil)) - -(define-card "Process Automation" - {:msg "gain 2 [Credits] and draw 1 card" - :async true - :effect (effect (gain-credits 2) - (draw eid 1 nil))}) - -(define-card "Push Your Luck" - (letfn [(runner-choice [choices] - {:prompt "Spend how many credits?" - :choices choices - :async true - :effect (effect (show-wait-prompt :runner "Corp to guess even or odd") - (clear-wait-prompt :corp) - (continue-ability :corp (corp-choice (str->int target)) card nil))}) - (corp-choice [spent] - {:prompt "Guess how many credits were spent" - :choices ["Even" "Odd"] - :async true - :effect (req (let [correct-guess ((if (= target "Even") even? odd?) spent)] - (clear-wait-prompt state :runner) - (deduct state :runner [:credit spent]) - (system-msg state :runner (str "spends " spent " [Credit]")) - (system-msg state :corp (str (if correct-guess " " " in") - "correctly guesses " (lower-case target))) - (wait-for (trigger-event-simult state side :reveal-spent-credits nil nil spent) - (when-not correct-guess - (system-msg state :runner (str "gains " (* 2 spent) " [Credits]")) - (gain-credits state :runner (* 2 spent))) - (effect-completed state side eid))))})] - {:async true - :effect (req (show-wait-prompt state :corp "Runner to spend credits") - (let [all-amounts (range (inc (get-in @state [:runner :credit]))) - valid-amounts (remove #(or (any-flag-fn? state :corp :prevent-secretly-spend %) - (any-flag-fn? state :runner :prevent-secretly-spend %)) - all-amounts) - choices (map str valid-amounts)] - (continue-ability state side (runner-choice choices) card nil)))})) - -(define-card "Pushing the Envelope" - (letfn [(hsize [s] (count (get-in s [:runner :hand])))] - {:msg (msg (if (<= (hsize @state) 2) - "make a run, and adds +2 strength to installed icebreakers" - "make a run")) - :prompt "Choose a server" - :choices (req runnable-servers) - :async true - :effect (req (when (<= (hsize @state) 2) - (let [breakers (filter #(has-subtype? % "Icebreaker") (all-active-installed state :runner))] - (doseq [t breakers] (pump state side t 2 :all-run)))) - (make-run state side (make-eid state) target))})) - -(define-card "Quality Time" - {:msg "draw 5 cards" :async true :effect (effect (draw eid 5 nil))}) - -(define-card "Queen's Gambit" - {:choices ["0", "1", "2", "3"] - :prompt "How many advancement tokens?" - :effect (req (let [c (str->int target)] - (resolve-ability - state side - {:choices {:req #(and (is-remote? (second (:zone %))) - (= (last (:zone %)) :content) - (not (:rezzed %)))} - :msg (msg "add " c " advancement tokens on a card and gain " (* 2 c) " [Credits]") - :effect (effect (gain-credits (* 2 c)) - (add-prop :corp target :advance-counter c {:placed true}) - (register-turn-flag! card :can-access - ;; prevent access of advanced card - (fn [_ _ card] (not (same-card? target card)))))} - card nil)))}) - -(define-card "Quest Completed" - {:req (req (and (some #{:hq} (:successful-run runner-reg)) - (some #{:rd} (:successful-run runner-reg)) - (some #{:archives} (:successful-run runner-reg)))) - :choices {:req installed?} :msg (msg "access " (:title target)) - :effect (effect (access-card target))}) - -(define-card "Rebirth" - {:msg "change identities" - :prompt "Choose an identity to become" - :choices (req (let [is-draft-id? #(.startsWith (:code %) "00") - runner-identity (:identity runner) - is-swappable #(and (= "Identity" (:type %)) - (= (:faction runner-identity) (:faction %)) - (not (is-draft-id? %)) - (not= (:title runner-identity) (:title %))) - swappable-ids (filter is-swappable (server-cards))] - (cancellable swappable-ids :sorted))) - :effect (req (let [old-runner-identity (:identity runner)] - ;; Handle hosted cards (Ayla) - Part 1 - (doseq [c (:hosted old-runner-identity)] - (move state side c :temp-hosted)) - (move state side (last (:discard runner)) :rfg) - (disable-identity state side) - ;; Manually reduce the runner's link by old link - (lose state :runner :link (:baselink old-runner-identity)) - ;; Move the selected ID to [:runner :identity] and set the zone - (let [new-id (-> target :title server-card make-card (assoc :zone [:identity])) - num-old-blanks (:num-disabled old-runner-identity)] - (swap! state assoc-in [side :identity] new-id) - ;; enable-identity does not do everything that init-identity does - (init-identity state side new-id) - (when num-old-blanks - (dotimes [_ num-old-blanks] - (disable-identity state side))))) - ;; Handle hosted cards (Ayla) - Part 2 - (doseq [c (get-in @state [:runner :temp-hosted])] - ;; Currently assumes all hosted cards are hosted facedown (Ayla) - (host state side (get-in @state [:runner :identity]) c {:facedown true})))}) - -(define-card "Reboot" - (letfn [(install-cards [state side eid card to-install titles] - (if-let [f (first to-install)] - (wait-for (runner-install state :runner f {:facedown true :no-msg true}) - (install-cards state side eid card (rest to-install) titles)) - (do - (move state side (find-latest state card) :rfg) - (system-msg state :runner (str "uses Reboot to install " (join ", " titles) " facedown")) - (effect-completed state side eid))))] - {:req (req archives-runnable) - :makes-run true - :effect (effect - (make-run - :archives - {:req (req (= target :archives)) - :replace-access - {:prompt "Choose up to five cards to install" - :show-discard true - :choices {:max 5 - :req #(and (in-discard? %) (runner? %) (not (same-card? % card)))} - :mandatory true + (effect-completed state side eid)))))} + + "Interdiction" + (let [ab (effect (register-turn-flag! + card :can-rez + (fn [state side card] + (if (and (= (:active-player @state) :runner) (not (ice? card))) + ((constantly false) + (toast state :corp "Cannot rez non-ICE on the Runner's turn due to Interdiction")) + true))))] + {:msg "prevent the Corp from rezzing non-ICE cards on the Runner's turn" + :effect ab + :events {:runner-turn-begins {:effect ab}} + :leave-play (req (clear-all-flags-for-card! state side card))}) + + "Itinerant Protesters" + {:msg "reduce the Corp's maximum hand size by 1 for each bad publicity" + :effect (req (change-hand-size state :corp (- (count-bad-pub state))) + (add-watch state :itin + (fn [k ref old new] + (let [bpnew (count-bad-pub new) + bpold (count-bad-pub old) + bpchange (- bpnew bpold)] + (when-not (zero? bpchange) + (change-hand-size state :corp (- bpchange))))))) + :leave-play (req (remove-watch state :itin) + (change-hand-size state :corp (count-bad-pub state)))} + + "Khusyuk" + (let [access-revealed (fn [revealed] + {:async true + :mandatory true + :prompt "Which of the revealed cards would you like to access (first card is on top)?" + :choices revealed + :req (req (not= (:max-access run) 0)) + :effect (effect (access-card eid target))}) + select-install-cost (fn [state] + (let [current-values + (->> (all-active-installed state :runner) + (map :cost) + (filter identity) + (remove zero?) + frequencies + (merge {1 0}) + (into (sorted-map)))] + {:async true + :prompt "Select an install cost from among your installed cards." + ;; We don't want to generate 99 prompt buttons, so only add 99 at the end + :choices (mapv str (for [x (->> current-values keys last inc (range 1) (#(concat % [99])))] + (str x " [Credit]: " + (quantify (get current-values x 0) "card")))) + :effect (effect (effect-completed + (make-result eid [(str->int (first (split target #" "))) + (min 6 (str->int (nth (split target #" ") 2)))])))}))] + {:req (req rd-runnable) + :async true + :effect (req + (make-run + state side + :rd + {:req (req (= target :rd)) :async true - :cancel-effect (req (move state side (find-latest state card) :rfg) - (effect-completed state side eid)) - :effect (req (install-cards state side eid card targets (map :title targets)))}} - card))})) - -(define-card "Recon" - (run-event)) - -(define-card "Rejig" - (let [valid-target? (fn [card] (and (runner? card) - (or (program? card) - (hardware? card)))) - pick-up {:async true - :prompt "Select a program or piece of hardware to add to your Grip" - :choices {:req #(and (valid-target? %) - (installed? %))} - :effect (req (move state side target :hand) - (effect-completed state side (make-result eid (:cost target))))} - put-down (fn [st si eid card bonus] + :replace-access {:async true - :prompt "Select a program or piece of hardware to install" - :choices {:req #(and (valid-target? %) - (can-pay? st si eid card nil (modified-install-cost st si % [:credit (- bonus)])))} - :effect (effect (install-cost-bonus [:credit (- bonus)]) - (runner-install eid target nil))})] - {:req (req (some valid-target? (all-installed state :runner))) - :effect (req (wait-for (resolve-ability state side pick-up card nil) - (continue-ability state side - (put-down state side eid card async-result) - card nil)))})) - -(define-card "Reshape" - {:prompt "Select two non-rezzed ICE to swap positions" - :choices {:req #(and (installed? %) (not (rezzed? %)) (ice? %)) :max 2} - :msg (msg "swap the positions of " (card-str state (first targets)) " and " (card-str state (second targets))) - :effect (req (when (= (count targets) 2) - (swap-ice state side (first targets) (second targets))))}) - -(define-card "Retrieval Run" - {:req (req archives-runnable) - :effect (effect (make-run - :archives - {:req (req (= target :archives)) - :replace-access - {:prompt "Choose a program to install" - :msg (msg "install " (:title target)) - :choices (req (filter program? (:discard runner))) - :effect (effect (runner-install target {:ignore-all-cost true}))}} - card))}) - -(define-card "Rigged Results" - (letfn [(runner-choice [choices] - {:prompt "Spend how many credits?" - :choices choices - :async true - :effect (effect (show-wait-prompt :runner "Corp to guess") - (clear-wait-prompt :corp) - (continue-ability :corp (corp-choice choices (str->int target)) card nil))}) - (corp-choice [choices spent] - {:prompt "Guess how many credits were spent" - :choices choices - :async true - :effect (req (clear-wait-prompt state :runner) - (deduct state :runner [:credit spent]) - (system-msg state :runner (str "spends " spent " [Credit]")) - (system-msg state :corp (str " guesses " target " [Credit]")) - (wait-for (trigger-event-simult state side :reveal-spent-credits nil nil spent) - (if (not= spent (str->int target)) - (continue-ability state :runner (choose-ice) card nil) - (effect-completed state side eid))))}) - (choose-ice [] - {:prompt "Select a piece of ICE to bypass" - :choices {:req ice?} - :msg (msg "bypass " (card-str state target)) - :effect (effect (make-run (second (:zone target))))})] - {:async true - :effect (req (show-wait-prompt state :corp "Runner to spend credits") - (let [all-amounts (range (min 3 (inc (get-in @state [:runner :credit])))) - valid-amounts (remove #(or (any-flag-fn? state :corp :prevent-secretly-spend %) - (any-flag-fn? state :runner :prevent-secretly-spend %)) - all-amounts) - choices (map str valid-amounts)] - (continue-ability state side (runner-choice choices) card nil)))})) - -(define-card "Rip Deal" - {:req (req hq-runnable) - :effect (effect - (make-run - :hq {:req (req (= target :hq)) - :replace-access - {:async true - :effect - (req (let [n (min (-> corp :hand count) (access-count state side :hq-access)) - heap (-> runner :discard count (- 1))] - (move state side (find-cid (:cid card) (:discard runner)) :rfg) - (if (pos? heap) - (continue-ability - state side - {:show-discard true - :prompt (str "Choose " (quantify (min n heap) "card") " to move from the Heap to your Grip") - :async true - :msg (msg "take " (join ", " (map :title targets)) " from their Heap to their Grip") - :choices {:max (min n heap) - :all true - :req #(and (runner? %) - (in-discard? %))} - :effect (req (doseq [c targets] - (move state side c :hand)) - (do-access state side eid (:server run) {:hq-root-only true}))} - card nil) - (continue-ability - state side - {:async true - :msg (msg "take no cards from their Heap to their Grip") - :effect (req (do-access state side eid (:server run) {:hq-root-only true}))} - card nil))))}} - card))}) - -(define-card "Rumor Mill" - (letfn [(eligible? [card] (and (:uniqueness card) - (or (asset? card) - (upgrade? card)) - (not (has-subtype? card "Region")))) - (rumor [state] (filter eligible? (concat (all-installed state :corp) - (get-in @state [:corp :hand]) - (get-in @state [:corp :deck]) - (get-in @state [:corp :discard]))))] - {:leave-play (req (doseq [c (rumor state)] - (enable-card state :corp c))) - :effect (req (doseq [c (rumor state)] - (disable-card state :corp c))) - :events {:corp-install {:req (req (eligible? target)) - :effect (effect (disable-card :corp target))}}})) - -(define-card "Run Amok" - {:implementation "Ice trash is manual" - :prompt "Choose a server" :choices (req runnable-servers) - :effect (effect (make-run target {:end-run {:msg " trash 1 piece of ICE that was rezzed during the run"}} card))}) - -(define-card "Running Interference" - (run-event - {:events {:pre-rez-cost nil - :run-ends nil}} - nil - nil - (effect (register-events {:pre-rez-cost {:req (req (ice? target)) - :msg (msg "double the cost (as an additional cost) to rez " (card-str state target)) - :effect (effect (rez-additional-cost-bonus [:credit (:cost target)]))} - :run-ends {:effect (effect (unregister-events card))}} - (assoc card :zone '(:discard)))))) - -(define-card "Satellite Uplink" - {:choices {:max 2 :req installed?} - :async true - :effect (req (let [[card1 card2] targets] - (wait-for (expose state side card1) - (expose state side eid card2))))}) - -(define-card "Scavenge" - {:prompt "Select an installed program to trash" - :choices {:req #(and (program? %) - (installed? %))} - :effect (req (let [trashed target tcost (- (:cost trashed)) - st state - si side - e eid - c card] - (trash state side trashed) - (resolve-ability - state side - {:prompt "Select a program to install from your Grip or Heap" - :show-discard true - :choices {:req #(and (program? %) - (#{[:hand] [:discard]} (:zone %)) - (can-pay? st si e c nil (modified-install-cost st si % [:credit tcost])))} - :effect (effect (install-cost-bonus [:credit (- (:cost trashed))]) - (runner-install target)) - :msg (msg "trash " (:title trashed) " and install " (:title target))} card nil)))}) - -(define-card "Scrubbed" - {:implementation "Encounter effect is manual" - :abilities [{:label "Lower ice strength" - :effect (effect (update! (assoc card :scrubbed-target current-ice)) - (update-ice-strength current-ice))}] - :events {:pre-ice-strength {:req (req (= (:cid target) (get-in card [:scrubbed-target :cid]))) - :effect (effect (ice-strength-bonus -2 target))} - :run-ends {:effect (effect (update! (dissoc card :scrubbed-target)))}}}) - -(define-card "Showing Off" - {:req (req rd-runnable) - :effect (effect (make-run - :rd - {:replace-access - {:msg "access cards from the bottom of R&D" - :async true - :effect (req - ;; Not sure why this is done - (wait-for (resolve-ability state side - {:effect (effect (register-events - (:events (card-def card)) - (assoc card :zone '(:discard))))} - card nil) - (do-access state side eid (:server run))))}} card)) - :events {:pre-access {:silent (req true) - :effect (req (swap! state assoc-in [:runner :rd-access-fn] reverse))} - :run-ends {:effect (req (swap! state assoc-in [:runner :rd-access-fn] seq) - (unregister-events state side card))}}}) - -(define-card "Singularity" - (run-event - {:choices (req (filter #(can-run-server? state %) remotes))} - {:req (req (is-remote? target)) - :replace-access {:mandatory true - :msg "trash all cards in the server at no cost" - :effect (req (doseq [c (:content run-server)] - (trash state side c)))}})) - -(define-card "Social Engineering" - {:prompt "Select an unrezzed piece of ICE" - :choices {:req #(and (= (last (:zone %)) :ices) (not (rezzed? %)) (ice? %))} - :effect (req (let [ice target - serv (zone->name (second (:zone ice)))] - (resolve-ability - state :runner - {:msg (msg "select the piece of ICE at position " (ice-index state ice) " of " serv) - :effect (effect (register-events {:runner-turn-ends (effect (unregister-events - card - {:events {:pre-rez-cost nil - :runner-turn-ends nil}})) - :pre-rez-cost - {:req (req (= target ice)) - :effect (req (let [cost (rez-cost state side (get-card state target))] - (gain-credits state :runner cost))) - :msg (msg "gain " (rez-cost state side (get-card state target)) " [Credits]")}} - (assoc card :zone '(:discard))))} - card nil)))}) - -(define-card "Spear Phishing" - {:implementation "Bypass is manual" - :prompt "Choose a server" - :choices (req runnable-servers) - :effect (effect (make-run target nil card))}) - -(define-card "Spec Work" - {:async true - :additional-cost [:program 1] - :msg "gain 4 [Credits] and draw 2 cards" - :effect (effect (gain-credits 4) - (draw eid 2 nil))}) - -(define-card "Special Order" - {:prompt "Choose an Icebreaker" - :effect (effect (trigger-event :searched-stack nil) - (shuffle! :deck) - (system-msg (str "adds " (:title target) " to their Grip and shuffles their Stack")) - (move target :hand)) - :choices (req (cancellable (filter #(has-subtype? % "Icebreaker") (:deck runner)) :sorted))}) - -(define-card "Spooned" - (cutlery "Code Gate")) - -(define-card "Spot the Prey" - {:prompt "Select 1 non-ICE card to expose" - :msg "expose 1 card and make a run" - :choices {:req #(and (installed? %) (not (ice? %)) (corp? %))} - :async true - :effect (req (wait-for (expose state side target) - (continue-ability - state side - {:prompt "Choose a server" - :choices (req runnable-servers) - :async true - :effect (effect (make-run eid target))} - card nil)))}) - -(define-card "Stimhack" - (run-event - nil - {:end-run {:msg "take 1 brain damage" - :effect (effect (damage eid :brain 1 {:unpreventable true - :card card}))}} - (effect (gain-next-run-credits 9)))) - -(define-card "Sure Gamble" - {:msg "gain 9 [Credits]" - :effect (effect (gain-credits 9))}) - -(define-card "Surge" - {:msg (msg "place 2 virus tokens on " (:title target)) - :choices {:req #(and (has-subtype? % "Virus") (:added-virus-counter %))} - :effect (req (add-counter state :runner target :virus 2))}) - -(define-card "SYN Attack" - {:async true - :effect (effect (show-wait-prompt "Corp to choose an option for SYN Attack") - (continue-ability - {:player :corp - :prompt "Discard 2 cards or draw 4 cards?" - :choices (concat (when (<= 2 (count (:hand corp))) - ["Discard 2"]) - ["Draw 4"]) - :effect (req (if (= target "Draw 4") - (do (draw state :corp 4) - (clear-wait-prompt state :runner) - (system-msg state :corp "draws 4 cards from SYN Attack")) - (continue-ability - state :corp - {:prompt "Choose 2 cards to discard" - :choices {:max 2 - :req #(and (in-hand? %) (corp? %))} - :effect (effect (trash-cards :corp targets) - (clear-wait-prompt state :runner) - (system-msg :corp "discards 2 cards from SYN Attack"))} - card nil)))} - card nil))}) - -(define-card "System Outage" - {:events {:corp-draw {:req (req (not (first-event? state side :corp-draw))) - :msg "force the Corp to lose 1 [Credits]" - :effect (effect (lose-credits :corp 1))}}}) - -(define-card "System Seizure" - {:effect (effect (register-events (:events (card-def card)) (assoc card :zone '(:discard)))) - :events {:pump-breaker {:silent (req true) - :req (req (or (and (has-flag? state side :current-run :system-seizure) - (run-flag? state side (second targets) :system-seizure)) - (not (get-in @state [:per-turn (:cid card)])))) - :effect (req (update! state side (update-in (second targets) [:pump :all-run] (fnil #(+ % (first targets)) 0))) - (register-run-flag! state side card :system-seizure (fn [_ _ c] (same-card? c (second targets)))) - (update-breaker-strength state side (second targets)) - (swap! state assoc-in [:per-turn (:cid card)] targets))}} - :move-zone (req (when (= [:discard] (:zone card)) - (unregister-events state side card)))}) - -(define-card "Test Run" - (let [move-ability {:req (req (seq (filter #(get-in % [:special :test-run]) (all-active-installed state :runner)))) - :effect (req (doseq [program (filter #(get-in % [:special :test-run]) (all-active-installed state :runner))] - (move state side program :deck {:front true}) - (system-msg state side (str "move " (:title program) " to the top of the Stack"))))}] - {:events {:corp-turn-ends move-ability - :runner-turn-ends move-ability} - :prompt "Install a program from your Stack or Heap?" - :choices ["Stack" "Heap"] - :msg (msg "install a program from their " target) - :effect (effect (register-events (:events (card-def card)) (assoc card :zone '(:discard))) + :effect (req + (wait-for + (resolve-ability state side (select-install-cost state) card nil) + (let [revealed (seq (take (second async-result) (:deck corp)))] + (system-msg state :runner (str "uses Khusyuk to choose an install cost of " + (first async-result) + " [Credit] and reveals " + (if revealed + (str "(top:) " (join ", " (map :title revealed)) + " from the top of R&D") + "no cards"))) + (if revealed + (do (reveal state side revealed) + (wait-for + (resolve-ability state side (access-revealed revealed) card nil) + (shuffle! state :corp :deck) + (system-msg state :runner " shuffles R&D") + (effect-completed state side eid))) + (do (shuffle! state :corp :deck) + (system-msg state :runner " shuffles R&D") + (effect-completed state side eid))))))}} + card))}) + + "Knifed" + (cutlery "Barrier") + + "Kraken" + {:req (req (:stole-agenda runner-reg)) :prompt "Choose a server" :choices (req servers) + :msg (msg "force the Corp to trash an ICE protecting " target) + :effect (req (let [serv (next (server->zone state target)) + servname target] + (resolve-ability + state :corp + {:prompt (msg "Select a piece of ICE in " target " to trash") + :choices {:req #(and (= (last (:zone %)) :ices) + (= serv (rest (butlast (:zone %)))))} + :effect (req (trash state :corp target) + (system-msg state side (str "trashes " + (card-str state target))))} + card nil)))} + + "Labor Rights" + {:req (req (pos? (+ (count (:deck runner)) (count (:discard runner))))) + :effect (req (let [mill-count (min 3 (count (:deck runner)))] + (mill state :runner :runner mill-count) + (system-msg state :runner (str "trashes the top " (quantify mill-count "card") " of their Stack")) + (let [heap-count (min 3 (count (get-in @state [:runner :discard])))] (continue-ability - (let [where target] - {:prompt "Choose a program to install" - :choices (req (cancellable - (filter program? ((if (= where "Heap") :discard :deck) runner)))) - :effect (effect (trigger-event :searched-stack nil) - (shuffle! :deck) - (runner-install eid - (assoc-in target [:special :test-run] true) - {:ignore-all-cost true}))}) - card nil))})) - -(define-card "The Maker's Eye" - {:req (req rd-runnable) - :effect (effect (make-run :rd nil card) - (register-events (:events (card-def card)) (assoc card :zone '(:discard)))) - :events {:successful-run {:silent (req true) - :req (req (= target :rd)) - :effect (effect (access-bonus :rd 2))} - :run-ends {:effect (effect (unregister-events card))}}}) - -(define-card "The Noble Path" - {:effect (req (doseq [c (:hand runner)] - (trash state side c)) - (register-events - state side - {:pre-damage {:effect (effect (damage-prevent :net Integer/MAX_VALUE) - (damage-prevent :meat Integer/MAX_VALUE) - (damage-prevent :brain Integer/MAX_VALUE))} - :run-ends {:effect (effect (unregister-events - card - {:events {:pre-damage nil - :run-ends nil}}))}} - (assoc card :zone '(:discard))) - (resolve-ability - state side - {:prompt "Choose a server" - :choices (req runnable-servers) - :msg (msg "trash their Grip and make a run on " target ", preventing all damage") - :effect (req (let [runtgt [(last (server->zone state target))] - ices (get-in @state (concat [:corp :servers] runtgt [:ices]))] - (swap! state assoc :per-run nil - :run {:server runtgt - :position (count ices) - :access-bonus [] - :run-effect nil}) - (gain-run-credits state :runner (count-bad-pub state)) - (swap! state update-in [:runner :register :made-run] #(conj % (first runtgt))) - (trigger-event state :runner :run runtgt)))} - card nil))}) - -(define-card "The Price of Freedom" - {:additional-cost [:connection 1] - :msg "prevent the Corp from advancing cards during their next turn" - :effect (effect (register-events (:events (card-def card)) (assoc card :zone '(:rfg))) - (move (first (:play-area runner)) :rfg)) - :events {:corp-turn-begins - {:effect (effect (register-turn-flag! - card :can-advance - (fn [state side card] - ((constantly false) - (toast state :corp "Cannot advance cards this turn due to The Price of Freedom." "warning")))) - (unregister-events card))}}}) - -(define-card "Three Steps Ahead" - {:effect (effect (register-events - {:runner-turn-ends - {:msg (msg "gain " (* 2 (count (:successful-run runner-reg))) " [Credits]") - :effect (effect (gain-credits (* 2 (count (:successful-run runner-reg)))) - (unregister-events card {:events {:runner-turn-ends nil}}))}} - (assoc card :zone '(:discard))))}) - -(define-card "Tinkering" - {:prompt "Select a piece of ICE" - :choices {:req #(and (= (last (:zone %)) :ices) (ice? %))} - :effect (req (let [ice target - serv (zone->name (second (:zone ice))) - stypes (:subtype ice)] - (resolve-ability - state :runner - {:msg (msg "make " (card-str state ice) " gain Sentry, Code Gate, and Barrier until the end of the turn") - :effect (effect (update! (assoc ice :subtype (combine-subtypes true (:subtype ice) "Sentry" "Code Gate" "Barrier"))) - (update-ice-strength (get-card state ice)) - (add-icon card (get-card state ice) "T" "green") - (register-events {:runner-turn-ends - {:effect (effect (remove-icon card (get-card state ice)) - (update! (assoc (get-card state ice) :subtype stypes)) - (unregister-events card {:events {:runner-turn-ends nil}}))}} - (assoc card :zone '(:discard))))} - card nil)))}) - -(define-card "Trade-In" - ;; Basically a hack. Ideally the additional cost cause the cost trash to be passed in as targets - (letfn [(trashed-hw [state] (last (get-in @state [:runner :discard])))] - {:additional-cost [:hardware 1] - :msg (msg (let [{:keys [title cost]} (trashed-hw state)] - (str "trash " title " and gain " (quot cost 2) " [Credits]"))) - :effect (req (let [{:keys [cost]} (trashed-hw state)] - (gain-credits state :runner (quot cost 2)) - (continue-ability state :runner - {:prompt "Choose a Hardware to add to your Grip from your Stack" - :choices (req (filter hardware? - (:deck runner))) - :msg (msg "add " (:title target) " to their Grip (and shuffle their Stack)") - :effect (effect (trigger-event :searched-stack nil) - (shuffle! :deck) - (move target :hand))} - card nil)))})) - -(define-card "Traffic Jam" - {:effect (effect (update-all-advancement-costs)) - :leave-play (effect (update-all-advancement-costs)) - :events {:pre-advancement-cost - {:effect (req (advancement-cost-bonus - state side (count (filter #(= (:title %) (:title target)) (:scored corp)))))}}}) - -(define-card "Uninstall" - {:choices {:req #(and (installed? %) - (not (facedown? %)) - (#{"Program" "Hardware"} (:type %)))} - :msg (msg "move " (:title target) " to their Grip") - :effect (effect (move target :hand))}) - -(define-card "Unscheduled Maintenance" - {:events {:corp-install {:req (req (ice? target)) - :effect (effect (register-turn-flag! - card :can-install-ice - (fn [state side card] - (if (ice? card) - ((constantly false) - (toast state :corp "Cannot install ICE the rest of this turn due to Unscheduled Maintenance")) - true))))}} - :leave-play (effect (clear-turn-flag! card :can-install-ice))}) - -(define-card "Vamp" - {:req (req hq-runnable) - :effect (effect (make-run - :hq {:req (req (= target :hq)) - :replace-access - {:async true - :prompt "How many [Credits]?" :choices :credit - :msg (msg "take 1 tag and make the Corp lose " target " [Credits]") - :effect (effect (lose-credits :corp target) - (gain-tags eid 1))}} card))}) - -(define-card "Wanton Destruction" - {:req (req hq-runnable) - :effect (effect (make-run - :hq {:req (req (= target :hq)) - :replace-access - {:msg (msg "force the Corp to discard " target " cards from HQ at random") - :prompt "How many [Click] do you want to spend?" - :choices (req (map str (range 1 (inc (:click runner))))) - :effect (req (let [n (str->int target)] - (when (pay state :runner card :click n) - (trash-cards state :corp (take n (shuffle (:hand corp)))))))}} card))}) - -(define-card "Watch the World Burn" - (letfn [(rfg-card-event [burn-name] - {:pre-access-card - {:req (req (= (:title target) burn-name)) - :msg (msg (str "uses the previously played Watch the World Burn to remove " burn-name " from the game")) - :effect (req (move state :corp target :rfg))}})] - {:makes-run true - :prompt "Choose a server" - :choices (req (filter #(can-run-server? state %) remotes)) - :effect (effect (make-run target nil card) - (register-events (:events (card-def card)) - (dissoc card :zone))) - :events {:pre-access-card {:req (req (and (not= (:type target) "Agenda") - (get-in @state [:run :successful]))) - :once :per-run - :effect (req (let [t (:title target)] - (system-msg state :runner (str "to remove " t " from the game, and watch for other copies of " t " to burn")) - (move state :corp target :rfg) - ;; in the below, the new :cid ensures that when unregister-events is called, the rfg-card-event is left alone - (register-events state side (rfg-card-event t) (dissoc (assoc card :cid (make-cid)) :zone))))} - :run-ends {:effect (effect (unregister-events (dissoc card :zone)))}}})) - -(define-card "White Hat" - (letfn [(finish-choice [choices] - (let [choices (filter #(not= "None" %) choices)] - (when (not-empty choices) - {:effect (req (doseq [c choices] - (move state :corp c :deck)) - (shuffle! state :corp :deck)) - :msg (str "shuffle " (join ", " (map :title choices)) " into R&D")}))) - (choose-cards [hand chosen] - {:prompt "Choose a card in HQ to shuffle into R&D" - :player :runner - :choices (conj (vec (clojure.set/difference hand chosen)) - "None") - :async true - :effect (req (if (and (empty? chosen) - (not= "None" target)) - (continue-ability state side (choose-cards hand (conj chosen target)) card nil) - (continue-ability state side (finish-choice (conj chosen target)) card nil)))})] - {:req (req (some #{:hq :rd :archives} (:successful-run runner-reg))) - :trace {:base 3 - :unsuccessful - {:async true - :msg "reveal all cards in HQ" - :effect (effect (reveal (:hand corp)) - (continue-ability :runner (choose-cards (set (:hand corp)) #{}) card nil))}}})) - -(define-card "Windfall" - {:effect (effect (shuffle! :deck) + state side + {:prompt (str "Choose " (quantify heap-count "card") " to shuffle into the stack") + :show-discard true + :async true + :choices {:max heap-count + :all true + :not-self true + :req #(and (runner? %) + (in-discard? %))} + :effect (req (doseq [c targets] + (move state side c :deck)) + (system-msg state :runner (str "shuffles " (join ", " (map :title targets)) + " from their Heap into their Stack, and draws 1 card")) + (shuffle! state :runner :deck) + (wait-for (draw state :runner 1 nil) + (move state side (find-latest state card) :rfg) + (system-msg state :runner "removes Labor Rights from the game") + (effect-completed state side eid)))} + card nil))))} + + "Lawyer Up" + {:msg "remove 2 tags and draw 3 cards" + :async true + :effect (req (wait-for (draw state side 3 nil) (lose-tags state side eid 2)))} + + "Lean and Mean" + {:prompt "Choose a server" + :choices (req runnable-servers) + :async true + :msg (msg "make a run on " target (when (< (count (filter program? (all-active-installed state :runner))) 4) + ", adding +2 strength to all icebreakers")) + :effect (req (when (< (count (filter program? (all-active-installed state :runner))) 4) + (doseq [c (filter #(has-subtype? % "Icebreaker") (all-active-installed state :runner))] + (pump state side c 2 :all-run))) + (make-run state side (make-eid state) target nil card))} + + "Leave No Trace" + (letfn [(get-rezzed-cids [ice] + (map :cid (filter #(and (rezzed? %) + (ice? %)) + ice)))] + {:prompt "Choose a server" + :msg "make a run and derez any ICE that are rezzed during this run" + :choices (req runnable-servers) + :async true + :effect (req + (let [old-ice-cids (get-rezzed-cids (all-installed state :corp))] + (swap! state assoc :lnt old-ice-cids) + (register-events state side (:events (card-def card)) (assoc card :zone '(:discard))) + (make-run state side (make-eid state) target nil card))) + :events {:run-ends {:effect (req (let [new (set (get-rezzed-cids (all-installed state :corp))) + old (set (:lnt @state)) + diff-cid (seq (clojure.set/difference new old)) + diff (map #(find-cid % (all-installed state :corp)) diff-cid)] + (doseq [ice diff] + (derez state :runner ice)) + (when-not (empty? diff) + (system-msg state side (str "derezzes " (join ", " (map :title diff)) " via Leave No Trace"))) + (swap! state dissoc :lnt) + (unregister-events state side card)))}}}) + + "Legwork" + {:req (req hq-runnable) + :effect (effect (make-run :hq nil card) + (register-events (:events (card-def card)) + (assoc card :zone '(:discard)))) + :events {:successful-run {:silent (req true) + :effect (effect (access-bonus :hq 2))} + :run-ends {:effect (effect (unregister-events card))}}} + + "Leverage" + {:req (req (some #{:hq} (:successful-run runner-reg))) + :player :corp + :prompt "Take 2 bad publicity?" + :yes-ability {:player :corp + :msg "takes 2 bad publicity" + :effect (effect (gain-bad-publicity :corp 2))} + :no-ability {:player :runner + :msg "is immune to damage until the beginning of the Runner's next turn" + :effect (effect + (register-events + {:pre-damage {:effect (effect (damage-prevent :net Integer/MAX_VALUE) + (damage-prevent :meat Integer/MAX_VALUE) + (damage-prevent :brain Integer/MAX_VALUE))} + :runner-turn-begins {:effect (effect (unregister-events + card + {:events {:runner-turn-begins nil + :pre-damage nil}}))}} + (assoc card :zone '(:discard))))}} + + "Levy AR Lab Access" + {:msg "shuffle their Grip and Heap into their Stack and draw 5 cards" + :async true + :effect (req (shuffle-into-deck state :runner :hand :discard) + (wait-for (draw state :runner 5 nil) + (move state side (first (:play-area runner)) :rfg) + (effect-completed state side eid)))} + + "Lucky Find" + {:msg "gain 9 [Credits]" + :effect (effect (gain-credits 9))} + + "Mad Dash" + {:prompt "Choose a server" + :choices (req runnable-servers) + :async true + :effect (effect (make-run target nil card) + (register-events (:events (card-def card)) (assoc card :zone '(:discard)))) + :events {:agenda-stolen {:silent (req true) + :effect (effect (update! (assoc card :steal true)))} + :run-ends {:async true + :effect (req (if (:steal card) + (wait-for (as-agenda state :runner (get-card state card) 1) + (system-msg state :runner + (str "adds Mad Dash to their score area as an agenda worth 1 agenda point"))) + (do (system-msg state :runner + (str "suffers 1 meat damage from Mad Dash")) + (damage state side eid :meat 1 {:card card}))) + (unregister-events state side card))}}} + + "Making an Entrance" + (letfn [(entrance-trash [cards] + {:prompt "Choose a card to trash" + :choices (cons "None" cards) + :async true + :msg (req (when (not= target "None") (str "trash " (:title target)))) + :effect (req (if (= target "None") + (if (not-empty cards) + (continue-ability state side (reorder-choice :runner :corp cards '() + (count cards) cards) card nil) + (do (clear-wait-prompt state :corp) + (effect-completed state side eid))) + (do (trash state side target {:unpreventable true}) + (continue-ability state side (entrance-trash (remove-once #(= % target) cards)) + card nil))))})] + {:msg "look at and trash or rearrange the top 6 cards of their Stack" + :async true + :effect (req (show-wait-prompt state :corp "Runner to rearrange the top cards of their stack") + (let [from (take 6 (:deck runner))] + (continue-ability state side (entrance-trash from) card nil)))}) + + "Marathon" + (run-event + {:choices (req (filter #(can-run-server? state %) remotes))} + {:end-run {:effect (req (prevent-run-on-server state card (:server run)) + (when (:successful run) + (system-msg state :runner "gains 1 [Click] and adds Marathon to their grip") + (gain state :runner :click 1) + (move state :runner (assoc card :zone [:discard]) :hand)))}}) + + "Mars for Martians" + (letfn [(count-clan [state] (count (filter #(and (has-subtype? % "Clan") (resource? %)) + (all-active-installed state :runner))))] + {:msg (msg "draw " (count-clan state) " cards and gain " (count-tags state) " [Credits]") + :async true + :effect (req (wait-for (draw state side (count-clan state) nil) + (gain-credits state side (count-tags state)) + (effect-completed state side eid)))}) + + "Mass Install" + (let [mhelper (fn mi [n] {:prompt "Select a program to install" + :choices {:req #(and (program? %) + (in-hand? %))} + :effect (req (runner-install state side target) + (when (< n 3) + (resolve-ability state side (mi (inc n)) card nil)))})] + {:effect (effect (resolve-ability (mhelper 1) card nil))}) + + "Mining Accident" + {:req (req (some #{:hq :rd :archives} (:successful-run runner-reg))) + :async true + :msg "make the Corp pay 5 [Credits] or take 1 bad publicity" + :effect (effect (move (first (:play-area runner)) :rfg) + (show-wait-prompt :runner "Corp to choose to pay or take bad publicity") + (continue-ability + {:player :corp + :async true + :prompt "Pay 5 [Credits] or take 1 Bad Publicity?" + :choices (concat (when (can-pay? state :corp eid card "Mining Accident" :credit 5) + ["Pay 5 [Credits]"]) + ["Take 1 Bad Publicity"]) + :effect (req (clear-wait-prompt state :runner) + (if (= target "Pay 5 [Credits]") + (do (lose-credits state :corp 5) + (system-msg state side "pays 5 [Credits] from Mining Accident") + (effect-completed state side eid)) + (do (gain-bad-publicity state :corp 1) + (system-msg state side "takes 1 bad publicity from Mining Accident") + (effect-completed state side eid))))} + card nil))} + + "Möbius" + {:req (req rd-runnable) + :async true + :effect (req (let [mob-eid (make-eid state) + events (:events (card-def card))] + (register-events state side + (assoc-in events [:successful-run-ends :eid] mob-eid) + (assoc card :zone '(:discard))) + (wait-for (make-run state side mob-eid :rd nil card) + (let [card (get-card state (assoc card :zone '(:discard)))] + (unregister-events state side card) + (when (:run-again card) + (update! state side (dissoc card :run-again)) + (register-events state side {:successful-run + {:req (req (= target :rd)) + :msg "gain 4 [Credits]" + :effect (effect (gain-credits 4))}} + card) + (wait-for (make-run state side (make-eid state mob-eid) :rd nil card) + (unregister-events state side card))))))) + :events {:successful-run nil + :successful-run-ends {:interactive (req true) + :optional {:req (req (= [:rd] (:server target))) + :prompt "Make another run on R&D?" + :yes-ability {:effect (effect (clear-wait-prompt :corp) + (update! (assoc card :run-again true)))}}}}} + + "Modded" + {:prompt "Select a program or piece of hardware to install from your Grip" + :choices {:req #(and (or (hardware? %) + (program? %)) + (in-hand? %))} + :effect (effect (install-cost-bonus [:credit -3]) (runner-install target))} + + "Net Celebrity" + {:recurring 1 + :interactions {:pay-credits {:req (req (:run @state)) + :type :recurring}}} + + "Networking" + {:msg "remove 1 tag" + :effect (effect (lose-tags 1)) + :optional {:prompt "Pay 1 [Credits] to add Networking to Grip?" + :yes-ability {:cost [:credit 1] + :msg "add it to their Grip" + :effect (effect (move (last (:discard runner)) :hand))}}} + + "Notoriety" + {:req (req (and (some #{:hq} (:successful-run runner-reg)) + (some #{:rd} (:successful-run runner-reg)) + (some #{:archives} (:successful-run runner-reg)))) + :async true + :effect (req (as-agenda state :runner eid (first (:play-area runner)) 1)) + :msg "add it to their score area as an agenda worth 1 agenda point"} + + "Office Supplies" + {:play-cost-bonus (req [:credit (- (:link runner 0))]) + :prompt "Gain 4 [Credits] or draw 4 cards?" + :choices ["Gain 4 [Credits]" "Draw 4 cards"] + :async true + :msg (msg (if (= target "Gain 4 [Credits]") + "gain 4 [Credits]" + "draw 4 cards")) + :effect (req (if (= target "Gain 4 [Credits]") + (do (gain-credits state :runner 4) + (effect-completed state side eid)) + (draw state :runner eid 4 nil)))} + + "On the Lam" + {:req (req (some resource? (all-active-installed state :runner))) + :prompt "Choose a resource to host On the Lam" + :choices {:req #(and (resource? %) + (installed? %))} + :effect (effect (host target (assoc card :zone [:discard] :installed true)) + (card-init (find-latest state card) {:resolve-effect false}) + (system-msg (str "hosts On the Lam on " (:title target)))) + :interactions {:prevent [{:type #{:net :brain :meat :tag} + :req (req true)}]} + :abilities [{:label "[Trash]: Avoid 3 tags" + :msg "avoid up to 3 tags" + :effect (effect (tag-prevent :runner 3) + (trash card {:cause :ability-cost}))} + {:label "[Trash]: Prevent up to 3 damage" + :msg "prevent up to 3 damage" + :effect (effect (damage-prevent :net 3) + (damage-prevent :meat 3) + (damage-prevent :brain 3) + (trash card {:cause :ability-cost}))}]} + + "Out of the Ashes" + (let [ashes-run {:prompt "Choose a server" + :choices (req runnable-servers) + :async true + :effect (effect (make-run eid target nil card))} + ashes-recur (fn ashes-recur [n] + {:optional + {:prompt "Remove Out of the Ashes from the game to make a run?" + :yes-ability + {:msg "removes Out of the Ashes from the game to make a run" + :effect + (req (let [card (some #(when (= "Out of the Ashes" (:title %)) %) (:discard runner))] + (move state side card :rfg) + (unregister-events state side card) + (wait-for (resolve-ability state side ashes-run card nil) + (if (< 1 n) + (continue-ability state side (ashes-recur (dec n)) card nil) + (effect-completed state side eid)))))}}}) + ashes-flag {:runner-phase-12 + {:priority -1 + :once :per-turn + :once-key :out-of-ashes + :effect (effect (continue-ability + (ashes-recur (count (filter #(= "Out of the Ashes" (:title %)) + (:discard runner)))) + card nil))}}] + (run-event + {:move-zone (req (if (= [:discard] (:zone card)) + (register-events state side ashes-flag (assoc card :zone [:discard])) + (unregister-events state side card {:events {:runner-phase-12 nil}})))} + nil)) + + "Paper Tripping" + {:msg "remove all tags" :effect (effect (lose-tags :all))} + + "Peace in Our Time" + {:req (req (not (:scored-agenda corp-reg))) + :msg "gain 10 [Credits]. The Corp gains 5 [Credits]" + :effect (req (gain-credits state :runner 10) + (gain-credits state :corp 5) + (apply prevent-run-on-server + state card (get-zones state)) + (register-events state side + {:runner-turn-ends {:effect (req (apply enable-run-on-server state card (get-zones state)))}} + (assoc card :zone '(:discard)))) + :events {:runner-turn-ends nil}} + + "Planned Assault" + {:msg (msg "play " (:title target)) + :choices (req (cancellable (filter #(and (has-subtype? % "Run") + (<= (:cost %) (:credit runner))) (:deck runner)) :sorted)) + :prompt "Choose a Run event" :effect (effect (trigger-event :searched-stack nil) + (shuffle! :deck) + (play-instant target {:no-additional-cost true}))} + + "Political Graffiti" + (let [update-agenda-points (fn [state side target amount] + (set-prop state side (get-card state target) :agendapoints (+ amount (:agendapoints (get-card state target)))) + (gain-agenda-point state side amount))] + {:req (req archives-runnable) + :events {:purge {:effect (effect (trash card {:cause :purge}))}} + :trash-effect {:effect (req (let [current-side (get-scoring-owner state {:cid (:agenda-cid card)})] + (update-agenda-points state current-side (find-cid (:agenda-cid card) (get-in @state [current-side :scored])) 1)))} + :effect (effect (make-run + :archives + {:req (req (= target :archives)) + :replace-access + {:prompt "Select an agenda to host Political Graffiti" + :choices {:req #(in-corp-scored? state side %)} + :msg (msg "host Political Graffiti on " (:title target) " as a hosted condition counter") + :effect (req (host state :runner (get-card state target) + ; keep host cid in :agenda-cid because `trash` will clear :host + (assoc card :zone [:discard] :installed true :agenda-cid (:cid (get-card state target)))) + (update-agenda-points state :corp target -1))}} card))}) + + "Populist Rally" + {:req (req (seq (filter #(has-subtype? % "Seedy") (all-active-installed state :runner)))) + :msg "give the Corp 1 fewer [Click] to spend on their next turn" + :effect (effect (lose :corp :click-per-turn 1) + (register-events (:events (card-def card)) + (assoc card :zone '(:discard)))) + :events {:corp-turn-ends {:effect (effect (gain :corp :click-per-turn 1) + (unregister-events card))}}} + + "Power Nap" + {:effect (effect (gain-credits (+ 2 (count (filter #(has-subtype? % "Double") + (:discard runner)))))) + :msg (msg "gain " (+ 2 (count (filter #(has-subtype? % "Double") (:discard runner)))) " [Credits]")} + + "Power to the People" + {:effect (effect (register-events + {:pre-steal-cost {:once :per-turn + :effect (effect (gain-credits 7)) + :msg "gain 7 [Credits]"} + :runner-turn-ends {:effect (effect (unregister-events card))}} + (assoc card :zone '(:discard)))) + :events {:pre-steal-cost nil + :runner-turn-ends nil}} + + "Prey" + (run-event {:implementation "Ice trash is manual"} nil) + + "Process Automation" + {:msg "gain 2 [Credits] and draw 1 card" + :async true + :effect (effect (gain-credits 2) + (draw eid 1 nil))} + + "Push Your Luck" + (letfn [(runner-choice [choices] + {:prompt "Spend how many credits?" + :choices choices + :async true + :effect (effect (show-wait-prompt :runner "Corp to guess even or odd") + (clear-wait-prompt :corp) + (continue-ability :corp (corp-choice (str->int target)) card nil))}) + (corp-choice [spent] + {:prompt "Guess how many credits were spent" + :choices ["Even" "Odd"] + :async true + :effect (req (let [correct-guess ((if (= target "Even") even? odd?) spent)] + (clear-wait-prompt state :runner) + (deduct state :runner [:credit spent]) + (system-msg state :runner (str "spends " spent " [Credit]")) + (system-msg state :corp (str (if correct-guess " " " in") + "correctly guesses " (lower-case target))) + (wait-for (trigger-event-simult state side :reveal-spent-credits nil nil spent) + (when-not correct-guess + (system-msg state :runner (str "gains " (* 2 spent) " [Credits]")) + (gain-credits state :runner (* 2 spent))) + (effect-completed state side eid))))})] + {:async true + :effect (req (show-wait-prompt state :corp "Runner to spend credits") + (let [all-amounts (range (inc (get-in @state [:runner :credit]))) + valid-amounts (remove #(or (any-flag-fn? state :corp :prevent-secretly-spend %) + (any-flag-fn? state :runner :prevent-secretly-spend %)) + all-amounts) + choices (map str valid-amounts)] + (continue-ability state side (runner-choice choices) card nil)))}) + + "Pushing the Envelope" + (letfn [(hsize [s] (count (get-in s [:runner :hand])))] + {:msg (msg (if (<= (hsize @state) 2) + "make a run, and adds +2 strength to installed icebreakers" + "make a run")) + :prompt "Choose a server" + :choices (req runnable-servers) + :async true + :effect (req (when (<= (hsize @state) 2) + (let [breakers (filter #(has-subtype? % "Icebreaker") (all-active-installed state :runner))] + (doseq [t breakers] (pump state side t 2 :all-run)))) + (make-run state side (make-eid state) target))}) + + "Quality Time" + {:msg "draw 5 cards" :async true :effect (effect (draw eid 5 nil))} + + "Queen's Gambit" + {:choices ["0", "1", "2", "3"] + :prompt "How many advancement tokens?" + :effect (req (let [c (str->int target)] + (resolve-ability + state side + {:choices {:req #(and (is-remote? (second (:zone %))) + (= (last (:zone %)) :content) + (not (:rezzed %)))} + :msg (msg "add " c " advancement tokens on a card and gain " (* 2 c) " [Credits]") + :effect (effect (gain-credits (* 2 c)) + (add-prop :corp target :advance-counter c {:placed true}) + (register-turn-flag! card :can-access + ;; prevent access of advanced card + (fn [_ _ card] (not (same-card? target card)))))} + card nil)))} + + "Quest Completed" + {:req (req (and (some #{:hq} (:successful-run runner-reg)) + (some #{:rd} (:successful-run runner-reg)) + (some #{:archives} (:successful-run runner-reg)))) + :choices {:req installed?} :msg (msg "access " (:title target)) + :effect (effect (access-card target))} + + "Rebirth" + {:msg "change identities" + :prompt "Choose an identity to become" + :choices (req (let [is-draft-id? #(.startsWith (:code %) "00") + runner-identity (:identity runner) + is-swappable #(and (= "Identity" (:type %)) + (= (:faction runner-identity) (:faction %)) + (not (is-draft-id? %)) + (not= (:title runner-identity) (:title %))) + swappable-ids (filter is-swappable (server-cards))] + (cancellable swappable-ids :sorted))) + :effect (req (let [old-runner-identity (:identity runner)] + ;; Handle hosted cards (Ayla) - Part 1 + (doseq [c (:hosted old-runner-identity)] + (move state side c :temp-hosted)) + (move state side (last (:discard runner)) :rfg) + (disable-identity state side) + ;; Manually reduce the runner's link by old link + (lose state :runner :link (:baselink old-runner-identity)) + ;; Move the selected ID to [:runner :identity] and set the zone + (let [new-id (-> target :title server-card make-card (assoc :zone [:identity])) + num-old-blanks (:num-disabled old-runner-identity)] + (swap! state assoc-in [side :identity] new-id) + ;; enable-identity does not do everything that init-identity does + (init-identity state side new-id) + (when num-old-blanks + (dotimes [_ num-old-blanks] + (disable-identity state side))))) + ;; Handle hosted cards (Ayla) - Part 2 + (doseq [c (get-in @state [:runner :temp-hosted])] + ;; Currently assumes all hosted cards are hosted facedown (Ayla) + (host state side (get-in @state [:runner :identity]) c {:facedown true})))} + + "Reboot" + (letfn [(install-cards [state side eid card to-install titles] + (if-let [f (first to-install)] + (wait-for (runner-install state :runner f {:facedown true :no-msg true}) + (install-cards state side eid card (rest to-install) titles)) + (do + (move state side (find-latest state card) :rfg) + (system-msg state :runner (str "uses Reboot to install " (join ", " titles) " facedown")) + (effect-completed state side eid))))] + {:req (req archives-runnable) + :makes-run true + :effect (effect + (make-run + :archives + {:req (req (= target :archives)) + :replace-access + {:prompt "Choose up to five cards to install" + :show-discard true + :choices {:max 5 + :req #(and (in-discard? %) (runner? %) (not (same-card? % card)))} + :mandatory true + :async true + :cancel-effect (req (move state side (find-latest state card) :rfg) + (effect-completed state side eid)) + :effect (req (install-cards state side eid card targets (map :title targets)))}} + card))}) + + "Recon" + (run-event) + + "Rejig" + (let [valid-target? (fn [card] (and (runner? card) + (or (program? card) + (hardware? card)))) + pick-up {:async true + :prompt "Select a program or piece of hardware to add to your Grip" + :choices {:req #(and (valid-target? %) + (installed? %))} + :effect (req (move state side target :hand) + (effect-completed state side (make-result eid (:cost target))))} + put-down (fn [st si eid card bonus] + {:async true + :prompt "Select a program or piece of hardware to install" + :choices {:req #(and (valid-target? %) + (can-pay? st si eid card nil (modified-install-cost st si % [:credit (- bonus)])))} + :effect (effect (install-cost-bonus [:credit (- bonus)]) + (runner-install eid target nil))})] + {:req (req (some valid-target? (all-installed state :runner))) + :effect (req (wait-for (resolve-ability state side pick-up card nil) + (continue-ability state side + (put-down state side eid card async-result) + card nil)))}) + + "Reshape" + {:prompt "Select two non-rezzed ICE to swap positions" + :choices {:req #(and (installed? %) (not (rezzed? %)) (ice? %)) :max 2} + :msg (msg "swap the positions of " (card-str state (first targets)) " and " (card-str state (second targets))) + :effect (req (when (= (count targets) 2) + (swap-ice state side (first targets) (second targets))))} + + "Retrieval Run" + {:req (req archives-runnable) + :effect (effect (make-run + :archives + {:req (req (= target :archives)) + :replace-access + {:prompt "Choose a program to install" + :msg (msg "install " (:title target)) + :choices (req (filter program? (:discard runner))) + :effect (effect (runner-install target {:ignore-all-cost true}))}} + card))} + + "Rigged Results" + (letfn [(runner-choice [choices] + {:prompt "Spend how many credits?" + :choices choices + :async true + :effect (effect (show-wait-prompt :runner "Corp to guess") + (clear-wait-prompt :corp) + (continue-ability :corp (corp-choice choices (str->int target)) card nil))}) + (corp-choice [choices spent] + {:prompt "Guess how many credits were spent" + :choices choices + :async true + :effect (req (clear-wait-prompt state :runner) + (deduct state :runner [:credit spent]) + (system-msg state :runner (str "spends " spent " [Credit]")) + (system-msg state :corp (str " guesses " target " [Credit]")) + (wait-for (trigger-event-simult state side :reveal-spent-credits nil nil spent) + (if (not= spent (str->int target)) + (continue-ability state :runner (choose-ice) card nil) + (effect-completed state side eid))))}) + (choose-ice [] + {:prompt "Select a piece of ICE to bypass" + :choices {:req ice?} + :msg (msg "bypass " (card-str state target)) + :effect (effect (make-run (second (:zone target))))})] + {:async true + :effect (req (show-wait-prompt state :corp "Runner to spend credits") + (let [all-amounts (range (min 3 (inc (get-in @state [:runner :credit])))) + valid-amounts (remove #(or (any-flag-fn? state :corp :prevent-secretly-spend %) + (any-flag-fn? state :runner :prevent-secretly-spend %)) + all-amounts) + choices (map str valid-amounts)] + (continue-ability state side (runner-choice choices) card nil)))}) + + "Rip Deal" + {:req (req hq-runnable) + :effect (effect + (make-run + :hq {:req (req (= target :hq)) + :replace-access + {:async true + :effect + (req (let [n (min (-> corp :hand count) (access-count state side :hq-access)) + heap (-> runner :discard count (- 1))] + (move state side (find-cid (:cid card) (:discard runner)) :rfg) + (if (pos? heap) + (continue-ability + state side + {:show-discard true + :prompt (str "Choose " (quantify (min n heap) "card") " to move from the Heap to your Grip") + :async true + :msg (msg "take " (join ", " (map :title targets)) " from their Heap to their Grip") + :choices {:max (min n heap) + :all true + :req #(and (runner? %) + (in-discard? %))} + :effect (req (doseq [c targets] + (move state side c :hand)) + (do-access state side eid (:server run) {:hq-root-only true}))} + card nil) + (continue-ability + state side + {:async true + :msg (msg "take no cards from their Heap to their Grip") + :effect (req (do-access state side eid (:server run) {:hq-root-only true}))} + card nil))))}} + card))} + + "Rumor Mill" + (letfn [(eligible? [card] (and (:uniqueness card) + (or (asset? card) + (upgrade? card)) + (not (has-subtype? card "Region")))) + (rumor [state] (filter eligible? (concat (all-installed state :corp) + (get-in @state [:corp :hand]) + (get-in @state [:corp :deck]) + (get-in @state [:corp :discard]))))] + {:leave-play (req (doseq [c (rumor state)] + (enable-card state :corp c))) + :effect (req (doseq [c (rumor state)] + (disable-card state :corp c))) + :events {:corp-install {:req (req (eligible? target)) + :effect (effect (disable-card :corp target))}}}) + + "Run Amok" + {:implementation "Ice trash is manual" + :prompt "Choose a server" :choices (req runnable-servers) + :effect (effect (make-run target {:end-run {:msg " trash 1 piece of ICE that was rezzed during the run"}} card))} + + "Running Interference" + (run-event + {:events {:pre-rez-cost nil + :run-ends nil}} + nil + nil + (effect (register-events {:pre-rez-cost {:req (req (ice? target)) + :msg (msg "double the cost (as an additional cost) to rez " (card-str state target)) + :effect (effect (rez-additional-cost-bonus [:credit (:cost target)]))} + :run-ends {:effect (effect (unregister-events card))}} + (assoc card :zone '(:discard))))) + + "Satellite Uplink" + {:choices {:max 2 :req installed?} + :async true + :effect (req (let [[card1 card2] targets] + (wait-for (expose state side card1) + (expose state side eid card2))))} + + "Scavenge" + {:prompt "Select an installed program to trash" + :choices {:req #(and (program? %) + (installed? %))} + :effect (req (let [trashed target tcost (- (:cost trashed)) + st state + si side + e eid + c card] + (trash state side trashed) + (resolve-ability + state side + {:prompt "Select a program to install from your Grip or Heap" + :show-discard true + :choices {:req #(and (program? %) + (#{[:hand] [:discard]} (:zone %)) + (can-pay? st si e c nil (modified-install-cost st si % [:credit tcost])))} + :effect (effect (install-cost-bonus [:credit (- (:cost trashed))]) + (runner-install target)) + :msg (msg "trash " (:title trashed) " and install " (:title target))} card nil)))} + + "Scrubbed" + {:implementation "Encounter effect is manual" + :abilities [{:label "Lower ice strength" + :effect (effect (update! (assoc card :scrubbed-target current-ice)) + (update-ice-strength current-ice))}] + :events {:pre-ice-strength {:req (req (= (:cid target) (get-in card [:scrubbed-target :cid]))) + :effect (effect (ice-strength-bonus -2 target))} + :run-ends {:effect (effect (update! (dissoc card :scrubbed-target)))}}} + + "Showing Off" + {:req (req rd-runnable) + :effect (effect (make-run + :rd + {:replace-access + {:msg "access cards from the bottom of R&D" + :async true + :effect (req + ;; Not sure why this is done + (wait-for (resolve-ability state side + {:effect (effect (register-events + (:events (card-def card)) + (assoc card :zone '(:discard))))} + card nil) + (do-access state side eid (:server run))))}} card)) + :events {:pre-access {:silent (req true) + :effect (req (swap! state assoc-in [:runner :rd-access-fn] reverse))} + :run-ends {:effect (req (swap! state assoc-in [:runner :rd-access-fn] seq) + (unregister-events state side card))}}} + + "Singularity" + (run-event + {:choices (req (filter #(can-run-server? state %) remotes))} + {:req (req (is-remote? target)) + :replace-access {:mandatory true + :msg "trash all cards in the server at no cost" + :effect (req (doseq [c (:content run-server)] + (trash state side c)))}}) + + "Social Engineering" + {:prompt "Select an unrezzed piece of ICE" + :choices {:req #(and (= (last (:zone %)) :ices) (not (rezzed? %)) (ice? %))} + :effect (req (let [ice target + serv (zone->name (second (:zone ice)))] (resolve-ability - {:effect (req (let [topcard (first (:deck runner)) - cost (:cost topcard)] - (trash state side topcard) - (when-not (event? topcard) - (gain-credits state side cost)) - (system-msg state side - (str "shuffles their Stack and trashes " (:title topcard) - (when-not (event? topcard) - (str " to gain " cost " [Credits]"))))))} - card nil))}) + state :runner + {:msg (msg "select the piece of ICE at position " (ice-index state ice) " of " serv) + :effect (effect (register-events {:runner-turn-ends (effect (unregister-events + card + {:events {:pre-rez-cost nil + :runner-turn-ends nil}})) + :pre-rez-cost + {:req (req (= target ice)) + :effect (req (let [cost (rez-cost state side (get-card state target))] + (gain-credits state :runner cost))) + :msg (msg "gain " (rez-cost state side (get-card state target)) " [Credits]")}} + (assoc card :zone '(:discard))))} + card nil)))} + + "Spear Phishing" + {:implementation "Bypass is manual" + :prompt "Choose a server" + :choices (req runnable-servers) + :effect (effect (make-run target nil card))} + + "Spec Work" + {:async true + :additional-cost [:program 1] + :msg "gain 4 [Credits] and draw 2 cards" + :effect (effect (gain-credits 4) + (draw eid 2 nil))} + + "Special Order" + {:prompt "Choose an Icebreaker" + :effect (effect (trigger-event :searched-stack nil) + (shuffle! :deck) + (system-msg (str "adds " (:title target) " to their Grip and shuffles their Stack")) + (move target :hand)) + :choices (req (cancellable (filter #(has-subtype? % "Icebreaker") (:deck runner)) :sorted))} + + "Spooned" + (cutlery "Code Gate") + + "Spot the Prey" + {:prompt "Select 1 non-ICE card to expose" + :msg "expose 1 card and make a run" + :choices {:req #(and (installed? %) (not (ice? %)) (corp? %))} + :async true + :effect (req (wait-for (expose state side target) + (continue-ability + state side + {:prompt "Choose a server" + :choices (req runnable-servers) + :async true + :effect (effect (make-run eid target))} + card nil)))} + + "Stimhack" + (run-event + nil + {:end-run {:msg "take 1 brain damage" + :effect (effect (damage eid :brain 1 {:unpreventable true + :card card}))}} + (effect (gain-next-run-credits 9))) + + "Sure Gamble" + {:msg "gain 9 [Credits]" + :effect (effect (gain-credits 9))} + + "Surge" + {:msg (msg "place 2 virus tokens on " (:title target)) + :choices {:req #(and (has-subtype? % "Virus") (:added-virus-counter %))} + :effect (req (add-counter state :runner target :virus 2))} + + "SYN Attack" + {:async true + :effect (effect (show-wait-prompt "Corp to choose an option for SYN Attack") + (continue-ability + {:player :corp + :prompt "Discard 2 cards or draw 4 cards?" + :choices (concat (when (<= 2 (count (:hand corp))) + ["Discard 2"]) + ["Draw 4"]) + :effect (req (if (= target "Draw 4") + (do (draw state :corp 4) + (clear-wait-prompt state :runner) + (system-msg state :corp "draws 4 cards from SYN Attack")) + (continue-ability + state :corp + {:prompt "Choose 2 cards to discard" + :choices {:max 2 + :req #(and (in-hand? %) (corp? %))} + :effect (effect (trash-cards :corp targets) + (clear-wait-prompt state :runner) + (system-msg :corp "discards 2 cards from SYN Attack"))} + card nil)))} + card nil))} + + "System Outage" + {:events {:corp-draw {:req (req (not (first-event? state side :corp-draw))) + :msg "force the Corp to lose 1 [Credits]" + :effect (effect (lose-credits :corp 1))}}} + + "System Seizure" + {:effect (effect (register-events (:events (card-def card)) (assoc card :zone '(:discard)))) + :events {:pump-breaker {:silent (req true) + :req (req (or (and (has-flag? state side :current-run :system-seizure) + (run-flag? state side (second targets) :system-seizure)) + (not (get-in @state [:per-turn (:cid card)])))) + :effect (req (update! state side (update-in (second targets) [:pump :all-run] (fnil #(+ % (first targets)) 0))) + (register-run-flag! state side card :system-seizure (fn [_ _ c] (same-card? c (second targets)))) + (update-breaker-strength state side (second targets)) + (swap! state assoc-in [:per-turn (:cid card)] targets))}} + :move-zone (req (when (= [:discard] (:zone card)) + (unregister-events state side card)))} + + "Test Run" + (let [move-ability {:req (req (seq (filter #(get-in % [:special :test-run]) (all-active-installed state :runner)))) + :effect (req (doseq [program (filter #(get-in % [:special :test-run]) (all-active-installed state :runner))] + (move state side program :deck {:front true}) + (system-msg state side (str "move " (:title program) " to the top of the Stack"))))}] + {:events {:corp-turn-ends move-ability + :runner-turn-ends move-ability} + :prompt "Install a program from your Stack or Heap?" + :choices ["Stack" "Heap"] + :msg (msg "install a program from their " target) + :effect (effect (register-events (:events (card-def card)) (assoc card :zone '(:discard))) + (continue-ability + (let [where target] + {:prompt "Choose a program to install" + :choices (req (cancellable + (filter program? ((if (= where "Heap") :discard :deck) runner)))) + :effect (effect (trigger-event :searched-stack nil) + (shuffle! :deck) + (runner-install eid + (assoc-in target [:special :test-run] true) + {:ignore-all-cost true}))}) + card nil))}) + + "The Maker's Eye" + {:req (req rd-runnable) + :effect (effect (make-run :rd nil card) + (register-events (:events (card-def card)) (assoc card :zone '(:discard)))) + :events {:successful-run {:silent (req true) + :req (req (= target :rd)) + :effect (effect (access-bonus :rd 2))} + :run-ends {:effect (effect (unregister-events card))}}} + + "The Noble Path" + {:effect (req (doseq [c (:hand runner)] + (trash state side c)) + (register-events + state side + {:pre-damage {:effect (effect (damage-prevent :net Integer/MAX_VALUE) + (damage-prevent :meat Integer/MAX_VALUE) + (damage-prevent :brain Integer/MAX_VALUE))} + :run-ends {:effect (effect (unregister-events + card + {:events {:pre-damage nil + :run-ends nil}}))}} + (assoc card :zone '(:discard))) + (resolve-ability + state side + {:prompt "Choose a server" + :choices (req runnable-servers) + :msg (msg "trash their Grip and make a run on " target ", preventing all damage") + :effect (req (let [runtgt [(last (server->zone state target))] + ices (get-in @state (concat [:corp :servers] runtgt [:ices]))] + (swap! state assoc :per-run nil + :run {:server runtgt + :position (count ices) + :access-bonus [] + :run-effect nil}) + (gain-run-credits state :runner (count-bad-pub state)) + (swap! state update-in [:runner :register :made-run] #(conj % (first runtgt))) + (trigger-event state :runner :run runtgt)))} + card nil))} + + "The Price of Freedom" + {:additional-cost [:connection 1] + :msg "prevent the Corp from advancing cards during their next turn" + :effect (effect (register-events (:events (card-def card)) (assoc card :zone '(:rfg))) + (move (first (:play-area runner)) :rfg)) + :events {:corp-turn-begins + {:effect (effect (register-turn-flag! + card :can-advance + (fn [state side card] + ((constantly false) + (toast state :corp "Cannot advance cards this turn due to The Price of Freedom." "warning")))) + (unregister-events card))}}} + + "Three Steps Ahead" + {:effect (effect (register-events + {:runner-turn-ends + {:msg (msg "gain " (* 2 (count (:successful-run runner-reg))) " [Credits]") + :effect (effect (gain-credits (* 2 (count (:successful-run runner-reg)))) + (unregister-events card {:events {:runner-turn-ends nil}}))}} + (assoc card :zone '(:discard))))} + + "Tinkering" + {:prompt "Select a piece of ICE" + :choices {:req #(and (= (last (:zone %)) :ices) (ice? %))} + :effect (req (let [ice target + serv (zone->name (second (:zone ice))) + stypes (:subtype ice)] + (resolve-ability + state :runner + {:msg (msg "make " (card-str state ice) " gain Sentry, Code Gate, and Barrier until the end of the turn") + :effect (effect (update! (assoc ice :subtype (combine-subtypes true (:subtype ice) "Sentry" "Code Gate" "Barrier"))) + (update-ice-strength (get-card state ice)) + (add-icon card (get-card state ice) "T" "green") + (register-events {:runner-turn-ends + {:effect (effect (remove-icon card (get-card state ice)) + (update! (assoc (get-card state ice) :subtype stypes)) + (unregister-events card {:events {:runner-turn-ends nil}}))}} + (assoc card :zone '(:discard))))} + card nil)))} + + "Trade-In" + ;; Basically a hack. Ideally the additional cost cause the cost trash to be passed in as targets + (letfn [(trashed-hw [state] (last (get-in @state [:runner :discard])))] + {:additional-cost [:hardware 1] + :msg (msg (let [{:keys [title cost]} (trashed-hw state)] + (str "trash " title " and gain " (quot cost 2) " [Credits]"))) + :effect (req (let [{:keys [cost]} (trashed-hw state)] + (gain-credits state :runner (quot cost 2)) + (continue-ability state :runner + {:prompt "Choose a Hardware to add to your Grip from your Stack" + :choices (req (filter hardware? + (:deck runner))) + :msg (msg "add " (:title target) " to their Grip (and shuffle their Stack)") + :effect (effect (trigger-event :searched-stack nil) + (shuffle! :deck) + (move target :hand))} + card nil)))}) + + "Traffic Jam" + {:effect (effect (update-all-advancement-costs)) + :leave-play (effect (update-all-advancement-costs)) + :events {:pre-advancement-cost + {:effect (req (advancement-cost-bonus + state side (count (filter #(= (:title %) (:title target)) (:scored corp)))))}}} + + "Uninstall" + {:choices {:req #(and (installed? %) + (not (facedown? %)) + (#{"Program" "Hardware"} (:type %)))} + :msg (msg "move " (:title target) " to their Grip") + :effect (effect (move target :hand))} + + "Unscheduled Maintenance" + {:events {:corp-install {:req (req (ice? target)) + :effect (effect (register-turn-flag! + card :can-install-ice + (fn [state side card] + (if (ice? card) + ((constantly false) + (toast state :corp "Cannot install ICE the rest of this turn due to Unscheduled Maintenance")) + true))))}} + :leave-play (effect (clear-turn-flag! card :can-install-ice))} + + "Vamp" + {:req (req hq-runnable) + :effect (effect (make-run + :hq {:req (req (= target :hq)) + :replace-access + {:async true + :prompt "How many [Credits]?" :choices :credit + :msg (msg "take 1 tag and make the Corp lose " target " [Credits]") + :effect (effect (lose-credits :corp target) + (gain-tags eid 1))}} card))} + + "Wanton Destruction" + {:req (req hq-runnable) + :effect (effect (make-run + :hq {:req (req (= target :hq)) + :replace-access + {:msg (msg "force the Corp to discard " target " cards from HQ at random") + :prompt "How many [Click] do you want to spend?" + :choices (req (map str (range 1 (inc (:click runner))))) + :effect (req (let [n (str->int target)] + (when (pay state :runner card :click n) + (trash-cards state :corp (take n (shuffle (:hand corp)))))))}} card))} + + "Watch the World Burn" + (letfn [(rfg-card-event [burn-name] + {:pre-access-card + {:req (req (= (:title target) burn-name)) + :msg (msg (str "uses the previously played Watch the World Burn to remove " burn-name " from the game")) + :effect (req (move state :corp target :rfg))}})] + {:makes-run true + :prompt "Choose a server" + :choices (req (filter #(can-run-server? state %) remotes)) + :effect (effect (make-run target nil card) + (register-events (:events (card-def card)) + (dissoc card :zone))) + :events {:pre-access-card {:req (req (and (not= (:type target) "Agenda") + (get-in @state [:run :successful]))) + :once :per-run + :effect (req (let [t (:title target)] + (system-msg state :runner (str "to remove " t " from the game, and watch for other copies of " t " to burn")) + (move state :corp target :rfg) + ;; in the below, the new :cid ensures that when unregister-events is called, the rfg-card-event is left alone + (register-events state side (rfg-card-event t) (dissoc (assoc card :cid (make-cid)) :zone))))} + :run-ends {:effect (effect (unregister-events (dissoc card :zone)))}}}) + + "White Hat" + (letfn [(finish-choice [choices] + (let [choices (filter #(not= "None" %) choices)] + (when (not-empty choices) + {:effect (req (doseq [c choices] + (move state :corp c :deck)) + (shuffle! state :corp :deck)) + :msg (str "shuffle " (join ", " (map :title choices)) " into R&D")}))) + (choose-cards [hand chosen] + {:prompt "Choose a card in HQ to shuffle into R&D" + :player :runner + :choices (conj (vec (clojure.set/difference hand chosen)) + "None") + :async true + :effect (req (if (and (empty? chosen) + (not= "None" target)) + (continue-ability state side (choose-cards hand (conj chosen target)) card nil) + (continue-ability state side (finish-choice (conj chosen target)) card nil)))})] + {:req (req (some #{:hq :rd :archives} (:successful-run runner-reg))) + :trace {:base 3 + :unsuccessful + {:async true + :msg "reveal all cards in HQ" + :effect (effect (reveal (:hand corp)) + (continue-ability :runner (choose-cards (set (:hand corp)) #{}) card nil))}}}) + + "Windfall" + {:effect (effect (shuffle! :deck) + (resolve-ability + {:effect (req (let [topcard (first (:deck runner)) + cost (:cost topcard)] + (trash state side topcard) + (when-not (event? topcard) + (gain-credits state side cost)) + (system-msg state side + (str "shuffles their Stack and trashes " (:title topcard) + (when-not (event? topcard) + (str " to gain " cost " [Credits]"))))))} + card nil))}}) diff --git a/src/clj/game/cards/hardware.clj b/src/clj/game/cards/hardware.clj index 2d709abff0..8cf913e7cd 100644 --- a/src/clj/game/cards/hardware.clj +++ b/src/clj/game/cards/hardware.clj @@ -1,7 +1,7 @@ (ns game.cards.hardware (:require [game.core :refer :all] [game.core.eid :refer [make-eid make-result effect-completed]] - [game.core.card-defs :refer [card-def define-card]] + [game.core.card-defs :refer [card-def]] [game.utils :refer :all] [game.macros :refer [effect req msg wait-for continue-ability]] [clojure.string :refer [split-lines split join lower-case includes? starts-with?]] @@ -9,1546 +9,1547 @@ [jinteki.utils :refer :all])) ;; Card definitions -(define-card "Acacia" - {:events {:pre-purge {:effect (req (let [counters (number-of-virus-counters state)] - (update! state side (assoc-in (get-card state card) [:special :numpurged] counters))))} - :purge {:async true - :effect (effect (show-wait-prompt :corp "Runner to decide if they will use Acacia") - (continue-ability - {:optional - {:player :runner - :prompt "Use Acacia?" - :yes-ability {:effect (req (let [counters (- (get-in (get-card state card) [:special :numpurged]) - (number-of-virus-counters state))] - (wait-for (trash state side card nil) - (gain-credits state side counters) - (system-msg state side (str "uses Acacia and gains " counters "[Credit]")) - (clear-wait-prompt state :corp) - (effect-completed state side eid))))} - :no-ability {:effect (effect (clear-wait-prompt :corp) - (effect-completed eid))}}} - card nil))}}}) - -(define-card "Adjusted Matrix" - {:implementation "Click Adjusted Matrix to use ability." - :req (req (not-empty (filter #(has-subtype? % "Icebreaker") (all-active-installed state :runner)))) - :prompt "Choose Icebreaker on which to install Adjusted Matrix" - :choices {:req #(and (runner? %) (has-subtype? % "Icebreaker") (installed? %))} - :msg (msg "host it on " (card-str state target)) - :effect (effect (update! (assoc target :subtype (combine-subtypes false (-> target :subtype) "AI"))) - (host (get-card state target) (get-card state card))) - :abilities [{:cost [:click 1] - :req (req run) - :msg "break ice subroutine"}] - :events {:pre-card-moved {:req (req (same-card? target card)) - :effect (effect (update! (assoc (-> card :host) :subtype (-> card :host :subtype (remove-subtypes-once ["AI"])))))}}}) - -(define-card "Akamatsu Mem Chip" - {:in-play [:memory 1]}) - -(define-card "Archives Interface" - {:events - {:pre-access - {:async true - :interactive (req true) - :req (req (and (= target :archives) - (not= (:max-access run) 0) - (not-empty (:discard corp)))) - :effect (req (swap! state update-in [:corp :discard] #(map (fn [c] (assoc c :seen true)) %)) - (continue-ability - state side - {:optional - {:prompt "Use Archives Interface to remove a card from the game instead of accessing it?" - :yes-ability {:prompt "Choose a card in Archives to remove from the game instead of accessing" - :choices (req (:discard corp)) - :msg (msg "remove " (:title target) " from the game") - :effect (effect (move :corp target :rfg))}}} card nil))}}}) - -(define-card "Astrolabe" - {:in-play [:memory 1] - :events {:server-created {:msg "draw 1 card" - :async true - :effect (effect (draw :runner eid 1 nil))}}}) - -(define-card "Autoscripter" - {:events {:runner-install {:silent (req true) - :req (req (and (program? target) - ;; only trigger on Runner's turn - (= (:active-player @state) :runner) - ;; only trigger when playing a Program from grip - (some #{:hand} (:previous-zone target)) - ;; check that we haven't played a Program from the grip this turn - ;; which translates to just one case of playing a Program in turn-events - (first-event? state :runner :runner-install - (fn [[card _]] (and (some #{:hand} (:previous-zone card)) - (program? card)))))) - :msg "gain [Click]" - :effect (effect (gain :click 1))} - :unsuccessful-run {:effect (effect (trash card) - (system-msg "trashes Autoscripter"))}}}) - -(define-card "Blackguard" - {:in-play [:memory 2] - :events {:expose - {:msg (msg "attempt to force the rez of " (:title target)) - :async true - :effect (req (trigger-event state side :pre-rez-cost target) - (let [c target - cdef (card-def c) - cname (:title c) - cost (rez-cost state side target) - additional-costs (concat (:additional-cost cdef) - (:additional-cost card) - (get-rez-additional-cost-bonus state side))] - (swap! state update-in [:bonus] dissoc :cost :rez) - (if (seq additional-costs) - (do (show-wait-prompt state :runner (str "Corp to decide if they will rez " cname)) - (continue-ability - state side - {:optional - {:prompt (msg (build-cost-str [:credit cost]) - ", plus " (build-cost-str additional-costs) - " as an additional cost to rez " cname "?") - :player :corp - :yes-ability {:effect (effect (rez :corp c))} - :no-ability {:msg (msg "declines to pay additional costs" - " and is not forced to rez " cname)} - :end-effect (effect (clear-wait-prompt :runner))}} - card nil)) - (do (rez state :corp target) - (effect-completed state side eid)))))}}}) - -(define-card "Bookmark" - {:abilities [{:label "Host up to 3 cards from your Grip facedown" - :cost [:click 1] :msg "host up to 3 cards from their Grip facedown" - :choices {:max 3 - :req #(and (runner? %) - (in-hand? %))} - :effect (req (doseq [c targets] - (host state side (get-card state card) c {:facedown true})))} - {:label "Add all hosted cards to Grip" :cost [:click 1] :msg "add all hosted cards to their Grip" - :effect (req (doseq [c (:hosted card)] - (move state side c :hand)))} - {:label "[Trash]: Add all hosted cards to Grip" :msg "add all hosted cards to their Grip" - :effect (req (doseq [c (:hosted card)] - (move state side c :hand)) - (update! state side (dissoc card :hosted)) - (trash state side (get-card state card) {:cause :ability-cost}))}]}) - -(define-card "Box-E" - {:in-play [:memory 2 :hand-size 2]}) - -(define-card "Brain Cage" - {:in-play [:hand-size 3] - :effect (effect (damage eid :brain 1 {:card card}))}) - -(define-card "Brain Chip" - (let [runner-points (fn [s] (max (get-in s [:runner :agenda-point] 0) 0))] - {:effect (req (gain state :runner - :memory (runner-points @state) - :hand-size (runner-points @state)) - (add-watch state (keyword (str "brainchip" (:cid card))) - (fn [k ref old new] - (let [bonus (- (runner-points new) (runner-points old))] - (when-not (zero? bonus) - (gain state :runner - :memory bonus - :hand-size bonus)))))) - :leave-play (req (remove-watch state (keyword (str "brainchip" (:cid card)))) - (lose state :runner - :memory (runner-points @state) - :hand-size (runner-points @state)))})) - -(define-card "Capstone" - {:abilities [{:req (req (pos? (count (:hand runner)))) - :cost [:click 1] - :effect (req (let [handsize (count (:hand runner))] - (resolve-ability - state side - {:prompt "Select any number of cards to trash from your Grip" - :choices {:max handsize - :req #(and (runner? %) - (in-hand? %))} - :effect (req (let [trashed (count targets) - remaining (- handsize trashed)] - (doseq [c targets] - (when (not (empty? (filter #(= (:title c) (:title %)) - (all-active-installed state :runner)))) - (draw state side))) - (trash-cards state side targets) - (system-msg state side - (str "spends [Click] to use Capstone to trash " - (join ", " (map :title targets)) " and draw " - (- (count (get-in @state [:runner :hand])) remaining) " cards"))))} - card nil)))}]}) - -(define-card "Chop Bot 3000" - {:flags {:runner-phase-12 (req (>= 2 (count (all-installed state :runner))))} - :abilities [{:req (req (:runner-phase-12 @state)) - :msg (msg "trash " (:title target)) - :choices {:req #(and (runner? %) - (installed? %)) - :not-self true} - :async true - :effect (req (wait-for (trash state :runner target nil) - (continue-ability - state side - (let [deck (pos? (count (:deck runner))) - tags (pos? (count-tags state))] - {:req (req (or deck tags)) - :prompt "Draw 1 card or remove 1 tag" - :choices (concat (when deck ["Draw 1 card"]) - (when tags ["Remove 1 tag"])) - :async true - :effect (req (if (= target "Draw 1 card") - (draw state side eid 1 nil) - (do (lose-tags state :runner 1) - (effect-completed state side eid))))}) - card nil)))}]}) - -(define-card "Clone Chip" - {:abilities [{:prompt "Select a program to install from your Heap" - :priority true - :show-discard true - :req (req (and (not (seq (get-in @state [:runner :locked :discard]))) - (not (install-locked? state side)))) - :choices {:req #(and (program? %) - (in-discard? %))} - :effect (req (when (>= (:credit runner) (:cost target)) - (runner-install state side target) - (trash state side card {:cause :ability-cost}) - (system-msg state side (str "uses " (:title card) " to install " (:title target)))))}]}) - -(define-card "Comet" - {:in-play [:memory 1] - :events {:play-event {:req (req (first-event? state side :play-event)) - :effect (req (system-msg state :runner - (str "can play another event without spending a [Click] by clicking on Comet")) - (update! state side (assoc card :comet-event true)))}} - :abilities [{:req (req (:comet-event card)) - :prompt "Select an Event in your Grip to play" - :choices {:req #(and (event? %) - (in-hand? %))} - :msg (msg "play " (:title target)) - :effect (effect (play-instant target) - (update! (dissoc (get-card state card) :comet-event)))}]}) - -(define-card "Cortez Chip" - {:abilities [{:prompt "Select a piece of ICE" - :choices {:req ice?} - :msg (msg "trashes Cortez Chip to increase the rez cost of " (card-str state target) - " by 2 [Credits] until the end of the turn") - :effect (effect (update! (assoc card :cortez-target target)) - (trash (get-card state card) {:cause :ability-cost}))}] - :trash-effect {:effect (effect (register-events {:pre-rez {:req (req (same-card? target (:cortez-target card))) - :effect (effect (rez-additional-cost-bonus [:credit 2]))} - :runner-turn-ends {:effect (effect (unregister-events card))} - :corp-turn-ends {:effect (effect (unregister-events card))}} - (get-card state card)))} - :events {:pre-rez nil :runner-turn-ends nil :corp-turn-ends nil}}) - -(define-card "Cyberdelia" - {:implementation "Credit gain is manually triggered." - :in-play [:memory 1] - :abilities [{:msg "gain 1 [Credits] for breaking all subroutines on a piece of ice" - :once :per-turn - :effect (effect (gain-credits 1))}]}) - -(define-card "Cyberfeeder" - {:recurring 1 - :interactions {:pay-credits {:req (req (or (and (= :runner-install (:source-type eid)) - (has-subtype? target "Virus") - (program? target)) - (and (= :ability (:source-type eid)) - (has-subtype? target "Icebreaker")))) - :type :recurring}}}) - -(define-card "CyberSolutions Mem Chip" - {:in-play [:memory 2]}) - -(define-card "Cybsoft MacroDrive" - {:recurring 1 - :interactions {:pay-credits {:req (req (and (= :runner-install (:source-type eid)) - (program? target))) - :type :recurring}}}) - -(define-card "Daredevil" - {:in-play [:memory 2] - :events {:run-big {:once :per-turn - :req (req (first-event? state side :run-big)) - :msg "draw two cards" - :async true - :effect (effect (draw eid 2 nil))}}}) - -(define-card "Dedicated Processor" - {:implementation "Click Dedicated Processor to use ability" - :req (req (not-empty (filter #(has-subtype? % "Icebreaker") (all-active-installed state :runner)))) - :hosting {:req #(and (has-subtype? % "Icebreaker") - (not (has-subtype? % "AI")) - (installed? %))} - :abilities [{:cost [:credit 2] - :req (req run) - :effect (effect (pump (get-card state (:host card)) 4)) - :msg (msg (str "pump the strength of " (get-in card [:host :title]) " by 4"))}]}) - -(define-card "Deep Red" - {:implementation "MU use restriction not enforced" - :in-play [:memory 3] - :events {:runner-install - {:optional - {:req (req (has-subtype? target "Caïssa")) - :prompt "Use Deep Red?" :priority 1 - :yes-ability {:async true - :effect (req (let [cid (:cid target)] - (continue-ability - state side - {:async true - :prompt "Choose the just-installed Caïssa to have Deep Red trigger its [Click] ability" - :choices {:req #(= cid (:cid %))} - :msg (msg "trigger the [Click] ability of " (:title target) - " without spending [Click]") - :effect (req (gain state :runner :click 1) - (play-ability state side {:card target :ability 0}) - (effect-completed state side eid))} - card nil)))}}}}}) - -(define-card "Demolisher" - {:in-play [:memory 1] - :events {:pre-trash {:effect (effect (trash-cost-bonus -1))} - :runner-trash {:once :per-turn - :req (req (corp? target)) - :msg "gain 1 [Credits]" - :effect (effect (gain-credits 1))}}}) - -(define-card "Desperado" - {:in-play [:memory 1] - :events {:successful-run {:silent (req true) - :msg "gain 1 [Credits]" :effect (effect (gain-credits 1))}}}) - -(define-card "Dinosaurus" - {:abilities [{:label "Install a non-AI icebreaker on Dinosaurus" - :req (req (empty? (:hosted card))) :cost [:click 1] - :prompt "Select a non-AI icebreaker in your Grip to install on Dinosaurus" - :choices {:req #(and (has-subtype? % "Icebreaker") - (not (has-subtype? % "AI")) - (in-hand? %))} - :effect (effect (runner-install target {:host-card card :no-mu true}) - (update! (assoc-in (get-card state card) [:special :dino-breaker] (:cid target))))} - {:label "Host an installed non-AI icebreaker on Dinosaurus" - :req (req (empty? (:hosted card))) - :prompt "Select an installed non-AI icebreaker to host on Dinosaurus" - :choices {:req #(and (has-subtype? % "Icebreaker") - (not (has-subtype? % "AI")) - (installed? %))} - :msg (msg "host " (:title target)) - :effect (req (free-mu state (:memoryunits target)) - (->> target - (get-card state) - (host state side card) - (update-breaker-strength state side)) - (update! state side (assoc-in (get-card state card) [:special :dino-breaker] (:cid target))))}] - :events {:pre-breaker-strength {:req (req (same-card? target (first (:hosted card)))) - :effect (effect (breaker-strength-bonus 2))} - :card-moved {:req (req (= (:cid target) (get-in (get-card state card) [:special :dino-breaker]))) - :effect (effect (update! (dissoc-in card [:special :dino-breaker])) - (use-mu (:memoryunits target)))}}}) - -(define-card "Doppelgänger" - {:in-play [:memory 1] - :events {:runner-install - {:req (req (= card target)) - :silent (req true) - :effect (effect (update! (assoc card :dopp-active true)))} - :runner-turn-begins - {:effect (effect (update! (assoc card :dopp-active true)))} - :successful-run-ends - {:interactive (req true) - :optional - {:req (req (:dopp-active card)) - :player :runner - :prompt "Use Doppelgänger to run again?" - :yes-ability {:prompt "Choose a server" - :async true - :choices (req runnable-servers) - :msg (msg "make a run on " target) - :makes-run true - :effect (effect (update! (dissoc card :dopp-active)) - (clear-wait-prompt :corp) - (make-run eid target))}}}}}) - -(define-card "Dorm Computer" - {:data {:counter {:power 4}} - :abilities [{:counter-cost [:power 1] - :cost [:click 1] - :req (req (not run)) - :prompt "Choose a server" - :choices (req runnable-servers) - :msg "make a run and avoid all tags for the remainder of the run" - :makes-run true - :effect (effect (update! (assoc card :dorm-active true)) - (make-run target))}] - :events {:pre-tag {:req (req (:dorm-active card)) - :effect (effect (tag-prevent :runner Integer/MAX_VALUE)) - :msg "avoid all tags during the run"} - :run-ends {:effect (effect (update! (dissoc card :dorm-active)))}}}) - -(define-card "Dyson Fractal Generator" - {:recurring 1 - :interactions {:pay-credits {:req (req (and (= :ability (:source-type eid)) - (has-subtype? target "Fracter") - (has-subtype? target "Icebreaker"))) - :type :recurring}}}) - -(define-card "Dyson Mem Chip" - {:in-play [:memory 1 :link 1]}) - -(define-card "e3 Feedback Implants" - {:implementation "Usage restriction not enforced" - :abilities [{:cost [:credit 1] :msg "break 1 additional subroutine"}]}) - -(define-card "Ekomind" - (let [update-base-mu (fn [state n] (swap! state assoc-in [:runner :memory :base] n))] - {:effect (req (update-base-mu state (count (get-in @state [:runner :hand]))) - (add-watch state :ekomind (fn [k ref old new] - (let [hand-size (count (get-in new [:runner :hand]))] - (when (not= (count (get-in old [:runner :hand])) hand-size) - (update-base-mu ref hand-size)))))) - :leave-play (req (remove-watch state :ekomind))})) - -(define-card "EMP Device" - {:abilities [{:req (req (:run @state)) - :msg "prevent the Corp from rezzing more than 1 piece of ICE for the remainder of the run" - :effect (effect (register-events - {:rez {:req (req (ice? target)) - :effect (effect (register-run-flag! - card :can-rez - (fn [state side card] - (if (ice? card) - ((constantly false) - (toast state :corp "Cannot rez ICE the rest of this run due to EMP Device")) - true))))} - :run-ends {:effect (effect (unregister-events card))}} (assoc card :zone '(:discard))) - (trash card {:cause :ability-cost}))}] - :events {:rez nil - :run-ends nil}}) - -(define-card "Feedback Filter" - {:interactions {:prevent [{:type #{:net :brain} - :req (req true)}]} - :abilities [{:cost [:credit 3] - :msg "prevent 1 net damage" - :effect (effect (damage-prevent :net 1))} - {:label "[Trash]: Prevent up to 2 brain damage" - :msg "prevent up to 2 brain damage" - :effect (effect (trash card {:cause :ability-cost}) - (damage-prevent :brain 2))}]}) - -(define-card "Flame-out" - (let [turn-end {:async true - :effect (req (unregister-events state :runner card) - (if-let [hosted (first (:hosted card))] - (do - (system-msg state :runner (str "trashes " (:title hosted) " from Flame-out")) - (trash state side eid hosted nil)) - (effect-completed state side eid)))}] - {:implementation "Credit usage restriction not enforced" - :data {:counter {:credit 9}} - :abilities [{:label "Take 1 [Credits] from Flame-out" - :req (req (and (not-empty (:hosted card)) - (pos? (get-counters card :credit)))) - :counter-cost [:credit 1] - :effect (req (gain-credits state :runner 1) - (system-msg state :runner "takes 1 [Credits] from Flame-out") - (register-events - state :runner - {:runner-turn-ends turn-end - :corp-turn-ends turn-end} - (get-card state card)))} - {:label "Take all [Credits] from Flame-out" - :req (req (and (not-empty (:hosted card)) - (pos? (get-counters card :credit)))) - :effect (req (let [credits (get-counters card :credit)] - (gain-credits state :runner credits) - (update! state :runner (dissoc-in card [:counter :credit])) - (system-msg state :runner (str "takes " credits "[Credits] from Flame-out")) - (register-events - state :runner - {:runner-turn-ends turn-end - :corp-turn-ends turn-end} - (get-card state card))))} - {:label "Install a program on Flame-out" - :req (req (empty? (:hosted card))) - :cost [:click 1] - :prompt "Select a program in your Grip to install on Flame-out" - :choices {:req #(and (program? %) - (in-hand? %))} - :effect (effect (runner-install target {:host-card card}) - (update! (assoc-in (get-card state card) [:special :flame-out] (:cid target))))} - {:label "Host an installed program on Flame-out" - :req (req (empty? (:hosted card))) - :prompt "Select an installed program to host on Flame-out" - :choices {:req #(and (program? %) - (installed? %))} - :msg (msg "host " (:title target)) - :effect (req (->> target - (get-card state) - (host state side card)) - (update! state side (assoc-in (get-card state card) [:special :flame-out] (:cid target))))}] - :events {:card-moved {:req (req (= (:cid target) (get-in (get-card state card) [:special :flame-out]))) - :effect (effect (update! (dissoc-in card [:special :flame-out])))} - :runner-turn-ends nil - :corp-turn-ends nil} - :interactions {:pay-credits {:req (req (and (= :ability (:source-type eid)) - (same-card? card (:host target)) - (pos? (get-counters card :credit)))) - :custom (req (add-counter state side card :credit -1) - (register-events state side - {:runner-turn-ends turn-end - :corp-turn-ends turn-end} - (get-card state card)) - (effect-completed state side (make-result eid 1))) - :type :custom}}})) - -(define-card "Flip Switch" - {:events - {:pre-init-trace - {:async true - :req (req (= :runner (:active-player @state))) - :effect (effect (show-wait-prompt :corp "Runner to use Flip Switch") - (continue-ability - :runner - {:optional - {:prompt "Use Flip Switch to reduce base trace strength to 0?" - :yes-ability {:msg "reduce the base trace strength to 0" - :effect (req (wait-for (trash state side card {:cause :ability-cost}) - (swap! state assoc-in [:trace :force-base] 0) - (effect-completed state side eid)))} - :end-effect (effect (clear-wait-prompt :corp))}} - card nil))}} - :abilities [{:label "Jack out" - :req (req (and run - (= :runner (:active-player @state)))) - :msg "jack out" - :effect (req (wait-for (trash state side card {:cause :ability-cost}) - (jack-out state side eid)))} - {:label "Remove 1 tag" - :req (req (and (pos? (count-tags state)) - (= :runner (:active-player @state)))) - :msg "remove 1 tag" - :effect (req (wait-for (trash state side card {:cause :ability-cost}) - (lose-tags state side eid 1)))}]}) - -(define-card "Forger" - {:interactions {:prevent [{:type #{:tag} - :req (req true)}]} - :in-play [:link 1] - :abilities [{:msg "avoid 1 tag" :label "[Trash]: Avoid 1 tag" - :effect (effect (tag-prevent :runner 1) (trash card {:cause :ability-cost}))} - {:msg "remove 1 tag" :label "[Trash]: Remove 1 tag" - :effect (effect (trash card {:cause :ability-cost}) (lose-tags 1))}]}) - -(define-card "Friday Chip" - (let [ability {:msg (msg "move 1 virus counter to " (:title target)) - :req (req (and (pos? (get-counters card :virus)) - (pos? (count-virus-programs state)))) - :choices {:req is-virus-program?} - :effect (req (add-counter state :runner card :virus -1) - (add-counter state :runner target :virus 1))}] - {:abilities [(set-autoresolve :auto-accept "Friday Chip")] - :effect (effect (toast "Tip: You can toggle automatically adding virus counters by clicking Friday Chip.")) - :events {:runner-turn-begins ability - :runner-trash {:async true - :req (req (some corp? targets)) - :effect (req (let [amt-trashed (count (filter corp? targets)) - sing-ab {:optional {:prompt "Place a virus counter on Friday Chip?" - :autoresolve (get-autoresolve :auto-accept) - :yes-ability {:effect (effect (system-msg - :runner - "places 1 virus counter on Friday Chip") - (add-counter :runner card :virus 1))}}} - mult-ab {:prompt "Place virus counters on Friday Chip?" - :choices {:number (req amt-trashed) - :default (req amt-trashed)} - :effect (effect (system-msg :runner - (str "places " - (quantify target "virus counter") - " on Friday Chip")) - (add-counter :runner card :virus target))} - ab (if (> amt-trashed 1) mult-ab sing-ab)] - (continue-ability state side ab card targets)))}}})) - -(define-card "Gebrselassie" - {:abilities [{:msg (msg "host it on an installed non-AI icebreaker") - :cost [:click 1] - :choices {:req #(and (installed? %) - (has-subtype? % "Icebreaker") - (not (has-subtype? % "AI")))} - :effect (req (when-let [host (get-card state (:host card))] - (update! state side (dissoc-in host [:pump :all-turn])) - (update-breaker-strength state side host)) - (host state side target card))}] - :events {:pump-breaker {:silent (req true) - :req (req (same-card? (second targets) (:host card))) - :effect (effect (update! (update-in (second targets) [:pump :all-turn] (fnil #(+ % (first targets)) 0))) - (update-breaker-strength (second targets)))}} - :leave-play (req (when-let [host (get-card state (:host card))] - (update! state side (dissoc-in host [:pump :all-turn])) - (update-breaker-strength state side host)))}) - -(define-card "GPI Net Tap" - {:implementation "Trash and jack out effect is manual" - :abilities [{:req (req (and (ice? current-ice) (not (rezzed? current-ice)))) - :async true - :effect (effect (expose eid current-ice))}]}) - -(define-card "Grimoire" - {:in-play [:memory 2] - :events {:runner-install {:silent (req true) - :req (req (has-subtype? target "Virus")) - :effect (effect (add-counter target :virus 1))}}}) - -(define-card "Heartbeat" - {:in-play [:memory 1] - :interactions {:prevent [{:type #{:net :brain :meat} - :req (req true)}]} - :abilities [{:msg (msg "prevent 1 damage, trashing a facedown " (:title target)) - :choices {:req #(and (runner? %) (installed? %))} - :priority 50 - :effect (effect (trash target {:unpreventable true}) - (damage-prevent :brain 1) - (damage-prevent :meat 1) - (damage-prevent :net 1))}]}) - -(define-card "Hijacked Router" - {:events {:server-created {:effect (effect (lose-credits :corp 1)) - :msg "force the Corp to lose 1 [Credits]"} - :successful-run {:req (req (= target :archives)) - :optional {:prompt "Trash Hijacked Router to force the Corp to lose 3 [Credits]?" - :yes-ability {:async true - :effect (req (system-msg state :runner "trashes Hijacked Router to force the Corp to lose 3 [Credits]") - (wait-for (trash state :runner card {:unpreventable true}) - (lose-credits state :corp 3) - (effect-completed state side eid)))}}}}}) - -(define-card "Hippo" - {:implementation "Subroutine and first encounter requirements not enforced" - :abilities [{:label "Remove Hippo from the game: trash outermost piece of ICE if all subroutines were broken" - :req (req (and run - (pos? (count run-ices)))) - :async true - :effect (req (let [ice (last run-ices)] - (system-msg - state :runner - (str "removes Hippo from the game to trash " (card-str state ice))) - (move state :runner card :rfg) - (trash state :runner eid ice nil)))}]}) - -(define-card "HQ Interface" - {:in-play [:hq-access 1]}) - -(define-card "Knobkierie" - {:implementation "MU usage restriction not enforced" - :in-play [:memory 3] - :events {:successful-run - {:req (req (and (first-event? state :runner :successful-run) - (pos? (count-virus-programs state)))) - :optional {:prompt "Place a virus counter?" - :autoresolve (get-autoresolve :auto-add) - :yes-ability {:prompt "Select an installed virus program for Knobkierie to add a virus counter to" - :choices {:req #(and (installed? %) - (has-subtype? % "Virus") - (program? %))} - :msg (msg "place 1 virus counter on " (:title target)) - :effect (effect (add-counter target :virus 1))}}}} - :abilities [(set-autoresolve :auto-add "Knobkierie")]}) - -(define-card "Lemuria Codecracker" - {:abilities [{:cost [:click 1 :credit 1] - :req (req (some #{:hq} (:successful-run runner-reg))) - :choices {:req installed?} - :effect (effect (expose eid target)) - :msg "expose 1 card"}]}) - -(define-card "LLDS Memory Diamond" - {:in-play [:link 1 :memory 1 :hand-size 1]}) - -(define-card "LLDS Processor" - (let [llds {:effect (req (let [cards (:llds-target card)] - (update! state side (dissoc card :llds-target)) - (doseq [c cards] - (update-breaker-strength state side - (find-cid (:cid c) (all-active-installed state :runner))))))}] - {:events - {:runner-turn-ends llds :corp-turn-ends llds - :runner-install {:silent (req true) - :req (req (has-subtype? target "Icebreaker")) - :effect (effect (update! (update-in card [:llds-target] #(conj % target))) - (update-breaker-strength target))} - :pre-breaker-strength {:req (req (some #(same-card? target %) (:llds-target card))) - :effect (effect (breaker-strength-bonus 1))}}})) - -(define-card "Lockpick" - {:recurring 1 - :interactions {:pay-credits {:req (req (and (= :ability (:source-type eid)) - (has-subtype? target "Decoder") - (has-subtype? target "Icebreaker"))) - :type :recurring}}}) - -(define-card "Logos" - {:in-play [:memory 1 :hand-size 1] - :events {:agenda-scored - {:player :runner :prompt "Choose a card" :msg (msg "add 1 card to their Grip from their Stack") - :choices (req (cancellable (:deck runner))) - :effect (effect (trigger-event :searched-stack nil) - (shuffle! :deck) - (move target :hand))}}}) - -(define-card "Lucky Charm" - {:interactions {:prevent [{:type #{:end-run} - :req (req (and (some #{:hq} (:successful-run runner-reg)) - (corp? (:card-cause target))))}]} - :abilities [{:msg "prevent the run from ending" - :req (req (some #{:hq} (:successful-run runner-reg))) - :effect (effect (end-run-prevent) - (move card :rfg))}]}) - -(define-card "Mâché" - {:abilities [{:label "Draw 1 card" - :msg "draw 1 card" - :counter-cost [:power 3] - :async true - :effect (effect (draw :runner eid 1 nil))}] - :events {:runner-trash {:once :per-turn - :req (req (and (corp? target) - (:access @state) - (:trash target))) - :effect (effect (system-msg (str "places " (:trash target) " power counters on Mâché")) - (add-counter card :power (:trash target)))}}}) - -(define-card "Masterwork (v37)" - {:in-play [:memory 1] - :events {:run {:optional - {:async true - :interactive (req true) - :req (req (and (<= 1 (:credit runner)) - (some hardware? (:hand runner)))) - :prompt "Pay 1 [Credit] to install a hardware?" - :yes-ability {:async true - :prompt "Select a piece of hardware" - :choices {:req #(and (in-hand? %) - (hardware? %))} - :msg (msg "install " (:title target) " from the grip, paying 1 [Credit] more") - :effect (effect (install-cost-bonus [:credit 1]) - (runner-install eid target nil))}}} - :runner-install {:async true - :req (req (and (hardware? target) - (first-event? state side :runner-install #(hardware? (first %))))) - :effect (effect (draw eid 1 nil))}}}) - -(define-card "Māui" - {:in-play [:memory 2] - :recurring (effect (set-prop card :rec-counter (count (:ices (get-in @state [:corp :servers :hq]))))) - :effect (effect (set-prop card :rec-counter (count (:ices (get-in @state [:corp :servers :hq]))))) - :interactions {:pay-credits {:req (req (= :hq (get-in @state [:run :server 0]))) - :type :recurring}}}) - -(define-card "Maw" - (let [ability {:label "Trash a card from HQ" - :req (req (and (= 1 (get-in @state [:runner :register :no-trash-or-steal])) - (pos? (count (:hand corp))) - (not= (first (:zone target)) :discard))) - :once :per-turn - :msg "force the Corp to trash a random card from HQ" - :effect (req (let [card-to-trash (first (shuffle (:hand corp))) - card-seen? (same-card? target card-to-trash) - card-to-trash (if card-seen? (assoc card-to-trash :seen true) - card-to-trash)] - ;; toggle access flag to prevent Hiro issue #2638 - (swap! state dissoc :access) - (trash state :corp card-to-trash) - (swap! state assoc :access true)))}] - {:in-play [:memory 2] - :abilities [ability] - :events {:post-access-card ability}})) - -(define-card "Maya" - {:in-play [:memory 2] - :abilities [{:once :per-turn - :async true - :label "Move this accessed card to bottom of R&D" - :req (req (when-let [accessed-card (-> @state :runner :prompt first :card)] - (in-deck? accessed-card))) - :msg "move the card just accessed to the bottom of R&D" - :effect (req (let [accessed-card (-> @state :runner :prompt first :card)] - (move state :corp accessed-card :deck) - (wait-for (gain-tags state :runner (make-eid state) 1) - (close-access-prompt state side))))} - {:once :per-turn - :label "Move a previously accessed card to bottom of R&D" - :effect (effect (resolve-ability - {:async true - ;; only allow targeting cards that were accessed this turn - :choices {:req #(some (fn [accessed-card] - (same-card? % accessed-card)) - (map first (turn-events state side :access)))} - :msg (msg "move " (:title target) " to the bottom of R&D") - :effect (req (move state :corp target :deck) - (gain-tags state :runner eid 1) - (swap! state update-in [side :prompt] rest) - (when-let [run (:run @state)] - (when (and (:ended run) - (empty? (get-in @state [:runner :prompt]))) - (handle-end-run state :runner))))} - card nil))}]}) - -(define-card "MemStrips" - {:implementation "MU usage restriction not enforced" - :in-play [:memory 3]}) - -(define-card "Mind's Eye" - {:in-play [:memory 1] - :implementation "Power counters added automatically" - :events {:successful-run {:silent (req true) - :req (req (= target :rd)) - :effect (effect (add-counter card :power 1))}} - :abilities [{:async true - :cost [:click 1] - :counter-cost [:power 3] - :msg "access the top card of R&D" - :effect (req (do-access state side eid [:rd] {:no-root true}))}]}) - -(define-card "Mirror" - {:in-play [:memory 2] - :events {:successful-run - {:async true - :req (req (= target :rd)) - :effect (effect (continue-ability - {:prompt "Select a card and replace 1 spent [Recurring Credits] on it" - :choices {:req #(< (get-counters % :recurring) (:recurring (card-def %) 0))} - :msg (msg "replace 1 spent [Recurring Credits] on " (:title target)) - :effect (effect (add-prop target :rec-counter 1))} - card nil))}}}) - -(define-card "Monolith" - (let [mhelper (fn mh [n] {:prompt "Select a program to install" - :choices {:req #(and (program? %) - (in-hand? %))} - :effect (req (install-cost-bonus state side [:credit -4]) - (runner-install state side target nil) - (when (< n 3) - (resolve-ability state side (mh (inc n)) card nil)))})] - {:interactions {:prevent [{:type #{:net :brain} - :req (req true)}]} - :in-play [:memory 3] - :effect (effect (resolve-ability (mhelper 1) card nil)) - :abilities [{:msg (msg "prevent 1 brain or net damage by trashing " (:title target)) - :priority 50 - :choices {:req #(and (program? %) - (in-hand? %))} - :prompt "Choose a program to trash from your Grip" - :effect (effect (trash target) - (damage-prevent :brain 1) - (damage-prevent :net 1))}]})) - -(define-card "Muresh Bodysuit" - {:events {:pre-damage {:once :per-turn :once-key :muresh-bodysuit - :req (req (= target :meat)) - :msg "prevent the first meat damage this turn" - :effect (effect (damage-prevent :meat 1))}}}) - -(define-card "Net-Ready Eyes" - {:effect (effect (damage eid :meat 2 {:unboostable true :card card})) :msg "suffer 2 meat damage" - :events {:run {:choices {:req #(and (installed? %) - (has-subtype? % "Icebreaker"))} - :msg (msg "give " (:title target) " +1 strength") - :effect (effect (pump target 1 :all-run))}}}) - -(define-card "NetChip" - {:abilities [{:label "Install a program on NetChip" - :req (req (empty? (:hosted card))) - :effect (req (let [n (count (filter #(= (:title %) (:title card)) (all-active-installed state :runner)))] - (resolve-ability - state side - {:cost [:click 1] - :prompt "Select a program in your Grip to install on NetChip" - :choices {:req #(and (program? %) - (runner-can-install? state side % false) - (<= (:memoryunits %) n) - (in-hand? %))} - :msg (msg "host " (:title target)) - :effect (effect (runner-install target {:host-card card :no-mu true}) - (update! (assoc (get-card state card) - :hosted-programs - (cons (:cid target) (:hosted-programs card)))))} - card nil)))} - {:label "Host an installed program on NetChip" - :req (req (empty? (:hosted card))) - :effect (req (let [n (count (filter #(= (:title %) (:title card)) (all-active-installed state :runner)))] - (resolve-ability - state side - {:prompt "Select an installed program to host on NetChip" - :choices {:req #(and (program? %) - (<= (:memoryunits %) n) - (installed? %))} - :msg (msg "host " (:title target)) - :effect (effect (host card target) - (free-mu (:memoryunits target)) - (update! (assoc (get-card state card) - :hosted-programs - (cons (:cid target) (:hosted-programs card)))))} - card nil)))}] - :events {:card-moved {:req (req (some #{(:cid target)} (:hosted-programs card))) - :effect (effect (update! (assoc card - :hosted-programs - (remove #(= (:cid target) %) (:hosted-programs card)))) - (use-mu (:memoryunits target)))}}}) - -(define-card "Obelus" - {:in-play [:memory 1] - :effect (req (change-hand-size state :runner (count-tags state))) - :leave-play (req (change-hand-size state :runner (- (count-tags state)))) - :events {:successful-run-ends {:once :per-turn - :req (req (and (#{:rd :hq} (first (:server target))) - (first-event? state side :successful-run-ends - #(#{:rd :hq} (first (:server (first %))))))) - :msg (msg "draw " (total-cards-accessed target) " cards") - :async true - :effect (effect (draw eid (total-cards-accessed target) nil))} - ;; Events for tracking hand size - :runner-gain-tag {:effect (req (change-hand-size state :runner target))} - :runner-lose-tag {:effect (req (change-hand-size state :runner (- target)))} - :runner-additional-tag-change {:effect (req (change-hand-size state :runner target))}}}) - -(define-card "Omni-drive" - {:recurring 1 - :abilities [{:label "Install and host a program of 1[Memory Unit] or less on Omni-drive" - :req (req (empty? (:hosted card))) - :cost [:click 1] - :prompt "Select a program of 1[Memory Unit] or less to install on Omni-drive from your grip" - :choices {:req #(and (program? %) - (<= (:memoryunits %) 1) - (in-hand? %))} - :msg (msg "host " (:title target)) - :effect (effect (runner-install target {:host-card card :no-mu true}) - (update! (assoc (get-card state card) :Omnidrive-prog (:cid target))))} - {:label "Host an installed program of 1[Memory Unit] or less on Omni-drive" - :prompt "Select an installed program of 1[Memory Unit] or less to host on Omni-drive" - :choices {:req #(and (program? %) - (<= (:memoryunits %) 1) - (installed? %))} - :msg (msg "host " (:title target)) - :effect (effect (host card target) - (free-mu (:memoryunits target)) - (update! (assoc (get-card state card) :Omnidrive-prog (:cid target))))}] - :events {:card-moved {:req (req (= (:cid target) (:Omnidrive-prog (get-card state card)))) - :effect (effect (update! (dissoc card :Omnidrive-prog)) - (use-mu (:memoryunits target)))}} - :interactions {:pay-credits {:req (req (and (= :ability (:source-type eid)) - (program? target) - (same-card? card (:host target)))) - :type :recurring}}}) - -(define-card "Paragon" - {:in-play [:memory 1] - :events {:successful-run - {:req (req (first-event? state side :successful-run)) - :async true - :interactive (get-autoresolve :autofire (complement never?)) - :silent (get-autoresolve :autofire never?) - :effect (effect - (show-wait-prompt :corp "Runner to decide if they will use Paragon") - (continue-ability - {:optional - {:player :runner - :autoresolve (get-autoresolve :auto-fire) - :prompt "Use Paragon?" - :yes-ability - {:msg "gain 1 [Credit] and look at the top card of Stack" - :async true - :effect (effect - (gain-credits :runner 1) - (continue-ability +(def card-definitions + {"Acacia" + {:events {:pre-purge {:effect (req (let [counters (number-of-virus-counters state)] + (update! state side (assoc-in (get-card state card) [:special :numpurged] counters))))} + :purge {:async true + :effect (effect (show-wait-prompt :corp "Runner to decide if they will use Acacia") + (continue-ability + {:optional {:player :runner - :optional - {:prompt (msg "Add " (:title (first (:deck runner))) " to bottom of Stack?") - :yes-ability - {:msg "add the top card of Stack to the bottom" - :effect (effect (move :runner (first (:deck runner)) :deck) - (clear-wait-prompt :corp))} - :no-ability {:effect (effect (clear-wait-prompt :corp))}}} - card nil))} - :no-ability {:effect (effect (clear-wait-prompt :corp) - (system-msg "does not add the top card of the Stack to the bottom"))}}} - card nil))}} - :abilities [(set-autoresolve :auto-fire "Paragon")]}) - -(define-card "Patchwork" - (letfn [(patchwork-discount [cost-type bonus-fn] - {:async true - :req (req (and (get-in card [:special :patchwork]) - (= "Runner" (:side target)) - ;; We need at least one card (that is not the card played) in hand - (not-empty (remove (partial same-card? target) (:hand runner))))) - :effect (req (let [playing target] - (continue-ability - state side - {:prompt (str "Trash a card to lower the " cost-type " cost of " (:title playing) " by 2 [Credits].") - :priority 2 - :choices {:req #(and (in-hand? %) - (runner? %) - (not (same-card? % playing)))} - :msg (msg "trash " (:title target) " to lower the " cost-type " cost of " - (:title playing) " by 2 [Credits]") - :effect (effect (trash target {:unpreventable true}) - (bonus-fn [:credit -2]) - (update! (dissoc-in card [:special :patchwork]))) - :cancel-effect (effect (effect-completed eid))} - card nil)))})] - (let [patchwork-ability {:once :per-turn - :effect (effect (update! (assoc-in card [:special :patchwork] true)) - (toast "Your next card played will trigger Patchwork." "info"))}] - {:in-play [:memory 1] - :implementation "Click Patchwork before playing/installing a card." - :abilities [patchwork-ability] - :events {:pre-play-instant (patchwork-discount "play" play-cost-bonus) - :pre-install (patchwork-discount "install" install-cost-bonus) - :runner-turn-ends {:effect (effect (update! (dissoc-in card [:special :patchwork])))} - :corp-turn-ends {:effect (effect (update! (dissoc-in card [:special :patchwork])))}} - :interactions {:pay-credits {:req (req (and (or (= :play (:source-type eid)) - (= :runner-install (:source-type eid))) - ;; We need at least one card (that is not the card played) in hand - (not-empty (remove (partial same-card? target) (:hand runner))) - ;; Patchwork wasn't used in the traditional way - (not (get-in card [:special :patchwork])) - ;; Check if Patchwork can trigger - (can-trigger? state side patchwork-ability card targets))) - :custom (req (let [cost-type (str (when (= :play (:source-type eid)) "play") - (when (= :runner-install (:source-type eid)) "install")) - patchwork card - targetcard target] - (continue-ability - state side - {:prompt (str "Trash a card to lower the " cost-type - " cost of " (:title patchwork) " by 2 [Credits].") - :priority 2 - :async true - :choices {:req #(and (in-hand? %) - (runner? %) - (not (same-card? % patchwork)))} - :msg (msg "trash " (:title target) " to lower the " cost-type " cost of " - (:title targetcard) " by 2 [Credits]") - :effect (req (trash state side target {:unpreventable true}) - (register-once state patchwork-ability patchwork) - (effect-completed state side (make-result eid 2))) ; provide 2 credits - :cancel-effect (effect (effect-completed (make-result eid 0)))} ; provide 0 credits - nil nil))) - :type :custom}}}))) - -(define-card "Plascrete Carapace" - {:data [:counter {:power 4}] - :interactions {:prevent [{:type #{:meat} - :req (req true)}]} - :abilities [{:counter-cost [:power 1] - :msg "prevent 1 meat damage" - :effect (req (damage-prevent state side :meat 1) - (when (zero? (get-counters (get-card state card) :power)) - (trash state side card {:unpreventable true})))}]}) - -(define-card "Polyhistor" - (let [abi {:optional - {:prompt "Draw 1 card to force the Corp to draw 1 card?" - :yes-ability {:msg "draw 1 card and force the Corp to draw 1 card" - :async true - :effect (req (wait-for (draw state :runner 1) - (draw state :corp eid 1 nil)))} - :no-ability {:effect (req (system-msg state side (str "does not use Polyhistor")) - (effect-completed state side eid))}}}] - {:in-play [:link 1 :memory 1] - :events {:pass-ice {:req (req (and (= (:server run) [:hq]) - (= (:position run) 1) ; trigger when last ICE passed - (pos? (count (:deck runner))))) - :async true - :once :per-turn - :effect (req (continue-ability state :runner abi card nil))} - :run {:req (req (and (= (:server run) [:hq]) - (zero? (:position run)) ; trigger on unprotected HQ - (pos? (count (:deck runner))))) - :async true - :once :per-turn - :effect (req (continue-ability state :runner abi card nil))}}})) - -(define-card "Prepaid VoicePAD" - {:recurring 1 - :interactions {:pay-credits {:req (req (= :play (:source-type eid))) - :type :recurring}}}) - -(define-card "Public Terminal" - {:recurring 1 - :interactions {:pay-credits {:req (req (and (= :play (:source-type eid)) - (has-subtype? target "Run"))) - :type :recurring}}}) - -(define-card "Q-Coherence Chip" - {:in-play [:memory 1] - :events (let [e {:req (req (= (last (:zone target)) :program)) - :effect (effect (trash card) - (system-msg (str "trashes Q-Coherence Chip")))}] - {:runner-trash e :corp-trash e})}) - -(define-card "Qianju PT" - {:flags {:runner-phase-12 (req true)} - :abilities [{:label "Lose [Click], avoid 1 tag (start of turn)" - :once :per-turn - :req (req (:runner-phase-12 @state)) - :effect (effect (update! (assoc card :qianju-active true))) - :msg "lose [Click] and avoid the first tag received until their next turn"}] - :events {:corp-turn-ends {:effect (effect (update! (dissoc card :qianju-active)))} - :runner-turn-begins {:req (req (:qianju-active card)) - :effect (effect (lose :click 1))} - :pre-tag {:req (req (:qianju-active card)) - :msg "avoid the first tag received" - :effect (effect (tag-prevent :runner 1) - (update! (dissoc card :qianju-active)))}}}) - -(define-card "R&D Interface" - {:in-play [:rd-access 1]}) - -(define-card "Rabbit Hole" - {:in-play [:link 1] - :effect - (effect (resolve-ability - {:optional {:req (req (some #(when (= (:title %) "Rabbit Hole") %) (:deck runner))) - :prompt "Install another Rabbit Hole?" :msg "install another Rabbit Hole" - :yes-ability {:effect (req (when-let [c (some #(when (= (:title %) "Rabbit Hole") %) - (:deck runner))] - (trigger-event state side :searched-stack nil) - (shuffle! state :runner :deck) - (runner-install state side c)))}}} card nil))}) - -(define-card "Ramujan-reliant 550 BMI" - {:interactions {:prevent [{:type #{:net :brain} - :req (req true)}]} - :abilities [{:req (req (not-empty (:deck runner))) - :effect (req (let [n (count (filter #(= (:title %) (:title card)) (all-active-installed state :runner)))] - (resolve-ability - state side - {:prompt "Choose how much damage to prevent" - :priority 50 - :choices {:number (req (min n (count (:deck runner))))} - :msg (msg "trash " (join ", " (map :title (take target (:deck runner)))) - " from their Stack and prevent " target " damage") - :effect (effect (damage-prevent :net target) - (damage-prevent :brain target) - (mill :runner target) - (trash card {:cause :ability-cost}))} card nil)))}]}) - -(define-card "Recon Drone" - ; eventmap uses reverse so we get the most recent event of each kind into map - (letfn [(eventmap [s] - (into {} (reverse (get s :turn-events))))] - {:interactions {:prevent [{:type #{:net :brain :meat} - :req (req (:access @state))}]} - :abilities [{:req (req (= (:cid (second (:pre-damage (eventmap @state)))) - (:cid (first (:pre-access-card (eventmap @state)))))) - :effect (effect (resolve-ability - {:prompt "Choose how much damage to prevent" - :priority 50 - :choices {:number (req (min (last (:pre-damage (eventmap @state))) - (:credit runner)))} - :msg (msg "prevent " target " damage") - :effect (effect (trash card {:cause :ability-cost}) - (damage-prevent (first (:pre-damage (eventmap @state))) target) - (lose-credits target))} - card nil))}]})) - -(define-card "Record Reconstructor" - {:events - {:successful-run - {:req (req (= (get-in @state [:run :server]) [:archives])) - :effect (req (let [rr card] - (swap! state assoc-in [:run :run-effect :replace-access] - {:effect (effect (resolve-ability - {:prompt "Choose one faceup card to add to the top of R&D" - :choices (req (filter #(:seen %) (:discard corp))) - :msg (msg "add " (:title target) " to the top of R&D") - :effect (req (move state :corp target :deck {:front true}))} - rr nil))})))}}}) - -(define-card "Reflection" - {:in-play [:memory 1 :link 1] - :events {:jack-out {:effect (req (let [card (first (shuffle (:hand corp)))] - (reveal state :corp card) - (system-msg state :runner (str "force the Corp to reveal " (:title card) " from HQ"))))}}}) - -(define-card "Replicator" - (letfn [(hardware-and-in-deck? [target runner] - (and (hardware? target) - (some #(= (:title %) (:title target)) (:deck runner))))] - {:events {:runner-install - {:interactive (req (hardware-and-in-deck? target runner)) - :silent (req (not (hardware-and-in-deck? target runner))) - :optional {:prompt "Use Replicator to add a copy?" - :req (req (hardware-and-in-deck? target runner)) - :yes-ability {:msg (msg "add a copy of " (:title target) " to their Grip") - :effect (effect (trigger-event :searched-stack nil) - (shuffle! :deck) - (move (some #(when (= (:title %) (:title target)) %) - (:deck runner)) :hand))}}}}})) - -(define-card "Respirocytes" - (let [ability {:once :per-turn - :msg "draw 1 card and add a power counter to itself" + :prompt "Use Acacia?" + :yes-ability {:effect (req (let [counters (- (get-in (get-card state card) [:special :numpurged]) + (number-of-virus-counters state))] + (wait-for (trash state side card nil) + (gain-credits state side counters) + (system-msg state side (str "uses Acacia and gains " counters "[Credit]")) + (clear-wait-prompt state :corp) + (effect-completed state side eid))))} + :no-ability {:effect (effect (clear-wait-prompt :corp) + (effect-completed eid))}}} + card nil))}}} + + "Adjusted Matrix" + {:implementation "Click Adjusted Matrix to use ability." + :req (req (not-empty (filter #(has-subtype? % "Icebreaker") (all-active-installed state :runner)))) + :prompt "Choose Icebreaker on which to install Adjusted Matrix" + :choices {:req #(and (runner? %) (has-subtype? % "Icebreaker") (installed? %))} + :msg (msg "host it on " (card-str state target)) + :effect (effect (update! (assoc target :subtype (combine-subtypes false (-> target :subtype) "AI"))) + (host (get-card state target) (get-card state card))) + :abilities [{:cost [:click 1] + :req (req run) + :msg "break ice subroutine"}] + :events {:pre-card-moved {:req (req (same-card? target card)) + :effect (effect (update! (assoc (-> card :host) :subtype (-> card :host :subtype (remove-subtypes-once ["AI"])))))}}} + + "Akamatsu Mem Chip" + {:in-play [:memory 1]} + + "Archives Interface" + {:events + {:pre-access + {:async true + :interactive (req true) + :req (req (and (= target :archives) + (not= (:max-access run) 0) + (not-empty (:discard corp)))) + :effect (req (swap! state update-in [:corp :discard] #(map (fn [c] (assoc c :seen true)) %)) + (continue-ability + state side + {:optional + {:prompt "Use Archives Interface to remove a card from the game instead of accessing it?" + :yes-ability {:prompt "Choose a card in Archives to remove from the game instead of accessing" + :choices (req (:discard corp)) + :msg (msg "remove " (:title target) " from the game") + :effect (effect (move :corp target :rfg))}}} card nil))}}} + + "Astrolabe" + {:in-play [:memory 1] + :events {:server-created {:msg "draw 1 card" + :async true + :effect (effect (draw :runner eid 1 nil))}}} + + "Autoscripter" + {:events {:runner-install {:silent (req true) + :req (req (and (program? target) + ;; only trigger on Runner's turn + (= (:active-player @state) :runner) + ;; only trigger when playing a Program from grip + (some #{:hand} (:previous-zone target)) + ;; check that we haven't played a Program from the grip this turn + ;; which translates to just one case of playing a Program in turn-events + (first-event? state :runner :runner-install + (fn [[card _]] (and (some #{:hand} (:previous-zone card)) + (program? card)))))) + :msg "gain [Click]" + :effect (effect (gain :click 1))} + :unsuccessful-run {:effect (effect (trash card) + (system-msg "trashes Autoscripter"))}}} + + "Blackguard" + {:in-play [:memory 2] + :events {:expose + {:msg (msg "attempt to force the rez of " (:title target)) + :async true + :effect (req (trigger-event state side :pre-rez-cost target) + (let [c target + cdef (card-def c) + cname (:title c) + cost (rez-cost state side target) + additional-costs (concat (:additional-cost cdef) + (:additional-cost card) + (get-rez-additional-cost-bonus state side))] + (swap! state update-in [:bonus] dissoc :cost :rez) + (if (seq additional-costs) + (do (show-wait-prompt state :runner (str "Corp to decide if they will rez " cname)) + (continue-ability + state side + {:optional + {:prompt (msg (build-cost-str [:credit cost]) + ", plus " (build-cost-str additional-costs) + " as an additional cost to rez " cname "?") + :player :corp + :yes-ability {:effect (effect (rez :corp c))} + :no-ability {:msg (msg "declines to pay additional costs" + " and is not forced to rez " cname)} + :end-effect (effect (clear-wait-prompt :runner))}} + card nil)) + (do (rez state :corp target) + (effect-completed state side eid)))))}}} + + "Bookmark" + {:abilities [{:label "Host up to 3 cards from your Grip facedown" + :cost [:click 1] :msg "host up to 3 cards from their Grip facedown" + :choices {:max 3 + :req #(and (runner? %) + (in-hand? %))} + :effect (req (doseq [c targets] + (host state side (get-card state card) c {:facedown true})))} + {:label "Add all hosted cards to Grip" :cost [:click 1] :msg "add all hosted cards to their Grip" + :effect (req (doseq [c (:hosted card)] + (move state side c :hand)))} + {:label "[Trash]: Add all hosted cards to Grip" :msg "add all hosted cards to their Grip" + :effect (req (doseq [c (:hosted card)] + (move state side c :hand)) + (update! state side (dissoc card :hosted)) + (trash state side (get-card state card) {:cause :ability-cost}))}]} + + "Box-E" + {:in-play [:memory 2 :hand-size 2]} + + "Brain Cage" + {:in-play [:hand-size 3] + :effect (effect (damage eid :brain 1 {:card card}))} + + "Brain Chip" + (let [runner-points (fn [s] (max (get-in s [:runner :agenda-point] 0) 0))] + {:effect (req (gain state :runner + :memory (runner-points @state) + :hand-size (runner-points @state)) + (add-watch state (keyword (str "brainchip" (:cid card))) + (fn [k ref old new] + (let [bonus (- (runner-points new) (runner-points old))] + (when-not (zero? bonus) + (gain state :runner + :memory bonus + :hand-size bonus)))))) + :leave-play (req (remove-watch state (keyword (str "brainchip" (:cid card)))) + (lose state :runner + :memory (runner-points @state) + :hand-size (runner-points @state)))}) + + "Capstone" + {:abilities [{:req (req (pos? (count (:hand runner)))) + :cost [:click 1] + :effect (req (let [handsize (count (:hand runner))] + (resolve-ability + state side + {:prompt "Select any number of cards to trash from your Grip" + :choices {:max handsize + :req #(and (runner? %) + (in-hand? %))} + :effect (req (let [trashed (count targets) + remaining (- handsize trashed)] + (doseq [c targets] + (when (not (empty? (filter #(= (:title c) (:title %)) + (all-active-installed state :runner)))) + (draw state side))) + (trash-cards state side targets) + (system-msg state side + (str "spends [Click] to use Capstone to trash " + (join ", " (map :title targets)) " and draw " + (- (count (get-in @state [:runner :hand])) remaining) " cards"))))} + card nil)))}]} + + "Chop Bot 3000" + {:flags {:runner-phase-12 (req (>= 2 (count (all-installed state :runner))))} + :abilities [{:req (req (:runner-phase-12 @state)) + :msg (msg "trash " (:title target)) + :choices {:req #(and (runner? %) + (installed? %)) + :not-self true} :async true - :effect (req (wait-for (draw state :runner 1 nil) - (do (add-counter state side (get-card state card) :power 1) - (if (= (get-counters (get-card state card) :power) 3) - (do (system-msg state :runner "trashes Respirocytes as it reached 3 power counters") - (trash state side eid card {:unpreventable true})) - (effect-completed state side eid)))))} - watch-id (fn [card] (keyword (str "respirocytes-" (:cid card))))] - {:effect (req (add-watch state (watch-id card) - (fn [k ref old new] - (when (and (seq (get-in old [:runner :hand])) - (empty? (get-in new [:runner :hand]))) - (resolve-ability ref side ability card nil)))) - (damage state side eid :meat 1 {:unboostable true :card card})) - :msg "suffer 1 meat damage" - :trash-effect {:effect (req (remove-watch state (watch-id card)))} - :leave-play (req (remove-watch state (watch-id card))) - :events {:runner-turn-begins {:req (req (empty? (get-in @state [:runner :hand]))) - :effect (effect (resolve-ability ability card nil))} - :corp-turn-begins {:req (req (empty? (get-in @state [:runner :hand]))) - :effect (effect (resolve-ability ability card nil))}}})) - -(define-card "Rubicon Switch" - {:abilities [{:cost [:click 1] - :once :per-turn - :async true - :prompt "How many [Credits]?" :choices :credit - :effect (effect (system-msg (str "spends a [Click] and " target " [Credit] on Rubicon Switch")) - (resolve-ability {:choices {:req #(and (ice? %) - (= :this-turn (:rezzed %)) - (<= (:cost %) target))} - :effect (effect (derez target)) - :msg (msg "derez " (:title target))} card nil))}]}) - -(define-card "Security Chip" - {:abilities [{:label "[Trash]: Add [Link] strength to a non-Cloud icebreaker until the end of the run" - :msg (msg "add " (:link runner) " strength to " (:title target) " until the end of the run") - :req (req (:run @state)) - :prompt "Select one non-Cloud icebreaker" - :choices {:req #(and (has-subtype? % "Icebreaker") - (not (has-subtype? % "Cloud")) - (installed? %))} - :effect (effect (pump target (:link runner) :all-run) - (trash (get-card state card) {:cause :ability-cost}))} - {:label "[Trash]: Add [Link] strength to any Cloud icebreakers until the end of the run" - :msg (msg "add " (:link runner) " strength to " (count targets) " Cloud icebreakers until the end of the run") - :req (req (:run @state)) - :prompt "Select any number of Cloud icebreakers" - :choices {:max 50 - :req #(and (has-subtype? % "Icebreaker") - (has-subtype? % "Cloud") - (installed? %))} - :effect (req (doseq [t targets] - (pump state side t (:link runner) :all-run) - (update-breaker-strength state side t)) - (trash state side (get-card state card) {:cause :ability-cost}))}]}) - -(define-card "Security Nexus" - {:implementation "Bypass is manual" - :in-play [:memory 1 :link 1] - :abilities [{:req (req (:run @state)) - :once :per-turn - :async true - :msg "force the Corp to initiate a trace" - :label "Trace 5 - Give the Runner 1 tag and end the run" - :trace {:base 5 - :successful {:msg "give the Runner 1 tag and end the run" - :effect (effect (gain-tags :runner eid 1) - (end-run))} - :unsuccessful {:msg "bypass the current ICE"}}}]}) - -(define-card "Severnius Stim Implant" - (letfn [(implant-fn [srv kw] - {:prompt "Choose at least 2 cards in your Grip to trash with Severnius Stim Implant" - :choices {:max (req (count (:hand runner))) - :req #(and (runner? %) - (in-hand? %))} - :msg (msg "trash " (quantify (count targets) "card") - " and access " (quantify (quot (count targets) 2) "additional card")) - :effect (req (let [bonus (quot (count targets) 2)] - (wait-for (trash-cards state side targets {:unpreventable true - :suppress-event true}) - (make-run state side srv nil card) - (register-events - state side - {:pre-access {:silent (req true) - :effect (effect (access-bonus kw bonus))} - :run-ends {:effect (effect (unregister-events card))}} - card) - (effect-completed state side eid))))})] - {:abilities [{:req (req (<= 2 (count (:hand runner)))) - :cost [:click 1] - :prompt "Choose a server to run with Severnius Stim Implant" - :choices ["HQ" "R&D"] - :effect (effect (continue-ability (implant-fn target (if (= target "HQ") :hq :rd)) card nil))}] - :events {:pre-access nil - :run-ends nil}})) - -(define-card "Şifr" - {:in-play [:memory 2] - :abilities [{:once :per-turn - :req (req (rezzed? current-ice)) - :msg (msg "lower their maximum hand size by 1 and lower the strength of " (:title current-ice) " to 0") - :effect (effect (lose :runner :hand-size 1) - (update! (assoc card :sifr-target current-ice :sifr-used true)) - (update-ice-strength current-ice))}] - :events {:runner-turn-begins {:req (req (:sifr-used card)) - :effect (effect (gain :runner :hand-size 1) - (update! (dissoc card :sifr-used)))} - :pre-ice-strength {:req (req (= (:cid target) (get-in card [:sifr-target :cid]))) - :effect (req (let [ice-str (:current-strength target)] - (ice-strength-bonus state side (- ice-str) target)))} - :run-ends {:effect (effect (update! (dissoc card :sifr-target)))}}}) - -(define-card "Silencer" - {:recurring 1 - :interactions {:pay-credits {:req (req (and (= :ability (:source-type eid)) - (has-subtype? target "Killer") - (has-subtype? target "Icebreaker"))) - :type :recurring}}}) - -(define-card "Skulljack" - {:effect (effect (damage eid :brain 1 {:card card})) - :events {:pre-trash {:effect (effect (trash-cost-bonus -1))}}}) - -(define-card "Spinal Modem" - {:in-play [:memory 1] - :recurring 2 - :events {:successful-trace {:req (req run) - :effect (effect (system-msg (str "suffers 1 brain damage from Spinal Modem")) - (damage eid :brain 1 {:card card}))}} - :interactions {:pay-credits {:req (req (and (= :ability (:source-type eid)) - (has-subtype? target "Icebreaker"))) - :type :recurring}}}) - -(define-card "Sports Hopper" - {:in-play [:link 1] - :abilities [{:label "Draw 3 cards" - :msg "draw 3 cards" - :async true - :effect (req (wait-for (trash state :runner card {:cause :ability-cost}) (draw state :runner eid 3 nil)))}]}) - -(define-card "Spy Camera" - {:abilities [{:cost [:click 1] - :async true - :label "Look at the top X cards of your Stack" - :msg "look at the top X cards of their Stack and rearrange them" - :effect (req (show-wait-prompt state :corp "Runner to rearrange the top cards of their stack") - (let [n (count (filter #(= (:title %) (:title card)) - (all-active-installed state :runner))) - from (take n (:deck runner))] - (if (pos? (count from)) - (continue-ability state side (reorder-choice :runner :corp from '() - (count from) from) card nil) - (do (clear-wait-prompt state :corp) - (effect-completed state side eid)))))} - {:label "[Trash]: Look at the top card of R&D" - :msg "trash it and look at the top card of R&D" - :effect (effect (prompt! card (str "The top card of R&D is " (:title (first (:deck corp)))) ["OK"] {}) - (trash card {:cause :ability-cost}))}]}) - -(define-card "Supercorridor" - {:in-play [:memory 2 :hand-size 1] - :events {:runner-turn-ends - {:req (req (= (:credit runner) (:credit corp))) - :async true - :effect (req (show-wait-prompt - state :corp - "Runner to decide if they will gain credits from Supercorridor") - (continue-ability - state :runner - {:optional - {:prompt "Gain credits from Supercorridor?" - :player :runner - :yes-ability {:msg "gain 2 [Credits]" - :effect (req (gain-credits state :runner 2) - (clear-wait-prompt state :corp))} - :no-ability {:effect (req (system-msg - state :runner - "chooses not to gain 2 [Credits] from Supercorridor") - (clear-wait-prompt state :corp))}}} - card nil))}}}) - -(define-card "The Gauntlet" - {:implementation "Requires Runner to manually (and honestly) set how many ICE were broken directly protecting HQ" - :in-play [:memory 2] - :events {:post-successful-run {:req (req (and (= :hq target) - run)) - :silent (req true) - :async true - :effect (effect (continue-ability - {:prompt "How many ICE protecting HQ did you break all subroutines on?" - ;; Makes number of ice on server (HQ) the upper limit. - ;; This should work since trashed ice do not count according to UFAQ - :choices {:number (req (count (get-in @state [:corp :servers :hq :ices])))} - :effect (effect (access-bonus :hq target))} - card nil))}}}) - -(define-card "The Personal Touch" - {:hosting {:req #(and (has-subtype? % "Icebreaker") - (installed? %))} - :effect (effect (update-breaker-strength (:host card))) - :events {:pre-breaker-strength {:req (req (same-card? target (:host card))) - :effect (effect (breaker-strength-bonus 1))}}}) - -(define-card "The Toolbox" - {:in-play [:link 2 :memory 2] - :recurring 2 - :interactions {:pay-credits {:req (req (and (= :ability (:source-type eid)) - (has-subtype? target "Icebreaker"))) - :type :recurring}}}) - -(define-card "Titanium Ribs" - {:events - {:pre-resolve-damage - {:async true - :req (req (and (pos? (last targets)) - (runner-can-choose-damage? state) - (not (get-in @state [:damage :damage-replace])))) - :effect (req (let [dtype target - src (second targets) - dmg (last targets)] - (when (> dmg (count (:hand runner))) - (flatline state)) - (when (= dtype :brain) - (swap! state update-in [:runner :brain-damage] #(+ % dmg)) - (swap! state update-in [:runner :hand-size :mod] #(- % dmg))) - (show-wait-prompt state :corp "Runner to use Titanium Ribs to choose cards to be trashed") - (wait-for (resolve-ability - state side - {:async true - :prompt (msg "Select " dmg " cards to trash for the " (name dtype) " damage") - :player :runner - :choices {:max dmg - :all true - :req #(and (in-hand? %) - (runner? %))} - :msg (msg "trash " (join ", " (map :title targets))) - :effect (req (clear-wait-prompt state :corp) - (doseq [c targets] - (trash state side c {:cause dtype :unpreventable true})) - (trigger-event state side :damage-chosen) - (damage-defer state side dtype 0) - (effect-completed state side eid))} - card nil) - (trigger-event-sync state side eid :damage dtype src dmg))))} - :damage-chosen {:effect (effect (enable-runner-damage-choice))}} - :async true - :effect (effect (enable-runner-damage-choice) - (system-msg (str "suffers 2 meat damage from installing Titanium Ribs")) - (damage eid :meat 2 {:unboostable true :card card})) - :leave-play (req (swap! state update-in [:damage] dissoc :damage-choose-runner))}) - -(define-card "Top Hat" - (letfn [(ability [n] - {:async true - :mandatory true - :req (req (not= (:max-access run) 0)) - :prompt "Which card from the top of R&D would you like to access? (Card 1 is on top.)" - :choices (take n ["1" "2" "3" "4" "5"]) - :effect (effect (system-msg (str "accesses the card at position " (str->int target) " of R&D")) - (access-card eid (nth (:deck corp) (dec (str->int target))) "an unseen card"))})] - {:events {:successful-run - {:req (req (= target :rd)) - :interactive (req true) - :optional {:prompt "Use Top Hat to choose one of the top 5 cards in R&D to access?" - :yes-ability {:effect (req (swap! state assoc-in [:run :run-effect :replace-access] - (ability (count (:deck corp)))))}}}}})) - -(define-card "Turntable" - {:in-play [:memory 1] - :events {:agenda-stolen - {:interactive (req true) - :req (req (not (empty? (:scored corp)))) - :async true - :effect (req - (let [stolen target] - (continue-ability - state side - {:optional - {:prompt (msg "Swap " (:title stolen) " for an agenda in the Corp's score area?") - :yes-ability - {:async true - :effect (req + :effect (req (wait-for (trash state :runner target nil) (continue-ability state side - {:prompt (str "Select a scored Corp agenda to swap with " (:title stolen)) - :choices {:req #(in-corp-scored? state side %)} - :effect (req (let [scored target] - (swap-agendas state side scored stolen) - (system-msg state side (str "uses Turntable to swap " - (:title stolen) " for " (:title scored))) - (effect-completed state side eid)))} - card targets))}}} - card targets)))}}}) - -(define-card "Ubax" - (let [ability {:req (req (:runner-phase-12 @state)) - :msg "draw 1 card" - :label "Draw 1 card (start of turn)" + (let [deck (pos? (count (:deck runner))) + tags (pos? (count-tags state))] + {:req (req (or deck tags)) + :prompt "Draw 1 card or remove 1 tag" + :choices (concat (when deck ["Draw 1 card"]) + (when tags ["Remove 1 tag"])) + :async true + :effect (req (if (= target "Draw 1 card") + (draw state side eid 1 nil) + (do (lose-tags state :runner 1) + (effect-completed state side eid))))}) + card nil)))}]} + + "Clone Chip" + {:abilities [{:prompt "Select a program to install from your Heap" + :priority true + :show-discard true + :req (req (and (not (seq (get-in @state [:runner :locked :discard]))) + (not (install-locked? state side)))) + :choices {:req #(and (program? %) + (in-discard? %))} + :effect (req (when (>= (:credit runner) (:cost target)) + (runner-install state side target) + (trash state side card {:cause :ability-cost}) + (system-msg state side (str "uses " (:title card) " to install " (:title target)))))}]} + + "Comet" + {:in-play [:memory 1] + :events {:play-event {:req (req (first-event? state side :play-event)) + :effect (req (system-msg state :runner + (str "can play another event without spending a [Click] by clicking on Comet")) + (update! state side (assoc card :comet-event true)))}} + :abilities [{:req (req (:comet-event card)) + :prompt "Select an Event in your Grip to play" + :choices {:req #(and (event? %) + (in-hand? %))} + :msg (msg "play " (:title target)) + :effect (effect (play-instant target) + (update! (dissoc (get-card state card) :comet-event)))}]} + + "Cortez Chip" + {:abilities [{:prompt "Select a piece of ICE" + :choices {:req ice?} + :msg (msg "trashes Cortez Chip to increase the rez cost of " (card-str state target) + " by 2 [Credits] until the end of the turn") + :effect (effect (update! (assoc card :cortez-target target)) + (trash (get-card state card) {:cause :ability-cost}))}] + :trash-effect {:effect (effect (register-events {:pre-rez {:req (req (same-card? target (:cortez-target card))) + :effect (effect (rez-additional-cost-bonus [:credit 2]))} + :runner-turn-ends {:effect (effect (unregister-events card))} + :corp-turn-ends {:effect (effect (unregister-events card))}} + (get-card state card)))} + :events {:pre-rez nil :runner-turn-ends nil :corp-turn-ends nil}} + + "Cyberdelia" + {:implementation "Credit gain is manually triggered." + :in-play [:memory 1] + :abilities [{:msg "gain 1 [Credits] for breaking all subroutines on a piece of ice" :once :per-turn + :effect (effect (gain-credits 1))}]} + + "Cyberfeeder" + {:recurring 1 + :interactions {:pay-credits {:req (req (or (and (= :runner-install (:source-type eid)) + (has-subtype? target "Virus") + (program? target)) + (and (= :ability (:source-type eid)) + (has-subtype? target "Icebreaker")))) + :type :recurring}}} + + "CyberSolutions Mem Chip" + {:in-play [:memory 2]} + + "Cybsoft MacroDrive" + {:recurring 1 + :interactions {:pay-credits {:req (req (and (= :runner-install (:source-type eid)) + (program? target))) + :type :recurring}}} + + "Daredevil" + {:in-play [:memory 2] + :events {:run-big {:once :per-turn + :req (req (first-event? state side :run-big)) + :msg "draw two cards" + :async true + :effect (effect (draw eid 2 nil))}}} + + "Dedicated Processor" + {:implementation "Click Dedicated Processor to use ability" + :req (req (not-empty (filter #(has-subtype? % "Icebreaker") (all-active-installed state :runner)))) + :hosting {:req #(and (has-subtype? % "Icebreaker") + (not (has-subtype? % "AI")) + (installed? %))} + :abilities [{:cost [:credit 2] + :req (req run) + :effect (effect (pump (get-card state (:host card)) 4)) + :msg (msg (str "pump the strength of " (get-in card [:host :title]) " by 4"))}]} + + "Deep Red" + {:implementation "MU use restriction not enforced" + :in-play [:memory 3] + :events {:runner-install + {:optional + {:req (req (has-subtype? target "Caïssa")) + :prompt "Use Deep Red?" :priority 1 + :yes-ability {:async true + :effect (req (let [cid (:cid target)] + (continue-ability + state side + {:async true + :prompt "Choose the just-installed Caïssa to have Deep Red trigger its [Click] ability" + :choices {:req #(= cid (:cid %))} + :msg (msg "trigger the [Click] ability of " (:title target) + " without spending [Click]") + :effect (req (gain state :runner :click 1) + (play-ability state side {:card target :ability 0}) + (effect-completed state side eid))} + card nil)))}}}}} + + "Demolisher" + {:in-play [:memory 1] + :events {:pre-trash {:effect (effect (trash-cost-bonus -1))} + :runner-trash {:once :per-turn + :req (req (corp? target)) + :msg "gain 1 [Credits]" + :effect (effect (gain-credits 1))}}} + + "Desperado" + {:in-play [:memory 1] + :events {:successful-run {:silent (req true) + :msg "gain 1 [Credits]" :effect (effect (gain-credits 1))}}} + + "Dinosaurus" + {:abilities [{:label "Install a non-AI icebreaker on Dinosaurus" + :req (req (empty? (:hosted card))) :cost [:click 1] + :prompt "Select a non-AI icebreaker in your Grip to install on Dinosaurus" + :choices {:req #(and (has-subtype? % "Icebreaker") + (not (has-subtype? % "AI")) + (in-hand? %))} + :effect (effect (runner-install target {:host-card card :no-mu true}) + (update! (assoc-in (get-card state card) [:special :dino-breaker] (:cid target))))} + {:label "Host an installed non-AI icebreaker on Dinosaurus" + :req (req (empty? (:hosted card))) + :prompt "Select an installed non-AI icebreaker to host on Dinosaurus" + :choices {:req #(and (has-subtype? % "Icebreaker") + (not (has-subtype? % "AI")) + (installed? %))} + :msg (msg "host " (:title target)) + :effect (req (free-mu state (:memoryunits target)) + (->> target + (get-card state) + (host state side card) + (update-breaker-strength state side)) + (update! state side (assoc-in (get-card state card) [:special :dino-breaker] (:cid target))))}] + :events {:pre-breaker-strength {:req (req (same-card? target (first (:hosted card)))) + :effect (effect (breaker-strength-bonus 2))} + :card-moved {:req (req (= (:cid target) (get-in (get-card state card) [:special :dino-breaker]))) + :effect (effect (update! (dissoc-in card [:special :dino-breaker])) + (use-mu (:memoryunits target)))}}} + + "Doppelgänger" + {:in-play [:memory 1] + :events {:runner-install + {:req (req (= card target)) + :silent (req true) + :effect (effect (update! (assoc card :dopp-active true)))} + :runner-turn-begins + {:effect (effect (update! (assoc card :dopp-active true)))} + :successful-run-ends + {:interactive (req true) + :optional + {:req (req (:dopp-active card)) + :player :runner + :prompt "Use Doppelgänger to run again?" + :yes-ability {:prompt "Choose a server" + :async true + :choices (req runnable-servers) + :msg (msg "make a run on " target) + :makes-run true + :effect (effect (update! (dissoc card :dopp-active)) + (clear-wait-prompt :corp) + (make-run eid target))}}}}} + + "Dorm Computer" + {:data {:counter {:power 4}} + :abilities [{:counter-cost [:power 1] + :cost [:click 1] + :req (req (not run)) + :prompt "Choose a server" + :choices (req runnable-servers) + :msg "make a run and avoid all tags for the remainder of the run" + :makes-run true + :effect (effect (update! (assoc card :dorm-active true)) + (make-run target))}] + :events {:pre-tag {:req (req (:dorm-active card)) + :effect (effect (tag-prevent :runner Integer/MAX_VALUE)) + :msg "avoid all tags during the run"} + :run-ends {:effect (effect (update! (dissoc card :dorm-active)))}}} + + "Dyson Fractal Generator" + {:recurring 1 + :interactions {:pay-credits {:req (req (and (= :ability (:source-type eid)) + (has-subtype? target "Fracter") + (has-subtype? target "Icebreaker"))) + :type :recurring}}} + + "Dyson Mem Chip" + {:in-play [:memory 1 :link 1]} + + "e3 Feedback Implants" + {:implementation "Usage restriction not enforced" + :abilities [{:cost [:credit 1] :msg "break 1 additional subroutine"}]} + + "Ekomind" + (let [update-base-mu (fn [state n] (swap! state assoc-in [:runner :memory :base] n))] + {:effect (req (update-base-mu state (count (get-in @state [:runner :hand]))) + (add-watch state :ekomind (fn [k ref old new] + (let [hand-size (count (get-in new [:runner :hand]))] + (when (not= (count (get-in old [:runner :hand])) hand-size) + (update-base-mu ref hand-size)))))) + :leave-play (req (remove-watch state :ekomind))}) + + "EMP Device" + {:abilities [{:req (req (:run @state)) + :msg "prevent the Corp from rezzing more than 1 piece of ICE for the remainder of the run" + :effect (effect (register-events + {:rez {:req (req (ice? target)) + :effect (effect (register-run-flag! + card :can-rez + (fn [state side card] + (if (ice? card) + ((constantly false) + (toast state :corp "Cannot rez ICE the rest of this run due to EMP Device")) + true))))} + :run-ends {:effect (effect (unregister-events card))}} (assoc card :zone '(:discard))) + (trash card {:cause :ability-cost}))}] + :events {:rez nil + :run-ends nil}} + + "Feedback Filter" + {:interactions {:prevent [{:type #{:net :brain} + :req (req true)}]} + :abilities [{:cost [:credit 3] + :msg "prevent 1 net damage" + :effect (effect (damage-prevent :net 1))} + {:label "[Trash]: Prevent up to 2 brain damage" + :msg "prevent up to 2 brain damage" + :effect (effect (trash card {:cause :ability-cost}) + (damage-prevent :brain 2))}]} + + "Flame-out" + (let [turn-end {:async true + :effect (req (unregister-events state :runner card) + (if-let [hosted (first (:hosted card))] + (do + (system-msg state :runner (str "trashes " (:title hosted) " from Flame-out")) + (trash state side eid hosted nil)) + (effect-completed state side eid)))}] + {:implementation "Credit usage restriction not enforced" + :data {:counter {:credit 9}} + :abilities [{:label "Take 1 [Credits] from Flame-out" + :req (req (and (not-empty (:hosted card)) + (pos? (get-counters card :credit)))) + :counter-cost [:credit 1] + :effect (req (gain-credits state :runner 1) + (system-msg state :runner "takes 1 [Credits] from Flame-out") + (register-events + state :runner + {:runner-turn-ends turn-end + :corp-turn-ends turn-end} + (get-card state card)))} + {:label "Take all [Credits] from Flame-out" + :req (req (and (not-empty (:hosted card)) + (pos? (get-counters card :credit)))) + :effect (req (let [credits (get-counters card :credit)] + (gain-credits state :runner credits) + (update! state :runner (dissoc-in card [:counter :credit])) + (system-msg state :runner (str "takes " credits "[Credits] from Flame-out")) + (register-events + state :runner + {:runner-turn-ends turn-end + :corp-turn-ends turn-end} + (get-card state card))))} + {:label "Install a program on Flame-out" + :req (req (empty? (:hosted card))) + :cost [:click 1] + :prompt "Select a program in your Grip to install on Flame-out" + :choices {:req #(and (program? %) + (in-hand? %))} + :effect (effect (runner-install target {:host-card card}) + (update! (assoc-in (get-card state card) [:special :flame-out] (:cid target))))} + {:label "Host an installed program on Flame-out" + :req (req (empty? (:hosted card))) + :prompt "Select an installed program to host on Flame-out" + :choices {:req #(and (program? %) + (installed? %))} + :msg (msg "host " (:title target)) + :effect (req (->> target + (get-card state) + (host state side card)) + (update! state side (assoc-in (get-card state card) [:special :flame-out] (:cid target))))}] + :events {:card-moved {:req (req (= (:cid target) (get-in (get-card state card) [:special :flame-out]))) + :effect (effect (update! (dissoc-in card [:special :flame-out])))} + :runner-turn-ends nil + :corp-turn-ends nil} + :interactions {:pay-credits {:req (req (and (= :ability (:source-type eid)) + (same-card? card (:host target)) + (pos? (get-counters card :credit)))) + :custom (req (add-counter state side card :credit -1) + (register-events state side + {:runner-turn-ends turn-end + :corp-turn-ends turn-end} + (get-card state card)) + (effect-completed state side (make-result eid 1))) + :type :custom}}}) + + "Flip Switch" + {:events + {:pre-init-trace + {:async true + :req (req (= :runner (:active-player @state))) + :effect (effect (show-wait-prompt :corp "Runner to use Flip Switch") + (continue-ability + :runner + {:optional + {:prompt "Use Flip Switch to reduce base trace strength to 0?" + :yes-ability {:msg "reduce the base trace strength to 0" + :effect (req (wait-for (trash state side card {:cause :ability-cost}) + (swap! state assoc-in [:trace :force-base] 0) + (effect-completed state side eid)))} + :end-effect (effect (clear-wait-prompt :corp))}} + card nil))}} + :abilities [{:label "Jack out" + :req (req (and run + (= :runner (:active-player @state)))) + :msg "jack out" + :effect (req (wait-for (trash state side card {:cause :ability-cost}) + (jack-out state side eid)))} + {:label "Remove 1 tag" + :req (req (and (pos? (count-tags state)) + (= :runner (:active-player @state)))) + :msg "remove 1 tag" + :effect (req (wait-for (trash state side card {:cause :ability-cost}) + (lose-tags state side eid 1)))}]} + + "Forger" + {:interactions {:prevent [{:type #{:tag} + :req (req true)}]} + :in-play [:link 1] + :abilities [{:msg "avoid 1 tag" :label "[Trash]: Avoid 1 tag" + :effect (effect (tag-prevent :runner 1) (trash card {:cause :ability-cost}))} + {:msg "remove 1 tag" :label "[Trash]: Remove 1 tag" + :effect (effect (trash card {:cause :ability-cost}) (lose-tags 1))}]} + + "Friday Chip" + (let [ability {:msg (msg "move 1 virus counter to " (:title target)) + :req (req (and (pos? (get-counters card :virus)) + (pos? (count-virus-programs state)))) + :choices {:req is-virus-program?} + :effect (req (add-counter state :runner card :virus -1) + (add-counter state :runner target :virus 1))}] + {:abilities [(set-autoresolve :auto-accept "Friday Chip")] + :effect (effect (toast "Tip: You can toggle automatically adding virus counters by clicking Friday Chip.")) + :events {:runner-turn-begins ability + :runner-trash {:async true + :req (req (some corp? targets)) + :effect (req (let [amt-trashed (count (filter corp? targets)) + sing-ab {:optional {:prompt "Place a virus counter on Friday Chip?" + :autoresolve (get-autoresolve :auto-accept) + :yes-ability {:effect (effect (system-msg + :runner + "places 1 virus counter on Friday Chip") + (add-counter :runner card :virus 1))}}} + mult-ab {:prompt "Place virus counters on Friday Chip?" + :choices {:number (req amt-trashed) + :default (req amt-trashed)} + :effect (effect (system-msg :runner + (str "places " + (quantify target "virus counter") + " on Friday Chip")) + (add-counter :runner card :virus target))} + ab (if (> amt-trashed 1) mult-ab sing-ab)] + (continue-ability state side ab card targets)))}}}) + + "Gebrselassie" + {:abilities [{:msg (msg "host it on an installed non-AI icebreaker") + :cost [:click 1] + :choices {:req #(and (installed? %) + (has-subtype? % "Icebreaker") + (not (has-subtype? % "AI")))} + :effect (req (when-let [host (get-card state (:host card))] + (update! state side (dissoc-in host [:pump :all-turn])) + (update-breaker-strength state side host)) + (host state side target card))}] + :events {:pump-breaker {:silent (req true) + :req (req (same-card? (second targets) (:host card))) + :effect (effect (update! (update-in (second targets) [:pump :all-turn] (fnil #(+ % (first targets)) 0))) + (update-breaker-strength (second targets)))}} + :leave-play (req (when-let [host (get-card state (:host card))] + (update! state side (dissoc-in host [:pump :all-turn])) + (update-breaker-strength state side host)))} + + "GPI Net Tap" + {:implementation "Trash and jack out effect is manual" + :abilities [{:req (req (and (ice? current-ice) (not (rezzed? current-ice)))) :async true - :effect (effect (draw eid 1 nil))}] - {:in-play [:memory 1] - :flags {:runner-turn-draw true - :runner-phase-12 (req (< 1 (count (filter #(card-flag? % :runner-turn-draw true) - (cons (get-in @state [:runner :identity]) - (all-active-installed state :runner))))))} - :events {:runner-turn-begins ability} - :abilities [ability]})) - -(define-card "Unregistered S&W '35" - {:abilities - [{:cost [:click 2] - :req (req (some #{:hq} (:successful-run runner-reg))) - :label "trash a Bioroid, Clone, Executive or Sysop" - :prompt "Select a Bioroid, Clone, Executive, or Sysop to trash" - :choices {:req #(and (rezzed? %) - (or (has-subtype? % "Bioroid") - (has-subtype? % "Clone") - (has-subtype? % "Executive") - (has-subtype? % "Sysop")) - (or (and (= (last (:zone %)) :content) (is-remote? (second (:zone %)))) - (= (last (:zone %)) :onhost)))} - :msg (msg "trash " (:title target)) :effect (effect (trash target))}]}) - -(define-card "Vigil" - (let [ability {:req (req (and (:runner-phase-12 @state) (= (count (:hand corp)) (hand-size state :corp)))) + :effect (effect (expose eid current-ice))}]} + + "Grimoire" + {:in-play [:memory 2] + :events {:runner-install {:silent (req true) + :req (req (has-subtype? target "Virus")) + :effect (effect (add-counter target :virus 1))}}} + + "Heartbeat" + {:in-play [:memory 1] + :interactions {:prevent [{:type #{:net :brain :meat} + :req (req true)}]} + :abilities [{:msg (msg "prevent 1 damage, trashing a facedown " (:title target)) + :choices {:req #(and (runner? %) (installed? %))} + :priority 50 + :effect (effect (trash target {:unpreventable true}) + (damage-prevent :brain 1) + (damage-prevent :meat 1) + (damage-prevent :net 1))}]} + + "Hijacked Router" + {:events {:server-created {:effect (effect (lose-credits :corp 1)) + :msg "force the Corp to lose 1 [Credits]"} + :successful-run {:req (req (= target :archives)) + :optional {:prompt "Trash Hijacked Router to force the Corp to lose 3 [Credits]?" + :yes-ability {:async true + :effect (req (system-msg state :runner "trashes Hijacked Router to force the Corp to lose 3 [Credits]") + (wait-for (trash state :runner card {:unpreventable true}) + (lose-credits state :corp 3) + (effect-completed state side eid)))}}}}} + + "Hippo" + {:implementation "Subroutine and first encounter requirements not enforced" + :abilities [{:label "Remove Hippo from the game: trash outermost piece of ICE if all subroutines were broken" + :req (req (and run + (pos? (count run-ices)))) + :async true + :effect (req (let [ice (last run-ices)] + (system-msg + state :runner + (str "removes Hippo from the game to trash " (card-str state ice))) + (move state :runner card :rfg) + (trash state :runner eid ice nil)))}]} + + "HQ Interface" + {:in-play [:hq-access 1]} + + "Knobkierie" + {:implementation "MU usage restriction not enforced" + :in-play [:memory 3] + :events {:successful-run + {:req (req (and (first-event? state :runner :successful-run) + (pos? (count-virus-programs state)))) + :optional {:prompt "Place a virus counter?" + :autoresolve (get-autoresolve :auto-add) + :yes-ability {:prompt "Select an installed virus program for Knobkierie to add a virus counter to" + :choices {:req #(and (installed? %) + (has-subtype? % "Virus") + (program? %))} + :msg (msg "place 1 virus counter on " (:title target)) + :effect (effect (add-counter target :virus 1))}}}} + :abilities [(set-autoresolve :auto-add "Knobkierie")]} + + "Lemuria Codecracker" + {:abilities [{:cost [:click 1 :credit 1] + :req (req (some #{:hq} (:successful-run runner-reg))) + :choices {:req installed?} + :effect (effect (expose eid target)) + :msg "expose 1 card"}]} + + "LLDS Memory Diamond" + {:in-play [:link 1 :memory 1 :hand-size 1]} + + "LLDS Processor" + (let [llds {:effect (req (let [cards (:llds-target card)] + (update! state side (dissoc card :llds-target)) + (doseq [c cards] + (update-breaker-strength state side + (find-cid (:cid c) (all-active-installed state :runner))))))}] + {:events + {:runner-turn-ends llds :corp-turn-ends llds + :runner-install {:silent (req true) + :req (req (has-subtype? target "Icebreaker")) + :effect (effect (update! (update-in card [:llds-target] #(conj % target))) + (update-breaker-strength target))} + :pre-breaker-strength {:req (req (some #(same-card? target %) (:llds-target card))) + :effect (effect (breaker-strength-bonus 1))}}}) + + "Lockpick" + {:recurring 1 + :interactions {:pay-credits {:req (req (and (= :ability (:source-type eid)) + (has-subtype? target "Decoder") + (has-subtype? target "Icebreaker"))) + :type :recurring}}} + + "Logos" + {:in-play [:memory 1 :hand-size 1] + :events {:agenda-scored + {:player :runner :prompt "Choose a card" :msg (msg "add 1 card to their Grip from their Stack") + :choices (req (cancellable (:deck runner))) + :effect (effect (trigger-event :searched-stack nil) + (shuffle! :deck) + (move target :hand))}}} + + "Lucky Charm" + {:interactions {:prevent [{:type #{:end-run} + :req (req (and (some #{:hq} (:successful-run runner-reg)) + (corp? (:card-cause target))))}]} + :abilities [{:msg "prevent the run from ending" + :req (req (some #{:hq} (:successful-run runner-reg))) + :effect (effect (end-run-prevent) + (move card :rfg))}]} + + "Mâché" + {:abilities [{:label "Draw 1 card" :msg "draw 1 card" - :label "Draw 1 card (start of turn)" + :counter-cost [:power 3] + :async true + :effect (effect (draw :runner eid 1 nil))}] + :events {:runner-trash {:once :per-turn + :req (req (and (corp? target) + (:access @state) + (:trash target))) + :effect (effect (system-msg (str "places " (:trash target) " power counters on Mâché")) + (add-counter card :power (:trash target)))}}} + + "Masterwork (v37)" + {:in-play [:memory 1] + :events {:run {:optional + {:async true + :interactive (req true) + :req (req (and (<= 1 (:credit runner)) + (some hardware? (:hand runner)))) + :prompt "Pay 1 [Credit] to install a hardware?" + :yes-ability {:async true + :prompt "Select a piece of hardware" + :choices {:req #(and (in-hand? %) + (hardware? %))} + :msg (msg "install " (:title target) " from the grip, paying 1 [Credit] more") + :effect (effect (install-cost-bonus [:credit 1]) + (runner-install eid target nil))}}} + :runner-install {:async true + :req (req (and (hardware? target) + (first-event? state side :runner-install #(hardware? (first %))))) + :effect (effect (draw eid 1 nil))}}} + + "Māui" + {:in-play [:memory 2] + :recurring (effect (set-prop card :rec-counter (count (:ices (get-in @state [:corp :servers :hq]))))) + :effect (effect (set-prop card :rec-counter (count (:ices (get-in @state [:corp :servers :hq]))))) + :interactions {:pay-credits {:req (req (= :hq (get-in @state [:run :server 0]))) + :type :recurring}}} + + "Maw" + (let [ability {:label "Trash a card from HQ" + :req (req (and (= 1 (get-in @state [:runner :register :no-trash-or-steal])) + (pos? (count (:hand corp))) + (not= (first (:zone target)) :discard))) + :once :per-turn + :msg "force the Corp to trash a random card from HQ" + :effect (req (let [card-to-trash (first (shuffle (:hand corp))) + card-seen? (same-card? target card-to-trash) + card-to-trash (if card-seen? (assoc card-to-trash :seen true) + card-to-trash)] + ;; toggle access flag to prevent Hiro issue #2638 + (swap! state dissoc :access) + (trash state :corp card-to-trash) + (swap! state assoc :access true)))}] + {:in-play [:memory 2] + :abilities [ability] + :events {:post-access-card ability}}) + + "Maya" + {:in-play [:memory 2] + :abilities [{:once :per-turn + :async true + :label "Move this accessed card to bottom of R&D" + :req (req (when-let [accessed-card (-> @state :runner :prompt first :card)] + (in-deck? accessed-card))) + :msg "move the card just accessed to the bottom of R&D" + :effect (req (let [accessed-card (-> @state :runner :prompt first :card)] + (move state :corp accessed-card :deck) + (wait-for (gain-tags state :runner (make-eid state) 1) + (close-access-prompt state side))))} + {:once :per-turn + :label "Move a previously accessed card to bottom of R&D" + :effect (effect (resolve-ability + {:async true + ;; only allow targeting cards that were accessed this turn + :choices {:req #(some (fn [accessed-card] + (same-card? % accessed-card)) + (map first (turn-events state side :access)))} + :msg (msg "move " (:title target) " to the bottom of R&D") + :effect (req (move state :corp target :deck) + (gain-tags state :runner eid 1) + (swap! state update-in [side :prompt] rest) + (when-let [run (:run @state)] + (when (and (:ended run) + (empty? (get-in @state [:runner :prompt]))) + (handle-end-run state :runner))))} + card nil))}]} + + "MemStrips" + {:implementation "MU usage restriction not enforced" + :in-play [:memory 3]} + + "Mind's Eye" + {:in-play [:memory 1] + :implementation "Power counters added automatically" + :events {:successful-run {:silent (req true) + :req (req (= target :rd)) + :effect (effect (add-counter card :power 1))}} + :abilities [{:async true + :cost [:click 1] + :counter-cost [:power 3] + :msg "access the top card of R&D" + :effect (req (do-access state side eid [:rd] {:no-root true}))}]} + + "Mirror" + {:in-play [:memory 2] + :events {:successful-run + {:async true + :req (req (= target :rd)) + :effect (effect (continue-ability + {:prompt "Select a card and replace 1 spent [Recurring Credits] on it" + :choices {:req #(< (get-counters % :recurring) (:recurring (card-def %) 0))} + :msg (msg "replace 1 spent [Recurring Credits] on " (:title target)) + :effect (effect (add-prop target :rec-counter 1))} + card nil))}}} + + "Monolith" + (let [mhelper (fn mh [n] {:prompt "Select a program to install" + :choices {:req #(and (program? %) + (in-hand? %))} + :effect (req (install-cost-bonus state side [:credit -4]) + (runner-install state side target nil) + (when (< n 3) + (resolve-ability state side (mh (inc n)) card nil)))})] + {:interactions {:prevent [{:type #{:net :brain} + :req (req true)}]} + :in-play [:memory 3] + :effect (effect (resolve-ability (mhelper 1) card nil)) + :abilities [{:msg (msg "prevent 1 brain or net damage by trashing " (:title target)) + :priority 50 + :choices {:req #(and (program? %) + (in-hand? %))} + :prompt "Choose a program to trash from your Grip" + :effect (effect (trash target) + (damage-prevent :brain 1) + (damage-prevent :net 1))}]}) + + "Muresh Bodysuit" + {:events {:pre-damage {:once :per-turn :once-key :muresh-bodysuit + :req (req (= target :meat)) + :msg "prevent the first meat damage this turn" + :effect (effect (damage-prevent :meat 1))}}} + + "Net-Ready Eyes" + {:effect (effect (damage eid :meat 2 {:unboostable true :card card})) :msg "suffer 2 meat damage" + :events {:run {:choices {:req #(and (installed? %) + (has-subtype? % "Icebreaker"))} + :msg (msg "give " (:title target) " +1 strength") + :effect (effect (pump target 1 :all-run))}}} + + "NetChip" + {:abilities [{:label "Install a program on NetChip" + :req (req (empty? (:hosted card))) + :effect (req (let [n (count (filter #(= (:title %) (:title card)) (all-active-installed state :runner)))] + (resolve-ability + state side + {:cost [:click 1] + :prompt "Select a program in your Grip to install on NetChip" + :choices {:req #(and (program? %) + (runner-can-install? state side % false) + (<= (:memoryunits %) n) + (in-hand? %))} + :msg (msg "host " (:title target)) + :effect (effect (runner-install target {:host-card card :no-mu true}) + (update! (assoc (get-card state card) + :hosted-programs + (cons (:cid target) (:hosted-programs card)))))} + card nil)))} + {:label "Host an installed program on NetChip" + :req (req (empty? (:hosted card))) + :effect (req (let [n (count (filter #(= (:title %) (:title card)) (all-active-installed state :runner)))] + (resolve-ability + state side + {:prompt "Select an installed program to host on NetChip" + :choices {:req #(and (program? %) + (<= (:memoryunits %) n) + (installed? %))} + :msg (msg "host " (:title target)) + :effect (effect (host card target) + (free-mu (:memoryunits target)) + (update! (assoc (get-card state card) + :hosted-programs + (cons (:cid target) (:hosted-programs card)))))} + card nil)))}] + :events {:card-moved {:req (req (some #{(:cid target)} (:hosted-programs card))) + :effect (effect (update! (assoc card + :hosted-programs + (remove #(= (:cid target) %) (:hosted-programs card)))) + (use-mu (:memoryunits target)))}}} + + "Obelus" + {:in-play [:memory 1] + :effect (req (change-hand-size state :runner (count-tags state))) + :leave-play (req (change-hand-size state :runner (- (count-tags state)))) + :events {:successful-run-ends {:once :per-turn + :req (req (and (#{:rd :hq} (first (:server target))) + (first-event? state side :successful-run-ends + #(#{:rd :hq} (first (:server (first %))))))) + :msg (msg "draw " (total-cards-accessed target) " cards") + :async true + :effect (effect (draw eid (total-cards-accessed target) nil))} + ;; Events for tracking hand size + :runner-gain-tag {:effect (req (change-hand-size state :runner target))} + :runner-lose-tag {:effect (req (change-hand-size state :runner (- target)))} + :runner-additional-tag-change {:effect (req (change-hand-size state :runner target))}}} + + "Omni-drive" + {:recurring 1 + :abilities [{:label "Install and host a program of 1[Memory Unit] or less on Omni-drive" + :req (req (empty? (:hosted card))) + :cost [:click 1] + :prompt "Select a program of 1[Memory Unit] or less to install on Omni-drive from your grip" + :choices {:req #(and (program? %) + (<= (:memoryunits %) 1) + (in-hand? %))} + :msg (msg "host " (:title target)) + :effect (effect (runner-install target {:host-card card :no-mu true}) + (update! (assoc (get-card state card) :Omnidrive-prog (:cid target))))} + {:label "Host an installed program of 1[Memory Unit] or less on Omni-drive" + :prompt "Select an installed program of 1[Memory Unit] or less to host on Omni-drive" + :choices {:req #(and (program? %) + (<= (:memoryunits %) 1) + (installed? %))} + :msg (msg "host " (:title target)) + :effect (effect (host card target) + (free-mu (:memoryunits target)) + (update! (assoc (get-card state card) :Omnidrive-prog (:cid target))))}] + :events {:card-moved {:req (req (= (:cid target) (:Omnidrive-prog (get-card state card)))) + :effect (effect (update! (dissoc card :Omnidrive-prog)) + (use-mu (:memoryunits target)))}} + :interactions {:pay-credits {:req (req (and (= :ability (:source-type eid)) + (program? target) + (same-card? card (:host target)))) + :type :recurring}}} + + "Paragon" + {:in-play [:memory 1] + :events {:successful-run + {:req (req (first-event? state side :successful-run)) + :async true + :interactive (get-autoresolve :autofire (complement never?)) + :silent (get-autoresolve :autofire never?) + :effect (effect + (show-wait-prompt :corp "Runner to decide if they will use Paragon") + (continue-ability + {:optional + {:player :runner + :autoresolve (get-autoresolve :auto-fire) + :prompt "Use Paragon?" + :yes-ability + {:msg "gain 1 [Credit] and look at the top card of Stack" + :async true + :effect (effect + (gain-credits :runner 1) + (continue-ability + {:player :runner + :optional + {:prompt (msg "Add " (:title (first (:deck runner))) " to bottom of Stack?") + :yes-ability + {:msg "add the top card of Stack to the bottom" + :effect (effect (move :runner (first (:deck runner)) :deck) + (clear-wait-prompt :corp))} + :no-ability {:effect (effect (clear-wait-prompt :corp))}}} + card nil))} + :no-ability {:effect (effect (clear-wait-prompt :corp) + (system-msg "does not add the top card of the Stack to the bottom"))}}} + card nil))}} + :abilities [(set-autoresolve :auto-fire "Paragon")]} + + "Patchwork" + (letfn [(patchwork-discount [cost-type bonus-fn] + {:async true + :req (req (and (get-in card [:special :patchwork]) + (= "Runner" (:side target)) + ;; We need at least one card (that is not the card played) in hand + (not-empty (remove (partial same-card? target) (:hand runner))))) + :effect (req (let [playing target] + (continue-ability + state side + {:prompt (str "Trash a card to lower the " cost-type " cost of " (:title playing) " by 2 [Credits].") + :priority 2 + :choices {:req #(and (in-hand? %) + (runner? %) + (not (same-card? % playing)))} + :msg (msg "trash " (:title target) " to lower the " cost-type " cost of " + (:title playing) " by 2 [Credits]") + :effect (effect (trash target {:unpreventable true}) + (bonus-fn [:credit -2]) + (update! (dissoc-in card [:special :patchwork]))) + :cancel-effect (effect (effect-completed eid))} + card nil)))})] + (let [patchwork-ability {:once :per-turn + :effect (effect (update! (assoc-in card [:special :patchwork] true)) + (toast "Your next card played will trigger Patchwork." "info"))}] + {:in-play [:memory 1] + :implementation "Click Patchwork before playing/installing a card." + :abilities [patchwork-ability] + :events {:pre-play-instant (patchwork-discount "play" play-cost-bonus) + :pre-install (patchwork-discount "install" install-cost-bonus) + :runner-turn-ends {:effect (effect (update! (dissoc-in card [:special :patchwork])))} + :corp-turn-ends {:effect (effect (update! (dissoc-in card [:special :patchwork])))}} + :interactions {:pay-credits {:req (req (and (or (= :play (:source-type eid)) + (= :runner-install (:source-type eid))) + ;; We need at least one card (that is not the card played) in hand + (not-empty (remove (partial same-card? target) (:hand runner))) + ;; Patchwork wasn't used in the traditional way + (not (get-in card [:special :patchwork])) + ;; Check if Patchwork can trigger + (can-trigger? state side patchwork-ability card targets))) + :custom (req (let [cost-type (str (when (= :play (:source-type eid)) "play") + (when (= :runner-install (:source-type eid)) "install")) + patchwork card + targetcard target] + (continue-ability + state side + {:prompt (str "Trash a card to lower the " cost-type + " cost of " (:title patchwork) " by 2 [Credits].") + :priority 2 + :async true + :choices {:req #(and (in-hand? %) + (runner? %) + (not (same-card? % patchwork)))} + :msg (msg "trash " (:title target) " to lower the " cost-type " cost of " + (:title targetcard) " by 2 [Credits]") + :effect (req (trash state side target {:unpreventable true}) + (register-once state patchwork-ability patchwork) + (effect-completed state side (make-result eid 2))) ; provide 2 credits + :cancel-effect (effect (effect-completed (make-result eid 0)))} ; provide 0 credits + nil nil))) + :type :custom}}})) + + "Plascrete Carapace" + {:data [:counter {:power 4}] + :interactions {:prevent [{:type #{:meat} + :req (req true)}]} + :abilities [{:counter-cost [:power 1] + :msg "prevent 1 meat damage" + :effect (req (damage-prevent state side :meat 1) + (when (zero? (get-counters (get-card state card) :power)) + (trash state side card {:unpreventable true})))}]} + + "Polyhistor" + (let [abi {:optional + {:prompt "Draw 1 card to force the Corp to draw 1 card?" + :yes-ability {:msg "draw 1 card and force the Corp to draw 1 card" + :async true + :effect (req (wait-for (draw state :runner 1) + (draw state :corp eid 1 nil)))} + :no-ability {:effect (req (system-msg state side (str "does not use Polyhistor")) + (effect-completed state side eid))}}}] + {:in-play [:link 1 :memory 1] + :events {:pass-ice {:req (req (and (= (:server run) [:hq]) + (= (:position run) 1) ; trigger when last ICE passed + (pos? (count (:deck runner))))) + :async true + :once :per-turn + :effect (req (continue-ability state :runner abi card nil))} + :run {:req (req (and (= (:server run) [:hq]) + (zero? (:position run)) ; trigger on unprotected HQ + (pos? (count (:deck runner))))) + :async true + :once :per-turn + :effect (req (continue-ability state :runner abi card nil))}}}) + + "Prepaid VoicePAD" + {:recurring 1 + :interactions {:pay-credits {:req (req (= :play (:source-type eid))) + :type :recurring}}} + + "Public Terminal" + {:recurring 1 + :interactions {:pay-credits {:req (req (and (= :play (:source-type eid)) + (has-subtype? target "Run"))) + :type :recurring}}} + + "Q-Coherence Chip" + {:in-play [:memory 1] + :events (let [e {:req (req (= (last (:zone target)) :program)) + :effect (effect (trash card) + (system-msg (str "trashes Q-Coherence Chip")))}] + {:runner-trash e :corp-trash e})} + + "Qianju PT" + {:flags {:runner-phase-12 (req true)} + :abilities [{:label "Lose [Click], avoid 1 tag (start of turn)" + :once :per-turn + :req (req (:runner-phase-12 @state)) + :effect (effect (update! (assoc card :qianju-active true))) + :msg "lose [Click] and avoid the first tag received until their next turn"}] + :events {:corp-turn-ends {:effect (effect (update! (dissoc card :qianju-active)))} + :runner-turn-begins {:req (req (:qianju-active card)) + :effect (effect (lose :click 1))} + :pre-tag {:req (req (:qianju-active card)) + :msg "avoid the first tag received" + :effect (effect (tag-prevent :runner 1) + (update! (dissoc card :qianju-active)))}}} + + "R&D Interface" + {:in-play [:rd-access 1]} + + "Rabbit Hole" + {:in-play [:link 1] + :effect + (effect (resolve-ability + {:optional {:req (req (some #(when (= (:title %) "Rabbit Hole") %) (:deck runner))) + :prompt "Install another Rabbit Hole?" :msg "install another Rabbit Hole" + :yes-ability {:effect (req (when-let [c (some #(when (= (:title %) "Rabbit Hole") %) + (:deck runner))] + (trigger-event state side :searched-stack nil) + (shuffle! state :runner :deck) + (runner-install state side c)))}}} card nil))} + + "Ramujan-reliant 550 BMI" + {:interactions {:prevent [{:type #{:net :brain} + :req (req true)}]} + :abilities [{:req (req (not-empty (:deck runner))) + :effect (req (let [n (count (filter #(= (:title %) (:title card)) (all-active-installed state :runner)))] + (resolve-ability + state side + {:prompt "Choose how much damage to prevent" + :priority 50 + :choices {:number (req (min n (count (:deck runner))))} + :msg (msg "trash " (join ", " (map :title (take target (:deck runner)))) + " from their Stack and prevent " target " damage") + :effect (effect (damage-prevent :net target) + (damage-prevent :brain target) + (mill :runner target) + (trash card {:cause :ability-cost}))} card nil)))}]} + + "Recon Drone" + ; eventmap uses reverse so we get the most recent event of each kind into map + (letfn [(eventmap [s] + (into {} (reverse (get s :turn-events))))] + {:interactions {:prevent [{:type #{:net :brain :meat} + :req (req (:access @state))}]} + :abilities [{:req (req (= (:cid (second (:pre-damage (eventmap @state)))) + (:cid (first (:pre-access-card (eventmap @state)))))) + :effect (effect (resolve-ability + {:prompt "Choose how much damage to prevent" + :priority 50 + :choices {:number (req (min (last (:pre-damage (eventmap @state))) + (:credit runner)))} + :msg (msg "prevent " target " damage") + :effect (effect (trash card {:cause :ability-cost}) + (damage-prevent (first (:pre-damage (eventmap @state))) target) + (lose-credits target))} + card nil))}]}) + + "Record Reconstructor" + {:events + {:successful-run + {:req (req (= (get-in @state [:run :server]) [:archives])) + :effect (req (let [rr card] + (swap! state assoc-in [:run :run-effect :replace-access] + {:effect (effect (resolve-ability + {:prompt "Choose one faceup card to add to the top of R&D" + :choices (req (filter #(:seen %) (:discard corp))) + :msg (msg "add " (:title target) " to the top of R&D") + :effect (req (move state :corp target :deck {:front true}))} + rr nil))})))}}} + + "Reflection" + {:in-play [:memory 1 :link 1] + :events {:jack-out {:effect (req (let [card (first (shuffle (:hand corp)))] + (reveal state :corp card) + (system-msg state :runner (str "force the Corp to reveal " (:title card) " from HQ"))))}}} + + "Replicator" + (letfn [(hardware-and-in-deck? [target runner] + (and (hardware? target) + (some #(= (:title %) (:title target)) (:deck runner))))] + {:events {:runner-install + {:interactive (req (hardware-and-in-deck? target runner)) + :silent (req (not (hardware-and-in-deck? target runner))) + :optional {:prompt "Use Replicator to add a copy?" + :req (req (hardware-and-in-deck? target runner)) + :yes-ability {:msg (msg "add a copy of " (:title target) " to their Grip") + :effect (effect (trigger-event :searched-stack nil) + (shuffle! :deck) + (move (some #(when (= (:title %) (:title target)) %) + (:deck runner)) :hand))}}}}}) + + "Respirocytes" + (let [ability {:once :per-turn + :msg "draw 1 card and add a power counter to itself" + :async true + :effect (req (wait-for (draw state :runner 1 nil) + (do (add-counter state side (get-card state card) :power 1) + (if (= (get-counters (get-card state card) :power) 3) + (do (system-msg state :runner "trashes Respirocytes as it reached 3 power counters") + (trash state side eid card {:unpreventable true})) + (effect-completed state side eid)))))} + watch-id (fn [card] (keyword (str "respirocytes-" (:cid card))))] + {:effect (req (add-watch state (watch-id card) + (fn [k ref old new] + (when (and (seq (get-in old [:runner :hand])) + (empty? (get-in new [:runner :hand]))) + (resolve-ability ref side ability card nil)))) + (damage state side eid :meat 1 {:unboostable true :card card})) + :msg "suffer 1 meat damage" + :trash-effect {:effect (req (remove-watch state (watch-id card)))} + :leave-play (req (remove-watch state (watch-id card))) + :events {:runner-turn-begins {:req (req (empty? (get-in @state [:runner :hand]))) + :effect (effect (resolve-ability ability card nil))} + :corp-turn-begins {:req (req (empty? (get-in @state [:runner :hand]))) + :effect (effect (resolve-ability ability card nil))}}}) + + "Rubicon Switch" + {:abilities [{:cost [:click 1] + :once :per-turn + :async true + :prompt "How many [Credits]?" :choices :credit + :effect (effect (system-msg (str "spends a [Click] and " target " [Credit] on Rubicon Switch")) + (resolve-ability {:choices {:req #(and (ice? %) + (= :this-turn (:rezzed %)) + (<= (:cost %) target))} + :effect (effect (derez target)) + :msg (msg "derez " (:title target))} card nil))}]} + + "Security Chip" + {:abilities [{:label "[Trash]: Add [Link] strength to a non-Cloud icebreaker until the end of the run" + :msg (msg "add " (:link runner) " strength to " (:title target) " until the end of the run") + :req (req (:run @state)) + :prompt "Select one non-Cloud icebreaker" + :choices {:req #(and (has-subtype? % "Icebreaker") + (not (has-subtype? % "Cloud")) + (installed? %))} + :effect (effect (pump target (:link runner) :all-run) + (trash (get-card state card) {:cause :ability-cost}))} + {:label "[Trash]: Add [Link] strength to any Cloud icebreakers until the end of the run" + :msg (msg "add " (:link runner) " strength to " (count targets) " Cloud icebreakers until the end of the run") + :req (req (:run @state)) + :prompt "Select any number of Cloud icebreakers" + :choices {:max 50 + :req #(and (has-subtype? % "Icebreaker") + (has-subtype? % "Cloud") + (installed? %))} + :effect (req (doseq [t targets] + (pump state side t (:link runner) :all-run) + (update-breaker-strength state side t)) + (trash state side (get-card state card) {:cause :ability-cost}))}]} + + "Security Nexus" + {:implementation "Bypass is manual" + :in-play [:memory 1 :link 1] + :abilities [{:req (req (:run @state)) + :once :per-turn + :async true + :msg "force the Corp to initiate a trace" + :label "Trace 5 - Give the Runner 1 tag and end the run" + :trace {:base 5 + :successful {:msg "give the Runner 1 tag and end the run" + :effect (effect (gain-tags :runner eid 1) + (end-run))} + :unsuccessful {:msg "bypass the current ICE"}}}]} + + "Severnius Stim Implant" + (letfn [(implant-fn [srv kw] + {:prompt "Choose at least 2 cards in your Grip to trash with Severnius Stim Implant" + :choices {:max (req (count (:hand runner))) + :req #(and (runner? %) + (in-hand? %))} + :msg (msg "trash " (quantify (count targets) "card") + " and access " (quantify (quot (count targets) 2) "additional card")) + :effect (req (let [bonus (quot (count targets) 2)] + (wait-for (trash-cards state side targets {:unpreventable true + :suppress-event true}) + (make-run state side srv nil card) + (register-events + state side + {:pre-access {:silent (req true) + :effect (effect (access-bonus kw bonus))} + :run-ends {:effect (effect (unregister-events card))}} + card) + (effect-completed state side eid))))})] + {:abilities [{:req (req (<= 2 (count (:hand runner)))) + :cost [:click 1] + :prompt "Choose a server to run with Severnius Stim Implant" + :choices ["HQ" "R&D"] + :effect (effect (continue-ability (implant-fn target (if (= target "HQ") :hq :rd)) card nil))}] + :events {:pre-access nil + :run-ends nil}}) + + "Şifr" + {:in-play [:memory 2] + :abilities [{:once :per-turn + :req (req (rezzed? current-ice)) + :msg (msg "lower their maximum hand size by 1 and lower the strength of " (:title current-ice) " to 0") + :effect (effect (lose :runner :hand-size 1) + (update! (assoc card :sifr-target current-ice :sifr-used true)) + (update-ice-strength current-ice))}] + :events {:runner-turn-begins {:req (req (:sifr-used card)) + :effect (effect (gain :runner :hand-size 1) + (update! (dissoc card :sifr-used)))} + :pre-ice-strength {:req (req (= (:cid target) (get-in card [:sifr-target :cid]))) + :effect (req (let [ice-str (:current-strength target)] + (ice-strength-bonus state side (- ice-str) target)))} + :run-ends {:effect (effect (update! (dissoc card :sifr-target)))}}} + + "Silencer" + {:recurring 1 + :interactions {:pay-credits {:req (req (and (= :ability (:source-type eid)) + (has-subtype? target "Killer") + (has-subtype? target "Icebreaker"))) + :type :recurring}}} + + "Skulljack" + {:effect (effect (damage eid :brain 1 {:card card})) + :events {:pre-trash {:effect (effect (trash-cost-bonus -1))}}} + + "Spinal Modem" + {:in-play [:memory 1] + :recurring 2 + :events {:successful-trace {:req (req run) + :effect (effect (system-msg (str "suffers 1 brain damage from Spinal Modem")) + (damage eid :brain 1 {:card card}))}} + :interactions {:pay-credits {:req (req (and (= :ability (:source-type eid)) + (has-subtype? target "Icebreaker"))) + :type :recurring}}} + + "Sports Hopper" + {:in-play [:link 1] + :abilities [{:label "Draw 3 cards" + :msg "draw 3 cards" + :async true + :effect (req (wait-for (trash state :runner card {:cause :ability-cost}) (draw state :runner eid 3 nil)))}]} + + "Spy Camera" + {:abilities [{:cost [:click 1] + :async true + :label "Look at the top X cards of your Stack" + :msg "look at the top X cards of their Stack and rearrange them" + :effect (req (show-wait-prompt state :corp "Runner to rearrange the top cards of their stack") + (let [n (count (filter #(= (:title %) (:title card)) + (all-active-installed state :runner))) + from (take n (:deck runner))] + (if (pos? (count from)) + (continue-ability state side (reorder-choice :runner :corp from '() + (count from) from) card nil) + (do (clear-wait-prompt state :corp) + (effect-completed state side eid)))))} + {:label "[Trash]: Look at the top card of R&D" + :msg "trash it and look at the top card of R&D" + :effect (effect (prompt! card (str "The top card of R&D is " (:title (first (:deck corp)))) ["OK"] {}) + (trash card {:cause :ability-cost}))}]} + + "Supercorridor" + {:in-play [:memory 2 :hand-size 1] + :events {:runner-turn-ends + {:req (req (= (:credit runner) (:credit corp))) + :async true + :effect (req (show-wait-prompt + state :corp + "Runner to decide if they will gain credits from Supercorridor") + (continue-ability + state :runner + {:optional + {:prompt "Gain credits from Supercorridor?" + :player :runner + :yes-ability {:msg "gain 2 [Credits]" + :effect (req (gain-credits state :runner 2) + (clear-wait-prompt state :corp))} + :no-ability {:effect (req (system-msg + state :runner + "chooses not to gain 2 [Credits] from Supercorridor") + (clear-wait-prompt state :corp))}}} + card nil))}}} + + "The Gauntlet" + {:implementation "Requires Runner to manually (and honestly) set how many ICE were broken directly protecting HQ" + :in-play [:memory 2] + :events {:post-successful-run {:req (req (and (= :hq target) + run)) + :silent (req true) + :async true + :effect (effect (continue-ability + {:prompt "How many ICE protecting HQ did you break all subroutines on?" + ;; Makes number of ice on server (HQ) the upper limit. + ;; This should work since trashed ice do not count according to UFAQ + :choices {:number (req (count (get-in @state [:corp :servers :hq :ices])))} + :effect (effect (access-bonus :hq target))} + card nil))}}} + + "The Personal Touch" + {:hosting {:req #(and (has-subtype? % "Icebreaker") + (installed? %))} + :effect (effect (update-breaker-strength (:host card))) + :events {:pre-breaker-strength {:req (req (same-card? target (:host card))) + :effect (effect (breaker-strength-bonus 1))}}} + + "The Toolbox" + {:in-play [:link 2 :memory 2] + :recurring 2 + :interactions {:pay-credits {:req (req (and (= :ability (:source-type eid)) + (has-subtype? target "Icebreaker"))) + :type :recurring}}} + + "Titanium Ribs" + {:events + {:pre-resolve-damage + {:async true + :req (req (and (pos? (last targets)) + (runner-can-choose-damage? state) + (not (get-in @state [:damage :damage-replace])))) + :effect (req (let [dtype target + src (second targets) + dmg (last targets)] + (when (> dmg (count (:hand runner))) + (flatline state)) + (when (= dtype :brain) + (swap! state update-in [:runner :brain-damage] #(+ % dmg)) + (swap! state update-in [:runner :hand-size :mod] #(- % dmg))) + (show-wait-prompt state :corp "Runner to use Titanium Ribs to choose cards to be trashed") + (wait-for (resolve-ability + state side + {:async true + :prompt (msg "Select " dmg " cards to trash for the " (name dtype) " damage") + :player :runner + :choices {:max dmg + :all true + :req #(and (in-hand? %) + (runner? %))} + :msg (msg "trash " (join ", " (map :title targets))) + :effect (req (clear-wait-prompt state :corp) + (doseq [c targets] + (trash state side c {:cause dtype :unpreventable true})) + (trigger-event state side :damage-chosen) + (damage-defer state side dtype 0) + (effect-completed state side eid))} + card nil) + (trigger-event-sync state side eid :damage dtype src dmg))))} + :damage-chosen {:effect (effect (enable-runner-damage-choice))}} + :async true + :effect (effect (enable-runner-damage-choice) + (system-msg (str "suffers 2 meat damage from installing Titanium Ribs")) + (damage eid :meat 2 {:unboostable true :card card})) + :leave-play (req (swap! state update-in [:damage] dissoc :damage-choose-runner))} + + "Top Hat" + (letfn [(ability [n] + {:async true + :mandatory true + :req (req (not= (:max-access run) 0)) + :prompt "Which card from the top of R&D would you like to access? (Card 1 is on top.)" + :choices (take n ["1" "2" "3" "4" "5"]) + :effect (effect (system-msg (str "accesses the card at position " (str->int target) " of R&D")) + (access-card eid (nth (:deck corp) (dec (str->int target))) "an unseen card"))})] + {:events {:successful-run + {:req (req (= target :rd)) + :interactive (req true) + :optional {:prompt "Use Top Hat to choose one of the top 5 cards in R&D to access?" + :yes-ability {:effect (req (swap! state assoc-in [:run :run-effect :replace-access] + (ability (count (:deck corp)))))}}}}}) + + "Turntable" + {:in-play [:memory 1] + :events {:agenda-stolen + {:interactive (req true) + :req (req (not (empty? (:scored corp)))) + :async true + :effect (req + (let [stolen target] + (continue-ability + state side + {:optional + {:prompt (msg "Swap " (:title stolen) " for an agenda in the Corp's score area?") + :yes-ability + {:async true + :effect (req + (continue-ability + state side + {:prompt (str "Select a scored Corp agenda to swap with " (:title stolen)) + :choices {:req #(in-corp-scored? state side %)} + :effect (req (let [scored target] + (swap-agendas state side scored stolen) + (system-msg state side (str "uses Turntable to swap " + (:title stolen) " for " (:title scored))) + (effect-completed state side eid)))} + card targets))}}} + card targets)))}}} + + "Ubax" + (let [ability {:req (req (:runner-phase-12 @state)) + :msg "draw 1 card" + :label "Draw 1 card (start of turn)" + :once :per-turn + :async true + :effect (effect (draw eid 1 nil))}] + {:in-play [:memory 1] + :flags {:runner-turn-draw true + :runner-phase-12 (req (< 1 (count (filter #(card-flag? % :runner-turn-draw true) + (cons (get-in @state [:runner :identity]) + (all-active-installed state :runner))))))} + :events {:runner-turn-begins ability} + :abilities [ability]}) + + "Unregistered S&W '35" + {:abilities + [{:cost [:click 2] + :req (req (some #{:hq} (:successful-run runner-reg))) + :label "trash a Bioroid, Clone, Executive or Sysop" + :prompt "Select a Bioroid, Clone, Executive, or Sysop to trash" + :choices {:req #(and (rezzed? %) + (or (has-subtype? % "Bioroid") + (has-subtype? % "Clone") + (has-subtype? % "Executive") + (has-subtype? % "Sysop")) + (or (and (= (last (:zone %)) :content) (is-remote? (second (:zone %)))) + (= (last (:zone %)) :onhost)))} + :msg (msg "trash " (:title target)) :effect (effect (trash target))}]} + + "Vigil" + (let [ability {:req (req (and (:runner-phase-12 @state) (= (count (:hand corp)) (hand-size state :corp)))) + :msg "draw 1 card" + :label "Draw 1 card (start of turn)" + :once :per-turn + :async true + :effect (effect (draw eid 1 nil))}] + {:in-play [:memory 1] + :events {:runner-turn-begins ability} + :abilities [ability]}) + + "Window" + {:abilities [{:cost [:click 1] :msg "draw 1 card from the bottom of their Stack" + :effect (effect (move (last (:deck runner)) :hand))}]} + + "Zamba" + {:implementation "Credit gain is automatic" + :in-play [:memory 2] + :events {:expose {:effect (effect (gain-credits :runner 1)) + :msg "gain 1 [Credits]"}}} + + "Zer0" + {:abilities [{:cost [:click 1 :net 1] :once :per-turn + :msg "gain 1 [Credits] and draw 2 cards" :async true - :effect (effect (draw eid 1 nil))}] - {:in-play [:memory 1] - :events {:runner-turn-begins ability} - :abilities [ability]})) - -(define-card "Window" - {:abilities [{:cost [:click 1] :msg "draw 1 card from the bottom of their Stack" - :effect (effect (move (last (:deck runner)) :hand))}]}) - -(define-card "Zamba" - {:implementation "Credit gain is automatic" - :in-play [:memory 2] - :events {:expose {:effect (effect (gain-credits :runner 1)) - :msg "gain 1 [Credits]"}}}) - -(define-card "Zer0" - {:abilities [{:cost [:click 1 :net 1] - :once :per-turn - :msg "gain 1 [Credits] and draw 2 cards" - :async true - :effect (effect (gain-credits 1) - (draw eid 2 nil))}]}) + :effect (effect (gain-credits 1) + (draw eid 2 nil))}]}}) diff --git a/src/clj/game/cards/ice.clj b/src/clj/game/cards/ice.clj index 9f2b71d4af..4dc458f5de 100644 --- a/src/clj/game/cards/ice.clj +++ b/src/clj/game/cards/ice.clj @@ -1,7 +1,7 @@ (ns game.cards.ice (:require [game.core :refer :all] [game.core.eid :refer [make-eid effect-completed]] - [game.core.card-defs :refer [card-def define-card]] + [game.core.card-defs :refer [card-def]] [game.utils :refer :all] [game.macros :refer [effect req msg wait-for continue-ability when-let*]] [clojure.string :refer [split-lines split join lower-case includes? starts-with?]] @@ -221,2466 +221,2467 @@ (assoc ice-def :implementation note)) ;; Card definitions -(define-card "Aiki" - {:subroutines [(do-psi {:label "Runner draws 2 cards" - :msg "make the Runner draw 2 cards" - :effect (effect (draw :runner 2) - (effect-completed eid))}) - (do-net-damage 1)]}) - -(define-card "Aimor" - {:subroutines [{:label "Trash the top 3 cards of the Stack. Trash Aimor." - :effect (req (when (not-empty (:deck runner)) - (system-msg state :corp - (str "uses Aimor to trash " - (join ", " (map :title (take 3 (:deck runner)))) - " from the Runner's Stack")) - (mill state :corp :runner 3)) - (when current-ice - (no-action state :corp nil) - (continue state :runner nil)) - (trash state side card) - (system-msg state side (str "trashes Aimor")))}]}) - -(define-card "Anansi" - (let [corp-draw {:optional {:prompt "Draw 1 card?" - :yes-ability {:async true - :msg "draw 1 card" - :effect (effect (draw eid 1 nil))}}} - runner-draw {:async true - :effect (req (show-wait-prompt state :corp "Runner to decide on card draw") - (continue-ability state side - {:player :runner - :optional - {:prompt "Pay 2 [Credits] to draw 1 card?" - :no-ability {:effect (effect (system-msg :runner "does not draw 1 card") - (clear-wait-prompt :corp))} - :yes-ability {:async true - :effect (effect - (system-msg :runner "pays 2 [Credits] to draw 1 card") - (lose-credits 2) - (clear-wait-prompt :corp) - (draw eid 1 nil))}}} - card nil))}] - {:implementation "Encounter-ends effect is manually triggered." - :subroutines [{:msg "rearrange the top 5 cards of R&D" - :async true - :effect (req (show-wait-prompt state :runner "Corp to rearrange the top cards of R&D") - (let [from (take 5 (:deck corp))] - (if (pos? (count from)) - (continue-ability state side (reorder-choice :corp :runner from '() - (count from) from) - card nil) - (do (clear-wait-prompt state :runner) - (effect-completed state side eid)))))} - {:label "Draw 1 card; allow runner to draw 1 card" - :async true - :effect (req (wait-for (resolve-ability state side corp-draw card nil) - (continue-ability state :runner runner-draw card nil)))} - (do-net-damage 1)] - :abilities [(do-net-damage 3)]})) - -(define-card "Archangel" - {:flags {:rd-reveal (req true)} - :access - {:async true - :req (req (not= (first (:zone card)) :discard)) - :effect (effect (show-wait-prompt :runner "Corp to decide to trigger Archangel") - (continue-ability - {:optional - {:prompt "Pay 3 [Credits] to force Runner to encounter Archangel?" - :yes-ability - {:cost [:credit 3] - :async true - :effect (effect (system-msg :corp "pays 3 [Credits] to force the Runner to encounter Archangel") - (clear-wait-prompt :runner) - (continue-ability - :runner - {:optional - {:player :runner - :prompt "You are encountering Archangel. Allow its subroutine to fire?" - :priority 1 - :yes-ability {:async true - :effect (effect (play-subroutine eid {:card card :subroutine 0}))} - :no-ability {:effect (effect (effect-completed eid))}}} - card nil))} - :no-ability {:effect (effect (system-msg :corp "declines to force the Runner to encounter Archangel") - (clear-wait-prompt :runner))}}} - card nil))} - :subroutines [(trace-ability - 6 - {:async true - :effect (effect (show-wait-prompt :runner "Corp to select Archangel target") - (continue-ability - {:choices {:req #(and (installed? %) - (runner? %))} - :label "Add 1 installed card to the Runner's Grip" - :msg "add 1 installed card to the Runner's Grip" - :effect (effect (clear-wait-prompt :runner) - (move :runner target :hand true) - (system-msg (str "adds " (:title target) - " to the Runner's Grip"))) - :cancel-effect (effect (clear-wait-prompt :runner) - (effect-completed eid))} - card nil))})]}) - -(define-card "Archer" - {:additional-cost [:forfeit] - :subroutines [(gain-credits-sub 2) - trash-program - end-the-run]}) - -(define-card "Architect" - {:flags {:untrashable-while-rezzed true} - :subroutines [{:label "Look at the top 5 cards of R&D" - :prompt "Choose a card to install" - :priority true - :activatemsg "uses Architect to look at the top 5 cards of R&D" - :req (req (and (not (string? target)) - (not (operation? target)))) - :not-distinct true - :choices (req (conj (take 5 (:deck corp)) "No install")) - :effect (effect (system-msg (str "chooses the card in position " - (+ 1 (.indexOf (take 5 (:deck corp)) target)) - " from R&D (top is 1)")) - (corp-install (move state side target :play-area) nil {:ignore-all-cost true}))} - {:label "Install a card from HQ or Archives" - :prompt "Select a card to install from Archives or HQ" - :show-discard true - :priority true - :choices {:req #(and (not (operation? %)) - (#{[:hand] [:discard]} (:zone %)) - (corp? %))} - :effect (effect (corp-install target nil)) - :msg (msg (corp-install-msg target))}]}) - -(define-card "Afshar" - {:implementation "Breaking both subs not restricted" - :subroutines [{:msg "make the Runner lose 2 [Credits]" - :effect (effect (lose-credits :runner 2))} - end-the-run]}) - -(define-card "Ashigaru" - {:abilities [{:label "Gain subroutines" - :msg (msg "gain " (count (:hand corp)) " subroutines")}] - :subroutines [end-the-run]}) - -(define-card "Assassin" - {:subroutines [(trace-ability 5 (do-net-damage 3)) - (trace-ability 4 trash-program)]}) - -(define-card "Asteroid Belt" - (space-ice end-the-run)) - -(define-card "Authenticator" - {:implementation "Encounter effect is manual" - :abilities [(give-tags 1)] - :runner-abilities [{:label "Take 1 tag" - :async true - :effect (req (system-msg state :runner "takes 1 tag on encountering Authenticator to Bypass it") - (gain-tags state :runner eid 1 {:unpreventable true}))}] - :subroutines [(gain-credits-sub 2) - end-the-run]}) - -(define-card "Bailiff" - {:implementation "Gain credit is manual" - :abilities [(gain-credits-sub 1)] - :subroutines [end-the-run]}) - -(define-card "Bandwidth" - {:subroutines [{:msg "give the Runner 1 tag" - :async true - :effect (effect (gain-tags :corp eid 1) - (register-events - {:successful-run {:effect (effect (lose-tags :corp 1)) - :msg "make the Runner lose 1 tag"} - :run-ends {:effect (effect (unregister-events card))}} - card))}] - :events {:successful-run nil :run-ends nil}}) - -(define-card "Bastion" - {:subroutines [end-the-run]}) - -(define-card "Battlement" - {:subroutines [end-the-run]}) - -(define-card "Blockchain" - (letfn [(sub-count [corp] (int (/ (count (filter #(and (operation? %) - (has-subtype? % "Transaction") - (:seen %)) - (:discard corp))) - 2)))] - {:implementation "Number of subs is manual" - :abilities [{:label "Gain subroutines" - :effect (req (let [c (sub-count corp)] - (update! state :corp - (assoc-in card [:special :extra-subs] (pos? c))) - (system-msg state :corp - (str "uses Blockchain to gain " c (pluralize " additional subroutine" c) - " (" (+ 2 c) " in total)"))))}] - :subroutines [{:label "Gain 1 [Credits], Runner loses 1 [Credits]" - :msg "gain 1 [Credits] and force the Runner to lose 1 [Credits]" - :effect (effect (gain-credits 1) - (lose-credits :runner 1))} - end-the-run]})) - -(define-card "Bloodletter" - {:subroutines [{:label "Runner trashes 1 program or top 2 cards of their Stack" - :effect (req (if (empty? (filter program? (all-active-installed state :runner))) - (do (mill state :runner 2) - (system-msg state :runner (str "trashes the top 2 cards of their Stack"))) - (do (show-wait-prompt state :corp "Runner to choose an option for Bloodletter") - (resolve-ability - state :runner - {:prompt "Trash 1 program or trash top 2 cards of the Stack?" - :choices ["Trash 1 program" "Trash top 2 of Stack"] - :effect (req (if (and (= target "Trash top 2 of Stack") (> (count (:deck runner)) 1)) - (do (mill state :runner 2) - (system-msg state :runner (str "trashes the top 2 cards of their Stack"))) - (resolve-ability state :runner trash-program card nil)) - (clear-wait-prompt state :corp))} - card nil))))}]}) - -(define-card "Bloom" - (let [ice-index (fn [state i] (first (keep-indexed #(when (same-card? %2 i) %1) - (get-in @state (cons :corp (:zone i))))))] - {:subroutines - [{:label "Install a piece of ice from HQ protecting another server, ignoring all costs" - :prompt "Choose ICE to install from HQ in another server" - :async true - :choices {:req #(and (ice? %) - (in-hand? %))} - :effect (req (let [this (zone->name (second (:zone card))) - nice target] - (continue-ability state side - {:prompt (str "Choose a location to install " (:title target)) - :choices (req (remove #(= this %) (corp-install-list state nice))) - :async true - :effect (effect (corp-install nice target {:ignore-all-cost true}))} - card nil)))} - {:label "Install a piece of ice from HQ in the next innermost position, protecting this server, ignoring all costs" - :prompt "Choose ICE to install from HQ in this server" - :async true - :choices {:req #(and (ice? %) - (in-hand? %))} - :effect (req (let [newice (assoc target :zone (:zone card)) - bndx (ice-index state card) - ices (get-in @state (cons :corp (:zone card))) - newices (apply conj (subvec ices 0 bndx) newice (subvec ices bndx))] - (swap! state assoc-in (cons :corp (:zone card)) newices) - (swap! state update-in (cons :corp (:zone target)) - (fn [coll] (remove-once #(same-card? % target) coll))) - (card-init state side newice {:resolve-effect false - :init-data true}) - (trigger-event state side :corp-install newice)))}]})) - -(define-card "Border Control" - {:abilities [{:label "End the run" - :msg (msg "end the run") - :async true - :effect (effect (trash card {:cause :ability-cost}) - (end-run eid card))}] - :subroutines [{:label "Gain 1 [Credits] for each ice protecting this server" - :msg (msg "gain " - (count (:ices (card->server state card))) - " [Credits]") - :effect (req (let [num-ice (count (:ices (card->server state card)))] - (gain-credits state :corp num-ice)))} - end-the-run]}) - -(define-card "Brainstorm" - {:abilities [{:label "Gain subroutines" - :msg (msg "gain " (count (:hand runner)) " subroutines")}] - :subroutines [(do-brain-damage 1)]}) - -(define-card "Builder" - {:abilities [{:label "Move Builder to the outermost position of any server" - :cost [:click 1] - :prompt "Choose a server" - :choices (req servers) - :msg (msg "move it to the outermost position of " target) - :effect (effect (move card (conj (server->zone state target) :ices)))}] - :subroutines [{:label "Place 1 advancement token on an ICE that can be advanced protecting this server" - :msg (msg "place 1 advancement token on " (card-str state target)) - :choices {:req #(and (ice? %) - (can-be-advanced? %))} - :effect (effect (add-prop target :advance-counter 1 {:placed true}))}]}) - -(define-card "Bullfrog" - {:subroutines [(do-psi {:label "Move Bullfrog to another server" - :player :corp - :prompt "Choose a server" - :choices (req servers) - :msg (msg "move it to the outermost position of " target) - :effect (req (let [dest (server->zone state target)] - (swap! state update-in [:run] - #(assoc % - :position (count (get-in corp (conj dest :ices))) - :server (rest dest)))) - (move state side card - (conj (server->zone state target) :ices)) - (effect-completed state side eid))})]}) - -(define-card "Bulwark" - {:effect take-bad-pub - :abilities [{:msg "gain 2 [Credits] if there is an installed AI" - :req (req (some #(has-subtype? % "AI") (all-active-installed state :runner))) - :effect (effect (gain-credits 2))}] - :subroutines [(assoc trash-program - :player :runner - :msg "force the Runner to trash 1 program" - :label "The Runner trashes 1 program") - {:msg "gain 2 [Credits] and end the run" - :effect (effect (gain-credits 2) - (end-run eid card))}]}) - -(define-card "Burke Bugs" - {:subroutines [(trace-ability 0 (assoc trash-program - :not-distinct true - :player :runner - :msg "force the Runner to trash a program" - :label "Force the Runner to trash a program"))]}) - -(define-card "Caduceus" - {:subroutines [(trace-ability 3 (gain-credits-sub 3)) - (trace-ability 2 end-the-run)]}) - -(define-card "Cell Portal" - {:subroutines [{:msg "make the Runner approach the outermost ICE" - :effect (req (let [srv (first (:server run)) - n (count (get-in @state [:corp :servers srv :ices]))] - (swap! state assoc-in [:run :position] n) - (derez state side card)))}]}) - -(define-card "Changeling" - (morph-ice "Barrier" "Sentry" end-the-run)) - -(define-card "Checkpoint" - {:effect take-bad-pub - :subroutines [(trace-ability 5 {:label "Do 3 meat damage when this run is successful" - :msg "do 3 meat damage when this run is successful" - :effect (effect (register-events - {:successful-run - {:async true - :msg "do 3 meat damage" - :effect (effect (damage eid :meat 3 {:card card}))} - :run-ends {:effect (effect (unregister-events card))}} - card))})] - :events {:successful-run nil :run-ends nil}}) - -(define-card "Chetana" - {:subroutines [{:msg "make each player gain 2 [Credits]" - :effect (effect (gain-credits :runner 2) - (gain-credits :corp 2))} - (do-psi {:label "Do 1 net damage for each card in the Runner's grip" - :msg (msg "do " (count (get-in @state [:runner :hand])) " net damage") - :effect (effect (damage eid :net (count (get-in @state [:runner :hand])) {:card card}))})]}) - -(define-card "Chimera" - (let [turn-end-ability {:effect (effect (derez :corp card) - (update! (assoc (get-card state card) :subtype "Mythic")))}] - {:prompt "Choose one subtype" - :choices ["Barrier" "Code Gate" "Sentry"] - :msg (msg "make it gain " target " until the end of the turn") - :effect (effect (update! (assoc card - :subtype-target target - :subtype (combine-subtypes true (:subtype card) target))) - (update-ice-strength card)) - :events {:runner-turn-ends turn-end-ability - :corp-turn-ends turn-end-ability} - :subroutines [end-the-run]})) - -(define-card "Chiyashi" - {:implementation "Trash effect when using an AI to break is activated manually" - :abilities [{:label "Trash the top 2 cards of the Runner's Stack" - :req (req (some #(has-subtype? % "AI") (all-active-installed state :runner))) - :msg (msg (str "trash " (join ", " (map :title (take 2 (:deck runner)))) " from the Runner's Stack")) - :effect (effect (mill :corp :runner 2))}] - :subroutines [(do-net-damage 2) - end-the-run]}) - -(define-card "Chrysalis" - {:flags {:rd-reveal (req true)} - :subroutines [(do-net-damage 2)] - :access {:async true - :req (req (not= (first (:zone card)) :discard)) - :effect (effect (show-wait-prompt :corp "Runner to decide to break Chrysalis subroutine") - (continue-ability - :runner {:optional - {:player :runner - :prompt "You are encountering Chrysalis. Allow its subroutine to fire?" - :priority 1 - :yes-ability {:effect (effect (clear-wait-prompt :corp) - (play-subroutine eid {:card card :subroutine 0}))} - :no-ability {:effect (effect (clear-wait-prompt :corp) - (effect-completed eid))}}} - card nil))}}) - -(define-card "Chum" - {:subroutines [{:label "Give +2 strength to next ICE Runner encounters" - :req (req this-server) - :prompt "Select the ICE the Runner is encountering" - :choices {:req #(and (rezzed? %) (ice? %))} - :msg (msg "give " (:title target) " +2 strength") - :effect (req (let [ice (:cid target)] - (register-events state side - {:pre-ice-strength {:req (req (= (:cid target) ice)) - :effect (effect (ice-strength-bonus 2 target))} - :run-ends {:effect (effect (unregister-events card))}} - card) - (update-all-ice state side)))} - (do-net-damage 3)] - :events {:pre-ice-strength nil :run-ends nil}}) - -(define-card "Clairvoyant Monitor" - {:subroutines [(do-psi {:label "Place 1 advancement token and end the run" - :player :corp - :prompt "Select a target for Clairvoyant Monitor" - :msg (msg "place 1 advancement token on " - (card-str state target) " and end the run") - :choices {:req installed?} - :effect (effect (add-prop target :advance-counter 1 {:placed true}) - (end-run eid))})]}) - -(define-card "Cobra" - {:subroutines [trash-program (do-net-damage 2)]}) - -(define-card "Colossus" - {:advanceable :always - :subroutines [{:label "Give the Runner 1 tag (Give the Runner 2 tags)" - :async true - :msg (msg "give the Runner " (if (wonder-sub card 3) "2 tags" "1 tag")) - :effect (effect (gain-tags :corp eid (if (wonder-sub card 3) 2 1)))} - {:label "Trash 1 program (Trash 1 program and 1 resource)" - :async true - :msg (msg "trash 1 program" (when (wonder-sub card 3) " and 1 resource")) - :effect (req (wait-for (resolve-ability state side trash-program card nil) - (if (wonder-sub card 3) - (continue-ability - state side - {:prompt "Choose a resource to trash" - :msg (msg "trash " (:title target)) - :choices {:req #(and (installed? %) - (resource? %))} - :cancel-effect (req (effect-completed state side eid)) - :effect (effect (trash target {:cause :subroutine}))} - card nil) - (effect-completed state side eid))))}] - :strength-bonus advance-counters}) - -(define-card "Congratulations!" - {:events {:pass-ice {:req (req (same-card? target card)) - :msg "gain 1 [Credits]" - :effect (effect (gain-credits :corp 1))}} - :subroutines [{:label "Gain 2 [Credits]. The Runner gains 1 [Credits]" - :msg "gain 2 [Credits]. The Runner gains 1 [Credits]" - :effect (effect (gain-credits :corp 2) - (gain-credits :runner 1))}]}) - -(define-card "Conundrum" - {:subroutines [(assoc trash-program - :player :runner - :msg "force the Runner to trash 1 program" - :label "The Runner trashes 1 program") - {:msg "force the Runner to lose 1 [Click] if able" - :effect runner-loses-click} - end-the-run] - :strength-bonus (req (if (some #(has-subtype? % "AI") (all-active-installed state :runner)) 3 0))}) - -(define-card "Cortex Lock" - {:subroutines [{:label "Do 1 net damage for each unused memory unit the Runner has" - :msg (msg "do " (available-mu state) " net damage") - :effect (effect (damage eid :net (available-mu state) {:card card}))}]}) - -(define-card "Crick" - {:subroutines [{:label "install a card from Archives" - :prompt "Select a card to install from Archives" - :show-discard true - :priority true - :choices {:req #(and (not (operation? %)) - (in-discard? %) - (corp? %))} - :msg (msg (corp-install-msg target)) - :effect (effect (corp-install target nil))}] - :strength-bonus (req (if (= (second (:zone card)) :archives) 3 0))}) - -(define-card "Curtain Wall" - {:subroutines [end-the-run] - :strength-bonus (req (let [ices (:ices (card->server state card))] - (if (same-card? card (last ices)) 4 0))) - :events (let [cw {:req (req (and (not (same-card? card target)) - (= (card->server state card) (card->server state target)))) - :effect (effect (update-ice-strength card))}] - {:corp-install cw - :trash cw - :card-moved cw})}) - -(define-card "Data Hound" - (letfn [(dh-trash [cards] - {:prompt "Choose a card to trash" - :choices cards - :async true - :msg (msg "trash " (:title target)) - :effect (req (trash state side target {:unpreventable true}) - (continue-ability - state side - (reorder-choice - :runner :runner (remove-once #(= % target) cards) - '() (count (remove-once #(= % target) cards)) - (remove-once #(= % target) cards)) - card nil))})] - {:subroutines [(trace-ability - 2 - {:async true - :label "Look at the top of Stack" - :msg "look at top X cards of Stack" - :effect (req (show-wait-prompt state :runner "Corp to rearrange the top cards of the Runner's Stack") - (let [c (- target (second targets)) - from (take c (:deck runner))] - (system-msg state :corp - (str "looks at the top " c " cards of Stack")) - (if (< 1 c) - (continue-ability state side (dh-trash from) card nil) - (do (system-msg state :corp (str "trashes " (:title (first from)))) - (trash state side (first from) {:unpreventable true}) - (clear-wait-prompt state :runner) - (effect-completed state side eid)))))})]})) - -(define-card "Data Loop" - (let [ability {:label "Add 2 cards from your Grip to the top of the Stack" - :req (req (pos? (count (:hand runner)))) - :effect (req (let [n (min 2 (count (:hand runner)))] - (resolve-ability state side - {:prompt (msg "Choose " n " cards in your Grip to add to the top of the Stack (first card targeted will be topmost)") - :choices {:max n :all true - :req #(and (in-hand? %) (runner? %))} - :effect (req (doseq [c targets] - (move state :runner c :deck {:front true})) - (system-msg state :runner (str "adds " n " cards from their Grip to the top of the Stack")))} - card nil)))}] - {:implementation "Encounter effect is manual" - :subroutines [end-the-run-if-tagged - end-the-run] - :abilities [ability] - :runner-abilities [ability]})) - -(define-card "Data Mine" - {:subroutines [{:msg "do 1 net damage" - :effect (req (damage state :runner eid :net 1 {:card card}) - (when current-ice - (no-action state side nil) - (continue state side nil)) - (trash state side card))}]}) - -(define-card "Data Raven" - {:implementation "Encounter effect is manual" - :abilities [(give-tags 1) - (power-counter-ability (give-tags 1))] - :runner-abilities [{:label "End the run" - :effect (req (end-run state :runner) - (system-msg state :runner "chooses to end the run on encountering Data Raven"))} - {:label "Take 1 tag" - :async true - :effect (req (system-msg state :runner "chooses to take 1 tag on encountering Data Raven") - (gain-tags state :runner eid 1))}] - :subroutines [(trace-ability 3 add-power-counter)]}) - -(define-card "Data Ward" - {:runner-abilities [{:label "Pay 3 [Credits]" - :effect (req (pay state :runner card :credit 3) - (system-msg state :runner "chooses to pay 3 [Credits] on encountering Data Ward"))} - {:label "Take 1 tag" - :async true - :effect (req (system-msg state :runner "chooses to take 1 tag on encountering Data Ward") - (gain-tags state :runner eid 1))}] - :subroutines [end-the-run-if-tagged]}) - -(define-card "Datapike" - {:subroutines [{:msg "force the Runner to pay 2 [Credits] if able" - :effect (effect (pay :runner card :credit 2))} - end-the-run]}) - -(define-card "DNA Tracker" - {:subroutines [{:msg "do 1 net damage and make the Runner lose 2 [Credits]" - :effect (req (wait-for (damage state side :net 1 {:card card}) - (lose-credits state :runner 2)))}]}) - -(define-card "Dracō" - {:prompt "How many power counters?" - :choices :credit - :msg (msg "add " target " power counters") - :effect (effect (add-counter card :power target) - (update-ice-strength card)) - :strength-bonus (req (get-counters card :power)) - :subroutines [(trace-ability 2 {:label "Give the Runner 1 tag and end the run" - :msg "give the Runner 1 tag and end the run" - :async true - :effect (effect (gain-tags :corp eid 1) - (end-run))})]}) - -(define-card "Eli 1.0" - {:subroutines [end-the-run] - :runner-abilities [(runner-break [:click 1] 1)]}) - -(define-card "Eli 2.0" - {:subroutines [{:msg "draw 1 card" :effect (effect (draw))} - end-the-run] - :runner-abilities [(runner-break [:click 2] 2)]}) - -(define-card "Endless EULA" - {:subroutines [end-the-run] - :runner-abilities [(runner-pay [:credit 1] 1) - (runner-pay [:credit 6] 6)]}) - -(define-card "Enforcer 1.0" - {:additional-cost [:forfeit] - :subroutines [trash-program - (do-brain-damage 1) - {:label "Trash a console" - :prompt "Select a console to trash" - :choices {:req #(has-subtype? % "Console")} - :msg (msg "trash " (:title target)) - :effect (effect (trash target))} - {:msg "trash all virtual resources" - :effect (req (doseq [c (filter #(has-subtype? % "Virtual") (all-active-installed state :runner))] - (trash state side c)))}] - :runner-abilities [(runner-break [:click 1] 1)]}) - -(define-card "Enigma" - {:subroutines [{:msg "force the Runner to lose 1 [Click] if able" - :effect runner-loses-click} - end-the-run]}) - -(define-card "Envelope" - {:subroutines [(do-net-damage 1) - end-the-run]}) - -(define-card "Errand Boy" - {:subroutines [(gain-credits-sub 1) - {:msg "draw 1 card" :effect (effect (draw))}]}) - -(define-card "Excalibur" - {:subroutines [{:label "The Runner cannot make another run this turn" - :msg "prevent the Runner from making another run" - :effect (effect (register-turn-flag! card :can-run nil))}]}) - -(define-card "Executive Functioning" - {:subroutines [(trace-ability 4 (do-brain-damage 1))]}) - -(define-card "Fairchild" - {:subroutines [end-the-run - (do-brain-damage 1)] - :runner-abilities [(runner-break [:credit 4] 1)]}) - -(define-card "Fairchild 1.0" - {:subroutines [{:label "Force the Runner to pay 1 [Credits] or trash an installed card" - :msg "force the Runner to pay 1 [Credits] or trash an installed card" - :player :runner - :prompt "Choose one" - :choices ["Pay 1 [Credits]" "Trash an installed card"] - :effect (req (if (= target "Pay 1 [Credits]") - (do (pay state side card :credit 1) - (system-msg state side "pays 1 [Credits]")) - (resolve-ability state :runner trash-installed card nil)))}] - :runner-abilities [(runner-break [:click 1] 1)]}) - -(define-card "Fairchild 2.0" - {:subroutines [{:label "Force the Runner to pay 2 [Credits] or trash an installed card" - :msg "force the Runner to pay 2 [Credits] or trash an installed card" - :player :runner - :prompt "Choose one" - :choices ["Pay 2 [Credits]" "Trash an installed card"] - :effect (req (if (= target "Pay 2 [Credits]") - (do (pay state side card :credit 2) - (system-msg state side "pays 2 [Credits]")) - (resolve-ability state :runner trash-installed card nil)))} - (do-brain-damage 1)] - :runner-abilities [(runner-break [:click 2] 2)]}) - -(define-card "Fairchild 3.0" - {:subroutines [{:label "Force the Runner to pay 3 [Credits] or trash an installed card" - :msg "force the Runner to pay 3 [Credits] or trash an installed card" - :player :runner - :prompt "Choose one" - :choices ["Pay 3 [Credits]" "Trash an installed card"] - :effect (req (if (= target "Pay 3 [Credits]") - (do (pay state side card :credit 3) - (system-msg state side "pays 3 [Credits]")) - (resolve-ability state :runner trash-installed card nil)))} - {:label "Do 1 brain damage or end the run" - :prompt "Choose one" - :choices ["Do 1 brain damage" "End the run"] - :msg (msg (lower-case target)) - :effect (req (if (= target "Do 1 brain damage") - (damage state side eid :brain 1 {:card card}) - (end-run state side)))}] - :runner-abilities [(runner-break [:click 3] 3)]}) - -(define-card "Fenris" - {:effect take-bad-pub - :subroutines [(do-brain-damage 1) - end-the-run]}) - -(define-card "Fire Wall" - {:advanceable :always - :subroutines [end-the-run] - :strength-bonus advance-counters}) - -(define-card "Flare" - {:subroutines [(trace-ability 6 {:label "Trash 1 hardware, do 2 meat damage, and end the run" - :msg "trash 1 hardware, do 2 meat damage, and end the run" - :async true - :effect (effect (continue-ability - {:prompt "Select a piece of hardware to trash" - :label "Trash a piece of hardware" - :choices {:req hardware?} - :msg (msg "trash " (:title target)) - :effect (req (wait-for - (trash state side target {:cause :subroutine}) - (do (damage state side eid :meat 2 {:unpreventable true - :card card}) - (end-run state side)))) - :cancel-effect (effect (damage eid :meat 2 {:unpreventable true :card card}) - (end-run))} - card nil))})]}) - -(define-card "Formicary" - {:optional {:prompt "Move Formicary?" - :req (req (and (:run @state) - (zero? (:position run)) - (not (contains? run :corp-phase-43)) - (not (contains? run :successful)))) - :yes-ability {:msg "rez and move Formicary. The Runner is now approaching Formicary." - :effect (req (move state side card - [:servers (first (:server run)) :ices] - {:front true}) - (swap! state assoc-in [:run :position] 1))} - :no-ability {:msg "rez Formicary without moving it"}} - :subroutines [{:label "End the run unless the Runner suffers 2 net damage" - :async true - :effect (req (wait-for (resolve-ability - state :runner - {:optional - {:prompt "Suffer 2 net damage? (If not, end the run)" - :yes-ability {:async true - :msg "let the Runner suffer 2 net damage" - :effect (effect (damage eid :net 2 {:card card :unpreventable true}))} - :no-ability end-the-run}} - card nil)))}]}) - -(define-card "Free Lunch" - {:abilities [(power-counter-ability {:label "Runner loses 1 [Credits]" - :msg "make the Runner lose 1 [Credits]" - :effect (effect (lose-credits :runner 1))})] - :subroutines [add-power-counter]}) - -(define-card "Galahad" - (grail-ice end-the-run)) - -(define-card "Gatekeeper" - (let [draw {:async true - :prompt "Draw how many cards?" - :choices {:number (req 3) - :max (req 3) - :default (req 1)} - :msg (msg "draw " target "cards") - :effect (effect (draw eid target nil))} - reveal-and-shuffle {:prompt "Reveal and shuffle up to 3 agendas" - :show-discard true - :choices {:req #(and (corp? %) - (or (= [:discard] (:zone %)) - (= [:hand] (:zone %))) - (agenda? %)) - :max (req 3)} - :effect (req (reveal state side targets) - (doseq [c targets] - (move state :corp c :deck)) - (shuffle! state :corp :deck)) - :cancel-effect (effect (shuffle! :deck)) - :msg (msg "add " - (str (join ", " (map :title targets))) - " to R&D")} - draw-reveal-shuffle {:async true - :label "Draw cards, reveal and shuffle agendas" - :effect (req (wait-for (resolve-ability state side draw card nil) - (continue-ability state side reveal-and-shuffle card nil)))}] - {:strength-bonus (req (if (= :this-turn (:rezzed card)) 6 0)) - :subroutines [draw-reveal-shuffle - end-the-run]})) - -(define-card "Gemini" - (constellation-ice (do-net-damage 1))) - -(define-card "Grim" - {:effect take-bad-pub - :subroutines [trash-program]}) - -(define-card "Guard" - {:implementation "Prevent bypass is manual" - :subroutines [end-the-run]}) - -(define-card "Gutenberg" - {:subroutines [(tag-trace 7)] - :strength-bonus (req (if (= (second (:zone card)) :rd) 3 0))}) - -(define-card "Gyri Labyrinth" - {:implementation "Hand size is not restored if trashed or derezzed after firing" - :subroutines [{:req (req (:run @state)) - :label "Reduce Runner's maximum hand size by 2 until start of next Corp turn" - :msg "reduce the Runner's maximum hand size by 2 until the start of the next Corp turn" - :effect (effect (lose :runner :hand-size 2) - (register-events {:corp-turn-begins - {:msg "increase the Runner's maximum hand size by 2" - :effect (effect (gain :runner :hand-size 2) - (unregister-events card))}} card))}] - :events {:corp-turn-begins nil}}) - -(define-card "Hadrian's Wall" - {:advanceable :always - :subroutines [end-the-run] - :strength-bonus advance-counters}) - -(define-card "Hagen" - {:subroutines [{:label "Trash 1 program" - :prompt "Choose a program that is not a decoder, fracter or killer" - :msg (msg "trash " (:title target)) - :choices {:req #(and (installed? %) - (program? %) - (not (has-subtype? % "Decoder")) - (not (has-subtype? % "Fracter")) - (not (has-subtype? % "Killer")))} - :effect (effect (trash target {:cause :subroutine}) - (clear-wait-prompt :runner))} - end-the-run] - :strength-bonus (req (- (count (filter #(has-subtype? % "Icebreaker") - (all-active-installed state :runner)))))}) - -(define-card "Hailstorm" - {:subroutines [{:label "Remove a card in the Heap from the game" - :prompt "Choose a card in the Runner's Heap" - :choices (req (:discard runner)) - :msg (msg "remove " (:title target) " from the game") - :effect (effect (move :runner target :rfg))} - end-the-run]}) - -(define-card "Harvester" - {:subroutines [{:label "Runner draws 3 cards and discards down to maximum hand size" - :msg "make the Runner draw 3 cards and discard down to their maximum hand size" - :async true - :effect (req (wait-for (draw state :runner 3 nil) - (let [delta (- (count (get-in @state [:runner :hand])) (hand-size state :runner))] - (if (pos? delta) - (continue-ability - state :runner - {:prompt (msg "Select " delta " cards to discard") - :player :runner - :choices {:max delta - :req #(in-hand? %)} - :effect (req (doseq [c targets] - (trash state :runner c)) - (system-msg state :runner - (str "trashes " (join ", " (map :title targets)))))} - card nil) - (effect-completed state side eid)))))}]}) - -(define-card "Heimdall 1.0" - {:subroutines [(do-brain-damage 1) - end-the-run] - :runner-abilities [(runner-break [:click 1] 1)]}) - -(define-card "Heimdall 2.0" - {:subroutines [(do-brain-damage 1) - {:msg "do 1 brain damage and end the run" :effect (effect (damage eid :brain 1 {:card card}) (end-run))} - end-the-run] - :runner-abilities [(runner-break [:click 2] 2)]}) - -(define-card "Herald" - {:flags {:rd-reveal (req true)} - :subroutines [(gain-credits-sub 2) - {:async true - :label "Pay up to 2 [Credits] to place up to 2 advancement tokens" - :prompt "How many advancement tokens?" - :choices (req (map str (range (inc (min 2 (:credit corp)))))) - :effect (req (let [c (str->int target)] - (if (can-pay? state side eid card (:title card) :credit c) - (do (pay state :corp card :credit c) - (continue-ability - state side - {:msg (msg "pay " c "[Credits] and place " (quantify c " advancement token") - " on " (card-str state target)) - :choices {:req can-be-advanced?} - :effect (effect (add-prop target :advance-counter c {:placed true}))} - card nil)) - (effect-completed state side eid))))}] - :access {:async true - :req (req (not= (first (:zone card)) :discard)) - :effect (effect (show-wait-prompt :corp "Runner to decide to break Herald subroutines") - (continue-ability - :runner {:optional - {:player :runner - :prompt "You are encountering Herald. Allow its subroutines to fire?" - :priority 1 - :yes-ability {:effect (effect (clear-wait-prompt :corp) - (play-subroutine :corp eid {:card card :subroutine 0}) - (play-subroutine :corp eid {:card card :subroutine 1}))} - :no-ability {:effect (effect (clear-wait-prompt :corp) - (effect-completed eid))}}} - card nil))}}) - -(define-card "Himitsu-Bako" - {:abilities [{:msg "add it to HQ" - :cost [:credit 1] - :effect (effect (move card :hand))}] - :subroutines [end-the-run]}) - -(define-card "Hive" - {:abilities [{:label "Gain subroutines" - :msg (msg "gain " (min 5 (max 0 (- 5 (:agenda-point corp 0)))) " subroutines")}] - :subroutines [end-the-run]}) - -(define-card "Holmegaard" - {:subroutines [(trace-ability 4 {:label "Runner cannot access any cards this run" - :msg "stop the Runner from accessing any cards this run" - :effect (effect (prevent-access))}) - {:label "Trash an icebreaker" - :prompt "Choose an icebreaker to trash" - :msg (msg "trash " (:title target)) - :choices {:req #(and (installed? %) - (has? % :subtype "Icebreaker"))} - :effect (effect (trash target {:cause :subroutine}) - (clear-wait-prompt :runner))}]}) - -(define-card "Hortum" - (letfn [(hort [n] {:prompt "Choose a card to add to HQ with Hortum" +(def card-definitions + {"Aiki" + {:subroutines [(do-psi {:label "Runner draws 2 cards" + :msg "make the Runner draw 2 cards" + :effect (effect (draw :runner 2) + (effect-completed eid))}) + (do-net-damage 1)]} + + "Aimor" + {:subroutines [{:label "Trash the top 3 cards of the Stack. Trash Aimor." + :effect (req (when (not-empty (:deck runner)) + (system-msg state :corp + (str "uses Aimor to trash " + (join ", " (map :title (take 3 (:deck runner)))) + " from the Runner's Stack")) + (mill state :corp :runner 3)) + (when current-ice + (no-action state :corp nil) + (continue state :runner nil)) + (trash state side card) + (system-msg state side (str "trashes Aimor")))}]} + + "Anansi" + (let [corp-draw {:optional {:prompt "Draw 1 card?" + :yes-ability {:async true + :msg "draw 1 card" + :effect (effect (draw eid 1 nil))}}} + runner-draw {:async true + :effect (req (show-wait-prompt state :corp "Runner to decide on card draw") + (continue-ability state side + {:player :runner + :optional + {:prompt "Pay 2 [Credits] to draw 1 card?" + :no-ability {:effect (effect (system-msg :runner "does not draw 1 card") + (clear-wait-prompt :corp))} + :yes-ability {:async true + :effect (effect + (system-msg :runner "pays 2 [Credits] to draw 1 card") + (lose-credits 2) + (clear-wait-prompt :corp) + (draw eid 1 nil))}}} + card nil))}] + {:implementation "Encounter-ends effect is manually triggered." + :subroutines [{:msg "rearrange the top 5 cards of R&D" :async true - :choices (req (cancellable (:deck corp) :sorted)) - :msg "add 1 card to HQ from R&D" - :cancel-effect (req (shuffle! state side :deck) - (system-msg state side (str "shuffles R&D")) - (effect-completed state side eid)) - :effect (req (move state side target :hand) - (if (< n 2) - (continue-ability state side (hort (inc n)) card nil) - (do (shuffle! state side :deck) - (system-msg state side (str "shuffles R&D")) - (effect-completed state side eid))))})] - {:advanceable :always - :subroutines [{:label "Gain 1 [Credits] (Gain 4 [Credits])" - :msg (msg "gain " (if (wonder-sub card 3) "4" "1") " [Credits]") - :effect (effect (gain-credits :corp (if (wonder-sub card 3) 4 1)))} - {:label "End the run (Search R&D for up to 2 cards and add them to HQ, shuffle R&D, end the run)" - :async true - :effect (req (if (wonder-sub card 3) - (wait-for - (resolve-ability state side (hort 1) card nil) - (do (end-run state side) - (system-msg state side - (str "uses Hortum to add 2 cards to HQ from R&D, " - "shuffle R&D, and end the run")))) - (do (end-run state side) - (system-msg state side (str "uses Hortum to end the run")) - (effect-completed state side eid))))}]})) - -(define-card "Hourglass" - {:subroutines [{:msg "force the Runner to lose 1 [Click] if able" - :effect runner-loses-click}]}) - -(define-card "Howler" - (let [ice-index (fn [state i] (first (keep-indexed #(when (same-card? %2 i) %1) - (get-in @state (cons :corp (:zone i))))))] - {:subroutines - [{:label "Install a piece of Bioroid ICE from HQ or Archives" - :prompt "Install ICE from HQ or Archives?" - :choices ["HQ" "Archives"] - :effect (req (let [fr target] - (resolve-ability state side - {:prompt "Choose a Bioroid ICE to install" - :choices (req (filter #(and (ice? %) - (has-subtype? % "Bioroid")) - ((if (= fr "HQ") :hand :discard) corp))) - :effect (req (let [newice (assoc target :zone (:zone card) :rezzed true) - hndx (ice-index state card) - ices (get-in @state (cons :corp (:zone card))) - newices (apply conj (subvec ices 0 hndx) newice (subvec ices hndx))] - (swap! state assoc-in (cons :corp (:zone card)) newices) - (swap! state update-in (cons :corp (:zone target)) - (fn [coll] (remove-once #(same-card? % target) coll))) - (update! state side (assoc card :howler-target newice)) - (card-init state side newice {:resolve-effect false - :init-data true}) - (trigger-event state side :corp-install newice)))} card nil)))}] - :events {:run-ends {:req (req (:howler-target card)) - :effect (effect (trash card {:cause :self-trash}) - (derez (get-card state (:howler-target card))))}}})) - -(define-card "Hudson 1.0" - {:subroutines [{:msg "prevent the Runner from accessing more than 1 card during this run" - :effect (effect (max-access 1))}] - :runner-abilities [(runner-break [:click 1] 1)]}) - -(define-card "Hunter" - {:subroutines [(tag-trace 3)]}) - -(define-card "Hydra" - (letfn [(otherwise-tag [message ability] - {:msg (msg (if tagged message "give the Runner 1 tag")) - :label (str (capitalize message) " if the Runner is tagged; otherwise, give the Runner 1 tag") - :async true - :effect (req (if tagged - (ability state :runner eid card nil) - (gain-tags state :runner eid 1)))})] - {:subroutines [(otherwise-tag - "do 3 net damage" - (req (damage state :runner :net 3 {:card card}))) - (otherwise-tag - "gain 5 [Credits]" - (req (gain-credits state :corp 5) - (effect-completed state side eid))) - (otherwise-tag - "end the run" - (req (end-run state side eid)))]})) - -(define-card "Ice Wall" - {:advanceable :always - :subroutines [end-the-run] - :strength-bonus advance-counters}) - -(define-card "Ichi 1.0" - {:subroutines [trash-program - (trace-ability 1 {:label "Give the Runner 1 tag and do 1 brain damage" - :msg "give the Runner 1 tag and do 1 brain damage" - :async true - :effect (req (wait-for (damage state :runner :brain 1 {:card card}) - (gain-tags state :corp eid 1)))})] - :runner-abilities [(runner-break [:click 1] 1)]}) - -(define-card "Ichi 2.0" - {:subroutines [trash-program - (trace-ability 3 {:label "Give the Runner 1 tag and do 1 brain damage" - :msg "give the Runner 1 tag and do 1 brain damage" - :async true - :effect (req (wait-for (damage state :runner :brain 1 {:card card}) - (gain-tags state :corp eid 1)))})] - :runner-abilities [(runner-break [:click 2] 2)]}) - -(define-card "Inazuma" - {:abilities [{:msg "prevent the Runner from breaking subroutines on the next piece of ICE they encounter this run"} - {:msg "prevent the Runner from jacking out until after the next piece of ICE" - :effect (effect (register-events - {:pass-ice {:effect (req (swap! state update-in [:run] dissoc :prevent-jack-out) - (unregister-events state side card))}} card) - (prevent-jack-out))}]}) - -(define-card "Information Overload" - {:implementation "Encounter effect is manual" - :abilities [{:label "Gain subroutines" - :msg (msg "gain " (count-tags state) " subroutines")} - (tag-trace 1)] - :subroutines [trash-installed]}) - -(define-card "IP Block" - {:abilities [(assoc (give-tags 1) - :req (req (seq (filter #(has-subtype? % "AI") (all-active-installed state :runner)))) - :label "Give the Runner 1 tag if there is an installed AI")] - :subroutines [(tag-trace 3) - end-the-run-if-tagged]}) - -(define-card "IQ" - {:effect (req (add-watch state (keyword (str "iq" (:cid card))) - (fn [k ref old new] - (let [handsize (count (get-in new [:corp :hand]))] - (when (not= (count (get-in old [:corp :hand])) handsize) - (update! ref side (assoc (get-card ref card) :strength-bonus handsize)) - (update-ice-strength ref side (get-card ref card))))))) - :subroutines [end-the-run] - :strength-bonus (req (count (:hand corp))) - :rez-cost-bonus (req (count (:hand corp))) - :leave-play (req (remove-watch state (keyword (str "iq" (:cid card)))))}) - -(define-card "Ireress" - {:abilities [{:label "Gain subroutines" - :msg (msg "gain " (count-bad-pub corp) " subroutines")}] - :subroutines [{:msg "make the Runner lose 1 [Credits]" - :effect (effect (lose-credits :runner 1))}]}) - -(define-card "It's a Trap!" - {:expose {:msg "do 2 net damage" - :async true - :effect (effect (damage eid :net 2 {:card card}))} - :subroutines [(assoc trash-installed :effect (req (trash state side target {:cause :subroutine}) - (when current-ice - (no-action state side nil) - (continue state side nil)) - (trash state side card)))]}) - -(define-card "Janus 1.0" - {:subroutines [(do-brain-damage 1)] - :runner-abilities [(runner-break [:click 1] 1)]}) - -(define-card "Jua" - {:implementation "Encounter effect is manual" - :abilities [{:msg "prevent the Runner from installing cards for the rest of the turn" - :effect (effect (register-turn-flag! card :runner-lock-install (constantly true)))}] - :subroutines [{:label "Choose 2 installed Runner cards, if able. The Runner must add 1 of those to the top of the Stack." - :req (req (>= (count (all-installed state :runner)) 2)) - :async true - :prompt "Select 2 installed Runner cards" - :choices {:req #(and (runner? %) - (installed? %)) - :max 2 - :all true} - :msg (msg "add either " (card-str state (first targets)) " or " (card-str state (second targets)) " to the Stack") - :effect (req (when (= (count targets) 2) - (show-wait-prompt state :corp "Runner to decide which card to move") - (continue-ability - state :runner - {:player :runner - :priority 1 - :prompt "Select a card to move to the Stack" - :choices targets ;{:req (fn [x] (some #(= % x) targets))} - Alternative version - :effect (req (let [c target] - (clear-wait-prompt state :corp) - (move state :runner c :deck {:front true}) - (system-msg state :runner (str "selected " (card-str state c) " to move to the Stack"))))} - card nil)))}]}) - -(define-card "Kakugo" - {:events {:pass-ice {:async true - :req (req (same-card? target card)) - :msg "do 1 net damage" - :effect (effect (damage eid :net 1 {:card card}))}} - :subroutines [end-the-run]}) - -(define-card "Kamali 1.0" - (letfn [(better-name [kind] (if (= "hardware" kind) "piece of hardware" kind)) - (runner-trash [kind] - {:prompt (str "Select an installed " (better-name kind) " to trash") - :label (str "Trash an installed " (better-name kind)) - :msg (msg "trash " (:title target)) - :async true - :choices {:req #(and (installed? %) - (is-type? % (capitalize kind)))} - :cancel-effect (effect (system-msg (str "fails to trash an installed " (better-name kind))) - (effect-completed eid)) - :effect (effect (trash eid target {:cause :subroutine}))}) - (sub-map [kind] - {:player :runner - :async true - :prompt "Choose one" - :choices ["Take 1 brain damage" (str "Trash an installed " (better-name kind))] - :effect (req (if (= target "Take 1 brain damage") - (do (system-msg state :corp "uses Kamali 1.0 to give the Runner 1 brain damage") - (damage state :runner eid :brain 1 {:card card})) - (continue-ability state :runner (runner-trash kind) card nil)))}) - (brain-trash [kind] - {:label (str "Force the Runner to take 1 brain damage or trash an installed " (better-name kind)) - :msg (str "force the Runner to take 1 brain damage or trash an installed " (better-name kind)) - :async true - :effect (req (show-wait-prompt state :corp "Runner to decide on Kamali 1.0 action") - (wait-for (resolve-ability state side (sub-map kind) card nil) - (clear-wait-prompt state :corp)))})] - {:subroutines [(brain-trash "resource") - (brain-trash "hardware") - (brain-trash "program")] - :runner-abilities [(runner-break [:click 1] 1)]})) - -(define-card "Kitsune" - {:subroutines [{:prompt "Select a card in HQ to force access" - :choices {:req in-hand?} - :label "Force the Runner to access a card in HQ" - :msg (msg "force the Runner to access " (:title target)) - :effect (req (trash state side card) - (wait-for (trigger-event-sync state side :pre-access :hq) - (wait-for (access-card state side target) - (let [from-hq (dec (access-count state side :hq-access))] - (continue-ability - state :runner - (access-helper-hq - state from-hq - ;; access-helper-hq uses a set to keep track of which cards have already - ;; been accessed. by adding HQ root's contents to this set, we make the runner - ;; unable to access those cards, as Kitsune intends. - (conj (set (get-in @state [:corp :servers :hq :content])) target)) - card nil)))))}]}) - -(define-card "Komainu" - {:abilities [{:label "Gain subroutines" - :msg (msg "gain " (count (:hand runner)) " subroutines")}] - :subroutines [(do-net-damage 1)]}) - -(define-card "Lab Dog" - {:subroutines [(assoc trash-hardware - :label "Force the Runner to trash an installed piece of hardware" - :player :runner - :msg (msg "force the Runner to trash " (:title target)) - :effect (req (trash state side target) - (when current-ice - (no-action state side nil) - (continue state side nil)) - (trash state side card)))]}) - -(define-card "Lancelot" - (grail-ice trash-program)) - -(define-card "Little Engine" - {:subroutines [end-the-run - {:msg "make the Runner gain 5 [Credits]" - :effect (effect (gain-credits :runner 5))}]}) - -(define-card "Lockdown" - {:subroutines [{:label "The Runner cannot draw cards for the remainder of this turn" - :msg "prevent the Runner from drawing cards" - :effect (effect (prevent-draw))}]}) - -(define-card "Loki" - {:implementation "Encounter effects not implemented" - :subroutines [{:label "End the run unless the Runner shuffles their Grip into the Stack" - :effect (req (if (zero? (count (:hand runner))) - (do (end-run state side) - (system-msg state :corp (str "uses Loki to end the run"))) - (do (show-wait-prompt state :corp "Runner to decide to shuffle their Grip into the Stack") - (resolve-ability - state :runner - {:optional - {:prompt "Reshuffle your Grip into the Stack?" - :player :runner - :yes-ability {:effect (req (doseq [c (:hand runner)] - (move state :runner c :deck)) - (shuffle! state :runner :deck) - (system-msg state :runner (str "shuffles their Grip into their Stack")) - (clear-wait-prompt state :corp))} - :no-ability {:effect (effect (end-run) - (system-msg :runner (str "doesn't shuffle their Grip into their Stack. Loki ends the run")) - (clear-wait-prompt :corp))}}} - card nil))))}]}) - -(define-card "Loot Box" - (letfn [(top-3 [state] (take 3 (get-in @state [:runner :deck]))) - (top-3-names [state] (map :title (top-3 state)))] - {:subroutines [{:label "End the run unless the Runner pays 2 [Credits]" - :msg "force the Runner to pay 2 [Credits] or end the run" - :player :runner - :prompt "Choose one" - :choices ["Pay 2 [Credits]" "End the run"] - :effect (req (if (= target "Pay 2 [Credits]") - (do (pay state :runner card :credit 2) - (system-msg state side "pays 2 [Credits]")) - (end-run state side)))} - {:label "Reveal the top 3 cards of the Stack" - :effect (effect (system-msg (str "uses Loot Box to reveal the top 3 cards of the stack: " - (join ", " (top-3-names state)))) - (reveal (top-3 state)) - (show-wait-prompt :runner "Corp to choose a card to add to the Grip") - (continue-ability - {:prompt "Choose a card to add to the Grip" - :msg (msg "add " (:title target) " to the Grip, gain " (:cost target) - " [Credits] and shuffle the Stack. Loot Box is trashed") - :choices (req (top-3 state)) - :effect (effect (move :runner target :hand) - (gain-credits :corp (:cost target)) - (shuffle! :runner :deck) - (trash card) - (clear-wait-prompt :runner))} card nil))}]})) - -(define-card "Lotus Field" - {:subroutines [end-the-run] - :flags {:cannot-lower-strength true}}) - -(define-card "Lycan" - (morph-ice "Sentry" "Code Gate" trash-program)) - -(define-card "Macrophage" - {:subroutines [(trace-ability 4 {:label "Purge virus counters" - :msg "purge virus counters" - :effect (effect (purge))}) - (trace-ability 3 {:label "Trash a virus" - :prompt "Choose a virus to trash" - :msg (msg "trash " (:title target)) - :choices {:req #(and (installed? %) - (has? % :subtype "Virus"))} - :effect (effect (trash target {:cause :subroutine}) - (clear-wait-prompt :runner))}) - (trace-ability 2 {:label "Remove a virus in the Heap from the game" - :prompt "Choose a virus in the Heap to remove from the game" - :choices (req (cancellable (filter #(has? % :subtype "Virus") (:discard runner)) :sorted)) - :msg (msg "remove " (:title target) " from the game") - :effect (effect (move :runner target :rfg))}) - (trace-ability 1 end-the-run)]}) - -(define-card "Magnet" - (letfn [(disable-hosted [state side c] - (doseq [hc (:hosted (get-card state c))] - (unregister-events state side hc) - (update! state side (dissoc hc :abilities))))] + :effect (req (show-wait-prompt state :runner "Corp to rearrange the top cards of R&D") + (let [from (take 5 (:deck corp))] + (if (pos? (count from)) + (continue-ability state side (reorder-choice :corp :runner from '() + (count from) from) + card nil) + (do (clear-wait-prompt state :runner) + (effect-completed state side eid)))))} + {:label "Draw 1 card; allow runner to draw 1 card" + :async true + :effect (req (wait-for (resolve-ability state side corp-draw card nil) + (continue-ability state :runner runner-draw card nil)))} + (do-net-damage 1)] + :abilities [(do-net-damage 3)]}) + + "Archangel" + {:flags {:rd-reveal (req true)} + :access {:async true - :effect (req (let [magnet card] - (wait-for (resolve-ability - state side - {:req (req (some #(some program? (:hosted %)) - (remove-once #(same-card? % magnet) - (filter ice? (all-installed state corp))))) - :prompt "Select a Program to host on Magnet" - :choices {:req #(and (program? %) - (ice? (:host %)) - (not (same-card? (:host %) magnet)))} - :effect (effect (host card target))} - card nil) - (disable-hosted state side card)))) - :derez-effect {:req (req (not-empty (:hosted card))) - :effect (req (doseq [c (get-in card [:hosted])] - (card-init state side c {:resolve-effect false})))} - :events {:runner-install {:req (req (same-card? card (:host target))) - :effect (req (disable-hosted state side card) - (update-ice-strength state side card))}} - :subroutines [end-the-run]})) - -(define-card "Mamba" - {:abilities [(power-counter-ability (do-net-damage 1))] - :subroutines [(do-net-damage 1) - (do-psi {:label "Add 1 power counter" - :msg "add 1 power counter" - :effect (effect (add-counter card :power 1) - (effect-completed eid))})]}) - -(define-card "Marker" - {:subroutines [{:label "Give the next ICE encountered \"End the run\" for the remainder of the run" - :msg (msg "give the next ICE encountered \"[Subroutine] End the run\" after all its other subroutines for the remainder of the run")}]}) - -(define-card "Markus 1.0" - {:subroutines [trash-installed end-the-run] - :runner-abilities [(runner-break [:click 1] 1)]}) - -(define-card "Masvingo" - {:implementation "Number of subs is manual" - :advanceable :always - :abilities [{:label "Gain subroutines" - :msg (msg "gain " (get-counters card :advancement) " subroutines")}] - :effect (effect (add-prop card :advance-counter 1)) - :subroutines [end-the-run]}) - -(define-card "Matrix Analyzer" - {:implementation "Encounter effect is manual" - :abilities [{:label "Place 1 advancement token on a card that can be advanced" - :msg (msg "place 1 advancement token on " (card-str state target)) - :choices {:req can-be-advanced?} - :cost [:credit 1] :effect (effect (add-prop target :advance-counter 1))}] - :subroutines [(tag-trace 2)]}) - -(define-card "Mausolus" - {:advanceable :always - :subroutines [{:label "Gain 1 [Credits] (Gain 3 [Credits])" - :msg (msg "gain " (if (wonder-sub card 3) 3 1) "[Credits]") - :effect (effect (gain-credits (if (wonder-sub card 3) 3 1)))} - {:label "Do 1 net damage (Do 3 net damage)" - :async true - :msg (msg "do " (if (wonder-sub card 3) 3 1) " net damage") - :effect (effect (damage eid :net (if (wonder-sub card 3) 3 1) {:card card}))} - {:label "Give the Runner 1 tag (and end the run)" - :async true - :msg (msg "give the Runner 1 tag" - (when (wonder-sub card 3) - " and end the run")) - :effect (req (gain-tags state :corp eid 1) - (when (wonder-sub card 3) - (end-run state side)))}]}) - -(define-card "Meridian" - {:subroutines [{:label "Gain 4 [Credits] and end the run, unless the runner adds Meridian to their score area as an agenda worth -1 agenda points" - :async true - :effect (req (show-wait-prompt state :corp "Runner to choose an option for Meridian") - (continue-ability - state :runner - {:prompt "Choose one" - :choices ["End the run" "Add Meridian to score area"] - :player :runner - :async true - :effect (req (if (= target "End the run") - (do (system-msg state :corp (str "uses Meridian to gain 4 [Credits] and end the run")) - (clear-wait-prompt state :corp) - (gain-credits state :corp 4) - (end-run state :runner eid)) - (do (system-msg state :runner (str "adds Meridian to their score area as an agenda worth -1 agenda points")) - (clear-wait-prompt state :corp) - (wait-for (as-agenda state :runner card -1) - (when current-ice - (no-action state side nil) - (continue state side nil)) - (effect-completed state side eid)))))} - card nil))}]}) - -(define-card "Merlin" - (grail-ice (do-net-damage 2))) - -(define-card "Meru Mati" - {:subroutines [end-the-run] - :strength-bonus (req (if (= (second (:zone card)) :hq) 3 0))}) - -(define-card "Metamorph" - {:subroutines [{:label "Swap two ICE or swap two installed non-ICE" - :msg "swap two ICE or swap two installed non-ICE" - :async true - :prompt "Choose one" - :choices ["Swap two ICE" "Swap two non-ICE"] - :effect (req (if (= target "Swap two ICE") - (continue-ability - state side - {:prompt "Select the two ICE to swap" + :req (req (not= (first (:zone card)) :discard)) + :effect (effect (show-wait-prompt :runner "Corp to decide to trigger Archangel") + (continue-ability + {:optional + {:prompt "Pay 3 [Credits] to force Runner to encounter Archangel?" + :yes-ability + {:cost [:credit 3] + :async true + :effect (effect (system-msg :corp "pays 3 [Credits] to force the Runner to encounter Archangel") + (clear-wait-prompt :runner) + (continue-ability + :runner + {:optional + {:player :runner + :prompt "You are encountering Archangel. Allow its subroutine to fire?" + :priority 1 + :yes-ability {:async true + :effect (effect (play-subroutine eid {:card card :subroutine 0}))} + :no-ability {:effect (effect (effect-completed eid))}}} + card nil))} + :no-ability {:effect (effect (system-msg :corp "declines to force the Runner to encounter Archangel") + (clear-wait-prompt :runner))}}} + card nil))} + :subroutines [(trace-ability + 6 + {:async true + :effect (effect (show-wait-prompt :runner "Corp to select Archangel target") + (continue-ability + {:choices {:req #(and (installed? %) + (runner? %))} + :label "Add 1 installed card to the Runner's Grip" + :msg "add 1 installed card to the Runner's Grip" + :effect (effect (clear-wait-prompt :runner) + (move :runner target :hand true) + (system-msg (str "adds " (:title target) + " to the Runner's Grip"))) + :cancel-effect (effect (clear-wait-prompt :runner) + (effect-completed eid))} + card nil))})]} + + "Archer" + {:additional-cost [:forfeit] + :subroutines [(gain-credits-sub 2) + trash-program + end-the-run]} + + "Architect" + {:flags {:untrashable-while-rezzed true} + :subroutines [{:label "Look at the top 5 cards of R&D" + :prompt "Choose a card to install" + :priority true + :activatemsg "uses Architect to look at the top 5 cards of R&D" + :req (req (and (not (string? target)) + (not (operation? target)))) + :not-distinct true + :choices (req (conj (take 5 (:deck corp)) "No install")) + :effect (effect (system-msg (str "chooses the card in position " + (+ 1 (.indexOf (take 5 (:deck corp)) target)) + " from R&D (top is 1)")) + (corp-install (move state side target :play-area) nil {:ignore-all-cost true}))} + {:label "Install a card from HQ or Archives" + :prompt "Select a card to install from Archives or HQ" + :show-discard true + :priority true + :choices {:req #(and (not (operation? %)) + (#{[:hand] [:discard]} (:zone %)) + (corp? %))} + :effect (effect (corp-install target nil)) + :msg (msg (corp-install-msg target))}]} + + "Afshar" + {:implementation "Breaking both subs not restricted" + :subroutines [{:msg "make the Runner lose 2 [Credits]" + :effect (effect (lose-credits :runner 2))} + end-the-run]} + + "Ashigaru" + {:abilities [{:label "Gain subroutines" + :msg (msg "gain " (count (:hand corp)) " subroutines")}] + :subroutines [end-the-run]} + + "Assassin" + {:subroutines [(trace-ability 5 (do-net-damage 3)) + (trace-ability 4 trash-program)]} + + "Asteroid Belt" + (space-ice end-the-run) + + "Authenticator" + {:implementation "Encounter effect is manual" + :abilities [(give-tags 1)] + :runner-abilities [{:label "Take 1 tag" + :async true + :effect (req (system-msg state :runner "takes 1 tag on encountering Authenticator to Bypass it") + (gain-tags state :runner eid 1 {:unpreventable true}))}] + :subroutines [(gain-credits-sub 2) + end-the-run]} + + "Bailiff" + {:implementation "Gain credit is manual" + :abilities [(gain-credits-sub 1)] + :subroutines [end-the-run]} + + "Bandwidth" + {:subroutines [{:msg "give the Runner 1 tag" + :async true + :effect (effect (gain-tags :corp eid 1) + (register-events + {:successful-run {:effect (effect (lose-tags :corp 1)) + :msg "make the Runner lose 1 tag"} + :run-ends {:effect (effect (unregister-events card))}} + card))}] + :events {:successful-run nil :run-ends nil}} + + "Bastion" + {:subroutines [end-the-run]} + + "Battlement" + {:subroutines [end-the-run]} + + "Blockchain" + (letfn [(sub-count [corp] (int (/ (count (filter #(and (operation? %) + (has-subtype? % "Transaction") + (:seen %)) + (:discard corp))) + 2)))] + {:implementation "Number of subs is manual" + :abilities [{:label "Gain subroutines" + :effect (req (let [c (sub-count corp)] + (update! state :corp + (assoc-in card [:special :extra-subs] (pos? c))) + (system-msg state :corp + (str "uses Blockchain to gain " c (pluralize " additional subroutine" c) + " (" (+ 2 c) " in total)"))))}] + :subroutines [{:label "Gain 1 [Credits], Runner loses 1 [Credits]" + :msg "gain 1 [Credits] and force the Runner to lose 1 [Credits]" + :effect (effect (gain-credits 1) + (lose-credits :runner 1))} + end-the-run]}) + + "Bloodletter" + {:subroutines [{:label "Runner trashes 1 program or top 2 cards of their Stack" + :effect (req (if (empty? (filter program? (all-active-installed state :runner))) + (do (mill state :runner 2) + (system-msg state :runner (str "trashes the top 2 cards of their Stack"))) + (do (show-wait-prompt state :corp "Runner to choose an option for Bloodletter") + (resolve-ability + state :runner + {:prompt "Trash 1 program or trash top 2 cards of the Stack?" + :choices ["Trash 1 program" "Trash top 2 of Stack"] + :effect (req (if (and (= target "Trash top 2 of Stack") (> (count (:deck runner)) 1)) + (do (mill state :runner 2) + (system-msg state :runner (str "trashes the top 2 cards of their Stack"))) + (resolve-ability state :runner trash-program card nil)) + (clear-wait-prompt state :corp))} + card nil))))}]} + + "Bloom" + (let [ice-index (fn [state i] (first (keep-indexed #(when (same-card? %2 i) %1) + (get-in @state (cons :corp (:zone i))))))] + {:subroutines + [{:label "Install a piece of ice from HQ protecting another server, ignoring all costs" + :prompt "Choose ICE to install from HQ in another server" + :async true + :choices {:req #(and (ice? %) + (in-hand? %))} + :effect (req (let [this (zone->name (second (:zone card))) + nice target] + (continue-ability state side + {:prompt (str "Choose a location to install " (:title target)) + :choices (req (remove #(= this %) (corp-install-list state nice))) + :async true + :effect (effect (corp-install nice target {:ignore-all-cost true}))} + card nil)))} + {:label "Install a piece of ice from HQ in the next innermost position, protecting this server, ignoring all costs" + :prompt "Choose ICE to install from HQ in this server" + :async true + :choices {:req #(and (ice? %) + (in-hand? %))} + :effect (req (let [newice (assoc target :zone (:zone card)) + bndx (ice-index state card) + ices (get-in @state (cons :corp (:zone card))) + newices (apply conj (subvec ices 0 bndx) newice (subvec ices bndx))] + (swap! state assoc-in (cons :corp (:zone card)) newices) + (swap! state update-in (cons :corp (:zone target)) + (fn [coll] (remove-once #(same-card? % target) coll))) + (card-init state side newice {:resolve-effect false + :init-data true}) + (trigger-event state side :corp-install newice)))}]}) + + "Border Control" + {:abilities [{:label "End the run" + :msg (msg "end the run") + :async true + :effect (effect (trash card {:cause :ability-cost}) + (end-run eid card))}] + :subroutines [{:label "Gain 1 [Credits] for each ice protecting this server" + :msg (msg "gain " + (count (:ices (card->server state card))) + " [Credits]") + :effect (req (let [num-ice (count (:ices (card->server state card)))] + (gain-credits state :corp num-ice)))} + end-the-run]} + + "Brainstorm" + {:abilities [{:label "Gain subroutines" + :msg (msg "gain " (count (:hand runner)) " subroutines")}] + :subroutines [(do-brain-damage 1)]} + + "Builder" + {:abilities [{:label "Move Builder to the outermost position of any server" + :cost [:click 1] + :prompt "Choose a server" + :choices (req servers) + :msg (msg "move it to the outermost position of " target) + :effect (effect (move card (conj (server->zone state target) :ices)))}] + :subroutines [{:label "Place 1 advancement token on an ICE that can be advanced protecting this server" + :msg (msg "place 1 advancement token on " (card-str state target)) + :choices {:req #(and (ice? %) + (can-be-advanced? %))} + :effect (effect (add-prop target :advance-counter 1 {:placed true}))}]} + + "Bullfrog" + {:subroutines [(do-psi {:label "Move Bullfrog to another server" + :player :corp + :prompt "Choose a server" + :choices (req servers) + :msg (msg "move it to the outermost position of " target) + :effect (req (let [dest (server->zone state target)] + (swap! state update-in [:run] + #(assoc % + :position (count (get-in corp (conj dest :ices))) + :server (rest dest)))) + (move state side card + (conj (server->zone state target) :ices)) + (effect-completed state side eid))})]} + + "Bulwark" + {:effect take-bad-pub + :abilities [{:msg "gain 2 [Credits] if there is an installed AI" + :req (req (some #(has-subtype? % "AI") (all-active-installed state :runner))) + :effect (effect (gain-credits 2))}] + :subroutines [(assoc trash-program + :player :runner + :msg "force the Runner to trash 1 program" + :label "The Runner trashes 1 program") + {:msg "gain 2 [Credits] and end the run" + :effect (effect (gain-credits 2) + (end-run eid card))}]} + + "Burke Bugs" + {:subroutines [(trace-ability 0 (assoc trash-program + :not-distinct true + :player :runner + :msg "force the Runner to trash a program" + :label "Force the Runner to trash a program"))]} + + "Caduceus" + {:subroutines [(trace-ability 3 (gain-credits-sub 3)) + (trace-ability 2 end-the-run)]} + + "Cell Portal" + {:subroutines [{:msg "make the Runner approach the outermost ICE" + :effect (req (let [srv (first (:server run)) + n (count (get-in @state [:corp :servers srv :ices]))] + (swap! state assoc-in [:run :position] n) + (derez state side card)))}]} + + "Changeling" + (morph-ice "Barrier" "Sentry" end-the-run) + + "Checkpoint" + {:effect take-bad-pub + :subroutines [(trace-ability 5 {:label "Do 3 meat damage when this run is successful" + :msg "do 3 meat damage when this run is successful" + :effect (effect (register-events + {:successful-run + {:async true + :msg "do 3 meat damage" + :effect (effect (damage eid :meat 3 {:card card}))} + :run-ends {:effect (effect (unregister-events card))}} + card))})] + :events {:successful-run nil :run-ends nil}} + + "Chetana" + {:subroutines [{:msg "make each player gain 2 [Credits]" + :effect (effect (gain-credits :runner 2) + (gain-credits :corp 2))} + (do-psi {:label "Do 1 net damage for each card in the Runner's grip" + :msg (msg "do " (count (get-in @state [:runner :hand])) " net damage") + :effect (effect (damage eid :net (count (get-in @state [:runner :hand])) {:card card}))})]} + + "Chimera" + (let [turn-end-ability {:effect (effect (derez :corp card) + (update! (assoc (get-card state card) :subtype "Mythic")))}] + {:prompt "Choose one subtype" + :choices ["Barrier" "Code Gate" "Sentry"] + :msg (msg "make it gain " target " until the end of the turn") + :effect (effect (update! (assoc card + :subtype-target target + :subtype (combine-subtypes true (:subtype card) target))) + (update-ice-strength card)) + :events {:runner-turn-ends turn-end-ability + :corp-turn-ends turn-end-ability} + :subroutines [end-the-run]}) + + "Chiyashi" + {:implementation "Trash effect when using an AI to break is activated manually" + :abilities [{:label "Trash the top 2 cards of the Runner's Stack" + :req (req (some #(has-subtype? % "AI") (all-active-installed state :runner))) + :msg (msg (str "trash " (join ", " (map :title (take 2 (:deck runner)))) " from the Runner's Stack")) + :effect (effect (mill :corp :runner 2))}] + :subroutines [(do-net-damage 2) + end-the-run]} + + "Chrysalis" + {:flags {:rd-reveal (req true)} + :subroutines [(do-net-damage 2)] + :access {:async true + :req (req (not= (first (:zone card)) :discard)) + :effect (effect (show-wait-prompt :corp "Runner to decide to break Chrysalis subroutine") + (continue-ability + :runner {:optional + {:player :runner + :prompt "You are encountering Chrysalis. Allow its subroutine to fire?" + :priority 1 + :yes-ability {:effect (effect (clear-wait-prompt :corp) + (play-subroutine eid {:card card :subroutine 0}))} + :no-ability {:effect (effect (clear-wait-prompt :corp) + (effect-completed eid))}}} + card nil))}} + + "Chum" + {:subroutines [{:label "Give +2 strength to next ICE Runner encounters" + :req (req this-server) + :prompt "Select the ICE the Runner is encountering" + :choices {:req #(and (rezzed? %) (ice? %))} + :msg (msg "give " (:title target) " +2 strength") + :effect (req (let [ice (:cid target)] + (register-events state side + {:pre-ice-strength {:req (req (= (:cid target) ice)) + :effect (effect (ice-strength-bonus 2 target))} + :run-ends {:effect (effect (unregister-events card))}} + card) + (update-all-ice state side)))} + (do-net-damage 3)] + :events {:pre-ice-strength nil :run-ends nil}} + + "Clairvoyant Monitor" + {:subroutines [(do-psi {:label "Place 1 advancement token and end the run" + :player :corp + :prompt "Select a target for Clairvoyant Monitor" + :msg (msg "place 1 advancement token on " + (card-str state target) " and end the run") + :choices {:req installed?} + :effect (effect (add-prop target :advance-counter 1 {:placed true}) + (end-run eid))})]} + + "Cobra" + {:subroutines [trash-program (do-net-damage 2)]} + + "Colossus" + {:advanceable :always + :subroutines [{:label "Give the Runner 1 tag (Give the Runner 2 tags)" + :async true + :msg (msg "give the Runner " (if (wonder-sub card 3) "2 tags" "1 tag")) + :effect (effect (gain-tags :corp eid (if (wonder-sub card 3) 2 1)))} + {:label "Trash 1 program (Trash 1 program and 1 resource)" + :async true + :msg (msg "trash 1 program" (when (wonder-sub card 3) " and 1 resource")) + :effect (req (wait-for (resolve-ability state side trash-program card nil) + (if (wonder-sub card 3) + (continue-ability + state side + {:prompt "Choose a resource to trash" + :msg (msg "trash " (:title target)) + :choices {:req #(and (installed? %) + (resource? %))} + :cancel-effect (req (effect-completed state side eid)) + :effect (effect (trash target {:cause :subroutine}))} + card nil) + (effect-completed state side eid))))}] + :strength-bonus advance-counters} + + "Congratulations!" + {:events {:pass-ice {:req (req (same-card? target card)) + :msg "gain 1 [Credits]" + :effect (effect (gain-credits :corp 1))}} + :subroutines [{:label "Gain 2 [Credits]. The Runner gains 1 [Credits]" + :msg "gain 2 [Credits]. The Runner gains 1 [Credits]" + :effect (effect (gain-credits :corp 2) + (gain-credits :runner 1))}]} + + "Conundrum" + {:subroutines [(assoc trash-program + :player :runner + :msg "force the Runner to trash 1 program" + :label "The Runner trashes 1 program") + {:msg "force the Runner to lose 1 [Click] if able" + :effect runner-loses-click} + end-the-run] + :strength-bonus (req (if (some #(has-subtype? % "AI") (all-active-installed state :runner)) 3 0))} + + "Cortex Lock" + {:subroutines [{:label "Do 1 net damage for each unused memory unit the Runner has" + :msg (msg "do " (available-mu state) " net damage") + :effect (effect (damage eid :net (available-mu state) {:card card}))}]} + + "Crick" + {:subroutines [{:label "install a card from Archives" + :prompt "Select a card to install from Archives" + :show-discard true + :priority true + :choices {:req #(and (not (operation? %)) + (in-discard? %) + (corp? %))} + :msg (msg (corp-install-msg target)) + :effect (effect (corp-install target nil))}] + :strength-bonus (req (if (= (second (:zone card)) :archives) 3 0))} + + "Curtain Wall" + {:subroutines [end-the-run] + :strength-bonus (req (let [ices (:ices (card->server state card))] + (if (same-card? card (last ices)) 4 0))) + :events (let [cw {:req (req (and (not (same-card? card target)) + (= (card->server state card) (card->server state target)))) + :effect (effect (update-ice-strength card))}] + {:corp-install cw + :trash cw + :card-moved cw})} + + "Data Hound" + (letfn [(dh-trash [cards] + {:prompt "Choose a card to trash" + :choices cards + :async true + :msg (msg "trash " (:title target)) + :effect (req (trash state side target {:unpreventable true}) + (continue-ability + state side + (reorder-choice + :runner :runner (remove-once #(= % target) cards) + '() (count (remove-once #(= % target) cards)) + (remove-once #(= % target) cards)) + card nil))})] + {:subroutines [(trace-ability + 2 + {:async true + :label "Look at the top of Stack" + :msg "look at top X cards of Stack" + :effect (req (show-wait-prompt state :runner "Corp to rearrange the top cards of the Runner's Stack") + (let [c (- target (second targets)) + from (take c (:deck runner))] + (system-msg state :corp + (str "looks at the top " c " cards of Stack")) + (if (< 1 c) + (continue-ability state side (dh-trash from) card nil) + (do (system-msg state :corp (str "trashes " (:title (first from)))) + (trash state side (first from) {:unpreventable true}) + (clear-wait-prompt state :runner) + (effect-completed state side eid)))))})]}) + + "Data Loop" + (let [ability {:label "Add 2 cards from your Grip to the top of the Stack" + :req (req (pos? (count (:hand runner)))) + :effect (req (let [n (min 2 (count (:hand runner)))] + (resolve-ability state side + {:prompt (msg "Choose " n " cards in your Grip to add to the top of the Stack (first card targeted will be topmost)") + :choices {:max n :all true + :req #(and (in-hand? %) (runner? %))} + :effect (req (doseq [c targets] + (move state :runner c :deck {:front true})) + (system-msg state :runner (str "adds " n " cards from their Grip to the top of the Stack")))} + card nil)))}] + {:implementation "Encounter effect is manual" + :subroutines [end-the-run-if-tagged + end-the-run] + :abilities [ability] + :runner-abilities [ability]}) + + "Data Mine" + {:subroutines [{:msg "do 1 net damage" + :effect (req (damage state :runner eid :net 1 {:card card}) + (when current-ice + (no-action state side nil) + (continue state side nil)) + (trash state side card))}]} + + "Data Raven" + {:implementation "Encounter effect is manual" + :abilities [(give-tags 1) + (power-counter-ability (give-tags 1))] + :runner-abilities [{:label "End the run" + :effect (req (end-run state :runner) + (system-msg state :runner "chooses to end the run on encountering Data Raven"))} + {:label "Take 1 tag" + :async true + :effect (req (system-msg state :runner "chooses to take 1 tag on encountering Data Raven") + (gain-tags state :runner eid 1))}] + :subroutines [(trace-ability 3 add-power-counter)]} + + "Data Ward" + {:runner-abilities [{:label "Pay 3 [Credits]" + :effect (req (pay state :runner card :credit 3) + (system-msg state :runner "chooses to pay 3 [Credits] on encountering Data Ward"))} + {:label "Take 1 tag" + :async true + :effect (req (system-msg state :runner "chooses to take 1 tag on encountering Data Ward") + (gain-tags state :runner eid 1))}] + :subroutines [end-the-run-if-tagged]} + + "Datapike" + {:subroutines [{:msg "force the Runner to pay 2 [Credits] if able" + :effect (effect (pay :runner card :credit 2))} + end-the-run]} + + "DNA Tracker" + {:subroutines [{:msg "do 1 net damage and make the Runner lose 2 [Credits]" + :effect (req (wait-for (damage state side :net 1 {:card card}) + (lose-credits state :runner 2)))}]} + + "Dracō" + {:prompt "How many power counters?" + :choices :credit + :msg (msg "add " target " power counters") + :effect (effect (add-counter card :power target) + (update-ice-strength card)) + :strength-bonus (req (get-counters card :power)) + :subroutines [(trace-ability 2 {:label "Give the Runner 1 tag and end the run" + :msg "give the Runner 1 tag and end the run" :async true - :choices {:req #(and (installed? %) (ice? %)) :max 2 :all true} - :msg (msg "swap the positions of " (card-str state (first targets)) " and " (card-str state (second targets))) - :effect (req (when (= (count targets) 2) - (swap-ice state side (first targets) (second targets)) - (effect-completed state side eid)))} - card nil) - (continue-ability - state side - {:prompt "Select the two cards to swap" + :effect (effect (gain-tags :corp eid 1) + (end-run))})]} + + "Eli 1.0" + {:subroutines [end-the-run] + :runner-abilities [(runner-break [:click 1] 1)]} + + "Eli 2.0" + {:subroutines [{:msg "draw 1 card" :effect (effect (draw))} + end-the-run] + :runner-abilities [(runner-break [:click 2] 2)]} + + "Endless EULA" + {:subroutines [end-the-run] + :runner-abilities [(runner-pay [:credit 1] 1) + (runner-pay [:credit 6] 6)]} + + "Enforcer 1.0" + {:additional-cost [:forfeit] + :subroutines [trash-program + (do-brain-damage 1) + {:label "Trash a console" + :prompt "Select a console to trash" + :choices {:req #(has-subtype? % "Console")} + :msg (msg "trash " (:title target)) + :effect (effect (trash target))} + {:msg "trash all virtual resources" + :effect (req (doseq [c (filter #(has-subtype? % "Virtual") (all-active-installed state :runner))] + (trash state side c)))}] + :runner-abilities [(runner-break [:click 1] 1)]} + + "Enigma" + {:subroutines [{:msg "force the Runner to lose 1 [Click] if able" + :effect runner-loses-click} + end-the-run]} + + "Envelope" + {:subroutines [(do-net-damage 1) + end-the-run]} + + "Errand Boy" + {:subroutines [(gain-credits-sub 1) + {:msg "draw 1 card" :effect (effect (draw))}]} + + "Excalibur" + {:subroutines [{:label "The Runner cannot make another run this turn" + :msg "prevent the Runner from making another run" + :effect (effect (register-turn-flag! card :can-run nil))}]} + + "Executive Functioning" + {:subroutines [(trace-ability 4 (do-brain-damage 1))]} + + "Fairchild" + {:subroutines [end-the-run + (do-brain-damage 1)] + :runner-abilities [(runner-break [:credit 4] 1)]} + + "Fairchild 1.0" + {:subroutines [{:label "Force the Runner to pay 1 [Credits] or trash an installed card" + :msg "force the Runner to pay 1 [Credits] or trash an installed card" + :player :runner + :prompt "Choose one" + :choices ["Pay 1 [Credits]" "Trash an installed card"] + :effect (req (if (= target "Pay 1 [Credits]") + (do (pay state side card :credit 1) + (system-msg state side "pays 1 [Credits]")) + (resolve-ability state :runner trash-installed card nil)))}] + :runner-abilities [(runner-break [:click 1] 1)]} + + "Fairchild 2.0" + {:subroutines [{:label "Force the Runner to pay 2 [Credits] or trash an installed card" + :msg "force the Runner to pay 2 [Credits] or trash an installed card" + :player :runner + :prompt "Choose one" + :choices ["Pay 2 [Credits]" "Trash an installed card"] + :effect (req (if (= target "Pay 2 [Credits]") + (do (pay state side card :credit 2) + (system-msg state side "pays 2 [Credits]")) + (resolve-ability state :runner trash-installed card nil)))} + (do-brain-damage 1)] + :runner-abilities [(runner-break [:click 2] 2)]} + + "Fairchild 3.0" + {:subroutines [{:label "Force the Runner to pay 3 [Credits] or trash an installed card" + :msg "force the Runner to pay 3 [Credits] or trash an installed card" + :player :runner + :prompt "Choose one" + :choices ["Pay 3 [Credits]" "Trash an installed card"] + :effect (req (if (= target "Pay 3 [Credits]") + (do (pay state side card :credit 3) + (system-msg state side "pays 3 [Credits]")) + (resolve-ability state :runner trash-installed card nil)))} + {:label "Do 1 brain damage or end the run" + :prompt "Choose one" + :choices ["Do 1 brain damage" "End the run"] + :msg (msg (lower-case target)) + :effect (req (if (= target "Do 1 brain damage") + (damage state side eid :brain 1 {:card card}) + (end-run state side)))}] + :runner-abilities [(runner-break [:click 3] 3)]} + + "Fenris" + {:effect take-bad-pub + :subroutines [(do-brain-damage 1) + end-the-run]} + + "Fire Wall" + {:advanceable :always + :subroutines [end-the-run] + :strength-bonus advance-counters} + + "Flare" + {:subroutines [(trace-ability 6 {:label "Trash 1 hardware, do 2 meat damage, and end the run" + :msg "trash 1 hardware, do 2 meat damage, and end the run" :async true - :choices {:req #(and (installed? %) (not (ice? %))) :max 2 :all true} - :msg (msg "swap the positions of " (card-str state (first targets)) " and " (card-str state (second targets))) - :effect (req (when (= (count targets) 2) - (swap-installed state side (first targets) (second targets)) - (effect-completed state side eid)))} - card nil)))}]}) - -(define-card "Mganga" - {:subroutines [(do-psi {:label "do 2 net damage" - :player :corp - :effect (req (wait-for (damage state :corp :net 2 {:card card}) - (trash state :corp eid card nil)))} - {:label "do 1 net damage" - :player :corp - :effect (req (wait-for (damage state :corp :net 1 {:card card}) - (trash state :corp eid card nil)))})]}) - -(define-card "Mind Game" - {:subroutines [(do-psi {:label "Redirect the run to another server" - :player :corp - :prompt "Choose a server" - :choices (req (remove #{(-> @state :run :server central->name)} servers)) - :msg (msg "redirect the run to " target) - :effect (req (let [dest (server->zone state target)] - (swap! state update-in [:run] - #(assoc % :position (count (get-in corp (conj dest :ices))) - :server (rest dest)))) - (effect-completed state side eid))})] - :runner-abilities [{:label "Add an installed card to the bottom of your Stack" - :prompt "Choose one of your installed cards" - :choices {:req #(and (installed? %) - (runner? %))} - :effect (effect (move target :deck) - (system-msg :runner (str "adds " (:title target) " to the bottom of their Stack")))}]}) - -(define-card "Minelayer" - {:subroutines [{:msg "install an ICE from HQ" - :choices {:req #(and (ice? %) - (in-hand? %))} - :prompt "Choose an ICE to install from HQ" - :effect (req (corp-install state side target (zone->name (first (:server run))) {:ignore-all-cost true}))}]}) - -(define-card "Mirāju" - {:abilities [{:label "Runner broke subroutine: Redirect run to Archives" - :msg "make the Runner continue the run on Archives. Mirāju is derezzed" - :effect (req (swap! state update-in [:run] - #(assoc % :position (count (get-in corp [:servers :archives :ices])) - :server [:archives])) - (derez state side card))}] - :subroutines [{:label "Draw 1 card, then shuffle 1 card from HQ into R&D" - :effect (req (wait-for (resolve-ability - state side - {:optional - {:prompt "Draw 1 card?" - :yes-ability {:async true - :msg "draw 1 card" - :effect (effect (draw eid 1 nil))}}} - card nil) - (resolve-ability - state side - {:prompt "Choose 1 card in HQ to shuffle into R&D" - :choices {:req #(and (in-hand? %) (corp? %))} - :msg "shuffle 1 card in HQ into R&D" - :effect (effect (move target :deck) - (shuffle! :deck))} - card nil)))}]}) - -(define-card "Mlinzi" - (letfn [(net-or-trash [net-dmg mill-cnt] - {:label (str "Do " net-dmg " net damage") - :effect (req (show-wait-prompt state :corp "Runner to choose an option for Mlinzi") - (resolve-ability - state :runner - {:prompt "Take net damage or trash cards from the stack?" - :choices [(str "Take " net-dmg " net damage") - (str "Trash the top " mill-cnt " cards of the stack")] - :effect (req (if (.startsWith target "Take") - (do (system-msg state :corp - (str "uses Mlinzi to do " - net-dmg " net damage")) - (clear-wait-prompt state :corp) - (damage state :runner eid :net net-dmg {:card card})) - (do (system-msg state :corp - (str "uses Mlinzi to trash " - (join ", " (map :title (take mill-cnt (:deck runner)))) - " from the runner's stack")) - (clear-wait-prompt state :corp) - (mill state :runner mill-cnt))))} - card nil))})] - {:subroutines [(net-or-trash 1 2) - (net-or-trash 2 3) - (net-or-trash 3 4)]})) - -(define-card "Mother Goddess" - (let [ab (effect (update! (let [subtype (->> (mapcat :ices (flatten (seq (:servers corp)))) - (filter #(and (rezzed? %) - (not (same-card? card %)))) - (mapcat #(split (:subtype %) #" - ")) - (cons "Mythic") - distinct - (join " - "))] - (assoc card - :subtype-target (remove-subtypes subtype "Mythic") - :subtype subtype)))) - mg {:req (req (ice? target)) - :effect ab}] - {:effect ab - :subroutines [end-the-run] - :events {:rez mg - :card-moved mg - :derez mg - :ice-subtype-changed mg}})) - -(define-card "Muckraker" - {:effect take-bad-pub - :subroutines [(tag-trace 1) - (tag-trace 2) - (tag-trace 3) - end-the-run-if-tagged]}) - -(define-card "Najja 1.0" - {:subroutines [end-the-run] - :runner-abilities [(runner-break [:click 1] 1)]}) - -(define-card "Nebula" - (space-ice trash-program)) - -(define-card "Negotiator" - {:subroutines [(gain-credits-sub 2) - trash-program] - :runner-abilities [(runner-break [:credit 2] 1)]}) - -(define-card "Nerine 2.0" - {:subroutines [{:label "Do 1 brain damage and Corp may draw 1 card" - :async true - :msg "do 1 brain damage" - :effect (req (wait-for (damage state :runner :brain 1 {:card card}) - (resolve-ability - state side - {:optional - {:prompt "Draw 1 card?" - :yes-ability {:async true - :msg "draw 1 card" - :effect (effect (draw eid 1 nil))}}} - card nil)))}] - :runner-abilities [(runner-break [:click 2] 2)]}) - -(define-card "Neural Katana" - {:subroutines [(do-net-damage 3)]}) - -(define-card "News Hound" - {:subroutines [(tag-trace 3) - {:label "End the run if a Current is active" - :req (req (or (not (empty? (runner :current))) - (not (empty? (corp :current))))) - :effect (effect (end-run)) :msg "end the run"}]}) - -(define-card "NEXT Bronze" - {:subroutines [end-the-run] - :strength-bonus (req (next-ice-count corp)) - :events (let [nb {:req (req (and (not (same-card? target card)) - (has-subtype? target "NEXT"))) - :effect (effect (update-ice-strength card))}] - {:rez nb - :derez nb - :trash nb - :card-moved nb})}) - -(define-card "NEXT Diamond" - {:rez-cost-bonus (req (- (next-ice-count corp))) - :subroutines [(do-brain-damage 1) - {:prompt "Select a card to trash" - :label "Trash 1 installed Runner card" - :msg (msg "trash " (:title target)) - :choices {:req #(and (installed? %) - (runner? %))} - :async true - :effect (req (trash state side eid target {:cause :subroutine}))}]}) - -(define-card "NEXT Gold" - {:subroutines [{:label "Do 1 net damage for each rezzed NEXT ice" - :msg (msg "do " (next-ice-count corp) " net damage") - :effect (effect (damage eid :net (next-ice-count corp) {:card card}))} - trash-program]}) - -(define-card "NEXT Opal" - {:subroutines [{:label "Install a card from HQ, paying all costs" - :prompt "Choose a card in HQ to install" - :priority true - :choices {:req #(and (not (operation? %)) - (in-hand? %) - (corp? %))} - :effect (effect (corp-install target nil)) - :msg (msg (corp-install-msg target))}]}) - -(define-card "NEXT Sapphire" - {:subroutines [{:label "Draw up to X cards" - :prompt "Draw how many cards?" - :msg (msg "draw " target " cards") - :choices {:number (req (next-ice-count corp)) - :default (req 1)} - :async true - :effect (effect (draw eid target nil))} - {:label "Add up to X cards from Archives to HQ" - :prompt "Select cards to add to HQ" - :show-discard true - :choices {:req #(and (corp? %) - (= [:discard] (:zone %))) - :max (req (next-ice-count corp))} - :effect (req (doseq [c targets] - (move state side c :hand))) - :msg (msg "add " - (let [seen (filter :seen targets) - m (count (filter #(not (:seen %)) targets))] - (str (join ", " (map :title seen)) - (when (pos? m) - (str (when-not (empty? seen) " and ") - (quantify m "unseen card"))))) - " to HQ")} - {:label "Shuffle up to X cards from HQ into R&D" - :prompt "Select cards to shuffle into R&D" - :choices {:req #(and (corp? %) - (= [:hand] (:zone %))) - :max (req (next-ice-count corp))} - :effect (req (doseq [c targets] - (move state :corp c :deck)) - (shuffle! state :corp :deck)) - :cancel-effect (effect (shuffle! :corp :deck)) - :msg (msg "shuffle " (count targets) " cards from HQ into R&D")}]}) - -(define-card "NEXT Silver" - {:abilities [{:label "Gain subroutines" - :msg (msg "gain " - (count (filter #(and (ice? %) - (has-subtype? % "NEXT")) - (all-active-installed state :corp))) - " subroutines")}] - :subroutines [end-the-run]}) - -(define-card "Nightdancer" - {:subroutines [{:label (str "The Runner loses [Click], if able. " - "You have an additional [Click] to spend during your next turn.") - :msg (str "force the runner to lose a [Click], if able. " - "Corp gains an additional [Click] to spend during their next turn") - :effect (req (lose state :runner :click 1) - (swap! state update-in [:corp :extra-click-temp] (fnil inc 0)))}]}) - -(define-card "Oduduwa" - {:implementation "Encounter effect is manual" - :abilities [{:label "Place 1 advancement counter on Oduduwa" - :msg (msg "place 1 advancement counter on Oduduwa") - :effect (req (add-prop state side card :advance-counter 1 {:placed true}))} - {:label "Place X advancement token on another piece of ice" - :msg (msg "place " (get-counters card :advancement) " advancement token on " (card-str state target)) - :choices {:req ice? - :not-self true} - :effect (req (add-prop state side target :advance-counter (get-counters card :advancement) {:placed true}))}] - :subroutines [end-the-run]}) - -(define-card "Orion" - (implementation-note "\"Resolve a subroutine...\" subroutine is not implemented" - (space-ice trash-program end-the-run))) - -(define-card "Otoroshi" - {:subroutines [{:async true - :label "Place 3 advancement tokens on installed card" - :msg "place 3 advancement tokens on installed card" - :prompt "Choose an installed Corp card" - :choices {:req #(and (corp? %) - (installed? %))} - :effect (req (let [c target - title (if (:rezzed c) - (:title c) - "selected unrezzed card")] - (add-counter state side c :advancement 3) - (show-wait-prompt state side "Runner to resolve Otoroshi") - (continue-ability - state side - {:player :runner + :effect (effect (continue-ability + {:prompt "Select a piece of hardware to trash" + :label "Trash a piece of hardware" + :choices {:req hardware?} + :msg (msg "trash " (:title target)) + :effect (req (wait-for + (trash state side target {:cause :subroutine}) + (do (damage state side eid :meat 2 {:unpreventable true + :card card}) + (end-run state side)))) + :cancel-effect (effect (damage eid :meat 2 {:unpreventable true :card card}) + (end-run))} + card nil))})]} + + "Formicary" + {:optional {:prompt "Move Formicary?" + :req (req (and (:run @state) + (zero? (:position run)) + (not (contains? run :corp-phase-43)) + (not (contains? run :successful)))) + :yes-ability {:msg "rez and move Formicary. The Runner is now approaching Formicary." + :effect (req (move state side card + [:servers (first (:server run)) :ices] + {:front true}) + (swap! state assoc-in [:run :position] 1))} + :no-ability {:msg "rez Formicary without moving it"}} + :subroutines [{:label "End the run unless the Runner suffers 2 net damage" + :async true + :effect (req (wait-for (resolve-ability + state :runner + {:optional + {:prompt "Suffer 2 net damage? (If not, end the run)" + :yes-ability {:async true + :msg "let the Runner suffer 2 net damage" + :effect (effect (damage eid :net 2 {:card card :unpreventable true}))} + :no-ability end-the-run}} + card nil)))}]} + + "Free Lunch" + {:abilities [(power-counter-ability {:label "Runner loses 1 [Credits]" + :msg "make the Runner lose 1 [Credits]" + :effect (effect (lose-credits :runner 1))})] + :subroutines [add-power-counter]} + + "Galahad" + (grail-ice end-the-run) + + "Gatekeeper" + (let [draw {:async true + :prompt "Draw how many cards?" + :choices {:number (req 3) + :max (req 3) + :default (req 1)} + :msg (msg "draw " target "cards") + :effect (effect (draw eid target nil))} + reveal-and-shuffle {:prompt "Reveal and shuffle up to 3 agendas" + :show-discard true + :choices {:req #(and (corp? %) + (or (= [:discard] (:zone %)) + (= [:hand] (:zone %))) + (agenda? %)) + :max (req 3)} + :effect (req (reveal state side targets) + (doseq [c targets] + (move state :corp c :deck)) + (shuffle! state :corp :deck)) + :cancel-effect (effect (shuffle! :deck)) + :msg (msg "add " + (str (join ", " (map :title targets))) + " to R&D")} + draw-reveal-shuffle {:async true + :label "Draw cards, reveal and shuffle agendas" + :effect (req (wait-for (resolve-ability state side draw card nil) + (continue-ability state side reveal-and-shuffle card nil)))}] + {:strength-bonus (req (if (= :this-turn (:rezzed card)) 6 0)) + :subroutines [draw-reveal-shuffle + end-the-run]}) + + "Gemini" + (constellation-ice (do-net-damage 1)) + + "Grim" + {:effect take-bad-pub + :subroutines [trash-program]} + + "Guard" + {:implementation "Prevent bypass is manual" + :subroutines [end-the-run]} + + "Gutenberg" + {:subroutines [(tag-trace 7)] + :strength-bonus (req (if (= (second (:zone card)) :rd) 3 0))} + + "Gyri Labyrinth" + {:implementation "Hand size is not restored if trashed or derezzed after firing" + :subroutines [{:req (req (:run @state)) + :label "Reduce Runner's maximum hand size by 2 until start of next Corp turn" + :msg "reduce the Runner's maximum hand size by 2 until the start of the next Corp turn" + :effect (effect (lose :runner :hand-size 2) + (register-events {:corp-turn-begins + {:msg "increase the Runner's maximum hand size by 2" + :effect (effect (gain :runner :hand-size 2) + (unregister-events card))}} card))}] + :events {:corp-turn-begins nil}} + + "Hadrian's Wall" + {:advanceable :always + :subroutines [end-the-run] + :strength-bonus advance-counters} + + "Hagen" + {:subroutines [{:label "Trash 1 program" + :prompt "Choose a program that is not a decoder, fracter or killer" + :msg (msg "trash " (:title target)) + :choices {:req #(and (installed? %) + (program? %) + (not (has-subtype? % "Decoder")) + (not (has-subtype? % "Fracter")) + (not (has-subtype? % "Killer")))} + :effect (effect (trash target {:cause :subroutine}) + (clear-wait-prompt :runner))} + end-the-run] + :strength-bonus (req (- (count (filter #(has-subtype? % "Icebreaker") + (all-active-installed state :runner)))))} + + "Hailstorm" + {:subroutines [{:label "Remove a card in the Heap from the game" + :prompt "Choose a card in the Runner's Heap" + :choices (req (:discard runner)) + :msg (msg "remove " (:title target) " from the game") + :effect (effect (move :runner target :rfg))} + end-the-run]} + + "Harvester" + {:subroutines [{:label "Runner draws 3 cards and discards down to maximum hand size" + :msg "make the Runner draw 3 cards and discard down to their maximum hand size" + :async true + :effect (req (wait-for (draw state :runner 3 nil) + (let [delta (- (count (get-in @state [:runner :hand])) (hand-size state :runner))] + (if (pos? delta) + (continue-ability + state :runner + {:prompt (msg "Select " delta " cards to discard") + :player :runner + :choices {:max delta + :req #(in-hand? %)} + :effect (req (doseq [c targets] + (trash state :runner c)) + (system-msg state :runner + (str "trashes " (join ", " (map :title targets)))))} + card nil) + (effect-completed state side eid)))))}]} + + "Heimdall 1.0" + {:subroutines [(do-brain-damage 1) + end-the-run] + :runner-abilities [(runner-break [:click 1] 1)]} + + "Heimdall 2.0" + {:subroutines [(do-brain-damage 1) + {:msg "do 1 brain damage and end the run" :effect (effect (damage eid :brain 1 {:card card}) (end-run))} + end-the-run] + :runner-abilities [(runner-break [:click 2] 2)]} + + "Herald" + {:flags {:rd-reveal (req true)} + :subroutines [(gain-credits-sub 2) + {:async true + :label "Pay up to 2 [Credits] to place up to 2 advancement tokens" + :prompt "How many advancement tokens?" + :choices (req (map str (range (inc (min 2 (:credit corp)))))) + :effect (req (let [c (str->int target)] + (if (can-pay? state side eid card (:title card) :credit c) + (do (pay state :corp card :credit c) + (continue-ability + state side + {:msg (msg "pay " c "[Credits] and place " (quantify c " advancement token") + " on " (card-str state target)) + :choices {:req can-be-advanced?} + :effect (effect (add-prop target :advance-counter c {:placed true}))} + card nil)) + (effect-completed state side eid))))}] + :access {:async true + :req (req (not= (first (:zone card)) :discard)) + :effect (effect (show-wait-prompt :corp "Runner to decide to break Herald subroutines") + (continue-ability + :runner {:optional + {:player :runner + :prompt "You are encountering Herald. Allow its subroutines to fire?" + :priority 1 + :yes-ability {:effect (effect (clear-wait-prompt :corp) + (play-subroutine :corp eid {:card card :subroutine 0}) + (play-subroutine :corp eid {:card card :subroutine 1}))} + :no-ability {:effect (effect (clear-wait-prompt :corp) + (effect-completed eid))}}} + card nil))}} + + "Himitsu-Bako" + {:abilities [{:msg "add it to HQ" + :cost [:credit 1] + :effect (effect (move card :hand))}] + :subroutines [end-the-run]} + + "Hive" + {:abilities [{:label "Gain subroutines" + :msg (msg "gain " (min 5 (max 0 (- 5 (:agenda-point corp 0)))) " subroutines")}] + :subroutines [end-the-run]} + + "Holmegaard" + {:subroutines [(trace-ability 4 {:label "Runner cannot access any cards this run" + :msg "stop the Runner from accessing any cards this run" + :effect (effect (prevent-access))}) + {:label "Trash an icebreaker" + :prompt "Choose an icebreaker to trash" + :msg (msg "trash " (:title target)) + :choices {:req #(and (installed? %) + (has? % :subtype "Icebreaker"))} + :effect (effect (trash target {:cause :subroutine}) + (clear-wait-prompt :runner))}]} + + "Hortum" + (letfn [(hort [n] {:prompt "Choose a card to add to HQ with Hortum" + :async true + :choices (req (cancellable (:deck corp) :sorted)) + :msg "add 1 card to HQ from R&D" + :cancel-effect (req (shuffle! state side :deck) + (system-msg state side (str "shuffles R&D")) + (effect-completed state side eid)) + :effect (req (move state side target :hand) + (if (< n 2) + (continue-ability state side (hort (inc n)) card nil) + (do (shuffle! state side :deck) + (system-msg state side (str "shuffles R&D")) + (effect-completed state side eid))))})] + {:advanceable :always + :subroutines [{:label "Gain 1 [Credits] (Gain 4 [Credits])" + :msg (msg "gain " (if (wonder-sub card 3) "4" "1") " [Credits]") + :effect (effect (gain-credits :corp (if (wonder-sub card 3) 4 1)))} + {:label "End the run (Search R&D for up to 2 cards and add them to HQ, shuffle R&D, end the run)" + :async true + :effect (req (if (wonder-sub card 3) + (wait-for + (resolve-ability state side (hort 1) card nil) + (do (end-run state side) + (system-msg state side + (str "uses Hortum to add 2 cards to HQ from R&D, " + "shuffle R&D, and end the run")))) + (do (end-run state side) + (system-msg state side (str "uses Hortum to end the run")) + (effect-completed state side eid))))}]}) + + "Hourglass" + {:subroutines [{:msg "force the Runner to lose 1 [Click] if able" + :effect runner-loses-click}]} + + "Howler" + (let [ice-index (fn [state i] (first (keep-indexed #(when (same-card? %2 i) %1) + (get-in @state (cons :corp (:zone i))))))] + {:subroutines + [{:label "Install a piece of Bioroid ICE from HQ or Archives" + :prompt "Install ICE from HQ or Archives?" + :choices ["HQ" "Archives"] + :effect (req (let [fr target] + (resolve-ability state side + {:prompt "Choose a Bioroid ICE to install" + :choices (req (filter #(and (ice? %) + (has-subtype? % "Bioroid")) + ((if (= fr "HQ") :hand :discard) corp))) + :effect (req (let [newice (assoc target :zone (:zone card) :rezzed true) + hndx (ice-index state card) + ices (get-in @state (cons :corp (:zone card))) + newices (apply conj (subvec ices 0 hndx) newice (subvec ices hndx))] + (swap! state assoc-in (cons :corp (:zone card)) newices) + (swap! state update-in (cons :corp (:zone target)) + (fn [coll] (remove-once #(same-card? % target) coll))) + (update! state side (assoc card :howler-target newice)) + (card-init state side newice {:resolve-effect false + :init-data true}) + (trigger-event state side :corp-install newice)))} card nil)))}] + :events {:run-ends {:req (req (:howler-target card)) + :effect (effect (trash card {:cause :self-trash}) + (derez (get-card state (:howler-target card))))}}}) + + "Hudson 1.0" + {:subroutines [{:msg "prevent the Runner from accessing more than 1 card during this run" + :effect (effect (max-access 1))}] + :runner-abilities [(runner-break [:click 1] 1)]} + + "Hunter" + {:subroutines [(tag-trace 3)]} + + "Hydra" + (letfn [(otherwise-tag [message ability] + {:msg (msg (if tagged message "give the Runner 1 tag")) + :label (str (capitalize message) " if the Runner is tagged; otherwise, give the Runner 1 tag") + :async true + :effect (req (if tagged + (ability state :runner eid card nil) + (gain-tags state :runner eid 1)))})] + {:subroutines [(otherwise-tag + "do 3 net damage" + (req (damage state :runner :net 3 {:card card}))) + (otherwise-tag + "gain 5 [Credits]" + (req (gain-credits state :corp 5) + (effect-completed state side eid))) + (otherwise-tag + "end the run" + (req (end-run state side eid)))]}) + + "Ice Wall" + {:advanceable :always + :subroutines [end-the-run] + :strength-bonus advance-counters} + + "Ichi 1.0" + {:subroutines [trash-program + (trace-ability 1 {:label "Give the Runner 1 tag and do 1 brain damage" + :msg "give the Runner 1 tag and do 1 brain damage" + :async true + :effect (req (wait-for (damage state :runner :brain 1 {:card card}) + (gain-tags state :corp eid 1)))})] + :runner-abilities [(runner-break [:click 1] 1)]} + + "Ichi 2.0" + {:subroutines [trash-program + (trace-ability 3 {:label "Give the Runner 1 tag and do 1 brain damage" + :msg "give the Runner 1 tag and do 1 brain damage" :async true - :prompt (str "Access " title " or pay 3 [Credits]?") - :choices (concat ["Access card"] - (when (>= (:credit runner) 3) - ["Pay 3 [Credits]"])) - :msg (msg "force the Runner to " - (if (= target "Access card") - (str "access " title) - "pay 3 [Credits]")) - :effect (req (clear-wait-prompt state :corp) - (if (= target "Access card") - (access-card state :runner eid c) - (pay-sync state :runner eid card :credit 3)))} - card nil)))}]}) - -(define-card "Owl" - {:subroutines [{:choices {:req #(and (installed? %) - (program? %))} - :label "Add installed program to the top of the Runner's Stack" - :msg "add an installed program to the top of the Runner's Stack" - :effect (effect (move :runner target :deck {:front true}) - (system-msg (str "adds " (:title target) " to the top of the Runner's Stack")))}]}) - -(define-card "Pachinko" - {:subroutines [end-the-run-if-tagged]}) - -(define-card "Paper Wall" - {:implementation "Trash on break is manual" - :subroutines [end-the-run]}) - -(define-card "Peeping Tom" - {:implementation "Encounter effect is manual" - :abilities [{:req (req (= current-ice card)) - :label "Name a card type and reveal all cards in the Runner's Grip" - :prompt "Choose a card type" - :choices ["Event" "Hardware" "Program" "Resource"] - :effect (req (let [n (count (filter #(is-type? % target) (:hand runner)))] - (system-msg state side (str "uses Peeping Tom to name " target ", then reveals " - (join ", " (map :title (:hand runner))) - " in the Runner's Grip. Peeping Tom gains " n " subroutines")) - (reveal state side (:hand runner))))}] - :runner-abilities [{:label "End the run" - :effect (req (end-run state :runner) - (system-msg state :runner "chooses to end the run"))} - {:label "Take 1 tag" - :async true - :effect (req (system-msg state :runner "chooses to take 1 tag from Peeping Tom") - (gain-tags state :runner eid 1))}]}) - -(define-card "Pop-up Window" - {:implementation "Encounter effect is manual. Runner choice is not implemented" - :abilities [(gain-credits-sub 1)] - :subroutines [end-the-run] - :runner-abilities [(runner-pay [:credit 1] 1)]}) - -(define-card "Pup" - {:subroutines [(do-net-damage 1)] - :runner-abilities [(runner-pay [:credit 1] 1)]}) - -(define-card "Quandary" - {:subroutines [end-the-run]}) - -(define-card "Quicksand" - {:implementation "Encounter effect is manual" - :abilities [{:req (req (and this-server (= (dec (:position run)) (ice-index state card)))) - :label "Add 1 power counter" - :effect (effect (add-counter card :power 1) - (update-all-ice))}] - :subroutines [end-the-run] - :strength-bonus (req (get-counters card :power))}) - -(define-card "Rainbow" - {:subroutines [end-the-run]}) - -(define-card "Ravana 1.0" - {:subroutines [{:label "Resolve a subroutine on another piece of rezzed bioroid ICE" - :choices {:req #(and (rezzed? %) (ice? %) (has-subtype? % "Bioroid"))} - :msg (msg "resolve a subroutine on " (:title target))}] - :runner-abilities [(runner-break [:click 1] 1)]}) - -(define-card "Red Tape" - {:subroutines [{:label "Give +3 strength to all ICE for the remainder of the run" - :msg "give +3 strength to all ICE for the remainder of the run" - :effect (effect (register-events - {:pre-ice-strength {:effect (effect (ice-strength-bonus 3 target))} - :run-ends {:effect (effect (unregister-events card))}} - card) - (update-all-ice))}] - :events {:pre-ice-strength nil :run-ends nil}}) - -(define-card "Resistor" - (let [resistor-effect {:effect (effect (update! (assoc (get-card state card) :strength-bonus (count-tags state))) - (update-ice-strength (get-card state card)))}] - {:events {:runner-gain-tag resistor-effect - :runner-lose-tag resistor-effect - :runner-additional-tag-change resistor-effect} - :strength-bonus (req (count-tags state)) - :subroutines [(trace-ability 4 end-the-run)]})) - -(define-card "Rime" - {:implementation "Can be rezzed anytime already" - :effect (effect (update-all-ice)) - :subroutines [{:label "Runner loses 1 [Credit]" - :msg "force the Runner to lose 1 [Credit]" - :effect (effect (lose-credits :runner 1))}] - :events {:corp-moved {:req (req (ice? target)) - :effect (effect (update-ice-strength target))} - :corp-install {:req (req (ice? target)) - :effect (effect (update-ice-strength target))} - :pre-ice-strength {:req (req (and (ice? target) - (protecting-same-server? card target))) - :effect (effect (ice-strength-bonus 1 target))}}}) - -(define-card "Rototurret" - {:subroutines [trash-program - end-the-run]}) - -(define-card "Sadaka" - (let [maybe-draw-effect - {:async true - :effect (req (show-wait-prompt state :runner "Corp to decide on Sadaka card draw action") - (continue-ability - state side - {:optional - {:player :corp - :prompt "Draw 1 card?" - :yes-ability - {:async true - :effect (effect (clear-wait-prompt :runner) - (draw eid 1 nil)) - :msg "draw 1 card"} - :no-ability {:effect (effect (clear-wait-prompt :runner) - (effect-completed eid))}}} - card nil))}] - {:subroutines [{:label "Look at the top 3 cards of R&D" - :req (req (not-empty (:deck corp))) - :async true - :effect (req (let [top-cards (take 3 (:deck corp)) - top-names (map :title top-cards)] - (show-wait-prompt state :runner "Corp to decide on Sadaka R&D card actions") + :effect (req (wait-for (damage state :runner :brain 1 {:card card}) + (gain-tags state :corp eid 1)))})] + :runner-abilities [(runner-break [:click 2] 2)]} + + "Inazuma" + {:abilities [{:msg "prevent the Runner from breaking subroutines on the next piece of ICE they encounter this run"} + {:msg "prevent the Runner from jacking out until after the next piece of ICE" + :effect (effect (register-events + {:pass-ice {:effect (req (swap! state update-in [:run] dissoc :prevent-jack-out) + (unregister-events state side card))}} card) + (prevent-jack-out))}]} + + "Information Overload" + {:implementation "Encounter effect is manual" + :abilities [{:label "Gain subroutines" + :msg (msg "gain " (count-tags state) " subroutines")} + (tag-trace 1)] + :subroutines [trash-installed]} + + "IP Block" + {:abilities [(assoc (give-tags 1) + :req (req (seq (filter #(has-subtype? % "AI") (all-active-installed state :runner)))) + :label "Give the Runner 1 tag if there is an installed AI")] + :subroutines [(tag-trace 3) + end-the-run-if-tagged]} + + "IQ" + {:effect (req (add-watch state (keyword (str "iq" (:cid card))) + (fn [k ref old new] + (let [handsize (count (get-in new [:corp :hand]))] + (when (not= (count (get-in old [:corp :hand])) handsize) + (update! ref side (assoc (get-card ref card) :strength-bonus handsize)) + (update-ice-strength ref side (get-card ref card))))))) + :subroutines [end-the-run] + :strength-bonus (req (count (:hand corp))) + :rez-cost-bonus (req (count (:hand corp))) + :leave-play (req (remove-watch state (keyword (str "iq" (:cid card)))))} + + "Ireress" + {:abilities [{:label "Gain subroutines" + :msg (msg "gain " (count-bad-pub corp) " subroutines")}] + :subroutines [{:msg "make the Runner lose 1 [Credits]" + :effect (effect (lose-credits :runner 1))}]} + + "It's a Trap!" + {:expose {:msg "do 2 net damage" + :async true + :effect (effect (damage eid :net 2 {:card card}))} + :subroutines [(assoc trash-installed :effect (req (trash state side target {:cause :subroutine}) + (when current-ice + (no-action state side nil) + (continue state side nil)) + (trash state side card)))]} + + "Janus 1.0" + {:subroutines [(do-brain-damage 1)] + :runner-abilities [(runner-break [:click 1] 1)]} + + "Jua" + {:implementation "Encounter effect is manual" + :abilities [{:msg "prevent the Runner from installing cards for the rest of the turn" + :effect (effect (register-turn-flag! card :runner-lock-install (constantly true)))}] + :subroutines [{:label "Choose 2 installed Runner cards, if able. The Runner must add 1 of those to the top of the Stack." + :req (req (>= (count (all-installed state :runner)) 2)) + :async true + :prompt "Select 2 installed Runner cards" + :choices {:req #(and (runner? %) + (installed? %)) + :max 2 + :all true} + :msg (msg "add either " (card-str state (first targets)) " or " (card-str state (second targets)) " to the Stack") + :effect (req (when (= (count targets) 2) + (show-wait-prompt state :corp "Runner to decide which card to move") + (continue-ability + state :runner + {:player :runner + :priority 1 + :prompt "Select a card to move to the Stack" + :choices targets ;{:req (fn [x] (some #(= % x) targets))} - Alternative version + :effect (req (let [c target] + (clear-wait-prompt state :corp) + (move state :runner c :deck {:front true}) + (system-msg state :runner (str "selected " (card-str state c) " to move to the Stack"))))} + card nil)))}]} + + "Kakugo" + {:events {:pass-ice {:async true + :req (req (same-card? target card)) + :msg "do 1 net damage" + :effect (effect (damage eid :net 1 {:card card}))}} + :subroutines [end-the-run]} + + "Kamali 1.0" + (letfn [(better-name [kind] (if (= "hardware" kind) "piece of hardware" kind)) + (runner-trash [kind] + {:prompt (str "Select an installed " (better-name kind) " to trash") + :label (str "Trash an installed " (better-name kind)) + :msg (msg "trash " (:title target)) + :async true + :choices {:req #(and (installed? %) + (is-type? % (capitalize kind)))} + :cancel-effect (effect (system-msg (str "fails to trash an installed " (better-name kind))) + (effect-completed eid)) + :effect (effect (trash eid target {:cause :subroutine}))}) + (sub-map [kind] + {:player :runner + :async true + :prompt "Choose one" + :choices ["Take 1 brain damage" (str "Trash an installed " (better-name kind))] + :effect (req (if (= target "Take 1 brain damage") + (do (system-msg state :corp "uses Kamali 1.0 to give the Runner 1 brain damage") + (damage state :runner eid :brain 1 {:card card})) + (continue-ability state :runner (runner-trash kind) card nil)))}) + (brain-trash [kind] + {:label (str "Force the Runner to take 1 brain damage or trash an installed " (better-name kind)) + :msg (str "force the Runner to take 1 brain damage or trash an installed " (better-name kind)) + :async true + :effect (req (show-wait-prompt state :corp "Runner to decide on Kamali 1.0 action") + (wait-for (resolve-ability state side (sub-map kind) card nil) + (clear-wait-prompt state :corp)))})] + {:subroutines [(brain-trash "resource") + (brain-trash "hardware") + (brain-trash "program")] + :runner-abilities [(runner-break [:click 1] 1)]}) + + "Kitsune" + {:subroutines [{:prompt "Select a card in HQ to force access" + :choices {:req in-hand?} + :label "Force the Runner to access a card in HQ" + :msg (msg "force the Runner to access " (:title target)) + :effect (req (trash state side card) + (wait-for (trigger-event-sync state side :pre-access :hq) + (wait-for (access-card state side target) + (let [from-hq (dec (access-count state side :hq-access))] + (continue-ability + state :runner + (access-helper-hq + state from-hq + ;; access-helper-hq uses a set to keep track of which cards have already + ;; been accessed. by adding HQ root's contents to this set, we make the runner + ;; unable to access those cards, as Kitsune intends. + (conj (set (get-in @state [:corp :servers :hq :content])) target)) + card nil)))))}]} + + "Komainu" + {:abilities [{:label "Gain subroutines" + :msg (msg "gain " (count (:hand runner)) " subroutines")}] + :subroutines [(do-net-damage 1)]} + + "Lab Dog" + {:subroutines [(assoc trash-hardware + :label "Force the Runner to trash an installed piece of hardware" + :player :runner + :msg (msg "force the Runner to trash " (:title target)) + :effect (req (trash state side target) + (when current-ice + (no-action state side nil) + (continue state side nil)) + (trash state side card)))]} + + "Lancelot" + (grail-ice trash-program) + + "Little Engine" + {:subroutines [end-the-run + {:msg "make the Runner gain 5 [Credits]" + :effect (effect (gain-credits :runner 5))}]} + + "Lockdown" + {:subroutines [{:label "The Runner cannot draw cards for the remainder of this turn" + :msg "prevent the Runner from drawing cards" + :effect (effect (prevent-draw))}]} + + "Loki" + {:implementation "Encounter effects not implemented" + :subroutines [{:label "End the run unless the Runner shuffles their Grip into the Stack" + :effect (req (if (zero? (count (:hand runner))) + (do (end-run state side) + (system-msg state :corp (str "uses Loki to end the run"))) + (do (show-wait-prompt state :corp "Runner to decide to shuffle their Grip into the Stack") + (resolve-ability + state :runner + {:optional + {:prompt "Reshuffle your Grip into the Stack?" + :player :runner + :yes-ability {:effect (req (doseq [c (:hand runner)] + (move state :runner c :deck)) + (shuffle! state :runner :deck) + (system-msg state :runner (str "shuffles their Grip into their Stack")) + (clear-wait-prompt state :corp))} + :no-ability {:effect (effect (end-run) + (system-msg :runner (str "doesn't shuffle their Grip into their Stack. Loki ends the run")) + (clear-wait-prompt :corp))}}} + card nil))))}]} + + "Loot Box" + (letfn [(top-3 [state] (take 3 (get-in @state [:runner :deck]))) + (top-3-names [state] (map :title (top-3 state)))] + {:subroutines [{:label "End the run unless the Runner pays 2 [Credits]" + :msg "force the Runner to pay 2 [Credits] or end the run" + :player :runner + :prompt "Choose one" + :choices ["Pay 2 [Credits]" "End the run"] + :effect (req (if (= target "Pay 2 [Credits]") + (do (pay state :runner card :credit 2) + (system-msg state side "pays 2 [Credits]")) + (end-run state side)))} + {:label "Reveal the top 3 cards of the Stack" + :effect (effect (system-msg (str "uses Loot Box to reveal the top 3 cards of the stack: " + (join ", " (top-3-names state)))) + (reveal (top-3 state)) + (show-wait-prompt :runner "Corp to choose a card to add to the Grip") (continue-ability - state side - {:prompt (str "Top 3 cards of R&D: " (clojure.string/join ", " top-names)) - :choices ["Arrange cards" "Shuffle R&D"] - :async true - :effect - (req (if (= target "Arrange cards") - (wait-for - (resolve-ability state side (reorder-choice :corp top-cards) card nil) - (do - (system-msg state :corp (str "rearranges the top " - (quantify (count top-cards) "card") - " of R&D")) - (clear-wait-prompt state :runner) - (continue-ability state side maybe-draw-effect card nil))) - (do - (shuffle! state :corp :deck) - (system-msg state :corp (str "shuffles R&D")) - (clear-wait-prompt state :runner) - (continue-ability state side maybe-draw-effect card nil))))} - card nil)))} - {:label "Trash 1 card in HQ" - :async true - :effect - (req (show-wait-prompt state :runner "Corp to select cards to trash with Sadaka") - (wait-for - (resolve-ability - state side - {:prompt "Choose a card in HQ to trash" - :choices (req (cancellable (:hand corp) :sorted)) - :async true - :cancel-effect (effect (system-msg "chooses not to trash a card from HQ") - (effect-completed eid)) - :effect (req (wait-for - (trash state :corp (make-eid state) target nil) - (do - (system-msg state :corp "trashes a card from HQ") - (wait-for - (resolve-ability state side trash-resource-sub card nil) - (effect-completed state side eid)))))} - card nil) - (do - (system-msg state :corp "trashes Sadaka") - (clear-wait-prompt state :runner) - (when current-ice - (no-action state side nil) - (continue state side nil)) - (trash state :corp eid card nil))))}]})) - -(define-card "Sagittarius" - (constellation-ice trash-program)) - -(define-card "Saisentan" - {:implementation "Encounter effect is manual" - :subroutines [{:label "Do 1 net damage" - :async true - :msg "do 1 net damage" - :effect (req (wait-for (damage state side :net 1 {:card card}) - (when-let* [choice (get-in card [:special :saisentan]) - cards (some #(when (same-card? (second %) card) (last %)) - (turn-events state :corp :damage)) - dmg (some #(when (= (:type %) choice) %) cards)] - (system-msg state :corp "uses Saisentan to deal a second net damage") - (damage state side eid :net 1 {:card card}))))}] - :abilities [{:label "Choose card type" - :req (req (and (same-card? current-ice card) - (rezzed? card))) - :effect (effect (show-wait-prompt :runner "Corp to choose Saisentan card type") + {:prompt "Choose a card to add to the Grip" + :msg (msg "add " (:title target) " to the Grip, gain " (:cost target) + " [Credits] and shuffle the Stack. Loot Box is trashed") + :choices (req (top-3 state)) + :effect (effect (move :runner target :hand) + (gain-credits :corp (:cost target)) + (shuffle! :runner :deck) + (trash card) + (clear-wait-prompt :runner))} card nil))}]}) + + "Lotus Field" + {:subroutines [end-the-run] + :flags {:cannot-lower-strength true}} + + "Lycan" + (morph-ice "Sentry" "Code Gate" trash-program) + + "Macrophage" + {:subroutines [(trace-ability 4 {:label "Purge virus counters" + :msg "purge virus counters" + :effect (effect (purge))}) + (trace-ability 3 {:label "Trash a virus" + :prompt "Choose a virus to trash" + :msg (msg "trash " (:title target)) + :choices {:req #(and (installed? %) + (has? % :subtype "Virus"))} + :effect (effect (trash target {:cause :subroutine}) + (clear-wait-prompt :runner))}) + (trace-ability 2 {:label "Remove a virus in the Heap from the game" + :prompt "Choose a virus in the Heap to remove from the game" + :choices (req (cancellable (filter #(has? % :subtype "Virus") (:discard runner)) :sorted)) + :msg (msg "remove " (:title target) " from the game") + :effect (effect (move :runner target :rfg))}) + (trace-ability 1 end-the-run)]} + + "Magnet" + (letfn [(disable-hosted [state side c] + (doseq [hc (:hosted (get-card state c))] + (unregister-events state side hc) + (update! state side (dissoc hc :abilities))))] + {:async true + :effect (req (let [magnet card] + (wait-for (resolve-ability + state side + {:req (req (some #(some program? (:hosted %)) + (remove-once #(same-card? % magnet) + (filter ice? (all-installed state corp))))) + :prompt "Select a Program to host on Magnet" + :choices {:req #(and (program? %) + (ice? (:host %)) + (not (same-card? (:host %) magnet)))} + :effect (effect (host card target))} + card nil) + (disable-hosted state side card)))) + :derez-effect {:req (req (not-empty (:hosted card))) + :effect (req (doseq [c (get-in card [:hosted])] + (card-init state side c {:resolve-effect false})))} + :events {:runner-install {:req (req (same-card? card (:host target))) + :effect (req (disable-hosted state side card) + (update-ice-strength state side card))}} + :subroutines [end-the-run]}) + + "Mamba" + {:abilities [(power-counter-ability (do-net-damage 1))] + :subroutines [(do-net-damage 1) + (do-psi {:label "Add 1 power counter" + :msg "add 1 power counter" + :effect (effect (add-counter card :power 1) + (effect-completed eid))})]} + + "Marker" + {:subroutines [{:label "Give the next ICE encountered \"End the run\" for the remainder of the run" + :msg (msg "give the next ICE encountered \"[Subroutine] End the run\" after all its other subroutines for the remainder of the run")}]} + + "Markus 1.0" + {:subroutines [trash-installed end-the-run] + :runner-abilities [(runner-break [:click 1] 1)]} + + "Masvingo" + {:implementation "Number of subs is manual" + :advanceable :always + :abilities [{:label "Gain subroutines" + :msg (msg "gain " (get-counters card :advancement) " subroutines")}] + :effect (effect (add-prop card :advance-counter 1)) + :subroutines [end-the-run]} + + "Matrix Analyzer" + {:implementation "Encounter effect is manual" + :abilities [{:label "Place 1 advancement token on a card that can be advanced" + :msg (msg "place 1 advancement token on " (card-str state target)) + :choices {:req can-be-advanced?} + :cost [:credit 1] :effect (effect (add-prop target :advance-counter 1))}] + :subroutines [(tag-trace 2)]} + + "Mausolus" + {:advanceable :always + :subroutines [{:label "Gain 1 [Credits] (Gain 3 [Credits])" + :msg (msg "gain " (if (wonder-sub card 3) 3 1) "[Credits]") + :effect (effect (gain-credits (if (wonder-sub card 3) 3 1)))} + {:label "Do 1 net damage (Do 3 net damage)" + :async true + :msg (msg "do " (if (wonder-sub card 3) 3 1) " net damage") + :effect (effect (damage eid :net (if (wonder-sub card 3) 3 1) {:card card}))} + {:label "Give the Runner 1 tag (and end the run)" + :async true + :msg (msg "give the Runner 1 tag" + (when (wonder-sub card 3) + " and end the run")) + :effect (req (gain-tags state :corp eid 1) + (when (wonder-sub card 3) + (end-run state side)))}]} + + "Meridian" + {:subroutines [{:label "Gain 4 [Credits] and end the run, unless the runner adds Meridian to their score area as an agenda worth -1 agenda points" + :async true + :effect (req (show-wait-prompt state :corp "Runner to choose an option for Meridian") (continue-ability - {:prompt "Choose a card type" - :choices ["Event" "Hardware" "Program" "Resource"] - :msg (msg "choose the card type " target) - :effect (effect (update! (assoc-in card [:special :saisentan] target)) - (clear-wait-prompt :runner))} - card nil))}]}) - -(define-card "Salvage" - {:advanceable :while-rezzed - :abilities [{:label "Gain subroutines" - :msg (msg "gain " (get-counters card :advancement) " subroutines")}] - :subroutines [(tag-trace 2)]}) - -(define-card "Sand Storm" - {:subroutines [{:req (req (:run @state)) - :label "Move Sand Storm and the run to another server" - :prompt "Choose another server and redirect the run to its outermost position" - :choices (req (cancellable servers)) - :msg (msg "move Sand Storm and the run. The Runner is now running on " target ". Sand Storm is trashed") - :effect (req (let [dest (server->zone state target)] - (swap! state update-in [:run] - #(assoc % :position (count (get-in corp (conj dest :ices))) - :server (rest dest))) - (trash state side card {:unpreventable true})))}]}) - -(define-card "Sandstone" - {:subroutines [end-the-run] - :strength-bonus (req (- (get-counters card :virus))) - :abilities [{:label "Place one virus counter" - :req (req (same-card? current-ice card)) - :msg "place 1 virus counter on Sandstone" - :effect (effect (add-counter card :virus 1) - (update-ice-strength (get-card state card)))}]}) - -(define-card "Sandman" - {:subroutines [{:label "Add an installed Runner card to the grip" - :req (req (not-empty (all-installed state :runner))) - :effect (effect (show-wait-prompt :runner "Corp to select Sandman target") - (resolve-ability {:choices {:req #(and (installed? %) - (runner? %))} - :msg (msg "to add " (:title target) " to the grip") - :effect (effect (clear-wait-prompt :runner) - (move :runner target :hand true)) - :cancel-effect (effect (clear-wait-prompt :runner))} - card nil))}]}) - -(define-card "Sapper" - {:flags {:rd-reveal (req true)} - :subroutines [trash-program] - :access {:async true - :req (req (and (not= (first (:zone card)) :discard) - (some program? (all-active-installed state :runner)))) - :effect (effect (show-wait-prompt :corp "Runner to decide to break Sapper subroutine") - (continue-ability - :runner {:optional - {:player :runner - :prompt "Allow Sapper subroutine to fire?" - :priority 1 - :yes-ability {:effect (req (clear-wait-prompt state :corp) - (show-wait-prompt state :runner "Corp to trash a program with Sapper") - (play-subroutine state :corp eid {:card card :subroutine 0}))} - :no-ability {:effect (effect (clear-wait-prompt :corp) - (effect-completed eid))}}} - card nil))}}) - -(define-card "Searchlight" - {:advanceable :always - :subroutines [{:label "Trace X - Give the Runner 1 tag" - :trace {:base advance-counters - :label "Give the Runner 1 tag" - :successful (give-tags 1)}}]}) - -(define-card "Seidr Adaptive Barrier" - (let [recalculate-strength (req (update-ice-strength state side (get-card state card))) - recalc-event {:req (req (= (:zone target) (:zone card))) - :effect recalculate-strength}] - {:effect recalculate-strength - :strength-bonus (req (count (:ices (card->server state card)))) - :subroutines [end-the-run] - :events {:card-moved recalc-event - :corp-install recalc-event}})) - -(define-card "Self-Adapting Code Wall" - {:subroutines [end-the-run] - :flags {:cannot-lower-strength true}}) - -(define-card "Sensei" - {:subroutines [{:label "Give each other ICE encountered \"End the run\" for the remainder of the run" - :msg (msg "give each other ICE encountered \"[Subroutine] End the run\" after all its other subroutines for the remainder of the run")}]}) - -(define-card "Shadow" - {:advanceable :always - :subroutines [(gain-credits-sub 2) - (tag-trace 3)] - :strength-bonus advance-counters}) - -(define-card "Sherlock 1.0" - {:subroutines [(trace-ability 4 {:choices {:req #(and (installed? %) - (program? %))} - :label "Add an installed program to the top of the Runner's Stack" - :msg (msg "add " (:title target) " to the top of the Runner's Stack") - :effect (effect (move :runner target :deck {:front true}))})] - :runner-abilities [(runner-break [:click 1] 1)]}) - -(define-card "Sherlock 2.0" - {:subroutines [(trace-ability 4 {:choices {:req #(and (installed? %) - (program? %))} - :label "Add an installed program to the bottom of the Runner's Stack" - :msg (msg "add " (:title target) " to the bottom of the Runner's Stack") - :effect (effect (move :runner target :deck))}) - (give-tags 1)] - :runner-abilities [(runner-break [:click 2] 2)]}) - -(define-card "Shinobi" - {:effect take-bad-pub - :subroutines [(trace-ability 1 (do-net-damage 1)) - (trace-ability 2 (do-net-damage 2)) - (trace-ability 3 {:label "Do 3 net damage and end the run" - :msg "do 3 net damage and end the run" - :effect (effect (damage eid :net 3 {:card card}) - (end-run))})]}) - -(define-card "Shiro" - {:subroutines [{:label "Rearrange the top 3 cards of R&D" - :msg "rearrange the top 3 cards of R&D" - :async true - :effect (req (show-wait-prompt state :runner "Corp to rearrange the top cards of R&D") - (let [from (take 3 (:deck corp))] - (if (pos? (count from)) - (continue-ability state side (reorder-choice :corp :runner from '() - (count from) from) card nil) - (do (clear-wait-prompt state :runner) - (effect-completed state side eid)))))} - {:label "Force the Runner to access the top card of R&D" - :async true - :effect (req (do-access state :runner eid [:rd] {:no-root true}))}]}) - -(define-card "Slot Machine" - (letfn [(name-builder [card] (str (:title card) " (" (:type card) ")")) - (top-3 [state] (take 3 (get-in @state [:runner :deck]))) - (top-3-names [state] (map name-builder (top-3 state))) - (top-3-types [state] (->> (top-3 state) (map :type) (into #{}) count))] - {:implementation "Encounter effect is manual" - :abilities [{:label "Roll them bones" - :req (req (same-card? current-ice card)) - :effect (effect (move :runner (first (:deck runner)) :deck) - (reveal (take 3 (:deck runner))) - (system-msg (str "uses Slot Machine to put the top card of the stack to the bottom," - " then reveal the top 3 cards in the stack: " - (join ", " (top-3-names state)))))}] - :subroutines [{:label "Runner loses 3 [Credits]" - :msg "force the Runner to lose 3 [Credits]" - :effect (effect (lose-credits :runner 3))} - {:label "Gain 3 [Credits]" - :effect (req (let [unique-types (top-3-types state)] - (when (>= 2 unique-types) - (system-msg state :corp (str "uses Slot Machine to gain 3 [Credits]")) - (gain-credits state :corp 3))))} - {:label "Place 3 advancement tokens" - :effect (req (let [unique-types (top-3-types state)] - (when (= 1 unique-types) - (continue-ability - state side - {:choices {:req installed?} - :prompt "Choose an installed card" - :msg (msg "place 3 advancement tokens on " - (card-str state target)) - :effect (effect (add-prop target :advance-counter 3 {:placed true}))} - card nil))))}]})) - -(define-card "Snoop" - {:implementation "Encounter effect is manual" - :abilities [{:req (req (= current-ice card)) - :label "Reveal all cards in the Runner's Grip" - :msg (msg "reveal the Runner's Grip ( " (join ", " (map :title (:hand runner))) " )")} - {:req (req (pos? (get-counters card :power))) - :counter-cost [:power 1] - :label "Hosted power counter: Reveal all cards in Grip and trash 1 card" - :msg (msg "look at all cards in Grip and trash " (:title target) - " using 1 power counter") - :choices (req (cancellable (:hand runner) :sorted)) - :prompt "Choose a card to trash" - :effect (effect (reveal (:hand runner)) - (trash target))}] - :subroutines [(trace-ability 3 add-power-counter)]}) - -(define-card "Snowflake" - {:subroutines [(do-psi {:label "End the run" - :msg "end the run" - :effect (effect (end-run eid))})]}) - -(define-card "Special Offer" - {:subroutines [{:label "Gain 5 [Credits] and trash Special Offer" - :effect (req (gain-credits state :corp 5) - (when current-ice - (no-action state side nil) - (continue state side nil)) - (trash state side card) - (system-msg state side (str "gains 5 [Credits] and trashes Special Offer")))}]}) - -(define-card "Spiderweb" - {:subroutines [end-the-run]}) - -(define-card "Surveyor" - (let [x (req (* 2 (count (:ices (card->server state card))))) - recalculate-strength (req (update-ice-strength state side (get-card state card))) - recalc-event {:req (req (= (:zone target) (:zone card))) - :effect recalculate-strength}] - {:effect recalculate-strength - :strength-bonus x - :subroutines [{:label "Trace X - Give the Runner 2 tags" - :trace {:base x - :label "Give the Runner 2 tags" - :successful (give-tags 2)}} - {:label "Trace X - End the run" - :trace {:base x - :label "End the run" - :successful end-the-run}}] - :events {:card-moved recalc-event - :corp-install recalc-event}})) - -(define-card "Susanoo-no-Mikoto" - {:subroutines [{:req (req (not= (:server run) [:discard])) - :msg "make the Runner continue the run on Archives" - :effect (req (swap! state update-in [:run] - #(assoc % - :position (count (get-in corp [:servers :archives :ices])) - :server [:archives])))}]}) - -(define-card "Swarm" - {:effect take-bad-pub - :advanceable :always - :abilities [{:label "Gain subroutines" - :msg (msg "gain " (get-counters card :advancement) " subroutines")}] - :subroutines [trash-program] - :runner-abilities [(runner-pay [:credit 3] 1)]}) - -(define-card "Swordsman" - {:implementation "AI restriction not implemented" - :subroutines [(do-net-damage 1) - {:prompt "Select an AI program to trash" - :msg (msg "trash " (:title target)) - :label "Trash an AI program" - :effect (effect (trash target)) - :choices {:req #(and (installed? %) - (program? %) - (has-subtype? % "AI"))}}]}) - -(define-card "SYNC BRE" - {:subroutines [(tag-trace 4) - (trace-ability 2 {:label "Runner reduces cards accessed by 1 for this run" + state :runner + {:prompt "Choose one" + :choices ["End the run" "Add Meridian to score area"] + :player :runner :async true - :msg "reduce cards accessed for this run by 1" - :effect (effect (access-bonus (-> card :zone second) -1))})]}) - -(define-card "Tapestry" - {:subroutines [{:label "force the Runner to lose 1 [Click], if able" - :msg "force the Runner to lose 1 [Click]" - :effect runner-loses-click} - {:msg "draw 1 card" - :effect (effect (draw))} - {:req (req (pos? (count (:hand corp)))) - :prompt "Choose a card in HQ to move to the top of R&D" - :choices {:req #(and (in-hand? %) (corp? %))} - :msg "add 1 card in HQ to the top of R&D" - :effect (effect (move target :deck {:front true}))}]}) - -(define-card "Taurus" - (constellation-ice trash-hardware)) - -(define-card "Thimblerig" - {:flags {:corp-phase-12 (req (>= (count (filter ice? (all-installed state :corp))) 2))} - :implementation "Does not restrict usage of swap ability to start of turn or after pass" - :abilities [{:label "Swap Thimblerig with a piece of ice" - :prompt "Choose a piece of ice to swap Thimblerig with" - :choices {:req ice? - :not-self true} - :effect (effect (swap-ice card target)) - :msg (msg "swap " (card-str state card) " with " (card-str state target))}] - :subroutines [end-the-run]}) - -(define-card "Thoth" - {:implementation "Encounter effect is manual" - :runner-abilities [{:label "Take 1 tag" - :async true - :effect (req (system-msg state :runner "takes 1 tag on encountering Thoth") - (gain-tags state :corp eid 1))}] - :subroutines [(trace-ability 4 {:label "Do 1 net damage for each Runner tag" - :async true - :msg (msg "do " (count-tags state) " net damage") - :effect (effect (damage eid :net (count-tags state) {:card card}))}) - (trace-ability 4 {:label "Runner loses 1 [Credits] for each tag" - :async true - :msg (msg "force the Runner to lose " (count-tags state) " [Credits]") - :effect (effect (lose-credits :runner (count-tags state)))})]}) - -(define-card "Tithonium" - {:alternative-cost [:forfeit] - :implementation "Does not handle UFAQ for Pawn or Blackguard interaction" - :cannot-host true - :subroutines [trash-program - end-the-run - {:label "Trash a resource" - :msg (msg "trash " (:title target)) - :async true - :choices {:req #(and (installed? %) - (resource? %))} - :effect (effect (trash target {:reason :subroutine}))}]}) - -(define-card "TL;DR" - {:subroutines [{:msg "duplicate subroutines on next piece of ICE encountered this run"}]}) - -(define-card "TMI" - {:trace {:base 2 - :msg "keep TMI rezzed" - :label "Keep TMI rezzed" - :unsuccessful {:effect (effect (derez card))}} - :subroutines [end-the-run]}) - -(define-card "Tollbooth" - {:implementation "Encounter effect is manual" - :abilities [{:msg "make the Runner pay 3 [Credits], if able" - :effect (effect (pay :runner card :credit 3))}] - :subroutines [end-the-run]}) - -(define-card "Tour Guide" - {:abilities [{:label "Gain subroutines" - :msg (msg "gain " (count (filter asset? (all-active-installed state :corp))) " subroutines")}] - :subroutines [end-the-run]}) - -(define-card "Trebuchet" - {:effect take-bad-pub - :subroutines [{:prompt "Select a card to trash" - :label "Trash 1 installed Runner card" - :msg (msg "trash " (:title target)) - :choices {:req #(and (installed? %) - (runner? %))} - :async true - :effect (req (trash state side eid target {:cause :subroutine}))} - (trace-ability 6 {:label "The Runner cannot steal or trash Corp cards for the remainder of this run" - :msg "prevent the Runner from stealing or trashing Corp cards for the remainder of the run" - :effect (req (register-run-flag! state side card :can-steal - (fn [state side card] - ((constantly false) - (toast state :runner "Cannot steal due to Trebuchet." "warning")))) - (register-run-flag! state side card :can-trash - (fn [state side card] - ((constantly (not= (:side card) "Corp")) - (toast state :runner "Cannot trash due to Trebuchet." "warning")))))})]}) - -(define-card "Tribunal" - {:subroutines [{:msg "force the Runner to trash 1 installed card" - :effect (effect (resolve-ability :runner trash-installed card nil))}]}) - -(define-card "Troll" - {:implementation "Encounter effect is manual" - :abilities [(trace-ability 2 {:label "Force the Runner to lose [Click] or end the run" - :msg "force the Runner to lose [Click] or end the run" - :player :runner - :prompt "Choose one" - :choices ["Lose [Click]" "End the run"] - :effect (req (if-not (and (= target "Lose [Click]") - (can-pay? state :runner eid card nil [:click 1])) - (do (end-run state side) - (system-msg state side "ends the run")) - (do (lose state side :click 1) - (system-msg state side "loses [Click]"))))})]}) - -(define-card "Tsurugi" - {:subroutines [end-the-run - (do-net-damage 1)]}) - -(define-card "Turing" - {:implementation "AI restriction not implemented" - :subroutines [end-the-run] - :strength-bonus (req (if (is-remote? (second (:zone card))) 3 0)) - :runner-abilities [(runner-pay [:click 3] 1)]}) - -(define-card "Turnpike" - {:implementation "Encounter effect is manual" - :abilities [{:msg "force the Runner to lose 1 [Credits]" - :effect (effect (lose-credits :runner 1))}] - :subroutines [(tag-trace 5)]}) - -(define-card "Tyrant" - {:advanceable :while-rezzed - :abilities [{:label "Gain subroutines" - :msg (msg "gain " (get-counters card :advancement) " subroutines")}] - :subroutines [end-the-run]}) - -(define-card "Universal Connectivity Fee" - {:subroutines [{:label "Force the Runner to lose credits" - :msg (msg "force the Runner to lose " (if tagged "all credits" "1 [Credits]")) - :effect (req (if tagged - (do (lose-credits state :runner :all) - (lose state :runner :run-credit :all) - (when current-ice - (no-action state side nil) - (continue state side nil)) - (trash state side card)) - (lose-credits state :runner 1)))}]}) - -(define-card "Upayoga" - {:implementation "\"Resolve a subroutine...\" subroutine is not implemented" - :subroutines [(do-psi {:label "Make the Runner lose 2 [Credits]" - :msg "make the Runner lose 2 [Credits]" - :effect (effect (lose-credits :runner 2) - (effect-completed eid))}) - {:msg "resolve a subroutine on a piece of rezzed psi ICE"}]}) - -(define-card "Uroboros" - {:subroutines [(trace-ability 4 {:label "Prevent the Runner from making another run" - :msg "prevent the Runner from making another run" - :effect (effect (register-turn-flag! card :can-run nil))}) - (trace-ability 4 end-the-run)]}) - -(define-card "Vanilla" - {:subroutines [end-the-run]}) - -(define-card "Veritas" - {:subroutines [{:label "Corp gains 2 [Credits]" - :msg "gain 2 [Credits]" - :effect (effect (gain-credits :corp 2))} - {:label "Runner loses 2 [Credits]" - :msg "force the Runner to lose 2 [Credits]" - :effect (effect (lose-credits :runner 2))} - (trace-ability 2 (give-tags 1))]}) - -(define-card "Vikram 1.0" - {:implementation "Program prevention is not implemented" - :subroutines [{:msg "prevent the Runner from using programs for the remainder of this run"} - (trace-ability 4 (do-brain-damage 1))] - :runner-abilities [(runner-break [:click 1] 1)]}) - -(define-card "Viktor 1.0" - {:subroutines [(do-brain-damage 1) - end-the-run] - :runner-abilities [(runner-break [:click 1] 1)]}) - -(define-card "Viktor 2.0" - {:abilities [(power-counter-ability (do-brain-damage 1))] - :subroutines [(trace-ability 2 add-power-counter) - end-the-run] - :runner-abilities [(runner-break [:click 2] 2)]}) - -(define-card "Viper" - {:subroutines [(trace-ability 3 {:label "The Runner loses 1 [Click] if able" - :msg "force the Runner to lose 1 [Click] if able" - :effect runner-loses-click}) - (trace-ability 3 end-the-run)]}) - -(define-card "Virgo" - (constellation-ice (give-tags 1))) - -(define-card "Waiver" - {:subroutines [(trace-ability 5 {:label "Reveal the Runner's Grip and trash cards" - :msg (msg "reveal all cards in the Runner's Grip: " (join ", " (map :title (:hand runner))) - ". Cards with a play/install cost less than or equal to " (- target (second targets)) - " will be trashed") - :effect (req (reveal state side (:hand runner)) - (let [delta (- target (second targets))] - (doseq [c (:hand runner)] - (when (<= (:cost c) delta) - (resolve-ability - state side - {:msg (msg "trash " (:title c)) - :effect (effect (trash c))} - card nil)))))})]}) - -(define-card "Wall of Static" - {:subroutines [end-the-run]}) - -(define-card "Wall of Thorns" - {:subroutines [(do-net-damage 2) - end-the-run]}) - -(define-card "Watchtower" - {:subroutines [{:label "Search R&D and add 1 card to HQ" - :prompt "Choose a card to add to HQ" - :msg "add a card from R&D to HQ" - :choices (req (cancellable (:deck corp) :sorted)) - :cancel-effect (effect (system-msg "cancels the effect of Watchtower")) - :effect (effect (shuffle! :deck) - (move target :hand))}]}) - -(define-card "Weir" - {:subroutines [{:label "force the Runner to lose 1 [Click], if able" - :msg "force the Runner to lose 1 [Click]" - :effect runner-loses-click} - {:label "Runner trashes 1 card from their Grip" - :req (req (pos? (count (:hand runner)))) - :prompt "Choose a card to trash from your Grip" - :player :runner - :choices (req (:hand runner)) - :not-distinct true - :effect (effect (trash :runner target) - (system-msg :runner (str "trashes " (:title target) " from their Grip")))}]}) - -(define-card "Wendigo" - (implementation-note - "Program prevention is not implemented" - (morph-ice "Code Gate" "Barrier" - {:msg "prevent the Runner from using a chosen program for the remainder of this run"}))) - -(define-card "Whirlpool" - {:subroutines [{:msg "prevent the Runner from jacking out" - :effect (req (when (and (is-remote? (second (:zone card))) - (> (count (concat (:ices (card->server state card)) - (:content (card->server state card)))) 1)) - (prevent-jack-out state side)) - (when current-ice - (no-action state side nil) - (continue state side nil)) - (trash state side card))}]}) - -(define-card "Woodcutter" - {:advanceable :while-rezzed - :abilities [{:label "Gain subroutines" - :msg (msg "gain " (get-counters card :advancement) " subroutines")}] - :subroutines [(do-net-damage 1)]}) - -(define-card "Wormhole" - ;; TODO: create an ability for wormhole - (implementation-note "Wormhole subroutine is not implemented" - (space-ice))) - -(define-card "Wotan" - {:subroutines [end-the-run - (do-brain-damage 1)] - :runner-abilities [(runner-pay [:click 2] 1) - (runner-pay [:credit 3] 1)]}) - -(define-card "Wraparound" - {:subroutines [end-the-run] - :strength-bonus (req (if (some #(has-subtype? % "Fracter") (all-active-installed state :runner)) - 0 7)) - :events (let [wr {:silent (req true) - :req (req (and (not (same-card? target card)) - (has-subtype? target "Fracter"))) - :effect (effect (update-ice-strength card))}] - {:runner-install wr :trash wr :card-moved wr})}) - -(define-card "Yagura" - {:subroutines [(do-net-damage 1) - {:msg "look at the top card of R&D" - :optional {:prompt (msg "Move " (:title (first (:deck corp))) " to the bottom of R&D?") - :yes-ability {:msg "move the top card of R&D to the bottom" - :effect (effect (move (first (:deck corp)) :deck))} - :no-ability {:effect (effect (system-msg :corp (str "does not use Yagura to move the top card of R&D to the bottom")))}}}]}) - -(define-card "Zed 1.0" - {:implementation "Restriction on having spent [click] is not implemented" - :subroutines [(do-brain-damage 1)] - :runner-abilities [(runner-break [:click 1] 1)]}) - -(define-card "Zed 2.0" - {:implementation "Restriction on having spent [click] is not implemented" - :subroutines [trash-hardware - (do-brain-damage 2)] - :runner-abilities [(runner-break [:click 2] 2)]}) + :effect (req (if (= target "End the run") + (do (system-msg state :corp (str "uses Meridian to gain 4 [Credits] and end the run")) + (clear-wait-prompt state :corp) + (gain-credits state :corp 4) + (end-run state :runner eid)) + (do (system-msg state :runner (str "adds Meridian to their score area as an agenda worth -1 agenda points")) + (clear-wait-prompt state :corp) + (wait-for (as-agenda state :runner card -1) + (when current-ice + (no-action state side nil) + (continue state side nil)) + (effect-completed state side eid)))))} + card nil))}]} + + "Merlin" + (grail-ice (do-net-damage 2)) + + "Meru Mati" + {:subroutines [end-the-run] + :strength-bonus (req (if (= (second (:zone card)) :hq) 3 0))} + + "Metamorph" + {:subroutines [{:label "Swap two ICE or swap two installed non-ICE" + :msg "swap two ICE or swap two installed non-ICE" + :async true + :prompt "Choose one" + :choices ["Swap two ICE" "Swap two non-ICE"] + :effect (req (if (= target "Swap two ICE") + (continue-ability + state side + {:prompt "Select the two ICE to swap" + :async true + :choices {:req #(and (installed? %) (ice? %)) :max 2 :all true} + :msg (msg "swap the positions of " (card-str state (first targets)) " and " (card-str state (second targets))) + :effect (req (when (= (count targets) 2) + (swap-ice state side (first targets) (second targets)) + (effect-completed state side eid)))} + card nil) + (continue-ability + state side + {:prompt "Select the two cards to swap" + :async true + :choices {:req #(and (installed? %) (not (ice? %))) :max 2 :all true} + :msg (msg "swap the positions of " (card-str state (first targets)) " and " (card-str state (second targets))) + :effect (req (when (= (count targets) 2) + (swap-installed state side (first targets) (second targets)) + (effect-completed state side eid)))} + card nil)))}]} + + "Mganga" + {:subroutines [(do-psi {:label "do 2 net damage" + :player :corp + :effect (req (wait-for (damage state :corp :net 2 {:card card}) + (trash state :corp eid card nil)))} + {:label "do 1 net damage" + :player :corp + :effect (req (wait-for (damage state :corp :net 1 {:card card}) + (trash state :corp eid card nil)))})]} + + "Mind Game" + {:subroutines [(do-psi {:label "Redirect the run to another server" + :player :corp + :prompt "Choose a server" + :choices (req (remove #{(-> @state :run :server central->name)} servers)) + :msg (msg "redirect the run to " target) + :effect (req (let [dest (server->zone state target)] + (swap! state update-in [:run] + #(assoc % :position (count (get-in corp (conj dest :ices))) + :server (rest dest)))) + (effect-completed state side eid))})] + :runner-abilities [{:label "Add an installed card to the bottom of your Stack" + :prompt "Choose one of your installed cards" + :choices {:req #(and (installed? %) + (runner? %))} + :effect (effect (move target :deck) + (system-msg :runner (str "adds " (:title target) " to the bottom of their Stack")))}]} + + "Minelayer" + {:subroutines [{:msg "install an ICE from HQ" + :choices {:req #(and (ice? %) + (in-hand? %))} + :prompt "Choose an ICE to install from HQ" + :effect (req (corp-install state side target (zone->name (first (:server run))) {:ignore-all-cost true}))}]} + + "Mirāju" + {:abilities [{:label "Runner broke subroutine: Redirect run to Archives" + :msg "make the Runner continue the run on Archives. Mirāju is derezzed" + :effect (req (swap! state update-in [:run] + #(assoc % :position (count (get-in corp [:servers :archives :ices])) + :server [:archives])) + (derez state side card))}] + :subroutines [{:label "Draw 1 card, then shuffle 1 card from HQ into R&D" + :effect (req (wait-for (resolve-ability + state side + {:optional + {:prompt "Draw 1 card?" + :yes-ability {:async true + :msg "draw 1 card" + :effect (effect (draw eid 1 nil))}}} + card nil) + (resolve-ability + state side + {:prompt "Choose 1 card in HQ to shuffle into R&D" + :choices {:req #(and (in-hand? %) (corp? %))} + :msg "shuffle 1 card in HQ into R&D" + :effect (effect (move target :deck) + (shuffle! :deck))} + card nil)))}]} + + "Mlinzi" + (letfn [(net-or-trash [net-dmg mill-cnt] + {:label (str "Do " net-dmg " net damage") + :effect (req (show-wait-prompt state :corp "Runner to choose an option for Mlinzi") + (resolve-ability + state :runner + {:prompt "Take net damage or trash cards from the stack?" + :choices [(str "Take " net-dmg " net damage") + (str "Trash the top " mill-cnt " cards of the stack")] + :effect (req (if (.startsWith target "Take") + (do (system-msg state :corp + (str "uses Mlinzi to do " + net-dmg " net damage")) + (clear-wait-prompt state :corp) + (damage state :runner eid :net net-dmg {:card card})) + (do (system-msg state :corp + (str "uses Mlinzi to trash " + (join ", " (map :title (take mill-cnt (:deck runner)))) + " from the runner's stack")) + (clear-wait-prompt state :corp) + (mill state :runner mill-cnt))))} + card nil))})] + {:subroutines [(net-or-trash 1 2) + (net-or-trash 2 3) + (net-or-trash 3 4)]}) + + "Mother Goddess" + (let [ab (effect (update! (let [subtype (->> (mapcat :ices (flatten (seq (:servers corp)))) + (filter #(and (rezzed? %) + (not (same-card? card %)))) + (mapcat #(split (:subtype %) #" - ")) + (cons "Mythic") + distinct + (join " - "))] + (assoc card + :subtype-target (remove-subtypes subtype "Mythic") + :subtype subtype)))) + mg {:req (req (ice? target)) + :effect ab}] + {:effect ab + :subroutines [end-the-run] + :events {:rez mg + :card-moved mg + :derez mg + :ice-subtype-changed mg}}) + + "Muckraker" + {:effect take-bad-pub + :subroutines [(tag-trace 1) + (tag-trace 2) + (tag-trace 3) + end-the-run-if-tagged]} + + "Najja 1.0" + {:subroutines [end-the-run] + :runner-abilities [(runner-break [:click 1] 1)]} + + "Nebula" + (space-ice trash-program) + + "Negotiator" + {:subroutines [(gain-credits-sub 2) + trash-program] + :runner-abilities [(runner-break [:credit 2] 1)]} + + "Nerine 2.0" + {:subroutines [{:label "Do 1 brain damage and Corp may draw 1 card" + :async true + :msg "do 1 brain damage" + :effect (req (wait-for (damage state :runner :brain 1 {:card card}) + (resolve-ability + state side + {:optional + {:prompt "Draw 1 card?" + :yes-ability {:async true + :msg "draw 1 card" + :effect (effect (draw eid 1 nil))}}} + card nil)))}] + :runner-abilities [(runner-break [:click 2] 2)]} + + "Neural Katana" + {:subroutines [(do-net-damage 3)]} + + "News Hound" + {:subroutines [(tag-trace 3) + {:label "End the run if a Current is active" + :req (req (or (not (empty? (runner :current))) + (not (empty? (corp :current))))) + :effect (effect (end-run)) :msg "end the run"}]} + + "NEXT Bronze" + {:subroutines [end-the-run] + :strength-bonus (req (next-ice-count corp)) + :events (let [nb {:req (req (and (not (same-card? target card)) + (has-subtype? target "NEXT"))) + :effect (effect (update-ice-strength card))}] + {:rez nb + :derez nb + :trash nb + :card-moved nb})} + + "NEXT Diamond" + {:rez-cost-bonus (req (- (next-ice-count corp))) + :subroutines [(do-brain-damage 1) + {:prompt "Select a card to trash" + :label "Trash 1 installed Runner card" + :msg (msg "trash " (:title target)) + :choices {:req #(and (installed? %) + (runner? %))} + :async true + :effect (req (trash state side eid target {:cause :subroutine}))}]} + + "NEXT Gold" + {:subroutines [{:label "Do 1 net damage for each rezzed NEXT ice" + :msg (msg "do " (next-ice-count corp) " net damage") + :effect (effect (damage eid :net (next-ice-count corp) {:card card}))} + trash-program]} + + "NEXT Opal" + {:subroutines [{:label "Install a card from HQ, paying all costs" + :prompt "Choose a card in HQ to install" + :priority true + :choices {:req #(and (not (operation? %)) + (in-hand? %) + (corp? %))} + :effect (effect (corp-install target nil)) + :msg (msg (corp-install-msg target))}]} + + "NEXT Sapphire" + {:subroutines [{:label "Draw up to X cards" + :prompt "Draw how many cards?" + :msg (msg "draw " target " cards") + :choices {:number (req (next-ice-count corp)) + :default (req 1)} + :async true + :effect (effect (draw eid target nil))} + {:label "Add up to X cards from Archives to HQ" + :prompt "Select cards to add to HQ" + :show-discard true + :choices {:req #(and (corp? %) + (= [:discard] (:zone %))) + :max (req (next-ice-count corp))} + :effect (req (doseq [c targets] + (move state side c :hand))) + :msg (msg "add " + (let [seen (filter :seen targets) + m (count (filter #(not (:seen %)) targets))] + (str (join ", " (map :title seen)) + (when (pos? m) + (str (when-not (empty? seen) " and ") + (quantify m "unseen card"))))) + " to HQ")} + {:label "Shuffle up to X cards from HQ into R&D" + :prompt "Select cards to shuffle into R&D" + :choices {:req #(and (corp? %) + (= [:hand] (:zone %))) + :max (req (next-ice-count corp))} + :effect (req (doseq [c targets] + (move state :corp c :deck)) + (shuffle! state :corp :deck)) + :cancel-effect (effect (shuffle! :corp :deck)) + :msg (msg "shuffle " (count targets) " cards from HQ into R&D")}]} + + "NEXT Silver" + {:abilities [{:label "Gain subroutines" + :msg (msg "gain " + (count (filter #(and (ice? %) + (has-subtype? % "NEXT")) + (all-active-installed state :corp))) + " subroutines")}] + :subroutines [end-the-run]} + + "Nightdancer" + {:subroutines [{:label (str "The Runner loses [Click], if able. " + "You have an additional [Click] to spend during your next turn.") + :msg (str "force the runner to lose a [Click], if able. " + "Corp gains an additional [Click] to spend during their next turn") + :effect (req (lose state :runner :click 1) + (swap! state update-in [:corp :extra-click-temp] (fnil inc 0)))}]} + + "Oduduwa" + {:implementation "Encounter effect is manual" + :abilities [{:label "Place 1 advancement counter on Oduduwa" + :msg (msg "place 1 advancement counter on Oduduwa") + :effect (req (add-prop state side card :advance-counter 1 {:placed true}))} + {:label "Place X advancement token on another piece of ice" + :msg (msg "place " (get-counters card :advancement) " advancement token on " (card-str state target)) + :choices {:req ice? + :not-self true} + :effect (req (add-prop state side target :advance-counter (get-counters card :advancement) {:placed true}))}] + :subroutines [end-the-run]} + + "Orion" + (implementation-note "\"Resolve a subroutine...\" subroutine is not implemented" + (space-ice trash-program end-the-run)) + + "Otoroshi" + {:subroutines [{:async true + :label "Place 3 advancement tokens on installed card" + :msg "place 3 advancement tokens on installed card" + :prompt "Choose an installed Corp card" + :choices {:req #(and (corp? %) + (installed? %))} + :effect (req (let [c target + title (if (:rezzed c) + (:title c) + "selected unrezzed card")] + (add-counter state side c :advancement 3) + (show-wait-prompt state side "Runner to resolve Otoroshi") + (continue-ability + state side + {:player :runner + :async true + :prompt (str "Access " title " or pay 3 [Credits]?") + :choices (concat ["Access card"] + (when (>= (:credit runner) 3) + ["Pay 3 [Credits]"])) + :msg (msg "force the Runner to " + (if (= target "Access card") + (str "access " title) + "pay 3 [Credits]")) + :effect (req (clear-wait-prompt state :corp) + (if (= target "Access card") + (access-card state :runner eid c) + (pay-sync state :runner eid card :credit 3)))} + card nil)))}]} + + "Owl" + {:subroutines [{:choices {:req #(and (installed? %) + (program? %))} + :label "Add installed program to the top of the Runner's Stack" + :msg "add an installed program to the top of the Runner's Stack" + :effect (effect (move :runner target :deck {:front true}) + (system-msg (str "adds " (:title target) " to the top of the Runner's Stack")))}]} + + "Pachinko" + {:subroutines [end-the-run-if-tagged]} + + "Paper Wall" + {:implementation "Trash on break is manual" + :subroutines [end-the-run]} + + "Peeping Tom" + {:implementation "Encounter effect is manual" + :abilities [{:req (req (= current-ice card)) + :label "Name a card type and reveal all cards in the Runner's Grip" + :prompt "Choose a card type" + :choices ["Event" "Hardware" "Program" "Resource"] + :effect (req (let [n (count (filter #(is-type? % target) (:hand runner)))] + (system-msg state side (str "uses Peeping Tom to name " target ", then reveals " + (join ", " (map :title (:hand runner))) + " in the Runner's Grip. Peeping Tom gains " n " subroutines")) + (reveal state side (:hand runner))))}] + :runner-abilities [{:label "End the run" + :effect (req (end-run state :runner) + (system-msg state :runner "chooses to end the run"))} + {:label "Take 1 tag" + :async true + :effect (req (system-msg state :runner "chooses to take 1 tag from Peeping Tom") + (gain-tags state :runner eid 1))}]} + + "Pop-up Window" + {:implementation "Encounter effect is manual. Runner choice is not implemented" + :abilities [(gain-credits-sub 1)] + :subroutines [end-the-run] + :runner-abilities [(runner-pay [:credit 1] 1)]} + + "Pup" + {:subroutines [(do-net-damage 1)] + :runner-abilities [(runner-pay [:credit 1] 1)]} + + "Quandary" + {:subroutines [end-the-run]} + + "Quicksand" + {:implementation "Encounter effect is manual" + :abilities [{:req (req (and this-server (= (dec (:position run)) (ice-index state card)))) + :label "Add 1 power counter" + :effect (effect (add-counter card :power 1) + (update-all-ice))}] + :subroutines [end-the-run] + :strength-bonus (req (get-counters card :power))} + + "Rainbow" + {:subroutines [end-the-run]} + + "Ravana 1.0" + {:subroutines [{:label "Resolve a subroutine on another piece of rezzed bioroid ICE" + :choices {:req #(and (rezzed? %) (ice? %) (has-subtype? % "Bioroid"))} + :msg (msg "resolve a subroutine on " (:title target))}] + :runner-abilities [(runner-break [:click 1] 1)]} + + "Red Tape" + {:subroutines [{:label "Give +3 strength to all ICE for the remainder of the run" + :msg "give +3 strength to all ICE for the remainder of the run" + :effect (effect (register-events + {:pre-ice-strength {:effect (effect (ice-strength-bonus 3 target))} + :run-ends {:effect (effect (unregister-events card))}} + card) + (update-all-ice))}] + :events {:pre-ice-strength nil :run-ends nil}} + + "Resistor" + (let [resistor-effect {:effect (effect (update! (assoc (get-card state card) :strength-bonus (count-tags state))) + (update-ice-strength (get-card state card)))}] + {:events {:runner-gain-tag resistor-effect + :runner-lose-tag resistor-effect + :runner-additional-tag-change resistor-effect} + :strength-bonus (req (count-tags state)) + :subroutines [(trace-ability 4 end-the-run)]}) + + "Rime" + {:implementation "Can be rezzed anytime already" + :effect (effect (update-all-ice)) + :subroutines [{:label "Runner loses 1 [Credit]" + :msg "force the Runner to lose 1 [Credit]" + :effect (effect (lose-credits :runner 1))}] + :events {:corp-moved {:req (req (ice? target)) + :effect (effect (update-ice-strength target))} + :corp-install {:req (req (ice? target)) + :effect (effect (update-ice-strength target))} + :pre-ice-strength {:req (req (and (ice? target) + (protecting-same-server? card target))) + :effect (effect (ice-strength-bonus 1 target))}}} + + "Rototurret" + {:subroutines [trash-program + end-the-run]} + + "Sadaka" + (let [maybe-draw-effect + {:async true + :effect (req (show-wait-prompt state :runner "Corp to decide on Sadaka card draw action") + (continue-ability + state side + {:optional + {:player :corp + :prompt "Draw 1 card?" + :yes-ability + {:async true + :effect (effect (clear-wait-prompt :runner) + (draw eid 1 nil)) + :msg "draw 1 card"} + :no-ability {:effect (effect (clear-wait-prompt :runner) + (effect-completed eid))}}} + card nil))}] + {:subroutines [{:label "Look at the top 3 cards of R&D" + :req (req (not-empty (:deck corp))) + :async true + :effect (req (let [top-cards (take 3 (:deck corp)) + top-names (map :title top-cards)] + (show-wait-prompt state :runner "Corp to decide on Sadaka R&D card actions") + (continue-ability + state side + {:prompt (str "Top 3 cards of R&D: " (clojure.string/join ", " top-names)) + :choices ["Arrange cards" "Shuffle R&D"] + :async true + :effect + (req (if (= target "Arrange cards") + (wait-for + (resolve-ability state side (reorder-choice :corp top-cards) card nil) + (do + (system-msg state :corp (str "rearranges the top " + (quantify (count top-cards) "card") + " of R&D")) + (clear-wait-prompt state :runner) + (continue-ability state side maybe-draw-effect card nil))) + (do + (shuffle! state :corp :deck) + (system-msg state :corp (str "shuffles R&D")) + (clear-wait-prompt state :runner) + (continue-ability state side maybe-draw-effect card nil))))} + card nil)))} + {:label "Trash 1 card in HQ" + :async true + :effect + (req (show-wait-prompt state :runner "Corp to select cards to trash with Sadaka") + (wait-for + (resolve-ability + state side + {:prompt "Choose a card in HQ to trash" + :choices (req (cancellable (:hand corp) :sorted)) + :async true + :cancel-effect (effect (system-msg "chooses not to trash a card from HQ") + (effect-completed eid)) + :effect (req (wait-for + (trash state :corp (make-eid state) target nil) + (do + (system-msg state :corp "trashes a card from HQ") + (wait-for + (resolve-ability state side trash-resource-sub card nil) + (effect-completed state side eid)))))} + card nil) + (do + (system-msg state :corp "trashes Sadaka") + (clear-wait-prompt state :runner) + (when current-ice + (no-action state side nil) + (continue state side nil)) + (trash state :corp eid card nil))))}]}) + + "Sagittarius" + (constellation-ice trash-program) + + "Saisentan" + {:implementation "Encounter effect is manual" + :subroutines [{:label "Do 1 net damage" + :async true + :msg "do 1 net damage" + :effect (req (wait-for (damage state side :net 1 {:card card}) + (when-let* [choice (get-in card [:special :saisentan]) + cards (some #(when (same-card? (second %) card) (last %)) + (turn-events state :corp :damage)) + dmg (some #(when (= (:type %) choice) %) cards)] + (system-msg state :corp "uses Saisentan to deal a second net damage") + (damage state side eid :net 1 {:card card}))))}] + :abilities [{:label "Choose card type" + :req (req (and (same-card? current-ice card) + (rezzed? card))) + :effect (effect (show-wait-prompt :runner "Corp to choose Saisentan card type") + (continue-ability + {:prompt "Choose a card type" + :choices ["Event" "Hardware" "Program" "Resource"] + :msg (msg "choose the card type " target) + :effect (effect (update! (assoc-in card [:special :saisentan] target)) + (clear-wait-prompt :runner))} + card nil))}]} + + "Salvage" + {:advanceable :while-rezzed + :abilities [{:label "Gain subroutines" + :msg (msg "gain " (get-counters card :advancement) " subroutines")}] + :subroutines [(tag-trace 2)]} + + "Sand Storm" + {:subroutines [{:req (req (:run @state)) + :label "Move Sand Storm and the run to another server" + :prompt "Choose another server and redirect the run to its outermost position" + :choices (req (cancellable servers)) + :msg (msg "move Sand Storm and the run. The Runner is now running on " target ". Sand Storm is trashed") + :effect (req (let [dest (server->zone state target)] + (swap! state update-in [:run] + #(assoc % :position (count (get-in corp (conj dest :ices))) + :server (rest dest))) + (trash state side card {:unpreventable true})))}]} + + "Sandstone" + {:subroutines [end-the-run] + :strength-bonus (req (- (get-counters card :virus))) + :abilities [{:label "Place one virus counter" + :req (req (same-card? current-ice card)) + :msg "place 1 virus counter on Sandstone" + :effect (effect (add-counter card :virus 1) + (update-ice-strength (get-card state card)))}]} + + "Sandman" + {:subroutines [{:label "Add an installed Runner card to the grip" + :req (req (not-empty (all-installed state :runner))) + :effect (effect (show-wait-prompt :runner "Corp to select Sandman target") + (resolve-ability {:choices {:req #(and (installed? %) + (runner? %))} + :msg (msg "to add " (:title target) " to the grip") + :effect (effect (clear-wait-prompt :runner) + (move :runner target :hand true)) + :cancel-effect (effect (clear-wait-prompt :runner))} + card nil))}]} + + "Sapper" + {:flags {:rd-reveal (req true)} + :subroutines [trash-program] + :access {:async true + :req (req (and (not= (first (:zone card)) :discard) + (some program? (all-active-installed state :runner)))) + :effect (effect (show-wait-prompt :corp "Runner to decide to break Sapper subroutine") + (continue-ability + :runner {:optional + {:player :runner + :prompt "Allow Sapper subroutine to fire?" + :priority 1 + :yes-ability {:effect (req (clear-wait-prompt state :corp) + (show-wait-prompt state :runner "Corp to trash a program with Sapper") + (play-subroutine state :corp eid {:card card :subroutine 0}))} + :no-ability {:effect (effect (clear-wait-prompt :corp) + (effect-completed eid))}}} + card nil))}} + + "Searchlight" + {:advanceable :always + :subroutines [{:label "Trace X - Give the Runner 1 tag" + :trace {:base advance-counters + :label "Give the Runner 1 tag" + :successful (give-tags 1)}}]} + + "Seidr Adaptive Barrier" + (let [recalculate-strength (req (update-ice-strength state side (get-card state card))) + recalc-event {:req (req (= (:zone target) (:zone card))) + :effect recalculate-strength}] + {:effect recalculate-strength + :strength-bonus (req (count (:ices (card->server state card)))) + :subroutines [end-the-run] + :events {:card-moved recalc-event + :corp-install recalc-event}}) + + "Self-Adapting Code Wall" + {:subroutines [end-the-run] + :flags {:cannot-lower-strength true}} + + "Sensei" + {:subroutines [{:label "Give each other ICE encountered \"End the run\" for the remainder of the run" + :msg (msg "give each other ICE encountered \"[Subroutine] End the run\" after all its other subroutines for the remainder of the run")}]} + + "Shadow" + {:advanceable :always + :subroutines [(gain-credits-sub 2) + (tag-trace 3)] + :strength-bonus advance-counters} + + "Sherlock 1.0" + {:subroutines [(trace-ability 4 {:choices {:req #(and (installed? %) + (program? %))} + :label "Add an installed program to the top of the Runner's Stack" + :msg (msg "add " (:title target) " to the top of the Runner's Stack") + :effect (effect (move :runner target :deck {:front true}))})] + :runner-abilities [(runner-break [:click 1] 1)]} + + "Sherlock 2.0" + {:subroutines [(trace-ability 4 {:choices {:req #(and (installed? %) + (program? %))} + :label "Add an installed program to the bottom of the Runner's Stack" + :msg (msg "add " (:title target) " to the bottom of the Runner's Stack") + :effect (effect (move :runner target :deck))}) + (give-tags 1)] + :runner-abilities [(runner-break [:click 2] 2)]} + + "Shinobi" + {:effect take-bad-pub + :subroutines [(trace-ability 1 (do-net-damage 1)) + (trace-ability 2 (do-net-damage 2)) + (trace-ability 3 {:label "Do 3 net damage and end the run" + :msg "do 3 net damage and end the run" + :effect (effect (damage eid :net 3 {:card card}) + (end-run))})]} + + "Shiro" + {:subroutines [{:label "Rearrange the top 3 cards of R&D" + :msg "rearrange the top 3 cards of R&D" + :async true + :effect (req (show-wait-prompt state :runner "Corp to rearrange the top cards of R&D") + (let [from (take 3 (:deck corp))] + (if (pos? (count from)) + (continue-ability state side (reorder-choice :corp :runner from '() + (count from) from) card nil) + (do (clear-wait-prompt state :runner) + (effect-completed state side eid)))))} + {:label "Force the Runner to access the top card of R&D" + :async true + :effect (req (do-access state :runner eid [:rd] {:no-root true}))}]} + + "Slot Machine" + (letfn [(name-builder [card] (str (:title card) " (" (:type card) ")")) + (top-3 [state] (take 3 (get-in @state [:runner :deck]))) + (top-3-names [state] (map name-builder (top-3 state))) + (top-3-types [state] (->> (top-3 state) (map :type) (into #{}) count))] + {:implementation "Encounter effect is manual" + :abilities [{:label "Roll them bones" + :req (req (same-card? current-ice card)) + :effect (effect (move :runner (first (:deck runner)) :deck) + (reveal (take 3 (:deck runner))) + (system-msg (str "uses Slot Machine to put the top card of the stack to the bottom," + " then reveal the top 3 cards in the stack: " + (join ", " (top-3-names state)))))}] + :subroutines [{:label "Runner loses 3 [Credits]" + :msg "force the Runner to lose 3 [Credits]" + :effect (effect (lose-credits :runner 3))} + {:label "Gain 3 [Credits]" + :effect (req (let [unique-types (top-3-types state)] + (when (>= 2 unique-types) + (system-msg state :corp (str "uses Slot Machine to gain 3 [Credits]")) + (gain-credits state :corp 3))))} + {:label "Place 3 advancement tokens" + :effect (req (let [unique-types (top-3-types state)] + (when (= 1 unique-types) + (continue-ability + state side + {:choices {:req installed?} + :prompt "Choose an installed card" + :msg (msg "place 3 advancement tokens on " + (card-str state target)) + :effect (effect (add-prop target :advance-counter 3 {:placed true}))} + card nil))))}]}) + + "Snoop" + {:implementation "Encounter effect is manual" + :abilities [{:req (req (= current-ice card)) + :label "Reveal all cards in the Runner's Grip" + :msg (msg "reveal the Runner's Grip ( " (join ", " (map :title (:hand runner))) " )")} + {:req (req (pos? (get-counters card :power))) + :counter-cost [:power 1] + :label "Hosted power counter: Reveal all cards in Grip and trash 1 card" + :msg (msg "look at all cards in Grip and trash " (:title target) + " using 1 power counter") + :choices (req (cancellable (:hand runner) :sorted)) + :prompt "Choose a card to trash" + :effect (effect (reveal (:hand runner)) + (trash target))}] + :subroutines [(trace-ability 3 add-power-counter)]} + + "Snowflake" + {:subroutines [(do-psi {:label "End the run" + :msg "end the run" + :effect (effect (end-run eid))})]} + + "Special Offer" + {:subroutines [{:label "Gain 5 [Credits] and trash Special Offer" + :effect (req (gain-credits state :corp 5) + (when current-ice + (no-action state side nil) + (continue state side nil)) + (trash state side card) + (system-msg state side (str "gains 5 [Credits] and trashes Special Offer")))}]} + + "Spiderweb" + {:subroutines [end-the-run]} + + "Surveyor" + (let [x (req (* 2 (count (:ices (card->server state card))))) + recalculate-strength (req (update-ice-strength state side (get-card state card))) + recalc-event {:req (req (= (:zone target) (:zone card))) + :effect recalculate-strength}] + {:effect recalculate-strength + :strength-bonus x + :subroutines [{:label "Trace X - Give the Runner 2 tags" + :trace {:base x + :label "Give the Runner 2 tags" + :successful (give-tags 2)}} + {:label "Trace X - End the run" + :trace {:base x + :label "End the run" + :successful end-the-run}}] + :events {:card-moved recalc-event + :corp-install recalc-event}}) + + "Susanoo-no-Mikoto" + {:subroutines [{:req (req (not= (:server run) [:discard])) + :msg "make the Runner continue the run on Archives" + :effect (req (swap! state update-in [:run] + #(assoc % + :position (count (get-in corp [:servers :archives :ices])) + :server [:archives])))}]} + + "Swarm" + {:effect take-bad-pub + :advanceable :always + :abilities [{:label "Gain subroutines" + :msg (msg "gain " (get-counters card :advancement) " subroutines")}] + :subroutines [trash-program] + :runner-abilities [(runner-pay [:credit 3] 1)]} + + "Swordsman" + {:implementation "AI restriction not implemented" + :subroutines [(do-net-damage 1) + {:prompt "Select an AI program to trash" + :msg (msg "trash " (:title target)) + :label "Trash an AI program" + :effect (effect (trash target)) + :choices {:req #(and (installed? %) + (program? %) + (has-subtype? % "AI"))}}]} + + "SYNC BRE" + {:subroutines [(tag-trace 4) + (trace-ability 2 {:label "Runner reduces cards accessed by 1 for this run" + :async true + :msg "reduce cards accessed for this run by 1" + :effect (effect (access-bonus (-> card :zone second) -1))})]} + + "Tapestry" + {:subroutines [{:label "force the Runner to lose 1 [Click], if able" + :msg "force the Runner to lose 1 [Click]" + :effect runner-loses-click} + {:msg "draw 1 card" + :effect (effect (draw))} + {:req (req (pos? (count (:hand corp)))) + :prompt "Choose a card in HQ to move to the top of R&D" + :choices {:req #(and (in-hand? %) (corp? %))} + :msg "add 1 card in HQ to the top of R&D" + :effect (effect (move target :deck {:front true}))}]} + + "Taurus" + (constellation-ice trash-hardware) + + "Thimblerig" + {:flags {:corp-phase-12 (req (>= (count (filter ice? (all-installed state :corp))) 2))} + :implementation "Does not restrict usage of swap ability to start of turn or after pass" + :abilities [{:label "Swap Thimblerig with a piece of ice" + :prompt "Choose a piece of ice to swap Thimblerig with" + :choices {:req ice? + :not-self true} + :effect (effect (swap-ice card target)) + :msg (msg "swap " (card-str state card) " with " (card-str state target))}] + :subroutines [end-the-run]} + + "Thoth" + {:implementation "Encounter effect is manual" + :runner-abilities [{:label "Take 1 tag" + :async true + :effect (req (system-msg state :runner "takes 1 tag on encountering Thoth") + (gain-tags state :corp eid 1))}] + :subroutines [(trace-ability 4 {:label "Do 1 net damage for each Runner tag" + :async true + :msg (msg "do " (count-tags state) " net damage") + :effect (effect (damage eid :net (count-tags state) {:card card}))}) + (trace-ability 4 {:label "Runner loses 1 [Credits] for each tag" + :async true + :msg (msg "force the Runner to lose " (count-tags state) " [Credits]") + :effect (effect (lose-credits :runner (count-tags state)))})]} + + "Tithonium" + {:alternative-cost [:forfeit] + :implementation "Does not handle UFAQ for Pawn or Blackguard interaction" + :cannot-host true + :subroutines [trash-program + end-the-run + {:label "Trash a resource" + :msg (msg "trash " (:title target)) + :async true + :choices {:req #(and (installed? %) + (resource? %))} + :effect (effect (trash target {:reason :subroutine}))}]} + + "TL;DR" + {:subroutines [{:msg "duplicate subroutines on next piece of ICE encountered this run"}]} + + "TMI" + {:trace {:base 2 + :msg "keep TMI rezzed" + :label "Keep TMI rezzed" + :unsuccessful {:effect (effect (derez card))}} + :subroutines [end-the-run]} + + "Tollbooth" + {:implementation "Encounter effect is manual" + :abilities [{:msg "make the Runner pay 3 [Credits], if able" + :effect (effect (pay :runner card :credit 3))}] + :subroutines [end-the-run]} + + "Tour Guide" + {:abilities [{:label "Gain subroutines" + :msg (msg "gain " (count (filter asset? (all-active-installed state :corp))) " subroutines")}] + :subroutines [end-the-run]} + + "Trebuchet" + {:effect take-bad-pub + :subroutines [{:prompt "Select a card to trash" + :label "Trash 1 installed Runner card" + :msg (msg "trash " (:title target)) + :choices {:req #(and (installed? %) + (runner? %))} + :async true + :effect (req (trash state side eid target {:cause :subroutine}))} + (trace-ability 6 {:label "The Runner cannot steal or trash Corp cards for the remainder of this run" + :msg "prevent the Runner from stealing or trashing Corp cards for the remainder of the run" + :effect (req (register-run-flag! state side card :can-steal + (fn [state side card] + ((constantly false) + (toast state :runner "Cannot steal due to Trebuchet." "warning")))) + (register-run-flag! state side card :can-trash + (fn [state side card] + ((constantly (not= (:side card) "Corp")) + (toast state :runner "Cannot trash due to Trebuchet." "warning")))))})]} + + "Tribunal" + {:subroutines [{:msg "force the Runner to trash 1 installed card" + :effect (effect (resolve-ability :runner trash-installed card nil))}]} + + "Troll" + {:implementation "Encounter effect is manual" + :abilities [(trace-ability 2 {:label "Force the Runner to lose [Click] or end the run" + :msg "force the Runner to lose [Click] or end the run" + :player :runner + :prompt "Choose one" + :choices ["Lose [Click]" "End the run"] + :effect (req (if-not (and (= target "Lose [Click]") + (can-pay? state :runner eid card nil [:click 1])) + (do (end-run state side) + (system-msg state side "ends the run")) + (do (lose state side :click 1) + (system-msg state side "loses [Click]"))))})]} + + "Tsurugi" + {:subroutines [end-the-run + (do-net-damage 1)]} + + "Turing" + {:implementation "AI restriction not implemented" + :subroutines [end-the-run] + :strength-bonus (req (if (is-remote? (second (:zone card))) 3 0)) + :runner-abilities [(runner-pay [:click 3] 1)]} + + "Turnpike" + {:implementation "Encounter effect is manual" + :abilities [{:msg "force the Runner to lose 1 [Credits]" + :effect (effect (lose-credits :runner 1))}] + :subroutines [(tag-trace 5)]} + + "Tyrant" + {:advanceable :while-rezzed + :abilities [{:label "Gain subroutines" + :msg (msg "gain " (get-counters card :advancement) " subroutines")}] + :subroutines [end-the-run]} + + "Universal Connectivity Fee" + {:subroutines [{:label "Force the Runner to lose credits" + :msg (msg "force the Runner to lose " (if tagged "all credits" "1 [Credits]")) + :effect (req (if tagged + (do (lose-credits state :runner :all) + (lose state :runner :run-credit :all) + (when current-ice + (no-action state side nil) + (continue state side nil)) + (trash state side card)) + (lose-credits state :runner 1)))}]} + + "Upayoga" + {:implementation "\"Resolve a subroutine...\" subroutine is not implemented" + :subroutines [(do-psi {:label "Make the Runner lose 2 [Credits]" + :msg "make the Runner lose 2 [Credits]" + :effect (effect (lose-credits :runner 2) + (effect-completed eid))}) + {:msg "resolve a subroutine on a piece of rezzed psi ICE"}]} + + "Uroboros" + {:subroutines [(trace-ability 4 {:label "Prevent the Runner from making another run" + :msg "prevent the Runner from making another run" + :effect (effect (register-turn-flag! card :can-run nil))}) + (trace-ability 4 end-the-run)]} + + "Vanilla" + {:subroutines [end-the-run]} + + "Veritas" + {:subroutines [{:label "Corp gains 2 [Credits]" + :msg "gain 2 [Credits]" + :effect (effect (gain-credits :corp 2))} + {:label "Runner loses 2 [Credits]" + :msg "force the Runner to lose 2 [Credits]" + :effect (effect (lose-credits :runner 2))} + (trace-ability 2 (give-tags 1))]} + + "Vikram 1.0" + {:implementation "Program prevention is not implemented" + :subroutines [{:msg "prevent the Runner from using programs for the remainder of this run"} + (trace-ability 4 (do-brain-damage 1))] + :runner-abilities [(runner-break [:click 1] 1)]} + + "Viktor 1.0" + {:subroutines [(do-brain-damage 1) + end-the-run] + :runner-abilities [(runner-break [:click 1] 1)]} + + "Viktor 2.0" + {:abilities [(power-counter-ability (do-brain-damage 1))] + :subroutines [(trace-ability 2 add-power-counter) + end-the-run] + :runner-abilities [(runner-break [:click 2] 2)]} + + "Viper" + {:subroutines [(trace-ability 3 {:label "The Runner loses 1 [Click] if able" + :msg "force the Runner to lose 1 [Click] if able" + :effect runner-loses-click}) + (trace-ability 3 end-the-run)]} + + "Virgo" + (constellation-ice (give-tags 1)) + + "Waiver" + {:subroutines [(trace-ability 5 {:label "Reveal the Runner's Grip and trash cards" + :msg (msg "reveal all cards in the Runner's Grip: " (join ", " (map :title (:hand runner))) + ". Cards with a play/install cost less than or equal to " (- target (second targets)) + " will be trashed") + :effect (req (reveal state side (:hand runner)) + (let [delta (- target (second targets))] + (doseq [c (:hand runner)] + (when (<= (:cost c) delta) + (resolve-ability + state side + {:msg (msg "trash " (:title c)) + :effect (effect (trash c))} + card nil)))))})]} + + "Wall of Static" + {:subroutines [end-the-run]} + + "Wall of Thorns" + {:subroutines [(do-net-damage 2) + end-the-run]} + + "Watchtower" + {:subroutines [{:label "Search R&D and add 1 card to HQ" + :prompt "Choose a card to add to HQ" + :msg "add a card from R&D to HQ" + :choices (req (cancellable (:deck corp) :sorted)) + :cancel-effect (effect (system-msg "cancels the effect of Watchtower")) + :effect (effect (shuffle! :deck) + (move target :hand))}]} + + "Weir" + {:subroutines [{:label "force the Runner to lose 1 [Click], if able" + :msg "force the Runner to lose 1 [Click]" + :effect runner-loses-click} + {:label "Runner trashes 1 card from their Grip" + :req (req (pos? (count (:hand runner)))) + :prompt "Choose a card to trash from your Grip" + :player :runner + :choices (req (:hand runner)) + :not-distinct true + :effect (effect (trash :runner target) + (system-msg :runner (str "trashes " (:title target) " from their Grip")))}]} + + "Wendigo" + (implementation-note + "Program prevention is not implemented" + (morph-ice "Code Gate" "Barrier" + {:msg "prevent the Runner from using a chosen program for the remainder of this run"})) + + "Whirlpool" + {:subroutines [{:msg "prevent the Runner from jacking out" + :effect (req (when (and (is-remote? (second (:zone card))) + (> (count (concat (:ices (card->server state card)) + (:content (card->server state card)))) 1)) + (prevent-jack-out state side)) + (when current-ice + (no-action state side nil) + (continue state side nil)) + (trash state side card))}]} + + "Woodcutter" + {:advanceable :while-rezzed + :abilities [{:label "Gain subroutines" + :msg (msg "gain " (get-counters card :advancement) " subroutines")}] + :subroutines [(do-net-damage 1)]} + + "Wormhole" + ;; TODO: create an ability for wormhole + (implementation-note "Wormhole subroutine is not implemented" + (space-ice)) + + "Wotan" + {:subroutines [end-the-run + (do-brain-damage 1)] + :runner-abilities [(runner-pay [:click 2] 1) + (runner-pay [:credit 3] 1)]} + + "Wraparound" + {:subroutines [end-the-run] + :strength-bonus (req (if (some #(has-subtype? % "Fracter") (all-active-installed state :runner)) + 0 7)) + :events (let [wr {:silent (req true) + :req (req (and (not (same-card? target card)) + (has-subtype? target "Fracter"))) + :effect (effect (update-ice-strength card))}] + {:runner-install wr :trash wr :card-moved wr})} + + "Yagura" + {:subroutines [(do-net-damage 1) + {:msg "look at the top card of R&D" + :optional {:prompt (msg "Move " (:title (first (:deck corp))) " to the bottom of R&D?") + :yes-ability {:msg "move the top card of R&D to the bottom" + :effect (effect (move (first (:deck corp)) :deck))} + :no-ability {:effect (effect (system-msg :corp (str "does not use Yagura to move the top card of R&D to the bottom")))}}}]} + + "Zed 1.0" + {:implementation "Restriction on having spent [click] is not implemented" + :subroutines [(do-brain-damage 1)] + :runner-abilities [(runner-break [:click 1] 1)]} + + "Zed 2.0" + {:implementation "Restriction on having spent [click] is not implemented" + :subroutines [trash-hardware + (do-brain-damage 2)] + :runner-abilities [(runner-break [:click 2] 2)]}}) diff --git a/src/clj/game/cards/identities.clj b/src/clj/game/cards/identities.clj index cff34a593d..99e40d8ddd 100644 --- a/src/clj/game/cards/identities.clj +++ b/src/clj/game/cards/identities.clj @@ -1,7 +1,7 @@ (ns game.cards.identities (:require [game.core :refer :all] [game.core.eid :refer [effect-completed]] - [game.core.card-defs :refer [card-def define-card]] + [game.core.card-defs :refer [card-def]] [game.utils :refer :all] [game.macros :refer [effect req msg wait-for continue-ability]] [clojure.string :refer [split-lines split join lower-case includes? starts-with?]] @@ -34,1356 +34,1357 @@ (= fc best-faction))) ;; Card definitions -(define-card "419: Amoral Scammer" - {:events {:corp-install - {:async true - :req (req (and (first-event? state :corp :corp-install) - (pos? (:turn @state)) - (not (rezzed? target)))) - :effect - (req (show-wait-prompt state :corp "Runner to use 419: Amoral Scammer") - (let [itarget target] - (continue-ability - state side - {:optional - {:prompt "Expose installed card unless Corp pays 1 [Credits]?" - :player :runner - :autoresolve (get-autoresolve :auto-419) - :no-ability {:effect (req (clear-wait-prompt state :corp))} - :yes-ability - {:async true - :effect (req (clear-wait-prompt state :corp) - (if (not (can-pay? state :corp eid card nil :credit 1)) - (do - (toast state :corp "Cannot afford to pay 1 credit to block card exposure" "info") - (expose state :runner eid itarget)) - (do - (show-wait-prompt state :runner "Corp decision") - (continue-ability - state side - {:optional - {:prompt "Pay 1 [Credits] to prevent exposure of installed card?" - :player :corp - :no-ability - {:async true - :effect (req (expose state :runner eid itarget) - (clear-wait-prompt state :runner))} - :yes-ability - {:effect (req (pay state :corp card [:credit 1]) - (system-msg state :corp (str "spends 1 [Credits] to prevent " - " card from being exposed")) - (clear-wait-prompt state :runner))}}} - card nil))))}}} - card nil)))}} - :abilities [(set-autoresolve :auto-419 "419")]}) - -(define-card "Acme Consulting: The Truth You Need" - (letfn [(activate [state card active] - (update! state :corp (assoc-in card [:special :acme-active] active)) - (swap! state update-in [:runner :tag :additional] (if active inc dec)) - (trigger-event state :corp :runner-additional-tag-change (if active 1 -1))) - (outermost? [run-position run-ices] - (and run-position - (pos? run-position) - (= run-position (count run-ices))))] - {:implementation "Additional tag is gained on approach, not on encounter" - ;; Should be comprehensive list of all cases when tag should be gained or lost - :events {:run {:effect (req (when (and (outermost? run-position run-ices) - (rezzed? current-ice)) - (activate state card true)))} - :rez {:effect (req (when (and (outermost? run-position run-ices) - (same-card? current-ice target)) - (activate state card true)))} - :derez {:effect (req (when (outermost? run-position run-ices) - (activate state card false)))} - :pass-ice {:effect (req (when (and (outermost? run-position run-ices) - (get-in card [:special :acme-active])) - (activate state card false)))} - :run-ends {:effect (req (when (get-in card [:special :acme-active]) - (activate state card false)))}}})) - -(define-card "Adam: Compulsive Hacker" - {:events {:pre-start-game - {:req (req (= side :runner)) - :async true - :effect (req (show-wait-prompt state :corp "Runner to choose starting directives") - (let [is-directive? #(has-subtype? % "Directive") - directives (filter is-directive? (server-cards)) - directives (map make-card directives) - directives (zone :play-area directives)] - ;; Add directives to :play-area - assumed to be empty - (swap! state assoc-in [:runner :play-area] directives) - (continue-ability state side - {:prompt (str "Choose 3 starting directives") - :choices {:max 3 - :all true - :req #(and (runner? %) - (in-play-area? %))} - :effect (req (doseq [c targets] - (runner-install state side c {:ignore-all-cost true - :custom-message (str "starts with " (:title c) " in play")})) - (swap! state assoc-in [:runner :play-area] []) - (clear-wait-prompt state :corp))} - card nil)))}}}) - -(define-card "AgInfusion: New Miracles for a New World" - {:abilities [{:once :per-turn - :req (req (and (:run @state) (not (rezzed? current-ice)) (can-rez? state side current-ice {:ignore-unique true}))) - :prompt "Choose another server and redirect the run to its outermost position" - :choices (req (cancellable (remove #{(-> @state :run :server central->name)} servers))) - :msg (msg "trash the approached ICE. The Runner is now running on " target) - :effect (req (let [dest (server->zone state target)] - (trash state side current-ice) - (swap! state update-in [:run] - #(assoc % :position (count (get-in corp (conj dest :ices))) - :server (rest dest)))))}]}) - -(define-card "Akiko Nisei: Head Case" - {:events {:pre-access {:req (req (= target :rd)) - :interactive (req true) - :psi {:player :runner - :equal {:msg "access 1 additional card" - :effect (effect (access-bonus :rd 1) - (effect-completed eid))}}}}}) - -(define-card "Alice Merchant: Clan Agitator" - {:events {:successful-run - {:async true - :interactive (req true) - :req (req (and (= target :archives) - (first-successful-run-on-server? state :archives) - (not-empty (:hand corp)))) - :effect (effect (show-wait-prompt :runner "Corp to trash 1 card from HQ") +(def card-definitions + {"419: Amoral Scammer" + {:events {:corp-install + {:async true + :req (req (and (first-event? state :corp :corp-install) + (pos? (:turn @state)) + (not (rezzed? target)))) + :effect + (req (show-wait-prompt state :corp "Runner to use 419: Amoral Scammer") + (let [itarget target] + (continue-ability + state side + {:optional + {:prompt "Expose installed card unless Corp pays 1 [Credits]?" + :player :runner + :autoresolve (get-autoresolve :auto-419) + :no-ability {:effect (req (clear-wait-prompt state :corp))} + :yes-ability + {:async true + :effect (req (clear-wait-prompt state :corp) + (if (not (can-pay? state :corp eid card nil :credit 1)) + (do + (toast state :corp "Cannot afford to pay 1 credit to block card exposure" "info") + (expose state :runner eid itarget)) + (do + (show-wait-prompt state :runner "Corp decision") + (continue-ability + state side + {:optional + {:prompt "Pay 1 [Credits] to prevent exposure of installed card?" + :player :corp + :no-ability + {:async true + :effect (req (expose state :runner eid itarget) + (clear-wait-prompt state :runner))} + :yes-ability + {:effect (req (pay state :corp card [:credit 1]) + (system-msg state :corp (str "spends 1 [Credits] to prevent " + " card from being exposed")) + (clear-wait-prompt state :runner))}}} + card nil))))}}} + card nil)))}} + :abilities [(set-autoresolve :auto-419 "419")]} + + "Acme Consulting: The Truth You Need" + (letfn [(activate [state card active] + (update! state :corp (assoc-in card [:special :acme-active] active)) + (swap! state update-in [:runner :tag :additional] (if active inc dec)) + (trigger-event state :corp :runner-additional-tag-change (if active 1 -1))) + (outermost? [run-position run-ices] + (and run-position + (pos? run-position) + (= run-position (count run-ices))))] + {:implementation "Additional tag is gained on approach, not on encounter" + ;; Should be comprehensive list of all cases when tag should be gained or lost + :events {:run {:effect (req (when (and (outermost? run-position run-ices) + (rezzed? current-ice)) + (activate state card true)))} + :rez {:effect (req (when (and (outermost? run-position run-ices) + (same-card? current-ice target)) + (activate state card true)))} + :derez {:effect (req (when (outermost? run-position run-ices) + (activate state card false)))} + :pass-ice {:effect (req (when (and (outermost? run-position run-ices) + (get-in card [:special :acme-active])) + (activate state card false)))} + :run-ends {:effect (req (when (get-in card [:special :acme-active]) + (activate state card false)))}}}) + + "Adam: Compulsive Hacker" + {:events {:pre-start-game + {:req (req (= side :runner)) + :async true + :effect (req (show-wait-prompt state :corp "Runner to choose starting directives") + (let [is-directive? #(has-subtype? % "Directive") + directives (filter is-directive? (server-cards)) + directives (map make-card directives) + directives (zone :play-area directives)] + ;; Add directives to :play-area - assumed to be empty + (swap! state assoc-in [:runner :play-area] directives) + (continue-ability state side + {:prompt (str "Choose 3 starting directives") + :choices {:max 3 + :all true + :req #(and (runner? %) + (in-play-area? %))} + :effect (req (doseq [c targets] + (runner-install state side c {:ignore-all-cost true + :custom-message (str "starts with " (:title c) " in play")})) + (swap! state assoc-in [:runner :play-area] []) + (clear-wait-prompt state :corp))} + card nil)))}}} + + "AgInfusion: New Miracles for a New World" + {:abilities [{:once :per-turn + :req (req (and (:run @state) (not (rezzed? current-ice)) (can-rez? state side current-ice {:ignore-unique true}))) + :prompt "Choose another server and redirect the run to its outermost position" + :choices (req (cancellable (remove #{(-> @state :run :server central->name)} servers))) + :msg (msg "trash the approached ICE. The Runner is now running on " target) + :effect (req (let [dest (server->zone state target)] + (trash state side current-ice) + (swap! state update-in [:run] + #(assoc % :position (count (get-in corp (conj dest :ices))) + :server (rest dest)))))}]} + + "Akiko Nisei: Head Case" + {:events {:pre-access {:req (req (= target :rd)) + :interactive (req true) + :psi {:player :runner + :equal {:msg "access 1 additional card" + :effect (effect (access-bonus :rd 1) + (effect-completed eid))}}}}} + + "Alice Merchant: Clan Agitator" + {:events {:successful-run + {:async true + :interactive (req true) + :req (req (and (= target :archives) + (first-successful-run-on-server? state :archives) + (not-empty (:hand corp)))) + :effect (effect (show-wait-prompt :runner "Corp to trash 1 card from HQ") + (continue-ability + {:prompt "Choose a card in HQ to discard" + :player :corp + :choices (req (:hand corp)) + :msg "force the Corp to trash 1 card from HQ" + :effect (effect (trash :corp target) + (clear-wait-prompt :runner))} + card nil))}}} + + "Andromeda: Dispossessed Ristie" + {:events {:pre-start-game {:req (req (= side :runner)) + :effect (effect (draw 4 {:suppress-event true}))}} + :mulligan (effect (draw 4 {:suppress-event true}))} + + "Apex: Invasive Predator" + (let [ability {:prompt "Select a card to install facedown" + :label "Install a card facedown (start of turn)" + :once :per-turn + :choices {:max 1 + :req #(and (runner? %) + (in-hand? %))} + :req (req (and (pos? (count (:hand runner))) + (:runner-phase-12 @state))) + :effect (effect (runner-install eid target {:facedown true}))}] + {:events {:runner-turn-begins ability} + :flags {:runner-phase-12 (req true)} + :abilities [ability]}) + + "Argus Security: Protection Guaranteed" + {:events {:agenda-stolen + {:prompt "Take 1 tag or suffer 2 meat damage?" + :async true + :choices ["1 tag" "2 meat damage"] :player :runner + :msg "make the Runner take 1 tag or suffer 2 meat damage" + :effect (req (if (= target "1 tag") + (do (system-msg state side "chooses to take 1 tag") + (gain-tags state :runner eid 1)) + (do (system-msg state side "chooses to suffer 2 meat damage") + (damage state :runner eid :meat 2 {:unboostable true :card card}))))}}} + + "Armand \"Geist\" Walker: Tech Lord" + {:events {:runner-trash {:req (req (and (= side :runner) (= (second targets) :ability-cost))) + :msg "draw a card" + :async true + :effect (effect (draw eid 1 nil))}}} + + "Asa Group: Security Through Vigilance" + {:events {:corp-install + {:async true + :req (req (first-event? state :corp :corp-install)) + :effect (req (let [installed-card target + z (butlast (:zone installed-card))] (continue-ability - {:prompt "Choose a card in HQ to discard" - :player :corp - :choices (req (:hand corp)) - :msg "force the Corp to trash 1 card from HQ" - :effect (effect (trash :corp target) - (clear-wait-prompt :runner))} - card nil))}}}) - -(define-card "Andromeda: Dispossessed Ristie" - {:events {:pre-start-game {:req (req (= side :runner)) - :effect (effect (draw 4 {:suppress-event true}))}} - :mulligan (effect (draw 4 {:suppress-event true}))}) - -(define-card "Apex: Invasive Predator" - (let [ability {:prompt "Select a card to install facedown" - :label "Install a card facedown (start of turn)" - :once :per-turn - :choices {:max 1 - :req #(and (runner? %) - (in-hand? %))} - :req (req (and (pos? (count (:hand runner))) - (:runner-phase-12 @state))) - :effect (effect (runner-install eid target {:facedown true}))}] - {:events {:runner-turn-begins ability} - :flags {:runner-phase-12 (req true)} - :abilities [ability]})) - -(define-card "Argus Security: Protection Guaranteed" - {:events {:agenda-stolen - {:prompt "Take 1 tag or suffer 2 meat damage?" - :async true - :choices ["1 tag" "2 meat damage"] :player :runner - :msg "make the Runner take 1 tag or suffer 2 meat damage" - :effect (req (if (= target "1 tag") - (do (system-msg state side "chooses to take 1 tag") - (gain-tags state :runner eid 1)) - (do (system-msg state side "chooses to suffer 2 meat damage") - (damage state :runner eid :meat 2 {:unboostable true :card card}))))}}}) - -(define-card "Armand \"Geist\" Walker: Tech Lord" - {:events {:runner-trash {:req (req (and (= side :runner) (= (second targets) :ability-cost))) - :msg "draw a card" + state side + {:prompt (str "Select a " + (if (is-remote? z) + "non-agenda" + "piece of ice") + " in HQ to install with Asa Group: Security Through Vigilance (optional)") + :choices {:req #(and (in-hand? %) + (corp? %) + (corp-installable-type? %) + (not (agenda? %)) + (or (is-remote? z) + (ice? %)))} + :effect (effect (corp-install eid target (zone->name z) nil))} + card nil)))}}} + + "Ayla \"Bios\" Rahim: Simulant Specialist" + {:abilities [{:label "[:click] Add 1 card from NVRAM to your grip" + :cost [:click 1] + :async true + :prompt "Choose a card from NVRAM" + :choices (req (cancellable (:hosted card))) + :msg "move a card from NVRAM to their Grip" + :effect (effect (move target :hand) + (effect-completed eid))}] + :events {:pre-start-game + {:req (req (= side :runner)) + :async true + :effect (req (show-wait-prompt state :corp "the Runner to choose cards for NVRAM") + (doseq [c (take 6 (:deck runner))] + (move state side c :play-area)) + (continue-ability + state side + {:prompt (str "Select 4 cards for NVRAM") + :async true + :choices {:max 4 + :all true + :req #(and (runner? %) + (in-play-area? %))} + :effect (req (doseq [c targets] + (host state side (get-card state card) c {:facedown true})) + (doseq [c (get-in @state [:runner :play-area])] + (move state side c :deck)) + (shuffle! state side :deck) + (clear-wait-prompt state :corp) + (effect-completed state side eid))} + card nil))}}} + + "Azmari EdTech: Shaping the Future" + (let [choose-type {:prompt "Name a Runner card type" + :choices ["Event" "Resource" "Program" "Hardware"] + :effect (effect (update! (assoc card :az-target target)) + (system-msg (str "uses Azmari EdTech: Shaping the Future to name " target)))} + check-type {:req (req (is-type? target (:az-target card))) + :effect (effect (gain-credits :corp 2)) + :once :per-turn + :msg (msg "gain 2 [Credits] from " (:az-target card))}] + {:events {:corp-turn-ends choose-type + :runner-install check-type + :play-event check-type}}) + + "Az McCaffrey: Mechanical Prodigy" + ;; Effect marks Az's ability as "used" if it has already met it's trigger condition this turn + (letfn [(az-type? [card] (or (hardware? card) + (and (resource? card) + (or (has-subtype? card "Job") + (has-subtype? card "Connection"))))) + (not-triggered? [state card] (not (get-in @state [:per-turn (:cid card)]))) + (mark-triggered [state card] (swap! state assoc-in [:per-turn (:cid card)] true))] + {:effect (req (when (pos? (event-count state :runner :runner-install #(az-type? (first %)))) + (mark-triggered state card))) + :events {:pre-install {:req (req (and (az-type? target) + (not-triggered? state card))) + :effect (effect (install-cost-bonus [:credit -1]))} + :runner-install {:req (req (and (az-type? target) + (not-triggered? state card))) + :silent (req true) + :msg (msg "reduce the install cost of " (:title target) " by 1 [Credits]") + :effect (req (mark-triggered state card))}}}) + + "Blue Sun: Powering the Future" + {:flags {:corp-phase-12 (req (and (not (:disabled card)) + (some rezzed? (all-installed state :corp))))} + :abilities [{:choices {:req rezzed?} + :effect (req (trigger-event state side :pre-rez-cost target) + (let [cost (rez-cost state side target)] + (gain-credits state side cost) + (move state side target :hand) + (system-msg state side (str "adds " (:title target) " to HQ and gains " cost " [Credits]")) + (swap! state update-in [:bonus] dissoc :cost :rez)))}]} + + "Boris \"Syfr\" Kovac: Crafty Veteran" + {:events {:pre-start-game {:effect draft-points-target} + :runner-turn-begins {:req (req (and (has-most-faction? state :runner "Criminal") + (pos? (get-in runner [:tag :base])))) + :msg "remove 1 tag" + :effect (effect (lose-tags 1))}}} + + "Cerebral Imaging: Infinite Frontiers" + {:effect (req (when (> (:turn @state) 1) + (swap! state assoc-in [:corp :hand-size :base] (:credit corp))) + (add-watch state :cerebral-imaging + (fn [k ref old new] + (let [credit (get-in new [:corp :credit])] + (when (not= (get-in old [:corp :credit]) credit) + (swap! ref assoc-in [:corp :hand-size :base] credit)))))) + :leave-play (req (remove-watch state :cerebral-imaging) + (swap! state assoc-in [:corp :hand-size :base] 5))} + + "Chaos Theory: Wünderkind" + {:effect (effect (gain :memory 1)) + :leave-play (effect (lose :runner :memory 1))} + + "Chronos Protocol: Selective Mind-mapping" + {:events + {:corp-phase-12 {:effect (effect (enable-corp-damage-choice))} + :runner-phase-12 {:effect (effect (enable-corp-damage-choice))} + :pre-resolve-damage + {:async true + :req (req (and (= target :net) + (corp-can-choose-damage? state) + (pos? (last targets)) + (empty? (filter #(= :net (first %)) (turn-events state :runner :damage))))) + :effect (req (damage-defer state side :net (last targets)) + (if (zero? (count (:hand runner))) + (do (swap! state update-in [:damage] dissoc :damage-choose-corp) + (damage state side eid :net (get-defer-damage state side :net nil) + {:unpreventable true :card card})) + (do (show-wait-prompt state :runner "Corp to use Chronos Protocol: Selective Mind-mapping") + (continue-ability + state side + {:optional + {:prompt (str "Use Chronos Protocol: Selective Mind-mapping to reveal the Runner's " + "Grip to select the first card trashed?") + :priority 10 + :player :corp + :yes-ability {:prompt (msg "Select a card to trash") + :choices (req (:hand runner)) :not-distinct true + :priority 10 + :msg (msg "trash " (:title target) + (when (pos? (dec (or (get-defer-damage state side :net nil) 0))) + (str " and deal " (- (get-defer-damage state side :net nil) 1) + " more net damage"))) + :effect (req (clear-wait-prompt state :runner) + (swap! state update-in [:damage] dissoc :damage-choose-corp) + (trash state side target {:cause :net :unpreventable true}) + (let [more (dec (or (get-defer-damage state side :net nil) 0))] + (damage-defer state side :net more {:part-resolved true})))} + :no-ability {:effect (req (clear-wait-prompt state :runner) + (swap! state update-in [:damage] dissoc :damage-choose-corp))}}} + card nil))))}} + :req (req (empty? (filter #(= :net (first %)) (turn-events state :runner :damage)))) + :effect (effect (enable-corp-damage-choice)) + :leave-play (req (swap! state update-in [:damage] dissoc :damage-choose-corp))} + + "Cybernetics Division: Humanity Upgraded" + {:effect (effect (lose :hand-size 1) + (lose :runner :hand-size 1)) + :leave-play (effect (gain :hand-size 1) + (gain :runner :hand-size 1))} + + "Earth Station: On the Grid" + {:events {:pre-first-turn {:req (req (= side :corp)) + :effect (effect (update! (assoc card :flipped false)))}} + :abilities [{:msg "flip their ID" + :effect (effect (update! (if (:flipped card) + (assoc card + :flipped false + :code (subs (:code card) 0 5)) + (assoc card + :flipped true + :code (str (subs (:code card) 0 5) "flip")))))}]} + + "Edward Kim: Humanity's Hammer" + {:events {:access {:once :per-turn + :req (req (and (operation? target) + (turn-flag? state side card :can-trash-operation))) + :effect (req (trash state side target) + (swap! state assoc-in [:run :did-trash] true) + (swap! state assoc-in [:runner :register :trashed-card] true) + (register-turn-flag! state side card :can-trash-operation (constantly false))) + :msg (msg "trash " (:title target))} + :successful-run-ends {:req (req (and (= (:server target) [:archives]) + (nil? (:replace-access (:run-effect target))) + (not= (:max-access target) 0) + (seq (filter operation? (:discard corp))))) + :effect (effect (register-turn-flag! card :can-trash-operation (constantly false)))}}} + + "Ele \"Smoke\" Scovak: Cynosure of the Net" + {:recurring 1 + :interactions {:pay-credits {:req (req (and (= :ability (:source-type eid)) + (has-subtype? target "Icebreaker"))) + :type :recurring}}} + + "Exile: Streethawk" + {:flags {:runner-install-draw true} + :events {:runner-install {:silent (req (not (and (program? target) + (some #{:discard} (:previous-zone target))))) + :async true + :req (req (and (program? target) + (some #{:discard} (:previous-zone target)))) + :msg (msg "draw a card") + :effect (req (draw state side eid 1 nil))}}} + + "Freedom Khumalo: Crypto-Anarchist" + {:interactions + {:access-ability + {:async true + :once :per-turn + :label "[Freedom]: Trash card" + :req (req (and (not (:disabled card)) + (not (agenda? target)) + (<= (:cost target) + (reduce + (map #(get-counters % :virus) + (all-installed state :runner)))))) + :effect (req (let [accessed-card target + play-or-rez (:cost target)] + (show-wait-prompt state :corp "Runner to use Freedom Khumalo's ability") + (if (zero? play-or-rez) + (continue-ability state side + {:async true + :msg (msg "trash " (:title accessed-card) " at no cost") + :effect (effect (clear-wait-prompt :corp) + (trash-no-cost eid accessed-card))} + card nil) + (wait-for (resolve-ability state side (pick-virus-counters-to-spend play-or-rez) card nil) + (do (clear-wait-prompt state :corp) + (if-let [msg (:msg async-result)] + (do (system-msg state :runner + (str "uses Freedom Khumalo: Crypto-Anarchist to" + " trash " (:title accessed-card) + " at no cost, spending " msg)) + (trash-no-cost state side eid accessed-card)) + ;; Player cancelled ability + (do (swap! state dissoc-in [:per-turn (:cid card)]) + (access-non-agenda state side eid accessed-card :skip-trigger-event true))))))))}}} + + "Fringe Applications: Tomorrow, Today" + {:events + {:pre-start-game {:effect draft-points-target} + :runner-turn-begins {:player :corp + :req (req (and (not (:disabled card)) + (has-most-faction? state :corp "Weyland Consortium") + (some ice? (all-installed state side)))) + :prompt "Select a piece of ICE to place 1 advancement token on" + :choices {:req #(and (installed? %) + (ice? %))} + :msg (msg "place 1 advancement token on " (card-str state target)) + :effect (req (add-prop state :corp target :advance-counter 1 {:placed true}))}}} + + "Gabriel Santiago: Consummate Professional" + {:events {:successful-run {:silent (req true) + :req (req (and (= target :hq) + (first-successful-run-on-server? state :hq))) + :msg "gain 2 [Credits]" + :effect (effect (gain-credits 2))}}} + + "Gagarin Deep Space: Expanding the Horizon" + {:flags {:slow-remote-access (req (not (:disabled card)))} + :events {:pre-access-card {:req (req (is-remote? (second (:zone target)))) + :effect (effect (access-cost-bonus [:credit 1])) + :msg "make the Runner spend 1 [Credits] to access"}}} + + "GRNDL: Power Unleashed" + {:events {:pre-start-game {:req (req (= :corp side)) + :effect (req (gain-credits state :corp 5) + (when (zero? (count-bad-pub state)) + (gain-bad-publicity state :corp 1)))}}} + + "Haarpsichord Studios: Entertainment Unleashed" + (let [haarp (fn [state side card] + (if (agenda? card) + ((constantly false) + (toast state :runner "Cannot steal due to Haarpsichord Studios." "warning")) + true))] + {:events {:agenda-stolen + {:effect (effect (register-turn-flag! card :can-steal haarp))}} + :effect (req (when-not (first-event? state side :agenda-stolen) + (register-turn-flag! state side card :can-steal haarp))) + :leave-play (effect (clear-turn-flag! card :can-steal))}) + + "Haas-Bioroid: Architects of Tomorrow" + {:events {:pass-ice + {:async true + :once :per-turn + :req (req (and (rezzed? target) + (has-subtype? target "Bioroid") + (empty? (filter #(and (rezzed? %) (has-subtype? % "Bioroid")) + (turn-events state side :pass-ice))))) + :effect (effect (show-wait-prompt :runner "Corp to use Haas-Bioroid: Architects of Tomorrow") + (continue-ability + {:prompt "Select a Bioroid to rez" :player :corp + :choices {:req #(and (has-subtype? % "Bioroid") (not (rezzed? %)))} + :msg (msg "rez " (:title target)) + :cancel-effect (effect (clear-wait-prompt :runner) + (effect-completed eid)) + :effect (effect (rez-cost-bonus -4) + (clear-wait-prompt :runner) + (rez eid target nil))} + card nil))}}} + + "Haas-Bioroid: Engineering the Future" + {:events {:corp-install {:req (req (first-event? state corp :corp-install)) + :msg "gain 1 [Credits]" + :effect (effect (gain-credits 1))}}} + + "Haas-Bioroid: Stronger Together" + {:events {:pre-ice-strength {:req (req (and (ice? target) (has-subtype? target "Bioroid"))) + :effect (effect (ice-strength-bonus 1 target))}} + :leave-play (effect (update-all-ice)) + :effect (effect (update-all-ice))} + + "Harishchandra Ent.: Where You're the Star" + {:events {:runner-gain-tag {:effect (req (when (is-tagged? state) + (reveal-hand state :runner)))} + :runner-lose-tag {:effect (req (when-not (is-tagged? state) + (conceal-hand state :runner)))} + ;; Triggered when Paparazzi enters / leaves + :runner-is-tagged {:effect (req (if (is-tagged? state) + (reveal-hand state :runner) + (conceal-hand state :runner)))} + ;; Triggered when gaining or losing additional tag + :runner-additional-tag-change {:effect (req (if (is-tagged? state) + (reveal-hand state :runner) + (conceal-hand state :runner)))}} + :effect (req (when (is-tagged? state) + (reveal-hand state :runner))) + :leave-play (req (when (is-tagged? state) + (conceal-hand state :runner)))} + + "Harmony Medtech: Biomedical Pioneer" + {:effect (effect (lose :agenda-point-req 1) (lose :runner :agenda-point-req 1)) + :leave-play (effect (gain :agenda-point-req 1) (gain :runner :agenda-point-req 1))} + + "Hayley Kaplan: Universal Scholar" + {:events {:runner-install + {:silent (req (not (and (first-event? state side :runner-install) + (some #(is-type? % (:type target)) (:hand runner))))) + :req (req (and (first-event? state side :runner-install) + (some #(is-type? % (:type target)) (:hand runner)))) + :once :per-turn + :async true + :effect + (req (let [itarget target + type (:type itarget)] + (continue-ability + state side + {:optional {:prompt (msg "Install another " type " from your Grip?") + :yes-ability + {:prompt (msg "Select another " type " to install from your Grip") + :choices {:req #(and (is-type? % type) + (in-hand? %))} + :msg (msg "install " (:title target)) + :effect (effect (runner-install eid target nil))}}} + card nil)))}}} + + "Hoshiko Shiro" + {:events {:pre-first-turn {:req (req (= side :runner)) + :effect (effect (update! (assoc card :flipped false)))}} + :abilities [{:msg "flip their ID" + :effect (effect (update! (if (:flipped card) + (assoc card + :flipped false + :code (subs (:code card) 0 5)) + (assoc card + :flipped true + :code (str (subs (:code card) 0 5) "flip")))))}]} + + "Hyoubu Institute: Absolute Clarity" + {:events {:corp-reveal {:once :per-turn + :req (req (first-event? state side :corp-reveal)) + :msg "gain 1 [Credits]" + :effect (effect (gain-credits 1))}} + :abilities [{:cost [:click 1] + :label "Reveal the top card of the Stack" + :effect (req (when-let [revealed-card (-> runner :deck first)] + (system-msg state side (str "uses Hyoubu Institute: Absolute Clarity to reveal " (:title revealed-card))) + (reveal state side revealed-card)))} + {:cost [:click 1] + :label "Reveal a random card from the Grip" + :effect (req (when-let [revealed-card (-> runner :hand shuffle first)] + (system-msg state side (str "uses Hyoubu Institute: Absolute Clarity to reveal " (:title revealed-card))) + (reveal state side revealed-card)))}]} + + "Iain Stirling: Retired Spook" + (let [ability {:req (req (> (:agenda-point corp) (:agenda-point runner))) + :once :per-turn + :msg "gain 2 [Credits]" + :effect (effect (gain-credits 2))}] + {:flags {:drip-economy true} + :events {:runner-turn-begins ability} + :abilities [ability]}) + + "Industrial Genomics: Growing Solutions" + {:events {:pre-trash {:effect (effect (trash-cost-bonus + (count (remove #(:seen %) (:discard corp)))))}}} + + "Information Dynamics: All You Need To Know" + {:events (let [inf {:req (req (and (not (:disabled card)) + (has-most-faction? state :corp "NBN"))) + :msg "give the Runner 1 tag" + :async true + :effect (effect (gain-tags :corp eid 1))}] + {:pre-start-game {:effect draft-points-target} + :agenda-scored inf :agenda-stolen inf})} + + "Jamie \"Bzzz\" Micken: Techno Savant" + {:events {:pre-start-game {:effect draft-points-target} + :pre-install {:req (req (and (has-most-faction? state :runner "Shaper") + (pos? (count (:deck runner))) + (first-event? state side :pre-install))) + :msg "draw 1 card" + :once :per-turn :async true - :effect (effect (draw eid 1 nil))}}}) - -(define-card "Asa Group: Security Through Vigilance" - {:events {:corp-install - {:async true - :req (req (first-event? state :corp :corp-install)) - :effect (req (let [installed-card target - z (butlast (:zone installed-card))] - (continue-ability - state side - {:prompt (str "Select a " - (if (is-remote? z) - "non-agenda" - "piece of ice") - " in HQ to install with Asa Group: Security Through Vigilance (optional)") - :choices {:req #(and (in-hand? %) - (corp? %) - (corp-installable-type? %) - (not (agenda? %)) - (or (is-remote? z) - (ice? %)))} - :effect (effect (corp-install eid target (zone->name z) nil))} - card nil)))}}}) - -(define-card "Ayla \"Bios\" Rahim: Simulant Specialist" - {:abilities [{:label "[:click] Add 1 card from NVRAM to your grip" - :cost [:click 1] - :async true - :prompt "Choose a card from NVRAM" - :choices (req (cancellable (:hosted card))) - :msg "move a card from NVRAM to their Grip" - :effect (effect (move target :hand) - (effect-completed eid))}] - :events {:pre-start-game - {:req (req (= side :runner)) - :async true - :effect (req (show-wait-prompt state :corp "the Runner to choose cards for NVRAM") - (doseq [c (take 6 (:deck runner))] - (move state side c :play-area)) - (continue-ability - state side - {:prompt (str "Select 4 cards for NVRAM") + :effect (effect (draw eid 1 nil))}}} + + "Jemison Astronautics: Sacrifice. Audacity. Success." + {:events {:corp-forfeit-agenda + {:async true + :effect (req (show-wait-prompt state :runner "Corp to place advancement tokens") + (let [p (inc (get-agenda-points state :corp target))] + (continue-ability + state side + {:prompt "Select a card to place advancement tokens on with Jemison Astronautics: Sacrifice. Audacity. Success." + :choices {:req #(and (installed? %) (corp? %))} + :msg (msg "place " p " advancement tokens on " (card-str state target)) + :cancel-effect (effect (clear-wait-prompt :runner)) + :effect (effect (add-prop :corp target :advance-counter p {:placed true}) + (clear-wait-prompt :runner))} + card nil)))}}} + + "Jesminder Sareen: Girl Behind the Curtain" + {:events {:pre-tag {:once :per-run + :req (req (:run @state)) + :msg "avoid the first tag during this run" + :effect (effect (tag-prevent :runner 1))}}} + + "Jinteki Biotech: Life Imagined" + {:events {:pre-first-turn {:req (req (= side :corp)) + :prompt "Choose a copy of Jinteki Biotech to use this game" + :choices ["The Brewery" "The Tank" "The Greenhouse"] + :effect (effect (update! (assoc card :biotech-target target)) + (system-msg (str "has chosen a copy of Jinteki Biotech for this game")))}} + :abilities [{:label "Check chosen flip identity" + :req (req (:biotech-target card)) + :effect (req (case (:biotech-target card) + "The Brewery" + (toast state :corp "Flip to: The Brewery (Do 2 net damage)" "info") + "The Tank" + (toast state :corp "Flip to: The Tank (Shuffle Archives into R&D)" "info") + "The Greenhouse" + (toast state :corp "Flip to: The Greenhouse (Place 4 advancement tokens on a card)" "info")))} + {:cost [:click 3] + :req (req (not (:biotech-used card))) + :label "Flip this identity" + :effect (req (let [flip (:biotech-target card)] + (case flip + "The Brewery" + (do (system-msg state side "uses The Brewery to do 2 net damage") + (damage state side eid :net 2 {:card card}) + (update! state side (assoc card :code "brewery"))) + "The Tank" + (do (system-msg state side "uses The Tank to shuffle Archives into R&D") + (shuffle-into-deck state side :discard) + (update! state side (assoc card :code "tank"))) + "The Greenhouse" + (do (system-msg state side (str "uses The Greenhouse to place 4 advancement tokens " + "on a card that can be advanced")) + (update! state side (assoc card :code "greenhouse")) + (resolve-ability + state side + {:prompt "Select a card that can be advanced" + :choices {:req can-be-advanced?} + :effect (effect (add-prop target :advance-counter 4 {:placed true}))} card nil))) + (update! state side (assoc (get-card state card) :biotech-used true))))}]} + + "Jinteki: Personal Evolution" + {:events {:agenda-scored {:interactive (req true) :async true - :choices {:max 4 - :all true - :req #(and (runner? %) - (in-play-area? %))} - :effect (req (doseq [c targets] - (host state side (get-card state card) c {:facedown true})) - (doseq [c (get-in @state [:runner :play-area])] - (move state side c :deck)) - (shuffle! state side :deck) - (clear-wait-prompt state :corp) - (effect-completed state side eid))} - card nil))}}}) - -(define-card "Azmari EdTech: Shaping the Future" - (let [choose-type {:prompt "Name a Runner card type" - :choices ["Event" "Resource" "Program" "Hardware"] - :effect (effect (update! (assoc card :az-target target)) - (system-msg (str "uses Azmari EdTech: Shaping the Future to name " target)))} - check-type {:req (req (is-type? target (:az-target card))) - :effect (effect (gain-credits :corp 2)) - :once :per-turn - :msg (msg "gain 2 [Credits] from " (:az-target card))}] - {:events {:corp-turn-ends choose-type - :runner-install check-type - :play-event check-type}})) - -(define-card "Az McCaffrey: Mechanical Prodigy" - ;; Effect marks Az's ability as "used" if it has already met it's trigger condition this turn - (letfn [(az-type? [card] (or (hardware? card) - (and (resource? card) - (or (has-subtype? card "Job") - (has-subtype? card "Connection"))))) - (not-triggered? [state card] (not (get-in @state [:per-turn (:cid card)]))) - (mark-triggered [state card] (swap! state assoc-in [:per-turn (:cid card)] true))] - {:effect (req (when (pos? (event-count state :runner :runner-install #(az-type? (first %)))) - (mark-triggered state card))) - :events {:pre-install {:req (req (and (az-type? target) - (not-triggered? state card))) - :effect (effect (install-cost-bonus [:credit -1]))} - :runner-install {:req (req (and (az-type? target) - (not-triggered? state card))) - :silent (req true) - :msg (msg "reduce the install cost of " (:title target) " by 1 [Credits]") - :effect (req (mark-triggered state card))}}})) - -(define-card "Blue Sun: Powering the Future" - {:flags {:corp-phase-12 (req (and (not (:disabled card)) - (some rezzed? (all-installed state :corp))))} - :abilities [{:choices {:req rezzed?} - :effect (req (trigger-event state side :pre-rez-cost target) - (let [cost (rez-cost state side target)] - (gain-credits state side cost) - (move state side target :hand) - (system-msg state side (str "adds " (:title target) " to HQ and gains " cost " [Credits]")) - (swap! state update-in [:bonus] dissoc :cost :rez)))}]}) - -(define-card "Boris \"Syfr\" Kovac: Crafty Veteran" - {:events {:pre-start-game {:effect draft-points-target} - :runner-turn-begins {:req (req (and (has-most-faction? state :runner "Criminal") - (pos? (get-in runner [:tag :base])))) - :msg "remove 1 tag" - :effect (effect (lose-tags 1))}}}) - -(define-card "Cerebral Imaging: Infinite Frontiers" - {:effect (req (when (> (:turn @state) 1) - (swap! state assoc-in [:corp :hand-size :base] (:credit corp))) - (add-watch state :cerebral-imaging - (fn [k ref old new] - (let [credit (get-in new [:corp :credit])] - (when (not= (get-in old [:corp :credit]) credit) - (swap! ref assoc-in [:corp :hand-size :base] credit)))))) - :leave-play (req (remove-watch state :cerebral-imaging) - (swap! state assoc-in [:corp :hand-size :base] 5))}) - -(define-card "Chaos Theory: Wünderkind" - {:effect (effect (gain :memory 1)) - :leave-play (effect (lose :runner :memory 1))}) - -(define-card "Chronos Protocol: Selective Mind-mapping" - {:events - {:corp-phase-12 {:effect (effect (enable-corp-damage-choice))} - :runner-phase-12 {:effect (effect (enable-corp-damage-choice))} - :pre-resolve-damage - {:async true - :req (req (and (= target :net) - (corp-can-choose-damage? state) - (pos? (last targets)) - (empty? (filter #(= :net (first %)) (turn-events state :runner :damage))))) - :effect (req (damage-defer state side :net (last targets)) - (if (zero? (count (:hand runner))) - (do (swap! state update-in [:damage] dissoc :damage-choose-corp) - (damage state side eid :net (get-defer-damage state side :net nil) - {:unpreventable true :card card})) - (do (show-wait-prompt state :runner "Corp to use Chronos Protocol: Selective Mind-mapping") - (continue-ability - state side - {:optional - {:prompt (str "Use Chronos Protocol: Selective Mind-mapping to reveal the Runner's " - "Grip to select the first card trashed?") - :priority 10 - :player :corp - :yes-ability {:prompt (msg "Select a card to trash") - :choices (req (:hand runner)) :not-distinct true - :priority 10 - :msg (msg "trash " (:title target) - (when (pos? (dec (or (get-defer-damage state side :net nil) 0))) - (str " and deal " (- (get-defer-damage state side :net nil) 1) - " more net damage"))) - :effect (req (clear-wait-prompt state :runner) - (swap! state update-in [:damage] dissoc :damage-choose-corp) - (trash state side target {:cause :net :unpreventable true}) - (let [more (dec (or (get-defer-damage state side :net nil) 0))] - (damage-defer state side :net more {:part-resolved true})))} - :no-ability {:effect (req (clear-wait-prompt state :runner) - (swap! state update-in [:damage] dissoc :damage-choose-corp))}}} - card nil))))}} - :req (req (empty? (filter #(= :net (first %)) (turn-events state :runner :damage)))) - :effect (effect (enable-corp-damage-choice)) - :leave-play (req (swap! state update-in [:damage] dissoc :damage-choose-corp))}) - -(define-card "Cybernetics Division: Humanity Upgraded" - {:effect (effect (lose :hand-size 1) - (lose :runner :hand-size 1)) - :leave-play (effect (gain :hand-size 1) - (gain :runner :hand-size 1))}) - -(define-card "Earth Station: On the Grid" - {:events {:pre-first-turn {:req (req (= side :corp)) - :effect (effect (update! (assoc card :flipped false)))}} - :abilities [{:msg "flip their ID" - :effect (effect (update! (if (:flipped card) - (assoc card - :flipped false - :code (subs (:code card) 0 5)) - (assoc card - :flipped true - :code (str (subs (:code card) 0 5) "flip")))))}]}) - -(define-card "Edward Kim: Humanity's Hammer" - {:events {:access {:once :per-turn - :req (req (and (operation? target) - (turn-flag? state side card :can-trash-operation))) - :effect (req (trash state side target) - (swap! state assoc-in [:run :did-trash] true) - (swap! state assoc-in [:runner :register :trashed-card] true) - (register-turn-flag! state side card :can-trash-operation (constantly false))) - :msg (msg "trash " (:title target))} - :successful-run-ends {:req (req (and (= (:server target) [:archives]) - (nil? (:replace-access (:run-effect target))) - (not= (:max-access target) 0) - (seq (filter operation? (:discard corp))))) - :effect (effect (register-turn-flag! card :can-trash-operation (constantly false)))}}}) - -(define-card "Ele \"Smoke\" Scovak: Cynosure of the Net" - {:recurring 1 - :interactions {:pay-credits {:req (req (and (= :ability (:source-type eid)) - (has-subtype? target "Icebreaker"))) - :type :recurring}}}) - -(define-card "Exile: Streethawk" - {:flags {:runner-install-draw true} - :events {:runner-install {:silent (req (not (and (program? target) - (some #{:discard} (:previous-zone target))))) + :req (req (not (:winner @state))) + :msg "do 1 net damage" + :effect (effect (damage eid :net 1 {:card card}))} + :agenda-stolen {:msg "do 1 net damage" :async true - :req (req (and (program? target) - (some #{:discard} (:previous-zone target)))) - :msg (msg "draw a card") - :effect (req (draw state side eid 1 nil))}}}) - -(define-card "Freedom Khumalo: Crypto-Anarchist" - {:interactions - {:access-ability - {:async true - :once :per-turn - :label "[Freedom]: Trash card" - :req (req (and (not (:disabled card)) - (not (agenda? target)) - (<= (:cost target) - (reduce + (map #(get-counters % :virus) - (all-installed state :runner)))))) - :effect (req (let [accessed-card target - play-or-rez (:cost target)] - (show-wait-prompt state :corp "Runner to use Freedom Khumalo's ability") - (if (zero? play-or-rez) - (continue-ability state side - {:async true - :msg (msg "trash " (:title accessed-card) " at no cost") - :effect (effect (clear-wait-prompt :corp) - (trash-no-cost eid accessed-card))} - card nil) - (wait-for (resolve-ability state side (pick-virus-counters-to-spend play-or-rez) card nil) - (do (clear-wait-prompt state :corp) - (if-let [msg (:msg async-result)] - (do (system-msg state :runner - (str "uses Freedom Khumalo: Crypto-Anarchist to" - " trash " (:title accessed-card) - " at no cost, spending " msg)) - (trash-no-cost state side eid accessed-card)) - ;; Player cancelled ability - (do (swap! state dissoc-in [:per-turn (:cid card)]) - (access-non-agenda state side eid accessed-card :skip-trigger-event true))))))))}}}) - -(define-card "Fringe Applications: Tomorrow, Today" - {:events - {:pre-start-game {:effect draft-points-target} - :runner-turn-begins {:player :corp - :req (req (and (not (:disabled card)) - (has-most-faction? state :corp "Weyland Consortium") - (some ice? (all-installed state side)))) - :prompt "Select a piece of ICE to place 1 advancement token on" - :choices {:req #(and (installed? %) - (ice? %))} - :msg (msg "place 1 advancement token on " (card-str state target)) - :effect (req (add-prop state :corp target :advance-counter 1 {:placed true}))}}}) - -(define-card "Gabriel Santiago: Consummate Professional" - {:events {:successful-run {:silent (req true) - :req (req (and (= target :hq) - (first-successful-run-on-server? state :hq))) - :msg "gain 2 [Credits]" - :effect (effect (gain-credits 2))}}}) - -(define-card "Gagarin Deep Space: Expanding the Horizon" - {:flags {:slow-remote-access (req (not (:disabled card)))} - :events {:pre-access-card {:req (req (is-remote? (second (:zone target)))) - :effect (effect (access-cost-bonus [:credit 1])) - :msg "make the Runner spend 1 [Credits] to access"}}}) - -(define-card "GRNDL: Power Unleashed" - {:events {:pre-start-game {:req (req (= :corp side)) - :effect (req (gain-credits state :corp 5) - (when (zero? (count-bad-pub state)) - (gain-bad-publicity state :corp 1)))}}}) - -(define-card "Haarpsichord Studios: Entertainment Unleashed" - (let [haarp (fn [state side card] - (if (agenda? card) - ((constantly false) - (toast state :runner "Cannot steal due to Haarpsichord Studios." "warning")) - true))] - {:events {:agenda-stolen - {:effect (effect (register-turn-flag! card :can-steal haarp))}} - :effect (req (when-not (first-event? state side :agenda-stolen) - (register-turn-flag! state side card :can-steal haarp))) - :leave-play (effect (clear-turn-flag! card :can-steal))})) - -(define-card "Haas-Bioroid: Architects of Tomorrow" - {:events {:pass-ice - {:async true - :once :per-turn - :req (req (and (rezzed? target) - (has-subtype? target "Bioroid") - (empty? (filter #(and (rezzed? %) (has-subtype? % "Bioroid")) - (turn-events state side :pass-ice))))) - :effect (effect (show-wait-prompt :runner "Corp to use Haas-Bioroid: Architects of Tomorrow") - (continue-ability - {:prompt "Select a Bioroid to rez" :player :corp - :choices {:req #(and (has-subtype? % "Bioroid") (not (rezzed? %)))} - :msg (msg "rez " (:title target)) - :cancel-effect (effect (clear-wait-prompt :runner) - (effect-completed eid)) - :effect (effect (rez-cost-bonus -4) - (clear-wait-prompt :runner) - (rez eid target nil))} - card nil))}}}) - -(define-card "Haas-Bioroid: Engineering the Future" - {:events {:corp-install {:req (req (first-event? state corp :corp-install)) - :msg "gain 1 [Credits]" - :effect (effect (gain-credits 1))}}}) - -(define-card "Haas-Bioroid: Stronger Together" - {:events {:pre-ice-strength {:req (req (and (ice? target) (has-subtype? target "Bioroid"))) - :effect (effect (ice-strength-bonus 1 target))}} - :leave-play (effect (update-all-ice)) - :effect (effect (update-all-ice))}) - -(define-card "Harishchandra Ent.: Where You're the Star" - {:events {:runner-gain-tag {:effect (req (when (is-tagged? state) - (reveal-hand state :runner)))} - :runner-lose-tag {:effect (req (when-not (is-tagged? state) - (conceal-hand state :runner)))} - ;; Triggered when Paparazzi enters / leaves - :runner-is-tagged {:effect (req (if (is-tagged? state) - (reveal-hand state :runner) - (conceal-hand state :runner)))} - ;; Triggered when gaining or losing additional tag - :runner-additional-tag-change {:effect (req (if (is-tagged? state) - (reveal-hand state :runner) - (conceal-hand state :runner)))}} - :effect (req (when (is-tagged? state) - (reveal-hand state :runner))) - :leave-play (req (when (is-tagged? state) - (conceal-hand state :runner)))}) - -(define-card "Harmony Medtech: Biomedical Pioneer" - {:effect (effect (lose :agenda-point-req 1) (lose :runner :agenda-point-req 1)) - :leave-play (effect (gain :agenda-point-req 1) (gain :runner :agenda-point-req 1))}) - -(define-card "Hayley Kaplan: Universal Scholar" - {:events {:runner-install - {:silent (req (not (and (first-event? state side :runner-install) - (some #(is-type? % (:type target)) (:hand runner))))) - :req (req (and (first-event? state side :runner-install) - (some #(is-type? % (:type target)) (:hand runner)))) - :once :per-turn - :async true - :effect - (req (let [itarget target - type (:type itarget)] - (continue-ability - state side - {:optional {:prompt (msg "Install another " type " from your Grip?") - :yes-ability - {:prompt (msg "Select another " type " to install from your Grip") - :choices {:req #(and (is-type? % type) - (in-hand? %))} - :msg (msg "install " (:title target)) - :effect (effect (runner-install eid target nil))}}} - card nil)))}}}) - -(define-card "Hoshiko Shiro" - {:events {:pre-first-turn {:req (req (= side :runner)) - :effect (effect (update! (assoc card :flipped false)))}} - :abilities [{:msg "flip their ID" - :effect (effect (update! (if (:flipped card) - (assoc card - :flipped false - :code (subs (:code card) 0 5)) - (assoc card - :flipped true - :code (str (subs (:code card) 0 5) "flip")))))}]}) - -(define-card "Hyoubu Institute: Absolute Clarity" - {:events {:corp-reveal {:once :per-turn - :req (req (first-event? state side :corp-reveal)) + :req (req (not (:winner @state))) + :effect (effect (damage eid :net 1 {:card card}))}}} + + "Jinteki: Potential Unleashed" + {:events {:pre-resolve-damage + {:req (req (and (-> @state :corp :disable-id not) (= target :net) (pos? (last targets)))) + :effect (req (let [c (first (get-in @state [:runner :deck]))] + (system-msg state :corp (str "uses Jinteki: Potential Unleashed to trash " (:title c) + " from the top of the Runner's Stack")) + (mill state :corp :runner 1)))}}} + + "Jinteki: Replicating Perfection" + {:events + {:runner-phase-12 {:effect (req (apply prevent-run-on-server + state card (map first (get-remotes state))))} + :run {:once :per-turn + :req (req (is-central? (:server run))) + :effect (req (apply enable-run-on-server + state card (map first (get-remotes state))))}} + :req (req (empty? (let [successes (turn-events state side :successful-run)] + (filter #(is-central? %) successes)))) + :effect (req (apply prevent-run-on-server state card (map first (get-remotes state)))) + :leave-play (req (apply enable-run-on-server state card (map first (get-remotes state))))} + + "Kabonesa Wu: Netspace Thrillseeker" + (let [rfg-ability {:req (req (seq (filter #(get-in % [:special :kabonesa]) (all-installed state :runner)))) + :effect (req (doseq [program (filter #(get-in % [:special :kabonesa]) (all-installed state :runner))] + (move state side program :rfg) + (system-msg state side (str "remove " (:title program) " from the game"))))}] + {:events {:corp-turn-ends rfg-ability + :runner-turn-ends rfg-ability} + :abilities [{:label "Install a non-virus program from your stack, lowering the cost by 1 [Credit]" + :cost [:click 1] + :prompt "Choose a program" + :choices (req (cancellable + (filter #(and (program? %) + (not (has-subtype? % "Virus"))) + (:deck runner)))) + :msg (msg "install " (:title target) " from their stack, lowering the cost by 1 [Credit]") + :effect (effect (trigger-event :searched-stack nil) + (shuffle! :deck) + (install-cost-bonus [:credit -1]) + (runner-install eid (assoc-in target [:special :kabonesa] true) nil))}]}) + + "Kate \"Mac\" McCaffrey: Digital Tinker" + ;; Effect marks Kate's ability as "used" if it has already met it's trigger condition this turn + (letfn [(kate-type? [card] (or (hardware? card) + (program? card))) + (not-triggered? [state card] (not (get-in @state [:per-turn (:cid card)]))) + (mark-triggered [state card] (swap! state assoc-in [:per-turn (:cid card)] true))] + {:effect (req (when (pos? (event-count state :runner :runner-install #(kate-type? (first %)))) + (mark-triggered state card))) + :events {:pre-install {:req (req (and (kate-type? target) + (not-triggered? state card))) + :effect (effect (install-cost-bonus [:credit -1]))} + :runner-install {:req (req (and (kate-type? target) + (not-triggered? state card))) + :silent (req true) + :msg (msg "reduce the install cost of " (:title target) " by 1 [Credits]") + :effect (req (mark-triggered state card))}}}) + + "Ken \"Express\" Tenma: Disappeared Clone" + {:events {:play-event {:req (req (and (has-subtype? target "Run") + (first-event? state :runner :play-event #(has-subtype? (first %) "Run")))) :msg "gain 1 [Credits]" - :effect (effect (gain-credits 1))}} - :abilities [{:cost [:click 1] - :label "Reveal the top card of the Stack" - :effect (req (when-let [revealed-card (-> runner :deck first)] - (system-msg state side (str "uses Hyoubu Institute: Absolute Clarity to reveal " (:title revealed-card))) - (reveal state side revealed-card)))} - {:cost [:click 1] - :label "Reveal a random card from the Grip" - :effect (req (when-let [revealed-card (-> runner :hand shuffle first)] - (system-msg state side (str "uses Hyoubu Institute: Absolute Clarity to reveal " (:title revealed-card))) - (reveal state side revealed-card)))}]}) - -(define-card "Iain Stirling: Retired Spook" - (let [ability {:req (req (> (:agenda-point corp) (:agenda-point runner))) - :once :per-turn - :msg "gain 2 [Credits]" - :effect (effect (gain-credits 2))}] - {:flags {:drip-economy true} - :events {:runner-turn-begins ability} - :abilities [ability]})) - -(define-card "Industrial Genomics: Growing Solutions" - {:events {:pre-trash {:effect (effect (trash-cost-bonus - (count (remove #(:seen %) (:discard corp)))))}}}) - -(define-card "Information Dynamics: All You Need To Know" - {:events (let [inf {:req (req (and (not (:disabled card)) - (has-most-faction? state :corp "NBN"))) - :msg "give the Runner 1 tag" - :async true - :effect (effect (gain-tags :corp eid 1))}] - {:pre-start-game {:effect draft-points-target} - :agenda-scored inf :agenda-stolen inf})}) - -(define-card "Jamie \"Bzzz\" Micken: Techno Savant" - {:events {:pre-start-game {:effect draft-points-target} - :pre-install {:req (req (and (has-most-faction? state :runner "Shaper") - (pos? (count (:deck runner))) - (first-event? state side :pre-install))) - :msg "draw 1 card" - :once :per-turn - :async true - :effect (effect (draw eid 1 nil))}}}) - -(define-card "Jemison Astronautics: Sacrifice. Audacity. Success." - {:events {:corp-forfeit-agenda - {:async true - :effect (req (show-wait-prompt state :runner "Corp to place advancement tokens") - (let [p (inc (get-agenda-points state :corp target))] - (continue-ability - state side - {:prompt "Select a card to place advancement tokens on with Jemison Astronautics: Sacrifice. Audacity. Success." - :choices {:req #(and (installed? %) (corp? %))} - :msg (msg "place " p " advancement tokens on " (card-str state target)) - :cancel-effect (effect (clear-wait-prompt :runner)) - :effect (effect (add-prop :corp target :advance-counter p {:placed true}) - (clear-wait-prompt :runner))} - card nil)))}}}) - -(define-card "Jesminder Sareen: Girl Behind the Curtain" - {:events {:pre-tag {:once :per-run - :req (req (:run @state)) - :msg "avoid the first tag during this run" - :effect (effect (tag-prevent :runner 1))}}}) - -(define-card "Jinteki Biotech: Life Imagined" - {:events {:pre-first-turn {:req (req (= side :corp)) - :prompt "Choose a copy of Jinteki Biotech to use this game" - :choices ["The Brewery" "The Tank" "The Greenhouse"] - :effect (effect (update! (assoc card :biotech-target target)) - (system-msg (str "has chosen a copy of Jinteki Biotech for this game")))}} - :abilities [{:label "Check chosen flip identity" - :req (req (:biotech-target card)) - :effect (req (case (:biotech-target card) - "The Brewery" - (toast state :corp "Flip to: The Brewery (Do 2 net damage)" "info") - "The Tank" - (toast state :corp "Flip to: The Tank (Shuffle Archives into R&D)" "info") - "The Greenhouse" - (toast state :corp "Flip to: The Greenhouse (Place 4 advancement tokens on a card)" "info")))} - {:cost [:click 3] - :req (req (not (:biotech-used card))) - :label "Flip this identity" - :effect (req (let [flip (:biotech-target card)] - (case flip - "The Brewery" - (do (system-msg state side "uses The Brewery to do 2 net damage") - (damage state side eid :net 2 {:card card}) - (update! state side (assoc card :code "brewery"))) - "The Tank" - (do (system-msg state side "uses The Tank to shuffle Archives into R&D") - (shuffle-into-deck state side :discard) - (update! state side (assoc card :code "tank"))) - "The Greenhouse" - (do (system-msg state side (str "uses The Greenhouse to place 4 advancement tokens " - "on a card that can be advanced")) - (update! state side (assoc card :code "greenhouse")) - (resolve-ability - state side - {:prompt "Select a card that can be advanced" - :choices {:req can-be-advanced?} - :effect (effect (add-prop target :advance-counter 4 {:placed true}))} card nil))) - (update! state side (assoc (get-card state card) :biotech-used true))))}]}) - -(define-card "Jinteki: Personal Evolution" - {:events {:agenda-scored {:interactive (req true) - :async true - :req (req (not (:winner @state))) - :msg "do 1 net damage" - :effect (effect (damage eid :net 1 {:card card}))} - :agenda-stolen {:msg "do 1 net damage" - :async true - :req (req (not (:winner @state))) - :effect (effect (damage eid :net 1 {:card card}))}}}) - -(define-card "Jinteki: Potential Unleashed" - {:events {:pre-resolve-damage - {:req (req (and (-> @state :corp :disable-id not) (= target :net) (pos? (last targets)))) - :effect (req (let [c (first (get-in @state [:runner :deck]))] - (system-msg state :corp (str "uses Jinteki: Potential Unleashed to trash " (:title c) - " from the top of the Runner's Stack")) - (mill state :corp :runner 1)))}}}) - -(define-card "Jinteki: Replicating Perfection" - {:events - {:runner-phase-12 {:effect (req (apply prevent-run-on-server - state card (map first (get-remotes state))))} - :run {:once :per-turn - :req (req (is-central? (:server run))) - :effect (req (apply enable-run-on-server - state card (map first (get-remotes state))))}} - :req (req (empty? (let [successes (turn-events state side :successful-run)] - (filter #(is-central? %) successes)))) - :effect (req (apply prevent-run-on-server state card (map first (get-remotes state)))) - :leave-play (req (apply enable-run-on-server state card (map first (get-remotes state))))}) - -(define-card "Kabonesa Wu: Netspace Thrillseeker" - (let [rfg-ability {:req (req (seq (filter #(get-in % [:special :kabonesa]) (all-installed state :runner)))) - :effect (req (doseq [program (filter #(get-in % [:special :kabonesa]) (all-installed state :runner))] - (move state side program :rfg) - (system-msg state side (str "remove " (:title program) " from the game"))))}] - {:events {:corp-turn-ends rfg-ability - :runner-turn-ends rfg-ability} - :abilities [{:label "Install a non-virus program from your stack, lowering the cost by 1 [Credit]" - :cost [:click 1] - :prompt "Choose a program" - :choices (req (cancellable - (filter #(and (program? %) - (not (has-subtype? % "Virus"))) - (:deck runner)))) - :msg (msg "install " (:title target) " from their stack, lowering the cost by 1 [Credit]") - :effect (effect (trigger-event :searched-stack nil) - (shuffle! :deck) - (install-cost-bonus [:credit -1]) - (runner-install eid (assoc-in target [:special :kabonesa] true) nil))}]})) - -(define-card "Kate \"Mac\" McCaffrey: Digital Tinker" - ;; Effect marks Kate's ability as "used" if it has already met it's trigger condition this turn - (letfn [(kate-type? [card] (or (hardware? card) - (program? card))) - (not-triggered? [state card] (not (get-in @state [:per-turn (:cid card)]))) - (mark-triggered [state card] (swap! state assoc-in [:per-turn (:cid card)] true))] - {:effect (req (when (pos? (event-count state :runner :runner-install #(kate-type? (first %)))) - (mark-triggered state card))) - :events {:pre-install {:req (req (and (kate-type? target) - (not-triggered? state card))) - :effect (effect (install-cost-bonus [:credit -1]))} - :runner-install {:req (req (and (kate-type? target) - (not-triggered? state card))) - :silent (req true) - :msg (msg "reduce the install cost of " (:title target) " by 1 [Credits]") - :effect (req (mark-triggered state card))}}})) - -(define-card "Ken \"Express\" Tenma: Disappeared Clone" - {:events {:play-event {:req (req (and (has-subtype? target "Run") - (first-event? state :runner :play-event #(has-subtype? (first %) "Run")))) - :msg "gain 1 [Credits]" - :effect (effect (gain-credits 1))}}}) - -(define-card "Khan: Savvy Skiptracer" - {:events {:pass-ice - {:req (req (first-event? state :corp :pass-ice)) - :async true - :effect (req (if (some #(has-subtype? % "Icebreaker") (:hand runner)) - (continue-ability state side - {:prompt "Select an icebreaker to install from your Grip" - :choices {:req #(and (in-hand? %) (has-subtype? % "Icebreaker"))} - :async true - :msg (msg "install " (:title target)) - :effect (effect (install-cost-bonus [:credit -1]) - (runner-install eid target nil))} - card nil) - (effect-completed state side eid)))}}}) - -(define-card "Laramy Fisk: Savvy Investor" - {:events - {:successful-run - {:async true - :interactive (get-autoresolve :auto-fisk (complement never?)) - :silent (get-autoresolve :auto-fisk never?) - :req (req (and (is-central? (:server run)) - (first-event? state side :successful-run is-central?))) - :effect (effect (continue-ability - {:optional - {:autoresolve (get-autoresolve :auto-fisk) - :prompt "Force the Corp to draw a card?" - :yes-ability {:msg "force the Corp to draw 1 card" - :async true - :effect (effect (draw :corp eid 1 nil))} - :no-ability {:effect (effect (system-msg "declines to use Laramy Fisk: Savvy Investor"))}}} - card nil))}} - :abilities [(set-autoresolve :auto-fisk "force Corp draw")]}) - -(define-card "Lat: Ethical Freelancer" - {:events - {:runner-turn-ends - {:req (req (= (count (:hand runner)) (count (:hand corp)))) - :optional {:autoresolve (get-autoresolve :auto-lat) - :prompt "Draw 1 card?" - :yes-ability {:async true + :effect (effect (gain-credits 1))}}} + + "Khan: Savvy Skiptracer" + {:events {:pass-ice + {:req (req (first-event? state :corp :pass-ice)) + :async true + :effect (req (if (some #(has-subtype? % "Icebreaker") (:hand runner)) + (continue-ability state side + {:prompt "Select an icebreaker to install from your Grip" + :choices {:req #(and (in-hand? %) (has-subtype? % "Icebreaker"))} + :async true + :msg (msg "install " (:title target)) + :effect (effect (install-cost-bonus [:credit -1]) + (runner-install eid target nil))} + card nil) + (effect-completed state side eid)))}}} + + "Laramy Fisk: Savvy Investor" + {:events + {:successful-run + {:async true + :interactive (get-autoresolve :auto-fisk (complement never?)) + :silent (get-autoresolve :auto-fisk never?) + :req (req (and (is-central? (:server run)) + (first-event? state side :successful-run is-central?))) + :effect (effect (continue-ability + {:optional + {:autoresolve (get-autoresolve :auto-fisk) + :prompt "Force the Corp to draw a card?" + :yes-ability {:msg "force the Corp to draw 1 card" + :async true + :effect (effect (draw :corp eid 1 nil))} + :no-ability {:effect (effect (system-msg "declines to use Laramy Fisk: Savvy Investor"))}}} + card nil))}} + :abilities [(set-autoresolve :auto-fisk "force Corp draw")]} + + "Lat: Ethical Freelancer" + {:events + {:runner-turn-ends + {:req (req (= (count (:hand runner)) (count (:hand corp)))) + :optional {:autoresolve (get-autoresolve :auto-lat) + :prompt "Draw 1 card?" + :yes-ability {:async true + :msg "draw 1 card" + :effect (effect (draw :runner eid 1 nil))} + :no-ability {:effect (effect (system-msg "declines to use Lat: Ethical Freelancer"))}}}} + :abilities [(set-autoresolve :auto-lat "Lat: Ethical Freelancer")]} + + "Leela Patel: Trained Pragmatist" + (let [leela {:interactive (req true) + :prompt "Select an unrezzed card to return to HQ" + :choices {:req #(and (not (rezzed? %)) (installed? %) (corp? %))} + :msg (msg "add " (card-str state target) " to HQ") + :effect (effect (move :corp target :hand))}] + {:flags {:slow-hq-access (req true)} + :events {:agenda-scored leela + :agenda-stolen leela}}) + + "Liza Talking Thunder: Prominent Legislator" + {:implementation "Needs to be resolved manually with Crisium Grid" + :events + {:successful-run + {:async true + :interactive (req true) + :msg "draw 2 cards and take 1 tag" + :req (req (and (is-central? (:server run)) + (first-event? state side :successful-run is-central?))) + :effect (req (wait-for (gain-tags state :runner 1) + (draw state :runner eid 2 nil)))}}} + + "Los: Data Hijacker" + {:events {:rez {:once :per-turn + :req (req (ice? target)) + :msg "gain 2 [Credits]" + :effect (effect (gain-credits :runner 2))}}} + + "MaxX: Maximum Punk Rock" + (let [ability {:msg (msg (let [deck (:deck runner)] + (if (pos? (count deck)) + (str "trash " (join ", " (map :title (take 2 deck))) " from their Stack and draw 1 card") + "trash the top 2 cards from their Stack and draw 1 card - but their Stack is empty"))) + :once :per-turn + :async true + :effect (effect (mill :runner 2) (draw eid 1 nil))}] + {:flags {:runner-turn-draw true + :runner-phase-12 (req (and (not (:disabled card)) + (some #(card-flag? % :runner-turn-draw true) (all-active-installed state :runner))))} + :events {:runner-turn-begins ability} + :abilities [ability]}) + + "Mti Mwekundu: Life Improved" + (let [ability {:once :per-turn + :label "Install a piece of ice from HQ at the innermost position" + :req (req (and run + (zero? (:position run)) + (not (contains? run :corp-phase-43)) + (not (contains? run :successful)))) + :prompt "Choose ICE to install from HQ" + :msg "install ice at the innermost position of this server. Runner is now approaching that ice" + :choices {:req #(and (ice? %) + (in-hand? %))} + :effect (req (corp-install state side target (zone->name (first (:server run))) + {:ignore-all-cost true + :front true}) + (swap! state assoc-in [:run :position] 1))}] + {:abilities [ability] + :events {:approach-server {:req (req (can-trigger? state side ability card nil)) + :effect (req (toast state :corp "You may use Mti Mwekundu: Life Improved to install ice from HQ." "info"))}}}) + + "Nasir Meidan: Cyber Explorer" + {:events {:rez {:req (req (and (:run @state) + ;; check that the rezzed item is the encountered ice + (= (:cid target) + (:cid (get-card state current-ice))))) + :effect (req (toast state :runner "Click Nasir Meidan: Cyber Explorer to lose all credits and gain credits equal to the rez cost of the newly rezzed ice." "info"))}} + :abilities [{:req (req (and (:run @state) + (:rezzed (get-card state current-ice)))) + :effect (req (let [current-ice (get-card state current-ice)] + (trigger-event state side :pre-rez-cost current-ice) + (let [cost (rez-cost state side current-ice)] + (lose-credits state side (:credit runner)) + (gain-credits state side cost) + (system-msg state side (str "loses all credits and gains " cost + " [Credits] from the rez of " (:title current-ice))) + (swap! state update-in [:bonus] dissoc :cost))))}]} + + "Nathaniel \"Gnat\" Hall: One-of-a-Kind" + (let [ability {:label "Gain 1 [Credits] (start of turn)" + :once :per-turn + :effect (req (when (and (> 3 (count (:hand runner))) + (:runner-phase-12 @state)) + (system-msg state :runner (str "uses " (:title card) " to gain 1 [Credits]")) + (gain-credits state :runner 1)))}] + {:flags {:drip-economy true + :runner-phase-12 (req (and (not (:disabled card)) + (some #(card-flag? % :runner-turn-draw true) (all-active-installed state :runner))))} + :abilities [ability] + :events {:runner-turn-begins ability}}) + + "NBN: Controlling the Message" + {:events {:runner-trash + {:async true + :req (req (and (= 1 (count (filter #(and (installed? (first %)) (corp? (first %))) + (turn-events state side :runner-trash)))) + (corp? target) + (installed? target))) + :effect (req (show-wait-prompt state :runner "Corp to use NBN: Controlling the Message") + (continue-ability + state :corp + {:optional + {:prompt "Trace the Runner with NBN: Controlling the Message?" + :autoresolve (get-autoresolve :auto-ctm) + :yes-ability {:trace {:base 4 + :successful + {:msg "give the Runner 1 tag" + :async true + :effect (effect (gain-tags :corp eid 1 {:unpreventable true}))}}} + :end-effect (effect (clear-wait-prompt :runner))}} + card nil))}} + :abilities [(set-autoresolve :auto-ctm "CtM")]} + + "NBN: Making News" + {:recurring 2 + :interactions {:pay-credits {:req (req (= :trace (:source-type eid))) + :type :recurring}}} + + "NBN: The World is Yours*" + {:effect (effect (gain :hand-size 1)) + :leave-play (effect (lose :hand-size 1))} + + "Near-Earth Hub: Broadcast Center" + {:events {:server-created {:req (req (first-event? state :corp :server-created)) :msg "draw 1 card" - :effect (effect (draw :runner eid 1 nil))} - :no-ability {:effect (effect (system-msg "declines to use Lat: Ethical Freelancer"))}}}} - :abilities [(set-autoresolve :auto-lat "Lat: Ethical Freelancer")]}) - -(define-card "Leela Patel: Trained Pragmatist" - (let [leela {:interactive (req true) - :prompt "Select an unrezzed card to return to HQ" - :choices {:req #(and (not (rezzed? %)) (installed? %) (corp? %))} - :msg (msg "add " (card-str state target) " to HQ") - :effect (effect (move :corp target :hand))}] - {:flags {:slow-hq-access (req true)} - :events {:agenda-scored leela - :agenda-stolen leela}})) - -(define-card "Liza Talking Thunder: Prominent Legislator" - {:implementation "Needs to be resolved manually with Crisium Grid" - :events - {:successful-run - {:async true - :interactive (req true) - :msg "draw 2 cards and take 1 tag" - :req (req (and (is-central? (:server run)) - (first-event? state side :successful-run is-central?))) - :effect (req (wait-for (gain-tags state :runner 1) - (draw state :runner eid 2 nil)))}}}) - -(define-card "Los: Data Hijacker" - {:events {:rez {:once :per-turn - :req (req (ice? target)) - :msg "gain 2 [Credits]" - :effect (effect (gain-credits :runner 2))}}}) + :async true + :effect (effect (draw :corp eid 1 nil))}}} -(define-card "MaxX: Maximum Punk Rock" - (let [ability {:msg (msg (let [deck (:deck runner)] - (if (pos? (count deck)) - (str "trash " (join ", " (map :title (take 2 deck))) " from their Stack and draw 1 card") - "trash the top 2 cards from their Stack and draw 1 card - but their Stack is empty"))) + "Nero Severn: Information Broker" + {:abilities [{:req (req (has-subtype? current-ice "Sentry")) :once :per-turn - :async true - :effect (effect (mill :runner 2) (draw eid 1 nil))}] - {:flags {:runner-turn-draw true - :runner-phase-12 (req (and (not (:disabled card)) - (some #(card-flag? % :runner-turn-draw true) (all-active-installed state :runner))))} - :events {:runner-turn-begins ability} - :abilities [ability]})) - -(define-card "Mti Mwekundu: Life Improved" - (let [ability {:once :per-turn - :label "Install a piece of ice from HQ at the innermost position" - :req (req (and run - (zero? (:position run)) - (not (contains? run :corp-phase-43)) - (not (contains? run :successful)))) - :prompt "Choose ICE to install from HQ" - :msg "install ice at the innermost position of this server. Runner is now approaching that ice" - :choices {:req #(and (ice? %) - (in-hand? %))} - :effect (req (corp-install state side target (zone->name (first (:server run))) - {:ignore-all-cost true - :front true}) - (swap! state assoc-in [:run :position] 1))}] - {:abilities [ability] - :events {:approach-server {:req (req (can-trigger? state side ability card nil)) - :effect (req (toast state :corp "You may use Mti Mwekundu: Life Improved to install ice from HQ." "info"))}}})) - -(define-card "Nasir Meidan: Cyber Explorer" - {:events {:rez {:req (req (and (:run @state) - ;; check that the rezzed item is the encountered ice - (= (:cid target) - (:cid (get-card state current-ice))))) - :effect (req (toast state :runner "Click Nasir Meidan: Cyber Explorer to lose all credits and gain credits equal to the rez cost of the newly rezzed ice." "info"))}} - :abilities [{:req (req (and (:run @state) - (:rezzed (get-card state current-ice)))) - :effect (req (let [current-ice (get-card state current-ice)] - (trigger-event state side :pre-rez-cost current-ice) - (let [cost (rez-cost state side current-ice)] - (lose-credits state side (:credit runner)) - (gain-credits state side cost) - (system-msg state side (str "loses all credits and gains " cost - " [Credits] from the rez of " (:title current-ice))) - (swap! state update-in [:bonus] dissoc :cost))))}]}) - -(define-card "Nathaniel \"Gnat\" Hall: One-of-a-Kind" - (let [ability {:label "Gain 1 [Credits] (start of turn)" + :msg "jack out when encountering a Sentry" + :effect (effect (jack-out nil))}]} + + "New Angeles Sol: Your News" + (let [nasol {:optional + {:prompt "Play a Current?" + :player :corp + :req (req (some #(has-subtype? % "Current") (concat (:hand corp) (:discard corp) (:current corp)))) + :yes-ability {:prompt "Select a Current to play from HQ or Archives" + :show-discard true + :async true + :choices {:req #(and (has-subtype? % "Current") + (corp? %) + (#{[:hand] [:discard]} (:zone %)))} + :msg (msg "play a current from " (name-zone "Corp" (:zone target))) + :effect (effect (play-instant eid target))}}}] + {:events {:agenda-scored nasol + :agenda-stolen nasol}}) + + "NEXT Design: Guarding the Net" + (let [ndhelper (fn nd [n] {:prompt (msg "When finished, click NEXT Design: Guarding the Net to draw back up to 5 cards in HQ. " + "Select a piece of ICE in HQ to install:") + :choices {:req #(and (corp? %) + (ice? %) + (in-hand? %))} + :effect (req (corp-install state side target nil) + (when (< n 3) + (resolve-ability state side (nd (inc n)) card nil)))})] + {:events {:pre-first-turn {:req (req (= side :corp)) + :msg "install up to 3 pieces of ICE and draw back up to 5 cards" + :effect (effect (resolve-ability (ndhelper 1) card nil) + (update! (assoc card :fill-hq true)))}} + :abilities [{:req (req (:fill-hq card)) + :msg (msg "draw " (- 5 (count (:hand corp))) " cards") + :effect (req (draw state side (- 5 (count (:hand corp)))) + (update! state side (dissoc card :fill-hq)) + (swap! state dissoc :turn-events))}]}) + + "Nisei Division: The Next Generation" + {:events {:reveal-spent-credits + {:req (req (and (some? (first targets)) + (some? (second targets)))) + :msg "gain 1 [Credits]" + :effect (effect (gain-credits :corp 1))}}} + + "Noise: Hacker Extraordinaire" + {:events {:runner-install {:msg "force the Corp to trash the top card of R&D" + :effect (effect (mill :corp)) + :req (req (has-subtype? target "Virus"))}}} + + "Null: Whistleblower" + {:abilities [{:once :per-turn + :req (req (and (:run @state) + (rezzed? current-ice))) + :prompt "Select a card in your Grip to trash" + :choices {:req in-hand?} + :msg (msg "trash " (:title target) " and reduce the strength of " (:title current-ice) + " by 2 for the remainder of the run") + :effect (effect (update! (assoc card :null-target current-ice)) + (update-ice-strength current-ice) + (trash target {:unpreventable true}))}] + :events {:pre-ice-strength + {:req (req (= (:cid target) (get-in card [:null-target :cid]))) + :effect (effect (ice-strength-bonus -2 target))} + :run-ends + {:effect (req (swap! state dissoc-in [:runner :identity :null-target]))}}} + + "Omar Keung: Conspiracy Theorist" + {:abilities [{:cost [:click 1] + :msg "make a run on Archives" :once :per-turn - :effect (req (when (and (> 3 (count (:hand runner))) - (:runner-phase-12 @state)) - (system-msg state :runner (str "uses " (:title card) " to gain 1 [Credits]")) - (gain-credits state :runner 1)))}] - {:flags {:drip-economy true - :runner-phase-12 (req (and (not (:disabled card)) - (some #(card-flag? % :runner-turn-draw true) (all-active-installed state :runner))))} - :abilities [ability] - :events {:runner-turn-begins ability}})) - -(define-card "NBN: Controlling the Message" - {:events {:runner-trash - {:async true - :req (req (and (= 1 (count (filter #(and (installed? (first %)) (corp? (first %))) - (turn-events state side :runner-trash)))) - (corp? target) - (installed? target))) - :effect (req (show-wait-prompt state :runner "Corp to use NBN: Controlling the Message") - (continue-ability - state :corp - {:optional - {:prompt "Trace the Runner with NBN: Controlling the Message?" - :autoresolve (get-autoresolve :auto-ctm) - :yes-ability {:trace {:base 4 - :successful - {:msg "give the Runner 1 tag" - :async true - :effect (effect (gain-tags :corp eid 1 {:unpreventable true}))}}} - :end-effect (effect (clear-wait-prompt :runner))}} - card nil))}} - :abilities [(set-autoresolve :auto-ctm "CtM")]}) - -(define-card "NBN: Making News" - {:recurring 2 - :interactions {:pay-credits {:req (req (= :trace (:source-type eid))) - :type :recurring}}}) - -(define-card "NBN: The World is Yours*" - {:effect (effect (gain :hand-size 1)) - :leave-play (effect (lose :hand-size 1))}) - -(define-card "Near-Earth Hub: Broadcast Center" - {:events {:server-created {:req (req (first-event? state :corp :server-created)) - :msg "draw 1 card" - :async true - :effect (effect (draw :corp eid 1 nil))}}}) - -(define-card "Nero Severn: Information Broker" - {:abilities [{:req (req (has-subtype? current-ice "Sentry")) - :once :per-turn - :msg "jack out when encountering a Sentry" - :effect (effect (jack-out nil))}]}) - -(define-card "New Angeles Sol: Your News" - (let [nasol {:optional - {:prompt "Play a Current?" - :player :corp - :req (req (some #(has-subtype? % "Current") (concat (:hand corp) (:discard corp) (:current corp)))) - :yes-ability {:prompt "Select a Current to play from HQ or Archives" - :show-discard true - :async true - :choices {:req #(and (has-subtype? % "Current") - (corp? %) - (#{[:hand] [:discard]} (:zone %)))} - :msg (msg "play a current from " (name-zone "Corp" (:zone target))) - :effect (effect (play-instant eid target))}}}] - {:events {:agenda-scored nasol - :agenda-stolen nasol}})) - -(define-card "NEXT Design: Guarding the Net" - (let [ndhelper (fn nd [n] {:prompt (msg "When finished, click NEXT Design: Guarding the Net to draw back up to 5 cards in HQ. " - "Select a piece of ICE in HQ to install:") - :choices {:req #(and (corp? %) - (ice? %) - (in-hand? %))} - :effect (req (corp-install state side target nil) - (when (< n 3) - (resolve-ability state side (nd (inc n)) card nil)))})] - {:events {:pre-first-turn {:req (req (= side :corp)) - :msg "install up to 3 pieces of ICE and draw back up to 5 cards" - :effect (effect (resolve-ability (ndhelper 1) card nil) - (update! (assoc card :fill-hq true)))}} - :abilities [{:req (req (:fill-hq card)) - :msg (msg "draw " (- 5 (count (:hand corp))) " cards") - :effect (req (draw state side (- 5 (count (:hand corp)))) - (update! state side (dissoc card :fill-hq)) - (swap! state dissoc :turn-events))}]})) - -(define-card "Nisei Division: The Next Generation" - {:events {:reveal-spent-credits - {:req (req (and (some? (first targets)) - (some? (second targets)))) - :msg "gain 1 [Credits]" - :effect (effect (gain-credits :corp 1))}}}) - -(define-card "Noise: Hacker Extraordinaire" - {:events {:runner-install {:msg "force the Corp to trash the top card of R&D" - :effect (effect (mill :corp)) - :req (req (has-subtype? target "Virus"))}}}) - -(define-card "Null: Whistleblower" - {:abilities [{:once :per-turn - :req (req (and (:run @state) - (rezzed? current-ice))) - :prompt "Select a card in your Grip to trash" - :choices {:req in-hand?} - :msg (msg "trash " (:title target) " and reduce the strength of " (:title current-ice) - " by 2 for the remainder of the run") - :effect (effect (update! (assoc card :null-target current-ice)) - (update-ice-strength current-ice) - (trash target {:unpreventable true}))}] - :events {:pre-ice-strength - {:req (req (= (:cid target) (get-in card [:null-target :cid]))) - :effect (effect (ice-strength-bonus -2 target))} - :run-ends - {:effect (req (swap! state dissoc-in [:runner :identity :null-target]))}}}) - -(define-card "Omar Keung: Conspiracy Theorist" - {:abilities [{:cost [:click 1] - :msg "make a run on Archives" - :once :per-turn - :makes-run true - :effect (effect (update! (assoc card :omar-run-activated true)) - (make-run :archives nil (get-card state card)))}] - :events {:pre-successful-run {:interactive (req true) - :req (req (and (:omar-run-activated card) - (= :archives (-> run :server first)))) - :prompt "Treat as a successful run on which server?" - :choices ["HQ" "R&D"] - :effect (req (let [target-server (if (= target "HQ") :hq :rd)] - (swap! state update-in [:runner :register :successful-run] #(rest %)) - (swap! state assoc-in [:run :server] [target-server]) - ; remove the :req from the run-effect, so that other cards that replace - ; access don't use Omar's req. - (swap! state dissoc-in [:run :run-effect :req]) - (trigger-event state :corp :no-action) - (swap! state update-in [:runner :register :successful-run] #(conj % target-server)) - (system-msg state side (str "uses Omar Keung: Conspiracy Theorist to make a successful run on " target))))} - :run-ends {:effect (req (swap! state dissoc-in [:runner :identity :omar-run-activated]))}}}) - -(define-card "Pālanā Foods: Sustainable Growth" - {:events {:runner-draw {:req (req (and (first-event? state :corp :runner-draw) - (pos? target))) - :msg "gain 1 [Credits]" - :effect (effect (gain-credits :corp 1))}}}) - -(define-card "Quetzal: Free Spirit" - {:abilities [{:once :per-turn - :msg "break 1 Barrier subroutine"}]}) - -(define-card "Reina Roja: Freedom Fighter" - (letfn [(not-triggered? [state card] (not (get-in @state [:per-turn (:cid card)]))) - (mark-triggered [state card] (swap! state assoc-in [:per-turn (:cid card)] true))] - {:effect (req (when (pos? (event-count state :corp :rez #(ice? (first %)))) - (mark-triggered state card))) - :events {:pre-rez {:req (req (and (ice? target) - (not-triggered? state card))) - :effect (effect (rez-cost-bonus 1))} - :rez {:req (req (and (ice? target) - (not-triggered? state card))) - :effect (req (mark-triggered state card))}}})) - -(define-card "Rielle \"Kit\" Peddler: Transhuman" - {:abilities [{:req (req (and (:run @state) - (:rezzed (get-card state current-ice)))) - :once :per-turn - :msg (msg "make " (:title current-ice) " gain Code Gate until the end of the run") - :effect (req (let [ice current-ice - stypes (:subtype ice)] - (update! state side (assoc ice :subtype (combine-subtypes true stypes "Code Gate"))) - (register-events state side - {:run-ends {:effect (effect (update! (assoc ice :subtype stypes)) - (trigger-event :ice-subtype-changed ice) - (unregister-events card))}} card) - (update-ice-strength state side ice) - (trigger-event state side :ice-subtype-changed ice)))}] - :events {:run-ends nil}}) - -(define-card "Saraswati Mnemonics: Endless Exploration" - (letfn [(install-card [chosen] - {:prompt "Select a remote server" - :choices (req (conj (vec (get-remote-names state)) "New remote")) - :async true - :effect (req (let [tgtcid (:cid chosen)] - (register-turn-flag! - state side - card :can-rez - (fn [state side card] - (if (= (:cid card) tgtcid) - ((constantly false) (toast state :corp "Cannot rez due to Saraswati Mnemonics: Endless Exploration." "warning")) - true))) - (register-turn-flag! - state side - card :can-score - (fn [state side card] - (if (and (= (:cid card) tgtcid) - (>= (get-counters card :advancement) (or (:current-cost card) (:advancementcost card)))) - ((constantly false) (toast state :corp "Cannot score due to Saraswati Mnemonics: Endless Exploration." "warning")) - true)))) - (wait-for (corp-install state side chosen target nil) - (add-prop state :corp (find-latest state chosen) :advance-counter 1 {:placed true}) - (effect-completed state side eid)))})] - {:abilities [{:async true - :label "Install a card from HQ" - :cost [:click 1 :credit 1] - :prompt "Select a card to install from HQ" - :choices {:req #(and (#{"Asset" "Agenda" "Upgrade"} (:type %)) - (corp? %) - (in-hand? %))} - :msg (msg "install a card in a remote server and place 1 advancement token on it") - :effect (effect (continue-ability (install-card target) card nil))}]})) - -(define-card "Seidr Laboratories: Destiny Defined" - {:implementation "Manually triggered" - :abilities [{:req (req (:run @state)) - :once :per-turn - :prompt "Select a card to add to the top of R&D" - :show-discard true - :choices {:req #(and (corp? %) (in-discard? %))} - :effect (effect (move target :deck {:front true})) - :msg (msg "add " (if (:seen target) (:title target) "a card") " to the top of R&D")}]}) - -(define-card "Silhouette: Stealth Operative" - {:events {:successful-run - {:interactive (req (some #(not (rezzed? %)) (all-installed state :corp))) - :async true - :req (req (and (= target :hq) - (first-successful-run-on-server? state :hq))) - :effect (effect (continue-ability {:choices {:req #(and (installed? %) - (not (rezzed? %)))} - :effect (effect (expose eid target)) - :msg "expose 1 card" - :async true} - card nil))}}}) - -(define-card "Skorpios Defense Systems: Persuasive Power" - {:implementation "Manually triggered, no restriction on which cards in Heap can be targeted. Cannot use on in progress run event" - :abilities [{:label "Remove a card in the Heap that was just trashed from the game" - :async true - :effect (req (when-not (and (used-this-turn? (:cid card) state) (active-prompt? state side card)) - (show-wait-prompt state :runner "Corp to use Skorpios' ability" {:card card}) - (continue-ability state side - {:prompt "Choose a card in the Runner's Heap that was just trashed" - :once :per-turn - :choices (req (cancellable - ;; do not allow a run event in progress to get nuked #2963 - (remove #(same-card? % (get-in @state [:run :run-effect :card])) - (:discard runner)))) - :msg (msg "remove " (:title target) " from the game") - :effect (req (move state :runner target :rfg) - (clear-wait-prompt state :runner) - (effect-completed state side eid)) - :cancel-effect (req (clear-wait-prompt state :runner) - (effect-completed state side eid))} - card nil)))}]}) - -(define-card "Spark Agency: Worldswide Reach" - {:events - {:rez {:req (req (and (has-subtype? target "Advertisement") - (first-event? state :corp :rez #(has-subtype? (first %) "Advertisement")))) - :effect (effect (lose-credits :runner 1)) - :msg (msg "make the Runner lose 1 [Credits] by rezzing an Advertisement")}}}) - -(define-card "Sportsmetal: Go Big or Go Home" - (let [ab {:prompt "Gain 2 credits or draw 2 cards?" - :player :corp - :choices ["2 credits" "2 cards"] - :msg "gain 2 [Credits] or draw 2 cards" - :async true - :interactive (req true) - :effect (req (if (= target "2 credits") - (do (system-msg state side "chooses to take 2 [Credits]") - (gain-credits state :corp 2) - (effect-completed state side eid)) - (do (system-msg state side "chooses to draw 2 cards") - (draw state :corp eid 2 nil))))}] - {:events {:agenda-scored ab - :agenda-stolen ab}})) - -(define-card "SSO Industries: Fueling Innovation" - (letfn [(installed-faceup-agendas [state] - (->> (all-installed state :corp) - (filter agenda?) - (filter faceup?))) - (selectable-ice? [card] - (and - (ice? card) - (installed? card) - (zero? (+ (get-counters card :advancement) - (:extra-advance-counter card 0))))) - (ice-with-no-advancement-tokens [state] - (->> (all-installed state :corp) - (filter selectable-ice?)))] - {:events {:corp-turn-ends - {:optional - {:prompt "Place advancement tokens?" - :req (req (and - (not-empty (installed-faceup-agendas state)) - (not-empty (ice-with-no-advancement-tokens state)))) - :autoresolve (get-autoresolve :auto-sso) - :yes-ability - {:async true - :effect (req (show-wait-prompt state :runner "Corp to use SSO Industries' ability") - (let [agendas (installed-faceup-agendas state) - agenda-points (->> agendas - (map :agendapoints) - (reduce +)) - ice (ice-with-no-advancement-tokens state)] - (continue-ability + :makes-run true + :effect (effect (update! (assoc card :omar-run-activated true)) + (make-run :archives nil (get-card state card)))}] + :events {:pre-successful-run {:interactive (req true) + :req (req (and (:omar-run-activated card) + (= :archives (-> run :server first)))) + :prompt "Treat as a successful run on which server?" + :choices ["HQ" "R&D"] + :effect (req (let [target-server (if (= target "HQ") :hq :rd)] + (swap! state update-in [:runner :register :successful-run] #(rest %)) + (swap! state assoc-in [:run :server] [target-server]) + ; remove the :req from the run-effect, so that other cards that replace + ; access don't use Omar's req. + (swap! state dissoc-in [:run :run-effect :req]) + (trigger-event state :corp :no-action) + (swap! state update-in [:runner :register :successful-run] #(conj % target-server)) + (system-msg state side (str "uses Omar Keung: Conspiracy Theorist to make a successful run on " target))))} + :run-ends {:effect (req (swap! state dissoc-in [:runner :identity :omar-run-activated]))}}} + + "Pālanā Foods: Sustainable Growth" + {:events {:runner-draw {:req (req (and (first-event? state :corp :runner-draw) + (pos? target))) + :msg "gain 1 [Credits]" + :effect (effect (gain-credits :corp 1))}}} + + "Quetzal: Free Spirit" + {:abilities [{:once :per-turn + :msg "break 1 Barrier subroutine"}]} + + "Reina Roja: Freedom Fighter" + (letfn [(not-triggered? [state card] (not (get-in @state [:per-turn (:cid card)]))) + (mark-triggered [state card] (swap! state assoc-in [:per-turn (:cid card)] true))] + {:effect (req (when (pos? (event-count state :corp :rez #(ice? (first %)))) + (mark-triggered state card))) + :events {:pre-rez {:req (req (and (ice? target) + (not-triggered? state card))) + :effect (effect (rez-cost-bonus 1))} + :rez {:req (req (and (ice? target) + (not-triggered? state card))) + :effect (req (mark-triggered state card))}}}) + + "Rielle \"Kit\" Peddler: Transhuman" + {:abilities [{:req (req (and (:run @state) + (:rezzed (get-card state current-ice)))) + :once :per-turn + :msg (msg "make " (:title current-ice) " gain Code Gate until the end of the run") + :effect (req (let [ice current-ice + stypes (:subtype ice)] + (update! state side (assoc ice :subtype (combine-subtypes true stypes "Code Gate"))) + (register-events state side + {:run-ends {:effect (effect (update! (assoc ice :subtype stypes)) + (trigger-event :ice-subtype-changed ice) + (unregister-events card))}} card) + (update-ice-strength state side ice) + (trigger-event state side :ice-subtype-changed ice)))}] + :events {:run-ends nil}} + + "Saraswati Mnemonics: Endless Exploration" + (letfn [(install-card [chosen] + {:prompt "Select a remote server" + :choices (req (conj (vec (get-remote-names state)) "New remote")) + :async true + :effect (req (let [tgtcid (:cid chosen)] + (register-turn-flag! state side - {:prompt (str "Select ICE with no advancement tokens to place " - (quantify agenda-points "advancement token") " on") - :choices {:req #(selectable-ice? %)} - :msg (msg "places " (quantify agenda-points "advancement token") - " on ICE with no advancement tokens") - :effect (req (add-prop state :corp target :advance-counter agenda-points {:placed true}) - (clear-wait-prompt state :runner)) - :cancel-effect (req (clear-wait-prompt state :runner))} - card nil)))}}}} - :abilities [(set-autoresolve :auto-sso "SSO")]})) - -(define-card "Steve Cambridge: Master Grifter" - {:events {:successful-run - {:req (req (and (= target :hq) - (first-successful-run-on-server? state :hq) - (if (-> @state :run :run-effect :card) - (> (count (:discard runner)) 2) - (> (count (:discard runner)) 1)))) - :interactive (req true) + card :can-rez + (fn [state side card] + (if (= (:cid card) tgtcid) + ((constantly false) (toast state :corp "Cannot rez due to Saraswati Mnemonics: Endless Exploration." "warning")) + true))) + (register-turn-flag! + state side + card :can-score + (fn [state side card] + (if (and (= (:cid card) tgtcid) + (>= (get-counters card :advancement) (or (:current-cost card) (:advancementcost card)))) + ((constantly false) (toast state :corp "Cannot score due to Saraswati Mnemonics: Endless Exploration." "warning")) + true)))) + (wait-for (corp-install state side chosen target nil) + (add-prop state :corp (find-latest state chosen) :advance-counter 1 {:placed true}) + (effect-completed state side eid)))})] + {:abilities [{:async true + :label "Install a card from HQ" + :cost [:click 1 :credit 1] + :prompt "Select a card to install from HQ" + :choices {:req #(and (#{"Asset" "Agenda" "Upgrade"} (:type %)) + (corp? %) + (in-hand? %))} + :msg (msg "install a card in a remote server and place 1 advancement token on it") + :effect (effect (continue-ability (install-card target) card nil))}]}) + + "Seidr Laboratories: Destiny Defined" + {:implementation "Manually triggered" + :abilities [{:req (req (:run @state)) + :once :per-turn + :prompt "Select a card to add to the top of R&D" + :show-discard true + :choices {:req #(and (corp? %) (in-discard? %))} + :effect (effect (move target :deck {:front true})) + :msg (msg "add " (if (:seen target) (:title target) "a card") " to the top of R&D")}]} + + "Silhouette: Stealth Operative" + {:events {:successful-run + {:interactive (req (some #(not (rezzed? %)) (all-installed state :corp))) + :async true + :req (req (and (= target :hq) + (first-successful-run-on-server? state :hq))) + :effect (effect (continue-ability {:choices {:req #(and (installed? %) + (not (rezzed? %)))} + :effect (effect (expose eid target)) + :msg "expose 1 card" + :async true} + card nil))}}} + + "Skorpios Defense Systems: Persuasive Power" + {:implementation "Manually triggered, no restriction on which cards in Heap can be targeted. Cannot use on in progress run event" + :abilities [{:label "Remove a card in the Heap that was just trashed from the game" + :async true + :effect (req (when-not (and (used-this-turn? (:cid card) state) (active-prompt? state side card)) + (show-wait-prompt state :runner "Corp to use Skorpios' ability" {:card card}) + (continue-ability state side + {:prompt "Choose a card in the Runner's Heap that was just trashed" + :once :per-turn + :choices (req (cancellable + ;; do not allow a run event in progress to get nuked #2963 + (remove #(same-card? % (get-in @state [:run :run-effect :card])) + (:discard runner)))) + :msg (msg "remove " (:title target) " from the game") + :effect (req (move state :runner target :rfg) + (clear-wait-prompt state :runner) + (effect-completed state side eid)) + :cancel-effect (req (clear-wait-prompt state :runner) + (effect-completed state side eid))} + card nil)))}]} + + "Spark Agency: Worldswide Reach" + {:events + {:rez {:req (req (and (has-subtype? target "Advertisement") + (first-event? state :corp :rez #(has-subtype? (first %) "Advertisement")))) + :effect (effect (lose-credits :runner 1)) + :msg (msg "make the Runner lose 1 [Credits] by rezzing an Advertisement")}}} + + "Sportsmetal: Go Big or Go Home" + (let [ab {:prompt "Gain 2 credits or draw 2 cards?" + :player :corp + :choices ["2 credits" "2 cards"] + :msg "gain 2 [Credits] or draw 2 cards" :async true - :effect (effect (continue-ability - {:async true - :prompt "Select 2 cards in your Heap" - :show-discard true - :choices {:max 2 :req #(and (in-discard? %) - (runner? %) - (not= (-> @state :run :run-effect :card :cid) (:cid %)))} - :cancel-effect (req (effect-completed state side eid)) - :effect (req (let [c1 (first targets) - c2 (second targets)] - (show-wait-prompt state :runner "Corp to choose which card to remove from the game") - (continue-ability - state :corp - {:prompt "Choose which card to remove from the game" - :player :corp - :choices [c1 c2] - :effect (req (if (= target c1) - (do (move state :runner c1 :rfg) - (move state :runner c2 :hand) - (system-msg state :runner - (str "uses Steve Cambridge: Master Grifter" - " to add " (:title c2) " to their Grip." - " Corp removes " (:title c1) " from the game"))) - (do (move state :runner c2 :rfg) - (move state :runner c1 :hand) - (system-msg state :runner - (str "uses Steve Cambridge: Master Grifter" - " to add " (:title c1) " to their Grip." - " Corp removes " (:title c2) " from the game")))) - (clear-wait-prompt state :runner) - (effect-completed state side eid))} - card nil)))} - card nil))}}}) - -(define-card "Strategic Innovations: Future Forward" - {:events {:pre-start-game {:effect draft-points-target} - :runner-turn-ends - {:req (req (and (not (:disabled card)) - (has-most-faction? state :corp "Haas-Bioroid") - (pos? (count (:discard corp))))) - :prompt "Select a card in Archives to shuffle into R&D" - :choices {:req #(and (corp? %) (in-discard? %))} - :player :corp :show-discard true :priority true - :msg (msg "shuffle " (if (:seen target) (:title target) "a card") - " into R&D") - :effect (effect (move :corp target :deck) - (shuffle! :corp :deck))}}}) - -(define-card "Sunny Lebeau: Security Specialist" - ;; No special implementation - {}) - -(define-card "SYNC: Everything, Everywhere" - {:effect (req (when (> (:turn @state) 1) - (if (:sync-front card) - (tag-remove-bonus state side -1) - (trash-resource-bonus state side 2)))) - :events {:pre-first-turn {:req (req (= side :corp)) - :effect (effect (update! (assoc card :sync-front true)) - (tag-remove-bonus -1))}} - :abilities [{:cost [:click 1] - :effect (req (if (:sync-front card) - (do (tag-remove-bonus state side 1) - (trash-resource-bonus state side 2) - (update! state side (-> card (assoc :sync-front false - :code "sync")))) - (do (tag-remove-bonus state side -1) - (trash-resource-bonus state side -2) - (update! state side (-> card (assoc :sync-front true - :code "09001")))))) - :msg (msg "flip their ID")}] - :leave-play (req (if (:sync-front card) - (tag-remove-bonus state side 1) - (trash-resource-bonus state side -2)))}) - -(define-card "Synthetic Systems: The World Re-imagined" - {:events {:pre-start-game {:effect draft-points-target}} - :flags {:corp-phase-12 (req (and (not (:disabled (get-card state card))) - (has-most-faction? state :corp "Jinteki") - (> (count (filter ice? (all-installed state :corp))) 1)))} - :abilities [{:prompt "Select two pieces of ICE to swap positions" - :choices {:req #(and (installed? %) - (ice? %)) - :max 2} - :once :per-turn - :effect (req (when (= (count targets) 2) - (swap-ice state side (first targets) (second targets)))) - :msg (msg "swap the positions of " (card-str state (first targets)) - " and " (card-str state (second targets)))}]}) - -(define-card "Tennin Institute: The Secrets Within" - {:flags {:corp-phase-12 (req (and (not (:disabled (get-card state card))) - (not-last-turn? state :runner :successful-run)))} - :abilities [{:msg (msg "place 1 advancement token on " (card-str state target)) - :choices {:req installed?} - :req (req (and (:corp-phase-12 @state) (not-last-turn? state :runner :successful-run))) - :once :per-turn - :effect (effect (add-prop target :advance-counter 1 {:placed true}))}]}) - -(define-card "The Foundry: Refining the Process" - {:events - {:rez {:req (req (and (ice? target) ;; Did you rez and ice just now - (first-event? state :runner :rez #(ice? (first %))))) - :optional - {:prompt "Add another copy to HQ?" - :yes-ability {:effect (req (if-let [found-card (some #(when (= (:title %) (:title target)) %) (concat (:deck corp) (:play-area corp)))] - (do (move state side found-card :hand) - (system-msg state side (str "uses The Foundry to add a copy of " - (:title found-card) " to HQ, and shuffles their deck")) - (shuffle! state side :deck)) - (do (system-msg state side (str "fails to find a target for The Foundry, and shuffles their deck")) - (shuffle! state side :deck))))}}}}}) - -(define-card "The Masque: Cyber General" - {:events {:pre-start-game {:effect draft-points-target}}}) - -(define-card "The Outfit: Family Owned and Operated" - {:events {:corp-gain-bad-publicity {:msg "gain 3 [Credit]" - :effect (effect (gain-credits 3))}}}) - -(define-card "The Professor: Keeper of Knowledge" - ;; No special implementation - {}) - -(define-card "The Shadow: Pulling the Strings" - {:events {:pre-start-game {:effect draft-points-target}}}) - -(define-card "Titan Transnational: Investing In Your Future" - {:events {:agenda-scored {:msg (msg "add 1 agenda counter to " (:title target)) - :effect (effect (add-counter (get-card state target) :agenda 1))}}}) - -(define-card "Valencia Estevez: The Angel of Cayambe" - {:events {:pre-start-game - {:req (req (and (= side :runner) - (zero? (count-bad-pub state)))) - ;; This doesn't use `gain-bad-publicity` to avoid the event - :effect (effect (gain :corp :bad-publicity 1))}}}) - -(define-card "Weyland Consortium: Because We Built It" - {:recurring 1 - :interactions {:pay-credits {:req (req (= :advance (:source-type eid))) - :type :recurring}}}) - -(define-card "Weyland Consortium: Builder of Nations" - {:implementation "Damage triggered manually" - :abilities [{:label "Do 1 meat damage" - :once :per-turn - :prompt "Do a meat damage from identity ability?" - :choices (cancellable ["Yes"]) - :async true - :effect (req (when (= target "Yes") - (damage state side eid :meat 1 {:card card}) - (system-msg state side "uses Weyland Consortium: Builder of Nations to do 1 meat damage")))}]}) - -(define-card "Weyland Consortium: Building a Better World" - {:events {:play-operation {:msg "gain 1 [Credits]" - :effect (effect (gain-credits 1)) - :req (req (has-subtype? target "Transaction"))}}}) - -(define-card "Whizzard: Master Gamer" - {:recurring 3 - :interactions {:pay-credits {:req (req (and (= :runner-trash-corp-cards (:source-type eid)) - (corp? target))) - :type :recurring}}}) - -(define-card "Wyvern: Chemically Enhanced" - {:events {:pre-start-game {:effect draft-points-target} - :runner-trash {:req (req (and (has-most-faction? state :runner "Anarch") - (corp? target) - (pos? (count (:discard runner))))) - :msg (msg "shuffle " (:title (last (:discard runner))) " into their Stack") - :effect (effect (move :runner (last (:discard runner)) :deck) - (shuffle! :runner :deck))}}}) + :interactive (req true) + :effect (req (if (= target "2 credits") + (do (system-msg state side "chooses to take 2 [Credits]") + (gain-credits state :corp 2) + (effect-completed state side eid)) + (do (system-msg state side "chooses to draw 2 cards") + (draw state :corp eid 2 nil))))}] + {:events {:agenda-scored ab + :agenda-stolen ab}}) + + "SSO Industries: Fueling Innovation" + (letfn [(installed-faceup-agendas [state] + (->> (all-installed state :corp) + (filter agenda?) + (filter faceup?))) + (selectable-ice? [card] + (and + (ice? card) + (installed? card) + (zero? (+ (get-counters card :advancement) + (:extra-advance-counter card 0))))) + (ice-with-no-advancement-tokens [state] + (->> (all-installed state :corp) + (filter selectable-ice?)))] + {:events {:corp-turn-ends + {:optional + {:prompt "Place advancement tokens?" + :req (req (and + (not-empty (installed-faceup-agendas state)) + (not-empty (ice-with-no-advancement-tokens state)))) + :autoresolve (get-autoresolve :auto-sso) + :yes-ability + {:async true + :effect (req (show-wait-prompt state :runner "Corp to use SSO Industries' ability") + (let [agendas (installed-faceup-agendas state) + agenda-points (->> agendas + (map :agendapoints) + (reduce +)) + ice (ice-with-no-advancement-tokens state)] + (continue-ability + state side + {:prompt (str "Select ICE with no advancement tokens to place " + (quantify agenda-points "advancement token") " on") + :choices {:req #(selectable-ice? %)} + :msg (msg "places " (quantify agenda-points "advancement token") + " on ICE with no advancement tokens") + :effect (req (add-prop state :corp target :advance-counter agenda-points {:placed true}) + (clear-wait-prompt state :runner)) + :cancel-effect (req (clear-wait-prompt state :runner))} + card nil)))}}}} + :abilities [(set-autoresolve :auto-sso "SSO")]}) + + "Steve Cambridge: Master Grifter" + {:events {:successful-run + {:req (req (and (= target :hq) + (first-successful-run-on-server? state :hq) + (if (-> @state :run :run-effect :card) + (> (count (:discard runner)) 2) + (> (count (:discard runner)) 1)))) + :interactive (req true) + :async true + :effect (effect (continue-ability + {:async true + :prompt "Select 2 cards in your Heap" + :show-discard true + :choices {:max 2 :req #(and (in-discard? %) + (runner? %) + (not= (-> @state :run :run-effect :card :cid) (:cid %)))} + :cancel-effect (req (effect-completed state side eid)) + :effect (req (let [c1 (first targets) + c2 (second targets)] + (show-wait-prompt state :runner "Corp to choose which card to remove from the game") + (continue-ability + state :corp + {:prompt "Choose which card to remove from the game" + :player :corp + :choices [c1 c2] + :effect (req (if (= target c1) + (do (move state :runner c1 :rfg) + (move state :runner c2 :hand) + (system-msg state :runner + (str "uses Steve Cambridge: Master Grifter" + " to add " (:title c2) " to their Grip." + " Corp removes " (:title c1) " from the game"))) + (do (move state :runner c2 :rfg) + (move state :runner c1 :hand) + (system-msg state :runner + (str "uses Steve Cambridge: Master Grifter" + " to add " (:title c1) " to their Grip." + " Corp removes " (:title c2) " from the game")))) + (clear-wait-prompt state :runner) + (effect-completed state side eid))} + card nil)))} + card nil))}}} + + "Strategic Innovations: Future Forward" + {:events {:pre-start-game {:effect draft-points-target} + :runner-turn-ends + {:req (req (and (not (:disabled card)) + (has-most-faction? state :corp "Haas-Bioroid") + (pos? (count (:discard corp))))) + :prompt "Select a card in Archives to shuffle into R&D" + :choices {:req #(and (corp? %) (in-discard? %))} + :player :corp :show-discard true :priority true + :msg (msg "shuffle " (if (:seen target) (:title target) "a card") + " into R&D") + :effect (effect (move :corp target :deck) + (shuffle! :corp :deck))}}} + + "Sunny Lebeau: Security Specialist" + ;; No special implementation + {} + + "SYNC: Everything, Everywhere" + {:effect (req (when (> (:turn @state) 1) + (if (:sync-front card) + (tag-remove-bonus state side -1) + (trash-resource-bonus state side 2)))) + :events {:pre-first-turn {:req (req (= side :corp)) + :effect (effect (update! (assoc card :sync-front true)) + (tag-remove-bonus -1))}} + :abilities [{:cost [:click 1] + :effect (req (if (:sync-front card) + (do (tag-remove-bonus state side 1) + (trash-resource-bonus state side 2) + (update! state side (-> card (assoc :sync-front false + :code "sync")))) + (do (tag-remove-bonus state side -1) + (trash-resource-bonus state side -2) + (update! state side (-> card (assoc :sync-front true + :code "09001")))))) + :msg (msg "flip their ID")}] + :leave-play (req (if (:sync-front card) + (tag-remove-bonus state side 1) + (trash-resource-bonus state side -2)))} + + "Synthetic Systems: The World Re-imagined" + {:events {:pre-start-game {:effect draft-points-target}} + :flags {:corp-phase-12 (req (and (not (:disabled (get-card state card))) + (has-most-faction? state :corp "Jinteki") + (> (count (filter ice? (all-installed state :corp))) 1)))} + :abilities [{:prompt "Select two pieces of ICE to swap positions" + :choices {:req #(and (installed? %) + (ice? %)) + :max 2} + :once :per-turn + :effect (req (when (= (count targets) 2) + (swap-ice state side (first targets) (second targets)))) + :msg (msg "swap the positions of " (card-str state (first targets)) + " and " (card-str state (second targets)))}]} + + "Tennin Institute: The Secrets Within" + {:flags {:corp-phase-12 (req (and (not (:disabled (get-card state card))) + (not-last-turn? state :runner :successful-run)))} + :abilities [{:msg (msg "place 1 advancement token on " (card-str state target)) + :choices {:req installed?} + :req (req (and (:corp-phase-12 @state) (not-last-turn? state :runner :successful-run))) + :once :per-turn + :effect (effect (add-prop target :advance-counter 1 {:placed true}))}]} + + "The Foundry: Refining the Process" + {:events + {:rez {:req (req (and (ice? target) ;; Did you rez and ice just now + (first-event? state :runner :rez #(ice? (first %))))) + :optional + {:prompt "Add another copy to HQ?" + :yes-ability {:effect (req (if-let [found-card (some #(when (= (:title %) (:title target)) %) (concat (:deck corp) (:play-area corp)))] + (do (move state side found-card :hand) + (system-msg state side (str "uses The Foundry to add a copy of " + (:title found-card) " to HQ, and shuffles their deck")) + (shuffle! state side :deck)) + (do (system-msg state side (str "fails to find a target for The Foundry, and shuffles their deck")) + (shuffle! state side :deck))))}}}}} + + "The Masque: Cyber General" + {:events {:pre-start-game {:effect draft-points-target}}} + + "The Outfit: Family Owned and Operated" + {:events {:corp-gain-bad-publicity {:msg "gain 3 [Credit]" + :effect (effect (gain-credits 3))}}} + + "The Professor: Keeper of Knowledge" + ;; No special implementation + {} + + "The Shadow: Pulling the Strings" + {:events {:pre-start-game {:effect draft-points-target}}} + + "Titan Transnational: Investing In Your Future" + {:events {:agenda-scored {:msg (msg "add 1 agenda counter to " (:title target)) + :effect (effect (add-counter (get-card state target) :agenda 1))}}} + + "Valencia Estevez: The Angel of Cayambe" + {:events {:pre-start-game + {:req (req (and (= side :runner) + (zero? (count-bad-pub state)))) + ;; This doesn't use `gain-bad-publicity` to avoid the event + :effect (effect (gain :corp :bad-publicity 1))}}} + + "Weyland Consortium: Because We Built It" + {:recurring 1 + :interactions {:pay-credits {:req (req (= :advance (:source-type eid))) + :type :recurring}}} + + "Weyland Consortium: Builder of Nations" + {:implementation "Damage triggered manually" + :abilities [{:label "Do 1 meat damage" + :once :per-turn + :prompt "Do a meat damage from identity ability?" + :choices (cancellable ["Yes"]) + :async true + :effect (req (when (= target "Yes") + (damage state side eid :meat 1 {:card card}) + (system-msg state side "uses Weyland Consortium: Builder of Nations to do 1 meat damage")))}]} + + "Weyland Consortium: Building a Better World" + {:events {:play-operation {:msg "gain 1 [Credits]" + :effect (effect (gain-credits 1)) + :req (req (has-subtype? target "Transaction"))}}} + + "Whizzard: Master Gamer" + {:recurring 3 + :interactions {:pay-credits {:req (req (and (= :runner-trash-corp-cards (:source-type eid)) + (corp? target))) + :type :recurring}}} + + "Wyvern: Chemically Enhanced" + {:events {:pre-start-game {:effect draft-points-target} + :runner-trash {:req (req (and (has-most-faction? state :runner "Anarch") + (corp? target) + (pos? (count (:discard runner))))) + :msg (msg "shuffle " (:title (last (:discard runner))) " into their Stack") + :effect (effect (move :runner (last (:discard runner)) :deck) + (shuffle! :runner :deck))}}}}) diff --git a/src/clj/game/cards/operations.clj b/src/clj/game/cards/operations.clj index 140003dd0f..1fdee349f4 100644 --- a/src/clj/game/cards/operations.clj +++ b/src/clj/game/cards/operations.clj @@ -1,7 +1,7 @@ (ns game.cards.operations (:require [game.core :refer :all] [game.core.eid :refer [make-eid make-result effect-completed]] - [game.core.card-defs :refer [card-def define-card]] + [game.core.card-defs :refer [card-def]] [game.utils :refer :all] [game.macros :refer [effect req msg wait-for continue-ability when-let*]] [clojure.string :refer [split-lines split join lower-case includes? starts-with?]] @@ -9,2100 +9,2101 @@ [jinteki.utils :refer :all])) ;; Card definitions -(define-card "24/7 News Cycle" - {:req (req (pos? (count (:scored corp)))) - :async true - :additional-cost [:forfeit] - :effect (req (continue-ability - state side - {:prompt "Select an agenda in your score area to trigger its \"when scored\" ability" - :choices {:req #(and (agenda? %) - (when-scored? %) - (is-scored? state :corp %))} - :msg (msg "trigger the \"when scored\" ability of " (:title target)) +(def card-definitions + {"24/7 News Cycle" + {:req (req (pos? (count (:scored corp)))) + :async true + :additional-cost [:forfeit] + :effect (req (continue-ability + state side + {:prompt "Select an agenda in your score area to trigger its \"when scored\" ability" + :choices {:req #(and (agenda? %) + (when-scored? %) + (is-scored? state :corp %))} + :msg (msg "trigger the \"when scored\" ability of " (:title target)) + :async true + :effect (effect (continue-ability (card-def target) target nil) + (unregister-events target {:events {:corp-turn-ends nil :runner-turn-ends nil}}))} + card nil))} + + "Accelerated Diagnostics" + (letfn [(ad [i n adcard] + {:prompt "Select an operation to play" + :choices {:req #(and (corp? %) + (operation? %) + (in-play-area? %))} + :msg (msg "play " (:title target)) + :async true + :effect (req (wait-for (play-instant state side target {:no-additional-cost true}) + (if (and (not (get-in @state [:corp :register :terminal])) (< i n)) + (continue-ability state side (ad (inc i) n adcard) adcard nil) + (effect-completed state side eid))))})] + {:async true + :implementation "Corp has to manually cards back to R&D to correctly play a draw operation" + :effect (req (let [n (count (filter operation? + (take 3 (:deck corp))))] + (continue-ability state side + {:msg "look at the top 3 cards of R&D" + :async true + :effect (req (doseq [c (take 3 (:deck corp))] + (move state side c :play-area)) + (continue-ability state side (ad 1 n card) card nil))} + card nil)))}) + + "Ad Blitz" + (let [abhelp (fn ab [n total] + {:prompt "Select an Advertisement to install and rez" :show-discard true :async true - :effect (effect (continue-ability (card-def target) target nil) - (unregister-events target {:events {:corp-turn-ends nil :runner-turn-ends nil}}))} - card nil))}) - -(define-card "Accelerated Diagnostics" - (letfn [(ad [i n adcard] - {:prompt "Select an operation to play" - :choices {:req #(and (corp? %) - (operation? %) - (in-play-area? %))} - :msg (msg "play " (:title target)) - :async true - :effect (req (wait-for (play-instant state side target {:no-additional-cost true}) - (if (and (not (get-in @state [:corp :register :terminal])) (< i n)) - (continue-ability state side (ad (inc i) n adcard) adcard nil) - (effect-completed state side eid))))})] - {:async true - :implementation "Corp has to manually cards back to R&D to correctly play a draw operation" - :effect (req (let [n (count (filter operation? - (take 3 (:deck corp))))] - (continue-ability state side - {:msg "look at the top 3 cards of R&D" - :async true - :effect (req (doseq [c (take 3 (:deck corp))] - (move state side c :play-area)) - (continue-ability state side (ad 1 n card) card nil))} - card nil)))})) - -(define-card "Ad Blitz" - (let [abhelp (fn ab [n total] - {:prompt "Select an Advertisement to install and rez" :show-discard true - :async true - :choices {:req #(and (corp? %) - (has-subtype? % "Advertisement") - (or (in-hand? %) - (in-discard? %)))} - :effect (req (wait-for - (corp-install state side target nil {:install-state :rezzed}) - (if (< n total) - (continue-ability state side (ab (inc n) total) card nil) - (effect-completed state side eid))))})] - {:prompt "How many Advertisements?" - :async true - :choices :credit - :msg (msg "install and rez " target " Advertisements") - :effect (effect (continue-ability (abhelp 1 target) card nil))})) - -(define-card "Aggressive Negotiation" - {:req (req (:scored-agenda corp-reg)) :prompt "Choose a card" - :choices (req (cancellable (:deck corp) :sorted)) - :effect (effect (move target :hand) - (shuffle! :deck)) - :msg "search R&D for a card and add it to HQ"}) - -(define-card "An Offer You Can't Refuse" - {:async true - :prompt "Choose a server" :choices ["Archives" "R&D" "HQ"] - :effect (req (let [serv target] - (show-wait-prompt state :corp (str "Runner to decide on running " target)) - (continue-ability - state side - {:optional - {:prompt (msg "Make a run on " serv "?") :player :runner - :yes-ability {:msg (msg "let the Runner make a run on " serv) - :effect (effect (clear-wait-prompt :corp) - (make-run eid serv nil card))} - :no-ability {:async true - :effect (req (clear-wait-prompt state :corp) - (as-agenda state :corp eid (some #(when (same-card? card %) %) (:discard corp)) 1)) - :msg "add it to their score area as an agenda worth 1 agenda point"}}} - card nil)))}) - -(define-card "Anonymous Tip" - {:msg "draw 3 cards" - :async true - :effect (effect (draw eid 3 nil))}) - -(define-card "Archived Memories" - {:async true - :effect (req (let [cid (:cid card)] - (continue-ability - state side - {:prompt "Select a card from Archives to add to HQ" - :show-discard true - :choices {:req #(and (not= (:cid %) cid) - (corp? %) - (in-discard? %))} - :effect (effect (move target :hand) - (system-msg (str "adds " (if (:seen target) (:title target) "an unseen card") " to HQ")))} - card nil)))}) - -(define-card "Ark Lockdown" - {:async true - :req (req (not-empty (:discard runner))) - :prompt "Name a card to remove all copies in the Heap from the game" - :choices (req (cancellable (:discard runner) :sorted)) - :msg (msg "remove all copies of " (:title target) " in the Heap from the game") - :effect (req (doseq [c (filter #(= (:title target) (:title %)) (:discard runner))] - (move state :runner c :rfg)) - (effect-completed state side eid))}) - -(define-card "Attitude Adjustment" - {:async true - :effect (req (wait-for (draw state side 2 nil) - (continue-ability - state side - {:prompt "Choose up to 2 agendas in HQ or Archives" - :choices {:max 2 - :req #(and (corp? %) - (agenda? %) - (or (in-hand? %) - (in-discard? %)))} - :effect (req (gain-credits state side (* 2 (count targets))) - (doseq [c targets] - (move state :corp c :deck)) - (shuffle! state :corp :deck) - (let [from-hq (map :title (filter #(= [:hand] (:zone %)) targets)) - from-archives (map :title (filter #(= [:discard] (:zone %)) targets))] - (system-msg - state side - (str "uses Attitude Adjustment to shuffle " - (join " and " - (filter identity - [(when (not-empty from-hq) - (str (join " and " from-hq) - " from HQ")) - (when (not-empty from-archives) - (str (join " and " from-archives) - " from Archives"))])) - " into R&D and gain " (* 2 (count targets)) " [Credits]"))))} - card nil)))}) - -(define-card "Audacity" - (let [audacity (fn au [n] {:prompt "Choose a card on which to place an advancement" + :choices {:req #(and (corp? %) + (has-subtype? % "Advertisement") + (or (in-hand? %) + (in-discard? %)))} + :effect (req (wait-for + (corp-install state side target nil {:install-state :rezzed}) + (if (< n total) + (continue-ability state side (ab (inc n) total) card nil) + (effect-completed state side eid))))})] + {:prompt "How many Advertisements?" + :async true + :choices :credit + :msg (msg "install and rez " target " Advertisements") + :effect (effect (continue-ability (abhelp 1 target) card nil))}) + + "Aggressive Negotiation" + {:req (req (:scored-agenda corp-reg)) :prompt "Choose a card" + :choices (req (cancellable (:deck corp) :sorted)) + :effect (effect (move target :hand) + (shuffle! :deck)) + :msg "search R&D for a card and add it to HQ"} + + "An Offer You Can't Refuse" + {:async true + :prompt "Choose a server" :choices ["Archives" "R&D" "HQ"] + :effect (req (let [serv target] + (show-wait-prompt state :corp (str "Runner to decide on running " target)) + (continue-ability + state side + {:optional + {:prompt (msg "Make a run on " serv "?") :player :runner + :yes-ability {:msg (msg "let the Runner make a run on " serv) + :effect (effect (clear-wait-prompt :corp) + (make-run eid serv nil card))} + :no-ability {:async true + :effect (req (clear-wait-prompt state :corp) + (as-agenda state :corp eid (some #(when (same-card? card %) %) (:discard corp)) 1)) + :msg "add it to their score area as an agenda worth 1 agenda point"}}} + card nil)))} + + "Anonymous Tip" + {:msg "draw 3 cards" + :async true + :effect (effect (draw eid 3 nil))} + + "Archived Memories" + {:async true + :effect (req (let [cid (:cid card)] + (continue-ability + state side + {:prompt "Select a card from Archives to add to HQ" + :show-discard true + :choices {:req #(and (not= (:cid %) cid) + (corp? %) + (in-discard? %))} + :effect (effect (move target :hand) + (system-msg (str "adds " (if (:seen target) (:title target) "an unseen card") " to HQ")))} + card nil)))} + + "Ark Lockdown" + {:async true + :req (req (not-empty (:discard runner))) + :prompt "Name a card to remove all copies in the Heap from the game" + :choices (req (cancellable (:discard runner) :sorted)) + :msg (msg "remove all copies of " (:title target) " in the Heap from the game") + :effect (req (doseq [c (filter #(= (:title target) (:title %)) (:discard runner))] + (move state :runner c :rfg)) + (effect-completed state side eid))} + + "Attitude Adjustment" + {:async true + :effect (req (wait-for (draw state side 2 nil) + (continue-ability + state side + {:prompt "Choose up to 2 agendas in HQ or Archives" + :choices {:max 2 + :req #(and (corp? %) + (agenda? %) + (or (in-hand? %) + (in-discard? %)))} + :effect (req (gain-credits state side (* 2 (count targets))) + (doseq [c targets] + (move state :corp c :deck)) + (shuffle! state :corp :deck) + (let [from-hq (map :title (filter #(= [:hand] (:zone %)) targets)) + from-archives (map :title (filter #(= [:discard] (:zone %)) targets))] + (system-msg + state side + (str "uses Attitude Adjustment to shuffle " + (join " and " + (filter identity + [(when (not-empty from-hq) + (str (join " and " from-hq) + " from HQ")) + (when (not-empty from-archives) + (str (join " and " from-archives) + " from Archives"))])) + " into R&D and gain " (* 2 (count targets)) " [Credits]"))))} + card nil)))} + + "Audacity" + (let [audacity (fn au [n] {:prompt "Choose a card on which to place an advancement" + :async true + :choices {:req can-be-advanced?} + :cancel-effect (req (effect-completed state side eid)) + :msg (msg "place an advancement token on " (card-str state target)) + :effect (req (add-prop state :corp target :advance-counter 1 {:placed true}) + (if (< n 2) + (continue-ability state side (au (inc n)) card nil) + (effect-completed state side eid)))})] + {:async true + :req (req (let [h (:hand corp) + p (:play-area corp)] + ;; this is needed to pass the req check for can-play? and again when card is actually played + (if (some #(same-card? % card) p) + (>= (count h) 2) + (>= (count h) 3)))) + :effect (req (system-msg state side "trashes all cards in HQ due to Audacity") + (doseq [c (:hand corp)] + (trash state side c {:unpreventable true})) + (continue-ability state side (audacity 1) card nil))}) + + "Back Channels" + {:async true + :prompt "Select an installed card in a server to trash" + :choices {:req #(and (= (last (:zone %)) :content) + (is-remote? (second (:zone %))))} + :effect (effect (gain-credits (* 3 (get-counters target :advancement))) + (trash eid target nil)) + :msg (msg "trash " (card-str state target) " and gain " + (* 3 (get-counters target :advancement)) " [Credits]")} + + "Bad Times" + {:implementation "Any required program trashing is manual" + :req (req tagged) + :msg "force the Runner to lose 2[mu] until the end of the turn" + :effect (req (lose state :runner :memory 2) + (when (neg? (available-mu state)) + ;; Give runner a toast as well + (toast-check-mu state) + (system-msg state :runner "must trash programs to free up [mu]")) + (register-events + state side + {:corp-turn-ends {:effect (effect (gain :runner :memory 2) + (system-msg :runner "regains 2[mu]") + (unregister-events card))} + :runner-turn-ends {:effect (effect (gain :runner :memory 2) + (system-msg :runner "regains 2[mu]") + (unregister-events card))}} + card)) + :events {:corp-turn-ends nil + :runner-turn-ends nil}} + + "Beanstalk Royalties" + {:msg "gain 3 [Credits]" + :effect (effect (gain-credits 3))} + + "Best Defense" + {:async true + :req (req (not-empty (all-installed state :runner))) + :effect (req (let [t (count-tags state)] + (continue-ability + state side + {:prompt (msg "Choose a Runner card with an install cost of " t " or less to trash") + :choices {:req #(and (installed? %) + (<= (:cost %) t))} + :msg (msg "trash " (:title target)) + :effect (effect (trash target))} + card nil)))} + + "Biased Reporting" + (letfn [(num-installed [state t] + (count (filter #(is-type? % t) (all-active-installed state :runner))))] + {:async true + :req (req (not-empty (all-active-installed state :runner))) + :prompt "Choose a card type" + :choices ["Hardware" "Program" "Resource"] + :effect (req (let [t target + n (num-installed state t)] + (show-wait-prompt state :corp "Runner to choose cards to trash") + (wait-for + (resolve-ability + state :runner + {:prompt (msg "Choose any number of cards of type " t " to trash") + :choices {:max n + :req #(and (installed? %) (is-type? % t))} + :effect (req (doseq [c targets] + (trash state :runner c {:unpreventable true})) + (gain-credits state :runner (count targets)) + (system-msg state :runner + (str "trashes " + (join ", " (map :title (sort-by :title targets))) + " and gains " (count targets) " [Credits]")) + (effect-completed state side eid))} + card nil) + (clear-wait-prompt state :corp) + (let [n (* 2 (num-installed state t))] + (when (pos? n) + (gain-credits state :corp n) + (system-msg state :corp (str "uses Biased Reporting to gain " n " [Credits]"))) + (effect-completed state side eid)))))}) + + "Big Brother" + {:req (req tagged) + :msg "give the Runner 2 tags" + :async true + :effect (effect (gain-tags :corp eid 2))} + + "Bioroid Efficiency Research" + {:implementation "Derez is manual" + :choices {:req #(and (ice? %) + (has-subtype? % "Bioroid") + (installed? %) + (not (rezzed? %)))} + :msg (msg "rez " (card-str state target {:visible true}) " at no cost") + :effect (effect (rez target {:ignore-cost :all-costs}) + (host (get-card state target) (assoc card :zone [:discard] :seen true :condition true)))} + + "Biotic Labor" + {:msg "gain [Click][Click]" + :effect (effect (gain :click 2))} + + "Blue Level Clearance" + {:msg "gain 5 [Credits] and draw 2 cards" + :async true + :effect (effect (gain-credits 5) + (draw eid 2 nil))} + + "BOOM!" + {:req (req (> (count-tags state) 1)) + :async true + :msg "do 7 meat damage" + :effect (effect (damage eid :meat 7 {:card card}))} + + "Building Blocks" + {:choices {:req #(and (corp? %) + (has-subtype? % "Barrier") + (in-hand? %))} + :async true + :effect (req (corp-install state side eid target nil {:ignore-all-cost true :install-state :rezzed-no-cost}))} + + "Casting Call" + {:choices {:req #(and (agenda? %) + (in-hand? %))} + :async true + :effect (req (let [agenda target] + (continue-ability + state side {:prompt (str "Choose a server to install " (:title agenda)) + :choices (installable-servers state agenda) + :effect (req (corp-install state side agenda target {:install-state :face-up}) + ; find where the agenda ended up and host on it + (let [agenda (some #(when (same-card? % agenda) %) + (all-installed state :corp))] + ; the operation ends up in :discard when it is played; to host it, + ; we need (host) to look for it in discard. + (host state side agenda (assoc card + :zone [:discard] + :seen true + :installed true)) + (system-msg state side (str "hosts Casting Call on " (:title agenda)))))} + card nil))) + :events {:access {:req (req (same-card? target (:host card))) + :async true + :effect (effect (gain-tags :runner eid 2)) :msg "give the Runner 2 tags"}}} + + "Celebrity Gift" + {:choices {:max 5 + :req #(and (corp? %) + (in-hand? %))} + :msg (msg "reveal " (join ", " (map :title (sort-by :title targets))) " and gain " (* 2 (count targets)) " [Credits]") + :effect (effect (reveal targets) (gain-credits (* 2 (count targets))))} + + "Cerebral Cast" + {:req (req (last-turn? state :runner :successful-run)) + :psi {:not-equal {:player :runner + :prompt "Take 1 tag or 1 brain damage?" + :choices ["1 tag" "1 brain damage"] + :msg (msg "give the Runner " target) + :effect (req (if (= target "1 tag") + (gain-tags state :runner eid 1) + (damage state side eid :brain 1 {:card card})))}}} + + "Cerebral Static" + {:msg "disable the Runner's identity" + :effect (effect (disable-identity :runner)) + :leave-play (effect (enable-identity :runner))} + + "\"Clones are not People\"" + {:events {:agenda-scored {:msg "add it to their score area as an agenda worth 1 agenda point" :async true - :choices {:req can-be-advanced?} - :cancel-effect (req (effect-completed state side eid)) - :msg (msg "place an advancement token on " (card-str state target)) - :effect (req (add-prop state :corp target :advance-counter 1 {:placed true}) - (if (< n 2) - (continue-ability state side (au (inc n)) card nil) - (effect-completed state side eid)))})] - {:async true - :req (req (let [h (:hand corp) - p (:play-area corp)] - ;; this is needed to pass the req check for can-play? and again when card is actually played - (if (some #(same-card? % card) p) - (>= (count h) 2) - (>= (count h) 3)))) - :effect (req (system-msg state side "trashes all cards in HQ due to Audacity") - (doseq [c (:hand corp)] - (trash state side c {:unpreventable true})) - (continue-ability state side (audacity 1) card nil))})) - -(define-card "Back Channels" - {:async true - :prompt "Select an installed card in a server to trash" - :choices {:req #(and (= (last (:zone %)) :content) - (is-remote? (second (:zone %))))} - :effect (effect (gain-credits (* 3 (get-counters target :advancement))) - (trash eid target nil)) - :msg (msg "trash " (card-str state target) " and gain " - (* 3 (get-counters target :advancement)) " [Credits]")}) - -(define-card "Bad Times" - {:implementation "Any required program trashing is manual" - :req (req tagged) - :msg "force the Runner to lose 2[mu] until the end of the turn" - :effect (req (lose state :runner :memory 2) - (when (neg? (available-mu state)) - ;; Give runner a toast as well - (toast-check-mu state) - (system-msg state :runner "must trash programs to free up [mu]")) - (register-events - state side - {:corp-turn-ends {:effect (effect (gain :runner :memory 2) - (system-msg :runner "regains 2[mu]") - (unregister-events card))} - :runner-turn-ends {:effect (effect (gain :runner :memory 2) - (system-msg :runner "regains 2[mu]") - (unregister-events card))}} - card)) - :events {:corp-turn-ends nil - :runner-turn-ends nil}}) - -(define-card "Beanstalk Royalties" - {:msg "gain 3 [Credits]" - :effect (effect (gain-credits 3))}) - -(define-card "Best Defense" - {:async true - :req (req (not-empty (all-installed state :runner))) - :effect (req (let [t (count-tags state)] - (continue-ability - state side - {:prompt (msg "Choose a Runner card with an install cost of " t " or less to trash") - :choices {:req #(and (installed? %) - (<= (:cost %) t))} - :msg (msg "trash " (:title target)) - :effect (effect (trash target))} - card nil)))}) - -(define-card "Biased Reporting" - (letfn [(num-installed [state t] - (count (filter #(is-type? % t) (all-active-installed state :runner))))] - {:async true - :req (req (not-empty (all-active-installed state :runner))) - :prompt "Choose a card type" - :choices ["Hardware" "Program" "Resource"] - :effect (req (let [t target - n (num-installed state t)] - (show-wait-prompt state :corp "Runner to choose cards to trash") - (wait-for - (resolve-ability - state :runner - {:prompt (msg "Choose any number of cards of type " t " to trash") - :choices {:max n - :req #(and (installed? %) (is-type? % t))} - :effect (req (doseq [c targets] - (trash state :runner c {:unpreventable true})) - (gain-credits state :runner (count targets)) - (system-msg state :runner - (str "trashes " - (join ", " (map :title (sort-by :title targets))) - " and gains " (count targets) " [Credits]")) - (effect-completed state side eid))} - card nil) - (clear-wait-prompt state :corp) - (let [n (* 2 (num-installed state t))] - (when (pos? n) - (gain-credits state :corp n) - (system-msg state :corp (str "uses Biased Reporting to gain " n " [Credits]"))) - (effect-completed state side eid)))))})) - -(define-card "Big Brother" - {:req (req tagged) - :msg "give the Runner 2 tags" - :async true - :effect (effect (gain-tags :corp eid 2))}) - -(define-card "Bioroid Efficiency Research" - {:implementation "Derez is manual" - :choices {:req #(and (ice? %) - (has-subtype? % "Bioroid") - (installed? %) - (not (rezzed? %)))} - :msg (msg "rez " (card-str state target {:visible true}) " at no cost") - :effect (effect (rez target {:ignore-cost :all-costs}) - (host (get-card state target) (assoc card :zone [:discard] :seen true :condition true)))}) - -(define-card "Biotic Labor" - {:msg "gain [Click][Click]" - :effect (effect (gain :click 2))}) - -(define-card "Blue Level Clearance" - {:msg "gain 5 [Credits] and draw 2 cards" - :async true - :effect (effect (gain-credits 5) - (draw eid 2 nil))}) - -(define-card "BOOM!" - {:req (req (> (count-tags state) 1)) - :async true - :msg "do 7 meat damage" - :effect (effect (damage eid :meat 7 {:card card}))}) - -(define-card "Building Blocks" - {:choices {:req #(and (corp? %) - (has-subtype? % "Barrier") - (in-hand? %))} - :async true - :effect (req (corp-install state side eid target nil {:ignore-all-cost true :install-state :rezzed-no-cost}))}) - -(define-card "Casting Call" - {:choices {:req #(and (agenda? %) - (in-hand? %))} - :async true - :effect (req (let [agenda target] - (continue-ability - state side {:prompt (str "Choose a server to install " (:title agenda)) - :choices (installable-servers state agenda) - :effect (req (corp-install state side agenda target {:install-state :face-up}) - ; find where the agenda ended up and host on it - (let [agenda (some #(when (same-card? % agenda) %) - (all-installed state :corp))] - ; the operation ends up in :discard when it is played; to host it, - ; we need (host) to look for it in discard. - (host state side agenda (assoc card - :zone [:discard] - :seen true - :installed true)) - (system-msg state side (str "hosts Casting Call on " (:title agenda)))))} - card nil))) - :events {:access {:req (req (same-card? target (:host card))) - :async true - :effect (effect (gain-tags :runner eid 2)) :msg "give the Runner 2 tags"}}}) - -(define-card "Celebrity Gift" - {:choices {:max 5 - :req #(and (corp? %) - (in-hand? %))} - :msg (msg "reveal " (join ", " (map :title (sort-by :title targets))) " and gain " (* 2 (count targets)) " [Credits]") - :effect (effect (reveal targets) (gain-credits (* 2 (count targets))))}) - -(define-card "Cerebral Cast" - {:req (req (last-turn? state :runner :successful-run)) - :psi {:not-equal {:player :runner - :prompt "Take 1 tag or 1 brain damage?" - :choices ["1 tag" "1 brain damage"] - :msg (msg "give the Runner " target) - :effect (req (if (= target "1 tag") - (gain-tags state :runner eid 1) - (damage state side eid :brain 1 {:card card})))}}}) - -(define-card "Cerebral Static" - {:msg "disable the Runner's identity" - :effect (effect (disable-identity :runner)) - :leave-play (effect (enable-identity :runner))}) - -(define-card "\"Clones are not People\"" - {:events {:agenda-scored {:msg "add it to their score area as an agenda worth 1 agenda point" - :async true - :effect (req (as-agenda state :corp eid card 1))}}}) - -(define-card "Closed Accounts" - {:req (req tagged) - :msg (msg "force the Runner to lose all " (:credit runner) " [Credits]") - :effect (effect (lose-credits :runner :all))}) - -(define-card "Commercialization" - {:msg (msg "gain " (get-counters target :advancement) " [Credits]") - :choices {:req ice?} - :effect (effect (gain-credits (get-counters target :advancement)))}) + :effect (req (as-agenda state :corp eid card 1))}}} + + "Closed Accounts" + {:req (req tagged) + :msg (msg "force the Runner to lose all " (:credit runner) " [Credits]") + :effect (effect (lose-credits :runner :all))} + + "Commercialization" + {:msg (msg "gain " (get-counters target :advancement) " [Credits]") + :choices {:req ice?} + :effect (effect (gain-credits (get-counters target :advancement)))} + + "Complete Image" + (letfn [(name-a-card [] + {:async true + :prompt "Name a Runner card" + :choices {:card-title (req (and (runner? target) + (not (identity? target))))} + :effect (effect (system-msg (str "uses Complete Image to name " target)) + (continue-ability (damage-ability) card targets))}) + (damage-ability [] + {:async true + :msg "do 1 net damage" + :effect (req (wait-for (damage state side :net 1 {:card card}) + (when-let* [should-continue (not (:winner @state)) + cards (some #(when (same-card? (second %) card) (last %)) + (turn-events state :corp :damage)) + dmg (some #(when (= (:title %) target) %) cards)] + (continue-ability state side (name-a-card) card nil))))})] + {:async true + :req (req (and (last-turn? state :runner :successful-run) + (<= 3 (:agenda-point runner)))) + :effect (effect (continue-ability (name-a-card) card nil))}) + + "Consulting Visit" + {:prompt "Choose an Operation from R&D to play" + :choices (req (cancellable + (filter #(and (operation? %) + (<= (:cost %) (:credit corp))) + (:deck corp)) + :sorted)) + :effect (effect (shuffle! :deck) + (system-msg "shuffles their deck") + (play-instant target)) + :msg (msg "search R&D for " (:title target) " and play it")} + + "Corporate Shuffle" + {:msg "shuffle all cards in HQ into R&D and draw 5 cards" + :async true + :effect (effect (shuffle-into-deck :hand) + (draw eid 5 nil))} + + "Cyberdex Trial" + {:msg "purge virus counters" + :effect (effect (purge))} + + "Death and Taxes" + (let [gain-cred-effect {:msg "gain 1 [Credits]" + :effect (effect (gain-credits :corp 1))}] + {:implementation "Credit gain mandatory to save on wait-prompts, adjust credits manually if credit not wanted." + :events {:runner-install gain-cred-effect + :runner-trash (assoc gain-cred-effect :req (req (installed? target)))}}) + + "Dedication Ceremony" + {:prompt "Select a faceup card" + :choices {:req #(or (and (corp? %) + (:rezzed %)) + (and (runner? %) + (or (installed? %) + (:host %)) + (not (facedown? %))))} + :msg (msg "place 3 advancement tokens on " (card-str state target)) + :effect (req (add-prop state :corp target :advance-counter 3 {:placed true}) + (effect-completed state side eid) + (let [tgtcid (:cid target)] + (register-turn-flag! + state side + target :can-score + (fn [state side card] + (if (and (= (:cid card) tgtcid) + (>= (get-counters card :advancement) (or (:current-cost card) + (:advancementcost card)))) + ((constantly false) (toast state :corp "Cannot score due to Dedication Ceremony." "warning")) + true)))))} + + "Defective Brainchips" + {:events {:pre-damage {:req (req (= target :brain)) + :msg "do 1 additional brain damage" + :once :per-turn + :effect (effect (damage-bonus :brain 1))}}} + + "Distract the Masses" + (let [shuffle-two {:async true + :effect (effect (rfg-and-shuffle-rd-effect (find-cid (:cid card) (:discard corp)) 2))} + trash-from-hq {:async true + :prompt "Select up to 2 cards in HQ to trash" + :choices {:max 2 + :req #(and (corp? %) + (in-hand? %))} + :msg (msg "trash " (quantify (count targets) "card") " from HQ") + :effect (req (wait-for + (trash-cards state side targets nil) + (continue-ability state side shuffle-two card nil))) + :cancel-effect (req (continue-ability state side shuffle-two card nil))}] + {:async true + :msg "give The Runner 2 [Credits]" + :effect (effect (gain-credits :runner 2) + (continue-ability trash-from-hq card nil))}) + + "Diversified Portfolio" + (letfn [(number-of-non-empty-remotes [state] + (count (filter #(not (empty? %)) + (map #(:content (second %)) + (get-remotes state)))))] + {:msg (msg "gain " (number-of-non-empty-remotes state) + " [Credits]") + :effect (effect (gain-credits (number-of-non-empty-remotes state)))}) -(define-card "Complete Image" - (letfn [(name-a-card [] - {:async true - :prompt "Name a Runner card" - :choices {:card-title (req (and (runner? target) - (not (identity? target))))} - :effect (effect (system-msg (str "uses Complete Image to name " target)) - (continue-ability (damage-ability) card targets))}) - (damage-ability [] + "Divert Power" + {:async true + :prompt "Select any number of cards to derez" + :choices {:req #(and (installed? %) + (rezzed? %)) + :max (req (count (filter rezzed? (all-installed state :corp))))} + :effect (req (doseq [c targets] + (derez state side c)) + (let [discount (* -3 (count targets))] + (continue-ability + state side + {:async true + :prompt "Select a card to rez" + :choices {:req #(and (installed? %) + (corp? %) + (not (rezzed? %)) + (not (agenda? %)))} + :effect (effect (rez-cost-bonus discount) + (rez eid target nil))} + card nil)))} + + "Door to Door" + {:events {:runner-turn-begins + {:trace {:base 1 + :label "Do 1 meat damage if Runner is tagged, or give the Runner 1 tag" + :successful {:msg (msg (if tagged + "do 1 meat damage" + "give the Runner 1 tag")) + :async true + :effect (req (if tagged + (damage state side eid :meat 1 {:card card}) + (gain-tags state :corp eid 1)))}}}}} + + "Eavesdrop" + (let [new-sub {:label "[Eavesdrop]: Trace 3 - Give the Runner 1 tag"}] + {:implementation "On encounter effect is manual" + :sub-effect {:label "Give the Runner 1 tag" + :trace {:base 3 + :successful {:msg "give the Runner 1 tag" + :async true + :effect (effect (gain-tags :runner eid 1))}}} + :choices {:req #(and (ice? %) + (installed? %))} + :msg (msg "give " (card-str state target {:visible false}) " additional text") + :effect (req (add-extra-sub state :corp (:cid card) (get-card state target) -1 new-sub) + (update-ice-strength state side target) + (host state side (get-card state target) (assoc card :zone [:discard] :seen true :condition true))) + :leave-play (req (remove-extra-subs state :corp (:cid card) (:host card))) + :events {:rez {:req (req (same-card? target (:host card))) + :effect (req (add-extra-sub state :corp (:cid card) (get-card state target) -1 new-sub))}}}) + + "Economic Warfare" + {:req (req (and (last-turn? state :runner :successful-run) + (can-pay? state :runner eid card nil :credit 4))) + :msg "make the runner lose 4 [Credits]" + :effect (effect (lose-credits :runner 4))} + + "Election Day" + {:req (req (->> (get-in @state [:corp :hand]) + (filter #(not (same-card? % card))) + count + pos?)) + :async true + :msg (msg "trash all cards in HQ and draw 5 cards") + :effect (effect (trash-cards (get-in @state [:corp :hand])) + (draw eid 5 nil))} + + "Enforced Curfew" + {:msg "reduce the Runner's maximum hand size by 1" + :effect (effect (lose :runner :hand-size 1)) + :leave-play (effect (gain :runner :hand-size 1))} + + "Enforcing Loyalty" + {:trace {:base 3 + :label "Trash a card not matching the faction of the Runner's identity" + :successful {:async true - :msg "do 1 net damage" - :effect (req (wait-for (damage state side :net 1 {:card card}) - (when-let* [should-continue (not (:winner @state)) - cards (some #(when (same-card? (second %) card) (last %)) - (turn-events state :corp :damage)) - dmg (some #(when (= (:title %) target) %) cards)] - (continue-ability state side (name-a-card) card nil))))})] - {:async true - :req (req (and (last-turn? state :runner :successful-run) - (<= 3 (:agenda-point runner)))) - :effect (effect (continue-ability (name-a-card) card nil))})) - -(define-card "Consulting Visit" - {:prompt "Choose an Operation from R&D to play" - :choices (req (cancellable - (filter #(and (operation? %) - (<= (:cost %) (:credit corp))) - (:deck corp)) - :sorted)) - :effect (effect (shuffle! :deck) - (system-msg "shuffles their deck") - (play-instant target)) - :msg (msg "search R&D for " (:title target) " and play it")}) - -(define-card "Corporate Shuffle" - {:msg "shuffle all cards in HQ into R&D and draw 5 cards" - :async true - :effect (effect (shuffle-into-deck :hand) - (draw eid 5 nil))}) - -(define-card "Cyberdex Trial" - {:msg "purge virus counters" - :effect (effect (purge))}) - -(define-card "Death and Taxes" - (let [gain-cred-effect {:msg "gain 1 [Credits]" - :effect (effect (gain-credits :corp 1))}] - {:implementation "Credit gain mandatory to save on wait-prompts, adjust credits manually if credit not wanted." - :events {:runner-install gain-cred-effect - :runner-trash (assoc gain-cred-effect :req (req (installed? target)))}})) - -(define-card "Dedication Ceremony" - {:prompt "Select a faceup card" - :choices {:req #(or (and (corp? %) - (:rezzed %)) - (and (runner? %) - (or (installed? %) - (:host %)) - (not (facedown? %))))} - :msg (msg "place 3 advancement tokens on " (card-str state target)) - :effect (req (add-prop state :corp target :advance-counter 3 {:placed true}) - (effect-completed state side eid) - (let [tgtcid (:cid target)] - (register-turn-flag! - state side - target :can-score - (fn [state side card] - (if (and (= (:cid card) tgtcid) - (>= (get-counters card :advancement) (or (:current-cost card) - (:advancementcost card)))) - ((constantly false) (toast state :corp "Cannot score due to Dedication Ceremony." "warning")) - true)))))}) - -(define-card "Defective Brainchips" - {:events {:pre-damage {:req (req (= target :brain)) - :msg "do 1 additional brain damage" - :once :per-turn - :effect (effect (damage-bonus :brain 1))}}}) - -(define-card "Distract the Masses" - (let [shuffle-two {:async true - :effect (effect (rfg-and-shuffle-rd-effect (find-cid (:cid card) (:discard corp)) 2))} - trash-from-hq {:async true - :prompt "Select up to 2 cards in HQ to trash" - :choices {:max 2 - :req #(and (corp? %) - (in-hand? %))} - :msg (msg "trash " (quantify (count targets) "card") " from HQ") - :effect (req (wait-for - (trash-cards state side targets nil) - (continue-ability state side shuffle-two card nil))) - :cancel-effect (req (continue-ability state side shuffle-two card nil))}] - {:async true - :msg "give The Runner 2 [Credits]" - :effect (effect (gain-credits :runner 2) - (continue-ability trash-from-hq card nil))})) - -(define-card "Diversified Portfolio" - (letfn [(number-of-non-empty-remotes [state] - (count (filter #(not (empty? %)) - (map #(:content (second %)) - (get-remotes state)))))] - {:msg (msg "gain " (number-of-non-empty-remotes state) - " [Credits]") - :effect (effect (gain-credits (number-of-non-empty-remotes state)))})) - -(define-card "Divert Power" - {:async true - :prompt "Select any number of cards to derez" - :choices {:req #(and (installed? %) - (rezzed? %)) - :max (req (count (filter rezzed? (all-installed state :corp))))} - :effect (req (doseq [c targets] - (derez state side c)) - (let [discount (* -3 (count targets))] - (continue-ability - state side - {:async true - :prompt "Select a card to rez" - :choices {:req #(and (installed? %) - (corp? %) - (not (rezzed? %)) - (not (agenda? %)))} - :effect (effect (rez-cost-bonus discount) - (rez eid target nil))} - card nil)))}) - -(define-card "Door to Door" - {:events {:runner-turn-begins - {:trace {:base 1 - :label "Do 1 meat damage if Runner is tagged, or give the Runner 1 tag" - :successful {:msg (msg (if tagged - "do 1 meat damage" - "give the Runner 1 tag")) - :async true - :effect (req (if tagged - (damage state side eid :meat 1 {:card card}) - (gain-tags state :corp eid 1)))}}}}}) - -(define-card "Eavesdrop" - (let [new-sub {:label "[Eavesdrop]: Trace 3 - Give the Runner 1 tag"}] - {:implementation "On encounter effect is manual" - :sub-effect {:label "Give the Runner 1 tag" - :trace {:base 3 - :successful {:msg "give the Runner 1 tag" - :async true - :effect (effect (gain-tags :runner eid 1))}}} - :choices {:req #(and (ice? %) - (installed? %))} - :msg (msg "give " (card-str state target {:visible false}) " additional text") - :effect (req (add-extra-sub state :corp (:cid card) (get-card state target) -1 new-sub) - (update-ice-strength state side target) - (host state side (get-card state target) (assoc card :zone [:discard] :seen true :condition true))) - :leave-play (req (remove-extra-subs state :corp (:cid card) (:host card))) - :events {:rez {:req (req (same-card? target (:host card))) - :effect (req (add-extra-sub state :corp (:cid card) (get-card state target) -1 new-sub))}}})) - -(define-card "Economic Warfare" - {:req (req (and (last-turn? state :runner :successful-run) - (can-pay? state :runner eid card nil :credit 4))) - :msg "make the runner lose 4 [Credits]" - :effect (effect (lose-credits :runner 4))}) - -(define-card "Election Day" - {:req (req (->> (get-in @state [:corp :hand]) - (filter #(not (same-card? % card))) - count - pos?)) - :async true - :msg (msg "trash all cards in HQ and draw 5 cards") - :effect (effect (trash-cards (get-in @state [:corp :hand])) - (draw eid 5 nil))}) - -(define-card "Enforced Curfew" - {:msg "reduce the Runner's maximum hand size by 1" - :effect (effect (lose :runner :hand-size 1)) - :leave-play (effect (gain :runner :hand-size 1))}) - -(define-card "Enforcing Loyalty" - {:trace {:base 3 - :label "Trash a card not matching the faction of the Runner's identity" - :successful - {:async true - :effect (req (let [f (:faction (:identity runner))] - (continue-ability - state side - {:prompt "Select an installed card not matching the faction of the Runner's identity" - :choices {:req #(and (installed? %) - (not= f (:faction %)) - (runner? %))} - :msg (msg "trash " (:title target)) - :effect (effect (trash target))} - card nil)))}}}) - -(define-card "Enhanced Login Protocol" - {:msg "uses Enhanced Login Protocol to add an additional cost of [Click] to make the first run not through a card ability this turn" - :events {:pre-init-run {:req (req (and (first-event? state side :pre-init-run #(= :click-run (second %))) - (= :click-run (second targets)))) - :effect (effect (run-additional-cost-bonus [:click 1]))}}}) - -(define-card "Exchange of Information" - {:req (req (and tagged - (seq (:scored runner)) - (seq (:scored corp)))) - :async true - :effect (req - (continue-ability - state side - {:prompt "Select a stolen agenda in the Runner's score area to swap" - :choices {:req #(in-runner-scored? state side %)} - :async true - :effect (req - (let [stolen target] + :effect (req (let [f (:faction (:identity runner))] (continue-ability state side - {:prompt (msg "Select a scored agenda to swap for " (:title stolen)) - :choices {:req #(in-corp-scored? state side %)} - :effect (req (let [scored target] - (swap-agendas state side scored stolen) - (system-msg state side (str "uses Exchange of Information to swap " - (:title scored) " for " (:title stolen))) - (effect-completed state side eid)))} - card nil)))} - card nil))}) - -(define-card "Fast Break" - {:async true - :req (req (-> runner :scored count pos?)) - :effect - (req (let [X (-> runner :scored count) - draw {:async true - :prompt "Draw how many cards?" - :choices {:number (req X) - :max (req X) - :default (req 1)} - :msg (msg "draw " target " cards") - :effect (effect (draw eid target nil))} - install-cards (fn install-cards - [server n] - {:prompt "Select a card to install" - :choices {:req #(and (corp? %) - (not (operation? %)) - (in-hand? %) - (seq (filter (fn [c] (= server c)) (corp-install-list state %))))} - :effect (req (wait-for - (corp-install state side target server nil) - (let [server (if (= "New remote" server) - (-> (turn-events state side :corp-install) - ffirst :zone second zone->name) - server)] - (if (< n X) - (continue-ability state side (install-cards server (inc n)) card nil) - (effect-completed state side eid))))) - :cancel-effect (effect (effect-completed eid))}) - select-server {:async true - :prompt "Install cards in which server?" - :choices (req (conj (vec (get-remote-names state)) "New remote")) - :effect (effect (continue-ability (install-cards target 1) card nil))}] - (gain-credits state :corp X) - (wait-for (resolve-ability state side draw card nil) - (continue-ability state side select-server card nil))))}) - -(define-card "Fast Track" - {:prompt "Choose an Agenda" - :choices (req (cancellable (filter agenda? (:deck corp)) :sorted)) - :effect (effect (system-msg (str "adds " (:title target) " to HQ and shuffle R&D")) - (reveal target) - (shuffle! :deck) - (move target :hand))}) - -(define-card "Financial Collapse" - {:async true - :req (req (and (>= (:credit runner) 6) (seq (filter resource? (all-active-installed state :runner))))) - :effect (req (let [rcount (count (filter resource? (all-active-installed state :runner)))] - (if (pos? rcount) - (do (show-wait-prompt state :corp "Runner to trash a resource to prevent Financial Collapse") - (continue-ability - state side - {:prompt (msg "Trash a resource to prevent Financial Collapse?") - :choices ["Yes" "No"] :player :runner - :async true - :effect (effect (continue-ability - (if (= target "Yes") - {:player :runner - :prompt "Select a resource to trash" - :choices {:req #(and (resource? %) (installed? %))} - :effect (req (trash state side target {:unpreventable true}) - (system-msg state :runner - (str "trashes " (:title target) - " to prevent Financial Collapse")) - (clear-wait-prompt state :corp))} - {:effect (effect (lose-credits :runner (* rcount 2)) - (clear-wait-prompt :corp)) - :msg (msg "make the Runner lose " (* rcount 2) " [Credits]")}) - card nil))} card nil)) - (continue-ability - state side - {:effect (effect (lose-credits :runner (* rcount 2))) - :msg (msg "make the Runner lose " (* rcount 2) " [Credits]")} card nil))))}) - -(define-card "Focus Group" - {:req (req (last-turn? state :runner :successful-run)) - :async true - :prompt "Choose a card type" - :choices ["Event" "Hardware" "Program" "Resource"] - :effect (req (let [type target - numtargets (count (filter #(= type (:type %)) (:hand runner)))] - (system-msg - state :corp - (str "uses Focus Group to choose " target " and reveal the Runner's Grip ( " - (join ", " (map :title (sort-by :title (:hand runner)))) " )")) - (reveal state side (:hand runner)) - (if (pos? numtargets) + {:prompt "Select an installed card not matching the faction of the Runner's identity" + :choices {:req #(and (installed? %) + (not= f (:faction %)) + (runner? %))} + :msg (msg "trash " (:title target)) + :effect (effect (trash target))} + card nil)))}}} + + "Enhanced Login Protocol" + {:msg "uses Enhanced Login Protocol to add an additional cost of [Click] to make the first run not through a card ability this turn" + :events {:pre-init-run {:req (req (and (first-event? state side :pre-init-run #(= :click-run (second %))) + (= :click-run (second targets)))) + :effect (effect (run-additional-cost-bonus [:click 1]))}}} + + "Exchange of Information" + {:req (req (and tagged + (seq (:scored runner)) + (seq (:scored corp)))) + :async true + :effect (req + (continue-ability + state side + {:prompt "Select a stolen agenda in the Runner's score area to swap" + :choices {:req #(in-runner-scored? state side %)} + :async true + :effect (req + (let [stolen target] + (continue-ability + state side + {:prompt (msg "Select a scored agenda to swap for " (:title stolen)) + :choices {:req #(in-corp-scored? state side %)} + :effect (req (let [scored target] + (swap-agendas state side scored stolen) + (system-msg state side (str "uses Exchange of Information to swap " + (:title scored) " for " (:title stolen))) + (effect-completed state side eid)))} + card nil)))} + card nil))} + + "Fast Break" + {:async true + :req (req (-> runner :scored count pos?)) + :effect + (req (let [X (-> runner :scored count) + draw {:async true + :prompt "Draw how many cards?" + :choices {:number (req X) + :max (req X) + :default (req 1)} + :msg (msg "draw " target " cards") + :effect (effect (draw eid target nil))} + install-cards (fn install-cards + [server n] + {:prompt "Select a card to install" + :choices {:req #(and (corp? %) + (not (operation? %)) + (in-hand? %) + (seq (filter (fn [c] (= server c)) (corp-install-list state %))))} + :effect (req (wait-for + (corp-install state side target server nil) + (let [server (if (= "New remote" server) + (-> (turn-events state side :corp-install) + ffirst :zone second zone->name) + server)] + (if (< n X) + (continue-ability state side (install-cards server (inc n)) card nil) + (effect-completed state side eid))))) + :cancel-effect (effect (effect-completed eid))}) + select-server {:async true + :prompt "Install cards in which server?" + :choices (req (conj (vec (get-remote-names state)) "New remote")) + :effect (effect (continue-ability (install-cards target 1) card nil))}] + (gain-credits state :corp X) + (wait-for (resolve-ability state side draw card nil) + (continue-ability state side select-server card nil))))} + + "Fast Track" + {:prompt "Choose an Agenda" + :choices (req (cancellable (filter agenda? (:deck corp)) :sorted)) + :effect (effect (system-msg (str "adds " (:title target) " to HQ and shuffle R&D")) + (reveal target) + (shuffle! :deck) + (move target :hand))} + + "Financial Collapse" + {:async true + :req (req (and (>= (:credit runner) 6) (seq (filter resource? (all-active-installed state :runner))))) + :effect (req (let [rcount (count (filter resource? (all-active-installed state :runner)))] + (if (pos? rcount) + (do (show-wait-prompt state :corp "Runner to trash a resource to prevent Financial Collapse") + (continue-ability + state side + {:prompt (msg "Trash a resource to prevent Financial Collapse?") + :choices ["Yes" "No"] :player :runner + :async true + :effect (effect (continue-ability + (if (= target "Yes") + {:player :runner + :prompt "Select a resource to trash" + :choices {:req #(and (resource? %) (installed? %))} + :effect (req (trash state side target {:unpreventable true}) + (system-msg state :runner + (str "trashes " (:title target) + " to prevent Financial Collapse")) + (clear-wait-prompt state :corp))} + {:effect (effect (lose-credits :runner (* rcount 2)) + (clear-wait-prompt :corp)) + :msg (msg "make the Runner lose " (* rcount 2) " [Credits]")}) + card nil))} card nil)) + (continue-ability + state side + {:effect (effect (lose-credits :runner (* rcount 2))) + :msg (msg "make the Runner lose " (* rcount 2) " [Credits]")} card nil))))} + + "Focus Group" + {:req (req (last-turn? state :runner :successful-run)) + :async true + :prompt "Choose a card type" + :choices ["Event" "Hardware" "Program" "Resource"] + :effect (req (let [type target + numtargets (count (filter #(= type (:type %)) (:hand runner)))] + (system-msg + state :corp + (str "uses Focus Group to choose " target " and reveal the Runner's Grip ( " + (join ", " (map :title (sort-by :title (:hand runner)))) " )")) + (reveal state side (:hand runner)) + (if (pos? numtargets) + (continue-ability + state :corp + {:async true + :prompt "Pay how many credits?" + :choices {:number (req numtargets)} + :effect (req (let [c target] + (if (can-pay? state side eid card (:title card) :credit c) + (do (pay state :corp card :credit c) + (continue-ability + state :corp + {:msg (msg "place " (quantify c " advancement token") " on " + (card-str state target)) + :choices {:req installed?} + :effect (effect (add-prop target :advance-counter c {:placed true}))} + card nil)) + (effect-completed state side eid))))} + card nil) + (effect-completed state side eid))))} + + "Foxfire" + {:trace {:base 7 + :successful {:prompt "Select 1 card to trash" + :not-distinct true + :choices {:req #(and (installed? %) + (or (has-subtype? % "Virtual") + (has-subtype? % "Link")))} + :msg "trash 1 virtual resource or link" + :effect (effect (trash target) + (system-msg (str "trashes " (:title target))))}}} + + "Freelancer" + {:req (req tagged) + :msg (msg "trash " (join ", " (map :title (sort-by :title targets)))) + :choices {:max 2 + :req #(and (installed? %) + (resource? %))} + :effect (effect (trash-cards :runner targets))} + + "Friends in High Places" + (let [fhelper (fn fhp [n] {:prompt "Select a card in Archives to install with Friends in High Places" + :priority -1 + :async true + :show-discard true + :choices {:req #(and (corp? %) + (not (operation? %)) + (in-discard? %))} + :effect (req (wait-for + (corp-install state side target nil nil) + (do (system-msg state side (str "uses Friends in High Places to " + (corp-install-msg target))) + (if (< n 2) + (continue-ability state side (fhp (inc n)) card nil) + (effect-completed state side eid)))))})] + {:async true + :effect (effect (continue-ability (fhelper 1) card nil))}) + + "Fully Operational" + (letfn [(full-servers [state] + (filter #(and (not-empty (:content %)) + (not-empty (:ices %))) + (vals (get-remotes state)))) + (repeat-choice [current total] + ;; if current > total, this ability will auto-resolve and finish the chain of async methods. + (when (<= current total) + {:async true + :prompt (str "Choice " current " of " total ": Gain 2 [Credits] or draw 2 cards? ") + :choices ["Gain 2 [Credits]" "Draw 2 cards"] + :msg (msg (lower-case target)) + :effect (req (if (= target "Gain 2 [Credits]") + (do (gain-credits state :corp 2) + (continue-ability state side (repeat-choice (inc current) total) + card nil)) + (wait-for (draw state :corp 2 nil) ; don't proceed with the next choice until the draw is done + (continue-ability state side (repeat-choice (inc current) total) + card nil))))}))] + {:async true + :msg (msg "uses Fully Operational to make " (quantify (inc (count (full-servers state))) "gain/draw decision")) + :effect (effect (continue-ability (repeat-choice 1 (inc (count (full-servers state)))) + card nil))}) + + "Game Changer" + {:effect (req (gain state side :click (count (:scored runner))) + (move state side (first (:play-area corp)) :rfg))} + + "Game Over" + {:req (req (last-turn? state :runner :stole-agenda)) + :async true + :prompt "Choose a card type" + :choices ["Hardware" "Program" "Resource"] + :effect (req (let [type target + trashtargets (filter #(and (is-type? % type) + (not (has? % :subtype "Icebreaker"))) + (all-active-installed state :runner)) + numtargets (count trashtargets) + typemsg (str (when (= type "Program") "non-Icebreaker ") type + (when-not (= type "Hardware") "s"))] + (system-msg state :corp (str "chooses to trash all " typemsg)) + (show-wait-prompt state :corp "Runner to prevent trashes") + (wait-for + (resolve-ability + state :runner + {:async true + :req (req (<= 3 (:credit runner))) + :prompt (msg "Prevent any " typemsg " from being trashed? Pay 3 [Credits] per card.") + :choices {:max (req (min numtargets (quot (:credit runner) 3))) + :req #(and (installed? %) + (is-type? % type) + (not (has? % :subtype "Icebreaker")))} + :effect (req (pay state :runner card :credit (* 3 (count targets))) + (system-msg + state :runner + (str "pays " (* 3 (count targets)) " [Credits] to prevent the trashing of " + (join ", " (map :title (sort-by :title targets))))) + (system-msg state :corp (str "trashes all other " typemsg)) + (effect-completed state side (make-result eid targets)))} + card nil) + (trash-cards state side (clojure.set/difference (set trashtargets) (set async-result))) + (clear-wait-prompt state :corp) + (gain-bad-publicity state :corp 1) + (system-msg state :corp "take 1 bad publicity") + (effect-completed state side eid))))} + + "Genotyping" + {:async true + :effect (effect (mill :corp 2) + (system-msg "trashes the top 2 cards of R&D") + (rfg-and-shuffle-rd-effect eid (first (:play-area corp)) 4 false))} + + "Green Level Clearance" + {:msg "gain 3 [Credits] and draw 1 card" + :async true + :effect (effect (gain-credits 3) + (draw eid 1 nil))} + + "Hangeki" + {:req (req (last-turn? state :runner :trashed-card)) + :async true + :prompt "Choose an installed Corp card" + :choices {:req #(and (corp? %) + (installed? %))} + :effect (effect (show-wait-prompt :corp "Runner to resolve Hangeki") (continue-ability - state :corp - {:async true - :prompt "Pay how many credits?" - :choices {:number (req numtargets)} - :effect (req (let [c target] - (if (can-pay? state side eid card (:title card) :credit c) - (do (pay state :corp card :credit c) - (continue-ability - state :corp - {:msg (msg "place " (quantify c " advancement token") " on " - (card-str state target)) - :choices {:req installed?} - :effect (effect (add-prop target :advance-counter c {:placed true}))} - card nil)) - (effect-completed state side eid))))} - card nil) - (effect-completed state side eid))))}) - -(define-card "Foxfire" - {:trace {:base 7 - :successful {:prompt "Select 1 card to trash" - :not-distinct true - :choices {:req #(and (installed? %) - (or (has-subtype? % "Virtual") - (has-subtype? % "Link")))} - :msg "trash 1 virtual resource or link" - :effect (effect (trash target) - (system-msg (str "trashes " (:title target))))}}}) - -(define-card "Freelancer" - {:req (req tagged) - :msg (msg "trash " (join ", " (map :title (sort-by :title targets)))) - :choices {:max 2 - :req #(and (installed? %) - (resource? %))} - :effect (effect (trash-cards :runner targets))}) - -(define-card "Friends in High Places" - (let [fhelper (fn fhp [n] {:prompt "Select a card in Archives to install with Friends in High Places" - :priority -1 + {:optional + {:player :runner + :async true + :prompt "Access card? (If not, add Hangeki to your score area worth -1 agenda point)" + :yes-ability + {:effect (req (clear-wait-prompt state :corp) + (wait-for (access-card state side target) + (move state :corp (find-latest state card) :rfg) + (system-msg state :corp "removes Hangeki from the game") + (effect-completed state side eid)))} + :no-ability + {:msg "add it to the Runner's score area as an agenda worth -1 agenda point" + :effect (effect (clear-wait-prompt :corp) + (as-agenda :runner eid (find-latest state card) -1))}}} + card targets))} + + "Hard-Hitting News" + {:req (req (last-turn? state :runner :made-run)) + :trace {:base 4 + :label "Give the Runner 4 tags" + :successful {:async true + :msg "give the Runner 4 tags" + :effect (effect (gain-tags eid 4))}}} + + "Hasty Relocation" + (letfn [(hr-final [chosen original] + {:prompt (str "The top cards of R&D will be " (clojure.string/join ", " (map :title chosen)) ".") + :choices ["Done" "Start over"] + :async true + :effect (req (if (= target "Done") + (do (doseq [c (reverse chosen)] (move state :corp c :deck {:front true})) + (clear-wait-prompt state :runner) + (effect-completed state side eid)) + (continue-ability state side (hr-choice original '() 3 original) + card nil)))}) + (hr-choice [remaining chosen n original] + {:prompt "Choose a card to move next onto R&D" + :choices remaining + :async true + :effect (req (let [chosen (cons target chosen)] + (if (< (count chosen) n) + (continue-ability state side (hr-choice (remove-once #(= target %) remaining) + chosen n original) card nil) + (continue-ability state side (hr-final chosen original) card nil))))})] + {:additional-cost [:mill 1] + :async true + :msg "trash the top card of R&D, draw 3 cards, and add 3 cards in HQ to the top of R&D" + :effect (req (wait-for (draw state side 3 nil) + (do (show-wait-prompt state :runner "Corp to add 3 cards in HQ to the top of R&D") + (let [from (get-in @state [:corp :hand])] + (continue-ability state :corp (hr-choice from '() 3 from) card nil)))))}) + + "Hatchet Job" + {:trace {:base 5 + :successful {:choices {:req #(and (installed? %) + (runner? %) + (not (has-subtype? % "Virtual")))} + :msg "add an installed non-virtual card to the Runner's grip" + :effect (effect (move :runner target :hand true))}}} + + "Hedge Fund" + {:msg "gain 9 [Credits]" :effect (effect (gain-credits 9))} + + "Hellion Alpha Test" + {:req (req (last-turn? state :runner :installed-resource)) + :trace {:base 2 + :successful {:msg "add a Resource to the top of the Stack" + :choices {:req #(and (installed? %) + (resource? %))} + :effect (effect (move :runner target :deck {:front true}) + (system-msg (str "adds " (:title target) " to the top of the Stack")))} + :unsuccessful {:msg "take 1 bad publicity" + :effect (effect (gain-bad-publicity :corp 1))}}} + + "Hellion Beta Test" + {:req (req (last-turn? state :runner :trashed-card)) + :trace {:base 2 + :label "Trace 2 - Trash 2 installed non-program cards or take 1 bad publicity" + :successful {:choices {:max (req (min 2 (count (filter #(or (facedown? %) + (not (program? %))) + (concat (all-installed state :corp) + (all-installed state :runner)))))) + :all true + :req #(and (installed? %) + (not (program? %)))} + :msg (msg "trash " (join ", " (map :title (sort-by :title targets)))) + :effect (req (doseq [c targets] + (trash state side c)))} + :unsuccessful {:msg "take 1 bad publicity" + :effect (effect (gain-bad-publicity :corp 1))}}} + + "Heritage Committee" + {:async true + :effect (req (wait-for (draw state side 3 nil) + (continue-ability state side + {:prompt "Select a card in HQ to put on top of R&D" + :choices {:req #(and (corp? %) + (in-hand? %))} + :msg "draw 3 cards and add 1 card from HQ to the top of R&D" + :effect (effect (move target :deck {:front true}))} + card nil)))} + + "High-Profile Target" + (letfn [(dmg-count [state] (* 2 (count-tags state)))] + {:req (req tagged) + :async true + :msg (msg "do " (dmg-count state) " meat damage") + :effect (effect (damage eid :meat (dmg-count state) {:card card}))}) + + "Housekeeping" + {:events {:runner-install {:player :runner + :prompt "Select a card from your Grip to trash for Housekeeping" :once :per-turn + :choices {:req #(and (runner? %) + (in-hand? %))} + :msg (msg "force the Runner to trash " (:title target) " from their Grip") + :effect (effect (trash target {:unpreventable true}))}}} + + "Hunter Seeker" + {:req (req (last-turn? state :runner :stole-agenda)) + :async true + :prompt "Choose a card to trash" + :choices {:req installed?} + :msg (msg "trash " (card-str state target)) + :effect (effect (trash target))} + + "Interns" + {:prompt "Select a card to install from Archives or HQ" + :show-discard true + :not-distinct true + :choices {:req #(and (not (operation? %)) + (corp? %) + (#{[:hand] [:discard]} (:zone %)))} + :effect (effect (corp-install target nil {:ignore-install-cost true})) + :msg (msg (corp-install-msg target))} + + "Invasion of Privacy" + (letfn [(iop [x] + {:async true + :req (req (->> (:hand runner) + (filter #(or (resource? %) + (event? %))) + count + pos?)) + :prompt "Choose a resource or event to trash" + :msg (msg "trash " (:title target)) + :choices (req (cancellable + (filter #(or (resource? %) + (event? %)) + (:hand runner)) + :sorted)) + :effect (req (trash state side target) + (if (pos? x) + (continue-ability state side (iop (dec x)) card nil) + (effect-completed state side eid)))})] + {:trace {:base 2 + :successful {:msg "reveal the Runner's Grip and trash up to X resources or events" + :effect (req (reveal state side (:hand runner)) + (let [x (- target (second targets))] + (system-msg + state :corp + (str "reveals the Runner's Grip ( " + (join ", " (map :title (sort-by :title (:hand runner)))) + " ) and can trash up to " x " resources or events")) + (continue-ability state side (iop (dec x)) card nil)))} + :unsuccessful {:msg "take 1 bad publicity" + :effect (effect (gain-bad-publicity :corp 1))}}}) + + "IPO" + {:msg "gain 13 [Credits]" :effect (effect (gain-credits 13))} + + "Kill Switch" + (let [trace-for-brain-damage {:msg (msg "reveal that they accessed " (:title target)) + :trace {:base 3 + :successful {:msg "do 1 brain damage" + :async true + :effect (effect (damage :runner eid :brain 1 {:card card}))}}}] + {:events {:access (assoc trace-for-brain-damage :req (req (agenda? target)) + :interactive (req (agenda? target))) + :agenda-scored trace-for-brain-damage}}) + + "Lag Time" + {:effect (effect (update-all-ice)) + :events {:pre-ice-strength {:effect (effect (ice-strength-bonus 1 target))}} + :leave-play (effect (update-all-ice))} + + "Lateral Growth" + {:async true + :msg "gain 4 [Credits]" + :effect (effect (gain-credits 4) + (continue-ability {:player :corp + :prompt "Select a card to install" + :choices {:req #(and (corp? %) + (not (operation? %)) + (in-hand? %))} + :async true + :msg (msg (corp-install-msg target)) + :effect (effect (corp-install eid target nil nil))} + card nil))} + + "Liquidation" + {:async true + :req (req (some #(and (rezzed? %) (not (agenda? %))) (all-installed state :corp))) + :effect (req (let [n (count (filter #(not (agenda? %)) (all-active-installed state :corp)))] + (continue-ability + state side + {:prompt "Select any number of rezzed cards to trash" + :choices {:max n + :req #(and (rezzed? %) + (not (agenda? %)))} + :msg (msg "trash " (join ", " (map :title (sort-by :title targets))) " and gain " (* (count targets) 3) " [Credits]") + :effect (req (doseq [c targets] + (trash state side c)) + (gain-credits state side (* (count targets) 3)))} + card nil)))} + + "Load Testing" + {:msg "make the Runner lose [Click] when their next turn begins" + :effect (effect (register-events (:events (card-def card)) + (assoc card :zone '(:discard)))) + :events {:runner-turn-begins {:msg "make the Runner lose [Click]" + :effect (effect (lose :runner :click 1) + (unregister-events card))}}} + + "Localized Product Line" + {:prompt "Choose a card" + :choices (req (cancellable (:deck corp) :sorted)) + :async true + :effect (req (let [c (:title target) + cs (filter #(= (:title %) c) (:deck corp))] + (continue-ability + state side + {:prompt "How many copies?" + :choices {:number (req (count cs))} + :msg (msg "add " (quantify target "cop" "y" "ies") " of " c " to HQ") + :effect (req (shuffle! state :corp :deck) + (doseq [c (take target cs)] + (move state side c :hand)))} + card nil)))} + + "Manhunt" + {:events {:successful-run {:interactive (req true) + :req (req (first-event? state side :successful-run)) + :trace {:base 2 + :successful {:msg "give the Runner 1 tag" + :async true + :effect (effect (gain-tags eid 1))}}}}} + + "Market Forces" + (letfn [(credit-diff [state] + (min (* 3 (count-tags state)) + (get-in @state [:runner :credit])))] + {:req (req tagged) + :msg (msg (let [c (credit-diff state)] + (str "make the runner lose " c " [Credits], and gain " c " [Credits]"))) + :effect (req (let [c (credit-diff state)] + (lose-credits state :runner c) + (gain-credits state :corp c)))}) + + "Mass Commercialization" + {:msg (msg "gain " (* 2 (count (filter #(pos? (+ (get-counters % :advancement) (:extra-advance-counter % 0))) + (get-all-installed state)))) " [Credits]") + :effect (effect (gain-credits (* 2 (count (filter #(pos? (+ (get-counters % :advancement) (:extra-advance-counter % 0))) + (get-all-installed state))))))} + + "MCA Informant" + {:req (req (not-empty (filter #(has-subtype? % "Connection") + (all-active-installed state :runner)))) + :prompt "Choose a connection to host MCA Informant on" + :choices {:req #(and (runner? %) + (has-subtype? % "Connection") + (installed? %))} + :msg (msg "host it on " (card-str state target) ". The Runner has an additional tag") + :effect (req (host state side (get-card state target) (assoc card :zone [:discard] :seen true)) + (swap! state update-in [:runner :tag :additional] inc) + (trigger-event state :corp :runner-additional-tag-change 1)) + :leave-play (req (swap! state update-in [:runner :tag :additional] dec) + (trigger-event state :corp :runner-additional-tag-change 1) + (system-msg state :corp "trashes MCA Informant")) + :runner-abilities [{:label "Trash MCA Informant host" + :cost [:credit 2 :click 1] + :req (req (= :runner side)) + :effect (effect (system-msg :runner (str "spends [Click] and 2 [Credits] to trash " (card-str state (:host card)))) + (trash :runner eid (get-card state (:host card)) nil))}]} + + "Medical Research Fundraiser" + {:msg "gain 8 [Credits]. The Runner gains 3 [Credits]" + :effect (effect (gain-credits 8) (gain-credits :runner 3))} + + "Midseason Replacements" + {:req (req (last-turn? state :runner :stole-agenda)) + :trace {:base 6 + :label "Trace 6 - Give the Runner X tags" + :successful {:msg "give the Runner X tags" + :async true + :effect (effect (system-msg + (str "gives the Runner " (- target (second targets)) " tags")) + (gain-tags eid (- target (second targets))))}}} + + "Mushin No Shin" + {:prompt "Select a card to install from HQ" + :choices {:req #(and (#{"Asset" "Agenda" "Upgrade"} (:type %)) + (corp? %) + (in-hand? %))} + :effect (req (corp-install state side (assoc target :advance-counter 3) "New remote") + (effect-completed state side eid) + (let [tgtcid (:cid target)] + (register-turn-flag! + state side + card :can-rez + (fn [state side card] + (if (= (:cid card) tgtcid) + ((constantly false) (toast state :corp "Cannot rez due to Mushin No Shin." "warning")) + true))) + (register-turn-flag! + state side + card :can-score + (fn [state side card] + (if (and (= (:cid card) tgtcid) + (>= (get-counters card :advancement) (or (:current-cost card) (:advancementcost card)))) + ((constantly false) (toast state :corp "Cannot score due to Mushin No Shin." "warning")) + true)))))} + + "Mutate" + {:req (req (some #(and (ice? %) + (rezzed? %)) + (all-installed state :corp))) + :prompt "Select a rezzed piece of ice to trash" + :choices {:req #(and (ice? %) + (rezzed? %))} + :async true + :effect (req (let [i (ice-index state target) + [revealed-cards r] (split-with (complement ice?) (get-in @state [:corp :deck])) + titles (->> (conj (vec revealed-cards) (first r)) + (filter identity) + (map :title))] + (wait-for (trash state :corp target nil) + (do + (system-msg state side (str "uses Mutate to trash " (:title target))) + (when (seq titles) + (reveal state side revealed-cards) + (system-msg state side (str "reveals " (clojure.string/join ", " titles) " from R&D"))) + (if-let [ice (first r)] + (let [newice (assoc ice :zone (:zone target) :rezzed true) + ices (get-in @state (cons :corp (:zone target)) []) + newices (apply conj (subvec ices 0 i) newice (subvec ices i))] + (swap! state assoc-in (cons :corp (:zone target)) newices) + (swap! state update-in [:corp :deck] (fn [coll] (remove-once #(same-card? % newice) coll))) + (trigger-event state side :corp-install newice) + (card-init state side newice {:resolve-effect false + :init-data true}) + (system-msg state side (str "uses Mutate to install and rez " (:title newice) " from R&D at no cost")) + (trigger-event state side :rez newice)) + (system-msg state side (str "does not find any ICE to install from R&D"))) + (shuffle! state :corp :deck) + (effect-completed state side eid)))))} + + "Neural EMP" + {:req (req (last-turn? state :runner :made-run)) + :msg "do 1 net damage" + :effect (effect (damage eid :net 1 {:card card}))} + + "O₂ Shortage" + {:async true + :effect (req (if (empty? (:hand runner)) + (do (gain state :corp :click 2) + (system-msg state side (str "uses O₂ Shortage to gain [Click][Click]")) + (effect-completed state side eid)) + (do (show-wait-prompt state :corp "Runner to decide whether or not to trash a card from their Grip") + (continue-ability + state side + {:optional + {:prompt "Trash 1 random card from your Grip?" + :player :runner + :yes-ability {:effect (effect (trash-cards :runner (take 1 (shuffle (:hand runner)))) + (clear-wait-prompt :corp))} + :no-ability {:msg "gain [Click][Click]" + :effect (effect (gain :corp :click 2) + (clear-wait-prompt :corp))}}} + card nil))))} + + "Observe and Destroy" + {:additional-cost [:tag 1] + :req (req (and (pos? (count-tags state)) + (< (:credit runner) 6))) + :async true + :effect (effect (continue-ability + {:prompt "Select an installed card to trash" + :choices {:req installed?} + :msg (msg "remove 1 Runner tag and trash " (:title target)) + :effect (effect (trash target))} + card nil))} + + "Oversight AI" + {:implementation "Trashing ICE is manual" + :choices {:req #(and (ice? %) (not (rezzed? %)) (= (last (:zone %)) :ices))} + :msg (msg "rez " (:title target) " at no cost") + :effect (effect (rez target {:ignore-cost :all-costs}) + (host (get-card state target) (assoc card :zone [:discard] :seen true :condition true)))} + + "Patch" + {:choices {:req #(and (ice? %) (rezzed? %))} + :msg (msg "give +2 strength to " (card-str state target)) + :effect (effect (host target (assoc card :zone [:discard] :seen true :condition true)) + (update-ice-strength (get-card state target))) + :events {:pre-ice-strength {:req (req (same-card? target (:host card))) + :effect (effect (ice-strength-bonus 2 target))}}} + + "Paywall Implementation" + {:events {:successful-run {:msg "gain 1 [Credits]" :effect (effect (gain-credits :corp 1))}}} + + "Peak Efficiency" + {:msg (msg "gain " (reduce (fn [c server] + (+ c (count (filter (fn [ice] (:rezzed ice)) (:ices server))))) + 0 (flatten (seq (:servers corp)))) + " [Credits]") + :effect (effect (gain-credits + (reduce (fn [c server] + (+ c (count (filter (fn [ice] (:rezzed ice)) (:ices server))))) + 0 (flatten (seq (:servers corp))))))} + + "Power Grid Overload" + {:req (req (last-turn? state :runner :made-run)) + :trace {:base 2 + :successful {:msg "trash 1 piece of hardware" + :async true + :effect (req (let [max-cost (- target (second targets))] + (continue-ability + state side + {:choices {:req #(and (hardware? %) + (<= (:cost %) max-cost))} + :msg (msg "trash " (:title target)) + :effect (effect (trash target))} + card nil)) + (system-msg + state :corp + (str "trashes 1 piece of hardware with install cost less than or equal to " + (- target (second targets)))))}}} + + "Power Shutdown" + {:req (req (and (last-turn? state :runner :made-run)) + (not-empty (filter #(or (= "Program" (:type %)) (= "Hardware" (:type %))) + (all-active-installed state :runner)))) + :prompt "Trash how many cards from the top R&D?" + :choices {:number (req (apply max (map :cost (filter #(or (= "Program" (:type %)) (= "Hardware" (:type %))) (all-active-installed state :runner)))))} + :msg (msg "trash " target " cards from the top of R&D") + :async true + :effect (req (mill state :corp target) + (let [n target] + (continue-ability state :runner + {:prompt "Select a Program or piece of Hardware to trash" + :choices {:req #(and (#{"Hardware" "Program"} (:type %)) + (<= (:cost %) n))} + :msg (msg "trash " (:title target)) + :effect (effect (trash target))} + card nil)))} + + "Precognition" + {:async true + :msg "rearrange the top 5 cards of R&D" + :effect (req (show-wait-prompt state :runner "Corp to rearrange the top cards of R&D") + (let [from (take 5 (:deck corp))] + (if (pos? (count from)) + (continue-ability state side (reorder-choice :corp :runner from '() + (count from) from) card nil) + (do (clear-wait-prompt state :runner) + (effect-completed state side eid)))))} + + "Predictive Algorithm" + {:events {:pre-steal-cost {:effect (effect (steal-cost-bonus [:credit 2]))}}} + + "Preemptive Action" + {:effect (effect (rfg-and-shuffle-rd-effect (first (:play-area corp)) (min (count (:discard corp)) 3) true))} + + "Priority Construction" + (letfn [(install-card [chosen] + {:prompt "Select a remote server" + :choices (req (conj (vec (get-remote-names state)) "New remote")) + :async true + :effect (effect (corp-install (assoc chosen :advance-counter 3) target {:ignore-all-cost true}))})] + {:async true + :prompt "Choose a piece of ICE in HQ to install" + :choices {:req #(and (in-hand? %) (corp? %) (ice? %))} + :msg "install an ICE from HQ and place 3 advancements on it" + :cancel-effect (req (effect-completed state side eid)) + :effect (effect (continue-ability (install-card target) card nil))}) + + "Product Recall" + {:async true + :prompt "Select a rezzed asset or upgrade to trash" + :choices {:req #(and (rezzed? %) + (or (asset? %) + (upgrade? %)))} + :effect (req (let [tcost (modified-trash-cost state side target)] + (wait-for (trash state side target {:unpreventable true}) + (do (gain-credits state :corp tcost) + (system-msg state side (str "uses Product Recall to trash " (card-str state target) + " and gain " tcost "[Credits]")) + (effect-completed state side eid)))))} + + "Psychographics" + {:req (req tagged) + :async true + :prompt "Pay how many credits?" + :choices {:number (req (count-tags state))} + :effect (req (let [c target] + (if (can-pay? state side eid card (:title card) :credit c) + (do (pay state :corp card :credit c) + (continue-ability + state side + {:msg (msg "place " (quantify c " advancement token") " on " (card-str state target)) + :choices {:req can-be-advanced?} + :effect (effect (add-prop target :advance-counter c {:placed true}))} + card nil)) + (effect-completed state side eid))))} + + "Psychokinesis" + (letfn [(choose-card [state cards] + (let [allowed-cards (filter #(some #{"New remote"} (installable-servers state %)) + cards)] + {:prompt "Select an agenda, asset, or upgrade to install" + :choices (cons "None" allowed-cards) + :async true + :effect (req (if-not (or (= target "None") (ice? target) (operation? target)) + (continue-ability state side (install-card target) card nil) + (system-msg state side "does not install an asset, agenda, or upgrade")) + (effect-completed state side eid) + (clear-wait-prompt state :runner))})) + (install-card [chosen] + {:prompt "Select a remote server" + :choices (req (conj (vec (get-remote-names state)) "New remote")) + :async true + :effect (effect (clear-wait-prompt :runner) + (corp-install (move state side chosen :play-area) target))})] + {:msg "look at the top 5 cards of R&D" + :async true + :effect (req (show-wait-prompt state :runner "Corp to look at the top cards of R&D") + (let [top-5 (take 5 (:deck corp))] + (continue-ability state side (choose-card state top-5) card nil)))}) + + "Punitive Counterstrike" + {:trace {:base 5 + :successful {:async true + :msg (msg "do " (:stole-agenda runner-reg-last 0) " meat damage") + :effect (effect (damage eid :meat (:stole-agenda runner-reg-last 0) {:card card}))}}} + + "Reclamation Order" + {:prompt "Select a card from Archives" + :show-discard true + :choices {:req #(and (corp? %) + (not= (:title %) "Reclamation Order") + (in-discard? %))} + :msg (msg "name " (:title target)) + :effect (req (let [title (:title target) + cards (filter #(= title (:title %)) (:discard corp)) + n (count cards)] + (continue-ability state side + {:prompt (str "Choose how many copies of " + title " to reveal") + :choices {:number (req n)} + :msg (msg "reveal " + (quantify target "cop" "y" "ies") + " of " title + " from Archives" + (when (pos? target) + (str " and add " + (if (= 1 target) "it" "them") + " to HQ"))) + :effect (req (reveal state side cards) + (doseq [c (->> cards + (sort-by :seen) + reverse + (take target))] + (move state side c :hand)))} + card nil)))} + + "Recruiting Trip" + (let [rthelp (fn rt [total left selected] + (if (pos? left) + {:prompt (str "Choose a Sysop (" (inc (- total left)) "/" total ")") + :choices (req (cancellable (filter #(and (has-subtype? % "Sysop") + (not-any? #{(:title %)} selected)) (:deck corp)) :sorted)) + :msg (msg "put " (:title target) " into HQ") + :async true + :effect (req (move state side target :hand) + (continue-ability + state side + (rt total (dec left) (cons (:title target) selected)) + card nil))} + {:effect (effect (shuffle! :corp :deck)) + :msg (msg "shuffle R&D")}))] + {:prompt "How many Sysops?" + :async true + :choices :credit + :msg (msg "search for " target " Sysops") + :effect (effect (continue-ability (rthelp target target []) card nil))}) + + "Red Level Clearance" + (let [all [{:msg "gain 2 [Credits]" + :effect (effect (gain-credits 2))} + {:msg "draw 2 cards" + :effect (effect (draw 2))} + {:msg "gain [Click]" + :effect (effect (gain :click 1))} + {:prompt "Choose a non-agenda to install" + :msg "install a non-agenda from hand" + :choices {:req #(and (not (agenda? %)) + (not (operation? %)) + (in-hand? %))} + :async true + :effect (effect (corp-install eid target nil nil))}] + can-install? (fn [hand] + (seq (remove #(or (agenda? %) + (operation? %)) + hand))) + choice (fn choice [abis chose-once] + {:prompt "Choose an ability to resolve" + :choices (map #(capitalize (:msg %)) abis) + :async true + :effect (req (let [chosen (some #(when (= target (capitalize (:msg %))) %) abis)] + (if (or (not= target "Install a non-agenda from hand") + (and (= target "Install a non-agenda from hand") + (can-install? (:hand corp)))) + (wait-for (resolve-ability state side chosen card nil) + (if (false? chose-once) + (continue-ability state side + (choice (remove #(= % chosen) abis) true) + card nil) + (do (clear-wait-prompt state :runner) + (effect-completed state side eid)))) + (continue-ability state side (choice abis chose-once) card nil))))})] + {:async true + :effect (effect (show-wait-prompt :runner "Corp to use Red Level Clearance") + (continue-ability (choice all false) card nil))}) + + + + "Red Planet Couriers" + {:async true + :req (req (some #(can-be-advanced? %) (all-installed state :corp))) + :prompt "Select an installed card that can be advanced" + :choices {:req can-be-advanced?} + :effect (req (let [installed (get-all-installed state) + total-adv (reduce + (map #(get-counters % :advancement) installed))] + (doseq [c installed] + (set-prop state side c :advance-counter 0)) + (set-prop state side target :advance-counter total-adv) + (update-all-ice state side) + (system-msg state side (str "uses Red Planet Couriers to move " total-adv + " advancement tokens to " (card-str state target))) + (effect-completed state side eid)))} + + "Replanting" + (letfn [(replant [n] + {:prompt "Select a card to install with Replanting" + :async true + :choices {:req #(and (corp? %) + (not (operation? %)) + (in-hand? %))} + :effect (req (wait-for (corp-install state side target nil {:ignore-all-cost true}) + (if (< n 2) + (continue-ability state side (replant (inc n)) card nil) + (effect-completed state side eid))))})] + {:async true + :prompt "Select an installed card to add to HQ" + :choices {:req #(and (corp? %) + (installed? %))} + :msg (msg "add " (card-str state target) " to HQ, then install 2 cards ignoring all costs") + :effect (req (move state side target :hand) + (resolve-ability state side (replant 1) card nil))}) + + "Restore" + {:async true + :effect (effect (continue-ability + {:prompt "Select a card in Archives to install & rez with Restore" + :priority -1 + :async true + :show-discard true + :choices {:req #(and (corp? %) + (not (operation? %)) + (in-discard? %))} + :effect (req (wait-for + (corp-install state side target nil {:install-state :rezzed}) + (do (system-msg state side (str "uses Restore to " + (corp-install-msg target))) + (let [leftover (filter #(= (:title target) (:title %)) (-> @state :corp :discard))] + (when (seq leftover) + (doseq [c leftover] + (move state side c :rfg)) + (system-msg state side (str "removes " (count leftover) " copies of " (:title target) " from the game")))) + (effect-completed state side eid))))} + card nil))} + + "Restoring Face" + {:async true + :prompt "Select a Sysop, Executive or Clone to trash" + :msg (msg "trash " (card-str state target) " to remove 2 bad publicity") + :choices {:req #(and (rezzed? %) + (or (has-subtype? % "Clone") + (has-subtype? % "Executive") + (has-subtype? % "Sysop")))} + :effect (effect (lose-bad-publicity 2) + (trash eid target nil))} + + "Restructure" + {:msg "gain 15 [Credits]" + :effect (effect (gain-credits 15))} + + "Reuse" + {:async true + :effect (req (let [n (count (:hand corp))] + (continue-ability + state side + {:prompt (msg "Select up to " n " cards in HQ to trash with Reuse") + :choices {:max n + :req #(and (corp? %) + (in-hand? %))} + :msg (msg (let [m (count targets)] + (str "trash " (quantify m "card") + " and gain " (* 2 m) " [Credits]"))) + :effect (effect (trash-cards targets) + (gain-credits (* 2 (count targets))))} card nil)))} + + "Reverse Infection" + {:prompt "Choose One:" + :choices ["Purge virus counters" + "Gain 2 [Credits]"] + :effect (req (if (= target "Gain 2 [Credits]") + (do (gain-credits state side 2) + (system-msg state side "uses Reverse Infection to gain 2 [Credits]")) + (let [pre-purge-virus (number-of-virus-counters state)] + (purge state side) + (let [post-purge-virus (number-of-virus-counters state) + num-virus-purged (- pre-purge-virus post-purge-virus) + num-to-trash (quot num-virus-purged 3)] + (mill state :corp :runner num-to-trash) + (system-msg state side (str "uses Reverse Infection to purge " + num-virus-purged (pluralize " virus counter" num-virus-purged) + " and trash " + num-to-trash (pluralize " card" num-to-trash) + " from the top of the stack"))))))} + + "Rework" + {:prompt "Select a card from HQ to shuffle into R&D" + :choices {:req #(and (corp? %) + (in-hand? %))} + :msg "shuffle a card from HQ into R&D" + :effect (effect (move target :deck) + (shuffle! :deck))} + + "Riot Suppression" + {:req (req (last-turn? state :runner :trashed-card)) + :async true + :effect (req (let [c card] + (show-wait-prompt state :corp "Runner to decide if they will take 1 brain damage") + (continue-ability + state :runner + {:optional + {:prompt "Take 1 brain damage to prevent having 3 fewer clicks next turn?" + :player :runner + :end-effect (req (clear-wait-prompt state :corp) + (move state :corp (find-latest state c) :rfg)) + :yes-ability + {:async true + :effect (req (system-msg + state :runner + "suffers 1 brain damage to prevent losing 3[Click] to Riot Suppression") + (damage state :runner eid :brain 1 {:card card}))} + :no-ability + {:msg "give the runner 3 fewer [Click] next turn" + :effect (req (swap! state update-in [:runner :extra-click-temp] (fnil #(- % 3) 0)))}}} + card nil)))} + + "Rolling Brownout" + {:msg "increase the play cost of operations and events by 1 [Credits]" + :events {:play-event {:once :per-turn + :msg "gain 1 [Credits]" + :effect (effect (gain-credits :corp 1))} + :pre-play-instant {:effect (effect (play-cost-bonus [:credit 1]))}}} + + "Rover Algorithm" + {:choices {:req #(and (ice? %) (rezzed? %))} + :msg (msg "host it as a condition counter on " (card-str state target)) + :effect (effect (host target (assoc card :zone [:discard] :seen true :condition true)) + (update-ice-strength (get-card state target))) + :events {:pass-ice {:req (req (same-card? target (:host card))) + :effect (effect (add-counter card :power 1))} + :pre-ice-strength {:req (req (same-card? target (:host card))) + :effect (effect (ice-strength-bonus (get-counters card :power) target))}}} + + "Sacrifice" + {:req (req (and (pos? (count-bad-pub state)) + (some #(pos? (:agendapoints %)) (:scored corp)))) + :additional-cost [:forfeit] + :effect (req (let [bp-lost (max 0 (min (:agendapoints (last (:rfg corp))) + (count-bad-pub state)))] + (system-msg state side (str "uses Sacrifice to lose " bp-lost " bad publicity and gain " bp-lost " [Credits]")) + (when (pos? bp-lost) + (lose-bad-publicity state side bp-lost) + (gain-credits state side bp-lost))))} + + "Salem's Hospitality" + {:prompt "Name a Runner card" + :choices {:card-title (req (and (runner? target) + (not (identity? target))))} + :effect (req (system-msg state side + (str "uses Salem's Hospitality to reveal the Runner's Grip ( " + (join ", " (map :title (sort-by :title (:hand runner)))) + " ) and trash any copies of " target)) + (reveal state side (filter #(= target (:title %)) (:hand runner))) + (doseq [c (filter #(= target (:title %)) (:hand runner))] + (trash state side c {:unpreventable true})))} + + "Scarcity of Resources" + {:msg "increase the install cost of resources by 2" + :events {:pre-install {:req (req (and (resource? target) + (not (second targets)))) ; not facedown + :effect (effect (install-cost-bonus [:credit 2]))}}} + + "Scorched Earth" + {:req (req tagged) + :async true + :msg "do 4 meat damage" + :effect (effect (damage eid :meat 4 {:card card}))} + + "SEA Source" + {:req (req (last-turn? state :runner :successful-run)) + :trace {:base 3 + :label "Trace 3 - Give the Runner 1 tag" + :successful {:msg "give the Runner 1 tag" + :async true + :effect (effect (gain-tags :corp eid 1))}}} + + "Secure and Protect" + {:interactive (req true) + :async true + :effect (req (if (seq (filter ice? (:deck corp))) + (do (show-wait-prompt state :runner "Corp to use Secure and Protect") + (continue-ability + state side + {:async true + :prompt "Choose a piece of ice" + :choices (req (filter ice? (:deck corp))) + :effect (req (let [chosen-ice target] + (continue-ability + state side + {:async true + :prompt (str "Select a server to install " (:title chosen-ice) " on") + :choices ["Archives" "R&D" "HQ"] + :msg (msg "reveal " (:title chosen-ice) "and install it, paying 3[credit] less") + :effect (effect (clear-wait-prompt :runner) + (reveal state side chosen-ice) + (shuffle! :deck) + (install-cost-bonus [:credit -3]) + (corp-install eid chosen-ice target nil))} + card nil)))} + card nil)) + (do (shuffle! state side :deck) + (effect-completed state side eid))))} + + "Self-Growth Program" + {:req (req tagged) + :prompt "Select two installed Runner cards" + :choices {:req #(and (installed? %) + (runner? %)) + :max 2} + :msg (msg (str "move " (join ", " (map :title targets)) " to the Runner's grip")) + :effect (req (doseq [c targets] + (move state :runner c :hand)))} + + "Service Outage" + {:msg "add a cost of 1 [Credit] for the Runner to make the first run each turn" + :events {:pre-init-run {:req (req (first-event? state side :pre-init-run)) + :effect (effect (run-additional-cost-bonus [:credit 1]))}}} + + "Shipment from Kaguya" + {:choices {:max 2 :req can-be-advanced?} + :msg (msg "place 1 advancement token on " (count targets) " cards") + :effect (req (doseq [t targets] + (add-prop state :corp t :advance-counter 1 {:placed true})) + (effect-completed state side eid))} + + "Shipment from MirrorMorph" + (let [shelper (fn sh [n] {:prompt "Select a card to install with Shipment from MirrorMorph" :async true - :show-discard true :choices {:req #(and (corp? %) (not (operation? %)) - (in-discard? %))} + (in-hand? %))} :effect (req (wait-for (corp-install state side target nil nil) - (do (system-msg state side (str "uses Friends in High Places to " - (corp-install-msg target))) - (if (< n 2) - (continue-ability state side (fhp (inc n)) card nil) - (effect-completed state side eid)))))})] - {:async true - :effect (effect (continue-ability (fhelper 1) card nil))})) - -(define-card "Fully Operational" - (letfn [(full-servers [state] - (filter #(and (not-empty (:content %)) - (not-empty (:ices %))) - (vals (get-remotes state)))) - (repeat-choice [current total] - ;; if current > total, this ability will auto-resolve and finish the chain of async methods. - (when (<= current total) - {:async true - :prompt (str "Choice " current " of " total ": Gain 2 [Credits] or draw 2 cards? ") - :choices ["Gain 2 [Credits]" "Draw 2 cards"] - :msg (msg (lower-case target)) - :effect (req (if (= target "Gain 2 [Credits]") - (do (gain-credits state :corp 2) - (continue-ability state side (repeat-choice (inc current) total) - card nil)) - (wait-for (draw state :corp 2 nil) ; don't proceed with the next choice until the draw is done - (continue-ability state side (repeat-choice (inc current) total) - card nil))))}))] - {:async true - :msg (msg "uses Fully Operational to make " (quantify (inc (count (full-servers state))) "gain/draw decision")) - :effect (effect (continue-ability (repeat-choice 1 (inc (count (full-servers state)))) - card nil))})) - -(define-card "Game Changer" - {:effect (req (gain state side :click (count (:scored runner))) - (move state side (first (:play-area corp)) :rfg))}) - -(define-card "Game Over" - {:req (req (last-turn? state :runner :stole-agenda)) - :async true - :prompt "Choose a card type" - :choices ["Hardware" "Program" "Resource"] - :effect (req (let [type target - trashtargets (filter #(and (is-type? % type) - (not (has? % :subtype "Icebreaker"))) - (all-active-installed state :runner)) - numtargets (count trashtargets) - typemsg (str (when (= type "Program") "non-Icebreaker ") type - (when-not (= type "Hardware") "s"))] - (system-msg state :corp (str "chooses to trash all " typemsg)) - (show-wait-prompt state :corp "Runner to prevent trashes") - (wait-for - (resolve-ability - state :runner - {:async true - :req (req (<= 3 (:credit runner))) - :prompt (msg "Prevent any " typemsg " from being trashed? Pay 3 [Credits] per card.") - :choices {:max (req (min numtargets (quot (:credit runner) 3))) - :req #(and (installed? %) - (is-type? % type) - (not (has? % :subtype "Icebreaker")))} - :effect (req (pay state :runner card :credit (* 3 (count targets))) - (system-msg - state :runner - (str "pays " (* 3 (count targets)) " [Credits] to prevent the trashing of " - (join ", " (map :title (sort-by :title targets))))) - (system-msg state :corp (str "trashes all other " typemsg)) - (effect-completed state side (make-result eid targets)))} - card nil) - (trash-cards state side (clojure.set/difference (set trashtargets) (set async-result))) - (clear-wait-prompt state :corp) - (gain-bad-publicity state :corp 1) - (system-msg state :corp "take 1 bad publicity") - (effect-completed state side eid))))}) - -(define-card "Genotyping" - {:async true - :effect (effect (mill :corp 2) - (system-msg "trashes the top 2 cards of R&D") - (rfg-and-shuffle-rd-effect eid (first (:play-area corp)) 4 false))}) - -(define-card "Green Level Clearance" - {:msg "gain 3 [Credits] and draw 1 card" - :async true - :effect (effect (gain-credits 3) - (draw eid 1 nil))}) - -(define-card "Hangeki" - {:req (req (last-turn? state :runner :trashed-card)) - :async true - :prompt "Choose an installed Corp card" - :choices {:req #(and (corp? %) - (installed? %))} - :effect (effect (show-wait-prompt :corp "Runner to resolve Hangeki") + (if (< n 3) + (continue-ability state side (sh (inc n)) card nil) + (effect-completed state side eid))))})] + {:async true + :effect (effect (continue-ability (shelper 1) card nil))}) + + "Shipment from SanSan" + {:choices ["0", "1", "2"] + :prompt "How many advancement tokens?" + :async true + :effect (req (let [c (str->int target)] (continue-ability - {:optional - {:player :runner - :async true - :prompt "Access card? (If not, add Hangeki to your score area worth -1 agenda point)" - :yes-ability - {:effect (req (clear-wait-prompt state :corp) - (wait-for (access-card state side target) - (move state :corp (find-latest state card) :rfg) - (system-msg state :corp "removes Hangeki from the game") - (effect-completed state side eid)))} - :no-ability - {:msg "add it to the Runner's score area as an agenda worth -1 agenda point" - :effect (effect (clear-wait-prompt :corp) - (as-agenda :runner eid (find-latest state card) -1))}}} - card targets))}) - -(define-card "Hard-Hitting News" - {:req (req (last-turn? state :runner :made-run)) - :trace {:base 4 - :label "Give the Runner 4 tags" - :successful {:async true - :msg "give the Runner 4 tags" - :effect (effect (gain-tags eid 4))}}}) - -(define-card "Hasty Relocation" - (letfn [(hr-final [chosen original] - {:prompt (str "The top cards of R&D will be " (clojure.string/join ", " (map :title chosen)) ".") - :choices ["Done" "Start over"] - :async true - :effect (req (if (= target "Done") - (do (doseq [c (reverse chosen)] (move state :corp c :deck {:front true})) - (clear-wait-prompt state :runner) - (effect-completed state side eid)) - (continue-ability state side (hr-choice original '() 3 original) - card nil)))}) - (hr-choice [remaining chosen n original] - {:prompt "Choose a card to move next onto R&D" - :choices remaining - :async true - :effect (req (let [chosen (cons target chosen)] - (if (< (count chosen) n) - (continue-ability state side (hr-choice (remove-once #(= target %) remaining) - chosen n original) card nil) - (continue-ability state side (hr-final chosen original) card nil))))})] - {:additional-cost [:mill 1] - :async true - :msg "trash the top card of R&D, draw 3 cards, and add 3 cards in HQ to the top of R&D" - :effect (req (wait-for (draw state side 3 nil) - (do (show-wait-prompt state :runner "Corp to add 3 cards in HQ to the top of R&D") - (let [from (get-in @state [:corp :hand])] - (continue-ability state :corp (hr-choice from '() 3 from) card nil)))))})) - -(define-card "Hatchet Job" - {:trace {:base 5 - :successful {:choices {:req #(and (installed? %) - (runner? %) - (not (has-subtype? % "Virtual")))} - :msg "add an installed non-virtual card to the Runner's grip" - :effect (effect (move :runner target :hand true))}}}) - -(define-card "Hedge Fund" - {:msg "gain 9 [Credits]" :effect (effect (gain-credits 9))}) - -(define-card "Hellion Alpha Test" - {:req (req (last-turn? state :runner :installed-resource)) - :trace {:base 2 - :successful {:msg "add a Resource to the top of the Stack" - :choices {:req #(and (installed? %) - (resource? %))} - :effect (effect (move :runner target :deck {:front true}) - (system-msg (str "adds " (:title target) " to the top of the Stack")))} - :unsuccessful {:msg "take 1 bad publicity" - :effect (effect (gain-bad-publicity :corp 1))}}}) - -(define-card "Hellion Beta Test" - {:req (req (last-turn? state :runner :trashed-card)) - :trace {:base 2 - :label "Trace 2 - Trash 2 installed non-program cards or take 1 bad publicity" - :successful {:choices {:max (req (min 2 (count (filter #(or (facedown? %) - (not (program? %))) - (concat (all-installed state :corp) - (all-installed state :runner)))))) - :all true - :req #(and (installed? %) - (not (program? %)))} - :msg (msg "trash " (join ", " (map :title (sort-by :title targets)))) - :effect (req (doseq [c targets] - (trash state side c)))} - :unsuccessful {:msg "take 1 bad publicity" - :effect (effect (gain-bad-publicity :corp 1))}}}) - -(define-card "Heritage Committee" - {:async true - :effect (req (wait-for (draw state side 3 nil) - (continue-ability state side - {:prompt "Select a card in HQ to put on top of R&D" - :choices {:req #(and (corp? %) - (in-hand? %))} - :msg "draw 3 cards and add 1 card from HQ to the top of R&D" - :effect (effect (move target :deck {:front true}))} - card nil)))}) - -(define-card "High-Profile Target" - (letfn [(dmg-count [state] (* 2 (count-tags state)))] - {:req (req tagged) - :async true - :msg (msg "do " (dmg-count state) " meat damage") - :effect (effect (damage eid :meat (dmg-count state) {:card card}))})) - -(define-card "Housekeeping" - {:events {:runner-install {:player :runner - :prompt "Select a card from your Grip to trash for Housekeeping" :once :per-turn - :choices {:req #(and (runner? %) - (in-hand? %))} - :msg (msg "force the Runner to trash " (:title target) " from their Grip") - :effect (effect (trash target {:unpreventable true}))}}}) - -(define-card "Hunter Seeker" - {:req (req (last-turn? state :runner :stole-agenda)) - :async true - :prompt "Choose a card to trash" - :choices {:req installed?} - :msg (msg "trash " (card-str state target)) - :effect (effect (trash target))}) - -(define-card "Interns" - {:prompt "Select a card to install from Archives or HQ" - :show-discard true - :not-distinct true - :choices {:req #(and (not (operation? %)) - (corp? %) - (#{[:hand] [:discard]} (:zone %)))} - :effect (effect (corp-install target nil {:ignore-install-cost true})) - :msg (msg (corp-install-msg target))}) - -(define-card "Invasion of Privacy" - (letfn [(iop [x] - {:async true - :req (req (->> (:hand runner) - (filter #(or (resource? %) - (event? %))) - count - pos?)) - :prompt "Choose a resource or event to trash" - :msg (msg "trash " (:title target)) - :choices (req (cancellable - (filter #(or (resource? %) - (event? %)) - (:hand runner)) - :sorted)) - :effect (req (trash state side target) - (if (pos? x) - (continue-ability state side (iop (dec x)) card nil) - (effect-completed state side eid)))})] - {:trace {:base 2 - :successful {:msg "reveal the Runner's Grip and trash up to X resources or events" - :effect (req (reveal state side (:hand runner)) - (let [x (- target (second targets))] - (system-msg - state :corp - (str "reveals the Runner's Grip ( " - (join ", " (map :title (sort-by :title (:hand runner)))) - " ) and can trash up to " x " resources or events")) - (continue-ability state side (iop (dec x)) card nil)))} - :unsuccessful {:msg "take 1 bad publicity" - :effect (effect (gain-bad-publicity :corp 1))}}})) - -(define-card "IPO" - {:msg "gain 13 [Credits]" :effect (effect (gain-credits 13))}) - -(define-card "Kill Switch" - (let [trace-for-brain-damage {:msg (msg "reveal that they accessed " (:title target)) - :trace {:base 3 - :successful {:msg "do 1 brain damage" - :async true - :effect (effect (damage :runner eid :brain 1 {:card card}))}}}] - {:events {:access (assoc trace-for-brain-damage :req (req (agenda? target)) - :interactive (req (agenda? target))) - :agenda-scored trace-for-brain-damage}})) - -(define-card "Lag Time" - {:effect (effect (update-all-ice)) - :events {:pre-ice-strength {:effect (effect (ice-strength-bonus 1 target))}} - :leave-play (effect (update-all-ice))}) - -(define-card "Lateral Growth" - {:async true - :msg "gain 4 [Credits]" - :effect (effect (gain-credits 4) - (continue-ability {:player :corp - :prompt "Select a card to install" - :choices {:req #(and (corp? %) - (not (operation? %)) - (in-hand? %))} - :async true - :msg (msg (corp-install-msg target)) - :effect (effect (corp-install eid target nil nil))} - card nil))}) - -(define-card "Liquidation" - {:async true - :req (req (some #(and (rezzed? %) (not (agenda? %))) (all-installed state :corp))) - :effect (req (let [n (count (filter #(not (agenda? %)) (all-active-installed state :corp)))] - (continue-ability - state side - {:prompt "Select any number of rezzed cards to trash" - :choices {:max n - :req #(and (rezzed? %) - (not (agenda? %)))} - :msg (msg "trash " (join ", " (map :title (sort-by :title targets))) " and gain " (* (count targets) 3) " [Credits]") - :effect (req (doseq [c targets] - (trash state side c)) - (gain-credits state side (* (count targets) 3)))} - card nil)))}) - -(define-card "Load Testing" - {:msg "make the Runner lose [Click] when their next turn begins" - :effect (effect (register-events (:events (card-def card)) - (assoc card :zone '(:discard)))) - :events {:runner-turn-begins {:msg "make the Runner lose [Click]" - :effect (effect (lose :runner :click 1) - (unregister-events card))}}}) - -(define-card "Localized Product Line" - {:prompt "Choose a card" - :choices (req (cancellable (:deck corp) :sorted)) - :async true - :effect (req (let [c (:title target) - cs (filter #(= (:title %) c) (:deck corp))] - (continue-ability - state side - {:prompt "How many copies?" - :choices {:number (req (count cs))} - :msg (msg "add " (quantify target "cop" "y" "ies") " of " c " to HQ") - :effect (req (shuffle! state :corp :deck) - (doseq [c (take target cs)] - (move state side c :hand)))} - card nil)))}) - -(define-card "Manhunt" - {:events {:successful-run {:interactive (req true) - :req (req (first-event? state side :successful-run)) - :trace {:base 2 - :successful {:msg "give the Runner 1 tag" - :async true - :effect (effect (gain-tags eid 1))}}}}}) - -(define-card "Market Forces" - (letfn [(credit-diff [state] - (min (* 3 (count-tags state)) - (get-in @state [:runner :credit])))] - {:req (req tagged) - :msg (msg (let [c (credit-diff state)] - (str "make the runner lose " c " [Credits], and gain " c " [Credits]"))) - :effect (req (let [c (credit-diff state)] - (lose-credits state :runner c) - (gain-credits state :corp c)))})) - -(define-card "Mass Commercialization" - {:msg (msg "gain " (* 2 (count (filter #(pos? (+ (get-counters % :advancement) (:extra-advance-counter % 0))) - (get-all-installed state)))) " [Credits]") - :effect (effect (gain-credits (* 2 (count (filter #(pos? (+ (get-counters % :advancement) (:extra-advance-counter % 0))) - (get-all-installed state))))))}) - -(define-card "MCA Informant" - {:req (req (not-empty (filter #(has-subtype? % "Connection") - (all-active-installed state :runner)))) - :prompt "Choose a connection to host MCA Informant on" - :choices {:req #(and (runner? %) - (has-subtype? % "Connection") - (installed? %))} - :msg (msg "host it on " (card-str state target) ". The Runner has an additional tag") - :effect (req (host state side (get-card state target) (assoc card :zone [:discard] :seen true)) - (swap! state update-in [:runner :tag :additional] inc) - (trigger-event state :corp :runner-additional-tag-change 1)) - :leave-play (req (swap! state update-in [:runner :tag :additional] dec) - (trigger-event state :corp :runner-additional-tag-change 1) - (system-msg state :corp "trashes MCA Informant")) - :runner-abilities [{:label "Trash MCA Informant host" - :cost [:credit 2 :click 1] - :req (req (= :runner side)) - :effect (effect (system-msg :runner (str "spends [Click] and 2 [Credits] to trash " (card-str state (:host card)))) - (trash :runner eid (get-card state (:host card)) nil))}]}) - -(define-card "Medical Research Fundraiser" - {:msg "gain 8 [Credits]. The Runner gains 3 [Credits]" - :effect (effect (gain-credits 8) (gain-credits :runner 3))}) - -(define-card "Midseason Replacements" - {:req (req (last-turn? state :runner :stole-agenda)) - :trace {:base 6 - :label "Trace 6 - Give the Runner X tags" - :successful {:msg "give the Runner X tags" - :async true - :effect (effect (system-msg - (str "gives the Runner " (- target (second targets)) " tags")) - (gain-tags eid (- target (second targets))))}}}) - -(define-card "Mushin No Shin" - {:prompt "Select a card to install from HQ" - :choices {:req #(and (#{"Asset" "Agenda" "Upgrade"} (:type %)) - (corp? %) - (in-hand? %))} - :effect (req (corp-install state side (assoc target :advance-counter 3) "New remote") - (effect-completed state side eid) - (let [tgtcid (:cid target)] - (register-turn-flag! - state side - card :can-rez - (fn [state side card] - (if (= (:cid card) tgtcid) - ((constantly false) (toast state :corp "Cannot rez due to Mushin No Shin." "warning")) - true))) - (register-turn-flag! - state side - card :can-score - (fn [state side card] - (if (and (= (:cid card) tgtcid) - (>= (get-counters card :advancement) (or (:current-cost card) (:advancementcost card)))) - ((constantly false) (toast state :corp "Cannot score due to Mushin No Shin." "warning")) - true)))))}) - -(define-card "Mutate" - {:req (req (some #(and (ice? %) - (rezzed? %)) - (all-installed state :corp))) - :prompt "Select a rezzed piece of ice to trash" - :choices {:req #(and (ice? %) - (rezzed? %))} - :async true - :effect (req (let [i (ice-index state target) - [revealed-cards r] (split-with (complement ice?) (get-in @state [:corp :deck])) - titles (->> (conj (vec revealed-cards) (first r)) - (filter identity) - (map :title))] - (wait-for (trash state :corp target nil) - (do - (system-msg state side (str "uses Mutate to trash " (:title target))) - (when (seq titles) - (reveal state side revealed-cards) - (system-msg state side (str "reveals " (clojure.string/join ", " titles) " from R&D"))) - (if-let [ice (first r)] - (let [newice (assoc ice :zone (:zone target) :rezzed true) - ices (get-in @state (cons :corp (:zone target)) []) - newices (apply conj (subvec ices 0 i) newice (subvec ices i))] - (swap! state assoc-in (cons :corp (:zone target)) newices) - (swap! state update-in [:corp :deck] (fn [coll] (remove-once #(same-card? % newice) coll))) - (trigger-event state side :corp-install newice) - (card-init state side newice {:resolve-effect false - :init-data true}) - (system-msg state side (str "uses Mutate to install and rez " (:title newice) " from R&D at no cost")) - (trigger-event state side :rez newice)) - (system-msg state side (str "does not find any ICE to install from R&D"))) - (shuffle! state :corp :deck) - (effect-completed state side eid)))))}) - -(define-card "Neural EMP" - {:req (req (last-turn? state :runner :made-run)) - :msg "do 1 net damage" - :effect (effect (damage eid :net 1 {:card card}))}) - -(define-card "O₂ Shortage" - {:async true - :effect (req (if (empty? (:hand runner)) - (do (gain state :corp :click 2) - (system-msg state side (str "uses O₂ Shortage to gain [Click][Click]")) - (effect-completed state side eid)) - (do (show-wait-prompt state :corp "Runner to decide whether or not to trash a card from their Grip") - (continue-ability - state side - {:optional - {:prompt "Trash 1 random card from your Grip?" - :player :runner - :yes-ability {:effect (effect (trash-cards :runner (take 1 (shuffle (:hand runner)))) - (clear-wait-prompt :corp))} - :no-ability {:msg "gain [Click][Click]" - :effect (effect (gain :corp :click 2) - (clear-wait-prompt :corp))}}} - card nil))))}) - -(define-card "Observe and Destroy" - {:additional-cost [:tag 1] - :req (req (and (pos? (count-tags state)) - (< (:credit runner) 6))) - :async true - :effect (effect (continue-ability - {:prompt "Select an installed card to trash" - :choices {:req installed?} - :msg (msg "remove 1 Runner tag and trash " (:title target)) - :effect (effect (trash target))} - card nil))}) - -(define-card "Oversight AI" - {:implementation "Trashing ICE is manual" - :choices {:req #(and (ice? %) (not (rezzed? %)) (= (last (:zone %)) :ices))} - :msg (msg "rez " (:title target) " at no cost") - :effect (effect (rez target {:ignore-cost :all-costs}) - (host (get-card state target) (assoc card :zone [:discard] :seen true :condition true)))}) - -(define-card "Patch" - {:choices {:req #(and (ice? %) (rezzed? %))} - :msg (msg "give +2 strength to " (card-str state target)) - :effect (effect (host target (assoc card :zone [:discard] :seen true :condition true)) - (update-ice-strength (get-card state target))) - :events {:pre-ice-strength {:req (req (same-card? target (:host card))) - :effect (effect (ice-strength-bonus 2 target))}}}) - -(define-card "Paywall Implementation" - {:events {:successful-run {:msg "gain 1 [Credits]" :effect (effect (gain-credits :corp 1))}}}) - -(define-card "Peak Efficiency" - {:msg (msg "gain " (reduce (fn [c server] - (+ c (count (filter (fn [ice] (:rezzed ice)) (:ices server))))) - 0 (flatten (seq (:servers corp)))) - " [Credits]") - :effect (effect (gain-credits - (reduce (fn [c server] - (+ c (count (filter (fn [ice] (:rezzed ice)) (:ices server))))) - 0 (flatten (seq (:servers corp))))))}) - -(define-card "Power Grid Overload" - {:req (req (last-turn? state :runner :made-run)) - :trace {:base 2 - :successful {:msg "trash 1 piece of hardware" - :async true - :effect (req (let [max-cost (- target (second targets))] - (continue-ability - state side - {:choices {:req #(and (hardware? %) - (<= (:cost %) max-cost))} - :msg (msg "trash " (:title target)) - :effect (effect (trash target))} - card nil)) - (system-msg - state :corp - (str "trashes 1 piece of hardware with install cost less than or equal to " - (- target (second targets)))))}}}) - -(define-card "Power Shutdown" - {:req (req (and (last-turn? state :runner :made-run)) - (not-empty (filter #(or (= "Program" (:type %)) (= "Hardware" (:type %))) - (all-active-installed state :runner)))) - :prompt "Trash how many cards from the top R&D?" - :choices {:number (req (apply max (map :cost (filter #(or (= "Program" (:type %)) (= "Hardware" (:type %))) (all-active-installed state :runner)))))} - :msg (msg "trash " target " cards from the top of R&D") - :async true - :effect (req (mill state :corp target) - (let [n target] - (continue-ability state :runner - {:prompt "Select a Program or piece of Hardware to trash" - :choices {:req #(and (#{"Hardware" "Program"} (:type %)) - (<= (:cost %) n))} - :msg (msg "trash " (:title target)) - :effect (effect (trash target))} - card nil)))}) - -(define-card "Precognition" - {:async true - :msg "rearrange the top 5 cards of R&D" - :effect (req (show-wait-prompt state :runner "Corp to rearrange the top cards of R&D") - (let [from (take 5 (:deck corp))] - (if (pos? (count from)) - (continue-ability state side (reorder-choice :corp :runner from '() - (count from) from) card nil) - (do (clear-wait-prompt state :runner) - (effect-completed state side eid)))))}) - -(define-card "Predictive Algorithm" - {:events {:pre-steal-cost {:effect (effect (steal-cost-bonus [:credit 2]))}}}) - -(define-card "Preemptive Action" - {:effect (effect (rfg-and-shuffle-rd-effect (first (:play-area corp)) (min (count (:discard corp)) 3) true))}) - -(define-card "Priority Construction" - (letfn [(install-card [chosen] - {:prompt "Select a remote server" - :choices (req (conj (vec (get-remote-names state)) "New remote")) - :async true - :effect (effect (corp-install (assoc chosen :advance-counter 3) target {:ignore-all-cost true}))})] - {:async true - :prompt "Choose a piece of ICE in HQ to install" - :choices {:req #(and (in-hand? %) (corp? %) (ice? %))} - :msg "install an ICE from HQ and place 3 advancements on it" - :cancel-effect (req (effect-completed state side eid)) - :effect (effect (continue-ability (install-card target) card nil))})) - -(define-card "Product Recall" - {:async true - :prompt "Select a rezzed asset or upgrade to trash" - :choices {:req #(and (rezzed? %) - (or (asset? %) - (upgrade? %)))} - :effect (req (let [tcost (modified-trash-cost state side target)] - (wait-for (trash state side target {:unpreventable true}) - (do (gain-credits state :corp tcost) - (system-msg state side (str "uses Product Recall to trash " (card-str state target) - " and gain " tcost "[Credits]")) - (effect-completed state side eid)))))}) - -(define-card "Psychographics" - {:req (req tagged) - :async true - :prompt "Pay how many credits?" - :choices {:number (req (count-tags state))} - :effect (req (let [c target] - (if (can-pay? state side eid card (:title card) :credit c) - (do (pay state :corp card :credit c) - (continue-ability - state side - {:msg (msg "place " (quantify c " advancement token") " on " (card-str state target)) - :choices {:req can-be-advanced?} - :effect (effect (add-prop target :advance-counter c {:placed true}))} - card nil)) - (effect-completed state side eid))))}) - -(define-card "Psychokinesis" - (letfn [(choose-card [state cards] - (let [allowed-cards (filter #(some #{"New remote"} (installable-servers state %)) - cards)] - {:prompt "Select an agenda, asset, or upgrade to install" - :choices (cons "None" allowed-cards) - :async true - :effect (req (if-not (or (= target "None") (ice? target) (operation? target)) - (continue-ability state side (install-card target) card nil) - (system-msg state side "does not install an asset, agenda, or upgrade")) - (effect-completed state side eid) - (clear-wait-prompt state :runner))})) - (install-card [chosen] - {:prompt "Select a remote server" - :choices (req (conj (vec (get-remote-names state)) "New remote")) + state side + {:choices {:req can-be-advanced?} + :msg (msg "place " c " advancement tokens on " (card-str state target)) + :effect (effect (add-prop :corp target :advance-counter c {:placed true}))} + card nil)))} + + "Shipment from Tennin" + {:async true + :req (req (not-last-turn? state :runner :successful-run)) + :choices {:req #(and (installed? %) (corp? %))} + :msg (msg "place 2 advancement tokens on " (card-str state target)) + :effect (effect (add-prop target :advance-counter 2 {:placed true}))} + + "Shoot the Moon" + {:choices {:req #(and (ice? %) (not (rezzed? %))) + :max (req (min (count-tags state) + (reduce (fn [c server] + (+ c (count (filter #(not (:rezzed %)) (:ices server))))) + 0 (flatten (seq (:servers corp))))))} + :req (req tagged) + :effect (req (doseq [t targets] (rez state side t {:ignore-cost :all-costs})) + (effect-completed state side eid))} + + "Snatch and Grab" + {:trace {:base 3 + :successful + {:msg "trash a connection" + :choices {:req #(has-subtype? % "Connection")} :async true - :effect (effect (clear-wait-prompt :runner) - (corp-install (move state side chosen :play-area) target))})] - {:msg "look at the top 5 cards of R&D" - :async true - :effect (req (show-wait-prompt state :runner "Corp to look at the top cards of R&D") - (let [top-5 (take 5 (:deck corp))] - (continue-ability state side (choose-card state top-5) card nil)))})) - -(define-card "Punitive Counterstrike" - {:trace {:base 5 - :successful {:async true - :msg (msg "do " (:stole-agenda runner-reg-last 0) " meat damage") - :effect (effect (damage eid :meat (:stole-agenda runner-reg-last 0) {:card card}))}}}) - -(define-card "Reclamation Order" - {:prompt "Select a card from Archives" - :show-discard true - :choices {:req #(and (corp? %) - (not= (:title %) "Reclamation Order") - (in-discard? %))} - :msg (msg "name " (:title target)) - :effect (req (let [title (:title target) - cards (filter #(= title (:title %)) (:discard corp)) - n (count cards)] - (continue-ability state side - {:prompt (str "Choose how many copies of " - title " to reveal") - :choices {:number (req n)} - :msg (msg "reveal " - (quantify target "cop" "y" "ies") - " of " title - " from Archives" - (when (pos? target) - (str " and add " - (if (= 1 target) "it" "them") - " to HQ"))) - :effect (req (reveal state side cards) - (doseq [c (->> cards - (sort-by :seen) - reverse - (take target))] - (move state side c :hand)))} - card nil)))}) - -(define-card "Recruiting Trip" - (let [rthelp (fn rt [total left selected] - (if (pos? left) - {:prompt (str "Choose a Sysop (" (inc (- total left)) "/" total ")") - :choices (req (cancellable (filter #(and (has-subtype? % "Sysop") - (not-any? #{(:title %)} selected)) (:deck corp)) :sorted)) - :msg (msg "put " (:title target) " into HQ") - :async true - :effect (req (move state side target :hand) - (continue-ability - state side - (rt total (dec left) (cons (:title target) selected)) - card nil))} - {:effect (effect (shuffle! :corp :deck)) - :msg (msg "shuffle R&D")}))] - {:prompt "How many Sysops?" - :async true - :choices :credit - :msg (msg "search for " target " Sysops") - :effect (effect (continue-ability (rthelp target target []) card nil))})) - -(define-card "Red Level Clearance" - (let [all [{:msg "gain 2 [Credits]" - :effect (effect (gain-credits 2))} - {:msg "draw 2 cards" - :effect (effect (draw 2))} - {:msg "gain [Click]" - :effect (effect (gain :click 1))} - {:prompt "Choose a non-agenda to install" - :msg "install a non-agenda from hand" - :choices {:req #(and (not (agenda? %)) - (not (operation? %)) + :effect (req (let [c target] + (show-wait-prompt state :corp "Runner to decide if they will take 1 tag") + (continue-ability + state side + {:player :runner + :prompt (msg "Take 1 tag to prevent " (:title c) " from being trashed?") + :choices ["Yes" "No"] + :async true + :effect (effect (clear-wait-prompt :corp) + (continue-ability + (if (= target "Yes") + {:msg (msg "take 1 tag to prevent " (:title c) + " from being trashed") + :async true + :effect (effect (gain-tags :runner eid 1 {:unpreventable true}))} + {:async true + :effect (effect (trash :corp eid c nil)) + :msg (msg "trash " (:title c))}) + card nil))} + card nil)))}}} + + "Special Report" + {:prompt "Select any number of cards in HQ to shuffle into R&D" + :choices {:max (req (count (:hand corp))) + :req #(and (corp? %) + (in-hand? %))} + :msg (msg "shuffle " (count targets) " cards in HQ into R&D and draw " (count targets) " cards") + :async true + :effect (req (doseq [c targets] + (move state side c :deck)) + (shuffle! state side :deck) + (draw state side eid (count targets) nil))} + + "Standard Procedure" + {:req (req (last-turn? state :runner :successful-run)) + :prompt "Choose a card type" + :choices ["Event" "Hardware" "Program" "Resource"] + :effect (req (let [n (* 2 (count (filter #(is-type? % target) (:hand runner))))] + (gain-credits state :corp n) + (reveal state side (:hand runner)) + (system-msg state side (str "uses Standard Procedure to name " target ", reveal " + (join ", " (map :title (:hand runner))) + " in the Runner's Grip, and gain " n " [Credits]"))))} + + "Stock Buy-Back" + {:msg (msg "gain " (* 3 (count (:scored runner))) " [Credits]") + :effect (effect (gain-credits (* 3 (count (:scored runner)))))} + + "Sub Boost" + (let [new-sub {:label "[Sub Boost]: End the run"}] + {:sub-effect {:label "End the run" + :msg "end the run" + :effect (effect (end-run eid card))} + :choices {:req #(and (ice? %) (rezzed? %))} + :msg (msg "make " (card-str state target) " gain Barrier and \"[Subroutine] End the run\"") + :effect (req (update! state side (assoc target :subtype (combine-subtypes true (:subtype target) "Barrier"))) + (add-extra-sub state :corp (:cid card) (get-card state target) -1 new-sub) + (update-ice-strength state side target) + (host state side (get-card state target) (assoc card :zone [:discard] :seen true :condition true))) + :leave-play (req (remove-extra-subs state :corp (:cid card) (:host card))) + :events {:rez {:req (req (same-card? target (:host card))) + :effect (req (add-extra-sub state :corp (:cid card) (get-card state target) -1 new-sub))}}}) + + "Subcontract" + (letfn [(sc [i sccard] + {:prompt "Select an operation in HQ to play" + :choices {:req #(and (corp? %) + (operation? %) (in-hand? %))} :async true - :effect (effect (corp-install eid target nil nil))}] - can-install? (fn [hand] - (seq (remove #(or (agenda? %) - (operation? %)) - hand))) - choice (fn choice [abis chose-once] - {:prompt "Choose an ability to resolve" - :choices (map #(capitalize (:msg %)) abis) - :async true - :effect (req (let [chosen (some #(when (= target (capitalize (:msg %))) %) abis)] - (if (or (not= target "Install a non-agenda from hand") - (and (= target "Install a non-agenda from hand") - (can-install? (:hand corp)))) - (wait-for (resolve-ability state side chosen card nil) - (if (false? chose-once) - (continue-ability state side - (choice (remove #(= % chosen) abis) true) - card nil) - (do (clear-wait-prompt state :runner) - (effect-completed state side eid)))) - (continue-ability state side (choice abis chose-once) card nil))))})] - {:async true - :effect (effect (show-wait-prompt :runner "Corp to use Red Level Clearance") - (continue-ability (choice all false) card nil))}) - - ) - -(define-card "Red Planet Couriers" - {:async true - :req (req (some #(can-be-advanced? %) (all-installed state :corp))) - :prompt "Select an installed card that can be advanced" - :choices {:req can-be-advanced?} - :effect (req (let [installed (get-all-installed state) - total-adv (reduce + (map #(get-counters % :advancement) installed))] - (doseq [c installed] - (set-prop state side c :advance-counter 0)) - (set-prop state side target :advance-counter total-adv) - (update-all-ice state side) - (system-msg state side (str "uses Red Planet Couriers to move " total-adv - " advancement tokens to " (card-str state target))) - (effect-completed state side eid)))}) - -(define-card "Replanting" - (letfn [(replant [n] - {:prompt "Select a card to install with Replanting" + :msg (msg "play " (:title target)) + :effect (req (wait-for (play-instant state side target) + (if (and (not (get-in @state [:corp :register :terminal])) (< i 2)) + (continue-ability state side (sc (inc i) sccard) sccard nil) + (effect-completed state side eid))))})] + {:req (req tagged) + :async true + :effect (effect (continue-ability (sc 1 card) card nil))}) + + "Subliminal Messaging" + (letfn [(subliminal [] + {:corp-phase-12 + {:effect + (req (if (not-last-turn? state :runner :made-run) + (do (resolve-ability state side + {:optional + {:prompt "Add Subliminal Messaging to HQ?" + :yes-ability {:effect (req (move state side card :hand) + (system-msg state side "adds Subliminal Messaging to HQ"))} + :no-ability {:effect (effect (register-events (subliminal) (assoc card :zone '(:discard))))}}} + card nil) + (unregister-events state side card)) + (do (unregister-events state side card) + (resolve-ability state side + {:effect (effect (register-events (subliminal) (assoc card :zone '(:discard))))} + card nil))))}})] + {:msg "gain 1 [Credits]" + :effect (effect (gain-credits 1) + (resolve-ability {:once :per-turn + :once-key :subliminal-messaging + :msg "gain [Click]" + :effect (effect (gain :corp :click 1))} card nil)) + :move-zone (req (if (= [:discard] (:zone card)) + (register-events state side (subliminal) (assoc card :zone '(:discard))) + (unregister-events state side card))) + :events {:corp-phase-12 nil}}) + + "Success" + {:additional-cost [:forfeit] + :effect (req (resolve-ability state side + {:choices {:req can-be-advanced?} + :msg (msg "advance " (card-str state target) " " + (advancement-cost state side (last (:rfg corp))) " times") + :effect (req (dotimes [_ (advancement-cost state side (last (:rfg corp)))] + (advance state :corp (make-eid state {:source card :source-type :advance}) target :no-cost)))} card nil))} + + "Successful Demonstration" + {:req (req (last-turn? state :runner :unsuccessful-run)) + :msg "gain 7 [Credits]" + :effect (effect (gain-credits 7))} + + "Sunset" + (letfn [(sun [serv] + {:prompt "Select two pieces of ICE to swap positions" + :choices {:req #(and (= serv (rest (butlast (:zone %)))) (ice? %)) + :max 2} + :async true + :effect (req (if (= (count targets) 2) + (do (swap-ice state side (first targets) (second targets)) + (continue-ability state side (sun serv) card nil)) + (do (system-msg state side "has finished rearranging ICE") + (effect-completed state side eid))))})] + {:prompt "Choose a server" + :choices (req servers) + :async true + :msg (msg "rearrange ICE protecting " target) + :effect (req (let [serv (next (server->zone state target))] + (continue-ability state side (sun serv) card nil)))}) + + "Surveillance Sweep" + {:events {:pre-init-trace {:req (req run) + :effect (req (swap! state assoc-in [:trace :player] :runner))}}} + + "Sweeps Week" + {:effect (effect (gain-credits (count (:hand runner)))) + :msg (msg "gain " (count (:hand runner)) " [Credits]")} + + "Targeted Marketing" + (let [gaincr {:req (req (= (:title target) (:marketing-target card))) + :effect (effect (gain-credits :corp 10)) + :msg (msg "gain 10 [Credits] from " (:marketing-target card))}] + {:prompt "Name a Runner card" + :choices {:card-title (req (and (runner? target) + (not (identity? target))))} + :effect (effect (update! (assoc card :marketing-target target)) + (system-msg (str "uses Targeted Marketing to name " target))) + :events {:runner-install gaincr + :play-event gaincr}}) + + "The All-Seeing I" + (let [trash-all-resources + {:msg "trash all resources" + :effect (effect (trash-cards :corp eid (filter resource? (all-active-installed state :runner)) nil))}] + {:req (req tagged) + :async true + :effect (effect + (continue-ability + (if (pos? (count-bad-pub state)) + {:optional + {:player :runner + :prompt "Remove 1 bad publicity to prevent all resources from being trashed?" + :yes-ability {:msg "remove 1 bad publicity, preventing all resources from being trashed" + :effect (effect (lose-bad-publicity 1))} + :no-ability trash-all-resources}} + trash-all-resources) + card nil))}) + + "Threat Assessment" + {:req (req (last-turn? state :runner :trashed-card)) + :prompt "Select an installed Runner card" + :choices {:req #(and (runner? %) (installed? %))} + :async true + :effect (req (let [chosen target] + (show-wait-prompt state side "Runner to resolve Threat Assessment") + (continue-ability + state :runner + {:prompt (str "Add " (:title chosen) " to the top of the Stack or take 2 tags?") + :choices [(str "Move " (:title chosen)) + "Take 2 tags"] + :async true + :effect (req (clear-wait-prompt state :corp) + (move state :corp (last (:discard corp)) :rfg) + (if (.startsWith target "Move") + (do (system-msg state side (str "chooses to move " (:title chosen) " to the Stack")) + (move state :runner chosen :deck {:front true}) + (effect-completed state side eid)) + (do (system-msg state side "chooses to take 2 tags") + (gain-tags state :runner eid 2))))} + card nil)))} + + "Threat Level Alpha" + {:trace {:base 1 + :successful + {:label "Give the Runner X tags" :async true - :choices {:req #(and (corp? %) - (not (operation? %)) - (in-hand? %))} - :effect (req (wait-for (corp-install state side target nil {:ignore-all-cost true}) - (if (< n 2) - (continue-ability state side (replant (inc n)) card nil) - (effect-completed state side eid))))})] - {:async true - :prompt "Select an installed card to add to HQ" - :choices {:req #(and (corp? %) - (installed? %))} - :msg (msg "add " (card-str state target) " to HQ, then install 2 cards ignoring all costs") - :effect (req (move state side target :hand) - (resolve-ability state side (replant 1) card nil))})) - -(define-card "Restore" - {:async true - :effect (effect (continue-ability - {:prompt "Select a card in Archives to install & rez with Restore" - :priority -1 + :effect (req (let [tags (max 1 (count-tags state))] + (gain-tags state :corp eid tags) + (system-msg + state side + (str "uses Threat Level Alpha to give the Runner " (quantify tags "tag")))))}}} + + "Too Big to Fail" + {:req (req (< (:credit corp) 10)) + :msg "gain 7 [Credits] and take 1 bad publicity" + :effect (effect (gain-credits 7) + (gain-bad-publicity :corp 1))} + + "Traffic Accident" + {:req (req (>= (count-tags state) 2)) + :msg "do 2 meat damage" + :async true + :effect (effect (damage eid :meat 2 {:card card}))} + + "Transparency Initiative" + {:choices {:req #(and (agenda? %) + (installed? %) + (not (faceup? %)))} + :effect (effect (update! (assoc target + :seen true + :rezzed true + :subtype (combine-subtypes false (:subtype target) "Public"))) + (host (get-card state target) (assoc card + :zone [:discard] + :seen true)) + (register-events + {:advance {:req (req (= (:hosted card) (:hosted target))) + :effect (effect (gain-credits 1) + (system-msg + (str "uses Transparency Initiative to gain 1 [Credit]")))}} + target))} + + "Trick of Light" + {:choices {:req #(pos? (get-counters % :advancement))} + :async true + :effect (req (let [fr target tol card] + (continue-ability + state side + {:prompt "Move how many advancement tokens?" + :choices (take (inc (get-counters fr :advancement)) ["0" "1" "2"]) :async true - :show-discard true - :choices {:req #(and (corp? %) - (not (operation? %)) - (in-discard? %))} - :effect (req (wait-for - (corp-install state side target nil {:install-state :rezzed}) - (do (system-msg state side (str "uses Restore to " - (corp-install-msg target))) - (let [leftover (filter #(= (:title target) (:title %)) (-> @state :corp :discard))] - (when (seq leftover) - (doseq [c leftover] - (move state side c :rfg)) - (system-msg state side (str "removes " (count leftover) " copies of " (:title target) " from the game")))) - (effect-completed state side eid))))} - card nil))}) - -(define-card "Restoring Face" - {:async true - :prompt "Select a Sysop, Executive or Clone to trash" - :msg (msg "trash " (card-str state target) " to remove 2 bad publicity") - :choices {:req #(and (rezzed? %) - (or (has-subtype? % "Clone") - (has-subtype? % "Executive") - (has-subtype? % "Sysop")))} - :effect (effect (lose-bad-publicity 2) - (trash eid target nil))}) - -(define-card "Restructure" - {:msg "gain 15 [Credits]" - :effect (effect (gain-credits 15))}) - -(define-card "Reuse" - {:async true - :effect (req (let [n (count (:hand corp))] - (continue-ability - state side - {:prompt (msg "Select up to " n " cards in HQ to trash with Reuse") - :choices {:max n - :req #(and (corp? %) - (in-hand? %))} - :msg (msg (let [m (count targets)] - (str "trash " (quantify m "card") - " and gain " (* 2 m) " [Credits]"))) - :effect (effect (trash-cards targets) - (gain-credits (* 2 (count targets))))} card nil)))}) - -(define-card "Reverse Infection" - {:prompt "Choose One:" - :choices ["Purge virus counters" - "Gain 2 [Credits]"] - :effect (req (if (= target "Gain 2 [Credits]") - (do (gain-credits state side 2) - (system-msg state side "uses Reverse Infection to gain 2 [Credits]")) - (let [pre-purge-virus (number-of-virus-counters state)] - (purge state side) - (let [post-purge-virus (number-of-virus-counters state) - num-virus-purged (- pre-purge-virus post-purge-virus) - num-to-trash (quot num-virus-purged 3)] - (mill state :corp :runner num-to-trash) - (system-msg state side (str "uses Reverse Infection to purge " - num-virus-purged (pluralize " virus counter" num-virus-purged) - " and trash " - num-to-trash (pluralize " card" num-to-trash) - " from the top of the stack"))))))}) - -(define-card "Rework" - {:prompt "Select a card from HQ to shuffle into R&D" - :choices {:req #(and (corp? %) - (in-hand? %))} - :msg "shuffle a card from HQ into R&D" - :effect (effect (move target :deck) - (shuffle! :deck))}) - -(define-card "Riot Suppression" - {:req (req (last-turn? state :runner :trashed-card)) - :async true - :effect (req (let [c card] - (show-wait-prompt state :corp "Runner to decide if they will take 1 brain damage") - (continue-ability - state :runner - {:optional - {:prompt "Take 1 brain damage to prevent having 3 fewer clicks next turn?" - :player :runner - :end-effect (req (clear-wait-prompt state :corp) - (move state :corp (find-latest state c) :rfg)) - :yes-ability - {:async true - :effect (req (system-msg - state :runner - "suffers 1 brain damage to prevent losing 3[Click] to Riot Suppression") - (damage state :runner eid :brain 1 {:card card}))} - :no-ability - {:msg "give the runner 3 fewer [Click] next turn" - :effect (req (swap! state update-in [:runner :extra-click-temp] (fnil #(- % 3) 0)))}}} - card nil)))}) - -(define-card "Rolling Brownout" - {:msg "increase the play cost of operations and events by 1 [Credits]" - :events {:play-event {:once :per-turn - :msg "gain 1 [Credits]" - :effect (effect (gain-credits :corp 1))} - :pre-play-instant {:effect (effect (play-cost-bonus [:credit 1]))}}}) - -(define-card "Rover Algorithm" - {:choices {:req #(and (ice? %) (rezzed? %))} - :msg (msg "host it as a condition counter on " (card-str state target)) - :effect (effect (host target (assoc card :zone [:discard] :seen true :condition true)) - (update-ice-strength (get-card state target))) - :events {:pass-ice {:req (req (same-card? target (:host card))) - :effect (effect (add-counter card :power 1))} - :pre-ice-strength {:req (req (same-card? target (:host card))) - :effect (effect (ice-strength-bonus (get-counters card :power) target))}}}) - -(define-card "Sacrifice" - {:req (req (and (pos? (count-bad-pub state)) - (some #(pos? (:agendapoints %)) (:scored corp)))) - :additional-cost [:forfeit] - :effect (req (let [bp-lost (max 0 (min (:agendapoints (last (:rfg corp))) - (count-bad-pub state)))] - (system-msg state side (str "uses Sacrifice to lose " bp-lost " bad publicity and gain " bp-lost " [Credits]")) - (when (pos? bp-lost) - (lose-bad-publicity state side bp-lost) - (gain-credits state side bp-lost))))}) - -(define-card "Salem's Hospitality" - {:prompt "Name a Runner card" - :choices {:card-title (req (and (runner? target) - (not (identity? target))))} - :effect (req (system-msg state side - (str "uses Salem's Hospitality to reveal the Runner's Grip ( " - (join ", " (map :title (sort-by :title (:hand runner)))) - " ) and trash any copies of " target)) - (reveal state side (filter #(= target (:title %)) (:hand runner))) - (doseq [c (filter #(= target (:title %)) (:hand runner))] - (trash state side c {:unpreventable true})))}) - -(define-card "Scarcity of Resources" - {:msg "increase the install cost of resources by 2" - :events {:pre-install {:req (req (and (resource? target) - (not (second targets)))) ; not facedown - :effect (effect (install-cost-bonus [:credit 2]))}}}) - -(define-card "Scorched Earth" - {:req (req tagged) - :async true - :msg "do 4 meat damage" - :effect (effect (damage eid :meat 4 {:card card}))}) - -(define-card "SEA Source" - {:req (req (last-turn? state :runner :successful-run)) - :trace {:base 3 - :label "Trace 3 - Give the Runner 1 tag" - :successful {:msg "give the Runner 1 tag" - :async true - :effect (effect (gain-tags :corp eid 1))}}}) - -(define-card "Secure and Protect" - {:interactive (req true) - :async true - :effect (req (if (seq (filter ice? (:deck corp))) - (do (show-wait-prompt state :runner "Corp to use Secure and Protect") - (continue-ability - state side - {:async true - :prompt "Choose a piece of ice" - :choices (req (filter ice? (:deck corp))) - :effect (req (let [chosen-ice target] + :effect (req (let [c (str->int target)] + (continue-ability + state side + {:prompt "Move to where?" + :choices {:req #(and (not (same-card? fr %)) + (can-be-advanced? %))} + :effect (effect (add-prop :corp target :advance-counter c {:placed true}) + (add-prop :corp fr :advance-counter (- c) {:placed true}) + (system-msg (str "moves " c " advancement tokens from " + (card-str state fr) " to " (card-str state target))))} + tol nil)))} + card nil)))} + + "Trojan Horse" + {:req (req (:accessed-cards runner-reg)) + :trace {:base 4 + :label "Trace 4 - Trash a program" + :successful {:async true + :effect (req (let [exceed (- target (second targets))] (continue-ability state side {:async true - :prompt (str "Select a server to install " (:title chosen-ice) " on") - :choices ["Archives" "R&D" "HQ"] - :msg (msg "reveal " (:title chosen-ice) "and install it, paying 3[credit] less") - :effect (effect (clear-wait-prompt :runner) - (reveal state side chosen-ice) - (shuffle! :deck) - (install-cost-bonus [:credit -3]) - (corp-install eid chosen-ice target nil))} - card nil)))} - card nil)) - (do (shuffle! state side :deck) - (effect-completed state side eid))))}) - -(define-card "Self-Growth Program" - {:req (req tagged) - :prompt "Select two installed Runner cards" - :choices {:req #(and (installed? %) - (runner? %)) - :max 2} - :msg (msg (str "move " (join ", " (map :title targets)) " to the Runner's grip")) - :effect (req (doseq [c targets] - (move state :runner c :hand)))}) - -(define-card "Service Outage" - {:msg "add a cost of 1 [Credit] for the Runner to make the first run each turn" - :events {:pre-init-run {:req (req (first-event? state side :pre-init-run)) - :effect (effect (run-additional-cost-bonus [:credit 1]))}}}) - -(define-card "Shipment from Kaguya" - {:choices {:max 2 :req can-be-advanced?} - :msg (msg "place 1 advancement token on " (count targets) " cards") - :effect (req (doseq [t targets] - (add-prop state :corp t :advance-counter 1 {:placed true})) - (effect-completed state side eid))}) - -(define-card "Shipment from MirrorMorph" - (let [shelper (fn sh [n] {:prompt "Select a card to install with Shipment from MirrorMorph" - :async true - :choices {:req #(and (corp? %) - (not (operation? %)) - (in-hand? %))} - :effect (req (wait-for - (corp-install state side target nil nil) - (if (< n 3) - (continue-ability state side (sh (inc n)) card nil) - (effect-completed state side eid))))})] - {:async true - :effect (effect (continue-ability (shelper 1) card nil))})) - -(define-card "Shipment from SanSan" - {:choices ["0", "1", "2"] - :prompt "How many advancement tokens?" - :async true - :effect (req (let [c (str->int target)] - (continue-ability - state side - {:choices {:req can-be-advanced?} - :msg (msg "place " c " advancement tokens on " (card-str state target)) - :effect (effect (add-prop :corp target :advance-counter c {:placed true}))} - card nil)))}) - -(define-card "Shipment from Tennin" - {:async true - :req (req (not-last-turn? state :runner :successful-run)) - :choices {:req #(and (installed? %) (corp? %))} - :msg (msg "place 2 advancement tokens on " (card-str state target)) - :effect (effect (add-prop target :advance-counter 2 {:placed true}))}) - -(define-card "Shoot the Moon" - {:choices {:req #(and (ice? %) (not (rezzed? %))) - :max (req (min (count-tags state) - (reduce (fn [c server] - (+ c (count (filter #(not (:rezzed %)) (:ices server))))) - 0 (flatten (seq (:servers corp))))))} - :req (req tagged) - :effect (req (doseq [t targets] (rez state side t {:ignore-cost :all-costs})) - (effect-completed state side eid))}) - -(define-card "Snatch and Grab" - {:trace {:base 3 - :successful - {:msg "trash a connection" - :choices {:req #(has-subtype? % "Connection")} - :async true - :effect (req (let [c target] - (show-wait-prompt state :corp "Runner to decide if they will take 1 tag") - (continue-ability - state side - {:player :runner - :prompt (msg "Take 1 tag to prevent " (:title c) " from being trashed?") - :choices ["Yes" "No"] - :async true - :effect (effect (clear-wait-prompt :corp) - (continue-ability - (if (= target "Yes") - {:msg (msg "take 1 tag to prevent " (:title c) - " from being trashed") - :async true - :effect (effect (gain-tags :runner eid 1 {:unpreventable true}))} - {:async true - :effect (effect (trash :corp eid c nil)) - :msg (msg "trash " (:title c))}) - card nil))} - card nil)))}}}) - -(define-card "Special Report" - {:prompt "Select any number of cards in HQ to shuffle into R&D" - :choices {:max (req (count (:hand corp))) - :req #(and (corp? %) - (in-hand? %))} - :msg (msg "shuffle " (count targets) " cards in HQ into R&D and draw " (count targets) " cards") - :async true - :effect (req (doseq [c targets] - (move state side c :deck)) - (shuffle! state side :deck) - (draw state side eid (count targets) nil))}) - -(define-card "Standard Procedure" - {:req (req (last-turn? state :runner :successful-run)) - :prompt "Choose a card type" - :choices ["Event" "Hardware" "Program" "Resource"] - :effect (req (let [n (* 2 (count (filter #(is-type? % target) (:hand runner))))] - (gain-credits state :corp n) - (reveal state side (:hand runner)) - (system-msg state side (str "uses Standard Procedure to name " target ", reveal " - (join ", " (map :title (:hand runner))) - " in the Runner's Grip, and gain " n " [Credits]"))))}) - -(define-card "Stock Buy-Back" - {:msg (msg "gain " (* 3 (count (:scored runner))) " [Credits]") - :effect (effect (gain-credits (* 3 (count (:scored runner)))))}) - -(define-card "Sub Boost" - (let [new-sub {:label "[Sub Boost]: End the run"}] - {:sub-effect {:label "End the run" - :msg "end the run" - :effect (effect (end-run eid card))} - :choices {:req #(and (ice? %) (rezzed? %))} - :msg (msg "make " (card-str state target) " gain Barrier and \"[Subroutine] End the run\"") - :effect (req (update! state side (assoc target :subtype (combine-subtypes true (:subtype target) "Barrier"))) - (add-extra-sub state :corp (:cid card) (get-card state target) -1 new-sub) - (update-ice-strength state side target) - (host state side (get-card state target) (assoc card :zone [:discard] :seen true :condition true))) - :leave-play (req (remove-extra-subs state :corp (:cid card) (:host card))) - :events {:rez {:req (req (same-card? target (:host card))) - :effect (req (add-extra-sub state :corp (:cid card) (get-card state target) -1 new-sub))}}})) - -(define-card "Subcontract" - (letfn [(sc [i sccard] - {:prompt "Select an operation in HQ to play" - :choices {:req #(and (corp? %) - (operation? %) - (in-hand? %))} - :async true - :msg (msg "play " (:title target)) - :effect (req (wait-for (play-instant state side target) - (if (and (not (get-in @state [:corp :register :terminal])) (< i 2)) - (continue-ability state side (sc (inc i) sccard) sccard nil) - (effect-completed state side eid))))})] - {:req (req tagged) - :async true - :effect (effect (continue-ability (sc 1 card) card nil))})) - -(define-card "Subliminal Messaging" - (letfn [(subliminal [] - {:corp-phase-12 - {:effect - (req (if (not-last-turn? state :runner :made-run) - (do (resolve-ability state side - {:optional - {:prompt "Add Subliminal Messaging to HQ?" - :yes-ability {:effect (req (move state side card :hand) - (system-msg state side "adds Subliminal Messaging to HQ"))} - :no-ability {:effect (effect (register-events (subliminal) (assoc card :zone '(:discard))))}}} - card nil) - (unregister-events state side card)) - (do (unregister-events state side card) - (resolve-ability state side - {:effect (effect (register-events (subliminal) (assoc card :zone '(:discard))))} - card nil))))}})] - {:msg "gain 1 [Credits]" - :effect (effect (gain-credits 1) - (resolve-ability {:once :per-turn - :once-key :subliminal-messaging - :msg "gain [Click]" - :effect (effect (gain :corp :click 1))} card nil)) - :move-zone (req (if (= [:discard] (:zone card)) - (register-events state side (subliminal) (assoc card :zone '(:discard))) - (unregister-events state side card))) - :events {:corp-phase-12 nil}})) - -(define-card "Success" - {:additional-cost [:forfeit] - :effect (req (resolve-ability state side - {:choices {:req can-be-advanced?} - :msg (msg "advance " (card-str state target) " " - (advancement-cost state side (last (:rfg corp))) " times") - :effect (req (dotimes [_ (advancement-cost state side (last (:rfg corp)))] - (advance state :corp (make-eid state {:source card :source-type :advance}) target :no-cost)))} card nil))}) - -(define-card "Successful Demonstration" - {:req (req (last-turn? state :runner :unsuccessful-run)) - :msg "gain 7 [Credits]" - :effect (effect (gain-credits 7))}) - -(define-card "Sunset" - (letfn [(sun [serv] - {:prompt "Select two pieces of ICE to swap positions" - :choices {:req #(and (= serv (rest (butlast (:zone %)))) (ice? %)) - :max 2} - :async true - :effect (req (if (= (count targets) 2) - (do (swap-ice state side (first targets) (second targets)) - (continue-ability state side (sun serv) card nil)) - (do (system-msg state side "has finished rearranging ICE") - (effect-completed state side eid))))})] - {:prompt "Choose a server" - :choices (req servers) - :async true - :msg (msg "rearrange ICE protecting " target) - :effect (req (let [serv (next (server->zone state target))] - (continue-ability state side (sun serv) card nil)))})) - -(define-card "Surveillance Sweep" - {:events {:pre-init-trace {:req (req run) - :effect (req (swap! state assoc-in [:trace :player] :runner))}}}) - -(define-card "Sweeps Week" - {:effect (effect (gain-credits (count (:hand runner)))) - :msg (msg "gain " (count (:hand runner)) " [Credits]")}) - -(define-card "Targeted Marketing" - (let [gaincr {:req (req (= (:title target) (:marketing-target card))) - :effect (effect (gain-credits :corp 10)) - :msg (msg "gain 10 [Credits] from " (:marketing-target card))}] - {:prompt "Name a Runner card" - :choices {:card-title (req (and (runner? target) - (not (identity? target))))} - :effect (effect (update! (assoc card :marketing-target target)) - (system-msg (str "uses Targeted Marketing to name " target))) - :events {:runner-install gaincr - :play-event gaincr}})) - -(define-card "The All-Seeing I" - (let [trash-all-resources - {:msg "trash all resources" - :effect (effect (trash-cards :corp eid (filter resource? (all-active-installed state :runner)) nil))}] - {:req (req tagged) - :async true - :effect (effect - (continue-ability - (if (pos? (count-bad-pub state)) - {:optional - {:player :runner - :prompt "Remove 1 bad publicity to prevent all resources from being trashed?" - :yes-ability {:msg "remove 1 bad publicity, preventing all resources from being trashed" - :effect (effect (lose-bad-publicity 1))} - :no-ability trash-all-resources}} - trash-all-resources) - card nil))})) - -(define-card "Threat Assessment" - {:req (req (last-turn? state :runner :trashed-card)) - :prompt "Select an installed Runner card" - :choices {:req #(and (runner? %) (installed? %))} - :async true - :effect (req (let [chosen target] - (show-wait-prompt state side "Runner to resolve Threat Assessment") - (continue-ability - state :runner - {:prompt (str "Add " (:title chosen) " to the top of the Stack or take 2 tags?") - :choices [(str "Move " (:title chosen)) - "Take 2 tags"] - :async true - :effect (req (clear-wait-prompt state :corp) - (move state :corp (last (:discard corp)) :rfg) - (if (.startsWith target "Move") - (do (system-msg state side (str "chooses to move " (:title chosen) " to the Stack")) - (move state :runner chosen :deck {:front true}) - (effect-completed state side eid)) - (do (system-msg state side "chooses to take 2 tags") - (gain-tags state :runner eid 2))))} - card nil)))}) - -(define-card "Threat Level Alpha" - {:trace {:base 1 - :successful - {:label "Give the Runner X tags" - :async true - :effect (req (let [tags (max 1 (count-tags state))] - (gain-tags state :corp eid tags) - (system-msg - state side - (str "uses Threat Level Alpha to give the Runner " (quantify tags "tag")))))}}}) - -(define-card "Too Big to Fail" - {:req (req (< (:credit corp) 10)) - :msg "gain 7 [Credits] and take 1 bad publicity" - :effect (effect (gain-credits 7) - (gain-bad-publicity :corp 1))}) - -(define-card "Traffic Accident" - {:req (req (>= (count-tags state) 2)) - :msg "do 2 meat damage" - :async true - :effect (effect (damage eid :meat 2 {:card card}))}) - -(define-card "Transparency Initiative" - {:choices {:req #(and (agenda? %) - (installed? %) - (not (faceup? %)))} - :effect (effect (update! (assoc target - :seen true - :rezzed true - :subtype (combine-subtypes false (:subtype target) "Public"))) - (host (get-card state target) (assoc card - :zone [:discard] - :seen true)) - (register-events - {:advance {:req (req (= (:hosted card) (:hosted target))) - :effect (effect (gain-credits 1) - (system-msg - (str "uses Transparency Initiative to gain 1 [Credit]")))}} - target))}) - -(define-card "Trick of Light" - {:choices {:req #(pos? (get-counters % :advancement))} - :async true - :effect (req (let [fr target tol card] - (continue-ability - state side - {:prompt "Move how many advancement tokens?" - :choices (take (inc (get-counters fr :advancement)) ["0" "1" "2"]) - :async true - :effect (req (let [c (str->int target)] - (continue-ability - state side - {:prompt "Move to where?" - :choices {:req #(and (not (same-card? fr %)) - (can-be-advanced? %))} - :effect (effect (add-prop :corp target :advance-counter c {:placed true}) - (add-prop :corp fr :advance-counter (- c) {:placed true}) - (system-msg (str "moves " c " advancement tokens from " - (card-str state fr) " to " (card-str state target))))} - tol nil)))} - card nil)))}) - -(define-card "Trojan Horse" - {:req (req (:accessed-cards runner-reg)) - :trace {:base 4 - :label "Trace 4 - Trash a program" - :successful {:async true - :effect (req (let [exceed (- target (second targets))] - (continue-ability - state side - {:async true - :prompt (str "Select a program with an install cost of no more than " - exceed "[Credits]") - :choices {:req #(and (program? %) - (installed? %) - (>= exceed (:cost %)))} - :msg (msg "trash " (card-str state target)) - :effect (effect (trash eid target nil))} - card nil)))}}}) - -(define-card "Ultraviolet Clearance" - {:async true - :effect (req (gain-credits state side 10) - (wait-for (draw state side 4 nil) - (continue-ability state side - {:prompt "Choose a card in HQ to install" - :choices {:req #(and (in-hand? %) (corp? %) (not (operation? %)))} - :msg "gain 10 [Credits], draw 4 cards, and install 1 card from HQ" - :cancel-effect (req (effect-completed state side eid)) - :effect (effect (corp-install target nil))} - card nil)))}) - -(define-card "Under the Bus" - {:req (req (and (last-turn? state :runner :accessed-cards) - (not-empty (filter - #(and (resource? %) - (has-subtype? % "Connection")) - (all-active-installed state :runner))))) - :prompt "Choose a connection to trash" - :choices {:req #(and (runner? %) - (resource? %) - (has-subtype? % "Connection") - (installed? %))} - :msg (msg "trash " (:title target) " and take 1 bad publicity") - :async true - :effect (req (wait-for (trash state side target nil) - (gain-bad-publicity state :corp eid 1)))}) - -(define-card "Violet Level Clearance" - {:msg "gain 8 [Credits] and draw 4 cards" - :async true - :effect (effect (gain-credits 8) - (draw eid 4 nil))}) - -(define-card "Voter Intimidation" - {:req (req (seq (:scored runner))) - :psi {:not-equal {:player :corp - :prompt "Select a resource to trash" - :choices {:req #(and (installed? %) - (resource? %))} - :msg (msg "trash " (:title target)) - :effect (effect (trash target) - (effect-completed eid))}}}) - -(define-card "Wake Up Call" - {:req (req (last-turn? state :runner :trashed-card)) - :prompt "Select a piece of hardware or non-virtual resource" - :choices {:req #(or (hardware? %) - (and (resource? %) (not (has-subtype? % "Virtual"))))} - :async true - :effect (effect (show-wait-prompt "Runner to resolve Wake Up Call") - (continue-ability - :runner - (let [chosen target - wake card] - {:prompt (str "Trash " (:title chosen) " or suffer 4 meat damage?") - :choices [(str "Trash " (:title chosen)) - "4 meat damage"] - :async true - :effect (req (clear-wait-prompt state :corp) - (move state :corp (last (:discard corp)) :rfg) - (if (= target "4 meat damage") - (do (system-msg state side "chooses to suffer meat damage") - (damage state side eid :meat 4 {:card wake - :unboostable true})) - (do (system-msg state side (str "chooses to trash " (:title chosen))) - (trash state side eid chosen nil))))}) - card nil))}) - -(define-card "Wetwork Refit" - (let [new-sub {:label "[Wetwork Refit] Do 1 brain damage"}] - {:choices {:req #(and (ice? %) - (has-subtype? % "Bioroid") - (rezzed? %))} - :msg (msg "give " (card-str state target) " \"[Subroutine] Do 1 brain damage\" before all its other subroutines") - :sub-effect (do-brain-damage 1) - :effect (req (add-extra-sub state :corp (:cid card) target 0 new-sub) - (host state side (get-card state target) (assoc card :zone [:discard] :seen true :condition true))) - :leave-play (req (remove-extra-subs state :corp (:cid card) (:host card))) - :events {:rez {:req (req (same-card? target (:host card))) - :effect (req (add-extra-sub state :corp (:cid card) (get-card state target) 0 new-sub))}}})) - -(define-card "Witness Tampering" - {:msg "remove 2 bad publicity" - :effect (effect (lose-bad-publicity 2))}) + :prompt (str "Select a program with an install cost of no more than " + exceed "[Credits]") + :choices {:req #(and (program? %) + (installed? %) + (>= exceed (:cost %)))} + :msg (msg "trash " (card-str state target)) + :effect (effect (trash eid target nil))} + card nil)))}}} + + "Ultraviolet Clearance" + {:async true + :effect (req (gain-credits state side 10) + (wait-for (draw state side 4 nil) + (continue-ability state side + {:prompt "Choose a card in HQ to install" + :choices {:req #(and (in-hand? %) (corp? %) (not (operation? %)))} + :msg "gain 10 [Credits], draw 4 cards, and install 1 card from HQ" + :cancel-effect (req (effect-completed state side eid)) + :effect (effect (corp-install target nil))} + card nil)))} + + "Under the Bus" + {:req (req (and (last-turn? state :runner :accessed-cards) + (not-empty (filter + #(and (resource? %) + (has-subtype? % "Connection")) + (all-active-installed state :runner))))) + :prompt "Choose a connection to trash" + :choices {:req #(and (runner? %) + (resource? %) + (has-subtype? % "Connection") + (installed? %))} + :msg (msg "trash " (:title target) " and take 1 bad publicity") + :async true + :effect (req (wait-for (trash state side target nil) + (gain-bad-publicity state :corp eid 1)))} + + "Violet Level Clearance" + {:msg "gain 8 [Credits] and draw 4 cards" + :async true + :effect (effect (gain-credits 8) + (draw eid 4 nil))} + + "Voter Intimidation" + {:req (req (seq (:scored runner))) + :psi {:not-equal {:player :corp + :prompt "Select a resource to trash" + :choices {:req #(and (installed? %) + (resource? %))} + :msg (msg "trash " (:title target)) + :effect (effect (trash target) + (effect-completed eid))}}} + + "Wake Up Call" + {:req (req (last-turn? state :runner :trashed-card)) + :prompt "Select a piece of hardware or non-virtual resource" + :choices {:req #(or (hardware? %) + (and (resource? %) (not (has-subtype? % "Virtual"))))} + :async true + :effect (effect (show-wait-prompt "Runner to resolve Wake Up Call") + (continue-ability + :runner + (let [chosen target + wake card] + {:prompt (str "Trash " (:title chosen) " or suffer 4 meat damage?") + :choices [(str "Trash " (:title chosen)) + "4 meat damage"] + :async true + :effect (req (clear-wait-prompt state :corp) + (move state :corp (last (:discard corp)) :rfg) + (if (= target "4 meat damage") + (do (system-msg state side "chooses to suffer meat damage") + (damage state side eid :meat 4 {:card wake + :unboostable true})) + (do (system-msg state side (str "chooses to trash " (:title chosen))) + (trash state side eid chosen nil))))}) + card nil))} + + "Wetwork Refit" + (let [new-sub {:label "[Wetwork Refit] Do 1 brain damage"}] + {:choices {:req #(and (ice? %) + (has-subtype? % "Bioroid") + (rezzed? %))} + :msg (msg "give " (card-str state target) " \"[Subroutine] Do 1 brain damage\" before all its other subroutines") + :sub-effect (do-brain-damage 1) + :effect (req (add-extra-sub state :corp (:cid card) target 0 new-sub) + (host state side (get-card state target) (assoc card :zone [:discard] :seen true :condition true))) + :leave-play (req (remove-extra-subs state :corp (:cid card) (:host card))) + :events {:rez {:req (req (same-card? target (:host card))) + :effect (req (add-extra-sub state :corp (:cid card) (get-card state target) 0 new-sub))}}}) + + "Witness Tampering" + {:msg "remove 2 bad publicity" + :effect (effect (lose-bad-publicity 2))}}) diff --git a/src/clj/game/cards/programs.clj b/src/clj/game/cards/programs.clj index f658018602..aef1ef3f4d 100644 --- a/src/clj/game/cards/programs.clj +++ b/src/clj/game/cards/programs.clj @@ -1,7 +1,7 @@ (ns game.cards.programs (:require [game.core :refer :all] [game.core.eid :refer [effect-completed]] - [game.core.card-defs :refer [card-def define-card]] + [game.core.card-defs :refer [card-def]] [game.utils :refer :all] [game.macros :refer [effect req msg wait-for continue-ability when-let*]] [clojure.string :refer [split-lines split join lower-case includes? starts-with?]] @@ -265,10 +265,10 @@ state side (pick-virus-counters-to-spend) card nil) (when-let* [message (:msg async-result) n (:number async-result)] - (system-msg state :runner - (str "spends " message - " to break " - (quantify n type-subroutine))))))} + (system-msg state :runner + (str "spends " message + " to break " + (quantify n type-subroutine))))))} {:label "Match strength of currently encountered ice" :req (req (and current-ice (> (ice-strength state side current-ice) @@ -281,2095 +281,2096 @@ card nil) (when-let* [message (:msg async-result) n (:number async-result)] - (add-strength state card message n))))} + (add-strength state card message n))))} {:label "Add strength" :effect (req (wait-for (resolve-ability state side (pick-virus-counters-to-spend) card nil) (when-let* [message (:msg async-result) n (:number async-result)] - (add-strength state card message n))))}]})) + (add-strength state card message n))))}]})) ;; Card definitions -(define-card "Abagnale" - (auto-icebreaker ["Code Gate"] - {:abilities [(break-sub 1 1 "Code Gate") - (strength-pump 2 2) - {:label "Bypass Code Gate being encountered" - :req (req (has-subtype? current-ice "Code Gate")) - :msg (msg "trash it and bypass " (:title current-ice)) - :effect (effect (trash card {:cause :ability-cost}))}]})) - -(define-card "Adept" - (ancient-greek-breaker "adept" [{:cost [:credit 2] - :req (req (or (has-subtype? current-ice "Barrier") - (has-subtype? current-ice "Sentry"))) - :msg "break 1 Sentry or Barrier subroutine"}])) - -(define-card "Aghora" - (deva "Aghora")) - -(define-card "Algernon" - {:events - {:runner-turn-begins - {:req (req (can-pay? state :runner eid card nil [:credit 2])) - :optional - {:prompt (msg "Pay 2 [Credits] to gain [Click]") - :player :runner - :yes-ability {:msg "gain [Click]" - :effect (req (gain state :runner :click 1) - (update! state :runner (assoc-in (get-card state card) [:special :used-algernon] true))) - :cost [:credit 2]}}} - :runner-turn-ends - {:async true - :effect (req (if (get-in card [:special :used-algernon]) - (do - (update! state :runner (dissoc-in card [:special :used-algernon])) - (if-not (:successful-run runner-reg) - (do - (system-msg state :runner "trashes Algernon because a successful run did not occur") - (trash state :runner eid card nil)) - (effect-completed state side eid))) - (effect-completed state side eid)))}}}) - -(define-card "Alias" - (central-breaker "Sentry" - (break-sub 1 1 "Sentry") - (strength-pump 2 3))) - -(define-card "Alpha" - (auto-icebreaker ["All"] - {:abilities [{:cost [:credit 1] - :req (req (= (:position run) (count run-ices))) - :msg "break 1 subroutine on the outermost ICE protecting this server"} - (strength-pump 1 1)]})) - -(define-card "Amina" - (auto-icebreaker ["Code Gate"] - {:abilities [(break-sub 2 3 "Code Gate") - (strength-pump 2 3) - {:once :per-turn - :label "Corp loses 1 [Credits]" - :req (req (and (has-subtype? current-ice "Code Gate") - (rezzed? current-ice))) - :msg (msg "make the Corp lose 1 [Credits]") - :effect (effect (lose-credits :corp 1))}]})) - -(define-card "Analog Dreamers" - {:abilities [{:cost [:click 1] - :msg "make a run on R&D" - :makes-run true - :effect (effect - (make-run :rd - {:req (req (= target :rd)) - :replace-access - {:prompt "Choose a card to shuffle into R&D" - :choices {:req #(and (not (ice? %)) - (not (rezzed? %)) - (zero? (get-counters % :advancement)))} - :effect (req (move state :corp target :deck) - (shuffle! state :corp :deck) - (swap! state update-in [:runner :prompt] rest) - (handle-end-run state side)) ; remove the replace-access prompt - :msg "shuffle a card into R&D"}} - card))}]}) - -(define-card "Ankusa" - (auto-icebreaker ["Barrier"] - {:abilities [(break-sub 2 1 "Barrier") - (strength-pump 1 1) - {:label "Add Barrier to HQ" - :req (req (and (has-subtype? current-ice "Barrier") - (rezzed? current-ice))) - :msg (msg "add " (:title current-ice) " to HQ after breaking all its subroutines") - :effect (req (let [c current-ice] - (move state :corp c :hand nil) - (continue state side nil)))}]})) - -(define-card "Atman" - {:prompt "How many power counters?" - :choices :credit - :msg (msg "add " target " power counters") - :effect (effect (add-counter card :power target)) - :abilities [(break-sub 1 1)] - :strength-bonus (req (get-counters card :power)) - :events {:counter-added {:req (req (same-card? target card)) - :effect (effect (update-breaker-strength card))}}}) - -(define-card "Au Revoir" - {:events {:jack-out {:effect (effect (gain-credits 1)) - :msg "gain 1 [Credits]"}}}) - -(define-card "Aumakua" - {:implementation "Add counters manually for access outside of a run or cards that replace access like Ash" - ; We would need a :once :per-access key to make this work for Gang Sign etc. - :abilities [(break-sub 1 1) - {:label "Add a virus counter" - :effect (effect (system-msg "manually adds a virus counter to Aumakua") - (add-counter card :virus 1))}] - :strength-bonus (req (get-virus-counters state card)) - :events {:run-ends {:req (req (and (not (or (get-in @state [:run :did-trash]) - (get-in @state [:run :did-steal]))) - (get-in @state [:run :did-access]))) - :effect (effect (add-counter card :virus 1))} - :expose {:effect (effect (add-counter card :virus 1))} - :counter-added {:req (req (same-card? target card)) - :effect (effect (update-breaker-strength card))}}}) - -(define-card "Aurora" - (auto-icebreaker ["Barrier"] - {:abilities [(break-sub 2 1 "Barrier") - (strength-pump 2 3)]})) - -(define-card "Baba Yaga" - (let [host-click {:cost [:click 1] - :label "Install a non-AI icebreaker on Baba Yaga" - :prompt "Choose a non-AI icebreaker in your Grip to install on Baba Yaga" +(def card-definitions + {"Abagnale" + (auto-icebreaker ["Code Gate"] + {:abilities [(break-sub 1 1 "Code Gate") + (strength-pump 2 2) + {:label "Bypass Code Gate being encountered" + :req (req (has-subtype? current-ice "Code Gate")) + :msg (msg "trash it and bypass " (:title current-ice)) + :effect (effect (trash card {:cause :ability-cost}))}]}) + + "Adept" + (ancient-greek-breaker "adept" [{:cost [:credit 2] + :req (req (or (has-subtype? current-ice "Barrier") + (has-subtype? current-ice "Sentry"))) + :msg "break 1 Sentry or Barrier subroutine"}]) + + "Aghora" + (deva "Aghora") + + "Algernon" + {:events + {:runner-turn-begins + {:req (req (can-pay? state :runner eid card nil [:credit 2])) + :optional + {:prompt (msg "Pay 2 [Credits] to gain [Click]") + :player :runner + :yes-ability {:msg "gain [Click]" + :effect (req (gain state :runner :click 1) + (update! state :runner (assoc-in (get-card state card) [:special :used-algernon] true))) + :cost [:credit 2]}}} + :runner-turn-ends + {:async true + :effect (req (if (get-in card [:special :used-algernon]) + (do + (update! state :runner (dissoc-in card [:special :used-algernon])) + (if-not (:successful-run runner-reg) + (do + (system-msg state :runner "trashes Algernon because a successful run did not occur") + (trash state :runner eid card nil)) + (effect-completed state side eid))) + (effect-completed state side eid)))}}} + + "Alias" + (central-breaker "Sentry" + (break-sub 1 1 "Sentry") + (strength-pump 2 3)) + + "Alpha" + (auto-icebreaker ["All"] + {:abilities [{:cost [:credit 1] + :req (req (= (:position run) (count run-ices))) + :msg "break 1 subroutine on the outermost ICE protecting this server"} + (strength-pump 1 1)]}) + + "Amina" + (auto-icebreaker ["Code Gate"] + {:abilities [(break-sub 2 3 "Code Gate") + (strength-pump 2 3) + {:once :per-turn + :label "Corp loses 1 [Credits]" + :req (req (and (has-subtype? current-ice "Code Gate") + (rezzed? current-ice))) + :msg (msg "make the Corp lose 1 [Credits]") + :effect (effect (lose-credits :corp 1))}]}) + + "Analog Dreamers" + {:abilities [{:cost [:click 1] + :msg "make a run on R&D" + :makes-run true + :effect (effect + (make-run :rd + {:req (req (= target :rd)) + :replace-access + {:prompt "Choose a card to shuffle into R&D" + :choices {:req #(and (not (ice? %)) + (not (rezzed? %)) + (zero? (get-counters % :advancement)))} + :effect (req (move state :corp target :deck) + (shuffle! state :corp :deck) + (swap! state update-in [:runner :prompt] rest) + (handle-end-run state side)) ; remove the replace-access prompt + :msg "shuffle a card into R&D"}} + card))}]} + + "Ankusa" + (auto-icebreaker ["Barrier"] + {:abilities [(break-sub 2 1 "Barrier") + (strength-pump 1 1) + {:label "Add Barrier to HQ" + :req (req (and (has-subtype? current-ice "Barrier") + (rezzed? current-ice))) + :msg (msg "add " (:title current-ice) " to HQ after breaking all its subroutines") + :effect (req (let [c current-ice] + (move state :corp c :hand nil) + (continue state side nil)))}]}) + + "Atman" + {:prompt "How many power counters?" + :choices :credit + :msg (msg "add " target " power counters") + :effect (effect (add-counter card :power target)) + :abilities [(break-sub 1 1)] + :strength-bonus (req (get-counters card :power)) + :events {:counter-added {:req (req (same-card? target card)) + :effect (effect (update-breaker-strength card))}}} + + "Au Revoir" + {:events {:jack-out {:effect (effect (gain-credits 1)) + :msg "gain 1 [Credits]"}}} + + "Aumakua" + {:implementation "Add counters manually for access outside of a run or cards that replace access like Ash" + ; We would need a :once :per-access key to make this work for Gang Sign etc. + :abilities [(break-sub 1 1) + {:label "Add a virus counter" + :effect (effect (system-msg "manually adds a virus counter to Aumakua") + (add-counter card :virus 1))}] + :strength-bonus (req (get-virus-counters state card)) + :events {:run-ends {:req (req (and (not (or (get-in @state [:run :did-trash]) + (get-in @state [:run :did-steal]))) + (get-in @state [:run :did-access]))) + :effect (effect (add-counter card :virus 1))} + :expose {:effect (effect (add-counter card :virus 1))} + :counter-added {:req (req (same-card? target card)) + :effect (effect (update-breaker-strength card))}}} + + "Aurora" + (auto-icebreaker ["Barrier"] + {:abilities [(break-sub 2 1 "Barrier") + (strength-pump 2 3)]}) + + "Baba Yaga" + (let [host-click {:cost [:click 1] + :label "Install a non-AI icebreaker on Baba Yaga" + :prompt "Choose a non-AI icebreaker in your Grip to install on Baba Yaga" + :choices {:req #(and (has-subtype? % "Icebreaker") + (not (has-subtype? % "AI")) + (in-hand? %))} + :effect (effect (runner-install target {:host-card card}))} + host-free {:label "Host an installed non-AI icebreaker on Baba Yaga" + :prompt "Choose an installed non-AI icebreaker to host on Baba Yaga" :choices {:req #(and (has-subtype? % "Icebreaker") (not (has-subtype? % "AI")) - (in-hand? %))} - :effect (effect (runner-install target {:host-card card}))} - host-free {:label "Host an installed non-AI icebreaker on Baba Yaga" - :prompt "Choose an installed non-AI icebreaker to host on Baba Yaga" - :choices {:req #(and (has-subtype? % "Icebreaker") - (not (has-subtype? % "AI")) - (installed? %))} - :effect (req (when (host state side card target) - (gain :memory (:memoryunits target))))} - gain-abis (req (let [new-abis (mapcat (fn [c] (map-indexed #(assoc %2 - :dynamic :copy - :source (:title c) - :index %1 - :label (make-label %2)) - (filter #(not= :manual-state (:ability-type %)) - (:abilities (card-def c))))) - (:hosted card))] - (update! state :runner (assoc card :abilities (concat new-abis [host-click host-free])))))] - {:abilities [host-click host-free] - :hosted-gained gain-abis - :hosted-lost gain-abis})) - -(define-card "Bankroll" - {:implementation "Bankroll gains credits automatically." - :events {:successful-run {:effect (effect (add-counter card :credit 1) - (system-msg "places 1 [Credit] on Bankroll"))}} - :abilities [{:label "[Trash]: Take all credits from Bankroll" - :async true - ;; Cannot trash unless there are counters (so game state changes) - :req (req (pos? (get-counters card :credit))) - :effect (req (let [credits-on-bankroll (get-counters card :credit)] - (wait-for (trash state :runner card {:cause :ability-cost}) - (gain-credits state :runner credits-on-bankroll) - (system-msg state :runner (str "trashes Bankroll and takes " - credits-on-bankroll " credits from it")))))}]}) - -(define-card "Battering Ram" - (auto-icebreaker ["Barrier"] - {:abilities [(break-sub 2 2 "Barrier") - (strength-pump 1 1 :all-run)]})) - -(define-card "Berserker" - {:implementation "Encounter effect is manual. Only gains strength on printed subs" - :abilities [{:label "Gain strength per barrier subroutine" - :req (req (and (rezzed? current-ice) - (has-subtype? current-ice "Barrier"))) - :msg (msg "gain " (count-num-subroutines current-ice) " strength") - :effect (effect (pump card (count-num-subroutines current-ice)))} - (break-sub 2 2 "Barrier")]}) - -(define-card "Bishop" - {:abilities [{:cost [:click 1] - :effect (req (let [b (get-card state card) - hosted? (ice? (:host b)) - remote? (is-remote? (second (:zone (:host b))))] - (resolve-ability - state side - {:prompt (msg "Host Bishop on a piece of ICE protecting " - (if hosted? (if remote? "a central" "a remote") "any") " server") - :choices {:req #(if hosted? - (and (if remote? - (is-central? (second (:zone %))) - (is-remote? (second (:zone %)))) - (ice? %) - (can-host? %) - (= (last (:zone %)) :ices) - (not-any? (fn [c] (has-subtype? c "Caïssa")) - (:hosted %))) - (and (ice? %) - (can-host? %) - (= (last (:zone %)) :ices) - (not-any? (fn [c] (has-subtype? c "Caïssa")) - (:hosted %))))} + (installed? %))} + :effect (req (when (host state side card target) + (gain :memory (:memoryunits target))))} + gain-abis (req (let [new-abis (mapcat (fn [c] (map-indexed #(assoc %2 + :dynamic :copy + :source (:title c) + :index %1 + :label (make-label %2)) + (filter #(not= :manual-state (:ability-type %)) + (:abilities (card-def c))))) + (:hosted card))] + (update! state :runner (assoc card :abilities (concat new-abis [host-click host-free])))))] + {:abilities [host-click host-free] + :hosted-gained gain-abis + :hosted-lost gain-abis}) + + "Bankroll" + {:implementation "Bankroll gains credits automatically." + :events {:successful-run {:effect (effect (add-counter card :credit 1) + (system-msg "places 1 [Credit] on Bankroll"))}} + :abilities [{:label "[Trash]: Take all credits from Bankroll" + :async true + ;; Cannot trash unless there are counters (so game state changes) + :req (req (pos? (get-counters card :credit))) + :effect (req (let [credits-on-bankroll (get-counters card :credit)] + (wait-for (trash state :runner card {:cause :ability-cost}) + (gain-credits state :runner credits-on-bankroll) + (system-msg state :runner (str "trashes Bankroll and takes " + credits-on-bankroll " credits from it")))))}]} + + "Battering Ram" + (auto-icebreaker ["Barrier"] + {:abilities [(break-sub 2 2 "Barrier") + (strength-pump 1 1 :all-run)]}) + + "Berserker" + {:implementation "Encounter effect is manual. Only gains strength on printed subs" + :abilities [{:label "Gain strength per barrier subroutine" + :req (req (and (rezzed? current-ice) + (has-subtype? current-ice "Barrier"))) + :msg (msg "gain " (count-num-subroutines current-ice) " strength") + :effect (effect (pump card (count-num-subroutines current-ice)))} + (break-sub 2 2 "Barrier")]} + + "Bishop" + {:abilities [{:cost [:click 1] + :effect (req (let [b (get-card state card) + hosted? (ice? (:host b)) + remote? (is-remote? (second (:zone (:host b))))] + (resolve-ability + state side + {:prompt (msg "Host Bishop on a piece of ICE protecting " + (if hosted? (if remote? "a central" "a remote") "any") " server") + :choices {:req #(if hosted? + (and (if remote? + (is-central? (second (:zone %))) + (is-remote? (second (:zone %)))) + (ice? %) + (can-host? %) + (= (last (:zone %)) :ices) + (not-any? (fn [c] (has-subtype? c "Caïssa")) + (:hosted %))) + (and (ice? %) + (can-host? %) + (= (last (:zone %)) :ices) + (not-any? (fn [c] (has-subtype? c "Caïssa")) + (:hosted %))))} + :msg (msg "host it on " (card-str state target)) + :effect (effect (host target card))} card nil)))}] + :events {:pre-ice-strength + {:req (req (and (= (:cid target) + (:cid (:host card))) + (:rezzed target))) + :effect (effect (ice-strength-bonus -2 target))}}} + + "Black Orchestra" + (conspiracy "Black Orchestra" "Code Gate" + [{:cost [:credit 3] + :effect (effect (pump card 2)) + :pump 2 + :msg "add 2 strength and break up to 2 subroutines"}]) + + "BlacKat" + {:implementation "Stealth credit restriction not enforced" + :abilities [(break-sub 1 1 "Barrier") + {:cost [:credit 1] + :msg "break up to 3 Barrier subroutines (using a stealth [Credits])"} + (strength-pump 2 1) + {:cost [:credit 2] + :msg "add 2 strength (using at least 1 stealth [Credits])" + :effect (effect (pump card 2)) + :pump 2}]} + + "Blackstone" + {:abilities [(break-sub 1 1 "Barrier") + {:cost [:credit 3] + :msg "add 4 strength (using at least 1 stealth [Credits])" + :effect (effect (pump card 4 :all-run)) + :pump 4}]} + + "Brahman" + (auto-icebreaker ["All"] + {:implementation "Adding non-virus program to top of Stack is manual" + :abilities [(break-sub 1 2 "ICE") + (strength-pump 2 1)]}) + + "Breach" + (central-breaker "Barrier" + (break-sub 2 3 "Barrier") + (strength-pump 2 4)) + + "Bug" + {:implementation "Can only pay to see last card drawn after multiple draws" + :req (req (some #{:hq} (:successful-run runner-reg))) + :events {:corp-draw {:optional + {:prompt (msg "Pay 2 [Credits] to reveal card just drawn?") + :player :runner + :yes-ability {:msg (msg "reveal the card just drawn: " (:title (last (:hand corp)))) + :cost [:credit 2]}}}}} + + "Bukhgalter" + (auto-icebreaker ["Sentry"] + {:abilities [(break-sub 1 1 "Sentry") + (strength-pump 1 1) + {:once :per-turn + :label "Gain 2 [Credits]" + :req (req (and (has-subtype? current-ice "Sentry") + (rezzed? current-ice))) + :msg (msg "gain 2 [Credits]") + :effect (effect (gain-credits :runner 2))}]}) + + "Cache" + {:abilities [{:counter-cost [:virus 1] + :effect (effect (gain-credits 1)) + :msg "gain 1 [Credits]"}] + :data {:counter {:virus 3}}} + + "Cerberus \"Cuj.0\" H3" + (cerberus "Sentry") + + "Cerberus \"Lady\" H1" + (cerberus "Barrier") + + "Cerberus \"Rex\" H2" + (cerberus "Code Gate") + + "Chakana" + {:leave-play (effect (update-all-advancement-costs)) + :events {:successful-run {:silent (req true) + :req (req (= target :rd)) + :effect (effect (add-counter card :virus 1))} + :pre-advancement-cost {:req (req (>= (get-virus-counters state card) 3)) + :effect (effect (advancement-cost-bonus 1))} + :counter-added + {:req (req (or (= (:title target) "Hivemind") (same-card? target card))) + :effect (effect (update-all-advancement-costs))} + :purge {:effect (effect (update-all-advancement-costs))}}} + + "Chameleon" + {:prompt "Choose one subtype" + :choices ["Barrier" "Code Gate" "Sentry"] + :msg (msg "choose " target) + :effect (effect (update! (assoc card :subtype-target target))) + :events {:runner-turn-ends {:msg "add itself to Grip" :effect (effect (move card :hand))}} + :abilities [{:cost [:credit 1] :msg (msg "break 1 " (:subtype-target card) " subroutine")}]} + + "Chisel" + {:implementation "Encounter effect is manual." + :hosting {:req (every-pred ice? can-host?)} + :abilities [{:req (req (and (same-card? current-ice (:host card)) + (rezzed? current-ice))) + :effect (req (if (zero? (get-strength current-ice)) + (do (system-msg state side (str "uses Chisel to trash " (card-str state current-ice))) + (trash state side current-ice)) + (do (system-msg state side (str "places 1 virus counter on " (card-str state current-ice))) + (add-counter state side card :virus 1))))}] + :events {:counter-added {:req (req (or (same-card? target card) + (= (:title target) "Hivemind"))) + :effect (effect (update-ice-strength (:host card)))} + :pre-ice-strength {:req (req (same-card? target (:host card))) + :effect (effect (ice-strength-bonus (- (get-virus-counters state card)) target))}}} + + "Cloak" + {:recurring 1 + :interactions {:pay-credits {:req (req (and (= :ability (:source-type eid)) + (has-subtype? target "Icebreaker"))) + :type :recurring}}} + + "Clot" + {:effect (req (let [agendas (map first (filter #(agenda? (first %)) + (turn-events state :corp :corp-install)))] + (swap! state assoc-in [:corp :register :cannot-score] agendas))) + :events {:purge {:effect (req (swap! state update-in [:corp :register] dissoc :cannot-score) + (trash state side card {:cause :purge}))} + :corp-install {:req (req (agenda? target)) + :effect (req (swap! state update-in [:corp :register :cannot-score] #(cons target %)))}} + :leave-play (req (swap! state update-in [:corp :register] dissoc :cannot-score))} + + "Collective Consciousness" + {:events {:rez {:req (req (ice? target)) + :msg "draw 1 card" + :async true + :effect (effect (draw :runner eid 1 nil))}}} + + "Consume" + {:events {:runner-trash {:async true + :req (req (some corp? targets)) + :effect (req (let [amt-trashed (count (filter corp? targets)) + sing-ab {:optional {:prompt "Place a virus counter on Consume?" + :autoresolve (get-autoresolve :auto-accept) + :yes-ability {:effect (effect (add-counter :runner card :virus 1)) + :msg "place 1 virus counter on Consume"}}} + mult-ab {:prompt "Place virus counters on Consume?" + :choices {:number (req amt-trashed) + :default (req amt-trashed)} + :msg (msg "place " (quantify target "virus counter") " on Consume") + :effect (effect (add-counter :runner card :virus target))} + ab (if (= 1 amt-trashed) sing-ab mult-ab)] + (continue-ability state side ab card targets)))}} + :abilities [{:req (req (pos? (get-virus-counters state card))) + :cost [:click 1] + :label "Gain 2 [Credits] for each hosted virus counter, then remove all virus counters." + :effect (req (gain-credits state side (* 2 (get-virus-counters state card))) + (update! state side (assoc-in card [:counter :virus] 0)) + (when-let [hiveminds (filter #(= "Hivemind" (:title %)) (all-active-installed state :runner))] + (doseq [h hiveminds] + (update! state side (assoc-in h [:counter :virus] 0))))) + :msg (msg (let [local-virus (get-counters card :virus) + global-virus (get-virus-counters state card) + hivemind-virus (- global-virus local-virus)] + (str "gain " (* 2 global-virus) " [Credits], removing " (quantify local-virus "virus counter") " from Consume" + (when (pos? hivemind-virus) + (str " (and " hivemind-virus " from Hivemind)")))))} + (set-autoresolve :auto-accept "adding virus counters")]} + + "Copycat" + {:abilities [{:req (req (and (:run @state) + (:rezzed current-ice))) + :effect (req (let [icename (:title current-ice)] + (resolve-ability + state side + {:prompt (msg "Choose a rezzed copy of " icename) + :choices {:req #(and (rezzed? %) + (ice? %) + (= (:title %) icename))} + :msg "redirect the run" + :effect (req (let [dest (second (:zone target)) + tgtndx (ice-index state target)] + (swap! state update-in [:run] + #(assoc % :position tgtndx :server [dest])) + (trash state side card {:cause :ability-cost})))} + card nil)))}]} + + "Corroder" + (auto-icebreaker ["Barrier"] + {:abilities [(break-sub 1 1 "Barrier") + (strength-pump 1 1)]}) + + "Cradle" + {:abilities [(break-sub 2 0 "Code Gate")] + :events {:card-moved {:silent (req true) + :req (req (and (= "Runner" (:side target)) + (= [:hand] (or (:zone target) + (:previous-zone target))))) + :effect (effect (update-breaker-strength card))} + :runner-draw {:silent (req true) + :req (req (when-let [drawn (-> @state :runner :register :most-recent-drawn first)] + (= [:hand] (or (:zone drawn) + (:previous-zone drawn))))) + :effect (effect (update-breaker-strength card))} } + :strength-bonus (req (-> @state :runner :hand count -))} + + "Creeper" + (cloud-icebreaker + (auto-icebreaker ["Sentry"] + {:abilities [(break-sub 2 1 "Sentry") + (strength-pump 1 1)]})) + + "Crescentus" + {:implementation "Does not check that all subroutines were broken" + :abilities [{:req (req (rezzed? current-ice)) + :msg (msg "derez " (:title current-ice)) + :effect (effect (trash card {:cause :ability-cost}) (derez current-ice))}]} + + "Crowbar" + (break-and-enter "Code Gate") + + "Crypsis" + (auto-icebreaker ["All"] + {:abilities [(break-sub 1 1 "ICE" (effect (update! (assoc card :crypsis-broke true)))) + (strength-pump 1 1) + {:cost [:click 1] + :msg "place 1 virus counter" + :effect (effect (add-counter card :virus 1))}] + :events (let [encounter-ends-effect + {:req (req (:crypsis-broke card)) + :effect (req ((:effect breaker-auto-pump) state side eid card targets) + (if (pos? (get-counters card :virus)) + (add-counter state side card :virus -1) + (trash state side card {:cause :self-trash})) + (update! state side (dissoc (get-card state card) :crypsis-broke)))}] + {:pass-ice encounter-ends-effect + :run-ends encounter-ends-effect}) + :move-zone (req (when (= [:discard] (:zone card)) + (update! state side (dissoc card :crypsis-broke))))}) + + "Customized Secretary" + (letfn [(custsec-host [cards] + {:prompt "Choose a program to host on Customized Secretary" + :choices (cons "None" cards) + :async true + :effect (req (if (or (= target "None") (not (program? target))) + (do (clear-wait-prompt state :corp) + (shuffle! state side :deck) + (system-msg state side (str "shuffles their Stack")) + (effect-completed state side eid)) + (do (host state side (get-card state card) target) + (system-msg state side (str "hosts " (:title target) " on Customized Secretary")) + (continue-ability state side (custsec-host (remove-once #(= % target) cards)) + card nil))))})] + {:async true + :interactive (req (some #(card-flag? % :runner-install-draw true) (all-active state :runner))) + :msg (msg "reveal the top 5 cards of their Stack: " (join ", " (map :title (take 5 (:deck runner))))) + :effect (req (reveal state side (take 5 (:deck runner))) + (show-wait-prompt state :corp "Runner to host programs on Customized Secretary") + (let [from (take 5 (:deck runner))] + (continue-ability state side (custsec-host from) card nil))) + :abilities [{:cost [:click 1] + :prompt "Choose a program hosted on Customized Secretary to install" + :choices (req (cancellable (filter #(can-pay? state side eid card nil :credit (:cost %)) + (:hosted card)))) + :msg (msg "install " (:title target)) + :effect (req (when (can-pay? state side eid card nil :credit (:cost target)) + (runner-install state side target)))}]}) + + "Cyber-Cypher" + (auto-icebreaker ["Code Gate"] + {:prompt "Choose a server where this copy of Cyber-Cypher can be used:" + :msg (msg "target " target) + :choices (req servers) + :effect (effect (update! (assoc card :server-target target))) + :leave-play (effect (update! (dissoc card :server-target))) + :abilities [(break-sub 1 1 "Code Gate") + (strength-pump 1 1)]}) + + "D4v1d" + {:implementation "Does not check that ICE strength is 5 or greater" + :data {:counter {:power 3}} + :abilities [{:counter-cost [:power 1] + :msg "break 1 subroutine"}]} + + "Dagger" + (auto-icebreaker ["Sentry"] + {:implementation "Stealth credit restriction not enforced" + :abilities [(break-sub 1 1 "Sentry") + (strength-pump 1 5)]}) + + "Dai V" + (auto-icebreaker ["All"] + {:implementation "Stealth credit restriction not enforced" + :abilities [{:cost [:credit 2] + :msg "break all ICE subroutines (using stealth [Credits])"} + (strength-pump 1 1)]}) + + "Darwin" + {:flags {:runner-phase-12 (req true)} + :events {:purge {:effect (effect (update-breaker-strength card))}} + :abilities [(break-sub 2 1 "ICE") + {:label "Place 1 virus counter (start of turn)" + :once :per-turn + :cost [:credit 1] + :msg "place 1 virus counter" + :req (req (:runner-phase-12 @state)) + :effect (effect (add-counter card :virus 1) + (update-breaker-strength card))}] + :strength-bonus (req (or (get-virus-counters state card) 0))} + + "Datasucker" + {:events (let [ds {:effect (req (update! state side (dissoc card :datasucker-count)))}] + {:successful-run {:silent (req true) + :effect (effect (add-counter card :virus 1)) + :req (req (#{:hq :rd :archives} target))} + :pre-ice-strength {:req (req (and (same-card? target current-ice) + (:datasucker-count card))) + :effect (req (let [c (:datasucker-count (get-card state card))] + (ice-strength-bonus state side (- c) target)))} + :pass-ice ds :run-ends ds}) + :abilities [{:counter-cost [:virus 1] + :msg (msg "give -1 strength to " (:title current-ice)) + :req (req (and current-ice (:rezzed current-ice))) + :effect (req (update! state side (update-in card [:datasucker-count] (fnil #(+ % 1) 0))) + (update-ice-strength state side current-ice))}]} + + "DaVinci" + {:events {:successful-run {:silent (req true) + :effect (effect (add-counter card :power 1))}} + :abilities [{:effect + (req (let [c card] + (resolve-ability state side + {:prompt "Choose a card to install from your Grip" + :choices {:req #(and (<= (:cost %) (get-counters c :power)) + (#{"Hardware" "Program" "Resource"} (:type %)) + (in-hand? %))} + :req (req (not (install-locked? state side))) + :msg (msg "install " (:title target) " at no cost") + :effect (effect (trash card {:cause :ability-cost}) + (runner-install target {:ignore-install-cost true}))} + card nil)))}]} + + "Deep Thought" + {:events {:successful-run {:silent (req true) + :effect (effect (add-counter card :virus 1)) + :req (req (= target :rd))} + :runner-turn-begins + {:req (req (>= (get-virus-counters state card) 3)) :msg "look at the top card of R&D" + :effect (effect (prompt! card (str "The top card of R&D is " + (:title (first (:deck corp)))) ["OK"] {}))}}} + + "Demara" + (auto-icebreaker ["Barrier"] + {:abilities [(break-sub 2 2 "Barrier") + (strength-pump 2 3) + {:label "Bypass Barrier being encountered" + :req (req (has-subtype? current-ice "Barrier")) + :msg (msg "trash it and bypass " (:title current-ice)) + :effect (effect (trash card {:cause :ability-cost}))}]}) + + "Deus X" + {:interactions {:prevent [{:type #{:net} + :req (req true)}]} + :abilities [{:msg "break any number of AP subroutines" + :effect (effect (trash card {:cause :ability-cost}))} + {:msg "prevent any amount of net damage" + :effect (effect (trash card {:cause :ability-cost}) + (damage-prevent :net Integer/MAX_VALUE))}]} + + "Dhegdheer" + {:abilities [{:label "Install a program on Dhegdheer" + :req (req (nil? (get-in card [:special :dheg-prog]))) + :effect (effect (resolve-ability + {:cost [:click 1] + :prompt "Choose a program in your Grip to install on Dhegdheer" + :choices {:req #(and (program? %) + (runner-can-install? state side % false) + (in-hand? %))} + :msg (msg (str "host " (:title target) + (when (-> target :cost pos?) + ", lowering its cost by 1 [Credit]"))) + :effect (effect (when (-> target :cost pos?) + (install-cost-bonus state side [:credit -1])) + (runner-install target {:host-card card :no-mu true}) + (update! (assoc-in (get-card state card) [:special :dheg-prog] (:cid target))))} + card nil))} + {:label "Host an installed program on Dhegdheer with [Credit] discount" + :req (req (nil? (get-in card [:special :dheg-prog]))) + :prompt "Choose an installed program to host on Dhegdheer with [Credit] discount" + :choices {:req #(and (program? %) + (installed? %))} + :msg (msg (str "host " (:title target) + (when (-> target :cost pos?) + ", lowering its cost by 1 [Credit]"))) + :effect (req (free-mu state (:memoryunits target)) + (when (-> target :cost pos?) + (gain-credits state side 1)) + (update-breaker-strength state side target) + (host state side card (get-card state target)) + (update! state side (assoc-in (get-card state card) [:special :dheg-prog] (:cid target))))} + {:label "Host an installed program on Dhegdheer" + :req (req (nil? (get-in card [:special :dheg-prog]))) + :prompt "Choose an installed program to host on Dhegdheer" + :choices {:req #(and (program? %) + (installed? %))} + :msg (msg (str "host " (:title target) + (when (-> target :cost pos?) + ", lowering its cost by 1 [Credit]"))) + :effect (effect (free-mu (:memoryunits target)) + (update-breaker-strength target) + (host card (get-card state target)) + (update! (assoc-in (get-card state card) [:special :dheg-prog] (:cid target))))}] + :events {:card-moved {:req (req (= (:cid target) (get-in (get-card state card) [:special :dheg-prog]))) + :effect (effect (update! (dissoc-in card [:special :dheg-prog])) + (use-mu (:memoryunits target)))}}} + + "Disrupter" + {:events + {:pre-init-trace + {:async true + :effect (effect (show-wait-prompt :corp "Runner to use Disrupter") + (continue-ability + :runner + {:optional + {:prompt "Use Disrupter's ability?" + :yes-ability + {:effect (req (trash state side card {:cause :ability-cost}) + (swap! state assoc-in [:trace :force-base] 0))} + :end-effect (effect (clear-wait-prompt :corp))}} + card nil))}}} + + "Diwan" + {:prompt "Choose the server that this copy of Diwan is targeting:" + :choices (req servers) + :effect (effect (update! (assoc card :server-target target))) + :events {:purge {:effect (effect (trash card {:cause :purge}))} + :pre-corp-install + {:req (req (let [c target + serv (:server (second targets))] + (and (= serv (:server-target card)) + (not (and (is-central? serv) + (upgrade? c)))))) + :effect (effect (install-cost-bonus [:credit 1]))}}} + + "Djinn" + {:abilities [{:label "Search your Stack for a virus program and add it to your Grip" + :prompt "Choose a Virus" + :msg (msg "add " (:title target) " to their Grip") + :choices (req (cancellable (filter #(and (program? %) + (has-subtype? % "Virus")) + (:deck runner)) :sorted)) + :cost [:click 1 :credit 1] + :effect (effect (trigger-event :searched-stack nil) + (shuffle! :deck) + (move target :hand))} + {:label "Install a non-Icebreaker program on Djinn" + :effect (effect (resolve-ability + {:cost [:click 1] + :prompt "Choose a non-Icebreaker program in your Grip to install on Djinn" + :choices {:req #(and (program? %) + (runner-can-install? state side % false) + (not (has-subtype? % "Icebreaker")) + (in-hand? %))} + :msg (msg "install and host " (:title target)) + :effect (effect (runner-install target {:host-card card :no-mu true}) + (update! (assoc (get-card state card) + :hosted-programs + (cons (:cid target) (:hosted-programs card)))))} + card nil))} + {:label "Host an installed non-Icebreaker program on Djinn" + :prompt "Choose an installed non-Icebreaker program to host on Djinn" + :choices {:req #(and (program? %) + (not (has-subtype? % "Icebreaker")) + (installed? %))} + :msg (msg "host " (:title target)) + :effect (effect (host card target) + (free-mu (:memoryunits target)) + (update! (assoc (get-card state card) + :hosted-programs (cons (:cid target) (:hosted-programs card)))))}] + :events {:card-moved {:req (req (some #{(:cid target)} (:hosted-programs card))) + :effect (effect (update! (assoc card + :hosted-programs (remove #(= (:cid target) %) (:hosted-programs card)))) + (use-mu (:memoryunits target)))}}} + + "Eater" + (auto-icebreaker ["All"] + {:abilities [{:cost [:credit 1] + :msg "break ICE subroutine and access 0 cards this run" + :effect (effect (max-access 0))} + (strength-pump 1 1)]}) + + "Egret" + {:implementation "Added subtypes don't get removed when Egret is moved/trashed" + :hosting {:req #(and (ice? %) (can-host? %) (rezzed? %))} + :msg (msg "make " (card-str state (:host card)) " gain Barrier, Code Gate and Sentry subtypes") + :effect (req (when-let [h (:host card)] + (update! state side (assoc-in card [:special :installing] true)) + (update-ice-strength state side h) + (when-let [card (get-card state card)] + (update! state side (update-in card [:special] dissoc :installing))))) + :events {:ice-strength-changed + {:effect (req (unregister-events state side card) + (when (get-in card [:special :installing]) + (update! state side (assoc (:host (get-card state card)) :subtype (combine-subtypes false(-> card :host :subtype) "Barrier" "Code Gate" "Sentry"))) + (update! state side (update-in card [:special] dissoc :installing)) + (trigger-event state side :runner-install card)) + (continue state side nil))}}} + + "Endless Hunger" + {:abilities [{:label "Trash 1 installed card to break 1 \"End the run.\" subroutine" + :prompt "Select a card to trash for Endless Hunger" + :choices {:req #(and (runner? %) (installed? %))} + :msg (msg "trash " (:title target) + " and break 1 \"[Subroutine] End the run.\" subroutine") + :effect (effect (trash target {:unpreventable true}))}]} + + "Engolo" + (auto-icebreaker + ["Code Gate"] + {:abilities [(break-sub 1 1 "Code Gate") + (strength-pump 2 4) + (wrestling-breaker 2 "Code Gate")]}) + + "Equivocation" + (let [force-draw (fn [title] + {:optional {:prompt (str "Force the Corp to draw " title "?") + :yes-ability {:async true + :effect (req (show-wait-prompt state :runner "Corp to draw") + (wait-for (draw state :corp 1 nil) + (do (system-msg state :corp (str "is forced to draw " title)) + (clear-wait-prompt state :runner) + (effect-completed state side eid))))}}}) + reveal {:optional {:prompt "Reveal the top card of R&D?" + :yes-ability {:async true + :effect (req (let [topcard (-> corp :deck first :title)] + (reveal state side topcard) + (system-msg state :runner (str "reveals " topcard + " from the top of R&D")) + (continue-ability state side (force-draw topcard) card nil)))}}}] + {:events {:successful-run {:req (req (= target :rd)) + :async true + :interactive (req true) + :effect (effect (continue-ability reveal card nil))}}}) + + "eXer" + {:in-play [:rd-access 1] + :events {:purge {:effect (effect (trash card {:cause :purge}))}}} + + "Expert Schedule Analyzer" + {:abilities [{:cost [:click 1] + :msg "make a run on HQ" + :makes-run true + :effect (effect (make-run :hq {:req (req (= target :hq)) + :replace-access + {:msg (msg "reveal cards in HQ: " + (join ", " (map :title (:hand corp))))}} card))}]} + + "Faerie" + (auto-icebreaker ["Sentry"] + {:abilities [(break-sub 0 1 "Sentry" (effect (update! (assoc-in card [:special :faerie-used] true)))) + (strength-pump 1 1)] + :events {:pass-ice {:req (req (get-in card [:special :faerie-used])) + :effect (effect (trash card))}}}) + + "False Echo" + {:abilities [{:req (req (and run + (< (:position run) (count run-ices)) + (not (rezzed? current-ice)))) + :msg "make the Corp rez the passed ICE or add it to HQ" + :effect (req (let [s (:server run) + ice (nth (get-in @state (vec (concat [:corp :servers] s [:ices]))) (:position run)) + icename (:title ice) + icecost (rez-cost state side ice)] + (continue-ability + state side + {:prompt (msg "Rez " icename " or add it to HQ?") :player :corp + :choices (req (if (< (:credit corp) icecost) + ["Add to HQ"] + ["Rez" "Add to HQ"])) + :effect (req (if (= target "Rez") + (rez state side ice) + (do (move state :corp ice :hand nil) + (system-msg state :corp (str "chooses to add the passed ICE to HQ")))) + (trash state side card))} + card nil)))}]} + + "Faust" + {:abilities [{:label "Trash 1 card from Grip to break 1 subroutine" + :cost [:trash-from-hand 1] + :msg (msg "break 1 subroutine")} + {:label "Trash 1 card from Grip to add 2 strength" + :cost [:trash-from-hand 1] + :msg (msg "add 2 strength") + :effect (effect (pump card 2))}]} + + "Fawkes" + {:implementation "Stealth credit restriction not enforced" + :abilities [(break-sub 1 1 "Sentry") + {:label "X [Credits]: +X strength for the remainder of the run (using at least 1 stealth [Credits])" + :choices :credit + :prompt "How many credits?" + :effect (effect (pump card target :all-run)) + :msg (msg "increase strength by " target " for the remainder of the run")}]} + + "Femme Fatale" + (auto-icebreaker ["Sentry"] + {:prompt "Select a piece of ICE to target for bypassing" + :choices {:req ice?} + :leave-play (req (remove-icon state side card)) + :effect (req (let [ice target] + (add-icon state side card ice "F" "blue") + (system-msg state side + (str "selects " (card-str state ice) + " for Femme Fatale's bypass ability")))) + :abilities [(break-sub 1 1 "Sentry") + (strength-pump 2 1)]}) + + "Flashbang" + (auto-icebreaker ["Sentry"] + {:abilities [(strength-pump 1 1) + {:label "Derez a Sentry being encountered" + :cost [:credit 6] + :req (req (and (rezzed? current-ice) (has-subtype? current-ice "Sentry"))) + :msg (msg "derez " (:title current-ice)) + :effect (effect (derez current-ice))}]}) + + "Force of Nature" + (auto-icebreaker ["Code Gate"] + {:abilities [(break-sub 2 2 "Code Gate") + (strength-pump 1 1)]}) + + "Garrote" + (auto-icebreaker ["Sentry"] + {:abilities [(break-sub 1 1 "Sentry") + (strength-pump 1 1)]}) + + "Gauss" + (auto-icebreaker ["Barrier"] + {:strength-bonus (req (if (= :this-turn (:installed card)) 3 0)) + :events (let [losestr {:effect (effect (update-breaker-strength card))}] + {:runner-turn-ends losestr + :corp-turn-ends losestr}) + :abilities [(break-sub 1 1 "Barrier") + (strength-pump 2 2)]}) + + "Gingerbread" + (auto-icebreaker ["Tracer"] + {:abilities [(break-sub 1 1 "Tracer") + (strength-pump 2 3)]}) + + "God of War" + (auto-icebreaker ["All"] + {:flags {:runner-phase-12 (req true)} + :abilities [(strength-pump 2 1) + {:counter-cost [:virus 1] + :msg "break 1 subroutine"} + {:label "Take 1 tag to place 2 virus counters (start of turn)" + :once :per-turn + :effect (req (wait-for (gain-tags state :runner 1) + (if (not (get-in @state [:tag :tag-prevent])) + (do (add-counter state side card :virus 2) + (system-msg state side + (str "takes 1 tag to place 2 virus counters on God of War")) + (effect-completed state side eid)) + (effect-completed state side eid))))}]}) + + "Golden" + (auto-icebreaker ["Sentry"] + {:abilities [(break-sub 2 2 "Sentry") + (strength-pump 2 4) + {:label "Derez a Sentry and return Golden to your Grip" + :cost [:credit 2] + :req (req (and (rezzed? current-ice) (has-subtype? current-ice "Sentry"))) + :msg (msg "derez " (:title current-ice) " and return Golden to their Grip") + :effect (effect (derez current-ice) + (move card :hand))}]}) + + "Gordian Blade" + (auto-icebreaker ["Code Gate"] + {:abilities [(break-sub 1 1 "Code Gate") + (strength-pump 1 1 :all-run)]}) + + "Gorman Drip v1" + {:abilities [{:cost [:click 1] :effect (effect (gain-credits (get-virus-counters state card)) + (trash card {:cause :ability-cost})) + :msg (msg "gain " (get-virus-counters state card) " [Credits]")}] + :events {:corp-click-credit {:effect (effect (add-counter :runner card :virus 1))} + :corp-click-draw {:effect (effect (add-counter :runner card :virus 1))}}} + + "Grappling Hook" + {:abilities [{:msg "break all but 1 subroutine" :effect (effect (trash card {:cause :ability-cost}))}]} + + "Gravedigger" + {:events (let [e {:req (req (and (installed? target) (= (:side target) "Corp"))) + :effect (effect (add-counter :runner card :virus 1))}] + {:runner-trash e :corp-trash e}) + :abilities [{:counter-cost [:virus 1] + :cost [:click 1] + :msg "force the Corp to trash the top card of R&D" + :effect (effect (mill :corp))}]} + + "GS Sherman M3" + (global-sec-breaker "Barrier") + + "GS Shrike M2" + (global-sec-breaker "Sentry") + + "GS Striker M1" + (global-sec-breaker "Code Gate") + + "Harbinger" + {:trash-effect + {:req (req (not-any? #{:facedown :hand} (:previous-zone card))) + :effect (req (let [lock (get-in @state [:runner :locked :discard])] + (swap! state assoc-in [:runner :locked] nil) + (runner-install state :runner card {:facedown true}) + (swap! state assoc-in [:runner :locked] lock)))}} + + "Hemorrhage" + {:events {:successful-run {:silent (req true) + :effect (effect (add-counter card :virus 1))}} + :abilities [{:counter-cost [:virus 2] + :cost [:click 1] + :req (req (pos? (count (:hand corp)))) + :msg "force the Corp to trash 1 card from HQ" + :effect (req (show-wait-prompt state :runner "Corp to trash a card from HQ") + (resolve-ability + state :corp + {:prompt "Choose a card to trash" + :choices (req (filter corp? (:hand corp))) + :effect (effect (trash target) + (clear-wait-prompt :runner))} + card nil))}]} + + "Hivemind" + (let [update-programs (req (let [virus-programs (->> (all-installed state :runner) + (filter #(and (program? %) + (has-subtype? % "Virus") + (not (facedown? %)))))] + (doseq [p virus-programs] + (update-breaker-strength state side p))))] + {:data {:counter {:virus 1}} + :effect update-programs + :trash-effect {:effect update-programs} + :events {:counter-added {:req (req (same-card? target card)) + :effect update-programs}} + :abilities [{:req (req (pos? (get-counters card :virus))) + :priority true + :prompt "Move a virus counter to which card?" + :choices {:req #(has-subtype? % "Virus")} + :effect (req (let [abilities (:abilities (card-def target)) + virus target] + (add-counter state :runner virus :virus 1) + (add-counter state :runner card :virus -1) + (if (= (count abilities) 1) + (do (swap! state update-in [side :prompt] rest) ; remove the Hivemind prompt so Imp works + (resolve-ability state side (first abilities) (get-card state virus) nil)) + (resolve-ability + state side + {:prompt "Choose an ability to trigger" + :choices (vec (map :msg abilities)) + :effect (req (swap! state update-in [side :prompt] rest) + (resolve-ability + state side + (first (filter #(= (:msg %) target) abilities)) + card nil))} + (get-card state virus) nil)))) + :msg (msg "trigger an ability on " (:title target))}]}) + + "Houdini" + {:abilities [(break-sub 1 1 "Code Gate") + {:cost [:credit 2] + :msg "add 4 strength (using at least 1 stealth [Credits])" + :effect (effect (pump card 4 :all-run)) :pump 4}]} + + "Hyperdriver" + {:flags {:runner-phase-12 (req true)} + :abilities [{:label "Remove Hyperdriver from the game to gain [Click] [Click] [Click]" + :req (req (:runner-phase-12 @state)) + :effect (effect (move card :rfg) (gain :click 3)) + :msg "gain [Click][Click][Click]"}]} + + "Ika" + (auto-icebreaker ["Sentry"] + {:abilities [(break-sub 1 2 "Sentry") + (strength-pump 2 3) + {:label "Host Ika on a piece of ICE" + :prompt (msg "Host Ika on a piece of ICE") + :cost [:credit 2] + :choices {:req #(and (ice? %) + (installed? %) + (can-host? %))} :msg (msg "host it on " (card-str state target)) - :effect (effect (host target card))} card nil)))}] - :events {:pre-ice-strength - {:req (req (and (= (:cid target) - (:cid (:host card))) - (:rezzed target))) - :effect (effect (ice-strength-bonus -2 target))}}}) - -(define-card "Black Orchestra" - (conspiracy "Black Orchestra" "Code Gate" - [{:cost [:credit 3] - :effect (effect (pump card 2)) - :pump 2 - :msg "add 2 strength and break up to 2 subroutines"}])) - -(define-card "BlacKat" - {:implementation "Stealth credit restriction not enforced" - :abilities [(break-sub 1 1 "Barrier") - {:cost [:credit 1] - :msg "break up to 3 Barrier subroutines (using a stealth [Credits])"} - (strength-pump 2 1) - {:cost [:credit 2] - :msg "add 2 strength (using at least 1 stealth [Credits])" - :effect (effect (pump card 2)) - :pump 2}]}) - -(define-card "Blackstone" - {:abilities [(break-sub 1 1 "Barrier") - {:cost [:credit 3] - :msg "add 4 strength (using at least 1 stealth [Credits])" - :effect (effect (pump card 4 :all-run)) - :pump 4}]}) - -(define-card "Brahman" - (auto-icebreaker ["All"] - {:implementation "Adding non-virus program to top of Stack is manual" - :abilities [(break-sub 1 2 "ICE") - (strength-pump 2 1)]})) - -(define-card "Breach" - (central-breaker "Barrier" - (break-sub 2 3 "Barrier") - (strength-pump 2 4))) - -(define-card "Bug" - {:implementation "Can only pay to see last card drawn after multiple draws" - :req (req (some #{:hq} (:successful-run runner-reg))) - :events {:corp-draw {:optional - {:prompt (msg "Pay 2 [Credits] to reveal card just drawn?") - :player :runner - :yes-ability {:msg (msg "reveal the card just drawn: " (:title (last (:hand corp)))) - :cost [:credit 2]}}}}}) - -(define-card "Bukhgalter" - (auto-icebreaker ["Sentry"] - {:abilities [(break-sub 1 1 "Sentry") - (strength-pump 1 1) - {:once :per-turn - :label "Gain 2 [Credits]" - :req (req (and (has-subtype? current-ice "Sentry") - (rezzed? current-ice))) - :msg (msg "gain 2 [Credits]") - :effect (effect (gain-credits :runner 2))}]})) - -(define-card "Cache" - {:abilities [{:counter-cost [:virus 1] - :effect (effect (gain-credits 1)) - :msg "gain 1 [Credits]"}] - :data {:counter {:virus 3}}}) - -(define-card "Cerberus \"Cuj.0\" H3" - (cerberus "Sentry")) - -(define-card "Cerberus \"Lady\" H1" - (cerberus "Barrier")) - -(define-card "Cerberus \"Rex\" H2" - (cerberus "Code Gate")) - -(define-card "Chakana" - {:leave-play (effect (update-all-advancement-costs)) - :events {:successful-run {:silent (req true) - :req (req (= target :rd)) - :effect (effect (add-counter card :virus 1))} - :pre-advancement-cost {:req (req (>= (get-virus-counters state card) 3)) - :effect (effect (advancement-cost-bonus 1))} - :counter-added - {:req (req (or (= (:title target) "Hivemind") (same-card? target card))) - :effect (effect (update-all-advancement-costs))} - :purge {:effect (effect (update-all-advancement-costs))}}}) - -(define-card "Chameleon" - {:prompt "Choose one subtype" - :choices ["Barrier" "Code Gate" "Sentry"] - :msg (msg "choose " target) - :effect (effect (update! (assoc card :subtype-target target))) - :events {:runner-turn-ends {:msg "add itself to Grip" :effect (effect (move card :hand))}} - :abilities [{:cost [:credit 1] :msg (msg "break 1 " (:subtype-target card) " subroutine")}]}) - -(define-card "Chisel" - {:implementation "Encounter effect is manual." - :hosting {:req (every-pred ice? can-host?)} - :abilities [{:req (req (and (same-card? current-ice (:host card)) - (rezzed? current-ice))) - :effect (req (if (zero? (get-strength current-ice)) - (do (system-msg state side (str "uses Chisel to trash " (card-str state current-ice))) - (trash state side current-ice)) - (do (system-msg state side (str "places 1 virus counter on " (card-str state current-ice))) - (add-counter state side card :virus 1))))}] - :events {:counter-added {:req (req (or (same-card? target card) - (= (:title target) "Hivemind"))) - :effect (effect (update-ice-strength (:host card)))} - :pre-ice-strength {:req (req (same-card? target (:host card))) - :effect (effect (ice-strength-bonus (- (get-virus-counters state card)) target))}}}) - -(define-card "Cloak" - {:recurring 1 - :interactions {:pay-credits {:req (req (and (= :ability (:source-type eid)) - (has-subtype? target "Icebreaker"))) - :type :recurring}}}) - -(define-card "Clot" - {:effect (req (let [agendas (map first (filter #(agenda? (first %)) - (turn-events state :corp :corp-install)))] - (swap! state assoc-in [:corp :register :cannot-score] agendas))) - :events {:purge {:effect (req (swap! state update-in [:corp :register] dissoc :cannot-score) - (trash state side card {:cause :purge}))} - :corp-install {:req (req (agenda? target)) - :effect (req (swap! state update-in [:corp :register :cannot-score] #(cons target %)))}} - :leave-play (req (swap! state update-in [:corp :register] dissoc :cannot-score))}) - -(define-card "Collective Consciousness" - {:events {:rez {:req (req (ice? target)) - :msg "draw 1 card" - :async true - :effect (effect (draw :runner eid 1 nil))}}}) - -(define-card "Consume" - {:events {:runner-trash {:async true - :req (req (some corp? targets)) - :effect (req (let [amt-trashed (count (filter corp? targets)) - sing-ab {:optional {:prompt "Place a virus counter on Consume?" - :autoresolve (get-autoresolve :auto-accept) - :yes-ability {:effect (effect (add-counter :runner card :virus 1)) - :msg "place 1 virus counter on Consume"}}} - mult-ab {:prompt "Place virus counters on Consume?" - :choices {:number (req amt-trashed) - :default (req amt-trashed)} - :msg (msg "place " (quantify target "virus counter") " on Consume") - :effect (effect (add-counter :runner card :virus target))} - ab (if (= 1 amt-trashed) sing-ab mult-ab)] - (continue-ability state side ab card targets)))}} - :abilities [{:req (req (pos? (get-virus-counters state card))) - :cost [:click 1] - :label "Gain 2 [Credits] for each hosted virus counter, then remove all virus counters." - :effect (req (gain-credits state side (* 2 (get-virus-counters state card))) - (update! state side (assoc-in card [:counter :virus] 0)) - (when-let [hiveminds (filter #(= "Hivemind" (:title %)) (all-active-installed state :runner))] - (doseq [h hiveminds] - (update! state side (assoc-in h [:counter :virus] 0))))) - :msg (msg (let [local-virus (get-counters card :virus) - global-virus (get-virus-counters state card) - hivemind-virus (- global-virus local-virus)] - (str "gain " (* 2 global-virus) " [Credits], removing " (quantify local-virus "virus counter") " from Consume" - (when (pos? hivemind-virus) - (str " (and " hivemind-virus " from Hivemind)")))))} - (set-autoresolve :auto-accept "adding virus counters")]}) - -(define-card "Copycat" - {:abilities [{:req (req (and (:run @state) - (:rezzed current-ice))) - :effect (req (let [icename (:title current-ice)] - (resolve-ability - state side - {:prompt (msg "Choose a rezzed copy of " icename) - :choices {:req #(and (rezzed? %) - (ice? %) - (= (:title %) icename))} - :msg "redirect the run" - :effect (req (let [dest (second (:zone target)) - tgtndx (ice-index state target)] - (swap! state update-in [:run] - #(assoc % :position tgtndx :server [dest])) - (trash state side card {:cause :ability-cost})))} - card nil)))}]}) - -(define-card "Corroder" - (auto-icebreaker ["Barrier"] - {:abilities [(break-sub 1 1 "Barrier") - (strength-pump 1 1)]})) - -(define-card "Cradle" - {:abilities [(break-sub 2 0 "Code Gate")] - :events {:card-moved {:silent (req true) - :req (req (and (= "Runner" (:side target)) - (= [:hand] (or (:zone target) - (:previous-zone target))))) - :effect (effect (update-breaker-strength card))} - :runner-draw {:silent (req true) - :req (req (when-let [drawn (-> @state :runner :register :most-recent-drawn first)] - (= [:hand] (or (:zone drawn) - (:previous-zone drawn))))) - :effect (effect (update-breaker-strength card))} } - :strength-bonus (req (-> @state :runner :hand count -))}) - -(define-card "Creeper" - (cloud-icebreaker - (auto-icebreaker ["Sentry"] - {:abilities [(break-sub 2 1 "Sentry") - (strength-pump 1 1)]}))) - -(define-card "Crescentus" - {:implementation "Does not check that all subroutines were broken" - :abilities [{:req (req (rezzed? current-ice)) - :msg (msg "derez " (:title current-ice)) - :effect (effect (trash card {:cause :ability-cost}) (derez current-ice))}]}) - -(define-card "Crowbar" - (break-and-enter "Code Gate")) - -(define-card "Crypsis" - (auto-icebreaker ["All"] - {:abilities [(break-sub 1 1 "ICE" (effect (update! (assoc card :crypsis-broke true)))) - (strength-pump 1 1) - {:cost [:click 1] - :msg "place 1 virus counter" - :effect (effect (add-counter card :virus 1))}] - :events (let [encounter-ends-effect - {:req (req (:crypsis-broke card)) - :effect (req ((:effect breaker-auto-pump) state side eid card targets) - (if (pos? (get-counters card :virus)) - (add-counter state side card :virus -1) - (trash state side card {:cause :self-trash})) - (update! state side (dissoc (get-card state card) :crypsis-broke)))}] - {:pass-ice encounter-ends-effect - :run-ends encounter-ends-effect}) - :move-zone (req (when (= [:discard] (:zone card)) - (update! state side (dissoc card :crypsis-broke))))})) - -(define-card "Customized Secretary" - (letfn [(custsec-host [cards] - {:prompt "Choose a program to host on Customized Secretary" - :choices (cons "None" cards) - :async true - :effect (req (if (or (= target "None") (not (program? target))) - (do (clear-wait-prompt state :corp) - (shuffle! state side :deck) - (system-msg state side (str "shuffles their Stack")) - (effect-completed state side eid)) - (do (host state side (get-card state card) target) - (system-msg state side (str "hosts " (:title target) " on Customized Secretary")) - (continue-ability state side (custsec-host (remove-once #(= % target) cards)) - card nil))))})] - {:async true - :interactive (req (some #(card-flag? % :runner-install-draw true) (all-active state :runner))) - :msg (msg "reveal the top 5 cards of their Stack: " (join ", " (map :title (take 5 (:deck runner))))) - :effect (req (reveal state side (take 5 (:deck runner))) - (show-wait-prompt state :corp "Runner to host programs on Customized Secretary") - (let [from (take 5 (:deck runner))] - (continue-ability state side (custsec-host from) card nil))) - :abilities [{:cost [:click 1] - :prompt "Choose a program hosted on Customized Secretary to install" - :choices (req (cancellable (filter #(can-pay? state side eid card nil :credit (:cost %)) - (:hosted card)))) - :msg (msg "install " (:title target)) - :effect (req (when (can-pay? state side eid card nil :credit (:cost target)) - (runner-install state side target)))}]})) - -(define-card "Cyber-Cypher" - (auto-icebreaker ["Code Gate"] - {:prompt "Choose a server where this copy of Cyber-Cypher can be used:" - :msg (msg "target " target) - :choices (req servers) - :effect (effect (update! (assoc card :server-target target))) - :leave-play (effect (update! (dissoc card :server-target))) - :abilities [(break-sub 1 1 "Code Gate") - (strength-pump 1 1)]})) - -(define-card "D4v1d" - {:implementation "Does not check that ICE strength is 5 or greater" - :data {:counter {:power 3}} - :abilities [{:counter-cost [:power 1] - :msg "break 1 subroutine"}]}) - -(define-card "Dagger" - (auto-icebreaker ["Sentry"] - {:implementation "Stealth credit restriction not enforced" - :abilities [(break-sub 1 1 "Sentry") - (strength-pump 1 5)]})) - -(define-card "Dai V" - (auto-icebreaker ["All"] - {:implementation "Stealth credit restriction not enforced" - :abilities [{:cost [:credit 2] - :msg "break all ICE subroutines (using stealth [Credits])"} - (strength-pump 1 1)]})) - -(define-card "Darwin" - {:flags {:runner-phase-12 (req true)} - :events {:purge {:effect (effect (update-breaker-strength card))}} - :abilities [(break-sub 2 1 "ICE") - {:label "Place 1 virus counter (start of turn)" - :once :per-turn - :cost [:credit 1] - :msg "place 1 virus counter" - :req (req (:runner-phase-12 @state)) - :effect (effect (add-counter card :virus 1) - (update-breaker-strength card))}] - :strength-bonus (req (or (get-virus-counters state card) 0))}) - -(define-card "Datasucker" - {:events (let [ds {:effect (req (update! state side (dissoc card :datasucker-count)))}] - {:successful-run {:silent (req true) - :effect (effect (add-counter card :virus 1)) - :req (req (#{:hq :rd :archives} target))} - :pre-ice-strength {:req (req (and (same-card? target current-ice) - (:datasucker-count card))) - :effect (req (let [c (:datasucker-count (get-card state card))] - (ice-strength-bonus state side (- c) target)))} - :pass-ice ds :run-ends ds}) - :abilities [{:counter-cost [:virus 1] - :msg (msg "give -1 strength to " (:title current-ice)) - :req (req (and current-ice (:rezzed current-ice))) - :effect (req (update! state side (update-in card [:datasucker-count] (fnil #(+ % 1) 0))) - (update-ice-strength state side current-ice))}]}) - -(define-card "DaVinci" - {:events {:successful-run {:silent (req true) - :effect (effect (add-counter card :power 1))}} - :abilities [{:effect - (req (let [c card] - (resolve-ability state side - {:prompt "Choose a card to install from your Grip" - :choices {:req #(and (<= (:cost %) (get-counters c :power)) - (#{"Hardware" "Program" "Resource"} (:type %)) - (in-hand? %))} - :req (req (not (install-locked? state side))) - :msg (msg "install " (:title target) " at no cost") - :effect (effect (trash card {:cause :ability-cost}) - (runner-install target {:ignore-install-cost true}))} - card nil)))}]}) - -(define-card "Deep Thought" - {:events {:successful-run {:silent (req true) - :effect (effect (add-counter card :virus 1)) - :req (req (= target :rd))} - :runner-turn-begins - {:req (req (>= (get-virus-counters state card) 3)) :msg "look at the top card of R&D" - :effect (effect (prompt! card (str "The top card of R&D is " - (:title (first (:deck corp)))) ["OK"] {}))}}}) - -(define-card "Demara" - (auto-icebreaker ["Barrier"] - {:abilities [(break-sub 2 2 "Barrier") - (strength-pump 2 3) - {:label "Bypass Barrier being encountered" - :req (req (has-subtype? current-ice "Barrier")) - :msg (msg "trash it and bypass " (:title current-ice)) - :effect (effect (trash card {:cause :ability-cost}))}]})) - -(define-card "Deus X" - {:interactions {:prevent [{:type #{:net} - :req (req true)}]} - :abilities [{:msg "break any number of AP subroutines" - :effect (effect (trash card {:cause :ability-cost}))} - {:msg "prevent any amount of net damage" - :effect (effect (trash card {:cause :ability-cost}) - (damage-prevent :net Integer/MAX_VALUE))}]}) - -(define-card "Dhegdheer" - {:abilities [{:label "Install a program on Dhegdheer" - :req (req (nil? (get-in card [:special :dheg-prog]))) - :effect (effect (resolve-ability - {:cost [:click 1] - :prompt "Choose a program in your Grip to install on Dhegdheer" - :choices {:req #(and (program? %) - (runner-can-install? state side % false) - (in-hand? %))} - :msg (msg (str "host " (:title target) - (when (-> target :cost pos?) - ", lowering its cost by 1 [Credit]"))) - :effect (effect (when (-> target :cost pos?) - (install-cost-bonus state side [:credit -1])) - (runner-install target {:host-card card :no-mu true}) - (update! (assoc-in (get-card state card) [:special :dheg-prog] (:cid target))))} - card nil))} - {:label "Host an installed program on Dhegdheer with [Credit] discount" - :req (req (nil? (get-in card [:special :dheg-prog]))) - :prompt "Choose an installed program to host on Dhegdheer with [Credit] discount" - :choices {:req #(and (program? %) - (installed? %))} - :msg (msg (str "host " (:title target) - (when (-> target :cost pos?) - ", lowering its cost by 1 [Credit]"))) - :effect (req (free-mu state (:memoryunits target)) - (when (-> target :cost pos?) - (gain-credits state side 1)) - (update-breaker-strength state side target) - (host state side card (get-card state target)) - (update! state side (assoc-in (get-card state card) [:special :dheg-prog] (:cid target))))} - {:label "Host an installed program on Dhegdheer" - :req (req (nil? (get-in card [:special :dheg-prog]))) - :prompt "Choose an installed program to host on Dhegdheer" - :choices {:req #(and (program? %) - (installed? %))} - :msg (msg (str "host " (:title target) - (when (-> target :cost pos?) - ", lowering its cost by 1 [Credit]"))) - :effect (effect (free-mu (:memoryunits target)) - (update-breaker-strength target) - (host card (get-card state target)) - (update! (assoc-in (get-card state card) [:special :dheg-prog] (:cid target))))}] - :events {:card-moved {:req (req (= (:cid target) (get-in (get-card state card) [:special :dheg-prog]))) - :effect (effect (update! (dissoc-in card [:special :dheg-prog])) - (use-mu (:memoryunits target)))}}}) - -(define-card "Disrupter" - {:events - {:pre-init-trace - {:async true - :effect (effect (show-wait-prompt :corp "Runner to use Disrupter") - (continue-ability - :runner - {:optional - {:prompt "Use Disrupter's ability?" - :yes-ability - {:effect (req (trash state side card {:cause :ability-cost}) - (swap! state assoc-in [:trace :force-base] 0))} - :end-effect (effect (clear-wait-prompt :corp))}} - card nil))}}}) - -(define-card "Diwan" - {:prompt "Choose the server that this copy of Diwan is targeting:" - :choices (req servers) - :effect (effect (update! (assoc card :server-target target))) - :events {:purge {:effect (effect (trash card {:cause :purge}))} - :pre-corp-install - {:req (req (let [c target - serv (:server (second targets))] - (and (= serv (:server-target card)) - (not (and (is-central? serv) - (upgrade? c)))))) - :effect (effect (install-cost-bonus [:credit 1]))}}}) - -(define-card "Djinn" - {:abilities [{:label "Search your Stack for a virus program and add it to your Grip" - :prompt "Choose a Virus" - :msg (msg "add " (:title target) " to their Grip") - :choices (req (cancellable (filter #(and (program? %) - (has-subtype? % "Virus")) - (:deck runner)) :sorted)) - :cost [:click 1 :credit 1] - :effect (effect (trigger-event :searched-stack nil) - (shuffle! :deck) - (move target :hand))} - {:label "Install a non-Icebreaker program on Djinn" - :effect (effect (resolve-ability - {:cost [:click 1] - :prompt "Choose a non-Icebreaker program in your Grip to install on Djinn" - :choices {:req #(and (program? %) - (runner-can-install? state side % false) - (not (has-subtype? % "Icebreaker")) - (in-hand? %))} - :msg (msg "install and host " (:title target)) - :effect (effect (runner-install target {:host-card card :no-mu true}) - (update! (assoc (get-card state card) - :hosted-programs - (cons (:cid target) (:hosted-programs card)))))} - card nil))} - {:label "Host an installed non-Icebreaker program on Djinn" - :prompt "Choose an installed non-Icebreaker program to host on Djinn" - :choices {:req #(and (program? %) - (not (has-subtype? % "Icebreaker")) - (installed? %))} - :msg (msg "host " (:title target)) - :effect (effect (host card target) - (free-mu (:memoryunits target)) - (update! (assoc (get-card state card) - :hosted-programs (cons (:cid target) (:hosted-programs card)))))}] - :events {:card-moved {:req (req (some #{(:cid target)} (:hosted-programs card))) - :effect (effect (update! (assoc card - :hosted-programs (remove #(= (:cid target) %) (:hosted-programs card)))) - (use-mu (:memoryunits target)))}}}) - -(define-card "Eater" - (auto-icebreaker ["All"] - {:abilities [{:cost [:credit 1] - :msg "break ICE subroutine and access 0 cards this run" - :effect (effect (max-access 0))} - (strength-pump 1 1)]})) - -(define-card "Egret" - {:implementation "Added subtypes don't get removed when Egret is moved/trashed" - :hosting {:req #(and (ice? %) (can-host? %) (rezzed? %))} - :msg (msg "make " (card-str state (:host card)) " gain Barrier, Code Gate and Sentry subtypes") - :effect (req (when-let [h (:host card)] - (update! state side (assoc-in card [:special :installing] true)) - (update-ice-strength state side h) - (when-let [card (get-card state card)] - (update! state side (update-in card [:special] dissoc :installing))))) - :events {:ice-strength-changed - {:effect (req (unregister-events state side card) - (when (get-in card [:special :installing]) - (update! state side (assoc (:host (get-card state card)) :subtype (combine-subtypes false(-> card :host :subtype) "Barrier" "Code Gate" "Sentry"))) - (update! state side (update-in card [:special] dissoc :installing)) - (trigger-event state side :runner-install card)) - (continue state side nil))}}}) - -(define-card "Endless Hunger" - {:abilities [{:label "Trash 1 installed card to break 1 \"End the run.\" subroutine" - :prompt "Select a card to trash for Endless Hunger" - :choices {:req #(and (runner? %) (installed? %))} - :msg (msg "trash " (:title target) - " and break 1 \"[Subroutine] End the run.\" subroutine") - :effect (effect (trash target {:unpreventable true}))}]}) - -(define-card "Engolo" - (auto-icebreaker - ["Code Gate"] - {:abilities [(break-sub 1 1 "Code Gate") - (strength-pump 2 4) - (wrestling-breaker 2 "Code Gate")]})) - -(define-card "Equivocation" - (let [force-draw (fn [title] - {:optional {:prompt (str "Force the Corp to draw " title "?") - :yes-ability {:async true - :effect (req (show-wait-prompt state :runner "Corp to draw") - (wait-for (draw state :corp 1 nil) - (do (system-msg state :corp (str "is forced to draw " title)) - (clear-wait-prompt state :runner) - (effect-completed state side eid))))}}}) - reveal {:optional {:prompt "Reveal the top card of R&D?" - :yes-ability {:async true - :effect (req (let [topcard (-> corp :deck first :title)] - (reveal state side topcard) - (system-msg state :runner (str "reveals " topcard - " from the top of R&D")) - (continue-ability state side (force-draw topcard) card nil)))}}}] - {:events {:successful-run {:req (req (= target :rd)) + :effect (effect (host target card))}]}) + + "Imp" + {:data {:counter {:virus 2}} + :interactions {:access-ability {:label "[Imp]: Trash card" + :req (req (and (not (get-in @state [:per-turn (:cid card)])) + (pos? (get-counters card :virus)))) + :counter-cost [:virus 1] + :msg (msg "trash " (:title target) " at no cost") + :once :per-turn + :async true + :effect (effect (trash-no-cost eid target))}}} + + "Incubator" + {:events {:runner-turn-begins {:effect (effect (add-counter card :virus 1))}} + :abilities [{:cost [:click 1] + :msg (msg "move " (get-counters card :virus) " virus counter to " (:title target)) + :choices {:req #(and (installed? %) + (has-subtype? % "Virus"))} + :effect (effect (trash card {:cause :ability-cost}) + (add-counter target :virus (get-counters card :virus)))}]} + + "Inti" + (auto-icebreaker ["Barrier"] + {:abilities [(break-sub 1 1 "Barrier") + (strength-pump 2 1 :all-run)]}) + + "Inversificator" + (auto-icebreaker ["Code Gate"] + {:implementation "No restriction on which pieces of ICE are chosen" + :abilities [{:label "Swap the Code Gate you just passed with another ICE" + :once :per-turn + :req (req (:run @state)) + :prompt "Select the Code Gate you just passed and another piece of ICE to swap positions" + :choices {:req #(and (installed? %) (ice? %)) :max 2} + :msg (msg "swap the positions of " (card-str state (first targets)) " and " (card-str state (second targets))) + :effect (req (when (= (count targets) 2) + (swap-ice state side (first targets) (second targets))))} + (break-sub 1 1 "Code Gate") + (strength-pump 1 1)]}) + + "Ixodidae" + {:events {:corp-credit-loss {:msg "gain 1 [Credits]" + :effect (effect (gain-credits :runner 1))} + :purge {:effect (effect (trash card {:cause :purge}))}}} + + "Keyhole" + {:abilities [{:cost [:click 1] + :msg "make a run on R&D" + :makes-run true + :effect (effect (make-run :rd + {:req (req (= target :rd)) + :replace-access + {:prompt "Choose a card to trash" + :not-distinct true + :msg (msg "trash " (:title target)) + :choices (req (take 3 (:deck corp))) + :mandatory true + :effect (effect (trash (assoc target :seen true)) + (shuffle! :corp :deck))}} card))}]} + + "Knight" + {:abilities [{:label "Host Knight on a piece of ICE" + :effect (req (let [k (get-card state card) + hosted (ice? (:host k)) + icepos (ice-index state (get-card state (:host k)))] + (resolve-ability + state side + {:prompt (msg "Host Knight on a piece of ICE" (when hosted " not before or after the current host ICE")) + :cost [:click 1] + :choices {:req #(if hosted + (and (or (when (= (:zone %) (:zone (:host k))) + (not= 1 (abs (- (ice-index state %) icepos)))) + (not= (:zone %) (:zone (:host k)))) + (ice? %) + (can-host? %) + (installed? %) + (not-any? (fn [c] (has? c :subtype "Caïssa")) (:hosted %))) + (and (ice? %) + (installed? %) + (can-host? %) + (not-any? (fn [c] (has? c :subtype "Caïssa")) (:hosted %))))} + :msg (msg "host it on " (card-str state target)) + :effect (effect (host target card))} card nil)))} + {:cost [:credit 2] + :req (req (ice? (get-nested-host card))) + :msg "break 1 subroutine on the host ICE"}]} + + "Kyuban" + {:hosting {:req #(and (ice? %) (can-host? %))} + :events {:pass-ice {:req (req (same-card? target (:host card))) + :msg "gain 2 [Credits]" + :effect (effect (gain-credits :runner 2))}}} + + "Laamb" + (auto-icebreaker + ["Barrier"] + {:abilities [(break-sub 2 0 "Barrier") + (strength-pump 3 6) + (wrestling-breaker 2 "Barrier")]}) + + "Lamprey" + {:events {:successful-run {:req (req (= target :hq)) + :msg "force the Corp to lose 1 [Credits]" + :effect (effect (lose-credits :corp 1))} + :purge {:effect (effect (trash card {:cause :purge}))}}} + + "Leprechaun" + {:abilities [{:label "Install a program on Leprechaun" + :req (req (< (count (get-in card [:special :hosted-programs])) 2)) + :effect (effect (resolve-ability + {:cost [:click 1] + :prompt "Choose a program in your Grip to install on Leprechaun" + :choices {:req #(and (program? %) + (runner-can-install? state side % false) + (in-hand? %))} + :msg (msg "host " (:title target)) + :effect (effect (runner-install target {:host-card card :no-mu true}) + (update! (assoc-in (get-card state card) + [:special :hosted-programs] + (cons (:cid target) + (get-in card [:special :hosted-programs])))))} + card nil))} + {:label "Host an installed program on Leprechaun" + :req (req (< (count (get-in card [:special :hosted-programs])) 2)) + :prompt "Choose an installed program to host on Leprechaun" + :choices {:req #(and (program? %) + (installed? %))} + :msg (msg "host " (:title target)) + :effect (effect (free-mu (:memoryunits target)) + (update-breaker-strength target) + (host card (get-card state target)) + (update! (assoc-in (get-card state card) + [:special :hosted-programs] + (cons (:cid target) + (get-in card [:special :hosted-programs])))))}] + :events {:card-moved {:req (req (some #{(:cid target)} (get-in card [:special :hosted-programs]))) + :effect (effect (update! (assoc-in card + [:special :hosted-programs] + (remove #(= (:cid target) %) + (get-in card [:special :hosted-programs])))) + (use-mu (:memoryunits target)))}}} + + "Leviathan" + (auto-icebreaker ["Code Gate"] + {:abilities [(break-sub 3 3 "Code Gate") + (strength-pump 3 5)]}) + + "LLDS Energy Regulator" + {:interactions {:prevent [{:type #{:trash-hardware} + :req (req true)}]} + :abilities [{:cost [:credit 3] + :msg "prevent a hardware from being trashed" + :effect (effect (trash-prevent :hardware 1))} + {:label "[Trash]: Prevent a hardware from being trashed" + :msg "prevent a hardware from being trashed" + :effect (effect (trash-prevent :hardware 1) + (trash card {:cause :ability-cost}))}]} + + "Lustig" + (auto-icebreaker ["Sentry"] + {:abilities [(break-sub 1 1 "Sentry") + (strength-pump 3 5) + {:label "Bypass Sentry being encountered" + :req (req (has-subtype? current-ice "Sentry")) + :msg (msg "trash it and bypass " (:title current-ice)) + :effect (effect (trash card {:cause :ability-cost}))}]}) + + "Magnum Opus" + {:abilities [{:cost [:click 1] + :effect (effect (gain-credits 2)) + :msg "gain 2 [Credits]"}]} + + "Mammon" + (auto-icebreaker ["All"] + {:flags {:runner-phase-12 (req (pos? (:credit runner)))} + :abilities [{:label "X [Credits]: Place X power counters" + :prompt "How many power counters to place on Mammon?" :once :per-turn + :choices {:number (req (:credit runner))} + :req (req (:runner-phase-12 @state)) + :effect (effect (lose-credits target) + (add-counter card :power target)) + :msg (msg "place " target " power counters on it")} + {:counter-cost [:power 1] + :label "Hosted power counter: Break ICE subroutine" + :msg "break 1 ICE subroutine"} + (strength-pump 2 2)] + :events {:runner-turn-ends {:effect (effect (update! (assoc-in card [:counter :power] 0)))}}}) + + "Mass-Driver" + (auto-icebreaker ["Code Gate"] + {:implementation "Prevention of subroutine resolution on next ICE is manual" + :abilities [(break-sub 2 1 "Code Gate") + (strength-pump 1 1)]}) + + "Maven" + {:abilities [(break-sub 2 1 "ICE")] + :events (let [maven {:silent (req true) + :req (req (program? target)) + :effect (effect (update-breaker-strength card))}] + {:runner-install maven :trash maven :card-moved maven}) + :strength-bonus (req (count (filter program? (all-active-installed state :runner))))} + + "Medium" + {:events + {:successful-run {:req (req (= target :rd)) + :effect (effect (add-counter card :virus 1))} + :pre-access {:async true + :req (req (= target :rd)) + :effect (effect (continue-ability + {:req (req (< 1 (get-virus-counters state card))) + :prompt "Choose how many additional R&D accesses to make with Medium" + :choices {:number (req (dec (get-virus-counters state card))) + :default (req (dec (get-virus-counters state card)))} + :msg (msg "access " target " additional cards from R&D") + :effect (effect (access-bonus :rd (max 0 target)))} + card nil))}}} + + "Mimic" + {:abilities [(break-sub 1 1 "Sentry")]} + + "Misdirection" + {:abilities [{:cost [:click 2] + :prompt "How many [Credits] to spend to remove that number of tags?" + :choices {:number (req (min (:credit runner) (get-in runner [:tag :base])))} + :msg (msg "spend " target " [Credits] and remove " target " tags") + :effect (effect (lose-credits target) + (lose-tags target))}]} + + "MKUltra" + (conspiracy "MKUltra" "Sentry" + [{:cost [:credit 3] + :effect (effect (pump card 2)) :pump 2 + :msg "add 2 strength and break up to 2 subroutines"}]) + + "Mongoose" + (auto-icebreaker ["Sentry"] + {:implementation "Usage restriction is not implemented" + :abilities [(break-sub 1 2 "Sentry") + (strength-pump 2 2)]}) + + "Morning Star" + {:abilities [(break-sub 1 0 "Barrier")]} + + "Multithreader" + {:recurring 2 + :interactions {:pay-credits {:req (req (and (= :ability (:source-type eid)) + (program? target))) + :type :recurring}}} + + "Musaazi" + (khumalo-breaker "sentry") + + "Na'Not'K" + (auto-icebreaker ["Sentry"] + {:effect (req (add-watch state (keyword (str "nanotk" (:cid card))) + (fn [k ref old new] + (let [server (first (get-in @state [:run :server]))] + (when (or + ; run initiated or ended + (not= (get-in old [:run]) + (get-in new [:run])) + ; server configuration changed (redirected or newly installed ICE) + (not= (get-in old [:corp :servers server :ices]) + (get-in new [:corp :servers server :ices]))) + (update-breaker-strength ref side card)))))) + :strength-bonus (req (if-let [numice (count run-ices)] numice 0)) + :leave-play (req (remove-watch state (keyword (str "nanotk" (:cid card))))) + :abilities [(break-sub 1 1 "Sentry") + (strength-pump 3 2)]}) + + "Nerve Agent" + {:events + {:successful-run {:req (req (= target :hq)) + :effect (effect (add-counter card :virus 1))} + :pre-access {:async true + :req (req (= target :hq)) + :effect (effect (continue-ability + {:req (req (< 1 (get-virus-counters state card))) + :prompt "Choose how many additional HQ accesses to make with Nerve Agent" + :choices {:number (req (dec (get-virus-counters state card))) + :default (req (dec (get-virus-counters state card)))} + :msg (msg "access " target " additional cards from HQ") + :effect (effect (access-bonus :hq (max 0 target)))} + card nil))}}} + + "Net Shield" + {:interactions {:prevent [{:type #{:net} + :req (req true)}]} + :abilities [{:cost [:credit 1] :once :per-turn :msg "prevent the first net damage this turn" + :effect (effect (damage-prevent :net 1))}]} + + "Nfr" + {:implementation "Adding power counter is manual" + :abilities [{:label "Place 1 power counter on Nfr" + :msg "place 1 power counter on it" + :ability-type :manual-state + :effect (effect (add-counter card :power 1) + (update-breaker-strength card))} + (break-sub 1 1 "Barrier")] + :strength-bonus (req (get-counters card :power))} + + "Ninja" + (auto-icebreaker ["Sentry"] + {:abilities [(break-sub 1 1 "Sentry") + (strength-pump 3 5)]}) + + "Nyashia" + {:data {:counter {:power 3}} + :events {:pre-access {:async true + :req (req (and (pos? (get-counters card :power)) + (= target :rd))) + :effect (effect (show-wait-prompt :corp "Runner to use Nyashia") + (continue-ability + {:optional + {:prompt "Spend a power counter on Nyashia to access 1 additional card?" + :autoresolve (get-autoresolve :auto-nyashia) + :yes-ability {:msg "access 1 additional card from R&D" + :effect (effect (access-bonus :rd 1) + (add-counter card :power -1) + (clear-wait-prompt :corp))} + :no-ability {:effect (effect (clear-wait-prompt :corp))}}} + card nil))}} + :abilities [(set-autoresolve :auto-nyashia "Nyashia")]} + + "Omega" + (auto-icebreaker ["All"] + {:abilities [{:cost [:credit 1] :req (req (= 1 (:position run))) + :msg "break 1 subroutine on the innermost ICE protecting this server"} + (strength-pump 1 1)]}) + + "Origami" + {:effect (effect (gain :hand-size + {:mod (dec (* 2 (count (filter #(= (:title %) "Origami") + (all-active-installed state :runner)))))})) + :leave-play (effect (lose :hand-size + {:mod (dec (* 2 (count (filter #(= (:title %) "Origami") + (all-active-installed state :runner)))))}))} + + "Overmind" + (auto-icebreaker ["All"] + {:effect (effect (add-counter card :power (available-mu state))) + :abilities [{:counter-cost [:power 1] + :msg "break 1 subroutine"} + (strength-pump 1 1)]}) + + "Paintbrush" + {:abilities [{:cost [:click 1] + :choices {:req #(and (installed? %) (ice? %) (rezzed? %))} + :effect (req (let [ice target + stypes (:subtype ice)] + (resolve-ability + state :runner + {:prompt (msg "Choose a subtype") + :choices ["Sentry" "Code Gate" "Barrier"] + :msg (msg "spend [Click] and make " (card-str state ice) " gain " (.toLowerCase target) + " until the end of the next run this turn") + :effect (effect (update! (assoc ice :subtype (combine-subtypes true stypes target))) + (update-ice-strength (get-card state ice)) + (register-events {:run-ends + {:effect (effect (update! (assoc ice :subtype stypes)) + (unregister-events card) + (update-ice-strength (get-card state ice)))}} card))} + card nil)))}] + :events {:run-ends nil}} + + "Panchatantra" + {:abilities [{:msg "add a custom subtype to currently encountered ICE" + :once :per-turn}]} + + "Paperclip" + (conspiracy "Paperclip" "Barrier" + [{:label "X [Credits]: +X strength, break X subroutines" + :choices {:number (req (:credit runner)) + :default (req (if (:current-strength current-ice) + (max (- (:current-strength current-ice) + (:current-strength card)) + 1) + 1))} + :prompt "How many credits?" + :effect (effect (lose-credits target) + (pump card target)) + :msg (msg "spend " target " [Credits], increase strength by " target ", and break " + (quantify target "Barrier subroutine"))}]) + + "Parasite" + {:hosting {:req #(and (ice? %) (can-host? %) (rezzed? %))} + :effect (req (when-let [h (:host card)] + (update! state side (assoc-in card [:special :installing] true)) + (update-ice-strength state side h) + (when-let [card (get-card state card)] + (update! state side (update-in card [:special] dissoc :installing))))) + :events {:runner-turn-begins + {:effect (req (add-counter state side card :virus 1))} + :counter-added + {:req (req (or (= (:title target) "Hivemind") (same-card? target card))) + :effect (effect (update-ice-strength (:host card)))} + :pre-ice-strength + {:req (req (same-card? target (:host card))) + :effect (effect (ice-strength-bonus (- (get-virus-counters state card)) target))} + :ice-strength-changed + {:req (req (and (same-card? target (:host card)) + (not (card-flag? (:host card) :untrashable-while-rezzed true)) + (<= (:current-strength target) 0))) + :effect (req (unregister-events state side card) + (when (get-in card [:special :installing]) + (update! state side (update-in card [:special] dissoc :installing)) + (trigger-event state side :runner-install card)) + (trash state side target) + (continue state side nil)) + :msg (msg "trash " (:title target))}}} + + "Paricia" + {:recurring 2 + :interactions {:pay-credits {:req (req (and (= :runner-trash-corp-cards (:source-type eid)) + (asset? target))) + :type :recurring}}} + + "Passport" + (central-breaker "Code Gate" + (break-sub 1 1 "Code Gate") + (strength-pump 2 2)) + + "Pawn" + {:implementation "All abilities are manual" + :abilities [{:label "Host Pawn on the outermost ICE of a central server" + :cost [:click 1] + :prompt "Host Pawn on the outermost ICE of a central server" + :choices {:req #(and (ice? %) + (can-host? %) + (= (last (:zone %)) :ices) + (is-central? (second (:zone %))))} + :msg (msg "host it on " (card-str state target)) + :effect (effect (host target card))} + {:label "Advance to next ICE" + :prompt "Choose the next innermost ICE to host Pawn on it" + :choices {:req #(and (ice? %) + (can-host? %) + (= (last (:zone %)) :ices) + (is-central? (second (:zone %))))} + :msg (msg "host it on " (card-str state target)) + :effect (effect (host target card))} + {:label "Trash Pawn and install a Caïssa from your Grip or Heap, ignoring all costs" + :effect (req (let [this-pawn (:cid card)] + (resolve-ability + state side + {:prompt "Choose a Caïssa program to install from your Grip or Heap" + :show-discard true + :choices {:req #(and (has-subtype? % "Caïssa") + (not= (:cid %) this-pawn) + (#{[:hand] [:discard]} (:zone %)))} + :msg (msg "install " (:title target)) + :effect (effect (runner-install target {:ignore-all-cost true}))} + card nil) + (trash state side card)))}]} + + "Peacock" + (auto-icebreaker ["Code Gate"] + {:abilities [(break-sub 2 1 "Code Gate") + (strength-pump 2 3)]}) + + "Peregrine" + (auto-icebreaker ["Code Gate"] + {:abilities [(break-sub 1 1 "Code Gate") + (strength-pump 3 3) + {:label "Derez a Code Gate and return Peregrine to your Grip" + :cost [:credit 2] + :req (req (and (rezzed? current-ice) (has-subtype? current-ice "Code Gate"))) + :msg (msg "derez " (:title current-ice) " and return Peregrine to their Grip") + :effect (effect (derez current-ice) + (move card :hand))}]}) + + "Persephone" + (auto-icebreaker ["Sentry"] + {:implementation "Requires runner to input the number of subroutines allowed to resolve" + :abilities [(break-sub 2 1 "Sentry") + (strength-pump 1 1)] + :events {:pass-ice {:req (req (and (has-subtype? target "Sentry") (rezzed? target)) (pos? (count (:deck runner)))) + :optional {:prompt (msg "Use Persephone's ability??") + :yes-ability {:prompt "How many subroutines resolved on the passed ICE?" + :async true + :choices {:number (req 10)} + :msg (msg (if (pos? target) + (str "trash " (:title (first (:deck runner))) " from their Stack and trash " target " cards from R&D") + (str "trash " (:title (first (:deck runner))) " from their Stack and nothing from R&D"))) + :effect (effect (mill :runner) + (mill :runner :corp target))}}}}}) + + "Pelangi" + {:data {:counter {:virus 2}} + :events {:pass-ice nil + :run-ends nil} + :abilities [{:once :per-turn + :req (req (and current-ice + (rezzed? current-ice))) + :counter-cost [:virus 1] + :label "Make currently encountered ice gain a subtype" + :prompt "Choose an ICE subtype" + :choices (req (->> (server-cards) + (reduce (fn [acc card] + (if (ice? card) + (apply conj acc (split (:subtype card) #" - ")) + acc)) + #{}) + sort)) + :msg (msg "make " (card-str state current-ice) " gain " target) + :effect (req (let [ice current-ice + chosen-type target + stypes (:subtype ice) + remove-subtype + {:effect (effect (update! (assoc (get-card state ice) :subtype stypes)) + (system-say (str (card-str state ice) " loses " chosen-type)) + (unregister-events card) + (register-events (:events (card-def card)) card))}] + (update! state side (assoc ice :subtype (combine-subtypes false stypes chosen-type))) + (update-ice-strength state side (get-card state ice)) + (register-events state side {:pass-ice remove-subtype + :run-ends remove-subtype} card)))}]} + + "Pheromones" + {:recurring (req (when (< (get-counters card :recurring) (get-counters card :virus)) + (set-prop state side card :rec-counter (get-counters card :virus)))) + :events {:successful-run {:silent (req true) + :req (req (= target :hq)) + :effect (effect (add-counter card :virus 1))}} + :interactions {:pay-credits {:req (req (= :hq (get-in @state [:run :server 0]))) + :type :recurring}}} + + "Pipeline" + (auto-icebreaker ["Sentry"] + {:abilities [(break-sub 1 1 "Sentry") + (strength-pump 2 1 :all-run)]}) + + "Plague" + {:prompt "Choose a server for Plague" + :choices (req servers) + :msg (msg "target " target) + :req (req (not (get-in card [:special :server-target]))) + :effect (effect (update! (assoc-in card [:special :server-target] target))) + :events {:successful-run + {:req (req (= (zone->name (get-in @state [:run :server])) + (get-in (get-card state card) [:special :server-target]))) + :msg "gain 2 virus counters" + :effect (effect (add-counter :runner card :virus 2))}}} + + "Progenitor" + {:abilities [{:label "Install a virus program on Progenitor" + :req (req (empty? (:hosted card))) + :effect (effect (resolve-ability + {:cost [:click 1] + :prompt "Choose a Virus program to install on Progenitor" + :choices {:req #(and (program? %) + (has-subtype? % "Virus") + (in-hand? %))} + :msg (msg "host " (:title target)) + :effect (effect (runner-install target {:host-card card :no-mu true}) + (update! (assoc (get-card state card) + :hosted-programs + (cons (:cid target) (:hosted-programs card)))))} + card nil))} + {:label "Host an installed virus on Progenitor" + :req (req (empty? (:hosted card))) + :prompt "Choose an installed virus program to host on Progenitor" + :choices {:req #(and (program? %) + (has-subtype? % "Virus") + (installed? %))} + :msg (msg "host " (:title target)) + :effect (effect (host card target) + (free-mu (:memoryunits target)) + (update! (assoc (get-card state card) + :hosted-programs (cons (:cid target) (:hosted-programs card)))))}] + :events {:pre-purge {:effect (req (when-let [c (first (:hosted card))] + (update! state side (assoc-in card [:special :numpurged] (get-counters c :virus)))))} + :purge {:req (req (pos? (get-in card [:special :numpurged] 0))) + :effect (req (when-let [c (first (:hosted card))] + (add-counter state side c :virus 1)))} + :card-moved {:req (req (some #{(:cid target)} (:hosted-programs card))) + :effect (effect (update! (assoc card :hosted-programs (remove #(= (:cid target) %) (:hosted-programs card)))) + (use-mu (:memoryunits target)))}}} + + "Puffer" + (auto-icebreaker ["Sentry"] + {:implementation "Memory use must be manually tracked by the Runner" + :abilities [(break-sub 1 1 "Sentry") + (strength-pump 2 1) + {:cost [:click 1] :msg "place one power counter" + :label "Place 1 power counter" + :effect (effect (add-counter card :power 1) + (update-breaker-strength card))} + {:cost [:click 1] :msg "remove one power counter" + :label "Remove 1 power counter" + :effect (effect (add-counter card :power -1) + (update-breaker-strength card))}] + :strength-bonus (req (get-counters card :power))}) + + "Reaver" + {:events {:runner-trash {:req (req (and (first-installed-trash? state side) + (installed? target))) + :async true + :effect (effect (draw :runner eid 1 nil)) + :msg "draw 1 card"}}} + + "Refractor" + (auto-icebreaker ["Code Gate"] + {:implementation "Stealth credit restriction not enforced" + :abilities [(break-sub 1 1 "Code Gate") + (strength-pump 1 3)]}) + + "Rezeki" + {:events {:runner-turn-begins {:msg "gain 1 [Credits]" + :effect (effect (gain-credits 1))}}} + + "RNG Key" + {:events {:pre-access-card {:req (req (get-in card [:special :rng-guess])) :async true - :interactive (req true) - :effect (effect (continue-ability reveal card nil))}}})) - -(define-card "eXer" - {:in-play [:rd-access 1] - :events {:purge {:effect (effect (trash card {:cause :purge}))}}}) - -(define-card "Expert Schedule Analyzer" - {:abilities [{:cost [:click 1] - :msg "make a run on HQ" - :makes-run true - :effect (effect (make-run :hq {:req (req (= target :hq)) - :replace-access - {:msg (msg "reveal cards in HQ: " - (join ", " (map :title (:hand corp))))}} card))}]}) - -(define-card "Faerie" - (auto-icebreaker ["Sentry"] - {:abilities [(break-sub 0 1 "Sentry" (effect (update! (assoc-in card [:special :faerie-used] true)))) - (strength-pump 1 1)] - :events {:pass-ice {:req (req (get-in card [:special :faerie-used])) - :effect (effect (trash card))}}})) - -(define-card "False Echo" - {:abilities [{:req (req (and run - (< (:position run) (count run-ices)) - (not (rezzed? current-ice)))) - :msg "make the Corp rez the passed ICE or add it to HQ" - :effect (req (let [s (:server run) - ice (nth (get-in @state (vec (concat [:corp :servers] s [:ices]))) (:position run)) - icename (:title ice) - icecost (rez-cost state side ice)] - (continue-ability - state side - {:prompt (msg "Rez " icename " or add it to HQ?") :player :corp - :choices (req (if (< (:credit corp) icecost) - ["Add to HQ"] - ["Rez" "Add to HQ"])) - :effect (req (if (= target "Rez") - (rez state side ice) - (do (move state :corp ice :hand nil) - (system-msg state :corp (str "chooses to add the passed ICE to HQ")))) - (trash state side card))} - card nil)))}]}) - -(define-card "Faust" - {:abilities [{:label "Trash 1 card from Grip to break 1 subroutine" - :cost [:trash-from-hand 1] - :msg (msg "break 1 subroutine")} - {:label "Trash 1 card from Grip to add 2 strength" - :cost [:trash-from-hand 1] - :msg (msg "add 2 strength") - :effect (effect (pump card 2))}]}) - -(define-card "Fawkes" - {:implementation "Stealth credit restriction not enforced" - :abilities [(break-sub 1 1 "Sentry") - {:label "X [Credits]: +X strength for the remainder of the run (using at least 1 stealth [Credits])" - :choices :credit - :prompt "How many credits?" - :effect (effect (pump card target :all-run)) - :msg (msg "increase strength by " target " for the remainder of the run")}]}) - -(define-card "Femme Fatale" - (auto-icebreaker ["Sentry"] - {:prompt "Select a piece of ICE to target for bypassing" - :choices {:req ice?} - :leave-play (req (remove-icon state side card)) - :effect (req (let [ice target] - (add-icon state side card ice "F" "blue") - (system-msg state side - (str "selects " (card-str state ice) - " for Femme Fatale's bypass ability")))) - :abilities [(break-sub 1 1 "Sentry") - (strength-pump 2 1)]})) - -(define-card "Flashbang" - (auto-icebreaker ["Sentry"] - {:abilities [(strength-pump 1 1) - {:label "Derez a Sentry being encountered" - :cost [:credit 6] - :req (req (and (rezzed? current-ice) (has-subtype? current-ice "Sentry"))) - :msg (msg "derez " (:title current-ice)) - :effect (effect (derez current-ice))}]})) - -(define-card "Force of Nature" - (auto-icebreaker ["Code Gate"] - {:abilities [(break-sub 2 2 "Code Gate") - (strength-pump 1 1)]})) - -(define-card "Garrote" - (auto-icebreaker ["Sentry"] - {:abilities [(break-sub 1 1 "Sentry") - (strength-pump 1 1)]})) - -(define-card "Gauss" - (auto-icebreaker ["Barrier"] - {:strength-bonus (req (if (= :this-turn (:installed card)) 3 0)) - :events (let [losestr {:effect (effect (update-breaker-strength card))}] - {:runner-turn-ends losestr - :corp-turn-ends losestr}) - :abilities [(break-sub 1 1 "Barrier") - (strength-pump 2 2)]})) - -(define-card "Gingerbread" - (auto-icebreaker ["Tracer"] - {:abilities [(break-sub 1 1 "Tracer") - (strength-pump 2 3)]})) - -(define-card "God of War" - (auto-icebreaker ["All"] - {:flags {:runner-phase-12 (req true)} - :abilities [(strength-pump 2 1) - {:counter-cost [:virus 1] - :msg "break 1 subroutine"} - {:label "Take 1 tag to place 2 virus counters (start of turn)" - :once :per-turn - :effect (req (wait-for (gain-tags state :runner 1) - (if (not (get-in @state [:tag :tag-prevent])) - (do (add-counter state side card :virus 2) - (system-msg state side - (str "takes 1 tag to place 2 virus counters on God of War")) - (effect-completed state side eid)) - (effect-completed state side eid))))}]})) - -(define-card "Golden" - (auto-icebreaker ["Sentry"] - {:abilities [(break-sub 2 2 "Sentry") - (strength-pump 2 4) - {:label "Derez a Sentry and return Golden to your Grip" - :cost [:credit 2] - :req (req (and (rezzed? current-ice) (has-subtype? current-ice "Sentry"))) - :msg (msg "derez " (:title current-ice) " and return Golden to their Grip") - :effect (effect (derez current-ice) - (move card :hand))}]})) - -(define-card "Gordian Blade" - (auto-icebreaker ["Code Gate"] - {:abilities [(break-sub 1 1 "Code Gate") - (strength-pump 1 1 :all-run)]})) - -(define-card "Gorman Drip v1" - {:abilities [{:cost [:click 1] :effect (effect (gain-credits (get-virus-counters state card)) - (trash card {:cause :ability-cost})) - :msg (msg "gain " (get-virus-counters state card) " [Credits]")}] - :events {:corp-click-credit {:effect (effect (add-counter :runner card :virus 1))} - :corp-click-draw {:effect (effect (add-counter :runner card :virus 1))}}}) - -(define-card "Grappling Hook" - {:abilities [{:msg "break all but 1 subroutine" :effect (effect (trash card {:cause :ability-cost}))}]}) - -(define-card "Gravedigger" - {:events (let [e {:req (req (and (installed? target) (= (:side target) "Corp"))) - :effect (effect (add-counter :runner card :virus 1))}] - {:runner-trash e :corp-trash e}) - :abilities [{:counter-cost [:virus 1] - :cost [:click 1] - :msg "force the Corp to trash the top card of R&D" - :effect (effect (mill :corp))}]}) - -(define-card "GS Sherman M3" - (global-sec-breaker "Barrier")) - -(define-card "GS Shrike M2" - (global-sec-breaker "Sentry")) - -(define-card "GS Striker M1" - (global-sec-breaker "Code Gate")) - -(define-card "Harbinger" - {:trash-effect - {:req (req (not-any? #{:facedown :hand} (:previous-zone card))) - :effect (req (let [lock (get-in @state [:runner :locked :discard])] - (swap! state assoc-in [:runner :locked] nil) - (runner-install state :runner card {:facedown true}) - (swap! state assoc-in [:runner :locked] lock)))}}) - -(define-card "Hemorrhage" - {:events {:successful-run {:silent (req true) - :effect (effect (add-counter card :virus 1))}} - :abilities [{:counter-cost [:virus 2] - :cost [:click 1] - :req (req (pos? (count (:hand corp)))) - :msg "force the Corp to trash 1 card from HQ" - :effect (req (show-wait-prompt state :runner "Corp to trash a card from HQ") - (resolve-ability - state :corp - {:prompt "Choose a card to trash" - :choices (req (filter corp? (:hand corp))) - :effect (effect (trash target) - (clear-wait-prompt :runner))} - card nil))}]}) - -(define-card "Hivemind" - (let [update-programs (req (let [virus-programs (->> (all-installed state :runner) - (filter #(and (program? %) - (has-subtype? % "Virus") - (not (facedown? %)))))] - (doseq [p virus-programs] - (update-breaker-strength state side p))))] - {:data {:counter {:virus 1}} - :effect update-programs - :trash-effect {:effect update-programs} - :events {:counter-added {:req (req (same-card? target card)) - :effect update-programs}} - :abilities [{:req (req (pos? (get-counters card :virus))) - :priority true - :prompt "Move a virus counter to which card?" - :choices {:req #(has-subtype? % "Virus")} - :effect (req (let [abilities (:abilities (card-def target)) - virus target] - (add-counter state :runner virus :virus 1) - (add-counter state :runner card :virus -1) - (if (= (count abilities) 1) - (do (swap! state update-in [side :prompt] rest) ; remove the Hivemind prompt so Imp works - (resolve-ability state side (first abilities) (get-card state virus) nil)) - (resolve-ability - state side - {:prompt "Choose an ability to trigger" - :choices (vec (map :msg abilities)) - :effect (req (swap! state update-in [side :prompt] rest) - (resolve-ability - state side - (first (filter #(= (:msg %) target) abilities)) - card nil))} - (get-card state virus) nil)))) - :msg (msg "trigger an ability on " (:title target))}]})) - -(define-card "Houdini" - {:abilities [(break-sub 1 1 "Code Gate") - {:cost [:credit 2] - :msg "add 4 strength (using at least 1 stealth [Credits])" - :effect (effect (pump card 4 :all-run)) :pump 4}]}) - -(define-card "Hyperdriver" - {:flags {:runner-phase-12 (req true)} - :abilities [{:label "Remove Hyperdriver from the game to gain [Click] [Click] [Click]" - :req (req (:runner-phase-12 @state)) - :effect (effect (move card :rfg) (gain :click 3)) - :msg "gain [Click][Click][Click]"}]}) - -(define-card "Ika" - (auto-icebreaker ["Sentry"] - {:abilities [(break-sub 1 2 "Sentry") - (strength-pump 2 3) - {:label "Host Ika on a piece of ICE" - :prompt (msg "Host Ika on a piece of ICE") - :cost [:credit 2] - :choices {:req #(and (ice? %) - (installed? %) - (can-host? %))} - :msg (msg "host it on " (card-str state target)) - :effect (effect (host target card))}]})) - -(define-card "Imp" - {:data {:counter {:virus 2}} - :interactions {:access-ability {:label "[Imp]: Trash card" - :req (req (and (not (get-in @state [:per-turn (:cid card)])) - (pos? (get-counters card :virus)))) - :counter-cost [:virus 1] - :msg (msg "trash " (:title target) " at no cost") - :once :per-turn - :async true - :effect (effect (trash-no-cost eid target))}}}) - -(define-card "Incubator" - {:events {:runner-turn-begins {:effect (effect (add-counter card :virus 1))}} - :abilities [{:cost [:click 1] - :msg (msg "move " (get-counters card :virus) " virus counter to " (:title target)) - :choices {:req #(and (installed? %) - (has-subtype? % "Virus"))} - :effect (effect (trash card {:cause :ability-cost}) - (add-counter target :virus (get-counters card :virus)))}]}) - -(define-card "Inti" - (auto-icebreaker ["Barrier"] - {:abilities [(break-sub 1 1 "Barrier") - (strength-pump 2 1 :all-run)]})) - -(define-card "Inversificator" - (auto-icebreaker ["Code Gate"] - {:implementation "No restriction on which pieces of ICE are chosen" - :abilities [{:label "Swap the Code Gate you just passed with another ICE" - :once :per-turn - :req (req (:run @state)) - :prompt "Select the Code Gate you just passed and another piece of ICE to swap positions" - :choices {:req #(and (installed? %) (ice? %)) :max 2} - :msg (msg "swap the positions of " (card-str state (first targets)) " and " (card-str state (second targets))) - :effect (req (when (= (count targets) 2) - (swap-ice state side (first targets) (second targets))))} - (break-sub 1 1 "Code Gate") - (strength-pump 1 1)]})) - -(define-card "Ixodidae" - {:events {:corp-credit-loss {:msg "gain 1 [Credits]" - :effect (effect (gain-credits :runner 1))} - :purge {:effect (effect (trash card {:cause :purge}))}}}) - -(define-card "Keyhole" - {:abilities [{:cost [:click 1] - :msg "make a run on R&D" - :makes-run true - :effect (effect (make-run :rd - {:req (req (= target :rd)) - :replace-access - {:prompt "Choose a card to trash" - :not-distinct true - :msg (msg "trash " (:title target)) - :choices (req (take 3 (:deck corp))) - :mandatory true - :effect (effect (trash (assoc target :seen true)) - (shuffle! :corp :deck))}} card))}]}) - -(define-card "Knight" - {:abilities [{:label "Host Knight on a piece of ICE" - :effect (req (let [k (get-card state card) - hosted (ice? (:host k)) - icepos (ice-index state (get-card state (:host k)))] - (resolve-ability - state side - {:prompt (msg "Host Knight on a piece of ICE" (when hosted " not before or after the current host ICE")) - :cost [:click 1] - :choices {:req #(if hosted - (and (or (when (= (:zone %) (:zone (:host k))) - (not= 1 (abs (- (ice-index state %) icepos)))) - (not= (:zone %) (:zone (:host k)))) - (ice? %) - (can-host? %) - (installed? %) - (not-any? (fn [c] (has? c :subtype "Caïssa")) (:hosted %))) - (and (ice? %) - (installed? %) - (can-host? %) - (not-any? (fn [c] (has? c :subtype "Caïssa")) (:hosted %))))} - :msg (msg "host it on " (card-str state target)) - :effect (effect (host target card))} card nil)))} - {:cost [:credit 2] - :req (req (ice? (get-nested-host card))) - :msg "break 1 subroutine on the host ICE"}]}) - -(define-card "Kyuban" - {:hosting {:req #(and (ice? %) (can-host? %))} - :events {:pass-ice {:req (req (same-card? target (:host card))) - :msg "gain 2 [Credits]" - :effect (effect (gain-credits :runner 2))}}}) - -(define-card "Laamb" - (auto-icebreaker - ["Barrier"] - {:abilities [(break-sub 2 0 "Barrier") - (strength-pump 3 6) - (wrestling-breaker 2 "Barrier")]})) - -(define-card "Lamprey" - {:events {:successful-run {:req (req (= target :hq)) - :msg "force the Corp to lose 1 [Credits]" - :effect (effect (lose-credits :corp 1))} - :purge {:effect (effect (trash card {:cause :purge}))}}}) - -(define-card "Leprechaun" - {:abilities [{:label "Install a program on Leprechaun" - :req (req (< (count (get-in card [:special :hosted-programs])) 2)) - :effect (effect (resolve-ability - {:cost [:click 1] - :prompt "Choose a program in your Grip to install on Leprechaun" - :choices {:req #(and (program? %) - (runner-can-install? state side % false) - (in-hand? %))} - :msg (msg "host " (:title target)) - :effect (effect (runner-install target {:host-card card :no-mu true}) - (update! (assoc-in (get-card state card) - [:special :hosted-programs] - (cons (:cid target) - (get-in card [:special :hosted-programs])))))} - card nil))} - {:label "Host an installed program on Leprechaun" - :req (req (< (count (get-in card [:special :hosted-programs])) 2)) - :prompt "Choose an installed program to host on Leprechaun" - :choices {:req #(and (program? %) - (installed? %))} - :msg (msg "host " (:title target)) - :effect (effect (free-mu (:memoryunits target)) - (update-breaker-strength target) - (host card (get-card state target)) - (update! (assoc-in (get-card state card) - [:special :hosted-programs] - (cons (:cid target) - (get-in card [:special :hosted-programs])))))}] - :events {:card-moved {:req (req (some #{(:cid target)} (get-in card [:special :hosted-programs]))) - :effect (effect (update! (assoc-in card - [:special :hosted-programs] - (remove #(= (:cid target) %) - (get-in card [:special :hosted-programs])))) - (use-mu (:memoryunits target)))}}}) - -(define-card "Leviathan" - (auto-icebreaker ["Code Gate"] - {:abilities [(break-sub 3 3 "Code Gate") - (strength-pump 3 5)]})) - -(define-card "LLDS Energy Regulator" - {:interactions {:prevent [{:type #{:trash-hardware} - :req (req true)}]} - :abilities [{:cost [:credit 3] - :msg "prevent a hardware from being trashed" - :effect (effect (trash-prevent :hardware 1))} - {:label "[Trash]: Prevent a hardware from being trashed" - :msg "prevent a hardware from being trashed" - :effect (effect (trash-prevent :hardware 1) - (trash card {:cause :ability-cost}))}]}) - -(define-card "Lustig" - (auto-icebreaker ["Sentry"] - {:abilities [(break-sub 1 1 "Sentry") - (strength-pump 3 5) - {:label "Bypass Sentry being encountered" - :req (req (has-subtype? current-ice "Sentry")) - :msg (msg "trash it and bypass " (:title current-ice)) - :effect (effect (trash card {:cause :ability-cost}))}]})) - -(define-card "Magnum Opus" - {:abilities [{:cost [:click 1] - :effect (effect (gain-credits 2)) - :msg "gain 2 [Credits]"}]}) - -(define-card "Mammon" - (auto-icebreaker ["All"] - {:flags {:runner-phase-12 (req (pos? (:credit runner)))} - :abilities [{:label "X [Credits]: Place X power counters" - :prompt "How many power counters to place on Mammon?" :once :per-turn - :choices {:number (req (:credit runner))} - :req (req (:runner-phase-12 @state)) - :effect (effect (lose-credits target) - (add-counter card :power target)) - :msg (msg "place " target " power counters on it")} - {:counter-cost [:power 1] - :label "Hosted power counter: Break ICE subroutine" - :msg "break 1 ICE subroutine"} - (strength-pump 2 2)] - :events {:runner-turn-ends {:effect (effect (update! (assoc-in card [:counter :power] 0)))}}})) - -(define-card "Mass-Driver" - (auto-icebreaker ["Code Gate"] - {:implementation "Prevention of subroutine resolution on next ICE is manual" - :abilities [(break-sub 2 1 "Code Gate") - (strength-pump 1 1)]})) - -(define-card "Maven" - {:abilities [(break-sub 2 1 "ICE")] - :events (let [maven {:silent (req true) - :req (req (program? target)) - :effect (effect (update-breaker-strength card))}] - {:runner-install maven :trash maven :card-moved maven}) - :strength-bonus (req (count (filter program? (all-active-installed state :runner))))}) - -(define-card "Medium" - {:events - {:successful-run {:req (req (= target :rd)) - :effect (effect (add-counter card :virus 1))} - :pre-access {:async true - :req (req (= target :rd)) - :effect (effect (continue-ability - {:req (req (< 1 (get-virus-counters state card))) - :prompt "Choose how many additional R&D accesses to make with Medium" - :choices {:number (req (dec (get-virus-counters state card))) - :default (req (dec (get-virus-counters state card)))} - :msg (msg "access " target " additional cards from R&D") - :effect (effect (access-bonus :rd (max 0 target)))} - card nil))}}}) - -(define-card "Mimic" - {:abilities [(break-sub 1 1 "Sentry")]}) - -(define-card "Misdirection" - {:abilities [{:cost [:click 2] - :prompt "How many [Credits] to spend to remove that number of tags?" - :choices {:number (req (min (:credit runner) (get-in runner [:tag :base])))} - :msg (msg "spend " target " [Credits] and remove " target " tags") - :effect (effect (lose-credits target) - (lose-tags target))}]}) - -(define-card "MKUltra" - (conspiracy "MKUltra" "Sentry" - [{:cost [:credit 3] - :effect (effect (pump card 2)) :pump 2 - :msg "add 2 strength and break up to 2 subroutines"}])) - -(define-card "Mongoose" - (auto-icebreaker ["Sentry"] - {:implementation "Usage restriction is not implemented" - :abilities [(break-sub 1 2 "Sentry") - (strength-pump 2 2)]})) - -(define-card "Morning Star" - {:abilities [(break-sub 1 0 "Barrier")]}) - -(define-card "Multithreader" - {:recurring 2 - :interactions {:pay-credits {:req (req (and (= :ability (:source-type eid)) - (program? target))) - :type :recurring}}}) - -(define-card "Musaazi" - (khumalo-breaker "sentry")) - -(define-card "Na'Not'K" - (auto-icebreaker ["Sentry"] - {:effect (req (add-watch state (keyword (str "nanotk" (:cid card))) - (fn [k ref old new] - (let [server (first (get-in @state [:run :server]))] - (when (or - ; run initiated or ended - (not= (get-in old [:run]) - (get-in new [:run])) - ; server configuration changed (redirected or newly installed ICE) - (not= (get-in old [:corp :servers server :ices]) - (get-in new [:corp :servers server :ices]))) - (update-breaker-strength ref side card)))))) - :strength-bonus (req (if-let [numice (count run-ices)] numice 0)) - :leave-play (req (remove-watch state (keyword (str "nanotk" (:cid card))))) - :abilities [(break-sub 1 1 "Sentry") - (strength-pump 3 2)]})) - -(define-card "Nerve Agent" - {:events - {:successful-run {:req (req (= target :hq)) - :effect (effect (add-counter card :virus 1))} - :pre-access {:async true - :req (req (= target :hq)) - :effect (effect (continue-ability - {:req (req (< 1 (get-virus-counters state card))) - :prompt "Choose how many additional HQ accesses to make with Nerve Agent" - :choices {:number (req (dec (get-virus-counters state card))) - :default (req (dec (get-virus-counters state card)))} - :msg (msg "access " target " additional cards from HQ") - :effect (effect (access-bonus :hq (max 0 target)))} - card nil))}}}) - -(define-card "Net Shield" - {:interactions {:prevent [{:type #{:net} - :req (req true)}]} - :abilities [{:cost [:credit 1] :once :per-turn :msg "prevent the first net damage this turn" - :effect (effect (damage-prevent :net 1))}]}) - -(define-card "Nfr" - {:implementation "Adding power counter is manual" - :abilities [{:label "Place 1 power counter on Nfr" - :msg "place 1 power counter on it" - :ability-type :manual-state - :effect (effect (add-counter card :power 1) - (update-breaker-strength card))} - (break-sub 1 1 "Barrier")] - :strength-bonus (req (get-counters card :power))}) - -(define-card "Ninja" - (auto-icebreaker ["Sentry"] - {:abilities [(break-sub 1 1 "Sentry") - (strength-pump 3 5)]})) - -(define-card "Nyashia" - {:data {:counter {:power 3}} - :events {:pre-access {:async true - :req (req (and (pos? (get-counters card :power)) - (= target :rd))) - :effect (effect (show-wait-prompt :corp "Runner to use Nyashia") - (continue-ability - {:optional - {:prompt "Spend a power counter on Nyashia to access 1 additional card?" - :autoresolve (get-autoresolve :auto-nyashia) - :yes-ability {:msg "access 1 additional card from R&D" - :effect (effect (access-bonus :rd 1) - (add-counter card :power -1) - (clear-wait-prompt :corp))} - :no-ability {:effect (effect (clear-wait-prompt :corp))}}} - card nil))}} - :abilities [(set-autoresolve :auto-nyashia "Nyashia")]}) - -(define-card "Omega" - (auto-icebreaker ["All"] - {:abilities [{:cost [:credit 1] :req (req (= 1 (:position run))) - :msg "break 1 subroutine on the innermost ICE protecting this server"} - (strength-pump 1 1)]})) - -(define-card "Origami" - {:effect (effect (gain :hand-size - {:mod (dec (* 2 (count (filter #(= (:title %) "Origami") - (all-active-installed state :runner)))))})) - :leave-play (effect (lose :hand-size - {:mod (dec (* 2 (count (filter #(= (:title %) "Origami") - (all-active-installed state :runner)))))}))}) - -(define-card "Overmind" - (auto-icebreaker ["All"] - {:effect (effect (add-counter card :power (available-mu state))) - :abilities [{:counter-cost [:power 1] - :msg "break 1 subroutine"} - (strength-pump 1 1)]})) - -(define-card "Paintbrush" - {:abilities [{:cost [:click 1] - :choices {:req #(and (installed? %) (ice? %) (rezzed? %))} - :effect (req (let [ice target - stypes (:subtype ice)] - (resolve-ability - state :runner - {:prompt (msg "Choose a subtype") - :choices ["Sentry" "Code Gate" "Barrier"] - :msg (msg "spend [Click] and make " (card-str state ice) " gain " (.toLowerCase target) - " until the end of the next run this turn") - :effect (effect (update! (assoc ice :subtype (combine-subtypes true stypes target))) - (update-ice-strength (get-card state ice)) - (register-events {:run-ends - {:effect (effect (update! (assoc ice :subtype stypes)) - (unregister-events card) - (update-ice-strength (get-card state ice)))}} card))} - card nil)))}] - :events {:run-ends nil}}) - -(define-card "Panchatantra" - {:abilities [{:msg "add a custom subtype to currently encountered ICE" - :once :per-turn}]}) - -(define-card "Paperclip" - (conspiracy "Paperclip" "Barrier" - [{:label "X [Credits]: +X strength, break X subroutines" - :choices {:number (req (:credit runner)) - :default (req (if (:current-strength current-ice) - (max (- (:current-strength current-ice) - (:current-strength card)) - 1) - 1))} - :prompt "How many credits?" - :effect (effect (lose-credits target) - (pump card target)) - :msg (msg "spend " target " [Credits], increase strength by " target ", and break " - (quantify target "Barrier subroutine"))}])) - -(define-card "Parasite" - {:hosting {:req #(and (ice? %) (can-host? %) (rezzed? %))} - :effect (req (when-let [h (:host card)] - (update! state side (assoc-in card [:special :installing] true)) - (update-ice-strength state side h) - (when-let [card (get-card state card)] - (update! state side (update-in card [:special] dissoc :installing))))) - :events {:runner-turn-begins - {:effect (req (add-counter state side card :virus 1))} - :counter-added - {:req (req (or (= (:title target) "Hivemind") (same-card? target card))) - :effect (effect (update-ice-strength (:host card)))} - :pre-ice-strength - {:req (req (same-card? target (:host card))) - :effect (effect (ice-strength-bonus (- (get-virus-counters state card)) target))} - :ice-strength-changed - {:req (req (and (same-card? target (:host card)) - (not (card-flag? (:host card) :untrashable-while-rezzed true)) - (<= (:current-strength target) 0))) - :effect (req (unregister-events state side card) - (when (get-in card [:special :installing]) - (update! state side (update-in card [:special] dissoc :installing)) - (trigger-event state side :runner-install card)) - (trash state side target) - (continue state side nil)) - :msg (msg "trash " (:title target))}}}) - -(define-card "Paricia" - {:recurring 2 - :interactions {:pay-credits {:req (req (and (= :runner-trash-corp-cards (:source-type eid)) - (asset? target))) - :type :recurring}}}) - -(define-card "Passport" - (central-breaker "Code Gate" - (break-sub 1 1 "Code Gate") - (strength-pump 2 2))) - -(define-card "Pawn" - {:implementation "All abilities are manual" - :abilities [{:label "Host Pawn on the outermost ICE of a central server" - :cost [:click 1] - :prompt "Host Pawn on the outermost ICE of a central server" - :choices {:req #(and (ice? %) - (can-host? %) - (= (last (:zone %)) :ices) - (is-central? (second (:zone %))))} - :msg (msg "host it on " (card-str state target)) - :effect (effect (host target card))} - {:label "Advance to next ICE" - :prompt "Choose the next innermost ICE to host Pawn on it" - :choices {:req #(and (ice? %) - (can-host? %) - (= (last (:zone %)) :ices) - (is-central? (second (:zone %))))} - :msg (msg "host it on " (card-str state target)) - :effect (effect (host target card))} - {:label "Trash Pawn and install a Caïssa from your Grip or Heap, ignoring all costs" - :effect (req (let [this-pawn (:cid card)] - (resolve-ability - state side - {:prompt "Choose a Caïssa program to install from your Grip or Heap" - :show-discard true - :choices {:req #(and (has-subtype? % "Caïssa") - (not= (:cid %) this-pawn) - (#{[:hand] [:discard]} (:zone %)))} - :msg (msg "install " (:title target)) - :effect (effect (runner-install target {:ignore-all-cost true}))} - card nil) - (trash state side card)))}]}) - -(define-card "Peacock" - (auto-icebreaker ["Code Gate"] - {:abilities [(break-sub 2 1 "Code Gate") - (strength-pump 2 3)]})) - -(define-card "Peregrine" - (auto-icebreaker ["Code Gate"] - {:abilities [(break-sub 1 1 "Code Gate") - (strength-pump 3 3) - {:label "Derez a Code Gate and return Peregrine to your Grip" - :cost [:credit 2] - :req (req (and (rezzed? current-ice) (has-subtype? current-ice "Code Gate"))) - :msg (msg "derez " (:title current-ice) " and return Peregrine to their Grip") - :effect (effect (derez current-ice) - (move card :hand))}]})) - -(define-card "Persephone" - (auto-icebreaker ["Sentry"] - {:implementation "Requires runner to input the number of subroutines allowed to resolve" - :abilities [(break-sub 2 1 "Sentry") - (strength-pump 1 1)] - :events {:pass-ice {:req (req (and (has-subtype? target "Sentry") (rezzed? target)) (pos? (count (:deck runner)))) - :optional {:prompt (msg "Use Persephone's ability??") - :yes-ability {:prompt "How many subroutines resolved on the passed ICE?" - :async true - :choices {:number (req 10)} - :msg (msg (if (pos? target) - (str "trash " (:title (first (:deck runner))) " from their Stack and trash " target " cards from R&D") - (str "trash " (:title (first (:deck runner))) " from their Stack and nothing from R&D"))) - :effect (effect (mill :runner) - (mill :runner :corp target))}}}}})) - -(define-card "Pelangi" - {:data {:counter {:virus 2}} - :events {:pass-ice nil - :run-ends nil} - :abilities [{:once :per-turn - :req (req (and current-ice - (rezzed? current-ice))) - :counter-cost [:virus 1] - :label "Make currently encountered ice gain a subtype" - :prompt "Choose an ICE subtype" - :choices (req (->> (server-cards) - (reduce (fn [acc card] - (if (ice? card) - (apply conj acc (split (:subtype card) #" - ")) - acc)) - #{}) - sort)) - :msg (msg "make " (card-str state current-ice) " gain " target) - :effect (req (let [ice current-ice - chosen-type target - stypes (:subtype ice) - remove-subtype - {:effect (effect (update! (assoc (get-card state ice) :subtype stypes)) - (system-say (str (card-str state ice) " loses " chosen-type)) - (unregister-events card) - (register-events (:events (card-def card)) card))}] - (update! state side (assoc ice :subtype (combine-subtypes false stypes chosen-type))) - (update-ice-strength state side (get-card state ice)) - (register-events state side {:pass-ice remove-subtype - :run-ends remove-subtype} card)))}]}) - -(define-card "Pheromones" - {:recurring (req (when (< (get-counters card :recurring) (get-counters card :virus)) - (set-prop state side card :rec-counter (get-counters card :virus)))) - :events {:successful-run {:silent (req true) - :req (req (= target :hq)) - :effect (effect (add-counter card :virus 1))}} - :interactions {:pay-credits {:req (req (= :hq (get-in @state [:run :server 0]))) - :type :recurring}}}) - -(define-card "Pipeline" - (auto-icebreaker ["Sentry"] - {:abilities [(break-sub 1 1 "Sentry") - (strength-pump 2 1 :all-run)]})) - -(define-card "Plague" - {:prompt "Choose a server for Plague" - :choices (req servers) - :msg (msg "target " target) - :req (req (not (get-in card [:special :server-target]))) - :effect (effect (update! (assoc-in card [:special :server-target] target))) - :events {:successful-run - {:req (req (= (zone->name (get-in @state [:run :server])) - (get-in (get-card state card) [:special :server-target]))) - :msg "gain 2 virus counters" - :effect (effect (add-counter :runner card :virus 2))}}}) - -(define-card "Progenitor" - {:abilities [{:label "Install a virus program on Progenitor" - :req (req (empty? (:hosted card))) - :effect (effect (resolve-ability - {:cost [:click 1] - :prompt "Choose a Virus program to install on Progenitor" - :choices {:req #(and (program? %) - (has-subtype? % "Virus") - (in-hand? %))} - :msg (msg "host " (:title target)) - :effect (effect (runner-install target {:host-card card :no-mu true}) - (update! (assoc (get-card state card) - :hosted-programs - (cons (:cid target) (:hosted-programs card)))))} - card nil))} - {:label "Host an installed virus on Progenitor" - :req (req (empty? (:hosted card))) - :prompt "Choose an installed virus program to host on Progenitor" - :choices {:req #(and (program? %) - (has-subtype? % "Virus") - (installed? %))} - :msg (msg "host " (:title target)) - :effect (effect (host card target) - (free-mu (:memoryunits target)) - (update! (assoc (get-card state card) - :hosted-programs (cons (:cid target) (:hosted-programs card)))))}] - :events {:pre-purge {:effect (req (when-let [c (first (:hosted card))] - (update! state side (assoc-in card [:special :numpurged] (get-counters c :virus)))))} - :purge {:req (req (pos? (get-in card [:special :numpurged] 0))) - :effect (req (when-let [c (first (:hosted card))] - (add-counter state side c :virus 1)))} - :card-moved {:req (req (some #{(:cid target)} (:hosted-programs card))) - :effect (effect (update! (assoc card :hosted-programs (remove #(= (:cid target) %) (:hosted-programs card)))) - (use-mu (:memoryunits target)))}}}) - -(define-card "Puffer" - (auto-icebreaker ["Sentry"] - {:implementation "Memory use must be manually tracked by the Runner" - :abilities [(break-sub 1 1 "Sentry") - (strength-pump 2 1) - {:cost [:click 1] :msg "place one power counter" - :label "Place 1 power counter" - :effect (effect (add-counter card :power 1) - (update-breaker-strength card))} - {:cost [:click 1] :msg "remove one power counter" - :label "Remove 1 power counter" - :effect (effect (add-counter card :power -1) - (update-breaker-strength card))}] - :strength-bonus (req (get-counters card :power))})) - -(define-card "Reaver" - {:events {:runner-trash {:req (req (and (first-installed-trash? state side) - (installed? target))) - :async true - :effect (effect (draw :runner eid 1 nil)) - :msg "draw 1 card"}}}) - -(define-card "Refractor" - (auto-icebreaker ["Code Gate"] - {:implementation "Stealth credit restriction not enforced" - :abilities [(break-sub 1 1 "Code Gate") - (strength-pump 1 3)]})) - -(define-card "Rezeki" - {:events {:runner-turn-begins {:msg "gain 1 [Credits]" - :effect (effect (gain-credits 1))}}}) - -(define-card "RNG Key" - {:events {:pre-access-card {:req (req (get-in card [:special :rng-guess])) - :async true - :msg (msg "reveal " (:title target)) - :effect (req (if-let [guess (get-in card [:special :rng-guess])] - (if (installed? target) - ;; Do not trigger on installed cards (can't "reveal" an installed card per UFAQ) - (do (toast state :runner "Installed cards cannot be revealed, so RNG Key does not pay out." "info") - (effect-completed state side eid)) - (if (or (= guess (:cost target)) - (= guess (:advancementcost target))) - (continue-ability state side - {:prompt "Choose RNG Key award" - :choices ["Gain 3 [Credits]" "Draw 2 cards"] - :async true - :effect (req (if (= target "Draw 2 cards") - (do (system-msg state :runner "uses RNG Key to draw 2 cards") - (draw state :runner eid 2 nil)) - (do (system-msg state :runner "uses RNG Key to gain 3 [Credits]") - (gain-credits state :runner 3) - (effect-completed state side eid))))} - card nil) - (effect-completed state side eid))) - (effect-completed state side eid)))} - :post-access-card {:effect (effect (update! (assoc-in card [:special :rng-guess] nil)))} - :successful-run {:req (req (and (#{:hq :rd} target) - (first-event? state :runner :successful-run #{[:hq] [:rd]}))) - :optional {:prompt "Fire RNG Key?" - :autoresolve (get-autoresolve :auto-fire) - :yes-ability {:prompt "Guess a number" - :choices {:number (req 20)} - :msg (msg "guess " target) - :effect (effect (update! (assoc-in card [:special :rng-guess] target)))}}}} - :abilities [(set-autoresolve :auto-fire "RNG Key")]}) - -(define-card "Rook" - {:abilities [{:cost [:click 1] - :effect (req (let [r (get-card state card) - hosted? (ice? (:host r)) - icepos (ice-index state (get-card state (:host r)))] - (resolve-ability - state side - {:prompt (if hosted? - (msg "Host Rook on a piece of ICE protecting this server or at position " - icepos " of a different server") - (msg "Host Rook on a piece of ICE protecting any server")) - :choices {:req #(if hosted? - (and (or (= (:zone %) (:zone (:host r))) - (= (ice-index state %) icepos)) - (= (last (:zone %)) :ices) - (ice? %) - (can-host? %) - (not-any? (fn [c] (has? c :subtype "Caïssa")) (:hosted %))) - (and (ice? %) - (can-host? %) - (= (last (:zone %)) :ices) - (not-any? (fn [c] (has? c :subtype "Caïssa")) (:hosted %))))} - :msg (msg "host it on " (card-str state target)) - :effect (effect (host target card))} card nil)))}] - :events {:pre-rez-cost {:req (req (= (:zone (:host card)) (:zone target))) - :effect (effect (rez-cost-bonus 2))}}}) - -(define-card "Sadyojata" - (deva "Sadyojata")) - -(define-card "Sage" - (ancient-greek-breaker "sage" [{:cost [:credit 2] - :req (req (or (has-subtype? current-ice "Barrier") - (has-subtype? current-ice "Code Gate"))) - :msg "break 1 Code Gate or Barrier subroutine"}])) - -(define-card "Sahasrara" - {:recurring 2 - :interactions {:pay-credits {:req (req (and (= :runner-install (:source-type eid)) - (program? target))) - :type :recurring}}}) - -(define-card "Saker" - (auto-icebreaker ["Barrier"] - {:abilities [(break-sub 1 1 "Barrier") - (strength-pump 2 2) - {:label "Derez a Barrier and return Saker to your Grip" - :cost [:credit 2] - :req (req (and (rezzed? current-ice) (has-subtype? current-ice "Barrier"))) - :msg (msg "derez " (:title current-ice) " and return Saker to their Grip") - :effect (effect (derez current-ice) - (move card :hand))}]})) - -(define-card "Savant" - (ancient-greek-breaker "savant" [{:cost [:credit 2] :req (req (has-subtype? current-ice "Sentry")) - :msg "break 1 Sentry subroutine"} - {:cost [:credit 2] :req (req (has-subtype? current-ice "Code Gate")) - :msg "break 2 Code Gate subroutines"}])) - -(define-card "Savoir-faire" - {:abilities [{:cost [:credit 2] - :once :per-turn - :req (req (not (install-locked? state side))) - :msg (msg "install " (:title target)) - :prompt "Choose a program to install from your grip" - :choices {:req #(and (program? %) - (in-hand? %))} - :effect (effect (runner-install target))}]}) - -(define-card "Scheherazade" - {:abilities [{:label "Install and host a program from Grip" - :effect (effect (resolve-ability - {:cost [:click 1] - :prompt "Choose a program to install on Scheherazade from your grip" - :choices {:req #(and (program? %) - (runner-can-install? state side % false) - (in-hand? %))} - :msg (msg "host " (:title target) " and gain 1 [Credits]") - :effect (effect (runner-install target {:host-card card}) (gain-credits 1))} - card nil))} - {:label "Host an installed program" - :prompt "Choose a program to host on Scheherazade" :priority 2 - :choices {:req #(and (program? %) - (installed? %))} - :msg (msg "host " (:title target) " and gain 1 [Credits]") - :effect (req (when (host state side card target) - (gain-credits state side 1)))}]}) - -(define-card "Self-modifying Code" - {:abilities [{:req (req (not (install-locked? state side))) - :effect (req (wait-for (trash state side card {:cause :ability-cost}) - (continue-ability state side - {:prompt "Choose a program to install" - :msg (req (if (not= target "No install") - (str "install " (:title target)) - (str "shuffle their Stack"))) - :priority true - :choices (req (cancellable - (conj (vec (sort-by :title (filter program? - (:deck runner)))) - "No install"))) - :cost [:credit 2] - :effect (req (trigger-event state side :searched-stack nil) - (shuffle! state side :deck) - (when (not= target "No install") - (runner-install state side target)))} card nil)))}]}) - -(define-card "Sharpshooter" - (auto-icebreaker ["Destroyer"] - {:abilities [{:label "[Trash]: Break any number of Destroyer subroutines" - :msg "break any number of Destroyer subroutines" - :effect (effect (trash card {:cause :ability-cost}))} - (strength-pump 1 2)]})) - -(define-card "Shiv" - (break-and-enter "Sentry")) - -(define-card "Sneakdoor Beta" - {:abilities [{:cost [:click 1] - :msg "make a run on Archives" - :makes-run true - :effect (effect (make-run :archives - {:req (req (= target :archives)) - :successful-run - {:silent (req true) - :effect (req (swap! state assoc-in [:run :server] [:hq]) - ; remove the :req from the run-effect, so that other cards that replace - ; access don't use Sneakdoor's req. (Security Testing, Ash 2X). - (swap! state dissoc-in [:run :run-effect :req]) - (trigger-event state :corp :no-action) - (system-msg state side - (str "uses Sneakdoor Beta to make a successful run on HQ")))}} - card))}]}) - -(define-card "Snitch" - {:abilities [{:once :per-run :req (req (and (ice? current-ice) (not (rezzed? current-ice)))) - :async true - :effect (req (wait-for (expose state side current-ice) + :msg (msg "reveal " (:title target)) + :effect (req (if-let [guess (get-in card [:special :rng-guess])] + (if (installed? target) + ;; Do not trigger on installed cards (can't "reveal" an installed card per UFAQ) + (do (toast state :runner "Installed cards cannot be revealed, so RNG Key does not pay out." "info") + (effect-completed state side eid)) + (if (or (= guess (:cost target)) + (= guess (:advancementcost target))) + (continue-ability state side + {:prompt "Choose RNG Key award" + :choices ["Gain 3 [Credits]" "Draw 2 cards"] + :async true + :effect (req (if (= target "Draw 2 cards") + (do (system-msg state :runner "uses RNG Key to draw 2 cards") + (draw state :runner eid 2 nil)) + (do (system-msg state :runner "uses RNG Key to gain 3 [Credits]") + (gain-credits state :runner 3) + (effect-completed state side eid))))} + card nil) + (effect-completed state side eid))) + (effect-completed state side eid)))} + :post-access-card {:effect (effect (update! (assoc-in card [:special :rng-guess] nil)))} + :successful-run {:req (req (and (#{:hq :rd} target) + (first-event? state :runner :successful-run #{[:hq] [:rd]}))) + :optional {:prompt "Fire RNG Key?" + :autoresolve (get-autoresolve :auto-fire) + :yes-ability {:prompt "Guess a number" + :choices {:number (req 20)} + :msg (msg "guess " target) + :effect (effect (update! (assoc-in card [:special :rng-guess] target)))}}}} + :abilities [(set-autoresolve :auto-fire "RNG Key")]} + + "Rook" + {:abilities [{:cost [:click 1] + :effect (req (let [r (get-card state card) + hosted? (ice? (:host r)) + icepos (ice-index state (get-card state (:host r)))] + (resolve-ability + state side + {:prompt (if hosted? + (msg "Host Rook on a piece of ICE protecting this server or at position " + icepos " of a different server") + (msg "Host Rook on a piece of ICE protecting any server")) + :choices {:req #(if hosted? + (and (or (= (:zone %) (:zone (:host r))) + (= (ice-index state %) icepos)) + (= (last (:zone %)) :ices) + (ice? %) + (can-host? %) + (not-any? (fn [c] (has? c :subtype "Caïssa")) (:hosted %))) + (and (ice? %) + (can-host? %) + (= (last (:zone %)) :ices) + (not-any? (fn [c] (has? c :subtype "Caïssa")) (:hosted %))))} + :msg (msg "host it on " (card-str state target)) + :effect (effect (host target card))} card nil)))}] + :events {:pre-rez-cost {:req (req (= (:zone (:host card)) (:zone target))) + :effect (effect (rez-cost-bonus 2))}}} + + "Sadyojata" + (deva "Sadyojata") + + "Sage" + (ancient-greek-breaker "sage" [{:cost [:credit 2] + :req (req (or (has-subtype? current-ice "Barrier") + (has-subtype? current-ice "Code Gate"))) + :msg "break 1 Code Gate or Barrier subroutine"}]) + + "Sahasrara" + {:recurring 2 + :interactions {:pay-credits {:req (req (and (= :runner-install (:source-type eid)) + (program? target))) + :type :recurring}}} + + "Saker" + (auto-icebreaker ["Barrier"] + {:abilities [(break-sub 1 1 "Barrier") + (strength-pump 2 2) + {:label "Derez a Barrier and return Saker to your Grip" + :cost [:credit 2] + :req (req (and (rezzed? current-ice) (has-subtype? current-ice "Barrier"))) + :msg (msg "derez " (:title current-ice) " and return Saker to their Grip") + :effect (effect (derez current-ice) + (move card :hand))}]}) + + "Savant" + (ancient-greek-breaker "savant" [{:cost [:credit 2] :req (req (has-subtype? current-ice "Sentry")) + :msg "break 1 Sentry subroutine"} + {:cost [:credit 2] :req (req (has-subtype? current-ice "Code Gate")) + :msg "break 2 Code Gate subroutines"}]) + + "Savoir-faire" + {:abilities [{:cost [:credit 2] + :once :per-turn + :req (req (not (install-locked? state side))) + :msg (msg "install " (:title target)) + :prompt "Choose a program to install from your grip" + :choices {:req #(and (program? %) + (in-hand? %))} + :effect (effect (runner-install target))}]} + + "Scheherazade" + {:abilities [{:label "Install and host a program from Grip" + :effect (effect (resolve-ability + {:cost [:click 1] + :prompt "Choose a program to install on Scheherazade from your grip" + :choices {:req #(and (program? %) + (runner-can-install? state side % false) + (in-hand? %))} + :msg (msg "host " (:title target) " and gain 1 [Credits]") + :effect (effect (runner-install target {:host-card card}) (gain-credits 1))} + card nil))} + {:label "Host an installed program" + :prompt "Choose a program to host on Scheherazade" :priority 2 + :choices {:req #(and (program? %) + (installed? %))} + :msg (msg "host " (:title target) " and gain 1 [Credits]") + :effect (req (when (host state side card target) + (gain-credits state side 1)))}]} + + "Self-modifying Code" + {:abilities [{:req (req (not (install-locked? state side))) + :effect (req (wait-for (trash state side card {:cause :ability-cost}) + (continue-ability state side + {:prompt "Choose a program to install" + :msg (req (if (not= target "No install") + (str "install " (:title target)) + (str "shuffle their Stack"))) + :priority true + :choices (req (cancellable + (conj (vec (sort-by :title (filter program? + (:deck runner)))) + "No install"))) + :cost [:credit 2] + :effect (req (trigger-event state side :searched-stack nil) + (shuffle! state side :deck) + (when (not= target "No install") + (runner-install state side target)))} card nil)))}]} + + "Sharpshooter" + (auto-icebreaker ["Destroyer"] + {:abilities [{:label "[Trash]: Break any number of Destroyer subroutines" + :msg "break any number of Destroyer subroutines" + :effect (effect (trash card {:cause :ability-cost}))} + (strength-pump 1 2)]}) + + "Shiv" + (break-and-enter "Sentry") + + "Sneakdoor Beta" + {:abilities [{:cost [:click 1] + :msg "make a run on Archives" + :makes-run true + :effect (effect (make-run :archives + {:req (req (= target :archives)) + :successful-run + {:silent (req true) + :effect (req (swap! state assoc-in [:run :server] [:hq]) + ; remove the :req from the run-effect, so that other cards that replace + ; access don't use Sneakdoor's req. (Security Testing, Ash 2X). + (swap! state dissoc-in [:run :run-effect :req]) + (trigger-event state :corp :no-action) + (system-msg state side + (str "uses Sneakdoor Beta to make a successful run on HQ")))}} + card))}]} + + "Snitch" + {:abilities [{:once :per-run :req (req (and (ice? current-ice) (not (rezzed? current-ice)))) + :async true + :effect (req (wait-for (expose state side current-ice) + (continue-ability + state side + {:optional {:prompt "Jack out?" + :yes-ability {:msg "jack out" + :effect (effect (jack-out nil))} + :no-ability {:msg "continue the run"}}} + card nil)))}]} + + "Snowball" + (auto-icebreaker ["Barrier"] + {:abilities [{:cost [:credit 1] :msg "break 1 Barrier subroutine" + :effect (effect (pump card 1 :all-run))} + (strength-pump 1 1)]}) + + "Spike" + (break-and-enter "Barrier") + + "Stargate" + {:abilities [{:cost [:click 1] + :once :per-turn + :msg "make a run on R&D" + :makes-run true + :effect (effect (make-run + :rd + {:req (req (= target :rd)) + :replace-access + {:msg (msg "reveal " + (->> + (:deck corp) + (take 3) + (map :title) + (join ", "))) + :mandatory true + :effect + (effect + (reveal (take 3 (:deck corp))) (continue-ability - state side - {:optional {:prompt "Jack out?" - :yes-ability {:msg "jack out" - :effect (effect (jack-out nil))} - :no-ability {:msg "continue the run"}}} - card nil)))}]}) - -(define-card "Snowball" - (auto-icebreaker ["Barrier"] - {:abilities [{:cost [:credit 1] :msg "break 1 Barrier subroutine" - :effect (effect (pump card 1 :all-run))} - (strength-pump 1 1)]})) - -(define-card "Spike" - (break-and-enter "Barrier")) - -(define-card "Stargate" - {:abilities [{:cost [:click 1] - :once :per-turn - :msg "make a run on R&D" - :makes-run true - :effect (effect (make-run - :rd - {:req (req (= target :rd)) - :replace-access - {:msg (msg "reveal " - (->> - (:deck corp) - (take 3) - (map :title) - (join ", "))) - :mandatory true - :effect - (effect - (reveal (take 3 (:deck corp))) - (continue-ability - {:prompt "Choose a card to trash" - :msg (msg "trash " (:title target)) - :not-distinct true - :choices (req (take 3 (:deck corp))) - :effect (effect (trash :runner (assoc target :seen true)))} - card nil))}} - card))}]}) - -(define-card "Study Guide" - {:abilities [(break-sub 1 1 "Code Gate") - {:cost [:credit 2] :msg "place 1 power counter" - :effect (effect (add-counter card :power 1) - (update-breaker-strength card))}] - :strength-bonus (req (get-counters card :power))}) - -(define-card "Sūnya" - {:implementation "Adding power counter is manual" - :abilities [{:label "Place 1 power counter on Sūnya" - :ability-type :manual-state - :effect (effect (add-counter card :power 1) - (system-msg (str "places 1 power counter on Sūnya")) - (update-breaker-strength card))} - (break-sub 2 1 "Sentry")] - :strength-bonus (req (get-counters card :power))}) - -(define-card "Surfer" - (letfn [(surf [state cice] - {:prompt (msg "Choose an ICE before or after " (:title cice)) - :choices {:req #(and (ice? %) - (= (:zone %) (:zone cice)) - (= 1 (abs (- (ice-index state %) - (ice-index state cice)))))} - :effect (req (let [tgtndx (ice-index state target) - cidx (ice-index state cice)] - (system-msg state :runner (str "uses Surfer to swap " - (card-str state cice) - " and " - (card-str state (nth run-ices tgtndx)))) - (swap! state update-in (cons :corp (:zone cice)) - #(assoc % tgtndx cice)) - (swap! state update-in (cons :corp (:zone cice)) - #(assoc % cidx target)) - (swap! state update-in [:run] #(assoc % :position (inc tgtndx))) - (update-all-ice state side) - (trigger-event state side :approach-ice current-ice)))})] - {:abilities [{:cost [:credit 2] - :msg "swap a piece of Barrier ICE" - :req (req (and (:run @state) - (rezzed? current-ice) - (has-subtype? current-ice "Barrier"))) - :label "Swap the Barrier ICE currently being encountered with a piece of ICE directly before or after it" - :effect (effect (resolve-ability (surf state current-ice) card nil))}]})) - -(define-card "Switchblade" - (auto-icebreaker ["Sentry"] - {:implementation "Stealth credit restriction not enforced" - :abilities [(break-sub 1 0 "Sentry") - (strength-pump 1 7)]})) - -(define-card "Takobi" - {:implementation "Adding power counter is manual" - :abilities [{:label "Add 1 power counter" - :effect (effect (add-counter card :power 1) - (system-msg "adds a power counter to Takobi"))} - {:req (req (and (:run @state) - (rezzed? current-ice) - (>= (get-counters card :power) 2))) - :counter-cost [:power 2] - :label "Increase non-AI icebreaker strength by +3 until end of encounter" - :prompt "Choose an installed non-AI icebreaker" - :choices {:req #(and (has-subtype? % "Icebreaker") - (not (has-subtype? % "AI")) - (installed? %))} - :msg (msg "add +3 strength to " (:title target) " for remainder of encounter") - :effect (effect (pump target 3 :encounter))}]}) - -(define-card "Tapwrm" - (let [ability {:label "Gain [Credits] (start of turn)" - :msg (msg "gain " (quot (:credit corp) 5) " [Credits]") + {:prompt "Choose a card to trash" + :msg (msg "trash " (:title target)) + :not-distinct true + :choices (req (take 3 (:deck corp))) + :effect (effect (trash :runner (assoc target :seen true)))} + card nil))}} + card))}]} + + "Study Guide" + {:abilities [(break-sub 1 1 "Code Gate") + {:cost [:credit 2] :msg "place 1 power counter" + :effect (effect (add-counter card :power 1) + (update-breaker-strength card))}] + :strength-bonus (req (get-counters card :power))} + + "Sūnya" + {:implementation "Adding power counter is manual" + :abilities [{:label "Place 1 power counter on Sūnya" + :ability-type :manual-state + :effect (effect (add-counter card :power 1) + (system-msg (str "places 1 power counter on Sūnya")) + (update-breaker-strength card))} + (break-sub 2 1 "Sentry")] + :strength-bonus (req (get-counters card :power))} + + "Surfer" + (letfn [(surf [state cice] + {:prompt (msg "Choose an ICE before or after " (:title cice)) + :choices {:req #(and (ice? %) + (= (:zone %) (:zone cice)) + (= 1 (abs (- (ice-index state %) + (ice-index state cice)))))} + :effect (req (let [tgtndx (ice-index state target) + cidx (ice-index state cice)] + (system-msg state :runner (str "uses Surfer to swap " + (card-str state cice) + " and " + (card-str state (nth run-ices tgtndx)))) + (swap! state update-in (cons :corp (:zone cice)) + #(assoc % tgtndx cice)) + (swap! state update-in (cons :corp (:zone cice)) + #(assoc % cidx target)) + (swap! state update-in [:run] #(assoc % :position (inc tgtndx))) + (update-all-ice state side) + (trigger-event state side :approach-ice current-ice)))})] + {:abilities [{:cost [:credit 2] + :msg "swap a piece of Barrier ICE" + :req (req (and (:run @state) + (rezzed? current-ice) + (has-subtype? current-ice "Barrier"))) + :label "Swap the Barrier ICE currently being encountered with a piece of ICE directly before or after it" + :effect (effect (resolve-ability (surf state current-ice) card nil))}]}) + + "Switchblade" + (auto-icebreaker ["Sentry"] + {:implementation "Stealth credit restriction not enforced" + :abilities [(break-sub 1 0 "Sentry") + (strength-pump 1 7)]}) + + "Takobi" + {:implementation "Adding power counter is manual" + :abilities [{:label "Add 1 power counter" + :effect (effect (add-counter card :power 1) + (system-msg "adds a power counter to Takobi"))} + {:req (req (and (:run @state) + (rezzed? current-ice) + (>= (get-counters card :power) 2))) + :counter-cost [:power 2] + :label "Increase non-AI icebreaker strength by +3 until end of encounter" + :prompt "Choose an installed non-AI icebreaker" + :choices {:req #(and (has-subtype? % "Icebreaker") + (not (has-subtype? % "AI")) + (installed? %))} + :msg (msg "add +3 strength to " (:title target) " for remainder of encounter") + :effect (effect (pump target 3 :encounter))}]} + + "Tapwrm" + (let [ability {:label "Gain [Credits] (start of turn)" + :msg (msg "gain " (quot (:credit corp) 5) " [Credits]") + :once :per-turn + :req (req (:runner-phase-12 @state)) + :effect (effect (gain-credits (quot (:credit corp) 5)))}] + {:req (req (some #{:hq :rd :archives} (:successful-run runner-reg))) + :flags {:drip-economy true} + :abilities [ability] + :events {:runner-turn-begins ability + :purge {:effect (effect (trash card {:cause :purge}))}}}) + + "Torch" + (auto-icebreaker ["Code Gate"] + {:abilities [(break-sub 1 1 "Code Gate") + (strength-pump 1 1)]}) + + "Tracker" + (let [ability {:prompt "Choose a server for Tracker" :choices (req servers) + :msg (msg "target " target) + :req (req (not (:server-target card))) + :effect (effect (update! (assoc card :server-target target)))}] + {:abilities [{:label "Make a run on targeted server" :cost [:click 1 :credit 2] + :req (req (some #(= (:server-target card) %) runnable-servers)) + :msg (msg "make a run on " (:server-target card) ". Prevent the first subroutine that would resolve from resolving") + :effect (effect (make-run (:server-target card) nil card))}] + :events {:runner-turn-begins ability + :runner-turn-ends {:effect (effect (update! (dissoc card :server-target)))}}}) + + "Trope" + {:events {:runner-turn-begins {:effect (effect (add-counter card :power 1))}} + :abilities [{:cost [:click 1] + :label "[Click], remove Trope from the game: Reshuffle cards from Heap back into Stack" + :effect (effect + (move card :rfg) + (resolve-ability + {:show-discard true + :choices {:max (min (get-counters card :power) (count (:discard runner))) + :all true + :req #(and (runner? %) + (in-discard? %))} + :msg (msg "shuffle " (join ", " (map :title targets)) + " into their Stack") + :effect (req (doseq [c targets] (move state side c :deck)) + (shuffle! state side :deck))} + card nil))}]} + + "Trypano" + (let [trash-if-5 (req (when-let [h (get-card state (:host card))] + (if (and (>= (get-virus-counters state card) 5) + (not (and (card-flag? h :untrashable-while-rezzed true) + (rezzed? h)))) + (do (system-msg state :runner (str "uses Trypano to trash " (card-str state h))) + (unregister-events state side card) + (trash state :runner eid h nil)) + (effect-completed state side eid))))] + {:hosting {:req #(and (ice? %) (can-host? %))} + :effect trash-if-5 + :abilities [(set-autoresolve :auto-accept "add virus counter to Trypano")] + :events {:runner-turn-begins + {:optional {:prompt (msg "Place a virus counter on Trypano?") + :autoresolve (get-autoresolve :auto-accept) + :yes-ability {:effect (req (system-msg state :runner "places a virus counter on Trypano") + (add-counter state side card :virus 1))}}} + :counter-added {:async true + :effect trash-if-5} + :card-moved {:effect trash-if-5 + :async true} + :runner-install {:effect trash-if-5 + :async true}}}) + + "Tycoon" + (auto-icebreaker ["Barrier"] + {:abilities [(break-sub 1 2 "Barrier" (effect (update! (assoc-in card [:special :tycoon-used] true)))) + (strength-pump 2 3)] + :events (let [give-credits {:req (req (get-in card [:special :tycoon-used])) + :msg "give the Corp 2 [Credits]" + :effect (effect (update! (dissoc-in card [:special :tycoon-used])) + (gain-credits :corp 2))}] + {:pass-ice give-credits + :run-ends give-credits})}) + + "Upya" + {:implementation "Power counters added automatically" + :events {:successful-run {:silent (req true) + :req (req (= target :rd)) + :effect (effect (add-counter card :power 1))}} + :abilities [{:cost [:click 1] + :counter-cost [:power 3] :once :per-turn - :req (req (:runner-phase-12 @state)) - :effect (effect (gain-credits (quot (:credit corp) 5)))}] - {:req (req (some #{:hq :rd :archives} (:successful-run runner-reg))) - :flags {:drip-economy true} - :abilities [ability] - :events {:runner-turn-begins ability - :purge {:effect (effect (trash card {:cause :purge}))}}})) - -(define-card "Torch" - (auto-icebreaker ["Code Gate"] - {:abilities [(break-sub 1 1 "Code Gate") - (strength-pump 1 1)]})) - -(define-card "Tracker" - (let [ability {:prompt "Choose a server for Tracker" :choices (req servers) - :msg (msg "target " target) - :req (req (not (:server-target card))) - :effect (effect (update! (assoc card :server-target target)))}] - {:abilities [{:label "Make a run on targeted server" :cost [:click 1 :credit 2] - :req (req (some #(= (:server-target card) %) runnable-servers)) - :msg (msg "make a run on " (:server-target card) ". Prevent the first subroutine that would resolve from resolving") - :effect (effect (make-run (:server-target card) nil card))}] - :events {:runner-turn-begins ability - :runner-turn-ends {:effect (effect (update! (dissoc card :server-target)))}}})) - -(define-card "Trope" - {:events {:runner-turn-begins {:effect (effect (add-counter card :power 1))}} - :abilities [{:cost [:click 1] - :label "[Click], remove Trope from the game: Reshuffle cards from Heap back into Stack" - :effect (effect - (move card :rfg) - (resolve-ability - {:show-discard true - :choices {:max (min (get-counters card :power) (count (:discard runner))) - :all true - :req #(and (runner? %) - (in-discard? %))} - :msg (msg "shuffle " (join ", " (map :title targets)) - " into their Stack") - :effect (req (doseq [c targets] (move state side c :deck)) - (shuffle! state side :deck))} - card nil))}]}) - -(define-card "Trypano" - (let [trash-if-5 (req (when-let [h (get-card state (:host card))] - (if (and (>= (get-virus-counters state card) 5) - (not (and (card-flag? h :untrashable-while-rezzed true) - (rezzed? h)))) - (do (system-msg state :runner (str "uses Trypano to trash " (card-str state h))) - (unregister-events state side card) - (trash state :runner eid h nil)) - (effect-completed state side eid))))] - {:hosting {:req #(and (ice? %) (can-host? %))} - :effect trash-if-5 - :abilities [(set-autoresolve :auto-accept "add virus counter to Trypano")] - :events {:runner-turn-begins - {:optional {:prompt (msg "Place a virus counter on Trypano?") - :autoresolve (get-autoresolve :auto-accept) - :yes-ability {:effect (req (system-msg state :runner "places a virus counter on Trypano") - (add-counter state side card :virus 1))}}} - :counter-added {:async true - :effect trash-if-5} - :card-moved {:effect trash-if-5 - :async true} - :runner-install {:effect trash-if-5 - :async true}}})) - -(define-card "Tycoon" - (auto-icebreaker ["Barrier"] - {:abilities [(break-sub 1 2 "Barrier" (effect (update! (assoc-in card [:special :tycoon-used] true)))) - (strength-pump 2 3)] - :events (let [give-credits {:req (req (get-in card [:special :tycoon-used])) - :msg "give the Corp 2 [Credits]" - :effect (effect (update! (dissoc-in card [:special :tycoon-used])) - (gain-credits :corp 2))}] - {:pass-ice give-credits - :run-ends give-credits})})) - -(define-card "Upya" - {:implementation "Power counters added automatically" - :events {:successful-run {:silent (req true) - :req (req (= target :rd)) - :effect (effect (add-counter card :power 1))}} - :abilities [{:cost [:click 1] - :counter-cost [:power 3] - :once :per-turn - :msg "gain [Click][Click]" - :effect (effect (gain :click 2))}]}) - -(define-card "Utae" - (auto-icebreaker ["Code Gate"] - {:abilities [{:label "X [Credits]: Break X Code Gate subroutines" - :once :per-run - :req (req (pos? (:credit runner))) - :prompt "How many credits?" - :choices :credit - :msg (msg "spend " target " [Credits] to break " (quantify target "Code Gate subroutine"))} - {:label "Break 1 Code Gate subroutine (Virtual restriction)" - :req (req (<= 3 (count (filter #(has-subtype? % "Virtual") - (all-active-installed state :runner))))) - :cost [:credit 1] - :msg "break 1 Code Gate subroutine"} - (strength-pump 1 1)]})) - -(define-card "Vamadeva" - (deva "Vamadeva")) - -(define-card "Wari" - (letfn [(prompt-for-subtype [] - {:prompt "Choose a subtype" - :choices ["Barrier" "Code Gate" "Sentry"] - :async true - :effect (req (wait-for (trash state side card {:unpreventable true}) - (continue-ability state side - (expose-and-maybe-bounce target) - card nil)))}) - (expose-and-maybe-bounce [chosen-subtype] - {:choices {:req #(and (ice? %) (not (rezzed? %)))} - :async true - :msg (str "name " chosen-subtype) - :effect (req (wait-for (expose state side target) - (do (if (and async-result - (has-subtype? target chosen-subtype)) - (do (move state :corp target :hand) - (system-msg state :runner - (str "add " (:title target) " to HQ")))) - (effect-completed state side eid))))})] - {:events {:successful-run - {:interactive (req true) - :async true - :req (req (and (= target :hq) - (first-successful-run-on-server? state :hq) - (some #(and (ice? %) (not (rezzed? %))) - (all-installed state :corp)))) - :effect (effect (continue-ability - {:prompt "Use Wari?" - :choices ["Yes" "No"] - :async true - :effect (req (if (= target "Yes") - (continue-ability state side - (prompt-for-subtype) - card nil) - (effect-completed state side eid)))} - card nil))}}})) - -(define-card "Wyrm" - (auto-icebreaker ["All"] - {:abilities [{:cost [:credit 3] - :msg "break 1 subroutine on ICE with 0 or less strength"} - {:cost [:credit 1] - :label "Give -1 strength to current ICE" - :req (req (rezzed? current-ice)) - :msg (msg "give -1 strength to " (:title current-ice)) - :effect (req (update! state side (update-in card [:wyrm-count] (fnil #(+ % 1) 0))) - (update-ice-strength state side current-ice))} - (strength-pump 1 1)] - :events (let [auto-pump (fn [state side eid card targets] - ((:effect breaker-auto-pump) state side eid card targets)) - wy {:effect (effect (update! (dissoc card :wyrm-count)) - (auto-pump eid (get-card state card) targets))}] - {:pre-ice-strength {:req (req (and (same-card? target current-ice) - (:wyrm-count card))) - :effect (req (let [c (:wyrm-count (get-card state card))] - (ice-strength-bonus state side (- c) target) - (auto-pump state side eid card targets)))} - :pass-ice wy - :run-ends wy})})) - -(define-card "Yog.0" - {:abilities [(break-sub 0 1 "Code Gate")]}) - -(define-card "Yusuf" - (khumalo-breaker "barrier")) - -(define-card "ZU.13 Key Master" - (cloud-icebreaker - (auto-icebreaker ["Code Gate"] - {:abilities [(break-sub 1 1 "Code Gate") - (strength-pump 1 1)]}))) + :msg "gain [Click][Click]" + :effect (effect (gain :click 2))}]} + + "Utae" + (auto-icebreaker ["Code Gate"] + {:abilities [{:label "X [Credits]: Break X Code Gate subroutines" + :once :per-run + :req (req (pos? (:credit runner))) + :prompt "How many credits?" + :choices :credit + :msg (msg "spend " target " [Credits] to break " (quantify target "Code Gate subroutine"))} + {:label "Break 1 Code Gate subroutine (Virtual restriction)" + :req (req (<= 3 (count (filter #(has-subtype? % "Virtual") + (all-active-installed state :runner))))) + :cost [:credit 1] + :msg "break 1 Code Gate subroutine"} + (strength-pump 1 1)]}) + + "Vamadeva" + (deva "Vamadeva") + + "Wari" + (letfn [(prompt-for-subtype [] + {:prompt "Choose a subtype" + :choices ["Barrier" "Code Gate" "Sentry"] + :async true + :effect (req (wait-for (trash state side card {:unpreventable true}) + (continue-ability state side + (expose-and-maybe-bounce target) + card nil)))}) + (expose-and-maybe-bounce [chosen-subtype] + {:choices {:req #(and (ice? %) (not (rezzed? %)))} + :async true + :msg (str "name " chosen-subtype) + :effect (req (wait-for (expose state side target) + (do (if (and async-result + (has-subtype? target chosen-subtype)) + (do (move state :corp target :hand) + (system-msg state :runner + (str "add " (:title target) " to HQ")))) + (effect-completed state side eid))))})] + {:events {:successful-run + {:interactive (req true) + :async true + :req (req (and (= target :hq) + (first-successful-run-on-server? state :hq) + (some #(and (ice? %) (not (rezzed? %))) + (all-installed state :corp)))) + :effect (effect (continue-ability + {:prompt "Use Wari?" + :choices ["Yes" "No"] + :async true + :effect (req (if (= target "Yes") + (continue-ability state side + (prompt-for-subtype) + card nil) + (effect-completed state side eid)))} + card nil))}}}) + + "Wyrm" + (auto-icebreaker ["All"] + {:abilities [{:cost [:credit 3] + :msg "break 1 subroutine on ICE with 0 or less strength"} + {:cost [:credit 1] + :label "Give -1 strength to current ICE" + :req (req (rezzed? current-ice)) + :msg (msg "give -1 strength to " (:title current-ice)) + :effect (req (update! state side (update-in card [:wyrm-count] (fnil #(+ % 1) 0))) + (update-ice-strength state side current-ice))} + (strength-pump 1 1)] + :events (let [auto-pump (fn [state side eid card targets] + ((:effect breaker-auto-pump) state side eid card targets)) + wy {:effect (effect (update! (dissoc card :wyrm-count)) + (auto-pump eid (get-card state card) targets))}] + {:pre-ice-strength {:req (req (and (same-card? target current-ice) + (:wyrm-count card))) + :effect (req (let [c (:wyrm-count (get-card state card))] + (ice-strength-bonus state side (- c) target) + (auto-pump state side eid card targets)))} + :pass-ice wy + :run-ends wy})}) + + "Yog.0" + {:abilities [(break-sub 0 1 "Code Gate")]} + + "Yusuf" + (khumalo-breaker "barrier") + + "ZU.13 Key Master" + (cloud-icebreaker + (auto-icebreaker ["Code Gate"] + {:abilities [(break-sub 1 1 "Code Gate") + (strength-pump 1 1)]}))}) diff --git a/src/clj/game/cards/resources.clj b/src/clj/game/cards/resources.clj index 0fa392f728..d74ba2584e 100644 --- a/src/clj/game/cards/resources.clj +++ b/src/clj/game/cards/resources.clj @@ -1,7 +1,7 @@ (ns game.cards.resources (:require [game.core :refer :all] [game.core.eid :refer [make-eid effect-completed]] - [game.core.card-defs :refer [card-def define-card]] + [game.core.card-defs :refer [card-def]] [game.utils :refer :all] [game.macros :refer [effect req msg wait-for continue-ability]] [clojure.string :refer [split-lines split join lower-case includes? starts-with?]] @@ -70,2413 +70,2414 @@ :abilities [(assoc ability :req ability-req)]}))) ;; Card definitions -(define-card "Aaron Marrón" - (let [am {:effect (effect (add-counter card :power 2) - (system-msg :runner (str "places 2 power counters on Aaron Marrón")))}] - {:abilities [{:counter-cost [:power 1] - :msg "remove 1 tag and draw 1 card" +(def card-definitions + {"Aaron Marrón" + (let [am {:effect (effect (add-counter card :power 2) + (system-msg :runner (str "places 2 power counters on Aaron Marrón")))}] + {:abilities [{:counter-cost [:power 1] + :msg "remove 1 tag and draw 1 card" + :async true + :effect (effect (lose-tags 1) (draw eid 1 nil))}] + :events {:agenda-scored am :agenda-stolen am}}) + + "Access to Globalsec" + {:in-play [:link 1]} + + "Activist Support" + {:events + {:corp-turn-begins {:async true + :effect (req (if (zero? (count-tags state)) + (do (gain-tags state :runner eid 1) + (system-msg state :runner (str "uses " (:title card) " to take 1 tag"))) + (effect-completed state :runner eid)))} + :runner-turn-begins {:async true + :effect (req (if (not (has-bad-pub? state)) + (do (gain-bad-publicity state :corp eid 1) + (system-msg state :runner + (str "uses " (:title card) " to give the corp 1 bad publicity"))) + (effect-completed state :runner eid)))}}} + + "Adjusted Chronotype" + {:events {:runner-loss {:req (req (and (some #{:click} target) + (let [click-losses (count (filter #(= :click %) (mapcat first (turn-events state side :runner-loss))))] + (or (= 1 click-losses) + (and (= 2 click-losses) + (has-flag? state side :persistent :genetics-trigger-twice)))))) + :msg "gain [Click]" + :effect (effect (gain :runner :click 1))}}} + + "Aeneas Informant" + {:events {:no-trash {:req (req (and (:trash target) + (not= (first (:zone target)) :discard))) + :optional {:autoresolve (get-autoresolve :auto-reveal-and-gain) + :prompt "Use Aeneas Informant?" + :yes-ability {:msg (msg (str "gain 1 [Credits]" + (when-not (installed? target) + (str " and reveal " (:title target))))) + :effect (effect (gain-credits 1))}}}} + :abilities [(set-autoresolve :auto-reveal-and-gain "Aeneas Informant")]} + + "Aesop's Pawnshop" + {:flags {:runner-phase-12 (req (>= (count (all-installed state :runner)) 2))} + :abilities [{:effect (req (resolve-ability + state side + {:msg (msg "trash " (:title target) " and gain 3 [Credits]") + :choices {:req #(and (runner? %) + (installed? %) + (not (same-card? % card)))} + :effect (effect (gain-credits 3) + (trash target {:unpreventable true}))} + card nil))}]} + + "Akshara Sareen" + {:in-play [:click-per-turn 1] + :msg "give each player 1 additional [Click] to spend during their turn" + :effect (effect (gain :corp :click-per-turn 1)) + :leave-play (effect (lose :corp :click-per-turn 1))} + + "Algo Trading" + {:flags {:runner-phase-12 (req (pos? (:credit runner)))} + :abilities [{:label "Move up to 3 [Credit] from credit pool to Algo Trading" + :prompt "Choose how many [Credit] to move" :once :per-turn + :choices {:number (req (min (:credit runner) 3))} + :effect (effect (lose-credits target) + (add-counter card :credit target)) + :msg (msg "move " target " [Credit] to Algo Trading")} + {:label "Take all credits from Algo Trading" + :cost [:click 1] + :msg (msg "trash it and gain " (get-counters card :credit) " [Credits]") + :effect (effect (gain-credits (get-counters card :credit)) + (trash card {:cause :ability-cost}))}] + :events {:runner-turn-begins {:req (req (>= (get-counters card :credit) 6)) + :effect (effect (add-counter card :credit 2) + (system-msg (str "adds 2 [Credit] to Algo Trading")))}}} + + "All-nighter" + {:abilities [{:cost [:click 1] + :effect (effect (trash card {:cause :ability-cost}) + (gain :click 2)) + :msg "gain [Click][Click]"}]} + + "Always Be Running" + {:implementation "Run requirement not enforced" + :events {:runner-turn-begins + {:effect (req (toast state :runner "Reminder: Always Be Running requires a run on the first click" "info"))}} + :abilities [{:once :per-turn + :cost [:click 2] + :msg (msg "break 1 subroutine")}]} + + "Angel Arena" + {:prompt "How many power counters?" + :choices :credit + :msg (msg "add " target " power counters") + :effect (effect (add-counter card :power target)) + :abilities [{:counter-cost [:power 1] + :msg "look at the top card of Stack" + :effect (req (when (zero? (get-counters (get-card state card) :power)) + (trash state :runner card {:unpreventable true}))) + :optional {:prompt (msg "Add " (:title (first (:deck runner))) " to bottom of Stack?") + :yes-ability {:msg "add the top card of Stack to the bottom" + :effect (req (move state side (first (:deck runner)) :deck))}}}]} + + "Armitage Codebusting" + {:data {:counter {:credit 12}} + :abilities [{:cost [:click 1] + :counter-cost [:credit 2] + :msg "gain 2 [Credits]" + :effect (req (gain-credits state :runner 2) + (when (zero? (get-counters (get-card state card) :credit)) + (trash state :runner card {:unpreventable true})))}]} + + "Artist Colony" + {:abilities [{:prompt "Choose a card to install" + :msg (msg "install " (:title target)) + :req (req (not (install-locked? state side))) + :cost [:forfeit] + :choices (req (cancellable (filter #(not (event? %)) (:deck runner)) :sorted)) + :effect (effect (trigger-event :searched-stack nil) + (shuffle! :deck) + (runner-install target))}]} + + "Assimilator" + {:abilities [{:label "Turn a facedown card faceup" + :cost [:click 2] + :prompt "Select a facedown installed card" + :choices {:req #(and (facedown? %) + (installed? %) + (runner? %))} + :effect (req (if (or (event? target) + (and (has-subtype? target "Console") + (some #(has-subtype? % "Console") (all-active-installed state :runner)))) + ;; Consoles and events are immediately unpreventably trashed. + (trash state side target {:unpreventable true}) + ;; Other cards are moved to rig and have events wired. + (flip-faceup state side target))) + :msg (msg "turn " (:title target) " faceup")}]} + + "\"Baklan\" Bochkin" + {:implementation "Encounter effect is manual." + :abilities [{:label "Place 1 power counter" + :once :per-run + :msg (msg "places 1 power counter on " (:title card)) + :effect (effect (add-counter card :power 1))} + {:label "[Trash]: Derez a piece of ice currently being encountered" + :msg "derez a piece of ice currently being encountered and take 1 tag" + :req (req (and current-ice + (rezzed? current-ice) + (<= (get-strength current-ice) (get-counters (get-card state card) :power)))) + :effect (effect (trash card {:cause :ability-cost}) + (derez current-ice) + (gain-tags eid 1))}]} + + "Bank Job" + {:data {:counter {:credit 8}} + :events {:successful-run + {:silent (req true) + :req (req (is-remote? (:server run))) + :effect (req (let [bj (get-card state card)] + (when-not (:replace-access (get-in @state [:run :run-effect])) + (swap! state assoc-in [:run :run-effect :replace-access] + {:effect (req (if (> (count (filter #(= (:title %) "Bank Job") (all-active-installed state :runner))) 1) + (resolve-ability + state side + {:prompt "Select a copy of Bank Job to use" + :choices {:req #(and (installed? %) (= (:title %) "Bank Job"))} + :effect (req (let [c target + creds (get-counters (get-card state c) :credit)] + (resolve-ability + state side + {:prompt "How many Bank Job credits?" + :choices {:number (req (get-counters (get-card state c) :credit))} + :msg (msg "gain " target " [Credits]") + :effect (req (gain-credits state side target) + (set-prop state side c :counter {:credit (- creds target)}) + (when (not (pos? (get-counters (get-card state c) :credit))) + (trash state side c {:unpreventable true})))} + card nil)))} + bj nil) + (resolve-ability + state side + {:prompt "How many Bank Job credits?" + :choices {:number (req (get-counters (get-card state card) :credit))} + :msg (msg "gain " target " [Credits]") + :effect (req (let [creds (get-counters (get-card state card) :credit)] + (gain-credits state side target) + (set-prop state side card :counter {:credit (- creds target)}) + (when (not (pos? (get-counters (get-card state card) :credit))) + (trash state side card {:unpreventable true}))))} + bj nil)))}))))}}} + + "Bazaar" + (letfn [(hardware-and-in-hand? [target runner] + (and (hardware? target) + (some #(= (:title %) (:title target)) (:hand runner))))] + {:events + {:runner-install + {:interactive (req (hardware-and-in-hand? target runner)) + :silent (req (not (hardware-and-in-hand? target runner))) + :async true + :req (req (and (hardware? target) (= [:hand] (:previous-zone target)))) + :effect (req (let [hw (:title target)] + (continue-ability state side + {:optional {:req (req (some #(when (= (:title %) hw) %) (:hand runner))) + :prompt (msg "Install another copy of " hw "?") + :msg (msg "install another copy of " hw) + :yes-ability {:async true + :effect (req (if-let [c (some #(when (= (:title %) hw) %) + (:hand runner))] + (runner-install state side eid c nil)))}}} card nil)))}}}) + + "Beach Party" + {:in-play [:hand-size 5] + :events {:runner-turn-begins {:msg "lose [Click]" + :effect (effect (lose :click 1))}}} + + "Beth Kilrain-Chang" + (let [ability {:once :per-turn + :label "Gain 1 [Credits], draw 1 card, or gain [Click] (start of turn)" + :req (req (:runner-phase-12 @state)) :async true - :effect (effect (lose-tags 1) (draw eid 1 nil))}] - :events {:agenda-scored am :agenda-stolen am}})) - -(define-card "Access to Globalsec" - {:in-play [:link 1]}) - -(define-card "Activist Support" - {:events - {:corp-turn-begins {:async true - :effect (req (if (zero? (count-tags state)) - (do (gain-tags state :runner eid 1) - (system-msg state :runner (str "uses " (:title card) " to take 1 tag"))) - (effect-completed state :runner eid)))} - :runner-turn-begins {:async true - :effect (req (if (not (has-bad-pub? state)) - (do (gain-bad-publicity state :corp eid 1) - (system-msg state :runner - (str "uses " (:title card) " to give the corp 1 bad publicity"))) - (effect-completed state :runner eid)))}}}) - -(define-card "Adjusted Chronotype" - {:events {:runner-loss {:req (req (and (some #{:click} target) - (let [click-losses (count (filter #(= :click %) (mapcat first (turn-events state side :runner-loss))))] - (or (= 1 click-losses) - (and (= 2 click-losses) - (has-flag? state side :persistent :genetics-trigger-twice)))))) - :msg "gain [Click]" - :effect (effect (gain :runner :click 1))}}}) - -(define-card "Aeneas Informant" - {:events {:no-trash {:req (req (and (:trash target) - (not= (first (:zone target)) :discard))) - :optional {:autoresolve (get-autoresolve :auto-reveal-and-gain) - :prompt "Use Aeneas Informant?" - :yes-ability {:msg (msg (str "gain 1 [Credits]" - (when-not (installed? target) - (str " and reveal " (:title target))))) - :effect (effect (gain-credits 1))}}}} - :abilities [(set-autoresolve :auto-reveal-and-gain "Aeneas Informant")]}) - -(define-card "Aesop's Pawnshop" - {:flags {:runner-phase-12 (req (>= (count (all-installed state :runner)) 2))} - :abilities [{:effect (req (resolve-ability - state side - {:msg (msg "trash " (:title target) " and gain 3 [Credits]") - :choices {:req #(and (runner? %) - (installed? %) - (not (same-card? % card)))} - :effect (effect (gain-credits 3) - (trash target {:unpreventable true}))} - card nil))}]}) - -(define-card "Akshara Sareen" - {:in-play [:click-per-turn 1] - :msg "give each player 1 additional [Click] to spend during their turn" - :effect (effect (gain :corp :click-per-turn 1)) - :leave-play (effect (lose :corp :click-per-turn 1))}) - -(define-card "Algo Trading" - {:flags {:runner-phase-12 (req (pos? (:credit runner)))} - :abilities [{:label "Move up to 3 [Credit] from credit pool to Algo Trading" - :prompt "Choose how many [Credit] to move" :once :per-turn - :choices {:number (req (min (:credit runner) 3))} - :effect (effect (lose-credits target) - (add-counter card :credit target)) - :msg (msg "move " target " [Credit] to Algo Trading")} - {:label "Take all credits from Algo Trading" - :cost [:click 1] - :msg (msg "trash it and gain " (get-counters card :credit) " [Credits]") - :effect (effect (gain-credits (get-counters card :credit)) - (trash card {:cause :ability-cost}))}] - :events {:runner-turn-begins {:req (req (>= (get-counters card :credit) 6)) - :effect (effect (add-counter card :credit 2) - (system-msg (str "adds 2 [Credit] to Algo Trading")))}}}) - -(define-card "All-nighter" - {:abilities [{:cost [:click 1] - :effect (effect (trash card {:cause :ability-cost}) - (gain :click 2)) - :msg "gain [Click][Click]"}]}) - -(define-card "Always Be Running" - {:implementation "Run requirement not enforced" - :events {:runner-turn-begins - {:effect (req (toast state :runner "Reminder: Always Be Running requires a run on the first click" "info"))}} - :abilities [{:once :per-turn - :cost [:click 2] - :msg (msg "break 1 subroutine")}]}) - -(define-card "Angel Arena" - {:prompt "How many power counters?" - :choices :credit - :msg (msg "add " target " power counters") - :effect (effect (add-counter card :power target)) - :abilities [{:counter-cost [:power 1] - :msg "look at the top card of Stack" - :effect (req (when (zero? (get-counters (get-card state card) :power)) - (trash state :runner card {:unpreventable true}))) - :optional {:prompt (msg "Add " (:title (first (:deck runner))) " to bottom of Stack?") - :yes-ability {:msg "add the top card of Stack to the bottom" - :effect (req (move state side (first (:deck runner)) :deck))}}}]}) - -(define-card "Armitage Codebusting" - {:data {:counter {:credit 12}} - :abilities [{:cost [:click 1] - :counter-cost [:credit 2] - :msg "gain 2 [Credits]" - :effect (req (gain-credits state :runner 2) - (when (zero? (get-counters (get-card state card) :credit)) - (trash state :runner card {:unpreventable true})))}]}) - -(define-card "Artist Colony" - {:abilities [{:prompt "Choose a card to install" - :msg (msg "install " (:title target)) - :req (req (not (install-locked? state side))) - :cost [:forfeit] - :choices (req (cancellable (filter #(not (event? %)) (:deck runner)) :sorted)) - :effect (effect (trigger-event :searched-stack nil) - (shuffle! :deck) - (runner-install target))}]}) - -(define-card "Assimilator" - {:abilities [{:label "Turn a facedown card faceup" - :cost [:click 2] - :prompt "Select a facedown installed card" - :choices {:req #(and (facedown? %) - (installed? %) - (runner? %))} - :effect (req (if (or (event? target) - (and (has-subtype? target "Console") - (some #(has-subtype? % "Console") (all-active-installed state :runner)))) - ;; Consoles and events are immediately unpreventably trashed. - (trash state side target {:unpreventable true}) - ;; Other cards are moved to rig and have events wired. - (flip-faceup state side target))) - :msg (msg "turn " (:title target) " faceup")}]}) - -(define-card "\"Baklan\" Bochkin" - {:implementation "Encounter effect is manual." - :abilities [{:label "Place 1 power counter" - :once :per-run - :msg (msg "places 1 power counter on " (:title card)) - :effect (effect (add-counter card :power 1))} - {:label "[Trash]: Derez a piece of ice currently being encountered" - :msg "derez a piece of ice currently being encountered and take 1 tag" - :req (req (and current-ice - (rezzed? current-ice) - (<= (get-strength current-ice) (get-counters (get-card state card) :power)))) - :effect (effect (trash card {:cause :ability-cost}) - (derez current-ice) - (gain-tags eid 1))}]}) - -(define-card "Bank Job" - {:data {:counter {:credit 8}} - :events {:successful-run - {:silent (req true) - :req (req (is-remote? (:server run))) - :effect (req (let [bj (get-card state card)] - (when-not (:replace-access (get-in @state [:run :run-effect])) - (swap! state assoc-in [:run :run-effect :replace-access] - {:effect (req (if (> (count (filter #(= (:title %) "Bank Job") (all-active-installed state :runner))) 1) - (resolve-ability - state side - {:prompt "Select a copy of Bank Job to use" - :choices {:req #(and (installed? %) (= (:title %) "Bank Job"))} - :effect (req (let [c target - creds (get-counters (get-card state c) :credit)] - (resolve-ability - state side - {:prompt "How many Bank Job credits?" - :choices {:number (req (get-counters (get-card state c) :credit))} - :msg (msg "gain " target " [Credits]") - :effect (req (gain-credits state side target) - (set-prop state side c :counter {:credit (- creds target)}) - (when (not (pos? (get-counters (get-card state c) :credit))) - (trash state side c {:unpreventable true})))} - card nil)))} - bj nil) - (resolve-ability - state side - {:prompt "How many Bank Job credits?" - :choices {:number (req (get-counters (get-card state card) :credit))} - :msg (msg "gain " target " [Credits]") - :effect (req (let [creds (get-counters (get-card state card) :credit)] - (gain-credits state side target) - (set-prop state side card :counter {:credit (- creds target)}) - (when (not (pos? (get-counters (get-card state card) :credit))) - (trash state side card {:unpreventable true}))))} - bj nil)))}))))}}}) - -(define-card "Bazaar" - (letfn [(hardware-and-in-hand? [target runner] - (and (hardware? target) - (some #(= (:title %) (:title target)) (:hand runner))))] - {:events - {:runner-install - {:interactive (req (hardware-and-in-hand? target runner)) - :silent (req (not (hardware-and-in-hand? target runner))) - :async true - :req (req (and (hardware? target) (= [:hand] (:previous-zone target)))) - :effect (req (let [hw (:title target)] - (continue-ability state side - {:optional {:req (req (some #(when (= (:title %) hw) %) (:hand runner))) - :prompt (msg "Install another copy of " hw "?") - :msg (msg "install another copy of " hw) - :yes-ability {:async true - :effect (req (if-let [c (some #(when (= (:title %) hw) %) - (:hand runner))] - (runner-install state side eid c nil)))}}} card nil)))}}})) - -(define-card "Beach Party" - {:in-play [:hand-size 5] - :events {:runner-turn-begins {:msg "lose [Click]" - :effect (effect (lose :click 1))}}}) - -(define-card "Beth Kilrain-Chang" - (let [ability {:once :per-turn - :label "Gain 1 [Credits], draw 1 card, or gain [Click] (start of turn)" - :req (req (:runner-phase-12 @state)) + :effect (req (let [c (:credit corp) + b (:title card)] + (cond + ;; gain 1 credit + (<= 5 c 9) + (do (system-msg state side (str "uses " b " to gain 1 [Credits]")) + (gain-credits state side 1) + (effect-completed state side eid)) + ;; draw 1 card + (<= 10 c 14) + (do (system-msg state side (str "uses " b " to draw 1 card")) + (draw state side eid 1 nil)) + ;; gain 1 click + (<= 15 c) + (do (system-msg state side (str "uses " b " to gain [Click]")) + (gain state side :click 1) + (effect-completed state side eid)) + :else (effect-completed state side eid))))}] + {:flags {:drip-economy true} + :abilities [ability] + :events {:runner-turn-begins ability}}) + + "Bhagat" + {:events {:successful-run {:req (req (and (= target :hq) + (first-successful-run-on-server? state :hq))) + :msg "force the Corp to trash the top card of R&D" + :effect (effect (mill :corp))}}} + + "Bio-Modeled Network" + {:interactions {:prevent [{:type #{:net} + :req (req true)}]} + :events {:pre-damage {:req (req (= target :net)) + :effect (effect (update! (assoc card :dmg-amount (nth targets 2))))}} + :abilities [{:msg (msg "prevent " (dec (:dmg-amount card)) " net damage") + :effect (effect (damage-prevent :net (dec (:dmg-amount card))) + (trash card {:cause :ability-cost}))}]} + + "Biometric Spoofing" + {:interactions {:prevent [{:type #{:net :brain :meat} + :req (req true)}]} + :abilities [{:label "[Trash]: Prevent 2 damage" + :msg "prevent 2 damage" + :effect (effect (trash card {:cause :ability-cost}) + (damage-prevent :brain 2) + (damage-prevent :net 2) + (damage-prevent :meat 2))}]} + + "Blockade Runner" + {:abilities [{:cost [:click 2] + :msg "draw 3 cards and shuffle 1 card from their Grip back into their Stack" :async true - :effect (req (let [c (:credit corp) - b (:title card)] - (cond - ;; gain 1 credit - (<= 5 c 9) - (do (system-msg state side (str "uses " b " to gain 1 [Credits]")) - (gain-credits state side 1) - (effect-completed state side eid)) - ;; draw 1 card - (<= 10 c 14) - (do (system-msg state side (str "uses " b " to draw 1 card")) - (draw state side eid 1 nil)) - ;; gain 1 click - (<= 15 c) - (do (system-msg state side (str "uses " b " to gain [Click]")) - (gain state side :click 1) - (effect-completed state side eid)) - :else (effect-completed state side eid))))}] - {:flags {:drip-economy true} - :abilities [ability] - :events {:runner-turn-begins ability}})) - -(define-card "Bhagat" - {:events {:successful-run {:req (req (and (= target :hq) - (first-successful-run-on-server? state :hq))) - :msg "force the Corp to trash the top card of R&D" - :effect (effect (mill :corp))}}}) - -(define-card "Bio-Modeled Network" - {:interactions {:prevent [{:type #{:net} - :req (req true)}]} - :events {:pre-damage {:req (req (= target :net)) - :effect (effect (update! (assoc card :dmg-amount (nth targets 2))))}} - :abilities [{:msg (msg "prevent " (dec (:dmg-amount card)) " net damage") - :effect (effect (damage-prevent :net (dec (:dmg-amount card))) - (trash card {:cause :ability-cost}))}]}) - -(define-card "Biometric Spoofing" - {:interactions {:prevent [{:type #{:net :brain :meat} - :req (req true)}]} - :abilities [{:label "[Trash]: Prevent 2 damage" - :msg "prevent 2 damage" - :effect (effect (trash card {:cause :ability-cost}) - (damage-prevent :brain 2) - (damage-prevent :net 2) - (damage-prevent :meat 2))}]}) - -(define-card "Blockade Runner" - {:abilities [{:cost [:click 2] - :msg "draw 3 cards and shuffle 1 card from their Grip back into their Stack" - :async true - :effect (req (wait-for (draw state side 3 nil) - (resolve-ability state side - {:prompt "Choose a card in your Grip to shuffle back into your Stack" - :choices {:req #(and (in-hand? %) - (runner? %))} - :effect (effect (move target :deck) - (shuffle! :deck))} - card nil)))}]}) - -(define-card "Bloo Moose" - {:flags {:runner-phase-12 (req true)} - :abilities [{:req (req (and (:runner-phase-12 @state) - (not (seq (get-in @state [:runner :locked :discard]))))) - :once :per-turn - :prompt "Choose a card in the Heap to remove from the game and gain 2 [Credits]" - :show-discard true - :choices {:req #(and (in-discard? %) (runner? %))} - :msg (msg "remove " (:title target) " from the game and gain 2 [Credits]") - :effect (effect (gain-credits 2) - (move target :rfg))}]}) - -(define-card "Borrowed Satellite" - {:in-play [:hand-size 1 :link 1]}) - -(define-card "Bug Out Bag" - {:prompt "How many power counters?" - :choices :credit - :msg (msg "add " target " power counters") - :effect (effect (add-counter card :power target)) - :events {:runner-turn-ends {:req (req (zero? (count (:hand runner)))) - :msg (msg "draw " (get-counters card :power) " cards. Bug Out Bag is trashed") - :async true - :effect (req (wait-for (draw state side (get-counters card :power) nil) - (trash state side eid card nil)))}}}) - -(define-card "Caldera" - {:interactions {:prevent [{:type #{:net :brain} - :req (req true)}]} - :abilities [{:cost [:credit 3] - :msg "prevent 1 net damage" - :effect (effect (damage-prevent :net 1))} - {:cost [:credit 3] - :msg "prevent 1 brain damage" - :effect (effect (damage-prevent :brain 1))}]}) - -(define-card "Charlatan" - {:abilities [{:cost [:click 2] - :label "Make a run" - :prompt "Choose a server" - :choices (req runnable-servers) - :msg (msg "make a run on " target) - :effect (effect (make-run target nil card))} - {:label "Pay credits equal to strength of approached rezzed ICE to bypass it" - :once :per-run - :req (req (and (:run @state) (rezzed? current-ice))) - :msg (msg "pay " (:current-strength current-ice) " [Credits] and bypass " (:title current-ice)) - :effect (effect (pay :runner card :credit (:current-strength current-ice)))}]}) - -(define-card "Chatterjee University" - {:abilities [{:cost [:click 1] - :label "Place 1 power counter" - :msg "place 1 power counter on it" - :effect (effect (add-counter card :power 1))} - {:cost [:click 1] - :label "Install a program from your Grip" - :prompt "Select a program to install from your Grip" - :choices {:req #(and (program? %) (in-hand? %))} - :msg (msg "install " (:title target)) - :effect (req (install-cost-bonus state side [:credit (- (get-counters card :power))]) - (runner-install state side target) - (when (pos? (get-counters card :power)) - (add-counter state side card :power -1)))}]}) - -(define-card "Chrome Parlor" - {:events - {:pre-damage {:req (req (has-subtype? (second targets) "Cybernetic")) - :effect (effect (damage-prevent target Integer/MAX_VALUE))}}}) - -(define-card "Citadel Sanctuary" - {:interactions {:prevent [{:type #{:meat} - :req (req true)}]} - :abilities [{:label "[Trash] and trash all cards in Grip to prevent all meat damage" - :msg "trash all cards in their Grip and prevent all meat damage" - :effect (req (trash state side card {:cause :ability-cost}) - (doseq [c (:hand runner)] - (trash state side c {:unpreventable true})) - (damage-prevent state side :meat Integer/MAX_VALUE))}] - :events {:runner-turn-ends - {:req (req (pos? (count-tags state))) - :msg "force the Corp to initiate a trace" - :label "Trace 1 - If unsuccessful, Runner removes 1 tag" - :trace {:base 1 - :unsuccessful {:msg "remove 1 tag" - :async true - :effect (effect (lose-tags :runner eid 1))}}}}}) - -(define-card "Clan Vengeance" - {:events {:pre-resolve-damage {:req (req (pos? (last targets))) - :effect (effect (add-counter card :power 1) - (system-msg :runner (str "places 1 power counter on Clan Vengeance")))}} - :abilities [{:label "[Trash]: Trash 1 random card from HQ for each power counter" - :req (req (pos? (get-counters card :power))) - :msg (msg "trash " (min (get-counters card :power) (count (:hand corp))) " cards from HQ") - :effect (effect (trash-cards (take (min (get-counters card :power) (count (:hand corp))) - (shuffle (:hand corp)))) - (trash card {:cause :ability-cost}))}]}) - -(define-card "Climactic Showdown" - (letfn [(iced-servers [state] - (filter #(-> (get-in @state (cons :corp (server->zone state %))) :ices count pos?) (zones->sorted-names (get-zones state)))) - (trash-or-bonus [chosen-server] - {:player :corp - :prompt "Choose a piece of ice to trash or cancel" - :choices {:req #(and (= (last (:zone %)) :ices) - (= chosen-server (rest (butlast (:zone %)))))} - :effect (effect (system-msg (str "trashes " (card-str state target))) - (trash :corp eid target {:unpreventable true})) - :cancel-effect (effect (system-msg (str "does not trash a piece of ice protecting " (zone->name chosen-server))) - (register-events - :runner - {:pre-access {:req (req (#{:hq :rd} target)) - :once :per-turn - :msg (msg "access 2 additional cards from " (zone->name target)) - :effect (effect (access-bonus :runner target 2))} - :runner-turn-ends {:effect (effect (unregister-events card {:events {:pre-access nil - :runner-turn-ends nil}}))}} - (assoc card :zone [:rfg])) - (effect-completed eid))})] - {:events {:pre-access nil - :runner-turn-ends nil - :runner-turn-begins - {:async true - :effect (effect (move card :rfg) - (continue-ability - (if (pos? (count (iced-servers state))) - {:prompt (msg "Choose a server") - :choices (req (iced-servers state)) - :msg (msg "choose " (zone->name (unknown->kw target)) - " and removes Climactic Showdown from the game") - :effect (effect (continue-ability - :corp - (trash-or-bonus (next (server->zone state target))) - card nil))} - {:msg (str "choose a server protected by ice but cannot" - " and removes Climactic Showdown from the game")}) - card nil))}}})) - -(define-card "Compromised Employee" - {:recurring 1 - :events {:rez {:req (req (ice? target)) + :effect (req (wait-for (draw state side 3 nil) + (resolve-ability state side + {:prompt "Choose a card in your Grip to shuffle back into your Stack" + :choices {:req #(and (in-hand? %) + (runner? %))} + :effect (effect (move target :deck) + (shuffle! :deck))} + card nil)))}]} + + "Bloo Moose" + {:flags {:runner-phase-12 (req true)} + :abilities [{:req (req (and (:runner-phase-12 @state) + (not (seq (get-in @state [:runner :locked :discard]))))) + :once :per-turn + :prompt "Choose a card in the Heap to remove from the game and gain 2 [Credits]" + :show-discard true + :choices {:req #(and (in-discard? %) (runner? %))} + :msg (msg "remove " (:title target) " from the game and gain 2 [Credits]") + :effect (effect (gain-credits 2) + (move target :rfg))}]} + + "Borrowed Satellite" + {:in-play [:hand-size 1 :link 1]} + + "Bug Out Bag" + {:prompt "How many power counters?" + :choices :credit + :msg (msg "add " target " power counters") + :effect (effect (add-counter card :power target)) + :events {:runner-turn-ends {:req (req (zero? (count (:hand runner)))) + :msg (msg "draw " (get-counters card :power) " cards. Bug Out Bag is trashed") + :async true + :effect (req (wait-for (draw state side (get-counters card :power) nil) + (trash state side eid card nil)))}}} + + "Caldera" + {:interactions {:prevent [{:type #{:net :brain} + :req (req true)}]} + :abilities [{:cost [:credit 3] + :msg "prevent 1 net damage" + :effect (effect (damage-prevent :net 1))} + {:cost [:credit 3] + :msg "prevent 1 brain damage" + :effect (effect (damage-prevent :brain 1))}]} + + "Charlatan" + {:abilities [{:cost [:click 2] + :label "Make a run" + :prompt "Choose a server" + :choices (req runnable-servers) + :msg (msg "make a run on " target) + :effect (effect (make-run target nil card))} + {:label "Pay credits equal to strength of approached rezzed ICE to bypass it" + :once :per-run + :req (req (and (:run @state) (rezzed? current-ice))) + :msg (msg "pay " (:current-strength current-ice) " [Credits] and bypass " (:title current-ice)) + :effect (effect (pay :runner card :credit (:current-strength current-ice)))}]} + + "Chatterjee University" + {:abilities [{:cost [:click 1] + :label "Place 1 power counter" + :msg "place 1 power counter on it" + :effect (effect (add-counter card :power 1))} + {:cost [:click 1] + :label "Install a program from your Grip" + :prompt "Select a program to install from your Grip" + :choices {:req #(and (program? %) (in-hand? %))} + :msg (msg "install " (:title target)) + :effect (req (install-cost-bonus state side [:credit (- (get-counters card :power))]) + (runner-install state side target) + (when (pos? (get-counters card :power)) + (add-counter state side card :power -1)))}]} + + "Chrome Parlor" + {:events + {:pre-damage {:req (req (has-subtype? (second targets) "Cybernetic")) + :effect (effect (damage-prevent target Integer/MAX_VALUE))}}} + + "Citadel Sanctuary" + {:interactions {:prevent [{:type #{:meat} + :req (req true)}]} + :abilities [{:label "[Trash] and trash all cards in Grip to prevent all meat damage" + :msg "trash all cards in their Grip and prevent all meat damage" + :effect (req (trash state side card {:cause :ability-cost}) + (doseq [c (:hand runner)] + (trash state side c {:unpreventable true})) + (damage-prevent state side :meat Integer/MAX_VALUE))}] + :events {:runner-turn-ends + {:req (req (pos? (count-tags state))) + :msg "force the Corp to initiate a trace" + :label "Trace 1 - If unsuccessful, Runner removes 1 tag" + :trace {:base 1 + :unsuccessful {:msg "remove 1 tag" + :async true + :effect (effect (lose-tags :runner eid 1))}}}}} + + "Clan Vengeance" + {:events {:pre-resolve-damage {:req (req (pos? (last targets))) + :effect (effect (add-counter card :power 1) + (system-msg :runner (str "places 1 power counter on Clan Vengeance")))}} + :abilities [{:label "[Trash]: Trash 1 random card from HQ for each power counter" + :req (req (pos? (get-counters card :power))) + :msg (msg "trash " (min (get-counters card :power) (count (:hand corp))) " cards from HQ") + :effect (effect (trash-cards (take (min (get-counters card :power) (count (:hand corp))) + (shuffle (:hand corp)))) + (trash card {:cause :ability-cost}))}]} + + "Climactic Showdown" + (letfn [(iced-servers [state] + (filter #(-> (get-in @state (cons :corp (server->zone state %))) :ices count pos?) (zones->sorted-names (get-zones state)))) + (trash-or-bonus [chosen-server] + {:player :corp + :prompt "Choose a piece of ice to trash or cancel" + :choices {:req #(and (= (last (:zone %)) :ices) + (= chosen-server (rest (butlast (:zone %)))))} + :effect (effect (system-msg (str "trashes " (card-str state target))) + (trash :corp eid target {:unpreventable true})) + :cancel-effect (effect (system-msg (str "does not trash a piece of ice protecting " (zone->name chosen-server))) + (register-events + :runner + {:pre-access {:req (req (#{:hq :rd} target)) + :once :per-turn + :msg (msg "access 2 additional cards from " (zone->name target)) + :effect (effect (access-bonus :runner target 2))} + :runner-turn-ends {:effect (effect (unregister-events card {:events {:pre-access nil + :runner-turn-ends nil}}))}} + (assoc card :zone [:rfg])) + (effect-completed eid))})] + {:events {:pre-access nil + :runner-turn-ends nil + :runner-turn-begins + {:async true + :effect (effect (move card :rfg) + (continue-ability + (if (pos? (count (iced-servers state))) + {:prompt (msg "Choose a server") + :choices (req (iced-servers state)) + :msg (msg "choose " (zone->name (unknown->kw target)) + " and removes Climactic Showdown from the game") + :effect (effect (continue-ability + :corp + (trash-or-bonus (next (server->zone state target))) + card nil))} + {:msg (str "choose a server protected by ice but cannot" + " and removes Climactic Showdown from the game")}) + card nil))}}}) + + "Compromised Employee" + {:recurring 1 + :events {:rez {:req (req (ice? target)) + :msg "gain 1 [Credits]" + :effect (effect (gain-credits :runner 1))}} + :interactions {:pay-credits {:req (req (= :trace (:source-type eid))) + :type :recurring}}} + + "Corporate Defector" + {:events {:corp-click-draw {:msg (msg "reveal " (-> target first :title)) + :effect (effect (reveal target))}}} + + "Councilman" + {:implementation "Does not restrict Runner to Asset / Upgrade just rezzed" + :events {:rez {:req (req (and (#{"Asset" "Upgrade"} (:type target)) + (can-pay? state :runner eid card nil [:credit (rez-cost state :corp target)]))) + :effect (req (toast state :runner (str "Click Councilman to derez " (card-str state target {:visible true}) + " that was just rezzed") "info") + (toast state :corp (str "Runner has the opportunity to derez with Councilman.") "error"))}} + :abilities [{:prompt "Select an asset or upgrade that was just rezzed" + :choices {:req #(and (rezzed? %) + (or (asset? %) (upgrade? %)))} + :effect (req (let [c target + creds (rez-cost state :corp c)] + (when (can-pay? state side eid card nil [:credit creds]) + (resolve-ability + state :runner + {:msg (msg "pay " creds " [Credit] and derez " (:title c) ". Councilman is trashed") + :effect (req (lose-credits state :runner creds) + (derez state :corp c) + (register-turn-flag! + state side card :can-rez + (fn [state side card] + (if (same-card? card c) + ((constantly false) + (toast state :corp "Cannot rez the rest of this turn due to Councilman")) + true))) + (trash state side card {:unpreventable true}))} + card nil))))}]} + + "Counter Surveillance" + {:implementation "Does not prevent access of cards installed in the root of a server" + :abilities [{:cost [:click 1] + :makes-run true + :prompt "Choose a server to run with Counter Surveillance" + :msg (msg "run " target " and trashes Counter Surveillance") + :choices (req (cancellable runnable-servers)) + :effect (req (trash state side card {:cause :ability-cost}) + (make-run state side target nil card) + (register-events state side + {:successful-run + {:silent (req true) + :effect (req (let [tags (count-tags state)] + (if (>= (:credit runner) tags) + ;; Can pay, do access + (do (system-msg state side (str "uses Counter Surveillance to access up to " + tags " cards by paying " + tags " [Credit]")) + (pay state side card :credit tags) + (access-bonus state side target (- tags 1))) + ;; Can't pay, don't access cards + (do (system-msg state side "could not afford to use Counter Surveillance") + ;; Cannot access any cards + (max-access state side 0)))))} + :run-ends {:effect (effect (unregister-events card))}} + (assoc card :zone '(:discard))))}] + :events {:successful-run nil :run-ends nil}} + + "Crash Space" + {:interactions {:prevent [{:type #{:meat} + :req (req true)}] + :pay-credits {:req (req (= :remove-tag (:source-type eid))) + :type :recurring}} + :recurring 2 + :abilities [{:label "Trash to prevent up to 3 meat damage" + :msg "prevent up to 3 meat damage" + :effect (effect (trash card {:cause :ability-cost}) (damage-prevent :meat 3))}]} + + "Crowdfunding" + (let [ability {:once :per-turn + :label "Take 1 [Credits] (start of turn)" :msg "gain 1 [Credits]" - :effect (effect (gain-credits :runner 1))}} - :interactions {:pay-credits {:req (req (= :trace (:source-type eid))) - :type :recurring}}}) - -(define-card "Corporate Defector" - {:events {:corp-click-draw {:msg (msg "reveal " (-> target first :title)) - :effect (effect (reveal target))}}}) - -(define-card "Councilman" - {:implementation "Does not restrict Runner to Asset / Upgrade just rezzed" - :events {:rez {:req (req (and (#{"Asset" "Upgrade"} (:type target)) - (can-pay? state :runner eid card nil [:credit (rez-cost state :corp target)]))) - :effect (req (toast state :runner (str "Click Councilman to derez " (card-str state target {:visible true}) - " that was just rezzed") "info") - (toast state :corp (str "Runner has the opportunity to derez with Councilman.") "error"))}} - :abilities [{:prompt "Select an asset or upgrade that was just rezzed" - :choices {:req #(and (rezzed? %) - (or (asset? %) (upgrade? %)))} - :effect (req (let [c target - creds (rez-cost state :corp c)] - (when (can-pay? state side eid card nil [:credit creds]) - (resolve-ability - state :runner - {:msg (msg "pay " creds " [Credit] and derez " (:title c) ". Councilman is trashed") - :effect (req (lose-credits state :runner creds) - (derez state :corp c) - (register-turn-flag! - state side card :can-rez - (fn [state side card] - (if (same-card? card c) - ((constantly false) - (toast state :corp "Cannot rez the rest of this turn due to Councilman")) - true))) - (trash state side card {:unpreventable true}))} - card nil))))}]}) - -(define-card "Counter Surveillance" - {:implementation "Does not prevent access of cards installed in the root of a server" - :abilities [{:cost [:click 1] - :makes-run true - :prompt "Choose a server to run with Counter Surveillance" - :msg (msg "run " target " and trashes Counter Surveillance") - :choices (req (cancellable runnable-servers)) - :effect (req (trash state side card {:cause :ability-cost}) - (make-run state side target nil card) - (register-events state side - {:successful-run - {:silent (req true) - :effect (req (let [tags (count-tags state)] - (if (>= (:credit runner) tags) - ;; Can pay, do access - (do (system-msg state side (str "uses Counter Surveillance to access up to " - tags " cards by paying " - tags " [Credit]")) - (pay state side card :credit tags) - (access-bonus state side target (- tags 1))) - ;; Can't pay, don't access cards - (do (system-msg state side "could not afford to use Counter Surveillance") - ;; Cannot access any cards - (max-access state side 0)))))} - :run-ends {:effect (effect (unregister-events card))}} - (assoc card :zone '(:discard))))}] - :events {:successful-run nil :run-ends nil}}) - -(define-card "Crash Space" - {:interactions {:prevent [{:type #{:meat} - :req (req true)}] - :pay-credits {:req (req (= :remove-tag (:source-type eid))) - :type :recurring}} - :recurring 2 - :abilities [{:label "Trash to prevent up to 3 meat damage" - :msg "prevent up to 3 meat damage" - :effect (effect (trash card {:cause :ability-cost}) (damage-prevent :meat 3))}]}) - -(define-card "Crowdfunding" - (let [ability {:once :per-turn - :label "Take 1 [Credits] (start of turn)" - :msg "gain 1 [Credits]" + :req (req (:runner-phase-12 @state)) + :counter-cost [:credit 1] + :async true + :effect (req (gain-credits state :runner 1) + (if (zero? (get-counters (get-card state card) :credit)) + (do (trash state :runner card {:unpreventable true}) + (system-msg state :runner (str "trashes Crowdfunding" + (when (not (empty? (:deck runner))) + " and draws 1 card"))) + (draw state :runner eid 1 nil)) + (effect-completed state side eid)))} + install-prompt {:req (req (and (in-discard? card) + (not (install-locked? state :runner)))) + :async true + :effect (req (continue-ability + state side + {:optional {:req (req (and (>= (count (get-in @state [:runner :register :successful-run])) 3) + (not (get-in @state [:runner :register :crowdfunding-prompt])))) + :player :runner + :prompt "Install Crowdfunding?" + :yes-ability {:effect (effect (unregister-events card) + (runner-install :runner card {:ignore-all-cost true}))} + ;; Add a register to note that the player was already asked about installing, + ;; to prevent multiple copies from prompting multiple times. + :no-ability {:effect (req (swap! state assoc-in [:runner :register :crowdfunding-prompt] true))}}} + card nil))} + heap-event (req (when (in-discard? card) + (unregister-events state side card) + (register-events state side + {:runner-turn-ends install-prompt} + (assoc card :zone [:discard]))))] + {:data {:counter {:credit 3}} + :flags {:drip-economy true + :runner-turn-draw (req (= 1 (get-counters (get-card state card) :credit))) + :runner-phase-12 (req (= 1 (get-counters (get-card state card) :credit)))} + :abilities [ability] + :move-zone heap-event + :events {:runner-turn-begins ability + :runner-turn-ends nil}}) + + "Crypt" + {:events {:successful-run + {:silent (req true) + :req (req (= :archives target)) + :optional {:prompt "Place a virus counter on Crypt?" + :autoresolve (get-autoresolve :auto-add) + :yes-ability {:effect (effect (add-counter card :virus 1) + (system-msg "places a virus counter on Crypt"))}}}} + :abilities [{:label "[Click][Trash]: install a virus program from the stack" + :prompt "Choose a virus" + :msg (msg "install " (:title target) " from the stack") + :choices (req (cancellable (filter #(and (program? %) + (has-subtype? % "Virus")) + (:deck runner)) :sorted)) + :cost [:click 1] + :counter-cost [:virus 3] + :effect (effect (trigger-event :searched-stack nil) + (shuffle! :deck) + (runner-install target) + (trash card {:cause :ability-cost}))} + (set-autoresolve :auto-add "adding virus counters to Crypt")]} + + "Dadiana Chacon" + (let [trashme {:effect (effect (unregister-events card) + (damage eid :meat 3 {:unboostable true :card card}) + (trash card {:cause :ability-cost})) + :msg (msg "trashes Dadiana Chacon and suffers 3 meat damage")} + ability {:once :per-turn + :msg "gain 1 [Credits]" + :req (req (< (get-in @state [:runner :credit]) 6)) + :effect (req (gain-credits state :runner 1))}] + {:effect (req (if (zero? (get-in @state [:runner :credit])) + (resolve-ability state side trashme card nil) + (add-watch state :dadiana + (fn [k ref old new] + (when (and (not (zero? (get-in old [:runner :credit]))) + (zero? (get-in new [:runner :credit]))) + (resolve-ability ref side trashme card nil)))))) + :leave-play (req (remove-watch state :dadiana)) + :flags {:drip-economy true} + :events {:play nil + :runner-turn-begins ability}}) + + "Daily Casts" + (let [ability {:once :per-turn + :label "Take 2 [Credits] (start of turn)" + :msg "gain 2 [Credits]" + :req (req (:runner-phase-12 @state)) + :counter-cost [:credit 2] + :effect (req (gain-credits state :runner 2) + (when (zero? (get-counters (get-card state card) :credit)) + (trash state :runner card {:unpreventable true})))}] + {:data {:counter {:credit 8}} + :flags {:drip-economy true} + :abilities [ability] + :events {:runner-turn-begins ability}}) + + "Data Dealer" + {:abilities [{:cost [:click 1 :forfeit] :effect (effect (gain-credits 9)) + :msg (msg "gain 9 [Credits]")}]} + + "Data Folding" + (let [ability {:label "Gain 1 [Credits] (start of turn)" + :msg "gain 1 [Credits]" + :once :per-turn + :req (req (and (>= (available-mu state) 2) (:runner-phase-12 @state))) + :effect (effect (gain-credits 1))}] + {:flags {:drip-economy true} + :abilities [ability] + :events {:runner-turn-begins ability}}) + + "Data Leak Reversal" + {:req (req (some #{:hq :rd :archives} (:successful-run runner-reg))) + :abilities [{:req (req tagged) + :cost [:click 1] + :effect (effect (mill :corp)) + :msg "force the Corp to trash the top card of R&D"}]} + + "DDoS" + {:abilities [{:msg "prevent the corp from rezzing the outermost piece of ice during a run on any server this turn" + :effect (effect + (register-turn-flag! + card :can-rez + (fn [state side card] + (let [idx (ice-index state card)] + (if (and (ice? card) + idx + (= (count (get-in @state (concat [:corp :servers] (:server (:run @state)) [:ices]))) + (inc idx))) + ((constantly false) (toast state :corp "Cannot rez any outermost ICE due to DDoS." "warning")) + true)))) + (trash card {:cause :ability-cost}))}]} + + "Dean Lister" + {:abilities [{:req (req (:run @state)) + :msg (msg "add +1 strength for each card in their Grip to " (:title target) " until the end of the run") + :choices {:req #(and (has-subtype? % "Icebreaker") + (installed? %))} + :effect (effect (update! (assoc card :dean-target target)) + (trash (get-card state card) {:cause :ability-cost}) + (update-breaker-strength target))}] + :events {:run-ends nil :pre-breaker-strength nil} + :trash-effect {:effect + (effect (register-events + (let [dean {:effect (effect (unregister-events card) + (update! (dissoc card :dean-target)) + (update-breaker-strength (:dean-target card)))}] + {:run-ends dean + :pre-breaker-strength {:req (req (same-card? target (:dean-target card))) + :effect (effect (breaker-strength-bonus (count (:hand runner))))}}) card))}} + + "Decoy" + {:interactions {:prevent [{:type #{:tag} + :req (req true)}]} + :abilities [{:msg "avoid 1 tag" :effect (effect (tag-prevent :runner 1) (trash card {:cause :ability-cost}))}]} + + "District 99" + (letfn [(eligible-cards [runner] (filter #(same-card? :faction (:identity runner) %) + (:discard runner)))] + {:implementation "Adding power counters must be done manually for programs/hardware trashed manually (e.g. by being over MU)" + :abilities [{:label "Add a card from your heap to your grip" + :req (req (seq (eligible-cards runner))) + :counter-cost [:power 3] + :cost [:click 1] + :prompt "Select a card to add to grip?" + :choices (req (eligible-cards runner)) + :effect (effect (move target :hand)) + :msg (msg "add " (:title target) " to grip")} + {:label "Add a power counter manually" + :once :per-turn + :effect (effect (add-counter card :power 1)) + :msg "manually add a power counter"}] + :events (let [prog-or-hw #(or (program? (first %)) + (hardware? (first %))) + trash-event (fn [side-trash] {:once :per-turn + :req (req (first-event? state side side-trash prog-or-hw)) + :effect (effect (system-msg :runner "adds 1 power counter on District 99") + (add-counter card :power 1))})] + {:corp-trash (trash-event :corp-trash) + :runner-trash (trash-event :runner-trash)})}) + + "DJ Fenris" + (let [is-draft-id? #(starts-with? (:code %) "00") + sorted-id-list (fn [runner] (->> (server-cards) + (filter #(and (identity? %) + (has-subtype? % "g-mod") + (not= (-> runner :identity :faction) + (:faction %)) + (not (is-draft-id? %)))) + (sort-by :title))) + fenris-effect {:prompt "Choose a g-mod identity to host on DJ Fenris" + :choices (req (sorted-id-list runner)) + :msg (msg "host " (:title target)) + :effect (req (let [card (assoc-host-zones card) + ;; Work around for get-card and update! + c (assoc target :type "Fake-Identity") + c (make-card c) + c (assoc c + :host (dissoc card :hosted) + :zone '(:onhost) + ;; semi hack to get deactivate to work + :installed true)] + ;; Manually host id on card + (update! state side (assoc card :hosted [c])) + (card-init state :runner c) + ;; Clean-up + (clear-wait-prompt state :corp) + (effect-completed state side eid)))}] + {:async true + :effect (req (show-wait-prompt state :corp "Runner to pick identity to host on DJ Fenris") + (continue-ability state side fenris-effect card nil)) + ;; Handle Dr. Lovegood / Malia + :disable {:effect (req (doseq [hosted (:hosted card)] + (disable-card state side hosted)))} + :reactivate {:effect (req (doseq [hosted (:hosted card) + :let [c (dissoc hosted :disabled) + {:keys [effect events]} (card-def c)]] + ;; Manually enable card to trigger `:effect`, similar to `enable-identity` + (update! state side c) + (when effect + (effect state side (make-eid state) c nil)) + (when events + (register-events state side events c))))}}) + + "Donut Taganes" + {:msg "increase the play cost of operations and events by 1 [Credits]" + :events {:pre-play-instant + {:effect (effect (play-cost-bonus [:credit 1]))}}} + + "Dr. Lovegood" + {:flags {:runner-phase-12 (req (> (count (all-installed state :runner)) 1))} + :abilities [{:req (req (:runner-phase-12 @state)) + :prompt "Select an installed card to make its text box blank for the remainder of the turn" + :once :per-turn + :choices {:req installed?} + :msg (msg "make the text box of " (:title target) " blank for the remainder of the turn") + :effect (req (let [c target] + (disable-card state side c) + (register-events state side + {:post-runner-turn-ends + {:effect (req (enable-card state side (get-card state c)) + (when-let [reactivate-effect (:reactivate (card-def c))] + (resolve-ability state :runner reactivate-effect + (get-card state c) nil)) + (unregister-events state side card))}} card)))}] + :events {:post-runner-turn-ends nil}} + + "Drug Dealer" + {:flags {:runner-phase-12 (req (some #(card-flag? % :drip-economy true) (all-active-installed state :runner)))} + :abilities [{:label "Lose 1 [Credits] (start of turn)" + :msg (msg (if (zero? (get-in @state [:runner :credit])) + "lose 0 [Credits] (runner has no credits to lose)" + "lose 1 [Credits]")) :req (req (:runner-phase-12 @state)) - :counter-cost [:credit 1] - :async true - :effect (req (gain-credits state :runner 1) - (if (zero? (get-counters (get-card state card) :credit)) - (do (trash state :runner card {:unpreventable true}) - (system-msg state :runner (str "trashes Crowdfunding" - (when (not (empty? (:deck runner))) - " and draws 1 card"))) - (draw state :runner eid 1 nil)) - (effect-completed state side eid)))} - install-prompt {:req (req (and (in-discard? card) - (not (install-locked? state :runner)))) - :async true - :effect (req (continue-ability - state side - {:optional {:req (req (and (>= (count (get-in @state [:runner :register :successful-run])) 3) - (not (get-in @state [:runner :register :crowdfunding-prompt])))) - :player :runner - :prompt "Install Crowdfunding?" - :yes-ability {:effect (effect (unregister-events card) - (runner-install :runner card {:ignore-all-cost true}))} - ;; Add a register to note that the player was already asked about installing, - ;; to prevent multiple copies from prompting multiple times. - :no-ability {:effect (req (swap! state assoc-in [:runner :register :crowdfunding-prompt] true))}}} - card nil))} - heap-event (req (when (in-discard? card) - (unregister-events state side card) - (register-events state side - {:runner-turn-ends install-prompt} - (assoc card :zone [:discard]))))] - {:data {:counter {:credit 3}} - :flags {:drip-economy true - :runner-turn-draw (req (= 1 (get-counters (get-card state card) :credit))) - :runner-phase-12 (req (= 1 (get-counters (get-card state card) :credit)))} - :abilities [ability] - :move-zone heap-event - :events {:runner-turn-begins ability - :runner-turn-ends nil}})) - -(define-card "Crypt" - {:events {:successful-run - {:silent (req true) - :req (req (= :archives target)) - :optional {:prompt "Place a virus counter on Crypt?" - :autoresolve (get-autoresolve :auto-add) - :yes-ability {:effect (effect (add-counter card :virus 1) - (system-msg "places a virus counter on Crypt"))}}}} - :abilities [{:label "[Click][Trash]: install a virus program from the stack" - :prompt "Choose a virus" - :msg (msg "install " (:title target) " from the stack") - :choices (req (cancellable (filter #(and (program? %) - (has-subtype? % "Virus")) - (:deck runner)) :sorted)) - :cost [:click 1] - :counter-cost [:virus 3] - :effect (effect (trigger-event :searched-stack nil) - (shuffle! :deck) - (runner-install target) - (trash card {:cause :ability-cost}))} - (set-autoresolve :auto-add "adding virus counters to Crypt")]}) - -(define-card "Dadiana Chacon" - (let [trashme {:effect (effect (unregister-events card) - (damage eid :meat 3 {:unboostable true :card card}) - (trash card {:cause :ability-cost})) - :msg (msg "trashes Dadiana Chacon and suffers 3 meat damage")} - ability {:once :per-turn - :msg "gain 1 [Credits]" - :req (req (< (get-in @state [:runner :credit]) 6)) - :effect (req (gain-credits state :runner 1))}] - {:effect (req (if (zero? (get-in @state [:runner :credit])) - (resolve-ability state side trashme card nil) - (add-watch state :dadiana - (fn [k ref old new] - (when (and (not (zero? (get-in old [:runner :credit]))) - (zero? (get-in new [:runner :credit]))) - (resolve-ability ref side trashme card nil)))))) - :leave-play (req (remove-watch state :dadiana)) - :flags {:drip-economy true} - :events {:play nil - :runner-turn-begins ability}})) - -(define-card "Daily Casts" - (let [ability {:once :per-turn - :label "Take 2 [Credits] (start of turn)" + :once :per-turn + :effect (effect (lose-credits 1))}] + :events {:corp-turn-begins {:msg (msg "draw " (if (zero? (count (get-in @state [:runner :deck]))) + "0 cards (runner's stack is empty)" + "1 card")) + :async true + :effect (effect (draw :runner eid 1 nil))} + :runner-turn-begins {:msg (msg "lose " (if (zero? (get-in @state [:runner :credit])) + "0 [Credits] (runner has no credits to lose)" + "1 [Credits]")) + :once :per-turn + :effect (effect (lose-credits 1))}}} + + "Duggar's" + {:abilities [{:cost [:click 4] :async true :effect (effect (draw eid 10 nil)) :msg "draw 10 cards"}]} + + "Dummy Box" + (letfn [(dummy-prevent [type] {:msg (str "prevent a " type " from being trashed") + :async true + :priority 15 + :prompt (str "Choose a " type " in your Grip") + :choices {:req #(and (is-type? % (capitalize type)) + (in-hand? %))} + :effect (effect (move target :discard) + (trash-prevent (keyword type) 1))})] + {:interactions {:prevent [{:type #{:trash-hardware :trash-resource :trash-program} + :req (req (not= :purge (:cause target)))}]} + :abilities [(dummy-prevent "hardware") + (dummy-prevent "resource") + (dummy-prevent "program")]}) + + "Earthrise Hotel" + (let [ability {:msg "draw 2 cards" + :once :per-turn + :counter-cost [:power 1] + :req (req (:runner-phase-12 @state)) + :async true + :effect (req (wait-for (draw state :runner 2 nil) + (if (zero? (get-counters (get-card state card) :power)) + (trash state :runner eid card {:unpreventable true}) + (effect-completed state side eid))))}] + {:flags {:runner-turn-draw true + :runner-phase-12 (req (< 1 (count (filter #(card-flag? % :runner-turn-draw true) + (cons (get-in @state [:runner :identity]) + (all-active-installed state :runner))))))} + :data {:counter {:power 3}} + :events {:runner-turn-begins ability} + :abilities [ability]}) + + "Eden Shard" + (shard-constructor :rd "force the Corp to draw 2 cards" (req (draw state :corp eid 2 nil))) + + "Emptied Mind" + (let [ability {:req (req (zero? (count (:hand runner)))) + :msg "gain [Click]" + :label "Gain [Click] (start of turn)" + :once :per-turn + :effect (effect (gain :click 1))}] + {:events {:runner-turn-begins ability} + :abilities [ability]}) + + "Enhanced Vision" + {:events {:successful-run {:silent (req true) + :effect (req (let [card (first (shuffle (:hand corp)))] + (reveal state :corp card) + (system-msg state :runner "force the Corp to reveal " (:title card)))) + :req (req (genetics-trigger? state side :successful-run))}}} + + "Fall Guy" + {:interactions {:prevent [{:type #{:trash-resource} + :req (req true)}]} + :abilities [{:label "[Trash]: Prevent another installed resource from being trashed" + :effect (effect (trash card {:unpreventable true :cause :ability-cost}) + (trash-prevent :resource 1))} + {:label "[Trash]: Gain 2 [Credits]" :msg "gain 2 [Credits]" - :req (req (:runner-phase-12 @state)) - :counter-cost [:credit 2] - :effect (req (gain-credits state :runner 2) - (when (zero? (get-counters (get-card state card) :credit)) - (trash state :runner card {:unpreventable true})))}] - {:data {:counter {:credit 8}} - :flags {:drip-economy true} - :abilities [ability] - :events {:runner-turn-begins ability}})) + :effect (effect (trash card {:cause :ability-cost}) + (gain-credits 2))}]} -(define-card "Data Dealer" - {:abilities [{:cost [:click 1 :forfeit] :effect (effect (gain-credits 9)) - :msg (msg "gain 9 [Credits]")}]}) + "Fan Site" + {:events {:agenda-scored {:msg "add it to their score area as an agenda worth 0 agenda points" + :async true + :req (req (installed? card)) + :effect (req (as-agenda state :runner eid card 0))}}} -(define-card "Data Folding" - (let [ability {:label "Gain 1 [Credits] (start of turn)" + "Fencer Fueno" + (companion-builder + (req (and (pos? (get-counters (get-card state card) :credit)) + (:successful run))) + (effect (show-wait-prompt :corp "Runner to take decision on Fencer Fueno") + (continue-ability + {:prompt "Pay 1 [Credits] or trash Fencer Fueno?" + :choices (req (if (can-pay? state :runner eid card nil :credit 1) + ["Pay 1 [Credits]" "Trash"] + ["Trash"])) + :player :runner + :effect (req (if (= target "Trash") + (do + (trash state :runner card) + (system-msg state :runner "trashes Fencer Fueno")) + (do + (pay state :runner card :credit 1) + (system-msg state :runner "pays 1 [Credits] to avoid trashing Fencer Fueno"))) + (clear-wait-prompt state :corp))} + card nil)) + {:msg "take 1 [Credits]" + :effect (effect (add-counter card :credit -1) + (gain-run-credits 1))}) + + "Fester" + {:events {:purge {:msg "force the Corp to lose 2 [Credits] if able" + :effect (effect (pay :corp card :credit 2))}}} + + "Film Critic" + (letfn [(get-agenda [card] (first (filter #(= "Agenda" (:type %)) (:hosted card)))) + (host-agenda? [agenda] + {:optional {:prompt (str "You access " (:title agenda) ". Host it on Film Critic?") + :yes-ability {:effect (req (host state side card (move state side agenda :play-area)) + (access-end state side eid agenda) + (when-not (:run @state) + (swap! state dissoc :access))) + :msg (msg "host " (:title agenda) " instead of accessing it")}}})] + {:events {:access {:req (req (and (empty? (filter #(= "Agenda" (:type %)) (:hosted card))) + (agenda? target))) + :interactive (req true) + :async true + :effect (effect (continue-ability (host-agenda? target) card nil))}} + :abilities [{:cost [:click 2] :label "Add hosted agenda to your score area" + :req (req (get-agenda card)) + :async true + :effect (req (let [c (get-agenda card) + points (get-agenda-points state :runner c)] + (as-agenda state :runner eid c points))) + :msg (msg (let [c (get-agenda card)] + (str "add " (:title c) " to their score area and gain " + (quantify (get-agenda-points state :runner c) "agenda point"))))}]}) + + "Find the Truth" + {:events {:post-runner-draw {:msg (msg "reveal that they drew: " + (join ", " (map :title (get-in @state [:runner :register :most-recent-drawn])))) + :effect (effect (reveal (get-in @state [:runner :register :most-recent-drawn])))} + :successful-run {:interactive (get-autoresolve :auto-peek (complement never?)) + :silent (get-autoresolve :auto-peek never?) + :optional {:req (req (and (first-event? state side :successful-run) + (-> @state :corp :deck count pos?))) + :autoresolve (get-autoresolve :auto-peek) + :prompt "Use Find the Truth to look at the top card of R&D?" + :yes-ability {:prompt (req (->> corp :deck first :title (str "The top card of R&D is "))) + :msg "look at the top card of R&D" + :choices ["OK"]}}}} + :abilities [(set-autoresolve :auto-peek "Find the Truth's peek at R&D ability")]} + + "First Responders" + {:abilities [{:cost [:credit 2] + :req (req (some corp? (map second (turn-events state :runner :damage)))) + :msg "draw 1 card" + :async true + :effect (effect (draw eid 1 nil))}]} + + "Gang Sign" + {:events {:agenda-scored + {:async true + :interactive (req true) + :msg (msg "access " (quantify (get-in @state [:runner :hq-access]) "card") " from HQ") + :effect (req (wait-for + ;; manually trigger the pre-access event to alert Nerve Agent. + (trigger-event-sync state side :pre-access :hq) + (let [from-hq (access-count state side :hq-access) + ;; access-helper-hq uses a set to keep track of which cards have already + ;; been accessed. By adding HQ root's contents to this set, we make the runner + ;; unable to access those cards, as Gang Sign intends. + accessed-cards (set (get-in @state [:corp :servers :hq :content])) + ability (access-helper-hq state from-hq accessed-cards)] + (continue-ability state :runner ability card nil))))}}} + + "Gbahali" + {:abilities [{:label "[Trash]: Break the last subroutine on the encountered piece of ice" + :req (req (and (:run @state) (rezzed? current-ice))) + :effect (effect (trash card {:cause :ability-cost}) + (system-msg :runner + (str "trashes Gbahali to break the last subroutine on " + (:title current-ice))))}]} + + "Gene Conditioning Shoppe" + {:msg "make Genetics trigger a second time each turn" + :effect (effect (register-persistent-flag! card :genetics-trigger-twice (constantly true))) + :leave-play (effect (clear-persistent-flag! card :genetics-trigger-twice))} + + "Ghost Runner" + {:data {:counter {:credit 3}} + :abilities [{:counter-cost [:credit 1] :msg "gain 1 [Credits]" + :req (req (:run @state)) + :effect (req (gain-credits state side 1) + (trigger-event state side :spent-stealth-credit card) + (when (zero? (get-counters (get-card state card) :credit)) + (trash state :runner card {:unpreventable true})))}] + ; See Net Mercur for why this implementation was chosen + :interactions {:pay-credits {:req (req (:run @state)) + :type :credit}}} + + "Globalsec Security Clearance" + {:req (req (> (:link runner) 1)) + :flags {:runner-phase-12 (req true)} + :abilities [{:msg "lose [Click] and look at the top card of R&D" :once :per-turn - :req (req (and (>= (available-mu state) 2) (:runner-phase-12 @state))) - :effect (effect (gain-credits 1))}] - {:flags {:drip-economy true} - :abilities [ability] - :events {:runner-turn-begins ability}})) - -(define-card "Data Leak Reversal" - {:req (req (some #{:hq :rd :archives} (:successful-run runner-reg))) - :abilities [{:req (req tagged) - :cost [:click 1] - :effect (effect (mill :corp)) - :msg "force the Corp to trash the top card of R&D"}]}) - -(define-card "DDoS" - {:abilities [{:msg "prevent the corp from rezzing the outermost piece of ice during a run on any server this turn" - :effect (effect - (register-turn-flag! - card :can-rez - (fn [state side card] - (let [idx (ice-index state card)] - (if (and (ice? card) - idx - (= (count (get-in @state (concat [:corp :servers] (:server (:run @state)) [:ices]))) - (inc idx))) - ((constantly false) (toast state :corp "Cannot rez any outermost ICE due to DDoS." "warning")) - true)))) - (trash card {:cause :ability-cost}))}]}) - -(define-card "Dean Lister" - {:abilities [{:req (req (:run @state)) - :msg (msg "add +1 strength for each card in their Grip to " (:title target) " until the end of the run") - :choices {:req #(and (has-subtype? % "Icebreaker") - (installed? %))} - :effect (effect (update! (assoc card :dean-target target)) - (trash (get-card state card) {:cause :ability-cost}) - (update-breaker-strength target))}] - :events {:run-ends nil :pre-breaker-strength nil} - :trash-effect {:effect - (effect (register-events - (let [dean {:effect (effect (unregister-events card) - (update! (dissoc card :dean-target)) - (update-breaker-strength (:dean-target card)))}] - {:run-ends dean - :pre-breaker-strength {:req (req (same-card? target (:dean-target card))) - :effect (effect (breaker-strength-bonus (count (:hand runner))))}}) card))}}) - -(define-card "Decoy" - {:interactions {:prevent [{:type #{:tag} - :req (req true)}]} - :abilities [{:msg "avoid 1 tag" :effect (effect (tag-prevent :runner 1) (trash card {:cause :ability-cost}))}]}) - -(define-card "District 99" - (letfn [(eligible-cards [runner] (filter #(same-card? :faction (:identity runner) %) - (:discard runner)))] - {:implementation "Adding power counters must be done manually for programs/hardware trashed manually (e.g. by being over MU)" - :abilities [{:label "Add a card from your heap to your grip" - :req (req (seq (eligible-cards runner))) - :counter-cost [:power 3] - :cost [:click 1] - :prompt "Select a card to add to grip?" - :choices (req (eligible-cards runner)) - :effect (effect (move target :hand)) - :msg (msg "add " (:title target) " to grip")} - {:label "Add a power counter manually" + :effect (effect (prompt! card (str "The top card of R&D is " + (:title (first (:deck corp)))) ["OK"] {}))}] + :events {:runner-turn-begins {:req (req (get-in @state [:per-turn (:cid card)])) + :effect (effect (lose :click 1))}}} + + "Grifter" + {:events {:runner-turn-ends + {:effect (req (let [ab (if (get-in @state [:runner :register :successful-run]) + {:effect (effect (gain-credits 1)) :msg "gain 1 [Credits]"} + {:effect (effect (trash card)) :msg "trash Grifter"})] + (resolve-ability state side ab card targets)))}}} + + "Guru Davinder" + {:flags {:cannot-pay-net-damage true} + :events {:pre-damage + {:req (req (and (or (= target :meat) (= target :net)) + (pos? (last targets)))) + :msg (msg "prevent all " (if (= target :meat) "meat" "net") " damage") + :effect (req (damage-prevent state side :meat Integer/MAX_VALUE) + (damage-prevent state side :net Integer/MAX_VALUE) + (if (< (:credit runner) 4) + (trash state side card) + (resolve-ability + state :runner + {:optional + {:prompt "Pay 4 [Credits] to prevent trashing Guru Davinder?" + :player :runner + :yes-ability {:effect (effect (lose-credits :runner 4) + (system-msg (str "pays 4 [Credits] to prevent Guru Davinder " + "from being trashed")))} + :no-ability {:effect (effect (trash card))}}} + card nil)))}}} + + "Hades Shard" + (shard-constructor :archives "access all cards in Archives" {:async true} + (req (trash state side card {:cause :ability-cost}) + (swap! state update-in [:corp :discard] #(map (fn [c] (assoc c :seen true)) %)) + (wait-for (trigger-event-sync state side :pre-access :archives) + (resolve-ability state :runner + (choose-access (get-in @state [:corp :discard]) + '(:archives) {:no-root true}) card nil)))) + + "Hard at Work" + (let [ability {:msg "gain 2 [Credits] and lose [Click]" :once :per-turn - :effect (effect (add-counter card :power 1)) - :msg "manually add a power counter"}] - :events (let [prog-or-hw #(or (program? (first %)) - (hardware? (first %))) - trash-event (fn [side-trash] {:once :per-turn - :req (req (first-event? state side side-trash prog-or-hw)) - :effect (effect (system-msg :runner "adds 1 power counter on District 99") - (add-counter card :power 1))})] - {:corp-trash (trash-event :corp-trash) - :runner-trash (trash-event :runner-trash)})})) - -(define-card "DJ Fenris" - (let [is-draft-id? #(starts-with? (:code %) "00") - sorted-id-list (fn [runner] (->> (server-cards) - (filter #(and (identity? %) - (has-subtype? % "g-mod") - (not= (-> runner :identity :faction) - (:faction %)) - (not (is-draft-id? %)))) - (sort-by :title))) - fenris-effect {:prompt "Choose a g-mod identity to host on DJ Fenris" - :choices (req (sorted-id-list runner)) - :msg (msg "host " (:title target)) - :effect (req (let [card (assoc-host-zones card) - ;; Work around for get-card and update! - c (assoc target :type "Fake-Identity") - c (make-card c) - c (assoc c - :host (dissoc card :hosted) - :zone '(:onhost) - ;; semi hack to get deactivate to work - :installed true)] - ;; Manually host id on card - (update! state side (assoc card :hosted [c])) - (card-init state :runner c) - ;; Clean-up - (clear-wait-prompt state :corp) - (effect-completed state side eid)))}] - {:async true - :effect (req (show-wait-prompt state :corp "Runner to pick identity to host on DJ Fenris") - (continue-ability state side fenris-effect card nil)) - ;; Handle Dr. Lovegood / Malia - :disable {:effect (req (doseq [hosted (:hosted card)] - (disable-card state side hosted)))} - :reactivate {:effect (req (doseq [hosted (:hosted card) - :let [c (dissoc hosted :disabled) - {:keys [effect events]} (card-def c)]] - ;; Manually enable card to trigger `:effect`, similar to `enable-identity` - (update! state side c) - (when effect - (effect state side (make-eid state) c nil)) - (when events - (register-events state side events c))))}})) - -(define-card "Donut Taganes" - {:msg "increase the play cost of operations and events by 1 [Credits]" - :events {:pre-play-instant - {:effect (effect (play-cost-bonus [:credit 1]))}}}) - -(define-card "Dr. Lovegood" - {:flags {:runner-phase-12 (req (> (count (all-installed state :runner)) 1))} - :abilities [{:req (req (:runner-phase-12 @state)) - :prompt "Select an installed card to make its text box blank for the remainder of the turn" - :once :per-turn - :choices {:req installed?} - :msg (msg "make the text box of " (:title target) " blank for the remainder of the turn") - :effect (req (let [c target] - (disable-card state side c) - (register-events state side - {:post-runner-turn-ends - {:effect (req (enable-card state side (get-card state c)) - (when-let [reactivate-effect (:reactivate (card-def c))] - (resolve-ability state :runner reactivate-effect - (get-card state c) nil)) - (unregister-events state side card))}} card)))}] - :events {:post-runner-turn-ends nil}}) - -(define-card "Drug Dealer" - {:flags {:runner-phase-12 (req (some #(card-flag? % :drip-economy true) (all-active-installed state :runner)))} - :abilities [{:label "Lose 1 [Credits] (start of turn)" - :msg (msg (if (zero? (get-in @state [:runner :credit])) - "lose 0 [Credits] (runner has no credits to lose)" - "lose 1 [Credits]")) - :req (req (:runner-phase-12 @state)) - :once :per-turn - :effect (effect (lose-credits 1))}] - :events {:corp-turn-begins {:msg (msg "draw " (if (zero? (count (get-in @state [:runner :deck]))) - "0 cards (runner's stack is empty)" - "1 card")) - :async true - :effect (effect (draw :runner eid 1 nil))} - :runner-turn-begins {:msg (msg "lose " (if (zero? (get-in @state [:runner :credit])) - "0 [Credits] (runner has no credits to lose)" - "1 [Credits]")) - :once :per-turn - :effect (effect (lose-credits 1))}}}) - -(define-card "Duggar's" - {:abilities [{:cost [:click 4] :async true :effect (effect (draw eid 10 nil)) :msg "draw 10 cards"}]}) - -(define-card "Dummy Box" - (letfn [(dummy-prevent [type] {:msg (str "prevent a " type " from being trashed") - :async true - :priority 15 - :prompt (str "Choose a " type " in your Grip") - :choices {:req #(and (is-type? % (capitalize type)) - (in-hand? %))} - :effect (effect (move target :discard) - (trash-prevent (keyword type) 1))})] - {:interactions {:prevent [{:type #{:trash-hardware :trash-resource :trash-program} - :req (req (not= :purge (:cause target)))}]} - :abilities [(dummy-prevent "hardware") - (dummy-prevent "resource") - (dummy-prevent "program")]})) - -(define-card "Earthrise Hotel" - (let [ability {:msg "draw 2 cards" - :once :per-turn + :effect (effect (lose :click 1) (gain-credits 2))}] + {:flags {:drip-economy true} + :events {:runner-turn-begins ability} + :abilities [ability]}) + + "Hernando Cortez" + {:events {:pre-rez-cost {:req (req (and (>= (:credit corp) 10) (ice? target))) + :effect (effect (rez-additional-cost-bonus + [:credit (count-num-subroutines target)])) + :msg (msg "increase the rez cost by " (count-num-subroutines target) " [Credit]")}}} + + "Human First" + {:events {:agenda-scored {:msg (msg "gain " (get-agenda-points state :corp target) " [Credits]") + :effect (effect (gain-credits :runner (get-agenda-points state :corp target)))} + :agenda-stolen {:msg (msg "gain " (get-agenda-points state :runner target) " [Credits]") + :effect (effect (gain-credits (get-agenda-points state :runner target)))}}} + + "Hunting Grounds" + {:abilities [{:label "Prevent a \"when encountered\" ability on a piece of ICE" + :msg "prevent a \"when encountered\" ability on a piece of ICE" + :once :per-turn} + {:label "[Trash]: Install the top 3 cards of your Stack facedown" + :msg "install the top 3 cards of their Stack facedown" + :effect (req (trash state side card {:cause :ability-cost}) + (doseq [c (take 3 (get-in @state [:runner :deck]))] + (runner-install state side c {:facedown true})))}]} + + "Ice Analyzer" + {:implementation "Credit use restriction is not enforced" + :events {:rez {:req (req (ice? target)) + :msg "place 1 [Credits] on Ice Analyzer" + :effect (effect (add-counter :runner card :credit 1))}} + :abilities [{:counter-cost [:credit 1] + :effect (effect (gain-credits 1)) + :msg "take 1 [Credits] to install programs"}] + :interactions {:pay-credits {:req (req (and (= :runner-install (:source-type eid)) + (program? target))) + :type :credit}}} + + "Ice Carver" + {:events {:pre-ice-strength + {:req (req (and (same-card? target current-ice) (:rezzed target))) + :effect (effect (ice-strength-bonus -1 target))}}} + + "Inside Man" + {:recurring 2 + :interactions {:pay-credits {:req (req (and (= :runner-install (:source-type eid)) + (hardware? target))) + :type :recurring}}} + + "Investigative Journalism" + {:req (req (has-bad-pub? state)) + :abilities [{:cost [:click 4] :msg "give the Corp 1 bad publicity" + :effect (effect (gain-bad-publicity :corp 1) + (trash card {:cause :ability-cost}))}]} + + "Jackpot!" + (let [jackpot {:interactive (req true) + :async true + :req (req (= :runner (:as-agenda-side target))) + :effect (req (show-wait-prompt state :corp "Runner to use Jackpot!") + (continue-ability + state side + {:optional + {:prompt "Trash Jackpot!?" + :no-ability {:effect (effect (clear-wait-prompt :corp))} + :yes-ability + {:prompt "Choose how many [Credit] to take" + :choices {:number (req (get-counters card :credit))} + :async true + :effect (req (gain-credits state :runner target) + (system-msg state :runner (str "trashes Jackpot! to gain " target " credits")) + (clear-wait-prompt state :corp) + (trash state :runner eid card nil))}}} + card nil))}] + {:events + {:runner-turn-begins {:effect (effect (add-counter :runner card :credit 1))} + :agenda-stolen (dissoc jackpot :req) + :as-agenda jackpot}}) + + "Jak Sinclair" + (let [ability {:label "Make a run (start of turn)" + :prompt "Choose a server to run with Jak Sinclair" + :once :per-turn + :req (req (:runner-phase-12 @state)) + :choices (req runnable-servers) + :msg (msg "make a run on " target " during which no programs can be used") + :makes-run true + :effect (effect (make-run target nil card))}] + {:implementation "Doesn't prevent program use" + :flags {:runner-phase-12 (req true)} + :install-cost-bonus (req [:credit (- (:link runner))]) + :events {:runner-turn-begins + {:optional {:req (req (not (get-in @state [:per-turn (:cid card)]))) + :prompt "Use Jak Sinclair to make a run?" + :yes-ability ability}}} + :abilities [ability]}) + + "Jarogniew Mercs" + {:effect (effect (gain-tags :runner eid 1) + (add-counter card :power (+ 3 (count-tags state)))) + :flags {:untrashable-while-resources true} + :interactions {:prevent [{:type #{:meat} + :req (req true)}]} + :abilities [{:label "Prevent 1 meat damage" :counter-cost [:power 1] - :req (req (:runner-phase-12 @state)) - :async true - :effect (req (wait-for (draw state :runner 2 nil) - (if (zero? (get-counters (get-card state card) :power)) - (trash state :runner eid card {:unpreventable true}) - (effect-completed state side eid))))}] - {:flags {:runner-turn-draw true - :runner-phase-12 (req (< 1 (count (filter #(card-flag? % :runner-turn-draw true) - (cons (get-in @state [:runner :identity]) - (all-active-installed state :runner))))))} - :data {:counter {:power 3}} - :events {:runner-turn-begins ability} - :abilities [ability]})) - -(define-card "Eden Shard" - (shard-constructor :rd "force the Corp to draw 2 cards" (req (draw state :corp eid 2 nil)))) - -(define-card "Emptied Mind" - (let [ability {:req (req (zero? (count (:hand runner)))) - :msg "gain [Click]" - :label "Gain [Click] (start of turn)" + :effect (req (damage-prevent state side :meat 1) + (when (zero? (get-counters (get-card state card) :power)) + (trash state :runner card {:unpreventable true})))}]} + + "John Masanori" + {:events {:successful-run {:req (req (= 1 (count (get-in @state [:runner :register :successful-run])))) + :interactive (req true) + :msg "draw 1 card" + :async true + :effect (effect (draw eid 1 nil))} + :unsuccessful-run {:req (req (= 1 (count (get-in @state [:runner :register :unsuccessful-run])))) + :async true + :msg "take 1 tag" + :effect (effect (gain-tags :runner eid 1))}}} + + "Joshua B." + (let [ability {:msg "gain [Click]" + :once :per-turn + :label "Gain [Click] (start of turn)" + :effect (effect (gain :click 1) + (update! (assoc-in card [:special :joshua-b] true)))}] + {:flags {:runner-phase-12 (req true)} + :events {:runner-turn-begins + {:optional {:prompt "Use Joshua B. to gain [Click]?" + :once :per-turn + :yes-ability ability}} + :runner-turn-ends {:interactive (req true) + :req (req (get-in card [:special :joshua-b])) + :async true + :effect (effect (gain-tags eid 1)) + :msg "gain 1 tag"}} + :abilities [ability]}) + + "Kasi String" + {:events {:run-ends {:req (req (and (first-event? state :runner :run-ends is-remote?) + (not (get-in @state [:run :did-steal])) + (get-in @state [:run :did-access]) + (is-remote? (:server run)))) + :effect (effect (add-counter card :power 1)) + :msg "add a power counter to itself"} + :counter-added {:req (req (>= (get-counters (get-card state card) :power) 4)) + :effect (effect (as-agenda :runner card 1)) + :msg "add it to their score area as an agenda worth 1 agenda point"}}} + + "Kati Jones" + {:abilities [{:cost [:click 1] + :msg "store 3 [Credits]" :once :per-turn - :effect (effect (gain :click 1))}] - {:events {:runner-turn-begins ability} - :abilities [ability]})) - -(define-card "Enhanced Vision" - {:events {:successful-run {:silent (req true) - :effect (req (let [card (first (shuffle (:hand corp)))] - (reveal state :corp card) - (system-msg state :runner "force the Corp to reveal " (:title card)))) - :req (req (genetics-trigger? state side :successful-run))}}}) - -(define-card "Fall Guy" - {:interactions {:prevent [{:type #{:trash-resource} - :req (req true)}]} - :abilities [{:label "[Trash]: Prevent another installed resource from being trashed" - :effect (effect (trash card {:unpreventable true :cause :ability-cost}) - (trash-prevent :resource 1))} - {:label "[Trash]: Gain 2 [Credits]" - :msg "gain 2 [Credits]" - :effect (effect (trash card {:cause :ability-cost}) - (gain-credits 2))}]}) - -(define-card "Fan Site" - {:events {:agenda-scored {:msg "add it to their score area as an agenda worth 0 agenda points" - :async true - :req (req (installed? card)) - :effect (req (as-agenda state :runner eid card 0))}}}) - -(define-card "Fencer Fueno" - (companion-builder - (req (and (pos? (get-counters (get-card state card) :credit)) - (:successful run))) - (effect (show-wait-prompt :corp "Runner to take decision on Fencer Fueno") - (continue-ability - {:prompt "Pay 1 [Credits] or trash Fencer Fueno?" - :choices (req (if (can-pay? state :runner eid card nil :credit 1) - ["Pay 1 [Credits]" "Trash"] - ["Trash"])) - :player :runner - :effect (req (if (= target "Trash") - (do - (trash state :runner card) - (system-msg state :runner "trashes Fencer Fueno")) - (do - (pay state :runner card :credit 1) - (system-msg state :runner "pays 1 [Credits] to avoid trashing Fencer Fueno"))) - (clear-wait-prompt state :corp))} - card nil)) - {:msg "take 1 [Credits]" - :effect (effect (add-counter card :credit -1) - (gain-run-credits 1))})) - -(define-card "Fester" - {:events {:purge {:msg "force the Corp to lose 2 [Credits] if able" - :effect (effect (pay :corp card :credit 2))}}}) - -(define-card "Film Critic" - (letfn [(get-agenda [card] (first (filter #(= "Agenda" (:type %)) (:hosted card)))) - (host-agenda? [agenda] - {:optional {:prompt (str "You access " (:title agenda) ". Host it on Film Critic?") - :yes-ability {:effect (req (host state side card (move state side agenda :play-area)) - (access-end state side eid agenda) - (when-not (:run @state) - (swap! state dissoc :access))) - :msg (msg "host " (:title agenda) " instead of accessing it")}}})] - {:events {:access {:req (req (and (empty? (filter #(= "Agenda" (:type %)) (:hosted card))) - (agenda? target))) - :interactive (req true) - :async true - :effect (effect (continue-ability (host-agenda? target) card nil))}} - :abilities [{:cost [:click 2] :label "Add hosted agenda to your score area" - :req (req (get-agenda card)) - :async true - :effect (req (let [c (get-agenda card) - points (get-agenda-points state :runner c)] - (as-agenda state :runner eid c points))) - :msg (msg (let [c (get-agenda card)] - (str "add " (:title c) " to their score area and gain " - (quantify (get-agenda-points state :runner c) "agenda point"))))}]})) - -(define-card "Find the Truth" - {:events {:post-runner-draw {:msg (msg "reveal that they drew: " - (join ", " (map :title (get-in @state [:runner :register :most-recent-drawn])))) - :effect (effect (reveal (get-in @state [:runner :register :most-recent-drawn])))} - :successful-run {:interactive (get-autoresolve :auto-peek (complement never?)) - :silent (get-autoresolve :auto-peek never?) - :optional {:req (req (and (first-event? state side :successful-run) - (-> @state :corp :deck count pos?))) - :autoresolve (get-autoresolve :auto-peek) - :prompt "Use Find the Truth to look at the top card of R&D?" - :yes-ability {:prompt (req (->> corp :deck first :title (str "The top card of R&D is "))) - :msg "look at the top card of R&D" - :choices ["OK"]}}}} - :abilities [(set-autoresolve :auto-peek "Find the Truth's peek at R&D ability")]}) - -(define-card "First Responders" - {:abilities [{:cost [:credit 2] - :req (req (some corp? (map second (turn-events state :runner :damage)))) - :msg "draw 1 card" - :async true - :effect (effect (draw eid 1 nil))}]}) - -(define-card "Gang Sign" - {:events {:agenda-scored - {:async true - :interactive (req true) - :msg (msg "access " (quantify (get-in @state [:runner :hq-access]) "card") " from HQ") - :effect (req (wait-for - ;; manually trigger the pre-access event to alert Nerve Agent. - (trigger-event-sync state side :pre-access :hq) - (let [from-hq (access-count state side :hq-access) - ;; access-helper-hq uses a set to keep track of which cards have already - ;; been accessed. By adding HQ root's contents to this set, we make the runner - ;; unable to access those cards, as Gang Sign intends. - accessed-cards (set (get-in @state [:corp :servers :hq :content])) - ability (access-helper-hq state from-hq accessed-cards)] - (continue-ability state :runner ability card nil))))}}}) - -(define-card "Gbahali" - {:abilities [{:label "[Trash]: Break the last subroutine on the encountered piece of ice" - :req (req (and (:run @state) (rezzed? current-ice))) - :effect (effect (trash card {:cause :ability-cost}) - (system-msg :runner - (str "trashes Gbahali to break the last subroutine on " - (:title current-ice))))}]}) - -(define-card "Gene Conditioning Shoppe" - {:msg "make Genetics trigger a second time each turn" - :effect (effect (register-persistent-flag! card :genetics-trigger-twice (constantly true))) - :leave-play (effect (clear-persistent-flag! card :genetics-trigger-twice))}) - -(define-card "Ghost Runner" - {:data {:counter {:credit 3}} - :abilities [{:counter-cost [:credit 1] - :msg "gain 1 [Credits]" - :req (req (:run @state)) - :effect (req (gain-credits state side 1) - (trigger-event state side :spent-stealth-credit card) - (when (zero? (get-counters (get-card state card) :credit)) - (trash state :runner card {:unpreventable true})))}] - ; See Net Mercur for why this implementation was chosen - :interactions {:pay-credits {:req (req (:run @state)) - :type :credit}}}) - -(define-card "Globalsec Security Clearance" - {:req (req (> (:link runner) 1)) - :flags {:runner-phase-12 (req true)} - :abilities [{:msg "lose [Click] and look at the top card of R&D" - :once :per-turn - :effect (effect (prompt! card (str "The top card of R&D is " - (:title (first (:deck corp)))) ["OK"] {}))}] - :events {:runner-turn-begins {:req (req (get-in @state [:per-turn (:cid card)])) - :effect (effect (lose :click 1))}}}) - -(define-card "Grifter" - {:events {:runner-turn-ends - {:effect (req (let [ab (if (get-in @state [:runner :register :successful-run]) - {:effect (effect (gain-credits 1)) :msg "gain 1 [Credits]"} - {:effect (effect (trash card)) :msg "trash Grifter"})] - (resolve-ability state side ab card targets)))}}}) - -(define-card "Guru Davinder" - {:flags {:cannot-pay-net-damage true} - :events {:pre-damage - {:req (req (and (or (= target :meat) (= target :net)) - (pos? (last targets)))) - :msg (msg "prevent all " (if (= target :meat) "meat" "net") " damage") - :effect (req (damage-prevent state side :meat Integer/MAX_VALUE) - (damage-prevent state side :net Integer/MAX_VALUE) - (if (< (:credit runner) 4) - (trash state side card) - (resolve-ability - state :runner - {:optional - {:prompt "Pay 4 [Credits] to prevent trashing Guru Davinder?" - :player :runner - :yes-ability {:effect (effect (lose-credits :runner 4) - (system-msg (str "pays 4 [Credits] to prevent Guru Davinder " - "from being trashed")))} - :no-ability {:effect (effect (trash card))}}} - card nil)))}}}) - -(define-card "Hades Shard" - (shard-constructor :archives "access all cards in Archives" {:async true} - (req (trash state side card {:cause :ability-cost}) - (swap! state update-in [:corp :discard] #(map (fn [c] (assoc c :seen true)) %)) - (wait-for (trigger-event-sync state side :pre-access :archives) - (resolve-ability state :runner - (choose-access (get-in @state [:corp :discard]) - '(:archives) {:no-root true}) card nil))))) - -(define-card "Hard at Work" - (let [ability {:msg "gain 2 [Credits] and lose [Click]" + :effect (effect (add-counter card :credit 3))} + {:cost [:click 1] + :msg (msg "gain " (get-counters card :credit) " [Credits]") :once :per-turn - :effect (effect (lose :click 1) (gain-credits 2))}] - {:flags {:drip-economy true} - :events {:runner-turn-begins ability} - :abilities [ability]})) - -(define-card "Hernando Cortez" - {:events {:pre-rez-cost {:req (req (and (>= (:credit corp) 10) (ice? target))) - :effect (effect (rez-additional-cost-bonus - [:credit (count-num-subroutines target)])) - :msg (msg "increase the rez cost by " (count-num-subroutines target) " [Credit]")}}}) - -(define-card "Human First" - {:events {:agenda-scored {:msg (msg "gain " (get-agenda-points state :corp target) " [Credits]") - :effect (effect (gain-credits :runner (get-agenda-points state :corp target)))} - :agenda-stolen {:msg (msg "gain " (get-agenda-points state :runner target) " [Credits]") - :effect (effect (gain-credits (get-agenda-points state :runner target)))}}}) - -(define-card "Hunting Grounds" - {:abilities [{:label "Prevent a \"when encountered\" ability on a piece of ICE" - :msg "prevent a \"when encountered\" ability on a piece of ICE" - :once :per-turn} - {:label "[Trash]: Install the top 3 cards of your Stack facedown" - :msg "install the top 3 cards of their Stack facedown" - :effect (req (trash state side card {:cause :ability-cost}) - (doseq [c (take 3 (get-in @state [:runner :deck]))] - (runner-install state side c {:facedown true})))}]}) - -(define-card "Ice Analyzer" - {:implementation "Credit use restriction is not enforced" - :events {:rez {:req (req (ice? target)) - :msg "place 1 [Credits] on Ice Analyzer" - :effect (effect (add-counter :runner card :credit 1))}} - :abilities [{:counter-cost [:credit 1] - :effect (effect (gain-credits 1)) - :msg "take 1 [Credits] to install programs"}] - :interactions {:pay-credits {:req (req (and (= :runner-install (:source-type eid)) - (program? target))) - :type :credit}}}) - -(define-card "Ice Carver" - {:events {:pre-ice-strength - {:req (req (and (same-card? target current-ice) (:rezzed target))) - :effect (effect (ice-strength-bonus -1 target))}}}) - -(define-card "Inside Man" - {:recurring 2 - :interactions {:pay-credits {:req (req (and (= :runner-install (:source-type eid)) - (hardware? target))) - :type :recurring}}}) - -(define-card "Investigative Journalism" - {:req (req (has-bad-pub? state)) - :abilities [{:cost [:click 4] :msg "give the Corp 1 bad publicity" - :effect (effect (gain-bad-publicity :corp 1) - (trash card {:cause :ability-cost}))}]}) - -(define-card "Jackpot!" - (let [jackpot {:interactive (req true) + :label "Take all credits" + :effect (req (gain-credits state side (get-counters card :credit)) + (add-counter state side card :credit (- (get-counters card :credit))))}]} + + "Keros Mcintyre" + {:events + {:derez + {:req (req (and (first-event? state side :derez) + (= (second targets) :runner))) + :once :per-turn + :msg "gain 2 [Credits]" + :effect (effect (gain-credits 2))}}} + + "Kongamato" + {:abilities [{:label "[Trash]: Break the first subroutine on the encountered piece of ice" + :req (req (and (:run @state) (rezzed? current-ice))) + :effect (effect (trash card {:cause :ability-cost}) + (system-msg :runner + (str "trashes Kongamato to break the first subroutine on " + (:title current-ice))))}]} + + "Laguna Velasco District" + {:events {:pre-runner-click-draw {:msg "draw 1 additional card" + :effect (effect (draw-bonus 1))}}} + + "Levy Advanced Research Lab" + (letfn [(lab-keep [cards] + {:prompt "Choose a Program to keep" + :choices (cons "None" (filter #(= "Program" (:type %)) cards)) + :async true + :msg (msg (if (= target "None") "take no card to their Grip" (str "take " (-> target :title) " to their Grip"))) + :effect (req (when (not= target "None") + (move state side target :hand)) + (if (not-empty cards) + (let [tobottom (remove #(= % target) cards)] + (continue-ability state side (reorder-choice :runner :corp tobottom '() + (count tobottom) tobottom "bottom") card nil)) + (do (clear-wait-prompt state :corp) + (effect-completed state side eid))))})] + {:abilities [{:cost [:click 1] + :msg (msg "draw 4 cards: " (join ", " (map :title (take 4 (:deck runner))))) + :async true + :effect (req (show-wait-prompt state :corp "Runner to choose card to keep") + (let [from (take 4 (:deck runner))] + (continue-ability state side (lab-keep from) card nil)))}]}) + + "Lewi Guilherme" + (let [ability {:once :per-turn + :optional {:once :per-turn + :prompt "Pay 1 [Credits] to keep Lewi Guilherme?" + :yes-ability {:effect (req (if (pos? (:credit runner)) + (do (lose-credits state side 1) + (system-msg state side "pays 1 [Credits] to keep Lewi Guilherme")) + (do (trash state side card) + (system-msg state side "must trash Lewi Guilherme"))))} + :no-ability {:effect (effect (trash card) + (system-msg "chooses to trash Lewi Guilherme"))}}}] + {:flags {:drip-economy true ;; for Drug Dealer + :runner-phase-12 (req (< 1 (count (filter #(card-flag? % :drip-economy true) + (all-active-installed state :runner)))))} + ;; KNOWN ISSUE: :effect is not fired when Assimilator turns cards over or Dr. Lovegood re-enables it. + :effect (effect (lose :corp :hand-size 1)) + :leave-play (effect (gain :corp :hand-size 1)) + :abilities [(assoc-in ability [:req] (req (:runner-phase-12 @state)))] + :events {:runner-turn-begins ability}}) + + "Liberated Account" + {:data {:counter {:credit 16}} + :abilities [{:cost [:click 1] + :counter-cost [:credit 4] + :msg "gain 4 [Credits]" + :effect (req (gain-credits state :runner 4) + (when (zero? (get-counters (get-card state card) :credit)) + (trash state :runner card {:unpreventable true})))}]} + + "Liberated Chela" + {:abilities [{:cost [:click 5 :forfeit] + :msg "add it to their score area" + :async true + :effect (req (if (not (empty? (:scored corp))) + (do (show-wait-prompt state :runner "Corp to decide whether or not to prevent Liberated Chela") + (continue-ability + state side + {:prompt (msg "Forfeit an agenda to prevent Liberated Chela from being added to Runner's score area?") + :choices ["Yes" "No"] + :player :corp + :async true + :effect (effect (continue-ability + (if (= target "Yes") + {:player :corp + :prompt "Select an agenda to forfeit" + :choices {:req #(in-corp-scored? state side %)} + :effect (effect (forfeit target) + (move :runner card :rfg) + (clear-wait-prompt :runner))} + {:async true + :effect (req (clear-wait-prompt state :runner) + (as-agenda state :runner eid card 2)) + :msg "add it to their score area as an agenda worth 2 points"}) + card nil))} card nil)) + (continue-ability + state side + {:async true + :effect (req (as-agenda state :runner eid card 2)) + :msg "add it to their score area as an agenda worth 2 points"} card nil)))}]} + + "Logic Bomb" + {:implementation "Bypass effect is manual" + :abilities [{:label "Bypass the encountered ice" + :req (req (and (:run @state) + (rezzed? current-ice))) + :msg (msg "bypass " + (:title current-ice) + (when (pos? (:click runner)) + (str " and loses " + (apply str (repeat (:click runner) "[Click]"))))) + :effect (effect (trash card {:cause :ability-cost}) + (lose :click (:click runner)))}]} + + "London Library" + {:abilities [{:label "Install a non-virus program on London Library" + :cost [:click 1] + :prompt "Select a non-virus program to install on London Library from your grip" + :choices {:req #(and (program? %) + (not (has-subtype? % "Virus")) + (in-hand? %))} + :msg (msg "host " (:title target)) + :effect (effect (runner-install target {:host-card card + :ignore-install-cost true}))} + {:label "Add a program hosted on London Library to your Grip" + :cost [:click 1] + :choices {:req #(:host %)} ;TODO: this seems to allow all hosted cards to be bounced + :msg (msg "add " (:title target) " to their Grip") + :effect (effect (move target :hand))}] + :events {:runner-turn-ends {:effect (req (doseq [c (:hosted card)] + (when (program? c) + (trash state side c))))}}} + + "Maxwell James" + {:in-play [:link 1] + :abilities [{:req (req (some #{:hq} (:successful-run runner-reg))) + :prompt "Choose a piece of ICE protecting a remote server" + :choices {:req #(and (ice? %) (rezzed? %) (is-remote? (second (:zone %))))} + :msg "derez a piece of ICE protecting a remote server" + :effect (effect (derez target) + (trash card {:cause :ability-cost}))}]} + + "Miss Bones" + {:data {:counter {:credit 12}} + :interactions {:pay-credits {:req (req (and (= :runner-trash-corp-cards (:source-type eid)) + (installed? target))) + :type :credit}} + :abilities [{:counter-cost [:credit 1] + :msg "gain 1 [Credits] for trashing installed cards" + :async true + :effect (req (gain-credits state :runner 1) + (if (zero? (get-counters (get-card state card) :credit)) + (trash state :runner eid card {:unpreventable true}) + (effect-completed state :runner eid)))}]} + + "Motivation" + (let [ability {:msg "look at the top card of their Stack" + :label "Look at the top card of Stack (start of turn)" + :once :per-turn + :req (req (:runner-phase-12 @state)) + :effect (effect (prompt! card (str "The top card of your Stack is " + (:title (first (:deck runner)))) ["OK"] {}))}] + {:flags {:runner-turn-draw true + :runner-phase-12 (req (some #(card-flag? % :runner-turn-draw true) (all-active-installed state :runner)))} + :events {:runner-turn-begins ability} + :abilities [ability]}) + + "Mr. Li" + {:abilities [{:cost [:click 1] + :msg (msg "draw 2 cards") :async true - :req (req (= :runner (:as-agenda-side target))) - :effect (req (show-wait-prompt state :corp "Runner to use Jackpot!") + :effect (req (wait-for (draw state side 2 nil) + (if-let [drawn (get-in @state [:runner :register :most-recent-drawn])] + (continue-ability + state side + {:prompt "Select 1 card to add to the bottom of the Stack" + :choices {:req #(and (in-hand? %) + (some (fn [c] (same-card? c %)) drawn))} + :msg (msg "add 1 card to the bottom of the Stack") + :effect (req (move state side target :deck))} + card nil) + (effect-completed state side eid))))}]} + + "Muertos Gang Member" + {:effect (req (resolve-ability + state :corp + {:prompt "Select a card to derez" + :choices {:req #(and (corp? %) + (not (agenda? %)) + (:rezzed %))} + :effect (req (derez state side target))} + card nil)) + :leave-play (req (resolve-ability + state :corp + {:prompt "Select a card to rez, ignoring the rez cost" + :choices {:req #(not (:rezzed %))} + :effect (req (rez state side target {:ignore-cost :rez-cost}) + (system-msg state side (str "rezzes " (:title target) " at no cost")))} + card nil)) + :abilities [{:msg "draw 1 card" + :async true + :effect (effect (trash card {:cause :ability-cost}) (draw eid 1 nil))}]} + + "Net Mercur" + {:abilities [{:counter-cost [:credit 1] + :msg "gain 1 [Credits]" + :effect (effect (gain-credits 1) + (trigger-event :spent-stealth-credit card))}] + :events {:spent-stealth-credit + {:req (req (and (:run @state) + (has-subtype? target "Stealth"))) + :once :per-run + :async true + :effect (effect (show-wait-prompt :corp "Runner to use Net Mercur") (continue-ability - state side - {:optional - {:prompt "Trash Jackpot!?" - :no-ability {:effect (effect (clear-wait-prompt :corp))} - :yes-ability - {:prompt "Choose how many [Credit] to take" - :choices {:number (req (get-counters card :credit))} - :async true - :effect (req (gain-credits state :runner target) - (system-msg state :runner (str "trashes Jackpot! to gain " target " credits")) - (clear-wait-prompt state :corp) - (trash state :runner eid card nil))}}} - card nil))}] - {:events - {:runner-turn-begins {:effect (effect (add-counter :runner card :credit 1))} - :agenda-stolen (dissoc jackpot :req) - :as-agenda jackpot}})) - -(define-card "Jak Sinclair" - (let [ability {:label "Make a run (start of turn)" - :prompt "Choose a server to run with Jak Sinclair" - :once :per-turn - :req (req (:runner-phase-12 @state)) - :choices (req runnable-servers) - :msg (msg "make a run on " target " during which no programs can be used") - :makes-run true - :effect (effect (make-run target nil card))}] - {:implementation "Doesn't prevent program use" - :flags {:runner-phase-12 (req true)} - :install-cost-bonus (req [:credit (- (:link runner))]) - :events {:runner-turn-begins - {:optional {:req (req (not (get-in @state [:per-turn (:cid card)]))) - :prompt "Use Jak Sinclair to make a run?" - :yes-ability ability}}} - :abilities [ability]})) - -(define-card "Jarogniew Mercs" - {:effect (effect (gain-tags :runner eid 1) - (add-counter card :power (+ 3 (count-tags state)))) - :flags {:untrashable-while-resources true} - :interactions {:prevent [{:type #{:meat} - :req (req true)}]} - :abilities [{:label "Prevent 1 meat damage" - :counter-cost [:power 1] - :effect (req (damage-prevent state side :meat 1) - (when (zero? (get-counters (get-card state card) :power)) - (trash state :runner card {:unpreventable true})))}]}) - -(define-card "John Masanori" - {:events {:successful-run {:req (req (= 1 (count (get-in @state [:runner :register :successful-run])))) - :interactive (req true) - :msg "draw 1 card" - :async true - :effect (effect (draw eid 1 nil))} - :unsuccessful-run {:req (req (= 1 (count (get-in @state [:runner :register :unsuccessful-run])))) - :async true - :msg "take 1 tag" - :effect (effect (gain-tags :runner eid 1))}}}) - -(define-card "Joshua B." - (let [ability {:msg "gain [Click]" - :once :per-turn - :label "Gain [Click] (start of turn)" - :effect (effect (gain :click 1) - (update! (assoc-in card [:special :joshua-b] true)))}] - {:flags {:runner-phase-12 (req true)} - :events {:runner-turn-begins - {:optional {:prompt "Use Joshua B. to gain [Click]?" - :once :per-turn - :yes-ability ability}} - :runner-turn-ends {:interactive (req true) - :req (req (get-in card [:special :joshua-b])) + {:prompt "Place 1 [Credits] on Net Mercur or draw 1 card?" + :player :runner + :choices ["Place 1 [Credits]" "Draw 1 card"] :async true - :effect (effect (gain-tags eid 1)) - :msg "gain 1 tag"}} - :abilities [ability]})) - -(define-card "Kasi String" - {:events {:run-ends {:req (req (and (first-event? state :runner :run-ends is-remote?) - (not (get-in @state [:run :did-steal])) - (get-in @state [:run :did-access]) - (is-remote? (:server run)))) - :effect (effect (add-counter card :power 1)) - :msg "add a power counter to itself"} - :counter-added {:req (req (>= (get-counters (get-card state card) :power) 4)) - :effect (effect (as-agenda :runner card 1)) - :msg "add it to their score area as an agenda worth 1 agenda point"}}}) - -(define-card "Kati Jones" - {:abilities [{:cost [:click 1] - :msg "store 3 [Credits]" - :once :per-turn - :effect (effect (add-counter card :credit 3))} - {:cost [:click 1] - :msg (msg "gain " (get-counters card :credit) " [Credits]") - :once :per-turn - :label "Take all credits" - :effect (req (gain-credits state side (get-counters card :credit)) - (add-counter state side card :credit (- (get-counters card :credit))))}]}) - -(define-card "Keros Mcintyre" - {:events - {:derez - {:req (req (and (first-event? state side :derez) - (= (second targets) :runner))) - :once :per-turn - :msg "gain 2 [Credits]" - :effect (effect (gain-credits 2))}}}) - -(define-card "Kongamato" - {:abilities [{:label "[Trash]: Break the first subroutine on the encountered piece of ice" - :req (req (and (:run @state) (rezzed? current-ice))) - :effect (effect (trash card {:cause :ability-cost}) - (system-msg :runner - (str "trashes Kongamato to break the first subroutine on " - (:title current-ice))))}]}) - -(define-card "Laguna Velasco District" - {:events {:pre-runner-click-draw {:msg "draw 1 additional card" - :effect (effect (draw-bonus 1))}}}) - -(define-card "Levy Advanced Research Lab" - (letfn [(lab-keep [cards] - {:prompt "Choose a Program to keep" - :choices (cons "None" (filter #(= "Program" (:type %)) cards)) - :async true - :msg (msg (if (= target "None") "take no card to their Grip" (str "take " (-> target :title) " to their Grip"))) - :effect (req (when (not= target "None") - (move state side target :hand)) - (if (not-empty cards) - (let [tobottom (remove #(= % target) cards)] - (continue-ability state side (reorder-choice :runner :corp tobottom '() - (count tobottom) tobottom "bottom") card nil)) - (do (clear-wait-prompt state :corp) - (effect-completed state side eid))))})] - {:abilities [{:cost [:click 1] - :msg (msg "draw 4 cards: " (join ", " (map :title (take 4 (:deck runner))))) - :async true - :effect (req (show-wait-prompt state :corp "Runner to choose card to keep") - (let [from (take 4 (:deck runner))] - (continue-ability state side (lab-keep from) card nil)))}]})) - -(define-card "Lewi Guilherme" - (let [ability {:once :per-turn - :optional {:once :per-turn - :prompt "Pay 1 [Credits] to keep Lewi Guilherme?" - :yes-ability {:effect (req (if (pos? (:credit runner)) - (do (lose-credits state side 1) - (system-msg state side "pays 1 [Credits] to keep Lewi Guilherme")) - (do (trash state side card) - (system-msg state side "must trash Lewi Guilherme"))))} - :no-ability {:effect (effect (trash card) - (system-msg "chooses to trash Lewi Guilherme"))}}}] - {:flags {:drip-economy true ;; for Drug Dealer - :runner-phase-12 (req (< 1 (count (filter #(card-flag? % :drip-economy true) - (all-active-installed state :runner)))))} - ;; KNOWN ISSUE: :effect is not fired when Assimilator turns cards over or Dr. Lovegood re-enables it. - :effect (effect (lose :corp :hand-size 1)) - :leave-play (effect (gain :corp :hand-size 1)) - :abilities [(assoc-in ability [:req] (req (:runner-phase-12 @state)))] - :events {:runner-turn-begins ability}})) - -(define-card "Liberated Account" - {:data {:counter {:credit 16}} - :abilities [{:cost [:click 1] - :counter-cost [:credit 4] - :msg "gain 4 [Credits]" - :effect (req (gain-credits state :runner 4) - (when (zero? (get-counters (get-card state card) :credit)) - (trash state :runner card {:unpreventable true})))}]}) - -(define-card "Liberated Chela" - {:abilities [{:cost [:click 5 :forfeit] - :msg "add it to their score area" - :async true - :effect (req (if (not (empty? (:scored corp))) - (do (show-wait-prompt state :runner "Corp to decide whether or not to prevent Liberated Chela") - (continue-ability - state side - {:prompt (msg "Forfeit an agenda to prevent Liberated Chela from being added to Runner's score area?") - :choices ["Yes" "No"] - :player :corp - :async true - :effect (effect (continue-ability - (if (= target "Yes") - {:player :corp - :prompt "Select an agenda to forfeit" - :choices {:req #(in-corp-scored? state side %)} - :effect (effect (forfeit target) - (move :runner card :rfg) - (clear-wait-prompt :runner))} - {:async true - :effect (req (clear-wait-prompt state :runner) - (as-agenda state :runner eid card 2)) - :msg "add it to their score area as an agenda worth 2 points"}) - card nil))} card nil)) - (continue-ability - state side - {:async true - :effect (req (as-agenda state :runner eid card 2)) - :msg "add it to their score area as an agenda worth 2 points"} card nil)))}]}) - -(define-card "Logic Bomb" - {:implementation "Bypass effect is manual" - :abilities [{:label "Bypass the encountered ice" - :req (req (and (:run @state) - (rezzed? current-ice))) - :msg (msg "bypass " - (:title current-ice) - (when (pos? (:click runner)) - (str " and loses " - (apply str (repeat (:click runner) "[Click]"))))) - :effect (effect (trash card {:cause :ability-cost}) - (lose :click (:click runner)))}]}) - -(define-card "London Library" - {:abilities [{:label "Install a non-virus program on London Library" - :cost [:click 1] - :prompt "Select a non-virus program to install on London Library from your grip" - :choices {:req #(and (program? %) - (not (has-subtype? % "Virus")) - (in-hand? %))} - :msg (msg "host " (:title target)) - :effect (effect (runner-install target {:host-card card - :ignore-install-cost true}))} - {:label "Add a program hosted on London Library to your Grip" - :cost [:click 1] - :choices {:req #(:host %)} ;TODO: this seems to allow all hosted cards to be bounced - :msg (msg "add " (:title target) " to their Grip") - :effect (effect (move target :hand))}] - :events {:runner-turn-ends {:effect (req (doseq [c (:hosted card)] - (when (program? c) - (trash state side c))))}}}) - -(define-card "Maxwell James" - {:in-play [:link 1] - :abilities [{:req (req (some #{:hq} (:successful-run runner-reg))) - :prompt "Choose a piece of ICE protecting a remote server" - :choices {:req #(and (ice? %) (rezzed? %) (is-remote? (second (:zone %))))} - :msg "derez a piece of ICE protecting a remote server" - :effect (effect (derez target) - (trash card {:cause :ability-cost}))}]}) - -(define-card "Miss Bones" - {:data {:counter {:credit 12}} - :interactions {:pay-credits {:req (req (and (= :runner-trash-corp-cards (:source-type eid)) - (installed? target))) - :type :credit}} - :abilities [{:counter-cost [:credit 1] - :msg "gain 1 [Credits] for trashing installed cards" - :async true - :effect (req (gain-credits state :runner 1) - (if (zero? (get-counters (get-card state card) :credit)) - (trash state :runner eid card {:unpreventable true}) - (effect-completed state :runner eid)))}]}) - -(define-card "Motivation" - (let [ability {:msg "look at the top card of their Stack" - :label "Look at the top card of Stack (start of turn)" + :effect (req (if (= target "Draw 1 card") + (do (clear-wait-prompt state :corp) + (system-msg state :runner (str "uses Net Mercur to draw 1 card")) + (draw state side eid 1 nil)) + (do (clear-wait-prompt state :corp) + (system-msg state :runner (str "places 1 [Credits] on Net Mercur")) + (add-counter state :runner card :credit 1) + (effect-completed state side eid))))} + card nil))}} + ; Normally this should be (req true), but having pay-credits prompts on + ; literally every interaction would get tiresome. Therefore Net Mercur will + ; only ask for payments during a run and on traces. + :interactions {:pay-credits {:req (req (or (:run @state) + (= :trace (:source-type eid)))) + :type :credit}}} + + "Network Exchange" + {:msg "increase the install cost of non-innermost ICE by 1" + :events {:pre-corp-install {:req (req (ice? target)) + :effect (req (when (pos? (count (:dest-zone (second targets)))) + (install-cost-bonus state :corp [:credit 1])))}}} + + "Neutralize All Threats" + {:in-play [:hq-access 1] + :events {:pre-access {:req (req (and (= target :archives) + (seq (filter :trash (:discard corp))))) + :effect (req (swap! state assoc-in [:per-turn (:cid card)] true))} + :pre-trash {:req (req (let [cards (map first (turn-events state side :pre-trash))] + (and (empty? (filter :trash cards)) + (number? (:trash target))))) + :once :per-turn + :effect (req (swap! state assoc-in [:runner :register :must-trash-with-credits] true))} + :post-access-card {:req (req (get-in @state [:runner :register :must-trash-with-credits])) + :effect (req (swap! state assoc-in [:runner :register :must-trash-with-credits] false))}}} + + "New Angeles City Hall" + {:interactions {:prevent [{:type #{:tag} + :req (req true)}]} + :events {:agenda-stolen {:msg "trash itself" + :effect (effect (trash card))}} + :abilities [{:cost [:credit 2] + :msg "avoid 1 tag" + :effect (effect (tag-prevent :runner 1))}]} + + "No One Home" + (letfn [(first-chance? [state side] + (< (+ (event-count state side :pre-tag) + (event-count state side :pre-damage)) + 2)) + (start-trace [type] + (let [message (str "avoid any " (if (= type :net) + "amount of net damage" + "number of tags"))] + {:player :corp + :label (str "Trace 0 - if unsuccessful, " message) + :trace {:base 0 + :priority 11 + :unsuccessful {:msg message + :effect (req (if (= type :net) + (damage-prevent state side :net Integer/MAX_VALUE) + (tag-prevent state :runner Integer/MAX_VALUE)))}}}))] + {:interactions {:prevent [{:type #{:net :tag} + :req (req (first-chance? state side))}]} + :abilities [{:msg "force the Corp to trace" + :async true + :effect (req (let [type (get-in @state [:prevent :current])] + (wait-for (trash state side card {:unpreventable true}) + (continue-ability state side (start-trace type) + card nil))))}]}) + + "Off-Campus Apartment" + {:flags {:runner-install-draw true} + :abilities [{:label "Install and host a connection on Off-Campus Apartment" + :effect (effect (resolve-ability + {:cost [:click 1] + :prompt "Select a connection in your Grip to install on Off-Campus Apartment" + :choices {:req #(and (has-subtype? % "Connection") + (can-pay? state side eid card nil :credit (:cost %)) + (in-hand? %))} + :msg (msg "host " (:title target) " and draw 1 card") + :effect (effect (runner-install target {:host-card card}))} + card nil))} + {:label "Host an installed connection" + :prompt "Select a connection to host on Off-Campus Apartment" + :choices {:req #(and (has-subtype? % "Connection") + (installed? %))} + :msg (msg "host " (:title target) " and draw 1 card") + :async true + :effect (effect (host card target) (draw eid 1 nil))}] + :events {:runner-install {:req (req (same-card? card (:host target))) + :async true + :effect (effect (draw eid 1 nil))}}} + + "Officer Frank" + {:abilities [{:cost [:credit 1] + :req (req (some #(= :meat %) (map first (turn-events state :runner :damage)))) + :msg "force the Corp to trash 2 random cards from HQ" + :effect (effect (trash-cards :corp (take 2 (shuffle (:hand corp)))) + (trash card {:cause :ability-cost}))}]} + + "Oracle May" + {:abilities [{:cost [:click 1] :once :per-turn - :req (req (:runner-phase-12 @state)) - :effect (effect (prompt! card (str "The top card of your Stack is " - (:title (first (:deck runner)))) ["OK"] {}))}] - {:flags {:runner-turn-draw true - :runner-phase-12 (req (some #(card-flag? % :runner-turn-draw true) (all-active-installed state :runner)))} - :events {:runner-turn-begins ability} - :abilities [ability]})) - -(define-card "Mr. Li" - {:abilities [{:cost [:click 1] - :msg (msg "draw 2 cards") - :async true - :effect (req (wait-for (draw state side 2 nil) - (if-let [drawn (get-in @state [:runner :register :most-recent-drawn])] - (continue-ability - state side - {:prompt "Select 1 card to add to the bottom of the Stack" - :choices {:req #(and (in-hand? %) - (some (fn [c] (same-card? c %)) drawn))} - :msg (msg "add 1 card to the bottom of the Stack") - :effect (req (move state side target :deck))} - card nil) - (effect-completed state side eid))))}]}) - -(define-card "Muertos Gang Member" - {:effect (req (resolve-ability - state :corp - {:prompt "Select a card to derez" - :choices {:req #(and (corp? %) - (not (agenda? %)) - (:rezzed %))} - :effect (req (derez state side target))} - card nil)) - :leave-play (req (resolve-ability - state :corp - {:prompt "Select a card to rez, ignoring the rez cost" - :choices {:req #(not (:rezzed %))} - :effect (req (rez state side target {:ignore-cost :rez-cost}) - (system-msg state side (str "rezzes " (:title target) " at no cost")))} - card nil)) - :abilities [{:msg "draw 1 card" - :async true - :effect (effect (trash card {:cause :ability-cost}) (draw eid 1 nil))}]}) - -(define-card "Net Mercur" - {:abilities [{:counter-cost [:credit 1] - :msg "gain 1 [Credits]" - :effect (effect (gain-credits 1) - (trigger-event :spent-stealth-credit card))}] - :events {:spent-stealth-credit - {:req (req (and (:run @state) - (has-subtype? target "Stealth"))) - :once :per-run - :async true - :effect (effect (show-wait-prompt :corp "Runner to use Net Mercur") - (continue-ability - {:prompt "Place 1 [Credits] on Net Mercur or draw 1 card?" - :player :runner - :choices ["Place 1 [Credits]" "Draw 1 card"] + :prompt "Choose card type" + :choices ["Event" "Hardware" "Program" "Resource"] + :async true + :effect (req (let [c (first (get-in @state [:runner :deck]))] + (system-msg state side (str "spends [Click] to use Oracle May, names " target + " and reveals " (:title c))) + (reveal state side c) + (if (is-type? c target) + (do (system-msg state side (str "gains 2 [Credits] and draws " (:title c))) + (gain-credits state side 2) + (draw state side eid 1 nil)) + (do (system-msg state side (str "trashes " (:title c))) + (mill state side) + (effect-completed state side eid)))))}]} + + "Order of Sol" + {:effect (req (add-watch state :order-of-sol + (fn [k ref old new] + (when (and (not (zero? (get-in old [:runner :credit]))) + (zero? (get-in new [:runner :credit]))) + (resolve-ability ref side {:msg "gain 1 [Credits]" + :once :per-turn + :effect (effect (gain-credits 1))} + card nil))))) + :events {:runner-turn-begins {:req (req (zero? (:credit runner))) + :msg "gain 1 [Credits]" + :effect (req (gain-credits state :runner 1) + (swap! state assoc-in [:per-turn (:cid card)] true))} + :corp-turn-begins {:req (req (zero? (:credit runner))) + :msg "gain 1 [Credits]" + :effect (req (gain-credits state :runner 1) + (swap! state assoc-in [:per-turn (:cid card)] true))} + :runner-install {:silent (req (pos? (:credit runner))) + :req (req (and (= target card) + (zero? (:credit runner)))) + :msg "gain 1 [Credits]" + :effect (req (gain-credits state :runner 1) + (swap! state assoc-in [:per-turn (:cid card)] true))}} + :leave-play (req (remove-watch state :order-of-sol))} + + "PAD Tap" + {:events {:corp-credit-gain + {:req (req (and (not= target :corp-click-credit) + (= 1 (->> (turn-events state :corp :corp-credit-gain) + (remove #(= (first %) :corp-click-credit)) + count)))) + :msg "gain 1 [Credits]" + :effect (effect (gain-credits :runner 1))}} + :corp-abilities [{:label "Trash PAD Tap" + :cost [:credit 3 :click 1] + :req (req (= :corp side)) + :effect (effect (system-msg :corp "spends [Click] and 3 [Credits] to trash PAD Tap") + (trash :corp card))}]} + + "Paige Piper" + (letfn [(pphelper [title cards] + {:optional + {:prompt (str "Use Paige Piper to trash copies of " title "?") + :yes-ability {:prompt "How many would you like to trash?" + :choices (take (inc (count cards)) ["0" "1" "2" "3" "4" "5"]) + :msg "shuffle their Stack" + :effect (req (let [target (str->int target)] + (trigger-event state side :searched-stack nil) + (shuffle! state :runner :deck) + (doseq [c (take target cards)] + (trash state side c {:unpreventable true})) + (when (pos? target) + (system-msg state side (str "trashes " + (quantify target "cop" "y" "ies") + " of " title)))))}}})] + {:events {:runner-install {:req (req (first-event? state side :runner-install)) :async true - :effect (req (if (= target "Draw 1 card") - (do (clear-wait-prompt state :corp) - (system-msg state :runner (str "uses Net Mercur to draw 1 card")) - (draw state side eid 1 nil)) - (do (clear-wait-prompt state :corp) - (system-msg state :runner (str "places 1 [Credits] on Net Mercur")) - (add-counter state :runner card :credit 1) - (effect-completed state side eid))))} - card nil))}} - ; Normally this should be (req true), but having pay-credits prompts on - ; literally every interaction would get tiresome. Therefore Net Mercur will - ; only ask for payments during a run and on traces. - :interactions {:pay-credits {:req (req (or (:run @state) - (= :trace (:source-type eid)))) - :type :credit}}}) - -(define-card "Network Exchange" - {:msg "increase the install cost of non-innermost ICE by 1" - :events {:pre-corp-install {:req (req (ice? target)) - :effect (req (when (pos? (count (:dest-zone (second targets)))) - (install-cost-bonus state :corp [:credit 1])))}}}) - -(define-card "Neutralize All Threats" - {:in-play [:hq-access 1] - :events {:pre-access {:req (req (and (= target :archives) - (seq (filter :trash (:discard corp))))) - :effect (req (swap! state assoc-in [:per-turn (:cid card)] true))} - :pre-trash {:req (req (let [cards (map first (turn-events state side :pre-trash))] - (and (empty? (filter :trash cards)) - (number? (:trash target))))) - :once :per-turn - :effect (req (swap! state assoc-in [:runner :register :must-trash-with-credits] true))} - :post-access-card {:req (req (get-in @state [:runner :register :must-trash-with-credits])) - :effect (req (swap! state assoc-in [:runner :register :must-trash-with-credits] false))}}}) - -(define-card "New Angeles City Hall" - {:interactions {:prevent [{:type #{:tag} - :req (req true)}]} - :events {:agenda-stolen {:msg "trash itself" - :effect (effect (trash card))}} - :abilities [{:cost [:credit 2] - :msg "avoid 1 tag" - :effect (effect (tag-prevent :runner 1))}]}) - -(define-card "No One Home" - (letfn [(first-chance? [state side] - (< (+ (event-count state side :pre-tag) - (event-count state side :pre-damage)) - 2)) - (start-trace [type] - (let [message (str "avoid any " (if (= type :net) - "amount of net damage" - "number of tags"))] - {:player :corp - :label (str "Trace 0 - if unsuccessful, " message) - :trace {:base 0 - :priority 11 - :unsuccessful {:msg message - :effect (req (if (= type :net) - (damage-prevent state side :net Integer/MAX_VALUE) - (tag-prevent state :runner Integer/MAX_VALUE)))}}}))] - {:interactions {:prevent [{:type #{:net :tag} - :req (req (first-chance? state side))}]} - :abilities [{:msg "force the Corp to trace" - :async true - :effect (req (let [type (get-in @state [:prevent :current])] - (wait-for (trash state side card {:unpreventable true}) - (continue-ability state side (start-trace type) - card nil))))}]})) - -(define-card "Off-Campus Apartment" - {:flags {:runner-install-draw true} - :abilities [{:label "Install and host a connection on Off-Campus Apartment" - :effect (effect (resolve-ability - {:cost [:click 1] - :prompt "Select a connection in your Grip to install on Off-Campus Apartment" - :choices {:req #(and (has-subtype? % "Connection") - (can-pay? state side eid card nil :credit (:cost %)) - (in-hand? %))} - :msg (msg "host " (:title target) " and draw 1 card") - :effect (effect (runner-install target {:host-card card}))} - card nil))} - {:label "Host an installed connection" - :prompt "Select a connection to host on Off-Campus Apartment" - :choices {:req #(and (has-subtype? % "Connection") - (installed? %))} - :msg (msg "host " (:title target) " and draw 1 card") - :async true - :effect (effect (host card target) (draw eid 1 nil))}] - :events {:runner-install {:req (req (same-card? card (:host target))) - :async true - :effect (effect (draw eid 1 nil))}}}) - -(define-card "Officer Frank" - {:abilities [{:cost [:credit 1] - :req (req (some #(= :meat %) (map first (turn-events state :runner :damage)))) - :msg "force the Corp to trash 2 random cards from HQ" - :effect (effect (trash-cards :corp (take 2 (shuffle (:hand corp)))) - (trash card {:cause :ability-cost}))}]}) - -(define-card "Oracle May" - {:abilities [{:cost [:click 1] + :effect (effect (continue-ability + (pphelper (:title target) + (->> (:deck runner) + (filter #(has? % :title (:title target))) + (vec))) + card nil))}}}) + + "Paparazzi" + {:effect (req (swap! state update-in [:runner :tag :is-tagged] inc) + (trigger-event state :runner :runner-is-tagged true)) + :events {:pre-damage {:req (req (= target :meat)) :msg "prevent all meat damage" + :effect (effect (damage-prevent :meat Integer/MAX_VALUE))}} + :leave-play (req (swap! state update-in [:runner :tag :is-tagged] dec) + (trigger-event state :runner :runner-is-tagged (pos? (get-in @state [:runner :tag :is-tagged]))))} + + "Patron" + (let [ability {:prompt "Choose a server for Patron" :choices (req (conj servers "No server")) + :req (req (and (not (click-spent? :runner state)) (not (used-this-turn? (:cid card) state)))) + :msg (msg "target " target) + :effect (req (when (not= target "No server") + (update! state side (assoc card :server-target target))))}] + {:events {:runner-turn-begins ability + :successful-run + {:req (req (= (zone->name (get-in @state [:run :server])) (:server-target (get-card state card)))) :once :per-turn - :prompt "Choose card type" - :choices ["Event" "Hardware" "Program" "Resource"] - :async true - :effect (req (let [c (first (get-in @state [:runner :deck]))] - (system-msg state side (str "spends [Click] to use Oracle May, names " target - " and reveals " (:title c))) - (reveal state side c) - (if (is-type? c target) - (do (system-msg state side (str "gains 2 [Credits] and draws " (:title c))) - (gain-credits state side 2) - (draw state side eid 1 nil)) - (do (system-msg state side (str "trashes " (:title c))) - (mill state side) - (effect-completed state side eid)))))}]}) - -(define-card "Order of Sol" - {:effect (req (add-watch state :order-of-sol - (fn [k ref old new] - (when (and (not (zero? (get-in old [:runner :credit]))) - (zero? (get-in new [:runner :credit]))) - (resolve-ability ref side {:msg "gain 1 [Credits]" - :once :per-turn - :effect (effect (gain-credits 1))} - card nil))))) - :events {:runner-turn-begins {:req (req (zero? (:credit runner))) + :effect (req (let [st card] + (swap! state assoc-in [:run :run-effect :replace-access] + {:mandatory true + :effect (effect (resolve-ability + {:msg "draw 2 cards instead of accessing" + :async true + :effect (effect (update! (dissoc st :server-target)) + (draw eid 2 nil))} + st nil))})))} + :runner-turn-ends {:effect (effect (update! (dissoc card :server-target)))}} + :abilities [ability]}) + + "Personal Workshop" + (let [remove-counter + {:req (req (not (empty? (:hosted card)))) + :once :per-turn + :msg (msg "remove 1 counter from " (:title target)) + :choices {:req #(:host %)} + :effect (req (if (zero? (get-counters (get-card state target) :power)) + (runner-install state side (dissoc target :counter) {:ignore-all-cost true}) + (add-counter state side target :power -1)))}] + {:flags {:drip-economy true} + :abilities [{:label "Host a program or piece of hardware" :cost [:click 1] + :prompt "Select a card to host on Personal Workshop" + :choices {:req #(and (#{"Program" "Hardware"} (:type %)) + (in-hand? %) + (runner? %))} + :effect (req (if (zero? (:cost target)) + (runner-install state side target) + (host state side card + (assoc target :counter {:power (:cost target)})))) + :msg (msg "host " (:title target) "")} + (assoc remove-counter + :label "Remove 1 counter from a hosted card (start of turn)" + :cost [:credit 1]) + {:label "X[Credit]: Remove counters from a hosted card" + :choices {:req #(:host %)} + :req (req (not (empty? (:hosted card)))) + :effect (req (let [paydowntarget target + num-counters (get-counters (get-card state paydowntarget) :power)] + (resolve-ability + state side + {:prompt "How many counters to remove?" + :choices {:number (req (min (:credit runner) + num-counters))} + :msg (msg "remove " target " counters from " (:title paydowntarget)) + :effect (req (do + (lose-credits state side target) + (if (= num-counters target) + (runner-install state side (dissoc paydowntarget :counter) {:ignore-all-cost true}) + (add-counter state side paydowntarget :power (- target)))))} + card nil)))}] + :events {:runner-turn-begins remove-counter}}) + + "Political Operative" + {:req (req (some #{:hq} (:successful-run runner-reg))) + :abilities [{:prompt "Select a rezzed card with a trash cost" + :choices {:req #(and (:trash %) + (rezzed? %))} + :effect (req (let [cost (modified-trash-cost state :runner target)] + (when (can-pay? state side eid card nil [:credit cost]) + (resolve-ability + state side + {:msg (msg "pay " cost " [Credit] and trash " (:title target)) + :effect (effect (lose-credits cost) + (trash card {:cause :ability-cost}) + (trash target))} + card targets))))}]} + + "Power Tap" + {:events {:pre-init-trace {:msg "gain 1 [Credits]" + :effect (effect (gain-credits :runner 1))}}} + + "Professional Contacts" + {:abilities [{:cost [:click 1] + :msg "gain 1 [Credits] and draw 1 card" + :async true + :effect (effect (gain-credits 1) + (draw eid 1 nil))}]} + + "Psych Mike" + {:events {:successful-run-ends + {:req (req (first-event? state side :successful-run-ends #(= :rd (first (:server (first %)))))) + :msg (msg "gain " (total-cards-accessed target :deck) " [Credits]") + :effect (effect (gain-credits :runner (total-cards-accessed target :deck)))}}} + + "Public Sympathy" + {:in-play [:hand-size 2]} + + "Rachel Beckman" + (trash-when-tagged-contructor "Rachel Beckman" {:in-play [:click-per-turn 1]}) + + "Raymond Flint" + {:events {:corp-gain-bad-publicity + {:effect (req (wait-for + ;; manually trigger the pre-access event to alert Nerve Agent. + (trigger-event-sync state side :pre-access :hq) + (let [from-hq (access-count state side :hq-access) + ;; see note in Gang Sign + already-accessed (set (get-in @state [:corp :servers :hq :content])) + ability (access-helper-hq state from-hq already-accessed)] + (resolve-ability state side ability card nil)))) }} + :abilities [{:msg "expose 1 card" + :label "Expose 1 installed card" + :choices {:req installed?} + :async true + :effect (effect (expose eid target) + (trash card {:cause :ability-cost}))}]} + + "Reclaim" + {:abilities + [{:label "Install a program, piece of hardware, or virtual resource from your Heap" + :cost [:click 1] + :req (req (not-empty (:hand runner))) + :prompt "Choose a card to trash" + :choices (req (cancellable (:hand runner) :sorted)) + :async true + :effect (req (wait-for + (trash state :runner card {:cause :ability-cost}) + (wait-for + (trash state :runner target {:unpreventable true}) + (continue-ability + state :runner + {:prompt "Choose a card to install" + :choices (req (cancellable + (filter #(and (or (program? %) + (hardware? %) + (and (resource? %) + (has-subtype? % "Virtual"))) + (can-pay? state :runner eid card nil (:cost %))) + (:discard runner)) + :sorted)) + :msg (msg "install " (:title target) " from the Heap") + :async true + :effect (req (runner-install state :runner eid target nil))} + card nil))))}]} + + "Rogue Trading" + {:data {:counter {:credit 18}} + :abilities [{:cost [:click 2] + :counter-cost [:credit 6] + :msg "gain 6 [Credits] and take 1 tag" + :effect (req (gain-credits state :runner 6) + (when (zero? (get-counters (get-card state card) :credit)) + (trash state :runner card {:unpreventable true})) + (gain-tags state :runner eid 1))}]} + + "Rolodex" + {:async true + :msg "look at the top 5 cards of their Stack" + :effect (req (show-wait-prompt state :corp "Runner to rearrange the top cards of their Stack") + (let [from (take 5 (:deck runner))] + (if (pos? (count from)) + (continue-ability + state side + (reorder-choice :runner :corp from '() (count from) from) + card nil) + (do (clear-wait-prompt state :corp) + (effect-completed state side eid))))) + :trash-effect {:effect (effect (system-msg :runner + (str "trashes " + (join ", " (map :title (take 3 (:deck runner)))) + " from their Stack due to Rolodex being trashed")) + (mill :runner 3))}} + + "Rosetta 2.0" + {:abilities [{:req (req (and (not (install-locked? state side)) + (some program? (all-active-installed state :runner)))) + :cost [:click 1] + :prompt "Choose an installed program to remove from the game" + :choices {:req #(and (installed? %) (program? %))} + :effect (req (let [n (:cost target) + t (:title target)] + (move state side target :rfg) + (resolve-ability + state side + {:prompt "Choose a non-virus program to install" + :msg (req (if (not= target "No install") + (str "remove " t + " from the game and install " (:title target) + ", lowering its cost by " n) + (str "shuffle their Stack"))) + :priority true + :choices (req (cancellable + (conj (vec (sort-by :title (filter #(and (program? %) + (not (has-subtype? % "Virus"))) + (:deck runner)))) + "No install"))) + :effect (req (trigger-event state side :searched-stack nil) + (shuffle! state side :deck) + (when (not= target "No install") + (install-cost-bonus state side [:credit (- n)]) + (runner-install state side target)))} card nil)))}]} + + "Sacrificial Clone" + {:interactions {:prevent [{:type #{:net :brain :meat} + :req (req true)}]} + :abilities [{:effect (req (doseq [c (concat (get-in runner [:rig :hardware]) + (filter #(not (has-subtype? % "Virtual")) + (get-in runner [:rig :resource])) + (:hand runner))] + (trash state side c {:cause :ability-cost})) + (lose-credits state side :all) + (lose-tags state side :all) + (lose state side :run-credit :all) + (damage-prevent state side :net Integer/MAX_VALUE) + (damage-prevent state side :meat Integer/MAX_VALUE) + (damage-prevent state side :brain Integer/MAX_VALUE))}]} + + "Sacrificial Construct" + {:interactions {:prevent [{:type #{:trash-program :trash-hardware} + :req (req true)}]} + :abilities [{:effect (effect (trash-prevent :program 1) (trash-prevent :hardware 1) + (trash card {:cause :ability-cost}))}]} + + "Safety First" + {:in-play [:hand-size -2] + :events {:runner-turn-ends + {:async true + :effect (req (if (< (count (:hand runner)) (hand-size state :runner)) + (do (system-msg state :runner (str "uses " (:title card) " to draw a card")) + (draw state :runner eid 1 nil)) + (effect-completed state :runner eid)))}}} + + "Salsette Slums" + {:interactions + {:access-ability + {:label "[Salsette Slums]: Remove card from game" + :req (req (and (not (get-in @state [:per-turn (:cid card)])) + (:trash target) + (can-pay? state :runner {:source card :source-type :ability} + card (:title target) [:credit (trash-cost state side target)]))) + :once :per-turn + :async true + :trash? false + :effect (req (let [trash-cost (trash-cost state side target)] + (wait-for (pay-sync state side (make-eid state eid) card [:credit trash-cost]) + (move state :corp target :rfg) + (system-msg state side + (str "pay " trash-cost + " [Credits] and remove " (:title target) + " from the game")) + (effect-completed state side eid))))}}} + + "Salvaged Vanadis Armory" + {:events {:damage + {:effect (req (show-wait-prompt state :corp "Runner to use Salvaged Vanadis Armory") + (resolve-ability + state :runner + {:optional + {:prompt "Use Salvaged Vanadis Armory?" + :yes-ability {:msg (msg "force the Corp to trash the top " + (get-turn-damage state :runner) + " cards of R&D and trash itself") + :effect (effect (mill :corp (get-turn-damage state :runner)) + (clear-wait-prompt :corp) + (trash card {:unpreventable true}))} + :no-ability {:effect (effect (clear-wait-prompt :corp))}}} + card nil))}}} + + "Same Old Thing" + {:abilities [{:cost [:click 2] + :req (req (and (not (seq (get-in @state [:runner :locked :discard]))) + (pos? (count (filter event? (:discard runner)))))) + :prompt "Select an event to play" + :msg (msg "play " (:title target)) + :show-discard true + :choices {:req #(and (event? %) + (in-discard? %))} + :effect (effect (trash card {:cause :ability-cost}) (play-instant target))}]} + + "Scrubber" + {:recurring 2 + :interactions {:pay-credits {:req (req (and (= :runner-trash-corp-cards (:source-type eid)) + (corp? target))) + :type :recurring}}} + + "Security Testing" + (let [ability {:prompt "Choose a server for Security Testing" + :choices (req (conj servers "No server")) + :msg (msg "target " target) + :req (req (and (not (click-spent? :runner state)) + (not (used-this-turn? (:cid card) state)))) + :effect (req (when (not= target "No server") + (update! state side (assoc card :server-target target))))}] + {:events {:runner-turn-begins ability + :successful-run + {:req (req (= (zone->name (get-in @state [:run :server])) + (:server-target (get-card state card)))) + :once :per-turn + :effect (req (let [st card] + (swap! state assoc-in [:run :run-effect :replace-access] + {:mandatory true + :effect (effect (resolve-ability + {:msg "gain 2 [Credits] instead of accessing" + :effect (effect (gain-credits 2) + (update! (dissoc st :server-target)))} + st nil))})))} + :runner-turn-ends {:effect (effect (update! (dissoc card :server-target)))}} + :abilities [ability]}) + + "Slipstream" + {:implementation "Use Slipstream before hitting Continue to pass current ice" + :abilities [{:req (req (:run @state)) + :effect (req (let [ice-pos (get-in @state [:run :position])] + (resolve-ability + state side + {:prompt (msg "Choose a piece of ICE protecting a central server at the same position as " (:title current-ice)) + :choices {:req #(and (is-central? (second (:zone %))) + (ice? %) + (= ice-pos (inc (ice-index state %))))} + :msg (msg "approach " (card-str state target)) + :effect (req (let [dest (second (:zone target))] + (swap! state update-in [:run] + #(assoc % :position ice-pos :server [dest])) + (trash state side card)))} + card nil)))}]} + + "Spoilers" + {:events {:agenda-scored {:interactive (req true) + :msg "trash the top card of R&D" + :effect (effect (mill :corp))}}} + + "Starlight Crusade Funding" + {:msg "ignore additional costs on Double events" + :effect (req (swap! state assoc-in [:runner :register :double-ignore-additional] true)) + :events {:runner-turn-begins + {:msg "lose [Click] and ignore additional costs on Double events" + :effect (req (lose state :runner :click 1) + (swap! state assoc-in [:runner :register :double-ignore-additional] true))}} + :leave-play (req (swap! state update-in [:runner :register] dissoc :double-ignore-additional))} + + "Stim Dealer" + {:events {:runner-turn-begins + {:effect (req (if (>= (get-counters card :power) 2) + (do (add-counter state side card :power (- (get-counters card :power))) + (damage state side eid :brain 1 {:unpreventable true :card card}) + (system-msg state side "takes 1 brain damage from Stim Dealer")) + (do (add-counter state side card :power 1) + (gain state side :click 1) + (system-msg state side "uses Stim Dealer to gain [Click]"))))}}} + + "Street Peddler" + {:interactive (req (some #(card-flag? % :runner-install-draw true) (all-active state :runner))) + :effect (req (doseq [c (take 3 (:deck runner))] + (host state side (get-card state card) c {:facedown true}))) + :abilities [{:req (req (not (install-locked? state side))) + :prompt "Choose a card on Street Peddler to install" + :choices (req (cancellable (filter #(and (not (event? %)) + (runner-can-install? state side % nil) + (can-pay? state side eid card nil (modified-install-cost state side % [:credit -1]))) + (:hosted card)))) + :msg (msg "install " (:title target) " lowering its install cost by 1 [Credits]") + :effect (req + (when (can-pay? state side eid card nil (modified-install-cost state side target [:credit -1])) + (install-cost-bonus state side [:credit -1]) + (trash state side (update-in card [:hosted] + (fn [coll] + (remove-once #(same-card? % target) coll))) + {:cause :ability-cost}) + (runner-install state side eid (dissoc target :facedown) nil)))}]} + + "Symmetrical Visage" + {:events {:runner-click-draw {:req (req (genetics-trigger? state side :runner-click-draw)) :msg "gain 1 [Credits]" - :effect (req (gain-credits state :runner 1) - (swap! state assoc-in [:per-turn (:cid card)] true))} - :corp-turn-begins {:req (req (zero? (:credit runner))) - :msg "gain 1 [Credits]" - :effect (req (gain-credits state :runner 1) - (swap! state assoc-in [:per-turn (:cid card)] true))} - :runner-install {:silent (req (pos? (:credit runner))) - :req (req (and (= target card) - (zero? (:credit runner)))) - :msg "gain 1 [Credits]" - :effect (req (gain-credits state :runner 1) - (swap! state assoc-in [:per-turn (:cid card)] true))}} - :leave-play (req (remove-watch state :order-of-sol))}) - -(define-card "PAD Tap" - {:events {:corp-credit-gain - {:req (req (and (not= target :corp-click-credit) - (= 1 (->> (turn-events state :corp :corp-credit-gain) - (remove #(= (first %) :corp-click-credit)) - count)))) - :msg "gain 1 [Credits]" - :effect (effect (gain-credits :runner 1))}} - :corp-abilities [{:label "Trash PAD Tap" - :cost [:credit 3 :click 1] - :req (req (= :corp side)) - :effect (effect (system-msg :corp "spends [Click] and 3 [Credits] to trash PAD Tap") - (trash :corp card))}]}) - -(define-card "Paige Piper" - (letfn [(pphelper [title cards] - {:optional - {:prompt (str "Use Paige Piper to trash copies of " title "?") - :yes-ability {:prompt "How many would you like to trash?" - :choices (take (inc (count cards)) ["0" "1" "2" "3" "4" "5"]) - :msg "shuffle their Stack" - :effect (req (let [target (str->int target)] - (trigger-event state side :searched-stack nil) - (shuffle! state :runner :deck) - (doseq [c (take target cards)] - (trash state side c {:unpreventable true})) - (when (pos? target) - (system-msg state side (str "trashes " - (quantify target "cop" "y" "ies") - " of " title)))))}}})] - {:events {:runner-install {:req (req (first-event? state side :runner-install)) - :async true - :effect (effect (continue-ability - (pphelper (:title target) - (->> (:deck runner) - (filter #(has? % :title (:title target))) - (vec))) - card nil))}}})) - -(define-card "Paparazzi" - {:effect (req (swap! state update-in [:runner :tag :is-tagged] inc) - (trigger-event state :runner :runner-is-tagged true)) - :events {:pre-damage {:req (req (= target :meat)) :msg "prevent all meat damage" - :effect (effect (damage-prevent :meat Integer/MAX_VALUE))}} - :leave-play (req (swap! state update-in [:runner :tag :is-tagged] dec) - (trigger-event state :runner :runner-is-tagged (pos? (get-in @state [:runner :tag :is-tagged]))))}) - -(define-card "Patron" - (let [ability {:prompt "Choose a server for Patron" :choices (req (conj servers "No server")) - :req (req (and (not (click-spent? :runner state)) (not (used-this-turn? (:cid card) state)))) - :msg (msg "target " target) - :effect (req (when (not= target "No server") - (update! state side (assoc card :server-target target))))}] - {:events {:runner-turn-begins ability - :successful-run - {:req (req (= (zone->name (get-in @state [:run :server])) (:server-target (get-card state card)))) - :once :per-turn - :effect (req (let [st card] - (swap! state assoc-in [:run :run-effect :replace-access] - {:mandatory true - :effect (effect (resolve-ability - {:msg "draw 2 cards instead of accessing" - :async true - :effect (effect (update! (dissoc st :server-target)) - (draw eid 2 nil))} - st nil))})))} - :runner-turn-ends {:effect (effect (update! (dissoc card :server-target)))}} - :abilities [ability]})) - -(define-card "Personal Workshop" - (let [remove-counter - {:req (req (not (empty? (:hosted card)))) - :once :per-turn - :msg (msg "remove 1 counter from " (:title target)) - :choices {:req #(:host %)} - :effect (req (if (zero? (get-counters (get-card state target) :power)) - (runner-install state side (dissoc target :counter) {:ignore-all-cost true}) - (add-counter state side target :power -1)))}] - {:flags {:drip-economy true} - :abilities [{:label "Host a program or piece of hardware" :cost [:click 1] - :prompt "Select a card to host on Personal Workshop" - :choices {:req #(and (#{"Program" "Hardware"} (:type %)) - (in-hand? %) - (runner? %))} - :effect (req (if (zero? (:cost target)) - (runner-install state side target) - (host state side card - (assoc target :counter {:power (:cost target)})))) - :msg (msg "host " (:title target) "")} - (assoc remove-counter - :label "Remove 1 counter from a hosted card (start of turn)" - :cost [:credit 1]) - {:label "X[Credit]: Remove counters from a hosted card" - :choices {:req #(:host %)} - :req (req (not (empty? (:hosted card)))) - :effect (req (let [paydowntarget target - num-counters (get-counters (get-card state paydowntarget) :power)] - (resolve-ability - state side - {:prompt "How many counters to remove?" - :choices {:number (req (min (:credit runner) - num-counters))} - :msg (msg "remove " target " counters from " (:title paydowntarget)) - :effect (req (do - (lose-credits state side target) - (if (= num-counters target) - (runner-install state side (dissoc paydowntarget :counter) {:ignore-all-cost true}) - (add-counter state side paydowntarget :power (- target)))))} - card nil)))}] - :events {:runner-turn-begins remove-counter}})) - -(define-card "Political Operative" - {:req (req (some #{:hq} (:successful-run runner-reg))) - :abilities [{:prompt "Select a rezzed card with a trash cost" - :choices {:req #(and (:trash %) - (rezzed? %))} - :effect (req (let [cost (modified-trash-cost state :runner target)] - (when (can-pay? state side eid card nil [:credit cost]) - (resolve-ability - state side - {:msg (msg "pay " cost " [Credit] and trash " (:title target)) - :effect (effect (lose-credits cost) - (trash card {:cause :ability-cost}) - (trash target))} - card targets))))}]}) - -(define-card "Power Tap" - {:events {:pre-init-trace {:msg "gain 1 [Credits]" - :effect (effect (gain-credits :runner 1))}}}) - -(define-card "Professional Contacts" - {:abilities [{:cost [:click 1] - :msg "gain 1 [Credits] and draw 1 card" - :async true - :effect (effect (gain-credits 1) - (draw eid 1 nil))}]}) - -(define-card "Psych Mike" - {:events {:successful-run-ends - {:req (req (first-event? state side :successful-run-ends #(= :rd (first (:server (first %)))))) - :msg (msg "gain " (total-cards-accessed target :deck) " [Credits]") - :effect (effect (gain-credits :runner (total-cards-accessed target :deck)))}}}) - -(define-card "Public Sympathy" - {:in-play [:hand-size 2]}) - -(define-card "Rachel Beckman" - (trash-when-tagged-contructor "Rachel Beckman" {:in-play [:click-per-turn 1]})) - -(define-card "Raymond Flint" - {:events {:corp-gain-bad-publicity - {:effect (req (wait-for - ;; manually trigger the pre-access event to alert Nerve Agent. - (trigger-event-sync state side :pre-access :hq) - (let [from-hq (access-count state side :hq-access) - ;; see note in Gang Sign - already-accessed (set (get-in @state [:corp :servers :hq :content])) - ability (access-helper-hq state from-hq already-accessed)] - (resolve-ability state side ability card nil)))) }} - :abilities [{:msg "expose 1 card" - :label "Expose 1 installed card" - :choices {:req installed?} - :async true - :effect (effect (expose eid target) - (trash card {:cause :ability-cost}))}]}) - -(define-card "Reclaim" - {:abilities - [{:label "Install a program, piece of hardware, or virtual resource from your Heap" - :cost [:click 1] - :req (req (not-empty (:hand runner))) - :prompt "Choose a card to trash" - :choices (req (cancellable (:hand runner) :sorted)) - :async true - :effect (req (wait-for - (trash state :runner card {:cause :ability-cost}) - (wait-for - (trash state :runner target {:unpreventable true}) - (continue-ability - state :runner - {:prompt "Choose a card to install" - :choices (req (cancellable - (filter #(and (or (program? %) - (hardware? %) - (and (resource? %) - (has-subtype? % "Virtual"))) - (can-pay? state :runner eid card nil (:cost %))) - (:discard runner)) - :sorted)) - :msg (msg "install " (:title target) " from the Heap") - :async true - :effect (req (runner-install state :runner eid target nil))} - card nil))))}]}) - -(define-card "Rogue Trading" - {:data {:counter {:credit 18}} - :abilities [{:cost [:click 2] - :counter-cost [:credit 6] - :msg "gain 6 [Credits] and take 1 tag" - :effect (req (gain-credits state :runner 6) - (when (zero? (get-counters (get-card state card) :credit)) - (trash state :runner card {:unpreventable true})) - (gain-tags state :runner eid 1))}]}) - -(define-card "Rolodex" - {:async true - :msg "look at the top 5 cards of their Stack" - :effect (req (show-wait-prompt state :corp "Runner to rearrange the top cards of their Stack") - (let [from (take 5 (:deck runner))] - (if (pos? (count from)) - (continue-ability - state side - (reorder-choice :runner :corp from '() (count from) from) - card nil) - (do (clear-wait-prompt state :corp) - (effect-completed state side eid))))) - :trash-effect {:effect (effect (system-msg :runner - (str "trashes " - (join ", " (map :title (take 3 (:deck runner)))) - " from their Stack due to Rolodex being trashed")) - (mill :runner 3))}}) - -(define-card "Rosetta 2.0" - {:abilities [{:req (req (and (not (install-locked? state side)) - (some program? (all-active-installed state :runner)))) - :cost [:click 1] - :prompt "Choose an installed program to remove from the game" - :choices {:req #(and (installed? %) (program? %))} - :effect (req (let [n (:cost target) - t (:title target)] - (move state side target :rfg) - (resolve-ability - state side - {:prompt "Choose a non-virus program to install" - :msg (req (if (not= target "No install") - (str "remove " t - " from the game and install " (:title target) - ", lowering its cost by " n) - (str "shuffle their Stack"))) - :priority true - :choices (req (cancellable - (conj (vec (sort-by :title (filter #(and (program? %) - (not (has-subtype? % "Virus"))) - (:deck runner)))) - "No install"))) - :effect (req (trigger-event state side :searched-stack nil) - (shuffle! state side :deck) - (when (not= target "No install") - (install-cost-bonus state side [:credit (- n)]) - (runner-install state side target)))} card nil)))}]}) - -(define-card "Sacrificial Clone" - {:interactions {:prevent [{:type #{:net :brain :meat} - :req (req true)}]} - :abilities [{:effect (req (doseq [c (concat (get-in runner [:rig :hardware]) - (filter #(not (has-subtype? % "Virtual")) - (get-in runner [:rig :resource])) - (:hand runner))] - (trash state side c {:cause :ability-cost})) - (lose-credits state side :all) - (lose-tags state side :all) - (lose state side :run-credit :all) - (damage-prevent state side :net Integer/MAX_VALUE) - (damage-prevent state side :meat Integer/MAX_VALUE) - (damage-prevent state side :brain Integer/MAX_VALUE))}]}) - -(define-card "Sacrificial Construct" - {:interactions {:prevent [{:type #{:trash-program :trash-hardware} - :req (req true)}]} - :abilities [{:effect (effect (trash-prevent :program 1) (trash-prevent :hardware 1) - (trash card {:cause :ability-cost}))}]}) - -(define-card "Safety First" - {:in-play [:hand-size -2] - :events {:runner-turn-ends - {:async true - :effect (req (if (< (count (:hand runner)) (hand-size state :runner)) - (do (system-msg state :runner (str "uses " (:title card) " to draw a card")) - (draw state :runner eid 1 nil)) - (effect-completed state :runner eid)))}}}) - -(define-card "Salsette Slums" - {:interactions - {:access-ability - {:label "[Salsette Slums]: Remove card from game" - :req (req (and (not (get-in @state [:per-turn (:cid card)])) - (:trash target) - (can-pay? state :runner {:source card :source-type :ability} - card (:title target) [:credit (trash-cost state side target)]))) - :once :per-turn - :async true - :trash? false - :effect (req (let [trash-cost (trash-cost state side target)] - (wait-for (pay-sync state side (make-eid state eid) card [:credit trash-cost]) - (move state :corp target :rfg) - (system-msg state side - (str "pay " trash-cost - " [Credits] and remove " (:title target) - " from the game")) - (effect-completed state side eid))))}}}) - -(define-card "Salvaged Vanadis Armory" - {:events {:damage - {:effect (req (show-wait-prompt state :corp "Runner to use Salvaged Vanadis Armory") - (resolve-ability - state :runner - {:optional - {:prompt "Use Salvaged Vanadis Armory?" - :yes-ability {:msg (msg "force the Corp to trash the top " - (get-turn-damage state :runner) - " cards of R&D and trash itself") - :effect (effect (mill :corp (get-turn-damage state :runner)) - (clear-wait-prompt :corp) - (trash card {:unpreventable true}))} - :no-ability {:effect (effect (clear-wait-prompt :corp))}}} - card nil))}}}) - -(define-card "Same Old Thing" - {:abilities [{:cost [:click 2] - :req (req (and (not (seq (get-in @state [:runner :locked :discard]))) - (pos? (count (filter event? (:discard runner)))))) - :prompt "Select an event to play" - :msg (msg "play " (:title target)) - :show-discard true - :choices {:req #(and (event? %) - (in-discard? %))} - :effect (effect (trash card {:cause :ability-cost}) (play-instant target))}]}) - -(define-card "Scrubber" - {:recurring 2 - :interactions {:pay-credits {:req (req (and (= :runner-trash-corp-cards (:source-type eid)) - (corp? target))) - :type :recurring}}}) - -(define-card "Security Testing" - (let [ability {:prompt "Choose a server for Security Testing" - :choices (req (conj servers "No server")) - :msg (msg "target " target) - :req (req (and (not (click-spent? :runner state)) - (not (used-this-turn? (:cid card) state)))) - :effect (req (when (not= target "No server") - (update! state side (assoc card :server-target target))))}] - {:events {:runner-turn-begins ability - :successful-run - {:req (req (= (zone->name (get-in @state [:run :server])) - (:server-target (get-card state card)))) - :once :per-turn - :effect (req (let [st card] - (swap! state assoc-in [:run :run-effect :replace-access] - {:mandatory true - :effect (effect (resolve-ability - {:msg "gain 2 [Credits] instead of accessing" - :effect (effect (gain-credits 2) - (update! (dissoc st :server-target)))} - st nil))})))} - :runner-turn-ends {:effect (effect (update! (dissoc card :server-target)))}} - :abilities [ability]})) - -(define-card "Slipstream" - {:implementation "Use Slipstream before hitting Continue to pass current ice" - :abilities [{:req (req (:run @state)) - :effect (req (let [ice-pos (get-in @state [:run :position])] - (resolve-ability - state side - {:prompt (msg "Choose a piece of ICE protecting a central server at the same position as " (:title current-ice)) - :choices {:req #(and (is-central? (second (:zone %))) - (ice? %) - (= ice-pos (inc (ice-index state %))))} - :msg (msg "approach " (card-str state target)) - :effect (req (let [dest (second (:zone target))] - (swap! state update-in [:run] - #(assoc % :position ice-pos :server [dest])) - (trash state side card)))} - card nil)))}]}) - -(define-card "Spoilers" - {:events {:agenda-scored {:interactive (req true) - :msg "trash the top card of R&D" - :effect (effect (mill :corp))}}}) - -(define-card "Starlight Crusade Funding" - {:msg "ignore additional costs on Double events" - :effect (req (swap! state assoc-in [:runner :register :double-ignore-additional] true)) - :events {:runner-turn-begins - {:msg "lose [Click] and ignore additional costs on Double events" - :effect (req (lose state :runner :click 1) - (swap! state assoc-in [:runner :register :double-ignore-additional] true))}} - :leave-play (req (swap! state update-in [:runner :register] dissoc :double-ignore-additional))}) - -(define-card "Stim Dealer" - {:events {:runner-turn-begins - {:effect (req (if (>= (get-counters card :power) 2) - (do (add-counter state side card :power (- (get-counters card :power))) - (damage state side eid :brain 1 {:unpreventable true :card card}) - (system-msg state side "takes 1 brain damage from Stim Dealer")) - (do (add-counter state side card :power 1) - (gain state side :click 1) - (system-msg state side "uses Stim Dealer to gain [Click]"))))}}}) - -(define-card "Street Peddler" - {:interactive (req (some #(card-flag? % :runner-install-draw true) (all-active state :runner))) - :effect (req (doseq [c (take 3 (:deck runner))] - (host state side (get-card state card) c {:facedown true}))) - :abilities [{:req (req (not (install-locked? state side))) - :prompt "Choose a card on Street Peddler to install" - :choices (req (cancellable (filter #(and (not (event? %)) - (runner-can-install? state side % nil) - (can-pay? state side eid card nil (modified-install-cost state side % [:credit -1]))) - (:hosted card)))) - :msg (msg "install " (:title target) " lowering its install cost by 1 [Credits]") - :effect (req - (when (can-pay? state side eid card nil (modified-install-cost state side target [:credit -1])) - (install-cost-bonus state side [:credit -1]) - (trash state side (update-in card [:hosted] - (fn [coll] - (remove-once #(same-card? % target) coll))) - {:cause :ability-cost}) - (runner-install state side eid (dissoc target :facedown) nil)))}]}) - -(define-card "Symmetrical Visage" - {:events {:runner-click-draw {:req (req (genetics-trigger? state side :runner-click-draw)) - :msg "gain 1 [Credits]" - :effect (effect (gain-credits 1))}}}) + :effect (effect (gain-credits 1))}}} -(define-card "Synthetic Blood" - {:events {:damage {:req (req (genetics-trigger? state side :damage)) - :msg "draw 1 card" - :async true - :effect (effect (draw :runner eid 1 nil))}}}) + "Synthetic Blood" + {:events {:damage {:req (req (genetics-trigger? state side :damage)) + :msg "draw 1 card" + :async true + :effect (effect (draw :runner eid 1 nil))}}} + + "Tallie Perrault" + {:abilities [{:label "Draw 1 card for each Corp bad publicity" + :async true + :effect (req (wait-for (trash state side card {:cause :ability-cost}) + (draw state side eid (count-bad-pub state) nil))) + :msg (msg "draw " (count-bad-pub state) " cards")}] + :events {:play-operation + {:req (req (or (has-subtype? target "Black Ops") + (has-subtype? target "Gray Ops"))) + :effect (req (show-wait-prompt state :corp "Runner to use Tallie Perrault") + (resolve-ability + state :runner + {:optional + {:prompt "Use Tallie Perrault to give the Corp 1 bad publicity and take 1 tag?" + :player :runner + :yes-ability {:msg "give the Corp 1 bad publicity and take 1 tag" + :async true + :effect (effect (gain-bad-publicity :corp 1) + (gain-tags :runner eid 1) + (clear-wait-prompt :corp))} + :no-ability {:effect (effect (clear-wait-prompt :corp))}}} + card nil))}}} + + "Tech Trader" + {:events {:runner-trash {:req (req (and (= side :runner) (= (second targets) :ability-cost))) + :msg "gain 1 [Credits]" + :effect (effect (gain-credits 1))}}} + + "Technical Writer" + {:events {:runner-install {:silent (req true) + :req (req (some #(= % (:type target)) '("Hardware" "Program"))) + :effect (effect (add-counter :runner card :credit 1) + (system-msg (str "places 1 [Credits] on Technical Writer")))}} + :abilities [{:cost [:click 1] + :msg (msg "gain " (get-counters card :credit) " [Credits]") + :effect (effect (trash card {:cause :ability-cost}) + (gain-credits (get-counters card :credit)))}]} + + "Temple of the Liberated Mind" + {:abilities [{:cost [:click 1] + :label "Place 1 power counter" + :msg "place 1 power counter on it" + :effect (effect (add-counter card :power 1))} + {:label "Gain [Click]" + :counter-cost [:power 1] + :req (req (= (:active-player @state) :runner)) + :msg "gain [Click]" :once :per-turn + :effect (effect (gain :click 1))}]} + + "Temüjin Contract" + {:data {:counter {:credit 20}} + :prompt "Choose a server for Temüjin Contract" + :choices (req servers) + :msg (msg "target " target) + :req (req (not (:server-target card))) + :effect (effect (update! (assoc card :server-target target))) + :events {:successful-run + {:req (req (= (zone->name (get-in @state [:run :server])) (:server-target (get-card state card)))) + :msg "gain 4 [Credits]" + :effect (req (let [creds (get-counters card :credit)] + (gain-credits state side 4) + (set-prop state side card :counter {:credit (- creds 4)}) + (when (zero? (get-counters (get-card state card) :credit)) + (trash state side card {:unpreventable true}))))}}} -(define-card "Tallie Perrault" - {:abilities [{:label "Draw 1 card for each Corp bad publicity" + "The Archivist" + {:in-play [:link 1] + :events {:agenda-scored {:req (req (or (has-subtype? target "Initiative") + (has-subtype? target "Security"))) + :interactive (req true) + :async true + :msg "force the Corp to initiate a trace" + :label "Trace 1 - If unsuccessful, take 1 bad publicity" + :trace {:base 1 + :unsuccessful + {:effect (effect (gain-bad-publicity :corp 1) + (system-msg :corp (str "takes 1 bad publicity")))}}}}} + + "The Artist" + {:abilities [{:cost [:click 1] + :label "Gain 2 [Credits]" + :msg "gain 2 [Credits]" + :once :per-turn + :once-key :artist-credits + :effect (effect (gain-credits 2))} + {:cost [:click 1] + :label "Install a program of piece of hardware" + :prompt "Select a program or piece of hardware to install from your Grip" + :choices {:req #(and (or (hardware? %) + (program? %)) + (in-hand? %))} + :once :per-turn + :once-key :artist-install + :effect (effect (install-cost-bonus [:credit -1]) + (runner-install target {:no-msg true})) + :msg (msg "install " (:title target) ", lowering its cost by 1 [Credits]")}]} + + "The Black File" + {:msg "prevent the Corp from winning the game unless they are flatlined" + :effect (req (swap! state assoc-in [:corp :cannot-win-on-points] true)) + :events {:runner-turn-begins + {:effect (req (if (>= (get-counters card :power) 2) + (do (move state side (dissoc card :counter) :rfg) + (swap! state update-in [:corp] dissoc :cannot-win-on-points) + (system-msg state side "removes The Black File from the game") + (gain-agenda-point state :corp 0)) + (add-counter state side card :power 1)))}} + :trash-effect {:effect (req (swap! state update-in [:corp] dissoc :cannot-win-on-points) + (gain-agenda-point state :corp 0))} + :leave-play (req (swap! state update-in [:corp] dissoc :cannot-win-on-points) + (gain-agenda-point state :corp 0))} + + "The Class Act" + (let [draw-ability {:req (req (= :this-turn (:installed card))) + :async true + :msg "draw 4 cards" + :effect (effect (draw :runner eid 4 nil))}] + {:events {:corp-turn-ends draw-ability + :runner-turn-ends draw-ability + :pre-runner-draw + {:msg "draw 1 additional card" + ;; The req catches draw events that happened before The Class Act was installed + :req (req (first-event? state :runner :pre-runner-draw)) :async true - :effect (req (wait-for (trash state side card {:cause :ability-cost}) - (draw state side eid (count-bad-pub state) nil))) - :msg (msg "draw " (count-bad-pub state) " cards")}] - :events {:play-operation - {:req (req (or (has-subtype? target "Black Ops") - (has-subtype? target "Gray Ops"))) - :effect (req (show-wait-prompt state :corp "Runner to use Tallie Perrault") - (resolve-ability - state :runner - {:optional - {:prompt "Use Tallie Perrault to give the Corp 1 bad publicity and take 1 tag?" - :player :runner - :yes-ability {:msg "give the Corp 1 bad publicity and take 1 tag" - :async true - :effect (effect (gain-bad-publicity :corp 1) - (gain-tags :runner eid 1) - (clear-wait-prompt :corp))} - :no-ability {:effect (effect (clear-wait-prompt :corp))}}} - card nil))}}}) - -(define-card "Tech Trader" - {:events {:runner-trash {:req (req (and (= side :runner) (= (second targets) :ability-cost))) - :msg "gain 1 [Credits]" - :effect (effect (gain-credits 1))}}}) - -(define-card "Technical Writer" - {:events {:runner-install {:silent (req true) - :req (req (some #(= % (:type target)) '("Hardware" "Program"))) - :effect (effect (add-counter :runner card :credit 1) - (system-msg (str "places 1 [Credits] on Technical Writer")))}} - :abilities [{:cost [:click 1] - :msg (msg "gain " (get-counters card :credit) " [Credits]") - :effect (effect (trash card {:cause :ability-cost}) - (gain-credits (get-counters card :credit)))}]}) - -(define-card "Temple of the Liberated Mind" - {:abilities [{:cost [:click 1] - :label "Place 1 power counter" - :msg "place 1 power counter on it" - :effect (effect (add-counter card :power 1))} - {:label "Gain [Click]" - :counter-cost [:power 1] - :req (req (= (:active-player @state) :runner)) - :msg "gain [Click]" :once :per-turn - :effect (effect (gain :click 1))}]}) - -(define-card "Temüjin Contract" - {:data {:counter {:credit 20}} - :prompt "Choose a server for Temüjin Contract" - :choices (req servers) - :msg (msg "target " target) - :req (req (not (:server-target card))) - :effect (effect (update! (assoc card :server-target target))) - :events {:successful-run - {:req (req (= (zone->name (get-in @state [:run :server])) (:server-target (get-card state card)))) - :msg "gain 4 [Credits]" - :effect (req (let [creds (get-counters card :credit)] - (gain-credits state side 4) - (set-prop state side card :counter {:credit (- creds 4)}) - (when (zero? (get-counters (get-card state card) :credit)) - (trash state side card {:unpreventable true}))))}}}) - -(define-card "The Archivist" - {:in-play [:link 1] - :events {:agenda-scored {:req (req (or (has-subtype? target "Initiative") - (has-subtype? target "Security"))) - :interactive (req true) - :async true - :msg "force the Corp to initiate a trace" - :label "Trace 1 - If unsuccessful, take 1 bad publicity" - :trace {:base 1 - :unsuccessful - {:effect (effect (gain-bad-publicity :corp 1) - (system-msg :corp (str "takes 1 bad publicity")))}}}}}) - -(define-card "The Artist" - {:abilities [{:cost [:click 1] - :label "Gain 2 [Credits]" - :msg "gain 2 [Credits]" - :once :per-turn - :once-key :artist-credits - :effect (effect (gain-credits 2))} - {:cost [:click 1] - :label "Install a program of piece of hardware" - :prompt "Select a program or piece of hardware to install from your Grip" - :choices {:req #(and (or (hardware? %) - (program? %)) - (in-hand? %))} + :interactive (req true) :once :per-turn - :once-key :artist-install - :effect (effect (install-cost-bonus [:credit -1]) - (runner-install target {:no-msg true})) - :msg (msg "install " (:title target) ", lowering its cost by 1 [Credits]")}]}) - -(define-card "The Black File" - {:msg "prevent the Corp from winning the game unless they are flatlined" - :effect (req (swap! state assoc-in [:corp :cannot-win-on-points] true)) - :events {:runner-turn-begins - {:effect (req (if (>= (get-counters card :power) 2) - (do (move state side (dissoc card :counter) :rfg) - (swap! state update-in [:corp] dissoc :cannot-win-on-points) - (system-msg state side "removes The Black File from the game") - (gain-agenda-point state :corp 0)) - (add-counter state side card :power 1)))}} - :trash-effect {:effect (req (swap! state update-in [:corp] dissoc :cannot-win-on-points) - (gain-agenda-point state :corp 0))} - :leave-play (req (swap! state update-in [:corp] dissoc :cannot-win-on-points) - (gain-agenda-point state :corp 0))}) - -(define-card "The Class Act" - (let [draw-ability {:req (req (= :this-turn (:installed card))) - :async true - :msg "draw 4 cards" - :effect (effect (draw :runner eid 4 nil))}] - {:events {:corp-turn-ends draw-ability - :runner-turn-ends draw-ability - :pre-runner-draw - {:msg "draw 1 additional card" - ;; The req catches draw events that happened before The Class Act was installed - :req (req (first-event? state :runner :pre-runner-draw)) - :async true - :interactive (req true) - :once :per-turn - :effect (req (if (zero? (count (get-in @state [:runner :deck]))) - (effect-completed state side eid) - (let [n (+ target (get-in @state [:bonus :draw] 0)) - to-draw (take (inc n) (:deck (:runner @state)))] - (show-wait-prompt state :corp "Runner to use The Class Act") - (continue-ability - state :runner - {:prompt "Select 1 card to add to the bottom of the stack" - :msg "add 1 card to the bottom of the Stack" - :choices to-draw - :effect (effect (move target :deck) - (clear-wait-prompt :corp) - (effect-completed eid))} - card nil))))}}})) - -(define-card "The Helpful AI" - {:in-play [:link 1] - :abilities [{:msg (msg "give +2 strength to " (:title target)) - :choices {:req #(and (has-subtype? % "Icebreaker") - (installed? %))} - :effect (effect (update! (assoc card :hai-target target)) - (trash (get-card state card) {:cause :ability-cost}) - (update-breaker-strength target))}] - :events {:runner-turn-ends nil :corp-turn-ends nil :pre-breaker-strength nil} - :trash-effect {:effect - (effect (register-events - (let [hai {:effect (effect (unregister-events card) - (update! (dissoc card :hai-target)) - (update-breaker-strength (:hai-target card)))}] - {:runner-turn-ends hai :corp-turn-ends hai - :pre-breaker-strength {:req (req (same-card? target (:hai-target card))) - :effect (effect (breaker-strength-bonus 2))}}) card))}}) - -(define-card "The Nihilist" - (let [has-2-virus-tokens? (req (<= 2 (number-of-virus-counters state))) - corp-choice {:optional {:player :corp - :prompt "Trash the top card of R&D to prevent the Runner drawing 2 cards?" - :async true - :yes-ability {:effect (effect (clear-wait-prompt :runner) - (system-msg :corp "trashes the top card of R&D to prevent the Runner drawing 2 cards") - (mill :corp) - (effect-completed eid))} - :no-ability {:async true - :effect (effect (clear-wait-prompt :runner) - (system-msg :runner "draw 2 cards") - (draw :runner eid 2 nil))}}} - maybe-spend-2 {:prompt "Spend 2 virus counters on The Nihilist?" + :effect (req (if (zero? (count (get-in @state [:runner :deck]))) + (effect-completed state side eid) + (let [n (+ target (get-in @state [:bonus :draw] 0)) + to-draw (take (inc n) (:deck (:runner @state)))] + (show-wait-prompt state :corp "Runner to use The Class Act") + (continue-ability + state :runner + {:prompt "Select 1 card to add to the bottom of the stack" + :msg "add 1 card to the bottom of the Stack" + :choices to-draw + :effect (effect (move target :deck) + (clear-wait-prompt :corp) + (effect-completed eid))} + card nil))))}}}) + + "The Helpful AI" + {:in-play [:link 1] + :abilities [{:msg (msg "give +2 strength to " (:title target)) + :choices {:req #(and (has-subtype? % "Icebreaker") + (installed? %))} + :effect (effect (update! (assoc card :hai-target target)) + (trash (get-card state card) {:cause :ability-cost}) + (update-breaker-strength target))}] + :events {:runner-turn-ends nil :corp-turn-ends nil :pre-breaker-strength nil} + :trash-effect {:effect + (effect (register-events + (let [hai {:effect (effect (unregister-events card) + (update! (dissoc card :hai-target)) + (update-breaker-strength (:hai-target card)))}] + {:runner-turn-ends hai :corp-turn-ends hai + :pre-breaker-strength {:req (req (same-card? target (:hai-target card))) + :effect (effect (breaker-strength-bonus 2))}}) card))}} + + "The Nihilist" + (let [has-2-virus-tokens? (req (<= 2 (number-of-virus-counters state))) + corp-choice {:optional {:player :corp + :prompt "Trash the top card of R&D to prevent the Runner drawing 2 cards?" + :async true + :yes-ability {:effect (effect (clear-wait-prompt :runner) + (system-msg :corp "trashes the top card of R&D to prevent the Runner drawing 2 cards") + (mill :corp) + (effect-completed eid))} + :no-ability {:async true + :effect (effect (clear-wait-prompt :runner) + (system-msg :runner "draw 2 cards") + (draw :runner eid 2 nil))}}} + maybe-spend-2 {:prompt "Spend 2 virus counters on The Nihilist?" + :async true + :yes-ability {:effect (req (wait-for (resolve-ability state side (pick-virus-counters-to-spend 2) card nil) + (if (:number async-result) + (do (system-msg state side (str "spends " (:msg async-result) " on The Nihilist")) + (show-wait-prompt state :runner "Corp to decide") + (continue-ability state side corp-choice card nil)) + (effect-completed state side eid))))}}] + {:events {:runner-turn-begins {:interactive (req true) + :req has-2-virus-tokens? + :optional maybe-spend-2} + :runner-install {:msg "add 2 virus tokens to The Nihilist" + :effect (effect (add-counter card :virus 2)) + :req (req (has-subtype? target "Virus")) + :once :per-turn}}}) + + "The Shadow Net" + (letfn [(events [runner] (filter #(and (event? %) (not (has-subtype? % "Priority"))) (:discard runner)))] + {:abilities [{:cost [:click 1 :forfeit] + :req (req (pos? (count (events runner)))) + :label "Play an event from your Heap, ignoring all costs" + :prompt "Choose an event to play" + :msg (msg "play " (:title target) " from the Heap, ignoring all costs") + :choices (req (cancellable (events runner) :sorted)) + :effect (effect (play-instant nil target {:ignore-cost true}))}]}) + + "The Source" + {:effect (effect (update-all-advancement-costs)) + :leave-play (effect (update-all-advancement-costs)) + :events {:agenda-scored {:effect (effect (trash card))} + :agenda-stolen {:effect (effect (trash card))} + :pre-advancement-cost {:effect (effect (advancement-cost-bonus 1))} + :pre-steal-cost {:effect (effect (steal-cost-bonus [:credit 3]))}}} + + "The Supplier" + (let [ability {:label "Install a hosted card (start of turn)" + :prompt "Choose a card hosted on The Supplier to install" + :req (req (some #(can-pay? state side eid card nil (modified-install-cost state side % [:credit -2])) + (:hosted card))) + :choices {:req #(and (= "The Supplier" (:title (:host %))) + (runner? %))} + :once :per-turn + :effect (req + (runner-can-install? state side target nil) + (when (and (can-pay? state side eid card nil (modified-install-cost state side target [:credit -2])) + (not (and (:uniqueness target) (in-play? state target)))) + (install-cost-bonus state side [:credit -2]) + (runner-install state side target) + (system-msg state side (str "uses The Supplier to install " (:title target) " lowering its install cost by 2")) + (update! state side (-> card + (assoc :supplier-installed (:cid target)) + (update-in [:hosted] + (fn [coll] + (remove-once #(same-card? % target) coll)))))))}] + {:flags {:drip-economy true} ; not technically drip economy, but has an interaction with Drug Dealer + :abilities [{:label "Host a resource or piece of hardware" :cost [:click 1] + :prompt "Select a card to host on The Supplier" + :choices {:req #(and (#{"Resource" "Hardware"} (:type %)) + (in-hand? %))} + :effect (effect (host card target)) :msg (msg "host " (:title target) "")} + ability] + ; A card installed by The Supplier is ineligible to receive the turn-begins event for this turn. + :suppress {:runner-turn-begins {:req (req (= (:cid target) (:supplier-installed (get-card state card))))}} + :events {:runner-turn-begins ability + :runner-turn-ends {:req (req (:supplier-installed card)) + :effect (effect (update! (dissoc card :supplier-installed)))}}}) + + "The Turning Wheel" + (let [ttw-ab (fn [m s] + {:label (str "Access an additional card in " m) + :counter-cost [:power 2] + :req (req run) + :msg (msg "access 1 additional card from " m " for the remainder of the run") + :effect (req (access-bonus state side s 1))})] + {:events {:agenda-stolen {:effect (effect (update! (assoc card :agenda-stolen true))) + :silent (req true)} + :run-ends {:effect (req (when (and (not (:agenda-stolen card)) + (#{:hq :rd} target)) + (add-counter state side card :power 1) + (system-msg state :runner (str "places a power counter on " (:title card)))) + (update! state side (dissoc (get-card state card) :agenda-stolen))) + :silent (req true)}} + :abilities [(ttw-ab "R&D" :rd) + (ttw-ab "HQ" :hq)]}) + + "Theophilius Bagbiter" + {:effect (req (lose-credits state :runner :all) + (lose state :runner :run-credit :all) + (swap! state assoc-in [:runner :hand-size :base] 0) + (add-watch state :theophilius-bagbiter + (fn [k ref old new] + (let [credit (get-in new [:runner :credit])] + (when (not= (get-in old [:runner :credit]) credit) + (swap! ref assoc-in [:runner :hand-size :base] credit)))))) + :leave-play (req (remove-watch state :theophilius-bagbiter) + (swap! state assoc-in [:runner :hand-size :base] 5))} + + "Thunder Art Gallery" + (let [first-event-check (fn [state fn1 fn2] (and (fn1 state :runner :runner-lose-tag #(= :runner (second %))) + (fn2 state :runner :runner-prevent (fn [t] (seq (filter #(some #{:tag} %) t)))))) + ability {:choices {:req #(and (runner? %) + (in-hand? %) + (not (event? %)))} + :async true + :prompt (msg "Select a card to install with Thunder Art Gallery") + :effect (req (if (and (runner-can-install? state side target) + (can-pay? state side (merge eid {:source card + :source-type :runner-install}) target nil + (install-cost state side target [:credit (dec (:cost target))]))) + (do (install-cost-bonus state side [:credit -1]) + (system-msg state side "uses Thunder Art Gallery to install a card.") + (runner-install state side (merge eid {:source card + :source-type :runner-install}) target nil)) + (effect-completed state side eid))) + :cancel-effect (effect (effect-completed eid))}] + {:events {:runner-lose-tag (assoc ability :req (req (and (first-event-check state first-event? no-event?) (= side :runner)))) + :runner-prevent (assoc ability :req (req (and (first-event-check state no-event? first-event?) (seq (filter #(some #{:tag} %) targets)))))}}) + + "Trickster Taka" + (assoc + (companion-builder + (req (and (pos? (get-counters (get-card state card) :credit)) + run + (not (:successful run)) + (not (:unsuccessful run)))) + (effect (show-wait-prompt :corp "Runner to take decision on Trickster Taka") + (continue-ability + {:prompt "Take 1 tag or trash Trickster Taka?" + :choices ["Take 1 tag" "Trash"] + :player :runner + :async true + :effect (req (clear-wait-prompt state :corp) + (if (= target "Trash") + (do + (trash state :runner card) + (system-msg state :runner "trashes Trickster Taka") + (effect-completed state side eid)) + (do + (system-msg state :runner "takes 1 tag to avoid trashing Trickster Taka") + (gain-tags state :runner eid 1))))} + card nil)) + {:msg "take 1 [Credits]" + :effect (effect (add-counter card :credit -1) + (trigger-event :spent-stealth-credit card) + (gain-credits 1))}) + :interactions {:pay-credits {:req (req (or (= :ability (:source-type eid)) + (program? target) + (:run @state))) + :type :credit}}) + + "Tri-maf Contact" + {:abilities [{:cost [:click 1] :msg "gain 2 [Credits]" :once :per-turn + :effect (effect (gain-credits 2))}] + :trash-effect {:effect (effect (damage eid :meat 3 {:unboostable true :card card}))}} + + "Tyson Observatory" + {:abilities [{:prompt "Choose a piece of Hardware" :msg (msg "add " (:title target) " to their Grip") + :choices (req (cancellable (filter hardware? (:deck runner)) :sorted)) + :cost [:click 2] + :effect (effect (trigger-event :searched-stack nil) + (shuffle! :deck) + (move target :hand))}]} + + "Underworld Contact" + (let [ability {:label "Gain 1 [Credits] (start of turn)" + :once :per-turn + :effect (req (when (and (>= (:link runner) 2) + (:runner-phase-12 @state)) + (system-msg state :runner (str "uses " (:title card) " to gain 1 [Credits]")) + (gain-credits state :runner 1)))}] + {:flags {:drip-economy true} + :abilities [ability] + :events {:runner-turn-begins ability}}) + + "Utopia Shard" + (shard-constructor :hq "force the Corp to discard 2 cards from HQ at random" + (effect (trash-cards :corp (take 2 (shuffle (:hand corp)))))) + + "Virus Breeding Ground" + {:events {:runner-turn-begins {:effect (effect (add-counter card :virus 1))}} + :abilities [{:cost [:click 1] + :req (req (pos? (get-counters card :virus))) + :effect (req (resolve-ability + state side + {:msg (msg "move 1 virus counter to " (:title target)) + :choices {:req #(pos? (get-virus-counters state %))} + :effect (req (add-counter state side card :virus -1) + (add-counter state side target :virus 1))} + card nil))}]} + + "Wasteland" + {:events {:runner-trash {:req (req (and (first-installed-trash-own? state :runner) + (installed? target) + (= (:side target) "Runner"))) + :effect (effect (gain-credits 1)) + :msg "gain 1 [Credits]"}}} + + "Whistleblower" + (letfn [(steal-events [named-agenda] + {:run-ends {:effect (effect (unregister-events card {:events {:access nil + :run-ends nil}}))} + :access {:req (req (= (:title target) named-agenda)) + :once :per-run :async true - :yes-ability {:effect (req (wait-for (resolve-ability state side (pick-virus-counters-to-spend 2) card nil) - (if (:number async-result) - (do (system-msg state side (str "spends " (:msg async-result) " on The Nihilist")) - (show-wait-prompt state :runner "Corp to decide") - (continue-ability state side corp-choice card nil)) - (effect-completed state side eid))))}}] - {:events {:runner-turn-begins {:interactive (req true) - :req has-2-virus-tokens? - :optional maybe-spend-2} - :runner-install {:msg "add 2 virus tokens to The Nihilist" - :effect (effect (add-counter card :virus 2)) - :req (req (has-subtype? target "Virus")) - :once :per-turn}}})) - -(define-card "The Shadow Net" - (letfn [(events [runner] (filter #(and (event? %) (not (has-subtype? % "Priority"))) (:discard runner)))] - {:abilities [{:cost [:click 1 :forfeit] - :req (req (pos? (count (events runner)))) - :label "Play an event from your Heap, ignoring all costs" - :prompt "Choose an event to play" - :msg (msg "play " (:title target) " from the Heap, ignoring all costs") - :choices (req (cancellable (events runner) :sorted)) - :effect (effect (play-instant nil target {:ignore-cost true}))}]})) - -(define-card "The Source" - {:effect (effect (update-all-advancement-costs)) - :leave-play (effect (update-all-advancement-costs)) - :events {:agenda-scored {:effect (effect (trash card))} - :agenda-stolen {:effect (effect (trash card))} - :pre-advancement-cost {:effect (effect (advancement-cost-bonus 1))} - :pre-steal-cost {:effect (effect (steal-cost-bonus [:credit 3]))}}}) - -(define-card "The Supplier" - (let [ability {:label "Install a hosted card (start of turn)" - :prompt "Choose a card hosted on The Supplier to install" - :req (req (some #(can-pay? state side eid card nil (modified-install-cost state side % [:credit -2])) - (:hosted card))) - :choices {:req #(and (= "The Supplier" (:title (:host %))) - (runner? %))} + :effect (effect (steal eid target))}})] + {:events {:successful-run {:optional {:autoresolve (get-autoresolve :auto-name-agenda) + :prompt "Trash Whistleblower to name an agenda?" + :yes-ability {:prompt "Name an agenda" + :choices {:card-title (req (and (corp? target) + (agenda? target)))} + :effect (effect (system-msg (str "trashes " (:title card) + " to name " (:title target))) + (register-events (steal-events target) + (dissoc card :zone)) + (trash eid card {:unpreventable true + :cause :ability-cost}))}}}} + :abilities [(set-autoresolve :auto-name-agenda "Whistleblower's ability")]}) + + "Wireless Net Pavilion" + {:effect (effect (trash-resource-bonus -2)) + :leave-play (effect (trash-resource-bonus 2)) + :implementation "Errata from FAQ 3.0.1: should be unique"} + + "Woman in the Red Dress" + (let [ability {:msg (msg "reveal " (:title (first (:deck corp))) " on the top of R&D") + :label "Reveal the top card of R&D (start of turn)" + :once :per-turn + :req (req (:runner-phase-12 @state)) + :effect (effect (reveal (:title (first (:deck corp)))) + (show-wait-prompt :runner "Corp to decide whether or not to draw with Woman in the Red Dress") + (resolve-ability + {:optional + {:player :corp + :prompt (msg "Draw " (:title (first (:deck corp))) "?") + :yes-ability {:effect (effect (clear-wait-prompt :runner) + (system-msg (str "draws " (:title (first (:deck corp))))) + (draw))} + :no-ability {:effect (effect (clear-wait-prompt :runner) + (system-msg "doesn't draw with Woman in the Red Dress"))}}} + card nil))}] + {:events {:runner-turn-begins ability} + :abilities [ability]}) + + "Wyldside" + {:flags {:runner-turn-draw true + :runner-phase-12 (req (< 1 (count (filter #(card-flag? % :runner-turn-draw true) + (cons (get-in @state [:runner :identity]) + (all-active-installed state :runner))))))} + :events {:runner-turn-begins {:async true + :effect (req (lose state side :click 1) + (when-not (get-in @state [:per-turn (:cid card)]) + (system-msg state side "uses Wyldside to draw 2 cards and lose [Click]") + (draw state side eid 2 nil)))}} + :abilities [{:msg "draw 2 cards and lose [Click]" :once :per-turn - :effect (req - (runner-can-install? state side target nil) - (when (and (can-pay? state side eid card nil (modified-install-cost state side target [:credit -2])) - (not (and (:uniqueness target) (in-play? state target)))) - (install-cost-bonus state side [:credit -2]) - (runner-install state side target) - (system-msg state side (str "uses The Supplier to install " (:title target) " lowering its install cost by 2")) - (update! state side (-> card - (assoc :supplier-installed (:cid target)) - (update-in [:hosted] - (fn [coll] - (remove-once #(same-card? % target) coll)))))))}] - {:flags {:drip-economy true} ; not technically drip economy, but has an interaction with Drug Dealer - :abilities [{:label "Host a resource or piece of hardware" :cost [:click 1] - :prompt "Select a card to host on The Supplier" - :choices {:req #(and (#{"Resource" "Hardware"} (:type %)) - (in-hand? %))} - :effect (effect (host card target)) :msg (msg "host " (:title target) "")} - ability] - ; A card installed by The Supplier is ineligible to receive the turn-begins event for this turn. - :suppress {:runner-turn-begins {:req (req (= (:cid target) (:supplier-installed (get-card state card))))}} - :events {:runner-turn-begins ability - :runner-turn-ends {:req (req (:supplier-installed card)) - :effect (effect (update! (dissoc card :supplier-installed)))}}})) - -(define-card "The Turning Wheel" - (let [ttw-ab (fn [m s] - {:label (str "Access an additional card in " m) - :counter-cost [:power 2] - :req (req run) - :msg (msg "access 1 additional card from " m " for the remainder of the run") - :effect (req (access-bonus state side s 1))})] - {:events {:agenda-stolen {:effect (effect (update! (assoc card :agenda-stolen true))) - :silent (req true)} - :run-ends {:effect (req (when (and (not (:agenda-stolen card)) - (#{:hq :rd} target)) - (add-counter state side card :power 1) - (system-msg state :runner (str "places a power counter on " (:title card)))) - (update! state side (dissoc (get-card state card) :agenda-stolen))) - :silent (req true)}} - :abilities [(ttw-ab "R&D" :rd) - (ttw-ab "HQ" :hq)]})) - -(define-card "Theophilius Bagbiter" - {:effect (req (lose-credits state :runner :all) - (lose state :runner :run-credit :all) - (swap! state assoc-in [:runner :hand-size :base] 0) - (add-watch state :theophilius-bagbiter - (fn [k ref old new] - (let [credit (get-in new [:runner :credit])] - (when (not= (get-in old [:runner :credit]) credit) - (swap! ref assoc-in [:runner :hand-size :base] credit)))))) - :leave-play (req (remove-watch state :theophilius-bagbiter) - (swap! state assoc-in [:runner :hand-size :base] 5))}) - -(define-card "Thunder Art Gallery" - (let [first-event-check (fn [state fn1 fn2] (and (fn1 state :runner :runner-lose-tag #(= :runner (second %))) - (fn2 state :runner :runner-prevent (fn [t] (seq (filter #(some #{:tag} %) t)))))) - ability {:choices {:req #(and (runner? %) - (in-hand? %) - (not (event? %)))} :async true - :prompt (msg "Select a card to install with Thunder Art Gallery") - :effect (req (if (and (runner-can-install? state side target) - (can-pay? state side (merge eid {:source card - :source-type :runner-install}) target nil - (install-cost state side target [:credit (dec (:cost target))]))) - (do (install-cost-bonus state side [:credit -1]) - (system-msg state side "uses Thunder Art Gallery to install a card.") - (runner-install state side (merge eid {:source card - :source-type :runner-install}) target nil)) - (effect-completed state side eid))) - :cancel-effect (effect (effect-completed eid))}] - {:events {:runner-lose-tag (assoc ability :req (req (and (first-event-check state first-event? no-event?) (= side :runner)))) - :runner-prevent (assoc ability :req (req (and (first-event-check state no-event? first-event?) (seq (filter #(some #{:tag} %) targets)))))}})) - -(define-card "Trickster Taka" - (assoc - (companion-builder - (req (and (pos? (get-counters (get-card state card) :credit)) - run - (not (:successful run)) - (not (:unsuccessful run)))) - (effect (show-wait-prompt :corp "Runner to take decision on Trickster Taka") - (continue-ability - {:prompt "Take 1 tag or trash Trickster Taka?" - :choices ["Take 1 tag" "Trash"] - :player :runner - :async true - :effect (req (clear-wait-prompt state :corp) - (if (= target "Trash") - (do - (trash state :runner card) - (system-msg state :runner "trashes Trickster Taka") - (effect-completed state side eid)) - (do - (system-msg state :runner "takes 1 tag to avoid trashing Trickster Taka") - (gain-tags state :runner eid 1))))} - card nil)) - {:msg "take 1 [Credits]" - :effect (effect (add-counter card :credit -1) - (trigger-event :spent-stealth-credit card) - (gain-credits 1))}) - :interactions {:pay-credits {:req (req (or (= :ability (:source-type eid)) - (program? target) - (:run @state))) - :type :credit}})) - -(define-card "Tri-maf Contact" - {:abilities [{:cost [:click 1] :msg "gain 2 [Credits]" :once :per-turn - :effect (effect (gain-credits 2))}] - :trash-effect {:effect (effect (damage eid :meat 3 {:unboostable true :card card}))}}) - -(define-card "Tyson Observatory" - {:abilities [{:prompt "Choose a piece of Hardware" :msg (msg "add " (:title target) " to their Grip") - :choices (req (cancellable (filter hardware? (:deck runner)) :sorted)) - :cost [:click 2] - :effect (effect (trigger-event :searched-stack nil) - (shuffle! :deck) - (move target :hand))}]}) - -(define-card "Underworld Contact" - (let [ability {:label "Gain 1 [Credits] (start of turn)" - :once :per-turn - :effect (req (when (and (>= (:link runner) 2) - (:runner-phase-12 @state)) - (system-msg state :runner (str "uses " (:title card) " to gain 1 [Credits]")) - (gain-credits state :runner 1)))}] - {:flags {:drip-economy true} - :abilities [ability] - :events {:runner-turn-begins ability}})) - -(define-card "Utopia Shard" - (shard-constructor :hq "force the Corp to discard 2 cards from HQ at random" - (effect (trash-cards :corp (take 2 (shuffle (:hand corp))))))) - -(define-card "Virus Breeding Ground" - {:events {:runner-turn-begins {:effect (effect (add-counter card :virus 1))}} - :abilities [{:cost [:click 1] - :req (req (pos? (get-counters card :virus))) - :effect (req (resolve-ability - state side - {:msg (msg "move 1 virus counter to " (:title target)) - :choices {:req #(pos? (get-virus-counters state %))} - :effect (req (add-counter state side card :virus -1) - (add-counter state side target :virus 1))} - card nil))}]}) - -(define-card "Wasteland" - {:events {:runner-trash {:req (req (and (first-installed-trash-own? state :runner) - (installed? target) - (= (:side target) "Runner"))) - :effect (effect (gain-credits 1)) - :msg "gain 1 [Credits]"}}}) - -(define-card "Whistleblower" - (letfn [(steal-events [named-agenda] - {:run-ends {:effect (effect (unregister-events card {:events {:access nil - :run-ends nil}}))} - :access {:req (req (= (:title target) named-agenda)) - :once :per-run - :async true - :effect (effect (steal eid target))}})] - {:events {:successful-run {:optional {:autoresolve (get-autoresolve :auto-name-agenda) - :prompt "Trash Whistleblower to name an agenda?" - :yes-ability {:prompt "Name an agenda" - :choices {:card-title (req (and (corp? target) - (agenda? target)))} - :effect (effect (system-msg (str "trashes " (:title card) - " to name " (:title target))) - (register-events (steal-events target) - (dissoc card :zone)) - (trash eid card {:unpreventable true - :cause :ability-cost}))}}}} - :abilities [(set-autoresolve :auto-name-agenda "Whistleblower's ability")]})) - -(define-card "Wireless Net Pavilion" - {:effect (effect (trash-resource-bonus -2)) - :leave-play (effect (trash-resource-bonus 2)) - :implementation "Errata from FAQ 3.0.1: should be unique"}) - -(define-card "Woman in the Red Dress" - (let [ability {:msg (msg "reveal " (:title (first (:deck corp))) " on the top of R&D") - :label "Reveal the top card of R&D (start of turn)" - :once :per-turn - :req (req (:runner-phase-12 @state)) - :effect (effect (reveal (:title (first (:deck corp)))) - (show-wait-prompt :runner "Corp to decide whether or not to draw with Woman in the Red Dress") - (resolve-ability - {:optional - {:player :corp - :prompt (msg "Draw " (:title (first (:deck corp))) "?") - :yes-ability {:effect (effect (clear-wait-prompt :runner) - (system-msg (str "draws " (:title (first (:deck corp))))) - (draw))} - :no-ability {:effect (effect (clear-wait-prompt :runner) - (system-msg "doesn't draw with Woman in the Red Dress"))}}} - card nil))}] - {:events {:runner-turn-begins ability} - :abilities [ability]})) - -(define-card "Wyldside" - {:flags {:runner-turn-draw true - :runner-phase-12 (req (< 1 (count (filter #(card-flag? % :runner-turn-draw true) - (cons (get-in @state [:runner :identity]) - (all-active-installed state :runner))))))} - :events {:runner-turn-begins {:async true - :effect (req (lose state side :click 1) - (when-not (get-in @state [:per-turn (:cid card)]) - (system-msg state side "uses Wyldside to draw 2 cards and lose [Click]") - (draw state side eid 2 nil)))}} - :abilities [{:msg "draw 2 cards and lose [Click]" - :once :per-turn - :async true - :effect (effect (draw eid 2 nil))}]}) - -(define-card "Xanadu" - {:events {:pre-rez-cost {:req (req (ice? target)) - :effect (effect (rez-cost-bonus 1))}}}) - -(define-card "Zona Sul Shipping" - (trash-when-tagged-contructor - "Zona Sul Shipping" - {:events {:runner-turn-begins {:effect (effect (add-counter card :credit 1))}} - :abilities [{:cost [:click 1] - :msg (msg "gain " (get-counters card :credit) " [Credits]") - :label "Take all credits" - :effect (effect (gain-credits (get-counters card :credit)) - (add-counter card :credit - (- (get-counters card :credit))))}]})) + :effect (effect (draw eid 2 nil))}]} + + "Xanadu" + {:events {:pre-rez-cost {:req (req (ice? target)) + :effect (effect (rez-cost-bonus 1))}}} + + "Zona Sul Shipping" + (trash-when-tagged-contructor + "Zona Sul Shipping" + {:events {:runner-turn-begins {:effect (effect (add-counter card :credit 1))}} + :abilities [{:cost [:click 1] + :msg (msg "gain " (get-counters card :credit) " [Credits]") + :label "Take all credits" + :effect (effect (gain-credits (get-counters card :credit)) + (add-counter card :credit + (- (get-counters card :credit))))}]})}) diff --git a/src/clj/game/cards/upgrades.clj b/src/clj/game/cards/upgrades.clj index b6611bc18b..d7273bd011 100644 --- a/src/clj/game/cards/upgrades.clj +++ b/src/clj/game/cards/upgrades.clj @@ -1,7 +1,7 @@ (ns game.cards.upgrades (:require [game.core :refer :all] [game.core.eid :refer [effect-completed]] - [game.core.card-defs :refer [card-def define-card]] + [game.core.card-defs :refer [card-def]] [game.utils :refer :all] [game.macros :refer [effect req msg wait-for continue-ability]] [clojure.string :refer [split-lines split join lower-case includes? starts-with?]] @@ -14,1368 +14,1369 @@ NOTE: If card has an :effect or :leave-play, see function source for the things they need to do to ensure tracking works." [unit-costs cdef] (let [store-key [:special :current-added-cost]] - (letfn [(reset-cost [state card amt] - (swap! state update-in [:corp :servers (second (:zone card)) :additional-cost] - #(merge-costs (concat % (vec (flatten (map (fn [x] [(first x) (* amt (second x))]) (partition 2 unit-costs)))))))) - (recompute-cost [state card] - (let [change ((fnil - 0 0) (get-counters card :power) (get-in card store-key))] - (reset-cost state card change) - (update! state :corp (assoc-in card store-key (get-counters card :power))))) - (clear-cost [state card] - (reset-cost state card (- (get-in card store-key 0))) - (update! state :corp (assoc-in card store-key 0)))] - (merge cdef - {:events (merge {:counter-added {:req (req (same-card? target card)) - :effect (req (recompute-cost state card))}} - (:events cdef)) - :effect (if (:effect cdef) (:effect cdef) - (req (add-counter state side (get-card state card) :power 0))) - :leave-play (if (:leave-play cdef) (:leave-play cdef) - (req (clear-cost state card)))})))) + (letfn [(reset-cost [state card amt] + (swap! state update-in [:corp :servers (second (:zone card)) :additional-cost] + #(merge-costs (concat % (vec (flatten (map (fn [x] [(first x) (* amt (second x))]) (partition 2 unit-costs)))))))) + (recompute-cost [state card] + (let [change ((fnil - 0 0) (get-counters card :power) (get-in card store-key))] + (reset-cost state card change) + (update! state :corp (assoc-in card store-key (get-counters card :power))))) + (clear-cost [state card] + (reset-cost state card (- (get-in card store-key 0))) + (update! state :corp (assoc-in card store-key 0)))] + (merge cdef + {:events (merge {:counter-added {:req (req (same-card? target card)) + :effect (req (recompute-cost state card))}} + (:events cdef)) + :effect (if (:effect cdef) (:effect cdef) + (req (add-counter state side (get-card state card) :power 0))) + :leave-play (if (:leave-play cdef) (:leave-play cdef) + (req (clear-cost state card)))})))) ;; Card definitions -(define-card "Akitaro Watanabe" - {:events {:pre-rez-cost {:req (req (and (ice? target) - (= (card->server state card) (card->server state target)))) - :effect (effect (rez-cost-bonus -2))}}}) - -(define-card "Amazon Industrial Zone" - {:events - {:corp-install {:optional {:req (req (and (ice? target) - (protecting-same-server? card target))) - :prompt "Rez ICE with rez cost lowered by 3?" :priority 2 - :yes-ability {:effect (effect (rez-cost-bonus -3) (rez target))}}}}}) - -(define-card "Arella Salvatore" - (let [select-ability - {:prompt "Select a card to install with Arella Salvatore" - :choices {:req #(and (corp-installable-type? %) - (in-hand? %) - (corp? %))} - :async true - :cancel-effect (req (effect-completed state side eid)) - :effect (req (wait-for (corp-install state :corp target nil {:ignore-all-cost true :display-message false}) - (let [inst-target (find-latest state target)] - (add-prop state :corp inst-target :advance-counter 1 {:placed true}) - (system-msg state :corp - (str "uses Arella Salvatore to install and place a counter on " - (card-str state inst-target) ", ignoring all costs")) - (effect-completed state side eid))))}] - {:events - {:agenda-scored - {:req (req (= (:previous-zone target) (:zone card))) - :interactive (req (some corp-installable-type? (:hand corp))) - :silent (req (not-any? corp-installable-type? (:hand corp))) - :async true - :effect (req (if (some corp-installable-type? (:hand corp)) - (continue-ability state side select-ability card nil) - (effect-completed state side eid)))}}})) - -(define-card "Ash 2X3ZB9CY" - {:events {:successful-run {:interactive (req true) - :req (req this-server) - :trace {:base 4 - :successful - {:msg "prevent the Runner from accessing cards other than Ash 2X3ZB9CY" - :effect (req (max-access state side 0) - (when-not (:replace-access (get-in @state [:run :run-effect])) - (let [ash card] - (swap! state update-in [:run :run-effect] - #(assoc % :replace-access - {:mandatory true - :async true - :effect (req (wait-for (access-card state :runner ash) - (effect-completed state side eid))) - :card ash})))))}}}}}) - -(define-card "Awakening Center" - {:can-host (req (ice? target)) - :abilities [{:label "Host a piece of Bioroid ICE" - :cost [:click 1] - :prompt "Select a piece of Bioroid ICE to host on Awakening Center" - :choices {:req #(and (ice? %) - (has-subtype? % "Bioroid") - (in-hand? %))} - :msg "host a piece of Bioroid ICE" - :effect (req (corp-install state side target card {:ignore-all-cost true}))} - {:req (req (and this-server - (zero? (get-in @state [:run :position])))) - :label "Rez a hosted piece of Bioroid ICE" - :prompt "Choose a piece of Bioroid ICE to rez" :choices (req (:hosted card)) - :msg (msg "lower the rez cost of " (:title target) " by 7 [Credits] and force the Runner to encounter it") - :effect (effect (rez-cost-bonus -7) (rez target) - (update! (dissoc (get-card state target) :facedown)) - (register-events {:run-ends - {:effect (req (doseq [c (:hosted card)] - (when (:rezzed c) - (trash state side c))) - (unregister-events state side card))}} card))}] - :events {:run-ends nil}}) - -(define-card "Bamboo Dome" - (letfn [(dome [dcard] - {:prompt "Select a card to add to HQ" - :async true - :choices {:req #(and (corp? %) - (in-play-area? %))} - :msg "move a card to HQ" - :effect (effect (move target :hand) - (continue-ability (put dcard) dcard nil))}) - (put [dcard] - {:prompt "Select first card to put back onto R&D" - :async true - :choices {:req #(and (corp? %) - (in-play-area? %))} - :msg "move remaining cards back to R&D" - :effect (effect (move target :deck {:front true}) - (move (first (get-in @state [:corp :play-area])) :deck {:front true}) - (clear-wait-prompt :runner) - (effect-completed eid))})] - {:init {:root "R&D"} - :install-req (req (filter #{"R&D"} targets)) - :abilities [{:cost [:click 1] - :req (req (>= (count (:deck corp)) 3)) - :async true - :msg (msg (str "reveal " (join ", " (map :title (take 3 (:deck corp)))) " from R&D")) - :label "Reveal the top 3 cards of R&D. Secretly choose 1 to add to HQ. Return the others to the top of R&D, in any order." - :effect (req (reveal state side (take 3 (:deck corp))) - (doseq [c (take 3 (:deck corp))] - (move state side c :play-area)) - (show-wait-prompt state :runner "Corp to use Bamboo Dome") - (continue-ability state side (dome card) card nil))}]})) - -(define-card "Ben Musashi" - (let [bm {:req (req (or (in-same-server? card target) - (from-same-server? card target))) - :effect (effect (steal-cost-bonus [:net 2]))}] - {:trash-effect - {:req (req (and (= :servers (first (:previous-zone card))) (:run @state))) - :effect (effect (register-events {:pre-steal-cost (assoc bm :req (req (or (= (:zone target) (:previous-zone card)) - (= (central->zone (:zone target)) - (butlast (:previous-zone card)))))) - :run-ends {:effect (effect (unregister-events card))}} - (assoc card :zone '(:discard))))} - :events {:pre-steal-cost bm :run-ends nil}})) - -(define-card "Bernice Mai" - {:events {:successful-run {:interactive (req true) - :req (req this-server) - :trace {:base 5 - :successful {:msg "give the Runner 1 tag" - :async true - :effect (effect (gain-tags :corp eid 1))} - :unsuccessful - {:effect (effect (system-msg "trashes Bernice Mai from the unsuccessful trace") - (trash card))}}}}}) - -(define-card "Bio Vault" - {:install-req (req (remove #{"HQ" "R&D" "Archives"} targets)) - :advanceable :always - :abilities [{:label "[Trash]: End the run" - :advance-counter-cost 2 - :req (req (:run @state)) - :msg "end the run" - :async true - :effect (req (wait-for (trash state :corp card {:cause :ability-cost}) - (end-run state :corp eid card)))}]}) - -(define-card "Black Level Clearance" - {:events {:successful-run - {:interactive (req true) - :req (req this-server) - :async true - :effect (effect (continue-ability - {:prompt "Take 1 brain damage or jack out?" - :player :runner - :choices ["Take 1 brain damage" "Jack out"] - :effect (req (if (= target "Take 1 brain damage") - (damage state side eid :brain 1 {:card card}) - (do (jack-out state side nil) - (swap! state update-in [:runner :prompt] rest) - (close-access-prompt state side) - (handle-end-run state side) - (gain-credits state :corp 5) - (draw state :corp) - (system-msg state :corp (str "gains 5 [Credits] and draws 1 card. Black Level Clearance is trashed")) - (trash state side card) - (effect-completed state side eid))))} - card nil))}}}) - -(define-card "Breaker Bay Grid" - {:events {:pre-rez-cost {:req (req (in-same-server? card target)) - :effect (effect (rez-cost-bonus -5))}}}) - -(define-card "Bryan Stinson" - {:abilities [{:cost [:click 1] - :req (req (and (< (:credit runner) 6) - (pos? (count (filter #(and (operation? %) - (has-subtype? % "Transaction")) (:discard corp)))))) - :label "Play a transaction operation from Archives, ignoring all costs, and remove it from the game" - :prompt "Choose a transaction operation to play" - :msg (msg "play " (:title target) " from Archives, ignoring all costs, and removes it from the game") - :choices (req (cancellable (filter #(and (operation? %) - (has-subtype? % "Transaction")) (:discard corp)) :sorted)) - :effect (effect (play-instant nil (assoc-in target [:special :rfg-when-trashed] true) {:ignore-cost true}) - (move target :rfg))}]}) - -(define-card "Calibration Testing" - {:install-req (req (remove #{"HQ" "R&D" "Archives"} targets)) - :abilities [{:label "[Trash]: Place 1 advancement token on a card in this server" - :async true - :effect (effect (continue-ability - {:prompt "Select a card in this server" - :choices {:req #(in-same-server? % card)} - :async true - :msg (msg "place an advancement token on " (card-str state target)) - :effect (effect (add-prop target :advance-counter 1 {:placed true}) - (trash eid card {:cause :ability-cost}))} - card nil))}]}) - -(define-card "Caprice Nisei" - (let [ability {:req (req (and this-server - (zero? (:position run)))) - :psi {:not-equal {:msg "end the run" - :effect (effect (end-run eid card))}}}] - {:events {:approach-server ability} - :abilities [ability]})) - -(define-card "ChiLo City Grid" - {:events {:successful-trace {:req (req this-server) - :async true - :effect (effect (gain-tags :corp eid 1)) - :msg "give the Runner 1 tag"}}}) - -(define-card "Code Replicator" - {:abilities [{:label "[Trash]: Force the runner to approach the passed piece of ice again" - :req (req (and this-server - (> (count (get-run-ices state)) (:position run)) - (:rezzed (get-in (:ices (card->server state card)) [(:position run)])))) - :effect (req (let [icename (:title (get-in (:ices (card->server state card)) [(:position run)]))] - (trash state :corp (get-card state card)) - (swap! state update-in [:run] #(assoc % :position (inc (:position run)))) - (system-msg state :corp (str "trashes Code Replicator to make the runner approach " - icename " again"))))}]}) - -(define-card "Cold Site Server" - (counter-based-extra-cost +(def card-definitions + {"Akitaro Watanabe" + {:events {:pre-rez-cost {:req (req (and (ice? target) + (= (card->server state card) (card->server state target)))) + :effect (effect (rez-cost-bonus -2))}}} + + "Amazon Industrial Zone" + {:events + {:corp-install {:optional {:req (req (and (ice? target) + (protecting-same-server? card target))) + :prompt "Rez ICE with rez cost lowered by 3?" :priority 2 + :yes-ability {:effect (effect (rez-cost-bonus -3) (rez target))}}}}} + + "Arella Salvatore" + (let [select-ability + {:prompt "Select a card to install with Arella Salvatore" + :choices {:req #(and (corp-installable-type? %) + (in-hand? %) + (corp? %))} + :async true + :cancel-effect (req (effect-completed state side eid)) + :effect (req (wait-for (corp-install state :corp target nil {:ignore-all-cost true :display-message false}) + (let [inst-target (find-latest state target)] + (add-prop state :corp inst-target :advance-counter 1 {:placed true}) + (system-msg state :corp + (str "uses Arella Salvatore to install and place a counter on " + (card-str state inst-target) ", ignoring all costs")) + (effect-completed state side eid))))}] + {:events + {:agenda-scored + {:req (req (= (:previous-zone target) (:zone card))) + :interactive (req (some corp-installable-type? (:hand corp))) + :silent (req (not-any? corp-installable-type? (:hand corp))) + :async true + :effect (req (if (some corp-installable-type? (:hand corp)) + (continue-ability state side select-ability card nil) + (effect-completed state side eid)))}}}) + + "Ash 2X3ZB9CY" + {:events {:successful-run {:interactive (req true) + :req (req this-server) + :trace {:base 4 + :successful + {:msg "prevent the Runner from accessing cards other than Ash 2X3ZB9CY" + :effect (req (max-access state side 0) + (when-not (:replace-access (get-in @state [:run :run-effect])) + (let [ash card] + (swap! state update-in [:run :run-effect] + #(assoc % :replace-access + {:mandatory true + :async true + :effect (req (wait-for (access-card state :runner ash) + (effect-completed state side eid))) + :card ash})))))}}}}} + + "Awakening Center" + {:can-host (req (ice? target)) + :abilities [{:label "Host a piece of Bioroid ICE" + :cost [:click 1] + :prompt "Select a piece of Bioroid ICE to host on Awakening Center" + :choices {:req #(and (ice? %) + (has-subtype? % "Bioroid") + (in-hand? %))} + :msg "host a piece of Bioroid ICE" + :effect (req (corp-install state side target card {:ignore-all-cost true}))} + {:req (req (and this-server + (zero? (get-in @state [:run :position])))) + :label "Rez a hosted piece of Bioroid ICE" + :prompt "Choose a piece of Bioroid ICE to rez" :choices (req (:hosted card)) + :msg (msg "lower the rez cost of " (:title target) " by 7 [Credits] and force the Runner to encounter it") + :effect (effect (rez-cost-bonus -7) (rez target) + (update! (dissoc (get-card state target) :facedown)) + (register-events {:run-ends + {:effect (req (doseq [c (:hosted card)] + (when (:rezzed c) + (trash state side c))) + (unregister-events state side card))}} card))}] + :events {:run-ends nil}} + + "Bamboo Dome" + (letfn [(dome [dcard] + {:prompt "Select a card to add to HQ" + :async true + :choices {:req #(and (corp? %) + (in-play-area? %))} + :msg "move a card to HQ" + :effect (effect (move target :hand) + (continue-ability (put dcard) dcard nil))}) + (put [dcard] + {:prompt "Select first card to put back onto R&D" + :async true + :choices {:req #(and (corp? %) + (in-play-area? %))} + :msg "move remaining cards back to R&D" + :effect (effect (move target :deck {:front true}) + (move (first (get-in @state [:corp :play-area])) :deck {:front true}) + (clear-wait-prompt :runner) + (effect-completed eid))})] + {:init {:root "R&D"} + :install-req (req (filter #{"R&D"} targets)) + :abilities [{:cost [:click 1] + :req (req (>= (count (:deck corp)) 3)) + :async true + :msg (msg (str "reveal " (join ", " (map :title (take 3 (:deck corp)))) " from R&D")) + :label "Reveal the top 3 cards of R&D. Secretly choose 1 to add to HQ. Return the others to the top of R&D, in any order." + :effect (req (reveal state side (take 3 (:deck corp))) + (doseq [c (take 3 (:deck corp))] + (move state side c :play-area)) + (show-wait-prompt state :runner "Corp to use Bamboo Dome") + (continue-ability state side (dome card) card nil))}]}) + + "Ben Musashi" + (let [bm {:req (req (or (in-same-server? card target) + (from-same-server? card target))) + :effect (effect (steal-cost-bonus [:net 2]))}] + {:trash-effect + {:req (req (and (= :servers (first (:previous-zone card))) (:run @state))) + :effect (effect (register-events {:pre-steal-cost (assoc bm :req (req (or (= (:zone target) (:previous-zone card)) + (= (central->zone (:zone target)) + (butlast (:previous-zone card)))))) + :run-ends {:effect (effect (unregister-events card))}} + (assoc card :zone '(:discard))))} + :events {:pre-steal-cost bm :run-ends nil}}) + + "Bernice Mai" + {:events {:successful-run {:interactive (req true) + :req (req this-server) + :trace {:base 5 + :successful {:msg "give the Runner 1 tag" + :async true + :effect (effect (gain-tags :corp eid 1))} + :unsuccessful + {:effect (effect (system-msg "trashes Bernice Mai from the unsuccessful trace") + (trash card))}}}}} + + "Bio Vault" + {:install-req (req (remove #{"HQ" "R&D" "Archives"} targets)) + :advanceable :always + :abilities [{:label "[Trash]: End the run" + :advance-counter-cost 2 + :req (req (:run @state)) + :msg "end the run" + :async true + :effect (req (wait-for (trash state :corp card {:cause :ability-cost}) + (end-run state :corp eid card)))}]} + + "Black Level Clearance" + {:events {:successful-run + {:interactive (req true) + :req (req this-server) + :async true + :effect (effect (continue-ability + {:prompt "Take 1 brain damage or jack out?" + :player :runner + :choices ["Take 1 brain damage" "Jack out"] + :effect (req (if (= target "Take 1 brain damage") + (damage state side eid :brain 1 {:card card}) + (do (jack-out state side nil) + (swap! state update-in [:runner :prompt] rest) + (close-access-prompt state side) + (handle-end-run state side) + (gain-credits state :corp 5) + (draw state :corp) + (system-msg state :corp (str "gains 5 [Credits] and draws 1 card. Black Level Clearance is trashed")) + (trash state side card) + (effect-completed state side eid))))} + card nil))}}} + + "Breaker Bay Grid" + {:events {:pre-rez-cost {:req (req (in-same-server? card target)) + :effect (effect (rez-cost-bonus -5))}}} + + "Bryan Stinson" + {:abilities [{:cost [:click 1] + :req (req (and (< (:credit runner) 6) + (pos? (count (filter #(and (operation? %) + (has-subtype? % "Transaction")) (:discard corp)))))) + :label "Play a transaction operation from Archives, ignoring all costs, and remove it from the game" + :prompt "Choose a transaction operation to play" + :msg (msg "play " (:title target) " from Archives, ignoring all costs, and removes it from the game") + :choices (req (cancellable (filter #(and (operation? %) + (has-subtype? % "Transaction")) (:discard corp)) :sorted)) + :effect (effect (play-instant nil (assoc-in target [:special :rfg-when-trashed] true) {:ignore-cost true}) + (move target :rfg))}]} + + "Calibration Testing" + {:install-req (req (remove #{"HQ" "R&D" "Archives"} targets)) + :abilities [{:label "[Trash]: Place 1 advancement token on a card in this server" + :async true + :effect (effect (continue-ability + {:prompt "Select a card in this server" + :choices {:req #(in-same-server? % card)} + :async true + :msg (msg "place an advancement token on " (card-str state target)) + :effect (effect (add-prop target :advance-counter 1 {:placed true}) + (trash eid card {:cause :ability-cost}))} + card nil))}]} + + "Caprice Nisei" + (let [ability {:req (req (and this-server + (zero? (:position run)))) + :psi {:not-equal {:msg "end the run" + :effect (effect (end-run eid card))}}}] + {:events {:approach-server ability} + :abilities [ability]}) + + "ChiLo City Grid" + {:events {:successful-trace {:req (req this-server) + :async true + :effect (effect (gain-tags :corp eid 1)) + :msg "give the Runner 1 tag"}}} + + "Code Replicator" + {:abilities [{:label "[Trash]: Force the runner to approach the passed piece of ice again" + :req (req (and this-server + (> (count (get-run-ices state)) (:position run)) + (:rezzed (get-in (:ices (card->server state card)) [(:position run)])))) + :effect (req (let [icename (:title (get-in (:ices (card->server state card)) [(:position run)]))] + (trash state :corp (get-card state card)) + (swap! state update-in [:run] #(assoc % :position (inc (:position run)))) + (system-msg state :corp (str "trashes Code Replicator to make the runner approach " + icename " again"))))}]} + + "Cold Site Server" + (counter-based-extra-cost [:credit 1 :click 1] {:events {:corp-turn-begins {:req (req (pos? (get-counters card :power))) :msg " uses Cold Site Server to remove all hosted power counters" :effect (effect (add-counter card :power (- (get-counters card :power))))}} :abilities [{:cost [:click 1] :msg "place 1 power counter on Cold Site Server" - :effect (effect (add-counter card :power 1))}]})) - -(define-card "Corporate Troubleshooter" - {:abilities [{:label "[Trash]: Add strength to a rezzed ICE protecting this server" :choices :credit - :prompt "How many credits?" - :effect (req (let [boost target] - (resolve-ability - state side - {:choices {:req #(and (ice? %) - (rezzed? %))} - :msg (msg "add " boost " strength to " (:title target)) - :effect (req (update! state side (assoc card - :troubleshooter-target target - :troubleshooter-amount boost)) - (trash state side (get-card state card)) - (update-ice-strength state side target))} card nil)))}] - :events {:pre-ice-strength nil - :runner-turn-ends nil - :corp-turn-ends nil} - :trash-effect - {:effect (req (register-events - state side - (let [ct {:effect (req (unregister-events state side card) - (update! state side (dissoc card :troubleshooter-target)) - (update-ice-strength state side (:troubleshooter-target card)))}] - {:pre-ice-strength - {:req (req (same-card? target (:troubleshooter-target card))) - :effect (effect (ice-strength-bonus (:troubleshooter-amount card) target))} - :runner-turn-ends ct - :corp-turn-ends ct}) - card))}}) - -(define-card "Crisium Grid" - (let [suppress-event {:req (req (and this-server (not (same-card? target card))))}] - {:suppress {:pre-successful-run suppress-event - :successful-run suppress-event} - :events {:pre-successful-run - {:silent (req true) - :req (req this-server) - :effect (req (swap! state update-in [:run :run-effect] dissoc :replace-access) - (swap! state update-in [:run] dissoc :successful) - (swap! state update-in [:runner :register :successful-run] #(next %)))}}})) - -(define-card "Cyberdex Virus Suite" - {:flags {:rd-reveal (req true)} - :access {:async true - :effect (effect (show-wait-prompt :runner "Corp to use Cyberdex Virus Suite") - (continue-ability - {:optional {:prompt "Purge virus counters with Cyberdex Virus Suite?" - :yes-ability {:msg (msg "purge virus counters") - :effect (effect (clear-wait-prompt :runner) - (purge))} - :no-ability {:effect (effect (clear-wait-prompt :runner))}}} - card nil))} - :abilities [{:label "[Trash]: Purge virus counters" - :msg "purge virus counters" :effect (effect (trash card) (purge))}]}) - -(define-card "Daruma" - (letfn [(choose-swap [to-swap] - {:prompt (str "Select a card to swap with " (:title to-swap)) - :choices {:not-self true - :req #(and (corp? %) - (#{"Asset" "Agenda" "Upgrade"} (:type %)) - (or (in-hand? %) ; agenda, asset or upgrade from HQ - (and (installed? %) ; card installed in a server - ;; central upgrades are not in a server - (not (#{:hq :rd :archives} (first (:zone %)))))))} - :effect (req (wait-for (trash state :corp card nil) - (move state :corp to-swap (:zone target) {:keep-server-alive true}) - (move state :corp target (:zone to-swap) {:keep-server-alive true}) - (system-msg state :corp - (str "uses Daruma to swap " (card-str state to-swap) - " with " (card-str state target))) - (clear-wait-prompt state :runner))) - :cancel-effect (effect (clear-wait-prompt :runner))}) - (ability [card] - {:optional {:prompt "Trash Daruma to swap a card in this server?" - :yes-ability {:async true - :prompt "Select a card in this server to swap" - :choices {:req #(and (installed? %) - (in-same-server? card %)) - :not-self true} - :effect (effect (continue-ability (choose-swap target) card nil))} - :no-ability {:effect (effect (clear-wait-prompt :runner))}}})] - {:events {:approach-server {:async true - :effect (effect (show-wait-prompt :runner "Corp to use Daruma") - (continue-ability :corp (ability card) card nil))}}})) - -(define-card "Dedicated Technician Team" - {:recurring 2 - :interactions {:pay-credits {:req (req (and (= :corp-install (:source-type eid)) - (= (second (:zone card)) - (second (server->zone state (:source eid)))))) - :type :recurring}}}) - -(define-card "Defense Construct" - {:advanceable :always - :abilities [{:label "[Trash]: Add 1 facedown card from Archives to HQ for each advancement token" - :req (req (and run - (= (:server run) [:archives]) - (pos? (get-counters card :advancement)))) - :effect (effect (resolve-ability - {:show-discard true - :choices {:max (get-counters card :advancement) - :req #(and (corp? %) - (not (:seen %)) - (in-discard? %))} - :msg (msg "add " (count targets) " facedown cards in Archives to HQ") - :effect (req (doseq [c targets] - (move state side c :hand)))} + :effect (effect (add-counter card :power 1))}]}) + + "Corporate Troubleshooter" + {:abilities [{:label "[Trash]: Add strength to a rezzed ICE protecting this server" :choices :credit + :prompt "How many credits?" + :effect (req (let [boost target] + (resolve-ability + state side + {:choices {:req #(and (ice? %) + (rezzed? %))} + :msg (msg "add " boost " strength to " (:title target)) + :effect (req (update! state side (assoc card + :troubleshooter-target target + :troubleshooter-amount boost)) + (trash state side (get-card state card)) + (update-ice-strength state side target))} card nil)))}] + :events {:pre-ice-strength nil + :runner-turn-ends nil + :corp-turn-ends nil} + :trash-effect + {:effect (req (register-events + state side + (let [ct {:effect (req (unregister-events state side card) + (update! state side (dissoc card :troubleshooter-target)) + (update-ice-strength state side (:troubleshooter-target card)))}] + {:pre-ice-strength + {:req (req (same-card? target (:troubleshooter-target card))) + :effect (effect (ice-strength-bonus (:troubleshooter-amount card) target))} + :runner-turn-ends ct + :corp-turn-ends ct}) + card))}} + + "Crisium Grid" + (let [suppress-event {:req (req (and this-server (not (same-card? target card))))}] + {:suppress {:pre-successful-run suppress-event + :successful-run suppress-event} + :events {:pre-successful-run + {:silent (req true) + :req (req this-server) + :effect (req (swap! state update-in [:run :run-effect] dissoc :replace-access) + (swap! state update-in [:run] dissoc :successful) + (swap! state update-in [:runner :register :successful-run] #(next %)))}}}) + + "Cyberdex Virus Suite" + {:flags {:rd-reveal (req true)} + :access {:async true + :effect (effect (show-wait-prompt :runner "Corp to use Cyberdex Virus Suite") + (continue-ability + {:optional {:prompt "Purge virus counters with Cyberdex Virus Suite?" + :yes-ability {:msg (msg "purge virus counters") + :effect (effect (clear-wait-prompt :runner) + (purge))} + :no-ability {:effect (effect (clear-wait-prompt :runner))}}} + card nil))} + :abilities [{:label "[Trash]: Purge virus counters" + :msg "purge virus counters" :effect (effect (trash card) (purge))}]} + + "Daruma" + (letfn [(choose-swap [to-swap] + {:prompt (str "Select a card to swap with " (:title to-swap)) + :choices {:not-self true + :req #(and (corp? %) + (#{"Asset" "Agenda" "Upgrade"} (:type %)) + (or (in-hand? %) ; agenda, asset or upgrade from HQ + (and (installed? %) ; card installed in a server + ;; central upgrades are not in a server + (not (#{:hq :rd :archives} (first (:zone %)))))))} + :effect (req (wait-for (trash state :corp card nil) + (move state :corp to-swap (:zone target) {:keep-server-alive true}) + (move state :corp target (:zone to-swap) {:keep-server-alive true}) + (system-msg state :corp + (str "uses Daruma to swap " (card-str state to-swap) + " with " (card-str state target))) + (clear-wait-prompt state :runner))) + :cancel-effect (effect (clear-wait-prompt :runner))}) + (ability [card] + {:optional {:prompt "Trash Daruma to swap a card in this server?" + :yes-ability {:async true + :prompt "Select a card in this server to swap" + :choices {:req #(and (installed? %) + (in-same-server? card %)) + :not-self true} + :effect (effect (continue-ability (choose-swap target) card nil))} + :no-ability {:effect (effect (clear-wait-prompt :runner))}}})] + {:events {:approach-server {:async true + :effect (effect (show-wait-prompt :runner "Corp to use Daruma") + (continue-ability :corp (ability card) card nil))}}}) + + "Dedicated Technician Team" + {:recurring 2 + :interactions {:pay-credits {:req (req (and (= :corp-install (:source-type eid)) + (= (second (:zone card)) + (second (server->zone state (:source eid)))))) + :type :recurring}}} + + "Defense Construct" + {:advanceable :always + :abilities [{:label "[Trash]: Add 1 facedown card from Archives to HQ for each advancement token" + :req (req (and run + (= (:server run) [:archives]) + (pos? (get-counters card :advancement)))) + :effect (effect (resolve-ability + {:show-discard true + :choices {:max (get-counters card :advancement) + :req #(and (corp? %) + (not (:seen %)) + (in-discard? %))} + :msg (msg "add " (count targets) " facedown cards in Archives to HQ") + :effect (req (doseq [c targets] + (move state side c :hand)))} + card nil) + (trash card))}]} + + "Disposable HQ" + (letfn [(dhq [n i] + {:req (req (pos? i)) + :prompt "Select a card in HQ to add to the bottom of R&D" + :choices {:req #(and (corp? %) + (in-hand? %))} + :async true + :msg "add a card to the bottom of R&D" + :effect (req (move state side target :deck) + (if (< n i) + (continue-ability state side (dhq (inc n) i) card nil) + (do + (clear-wait-prompt state :runner) + (effect-completed state side eid)))) + :cancel-effect (effect (clear-wait-prompt :runner))})] + {:flags {:rd-reveal (req true)} + :access {:async true + :effect (req (let [n (count (:hand corp))] + (show-wait-prompt state :runner "Corp to finish using Disposable HQ") + (continue-ability + state side + {:optional + {:prompt "Use Disposable HQ to add cards to the bottom of R&D?" + :yes-ability {:async true + :msg "add cards in HQ to the bottom of R&D" + :effect (effect (continue-ability (dhq 1 n) card nil))} + :no-ability {:effect (effect (clear-wait-prompt :runner))}}} + card nil)))}}) + + "Drone Screen" + {:events {:run {:req (req (and this-server tagged)) + :async true + :trace {:base 3 + :successful + {:msg "do 1 meat damage" + :effect (effect (damage eid :meat 1 {:card card + :unpreventable true}))}}}}} + + "Embolus" + (let [maybe-gain-counter {:once :per-turn + :label "Place a power counter on Embolus" + :effect (effect + (continue-ability + {:optional + {:prompt "Pay 1 [Credit] to place a power counter on Embolus?" + :yes-ability {:effect (effect (add-counter card :power 1)) + :cost [:credit 1] + :msg "pay 1 [Credit] to place a power counter on Embolus"}}} + card nil))} + etr {:req (req this-server) + :counter-cost [:power 1] + :msg "end the run" + :effect (effect (end-run eid card))}] + {:derezzed-events {:runner-turn-ends corp-rez-toast} + :events {:corp-turn-begins maybe-gain-counter + :successful-run {:req (req (pos? (get-counters card :power))) + :msg "remove 1 power counter from Embolus" + :effect (effect (add-counter card :power -1))}} + :abilities [maybe-gain-counter etr]}) + + "Experiential Data" + {:effect (req (update-ice-in-server state side (card->server state card))) + :events {:pre-ice-strength {:req (req (protecting-same-server? card target)) + :effect (effect (ice-strength-bonus 1 target))}} + :derez-effect {:effect (req (update-ice-in-server state side (card->server state card)))} + :trash-effect {:effect (req (update-all-ice state side))}} + + "Expo Grid" + (let [ability {:req (req (some #(and (asset? %) + (rezzed? %)) + (get-in corp (:zone card)))) + :msg "gain 1 [Credits]" + :once :per-turn + :label "Gain 1 [Credits] (start of turn)" + :effect (effect (gain-credits 1))}] + {:derezzed-events {:runner-turn-ends corp-rez-toast} + :events {:corp-turn-begins ability} + :abilities [ability]}) + + "Forced Connection" + {:flags {:rd-reveal (req true)} + :access {:req (req (not= (first (:zone card)) :discard)) + :interactive (req true) + :trace {:base 3 + :successful {:msg "give the Runner 2 tags" + :async true + :effect (effect (gain-tags :corp eid 2))}}}} + + "Fractal Threat Matrix" + {:implementation "Manual trigger each time all subs are broken" + :abilities [{:label "Trash the top 2 cards from the Stack" + :msg (msg (let [deck (:deck runner)] + (if (pos? (count deck)) + (str "trash " (join ", " (map :title (take 2 deck))) " from the Stack") + "trash the top 2 cards from their Stack - but the Stack is empty"))) + :effect (effect (mill :corp :runner 2))}]} + + "Georgia Emelyov" + {:events {:unsuccessful-run {:req (req (= (first (:server target)) + (second (:zone card)))) + :async true + :msg "do 1 net damage" + :effect (effect (damage eid :net 1 {:card card}))}} + :abilities [{:cost [:credit 2] + :label "Move to another server" + :async true + :effect (effect (continue-ability + {:prompt "Choose a server" + :choices (server-list state) + :msg (msg "move to " target) + :effect (req (let [c (move state side card + (conj (server->zone state target) :content))] + (unregister-events state side card) + (register-events state side (:events (card-def c)) c)))} + card nil))}]} + + "Giordano Memorial Field" + {:events + {:successful-run + {:interactive (req true) + :async true + :req (req this-server) + :msg "force the Runner to pay or end the run" + :effect (req (let [credits (:credit runner) + cost (* 2 (count (:scored runner))) + pay-str (str "pay " cost " [Credits]") + c-pay-str (capitalize pay-str)] + (show-wait-prompt state :corp (str "Runner to " pay-str " or end the run")) + (continue-ability + state :runner + {:player :runner + :async true + :prompt (msg "You must " pay-str " or end the run") + :choices (concat (when (>= credits cost) + [c-pay-str]) + ["End the run"]) + :effect (req (clear-wait-prompt state :corp) + (if (= c-pay-str target) + (do (pay state :runner card :credit cost) + (system-msg state :runner (str "pays " cost " [Credits]"))) + (do (end-run state side eid card) + (system-msg state :corp "ends the run"))) + (effect-completed state side eid))} + card nil)))}}} + + "Heinlein Grid" + {:abilities [{:req (req this-server) + :label "Force the Runner to lose all [Credits] from spending or losing a [Click]" + :msg (msg "force the Runner to lose all " (:credit runner) " [Credits]") + :once :per-run + :effect (effect (lose-credits :runner :all) + (lose :runner :run-credit :all))}]} + + "Helheim Servers" + {:abilities [{:label "Trash 1 card from HQ: All ice protecting this server has +2 strength until the end of the run" + :req (req (and this-server + (pos? (count run-ices)) + (pos? (count (:hand corp))))) + :async true + :effect (req (show-wait-prompt state :runner "Corp to use Helheim Servers") + (wait-for + (resolve-ability + state side + {:prompt "Choose a card in HQ to trash" + :choices {:req #(and (in-hand? %) + (corp? %))} + :msg "trash a card from HQ and give all ice protecting this server +2 strength until the end of the run" + :effect (effect (clear-wait-prompt :runner) + (trash eid target nil))} card nil) - (trash card))}]}) - -(define-card "Disposable HQ" - (letfn [(dhq [n i] - {:req (req (pos? i)) - :prompt "Select a card in HQ to add to the bottom of R&D" - :choices {:req #(and (corp? %) - (in-hand? %))} - :async true - :msg "add a card to the bottom of R&D" - :effect (req (move state side target :deck) - (if (< n i) - (continue-ability state side (dhq (inc n) i) card nil) - (do - (clear-wait-prompt state :runner) - (effect-completed state side eid)))) - :cancel-effect (effect (clear-wait-prompt :runner))})] - {:flags {:rd-reveal (req true)} - :access {:async true - :effect (req (let [n (count (:hand corp))] - (show-wait-prompt state :runner "Corp to finish using Disposable HQ") + (register-events + state side + {:pre-ice-strength {:req (req (= (card->server state card) + (card->server state target))) + :effect (effect (ice-strength-bonus 2 target))} + :run-ends {:effect (effect (unregister-events card))}} + card) + (continue-ability + state side + {:effect (req (update-ice-in-server + state side (card->server state card)))} + card nil)))}] + :events {:pre-ice-strength nil}} + + "Henry Phillips" + {:implementation "Manually triggered by Corp" + :abilities [{:req (req (and this-server tagged)) + :msg "gain 2 [Credits]" + :effect (effect (gain-credits 2))}]} + + "Hired Help" + (let [prompt-to-trash-agenda-or-etr + {:prompt "Choose one" + :player :runner + :choices ["Trash 1 scored agenda" "End the run"] + :effect (req (if (= target "End the run") + (do (system-msg state :runner (str "declines to pay the additional cost from Hired Help")) + (end-run state side eid card)) + (if (seq (:scored runner)) + (continue-ability state :runner + {:prompt "Choose an Agenda to trash" + :async true + :choices {:max 1 + :req #(is-scored? state side %)} + :effect (req (wait-for (trash state side target {:unpreventable true}) + (system-msg state :runner (str "trashes " (:title target) + " as an additional cost to initiate a run")) + (effect-completed state side eid)))} + card nil) + (do (system-msg state :runner (str "wants to pay the additional cost from Hired Help but has no scored agenda to trash")) + (end-run state side eid card)))))}] + {:events {:run {:req (req (and this-server + (empty? (filter #(= :hq %) (:successful-run runner-reg))))) + :effect (req (continue-ability state :runner prompt-to-trash-agenda-or-etr card nil))}}}) + + "Hokusai Grid" + {:events {:successful-run {:req (req this-server) + :msg "do 1 net damage" + :async true + :effect (effect (damage eid :net 1 {:card card}))}}} + + "Increased Drop Rates" + {:flags {:rd-reveal (req true)} + :access {:interactive (req true) + :effect (effect (show-wait-prompt :corp "Runner to decide if they will take 1 tag") (continue-ability - state side {:optional - {:prompt "Use Disposable HQ to add cards to the bottom of R&D?" + {:player :runner + :prompt "Take 1 tag to prevent Corp from removing 1 bad publicity?" :yes-ability {:async true - :msg "add cards in HQ to the bottom of R&D" - :effect (effect (continue-ability (dhq 1 n) card nil))} - :no-ability {:effect (effect (clear-wait-prompt :runner))}}} - card nil)))}})) - -(define-card "Drone Screen" - {:events {:run {:req (req (and this-server tagged)) - :async true - :trace {:base 3 - :successful - {:msg "do 1 meat damage" - :effect (effect (damage eid :meat 1 {:card card - :unpreventable true}))}}}}}) - -(define-card "Embolus" - (let [maybe-gain-counter {:once :per-turn - :label "Place a power counter on Embolus" - :effect (effect + :effect (req (gain-tags state :runner eid 1 {:unpreventable true}) + (system-msg + state :runner + "takes 1 tag to prevent Corp from removing 1 bad publicity"))} + :no-ability {:msg "remove 1 bad publicity" + :effect (effect (lose-bad-publicity :corp 1))} + :end-effect (effect (clear-wait-prompt :corp) + (effect-completed eid))}} + card nil))}} + + "Intake" + {:flags {:rd-reveal (req true)} + :access {:req (req (not= (first (:zone card)) :discard)) + :interactive (req true) + :trace {:base 4 + :label "add an installed program or virtual resource to the Grip" + :successful + {:async true + :effect (effect (show-wait-prompt :runner "Corp to resolve Intake") (continue-ability - {:optional - {:prompt "Pay 1 [Credit] to place a power counter on Embolus?" - :yes-ability {:effect (effect (add-counter card :power 1)) - :cost [:credit 1] - :msg "pay 1 [Credit] to place a power counter on Embolus"}}} - card nil))} - etr {:req (req this-server) - :counter-cost [:power 1] - :msg "end the run" - :effect (effect (end-run eid card))}] - {:derezzed-events {:runner-turn-ends corp-rez-toast} - :events {:corp-turn-begins maybe-gain-counter - :successful-run {:req (req (pos? (get-counters card :power))) - :msg "remove 1 power counter from Embolus" - :effect (effect (add-counter card :power -1))}} - :abilities [maybe-gain-counter etr]})) - -(define-card "Experiential Data" - {:effect (req (update-ice-in-server state side (card->server state card))) - :events {:pre-ice-strength {:req (req (protecting-same-server? card target)) - :effect (effect (ice-strength-bonus 1 target))}} - :derez-effect {:effect (req (update-ice-in-server state side (card->server state card)))} - :trash-effect {:effect (req (update-all-ice state side))}}) - -(define-card "Expo Grid" - (let [ability {:req (req (some #(and (asset? %) - (rezzed? %)) - (get-in corp (:zone card)))) - :msg "gain 1 [Credits]" - :once :per-turn - :label "Gain 1 [Credits] (start of turn)" - :effect (effect (gain-credits 1))}] - {:derezzed-events {:runner-turn-ends corp-rez-toast} - :events {:corp-turn-begins ability} - :abilities [ability]})) - -(define-card "Forced Connection" - {:flags {:rd-reveal (req true)} - :access {:req (req (not= (first (:zone card)) :discard)) - :interactive (req true) - :trace {:base 3 - :successful {:msg "give the Runner 2 tags" - :async true - :effect (effect (gain-tags :corp eid 2))}}}}) - -(define-card "Fractal Threat Matrix" - {:implementation "Manual trigger each time all subs are broken" - :abilities [{:label "Trash the top 2 cards from the Stack" - :msg (msg (let [deck (:deck runner)] - (if (pos? (count deck)) - (str "trash " (join ", " (map :title (take 2 deck))) " from the Stack") - "trash the top 2 cards from their Stack - but the Stack is empty"))) - :effect (effect (mill :corp :runner 2))}]}) - -(define-card "Georgia Emelyov" - {:events {:unsuccessful-run {:req (req (= (first (:server target)) - (second (:zone card)))) - :async true - :msg "do 1 net damage" - :effect (effect (damage eid :net 1 {:card card}))}} - :abilities [{:cost [:credit 2] - :label "Move to another server" - :async true - :effect (effect (continue-ability - {:prompt "Choose a server" - :choices (server-list state) - :msg (msg "move to " target) - :effect (req (let [c (move state side card - (conj (server->zone state target) :content))] - (unregister-events state side card) - (register-events state side (:events (card-def c)) c)))} - card nil))}]}) - -(define-card "Giordano Memorial Field" - {:events - {:successful-run - {:interactive (req true) - :async true - :req (req this-server) - :msg "force the Runner to pay or end the run" - :effect (req (let [credits (:credit runner) - cost (* 2 (count (:scored runner))) - pay-str (str "pay " cost " [Credits]") - c-pay-str (capitalize pay-str)] - (show-wait-prompt state :corp (str "Runner to " pay-str " or end the run")) - (continue-ability - state :runner - {:player :runner - :async true - :prompt (msg "You must " pay-str " or end the run") - :choices (concat (when (>= credits cost) - [c-pay-str]) - ["End the run"]) - :effect (req (clear-wait-prompt state :corp) - (if (= c-pay-str target) - (do (pay state :runner card :credit cost) - (system-msg state :runner (str "pays " cost " [Credits]"))) - (do (end-run state side eid card) - (system-msg state :corp "ends the run"))) - (effect-completed state side eid))} - card nil)))}}}) - -(define-card "Heinlein Grid" - {:abilities [{:req (req this-server) - :label "Force the Runner to lose all [Credits] from spending or losing a [Click]" - :msg (msg "force the Runner to lose all " (:credit runner) " [Credits]") - :once :per-run - :effect (effect (lose-credits :runner :all) - (lose :runner :run-credit :all))}]}) - -(define-card "Helheim Servers" - {:abilities [{:label "Trash 1 card from HQ: All ice protecting this server has +2 strength until the end of the run" - :req (req (and this-server - (pos? (count run-ices)) - (pos? (count (:hand corp))))) - :async true - :effect (req (show-wait-prompt state :runner "Corp to use Helheim Servers") - (wait-for - (resolve-ability - state side - {:prompt "Choose a card in HQ to trash" - :choices {:req #(and (in-hand? %) - (corp? %))} - :msg "trash a card from HQ and give all ice protecting this server +2 strength until the end of the run" - :effect (effect (clear-wait-prompt :runner) - (trash eid target nil))} - card nil) - (register-events - state side - {:pre-ice-strength {:req (req (= (card->server state card) - (card->server state target))) - :effect (effect (ice-strength-bonus 2 target))} - :run-ends {:effect (effect (unregister-events card))}} - card) - (continue-ability - state side - {:effect (req (update-ice-in-server - state side (card->server state card)))} - card nil)))}] - :events {:pre-ice-strength nil}}) - -(define-card "Henry Phillips" - {:implementation "Manually triggered by Corp" - :abilities [{:req (req (and this-server tagged)) - :msg "gain 2 [Credits]" - :effect (effect (gain-credits 2))}]}) - -(define-card "Hired Help" - (let [prompt-to-trash-agenda-or-etr - {:prompt "Choose one" - :player :runner - :choices ["Trash 1 scored agenda" "End the run"] - :effect (req (if (= target "End the run") - (do (system-msg state :runner (str "declines to pay the additional cost from Hired Help")) - (end-run state side eid card)) - (if (seq (:scored runner)) - (continue-ability state :runner - {:prompt "Choose an Agenda to trash" - :async true - :choices {:max 1 - :req #(is-scored? state side %)} - :effect (req (wait-for (trash state side target {:unpreventable true}) - (system-msg state :runner (str "trashes " (:title target) - " as an additional cost to initiate a run")) - (effect-completed state side eid)))} + {:prompt "Select a program or virtual resource" + :player :corp + :choices {:req #(and (installed? %) + (or (program? %) + (and (resource? %) + (has-subtype? % "Virtual"))))} + :msg (msg "move " (:title target) " to the Grip") + :effect (effect (move :runner target :hand)) + :end-effect (req (clear-wait-prompt state :runner) + (effect-completed state side eid))} + card nil))}}}} + + "Jinja City Grid" + (letfn [(install-ice [ice ices grids server] + (let [remaining (remove-once #(same-card? % ice) ices)] + {:async true + :effect (req (if (= "None" server) + (continue-ability state side (choose-ice remaining grids) card nil) + (do (reveal state side ice) + (system-msg state side (str "reveals that they drew " (:title ice))) + (wait-for (corp-install state side ice server {:extra-cost [:credit -4]}) + (if (= 1 (count ices)) + (effect-completed state side eid) + (continue-ability state side (choose-ice remaining grids) + card nil))))))})) + (choose-grid [ice ices grids] + (if (= 1 (count grids)) + (install-ice ice ices grids (-> (first grids) :zone second zone->name)) + {:async true + :prompt (str "Choose a server to install " (:title ice)) + :choices (conj (mapv #(-> % :zone second zone->name) grids) "None") + :effect (effect (continue-ability (install-ice ice ices grids target) card nil))})) + (choose-ice [ices grids] + (when (seq ices) + {:async true + :prompt "Choose an ice to reveal and install, or None to decline" + :choices (conj (mapv :title ices) "None") + :effect (req (if (= "None" target) + (effect-completed state side eid) + (continue-ability state side + (choose-grid (some #(when (= target (:title %)) %) ices) + ices grids) + card nil)))}))] + {:events {:corp-draw {;; THIS IS A HACK: it prevents multiple Jinja from showing the "choose a server to install into" sequence + :once :per-turn + :once-key :jinja-city-grid-draw + :async true + :effect (req (cond + ;; If ice were drawn, do the full routine. + (some ice? (:most-recent-drawn corp-reg)) + (let [ices (filter #(and (ice? %) + (get-card state %)) + (:most-recent-drawn corp-reg)) + grids (filterv #(= "Jinja City Grid" (:title %)) + (all-active-installed state :corp))] + (when (= :runner (:active-player @state)) + (show-wait-prompt state :runner "Corp to resolve Jinja City Grid")) + (if (not-empty ices) + (continue-ability state side (choose-ice ices grids) card nil) + (effect-completed state side eid))) + ;; else, if it's the runner's turn, show a fake prompt so the runner can't infer that ice weren't drawn + (= :runner (:active-player @state)) + (continue-ability + state :corp + {:prompt "You did not draw any ice to use with Jinja City Grid" + :choices ["Carry on!"] + :prompt-type :bogus + :effect nil} card nil) - (do (system-msg state :runner (str "wants to pay the additional cost from Hired Help but has no scored agenda to trash")) - (end-run state side eid card)))))}] - {:events {:run {:req (req (and this-server - (empty? (filter #(= :hq %) (:successful-run runner-reg))))) - :effect (req (continue-ability state :runner prompt-to-trash-agenda-or-etr card nil))}}})) - -(define-card "Hokusai Grid" - {:events {:successful-run {:req (req this-server) - :msg "do 1 net damage" - :async true - :effect (effect (damage eid :net 1 {:card card}))}}}) - -(define-card "Increased Drop Rates" - {:flags {:rd-reveal (req true)} - :access {:interactive (req true) - :effect (effect (show-wait-prompt :corp "Runner to decide if they will take 1 tag") - (continue-ability - {:optional - {:player :runner - :prompt "Take 1 tag to prevent Corp from removing 1 bad publicity?" - :yes-ability {:async true - :effect (req (gain-tags state :runner eid 1 {:unpreventable true}) - (system-msg - state :runner - "takes 1 tag to prevent Corp from removing 1 bad publicity"))} - :no-ability {:msg "remove 1 bad publicity" - :effect (effect (lose-bad-publicity :corp 1))} - :end-effect (effect (clear-wait-prompt :corp) - (effect-completed eid))}} - card nil))}}) - -(define-card "Intake" - {:flags {:rd-reveal (req true)} - :access {:req (req (not= (first (:zone card)) :discard)) - :interactive (req true) - :trace {:base 4 - :label "add an installed program or virtual resource to the Grip" - :successful - {:async true - :effect (effect (show-wait-prompt :runner "Corp to resolve Intake") - (continue-ability - {:prompt "Select a program or virtual resource" - :player :corp - :choices {:req #(and (installed? %) - (or (program? %) - (and (resource? %) - (has-subtype? % "Virtual"))))} - :msg (msg "move " (:title target) " to the Grip") - :effect (effect (move :runner target :hand)) - :end-effect (req (clear-wait-prompt state :runner) - (effect-completed state side eid))} - card nil))}}}}) - -(define-card "Jinja City Grid" - (letfn [(install-ice [ice ices grids server] - (let [remaining (remove-once #(same-card? % ice) ices)] - {:async true - :effect (req (if (= "None" server) - (continue-ability state side (choose-ice remaining grids) card nil) - (do (reveal state side ice) - (system-msg state side (str "reveals that they drew " (:title ice))) - (wait-for (corp-install state side ice server {:extra-cost [:credit -4]}) - (if (= 1 (count ices)) - (effect-completed state side eid) - (continue-ability state side (choose-ice remaining grids) - card nil))))))})) - (choose-grid [ice ices grids] - (if (= 1 (count grids)) - (install-ice ice ices grids (-> (first grids) :zone second zone->name)) - {:async true - :prompt (str "Choose a server to install " (:title ice)) - :choices (conj (mapv #(-> % :zone second zone->name) grids) "None") - :effect (effect (continue-ability (install-ice ice ices grids target) card nil))})) - (choose-ice [ices grids] - (when (seq ices) - {:async true - :prompt "Choose an ice to reveal and install, or None to decline" - :choices (conj (mapv :title ices) "None") - :effect (req (if (= "None" target) - (effect-completed state side eid) - (continue-ability state side - (choose-grid (some #(when (= target (:title %)) %) ices) - ices grids) - card nil)))}))] - {:events {:corp-draw {;; THIS IS A HACK: it prevents multiple Jinja from showing the "choose a server to install into" sequence - :once :per-turn - :once-key :jinja-city-grid-draw + ;; otherwise, we done + :else + (effect-completed state side eid)))} + :post-corp-draw {:effect (req (swap! state dissoc-in [:per-turn :jinja-city-grid-draw]) + (when (= :runner (:active-player @state)) + (clear-wait-prompt state :runner)))}}}) + + "K. P. Lynn" + (let [abi {:prompt "Choose one" + :player :runner + :choices ["Take 1 tag" "End the run"] + :effect (req (if (= target "Take 1 tag") + (do (gain-tags state :runner 1) + (system-msg state :corp (str "uses K. P. Lynn. Runner chooses to take 1 tag"))) + (do (end-run state side eid card) + (system-msg state :corp (str "uses K. P. Lynn. Runner chooses to end the run")))))}] + {:events {:pass-ice {:req (req (and this-server (= (:position run) 1))) ; trigger when last ice passed :async true - :effect (req (cond - ;; If ice were drawn, do the full routine. - (some ice? (:most-recent-drawn corp-reg)) - (let [ices (filter #(and (ice? %) - (get-card state %)) - (:most-recent-drawn corp-reg)) - grids (filterv #(= "Jinja City Grid" (:title %)) - (all-active-installed state :corp))] - (when (= :runner (:active-player @state)) - (show-wait-prompt state :runner "Corp to resolve Jinja City Grid")) - (if (not-empty ices) - (continue-ability state side (choose-ice ices grids) card nil) - (effect-completed state side eid))) - ;; else, if it's the runner's turn, show a fake prompt so the runner can't infer that ice weren't drawn - (= :runner (:active-player @state)) - (continue-ability - state :corp - {:prompt "You did not draw any ice to use with Jinja City Grid" - :choices ["Carry on!"] - :prompt-type :bogus - :effect nil} - card nil) - ;; otherwise, we done - :else - (effect-completed state side eid)))} - :post-corp-draw {:effect (req (swap! state dissoc-in [:per-turn :jinja-city-grid-draw]) - (when (= :runner (:active-player @state)) - (clear-wait-prompt state :runner)))}}})) - -(define-card "K. P. Lynn" - (let [abi {:prompt "Choose one" - :player :runner - :choices ["Take 1 tag" "End the run"] - :effect (req (if (= target "Take 1 tag") - (do (gain-tags state :runner 1) - (system-msg state :corp (str "uses K. P. Lynn. Runner chooses to take 1 tag"))) - (do (end-run state side eid card) - (system-msg state :corp (str "uses K. P. Lynn. Runner chooses to end the run")))))}] - {:events {:pass-ice {:req (req (and this-server (= (:position run) 1))) ; trigger when last ice passed - :async true - :effect (req (continue-ability state :runner abi card nil))} - :run {:req (req (and this-server - (zero? (:position run)))) ; trigger on unprotected server - :async true - :effect (req (continue-ability state :runner abi card nil))}}})) - -(define-card "Letheia Nisei" - (let [ability {:label "Force runner to re-approach outer ice" - :once :per-turn + :effect (req (continue-ability state :runner abi card nil))} + :run {:req (req (and this-server + (zero? (:position run)))) ; trigger on unprotected server + :async true + :effect (req (continue-ability state :runner abi card nil))}}}) + + "Letheia Nisei" + (let [ability {:label "Force runner to re-approach outer ice" + :once :per-turn + :req (req (and this-server + (zero? (:position run)))) + :psi {:not-equal + {:effect + (effect + (show-wait-prompt :runner "Corp to use Letheia Nisei") + (continue-ability + :corp + {:optional + {:prompt "Trash to force re-approach outer ice?" + :autoresolve (get-autoresolve :auto-fire) + :yes-ability + {:msg "force the Runner to approach outermost piece of ice" + :effect (req (swap! state assoc-in [:run :position] (count run-ices)) + (trash state side eid card {:cause :ability-cost}))} + :end-effect (effect (clear-wait-prompt :runner))}} + card nil))}}}] + {:events {:approach-server ability} + :abilities [ability + (set-autoresolve :auto-fire "Fire Letheia Nisei?")]}) + + "Keegan Lane" + {:abilities [{:label "[Trash], remove a tag: Trash a program" :req (req (and this-server - (zero? (:position run)))) - :psi {:not-equal - {:effect - (effect - (show-wait-prompt :runner "Corp to use Letheia Nisei") - (continue-ability - :corp - {:optional - {:prompt "Trash to force re-approach outer ice?" - :autoresolve (get-autoresolve :auto-fire) - :yes-ability - {:msg "force the Runner to approach outermost piece of ice" - :effect (req (swap! state assoc-in [:run :position] (count run-ices)) - (trash state side eid card {:cause :ability-cost}))} - :end-effect (effect (clear-wait-prompt :runner))}} - card nil))}}}] - {:events {:approach-server ability} - :abilities [ability - (set-autoresolve :auto-fire "Fire Letheia Nisei?")]})) - -(define-card "Keegan Lane" - {:abilities [{:label "[Trash], remove a tag: Trash a program" - :req (req (and this-server - (pos? (get-in @state [:runner :tag :base])) - (not (empty? (filter program? - (all-active-installed state :runner)))))) - :msg (msg "remove 1 tag") - :effect (req (resolve-ability state side trash-program card nil) - (trash state side card {:cause :ability-cost}) - (lose-tags state :corp 1))}]}) - -(define-card "Khondi Plaza" - {:recurring (effect (set-prop card :rec-counter (count (get-remotes state)))) - :effect (effect (set-prop card :rec-counter (count (get-remotes state)))) - :interactions {:pay-credits {:req (req (and (= :rez (:source-type eid)) - (ice? target) - (= (card->server state card) (card->server state target)))) - :type :recurring}}}) - -(define-card "Manta Grid" - {:events {:successful-run-ends - {:msg "gain a [Click] next turn" - :req (req (and (= (first (:server target)) (second (:zone card))) - (or (< (:credit runner) 6) (zero? (:click runner))))) - :effect (req (swap! state update-in [:corp :extra-click-temp] (fnil inc 0)))}}}) - -(define-card "Marcus Batty" - {:abilities [{:req (req this-server) - :label "[Trash]: Start a Psi game" - :psi {:not-equal {:prompt "Select a rezzed piece of ICE to resolve one of its subroutines" - :choices {:req #(and (ice? %) - (rezzed? %))} - :msg (msg "resolve a subroutine on " (:title target))}} - :effect (effect (trash card {:cause :ability-cost}))}]}) - -(define-card "Mason Bellamy" - {:implementation "Manually triggered by Corp" - :abilities [{:label "Force the Runner to lose [Click] after an encounter where they broke a subroutine" - :req (req this-server) - :msg "force the Runner to lose [Click]" - :effect (effect (lose :runner :click 1))}]}) - -(define-card "Midori" - {:abilities - [{:req (req this-server) - :label "Swap the ICE being approached with a piece of ICE from HQ" - :prompt "Select a piece of ICE" - :choices {:req #(and (ice? %) - (in-hand? %))} - :once :per-run - :msg (msg "swap " (card-str state current-ice) " with a piece of ICE from HQ") - :effect (req (let [hqice target - c current-ice] - (resolve-ability + (pos? (get-in @state [:runner :tag :base])) + (not (empty? (filter program? + (all-active-installed state :runner)))))) + :msg (msg "remove 1 tag") + :effect (req (resolve-ability state side trash-program card nil) + (trash state side card {:cause :ability-cost}) + (lose-tags state :corp 1))}]} + + "Khondi Plaza" + {:recurring (effect (set-prop card :rec-counter (count (get-remotes state)))) + :effect (effect (set-prop card :rec-counter (count (get-remotes state)))) + :interactions {:pay-credits {:req (req (and (= :rez (:source-type eid)) + (ice? target) + (= (card->server state card) (card->server state target)))) + :type :recurring}}} + + "Manta Grid" + {:events {:successful-run-ends + {:msg "gain a [Click] next turn" + :req (req (and (= (first (:server target)) (second (:zone card))) + (or (< (:credit runner) 6) (zero? (:click runner))))) + :effect (req (swap! state update-in [:corp :extra-click-temp] (fnil inc 0)))}}} + + "Marcus Batty" + {:abilities [{:req (req this-server) + :label "[Trash]: Start a Psi game" + :psi {:not-equal {:prompt "Select a rezzed piece of ICE to resolve one of its subroutines" + :choices {:req #(and (ice? %) + (rezzed? %))} + :msg (msg "resolve a subroutine on " (:title target))}} + :effect (effect (trash card {:cause :ability-cost}))}]} + + "Mason Bellamy" + {:implementation "Manually triggered by Corp" + :abilities [{:label "Force the Runner to lose [Click] after an encounter where they broke a subroutine" + :req (req this-server) + :msg "force the Runner to lose [Click]" + :effect (effect (lose :runner :click 1))}]} + + "Midori" + {:abilities + [{:req (req this-server) + :label "Swap the ICE being approached with a piece of ICE from HQ" + :prompt "Select a piece of ICE" + :choices {:req #(and (ice? %) + (in-hand? %))} + :once :per-run + :msg (msg "swap " (card-str state current-ice) " with a piece of ICE from HQ") + :effect (req (let [hqice target + c current-ice] + (resolve-ability + state side + {:effect (req (let [newice (assoc hqice :zone (:zone c)) + cndx (ice-index state c) + ices (get-in @state (cons :corp (:zone c))) + newices (apply conj (subvec ices 0 cndx) newice (subvec ices cndx))] + (swap! state assoc-in (cons :corp (:zone c)) newices) + (swap! state update-in [:corp :hand] + (fn [coll] (remove-once #(same-card? % hqice) coll))) + (trigger-event state side :corp-install newice) + (move state side c :hand)))} card nil)))}]} + + "Mumbad City Grid" + {:abilities [{:req (req (let [num-ice (count run-ices)] + (and this-server + (>= num-ice 2) + (< (:position run 0) num-ice)))) + :label "Swap the ICE just passed with another piece of ICE protecting this server" + :effect (req (let [passed-ice (nth (get-in @state (vec (concat [:corp :servers] (:server run) [:ices]))) + (:position run)) + ice-zone (:zone passed-ice)] + (resolve-ability + state :corp + {:prompt (msg "Select a piece of ICE to swap with " (:title passed-ice)) + :choices {:req #(and (= ice-zone (:zone %)) (ice? %))} + :effect (req (let [fndx (ice-index state passed-ice) + sndx (ice-index state target) + fnew (assoc passed-ice :zone (:zone target)) + snew (assoc target :zone (:zone passed-ice))] + (swap! state update-in (cons :corp ice-zone) + #(assoc % fndx snew)) + (swap! state update-in (cons :corp ice-zone) + #(assoc % sndx fnew)) + (update-ice-strength state side fnew) + (update-ice-strength state side snew) + (system-msg state side (str "uses Mumbad City Grid to swap " + (card-str state passed-ice) + " with " (card-str state target)))))} + card nil)))}]} + + "Mumbad Virtual Tour" + {:flags {:must-trash (req (when installed true))}} + + "Mwanza City Grid" + (let [gain-creds-and-clear {:req (req (= (:from-server target) + (second (:zone card)))) + :silent (req true) + :effect (req (let [cnt (total-cards-accessed run) + total (* 2 cnt)] + (when cnt + (gain-credits state :corp total) + (system-msg state :corp + (str "gains " total " [Credits] from Mwanza City Grid")))))} + boost-access-by-3 {:req (req (= target (second (:zone card)))) + :msg "force the Runner to access 3 additional cards" + :effect (req (access-bonus state :runner (-> card :zone second) 3))}] + {:install-req (req (filter #{"HQ" "R&D"} targets)) + :events {:pre-access boost-access-by-3 + :end-access-phase gain-creds-and-clear} + ;; TODO: as written, this may fail if mwanza is trashed outside of a run on its server + ;; (e.g. mwanza on R&D, run HQ, use polop to trash mwanza mid-run, shiro fires to cause RD + :trash-effect ; if there is a run, mark mwanza effects to remain active until the end of the run + {:req (req (:run @state)) + :effect (effect (register-events {:pre-access (assoc boost-access-by-3 :req (req (= target (second (:previous-zone card))))) + :end-access-phase (assoc gain-creds-and-clear :req (req (= (:from-server target) (second (:previous-zone card))))) + :unsuccessful-run-ends {:effect (effect (unregister-events card))} + :successful-run-ends {:effect (effect (unregister-events card))}} + (assoc card :zone '(:discard))))}}) + + "NeoTokyo Grid" + (let [ng {:req (req (in-same-server? card target)) + :once :per-turn + :msg "gain 1 [Credits]" + :effect (effect (gain-credits 1))}] + {:events {:advance ng + :advancement-placed ng}}) + + "Nihongai Grid" + {:events + {:successful-run + {:interactive (req true) + :async true + :req (req (and this-server + (or (< (:credit runner) 6) + (< (count (:hand runner)) 2)) + (not-empty (:hand corp)))) + :effect (req (show-wait-prompt state :runner "Corp to use Nihongai Grid") + (let [top5 (take 5 (:deck corp))] + (if (pos? (count top5)) + (continue-ability + state side + {:optional + {:prompt "Use Nihongai Grid to look at top 5 cards of R&D and swap one with a card from HQ?" + :yes-ability + {:async true + :prompt "Choose a card to swap with a card from HQ" + :choices top5 + :effect (req (let [rdc target] + (continue-ability + state side + {:async true + :prompt (msg "Choose a card in HQ to swap for " (:title rdc)) + :choices {:req in-hand?} + :msg "swap a card from the top 5 of R&D with a card in HQ" + :effect (req (let [hqc target + newrdc (assoc hqc :zone [:deck]) + deck (vec (get-in @state [:corp :deck])) + rdcndx (first (keep-indexed #(when (same-card? %2 rdc) %1) deck)) + newdeck (seq (apply conj (subvec deck 0 rdcndx) target (subvec deck rdcndx)))] + (swap! state assoc-in [:corp :deck] newdeck) + (swap! state update-in [:corp :hand] + (fn [coll] (remove-once #(same-card? % hqc) coll))) + (move state side rdc :hand) + (clear-wait-prompt state :runner) + (effect-completed state side eid)))} + card nil)))} + :no-ability {:effect (req (clear-wait-prompt state :runner) + (effect-completed state side eid))}}} + card nil) + (do (clear-wait-prompt state :runner) + (effect-completed state side eid)))))}}} + + "Oaktown Grid" + {:events {:pre-trash {:req (req (in-same-server? card target)) + :effect (effect (trash-cost-bonus 3))}}} + + "Oberth Protocol" + {:additional-cost [:forfeit] + :events {:advance {:req (req (and (same-server? card target) + (= 1 (count (filter #(= (second (:zone %)) (second (:zone card))) + (map first (turn-events state side :advance))))))) + :msg (msg "place an additional advancement token on " (card-str state target)) + :effect (effect (add-prop :corp target :advance-counter 1 {:placed true}))}}} + + "Off the Grid" + {:install-req (req (remove #{"HQ" "R&D" "Archives"} targets)) + :effect (req (prevent-run-on-server state card (second (:zone card)))) + :events {:runner-turn-begins {:effect (req (prevent-run-on-server state card (second (:zone card))))} + :successful-run {:req (req (= target :hq)) + :effect (req (trash state :corp card) + (enable-run-on-server state card + (second (:zone card))) + (system-msg state :corp (str "trashes Off the Grid")))}} + :leave-play (req (enable-run-on-server state card (second (:zone card))))} + + "Old Hollywood Grid" + (let [ohg {:req (req (or (in-same-server? card target) + (from-same-server? card target))) + :effect (req (register-persistent-flag! + state side + card :can-steal + (fn [state _ card] + (if-not (some #(= (:title %) (:title card)) (:scored runner)) + ((constantly false) + (toast state :runner "Cannot steal due to Old Hollywood Grid." "warning")) + true))))}] + {:trash-effect + {:req (req (and (= :servers (first (:previous-zone card))) (:run @state))) + :effect (req (register-events state side - {:effect (req (let [newice (assoc hqice :zone (:zone c)) - cndx (ice-index state c) - ices (get-in @state (cons :corp (:zone c))) - newices (apply conj (subvec ices 0 cndx) newice (subvec ices cndx))] - (swap! state assoc-in (cons :corp (:zone c)) newices) - (swap! state update-in [:corp :hand] - (fn [coll] (remove-once #(same-card? % hqice) coll))) - (trigger-event state side :corp-install newice) - (move state side c :hand)))} card nil)))}]}) - -(define-card "Mumbad City Grid" - {:abilities [{:req (req (let [num-ice (count run-ices)] - (and this-server - (>= num-ice 2) - (< (:position run 0) num-ice)))) - :label "Swap the ICE just passed with another piece of ICE protecting this server" - :effect (req (let [passed-ice (nth (get-in @state (vec (concat [:corp :servers] (:server run) [:ices]))) - (:position run)) - ice-zone (:zone passed-ice)] - (resolve-ability - state :corp - {:prompt (msg "Select a piece of ICE to swap with " (:title passed-ice)) - :choices {:req #(and (= ice-zone (:zone %)) (ice? %))} - :effect (req (let [fndx (ice-index state passed-ice) - sndx (ice-index state target) - fnew (assoc passed-ice :zone (:zone target)) - snew (assoc target :zone (:zone passed-ice))] - (swap! state update-in (cons :corp ice-zone) - #(assoc % fndx snew)) - (swap! state update-in (cons :corp ice-zone) - #(assoc % sndx fnew)) - (update-ice-strength state side fnew) - (update-ice-strength state side snew) - (system-msg state side (str "uses Mumbad City Grid to swap " - (card-str state passed-ice) - " with " (card-str state target)))))} - card nil)))}]}) - -(define-card "Mumbad Virtual Tour" - {:flags {:must-trash (req (when installed true))}}) - -(define-card "Mwanza City Grid" - (let [gain-creds-and-clear {:req (req (= (:from-server target) - (second (:zone card)))) - :silent (req true) - :effect (req (let [cnt (total-cards-accessed run) - total (* 2 cnt)] - (when cnt - (gain-credits state :corp total) - (system-msg state :corp - (str "gains " total " [Credits] from Mwanza City Grid")))))} - boost-access-by-3 {:req (req (= target (second (:zone card)))) - :msg "force the Runner to access 3 additional cards" - :effect (req (access-bonus state :runner (-> card :zone second) 3))}] - {:install-req (req (filter #{"HQ" "R&D"} targets)) - :events {:pre-access boost-access-by-3 - :end-access-phase gain-creds-and-clear} - ;; TODO: as written, this may fail if mwanza is trashed outside of a run on its server - ;; (e.g. mwanza on R&D, run HQ, use polop to trash mwanza mid-run, shiro fires to cause RD - :trash-effect ; if there is a run, mark mwanza effects to remain active until the end of the run - {:req (req (:run @state)) - :effect (effect (register-events {:pre-access (assoc boost-access-by-3 :req (req (= target (second (:previous-zone card))))) - :end-access-phase (assoc gain-creds-and-clear :req (req (= (:from-server target) (second (:previous-zone card))))) - :unsuccessful-run-ends {:effect (effect (unregister-events card))} - :successful-run-ends {:effect (effect (unregister-events card))}} - (assoc card :zone '(:discard))))}})) - -(define-card "NeoTokyo Grid" - (let [ng {:req (req (in-same-server? card target)) - :once :per-turn - :msg "gain 1 [Credits]" - :effect (effect (gain-credits 1))}] - {:events {:advance ng - :advancement-placed ng}})) - -(define-card "Nihongai Grid" - {:events - {:successful-run - {:interactive (req true) - :async true - :req (req (and this-server - (or (< (:credit runner) 6) - (< (count (:hand runner)) 2)) - (not-empty (:hand corp)))) - :effect (req (show-wait-prompt state :runner "Corp to use Nihongai Grid") - (let [top5 (take 5 (:deck corp))] - (if (pos? (count top5)) - (continue-ability - state side - {:optional - {:prompt "Use Nihongai Grid to look at top 5 cards of R&D and swap one with a card from HQ?" - :yes-ability - {:async true - :prompt "Choose a card to swap with a card from HQ" - :choices top5 - :effect (req (let [rdc target] - (continue-ability - state side - {:async true - :prompt (msg "Choose a card in HQ to swap for " (:title rdc)) - :choices {:req in-hand?} - :msg "swap a card from the top 5 of R&D with a card in HQ" - :effect (req (let [hqc target - newrdc (assoc hqc :zone [:deck]) - deck (vec (get-in @state [:corp :deck])) - rdcndx (first (keep-indexed #(when (same-card? %2 rdc) %1) deck)) - newdeck (seq (apply conj (subvec deck 0 rdcndx) target (subvec deck rdcndx)))] - (swap! state assoc-in [:corp :deck] newdeck) - (swap! state update-in [:corp :hand] - (fn [coll] (remove-once #(same-card? % hqc) coll))) - (move state side rdc :hand) - (clear-wait-prompt state :runner) - (effect-completed state side eid)))} - card nil)))} - :no-ability {:effect (req (clear-wait-prompt state :runner) - (effect-completed state side eid))}}} - card nil) - (do (clear-wait-prompt state :runner) - (effect-completed state side eid)))))}}}) - -(define-card "Oaktown Grid" - {:events {:pre-trash {:req (req (in-same-server? card target)) - :effect (effect (trash-cost-bonus 3))}}}) - -(define-card "Oberth Protocol" - {:additional-cost [:forfeit] - :events {:advance {:req (req (and (same-server? card target) - (= 1 (count (filter #(= (second (:zone %)) (second (:zone card))) - (map first (turn-events state side :advance))))))) - :msg (msg "place an additional advancement token on " (card-str state target)) - :effect (effect (add-prop :corp target :advance-counter 1 {:placed true}))}}}) - -(define-card "Off the Grid" - {:install-req (req (remove #{"HQ" "R&D" "Archives"} targets)) - :effect (req (prevent-run-on-server state card (second (:zone card)))) - :events {:runner-turn-begins {:effect (req (prevent-run-on-server state card (second (:zone card))))} - :successful-run {:req (req (= target :hq)) - :effect (req (trash state :corp card) - (enable-run-on-server state card - (second (:zone card))) - (system-msg state :corp (str "trashes Off the Grid")))}} - :leave-play (req (enable-run-on-server state card (second (:zone card))))}) - -(define-card "Old Hollywood Grid" - (let [ohg {:req (req (or (in-same-server? card target) + {:pre-steal-cost (assoc ohg :req (req (or (= (:zone (get-nested-host target)) + (:previous-zone card)) + (= (central->zone (:zone target)) + (butlast (:previous-zone card)))))) + :run-ends {:effect (req (unregister-events state side (find-latest state card)) + (clear-persistent-flag! state side card :can-steal))}} + (assoc card :zone '(:discard))))} + :events {:pre-steal-cost ohg + :access {:effect (req (clear-persistent-flag! state side card :can-steal))} + :run-ends nil}}) + + "Overseer Matrix" + (let [om {:req (req (in-same-server? card target)) + :async true + :effect (effect (show-wait-prompt :runner "Corp to use Overseer Matrix") + (continue-ability + {:optional + {:prompt "Pay 1 [Credits] to use Overseer Matrix ability?" + :player :corp + :end-effect (effect (clear-wait-prompt :runner) + (effect-completed eid)) + :yes-ability {:cost [:credit 1] + :msg "give the Runner 1 tag" + :async true + :effect (req (gain-tags state :corp eid 1))}}} + card nil))}] + {:trash-effect + {:req (req (and (= :servers (first (:previous-zone card))) + (:run @state))) + :effect (effect (register-events {:runner-trash (assoc om :req (req (or (= (:zone (get-nested-host target)) + (:previous-zone card)) + (= (central->zone (:zone target)) + (butlast (:previous-zone card)))))) + :run-ends {:effect (effect (unregister-events card))}} + (assoc card :zone '(:discard))))} + :events {:run-ends nil + :runner-trash om}}) + + "Panic Button" + {:init {:root "HQ"} + :install-req (req (filter #{"HQ"} targets)) + :abilities [{:cost [:credit 1] :label "Draw 1 card" :effect (effect (draw)) + :req (req (and run (= (first (:server run)) :hq)))}]} + + "Port Anson Grid" + {:msg "prevent the Runner from jacking out unless they trash an installed program" + :effect (req (when this-server + (prevent-jack-out state side))) + :events {:run {:req (req this-server) + :msg "prevent the Runner from jacking out unless they trash an installed program" + :effect (effect (prevent-jack-out))} + :runner-trash {:req (req (and this-server (program? target))) + :effect (req (swap! state update-in [:run] dissoc :cannot-jack-out))}}} + + "Prisec" + {:access {:req (req (installed? card)) + :async true + :effect (effect (show-wait-prompt :runner "Corp to use Prisec") + (continue-ability + {:optional + {:prompt "Pay 2 [Credits] to use Prisec ability?" + :end-effect (effect (clear-wait-prompt :runner)) + :yes-ability {:cost [:credit 2] + :msg "do 1 meat damage and give the Runner 1 tag" + :async true + :effect (req (wait-for (damage state side :meat 1 {:card card}) + (gain-tags state :corp eid 1)))}}} + card nil))}} + + "Product Placement" + {:flags {:rd-reveal (req true)} + :access {:req (req (not= (first (:zone card)) :discard)) + :msg "gain 2 [Credits]" :effect (effect (gain-credits :corp 2))}} + + "Red Herrings" + (let [ab {:req (req (or (in-same-server? card target) (from-same-server? card target))) - :effect (req (register-persistent-flag! - state side - card :can-steal - (fn [state _ card] - (if-not (some #(= (:title %) (:title card)) (:scored runner)) - ((constantly false) - (toast state :runner "Cannot steal due to Old Hollywood Grid." "warning")) - true))))}] - {:trash-effect - {:req (req (and (= :servers (first (:previous-zone card))) (:run @state))) - :effect (req (register-events - state side - {:pre-steal-cost (assoc ohg :req (req (or (= (:zone (get-nested-host target)) - (:previous-zone card)) - (= (central->zone (:zone target)) - (butlast (:previous-zone card)))))) - :run-ends {:effect (req (unregister-events state side (find-latest state card)) - (clear-persistent-flag! state side card :can-steal))}} - (assoc card :zone '(:discard))))} - :events {:pre-steal-cost ohg - :access {:effect (req (clear-persistent-flag! state side card :can-steal))} - :run-ends nil}})) - -(define-card "Overseer Matrix" - (let [om {:req (req (in-same-server? card target)) - :async true - :effect (effect (show-wait-prompt :runner "Corp to use Overseer Matrix") - (continue-ability - {:optional - {:prompt "Pay 1 [Credits] to use Overseer Matrix ability?" - :player :corp - :end-effect (effect (clear-wait-prompt :runner) - (effect-completed eid)) - :yes-ability {:cost [:credit 1] - :msg "give the Runner 1 tag" - :async true - :effect (req (gain-tags state :corp eid 1))}}} - card nil))}] - {:trash-effect - {:req (req (and (= :servers (first (:previous-zone card))) - (:run @state))) - :effect (effect (register-events {:runner-trash (assoc om :req (req (or (= (:zone (get-nested-host target)) - (:previous-zone card)) - (= (central->zone (:zone target)) - (butlast (:previous-zone card)))))) - :run-ends {:effect (effect (unregister-events card))}} - (assoc card :zone '(:discard))))} - :events {:run-ends nil - :runner-trash om}})) - -(define-card "Panic Button" - {:init {:root "HQ"} - :install-req (req (filter #{"HQ"} targets)) - :abilities [{:cost [:credit 1] :label "Draw 1 card" :effect (effect (draw)) - :req (req (and run (= (first (:server run)) :hq)))}]}) - -(define-card "Port Anson Grid" - {:msg "prevent the Runner from jacking out unless they trash an installed program" - :effect (req (when this-server - (prevent-jack-out state side))) - :events {:run {:req (req this-server) - :msg "prevent the Runner from jacking out unless they trash an installed program" - :effect (effect (prevent-jack-out))} - :runner-trash {:req (req (and this-server (program? target))) - :effect (req (swap! state update-in [:run] dissoc :cannot-jack-out))}}}) - -(define-card "Prisec" - {:access {:req (req (installed? card)) - :async true - :effect (effect (show-wait-prompt :runner "Corp to use Prisec") - (continue-ability - {:optional - {:prompt "Pay 2 [Credits] to use Prisec ability?" - :end-effect (effect (clear-wait-prompt :runner)) - :yes-ability {:cost [:credit 2] - :msg "do 1 meat damage and give the Runner 1 tag" - :async true - :effect (req (wait-for (damage state side :meat 1 {:card card}) - (gain-tags state :corp eid 1)))}}} - card nil))}}) - -(define-card "Product Placement" - {:flags {:rd-reveal (req true)} - :access {:req (req (not= (first (:zone card)) :discard)) - :msg "gain 2 [Credits]" :effect (effect (gain-credits :corp 2))}}) - -(define-card "Red Herrings" - (let [ab {:req (req (or (in-same-server? card target) - (from-same-server? card target))) - :effect (effect (steal-cost-bonus [:credit 5]))}] - {:trash-effect - {:req (req (and (= :servers (first (:previous-zone card))) (:run @state))) - :effect (effect (register-events {:pre-steal-cost (assoc ab :req (req (or (= (:zone target) (:previous-zone card)) - (= (central->zone (:zone target)) - (butlast (:previous-zone card)))))) - :run-ends {:effect (effect (unregister-events card))}} - (assoc card :zone '(:discard))))} - :events {:pre-steal-cost ab :run-ends nil}})) - -(define-card "Reduced Service" - (counter-based-extra-cost + :effect (effect (steal-cost-bonus [:credit 5]))}] + {:trash-effect + {:req (req (and (= :servers (first (:previous-zone card))) (:run @state))) + :effect (effect (register-events {:pre-steal-cost (assoc ab :req (req (or (= (:zone target) (:previous-zone card)) + (= (central->zone (:zone target)) + (butlast (:previous-zone card)))))) + :run-ends {:effect (effect (unregister-events card))}} + (assoc card :zone '(:discard))))} + :events {:pre-steal-cost ab :run-ends nil}}) + + "Reduced Service" + (counter-based-extra-cost [:credit 2] {:events {:successful-run {:req (req (and (pos? (get-counters card :power)) (is-central? (:server run)))) :msg "remove a hosted power counter" :effect (effect (add-counter card :power -1))}} - :effect (req (show-wait-prompt state :runner "Corp to place credits on Reduced Service") - (continue-ability state side {:choices (req (map str (range (inc (min 4 (get-in @state [:corp :credit])))))) - :prompt "How many credits to spend?" - :effect (req (clear-wait-prompt state :runner) - (let [spent (str->int target)] - (deduct state :corp [:credit spent]) - (add-counter state :corp card :power spent) - (system-msg state :corp (str "places " (quantify spent "power counter") " on Reduced Service")) - (effect-completed state side eid)))} - card nil))})) - -(define-card "Research Station" - {:init {:root "HQ"} - :install-req (req (filter #{"HQ"} targets)) - :in-play [:hand-size 2]}) - -(define-card "Ruhr Valley" - (letfn [(change-click-cost [n] - (req (let [server (second (:zone card))] - (swap! state update-in [:corp :servers server :additional-cost] - #(merge-costs (concat % [:click n]))))))] - {:effect (change-click-cost 1) - :leave-play (change-click-cost -1)})) - -(define-card "Rutherford Grid" - {:events {:pre-init-trace {:req (req this-server) - :effect (effect (init-trace-bonus 2))}}}) - -(define-card "Ryon Knight" - {:abilities [{:label "[Trash]: Do 1 brain damage" - :msg "do 1 brain damage" :req (req (and this-server (zero? (:click runner)))) - :async true - :effect (effect (trash card) (damage eid :brain 1 {:card card}))}]}) - -(define-card "SanSan City Grid" - {:effect (req (when-let [agenda (some #(when (agenda? %) %) - (:content (card->server state card)))] - (update-advancement-cost state side agenda))) - :events {:corp-install {:req (req (and (agenda? target) - (in-same-server? card target))) - :effect (effect (update-advancement-cost target))} - :pre-advancement-cost {:req (req (in-same-server? card target)) - :effect (effect (advancement-cost-bonus -1))}}}) - -(define-card "Satellite Grid" - {:effect (req (doseq [c (:ices (card->server state card))] - (set-prop state side c :extra-advance-counter 1)) - (update-all-ice state side)) - :events {:corp-install {:req (req (and (ice? target) - (protecting-same-server? card target))) - :effect (effect (set-prop target :extra-advance-counter 1))}} - :leave-play (req (doseq [c (:ices (card->server state card))] - (update! state side (dissoc c :extra-advance-counter))) - (update-all-ice state side))}) - -(define-card "Self-destruct" - {:install-req (req (remove #{"HQ" "R&D" "Archives"} targets)) - :abilities [{:req (req this-server) - :label "[Trash]: Trace X - Do 3 net damage" - :effect (req (let [serv (card->server state card) - cards (concat (:ices serv) (:content serv))] - (trash state side card) - (doseq [c cards] - (trash state side c)) - (resolve-ability - state side - {:trace {:base (req (dec (count cards))) - :successful {:msg "do 3 net damage" - :effect (effect (damage eid :net 3 {:card card}))}}} - card nil)))}]}) - -(define-card "Shell Corporation" - {:abilities - [{:cost [:click 1] - :msg "store 3 [Credits]" - :once :per-turn - :effect (effect (add-counter card :credit 3))} - {:cost [:click 1] - :msg (msg "gain " (get-counters card :credit) " [Credits]") - :once :per-turn - :label "Take all credits" - :effect (effect (gain-credits (get-counters card :credit)) - (set-prop card :counter {:credit 0}))}]}) - -(define-card "Signal Jamming" - {:abilities [{:label "[Trash]: Cards cannot be installed until the end of the run" - :msg (msg "prevent cards being installed until the end of the run") - :req (req this-server) - :effect (effect (trash (get-card state card) {:cause :ability-cost}))}] - :trash-effect {:effect (effect (register-run-flag! card :corp-lock-install (constantly true)) - (register-run-flag! card :runner-lock-install (constantly true)) - (toast :runner "Cannot install until the end of the run") - (toast :corp "Cannot install until the end of the run"))} - :events {:run-ends nil}}) - -(define-card "Simone Diego" - {:recurring 2 - :interactions {:pay-credits {:req (req (and (= :advance (:source-type eid)) - (same-server? card target))) - :type :recurring}}}) - -(define-card "Strongbox" - (let [ab {:req (req (or (in-same-server? card target) - (from-same-server? card target))) - :effect (effect (steal-cost-bonus [:click 1]))}] - {:trash-effect - {:req (req (and (= :servers (first (:previous-zone card))) (:run @state))) - :effect (effect (register-events {:pre-steal-cost (assoc ab :req (req (or (= (:zone target) (:previous-zone card)) - (= (central->zone (:zone target)) - (butlast (:previous-zone card)))))) - :run-ends {:effect (effect (unregister-events card))}} - (assoc card :zone '(:discard))))} - :events {:pre-steal-cost ab - :run-ends nil}})) - -(define-card "Surat City Grid" - {:events - {:rez {:req (req (and (same-server? card target) - (not (and (upgrade? target) - (is-central? (second (:zone target))))) - (not (same-card? target card)) - (seq (filter #(and (not (rezzed? %)) - (not (agenda? %))) (all-installed state :corp))))) - :effect (effect (resolve-ability - {:optional - {:prompt (msg "Rez another card with Surat City Grid?") - :yes-ability {:prompt "Select a card to rez" - :choices {:req #(and (not (rezzed? %)) - (not (agenda? %)))} - :msg (msg "rez " (:title target) ", lowering the rez cost by 2 [Credits]") - :effect (effect (rez-cost-bonus -2) - (rez target))}}} - card nil))}}}) - -(define-card "Tempus" - {:flags {:rd-reveal (req true)} - :access {:req (req (not= (first (:zone card)) :discard)) - :interactive (req true) - :effect (req (when (= (first (:zone card)) :deck) - (system-msg state :runner (str "accesses Tempus")))) - :trace {:base 3 - :successful - {:msg "make the Runner choose between losing [Click][Click] or suffering 1 brain damage" - :async true - :effect (req (let [tempus card] - (if (< (:click runner) 2) - (do - (system-msg state side "suffers 1 brain damage") - (damage state side eid :brain 1 {:card tempus})) - (do - (show-wait-prompt state :corp "Runner to resolve Tempus") - (continue-ability - state :runner - {:prompt "Lose [Click][Click] or take 1 brain damage?" - :player :runner - :choices ["Lose [Click][Click]" "Take 1 brain damage"] - :async true - :effect - (req (clear-wait-prompt state :corp) - (if (.startsWith target "Take") - (do - (system-msg state side (str "chooses to take 1 brain damage")) - (damage state side eid :brain 1 {:card tempus})) - (do - (system-msg state side "chooses to lose [Click][Click]") - (lose state :runner :click 2) - (effect-completed state side eid))))} - card nil)))))}}}}) - -(define-card "The Twins" - {:abilities [{:label "Reveal and trash a copy of the ICE just passed from HQ" - :req (req (and this-server - (> (count (get-run-ices state)) (:position run)) - (:rezzed (get-in (:ices (card->server state card)) [(:position run)])))) - :effect (req (let [icename (:title (get-in (:ices (card->server state card)) [(:position run)]))] - (resolve-ability - state side - {:prompt "Select a copy of the ICE just passed" - :choices {:req #(and (in-hand? %) - (ice? %) - (= (:title %) icename))} - :effect (req (reveal state side target) - (trash state side (assoc target :seen true)) - (swap! state update-in [:run] - #(assoc % :position (inc (:position run))))) - :msg (msg "trash a copy of " (:title target) " from HQ and force the Runner to encounter it again")} - card nil)))}]}) - -(define-card "Tori Hanzō" - {:events - {:pre-resolve-damage - {:once :per-run - :async true - :req (req (and this-server - (= target :net) - (pos? (last targets)) - (can-pay? state :corp eid card nil [:credit 2]))) - :effect (req (swap! state assoc-in [:damage :damage-replace] true) - (damage-defer state side :net (last targets)) - (show-wait-prompt state :runner "Corp to use Tori Hanzō") - (continue-ability - state side - {:optional - {:prompt (str "Pay 2 [Credits] to do 1 brain damage with Tori Hanzō?") - :player :corp - :yes-ability {:async true - :msg "do 1 brain damage instead of net damage" - :effect (req (swap! state update-in [:damage] dissoc :damage-replace :defer-damage) + :effect (req (show-wait-prompt state :runner "Corp to place credits on Reduced Service") + (continue-ability state side {:choices (req (map str (range (inc (min 4 (get-in @state [:corp :credit])))))) + :prompt "How many credits to spend?" + :effect (req (clear-wait-prompt state :runner) + (let [spent (str->int target)] + (deduct state :corp [:credit spent]) + (add-counter state :corp card :power spent) + (system-msg state :corp (str "places " (quantify spent "power counter") " on Reduced Service")) + (effect-completed state side eid)))} + card nil))}) + + "Research Station" + {:init {:root "HQ"} + :install-req (req (filter #{"HQ"} targets)) + :in-play [:hand-size 2]} + + "Ruhr Valley" + (letfn [(change-click-cost [n] + (req (let [server (second (:zone card))] + (swap! state update-in [:corp :servers server :additional-cost] + #(merge-costs (concat % [:click n]))))))] + {:effect (change-click-cost 1) + :leave-play (change-click-cost -1)}) + + "Rutherford Grid" + {:events {:pre-init-trace {:req (req this-server) + :effect (effect (init-trace-bonus 2))}}} + + "Ryon Knight" + {:abilities [{:label "[Trash]: Do 1 brain damage" + :msg "do 1 brain damage" :req (req (and this-server (zero? (:click runner)))) + :async true + :effect (effect (trash card) (damage eid :brain 1 {:card card}))}]} + + "SanSan City Grid" + {:effect (req (when-let [agenda (some #(when (agenda? %) %) + (:content (card->server state card)))] + (update-advancement-cost state side agenda))) + :events {:corp-install {:req (req (and (agenda? target) + (in-same-server? card target))) + :effect (effect (update-advancement-cost target))} + :pre-advancement-cost {:req (req (in-same-server? card target)) + :effect (effect (advancement-cost-bonus -1))}}} + + "Satellite Grid" + {:effect (req (doseq [c (:ices (card->server state card))] + (set-prop state side c :extra-advance-counter 1)) + (update-all-ice state side)) + :events {:corp-install {:req (req (and (ice? target) + (protecting-same-server? card target))) + :effect (effect (set-prop target :extra-advance-counter 1))}} + :leave-play (req (doseq [c (:ices (card->server state card))] + (update! state side (dissoc c :extra-advance-counter))) + (update-all-ice state side))} + + "Self-destruct" + {:install-req (req (remove #{"HQ" "R&D" "Archives"} targets)) + :abilities [{:req (req this-server) + :label "[Trash]: Trace X - Do 3 net damage" + :effect (req (let [serv (card->server state card) + cards (concat (:ices serv) (:content serv))] + (trash state side card) + (doseq [c cards] + (trash state side c)) + (resolve-ability + state side + {:trace {:base (req (dec (count cards))) + :successful {:msg "do 3 net damage" + :effect (effect (damage eid :net 3 {:card card}))}}} + card nil)))}]} + + "Shell Corporation" + {:abilities + [{:cost [:click 1] + :msg "store 3 [Credits]" + :once :per-turn + :effect (effect (add-counter card :credit 3))} + {:cost [:click 1] + :msg (msg "gain " (get-counters card :credit) " [Credits]") + :once :per-turn + :label "Take all credits" + :effect (effect (gain-credits (get-counters card :credit)) + (set-prop card :counter {:credit 0}))}]} + + "Signal Jamming" + {:abilities [{:label "[Trash]: Cards cannot be installed until the end of the run" + :msg (msg "prevent cards being installed until the end of the run") + :req (req this-server) + :effect (effect (trash (get-card state card) {:cause :ability-cost}))}] + :trash-effect {:effect (effect (register-run-flag! card :corp-lock-install (constantly true)) + (register-run-flag! card :runner-lock-install (constantly true)) + (toast :runner "Cannot install until the end of the run") + (toast :corp "Cannot install until the end of the run"))} + :events {:run-ends nil}} + + "Simone Diego" + {:recurring 2 + :interactions {:pay-credits {:req (req (and (= :advance (:source-type eid)) + (same-server? card target))) + :type :recurring}}} + + "Strongbox" + (let [ab {:req (req (or (in-same-server? card target) + (from-same-server? card target))) + :effect (effect (steal-cost-bonus [:click 1]))}] + {:trash-effect + {:req (req (and (= :servers (first (:previous-zone card))) (:run @state))) + :effect (effect (register-events {:pre-steal-cost (assoc ab :req (req (or (= (:zone target) (:previous-zone card)) + (= (central->zone (:zone target)) + (butlast (:previous-zone card)))))) + :run-ends {:effect (effect (unregister-events card))}} + (assoc card :zone '(:discard))))} + :events {:pre-steal-cost ab + :run-ends nil}}) + + "Surat City Grid" + {:events + {:rez {:req (req (and (same-server? card target) + (not (and (upgrade? target) + (is-central? (second (:zone target))))) + (not (same-card? target card)) + (seq (filter #(and (not (rezzed? %)) + (not (agenda? %))) (all-installed state :corp))))) + :effect (effect (resolve-ability + {:optional + {:prompt (msg "Rez another card with Surat City Grid?") + :yes-ability {:prompt "Select a card to rez" + :choices {:req #(and (not (rezzed? %)) + (not (agenda? %)))} + :msg (msg "rez " (:title target) ", lowering the rez cost by 2 [Credits]") + :effect (effect (rez-cost-bonus -2) + (rez target))}}} + card nil))}}} + + "Tempus" + {:flags {:rd-reveal (req true)} + :access {:req (req (not= (first (:zone card)) :discard)) + :interactive (req true) + :effect (req (when (= (first (:zone card)) :deck) + (system-msg state :runner (str "accesses Tempus")))) + :trace {:base 3 + :successful + {:msg "make the Runner choose between losing [Click][Click] or suffering 1 brain damage" + :async true + :effect (req (let [tempus card] + (if (< (:click runner) 2) + (do + (system-msg state side "suffers 1 brain damage") + (damage state side eid :brain 1 {:card tempus})) + (do + (show-wait-prompt state :corp "Runner to resolve Tempus") + (continue-ability + state :runner + {:prompt "Lose [Click][Click] or take 1 brain damage?" + :player :runner + :choices ["Lose [Click][Click]" "Take 1 brain damage"] + :async true + :effect + (req (clear-wait-prompt state :corp) + (if (.startsWith target "Take") + (do + (system-msg state side (str "chooses to take 1 brain damage")) + (damage state side eid :brain 1 {:card tempus})) + (do + (system-msg state side "chooses to lose [Click][Click]") + (lose state :runner :click 2) + (effect-completed state side eid))))} + card nil)))))}}}} + + "The Twins" + {:abilities [{:label "Reveal and trash a copy of the ICE just passed from HQ" + :req (req (and this-server + (> (count (get-run-ices state)) (:position run)) + (:rezzed (get-in (:ices (card->server state card)) [(:position run)])))) + :effect (req (let [icename (:title (get-in (:ices (card->server state card)) [(:position run)]))] + (resolve-ability + state side + {:prompt "Select a copy of the ICE just passed" + :choices {:req #(and (in-hand? %) + (ice? %) + (= (:title %) icename))} + :effect (req (reveal state side target) + (trash state side (assoc target :seen true)) + (swap! state update-in [:run] + #(assoc % :position (inc (:position run))))) + :msg (msg "trash a copy of " (:title target) " from HQ and force the Runner to encounter it again")} + card nil)))}]} + + "Tori Hanzō" + {:events + {:pre-resolve-damage + {:once :per-run + :async true + :req (req (and this-server + (= target :net) + (pos? (last targets)) + (can-pay? state :corp eid card nil [:credit 2]))) + :effect (req (swap! state assoc-in [:damage :damage-replace] true) + (damage-defer state side :net (last targets)) + (show-wait-prompt state :runner "Corp to use Tori Hanzō") + (continue-ability + state side + {:optional + {:prompt (str "Pay 2 [Credits] to do 1 brain damage with Tori Hanzō?") + :player :corp + :yes-ability {:async true + :msg "do 1 brain damage instead of net damage" + :effect (req (swap! state update-in [:damage] dissoc :damage-replace :defer-damage) + (clear-wait-prompt state :runner) + (pay state :corp card :credit 2) + (wait-for (damage state side :brain 1 {:card card}) + (do (swap! state assoc-in [:damage :damage-replace] true) + (effect-completed state side eid))))} + :no-ability {:async true + :effect (req (swap! state update-in [:damage] dissoc :damage-replace) (clear-wait-prompt state :runner) - (pay state :corp card :credit 2) - (wait-for (damage state side :brain 1 {:card card}) - (do (swap! state assoc-in [:damage :damage-replace] true) - (effect-completed state side eid))))} - :no-ability {:async true - :effect (req (swap! state update-in [:damage] dissoc :damage-replace) - (clear-wait-prompt state :runner) - (effect-completed state side eid))}}} - card nil))} - :prevented-damage {:req (req (and this-server - (= target :net) - (pos? (last targets)))) - :effect (req (swap! state assoc-in [:per-run (:cid card)] true))}}}) - -(define-card "Traffic Analyzer" - {:events {:rez {:req (req (and (protecting-same-server? card target) - (ice? target))) - :interactive (req true) - :trace {:base 2 - :successful {:msg "gain 1 [Credits]" - :effect (effect (gain-credits 1))}}}}}) - -(define-card "Tyr's Hand" - {:abilities [{:label "[Trash]: Prevent a subroutine on a piece of Bioroid ICE from being broken" - :req (req (and (= (butlast (:zone current-ice)) (butlast (:zone card))) - (has-subtype? current-ice "Bioroid"))) - :effect (effect (trash card)) - :msg (msg "prevent a subroutine on " (:title current-ice) " from being broken")}]}) - -(define-card "Underway Grid" - {:implementation "Bypass prevention is not implemented" - :events {:pre-expose {:req (req (same-server? card target)) - :msg "prevent 1 card from being exposed" - :effect (effect (expose-prevent 1))}}}) - -(define-card "Valley Grid" - {:implementation "Activation is manual" - :abilities [{:req (req this-server) - :label "Reduce Runner's maximum hand size by 1 until start of next Corp turn" - :msg "reduce the Runner's maximum hand size by 1 until the start of the next Corp turn" - :effect (req (update! state side (assoc card :times-used (inc (get card :times-used 0)))) - (change-hand-size state :runner -1))}] - :trash-effect {:req (req (and (= :servers (first (:previous-zone card))) (:run @state))) - :effect (req (when-let [n (:times-used card)] - (register-events state side - {:corp-turn-begins - {:msg (msg "increase the Runner's maximum hand size by " n) - :effect (effect (change-hand-size :runner n) - (unregister-events card) - (update! (dissoc card :times-used)))}} - (assoc card :zone '(:discard)))))} - :events {:corp-turn-begins {:req (req (:times-used card)) - :msg (msg "increase the Runner's maximum hand size by " - (:times-used card)) - :effect (effect (change-hand-size :runner (:times-used card)) - (update! (dissoc card :times-used)))}}}) - -(define-card "Warroid Tracker" - (letfn [(wt [card n t] - {:prompt "Choose an installed card to trash due to Warroid Tracker" - :async true - :player :runner - :priority 2 - :choices {:req #(and (installed? %) (runner? %))} - :effect (req (system-msg state side (str "trashes " (card-str state target) " due to Warroid Tracker")) - (trash state side target {:unpreventable true}) - (if (> n t) - (continue-ability state side (wt card n (inc t)) card nil) - (do (clear-wait-prompt state :corp) - (effect-completed state side eid))) - ;; this ends-the-run if WT is the only card and is trashed, and trashes at least one runner card - (when (zero? (count (cards-to-access state side (get-in @state [:run :server])))) - (handle-end-run state side)))})] - {:implementation "Does not handle UFAQ interaction with Singularity" - :events {:runner-trash - {:async true - :req (req (let [target-zone (:zone target) - target-zone (or (central->zone target-zone) target-zone) - warroid-zone (:zone card)] - (= (second warroid-zone) - (second target-zone)))) - :trace {:base 4 - :successful - {:effect - (req (let [n (count (all-installed state :runner)) - n (if (> n 2) 2 n)] - (if (pos? n) - (do (system-msg - state side - (str "uses Warroid Tracker to force the runner to trash " - (quantify n " installed card"))) - (show-wait-prompt state :corp "Runner to choose cards to trash") - (resolve-ability state side (wt card n 1) card nil)) - (system-msg - state side - "uses Warroid Tracker but there are no installed cards to trash"))))}}}}})) - -(define-card "Will-o'-the-Wisp" - {:events - {:successful-run - {:interactive (req true) - :async true - :req (req (and this-server - (some #(has-subtype? % "Icebreaker") (all-active-installed state :runner)))) - :effect (effect (show-wait-prompt :runner "Corp to use Will-o'-the-Wisp") - (continue-ability {:optional - {:prompt "Trash Will-o'-the-Wisp?" - :choices {:req #(has-subtype? % "Icebreaker")} - :yes-ability {:async true - :prompt "Choose an icebreaker used to break at least 1 subroutine during this run" - :choices {:req #(has-subtype? % "Icebreaker")} - :msg (msg "add " (:title target) " to the bottom of the Runner's Stack") - :effect (effect (trash card) - (move :runner target :deck) - (clear-wait-prompt :runner) - (effect-completed eid))} - :no-ability {:effect (effect (clear-wait-prompt :runner) - (effect-completed eid))}}} - card nil))}}}) + (effect-completed state side eid))}}} + card nil))} + :prevented-damage {:req (req (and this-server + (= target :net) + (pos? (last targets)))) + :effect (req (swap! state assoc-in [:per-run (:cid card)] true))}}} + + "Traffic Analyzer" + {:events {:rez {:req (req (and (protecting-same-server? card target) + (ice? target))) + :interactive (req true) + :trace {:base 2 + :successful {:msg "gain 1 [Credits]" + :effect (effect (gain-credits 1))}}}}} + + "Tyr's Hand" + {:abilities [{:label "[Trash]: Prevent a subroutine on a piece of Bioroid ICE from being broken" + :req (req (and (= (butlast (:zone current-ice)) (butlast (:zone card))) + (has-subtype? current-ice "Bioroid"))) + :effect (effect (trash card)) + :msg (msg "prevent a subroutine on " (:title current-ice) " from being broken")}]} + + "Underway Grid" + {:implementation "Bypass prevention is not implemented" + :events {:pre-expose {:req (req (same-server? card target)) + :msg "prevent 1 card from being exposed" + :effect (effect (expose-prevent 1))}}} + + "Valley Grid" + {:implementation "Activation is manual" + :abilities [{:req (req this-server) + :label "Reduce Runner's maximum hand size by 1 until start of next Corp turn" + :msg "reduce the Runner's maximum hand size by 1 until the start of the next Corp turn" + :effect (req (update! state side (assoc card :times-used (inc (get card :times-used 0)))) + (change-hand-size state :runner -1))}] + :trash-effect {:req (req (and (= :servers (first (:previous-zone card))) (:run @state))) + :effect (req (when-let [n (:times-used card)] + (register-events state side + {:corp-turn-begins + {:msg (msg "increase the Runner's maximum hand size by " n) + :effect (effect (change-hand-size :runner n) + (unregister-events card) + (update! (dissoc card :times-used)))}} + (assoc card :zone '(:discard)))))} + :events {:corp-turn-begins {:req (req (:times-used card)) + :msg (msg "increase the Runner's maximum hand size by " + (:times-used card)) + :effect (effect (change-hand-size :runner (:times-used card)) + (update! (dissoc card :times-used)))}}} + + "Warroid Tracker" + (letfn [(wt [card n t] + {:prompt "Choose an installed card to trash due to Warroid Tracker" + :async true + :player :runner + :priority 2 + :choices {:req #(and (installed? %) (runner? %))} + :effect (req (system-msg state side (str "trashes " (card-str state target) " due to Warroid Tracker")) + (trash state side target {:unpreventable true}) + (if (> n t) + (continue-ability state side (wt card n (inc t)) card nil) + (do (clear-wait-prompt state :corp) + (effect-completed state side eid))) + ;; this ends-the-run if WT is the only card and is trashed, and trashes at least one runner card + (when (zero? (count (cards-to-access state side (get-in @state [:run :server])))) + (handle-end-run state side)))})] + {:implementation "Does not handle UFAQ interaction with Singularity" + :events {:runner-trash + {:async true + :req (req (let [target-zone (:zone target) + target-zone (or (central->zone target-zone) target-zone) + warroid-zone (:zone card)] + (= (second warroid-zone) + (second target-zone)))) + :trace {:base 4 + :successful + {:effect + (req (let [n (count (all-installed state :runner)) + n (if (> n 2) 2 n)] + (if (pos? n) + (do (system-msg + state side + (str "uses Warroid Tracker to force the runner to trash " + (quantify n " installed card"))) + (show-wait-prompt state :corp "Runner to choose cards to trash") + (resolve-ability state side (wt card n 1) card nil)) + (system-msg + state side + "uses Warroid Tracker but there are no installed cards to trash"))))}}}}}) + + "Will-o'-the-Wisp" + {:events + {:successful-run + {:interactive (req true) + :async true + :req (req (and this-server + (some #(has-subtype? % "Icebreaker") (all-active-installed state :runner)))) + :effect (effect (show-wait-prompt :runner "Corp to use Will-o'-the-Wisp") + (continue-ability {:optional + {:prompt "Trash Will-o'-the-Wisp?" + :choices {:req #(has-subtype? % "Icebreaker")} + :yes-ability {:async true + :prompt "Choose an icebreaker used to break at least 1 subroutine during this run" + :choices {:req #(has-subtype? % "Icebreaker")} + :msg (msg "add " (:title target) " to the bottom of the Runner's Stack") + :effect (effect (trash card) + (move :runner target :deck) + (clear-wait-prompt :runner) + (effect-completed eid))} + :no-ability {:effect (effect (clear-wait-prompt :runner) + (effect-completed eid))}}} + card nil))}}}}) diff --git a/src/clj/game/core/card_defs.clj b/src/clj/game/core/card_defs.clj index ae0137cbcf..79b10db2db 100644 --- a/src/clj/game/core/card_defs.clj +++ b/src/clj/game/core/card_defs.clj @@ -3,30 +3,46 @@ [clojure.java.io :refer [file]] [clojure.stacktrace :refer [print-stack-trace]])) -(defonce card-defs (atom {})) +(defn- load-all-cards + "Load all card definitions into their own namespaces" + ([] (load-all-cards nil)) + ([path] + (doall (pmap load-file + (->> (file (str "src/clj/game/cards" (when path (str "/" path ".clj")))) + (file-seq) + (filter #(and (.isFile %) + (ends-with? % ".clj"))) + (map str)))))) -(defn define-card - [title impl] - (swap! card-defs assoc title impl)) +(defn- get-card-defs + ([] (get-card-defs nil)) + ([path] + (->> (all-ns) + (filter #(starts-with? % (str "game.cards" (when path (str "." path))))) + (map #(ns-resolve % 'card-definitions)) + (map var-get) + (apply merge)))) + +(def card-defs {}) + +(defn reset-card-defs + "Performs any once only initialization that should be performed on startup" + ([] (reset-card-defs nil)) + ([path] + (let [cards-var #'game.core.card-defs/card-defs] + (alter-var-root cards-var + (constantly + (merge card-defs + (do (load-all-cards path) + (get-card-defs path)))))) + 'loaded)) (defn card-def "Retrieves a card's abilities definition map." [card] (if-let [title (:title card)] - (get @card-defs title) + (get card-defs title) (.println *err* (with-out-str (print-stack-trace (Exception. (str "Tried to select card def for non-existent card: " card)) 25))))) - -(defn reset-card-defs [] - (require '[game.cards.agendas] - '[game.cards.assets] - '[game.cards.events] - '[game.cards.hardware] - '[game.cards.ice] - '[game.cards.identities] - '[game.cards.operations] - '[game.cards.programs] - '[game.cards.resources] - '[game.cards.upgrades])) diff --git a/src/clj/game/core/events.clj b/src/clj/game/core/events.clj index 09914cfde0..987b492692 100644 --- a/src/clj/game/core/events.clj +++ b/src/clj/game/core/events.clj @@ -1,6 +1,6 @@ (in-ns 'game.core) -(declare can-trigger? clear-wait-prompt event-title get-card +(declare can-trigger? card-def clear-wait-prompt event-title get-card get-nested-host get-remote-names get-runnable-zones get-zones installed? register-suppress resolve-ability show-wait-prompt trigger-suppress unregister-suppress) diff --git a/test/clj/game_test/core.clj b/test/clj/game_test/core.clj index 89ed6e0c94..6c08e2292d 100644 --- a/test/clj/game_test/core.clj +++ b/test/clj/game_test/core.clj @@ -17,12 +17,21 @@ edn/read-string merge)) -(when (empty? @all-cards) - (->> (load-cards) - (map (juxt :title identity)) - (into {}) - (reset! all-cards))) -(reset-card-defs) +(defn load-all-cards [] + (when (empty? @all-cards) + (->> (load-cards) + (map #(assoc % :cid (utils/make-cid))) + (map (juxt :title identity)) + (into {}) + (reset! all-cards)) + (reset-card-defs))) +(load-all-cards) + +(hawk/watch! [{:paths ["src/clj/game/cards"] + :filter hawk/file? + :handler (fn [ctx e] + (reset-card-defs + (-> e :file io/file .getName (string/split #"\.") first)))}]) ;; General utilities necessary for starting a new game (defn find-card