diff --git a/src/clj/game/cards/agendas.clj b/src/clj/game/cards/agendas.clj index d817f40807..5792e2f87b 100644 --- a/src/clj/game/cards/agendas.clj +++ b/src/clj/game/cards/agendas.clj @@ -593,6 +593,7 @@ (defcard "Corporate Sales Team" (let [e {:req (req (pos? (get-counters card :credit))) :msg "gain 1 [Credits]" + :automatic :gain-credits :async true :effect (req (take-credits state side eid card :credit 1))}] {:on-score {:effect (req (add-counter state side eid card :credit 10 nil)) @@ -614,6 +615,7 @@ (let [ability {:req (req tagged) :async true :label "Do 1 meat damage (start of turn)" + :automatic :corp-damage :once :per-turn :msg "do 1 meat damage" :effect (effect (damage eid :meat 1 {:card card}))}] @@ -1765,6 +1767,7 @@ (defcard "Puppet Master" {:events [{:event :successful-run + :skippable true :player :corp :interactive (req true) :waiting-prompt true @@ -2086,6 +2089,7 @@ :stolen add-credits-abi :on-score add-credits-abi :events [{:event :corp-turn-begins + :automatic :gain-credits :optional {:req (req (pos? (get-counters card :credit))) :once :per-turn diff --git a/src/clj/game/cards/assets.clj b/src/clj/game/cards/assets.clj index e059c33d80..74e6e9f6a8 100644 --- a/src/clj/game/cards/assets.clj +++ b/src/clj/game/cards/assets.clj @@ -77,6 +77,7 @@ (let [num-counters (fn [card] (min per-turn (get-counters card counter-type))) ability {:msg (msg "gain " (num-counters card) " [Credits]") :once :per-turn + :automatic :gain-credits :req (req (:corp-phase-12 @state)) :label (str "Gain " per-turn " [Credits] (start of turn)") :async true @@ -381,6 +382,7 @@ (defcard "Bio-Ethics Association" (let [ability {:req (req unprotected) + :automatic :corp-damage :async true :label "Do 1 net damage (start of turn)" :once :per-turn @@ -411,6 +413,7 @@ (let [ability {:msg "gain 1 [Credits]" :label "Gain 1 [Credits] (start of turn)" :once :per-turn + :automatic :pre-gain-credits :interactive (req true) :async true :effect (req (wait-for (gain-credits state side 1) @@ -530,6 +533,7 @@ (gain-credits state side 4) (draw state side eid 1))))}}} queue-ability {:interactive (req true) + :skippable true :event :corp-turn-begins :req (req (and (not-used-once? state {:once :per-turn} card) (:corp-phase-12 @state))) @@ -649,6 +653,7 @@ {:flags {:corp-phase-12 (req true)} :derezzed-events [corp-rez-toast] :events [{:event :corp-turn-begins + :skippable true :prompt "Choose one" :interactive (req true) :choices (req [(when (seq (:hand corp)) "Trash 1 card from HQ to gain 2 [Credits] and draw 1 card") @@ -696,6 +701,7 @@ (defcard "Commercial Bankers Group" (let [ability {:req (req unprotected) + :automatic :gain-credits :label "Gain 3 [Credits] (start of turn)" :once :per-turn :msg "gain 3 [Credits]" @@ -796,6 +802,7 @@ (let [ability {:once :per-turn :async true :label "Draw 1 card (start of turn)" + :automatic :draw-cards :interactive (req true) :effect (effect (continue-ability {:optional @@ -872,6 +879,7 @@ (second (get-zone (:host card)))]) (:successful-run runner-reg-last)))) :label "gain 3 [Credits] (start of turn)" + :automatic :gain-credits :msg "gain 3 [Credits]" :async true :effect (effect (gain-credits :corp eid 3))}] @@ -1054,6 +1062,7 @@ :suppress [{:event :corp-turn-begins :req (req (= (:cid target) (:ebc-rezzed (get-card state card))))}] :events [{:event :corp-turn-ends + :silent (req true) :req (req (:ebc-rezzed card)) :effect (effect (update! (dissoc card :ebc-rezzed)))}] :abilities [{:async true @@ -1127,6 +1136,7 @@ {:once :per-turn :req (req (and (:corp-phase-12 @state) (not-empty (:deck corp)))) + :skippable true :interactive (req true) :label "Look at the top 3 cards of R&D (start of turn)" :async true @@ -1225,6 +1235,7 @@ (move state :corp target :hand) (effect-completed state side eid))))} ability {:once :per-turn + :skippable true :async true :label "Search R&D for an operation (start of turn)" :interactive (req true) @@ -1263,6 +1274,7 @@ (when (zero? (remaining-draws state :runner)) (prevent-draw state :runner)))} :events [{:event :runner-turn-begins + :silent (req true) :effect (effect (max-draw :runner 2))}] :leave-play (req (swap! state update-in [:runner :register] dissoc :max-draw :cannot-draw))}) @@ -1301,6 +1313,7 @@ :effect (effect (add-prop eid target :advance-counter 1 {:placed true}))} ability {:req (req (:corp-phase-12 @state)) :label "Move 1 hosted advancement counter to another card you can advance (start of turn)" + :skippable true :once :per-turn :waiting-prompt true :prompt "Choose an installed card to move 1 hosted advancement counter from" @@ -1395,6 +1408,7 @@ (let [ability {:msg "gain 1 [Credits] and draw 1 card" :label "Gain 1 [Credits] and draw 1 card (start of turn)" :once :per-turn + :automatic :draw-cards :async true :req (req (:corp-phase-12 @state)) :effect (req (wait-for (gain-credits state side 1) @@ -1515,6 +1529,7 @@ (resolve-ability state side eid ability card nil) (effect-completed state side eid))))} {:event :corp-turn-ends + :silent (req true) :effect cleanup}]})) (defcard "Kala Ghoda Real TV" @@ -1571,6 +1586,7 @@ (update-all-agenda-points state) (check-win-by-agenda state side))}] :events [{:event :corp-turn-begins + :automatic :last ;;so it goes after warm reception :async true :effect (effect (add-counter eid card :power 1 nil))}]}) @@ -1702,6 +1718,7 @@ :label (str "Gain 2 [Credits] (start of turn)") :msg (msg "gain " (min 2 (get-counters card :credit)) " [Credits]") :async true + :automatic :gain-credits :effect (req (wait-for (take-credits state side card :credit 2) (if (not (pos? (get-counters (get-card state card) :credit))) @@ -1737,6 +1754,7 @@ (defcard "Marked Accounts" (let [ability {:msg "gain 1 [Credits]" :label "Take 1 [Credits] (start of turn)" + :automatic :gain-credits :once :per-turn :req (req (pos? (get-counters card :credit))) :async true @@ -1777,6 +1795,7 @@ (defcard "Mental Health Clinic" (let [ability {:msg "gain 1 [Credits]" :label "Gain 1 [Credits] (start of turn)" + :automatic :gain-credits :once :per-turn :async true :effect (effect (gain-credits eid 1))}] @@ -1883,6 +1902,7 @@ (defcard "Mumbad Construction Co." {:derezzed-events [corp-rez-toast] :events [{:event :corp-turn-begins + :silent (req true) :async true :effect (effect (add-prop eid card :advance-counter 1 {:placed true}))}] :abilities [{:cost [(->c :credit 2)] @@ -1945,6 +1965,7 @@ (defcard "NASX" (let [ability {:msg "gain 1 [Credits]" + :automatic :gain-credits :label "Gain 1 [Credits] (start of turn)" :once :per-turn :async true @@ -2040,6 +2061,7 @@ {:async true :interactive (req true) :once :per-turn + :automatic :draw-cards :label "Take 3 [Credits] (start of turn)" :msg (msg "gain " (min 3 (get-counters card :credit)) " [Credits]") :req (req (:corp-phase-12 @state)) @@ -2102,6 +2124,7 @@ :label "Gain 1 [Credits] (start of turn)" :once :per-turn :async true + :automatic :gain-credits :effect (effect (gain-credits eid 1))}] {:derezzed-events [corp-rez-toast] :events [(assoc ability :event :corp-turn-begins)] @@ -2135,6 +2158,7 @@ (let [ability {:msg "make each player draw 1 card" :label "Make each player draw 1 card (start of turn)" :once :per-turn + :automatic :draw-cards :async true :effect (req (wait-for (draw state :corp 1) (draw state :runner eid 1)))}] @@ -2269,6 +2293,7 @@ (let [ability {:once :per-turn :label "Reveal the top card of R&D and gain 2 [Credits] (start of turn)" :interactive (req true) + :automatic :gain-credits :msg (msg "reveal " (:title (first (:deck corp))) " from the top of R&D" " and gain 2 [Credits]") @@ -2362,6 +2387,7 @@ (defcard "Rashida Jaheem" (let [ability {:once :per-turn + :skippable true :async true :label "Gain 3 [Credits] and draw 3 cards (start of turn)" :req (req (:corp-phase-12 @state)) @@ -2389,6 +2415,7 @@ (let [ability {:effect (effect (gain-credits eid (if tagged 2 1))) :async true :label "Gain credits (start of turn)" + :automatic :gain-credits :once :per-turn :msg (msg (if tagged "gain 2 [Credits]" "gain 1 [Credits]"))}] {:on-rez {:msg "take 1 bad publicity" @@ -2401,6 +2428,7 @@ (let [ability {:async true :once :per-turn :label "Trash this asset to do 2 net damage (start of turn)" + :automatic :corp-damage :interactive (req true) :req (req (:corp-phase-12 @state)) :effect @@ -2447,6 +2475,7 @@ (let [ability {:msg "gain 2 [Credits]" :label "Gain 2 [Credits] (start of turn)" :once :per-turn + :automatic :gain-credits :async true :effect (effect (gain-credits eid 2))}] {:derezzed-events [corp-rez-toast] @@ -2603,6 +2632,7 @@ (let [ability {:effect (effect (gain-credits eid 2)) :async true :once :per-turn + :automatic :gain-credits :label "Gain 2 [Credits] (start of turn)" :msg "gain 2 [Credits]"}] {:derezzed-events [corp-rez-toast] @@ -2919,6 +2949,7 @@ (defcard "The News Now Hour" {:events [{:event :runner-turn-begins + :silent (req true) :effect (req (prevent-current state side))}] :on-rez {:effect (req (prevent-current state side))} :leave-play (req (swap! state assoc-in [:runner :register :cannot-play-current] false))}) @@ -3010,6 +3041,7 @@ (defcard "Ubiquitous Vig" (let [ability {:msg (msg "gain " (get-counters card :advancement) " [Credits]") :label "Gain 1 [Credits] for each advancement counter (start of turn)" + :automatic :corp-gain-credits :once :per-turn :async true :effect (effect (gain-credits eid (get-counters card :advancement)))}] @@ -3022,6 +3054,7 @@ {:data {:counter {:power 3}} :derezzed-events [corp-rez-toast] :events [{:event :corp-turn-begins + :automatic :corp-damage :async true :interactive (req true) :effect (req (wait-for @@ -3139,6 +3172,7 @@ (continue-ability state side (choice (remove-once #(= % chosen-ability) abis) (dec n)) card nil) (effect-completed state side eid)))))})) ability {:async true + :automatic :last ;; so it can go after rashida :label "resolve an ability (start of turn)" :once :per-turn :effect (effect (continue-ability (choice all (if (< 1 (count (filter asset? (all-active-installed state :corp)))) diff --git a/src/clj/game/cards/events.clj b/src/clj/game/cards/events.clj index 3a04632793..64cae252f8 100644 --- a/src/clj/game/cards/events.clj +++ b/src/clj/game/cards/events.clj @@ -142,6 +142,7 @@ :run-again attacked-server :run-again-ice ice))))}}} {:event :encounter-ice + :automatic :bypass :once :per-run :req (req (and (get-in card [:special :run-again]) (same-card? (:ice context) (get-in card [:special :run-again-ice])))) @@ -713,6 +714,7 @@ :async true :effect (effect (make-run eid target card))} :events [{:event :encounter-ice + :skippable true :optional {:prompt "Install a program?" :req (req (first-run-event? state side :encounter-ice)) @@ -1099,6 +1101,7 @@ :async true :effect (effect (make-run eid target card))} :events [{:event :encounter-ice + :skippable true :optional {:req (req (seq (filter program? (:hand runner)))) :prompt "Install a program from the grip?" @@ -1549,6 +1552,7 @@ :change-in-game-state (req hq-runnable) :effect (req (make-run state side eid :hq card))} :events [{:event :encounter-ice + :automatic :bypass :req (req (< (get-in card [:special :bypass-count] 0) 2)) :msg (msg "bypass " (:title (:ice context))) :effect (req (bypass-ice state) @@ -1827,6 +1831,7 @@ :change-in-game-state (req hq-runnable) :effect (req (make-run state side eid :hq card))} :events [{:event :successful-run + :automatic :gain-credits :async true :msg "gain 9 [Credits] and take 1 tag" :req (req (and (= :hq (target-server context)) @@ -1851,6 +1856,7 @@ :change-in-game-state (req archives-runnable) :effect (req (make-run state side eid :archives card))} :events [{:event :breach-server + :automatic :pre-breach :async true :req (req (and (= target :archives) ;; don't prompt unless there's at least 1 rezzed piece of ice matching one in Archives @@ -2040,6 +2046,7 @@ :async true :effect (effect (make-run eid target card))} :events [{:event :encounter-ice + :automatic :bypass :req (req (first-run-event? state side :encounter-ice)) :msg (msg "bypass " (:title (:ice context))) :effect (req (bypass-ice state))}]}) @@ -2069,6 +2076,7 @@ {:on-play {:msg "prevent the Corp from rezzing non-ice cards on the Runner's turn" :effect ab} :events [{:event :runner-turn-begins + :silent (req true) :effect ab}] :leave-play (req (clear-all-flags-for-card! state side card))})) @@ -2131,6 +2139,7 @@ :effect (effect (update! (update-in (get-card state card) [:special :how-deep-are-we] (fnil inc 0))))}]) (make-run eid target card))} :events [{:event :successful-run + :automatic :gain-credits :interactive (req true) :async true :req (req this-card-run) @@ -2160,6 +2169,7 @@ :async true :effect (req (make-run state side eid target card))} :events [{:event :successful-run + :automatic :draw-cards :silent (req true) :async true :msg "draw 1 card" @@ -2174,6 +2184,7 @@ :change-in-game-state (req rd-runnable) :effect (req (make-run state side eid :rd card))} :events [{:event :successful-run + :automatic :draw-cards :silent (req true) :async true :req (req (and (= :rd (target-server context)) @@ -2189,6 +2200,7 @@ :choices (req runnable-servers) :effect (effect (make-run eid target card))} :events [{:event :successful-run + :automatic :draw-cards :req (req (and this-card-run (not (zone-locked? state :runner :discard)))) :prompt "Choose 1 card to add to the grip" @@ -2589,6 +2601,7 @@ (make-run state side eid :rd card) (effect-completed state side eid)))))} :events [{:event :successful-run + :automatic :gain-credits :req (req (and (get-in card [:special :run-again]) (= :rd (target-server context)))) :msg "gain 4 [Credits]" @@ -2740,6 +2753,7 @@ :async true :effect (effect (make-run eid target card))} :events [{:event :runner-turn-begins + :skippable true :async true :interactive (req true) :silent (req (let [ashes (filter #(= "Out of the Ashes" (:title %)) @@ -3200,6 +3214,7 @@ :async true :effect (effect (make-run eid target card))} :events [{:event :encounter-ice + :skippable true :optional (:optional (offer-jack-out {:req (req (first-run-event? state side :encounter-ice))}))}]}) @@ -3299,6 +3314,7 @@ card (let [target-ice target] [{:event :encounter-ice + :automatic :bypass :req (req (same-card? target-ice (:ice context))) :msg (msg "bypass " (:title (:ice context))) :effect (req (bypass-ice state))}])) @@ -3396,6 +3412,7 @@ :change-in-game-state (req hq-runnable) :effect (req (make-run state side eid :hq card))} :events [{:event :successful-run + :automatic :draw-cards :silent (req true) :req (req (and (= :hq (target-server context)) this-card-run)) @@ -3480,11 +3497,13 @@ :async true :effect (effect (make-run eid target card))} :events [{:event :encounter-ice + :automatic :bypass :req (req (first-run-event? state side :encounter-ice)) :once :per-run :msg (msg "bypass " (card-str state current-ice)) :effect (req (bypass-ice state))} {:event :encounter-ice + :skippable true :req (req (and (= 2 (count (run-events state side :encounter-ice))) (threat-level 4 state))) :async true @@ -3671,6 +3690,7 @@ :async true :effect (effect (make-run eid target card))} :events [{:event :encounter-ice + :automatic :bypass :req (req (= 1 run-position)) :msg (msg "bypass " (:title (:ice context))) :effect (req (bypass-ice state))}]}) @@ -3999,6 +4019,7 @@ {:effect (effect (register-events card [{:event :runner-turn-ends + :automatic :gain-credits :duration :end-of-turn :unregister-once-resolved true :msg (msg "gain " (* 2 (count (:successful-run runner-reg))) " [Credits]") @@ -4020,7 +4041,8 @@ :value ["Sentry" "Code Gate" "Barrier"]})) (add-icon state side card target "T" (faction-label card)) (let [t target] - (register-events state side card + (register-events + state side card [{:event :runner-turn-ends :duration :end-of-turn :unregister-once-resolved true @@ -4079,6 +4101,7 @@ (update! state side (assoc-in card [:special :run-eid] eid)) (make-run state side eid :rd card))} :events [{:event :successful-run + :automatic :gain-credits :unregister-once-resolved true :silent (req true) :req (req (and (= :rd (target-server context)) diff --git a/src/clj/game/cards/hardware.clj b/src/clj/game/cards/hardware.clj index a343cb8799..af68add80f 100644 --- a/src/clj/game/cards/hardware.clj +++ b/src/clj/game/cards/hardware.clj @@ -120,7 +120,6 @@ (defcard "Akamatsu Mem Chip" {:static-abilities [(mu+ 1)]}) - (defcard "Alarm Clock" (let [ability {:once :per-turn :req (req (:runner-phase-12 @state)) @@ -130,6 +129,7 @@ :effect (req (register-events state side card [{:event :encounter-ice + :skippable true :unregister-once-resolved true :duration :end-of-run :optional @@ -144,6 +144,7 @@ (effect-completed state side eid)))}] {:flags {:runner-phase-12 (req true)} :events [{:event :runner-turn-begins + :skippable true :interactive (req true) :optional {:once :per-turn @@ -186,6 +187,7 @@ (defcard "Archives Interface" {:events [{:event :breach-server + :automatic :pre-breach :async true :interactive (req true) :req (req (and (= target :archives) @@ -557,6 +559,7 @@ {:flags {:runner-phase-12 (req (>= (count (all-installed state :runner)) 2))} :events [(assoc ability :event :runner-turn-begins + :skippable true :interactive (req true))] :abilities [ability]})) @@ -693,6 +696,7 @@ (defcard "Desperado" {:static-abilities [(mu+ 1)] :events [{:event :successful-run + :automatic :gain-credits :silent (req true) :async true :msg "gain 1 [Credits]" @@ -700,6 +704,7 @@ (defcard "Devil Charm" {:events [{:event :encounter-ice + :skippable true :interactive (req true) :optional {:prompt "Remove Devil Charm from the game to give encountered ice -6 strength?" @@ -870,6 +875,7 @@ (fn [state card] (update! state :runner (assoc-in (get-card state card) [:special :flame-out-trigger] true))) maybe-turn-end {:async true + :automatic :last :req (req (:flame-out-trigger (:special (get-card state card)))) :effect (req (update! state side (assoc-in (get-card state card) [:special :flame-out-trigger] nil)) (if-let [hosted (first (:hosted card))] @@ -958,6 +964,7 @@ (defcard "Friday Chip" (let [ability {:msg (msg "move 1 virus counter to " (:title target)) + :skippable true :req (req (and (pos? (get-counters card :virus)) (pos? (count-virus-programs state)))) :choices {:card virus-program?} @@ -1181,6 +1188,7 @@ :async true :effect (effect (lose-credits :corp eid 1))} {:event :successful-run + :skippable true :optional {:req (req (= :archives (target-server context))) :prompt (msg "Trash " (:title card) " to force the Corp to lose 3 [Credits]?") @@ -1272,6 +1280,7 @@ (defcard "Knobkierie" {:static-abilities [(virus-mu+ 3)] :events [{:event :successful-run + :skippable true :interactive (req true) :optional {:req (req (and (first-event? state :runner :successful-run) (pos? (count-virus-programs state)))) @@ -1474,6 +1483,7 @@ (defcard "Mirror" {:static-abilities [(mu+ 2)] :events [{:event :successful-run + :skippable true :async true :req (req (= :rd (target-server context))) :effect (effect (continue-ability @@ -1511,6 +1521,7 @@ (defcard "Mu Safecracker" {:implementation "Stealth credit restriction not enforced" :events [{:event :successful-run + :skippable true :optional {:req (req (and (= :hq (target-server context)) (some #(has-subtype? % "Stealth") @@ -1522,6 +1533,7 @@ :effect (effect (register-events card [(breach-access-bonus :hq 1 {:duration :end-of-run})]))}}} {:event :successful-run + :skippable true :optional {:req (req (and (= :rd (target-server context)) (some #(has-subtype? % "Stealth") @@ -1618,6 +1630,7 @@ :msg "suffer 1 meat damage" :effect (effect (damage eid :meat 1 {:unboostable true :card card}))} :events [{:event :successful-run + :automatic :drain-credits :req (req (and (= :hq (first (:server target))) (first-event? state side :successful-run #(= :hq (first (:server (first %))))))) @@ -1659,6 +1672,7 @@ (defcard "Paragon" {:static-abilities [(mu+ 1)] :events [{:event :successful-run + :automatic :pre-draw :interactive (get-autoresolve :auto-fire (complement never?)) :silent (get-autoresolve :auto-fire never?) :optional @@ -2043,10 +2057,12 @@ :req (req (and (some #{:hand} (:previous-zone (:card context))) (zero? (count (:hand runner)))))) {:event :runner-turn-begins + :automatic :draw-cards :req (req (empty? (:hand runner))) :async true :effect (effect (continue-ability ability card nil))} {:event :corp-turn-begins + :automatic :draw-cards :req (req (empty? (:hand runner))) :async true :effect (effect (continue-ability ability card nil))}] @@ -2100,6 +2116,7 @@ {:static-abilities [(mu+ 1) (link+ 1)] :events [{:event :encounter-ice + :skippable true :interactive (req true) :optional {:prompt "Trace 5 to bypass current ice?" @@ -2166,6 +2183,7 @@ (abs))))] {:static-abilities [(mu+ 2)] :events [{:event :encounter-ice + :skippable true :interactive (req true) :optional {:prompt "Lower your maximum hand size by 1 to reduce the strength of encountered ice to 0?" @@ -2242,6 +2260,7 @@ (defcard "Solidarity Badge" {:events [{:event :runner-turn-begins + :skippable true :req (req (pos? (get-counters (get-card state card) :power))) :async true :interactive (req (pos? (get-counters (get-card state card) :power))) @@ -2352,6 +2371,7 @@ (defcard "The Gauntlet" {:static-abilities [(mu+ 2)] :events [{:event :breach-server + :automatic :pre-breach :req (req (= :hq target)) :effect (req (let [evs (run-events state side :subroutines-broken) relevant (filter #(let [context (first %) @@ -2440,6 +2460,7 @@ {:data {:counter {:power 1}} :req (req (some #{:hq :rd :archives} (:successful-run runner-reg))) :events [{:event :runner-turn-begins + :automatic :force-discard :async true :effect (req (if (<= 3 (get-counters (get-card state card) :power)) (wait-for (trash state side card {:cause-card card}) @@ -2510,6 +2531,7 @@ (defcard "Ubax" (let [ability {:req (req (:runner-phase-12 @state)) + :automatic :draw-cards :msg "draw 1 card" :label "Draw 1 card (start of turn)" :once :per-turn @@ -2545,6 +2567,7 @@ (defcard "Vigil" (let [ability {:req (req (and (:runner-phase-12 @state) (= (count (:hand corp)) (hand-size state :corp)))) + :automatic :draw-cards :msg "draw 1 card" :label "Draw 1 card (start of turn)" :once :per-turn @@ -2591,6 +2614,7 @@ :msg "place 1 power counter on itself" :effect (req (add-counter state :runner eid card :power 1 {:placed true}))} {:event :breach-server + :automatic :pre-breach :async true :req (req (and (= :rd target) (pos? (get-counters card :power)))) @@ -2635,6 +2659,7 @@ {:on-install {:async true :effect (effect (damage eid :brain 1 {:card card}))} :events [{:event :successful-run + :automatic :draw-cards :async true :req (req (and (is-central? (:server context)) (first-event? state side :successful-run diff --git a/src/clj/game/cards/ice.clj b/src/clj/game/cards/ice.clj index e892e40dc0..462efc8276 100644 --- a/src/clj/game/cards/ice.clj +++ b/src/clj/game/cards/ice.clj @@ -927,6 +927,7 @@ (register-events state side card [{:event :successful-run + :automatic :corp-lose-tag :duration :end-of-run :unregister-once-resolved true :async true @@ -1161,6 +1162,7 @@ :effect (effect (register-events card [{:event :successful-run + :automatic :corp-damage :duration :end-of-run :async true :msg "do 3 meat damage" @@ -2110,6 +2112,7 @@ (effect-completed state side eid)))} card nil)))}] {:events [{:event :corp-turn-begins + :skippable true :interactive (req true) :req (req (rezzed? card)) :optional {:prompt (msg "Add " (card-str state card) " to HQ?") @@ -4041,6 +4044,7 @@ (defcard "Thimblerig" (let [ability {:interactive (req run) + :skippable true :optional {:req (req (and (<= 2 (count (filter ice? (all-installed state :corp)))) (if run (same-card? (:ice context) card) true))) diff --git a/src/clj/game/cards/identities.clj b/src/clj/game/cards/identities.clj index 67172b0066..91938fbd92 100644 --- a/src/clj/game/cards/identities.clj +++ b/src/clj/game/cards/identities.clj @@ -260,6 +260,7 @@ (defcard "Akiko Nisei: Head Case" {:events [{:event :breach-server + :automatic :pre-breach :interactive (req true) :psi {:req (req (= target :rd)) :player :runner @@ -271,6 +272,7 @@ (defcard "Alice Merchant: Clan Agitator" {:events [{:event :successful-run :interactive (req true) + :automatic :force-discard :req (req (and (= :archives (target-server context)) (first-successful-run-on-server? state :archives) (not-empty (:hand corp)))) @@ -732,6 +734,7 @@ (defcard "Gabriel Santiago: Consummate Professional" {:events [{:event :successful-run + :automatic :gain-credits :silent (req true) :req (req (and (= :hq (target-server context)) (first-successful-run-on-server? state :hq))) @@ -904,6 +907,7 @@ :req (req (= side :runner)) :effect (effect (update! (assoc card :flipped false :face :front)))} {:event :runner-turn-ends + :automatic :gain-credits :interactive (req true) :async true :effect (req (cond @@ -911,16 +915,15 @@ (not (:accessed-cards runner-reg))) (do (system-msg state :runner "flips [their] identity to Hoshiko Shiro: Untold Protagonist") (continue-ability state :runner {:effect flip-effect} card nil)) - (and (not (:flipped card)) (:accessed-cards runner-reg)) (wait-for (gain-credits state :runner 2) (system-msg state :runner "gains 2 [Credits] and flips [their] identity to Hoshiko Shiro: Mahou Shoujo") (continue-ability state :runner {:effect flip-effect} card nil)) - :else (effect-completed state side eid)))} {:event :runner-turn-begins + :automatic :lose-credits :req (req (:flipped card)) :async true :effect (req (wait-for (draw state :runner 1) @@ -961,6 +964,7 @@ (defcard "Iain Stirling: Retired Spook" (let [ability {:req (req (> (:agenda-point corp) (:agenda-point runner))) :once :per-turn + :automatic :gain-credits :msg "gain 2 [Credits]" :async true :effect (effect (gain-credits eid 2))}] @@ -1118,6 +1122,7 @@ (defcard "Jinteki: Restoring Humanity" {:events [{:event :corp-turn-ends + :automatic :gain-credits :req (req (pos? (count (remove :seen (:discard corp))))) :msg "gain 1 [Credits]" :async true @@ -1201,6 +1206,7 @@ (defcard "Laramy Fisk: Savvy Investor" {:events [{:event :successful-run + :skippable true :async true :interactive (get-autoresolve :auto-fire (complement never?)) :silent (get-autoresolve :auto-fire never?) @@ -1249,6 +1255,7 @@ (defcard "Liza Talking Thunder: Prominent Legislator" {:events [{:event :successful-run + :automatic :draw-cards :async true :interactive (req true) :msg "draw 2 cards and take 1 tag" @@ -1275,6 +1282,7 @@ "trash the top 2 cards from the stack and draw 1 card - but the stack is empty"))) :label "trash and draw cards" :once :per-turn + :automatic :post-draw-cards :async true :effect (req (wait-for (mill state :runner :runner 2) (draw state :runner eid 1)))}] @@ -1287,6 +1295,7 @@ (defcard "Mercury: Chrome Libertador" {:events [{:event :breach-server + :automatic :pre-breach :req (req (and run (empty? (run-events state side :subroutines-broken)) (#{:hq :rd} target))) @@ -1341,10 +1350,12 @@ (continue-ability state side mm-ability (get-card state card) nil) (effect-completed state side eid))))} {:event :runner-turn-begins + :silent (req true) :effect (effect (update! (assoc-in card [:special :mm-actions] [])) (update! (assoc-in (get-card state card) [:special :mm-click] false)))} {:event :corp-turn-ends + :silent (req true) :effect (effect (update! (assoc-in card [:special :mm-actions] [])) (update! (assoc-in (get-card state card) [:special :mm-click] false)))}] @@ -1421,6 +1432,7 @@ :once :per-turn :interactive (req true) :async true + :automatic :pre-draw-cards :effect (req (if (and (> 3 (count (:hand runner))) (:runner-phase-12 @state)) (do (system-msg state :runner (str "uses " (:title card) " to gain 1 [Credits]")) @@ -1508,6 +1520,7 @@ (defcard "Nero Severn: Information Broker" {:events [{:event :encounter-ice + :skippable true :optional (:optional (offer-jack-out {:req (req (has-subtype? (:ice context) "Sentry")) :once :per-turn}))}]}) @@ -1571,6 +1584,7 @@ (defcard "Null: Whistleblower" {:events [{:event :encounter-ice + :skippable true :optional {:req (req (pos? (count (:hand runner)))) :prompt "Trash a card in the grip to lower the strength of encountered ice by 2?" @@ -1622,6 +1636,7 @@ {:events [mark-changed-event (assoc identify-mark-ability :event :runner-turn-begins) {:event :successful-run + :automatic :gain-clicks :interactive (req true) :req (req (and (:marked-server target) (first-event? state side :successful-run #(:marked-server (first %))))) @@ -1786,6 +1801,7 @@ (defcard "Pravdivost Consulting: Political Solutions" {:events [{:event :successful-run + :skippable true :req (req (first-event? state side :successful-run)) :interactive (req true) :async true @@ -1874,6 +1890,7 @@ :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))}] :events [{:event :corp-turn-begins + :silent (req true) :effect (req (clear-persistent-flag! state side card :can-rez))}]})) (defcard "Sebastião Souza Pessoa: Activist Organizer" @@ -1910,6 +1927,7 @@ (defcard "Silhouette: Stealth Operative" {:events [{:event :successful-run + :skippable true :interactive (req (some #(not (rezzed? %)) (all-installed state :corp))) :async true :req (req (and (= :hq (target-server context)) @@ -2044,6 +2062,7 @@ (defcard "Steve Cambridge: Master Grifter" {:events [{:event :successful-run + :skippable true :optional {:req (req (and (= :hq (target-server context)) (first-successful-run-on-server? state :hq) diff --git a/src/clj/game/cards/operations.clj b/src/clj/game/cards/operations.clj index c1b15dd454..e0b8d4ad9e 100644 --- a/src/clj/game/cards/operations.clj +++ b/src/clj/game/cards/operations.clj @@ -235,11 +235,12 @@ (defcard "Argus Crackdown" (lockdown - {:events [{:event :successful-run - :req (req (not-empty run-ices)) - :msg "deal 2 meat damage" - :async true - :effect (effect (damage eid :meat 2 {:card card}))}]})) + {:events [{:event :successful-run + :automatic :corp-damage + :req (req (not-empty run-ices)) + :msg "deal 2 meat damage" + :async true + :effect (effect (damage eid :meat 2 {:card card}))}]})) (defcard "Ark Lockdown" {:on-play @@ -858,6 +859,7 @@ (defcard "Door to Door" {:events [{:event :runner-turn-begins + :automatic :corp-damage :trace {:base 1 :label "Do 1 meat damage if Runner is tagged, or give the Runner 1 tag" :successful {:msg (msg (if tagged @@ -1969,6 +1971,7 @@ (defcard "Paywall Implementation" {:events [{:event :successful-run + :automatic :gain-credits :msg "gain 1 [Credits]" :async true :effect (effect (gain-credits :corp eid 1))}]}) diff --git a/src/clj/game/cards/programs.clj b/src/clj/game/cards/programs.clj index 7c231d095e..fecf174133 100644 --- a/src/clj/game/cards/programs.clj +++ b/src/clj/game/cards/programs.clj @@ -104,6 +104,7 @@ {:abilities abilities :highlight-in-discard true :events [{:event :encounter-ice + :skippable true :async true :location :discard :req (req (and (in-discard? card) @@ -270,6 +271,7 @@ (auto-icebreaker {:abilities abilities :events [{:event :encounter-ice + :skippable true :req (req (and (not-used-once? state {:once :per-turn} card) (not (has-subtype? (:ice context) ice-type)) (can-pay? state :runner eid card nil [(->c :credit 2)]))) @@ -409,6 +411,7 @@ (defcard "Afterimage" (auto-icebreaker {:events [{:event :encounter-ice + :skippable true :interactive (req true) :optional {:req (req (and (has-subtype? (:ice context) "Sentry") @@ -432,6 +435,7 @@ (defcard "Algernon" {:events [{:event :runner-turn-begins + :skippable true :optional {:prompt (msg "Pay 2 [Credits] to gain [Click]?") :req (req (can-pay? state :runner (assoc eid :source card :source-type :ability) card nil [(->c :credit 2)])) @@ -815,6 +819,7 @@ :req (req (same-card? target (:host card))) :value (req (- (get-virus-counters state card)))}] :events [{:event :encounter-ice + :automatic :pre-bypass :req (req (same-card? (:ice context) (:host card))) :async true :effect (req (if (pos? (ice-strength state side (:ice context))) @@ -959,6 +964,7 @@ (defcard "Cordyceps" {:data {:counter {:virus 2}} :events [{:event :successful-run + :skippable true :interactive (req true) :optional {:req (req (and (is-central? (target-server context)) @@ -1050,6 +1056,7 @@ :msg (msg "host " (:title target) " on itself") :effect (req (host state side card (assoc target :seen true :installed false)))} {:event :breach-server + :automatic :pre-breach :async true :optional {:req (req (and (= :hq target) (seq (filter corp? (:hosted card))))) @@ -1074,6 +1081,7 @@ (strength-pump 1 1)] :interactive (req true) :events [{:event :encounter-ice + :skippable true :optional {:prompt (msg "Spend 3 power counters to bypass " (card-str state current-ice) "?") :waiting-prompt true :req (req (and @@ -1446,6 +1454,7 @@ (register-events state side card [{:event :encounter-ice + :skippable true :interactive (req true) :optional {:req (req (and (same-card? ice (:ice context)) @@ -1520,6 +1529,7 @@ :effect (req (add-counter state side eid card :virus 2 nil))}] {:flags {:runner-phase-12 (req true)} :events [{:event :runner-turn-begins + :skippable true :interactive (req true) :optional {:prompt "Take 1 tag: Place 2 virus counters on God of War" @@ -1621,6 +1631,7 @@ (defcard "Heliamphora" {:events [{:event :breach-server + :automatic :pre-breach :async true :interactive (req true) :req (req (and (= target :archives) @@ -1935,6 +1946,7 @@ (defcard "Lamprey" {:events [{:event :successful-run + :automatic :drain-credits :req (req (= :hq (target-server context))) :msg "force the Corp to lose 1 [Credits]" :async true @@ -1947,6 +1959,7 @@ (defcard "Laser Pointer" {:events [{:event :encounter-ice + :skippable true :req (req (has-any-subtype? current-ice ["AP" "Observer" "Destroyer"])) :async true :effect (effect (continue-ability @@ -2062,6 +2075,7 @@ {:data {:counter {:power 2}} :events [(rfg-on-empty :power) {:event :encounter-ice + :skippable true :interactive (req true) :ability-name "Malandragem (rfg)" :optional {:prompt "Remove this program from the game to bypass encountered ice?" @@ -2070,6 +2084,7 @@ :msg (msg "bypass " (card-str state current-ice)) :effect (req (bypass-ice state))}}} {:event :encounter-ice + :skippable true :interactive (req true) :ability-name "Malandragem (Power counter)" :optional {:prompt "Remove 1 power counter to bypass encountered ice?" @@ -2093,6 +2108,7 @@ (break-sub [(->c :power 1)] 1) (strength-pump 2 2)] :events [{:event :runner-turn-ends + :silent (req true) :effect (effect (update! (assoc-in card [:counter :power] 0)))}]})) (defcard "Mantle" @@ -2192,6 +2208,7 @@ :async true :effect (effect (add-counter eid card :virus 1 nil))} {:event :breach-server + :automatic :pre-breach :async true :req (req (= target :rd)) :effect (effect (continue-ability @@ -2350,6 +2367,7 @@ :async true :effect (effect (add-counter eid card :virus 1 nil))} {:event :breach-server + :automatic :pre-breach :async true :req (req (= target :hq)) :effect (effect (continue-ability @@ -2386,23 +2404,24 @@ {:data {:counter {:power 3}} :events [(trash-on-empty :power) {:event :successful-run - :interactive (get-autoresolve :auto-fire (complement never?)) - :silent (get-autoresolve :auto-fire never?) - :optional - {:req (req (and (first-event? state side :successful-run) - (pos? (get-counters card :power)))) - :player :runner - :autoresolve (get-autoresolve :auto-fire) - :waiting-prompt true - :prompt "Remove 1 hosted power counter?" - :yes-ability - {:msg "remove 1 hosted power counter to sabotage 1" - :async true - :cost [(->c :power 1)] - :effect (effect (continue-ability - (sabotage-ability 1) - card nil))} - :no-ability {:effect (effect (system-msg (str "declines to use " (:title card))))}}}] + :skippable true + :interactive (get-autoresolve :auto-fire (complement never?)) + :silent (get-autoresolve :auto-fire never?) + :optional + {:req (req (and (first-event? state side :successful-run) + (pos? (get-counters card :power)))) + :player :runner + :autoresolve (get-autoresolve :auto-fire) + :waiting-prompt true + :prompt "Remove 1 hosted power counter?" + :yes-ability + {:msg "remove 1 hosted power counter to sabotage 1" + :async true + :cost [(->c :power 1)] + :effect (effect (continue-ability + (sabotage-ability 1) + card nil))} + :no-ability {:effect (effect (system-msg (str "declines to use " (:title card))))}}}] :abilities [(set-autoresolve :auto-fire "Nga")]}) (defcard "Ninja" @@ -2415,6 +2434,7 @@ (defcard "Nyashia" {:data {:counter {:power 3}} :events [{:event :breach-server + :skippable true :optional {:req (req (and (pos? (get-counters card :power)) (= target :rd))) @@ -2490,6 +2510,7 @@ (defcard "Panchatantra" {:events [{:event :encounter-ice + :skippable true :optional {:prompt "Give encountered piece ice a subtype?" :req (req (not (get-in @state [:per-turn (:cid card)]))) @@ -2704,6 +2725,7 @@ :effect (req (trash state :runner eid card {:cause :purge :cause-card card}))} {:event :encounter-ice + :skippable true :optional {:prompt (msg "Pay " (count (:subroutines (get-card state current-ice))) " [Credits] to bypass encountered ice?") :req (req (and (not (has-subtype? current-ice "Barrier")) @@ -2839,6 +2861,7 @@ (defcard "Rezeki" {:events [{:event :runner-turn-begins + :automatic :gain-credits :msg "gain 1 [Credits]" :async true :effect (effect (gain-credits eid 1))}]}) @@ -3235,6 +3258,7 @@ (defcard "Tapwrm" (let [ability {:label "Gain [Credits] (start of turn)" + :automatic :gain-credits :msg (msg "gain " (quot (:credit corp) 5) " [Credits]") :once :per-turn :req (req (:runner-phase-12 @state)) @@ -3275,6 +3299,7 @@ (make-run eid (:card-target card) card))}] :events [(assoc ability :event :runner-turn-begins) {:event :runner-turn-ends + :silent (req true) :effect (effect (update! (dissoc card :card-target)))}]})) (defcard "Tranquilizer" @@ -3419,6 +3444,7 @@ (has-subtype? % "Icebreaker")) (all-active-installed state :runner))))})]})) +;; TODO - just make this an autoresolve (defcard "Upya" {:implementation "Power counters added automatically" :events [{:event :successful-run diff --git a/src/clj/game/cards/resources.clj b/src/clj/game/cards/resources.clj index 18af95f27d..0af2482ecc 100644 --- a/src/clj/game/cards/resources.clj +++ b/src/clj/game/cards/resources.clj @@ -121,6 +121,7 @@ and requires `effect-completed`." [pay-credits-req turn-ends-ability ability] (let [place-credit {:msg "add 1 [Credits] to itself" + :automatic :gain-credits :async true :effect (req (add-counter state side eid card :credit 1))}] {:interactions {:pay-credits {:req pay-credits-req @@ -244,6 +245,7 @@ (gain-credits state side eid 3)))}] {:flags {:runner-phase-12 (req (>= (count (all-installed state :runner)) 2))} :events [(assoc ability + :skippable true :event :runner-turn-begins :interactive (req true))] :abilities [ability]})) @@ -298,6 +300,7 @@ :async true :effect (req (add-counter state side eid (get-card state card) :power 1))} {:event :runner-turn-begins + :skippable true :optional {:prompt "Trash this resource to force the Corp to lose 10 [Credits]?" :req (req (>= (get-counters (get-card state card) :power) 3)) @@ -402,18 +405,19 @@ (trigger-event state side :searched-stack) (shuffle! state side :deck) (effect-completed state side eid)))} - :events [{:event :runner-turn-begins - :label "Add a hosted card to the grip (start of turn)" - :prompt "Choose a hosted card to move to the grip" - :choices {:req (req (same-card? card (:host target)))} - :msg (msg "add " (get-title target) " to the grip") - :once :per-turn - :cancel-effect (req (system-msg state side (str "declines to use " (get-title card))) - (trash-if-empty state side eid card)) - :async true - :waiting-prompt true - :effect (req (move state side target :hand) - (trash-if-empty state side eid card))}]})) + :events [{:event :runner-turn-begins + :skippable true + :label "Add a hosted card to the grip (start of turn)" + :prompt "Choose a hosted card to move to the grip" + :choices {:req (req (same-card? card (:host target)))} + :msg (msg "add " (get-title target) " to the grip") + :once :per-turn + :cancel-effect (req (system-msg state side (str "declines to use " (get-title card))) + (trash-if-empty state side eid card)) + :async true + :waiting-prompt true + :effect (req (move state side target :hand) + (trash-if-empty state side eid card))}]})) (defcard "Assimilator" {:abilities [{:action true @@ -452,6 +456,7 @@ {:events [mark-changed-event (assoc identify-mark-ability :event :runner-turn-begins) {:event :encounter-ice + :skippable true :async true :interactive (req true) :optional @@ -466,6 +471,7 @@ (defcard "\"Baklan\" Bochkin" {:events [{:event :encounter-ice + :automatic :pre-bypass :req (req (first-run-event? state side :encounter-ice)) :msg "place 1 power counter on itself" :async true @@ -530,6 +536,7 @@ (defcard "Beach Party" {:static-abilities [(runner-hand-size+ 5)] :events [{:event :runner-turn-begins + :automatic :lose-clicks :msg "lose [Click]" :effect (effect (lose-clicks 1))}]}) @@ -553,6 +560,7 @@ (defcard "Beth Kilrain-Chang" (let [ability {:once :per-turn + :automatic :gain-clicks :label "Gain 1 [Credits], draw 1 card, or gain [Click] (start of turn)" :req (req (:runner-phase-12 @state)) :async true @@ -579,6 +587,7 @@ (defcard "Bhagat" {:events [{:event :successful-run + :automatic :force-discard :async true :req (req (and (= :hq (target-server context)) (first-successful-run-on-server? state :hq))) @@ -626,19 +635,20 @@ (defcard "Bloo Moose" (let [ability {:req (req (not (zone-locked? state :runner :discard))) - :label "Remove a card in the Heap from the game to gain 2 [Credits]" - :once :per-turn - :prompt "Choose a card in the Heap" - :show-discard true - :choices {:card #(and (in-discard? %) - (runner? %))} - :msg (msg "remove " (:title target) " from the game and gain 2 [Credits]") - :async true - :effect (effect (move target :rfg) - (gain-credits eid 2))}] + :label "Remove a card in the Heap from the game to gain 2 [Credits]" + :once :per-turn + :prompt "Choose a card in the Heap" + :show-discard true + :choices {:card #(and (in-discard? %) + (runner? %))} + :msg (msg "remove " (:title target) " from the game and gain 2 [Credits]") + :async true + :effect (effect (move target :rfg) + (gain-credits eid 2))}] {:flags {:runner-phase-12 (req (not (zone-locked? state :runner :discard)))} :events [(assoc ability :event :runner-turn-begins + :automatic :gain-credits :interactive (req true))] :abilities [ability]})) @@ -653,6 +663,7 @@ :async true :effect (req (add-counter state side eid card :power target))} :events [{:event :runner-turn-ends + :automatic :draw-cards :req (req (zero? (count (:hand runner)))) :msg (msg "draw " (quantify (get-counters card :power) "card")) :async true @@ -704,6 +715,7 @@ (register-events state :runner card [{:event :encounter-ice + :automatic :bypass :req (req (and (same-card? ice (:ice context)) (rezzed? (:ice context)))) :effect (req (bypass-ice state))}]) @@ -763,6 +775,7 @@ :msg (msg "prevent " (:remaining context) " " (damage-name state) " damage") :effect (req (prevent-damage state side eid :all))}}] :events [{:event :runner-turn-ends + :automatic :trace :interactive (req true) :msg "force the Corp to initiate a trace" :label "Trace 1 - If unsuccessful, Runner removes 1 tag" @@ -804,6 +817,7 @@ (register-events :runner card [{:event :breach-server + :automatic :pre-breach :duration :until-runner-turn-ends :req (req (#{:hq :rd} target)) :once :per-turn @@ -944,6 +958,7 @@ (defcard "Crowdfunding" (let [ability {:async true :once :per-turn + :automatic :gain-credits :label "Take 1 [Credits] (start of turn)" :msg "gain 1 [Credits]" :req (req (and (:runner-phase-12 @state) @@ -972,6 +987,7 @@ :abilities [ability] :events [(assoc ability :event :runner-turn-begins) {:event :runner-turn-ends + :skippable true :async true :location :discard :req (req (runner-can-install? state side card nil)) @@ -1041,6 +1057,7 @@ :events [(assoc (trash-effect) :event :runner-credit-loss) (assoc (trash-effect) :event :runner-spent-credits) {:event :runner-turn-begins + :automatic :gain-credits :once :per-turn :interactive (req true) :async true @@ -1054,6 +1071,7 @@ (defcard "Daily Casts" (let [ability {:once :per-turn + :automatic :gain-credits :label "Take 2 [Credits] (start of turn)" :req (req (and (:runner-phase-12 @state) (pos? (get-counters card :credit)))) @@ -1082,6 +1100,7 @@ (defcard "Data Folding" (let [ability {:label "Gain 1 [Credits] (start of turn)" + :automatic :gain-credits :msg "gain 1 [Credits]" :once :per-turn :req (req (and (<= 2 (available-mu state)) @@ -1248,6 +1267,7 @@ (defcard "Dr. Lovegood" {:events [{:event :runner-turn-begins + :skippable true :label "blank a card" :prompt "Choose an installed card to make its text box blank for the remainder of the turn" :once :per-turn @@ -1286,6 +1306,7 @@ (defcard "DreamNet" {:events [{:event :successful-run + :automatic :draw-cards :async true :req (req (first-event? state :runner :successful-run)) :msg (msg "draw 1 card" @@ -1309,12 +1330,14 @@ :async true :effect (effect (lose-credits eid 1))}] :events [{:event :corp-turn-begins + :automatic :draw-cards :msg (msg "draw " (if (zero? (count (get-in @state [:runner :deck]))) "no cards (the stack is empty)" "1 card")) :async true :effect (effect (draw :runner eid 1))} {:event :runner-turn-begins + :automatic :lose-credits :msg (msg "lose " (if (zero? (get-in @state [:runner :credit])) "0 [Credits] (Runner has no credits to lose)" "1 [Credits]")) @@ -1338,6 +1361,7 @@ (defcard "Earthrise Hotel" (let [ability {:msg "draw 2 cards" + :automatic :draw-cards :once :per-turn :req (req (:runner-phase-12 @state)) :async true @@ -1360,6 +1384,7 @@ (defcard "Emptied Mind" (let [ability {:req (req (zero? (count (:hand runner)))) + :automatic :gain-clicks :msg "gain [Click]" :label "Gain [Click] (start of turn)" :once :per-turn @@ -1405,6 +1430,7 @@ (defcard "Eru Ayase-Pessoa" (let [constant-effect {:event :breach-server + :automatic :pre-breach :req (req (and (threat-level 3 state) (= :rd target) (= :archives (first (:server run))))) @@ -1615,10 +1641,12 @@ :abilities [ability (set-autoresolve :auto-fire "Globalsec Security Clearance")] :events [(assoc ability :event :runner-turn-begins + :skippable true :interactive (req true))]})) (defcard "Grifter" {:events [{:event :runner-turn-ends + :automatic :gain-credits :async true :effect (req (let [ab (if (:successful-run runner-reg) {:msg "gain 1 [Credits]" @@ -1705,6 +1733,7 @@ (defcard "Hard at Work" (let [ability {:msg "gain 2 [Credits] and lose [Click]" + :automatic :lose-clicks :once :per-turn :async true :effect (effect (lose-clicks 1) @@ -1818,6 +1847,7 @@ (defcard "Jackpot!" {:implementation "Credit gain must be manually triggered" :events [{:event :runner-turn-begins + :silent (req true) :async true :effect (effect (add-counter :runner eid card :credit 1))} ;; TODO (NoahTheDuke, Oct 2020): @@ -1853,6 +1883,7 @@ :flags {:runner-phase-12 (req true)} :install-cost-bonus (req (- (get-link state))) :events [{:event :runner-turn-begins + :skippable true :interactive (req true) :optional {:once :per-turn @@ -1877,6 +1908,7 @@ (defcard "John Masanori" {:events [{:event :successful-run + :automatic :draw-cards :req (req (first-event? state side :successful-run)) :interactive (req true) :msg "draw 1 card" @@ -1896,6 +1928,7 @@ (update! (assoc-in card [:special :joshua-b] true)))}] {:flags {:runner-phase-12 (req true)} :events [{:event :runner-turn-begins + :skippable true :optional {:prompt "Gain [Click]?" :once :per-turn :yes-ability ability @@ -2158,6 +2191,7 @@ (threat-level 3 state))) :value [(->c :trash-from-hand 1)]}] :events [{:event :breach-server + :automatic :pre-breach :req (req (and tagged (or (= target :rd) (= target :hq)))) :msg (msg "access 1 additional card from " (zone->name target)) @@ -2166,6 +2200,7 @@ (defcard "\"Pretty\" Mary da Silva" {:implementation "only works after other abilities increasing the number of accesses have resolved" :events [{:event :breach-server + :automatic :last :async true :interactive (req true) :req (req (and (= :rd target) @@ -2568,6 +2603,7 @@ (let [ability {:prompt "Choose a server" :label "Choose a server (start of turn)" :choices (req (concat servers ["No server"])) + :skippable true :once :per-turn :req (req (and (:runner-phase-12 @state) (not (used-this-turn? (:cid card) state)))) @@ -2817,6 +2853,7 @@ {:data {:counter {:credit 12}} :events [(trash-on-empty :credit) {:event :successful-run + :automatic :gain-credits :req (req this-card-run) :msg (msg "gain " (min 3 (get-counters card :credit)) " [Credits]") :interactive (req true) @@ -2942,6 +2979,7 @@ (defcard "Safety First" {:static-abilities [(runner-hand-size+ -2)] :events [{:event :runner-turn-ends + :automatic :pre-draw-cards :async true :effect (req (if (< (count (:hand runner)) (hand-size state :runner)) (do (system-msg state :runner (str "uses " (:title card) " to draw 1 card")) @@ -3006,6 +3044,7 @@ (defcard "Security Testing" (let [ability {:prompt "Choose a server" :label "Choose a server (start of turn)" + :skippable true :choices (req (concat servers ["No server"])) :interactive (req true) :msg (msg "target " target) @@ -3035,6 +3074,7 @@ (defcard "Smartware Distributor" (let [start-of-turn-ability {:once :per-turn + :automatic :gain-credits :label "Take 1 [Credits] (start of turn)" :req (req (and (:runner-phase-12 @state) (pos? (get-counters card :credit)))) @@ -3097,6 +3137,7 @@ {:on-install {:msg "ignore additional costs on Double events" :effect (req (swap! state assoc-in [:runner :register :double-ignore-additional] true))} :events [{:event :runner-turn-begins + :automatic :lose-clicks :msg "lose [Click] and ignore additional costs on Double events" :effect (req (lose-clicks state :runner 1) (swap! state assoc-in [:runner :register :double-ignore-additional] true))}] @@ -3278,6 +3319,7 @@ :effect (effect (update! (assoc card :card-target target)))} :events [(trash-on-empty :credit) {:event :successful-run + :automatic :gain-credits :req (req (= (zone->name (:server context)) (:card-target (get-card state card)))) :msg (msg "gain " (min 4 (get-counters card :credit)) " [Credits]") :async true @@ -3378,6 +3420,7 @@ (defcard "The Class Act" (let [draw-ability {:req (req (= :this-turn (installed? card))) :async true + :automatic :pre-draw-cards ;; queue this before smaller draws :msg "draw 4 cards" :effect (effect (draw :runner eid 4))}] {:events [(assoc draw-ability :event :corp-turn-ends) @@ -3431,6 +3474,7 @@ (register-events card [{:event :successful-run + :automatic :draw-cards :unregister-once-resolved true :duration :end-of-run :async true @@ -3465,6 +3509,7 @@ (draw state :runner eid 2) (mill state :corp eid :corp 1)))} maybe-spend-2 {:event :runner-turn-begins + :skippable true :interactive (req true) :optional {:prompt "Spend 2 virus counters?" @@ -3511,6 +3556,7 @@ (defcard "The Supplier" (let [ability {:label "Install a hosted card (start of turn)" + :skippable true :prompt "Choose a hosted card to install" :req (req (some #(can-pay? state side (assoc eid :source card :source-type :runner-install) % nil [(->c :credit (install-cost state side % {:cost-bonus -2}))]) @@ -3552,6 +3598,7 @@ :req (req (= (:cid target) (:supplier-installed (get-card state card))))}] :events [(assoc ability :event :runner-turn-begins) {:event :runner-turn-ends + :silent (req true) :req (req (:supplier-installed card)) :effect (effect (update! (dissoc card :supplier-installed)))}]})) @@ -3595,6 +3642,7 @@ :msg "place a power counter on itself" :effect (req (add-counter state :runner eid card :power 1 {:placed true}))} {:event :breach-server + :automatic :pre-breach :async true :req (req (and (#{:rd :hq} target) (< 0 (get-counters card :power)))) @@ -3697,6 +3745,7 @@ (zone->name (second (get-zone target))))) ability {:prompt "Choose a server" :label "Choose a server (start of turn)" + :skippable true :choices (req (concat servers ["No server"])) :interactive (req true) :waiting-prompt true @@ -3741,6 +3790,7 @@ (defcard "Underworld Contact" (let [ability {:label "Gain 1 [Credits] (start of turn)" :once :per-turn + :automatic :gain-credits :async true :effect (req (if (and (<= 2 (get-link state)) (:runner-phase-12 @state)) @@ -3772,6 +3822,7 @@ :type :credit}} :flags {:runner-phase-12 (req (some is-eligible? (all-installed-runner state)))} :events [(assoc ability + :skippable true :event :runner-turn-begins :interactive (req true))] :abilities [ability]})) @@ -3842,6 +3893,7 @@ (defcard "Whistleblower" {:events [{:event :successful-run + :skippable true :optional {:autoresolve (get-autoresolve :auto-fire) :prompt "Name an agenda?" @@ -3903,6 +3955,7 @@ (cons (get-in @state [:runner :identity]) (all-active-installed state :runner))))))} :events [{:event :runner-turn-begins + :automatic :lose-clicks :async true :effect (req (lose-clicks state side 1) (if (get-in @state [:per-turn (:cid card)]) @@ -3923,6 +3976,7 @@ (trash-when-tagged "Zona Sul Shipping" {:events [{:event :runner-turn-begins + :automatic :gain-credits :async true :effect (effect (add-counter eid card :credit 1))}] :abilities [{:action true diff --git a/src/clj/game/cards/upgrades.clj b/src/clj/game/cards/upgrades.clj index f4b917505b..d7af85c8b8 100644 --- a/src/clj/game/cards/upgrades.clj +++ b/src/clj/game/cards/upgrades.clj @@ -63,6 +63,7 @@ ([ev] (mobile-sysop-event ev nil)) ([ev callback] {:event ev + :skippable true :optional {:prompt (msg "Move " (:title card) " to another server?") :waiting-prompt true @@ -485,6 +486,7 @@ :req (req (= (:server (second targets)) (unknown->kw (get-zone card)))) :value (req (repeat (get-counters card :power) [(->c :credit 1) (->c :click 1)]))}] :events [{:event :corp-turn-begins + :automatic :last ;; for warm reception shenanigans :req (req (pos? (get-counters card :power))) :msg "remove all hosted power counters" :async true @@ -704,6 +706,7 @@ (get-in corp (get-zone card)))) :msg "gain 1 [Credits]" :once :per-turn + :automatic :gain-credits :label "Gain 1 [Credits] (start of turn)" :async true :effect (effect (gain-credits eid 1))}] @@ -874,6 +877,7 @@ (defcard "Hokusai Grid" {:events [{:event :successful-run + :automatic :corp-damage :req (req this-server) :msg "do 1 net damage" :async true @@ -1368,6 +1372,7 @@ (defcard "Nihongai Grid" {:events [{:event :successful-run :interactive (req true) + :skippable true :optional {:req (req (and this-server (or (< (total-available-credits state :runner eid card) 6) @@ -1670,12 +1675,15 @@ (not (same-card? target card)) (some #(and (not (rezzed? %)) (not (agenda? %)) + (corp? %) (can-pay-to-rez? state side (assoc eid :source card) % {:cost-bonus -2})) (all-installed state :corp))))) :prompt "Rez another card paying 2 [Credits] less?" :yes-ability {:prompt "Choose a card to rez" :choices {:req (req (and (not (rezzed? target)) (not (agenda? target)) + (corp? target) + (installed? target) (can-pay-to-rez? state side (assoc eid :source card) target {:cost-bonus -2})))} :msg (msg "rez " (:title target) ", lowering the rez cost by 2 [Credits]") :async true diff --git a/src/clj/game/core/diffs.clj b/src/clj/game/core/diffs.clj index b50fb12d3a..8ba7117f24 100644 --- a/src/clj/game/core/diffs.clj +++ b/src/clj/game/core/diffs.clj @@ -203,6 +203,7 @@ :card :prompt-type :show-discard + :selectable ;; traces :player :base diff --git a/src/clj/game/core/engine.clj b/src/clj/game/core/engine.clj index 9d9e06db2d..ea81cbab9a 100644 --- a/src/clj/game/core/engine.clj +++ b/src/clj/game/core/engine.clj @@ -604,6 +604,33 @@ (swap! state assoc :events (remove-once #(= uuid (:uuid %)) (:events @state)))) ;; triggering events +(defn- handler-skippable? + "This handler is safe to completely skip (as if the player declined)" + [handler] + (or (->> handler :handler :ability :skippable) + (->> handler :ability :skippable))) + +(def automatic-priority + {:pre-bypass 1 ;; chisel, baklan, etc, should fire before bypass + :corp-damage 1 + :force-discard 1 + :lose-clicks 1 + :gain-clicks 2 + :drain-credits 4 + :bypass 4 + :lose-credits 4 + :pre-gain-credits 5 ;; bladderwort, etc + :gain-credits 6 + :pre-draw-cards 7 + :draw-cards 8 + :post-draw-cards 9 + :pre-breach 9 + true 10 + :trace 11 + :corp-lose-tag 11 + :last 999 ;; pretty mary wants to trigger last always + }) + (defn- get-side [ability] (to-keyword (get-in ability [:card :side]))) @@ -738,7 +765,7 @@ (should-continue [state handlers] (and (< 1 (count handlers)) (not (and cancel-fn (cancel-fn state))))) - (choose-handler [handlers] + (choose-handler [handlers done?] (let [handlers (when-not (and cancel-fn (cancel-fn state)) (filter #(and (card-for-ability state %) (not (:disabled (card-for-ability state %)))) @@ -754,6 +781,7 @@ (assoc card :title abi-name) card)) non-silent) + titles (concat titles ["Done"]) interactive (filter #(let [interactive-fn (:interactive (:ability %)) card (card-for-ability state %)] (and interactive-fn @@ -782,36 +810,53 @@ the-card event-targets) (if (should-continue state handlers) (continue-ability state side - (choose-handler remaining-handlers) nil event-targets) + (choose-handler remaining-handlers done?) + nil event-targets) (effect-completed state side eid)))))} {:async true :effect (req (if (should-continue state handlers) - (continue-ability state side (choose-handler (rest handlers)) nil event-targets) + (continue-ability state side (choose-handler (rest handlers) done?) nil event-targets) (effect-completed state side eid)))})) {:prompt "Choose a trigger to resolve" :choices titles :async true - :effect (req (let [same-card-ability? (fn [chosen-card {:keys [ability card]}] - (if-let [abi-name (:ability-name ability)] - (= abi-name (:title chosen-card)) - (same-card? chosen-card card))) - to-resolve (some #(when (same-card-ability? target %) %) handlers) - ability-to-resolve (dissoc-req (:ability to-resolve)) - the-card (card-for-ability state to-resolve) - new-eid (make-eid state (assoc eid :source the-card :source-type :ability))] - (when (:unregister-once-resolved to-resolve) - (unregister-event-by-uuid state side (:uuid to-resolve))) - (wait-for - (resolve-ability state (to-keyword (:side the-card)) - new-eid - ability-to-resolve the-card event-targets) - (if (should-continue state handlers) - (continue-ability state side - (choose-handler - (remove-once #(same-card-ability? target %) handlers)) - nil event-targets) - (effect-completed state side eid)))))})))] - (continue-ability state side (choose-handler handlers) nil event-targets)) + :effect (req + (if (= target "Done") + (do + (doseq [{:keys [handler]} (filter handler-skippable? handlers)] + (when (:unregister-once-resolved handler) + (unregister-event-by-uuid state side (:uuid handler))) + (register-once state nil (:ability handler) (:card handler))) + (let [auto-handlers (->> (filter (complement handler-skippable?) handlers) + (sort-by #(or (->> % :card :printed-title) "")) + (sort-by #(get automatic-priority (->> % :ability :automatic) 10))) + auto-handlers (map #(update-in % [:ability] merge {:silent (req true) :interactive nil}) auto-handlers)] + (if (seq auto-handlers) + (continue-ability + state side + (choose-handler auto-handlers true) + card nil) + (effect-completed state side eid)))) + (let [same-card-ability? (fn [chosen-card {:keys [ability card]}] + (if-let [abi-name (:ability-name ability)] + (= abi-name (:title chosen-card)) + (same-card? chosen-card card))) + to-resolve (some #(when (same-card-ability? target %) %) handlers) + ability-to-resolve (dissoc-req (:ability to-resolve)) + the-card (card-for-ability state to-resolve) + new-eid (make-eid state (assoc eid :source the-card :source-type :ability))] + (when (:unregister-once-resolved to-resolve) + (unregister-event-by-uuid state side (:uuid to-resolve))) + (wait-for + (resolve-ability state (to-keyword (:side the-card)) + new-eid + ability-to-resolve the-card event-targets) + (if (should-continue state handlers) + (continue-ability state side + (choose-handler (remove-once #(same-card-ability? target %) handlers) done?) + nil event-targets) + (effect-completed state side eid))))))})))] + (continue-ability state side (choose-handler handlers nil) nil event-targets)) (effect-completed state side eid))) (defn ability-as-handler @@ -937,7 +982,7 @@ choices-map (map #(vector (or (:ability-name (:ability (:handler %))) (card-for-ability state (:handler %))) %) cards-with-titles) - choices-titles (map first choices-map) + choices-titles (concat (map first choices-map) ["Done"]) interactive (filter #(let [interactive-fn (:interactive (:ability (:handler %)))] (and interactive-fn (interactive-fn state side @@ -975,24 +1020,36 @@ {:async true :prompt "Choose a trigger to resolve" :choices choices-titles - :effect (req (let [choice-target (first (filter #(or (= target (first %)) - (same-card? target (first %))) choices-map)) - handler (second choice-target) - to-resolve (:handler handler) - ability (:ability to-resolve) - context (:context handler) - ability-card (card-for-ability state to-resolve) - new-eid (make-eid state (assoc eid :source ability-card :source-type :ability))] - (when (:unregister-once-resolved to-resolve) - (unregister-event-by-uuid state side (:uuid to-resolve))) - (wait-for - (resolve-ability state (to-keyword (:side ability-card)) - new-eid - (dissoc-req ability) - ability-card - context) - (let [remaining-handlers (remove-once #(= handler %) handlers)] - (trigger-queued-event-player state side eid remaining-handlers args)))))}) + :effect (req (if (= target "Done") + (do (doseq [{:keys [handler]} (filter handler-skippable? handlers)] + (when (:unregister-once-resolved handler) + (unregister-event-by-uuid state side (:uuid handler))) + (register-once state nil (:ability handler) (:card handler))) + (let [auto-handlers (->> (filter (complement handler-skippable?) handlers) + (sort-by #(or (->> % :handler :card :printed-title) "")) + (sort-by #(get automatic-priority (->> % :handler :ability :automatic) 10))) + auto-handlers (map #(update-in % [:handler :ability] merge {:silent (req true) :interactive nil}) auto-handlers)] + (if (seq auto-handlers) + (trigger-queued-event-player state side eid auto-handlers args) + (effect-completed state side eid)))) + (let [choice-target (first (filter #(or (= target (first %)) + (same-card? target (first %))) choices-map)) + handler (second choice-target) + to-resolve (:handler handler) + ability (:ability to-resolve) + context (:context handler) + ability-card (card-for-ability state to-resolve) + new-eid (make-eid state (assoc eid :source ability-card :source-type :ability))] + (when (:unregister-once-resolved to-resolve) + (unregister-event-by-uuid state side (:uuid to-resolve))) + (wait-for + (resolve-ability state (to-keyword (:side ability-card)) + new-eid + (dissoc-req ability) + ability-card + context) + (let [remaining-handlers (remove-once #(= handler %) handlers)] + (trigger-queued-event-player state side eid remaining-handlers args))))))}) nil nil))))) (defn- is-player diff --git a/src/clj/game/core/prompts.clj b/src/clj/game/core/prompts.clj index 64b5614989..a9c1649b1a 100644 --- a/src/clj/game/core/prompts.clj +++ b/src/clj/game/core/prompts.clj @@ -1,12 +1,13 @@ (ns game.core.prompts (:require - [clj-uuid :as uuid] - [game.core.eid :refer [effect-completed make-eid]] - [game.core.prompt-state :refer [add-to-prompt-queue remove-from-prompt-queue]] - [game.core.toasts :refer [toast]] - [game.macros :refer [when-let*]] - [game.utils :refer [pluralize side-str]] - [medley.core :refer [find-first]])) + [clj-uuid :as uuid] + [game.core.board :refer [get-all-cards]] + [game.core.eid :refer [effect-completed make-eid]] + [game.core.prompt-state :refer [add-to-prompt-queue remove-from-prompt-queue]] + [game.core.toasts :refer [toast]] + [game.macros :refer [when-let*]] + [game.utils :refer [pluralize side-str]] + [medley.core :refer [find-first]])) (defn choice-parser [choices] @@ -19,21 +20,29 @@ :uuid (uuid/v4) :idx idx})))) +(defn update-selectable + [prev-selectable choices] + (if (or (not choices) (string? choices) (keyword? choices) (not (sequential? choices))) + prev-selectable + (concat (or prev-selectable []) (filterv identity (map #(->> % :value :cid) choices))))) + (defn show-prompt "Engine-private method for displaying a prompt where a *function*, not a card ability, is invoked when the prompt is resolved. All prompts flow through this method." ([state side card message choices f] (show-prompt state side (make-eid state) card message choices f nil)) ([state side card message choices f args] (show-prompt state side (make-eid state) card message choices f args)) ([state side eid card message choices f - {:keys [waiting-prompt prompt-type show-discard cancel-effect end-effect targets]}] + {:keys [waiting-prompt prompt-type show-discard cancel-effect end-effect targets selectable]}] (let [prompt (if (string? message) message (message state side eid card targets)) choices (choice-parser choices) + selectable (update-selectable selectable choices) newitem ^:ignore-async-check {:eid eid :msg prompt :choices choices :effect f :card card + :selectable selectable :prompt-type (or prompt-type :other) :show-discard show-discard :cancel-effect cancel-effect @@ -49,7 +58,7 @@ {:eid (select-keys eid [:eid]) :card card :prompt-type :waiting - :msg (str "Waiting for " + :msg (str "Waiting for " (if (true? waiting-prompt) (str (side-str side) " to make a decision") waiting-prompt))})) @@ -116,6 +125,13 @@ (cancel-effect nil) (effect-completed state side (:eid (:ability selected))))))) +(defn- compute-selectable + [state side card ability req-fn card-fn] + (let [valid (filter #(not= (:zone %) [:deck]) (get-all-cards state)) + valid (filter #(or (= nil card-fn) (card-fn %)) valid) + valid (if (nil? req-fn) valid (filter #(req-fn state side (make-eid state) card [%]) valid))] + (map :cid valid))) + (defn show-select "A select prompt uses a targeting cursor so the user can click their desired target of the ability. The preferred method for showing a select prompt is through resolve-ability." @@ -129,6 +145,7 @@ ability (update-in ability [:choices :min] #(if (fn? %) (% state side (make-eid state) card targets) %)) all (get-in ability [:choices :all]) ability (if all (update-in ability [:choices] dissoc :min) ability) ; ignore :min if :all is set + selectable-cards (compute-selectable state side card ability (get-in ability [:choices :req]) (get-in ability [:choices :card])) min-choices (get-in ability [:choices :min]) max-choices (get-in ability [:choices :max])] (swap! state update-in [side :selected] @@ -180,6 +197,7 @@ update! resolve-ability))))) (-> args (assoc :prompt-type :select + :selectable selectable-cards :show-discard (:show-discard ability)) (wrap-function :cancel-effect))))))) diff --git a/src/clj/game/core/rezzing.clj b/src/clj/game/core/rezzing.clj index 5736fe0b45..45d07ecd1c 100644 --- a/src/clj/game/core/rezzing.clj +++ b/src/clj/game/core/rezzing.clj @@ -99,7 +99,7 @@ ([state side eid card args] (let [eid (assoc eid :source-type :rez) card (get-card state card) - costs (get-rez-cost state side card args) + costs (or (get-rez-cost state side card args) 0) alternative-cost (when (and card (not (is-disabled? state side card))) (:alternative-cost (card-def card)))] diff --git a/src/clj/game/core/runs.clj b/src/clj/game/core/runs.clj index e2d0512ebf..398c35b92e 100644 --- a/src/clj/game/core/runs.clj +++ b/src/clj/game/core/runs.clj @@ -589,7 +589,7 @@ (defn choose-replacement-ability [state handlers] (let [mandatory (some :mandatory handlers) - titles (into [] (keep #(get-in % [:card :title]) handlers)) + titles (into [] (keep #(:card %) handlers)) eid (make-phase-eid state nil)] (cond ;; If you can't access, there's nothing to replace @@ -599,8 +599,7 @@ (zero? (count titles)) (wait-for (breach-server state :runner (make-eid state) (get-in @state [:run :server])) (handle-end-run state :runner eid)) - ;; If there's only 1 handler and it's mandatory - ;; just execute it + ;; If there's only 1 handler and it's mandatory, just execute it (and mandatory (= 1 (count titles))) (let [chosen (first handlers) ability (:ability chosen) @@ -608,14 +607,14 @@ (system-msg state :runner (str "uses the replacement effect from " (:title card))) (wait-for (resolve-ability state :runner ability card [(select-keys (:run @state) [:server :run-id])]) (handle-end-run state :runner eid))) - ;; there are multiple handlers + ;; there are multiple handlers or the handler is optional (pos? (count titles)) (resolve-ability state :runner {:prompt "Choose a breach replacement ability" :choices (if mandatory titles (conj titles (str "Breach " (zone->name (:server (:run @state)))))) :async true - :effect (req (let [chosen (some #(when (= target (get-in % [:card :title])) %) handlers) + :effect (req (let [chosen (some #(when (same-card? target (:card %)) %) handlers) ability (:ability chosen) card (:card chosen)] (if chosen diff --git a/src/cljs/nr/gameboard/board.cljs b/src/cljs/nr/gameboard/board.cljs index 35f7fe85e0..9925b1c49a 100644 --- a/src/cljs/nr/gameboard/board.cljs +++ b/src/cljs/nr/gameboard/board.cljs @@ -170,6 +170,15 @@ (:poison card) (:highlight-in-discard card)))) +(defn- prompt-button-from-card? + [clicked-card {:keys [card msg prompt-type choices] :as prompt-state}] + (when-not (or (some #{:counter :card-title :number} choices) + (= choices "credit") + (= prompt-type "trace")) + (some (fn [{:keys [_ uuid value]}] + (when (= (:cid value) (:cid clicked-card)) uuid)) + choices))) + (defn handle-card-click [{:keys [type zone] :as card} shift-key-held] (let [side (:side @game-state)] (when (not-spectator?) @@ -178,6 +187,10 @@ (= (get-in @game-state [side :prompt-state :prompt-type]) "select") (send-command "select" {:card (card-for-click card) :shift-key-held shift-key-held}) + ;; A selectable card is clicked outside of a select prompt (ie it's a button on a choices prompt) + (contains? (into #{} (get-in @game-state [side :prompt-state :selectable])) (:cid card)) + (send-command "choice" {:choice {:uuid (prompt-button-from-card? card (get-in @game-state [side :prompt-state]))}}) + ;; Card is an identity of player's side (and (= (:type card) "Identity") (= side (keyword (lower-case (:side card))))) @@ -699,10 +712,12 @@ subtype-target corp-abilities] :as card} flipped disable-click] (let [title (get-title card)] - [:div.card-frame.menu-container - [:div.blue-shade.card {:class (str (cond selected "selected" - (same-card? card (:button @app-state)) "hovered" - (same-card? card (-> @game-state :encounters :ice)) "encountered" + (r/with-let [prompt-state (r/cursor game-state [(keyword (lower-case side)) :prompt-state])] + [:div.card-frame.menu-container + [:div.blue-shade.card {:class (str (cond selected "selected" + (contains? (into #{} (get-in @prompt-state [:selectable])) (:cid card)) "selectable" + (same-card? card (:button @app-state)) "hovered" + (same-card? card (-> @game-state :encounters :ice)) "encountered" (and (not (any-prompt-open? side)) (playable? card)) "playable" ghost "ghost" (graveyard-highlight-card? card) "graveyard-highlight" @@ -791,7 +806,7 @@ (for [card hosted] (let [flipped (draw-facedown? card)] ^{:key (:cid card)} - [card-view card flipped]))))])])) + [card-view card flipped]))))])]))) (defn show-distinct-cards [distinct-cards] @@ -1764,6 +1779,7 @@ :else (doall (for [{:keys [idx uuid value]} choices :when (not= value "Hide")] + ;; HERE [:button {:key idx :on-click #(do (send-command "choice" {:choice {:uuid uuid}}) (card-highlight-mouse-out % value button-channel)) diff --git a/src/css/gameboard.styl b/src/css/gameboard.styl index 16cb2f053d..ad75bae481 100644 --- a/src/css/gameboard.styl +++ b/src/css/gameboard.styl @@ -126,6 +126,10 @@ box-shadow(0 0 1px 2px gold-base) animation: none + .card.selectable + box-shadow(0 0 1px 2px gold-base) + animation: none + .card.ghost filter: opacity(0.65) grayscale(80%); box-shadow(0 0 1px 2px blue-light) diff --git a/test/clj/game/cards/agendas_test.clj b/test/clj/game/cards/agendas_test.clj index 80295730bd..af6f62d282 100644 --- a/test/clj/game/cards/agendas_test.clj +++ b/test/clj/game/cards/agendas_test.clj @@ -3901,7 +3901,7 @@ (take-credits state :runner) (play-and-score state "SDS Drone Deployment") (is (= "Choose a trigger to resolve" (:msg (prompt-map :corp)))) - (is (= #{"SDS Drone Deployment" "Amani Senai" "Team Sponsorship"} (into #{} (map :title (prompt-buttons :corp))))) + (is (= #{"SDS Drone Deployment" "Amani Senai" "Team Sponsorship" "Done"} (into #{} (prompt-titles :corp)))) (click-prompt state :corp "SDS Drone Deployment") (click-card state :corp "Cache") (click-prompt state :corp "Amani Senai") diff --git a/test/clj/game/cards/assets_test.clj b/test/clj/game/cards/assets_test.clj index 48af43a9af..6ee366a5f9 100644 --- a/test/clj/game/cards/assets_test.clj +++ b/test/clj/game/cards/assets_test.clj @@ -906,7 +906,7 @@ (take-credits state :runner) (is (:corp-phase-12 @state) "Corp is in Step 1.2") (end-phase-12 state :corp) - (is (= ["Clearinghouse" "Clearinghouse"] (prompt-titles :corp))))) + (is (= ["Clearinghouse" "Clearinghouse" "Done"] (prompt-titles :corp))))) (deftest clone-suffrage-movement ;; Clone Suffrage Movement @@ -1451,7 +1451,7 @@ (click-prompt state :corp "Carry on!") (is (= ["Enigma" "None"] (prompt-buttons :corp))) (click-prompt state :corp "Enigma") - (is (= ["Daily Business Show" "Jinja City Grid"] (prompt-titles :corp))) + (is (= ["Daily Business Show" "Jinja City Grid" "Done"] (prompt-titles :corp))) (click-prompt state :corp "Jinja City Grid") (is (= ["Ice Wall" "None"] (prompt-buttons :corp))) (click-prompt state :corp "Ice Wall") @@ -4547,7 +4547,7 @@ (take-credits state :runner) (is (:corp-phase-12 @state) "Corp is in Step 1.2") (end-phase-12 state :corp) - (is (= 2 (-> (prompt-map :corp) :choices count)) "Corp should have two abilities to trigger") + (is (= 3 (-> (prompt-map :corp) :choices count)) "Corp should have two abilities to trigger (+ Done)") (click-prompt state :corp "Marilyn Campaign") (click-prompt state :corp "Shuffle Marilyn Campaign into R&D") (is (find-card "Marilyn Campaign" (:deck (get-corp)))) @@ -4601,7 +4601,7 @@ (take-credits state :runner) (is (:corp-phase-12 @state) "Corp is in Step 1.2") (end-phase-12 state :corp) - (is (= ["Reaper Function" "Reaper Function"] (prompt-titles :corp))))) + (is (= ["Reaper Function" "Reaper Function" "Done"] (prompt-titles :corp))))) (deftest reconstruction-contract ;; Reconstruction Contract - place advancement token when runner takes meat damage @@ -6210,7 +6210,7 @@ (rez state :corp (get-content state :remote2 0)) (take-credits state :corp) (take-credits state :runner) - (is (= ["Urban Renewal" "Urban Renewal"] (prompt-titles :corp))))) + (is (= ["Urban Renewal" "Urban Renewal" "Done"] (prompt-titles :corp))))) (deftest urtica-cipher ;; Urtica Cipher diff --git a/test/clj/game/cards/events_test.clj b/test/clj/game/cards/events_test.clj index e94547c9a6..2ac2565fca 100644 --- a/test/clj/game/cards/events_test.clj +++ b/test/clj/game/cards/events_test.clj @@ -924,7 +924,7 @@ (play-from-hand state :runner "Film Critic") (is (= 1 (count (:discard (get-runner)))) "By Any Means has been played") (run-empty-server state "HQ") - (is (= #{"Film Critic" "By Any Means"} + (is (= #{"Film Critic" "By Any Means" "Done"} (into #{} (prompt-titles :runner))) "A choice of which to trigger first") (click-prompt state :runner "Film Critic") @@ -936,7 +936,7 @@ (core/move state :runner (find-card "By Any Means" (:discard (get-runner))) :hand) (play-from-hand state :runner "By Any Means") (run-empty-server state "HQ") - (is (= #{"Film Critic" "By Any Means"} + (is (= #{"Film Critic" "By Any Means" "Done"} (into #{} (prompt-titles :runner))) "A choice of which to trigger first") (click-prompt state :runner "By Any Means") @@ -1209,7 +1209,7 @@ (take-credits state :corp) (play-from-hand state :runner "Code Siphon") (run-continue-until state :success) - (is (= ["Code Siphon" "Breach R&D"] (prompt-buttons :runner)) + (is (= ["Code Siphon" "Breach R&D"] (prompt-titles :runner)) "Replacement effect isn't mandatory") (click-prompt state :runner "Code Siphon") (let [credits (:credit (get-runner))] @@ -5134,7 +5134,7 @@ (trash-from-hand state :runner "Out of the Ashes") (take-credits state :runner) (take-credits state :corp) - (is (= `("Rezeki" "Data Folding" "Out of the Ashes") (prompt-titles :runner)) "Out of the Ashes reduced to a single choice") + (is (= `("Rezeki" "Data Folding" "Out of the Ashes" "Done") (prompt-titles :runner)) "Out of the Ashes reduced to a single choice") (click-prompt state :runner "Rezeki") (click-prompt state :runner "Out of the Ashes") (click-prompt state :runner "Yes") diff --git a/test/clj/game/cards/hardware_test.clj b/test/clj/game/cards/hardware_test.clj index b61643d0e6..000b022fd8 100644 --- a/test/clj/game/cards/hardware_test.clj +++ b/test/clj/game/cards/hardware_test.clj @@ -646,7 +646,7 @@ (card-ability state :runner (get-hardware state 1) 0) (click-prompt state :runner "End the run") (run-continue-until state :success) - (is (= ["Boomerang" "Virtuoso"] (prompt-titles :runner)) "Boomerang only shows up once!") + (is (= ["Boomerang" "Virtuoso" "Done"] (prompt-titles :runner)) "Boomerang only shows up once!") (click-prompt state :runner "Virtuoso") (click-prompt state :runner "No action") (click-prompt state :runner "Yes") @@ -1277,7 +1277,7 @@ (damage state :runner :meat 1))] (testing "Buttons are displayed correctly" (do-game state - (is (= ["Buffer Drive" "I've Had Worse"] (sort (prompt-titles :runner)))))) + (is (= ["Buffer Drive" "Done" "I've Had Worse"] (sort (prompt-titles :runner)))))) (testing "Choosing I've Had Worse" (do-game state (is (changed? [(count (:hand (get-runner))) 3] diff --git a/test/clj/game/cards/identities_test.clj b/test/clj/game/cards/identities_test.clj index 3ff3ff5e0c..8f682c18a7 100644 --- a/test/clj/game/cards/identities_test.clj +++ b/test/clj/game/cards/identities_test.clj @@ -1447,7 +1447,7 @@ (card-ability state :runner (get-hardware state 0) 0) (click-card state :runner (find-card "Customized Secretary" (:discard (get-runner)))) ;; Make sure the simultaneous-resolution prompt is showing with 2 choices - (is (= 2 (count (prompt-buttons :runner))) "Simultaneous-resolution prompt is showing") + (is (= 3 (count (prompt-buttons :runner))) "Simultaneous-resolution prompt is showing") (click-prompt state :runner "Exile: Streethawk") (is (= 1 (count (:hand (get-runner)))) "Exile drew a card"))) @@ -3090,7 +3090,7 @@ (take-credits state :runner) (score-agenda state :corp (get-content state :remote1 0)) ;; Simultaneous prompt: Leela or Gang Sign - (is (= ["Leela Patel: Trained Pragmatist" "Gang Sign"] (map :title (prompt-buttons :runner)))) + (is (= ["Leela Patel: Trained Pragmatist" "Gang Sign" "Done"] (prompt-titles :runner))) (click-prompt state :runner "Gang Sign") (click-prompt state :runner "Steal") (click-card state :runner (get-content state :remote2 0)) ; Bounce from Gang Sign steal diff --git a/test/clj/game/cards/resources_test.clj b/test/clj/game/cards/resources_test.clj index 18a202f6dc..6a81a90e9d 100644 --- a/test/clj/game/cards/resources_test.clj +++ b/test/clj/game/cards/resources_test.clj @@ -2387,7 +2387,7 @@ (take-credits state :runner) (take-credits state :corp) (play-from-hand state :runner "Muse") - (is (= ["Environmental Testing" "Muse"] (sort (prompt-titles :runner))) + (is (= #{"Environmental Testing" "Muse" "Done"} (into #{} (prompt-titles :runner))) "Option to trigger either muse of environmental testing first"))) (deftest environmental-testing-works-with-prevention @@ -4760,7 +4760,7 @@ (play-from-hand state :runner "Street Peddler") (click-prompt state :runner (:title oca)) ;; Make sure the simultaneous-resolution prompt is showing with 2 choices - (is (= 2 (count (prompt-buttons :runner))) "Simultaneous-resolution prompt is showing") + (is (= 3 (count (prompt-buttons :runner))) "Simultaneous-resolution prompt is showing") (click-prompt state :runner "Off-Campus Apartment") (is (= 2 (count (:hand (get-runner)))) "Drew a card from OCA")))) @@ -4971,7 +4971,7 @@ (play-from-hand state :runner "Rolodex") (is (= "Choose a trigger to resolve" (:msg (prompt-map :runner))) "Runner has simultaneous resolution prompt") - (is (= #{"Rolodex" "Paige Piper"} (set (prompt-titles :runner))) + (is (= #{"Rolodex" "Paige Piper" "Done"} (set (prompt-titles :runner))) "Both Rolodex and Paige Piper can be chosen") (click-prompt state :runner "Paige Piper") (click-prompt state :runner "Yes") diff --git a/test/clj/game/core/rezzing_test.clj b/test/clj/game/core/rezzing_test.clj index 95b55de2bf..b18e672c4f 100644 --- a/test/clj/game/core/rezzing_test.clj +++ b/test/clj/game/core/rezzing_test.clj @@ -37,7 +37,7 @@ "Corp pays 1 credit to rez AAL") (is (waiting? state :runner)) (is (= "Choose a trigger to resolve" (:msg (prompt-map :corp)))) - (is (= ["Advanced Assembly Lines" "Surat City Grid"] (sort (prompt-titles :corp)))) + (is (= ["Advanced Assembly Lines" "Done" "Surat City Grid"] (sort (prompt-titles :corp)))) (is (changed? [(:credit (get-corp)) 3] (click-prompt state :corp "Advanced Assembly Lines")) "Corp gains 3 from choosing AAL first") diff --git a/test/clj/game/core/rules_test.clj b/test/clj/game/core/rules_test.clj index e574b21d9d..3fca16fb09 100644 --- a/test/clj/game/core/rules_test.clj +++ b/test/clj/game/core/rules_test.clj @@ -704,7 +704,7 @@ (into #{} (prompt-titles :corp))) "Corp only has the marilyn interrupt") (click-prompt state :corp "Shuffle Marilyn Campaign into R&D") - (is (= #{"Hostile Infrastructure""Calvin B4L3Y"} + (is (= #{"Done" "Hostile Infrastructure""Calvin B4L3Y"} (into #{} (prompt-titles :corp))) "Corp has the simultaneous prompt") (click-prompt state :corp "Calvin B4L3Y") diff --git a/test/clj/game/core/runs_test.clj b/test/clj/game/core/runs_test.clj index 25abe881b0..c9141b8b49 100644 --- a/test/clj/game/core/runs_test.clj +++ b/test/clj/game/core/runs_test.clj @@ -252,7 +252,8 @@ (is (changed? [(get-counters (get-content state :remote1 0) :power) -1] (run-empty-server state :remote2) (is (= "Choose a trigger to resolve" (:msg (prompt-map :corp)))) - (is (= ["Embolus" "Giordano Memorial Field"] (map :title (prompt-buttons :corp)))) + (is (= #{"Done" "Embolus" "Giordano Memorial Field"} + (into #{} (prompt-titles :corp)))) (click-prompt state :corp "Giordano Memorial Field") (click-prompt state :runner "End the run")) "Embolus loses a power counter even tho GMF is resolved first and ends the run"))) @@ -267,7 +268,7 @@ (take-credits state :corp) (play-from-hand state :runner "Account Siphon") (run-continue state) - (is (= ["Account Siphon" "Breach HQ"] (prompt-buttons :runner)) "Runner can choose") + (is (= ["Account Siphon" "Breach HQ"] (prompt-titles :runner)) "Runner can choose") (click-prompt state :runner "Account Siphon") (is (second-last-log-contains? state "Runner uses the replacement effect from Account Siphon") "Replacement effect is noted") @@ -280,7 +281,7 @@ (take-credits state :corp) (play-from-hand state :runner "Account Siphon") (run-continue state) - (is (= ["Account Siphon" "Breach HQ"] (prompt-buttons :runner)) "Runner can choose") + (is (= ["Account Siphon" "Breach HQ"] (prompt-titles :runner)) "Runner can choose") (click-prompt state :runner "Breach HQ") (is (last-n-log-contains? state 2 "Runner chooses to breach HQ instead of use a replacement effect") "Not choosing replacement effect is noted") @@ -318,7 +319,7 @@ (click-prompt state :runner "HQ") (play-from-hand state :runner "Account Siphon") (run-continue state) - (is (= ["Account Siphon" "Security Testing"] (prompt-buttons :runner)) "Runner can choose") + (is (= ["Account Siphon" "Security Testing"] (prompt-titles :runner)) "Runner can choose") (click-prompt state :runner "Account Siphon") (is (second-last-log-contains? state "Runner uses the replacement effect from Account Siphon") "Replacement effect is noted")